vzcode 2.3.0 → 2.5.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.
Files changed (198) hide show
  1. package/dist/assets/{index-DuUjtrt6.js → index-DB93Pffa.js} +84 -84
  2. package/dist/index.html +1 -1
  3. package/package.json +3 -2
  4. package/src/cli.ts +208 -0
  5. package/src/client/AIAssist/AIAssistCodeMirrorKeyMap.ts +42 -0
  6. package/src/client/AIAssist/AIAssistWidget/index.tsx +135 -0
  7. package/src/client/AIAssist/AIAssistWidget/style.scss +47 -0
  8. package/src/client/AIAssist/Spinner.jsx +76 -0
  9. package/src/client/AIAssist/formatFile.ts +27 -0
  10. package/src/client/AIAssist/generateFilesContext.ts +25 -0
  11. package/src/client/AIAssist/index.ts +2 -0
  12. package/src/client/AIAssist/startAIAssist.ts +95 -0
  13. package/src/client/App/index.tsx +152 -0
  14. package/src/client/App/style.scss +82 -0
  15. package/src/client/App/usePending.ts +51 -0
  16. package/src/client/App/useShareDB.ts +161 -0
  17. package/src/client/CodeEditor/Copilot/index.ts +74 -0
  18. package/src/client/CodeEditor/InteractiveWidgets/boolToggler.ts +12 -0
  19. package/src/client/CodeEditor/InteractiveWidgets/colorPicker.ts +68 -0
  20. package/src/client/CodeEditor/InteractiveWidgets/colorsInTextPlugin.ts +174 -0
  21. package/src/client/CodeEditor/InteractiveWidgets/highlightWidgets.ts +79 -0
  22. package/src/client/CodeEditor/InteractiveWidgets/index.ts +44 -0
  23. package/src/client/CodeEditor/InteractiveWidgets/markdownCheckboxToggler.ts +12 -0
  24. package/src/client/CodeEditor/InteractiveWidgets/numberDragger.ts +20 -0
  25. package/src/client/CodeEditor/InteractiveWidgets/rotateWidget.ts +143 -0
  26. package/src/client/CodeEditor/InteractiveWidgets/urlClicker.test.ts +48 -0
  27. package/src/client/CodeEditor/InteractiveWidgets/urlClicker.ts +12 -0
  28. package/src/client/CodeEditor/InteractiveWidgets/vec2Slider.ts +25 -0
  29. package/src/client/CodeEditor/basicSetup.ts +73 -0
  30. package/src/client/CodeEditor/getOrCreateEditor.tsx +615 -0
  31. package/src/client/CodeEditor/index.tsx +202 -0
  32. package/src/client/CodeEditor/json1PresenceBroadcast.ts +41 -0
  33. package/src/client/CodeEditor/json1PresenceDisplay.ts +350 -0
  34. package/src/client/CodeEditor/rainbowBrackets.ts +122 -0
  35. package/src/client/CodeEditor/style.scss +156 -0
  36. package/src/client/CodeEditor/typescriptExtension/worker.ts +32 -0
  37. package/src/client/CodeErrorOverlay/index.tsx +77 -0
  38. package/src/client/CodeErrorOverlay/style.scss +33 -0
  39. package/src/client/Fonts/fonts.ts +8 -0
  40. package/src/client/Icons/AdjustmentSVG.tsx +19 -0
  41. package/src/client/Icons/BugSVG.tsx +20 -0
  42. package/src/client/Icons/CloseSVG.tsx +19 -0
  43. package/src/client/Icons/CssSVG.tsx +16 -0
  44. package/src/client/Icons/DirectoryArrowSVG.tsx +19 -0
  45. package/src/client/Icons/EditSVG.tsx +19 -0
  46. package/src/client/Icons/FileSVG.tsx +19 -0
  47. package/src/client/Icons/FolderSVG.tsx +20 -0
  48. package/src/client/Icons/GearSVG.tsx +16 -0
  49. package/src/client/Icons/HtmlSVG.tsx +16 -0
  50. package/src/client/Icons/JavaScriptSVG.tsx +16 -0
  51. package/src/client/Icons/JsonSVG.tsx +16 -0
  52. package/src/client/Icons/MarkdownSVG.tsx +16 -0
  53. package/src/client/Icons/MicOffSVG.tsx +35 -0
  54. package/src/client/Icons/MicSVG.tsx +25 -0
  55. package/src/client/Icons/NewSVG.tsx +19 -0
  56. package/src/client/Icons/PinSVG.tsx +14 -0
  57. package/src/client/Icons/PlaySVG.tsx +16 -0
  58. package/src/client/Icons/QuestionMarkSVG.tsx +18 -0
  59. package/src/client/Icons/ReactSVG.tsx +16 -0
  60. package/src/client/Icons/SearchSVG.tsx +20 -0
  61. package/src/client/Icons/SparklesSVG.tsx +23 -0
  62. package/src/client/Icons/SplitEditorSVG.tsx +34 -0
  63. package/src/client/Icons/StopSVG.tsx +15 -0
  64. package/src/client/Icons/SvelteSVG.tsx +16 -0
  65. package/src/client/Icons/TrashSVG.tsx +19 -0
  66. package/src/client/Icons/TypeScriptSVG.tsx +16 -0
  67. package/src/client/Icons/index.tsx +26 -0
  68. package/src/client/ImageViewer/index.tsx +91 -0
  69. package/src/client/ImageViewer/style.scss +41 -0
  70. package/src/client/PresenceNotifications/PresenceNotifications.test.ts +134 -0
  71. package/src/client/PresenceNotifications/index.tsx +158 -0
  72. package/src/client/PresenceNotifications/style.scss +17 -0
  73. package/src/client/RunCodeWidget/index.tsx +117 -0
  74. package/src/client/RunCodeWidget/style.scss +24 -0
  75. package/src/client/SplitPaneResizeContext.tsx +159 -0
  76. package/src/client/TabList/Tab.tsx +73 -0
  77. package/src/client/TabList/index.tsx +35 -0
  78. package/src/client/TabList/style.scss +51 -0
  79. package/src/client/VZCodeContext/index.tsx +29 -0
  80. package/src/client/VZCodeContext/types.ts +251 -0
  81. package/src/client/VZCodeContext/useVZCodeState.ts +606 -0
  82. package/src/client/VZKeyboardShortcutsDoc.tsx +148 -0
  83. package/src/client/VZLeft.tsx +28 -0
  84. package/src/client/VZMiddle.tsx +172 -0
  85. package/src/client/VZResizer/index.tsx +128 -0
  86. package/src/client/VZResizer/styles.scss +33 -0
  87. package/src/client/VZRight.tsx +113 -0
  88. package/src/client/VZSettings.tsx +275 -0
  89. package/src/client/VZSidebar/AIChat/ChatInput.tsx +243 -0
  90. package/src/client/VZSidebar/AIChat/DiffView.scss +136 -0
  91. package/src/client/VZSidebar/AIChat/DiffView.tsx +176 -0
  92. package/src/client/VZSidebar/AIChat/Message.tsx +79 -0
  93. package/src/client/VZSidebar/AIChat/MessageList.tsx +190 -0
  94. package/src/client/VZSidebar/AIChat/StreamingMessage.tsx +35 -0
  95. package/src/client/VZSidebar/AIChat/ThinkingScratchpad.tsx +156 -0
  96. package/src/client/VZSidebar/AIChat/TypingIndicator.tsx +19 -0
  97. package/src/client/VZSidebar/AIChat/index.tsx +359 -0
  98. package/src/client/VZSidebar/AIChat/styles.scss +646 -0
  99. package/src/client/VZSidebar/AIChat/useSpeechRecognition.ts +117 -0
  100. package/src/client/VZSidebar/CreateDirModal.tsx +111 -0
  101. package/src/client/VZSidebar/CreateFileModal.tsx +111 -0
  102. package/src/client/VZSidebar/DeleteConfirmationModal.tsx +76 -0
  103. package/src/client/VZSidebar/DirectoryListing.tsx +89 -0
  104. package/src/client/VZSidebar/EmptyState.tsx +11 -0
  105. package/src/client/VZSidebar/FileListing.tsx +91 -0
  106. package/src/client/VZSidebar/FileTypeIcon.tsx +49 -0
  107. package/src/client/VZSidebar/Item.tsx +237 -0
  108. package/src/client/VZSidebar/Listing.tsx +47 -0
  109. package/src/client/VZSidebar/Search.tsx +527 -0
  110. package/src/client/VZSidebar/VisualEditor.tsx +282 -0
  111. package/src/client/VZSidebar/VoiceChatModal.tsx +121 -0
  112. package/src/client/VZSidebar/aiCopyPaste.ts +199 -0
  113. package/src/client/VZSidebar/index.tsx +728 -0
  114. package/src/client/VZSidebar/styles.scss +612 -0
  115. package/src/client/VZSidebar/useDragAndDrop.tsx +408 -0
  116. package/src/client/bootstrap.ts +26 -0
  117. package/src/client/buildWorker.ts +3 -0
  118. package/src/client/featureFlags.ts +8 -0
  119. package/src/client/generateRequestId.ts +6 -0
  120. package/src/client/getFileTree.ts +53 -0
  121. package/src/client/index.ts +19 -0
  122. package/src/client/main.jsx +10 -0
  123. package/src/client/presenceColor.ts +8 -0
  124. package/src/client/shouldTriggerRun.ts +31 -0
  125. package/src/client/sortFileTree.ts +21 -0
  126. package/src/client/tabsSearchParameters.test.ts +79 -0
  127. package/src/client/tabsSearchParameters.ts +137 -0
  128. package/src/client/themes/index.ts +7 -0
  129. package/src/client/themes/themes.ts +77 -0
  130. package/src/client/themes/uiwImports.ts +32 -0
  131. package/src/client/themes/useDynamicTheme.ts +29 -0
  132. package/src/client/themes/vizhubTheme/colors.ts +80 -0
  133. package/src/client/themes/vizhubTheme/index.ts +193 -0
  134. package/src/client/useActions.ts +288 -0
  135. package/src/client/useESLint/index.ts +72 -0
  136. package/src/client/useESLint/worker.ts +113 -0
  137. package/src/client/useEditorCache.ts +35 -0
  138. package/src/client/useFileCRUD.ts +228 -0
  139. package/src/client/useKeyboardShortcuts.ts +451 -0
  140. package/src/client/useOpenDirectories.ts +99 -0
  141. package/src/client/usePresenceAutoFollow.ts +85 -0
  142. package/src/client/usePrettier/index.ts +178 -0
  143. package/src/client/usePrettier/worker.ts +109 -0
  144. package/src/client/useRunCode.tsx +15 -0
  145. package/src/client/useRuntimeError.ts +32 -0
  146. package/src/client/useSubmitOperation.ts +15 -0
  147. package/src/client/useURLSync.ts +137 -0
  148. package/src/client/usernameLocalStorage.ts +43 -0
  149. package/src/client/utils/fileExtension.ts +10 -0
  150. package/src/client/utils/isImageFile.ts +12 -0
  151. package/src/client/vzReducer/aiChatReducer.ts +44 -0
  152. package/src/client/vzReducer/closeTabReducer.test.ts +205 -0
  153. package/src/client/vzReducer/closeTabsReducer.ts +96 -0
  154. package/src/client/vzReducer/createInitialState.ts +38 -0
  155. package/src/client/vzReducer/editorNoLongerWantsFocusReducer.ts +9 -0
  156. package/src/client/vzReducer/findPane.ts +19 -0
  157. package/src/client/vzReducer/index.ts +250 -0
  158. package/src/client/vzReducer/openTabReducer.test.ts +127 -0
  159. package/src/client/vzReducer/openTabReducer.ts +74 -0
  160. package/src/client/vzReducer/searchReducer.test.ts +292 -0
  161. package/src/client/vzReducer/searchReducer.ts +181 -0
  162. package/src/client/vzReducer/setActiveFileIdReducer.ts +28 -0
  163. package/src/client/vzReducer/setActiveFileLeftRightReducer.ts +76 -0
  164. package/src/client/vzReducer/setIsDocOpenReducer.ts +9 -0
  165. package/src/client/vzReducer/setIsSettingsOpenReducer.ts +9 -0
  166. package/src/client/vzReducer/setThemeReducer.ts +9 -0
  167. package/src/client/vzReducer/setUsernameReducer.ts +9 -0
  168. package/src/client/vzReducer/splitCurrentPaneReducer.ts +66 -0
  169. package/src/client/vzReducer/toggleAutoFollowReducer.ts +12 -0
  170. package/src/client/vzReducer/updatePane.ts +40 -0
  171. package/src/client/vzReducer/updatePresenceIndicatorReducer.ts +30 -0
  172. package/src/client/vzReducer/visualEditorReducer.ts +9 -0
  173. package/src/ot.ts +24 -0
  174. package/src/randomId.ts +15 -0
  175. package/src/reactRouterExports.ts +15 -0
  176. package/src/runCode.ts +27 -0
  177. package/src/server/aiChatHandler/aiEditing.ts +151 -0
  178. package/src/server/aiChatHandler/chatOperations.ts +476 -0
  179. package/src/server/aiChatHandler/errorHandling.ts +66 -0
  180. package/src/server/aiChatHandler/index.ts +199 -0
  181. package/src/server/aiChatHandler/llmStreaming.ts +419 -0
  182. package/src/server/aiChatHandler/validation.ts +24 -0
  183. package/src/server/computeInitialDocument.ts +220 -0
  184. package/src/server/config.ts +24 -0
  185. package/src/server/featureFlags.ts +8 -0
  186. package/src/server/generateAIResponse.ts +188 -0
  187. package/src/server/handleAIAssist.ts +53 -0
  188. package/src/server/handleAIChatMessage.ts +2 -0
  189. package/src/server/handleAICopilot.ts +97 -0
  190. package/src/server/index.ts +379 -0
  191. package/src/server/isDirectory.ts +1 -0
  192. package/src/server/livekit.ts +24 -0
  193. package/src/server/prettier.ts +115 -0
  194. package/src/server/setupEnv.ts +8 -0
  195. package/src/submitOperation.ts +25 -0
  196. package/src/types.ts +205 -0
  197. package/src/utils/fileDiff.ts +117 -0
  198. package/src/vite-env.d.ts +10 -0
@@ -0,0 +1,527 @@
1
+ import {
2
+ useRef,
3
+ useEffect,
4
+ useContext,
5
+ useState,
6
+ useCallback,
7
+ } from 'react';
8
+ import { Form } from '../bootstrap';
9
+ import { VZCodeContext } from '../VZCodeContext';
10
+ import { VizFileId } from '@vizhub/viz-types';
11
+ import { SearchFile } from '../../types';
12
+ import { EditorView } from 'codemirror';
13
+ import { CloseSVG, DirectoryArrowSVG } from '../Icons';
14
+ import { FileTypeIcon } from './FileTypeIcon';
15
+ import { editorCacheKey } from '../useEditorCache';
16
+
17
+ function jumpToPattern(
18
+ editor: EditorView,
19
+ pattern: string,
20
+ line: number,
21
+ index: number,
22
+ ) {
23
+ const position: number =
24
+ editor.state.doc.line(line).from + index;
25
+
26
+ editor.dispatch({
27
+ selection: {
28
+ anchor: position,
29
+ head: position + pattern.length,
30
+ },
31
+ scrollIntoView: true,
32
+ effects: EditorView.scrollIntoView(position, {
33
+ y: 'center',
34
+ }),
35
+ });
36
+ }
37
+
38
+ function isResultElementWithinView(container, element) {
39
+ const containerTop = container.scrollTop;
40
+ const containerBottom =
41
+ containerTop + container.clientHeight;
42
+
43
+ const elementTop =
44
+ element.offsetTop - container.offsetTop;
45
+ const elementBottom = elementTop + element.clientHeight;
46
+
47
+ return (
48
+ elementTop >= containerTop + 100 &&
49
+ elementBottom <= containerBottom - 100
50
+ );
51
+ }
52
+
53
+ export const Search = () => {
54
+ const [isMounted, setIsMounted] = useState(false);
55
+ const [isSearching, setIsSearching] = useState(false);
56
+ const {
57
+ search,
58
+ setSearch,
59
+ activePaneId,
60
+ setActiveFileId,
61
+ setSearchResults,
62
+ setSearchFileVisibility,
63
+ setSearchLineVisibility,
64
+ setSearchFocusedIndex,
65
+ shareDBDoc,
66
+ editorCache,
67
+ openTab,
68
+ } = useContext(VZCodeContext);
69
+ const {
70
+ pattern,
71
+ results,
72
+ focusedIndex,
73
+ focusedChildIndex,
74
+ focused,
75
+ } = search;
76
+ const inputRef = useRef(null);
77
+ const files: [string, SearchFile][] = Object.entries(
78
+ results,
79
+ ).filter(([_, file]) => file.visibility !== 'closed');
80
+
81
+ useEffect(() => {
82
+ if (isMounted) {
83
+ // Only conduct a search after fully mounting and entering a new non-empty pattern
84
+ setIsSearching(pattern.trim().length >= 1);
85
+
86
+ // Search about 2 seconds after entering a new pattern
87
+ const delaySearch = setTimeout(() => {
88
+ if (
89
+ pattern.trim().length >= 1 &&
90
+ inputRef.current
91
+ ) {
92
+ setSearchResults(shareDBDoc);
93
+ setIsSearching(false);
94
+ }
95
+ }, 2000);
96
+
97
+ return () => clearTimeout(delaySearch);
98
+ } else {
99
+ setIsMounted(true);
100
+ }
101
+ }, [pattern]);
102
+
103
+ const flattenResult = useCallback(
104
+ (fileId: string, file: SearchFile) => {
105
+ setSearchFileVisibility(
106
+ shareDBDoc,
107
+ fileId,
108
+ file.visibility === 'open' &&
109
+ focusedChildIndex === null
110
+ ? 'flattened'
111
+ : 'open',
112
+ );
113
+ },
114
+ [focusedIndex, focusedChildIndex],
115
+ );
116
+
117
+ const closeResult = useCallback((fileId: VizFileId) => {
118
+ setSearchFileVisibility(shareDBDoc, fileId, 'closed');
119
+ }, []);
120
+
121
+ const focusFileElement = useCallback(
122
+ (fileId: VizFileId, index: number) => {
123
+ setActiveFileId(fileId);
124
+ setSearchFocusedIndex(index, null);
125
+ },
126
+ [],
127
+ );
128
+
129
+ const handleKeyDown = (event) => {
130
+ event.preventDefault();
131
+
132
+ if (files.length === 0) {
133
+ return;
134
+ }
135
+
136
+ const matchingLines: number =
137
+ files[focusedIndex][1].matches.length;
138
+
139
+ switch (event.key) {
140
+ case 'Tab':
141
+ // Focus the file heading
142
+ setSearchFocusedIndex(focusedIndex, null);
143
+ break;
144
+ case 'ArrowUp':
145
+ if (
146
+ focusedIndex == 0 &&
147
+ focusedChildIndex == null
148
+ ) {
149
+ // No effect on first search listing
150
+ break;
151
+ } else if (
152
+ focusedChildIndex === null ||
153
+ matchingLines == 0
154
+ ) {
155
+ // Toggle the previous file last child, if any
156
+ const previousMatchingLines: number =
157
+ files[focusedIndex - 1][1].matches.length;
158
+ setSearchFocusedIndex(
159
+ focusedIndex - 1,
160
+ previousMatchingLines > 0
161
+ ? previousMatchingLines - 1
162
+ : null,
163
+ );
164
+ } else if (focusedChildIndex === 0) {
165
+ // Toggle the file
166
+ setSearchFocusedIndex(focusedIndex, null);
167
+ } else {
168
+ // Toggle the previous matching line
169
+ setSearchFocusedIndex(
170
+ focusedIndex,
171
+ focusedChildIndex - 1,
172
+ );
173
+ }
174
+
175
+ break;
176
+ case 'ArrowDown':
177
+ if (
178
+ focusedIndex == files.length - 1 &&
179
+ focusedChildIndex == matchingLines - 1
180
+ ) {
181
+ // Last matching line should have no effect
182
+ break;
183
+ } else if (
184
+ focusedChildIndex === null &&
185
+ matchingLines > 0
186
+ ) {
187
+ // Toggle the first matching line
188
+ setSearchFocusedIndex(focusedIndex, 0);
189
+ } else if (
190
+ focusedChildIndex == matchingLines - 1 ||
191
+ matchingLines == 0
192
+ ) {
193
+ // Toggle the next file
194
+ setSearchFocusedIndex(focusedIndex + 1, null);
195
+ } else {
196
+ // Toggle the next matching line
197
+ setSearchFocusedIndex(
198
+ focusedIndex,
199
+ focusedChildIndex + 1,
200
+ );
201
+ }
202
+
203
+ break;
204
+ case 'ArrowLeft':
205
+ if (focusedChildIndex !== null) {
206
+ setSearchFocusedIndex(focusedIndex, null);
207
+ } else {
208
+ flattenResult(
209
+ files[focusedIndex][0],
210
+ files[focusedIndex][1],
211
+ );
212
+ }
213
+ break;
214
+ case 'ArrowRight':
215
+ if (files[focusedIndex][1].visibility !== 'open') {
216
+ flattenResult(
217
+ files[focusedIndex][0],
218
+ files[focusedIndex][1],
219
+ );
220
+ } else if (
221
+ focusedChildIndex === null &&
222
+ matchingLines !== 0
223
+ ) {
224
+ setSearchFocusedIndex(focusedIndex, 0);
225
+ }
226
+
227
+ break;
228
+ case 'Enter':
229
+ case ' ':
230
+ const fileId: VizFileId = files[focusedIndex][0];
231
+
232
+ openTab({ fileId, isTransient: true });
233
+
234
+ setActiveFileId(fileId);
235
+
236
+ if (focusedChildIndex !== null) {
237
+ // Jump to matching line
238
+ const line: number =
239
+ files[focusedIndex][1].matches[
240
+ focusedChildIndex
241
+ ].line;
242
+ const index: number =
243
+ files[focusedIndex][1].matches[
244
+ focusedChildIndex
245
+ ].index;
246
+
247
+ const cacheKey = editorCacheKey(
248
+ fileId,
249
+ activePaneId,
250
+ );
251
+ if (editorCache.has(cacheKey)) {
252
+ jumpToPattern(
253
+ editorCache.get(cacheKey).editor,
254
+ pattern,
255
+ line,
256
+ index,
257
+ );
258
+ }
259
+ }
260
+ break;
261
+ default:
262
+ break;
263
+ }
264
+
265
+ // Ensure keyboard navigation keeps results within the current view
266
+ const file: string = files[focusedIndex][0];
267
+ const container = document.getElementById(
268
+ 'sidebar-view-container',
269
+ );
270
+
271
+ if (container) {
272
+ if (focusedChildIndex === null) {
273
+ const fileElement = document.getElementById(file);
274
+
275
+ if (
276
+ !isResultElementWithinView(container, fileElement)
277
+ ) {
278
+ fileElement.scrollIntoView({ block: 'center' });
279
+ }
280
+ } else {
281
+ const line =
282
+ files[focusedIndex][1].matches[focusedChildIndex]
283
+ .line;
284
+ const lineElement = document.getElementById(
285
+ file + '-' + line,
286
+ );
287
+
288
+ if (
289
+ !isResultElementWithinView(container, lineElement)
290
+ ) {
291
+ lineElement.scrollIntoView({ block: 'center' });
292
+ }
293
+ }
294
+ }
295
+ };
296
+
297
+ useEffect(() => {
298
+ // Focus the search input on mount and keyboard shortcut invocation
299
+ if (inputRef.current) {
300
+ inputRef.current.focus();
301
+ }
302
+ }, [focused]);
303
+
304
+ return (
305
+ <div>
306
+ <Form.Group
307
+ className="sidebar-search-form mb-3"
308
+ controlId="searchName"
309
+ >
310
+ <Form.Control
311
+ type="text"
312
+ value={pattern}
313
+ onChange={(event) =>
314
+ setSearch(event.target.value)
315
+ }
316
+ ref={inputRef}
317
+ spellCheck="false"
318
+ />
319
+ </Form.Group>
320
+ {Object.keys(results).length >= 1 &&
321
+ pattern.trim().length >= 1 ? (
322
+ <div
323
+ onKeyDown={handleKeyDown}
324
+ tabIndex={0}
325
+ className="search-results"
326
+ >
327
+ {files.map(
328
+ (
329
+ [fileId, file]: [string, SearchFile],
330
+ index,
331
+ ) => {
332
+ const matches = file.matches;
333
+ return (
334
+ <div
335
+ className="search-result"
336
+ key={file.name}
337
+ >
338
+ <div
339
+ onClick={() =>
340
+ focusFileElement(fileId, index)
341
+ }
342
+ id={fileId}
343
+ className={`search-file-heading
344
+ ${
345
+ focusedIndex == index &&
346
+ focusedChildIndex == null
347
+ ? 'active'
348
+ : ''
349
+ }`}
350
+ >
351
+ <div className="search-file-title">
352
+ <div
353
+ className="arrow-wrapper"
354
+ onClick={() =>
355
+ flattenResult(fileId, file)
356
+ }
357
+ style={{
358
+ transform: `rotate(${file.visibility === 'open' ? 90 : 0}deg)`,
359
+ }}
360
+ >
361
+ <DirectoryArrowSVG />
362
+ </div>
363
+ <div className="search-file-name">
364
+ <FileTypeIcon name={file.name} />
365
+ <h5>{file.name}</h5>
366
+ </div>
367
+ </div>
368
+ <div className="search-file-info">
369
+ {index == focusedIndex &&
370
+ focusedChildIndex == null ? (
371
+ <span
372
+ className="search-file-close"
373
+ onClick={(event) => {
374
+ event.stopPropagation();
375
+ closeResult(fileId);
376
+ // Focus the previous search file, if possible
377
+ setSearchFocusedIndex(
378
+ Math.max(0, index - 1),
379
+ null,
380
+ );
381
+ }}
382
+ >
383
+ <CloseSVG />
384
+ </span>
385
+ ) : (
386
+ <h6 className="search-file-count">
387
+ {matches.length}
388
+ </h6>
389
+ )}
390
+ </div>
391
+ </div>
392
+ <div className="search-file-lines">
393
+ {(file.visibility === 'open' ||
394
+ (file.visibility === 'flattened' &&
395
+ index == focusedIndex &&
396
+ focusedChildIndex !== null)) &&
397
+ file.matches.map(
398
+ (match, childIndex) => {
399
+ const before =
400
+ match.text.substring(
401
+ 0,
402
+ match.index,
403
+ );
404
+ const hit = match.text.substring(
405
+ match.index,
406
+ match.index + pattern.length,
407
+ );
408
+ const after =
409
+ match.text.substring(
410
+ match.index + pattern.length,
411
+ );
412
+
413
+ const identifier =
414
+ file.name + '-' + match.line;
415
+
416
+ return (
417
+ <div
418
+ key={identifier}
419
+ tabIndex={
420
+ index == focusedIndex
421
+ ? 0
422
+ : -1
423
+ }
424
+ id={fileId + '-' + match.line}
425
+ className={`search-line
426
+ ${
427
+ focusedIndex == index &&
428
+ focusedChildIndex ==
429
+ childIndex
430
+ ? 'active'
431
+ : ''
432
+ }`}
433
+ >
434
+ <p
435
+ key={
436
+ file.name +
437
+ ' - ' +
438
+ match.line +
439
+ ' - ' +
440
+ match.index
441
+ }
442
+ onClick={() => {
443
+ setSearchFocusedIndex(
444
+ index,
445
+ childIndex,
446
+ );
447
+
448
+ openTab({
449
+ fileId,
450
+ isTransient: true,
451
+ });
452
+
453
+ setActiveFileId(fileId);
454
+
455
+ const cacheKey =
456
+ editorCacheKey(
457
+ fileId,
458
+ activePaneId,
459
+ );
460
+
461
+ if (
462
+ editorCache.has(
463
+ cacheKey,
464
+ )
465
+ ) {
466
+ jumpToPattern(
467
+ editorCache.get(
468
+ cacheKey,
469
+ ).editor,
470
+ pattern,
471
+ match.line,
472
+ match.index,
473
+ );
474
+ }
475
+ }}
476
+ >
477
+ {before}
478
+ <span className="search-pattern">
479
+ {hit}
480
+ </span>
481
+ {after}
482
+ </p>
483
+ {focusedIndex == index &&
484
+ focusedChildIndex ===
485
+ childIndex && (
486
+ <span
487
+ className="search-file-close"
488
+ onClick={(event) => {
489
+ event.stopPropagation();
490
+ setSearchLineVisibility(
491
+ shareDBDoc,
492
+ fileId,
493
+ match.line,
494
+ );
495
+
496
+ if (childIndex == 0) {
497
+ // Removing remaining single line
498
+ setSearchFocusedIndex(
499
+ index,
500
+ null,
501
+ );
502
+ }
503
+ }}
504
+ >
505
+ <CloseSVG />
506
+ </span>
507
+ )}
508
+ </div>
509
+ );
510
+ },
511
+ )}
512
+ </div>
513
+ </div>
514
+ );
515
+ },
516
+ )}
517
+ </div>
518
+ ) : (
519
+ <div className="search-state">
520
+ <h6>
521
+ {isSearching ? 'Searching...' : 'No Results'}
522
+ </h6>
523
+ </div>
524
+ )}
525
+ </div>
526
+ );
527
+ };