diff --git a/TODO.md b/TODO.md index c4ff1868..5a7370a8 100644 --- a/TODO.md +++ b/TODO.md @@ -76,9 +76,10 @@ [ ] canceling chat doesn't seems to work (the spinner keeps spinning) :/ [x] build the events (+ types) as a dedicated file [ ] automate publishing the main branch -[ ] export the chat history component -[ ] add vscode specific button for opening the history in a tab +[x] export the chat history component +[x] add vscode specific button for opening the history in a tab [ ] should be monotype font on tooltip (will require adding a custom tooltip) +[ ] command completion combobox interactions ### EVENTS TODO FOR IDEs diff --git a/src/components/ChatForm/ChatForm.tsx b/src/components/ChatForm/ChatForm.tsx index 3da20382..75203684 100644 --- a/src/components/ChatForm/ChatForm.tsx +++ b/src/components/ChatForm/ChatForm.tsx @@ -100,20 +100,18 @@ export const ChatForm: React.FC<{ const str = typeof command === "function" ? command(value) : command; if ( + !commands.selected_command && str.endsWith(" ") && commands.available_commands.includes(str.slice(0, -1)) ) { setSelectedCommand(str); - } else { + } else if (!str.startsWith(commands.selected_command)) { setSelectedCommand(""); } - // TODO: get the cursor position - // Does order matter here? if (str.startsWith("@")) { requestCommandsCompletion(str, str.length); } - // set selected command if value ends with space and is in commands }; if (error) { return ( @@ -123,8 +121,6 @@ export const ChatForm: React.FC<{ ); } - // console.log({ commands }); - return ( {!isOnline && Offline} @@ -162,14 +158,13 @@ export const ChatForm: React.FC<{ onSubmit={() => handleSubmit()} > commands.selected_command + c, - )} + //TODO: maybe add a ref for cursor position? + commands={commands.available_commands} + commandArguments={commands.arguments} + selectedCommand={commands.selected_command} value={value} onChange={handleChange} onSubmit={(event) => { - // console.log("submit", event); handleEnter(event); }} placeholder={ diff --git a/src/components/ComboBox/ComboBox.module.css b/src/components/ComboBox/ComboBox.module.css index a35e2f6f..2de4dc46 100644 --- a/src/components/ComboBox/ComboBox.module.css +++ b/src/components/ComboBox/ComboBox.module.css @@ -2,7 +2,7 @@ position: relative; z-index: 50; min-width: 180px; - max-width: 280px; + /* max-width: 280px; */ border-radius: max(var(--radius-2), var(--radius-full)); } diff --git a/src/components/ComboBox/ComboBox.tsx b/src/components/ComboBox/ComboBox.tsx index 9914eb35..2c1bcaa1 100644 --- a/src/components/ComboBox/ComboBox.tsx +++ b/src/components/ComboBox/ComboBox.tsx @@ -60,24 +60,39 @@ const Popover: React.FC< ); }; -// TODO: force this open when there are commands + export const ComboBox: React.FC<{ commands: string[]; + commandArguments: string[]; + selectedCommand: string; onChange: React.Dispatch>; value: string; onSubmit: React.KeyboardEventHandler; placeholder?: string; render: (props: TextAreaProps) => React.ReactElement; -}> = ({ commands, onSubmit, placeholder, onChange, value, render }) => { +}> = ({ + commands, + onSubmit, + placeholder, + onChange, + value, + render, + selectedCommand, + commandArguments, +}) => { const ref = React.useRef(null); - const [trigger, setTrigger] = React.useState(""); + const [trigger, setTrigger] = React.useState(selectedCommand); + + const commandsWithArguments = selectedCommand + ? commandArguments.map((arg) => selectedCommand + arg) + : commands; const combobox = useComboboxStore({ defaultOpen: false, placement: "top-start", }); - const matches = matchSorter(commands, trigger, { + const matches = matchSorter(commandsWithArguments, trigger, { baseSort: (a, b) => (a.index < b.index ? -1 : 1), }); @@ -107,7 +122,6 @@ export const ComboBox: React.FC<{ const state = combobox.getState(); const tabOrEnter = event.key === "Tab" || event.key === "Enter"; if (state.open && tabOrEnter && state.activeValue) { - event.preventDefault(); const newInput = value.replace(trigger, state.activeValue + " "); combobox.setValue(newInput); onChange(newInput); @@ -119,7 +133,7 @@ export const ComboBox: React.FC<{ const handleChange = (event: React.ChangeEvent) => { const maybeCommand = event.target.value.startsWith("@") - ? event.target.value.split(/\s/)[0] + ? event.target.value.split(/\n/)[0] : ""; if (maybeCommand && event.target.selectionEnd <= maybeCommand.length) { @@ -129,6 +143,7 @@ export const ComboBox: React.FC<{ setTrigger(""); combobox.hide(); } + onChange(event.target.value); combobox.setValue(trigger); }; @@ -136,7 +151,7 @@ export const ComboBox: React.FC<{ const onItemClick = (item: string) => { const textarea = ref.current; if (!textarea) return; - onChange((prevValue) => prevValue.replace(trigger, item + " ")); + onChange(selectedCommand ? item + " " : item); setTrigger(() => ""); }; diff --git a/src/hooks/useEventBusForChat.ts b/src/hooks/useEventBusForChat.ts index bb359238..748884cf 100644 --- a/src/hooks/useEventBusForChat.ts +++ b/src/hooks/useEventBusForChat.ts @@ -270,12 +270,17 @@ function reducer(state: ChatState, action: ActionToChat): ChatState { } if (isThisChat && isReceiveAtCommandCompletion(action)) { + const selectedCommand = state.rag_commands.selected_command; + const availableCommands = selectedCommand + ? state.rag_commands.available_commands + : action.payload.completions; + const args = selectedCommand ? action.payload.completions : []; return { ...state, rag_commands: { ...state.rag_commands, - available_commands: action.payload.completions, - // arguments: args, + available_commands: availableCommands, + arguments: args, is_cmd_executable: action.payload.is_cmd_executable, }, };