diff options
Diffstat (limited to 'extension/react-app/src')
9 files changed, 92 insertions, 32 deletions
| 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> | 
