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.
Files changed (196) hide show
  1. package/package.json +3 -2
  2. package/src/cli.ts +208 -0
  3. package/src/client/AIAssist/AIAssistCodeMirrorKeyMap.ts +42 -0
  4. package/src/client/AIAssist/AIAssistWidget/index.tsx +135 -0
  5. package/src/client/AIAssist/AIAssistWidget/style.scss +47 -0
  6. package/src/client/AIAssist/Spinner.jsx +76 -0
  7. package/src/client/AIAssist/formatFile.ts +27 -0
  8. package/src/client/AIAssist/generateFilesContext.ts +25 -0
  9. package/src/client/AIAssist/index.ts +2 -0
  10. package/src/client/AIAssist/startAIAssist.ts +95 -0
  11. package/src/client/App/index.tsx +152 -0
  12. package/src/client/App/style.scss +82 -0
  13. package/src/client/App/usePending.ts +51 -0
  14. package/src/client/App/useShareDB.ts +161 -0
  15. package/src/client/CodeEditor/Copilot/index.ts +74 -0
  16. package/src/client/CodeEditor/InteractiveWidgets/boolToggler.ts +12 -0
  17. package/src/client/CodeEditor/InteractiveWidgets/colorPicker.ts +68 -0
  18. package/src/client/CodeEditor/InteractiveWidgets/colorsInTextPlugin.ts +174 -0
  19. package/src/client/CodeEditor/InteractiveWidgets/highlightWidgets.ts +79 -0
  20. package/src/client/CodeEditor/InteractiveWidgets/index.ts +44 -0
  21. package/src/client/CodeEditor/InteractiveWidgets/markdownCheckboxToggler.ts +12 -0
  22. package/src/client/CodeEditor/InteractiveWidgets/numberDragger.ts +20 -0
  23. package/src/client/CodeEditor/InteractiveWidgets/rotateWidget.ts +143 -0
  24. package/src/client/CodeEditor/InteractiveWidgets/urlClicker.test.ts +48 -0
  25. package/src/client/CodeEditor/InteractiveWidgets/urlClicker.ts +12 -0
  26. package/src/client/CodeEditor/InteractiveWidgets/vec2Slider.ts +25 -0
  27. package/src/client/CodeEditor/basicSetup.ts +73 -0
  28. package/src/client/CodeEditor/getOrCreateEditor.tsx +615 -0
  29. package/src/client/CodeEditor/index.tsx +202 -0
  30. package/src/client/CodeEditor/json1PresenceBroadcast.ts +41 -0
  31. package/src/client/CodeEditor/json1PresenceDisplay.ts +350 -0
  32. package/src/client/CodeEditor/rainbowBrackets.ts +122 -0
  33. package/src/client/CodeEditor/style.scss +156 -0
  34. package/src/client/CodeEditor/typescriptExtension/worker.ts +32 -0
  35. package/src/client/CodeErrorOverlay/index.tsx +77 -0
  36. package/src/client/CodeErrorOverlay/style.scss +33 -0
  37. package/src/client/Fonts/fonts.ts +8 -0
  38. package/src/client/Icons/AdjustmentSVG.tsx +19 -0
  39. package/src/client/Icons/BugSVG.tsx +20 -0
  40. package/src/client/Icons/CloseSVG.tsx +19 -0
  41. package/src/client/Icons/CssSVG.tsx +16 -0
  42. package/src/client/Icons/DirectoryArrowSVG.tsx +19 -0
  43. package/src/client/Icons/EditSVG.tsx +19 -0
  44. package/src/client/Icons/FileSVG.tsx +19 -0
  45. package/src/client/Icons/FolderSVG.tsx +20 -0
  46. package/src/client/Icons/GearSVG.tsx +16 -0
  47. package/src/client/Icons/HtmlSVG.tsx +16 -0
  48. package/src/client/Icons/JavaScriptSVG.tsx +16 -0
  49. package/src/client/Icons/JsonSVG.tsx +16 -0
  50. package/src/client/Icons/MarkdownSVG.tsx +16 -0
  51. package/src/client/Icons/MicOffSVG.tsx +35 -0
  52. package/src/client/Icons/MicSVG.tsx +25 -0
  53. package/src/client/Icons/NewSVG.tsx +19 -0
  54. package/src/client/Icons/PinSVG.tsx +14 -0
  55. package/src/client/Icons/PlaySVG.tsx +16 -0
  56. package/src/client/Icons/QuestionMarkSVG.tsx +18 -0
  57. package/src/client/Icons/ReactSVG.tsx +16 -0
  58. package/src/client/Icons/SearchSVG.tsx +20 -0
  59. package/src/client/Icons/SparklesSVG.tsx +23 -0
  60. package/src/client/Icons/SplitEditorSVG.tsx +34 -0
  61. package/src/client/Icons/StopSVG.tsx +15 -0
  62. package/src/client/Icons/SvelteSVG.tsx +16 -0
  63. package/src/client/Icons/TrashSVG.tsx +19 -0
  64. package/src/client/Icons/TypeScriptSVG.tsx +16 -0
  65. package/src/client/Icons/index.tsx +26 -0
  66. package/src/client/ImageViewer/index.tsx +91 -0
  67. package/src/client/ImageViewer/style.scss +41 -0
  68. package/src/client/PresenceNotifications/PresenceNotifications.test.ts +134 -0
  69. package/src/client/PresenceNotifications/index.tsx +158 -0
  70. package/src/client/PresenceNotifications/style.scss +17 -0
  71. package/src/client/RunCodeWidget/index.tsx +117 -0
  72. package/src/client/RunCodeWidget/style.scss +24 -0
  73. package/src/client/SplitPaneResizeContext.tsx +159 -0
  74. package/src/client/TabList/Tab.tsx +73 -0
  75. package/src/client/TabList/index.tsx +35 -0
  76. package/src/client/TabList/style.scss +51 -0
  77. package/src/client/VZCodeContext/index.tsx +29 -0
  78. package/src/client/VZCodeContext/types.ts +248 -0
  79. package/src/client/VZCodeContext/useVZCodeState.ts +602 -0
  80. package/src/client/VZKeyboardShortcutsDoc.tsx +148 -0
  81. package/src/client/VZLeft.tsx +28 -0
  82. package/src/client/VZMiddle.tsx +172 -0
  83. package/src/client/VZResizer/index.tsx +128 -0
  84. package/src/client/VZResizer/styles.scss +33 -0
  85. package/src/client/VZRight.tsx +113 -0
  86. package/src/client/VZSettings.tsx +275 -0
  87. package/src/client/VZSidebar/AIChat/ChatInput.tsx +243 -0
  88. package/src/client/VZSidebar/AIChat/DiffView.scss +136 -0
  89. package/src/client/VZSidebar/AIChat/DiffView.tsx +176 -0
  90. package/src/client/VZSidebar/AIChat/Message.tsx +79 -0
  91. package/src/client/VZSidebar/AIChat/MessageList.tsx +190 -0
  92. package/src/client/VZSidebar/AIChat/StreamingMessage.tsx +35 -0
  93. package/src/client/VZSidebar/AIChat/ThinkingScratchpad.tsx +156 -0
  94. package/src/client/VZSidebar/AIChat/TypingIndicator.tsx +19 -0
  95. package/src/client/VZSidebar/AIChat/index.tsx +357 -0
  96. package/src/client/VZSidebar/AIChat/styles.scss +646 -0
  97. package/src/client/VZSidebar/AIChat/useSpeechRecognition.ts +117 -0
  98. package/src/client/VZSidebar/CreateDirModal.tsx +111 -0
  99. package/src/client/VZSidebar/CreateFileModal.tsx +111 -0
  100. package/src/client/VZSidebar/DeleteConfirmationModal.tsx +76 -0
  101. package/src/client/VZSidebar/DirectoryListing.tsx +89 -0
  102. package/src/client/VZSidebar/EmptyState.tsx +11 -0
  103. package/src/client/VZSidebar/FileListing.tsx +91 -0
  104. package/src/client/VZSidebar/FileTypeIcon.tsx +49 -0
  105. package/src/client/VZSidebar/Item.tsx +237 -0
  106. package/src/client/VZSidebar/Listing.tsx +47 -0
  107. package/src/client/VZSidebar/Search.tsx +527 -0
  108. package/src/client/VZSidebar/VisualEditor.tsx +282 -0
  109. package/src/client/VZSidebar/VoiceChatModal.tsx +121 -0
  110. package/src/client/VZSidebar/aiCopyPaste.ts +199 -0
  111. package/src/client/VZSidebar/index.tsx +728 -0
  112. package/src/client/VZSidebar/styles.scss +612 -0
  113. package/src/client/VZSidebar/useDragAndDrop.tsx +408 -0
  114. package/src/client/bootstrap.ts +26 -0
  115. package/src/client/buildWorker.ts +3 -0
  116. package/src/client/featureFlags.ts +8 -0
  117. package/src/client/generateRequestId.ts +6 -0
  118. package/src/client/getFileTree.ts +53 -0
  119. package/src/client/index.ts +19 -0
  120. package/src/client/main.jsx +10 -0
  121. package/src/client/presenceColor.ts +8 -0
  122. package/src/client/shouldTriggerRun.ts +31 -0
  123. package/src/client/sortFileTree.ts +21 -0
  124. package/src/client/tabsSearchParameters.test.ts +79 -0
  125. package/src/client/tabsSearchParameters.ts +137 -0
  126. package/src/client/themes/index.ts +7 -0
  127. package/src/client/themes/themes.ts +77 -0
  128. package/src/client/themes/uiwImports.ts +32 -0
  129. package/src/client/themes/useDynamicTheme.ts +29 -0
  130. package/src/client/themes/vizhubTheme/colors.ts +80 -0
  131. package/src/client/themes/vizhubTheme/index.ts +193 -0
  132. package/src/client/useActions.ts +288 -0
  133. package/src/client/useESLint/index.ts +72 -0
  134. package/src/client/useESLint/worker.ts +113 -0
  135. package/src/client/useEditorCache.ts +35 -0
  136. package/src/client/useFileCRUD.ts +228 -0
  137. package/src/client/useKeyboardShortcuts.ts +451 -0
  138. package/src/client/useOpenDirectories.ts +99 -0
  139. package/src/client/usePresenceAutoFollow.ts +85 -0
  140. package/src/client/usePrettier/index.ts +178 -0
  141. package/src/client/usePrettier/worker.ts +109 -0
  142. package/src/client/useRunCode.tsx +15 -0
  143. package/src/client/useRuntimeError.ts +32 -0
  144. package/src/client/useSubmitOperation.ts +15 -0
  145. package/src/client/useURLSync.ts +137 -0
  146. package/src/client/usernameLocalStorage.ts +43 -0
  147. package/src/client/utils/fileExtension.ts +10 -0
  148. package/src/client/utils/isImageFile.ts +12 -0
  149. package/src/client/vzReducer/aiChatReducer.ts +44 -0
  150. package/src/client/vzReducer/closeTabReducer.test.ts +205 -0
  151. package/src/client/vzReducer/closeTabsReducer.ts +96 -0
  152. package/src/client/vzReducer/createInitialState.ts +38 -0
  153. package/src/client/vzReducer/editorNoLongerWantsFocusReducer.ts +9 -0
  154. package/src/client/vzReducer/findPane.ts +19 -0
  155. package/src/client/vzReducer/index.ts +250 -0
  156. package/src/client/vzReducer/openTabReducer.test.ts +127 -0
  157. package/src/client/vzReducer/openTabReducer.ts +74 -0
  158. package/src/client/vzReducer/searchReducer.test.ts +292 -0
  159. package/src/client/vzReducer/searchReducer.ts +181 -0
  160. package/src/client/vzReducer/setActiveFileIdReducer.ts +28 -0
  161. package/src/client/vzReducer/setActiveFileLeftRightReducer.ts +76 -0
  162. package/src/client/vzReducer/setIsDocOpenReducer.ts +9 -0
  163. package/src/client/vzReducer/setIsSettingsOpenReducer.ts +9 -0
  164. package/src/client/vzReducer/setThemeReducer.ts +9 -0
  165. package/src/client/vzReducer/setUsernameReducer.ts +9 -0
  166. package/src/client/vzReducer/splitCurrentPaneReducer.ts +66 -0
  167. package/src/client/vzReducer/toggleAutoFollowReducer.ts +12 -0
  168. package/src/client/vzReducer/updatePane.ts +40 -0
  169. package/src/client/vzReducer/updatePresenceIndicatorReducer.ts +30 -0
  170. package/src/client/vzReducer/visualEditorReducer.ts +9 -0
  171. package/src/ot.ts +24 -0
  172. package/src/randomId.ts +15 -0
  173. package/src/reactRouterExports.ts +15 -0
  174. package/src/runCode.ts +27 -0
  175. package/src/server/aiChatHandler/aiEditing.ts +151 -0
  176. package/src/server/aiChatHandler/chatOperations.ts +476 -0
  177. package/src/server/aiChatHandler/errorHandling.ts +66 -0
  178. package/src/server/aiChatHandler/index.ts +199 -0
  179. package/src/server/aiChatHandler/llmStreaming.ts +419 -0
  180. package/src/server/aiChatHandler/validation.ts +24 -0
  181. package/src/server/computeInitialDocument.ts +220 -0
  182. package/src/server/config.ts +24 -0
  183. package/src/server/featureFlags.ts +8 -0
  184. package/src/server/generateAIResponse.ts +188 -0
  185. package/src/server/handleAIAssist.ts +53 -0
  186. package/src/server/handleAIChatMessage.ts +2 -0
  187. package/src/server/handleAICopilot.ts +97 -0
  188. package/src/server/index.ts +379 -0
  189. package/src/server/isDirectory.ts +1 -0
  190. package/src/server/livekit.ts +24 -0
  191. package/src/server/prettier.ts +115 -0
  192. package/src/server/setupEnv.ts +8 -0
  193. package/src/submitOperation.ts +25 -0
  194. package/src/types.ts +205 -0
  195. package/src/utils/fileDiff.ts +117 -0
  196. package/src/vite-env.d.ts +10 -0
@@ -0,0 +1,35 @@
1
+ export const MicOffSVG = () => (
2
+ <svg
3
+ fill="#000000"
4
+ height="24"
5
+ width="24"
6
+ stroke="currentColor"
7
+ version="1.1"
8
+ xmlns="http://www.w3.org/2000/svg"
9
+ viewBox="0 0 512 512"
10
+ enableBackground="new 0 0 512 512"
11
+ >
12
+ <g>
13
+ <g>
14
+ <path
15
+ fill="currentColor"
16
+ d="m439.5,236c0-11.3-9.1-20.4-20.4-20.4s-20.4,9.1-20.4,20.4c0,70-64,126.9-142.7,126.9-78.7,0-142.7-56.9-142.7-126.9 0-11.3-9.1-20.4-20.4-20.4s-20.4,9.1-20.4,20.4c0,86.2 71.5,157.4 163.1,166.7v57.5h-23.6c-11.3,0-20.4,9.1-20.4,20.4 0,11.3 9.1,20.4 20.4,20.4h88c11.3,0 20.4-9.1 20.4-20.4 0-11.3-9.1-20.4-20.4-20.4h-23.6v-57.5c91.6-9.3 163.1-80.5 163.1-166.7z"
17
+ />
18
+ <path
19
+ fill="currentColor"
20
+ d="m256,323.5c51,0 92.3-41.3 92.3-92.3v-127.9c0-51-41.3-92.3-92.3-92.3s-92.3,41.3-92.3,92.3v127.9c0,51 41.3,92.3 92.3,92.3zm-52.3-220.2c0-28.8 23.5-52.3 52.3-52.3s52.3,23.5 52.3,52.3v127.9c0,28.8-23.5,52.3-52.3,52.3s-52.3-23.5-52.3-52.3v-127.9z"
21
+ />
22
+ {/* Diagonal line to indicate "off" state */}
23
+ <line
24
+ x1="100"
25
+ y1="100"
26
+ x2="412"
27
+ y2="412"
28
+ stroke="currentColor"
29
+ strokeWidth="40"
30
+ strokeLinecap="round"
31
+ />
32
+ </g>
33
+ </g>
34
+ </svg>
35
+ );
@@ -0,0 +1,25 @@
1
+ export const MicSVG = () => (
2
+ <svg
3
+ fill="#000000"
4
+ height="24"
5
+ width="24"
6
+ stroke="currentColor"
7
+ version="1.1"
8
+ xmlns="http://www.w3.org/2000/svg"
9
+ viewBox="0 0 512 512"
10
+ enableBackground="new 0 0 512 512"
11
+ >
12
+ <g>
13
+ <g>
14
+ <path
15
+ fill="currentColor"
16
+ d="m439.5,236c0-11.3-9.1-20.4-20.4-20.4s-20.4,9.1-20.4,20.4c0,70-64,126.9-142.7,126.9-78.7,0-142.7-56.9-142.7-126.9 0-11.3-9.1-20.4-20.4-20.4s-20.4,9.1-20.4,20.4c0,86.2 71.5,157.4 163.1,166.7v57.5h-23.6c-11.3,0-20.4,9.1-20.4,20.4 0,11.3 9.1,20.4 20.4,20.4h88c11.3,0 20.4-9.1 20.4-20.4 0-11.3-9.1-20.4-20.4-20.4h-23.6v-57.5c91.6-9.3 163.1-80.5 163.1-166.7z"
17
+ />
18
+ <path
19
+ fill="currentColor"
20
+ d="m256,323.5c51,0 92.3-41.3 92.3-92.3v-127.9c0-51-41.3-92.3-92.3-92.3s-92.3,41.3-92.3,92.3v127.9c0,51 41.3,92.3 92.3,92.3zm-52.3-220.2c0-28.8 23.5-52.3 52.3-52.3s52.3,23.5 52.3,52.3v127.9c0,28.8-23.5,52.3-52.3,52.3s-52.3-23.5-52.3-52.3v-127.9z"
21
+ />
22
+ </g>
23
+ </g>
24
+ </svg>
25
+ );
@@ -0,0 +1,19 @@
1
+ export const NewSVG = () => {
2
+ return (
3
+ <svg
4
+ xmlns="http://www.w3.org/2000/svg"
5
+ width="24"
6
+ height="24"
7
+ viewBox="0 0 24 24"
8
+ fill="none"
9
+ >
10
+ <path
11
+ stroke="currentColor"
12
+ strokeLinecap="round"
13
+ strokeLinejoin="round"
14
+ strokeWidth="1.5"
15
+ d="M19.5 14.25v-2.625a3.375 3.375 0 0 0-3.375-3.375h-1.5A1.125 1.125 0 0 1 13.5 7.125v-1.5a3.375 3.375 0 0 0-3.375-3.375H8.25m3.75 9v6m3-3H9m1.5-12H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 0 0-9-9Z"
16
+ />
17
+ </svg>
18
+ );
19
+ };
@@ -0,0 +1,14 @@
1
+ // From https://primer.style/foundations/icons/pin-24
2
+ export const PinSVG = () => (
3
+ <svg
4
+ xmlns="http://www.w3.org/2000/svg"
5
+ viewBox="0 0 24 24"
6
+ width="24"
7
+ height="24"
8
+ >
9
+ <path
10
+ fill="currentColor"
11
+ d="m16.114 1.553 6.333 6.333a1.75 1.75 0 0 1-.603 2.869l-1.63.633a5.67 5.67 0 0 0-3.395 3.725l-1.131 3.959a1.75 1.75 0 0 1-2.92.757L9 16.061l-5.595 5.594a.749.749 0 1 1-1.06-1.06L7.939 15l-3.768-3.768a1.75 1.75 0 0 1 .757-2.92l3.959-1.131a5.666 5.666 0 0 0 3.725-3.395l.633-1.63a1.75 1.75 0 0 1 2.869-.603ZM5.232 10.171l8.597 8.597a.25.25 0 0 0 .417-.108l1.131-3.959A7.17 7.17 0 0 1 19.67 9.99l1.63-.634a.25.25 0 0 0 .086-.409l-6.333-6.333a.25.25 0 0 0-.409.086l-.634 1.63a7.17 7.17 0 0 1-4.711 4.293L5.34 9.754a.25.25 0 0 0-.108.417Z"
12
+ ></path>
13
+ </svg>
14
+ );
@@ -0,0 +1,16 @@
1
+ export const PlaySVG = () => (
2
+ <svg
3
+ width="24"
4
+ height="24"
5
+ fill="none"
6
+ viewBox="0 0 24 24"
7
+ strokeWidth={1.5}
8
+ stroke="currentColor"
9
+ >
10
+ <path
11
+ strokeLinecap="round"
12
+ strokeLinejoin="round"
13
+ d="M5.25 5.653c0-.856.917-1.398 1.667-.986l11.54 6.347a1.125 1.125 0 0 1 0 1.972l-11.54 6.347a1.125 1.125 0 0 1-1.667-.986V5.653Z"
14
+ />
15
+ </svg>
16
+ );
@@ -0,0 +1,18 @@
1
+ // From https://heroicons.com/
2
+ export const QuestionMarkSVG = () => (
3
+ <svg
4
+ xmlns="http://www.w3.org/2000/svg"
5
+ fill="none"
6
+ width="24"
7
+ height="24"
8
+ viewBox="0 0 24 24"
9
+ strokeWidth="1.5"
10
+ stroke="currentColor"
11
+ >
12
+ <path
13
+ strokeLinecap="round"
14
+ strokeLinejoin="round"
15
+ d="M9.879 7.519c1.171-1.025 3.071-1.025 4.242 0 1.172 1.025 1.172 2.687 0 3.712-.203.179-.43.326-.67.442-.745.361-1.45.999-1.45 1.827v.75M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Zm-9 5.25h.008v.008H12v-.008Z"
16
+ />
17
+ </svg>
18
+ );
@@ -0,0 +1,16 @@
1
+ // https://reactsvgicons.com/
2
+ export const ReactSVG = () => {
3
+ return (
4
+ <svg
5
+ width="24"
6
+ height="24"
7
+ xmlns="http://www.w3.org/2000/svg"
8
+ fill="currentColor"
9
+ viewBox="0 0 24 24"
10
+ strokeWidth="1.5"
11
+ stroke="none"
12
+ >
13
+ <path d="M14.23 12.004a2.236 2.236 0 01-2.235 2.236 2.236 2.236 0 01-2.236-2.236 2.236 2.236 0 012.235-2.236 2.236 2.236 0 012.236 2.236zm2.648-10.69c-1.346 0-3.107.96-4.888 2.622-1.78-1.653-3.542-2.602-4.887-2.602-.41 0-.783.093-1.106.278-1.375.793-1.683 3.264-.973 6.365C1.98 8.917 0 10.42 0 12.004c0 1.59 1.99 3.097 5.043 4.03-.704 3.113-.39 5.588.988 6.38.32.187.69.275 1.102.275 1.345 0 3.107-.96 4.888-2.624 1.78 1.654 3.542 2.603 4.887 2.603.41 0 .783-.09 1.106-.275 1.374-.792 1.683-3.263.973-6.365C22.02 15.096 24 13.59 24 12.004c0-1.59-1.99-3.097-5.043-4.032.704-3.11.39-5.587-.988-6.38a2.167 2.167 0 00-1.092-.278zm-.005 1.09v.006c.225 0 .406.044.558.127.666.382.955 1.835.73 3.704-.054.46-.142.945-.25 1.44a23.476 23.476 0 00-3.107-.534A23.892 23.892 0 0012.769 4.7c1.592-1.48 3.087-2.292 4.105-2.295zm-9.77.02c1.012 0 2.514.808 4.11 2.28-.686.72-1.37 1.537-2.02 2.442a22.73 22.73 0 00-3.113.538 15.02 15.02 0 01-.254-1.42c-.23-1.868.054-3.32.714-3.707.19-.09.4-.127.563-.132zm4.882 3.05c.455.468.91.992 1.36 1.564-.44-.02-.89-.034-1.345-.034-.46 0-.915.01-1.36.034.44-.572.895-1.096 1.345-1.565zM12 8.1c.74 0 1.477.034 2.202.093.406.582.802 1.203 1.183 1.86.372.64.71 1.29 1.018 1.946-.308.655-.646 1.31-1.013 1.95-.38.66-.773 1.288-1.18 1.87a25.64 25.64 0 01-4.412.005 26.64 26.64 0 01-1.183-1.86c-.372-.64-.71-1.29-1.018-1.946a25.17 25.17 0 011.013-1.954c.38-.66.773-1.286 1.18-1.868A25.245 25.245 0 0112 8.098zm-3.635.254c-.24.377-.48.763-.704 1.16-.225.39-.435.782-.635 1.174-.265-.656-.49-1.31-.676-1.947.64-.15 1.315-.283 2.015-.386zm7.26 0c.695.103 1.365.23 2.006.387-.18.632-.405 1.282-.66 1.933a25.952 25.952 0 00-1.345-2.32zm3.063.675c.484.15.944.317 1.375.498 1.732.74 2.852 1.708 2.852 2.476-.005.768-1.125 1.74-2.857 2.475-.42.18-.88.342-1.355.493a23.966 23.966 0 00-1.1-2.98c.45-1.017.81-2.01 1.085-2.964zm-13.395.004c.278.96.645 1.957 1.1 2.98a23.142 23.142 0 00-1.086 2.964c-.484-.15-.944-.318-1.37-.5-1.732-.737-2.852-1.706-2.852-2.474 0-.768 1.12-1.742 2.852-2.476.42-.18.88-.342 1.356-.494zm11.678 4.28c.265.657.49 1.312.676 1.948-.64.157-1.316.29-2.016.39a25.819 25.819 0 001.341-2.338zm-9.945.02c.2.392.41.783.64 1.175.23.39.465.772.705 1.143a22.005 22.005 0 01-2.006-.386c.18-.63.406-1.282.66-1.933zM17.92 16.32c.112.493.2.968.254 1.423.23 1.868-.054 3.32-.714 3.708-.147.09-.338.128-.563.128-1.012 0-2.514-.807-4.11-2.28.686-.72 1.37-1.536 2.02-2.44 1.107-.118 2.154-.3 3.113-.54zm-11.83.01c.96.234 2.006.415 3.107.532.66.905 1.345 1.727 2.035 2.446-1.595 1.483-3.092 2.295-4.11 2.295a1.185 1.185 0 01-.553-.132c-.666-.38-.955-1.834-.73-3.703.054-.46.142-.944.25-1.438zm4.56.64c.44.02.89.034 1.345.034.46 0 .915-.01 1.36-.034-.44.572-.895 1.095-1.345 1.565-.455-.47-.91-.993-1.36-1.565z" />
14
+ </svg>
15
+ );
16
+ };
@@ -0,0 +1,20 @@
1
+ // From Heroicons
2
+ export const SearchSVG = () => {
3
+ return (
4
+ <svg
5
+ width="24"
6
+ height="24"
7
+ xmlns="http://www.w3.org/2000/svg"
8
+ fill="none"
9
+ viewBox="0 0 24 24"
10
+ strokeWidth="1.5"
11
+ stroke="currentColor"
12
+ >
13
+ <path
14
+ strokeLinecap="round"
15
+ strokeLinejoin="round"
16
+ d="m21 21-5.197-5.197m0 0A7.5 7.5 0 1 0 5.196 5.196a7.5 7.5 0 0 0 10.607 10.607Z"
17
+ />
18
+ </svg>
19
+ );
20
+ };
@@ -0,0 +1,23 @@
1
+ export const SparklesSVG = ({
2
+ width = '32',
3
+ height = '32',
4
+ }: {
5
+ width?: string | number;
6
+ height?: string | number;
7
+ }) => (
8
+ <svg
9
+ xmlns="http://www.w3.org/2000/svg"
10
+ width={width}
11
+ height={height}
12
+ viewBox="0 0 24 24"
13
+ fill="currentColor"
14
+ stroke="currentColor"
15
+ strokeWidth="1.5"
16
+ strokeLinecap="round"
17
+ strokeLinejoin="round"
18
+ >
19
+ <path d="M9.8132 15.9038L9 18.75L8.1868 15.9038C7.75968 14.4089 6.59112 13.2403 5.09619 12.8132L2.25 12L5.09619 11.1868C6.59113 10.7597 7.75968 9.59112 8.1868 8.09619L9 5.25L9.8132 8.09619C10.2403 9.59113 11.4089 10.7597 12.9038 11.1868L15.75 12L12.9038 12.8132C11.4089 13.2403 10.2403 14.4089 9.8132 15.9038Z" />
20
+ <path d="M18.2589 8.71454L18 9.75L17.7411 8.71454C17.4388 7.50533 16.4947 6.56117 15.2855 6.25887L14.25 6L15.2855 5.74113C16.4947 5.43883 17.4388 4.49467 17.7411 3.28546L18 2.25L18.2589 3.28546C18.5612 4.49467 19.5053 5.43883 20.7145 5.74113L21.75 6L20.7145 6.25887C19.5053 6.56117 18.5612 7.50533 18.2589 8.71454Z" />
21
+ <path d="M16.8942 20.5673L16.5 21.75L16.1058 20.5673C15.8818 19.8954 15.3546 19.3682 14.6827 19.1442L13.5 18.75L14.6827 18.3558C15.3546 18.1318 15.8818 17.6046 16.1058 16.9327L16.5 15.75L16.8942 16.9327C17.1182 17.6046 17.6454 18.1318 18.3173 18.3558L19.5 18.75L18.3173 19.1442C17.6454 19.3682 17.1182 19.8954 16.8942 20.5673Z" />
22
+ </svg>
23
+ );
@@ -0,0 +1,34 @@
1
+ export const SplitEditorSVG = () => {
2
+ return (
3
+ <svg
4
+ xmlns="http://www.w3.org/2000/svg"
5
+ width="24"
6
+ height="24"
7
+ viewBox="0 0 24 24"
8
+ fill="none"
9
+ >
10
+ <rect
11
+ x="5"
12
+ y="4"
13
+ width="7.5"
14
+ height="16"
15
+ rx="1"
16
+ ry="1"
17
+ stroke="currentColor"
18
+ strokeWidth="1.5"
19
+ fill="none"
20
+ />
21
+ <rect
22
+ x="12"
23
+ y="4"
24
+ width="7.5"
25
+ height="16"
26
+ rx="1"
27
+ ry="1"
28
+ stroke="currentColor"
29
+ strokeWidth="1.5"
30
+ fill="none"
31
+ />
32
+ </svg>
33
+ );
34
+ };
@@ -0,0 +1,15 @@
1
+ // From Wikimedia
2
+ // https://commons.wikimedia.org/wiki/File:Stopsign.svg
3
+ export const StopSVG = () => (
4
+ <svg
5
+ width="15pt"
6
+ height="15pt"
7
+ version="1.1"
8
+ viewBox="0 0 15 15"
9
+ xmlns="http://www.w3.org/2000/svg"
10
+ >
11
+ <g transform="scale(.75)" fill="#fff" stroke="#000">
12
+ <path d="m17.5 0.5c1.1055 0 2 0.89453 2 2v15c0 1.1055-0.89453 2-2 2h-15c-1.1055 0-2-0.89453-2-2v-15c0-1.1055 0.89453-2 2-2z" />
13
+ </g>
14
+ </svg>
15
+ );
@@ -0,0 +1,16 @@
1
+ // https://reactsvgicons.com/
2
+ export const SvelteSVG = () => {
3
+ return (
4
+ <svg
5
+ width="24"
6
+ height="24"
7
+ xmlns="http://www.w3.org/2000/svg"
8
+ fill="currentColor"
9
+ viewBox="0 0 24 24"
10
+ strokeWidth="1.5"
11
+ stroke="none"
12
+ >
13
+ <path d="M10.354 21.125a4.44 4.44 0 01-4.765-1.767 4.109 4.109 0 01-.703-3.107 3.898 3.898 0 01.134-.522l.105-.321.287.21a7.21 7.21 0 002.186 1.092l.208.063-.02.208a1.253 1.253 0 00.226.83 1.337 1.337 0 001.435.533 1.231 1.231 0 00.343-.15l5.59-3.562a1.164 1.164 0 00.524-.778 1.242 1.242 0 00-.211-.937 1.338 1.338 0 00-1.435-.533 1.23 1.23 0 00-.343.15l-2.133 1.36a4.078 4.078 0 01-1.135.499 4.44 4.44 0 01-4.765-1.766 4.108 4.108 0 01-.702-3.108 3.855 3.855 0 011.742-2.582l5.589-3.563a4.072 4.072 0 011.135-.499 4.44 4.44 0 014.765 1.767 4.109 4.109 0 01.703 3.107 3.943 3.943 0 01-.134.522l-.105.321-.286-.21a7.204 7.204 0 00-2.187-1.093l-.208-.063.02-.207a1.255 1.255 0 00-.226-.831 1.337 1.337 0 00-1.435-.532 1.231 1.231 0 00-.343.15L8.62 9.368a1.162 1.162 0 00-.524.778 1.24 1.24 0 00.211.937 1.338 1.338 0 001.435.533 1.235 1.235 0 00.344-.151l2.132-1.36a4.067 4.067 0 011.135-.498 4.44 4.44 0 014.765 1.766 4.108 4.108 0 01.702 3.108 3.857 3.857 0 01-1.742 2.583l-5.589 3.562a4.072 4.072 0 01-1.135.499m10.358-17.95C18.484-.015 14.082-.96 10.9 1.068L5.31 4.63a6.412 6.412 0 00-2.896 4.295 6.753 6.753 0 00.666 4.336 6.43 6.43 0 00-.96 2.396 6.833 6.833 0 001.168 5.167c2.229 3.19 6.63 4.135 9.812 2.108l5.59-3.562a6.41 6.41 0 002.896-4.295 6.756 6.756 0 00-.665-4.336 6.429 6.429 0 00.958-2.396 6.831 6.831 0 00-1.167-5.168z" />
14
+ </svg>
15
+ );
16
+ };
@@ -0,0 +1,19 @@
1
+ export const TrashSVG = () => {
2
+ return (
3
+ <svg
4
+ xmlns="http://www.w3.org/2000/svg"
5
+ width="24"
6
+ height="24"
7
+ viewBox="0 0 24 24"
8
+ >
9
+ <path
10
+ fill="none"
11
+ stroke="currentColor"
12
+ d="M14.7404 9L14.3942 18M9.60577 18L9.25962 9M19.2276 5.79057C19.5696 5.84221 19.9104 5.89747 20.25 5.95629M19.2276 5.79057L18.1598 19.6726C18.0696 20.8448 17.0921 21.75 15.9164 21.75H8.08357C6.90786 21.75 5.93037 20.8448 5.8402 19.6726L4.77235 5.79057M19.2276 5.79057C18.0812 5.61744 16.9215 5.48485 15.75 5.39432M3.75 5.95629C4.08957 5.89747 4.43037 5.84221 4.77235 5.79057M4.77235 5.79057C5.91878 5.61744 7.07849 5.48485 8.25 5.39432M15.75 5.39432V4.47819C15.75 3.29882 14.8393 2.31423 13.6606 2.27652C13.1092 2.25889 12.5556 2.25 12 2.25C11.4444 2.25 10.8908 2.25889 10.3394 2.27652C9.16065 2.31423 8.25 3.29882 8.25 4.47819V5.39432M15.75 5.39432C14.5126 5.2987 13.262 5.25 12 5.25C10.738 5.25 9.48744 5.2987 8.25 5.39432"
13
+ strokeWidth="1.5"
14
+ strokeLinecap="round"
15
+ strokeLinejoin="round"
16
+ />
17
+ </svg>
18
+ );
19
+ };
@@ -0,0 +1,16 @@
1
+ // https://reactsvgicons.com/
2
+ export const TypeScriptSVG = () => {
3
+ return (
4
+ <svg
5
+ width="24"
6
+ height="24"
7
+ xmlns="http://www.w3.org/2000/svg"
8
+ fill="currentColor"
9
+ viewBox="0 0 24 24"
10
+ strokeWidth="1.5"
11
+ stroke="none"
12
+ >
13
+ <path d="M3 3h18v18H3V3m10.71 14.86c.5.98 1.51 1.73 3.09 1.73 1.6 0 2.8-.83 2.8-2.36 0-1.41-.81-2.04-2.25-2.66l-.42-.18c-.73-.31-1.04-.52-1.04-1.02 0-.41.31-.73.81-.73.48 0 .8.21 1.09.73l1.31-.87c-.55-.96-1.33-1.33-2.4-1.33-1.51 0-2.48.96-2.48 2.23 0 1.38.81 2.03 2.03 2.55l.42.18c.78.34 1.24.55 1.24 1.13 0 .48-.45.83-1.15.83-.83 0-1.31-.43-1.67-1.03l-1.38.8M13 11.25H8v1.5h1.5V20h1.75v-7.25H13v-1.5z" />
14
+ </svg>
15
+ );
16
+ };
@@ -0,0 +1,26 @@
1
+ export { SparklesSVG } from './SparklesSVG';
2
+ export { StopSVG } from './StopSVG';
3
+ export { EditSVG } from './EditSVG';
4
+ export { NewSVG } from './NewSVG';
5
+ export { GearSVG } from './GearSVG';
6
+ export { CloseSVG } from './CloseSVG';
7
+ export { TrashSVG } from './TrashSVG';
8
+ export { BugSVG } from './BugSVG';
9
+ export { FileSVG } from './FileSVG';
10
+ export { DirectoryArrowSVG } from './DirectoryArrowSVG';
11
+ export { PlaySVG } from './PlaySVG';
12
+ export { QuestionMarkSVG } from './QuestionMarkSVG';
13
+ export { PinSVG } from './PinSVG';
14
+ export { FolderSVG } from './FolderSVG';
15
+ export { SearchSVG } from './SearchSVG';
16
+ export { JavaScriptSVG } from './JavaScriptSVG';
17
+ export { TypeScriptSVG } from './TypeScriptSVG';
18
+ export { ReactSVG } from './ReactSVG';
19
+ export { SvelteSVG } from './SvelteSVG';
20
+ export { HtmlSVG } from './HtmlSVG';
21
+ export { CssSVG } from './CssSVG';
22
+ export { JsonSVG } from './JsonSVG';
23
+ export { MarkdownSVG } from './MarkdownSVG';
24
+ export { MicSVG } from './MicSVG';
25
+ export { MicOffSVG } from './MicOffSVG';
26
+ export { AdjustmentSVG } from './AdjustmentSVG';
@@ -0,0 +1,91 @@
1
+ import { useContext } from 'react';
2
+ import { VZCodeContext } from '../VZCodeContext';
3
+ import './style.scss';
4
+
5
+ export const ImageViewer = () => {
6
+ const { activePane, content } = useContext(VZCodeContext);
7
+
8
+ // Get the active file
9
+ const activeFileId = activePane?.activeFileId;
10
+ const activeFile =
11
+ activeFileId && content?.files?.[activeFileId];
12
+
13
+ if (!activeFile) {
14
+ return (
15
+ <div className="image-viewer-error">
16
+ No file selected
17
+ </div>
18
+ );
19
+ }
20
+
21
+ // Check if it's an image file based on extension
22
+ const isImageFile = activeFile.name.match(
23
+ /\.(png|jpg|jpeg|gif|bmp|svg|webp)$/i,
24
+ );
25
+
26
+ if (!isImageFile) {
27
+ return (
28
+ <div className="image-viewer-error">
29
+ Not an image file
30
+ </div>
31
+ );
32
+ }
33
+
34
+ // Get the MIME type from the file extension
35
+ const getImageMimeType = (fileName: string): string => {
36
+ const extension = fileName
37
+ .split('.')
38
+ .pop()
39
+ ?.toLowerCase();
40
+ switch (extension) {
41
+ case 'jpg':
42
+ case 'jpeg':
43
+ return 'image/jpeg';
44
+ case 'png':
45
+ return 'image/png';
46
+ case 'gif':
47
+ return 'image/gif';
48
+ case 'bmp':
49
+ return 'image/bmp';
50
+ case 'svg':
51
+ return 'image/svg+xml';
52
+ case 'webp':
53
+ return 'image/webp';
54
+ default:
55
+ return 'image/png'; // fallback
56
+ }
57
+ };
58
+
59
+ const mimeType = getImageMimeType(activeFile.name);
60
+ const extension = activeFile.name
61
+ .split('.')
62
+ .pop()
63
+ ?.toLowerCase();
64
+
65
+ // For SVG files, the content might already be the SVG text, not base64
66
+ const imageSrc =
67
+ extension === 'svg' &&
68
+ !activeFile.text.startsWith('PHN2Zw') // Check if it's not base64-encoded SVG
69
+ ? `data:${mimeType};utf8,${encodeURIComponent(activeFile.text)}`
70
+ : `data:${mimeType};base64,${activeFile.text}`;
71
+
72
+ return (
73
+ <div className="image-viewer">
74
+ <div className="image-viewer-content">
75
+ <img
76
+ src={imageSrc}
77
+ alt={activeFile.name}
78
+ className="image-viewer-img"
79
+ onError={(e) => {
80
+ console.error(
81
+ 'Failed to load image:',
82
+ activeFile.name,
83
+ );
84
+ (e.target as HTMLImageElement).style.display =
85
+ 'none';
86
+ }}
87
+ />
88
+ </div>
89
+ </div>
90
+ );
91
+ };
@@ -0,0 +1,41 @@
1
+ .image-viewer {
2
+ height: 100%;
3
+ width: 100%;
4
+ display: flex;
5
+ flex-direction: column;
6
+ background-color: var(--bs-body-bg);
7
+ color: var(--bs-body-color);
8
+ }
9
+
10
+ .image-viewer-header {
11
+ padding: 12px 16px;
12
+ border-bottom: 1px solid var(--bs-border-color);
13
+ background-color: var(--bs-tertiary-bg);
14
+ }
15
+
16
+ .image-viewer-content {
17
+ flex: 1;
18
+ padding: 20px;
19
+ display: flex;
20
+ align-items: center;
21
+ justify-content: center;
22
+ overflow: auto;
23
+ }
24
+
25
+ .image-viewer-img {
26
+ max-width: 100%;
27
+ max-height: 100%;
28
+ border-radius: 4px;
29
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
30
+ object-fit: contain;
31
+ }
32
+
33
+ .image-viewer-error {
34
+ display: flex;
35
+ align-items: center;
36
+ justify-content: center;
37
+ height: 100%;
38
+ color: var(--bs-text-muted);
39
+ font-family: var(--bs-font-monospace);
40
+ font-size: 14px;
41
+ }
@@ -0,0 +1,134 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import {
3
+ extractTimestampFromId,
4
+ shouldShowNotification,
5
+ handleJoin,
6
+ handleLeave,
7
+ } from '.';
8
+
9
+ describe('extractTimestampFromId', () => {
10
+ it('Extracts a timestamp from a given ID', () => {
11
+ const id = 'abcd-xyz';
12
+ const timestamp = extractTimestampFromId(id);
13
+ expect(timestamp).toBe(parseInt('abcd', 36));
14
+ });
15
+ });
16
+
17
+ describe('shouldShowNotification', () => {
18
+ it('Returns true if remote presence timestamp is greater than local', () => {
19
+ const remoteId = 'abcd-xyz';
20
+ const localId = 'aaa-xyz';
21
+ const result = shouldShowNotification(
22
+ remoteId,
23
+ localId,
24
+ );
25
+ expect(result).toBe(true);
26
+ });
27
+
28
+ it('Returns false if remote presence timestamp is not greater than local', () => {
29
+ const remoteId = 'aaa-xyz';
30
+ const localId = 'abcd-xyz';
31
+ const result = shouldShowNotification(
32
+ remoteId,
33
+ localId,
34
+ );
35
+ expect(result).toBe(false);
36
+ });
37
+ });
38
+
39
+ describe('handleJoin', () => {
40
+ it('Adds a new user if not already present', () => {
41
+ const presenceId = 'abcd-xyz';
42
+ const username = 'John';
43
+ const alreadyJoined = { current: new Map() };
44
+ let notifications = [];
45
+ const setNotifications = (updater) => {
46
+ notifications = updater(notifications);
47
+ };
48
+
49
+ handleJoin(
50
+ presenceId,
51
+ username,
52
+ alreadyJoined,
53
+ setNotifications,
54
+ );
55
+
56
+ expect(notifications.length).toBe(1);
57
+ expect(notifications[0].user).toBe(username);
58
+ expect(notifications[0].join).toBe(true);
59
+ expect(alreadyJoined.current.has(presenceId)).toBe(
60
+ true,
61
+ );
62
+ });
63
+
64
+ it('Updates username if user already present', () => {
65
+ const presenceId = 'abcd-xyz';
66
+ const username = 'John';
67
+ const alreadyJoined = {
68
+ current: new Map([[presenceId, 'Doe']]),
69
+ };
70
+ let notifications = [];
71
+ const setNotifications = (updater) => {
72
+ notifications = updater(notifications);
73
+ };
74
+
75
+ handleJoin(
76
+ presenceId,
77
+ username,
78
+ alreadyJoined,
79
+ setNotifications,
80
+ );
81
+
82
+ expect(notifications.length).toBe(0);
83
+ expect(alreadyJoined.current.get(presenceId)).toBe(
84
+ username,
85
+ );
86
+ });
87
+ });
88
+
89
+ describe('handleLeave', () => {
90
+ it('Removes an existing user', () => {
91
+ const presenceId = 'abcd-xyz';
92
+ const username = 'John';
93
+ const alreadyJoined = {
94
+ current: new Map([[presenceId, username]]),
95
+ };
96
+ let notifications = [];
97
+ const setNotifications = (updater) => {
98
+ notifications = updater(notifications);
99
+ };
100
+
101
+ handleLeave(
102
+ presenceId,
103
+ alreadyJoined,
104
+ setNotifications,
105
+ );
106
+
107
+ expect(notifications.length).toBe(1);
108
+ expect(notifications[0].user).toBe(username);
109
+ expect(notifications[0].join).toBe(false);
110
+ expect(alreadyJoined.current.has(presenceId)).toBe(
111
+ false,
112
+ );
113
+ });
114
+
115
+ it('Does nothing if user not present', () => {
116
+ const presenceId = 'abcd-xyz';
117
+ const alreadyJoined = { current: new Map() };
118
+ let notifications = [];
119
+ const setNotifications = (updater) => {
120
+ notifications = updater(notifications);
121
+ };
122
+
123
+ handleLeave(
124
+ presenceId,
125
+ alreadyJoined,
126
+ setNotifications,
127
+ );
128
+
129
+ expect(notifications.length).toBe(0);
130
+ expect(alreadyJoined.current.has(presenceId)).toBe(
131
+ false,
132
+ );
133
+ });
134
+ });