diff options
| author | Nate Sesti <sestinj@gmail.com> | 2023-06-26 16:39:07 -0700 | 
|---|---|---|
| committer | Nate Sesti <sestinj@gmail.com> | 2023-06-26 16:39:07 -0700 | 
| commit | f3c6533d4054de5a71c8867c2b76fc8b0747447c (patch) | |
| tree | 8e53ce9533aa42ee5d0a31504aa07be691d87157 /extension/react-app | |
| parent | f403bbe26b8864ff5367eab08afe4a01fdde8e72 (diff) | |
| parent | 292d4d10ce28d720d61f97918c490b0d9cdae9e7 (diff) | |
| download | sncontinue-f3c6533d4054de5a71c8867c2b76fc8b0747447c.tar.gz sncontinue-f3c6533d4054de5a71c8867c2b76fc8b0747447c.tar.bz2 sncontinue-f3c6533d4054de5a71c8867c2b76fc8b0747447c.zip | |
Merge branch 'main' into styled-code
Diffstat (limited to 'extension/react-app')
18 files changed, 292 insertions, 66 deletions
| diff --git a/extension/react-app/package-lock.json b/extension/react-app/package-lock.json index 18ef3ee6..fa624b3f 100644 --- a/extension/react-app/package-lock.json +++ b/extension/react-app/package-lock.json @@ -18,6 +18,7 @@          "react-markdown": "^8.0.5",          "react-redux": "^8.0.5",          "react-syntax-highlighter": "^15.5.0", +        "react-switch": "^7.0.0",          "styled-components": "^5.3.6",          "vscode-webview": "^1.0.1-beta.1"        }, @@ -2921,19 +2922,16 @@          }        }      }, -    "node_modules/react-syntax-highlighter": { -      "version": "15.5.0", -      "resolved": "https://registry.npmjs.org/react-syntax-highlighter/-/react-syntax-highlighter-15.5.0.tgz", -      "integrity": "sha512-+zq2myprEnQmH5yw6Gqc8lD55QHnpKaU8TOcFeC/Lg/MQSs8UknEA0JC4nTZGFAXC2J2Hyj/ijJ7NlabyPi2gg==", +    "node_modules/react-switch": { +      "version": "7.0.0", +      "resolved": "https://registry.npmjs.org/react-switch/-/react-switch-7.0.0.tgz", +      "integrity": "sha512-KkDeW+cozZXI6knDPyUt3KBN1rmhoVYgAdCJqAh7st7tk8YE6N0iR89zjCWO8T8dUTeJGTR0KU+5CHCRMRffiA==",        "dependencies": { -        "@babel/runtime": "^7.3.1", -        "highlight.js": "^10.4.1", -        "lowlight": "^1.17.0", -        "prismjs": "^1.27.0", -        "refractor": "^3.6.0" +        "prop-types": "^15.7.2"        },        "peerDependencies": { -        "react": ">= 0.14.0" +        "react": "^15.3.0 || ^16.0.0 || ^17.0.0 || ^18.0.0", +        "react-dom": "^15.3.0 || ^16.0.0 || ^17.0.0 || ^18.0.0"        }      },      "node_modules/read-cache": { @@ -5351,16 +5349,12 @@          "use-sync-external-store": "^1.0.0"        }      }, -    "react-syntax-highlighter": { -      "version": "15.5.0", -      "resolved": "https://registry.npmjs.org/react-syntax-highlighter/-/react-syntax-highlighter-15.5.0.tgz", -      "integrity": "sha512-+zq2myprEnQmH5yw6Gqc8lD55QHnpKaU8TOcFeC/Lg/MQSs8UknEA0JC4nTZGFAXC2J2Hyj/ijJ7NlabyPi2gg==", +    "react-switch": { +      "version": "7.0.0", +      "resolved": "https://registry.npmjs.org/react-switch/-/react-switch-7.0.0.tgz", +      "integrity": "sha512-KkDeW+cozZXI6knDPyUt3KBN1rmhoVYgAdCJqAh7st7tk8YE6N0iR89zjCWO8T8dUTeJGTR0KU+5CHCRMRffiA==",        "requires": { -        "@babel/runtime": "^7.3.1", -        "highlight.js": "^10.4.1", -        "lowlight": "^1.17.0", -        "prismjs": "^1.27.0", -        "refractor": "^3.6.0" +        "prop-types": "^15.7.2"        }      },      "read-cache": { diff --git a/extension/react-app/package.json b/extension/react-app/package.json index d43ec55e..5fe27916 100644 --- a/extension/react-app/package.json +++ b/extension/react-app/package.json @@ -19,6 +19,7 @@      "react-markdown": "^8.0.5",      "react-redux": "^8.0.5",      "react-syntax-highlighter": "^15.5.0", +    "react-switch": "^7.0.0",      "styled-components": "^5.3.6",      "vscode-webview": "^1.0.1-beta.1"    }, diff --git a/extension/react-app/public/continue.gif b/extension/react-app/public/continue.gifBinary files differ new file mode 100644 index 00000000..daed6663 --- /dev/null +++ b/extension/react-app/public/continue.gif diff --git a/extension/react-app/public/continue_arrow.png b/extension/react-app/public/continue_arrow.pngBinary files differ deleted file mode 100644 index 3b16ddf9..00000000 --- a/extension/react-app/public/continue_arrow.png +++ /dev/null diff --git a/extension/react-app/public/play_button.png b/extension/react-app/public/play_button.pngBinary files differ new file mode 100644 index 00000000..af379375 --- /dev/null +++ b/extension/react-app/public/play_button.png diff --git a/extension/react-app/src/components/CodeBlock.tsx b/extension/react-app/src/components/CodeBlock.tsx index a720a6ce..9438ab23 100644 --- a/extension/react-app/src/components/CodeBlock.tsx +++ b/extension/react-app/src/components/CodeBlock.tsx @@ -22,7 +22,7 @@ const StyledCopyButton = styled.button<{ visible: boolean }>`    /* position: relative; */    float: right;    border: none; -  background-color: transparent; +  background-color: ${secondaryDark};    cursor: pointer;    padding: 0;    /* margin: 4px; */ @@ -77,7 +77,10 @@ function CodeBlock(props: { language?: string; children: string }) {          setHovered(false);        }}      > -      <CopyButton visible={hovered} textToCopy={props.children} /> +      <CopyButton +        visible={hovered} +        textToCopy={(props.children as any).props.children[0]} +      />        <StyledCode>{props.children}</StyledCode>      </StyledPre>    ); diff --git a/extension/react-app/src/components/ComboBox.tsx b/extension/react-app/src/components/ComboBox.tsx index ddc9d5dc..3816cee8 100644 --- a/extension/react-app/src/components/ComboBox.tsx +++ b/extension/react-app/src/components/ComboBox.tsx @@ -116,6 +116,15 @@ const ComboBox = React.forwardRef((props: ComboBoxProps, ref) => {          disabled={props.disabled}          placeholder="Type '/' to see available slash commands."          {...getInputProps({ +          onChange: (e) => { +            const target = e.target as HTMLTextAreaElement; +            // Update the height of the textarea to match the content, up to a max of 200px. +            target.style.height = "auto"; +            target.style.height = `${Math.min( +              target.scrollHeight, +              300 +            ).toString()}px`; +          },            onKeyDown: (event) => {              if (event.key === "Enter" && event.shiftKey) {                // Prevent Downshift's default 'Enter' behavior. @@ -136,14 +145,24 @@ const ComboBox = React.forwardRef((props: ComboBoxProps, ref) => {              } else if (event.key === "Tab" && items.length > 0) {                setInputValue(items[0].name);                event.preventDefault(); -            } else if (event.key === "ArrowUp") { +            } else if ( +              event.key === "ArrowUp" || +              (event.key === "ArrowDown" && +                event.currentTarget.value.split("\n").length > 1) +            ) { +              (event.nativeEvent as any).preventDownshiftDefault = true; +            } else if ( +              event.key === "ArrowUp" && +              event.currentTarget.value.split("\n").length > 1 +            ) {                if (positionInHistory == 0) return;                setInputValue(history[positionInHistory - 1]);                setPositionInHistory((prev) => prev - 1); -            } else if (event.key === "ArrowDown") { -              if (positionInHistory >= history.length - 1) { -                setInputValue(""); -              } else { +            } else if ( +              event.key === "ArrowDown" && +              event.currentTarget.value.split("\n").length > 1 +            ) { +              if (positionInHistory < history.length - 1) {                  setInputValue(history[positionInHistory + 1]);                }                setPositionInHistory((prev) => diff --git a/extension/react-app/src/components/ContinueButton.tsx b/extension/react-app/src/components/ContinueButton.tsx index c6117bf9..ef6719b7 100644 --- a/extension/react-app/src/components/ContinueButton.tsx +++ b/extension/react-app/src/components/ContinueButton.tsx @@ -1,6 +1,8 @@  import styled, { keyframes } from "styled-components";  import { Button } from ".";  import { Play } from "@styled-icons/heroicons-outline"; +import { useSelector } from "react-redux"; +import { RootStore } from "../redux/store";  let StyledButton = styled(Button)`    margin: auto; @@ -25,14 +27,21 @@ let StyledButton = styled(Button)`  `;  function ContinueButton(props: { onClick?: () => void; hidden?: boolean }) { +  const vscMediaUrl = useSelector( +    (state: RootStore) => state.config.vscMediaUrl +  ); +    return (      <StyledButton        hidden={props.hidden}        className="m-auto"        onClick={props.onClick}      > -      <Play /> -      {/* <img src={"/continue_arrow.png"} width="16px"></img> */} +      {vscMediaUrl ? ( +        <img src={`${vscMediaUrl}/play_button.png`} width="22px" /> +      ) : ( +        <Play /> +      )}        Continue      </StyledButton>    ); diff --git a/extension/react-app/src/components/DebugPanel.tsx b/extension/react-app/src/components/DebugPanel.tsx index 30f38779..94dbac9e 100644 --- a/extension/react-app/src/components/DebugPanel.tsx +++ b/extension/react-app/src/components/DebugPanel.tsx @@ -6,6 +6,7 @@ import {    setApiUrl,    setVscMachineId,    setSessionId, +  setVscMediaUrl,  } from "../redux/slices/configSlice";  import { setHighlightedCode } from "../redux/slices/miscSlice";  import { updateFileSystem } from "../redux/slices/debugContexSlice"; @@ -37,6 +38,7 @@ function DebugPanel(props: DebugPanelProps) {            dispatch(setApiUrl(event.data.apiUrl));            dispatch(setVscMachineId(event.data.vscMachineId));            dispatch(setSessionId(event.data.sessionId)); +          dispatch(setVscMediaUrl(event.data.vscMediaUrl));            break;          case "highlightedCode":            dispatch(setHighlightedCode(event.data.rangeInFile)); diff --git a/extension/react-app/src/components/HeaderButtonWithText.tsx b/extension/react-app/src/components/HeaderButtonWithText.tsx index f9483f0f..30931f86 100644 --- a/extension/react-app/src/components/HeaderButtonWithText.tsx +++ b/extension/react-app/src/components/HeaderButtonWithText.tsx @@ -6,14 +6,20 @@ interface HeaderButtonWithTextProps {    text: string;    onClick?: (e: any) => void;    children: React.ReactNode; +  disabled?: boolean;  }  const HeaderButtonWithText = (props: HeaderButtonWithTextProps) => {    const [hover, setHover] = useState(false);    return (      <HeaderButton -      style={{ padding: "3px" }} -      onMouseEnter={() => setHover(true)} +      disabled={props.disabled} +      style={{ padding: "1px", paddingLeft: hover ? "4px" : "1px" }} +      onMouseEnter={() => { +        if (!props.disabled) { +          setHover(true); +        } +      }}        onMouseLeave={() => {          setHover(false);        }} diff --git a/extension/react-app/src/components/LoadingCover.tsx b/extension/react-app/src/components/LoadingCover.tsx new file mode 100644 index 00000000..a0f8f7a2 --- /dev/null +++ b/extension/react-app/src/components/LoadingCover.tsx @@ -0,0 +1,50 @@ +import React from "react"; +import styled from "styled-components"; + +const StyledDiv = styled.div` +  position: absolute; +  top: 0; +  left: 0; +  width: 100%; +  height: 100vh; +  background: linear-gradient( +    101.79deg, +    #12887a 0%, +    #87245c 32%, +    #e12637 63%, +    #ffb215 100% +  ); +  display: flex; +  justify-content: center; +  align-items: center; +  flex-direction: column; +  z-index: 10; +`; + +const StyledImg = styled.img` +  /* add your styles here */ +`; + +const StyledDiv2 = styled.div` +  width: 50%; +  height: 5px; +  background: white; +  margin-top: 20px; +`; + +interface LoadingCoverProps { +  message: string; +  hidden?: boolean; +} + +const LoadingCover = (props: LoadingCoverProps) => { +  return ( +    <StyledDiv style={{ display: props.hidden ? "none" : "inherit" }}> +      <StyledImg src="continue.gif" alt="centered image" width="50%" /> +      <StyledDiv2></StyledDiv2> +      <p>{props.message}</p> +    </StyledDiv> +  ); +}; + +export default LoadingCover; diff --git a/extension/react-app/src/components/StepContainer.tsx b/extension/react-app/src/components/StepContainer.tsx index 1eb1d1fd..827d2d5f 100644 --- a/extension/react-app/src/components/StepContainer.tsx +++ b/extension/react-app/src/components/StepContainer.tsx @@ -49,9 +49,13 @@ const StepContainerDiv = styled.div<{ open: boolean }>`    /* padding: 8px; */  `; -const HeaderDiv = styled.div<{ error: boolean }>` +const HeaderDiv = styled.div<{ error: boolean; loading: boolean }>`    background-color: ${(props) => -    props.error ? "#522" : vscBackgroundTransparent}; +    props.error +      ? "#522" +      : props.loading +      ? vscBackgroundTransparent +      : vscBackground};    display: grid;    grid-template-columns: 1fr auto auto;    grid-gap: 8px; @@ -76,12 +80,22 @@ const StyledCode = styled.code`    color: lightgray;  `; +const gradient = keyframes` +  0% { +    background-position: 0px 0; +  } +  100% { +    background-position: 100em 0; +  } +`; +  const GradientBorder = styled.div<{    borderWidth?: number;    borderRadius?: string;    borderColor?: string;    isFirst: boolean;    isLast: boolean; +  loading: boolean;  }>`    border-radius: ${(props) => props.borderRadius || "0"};    padding-top: ${(props) => @@ -91,13 +105,18 @@ const GradientBorder = styled.div<{    background: ${(props) =>      props.borderColor        ? props.borderColor -      : `linear-gradient( +      : `repeating-linear-gradient(      101.79deg,      #12887a 0%, -    #87245c 37.64%, -    #e12637 65.98%, -    #ffb215 110.45% +    #87245c 16%, +    #e12637 33%, +    #ffb215 55%, +    #e12637 67%, +    #87245c 85%, +    #12887a 99%    )`}; +  animation: ${(props) => (props.loading ? gradient : "")} 6s linear infinite; +  background-size: 200% 200%;  `;  function StepContainer(props: StepContainerProps) { @@ -138,10 +157,15 @@ function StepContainer(props: StepContainerProps) {      >        <StepContainerDiv open={props.open}>          <GradientBorder +          loading={props.historyNode.active as boolean | false}            isFirst={props.isFirst}            isLast={props.isLast}            borderColor={ -            props.historyNode.observation?.error ? "#f00" : undefined +            props.historyNode.observation?.error +              ? "#f00" +              : props.historyNode.active +              ? undefined +              : "white"            }            className="overflow-hidden cursor-pointer"            onClick={(e) => { @@ -153,6 +177,7 @@ function StepContainer(props: StepContainerProps) {            }}          >            <HeaderDiv +            loading={props.historyNode.active as boolean | false}              error={props.historyNode.observation?.error ? true : false}            >              <h4 className="m-2"> @@ -175,6 +200,7 @@ function StepContainer(props: StepContainerProps) {              <>                <HeaderButtonWithText +                disabled={props.historyNode.active as boolean}                  onClick={(e) => {                    e.stopPropagation();                    props.onDelete(); diff --git a/extension/react-app/src/components/TextDialog.tsx b/extension/react-app/src/components/TextDialog.tsx index e50a7686..2632e572 100644 --- a/extension/react-app/src/components/TextDialog.tsx +++ b/extension/react-app/src/components/TextDialog.tsx @@ -15,6 +15,7 @@ const DialogContainer = styled.div`    top: 50%;    left: 50%;    transform: translate(-50%, -50%); +  width: 75%;  `;  const Dialog = styled.div` @@ -76,7 +77,6 @@ const TextDialog = (props: {          <Dialog>            <P>Thanks for your feedback. We'll get back to you soon!</P>            <TextArea -            cols={50}              rows={10}              ref={textAreaRef}              onKeyDown={(e) => { diff --git a/extension/react-app/src/hooks/ContinueGUIClientProtocol.ts b/extension/react-app/src/hooks/ContinueGUIClientProtocol.ts index 824bb086..3d8e0a38 100644 --- a/extension/react-app/src/hooks/ContinueGUIClientProtocol.ts +++ b/extension/react-app/src/hooks/ContinueGUIClientProtocol.ts @@ -13,6 +13,8 @@ abstract class AbstractContinueGUIClientProtocol {      callback: (commands: { name: string; description: string }[]) => void    ): void; +  abstract changeDefaultModel(model: string): void; +    abstract sendClear(): void;    abstract retryAtIndex(index: number): void; diff --git a/extension/react-app/src/hooks/useContinueGUIProtocol.ts b/extension/react-app/src/hooks/useContinueGUIProtocol.ts index 59397742..f43a66ff 100644 --- a/extension/react-app/src/hooks/useContinueGUIProtocol.ts +++ b/extension/react-app/src/hooks/useContinueGUIProtocol.ts @@ -55,6 +55,10 @@ class ContinueGUIClientProtocol extends AbstractContinueGUIClientProtocol {      });    } +  changeDefaultModel(model: string) { +    this.messenger.send("change_default_model", { model }); +  } +    sendClear() {      this.messenger.send("clear_history", {});    } diff --git a/extension/react-app/src/redux/slices/configSlice.ts b/extension/react-app/src/redux/slices/configSlice.ts index a6a641e6..1b107bed 100644 --- a/extension/react-app/src/redux/slices/configSlice.ts +++ b/extension/react-app/src/redux/slices/configSlice.ts @@ -37,9 +37,21 @@ export const configSlice = createSlice({        ...state,        sessionId: action.payload,      }), +    setVscMediaUrl: ( +      state: RootStore["config"], +      action: { type: string; payload: string } +    ) => ({ +      ...state, +      vscMediaUrl: action.payload, +    }),    },  }); -export const { setVscMachineId, setApiUrl, setWorkspacePath, setSessionId } = -  configSlice.actions; +export const { +  setVscMachineId, +  setApiUrl, +  setWorkspacePath, +  setSessionId, +  setVscMediaUrl, +} = configSlice.actions;  export default configSlice.reducer; diff --git a/extension/react-app/src/redux/store.ts b/extension/react-app/src/redux/store.ts index f9eb0517..a5eef4ba 100644 --- a/extension/react-app/src/redux/store.ts +++ b/extension/react-app/src/redux/store.ts @@ -21,6 +21,7 @@ export interface RootStore {      vscMachineId: string | undefined;      sessionId: string | undefined;      sessionStarted: number | undefined; +    vscMediaUrl: string | undefined;    };    chat: {      messages: ChatMessage[]; diff --git a/extension/react-app/src/tabs/gui.tsx b/extension/react-app/src/tabs/gui.tsx index 624d22d5..13b74423 100644 --- a/extension/react-app/src/tabs/gui.tsx +++ b/extension/react-app/src/tabs/gui.tsx @@ -1,11 +1,5 @@  import styled from "styled-components"; -import { -  defaultBorderRadius, -  vscBackground, -  Loader, -  MainTextInput, -  HeaderButton, -} from "../components"; +import { defaultBorderRadius, Loader } from "../components";  import ContinueButton from "../components/ContinueButton";  import { useCallback, useEffect, useRef, useState } from "react";  import { History } from "../../../schema/History"; @@ -20,6 +14,11 @@ import {  import ComboBox from "../components/ComboBox";  import TextDialog from "../components/TextDialog";  import HeaderButtonWithText from "../components/HeaderButtonWithText"; +import ReactSwitch from "react-switch"; +import { usePostHog } from "posthog-js/react"; +import { useSelector } from "react-redux"; +import { RootStore } from "../redux/store"; +import LoadingCover from "../components/LoadingCover";  const TopGUIDiv = styled.div`    overflow: hidden; @@ -33,7 +32,7 @@ const UserInputQueueItem = styled.div`    text-align: center;  `; -const Footer = styled.footer` +const Footer = styled.footer<{ dataSwitchChecked: boolean }>`    display: flex;    flex-direction: row;    gap: 8px; @@ -42,6 +41,8 @@ const Footer = styled.footer`    align-items: center;    margin-top: 8px;    border-top: 0.1px solid gray; +  background-color: ${(props) => +    props.dataSwitchChecked ? "#12887a33" : "transparent"};  `;  interface GUIProps { @@ -49,14 +50,22 @@ interface GUIProps {  }  function GUI(props: GUIProps) { +  const posthog = usePostHog(); +  const vscMachineId = useSelector( +    (state: RootStore) => state.config.vscMachineId +  ); + +  const [usingFastModel, setUsingFastModel] = useState(false);    const [waitingForSteps, setWaitingForSteps] = useState(false);    const [userInputQueue, setUserInputQueue] = useState<string[]>([]);    const [availableSlashCommands, setAvailableSlashCommands] = useState<      { name: string; description: string }[]    >([]); +  const [dataSwitchChecked, setDataSwitchChecked] = useState(false); +  const [showDataSharingInfo, setShowDataSharingInfo] = useState(false);    const [stepsOpen, setStepsOpen] = useState<boolean[]>([]);    const [history, setHistory] = useState<History | undefined>(); -  // { +  //   {    //   timeline: [    //     {    //       step: { @@ -144,6 +153,7 @@ function GUI(props: GUIProps) {    //       ],    //     },    //     { +  //       active: false,    //       step: {    //         name: "SolveTracebackStep",    //         traceback: { @@ -204,24 +214,41 @@ function GUI(props: GUIProps) {    }, [topGuiDivRef.current, scrollTimeout]);    useEffect(() => { +    const listener = (e: any) => { +      // Cmd + J to toggle fast model +      if (e.key === "i" && e.metaKey && e.shiftKey) { +        setUsingFastModel((prev) => !prev); +      } +    }; +    window.addEventListener("keydown", listener); + +    return () => { +      window.removeEventListener("keydown", listener); +    }; +  }, []); + +  useEffect(() => {      console.log("CLIENT ON STATE UPDATE: ", client, client?.onStateUpdate);      client?.onStateUpdate((state) => {        // Scroll only if user is at very bottom of the window. +      setUsingFastModel(state.default_model === "gpt-3.5-turbo");        const shouldScrollToBottom =          topGuiDivRef.current &&          topGuiDivRef.current?.offsetHeight - window.scrollY < 100;        setWaitingForSteps(state.active);        setHistory(state.history);        setUserInputQueue(state.user_input_queue); -      const nextStepsOpen = [...stepsOpen]; -      for ( -        let i = nextStepsOpen.length; -        i < state.history.timeline.length; -        i++ -      ) { -        nextStepsOpen.push(true); -      } -      setStepsOpen(nextStepsOpen); +      setStepsOpen((prev) => { +        const nextStepsOpen = [...prev]; +        for ( +          let i = nextStepsOpen.length; +          i < state.history.timeline.length; +          i++ +        ) { +          nextStepsOpen.push(true); +        } +        return nextStepsOpen; +      });        if (shouldScrollToBottom) {          scrollToBottom(); @@ -263,8 +290,9 @@ function GUI(props: GUIProps) {    const onMainTextInput = () => {      if (mainTextInputRef.current) { -      if (!client) return;        let input = mainTextInputRef.current.value; +      mainTextInputRef.current.value = ""; +      if (!client) return;        if (          history?.timeline.length && @@ -301,6 +329,7 @@ function GUI(props: GUIProps) {    // const iterations = useSelector(selectIterations);    return (      <> +      <LoadingCover hidden={true} message="Downloading local model..." />        <TextDialog          showDialog={showFeedbackDialog}          onEnter={(text) => { @@ -323,9 +352,7 @@ function GUI(props: GUIProps) {          {typeof client === "undefined" && (            <>              <Loader></Loader> -            <p style={{ textAlign: "center" }}> -              Trying to reconnect with server... -            </p> +            <p style={{ textAlign: "center" }}>Loading Continue server...</p>            </>          )}          {history?.timeline.map((node: HistoryNode, index: number) => { @@ -340,7 +367,7 @@ function GUI(props: GUIProps) {                  setStepsOpen(nextStepsOpen);                }}                onToggleAll={() => { -                setStepsOpen((prev) => prev.map(() => !prev[index])); +                setStepsOpen((prev) => prev.map((_, index) => !prev[index]));                }}                key={index}                onUserInput={(input: string) => { @@ -390,17 +417,87 @@ function GUI(props: GUIProps) {          />          <ContinueButton onClick={onMainTextInput} />        </TopGUIDiv> -      <Footer> +      <div +        style={{ +          position: "fixed", +          bottom: "50px", +          backgroundColor: "white", +          color: "black", +          borderRadius: defaultBorderRadius, +          padding: "16px", +          margin: "16px", +        }} +        hidden={!showDataSharingInfo} +      > +        By turning on this switch, you signal that you would contribute this +        software development data to a publicly accessible, open-source dataset +        in the future. +        <br /> +        <br /> +        <b> +          {dataSwitchChecked +            ? "No data is being collected. In the future, you would be contributing data" +            : "No data is being collected. In the future, your data would not be shared"} +        </b> +      </div> +      <Footer dataSwitchChecked={dataSwitchChecked}> +        <div +          style={{ +            display: "flex", +            gap: "4px", +            marginRight: "auto", +            alignItems: "center", +          }} +          onMouseEnter={() => { +            setShowDataSharingInfo(true); +          }} +          onMouseLeave={() => { +            setShowDataSharingInfo(false); +          }} +        > +          <ReactSwitch +            height={20} +            handleDiameter={20} +            width={40} +            onChange={() => { +              posthog?.capture("data_switch_toggled", { +                vscMachineId: vscMachineId, +                dataSwitchChecked: !dataSwitchChecked, +              }); +              setDataSwitchChecked((prev) => !prev); +            }} +            onColor="#12887a" +            checked={dataSwitchChecked} +          /> +          <span style={{ cursor: "help", fontSize: "14px" }}> +            Contribute Data +          </span> +        </div> +        <HeaderButtonWithText +          onClick={() => { +            client?.changeDefaultModel( +              usingFastModel ? "gpt-4" : "gpt-3.5-turbo" +            ); +            setUsingFastModel((prev) => !prev); +          }} +          text={usingFastModel ? "gpt-3.5-turbo" : "gpt-4"} +        > +          <div +            style={{ fontSize: "18px", marginLeft: "2px", marginRight: "2px" }} +          > +            {usingFastModel ? "⚡" : "🧠"} +          </div> +        </HeaderButtonWithText>          <HeaderButtonWithText            onClick={() => {              client?.sendClear();            }} -          text="Clear History" +          text="Clear All"          >            <Trash size="1.6em" />          </HeaderButtonWithText>          <a href="https://continue.dev/docs" className="no-underline"> -          <HeaderButtonWithText text="Continue Docs"> +          <HeaderButtonWithText text="Docs">              <BookOpen size="1.6em" />            </HeaderButtonWithText>          </a> | 
