Skip to content

Commit

Permalink
wip(command completion): rough implementation of completion box
Browse files Browse the repository at this point in the history
  • Loading branch information
MarcMcIntosh committed Feb 2, 2024
1 parent 6558f2e commit de9f6f5
Show file tree
Hide file tree
Showing 5 changed files with 39 additions and 23 deletions.
5 changes: 3 additions & 2 deletions TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
17 changes: 6 additions & 11 deletions src/components/ChatForm/ChatForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand All @@ -123,8 +121,6 @@ export const ChatForm: React.FC<{
);
}

// console.log({ commands });

return (
<Box mt="1" position="relative">
{!isOnline && <Callout type="info">Offline</Callout>}
Expand Down Expand Up @@ -162,14 +158,13 @@ export const ChatForm: React.FC<{
onSubmit={() => handleSubmit()}
>
<ComboBox
// maybe add a ref for cursor position?
commands={commands.available_commands.map(
(c) => 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={
Expand Down
2 changes: 1 addition & 1 deletion src/components/ComboBox/ComboBox.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}

Expand Down
29 changes: 22 additions & 7 deletions src/components/ComboBox/ComboBox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,24 +60,39 @@ const Popover: React.FC<
</Box>
);
};
// TODO: force this open when there are commands

export const ComboBox: React.FC<{
commands: string[];
commandArguments: string[];
selectedCommand: string;
onChange: React.Dispatch<React.SetStateAction<string>>;
value: string;
onSubmit: React.KeyboardEventHandler<HTMLTextAreaElement>;
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<HTMLTextAreaElement>(null);
const [trigger, setTrigger] = React.useState<string>("");
const [trigger, setTrigger] = React.useState<string>(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),
});

Expand Down Expand Up @@ -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);
Expand All @@ -119,7 +133,7 @@ export const ComboBox: React.FC<{

const handleChange = (event: React.ChangeEvent<HTMLTextAreaElement>) => {
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) {
Expand All @@ -129,14 +143,15 @@ export const ComboBox: React.FC<{
setTrigger("");
combobox.hide();
}

onChange(event.target.value);
combobox.setValue(trigger);
};

const onItemClick = (item: string) => {
const textarea = ref.current;
if (!textarea) return;
onChange((prevValue) => prevValue.replace(trigger, item + " "));
onChange(selectedCommand ? item + " " : item);
setTrigger(() => "");
};

Expand Down
9 changes: 7 additions & 2 deletions src/hooks/useEventBusForChat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
},
};
Expand Down

0 comments on commit de9f6f5

Please sign in to comment.