vzcode 2.3.0 → 2.4.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/package.json +3 -2
- package/src/cli.ts +208 -0
- package/src/client/AIAssist/AIAssistCodeMirrorKeyMap.ts +42 -0
- package/src/client/AIAssist/AIAssistWidget/index.tsx +135 -0
- package/src/client/AIAssist/AIAssistWidget/style.scss +47 -0
- package/src/client/AIAssist/Spinner.jsx +76 -0
- package/src/client/AIAssist/formatFile.ts +27 -0
- package/src/client/AIAssist/generateFilesContext.ts +25 -0
- package/src/client/AIAssist/index.ts +2 -0
- package/src/client/AIAssist/startAIAssist.ts +95 -0
- package/src/client/App/index.tsx +152 -0
- package/src/client/App/style.scss +82 -0
- package/src/client/App/usePending.ts +51 -0
- package/src/client/App/useShareDB.ts +161 -0
- package/src/client/CodeEditor/Copilot/index.ts +74 -0
- package/src/client/CodeEditor/InteractiveWidgets/boolToggler.ts +12 -0
- package/src/client/CodeEditor/InteractiveWidgets/colorPicker.ts +68 -0
- package/src/client/CodeEditor/InteractiveWidgets/colorsInTextPlugin.ts +174 -0
- package/src/client/CodeEditor/InteractiveWidgets/highlightWidgets.ts +79 -0
- package/src/client/CodeEditor/InteractiveWidgets/index.ts +44 -0
- package/src/client/CodeEditor/InteractiveWidgets/markdownCheckboxToggler.ts +12 -0
- package/src/client/CodeEditor/InteractiveWidgets/numberDragger.ts +20 -0
- package/src/client/CodeEditor/InteractiveWidgets/rotateWidget.ts +143 -0
- package/src/client/CodeEditor/InteractiveWidgets/urlClicker.test.ts +48 -0
- package/src/client/CodeEditor/InteractiveWidgets/urlClicker.ts +12 -0
- package/src/client/CodeEditor/InteractiveWidgets/vec2Slider.ts +25 -0
- package/src/client/CodeEditor/basicSetup.ts +73 -0
- package/src/client/CodeEditor/getOrCreateEditor.tsx +615 -0
- package/src/client/CodeEditor/index.tsx +202 -0
- package/src/client/CodeEditor/json1PresenceBroadcast.ts +41 -0
- package/src/client/CodeEditor/json1PresenceDisplay.ts +350 -0
- package/src/client/CodeEditor/rainbowBrackets.ts +122 -0
- package/src/client/CodeEditor/style.scss +156 -0
- package/src/client/CodeEditor/typescriptExtension/worker.ts +32 -0
- package/src/client/CodeErrorOverlay/index.tsx +77 -0
- package/src/client/CodeErrorOverlay/style.scss +33 -0
- package/src/client/Fonts/fonts.ts +8 -0
- package/src/client/Icons/AdjustmentSVG.tsx +19 -0
- package/src/client/Icons/BugSVG.tsx +20 -0
- package/src/client/Icons/CloseSVG.tsx +19 -0
- package/src/client/Icons/CssSVG.tsx +16 -0
- package/src/client/Icons/DirectoryArrowSVG.tsx +19 -0
- package/src/client/Icons/EditSVG.tsx +19 -0
- package/src/client/Icons/FileSVG.tsx +19 -0
- package/src/client/Icons/FolderSVG.tsx +20 -0
- package/src/client/Icons/GearSVG.tsx +16 -0
- package/src/client/Icons/HtmlSVG.tsx +16 -0
- package/src/client/Icons/JavaScriptSVG.tsx +16 -0
- package/src/client/Icons/JsonSVG.tsx +16 -0
- package/src/client/Icons/MarkdownSVG.tsx +16 -0
- package/src/client/Icons/MicOffSVG.tsx +35 -0
- package/src/client/Icons/MicSVG.tsx +25 -0
- package/src/client/Icons/NewSVG.tsx +19 -0
- package/src/client/Icons/PinSVG.tsx +14 -0
- package/src/client/Icons/PlaySVG.tsx +16 -0
- package/src/client/Icons/QuestionMarkSVG.tsx +18 -0
- package/src/client/Icons/ReactSVG.tsx +16 -0
- package/src/client/Icons/SearchSVG.tsx +20 -0
- package/src/client/Icons/SparklesSVG.tsx +23 -0
- package/src/client/Icons/SplitEditorSVG.tsx +34 -0
- package/src/client/Icons/StopSVG.tsx +15 -0
- package/src/client/Icons/SvelteSVG.tsx +16 -0
- package/src/client/Icons/TrashSVG.tsx +19 -0
- package/src/client/Icons/TypeScriptSVG.tsx +16 -0
- package/src/client/Icons/index.tsx +26 -0
- package/src/client/ImageViewer/index.tsx +91 -0
- package/src/client/ImageViewer/style.scss +41 -0
- package/src/client/PresenceNotifications/PresenceNotifications.test.ts +134 -0
- package/src/client/PresenceNotifications/index.tsx +158 -0
- package/src/client/PresenceNotifications/style.scss +17 -0
- package/src/client/RunCodeWidget/index.tsx +117 -0
- package/src/client/RunCodeWidget/style.scss +24 -0
- package/src/client/SplitPaneResizeContext.tsx +159 -0
- package/src/client/TabList/Tab.tsx +73 -0
- package/src/client/TabList/index.tsx +35 -0
- package/src/client/TabList/style.scss +51 -0
- package/src/client/VZCodeContext/index.tsx +29 -0
- package/src/client/VZCodeContext/types.ts +248 -0
- package/src/client/VZCodeContext/useVZCodeState.ts +602 -0
- package/src/client/VZKeyboardShortcutsDoc.tsx +148 -0
- package/src/client/VZLeft.tsx +28 -0
- package/src/client/VZMiddle.tsx +172 -0
- package/src/client/VZResizer/index.tsx +128 -0
- package/src/client/VZResizer/styles.scss +33 -0
- package/src/client/VZRight.tsx +113 -0
- package/src/client/VZSettings.tsx +275 -0
- package/src/client/VZSidebar/AIChat/ChatInput.tsx +243 -0
- package/src/client/VZSidebar/AIChat/DiffView.scss +136 -0
- package/src/client/VZSidebar/AIChat/DiffView.tsx +176 -0
- package/src/client/VZSidebar/AIChat/Message.tsx +79 -0
- package/src/client/VZSidebar/AIChat/MessageList.tsx +190 -0
- package/src/client/VZSidebar/AIChat/StreamingMessage.tsx +35 -0
- package/src/client/VZSidebar/AIChat/ThinkingScratchpad.tsx +156 -0
- package/src/client/VZSidebar/AIChat/TypingIndicator.tsx +19 -0
- package/src/client/VZSidebar/AIChat/index.tsx +357 -0
- package/src/client/VZSidebar/AIChat/styles.scss +646 -0
- package/src/client/VZSidebar/AIChat/useSpeechRecognition.ts +117 -0
- package/src/client/VZSidebar/CreateDirModal.tsx +111 -0
- package/src/client/VZSidebar/CreateFileModal.tsx +111 -0
- package/src/client/VZSidebar/DeleteConfirmationModal.tsx +76 -0
- package/src/client/VZSidebar/DirectoryListing.tsx +89 -0
- package/src/client/VZSidebar/EmptyState.tsx +11 -0
- package/src/client/VZSidebar/FileListing.tsx +91 -0
- package/src/client/VZSidebar/FileTypeIcon.tsx +49 -0
- package/src/client/VZSidebar/Item.tsx +237 -0
- package/src/client/VZSidebar/Listing.tsx +47 -0
- package/src/client/VZSidebar/Search.tsx +527 -0
- package/src/client/VZSidebar/VisualEditor.tsx +282 -0
- package/src/client/VZSidebar/VoiceChatModal.tsx +121 -0
- package/src/client/VZSidebar/aiCopyPaste.ts +199 -0
- package/src/client/VZSidebar/index.tsx +728 -0
- package/src/client/VZSidebar/styles.scss +612 -0
- package/src/client/VZSidebar/useDragAndDrop.tsx +408 -0
- package/src/client/bootstrap.ts +26 -0
- package/src/client/buildWorker.ts +3 -0
- package/src/client/featureFlags.ts +8 -0
- package/src/client/generateRequestId.ts +6 -0
- package/src/client/getFileTree.ts +53 -0
- package/src/client/index.ts +19 -0
- package/src/client/main.jsx +10 -0
- package/src/client/presenceColor.ts +8 -0
- package/src/client/shouldTriggerRun.ts +31 -0
- package/src/client/sortFileTree.ts +21 -0
- package/src/client/tabsSearchParameters.test.ts +79 -0
- package/src/client/tabsSearchParameters.ts +137 -0
- package/src/client/themes/index.ts +7 -0
- package/src/client/themes/themes.ts +77 -0
- package/src/client/themes/uiwImports.ts +32 -0
- package/src/client/themes/useDynamicTheme.ts +29 -0
- package/src/client/themes/vizhubTheme/colors.ts +80 -0
- package/src/client/themes/vizhubTheme/index.ts +193 -0
- package/src/client/useActions.ts +288 -0
- package/src/client/useESLint/index.ts +72 -0
- package/src/client/useESLint/worker.ts +113 -0
- package/src/client/useEditorCache.ts +35 -0
- package/src/client/useFileCRUD.ts +228 -0
- package/src/client/useKeyboardShortcuts.ts +451 -0
- package/src/client/useOpenDirectories.ts +99 -0
- package/src/client/usePresenceAutoFollow.ts +85 -0
- package/src/client/usePrettier/index.ts +178 -0
- package/src/client/usePrettier/worker.ts +109 -0
- package/src/client/useRunCode.tsx +15 -0
- package/src/client/useRuntimeError.ts +32 -0
- package/src/client/useSubmitOperation.ts +15 -0
- package/src/client/useURLSync.ts +137 -0
- package/src/client/usernameLocalStorage.ts +43 -0
- package/src/client/utils/fileExtension.ts +10 -0
- package/src/client/utils/isImageFile.ts +12 -0
- package/src/client/vzReducer/aiChatReducer.ts +44 -0
- package/src/client/vzReducer/closeTabReducer.test.ts +205 -0
- package/src/client/vzReducer/closeTabsReducer.ts +96 -0
- package/src/client/vzReducer/createInitialState.ts +38 -0
- package/src/client/vzReducer/editorNoLongerWantsFocusReducer.ts +9 -0
- package/src/client/vzReducer/findPane.ts +19 -0
- package/src/client/vzReducer/index.ts +250 -0
- package/src/client/vzReducer/openTabReducer.test.ts +127 -0
- package/src/client/vzReducer/openTabReducer.ts +74 -0
- package/src/client/vzReducer/searchReducer.test.ts +292 -0
- package/src/client/vzReducer/searchReducer.ts +181 -0
- package/src/client/vzReducer/setActiveFileIdReducer.ts +28 -0
- package/src/client/vzReducer/setActiveFileLeftRightReducer.ts +76 -0
- package/src/client/vzReducer/setIsDocOpenReducer.ts +9 -0
- package/src/client/vzReducer/setIsSettingsOpenReducer.ts +9 -0
- package/src/client/vzReducer/setThemeReducer.ts +9 -0
- package/src/client/vzReducer/setUsernameReducer.ts +9 -0
- package/src/client/vzReducer/splitCurrentPaneReducer.ts +66 -0
- package/src/client/vzReducer/toggleAutoFollowReducer.ts +12 -0
- package/src/client/vzReducer/updatePane.ts +40 -0
- package/src/client/vzReducer/updatePresenceIndicatorReducer.ts +30 -0
- package/src/client/vzReducer/visualEditorReducer.ts +9 -0
- package/src/ot.ts +24 -0
- package/src/randomId.ts +15 -0
- package/src/reactRouterExports.ts +15 -0
- package/src/runCode.ts +27 -0
- package/src/server/aiChatHandler/aiEditing.ts +151 -0
- package/src/server/aiChatHandler/chatOperations.ts +476 -0
- package/src/server/aiChatHandler/errorHandling.ts +66 -0
- package/src/server/aiChatHandler/index.ts +199 -0
- package/src/server/aiChatHandler/llmStreaming.ts +419 -0
- package/src/server/aiChatHandler/validation.ts +24 -0
- package/src/server/computeInitialDocument.ts +220 -0
- package/src/server/config.ts +24 -0
- package/src/server/featureFlags.ts +8 -0
- package/src/server/generateAIResponse.ts +188 -0
- package/src/server/handleAIAssist.ts +53 -0
- package/src/server/handleAIChatMessage.ts +2 -0
- package/src/server/handleAICopilot.ts +97 -0
- package/src/server/index.ts +379 -0
- package/src/server/isDirectory.ts +1 -0
- package/src/server/livekit.ts +24 -0
- package/src/server/prettier.ts +115 -0
- package/src/server/setupEnv.ts +8 -0
- package/src/submitOperation.ts +25 -0
- package/src/types.ts +205 -0
- package/src/utils/fileDiff.ts +117 -0
- package/src/vite-env.d.ts +10 -0
|
@@ -0,0 +1,602 @@
|
|
|
1
|
+
import {
|
|
2
|
+
useCallback,
|
|
3
|
+
useMemo,
|
|
4
|
+
useReducer,
|
|
5
|
+
useRef,
|
|
6
|
+
useState,
|
|
7
|
+
} from 'react';
|
|
8
|
+
import { VizContent } from '@vizhub/viz-types';
|
|
9
|
+
import { defaultTheme, useDynamicTheme } from '../themes';
|
|
10
|
+
import { useActions } from '../useActions';
|
|
11
|
+
import { useEditorCache } from '../useEditorCache';
|
|
12
|
+
import { useFileCRUD } from '../useFileCRUD';
|
|
13
|
+
import { useKeyboardShortcuts } from '../useKeyboardShortcuts';
|
|
14
|
+
import { useOpenDirectories } from '../useOpenDirectories';
|
|
15
|
+
import { usePrettier } from '../usePrettier';
|
|
16
|
+
import { useRunCode } from '../useRunCode';
|
|
17
|
+
import { useRuntimeError } from '../useRuntimeError';
|
|
18
|
+
import { useURLSync } from '../useURLSync';
|
|
19
|
+
import {
|
|
20
|
+
createInitialState,
|
|
21
|
+
vzReducer,
|
|
22
|
+
} from '../vzReducer';
|
|
23
|
+
import { findPane } from '../vzReducer/findPane';
|
|
24
|
+
import { usePresenceAutoFollow } from '../usePresenceAutoFollow';
|
|
25
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
26
|
+
import {
|
|
27
|
+
VZCodeContextValue,
|
|
28
|
+
VZCodeProviderProps,
|
|
29
|
+
} from './types';
|
|
30
|
+
|
|
31
|
+
export const useVZCodeState = ({
|
|
32
|
+
content,
|
|
33
|
+
shareDBDoc,
|
|
34
|
+
submitOperation,
|
|
35
|
+
localPresence,
|
|
36
|
+
docPresence,
|
|
37
|
+
prettierWorker,
|
|
38
|
+
initialUsername,
|
|
39
|
+
codeError = null,
|
|
40
|
+
connected,
|
|
41
|
+
pending,
|
|
42
|
+
liveKitToken,
|
|
43
|
+
setLiveKitToken,
|
|
44
|
+
liveKitRoomName,
|
|
45
|
+
setLiveKitRoom,
|
|
46
|
+
liveKitConnection,
|
|
47
|
+
setLiveKitConnection,
|
|
48
|
+
aiChatEndpoint,
|
|
49
|
+
aiChatOptions,
|
|
50
|
+
autoForkAndRetryAI,
|
|
51
|
+
clearStoredAIPrompt,
|
|
52
|
+
getStoredAIPrompt,
|
|
53
|
+
additionalWidgets,
|
|
54
|
+
iframeRef: externalIframeRef,
|
|
55
|
+
handleChatError,
|
|
56
|
+
}: Omit<
|
|
57
|
+
VZCodeProviderProps,
|
|
58
|
+
'children'
|
|
59
|
+
>): VZCodeContextValue => {
|
|
60
|
+
// Auto-run Prettier after local changes.
|
|
61
|
+
const { prettierError, runPrettierRef } = usePrettier({
|
|
62
|
+
shareDBDoc,
|
|
63
|
+
submitOperation,
|
|
64
|
+
prettierWorker,
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
// Handle runtime errors from the iframe
|
|
68
|
+
const {
|
|
69
|
+
runtimeError,
|
|
70
|
+
handleRuntimeError,
|
|
71
|
+
clearRuntimeError,
|
|
72
|
+
} = useRuntimeError();
|
|
73
|
+
|
|
74
|
+
const runCodeRef = useRunCode(submitOperation);
|
|
75
|
+
|
|
76
|
+
const sidebarRef = useRef(null);
|
|
77
|
+
|
|
78
|
+
const codeEditorRef = useRef(null);
|
|
79
|
+
|
|
80
|
+
// Use external iframeRef if provided, otherwise create our own
|
|
81
|
+
const iframeRef = externalIframeRef || useRef(null);
|
|
82
|
+
|
|
83
|
+
// The error message shows errors in order of priority:
|
|
84
|
+
// * `runtimeError` - errors from runtime execution, highest priority
|
|
85
|
+
// * `prettierError` - errors from Prettier, client-side only
|
|
86
|
+
// * `codeError` - errors from an external source, such as
|
|
87
|
+
// build-time errors or intercepted runtime errors.
|
|
88
|
+
// Runtime errors are prioritized since they indicate immediate execution issues.
|
|
89
|
+
// Prettier errors are next since they surface syntax errors that are likely
|
|
90
|
+
// to be useful to the user.
|
|
91
|
+
const errorMessage: string | null = runtimeError
|
|
92
|
+
? runtimeError
|
|
93
|
+
: prettierError
|
|
94
|
+
? prettierError
|
|
95
|
+
: codeError;
|
|
96
|
+
|
|
97
|
+
// Set up the reducer that manages much of the application state.
|
|
98
|
+
// See https://react.dev/reference/react/useReducer
|
|
99
|
+
const [state, dispatch] = useReducer(
|
|
100
|
+
vzReducer,
|
|
101
|
+
{ defaultTheme, initialUsername },
|
|
102
|
+
createInitialState,
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
// Unpack state.
|
|
106
|
+
const {
|
|
107
|
+
pane,
|
|
108
|
+
activePaneId,
|
|
109
|
+
theme,
|
|
110
|
+
search,
|
|
111
|
+
isSearchOpen,
|
|
112
|
+
isVisualEditorOpen,
|
|
113
|
+
isAIChatOpen,
|
|
114
|
+
aiChatFocused,
|
|
115
|
+
isSettingsOpen,
|
|
116
|
+
isDocOpen,
|
|
117
|
+
editorWantsFocus,
|
|
118
|
+
username,
|
|
119
|
+
enableAutoFollow,
|
|
120
|
+
sidebarPresenceIndicators,
|
|
121
|
+
aiChatMode,
|
|
122
|
+
} = state;
|
|
123
|
+
|
|
124
|
+
const activePane = findPane(pane, activePaneId);
|
|
125
|
+
if (activePane.type !== 'leafPane') {
|
|
126
|
+
// Should never happen
|
|
127
|
+
throw new Error('Expected leafPane');
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Functions for dispatching actions to the reducer.
|
|
131
|
+
const {
|
|
132
|
+
setActiveFileId,
|
|
133
|
+
setActiveFileLeft,
|
|
134
|
+
setActiveFileRight,
|
|
135
|
+
openTab,
|
|
136
|
+
closeTabs,
|
|
137
|
+
setTheme,
|
|
138
|
+
setIsSearchOpen,
|
|
139
|
+
setSearch,
|
|
140
|
+
setSearchResults,
|
|
141
|
+
setSearchFileVisibility,
|
|
142
|
+
setSearchLineVisibility,
|
|
143
|
+
setSearchFocusedIndex,
|
|
144
|
+
toggleSearchFocused,
|
|
145
|
+
setIsVisualEditorOpen,
|
|
146
|
+
setIsAIChatOpen,
|
|
147
|
+
toggleAIChatFocused,
|
|
148
|
+
setAIChatMode,
|
|
149
|
+
setIsSettingsOpen,
|
|
150
|
+
setIsDocOpen,
|
|
151
|
+
closeSettings,
|
|
152
|
+
closeDoc,
|
|
153
|
+
editorNoLongerWantsFocus,
|
|
154
|
+
setUsername,
|
|
155
|
+
toggleAutoFollow,
|
|
156
|
+
updatePresenceIndicator,
|
|
157
|
+
splitCurrentPane,
|
|
158
|
+
} = useActions(dispatch);
|
|
159
|
+
|
|
160
|
+
// Sync tab state to the URL.
|
|
161
|
+
useURLSync({
|
|
162
|
+
content,
|
|
163
|
+
openTab,
|
|
164
|
+
setActiveFileId,
|
|
165
|
+
tabList: activePane.tabList,
|
|
166
|
+
activeFileId: activePane.activeFileId,
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
// The set of open directories.
|
|
170
|
+
const { isDirectoryOpen, toggleDirectory } =
|
|
171
|
+
useOpenDirectories({ activePane, content });
|
|
172
|
+
|
|
173
|
+
// Cache of CodeMirror editors by file id.
|
|
174
|
+
const editorCache = useEditorCache();
|
|
175
|
+
|
|
176
|
+
// Handle dynamic theme changes.
|
|
177
|
+
useDynamicTheme(editorCache, theme);
|
|
178
|
+
|
|
179
|
+
// Handle file CRUD operations (Create, Read, Update, Delete)
|
|
180
|
+
const {
|
|
181
|
+
createFile,
|
|
182
|
+
renameFile,
|
|
183
|
+
deleteFile,
|
|
184
|
+
createDirectory,
|
|
185
|
+
renameDirectory,
|
|
186
|
+
deleteDirectory,
|
|
187
|
+
deleteAllFiles,
|
|
188
|
+
} = useFileCRUD({
|
|
189
|
+
submitOperation,
|
|
190
|
+
closeTabs,
|
|
191
|
+
openTab,
|
|
192
|
+
editorCache,
|
|
193
|
+
content,
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
// State to control the create file modal's visibility
|
|
197
|
+
const [isCreateFileModalOpen, setIsCreateFileModalOpen] =
|
|
198
|
+
useState(false);
|
|
199
|
+
|
|
200
|
+
const handleOpenCreateFileModal = useCallback(() => {
|
|
201
|
+
setIsCreateFileModalOpen(true);
|
|
202
|
+
}, []);
|
|
203
|
+
|
|
204
|
+
const handleCloseCreateFileModal = useCallback(() => {
|
|
205
|
+
setIsCreateFileModalOpen(false);
|
|
206
|
+
}, []);
|
|
207
|
+
|
|
208
|
+
const handleCreateFileClick = useCallback(
|
|
209
|
+
(newFileName: string) => {
|
|
210
|
+
createFile(newFileName);
|
|
211
|
+
setIsCreateFileModalOpen(false);
|
|
212
|
+
},
|
|
213
|
+
[createFile, setIsCreateFileModalOpen],
|
|
214
|
+
);
|
|
215
|
+
|
|
216
|
+
// State to control the create directory modal's visibility
|
|
217
|
+
const [isCreateDirModalOpen, setIsCreateDirModalOpen] =
|
|
218
|
+
useState(false);
|
|
219
|
+
|
|
220
|
+
const handleOpenCreateDirModal = useCallback(() => {
|
|
221
|
+
setIsCreateDirModalOpen(true);
|
|
222
|
+
}, []);
|
|
223
|
+
|
|
224
|
+
const handleCloseCreateDirModal = useCallback(() => {
|
|
225
|
+
setIsCreateDirModalOpen(false);
|
|
226
|
+
}, []);
|
|
227
|
+
|
|
228
|
+
const handleCreateDirClick = useCallback(
|
|
229
|
+
(newDirName: string) => {
|
|
230
|
+
createDirectory(newDirName);
|
|
231
|
+
setIsCreateDirModalOpen(false);
|
|
232
|
+
},
|
|
233
|
+
[createDirectory, setIsCreateDirModalOpen],
|
|
234
|
+
);
|
|
235
|
+
|
|
236
|
+
// Isolate the files object from the document.
|
|
237
|
+
const files = content ? content.files : null;
|
|
238
|
+
|
|
239
|
+
useKeyboardShortcuts({
|
|
240
|
+
closeTabs,
|
|
241
|
+
activeFileId: activePane.activeFileId,
|
|
242
|
+
activePaneId,
|
|
243
|
+
handleOpenCreateFileModal,
|
|
244
|
+
setActiveFileLeft,
|
|
245
|
+
setActiveFileRight,
|
|
246
|
+
toggleSearchFocused,
|
|
247
|
+
runPrettierRef,
|
|
248
|
+
runCodeRef,
|
|
249
|
+
sidebarRef,
|
|
250
|
+
editorCache,
|
|
251
|
+
codeEditorRef,
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
// Track the currently hovered file id.
|
|
255
|
+
const [hoveredItemId, setHoveredItemId] = useState(null);
|
|
256
|
+
|
|
257
|
+
// Handle presence-based auto-following
|
|
258
|
+
usePresenceAutoFollow({
|
|
259
|
+
docPresence,
|
|
260
|
+
enableAutoFollow,
|
|
261
|
+
openTab,
|
|
262
|
+
activePane,
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
// Livekit Voice Chat Modal
|
|
266
|
+
const [voiceChatModalOpen, setVoiceChatModalOpen] =
|
|
267
|
+
useState(false);
|
|
268
|
+
|
|
269
|
+
const [aiChatMessage, setAIChatMessage] = useState('');
|
|
270
|
+
|
|
271
|
+
const DEBUG = false;
|
|
272
|
+
|
|
273
|
+
// Compute isLoading based on the current chat's aiStatus
|
|
274
|
+
const [currentChatId] = useState(() => uuidv4());
|
|
275
|
+
const [selectedChatId, setSelectedChatId] = useState<
|
|
276
|
+
string | null
|
|
277
|
+
>(null);
|
|
278
|
+
|
|
279
|
+
// Use selectedChatId if available, otherwise use currentChatId for backward compatibility
|
|
280
|
+
const activeChatId = selectedChatId || currentChatId;
|
|
281
|
+
const currentChat = content?.chats?.[activeChatId];
|
|
282
|
+
const isLoading = currentChat?.aiStatus === 'generating';
|
|
283
|
+
|
|
284
|
+
// Message history for up/down arrow navigation - using ShareDB document data
|
|
285
|
+
const messageHistory = useMemo(() => {
|
|
286
|
+
if (!currentChat?.messages) return [];
|
|
287
|
+
// Extract user messages in chronological order for history navigation
|
|
288
|
+
return currentChat.messages
|
|
289
|
+
.filter((msg) => msg.role === 'user')
|
|
290
|
+
.map((msg) => msg.content);
|
|
291
|
+
}, [currentChat?.messages]);
|
|
292
|
+
|
|
293
|
+
const [historyIndex, setHistoryIndex] = useState(-1);
|
|
294
|
+
const [currentDraft, setCurrentDraft] = useState('');
|
|
295
|
+
|
|
296
|
+
const [aiErrorMessage, setAIErrorMessageState] = useState<
|
|
297
|
+
string | null
|
|
298
|
+
>(null);
|
|
299
|
+
|
|
300
|
+
// Create the combined setAIErrorMessage function that calls both state setter and external callback
|
|
301
|
+
const setAIErrorMessage = useCallback(
|
|
302
|
+
(error: string | null) => {
|
|
303
|
+
setAIErrorMessageState(error);
|
|
304
|
+
if (error && handleChatError) {
|
|
305
|
+
handleChatError(error);
|
|
306
|
+
}
|
|
307
|
+
},
|
|
308
|
+
[handleChatError],
|
|
309
|
+
);
|
|
310
|
+
|
|
311
|
+
// Message history navigation functions
|
|
312
|
+
const navigateMessageHistoryUp = useCallback(() => {
|
|
313
|
+
if (messageHistory.length === 0) return;
|
|
314
|
+
|
|
315
|
+
// If we're not navigating history, save current draft
|
|
316
|
+
if (historyIndex === -1) {
|
|
317
|
+
setCurrentDraft(aiChatMessage);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
const newIndex =
|
|
321
|
+
historyIndex < messageHistory.length - 1
|
|
322
|
+
? historyIndex + 1
|
|
323
|
+
: historyIndex;
|
|
324
|
+
setHistoryIndex(newIndex);
|
|
325
|
+
setAIChatMessage(
|
|
326
|
+
messageHistory[messageHistory.length - 1 - newIndex],
|
|
327
|
+
);
|
|
328
|
+
}, [messageHistory, historyIndex, aiChatMessage]);
|
|
329
|
+
|
|
330
|
+
const navigateMessageHistoryDown = useCallback(() => {
|
|
331
|
+
if (historyIndex === -1) return;
|
|
332
|
+
|
|
333
|
+
const newIndex = historyIndex - 1;
|
|
334
|
+
if (newIndex === -1) {
|
|
335
|
+
// Return to draft
|
|
336
|
+
setHistoryIndex(-1);
|
|
337
|
+
setAIChatMessage(currentDraft);
|
|
338
|
+
setCurrentDraft('');
|
|
339
|
+
} else {
|
|
340
|
+
setHistoryIndex(newIndex);
|
|
341
|
+
setAIChatMessage(
|
|
342
|
+
messageHistory[
|
|
343
|
+
messageHistory.length - 1 - newIndex
|
|
344
|
+
],
|
|
345
|
+
);
|
|
346
|
+
}
|
|
347
|
+
}, [messageHistory, historyIndex, currentDraft]);
|
|
348
|
+
|
|
349
|
+
const resetMessageHistoryNavigation = useCallback(() => {
|
|
350
|
+
if (historyIndex !== -1) {
|
|
351
|
+
setHistoryIndex(-1);
|
|
352
|
+
setCurrentDraft('');
|
|
353
|
+
}
|
|
354
|
+
}, [historyIndex]);
|
|
355
|
+
|
|
356
|
+
const handleSendMessage = useCallback(
|
|
357
|
+
async (
|
|
358
|
+
messageToSend?: string,
|
|
359
|
+
options?: Record<string, string>,
|
|
360
|
+
) => {
|
|
361
|
+
DEBUG &&
|
|
362
|
+
console.log(
|
|
363
|
+
'AIChat: handleSendMessage called with:',
|
|
364
|
+
messageToSend,
|
|
365
|
+
'aiChatMessage:',
|
|
366
|
+
aiChatMessage,
|
|
367
|
+
);
|
|
368
|
+
const messageContent = messageToSend || aiChatMessage;
|
|
369
|
+
|
|
370
|
+
if (
|
|
371
|
+
!messageContent ||
|
|
372
|
+
typeof messageContent !== 'string' ||
|
|
373
|
+
!messageContent.trim() ||
|
|
374
|
+
isLoading
|
|
375
|
+
) {
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
const currentPrompt = aiChatMessage.trim();
|
|
380
|
+
setAIChatMessage('');
|
|
381
|
+
|
|
382
|
+
// Reset history navigation when sending a message
|
|
383
|
+
setHistoryIndex(-1);
|
|
384
|
+
setCurrentDraft('');
|
|
385
|
+
|
|
386
|
+
setAIErrorMessage(null); // Clear any previous errors
|
|
387
|
+
|
|
388
|
+
// Call backend endpoint for AI response
|
|
389
|
+
try {
|
|
390
|
+
const response = await fetch(aiChatEndpoint, {
|
|
391
|
+
method: 'POST',
|
|
392
|
+
headers: {
|
|
393
|
+
'Content-Type': 'application/json',
|
|
394
|
+
},
|
|
395
|
+
body: JSON.stringify({
|
|
396
|
+
...aiChatOptions,
|
|
397
|
+
content: messageContent.trim(),
|
|
398
|
+
chatId: activeChatId,
|
|
399
|
+
mode: aiChatMode,
|
|
400
|
+
...options,
|
|
401
|
+
}),
|
|
402
|
+
});
|
|
403
|
+
|
|
404
|
+
if (!response.ok) {
|
|
405
|
+
throw new Error(
|
|
406
|
+
`HTTP error! status: ${response.status}`,
|
|
407
|
+
);
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
// Parse the response to check for errors
|
|
411
|
+
const responseData = await response.json();
|
|
412
|
+
|
|
413
|
+
// Check if the response contains a VizHub error
|
|
414
|
+
if (
|
|
415
|
+
responseData.outcome === 'failure' &&
|
|
416
|
+
responseData.error
|
|
417
|
+
) {
|
|
418
|
+
const errorMessage = responseData.error.message;
|
|
419
|
+
|
|
420
|
+
// Check if this is the specific permission error that should trigger auto-fork
|
|
421
|
+
if (
|
|
422
|
+
errorMessage ===
|
|
423
|
+
'You do not have permission to use AI chat on this visualization. Only users with edit access can use this feature. Fork the viz to edit it.'
|
|
424
|
+
) {
|
|
425
|
+
// Trigger auto-fork instead of showing error
|
|
426
|
+
try {
|
|
427
|
+
await autoForkAndRetryAI?.(
|
|
428
|
+
currentPrompt,
|
|
429
|
+
aiChatMode,
|
|
430
|
+
);
|
|
431
|
+
// If we reach here, the fork was successful and redirect should happen
|
|
432
|
+
return;
|
|
433
|
+
} catch (forkError) {
|
|
434
|
+
console.error('Auto-fork failed:', forkError);
|
|
435
|
+
setAIErrorMessage(
|
|
436
|
+
'Failed to fork visualization. Please try forking manually.',
|
|
437
|
+
);
|
|
438
|
+
return;
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
// For other errors, show the error message
|
|
443
|
+
setAIErrorMessage(errorMessage);
|
|
444
|
+
return;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
// The backend handles all ShareDB operations for successful responses
|
|
448
|
+
// The loading state is now managed via ShareDB aiStatus
|
|
449
|
+
} catch (error) {
|
|
450
|
+
console.error('Error getting AI response:', error);
|
|
451
|
+
setAIErrorMessage(
|
|
452
|
+
'Failed to send message. Please try again.',
|
|
453
|
+
);
|
|
454
|
+
}
|
|
455
|
+
},
|
|
456
|
+
[
|
|
457
|
+
aiChatMessage,
|
|
458
|
+
isLoading,
|
|
459
|
+
aiChatEndpoint,
|
|
460
|
+
aiChatOptions,
|
|
461
|
+
activeChatId,
|
|
462
|
+
aiChatMode,
|
|
463
|
+
autoForkAndRetryAI,
|
|
464
|
+
],
|
|
465
|
+
);
|
|
466
|
+
|
|
467
|
+
// Return the context value
|
|
468
|
+
return {
|
|
469
|
+
content,
|
|
470
|
+
shareDBDoc,
|
|
471
|
+
submitOperation,
|
|
472
|
+
localPresence,
|
|
473
|
+
docPresence,
|
|
474
|
+
|
|
475
|
+
files,
|
|
476
|
+
createFile,
|
|
477
|
+
renameFile,
|
|
478
|
+
deleteFile,
|
|
479
|
+
createDirectory,
|
|
480
|
+
renameDirectory,
|
|
481
|
+
deleteDirectory,
|
|
482
|
+
deleteAllFiles,
|
|
483
|
+
|
|
484
|
+
setActiveFileId,
|
|
485
|
+
setActiveFileLeft,
|
|
486
|
+
setActiveFileRight,
|
|
487
|
+
|
|
488
|
+
activePaneId,
|
|
489
|
+
// Root pane
|
|
490
|
+
pane,
|
|
491
|
+
// Active leaf pane
|
|
492
|
+
activePane,
|
|
493
|
+
|
|
494
|
+
openTab,
|
|
495
|
+
closeTabs,
|
|
496
|
+
|
|
497
|
+
search,
|
|
498
|
+
isSearchOpen,
|
|
499
|
+
setIsSearchOpen,
|
|
500
|
+
setSearch,
|
|
501
|
+
setSearchResults,
|
|
502
|
+
setSearchFileVisibility,
|
|
503
|
+
setSearchLineVisibility,
|
|
504
|
+
setSearchFocusedIndex,
|
|
505
|
+
toggleSearchFocused,
|
|
506
|
+
|
|
507
|
+
isVisualEditorOpen,
|
|
508
|
+
setIsVisualEditorOpen,
|
|
509
|
+
|
|
510
|
+
isAIChatOpen,
|
|
511
|
+
setIsAIChatOpen,
|
|
512
|
+
aiChatFocused,
|
|
513
|
+
toggleAIChatFocused,
|
|
514
|
+
aiChatMode: state.aiChatMode,
|
|
515
|
+
setAIChatMode,
|
|
516
|
+
aiChatEndpoint,
|
|
517
|
+
aiChatOptions,
|
|
518
|
+
|
|
519
|
+
isSettingsOpen,
|
|
520
|
+
setIsSettingsOpen,
|
|
521
|
+
closeSettings,
|
|
522
|
+
|
|
523
|
+
isDocOpen,
|
|
524
|
+
setIsDocOpen,
|
|
525
|
+
closeDoc,
|
|
526
|
+
|
|
527
|
+
theme,
|
|
528
|
+
setTheme,
|
|
529
|
+
|
|
530
|
+
username,
|
|
531
|
+
setUsername,
|
|
532
|
+
|
|
533
|
+
isDirectoryOpen,
|
|
534
|
+
toggleDirectory,
|
|
535
|
+
|
|
536
|
+
editorCache,
|
|
537
|
+
editorWantsFocus,
|
|
538
|
+
editorNoLongerWantsFocus,
|
|
539
|
+
|
|
540
|
+
errorMessage,
|
|
541
|
+
|
|
542
|
+
handleRuntimeError,
|
|
543
|
+
clearRuntimeError,
|
|
544
|
+
|
|
545
|
+
isCreateFileModalOpen,
|
|
546
|
+
handleOpenCreateFileModal,
|
|
547
|
+
handleCloseCreateFileModal,
|
|
548
|
+
handleCreateFileClick,
|
|
549
|
+
isCreateDirModalOpen,
|
|
550
|
+
handleOpenCreateDirModal,
|
|
551
|
+
handleCloseCreateDirModal,
|
|
552
|
+
handleCreateDirClick,
|
|
553
|
+
runPrettierRef,
|
|
554
|
+
runCodeRef,
|
|
555
|
+
sidebarRef,
|
|
556
|
+
codeEditorRef,
|
|
557
|
+
iframeRef,
|
|
558
|
+
|
|
559
|
+
connected,
|
|
560
|
+
pending,
|
|
561
|
+
|
|
562
|
+
hoveredItemId,
|
|
563
|
+
setHoveredItemId,
|
|
564
|
+
|
|
565
|
+
enableAutoFollow,
|
|
566
|
+
toggleAutoFollow,
|
|
567
|
+
updatePresenceIndicator,
|
|
568
|
+
sidebarPresenceIndicators,
|
|
569
|
+
splitCurrentPane,
|
|
570
|
+
liveKitRoomName,
|
|
571
|
+
setLiveKitRoom,
|
|
572
|
+
liveKitToken,
|
|
573
|
+
setLiveKitToken,
|
|
574
|
+
liveKitConnection,
|
|
575
|
+
setLiveKitConnection,
|
|
576
|
+
voiceChatModalOpen,
|
|
577
|
+
setVoiceChatModalOpen,
|
|
578
|
+
|
|
579
|
+
aiChatMessage,
|
|
580
|
+
setAIChatMessage,
|
|
581
|
+
isLoading,
|
|
582
|
+
currentChatId,
|
|
583
|
+
selectedChatId,
|
|
584
|
+
setSelectedChatId,
|
|
585
|
+
aiErrorMessage,
|
|
586
|
+
setAIErrorMessage,
|
|
587
|
+
handleSendMessage,
|
|
588
|
+
|
|
589
|
+
// Message history navigation
|
|
590
|
+
navigateMessageHistoryUp,
|
|
591
|
+
navigateMessageHistoryDown,
|
|
592
|
+
resetMessageHistoryNavigation,
|
|
593
|
+
|
|
594
|
+
// Auto-fork functions for VizHub integration
|
|
595
|
+
autoForkAndRetryAI,
|
|
596
|
+
clearStoredAIPrompt,
|
|
597
|
+
getStoredAIPrompt,
|
|
598
|
+
|
|
599
|
+
// Additional widgets that can be rendered in AI chat messages
|
|
600
|
+
additionalWidgets,
|
|
601
|
+
};
|
|
602
|
+
};
|