vzcode 1.44.0 → 1.46.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/{buildWorker-CRLuRnCr.js → buildWorker-C_tGhWXG.js} +135 -74
- package/dist/assets/index-CC0CUUDi.js +457 -0
- package/dist/assets/{index-Beqaj63x.css → index-CQyHWTWE.css} +1 -1
- package/dist/assets/worker-CekYspLa.js +453 -0
- package/dist/index.html +2 -2
- package/package.json +13 -7
- package/src/client/CodeEditor/{getOrCreateEditor.ts → getOrCreateEditor.tsx} +129 -1
- package/src/client/CodeEditor/index.tsx +11 -4
- package/src/client/Icons/SparklesSVG.tsx +9 -3
- package/src/client/RunCodeWidget/index.tsx +18 -15
- package/src/client/SplitPaneResizeContext.tsx +26 -108
- package/src/client/VZCodeContext.tsx +15 -1
- package/src/client/VZRight.tsx +32 -4
- package/src/client/VZSidebar/AIChat/ChatInput.tsx +80 -10
- package/src/client/VZSidebar/AIChat/DiffView.scss +154 -0
- package/src/client/VZSidebar/AIChat/DiffView.tsx +133 -0
- package/src/client/VZSidebar/AIChat/Message.tsx +24 -0
- package/src/client/VZSidebar/AIChat/MessageList.tsx +134 -12
- package/src/client/VZSidebar/AIChat/ThinkingScratchpad.tsx +37 -0
- package/src/client/VZSidebar/AIChat/index.tsx +155 -17
- package/src/client/VZSidebar/AIChat/styles.scss +223 -1
- package/src/client/VZSidebar/aiCopyPaste.ts +15 -2
- package/src/client/VZSidebar/index.tsx +10 -2
- package/src/client/bootstrap.ts +11 -1
- package/src/client/featureFlags.ts +2 -0
- package/src/client/useActions.ts +12 -0
- package/src/client/usePrettier/index.ts +1 -3
- package/src/client/vzReducer/aiChatReducer.ts +13 -0
- package/src/client/vzReducer/createInitialState.ts +1 -0
- package/src/client/vzReducer/index.ts +9 -0
- package/src/client/vzReducer/searchReducer.test.ts +44 -2
- package/src/runCode.ts +5 -17
- package/src/server/aiChatHandler/aiEditing.ts +56 -0
- package/src/server/aiChatHandler/chatOperations.ts +78 -1
- package/src/server/aiChatHandler/index.ts +33 -9
- package/src/server/aiChatHandler/llmStreaming.ts +101 -19
- package/src/server/computeInitialDocument.ts +25 -6
- package/src/server/generateAIResponse.ts +4 -0
- package/src/server/index.ts +83 -2
- package/src/utils/fileDiff.ts +114 -0
- package/dist/assets/index-C46Hp5ym.js +0 -340
- package/dist/assets/worker-DkYJN5WQ.js +0 -453
package/dist/index.html
CHANGED
|
@@ -20,8 +20,8 @@
|
|
|
20
20
|
href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap"
|
|
21
21
|
rel="stylesheet"
|
|
22
22
|
/>
|
|
23
|
-
<script type="module" crossorigin src="/assets/index-
|
|
24
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
23
|
+
<script type="module" crossorigin src="/assets/index-CC0CUUDi.js"></script>
|
|
24
|
+
<link rel="stylesheet" crossorigin href="/assets/index-CQyHWTWE.css">
|
|
25
25
|
</head>
|
|
26
26
|
<body>
|
|
27
27
|
<div id="root"></div>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vzcode",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.46.0",
|
|
4
4
|
"description": "Multiplayer code editor system",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"type": "module",
|
|
@@ -18,6 +18,9 @@
|
|
|
18
18
|
"build": "vite build",
|
|
19
19
|
"build-release": "vite build --mode release",
|
|
20
20
|
"preview": "concurrently \"npm run test-interactive\" \"vite preview\"",
|
|
21
|
+
"tauri": "tauri",
|
|
22
|
+
"tauri:dev": "tauri dev",
|
|
23
|
+
"tauri:build": "tauri build",
|
|
21
24
|
"prepublishOnly": "npm run build-release",
|
|
22
25
|
"upgrade": "ncu -x react,react-dom,@types/react,@types/react-dom -u"
|
|
23
26
|
},
|
|
@@ -109,7 +112,7 @@
|
|
|
109
112
|
"@codemirror/lang-html": "^6.4.9",
|
|
110
113
|
"@codemirror/lang-javascript": "^6.2.4",
|
|
111
114
|
"@codemirror/lang-json": "^6.0.2",
|
|
112
|
-
"@codemirror/lang-markdown": "^6.3.
|
|
115
|
+
"@codemirror/lang-markdown": "^6.3.4",
|
|
113
116
|
"@codemirror/lint": "^6.8.5",
|
|
114
117
|
"@codemirror/state": "^6.5.2",
|
|
115
118
|
"@codemirror/theme-one-dark": "^6.1.3",
|
|
@@ -134,9 +137,9 @@
|
|
|
134
137
|
"@uiw/codemirror-theme-xcode": "^4.24.2",
|
|
135
138
|
"@uiw/codemirror-themes": "^4.24.2",
|
|
136
139
|
"@valtown/codemirror-ts": "^2.3.1",
|
|
137
|
-
"@vizhub/runtime": "^4.
|
|
138
|
-
"@vizhub/viz-types": "^0.
|
|
139
|
-
"@vizhub/viz-utils": "^1.
|
|
140
|
+
"@vizhub/runtime": "^4.3.0",
|
|
141
|
+
"@vizhub/viz-types": "^0.4.0",
|
|
142
|
+
"@vizhub/viz-utils": "^1.4.0",
|
|
140
143
|
"body-parser": "^2.2.0",
|
|
141
144
|
"codemirror": "^6.0.2",
|
|
142
145
|
"codemirror-copilot": "^0.0.7",
|
|
@@ -144,7 +147,9 @@
|
|
|
144
147
|
"color-hash": "^2.0.2",
|
|
145
148
|
"comlink": "^4.4.2",
|
|
146
149
|
"d3-array": "^3.2.4",
|
|
150
|
+
"diff": "^8.0.2",
|
|
147
151
|
"diff-match-patch": "^1.0.5",
|
|
152
|
+
"diff2html": "^3.4.52",
|
|
148
153
|
"dotenv": "^17.2.1",
|
|
149
154
|
"editcodewithai": "^2.3.0",
|
|
150
155
|
"eslint-linter-browserify": "^9.32.0",
|
|
@@ -154,7 +159,7 @@
|
|
|
154
159
|
"jszip": "^3.10.1",
|
|
155
160
|
"livekit-server-sdk": "^2.13.1",
|
|
156
161
|
"llm-code-format": "^3.0.0",
|
|
157
|
-
"npm": "^11.5.
|
|
162
|
+
"npm": "^11.5.2",
|
|
158
163
|
"open": "^10.2.0",
|
|
159
164
|
"prettier-plugin-svelte": "^3.4.0",
|
|
160
165
|
"react": "^18",
|
|
@@ -171,6 +176,7 @@
|
|
|
171
176
|
},
|
|
172
177
|
"devDependencies": {
|
|
173
178
|
"@eslint/js": "^9.32.0",
|
|
179
|
+
"@tauri-apps/cli": "^2.7.1",
|
|
174
180
|
"@types/react": "^18",
|
|
175
181
|
"@types/react-dom": "^18",
|
|
176
182
|
"@vitejs/plugin-react": "^4.7.0",
|
|
@@ -182,7 +188,7 @@
|
|
|
182
188
|
"prettier": "^3.6.2",
|
|
183
189
|
"sass": "^1.89.2",
|
|
184
190
|
"ts-node": "^10.9.2",
|
|
185
|
-
"typescript": "^5.
|
|
191
|
+
"typescript": "^5.9.2",
|
|
186
192
|
"vite": "^7.0.6",
|
|
187
193
|
"vitest": "^3.2.4"
|
|
188
194
|
},
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { createRoot } from 'react-dom/client';
|
|
1
2
|
import { EditorView } from 'codemirror';
|
|
2
3
|
import {
|
|
3
4
|
Compartment,
|
|
@@ -43,7 +44,17 @@ import {
|
|
|
43
44
|
EditorCacheValue,
|
|
44
45
|
} from '../useEditorCache';
|
|
45
46
|
import { ThemeLabel, themeOptionsByLabel } from '../themes';
|
|
46
|
-
import {
|
|
47
|
+
import {
|
|
48
|
+
keymap,
|
|
49
|
+
Decoration,
|
|
50
|
+
WidgetType,
|
|
51
|
+
ViewUpdate,
|
|
52
|
+
ViewPlugin,
|
|
53
|
+
DecorationSet,
|
|
54
|
+
} from '@codemirror/view';
|
|
55
|
+
import { Range } from '@codemirror/state';
|
|
56
|
+
import { syntaxTree } from '@codemirror/language';
|
|
57
|
+
import { RegExpCursor } from '@codemirror/search';
|
|
47
58
|
import { basicSetup } from './basicSetup';
|
|
48
59
|
import { InteractRule } from '@replit/codemirror-interact';
|
|
49
60
|
import rainbowBrackets from './rainbowBrackets';
|
|
@@ -60,6 +71,9 @@ import {
|
|
|
60
71
|
tsSyncWorker,
|
|
61
72
|
} from '@valtown/codemirror-ts';
|
|
62
73
|
import { getFileExtension } from '../utils/fileExtension';
|
|
74
|
+
import { SparklesSVG } from '../Icons/SparklesSVG';
|
|
75
|
+
import { VZCodeContext } from '../VZCodeContext';
|
|
76
|
+
import { useContext, useMemo } from 'react';
|
|
63
77
|
|
|
64
78
|
const DEBUG = false;
|
|
65
79
|
|
|
@@ -178,6 +192,8 @@ export const getOrCreateEditor = async ({
|
|
|
178
192
|
aiCopilotEndpoint,
|
|
179
193
|
esLintSource,
|
|
180
194
|
rainbowBracketsEnabled = true,
|
|
195
|
+
setIsAIChatOpen,
|
|
196
|
+
setAIChatMessage,
|
|
181
197
|
}: {
|
|
182
198
|
// TODO pass this in from the outside
|
|
183
199
|
paneId?: PaneId;
|
|
@@ -213,6 +229,8 @@ export const getOrCreateEditor = async ({
|
|
|
213
229
|
view: EditorView,
|
|
214
230
|
) => Promise<readonly Diagnostic[]>;
|
|
215
231
|
rainbowBracketsEnabled?: boolean; // New parameter type
|
|
232
|
+
setIsAIChatOpen: any; // TODO fix types
|
|
233
|
+
setAIChatMessage: any; // TODO fix types
|
|
216
234
|
}): Promise<ExtendedEditorCacheValue> => {
|
|
217
235
|
// Cache hit
|
|
218
236
|
|
|
@@ -454,6 +472,116 @@ export const getOrCreateEditor = async ({
|
|
|
454
472
|
extensions.push(copilot({ aiCopilotEndpoint }));
|
|
455
473
|
}
|
|
456
474
|
|
|
475
|
+
// const { setIsAIChatOpen } = useContext(VZCodeContext);
|
|
476
|
+
|
|
477
|
+
// Widget appears after instances of "todo" in code editor, allowing AI to implement todo tasks when clicked
|
|
478
|
+
function createToDoPlugin() {
|
|
479
|
+
// setIsAIChatOpen: (open: boolean) => void,
|
|
480
|
+
class ToDoWidget extends WidgetType {
|
|
481
|
+
constructor() {
|
|
482
|
+
super();
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
eq(other: ToDoWidget) {
|
|
486
|
+
return false;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
toDOM() {
|
|
490
|
+
const wrap = document.createElement('i');
|
|
491
|
+
wrap.style.display = 'inline-flex'; // prevent block expansion
|
|
492
|
+
wrap.style.alignItems = 'center';
|
|
493
|
+
wrap.style.justifyContent = 'center';
|
|
494
|
+
wrap.className = 'icon-button icon-button-dark';
|
|
495
|
+
const reactContainer =
|
|
496
|
+
document.createElement('div');
|
|
497
|
+
wrap.appendChild(reactContainer);
|
|
498
|
+
|
|
499
|
+
const root = createRoot(reactContainer);
|
|
500
|
+
root.render(
|
|
501
|
+
<div
|
|
502
|
+
onClick={() => {
|
|
503
|
+
console.log(
|
|
504
|
+
'TODO set the chat prompt to "Implement the TODO"',
|
|
505
|
+
);
|
|
506
|
+
setIsAIChatOpen(true);
|
|
507
|
+
setAIChatMessage('Implement the TODO');
|
|
508
|
+
// submitAIChatMessage()
|
|
509
|
+
}}
|
|
510
|
+
>
|
|
511
|
+
<SparklesSVG width={14} height={14} />
|
|
512
|
+
</div>,
|
|
513
|
+
);
|
|
514
|
+
return wrap;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
ignoreEvent() {
|
|
518
|
+
return false;
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
function toDoWidgets(editor: EditorView) {
|
|
523
|
+
const { state } = editor;
|
|
524
|
+
const tree = syntaxTree(state);
|
|
525
|
+
const findToDo = new RegExpCursor(state.doc, 'todo', {
|
|
526
|
+
ignoreCase: true,
|
|
527
|
+
});
|
|
528
|
+
const widgets: Range<Decoration>[] = [];
|
|
529
|
+
|
|
530
|
+
while (!findToDo.next().done) {
|
|
531
|
+
const { from, to } = findToDo.value;
|
|
532
|
+
const node = tree.resolve(from);
|
|
533
|
+
let inComment = false;
|
|
534
|
+
|
|
535
|
+
// Walk up the parents until we hit the root, looking for a comment node
|
|
536
|
+
for (let cur: any = node; cur; cur = cur.parent) {
|
|
537
|
+
if (
|
|
538
|
+
cur.type.is('Comment') || // grammars that group comments
|
|
539
|
+
cur.type.is('comment') || // generic lowercase group
|
|
540
|
+
/Comment$/.test(cur.type.name) // fallback for e.g. LineComment
|
|
541
|
+
) {
|
|
542
|
+
inComment = true;
|
|
543
|
+
break;
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
if (!inComment) continue; // skip non-comment TODOs
|
|
547
|
+
|
|
548
|
+
const deco = Decoration.widget({
|
|
549
|
+
widget: new ToDoWidget(),
|
|
550
|
+
side: 1,
|
|
551
|
+
}).range(to);
|
|
552
|
+
widgets.push(deco);
|
|
553
|
+
}
|
|
554
|
+
return Decoration.set(widgets);
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
const todoPlugin = ViewPlugin.fromClass(
|
|
558
|
+
class {
|
|
559
|
+
decorations: DecorationSet;
|
|
560
|
+
|
|
561
|
+
constructor(view: EditorView) {
|
|
562
|
+
this.decorations = toDoWidgets(view);
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
update(update: ViewUpdate) {
|
|
566
|
+
if (
|
|
567
|
+
update.docChanged ||
|
|
568
|
+
update.viewportChanged ||
|
|
569
|
+
syntaxTree(update.startState) !==
|
|
570
|
+
syntaxTree(update.state)
|
|
571
|
+
)
|
|
572
|
+
this.decorations = toDoWidgets(update.view);
|
|
573
|
+
}
|
|
574
|
+
},
|
|
575
|
+
{
|
|
576
|
+
decorations: (v) => v.decorations,
|
|
577
|
+
},
|
|
578
|
+
);
|
|
579
|
+
|
|
580
|
+
return todoPlugin;
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
extensions.push(createToDoPlugin());
|
|
584
|
+
|
|
457
585
|
const editor = new EditorView({
|
|
458
586
|
state: EditorState.create({
|
|
459
587
|
doc: text,
|
|
@@ -43,7 +43,8 @@ export const CodeEditor = ({
|
|
|
43
43
|
theme,
|
|
44
44
|
codeEditorRef,
|
|
45
45
|
enableAutoFollow,
|
|
46
|
-
|
|
46
|
+
setIsAIChatOpen,
|
|
47
|
+
setAIChatMessage,
|
|
47
48
|
} = useContext(VZCodeContext);
|
|
48
49
|
|
|
49
50
|
// Set `doc.data.isInteracting` to `true` when the user is interacting
|
|
@@ -61,13 +62,17 @@ export const CodeEditor = ({
|
|
|
61
62
|
clearTimeout(interactTimeoutRef.current);
|
|
62
63
|
}
|
|
63
64
|
|
|
64
|
-
// Set `isInteracting: undefined` after a delay
|
|
65
|
+
// Set `isInteracting: undefined` after a delay,
|
|
66
|
+
// so that when the runId changes in the future,
|
|
67
|
+
// it will trigger a hard re-run, not a hot reload.
|
|
65
68
|
interactTimeoutRef.current = setTimeout(() => {
|
|
66
69
|
interactTimeoutRef.current = null;
|
|
67
70
|
|
|
68
|
-
// This logic deletes the `isInteracting` property from the document
|
|
71
|
+
// This logic deletes the `isInteracting` property from the document
|
|
69
72
|
submitOperation(
|
|
70
|
-
({ isInteracting, ...newDocument }) =>
|
|
73
|
+
({ isInteracting, ...newDocument }) => ({
|
|
74
|
+
...newDocument,
|
|
75
|
+
}),
|
|
71
76
|
);
|
|
72
77
|
}, 800);
|
|
73
78
|
}, [submitOperation]);
|
|
@@ -109,6 +114,8 @@ export const CodeEditor = ({
|
|
|
109
114
|
enableAutoFollowRef,
|
|
110
115
|
aiCopilotEndpoint,
|
|
111
116
|
esLintSource,
|
|
117
|
+
setIsAIChatOpen,
|
|
118
|
+
setAIChatMessage,
|
|
112
119
|
});
|
|
113
120
|
|
|
114
121
|
if (isMounted) {
|
|
@@ -1,8 +1,14 @@
|
|
|
1
|
-
export const SparklesSVG = (
|
|
1
|
+
export const SparklesSVG = ({
|
|
2
|
+
width = '32',
|
|
3
|
+
height = '32',
|
|
4
|
+
}: {
|
|
5
|
+
width?: string | number;
|
|
6
|
+
height?: string | number;
|
|
7
|
+
}) => (
|
|
2
8
|
<svg
|
|
3
9
|
xmlns="http://www.w3.org/2000/svg"
|
|
4
|
-
width=
|
|
5
|
-
height=
|
|
10
|
+
width={width}
|
|
11
|
+
height={height}
|
|
6
12
|
viewBox="0 0 24 24"
|
|
7
13
|
fill="currentColor"
|
|
8
14
|
stroke="currentColor"
|
|
@@ -28,24 +28,27 @@ export const RunCodeWidget = ({
|
|
|
28
28
|
useContext(VZCodeContext);
|
|
29
29
|
const [isRunning, setIsRunning] = useState(false);
|
|
30
30
|
|
|
31
|
-
const handleClick = useCallback(
|
|
32
|
-
|
|
31
|
+
const handleClick = useCallback(
|
|
32
|
+
(event?: React.MouseEvent) => {
|
|
33
|
+
setIsRunning(true); // Set the running state to true
|
|
33
34
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
35
|
+
// Run Prettier
|
|
36
|
+
const runPrettier = runPrettierRef.current;
|
|
37
|
+
if (runPrettier !== null) {
|
|
38
|
+
runPrettier();
|
|
39
|
+
}
|
|
39
40
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
41
|
+
// Run the code
|
|
42
|
+
const runCode = runCodeRef.current;
|
|
43
|
+
if (runCode !== null) {
|
|
44
|
+
runCode();
|
|
45
|
+
}
|
|
45
46
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
47
|
+
// Optional: reset the icon state after animation completes (e.g., 1 second)
|
|
48
|
+
setTimeout(() => setIsRunning(false), 1000);
|
|
49
|
+
},
|
|
50
|
+
[runCodeRef, runPrettierRef],
|
|
51
|
+
);
|
|
49
52
|
|
|
50
53
|
const handleSplitEditor = useCallback(() => {
|
|
51
54
|
splitCurrentPane();
|
|
@@ -2,9 +2,8 @@
|
|
|
2
2
|
import {
|
|
3
3
|
createContext,
|
|
4
4
|
useReducer,
|
|
5
|
-
useEffect,
|
|
6
5
|
useCallback,
|
|
7
|
-
|
|
6
|
+
useEffect,
|
|
8
7
|
} from 'react';
|
|
9
8
|
|
|
10
9
|
export type Side = 'left' | 'right';
|
|
@@ -16,63 +15,22 @@ export type SplitPaneResizeContextValue = {
|
|
|
16
15
|
isDraggingRight: boolean;
|
|
17
16
|
isDraggingLeft: boolean;
|
|
18
17
|
setIsDragging: (a: boolean, side: Side) => void;
|
|
18
|
+
setSidebarView: (isAIChatOpen: boolean) => void;
|
|
19
19
|
};
|
|
20
20
|
|
|
21
|
-
//
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
// The amount of time to wait idle before writing to localStorage.
|
|
25
|
-
// This is to avoid writing to localStorage on every resize event.
|
|
26
|
-
// MS = milliseconds
|
|
27
|
-
const localStorageWriteDebounceMS = 800;
|
|
28
|
-
|
|
29
|
-
// Properties in LocalStorage
|
|
30
|
-
const sidebarWidthProperty = 'vzCodeSidebarWidth';
|
|
31
|
-
const codeEditorWidthProperty = 'vzCodeCodeEditorWidth';
|
|
32
|
-
|
|
33
|
-
const initialSidebarWidthDefault = 250;
|
|
34
|
-
let initialSidebarWidth: number =
|
|
35
|
-
initialSidebarWidthDefault;
|
|
36
|
-
|
|
21
|
+
// Default widths for different sidebar views
|
|
22
|
+
const filesViewWidth = 300;
|
|
23
|
+
const aiChatViewWidth = 600;
|
|
37
24
|
const initialCodeEditorWidthDefault = 800;
|
|
38
|
-
let initialCodeEditorWidth: number =
|
|
39
|
-
initialCodeEditorWidthDefault;
|
|
40
|
-
|
|
41
|
-
// If no value is found in localStorage, we'll use the default.
|
|
42
|
-
let needsInitialization = false;
|
|
43
|
-
|
|
44
|
-
// If we're in the browser,
|
|
45
|
-
if (typeof window !== 'undefined' && enableLocalStorage) {
|
|
46
|
-
//check localStorage for a previously stored width.
|
|
47
|
-
const sidebarWidthLocal: string | null =
|
|
48
|
-
window.localStorage.getItem(sidebarWidthProperty);
|
|
49
|
-
|
|
50
|
-
const codeEditorWidthLocal: string | null =
|
|
51
|
-
window.localStorage.getItem(codeEditorWidthProperty);
|
|
52
|
-
|
|
53
|
-
// If there is a previously stored width,
|
|
54
|
-
if (sidebarWidthLocal !== null) {
|
|
55
|
-
// use it as the initial width.
|
|
56
|
-
initialSidebarWidth = +sidebarWidthLocal;
|
|
57
|
-
needsInitialization = true;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
if (codeEditorWidthLocal !== null) {
|
|
61
|
-
// use it as the initial width.
|
|
62
|
-
initialCodeEditorWidth = +codeEditorWidthLocal;
|
|
63
|
-
needsInitialization = true;
|
|
64
|
-
}
|
|
65
|
-
} else {
|
|
66
|
-
// If we're not in the browser, use the default initial width.
|
|
67
|
-
}
|
|
68
25
|
|
|
69
26
|
const initialValue: SplitPaneResizeContextValue = {
|
|
70
|
-
codeEditorWidth:
|
|
71
|
-
sidebarWidth:
|
|
27
|
+
codeEditorWidth: initialCodeEditorWidthDefault,
|
|
28
|
+
sidebarWidth: filesViewWidth, // Default to files view
|
|
72
29
|
moveSplitPane: () => {},
|
|
73
30
|
isDraggingRight: false,
|
|
74
31
|
isDraggingLeft: false,
|
|
75
32
|
setIsDragging: () => {},
|
|
33
|
+
setSidebarView: () => {},
|
|
76
34
|
};
|
|
77
35
|
|
|
78
36
|
export const SplitPaneResizeContext =
|
|
@@ -84,30 +42,13 @@ type SplitPaneState = {
|
|
|
84
42
|
codeEditorWidth: number;
|
|
85
43
|
isDraggingLeft: boolean;
|
|
86
44
|
isDraggingRight: boolean;
|
|
87
|
-
|
|
88
|
-
// False on first render
|
|
89
|
-
isInitialized: boolean;
|
|
90
45
|
};
|
|
91
46
|
|
|
92
47
|
const initialState: SplitPaneState = {
|
|
93
|
-
|
|
94
|
-
// codeEditorWidth: initialCodeEditorWidth,
|
|
95
|
-
|
|
96
|
-
// These need to match between SSR and the first
|
|
97
|
-
// client-side render (hydration), to avoid
|
|
98
|
-
// React hydration errors like this one:
|
|
99
|
-
// * Warning: Prop `style` did not match.
|
|
100
|
-
// * Server: "left:210px;width:20px"
|
|
101
|
-
// * Client: "left:218px;width:20px"
|
|
102
|
-
|
|
103
|
-
sidebarWidth: initialSidebarWidthDefault,
|
|
48
|
+
sidebarWidth: filesViewWidth,
|
|
104
49
|
codeEditorWidth: initialCodeEditorWidthDefault,
|
|
105
50
|
isDraggingLeft: false,
|
|
106
51
|
isDraggingRight: false,
|
|
107
|
-
|
|
108
|
-
// Solution for hydration errors: we set the value from localStorage
|
|
109
|
-
// in a layoutEffect that happens after first render.
|
|
110
|
-
isInitialized: false,
|
|
111
52
|
};
|
|
112
53
|
|
|
113
54
|
type SplitPaneAction =
|
|
@@ -122,7 +63,8 @@ type SplitPaneAction =
|
|
|
122
63
|
isDragging: boolean;
|
|
123
64
|
}
|
|
124
65
|
| {
|
|
125
|
-
type: '
|
|
66
|
+
type: 'setSidebarView';
|
|
67
|
+
isAIChatOpen: boolean;
|
|
126
68
|
};
|
|
127
69
|
|
|
128
70
|
const splitPaneReducer = (
|
|
@@ -130,13 +72,6 @@ const splitPaneReducer = (
|
|
|
130
72
|
action: SplitPaneAction,
|
|
131
73
|
) => {
|
|
132
74
|
switch (action.type) {
|
|
133
|
-
case 'initialize':
|
|
134
|
-
return {
|
|
135
|
-
...state,
|
|
136
|
-
sidebarWidth: initialSidebarWidth,
|
|
137
|
-
codeEditorWidth: initialCodeEditorWidth,
|
|
138
|
-
isInitialized: true,
|
|
139
|
-
};
|
|
140
75
|
case 'move':
|
|
141
76
|
return action.side === 'left'
|
|
142
77
|
? {
|
|
@@ -160,6 +95,13 @@ const splitPaneReducer = (
|
|
|
160
95
|
...state,
|
|
161
96
|
isDraggingRight: action.isDragging,
|
|
162
97
|
};
|
|
98
|
+
case 'setSidebarView':
|
|
99
|
+
return {
|
|
100
|
+
...state,
|
|
101
|
+
sidebarWidth: action.isAIChatOpen
|
|
102
|
+
? aiChatViewWidth
|
|
103
|
+
: filesViewWidth,
|
|
104
|
+
};
|
|
163
105
|
default:
|
|
164
106
|
return state;
|
|
165
107
|
}
|
|
@@ -185,45 +127,20 @@ export const SplitPaneResizeProvider = ({ children }) => {
|
|
|
185
127
|
[dispatch],
|
|
186
128
|
);
|
|
187
129
|
|
|
130
|
+
const setSidebarView = useCallback(
|
|
131
|
+
(isAIChatOpen: boolean) => {
|
|
132
|
+
dispatch({ type: 'setSidebarView', isAIChatOpen });
|
|
133
|
+
},
|
|
134
|
+
[dispatch],
|
|
135
|
+
);
|
|
136
|
+
|
|
188
137
|
const {
|
|
189
138
|
sidebarWidth,
|
|
190
139
|
codeEditorWidth,
|
|
191
140
|
isDraggingLeft,
|
|
192
141
|
isDraggingRight,
|
|
193
|
-
isInitialized,
|
|
194
142
|
} = state;
|
|
195
143
|
|
|
196
|
-
// Logic around initializing from localStorage.
|
|
197
|
-
// Only run this on the client.
|
|
198
|
-
if (typeof window !== 'undefined') {
|
|
199
|
-
useLayoutEffect(() => {
|
|
200
|
-
if (needsInitialization && !isInitialized) {
|
|
201
|
-
dispatch({ type: 'initialize' });
|
|
202
|
-
}
|
|
203
|
-
}, [isInitialized]);
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
// Logic around storing the values in localStorage.
|
|
207
|
-
useEffect(() => {
|
|
208
|
-
if (sidebarWidth !== initialSidebarWidth) {
|
|
209
|
-
setTimeout(() => {
|
|
210
|
-
window.localStorage.setItem(
|
|
211
|
-
sidebarWidthProperty,
|
|
212
|
-
'' + sidebarWidth,
|
|
213
|
-
);
|
|
214
|
-
}, localStorageWriteDebounceMS);
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
if (codeEditorWidth !== initialCodeEditorWidth) {
|
|
218
|
-
setTimeout(() => {
|
|
219
|
-
window.localStorage.setItem(
|
|
220
|
-
codeEditorWidthProperty,
|
|
221
|
-
'' + codeEditorWidth,
|
|
222
|
-
);
|
|
223
|
-
}, localStorageWriteDebounceMS);
|
|
224
|
-
}
|
|
225
|
-
}, [codeEditorWidth, sidebarWidth]);
|
|
226
|
-
|
|
227
144
|
const value: SplitPaneResizeContextValue = {
|
|
228
145
|
codeEditorWidth,
|
|
229
146
|
sidebarWidth,
|
|
@@ -231,6 +148,7 @@ export const SplitPaneResizeProvider = ({ children }) => {
|
|
|
231
148
|
isDraggingLeft,
|
|
232
149
|
isDraggingRight,
|
|
233
150
|
setIsDragging,
|
|
151
|
+
setSidebarView,
|
|
234
152
|
};
|
|
235
153
|
|
|
236
154
|
return (
|
|
@@ -126,6 +126,8 @@ export type VZCodeContextValue = {
|
|
|
126
126
|
setIsAIChatOpen: (isAIChatOpen: boolean) => void;
|
|
127
127
|
aiChatFocused: boolean;
|
|
128
128
|
toggleAIChatFocused: () => void;
|
|
129
|
+
aiChatMode: 'ask' | 'edit';
|
|
130
|
+
setAIChatMode: (mode: 'ask' | 'edit') => void;
|
|
129
131
|
aiChatEndpoint?: string;
|
|
130
132
|
aiChatOptions?: { [key: string]: any };
|
|
131
133
|
setSearchResults: (files: ShareDBDoc<VizContent>) => void;
|
|
@@ -158,7 +160,9 @@ export type VZCodeContextValue = {
|
|
|
158
160
|
runPrettierRef: React.MutableRefObject<
|
|
159
161
|
null | (() => void)
|
|
160
162
|
>;
|
|
161
|
-
runCodeRef: React.MutableRefObject<
|
|
163
|
+
runCodeRef: React.MutableRefObject<
|
|
164
|
+
null | ((hardRerun?: boolean) => void)
|
|
165
|
+
>;
|
|
162
166
|
|
|
163
167
|
sidebarRef: React.RefObject<HTMLDivElement>;
|
|
164
168
|
|
|
@@ -188,6 +192,8 @@ export type VZCodeContextValue = {
|
|
|
188
192
|
setLiveKitConnection: (state: boolean) => void;
|
|
189
193
|
voiceChatModalOpen: boolean;
|
|
190
194
|
setVoiceChatModalOpen: (state: boolean) => void;
|
|
195
|
+
aiChatMessage: string;
|
|
196
|
+
setAIChatMessage: (message: string) => void;
|
|
191
197
|
};
|
|
192
198
|
|
|
193
199
|
export const VZCodeProvider = ({
|
|
@@ -315,6 +321,7 @@ export const VZCodeProvider = ({
|
|
|
315
321
|
toggleSearchFocused,
|
|
316
322
|
setIsAIChatOpen,
|
|
317
323
|
toggleAIChatFocused,
|
|
324
|
+
setAIChatMode,
|
|
318
325
|
setIsSettingsOpen,
|
|
319
326
|
setIsDocOpen,
|
|
320
327
|
closeSettings,
|
|
@@ -442,6 +449,8 @@ export const VZCodeProvider = ({
|
|
|
442
449
|
const [voiceChatModalOpen, setVoiceChatModalOpen] =
|
|
443
450
|
useState(false);
|
|
444
451
|
|
|
452
|
+
const [aiChatMessage, setAIChatMessage] = useState('');
|
|
453
|
+
|
|
445
454
|
// The value provided by this context.
|
|
446
455
|
const value: VZCodeContextValue = {
|
|
447
456
|
content,
|
|
@@ -485,6 +494,8 @@ export const VZCodeProvider = ({
|
|
|
485
494
|
setIsAIChatOpen,
|
|
486
495
|
aiChatFocused,
|
|
487
496
|
toggleAIChatFocused,
|
|
497
|
+
aiChatMode: state.aiChatMode,
|
|
498
|
+
setAIChatMode,
|
|
488
499
|
aiChatEndpoint,
|
|
489
500
|
aiChatOptions,
|
|
490
501
|
|
|
@@ -546,6 +557,9 @@ export const VZCodeProvider = ({
|
|
|
546
557
|
setLiveKitConnection,
|
|
547
558
|
voiceChatModalOpen,
|
|
548
559
|
setVoiceChatModalOpen,
|
|
560
|
+
|
|
561
|
+
aiChatMessage,
|
|
562
|
+
setAIChatMessage,
|
|
549
563
|
};
|
|
550
564
|
|
|
551
565
|
return (
|