diff options
Diffstat (limited to 'extension/react-app/src')
5 files changed, 196 insertions, 64 deletions
diff --git a/extension/react-app/src/components/ComboBox.tsx b/extension/react-app/src/components/ComboBox.tsx index 0cf2bc19..c407a779 100644 --- a/extension/react-app/src/components/ComboBox.tsx +++ b/extension/react-app/src/components/ComboBox.tsx @@ -7,6 +7,8 @@ import React, { import { useCombobox } from "downshift"; import styled from "styled-components"; import { + Button, + TextInput, defaultBorderRadius, lightGray, secondaryDark, @@ -15,12 +17,20 @@ import { } from "."; import PillButton from "./PillButton"; import HeaderButtonWithText from "./HeaderButtonWithText"; -import { DocumentPlusIcon } from "@heroicons/react/24/outline"; +import { + BookmarkIcon, + DocumentPlusIcon, + FolderArrowDownIcon, +} from "@heroicons/react/24/outline"; import { ContextItem } from "../../../schema/FullState"; import { postVscMessage } from "../vscode"; import { GUIClientContext } from "../App"; import { MeiliSearch } from "meilisearch"; -import { setBottomMessage } from "../redux/slices/uiStateSlice"; +import { + setBottomMessage, + setDialogMessage, + setShowDialog, +} from "../redux/slices/uiStateSlice"; import { useDispatch, useSelector } from "react-redux"; import { RootStore } from "../redux/store"; @@ -29,6 +39,38 @@ const SEARCH_INDEX_NAME = "continue_context_items"; // #region styled components const mainInputFontSize = 13; +const MiniPillSpan = styled.span` + padding: 3px; + padding-left: 6px; + padding-right: 6px; + border-radius: ${defaultBorderRadius}; + color: ${vscForeground}; + background-color: #fff3; + overflow: hidden; + font-size: 12px; + display: flex; + align-items: center; + text-align: center; + justify-content: center; +`; + +const ContextGroupSelectDiv = styled.div` + display: flex; + align-items: center; + gap: 8px; + padding: 8px; + border-radius: ${defaultBorderRadius}; + background-color: ${secondaryDark}; + color: ${vscForeground}; + margin-top: 8px; + cursor: pointer; + + &:hover { + background-color: ${vscBackground}; + color: ${vscForeground}; + } +`; + const EmptyPillDiv = styled.div` padding: 4px; padding-left: 8px; @@ -137,6 +179,9 @@ const ComboBox = React.forwardRef((props: ComboBoxProps, ref) => { const workspacePaths = useSelector( (state: RootStore) => state.config.workspacePaths ); + const savedContextGroups = useSelector( + (state: RootStore) => state.serverState.saved_context_groups + ); const [history, setHistory] = React.useState<string[]>([]); // The position of the current command you are typing now, so the one that will be appended to history once you press enter @@ -328,6 +373,87 @@ const ComboBox = React.forwardRef((props: ComboBoxProps, ref) => { }; }, [inputRef.current]); + const showSelectContextGroupDialog = () => { + dispatch( + setDialogMessage( + <div className="px-4"> + <h2>Saved Context Groups</h2> + + {savedContextGroups && Object.keys(savedContextGroups).length > 0 ? ( + <div className="overflow-scroll"> + {Object.keys(savedContextGroups).map((key: string) => { + const contextGroup = savedContextGroups[key]; + return ( + <ContextGroupSelectDiv + onClick={() => { + dispatch(setDialogMessage(undefined)); + dispatch(setShowDialog(false)); + client?.selectContextGroup(key); + }} + > + <b>{key}: </b> + + {contextGroup.map((contextItem) => { + return ( + <MiniPillSpan> + {contextItem.description.name} + </MiniPillSpan> + ); + })} + </ContextGroupSelectDiv> + ); + })} + </div> + ) : ( + <div>No saved context groups</div> + )} + <Button + className="ml-auto" + onClick={() => { + dispatch(setDialogMessage(undefined)); + dispatch(setShowDialog(false)); + }} + > + Cancel + </Button> + </div> + ) + ); + dispatch(setShowDialog(true)); + }; + + const showDialogToSaveContextGroup = () => { + let inputElement: HTMLInputElement | null = null; + dispatch( + setDialogMessage( + <div> + <TextInput + defaultValue="My Context Group" + type="text" + ref={(input) => { + inputElement = input; + }} + /> + <br /> + <Button + className="ml-auto" + onClick={() => { + dispatch(setDialogMessage(undefined)); + dispatch(setShowDialog(false)); + const title = inputElement + ? inputElement.value + : "My Context Group"; + client?.saveContextGroup(title, props.selectedContextItems); + }} + > + Create + </Button> + </div> + ) + ); + dispatch(setShowDialog(true)); + }; + return ( <> <div @@ -354,32 +480,65 @@ const ComboBox = React.forwardRef((props: ComboBoxProps, ref) => { /> ); })} - {props.selectedContextItems.length > 0 && - (props.addingHighlightedCode ? ( - <EmptyPillDiv - onClick={() => { - props.onToggleAddContext(); - }} - > - Highlight code section - </EmptyPillDiv> - ) : ( + <HeaderButtonWithText + text="Load bookmarked context" + onClick={() => { + showSelectContextGroupDialog(); + }} + className="pill-button focus:outline-none focus:border-red-600 focus:border focus:border-solid" + onKeyDown={(e: KeyboardEvent) => { + e.preventDefault(); + if (e.key === "Enter") { + showSelectContextGroupDialog(); + } + }} + > + <FolderArrowDownIcon width="1.4em" height="1.4em" /> + </HeaderButtonWithText> + {props.selectedContextItems.length > 0 && ( + <> <HeaderButtonWithText - text="Add more code to context" + text="Bookmark context" onClick={() => { - props.onToggleAddContext(); + showDialogToSaveContextGroup(); }} className="pill-button focus:outline-none focus:border-red-600 focus:border focus:border-solid" onKeyDown={(e: KeyboardEvent) => { e.preventDefault(); if (e.key === "Enter") { - props.onToggleAddContext(); + showDialogToSaveContextGroup(); } }} > - <DocumentPlusIcon width="1.4em" height="1.4em" /> + <BookmarkIcon width="1.4em" height="1.4em" /> </HeaderButtonWithText> - ))} + {props.addingHighlightedCode ? ( + <EmptyPillDiv + onClick={() => { + props.onToggleAddContext(); + }} + > + Highlight code section + </EmptyPillDiv> + ) : ( + <HeaderButtonWithText + text="Add more code to context" + onClick={() => { + props.onToggleAddContext(); + }} + className="pill-button focus:outline-none focus:border-red-600 focus:border focus:border-solid" + onKeyDown={(e: KeyboardEvent) => { + e.preventDefault(); + if (e.key === "Enter") { + props.onToggleAddContext(); + } + }} + > + <DocumentPlusIcon width="1.4em" height="1.4em" /> + </HeaderButtonWithText> + )} + </> + )} </div> <div className="flex px-2" ref={divRef} hidden={!downshiftProps.isOpen}> <MainTextInput @@ -516,7 +675,7 @@ const ComboBox = React.forwardRef((props: ComboBoxProps, ref) => { <> <br /> <pre style={{ color: lightGray }}> - {item.content.split('\n').slice(0, 5).join('\n')} + {item.content.split("\n").slice(0, 5).join("\n")} </pre> </> )} diff --git a/extension/react-app/src/components/Layout.tsx b/extension/react-app/src/components/Layout.tsx index 86192afb..eee92ff7 100644 --- a/extension/react-app/src/components/Layout.tsx +++ b/extension/react-app/src/components/Layout.tsx @@ -112,15 +112,13 @@ const Layout = () => { <Onboarding /> <TextDialog showDialog={showDialog} - onEnter={(text) => { - client?.sendMainInput(`/feedback ${text}`); + onEnter={() => { dispatch(setShowDialog(false)); }} onClose={() => { dispatch(setShowDialog(false)); }} message={dialogMessage} - entryOn={dialogEntryOn} /> <Outlet /> diff --git a/extension/react-app/src/components/TextDialog.tsx b/extension/react-app/src/components/TextDialog.tsx index 41f811e8..7fcc41f1 100644 --- a/extension/react-app/src/components/TextDialog.tsx +++ b/extension/react-app/src/components/TextDialog.tsx @@ -54,20 +54,10 @@ const P = styled.p` const TextDialog = (props: { showDialog: boolean; - onEnter: (text: string) => void; + onEnter: () => void; onClose: () => void; message?: string | JSX.Element; - entryOn?: boolean; }) => { - const [text, setText] = useState(""); - const textAreaRef = React.createRef<HTMLTextAreaElement>(); - - useEffect(() => { - if (textAreaRef.current) { - textAreaRef.current.focus(); - } - }, [props.showDialog]); - return ( <ScreenCover onClick={() => { @@ -120,36 +110,6 @@ const TextDialog = (props: { ) : ( props.message )} - {props.entryOn && ( - <> - <TextArea - rows={10} - ref={textAreaRef} - onKeyDown={(e) => { - if ( - e.key === "Enter" && - isMetaEquivalentKeyPressed(e) && - textAreaRef.current - ) { - props.onEnter(textAreaRef.current.value); - setText(""); - } else if (e.key === "Escape") { - props.onClose(); - } - }} - /> - <Button - onClick={() => { - if (textAreaRef.current) { - props.onEnter(textAreaRef.current.value); - setText(""); - } - }} - > - Enter - </Button> - </> - )} </Dialog> </DialogContainer> </ScreenCover> diff --git a/extension/react-app/src/hooks/AbstractContinueGUIClientProtocol.ts b/extension/react-app/src/hooks/AbstractContinueGUIClientProtocol.ts index 804362aa..2e8aaeef 100644 --- a/extension/react-app/src/hooks/AbstractContinueGUIClientProtocol.ts +++ b/extension/react-app/src/hooks/AbstractContinueGUIClientProtocol.ts @@ -1,4 +1,4 @@ -import { ContextItemId } from "../../../schema/FullState"; +import { ContextItem, ContextItemId } from "../../../schema/FullState"; abstract class AbstractContinueGUIClientProtocol { abstract sendMainInput(input: string): void; @@ -36,6 +36,10 @@ abstract class AbstractContinueGUIClientProtocol { abstract onReconnectAtSession(session_id: string): void; abstract editStepAtIndex(userInput: string, index: number): void; + + abstract saveContextGroup(title: string, contextItems: ContextItem[]): void; + + abstract selectContextGroup(id: string): void; } export default AbstractContinueGUIClientProtocol; diff --git a/extension/react-app/src/hooks/ContinueGUIClientProtocol.ts b/extension/react-app/src/hooks/ContinueGUIClientProtocol.ts index 82aeee28..aa558adb 100644 --- a/extension/react-app/src/hooks/ContinueGUIClientProtocol.ts +++ b/extension/react-app/src/hooks/ContinueGUIClientProtocol.ts @@ -1,4 +1,4 @@ -import { ContextItemId } from "../../../schema/FullState"; +import { ContextItem, ContextItemId } from "../../../schema/FullState"; import AbstractContinueGUIClientProtocol from "./AbstractContinueGUIClientProtocol"; import { Messenger, WebsocketMessenger } from "./messenger"; import { VscodeMessenger } from "./vscodeMessenger"; @@ -132,6 +132,17 @@ class ContinueGUIClientProtocol extends AbstractContinueGUIClientProtocol { index, }); } + + saveContextGroup(title: string, contextItems: ContextItem[]): void { + this.messenger?.send("save_context_group", { + context_items: contextItems, + title, + }); + } + + selectContextGroup(id: string): void { + this.messenger?.send("select_context_group", { id }); + } } export default ContinueGUIClientProtocol; |