Skip to content

Commit

Permalink
feat: attach files i vscode
Browse files Browse the repository at this point in the history
  • Loading branch information
MarcMcIntosh committed Jan 19, 2024
1 parent e2a6185 commit 090cf3b
Show file tree
Hide file tree
Showing 9 changed files with 122 additions and 8 deletions.
10 changes: 8 additions & 2 deletions TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,12 +61,18 @@
[x] add combobox to chat form and maybe pass text-area as a child component
[x] remove test commands

[ ] rag commands come from the caps url.
[x] rag commands come from the caps url.

[X] ensure vscode api is called only once
[ ] vscode specific commands and components
[ ] export the types for re-use in refact-vscode
[ ] vscode attach file
[x] vscode attach file
[x] send some info to the chat about the current file open in vscode, probably a REQUEST and RECEIVE file info events
[ ] new file button
[ ] paste diff button

[ ] check what happens when the lsp isn't on in vscode
[ ] in vscode attach shouldn't show if there's no files (like when opening the ide)

### EVENTS TODO FOR IDEs

Expand Down
10 changes: 10 additions & 0 deletions src/components/ChatForm/CharForm.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ describe("ChatForm", () => {
handleContextFile={vi.fn}
hasContextFile={false}
commands={[]}
attachFile={{
name: "",
can_paste: false,
attach: false,
}}
/>,
);

Expand Down Expand Up @@ -51,6 +56,11 @@ describe("ChatForm", () => {
handleContextFile={vi.fn}
hasContextFile={false}
commands={[]}
attachFile={{
name: "",
can_paste: false,
attach: false,
}}
/>,
);
const textarea = app.container.querySelector("textarea");
Expand Down
5 changes: 5 additions & 0 deletions src/components/ChatForm/ChatForm.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,11 @@ const meta = {
hasContextFile: false,
handleContextFile: noop,
commands: testCommands,
attachFile: {
name: "todo.md",
can_paste: true,
attach: false,
},
},
} satisfies Meta<typeof ChatForm>;

Expand Down
10 changes: 9 additions & 1 deletion src/components/ChatForm/ChatForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { Select } from "../Select/Select";
import { FileUpload } from "../FileUpload";
import { Button } from "@radix-ui/themes";
import { ComboBox } from "../ComboBox";
import type { ChatState } from "../../hooks";

const CapsSelect: React.FC<{
value: string;
Expand Down Expand Up @@ -53,6 +54,7 @@ export const ChatForm: React.FC<{
handleContextFile: () => void;
hasContextFile: boolean;
commands: string[];
attachFile: ChatState["active_file"];
}> = ({
onSubmit,
onClose,
Expand All @@ -68,8 +70,10 @@ export const ChatForm: React.FC<{
handleContextFile,
hasContextFile,
commands,
attachFile,
}) => {
const [value, setValue] = React.useState("");

const isOnline = useIsOnline();

const handleSubmit = () => {
Expand All @@ -93,7 +97,11 @@ export const ChatForm: React.FC<{
<Box mt="1" position="relative">
{!isOnline && <Callout type="info">Offline</Callout>}
{canChangeModel && (
<FileUpload onClick={handleContextFile} checked={hasContextFile} />
<FileUpload
fileName={attachFile.name}
onClick={handleContextFile}
checked={hasContextFile || attachFile.attach}
/>
)}
<Flex>
{canChangeModel && (
Expand Down
4 changes: 2 additions & 2 deletions src/components/FileUpload/FileUpload.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React from "react";
import { Checkbox, Flex, Text } from "@radix-ui/themes";

export const FileUpload: React.FC<{
onClick: () => void;
onClick: (value: boolean) => void;
fileName?: string;
checked: boolean;
}> = ({ onClick, fileName, checked }) => {
Expand All @@ -12,7 +12,7 @@ export const FileUpload: React.FC<{
<Checkbox
checked={checked}
onCheckedChange={() => {
onClick();
onClick(!checked);
}}
/>{" "}
Attach {fileName ?? "a file"}
Expand Down
28 changes: 28 additions & 0 deletions src/events/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ export enum EVENT_NAMES_FROM_CHAT {
OPEN_IN_CHAT_IN_TAB = "open_chat_in_new_tab",
SEND_TO_SIDE_BAR = "chat_send_to_sidebar",
READY = "chat_ready",
// TODO
NEW_FILE = "chat_create_new_file",
PASTE_DIFF = "chat_paste_diff",
}

export enum EVENT_NAMES_TO_CHAT {
Expand All @@ -32,13 +35,17 @@ export enum EVENT_NAMES_TO_CHAT {
SET_DISABLE_CHAT = "set_disable_chat",
RECEIVE_FILES = "receive_context_file",
REMOVE_FILES = "remove_context_file",
// TODO
ACTIVE_FILE_INFO = "chat_active_file_info",
TOGGLE_ACTIVE_FILE = "chat_toggle_active_file",
}

export type ChatThread = {
id: string;
messages: ChatMessages;
title?: string;
model: string;
attach_file?: boolean;
};
interface BaseAction {
type: EVENT_NAMES_FROM_CHAT | EVENT_NAMES_TO_CHAT;
Expand Down Expand Up @@ -131,6 +138,27 @@ export function isActionToChat(action: unknown): action is ActionToChat {
return Object.values(EVENT_NAMES).includes(action.type);
}

export interface ToggleActiveFile extends ActionToChat {
type: EVENT_NAMES_TO_CHAT.TOGGLE_ACTIVE_FILE;
payload: { id: string; attach: boolean };
}

export function isToggleActiveFile(
action: unknown,
): action is ToggleActiveFile {
if (!isActionToChat(action)) return false;
return action.type === EVENT_NAMES_TO_CHAT.TOGGLE_ACTIVE_FILE;
}
export interface ActiveFileInfo extends ActionToChat {
type: EVENT_NAMES_TO_CHAT.ACTIVE_FILE_INFO;
payload: { id: string; name: string; can_paste: boolean };
}

export function isActiveFileInfo(action: unknown): action is ActiveFileInfo {
if (!isActionToChat(action)) return false;
return action.type === EVENT_NAMES_TO_CHAT.ACTIVE_FILE_INFO;
}

export interface ReceiveContextFile extends ActionToChat {
type: EVENT_NAMES_TO_CHAT.RECEIVE_FILES;
payload: {
Expand Down
1 change: 1 addition & 0 deletions src/features/Chat.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ export const Chat: React.FC<{ style?: React.CSSProperties }> = (props) => {
hasContextFile={hasContextFile}
commands={state.rag_commands}
onClose={maybeSendToSideBar}
attachFile={state.active_file}
/>
</Flex>
);
Expand Down
56 changes: 54 additions & 2 deletions src/hooks/useEventBusForChat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,11 @@ import {
isReceiveContextFile,
isRequestForFileFromChat,
isRemoveContext,
isActiveFileInfo,
isToggleActiveFile,
ToggleActiveFile,
} from "../events";
import { useConfig } from "../contexts/config-context";

declare global {
interface Window {
Expand Down Expand Up @@ -257,6 +261,28 @@ function reducer(state: ChatState, action: ActionToChat): ChatState {
};
}

if (isThisChat && isActiveFileInfo(action)) {
const { name, can_paste } = action.payload;
return {
...state,
active_file: {
name,
can_paste,
attach: state.active_file.attach,
},
};
}

if (isThisChat && isToggleActiveFile(action)) {
return {
...state,
active_file: {
...state.active_file,
attach: action.payload.attach,
},
};
}

return state;
}

Expand All @@ -273,6 +299,11 @@ export type ChatState = {
error: string | null;
caps: ChatCapsState;
rag_commands: string[];
active_file: {
name: string;
attach: boolean;
can_paste: boolean;
};
};

function createInitialState(): ChatState {
Expand All @@ -292,12 +323,19 @@ function createInitialState(): ChatState {
available_caps: [],
},
rag_commands: [],

active_file: {
name: "",
attach: false,
can_paste: false,
},
};
}

const initialState = createInitialState();

export const useEventBusForChat = () => {
const config = useConfig();
const [state, dispatch] = useReducer(reducer, initialState);
const postMessage = usePostMessage();

Expand Down Expand Up @@ -341,11 +379,12 @@ export const useEventBusForChat = () => {
type: EVENT_NAMES_TO_CHAT.SET_DISABLE_CHAT,
payload: { id: state.chat.id, disable: true },
});
const payload = {
const payload: ChatThread = {
id: state.chat.id,
messages: messages,
title: state.chat.title,
model: state.chat.model,
attach_file: state.active_file.attach,
};

dispatch({
Expand Down Expand Up @@ -407,7 +446,8 @@ export const useEventBusForChat = () => {
isChatContextFileMessage(message),
);

function handleContextFile() {
function handleContextFileForWeb() {
// TODO: in wed request or remove a file, else toggle the active_file value
if (hasContextFile) {
dispatch({
type: EVENT_NAMES_TO_CHAT.REMOVE_FILES,
Expand All @@ -421,6 +461,18 @@ export const useEventBusForChat = () => {
}
}

function handleContextFile(toggle?: boolean) {
if (config.host === "web") {
handleContextFileForWeb();
} else {
const action: ToggleActiveFile = {
type: EVENT_NAMES_TO_CHAT.TOGGLE_ACTIVE_FILE,
payload: { id: state.chat.id, attach: !!toggle },
};
dispatch(action);
}
}

function backFromChat() {
postMessage({
type: EVENT_NAMES_FROM_CHAT.BACK_FROM_CHAT,
Expand Down
6 changes: 5 additions & 1 deletion src/lib/render/Chat.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@ import { ChatWithOutSideBar } from "./ChatWithoutSideBar.tsx";
export const Chat: React.FC<Config> = (config) => {
return (
<ConfigProvider config={config}>
{config.host === "web" ? <ChatWithSideBar /> : <ChatWithOutSideBar />}
{config.host === "web" || config.dev ? (
<ChatWithSideBar />
) : (
<ChatWithOutSideBar />
)}
</ConfigProvider>
);
};

0 comments on commit 090cf3b

Please sign in to comment.