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
|
@@ -24,6 +24,10 @@ import {
|
|
|
24
24
|
tryParseSceneJson,
|
|
25
25
|
} from '../domain/excalidraw-scene.js';
|
|
26
26
|
import { resolveWsBaseUrl } from '../domain/runtime-paths.js';
|
|
27
|
+
import {
|
|
28
|
+
normalizeCommentQuote,
|
|
29
|
+
} from '../../domain/comment-threads.js';
|
|
30
|
+
import { CommentThreadStore } from './comment-thread-store.js';
|
|
27
31
|
import { stopReconnectOnControlledClose } from './yjs-provider-reset-guard.js';
|
|
28
32
|
|
|
29
33
|
const DEFAULT_EMPTY_SCENE_GUARD_MS = 250;
|
|
@@ -39,16 +43,21 @@ export const EXCALIDRAW_ROOM_CONNECTION_STATE = Object.freeze({
|
|
|
39
43
|
RECONNECTING_READONLY: 'reconnecting-readonly',
|
|
40
44
|
});
|
|
41
45
|
|
|
42
|
-
function
|
|
43
|
-
if (
|
|
44
|
-
return
|
|
46
|
+
function areExcalidrawFilesEqual(left, right) {
|
|
47
|
+
if (left === right) {
|
|
48
|
+
return true;
|
|
45
49
|
}
|
|
46
50
|
|
|
47
|
-
if (
|
|
48
|
-
return
|
|
51
|
+
if (!left || !right || typeof left !== 'object' || typeof right !== 'object') {
|
|
52
|
+
return false;
|
|
49
53
|
}
|
|
50
54
|
|
|
51
|
-
return
|
|
55
|
+
return left.id === right.id
|
|
56
|
+
&& left.mimeType === right.mimeType
|
|
57
|
+
&& left.dataURL === right.dataURL
|
|
58
|
+
&& left.created === right.created
|
|
59
|
+
&& left.lastRetrieved === right.lastRetrieved
|
|
60
|
+
&& left.version === right.version;
|
|
52
61
|
}
|
|
53
62
|
|
|
54
63
|
function normalizeElementVersion(value) {
|
|
@@ -81,6 +90,35 @@ function shouldSyncElementToRoom(localElement, roomElement) {
|
|
|
81
90
|
return localVersionNonce < roomVersionNonce;
|
|
82
91
|
}
|
|
83
92
|
|
|
93
|
+
function createDiagramCommentAnchor(element) {
|
|
94
|
+
const elementId = typeof element?.id === 'string' ? element.id.trim() : '';
|
|
95
|
+
const x = Number(element?.x);
|
|
96
|
+
const y = Number(element?.y);
|
|
97
|
+
const width = Number(element?.width);
|
|
98
|
+
const height = Number(element?.height);
|
|
99
|
+
if (!elementId || ![x, y, width, height].every(Number.isFinite)) {
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return {
|
|
104
|
+
anchorKind: 'diagram-element',
|
|
105
|
+
anchorPoint: {
|
|
106
|
+
x: x + (width / 2),
|
|
107
|
+
y: y + (height / 2),
|
|
108
|
+
},
|
|
109
|
+
anchorQuote: normalizeCommentQuote(element.text),
|
|
110
|
+
anchorSnapshot: {
|
|
111
|
+
height,
|
|
112
|
+
text: normalizeCommentQuote(element.text),
|
|
113
|
+
type: typeof element.type === 'string' ? element.type : 'element',
|
|
114
|
+
width,
|
|
115
|
+
x,
|
|
116
|
+
y,
|
|
117
|
+
},
|
|
118
|
+
elementId,
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
|
|
84
122
|
export class ExcalidrawRoomClient {
|
|
85
123
|
constructor({
|
|
86
124
|
cancelAnimationFrameFn = (frameId) => cancelAnimationFrame(frameId),
|
|
@@ -89,6 +127,7 @@ export class ExcalidrawRoomClient {
|
|
|
89
127
|
filePath = '',
|
|
90
128
|
now = () => Date.now(),
|
|
91
129
|
onCollaboratorsChange = () => {},
|
|
130
|
+
onCommentThreadsChange = () => {},
|
|
92
131
|
onConnectionStateChange = () => {},
|
|
93
132
|
onRemoteSceneJson = () => {},
|
|
94
133
|
requestAnimationFrameFn = (callback) => requestAnimationFrame(callback),
|
|
@@ -106,6 +145,7 @@ export class ExcalidrawRoomClient {
|
|
|
106
145
|
this.filePath = filePath;
|
|
107
146
|
this.now = now;
|
|
108
147
|
this.onCollaboratorsChange = onCollaboratorsChange;
|
|
148
|
+
this.onCommentThreadsChange = onCommentThreadsChange;
|
|
109
149
|
this.onConnectionStateChange = onConnectionStateChange;
|
|
110
150
|
this.onRemoteSceneJson = onRemoteSceneJson;
|
|
111
151
|
this.requestAnimationFrameFn = requestAnimationFrameFn;
|
|
@@ -138,12 +178,22 @@ export class ExcalidrawRoomClient {
|
|
|
138
178
|
this.lastViewportSignature = '';
|
|
139
179
|
this.lastSelectedIdsSignature = '';
|
|
140
180
|
this.pendingEmptySceneCandidate = null;
|
|
181
|
+
this.structuredObserversAttached = false;
|
|
141
182
|
this.canWriteToRoom = false;
|
|
142
183
|
this.waitingForAuthoritativeSync = false;
|
|
143
184
|
this.connectionState = EXCALIDRAW_ROOM_CONNECTION_STATE.CLOSED;
|
|
144
185
|
this.hasAuthoritativeSync = false;
|
|
145
186
|
this.applyingSharedSnapshotDepth = 0;
|
|
146
187
|
this.suppressStructuredSceneUpdateDepth = 0;
|
|
188
|
+
this.commentThreadStore = new CommentThreadStore({
|
|
189
|
+
canWrite: () => this.canWriteToRoom,
|
|
190
|
+
createAnchor: createDiagramCommentAnchor,
|
|
191
|
+
getDoc: () => this.ydoc,
|
|
192
|
+
getEditorState: () => null,
|
|
193
|
+
getLocalUser: () => this.getLocalUser(),
|
|
194
|
+
onCommentsChange: (threads) => this.onCommentThreadsChange(threads),
|
|
195
|
+
resolveThread: (thread) => thread,
|
|
196
|
+
});
|
|
147
197
|
|
|
148
198
|
this.handleAwarenessChange = () => {
|
|
149
199
|
this.onCollaboratorsChange(buildCollaboratorsMap(this.awareness));
|
|
@@ -177,6 +227,25 @@ export class ExcalidrawRoomClient {
|
|
|
177
227
|
return this.localUser;
|
|
178
228
|
}
|
|
179
229
|
|
|
230
|
+
getCommentThreads() {
|
|
231
|
+
return this.commentThreadStore.getCommentThreads();
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
createCommentThread({ body, element } = {}) {
|
|
235
|
+
return this.commentThreadStore.createCommentThread({
|
|
236
|
+
anchor: element,
|
|
237
|
+
body,
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
replyToCommentThread(threadId, body) {
|
|
242
|
+
return this.commentThreadStore.replyToCommentThread(threadId, body);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
deleteCommentThread(threadId) {
|
|
246
|
+
return this.commentThreadStore.deleteCommentThread(threadId);
|
|
247
|
+
}
|
|
248
|
+
|
|
180
249
|
getConnectionState() {
|
|
181
250
|
return this.connectionState;
|
|
182
251
|
}
|
|
@@ -268,7 +337,16 @@ export class ExcalidrawRoomClient {
|
|
|
268
337
|
return this.localUser;
|
|
269
338
|
}
|
|
270
339
|
|
|
271
|
-
async connect(
|
|
340
|
+
async connect(options = {}) {
|
|
341
|
+
try {
|
|
342
|
+
return await this.connectInternal(options);
|
|
343
|
+
} catch (error) {
|
|
344
|
+
this.disconnect();
|
|
345
|
+
throw error;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
async connectInternal({ initialUser = null } = {}) {
|
|
272
350
|
this.localUser = initialUser;
|
|
273
351
|
this.setConnectionState(EXCALIDRAW_ROOM_CONNECTION_STATE.CONNECTING);
|
|
274
352
|
|
|
@@ -277,6 +355,7 @@ export class ExcalidrawRoomClient {
|
|
|
277
355
|
this.lastSceneJson = JSON.stringify(scene);
|
|
278
356
|
this.hasAuthoritativeSync = true;
|
|
279
357
|
this.setConnectionState(EXCALIDRAW_ROOM_CONNECTION_STATE.AUTHORITATIVE);
|
|
358
|
+
this.onCommentThreadsChange([]);
|
|
280
359
|
return scene;
|
|
281
360
|
}
|
|
282
361
|
|
|
@@ -367,6 +446,11 @@ export class ExcalidrawRoomClient {
|
|
|
367
446
|
this.roomElements.observeDeep(this.handleStructuredSceneUpdate);
|
|
368
447
|
this.roomFiles.observeDeep(this.handleStructuredSceneUpdate);
|
|
369
448
|
this.roomAppState.observe(this.handleStructuredSceneUpdate);
|
|
449
|
+
this.structuredObserversAttached = true;
|
|
450
|
+
this.commentThreadStore.bind({
|
|
451
|
+
commentThreads: this.ydoc.getArray('comments'),
|
|
452
|
+
ydoc: this.ydoc,
|
|
453
|
+
});
|
|
370
454
|
if (usedApiFallback && this.canWriteToRoom) {
|
|
371
455
|
this.commitSceneJson(this.lastSceneJson, {
|
|
372
456
|
origin: 'excalidraw-api-fallback',
|
|
@@ -501,7 +585,7 @@ export class ExcalidrawRoomClient {
|
|
|
501
585
|
|
|
502
586
|
mergeLocallyChangedAppState(appState, baseSceneJson) {
|
|
503
587
|
const baseScene = parseSceneJson(baseSceneJson);
|
|
504
|
-
const roomScene = parseSceneJson(this.
|
|
588
|
+
const roomScene = parseSceneJson(this.lastSceneJson || this.getStructuredSceneJson());
|
|
505
589
|
const nextAppState = {
|
|
506
590
|
...roomScene.appState,
|
|
507
591
|
};
|
|
@@ -523,7 +607,7 @@ export class ExcalidrawRoomClient {
|
|
|
523
607
|
elements,
|
|
524
608
|
files,
|
|
525
609
|
} = {}) {
|
|
526
|
-
const roomSceneJson = this.
|
|
610
|
+
const roomSceneJson = this.lastSceneJson || this.getStructuredSceneJson() || baseSceneJson;
|
|
527
611
|
const roomScene = parseSceneJson(roomSceneJson);
|
|
528
612
|
const nextAppState = this.mergeLocallyChangedAppState(appState, baseSceneJson);
|
|
529
613
|
const roomElementsById = new Map(roomScene.elements.map((element) => [element.id, element]));
|
|
@@ -533,7 +617,7 @@ export class ExcalidrawRoomClient {
|
|
|
533
617
|
const nextFiles = files && typeof files === 'object' ? files : {};
|
|
534
618
|
const changedFiles = Object.fromEntries(
|
|
535
619
|
Object.entries(nextFiles).filter(([fileId, file]) => (
|
|
536
|
-
|
|
620
|
+
!areExcalidrawFilesEqual(file, roomScene.files?.[fileId])
|
|
537
621
|
)),
|
|
538
622
|
);
|
|
539
623
|
const appStateChanged = (
|
|
@@ -869,10 +953,15 @@ export class ExcalidrawRoomClient {
|
|
|
869
953
|
this.lastSelectedIdsSignature = '';
|
|
870
954
|
this.pendingEmptySceneCandidate = null;
|
|
871
955
|
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
956
|
+
if (this.structuredObserversAttached) {
|
|
957
|
+
this.roomMeta?.unobserve(this.handleStructuredSceneUpdate);
|
|
958
|
+
this.roomElements?.unobserveDeep(this.handleStructuredSceneUpdate);
|
|
959
|
+
this.roomFiles?.unobserveDeep(this.handleStructuredSceneUpdate);
|
|
960
|
+
this.roomAppState?.unobserve(this.handleStructuredSceneUpdate);
|
|
961
|
+
this.structuredObserversAttached = false;
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
this.commentThreadStore.unbind();
|
|
876
965
|
|
|
877
966
|
if (this.awareness) {
|
|
878
967
|
this.awareness.off('change', this.handleAwarenessChange);
|
|
@@ -74,6 +74,11 @@ export class GitApiClient {
|
|
|
74
74
|
return parseApiResponse(response, 'Failed to load historical file preview');
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
+
getFileAttachmentUrl({ hash, path } = {}) {
|
|
78
|
+
const params = createSearchParams({ hash, path });
|
|
79
|
+
return resolveApiUrl(`/git/file-attachment?${params.toString()}`);
|
|
80
|
+
}
|
|
81
|
+
|
|
77
82
|
async stageFile({ path, requestId = null } = {}) {
|
|
78
83
|
return this.#postJson('/git/stage', { path }, { requestId, fallbackError: 'Failed to stage file' });
|
|
79
84
|
}
|
|
@@ -42,6 +42,45 @@ export class VaultApiClient {
|
|
|
42
42
|
return parseApiResponse(response, 'Failed to read file');
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
+
async readBacklinks(filePath, { signal } = {}) {
|
|
46
|
+
const response = await fetch(
|
|
47
|
+
resolveApiUrl(`/backlinks?file=${encodeURIComponent(filePath)}`),
|
|
48
|
+
{ signal },
|
|
49
|
+
);
|
|
50
|
+
const data = await parseApiResponse(response, 'Failed to load backlinks');
|
|
51
|
+
return Array.isArray(data.backlinks) ? data.backlinks : [];
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async syncStructurizrWorkspace({ path, source }) {
|
|
55
|
+
const response = await fetch(resolveApiUrl('/structurizr/sync'), {
|
|
56
|
+
body: JSON.stringify({
|
|
57
|
+
path,
|
|
58
|
+
source,
|
|
59
|
+
}),
|
|
60
|
+
headers: {
|
|
61
|
+
'Content-Type': 'application/json',
|
|
62
|
+
},
|
|
63
|
+
method: 'POST',
|
|
64
|
+
});
|
|
65
|
+
return parseApiResponse(response, 'Failed to sync Structurizr workspace');
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async renderSvg(source) {
|
|
69
|
+
const response = await fetch(resolveApiUrl('/plantuml/render'), {
|
|
70
|
+
body: JSON.stringify({ source }),
|
|
71
|
+
headers: {
|
|
72
|
+
'Content-Type': 'application/json',
|
|
73
|
+
},
|
|
74
|
+
method: 'POST',
|
|
75
|
+
});
|
|
76
|
+
const data = await parseApiResponse(response, 'Failed to render PlantUML');
|
|
77
|
+
if (!data.ok || typeof data.svg !== 'string') {
|
|
78
|
+
throw new Error(data.error || 'Failed to render PlantUML');
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return data.svg;
|
|
82
|
+
}
|
|
83
|
+
|
|
45
84
|
async searchText({ limit = 50, query = '', signal = null } = {}) {
|
|
46
85
|
const searchParams = new URLSearchParams({
|
|
47
86
|
limit: String(limit),
|
|
@@ -236,6 +275,18 @@ export class VaultApiClient {
|
|
|
236
275
|
return parseApiResponse(response, 'Failed to upload image');
|
|
237
276
|
}
|
|
238
277
|
|
|
278
|
+
async uploadFile({ file, path, requestId = null }) {
|
|
279
|
+
const response = await fetch(resolveApiUrl('/file/upload'), {
|
|
280
|
+
body: file,
|
|
281
|
+
headers: createRequestHeaders(requestId, {
|
|
282
|
+
'Content-Type': file?.type || 'application/octet-stream',
|
|
283
|
+
'X-CollabMD-File-Path': encodeHeaderMetadata(path),
|
|
284
|
+
}),
|
|
285
|
+
method: 'POST',
|
|
286
|
+
});
|
|
287
|
+
return parseApiResponse(response, 'Failed to upload file');
|
|
288
|
+
}
|
|
289
|
+
|
|
239
290
|
async downloadFile(path) {
|
|
240
291
|
const fallbackFileName = getVaultPathLeaf(path) || 'download';
|
|
241
292
|
return triggerDownload(resolveApiUrl(`/download/file?path=${encodeURIComponent(path)}`), {
|
|
@@ -43,11 +43,18 @@ function createNode(entry) {
|
|
|
43
43
|
};
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
|
|
46
|
+
const node = {
|
|
47
47
|
name: entry.name,
|
|
48
48
|
path: entry.path,
|
|
49
49
|
type: entry.type,
|
|
50
50
|
};
|
|
51
|
+
|
|
52
|
+
const mtimeMs = Number(entry.mtimeMs);
|
|
53
|
+
if (Number.isFinite(mtimeMs)) {
|
|
54
|
+
node.mtimeMs = mtimeMs;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return node;
|
|
51
58
|
}
|
|
52
59
|
|
|
53
60
|
function sortNodes(nodes = []) {
|
|
@@ -184,6 +191,11 @@ class WorkspaceTreeModel {
|
|
|
184
191
|
node.name = nextNode.name;
|
|
185
192
|
node.path = nextNode.path;
|
|
186
193
|
node.type = nextNode.type;
|
|
194
|
+
if (nextNode.mtimeMs === undefined) {
|
|
195
|
+
delete node.mtimeMs;
|
|
196
|
+
} else {
|
|
197
|
+
node.mtimeMs = nextNode.mtimeMs;
|
|
198
|
+
}
|
|
187
199
|
if (node.type === 'directory' && !Array.isArray(node.children)) {
|
|
188
200
|
node.children = [];
|
|
189
201
|
}
|
|
@@ -1,20 +1,14 @@
|
|
|
1
1
|
import { stripVaultFileExtension } from '../../domain/file-kind.js';
|
|
2
2
|
import { getVaultPathLeaf, getVaultPathParent } from '../domain/vault-paths.js';
|
|
3
|
+
import {
|
|
4
|
+
createCommentOverviewThread,
|
|
5
|
+
formatAnchorLabel,
|
|
6
|
+
} from './comment-ui/comment-ui-shared.js';
|
|
3
7
|
|
|
4
8
|
function asArray(value) {
|
|
5
9
|
return Array.isArray(value) ? value : [];
|
|
6
10
|
}
|
|
7
11
|
|
|
8
|
-
function formatLineLabel(anchor = {}) {
|
|
9
|
-
const startLine = Number(anchor.startLine || 0);
|
|
10
|
-
const endLine = Number(anchor.endLine || startLine);
|
|
11
|
-
if (!Number.isFinite(startLine) || startLine <= 0) {
|
|
12
|
-
return 'No source anchor';
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
return startLine === endLine ? `Line ${startLine}` : `Lines ${startLine}-${endLine}`;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
12
|
function createOverviewState({ copy, title, tone = '' }) {
|
|
19
13
|
const state = document.createElement('div');
|
|
20
14
|
state.className = 'comment-overview-empty ui-empty-state ui-empty-state--compact';
|
|
@@ -80,7 +74,7 @@ export class CommentOverviewController {
|
|
|
80
74
|
try {
|
|
81
75
|
const payload = await this.vaultApiClient.readCommentOverview();
|
|
82
76
|
this.errorMessage = '';
|
|
83
|
-
this.setOverview(payload?.overview ?? payload ?? {});
|
|
77
|
+
this.setOverview(payload?.overview ?? payload ?? {}, { render: false });
|
|
84
78
|
} catch (error) {
|
|
85
79
|
console.error('[comments] Failed to load comment overview:', error.message);
|
|
86
80
|
this.errorMessage = 'Unable to load open comments.';
|
|
@@ -95,7 +89,7 @@ export class CommentOverviewController {
|
|
|
95
89
|
return this.refreshPromise;
|
|
96
90
|
}
|
|
97
91
|
|
|
98
|
-
setOverview(overview = {}) {
|
|
92
|
+
setOverview(overview = {}, { render = true } = {}) {
|
|
99
93
|
this.errorMessage = '';
|
|
100
94
|
this.overview = {
|
|
101
95
|
files: asArray(overview.files),
|
|
@@ -105,7 +99,9 @@ export class CommentOverviewController {
|
|
|
105
99
|
this.onOverviewChange?.(this.overview, {
|
|
106
100
|
threadCounts: this.getThreadCounts(),
|
|
107
101
|
});
|
|
108
|
-
|
|
102
|
+
if (render) {
|
|
103
|
+
this.render();
|
|
104
|
+
}
|
|
109
105
|
}
|
|
110
106
|
|
|
111
107
|
render() {
|
|
@@ -187,9 +183,15 @@ export class CommentOverviewController {
|
|
|
187
183
|
}
|
|
188
184
|
|
|
189
185
|
createThreadButton(filePath, thread) {
|
|
190
|
-
const
|
|
191
|
-
button
|
|
192
|
-
|
|
186
|
+
const messageCount = Number(thread.messageCount || 0);
|
|
187
|
+
const button = createCommentOverviewThread({
|
|
188
|
+
authorName: thread.latestMessage?.userName || thread.createdByName || 'Anonymous',
|
|
189
|
+
lineLabel: formatAnchorLabel(thread.anchor),
|
|
190
|
+
messageCount,
|
|
191
|
+
previewBody: thread.latestMessage?.bodyPreview || '',
|
|
192
|
+
quote: thread.anchor?.quote || 'Source anchored comment',
|
|
193
|
+
timestamp: this.formatTimestamp(thread.latestActivityAt),
|
|
194
|
+
});
|
|
193
195
|
button.addEventListener('click', () => {
|
|
194
196
|
this.onThreadSelect?.({
|
|
195
197
|
anchor: thread.anchor,
|
|
@@ -197,43 +199,6 @@ export class CommentOverviewController {
|
|
|
197
199
|
threadId: thread.id,
|
|
198
200
|
});
|
|
199
201
|
});
|
|
200
|
-
|
|
201
|
-
const header = document.createElement('div');
|
|
202
|
-
header.className = 'comment-overview-thread-header';
|
|
203
|
-
|
|
204
|
-
const line = document.createElement('span');
|
|
205
|
-
line.className = 'comment-overview-thread-line';
|
|
206
|
-
line.textContent = formatLineLabel(thread.anchor);
|
|
207
|
-
|
|
208
|
-
const meta = document.createElement('span');
|
|
209
|
-
meta.className = 'comment-overview-thread-meta';
|
|
210
|
-
meta.textContent = this.formatTimestamp(thread.latestActivityAt);
|
|
211
|
-
header.append(line, meta);
|
|
212
|
-
|
|
213
|
-
const body = document.createElement('div');
|
|
214
|
-
body.className = 'comment-overview-thread-body';
|
|
215
|
-
|
|
216
|
-
const quote = document.createElement('span');
|
|
217
|
-
quote.className = 'comment-overview-thread-quote';
|
|
218
|
-
quote.textContent = thread.anchor?.quote || 'Source anchored comment';
|
|
219
|
-
|
|
220
|
-
const preview = document.createElement('p');
|
|
221
|
-
preview.className = 'comment-overview-thread-preview';
|
|
222
|
-
preview.textContent = thread.latestMessage?.bodyPreview || '';
|
|
223
|
-
body.append(preview, quote);
|
|
224
|
-
|
|
225
|
-
const footer = document.createElement('div');
|
|
226
|
-
footer.className = 'comment-overview-thread-footer';
|
|
227
|
-
const author = document.createElement('span');
|
|
228
|
-
author.className = 'comment-overview-thread-author';
|
|
229
|
-
author.textContent = thread.latestMessage?.userName || thread.createdByName || 'Anonymous';
|
|
230
|
-
const replies = document.createElement('span');
|
|
231
|
-
replies.className = 'comment-overview-thread-message-count';
|
|
232
|
-
const messageCount = Number(thread.messageCount || 0);
|
|
233
|
-
replies.textContent = `${messageCount} message${messageCount === 1 ? '' : 's'}`;
|
|
234
|
-
footer.append(author, replies);
|
|
235
|
-
|
|
236
|
-
button.append(header, body, footer);
|
|
237
202
|
return button;
|
|
238
203
|
}
|
|
239
204
|
|
|
@@ -360,17 +360,6 @@ function createThreadElement(thread) {
|
|
|
360
360
|
const actions = document.createElement('div');
|
|
361
361
|
actions.className = 'ui-record-actions comment-thread-card-actions';
|
|
362
362
|
|
|
363
|
-
const jump = document.createElement('button');
|
|
364
|
-
jump.type = 'button';
|
|
365
|
-
jump.className = buttonClassNames({
|
|
366
|
-
variant: 'ghost',
|
|
367
|
-
size: 'compact',
|
|
368
|
-
pill: true,
|
|
369
|
-
extra: ['ui-action-pill', 'comment-thread-card-action'],
|
|
370
|
-
});
|
|
371
|
-
jump.textContent = 'Jump';
|
|
372
|
-
jump.addEventListener('click', () => this.onNavigateToLine?.(thread.anchor?.startLine ?? 1));
|
|
373
|
-
|
|
374
363
|
const reply = document.createElement('button');
|
|
375
364
|
reply.type = 'button';
|
|
376
365
|
reply.className = buttonClassNames({
|
|
@@ -409,7 +398,7 @@ function createThreadElement(thread) {
|
|
|
409
398
|
await this.onResolveThread?.(thread.id);
|
|
410
399
|
});
|
|
411
400
|
|
|
412
|
-
actions.append(
|
|
401
|
+
actions.append(reply, resolve);
|
|
413
402
|
|
|
414
403
|
thread.messages.forEach((message, index) => {
|
|
415
404
|
article.appendChild(this.createMessageElement(thread, message, {
|