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
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
createReferenceTargetAliasMap,
|
|
13
13
|
} from '../markdown-reference-extractor.js';
|
|
14
14
|
import { mapWithConcurrency } from '../../shared/async-utils.js';
|
|
15
|
+
import { isPlainObject } from './base-definition.js';
|
|
15
16
|
import { createLinkValue, dedupeLinkValues } from './base-expression-runtime.js';
|
|
16
17
|
|
|
17
18
|
const INLINE_TAG_RE = /(^|[\s(])#([A-Za-z0-9/_-]+)/g;
|
|
@@ -39,10 +40,6 @@ function createRowCacheKey(filePath, workspaceState, fileSetSignature) {
|
|
|
39
40
|
].join('\0');
|
|
40
41
|
}
|
|
41
42
|
|
|
42
|
-
function isPlainObject(value) {
|
|
43
|
-
return Object.prototype.toString.call(value) === '[object Object]';
|
|
44
|
-
}
|
|
45
|
-
|
|
46
43
|
function normalizeTags(value) {
|
|
47
44
|
if (Array.isArray(value)) {
|
|
48
45
|
return value
|
|
@@ -2,15 +2,12 @@ import { dump as dumpYaml } from 'js-yaml';
|
|
|
2
2
|
|
|
3
3
|
import {
|
|
4
4
|
findView,
|
|
5
|
+
isPlainObject,
|
|
5
6
|
normalizeBaseDefinition,
|
|
6
7
|
normalizeRawDefinitionForWrite,
|
|
7
8
|
parseBaseSource,
|
|
8
9
|
} from './base-definition.js';
|
|
9
10
|
|
|
10
|
-
function isPlainObject(value) {
|
|
11
|
-
return Object.prototype.toString.call(value) === '[object Object]';
|
|
12
|
-
}
|
|
13
|
-
|
|
14
11
|
function cloneRawSource(source = '') {
|
|
15
12
|
const parsed = parseBaseSource(source);
|
|
16
13
|
return isPlainObject(parsed) ? parsed : {};
|
|
@@ -6,9 +6,9 @@ import * as decoding from 'lib0/decoding';
|
|
|
6
6
|
|
|
7
7
|
import { MSG_AWARENESS, MSG_SYNC } from './protocol.js';
|
|
8
8
|
import { CollaborationDocumentStore } from './collaboration-document-store.js';
|
|
9
|
-
import { RoomPersistenceController } from './room-persistence-controller.js';
|
|
10
9
|
import { logPerfEvent } from '../../config/perf-logging.js';
|
|
11
10
|
import { populateCommentThreads, serializeCommentThreads } from '../../../domain/comment-threads.js';
|
|
11
|
+
import { getVaultFileKind } from '../../../domain/file-kind.js';
|
|
12
12
|
import {
|
|
13
13
|
EXCALIDRAW_APP_STATE_KEY,
|
|
14
14
|
EXCALIDRAW_ELEMENTS_KEY,
|
|
@@ -123,7 +123,7 @@ function readAwarenessEntries(update) {
|
|
|
123
123
|
}
|
|
124
124
|
|
|
125
125
|
function isExcalidrawRoom(name) {
|
|
126
|
-
return
|
|
126
|
+
return getVaultFileKind(name) === 'excalidraw';
|
|
127
127
|
}
|
|
128
128
|
|
|
129
129
|
function isWorkspaceRoom(name) {
|
|
@@ -214,15 +214,10 @@ export class CollaborationRoom {
|
|
|
214
214
|
lastHydrate: null,
|
|
215
215
|
lastInitialSyncAt: 0,
|
|
216
216
|
};
|
|
217
|
-
this.
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
this.doc.destroy();
|
|
222
|
-
this.onEmpty?.(this.name);
|
|
223
|
-
},
|
|
224
|
-
onPersist: () => this.persist(),
|
|
225
|
-
});
|
|
217
|
+
this.persistTimer = null;
|
|
218
|
+
this.destroyTimer = null;
|
|
219
|
+
this.finalizePromise = null;
|
|
220
|
+
this.shutdownGeneration = 0;
|
|
226
221
|
|
|
227
222
|
this.awareness.setLocalState(null);
|
|
228
223
|
this.registerDocListeners();
|
|
@@ -475,11 +470,12 @@ export class CollaborationRoom {
|
|
|
475
470
|
return;
|
|
476
471
|
}
|
|
477
472
|
|
|
478
|
-
this.
|
|
473
|
+
clearTimeout(this.persistTimer);
|
|
474
|
+
this.persistTimer = setTimeout(() => {
|
|
479
475
|
this.persist().catch((error) => {
|
|
480
476
|
console.error(`[room:${this.name}] Failed to persist document:`, error.message);
|
|
481
477
|
});
|
|
482
|
-
});
|
|
478
|
+
}, 500);
|
|
483
479
|
}
|
|
484
480
|
|
|
485
481
|
async persist() {
|
|
@@ -620,9 +616,17 @@ export class CollaborationRoom {
|
|
|
620
616
|
this.documentStore?.rename(nextName);
|
|
621
617
|
}
|
|
622
618
|
|
|
619
|
+
cancelPersistenceTimers() {
|
|
620
|
+
this.shutdownGeneration += 1;
|
|
621
|
+
clearTimeout(this.persistTimer);
|
|
622
|
+
clearTimeout(this.destroyTimer);
|
|
623
|
+
this.persistTimer = null;
|
|
624
|
+
this.destroyTimer = null;
|
|
625
|
+
}
|
|
626
|
+
|
|
623
627
|
markDeleted() {
|
|
624
628
|
this.deleted = true;
|
|
625
|
-
this.
|
|
629
|
+
this.cancelPersistenceTimers();
|
|
626
630
|
if (this.clients.size === 0) {
|
|
627
631
|
this.finalizeIfIdle();
|
|
628
632
|
}
|
|
@@ -669,7 +673,9 @@ export class CollaborationRoom {
|
|
|
669
673
|
}
|
|
670
674
|
|
|
671
675
|
async addClient(ws, { sendInitialSync: shouldSendInitialSync = true } = {}) {
|
|
672
|
-
this.
|
|
676
|
+
this.shutdownGeneration += 1;
|
|
677
|
+
clearTimeout(this.destroyTimer);
|
|
678
|
+
this.destroyTimer = null;
|
|
673
679
|
await this.hydrate();
|
|
674
680
|
|
|
675
681
|
this.ensureClientState(ws);
|
|
@@ -721,9 +727,9 @@ export class CollaborationRoom {
|
|
|
721
727
|
}
|
|
722
728
|
|
|
723
729
|
this.destroyed = true;
|
|
724
|
-
this.
|
|
730
|
+
this.cancelPersistenceTimers();
|
|
725
731
|
await Promise.allSettled([
|
|
726
|
-
this.
|
|
732
|
+
this.finalizePromise,
|
|
727
733
|
this.activePersistPromise,
|
|
728
734
|
]);
|
|
729
735
|
|
|
@@ -745,12 +751,40 @@ export class CollaborationRoom {
|
|
|
745
751
|
}
|
|
746
752
|
|
|
747
753
|
finalizeIfIdle() {
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
754
|
+
if (this.clients.size > 0 || this.finalizePromise) {
|
|
755
|
+
return;
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
const generation = ++this.shutdownGeneration;
|
|
759
|
+
this.finalizePromise = (async () => {
|
|
760
|
+
clearTimeout(this.persistTimer);
|
|
761
|
+
this.persistTimer = null;
|
|
762
|
+
|
|
763
|
+
try {
|
|
764
|
+
await this.persist();
|
|
765
|
+
} catch (error) {
|
|
751
766
|
console.error(`[room:${this.name}] Failed to persist final room state:`, error.message);
|
|
752
|
-
}
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
if (this.clients.size > 0 || generation !== this.shutdownGeneration) {
|
|
770
|
+
return;
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
this.destroyTimer = setTimeout(() => {
|
|
774
|
+
if (this.clients.size > 0 || generation !== this.shutdownGeneration) {
|
|
775
|
+
return;
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
this.awareness.destroy();
|
|
779
|
+
this.doc.destroy();
|
|
780
|
+
this.onEmpty?.(this.name);
|
|
781
|
+
}, this.idleGraceMs);
|
|
782
|
+
this.destroyTimer.unref?.();
|
|
783
|
+
})().finally(() => {
|
|
784
|
+
this.finalizePromise = null;
|
|
753
785
|
});
|
|
786
|
+
|
|
787
|
+
return this.finalizePromise;
|
|
754
788
|
}
|
|
755
789
|
|
|
756
790
|
clearEphemeralExcalidrawHistory() {
|
|
@@ -39,10 +39,16 @@ function createThreadSummary(thread) {
|
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
const id = asString(thread.id);
|
|
42
|
+
const anchorKind = asString(thread.anchorKind) || 'line';
|
|
43
|
+
const isDiagramElementAnchor = anchorKind === 'diagram-element';
|
|
42
44
|
const anchorStartLine = asFiniteNumber(thread.anchorStartLine);
|
|
43
45
|
const anchorEndLine = asFiniteNumber(thread.anchorEndLine);
|
|
44
46
|
const messages = asArray(thread.messages);
|
|
45
|
-
if (
|
|
47
|
+
if (
|
|
48
|
+
!id
|
|
49
|
+
|| messages.length === 0
|
|
50
|
+
|| (!isDiagramElementAnchor && (anchorStartLine === null || anchorEndLine === null))
|
|
51
|
+
) {
|
|
46
52
|
return null;
|
|
47
53
|
}
|
|
48
54
|
|
|
@@ -53,10 +59,14 @@ function createThreadSummary(thread) {
|
|
|
53
59
|
|
|
54
60
|
return {
|
|
55
61
|
anchor: {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
62
|
+
...(isDiagramElementAnchor
|
|
63
|
+
? {}
|
|
64
|
+
: {
|
|
65
|
+
endLine: Math.max(anchorEndLine, anchorStartLine),
|
|
66
|
+
startLine: Math.max(anchorStartLine, 1),
|
|
67
|
+
}),
|
|
68
|
+
kind: anchorKind,
|
|
69
|
+
quote: asString(thread.anchorQuote) || (isDiagramElementAnchor ? 'Diagram element' : ''),
|
|
60
70
|
},
|
|
61
71
|
createdAt: asFiniteNumber(thread.createdAt) ?? latestActivityAt,
|
|
62
72
|
createdByColor: asString(thread.createdByColor),
|
|
@@ -22,7 +22,9 @@ const TEXT_SEARCH_GLOBS = Object.freeze([
|
|
|
22
22
|
'*.mermaid',
|
|
23
23
|
'*.puml',
|
|
24
24
|
'*.plantuml',
|
|
25
|
+
'*.dsl',
|
|
25
26
|
'*.drawio',
|
|
27
|
+
'*.excalidraw',
|
|
26
28
|
]);
|
|
27
29
|
|
|
28
30
|
function normalizePositiveInt(value, fallback, { min = 1, max = Number.MAX_SAFE_INTEGER } = {}) {
|
|
@@ -278,6 +280,7 @@ export class RipgrepSearchService {
|
|
|
278
280
|
async search({
|
|
279
281
|
limit = DEFAULT_MAX_FILES,
|
|
280
282
|
query = '',
|
|
283
|
+
signal = null,
|
|
281
284
|
} = {}) {
|
|
282
285
|
const normalizedQuery = String(query ?? '').trim();
|
|
283
286
|
const maxFiles = normalizePositiveInt(limit, this.maxFiles, {
|
|
@@ -310,6 +313,7 @@ export class RipgrepSearchService {
|
|
|
310
313
|
cwd: this.vaultDir,
|
|
311
314
|
encoding: 'utf8',
|
|
312
315
|
maxBuffer: this.maxBufferBytes,
|
|
316
|
+
...(signal ? { signal } : {}),
|
|
313
317
|
timeout: this.timeoutMs,
|
|
314
318
|
});
|
|
315
319
|
const parsed = parseRipgrepJson(result.stdout, {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { countPatchLines, parseNumstatOutput, parseUnifiedDiff } from './parsers.js';
|
|
2
2
|
import { createEmptyStats } from './responses.js';
|
|
3
|
+
import { getVaultFileKind } from '../../../domain/file-kind.js';
|
|
3
4
|
|
|
4
5
|
function createSectionMap(sections = []) {
|
|
5
6
|
return new Map(sections.map((section) => [section.key, section]));
|
|
@@ -33,6 +34,7 @@ function mergeScopedFile(existingFile, nextFile, scope) {
|
|
|
33
34
|
return {
|
|
34
35
|
...nextFile,
|
|
35
36
|
...nextFlags,
|
|
37
|
+
fileKind: nextFile.fileKind || getVaultFileKind(nextFile.path),
|
|
36
38
|
scope,
|
|
37
39
|
stats: createEmptyStats(),
|
|
38
40
|
};
|
|
@@ -44,6 +46,7 @@ function mergeScopedFile(existingFile, nextFile, scope) {
|
|
|
44
46
|
return {
|
|
45
47
|
...existingFile,
|
|
46
48
|
...nextFile,
|
|
49
|
+
fileKind: existingFile.fileKind || getVaultFileKind(nextFile.path),
|
|
47
50
|
hasStagedChanges: existingFile.hasStagedChanges || nextFlags.hasStagedChanges,
|
|
48
51
|
hasUntrackedChanges: existingFile.hasUntrackedChanges || nextFlags.hasUntrackedChanges,
|
|
49
52
|
hasWorkingTreeChanges: existingFile.hasWorkingTreeChanges || nextFlags.hasWorkingTreeChanges,
|
|
@@ -54,6 +57,7 @@ function mergeScopedFile(existingFile, nextFile, scope) {
|
|
|
54
57
|
|
|
55
58
|
return {
|
|
56
59
|
...existingFile,
|
|
60
|
+
fileKind: existingFile.fileKind || getVaultFileKind(nextFile.path),
|
|
57
61
|
hasStagedChanges: existingFile.hasStagedChanges || nextFlags.hasStagedChanges,
|
|
58
62
|
hasUntrackedChanges: existingFile.hasUntrackedChanges || nextFlags.hasUntrackedChanges,
|
|
59
63
|
hasWorkingTreeChanges: existingFile.hasWorkingTreeChanges || nextFlags.hasWorkingTreeChanges,
|
|
@@ -372,6 +376,7 @@ export class GitDiffService {
|
|
|
372
376
|
return {
|
|
373
377
|
...file,
|
|
374
378
|
...detail,
|
|
379
|
+
fileKind: file.fileKind || detail.fileKind || getVaultFileKind(file.path),
|
|
375
380
|
canLoadFullPatch: false,
|
|
376
381
|
patchLineCount,
|
|
377
382
|
tooLarge: false,
|
|
@@ -70,6 +70,23 @@ export class GitService {
|
|
|
70
70
|
|
|
71
71
|
return String(result.stdout ?? '');
|
|
72
72
|
},
|
|
73
|
+
async execGitBuffer(args, { env = null } = {}) {
|
|
74
|
+
const result = await gitExecFile('git', ['-c', 'core.quotepath=false', ...args], {
|
|
75
|
+
cwd: vaultDir,
|
|
76
|
+
encoding: null,
|
|
77
|
+
env: {
|
|
78
|
+
...process.env,
|
|
79
|
+
...(commandEnv ?? {}),
|
|
80
|
+
...(env ?? {}),
|
|
81
|
+
},
|
|
82
|
+
maxBuffer: 5 * 1024 * 1024,
|
|
83
|
+
timeout: 10_000,
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
return Buffer.isBuffer(result.stdout)
|
|
87
|
+
? result.stdout
|
|
88
|
+
: Buffer.from(result.stdout ?? '');
|
|
89
|
+
},
|
|
73
90
|
};
|
|
74
91
|
this.untrackedFileService = new GitUntrackedFileService({ vaultDir });
|
|
75
92
|
this.statusService = new GitStatusService({
|
|
@@ -186,7 +203,7 @@ export class GitService {
|
|
|
186
203
|
};
|
|
187
204
|
}
|
|
188
205
|
|
|
189
|
-
async pullBranch() {
|
|
206
|
+
async pullBranch({ author = null } = {}) {
|
|
190
207
|
const status = await this.getStatus({ force: true });
|
|
191
208
|
if (status.branch?.detached) {
|
|
192
209
|
throw createGitRequestError(409, 'Cannot pull from a detached HEAD');
|
|
@@ -203,16 +220,22 @@ export class GitService {
|
|
|
203
220
|
throw createGitRequestError(409, 'Unable to resolve upstream branch for pull');
|
|
204
221
|
}
|
|
205
222
|
|
|
206
|
-
|
|
223
|
+
const mergeBaseRef = beforeRef
|
|
224
|
+
? await this.resolveMergeBase(beforeRef, targetRef)
|
|
225
|
+
: null;
|
|
226
|
+
if (beforeRef && !mergeBaseRef) {
|
|
227
|
+
throw createGitRequestError(409, 'Cannot pull because local and remote histories are unrelated');
|
|
228
|
+
}
|
|
229
|
+
if (beforeRef && !(await this.canMergeWith(targetRef))) {
|
|
207
230
|
throw createGitRequestError(
|
|
208
231
|
409,
|
|
209
|
-
'Cannot pull because local and remote commits
|
|
210
|
-
{ requestCode: '
|
|
232
|
+
'Cannot pull automatically because local and remote commits conflict.',
|
|
233
|
+
{ requestCode: 'pull_conflicted_commits' },
|
|
211
234
|
);
|
|
212
235
|
}
|
|
213
236
|
|
|
214
237
|
const dirtyEntries = this.collectDirtyEntries(status);
|
|
215
|
-
const upstreamWorkspaceChange = await this.createWorkspaceChangeFromRefs(
|
|
238
|
+
const upstreamWorkspaceChange = await this.createWorkspaceChangeFromRefs(mergeBaseRef, targetRef);
|
|
216
239
|
const overlappingEntries = this.findOverlappingDirtyEntries({
|
|
217
240
|
dirtyEntries,
|
|
218
241
|
workspaceChange: upstreamWorkspaceChange,
|
|
@@ -230,7 +253,9 @@ export class GitService {
|
|
|
230
253
|
|
|
231
254
|
let output;
|
|
232
255
|
try {
|
|
233
|
-
output = await this.commandRunner.execGit(['
|
|
256
|
+
output = await this.commandRunner.execGit(['merge', '--no-edit', '--autostash', targetRef], {
|
|
257
|
+
env: buildAuthorEnv(author),
|
|
258
|
+
});
|
|
234
259
|
} catch (error) {
|
|
235
260
|
throw await this.classifyPullError(error);
|
|
236
261
|
}
|
|
@@ -293,6 +318,10 @@ export class GitService {
|
|
|
293
318
|
return this.historyService.getFileSnapshot({ hash, path });
|
|
294
319
|
}
|
|
295
320
|
|
|
321
|
+
async getFileAttachment({ hash, path } = {}) {
|
|
322
|
+
return this.historyService.getFileAttachment({ hash, path });
|
|
323
|
+
}
|
|
324
|
+
|
|
296
325
|
async pathExistsAtRef(ref, path) {
|
|
297
326
|
try {
|
|
298
327
|
await this.commandRunner.execGit(['cat-file', '-e', `${ref}:${path}`]);
|
|
@@ -318,14 +347,18 @@ export class GitService {
|
|
|
318
347
|
: ['fetch', '--prune']);
|
|
319
348
|
}
|
|
320
349
|
|
|
321
|
-
async
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
return
|
|
350
|
+
async resolveMergeBase(leftRef, rightRef) {
|
|
351
|
+
try {
|
|
352
|
+
const output = await this.commandRunner.execGit(['merge-base', leftRef, rightRef]);
|
|
353
|
+
return output.trim() || null;
|
|
354
|
+
} catch {
|
|
355
|
+
return null;
|
|
325
356
|
}
|
|
357
|
+
}
|
|
326
358
|
|
|
359
|
+
async canMergeWith(targetRef) {
|
|
327
360
|
try {
|
|
328
|
-
await this.commandRunner.execGit(['merge-
|
|
361
|
+
await this.commandRunner.execGit(['merge-tree', '--write-tree', '--quiet', 'HEAD', targetRef]);
|
|
329
362
|
return true;
|
|
330
363
|
} catch (error) {
|
|
331
364
|
if (error?.code === 1) {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createRequestError as createGitRequestError } from '../http/http-errors.js';
|
|
2
2
|
import { getVaultFileKind, isImageAttachmentFilePath } from '../../../domain/file-kind.js';
|
|
3
3
|
import { normalizeRelativeGitPath } from './path-utils.js';
|
|
4
|
+
import { getImageMimeType } from './image-mime.js';
|
|
4
5
|
import {
|
|
5
6
|
countPatchLines,
|
|
6
7
|
parseGitLogHeader,
|
|
@@ -308,6 +309,7 @@ export class GitHistoryService {
|
|
|
308
309
|
|
|
309
310
|
if (metaOnly) {
|
|
310
311
|
const metaResponse = {
|
|
312
|
+
baseRef: meta.baseRef,
|
|
311
313
|
commit: meta.commit,
|
|
312
314
|
files: meta.files,
|
|
313
315
|
isGitRepo: true,
|
|
@@ -335,6 +337,7 @@ export class GitHistoryService {
|
|
|
335
337
|
&& (fileSummary.additions + fileSummary.deletions) > this.maxInitialPatchLines
|
|
336
338
|
) {
|
|
337
339
|
const guardedResponse = {
|
|
340
|
+
baseRef: meta.baseRef,
|
|
338
341
|
commit: meta.commit,
|
|
339
342
|
files: [{
|
|
340
343
|
...baseFile,
|
|
@@ -396,6 +399,7 @@ export class GitHistoryService {
|
|
|
396
399
|
};
|
|
397
400
|
|
|
398
401
|
const response = {
|
|
402
|
+
baseRef: meta.baseRef,
|
|
399
403
|
commit: meta.commit,
|
|
400
404
|
files: [detailedFile],
|
|
401
405
|
isGitRepo: true,
|
|
@@ -460,6 +464,46 @@ export class GitHistoryService {
|
|
|
460
464
|
});
|
|
461
465
|
}
|
|
462
466
|
|
|
467
|
+
async getFileAttachment({ hash, path } = {}) {
|
|
468
|
+
const isGitRepo = await this.commandRunner.isGitRepo();
|
|
469
|
+
const normalizedHash = this.normalizeAttachmentRef(hash);
|
|
470
|
+
const normalizedPath = normalizeRelativeGitPath(path);
|
|
471
|
+
|
|
472
|
+
if (!isGitRepo) {
|
|
473
|
+
throw createGitRequestError(404, 'Git repository not found');
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
if (!isImageAttachmentFilePath(normalizedPath)) {
|
|
477
|
+
throw createGitRequestError(400, 'Unsupported image attachment path');
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
const cacheKey = JSON.stringify({
|
|
481
|
+
hash: normalizedHash,
|
|
482
|
+
path: normalizedPath,
|
|
483
|
+
type: 'file-attachment',
|
|
484
|
+
});
|
|
485
|
+
const cached = getCachedValue(this.commitCache, cacheKey);
|
|
486
|
+
if (cached) {
|
|
487
|
+
return cached;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
return this.runRequest(cacheKey, async () => {
|
|
491
|
+
let content;
|
|
492
|
+
try {
|
|
493
|
+
content = await this.commandRunner.execGitBuffer(['cat-file', 'blob', `${normalizedHash}:${normalizedPath}`]);
|
|
494
|
+
} catch {
|
|
495
|
+
throw createGitRequestError(404, 'Commit image not found');
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
return setCachedValue(this.commitCache, cacheKey, {
|
|
499
|
+
content,
|
|
500
|
+
hash: normalizedHash,
|
|
501
|
+
mimeType: getImageMimeType(normalizedPath),
|
|
502
|
+
path: normalizedPath,
|
|
503
|
+
}, this.responseCacheTtlMs);
|
|
504
|
+
});
|
|
505
|
+
}
|
|
506
|
+
|
|
463
507
|
async runRequest(key, callback) {
|
|
464
508
|
if (this.pendingRequests.has(key)) {
|
|
465
509
|
return this.pendingRequests.get(key);
|
|
@@ -494,6 +538,14 @@ export class GitHistoryService {
|
|
|
494
538
|
return normalizedHash;
|
|
495
539
|
}
|
|
496
540
|
|
|
541
|
+
normalizeAttachmentRef(hash) {
|
|
542
|
+
const normalizedHash = String(hash ?? '').trim();
|
|
543
|
+
if (normalizedHash === 'HEAD') {
|
|
544
|
+
return normalizedHash;
|
|
545
|
+
}
|
|
546
|
+
return this.normalizeCommitHash(normalizedHash);
|
|
547
|
+
}
|
|
548
|
+
|
|
497
549
|
parseHistoryChunk(chunk) {
|
|
498
550
|
const [headerLine = '', ...statLines] = String(chunk ?? '')
|
|
499
551
|
.split(/\r?\n/u)
|
|
@@ -588,6 +640,7 @@ export class GitHistoryService {
|
|
|
588
640
|
authoredAt: chunk.authoredAt,
|
|
589
641
|
deletions: Number(numstatEntry?.deletions || 0),
|
|
590
642
|
filesChanged: 1,
|
|
643
|
+
fileKind: getVaultFileKind(nameStatusEntry?.path),
|
|
591
644
|
hash: chunk.hash,
|
|
592
645
|
isMergeCommit: chunk.isMergeCommit,
|
|
593
646
|
oldPath: nameStatusEntry?.oldPath ?? null,
|
|
@@ -679,6 +732,7 @@ export class GitHistoryService {
|
|
|
679
732
|
const numstatEntry = numstatEntries[index];
|
|
680
733
|
return {
|
|
681
734
|
...entry,
|
|
735
|
+
fileKind: getVaultFileKind(entry.path),
|
|
682
736
|
stats: numstatEntry
|
|
683
737
|
? {
|
|
684
738
|
additions: numstatEntry.additions,
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
const IMAGE_MIME_TYPES = Object.freeze({
|
|
2
|
+
'.gif': 'image/gif',
|
|
3
|
+
'.jpeg': 'image/jpeg',
|
|
4
|
+
'.jpg': 'image/jpeg',
|
|
5
|
+
'.png': 'image/png',
|
|
6
|
+
'.svg': 'image/svg+xml',
|
|
7
|
+
'.webp': 'image/webp',
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
export function getImageMimeType(filePath) {
|
|
11
|
+
const normalizedPath = String(filePath ?? '').toLowerCase();
|
|
12
|
+
const extension = Object.keys(IMAGE_MIME_TYPES).find((candidate) => normalizedPath.endsWith(candidate));
|
|
13
|
+
return extension ? IMAGE_MIME_TYPES[extension] : 'application/octet-stream';
|
|
14
|
+
}
|
|
@@ -2,11 +2,17 @@ import { createReadStream } from 'node:fs';
|
|
|
2
2
|
import { readFile } from 'node:fs/promises';
|
|
3
3
|
import { join } from 'node:path';
|
|
4
4
|
|
|
5
|
+
import { getVaultFileKind, isImageAttachmentFilePath } from '../../../domain/file-kind.js';
|
|
5
6
|
import { mapWithConcurrency } from '../../shared/async-utils.js';
|
|
6
7
|
import { createEmptyStats } from './responses.js';
|
|
7
8
|
import { splitContentLines } from './parsers.js';
|
|
9
|
+
import { getImageMimeType } from './image-mime.js';
|
|
8
10
|
|
|
9
11
|
async function countFileLines(filePath) {
|
|
12
|
+
if (isImageAttachmentFilePath(filePath)) {
|
|
13
|
+
return 0;
|
|
14
|
+
}
|
|
15
|
+
|
|
10
16
|
return new Promise((resolve, reject) => {
|
|
11
17
|
const stream = createReadStream(filePath, { encoding: 'utf8' });
|
|
12
18
|
let lineCount = 0;
|
|
@@ -36,6 +42,7 @@ function buildSyntheticAddedFileDiff(pathValue, content) {
|
|
|
36
42
|
const normalizedContent = String(content ?? '').replace(/\r\n/g, '\n');
|
|
37
43
|
const file = {
|
|
38
44
|
code: 'U',
|
|
45
|
+
fileKind: getVaultFileKind(pathValue),
|
|
39
46
|
hunks: [],
|
|
40
47
|
isBinary: false,
|
|
41
48
|
oldPath: null,
|
|
@@ -89,7 +96,27 @@ export class GitUntrackedFileService {
|
|
|
89
96
|
async buildSyntheticDiffs(files = []) {
|
|
90
97
|
const results = await mapWithConcurrency(files, 2, async (file) => {
|
|
91
98
|
try {
|
|
92
|
-
const
|
|
99
|
+
const absolutePath = join(this.vaultDir, file.path);
|
|
100
|
+
if (isImageAttachmentFilePath(file.path)) {
|
|
101
|
+
const content = await readFile(absolutePath);
|
|
102
|
+
return {
|
|
103
|
+
...file,
|
|
104
|
+
binaryMessage: 'Binary image file',
|
|
105
|
+
byteLength: content.byteLength,
|
|
106
|
+
code: file.code || 'U',
|
|
107
|
+
fileKind: 'image',
|
|
108
|
+
hunks: [],
|
|
109
|
+
isBinary: true,
|
|
110
|
+
mimeType: getImageMimeType(file.path),
|
|
111
|
+
oldPath: file.oldPath ?? null,
|
|
112
|
+
path: file.path,
|
|
113
|
+
stats: createEmptyStats(),
|
|
114
|
+
status: file.status || 'untracked',
|
|
115
|
+
synthetic: true,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const content = await readFile(absolutePath, 'utf8');
|
|
93
120
|
return buildSyntheticAddedFileDiff(file.path, content);
|
|
94
121
|
} catch {
|
|
95
122
|
return null;
|
|
@@ -130,7 +130,9 @@ export function createGitApiCommandHandler({
|
|
|
130
130
|
action: 'pull',
|
|
131
131
|
req,
|
|
132
132
|
responsePayload: await workspaceMutationCoordinator.runManagedWorkspaceMutation(
|
|
133
|
-
() => gitService.pullBranch(
|
|
133
|
+
() => gitService.pullBranch({
|
|
134
|
+
author: authService?.getAuthenticatedUser?.(req) ?? null,
|
|
135
|
+
}),
|
|
134
136
|
),
|
|
135
137
|
workspaceMutationCoordinator,
|
|
136
138
|
}));
|
|
@@ -1,5 +1,12 @@
|
|
|
1
|
+
import { basename } from 'node:path';
|
|
2
|
+
|
|
1
3
|
import { handleApiError } from './http-request-helpers.js';
|
|
2
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
createSafeAsciiFilename,
|
|
6
|
+
encodeContentDispositionFilename,
|
|
7
|
+
jsonResponse,
|
|
8
|
+
sendResponse,
|
|
9
|
+
} from './http-response.js';
|
|
3
10
|
|
|
4
11
|
function resolveDiffScope(requestUrl) {
|
|
5
12
|
const scope = String(requestUrl.searchParams.get('scope') || '').trim().toLowerCase();
|
|
@@ -106,6 +113,29 @@ export function createGitApiQueryHandler({ gitService }) {
|
|
|
106
113
|
return true;
|
|
107
114
|
}
|
|
108
115
|
|
|
116
|
+
if (requestUrl.pathname === '/api/git/file-attachment' && req.method === 'GET') {
|
|
117
|
+
try {
|
|
118
|
+
const attachment = await gitService.getFileAttachment({
|
|
119
|
+
hash: requestUrl.searchParams.get('hash'),
|
|
120
|
+
path: requestUrl.searchParams.get('path'),
|
|
121
|
+
});
|
|
122
|
+
const fileName = basename(String(attachment.path || 'image'));
|
|
123
|
+
sendResponse(req, res, {
|
|
124
|
+
body: attachment.content,
|
|
125
|
+
headers: {
|
|
126
|
+
'Cache-Control': 'private, max-age=300, stale-while-revalidate=3600',
|
|
127
|
+
'Content-Disposition': `inline; filename="${createSafeAsciiFilename(fileName)}"; filename*=UTF-8''${encodeContentDispositionFilename(fileName)}`,
|
|
128
|
+
'Content-Type': attachment.mimeType || 'application/octet-stream',
|
|
129
|
+
'X-Content-Type-Options': 'nosniff',
|
|
130
|
+
},
|
|
131
|
+
statusCode: 200,
|
|
132
|
+
});
|
|
133
|
+
} catch (error) {
|
|
134
|
+
handleApiError(req, res, error, '[api] Failed to read git file attachment:', 'Failed to read git file attachment');
|
|
135
|
+
}
|
|
136
|
+
return true;
|
|
137
|
+
}
|
|
138
|
+
|
|
109
139
|
if (requestUrl.pathname === '/api/git/pull-backups' && req.method === 'GET') {
|
|
110
140
|
try {
|
|
111
141
|
jsonResponse(req, res, 200, {
|