Skip to content

Commit

Permalink
wip(command completion): add event bus and state for the combobox
Browse files Browse the repository at this point in the history
  • Loading branch information
MarcMcIntosh committed Feb 1, 2024
1 parent fbc9b50 commit 74e0735
Show file tree
Hide file tree
Showing 4 changed files with 149 additions and 7 deletions.
10 changes: 8 additions & 2 deletions src/components/ChatForm/ChatForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,13 @@ export const ChatForm: React.FC<{
onStopStreaming: () => void;
handleContextFile: () => void;
hasContextFile: boolean;
commands: string[];
commands: ChatState["rag_commands"];
attachFile: ChatState["active_file"];
requestCommandsCompletion: (
query: string,
cursor: number,
number?: number,
) => void;
}> = ({
onSubmit,
onClose,
Expand All @@ -71,6 +76,7 @@ export const ChatForm: React.FC<{
hasContextFile,
commands,
attachFile,
// requestCommandsCompletion,
}) => {
const [value, setValue] = React.useState("");

Expand Down Expand Up @@ -130,7 +136,7 @@ export const ChatForm: React.FC<{
onSubmit={() => handleSubmit()}
>
<ComboBox
commands={commands}
commands={commands.available_commands}
value={value}
onChange={setValue}
onSubmit={handleEnter}
Expand Down
51 changes: 51 additions & 0 deletions src/events/chat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
CapsResponse,
isCapsResponse,
ChatContextFile,
CommandCompletionResponse,
} from "../services/refact";

export enum EVENT_NAMES_FROM_CHAT {
Expand All @@ -18,6 +19,8 @@ export enum EVENT_NAMES_FROM_CHAT {
READY = "chat_ready",
NEW_FILE = "chat_create_new_file",
PASTE_DIFF = "chat_paste_diff",
REQUEST_AT_COMMAND_COMPLETION = "chat_request_at_command_completion",
REQUEST_AT_COMMAND_PREVIEW = "chat_request_at_command_preview",
}

export enum EVENT_NAMES_TO_CHAT {
Expand All @@ -36,6 +39,8 @@ export enum EVENT_NAMES_TO_CHAT {
REMOVE_FILES = "remove_context_file",
ACTIVE_FILE_INFO = "chat_active_file_info",
TOGGLE_ACTIVE_FILE = "chat_toggle_active_file",
RECEIVE_AT_COMMAND_COMPLETION = "chat_receive_at_command_completion",
RECEIVE_AT_COMMAND_PREVIEW = "chat_receive_at_command_preview",
}

export type ChatThread = {
Expand Down Expand Up @@ -68,6 +73,29 @@ export function isActionFromChat(action: unknown): action is ActionFromChat {
return Object.values(ALL_EVENT_NAMES).includes(action.type);
}

export interface RequestAtCommandCompletion extends ActionFromChat {
type: EVENT_NAMES_FROM_CHAT.REQUEST_AT_COMMAND_COMPLETION;
payload: { id: string; query: string; cursor: number; number: number };
}

export function isRequestAtCommandCompletion(
action: unknown,
): action is RequestAtCommandCompletion {
if (!isActionFromChat(action)) return false;
return action.type === EVENT_NAMES_FROM_CHAT.REQUEST_AT_COMMAND_COMPLETION;
}

export interface RequestAtCommandPreview extends ActionFromChat {
type: EVENT_NAMES_FROM_CHAT.REQUEST_AT_COMMAND_PREVIEW;
payload: { id: string; query: string };
}

export function isRequestAtCommandPreview(
action: unknown,
): action is RequestAtCommandPreview {
if (!isActionFromChat(action)) return false;
return action.type === EVENT_NAMES_FROM_CHAT.REQUEST_AT_COMMAND_PREVIEW;
}
export interface NewFileFromChat extends ActionFromChat {
type: EVENT_NAMES_FROM_CHAT.NEW_FILE;
payload: {
Expand Down Expand Up @@ -165,6 +193,29 @@ export function isActionToChat(action: unknown): action is ActionToChat {
return Object.values(EVENT_NAMES).includes(action.type);
}

export interface ReceiveAtCommandCompletion extends ActionToChat {
type: EVENT_NAMES_TO_CHAT.RECEIVE_AT_COMMAND_COMPLETION;
payload: { id: string } & CommandCompletionResponse;
}

export function isReceiveAtCommandCompletion(
action: unknown,
): action is ReceiveAtCommandCompletion {
if (!isActionToChat(action)) return false;
return action.type === EVENT_NAMES_TO_CHAT.RECEIVE_AT_COMMAND_COMPLETION;
}

export interface ReceiveAtCommandPreview extends ActionToChat {
type: EVENT_NAMES_TO_CHAT.RECEIVE_AT_COMMAND_PREVIEW;
payload: { id: string; file_content: string; file_name: string };
}

export function isReceiveAtCommandPreview(
action: unknown,
): action is ReceiveAtCommandPreview {
if (!isActionToChat(action)) return false;
return action.type === EVENT_NAMES_TO_CHAT.RECEIVE_AT_COMMAND_PREVIEW;
}
export interface ToggleActiveFile extends ActionToChat {
type: EVENT_NAMES_TO_CHAT.TOGGLE_ACTIVE_FILE;
payload: { id: string; attach_file: boolean };
Expand Down
48 changes: 44 additions & 4 deletions src/hooks/useEventBusForChat.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { useEffect, useReducer } from "react";
import { useEffectOnce } from "usehooks-ts";
import {
ChatContextFile,
ChatMessages,
Expand Down Expand Up @@ -33,6 +34,8 @@ import {
NewFileFromChat,
PasteDiffFromChat,
ReadyMessage,
RequestAtCommandCompletion,
isReceiveAtCommandCompletion,
} from "../events";
import { useConfig } from "../contexts/config-context";
import { usePostMessage } from "./usePostMessage";
Expand Down Expand Up @@ -139,7 +142,7 @@ function reducer(state: ChatState, action: ActionToChat): ChatState {
const default_cap = action.payload.caps.code_chat_default_model;
const available_caps = Object.keys(action.payload.caps.code_chat_models);
const error = available_caps.length === 0 ? "No available caps" : null;
const rag_commands = action.payload.caps.chat_rag_functions ?? [];
// const rag_commands = action.payload.caps.chat_rag_functions ?? [];
return {
...state,
error,
Expand All @@ -152,7 +155,7 @@ function reducer(state: ChatState, action: ActionToChat): ChatState {
default_cap: default_cap || available_caps[0] || "",
available_caps,
},
rag_commands,
// rag_commands,
};
}

Expand Down Expand Up @@ -264,6 +267,16 @@ function reducer(state: ChatState, action: ActionToChat): ChatState {
};
}

if (isThisChat && isReceiveAtCommandCompletion(action)) {
return {
...state,
rag_commands: {
...state.rag_commands,
available_commands: action.payload.completions,
},
};
}

return state;
}

Expand All @@ -279,7 +292,12 @@ export type ChatState = {
streaming: boolean;
error: string | null;
caps: ChatCapsState;
rag_commands: string[];
rag_commands: {
available_commands: string[];
selected_command: string;
arguments: string[];
is_cmd_executable: boolean;
};
active_file: {
name: string;
attach: boolean;
Expand All @@ -303,7 +321,12 @@ function createInitialState(): ChatState {
default_cap: "",
available_caps: [],
},
rag_commands: [],
rag_commands: {
available_commands: [],
selected_command: "",
arguments: [],
is_cmd_executable: false,
},

active_file: {
name: "",
Expand Down Expand Up @@ -503,6 +526,22 @@ export const useEventBusForChat = () => {
postMessage(action);
}

function requestCommandsCompletion(
query: string,
cursor: number,
number = 5,
) {
const action: RequestAtCommandCompletion = {
type: EVENT_NAMES_FROM_CHAT.REQUEST_AT_COMMAND_COMPLETION,
payload: { id: state.chat.id, query, cursor, number },
};
postMessage(action);
}

useEffectOnce(() => {
requestCommandsCompletion("@", 1);
});

return {
state,
askQuestion,
Expand All @@ -518,5 +557,6 @@ export const useEventBusForChat = () => {
sendReadyMessage,
handleNewFileClick,
handlePasteDiffClick,
requestCommandsCompletion,
};
};
47 changes: 46 additions & 1 deletion src/hooks/useEventBusForHost.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import { useEffect, useRef } from "react";
import { sendChat, getCaps, ChatContextFile } from "../services/refact";
import {
sendChat,
getCaps,
ChatContextFile,
getAtCommandCompletion,
getAtCommandPreview,
} from "../services/refact";
import { useChatHistory } from "./useChatHistory";
import {
EVENT_NAMES_TO_CHAT,
Expand All @@ -9,6 +15,10 @@ import {
isRequestCapsFromChat,
isStopStreamingFromChat,
isRequestForFileFromChat,
isRequestAtCommandCompletion,
isRequestAtCommandPreview,
ReceiveAtCommandCompletion,
ReceiveAtCommandPreview,
} from "../events";
import { useConfig } from "../contexts/config-context";

Expand Down Expand Up @@ -119,6 +129,41 @@ export function useEventBusForHost() {
);
});
}

if (isRequestAtCommandCompletion(event.data)) {
const { id, query, cursor, number } = event.data.payload;
getAtCommandCompletion(query, cursor, number, lspUrl)
.then((res) => {
const message: ReceiveAtCommandCompletion = {
type: EVENT_NAMES_TO_CHAT.RECEIVE_AT_COMMAND_COMPLETION,
payload: { id, ...res },
};

window.postMessage(message, "*");
})
.catch((error) => {
// TODO: handle error
// eslint-disable-next-line no-console
console.error(error);
});
}

if (isRequestAtCommandPreview(event.data)) {
const { id, query } = event.data.payload;
getAtCommandPreview(query, lspUrl)
.then((res) => {
const message: ReceiveAtCommandPreview = {
type: EVENT_NAMES_TO_CHAT.RECEIVE_AT_COMMAND_PREVIEW,
payload: { id, ...res },
};
window.postMessage(message, "*");
})
.catch((error) => {
// TODO: handle error
// eslint-disable-next-line no-console
console.error(error);
});
}
};

window.addEventListener("message", listener);
Expand Down

0 comments on commit 74e0735

Please sign in to comment.