diff options
author | Ty Dunn <ty@tydunn.com> | 2023-07-04 21:37:39 -0700 |
---|---|---|
committer | Ty Dunn <ty@tydunn.com> | 2023-07-04 21:37:39 -0700 |
commit | 9dd364dc7f5a5a8513ee4805dfeb303076dc1274 (patch) | |
tree | e7e0a2b6b8e19b6c95d99bb2de99e77c4f0b7a79 /extension | |
parent | 458d2837163489ebaab0b21b3d6f3ab89c6a45d2 (diff) | |
parent | 35f5beabe55c59149ad0a6e4eb242fbc5352bf2e (diff) | |
download | sncontinue-9dd364dc7f5a5a8513ee4805dfeb303076dc1274.tar.gz sncontinue-9dd364dc7f5a5a8513ee4805dfeb303076dc1274.tar.bz2 sncontinue-9dd364dc7f5a5a8513ee4805dfeb303076dc1274.zip |
Merge branch 'main' into stop
Diffstat (limited to 'extension')
-rw-r--r-- | extension/package-lock.json | 4 | ||||
-rw-r--r-- | extension/package.json | 4 | ||||
-rw-r--r-- | extension/react-app/src/components/ComboBox.tsx | 53 | ||||
-rw-r--r-- | extension/react-app/src/components/HeaderButtonWithText.tsx | 2 | ||||
-rw-r--r-- | extension/react-app/src/components/PillButton.tsx | 10 | ||||
-rw-r--r-- | extension/react-app/src/components/StepContainer.tsx | 3 | ||||
-rw-r--r-- | extension/react-app/src/components/UserInputContainer.tsx | 6 | ||||
-rw-r--r-- | extension/react-app/src/components/index.ts | 13 | ||||
-rw-r--r-- | extension/react-app/src/hooks/ContinueGUIClientProtocol.ts | 2 | ||||
-rw-r--r-- | extension/react-app/src/hooks/useContinueGUIProtocol.ts | 4 | ||||
-rw-r--r-- | extension/react-app/src/tabs/gui.tsx | 31 | ||||
-rw-r--r-- | extension/src/continueIdeClient.ts | 67 |
12 files changed, 149 insertions, 50 deletions
diff --git a/extension/package-lock.json b/extension/package-lock.json index 4a3e01f2..2dd7d6d4 100644 --- a/extension/package-lock.json +++ b/extension/package-lock.json @@ -1,12 +1,12 @@ { "name": "continue", - "version": "0.0.106", + "version": "0.0.108", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "continue", - "version": "0.0.106", + "version": "0.0.108", "license": "Apache-2.0", "dependencies": { "@electron/rebuild": "^3.2.10", diff --git a/extension/package.json b/extension/package.json index f39e7bd7..52e7f891 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.108", "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 742c643b..3e1f3e16 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<HTMLInputElement>) => void; - highlightedCodeSections?: (RangeInFile & { contents: string })[]; - deleteContextItem?: (idx: number) => void; + onEnter: (e: React.KeyboardEvent<HTMLInputElement>) => 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,42 @@ const ComboBox = React.forwardRef((props: ComboBoxProps, ref) => { </Ul> </div> <div className="px-2 flex gap-2 items-center flex-wrap"> + {highlightedCodeSections.length > 0 && ( + <> + <HeaderButtonWithText + text="Clear Context" + onClick={() => { + props.deleteContextItems( + highlightedCodeSections.map((_, idx) => idx) + ); + }} + > + <Trash size="1.6em" /> + </HeaderButtonWithText> + <HeaderButtonWithText + text={pinned ? "Unpin Context" : "Pin Context"} + inverted={pinned} + onClick={() => { + setPinned((prev) => !prev); + props.onTogglePin(); + }} + > + {pinned ? ( + <LockClosed size="1.6em"></LockClosed> + ) : ( + <LockOpen size="1.6em"></LockOpen> + )} + </HeaderButtonWithText> + </> + )} {highlightedCodeSections.map((section, idx) => ( <PillButton - title={section.filepath} + title={`${section.filepath} (${section.range.start.line + 1}-${ + section.range.end.line + 1 + })`} onDelete={() => { - if (props.deleteContextItem) { - props.deleteContextItem(idx); + if (props.deleteContextItems) { + props.deleteContextItems([idx]); } setHighlightedCodeSections((prev) => { const newSections = [...prev]; @@ -280,7 +317,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 ( <HeaderButton + inverted={props.inverted} disabled={props.disabled} style={{ padding: "1px", paddingLeft: hover ? "4px" : "1px" }} onMouseEnter={() => { diff --git a/extension/react-app/src/components/PillButton.tsx b/extension/react-app/src/components/PillButton.tsx index 33451db5..2352c3ad 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,22 +43,21 @@ const PillButton = (props: PillButtonProps) => { <div style={{ display: "grid", gridTemplateColumns: "1fr auto", gap: "4px" }} > - <span>{props.title}</span> <span style={{ cursor: "pointer", color: "red", - borderLeft: "1px solid black", - paddingLeft: "4px", + borderRight: "1px solid black", + paddingRight: "4px", }} - hidden={!isHovered} onClick={() => { props.onDelete?.(); props.onHover?.(false); }} > - X + <XMark style={{ padding: "0px" }} size="1.2em" strokeWidth="2px" /> </span> + <span>{props.title}</span> </div> </Button> ); diff --git a/extension/react-app/src/components/StepContainer.tsx b/extension/react-app/src/components/StepContainer.tsx index 492857b5..311f68cf 100644 --- a/extension/react-app/src/components/StepContainer.tsx +++ b/extension/react-app/src/components/StepContainer.tsx @@ -84,7 +84,8 @@ const MarkdownPre = styled.pre` const StyledCode = styled.code` word-wrap: break-word; - color: lightgray; + color: #f69292; + background: transparent; `; const gradient = keyframes` 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 ( - <StyledDiv> + <StyledDiv hidden={props.historyNode.step.hide as any}> {props.children} <div style={{ marginLeft: "auto" }}> <HeaderButtonWithText @@ -36,7 +38,7 @@ const UserInputContainer = (props: UserInputContainerProps) => { }} text="Delete" > - <XMark size="1.6em" onClick={props.onDelete} /> + <XMark size="1.6em" /> </HeaderButtonWithText> </div> </StyledDiv> 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/hooks/ContinueGUIClientProtocol.ts b/extension/react-app/src/hooks/ContinueGUIClientProtocol.ts index 228e9a53..96ea7ab3 100644 --- a/extension/react-app/src/hooks/ContinueGUIClientProtocol.ts +++ b/extension/react-app/src/hooks/ContinueGUIClientProtocol.ts @@ -21,7 +21,7 @@ abstract class AbstractContinueGUIClientProtocol { abstract deleteAtIndex(index: number): void; - abstract deleteContextItemAtIndex(index: number): void; + abstract deleteContextAtIndices(indices: number[]): void; } export default AbstractContinueGUIClientProtocol; diff --git a/extension/react-app/src/hooks/useContinueGUIProtocol.ts b/extension/react-app/src/hooks/useContinueGUIProtocol.ts index a0c38c0f..e950387c 100644 --- a/extension/react-app/src/hooks/useContinueGUIProtocol.ts +++ b/extension/react-app/src/hooks/useContinueGUIProtocol.ts @@ -71,8 +71,8 @@ class ContinueGUIClientProtocol extends AbstractContinueGUIClientProtocol { this.messenger.send("delete_at_index", { index }); } - deleteContextItemAtIndex(index: number) { - this.messenger.send("delete_context_item_at_index", { index }); + deleteContextAtIndices(indices: number[]) { + this.messenger.send("delete_context_at_indices", { indices }); } } diff --git a/extension/react-app/src/tabs/gui.tsx b/extension/react-app/src/tabs/gui.tsx index 40256f86..bbf0b126 100644 --- a/extension/react-app/src/tabs/gui.tsx +++ b/extension/react-app/src/tabs/gui.tsx @@ -74,6 +74,7 @@ function GUI(props: GUIProps) { const [availableSlashCommands, setAvailableSlashCommands] = useState< { name: string; description: string }[] >([]); + const [pinned, setPinned] = useState(false); const [showDataSharingInfo, setShowDataSharingInfo] = useState(false); const [stepsOpen, setStepsOpen] = useState<boolean[]>([ true, @@ -87,13 +88,16 @@ function GUI(props: GUIProps) { step: { name: "Welcome to Continue", hide: false, - description: - "Highlight code and ask a question or give instructions. Past steps are used as additional context by default. Use slash commands when you want fine-grained control.", + 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`, system_message: null, chat_context: [], manage_own_chat_context: false, - message: - "Highlight code and ask a question or give instructions. Past steps are used as additional context by default. Use slash commands when you want fine-grained control.", + message: "", }, depth: 0, deleted: false, @@ -185,9 +189,9 @@ function GUI(props: GUIProps) { const mainTextInputRef = useRef<HTMLInputElement>(null); - const deleteContextItem = useCallback( - (idx: number) => { - client?.deleteContextItemAtIndex(idx); + const deleteContextItems = useCallback( + (indices: number[]) => { + client?.deleteContextAtIndices(indices); }, [client] ); @@ -241,6 +245,13 @@ function GUI(props: GUIProps) { setUserInputQueue((queue) => { return [...queue, input]; }); + + // Delete all context items unless locked + if (!pinned) { + client?.deleteContextAtIndices( + highlightedRanges.map((_, index) => index) + ); + } } }; @@ -286,6 +297,7 @@ function GUI(props: GUIProps) { onDelete={() => { client?.deleteAtIndex(index); }} + historyNode={node} > {node.step.description as string} </UserInputContainer> @@ -345,7 +357,10 @@ function GUI(props: GUIProps) { onInputValueChange={() => {}} items={availableSlashCommands} highlightedCodeSections={highlightedRanges} - deleteContextItem={deleteContextItem} + deleteContextItems={deleteContextItems} + onTogglePin={() => { + setPinned((prev: boolean) => !prev); + }} /> <ContinueButton onClick={onMainTextInput} /> </TopGUIDiv> diff --git a/extension/src/continueIdeClient.ts b/extension/src/continueIdeClient.ts index 999bca88..b9969858 100644 --- a/extension/src/continueIdeClient.ts +++ b/extension/src/continueIdeClient.ts @@ -159,6 +159,9 @@ class IdeProtocolClient { case "showSuggestion": this.showSuggestion(data.edit); break; + case "showDiff": + this.showDiff(data.filepath, data.replacement); + break; case "openGUI": case "connected": break; @@ -236,6 +239,42 @@ class IdeProtocolClient { ); } + contentProvider: vscode.Disposable | null = null; + + showDiff(filepath: string, replacement: string) { + const myProvider = new (class + implements vscode.TextDocumentContentProvider + { + onDidChangeEmitter = new vscode.EventEmitter<vscode.Uri>(); + onDidChange = this.onDidChangeEmitter.event; + provideTextDocumentContent = (uri: vscode.Uri) => { + return replacement; + }; + })(); + this.contentProvider = vscode.workspace.registerTextDocumentContentProvider( + "continueDiff", + myProvider + ); + + // Call the event fire + const diffFilename = `continueDiff://${filepath}`; + myProvider.onDidChangeEmitter.fire(vscode.Uri.parse(diffFilename)); + + const leftUri = vscode.Uri.file(filepath); + const rightUri = vscode.Uri.parse(diffFilename); + const title = "Continue Diff"; + vscode.commands + .executeCommand("vscode.diff", leftUri, rightUri, title) + .then( + () => { + console.log("Diff view opened successfully"); + }, + (error) => { + console.error("Error opening diff view:", error); + } + ); + } + openFile(filepath: string) { // vscode has a builtin open/get open files openEditorAndRevealRange(filepath, undefined, vscode.ViewColumn.One); @@ -371,21 +410,21 @@ class IdeProtocolClient { let rangeInFiles: RangeInFile[] = []; vscode.window.visibleTextEditors.forEach((editor) => { editor.selections.forEach((selection) => { - if (!selection.isEmpty) { - rangeInFiles.push({ - filepath: editor.document.uri.fsPath, - range: { - start: { - line: selection.start.line, - character: selection.start.character, - }, - end: { - line: selection.end.line, - character: selection.end.character, - }, + // if (!selection.isEmpty) { + rangeInFiles.push({ + filepath: editor.document.uri.fsPath, + range: { + start: { + line: selection.start.line, + character: selection.start.character, }, - }); - } + end: { + line: selection.end.line, + character: selection.end.character, + }, + }, + }); + // } }); }); return rangeInFiles; |