summaryrefslogtreecommitdiff
path: root/extension/react-app
diff options
context:
space:
mode:
Diffstat (limited to 'extension/react-app')
-rw-r--r--extension/react-app/README.md3
-rw-r--r--extension/react-app/package-lock.json39
-rw-r--r--extension/react-app/package.json1
-rw-r--r--extension/react-app/src/App.tsx6
-rw-r--r--extension/react-app/src/components/CodeBlock.tsx3
-rw-r--r--extension/react-app/src/components/DebugPanel.tsx4
-rw-r--r--extension/react-app/src/components/StepContainer.tsx31
-rw-r--r--extension/react-app/src/components/index.ts2
-rw-r--r--extension/react-app/src/hooks/ContinueGUIClientProtocol.ts13
-rw-r--r--extension/react-app/src/hooks/messenger.ts108
-rw-r--r--extension/react-app/src/hooks/useContinueGUIProtocol.ts49
-rw-r--r--extension/react-app/src/hooks/useWebsocket.ts82
-rw-r--r--extension/react-app/src/hooks/vscodeMessenger.ts71
-rw-r--r--extension/react-app/src/index.css2
-rw-r--r--extension/react-app/src/main.tsx25
-rw-r--r--extension/react-app/src/tabs/chat/MessageDiv.tsx3
-rw-r--r--extension/react-app/src/tabs/gui.tsx (renamed from extension/react-app/src/tabs/notebook.tsx)90
-rw-r--r--extension/react-app/src/vscode/index.ts1
-rw-r--r--extension/react-app/tsconfig.json2
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" }]
}