summaryrefslogtreecommitdiff
path: root/extension
diff options
context:
space:
mode:
Diffstat (limited to 'extension')
-rw-r--r--extension/react-app/src/components/ComboBox.tsx195
-rw-r--r--extension/react-app/src/components/Layout.tsx4
-rw-r--r--extension/react-app/src/components/TextDialog.tsx42
-rw-r--r--extension/react-app/src/hooks/AbstractContinueGUIClientProtocol.ts6
-rw-r--r--extension/react-app/src/hooks/ContinueGUIClientProtocol.ts13
-rw-r--r--extension/schema/FullState.d.ts4
6 files changed, 200 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;
diff --git a/extension/schema/FullState.d.ts b/extension/schema/FullState.d.ts
index 6f9304fe..e3d5389a 100644
--- a/extension/schema/FullState.d.ts
+++ b/extension/schema/FullState.d.ts
@@ -53,6 +53,7 @@ export interface FullState1 {
adding_highlighted_code: AddingHighlightedCode;
selected_context_items: SelectedContextItems;
session_info?: SessionInfo;
+ saved_context_groups?: SavedContextGroups;
[k: string]: unknown;
}
/**
@@ -140,3 +141,6 @@ export interface SessionInfo {
date_created: DateCreated;
[k: string]: unknown;
}
+export interface SavedContextGroups {
+ [k: string]: ContextItem[];
+}