From d566dc90a32db926393e1d10f983c79bc3feb56a Mon Sep 17 00:00:00 2001
From: Nate Sesti <sestinj@gmail.com>
Date: Sun, 9 Jul 2023 16:19:16 -0700
Subject: cache server env in .continue folder

---
 extension/react-app/src/tabs/gui.tsx | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'extension/react-app/src/tabs/gui.tsx')

diff --git a/extension/react-app/src/tabs/gui.tsx b/extension/react-app/src/tabs/gui.tsx
index e1ecec9e..646aef50 100644
--- a/extension/react-app/src/tabs/gui.tsx
+++ b/extension/react-app/src/tabs/gui.tsx
@@ -438,7 +438,7 @@ function GUI(props: GUIProps) {
             if (!usingFastModel) {
               // Show the dialog
               setFeedbackDialogMessage(
-                "We don't yet support local models, but we're working on it! If privacy is a concern of yours, please use the feedback button in the bottom right to let us know."
+                "We don't yet support local models, but we're working on it! If privacy is a concern of yours, please write a short note to let us know."
               );
               setShowFeedbackDialog(true);
             }
-- 
cgit v1.2.3-70-g09d2


From b9525d5f0851d13b3d4936868f064810f3376dbd Mon Sep 17 00:00:00 2001
From: Ty Dunn <ty@tydunn.com>
Date: Mon, 10 Jul 2023 15:25:29 -0700
Subject: adding onboarding

---
 extension/react-app/public/edit.gif               | Bin 0 -> 41667733 bytes
 extension/react-app/public/explain.gif            | Bin 0 -> 56475028 bytes
 extension/react-app/public/generate.gif           | Bin 0 -> 32532380 bytes
 extension/react-app/public/intro.gif              | Bin 0 -> 3976676 bytes
 extension/react-app/src/components/Onboarding.tsx |  51 ++++++++++++++++++++++
 extension/react-app/src/tabs/gui.tsx              |   2 +
 6 files changed, 53 insertions(+)
 create mode 100644 extension/react-app/public/edit.gif
 create mode 100644 extension/react-app/public/explain.gif
 create mode 100644 extension/react-app/public/generate.gif
 create mode 100644 extension/react-app/public/intro.gif
 create mode 100644 extension/react-app/src/components/Onboarding.tsx

(limited to 'extension/react-app/src/tabs/gui.tsx')

diff --git a/extension/react-app/public/edit.gif b/extension/react-app/public/edit.gif
new file mode 100644
index 00000000..6780cdf7
Binary files /dev/null and b/extension/react-app/public/edit.gif differ
diff --git a/extension/react-app/public/explain.gif b/extension/react-app/public/explain.gif
new file mode 100644
index 00000000..e74803dc
Binary files /dev/null and b/extension/react-app/public/explain.gif differ
diff --git a/extension/react-app/public/generate.gif b/extension/react-app/public/generate.gif
new file mode 100644
index 00000000..5c1d112b
Binary files /dev/null and b/extension/react-app/public/generate.gif differ
diff --git a/extension/react-app/public/intro.gif b/extension/react-app/public/intro.gif
new file mode 100644
index 00000000..f872dc91
Binary files /dev/null and b/extension/react-app/public/intro.gif differ
diff --git a/extension/react-app/src/components/Onboarding.tsx b/extension/react-app/src/components/Onboarding.tsx
new file mode 100644
index 00000000..373f7bea
--- /dev/null
+++ b/extension/react-app/src/components/Onboarding.tsx
@@ -0,0 +1,51 @@
+import { useSelector } from "react-redux";
+import { RootStore } from "../redux/store";
+import React, { useState } from 'react';
+
+
+const Onboarding = () => {
+    const [counter, setCounter] = useState(0);
+
+    const handleClick = () => {
+        setCounter(counter + 1);
+    }
+
+    const vscMediaUrl = useSelector(
+        (state: RootStore) => state.config.vscMediaUrl
+      );
+
+    return (
+    <div style={{position: 'absolute', top: '0', left: '0', width: '100%', height: '100%', backgroundColor: '#1E1E1E', zIndex: 200 }} onClick={handleClick}>
+    {counter === 0 && (
+    <div style={{display: 'flex', flexDirection: 'column', justifyContent: 'center', alignItems: 'center', height: '100%'}}>
+    <h1>Welcome to Continue!</h1>
+    <img src={`${vscMediaUrl}/intro.gif`} alt="Intro" />
+    </div>
+    )}
+    {counter === 1 && (
+    <div style={{display: 'flex', flexDirection: 'column', justifyContent: 'center', alignItems: 'center', height: '100%'}}>
+    <h1>Answer coding questions</h1>
+    <img src={`${vscMediaUrl}/explain.gif`} alt="Explain" />
+    <p style={{ fontSize: '16px', paddingLeft: '50px', paddingRight: '50px', textAlign: 'center' }}>Ask Continue about a part of your code to get another perspective</p>
+    </div>
+    )}
+    {counter === 2 && (
+    <div style={{display: 'flex', flexDirection: 'column', justifyContent: 'center', alignItems: 'center', height: '100%'}}>
+    <h1>Edit in natural language</h1>
+    <img src={`${vscMediaUrl}/edit.gif`} alt="Edit" />
+    <p style={{ fontSize: '16px', paddingLeft: '50px', paddingRight: '50px', textAlign: 'center' }}>Highlight a section of code and instruct Continue to refactor it</p>
+    </div>
+    )}
+    {counter === 3 && (
+    <div style={{display: 'flex', flexDirection: 'column', justifyContent: 'center', alignItems: 'center', height: '100%'}}>
+    <h1>Generate files from scratch</h1>
+    <img src={`${vscMediaUrl}/generate.gif`} alt="Generate" />
+    <p style={{ fontSize: '16px', paddingLeft: '50px', paddingRight: '50px', textAlign: 'center' }}>Let Continue build the scaffolding of Python scripts, React components, and more</p>
+    </div>
+    )}
+    <p style={{ paddingLeft: '50px', paddingRight: '50px', paddingBottom: '50px', textAlign: 'center' }}>Click to learn how to use Continue...</p>
+    </div>
+    );
+}
+
+export default Onboarding;
diff --git a/extension/react-app/src/tabs/gui.tsx b/extension/react-app/src/tabs/gui.tsx
index e1ecec9e..14556829 100644
--- a/extension/react-app/src/tabs/gui.tsx
+++ b/extension/react-app/src/tabs/gui.tsx
@@ -23,6 +23,7 @@ import { RootStore } from "../redux/store";
 import LoadingCover from "../components/LoadingCover";
 import { postVscMessage } from "../vscode";
 import UserInputContainer from "../components/UserInputContainer";
+import Onboarding from "../components/Onboarding";
 
 const TopGUIDiv = styled.div`
   overflow: hidden;
@@ -267,6 +268,7 @@ function GUI(props: GUIProps) {
   // const iterations = useSelector(selectIterations);
   return (
     <>
+      <Onboarding></Onboarding>
       <LoadingCover hidden={true} message="Downloading local model..." />
       <TextDialog
         showDialog={showFeedbackDialog}
-- 
cgit v1.2.3-70-g09d2


From 3fc16804fe74c38d4f94c25281025a84fd51771b Mon Sep 17 00:00:00 2001
From: Nate Sesti <sestinj@gmail.com>
Date: Tue, 11 Jul 2023 14:13:14 -0700
Subject: little ui stuff

---
 extension/react-app/src/components/ComboBox.tsx |  3 ++-
 extension/react-app/src/tabs/gui.tsx            | 11 +++--------
 2 files changed, 5 insertions(+), 9 deletions(-)

(limited to 'extension/react-app/src/tabs/gui.tsx')

diff --git a/extension/react-app/src/components/ComboBox.tsx b/extension/react-app/src/components/ComboBox.tsx
index 585a0584..ac994b0a 100644
--- a/extension/react-app/src/components/ComboBox.tsx
+++ b/extension/react-app/src/components/ComboBox.tsx
@@ -378,7 +378,8 @@ const ComboBox = React.forwardRef((props: ComboBoxProps, ref) => {
         </Ul>
       </div>
       {highlightedCodeSections.length === 0 &&
-        (downshiftProps.inputValue?.startsWith("/edit") || metaKeyPressed) && (
+        (downshiftProps.inputValue?.startsWith("/edit") ||
+          (metaKeyPressed && downshiftProps.inputValue?.length > 0)) && (
           <div className="text-trueGray-400 pr-4 text-xs text-right">
             Inserting at cursor
           </div>
diff --git a/extension/react-app/src/tabs/gui.tsx b/extension/react-app/src/tabs/gui.tsx
index 0e60e05c..619b91e1 100644
--- a/extension/react-app/src/tabs/gui.tsx
+++ b/extension/react-app/src/tabs/gui.tsx
@@ -170,6 +170,7 @@ function GUI(props: GUIProps) {
       const waitingForSteps =
         state.active &&
         state.history.current_index < state.history.timeline.length &&
+        state.history.timeline[state.history.current_index] &&
         state.history.timeline[
           state.history.current_index
         ].step.description?.trim() === "";
@@ -236,14 +237,14 @@ function GUI(props: GUIProps) {
         history.current_index < history.timeline.length
       ) {
         if (
-          history.timeline[history.current_index].step.name ===
+          history.timeline[history.current_index]?.step.name ===
           "Waiting for user input"
         ) {
           if (input.trim() === "") return;
           onStepUserInput(input, history!.current_index);
           return;
         } else if (
-          history.timeline[history.current_index].step.name ===
+          history.timeline[history.current_index]?.step.name ===
           "Waiting for user confirmation"
         ) {
           onStepUserInput("ok", history!.current_index);
@@ -350,12 +351,6 @@ function GUI(props: GUIProps) {
         </div>
 
         <ComboBox
-          // disabled={
-          //   history?.timeline.length
-          //     ? history.timeline[history.current_index].step.name ===
-          //       "Waiting for user confirmation"
-          //     : false
-          // }
           ref={mainTextInputRef}
           onEnter={(e) => {
             onMainTextInput(e);
-- 
cgit v1.2.3-70-g09d2


From 856d450c7277edd0302f17365322e73dc37808e0 Mon Sep 17 00:00:00 2001
From: Nate Sesti <sestinj@gmail.com>
Date: Wed, 12 Jul 2023 09:42:25 -0700
Subject: patch, don't delete non-active steps with cmd+back

---
 extension/package-lock.json          | 4 ++--
 extension/package.json               | 2 +-
 extension/react-app/src/tabs/gui.tsx | 3 ++-
 3 files changed, 5 insertions(+), 4 deletions(-)

(limited to 'extension/react-app/src/tabs/gui.tsx')

diff --git a/extension/package-lock.json b/extension/package-lock.json
index 683bf261..d310643b 100644
--- a/extension/package-lock.json
+++ b/extension/package-lock.json
@@ -1,12 +1,12 @@
 {
   "name": "continue",
-  "version": "0.0.152",
+  "version": "0.0.153",
   "lockfileVersion": 2,
   "requires": true,
   "packages": {
     "": {
       "name": "continue",
-      "version": "0.0.152",
+      "version": "0.0.153",
       "license": "Apache-2.0",
       "dependencies": {
         "@electron/rebuild": "^3.2.10",
diff --git a/extension/package.json b/extension/package.json
index a0f970ed..61eee205 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.152",
+  "version": "0.0.153",
   "publisher": "Continue",
   "engines": {
     "vscode": "^1.67.0"
diff --git a/extension/react-app/src/tabs/gui.tsx b/extension/react-app/src/tabs/gui.tsx
index 619b91e1..b6a18dc8 100644
--- a/extension/react-app/src/tabs/gui.tsx
+++ b/extension/react-app/src/tabs/gui.tsx
@@ -147,7 +147,8 @@ function GUI(props: GUIProps) {
       } else if (
         e.key === "Backspace" &&
         e.metaKey &&
-        typeof history?.current_index !== "undefined"
+        typeof history?.current_index !== "undefined" &&
+        history.timeline[history.current_index]?.active
       ) {
         client?.deleteAtIndex(history.current_index);
       }
-- 
cgit v1.2.3-70-g09d2


From 93ac6b52c9e5067c9703d096104c99f4f29e8eb5 Mon Sep 17 00:00:00 2001
From: Nate Sesti <sestinj@gmail.com>
Date: Wed, 12 Jul 2023 13:22:40 -0700
Subject: purging unecessary files

---
 extension/react-app/src/App.tsx                    |  13 +-
 extension/react-app/src/pages/gui.tsx              | 486 +++++++++++++++++++++
 extension/react-app/src/tabs/additionalContext.tsx |  18 -
 extension/react-app/src/tabs/chat/MessageDiv.tsx   |  75 ----
 extension/react-app/src/tabs/chat/index.tsx        | 267 -----------
 extension/react-app/src/tabs/gui.tsx               | 486 ---------------------
 extension/react-app/src/tabs/main.tsx              | 189 --------
 extension/react-app/src/tabs/welcome.tsx           |  22 -
 extension/src/bridge.ts                            | 215 +--------
 9 files changed, 492 insertions(+), 1279 deletions(-)
 create mode 100644 extension/react-app/src/pages/gui.tsx
 delete mode 100644 extension/react-app/src/tabs/additionalContext.tsx
 delete mode 100644 extension/react-app/src/tabs/chat/MessageDiv.tsx
 delete mode 100644 extension/react-app/src/tabs/chat/index.tsx
 delete mode 100644 extension/react-app/src/tabs/gui.tsx
 delete mode 100644 extension/react-app/src/tabs/main.tsx
 delete mode 100644 extension/react-app/src/tabs/welcome.tsx

(limited to 'extension/react-app/src/tabs/gui.tsx')

diff --git a/extension/react-app/src/App.tsx b/extension/react-app/src/App.tsx
index 8785f88f..c9bd42e0 100644
--- a/extension/react-app/src/App.tsx
+++ b/extension/react-app/src/App.tsx
@@ -1,8 +1,5 @@
 import DebugPanel from "./components/DebugPanel";
-import MainTab from "./tabs/main";
-import WelcomeTab from "./tabs/welcome";
-import ChatTab from "./tabs/chat";
-import GUI from "./tabs/gui";
+import GUI from "./pages/gui";
 import { createContext } from "react";
 import useContinueGUIProtocol from "./hooks/useWebsocket";
 import ContinueGUIClientProtocol from "./hooks/useContinueGUIProtocol";
@@ -18,13 +15,7 @@ function App() {
     <GUIClientContext.Provider value={client}>
       <DebugPanel
         tabs={[
-          {
-            element: <GUI />,
-            title: "GUI",
-          },
-          // { element: <MainTab />, title: "Debug Panel" },
-          // { element: <WelcomeTab />, title: "Welcome" },
-          // { element: <ChatTab />, title: "Chat" },
+          { element: <GUI />, title: "GUI" }
         ]}
       />
     </GUIClientContext.Provider>
diff --git a/extension/react-app/src/pages/gui.tsx b/extension/react-app/src/pages/gui.tsx
new file mode 100644
index 00000000..b6a18dc8
--- /dev/null
+++ b/extension/react-app/src/pages/gui.tsx
@@ -0,0 +1,486 @@
+import styled from "styled-components";
+import { defaultBorderRadius } from "../components";
+import Loader from "../components/Loader";
+import ContinueButton from "../components/ContinueButton";
+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 { GUIClientContext } from "../App";
+import {
+  BookOpen,
+  ChatBubbleOvalLeftEllipsis,
+  Trash,
+} from "@styled-icons/heroicons-outline";
+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";
+import { postVscMessage } from "../vscode";
+import UserInputContainer from "../components/UserInputContainer";
+import Onboarding from "../components/Onboarding";
+
+const TopGUIDiv = styled.div`
+  overflow: hidden;
+`;
+
+const UserInputQueueItem = styled.div`
+  border-radius: ${defaultBorderRadius};
+  color: gray;
+  padding: 8px;
+  margin: 8px;
+  text-align: center;
+`;
+
+const Footer = styled.footer<{ dataSwitchChecked: boolean }>`
+  display: flex;
+  flex-direction: row;
+  gap: 8px;
+  justify-content: right;
+  padding: 8px;
+  align-items: center;
+  margin-top: 8px;
+  border-top: 0.1px solid gray;
+  background-color: ${(props) =>
+    props.dataSwitchChecked ? "#12887a33" : "transparent"};
+`;
+
+interface GUIProps {
+  firstObservation?: any;
+}
+
+function GUI(props: GUIProps) {
+  const client = useContext(GUIClientContext);
+  const posthog = usePostHog();
+  const vscMachineId = useSelector(
+    (state: RootStore) => state.config.vscMachineId
+  );
+  const [dataSwitchChecked, setDataSwitchChecked] = useState(false);
+  const dataSwitchOn = useSelector(
+    (state: RootStore) => state.config.dataSwitchOn
+  );
+
+  useEffect(() => {
+    if (typeof dataSwitchOn !== "undefined") {
+      setDataSwitchChecked(dataSwitchOn);
+    }
+  }, [dataSwitchOn]);
+
+  const [usingFastModel, setUsingFastModel] = useState(false);
+  const [waitingForSteps, setWaitingForSteps] = useState(false);
+  const [userInputQueue, setUserInputQueue] = useState<string[]>([]);
+  const [highlightedRanges, setHighlightedRanges] = useState<
+    HighlightedRangeContext[]
+  >([]);
+  const [addingHighlightedCode, setAddingHighlightedCode] = useState(false);
+  const [availableSlashCommands, setAvailableSlashCommands] = useState<
+    { name: string; description: string }[]
+  >([]);
+  const [pinned, setPinned] = useState(false);
+  const [showDataSharingInfo, setShowDataSharingInfo] = useState(false);
+  const [stepsOpen, setStepsOpen] = useState<boolean[]>([
+    true,
+    true,
+    true,
+    true,
+  ]);
+  const [history, setHistory] = useState<History | undefined>({
+    timeline: [
+      {
+        step: {
+          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`,
+          system_message: null,
+          chat_context: [],
+          manage_own_chat_context: false,
+          message: "",
+        },
+        depth: 0,
+        deleted: false,
+        active: false,
+      },
+    ],
+    current_index: 3,
+  } as any);
+
+  const [showFeedbackDialog, setShowFeedbackDialog] = useState(false);
+  const [feedbackDialogMessage, setFeedbackDialogMessage] = useState("");
+
+  const topGuiDivRef = useRef<HTMLDivElement>(null);
+
+  const [scrollTimeout, setScrollTimeout] = useState<NodeJS.Timeout | null>(
+    null
+  );
+  const scrollToBottom = useCallback(() => {
+    if (scrollTimeout) {
+      clearTimeout(scrollTimeout);
+    }
+    // Debounced smooth scroll to bottom of screen
+    if (topGuiDivRef.current) {
+      const timeout = setTimeout(() => {
+        window.scrollTo({
+          top: topGuiDivRef.current!.offsetHeight,
+          behavior: "smooth",
+        });
+      }, 200);
+      setScrollTimeout(timeout);
+    }
+  }, [topGuiDivRef.current, scrollTimeout]);
+
+  useEffect(() => {
+    const listener = (e: any) => {
+      // Cmd + i to toggle fast model
+      if (e.key === "i" && e.metaKey && e.shiftKey) {
+        setUsingFastModel((prev) => !prev);
+        // Cmd + backspace to stop currently running step
+      } else if (
+        e.key === "Backspace" &&
+        e.metaKey &&
+        typeof history?.current_index !== "undefined" &&
+        history.timeline[history.current_index]?.active
+      ) {
+        client?.deleteAtIndex(history.current_index);
+      }
+    };
+    window.addEventListener("keydown", listener);
+
+    return () => {
+      window.removeEventListener("keydown", listener);
+    };
+  }, [client, history]);
+
+  useEffect(() => {
+    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 =
+        topGuiDivRef.current &&
+        topGuiDivRef.current?.offsetHeight - window.scrollY < 100;
+
+      const waitingForSteps =
+        state.active &&
+        state.history.current_index < state.history.timeline.length &&
+        state.history.timeline[state.history.current_index] &&
+        state.history.timeline[
+          state.history.current_index
+        ].step.description?.trim() === "";
+
+      setWaitingForSteps(waitingForSteps);
+      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 {
+            name: `/${c.name}`,
+            description: c.description,
+          };
+        })
+      );
+      setStepsOpen((prev) => {
+        const nextStepsOpen = [...prev];
+        for (
+          let i = nextStepsOpen.length;
+          i < state.history.timeline.length;
+          i++
+        ) {
+          nextStepsOpen.push(true);
+        }
+        return nextStepsOpen;
+      });
+
+      if (shouldScrollToBottom) {
+        scrollToBottom();
+      }
+    });
+  }, [client]);
+
+  useEffect(() => {
+    scrollToBottom();
+  }, [waitingForSteps]);
+
+  const mainTextInputRef = useRef<HTMLInputElement>(null);
+
+  const deleteContextItems = useCallback(
+    (indices: number[]) => {
+      client?.deleteContextAtIndices(indices);
+    },
+    [client]
+  );
+
+  const onMainTextInput = (event?: any) => {
+    if (mainTextInputRef.current) {
+      let input = (mainTextInputRef.current as any).inputValue;
+      // cmd+enter to /edit
+      if (event?.metaKey) {
+        input = `/edit ${input}`;
+      }
+      (mainTextInputRef.current as any).setInputValue("");
+      if (!client) return;
+
+      setWaitingForSteps(true);
+
+      if (
+        history &&
+        history.current_index >= 0 &&
+        history.current_index < history.timeline.length
+      ) {
+        if (
+          history.timeline[history.current_index]?.step.name ===
+          "Waiting for user input"
+        ) {
+          if (input.trim() === "") return;
+          onStepUserInput(input, history!.current_index);
+          return;
+        } else if (
+          history.timeline[history.current_index]?.step.name ===
+          "Waiting for user confirmation"
+        ) {
+          onStepUserInput("ok", history!.current_index);
+          return;
+        }
+      }
+      if (input.trim() === "") return;
+
+      client.sendMainInput(input);
+      setUserInputQueue((queue) => {
+        return [...queue, input];
+      });
+    }
+  };
+
+  const onStepUserInput = (input: string, index: number) => {
+    if (!client) return;
+    console.log("Sending step user input", input, index);
+    client.sendStepUserInput(input, index);
+  };
+
+  // const iterations = useSelector(selectIterations);
+  return (
+    <>
+      <Onboarding></Onboarding>
+      <LoadingCover hidden={true} message="Downloading local model..." />
+      <TextDialog
+        showDialog={showFeedbackDialog}
+        onEnter={(text) => {
+          client?.sendMainInput(`/feedback ${text}`);
+          setShowFeedbackDialog(false);
+        }}
+        onClose={() => {
+          setShowFeedbackDialog(false);
+        }}
+        message={feedbackDialogMessage}
+      ></TextDialog>
+
+      <TopGUIDiv
+        ref={topGuiDivRef}
+        onKeyDown={(e) => {
+          if (e.key === "Enter" && e.ctrlKey) {
+            onMainTextInput();
+          }
+        }}
+      >
+        {typeof client === "undefined" && (
+          <>
+            <Loader />
+            <p style={{ textAlign: "center" }}>Loading Continue server...</p>
+          </>
+        )}
+        {history?.timeline.map((node: HistoryNode, index: number) => {
+          return node.step.name === "User Input" ? (
+            node.step.hide || (
+              <UserInputContainer
+                onDelete={() => {
+                  client?.deleteAtIndex(index);
+                }}
+                historyNode={node}
+              >
+                {node.step.description as string}
+              </UserInputContainer>
+            )
+          ) : (
+            <StepContainer
+              isLast={index === history.timeline.length - 1}
+              isFirst={index === 0}
+              open={stepsOpen[index]}
+              onToggle={() => {
+                const nextStepsOpen = [...stepsOpen];
+                nextStepsOpen[index] = !nextStepsOpen[index];
+                setStepsOpen(nextStepsOpen);
+              }}
+              onToggleAll={() => {
+                const shouldOpen = !stepsOpen[index];
+                setStepsOpen((prev) => prev.map(() => shouldOpen));
+              }}
+              key={index}
+              onUserInput={(input: string) => {
+                onStepUserInput(input, index);
+              }}
+              inFuture={index > history?.current_index}
+              historyNode={node}
+              onReverse={() => {
+                client?.reverseToIndex(index);
+              }}
+              onRetry={() => {
+                client?.retryAtIndex(index);
+                setWaitingForSteps(true);
+              }}
+              onDelete={() => {
+                client?.deleteAtIndex(index);
+              }}
+            />
+          );
+        })}
+        {waitingForSteps && <Loader></Loader>}
+
+        <div>
+          {userInputQueue.map((input) => {
+            return <UserInputQueueItem>{input}</UserInputQueueItem>;
+          })}
+        </div>
+
+        <ComboBox
+          ref={mainTextInputRef}
+          onEnter={(e) => {
+            onMainTextInput(e);
+            e.stopPropagation();
+            e.preventDefault();
+          }}
+          onInputValueChange={() => {}}
+          items={availableSlashCommands}
+          highlightedCodeSections={highlightedRanges}
+          deleteContextItems={deleteContextItems}
+          onTogglePin={() => {
+            setPinned((prev: boolean) => !prev);
+          }}
+          onToggleAddContext={() => {
+            client?.toggleAddingHighlightedCode();
+          }}
+          addingHighlightedCode={addingHighlightedCode}
+        />
+        <ContinueButton onClick={onMainTextInput} />
+      </TopGUIDiv>
+      <div
+        style={{
+          position: "fixed",
+          bottom: "50px",
+          backgroundColor: "white",
+          color: "black",
+          borderRadius: defaultBorderRadius,
+          padding: "16px",
+          margin: "16px",
+          zIndex: 100,
+        }}
+        hidden={!showDataSharingInfo}
+      >
+        By turning on this switch, you will begin collecting accepted and
+        rejected suggestions in .continue/suggestions.json. This data is stored
+        locally on your machine and not sent anywhere.
+        <br />
+        <br />
+        <b>
+          {dataSwitchChecked
+            ? "👍 Data is being collected"
+            : "👎 No data is being collected"}
+        </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,
+              });
+              postVscMessage("toggleDataSwitch", { on: !dataSwitchChecked });
+              setDataSwitchChecked((prev) => !prev);
+            }}
+            onColor="#12887a"
+            checked={dataSwitchChecked}
+          />
+          <span style={{ cursor: "help", fontSize: "14px" }}>Collect Data</span>
+        </div>
+        <HeaderButtonWithText
+          onClick={() => {
+            // client?.changeDefaultModel(
+            //   usingFastModel ? "gpt-4" : "gpt-3.5-turbo"
+            // );
+            if (!usingFastModel) {
+              // Show the dialog
+              setFeedbackDialogMessage(
+                "We don't yet support local models, but we're working on it! If privacy is a concern of yours, please write a short note to let us know."
+              );
+              setShowFeedbackDialog(true);
+            }
+            setUsingFastModel((prev) => !prev);
+          }}
+          text={usingFastModel ? "local" : "gpt-4"}
+        >
+          <div
+            style={{ fontSize: "18px", marginLeft: "2px", marginRight: "2px" }}
+          >
+            {usingFastModel ? "🔒" : "🧠"}
+          </div>
+        </HeaderButtonWithText>
+        <HeaderButtonWithText
+          onClick={() => {
+            client?.sendClear();
+          }}
+          text="Clear"
+        >
+          <Trash size="1.6em" />
+        </HeaderButtonWithText>
+        <a
+          href="https://continue.dev/docs/how-to-use-continue"
+          className="no-underline"
+        >
+          <HeaderButtonWithText text="Docs">
+            <BookOpen size="1.6em" />
+          </HeaderButtonWithText>
+        </a>
+        <HeaderButtonWithText
+          onClick={() => {
+            // Set dialog open
+            setFeedbackDialogMessage(
+              "Having trouble using Continue? Want a new feature? Let us know! This box is anonymous, but we will promptly address your feedback."
+            );
+            setShowFeedbackDialog(true);
+          }}
+          text="Feedback"
+        >
+          <ChatBubbleOvalLeftEllipsis size="1.6em" />
+        </HeaderButtonWithText>
+      </Footer>
+    </>
+  );
+}
+
+export default GUI;
diff --git a/extension/react-app/src/tabs/additionalContext.tsx b/extension/react-app/src/tabs/additionalContext.tsx
deleted file mode 100644
index 98fce9f1..00000000
--- a/extension/react-app/src/tabs/additionalContext.tsx
+++ /dev/null
@@ -1,18 +0,0 @@
-import React from "react";
-import { H3, TextArea } from "../components";
-
-function AdditionalContextTab() {
-  return (
-    <div className="mx-5">
-      <H3>Additional Context</H3>
-      <TextArea
-        rows={8}
-        placeholder="Copy and paste information related to the bug from GitHub Issues, Slack threads, or other notes here."
-        className="additionalContextTextarea"
-      ></TextArea>
-      <br></br>
-    </div>
-  );
-}
-
-export default AdditionalContextTab;
diff --git a/extension/react-app/src/tabs/chat/MessageDiv.tsx b/extension/react-app/src/tabs/chat/MessageDiv.tsx
deleted file mode 100644
index 3543dd93..00000000
--- a/extension/react-app/src/tabs/chat/MessageDiv.tsx
+++ /dev/null
@@ -1,75 +0,0 @@
-import React, { useEffect } from "react";
-import { ChatMessage } from "../../redux/store";
-import styled from "styled-components";
-import {
-  buttonColor,
-  defaultBorderRadius,
-  secondaryDark,
-} from "../../components";
-import VSCodeFileLink from "../../components/VSCodeFileLink";
-import ReactMarkdown from "react-markdown";
-import "../../highlight/dark.min.css";
-import hljs from "highlight.js";
-import { useSelector } from "react-redux";
-import { selectIsStreaming } from "../../redux/selectors/chatSelectors";
-
-const Container = styled.div`
-  padding-left: 8px;
-  padding-right: 8px;
-  border-radius: 8px;
-  margin: 3px;
-  width: fit-content;
-  max-width: 75%;
-  overflow-y: scroll;
-  word-wrap: break-word;
-  -ms-word-wrap: break-word;
-  height: fit-content;
-  overflow: hidden;
-  background-color: ${(props) => {
-    if (props.role === "user") {
-      return buttonColor;
-    } else {
-      return secondaryDark;
-    }
-  }};
-  float: ${(props) => {
-    if (props.role === "user") {
-      return "right";
-    } else {
-      return "left";
-    }
-  }};
-  display: block;
-
-  & pre {
-    border: 1px solid gray;
-    border-radius: ${defaultBorderRadius};
-  }
-`;
-
-function MessageDiv(props: ChatMessage) {
-  const [richContent, setRichContent] = React.useState<JSX.Element[]>([]);
-  const isStreaming = useSelector(selectIsStreaming);
-
-  useEffect(() => {
-    if (!isStreaming) {
-      hljs.highlightAll();
-    }
-  }, [richContent, isStreaming]);
-
-  useEffect(() => {
-    setRichContent([
-      <ReactMarkdown key={1} children={props.content}></ReactMarkdown>,
-    ]);
-  }, [props.content]);
-
-  return (
-    <>
-      <div className="overflow-auto">
-        <Container role={props.role}>{richContent}</Container>
-      </div>
-    </>
-  );
-}
-
-export default MessageDiv;
diff --git a/extension/react-app/src/tabs/chat/index.tsx b/extension/react-app/src/tabs/chat/index.tsx
deleted file mode 100644
index a93ad4f9..00000000
--- a/extension/react-app/src/tabs/chat/index.tsx
+++ /dev/null
@@ -1,267 +0,0 @@
-import React, { useCallback, useEffect, useRef, useState } from "react";
-import { useDispatch, useSelector } from "react-redux";
-import { selectChatMessages } from "../../redux/selectors/chatSelectors";
-import MessageDiv from "./MessageDiv";
-import styled from "styled-components";
-import { addMessage, setIsStreaming } from "../../redux/slices/chatSlice";
-import { AnyAction, Dispatch } from "@reduxjs/toolkit";
-import { closeStream, streamUpdate } from "../../redux/slices/chatSlice";
-import { ChatMessage, RootStore } from "../../redux/store";
-import { postVscMessage, vscRequest } from "../../vscode";
-import { defaultBorderRadius, Loader } from "../../components";
-import { selectHighlightedCode } from "../../redux/selectors/miscSelectors";
-import { readRangeInVirtualFileSystem } from "../../util";
-import { selectDebugContext } from "../../redux/selectors/debugContextSelectors";
-
-let textEntryBarHeight = "30px";
-
-const ChatContainer = styled.div`
-  display: grid;
-  grid-template-rows: 1fr auto;
-  height: 100%;
-`;
-
-const BottomDiv = styled.div`
-  display: grid;
-  grid-template-rows: auto auto;
-`;
-
-const BottomButton = styled.button(
-  (props: { active: boolean }) => `
-  font-size: 10px;
-  border: none;
-  color: white;
-  margin-right: 4px;
-  cursor: pointer;
-  background-color: ${props.active ? "black" : "gray"};
-  border-radius: ${defaultBorderRadius};
-  padding: 8px;
-`
-);
-
-const TextEntryBar = styled.input`
-  height: ${textEntryBarHeight};
-  border-bottom-left-radius: ${defaultBorderRadius};
-  border-bottom-right-radius: ${defaultBorderRadius};
-  padding: 8px;
-  border: 1px solid white;
-  background-color: black;
-  color: white;
-  outline: none;
-`;
-
-function ChatTab() {
-  const dispatch = useDispatch();
-  const chatMessages = useSelector(selectChatMessages);
-  const isStreaming = useSelector((state: RootStore) => state.chat.isStreaming);
-  const baseUrl = useSelector((state: RootStore) => state.config.apiUrl);
-  const debugContext = useSelector(selectDebugContext);
-
-  const [includeHighlightedCode, setIncludeHighlightedCode] = useState(true);
-  const [writeToEditor, setWriteToEditor] = useState(false);
-  const [waitingForResponse, setWaitingForResponse] = useState(false);
-
-  const highlightedCode = useSelector(selectHighlightedCode);
-
-  const streamToStateThunk = useCallback(
-    (dispatch: Dispatch<AnyAction>, getResponse: () => Promise<Response>) => {
-      let streamToCursor = writeToEditor;
-      getResponse().then((resp) => {
-        setWaitingForResponse(false);
-        if (resp.body) {
-          resp.body.pipeTo(
-            new WritableStream({
-              write(chunk) {
-                let update = new TextDecoder("utf-8").decode(chunk);
-                dispatch(streamUpdate(update));
-                if (streamToCursor) {
-                  postVscMessage("streamUpdate", { update });
-                }
-              },
-              close() {
-                dispatch(closeStream());
-                if (streamToCursor) {
-                  postVscMessage("closeStream", null);
-                }
-              },
-            })
-          );
-        }
-      });
-    },
-    [writeToEditor]
-  );
-
-  const compileHiddenChatMessages = useCallback(async () => {
-    let messages: ChatMessage[] = [];
-    if (
-      includeHighlightedCode &&
-      highlightedCode?.filepath !== undefined &&
-      highlightedCode?.range !== undefined &&
-      debugContext.filesystem[highlightedCode.filepath] !== undefined
-    ) {
-      let fileContents = readRangeInVirtualFileSystem(
-        highlightedCode,
-        debugContext.filesystem
-      );
-      if (fileContents) {
-        messages.push({
-          role: "user",
-          content: fileContents,
-        });
-      }
-    } else {
-      // Similarity search over workspace
-      let data = await vscRequest("queryEmbeddings", {
-        query: chatMessages[chatMessages.length - 1].content,
-      });
-      let codeContextMessages = data.results.map(
-        (result: { id: string; document: string }) => {
-          let msg: ChatMessage = {
-            role: "user",
-            content: `File: ${result.id} \n ${result.document}`,
-          };
-          return msg;
-        }
-      );
-      codeContextMessages.push({
-        role: "user",
-        content:
-          "Use the above code to help you answer the question below. Answer in asterisk bullet points, and give the full path whenever you reference files.",
-      });
-      messages.push(...codeContextMessages);
-    }
-
-    let systemMsgContent = writeToEditor
-      ? "Respond only with the exact code requested, no additional text."
-      : "Use the above code to help you answer the question below. Respond in markdown if using bullets or other special formatting, being sure to specify language for code blocks.";
-
-    messages.push({
-      role: "system",
-      content: systemMsgContent,
-    });
-    return messages;
-  }, [highlightedCode, chatMessages, includeHighlightedCode, writeToEditor]);
-
-  useEffect(() => {
-    if (
-      chatMessages.length > 0 &&
-      chatMessages[chatMessages.length - 1].role === "user" &&
-      !isStreaming
-    ) {
-      dispatch(setIsStreaming(true));
-      streamToStateThunk(dispatch, async () => {
-        if (chatMessages.length === 0) {
-          return new Promise((resolve, _) => resolve(new Response()));
-        }
-        let hiddenChatMessages = await compileHiddenChatMessages();
-        let augmentedMessages = [
-          ...chatMessages.slice(0, -1),
-          ...hiddenChatMessages,
-          chatMessages[chatMessages.length - 1],
-        ];
-        console.log(augmentedMessages);
-        // The autogenerated client can't handle streams, so have to go raw
-        return fetch(`${baseUrl}/chat/complete`, {
-          method: "POST",
-          headers: {
-            "Content-Type": "application/json",
-          },
-          body: JSON.stringify({
-            messages: augmentedMessages,
-          }),
-        });
-      });
-    }
-  }, [chatMessages, dispatch, isStreaming, highlightedCode]);
-
-  const chatMessagesDiv = useRef<HTMLDivElement>(null);
-  useEffect(() => {
-    // Scroll to bottom
-    let interval = setInterval(() => {
-      if (chatMessagesDiv.current && !waitingForResponse) {
-        chatMessagesDiv.current.scrollTop += Math.max(
-          4,
-          0.05 * chatMessagesDiv.current.scrollHeight -
-            chatMessagesDiv.current.clientHeight -
-            chatMessagesDiv.current.scrollTop
-        );
-        if (
-          chatMessagesDiv.current.scrollTop >=
-          chatMessagesDiv.current.scrollHeight -
-            chatMessagesDiv.current.clientHeight
-        ) {
-          clearInterval(interval);
-        }
-      }
-    }, 10);
-  }, [chatMessages, chatMessagesDiv, waitingForResponse]);
-
-  return (
-    <ChatContainer>
-      <div className="mx-5 overflow-y-scroll" ref={chatMessagesDiv}>
-        <h1>Chat</h1>
-        <hr></hr>
-        <div>
-          {chatMessages.length > 0 ? (
-            chatMessages.map((message, idx) => {
-              return <MessageDiv key={idx} {...message}></MessageDiv>;
-            })
-          ) : (
-            <p className="text-gray-400 m-auto text-center">
-              You can ask questions about your codebase or ask for code written
-              directly in the editor.
-            </p>
-          )}
-          {waitingForResponse && <Loader></Loader>}
-        </div>
-      </div>
-
-      <BottomDiv>
-        <div className="h-12 bg-secondary-">
-          <div className="flex items-center p-2">
-            {/* <p className="mr-auto text-xs">
-              Highlighted code is automatically included in your chat message.
-            </p> */}
-            <BottomButton
-              className="ml-auto"
-              active={writeToEditor}
-              onClick={() => {
-                setWriteToEditor(!writeToEditor);
-              }}
-            >
-              {writeToEditor ? "Writing to editor" : "Write to editor"}
-            </BottomButton>
-
-            <BottomButton
-              active={includeHighlightedCode}
-              onClick={() => {
-                setIncludeHighlightedCode(!includeHighlightedCode);
-              }}
-            >
-              {includeHighlightedCode
-                ? "Including highlighted code"
-                : "Automatically finding relevant code"}
-            </BottomButton>
-          </div>
-        </div>
-        <TextEntryBar
-          type="text"
-          placeholder="Enter your message here"
-          onKeyDown={(e) => {
-            if (e.key === "Enter" && e.currentTarget.value !== "") {
-              console.log("Sending message", e.currentTarget.value);
-              dispatch(
-                addMessage({ content: e.currentTarget.value, role: "user" })
-              );
-              (e.target as any).value = "";
-              setWaitingForResponse(true);
-            }
-          }}
-        ></TextEntryBar>
-      </BottomDiv>
-    </ChatContainer>
-  );
-}
-
-export default ChatTab;
diff --git a/extension/react-app/src/tabs/gui.tsx b/extension/react-app/src/tabs/gui.tsx
deleted file mode 100644
index b6a18dc8..00000000
--- a/extension/react-app/src/tabs/gui.tsx
+++ /dev/null
@@ -1,486 +0,0 @@
-import styled from "styled-components";
-import { defaultBorderRadius } from "../components";
-import Loader from "../components/Loader";
-import ContinueButton from "../components/ContinueButton";
-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 { GUIClientContext } from "../App";
-import {
-  BookOpen,
-  ChatBubbleOvalLeftEllipsis,
-  Trash,
-} from "@styled-icons/heroicons-outline";
-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";
-import { postVscMessage } from "../vscode";
-import UserInputContainer from "../components/UserInputContainer";
-import Onboarding from "../components/Onboarding";
-
-const TopGUIDiv = styled.div`
-  overflow: hidden;
-`;
-
-const UserInputQueueItem = styled.div`
-  border-radius: ${defaultBorderRadius};
-  color: gray;
-  padding: 8px;
-  margin: 8px;
-  text-align: center;
-`;
-
-const Footer = styled.footer<{ dataSwitchChecked: boolean }>`
-  display: flex;
-  flex-direction: row;
-  gap: 8px;
-  justify-content: right;
-  padding: 8px;
-  align-items: center;
-  margin-top: 8px;
-  border-top: 0.1px solid gray;
-  background-color: ${(props) =>
-    props.dataSwitchChecked ? "#12887a33" : "transparent"};
-`;
-
-interface GUIProps {
-  firstObservation?: any;
-}
-
-function GUI(props: GUIProps) {
-  const client = useContext(GUIClientContext);
-  const posthog = usePostHog();
-  const vscMachineId = useSelector(
-    (state: RootStore) => state.config.vscMachineId
-  );
-  const [dataSwitchChecked, setDataSwitchChecked] = useState(false);
-  const dataSwitchOn = useSelector(
-    (state: RootStore) => state.config.dataSwitchOn
-  );
-
-  useEffect(() => {
-    if (typeof dataSwitchOn !== "undefined") {
-      setDataSwitchChecked(dataSwitchOn);
-    }
-  }, [dataSwitchOn]);
-
-  const [usingFastModel, setUsingFastModel] = useState(false);
-  const [waitingForSteps, setWaitingForSteps] = useState(false);
-  const [userInputQueue, setUserInputQueue] = useState<string[]>([]);
-  const [highlightedRanges, setHighlightedRanges] = useState<
-    HighlightedRangeContext[]
-  >([]);
-  const [addingHighlightedCode, setAddingHighlightedCode] = useState(false);
-  const [availableSlashCommands, setAvailableSlashCommands] = useState<
-    { name: string; description: string }[]
-  >([]);
-  const [pinned, setPinned] = useState(false);
-  const [showDataSharingInfo, setShowDataSharingInfo] = useState(false);
-  const [stepsOpen, setStepsOpen] = useState<boolean[]>([
-    true,
-    true,
-    true,
-    true,
-  ]);
-  const [history, setHistory] = useState<History | undefined>({
-    timeline: [
-      {
-        step: {
-          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`,
-          system_message: null,
-          chat_context: [],
-          manage_own_chat_context: false,
-          message: "",
-        },
-        depth: 0,
-        deleted: false,
-        active: false,
-      },
-    ],
-    current_index: 3,
-  } as any);
-
-  const [showFeedbackDialog, setShowFeedbackDialog] = useState(false);
-  const [feedbackDialogMessage, setFeedbackDialogMessage] = useState("");
-
-  const topGuiDivRef = useRef<HTMLDivElement>(null);
-
-  const [scrollTimeout, setScrollTimeout] = useState<NodeJS.Timeout | null>(
-    null
-  );
-  const scrollToBottom = useCallback(() => {
-    if (scrollTimeout) {
-      clearTimeout(scrollTimeout);
-    }
-    // Debounced smooth scroll to bottom of screen
-    if (topGuiDivRef.current) {
-      const timeout = setTimeout(() => {
-        window.scrollTo({
-          top: topGuiDivRef.current!.offsetHeight,
-          behavior: "smooth",
-        });
-      }, 200);
-      setScrollTimeout(timeout);
-    }
-  }, [topGuiDivRef.current, scrollTimeout]);
-
-  useEffect(() => {
-    const listener = (e: any) => {
-      // Cmd + i to toggle fast model
-      if (e.key === "i" && e.metaKey && e.shiftKey) {
-        setUsingFastModel((prev) => !prev);
-        // Cmd + backspace to stop currently running step
-      } else if (
-        e.key === "Backspace" &&
-        e.metaKey &&
-        typeof history?.current_index !== "undefined" &&
-        history.timeline[history.current_index]?.active
-      ) {
-        client?.deleteAtIndex(history.current_index);
-      }
-    };
-    window.addEventListener("keydown", listener);
-
-    return () => {
-      window.removeEventListener("keydown", listener);
-    };
-  }, [client, history]);
-
-  useEffect(() => {
-    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 =
-        topGuiDivRef.current &&
-        topGuiDivRef.current?.offsetHeight - window.scrollY < 100;
-
-      const waitingForSteps =
-        state.active &&
-        state.history.current_index < state.history.timeline.length &&
-        state.history.timeline[state.history.current_index] &&
-        state.history.timeline[
-          state.history.current_index
-        ].step.description?.trim() === "";
-
-      setWaitingForSteps(waitingForSteps);
-      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 {
-            name: `/${c.name}`,
-            description: c.description,
-          };
-        })
-      );
-      setStepsOpen((prev) => {
-        const nextStepsOpen = [...prev];
-        for (
-          let i = nextStepsOpen.length;
-          i < state.history.timeline.length;
-          i++
-        ) {
-          nextStepsOpen.push(true);
-        }
-        return nextStepsOpen;
-      });
-
-      if (shouldScrollToBottom) {
-        scrollToBottom();
-      }
-    });
-  }, [client]);
-
-  useEffect(() => {
-    scrollToBottom();
-  }, [waitingForSteps]);
-
-  const mainTextInputRef = useRef<HTMLInputElement>(null);
-
-  const deleteContextItems = useCallback(
-    (indices: number[]) => {
-      client?.deleteContextAtIndices(indices);
-    },
-    [client]
-  );
-
-  const onMainTextInput = (event?: any) => {
-    if (mainTextInputRef.current) {
-      let input = (mainTextInputRef.current as any).inputValue;
-      // cmd+enter to /edit
-      if (event?.metaKey) {
-        input = `/edit ${input}`;
-      }
-      (mainTextInputRef.current as any).setInputValue("");
-      if (!client) return;
-
-      setWaitingForSteps(true);
-
-      if (
-        history &&
-        history.current_index >= 0 &&
-        history.current_index < history.timeline.length
-      ) {
-        if (
-          history.timeline[history.current_index]?.step.name ===
-          "Waiting for user input"
-        ) {
-          if (input.trim() === "") return;
-          onStepUserInput(input, history!.current_index);
-          return;
-        } else if (
-          history.timeline[history.current_index]?.step.name ===
-          "Waiting for user confirmation"
-        ) {
-          onStepUserInput("ok", history!.current_index);
-          return;
-        }
-      }
-      if (input.trim() === "") return;
-
-      client.sendMainInput(input);
-      setUserInputQueue((queue) => {
-        return [...queue, input];
-      });
-    }
-  };
-
-  const onStepUserInput = (input: string, index: number) => {
-    if (!client) return;
-    console.log("Sending step user input", input, index);
-    client.sendStepUserInput(input, index);
-  };
-
-  // const iterations = useSelector(selectIterations);
-  return (
-    <>
-      <Onboarding></Onboarding>
-      <LoadingCover hidden={true} message="Downloading local model..." />
-      <TextDialog
-        showDialog={showFeedbackDialog}
-        onEnter={(text) => {
-          client?.sendMainInput(`/feedback ${text}`);
-          setShowFeedbackDialog(false);
-        }}
-        onClose={() => {
-          setShowFeedbackDialog(false);
-        }}
-        message={feedbackDialogMessage}
-      ></TextDialog>
-
-      <TopGUIDiv
-        ref={topGuiDivRef}
-        onKeyDown={(e) => {
-          if (e.key === "Enter" && e.ctrlKey) {
-            onMainTextInput();
-          }
-        }}
-      >
-        {typeof client === "undefined" && (
-          <>
-            <Loader />
-            <p style={{ textAlign: "center" }}>Loading Continue server...</p>
-          </>
-        )}
-        {history?.timeline.map((node: HistoryNode, index: number) => {
-          return node.step.name === "User Input" ? (
-            node.step.hide || (
-              <UserInputContainer
-                onDelete={() => {
-                  client?.deleteAtIndex(index);
-                }}
-                historyNode={node}
-              >
-                {node.step.description as string}
-              </UserInputContainer>
-            )
-          ) : (
-            <StepContainer
-              isLast={index === history.timeline.length - 1}
-              isFirst={index === 0}
-              open={stepsOpen[index]}
-              onToggle={() => {
-                const nextStepsOpen = [...stepsOpen];
-                nextStepsOpen[index] = !nextStepsOpen[index];
-                setStepsOpen(nextStepsOpen);
-              }}
-              onToggleAll={() => {
-                const shouldOpen = !stepsOpen[index];
-                setStepsOpen((prev) => prev.map(() => shouldOpen));
-              }}
-              key={index}
-              onUserInput={(input: string) => {
-                onStepUserInput(input, index);
-              }}
-              inFuture={index > history?.current_index}
-              historyNode={node}
-              onReverse={() => {
-                client?.reverseToIndex(index);
-              }}
-              onRetry={() => {
-                client?.retryAtIndex(index);
-                setWaitingForSteps(true);
-              }}
-              onDelete={() => {
-                client?.deleteAtIndex(index);
-              }}
-            />
-          );
-        })}
-        {waitingForSteps && <Loader></Loader>}
-
-        <div>
-          {userInputQueue.map((input) => {
-            return <UserInputQueueItem>{input}</UserInputQueueItem>;
-          })}
-        </div>
-
-        <ComboBox
-          ref={mainTextInputRef}
-          onEnter={(e) => {
-            onMainTextInput(e);
-            e.stopPropagation();
-            e.preventDefault();
-          }}
-          onInputValueChange={() => {}}
-          items={availableSlashCommands}
-          highlightedCodeSections={highlightedRanges}
-          deleteContextItems={deleteContextItems}
-          onTogglePin={() => {
-            setPinned((prev: boolean) => !prev);
-          }}
-          onToggleAddContext={() => {
-            client?.toggleAddingHighlightedCode();
-          }}
-          addingHighlightedCode={addingHighlightedCode}
-        />
-        <ContinueButton onClick={onMainTextInput} />
-      </TopGUIDiv>
-      <div
-        style={{
-          position: "fixed",
-          bottom: "50px",
-          backgroundColor: "white",
-          color: "black",
-          borderRadius: defaultBorderRadius,
-          padding: "16px",
-          margin: "16px",
-          zIndex: 100,
-        }}
-        hidden={!showDataSharingInfo}
-      >
-        By turning on this switch, you will begin collecting accepted and
-        rejected suggestions in .continue/suggestions.json. This data is stored
-        locally on your machine and not sent anywhere.
-        <br />
-        <br />
-        <b>
-          {dataSwitchChecked
-            ? "👍 Data is being collected"
-            : "👎 No data is being collected"}
-        </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,
-              });
-              postVscMessage("toggleDataSwitch", { on: !dataSwitchChecked });
-              setDataSwitchChecked((prev) => !prev);
-            }}
-            onColor="#12887a"
-            checked={dataSwitchChecked}
-          />
-          <span style={{ cursor: "help", fontSize: "14px" }}>Collect Data</span>
-        </div>
-        <HeaderButtonWithText
-          onClick={() => {
-            // client?.changeDefaultModel(
-            //   usingFastModel ? "gpt-4" : "gpt-3.5-turbo"
-            // );
-            if (!usingFastModel) {
-              // Show the dialog
-              setFeedbackDialogMessage(
-                "We don't yet support local models, but we're working on it! If privacy is a concern of yours, please write a short note to let us know."
-              );
-              setShowFeedbackDialog(true);
-            }
-            setUsingFastModel((prev) => !prev);
-          }}
-          text={usingFastModel ? "local" : "gpt-4"}
-        >
-          <div
-            style={{ fontSize: "18px", marginLeft: "2px", marginRight: "2px" }}
-          >
-            {usingFastModel ? "🔒" : "🧠"}
-          </div>
-        </HeaderButtonWithText>
-        <HeaderButtonWithText
-          onClick={() => {
-            client?.sendClear();
-          }}
-          text="Clear"
-        >
-          <Trash size="1.6em" />
-        </HeaderButtonWithText>
-        <a
-          href="https://continue.dev/docs/how-to-use-continue"
-          className="no-underline"
-        >
-          <HeaderButtonWithText text="Docs">
-            <BookOpen size="1.6em" />
-          </HeaderButtonWithText>
-        </a>
-        <HeaderButtonWithText
-          onClick={() => {
-            // Set dialog open
-            setFeedbackDialogMessage(
-              "Having trouble using Continue? Want a new feature? Let us know! This box is anonymous, but we will promptly address your feedback."
-            );
-            setShowFeedbackDialog(true);
-          }}
-          text="Feedback"
-        >
-          <ChatBubbleOvalLeftEllipsis size="1.6em" />
-        </HeaderButtonWithText>
-      </Footer>
-    </>
-  );
-}
-
-export default GUI;
diff --git a/extension/react-app/src/tabs/main.tsx b/extension/react-app/src/tabs/main.tsx
deleted file mode 100644
index a8b3300d..00000000
--- a/extension/react-app/src/tabs/main.tsx
+++ /dev/null
@@ -1,189 +0,0 @@
-import React, { useEffect, useState } from "react";
-import { H3, TextArea, Button, Pre, Loader } from "../components";
-import styled from "styled-components";
-import { postVscMessage, withProgress } from "../vscode";
-import { useDebugContextValue } from "../redux/hooks";
-import CodeMultiselect from "../components/CodeMultiselect";
-import { useSelector } from "react-redux";
-import { selectDebugContext } from "../redux/selectors/debugContextSelectors";
-import { useDispatch } from "react-redux";
-import { updateValue } from "../redux/slices/debugContexSlice";
-import { setWorkspacePath } from "../redux/slices/configSlice";
-import { SerializedDebugContext } from "../../../src/client";
-import { useEditCache } from "../util/editCache";
-import { useApi } from "../util/api";
-
-const ButtonDiv = styled.div`
-  display: flex;
-  justify-content: space-between;
-  align-items: center;
-  gap: 4px;
-  margin: 4px;
-  flex-wrap: wrap;
-
-  & button {
-    flex-grow: 1;
-  }
-`;
-
-function MainTab(props: any) {
-  const dispatch = useDispatch();
-
-  const [suggestion, setSuggestion] = useState("");
-  const [traceback, setTraceback] = useDebugContextValue("traceback", "");
-  const [selectedRanges, setSelectedRanges] = useDebugContextValue(
-    "rangesInFiles",
-    []
-  );
-
-  const editCache = useEditCache();
-  const { debugApi } = useApi();
-
-  const [responseLoading, setResponseLoading] = useState(false);
-
-  let debugContext = useSelector(selectDebugContext);
-
-  useEffect(() => {
-    editCache.preloadEdit(debugContext);
-  }, [debugContext]);
-
-  function postVscMessageWithDebugContext(
-    type: string,
-    overrideDebugContext: SerializedDebugContext | null = null
-  ) {
-    postVscMessage(type, {
-      debugContext: overrideDebugContext || debugContext,
-    });
-  }
-
-  function launchFindSuspiciousCode(newTraceback: string) {
-    // setTraceback's effects don't occur immediately, so we have to add it to the debug context manually
-    let updatedDebugContext = {
-      ...debugContext,
-      traceback: newTraceback,
-    };
-    postVscMessageWithDebugContext("findSuspiciousCode", updatedDebugContext);
-    postVscMessageWithDebugContext("preloadEdit", updatedDebugContext);
-  }
-
-  useEffect(() => {
-    const eventListener = (event: any) => {
-      switch (event.data.type) {
-        case "suggestFix":
-        case "explainCode":
-        case "listTenThings":
-          setSuggestion(event.data.value);
-          setResponseLoading(false);
-          break;
-        case "traceback":
-          setTraceback(event.data.value);
-          launchFindSuspiciousCode(event.data.value);
-          break;
-        case "workspacePath":
-          dispatch(setWorkspacePath(event.data.value));
-          break;
-      }
-    };
-    window.addEventListener("message", eventListener);
-
-    return () => window.removeEventListener("message", eventListener);
-  }, [debugContext, selectedRanges]);
-
-  return (
-    <div className="mx-5">
-      <h1>Debug Panel</h1>
-
-      <H3>Code Sections</H3>
-      <CodeMultiselect></CodeMultiselect>
-
-      <H3>Bug Description</H3>
-      <TextArea
-        id="bugDescription"
-        name="bugDescription"
-        className="bugDescription"
-        rows={4}
-        cols={50}
-        placeholder="Describe your bug..."
-      ></TextArea>
-
-      <H3>Stack Trace</H3>
-      <TextArea
-        id="traceback"
-        className="traceback"
-        name="traceback"
-        rows={4}
-        cols={50}
-        placeholder="Paste stack trace here"
-        onChange={(e) => {
-          setTraceback(e.target.value);
-          dispatch(updateValue({ key: "traceback", value: e.target.value }));
-          // postVscMessageWithDebugContext("findSuspiciousCode");
-        }}
-        onPaste={(e) => {
-          let pasted = e.clipboardData.getData("text");
-          console.log("PASTED", pasted);
-          setTraceback(pasted);
-          launchFindSuspiciousCode(pasted);
-        }}
-        value={traceback}
-      ></TextArea>
-
-      <select
-        hidden
-        id="relevantVars"
-        className="relevantVars"
-        name="relevantVars"
-      ></select>
-
-      <ButtonDiv>
-        <Button
-          onClick={() => {
-            postVscMessageWithDebugContext("explainCode");
-            setResponseLoading(true);
-          }}
-        >
-          Explain Code
-        </Button>
-        <Button
-          onClick={() => {
-            postVscMessageWithDebugContext("suggestFix");
-            setResponseLoading(true);
-          }}
-        >
-          Generate Ideas
-        </Button>
-        <Button
-          disabled={selectedRanges.length === 0}
-          onClick={async () => {
-            withProgress("Generating Fix", async () => {
-              let edits = await editCache.getEdit(debugContext);
-              postVscMessage("makeEdit", { edits });
-            });
-          }}
-        >
-          Suggest Fix
-        </Button>
-        <Button
-          disabled={selectedRanges.length === 0}
-          onClick={() => {
-            postVscMessageWithDebugContext("generateUnitTest");
-          }}
-        >
-          Create Test
-        </Button>
-      </ButtonDiv>
-      <Loader hidden={!responseLoading}></Loader>
-
-      <Pre
-        className="fixSuggestion"
-        hidden={!(typeof suggestion === "string" && suggestion.length > 0)}
-      >
-        {suggestion}
-      </Pre>
-
-      <br></br>
-    </div>
-  );
-}
-
-export default MainTab;
diff --git a/extension/react-app/src/tabs/welcome.tsx b/extension/react-app/src/tabs/welcome.tsx
deleted file mode 100644
index c29d260a..00000000
--- a/extension/react-app/src/tabs/welcome.tsx
+++ /dev/null
@@ -1,22 +0,0 @@
-import React from "react";
-
-function WelcomeTab() {
-  return (
-    <div className="mx-5">
-      <h1>Welcome to Continue</h1>
-
-      <p>
-        Learn more in the{" "}
-        <a href="https://www.notion.so/continue-dev/Continue-User-Guide-1c6ad99887d0474d9e42206f6c98efa4">
-          Continue User Guide
-        </a>{" "}
-      </p>
-      <p>Send Nate or Ty your feedback:</p>
-      <p>1. What excites you about Continue?</p>
-      <p>2. What did you struggle with when using Continue?</p>
-      <p>3. How do you wish Continue worked?</p>
-    </div>
-  );
-}
-
-export default WelcomeTab;
diff --git a/extension/src/bridge.ts b/extension/src/bridge.ts
index 92ba4044..55c4cc3b 100644
--- a/extension/src/bridge.ts
+++ b/extension/src/bridge.ts
@@ -4,14 +4,11 @@ import * as vscode from "vscode";
 import {
   Configuration,
   DebugApi,
-  RangeInFile,
-  SerializedDebugContext,
   UnittestApi,
 } from "./client";
 import { convertSingleToDoubleQuoteJSON } from "./util/util";
 import { getExtensionUri } from "./util/vscode";
 import { extensionContext } from "./activation/activate";
-const axios = require("axios").default;
 const util = require("util");
 const exec = util.promisify(require("child_process").exec);
 
@@ -36,21 +33,16 @@ const configuration = new Configuration({
 export const debugApi = new DebugApi(configuration);
 export const unittestApi = new UnittestApi(configuration);
 
-function get_python_path() {
-  return path.join(getExtensionUri().fsPath, "..");
-}
-
 export function get_api_url() {
-  let extensionUri = getExtensionUri();
-  let configFile = path.join(extensionUri.fsPath, "config/config.json");
-  let config = require(configFile);
+  const extensionUri = getExtensionUri();
+  const configFile = path.join(extensionUri.fsPath, "config/config.json");
+  const config = require(configFile);
 
   if (config.API_URL) {
     return config.API_URL;
   }
   return "http://localhost:65432";
 }
-const API_URL = get_api_url();
 
 export function getContinueServerUrl() {
   // If in debug mode, always use 8001
@@ -58,7 +50,7 @@ export function getContinueServerUrl() {
     extensionContext &&
     extensionContext.extensionMode === vscode.ExtensionMode.Development
   ) {
-    return "http://localhost:8001";
+    // return "http://localhost:8001";
   }
   return (
     vscode.workspace.getConfiguration("continue").get<string>("serverUrl") ||
@@ -66,10 +58,6 @@ export function getContinueServerUrl() {
   );
 }
 
-function build_python_command(cmd: string): string {
-  return `cd ${get_python_path()} && source env/bin/activate && ${cmd}`;
-}
-
 function listToCmdLineArgs(list: string[]): string {
   return list.map((el) => `"$(echo "${el}")"`).join(" ");
 }
@@ -103,198 +91,3 @@ export async function runPythonScript(
     }
   }
 }
-
-function parseStdout(
-  stdout: string,
-  key: string,
-  until_end: boolean = false
-): string {
-  const prompt = `${key}=`;
-  let lines = stdout.split("\n");
-
-  let value: string = "";
-  for (let i = 0; i < lines.length; i++) {
-    if (lines[i].startsWith(prompt)) {
-      if (until_end) {
-        return lines.slice(i).join("\n").substring(prompt.length);
-      } else {
-        return lines[i].substring(prompt.length);
-      }
-    }
-  }
-  return "";
-}
-
-export async function askQuestion(
-  question: string,
-  workspacePath: string
-): Promise<{ answer: string; range: vscode.Range; filename: string }> {
-  const command = build_python_command(
-    `python3 ${path.join(
-      get_python_path(),
-      "ask.py"
-    )} ask ${workspacePath} "${question}"`
-  );
-
-  const { stdout, stderr } = await exec(command);
-  if (stderr) {
-    throw new Error(stderr);
-  }
-  // Use the output
-  const answer = parseStdout(stdout, "Answer");
-  const filename = parseStdout(stdout, "Filename");
-  const startLineno = parseInt(parseStdout(stdout, "Start lineno"));
-  const endLineno = parseInt(parseStdout(stdout, "End lineno"));
-  const range = new vscode.Range(
-    new vscode.Position(startLineno, 0),
-    new vscode.Position(endLineno, 0)
-  );
-  if (answer && filename && startLineno && endLineno) {
-    return { answer, filename, range };
-  } else {
-    throw new Error("Error: No answer found");
-  }
-}
-
-export async function apiRequest(
-  endpoint: string,
-  options: {
-    method?: string;
-    query?: { [key: string]: any };
-    body?: { [key: string]: any };
-  }
-): Promise<any> {
-  let defaults = {
-    method: "GET",
-    query: {},
-    body: {},
-  };
-  options = Object.assign(defaults, options); // Second takes over first
-  if (endpoint.startsWith("/")) endpoint = endpoint.substring(1);
-  console.log("API request: ", options.body);
-
-  let resp;
-  try {
-    resp = await axios({
-      method: options.method,
-      url: `${API_URL}/${endpoint}`,
-      data: options.body,
-      params: options.query,
-      headers: {
-        "x-vsc-machine-id": vscode.env.machineId,
-      },
-    });
-  } catch (err) {
-    console.log("Error: ", err);
-    throw err;
-  }
-
-  return resp.data;
-}
-
-// Write a docstring for the most specific function or class at the current line in the given file
-export async function writeDocstringForFunction(
-  filename: string,
-  position: vscode.Position
-): Promise<{ lineno: number; docstring: string }> {
-  let resp = await apiRequest("docstring/forline", {
-    query: {
-      filecontents: (
-        await vscode.workspace.fs.readFile(vscode.Uri.file(filename))
-      ).toString(),
-      lineno: position.line.toString(),
-    },
-  });
-
-  const lineno = resp.lineno;
-  const docstring = resp.completion;
-  if (lineno && docstring) {
-    return { lineno, docstring };
-  } else {
-    throw new Error("Error: No docstring returned");
-  }
-}
-
-export async function findSuspiciousCode(
-  ctx: SerializedDebugContext
-): Promise<RangeInFile[]> {
-  if (!ctx.traceback) return [];
-  let files = await getFileContents(
-    getFilenamesFromPythonStacktrace(ctx.traceback)
-  );
-  let resp = await debugApi.findSusCodeEndpointDebugFindPost({
-    findBody: {
-      traceback: ctx.traceback,
-      description: ctx.description,
-      filesystem: files,
-    },
-  });
-  let ranges = resp.response;
-  if (
-    ranges.length <= 1 &&
-    ctx.traceback &&
-    ctx.traceback.includes("AssertionError")
-  ) {
-    let parsed_traceback =
-      await debugApi.parseTracebackEndpointDebugParseTracebackGet({
-        traceback: ctx.traceback,
-      });
-    let last_frame = parsed_traceback.frames[0];
-    if (!last_frame) return [];
-    ranges = (
-      await runPythonScript("build_call_graph.py", [
-        last_frame.filepath,
-        last_frame.lineno.toString(),
-        last_frame._function,
-      ])
-    ).value;
-  }
-
-  return ranges;
-}
-
-export async function writeUnitTestForFunction(
-  filename: string,
-  position: vscode.Position
-): Promise<string> {
-  let resp = await apiRequest("unittest/forline", {
-    method: "POST",
-    body: {
-      filecontents: (
-        await vscode.workspace.fs.readFile(vscode.Uri.file(filename))
-      ).toString(),
-      lineno: position.line,
-      userid: vscode.env.machineId,
-    },
-  });
-
-  return resp.completion;
-}
-
-async function getFileContents(
-  files: string[]
-): Promise<{ [key: string]: string }> {
-  let contents = await Promise.all(
-    files.map(async (file: string) => {
-      return (
-        await vscode.workspace.fs.readFile(vscode.Uri.file(file))
-      ).toString();
-    })
-  );
-  let fileContents: { [key: string]: string } = {};
-  for (let i = 0; i < files.length; i++) {
-    fileContents[files[i]] = contents[i];
-  }
-  return fileContents;
-}
-
-function getFilenamesFromPythonStacktrace(traceback: string): string[] {
-  let filenames: string[] = [];
-  for (let line of traceback.split("\n")) {
-    let match = line.match(/File "(.*)", line/);
-    if (match) {
-      filenames.push(match[1]);
-    }
-  }
-  return filenames;
-}
-- 
cgit v1.2.3-70-g09d2

