diff options
Diffstat (limited to 'extension/react-app/src')
| -rw-r--r-- | extension/react-app/src/components/ContinueButton.tsx | 8 | ||||
| -rw-r--r-- | extension/react-app/src/components/DebugPanel.tsx | 15 | ||||
| -rw-r--r-- | extension/react-app/src/components/InputAndButton.tsx | 77 | ||||
| -rw-r--r-- | extension/react-app/src/components/StepContainer.tsx | 94 | ||||
| -rw-r--r-- | extension/react-app/src/components/ToggleErrorDiv.tsx | 41 | ||||
| -rw-r--r-- | extension/react-app/src/components/index.ts | 19 | ||||
| -rw-r--r-- | extension/react-app/src/tabs/chat/MessageDiv.tsx | 1 | ||||
| -rw-r--r-- | extension/react-app/src/tabs/gui.tsx | 91 | 
8 files changed, 262 insertions, 84 deletions
| diff --git a/extension/react-app/src/components/ContinueButton.tsx b/extension/react-app/src/components/ContinueButton.tsx index 11dc7a92..c6117bf9 100644 --- a/extension/react-app/src/components/ContinueButton.tsx +++ b/extension/react-app/src/components/ContinueButton.tsx @@ -24,9 +24,13 @@ let StyledButton = styled(Button)`    }  `; -function ContinueButton(props: { onClick?: () => void }) { +function ContinueButton(props: { onClick?: () => void; hidden?: boolean }) {    return ( -    <StyledButton className="m-auto" onClick={props.onClick}> +    <StyledButton +      hidden={props.hidden} +      className="m-auto" +      onClick={props.onClick} +    >        <Play />        {/* <img src={"/continue_arrow.png"} width="16px"></img> */}        Continue diff --git a/extension/react-app/src/components/DebugPanel.tsx b/extension/react-app/src/components/DebugPanel.tsx index 9dacc624..11ec2fe2 100644 --- a/extension/react-app/src/components/DebugPanel.tsx +++ b/extension/react-app/src/components/DebugPanel.tsx @@ -9,7 +9,7 @@ import {  } from "../redux/slices/configSlice";  import { setHighlightedCode } from "../redux/slices/miscSlice";  import { updateFileSystem } from "../redux/slices/debugContexSlice"; -import { buttonColor, defaultBorderRadius, vscBackground } from "."; +import { defaultBorderRadius, secondaryDark, vscBackground } from ".";  interface DebugPanelProps {    tabs: {      element: React.ReactElement; @@ -19,14 +19,15 @@ interface DebugPanelProps {  const GradientContainer = styled.div`    // Uncomment to get gradient border -  background: linear-gradient( +  /* background: linear-gradient(      101.79deg,      #12887a 0%,      #87245c 37.64%,      #e12637 65.98%,      #ffb215 110.45% -  ); +  ); */    /* padding: 10px; */ +  background-color: ${secondaryDark};    margin: 0;    height: 100%;    /* border: 1px solid white; */ @@ -36,11 +37,8 @@ const GradientContainer = styled.div`  const MainDiv = styled.div`    height: 100%;    border-radius: ${defaultBorderRadius}; -  overflow-y: scroll; -  scrollbar-gutter: stable both-edges;    scrollbar-base-color: transparent; -  /* background: ${vscBackground}; */ -  background-color: #1e1e1ede; +  background-color: ${vscBackground};  `;  const TabBar = styled.div<{ numTabs: number }>` @@ -105,9 +103,6 @@ function DebugPanel(props: DebugPanelProps) {                <div                  key={index}                  hidden={index !== currentTab} -                className={ -                  tab.title === "Chat" ? "overflow-hidden" : "overflow-scroll" -                }                  style={{ scrollbarGutter: "stable both-edges" }}                >                  {tab.element} diff --git a/extension/react-app/src/components/InputAndButton.tsx b/extension/react-app/src/components/InputAndButton.tsx new file mode 100644 index 00000000..0a8592f2 --- /dev/null +++ b/extension/react-app/src/components/InputAndButton.tsx @@ -0,0 +1,77 @@ +import React, { useRef } from "react"; +import styled from "styled-components"; +import { vscBackground } from "."; + +interface InputAndButtonProps { +  onUserInput: (input: string) => void; +} + +const TopDiv = styled.div` +  display: grid; +  grid-template-columns: 3fr 1fr; +  grid-gap: 0; +`; + +const Input = styled.input` +  padding: 0.5rem; +  border: 1px solid white; +  background-color: ${vscBackground}; +  color: white; +  border-radius: 4px; +  border-top-right-radius: 0; +  border-bottom-right-radius: 0; +  outline: none; +`; + +const Button = styled.button` +  padding: 0.5rem; +  border: 1px solid white; +  background-color: ${vscBackground}; +  color: white; +  border-radius: 4px; +  border-top-left-radius: 0; +  border-bottom-left-radius: 0; +  border-left: 0; +  cursor: pointer; + +  &:hover { +    background-color: white; +    color: black; +  } +`; + +function InputAndButton(props: InputAndButtonProps) { +  const userInputRef = useRef<HTMLInputElement>(null); + +  return ( +    <TopDiv className="grid grid-cols-2 space-x-0"> +      <Input +        ref={userInputRef} +        onKeyDown={(e) => { +          if (e.key === "Enter") { +            props.onUserInput(e.currentTarget.value); +          } +        }} +        type="text" +        onSubmit={(ev) => { +          props.onUserInput(ev.currentTarget.value); +        }} +        onClick={(e) => { +          e.stopPropagation(); +        }} +      /> +      <Button +        onClick={(e) => { +          if (userInputRef.current) { +            props.onUserInput(userInputRef.current.value); +          } +          e.stopPropagation(); +        }} +      > +        Enter +      </Button> +    </TopDiv> +  ); +} + +export default InputAndButton; diff --git a/extension/react-app/src/components/StepContainer.tsx b/extension/react-app/src/components/StepContainer.tsx index 903f9b94..8ea54325 100644 --- a/extension/react-app/src/components/StepContainer.tsx +++ b/extension/react-app/src/components/StepContainer.tsx @@ -2,13 +2,11 @@ import { useCallback, useEffect, useRef, useState } from "react";  import styled, { keyframes } from "styled-components";  import {    appear, -  buttonColor,    defaultBorderRadius, -  MainContainerWithBorder, -  MainTextInput,    secondaryDark,    vscBackground,    GradientBorder, +  vscBackgroundTransparent,  } from ".";  import { RangeInFile, FileEdit } from "../../../src/client";  import CodeBlock from "./CodeBlock"; @@ -23,6 +21,8 @@ import {  import { HistoryNode } from "../../../schema/HistoryNode";  import ReactMarkdown from "react-markdown";  import ContinueButton from "./ContinueButton"; +import InputAndButton from "./InputAndButton"; +import ToggleErrorDiv from "./ToggleErrorDiv";  interface StepContainerProps {    historyNode: HistoryNode; @@ -31,6 +31,7 @@ interface StepContainerProps {    onRefinement: (input: string) => void;    onUserInput: (input: string) => void;    onRetry: () => void; +  open?: boolean;  }  const MainDiv = styled.div<{ stepDepth: number; inFuture: boolean }>` @@ -43,17 +44,26 @@ const MainDiv = styled.div<{ stepDepth: number; inFuture: boolean }>`  `;  const StepContainerDiv = styled.div<{ open: boolean }>` -  background-color: ${(props) => (props.open ? vscBackground : secondaryDark)}; -  border-radius: ${defaultBorderRadius}; -  padding: 8px; +  /* background-color: ${(props) => +    props.open ? vscBackground : secondaryDark}; */ +  /* border-radius: ${defaultBorderRadius}; */ +  /* padding: 8px; */  `; -const HeaderDiv = styled.div` +const HeaderDiv = styled.div<{ error: boolean }>` +  background-color: ${(props) => +    props.error ? "#522" : vscBackgroundTransparent};    display: grid;    grid-template-columns: 1fr auto;    align-items: center;  `; +const ContentDiv = styled.div` +  padding: 8px; +  padding-left: 16px; +  background-color: ${vscBackground}; +`; +  const HeaderButton = styled.button`    background-color: transparent;    border: 1px solid white; @@ -74,12 +84,10 @@ const OnHoverDiv = styled.div`    animation: ${appear} 0.3s ease-in-out;  `; -const NaturalLanguageInput = styled(MainTextInput)` -  width: 80%; -`; -  function StepContainer(props: StepContainerProps) { -  const [open, setOpen] = useState(false); +  const [open, setOpen] = useState( +    typeof props.open === "undefined" ? true : props.open +  );    const [isHovered, setIsHovered] = useState(false);    const naturalLanguageInputRef = useRef<HTMLTextAreaElement>(null);    const userInputRef = useRef<HTMLInputElement>(null); @@ -115,19 +123,25 @@ function StepContainer(props: StepContainerProps) {        }}        hidden={props.historyNode.step.hide as any}      > -      <GradientBorder -        className="m-2 overflow-hidden cursor-pointer" -        onClick={() => setOpen((prev) => !prev)} -      > -        <StepContainerDiv open={open}> -          <HeaderDiv> +      <StepContainerDiv open={open}> +        <GradientBorder +          borderColor={ +            props.historyNode.observation?.error ? "#f00" : undefined +          } +          className="overflow-hidden cursor-pointer" +          onClick={() => setOpen((prev) => !prev)} +        > +          <HeaderDiv +            error={props.historyNode.observation?.error ? true : false} +          >              <h4 className="m-2">                {open ? (                  <ChevronDown size="1.4em" />                ) : (                  <ChevronRight size="1.4em" />                )} -              {props.historyNode.step.name as any}: +              {props.historyNode.observation?.title || +                (props.historyNode.step.name as any)}              </h4>              {/* <HeaderButton                onClick={(e) => { @@ -151,8 +165,9 @@ function StepContainer(props: StepContainerProps) {                <></>              )}            </HeaderDiv> - -          {open && ( +        </GradientBorder> +        <ContentDiv hidden={!open}> +          {open && false && (              <>                <pre className="overflow-scroll">                  Step Details: @@ -163,36 +178,21 @@ function StepContainer(props: StepContainerProps) {            )}            {props.historyNode.observation?.error ? ( -            <> -              Error while running step: -              <br /> -              <pre className="overflow-scroll"> -                {props.historyNode.observation.error as string} -              </pre> -            </> +            <pre className="overflow-x-scroll"> +              {props.historyNode.observation.error as string} +            </pre>            ) : (              <ReactMarkdown key={1} className="overflow-scroll">                {props.historyNode.step.description as any}              </ReactMarkdown>            )} -          {props.historyNode.step.name === "Waiting for user input" && ( -            <input -              ref={userInputRef} -              className="m-auto p-2 rounded-md border-1 border-solid text-white w-3/4 border-gray-200 bg-vsc-background" -              onKeyDown={(e) => { -                if (e.key === "Enter") { -                  props.onUserInput(e.currentTarget.value); -                } -              }} -              type="text" -              onSubmit={(ev) => { -                props.onUserInput(ev.currentTarget.value); +          {/* {props.historyNode.step.name === "Waiting for user input" && ( +            <InputAndButton +              onUserInput={(value) => { +                props.onUserInput(value);                }} -              onClick={(e) => { -                e.stopPropagation(); -              }} -            /> +            ></InputAndButton>            )}            {props.historyNode.step.name === "Waiting for user confirmation" && (              <> @@ -212,9 +212,9 @@ function StepContainer(props: StepContainerProps) {                  value="Confirm"                />              </> -          )} -        </StepContainerDiv> -      </GradientBorder> +          )} */} +        </ContentDiv> +      </StepContainerDiv>        {/* <OnHoverDiv hidden={!open}>          <NaturalLanguageInput diff --git a/extension/react-app/src/components/ToggleErrorDiv.tsx b/extension/react-app/src/components/ToggleErrorDiv.tsx new file mode 100644 index 00000000..69112ef7 --- /dev/null +++ b/extension/react-app/src/components/ToggleErrorDiv.tsx @@ -0,0 +1,41 @@ +import React, { useState } from "react"; +import styled from "styled-components"; +import { defaultBorderRadius } from "."; + +// Should be a toggleable div with red border and light red background that displays a main message and detail inside + +interface ToggleErrorDivProps { +  title: string; +  error: string; +} + +const TopDiv = styled.div` +  border: 1px solid red; +  background-color: #ff000020; +  padding: 8px; + +  border-radius: ${defaultBorderRadius}; +  cursor: pointer; +`; + +const ToggleErrorDiv = (props: ToggleErrorDivProps) => { +  const [open, setOpen] = useState(false); +  return ( +    <TopDiv +      onClick={() => { +        setOpen(!open); +      }} +    > +      <div className="flex flex-row"> +        <div className="flex-grow"> +          <p> +            {open ? "▼" : "▶"} {props.title} +          </p> +        </div> +      </div> +      {open && <pre className="overflow-scroll">{props.error}</pre>} +    </TopDiv> +  ); +}; + +export default ToggleErrorDiv; diff --git a/extension/react-app/src/components/index.ts b/extension/react-app/src/components/index.ts index 7ba60467..4966f3e8 100644 --- a/extension/react-app/src/components/index.ts +++ b/extension/react-app/src/components/index.ts @@ -3,6 +3,7 @@ import styled, { keyframes } from "styled-components";  export const defaultBorderRadius = "5px";  export const secondaryDark = "rgb(37 37 38)";  export const vscBackground = "rgb(30 30 30)"; +export const vscBackgroundTransparent = "#1e1e1ede";  export const buttonColor = "rgb(113 28 59)";  export const buttonColorHover = "rgb(113 28 59 0.67)"; @@ -94,16 +95,24 @@ export const Loader = styled.div`    margin: auto;  `; -export const GradientBorder = styled.div<{ borderWidth?: string }>` -  border-radius: ${defaultBorderRadius}; -  padding: ${(props) => props.borderWidth || "1px"}; -  background: linear-gradient( +export const GradientBorder = styled.div<{ +  borderWidth?: string; +  borderRadius?: string; +  borderColor?: string; +}>` +  border-radius: ${(props) => props.borderRadius || "0"}; +  padding-top: ${(props) => props.borderWidth || "1px"}; +  padding-bottom: ${(props) => props.borderWidth || "1px"}; +  background: ${(props) => +    props.borderColor +      ? props.borderColor +      : `linear-gradient(      101.79deg,      #12887a 0%,      #87245c 37.64%,      #e12637 65.98%,      #ffb215 110.45% -  ); +  )`};  `;  export const MainContainerWithBorder = styled.div<{ borderWidth?: string }>` diff --git a/extension/react-app/src/tabs/chat/MessageDiv.tsx b/extension/react-app/src/tabs/chat/MessageDiv.tsx index ad81f5e9..1d7bb5f5 100644 --- a/extension/react-app/src/tabs/chat/MessageDiv.tsx +++ b/extension/react-app/src/tabs/chat/MessageDiv.tsx @@ -21,7 +21,6 @@ const Container = styled.div`    width: fit-content;    max-width: 75%;    overflow-y: scroll; -  scrollbar-gutter: stable both-edges;    word-wrap: break-word;    -ms-word-wrap: break-word;    height: fit-content; diff --git a/extension/react-app/src/tabs/gui.tsx b/extension/react-app/src/tabs/gui.tsx index a08698a4..5c75579b 100644 --- a/extension/react-app/src/tabs/gui.tsx +++ b/extension/react-app/src/tabs/gui.tsx @@ -1,25 +1,21 @@  import styled from "styled-components";  import { -  Button,    defaultBorderRadius,    vscBackground, -  MainTextInput,    Loader, +  MainTextInput,  } from "../components";  import ContinueButton from "../components/ContinueButton";  import { useCallback, useEffect, useRef, useState } from "react";  import { History } from "../../../schema/History";  import { HistoryNode } from "../../../schema/HistoryNode";  import StepContainer from "../components/StepContainer"; -import { useSelector } from "react-redux"; -import { RootStore } from "../redux/store"; -import useContinueWebsocket from "../hooks/useWebsocket";  import useContinueGUIProtocol from "../hooks/useWebsocket";  let TopGUIDiv = styled.div`    display: grid;    grid-template-columns: 1fr; -  overflow: scroll; +  background-color: ${vscBackground};  `;  let UserInputQueueItem = styled.div` @@ -38,15 +34,20 @@ function GUI(props: GUIProps) {    const [waitingForSteps, setWaitingForSteps] = useState(false);    const [userInputQueue, setUserInputQueue] = useState<string[]>([]);    const [history, setHistory] = useState<History | undefined>(); -  // { +  //   {    //   timeline: [    //     {    //       step: { -  //         name: "RunCodeStep", +  //         name: "Waiting for user input",    //         cmd: "python3 /Users/natesesti/Desktop/continue/extension/examples/python/main.py",    //         description:    //           "Run `python3 /Users/natesesti/Desktop/continue/extension/examples/python/main.py`",    //       }, +  //       observation: { +  //         title: "ERROR FOUND", +  //         error: +  //           "Traceback (most recent call last):\n  File \"/Users/natesesti/Desktop/continue/extension/examples/python/main.py\", line 7, in <module>\n    print(sum(first, second))\n          ^^^^^^^^^^^^^^^^^^\n  File \"/Users/natesesti/Desktop/continue/extension/examples/python/sum.py\", line 2, in sum\n    return a + b\n           ~~^~~\nTypeError: unsupported operand type(s) for +: 'int' and 'str'", +  //       },    //       output: [    //         {    //           traceback: { @@ -156,8 +157,20 @@ function GUI(props: GUIProps) {    //   current_index: 0,    // } as any); +  const topGuiDivRef = useRef<HTMLDivElement>(null);    const client = useContinueGUIProtocol(); +  const scrollToBottom = useCallback(() => { +    if (topGuiDivRef.current) { +      setTimeout(() => { +        window.scrollTo({ +          top: window.outerHeight, +          behavior: "smooth", +        }); +      }, 100); +    } +  }, [topGuiDivRef.current]); +    useEffect(() => {      console.log("CLIENT ON STATE UPDATE: ", client, client?.onStateUpdate);      client?.onStateUpdate((state) => { @@ -165,9 +178,15 @@ function GUI(props: GUIProps) {        setWaitingForSteps(state.active);        setHistory(state.history);        setUserInputQueue(state.user_input_queue); + +      scrollToBottom();      });    }, [client]); +  useEffect(() => { +    scrollToBottom(); +  }, [waitingForSteps]); +    const mainTextInputRef = useRef<HTMLTextAreaElement>(null);    useEffect(() => { @@ -189,14 +208,33 @@ function GUI(props: GUIProps) {      if (mainTextInputRef.current) {        if (!client) return;        let input = mainTextInputRef.current.value; -      setWaitingForSteps(true); -      client.sendMainInput(input); -      setUserInputQueue((queue) => { -        return [...queue, input]; -      }); + +      if ( +        history && +        history.timeline[history.current_index].step.name === +          "Waiting for user input" +      ) { +        if (input.trim() === "") return; +        onStepUserInput(input, history!.current_index); +      } else if ( +        history && +        history.timeline[history.current_index].step.name === +          "Waiting for user confirmation" +      ) { +        onStepUserInput("ok", history!.current_index); +      } else { +        if (input.trim() === "") return; + +        client.sendMainInput(input); +        setUserInputQueue((queue) => { +          return [...queue, input]; +        }); +      }        mainTextInputRef.current.value = "";        mainTextInputRef.current.style.height = "";      } + +    setWaitingForSteps(true);    };    const onStepUserInput = (input: string, index: number) => { @@ -207,7 +245,14 @@ function GUI(props: GUIProps) {    // const iterations = useSelector(selectIterations);    return ( -    <TopGUIDiv> +    <TopGUIDiv +      ref={topGuiDivRef} +      onKeyDown={(e) => { +        if (e.key === "Enter" && e.ctrlKey) { +          onMainTextInput(); +        } +      }} +    >        {typeof client === "undefined" && (          <>            <Loader></Loader> @@ -247,6 +292,12 @@ function GUI(props: GUIProps) {        </div>        <MainTextInput +        disabled={ +          history +            ? history.timeline[history.current_index].step.name === +              "Waiting for user confirmation" +            : false +        }          ref={mainTextInputRef}          onKeyDown={(e) => {            if (e.key === "Enter") { @@ -257,13 +308,15 @@ function GUI(props: GUIProps) {          }}          rows={1}          onChange={() => { -          let textarea = mainTextInputRef.current!; +          const textarea = mainTextInputRef.current!;            textarea.style.height = ""; /* Reset the height*/ -          textarea.style.height = -            Math.min(textarea.scrollHeight - 15, 500) + "px"; +          textarea.style.height = `${Math.min( +            textarea.scrollHeight - 15, +            500 +          )}px`;          }} -      ></MainTextInput> -      <ContinueButton onClick={onMainTextInput}></ContinueButton> +      /> +      <ContinueButton onClick={onMainTextInput} />      </TopGUIDiv>    );  } | 
