collabmd 0.1.44 → 0.1.46
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/README.md +39 -19
- package/bin/collabmd.js +27 -1
- package/dist/client/assets/CodeMirrorEditor-UCMRAMCV-CYM5nxUi.js +1 -0
- package/dist/client/assets/browser-utils--H_DkyEF.js +1 -0
- package/dist/client/assets/chunk-ERSWUTIN-hHdvnhaY.js +41 -0
- package/dist/client/assets/{chunk-CZQZEIFA-DT3N9fo7.js → chunk-YGJVY4SA-CjSe7Bx7.js} +2 -2
- package/dist/client/assets/comment-thread-store-CdcwrObc.js +1 -0
- package/dist/client/assets/comment-threads-9J6Em-_y.js +4 -0
- package/dist/client/assets/comment-ui-shared-BC4pt4w8.js +7 -0
- package/dist/client/assets/dist-B5HTH2BO.js +1 -0
- package/dist/client/assets/{dist-DhLvC8DS.js → dist-B5Jf1SLZ.js} +2 -2
- package/dist/client/assets/{dist-CnL7Y2sM.js → dist-BDt3rz7l.js} +1 -1
- package/dist/client/assets/dist-BEwK4csK.js +1 -0
- package/dist/client/assets/dist-BKW4PALF.js +1 -0
- package/dist/client/assets/dist-BWcHHOx-.js +1 -0
- package/dist/client/assets/{dist-CYZmbokb.js → dist-BalScOwZ.js} +9 -9
- package/dist/client/assets/dist-Bf8f4HmM.js +6 -0
- package/dist/client/assets/dist-BjHJDtyU.js +1 -0
- package/dist/client/assets/dist-Bm6pf9Bl.js +1 -0
- package/dist/client/assets/dist-BoVBdSjk.js +1 -0
- package/dist/client/assets/dist-CC5IrQMG.js +1 -0
- package/dist/client/assets/{dist-C11hHqv_.js → dist-CCP_AKPG.js} +12 -12
- package/dist/client/assets/dist-CZgOmxK6.js +1 -0
- package/dist/client/assets/{dist-D2AQ-76g.js → dist-Cqhv5LAk.js} +1 -1
- package/dist/client/assets/{dist-MvcV4190.js → dist-D-Z6FLem.js} +1 -1
- package/dist/client/assets/dist-DLMiIsNC.js +11 -0
- package/dist/client/assets/{dist-3bgCp7iY.js → dist-DZKu9Cs8.js} +1 -1
- package/dist/client/assets/dist-DaYdhcVs.js +1 -0
- package/dist/client/assets/dist-FWJsgGYI.js +1 -0
- package/dist/client/assets/dist-Kh2COra_.js +1 -0
- package/dist/client/assets/{dist-JYEDRrPT.js → dist-PDZ3bSqm.js} +1 -1
- package/dist/client/assets/{dist-Drp8uu5J.js → dist-TjMh8n1u.js} +1 -1
- package/dist/client/assets/{dist-DGb27Dl6.js → dist-dtAdgL39.js} +1 -1
- package/dist/client/assets/dist-jv4WdPnK.js +9 -0
- package/dist/client/assets/{dist-DK5Gs2wa.js → dist-rGBmqxC7.js} +1 -1
- package/dist/client/assets/{dist-DCzuX1AF.js → dist-shrl6K_0.js} +1 -1
- package/dist/client/assets/dist-tOaegDYS2.js +1 -0
- package/dist/client/assets/dist-ule2n6ak.js +1 -0
- package/dist/client/assets/dist-xElaxbia.js +1 -0
- package/dist/client/assets/drawio-editor-FzY1cVw8.js +9 -0
- package/dist/client/assets/drawioEditor-BjiKJqlc.js +2 -0
- package/dist/client/assets/editor-session-Cm9ekeNo.js +54 -0
- package/dist/client/assets/embedded-editor-base-CwTkk911.css +1 -0
- package/dist/client/assets/{en-GADW4C6J-DQ6zyCEM.js → en-7NFCYGKX-BrgYZAEe.js} +1 -1
- package/dist/client/assets/{preview-render-profile-BZzGSg6K.js → es-DXGV3RHd.js} +13 -13
- package/dist/client/assets/excalidraw-editor-B46zsTFI.js +9 -0
- package/dist/client/assets/excalidraw-editor-Bt7gMSoh.css +1 -0
- package/dist/client/assets/excalidraw-scene-BOben2gr.js +1 -0
- package/dist/client/assets/excalidrawEditor-CT2pHq7e.js +2 -0
- package/dist/client/assets/export-page-A7NHawJY.js +39 -0
- package/dist/client/assets/exportDocument-B2L8fe34.css +1 -0
- package/dist/client/assets/exportDocument-BaqcOAAW.js +2 -0
- package/dist/client/assets/file-icon-svg-CQAjlWjK.js +1 -0
- package/dist/client/assets/file-kind-BbqgGzpc.js +1 -0
- package/dist/client/assets/image-QXRYHUNP-SWEV8H0J.js +1 -0
- package/dist/client/assets/index-ClbjMBVt.js +2 -0
- package/dist/client/assets/index-Pum8ET-5.css +1 -0
- package/dist/client/assets/main-B3uPyqic.js +893 -0
- package/dist/client/assets/modulepreload-polyfill-P2Xu9kJm.js +1 -0
- package/dist/client/assets/pdf-BL9Pr2D5.js +54 -0
- package/dist/client/assets/pdf.worker-CLesOks4.mjs +64857 -0
- package/dist/client/assets/pdf_viewer-FVkkThnz.js +551 -0
- package/dist/client/assets/preview-diagram-utils-txBCoW8d.js +1 -0
- package/dist/client/assets/{preview-render-compiler-BSaQO_p3.js → preview-render-compiler-BD-XhkS2.js} +2 -2
- package/dist/client/assets/preview-render-compiler-BKedaMxx.js +1 -0
- package/dist/client/assets/preview-render-profile-CE-5rVTC.js +1 -0
- package/dist/client/assets/{preview-render-worker-B42LWvrD.js → preview-render-worker-CiPqc9n6.js} +20 -20
- package/dist/client/assets/prod-Dy20X7eT.js +1 -0
- package/dist/client/assets/prod-hnwh7wRM.js +177 -0
- package/dist/client/assets/quick-switcher-controller-fqOYR7qR.js +12 -0
- package/dist/client/assets/runtime-config-BLrEKVO1.js +1 -0
- package/dist/client/assets/vault-api-client-_LpV1aIn.js +6 -0
- package/dist/client/assets/{yjs-provider-reset-guard-Cox4nxwu.js → yjs-provider-reset-guard-DE0vINDP.js} +1 -1
- package/dist/client/drawio-editor.html +3 -2
- package/dist/client/excalidraw-editor.html +3 -2
- package/dist/client/export-document.html +4 -3
- package/dist/client/index.html +109 -27
- package/docker-compose.yml +42 -0
- package/package.json +26 -15
- package/scripts/local-structurizr-compose.mjs +101 -0
- package/src/client/app/drawio-editor-entry.js +0 -3
- package/src/client/app/drawio-editor.html +1 -0
- package/src/client/app/excalidraw-editor-entry.js +0 -3
- package/src/client/app/excalidraw-editor.html +1 -0
- package/src/client/app/export-document-entry.js +0 -3
- package/src/client/app/export-document.html +1 -0
- package/src/client/app/index.html +106 -24
- package/src/client/app/main-entry.js +0 -5
- package/src/client/application/app-shell/chat-feature.js +180 -124
- package/src/client/application/app-shell/comments-feature.js +20 -11
- package/src/client/application/app-shell/git-feature.js +16 -9
- package/src/client/application/app-shell/presence-feature.js +18 -11
- package/src/client/application/app-shell/ui-feature-shell.js +78 -17
- package/src/client/application/app-shell/ui-feature-sidebar.js +74 -1
- package/src/client/application/app-shell/ui-feature-tab-activity.js +2 -2
- package/src/client/application/app-shell/ui-feature-toolbar.js +2 -52
- package/src/client/application/app-shell/workspace-feature.js +27 -84
- package/src/client/application/app-shell-elements.js +9 -1
- package/src/client/application/diagram-chrome.js +161 -166
- package/src/client/application/diagram-preview-export.js +7 -34
- package/src/client/application/diagram-preview-hydrator.js +1 -19
- package/src/client/application/markdown-frontmatter.js +0 -2
- package/src/client/application/mermaid-preview-hydrator.js +1 -14
- package/src/client/application/pdf-preview-controller.js +992 -0
- package/src/client/application/plantuml-preview-hydrator.js +0 -10
- package/src/client/application/preview-diagram-utils.js +31 -2
- package/src/client/application/preview-render-compiler.js +2 -1
- package/src/client/application/preview-renderer.js +2 -2
- package/src/client/application/quick-switcher-loader.js +3 -1
- package/src/client/application/structurizr-preview-controller.js +395 -0
- package/src/client/application/workspace-coordinator.js +25 -3
- package/src/client/application/workspace-preview-controller.js +91 -5
- package/src/client/bootstrap/collabmd-app-shell.js +155 -43
- package/src/client/browser-utils.js +44 -0
- package/src/client/domain/excalidraw-collaboration.js +0 -2
- package/src/client/domain/room.js +38 -0
- package/src/client/domain/runtime-paths.js +26 -5
- package/src/client/domain/structurizr-language.js +99 -0
- package/src/client/domain/vault-paths.js +48 -0
- package/src/client/excalidraw-editor.js +833 -51
- package/src/client/export/export-pipeline.js +6 -34
- package/src/client/infrastructure/auth-client.js +4 -10
- package/src/client/infrastructure/browser-notification-port.js +33 -20
- package/src/client/infrastructure/browser-preferences-port.js +46 -9
- package/src/client/infrastructure/comment-thread-store.js +34 -13
- package/src/client/infrastructure/editor-session.js +10 -0
- package/src/client/infrastructure/editor-view-adapter.js +133 -5
- package/src/client/infrastructure/excalidraw-room-client.js +103 -14
- package/src/client/infrastructure/git-api-client.js +5 -0
- package/src/client/infrastructure/lobby-presence.js +1 -0
- package/src/client/infrastructure/vault-api-client.js +51 -0
- package/src/client/infrastructure/workspace-sync-client.js +13 -1
- package/src/client/presentation/comment-overview-controller.js +18 -53
- package/src/client/presentation/comment-ui/comment-ui-card.js +1 -12
- package/src/client/presentation/comment-ui/comment-ui-layout.js +242 -219
- package/src/client/presentation/comment-ui/comment-ui-render.js +32 -45
- package/src/client/presentation/comment-ui/comment-ui-shared.js +68 -13
- package/src/client/presentation/comment-ui-controller.js +5 -1
- package/src/client/presentation/drawio-embed-controller.js +9 -102
- package/src/client/presentation/drawio-viewer.js +118 -0
- package/src/client/presentation/excalidraw-embed-controller.js +74 -8
- package/src/client/presentation/file-action-controller.js +134 -0
- package/src/client/presentation/file-explorer-controller.js +33 -3
- package/src/client/presentation/file-explorer-view.js +59 -34
- package/src/client/presentation/file-icon-svg.js +40 -0
- package/src/client/presentation/file-tree-state.js +12 -4
- package/src/client/presentation/git-diff-view-controller.js +731 -3
- package/src/client/presentation/outline-controller.js +86 -0
- package/src/client/presentation/quick-switcher-controller.js +257 -74
- package/src/client/presentation/quick-switcher-text-search.js +3 -3
- package/src/client/presentation/toast-controller.js +3 -0
- package/src/client/styles/components/feedback.css +6 -0
- package/src/client/styles/components/navigation.css +11 -1
- package/src/client/styles/features/collaboration-chat.css +49 -53
- package/src/client/styles/features/collaboration-presence.css +9 -7
- package/src/client/styles/features/comment-card.css +25 -11
- package/src/client/styles/features/comment-overlays.css +0 -4
- package/src/client/styles/features/comment-overview.css +44 -21
- package/src/client/styles/features/comments-drawer.css +2 -100
- package/src/client/styles/features/diagram-preview.css +81 -18
- package/src/client/styles/features/diff-special.css +309 -0
- package/src/client/styles/features/embedded-preview.css +8 -33
- package/src/client/styles/features/quick-switcher.css +8 -0
- package/src/client/styles/foundation/themes.css +6 -0
- package/src/client/styles/foundation/tokens.css +1 -0
- package/src/client/styles/layout/editor-page.css +40 -0
- package/src/client/styles/layout/responsive.css +31 -1
- package/src/client/styles/layout/shell.css +383 -7
- package/src/client/styles/layout/sidebar.css +47 -15
- package/src/client/styles/layout/view-modes.css +1 -0
- package/src/client/styles/overrides/codemirror.css +217 -15
- package/src/client/styles/style.css +1 -0
- package/src/client/styles/surfaces/excalidraw-editor.css +294 -0
- package/src/domain/comment-threads.js +63 -5
- package/src/domain/excalidraw-room-codec.js +6 -4
- package/src/domain/file-kind.js +35 -3
- package/src/server/application/workspace-reconciliation.js +1 -1
- package/src/server/config/env.js +79 -0
- package/src/server/create-app-server.js +14 -3
- package/src/server/domain/bases/base-definition.js +1 -1
- package/src/server/domain/bases/base-expression-runtime.js +1 -4
- package/src/server/domain/bases/base-index-snapshot-store.js +1 -4
- package/src/server/domain/bases/base-query-service.js +1 -77
- package/src/server/domain/bases/base-transform.js +1 -4
- package/src/server/domain/collaboration/collaboration-room.js +55 -21
- package/src/server/domain/comment-overview.js +25 -8
- package/src/server/domain/docx-exporter.js +24 -33
- package/src/server/domain/ripgrep-search-service.js +7 -1
- package/src/server/domain/workspace-state.js +32 -11
- package/src/server/infrastructure/git/diff-service.js +29 -13
- package/src/server/infrastructure/git/git-service.js +61 -10
- package/src/server/infrastructure/git/history-service.js +96 -29
- package/src/server/infrastructure/git/image-mime.js +14 -0
- package/src/server/infrastructure/git/path-utils.js +1 -1
- package/src/server/infrastructure/git/responses.js +0 -95
- package/src/server/infrastructure/git/untracked-files.js +28 -1
- package/src/server/infrastructure/github/github-app-client.js +22 -15
- package/src/server/infrastructure/http/create-git-api-query-handler.js +31 -1
- package/src/server/infrastructure/http/create-request-handler.js +18 -31
- package/src/server/infrastructure/http/create-static-handler.js +5 -2
- package/src/server/infrastructure/http/create-structurizr-api-handler.js +145 -0
- package/src/server/infrastructure/http/create-vault-api-command-handler.js +70 -8
- package/src/server/infrastructure/http/create-vault-api-query-handler.js +51 -3
- package/src/server/infrastructure/http/http-errors.js +7 -1
- package/src/server/infrastructure/http/http-request-helpers.js +22 -0
- package/src/server/infrastructure/persistence/path-utils.js +1 -1
- package/src/server/infrastructure/persistence/vault-file-store.js +3 -0
- package/src/server/infrastructure/structurizr/structurizr-embed.css +43 -0
- package/src/server/infrastructure/structurizr/structurizr-workspace-service.js +374 -0
- package/src/server/infrastructure/websocket/attach-collaboration-gateway.js +4 -22
- package/dist/client/assets/CodeMirrorEditor-UCMRAMCV-CbSISC1w.js +0 -1
- package/dist/client/assets/bases-preview-controller-2pLHk0uk.js +0 -143
- package/dist/client/assets/browser-utils-CKtI_rZl.js +0 -1
- package/dist/client/assets/button-BCEiw4Rw.js +0 -1
- package/dist/client/assets/chunk-AXR3VHVW-0GTk_evH.js +0 -41
- package/dist/client/assets/comment-threads-D-iKphIp.js +0 -4
- package/dist/client/assets/diagram-action-icons-aa_KOv9h.js +0 -36
- package/dist/client/assets/dist-BH87JoHx.js +0 -11
- package/dist/client/assets/dist-BIkPRZO7.js +0 -6
- package/dist/client/assets/dist-BqCsFpx4.js +0 -1
- package/dist/client/assets/dist-C2JHCt8U.js +0 -1
- package/dist/client/assets/dist-CZjTtmTu.js +0 -1
- package/dist/client/assets/dist-Cig4Ov_X.js +0 -1
- package/dist/client/assets/dist-CqQEC8gR.js +0 -1
- package/dist/client/assets/dist-D4Rzqod1.js +0 -1
- package/dist/client/assets/dist-D5sn-mwk.js +0 -9
- package/dist/client/assets/dist-D7G-n87v.js +0 -1
- package/dist/client/assets/dist-DRqOrNgN.js +0 -1
- package/dist/client/assets/dist-DetkMS_4.js +0 -1
- package/dist/client/assets/dist-Dp6FVkQ1.js +0 -1
- package/dist/client/assets/dist-DtbjNB28.js +0 -1
- package/dist/client/assets/dist-DyrYcvpo2.js +0 -1
- package/dist/client/assets/dist-GrygD2zE.js +0 -1
- package/dist/client/assets/dist-HmEpWUow.js +0 -1
- package/dist/client/assets/dist-RTa81Km7.js +0 -1
- package/dist/client/assets/drawio-editor-DDvFoarP.js +0 -9
- package/dist/client/assets/drawio-embed-controller-BD1GTRwn.js +0 -1
- package/dist/client/assets/drawioEditor-Bu5grlH5.js +0 -2
- package/dist/client/assets/editor-session-DGlpiAsK.js +0 -16
- package/dist/client/assets/embedded-editor-base-C9roKYpW.css +0 -1
- package/dist/client/assets/excalidraw-editor-BHu6k8d4.js +0 -9
- package/dist/client/assets/excalidraw-editor-Cr_184EF.css +0 -1
- package/dist/client/assets/excalidraw-embed-controller-y2fenN_g.js +0 -1
- package/dist/client/assets/excalidraw-embed-reconciler-DGiagExg.js +0 -1
- package/dist/client/assets/excalidraw-scene-BvsQ6mLP.js +0 -176
- package/dist/client/assets/excalidrawEditor-4vFE--Uw.js +0 -2
- package/dist/client/assets/export-page-Cby8JzPO.js +0 -39
- package/dist/client/assets/exportDocument-C9v-bc7I.css +0 -1
- package/dist/client/assets/exportDocument-CRHCfsdo.js +0 -2
- package/dist/client/assets/file-history-view-controller-BDjEV9xJ.js +0 -112
- package/dist/client/assets/file-kind-BKPOIRPE.js +0 -1
- package/dist/client/assets/git-diff-view-controller-DgdM1aqb.js +0 -150
- package/dist/client/assets/git-panel-controller-BDTR2HVp.js +0 -207
- package/dist/client/assets/image-XGD7JQR6-XDUWHRpO.js +0 -1
- package/dist/client/assets/index-7xOyXDhP.css +0 -1
- package/dist/client/assets/index-Bz1KuQ6y.js +0 -2
- package/dist/client/assets/inter-cyrillic-ext-wght-normal-BOeWTOD4.woff2 +0 -0
- package/dist/client/assets/inter-cyrillic-wght-normal-DqGufNeO.woff2 +0 -0
- package/dist/client/assets/inter-greek-ext-wght-normal-DlzME5K_.woff2 +0 -0
- package/dist/client/assets/inter-greek-wght-normal-CkhJZR-_.woff2 +0 -0
- package/dist/client/assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2 +0 -0
- package/dist/client/assets/inter-latin-wght-normal-Dx4kXJAl.woff2 +0 -0
- package/dist/client/assets/inter-vietnamese-wght-normal-CBcvBZtf.woff2 +0 -0
- package/dist/client/assets/jetbrains-mono-cyrillic-400-normal-BEIGL1Tu.woff2 +0 -0
- package/dist/client/assets/jetbrains-mono-cyrillic-400-normal-ugxPyKxw.woff +0 -0
- package/dist/client/assets/jetbrains-mono-greek-400-normal-B9oWc5Lo.woff +0 -0
- package/dist/client/assets/jetbrains-mono-greek-400-normal-C190GLew.woff2 +0 -0
- package/dist/client/assets/jetbrains-mono-latin-400-normal-6-qcROiO.woff +0 -0
- package/dist/client/assets/jetbrains-mono-latin-400-normal-V6pRDFza.woff2 +0 -0
- package/dist/client/assets/jetbrains-mono-latin-ext-400-normal-Bc8Ftmh3.woff2 +0 -0
- package/dist/client/assets/jetbrains-mono-latin-ext-400-normal-fXTG6kC5.woff +0 -0
- package/dist/client/assets/jetbrains-mono-vietnamese-400-normal-CqNFfHCs.woff +0 -0
- package/dist/client/assets/main-Ci07m9oP.js +0 -152
- package/dist/client/assets/preview-diagram-utils-n_bbJA9V.js +0 -1
- package/dist/client/assets/preview-render-compiler-csGM5ZFz.js +0 -1
- package/dist/client/assets/quick-switcher-controller-CoiYvllm.js +0 -12
- package/dist/client/assets/room-CDrmIdDt.js +0 -1
- package/dist/client/assets/runtime-config-CDgeuPWN.js +0 -1
- package/dist/client/assets/runtime-config-loader-BHGniHP5.js +0 -1
- package/dist/client/assets/runtime-paths-DvpihCEq.js +0 -1
- package/dist/client/assets/segmented-control-DJrHSz8M.js +0 -1
- package/dist/client/assets/vault-api-client-sLB2bo56.js +0 -1
- package/dist/client/assets/vault-paths-CVkuh0j-.js +0 -5
- package/src/client/bootstrap/lazy-controller-feature.js +0 -433
- package/src/client/infrastructure/backlinks-api-client.js +0 -16
- package/src/client/infrastructure/plantuml-api-client.js +0 -19
- package/src/client/infrastructure/runtime-config-loader.js +0 -56
- package/src/server/domain/collaboration/room-persistence-controller.js +0 -80
- package/src/server/infrastructure/git/command-runner.js +0 -49
- package/src/server/infrastructure/git/errors.js +0 -8
- package/src/server/infrastructure/http/create-esm-proxy-handler.js +0 -130
|
@@ -55,6 +55,21 @@
|
|
|
55
55
|
<span class="sidebar-create-btn-label">Create</span>
|
|
56
56
|
</button>
|
|
57
57
|
</div>
|
|
58
|
+
<button
|
|
59
|
+
class="ui-icon-button sidebar-options-btn"
|
|
60
|
+
id="fileExplorerOptionsBtn"
|
|
61
|
+
type="button"
|
|
62
|
+
aria-label="File explorer options"
|
|
63
|
+
aria-expanded="false"
|
|
64
|
+
aria-haspopup="menu"
|
|
65
|
+
title="File explorer options"
|
|
66
|
+
>
|
|
67
|
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true">
|
|
68
|
+
<circle cx="12" cy="5" r="1"></circle>
|
|
69
|
+
<circle cx="12" cy="12" r="1"></circle>
|
|
70
|
+
<circle cx="12" cy="19" r="1"></circle>
|
|
71
|
+
</svg>
|
|
72
|
+
</button>
|
|
58
73
|
</div>
|
|
59
74
|
</div>
|
|
60
75
|
<div class="ui-nav-tabs" id="sidebarTabs">
|
|
@@ -111,6 +126,19 @@
|
|
|
111
126
|
<nav id="fileTree" class="file-tree" aria-label="File explorer"></nav>
|
|
112
127
|
<section id="commentOverviewPanel" class="comment-overview-panel hidden" aria-label="Open comments"></section>
|
|
113
128
|
<div id="gitPanel" class="git-panel hidden" aria-label="Git changes"></div>
|
|
129
|
+
<div
|
|
130
|
+
class="sidebar-resizer"
|
|
131
|
+
id="sidebarResizer"
|
|
132
|
+
role="separator"
|
|
133
|
+
aria-orientation="vertical"
|
|
134
|
+
aria-label="Resize sidebar"
|
|
135
|
+
aria-controls="sidebar"
|
|
136
|
+
aria-valuemin="220"
|
|
137
|
+
aria-valuemax="420"
|
|
138
|
+
aria-valuenow="260"
|
|
139
|
+
tabindex="0"
|
|
140
|
+
title="Drag to resize sidebar"
|
|
141
|
+
></div>
|
|
114
142
|
</aside>
|
|
115
143
|
|
|
116
144
|
<button
|
|
@@ -138,7 +166,24 @@
|
|
|
138
166
|
</div>
|
|
139
167
|
|
|
140
168
|
<div class="toolbar-center" id="toolbarCenter">
|
|
141
|
-
<
|
|
169
|
+
<button
|
|
170
|
+
class="ui-button ui-button--secondary ui-button--compact toolbar-search-trigger"
|
|
171
|
+
id="toolbarSearchBtn"
|
|
172
|
+
type="button"
|
|
173
|
+
aria-label="Search files and text"
|
|
174
|
+
aria-controls="quickSwitcher"
|
|
175
|
+
aria-haspopup="dialog"
|
|
176
|
+
aria-keyshortcuts="Meta+K Control+K"
|
|
177
|
+
title="Search files and text (⌘K)"
|
|
178
|
+
>
|
|
179
|
+
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true">
|
|
180
|
+
<circle cx="11" cy="11" r="7"></circle>
|
|
181
|
+
<line x1="20" y1="20" x2="16.65" y2="16.65"></line>
|
|
182
|
+
</svg>
|
|
183
|
+
<span class="toolbar-search-label">Search</span>
|
|
184
|
+
<kbd class="toolbar-search-kbd">⌘K</kbd>
|
|
185
|
+
</button>
|
|
186
|
+
<div class="ui-segmented-control view-toggle" id="toolbarViewToggle" role="group" aria-label="View mode">
|
|
142
187
|
<button class="ui-segmented-btn active view-btn" data-view="split" aria-label="Split view" aria-pressed="true" title="Split view">
|
|
143
188
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
144
189
|
<rect x="3" y="3" width="18" height="18" rx="2"></rect>
|
|
@@ -186,7 +231,7 @@
|
|
|
186
231
|
|
|
187
232
|
<div class="user-avatars toolbar-presence-cluster" id="userAvatars"></div>
|
|
188
233
|
|
|
189
|
-
<section class="presence-panel hidden" id="presencePanel" aria-label="Online users" aria-hidden="true">
|
|
234
|
+
<section class="collaboration-popover presence-panel hidden" id="presencePanel" aria-label="Online users" aria-hidden="true">
|
|
190
235
|
<div class="presence-panel-header">
|
|
191
236
|
<div>
|
|
192
237
|
<h2 class="presence-panel-title">Online now</h2>
|
|
@@ -197,7 +242,7 @@
|
|
|
197
242
|
</section>
|
|
198
243
|
|
|
199
244
|
<div class="chat-container" id="chatContainer">
|
|
200
|
-
<button class="ui-button ui-button--ghost ui-button--toolbar chat-toggle-btn" id="chatToggleBtn" aria-label="Open team chat" aria-expanded="false" title="Team chat">
|
|
245
|
+
<button type="button" class="ui-button ui-button--ghost ui-button--toolbar chat-toggle-btn" id="chatToggleBtn" aria-label="Open team chat" aria-controls="chatPanel" aria-expanded="false" title="Team chat">
|
|
201
246
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
202
247
|
<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"></path>
|
|
203
248
|
</svg>
|
|
@@ -206,23 +251,44 @@
|
|
|
206
251
|
</button>
|
|
207
252
|
<div class="toast-container chat-toast-container" id="chatToastContainer" aria-live="polite" aria-atomic="true"></div>
|
|
208
253
|
|
|
209
|
-
<section class="chat-panel hidden" id="chatPanel" aria-
|
|
254
|
+
<section class="collaboration-popover chat-panel hidden" id="chatPanel" role="dialog" aria-labelledby="chatPanelTitle">
|
|
210
255
|
<div class="chat-panel-header">
|
|
211
|
-
<div>
|
|
212
|
-
<h2 class="chat-panel-title">Team chat</h2>
|
|
213
|
-
<p class="chat-panel-status" id="chatStatus">Syncing...</p>
|
|
256
|
+
<div class="chat-panel-heading">
|
|
257
|
+
<h2 class="chat-panel-title" id="chatPanelTitle">Team chat</h2>
|
|
258
|
+
<p class="chat-panel-status" id="chatStatus" role="status" aria-live="polite" aria-atomic="true">Syncing...</p>
|
|
259
|
+
</div>
|
|
260
|
+
<div class="chat-panel-actions">
|
|
261
|
+
<span class="ui-status-badge ui-status-badge--accent chat-notification-status hidden" id="chatNotificationStatus" role="status"></span>
|
|
262
|
+
<button
|
|
263
|
+
type="button"
|
|
264
|
+
class="ui-button ui-button--ghost ui-button--compact chat-notification-action"
|
|
265
|
+
id="chatNotificationBtn"
|
|
266
|
+
>Enable desktop alerts</button>
|
|
267
|
+
<button
|
|
268
|
+
type="button"
|
|
269
|
+
class="ui-icon-button chat-notification-mute-action hidden"
|
|
270
|
+
id="chatNotificationMuteBtn"
|
|
271
|
+
aria-label="Mute desktop alerts for one hour"
|
|
272
|
+
aria-pressed="false"
|
|
273
|
+
title="Mute desktop alerts for one hour"
|
|
274
|
+
>
|
|
275
|
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
|
276
|
+
<path d="M18 8a6 6 0 0 0-12 0c0 7-3 7-3 9h18c0-2-3-2-3-9"></path>
|
|
277
|
+
<path d="M13.73 21a2 2 0 0 1-3.46 0"></path>
|
|
278
|
+
<path class="chat-notification-mute-slash" d="m4 4 16 16"></path>
|
|
279
|
+
</svg>
|
|
280
|
+
</button>
|
|
281
|
+
<button type="button" class="ui-icon-button chat-close-btn" id="chatCloseBtn" aria-label="Close team chat" title="Close team chat">
|
|
282
|
+
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true">
|
|
283
|
+
<line x1="18" y1="6" x2="6" y2="18"></line>
|
|
284
|
+
<line x1="6" y1="6" x2="18" y2="18"></line>
|
|
285
|
+
</svg>
|
|
286
|
+
</button>
|
|
214
287
|
</div>
|
|
215
|
-
<button
|
|
216
|
-
class="ui-button ui-button--secondary ui-button--compact ui-button--action ui-button--surface ui-button--pill chat-notification-action"
|
|
217
|
-
id="chatNotificationBtn"
|
|
218
|
-
type="button"
|
|
219
|
-
>
|
|
220
|
-
Enable alerts
|
|
221
|
-
</button>
|
|
222
288
|
</div>
|
|
223
289
|
|
|
224
|
-
<div class="chat-empty-state" id="chatEmptyState">
|
|
225
|
-
|
|
290
|
+
<div class="ui-empty-state ui-empty-state--compact chat-empty-state" id="chatEmptyState">
|
|
291
|
+
Messages are visible to everyone in this room.
|
|
226
292
|
</div>
|
|
227
293
|
<div class="chat-messages hidden" id="chatMessages" role="log" aria-live="polite" aria-relevant="additions text"></div>
|
|
228
294
|
|
|
@@ -231,10 +297,11 @@
|
|
|
231
297
|
type="text"
|
|
232
298
|
class="ui-input chat-input"
|
|
233
299
|
id="chatInput"
|
|
234
|
-
placeholder="
|
|
300
|
+
placeholder="Message the room..."
|
|
235
301
|
autocomplete="off"
|
|
236
302
|
spellcheck="true"
|
|
237
303
|
maxlength="280"
|
|
304
|
+
enterkeyhint="send"
|
|
238
305
|
aria-label="Team chat message"
|
|
239
306
|
>
|
|
240
307
|
<button type="submit" class="ui-button ui-button--primary chat-send-btn">Send</button>
|
|
@@ -290,6 +357,18 @@
|
|
|
290
357
|
<span class="ui-toolbar-button-label">Search files</span>
|
|
291
358
|
</button>
|
|
292
359
|
|
|
360
|
+
<button class="ui-button ui-button--ghost ui-button--toolbar ui-button--toggle toolbar-vim-button" id="toggleVimModeBtn" aria-label="Enable Vim keybindings" aria-pressed="false" title="Enable Vim keybindings">
|
|
361
|
+
<span class="toolbar-vim-icon" aria-hidden="true">
|
|
362
|
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">
|
|
363
|
+
<rect x="3" y="4" width="18" height="16" rx="2"></rect>
|
|
364
|
+
<path d="m7 9 3 3-3 3"></path>
|
|
365
|
+
<path d="M13 15h4"></path>
|
|
366
|
+
</svg>
|
|
367
|
+
</span>
|
|
368
|
+
<span class="ui-toolbar-button-label">Vim</span>
|
|
369
|
+
<span class="ui-pill-badge ui-pill-badge--muted toolbar-vim-state" id="vimModeToggleLabel">Off</span>
|
|
370
|
+
</button>
|
|
371
|
+
|
|
293
372
|
<details class="toolbar-overflow-group hidden" id="exportMenuGroup">
|
|
294
373
|
<summary class="ui-button ui-button--ghost ui-button--toolbar toolbar-overflow-group-toggle" aria-label="Export options" title="Export">
|
|
295
374
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
|
@@ -646,29 +725,32 @@
|
|
|
646
725
|
|
|
647
726
|
<div class="toast-container" id="toastContainer"></div>
|
|
648
727
|
|
|
649
|
-
<div class="qs-overlay" id="quickSwitcher">
|
|
728
|
+
<div class="qs-overlay" id="quickSwitcher" role="dialog" aria-modal="true" aria-labelledby="quickSwitcherTitle" aria-hidden="true">
|
|
650
729
|
<div class="qs-modal">
|
|
730
|
+
<h2 class="sr-only" id="quickSwitcherTitle">Search files and text</h2>
|
|
651
731
|
<div class="qs-mode-tabs" role="tablist" aria-label="Search mode">
|
|
652
|
-
<button class="qs-mode-tab active" type="button" role="tab" aria-selected="true" data-qs-mode="files">Files</button>
|
|
653
|
-
<button class="qs-mode-tab" type="button" role="tab" aria-selected="false" data-qs-mode="text">Text</button>
|
|
732
|
+
<button class="qs-mode-tab active" id="quickSwitcherFilesTab" type="button" role="tab" aria-controls="quickSwitcherResults" aria-selected="true" data-qs-mode="files">Files</button>
|
|
733
|
+
<button class="qs-mode-tab" id="quickSwitcherTextTab" type="button" role="tab" aria-controls="quickSwitcherResults" aria-selected="false" data-qs-mode="text">Text</button>
|
|
654
734
|
</div>
|
|
655
735
|
<div class="qs-input-wrap">
|
|
656
736
|
<svg class="qs-search-icon" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round">
|
|
657
737
|
<circle cx="11" cy="11" r="8"></circle>
|
|
658
738
|
<line x1="21" y1="21" x2="16.65" y2="16.65"></line>
|
|
659
739
|
</svg>
|
|
660
|
-
<input type="text" class="qs-input" id="quickSwitcherInput" placeholder="Search files..." autocomplete="off" spellcheck="false">
|
|
740
|
+
<input type="text" class="qs-input" id="quickSwitcherInput" placeholder="Search files..." aria-label="Search files" aria-controls="quickSwitcherResults" aria-describedby="quickSwitcherHint quickSwitcherScope" autocomplete="off" spellcheck="false">
|
|
661
741
|
<kbd class="qs-kbd">esc</kbd>
|
|
662
742
|
</div>
|
|
663
|
-
<div class="qs-
|
|
664
|
-
<div class="qs-
|
|
665
|
-
<div class="qs-
|
|
743
|
+
<div class="qs-scope hidden" id="quickSwitcherScope" role="note" aria-hidden="true">Exact, case-insensitive text in Markdown, Base, Mermaid, PlantUML, Draw.io, and Excalidraw files. Images are excluded.</div>
|
|
744
|
+
<div class="qs-results" id="quickSwitcherResults" role="listbox" aria-label="Search results" aria-busy="false" tabindex="-1"></div>
|
|
745
|
+
<div class="qs-hint" id="quickSwitcherHint" role="status" aria-live="polite" aria-atomic="true">Type to search files</div>
|
|
746
|
+
<div class="qs-footer">
|
|
666
747
|
<span><kbd>↑</kbd><kbd>↓</kbd> Navigate</span>
|
|
667
748
|
<span><kbd>↵</kbd> Open</span>
|
|
668
749
|
<span><kbd>Tab</kbd> Switch mode</span>
|
|
669
750
|
</div>
|
|
670
751
|
</div>
|
|
671
752
|
</div>
|
|
753
|
+
<script vite-ignore src="./app-config.js"></script>
|
|
672
754
|
<script type="module" src="./main-entry.js"></script>
|
|
673
755
|
</body>
|
|
674
756
|
</html>
|
|
@@ -1,12 +1,8 @@
|
|
|
1
1
|
import darkHighlightThemeUrl from '../assets/vendor/highlight/github-dark.min.css?url';
|
|
2
2
|
import lightHighlightThemeUrl from '../assets/vendor/highlight/github.min.css?url';
|
|
3
|
-
import '@fontsource-variable/inter';
|
|
4
|
-
import '@fontsource/jetbrains-mono';
|
|
5
3
|
import '../styles/base.css';
|
|
6
4
|
import '../styles/style.css';
|
|
7
5
|
|
|
8
|
-
import { ensureRuntimeConfigLoaded } from '../infrastructure/runtime-config-loader.js';
|
|
9
|
-
|
|
10
6
|
function ensureHighlightThemeStylesheet() {
|
|
11
7
|
let themeStylesheet = document.getElementById('hljs-theme');
|
|
12
8
|
if (!(themeStylesheet instanceof HTMLLinkElement)) {
|
|
@@ -21,6 +17,5 @@ function ensureHighlightThemeStylesheet() {
|
|
|
21
17
|
themeStylesheet.dataset.lightHref = lightHighlightThemeUrl;
|
|
22
18
|
}
|
|
23
19
|
|
|
24
|
-
await ensureRuntimeConfigLoaded();
|
|
25
20
|
ensureHighlightThemeStylesheet();
|
|
26
21
|
await import('../main.js');
|
|
@@ -1,3 +1,41 @@
|
|
|
1
|
+
import { getUserAvatarTextColor } from '../../domain/room.js';
|
|
2
|
+
|
|
3
|
+
const CHAT_ALERT_MUTE_STORAGE_KEY = 'collabmd-chat-alerts-muted-until';
|
|
4
|
+
const CHAT_ALERT_MUTE_DURATION_MS = 60 * 60 * 1000;
|
|
5
|
+
|
|
6
|
+
function getChatConnectionState(context) {
|
|
7
|
+
return context.lobby?.getConnectionState?.() ?? { status: 'connected', unreachable: false };
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function isChatAtBottom(list) {
|
|
11
|
+
return list.scrollHeight - list.scrollTop - list.clientHeight <= 24;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function getChatAlertMuteUntil() {
|
|
15
|
+
try {
|
|
16
|
+
const mutedUntil = Number(globalThis.localStorage?.getItem(CHAT_ALERT_MUTE_STORAGE_KEY));
|
|
17
|
+
if (!Number.isFinite(mutedUntil) || mutedUntil <= Date.now()) {
|
|
18
|
+
globalThis.localStorage?.removeItem(CHAT_ALERT_MUTE_STORAGE_KEY);
|
|
19
|
+
return 0;
|
|
20
|
+
}
|
|
21
|
+
return mutedUntil;
|
|
22
|
+
} catch {
|
|
23
|
+
return 0;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function setChatAlertMuteUntil(mutedUntil) {
|
|
28
|
+
try {
|
|
29
|
+
if (mutedUntil > 0) {
|
|
30
|
+
globalThis.localStorage?.setItem(CHAT_ALERT_MUTE_STORAGE_KEY, String(mutedUntil));
|
|
31
|
+
} else {
|
|
32
|
+
globalThis.localStorage?.removeItem(CHAT_ALERT_MUTE_STORAGE_KEY);
|
|
33
|
+
}
|
|
34
|
+
} catch {
|
|
35
|
+
// Local storage can be unavailable in private or restricted browsing modes.
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
1
39
|
export const chatFeature = {
|
|
2
40
|
updateChatMessages(messages, { initial = false } = {}) {
|
|
3
41
|
const previousIds = new Set(this.chatMessageIds);
|
|
@@ -36,7 +74,7 @@ export const chatFeature = {
|
|
|
36
74
|
|
|
37
75
|
toggleChatPanel() {
|
|
38
76
|
if (this.chatIsOpen) {
|
|
39
|
-
this.closeChatPanel();
|
|
77
|
+
this.closeChatPanel({ restoreFocus: true });
|
|
40
78
|
return;
|
|
41
79
|
}
|
|
42
80
|
|
|
@@ -46,24 +84,27 @@ export const chatFeature = {
|
|
|
46
84
|
openChatPanel() {
|
|
47
85
|
this.chatIsOpen = true;
|
|
48
86
|
this.chatUnreadCount = 0;
|
|
49
|
-
this.renderChat();
|
|
87
|
+
this.renderChat({ stickToBottom: true });
|
|
50
88
|
requestAnimationFrame(() => {
|
|
51
89
|
this.elements.chatInput?.focus();
|
|
52
|
-
this.scrollChatToBottom();
|
|
53
90
|
});
|
|
54
91
|
},
|
|
55
92
|
|
|
56
|
-
closeChatPanel() {
|
|
93
|
+
closeChatPanel({ restoreFocus = false } = {}) {
|
|
57
94
|
if (!this.chatIsOpen) {
|
|
58
95
|
return;
|
|
59
96
|
}
|
|
60
97
|
|
|
61
98
|
this.chatIsOpen = false;
|
|
62
|
-
this.renderChat();
|
|
99
|
+
this.renderChat({ messagesChanged: false });
|
|
100
|
+
if (restoreFocus) {
|
|
101
|
+
requestAnimationFrame(() => this.elements.chatToggleButton?.focus());
|
|
102
|
+
}
|
|
63
103
|
},
|
|
64
104
|
|
|
65
105
|
handleChatSubmit() {
|
|
66
|
-
|
|
106
|
+
const connectionState = getChatConnectionState(this);
|
|
107
|
+
if (this.isTabActive === false || connectionState.status !== 'connected') {
|
|
67
108
|
return;
|
|
68
109
|
}
|
|
69
110
|
|
|
@@ -84,32 +125,41 @@ export const chatFeature = {
|
|
|
84
125
|
return;
|
|
85
126
|
}
|
|
86
127
|
|
|
87
|
-
this.renderChat();
|
|
128
|
+
this.renderChat({ stickToBottom: true });
|
|
88
129
|
},
|
|
89
130
|
|
|
90
|
-
renderChat() {
|
|
131
|
+
renderChat({ messagesChanged = true, stickToBottom = false } = {}) {
|
|
91
132
|
this.elements.chatContainer?.classList.toggle('is-open', this.chatIsOpen);
|
|
92
133
|
this.elements.chatPanel?.classList.toggle('hidden', !this.chatIsOpen);
|
|
93
134
|
|
|
94
135
|
this.syncChatToggleButton();
|
|
95
136
|
this.syncChatNotificationButton();
|
|
96
|
-
|
|
97
137
|
const list = this.elements.chatMessages;
|
|
98
138
|
const emptyState = this.elements.chatEmptyState;
|
|
139
|
+
const connectionState = getChatConnectionState(this);
|
|
140
|
+
const isConnected = connectionState.status === 'connected';
|
|
141
|
+
const canSend = isConnected && this.chatInitialSyncComplete && this.isTabActive !== false;
|
|
99
142
|
|
|
100
143
|
if (this.elements.chatStatus) {
|
|
101
|
-
this.elements.chatStatus.textContent = this.chatInitialSyncComplete
|
|
102
|
-
?
|
|
103
|
-
:
|
|
144
|
+
this.elements.chatStatus.textContent = !this.chatInitialSyncComplete
|
|
145
|
+
? 'Syncing...'
|
|
146
|
+
: !isConnected
|
|
147
|
+
? connectionState.unreachable ? 'Server unreachable' : 'Reconnecting...'
|
|
148
|
+
: `${this.globalUsers.length} online`;
|
|
104
149
|
}
|
|
105
150
|
|
|
106
|
-
|
|
107
|
-
|
|
151
|
+
const sendButton = this.elements.chatForm?.querySelector?.('button[type="submit"]');
|
|
152
|
+
if (sendButton) {
|
|
153
|
+
sendButton.disabled = !canSend;
|
|
154
|
+
sendButton.title = canSend ? 'Send message' : 'Chat is unavailable while disconnected';
|
|
108
155
|
}
|
|
109
156
|
|
|
110
|
-
list.
|
|
157
|
+
if (!list || !this.chatIsOpen || !messagesChanged) {
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
111
160
|
|
|
112
161
|
if (this.chatMessages.length === 0) {
|
|
162
|
+
list.replaceChildren();
|
|
113
163
|
emptyState?.classList.remove('hidden');
|
|
114
164
|
list.classList.add('hidden');
|
|
115
165
|
return;
|
|
@@ -118,13 +168,14 @@ export const chatFeature = {
|
|
|
118
168
|
emptyState?.classList.add('hidden');
|
|
119
169
|
list.classList.remove('hidden');
|
|
120
170
|
|
|
171
|
+
const wasAtBottom = stickToBottom || isChatAtBottom(list);
|
|
121
172
|
const fragment = document.createDocumentFragment();
|
|
122
173
|
this.chatMessages.forEach((message) => {
|
|
123
174
|
fragment.appendChild(this.createChatMessageElement(message));
|
|
124
175
|
});
|
|
125
|
-
list.
|
|
176
|
+
list.replaceChildren(fragment);
|
|
126
177
|
|
|
127
|
-
if (
|
|
178
|
+
if (wasAtBottom) {
|
|
128
179
|
this.scrollChatToBottom();
|
|
129
180
|
}
|
|
130
181
|
},
|
|
@@ -137,7 +188,9 @@ export const chatFeature = {
|
|
|
137
188
|
|
|
138
189
|
const avatar = document.createElement('div');
|
|
139
190
|
avatar.className = 'chat-message-avatar';
|
|
140
|
-
|
|
191
|
+
const avatarColor = message.userColor || 'var(--color-primary-active)';
|
|
192
|
+
avatar.style.backgroundColor = avatarColor;
|
|
193
|
+
avatar.style.color = getUserAvatarTextColor(message.userColor);
|
|
141
194
|
avatar.textContent = (message.userName || '?').charAt(0).toUpperCase();
|
|
142
195
|
avatar.setAttribute('aria-hidden', 'true');
|
|
143
196
|
|
|
@@ -160,7 +213,7 @@ export const chatFeature = {
|
|
|
160
213
|
const fileLabel = this.getChatMessageFileLabel(message.filePath);
|
|
161
214
|
if (fileLabel) {
|
|
162
215
|
const file = document.createElement('span');
|
|
163
|
-
file.className = 'chat-message-file';
|
|
216
|
+
file.className = 'ui-pill chat-message-file';
|
|
164
217
|
file.textContent = fileLabel;
|
|
165
218
|
meta.append(file);
|
|
166
219
|
}
|
|
@@ -212,6 +265,92 @@ export const chatFeature = {
|
|
|
212
265
|
return `${sender}: ${compactText}`;
|
|
213
266
|
},
|
|
214
267
|
|
|
268
|
+
syncChatNotificationButton() {
|
|
269
|
+
const button = this.elements.chatNotificationButton;
|
|
270
|
+
const muteButton = this.elements.chatNotificationMuteButton;
|
|
271
|
+
const status = this.elements.chatNotificationStatus;
|
|
272
|
+
if (!button && !muteButton && !status) {
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
const permission = this.notifications?.getPermission?.() ?? 'unsupported';
|
|
277
|
+
const enabled = permission === 'granted';
|
|
278
|
+
const muted = enabled && getChatAlertMuteUntil() > 0;
|
|
279
|
+
const blocked = permission === 'denied';
|
|
280
|
+
const statusState = enabled
|
|
281
|
+
? {
|
|
282
|
+
text: muted ? 'Alerts muted' : 'Alerts on',
|
|
283
|
+
label: muted ? 'Desktop alerts muted' : 'Desktop alerts enabled',
|
|
284
|
+
}
|
|
285
|
+
: blocked
|
|
286
|
+
? {
|
|
287
|
+
text: 'Alerts blocked',
|
|
288
|
+
label: 'Desktop alerts blocked in browser settings',
|
|
289
|
+
}
|
|
290
|
+
: null;
|
|
291
|
+
|
|
292
|
+
if (button) {
|
|
293
|
+
button.classList.toggle('hidden', permission !== 'default');
|
|
294
|
+
button.disabled = false;
|
|
295
|
+
button.removeAttribute('aria-pressed');
|
|
296
|
+
button.textContent = 'Enable desktop alerts';
|
|
297
|
+
button.title = 'Allow desktop alerts for new chat messages';
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
if (muteButton) {
|
|
301
|
+
muteButton.classList.toggle('hidden', !enabled);
|
|
302
|
+
muteButton.disabled = false;
|
|
303
|
+
muteButton.setAttribute('aria-pressed', String(muted));
|
|
304
|
+
const muteLabel = muted ? 'Unmute desktop alerts' : 'Mute desktop alerts for one hour';
|
|
305
|
+
muteButton.setAttribute('aria-label', muteLabel);
|
|
306
|
+
muteButton.title = muteLabel;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
if (status) {
|
|
310
|
+
status.classList.toggle('hidden', !statusState);
|
|
311
|
+
status.textContent = statusState?.text ?? '';
|
|
312
|
+
if (statusState) {
|
|
313
|
+
status.setAttribute('aria-label', statusState.label);
|
|
314
|
+
status.title = statusState.label;
|
|
315
|
+
} else {
|
|
316
|
+
status.removeAttribute('aria-label');
|
|
317
|
+
status.removeAttribute('title');
|
|
318
|
+
}
|
|
319
|
+
status.classList.toggle('ui-status-badge--accent', enabled && !muted);
|
|
320
|
+
status.classList.toggle('ui-status-badge--muted', blocked || muted);
|
|
321
|
+
}
|
|
322
|
+
},
|
|
323
|
+
|
|
324
|
+
async handleChatNotificationToggle() {
|
|
325
|
+
const permission = await this.notifications?.requestPermission?.();
|
|
326
|
+
this.syncChatNotificationButton();
|
|
327
|
+
|
|
328
|
+
if (permission === 'granted') {
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
if (permission === 'denied') {
|
|
333
|
+
(this.chatToastController ?? this.toastController)?.show(
|
|
334
|
+
'Desktop alerts are blocked. Allow them in browser site settings.',
|
|
335
|
+
{ duration: 5000, tone: 'warning' },
|
|
336
|
+
);
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
if (permission === 'unsupported') {
|
|
341
|
+
(this.chatToastController ?? this.toastController)?.show(
|
|
342
|
+
'This browser does not support desktop alerts.',
|
|
343
|
+
{ duration: 5000, tone: 'warning' },
|
|
344
|
+
);
|
|
345
|
+
}
|
|
346
|
+
},
|
|
347
|
+
|
|
348
|
+
toggleChatNotificationMute() {
|
|
349
|
+
const mutedUntil = getChatAlertMuteUntil();
|
|
350
|
+
setChatAlertMuteUntil(mutedUntil > 0 ? 0 : Date.now() + CHAT_ALERT_MUTE_DURATION_MS);
|
|
351
|
+
this.renderChat({ messagesChanged: false });
|
|
352
|
+
},
|
|
353
|
+
|
|
215
354
|
syncChatToggleButton() {
|
|
216
355
|
const button = this.elements.chatToggleButton;
|
|
217
356
|
const badge = this.elements.chatToggleBadge;
|
|
@@ -224,6 +363,7 @@ export const chatFeature = {
|
|
|
224
363
|
|
|
225
364
|
button.classList.toggle('is-active', this.chatIsOpen);
|
|
226
365
|
button.classList.toggle('is-unread', shouldEmphasizeUnread);
|
|
366
|
+
button.setAttribute('aria-controls', 'chatPanel');
|
|
227
367
|
button.setAttribute('aria-expanded', String(this.chatIsOpen));
|
|
228
368
|
button.setAttribute(
|
|
229
369
|
'aria-label',
|
|
@@ -241,123 +381,39 @@ export const chatFeature = {
|
|
|
241
381
|
badge.textContent = this.chatUnreadCount > 9 ? '9+' : String(this.chatUnreadCount);
|
|
242
382
|
},
|
|
243
383
|
|
|
244
|
-
syncChatNotificationButton() {
|
|
245
|
-
const button = this.elements.chatNotificationButton;
|
|
246
|
-
if (!button) {
|
|
247
|
-
return;
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
const permission = this.notifications.getPermission();
|
|
251
|
-
this.chatNotificationPermission = permission;
|
|
252
|
-
|
|
253
|
-
let label = 'Enable alerts';
|
|
254
|
-
let title = 'Enable browser notifications for new chat messages';
|
|
255
|
-
let pressed = false;
|
|
256
|
-
|
|
257
|
-
if (permission === 'unsupported') {
|
|
258
|
-
label = 'Alerts unavailable';
|
|
259
|
-
title = 'Browser notifications are unavailable here';
|
|
260
|
-
} else if (permission === 'denied') {
|
|
261
|
-
label = 'Alerts off';
|
|
262
|
-
title = 'Browser notifications are blocked for this site';
|
|
263
|
-
} else if (permission === 'granted') {
|
|
264
|
-
pressed = this.chatNotificationsEnabled;
|
|
265
|
-
label = pressed ? 'Alerts on' : 'Alerts off';
|
|
266
|
-
title = pressed
|
|
267
|
-
? 'Disable browser notifications for chat'
|
|
268
|
-
: 'Enable browser notifications for chat';
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
button.textContent = label;
|
|
272
|
-
button.title = title;
|
|
273
|
-
button.setAttribute('aria-pressed', String(pressed));
|
|
274
|
-
button.classList.toggle('is-enabled', pressed);
|
|
275
|
-
button.classList.toggle('is-blocked', permission === 'denied');
|
|
276
|
-
},
|
|
277
|
-
|
|
278
|
-
async handleChatNotificationToggle() {
|
|
279
|
-
const permission = this.notifications.getPermission();
|
|
280
|
-
this.chatNotificationPermission = permission;
|
|
281
|
-
|
|
282
|
-
if (permission === 'unsupported') {
|
|
283
|
-
this.toastController.show('Browser notifications are unavailable here');
|
|
284
|
-
this.syncChatNotificationButton();
|
|
285
|
-
return;
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
if (permission === 'denied') {
|
|
289
|
-
this.chatNotificationsEnabled = false;
|
|
290
|
-
this.preferences.setChatNotificationsEnabled(false);
|
|
291
|
-
this.toastController.show('Browser notifications are blocked for this site');
|
|
292
|
-
this.syncChatNotificationButton();
|
|
293
|
-
return;
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
if (permission === 'default') {
|
|
297
|
-
const nextPermission = await this.notifications.requestPermission();
|
|
298
|
-
this.chatNotificationPermission = nextPermission;
|
|
299
|
-
|
|
300
|
-
if (nextPermission !== 'granted') {
|
|
301
|
-
this.chatNotificationsEnabled = false;
|
|
302
|
-
this.preferences.setChatNotificationsEnabled(false);
|
|
303
|
-
if (nextPermission === 'denied') {
|
|
304
|
-
this.toastController.show('Browser notifications were blocked');
|
|
305
|
-
}
|
|
306
|
-
this.syncChatNotificationButton();
|
|
307
|
-
return;
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
this.chatNotificationsEnabled = true;
|
|
311
|
-
this.preferences.setChatNotificationsEnabled(true);
|
|
312
|
-
this.syncChatNotificationButton();
|
|
313
|
-
return;
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
this.chatNotificationsEnabled = !this.chatNotificationsEnabled;
|
|
317
|
-
this.preferences.setChatNotificationsEnabled(this.chatNotificationsEnabled);
|
|
318
|
-
this.syncChatNotificationButton();
|
|
319
|
-
},
|
|
320
|
-
|
|
321
384
|
maybeNotifyChatMessage(message) {
|
|
322
385
|
if (!this.chatInitialSyncComplete) {
|
|
323
386
|
return;
|
|
324
387
|
}
|
|
325
388
|
|
|
326
|
-
if (
|
|
327
|
-
this.maybeShowBrowserChatNotification(message);
|
|
389
|
+
if (this.isTabActive === false || this.chatIsOpen || getChatAlertMuteUntil() > 0) {
|
|
328
390
|
return;
|
|
329
391
|
}
|
|
330
392
|
|
|
331
|
-
|
|
332
|
-
|
|
393
|
+
const pageHidden = Boolean(
|
|
394
|
+
globalThis.document?.hidden || globalThis.document?.visibilityState === 'hidden',
|
|
395
|
+
);
|
|
396
|
+
const pageFocused = typeof globalThis.document?.hasFocus === 'function'
|
|
397
|
+
? globalThis.document.hasFocus()
|
|
398
|
+
: !pageHidden;
|
|
399
|
+
let notification = null;
|
|
400
|
+
if (pageHidden || !pageFocused) {
|
|
401
|
+
notification = this.notifications?.show?.({
|
|
402
|
+
body: String(message?.text ?? '').replace(/\s+/g, ' ').trim(),
|
|
403
|
+
onClick: () => {
|
|
404
|
+
window.focus?.();
|
|
405
|
+
notification?.close?.();
|
|
406
|
+
this.openChatPanel();
|
|
407
|
+
},
|
|
408
|
+
tag: `collabmd-chat-${message?.id ?? 'message'}`,
|
|
409
|
+
title: `New message from ${message?.userName || 'Someone'}`,
|
|
410
|
+
});
|
|
333
411
|
}
|
|
334
412
|
|
|
335
|
-
|
|
336
|
-
},
|
|
337
|
-
|
|
338
|
-
maybeShowBrowserChatNotification(message) {
|
|
339
|
-
if (!this.chatNotificationsEnabled || this.notifications.getPermission() !== 'granted') {
|
|
413
|
+
if (notification) {
|
|
340
414
|
return;
|
|
341
415
|
}
|
|
342
416
|
|
|
343
|
-
|
|
344
|
-
const fileLabel = this.getChatMessageFileLabel(message.filePath);
|
|
345
|
-
const body = fileLabel ? `${fileLabel}: ${message.text}` : message.text;
|
|
346
|
-
const notification = this.notifications.createNotification({
|
|
347
|
-
body,
|
|
348
|
-
onClick: () => {
|
|
349
|
-
if (message.filePath) {
|
|
350
|
-
this.navigation.navigateToFile(message.filePath);
|
|
351
|
-
}
|
|
352
|
-
},
|
|
353
|
-
tag: `collabmd-chat-${message.id}`,
|
|
354
|
-
title,
|
|
355
|
-
});
|
|
356
|
-
|
|
357
|
-
if (notification) {
|
|
358
|
-
setTimeout(() => {
|
|
359
|
-
notification.close?.();
|
|
360
|
-
}, 6000);
|
|
361
|
-
}
|
|
417
|
+
(this.chatToastController ?? this.toastController).show(this.formatChatToastMessage(message), 4000);
|
|
362
418
|
},
|
|
363
419
|
};
|