From 894a197892fc4e3a6a5af7d47b7702ea895e20b7 Mon Sep 17 00:00:00 2001 From: Nate Sesti Date: Sun, 2 Jul 2023 20:14:27 -0700 Subject: finishing up explicit context --- extension/react-app/src/components/PillButton.tsx | 66 +++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 extension/react-app/src/components/PillButton.tsx (limited to 'extension/react-app/src/components/PillButton.tsx') diff --git a/extension/react-app/src/components/PillButton.tsx b/extension/react-app/src/components/PillButton.tsx new file mode 100644 index 00000000..33451db5 --- /dev/null +++ b/extension/react-app/src/components/PillButton.tsx @@ -0,0 +1,66 @@ +import { useState } from "react"; +import styled from "styled-components"; +import { defaultBorderRadius } from "."; + +const Button = styled.button` + border: none; + color: white; + background-color: transparent; + border: 1px solid white; + border-radius: ${defaultBorderRadius}; + padding: 3px 6px; + + &:hover { + background-color: white; + color: black; + } +`; + +interface PillButtonProps { + onHover?: (arg0: boolean) => void; + onDelete?: () => void; + title: string; +} + +const PillButton = (props: PillButtonProps) => { + const [isHovered, setIsHovered] = useState(false); + return ( + + ); +}; + +export default PillButton; -- cgit v1.2.3-70-g09d2 From 2ec0b4118bd092a528a1171df6ae4ffcd7cfb33b Mon Sep 17 00:00:00 2001 From: Nate Sesti Date: Tue, 4 Jul 2023 12:47:28 -0700 Subject: better control over context --- extension/react-app/src/components/ComboBox.tsx | 57 +++++++++++++++++++--- .../src/components/HeaderButtonWithText.tsx | 2 + extension/react-app/src/components/PillButton.tsx | 9 ++-- extension/react-app/src/components/index.ts | 13 +++-- extension/react-app/src/tabs/gui.tsx | 23 +++++++-- 5 files changed, 82 insertions(+), 22 deletions(-) (limited to 'extension/react-app/src/components/PillButton.tsx') diff --git a/extension/react-app/src/components/ComboBox.tsx b/extension/react-app/src/components/ComboBox.tsx index 742c643b..bdb8850d 100644 --- a/extension/react-app/src/components/ComboBox.tsx +++ b/extension/react-app/src/components/ComboBox.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useEffect } from "react"; +import React, { useCallback, useEffect, useState } from "react"; import { useCombobox } from "downshift"; import styled from "styled-components"; import { @@ -10,7 +10,10 @@ import { import CodeBlock from "./CodeBlock"; import { RangeInFile } from "../../../src/client"; import PillButton from "./PillButton"; +import HeaderButtonWithText from "./HeaderButtonWithText"; +import { Trash, LockClosed, LockOpen } from "@styled-icons/heroicons-outline"; +// #region styled components const mainInputFontSize = 16; const ContextDropdown = styled.div` @@ -87,13 +90,16 @@ const Li = styled.li<{ cursor: pointer; `; +// #endregion + interface ComboBoxProps { items: { name: string; description: string }[]; onInputValueChange: (inputValue: string) => void; disabled?: boolean; - onEnter?: (e: React.KeyboardEvent) => void; - highlightedCodeSections?: (RangeInFile & { contents: string })[]; - deleteContextItem?: (idx: number) => void; + onEnter: (e: React.KeyboardEvent) => void; + highlightedCodeSections: (RangeInFile & { contents: string })[]; + deleteContextItems: (indices: number[]) => void; + onTogglePin: () => void; } const ComboBox = React.forwardRef((props: ComboBoxProps, ref) => { @@ -104,6 +110,7 @@ const ComboBox = React.forwardRef((props: ComboBoxProps, ref) => { const [hoveringButton, setHoveringButton] = React.useState(false); const [hoveringContextDropdown, setHoveringContextDropdown] = React.useState(false); + const [pinned, setPinned] = useState(false); const [highlightedCodeSections, setHighlightedCodeSections] = React.useState( props.highlightedCodeSections || [ { @@ -242,12 +249,46 @@ const ComboBox = React.forwardRef((props: ComboBoxProps, ref) => {
+ {highlightedCodeSections.length > 0 && ( + <> + { + props.deleteContextItems( + highlightedCodeSections.map((_, idx) => idx) + ); + }} + > + + + { + setPinned((prev) => !prev); + props.onTogglePin(); + }} + > + {pinned ? ( + + ) : ( + + )} + + + )} {highlightedCodeSections.map((section, idx) => ( { - if (props.deleteContextItem) { - props.deleteContextItem(idx); + if (props.deleteContextItems) { + props.deleteContextItems([idx]); } setHighlightedCodeSections((prev) => { const newSections = [...prev]; @@ -280,7 +321,7 @@ const ComboBox = React.forwardRef((props: ComboBoxProps, ref) => { onMouseLeave={() => { setHoveringContextDropdown(false); }} - hidden={!hoveringContextDropdown && !hoveringButton} + hidden={true || (!hoveringContextDropdown && !hoveringButton)} > {highlightedCodeSections.map((section, idx) => ( <> diff --git a/extension/react-app/src/components/HeaderButtonWithText.tsx b/extension/react-app/src/components/HeaderButtonWithText.tsx index 30931f86..3ddac93c 100644 --- a/extension/react-app/src/components/HeaderButtonWithText.tsx +++ b/extension/react-app/src/components/HeaderButtonWithText.tsx @@ -7,12 +7,14 @@ interface HeaderButtonWithTextProps { onClick?: (e: any) => void; children: React.ReactNode; disabled?: boolean; + inverted?: boolean; } const HeaderButtonWithText = (props: HeaderButtonWithTextProps) => { const [hover, setHover] = useState(false); return ( { diff --git a/extension/react-app/src/components/PillButton.tsx b/extension/react-app/src/components/PillButton.tsx index 33451db5..55fe1ac6 100644 --- a/extension/react-app/src/components/PillButton.tsx +++ b/extension/react-app/src/components/PillButton.tsx @@ -1,6 +1,7 @@ import { useState } from "react"; import styled from "styled-components"; import { defaultBorderRadius } from "."; +import { XMark } from "@styled-icons/heroicons-outline"; const Button = styled.button` border: none; @@ -42,13 +43,12 @@ const PillButton = (props: PillButtonProps) => {
- {props.title} + {props.title}
); diff --git a/extension/react-app/src/components/index.ts b/extension/react-app/src/components/index.ts index 429a7df5..db1925ed 100644 --- a/extension/react-app/src/components/index.ts +++ b/extension/react-app/src/components/index.ts @@ -124,16 +124,19 @@ export const appear = keyframes` } `; -export const HeaderButton = styled.button` - background-color: transparent; +export const HeaderButton = styled.button<{ inverted: boolean | undefined }>` + background-color: ${({ inverted }) => (inverted ? "white" : "transparent")}; + color: ${({ inverted }) => (inverted ? "black" : "white")}; + border: 1px solid white; border-radius: ${defaultBorderRadius}; cursor: pointer; - color: white; &:hover { - background-color: white; - color: black; + background-color: ${({ inverted }) => + typeof inverted === "undefined" || inverted ? "white" : "transparent"}; + color: ${({ inverted }) => + typeof inverted === "undefined" || inverted ? "black" : "white"}; } display: flex; align-items: center; diff --git a/extension/react-app/src/tabs/gui.tsx b/extension/react-app/src/tabs/gui.tsx index 0a9e48fb..c2ff101a 100644 --- a/extension/react-app/src/tabs/gui.tsx +++ b/extension/react-app/src/tabs/gui.tsx @@ -70,10 +70,13 @@ function GUI(props: GUIProps) { const [usingFastModel, setUsingFastModel] = useState(false); const [waitingForSteps, setWaitingForSteps] = useState(false); const [userInputQueue, setUserInputQueue] = useState([]); - const [highlightedRanges, setHighlightedRanges] = useState([]); + const [highlightedRanges, setHighlightedRanges] = useState([ + { filepath: "abc.txt", range: { start: { line: 2 }, end: { line: 4 } } }, + ]); const [availableSlashCommands, setAvailableSlashCommands] = useState< { name: string; description: string }[] >([]); + const [pinned, setPinned] = useState(false); const [showDataSharingInfo, setShowDataSharingInfo] = useState(false); const [stepsOpen, setStepsOpen] = useState([ true, @@ -185,9 +188,9 @@ function GUI(props: GUIProps) { const mainTextInputRef = useRef(null); - const deleteContextItem = useCallback( - (idx: number) => { - client?.deleteContextAtIndices([idx]); + const deleteContextItems = useCallback( + (indices: number[]) => { + client?.deleteContextAtIndices(indices); }, [client] ); @@ -241,6 +244,13 @@ function GUI(props: GUIProps) { setUserInputQueue((queue) => { return [...queue, input]; }); + + // Delete all context items unless locked + if (!pinned) { + client?.deleteContextAtIndices( + highlightedRanges.map((_, index) => index) + ); + } } }; @@ -345,7 +355,10 @@ function GUI(props: GUIProps) { onInputValueChange={() => {}} items={availableSlashCommands} highlightedCodeSections={highlightedRanges} - deleteContextItem={deleteContextItem} + deleteContextItems={deleteContextItems} + onTogglePin={() => { + setPinned((prev: boolean) => !prev); + }} /> -- cgit v1.2.3-70-g09d2 From 4b1d10f3bae1f1c80ecf74993f18f318c7b22b7f Mon Sep 17 00:00:00 2001 From: Nate Sesti Date: Tue, 4 Jul 2023 13:43:09 -0700 Subject: many fixes --- continuedev/src/continuedev/core/autopilot.py | 11 ++++++++--- continuedev/src/continuedev/core/main.py | 7 +++---- continuedev/src/continuedev/core/sdk.py | 13 ++++++++++++- continuedev/src/continuedev/steps/chat.py | 1 + extension/package-lock.json | 4 ++-- extension/package.json | 4 ++-- extension/react-app/src/components/ComboBox.tsx | 10 +++------- extension/react-app/src/components/PillButton.tsx | 1 - extension/react-app/src/components/UserInputContainer.tsx | 6 ++++-- extension/react-app/src/tabs/gui.tsx | 5 ++--- 10 files changed, 37 insertions(+), 25 deletions(-) (limited to 'extension/react-app/src/components/PillButton.tsx') diff --git a/continuedev/src/continuedev/core/autopilot.py b/continuedev/src/continuedev/core/autopilot.py index 05e48f40..313ceded 100644 --- a/continuedev/src/continuedev/core/autopilot.py +++ b/continuedev/src/continuedev/core/autopilot.py @@ -144,9 +144,7 @@ class Autopilot(ContinueBaseModel): async def handle_highlighted_code(self, range_in_files: List[RangeInFileWithContents]): workspace_path = self.continue_sdk.ide.workspace_directory for rif in range_in_files: - rif.filepath = os.path.relpath(rif.filepath, workspace_path) - if rif.filepath.startswith(".."): - rif.filepath = os.path.basename(rif.filepath) + rif.filepath = os.path.basename(rif.filepath) # If current range overlaps with any others, delete them and only keep the new range new_ranges = [] @@ -156,6 +154,13 @@ class Autopilot(ContinueBaseModel): if rif.filepath == new_rif.filepath and rif.range.overlaps_with(new_rif.range): found_overlap = True break + + # Also don't allow multiple ranges in same file with same content. This is useless to the model, and avoids + # the bug where cmd+f causes repeated highlights + if rif.filepath == new_rif.filepath and rif.contents == new_rif.contents: + found_overlap = True + break + if not found_overlap: new_ranges.append(rif) diff --git a/continuedev/src/continuedev/core/main.py b/continuedev/src/continuedev/core/main.py index 9a6617f4..8bad09d1 100644 --- a/continuedev/src/continuedev/core/main.py +++ b/continuedev/src/continuedev/core/main.py @@ -107,11 +107,9 @@ class HistoryNode(ContinueBaseModel): return self.step.chat_context return self.step.chat_context + [ ChatMessage( - role="function", + role="assistant", name=self.step.__class__.__name__, - content=json.dumps({ - "description": self.step.description or "Function complete", - }), + content=self.step.description or f"Ran function {self.step.name}", summary=f"Called function {self.step.name}" )] @@ -200,6 +198,7 @@ class SlashCommandDescription(ContinueBaseModel): name: str description: str + class FullState(ContinueBaseModel): """A full state of the program, including the history""" history: History diff --git a/continuedev/src/continuedev/core/sdk.py b/continuedev/src/continuedev/core/sdk.py index 988ac6b0..fe975b99 100644 --- a/continuedev/src/continuedev/core/sdk.py +++ b/continuedev/src/continuedev/core/sdk.py @@ -97,7 +97,18 @@ class ContinueSDK(AbstractContinueSDK): async def _ensure_absolute_path(self, path: str) -> str: if os.path.isabs(path): return path - return os.path.join(self.ide.workspace_directory, path) + + # Else if in workspace + workspace_path = os.path.join(self.ide.workspace_directory, path) + if os.path.exists(workspace_path): + return workspace_path + else: + # Check if it matches any of the open files, then use that absolute path + open_files = await self.ide.getOpenFiles() + for open_file in open_files: + if os.path.basename(open_file) == os.path.basename(path): + return open_file + raise Exception(f"Path {path} does not exist") async def run_step(self, step: Step) -> Coroutine[Observation, None, None]: return await self.__autopilot._run_singular_step(step) diff --git a/continuedev/src/continuedev/steps/chat.py b/continuedev/src/continuedev/steps/chat.py index 49dd98e4..db3f9d7f 100644 --- a/continuedev/src/continuedev/steps/chat.py +++ b/continuedev/src/continuedev/steps/chat.py @@ -149,6 +149,7 @@ class ChatWithFunctions(Step): name: str = "Input" manage_own_chat_context: bool = True description: str = "" + hide: bool = True async def run(self, sdk: ContinueSDK): await sdk.update_ui() diff --git a/extension/package-lock.json b/extension/package-lock.json index c9b8b757..94823004 100644 --- a/extension/package-lock.json +++ b/extension/package-lock.json @@ -1,12 +1,12 @@ { "name": "continue", - "version": "0.0.106", + "version": "0.0.107", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "continue", - "version": "0.0.106", + "version": "0.0.107", "license": "Apache-2.0", "dependencies": { "@electron/rebuild": "^3.2.10", diff --git a/extension/package.json b/extension/package.json index 184d16e2..6434efc3 100644 --- a/extension/package.json +++ b/extension/package.json @@ -14,7 +14,7 @@ "displayName": "Continue", "pricing": "Free", "description": "The open-source coding autopilot", - "version": "0.0.106", + "version": "0.0.107", "publisher": "Continue", "engines": { "vscode": "^1.67.0" @@ -55,7 +55,7 @@ }, "continue.OPENAI_API_KEY": { "type": "password", - "default": "", + "default": null, "description": "The OpenAI API key to use for code generation." }, "continue.dataSwitch": { diff --git a/extension/react-app/src/components/ComboBox.tsx b/extension/react-app/src/components/ComboBox.tsx index bdb8850d..3e1f3e16 100644 --- a/extension/react-app/src/components/ComboBox.tsx +++ b/extension/react-app/src/components/ComboBox.tsx @@ -279,13 +279,9 @@ const ComboBox = React.forwardRef((props: ComboBoxProps, ref) => { )} {highlightedCodeSections.map((section, idx) => ( { if (props.deleteContextItems) { props.deleteContextItems([idx]); diff --git a/extension/react-app/src/components/PillButton.tsx b/extension/react-app/src/components/PillButton.tsx index 55fe1ac6..2352c3ad 100644 --- a/extension/react-app/src/components/PillButton.tsx +++ b/extension/react-app/src/components/PillButton.tsx @@ -50,7 +50,6 @@ const PillButton = (props: PillButtonProps) => { borderRight: "1px solid black", paddingRight: "4px", }} - hidden={!isHovered} onClick={() => { props.onDelete?.(); props.onHover?.(false); diff --git a/extension/react-app/src/components/UserInputContainer.tsx b/extension/react-app/src/components/UserInputContainer.tsx index 59453169..44fdba38 100644 --- a/extension/react-app/src/components/UserInputContainer.tsx +++ b/extension/react-app/src/components/UserInputContainer.tsx @@ -6,10 +6,12 @@ import HeaderButtonWithText from "./HeaderButtonWithText"; import { Play, XMark } from "@styled-icons/heroicons-outline"; import { RootStore } from "../redux/store"; import { useSelector } from "react-redux"; +import { HistoryNode } from "../../../schema/HistoryNode"; interface UserInputContainerProps { onDelete: () => void; children: string; + historyNode: HistoryNode; } const StyledDiv = styled.div` @@ -26,7 +28,7 @@ const StyledDiv = styled.div` const UserInputContainer = (props: UserInputContainerProps) => { return ( - + diff --git a/extension/react-app/src/tabs/gui.tsx b/extension/react-app/src/tabs/gui.tsx index c2ff101a..8bfadc05 100644 --- a/extension/react-app/src/tabs/gui.tsx +++ b/extension/react-app/src/tabs/gui.tsx @@ -70,9 +70,7 @@ function GUI(props: GUIProps) { const [usingFastModel, setUsingFastModel] = useState(false); const [waitingForSteps, setWaitingForSteps] = useState(false); const [userInputQueue, setUserInputQueue] = useState([]); - const [highlightedRanges, setHighlightedRanges] = useState([ - { filepath: "abc.txt", range: { start: { line: 2 }, end: { line: 4 } } }, - ]); + const [highlightedRanges, setHighlightedRanges] = useState([]); const [availableSlashCommands, setAvailableSlashCommands] = useState< { name: string; description: string }[] >([]); @@ -296,6 +294,7 @@ function GUI(props: GUIProps) { onDelete={() => { client?.deleteAtIndex(index); }} + historyNode={node} > {node.step.description as string} -- cgit v1.2.3-70-g09d2 From 8f080c4d2447e3eb66db448c08324ee71614709e Mon Sep 17 00:00:00 2001 From: Nate Sesti Date: Wed, 5 Jul 2023 01:04:33 -0700 Subject: highlighted code improvements --- continuedev/src/continuedev/core/autopilot.py | 11 ++++++++- continuedev/src/continuedev/core/main.py | 1 + continuedev/src/continuedev/server/gui.py | 7 ++++++ continuedev/src/continuedev/steps/core/core.py | 1 + extension/react-app/src/components/ComboBox.tsx | 27 ++++++++++++++++++---- extension/react-app/src/components/PillButton.tsx | 27 +++++++--------------- .../src/components/UserInputContainer.tsx | 4 +++- .../src/hooks/ContinueGUIClientProtocol.ts | 2 ++ .../react-app/src/hooks/useContinueGUIProtocol.ts | 4 ++++ extension/react-app/src/tabs/gui.tsx | 6 +++++ 10 files changed, 65 insertions(+), 25 deletions(-) (limited to 'extension/react-app/src/components/PillButton.tsx') diff --git a/continuedev/src/continuedev/core/autopilot.py b/continuedev/src/continuedev/core/autopilot.py index 313ceded..29be3b79 100644 --- a/continuedev/src/continuedev/core/autopilot.py +++ b/continuedev/src/continuedev/core/autopilot.py @@ -69,7 +69,8 @@ class Autopilot(ContinueBaseModel): user_input_queue=self._main_user_input_queue, default_model=self.continue_sdk.config.default_model, highlighted_ranges=self._highlighted_ranges, - slash_commands=self.get_available_slash_commands() + slash_commands=self.get_available_slash_commands(), + adding_highlighted_code=self._adding_highlighted_code, ) def get_available_slash_commands(self) -> List[Dict]: @@ -140,8 +141,12 @@ class Autopilot(ContinueBaseModel): await self._run_singular_step(step) _highlighted_ranges: List[RangeInFileWithContents] = [] + _adding_highlighted_code: bool = False async def handle_highlighted_code(self, range_in_files: List[RangeInFileWithContents]): + if not self._adding_highlighted_code: + return + workspace_path = self.continue_sdk.ide.workspace_directory for rif in range_in_files: rif.filepath = os.path.basename(rif.filepath) @@ -186,6 +191,10 @@ class Autopilot(ContinueBaseModel): self._highlighted_ranges = kept_ranges await self.update_subscribers() + async def toggle_adding_highlighted_code(self): + self._adding_highlighted_code = not self._adding_highlighted_code + await self.update_subscribers() + async def _run_singular_step(self, step: "Step", is_future_step: bool = False) -> Coroutine[Observation, None, None]: # Allow config to set disallowed steps if step.__class__.__name__ in self.continue_sdk.config.disallowed_steps: diff --git a/continuedev/src/continuedev/core/main.py b/continuedev/src/continuedev/core/main.py index 8bad09d1..28fd964e 100644 --- a/continuedev/src/continuedev/core/main.py +++ b/continuedev/src/continuedev/core/main.py @@ -207,6 +207,7 @@ class FullState(ContinueBaseModel): default_model: str highlighted_ranges: List[RangeInFileWithContents] slash_commands: List[SlashCommandDescription] + adding_highlighted_code: bool class ContinueSDK: diff --git a/continuedev/src/continuedev/server/gui.py b/continuedev/src/continuedev/server/gui.py index 4e960f7c..fa573b37 100644 --- a/continuedev/src/continuedev/server/gui.py +++ b/continuedev/src/continuedev/server/gui.py @@ -85,6 +85,8 @@ class GUIProtocolServer(AbstractGUIProtocolServer): self.on_delete_at_index(data["index"]) elif message_type == "delete_context_at_indices": self.on_delete_context_at_indices(data["indices"]) + elif message_type == "toggle_adding_highlighted_code": + self.on_toggle_adding_highlighted_code() except Exception as e: print(e) @@ -128,6 +130,11 @@ class GUIProtocolServer(AbstractGUIProtocolServer): self.session.autopilot.delete_context_at_indices(indices) ) + def on_toggle_adding_highlighted_code(self): + asyncio.create_task( + self.session.autopilot.toggle_adding_highlighted_code() + ) + @router.websocket("/ws") async def websocket_endpoint(websocket: WebSocket, session: Session = Depends(websocket_session)): diff --git a/continuedev/src/continuedev/steps/core/core.py b/continuedev/src/continuedev/steps/core/core.py index b215b317..c74412ba 100644 --- a/continuedev/src/continuedev/steps/core/core.py +++ b/continuedev/src/continuedev/steps/core/core.py @@ -286,6 +286,7 @@ class DefaultModelEditCodeStep(Step): return "```" in line or "" in line or "" in line or "" in line or "" in line or "" in line or "" in line or "" in line or "" in line async def stream_rif(self, rif: RangeInFileWithContents, sdk: ContinueSDK): + await sdk.ide.saveFile(rif.filepath) full_file_contents = await sdk.ide.readFile(rif.filepath) file_prefix, contents, file_suffix, model_to_use = await self.get_prompt_parts( diff --git a/extension/react-app/src/components/ComboBox.tsx b/extension/react-app/src/components/ComboBox.tsx index 3e1f3e16..97f5b57e 100644 --- a/extension/react-app/src/components/ComboBox.tsx +++ b/extension/react-app/src/components/ComboBox.tsx @@ -11,7 +11,12 @@ import CodeBlock from "./CodeBlock"; import { RangeInFile } from "../../../src/client"; import PillButton from "./PillButton"; import HeaderButtonWithText from "./HeaderButtonWithText"; -import { Trash, LockClosed, LockOpen } from "@styled-icons/heroicons-outline"; +import { + Trash, + LockClosed, + LockOpen, + Plus, +} from "@styled-icons/heroicons-outline"; // #region styled components const mainInputFontSize = 16; @@ -100,6 +105,8 @@ interface ComboBoxProps { highlightedCodeSections: (RangeInFile & { contents: string })[]; deleteContextItems: (indices: number[]) => void; onTogglePin: () => void; + onToggleAddContext: () => void; + addingHighlightedCode: boolean; } const ComboBox = React.forwardRef((props: ComboBoxProps, ref) => { @@ -249,6 +256,19 @@ const ComboBox = React.forwardRef((props: ComboBoxProps, ref) => {
+ {highlightedCodeSections.length === 0 && ( + { + props.onToggleAddContext(); + }} + inverted={props.addingHighlightedCode} + > + + + )} {highlightedCodeSections.length > 0 && ( <> { ))} - Highlight code to include as context.{" "} - {highlightedCodeSections.length === 0 && - "Otherwise using entire currently open file."} + Highlight code to include as context. Currently open file included by + default. {highlightedCodeSections.length === 0 && ""}
{ props.onHover(false); } }} + onClick={() => { + if (props.onDelete) { + props.onDelete(); + } + }} > -
- { - props.onDelete?.(); - props.onHover?.(false); - }} - > - - - {props.title} -
+ {props.title} ); }; diff --git a/extension/react-app/src/components/UserInputContainer.tsx b/extension/react-app/src/components/UserInputContainer.tsx index 44fdba38..a72f6098 100644 --- a/extension/react-app/src/components/UserInputContainer.tsx +++ b/extension/react-app/src/components/UserInputContainer.tsx @@ -14,7 +14,7 @@ interface UserInputContainerProps { historyNode: HistoryNode; } -const StyledDiv = styled.div` +const StyledDiv = styled.div<{ hidden: boolean }>` background-color: rgb(50 50 50); padding: 8px; padding-left: 16px; @@ -24,6 +24,8 @@ const StyledDiv = styled.div` font-size: 13px; display: flex; align-items: center; + visibility: ${(props) => (props.hidden ? "hidden" : "visible")}; + height: ${(props) => (props.hidden ? "0px" : "auto")}; `; const UserInputContainer = (props: UserInputContainerProps) => { diff --git a/extension/react-app/src/hooks/ContinueGUIClientProtocol.ts b/extension/react-app/src/hooks/ContinueGUIClientProtocol.ts index 96ea7ab3..f123bb2b 100644 --- a/extension/react-app/src/hooks/ContinueGUIClientProtocol.ts +++ b/extension/react-app/src/hooks/ContinueGUIClientProtocol.ts @@ -22,6 +22,8 @@ abstract class AbstractContinueGUIClientProtocol { abstract deleteAtIndex(index: number): void; abstract deleteContextAtIndices(indices: number[]): void; + + abstract toggleAddingHighlightedCode(): void; } export default AbstractContinueGUIClientProtocol; diff --git a/extension/react-app/src/hooks/useContinueGUIProtocol.ts b/extension/react-app/src/hooks/useContinueGUIProtocol.ts index e950387c..49f200ae 100644 --- a/extension/react-app/src/hooks/useContinueGUIProtocol.ts +++ b/extension/react-app/src/hooks/useContinueGUIProtocol.ts @@ -74,6 +74,10 @@ class ContinueGUIClientProtocol extends AbstractContinueGUIClientProtocol { deleteContextAtIndices(indices: number[]) { this.messenger.send("delete_context_at_indices", { indices }); } + + toggleAddingHighlightedCode(): void { + this.messenger.send("toggle_adding_highlighted_code", {}); + } } export default ContinueGUIClientProtocol; diff --git a/extension/react-app/src/tabs/gui.tsx b/extension/react-app/src/tabs/gui.tsx index bbf0b126..851045d5 100644 --- a/extension/react-app/src/tabs/gui.tsx +++ b/extension/react-app/src/tabs/gui.tsx @@ -71,6 +71,7 @@ function GUI(props: GUIProps) { const [waitingForSteps, setWaitingForSteps] = useState(false); const [userInputQueue, setUserInputQueue] = useState([]); const [highlightedRanges, setHighlightedRanges] = useState([]); + const [addingHighlightedCode, setAddingHighlightedCode] = useState(false); const [availableSlashCommands, setAvailableSlashCommands] = useState< { name: string; description: string }[] >([]); @@ -157,6 +158,7 @@ function GUI(props: GUIProps) { setHistory(state.history); setHighlightedRanges(state.highlighted_ranges); setUserInputQueue(state.user_input_queue); + setAddingHighlightedCode(state.adding_highlighted_code); setAvailableSlashCommands( state.slash_commands.map((c: any) => { return { @@ -361,6 +363,10 @@ function GUI(props: GUIProps) { onTogglePin={() => { setPinned((prev: boolean) => !prev); }} + onToggleAddContext={() => { + client?.toggleAddingHighlightedCode(); + }} + addingHighlightedCode={addingHighlightedCode} /> -- cgit v1.2.3-70-g09d2 From 5c03dd4775845c559a1f6298f7577609ba907bb1 Mon Sep 17 00:00:00 2001 From: Nate Sesti Date: Wed, 5 Jul 2023 23:49:37 -0700 Subject: ui overhaul --- continuedev/src/continuedev/core/autopilot.py | 47 +++- continuedev/src/continuedev/core/main.py | 9 +- .../src/continuedev/models/generate_json_schema.py | 4 +- continuedev/src/continuedev/server/gui.py | 14 + .../media/Lexend/Lexend-VariableFont_wght.ttf | Bin 0 -> 176220 bytes extension/media/Lexend/OFL.txt | 93 +++++++ extension/media/Lexend/README.txt | 71 +++++ extension/media/Lexend/static/Lexend-Black.ttf | Bin 0 -> 78496 bytes extension/media/Lexend/static/Lexend-Bold.ttf | Bin 0 -> 78632 bytes extension/media/Lexend/static/Lexend-ExtraBold.ttf | Bin 0 -> 78716 bytes .../media/Lexend/static/Lexend-ExtraLight.ttf | Bin 0 -> 78508 bytes extension/media/Lexend/static/Lexend-Light.ttf | Bin 0 -> 78668 bytes extension/media/Lexend/static/Lexend-Medium.ttf | Bin 0 -> 78696 bytes extension/media/Lexend/static/Lexend-Regular.ttf | Bin 0 -> 78360 bytes extension/media/Lexend/static/Lexend-SemiBold.ttf | Bin 0 -> 78744 bytes extension/media/Lexend/static/Lexend-Thin.ttf | Bin 0 -> 78380 bytes extension/react-app/package-lock.json | 59 ++++ extension/react-app/package.json | 1 + extension/react-app/src/App.tsx | 39 +-- extension/react-app/src/components/CodeBlock.tsx | 4 +- extension/react-app/src/components/ComboBox.tsx | 191 +++++++------ .../react-app/src/components/ContinueButton.tsx | 2 +- .../src/components/HeaderButtonWithText.tsx | 44 +-- extension/react-app/src/components/Loader.tsx | 40 +++ extension/react-app/src/components/PillButton.tsx | 144 +++++++--- .../react-app/src/components/StepContainer.tsx | 13 +- extension/react-app/src/components/TextDialog.tsx | 2 +- .../src/components/UserInputContainer.tsx | 6 +- extension/react-app/src/components/index.ts | 22 +- .../src/hooks/ContinueGUIClientProtocol.ts | 4 + .../react-app/src/hooks/useContinueGUIProtocol.ts | 8 + extension/react-app/src/index.css | 9 +- extension/react-app/src/main.tsx | 6 +- extension/react-app/src/tabs/gui.tsx | 22 +- extension/schema/FullState.d.ts | 133 +++++++++ extension/src/debugPanel.ts | 4 + schema/json/FullState.json | 304 +++++++++++++++++++++ 37 files changed, 1088 insertions(+), 207 deletions(-) create mode 100644 extension/media/Lexend/Lexend-VariableFont_wght.ttf create mode 100644 extension/media/Lexend/OFL.txt create mode 100644 extension/media/Lexend/README.txt create mode 100644 extension/media/Lexend/static/Lexend-Black.ttf create mode 100644 extension/media/Lexend/static/Lexend-Bold.ttf create mode 100644 extension/media/Lexend/static/Lexend-ExtraBold.ttf create mode 100644 extension/media/Lexend/static/Lexend-ExtraLight.ttf create mode 100644 extension/media/Lexend/static/Lexend-Light.ttf create mode 100644 extension/media/Lexend/static/Lexend-Medium.ttf create mode 100644 extension/media/Lexend/static/Lexend-Regular.ttf create mode 100644 extension/media/Lexend/static/Lexend-SemiBold.ttf create mode 100644 extension/media/Lexend/static/Lexend-Thin.ttf create mode 100644 extension/react-app/src/components/Loader.tsx create mode 100644 extension/schema/FullState.d.ts create mode 100644 schema/json/FullState.json (limited to 'extension/react-app/src/components/PillButton.tsx') diff --git a/continuedev/src/continuedev/core/autopilot.py b/continuedev/src/continuedev/core/autopilot.py index b1c4f471..acdc1f0d 100644 --- a/continuedev/src/continuedev/core/autopilot.py +++ b/continuedev/src/continuedev/core/autopilot.py @@ -12,7 +12,7 @@ from .observation import Observation, InternalErrorObservation from ..server.ide_protocol import AbstractIdeProtocolServer from ..libs.util.queue import AsyncSubscriptionQueue from ..models.main import ContinueBaseModel -from .main import Context, ContinueCustomException, Policy, History, FullState, Step, HistoryNode +from .main import Context, ContinueCustomException, HighlightedRangeContext, Policy, History, FullState, Step, HistoryNode from ..steps.core.core import ReversibleStep, ManualEditStep, UserInputStep from ..libs.util.telemetry import capture_event from .sdk import ContinueSDK @@ -140,11 +140,24 @@ class Autopilot(ContinueBaseModel): tb_step.step_name, {"output": output, **tb_step.params}) await self._run_singular_step(step) - _highlighted_ranges: List[RangeInFileWithContents] = [] + _highlighted_ranges: List[HighlightedRangeContext] = [] _adding_highlighted_code: bool = False + def _make_sure_is_editing_range(self): + """If none of the highlighted ranges are currently being edited, the first should be selected""" + if len(self._highlighted_ranges) == 0: + return + if not any(map(lambda x: x.editing, self._highlighted_ranges)): + self._highlighted_ranges[0].editing = True + async def handle_highlighted_code(self, range_in_files: List[RangeInFileWithContents]): - if not self._adding_highlighted_code: + if not self._adding_highlighted_code and len(self._highlighted_ranges) > 0: + return + + # If un-highlighting, then remove the range + if len(self._highlighted_ranges) == 1 and len(range_in_files) == 1 and range_in_files[0].range.start == range_in_files[0].range.end: + self._highlighted_ranges = [] + await self.update_subscribers() return # Filter out rifs from ~/.continue/diffs folder @@ -160,20 +173,25 @@ class Autopilot(ContinueBaseModel): for i, rif in enumerate(self._highlighted_ranges): found_overlap = False for new_rif in range_in_files: - if rif.filepath == new_rif.filepath and rif.range.overlaps_with(new_rif.range): + if rif.range.filepath == new_rif.filepath and rif.range.range.overlaps_with(new_rif.range): found_overlap = True break # Also don't allow multiple ranges in same file with same content. This is useless to the model, and avoids # the bug where cmd+f causes repeated highlights - if rif.filepath == new_rif.filepath and rif.contents == new_rif.contents: + if rif.range.filepath == new_rif.filepath and rif.range.contents == new_rif.contents: found_overlap = True break if not found_overlap: new_ranges.append(rif) - self._highlighted_ranges = new_ranges + range_in_files + self._highlighted_ranges = new_ranges + [HighlightedRangeContext( + range=rif, editing=False, pinned=False + ) for rif in range_in_files] + + self._make_sure_is_editing_range() + await self.update_subscribers() _step_depth: int = 0 @@ -193,12 +211,25 @@ class Autopilot(ContinueBaseModel): if i not in indices: kept_ranges.append(rif) self._highlighted_ranges = kept_ranges + + self._make_sure_is_editing_range() + await self.update_subscribers() async def toggle_adding_highlighted_code(self): self._adding_highlighted_code = not self._adding_highlighted_code await self.update_subscribers() + async def set_editing_at_indices(self, indices: List[int]): + for i in range(len(self._highlighted_ranges)): + self._highlighted_ranges[i].editing = i in indices + await self.update_subscribers() + + async def set_pinned_at_indices(self, indices: List[int]): + for i in range(len(self._highlighted_ranges)): + self._highlighted_ranges[i].pinned = i in indices + await self.update_subscribers() + async def _run_singular_step(self, step: "Step", is_future_step: bool = False) -> Coroutine[Observation, None, None]: # Allow config to set disallowed steps if step.__class__.__name__ in self.continue_sdk.config.disallowed_steps: @@ -359,6 +390,10 @@ class Autopilot(ContinueBaseModel): if len(self._main_user_input_queue) > 1: return + # Remove context unless pinned + self._highlighted_ranges = [ + hr for hr in self._highlighted_ranges if hr.pinned] + # await self._request_halt() # Just run the step that takes user input, and # then up to the policy to decide how to deal with it. diff --git a/continuedev/src/continuedev/core/main.py b/continuedev/src/continuedev/core/main.py index 28fd964e..62cc4936 100644 --- a/continuedev/src/continuedev/core/main.py +++ b/continuedev/src/continuedev/core/main.py @@ -199,13 +199,20 @@ class SlashCommandDescription(ContinueBaseModel): description: str +class HighlightedRangeContext(ContinueBaseModel): + """Context for a highlighted range""" + range: RangeInFileWithContents + editing: bool + pinned: bool + + class FullState(ContinueBaseModel): """A full state of the program, including the history""" history: History active: bool user_input_queue: List[str] default_model: str - highlighted_ranges: List[RangeInFileWithContents] + highlighted_ranges: List[HighlightedRangeContext] slash_commands: List[SlashCommandDescription] adding_highlighted_code: bool diff --git a/continuedev/src/continuedev/models/generate_json_schema.py b/continuedev/src/continuedev/models/generate_json_schema.py index 080787a5..6cebf429 100644 --- a/continuedev/src/continuedev/models/generate_json_schema.py +++ b/continuedev/src/continuedev/models/generate_json_schema.py @@ -1,7 +1,7 @@ from .main import * from .filesystem import RangeInFile, FileEdit from .filesystem_edit import FileEditWithFullContents -from ..core.main import History, HistoryNode +from ..core.main import History, HistoryNode, FullState from pydantic import schema_json_of import os @@ -12,7 +12,7 @@ MODELS_TO_GENERATE = [ ] + [ FileEditWithFullContents ] + [ - History, HistoryNode + History, HistoryNode, FullState ] RENAMES = { diff --git a/continuedev/src/continuedev/server/gui.py b/continuedev/src/continuedev/server/gui.py index fa573b37..8e9b1fb9 100644 --- a/continuedev/src/continuedev/server/gui.py +++ b/continuedev/src/continuedev/server/gui.py @@ -87,6 +87,10 @@ class GUIProtocolServer(AbstractGUIProtocolServer): self.on_delete_context_at_indices(data["indices"]) elif message_type == "toggle_adding_highlighted_code": self.on_toggle_adding_highlighted_code() + elif message_type == "set_editing_at_indices": + self.on_set_editing_at_indices(data["indices"]) + elif message_type == "set_pinned_at_indices": + self.on_set_pinned_at_indices(data["indices"]) except Exception as e: print(e) @@ -135,6 +139,16 @@ class GUIProtocolServer(AbstractGUIProtocolServer): self.session.autopilot.toggle_adding_highlighted_code() ) + def on_set_editing_at_indices(self, indices: List[int]): + asyncio.create_task( + self.session.autopilot.set_editing_at_indices(indices) + ) + + def on_set_pinned_at_indices(self, indices: List[int]): + asyncio.create_task( + self.session.autopilot.set_pinned_at_indices(indices) + ) + @router.websocket("/ws") async def websocket_endpoint(websocket: WebSocket, session: Session = Depends(websocket_session)): diff --git a/extension/media/Lexend/Lexend-VariableFont_wght.ttf b/extension/media/Lexend/Lexend-VariableFont_wght.ttf new file mode 100644 index 00000000..b294dc84 Binary files /dev/null and b/extension/media/Lexend/Lexend-VariableFont_wght.ttf differ diff --git a/extension/media/Lexend/OFL.txt b/extension/media/Lexend/OFL.txt new file mode 100644 index 00000000..6b679248 --- /dev/null +++ b/extension/media/Lexend/OFL.txt @@ -0,0 +1,93 @@ +Copyright 2018 The Lexend Project Authors (https://github.com/googlefonts/lexend), with Reserved Font Name โ€œRevReading Lexendโ€. + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/extension/media/Lexend/README.txt b/extension/media/Lexend/README.txt new file mode 100644 index 00000000..f2966dfe --- /dev/null +++ b/extension/media/Lexend/README.txt @@ -0,0 +1,71 @@ +Lexend Variable Font +==================== + +This download contains Lexend as both a variable font and static fonts. + +Lexend is a variable font with this axis: + wght + +This means all the styles are contained in a single file: + Lexend-VariableFont_wght.ttf + +If your app fully supports variable fonts, you can now pick intermediate styles +that arenโ€™t available as static fonts. Not all apps support variable fonts, and +in those cases you can use the static font files for Lexend: + static/Lexend-Thin.ttf + static/Lexend-ExtraLight.ttf + static/Lexend-Light.ttf + static/Lexend-Regular.ttf + static/Lexend-Medium.ttf + static/Lexend-SemiBold.ttf + static/Lexend-Bold.ttf + static/Lexend-ExtraBold.ttf + static/Lexend-Black.ttf + +Get started +----------- + +1. Install the font files you want to use + +2. Use your app's font picker to view the font family and all the +available styles + +Learn more about variable fonts +------------------------------- + + https://developers.google.com/web/fundamentals/design-and-ux/typography/variable-fonts + https://variablefonts.typenetwork.com + https://medium.com/variable-fonts + +In desktop apps + + https://theblog.adobe.com/can-variable-fonts-illustrator-cc + https://helpx.adobe.com/nz/photoshop/using/fonts.html#variable_fonts + +Online + + https://developers.google.com/fonts/docs/getting_started + https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Fonts/Variable_Fonts_Guide + https://developer.microsoft.com/en-us/microsoft-edge/testdrive/demos/variable-fonts + +Installing fonts + + MacOS: https://support.apple.com/en-us/HT201749 + Linux: https://www.google.com/search?q=how+to+install+a+font+on+gnu%2Blinux + Windows: https://support.microsoft.com/en-us/help/314960/how-to-install-or-remove-a-font-in-windows + +Android Apps + + https://developers.google.com/fonts/docs/android + https://developer.android.com/guide/topics/ui/look-and-feel/downloadable-fonts + +License +------- +Please read the full license text (OFL.txt) to understand the permissions, +restrictions and requirements for usage, redistribution, and modification. + +You can use them in your products & projects โ€“ print or digital, +commercial or otherwise. + +This isn't legal advice, please consider consulting a lawyer and see the full +license for all details. diff --git a/extension/media/Lexend/static/Lexend-Black.ttf b/extension/media/Lexend/static/Lexend-Black.ttf new file mode 100644 index 00000000..2fea087c Binary files /dev/null and b/extension/media/Lexend/static/Lexend-Black.ttf differ diff --git a/extension/media/Lexend/static/Lexend-Bold.ttf b/extension/media/Lexend/static/Lexend-Bold.ttf new file mode 100644 index 00000000..95884f6e Binary files /dev/null and b/extension/media/Lexend/static/Lexend-Bold.ttf differ diff --git a/extension/media/Lexend/static/Lexend-ExtraBold.ttf b/extension/media/Lexend/static/Lexend-ExtraBold.ttf new file mode 100644 index 00000000..02f84ed3 Binary files /dev/null and b/extension/media/Lexend/static/Lexend-ExtraBold.ttf differ diff --git a/extension/media/Lexend/static/Lexend-ExtraLight.ttf b/extension/media/Lexend/static/Lexend-ExtraLight.ttf new file mode 100644 index 00000000..20e7068d Binary files /dev/null and b/extension/media/Lexend/static/Lexend-ExtraLight.ttf differ diff --git a/extension/media/Lexend/static/Lexend-Light.ttf b/extension/media/Lexend/static/Lexend-Light.ttf new file mode 100644 index 00000000..fb6d097c Binary files /dev/null and b/extension/media/Lexend/static/Lexend-Light.ttf differ diff --git a/extension/media/Lexend/static/Lexend-Medium.ttf b/extension/media/Lexend/static/Lexend-Medium.ttf new file mode 100644 index 00000000..d91a8673 Binary files /dev/null and b/extension/media/Lexend/static/Lexend-Medium.ttf differ diff --git a/extension/media/Lexend/static/Lexend-Regular.ttf b/extension/media/Lexend/static/Lexend-Regular.ttf new file mode 100644 index 00000000..b423d3ab Binary files /dev/null and b/extension/media/Lexend/static/Lexend-Regular.ttf differ diff --git a/extension/media/Lexend/static/Lexend-SemiBold.ttf b/extension/media/Lexend/static/Lexend-SemiBold.ttf new file mode 100644 index 00000000..9dcb8214 Binary files /dev/null and b/extension/media/Lexend/static/Lexend-SemiBold.ttf differ diff --git a/extension/media/Lexend/static/Lexend-Thin.ttf b/extension/media/Lexend/static/Lexend-Thin.ttf new file mode 100644 index 00000000..0d7df881 Binary files /dev/null and b/extension/media/Lexend/static/Lexend-Thin.ttf differ diff --git a/extension/react-app/package-lock.json b/extension/react-app/package-lock.json index fb13dffd..7316581d 100644 --- a/extension/react-app/package-lock.json +++ b/extension/react-app/package-lock.json @@ -20,6 +20,7 @@ "react-redux": "^8.0.5", "react-switch": "^7.0.0", "react-syntax-highlighter": "^15.5.0", + "react-tooltip": "^5.18.0", "styled-components": "^5.3.6", "vscode-webview": "^1.0.1-beta.1" }, @@ -597,6 +598,19 @@ "node": ">=12" } }, + "node_modules/@floating-ui/core": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.3.1.tgz", + "integrity": "sha512-Bu+AMaXNjrpjh41znzHqaz3r2Nr8hHuHZT6V2LBKMhyMl0FgKA62PNYbqnfgmzOhoWZj70Zecisbo4H1rotP5g==" + }, + "node_modules/@floating-ui/dom": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.4.4.tgz", + "integrity": "sha512-21hhDEPOiWkGp0Ys4Wi6Neriah7HweToKra626CIK712B5m9qkdz54OP9gVldUg+URnBTpv/j/bi/skmGdstXQ==", + "dependencies": { + "@floating-ui/core": "^1.3.1" + } + }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", @@ -1310,6 +1324,11 @@ "node": ">= 6" } }, + "node_modules/classnames": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.2.tgz", + "integrity": "sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw==" + }, "node_modules/color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -2967,6 +2986,19 @@ "react": ">= 0.14.0" } }, + "node_modules/react-tooltip": { + "version": "5.18.0", + "resolved": "https://registry.npmjs.org/react-tooltip/-/react-tooltip-5.18.0.tgz", + "integrity": "sha512-qjDK/skUJJ27sc9lTWeNxp2rLzmenBTskSsRiDOCPnupGSz2GhL5IZxDizK/sOsk0hn5iSCywt+3jKxUJ3Y4Sw==", + "dependencies": { + "@floating-ui/dom": "^1.0.0", + "classnames": "^2.3.0" + }, + "peerDependencies": { + "react": ">=16.14.0", + "react-dom": ">=16.14.0" + } + }, "node_modules/read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", @@ -3886,6 +3918,19 @@ "dev": true, "optional": true }, + "@floating-ui/core": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.3.1.tgz", + "integrity": "sha512-Bu+AMaXNjrpjh41znzHqaz3r2Nr8hHuHZT6V2LBKMhyMl0FgKA62PNYbqnfgmzOhoWZj70Zecisbo4H1rotP5g==" + }, + "@floating-ui/dom": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.4.4.tgz", + "integrity": "sha512-21hhDEPOiWkGp0Ys4Wi6Neriah7HweToKra626CIK712B5m9qkdz54OP9gVldUg+URnBTpv/j/bi/skmGdstXQ==", + "requires": { + "@floating-ui/core": "^1.3.1" + } + }, "@jridgewell/gen-mapping": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", @@ -4350,6 +4395,11 @@ } } }, + "classnames": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.2.tgz", + "integrity": "sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw==" + }, "color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -5411,6 +5461,15 @@ "refractor": "^3.6.0" } }, + "react-tooltip": { + "version": "5.18.0", + "resolved": "https://registry.npmjs.org/react-tooltip/-/react-tooltip-5.18.0.tgz", + "integrity": "sha512-qjDK/skUJJ27sc9lTWeNxp2rLzmenBTskSsRiDOCPnupGSz2GhL5IZxDizK/sOsk0hn5iSCywt+3jKxUJ3Y4Sw==", + "requires": { + "@floating-ui/dom": "^1.0.0", + "classnames": "^2.3.0" + } + }, "read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", diff --git a/extension/react-app/package.json b/extension/react-app/package.json index 12701906..4bedb813 100644 --- a/extension/react-app/package.json +++ b/extension/react-app/package.json @@ -21,6 +21,7 @@ "react-redux": "^8.0.5", "react-switch": "^7.0.0", "react-syntax-highlighter": "^15.5.0", + "react-tooltip": "^5.18.0", "styled-components": "^5.3.6", "vscode-webview": "^1.0.1-beta.1" }, diff --git a/extension/react-app/src/App.tsx b/extension/react-app/src/App.tsx index a51541d0..8785f88f 100644 --- a/extension/react-app/src/App.tsx +++ b/extension/react-app/src/App.tsx @@ -1,28 +1,33 @@ import DebugPanel from "./components/DebugPanel"; import MainTab from "./tabs/main"; -import { Provider } from "react-redux"; -import store from "./redux/store"; import WelcomeTab from "./tabs/welcome"; import ChatTab from "./tabs/chat"; import GUI from "./tabs/gui"; +import { createContext } from "react"; +import useContinueGUIProtocol from "./hooks/useWebsocket"; +import ContinueGUIClientProtocol from "./hooks/useContinueGUIProtocol"; + +export const GUIClientContext = createContext< + ContinueGUIClientProtocol | undefined +>(undefined); function App() { + const client = useContinueGUIProtocol(); + return ( - <> - - , - title: "GUI", - }, - // { element: , title: "Debug Panel" }, - // { element: , title: "Welcome" }, - // { element: , title: "Chat" }, - ]} - > - - + + , + title: "GUI", + }, + // { element: , title: "Debug Panel" }, + // { element: , title: "Welcome" }, + // { element: , title: "Chat" }, + ]} + /> + ); } diff --git a/extension/react-app/src/components/CodeBlock.tsx b/extension/react-app/src/components/CodeBlock.tsx index 17f5626b..fe9b3a95 100644 --- a/extension/react-app/src/components/CodeBlock.tsx +++ b/extension/react-app/src/components/CodeBlock.tsx @@ -52,9 +52,9 @@ function CopyButton(props: { textToCopy: string; visible: boolean }) { }} > {clicked ? ( - + ) : ( - + )} diff --git a/extension/react-app/src/components/ComboBox.tsx b/extension/react-app/src/components/ComboBox.tsx index 81b148b9..7ee5dc24 100644 --- a/extension/react-app/src/components/ComboBox.tsx +++ b/extension/react-app/src/components/ComboBox.tsx @@ -4,6 +4,7 @@ import styled from "styled-components"; import { buttonColor, defaultBorderRadius, + lightGray, secondaryDark, vscBackground, } from "."; @@ -16,11 +17,32 @@ import { LockClosed, LockOpen, Plus, + DocumentPlus, } from "@styled-icons/heroicons-outline"; +import { HighlightedRangeContext } from "../../../schema/FullState"; // #region styled components const mainInputFontSize = 16; +const EmptyPillDiv = styled.div` + padding: 8px; + border-radius: ${defaultBorderRadius}; + border: 1px dashed ${lightGray}; + color: ${lightGray}; + background-color: ${vscBackground}; + overflow: hidden; + display: flex; + align-items: center; + text-align: center; + cursor: pointer; + font-size: 13px; + + &:hover { + background-color: ${lightGray}; + color: ${vscBackground}; + } +`; + const ContextDropdown = styled.div` position: absolute; padding: 4px; @@ -41,17 +63,19 @@ const MainTextInput = styled.textarea` padding: 8px; font-size: ${mainInputFontSize}px; + font-family: inherit; + border: 1px solid transparent; border-radius: ${defaultBorderRadius}; - border: 1px solid white; margin: 8px auto; + height: auto; width: 100%; - background-color: ${vscBackground}; + background-color: ${secondaryDark}; color: white; z-index: 1; &:focus { + outline: 1px solid #ff000066; border: 1px solid transparent; - outline: 1px solid orange; } `; @@ -69,7 +93,6 @@ const Ul = styled.ul<{ background: ${vscBackground}; background-color: ${secondaryDark}; color: white; - font-family: "Fira Code", monospace; max-height: ${UlMaxHeight}px; overflow: scroll; padding: 0; @@ -102,7 +125,7 @@ interface ComboBoxProps { onInputValueChange: (inputValue: string) => void; disabled?: boolean; onEnter: (e: React.KeyboardEvent) => void; - highlightedCodeSections: (RangeInFile & { contents: string })[]; + highlightedCodeSections: HighlightedRangeContext[]; deleteContextItems: (indices: number[]) => void; onTogglePin: () => void; onToggleAddContext: () => void; @@ -119,16 +142,7 @@ const ComboBox = React.forwardRef((props: ComboBoxProps, ref) => { React.useState(false); const [pinned, setPinned] = useState(false); const [highlightedCodeSections, setHighlightedCodeSections] = React.useState( - props.highlightedCodeSections || [ - { - filepath: "test.ts", - range: { - start: { line: 0, character: 0 }, - end: { line: 0, character: 0 }, - }, - contents: "import * as a from 'a';", - }, - ] + props.highlightedCodeSections || [] ); useEffect(() => { @@ -169,6 +183,71 @@ const ComboBox = React.forwardRef((props: ComboBoxProps, ref) => { return ( <> +
+ {highlightedCodeSections.length > 1 && ( + <> + { + props.deleteContextItems( + highlightedCodeSections.map((_, idx) => idx) + ); + }} + > + + + + )} + {highlightedCodeSections.map((section, idx) => ( + { + if (props.deleteContextItems) { + props.deleteContextItems([idx]); + } + setHighlightedCodeSections((prev) => { + const newSections = [...prev]; + newSections.splice(idx, 1); + return newSections; + }); + }} + onHover={(val: boolean) => { + if (val) { + setHoveringButton(val); + } else { + setTimeout(() => { + setHoveringButton(val); + }, 100); + } + }} + /> + ))} + {props.highlightedCodeSections.length > 0 && + (props.addingHighlightedCode ? ( + { + props.onToggleAddContext(); + }} + > + Highlight to Add Context + + ) : ( + { + props.onToggleAddContext(); + }} + > + + + ))} +
-
- {highlightedCodeSections.length === 0 && ( - { - props.onToggleAddContext(); - }} - inverted={props.addingHighlightedCode} - > - - - )} - {highlightedCodeSections.length > 0 && ( - <> - { - props.deleteContextItems( - highlightedCodeSections.map((_, idx) => idx) - ); - }} - > - - - { - setPinned((prev) => !prev); - props.onTogglePin(); - }} - > - {pinned ? ( - - ) : ( - - )} - - - )} - {highlightedCodeSections.map((section, idx) => ( - { - if (props.deleteContextItems) { - props.deleteContextItems([idx]); - } - setHighlightedCodeSections((prev) => { - const newSections = [...prev]; - newSections.splice(idx, 1); - return newSections; - }); - }} - onHover={(val: boolean) => { - if (val) { - setHoveringButton(val); - } else { - setTimeout(() => { - setHoveringButton(val); - }, 100); - } - }} - /> - ))} - - - Highlight code to include as context. Currently open file included by - default. {highlightedCodeSections.length === 0 && ""} - -
+ {/* + Highlight code to include as context. Currently open file included by + default. {highlightedCodeSections.length === 0 && ""} + */} { setHoveringContextDropdown(true); @@ -345,9 +354,9 @@ const ComboBox = React.forwardRef((props: ComboBoxProps, ref) => { > {highlightedCodeSections.map((section, idx) => ( <> -

{section.filepath}

+

{section.range.filepath}

- {section.contents} + {section.range.contents} ))} diff --git a/extension/react-app/src/components/ContinueButton.tsx b/extension/react-app/src/components/ContinueButton.tsx index 5295799a..462f2b46 100644 --- a/extension/react-app/src/components/ContinueButton.tsx +++ b/extension/react-app/src/components/ContinueButton.tsx @@ -18,7 +18,7 @@ let StyledButton = styled(Button)` &:hover { transition-delay: 0.5s; - transition-property: background; + transition-property: "background"; background: linear-gradient( 45deg, #be1a55 14.44%, diff --git a/extension/react-app/src/components/HeaderButtonWithText.tsx b/extension/react-app/src/components/HeaderButtonWithText.tsx index 72a653c5..de8e3c98 100644 --- a/extension/react-app/src/components/HeaderButtonWithText.tsx +++ b/extension/react-app/src/components/HeaderButtonWithText.tsx @@ -1,6 +1,7 @@ import React, { useState } from "react"; - -import { HeaderButton } from "."; +import { Tooltip } from "react-tooltip"; +import styled from "styled-components"; +import { HeaderButton, StyledTooltip, defaultBorderRadius } from "."; interface HeaderButtonWithTextProps { text: string; @@ -13,25 +14,28 @@ interface HeaderButtonWithTextProps { const HeaderButtonWithText = (props: HeaderButtonWithTextProps) => { const [hover, setHover] = useState(false); - const paddingLeft = (props.disabled ? (props.active ? "3px" : "1px"): (hover ? "4px" : "1px")); return ( - { - if (!props.disabled) { - setHover(true); - } - }} - onMouseLeave={() => { - setHover(false); - }} - onClick={props.onClick} - > - - {props.children} - + <> + { + if (!props.disabled) { + setHover(true); + } + }} + onMouseLeave={() => { + setHover(false); + }} + onClick={props.onClick} + > + {props.children} + + + {props.text} + + ); }; diff --git a/extension/react-app/src/components/Loader.tsx b/extension/react-app/src/components/Loader.tsx new file mode 100644 index 00000000..90eff793 --- /dev/null +++ b/extension/react-app/src/components/Loader.tsx @@ -0,0 +1,40 @@ +import { Play } from "@styled-icons/heroicons-outline"; +import { useSelector } from "react-redux"; +import styled from "styled-components"; +import { RootStore } from "../redux/store"; + +const DEFAULT_SIZE = "28px"; + +const FlashingDiv = styled.div` + margin: auto; + width: ${DEFAULT_SIZE}; + animation: flash 1.2s infinite ease-in-out; + @keyframes flash { + 0% { + opacity: 0.4; + } + 50% { + opacity: 1; + } + 100% { + opacity: 0.4; + } + } +`; + +function Loader(props: { size?: string }) { + const vscMediaUrl = useSelector( + (state: RootStore) => state.config.vscMediaUrl + ); + return ( + + {vscMediaUrl ? ( + + ) : ( + + )} + + ); +} + +export default Loader; diff --git a/extension/react-app/src/components/PillButton.tsx b/extension/react-app/src/components/PillButton.tsx index 5a02c6b2..a384832e 100644 --- a/extension/react-app/src/components/PillButton.tsx +++ b/extension/react-app/src/components/PillButton.tsx @@ -1,54 +1,136 @@ -import { useState } from "react"; +import { useContext, useState } from "react"; import styled from "styled-components"; -import { defaultBorderRadius } from "."; -import { XMark } from "@styled-icons/heroicons-outline"; +import { + StyledTooltip, + defaultBorderRadius, + lightGray, + secondaryDark, +} from "."; +import { Trash, PaintBrush, MapPin } from "@styled-icons/heroicons-outline"; +import { GUIClientContext } from "../App"; const Button = styled.button` border: none; color: white; - background-color: transparent; - border: 1px solid white; + background-color: ${secondaryDark}; border-radius: ${defaultBorderRadius}; - padding: 3px 6px; + padding: 8px; + overflow: hidden; + + cursor: pointer; +`; + +const GridDiv = styled.div` + position: absolute; + left: 0px; + top: 0px; + width: 100%; + height: 100%; + display: grid; + grid-gap: 0; + grid-template-columns: 1fr 1fr; + align-items: center; + border-radius: ${defaultBorderRadius}; + overflow: hidden; + + background-color: ${secondaryDark}; +`; + +const ButtonDiv = styled.div<{ backgroundColor: string }>` + background-color: ${secondaryDark}; + padding: 3px; + height: 100%; + display: flex; + align-items: center; &:hover { - background-color: white; - color: black; + background-color: ${(props) => props.backgroundColor}; } - - cursor: pointer; `; interface PillButtonProps { onHover?: (arg0: boolean) => void; onDelete?: () => void; title: string; + index: number; + editing: boolean; + pinned: boolean; } const PillButton = (props: PillButtonProps) => { const [isHovered, setIsHovered] = useState(false); + const client = useContext(GUIClientContext); + return ( - + <> + + + {props.editing ? "Editing this range" : "Edit this range"} + + Delete + ); }; diff --git a/extension/react-app/src/components/StepContainer.tsx b/extension/react-app/src/components/StepContainer.tsx index 2aed2e72..91d7b8ef 100644 --- a/extension/react-app/src/components/StepContainer.tsx +++ b/extension/react-app/src/components/StepContainer.tsx @@ -13,7 +13,7 @@ import { ArrowPath, XMark, } from "@styled-icons/heroicons-outline"; -import { Stop } from "@styled-icons/heroicons-solid"; +import { StopCircle } from "@styled-icons/heroicons-solid"; import { HistoryNode } from "../../../schema/HistoryNode"; import ReactMarkdown from "react-markdown"; import HeaderButtonWithText from "./HeaderButtonWithText"; @@ -67,7 +67,6 @@ const HeaderDiv = styled.div<{ error: boolean; loading: boolean }>` const ContentDiv = styled.div<{ isUserInput: boolean }>` padding: 8px; - padding-left: 16px; background-color: ${(props) => props.isUserInput ? secondaryDark : vscBackground}; font-size: 13px; @@ -167,7 +166,7 @@ function StepContainer(props: StepContainerProps) { ? "#f00" : props.historyNode.active ? undefined - : "white" + : "transparent" } className="overflow-hidden cursor-pointer" onClick={(e) => { @@ -182,7 +181,7 @@ function StepContainer(props: StepContainerProps) { loading={props.historyNode.active as boolean | false} error={props.historyNode.observation?.error ? true : false} > -

+
{!isUserInput && (props.open ? ( @@ -191,7 +190,7 @@ function StepContainer(props: StepContainerProps) { ))} {props.historyNode.observation?.title || (props.historyNode.step.name as any)} -

+ {/* { e.stopPropagation(); @@ -203,16 +202,14 @@ function StepContainer(props: StepContainerProps) { <> { e.stopPropagation(); props.onDelete(); }} text={props.historyNode.active ? "Stop" : "Delete"} - active={props.historyNode.active} > {props.historyNode.active ? ( - + ) : ( )} diff --git a/extension/react-app/src/components/TextDialog.tsx b/extension/react-app/src/components/TextDialog.tsx index a564f884..ea5727f0 100644 --- a/extension/react-app/src/components/TextDialog.tsx +++ b/extension/react-app/src/components/TextDialog.tsx @@ -8,6 +8,7 @@ const ScreenCover = styled.div` width: 100%; height: 100%; background-color: rgba(168, 168, 168, 0.5); + z-index: 100; `; const DialogContainer = styled.div` @@ -35,7 +36,6 @@ const TextArea = styled.textarea` border-radius: 8px; padding: 8px; outline: 1px solid black; - font-family: Arial, Helvetica, sans-serif; resize: none; &:focus { diff --git a/extension/react-app/src/components/UserInputContainer.tsx b/extension/react-app/src/components/UserInputContainer.tsx index 28437d35..f51f0cb5 100644 --- a/extension/react-app/src/components/UserInputContainer.tsx +++ b/extension/react-app/src/components/UserInputContainer.tsx @@ -15,12 +15,10 @@ interface UserInputContainerProps { } const StyledDiv = styled.div` - background-color: rgb(45 45 45); + background-color: ${secondaryDark}; padding: 8px; padding-left: 16px; padding-right: 16px; - border-bottom: 1px solid white; - border-top: 1px solid white; font-size: 13px; display: flex; align-items: center; @@ -29,7 +27,7 @@ const StyledDiv = styled.div` const UserInputContainer = (props: UserInputContainerProps) => { return ( - {props.children} + {props.children}
{ diff --git a/extension/react-app/src/components/index.ts b/extension/react-app/src/components/index.ts index db1925ed..9ae0f097 100644 --- a/extension/react-app/src/components/index.ts +++ b/extension/react-app/src/components/index.ts @@ -1,7 +1,9 @@ +import { Tooltip } from "react-tooltip"; import styled, { keyframes } from "styled-components"; export const defaultBorderRadius = "5px"; -export const secondaryDark = "rgb(42 42 42)"; +export const lightGray = "rgb(100 100 100)"; +export const secondaryDark = "rgb(45 45 45)"; export const vscBackground = "rgb(30 30 30)"; export const vscBackgroundTransparent = "#1e1e1ede"; export const buttonColor = "rgb(113 28 59)"; @@ -26,6 +28,16 @@ export const Button = styled.button` } `; +export const StyledTooltip = styled(Tooltip)` + font-size: 12px; + background-color: rgb(60 60 60); + border-radius: ${defaultBorderRadius}; + padding: 6px; + padding-left: 12px; + padding-right: 12px; + z-index: 100; +`; + export const TextArea = styled.textarea` width: 100%; border-radius: ${defaultBorderRadius}; @@ -128,19 +140,17 @@ export const HeaderButton = styled.button<{ inverted: boolean | undefined }>` background-color: ${({ inverted }) => (inverted ? "white" : "transparent")}; color: ${({ inverted }) => (inverted ? "black" : "white")}; - border: 1px solid white; + border: none; border-radius: ${defaultBorderRadius}; cursor: pointer; &:hover { background-color: ${({ inverted }) => - typeof inverted === "undefined" || inverted ? "white" : "transparent"}; - color: ${({ inverted }) => - typeof inverted === "undefined" || inverted ? "black" : "white"}; + typeof inverted === "undefined" || inverted ? lightGray : "transparent"}; } display: flex; align-items: center; justify-content: center; gap: 4px; - padding: 1px; + padding: 2px; `; diff --git a/extension/react-app/src/hooks/ContinueGUIClientProtocol.ts b/extension/react-app/src/hooks/ContinueGUIClientProtocol.ts index f123bb2b..a179c2bf 100644 --- a/extension/react-app/src/hooks/ContinueGUIClientProtocol.ts +++ b/extension/react-app/src/hooks/ContinueGUIClientProtocol.ts @@ -23,6 +23,10 @@ abstract class AbstractContinueGUIClientProtocol { abstract deleteContextAtIndices(indices: number[]): void; + abstract setEditingAtIndices(indices: number[]): void; + + abstract setPinnedAtIndices(indices: number[]): void; + abstract toggleAddingHighlightedCode(): void; } diff --git a/extension/react-app/src/hooks/useContinueGUIProtocol.ts b/extension/react-app/src/hooks/useContinueGUIProtocol.ts index 49f200ae..2060dd7f 100644 --- a/extension/react-app/src/hooks/useContinueGUIProtocol.ts +++ b/extension/react-app/src/hooks/useContinueGUIProtocol.ts @@ -75,6 +75,14 @@ class ContinueGUIClientProtocol extends AbstractContinueGUIClientProtocol { this.messenger.send("delete_context_at_indices", { indices }); } + setEditingAtIndices(indices: number[]) { + this.messenger.send("set_editing_at_indices", { indices }); + } + + setPinnedAtIndices(indices: number[]) { + this.messenger.send("set_pinned_at_indices", { indices }); + } + toggleAddingHighlightedCode(): void { this.messenger.send("toggle_adding_highlighted_code", {}); } diff --git a/extension/react-app/src/index.css b/extension/react-app/src/index.css index 6dc514ec..682551f8 100644 --- a/extension/react-app/src/index.css +++ b/extension/react-app/src/index.css @@ -10,19 +10,12 @@ --def-border-radius: 5px; } -@font-face { - font-family: "Mona Sans"; - src: url("assets/Mona-Sans.woff2") format("woff2 supports variations"), - url("assets/Mona-Sans.woff2") format("woff2-variations"); - font-weight: 200 900; - font-stretch: 75% 85%; -} - html, body, #root { height: 100%; background-color: var(--vsc-background); + font-family: "Lexend", sans-serif; } body { diff --git a/extension/react-app/src/main.tsx b/extension/react-app/src/main.tsx index 0b02575c..a76bced6 100644 --- a/extension/react-app/src/main.tsx +++ b/extension/react-app/src/main.tsx @@ -1,6 +1,8 @@ import React from "react"; import ReactDOM from "react-dom/client"; import App from "./App"; +import { Provider } from "react-redux"; +import store from "./redux/store"; import "./index.css"; import posthog from "posthog-js"; @@ -17,7 +19,9 @@ posthog.init("phc_JS6XFROuNbhJtVCEdTSYk6gl5ArRrTNMpCcguAXlSPs", { ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render( - + + + ); diff --git a/extension/react-app/src/tabs/gui.tsx b/extension/react-app/src/tabs/gui.tsx index e5320c6a..3cce30de 100644 --- a/extension/react-app/src/tabs/gui.tsx +++ b/extension/react-app/src/tabs/gui.tsx @@ -1,11 +1,13 @@ import styled from "styled-components"; -import { defaultBorderRadius, Loader } from "../components"; +import { defaultBorderRadius } from "../components"; +import Loader from "../components/Loader"; import ContinueButton from "../components/ContinueButton"; -import { useCallback, useEffect, useRef, useState } from "react"; +import { FullState, HighlightedRangeContext } from "../../../schema/FullState"; +import { useCallback, useEffect, useRef, useState, useContext } from "react"; import { History } from "../../../schema/History"; import { HistoryNode } from "../../../schema/HistoryNode"; import StepContainer from "../components/StepContainer"; -import useContinueGUIProtocol from "../hooks/useWebsocket"; +import { GUIClientContext } from "../App"; import { BookOpen, ChatBubbleOvalLeftEllipsis, @@ -52,6 +54,7 @@ interface GUIProps { } function GUI(props: GUIProps) { + const client = useContext(GUIClientContext); const posthog = usePostHog(); const vscMachineId = useSelector( (state: RootStore) => state.config.vscMachineId @@ -70,7 +73,9 @@ function GUI(props: GUIProps) { const [usingFastModel, setUsingFastModel] = useState(false); const [waitingForSteps, setWaitingForSteps] = useState(false); const [userInputQueue, setUserInputQueue] = useState([]); - const [highlightedRanges, setHighlightedRanges] = useState([]); + const [highlightedRanges, setHighlightedRanges] = useState< + HighlightedRangeContext[] + >([]); const [addingHighlightedCode, setAddingHighlightedCode] = useState(false); const [availableSlashCommands, setAvailableSlashCommands] = useState< { name: string; description: string }[] @@ -112,7 +117,6 @@ function GUI(props: GUIProps) { const [feedbackDialogMessage, setFeedbackDialogMessage] = useState(""); const topGuiDivRef = useRef(null); - const client = useContinueGUIProtocol(); const [scrollTimeout, setScrollTimeout] = useState( null @@ -148,7 +152,7 @@ function GUI(props: GUIProps) { }, []); useEffect(() => { - client?.onStateUpdate((state) => { + client?.onStateUpdate((state: FullState) => { // Scroll only if user is at very bottom of the window. setUsingFastModel(state.default_model === "gpt-3.5-turbo"); const shouldScrollToBottom = @@ -289,7 +293,7 @@ function GUI(props: GUIProps) { > {typeof client === "undefined" && ( <> - +

Loading Continue server...

)} @@ -316,7 +320,8 @@ function GUI(props: GUIProps) { setStepsOpen(nextStepsOpen); }} onToggleAll={() => { - setStepsOpen((prev) => prev.map((_, index) => !prev[index])); + const shouldOpen = !stepsOpen[index]; + setStepsOpen((prev) => prev.map(() => shouldOpen)); }} key={index} onUserInput={(input: string) => { @@ -381,6 +386,7 @@ function GUI(props: GUIProps) { borderRadius: defaultBorderRadius, padding: "16px", margin: "16px", + zIndex: 100, }} hidden={!showDataSharingInfo} > diff --git a/extension/schema/FullState.d.ts b/extension/schema/FullState.d.ts new file mode 100644 index 00000000..981e772e --- /dev/null +++ b/extension/schema/FullState.d.ts @@ -0,0 +1,133 @@ +/* eslint-disable */ +/** + * This file was automatically generated by json-schema-to-typescript. + * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file, + * and run json-schema-to-typescript to regenerate this file. + */ + +export type FullState = FullState1; +export type Name = string; +export type Hide = boolean; +export type Description = string; +export type SystemMessage = string; +export type Role = "assistant" | "user" | "system" | "function"; +export type Content = string; +export type Name1 = string; +export type Summary = string; +export type Name2 = string; +export type Arguments = string; +export type ChatContext = ChatMessage[]; +export type ManageOwnChatContext = boolean; +export type Depth = number; +export type Deleted = boolean; +export type Active = boolean; +export type Timeline = HistoryNode[]; +export type CurrentIndex = number; +export type Active1 = boolean; +export type UserInputQueue = string[]; +export type DefaultModel = string; +export type Filepath = string; +export type Line = number; +export type Character = number; +export type Contents = string; +export type Editing = boolean; +export type Pinned = boolean; +export type HighlightedRanges = HighlightedRangeContext[]; +export type Name3 = string; +export type Description1 = string; +export type SlashCommands = SlashCommandDescription[]; +export type AddingHighlightedCode = boolean; + +/** + * A full state of the program, including the history + */ +export interface FullState1 { + history: History; + active: Active1; + user_input_queue: UserInputQueue; + default_model: DefaultModel; + highlighted_ranges: HighlightedRanges; + slash_commands: SlashCommands; + adding_highlighted_code: AddingHighlightedCode; + [k: string]: unknown; +} +/** + * A history of steps taken and their results + */ +export interface History { + timeline: Timeline; + current_index: CurrentIndex; + [k: string]: unknown; +} +/** + * A point in history, a list of which make up History + */ +export interface HistoryNode { + step: Step; + observation?: Observation; + depth: Depth; + deleted?: Deleted; + active?: Active; + [k: string]: unknown; +} +export interface Step { + name?: Name; + hide?: Hide; + description?: Description; + system_message?: SystemMessage; + chat_context?: ChatContext; + manage_own_chat_context?: ManageOwnChatContext; + [k: string]: unknown; +} +export interface ChatMessage { + role: Role; + content?: Content; + name?: Name1; + summary: Summary; + function_call?: FunctionCall; + [k: string]: unknown; +} +export interface FunctionCall { + name: Name2; + arguments: Arguments; + [k: string]: unknown; +} +export interface Observation { + [k: string]: unknown; +} +/** + * Context for a highlighted range + */ +export interface HighlightedRangeContext { + range: RangeInFileWithContents; + editing: Editing; + pinned: Pinned; + [k: string]: unknown; +} +/** + * A range in a file with the contents of the range. + */ +export interface RangeInFileWithContents { + filepath: Filepath; + range: Range; + contents: Contents; + [k: string]: unknown; +} +/** + * A range in a file. 0-indexed. + */ +export interface Range { + start: Position; + end: Position; + [k: string]: unknown; +} +export interface Position { + line: Line; + character: Character; + [k: string]: unknown; +} +export interface SlashCommandDescription { + name: Name3; + description: Description1; + [k: string]: unknown; +} diff --git a/extension/src/debugPanel.ts b/extension/src/debugPanel.ts index b88c86f3..487bbedf 100644 --- a/extension/src/debugPanel.ts +++ b/extension/src/debugPanel.ts @@ -341,6 +341,10 @@ export function setupDebugPanel( + + + + Continue diff --git a/schema/json/FullState.json b/schema/json/FullState.json new file mode 100644 index 00000000..af0f25e1 --- /dev/null +++ b/schema/json/FullState.json @@ -0,0 +1,304 @@ +{ + "title": "FullState", + "$ref": "#/definitions/src__continuedev__core__main__FullState", + "definitions": { + "FunctionCall": { + "title": "FunctionCall", + "type": "object", + "properties": { + "name": { + "title": "Name", + "type": "string" + }, + "arguments": { + "title": "Arguments", + "type": "string" + } + }, + "required": [ + "name", + "arguments" + ] + }, + "ChatMessage": { + "title": "ChatMessage", + "type": "object", + "properties": { + "role": { + "title": "Role", + "enum": [ + "assistant", + "user", + "system", + "function" + ], + "type": "string" + }, + "content": { + "title": "Content", + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" + }, + "summary": { + "title": "Summary", + "type": "string" + }, + "function_call": { + "$ref": "#/definitions/FunctionCall" + } + }, + "required": [ + "role", + "summary" + ] + }, + "Step": { + "title": "Step", + "type": "object", + "properties": { + "name": { + "title": "Name", + "type": "string" + }, + "hide": { + "title": "Hide", + "default": false, + "type": "boolean" + }, + "description": { + "title": "Description", + "type": "string" + }, + "system_message": { + "title": "System Message", + "type": "string" + }, + "chat_context": { + "title": "Chat Context", + "default": [], + "type": "array", + "items": { + "$ref": "#/definitions/ChatMessage" + } + }, + "manage_own_chat_context": { + "title": "Manage Own Chat Context", + "default": false, + "type": "boolean" + } + } + }, + "Observation": { + "title": "Observation", + "type": "object", + "properties": {} + }, + "HistoryNode": { + "title": "HistoryNode", + "description": "A point in history, a list of which make up History", + "type": "object", + "properties": { + "step": { + "$ref": "#/definitions/Step" + }, + "observation": { + "$ref": "#/definitions/Observation" + }, + "depth": { + "title": "Depth", + "type": "integer" + }, + "deleted": { + "title": "Deleted", + "default": false, + "type": "boolean" + }, + "active": { + "title": "Active", + "default": true, + "type": "boolean" + } + }, + "required": [ + "step", + "depth" + ] + }, + "History": { + "title": "History", + "description": "A history of steps taken and their results", + "type": "object", + "properties": { + "timeline": { + "title": "Timeline", + "type": "array", + "items": { + "$ref": "#/definitions/HistoryNode" + } + }, + "current_index": { + "title": "Current Index", + "type": "integer" + } + }, + "required": [ + "timeline", + "current_index" + ] + }, + "Position": { + "title": "Position", + "type": "object", + "properties": { + "line": { + "title": "Line", + "type": "integer" + }, + "character": { + "title": "Character", + "type": "integer" + } + }, + "required": [ + "line", + "character" + ] + }, + "Range": { + "title": "Range", + "description": "A range in a file. 0-indexed.", + "type": "object", + "properties": { + "start": { + "$ref": "#/definitions/Position" + }, + "end": { + "$ref": "#/definitions/Position" + } + }, + "required": [ + "start", + "end" + ] + }, + "RangeInFileWithContents": { + "title": "RangeInFileWithContents", + "description": "A range in a file with the contents of the range.", + "type": "object", + "properties": { + "filepath": { + "title": "Filepath", + "type": "string" + }, + "range": { + "$ref": "#/definitions/Range" + }, + "contents": { + "title": "Contents", + "type": "string" + } + }, + "required": [ + "filepath", + "range", + "contents" + ] + }, + "HighlightedRangeContext": { + "title": "HighlightedRangeContext", + "description": "Context for a highlighted range", + "type": "object", + "properties": { + "range": { + "$ref": "#/definitions/RangeInFileWithContents" + }, + "editing": { + "title": "Editing", + "type": "boolean" + }, + "pinned": { + "title": "Pinned", + "type": "boolean" + } + }, + "required": [ + "range", + "editing", + "pinned" + ] + }, + "SlashCommandDescription": { + "title": "SlashCommandDescription", + "type": "object", + "properties": { + "name": { + "title": "Name", + "type": "string" + }, + "description": { + "title": "Description", + "type": "string" + } + }, + "required": [ + "name", + "description" + ] + }, + "src__continuedev__core__main__FullState": { + "title": "FullState", + "description": "A full state of the program, including the history", + "type": "object", + "properties": { + "history": { + "$ref": "#/definitions/History" + }, + "active": { + "title": "Active", + "type": "boolean" + }, + "user_input_queue": { + "title": "User Input Queue", + "type": "array", + "items": { + "type": "string" + } + }, + "default_model": { + "title": "Default Model", + "type": "string" + }, + "highlighted_ranges": { + "title": "Highlighted Ranges", + "type": "array", + "items": { + "$ref": "#/definitions/HighlightedRangeContext" + } + }, + "slash_commands": { + "title": "Slash Commands", + "type": "array", + "items": { + "$ref": "#/definitions/SlashCommandDescription" + } + }, + "adding_highlighted_code": { + "title": "Adding Highlighted Code", + "type": "boolean" + } + }, + "required": [ + "history", + "active", + "user_input_queue", + "default_model", + "highlighted_ranges", + "slash_commands", + "adding_highlighted_code" + ] + } + } +} \ No newline at end of file -- cgit v1.2.3-70-g09d2 From 7207da47c790aab81f59e777b9d02769b46fb7f4 Mon Sep 17 00:00:00 2001 From: Nate Sesti Date: Thu, 6 Jul 2023 12:39:17 -0700 Subject: details --- continuedev/src/continuedev/core/autopilot.py | 18 +++++++++++++----- continuedev/src/continuedev/core/main.py | 2 +- continuedev/src/continuedev/steps/chat.py | 2 +- continuedev/src/continuedev/steps/core/core.py | 2 +- continuedev/src/continuedev/steps/custom_command.py | 7 ++++--- extension/package-lock.json | 4 ++-- extension/package.json | 2 +- extension/react-app/src/components/ComboBox.tsx | 10 ++++------ extension/react-app/src/components/ContinueButton.tsx | 12 ++---------- extension/react-app/src/components/PillButton.tsx | 4 +++- .../react-app/src/components/UserInputContainer.tsx | 3 ++- extension/react-app/src/index.css | 1 - extension/react-app/src/tabs/gui.tsx | 10 +++++++++- extension/src/lang-server/codeLens.ts | 4 ++-- 14 files changed, 45 insertions(+), 36 deletions(-) (limited to 'extension/react-app/src/components/PillButton.tsx') diff --git a/continuedev/src/continuedev/core/autopilot.py b/continuedev/src/continuedev/core/autopilot.py index acdc1f0d..4466a01e 100644 --- a/continuedev/src/continuedev/core/autopilot.py +++ b/continuedev/src/continuedev/core/autopilot.py @@ -151,15 +151,23 @@ class Autopilot(ContinueBaseModel): self._highlighted_ranges[0].editing = True async def handle_highlighted_code(self, range_in_files: List[RangeInFileWithContents]): - if not self._adding_highlighted_code and len(self._highlighted_ranges) > 0: - return # If un-highlighting, then remove the range - if len(self._highlighted_ranges) == 1 and len(range_in_files) == 1 and range_in_files[0].range.start == range_in_files[0].range.end: + if len(self._highlighted_ranges) == 1 and len(range_in_files) <= 1 and (len(range_in_files) == 0 or range_in_files[0].range.start == range_in_files[0].range.end): self._highlighted_ranges = [] await self.update_subscribers() return + # If not toggled to be adding context, only edit or add the first range + if not self._adding_highlighted_code and len(self._highlighted_ranges) > 0: + if len(range_in_files) == 0: + return + if range_in_files[0].range.overlaps_with(self._highlighted_ranges[0].range) and range_in_files[0].filepath == self._highlighted_ranges[0].range.filepath: + self._highlighted_ranges = [HighlightedRangeContext( + range=range_in_files[0].range, editing=True, pinned=False)] + await self.update_subscribers() + return + # Filter out rifs from ~/.continue/diffs folder range_in_files = [ rif for rif in range_in_files if not os.path.dirname(rif.filepath) == os.path.expanduser("~/.continue/diffs")] @@ -391,8 +399,8 @@ class Autopilot(ContinueBaseModel): return # Remove context unless pinned - self._highlighted_ranges = [ - hr for hr in self._highlighted_ranges if hr.pinned] + # self._highlighted_ranges = [ + # hr for hr in self._highlighted_ranges if hr.pinned] # await self._request_halt() # Just run the step that takes user input, and diff --git a/continuedev/src/continuedev/core/main.py b/continuedev/src/continuedev/core/main.py index 62cc4936..d8bacb1f 100644 --- a/continuedev/src/continuedev/core/main.py +++ b/continuedev/src/continuedev/core/main.py @@ -259,7 +259,7 @@ class Step(ContinueBaseModel): if self.description is not None: d["description"] = self.description else: - d["description"] = "`Step in progress...`" + d["description"] = "Step in progress..." return d @validator("name", pre=True, always=True) diff --git a/continuedev/src/continuedev/steps/chat.py b/continuedev/src/continuedev/steps/chat.py index 8f88244c..d310a498 100644 --- a/continuedev/src/continuedev/steps/chat.py +++ b/continuedev/src/continuedev/steps/chat.py @@ -20,7 +20,7 @@ openai.api_key = OPENAI_API_KEY class SimpleChatStep(Step): user_input: str - name: str = "Chat" + name: str = "Generating Response..." manage_own_chat_context: bool = True description: str = "" diff --git a/continuedev/src/continuedev/steps/core/core.py b/continuedev/src/continuedev/steps/core/core.py index cbf0fe5e..5577c49a 100644 --- a/continuedev/src/continuedev/steps/core/core.py +++ b/continuedev/src/continuedev/steps/core/core.py @@ -302,7 +302,7 @@ class DefaultModelEditCodeStep(Step): completion = "\n".join(lines) full_prefix_lines = full_file_contents_lines[:rif.range.start.line] - full_suffix_lines = full_file_contents_lines[rif.range.end.line + 1:] + full_suffix_lines = full_file_contents_lines[rif.range.end.line:] new_file_contents = "\n".join( full_prefix_lines) + "\n" + completion + "\n" + "\n".join(full_suffix_lines) await sdk.ide.showDiff(rif.filepath, new_file_contents) diff --git a/continuedev/src/continuedev/steps/custom_command.py b/continuedev/src/continuedev/steps/custom_command.py index b84e7b35..9d675091 100644 --- a/continuedev/src/continuedev/steps/custom_command.py +++ b/continuedev/src/continuedev/steps/custom_command.py @@ -1,16 +1,17 @@ from ..core.main import Step from ..core.sdk import ContinueSDK -from ..steps.chat import ChatWithFunctions +from ..steps.chat import ChatWithFunctions, SimpleChatStep class CustomCommandStep(Step): name: str prompt: str user_input: str - + hide: bool = True + async def describe(self): return self.prompt async def run(self, sdk: ContinueSDK): prompt_user_input = f"Task: {self.prompt}. Additional info: {self.user_input}" - await sdk.run_step(ChatWithFunctions(user_input=prompt_user_input)) \ No newline at end of file + await sdk.run_step(SimpleChatStep(user_input=prompt_user_input)) diff --git a/extension/package-lock.json b/extension/package-lock.json index b34d1c61..cb11c5a8 100644 --- a/extension/package-lock.json +++ b/extension/package-lock.json @@ -1,12 +1,12 @@ { "name": "continue", - "version": "0.0.114", + "version": "0.0.115", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "continue", - "version": "0.0.114", + "version": "0.0.115", "license": "Apache-2.0", "dependencies": { "@electron/rebuild": "^3.2.10", diff --git a/extension/package.json b/extension/package.json index 4c972e86..bbc83e4a 100644 --- a/extension/package.json +++ b/extension/package.json @@ -14,7 +14,7 @@ "displayName": "Continue", "pricing": "Free", "description": "The open-source coding autopilot", - "version": "0.0.114", + "version": "0.0.115", "publisher": "Continue", "engines": { "vscode": "^1.67.0" diff --git a/extension/react-app/src/components/ComboBox.tsx b/extension/react-app/src/components/ComboBox.tsx index af673b42..b825dc2a 100644 --- a/extension/react-app/src/components/ComboBox.tsx +++ b/extension/react-app/src/components/ComboBox.tsx @@ -22,7 +22,7 @@ import { import { HighlightedRangeContext } from "../../../schema/FullState"; // #region styled components -const mainInputFontSize = 16; +const mainInputFontSize = 13; const EmptyPillDiv = styled.div` padding: 8px; @@ -64,7 +64,6 @@ const MainTextInput = styled.textarea` padding: 8px; font-size: ${mainInputFontSize}px; font-family: inherit; - border: 1px solid transparent; border-radius: ${defaultBorderRadius}; margin: 8px auto; height: auto; @@ -74,8 +73,7 @@ const MainTextInput = styled.textarea` z-index: 1; &:focus { - outline: 1px solid #ff000066; - border: 1px solid transparent; + outline: 1px solid ${lightGray}; } `; @@ -191,7 +189,7 @@ const ComboBox = React.forwardRef((props: ComboBoxProps, ref) => { return ( <>
- {highlightedCodeSections.length > 1 && ( + {/* {highlightedCodeSections.length > 1 && ( <> { - )} + )} */} {highlightedCodeSections.map((section, idx) => ( { {props.title} - {props.editing ? "Editing this range" : "Edit this range"} + {props.editing + ? "Editing this range (with rest of file as context)" + : "Edit this range"} Delete diff --git a/extension/react-app/src/components/UserInputContainer.tsx b/extension/react-app/src/components/UserInputContainer.tsx index f51f0cb5..0ff3e74f 100644 --- a/extension/react-app/src/components/UserInputContainer.tsx +++ b/extension/react-app/src/components/UserInputContainer.tsx @@ -1,7 +1,7 @@ import React from "react"; import ReactMarkdown from "react-markdown"; import styled from "styled-components"; -import { buttonColor, secondaryDark } from "."; +import { buttonColor, secondaryDark, vscBackground } from "."; import HeaderButtonWithText from "./HeaderButtonWithText"; import { Play, XMark } from "@styled-icons/heroicons-outline"; import { RootStore } from "../redux/store"; @@ -22,6 +22,7 @@ const StyledDiv = styled.div` font-size: 13px; display: flex; align-items: center; + border-bottom: 1px solid ${vscBackground}; `; const UserInputContainer = (props: UserInputContainerProps) => { diff --git a/extension/react-app/src/index.css b/extension/react-app/src/index.css index 682551f8..6e33c89c 100644 --- a/extension/react-app/src/index.css +++ b/extension/react-app/src/index.css @@ -21,7 +21,6 @@ body, body { padding: 0; color: white; - font-family: "Mona Sans", "Arial", sans-serif; padding: 0px; margin: 0px; height: 100%; diff --git a/extension/react-app/src/tabs/gui.tsx b/extension/react-app/src/tabs/gui.tsx index ca369547..c8a42d9a 100644 --- a/extension/react-app/src/tabs/gui.tsx +++ b/extension/react-app/src/tabs/gui.tsx @@ -165,7 +165,15 @@ function GUI(props: GUIProps) { const shouldScrollToBottom = topGuiDivRef.current && topGuiDivRef.current?.offsetHeight - window.scrollY < 100; - setWaitingForSteps(state.active); + + const waitingForSteps = + state.active && + state.history.current_index < state.history.timeline.length && + state.history.timeline[ + state.history.current_index + ].step.description?.trim() === ""; + + setWaitingForSteps(waitingForSteps); setHistory(state.history); setHighlightedRanges(state.highlighted_ranges); setUserInputQueue(state.user_input_queue); diff --git a/extension/src/lang-server/codeLens.ts b/extension/src/lang-server/codeLens.ts index 381a0084..79126eaa 100644 --- a/extension/src/lang-server/codeLens.ts +++ b/extension/src/lang-server/codeLens.ts @@ -72,12 +72,12 @@ class DiffViewerCodeLensProvider implements vscode.CodeLensProvider { const range = new vscode.Range(0, 0, 1, 0); codeLenses.push( new vscode.CodeLens(range, { - title: "Accept โœ…", + title: "Accept โœ… (โŒ˜โ‡งโ†ฉ)", command: "continue.acceptDiff", arguments: [document.uri.fsPath], }), new vscode.CodeLens(range, { - title: "Reject โŒ", + title: "Reject โŒ (โŒ˜โ‡งโŒซ)", command: "continue.rejectDiff", arguments: [document.uri.fsPath], }) -- cgit v1.2.3-70-g09d2 From 39cd2ef27d6ed439b00a9edec4a487343ff1c2c9 Mon Sep 17 00:00:00 2001 From: Nate Sesti Date: Fri, 14 Jul 2023 03:24:46 -0700 Subject: warn of large highlighted ranges, cmd+k->m --- continuedev/src/continuedev/core/policy.py | 2 +- continuedev/src/continuedev/steps/help.py | 2 +- extension/package-lock.json | 4 +- extension/package.json | 6 +- extension/react-app/src/components/ComboBox.tsx | 5 + extension/react-app/src/components/Onboarding.tsx | 1 + extension/react-app/src/components/PillButton.tsx | 167 +++++++++++++--------- extension/react-app/src/pages/gui.tsx | 27 ++-- extension/src/commands.ts | 17 ++- extension/src/lang-server/codeLens.ts | 4 +- 10 files changed, 141 insertions(+), 94 deletions(-) (limited to 'extension/react-app/src/components/PillButton.tsx') diff --git a/continuedev/src/continuedev/core/policy.py b/continuedev/src/continuedev/core/policy.py index 59ea78b1..bc897357 100644 --- a/continuedev/src/continuedev/core/policy.py +++ b/continuedev/src/continuedev/core/policy.py @@ -59,7 +59,7 @@ class DemoPolicy(Policy): return ( MessageStep(name="Welcome to Continue", message=dedent("""\ - Highlight code and ask a question or give instructions - - Use `cmd+k` (Mac) / `ctrl+k` (Windows) to open Continue + - Use `cmd+m` (Mac) / `ctrl+m` (Windows) to open Continue - Use `/help` to ask questions about how to use Continue""")) >> WelcomeStep() >> # SetupContinueWorkspaceStep() >> diff --git a/continuedev/src/continuedev/steps/help.py b/continuedev/src/continuedev/steps/help.py index 2dc3647c..ba1e6087 100644 --- a/continuedev/src/continuedev/steps/help.py +++ b/continuedev/src/continuedev/steps/help.py @@ -19,7 +19,7 @@ help = dedent("""\ Continue passes all of the sections of code you highlight, the code above and below the to-be edited highlighted code section, and all previous steps above input box as context to the LLM. - You can use cmd+k (Mac) / ctrl+k (Windows) to open Continue. You can use cmd+shift+e / ctrl+shift+e to open file Explorer. You can add your own OpenAI API key to VS Code Settings with `cmd+,` + You can use cmd+m (Mac) / ctrl+m (Windows) to open Continue. You can use cmd+shift+e / ctrl+shift+e to open file Explorer. You can add your own OpenAI API key to VS Code Settings with `cmd+,` If Continue is stuck loading, try using `cmd+shift+p` to open the command palette, search "Reload Window", and then select it. This will reload VS Code and Continue and often fixes issues. diff --git a/extension/package-lock.json b/extension/package-lock.json index 9d5c73e1..a79dd6b4 100644 --- a/extension/package-lock.json +++ b/extension/package-lock.json @@ -1,12 +1,12 @@ { "name": "continue", - "version": "0.0.163", + "version": "0.0.164", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "continue", - "version": "0.0.163", + "version": "0.0.164", "license": "Apache-2.0", "dependencies": { "@electron/rebuild": "^3.2.10", diff --git a/extension/package.json b/extension/package.json index 2b0f6b94..de1f395d 100644 --- a/extension/package.json +++ b/extension/package.json @@ -14,7 +14,7 @@ "displayName": "Continue", "pricing": "Free", "description": "The open-source coding autopilot", - "version": "0.0.163", + "version": "0.0.164", "publisher": "Continue", "engines": { "vscode": "^1.67.0" @@ -111,8 +111,8 @@ "keybindings": [ { "command": "continue.focusContinueInput", - "mac": "cmd+k", - "key": "ctrl+k" + "mac": "cmd+m", + "key": "ctrl+m" }, { "command": "continue.suggestionDown", diff --git a/extension/react-app/src/components/ComboBox.tsx b/extension/react-app/src/components/ComboBox.tsx index 73db33ca..bd0d59b5 100644 --- a/extension/react-app/src/components/ComboBox.tsx +++ b/extension/react-app/src/components/ComboBox.tsx @@ -228,6 +228,11 @@ const ComboBox = React.forwardRef((props: ComboBoxProps, ref) => { )} */} {highlightedCodeSections.map((section, idx) => ( 4000 && section.editing + ? "Editing such a large range may be slow" + : undefined + } editing={section.editing} pinned={section.pinned} index={idx} diff --git a/extension/react-app/src/components/Onboarding.tsx b/extension/react-app/src/components/Onboarding.tsx index 6bfb0ccd..231c1e93 100644 --- a/extension/react-app/src/components/Onboarding.tsx +++ b/extension/react-app/src/components/Onboarding.tsx @@ -109,6 +109,7 @@ const Onboarding = () => { paddingBottom: "50px", textAlign: "center", cursor: "pointer", + whiteSpace: "nowrap", }} > ` } `; +const CircleDiv = styled.div` + position: absolute; + top: -10px; + right: -10px; + width: 20px; + height: 20px; + border-radius: 50%; + background-color: red; + color: white; + display: flex; + align-items: center; + justify-content: center; + padding: 2px; +`; + interface PillButtonProps { onHover?: (arg0: boolean) => void; onDelete?: () => void; @@ -55,6 +68,7 @@ interface PillButtonProps { index: number; editing: boolean; pinned: boolean; + warning?: string; } const PillButton = (props: PillButtonProps) => { @@ -63,75 +77,96 @@ const PillButton = (props: PillButtonProps) => { return ( <> - + + {props.editing + ? "Editing this range (with rest of file as context)" + : "Edit this range"} + + Delete + {props.warning && ( + <> + + + + + {props.warning} - { - if (props.onDelete) { - props.onDelete(); - } - }} - > - - - + )} - {props.title} - - - {props.editing - ? "Editing this range (with rest of file as context)" - : "Edit this range"} - - Delete +
); }; diff --git a/extension/react-app/src/pages/gui.tsx b/extension/react-app/src/pages/gui.tsx index 4ff260fa..57cebac3 100644 --- a/extension/react-app/src/pages/gui.tsx +++ b/extension/react-app/src/pages/gui.tsx @@ -95,11 +95,8 @@ function GUI(props: GUIProps) { name: "Welcome to Continue", hide: false, description: `- Highlight code and ask a question or give instructions -- Use \`cmd+k\` (Mac) / \`ctrl+k\` (Windows) to open Continue -- Use \`cmd+shift+e\` / \`ctrl+shift+e\` to open file Explorer -- Add your own OpenAI API key to VS Code Settings with \`cmd+,\` -- Use slash commands when you want fine-grained control -- Past steps are included as part of the context by default`, + - Use \`cmd+m\` (Mac) / \`ctrl+m\` (Windows) to open Continue + - Use \`/help\` to ask questions about how to use Continue`, system_message: null, chat_context: [], manage_own_chat_context: false, @@ -269,15 +266,17 @@ function GUI(props: GUIProps) { return ( <> - { - client?.sendMainInput(`/feedback ${text}`); - setShowFeedbackDialog(false); - }} - onClose={() => { - setShowFeedbackDialog(false); - }} - message={feedbackDialogMessage} /> + { + client?.sendMainInput(`/feedback ${text}`); + setShowFeedbackDialog(false); + }} + onClose={() => { + setShowFeedbackDialog(false); + }} + message={feedbackDialogMessage} + /> any } = { "continue.suggestionDown": suggestionDownCommand, @@ -30,10 +32,15 @@ const commandsMap: { [command: string]: (...args: any) => any } = { "continue.acceptAllSuggestions": acceptAllSuggestionsCommand, "continue.rejectAllSuggestions": rejectAllSuggestionsCommand, "continue.focusContinueInput": async () => { - vscode.commands.executeCommand("continue.continueGUIView.focus"); - debugPanelWebview?.postMessage({ - type: "focusContinueInput", - }); + if (focusedOnContinueInput) { + vscode.commands.executeCommand("workbench.action.focusActiveEditorGroup"); + } else { + vscode.commands.executeCommand("continue.continueGUIView.focus"); + debugPanelWebview?.postMessage({ + type: "focusContinueInput", + }); + } + focusedOnContinueInput = !focusedOnContinueInput; }, "continue.quickTextEntry": async () => { const text = await vscode.window.showInputBox({ @@ -53,4 +60,4 @@ export function registerAllCommands(context: vscode.ExtensionContext) { vscode.commands.registerCommand(command, callback) ); } -} \ No newline at end of file +} diff --git a/extension/src/lang-server/codeLens.ts b/extension/src/lang-server/codeLens.ts index 5800a00e..1cfef5d5 100644 --- a/extension/src/lang-server/codeLens.ts +++ b/extension/src/lang-server/codeLens.ts @@ -60,12 +60,12 @@ class DiffViewerCodeLensProvider implements vscode.CodeLensProvider { } codeLenses.push( new vscode.CodeLens(range, { - title: "Accept โœ… (โŒ˜โ‡งโ†ฉ)", + title: "Accept All โœ… (โŒ˜โ‡งโ†ฉ)", command: "continue.acceptDiff", arguments: [document.uri.fsPath], }), new vscode.CodeLens(range, { - title: "Reject โŒ (โŒ˜โ‡งโŒซ)", + title: "Reject All โŒ (โŒ˜โ‡งโŒซ)", command: "continue.rejectDiff", arguments: [document.uri.fsPath], }) -- cgit v1.2.3-70-g09d2 From 88c1f16c597e0a55271e622a5283562ccb7a80a1 Mon Sep 17 00:00:00 2001 From: Nate Sesti Date: Mon, 17 Jul 2023 00:00:31 -0700 Subject: only show delete when only one range selected --- extension/react-app/src/components/ComboBox.tsx | 2 ++ extension/react-app/src/components/PillButton.tsx | 34 +++++++++++++---------- 2 files changed, 22 insertions(+), 14 deletions(-) (limited to 'extension/react-app/src/components/PillButton.tsx') diff --git a/extension/react-app/src/components/ComboBox.tsx b/extension/react-app/src/components/ComboBox.tsx index 773fcf72..dbebd534 100644 --- a/extension/react-app/src/components/ComboBox.tsx +++ b/extension/react-app/src/components/ComboBox.tsx @@ -218,6 +218,7 @@ const ComboBox = React.forwardRef((props: ComboBoxProps, ref) => { ? "Editing such a large range may be slow" : undefined } + onlyShowDelete={highlightedCodeSections.length <= 1} editing={section.editing} pinned={section.pinned} index={idx} @@ -334,6 +335,7 @@ const ComboBox = React.forwardRef((props: ComboBoxProps, ref) => { })} showAbove={showAbove()} ulHeightPixels={ulRef.current?.getBoundingClientRect().height || 0} + hidden={!downshiftProps.isOpen || items.length === 0} > {downshiftProps.isOpen && items.map((item, index) => ( diff --git a/extension/react-app/src/components/PillButton.tsx b/extension/react-app/src/components/PillButton.tsx index d9d779d1..5a16516e 100644 --- a/extension/react-app/src/components/PillButton.tsx +++ b/extension/react-app/src/components/PillButton.tsx @@ -27,7 +27,6 @@ const GridDiv = styled.div` height: 100%; display: grid; grid-gap: 0; - grid-template-columns: 1fr 1fr; align-items: center; border-radius: ${defaultBorderRadius}; @@ -69,6 +68,7 @@ interface PillButtonProps { editing: boolean; pinned: boolean; warning?: string; + onlyShowDelete?: boolean; } const PillButton = (props: PillButtonProps) => { @@ -105,19 +105,25 @@ const PillButton = (props: PillButtonProps) => { }} > {isHovered && ( - - { - client?.setEditingAtIndices([props.index]); - }} - > - - + + {props.onlyShowDelete || ( + { + client?.setEditingAtIndices([props.index]); + }} + > + + + )} {/* Date: Mon, 17 Jul 2023 11:36:21 -0500 Subject: align on `code section` --- continuedev/src/continuedev/core/policy.py | 2 +- extension/react-app/src/components/ComboBox.tsx | 6 +++--- extension/react-app/src/components/PillButton.tsx | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) (limited to 'extension/react-app/src/components/PillButton.tsx') diff --git a/continuedev/src/continuedev/core/policy.py b/continuedev/src/continuedev/core/policy.py index bc897357..d007c92b 100644 --- a/continuedev/src/continuedev/core/policy.py +++ b/continuedev/src/continuedev/core/policy.py @@ -58,7 +58,7 @@ class DemoPolicy(Policy): if history.get_current() is None: return ( MessageStep(name="Welcome to Continue", message=dedent("""\ - - Highlight code and ask a question or give instructions + - Highlight code section and ask a question or give instructions - Use `cmd+m` (Mac) / `ctrl+m` (Windows) to open Continue - Use `/help` to ask questions about how to use Continue""")) >> WelcomeStep() >> diff --git a/extension/react-app/src/components/ComboBox.tsx b/extension/react-app/src/components/ComboBox.tsx index dbebd534..8136399a 100644 --- a/extension/react-app/src/components/ComboBox.tsx +++ b/extension/react-app/src/components/ComboBox.tsx @@ -245,11 +245,11 @@ const ComboBox = React.forwardRef((props: ComboBoxProps, ref) => { props.onToggleAddContext(); }} > - Highlight to Add Context + Highlight code section ) : ( { props.onToggleAddContext(); }} @@ -261,7 +261,7 @@ const ComboBox = React.forwardRef((props: ComboBoxProps, ref) => {