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,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
|
+
});
|