diff options
Diffstat (limited to 'extension/react-app/src')
| -rw-r--r-- | extension/react-app/src/App.tsx | 50 | ||||
| -rw-r--r-- | extension/react-app/src/components/DebugPanel.tsx | 99 | ||||
| -rw-r--r-- | extension/react-app/src/components/Layout.tsx | 28 | ||||
| -rw-r--r-- | extension/react-app/src/hooks/AbstractContinueGUIClientProtocol.ts | 2 | ||||
| -rw-r--r-- | extension/react-app/src/hooks/ContinueGUIClientProtocol.ts | 63 | ||||
| -rw-r--r-- | extension/react-app/src/index.css | 4 | ||||
| -rw-r--r-- | extension/react-app/src/main.tsx | 10 | ||||
| -rw-r--r-- | extension/react-app/src/pages/gui.tsx | 14 | ||||
| -rw-r--r-- | extension/react-app/src/pages/history.tsx | 55 | 
9 files changed, 201 insertions, 124 deletions
| diff --git a/extension/react-app/src/App.tsx b/extension/react-app/src/App.tsx index aa462171..15b536db 100644 --- a/extension/react-app/src/App.tsx +++ b/extension/react-app/src/App.tsx @@ -1,8 +1,26 @@ -import DebugPanel from "./components/DebugPanel";  import GUI from "./pages/gui"; -import { createContext } from "react"; +import History from "./pages/history"; +import Layout from "./components/Layout"; +import { createContext, useEffect } from "react";  import useContinueGUIProtocol from "./hooks/useWebsocket";  import ContinueGUIClientProtocol from "./hooks/ContinueGUIClientProtocol"; +import { +  BrowserRouter as Router, +  Route, +  Routes, +  BrowserRouter, +} from "react-router-dom"; +import { useDispatch } from "react-redux"; +import { +  setApiUrl, +  setVscMachineId, +  setSessionId, +  setVscMediaUrl, +  setDataSwitchOn, +} from "./redux/slices/configSlice"; +import { updateFileSystem } from "./redux/slices/debugContexSlice"; +import { setHighlightedCode } from "./redux/slices/miscSlice"; +import { postVscMessage } from "./vscode";  export const GUIClientContext = createContext<    ContinueGUIClientProtocol | undefined @@ -11,9 +29,35 @@ export const GUIClientContext = createContext<  function App() {    const client = useContinueGUIProtocol(); +  const dispatch = useDispatch(); +  useEffect(() => { +    const eventListener = (event: any) => { +      switch (event.data.type) { +        case "onLoad": +          dispatch(setApiUrl(event.data.apiUrl)); +          dispatch(setVscMachineId(event.data.vscMachineId)); +          dispatch(setSessionId(event.data.sessionId)); +          dispatch(setVscMediaUrl(event.data.vscMediaUrl)); +          dispatch(setDataSwitchOn(event.data.dataSwitchOn)); +          break; +        case "highlightedCode": +          dispatch(setHighlightedCode(event.data.rangeInFile)); +          dispatch(updateFileSystem(event.data.filesystem)); +          break; +      } +    }; +    window.addEventListener("message", eventListener); +    postVscMessage("onLoad", {}); +    return () => window.removeEventListener("message", eventListener); +  }, []); +    return (      <GUIClientContext.Provider value={client}> -      <DebugPanel tabs={[{ element: <GUI />, title: "GUI" }]} /> +      <Routes> +        <Route path="/" element={<Layout />} /> +        <Route path="/gui" element={<GUI />} /> +        <Route path="/history" element={<History />} /> +      </Routes>      </GUIClientContext.Provider>    );  } diff --git a/extension/react-app/src/components/DebugPanel.tsx b/extension/react-app/src/components/DebugPanel.tsx deleted file mode 100644 index fffb6c6e..00000000 --- a/extension/react-app/src/components/DebugPanel.tsx +++ /dev/null @@ -1,99 +0,0 @@ -import React, { useEffect, useState } from "react"; -import styled from "styled-components"; -import { postVscMessage } from "../vscode"; -import { useDispatch } from "react-redux"; -import { -  setApiUrl, -  setVscMachineId, -  setSessionId, -  setVscMediaUrl, -  setDataSwitchOn, -} from "../redux/slices/configSlice"; -import { setHighlightedCode } from "../redux/slices/miscSlice"; -import { updateFileSystem } from "../redux/slices/debugContexSlice"; -import { defaultBorderRadius, secondaryDark, vscBackground } from "."; -interface DebugPanelProps { -  tabs: { -    element: React.ReactElement; -    title: string; -  }[]; -} - -const TabBar = styled.div<{ numTabs: number }>` -  display: grid; -  grid-template-columns: repeat(${(props) => props.numTabs}, 1fr); -`; - -const TabsAndBodyDiv = styled.div` -  height: 100%; -  border-radius: ${defaultBorderRadius}; -  scrollbar-base-color: transparent; -`; - -function DebugPanel(props: DebugPanelProps) { -  const dispatch = useDispatch(); -  useEffect(() => { -    const eventListener = (event: any) => { -      switch (event.data.type) { -        case "onLoad": -          dispatch(setApiUrl(event.data.apiUrl)); -          dispatch(setVscMachineId(event.data.vscMachineId)); -          dispatch(setSessionId(event.data.sessionId)); -          dispatch(setVscMediaUrl(event.data.vscMediaUrl)); -          dispatch(setDataSwitchOn(event.data.dataSwitchOn)); -          break; -        case "highlightedCode": -          dispatch(setHighlightedCode(event.data.rangeInFile)); -          dispatch(updateFileSystem(event.data.filesystem)); -          break; -      } -    }; -    window.addEventListener("message", eventListener); -    postVscMessage("onLoad", {}); -    return () => window.removeEventListener("message", eventListener); -  }, []); - -  const [currentTab, setCurrentTab] = useState(0); - -  return ( -    <TabsAndBodyDiv> -      {props.tabs.length > 1 && ( -        <TabBar numTabs={props.tabs.length}> -          {props.tabs.map((tab, index) => { -            return ( -              <div -                key={index} -                className={`p-2 cursor-pointer text-center ${ -                  index === currentTab -                    ? "bg-secondary-dark" -                    : "bg-vsc-background" -                }`} -                onClick={() => setCurrentTab(index)} -              > -                {tab.title} -              </div> -            ); -          })} -        </TabBar> -      )} -      {props.tabs.map((tab, index) => { -        return ( -          <div -            key={index} -            hidden={index !== currentTab} -            style={{ -              scrollbarGutter: "stable both-edges", -              minHeight: "100%", -              display: "grid", -              gridTemplateRows: "1fr auto", -            }} -          > -            {tab.element} -          </div> -        ); -      })} -    </TabsAndBodyDiv> -  ); -} - -export default DebugPanel; diff --git a/extension/react-app/src/components/Layout.tsx b/extension/react-app/src/components/Layout.tsx new file mode 100644 index 00000000..d1688299 --- /dev/null +++ b/extension/react-app/src/components/Layout.tsx @@ -0,0 +1,28 @@ +import styled from "styled-components"; +import { defaultBorderRadius } from "."; +import { Outlet } from "react-router-dom"; + +const LayoutTopDiv = styled.div` +  height: 100%; +  border-radius: ${defaultBorderRadius}; +  scrollbar-base-color: transparent; +  scrollbar-width: thin; +`; +const Layout = () => { +  return ( +    <LayoutTopDiv> +      <div +        style={{ +          scrollbarGutter: "stable both-edges", +          minHeight: "100%", +          display: "grid", +          gridTemplateRows: "1fr auto", +        }} +      > +        <Outlet /> +      </div> +    </LayoutTopDiv> +  ); +}; + +export default Layout; diff --git a/extension/react-app/src/hooks/AbstractContinueGUIClientProtocol.ts b/extension/react-app/src/hooks/AbstractContinueGUIClientProtocol.ts index 8d8b7b7e..168fb156 100644 --- a/extension/react-app/src/hooks/AbstractContinueGUIClientProtocol.ts +++ b/extension/react-app/src/hooks/AbstractContinueGUIClientProtocol.ts @@ -30,6 +30,8 @@ abstract class AbstractContinueGUIClientProtocol {    abstract showLogsAtIndex(index: number): void;    abstract selectContextItem(id: string, query: string): void; + +  abstract onReconnectAtSession(session_id: string): void;  }  export default AbstractContinueGUIClientProtocol; diff --git a/extension/react-app/src/hooks/ContinueGUIClientProtocol.ts b/extension/react-app/src/hooks/ContinueGUIClientProtocol.ts index b6dd43d9..830954c5 100644 --- a/extension/react-app/src/hooks/ContinueGUIClientProtocol.ts +++ b/extension/react-app/src/hooks/ContinueGUIClientProtocol.ts @@ -4,15 +4,18 @@ import { Messenger, WebsocketMessenger } from "./messenger";  import { VscodeMessenger } from "./vscodeMessenger";  class ContinueGUIClientProtocol extends AbstractContinueGUIClientProtocol { -  messenger: Messenger; +  messenger?: Messenger;    // Server URL must contain the session ID param    serverUrlWithSessionId: string; +  useVscodeMessagePassing: boolean; -  constructor( +  private connectMessenger(      serverUrlWithSessionId: string,      useVscodeMessagePassing: boolean    ) { -    super(); +    if (this.messenger) { +      // this.messenger.close(); TODO +    }      this.serverUrlWithSessionId = serverUrlWithSessionId;      this.messenger = useVscodeMessagePassing        ? new VscodeMessenger(serverUrlWithSessionId) @@ -24,26 +27,52 @@ class ContinueGUIClientProtocol extends AbstractContinueGUIClientProtocol {      this.messenger.onError((error) => {        console.log("GUI -> IDE websocket error", error);      }); + +    this.messenger.onMessageType("reconnect_at_session", (data: any) => { +      if (data.session_id) { +        this.onReconnectAtSession(data.session_id); +      } +    }); +  } + +  constructor( +    serverUrlWithSessionId: string, +    useVscodeMessagePassing: boolean +  ) { +    super(); +    this.serverUrlWithSessionId = serverUrlWithSessionId; +    this.useVscodeMessagePassing = useVscodeMessagePassing; +    this.connectMessenger(serverUrlWithSessionId, useVscodeMessagePassing); +  } + +  onReconnectAtSession(session_id: string): void { +    this.connectMessenger( +      this.serverUrlWithSessionId.replace( +        /\/session\/[a-zA-Z0-9-]+/, +        `/session/${session_id}` +      ), +      this.useVscodeMessagePassing +    );    }    sendMainInput(input: string) { -    this.messenger.send("main_input", { input }); +    this.messenger?.send("main_input", { input });    }    reverseToIndex(index: number) { -    this.messenger.send("reverse_to_index", { index }); +    this.messenger?.send("reverse_to_index", { index });    }    sendRefinementInput(input: string, index: number) { -    this.messenger.send("refinement_input", { input, index }); +    this.messenger?.send("refinement_input", { input, index });    }    sendStepUserInput(input: string, index: number) { -    this.messenger.send("step_user_input", { input, index }); +    this.messenger?.send("step_user_input", { input, index });    }    onStateUpdate(callback: (state: any) => void) { -    this.messenger.onMessageType("state_update", (data: any) => { +    this.messenger?.onMessageType("state_update", (data: any) => {        if (data.state) {          callback(data.state);        } @@ -53,7 +82,7 @@ class ContinueGUIClientProtocol extends AbstractContinueGUIClientProtocol {    onAvailableSlashCommands(      callback: (commands: { name: string; description: string }[]) => void    ) { -    this.messenger.onMessageType("available_slash_commands", (data: any) => { +    this.messenger?.onMessageType("available_slash_commands", (data: any) => {        if (data.commands) {          callback(data.commands);        } @@ -61,37 +90,37 @@ class ContinueGUIClientProtocol extends AbstractContinueGUIClientProtocol {    }    sendClear() { -    this.messenger.send("clear_history", {}); +    this.messenger?.send("clear_history", {});    }    retryAtIndex(index: number) { -    this.messenger.send("retry_at_index", { index }); +    this.messenger?.send("retry_at_index", { index });    }    deleteAtIndex(index: number) { -    this.messenger.send("delete_at_index", { index }); +    this.messenger?.send("delete_at_index", { index });    }    deleteContextWithIds(ids: ContextItemId[]) { -    this.messenger.send("delete_context_with_ids", { +    this.messenger?.send("delete_context_with_ids", {        ids: ids.map((id) => `${id.provider_title}-${id.item_id}`),      });    }    setEditingAtIds(ids: string[]) { -    this.messenger.send("set_editing_at_ids", { ids }); +    this.messenger?.send("set_editing_at_ids", { ids });    }    toggleAddingHighlightedCode(): void { -    this.messenger.send("toggle_adding_highlighted_code", {}); +    this.messenger?.send("toggle_adding_highlighted_code", {});    }    showLogsAtIndex(index: number): void { -    this.messenger.send("show_logs_at_index", { index }); +    this.messenger?.send("show_logs_at_index", { index });    }    selectContextItem(id: string, query: string): void { -    this.messenger.send("select_context_item", { id, query }); +    this.messenger?.send("select_context_item", { id, query });    }  } diff --git a/extension/react-app/src/index.css b/extension/react-app/src/index.css index 6a46800e..269da69a 100644 --- a/extension/react-app/src/index.css +++ b/extension/react-app/src/index.css @@ -9,9 +9,9 @@    --button-color-hover: rgba(113, 28, 59, 0.667);    --def-border-radius: 5px; -  /* --vscode-editor-background: rgb(30, 30, 30); +  --vscode-editor-background: rgb(30, 30, 30);    --vscode-editor-foreground: rgb(197, 200, 198); -  --vscode-textBlockQuote-background: rgba(255, 255, 255, 0.05); */ +  --vscode-textBlockQuote-background: rgba(255, 255, 255, 0.05);  }  html, diff --git a/extension/react-app/src/main.tsx b/extension/react-app/src/main.tsx index 1776490c..1b70e786 100644 --- a/extension/react-app/src/main.tsx +++ b/extension/react-app/src/main.tsx @@ -7,6 +7,14 @@ import "./index.css";  import posthog from "posthog-js";  import { PostHogProvider } from "posthog-js/react"; +import { createBrowserRouter, RouterProvider } from "react-router-dom"; + +const router = createBrowserRouter([ +  { +    path: "/", +    element: <App />, +  }, +]);  console.log("Starting React"); @@ -19,7 +27,7 @@ ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(    <React.StrictMode>      <PostHogProvider client={posthog}>        <Provider store={store}> -        <App /> +        <RouterProvider router={router} />        </Provider>      </PostHogProvider>    </React.StrictMode> diff --git a/extension/react-app/src/pages/gui.tsx b/extension/react-app/src/pages/gui.tsx index d69da57e..247789d6 100644 --- a/extension/react-app/src/pages/gui.tsx +++ b/extension/react-app/src/pages/gui.tsx @@ -16,11 +16,12 @@ import {    BookOpenIcon,    ChatBubbleOvalLeftEllipsisIcon,    TrashIcon, +  PlusCircleIcon, +  FolderIcon,  } from "@heroicons/react/24/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 { useDispatch, useSelector } from "react-redux";  import { RootStore } from "../redux/store"; @@ -589,7 +590,16 @@ If you already have an LLM deployed on your own infrastructure, or would like to            }}            text="Clear"          > -          <TrashIcon width="1.4em" height="1.4em" /> +          <PlusCircleIcon width="1.4em" height="1.4em" /> +        </HeaderButtonWithText> +        <HeaderButtonWithText +          onClick={() => { +            // Go to /history page +            document.location.href = "/history"; +          }} +          text="History" +        > +          <FolderIcon width="1.4em" height="1.4em" />          </HeaderButtonWithText>          <a            href="https://continue.dev/docs/how-to-use-continue" diff --git a/extension/react-app/src/pages/history.tsx b/extension/react-app/src/pages/history.tsx new file mode 100644 index 00000000..6539f0f5 --- /dev/null +++ b/extension/react-app/src/pages/history.tsx @@ -0,0 +1,55 @@ +import React, { useContext, useEffect, useState } from "react"; +import { SessionInfo } from "../../../schema/SessionInfo"; +import { GUIClientContext } from "../App"; +import fetch from "node-fetch"; +import { useSelector } from "react-redux"; +import { RootStore } from "../redux/store"; + +function History() { +  const [sessions, setSessions] = useState<SessionInfo[]>([]); +  const client = useContext(GUIClientContext); +  const apiUrl = useSelector((state: RootStore) => state.config.apiUrl); + +  useEffect(() => { +    const fetchSessions = async () => { +      console.log("fetching sessions"); +      if (!apiUrl) { +        return; +      } +      const response = await fetch(`${apiUrl}/sessions/list`); +      const json = await response.json(); +      console.log(json); +      setSessions(json); +    }; +    fetchSessions(); +  }, [client]); + +  return ( +    <div style={{ width: "100%" }}> +      <table style={{ width: "100%" }}> +        <tbody> +          {sessions.map((session, index) => ( +            <tr key={index}> +              <td> +                <div +                  style={{ cursor: "pointer" }} +                  onClick={() => { +                    // client?.loadSession(session.id); +                    // document.location.href = "/gui"; +                  }} +                > +                  <div>{session.title}</div> +                  <div style={{ color: "lightgray" }}> +                    {session.date_created} +                  </div> +                </div> +              </td> +            </tr> +          ))} +        </tbody> +      </table> +    </div> +  ); +} + +export default History; | 
