vzcode 1.38.0 → 1.39.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/{index-Cw7RI1uP.js → index-0y6ZxgSv.js} +91 -91
- package/dist/index.html +1 -1
- package/package.json +6 -5
- package/src/client/CodeEditor/getOrCreateEditor.ts +0 -3
- package/src/client/CodeEditor/index.tsx +0 -1
- package/src/client/CodeEditor/json1PresenceDisplay.ts +4 -18
- package/src/client/VZCodeContext.tsx +11 -0
- package/src/client/VZSidebar/index.tsx +6 -0
- package/src/client/useKeyboardShortcuts.ts +7 -1
- package/src/client/usePresenceAutoFollow.ts +86 -0
- package/src/client/useRunCode.tsx +2 -16
- package/src/client/useSubmitOperation.ts +4 -11
- package/src/runCode.js +48 -0
- package/src/runCode.ts +58 -0
- package/src/server/aiChatHandler/aiEditing.js +32 -30
- package/src/server/aiChatHandler/chatOperations.js +132 -7
- package/src/server/aiChatHandler/index.js +17 -23
- package/src/server/aiChatHandler/llmStreaming.js +144 -32
- package/src/server/index.js +19 -9
- package/src/submitOperation.js +16 -0
- package/src/submitOperation.ts +25 -0
package/dist/index.html
CHANGED
|
@@ -20,7 +20,7 @@
|
|
|
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-
|
|
23
|
+
<script type="module" crossorigin src="/assets/index-0y6ZxgSv.js"></script>
|
|
24
24
|
<link rel="stylesheet" crossorigin href="/assets/index-DKLiE98a.css">
|
|
25
25
|
</head>
|
|
26
26
|
<body>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vzcode",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.39.0",
|
|
4
4
|
"description": "Multiplayer code editor system",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"type": "module",
|
|
@@ -139,21 +139,19 @@
|
|
|
139
139
|
"body-parser": "^2.2.0",
|
|
140
140
|
"codemirror": "^6.0.2",
|
|
141
141
|
"codemirror-copilot": "^0.0.7",
|
|
142
|
-
"codemirror-ot": "^4.
|
|
142
|
+
"codemirror-ot": "^4.5.0",
|
|
143
143
|
"color-hash": "^2.0.2",
|
|
144
144
|
"comlink": "^4.4.2",
|
|
145
145
|
"d3-array": "^3.2.4",
|
|
146
146
|
"diff-match-patch": "^1.0.5",
|
|
147
147
|
"dotenv": "^17.2.0",
|
|
148
148
|
"editcodewithai": "^2.2.0",
|
|
149
|
-
"eslint": "^9.31.0",
|
|
150
149
|
"eslint-linter-browserify": "^9.31.0",
|
|
151
150
|
"express": "^5.1.0",
|
|
152
|
-
"globals": "^16.3.0",
|
|
153
151
|
"ignore": "^7.0.5",
|
|
154
152
|
"json0-ot-diff": "^1.1.2",
|
|
155
153
|
"livekit-server-sdk": "^2.13.1",
|
|
156
|
-
"llm-code-format": "^2.0
|
|
154
|
+
"llm-code-format": "^2.1.0",
|
|
157
155
|
"ngrok": "^5.0.0-beta.2",
|
|
158
156
|
"npm": "^11.4.2",
|
|
159
157
|
"open": "^10.2.0",
|
|
@@ -171,11 +169,14 @@
|
|
|
171
169
|
"ws": "^8.18.3"
|
|
172
170
|
},
|
|
173
171
|
"devDependencies": {
|
|
172
|
+
"@eslint/js": "^9.31.0",
|
|
174
173
|
"@types/react": "^18",
|
|
175
174
|
"@types/react-dom": "^18",
|
|
176
175
|
"@vitejs/plugin-react": "^4.7.0",
|
|
177
176
|
"concurrently": "^9.2.0",
|
|
178
177
|
"cross-env": "^7.0.3",
|
|
178
|
+
"eslint": "^9.31.0",
|
|
179
|
+
"globals": "^16.3.0",
|
|
179
180
|
"npm-check-updates": "^18.0.1",
|
|
180
181
|
"prettier": "^3.6.2",
|
|
181
182
|
"sass": "^1.89.2",
|
|
@@ -176,7 +176,6 @@ export const getOrCreateEditor = async ({
|
|
|
176
176
|
usernameRef,
|
|
177
177
|
customInteractRules,
|
|
178
178
|
enableAutoFollowRef,
|
|
179
|
-
openTab,
|
|
180
179
|
aiCopilotEndpoint,
|
|
181
180
|
esLintSource,
|
|
182
181
|
rainbowBracketsEnabled = true,
|
|
@@ -213,7 +212,6 @@ export const getOrCreateEditor = async ({
|
|
|
213
212
|
// Ref to a boolean that determines whether to
|
|
214
213
|
// enable auto-following the cursors of remote users.
|
|
215
214
|
enableAutoFollowRef: React.MutableRefObject<boolean>;
|
|
216
|
-
openTab: (tabState: TabState) => void;
|
|
217
215
|
aiCopilotEndpoint?: string;
|
|
218
216
|
esLintSource: (
|
|
219
217
|
view: EditorView,
|
|
@@ -285,7 +283,6 @@ export const getOrCreateEditor = async ({
|
|
|
285
283
|
path: textPath,
|
|
286
284
|
docPresence,
|
|
287
285
|
enableAutoFollowRef,
|
|
288
|
-
openTab,
|
|
289
286
|
}),
|
|
290
287
|
);
|
|
291
288
|
}
|
|
@@ -6,20 +6,14 @@ import {
|
|
|
6
6
|
} from '@codemirror/view';
|
|
7
7
|
import { Annotation, RangeSet } from '@codemirror/state';
|
|
8
8
|
import { assignUserColor } from '../presenceColor';
|
|
9
|
-
import { VizFileId } from '@vizhub/viz-types';
|
|
10
9
|
import {
|
|
11
10
|
Presence,
|
|
12
11
|
PresenceId,
|
|
13
|
-
TabState,
|
|
14
12
|
Username,
|
|
15
13
|
} from '../../types';
|
|
16
14
|
|
|
17
|
-
const
|
|
15
|
+
const DEBUG = true;
|
|
18
16
|
|
|
19
|
-
// export let enableAutoFollow = false;
|
|
20
|
-
// export const toggleAutoFollowButton = () => {
|
|
21
|
-
// enableAutoFollow = !enableAutoFollow;
|
|
22
|
-
// };
|
|
23
17
|
// Deals with receiving the broadcasted presence cursor locations
|
|
24
18
|
// from other clients and displaying them.
|
|
25
19
|
//
|
|
@@ -32,12 +26,10 @@ export const json1PresenceDisplay = ({
|
|
|
32
26
|
path,
|
|
33
27
|
docPresence,
|
|
34
28
|
enableAutoFollowRef,
|
|
35
|
-
openTab,
|
|
36
29
|
}: {
|
|
37
30
|
path: Array<string>;
|
|
38
31
|
docPresence: any;
|
|
39
32
|
enableAutoFollowRef: React.MutableRefObject<boolean>;
|
|
40
|
-
openTab: (tabState: TabState) => void;
|
|
41
33
|
}) => [
|
|
42
34
|
ViewPlugin.fromClass(
|
|
43
35
|
class {
|
|
@@ -68,7 +60,7 @@ export const json1PresenceDisplay = ({
|
|
|
68
60
|
docPresence.on(
|
|
69
61
|
'receive',
|
|
70
62
|
(id: PresenceId, presence: Presence) => {
|
|
71
|
-
if (
|
|
63
|
+
if (DEBUG) {
|
|
72
64
|
console.log(
|
|
73
65
|
`Received presence for id ${id}`,
|
|
74
66
|
presence,
|
|
@@ -94,14 +86,8 @@ export const json1PresenceDisplay = ({
|
|
|
94
86
|
// Otherwise, delete the presence state.
|
|
95
87
|
delete presenceState[id];
|
|
96
88
|
|
|
97
|
-
//
|
|
98
|
-
// in
|
|
99
|
-
if (enableAutoFollowRef.current) {
|
|
100
|
-
openTab({
|
|
101
|
-
fileId: presence.start[1] as VizFileId,
|
|
102
|
-
isTransient: true,
|
|
103
|
-
});
|
|
104
|
-
}
|
|
89
|
+
// Note: Auto-follow tab opening is now handled by usePresenceAutoFollow hook
|
|
90
|
+
// in VZCodeContext, which works independently of CodeMirror extensions
|
|
105
91
|
}
|
|
106
92
|
// Update decorations to reflect new presence state.
|
|
107
93
|
// TODO consider mutating this rather than recomputing it on each change.
|
|
@@ -37,6 +37,7 @@ import { useRunCode } from './useRunCode';
|
|
|
37
37
|
import { useURLSync } from './useURLSync';
|
|
38
38
|
import { createInitialState, vzReducer } from './vzReducer';
|
|
39
39
|
import { findPane } from './vzReducer/findPane';
|
|
40
|
+
import { usePresenceAutoFollow } from './usePresenceAutoFollow';
|
|
40
41
|
|
|
41
42
|
// This context centralizes all the "smart" logic
|
|
42
43
|
// to do with the application state. This includes
|
|
@@ -408,6 +409,16 @@ export const VZCodeProvider = ({
|
|
|
408
409
|
const [hoveredItemId, setHoveredItemId] =
|
|
409
410
|
useState<ItemId | null>(null);
|
|
410
411
|
|
|
412
|
+
// Handle presence-based auto-following
|
|
413
|
+
// This hook manages opening tabs when presence is received on files
|
|
414
|
+
// that are not currently open, independent of CodeMirror extensions
|
|
415
|
+
usePresenceAutoFollow({
|
|
416
|
+
docPresence,
|
|
417
|
+
enableAutoFollow,
|
|
418
|
+
openTab,
|
|
419
|
+
activePane,
|
|
420
|
+
});
|
|
421
|
+
|
|
411
422
|
// Livekit Voice Chat Modal
|
|
412
423
|
|
|
413
424
|
const [voiceChatModalOpen, setVoiceChatModalOpen] =
|
|
@@ -224,6 +224,12 @@ export const VZSidebar = ({
|
|
|
224
224
|
presenceId: PresenceId,
|
|
225
225
|
update: Presence,
|
|
226
226
|
) => {
|
|
227
|
+
// Sometimes this can be null, so we check.
|
|
228
|
+
// This can happen when the user disconnects.
|
|
229
|
+
// If this happens, we do not update the presence indicator.
|
|
230
|
+
// TODO test removal of presence indicator.
|
|
231
|
+
if (!update) return;
|
|
232
|
+
|
|
227
233
|
const presenceIndicator: PresenceIndicator = {
|
|
228
234
|
username: update.username,
|
|
229
235
|
fileId: update.start[1] as VizFileId,
|
|
@@ -378,7 +378,13 @@ export const useKeyboardShortcuts = ({
|
|
|
378
378
|
return;
|
|
379
379
|
}
|
|
380
380
|
|
|
381
|
-
|
|
381
|
+
let position: number;
|
|
382
|
+
try {
|
|
383
|
+
position = editor.posAtDOM(element);
|
|
384
|
+
} catch (error) {
|
|
385
|
+
// Element is not within the current editor's document
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
382
388
|
const identifier: SyntaxNode = tree.resolveInner(
|
|
383
389
|
position,
|
|
384
390
|
1,
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { useContext, useEffect, useRef } from 'react';
|
|
2
|
+
import { VZCodeContext } from './VZCodeContext';
|
|
3
|
+
import { Presence, PresenceId, TabState } from '../types';
|
|
4
|
+
import { VizFileId } from '@vizhub/viz-types';
|
|
5
|
+
|
|
6
|
+
const DEBUG = true;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Custom hook that handles auto-following users by opening tabs when presence
|
|
10
|
+
* is received on files that are not currently open. This logic is independent
|
|
11
|
+
* of CodeMirror extensions and works regardless of editor cache state.
|
|
12
|
+
*/
|
|
13
|
+
export const usePresenceAutoFollow = ({
|
|
14
|
+
docPresence,
|
|
15
|
+
enableAutoFollow,
|
|
16
|
+
openTab,
|
|
17
|
+
activePane,
|
|
18
|
+
}) => {
|
|
19
|
+
// Track enableAutoFollow on a ref for use in event handlers
|
|
20
|
+
const enableAutoFollowRef = useRef(enableAutoFollow);
|
|
21
|
+
useEffect(() => {
|
|
22
|
+
enableAutoFollowRef.current = enableAutoFollow;
|
|
23
|
+
}, [enableAutoFollow]);
|
|
24
|
+
|
|
25
|
+
// Track active file for comparison
|
|
26
|
+
const activeFileIdRef = useRef(activePane?.activeFileId);
|
|
27
|
+
useEffect(() => {
|
|
28
|
+
activeFileIdRef.current = activePane?.activeFileId;
|
|
29
|
+
}, [activePane?.activeFileId]);
|
|
30
|
+
|
|
31
|
+
useEffect(() => {
|
|
32
|
+
if (!docPresence) return;
|
|
33
|
+
|
|
34
|
+
const handlePresenceReceive = (
|
|
35
|
+
id: PresenceId,
|
|
36
|
+
presence: Presence,
|
|
37
|
+
) => {
|
|
38
|
+
if (DEBUG) {
|
|
39
|
+
console.log(
|
|
40
|
+
`[usePresenceAutoFollow] Received presence for id ${id}`,
|
|
41
|
+
presence,
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// If presence is null, the user has disconnected
|
|
46
|
+
if (!presence) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// If auto-follow is not enabled, do nothing
|
|
51
|
+
if (!enableAutoFollowRef.current) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Extract the file ID from the presence path
|
|
56
|
+
// Presence path structure: [path, to, file, fileId, position]
|
|
57
|
+
// We want the fileId which should be at index 1 for files
|
|
58
|
+
const presenceFileId = presence.start[1] as VizFileId;
|
|
59
|
+
|
|
60
|
+
// If the presence is for the currently active file, do nothing
|
|
61
|
+
// (the CodeMirror extension will handle displaying the cursor)
|
|
62
|
+
if (presenceFileId === activeFileIdRef.current) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// If the presence is for a different file, open that tab
|
|
67
|
+
DEBUG &&
|
|
68
|
+
console.log(
|
|
69
|
+
`[usePresenceAutoFollow] Auto-opening tab for ${presence.username} in file ${presenceFileId}`,
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
openTab({
|
|
73
|
+
fileId: presenceFileId,
|
|
74
|
+
isTransient: true,
|
|
75
|
+
});
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
// Subscribe to presence changes
|
|
79
|
+
docPresence.on('receive', handlePresenceReceive);
|
|
80
|
+
|
|
81
|
+
// Cleanup subscription on unmount
|
|
82
|
+
return () => {
|
|
83
|
+
docPresence.off('receive', handlePresenceReceive);
|
|
84
|
+
};
|
|
85
|
+
}, [docPresence, openTab]);
|
|
86
|
+
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useEffect, useRef } from 'react';
|
|
2
2
|
import { SubmitOperation } from '../types';
|
|
3
|
-
import {
|
|
3
|
+
import { createRunCodeFunction } from '../runCode';
|
|
4
4
|
|
|
5
5
|
// Runs the code by flashing `isInteracting` to `true`.
|
|
6
6
|
export const useRunCode = (
|
|
@@ -8,21 +8,7 @@ export const useRunCode = (
|
|
|
8
8
|
) => {
|
|
9
9
|
const runCodeRef = useRef(null);
|
|
10
10
|
useEffect(() => {
|
|
11
|
-
const runCode = ()
|
|
12
|
-
submitOperation((content: VizContent) => ({
|
|
13
|
-
...content,
|
|
14
|
-
isInteracting: true,
|
|
15
|
-
}));
|
|
16
|
-
setTimeout(() => {
|
|
17
|
-
// This somewhat cryptic logic
|
|
18
|
-
// deletes the `isInteracting` property
|
|
19
|
-
// from the document.
|
|
20
|
-
submitOperation(
|
|
21
|
-
({ isInteracting, ...newDocument }) =>
|
|
22
|
-
newDocument,
|
|
23
|
-
);
|
|
24
|
-
}, 0);
|
|
25
|
-
};
|
|
11
|
+
const runCode = createRunCodeFunction(submitOperation);
|
|
26
12
|
runCodeRef.current = runCode;
|
|
27
13
|
}, [submitOperation]);
|
|
28
14
|
return runCodeRef;
|
|
@@ -1,22 +1,15 @@
|
|
|
1
1
|
import { useCallback } from 'react';
|
|
2
2
|
import { ShareDBDoc } from '../types'; // Assuming ShareDBDoc is already generic
|
|
3
|
-
import {
|
|
3
|
+
import { createSubmitOperation } from '../submitOperation';
|
|
4
4
|
|
|
5
5
|
// A generic helper function to submit diff-based operations to ShareDB
|
|
6
6
|
export const useSubmitOperation = <T>(
|
|
7
7
|
shareDBDoc: ShareDBDoc<T>,
|
|
8
8
|
): ((next: (data: T) => T) => void) => {
|
|
9
9
|
const submitOperation: (next: (data: T) => T) => void =
|
|
10
|
-
useCallback(
|
|
11
|
-
(
|
|
12
|
-
|
|
13
|
-
const op = diff(data, next(data));
|
|
14
|
-
if (op && shareDBDoc) {
|
|
15
|
-
shareDBDoc.submitOp(op);
|
|
16
|
-
}
|
|
17
|
-
},
|
|
18
|
-
[shareDBDoc],
|
|
19
|
-
);
|
|
10
|
+
useCallback(() => {
|
|
11
|
+
return createSubmitOperation(shareDBDoc);
|
|
12
|
+
}, [shareDBDoc])();
|
|
20
13
|
|
|
21
14
|
return submitOperation;
|
|
22
15
|
};
|
package/src/runCode.js
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { createSubmitOperation } from './submitOperation.js';
|
|
2
|
+
// TODO migrate the server-side code to use TypeScript,
|
|
3
|
+
// and delete this file once the migration is complete (use src/runCode.ts instead).
|
|
4
|
+
/**
|
|
5
|
+
* Creates a runCode function that triggers code execution by flashing `isInteracting` to `true`.
|
|
6
|
+
* This works for both client-side (with submitOperation) and server-side (with ShareDB document).
|
|
7
|
+
*/
|
|
8
|
+
export const createRunCodeFunction = (
|
|
9
|
+
submitOperationOrDoc,
|
|
10
|
+
) => {
|
|
11
|
+
return () => {
|
|
12
|
+
let submitOperation;
|
|
13
|
+
|
|
14
|
+
// Check if this is a client-side submitOperation or server-side ShareDB document
|
|
15
|
+
if (typeof submitOperationOrDoc === 'function') {
|
|
16
|
+
// Client-side: already a submitOperation function
|
|
17
|
+
submitOperation = submitOperationOrDoc;
|
|
18
|
+
} else {
|
|
19
|
+
// Server-side: create submitOperation from ShareDB document
|
|
20
|
+
submitOperation = createSubmitOperation(
|
|
21
|
+
submitOperationOrDoc,
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Use the unified submitOperation approach for both client and server
|
|
26
|
+
submitOperation((content) => ({
|
|
27
|
+
...content,
|
|
28
|
+
isInteracting: true,
|
|
29
|
+
}));
|
|
30
|
+
|
|
31
|
+
setTimeout(() => {
|
|
32
|
+
submitOperation((content) => ({
|
|
33
|
+
...content,
|
|
34
|
+
isInteracting: false,
|
|
35
|
+
}));
|
|
36
|
+
}, 0);
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Creates a runCodeRef object compatible with React refs.
|
|
42
|
+
* This is useful for maintaining compatibility with existing code that expects a ref.
|
|
43
|
+
*/
|
|
44
|
+
export const createRunCodeRef = (submitOperationOrDoc) => {
|
|
45
|
+
return {
|
|
46
|
+
current: createRunCodeFunction(submitOperationOrDoc),
|
|
47
|
+
};
|
|
48
|
+
};
|
package/src/runCode.ts
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { SubmitOperation } from './types';
|
|
2
|
+
import { VizContent } from '@vizhub/viz-types';
|
|
3
|
+
import { createSubmitOperation } from './submitOperation';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Creates a runCode function that triggers code execution by flashing `isInteracting` to `true`.
|
|
7
|
+
* This works for both client-side (with submitOperation) and server-side (with ShareDB document).
|
|
8
|
+
*/
|
|
9
|
+
export const createRunCodeFunction = (
|
|
10
|
+
submitOperationOrDoc:
|
|
11
|
+
| SubmitOperation
|
|
12
|
+
| { data: any; submitOp: (ops: any) => void },
|
|
13
|
+
) => {
|
|
14
|
+
return () => {
|
|
15
|
+
let submitOperation: SubmitOperation;
|
|
16
|
+
|
|
17
|
+
// Check if this is a client-side submitOperation or server-side ShareDB document
|
|
18
|
+
if (typeof submitOperationOrDoc === 'function') {
|
|
19
|
+
// Client-side: already a submitOperation function
|
|
20
|
+
submitOperation =
|
|
21
|
+
submitOperationOrDoc as SubmitOperation;
|
|
22
|
+
} else {
|
|
23
|
+
// Server-side: create submitOperation from ShareDB document
|
|
24
|
+
submitOperation = createSubmitOperation(
|
|
25
|
+
submitOperationOrDoc,
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Use the unified submitOperation approach for both client and server
|
|
30
|
+
submitOperation((content: VizContent) => ({
|
|
31
|
+
...content,
|
|
32
|
+
isInteracting: true,
|
|
33
|
+
}));
|
|
34
|
+
|
|
35
|
+
setTimeout(() => {
|
|
36
|
+
// This somewhat cryptic logic
|
|
37
|
+
// deletes the `isInteracting` property
|
|
38
|
+
// from the document.
|
|
39
|
+
submitOperation(
|
|
40
|
+
({ isInteracting, ...newDocument }) => newDocument,
|
|
41
|
+
);
|
|
42
|
+
}, 0);
|
|
43
|
+
};
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Creates a runCodeRef object compatible with React refs.
|
|
48
|
+
* This is useful for maintaining compatibility with existing code that expects a ref.
|
|
49
|
+
*/
|
|
50
|
+
export const createRunCodeRef = (
|
|
51
|
+
submitOperationOrDoc:
|
|
52
|
+
| SubmitOperation
|
|
53
|
+
| { data: any; submitOp: (ops: any) => void },
|
|
54
|
+
) => {
|
|
55
|
+
return {
|
|
56
|
+
current: createRunCodeFunction(submitOperationOrDoc),
|
|
57
|
+
};
|
|
58
|
+
};
|
|
@@ -1,42 +1,44 @@
|
|
|
1
|
-
import { performAiEdit } from 'editcodewithai';
|
|
2
1
|
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
} from '
|
|
2
|
+
assembleFullPrompt,
|
|
3
|
+
prepareFilesForPrompt,
|
|
4
|
+
} from 'editcodewithai';
|
|
5
|
+
import { formatMarkdownFiles } from 'llm-code-format';
|
|
7
6
|
|
|
8
7
|
/**
|
|
9
|
-
* Performs AI editing operations
|
|
8
|
+
* Performs AI editing operations using streaming with incremental OT operations
|
|
10
9
|
*/
|
|
11
|
-
export const performAIEditing = async (
|
|
10
|
+
export const performAIEditing = async ({
|
|
11
|
+
prompt,
|
|
12
12
|
shareDBDoc,
|
|
13
|
-
chatId,
|
|
14
|
-
content,
|
|
15
|
-
files,
|
|
16
13
|
llmFunction,
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
files,
|
|
21
|
-
llmFunction,
|
|
22
|
-
apiKey: process.env.VIZHUB_EDIT_WITH_AI_API_KEY,
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
// Clear the scratchpad and update status
|
|
26
|
-
clearAIScratchpadAndStatus(
|
|
27
|
-
shareDBDoc,
|
|
28
|
-
chatId,
|
|
29
|
-
'Done editing with AI.',
|
|
14
|
+
runCode,
|
|
15
|
+
}) => {
|
|
16
|
+
const preparedFiles = prepareFilesForPrompt(
|
|
17
|
+
shareDBDoc.data.files,
|
|
30
18
|
);
|
|
19
|
+
const filesContext = formatMarkdownFiles(preparedFiles);
|
|
31
20
|
|
|
32
|
-
//
|
|
33
|
-
|
|
21
|
+
// 2. Assemble the final prompt
|
|
22
|
+
const fullPrompt = assembleFullPrompt({
|
|
23
|
+
filesContext,
|
|
24
|
+
prompt,
|
|
25
|
+
editFormat: 'whole',
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
// Call the LLM function which will handle streaming and incremental file updates
|
|
29
|
+
const result = await llmFunction(fullPrompt);
|
|
34
30
|
|
|
35
|
-
//
|
|
36
|
-
|
|
31
|
+
// Clear the scratchpad and update status
|
|
32
|
+
// clearAIScratchpadAndStatus(
|
|
33
|
+
// shareDBDoc,
|
|
34
|
+
// chatId,
|
|
35
|
+
// 'Done editing with AI.',
|
|
36
|
+
// );
|
|
37
37
|
|
|
38
|
-
|
|
39
|
-
setIsInteracting(shareDBDoc, false);
|
|
38
|
+
runCode();
|
|
40
39
|
|
|
41
|
-
return
|
|
40
|
+
return {
|
|
41
|
+
content: result.content,
|
|
42
|
+
generationId: result.generationId,
|
|
43
|
+
};
|
|
42
44
|
};
|