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,250 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Pane,
|
|
3
|
+
PaneId,
|
|
4
|
+
PresenceIndicator,
|
|
5
|
+
SearchFileVisibility,
|
|
6
|
+
SearchResults,
|
|
7
|
+
ShareDBDoc,
|
|
8
|
+
Username,
|
|
9
|
+
} from '../../types';
|
|
10
|
+
import { VizFileId, VizContent } from '@vizhub/viz-types';
|
|
11
|
+
import { ThemeLabel } from '../themes';
|
|
12
|
+
import { closeTabsReducer } from './closeTabsReducer';
|
|
13
|
+
import { openTabReducer } from './openTabReducer';
|
|
14
|
+
import { setActiveFileIdReducer } from './setActiveFileIdReducer';
|
|
15
|
+
import {
|
|
16
|
+
setActiveFileLeftReducer,
|
|
17
|
+
setActiveFileRightReducer,
|
|
18
|
+
} from './setActiveFileLeftRightReducer';
|
|
19
|
+
import { setIsSettingsOpenReducer } from './setIsSettingsOpenReducer';
|
|
20
|
+
import { setIsDocOpenReducer } from './setIsDocOpenReducer';
|
|
21
|
+
import { setThemeReducer } from './setThemeReducer';
|
|
22
|
+
import { editorNoLongerWantsFocusReducer } from './editorNoLongerWantsFocusReducer';
|
|
23
|
+
import { setUsernameReducer } from './setUsernameReducer';
|
|
24
|
+
import {
|
|
25
|
+
setIsSearchOpenReducer,
|
|
26
|
+
setSearchReducer,
|
|
27
|
+
setSearchResultsReducer,
|
|
28
|
+
setSearchFileVisibilityReducer,
|
|
29
|
+
setSearchLineVisibilityReducer,
|
|
30
|
+
setSearchFocusedIndexReducer,
|
|
31
|
+
toggleSearchFocusedReducer,
|
|
32
|
+
} from './searchReducer';
|
|
33
|
+
import {
|
|
34
|
+
setIsAIChatOpenReducer,
|
|
35
|
+
toggleAIChatFocusedReducer,
|
|
36
|
+
setAIChatModeReducer,
|
|
37
|
+
} from './aiChatReducer';
|
|
38
|
+
import { toggleAutoFollowReducer } from './toggleAutoFollowReducer';
|
|
39
|
+
import { updatePresenceIndicatorReducer } from './updatePresenceIndicatorReducer';
|
|
40
|
+
import { splitCurrentPaneReducer } from './splitCurrentPaneReducer';
|
|
41
|
+
import { setIsVisualEditorOpenReducer } from './visualEditorReducer';
|
|
42
|
+
export { createInitialState } from './createInitialState';
|
|
43
|
+
|
|
44
|
+
// The shape of the state managed by the reducer.
|
|
45
|
+
export type VZState = {
|
|
46
|
+
// The state of the split pane (tree data structure).
|
|
47
|
+
pane: Pane;
|
|
48
|
+
|
|
49
|
+
// The id of the currently active pane.
|
|
50
|
+
activePaneId: PaneId;
|
|
51
|
+
|
|
52
|
+
// The theme that is currently active.
|
|
53
|
+
theme: ThemeLabel;
|
|
54
|
+
|
|
55
|
+
// Search pattern and most recent results based on the current pattern
|
|
56
|
+
search: SearchResults;
|
|
57
|
+
|
|
58
|
+
// True to show the search instead of files
|
|
59
|
+
isSearchOpen: boolean;
|
|
60
|
+
|
|
61
|
+
// True to show the visual editor instead of files
|
|
62
|
+
isVisualEditorOpen: boolean;
|
|
63
|
+
|
|
64
|
+
// True to show the AI chat instead of files
|
|
65
|
+
isAIChatOpen: boolean;
|
|
66
|
+
|
|
67
|
+
// True if the AI chat input should focus on the next render.
|
|
68
|
+
aiChatFocused: boolean;
|
|
69
|
+
|
|
70
|
+
// The current AI chat mode: 'ask' or 'edit'
|
|
71
|
+
aiChatMode: 'ask' | 'edit';
|
|
72
|
+
|
|
73
|
+
// True to show the settings modal.
|
|
74
|
+
isSettingsOpen: boolean;
|
|
75
|
+
|
|
76
|
+
isDocOpen: boolean;
|
|
77
|
+
|
|
78
|
+
// True if the editor should focus on the next render.
|
|
79
|
+
editorWantsFocus: boolean;
|
|
80
|
+
|
|
81
|
+
// The username of the current user.
|
|
82
|
+
username: Username;
|
|
83
|
+
|
|
84
|
+
// True if "auto-follow" is enabled.
|
|
85
|
+
// This is a feature that automatically scrolls the editor to the
|
|
86
|
+
// currently active remote user's cursor position.
|
|
87
|
+
enableAutoFollow: boolean;
|
|
88
|
+
|
|
89
|
+
// The list of remote users and which file they have open,
|
|
90
|
+
// for presence display in the sidebar.
|
|
91
|
+
sidebarPresenceIndicators: Array<PresenceIndicator>;
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
// The shape of the actions that can be dispatched to the reducer.
|
|
95
|
+
export type VZAction =
|
|
96
|
+
// `set_active_file_id`
|
|
97
|
+
// * Sets the active file ID.
|
|
98
|
+
| { type: 'set_active_file_id'; activeFileId: VizFileId }
|
|
99
|
+
|
|
100
|
+
// `set_active_file_left' 'set_active_file_right`
|
|
101
|
+
// * Sets the active file ID to be the tab directly to the left or right
|
|
102
|
+
// * of the currently active tab.
|
|
103
|
+
| { type: 'set_active_file_left' }
|
|
104
|
+
| { type: 'set_active_file_right' }
|
|
105
|
+
|
|
106
|
+
// `open_tab`
|
|
107
|
+
// * Opens a tab.
|
|
108
|
+
// * Also serves to change an already open transient tab to persistent.
|
|
109
|
+
| {
|
|
110
|
+
type: 'open_tab';
|
|
111
|
+
fileId: VizFileId;
|
|
112
|
+
isTransient?: boolean;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// `close_tabs`
|
|
116
|
+
// * Closes a set of tabs.
|
|
117
|
+
| {
|
|
118
|
+
type: 'close_tabs';
|
|
119
|
+
fileIdsToClose: Array<VizFileId>;
|
|
120
|
+
// The pane id to close tabs from.
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// `set_theme`
|
|
124
|
+
// * Sets the theme.
|
|
125
|
+
| { type: 'set_theme'; themeLabel: ThemeLabel }
|
|
126
|
+
|
|
127
|
+
// `set_is_settings_open`
|
|
128
|
+
// * Sets whether the settings modal is open.
|
|
129
|
+
| { type: 'set_is_settings_open'; value: boolean }
|
|
130
|
+
| { type: 'set_is_doc_open'; value: boolean }
|
|
131
|
+
|
|
132
|
+
// `set_is_search_open`
|
|
133
|
+
// * Sets whether the search tab is open.
|
|
134
|
+
| { type: 'set_is_search_open'; value: boolean }
|
|
135
|
+
|
|
136
|
+
// `set_is_ai_chat_open`
|
|
137
|
+
// * Sets whether the AI chat tab is open.
|
|
138
|
+
| { type: 'set_is_ai_chat_open'; value: boolean }
|
|
139
|
+
|
|
140
|
+
// 'set_is_visual_editor_open'
|
|
141
|
+
// * Sets whether the Visual Editor is open.
|
|
142
|
+
| { type: 'set_is_visual_editor_open'; value: boolean }
|
|
143
|
+
|
|
144
|
+
// `toggle_ai_chat_focused`
|
|
145
|
+
// * Toggles focused variable to trigger AI chat input focus
|
|
146
|
+
| { type: 'toggle_ai_chat_focused' }
|
|
147
|
+
|
|
148
|
+
// `set_ai_chat_mode`
|
|
149
|
+
// * Sets the AI chat mode (ask or edit)
|
|
150
|
+
| { type: 'set_ai_chat_mode'; mode: 'ask' | 'edit' }
|
|
151
|
+
|
|
152
|
+
// `set_search`
|
|
153
|
+
// * Sets the current search pattern
|
|
154
|
+
| { type: 'set_search'; value: string }
|
|
155
|
+
|
|
156
|
+
// `set_search_results`
|
|
157
|
+
// * Sets the current search pattern
|
|
158
|
+
| {
|
|
159
|
+
type: 'set_search_results';
|
|
160
|
+
files: ShareDBDoc<VizContent>;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// `set_search_results_visibility`
|
|
164
|
+
// * Sets the visibility of a current search pattern file
|
|
165
|
+
| {
|
|
166
|
+
type: 'set_search_file_visibility';
|
|
167
|
+
files: ShareDBDoc<VizContent>;
|
|
168
|
+
id: string;
|
|
169
|
+
visibility: SearchFileVisibility;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// `hide_search_results_line`
|
|
173
|
+
// * Hides a current search pattern file's specific matching line
|
|
174
|
+
| {
|
|
175
|
+
type: 'hide_search_results_line';
|
|
176
|
+
files: ShareDBDoc<VizContent>;
|
|
177
|
+
id: string;
|
|
178
|
+
line: number;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// `set_active_search_index`
|
|
182
|
+
// * Sets focused (file) index and child focused (line) index for search results
|
|
183
|
+
| {
|
|
184
|
+
type: 'set_active_search_index';
|
|
185
|
+
focusedIndex: number;
|
|
186
|
+
childIndex: number;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// `toggle_search_focused`
|
|
190
|
+
// * Toggles focused variable to trigger search input focus
|
|
191
|
+
| { type: 'toggle_search_focused' }
|
|
192
|
+
|
|
193
|
+
// `editor_no_longer_wants_focus`
|
|
194
|
+
// * Sets `editorWantsFocus` to `false`.
|
|
195
|
+
| { type: 'editor_no_longer_wants_focus' }
|
|
196
|
+
|
|
197
|
+
// `set_username`
|
|
198
|
+
// * Sets the username.
|
|
199
|
+
| { type: 'set_username'; username: Username }
|
|
200
|
+
|
|
201
|
+
// `toggle_auto_follow`
|
|
202
|
+
// * Toggles the auto-follow feature.
|
|
203
|
+
| { type: 'toggle_auto_follow' }
|
|
204
|
+
|
|
205
|
+
// `update_presence_indicator
|
|
206
|
+
// * Updates a single presence indicator
|
|
207
|
+
| {
|
|
208
|
+
type: 'update_presence_indicator';
|
|
209
|
+
presenceIndicator: PresenceIndicator;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// `split_current_pane`
|
|
213
|
+
| { type: 'split_current_pane' };
|
|
214
|
+
|
|
215
|
+
const reducers = [
|
|
216
|
+
setActiveFileIdReducer,
|
|
217
|
+
setActiveFileLeftReducer,
|
|
218
|
+
setActiveFileRightReducer,
|
|
219
|
+
openTabReducer,
|
|
220
|
+
closeTabsReducer,
|
|
221
|
+
setThemeReducer,
|
|
222
|
+
setSearchReducer,
|
|
223
|
+
setSearchResultsReducer,
|
|
224
|
+
setSearchFileVisibilityReducer,
|
|
225
|
+
setSearchLineVisibilityReducer,
|
|
226
|
+
setSearchFocusedIndexReducer,
|
|
227
|
+
toggleSearchFocusedReducer,
|
|
228
|
+
setIsSearchOpenReducer,
|
|
229
|
+
setIsAIChatOpenReducer,
|
|
230
|
+
setIsVisualEditorOpenReducer,
|
|
231
|
+
toggleAIChatFocusedReducer,
|
|
232
|
+
setAIChatModeReducer,
|
|
233
|
+
setIsSettingsOpenReducer,
|
|
234
|
+
setIsDocOpenReducer,
|
|
235
|
+
editorNoLongerWantsFocusReducer,
|
|
236
|
+
setUsernameReducer,
|
|
237
|
+
toggleAutoFollowReducer,
|
|
238
|
+
updatePresenceIndicatorReducer,
|
|
239
|
+
splitCurrentPaneReducer,
|
|
240
|
+
];
|
|
241
|
+
|
|
242
|
+
export const vzReducer = (
|
|
243
|
+
state: VZState,
|
|
244
|
+
action: VZAction,
|
|
245
|
+
): VZState =>
|
|
246
|
+
reducers.reduce(
|
|
247
|
+
(currentState, currentReducer) =>
|
|
248
|
+
currentReducer(currentState, action),
|
|
249
|
+
state,
|
|
250
|
+
);
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { assert, describe, expect, it } from 'vitest';
|
|
2
|
+
import { openTabReducer } from './openTabReducer';
|
|
3
|
+
import { VZAction, VZState, createInitialState } from '.';
|
|
4
|
+
import { defaultTheme } from '../themes';
|
|
5
|
+
|
|
6
|
+
const initialState: VZState = createInitialState({
|
|
7
|
+
defaultTheme,
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
describe('openTabReducer', () => {
|
|
11
|
+
it('Opens a new tab', () => {
|
|
12
|
+
const action: VZAction = {
|
|
13
|
+
type: 'open_tab',
|
|
14
|
+
fileId: 'file1',
|
|
15
|
+
isTransient: false,
|
|
16
|
+
};
|
|
17
|
+
const newState = openTabReducer(initialState, action);
|
|
18
|
+
|
|
19
|
+
assert(newState.pane.type === 'leafPane');
|
|
20
|
+
expect(newState.pane.tabList.length).toBe(1);
|
|
21
|
+
expect(newState.pane.tabList[0].fileId).toBe('file1');
|
|
22
|
+
expect(newState.pane.tabList[0].isTransient).toBe(
|
|
23
|
+
false,
|
|
24
|
+
);
|
|
25
|
+
expect(newState.pane.activeFileId).toBe('file1');
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('Replaces a transient tab', () => {
|
|
29
|
+
const stateWithTransientTab = {
|
|
30
|
+
...initialState,
|
|
31
|
+
pane: {
|
|
32
|
+
...initialState.pane,
|
|
33
|
+
tabList: [{ fileId: 'file2', isTransient: true }],
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
const action: VZAction = {
|
|
37
|
+
type: 'open_tab',
|
|
38
|
+
fileId: 'file1',
|
|
39
|
+
isTransient: true,
|
|
40
|
+
};
|
|
41
|
+
const newState = openTabReducer(
|
|
42
|
+
stateWithTransientTab,
|
|
43
|
+
action,
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
assert(newState.pane.type === 'leafPane');
|
|
47
|
+
expect(newState.pane.tabList.length).toBe(1);
|
|
48
|
+
expect(newState.pane.tabList[0].fileId).toBe('file1');
|
|
49
|
+
expect(newState.pane.tabList[0].isTransient).toBe(true);
|
|
50
|
+
expect(newState.pane.activeFileId).toBe('file1');
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('Does not modify a non-transient tab', () => {
|
|
54
|
+
const stateWithNonTransientTab = {
|
|
55
|
+
...initialState,
|
|
56
|
+
pane: {
|
|
57
|
+
...initialState.pane,
|
|
58
|
+
tabList: [{ fileId: 'file1', isTransient: false }],
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
const action: VZAction = {
|
|
62
|
+
type: 'open_tab',
|
|
63
|
+
fileId: 'file1',
|
|
64
|
+
isTransient: true,
|
|
65
|
+
};
|
|
66
|
+
const newState = openTabReducer(
|
|
67
|
+
stateWithNonTransientTab,
|
|
68
|
+
action,
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
assert(newState.pane.type === 'leafPane');
|
|
72
|
+
expect(newState.pane.tabList.length).toBe(1);
|
|
73
|
+
expect(newState.pane.tabList[0].fileId).toBe('file1');
|
|
74
|
+
expect(newState.pane.tabList[0].isTransient).toBe(
|
|
75
|
+
false,
|
|
76
|
+
); // should remain non-transient
|
|
77
|
+
expect(newState.pane.activeFileId).toBe('file1');
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('Activates an already open tab', () => {
|
|
81
|
+
const stateWithMultipleTabs = {
|
|
82
|
+
...initialState,
|
|
83
|
+
pane: {
|
|
84
|
+
...initialState.pane,
|
|
85
|
+
tabList: [
|
|
86
|
+
{ fileId: 'file1', isTransient: false },
|
|
87
|
+
{ fileId: 'file2', isTransient: false },
|
|
88
|
+
],
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
const action: VZAction = {
|
|
92
|
+
type: 'open_tab',
|
|
93
|
+
fileId: 'file1',
|
|
94
|
+
isTransient: false,
|
|
95
|
+
};
|
|
96
|
+
const newState = openTabReducer(
|
|
97
|
+
stateWithMultipleTabs,
|
|
98
|
+
action,
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
assert(newState.pane.type === 'leafPane');
|
|
102
|
+
expect(newState.pane.tabList.length).toBe(2);
|
|
103
|
+
expect(newState.pane.activeFileId).toBe('file1');
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it('Sets editorWantsFocus to true for non-transient tabs', () => {
|
|
107
|
+
const action: VZAction = {
|
|
108
|
+
type: 'open_tab',
|
|
109
|
+
fileId: 'file3',
|
|
110
|
+
isTransient: false, // This tab is persistent
|
|
111
|
+
};
|
|
112
|
+
const newState = openTabReducer(initialState, action);
|
|
113
|
+
|
|
114
|
+
expect(newState.editorWantsFocus).toBe(true);
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it('Sets editorWantsFocus to false for transient tabs', () => {
|
|
118
|
+
const action: VZAction = {
|
|
119
|
+
type: 'open_tab',
|
|
120
|
+
fileId: 'file4',
|
|
121
|
+
isTransient: true, // This tab is transient
|
|
122
|
+
};
|
|
123
|
+
const newState = openTabReducer(initialState, action);
|
|
124
|
+
|
|
125
|
+
expect(newState.editorWantsFocus).toBe(false);
|
|
126
|
+
});
|
|
127
|
+
});
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { VZAction, VZState } from '.';
|
|
2
|
+
import { Pane, TabState } from '../../types';
|
|
3
|
+
import { findPane } from './findPane';
|
|
4
|
+
import { updatePane } from './updatePane';
|
|
5
|
+
|
|
6
|
+
export const openTabReducer = (
|
|
7
|
+
state: VZState,
|
|
8
|
+
action: VZAction,
|
|
9
|
+
): VZState => {
|
|
10
|
+
if (action.type !== 'open_tab') return state;
|
|
11
|
+
|
|
12
|
+
const { pane, activePaneId } = state;
|
|
13
|
+
const activePane: Pane = findPane(pane, activePaneId);
|
|
14
|
+
|
|
15
|
+
if (activePane.type !== 'leafPane') {
|
|
16
|
+
throw new Error(
|
|
17
|
+
'closeTabsReducer: activePane is not a leafPane',
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Check if the tab is already open and if it's transient.
|
|
22
|
+
const existingTabIndex = activePane.tabList.findIndex(
|
|
23
|
+
(tab) => tab.fileId === action.fileId,
|
|
24
|
+
);
|
|
25
|
+
const tabExists = existingTabIndex !== -1;
|
|
26
|
+
const existingTabIsTransient =
|
|
27
|
+
tabExists &&
|
|
28
|
+
activePane.tabList[existingTabIndex].isTransient;
|
|
29
|
+
|
|
30
|
+
// Find if there's any other transient tab.
|
|
31
|
+
const transientTabIndex = activePane.tabList.findIndex(
|
|
32
|
+
(tab) =>
|
|
33
|
+
tab.isTransient && tab.fileId !== action.fileId,
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
// The new tab state.
|
|
37
|
+
const newTabState: TabState = {
|
|
38
|
+
fileId: action.fileId,
|
|
39
|
+
isTransient: action.isTransient,
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
let newTabList: Array<TabState>;
|
|
43
|
+
|
|
44
|
+
if (tabExists) {
|
|
45
|
+
if (existingTabIsTransient) {
|
|
46
|
+
newTabList = activePane.tabList.map((tabState) =>
|
|
47
|
+
tabState.fileId === action.fileId
|
|
48
|
+
? newTabState
|
|
49
|
+
: tabState,
|
|
50
|
+
);
|
|
51
|
+
} else {
|
|
52
|
+
newTabList = [...activePane.tabList];
|
|
53
|
+
}
|
|
54
|
+
} else if (transientTabIndex !== -1) {
|
|
55
|
+
newTabList = [...activePane.tabList];
|
|
56
|
+
newTabList[transientTabIndex] = newTabState;
|
|
57
|
+
} else {
|
|
58
|
+
newTabList = [...activePane.tabList, newTabState];
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// If the tab is persistent, the editor should focus on the next render.
|
|
62
|
+
const editorWantsFocus = !action.isTransient;
|
|
63
|
+
|
|
64
|
+
return {
|
|
65
|
+
...state,
|
|
66
|
+
pane: updatePane({
|
|
67
|
+
pane,
|
|
68
|
+
activePaneId,
|
|
69
|
+
newTabList,
|
|
70
|
+
newActiveFileId: action.fileId,
|
|
71
|
+
}),
|
|
72
|
+
editorWantsFocus,
|
|
73
|
+
};
|
|
74
|
+
};
|