diff options
Diffstat (limited to 'extension/react-app')
19 files changed, 391 insertions, 144 deletions
diff --git a/extension/react-app/README.md b/extension/react-app/README.md new file mode 100644 index 00000000..006b6b11 --- /dev/null +++ b/extension/react-app/README.md @@ -0,0 +1,3 @@ +# Continue React App + +The Continue React app is a notebook-like interface to the Continue server. It allows the user to submit arbitrary text input, then communicates with the server to takes steps, which are displayed as a sequence of editable cells. The React app should sit beside an IDE, as in the VS Code extension. diff --git a/extension/react-app/package-lock.json b/extension/react-app/package-lock.json index 1ba8cfe8..dbcbc5cc 100644 --- a/extension/react-app/package-lock.json +++ b/extension/react-app/package-lock.json @@ -10,6 +10,7 @@ "dependencies": { "@styled-icons/heroicons-outline": "^10.47.0", "@types/vscode-webview": "^1.57.1", + "posthog-js": "^1.58.0", "react": "^18.2.0", "react-dom": "^18.2.0", "react-markdown": "^8.0.5", @@ -1506,6 +1507,11 @@ "reusify": "^1.0.4" } }, + "node_modules/fflate": { + "version": "0.4.8", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.4.8.tgz", + "integrity": "sha512-FJqqoDBR00Mdj9ppamLa/Y7vxm+PRmNWA67N846RvsoYVMKB4q3y/de5PA7gUmRMYK/8CMz2GDZQmCRN1wBcWA==" + }, "node_modules/fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -2528,6 +2534,15 @@ "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" }, + "node_modules/posthog-js": { + "version": "1.58.0", + "resolved": "https://registry.npmjs.org/posthog-js/-/posthog-js-1.58.0.tgz", + "integrity": "sha512-PpH/MwjwV6UHDsv78xFvteEWYgY3O/HTKBnotzmkNCDWgsKzNr978B1AKzgtBU2GzBsnwUfuK+u2O6mxRzFSWw==", + "dependencies": { + "fflate": "^0.4.1", + "rrweb-snapshot": "^1.1.14" + } + }, "node_modules/prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", @@ -2778,6 +2793,11 @@ "fsevents": "~2.3.2" } }, + "node_modules/rrweb-snapshot": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/rrweb-snapshot/-/rrweb-snapshot-1.1.14.tgz", + "integrity": "sha512-eP5pirNjP5+GewQfcOQY4uBiDnpqxNRc65yKPW0eSoU1XamDfc4M8oqpXGMyUyvLyxFDB0q0+DChuxxiU2FXBQ==" + }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -4196,6 +4216,11 @@ "reusify": "^1.0.4" } }, + "fflate": { + "version": "0.4.8", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.4.8.tgz", + "integrity": "sha512-FJqqoDBR00Mdj9ppamLa/Y7vxm+PRmNWA67N846RvsoYVMKB4q3y/de5PA7gUmRMYK/8CMz2GDZQmCRN1wBcWA==" + }, "fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -4803,6 +4828,15 @@ "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" }, + "posthog-js": { + "version": "1.58.0", + "resolved": "https://registry.npmjs.org/posthog-js/-/posthog-js-1.58.0.tgz", + "integrity": "sha512-PpH/MwjwV6UHDsv78xFvteEWYgY3O/HTKBnotzmkNCDWgsKzNr978B1AKzgtBU2GzBsnwUfuK+u2O6mxRzFSWw==", + "requires": { + "fflate": "^0.4.1", + "rrweb-snapshot": "^1.1.14" + } + }, "prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", @@ -4964,6 +4998,11 @@ "fsevents": "~2.3.2" } }, + "rrweb-snapshot": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/rrweb-snapshot/-/rrweb-snapshot-1.1.14.tgz", + "integrity": "sha512-eP5pirNjP5+GewQfcOQY4uBiDnpqxNRc65yKPW0eSoU1XamDfc4M8oqpXGMyUyvLyxFDB0q0+DChuxxiU2FXBQ==" + }, "run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", diff --git a/extension/react-app/package.json b/extension/react-app/package.json index 3993b030..7d1211de 100644 --- a/extension/react-app/package.json +++ b/extension/react-app/package.json @@ -11,6 +11,7 @@ "dependencies": { "@styled-icons/heroicons-outline": "^10.47.0", "@types/vscode-webview": "^1.57.1", + "posthog-js": "^1.58.0", "react": "^18.2.0", "react-dom": "^18.2.0", "react-markdown": "^8.0.5", diff --git a/extension/react-app/src/App.tsx b/extension/react-app/src/App.tsx index 0c40ced1..a51541d0 100644 --- a/extension/react-app/src/App.tsx +++ b/extension/react-app/src/App.tsx @@ -4,7 +4,7 @@ import { Provider } from "react-redux"; import store from "./redux/store"; import WelcomeTab from "./tabs/welcome"; import ChatTab from "./tabs/chat"; -import Notebook from "./tabs/notebook"; +import GUI from "./tabs/gui"; function App() { return ( @@ -13,8 +13,8 @@ function App() { <DebugPanel tabs={[ { - element: <Notebook />, - title: "Notebook", + element: <GUI />, + title: "GUI", }, // { element: <MainTab />, title: "Debug Panel" }, // { element: <WelcomeTab />, title: "Welcome" }, diff --git a/extension/react-app/src/components/CodeBlock.tsx b/extension/react-app/src/components/CodeBlock.tsx index 4c10ab23..e0336554 100644 --- a/extension/react-app/src/components/CodeBlock.tsx +++ b/extension/react-app/src/components/CodeBlock.tsx @@ -6,7 +6,8 @@ import { defaultBorderRadius, vscBackground } from "."; import { Clipboard } from "@styled-icons/heroicons-outline"; const StyledPre = styled.pre` - overflow: scroll; + overflow-y: scroll; + word-wrap: normal; border: 1px solid gray; border-radius: ${defaultBorderRadius}; background-color: ${vscBackground}; diff --git a/extension/react-app/src/components/DebugPanel.tsx b/extension/react-app/src/components/DebugPanel.tsx index ed00571b..9dacc624 100644 --- a/extension/react-app/src/components/DebugPanel.tsx +++ b/extension/react-app/src/components/DebugPanel.tsx @@ -36,7 +36,8 @@ const GradientContainer = styled.div` const MainDiv = styled.div` height: 100%; border-radius: ${defaultBorderRadius}; - overflow: scroll; + overflow-y: scroll; + scrollbar-gutter: stable both-edges; scrollbar-base-color: transparent; /* background: ${vscBackground}; */ background-color: #1e1e1ede; @@ -107,6 +108,7 @@ function DebugPanel(props: DebugPanelProps) { className={ tab.title === "Chat" ? "overflow-hidden" : "overflow-scroll" } + style={{ scrollbarGutter: "stable both-edges" }} > {tab.element} </div> diff --git a/extension/react-app/src/components/StepContainer.tsx b/extension/react-app/src/components/StepContainer.tsx index 03649b66..5e979b34 100644 --- a/extension/react-app/src/components/StepContainer.tsx +++ b/extension/react-app/src/components/StepContainer.tsx @@ -36,6 +36,8 @@ const MainDiv = styled.div<{ stepDepth: number; inFuture: boolean }>` animation: ${appear} 0.3s ease-in-out; /* padding-left: ${(props) => props.stepDepth * 20}px; */ overflow: hidden; + margin-left: 0px; + margin-right: 0px; `; const StepContainerDiv = styled.div<{ open: boolean }>` @@ -78,6 +80,13 @@ function StepContainer(props: StepContainerProps) { const [open, setOpen] = useState(false); const [isHovered, setIsHovered] = useState(false); const naturalLanguageInputRef = useRef<HTMLTextAreaElement>(null); + const userInputRef = useRef<HTMLInputElement>(null); + + useEffect(() => { + if (userInputRef?.current) { + userInputRef.current.focus(); + } + }, [userInputRef]); useEffect(() => { if (isHovered) { @@ -134,6 +143,7 @@ function StepContainer(props: StepContainerProps) { {props.historyNode.step.name === "Waiting for user input" && ( <input + ref={userInputRef} className="m-auto p-2 rounded-md border-1 border-solid text-white w-3/4 border-gray-200 bg-vsc-background" onKeyDown={(e) => { if (e.key === "Enter") { @@ -144,6 +154,9 @@ function StepContainer(props: StepContainerProps) { onSubmit={(ev) => { props.onUserInput(ev.currentTarget.value); }} + onClick={(e) => { + e.stopPropagation(); + }} /> )} {props.historyNode.step.name === "Waiting for user confirmation" && ( @@ -165,24 +178,6 @@ function StepContainer(props: StepContainerProps) { /> </> )} - - {open && ( - <> - {/* {props.historyNode.observation && ( - <SubContainer title="Error"> - <CodeBlock>Error Here</CodeBlock> - </SubContainer> - )} */} - {/* {props.iterationContext.suggestedChanges.map((sc) => { - return ( - <SubContainer title="Suggested Change"> - {sc.filepath} - <CodeBlock>{sc.replacement}</CodeBlock> - </SubContainer> - ); - })} */} - </> - )} </StepContainerDiv> </GradientBorder> diff --git a/extension/react-app/src/components/index.ts b/extension/react-app/src/components/index.ts index e37c97f3..7ba60467 100644 --- a/extension/react-app/src/components/index.ts +++ b/extension/react-app/src/components/index.ts @@ -45,7 +45,7 @@ export const Pre = styled.pre` border-radius: ${defaultBorderRadius}; padding: 8px; max-height: 150px; - overflow: scroll; + overflow-y: scroll; margin: 0; background-color: ${secondaryDark}; border: none; diff --git a/extension/react-app/src/hooks/ContinueGUIClientProtocol.ts b/extension/react-app/src/hooks/ContinueGUIClientProtocol.ts new file mode 100644 index 00000000..18a91de7 --- /dev/null +++ b/extension/react-app/src/hooks/ContinueGUIClientProtocol.ts @@ -0,0 +1,13 @@ +abstract class AbstractContinueGUIClientProtocol { + abstract sendMainInput(input: string): void; + + abstract reverseToIndex(index: number): void; + + abstract sendRefinementInput(input: string, index: number): void; + + abstract sendStepUserInput(input: string, index: number): void; + + abstract onStateUpdate(state: any): void; +} + +export default AbstractContinueGUIClientProtocol; diff --git a/extension/react-app/src/hooks/messenger.ts b/extension/react-app/src/hooks/messenger.ts new file mode 100644 index 00000000..e2a0bab8 --- /dev/null +++ b/extension/react-app/src/hooks/messenger.ts @@ -0,0 +1,108 @@ +// console.log("Websocket import"); +// const WebSocket = require("ws"); + +export abstract class Messenger { + abstract send(messageType: string, data: object): void; + + abstract onMessageType( + messageType: string, + callback: (data: object) => void + ): void; + + abstract onMessage(callback: (messageType: string, data: any) => void): void; + + abstract onOpen(callback: () => void): void; + + abstract onClose(callback: () => void): void; + + abstract sendAndReceive(messageType: string, data: any): Promise<any>; +} + +export class WebsocketMessenger extends Messenger { + websocket: WebSocket; + private onMessageListeners: { + [messageType: string]: ((data: object) => void)[]; + } = {}; + private onOpenListeners: (() => void)[] = []; + private onCloseListeners: (() => void)[] = []; + private serverUrl: string; + + _newWebsocket(): WebSocket { + // // Dynamic import, because WebSocket is builtin with browser, but not with node. And can't use require in browser. + // if (typeof process === "object") { + // console.log("Using node"); + // // process is only available in Node + // var WebSocket = require("ws"); + // } + + const newWebsocket = new WebSocket(this.serverUrl); + for (const listener of this.onOpenListeners) { + this.onOpen(listener); + } + for (const listener of this.onCloseListeners) { + this.onClose(listener); + } + for (const messageType in this.onMessageListeners) { + for (const listener of this.onMessageListeners[messageType]) { + this.onMessageType(messageType, listener); + } + } + return newWebsocket; + } + + constructor(serverUrl: string) { + super(); + this.serverUrl = serverUrl; + this.websocket = this._newWebsocket(); + } + + send(messageType: string, data: object) { + const payload = JSON.stringify({ messageType, data }); + if (this.websocket.readyState === this.websocket.OPEN) { + this.websocket.send(payload); + } else { + if (this.websocket.readyState !== this.websocket.CONNECTING) { + this.websocket = this._newWebsocket(); + } + this.websocket.addEventListener("open", () => { + this.websocket.send(payload); + }); + } + } + + sendAndReceive(messageType: string, data: any): Promise<any> { + return new Promise((resolve, reject) => { + const eventListener = (data: any) => { + // THIS ISN"T GETTING CALLED + resolve(data); + this.websocket.removeEventListener("message", eventListener); + }; + this.onMessageType(messageType, eventListener); + this.send(messageType, data); + }); + } + + onMessageType(messageType: string, callback: (data: any) => void): void { + this.websocket.addEventListener("message", (event: any) => { + const msg = JSON.parse(event.data); + if (msg.messageType === messageType) { + callback(msg.data); + } + }); + } + + onMessage(callback: (messageType: string, data: any) => void): void { + this.websocket.addEventListener("message", (event) => { + const msg = JSON.parse(event.data); + callback(msg.messageType, msg.data); + }); + } + + onOpen(callback: () => void): void { + this.websocket.addEventListener("open", callback); + } + + onClose(callback: () => void): void { + this.websocket.addEventListener("close", callback); + } +} diff --git a/extension/react-app/src/hooks/useContinueGUIProtocol.ts b/extension/react-app/src/hooks/useContinueGUIProtocol.ts new file mode 100644 index 00000000..a3a1d0c9 --- /dev/null +++ b/extension/react-app/src/hooks/useContinueGUIProtocol.ts @@ -0,0 +1,49 @@ +import AbstractContinueGUIClientProtocol from "./ContinueGUIClientProtocol"; +// import { Messenger, WebsocketMessenger } from "../../../src/util/messenger"; +import { Messenger, WebsocketMessenger } from "./messenger"; +import { VscodeMessenger } from "./vscodeMessenger"; + +class ContinueGUIClientProtocol extends AbstractContinueGUIClientProtocol { + messenger: Messenger; + // Server URL must contain the session ID param + serverUrlWithSessionId: string; + + constructor( + serverUrlWithSessionId: string, + useVscodeMessagePassing: boolean + ) { + super(); + this.serverUrlWithSessionId = serverUrlWithSessionId; + if (useVscodeMessagePassing) { + this.messenger = new VscodeMessenger(serverUrlWithSessionId); + } else { + this.messenger = new WebsocketMessenger(serverUrlWithSessionId); + } + } + + sendMainInput(input: string) { + this.messenger.send("main_input", { input }); + } + + reverseToIndex(index: number) { + this.messenger.send("reverse_to_index", { index }); + } + + sendRefinementInput(input: string, index: number) { + this.messenger.send("refinement_input", { input, index }); + } + + sendStepUserInput(input: string, index: number) { + this.messenger.send("step_user_input", { input, index }); + } + + onStateUpdate(callback: (state: any) => void) { + this.messenger.onMessageType("state_update", (data: any) => { + if (data.state) { + callback(data.state); + } + }); + } +} + +export default ContinueGUIClientProtocol; diff --git a/extension/react-app/src/hooks/useWebsocket.ts b/extension/react-app/src/hooks/useWebsocket.ts index 147172bd..e762666f 100644 --- a/extension/react-app/src/hooks/useWebsocket.ts +++ b/extension/react-app/src/hooks/useWebsocket.ts @@ -1,67 +1,39 @@ import React, { useEffect, useState } from "react"; import { RootStore } from "../redux/store"; import { useSelector } from "react-redux"; +import ContinueGUIClientProtocol from "./useContinueGUIProtocol"; +import { postVscMessage } from "../vscode"; -function useContinueWebsocket( - serverUrl: string, - onMessage: (message: { data: any }) => void -) { +function useContinueGUIProtocol(useVscodeMessagePassing: boolean = true) { const sessionId = useSelector((state: RootStore) => state.config.sessionId); - const [websocket, setWebsocket] = useState<WebSocket | undefined>(undefined); + const serverHttpUrl = useSelector((state: RootStore) => state.config.apiUrl); + const [client, setClient] = useState<ContinueGUIClientProtocol | undefined>( + undefined + ); - async function connect() { - while (!sessionId) { - await new Promise((resolve) => setTimeout(resolve, 300)); + useEffect(() => { + if (!sessionId || !serverHttpUrl) { + if (useVscodeMessagePassing) { + postVscMessage("onLoad", {}); + } + setClient(undefined); + return; } - console.log("Creating websocket", sessionId); - - const wsUrl = - serverUrl.replace("http", "ws") + - "/notebook/ws?session_id=" + + const serverUrlWithSessionId = + serverHttpUrl.replace("http", "ws") + + "/gui/ws?session_id=" + encodeURIComponent(sessionId); - const ws = new WebSocket(wsUrl); - setWebsocket(ws); - - // Set up callbacks - ws.onopen = () => { - console.log("Websocket opened"); - ws.send(JSON.stringify({ sessionId })); - }; - - ws.onmessage = (msg) => { - onMessage(msg); - console.log("Got message", msg); - }; - - ws.onclose = (msg) => { - console.log("Websocket closed"); - setWebsocket(undefined); - }; - - return ws; - } - - async function getConnection() { - if (!websocket) { - return await connect(); - } - return websocket; - } - - async function send(message: object) { - let ws = await getConnection(); - ws.send(JSON.stringify(message)); - } - - useEffect(() => { - if (!sessionId) { - return; - } - connect(); - }, [sessionId]); + console.log("Creating websocket", serverUrlWithSessionId); + console.log("Using vscode message passing", useVscodeMessagePassing); + const newClient = new ContinueGUIClientProtocol( + serverUrlWithSessionId, + useVscodeMessagePassing + ); + setClient(newClient); + }, [sessionId, serverHttpUrl]); - return { send }; + return client; } -export default useContinueWebsocket; +export default useContinueGUIProtocol; diff --git a/extension/react-app/src/hooks/vscodeMessenger.ts b/extension/react-app/src/hooks/vscodeMessenger.ts new file mode 100644 index 00000000..ba19586b --- /dev/null +++ b/extension/react-app/src/hooks/vscodeMessenger.ts @@ -0,0 +1,71 @@ +import { postVscMessage } from "../vscode"; +// import { Messenger } from "../../../src/util/messenger"; +import { Messenger } from "./messenger"; + +export class VscodeMessenger extends Messenger { + serverUrl: string; + + constructor(serverUrl: string) { + super(); + this.serverUrl = serverUrl; + postVscMessage("websocketForwardingOpen", { url: this.serverUrl }); + } + + send(messageType: string, data: object) { + postVscMessage("websocketForwardingMessage", { + message: { messageType, data }, + url: this.serverUrl, + }); + } + + onMessageType(messageType: string, callback: (data: object) => void): void { + window.addEventListener("message", (event: any) => { + if (event.data.type === "websocketForwardingMessage") { + const data = JSON.parse(event.data.data); + if (data.messageType === messageType) { + callback(data.data); + } + } + }); + } + + onMessage(callback: (messageType: string, data: any) => void): void { + window.addEventListener("message", (event: any) => { + if (event.data.type === "websocketForwardingMessage") { + const data = JSON.parse(event.data.data); + callback(data.messageType, data.data); + } + }); + } + + sendAndReceive(messageType: string, data: any): Promise<any> { + return new Promise((resolve) => { + const handler = (event: any) => { + if (event.data.type === "websocketForwardingMessage") { + const data = JSON.parse(event.data.data); + if (data.messageType === messageType) { + window.removeEventListener("message", handler); + resolve(data.data); + } + } + }; + window.addEventListener("message", handler); + this.send(messageType, data); + }); + } + + onOpen(callback: () => void): void { + window.addEventListener("message", (event: any) => { + if (event.data.type === "websocketForwardingOpen") { + callback(); + } + }); + } + onClose(callback: () => void): void { + window.addEventListener("message", (event: any) => { + if (event.data.type === "websocketForwardingClose") { + callback(); + } + }); + } +} diff --git a/extension/react-app/src/index.css b/extension/react-app/src/index.css index dd38eec3..20599d30 100644 --- a/extension/react-app/src/index.css +++ b/extension/react-app/src/index.css @@ -21,7 +21,7 @@ html, body, #root { - height: calc(100% - 7px); + height: calc(100%); } body { diff --git a/extension/react-app/src/main.tsx b/extension/react-app/src/main.tsx index 791f139e..1b94dc82 100644 --- a/extension/react-app/src/main.tsx +++ b/extension/react-app/src/main.tsx @@ -1,10 +1,19 @@ -import React from 'react' -import ReactDOM from 'react-dom/client' -import App from './App' -import './index.css' +import React from "react"; +import ReactDOM from "react-dom/client"; +import App from "./App"; +import "./index.css"; -ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render( +import posthog from "posthog-js"; +import { PostHogProvider } from "posthog-js/react"; + +posthog.init("phc_JS6XFROuNbhJtVCEdTSYk6gl5ArRrTNMpCcguAXlSPs", { + api_host: "https://app.posthog.com", +}); + +ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render( <React.StrictMode> - <App /> - </React.StrictMode>, -) + <PostHogProvider client={posthog}> + <App /> + </PostHogProvider> + </React.StrictMode> +); diff --git a/extension/react-app/src/tabs/chat/MessageDiv.tsx b/extension/react-app/src/tabs/chat/MessageDiv.tsx index d7c79721..9bdd8638 100644 --- a/extension/react-app/src/tabs/chat/MessageDiv.tsx +++ b/extension/react-app/src/tabs/chat/MessageDiv.tsx @@ -20,7 +20,8 @@ const Container = styled.div` margin: 3px; width: fit-content; max-width: 75%; - overflow: scroll; + overflow-y: scroll; + scrollbar-gutter: stable both-edges; word-wrap: break-word; -ms-word-wrap: break-word; height: fit-content; diff --git a/extension/react-app/src/tabs/notebook.tsx b/extension/react-app/src/tabs/gui.tsx index a9c69c5b..42ad4ed5 100644 --- a/extension/react-app/src/tabs/notebook.tsx +++ b/extension/react-app/src/tabs/gui.tsx @@ -14,10 +14,12 @@ import StepContainer from "../components/StepContainer"; import { useSelector } from "react-redux"; import { RootStore } from "../redux/store"; import useContinueWebsocket from "../hooks/useWebsocket"; +import useContinueGUIProtocol from "../hooks/useWebsocket"; -let TopNotebookDiv = styled.div` +let TopGUIDiv = styled.div` display: grid; grid-template-columns: 1fr; + overflow: scroll; `; let UserInputQueueItem = styled.div` @@ -28,17 +30,15 @@ let UserInputQueueItem = styled.div` text-align: center; `; -interface NotebookProps { +interface GUIProps { firstObservation?: any; } -function Notebook(props: NotebookProps) { - const serverUrl = useSelector((state: RootStore) => state.config.apiUrl); - +function GUI(props: GUIProps) { const [waitingForSteps, setWaitingForSteps] = useState(false); const [userInputQueue, setUserInputQueue] = useState<string[]>([]); const [history, setHistory] = useState<History | undefined>(); - // { + // { // timeline: [ // { // step: { @@ -154,33 +154,19 @@ function Notebook(props: NotebookProps) { // }, // ], // current_index: 0, - // } as any - // ); + // } as any); - const { send: websocketSend } = useContinueWebsocket(serverUrl, (msg) => { - let data = JSON.parse(msg.data); - if (data.messageType === "state") { - setWaitingForSteps(data.state.active); - setHistory(data.state.history); - setUserInputQueue(data.state.user_input_queue); - } - }); + const client = useContinueGUIProtocol(); - // useEffect(() => { - // (async () => { - // if (sessionId && props.firstObservation) { - // let resp = await fetch(serverUrl + "/observation", { - // method: "POST", - // headers: new Headers({ - // "x-continue-session-id": sessionId, - // }), - // body: JSON.stringify({ - // observation: props.firstObservation, - // }), - // }); - // } - // })(); - // }, [props.firstObservation]); + useEffect(() => { + console.log("CLIENT ON STATE UPDATE: ", client, client?.onStateUpdate); + client?.onStateUpdate((state) => { + console.log("Received state update: ", state); + setWaitingForSteps(state.active); + setHistory(state.history); + setUserInputQueue(state.user_input_queue); + }); + }, [client]); const mainTextInputRef = useRef<HTMLTextAreaElement>(null); @@ -201,14 +187,12 @@ function Notebook(props: NotebookProps) { const onMainTextInput = () => { if (mainTextInputRef.current) { - let value = mainTextInputRef.current.value; + if (!client) return; + let input = mainTextInputRef.current.value; setWaitingForSteps(true); - websocketSend({ - messageType: "main_input", - value: value, - }); + client.sendMainInput(input); setUserInputQueue((queue) => { - return [...queue, value]; + return [...queue, input]; }); mainTextInputRef.current.value = ""; mainTextInputRef.current.style.height = ""; @@ -216,17 +200,22 @@ function Notebook(props: NotebookProps) { }; const onStepUserInput = (input: string, index: number) => { + if (!client) return; console.log("Sending step user input", input, index); - websocketSend({ - messageType: "step_user_input", - value: input, - index, - }); + client.sendStepUserInput(input, index); }; // const iterations = useSelector(selectIterations); return ( - <TopNotebookDiv> + <TopGUIDiv> + {typeof client === "undefined" && ( + <> + <Loader></Loader> + <p style={{ textAlign: "center" }}> + Trying to reconnect with server... + </p> + </> + )} {history?.timeline.map((node: HistoryNode, index: number) => { return ( <StepContainer @@ -237,17 +226,10 @@ function Notebook(props: NotebookProps) { inFuture={index > history?.current_index} historyNode={node} onRefinement={(input: string) => { - websocketSend({ - messageType: "refinement_input", - value: input, - index, - }); + client?.sendRefinementInput(input, index); }} onReverse={() => { - websocketSend({ - messageType: "reverse", - index, - }); + client?.reverseToIndex(index); }} /> ); @@ -278,8 +260,8 @@ function Notebook(props: NotebookProps) { }} ></MainTextInput> <ContinueButton onClick={onMainTextInput}></ContinueButton> - </TopNotebookDiv> + </TopGUIDiv> ); } -export default Notebook; +export default GUI; diff --git a/extension/react-app/src/vscode/index.ts b/extension/react-app/src/vscode/index.ts index 7e373cd9..0785aa4d 100644 --- a/extension/react-app/src/vscode/index.ts +++ b/extension/react-app/src/vscode/index.ts @@ -5,6 +5,7 @@ declare const vscode: any; export function postVscMessage(type: string, data: any) { if (typeof vscode === "undefined") { + console.log("Unable to send message: vscode is undefined"); return; } vscode.postMessage({ diff --git a/extension/react-app/tsconfig.json b/extension/react-app/tsconfig.json index 3d0a51a8..940a9359 100644 --- a/extension/react-app/tsconfig.json +++ b/extension/react-app/tsconfig.json @@ -16,6 +16,6 @@ "noEmit": true, "jsx": "react-jsx" }, - "include": ["src"], + "include": ["src", "../src/util/messenger.ts"], "references": [{ "path": "./tsconfig.node.json" }] } |