vzcode 2.2.0 → 2.3.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-Dsx6rOdK.js → buildWorker-wLkQ0yz1.js} +64 -64
- package/dist/assets/{index-BM8tDh6M.css → index-BoFeK4GL.css} +1 -1
- package/dist/assets/{index-CpxqGhXj.js → index-DuUjtrt6.js} +150 -150
- package/dist/assets/{worker-BeBZGG1n.js → worker-DHxSPM7N.js} +78 -78
- package/dist/cli.js +113 -0
- package/dist/index.html +2 -2
- package/{src/ot.ts → dist/ot.js} +2 -14
- package/{src/randomId.ts → dist/randomId.js} +5 -6
- package/dist/runCode.js +20 -0
- package/dist/server/aiChatHandler/aiEditing.js +89 -0
- package/dist/server/aiChatHandler/chatOperations.js +360 -0
- package/dist/server/aiChatHandler/errorHandling.js +49 -0
- package/dist/server/aiChatHandler/index.js +112 -0
- package/dist/server/aiChatHandler/llmStreaming.js +284 -0
- package/dist/server/aiChatHandler/validation.js +19 -0
- package/dist/server/computeInitialDocument.js +146 -0
- package/{src/server/config.ts → dist/server/config.js} +4 -7
- package/{src/server/featureFlags.ts → dist/server/featureFlags.js} +0 -3
- package/dist/server/generateAIResponse.js +129 -0
- package/dist/server/handleAIAssist.js +39 -0
- package/dist/server/handleAICopilot.js +81 -0
- package/dist/server/index.js +297 -0
- package/dist/server/livekit.js +17 -0
- package/dist/server/prettier.js +90 -0
- package/{src/server/setupEnv.ts → dist/server/setupEnv.js} +1 -2
- package/dist/submitOperation.js +14 -0
- package/dist/types.js +1 -0
- package/dist/utils/fileDiff.js +66 -0
- package/package.json +22 -22
- package/src/client/AIAssist/AIAssistCodeMirrorKeyMap.ts +0 -42
- package/src/client/AIAssist/AIAssistWidget/index.tsx +0 -135
- package/src/client/AIAssist/AIAssistWidget/style.scss +0 -47
- package/src/client/AIAssist/Spinner.jsx +0 -76
- package/src/client/AIAssist/formatFile.ts +0 -27
- package/src/client/AIAssist/generateFilesContext.ts +0 -25
- package/src/client/AIAssist/index.ts +0 -2
- package/src/client/AIAssist/startAIAssist.ts +0 -95
- package/src/client/App/index.tsx +0 -153
- package/src/client/App/style.scss +0 -82
- package/src/client/App/usePending.ts +0 -51
- package/src/client/App/useShareDB.ts +0 -161
- package/src/client/CodeEditor/Copilot/index.ts +0 -74
- package/src/client/CodeEditor/InteractiveWidgets/boolToggler.ts +0 -12
- package/src/client/CodeEditor/InteractiveWidgets/colorPicker.ts +0 -68
- package/src/client/CodeEditor/InteractiveWidgets/colorsInTextPlugin.ts +0 -174
- package/src/client/CodeEditor/InteractiveWidgets/highlightWidgets.ts +0 -79
- package/src/client/CodeEditor/InteractiveWidgets/index.ts +0 -44
- package/src/client/CodeEditor/InteractiveWidgets/markdownCheckboxToggler.ts +0 -12
- package/src/client/CodeEditor/InteractiveWidgets/numberDragger.ts +0 -20
- package/src/client/CodeEditor/InteractiveWidgets/rotateWidget.ts +0 -143
- package/src/client/CodeEditor/InteractiveWidgets/urlClicker.test.ts +0 -48
- package/src/client/CodeEditor/InteractiveWidgets/urlClicker.ts +0 -12
- package/src/client/CodeEditor/InteractiveWidgets/vec2Slider.ts +0 -25
- package/src/client/CodeEditor/basicSetup.ts +0 -73
- package/src/client/CodeEditor/getOrCreateEditor.tsx +0 -615
- package/src/client/CodeEditor/index.tsx +0 -202
- package/src/client/CodeEditor/json1PresenceBroadcast.ts +0 -41
- package/src/client/CodeEditor/json1PresenceDisplay.ts +0 -350
- package/src/client/CodeEditor/rainbowBrackets.ts +0 -122
- package/src/client/CodeEditor/style.scss +0 -156
- package/src/client/CodeEditor/typescriptExtension/worker.ts +0 -32
- package/src/client/CodeErrorOverlay/index.tsx +0 -77
- package/src/client/CodeErrorOverlay/style.scss +0 -33
- package/src/client/Fonts/fonts.ts +0 -8
- package/src/client/Icons/AdjustmentSVG.tsx +0 -19
- package/src/client/Icons/BugSVG.tsx +0 -20
- package/src/client/Icons/CloseSVG.tsx +0 -19
- package/src/client/Icons/CssSVG.tsx +0 -16
- package/src/client/Icons/DirectoryArrowSVG.tsx +0 -19
- package/src/client/Icons/EditSVG.tsx +0 -19
- package/src/client/Icons/FileSVG.tsx +0 -19
- package/src/client/Icons/FolderSVG.tsx +0 -20
- package/src/client/Icons/GearSVG.tsx +0 -16
- package/src/client/Icons/HtmlSVG.tsx +0 -16
- package/src/client/Icons/JavaScriptSVG.tsx +0 -16
- package/src/client/Icons/JsonSVG.tsx +0 -16
- package/src/client/Icons/MarkdownSVG.tsx +0 -16
- package/src/client/Icons/MicOffSVG.tsx +0 -35
- package/src/client/Icons/MicSVG.tsx +0 -25
- package/src/client/Icons/NewSVG.tsx +0 -19
- package/src/client/Icons/PinSVG.tsx +0 -14
- package/src/client/Icons/PlaySVG.tsx +0 -16
- package/src/client/Icons/QuestionMarkSVG.tsx +0 -18
- package/src/client/Icons/ReactSVG.tsx +0 -16
- package/src/client/Icons/SearchSVG.tsx +0 -20
- package/src/client/Icons/SparklesSVG.tsx +0 -23
- package/src/client/Icons/SplitEditorSVG.tsx +0 -34
- package/src/client/Icons/StopSVG.tsx +0 -15
- package/src/client/Icons/SvelteSVG.tsx +0 -16
- package/src/client/Icons/TrashSVG.tsx +0 -19
- package/src/client/Icons/TypeScriptSVG.tsx +0 -16
- package/src/client/Icons/index.tsx +0 -26
- package/src/client/ImageViewer/index.tsx +0 -91
- package/src/client/ImageViewer/style.scss +0 -41
- package/src/client/PresenceNotifications/PresenceNotifications.test.ts +0 -134
- package/src/client/PresenceNotifications/index.tsx +0 -158
- package/src/client/PresenceNotifications/style.scss +0 -17
- package/src/client/RunCodeWidget/index.tsx +0 -117
- package/src/client/RunCodeWidget/style.scss +0 -24
- package/src/client/SplitPaneResizeContext.tsx +0 -159
- package/src/client/TabList/Tab.tsx +0 -73
- package/src/client/TabList/index.tsx +0 -35
- package/src/client/TabList/style.scss +0 -51
- package/src/client/VZCodeContext/index.tsx +0 -29
- package/src/client/VZCodeContext/types.ts +0 -249
- package/src/client/VZCodeContext/useVZCodeState.ts +0 -584
- package/src/client/VZKeyboardShortcutsDoc.tsx +0 -148
- package/src/client/VZLeft.tsx +0 -28
- package/src/client/VZMiddle.tsx +0 -172
- package/src/client/VZResizer/index.tsx +0 -128
- package/src/client/VZResizer/styles.scss +0 -33
- package/src/client/VZRight.tsx +0 -113
- package/src/client/VZSettings.tsx +0 -275
- package/src/client/VZSidebar/AIChat/ChatInput.tsx +0 -239
- package/src/client/VZSidebar/AIChat/DiffView.scss +0 -136
- package/src/client/VZSidebar/AIChat/DiffView.tsx +0 -176
- package/src/client/VZSidebar/AIChat/Message.tsx +0 -143
- package/src/client/VZSidebar/AIChat/MessageList.tsx +0 -205
- package/src/client/VZSidebar/AIChat/StreamingMessage.tsx +0 -35
- package/src/client/VZSidebar/AIChat/ThinkingScratchpad.tsx +0 -156
- package/src/client/VZSidebar/AIChat/TypingIndicator.tsx +0 -19
- package/src/client/VZSidebar/AIChat/index.tsx +0 -273
- package/src/client/VZSidebar/AIChat/styles.scss +0 -627
- package/src/client/VZSidebar/AIChat/useSpeechRecognition.ts +0 -117
- package/src/client/VZSidebar/CreateDirModal.tsx +0 -111
- package/src/client/VZSidebar/CreateFileModal.tsx +0 -111
- package/src/client/VZSidebar/DeleteConfirmationModal.tsx +0 -76
- package/src/client/VZSidebar/DirectoryListing.tsx +0 -89
- package/src/client/VZSidebar/EmptyState.tsx +0 -11
- package/src/client/VZSidebar/FileListing.tsx +0 -91
- package/src/client/VZSidebar/FileTypeIcon.tsx +0 -49
- package/src/client/VZSidebar/Item.tsx +0 -237
- package/src/client/VZSidebar/Listing.tsx +0 -47
- package/src/client/VZSidebar/Search.tsx +0 -527
- package/src/client/VZSidebar/VisualEditor.tsx +0 -282
- package/src/client/VZSidebar/VoiceChatModal.tsx +0 -121
- package/src/client/VZSidebar/aiCopyPaste.ts +0 -199
- package/src/client/VZSidebar/index.tsx +0 -728
- package/src/client/VZSidebar/styles.scss +0 -612
- package/src/client/VZSidebar/useDragAndDrop.tsx +0 -408
- package/src/client/bootstrap.ts +0 -26
- package/src/client/buildWorker.ts +0 -3
- package/src/client/featureFlags.ts +0 -8
- package/src/client/generateRequestId.ts +0 -6
- package/src/client/getFileTree.ts +0 -53
- package/src/client/index.ts +0 -19
- package/src/client/main.jsx +0 -10
- package/src/client/presenceColor.ts +0 -8
- package/src/client/shouldTriggerRun.ts +0 -31
- package/src/client/sortFileTree.ts +0 -21
- package/src/client/tabsSearchParameters.test.ts +0 -79
- package/src/client/tabsSearchParameters.ts +0 -137
- package/src/client/themes/index.ts +0 -7
- package/src/client/themes/themes.ts +0 -77
- package/src/client/themes/uiwImports.ts +0 -32
- package/src/client/themes/useDynamicTheme.ts +0 -29
- package/src/client/themes/vizhubTheme/colors.ts +0 -80
- package/src/client/themes/vizhubTheme/index.ts +0 -193
- package/src/client/useActions.ts +0 -288
- package/src/client/useESLint/index.ts +0 -72
- package/src/client/useESLint/worker.ts +0 -113
- package/src/client/useEditorCache.ts +0 -35
- package/src/client/useFileCRUD.ts +0 -228
- package/src/client/useKeyboardShortcuts.ts +0 -451
- package/src/client/useOpenDirectories.ts +0 -99
- package/src/client/usePresenceAutoFollow.ts +0 -85
- package/src/client/usePrettier/index.ts +0 -178
- package/src/client/usePrettier/worker.ts +0 -109
- package/src/client/useRunCode.tsx +0 -15
- package/src/client/useRuntimeError.ts +0 -32
- package/src/client/useSubmitOperation.ts +0 -15
- package/src/client/useURLSync.ts +0 -137
- package/src/client/usernameLocalStorage.ts +0 -43
- package/src/client/utils/fileExtension.ts +0 -10
- package/src/client/utils/isImageFile.ts +0 -12
- package/src/client/vzReducer/aiChatReducer.ts +0 -44
- package/src/client/vzReducer/closeTabReducer.test.ts +0 -205
- package/src/client/vzReducer/closeTabsReducer.ts +0 -96
- package/src/client/vzReducer/createInitialState.ts +0 -38
- package/src/client/vzReducer/editorNoLongerWantsFocusReducer.ts +0 -9
- package/src/client/vzReducer/findPane.ts +0 -19
- package/src/client/vzReducer/index.ts +0 -250
- package/src/client/vzReducer/openTabReducer.test.ts +0 -127
- package/src/client/vzReducer/openTabReducer.ts +0 -74
- package/src/client/vzReducer/searchReducer.test.ts +0 -292
- package/src/client/vzReducer/searchReducer.ts +0 -181
- package/src/client/vzReducer/setActiveFileIdReducer.ts +0 -28
- package/src/client/vzReducer/setActiveFileLeftRightReducer.ts +0 -76
- package/src/client/vzReducer/setIsDocOpenReducer.ts +0 -9
- package/src/client/vzReducer/setIsSettingsOpenReducer.ts +0 -9
- package/src/client/vzReducer/setThemeReducer.ts +0 -9
- package/src/client/vzReducer/setUsernameReducer.ts +0 -9
- package/src/client/vzReducer/splitCurrentPaneReducer.ts +0 -66
- package/src/client/vzReducer/toggleAutoFollowReducer.ts +0 -12
- package/src/client/vzReducer/updatePane.ts +0 -40
- package/src/client/vzReducer/updatePresenceIndicatorReducer.ts +0 -30
- package/src/client/vzReducer/visualEditorReducer.ts +0 -9
- package/src/reactRouterExports.ts +0 -15
- package/src/runCode.ts +0 -27
- package/src/server/aiChatHandler/aiEditing.ts +0 -152
- package/src/server/aiChatHandler/chatOperations.ts +0 -510
- package/src/server/aiChatHandler/errorHandling.ts +0 -66
- package/src/server/aiChatHandler/index.ts +0 -200
- package/src/server/aiChatHandler/llmStreaming.ts +0 -419
- package/src/server/aiChatHandler/undoHandler.ts +0 -97
- package/src/server/aiChatHandler/validation.ts +0 -24
- package/src/server/computeInitialDocument.ts +0 -220
- package/src/server/generateAIResponse.ts +0 -188
- package/src/server/handleAIAssist.ts +0 -53
- package/src/server/handleAIChatUndo.ts +0 -2
- package/src/server/handleAICopilot.ts +0 -97
- package/src/server/index.ts +0 -388
- package/src/server/livekit.ts +0 -24
- package/src/server/prettier.ts +0 -115
- package/src/submitOperation.ts +0 -25
- package/src/types.ts +0 -205
- package/src/utils/fileDiff.ts +0 -117
- package/src/vite-env.d.ts +0 -10
- /package/{src/server/handleAIChatMessage.ts → dist/server/handleAIChatMessage.js} +0 -0
- /package/{src/server/isDirectory.ts → dist/server/isDirectory.js} +0 -0
package/dist/cli.js
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { spawn } from 'child_process';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import { fileURLToPath } from 'url';
|
|
5
|
+
import fs from 'fs';
|
|
6
|
+
// Get the directory of the current module
|
|
7
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
8
|
+
const __dirname = path.dirname(__filename);
|
|
9
|
+
// Function to find the Tauri binary
|
|
10
|
+
function findTauriApp() {
|
|
11
|
+
// Look for the Tauri binary in common locations
|
|
12
|
+
const possiblePaths = [
|
|
13
|
+
// Development location (when running from source)
|
|
14
|
+
path.join(__dirname, '..', 'src-tauri', 'target', 'debug', 'vzcode'),
|
|
15
|
+
path.join(__dirname, '..', 'src-tauri', 'target', 'debug', 'vzcode.exe'),
|
|
16
|
+
// Release location (when running from built package)
|
|
17
|
+
path.join(__dirname, '..', 'src-tauri', 'target', 'release', 'vzcode'),
|
|
18
|
+
path.join(__dirname, '..', 'src-tauri', 'target', 'release', 'vzcode.exe'),
|
|
19
|
+
// MacOS app bundle
|
|
20
|
+
path.join(__dirname, '..', 'src-tauri', 'target', 'debug', 'vzcode.app', 'Contents', 'MacOS', 'vzcode'),
|
|
21
|
+
path.join(__dirname, '..', 'src-tauri', 'target', 'release', 'vzcode.app', 'Contents', 'MacOS', 'vzcode'),
|
|
22
|
+
];
|
|
23
|
+
for (const binaryPath of possiblePaths) {
|
|
24
|
+
if (fs.existsSync(binaryPath)) {
|
|
25
|
+
return binaryPath;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
// Function to launch the Tauri app
|
|
31
|
+
function launchTauriApp(workingDirectory) {
|
|
32
|
+
const tauriBinary = findTauriApp();
|
|
33
|
+
if (!tauriBinary) {
|
|
34
|
+
console.error('VZCode desktop app not found. Please build it first with: npm run tauri:build');
|
|
35
|
+
console.error('Falling back to launching browser version...');
|
|
36
|
+
launchBrowserVersion(workingDirectory);
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
console.log(`Launching VZCode desktop app from: ${workingDirectory}`);
|
|
40
|
+
// First start the server in the background
|
|
41
|
+
startServerInBackground(workingDirectory);
|
|
42
|
+
// Give the server a moment to start, then launch Tauri
|
|
43
|
+
setTimeout(() => {
|
|
44
|
+
const child = spawn(tauriBinary, [], {
|
|
45
|
+
cwd: workingDirectory,
|
|
46
|
+
stdio: 'inherit',
|
|
47
|
+
detached: true,
|
|
48
|
+
});
|
|
49
|
+
// Allow the parent process to exit while keeping the child running
|
|
50
|
+
child.unref();
|
|
51
|
+
}, 2000);
|
|
52
|
+
}
|
|
53
|
+
// Function to start the server in the background
|
|
54
|
+
function startServerInBackground(workingDirectory) {
|
|
55
|
+
// Start the server process in the background
|
|
56
|
+
const serverPath = path.join(__dirname, 'server', 'index.js');
|
|
57
|
+
const serverChild = spawn('node', [serverPath], {
|
|
58
|
+
cwd: workingDirectory,
|
|
59
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
60
|
+
detached: true,
|
|
61
|
+
});
|
|
62
|
+
// Don't block on the server process
|
|
63
|
+
serverChild.unref();
|
|
64
|
+
// Log server output for debugging
|
|
65
|
+
if (serverChild.stdout) {
|
|
66
|
+
serverChild.stdout.on('data', (data) => {
|
|
67
|
+
console.log('Server:', data.toString());
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
if (serverChild.stderr) {
|
|
71
|
+
serverChild.stderr.on('data', (data) => {
|
|
72
|
+
console.error('Server error:', data.toString());
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
// Fallback function to launch the browser version
|
|
77
|
+
async function launchBrowserVersion(workingDirectory) {
|
|
78
|
+
const { default: open } = await import('open');
|
|
79
|
+
const serverModule = await import('./server/index.js');
|
|
80
|
+
// Note: The server/index.js will use process.cwd() automatically
|
|
81
|
+
// and we're already in the correct working directory
|
|
82
|
+
console.log(`Starting VZCode server from: ${workingDirectory}`);
|
|
83
|
+
}
|
|
84
|
+
// Main CLI function
|
|
85
|
+
function main() {
|
|
86
|
+
const workingDirectory = process.cwd();
|
|
87
|
+
// Parse command line arguments
|
|
88
|
+
const args = process.argv.slice(2);
|
|
89
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
90
|
+
console.log('VZCode - Multiplayer Code Editor');
|
|
91
|
+
console.log('');
|
|
92
|
+
console.log('Usage: vzcode [options]');
|
|
93
|
+
console.log('');
|
|
94
|
+
console.log('Options:');
|
|
95
|
+
console.log(' --help, -h Show this help message');
|
|
96
|
+
console.log(' --browser Force browser version instead of desktop app');
|
|
97
|
+
console.log(' --port=<port> Specify port for server (default: 3030)');
|
|
98
|
+
console.log('');
|
|
99
|
+
console.log('Examples:');
|
|
100
|
+
console.log(' vzcode # Launch desktop app in current directory');
|
|
101
|
+
console.log(' vzcode --browser # Launch browser version');
|
|
102
|
+
console.log(' vzcode --port=4000 # Start server on port 4000');
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
if (args.includes('--browser')) {
|
|
106
|
+
launchBrowserVersion(workingDirectory);
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
launchTauriApp(workingDirectory);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
// Run the CLI
|
|
113
|
+
main();
|
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-DuUjtrt6.js"></script>
|
|
24
|
+
<link rel="stylesheet" crossorigin href="/assets/index-BoFeK4GL.css">
|
|
25
25
|
</head>
|
|
26
26
|
<body>
|
|
27
27
|
<div id="root"></div>
|
package/{src/ot.ts → dist/ot.js}
RENAMED
|
@@ -4,21 +4,9 @@
|
|
|
4
4
|
import OTJSON1Presence from 'sharedb-client-browser/dist/ot-json1-presence-umd.cjs';
|
|
5
5
|
import jsondiff from 'json0-ot-diff';
|
|
6
6
|
import diffMatchPatch from 'diff-match-patch';
|
|
7
|
-
|
|
8
|
-
export const { json1Presence, textUnicode } =
|
|
9
|
-
OTJSON1Presence;
|
|
10
|
-
|
|
7
|
+
export const { json1Presence, textUnicode } = OTJSON1Presence;
|
|
11
8
|
// The OT type used throughout the codebase.
|
|
12
9
|
export const otType = json1Presence.type;
|
|
13
|
-
|
|
14
10
|
// Applies an OT op to an object.
|
|
15
11
|
export const apply = otType.apply;
|
|
16
|
-
|
|
17
|
-
export const diff = (a, b) =>
|
|
18
|
-
jsondiff(
|
|
19
|
-
a,
|
|
20
|
-
b,
|
|
21
|
-
diffMatchPatch,
|
|
22
|
-
json1Presence,
|
|
23
|
-
textUnicode,
|
|
24
|
-
);
|
|
12
|
+
export const diff = (a, b) => jsondiff(a, b, diffMatchPatch, json1Presence, textUnicode);
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
// Generate a random ID string consisting of digits.
|
|
2
2
|
const numDigits = 8;
|
|
3
3
|
export const randomId = () => {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
4
|
+
let randomString = '';
|
|
5
|
+
for (let i = 0; i < numDigits; i++) {
|
|
6
|
+
randomString += Math.floor(Math.random() * 10);
|
|
7
|
+
}
|
|
8
|
+
return randomString;
|
|
9
9
|
};
|
|
10
|
-
|
|
11
10
|
// const probabilityOfCollision =
|
|
12
11
|
// 1 - Math.pow(1 - 1 / Math.pow(10, numDigits), numRuns);
|
|
13
12
|
// Using this formula, the probability of generating the same ID twice
|
package/dist/runCode.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { generateRunId } from '@vizhub/viz-utils';
|
|
2
|
+
/**
|
|
3
|
+
* Creates a runCode function that triggers code execution by changing the `runId` in the content.
|
|
4
|
+
* This works for both client-side (with submitOperation) and server-side (with ShareDB document).
|
|
5
|
+
*/
|
|
6
|
+
export const createRunCodeFunction = (submitOperation) => () => {
|
|
7
|
+
submitOperation((content) => ({
|
|
8
|
+
...content,
|
|
9
|
+
runId: generateRunId(),
|
|
10
|
+
}));
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Creates a runCodeRef object compatible with React refs.
|
|
14
|
+
* This is useful for maintaining compatibility with existing code that expects a ref.
|
|
15
|
+
*/
|
|
16
|
+
export const createRunCodeRef = (submitOperation) => {
|
|
17
|
+
return {
|
|
18
|
+
current: createRunCodeFunction(submitOperation),
|
|
19
|
+
};
|
|
20
|
+
};
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { assembleFullPrompt, prepareFilesForPrompt, } from 'editcodewithai';
|
|
2
|
+
import { formatMarkdownFiles } from 'llm-code-format';
|
|
3
|
+
import { createFilesSnapshot, generateFilesUnifiedDiff, } from '../../utils/fileDiff.js';
|
|
4
|
+
import { formatFiles } from '../prettier.js';
|
|
5
|
+
import { createSubmitOperation } from '../../submitOperation.js';
|
|
6
|
+
// Dev flag for waiting 1 second before starting the LLM function.
|
|
7
|
+
// Useful for debugging and testing purposes, e.g. checking the typing indicator.
|
|
8
|
+
const delayStart = false;
|
|
9
|
+
/**
|
|
10
|
+
* Performs AI chat without editing - just generates a response
|
|
11
|
+
*/
|
|
12
|
+
export const performAIChat = async ({ prompt, shareDBDoc, llmFunction, }) => {
|
|
13
|
+
const preparedFiles = prepareFilesForPrompt(shareDBDoc.data.files);
|
|
14
|
+
const filesContext = formatMarkdownFiles(preparedFiles);
|
|
15
|
+
// 2. Assemble the final prompt for Q&A mode
|
|
16
|
+
const fullPrompt = assembleFullPrompt({
|
|
17
|
+
filesContext,
|
|
18
|
+
prompt,
|
|
19
|
+
editFormat: 'whole',
|
|
20
|
+
});
|
|
21
|
+
if (delayStart) {
|
|
22
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
23
|
+
}
|
|
24
|
+
// Call the LLM function which will handle streaming but won't edit files
|
|
25
|
+
const result = await llmFunction(fullPrompt);
|
|
26
|
+
return {
|
|
27
|
+
content: result.content,
|
|
28
|
+
generationId: result.generationId,
|
|
29
|
+
diffData: {}, // No file changes in ask mode
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Performs AI editing operations using streaming with incremental OT operations
|
|
34
|
+
*/
|
|
35
|
+
export const performAIEditing = async ({ prompt, shareDBDoc, llmFunction, runCode, }) => {
|
|
36
|
+
// 1. Capture the current state of files before editing
|
|
37
|
+
const beforeFiles = createFilesSnapshot(shareDBDoc.data.files);
|
|
38
|
+
const preparedFiles = prepareFilesForPrompt(shareDBDoc.data.files);
|
|
39
|
+
const filesContext = formatMarkdownFiles(preparedFiles);
|
|
40
|
+
// 2. Assemble the final prompt
|
|
41
|
+
const fullPrompt = assembleFullPrompt({
|
|
42
|
+
filesContext,
|
|
43
|
+
prompt,
|
|
44
|
+
editFormat: 'whole',
|
|
45
|
+
});
|
|
46
|
+
if (delayStart) {
|
|
47
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
48
|
+
}
|
|
49
|
+
// Call the LLM function which will handle streaming and incremental file updates
|
|
50
|
+
const result = await llmFunction(fullPrompt);
|
|
51
|
+
// 3. Run Prettier on changed files before running code
|
|
52
|
+
const afterLLMFiles = createFilesSnapshot(shareDBDoc.data.files);
|
|
53
|
+
// Find files that were changed by the AI
|
|
54
|
+
const changedFileIds = Object.keys(afterLLMFiles).filter((fileId) => beforeFiles[fileId]?.text !==
|
|
55
|
+
afterLLMFiles[fileId]?.text);
|
|
56
|
+
if (changedFileIds.length > 0) {
|
|
57
|
+
// Format the changed files
|
|
58
|
+
const formattedFiles = await formatFiles(shareDBDoc.data.files, changedFileIds);
|
|
59
|
+
// Apply formatted versions to shareDBDoc if formatting succeeded
|
|
60
|
+
if (Object.keys(formattedFiles).length > 0) {
|
|
61
|
+
const submitOperation = createSubmitOperation(shareDBDoc);
|
|
62
|
+
submitOperation((document) => {
|
|
63
|
+
const updatedFiles = { ...document.files };
|
|
64
|
+
// Apply each formatted file
|
|
65
|
+
Object.entries(formattedFiles).forEach(([fileId, formattedText]) => {
|
|
66
|
+
if (updatedFiles[fileId]) {
|
|
67
|
+
updatedFiles[fileId] = {
|
|
68
|
+
...updatedFiles[fileId],
|
|
69
|
+
text: formattedText,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
return {
|
|
74
|
+
...document,
|
|
75
|
+
files: updatedFiles,
|
|
76
|
+
};
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
runCode();
|
|
81
|
+
// 4. Capture the state of files after editing and formatting, then generate diff
|
|
82
|
+
const afterFiles = createFilesSnapshot(shareDBDoc.data.files);
|
|
83
|
+
const diffData = generateFilesUnifiedDiff(beforeFiles, afterFiles);
|
|
84
|
+
return {
|
|
85
|
+
content: result.content,
|
|
86
|
+
generationId: result.generationId,
|
|
87
|
+
diffData,
|
|
88
|
+
};
|
|
89
|
+
};
|
|
@@ -0,0 +1,360 @@
|
|
|
1
|
+
import { dateToTimestamp, } from '@vizhub/viz-utils';
|
|
2
|
+
import { randomId } from '../../randomId.js';
|
|
3
|
+
import { diff } from '../../ot.js';
|
|
4
|
+
/**
|
|
5
|
+
* Ensures the chats object exists in the ShareDB document
|
|
6
|
+
*/
|
|
7
|
+
export const ensureChatsExist = (shareDBDoc) => {
|
|
8
|
+
if (!shareDBDoc.data.chats) {
|
|
9
|
+
const op = diff(shareDBDoc.data, {
|
|
10
|
+
...shareDBDoc.data,
|
|
11
|
+
chats: {},
|
|
12
|
+
});
|
|
13
|
+
shareDBDoc.submitOp(op);
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Ensures a specific chat exists in the ShareDB document
|
|
18
|
+
*/
|
|
19
|
+
export const ensureChatExists = (shareDBDoc, chatId) => {
|
|
20
|
+
if (!shareDBDoc.data.chats[chatId]) {
|
|
21
|
+
const op = diff(shareDBDoc.data, {
|
|
22
|
+
...shareDBDoc.data,
|
|
23
|
+
chats: {
|
|
24
|
+
...shareDBDoc.data.chats,
|
|
25
|
+
[chatId]: {
|
|
26
|
+
id: chatId,
|
|
27
|
+
messages: [],
|
|
28
|
+
createdAt: dateToTimestamp(new Date()),
|
|
29
|
+
updatedAt: dateToTimestamp(new Date()),
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
shareDBDoc.submitOp(op);
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* Adds a user message to the chat
|
|
38
|
+
*/
|
|
39
|
+
export const addUserMessage = (shareDBDoc, chatId, content) => {
|
|
40
|
+
const userMessage = {
|
|
41
|
+
id: `user-${Date.now()}`,
|
|
42
|
+
role: 'user',
|
|
43
|
+
content: content,
|
|
44
|
+
timestamp: dateToTimestamp(new Date()),
|
|
45
|
+
};
|
|
46
|
+
const userMessageOp = diff(shareDBDoc.data, {
|
|
47
|
+
...shareDBDoc.data,
|
|
48
|
+
chats: {
|
|
49
|
+
...shareDBDoc.data.chats,
|
|
50
|
+
[chatId]: {
|
|
51
|
+
...shareDBDoc.data.chats[chatId],
|
|
52
|
+
messages: [
|
|
53
|
+
...shareDBDoc.data.chats[chatId].messages,
|
|
54
|
+
userMessage,
|
|
55
|
+
],
|
|
56
|
+
updatedAt: dateToTimestamp(new Date()),
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
});
|
|
60
|
+
shareDBDoc.submitOp(userMessageOp);
|
|
61
|
+
return userMessage;
|
|
62
|
+
};
|
|
63
|
+
/**
|
|
64
|
+
* Updates AI status in the chat
|
|
65
|
+
*/
|
|
66
|
+
export const updateAIStatus = (shareDBDoc, chatId, status) => {
|
|
67
|
+
const op = diff(shareDBDoc.data, {
|
|
68
|
+
...shareDBDoc.data,
|
|
69
|
+
chats: {
|
|
70
|
+
...shareDBDoc.data.chats,
|
|
71
|
+
[chatId]: {
|
|
72
|
+
...shareDBDoc.data.chats[chatId],
|
|
73
|
+
aiStatus: status,
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
});
|
|
77
|
+
shareDBDoc.submitOp(op);
|
|
78
|
+
};
|
|
79
|
+
/**
|
|
80
|
+
* Updates AI scratchpad content
|
|
81
|
+
*/
|
|
82
|
+
export const updateAIScratchpad = (shareDBDoc, chatId, content) => {
|
|
83
|
+
const op = diff(shareDBDoc.data, {
|
|
84
|
+
...shareDBDoc.data,
|
|
85
|
+
chats: {
|
|
86
|
+
...shareDBDoc.data.chats,
|
|
87
|
+
[chatId]: {
|
|
88
|
+
...shareDBDoc.data.chats[chatId],
|
|
89
|
+
aiScratchpad: content,
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
});
|
|
93
|
+
// op is `null` if there are no changes
|
|
94
|
+
// This can happen if the content is the same as before
|
|
95
|
+
// In that case, we don't need to submit an operation
|
|
96
|
+
if (op) {
|
|
97
|
+
shareDBDoc.submitOp(op);
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
/**
|
|
101
|
+
* Clears AI scratchpad and updates status
|
|
102
|
+
*/
|
|
103
|
+
export const clearAIScratchpadAndStatus = (shareDBDoc, chatId, status) => {
|
|
104
|
+
const op = diff(shareDBDoc.data, {
|
|
105
|
+
...shareDBDoc.data,
|
|
106
|
+
chats: {
|
|
107
|
+
...shareDBDoc.data.chats,
|
|
108
|
+
[chatId]: {
|
|
109
|
+
...shareDBDoc.data.chats[chatId],
|
|
110
|
+
aiScratchpad: undefined,
|
|
111
|
+
aiStatus: status,
|
|
112
|
+
},
|
|
113
|
+
},
|
|
114
|
+
});
|
|
115
|
+
shareDBDoc.submitOp(op);
|
|
116
|
+
};
|
|
117
|
+
/**
|
|
118
|
+
* Creates an initial empty AI message for streaming
|
|
119
|
+
*/
|
|
120
|
+
export const createAIMessage = (shareDBDoc, chatId) => {
|
|
121
|
+
const aiMessage = {
|
|
122
|
+
id: `assistant-${Date.now()}`,
|
|
123
|
+
role: 'assistant',
|
|
124
|
+
content: '',
|
|
125
|
+
timestamp: dateToTimestamp(new Date()),
|
|
126
|
+
};
|
|
127
|
+
const messageOp = diff(shareDBDoc.data, {
|
|
128
|
+
...shareDBDoc.data,
|
|
129
|
+
chats: {
|
|
130
|
+
...shareDBDoc.data.chats,
|
|
131
|
+
[chatId]: {
|
|
132
|
+
...shareDBDoc.data.chats[chatId],
|
|
133
|
+
messages: [
|
|
134
|
+
...shareDBDoc.data.chats[chatId].messages,
|
|
135
|
+
aiMessage,
|
|
136
|
+
],
|
|
137
|
+
updatedAt: dateToTimestamp(new Date()),
|
|
138
|
+
},
|
|
139
|
+
},
|
|
140
|
+
});
|
|
141
|
+
shareDBDoc.submitOp(messageOp);
|
|
142
|
+
return aiMessage.id;
|
|
143
|
+
};
|
|
144
|
+
/**
|
|
145
|
+
* Updates the content of an AI message during streaming
|
|
146
|
+
*/
|
|
147
|
+
export const updateAIMessageContent = (shareDBDoc, chatId, messageId, content) => {
|
|
148
|
+
const chat = shareDBDoc.data.chats[chatId];
|
|
149
|
+
const messageIndex = chat.messages.findIndex((msg) => msg.id === messageId);
|
|
150
|
+
if (messageIndex === -1) {
|
|
151
|
+
console.warn(`AI message with id ${messageId} not found`);
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
const updatedMessages = [...chat.messages];
|
|
155
|
+
updatedMessages[messageIndex] = {
|
|
156
|
+
...updatedMessages[messageIndex],
|
|
157
|
+
content,
|
|
158
|
+
};
|
|
159
|
+
const messageOp = diff(shareDBDoc.data, {
|
|
160
|
+
...shareDBDoc.data,
|
|
161
|
+
chats: {
|
|
162
|
+
...shareDBDoc.data.chats,
|
|
163
|
+
[chatId]: {
|
|
164
|
+
...chat,
|
|
165
|
+
messages: updatedMessages,
|
|
166
|
+
updatedAt: dateToTimestamp(new Date()),
|
|
167
|
+
},
|
|
168
|
+
},
|
|
169
|
+
});
|
|
170
|
+
shareDBDoc.submitOp(messageOp);
|
|
171
|
+
};
|
|
172
|
+
/**
|
|
173
|
+
* Sets the AI status for a chat
|
|
174
|
+
*/
|
|
175
|
+
export const setAIStatus = (shareDBDoc, chatId, status) => {
|
|
176
|
+
const op = diff(shareDBDoc.data, {
|
|
177
|
+
...shareDBDoc.data,
|
|
178
|
+
chats: {
|
|
179
|
+
...shareDBDoc.data.chats,
|
|
180
|
+
[chatId]: {
|
|
181
|
+
...shareDBDoc.data.chats[chatId],
|
|
182
|
+
aiStatus: status,
|
|
183
|
+
updatedAt: dateToTimestamp(new Date()),
|
|
184
|
+
},
|
|
185
|
+
},
|
|
186
|
+
});
|
|
187
|
+
shareDBDoc.submitOp(op);
|
|
188
|
+
};
|
|
189
|
+
/**
|
|
190
|
+
* Finalizes an AI message by clearing temporary fields
|
|
191
|
+
*/
|
|
192
|
+
export const finalizeAIMessage = (shareDBDoc, chatId) => {
|
|
193
|
+
const op = diff(shareDBDoc.data, {
|
|
194
|
+
...shareDBDoc.data,
|
|
195
|
+
chats: {
|
|
196
|
+
...shareDBDoc.data.chats,
|
|
197
|
+
[chatId]: {
|
|
198
|
+
...shareDBDoc.data.chats[chatId],
|
|
199
|
+
aiScratchpad: undefined,
|
|
200
|
+
aiStatus: undefined,
|
|
201
|
+
updatedAt: dateToTimestamp(new Date()),
|
|
202
|
+
},
|
|
203
|
+
},
|
|
204
|
+
});
|
|
205
|
+
shareDBDoc.submitOp(op);
|
|
206
|
+
};
|
|
207
|
+
/**
|
|
208
|
+
* Adds diff data to the most recent AI message
|
|
209
|
+
*/
|
|
210
|
+
export const addDiffToAIMessage = (shareDBDoc, chatId, diffData, beforeCommitId) => {
|
|
211
|
+
const chat = shareDBDoc.data.chats[chatId];
|
|
212
|
+
const messages = [...chat.messages];
|
|
213
|
+
// Find the most recent AI message
|
|
214
|
+
const lastAIMessageIndex = messages.length - 1;
|
|
215
|
+
if (lastAIMessageIndex >= 0 &&
|
|
216
|
+
messages[lastAIMessageIndex].role === 'assistant') {
|
|
217
|
+
const newMessage = {
|
|
218
|
+
...messages[lastAIMessageIndex],
|
|
219
|
+
diffData,
|
|
220
|
+
...(beforeCommitId && { beforeCommitId }), // Add beforeCommitId for VizHub integration
|
|
221
|
+
};
|
|
222
|
+
// Use type assertion to extend the message with diffData
|
|
223
|
+
messages[lastAIMessageIndex] = newMessage;
|
|
224
|
+
const messageOp = diff(shareDBDoc.data, {
|
|
225
|
+
...shareDBDoc.data,
|
|
226
|
+
chats: {
|
|
227
|
+
...shareDBDoc.data.chats,
|
|
228
|
+
[chatId]: {
|
|
229
|
+
...chat,
|
|
230
|
+
messages,
|
|
231
|
+
updatedAt: dateToTimestamp(new Date()),
|
|
232
|
+
},
|
|
233
|
+
},
|
|
234
|
+
});
|
|
235
|
+
shareDBDoc.submitOp(messageOp);
|
|
236
|
+
}
|
|
237
|
+
};
|
|
238
|
+
/**
|
|
239
|
+
* Adds an AI response message to the chat (legacy function, kept for compatibility)
|
|
240
|
+
*/
|
|
241
|
+
export const addAIMessage = (shareDBDoc, chatId, content) => {
|
|
242
|
+
const aiResponse = {
|
|
243
|
+
id: Date.now() + 1,
|
|
244
|
+
role: 'assistant',
|
|
245
|
+
content: content || 'AI edit completed successfully.',
|
|
246
|
+
timestamp: dateToTimestamp(new Date()),
|
|
247
|
+
};
|
|
248
|
+
const messageOp = diff(shareDBDoc.data, {
|
|
249
|
+
...shareDBDoc.data,
|
|
250
|
+
chats: {
|
|
251
|
+
...shareDBDoc.data.chats,
|
|
252
|
+
[chatId]: {
|
|
253
|
+
...shareDBDoc.data.chats[chatId],
|
|
254
|
+
messages: [
|
|
255
|
+
...shareDBDoc.data.chats[chatId].messages,
|
|
256
|
+
aiResponse,
|
|
257
|
+
],
|
|
258
|
+
updatedAt: dateToTimestamp(new Date()),
|
|
259
|
+
aiStatus: undefined,
|
|
260
|
+
},
|
|
261
|
+
},
|
|
262
|
+
});
|
|
263
|
+
shareDBDoc.submitOp(messageOp);
|
|
264
|
+
return aiResponse;
|
|
265
|
+
};
|
|
266
|
+
/**
|
|
267
|
+
* Updates files in the ShareDB document
|
|
268
|
+
*/
|
|
269
|
+
export const updateFiles = (shareDBDoc, files) => {
|
|
270
|
+
const filesOp = diff(shareDBDoc.data, {
|
|
271
|
+
...shareDBDoc.data,
|
|
272
|
+
files,
|
|
273
|
+
});
|
|
274
|
+
shareDBDoc.submitOp(filesOp);
|
|
275
|
+
};
|
|
276
|
+
/**
|
|
277
|
+
* Finds a file ID by searching for a matching file name
|
|
278
|
+
*/
|
|
279
|
+
export const resolveFileId = (fileName, shareDBDoc) => {
|
|
280
|
+
const files = shareDBDoc.data.files;
|
|
281
|
+
// Search through all files to find matching name
|
|
282
|
+
for (const [fileId, file] of Object.entries(files)) {
|
|
283
|
+
if (file.name === fileName) {
|
|
284
|
+
return fileId;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
// If file doesn't exist, return null
|
|
288
|
+
return null;
|
|
289
|
+
};
|
|
290
|
+
/**
|
|
291
|
+
* Creates a new file with a random ID
|
|
292
|
+
*/
|
|
293
|
+
export const createNewFile = (shareDBDoc, fileName) => {
|
|
294
|
+
// Generate a new random file ID
|
|
295
|
+
const newFileId = randomId();
|
|
296
|
+
const newState = {
|
|
297
|
+
...shareDBDoc.data,
|
|
298
|
+
files: {
|
|
299
|
+
...shareDBDoc.data.files,
|
|
300
|
+
[newFileId]: {
|
|
301
|
+
name: fileName,
|
|
302
|
+
text: '',
|
|
303
|
+
},
|
|
304
|
+
},
|
|
305
|
+
};
|
|
306
|
+
const op = diff(shareDBDoc.data, newState);
|
|
307
|
+
shareDBDoc.submitOp(op);
|
|
308
|
+
return newFileId;
|
|
309
|
+
};
|
|
310
|
+
/**
|
|
311
|
+
* Ensures a file exists, creating it if necessary
|
|
312
|
+
*/
|
|
313
|
+
export const ensureFileExists = (shareDBDoc, fileName) => {
|
|
314
|
+
let fileId = resolveFileId(fileName, shareDBDoc);
|
|
315
|
+
if (!fileId) {
|
|
316
|
+
// File doesn't exist, create it
|
|
317
|
+
fileId = createNewFile(shareDBDoc, fileName);
|
|
318
|
+
}
|
|
319
|
+
return fileId;
|
|
320
|
+
};
|
|
321
|
+
/**
|
|
322
|
+
* Clears the content of a file
|
|
323
|
+
*/
|
|
324
|
+
export const clearFileContent = (shareDBDoc, fileId) => {
|
|
325
|
+
const currentFile = shareDBDoc.data.files[fileId];
|
|
326
|
+
if (currentFile && currentFile.text) {
|
|
327
|
+
// Clear the file content
|
|
328
|
+
const newState = {
|
|
329
|
+
...shareDBDoc.data,
|
|
330
|
+
files: {
|
|
331
|
+
...shareDBDoc.data.files,
|
|
332
|
+
[fileId]: {
|
|
333
|
+
...currentFile,
|
|
334
|
+
text: '',
|
|
335
|
+
},
|
|
336
|
+
},
|
|
337
|
+
};
|
|
338
|
+
const op = diff(shareDBDoc.data, newState);
|
|
339
|
+
shareDBDoc.submitOp(op);
|
|
340
|
+
}
|
|
341
|
+
};
|
|
342
|
+
/**
|
|
343
|
+
* Appends a line to a file using OT operations
|
|
344
|
+
*/
|
|
345
|
+
export const appendLineToFile = (shareDBDoc, fileId, line) => {
|
|
346
|
+
const currentFile = shareDBDoc.data.files[fileId];
|
|
347
|
+
const currentContent = currentFile?.text || '';
|
|
348
|
+
const newContent = currentContent + line + '\n';
|
|
349
|
+
const newDocState = {
|
|
350
|
+
...shareDBDoc.data,
|
|
351
|
+
files: {
|
|
352
|
+
...shareDBDoc.data.files,
|
|
353
|
+
[fileId]: {
|
|
354
|
+
...currentFile,
|
|
355
|
+
text: newContent,
|
|
356
|
+
},
|
|
357
|
+
},
|
|
358
|
+
};
|
|
359
|
+
shareDBDoc.submitOp(diff(shareDBDoc.data, newDocState));
|
|
360
|
+
};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { dateToTimestamp } from '@vizhub/viz-utils';
|
|
2
|
+
import { diff } from '../../ot.js';
|
|
3
|
+
/**
|
|
4
|
+
* Handles errors by adding an error message to the chat and clearing AI state
|
|
5
|
+
*/
|
|
6
|
+
export const handleError = (shareDBDoc, chatId, error, res) => {
|
|
7
|
+
console.error('[handleAIChatMessage] error:', error);
|
|
8
|
+
// Clear scratchpad and add error message on error
|
|
9
|
+
try {
|
|
10
|
+
const errorResponse = {
|
|
11
|
+
id: `error-${Date.now()}`,
|
|
12
|
+
role: 'assistant',
|
|
13
|
+
content: 'Sorry, I encountered an error while processing your message. Please try again.',
|
|
14
|
+
timestamp: dateToTimestamp(new Date()),
|
|
15
|
+
};
|
|
16
|
+
const errorOp = diff(shareDBDoc.data, {
|
|
17
|
+
...shareDBDoc.data,
|
|
18
|
+
chats: {
|
|
19
|
+
...shareDBDoc.data.chats,
|
|
20
|
+
[chatId]: {
|
|
21
|
+
...shareDBDoc.data.chats[chatId],
|
|
22
|
+
messages: [
|
|
23
|
+
...shareDBDoc.data.chats[chatId].messages,
|
|
24
|
+
errorResponse,
|
|
25
|
+
],
|
|
26
|
+
aiScratchpad: undefined,
|
|
27
|
+
aiStatus: undefined,
|
|
28
|
+
updatedAt: dateToTimestamp(new Date()),
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
shareDBDoc.submitOp(errorOp);
|
|
33
|
+
}
|
|
34
|
+
catch (opError) {
|
|
35
|
+
console.error('[handleAIChatMessage] error handling error state:', opError);
|
|
36
|
+
}
|
|
37
|
+
if (res) {
|
|
38
|
+
res.status(500).json({
|
|
39
|
+
error: 'Internal server error',
|
|
40
|
+
message: error.message,
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
/**
|
|
45
|
+
* Handles errors in background processing (without HTTP response)
|
|
46
|
+
*/
|
|
47
|
+
export const handleBackgroundError = (shareDBDoc, chatId, error) => {
|
|
48
|
+
handleError(shareDBDoc, chatId, error, null);
|
|
49
|
+
};
|