summaryrefslogtreecommitdiff
path: root/extension/react-app
diff options
context:
space:
mode:
authorNate Sesti <sestinj@gmail.com>2023-08-06 22:51:10 -0700
committerNate Sesti <sestinj@gmail.com>2023-08-06 22:51:10 -0700
commit6bce4b2802d06818f33b260ca4353331ea4b2df9 (patch)
tree1aafefb3645707b8fdebe5d8acee7c94eebda041 /extension/react-app
parentf96eecb0a531eaef86a503c216882f196ed9299e (diff)
downloadsncontinue-6bce4b2802d06818f33b260ca4353331ea4b2df9.tar.gz
sncontinue-6bce4b2802d06818f33b260ca4353331ea4b2df9.tar.bz2
sncontinue-6bce4b2802d06818f33b260ca4353331ea4b2df9.zip
refactor: :recycle: hoist FullState up to redux
Diffstat (limited to 'extension/react-app')
-rw-r--r--extension/react-app/src/App.tsx2
-rw-r--r--extension/react-app/src/pages/gui.tsx75
-rw-r--r--extension/react-app/src/pages/history.tsx13
-rw-r--r--extension/react-app/src/redux/hooks.ts21
-rw-r--r--extension/react-app/src/redux/selectors/debugContextSelectors.ts29
-rw-r--r--extension/react-app/src/redux/slices/debugContexSlice.ts149
-rw-r--r--extension/react-app/src/redux/slices/serverStateReducer.ts53
-rw-r--r--extension/react-app/src/redux/store.ts12
8 files changed, 102 insertions, 252 deletions
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<string[]>([]);
- 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<History | undefined>({
- 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 && <Loader />}
<div>
- {userInputQueue.map((input) => {
+ {user_input_queue.map((input) => {
return <UserInputQueueItem>{input}</UserInputQueueItem>;
})}
</div>
@@ -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}
/>
<ContinueButton onClick={onMainTextInput} />
</div>
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() {
}}
>
<div className="text-lg">{session.title}</div>
- <div className="text-gray-400">{session.date_created}</div>
+ <div className="text-gray-400">
+ {new Date(
+ parseInt(session.date_created) * 1000
+ ).toLocaleString("en-US", {
+ weekday: "short",
+ year: "numeric",
+ month: "long",
+ day: "numeric",
+ hour: "numeric",
+ minute: "numeric",
+ })}
+ </div>
</TdDiv>
</td>
</Tr>
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,
},
});