collabmd 0.1.45 → 0.1.47
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 +40 -19
- package/bin/collabmd.js +27 -1
- package/dist/client/assets/CodeMirrorEditor-UCMRAMCV-BPH4FJkv.js +1 -0
- package/dist/client/assets/{abnfDiagram-N423BO3Z-CUMTFJhI.js → abnfDiagram-VCTEODGH-BNDLzJ7L.js} +1 -1
- package/dist/client/assets/architecture-7GRP2DOG-BMOPAkUC.js +1 -0
- package/dist/client/assets/{architectureDiagram-T3A2C74G-w25m71oV.js → architectureDiagram-5GKGNRK7-I39sYjHI.js} +1 -1
- package/dist/client/assets/base-BYODPHdS.js +1 -0
- package/dist/client/assets/blockDiagram-NRAW4CY4-C6DtJy5Y.js +129 -0
- package/dist/client/assets/browser-BKLM0ThC-IxjwuOIz-BBqQnUEb.js +1 -0
- package/dist/client/assets/browser-utils--H_DkyEF.js +1 -0
- package/dist/client/assets/c4Diagram-UCG6FXSJ-CtWtxS9_.js +38 -0
- package/dist/client/assets/channel-CaoKA_We.js +1 -0
- package/dist/client/assets/{chunk-U6XO7XAA-BFxjP5U3.js → chunk-2ZTRR5NV-C2qZJila.js} +1 -1
- package/dist/client/assets/{chunk-YOTPTUD7-D0vRKqYp.js → chunk-3Z5EZCMW-DMniOlPd.js} +1 -1
- package/dist/client/assets/{chunk-BIQX33UG-DZEsGuSn.js → chunk-5V3GS4D5-i-ZDOEnP.js} +1 -1
- package/dist/client/assets/{chunk-MOZMSUNE-XVucA-n6.js → chunk-6AZGARVD-BB6FqMl-.js} +1 -1
- package/dist/client/assets/{chunk-5JV3BV7I-Cy5pj3QY.js → chunk-6EIED4P4-DdUJkP0f.js} +1 -1
- package/dist/client/assets/{chunk-R7FJI6CG-Cnn3gE-Y.js → chunk-6K3QC6MW-CqVsVn-y.js} +1 -1
- package/dist/client/assets/{chunk-OSBZ3O6U-CrKZOUOr.js → chunk-6TQVIW2G-DKlLbNIP.js} +1 -1
- package/dist/client/assets/{chunk-JG7HCLWE-BYfiSaVt.js → chunk-747NJXEK-BFkZPlab.js} +1 -1
- package/dist/client/assets/{chunk-NSK5VX7P-TLH37VXI.js → chunk-75Z2AOVW-CdFSaPjz.js} +1 -1
- package/dist/client/assets/chunk-CKBBP62Z-DG26UiBv.js +2 -0
- package/dist/client/assets/{chunk-3NCLNEKW-Y2Jf_LI-.js → chunk-CLGD4ZFX-DQLgbQrv.js} +1 -1
- package/dist/client/assets/chunk-DSNCTWBM-8xkug7fQ.js +2 -0
- package/dist/client/assets/chunk-DU6HZSFF-COXDzJK1.js +127 -0
- package/dist/client/assets/{chunk-2GRJ4B5K-DhnlUv3F.js → chunk-F27PBJKO-CNHIFhjo.js} +1 -1
- package/dist/client/assets/{chunk-YGJVY4SA-CjSe7Bx7.js → chunk-FLYJHZSQ-Be7lKF17.js} +1 -1
- package/dist/client/assets/{chunk-KEIR6QF5-BICK3FdT.js → chunk-FOHPRMQF-DHwB1DNv.js} +2 -2
- package/dist/client/assets/{chunk-5RXB4S5H-BWrwFqMI.js → chunk-G27WJ6UU-D9-eg537.js} +2 -2
- package/dist/client/assets/chunk-GMAD6QVW-CbcBqPM5.js +72 -0
- package/dist/client/assets/{chunk-QBLGF6JB-CxAyS2mW.js → chunk-I5DQTOEV-Hm2LfiYI.js} +1 -1
- package/dist/client/assets/{chunk-5FCAYU7R-Dh5nylU8.js → chunk-ICYGCRZG-749o-pGJ.js} +1 -1
- package/dist/client/assets/{chunk-CQNSW5MT-B8S09OIh.js → chunk-IH6LHLGP-CC6I9F3i.js} +1 -1
- package/dist/client/assets/chunk-JQ64N6SF-DSFNH4bh.js +1 -0
- package/dist/client/assets/{chunk-CYSBUYHQ-C1Rf4art.js → chunk-KI3K4JFJ-Rma-0k_M.js} +1 -1
- package/dist/client/assets/{chunk-GF5L2VYU-DkJpO7ff.js → chunk-LCL6LL3I-DmDkVukh.js} +1 -1
- package/dist/client/assets/chunk-LNGE3PJU-DWpGwHrW.js +34 -0
- package/dist/client/assets/chunk-OBVCFTLP-BJR7u1jh.js +1 -0
- package/dist/client/assets/{chunk-5TONJI2A-DQOY3q1H.js → chunk-OUJLGHUK-D0j2JRSg.js} +1 -1
- package/dist/client/assets/chunk-P2QGCYS3-BRpqY32H.js +1 -0
- package/dist/client/assets/{chunk-KBJHAD2P-CB_3zWUf.js → chunk-POPQ4Y6H-Cb4SlVZF.js} +1 -1
- package/dist/client/assets/{chunk-4I5QYGJK-CCB2YcIH.js → chunk-PWAF6VOD-Cz8IwBB5.js} +1 -1
- package/dist/client/assets/chunk-R7TYR2AO-CTd0Yj98.js +10 -0
- package/dist/client/assets/chunk-RHFEMEQ7-CWY8cf4H.js +168 -0
- package/dist/client/assets/chunk-RM4UPSZO-CWRwxNWJ.js +41 -0
- package/dist/client/assets/{chunk-6Q2QTUOP-CPlWzBtc.js → chunk-SVP7TREG-CyHOmcac.js} +1 -1
- package/dist/client/assets/chunk-TEH6E4GO-DVayPj-q.js +62 -0
- package/dist/client/assets/{chunk-EMLP6XTP-RLD4nXpI.js → chunk-UY3FDG6J-CnmaAkG5.js} +1 -1
- package/dist/client/assets/{chunk-5HE753X5-Bw-yg4kO.js → chunk-XHIXRSVI-BFQ6B8BE.js} +1 -1
- package/dist/client/assets/classDiagram-DTDB5LWJ-7fEmn0zV.js +1 -0
- package/dist/client/assets/classDiagram-v2-JRS7N3AN-7fEmn0zV.js +1 -0
- 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-DWkk9Vh3.js +7 -0
- package/dist/client/assets/cynefin-OW5HDTMX-7tP7QfM3.js +1 -0
- package/dist/client/assets/{cynefinDiagram-MW4NZA55-CJxXfJek.js → cynefinDiagram-5FMLGOSQ-CdTPi2yO.js} +1 -1
- package/dist/client/assets/dagre-3AP2YEHR-5gtJtd0O.js +4 -0
- package/dist/client/assets/dagre-PrKaheQc.js +1 -0
- package/dist/client/assets/{diagram-7IWD3JNH-B2d2Bm15.js → diagram-S7CK7UJ4-BudWz9WU.js} +4 -4
- package/dist/client/assets/{diagram-UB23O5K3-DJcKCwv9.js → diagram-UQ7AKVKN-3v4rbH-s.js} +1 -1
- package/dist/client/assets/{diagram-B4RE2ZJO-NP4UAwjR.js → diagram-VSXAHHWV-CQRVEZR7.js} +1 -1
- package/dist/client/assets/{diagram-Q27KOJAE-DylDuwor.js → diagram-VX7I27RA-C_-rj0sw.js} +1 -1
- package/dist/client/assets/{diagram-LBJQPF4R-D1FJQV8t.js → diagram-Z3DM3KII-B4yxavgj.js} +1 -1
- package/dist/client/assets/direct-engine-DdrQKpgq-CtKfQzCt.js +5 -0
- package/dist/client/assets/dist-73RPdcyU.js +1 -0
- package/dist/client/assets/dist-BJ1bJs33.js +1 -0
- package/dist/client/assets/{dist-CYZmbokb.js → dist-BK9QvQrc.js} +9 -9
- package/dist/client/assets/dist-BPHLJ09L.js +11 -0
- package/dist/client/assets/dist-B_yQaEv_.js +6 -0
- package/dist/client/assets/dist-BeajGMhO.js +1 -0
- package/dist/client/assets/{dist-JYEDRrPT.js → dist-Bw3VIE_v.js} +1 -1
- package/dist/client/assets/dist-ByrG2qBA.js +1 -0
- package/dist/client/assets/dist-C5NgpdG5.js +1 -0
- package/dist/client/assets/dist-CH-sQu3r.js +1 -0
- package/dist/client/assets/dist-CKS_-are.js +1 -0
- package/dist/client/assets/{dist-MvcV4190.js → dist-CdmKGRGr.js} +1 -1
- package/dist/client/assets/{dist-DGb27Dl6.js → dist-CietwTVS.js} +1 -1
- package/dist/client/assets/dist-D37Hyu3h.js +9 -0
- package/dist/client/assets/dist-D3fehAZ2.js +2 -0
- package/dist/client/assets/dist-D5wCC32z.js +1 -0
- package/dist/client/assets/{dist-DCzuX1AF.js → dist-DDUn5zTz.js} +1 -1
- package/dist/client/assets/dist-DGEbEfw4.js +1 -0
- package/dist/client/assets/dist-DVy5woUy.js +1 -0
- package/dist/client/assets/{dist-3bgCp7iY.js → dist-DZAAWTG-.js} +1 -1
- package/dist/client/assets/{dist-Drp8uu5J.js → dist-Dir5dDKZ.js} +1 -1
- package/dist/client/assets/dist-Dl_tpDSr.js +1 -0
- package/dist/client/assets/dist-DuH3ZrHo.js +1 -0
- package/dist/client/assets/dist-DutPuFmC.js +1 -0
- package/dist/client/assets/{dist-D2AQ-76g.js → dist-LQ4kDCKz.js} +1 -1
- package/dist/client/assets/{dist-C11hHqv_.js → dist-RKBTaQMb.js} +12 -12
- package/dist/client/assets/dist-Tw-Fxpeb.js +1 -0
- package/dist/client/assets/dist-eBx8q6Sy.js +1 -0
- package/dist/client/assets/{dist-DK5Gs2wa.js → dist-mj2Ht75J.js} +1 -1
- package/dist/client/assets/{dist-CnL7Y2sM.js → dist-netZOQtD.js} +1 -1
- package/dist/client/assets/{drawio-editor-ClE8jpoG.js → drawio-editor-BjrxTe3l.js} +1 -1
- package/dist/client/assets/drawioEditor-DUVZjSSr.js +2 -0
- package/dist/client/assets/{ebnfDiagram-BXEA7PRR-nw0wmA6y.js → ebnfDiagram-PWID7BFC-OXa8VEp1.js} +1 -1
- package/dist/client/assets/editor-session-DfZuRgB_.js +54 -0
- package/dist/client/assets/embedded-editor-base-DvMynhKu.css +1 -0
- package/dist/client/assets/embedpdf-BmE13ZZ0.js +1 -0
- package/dist/client/assets/embedpdf-DA4bA119-BPqZxwjc.js +2363 -0
- package/dist/client/assets/{en-7NFCYGKX-BrgYZAEe.js → en-SJ2YIMFF-CJBL3nkq.js} +1 -1
- package/dist/client/assets/erDiagram-SSCWMZ5O-C-iy6VkX.js +99 -0
- package/dist/client/assets/es-CAsWR6hG.js +22 -0
- package/dist/client/assets/eventmodeling-NTZA5JFV-BR2FzK4R.js +1 -0
- package/dist/client/assets/excalidraw-editor-BlcN6umJ.css +1 -0
- package/dist/client/assets/excalidraw-editor-D7pnUCLH.js +9 -0
- package/dist/client/assets/excalidraw-scene-BOben2gr.js +1 -0
- package/dist/client/assets/excalidrawEditor-B0jOK3lv.js +2 -0
- package/dist/client/assets/export-page-Bs3xE1fg.js +57 -0
- package/dist/client/assets/exportDocument-Dg9MjkIk.js +2 -0
- package/dist/client/assets/exportDocument-PnYU3v1s.css +1 -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/flowDiagram-A5DVABFB-CiW1eN8C.js +1 -0
- package/dist/client/assets/{ganttDiagram-PKOTCBZU-BnM97BCT.js → ganttDiagram-EL5Y4UJY-DtKHAbez.js} +1 -1
- package/dist/client/assets/gitGraph-4MIJSDKK-DYlXv_yJ.js +1 -0
- package/dist/client/assets/{gitGraphDiagram-DS77QQ5N-BQpQhjuN.js → gitGraphDiagram-WWUBYQGX-oue8fQ5o.js} +1 -1
- package/dist/client/assets/github-dark.min-BV82VuKq.css +1 -0
- package/dist/client/assets/github.min-v3dd8CNH.css +1 -0
- package/dist/client/assets/image-ZVKINSPA-0zXSnE7M.js +1 -0
- package/dist/client/assets/index-BIpgxyGZ.css +1 -0
- package/dist/client/assets/index-Z5sI0Bj-.js +2 -0
- package/dist/client/assets/info-A6RAGUB7-BlU-8_xu.js +1 -0
- package/dist/client/assets/{infoDiagram-6WML65LV-DYB8HCDQ.js → infoDiagram-RXCK75RN-BMzo6KIn.js} +1 -1
- package/dist/client/assets/{ishikawaDiagram-WSZJBQD7-Coz_P3t6.js → ishikawaDiagram-5VMMS53U-BIM3h287.js} +1 -1
- package/dist/client/assets/{journeyDiagram-NVQOT4AX-B6edChce.js → journeyDiagram-EYS64GPL-mF6Lh8O-.js} +1 -1
- package/dist/client/assets/{kanban-definition-27J2QSJJ-BFd9-cLc.js → kanban-definition-3QL26DDD-DLN2NyoR.js} +2 -2
- package/dist/client/assets/{line-B_S_S8kV.js → line-BfTWJmgf.js} +1 -1
- package/dist/client/assets/main-Dwnj9Gaq.js +903 -0
- package/dist/client/assets/{mermaid-parser.core-CUVdebAV.js → mermaid-parser.core-Bl0efOMt.js} +2 -2
- package/dist/client/assets/mermaid.core-CLZBlpIW.js +11 -0
- package/dist/client/assets/{mindmap-definition-FAOFIHXS-C9PQ38Al.js → mindmap-definition-FBJOCRG2-CRyHaHTk.js} +1 -1
- package/dist/client/assets/packet-AYTQ26CC-BSJEH9Lb.js +1 -0
- package/dist/client/assets/pdfium-RAgkpwfK.wasm +0 -0
- package/dist/client/assets/{pegDiagram-VL7TDLO6-BMZUrSaU.js → pegDiagram-XKGWAZYB-B31fKPR2.js} +1 -1
- package/dist/client/assets/pie-WAS4IAKB-qxv01Y8W.js +1 -0
- package/dist/client/assets/{pieDiagram-7S7Q4E2Y-h8kbCL9o.js → pieDiagram-E7YTZNPT-B5OZPb88.js} +1 -1
- package/dist/client/assets/preview-render-compiler-BApvlLLo.js +35 -0
- package/dist/client/assets/preview-render-compiler-BQ_3h9vD.js +1 -0
- package/dist/client/assets/preview-render-profile-CE-5rVTC.js +1 -0
- package/dist/client/assets/preview-render-worker-BmN79bVc.js +60 -0
- package/dist/client/assets/prod-BiYPMR1i.js +177 -0
- package/dist/client/assets/prod-VxdGZ5Ly.js +1 -0
- package/dist/client/assets/{quadrantDiagram-CIZ2JOQS-CkElyfMi.js → quadrantDiagram-AXDQQJYC-Drrw14YH.js} +1 -1
- package/dist/client/assets/quick-switcher-controller-ButWPmCI.js +12 -0
- package/dist/client/assets/radar-RG4KPBEZ-B6h6pWOs.js +1 -0
- package/dist/client/assets/railroad-74A4TZTK-CGtTWdGc.js +1 -0
- package/dist/client/assets/railroad-abnf-HS5TGJTU-Cq7y22IA.js +1 -0
- package/dist/client/assets/railroad-ebnf-LZEXJU2U-V0aQVv9m.js +1 -0
- package/dist/client/assets/railroad-peg-WCYAUIDC-BQiIsX_p.js +1 -0
- package/dist/client/assets/{railroadDiagram-AXF67PYL-c75gnikD.js → railroadDiagram-O6MQD6OU-BPifP0MB.js} +1 -1
- package/dist/client/assets/{requirementDiagram-LRYGKXZP-mNxKLuHS.js → requirementDiagram-EFPCY7ZU-CRZG6mRB.js} +2 -2
- package/dist/client/assets/runtime-config-BLrEKVO1.js +1 -0
- package/dist/client/assets/{sankeyDiagram-W5VNT64P-0OhplksI.js → sankeyDiagram-P5KCCOFB-DvarEJpo.js} +1 -1
- package/dist/client/assets/{sequenceDiagram-SI44F4Z6-BJ_yAocC.js → sequenceDiagram-WJ2MYXX4-B4oWlb-w.js} +1 -1
- package/dist/client/assets/{stateDiagram-OKZ733FA-Cc83O86s.js → stateDiagram-HBIQ2CUA-qbHIIqRZ.js} +1 -1
- package/dist/client/assets/stateDiagram-v2-4QOOHH4V-B--gjrit.js +1 -0
- package/dist/client/assets/swimlanes-XN3QIQJK-xB6CtUGj.js +1 -0
- package/dist/client/assets/swimlanesDiagram-VK2B7HYN-DO8XNXEg.js +8 -0
- package/dist/client/assets/{timeline-definition-Z64GVDOM-BWDAGTHz.js → timeline-definition-24CTP7MA-Lj_yTNlY.js} +1 -1
- package/dist/client/assets/treeView-Q6P3EWNA-BTTYcvrF.js +1 -0
- package/dist/client/assets/treemap-WGGIJYW6-Cg7tyfH8.js +1 -0
- package/dist/client/assets/vault-api-client-_LpV1aIn.js +6 -0
- package/dist/client/assets/{vennDiagram-T6HMQDX7-LOSsnlMB.js → vennDiagram-4TSXK5OY-BekRp5Y0.js} +1 -1
- package/dist/client/assets/wardley-WFR3VGLG-C6N1rULY.js +1 -0
- package/dist/client/assets/{wardleyDiagram-T6FBY63Y-BxII3foY.js → wardleyDiagram-VM6X3IG4-kdnV_Hdm.js} +1 -1
- package/dist/client/assets/worker-engine-m0rlHn8R-S8mpn_O0.js +17369 -0
- package/dist/client/assets/{xychartDiagram-ELKLHX3M-B-pTEjju.js → xychartDiagram-S5SC5T6Z-DufGwNSB.js} +3 -3
- package/dist/client/drawio-editor.html +1 -1
- package/dist/client/excalidraw-editor.html +1 -1
- package/dist/client/export-document.html +3 -3
- package/dist/client/index.html +30 -8
- package/docker-compose.yml +42 -0
- package/package.json +32 -18
- package/scripts/local-structurizr-compose.mjs +101 -0
- package/src/client/app/index.html +27 -5
- package/src/client/application/app-shell/comments-feature.js +21 -13
- package/src/client/application/app-shell/git-feature.js +14 -2
- package/src/client/application/app-shell/presence-feature.js +4 -3
- package/src/client/application/app-shell/ui-feature-shell.js +109 -2
- package/src/client/application/app-shell/ui-feature-toolbar.js +2 -52
- package/src/client/application/app-shell/workspace-feature.js +5 -1
- package/src/client/application/app-shell-elements.js +3 -0
- package/src/client/application/pdf-preview-controller.js +75 -0
- package/src/client/application/preview-render-compiler.js +3 -0
- package/src/client/application/quick-switcher-loader.js +1 -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 +88 -2
- package/src/client/assets/vendor/highlight/github-dark.min.css +2 -10
- package/src/client/assets/vendor/highlight/github.min.css +2 -10
- package/src/client/bootstrap/collabmd-app-shell.js +39 -6
- package/src/client/browser-utils.js +44 -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 +844 -51
- package/src/client/export/export-host.js +1 -1
- package/src/client/export/export-page.js +6 -2
- package/src/client/export/export-pipeline.js +60 -1
- package/src/client/infrastructure/browser-preferences-port.js +10 -0
- 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 +46 -2
- package/src/client/infrastructure/excalidraw-room-client.js +103 -14
- package/src/client/infrastructure/git-api-client.js +5 -0
- package/src/client/infrastructure/vault-api-client.js +26 -0
- package/src/client/presentation/comment-ui/comment-ui-render.js +4 -0
- package/src/client/presentation/comment-ui/comment-ui-shared.js +4 -0
- package/src/client/presentation/comment-ui-controller.js +2 -0
- 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 +81 -8
- package/src/client/presentation/file-action-controller.js +102 -0
- package/src/client/presentation/file-explorer-controller.js +0 -5
- package/src/client/presentation/file-explorer-view.js +51 -73
- package/src/client/presentation/file-icon-svg.js +40 -0
- package/src/client/presentation/file-tree-state.js +6 -3
- package/src/client/presentation/git-diff-view-controller.js +731 -3
- package/src/client/presentation/git-panel-controller.js +2 -2
- package/src/client/presentation/outline-controller.js +86 -0
- package/src/client/presentation/quick-switcher-controller.js +73 -22
- package/src/client/presentation/quick-switcher-text-search.js +1 -1
- package/src/client/styles/components/navigation.css +11 -1
- package/src/client/styles/features/diagram-preview.css +76 -0
- package/src/client/styles/features/diff-special.css +309 -0
- package/src/client/styles/features/preview-markdown.css +25 -0
- package/src/client/styles/features/quick-switcher.css +8 -0
- package/src/client/styles/foundation/themes.css +7 -0
- package/src/client/styles/layout/shell.css +40 -1
- package/src/client/styles/layout/sidebar.css +42 -4
- package/src/client/styles/layout/view-modes.css +1 -0
- package/src/client/styles/overrides/highlightjs.css +20 -4
- 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/config/env.js +79 -0
- package/src/server/create-app-server.js +12 -0
- 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-transform.js +1 -4
- package/src/server/domain/collaboration/collaboration-room.js +55 -21
- package/src/server/domain/comment-overview.js +15 -5
- package/src/server/domain/ripgrep-search-service.js +4 -0
- package/src/server/infrastructure/git/diff-service.js +5 -0
- package/src/server/infrastructure/git/git-service.js +44 -11
- package/src/server/infrastructure/git/history-service.js +54 -0
- package/src/server/infrastructure/git/image-mime.js +14 -0
- package/src/server/infrastructure/git/untracked-files.js +28 -1
- package/src/server/infrastructure/http/create-git-api-command-handler.js +3 -1
- package/src/server/infrastructure/http/create-git-api-query-handler.js +31 -1
- package/src/server/infrastructure/http/create-request-handler.js +16 -23
- package/src/server/infrastructure/http/create-static-handler.js +5 -2
- package/src/server/infrastructure/http/create-structurizr-api-handler.js +147 -0
- package/src/server/infrastructure/http/create-vault-api-command-handler.js +66 -4
- package/src/server/infrastructure/http/create-vault-api-query-handler.js +51 -3
- 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 +400 -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/architecture-TIHT7OUA-CsHfPErM.js +0 -1
- package/dist/client/assets/base-CxPEiHuy.js +0 -1
- package/dist/client/assets/blockDiagram-VBNYF7ZC-CipJt9Jl.js +0 -132
- package/dist/client/assets/browser-utils-CKtI_rZl.js +0 -1
- package/dist/client/assets/c4Diagram-5PPSVZJV-B5ktVx6g.js +0 -10
- package/dist/client/assets/channel-4Jkmq93I.js +0 -1
- package/dist/client/assets/chunk-7BUUIJ7U-Bb538aSH.js +0 -1
- package/dist/client/assets/chunk-7Z6QIM7H-BP9Pdsm9.js +0 -10
- package/dist/client/assets/chunk-ERSWUTIN-ME9RVNel.js +0 -41
- package/dist/client/assets/chunk-I66GZJ75-CVFiiffQ.js +0 -127
- package/dist/client/assets/chunk-J7OUQ5F2-4CEb7pvH.js +0 -2
- package/dist/client/assets/chunk-JQJVKLGR-cx8X_24v.js +0 -156
- package/dist/client/assets/chunk-QR6OTTB3-CDAA_5Ib.js +0 -62
- package/dist/client/assets/chunk-RYQCIY6F-D-a0zH8K.js +0 -1
- package/dist/client/assets/chunk-UBXNYLIW-Bhk-PbFK.js +0 -1
- package/dist/client/assets/chunk-W5SLKNZC-BdNSCxZ9.js +0 -1
- package/dist/client/assets/chunk-WRU74C26-h6_XkD3_.js +0 -70
- package/dist/client/assets/chunk-ZIRB5QZD-O39ZBUk3.js +0 -32
- package/dist/client/assets/classDiagram-JCYQIIEL-L7lsyW_w.js +0 -1
- package/dist/client/assets/classDiagram-v2-OCEON4UE-L7lsyW_w.js +0 -1
- package/dist/client/assets/comment-threads-DDegmaFO.js +0 -4
- package/dist/client/assets/cynefin-VYW2F7L2-Ds2Fw2Y0.js +0 -1
- package/dist/client/assets/dagre-Buvkdvvj.js +0 -1
- package/dist/client/assets/dagre-VZM6K2ZE-L1xArOGs.js +0 -4
- 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-DhLvC8DS.js +0 -2
- 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/drawioEditor-CXaTM9Ds.js +0 -2
- package/dist/client/assets/editor-session-GTwZS6Df.js +0 -16
- package/dist/client/assets/embedded-editor-base-CiNEc8vy.css +0 -1
- package/dist/client/assets/erDiagram-JOGREHBK-cYUpEKpf.js +0 -85
- package/dist/client/assets/eventmodeling-45OFAUF4-CLEiVx5s.js +0 -1
- package/dist/client/assets/excalidraw-editor-BZVM81BO.js +0 -9
- package/dist/client/assets/excalidraw-editor-CyGwOhLz.css +0 -1
- package/dist/client/assets/excalidraw-scene-C8n9CHsK.js +0 -176
- package/dist/client/assets/excalidrawEditor-DxJanCze.js +0 -2
- package/dist/client/assets/export-page-CUfws2T-.js +0 -39
- package/dist/client/assets/exportDocument-BCuxqXDY.js +0 -2
- package/dist/client/assets/exportDocument-DV-OX74E.css +0 -1
- package/dist/client/assets/file-kind-BKPOIRPE.js +0 -1
- package/dist/client/assets/flowDiagram-UKHOOZJN-D0ypP0Ez.js +0 -1
- package/dist/client/assets/gitGraph-TEB2WS4Q-CZFBqwW-.js +0 -1
- package/dist/client/assets/github-dark.min-Z5exjX_n.css +0 -1
- package/dist/client/assets/github.min-D2BCvnWf.css +0 -1
- package/dist/client/assets/image-QXRYHUNP-lfTHE3bb.js +0 -1
- package/dist/client/assets/index-C3YnUgMD.css +0 -1
- package/dist/client/assets/index-D2wXraJ1.js +0 -2
- package/dist/client/assets/info-DKCQHKI2--1InqQeT.js +0 -1
- package/dist/client/assets/main-DtiGz7uI.js +0 -795
- package/dist/client/assets/map-BaFkSB1l.js +0 -1
- package/dist/client/assets/mermaid.core-DN-lF51U.js +0 -11
- package/dist/client/assets/packet-7NZHBO7P-BGMXB0XL.js +0 -1
- package/dist/client/assets/pie-RZYD4A2V-DgYg3tCt.js +0 -1
- package/dist/client/assets/preview-render-compiler-BSaQO_p3.js +0 -35
- package/dist/client/assets/preview-render-compiler-csGM5ZFz.js +0 -1
- package/dist/client/assets/preview-render-profile-BZzGSg6K.js +0 -22
- package/dist/client/assets/preview-render-worker-B42LWvrD.js +0 -60
- package/dist/client/assets/quick-switcher-controller-shW_6vPY.js +0 -12
- package/dist/client/assets/radar-I7S5WNFK-Ag-zp_Rq.js +0 -1
- package/dist/client/assets/railroad-3IZDKUUU-aibJDIDV.js +0 -1
- package/dist/client/assets/railroad-abnf-AHOZXSZD-BBMEuvIl.js +0 -1
- package/dist/client/assets/railroad-ebnf-EBAXGLYW-Be1nseej.js +0 -1
- package/dist/client/assets/railroad-peg-LSFZ7HO6-DIvD-Eyb.js +0 -1
- package/dist/client/assets/room-CQLoqtMo.js +0 -1
- package/dist/client/assets/runtime-config-DSGmGE9V.js +0 -1
- package/dist/client/assets/stateDiagram-v2-UEYNNEHI-B1-BufUk.js +0 -1
- package/dist/client/assets/swimlanes-SLNWSIFB-oVTWEJIj.js +0 -2
- package/dist/client/assets/swimlanesDiagram-ULZ7WXOC-C5OTVwEu.js +0 -8
- package/dist/client/assets/treeView-QDETBFTQ-uHZ-3Fz8.js +0 -1
- package/dist/client/assets/treemap-6X3UGDF4-BnUKyy3l.js +0 -1
- package/dist/client/assets/vault-api-client-C6bcGwy-.js +0 -5
- package/dist/client/assets/wardley-OPB4EBWU-Da7TLC2F.js +0 -1
- package/src/server/domain/collaboration/room-persistence-controller.js +0 -80
|
@@ -1,6 +1,283 @@
|
|
|
1
1
|
import { escapeHtml } from '../domain/vault-utils.js';
|
|
2
2
|
import { getVaultPathLeaf, getVaultPathParent } from '../domain/vault-paths.js';
|
|
3
|
+
import { resolveApiUrl } from '../domain/runtime-paths.js';
|
|
4
|
+
import { renderDrawioViewer } from './drawio-viewer.js';
|
|
3
5
|
import { buttonClassNames } from './components/ui/button.js';
|
|
6
|
+
import { getVaultFileKind } from '../../domain/file-kind.js';
|
|
7
|
+
|
|
8
|
+
const IMAGE_MIME_LABELS = Object.freeze({
|
|
9
|
+
'.gif': 'GIF',
|
|
10
|
+
'.jpeg': 'JPEG',
|
|
11
|
+
'.jpg': 'JPEG',
|
|
12
|
+
'.png': 'PNG',
|
|
13
|
+
'.svg': 'SVG',
|
|
14
|
+
'.webp': 'WebP',
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
function getDiffFileKind(file = null) {
|
|
18
|
+
return file?.fileKind || getVaultFileKind(file?.path) || null;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function getImageFormat(filePath = '') {
|
|
22
|
+
const normalizedPath = String(filePath ?? '').toLowerCase();
|
|
23
|
+
const extension = Object.keys(IMAGE_MIME_LABELS).find((candidate) => normalizedPath.endsWith(candidate));
|
|
24
|
+
return extension ? IMAGE_MIME_LABELS[extension] : 'Image';
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function parseExcalidrawScene(rawSource = '') {
|
|
28
|
+
try {
|
|
29
|
+
const parsed = JSON.parse(String(rawSource ?? ''));
|
|
30
|
+
if (!parsed || typeof parsed !== 'object' || !Array.isArray(parsed.elements)) {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
return parsed;
|
|
34
|
+
} catch {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function pickExcalidrawSceneSource(lines = []) {
|
|
40
|
+
if (lines.length === 0) {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const joinedSource = lines.join('\n');
|
|
45
|
+
if (parseExcalidrawScene(joinedSource)) {
|
|
46
|
+
return joinedSource;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return lines.find((line) => parseExcalidrawScene(line)) ?? null;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function getExcalidrawSceneSources(detail = {}) {
|
|
53
|
+
const beforeLines = [];
|
|
54
|
+
const afterLines = [];
|
|
55
|
+
|
|
56
|
+
for (const hunk of detail.hunks ?? []) {
|
|
57
|
+
for (const line of hunk.lines ?? []) {
|
|
58
|
+
if (line.type === 'context') {
|
|
59
|
+
beforeLines.push(line.content);
|
|
60
|
+
afterLines.push(line.content);
|
|
61
|
+
} else if (line.type === 'deletion') {
|
|
62
|
+
beforeLines.push(line.content);
|
|
63
|
+
} else if (line.type === 'addition') {
|
|
64
|
+
afterLines.push(line.content);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const beforeSource = pickExcalidrawSceneSource(beforeLines);
|
|
70
|
+
const afterSource = pickExcalidrawSceneSource(afterLines);
|
|
71
|
+
const isAdded = detail.status === 'added' || detail.status === 'untracked';
|
|
72
|
+
const isDeleted = detail.status === 'deleted';
|
|
73
|
+
|
|
74
|
+
return {
|
|
75
|
+
after: isDeleted ? null : parseExcalidrawScene(afterSource),
|
|
76
|
+
before: isAdded ? null : parseExcalidrawScene(beforeSource),
|
|
77
|
+
afterSource: isDeleted ? '' : afterSource || '',
|
|
78
|
+
beforeSource: isAdded ? '' : beforeSource || '',
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function getVisibleSceneElements(scene = null) {
|
|
83
|
+
return Array.isArray(scene?.elements)
|
|
84
|
+
? scene.elements.filter((element) => element && !element.isDeleted)
|
|
85
|
+
: [];
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function sceneElementSignature(element) {
|
|
89
|
+
if (!element || typeof element !== 'object') {
|
|
90
|
+
return '';
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const copy = { ...element };
|
|
94
|
+
delete copy.version;
|
|
95
|
+
delete copy.versionNonce;
|
|
96
|
+
return JSON.stringify(copy);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function summarizeExcalidrawChange(beforeScene, afterScene) {
|
|
100
|
+
if (!beforeScene && !afterScene) {
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const beforeById = new Map(getVisibleSceneElements(beforeScene).map((element) => [element.id, element]));
|
|
105
|
+
const afterById = new Map(getVisibleSceneElements(afterScene).map((element) => [element.id, element]));
|
|
106
|
+
let added = 0;
|
|
107
|
+
let removed = 0;
|
|
108
|
+
let updated = 0;
|
|
109
|
+
|
|
110
|
+
for (const [id, element] of afterById) {
|
|
111
|
+
if (!beforeById.has(id)) {
|
|
112
|
+
added += 1;
|
|
113
|
+
} else if (sceneElementSignature(beforeById.get(id)) !== sceneElementSignature(element)) {
|
|
114
|
+
updated += 1;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
for (const id of beforeById.keys()) {
|
|
119
|
+
if (!afterById.has(id)) {
|
|
120
|
+
removed += 1;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return {
|
|
125
|
+
added,
|
|
126
|
+
afterCount: afterById.size,
|
|
127
|
+
beforeCount: beforeById.size,
|
|
128
|
+
removed,
|
|
129
|
+
updated,
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function formatByteLength(value) {
|
|
134
|
+
const bytes = Number(value);
|
|
135
|
+
if (!Number.isFinite(bytes) || bytes < 0) {
|
|
136
|
+
return '';
|
|
137
|
+
}
|
|
138
|
+
if (bytes < 1024) {
|
|
139
|
+
return `${bytes} B`;
|
|
140
|
+
}
|
|
141
|
+
if (bytes < 1024 * 1024) {
|
|
142
|
+
return `${(bytes / 1024).toFixed(bytes < 10 * 1024 ? 1 : 0)} KB`;
|
|
143
|
+
}
|
|
144
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function hasDrawioSourceRoot(source) {
|
|
148
|
+
return /<(?:mxfile|mxGraphModel|diagram)\b/iu.test(String(source ?? ''));
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function formatDrawioSource(source) {
|
|
152
|
+
return String(source ?? '')
|
|
153
|
+
.replace(/>\s*</gu, '>\n<')
|
|
154
|
+
.trim();
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function pickDrawioSource(lines = []) {
|
|
158
|
+
if (lines.length === 0) {
|
|
159
|
+
return '';
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const joinedSource = lines.join('\n');
|
|
163
|
+
if (hasDrawioSourceRoot(joinedSource)) {
|
|
164
|
+
return joinedSource;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
return lines.find((line) => hasDrawioSourceRoot(line)) ?? '';
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function getDrawioSourcePair(detail = {}) {
|
|
171
|
+
const beforeLines = [];
|
|
172
|
+
const afterLines = [];
|
|
173
|
+
|
|
174
|
+
for (const hunk of detail.hunks ?? []) {
|
|
175
|
+
for (const line of hunk.lines ?? []) {
|
|
176
|
+
if (line.type === 'context') {
|
|
177
|
+
beforeLines.push(line.content);
|
|
178
|
+
afterLines.push(line.content);
|
|
179
|
+
} else if (line.type === 'deletion') {
|
|
180
|
+
beforeLines.push(line.content);
|
|
181
|
+
} else if (line.type === 'addition') {
|
|
182
|
+
afterLines.push(line.content);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const beforeSource = typeof detail.drawioBeforeSource === 'string'
|
|
188
|
+
? detail.drawioBeforeSource
|
|
189
|
+
: pickDrawioSource(beforeLines);
|
|
190
|
+
const afterSource = typeof detail.drawioAfterSource === 'string'
|
|
191
|
+
? detail.drawioAfterSource
|
|
192
|
+
: pickDrawioSource(afterLines);
|
|
193
|
+
const isAdded = detail.status === 'added' || detail.status === 'untracked';
|
|
194
|
+
const isDeleted = detail.status === 'deleted';
|
|
195
|
+
|
|
196
|
+
return {
|
|
197
|
+
after: isDeleted ? '' : afterSource,
|
|
198
|
+
before: isAdded ? '' : beforeSource,
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function decodeXmlAttribute(value) {
|
|
203
|
+
return String(value ?? '')
|
|
204
|
+
.replace(/"/gu, '"')
|
|
205
|
+
.replace(/'/gu, "'")
|
|
206
|
+
.replace(/&/gu, '&')
|
|
207
|
+
.replace(/</gu, '<')
|
|
208
|
+
.replace(/>/gu, '>');
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function getDrawioCellMap(source) {
|
|
212
|
+
const cells = new Map();
|
|
213
|
+
const cellPattern = /<mxCell\b([^>]*?)(?:\/|>([\s\S]*?)<\/mxCell\s*)>/giu;
|
|
214
|
+
let match;
|
|
215
|
+
|
|
216
|
+
while ((match = cellPattern.exec(String(source ?? ''))) !== null) {
|
|
217
|
+
const idMatch = match[1].match(/\bid\s*=\s*(['"])(.*?)\1/iu);
|
|
218
|
+
const id = decodeXmlAttribute(idMatch?.[2] || '');
|
|
219
|
+
if (!id || id === '0' || id === '1') {
|
|
220
|
+
continue;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const signature = `${match[1]}${match[2] || ''}`.replace(/\s+/gu, ' ').trim();
|
|
224
|
+
cells.set(id, signature);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return cells;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function isEmptyDrawioModel(source) {
|
|
231
|
+
return /<mxGraphModel\b/iu.test(String(source ?? '')) && getDrawioCellMap(source).size === 0;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
function summarizeDrawioChange(beforeSource, afterSource) {
|
|
235
|
+
if (!beforeSource && !afterSource) {
|
|
236
|
+
return null;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const beforeCells = getDrawioCellMap(beforeSource);
|
|
240
|
+
const afterCells = getDrawioCellMap(afterSource);
|
|
241
|
+
const cellBased = beforeCells.size > 0 || afterCells.size > 0;
|
|
242
|
+
let added = 0;
|
|
243
|
+
let removed = 0;
|
|
244
|
+
let updated = 0;
|
|
245
|
+
|
|
246
|
+
if (cellBased) {
|
|
247
|
+
for (const [id, signature] of afterCells) {
|
|
248
|
+
if (!beforeCells.has(id)) {
|
|
249
|
+
added += 1;
|
|
250
|
+
} else if (beforeCells.get(id) !== signature) {
|
|
251
|
+
updated += 1;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
for (const id of beforeCells.keys()) {
|
|
256
|
+
if (!afterCells.has(id)) {
|
|
257
|
+
removed += 1;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
if (beforeSource !== afterSource && added === 0 && removed === 0 && updated === 0) {
|
|
262
|
+
updated = 1;
|
|
263
|
+
}
|
|
264
|
+
} else {
|
|
265
|
+
const hasBefore = Boolean(beforeSource);
|
|
266
|
+
const hasAfter = Boolean(afterSource);
|
|
267
|
+
added = !hasBefore && hasAfter ? 1 : 0;
|
|
268
|
+
removed = hasBefore && !hasAfter ? 1 : 0;
|
|
269
|
+
updated = hasBefore && hasAfter && beforeSource !== afterSource ? 1 : 0;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
return {
|
|
273
|
+
added,
|
|
274
|
+
afterCount: afterCells.size,
|
|
275
|
+
beforeCount: beforeCells.size,
|
|
276
|
+
cellBased,
|
|
277
|
+
removed,
|
|
278
|
+
updated,
|
|
279
|
+
};
|
|
280
|
+
}
|
|
4
281
|
|
|
5
282
|
function createSectionId(pathValue) {
|
|
6
283
|
return `diff-section-${encodeURIComponent(String(pathValue ?? '')).replace(/%/g, '_')}`;
|
|
@@ -136,6 +413,7 @@ function renderSplitRow(leftLine, rightLine) {
|
|
|
136
413
|
|
|
137
414
|
export class GitDiffViewController {
|
|
138
415
|
constructor({
|
|
416
|
+
getTheme = () => 'dark',
|
|
139
417
|
gitApiClient = null,
|
|
140
418
|
onBackToHistory = null,
|
|
141
419
|
onCommitStaged = null,
|
|
@@ -143,7 +421,9 @@ export class GitDiffViewController {
|
|
|
143
421
|
onStageFile = null,
|
|
144
422
|
onUnstageFile = null,
|
|
145
423
|
toastController = null,
|
|
424
|
+
vaultApiClient = null,
|
|
146
425
|
} = {}) {
|
|
426
|
+
this.getTheme = getTheme;
|
|
147
427
|
this.gitApiClient = gitApiClient;
|
|
148
428
|
this.onBackToHistory = onBackToHistory;
|
|
149
429
|
this.onCommitStaged = onCommitStaged;
|
|
@@ -151,6 +431,7 @@ export class GitDiffViewController {
|
|
|
151
431
|
this.onStageFile = onStageFile;
|
|
152
432
|
this.onUnstageFile = onUnstageFile;
|
|
153
433
|
this.toastController = toastController;
|
|
434
|
+
this.vaultApiClient = vaultApiClient;
|
|
154
435
|
this.page = document.getElementById('diff-page');
|
|
155
436
|
this.content = document.getElementById('diffContent');
|
|
156
437
|
this.scrollContainer = document.getElementById('diffScroll');
|
|
@@ -180,8 +461,13 @@ export class GitDiffViewController {
|
|
|
180
461
|
this.loadingFiles = new Set();
|
|
181
462
|
this.collapsedFiles = new Set();
|
|
182
463
|
this.fileLoadPromises = new Map();
|
|
464
|
+
this.excalidrawPreviewPayloads = new Map();
|
|
465
|
+
this.excalidrawPreviewCounter = 0;
|
|
466
|
+
this.drawioPreviewPayloads = new Map();
|
|
467
|
+
this.drawioPreviewCounter = 0;
|
|
183
468
|
this.requestScope = 'all';
|
|
184
469
|
this.commitHash = null;
|
|
470
|
+
this.commitBaseRef = null;
|
|
185
471
|
this.commitMeta = null;
|
|
186
472
|
this.historyFilePath = null;
|
|
187
473
|
this.pendingAction = null;
|
|
@@ -330,8 +616,11 @@ export class GitDiffViewController {
|
|
|
330
616
|
this.loadingFiles.clear();
|
|
331
617
|
this.collapsedFiles.clear();
|
|
332
618
|
this.fileLoadPromises.clear();
|
|
619
|
+
this.excalidrawPreviewPayloads.clear();
|
|
620
|
+
this.drawioPreviewPayloads.clear();
|
|
333
621
|
this.requestScope = 'all';
|
|
334
622
|
this.commitHash = null;
|
|
623
|
+
this.commitBaseRef = null;
|
|
335
624
|
this.commitMeta = null;
|
|
336
625
|
this.historyFilePath = null;
|
|
337
626
|
this.pendingAction = null;
|
|
@@ -343,6 +632,7 @@ export class GitDiffViewController {
|
|
|
343
632
|
this.source = 'workspace';
|
|
344
633
|
this.layoutMode = 'focused';
|
|
345
634
|
this.commitHash = null;
|
|
635
|
+
this.commitBaseRef = null;
|
|
346
636
|
this.commitMeta = null;
|
|
347
637
|
this.historyFilePath = null;
|
|
348
638
|
this.activeFilePath = filePath;
|
|
@@ -351,6 +641,8 @@ export class GitDiffViewController {
|
|
|
351
641
|
this.loadingFiles.clear();
|
|
352
642
|
this.collapsedFiles.clear();
|
|
353
643
|
this.fileLoadPromises.clear();
|
|
644
|
+
this.excalidrawPreviewPayloads.clear();
|
|
645
|
+
this.drawioPreviewPayloads.clear();
|
|
354
646
|
this.requestScope = scope;
|
|
355
647
|
this.renderLoading('Loading diff summary...');
|
|
356
648
|
|
|
@@ -392,6 +684,7 @@ export class GitDiffViewController {
|
|
|
392
684
|
this.source = 'commit';
|
|
393
685
|
this.layoutMode = 'stacked';
|
|
394
686
|
this.commitHash = String(hash ?? '').trim() || null;
|
|
687
|
+
this.commitBaseRef = null;
|
|
395
688
|
this.commitMeta = null;
|
|
396
689
|
this.historyFilePath = String(historyFilePath ?? '').trim() || null;
|
|
397
690
|
this.activeFilePath = path || null;
|
|
@@ -400,6 +693,8 @@ export class GitDiffViewController {
|
|
|
400
693
|
this.loadingFiles.clear();
|
|
401
694
|
this.collapsedFiles.clear();
|
|
402
695
|
this.fileLoadPromises.clear();
|
|
696
|
+
this.excalidrawPreviewPayloads.clear();
|
|
697
|
+
this.drawioPreviewPayloads.clear();
|
|
403
698
|
this.requestScope = 'all';
|
|
404
699
|
this.renderLoading('Loading commit summary...');
|
|
405
700
|
|
|
@@ -410,6 +705,7 @@ export class GitDiffViewController {
|
|
|
410
705
|
});
|
|
411
706
|
this.data = data;
|
|
412
707
|
this.commitMeta = data.commit ?? null;
|
|
708
|
+
this.commitBaseRef = data.baseRef || null;
|
|
413
709
|
const initialIndex = path
|
|
414
710
|
? Math.max(0, data.files.findIndex((file) => file.path === path))
|
|
415
711
|
: 0;
|
|
@@ -547,12 +843,408 @@ export class GitDiffViewController {
|
|
|
547
843
|
`;
|
|
548
844
|
}
|
|
549
845
|
|
|
846
|
+
getGitImageUrl(hash, filePath) {
|
|
847
|
+
if (!hash || !filePath) {
|
|
848
|
+
return '';
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
if (typeof this.gitApiClient?.getFileAttachmentUrl === 'function') {
|
|
852
|
+
return this.gitApiClient.getFileAttachmentUrl({ hash, path: filePath });
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
return resolveApiUrl(`/git/file-attachment?hash=${encodeURIComponent(hash)}&path=${encodeURIComponent(filePath)}`);
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
getCurrentImageUrl(filePath) {
|
|
859
|
+
if (!filePath) {
|
|
860
|
+
return '';
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
return resolveApiUrl(`/attachment?path=${encodeURIComponent(filePath)}`);
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
getImageDiffSources(file) {
|
|
867
|
+
const isAdded = file.status === 'added' || file.status === 'untracked';
|
|
868
|
+
const isDeleted = file.status === 'deleted';
|
|
869
|
+
const oldPath = file.oldPath || file.path;
|
|
870
|
+
const newPath = file.path;
|
|
871
|
+
|
|
872
|
+
if (this.source === 'commit') {
|
|
873
|
+
return {
|
|
874
|
+
after: isDeleted ? '' : this.getGitImageUrl(this.commitHash, newPath),
|
|
875
|
+
before: isAdded ? '' : this.getGitImageUrl(this.commitBaseRef, oldPath),
|
|
876
|
+
};
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
return {
|
|
880
|
+
after: isDeleted ? '' : this.getCurrentImageUrl(newPath),
|
|
881
|
+
before: isAdded ? '' : this.getGitImageUrl('HEAD', oldPath),
|
|
882
|
+
};
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
renderImagePane({ file, label, source, emptyLabel }) {
|
|
886
|
+
const imageMarkup = source
|
|
887
|
+
? `<img class="diff-media-image" src="${escapeHtml(source)}" alt="${escapeHtml(`${label} ${file.path}`)}" loading="lazy" decoding="async">`
|
|
888
|
+
: `<div class="diff-media-empty"><span class="diff-media-empty-icon" aria-hidden="true">—</span><span>${escapeHtml(emptyLabel)}</span></div>`;
|
|
889
|
+
|
|
890
|
+
return `
|
|
891
|
+
<div class="diff-media-pane">
|
|
892
|
+
<div class="diff-media-pane-header">
|
|
893
|
+
<span class="diff-media-pane-label">${escapeHtml(label)}</span>
|
|
894
|
+
<span class="diff-media-pane-meta">${escapeHtml(getImageFormat(file.path))}</span>
|
|
895
|
+
</div>
|
|
896
|
+
<div class="diff-media-canvas">${imageMarkup}</div>
|
|
897
|
+
</div>
|
|
898
|
+
`;
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
renderImageDiff(file) {
|
|
902
|
+
const sources = this.getImageDiffSources(file);
|
|
903
|
+
const sizeLabel = formatByteLength(file.byteLength);
|
|
904
|
+
return `
|
|
905
|
+
<section class="diff-special diff-media-diff" aria-label="Image diff">
|
|
906
|
+
<div class="diff-special-header">
|
|
907
|
+
<div>
|
|
908
|
+
<span class="diff-special-eyebrow">Binary preview</span>
|
|
909
|
+
<h2 class="diff-special-title">Image comparison</h2>
|
|
910
|
+
</div>
|
|
911
|
+
<span class="diff-special-note">${escapeHtml(getImageFormat(file.path))}${sizeLabel ? ` · ${escapeHtml(sizeLabel)}` : ''}</span>
|
|
912
|
+
</div>
|
|
913
|
+
<div class="diff-media-grid">
|
|
914
|
+
${this.renderImagePane({
|
|
915
|
+
emptyLabel: 'No previous image',
|
|
916
|
+
file,
|
|
917
|
+
label: 'Before',
|
|
918
|
+
source: sources.before,
|
|
919
|
+
})}
|
|
920
|
+
${this.renderImagePane({
|
|
921
|
+
emptyLabel: 'No image in this revision',
|
|
922
|
+
file,
|
|
923
|
+
label: 'After',
|
|
924
|
+
source: sources.after,
|
|
925
|
+
})}
|
|
926
|
+
</div>
|
|
927
|
+
<p class="diff-special-footnote">Image bytes are shown as a visual comparison instead of a text patch.</p>
|
|
928
|
+
</section>
|
|
929
|
+
`;
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
registerExcalidrawPreview(scene) {
|
|
933
|
+
if (!scene) {
|
|
934
|
+
return '';
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
this.excalidrawPreviewCounter += 1;
|
|
938
|
+
const id = `diff-excalidraw-preview-${this.excalidrawPreviewCounter.toString(36)}`;
|
|
939
|
+
this.excalidrawPreviewPayloads.set(id, scene);
|
|
940
|
+
return id;
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
renderExcalidrawPane({ label, scene, source, emptyLabel }) {
|
|
944
|
+
const previewId = this.registerExcalidrawPreview(scene);
|
|
945
|
+
const previewMarkup = scene
|
|
946
|
+
? `
|
|
947
|
+
<div class="diff-excalidraw-preview" data-diff-excalidraw-preview="${escapeHtml(previewId)}">
|
|
948
|
+
<span class="diff-excalidraw-preview-loading">Rendering diagram…</span>
|
|
949
|
+
</div>
|
|
950
|
+
`
|
|
951
|
+
: `<div class="diff-excalidraw-preview diff-excalidraw-preview--empty"><span>${escapeHtml(emptyLabel)}</span></div>`;
|
|
952
|
+
const sourceMarkup = source
|
|
953
|
+
? `
|
|
954
|
+
<details class="diff-excalidraw-source">
|
|
955
|
+
<summary>Structured scene</summary>
|
|
956
|
+
<pre>${escapeHtml(source)}</pre>
|
|
957
|
+
</details>
|
|
958
|
+
`
|
|
959
|
+
: '';
|
|
960
|
+
|
|
961
|
+
return `
|
|
962
|
+
<div class="diff-excalidraw-pane">
|
|
963
|
+
<div class="diff-excalidraw-pane-header">
|
|
964
|
+
<span class="diff-media-pane-label">${escapeHtml(label)}</span>
|
|
965
|
+
<span class="diff-media-pane-meta">${scene ? `${getVisibleSceneElements(scene).length} elements` : '—'}</span>
|
|
966
|
+
</div>
|
|
967
|
+
${previewMarkup}
|
|
968
|
+
${sourceMarkup}
|
|
969
|
+
</div>
|
|
970
|
+
`;
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
renderExcalidrawDiff(file) {
|
|
974
|
+
const scenes = getExcalidrawSceneSources(file);
|
|
975
|
+
const changeSummary = summarizeExcalidrawChange(scenes.before, scenes.after);
|
|
976
|
+
const stats = changeSummary ?? {
|
|
977
|
+
added: 0,
|
|
978
|
+
afterCount: 0,
|
|
979
|
+
beforeCount: 0,
|
|
980
|
+
removed: 0,
|
|
981
|
+
updated: 0,
|
|
982
|
+
};
|
|
983
|
+
const hasSceneData = Boolean(scenes.before || scenes.after);
|
|
984
|
+
const loadButton = file.tooLarge && file.canLoadFullPatch
|
|
985
|
+
? `<button class="${buttonClassNames({ variant: 'secondary', extra: 'diff-load-full-btn' })}" type="button" data-load-full-diff data-diff-file-path="${escapeHtml(file.path)}">Load full diff</button>`
|
|
986
|
+
: '';
|
|
987
|
+
|
|
988
|
+
return `
|
|
989
|
+
<section class="diff-special diff-excalidraw-diff" aria-label="Excalidraw diff">
|
|
990
|
+
<div class="diff-special-header">
|
|
991
|
+
<div>
|
|
992
|
+
<span class="diff-special-eyebrow">Diagram-aware preview</span>
|
|
993
|
+
<h2 class="diff-special-title">Excalidraw scene changes</h2>
|
|
994
|
+
</div>
|
|
995
|
+
<span class="diff-special-note">${hasSceneData ? `${stats.afterCount} visible elements` : 'Scene preview unavailable'}</span>
|
|
996
|
+
</div>
|
|
997
|
+
<div class="diff-scene-summary" aria-label="Scene change summary">
|
|
998
|
+
<span class="diff-scene-stat diff-scene-stat--add"><strong>+${stats.added}</strong><span>added</span></span>
|
|
999
|
+
<span class="diff-scene-stat diff-scene-stat--update"><strong>~${stats.updated}</strong><span>updated</span></span>
|
|
1000
|
+
<span class="diff-scene-stat diff-scene-stat--remove"><strong>-${stats.removed}</strong><span>removed</span></span>
|
|
1001
|
+
</div>
|
|
1002
|
+
<div class="diff-excalidraw-grid">
|
|
1003
|
+
${this.renderExcalidrawPane({
|
|
1004
|
+
emptyLabel: 'No previous scene',
|
|
1005
|
+
label: 'Before',
|
|
1006
|
+
scene: scenes.before,
|
|
1007
|
+
source: scenes.beforeSource ? JSON.stringify(scenes.before || parseExcalidrawScene(scenes.beforeSource), null, 2) : '',
|
|
1008
|
+
})}
|
|
1009
|
+
${this.renderExcalidrawPane({
|
|
1010
|
+
emptyLabel: file.tooLarge ? 'Load the full diff to preview this scene' : 'No scene in this revision',
|
|
1011
|
+
label: 'After',
|
|
1012
|
+
scene: scenes.after,
|
|
1013
|
+
source: scenes.afterSource ? JSON.stringify(scenes.after || parseExcalidrawScene(scenes.afterSource), null, 2) : '',
|
|
1014
|
+
})}
|
|
1015
|
+
</div>
|
|
1016
|
+
${loadButton}
|
|
1017
|
+
<p class="diff-special-footnote">The preview compares drawable elements by id; expand “Structured scene” when you need the underlying JSON.</p>
|
|
1018
|
+
</section>
|
|
1019
|
+
`;
|
|
1020
|
+
}
|
|
1021
|
+
|
|
1022
|
+
async readDrawioSource({ hash = null, path = null, current = false } = {}) {
|
|
1023
|
+
if (!path) {
|
|
1024
|
+
return '';
|
|
1025
|
+
}
|
|
1026
|
+
|
|
1027
|
+
try {
|
|
1028
|
+
if (current) {
|
|
1029
|
+
const response = await this.vaultApiClient?.readFile?.(path);
|
|
1030
|
+
return typeof response?.content === 'string' ? response.content : '';
|
|
1031
|
+
}
|
|
1032
|
+
|
|
1033
|
+
if (!hash || typeof this.gitApiClient?.readFileSnapshot !== 'function') {
|
|
1034
|
+
return '';
|
|
1035
|
+
}
|
|
1036
|
+
|
|
1037
|
+
const response = await this.gitApiClient.readFileSnapshot({ hash, path });
|
|
1038
|
+
return typeof response?.content === 'string' ? response.content : '';
|
|
1039
|
+
} catch {
|
|
1040
|
+
return '';
|
|
1041
|
+
}
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
async enrichDrawioDetail(detail) {
|
|
1045
|
+
const sources = getDrawioSourcePair(detail);
|
|
1046
|
+
let beforeSource = sources.before;
|
|
1047
|
+
let afterSource = sources.after;
|
|
1048
|
+
const isAdded = detail.status === 'added' || detail.status === 'untracked';
|
|
1049
|
+
const isDeleted = detail.status === 'deleted';
|
|
1050
|
+
const beforePath = detail.oldPath || detail.path;
|
|
1051
|
+
const afterPath = detail.path;
|
|
1052
|
+
|
|
1053
|
+
if (!isAdded && !hasDrawioSourceRoot(beforeSource)) {
|
|
1054
|
+
beforeSource = await this.readDrawioSource({
|
|
1055
|
+
current: false,
|
|
1056
|
+
hash: this.source === 'commit' ? this.commitBaseRef : 'HEAD',
|
|
1057
|
+
path: beforePath,
|
|
1058
|
+
});
|
|
1059
|
+
}
|
|
1060
|
+
|
|
1061
|
+
if (!isDeleted && !hasDrawioSourceRoot(afterSource)) {
|
|
1062
|
+
afterSource = await this.readDrawioSource({
|
|
1063
|
+
current: this.source !== 'commit',
|
|
1064
|
+
hash: this.source === 'commit' ? this.commitHash : null,
|
|
1065
|
+
path: afterPath,
|
|
1066
|
+
});
|
|
1067
|
+
}
|
|
1068
|
+
|
|
1069
|
+
return {
|
|
1070
|
+
...detail,
|
|
1071
|
+
drawioAfterSource: afterSource,
|
|
1072
|
+
drawioBeforeSource: beforeSource,
|
|
1073
|
+
};
|
|
1074
|
+
}
|
|
1075
|
+
|
|
1076
|
+
registerDrawioPreview(source) {
|
|
1077
|
+
if (!source) {
|
|
1078
|
+
return '';
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1081
|
+
this.drawioPreviewCounter += 1;
|
|
1082
|
+
const id = `diff-drawio-preview-${this.drawioPreviewCounter.toString(36)}`;
|
|
1083
|
+
this.drawioPreviewPayloads.set(id, source);
|
|
1084
|
+
return id;
|
|
1085
|
+
}
|
|
1086
|
+
|
|
1087
|
+
renderDrawioPane({ emptyLabel, label, source }) {
|
|
1088
|
+
const isEmptyModel = isEmptyDrawioModel(source);
|
|
1089
|
+
const previewId = isEmptyModel ? '' : this.registerDrawioPreview(source);
|
|
1090
|
+
const previewMarkup = source && !isEmptyModel
|
|
1091
|
+
? `
|
|
1092
|
+
<div class="diff-drawio-preview" data-diff-drawio-preview="${escapeHtml(previewId)}">
|
|
1093
|
+
<span class="diff-drawio-preview-loading">Rendering draw.io preview…</span>
|
|
1094
|
+
</div>
|
|
1095
|
+
`
|
|
1096
|
+
: `<div class="diff-drawio-preview diff-drawio-preview--empty"><span>${escapeHtml(isEmptyModel ? 'No drawable cells' : emptyLabel)}</span></div>`;
|
|
1097
|
+
const sourceMarkup = source
|
|
1098
|
+
? `
|
|
1099
|
+
<details class="diff-drawio-source">
|
|
1100
|
+
<summary>XML source</summary>
|
|
1101
|
+
<pre>${escapeHtml(formatDrawioSource(source))}</pre>
|
|
1102
|
+
</details>
|
|
1103
|
+
`
|
|
1104
|
+
: '';
|
|
1105
|
+
|
|
1106
|
+
return `
|
|
1107
|
+
<div class="diff-drawio-pane">
|
|
1108
|
+
<div class="diff-drawio-pane-header">
|
|
1109
|
+
<span class="diff-media-pane-label">${escapeHtml(label)}</span>
|
|
1110
|
+
<span class="diff-media-pane-meta">draw.io</span>
|
|
1111
|
+
</div>
|
|
1112
|
+
${previewMarkup}
|
|
1113
|
+
${sourceMarkup}
|
|
1114
|
+
</div>
|
|
1115
|
+
`;
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1118
|
+
renderDrawioDiff(file) {
|
|
1119
|
+
const sources = getDrawioSourcePair(file);
|
|
1120
|
+
const changeSummary = summarizeDrawioChange(sources.before, sources.after);
|
|
1121
|
+
const stats = changeSummary ?? {
|
|
1122
|
+
added: 0,
|
|
1123
|
+
afterCount: 0,
|
|
1124
|
+
beforeCount: 0,
|
|
1125
|
+
cellBased: false,
|
|
1126
|
+
removed: 0,
|
|
1127
|
+
updated: 0,
|
|
1128
|
+
};
|
|
1129
|
+
const hasSource = Boolean(sources.before || sources.after);
|
|
1130
|
+
const countLabel = !hasSource
|
|
1131
|
+
? 'Preview unavailable'
|
|
1132
|
+
: stats.cellBased
|
|
1133
|
+
? `${stats.afterCount} drawable cells`
|
|
1134
|
+
: 'Source-level comparison';
|
|
1135
|
+
const loadButton = file.tooLarge && file.canLoadFullPatch
|
|
1136
|
+
? `<button class="${buttonClassNames({ variant: 'secondary', extra: 'diff-load-full-btn' })}" type="button" data-load-full-diff data-diff-file-path="${escapeHtml(file.path)}">Load full diff</button>`
|
|
1137
|
+
: '';
|
|
1138
|
+
|
|
1139
|
+
return `
|
|
1140
|
+
<section class="diff-special diff-drawio-diff" aria-label="draw.io diff">
|
|
1141
|
+
<div class="diff-special-header">
|
|
1142
|
+
<div>
|
|
1143
|
+
<span class="diff-special-eyebrow">Diagram-aware preview</span>
|
|
1144
|
+
<h2 class="diff-special-title">draw.io diagram changes</h2>
|
|
1145
|
+
</div>
|
|
1146
|
+
<span class="diff-special-note">${escapeHtml(countLabel)}</span>
|
|
1147
|
+
</div>
|
|
1148
|
+
<div class="diff-scene-summary" aria-label="draw.io change summary">
|
|
1149
|
+
<span class="diff-scene-stat diff-scene-stat--add"><strong>+${stats.added}</strong><span>added</span></span>
|
|
1150
|
+
<span class="diff-scene-stat diff-scene-stat--update"><strong>~${stats.updated}</strong><span>updated</span></span>
|
|
1151
|
+
<span class="diff-scene-stat diff-scene-stat--remove"><strong>-${stats.removed}</strong><span>removed</span></span>
|
|
1152
|
+
</div>
|
|
1153
|
+
<div class="diff-drawio-grid">
|
|
1154
|
+
${this.renderDrawioPane({
|
|
1155
|
+
emptyLabel: 'No previous diagram',
|
|
1156
|
+
label: 'Before',
|
|
1157
|
+
source: sources.before,
|
|
1158
|
+
})}
|
|
1159
|
+
${this.renderDrawioPane({
|
|
1160
|
+
emptyLabel: file.tooLarge ? 'Load the full diff to preview this diagram' : 'No diagram in this revision',
|
|
1161
|
+
label: 'After',
|
|
1162
|
+
source: sources.after,
|
|
1163
|
+
})}
|
|
1164
|
+
</div>
|
|
1165
|
+
${loadButton}
|
|
1166
|
+
<p class="diff-special-footnote">Both revisions use the draw.io viewer; expand “XML source” when you need the underlying document.</p>
|
|
1167
|
+
</section>
|
|
1168
|
+
`;
|
|
1169
|
+
}
|
|
1170
|
+
|
|
1171
|
+
async hydrateExcalidrawPreviews() {
|
|
1172
|
+
if (this.excalidrawPreviewPayloads.size === 0 || !this.content) {
|
|
1173
|
+
return;
|
|
1174
|
+
}
|
|
1175
|
+
|
|
1176
|
+
let exportToSvg;
|
|
1177
|
+
try {
|
|
1178
|
+
({ exportToSvg } = await import('@excalidraw/excalidraw'));
|
|
1179
|
+
} catch (error) {
|
|
1180
|
+
console.warn('[git-diff] Excalidraw preview renderer unavailable:', error);
|
|
1181
|
+
return;
|
|
1182
|
+
}
|
|
1183
|
+
|
|
1184
|
+
for (const [previewId, scene] of this.excalidrawPreviewPayloads) {
|
|
1185
|
+
const preview = this.content.querySelector?.(`[data-diff-excalidraw-preview="${previewId}"]`);
|
|
1186
|
+
if (!preview) {
|
|
1187
|
+
continue;
|
|
1188
|
+
}
|
|
1189
|
+
|
|
1190
|
+
try {
|
|
1191
|
+
const svg = await exportToSvg({
|
|
1192
|
+
appState: {
|
|
1193
|
+
exportBackground: true,
|
|
1194
|
+
exportWithDarkMode: false,
|
|
1195
|
+
viewBackgroundColor: scene.appState?.viewBackgroundColor || '#ffffff',
|
|
1196
|
+
},
|
|
1197
|
+
elements: getVisibleSceneElements(scene),
|
|
1198
|
+
exportPadding: 24,
|
|
1199
|
+
files: scene.files || null,
|
|
1200
|
+
});
|
|
1201
|
+
preview.replaceChildren(svg);
|
|
1202
|
+
preview.classList.add('is-ready');
|
|
1203
|
+
} catch (error) {
|
|
1204
|
+
console.warn('[git-diff] Failed to render Excalidraw scene preview:', error);
|
|
1205
|
+
preview.textContent = 'Visual preview unavailable';
|
|
1206
|
+
preview.classList.add('is-error');
|
|
1207
|
+
}
|
|
1208
|
+
}
|
|
1209
|
+
}
|
|
1210
|
+
|
|
1211
|
+
async hydrateDrawioPreviews() {
|
|
1212
|
+
if (this.drawioPreviewPayloads.size === 0 || !this.content) {
|
|
1213
|
+
return;
|
|
1214
|
+
}
|
|
1215
|
+
|
|
1216
|
+
for (const [previewId, source] of this.drawioPreviewPayloads) {
|
|
1217
|
+
const preview = this.content.querySelector?.(`[data-diff-drawio-preview="${previewId}"]`);
|
|
1218
|
+
if (!preview) {
|
|
1219
|
+
continue;
|
|
1220
|
+
}
|
|
1221
|
+
|
|
1222
|
+
try {
|
|
1223
|
+
await renderDrawioViewer(preview, {
|
|
1224
|
+
ariaLabel: 'draw.io diagram diff preview',
|
|
1225
|
+
className: 'mxgraph drawio-viewer-frame diff-drawio-viewer-frame',
|
|
1226
|
+
source,
|
|
1227
|
+
theme: this.getTheme?.() === 'light' ? 'light' : 'dark',
|
|
1228
|
+
});
|
|
1229
|
+
preview.classList.add('is-ready');
|
|
1230
|
+
} catch (error) {
|
|
1231
|
+
console.warn('[git-diff] Failed to render draw.io diagram preview:', error);
|
|
1232
|
+
preview.textContent = 'Visual preview unavailable';
|
|
1233
|
+
preview.classList.add('is-error');
|
|
1234
|
+
}
|
|
1235
|
+
}
|
|
1236
|
+
}
|
|
1237
|
+
|
|
550
1238
|
renderFileHeader(file) {
|
|
1239
|
+
const fileKind = getDiffFileKind(file);
|
|
1240
|
+
const headerStats = fileKind === 'image'
|
|
1241
|
+
? `<span class="diff-file-header-kind">${escapeHtml(getImageFormat(file.path))} image</span>`
|
|
1242
|
+
: `<span class="diff-file-header-stats"><span class="ui-stat-token ui-stat-token--add diff-stats-add">+${file.stats?.additions ?? 0}</span><span class="ui-stat-token ui-stat-token--del diff-stats-del">-${file.stats?.deletions ?? 0}</span></span>`;
|
|
551
1243
|
return `
|
|
552
1244
|
<div class="diff-file-header">
|
|
553
1245
|
<span class="diff-file-path">${escapeHtml(file.path)}</span>
|
|
554
1246
|
<span class="ui-status-badge ${badgeClass(file.status)}">${escapeHtml(file.status)}</span>
|
|
555
|
-
|
|
1247
|
+
${headerStats}
|
|
556
1248
|
</div>
|
|
557
1249
|
`;
|
|
558
1250
|
}
|
|
@@ -602,6 +1294,19 @@ export class GitDiffViewController {
|
|
|
602
1294
|
return '<div class="diff-empty-state">Select a file to load its diff.</div>';
|
|
603
1295
|
}
|
|
604
1296
|
|
|
1297
|
+
const fileKind = getDiffFileKind(detail);
|
|
1298
|
+
if (fileKind === 'image') {
|
|
1299
|
+
return `${includeHeader ? this.renderFileHeader(detail) : ''}${this.renderImageDiff(detail)}`;
|
|
1300
|
+
}
|
|
1301
|
+
|
|
1302
|
+
if (fileKind === 'excalidraw') {
|
|
1303
|
+
return `${includeHeader ? this.renderFileHeader(detail) : ''}${this.renderExcalidrawDiff(detail)}`;
|
|
1304
|
+
}
|
|
1305
|
+
|
|
1306
|
+
if (fileKind === 'drawio') {
|
|
1307
|
+
return `${includeHeader ? this.renderFileHeader(detail) : ''}${this.renderDrawioDiff(detail)}`;
|
|
1308
|
+
}
|
|
1309
|
+
|
|
605
1310
|
if (detail.tooLarge) {
|
|
606
1311
|
return `
|
|
607
1312
|
${includeHeader ? this.renderFileHeader(detail) : ''}
|
|
@@ -1095,6 +1800,10 @@ export class GitDiffViewController {
|
|
|
1095
1800
|
};
|
|
1096
1801
|
}
|
|
1097
1802
|
|
|
1803
|
+
if (getDiffFileKind(detail) === 'drawio') {
|
|
1804
|
+
detail = await this.enrichDrawioDetail(detail);
|
|
1805
|
+
}
|
|
1806
|
+
|
|
1098
1807
|
if (cacheKey) {
|
|
1099
1808
|
this.fileCache.set(cacheKey, detail);
|
|
1100
1809
|
}
|
|
@@ -1134,8 +1843,22 @@ export class GitDiffViewController {
|
|
|
1134
1843
|
}
|
|
1135
1844
|
|
|
1136
1845
|
if (this.stats) {
|
|
1137
|
-
const
|
|
1138
|
-
|
|
1846
|
+
const imageLineStats = (this.data?.files ?? [])
|
|
1847
|
+
.filter((file) => getDiffFileKind(file) === 'image')
|
|
1848
|
+
.reduce((summary, file) => ({
|
|
1849
|
+
additions: summary.additions + Number(file.stats?.additions || 0),
|
|
1850
|
+
deletions: summary.deletions + Number(file.stats?.deletions || 0),
|
|
1851
|
+
}), { additions: 0, deletions: 0 });
|
|
1852
|
+
const files = this.data?.files ?? [];
|
|
1853
|
+
const allFilesAreImages = files.length > 0 && files.every((file) => getDiffFileKind(file) === 'image');
|
|
1854
|
+
const rawAdditions = Number(this.data?.summary?.additions ?? 0);
|
|
1855
|
+
const rawDeletions = Number(this.data?.summary?.deletions ?? 0);
|
|
1856
|
+
const additions = allFilesAreImages
|
|
1857
|
+
? 0
|
|
1858
|
+
: Math.max(0, rawAdditions - imageLineStats.additions);
|
|
1859
|
+
const deletions = allFilesAreImages
|
|
1860
|
+
? 0
|
|
1861
|
+
: Math.max(0, rawDeletions - imageLineStats.deletions);
|
|
1139
1862
|
this.stats.innerHTML = `
|
|
1140
1863
|
<span class="ui-stat-token ui-stat-token--add diff-stats-add">+${additions}</span>
|
|
1141
1864
|
<span class="ui-stat-token ui-stat-token--del diff-stats-del">-${deletions}</span>
|
|
@@ -1207,12 +1930,17 @@ export class GitDiffViewController {
|
|
|
1207
1930
|
return;
|
|
1208
1931
|
}
|
|
1209
1932
|
|
|
1933
|
+
this.excalidrawPreviewPayloads.clear();
|
|
1934
|
+
this.drawioPreviewPayloads.clear();
|
|
1935
|
+
|
|
1210
1936
|
if (this.source === 'commit') {
|
|
1211
1937
|
this.content.innerHTML = `${this.renderCommitHeader()}${this.renderCommitBody()}`;
|
|
1212
1938
|
} else {
|
|
1213
1939
|
this.content.innerHTML = this.renderFocusedFileBody();
|
|
1214
1940
|
}
|
|
1215
1941
|
this.syncToolbar();
|
|
1942
|
+
void this.hydrateExcalidrawPreviews();
|
|
1943
|
+
void this.hydrateDrawioPreviews();
|
|
1216
1944
|
}
|
|
1217
1945
|
|
|
1218
1946
|
getToolbarTitle({ commitHash = null, filePath = null, path = null, scope = 'all', source = 'workspace' } = {}) {
|