From f19345c652cfcf1bdf13d0a44a2f302e0cd1aa4c Mon Sep 17 00:00:00 2001 From: Nate Sesti Date: Sun, 6 Aug 2023 09:28:22 -0700 Subject: feat: :construction: Router and new history page --- extension/react-app/src/pages/history.tsx | 55 +++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 extension/react-app/src/pages/history.tsx (limited to 'extension/react-app/src/pages/history.tsx') 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([]); + 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 ( +
+ + + {sessions.map((session, index) => ( + + + + ))} + +
+
{ + // client?.loadSession(session.id); + // document.location.href = "/gui"; + }} + > +
{session.title}
+
+ {session.date_created} +
+
+
+
+ ); +} + +export default History; -- cgit v1.2.3-70-g09d2 From 31e7c9828f985eceb16b4c9c749cc5d4d9fd5beb Mon Sep 17 00:00:00 2001 From: Nate Sesti Date: Sun, 6 Aug 2023 13:28:22 -0700 Subject: feat: :construction: react-router-dom work --- .../src/continuedev/server/session_manager.py | 4 + extension/react-app/package-lock.json | 219 ++++----------------- extension/react-app/package.json | 2 +- extension/react-app/src/App.tsx | 36 ++-- extension/react-app/src/components/Layout.tsx | 1 + extension/react-app/src/main.tsx | 10 +- extension/react-app/src/pages/error.tsx | 17 ++ extension/react-app/src/pages/gui.tsx | 5 +- extension/react-app/src/pages/history.tsx | 23 ++- 9 files changed, 108 insertions(+), 209 deletions(-) create mode 100644 extension/react-app/src/pages/error.tsx (limited to 'extension/react-app/src/pages/history.tsx') diff --git a/continuedev/src/continuedev/server/session_manager.py b/continuedev/src/continuedev/server/session_manager.py index dade9853..f876c9a9 100644 --- a/continuedev/src/continuedev/server/session_manager.py +++ b/continuedev/src/continuedev/server/session_manager.py @@ -159,5 +159,9 @@ session_manager = SessionManager() async def list_sessions(): """List all sessions""" sessions_list_file = getSessionsListFilePath() + if not os.path.exists(sessions_list_file): + print("Returning empty sessions list") + return [] sessions = json.load(open(sessions_list_file, "r")) + print("Returning sessions list: ", sessions) return sessions diff --git a/extension/react-app/package-lock.json b/extension/react-app/package-lock.json index b5e32316..b057fe20 100644 --- a/extension/react-app/package-lock.json +++ b/extension/react-app/package-lock.json @@ -18,7 +18,7 @@ "react": "^18.2.0", "react-dom": "^18.2.0", "react-redux": "^8.0.5", - "react-router-dom": "^5.2.0", + "react-router-dom": "^6.14.2", "react-switch": "^7.0.0", "react-syntax-highlighter": "^15.5.0", "react-tooltip": "^5.18.0", @@ -872,6 +872,14 @@ "node": ">= 8" } }, + "node_modules/@remix-run/router": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.7.2.tgz", + "integrity": "sha512-7Lcn7IqGMV+vizMPoEl5F0XDshcdDYtMI6uJLQdQz5CfZAwy3vvGKYSUk789qndt5dEC4HfSjviSYlSoHGL2+A==", + "engines": { + "node": ">=14" + } + }, "node_modules/@swc/core": { "version": "1.3.73", "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.3.73.tgz", @@ -2250,19 +2258,6 @@ "node": "*" } }, - "node_modules/history": { - "version": "4.10.1", - "resolved": "https://registry.npmjs.org/history/-/history-4.10.1.tgz", - "integrity": "sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==", - "dependencies": { - "@babel/runtime": "^7.1.2", - "loose-envify": "^1.2.0", - "resolve-pathname": "^3.0.0", - "tiny-invariant": "^1.0.2", - "tiny-warning": "^1.0.0", - "value-equal": "^1.0.1" - } - }, "node_modules/hoist-non-react-statics": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", @@ -2433,11 +2428,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==" - }, "node_modules/jiti": { "version": "1.19.1", "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.19.1.tgz", @@ -3513,14 +3503,6 @@ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, - "node_modules/path-to-regexp": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", - "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", - "dependencies": { - "isarray": "0.0.1" - } - }, "node_modules/picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", @@ -3842,61 +3824,35 @@ } }, "node_modules/react-router": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-5.2.0.tgz", - "integrity": "sha512-smz1DUuFHRKdcJC0jobGo8cVbhO3x50tCL4icacOlcwDOEQPq4TMqwx3sY1TP+DvtTgz4nm3thuo7A+BK2U0Dw==", - "dependencies": { - "@babel/runtime": "^7.1.2", - "history": "^4.9.0", - "hoist-non-react-statics": "^3.1.0", - "loose-envify": "^1.3.1", - "mini-create-react-context": "^0.4.0", - "path-to-regexp": "^1.7.0", - "prop-types": "^15.6.2", - "react-is": "^16.6.0", - "tiny-invariant": "^1.0.2", - "tiny-warning": "^1.0.0" + "version": "6.14.2", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.14.2.tgz", + "integrity": "sha512-09Zss2dE2z+T1D03IheqAFtK4UzQyX8nFPWx6jkwdYzGLXd5ie06A6ezS2fO6zJfEb/SpG6UocN2O1hfD+2urQ==", + "dependencies": { + "@remix-run/router": "1.7.2" + }, + "engines": { + "node": ">=14" }, "peerDependencies": { - "react": ">=15" + "react": ">=16.8" } }, "node_modules/react-router-dom": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-5.2.0.tgz", - "integrity": "sha512-gxAmfylo2QUjcwxI63RhQ5G85Qqt4voZpUXSEqCwykV0baaOTQDR1f0PmY8AELqIyVc0NEZUj0Gov5lNGcXgsA==", + "version": "6.14.2", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.14.2.tgz", + "integrity": "sha512-5pWX0jdKR48XFZBuJqHosX3AAHjRAzygouMTyimnBPOLdY3WjzUSKhus2FVMihUFWzeLebDgr4r8UeQFAct7Bg==", "dependencies": { - "@babel/runtime": "^7.1.2", - "history": "^4.9.0", - "loose-envify": "^1.3.1", - "prop-types": "^15.6.2", - "react-router": "5.2.0", - "tiny-invariant": "^1.0.2", - "tiny-warning": "^1.0.0" + "@remix-run/router": "1.7.2", + "react-router": "6.14.2" }, - "peerDependencies": { - "react": ">=15" - } - }, - "node_modules/react-router/node_modules/mini-create-react-context": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/mini-create-react-context/-/mini-create-react-context-0.4.1.tgz", - "integrity": "sha512-YWCYEmd5CQeHGSAKrYvXgmzzkrvssZcuuQDDeqkT+PziKGMgE+0MCCtcKbROzocGBG1meBLl2FotlRwf4gAzbQ==", - "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", - "dependencies": { - "@babel/runtime": "^7.12.1", - "tiny-warning": "^1.0.3" + "engines": { + "node": ">=14" }, "peerDependencies": { - "prop-types": "^15.0.0", - "react": "^0.14.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" + "react": ">=16.8", + "react-dom": ">=16.8" } }, - "node_modules/react-router/node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" - }, "node_modules/react-switch": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/react-switch/-/react-switch-7.0.0.tgz", @@ -4291,11 +4247,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/resolve-pathname": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-3.0.0.tgz", - "integrity": "sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng==" - }, "node_modules/reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", @@ -4536,16 +4487,6 @@ "node": ">=0.8" } }, - "node_modules/tiny-invariant": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.1.tgz", - "integrity": "sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw==" - }, - "node_modules/tiny-warning": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz", - "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==" - }, "node_modules/to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", @@ -4781,11 +4722,6 @@ "node": ">=8" } }, - "node_modules/value-equal": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/value-equal/-/value-equal-1.0.1.tgz", - "integrity": "sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw==" - }, "node_modules/vfile": { "version": "5.3.7", "resolved": "https://registry.npmjs.org/vfile/-/vfile-5.3.7.tgz", @@ -5470,6 +5406,11 @@ "fastq": "^1.6.0" } }, + "@remix-run/router": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.7.2.tgz", + "integrity": "sha512-7Lcn7IqGMV+vizMPoEl5F0XDshcdDYtMI6uJLQdQz5CfZAwy3vvGKYSUk789qndt5dEC4HfSjviSYlSoHGL2+A==" + }, "@swc/core": { "version": "1.3.73", "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.3.73.tgz", @@ -6446,19 +6387,6 @@ "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==" }, - "history": { - "version": "4.10.1", - "resolved": "https://registry.npmjs.org/history/-/history-4.10.1.tgz", - "integrity": "sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==", - "requires": { - "@babel/runtime": "^7.1.2", - "loose-envify": "^1.2.0", - "resolve-pathname": "^3.0.0", - "tiny-invariant": "^1.0.2", - "tiny-warning": "^1.0.0", - "value-equal": "^1.0.1" - } - }, "hoist-non-react-statics": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", @@ -6573,11 +6501,6 @@ "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==" }, - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==" - }, "jiti": { "version": "1.19.1", "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.19.1.tgz", @@ -7268,14 +7191,6 @@ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, - "path-to-regexp": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", - "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", - "requires": { - "isarray": "0.0.1" - } - }, "picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", @@ -7462,50 +7377,20 @@ } }, "react-router": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-5.2.0.tgz", - "integrity": "sha512-smz1DUuFHRKdcJC0jobGo8cVbhO3x50tCL4icacOlcwDOEQPq4TMqwx3sY1TP+DvtTgz4nm3thuo7A+BK2U0Dw==", - "requires": { - "@babel/runtime": "^7.1.2", - "history": "^4.9.0", - "hoist-non-react-statics": "^3.1.0", - "loose-envify": "^1.3.1", - "mini-create-react-context": "^0.4.0", - "path-to-regexp": "^1.7.0", - "prop-types": "^15.6.2", - "react-is": "^16.6.0", - "tiny-invariant": "^1.0.2", - "tiny-warning": "^1.0.0" - }, - "dependencies": { - "mini-create-react-context": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/mini-create-react-context/-/mini-create-react-context-0.4.1.tgz", - "integrity": "sha512-YWCYEmd5CQeHGSAKrYvXgmzzkrvssZcuuQDDeqkT+PziKGMgE+0MCCtcKbROzocGBG1meBLl2FotlRwf4gAzbQ==", - "requires": { - "@babel/runtime": "^7.12.1", - "tiny-warning": "^1.0.3" - } - }, - "react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" - } + "version": "6.14.2", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.14.2.tgz", + "integrity": "sha512-09Zss2dE2z+T1D03IheqAFtK4UzQyX8nFPWx6jkwdYzGLXd5ie06A6ezS2fO6zJfEb/SpG6UocN2O1hfD+2urQ==", + "requires": { + "@remix-run/router": "1.7.2" } }, "react-router-dom": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-5.2.0.tgz", - "integrity": "sha512-gxAmfylo2QUjcwxI63RhQ5G85Qqt4voZpUXSEqCwykV0baaOTQDR1f0PmY8AELqIyVc0NEZUj0Gov5lNGcXgsA==", + "version": "6.14.2", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.14.2.tgz", + "integrity": "sha512-5pWX0jdKR48XFZBuJqHosX3AAHjRAzygouMTyimnBPOLdY3WjzUSKhus2FVMihUFWzeLebDgr4r8UeQFAct7Bg==", "requires": { - "@babel/runtime": "^7.1.2", - "history": "^4.9.0", - "loose-envify": "^1.3.1", - "prop-types": "^15.6.2", - "react-router": "5.2.0", - "tiny-invariant": "^1.0.2", - "tiny-warning": "^1.0.0" + "@remix-run/router": "1.7.2", + "react-router": "6.14.2" } }, "react-switch": { @@ -7798,11 +7683,6 @@ "supports-preserve-symlinks-flag": "^1.0.0" } }, - "resolve-pathname": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-3.0.0.tgz", - "integrity": "sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng==" - }, "reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", @@ -7967,16 +7847,6 @@ "thenify": ">= 3.1.0 < 4" } }, - "tiny-invariant": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.1.tgz", - "integrity": "sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw==" - }, - "tiny-warning": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz", - "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==" - }, "to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", @@ -8132,11 +8002,6 @@ "sade": "^1.7.3" } }, - "value-equal": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/value-equal/-/value-equal-1.0.1.tgz", - "integrity": "sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw==" - }, "vfile": { "version": "5.3.7", "resolved": "https://registry.npmjs.org/vfile/-/vfile-5.3.7.tgz", diff --git a/extension/react-app/package.json b/extension/react-app/package.json index 5eb58c8a..b4762990 100644 --- a/extension/react-app/package.json +++ b/extension/react-app/package.json @@ -19,7 +19,7 @@ "react": "^18.2.0", "react-dom": "^18.2.0", "react-redux": "^8.0.5", - "react-router-dom": "^5.2.0", + "react-router-dom": "^6.14.2", "react-switch": "^7.0.0", "react-syntax-highlighter": "^15.5.0", "react-tooltip": "^5.18.0", diff --git a/extension/react-app/src/App.tsx b/extension/react-app/src/App.tsx index 15b536db..48ecebaa 100644 --- a/extension/react-app/src/App.tsx +++ b/extension/react-app/src/App.tsx @@ -4,12 +4,6 @@ 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, @@ -21,6 +15,30 @@ import { import { updateFileSystem } from "./redux/slices/debugContexSlice"; import { setHighlightedCode } from "./redux/slices/miscSlice"; import { postVscMessage } from "./vscode"; +import { createBrowserRouter, RouterProvider } from "react-router-dom"; +import ErrorPage from "./pages/error"; + +const router = createBrowserRouter([ + { + path: "/", + element: , + errorElement: , + children: [ + { + path: "/index.html", + element: , + }, + { + path: "/", + element: , + }, + { + path: "/history", + element: , + }, + ], + }, +]); export const GUIClientContext = createContext< ContinueGUIClientProtocol | undefined @@ -53,11 +71,7 @@ function App() { return ( - - } /> - } /> - } /> - + ); } diff --git a/extension/react-app/src/components/Layout.tsx b/extension/react-app/src/components/Layout.tsx index d1688299..47ed9cc3 100644 --- a/extension/react-app/src/components/Layout.tsx +++ b/extension/react-app/src/components/Layout.tsx @@ -8,6 +8,7 @@ const LayoutTopDiv = styled.div` scrollbar-base-color: transparent; scrollbar-width: thin; `; + const Layout = () => { return ( diff --git a/extension/react-app/src/main.tsx b/extension/react-app/src/main.tsx index 1b70e786..1776490c 100644 --- a/extension/react-app/src/main.tsx +++ b/extension/react-app/src/main.tsx @@ -7,14 +7,6 @@ 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: , - }, -]); console.log("Starting React"); @@ -27,7 +19,7 @@ ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render( - + diff --git a/extension/react-app/src/pages/error.tsx b/extension/react-app/src/pages/error.tsx new file mode 100644 index 00000000..5267c405 --- /dev/null +++ b/extension/react-app/src/pages/error.tsx @@ -0,0 +1,17 @@ +import { useRouteError } from "react-router-dom"; + +export default function ErrorPage() { + const error: any = useRouteError(); + console.error(error); + + return ( +
+

Error in Continue React App

+

+ {error.statusText || error.message} +

+
+
{error.stack}
+
+ ); +} diff --git a/extension/react-app/src/pages/gui.tsx b/extension/react-app/src/pages/gui.tsx index 247789d6..d565e64f 100644 --- a/extension/react-app/src/pages/gui.tsx +++ b/extension/react-app/src/pages/gui.tsx @@ -34,6 +34,7 @@ import { setBottomMessageCloseTimeout, } from "../redux/slices/uiStateSlice"; import RingLoader from "../components/RingLoader"; +import { useNavigate } from "react-router-dom"; const TopGUIDiv = styled.div` overflow: hidden; @@ -83,6 +84,8 @@ interface GUIProps { } function GUI(props: GUIProps) { + const navigate = useNavigate(); + const client = useContext(GUIClientContext); const posthog = usePostHog(); @@ -595,7 +598,7 @@ If you already have an LLM deployed on your own infrastructure, or would like to { // Go to /history page - document.location.href = "/history"; + navigate("/history"); }} text="History" > diff --git a/extension/react-app/src/pages/history.tsx b/extension/react-app/src/pages/history.tsx index 6539f0f5..052fe5be 100644 --- a/extension/react-app/src/pages/history.tsx +++ b/extension/react-app/src/pages/history.tsx @@ -1,22 +1,26 @@ 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"; +import { useNavigate } from "react-router-dom"; function History() { + const navigate = useNavigate(); const [sessions, setSessions] = useState([]); const client = useContext(GUIClientContext); const apiUrl = useSelector((state: RootStore) => state.config.apiUrl); useEffect(() => { const fetchSessions = async () => { - console.log("fetching sessions"); + console.log("fetching sessions from: ", apiUrl); if (!apiUrl) { return; } const response = await fetch(`${apiUrl}/sessions/list`); + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } const json = await response.json(); console.log(json); setSessions(json); @@ -25,23 +29,22 @@ function History() { }, [client]); return ( -
- +
+

History

+
{sessions.map((session, index) => ( -- cgit v1.2.3-70-g09d2 From c25527926ad1d1f861dbed01df577e962e08d746 Mon Sep 17 00:00:00 2001 From: Nate Sesti Date: Sun, 6 Aug 2023 15:24:13 -0700 Subject: feat: :construction: successfully loading past sessions --- continuedev/src/continuedev/core/autopilot.py | 21 ++++++++++++--- continuedev/src/continuedev/core/main.py | 15 ++++++----- continuedev/src/continuedev/libs/util/paths.py | 3 +++ continuedev/src/continuedev/libs/util/telemetry.py | 1 - continuedev/src/continuedev/server/gui.py | 11 ++++++-- .../src/continuedev/server/session_manager.py | 27 ++++++++----------- .../src/hooks/AbstractContinueGUIClientProtocol.ts | 2 ++ .../src/hooks/ContinueGUIClientProtocol.ts | 13 ++++++---- extension/react-app/src/hooks/messenger.ts | 6 +++++ extension/react-app/src/hooks/vscodeMessenger.ts | 4 +++ extension/react-app/src/pages/history.tsx | 30 +++++++++++++++++----- .../react-app/src/redux/slices/configSlice.ts | 4 +-- extension/src/debugPanel.ts | 9 +++++++ extension/src/util/messenger.ts | 6 +++++ 14 files changed, 108 insertions(+), 44 deletions(-) (limited to 'extension/react-app/src/pages/history.tsx') diff --git a/continuedev/src/continuedev/core/autopilot.py b/continuedev/src/continuedev/core/autopilot.py index 7e7ce5d8..6dd30db1 100644 --- a/continuedev/src/continuedev/core/autopilot.py +++ b/continuedev/src/continuedev/core/autopilot.py @@ -15,7 +15,7 @@ from ..plugins.context_providers.highlighted_code import HighlightedCodeContextP from ..server.ide_protocol import AbstractIdeProtocolServer from ..libs.util.queue import AsyncSubscriptionQueue from ..models.main import ContinueBaseModel -from .main import Context, ContinueCustomException, Policy, History, FullState, Step, HistoryNode +from .main import Context, ContinueCustomException, Policy, History, FullState, SessionInfo, Step, HistoryNode from ..plugins.steps.core.core import DisplayErrorStep, ReversibleStep, ManualEditStep, UserInputStep from .sdk import ContinueSDK from ..libs.util.traceback_parsers import get_python_traceback, get_javascript_traceback @@ -53,7 +53,8 @@ class Autopilot(ContinueBaseModel): policy: Policy = DefaultPolicy() history: History = History.from_empty() context: Context = Context() - full_state: Union[FullState, None] = None + full_state: Optional[FullState] = None + session_info: Optional[SessionInfo] = None context_manager: ContextManager = ContextManager() continue_sdk: ContinueSDK = None @@ -88,7 +89,6 @@ class Autopilot(ContinueBaseModel): if full_state is not None: self.history = full_state.history self.context_manager.context_providers["code"].adding_highlighted_code = full_state.adding_highlighted_code - await self.context_manager.set_selected_items(full_state.selected_context_items) self.started = True @@ -112,6 +112,7 @@ class Autopilot(ContinueBaseModel): adding_highlighted_code=self.context_manager.context_providers[ "code"].adding_highlighted_code if "code" in self.context_manager.context_providers else False, selected_context_items=await self.context_manager.get_selected_items() if self.context_manager is not None else [], + session_info=self.session_info ) self.full_state = full_state return full_state @@ -375,6 +376,20 @@ class Autopilot(ContinueBaseModel): self._main_user_input_queue.append(user_input) await self.update_subscribers() + # Use the first input to create title for session info, and make the session saveable + if self.session_info is None: + async def create_title(): + title = await self.continue_sdk.models.medium.complete(f"Give a short title to describe the current chat session. Do not put quotes around the title. The first message was: \"{user_input}\". The title is: ") + self.session_info = SessionInfo( + title=title, + session_id=self.ide.session_id, + date_created=time.strftime( + "%Y-%m-%d %H:%M:%S", time.gmtime()) + ) + + create_async_task(create_title(), on_error=lambda e: self.continue_sdk.run_step( + DisplayErrorStep(e=e))) + if len(self._main_user_input_queue) > 1: return diff --git a/continuedev/src/continuedev/core/main.py b/continuedev/src/continuedev/core/main.py index 9a06f2e1..a33d777e 100644 --- a/continuedev/src/continuedev/core/main.py +++ b/continuedev/src/continuedev/core/main.py @@ -1,5 +1,5 @@ import json -from typing import Coroutine, Dict, List, Literal, Union +from typing import Coroutine, Dict, List, Literal, Optional, Union from pydantic.schema import schema @@ -253,6 +253,12 @@ class ContextItem(BaseModel): editable: bool = False +class SessionInfo(ContinueBaseModel): + session_id: str + title: str + date_created: str + + class FullState(ContinueBaseModel): """A full state of the program, including the history""" history: History @@ -261,12 +267,7 @@ class FullState(ContinueBaseModel): slash_commands: List[SlashCommandDescription] adding_highlighted_code: bool selected_context_items: List[ContextItem] - - -class SessionInfo(ContinueBaseModel): - session_id: str - title: str - date_created: str + session_info: Optional[SessionInfo] = None class ContinueSDK: diff --git a/continuedev/src/continuedev/libs/util/paths.py b/continuedev/src/continuedev/libs/util/paths.py index 66c921f7..01b594cf 100644 --- a/continuedev/src/continuedev/libs/util/paths.py +++ b/continuedev/src/continuedev/libs/util/paths.py @@ -35,6 +35,9 @@ def getSessionFilePath(session_id: str): def getSessionsListFilePath(): path = os.path.join(getSessionsFolderPath(), "sessions.json") os.makedirs(os.path.dirname(path), exist_ok=True) + if not os.path.exists(path): + with open(path, 'w') as f: + f.write("[]") return path diff --git a/continuedev/src/continuedev/libs/util/telemetry.py b/continuedev/src/continuedev/libs/util/telemetry.py index 60c910bb..0f66ad8d 100644 --- a/continuedev/src/continuedev/libs/util/telemetry.py +++ b/continuedev/src/continuedev/libs/util/telemetry.py @@ -23,7 +23,6 @@ class PostHogLogger: self.posthog = Posthog(self.api_key, host='https://app.posthog.com') def setup(self, unique_id: str, allow_anonymous_telemetry: bool): - logger.debug(f"Setting unique_id as {unique_id}") self.unique_id = unique_id or "NO_UNIQUE_ID" self.allow_anonymous_telemetry = allow_anonymous_telemetry or True diff --git a/continuedev/src/continuedev/server/gui.py b/continuedev/src/continuedev/server/gui.py index b6f7b141..661e1787 100644 --- a/continuedev/src/continuedev/server/gui.py +++ b/continuedev/src/continuedev/server/gui.py @@ -99,6 +99,8 @@ class GUIProtocolServer(AbstractGUIProtocolServer): self.on_show_logs_at_index(data["index"]) elif message_type == "select_context_item": self.select_context_item(data["id"], data["query"]) + elif message_type == "load_session": + self.load_session(data["session_id"]) def on_main_input(self, input: str): # Do something with user input @@ -154,8 +156,13 @@ class GUIProtocolServer(AbstractGUIProtocolServer): create_async_task( self.session.autopilot.select_context_item(id, query), self.on_error) - async def reconnect_at_session(self, session_id: str): - await self._send_json("reconnect_at_session", {"session_id": session_id}) + def load_session(self, session_id: str): + async def load_and_tell_to_reconnect(): + await session_manager.load_session(self.session.session_id, session_id) + await self._send_json("reconnect_at_session", {"session_id": session_id}) + + create_async_task( + load_and_tell_to_reconnect(), self.on_error) @router.websocket("/ws") diff --git a/continuedev/src/continuedev/server/session_manager.py b/continuedev/src/continuedev/server/session_manager.py index f876c9a9..062f9527 100644 --- a/continuedev/src/continuedev/server/session_manager.py +++ b/continuedev/src/continuedev/server/session_manager.py @@ -78,18 +78,7 @@ class SessionManager: try: await autopilot.start(full_state=full_state) except Exception as e: - # Have to manually add to history because autopilot isn't started - formatted_err = '\n'.join(traceback.format_exception(e)) - msg_step = MessageStep( - name="Error starting Autopilot", message=formatted_err) - msg_step.description = f"```\n{formatted_err}\n```" - autopilot.history.add_node(HistoryNode( - step=msg_step, - observation=None, - depth=0, - active=False - )) - logger.warning(f"Error starting Autopilot: {e}") + await self.on_error(e) def on_error(e: Exception) -> Coroutine: err_msg = '\n'.join(traceback.format_exception(e)) @@ -101,7 +90,7 @@ class SessionManager: async def remove_session(self, session_id: str): logger.debug(f"Removing session: {session_id}") if session_id in self.sessions: - if session_id in self.registered_ides: + if session_id in self.registered_ides and self.registered_ides[session_id] is not None: ws_to_close = self.registered_ides[session_id].websocket if ws_to_close is not None and ws_to_close.client_state != WebSocketState.DISCONNECTED: await self.sessions[session_id].autopilot.ide.websocket.close() @@ -111,6 +100,9 @@ class SessionManager: async def persist_session(self, session_id: str): """Save the session's FullState as a json file""" full_state = await self.sessions[session_id].autopilot.get_full_state() + if full_state.session_info is None: + return + with open(getSessionFilePath(session_id), "w") as f: json.dump(full_state.dict(), f) @@ -118,9 +110,10 @@ class SessionManager: with open(getSessionsListFilePath(), "r") as f: sessions_list = json.load(f) - sessions_list.append(SessionInfo( - session_info=full_state.session_info - )) + sessions_list.append(full_state.session_info.dict()) + + with open(getSessionsListFilePath(), "w") as f: + json.dump(sessions_list, f) async def load_session(self, old_session_id: str, new_session_id: str): """Load the session's FullState from a json file""" @@ -130,7 +123,7 @@ class SessionManager: # Delete the old session, but keep the IDE ide = self.registered_ides[old_session_id] - self.registered_ides[old_session_id] = None + del self.registered_ides[old_session_id] # Start the new session await self.new_session(ide, session_id=new_session_id) diff --git a/extension/react-app/src/hooks/AbstractContinueGUIClientProtocol.ts b/extension/react-app/src/hooks/AbstractContinueGUIClientProtocol.ts index 168fb156..139c9d05 100644 --- a/extension/react-app/src/hooks/AbstractContinueGUIClientProtocol.ts +++ b/extension/react-app/src/hooks/AbstractContinueGUIClientProtocol.ts @@ -31,6 +31,8 @@ abstract class AbstractContinueGUIClientProtocol { abstract selectContextItem(id: string, query: string): void; + abstract loadSession(session_id: string): void; + abstract onReconnectAtSession(session_id: string): void; } diff --git a/extension/react-app/src/hooks/ContinueGUIClientProtocol.ts b/extension/react-app/src/hooks/ContinueGUIClientProtocol.ts index 830954c5..6cfbf66a 100644 --- a/extension/react-app/src/hooks/ContinueGUIClientProtocol.ts +++ b/extension/react-app/src/hooks/ContinueGUIClientProtocol.ts @@ -14,9 +14,11 @@ class ContinueGUIClientProtocol extends AbstractContinueGUIClientProtocol { useVscodeMessagePassing: boolean ) { if (this.messenger) { - // this.messenger.close(); TODO + console.log("Closing session: ", this.serverUrlWithSessionId); + this.messenger.close(); } this.serverUrlWithSessionId = serverUrlWithSessionId; + this.useVscodeMessagePassing = useVscodeMessagePassing; this.messenger = useVscodeMessagePassing ? new VscodeMessenger(serverUrlWithSessionId) : new WebsocketMessenger(serverUrlWithSessionId); @@ -45,12 +47,13 @@ class ContinueGUIClientProtocol extends AbstractContinueGUIClientProtocol { this.connectMessenger(serverUrlWithSessionId, useVscodeMessagePassing); } + loadSession(session_id: string): void { + this.messenger?.send("load_session", { session_id }); + } + onReconnectAtSession(session_id: string): void { this.connectMessenger( - this.serverUrlWithSessionId.replace( - /\/session\/[a-zA-Z0-9-]+/, - `/session/${session_id}` - ), + `${this.serverUrlWithSessionId.split("?")[0]}?session_id=${session_id}`, this.useVscodeMessagePassing ); } diff --git a/extension/react-app/src/hooks/messenger.ts b/extension/react-app/src/hooks/messenger.ts index ecf646c7..0bfbe00c 100644 --- a/extension/react-app/src/hooks/messenger.ts +++ b/extension/react-app/src/hooks/messenger.ts @@ -15,6 +15,8 @@ export abstract class Messenger { abstract sendAndReceive(messageType: string, data: any): Promise; abstract onError(callback: (error: any) => void): void; + + abstract close(): void; } export class WebsocketMessenger extends Messenger { @@ -105,4 +107,8 @@ export class WebsocketMessenger extends Messenger { onError(callback: (error: any) => void): void { this.websocket.addEventListener("error", callback); } + + close(): void { + this.websocket.close(); + } } diff --git a/extension/react-app/src/hooks/vscodeMessenger.ts b/extension/react-app/src/hooks/vscodeMessenger.ts index 13f5092b..cf626721 100644 --- a/extension/react-app/src/hooks/vscodeMessenger.ts +++ b/extension/react-app/src/hooks/vscodeMessenger.ts @@ -76,4 +76,8 @@ export class VscodeMessenger extends Messenger { } }); } + + close(): void { + postVscMessage("websocketForwardingClose", { url: this.serverUrl }); + } } diff --git a/extension/react-app/src/pages/history.tsx b/extension/react-app/src/pages/history.tsx index 052fe5be..0142836f 100644 --- a/extension/react-app/src/pages/history.tsx +++ b/extension/react-app/src/pages/history.tsx @@ -4,6 +4,23 @@ import { GUIClientContext } from "../App"; import { useSelector } from "react-redux"; import { RootStore } from "../redux/store"; import { useNavigate } from "react-router-dom"; +import { secondaryDark, vscBackground } from "../components"; +import styled from "styled-components"; + +const Tr = styled.tr` + &:hover { + background-color: ${secondaryDark}; + } +`; + +const TdDiv = styled.div` + cursor: pointer; + padding-left: 1rem; + padding-right: 1rem; + padding-top: 0.5rem; + padding-bottom: 0.5rem; + border-bottom: 1px solid ${secondaryDark}; +`; function History() { const navigate = useNavigate(); @@ -30,24 +47,23 @@ function History() { return (
-

History

+

History

{ // client?.loadSession(session.id); - // document.location.href = "/gui"; + navigate("/"); }} > -
{session.title}
-
- {session.date_created} -
+
{session.title}
+
{session.date_created}
{sessions.map((session, index) => ( - + - + ))}
-
{ - // client?.loadSession(session.id); + client?.loadSession(session.session_id); navigate("/"); }} >
{session.title}
{session.date_created}
-
+
diff --git a/extension/react-app/src/redux/slices/configSlice.ts b/extension/react-app/src/redux/slices/configSlice.ts index 57c4f860..59c76066 100644 --- a/extension/react-app/src/redux/slices/configSlice.ts +++ b/extension/react-app/src/redux/slices/configSlice.ts @@ -50,7 +50,7 @@ export const configSlice = createSlice({ ) => ({ ...state, dataSwitchOn: action.payload, - }) + }), }, }); @@ -60,6 +60,6 @@ export const { setWorkspacePath, setSessionId, setVscMediaUrl, - setDataSwitchOn + setDataSwitchOn, } = configSlice.actions; export default configSlice.reducer; diff --git a/extension/src/debugPanel.ts b/extension/src/debugPanel.ts index b687c3e4..d133080b 100644 --- a/extension/src/debugPanel.ts +++ b/extension/src/debugPanel.ts @@ -221,6 +221,15 @@ export function setupDebugPanel( } break; } + case "websocketForwardingClose": { + let url = data.url; + let connection = websocketConnections[url]; + if (typeof connection !== "undefined") { + connection.close(); + websocketConnections[url] = undefined; + } + break; + } case "websocketForwardingMessage": { let url = data.url; let connection = websocketConnections[url]; diff --git a/extension/src/util/messenger.ts b/extension/src/util/messenger.ts index bcc88fe1..152d4a1f 100644 --- a/extension/src/util/messenger.ts +++ b/extension/src/util/messenger.ts @@ -18,6 +18,8 @@ export abstract class Messenger { abstract onError(callback: () => void): void; abstract sendAndReceive(messageType: string, data: any): Promise; + + abstract close(): void; } export class WebsocketMessenger extends Messenger { @@ -160,4 +162,8 @@ export class WebsocketMessenger extends Messenger { onError(callback: () => void): void { this.websocket.addEventListener("error", callback); } + + close(): void { + this.websocket.close(); + } } -- cgit v1.2.3-70-g09d2 From aafa5d5ec91a533f70d644e4d3fadd6f388c3e4b Mon Sep 17 00:00:00 2001 From: Nate Sesti Date: Sun, 6 Aug 2023 16:23:19 -0700 Subject: feat: :sparkles: back button on history page --- extension/react-app/src/pages/history.tsx | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'extension/react-app/src/pages/history.tsx') diff --git a/extension/react-app/src/pages/history.tsx b/extension/react-app/src/pages/history.tsx index 0142836f..f925d52c 100644 --- a/extension/react-app/src/pages/history.tsx +++ b/extension/react-app/src/pages/history.tsx @@ -6,6 +6,7 @@ import { RootStore } from "../redux/store"; import { useNavigate } from "react-router-dom"; import { secondaryDark, vscBackground } from "../components"; import styled from "styled-components"; +import { ArrowLeftIcon } from "@heroicons/react/24/outline"; const Tr = styled.tr` &:hover { @@ -47,7 +48,15 @@ function History() { return (
-

History

+
+ navigate("/")} + className="inline-block ml-4 cursor-pointer" + /> +

History

+
{sessions.map((session, index) => ( -- cgit v1.2.3-70-g09d2 From 6bce4b2802d06818f33b260ca4353331ea4b2df9 Mon Sep 17 00:00:00 2001 From: Nate Sesti Date: Sun, 6 Aug 2023 22:51:10 -0700 Subject: refactor: :recycle: hoist FullState up to redux --- continuedev/src/continuedev/core/autopilot.py | 5 +- extension/react-app/src/App.tsx | 2 - extension/react-app/src/pages/gui.tsx | 75 +++++------ extension/react-app/src/pages/history.tsx | 13 +- extension/react-app/src/redux/hooks.ts | 21 --- .../src/redux/selectors/debugContextSelectors.ts | 29 ---- .../react-app/src/redux/slices/debugContexSlice.ts | 149 --------------------- .../src/redux/slices/serverStateReducer.ts | 53 ++++++++ extension/react-app/src/redux/store.ts | 12 +- 9 files changed, 105 insertions(+), 254 deletions(-) delete mode 100644 extension/react-app/src/redux/hooks.ts delete mode 100644 extension/react-app/src/redux/selectors/debugContextSelectors.ts delete mode 100644 extension/react-app/src/redux/slices/debugContexSlice.ts create mode 100644 extension/react-app/src/redux/slices/serverStateReducer.ts (limited to 'extension/react-app/src/pages/history.tsx') diff --git a/continuedev/src/continuedev/core/autopilot.py b/continuedev/src/continuedev/core/autopilot.py index ee29dc88..256f3439 100644 --- a/continuedev/src/continuedev/core/autopilot.py +++ b/continuedev/src/continuedev/core/autopilot.py @@ -5,6 +5,7 @@ from typing import Callable, Coroutine, Dict, List, Optional, Union from aiohttp import ClientPayloadError from pydantic import root_validator +from ..libs.util.strings import remove_quotes_and_escapes from ..models.filesystem import RangeInFileWithContents from ..models.filesystem_edit import FileEditWithFullContents from .observation import Observation, InternalErrorObservation @@ -381,11 +382,11 @@ class Autopilot(ContinueBaseModel): if self.session_info is None: async def create_title(): title = await self.continue_sdk.models.medium.complete(f"Give a short title to describe the current chat session. Do not put quotes around the title. The first message was: \"{user_input}\". The title is: ") + title = remove_quotes_and_escapes(title) self.session_info = SessionInfo( title=title, session_id=self.ide.session_id, - date_created=time.strftime( - "%Y-%m-%d %H:%M:%S", time.gmtime()) + date_created=str(time.time()) ) create_async_task(create_title(), on_error=lambda e: self.continue_sdk.run_step( diff --git a/extension/react-app/src/App.tsx b/extension/react-app/src/App.tsx index 48ecebaa..879373a0 100644 --- a/extension/react-app/src/App.tsx +++ b/extension/react-app/src/App.tsx @@ -12,7 +12,6 @@ import { setVscMediaUrl, setDataSwitchOn, } from "./redux/slices/configSlice"; -import { updateFileSystem } from "./redux/slices/debugContexSlice"; import { setHighlightedCode } from "./redux/slices/miscSlice"; import { postVscMessage } from "./vscode"; import { createBrowserRouter, RouterProvider } from "react-router-dom"; @@ -60,7 +59,6 @@ function App() { break; case "highlightedCode": dispatch(setHighlightedCode(event.data.rangeInFile)); - dispatch(updateFileSystem(event.data.filesystem)); break; } }; diff --git a/extension/react-app/src/pages/gui.tsx b/extension/react-app/src/pages/gui.tsx index e2e3431a..e96ffe65 100644 --- a/extension/react-app/src/pages/gui.tsx +++ b/extension/react-app/src/pages/gui.tsx @@ -2,9 +2,8 @@ import styled from "styled-components"; import { defaultBorderRadius } from "../components"; import Loader from "../components/Loader"; import ContinueButton from "../components/ContinueButton"; -import { ContextItem, FullState } from "../../../schema/FullState"; +import { FullState } 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"; @@ -23,6 +22,10 @@ import { setShowDialog, } from "../redux/slices/uiStateSlice"; import RingLoader from "../components/RingLoader"; +import { + setServerState, + temporarilySetUserInputQueue, +} from "../redux/slices/serverStateReducer"; const UserInputQueueItem = styled.div` border-radius: ${defaultBorderRadius}; @@ -44,13 +47,18 @@ function GUI(props: GUIProps) { // #endregion + // #region Selectors + const { + history, + user_input_queue, + adding_highlighted_code, + selected_context_items, + } = useSelector((state: RootStore) => state.serverState); + + // #endregion + // #region State const [waitingForSteps, setWaitingForSteps] = useState(false); - const [userInputQueue, setUserInputQueue] = useState([]); - const [addingHighlightedCode, setAddingHighlightedCode] = useState(false); - const [selectedContextItems, setSelectedContextItems] = useState< - ContextItem[] - >([]); const [availableSlashCommands, setAvailableSlashCommands] = useState< { name: string; description: string }[] >([]); @@ -60,27 +68,6 @@ function GUI(props: GUIProps) { true, true, ]); - const [history, setHistory] = useState({ - timeline: [ - { - step: { - name: "Welcome to Continue", - hide: false, - description: `- Highlight code section and ask a question or give instructions -- Use \`cmd+m\` (Mac) / \`ctrl+m\` (Windows) to open Continue -- Use \`/help\` to ask questions about how to use Continue`, - system_message: null, - chat_context: [], - manage_own_chat_context: false, - message: "", - }, - depth: 0, - deleted: false, - active: false, - }, - ], - current_index: 3, - } as any); const [waitingForClient, setWaitingForClient] = useState(true); const [showLoading, setShowLoading] = useState(false); @@ -163,11 +150,9 @@ function GUI(props: GUIProps) { state.history.current_index ].step.description?.trim() === ""; + dispatch(setServerState(state)); + setWaitingForSteps(waitingForSteps); - setHistory(state.history); - setSelectedContextItems(state.selected_context_items || []); - setUserInputQueue(state.user_input_queue); - setAddingHighlightedCode(state.adding_highlighted_code); setAvailableSlashCommands( state.slash_commands.map((c: any) => { return { @@ -203,11 +188,11 @@ function GUI(props: GUIProps) { useEffect(() => { if (client && waitingForClient) { setWaitingForClient(false); - for (const input of userInputQueue) { + for (const input of user_input_queue) { client.sendMainInput(input); } } - }, [client, userInputQueue, waitingForClient]); + }, [client, user_input_queue, waitingForClient]); const onMainTextInput = (event?: any) => { if (mainTextInputRef.current) { @@ -218,9 +203,11 @@ function GUI(props: GUIProps) { } (mainTextInputRef.current as any).setInputValue(""); if (!client) { - setUserInputQueue((queue) => { - return [...queue, input]; - }); + dispatch( + temporarilySetUserInputQueue((queue: string[]) => { + return [...queue, input]; + }) + ); return; } @@ -249,9 +236,11 @@ function GUI(props: GUIProps) { if (input.trim() === "") return; client.sendMainInput(input); - setUserInputQueue((queue) => { - return [...queue, input]; - }); + dispatch( + temporarilySetUserInputQueue((queue: string[]) => { + return [...queue, input]; + }) + ); // Increment localstorage counter const counter = localStorage.getItem("mainTextEntryCounter"); @@ -435,7 +424,7 @@ function GUI(props: GUIProps) { {waitingForSteps && }
- {userInputQueue.map((input) => { + {user_input_queue.map((input) => { return {input}; })}
@@ -450,11 +439,11 @@ function GUI(props: GUIProps) { }} onInputValueChange={() => {}} items={availableSlashCommands} - selectedContextItems={selectedContextItems} + selectedContextItems={selected_context_items} onToggleAddContext={() => { client?.toggleAddingHighlightedCode(); }} - addingHighlightedCode={addingHighlightedCode} + addingHighlightedCode={adding_highlighted_code} /> diff --git a/extension/react-app/src/pages/history.tsx b/extension/react-app/src/pages/history.tsx index f925d52c..a553c6ae 100644 --- a/extension/react-app/src/pages/history.tsx +++ b/extension/react-app/src/pages/history.tsx @@ -69,7 +69,18 @@ function History() { }} >
{session.title}
-
{session.date_created}
+
+ {new Date( + parseInt(session.date_created) * 1000 + ).toLocaleString("en-US", { + weekday: "short", + year: "numeric", + month: "long", + day: "numeric", + hour: "numeric", + minute: "numeric", + })} +
diff --git a/extension/react-app/src/redux/hooks.ts b/extension/react-app/src/redux/hooks.ts deleted file mode 100644 index a6aef869..00000000 --- a/extension/react-app/src/redux/hooks.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { useCallback } from "react"; -import { useDispatch, useSelector } from "react-redux"; -import { RootStore } from "./store"; -import { selectDebugContextValue } from "./selectors/debugContextSelectors"; -import { updateValue } from "./slices/debugContexSlice"; -import { SerializedDebugContext } from "../../../src/client"; - -export function useDebugContextValue( - key: keyof SerializedDebugContext, - defaultValue: any -): [any, (value: any) => void] { - const dispatch = useDispatch(); - const state = - useSelector((state: RootStore) => selectDebugContextValue(state, key)) || - defaultValue; - const boundAction = useCallback( - (value: any) => dispatch(updateValue({ key, value })), - [dispatch, key] - ); - return [state, boundAction]; -} diff --git a/extension/react-app/src/redux/selectors/debugContextSelectors.ts b/extension/react-app/src/redux/selectors/debugContextSelectors.ts deleted file mode 100644 index 89201bb7..00000000 --- a/extension/react-app/src/redux/selectors/debugContextSelectors.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { RootStore } from "../store"; - -const selectDebugContext = (state: RootStore) => { - return { - ...state.debugState.debugContext, - rangesInFiles: state.debugState.debugContext.rangesInFiles.filter( - (_, index) => state.debugState.rangesMask[index] - ), - }; -}; - -const selectAllRangesInFiles = (state: RootStore) => { - return state.debugState.debugContext.rangesInFiles; -}; - -const selectRangesMask = (state: RootStore) => { - return state.debugState.rangesMask; -}; - -const selectDebugContextValue = (state: RootStore, key: string) => { - return (state.debugState.debugContext as any)[key]; -}; - -export { - selectDebugContext, - selectDebugContextValue, - selectAllRangesInFiles, - selectRangesMask, -}; diff --git a/extension/react-app/src/redux/slices/debugContexSlice.ts b/extension/react-app/src/redux/slices/debugContexSlice.ts deleted file mode 100644 index 647440d5..00000000 --- a/extension/react-app/src/redux/slices/debugContexSlice.ts +++ /dev/null @@ -1,149 +0,0 @@ -import { createSlice } from "@reduxjs/toolkit"; -import { RangeInFile, SerializedDebugContext } from "../../../../src/client"; -import { RootStore } from "../store"; - -export const debugStateSlice = createSlice({ - name: "debugState", - initialState: { - debugContext: { - rangesInFiles: [], - filesystem: {}, - traceback: undefined, - description: undefined, - }, - rangesMask: [], - } as RootStore["debugState"], - reducers: { - updateValue: ( - state: RootStore["debugState"], - action: { - type: string; - payload: { key: keyof SerializedDebugContext; value: any }; - } - ) => { - return { - ...state, - debugContext: { - ...state.debugContext, - [action.payload.key]: action.payload.value, - }, - }; - }, - addRangeInFile: ( - state: RootStore["debugState"], - action: { - type: string; - payload: { - rangeInFile: RangeInFile; - canUpdateLast: boolean; - }; - } - ) => { - let rangesInFiles = state.debugContext.rangesInFiles; - // If identical to existing range, don't add. Ideally you check for overlap of ranges. - for (let range of rangesInFiles) { - if ( - range.filepath === action.payload.rangeInFile.filepath && - range.range.start.line === - action.payload.rangeInFile.range.start.line && - range.range.end.line === action.payload.rangeInFile.range.end.line - ) { - return state; - } - } - - if ( - action.payload.canUpdateLast && - rangesInFiles.length > 0 && - rangesInFiles[rangesInFiles.length - 1].filepath === - action.payload.rangeInFile.filepath - ) { - return { - ...state, - debugContext: { - ...state.debugContext, - rangesInFiles: [ - ...rangesInFiles.slice(0, rangesInFiles.length - 1), - action.payload.rangeInFile, - ], - }, - }; - } else { - return { - ...state, - debugContext: { - ...state.debugContext, - rangesInFiles: [ - ...state.debugContext.rangesInFiles, - action.payload.rangeInFile, - ], - }, - rangesMask: [...state.rangesMask, true], - }; - } - }, - deleteRangeInFileAt: ( - state: RootStore["debugState"], - action: { - type: string; - payload: number; - } - ) => { - return { - ...state, - debugContext: { - ...state.debugContext, - rangesInFiles: state.debugContext.rangesInFiles.filter( - (_, index) => index !== action.payload - ), - }, - rangesMask: state.rangesMask.filter( - (_, index) => index !== action.payload - ), - }; - }, - toggleSelectionAt: ( - state: RootStore["debugState"], - action: { - type: string; - payload: number; - } - ) => { - return { - ...state, - rangesMask: state.rangesMask.map((_, index) => - index === action.payload - ? !state.rangesMask[index] - : state.rangesMask[index] - ), - }; - }, - updateFileSystem: ( - state: RootStore["debugState"], - action: { - type: string; - payload: { [filepath: string]: string }; - } - ) => { - return { - ...state, - debugContext: { - ...state.debugContext, - filesystem: { - ...state.debugContext.filesystem, - ...action.payload, - }, - }, - }; - }, - }, -}); - -export const { - updateValue, - updateFileSystem, - addRangeInFile, - deleteRangeInFileAt, - toggleSelectionAt, -} = debugStateSlice.actions; -export default debugStateSlice.reducer; diff --git a/extension/react-app/src/redux/slices/serverStateReducer.ts b/extension/react-app/src/redux/slices/serverStateReducer.ts new file mode 100644 index 00000000..4d9dc326 --- /dev/null +++ b/extension/react-app/src/redux/slices/serverStateReducer.ts @@ -0,0 +1,53 @@ +import { createSlice } from "@reduxjs/toolkit"; +import { FullState } from "../../../../schema/FullState"; + +const initialState: FullState = { + history: { + timeline: [ + { + step: { + name: "Welcome to Continue", + hide: false, + description: `- Highlight code section and ask a question or give instructions + - Use \`cmd+m\` (Mac) / \`ctrl+m\` (Windows) to open Continue + - Use \`/help\` to ask questions about how to use Continue`, + system_message: null, + chat_context: [], + manage_own_chat_context: false, + message: "", + }, + depth: 0, + deleted: false, + active: false, + }, + ], + current_index: 3, + } as any, + user_input_queue: [], + active: false, + slash_commands: [], + adding_highlighted_code: false, + selected_context_items: [], +}; + +export const serverStateSlice = createSlice({ + name: "serverState", + initialState, + reducers: { + setServerState: (state, action) => { + return { + ...action.payload, + selected_context_items: action.payload.selected_context_items || [], + user_input_queue: action.payload.user_input_queue || [], + slash_commands: action.payload.slash_commands || [], + }; + }, + temporarilySetUserInputQueue: (state, action) => { + state.user_input_queue = action.payload; + }, + }, +}); + +export const { setServerState, temporarilySetUserInputQueue } = + serverStateSlice.actions; +export default serverStateSlice.reducer; diff --git a/extension/react-app/src/redux/store.ts b/extension/react-app/src/redux/store.ts index aa8f5e7b..59339060 100644 --- a/extension/react-app/src/redux/store.ts +++ b/extension/react-app/src/redux/store.ts @@ -1,10 +1,11 @@ import { configureStore } from "@reduxjs/toolkit"; -import debugStateReducer from "./slices/debugContexSlice"; import chatReducer from "./slices/chatSlice"; import configReducer from "./slices/configSlice"; import miscReducer from "./slices/miscSlice"; import uiStateReducer from "./slices/uiStateSlice"; -import { RangeInFile, SerializedDebugContext } from "../../../src/client"; +import { RangeInFile } from "../../../src/client"; +import { FullState } from "../../../schema/FullState"; +import serverStateReducer from "./slices/serverStateReducer"; export interface ChatMessage { role: "system" | "user" | "assistant"; @@ -12,10 +13,6 @@ export interface ChatMessage { } export interface RootStore { - debugState: { - debugContext: SerializedDebugContext; - rangesMask: boolean[]; - }; config: { workspacePath: string | undefined; apiUrl: string; @@ -40,15 +37,16 @@ export interface RootStore { dialogMessage: string | JSX.Element; dialogEntryOn: boolean; }; + serverState: FullState; } const store = configureStore({ reducer: { - debugState: debugStateReducer, chat: chatReducer, config: configReducer, misc: miscReducer, uiState: uiStateReducer, + serverState: serverStateReducer, }, }); -- cgit v1.2.3-70-g09d2 From 8ada89b0f66f9e746394ee64591359537fe0c7f0 Mon Sep 17 00:00:00 2001 From: Nate Sesti Date: Sun, 6 Aug 2023 22:54:31 -0700 Subject: feat: :memo: note about where session data is stored --- extension/react-app/src/pages/history.tsx | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'extension/react-app/src/pages/history.tsx') diff --git a/extension/react-app/src/pages/history.tsx b/extension/react-app/src/pages/history.tsx index a553c6ae..c3240a66 100644 --- a/extension/react-app/src/pages/history.tsx +++ b/extension/react-app/src/pages/history.tsx @@ -87,6 +87,10 @@ function History() { ))}
+
+ + All session data is saved in ~/.continue/sessions +
); } -- cgit v1.2.3-70-g09d2