collabmd 0.1.46 → 0.1.48

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.
Files changed (317) hide show
  1. package/README.md +9 -5
  2. package/dist/client/assets/{CodeMirrorEditor-UCMRAMCV-CYM5nxUi.js → CodeMirrorEditor-UCMRAMCV-BPH4FJkv.js} +1 -1
  3. package/dist/client/assets/JetBrainsMono-Regular-BQaDgvhP.woff2 +0 -0
  4. package/dist/client/assets/{abnfDiagram-N423BO3Z-CUMTFJhI.js → abnfDiagram-VCTEODGH-BNDLzJ7L.js} +1 -1
  5. package/dist/client/assets/architecture-7GRP2DOG-BMOPAkUC.js +1 -0
  6. package/dist/client/assets/{architectureDiagram-T3A2C74G-w25m71oV.js → architectureDiagram-5GKGNRK7-I39sYjHI.js} +1 -1
  7. package/dist/client/assets/babel-CY5KOv-I.js +16 -0
  8. package/dist/client/assets/base-BYODPHdS.js +1 -0
  9. package/dist/client/assets/blockDiagram-NRAW4CY4-C6DtJy5Y.js +129 -0
  10. package/dist/client/assets/browser-BKLM0ThC-IxjwuOIz-BBqQnUEb.js +1 -0
  11. package/dist/client/assets/c4Diagram-UCG6FXSJ-CtWtxS9_.js +38 -0
  12. package/dist/client/assets/channel-CaoKA_We.js +1 -0
  13. package/dist/client/assets/{chunk-U6XO7XAA-BFxjP5U3.js → chunk-2ZTRR5NV-C2qZJila.js} +1 -1
  14. package/dist/client/assets/{chunk-YOTPTUD7-D0vRKqYp.js → chunk-3Z5EZCMW-DMniOlPd.js} +1 -1
  15. package/dist/client/assets/{chunk-BIQX33UG-DZEsGuSn.js → chunk-5V3GS4D5-i-ZDOEnP.js} +1 -1
  16. package/dist/client/assets/{chunk-MOZMSUNE-XVucA-n6.js → chunk-6AZGARVD-BB6FqMl-.js} +1 -1
  17. package/dist/client/assets/{chunk-5JV3BV7I-Cy5pj3QY.js → chunk-6EIED4P4-DdUJkP0f.js} +1 -1
  18. package/dist/client/assets/{chunk-R7FJI6CG-Cnn3gE-Y.js → chunk-6K3QC6MW-CqVsVn-y.js} +1 -1
  19. package/dist/client/assets/{chunk-OSBZ3O6U-CrKZOUOr.js → chunk-6TQVIW2G-DKlLbNIP.js} +1 -1
  20. package/dist/client/assets/{chunk-JG7HCLWE-BYfiSaVt.js → chunk-747NJXEK-BFkZPlab.js} +1 -1
  21. package/dist/client/assets/{chunk-NSK5VX7P-TLH37VXI.js → chunk-75Z2AOVW-CdFSaPjz.js} +1 -1
  22. package/dist/client/assets/chunk-CKBBP62Z-DG26UiBv.js +2 -0
  23. package/dist/client/assets/{chunk-3NCLNEKW-Y2Jf_LI-.js → chunk-CLGD4ZFX-DQLgbQrv.js} +1 -1
  24. package/dist/client/assets/chunk-DSNCTWBM-8xkug7fQ.js +2 -0
  25. package/dist/client/assets/chunk-DU6HZSFF-COXDzJK1.js +127 -0
  26. package/dist/client/assets/{chunk-2GRJ4B5K-DhnlUv3F.js → chunk-F27PBJKO-CNHIFhjo.js} +1 -1
  27. package/dist/client/assets/{chunk-YGJVY4SA-CjSe7Bx7.js → chunk-FLYJHZSQ-Be7lKF17.js} +1 -1
  28. package/dist/client/assets/{chunk-KEIR6QF5-BICK3FdT.js → chunk-FOHPRMQF-DHwB1DNv.js} +2 -2
  29. package/dist/client/assets/{chunk-5RXB4S5H-BWrwFqMI.js → chunk-G27WJ6UU-D9-eg537.js} +2 -2
  30. package/dist/client/assets/chunk-GMAD6QVW-CbcBqPM5.js +72 -0
  31. package/dist/client/assets/{chunk-QBLGF6JB-CxAyS2mW.js → chunk-I5DQTOEV-Hm2LfiYI.js} +1 -1
  32. package/dist/client/assets/{chunk-5FCAYU7R-Dh5nylU8.js → chunk-ICYGCRZG-749o-pGJ.js} +1 -1
  33. package/dist/client/assets/{chunk-CQNSW5MT-B8S09OIh.js → chunk-IH6LHLGP-CC6I9F3i.js} +1 -1
  34. package/dist/client/assets/chunk-JQ64N6SF-DSFNH4bh.js +1 -0
  35. package/dist/client/assets/{chunk-CYSBUYHQ-C1Rf4art.js → chunk-KI3K4JFJ-Rma-0k_M.js} +1 -1
  36. package/dist/client/assets/{chunk-GF5L2VYU-DkJpO7ff.js → chunk-LCL6LL3I-DmDkVukh.js} +1 -1
  37. package/dist/client/assets/chunk-LNGE3PJU-DWpGwHrW.js +34 -0
  38. package/dist/client/assets/chunk-OBVCFTLP-BJR7u1jh.js +1 -0
  39. package/dist/client/assets/{chunk-5TONJI2A-DQOY3q1H.js → chunk-OUJLGHUK-D0j2JRSg.js} +1 -1
  40. package/dist/client/assets/chunk-P2QGCYS3-BRpqY32H.js +1 -0
  41. package/dist/client/assets/{chunk-KBJHAD2P-CB_3zWUf.js → chunk-POPQ4Y6H-Cb4SlVZF.js} +1 -1
  42. package/dist/client/assets/{chunk-4I5QYGJK-CCB2YcIH.js → chunk-PWAF6VOD-Cz8IwBB5.js} +1 -1
  43. package/dist/client/assets/chunk-R7TYR2AO-CTd0Yj98.js +10 -0
  44. package/dist/client/assets/chunk-RHFEMEQ7-CWY8cf4H.js +168 -0
  45. package/dist/client/assets/chunk-RM4UPSZO-CWRwxNWJ.js +41 -0
  46. package/dist/client/assets/{chunk-6Q2QTUOP-CPlWzBtc.js → chunk-SVP7TREG-CyHOmcac.js} +1 -1
  47. package/dist/client/assets/chunk-TEH6E4GO-DVayPj-q.js +62 -0
  48. package/dist/client/assets/{chunk-EMLP6XTP-RLD4nXpI.js → chunk-UY3FDG6J-CnmaAkG5.js} +1 -1
  49. package/dist/client/assets/{chunk-5HE753X5-Bw-yg4kO.js → chunk-XHIXRSVI-BFQ6B8BE.js} +1 -1
  50. package/dist/client/assets/classDiagram-DTDB5LWJ-7fEmn0zV.js +1 -0
  51. package/dist/client/assets/classDiagram-v2-JRS7N3AN-7fEmn0zV.js +1 -0
  52. package/dist/client/assets/comment-ui-shared-DuoyznXW.js +7 -0
  53. package/dist/client/assets/cynefin-OW5HDTMX-7tP7QfM3.js +1 -0
  54. package/dist/client/assets/{cynefinDiagram-MW4NZA55-CJxXfJek.js → cynefinDiagram-5FMLGOSQ-CdTPi2yO.js} +1 -1
  55. package/dist/client/assets/dagre-3AP2YEHR-5gtJtd0O.js +4 -0
  56. package/dist/client/assets/dagre-PrKaheQc.js +1 -0
  57. package/dist/client/assets/{diagram-7IWD3JNH-B2d2Bm15.js → diagram-S7CK7UJ4-BudWz9WU.js} +4 -4
  58. package/dist/client/assets/{diagram-UB23O5K3-DJcKCwv9.js → diagram-UQ7AKVKN-3v4rbH-s.js} +1 -1
  59. package/dist/client/assets/{diagram-B4RE2ZJO-NP4UAwjR.js → diagram-VSXAHHWV-CQRVEZR7.js} +1 -1
  60. package/dist/client/assets/{diagram-Q27KOJAE-DylDuwor.js → diagram-VX7I27RA-C_-rj0sw.js} +1 -1
  61. package/dist/client/assets/{diagram-LBJQPF4R-D1FJQV8t.js → diagram-Z3DM3KII-B4yxavgj.js} +1 -1
  62. package/dist/client/assets/direct-engine-DdrQKpgq-CtKfQzCt.js +5 -0
  63. package/dist/client/assets/dist-73RPdcyU.js +1 -0
  64. package/dist/client/assets/{dist-Bm6pf9Bl.js → dist-BJ1bJs33.js} +1 -1
  65. package/dist/client/assets/{dist-BalScOwZ.js → dist-BK9QvQrc.js} +1 -1
  66. package/dist/client/assets/{dist-DLMiIsNC.js → dist-BPHLJ09L.js} +1 -1
  67. package/dist/client/assets/{dist-Bf8f4HmM.js → dist-B_yQaEv_.js} +1 -1
  68. package/dist/client/assets/{dist-CC5IrQMG.js → dist-BeajGMhO.js} +1 -1
  69. package/dist/client/assets/{dist-PDZ3bSqm.js → dist-Bw3VIE_v.js} +1 -1
  70. package/dist/client/assets/{dist-xElaxbia.js → dist-ByrG2qBA.js} +1 -1
  71. package/dist/client/assets/{dist-ule2n6ak.js → dist-C5NgpdG5.js} +1 -1
  72. package/dist/client/assets/dist-CH-sQu3r.js +1 -0
  73. package/dist/client/assets/dist-CKS_-are.js +1 -0
  74. package/dist/client/assets/{dist-D-Z6FLem.js → dist-CdmKGRGr.js} +1 -1
  75. package/dist/client/assets/dist-ChRoo3IB.js +9 -0
  76. package/dist/client/assets/{dist-dtAdgL39.js → dist-CietwTVS.js} +1 -1
  77. package/dist/client/assets/{dist-jv4WdPnK.js → dist-D37Hyu3h.js} +1 -1
  78. package/dist/client/assets/dist-D3fehAZ2.js +2 -0
  79. package/dist/client/assets/{dist-BEwK4csK.js → dist-D5wCC32z.js} +1 -1
  80. package/dist/client/assets/{dist-shrl6K_0.js → dist-DDUn5zTz.js} +1 -1
  81. package/dist/client/assets/{dist-CZgOmxK6.js → dist-DGEbEfw4.js} +1 -1
  82. package/dist/client/assets/{dist-B5HTH2BO.js → dist-DVy5woUy.js} +1 -1
  83. package/dist/client/assets/{dist-DZKu9Cs8.js → dist-DZAAWTG-.js} +1 -1
  84. package/dist/client/assets/{dist-TjMh8n1u.js → dist-Dir5dDKZ.js} +1 -1
  85. package/dist/client/assets/dist-Dl_tpDSr.js +1 -0
  86. package/dist/client/assets/{dist-FWJsgGYI.js → dist-DuH3ZrHo.js} +1 -1
  87. package/dist/client/assets/{dist-BjHJDtyU.js → dist-DutPuFmC.js} +1 -1
  88. package/dist/client/assets/{dist-Cqhv5LAk.js → dist-LQ4kDCKz.js} +1 -1
  89. package/dist/client/assets/{dist-CCP_AKPG.js → dist-RKBTaQMb.js} +1 -1
  90. package/dist/client/assets/{dist-BoVBdSjk.js → dist-Tw-Fxpeb.js} +1 -1
  91. package/dist/client/assets/dist-eBx8q6Sy.js +1 -0
  92. package/dist/client/assets/{dist-rGBmqxC7.js → dist-mj2Ht75J.js} +1 -1
  93. package/dist/client/assets/{dist-BDt3rz7l.js → dist-netZOQtD.js} +1 -1
  94. package/dist/client/assets/document-formatter-Dxl_a8XO.js +2 -0
  95. package/dist/client/assets/{drawio-editor-FzY1cVw8.js → drawio-editor-BVaNNtYp.js} +2 -2
  96. package/dist/client/assets/{drawioEditor-BjiKJqlc.js → drawioEditor-BkIBlXx0.js} +2 -2
  97. package/dist/client/assets/{ebnfDiagram-BXEA7PRR-nw0wmA6y.js → ebnfDiagram-PWID7BFC-OXa8VEp1.js} +1 -1
  98. package/dist/client/assets/editor-session-DMyVdHLj.js +54 -0
  99. package/dist/client/assets/embedded-editor-base-B3l6Xy65.css +1 -0
  100. package/dist/client/assets/embedpdf-BmE13ZZ0.js +1 -0
  101. package/dist/client/assets/embedpdf-DA4bA119-BPqZxwjc.js +2363 -0
  102. package/dist/client/assets/{en-7NFCYGKX-BrgYZAEe.js → en-SJ2YIMFF-CJBL3nkq.js} +1 -1
  103. package/dist/client/assets/erDiagram-SSCWMZ5O-C-iy6VkX.js +99 -0
  104. package/dist/client/assets/estree-CuT_pB8K.js +44 -0
  105. package/dist/client/assets/eventmodeling-NTZA5JFV-BR2FzK4R.js +1 -0
  106. package/dist/client/assets/excalidraw-editor-BbSbI80a.css +1 -0
  107. package/dist/client/assets/excalidraw-editor-BjYaNEMY.js +9 -0
  108. package/dist/client/assets/excalidrawEditor-ChqF_d82.js +2 -0
  109. package/dist/client/assets/export-page-Dyu0XCbu.js +57 -0
  110. package/dist/client/assets/exportDocument-CJBx4Xjx.css +1 -0
  111. package/dist/client/assets/exportDocument-Dgvfa2Sd.js +2 -0
  112. package/dist/client/assets/{file-icon-svg-CQAjlWjK.js → file-icon-svg-Y_kpF957.js} +1 -1
  113. package/dist/client/assets/file-kind-BwMIcZbi.js +1 -0
  114. package/dist/client/assets/file-search-DDLyyLDU.js +1 -0
  115. package/dist/client/assets/flowDiagram-A5DVABFB-CiW1eN8C.js +1 -0
  116. package/dist/client/assets/{ganttDiagram-PKOTCBZU-BnM97BCT.js → ganttDiagram-EL5Y4UJY-DtKHAbez.js} +1 -1
  117. package/dist/client/assets/gitGraph-4MIJSDKK-DYlXv_yJ.js +1 -0
  118. package/dist/client/assets/{gitGraphDiagram-DS77QQ5N-BQpQhjuN.js → gitGraphDiagram-WWUBYQGX-oue8fQ5o.js} +1 -1
  119. package/dist/client/assets/github-dark.min-BV82VuKq.css +1 -0
  120. package/dist/client/assets/github.min-v3dd8CNH.css +1 -0
  121. package/dist/client/assets/html-Cdnr2qzU.js +25 -0
  122. package/dist/client/assets/image-ZVKINSPA-0zXSnE7M.js +1 -0
  123. package/dist/client/assets/index-CSNIMyOP.css +1 -0
  124. package/dist/client/assets/index-DG3N2Ps-.js +2 -0
  125. package/dist/client/assets/info-A6RAGUB7-BlU-8_xu.js +1 -0
  126. package/dist/client/assets/{infoDiagram-6WML65LV-DYB8HCDQ.js → infoDiagram-RXCK75RN-BMzo6KIn.js} +1 -1
  127. package/dist/client/assets/{ishikawaDiagram-WSZJBQD7-Coz_P3t6.js → ishikawaDiagram-5VMMS53U-BIM3h287.js} +1 -1
  128. package/dist/client/assets/{journeyDiagram-NVQOT4AX-B6edChce.js → journeyDiagram-EYS64GPL-mF6Lh8O-.js} +1 -1
  129. package/dist/client/assets/{kanban-definition-27J2QSJJ-BFd9-cLc.js → kanban-definition-3QL26DDD-DLN2NyoR.js} +2 -2
  130. package/dist/client/assets/{line-B_S_S8kV.js → line-BfTWJmgf.js} +1 -1
  131. package/dist/client/assets/main-D0wCxpVn.js +903 -0
  132. package/dist/client/assets/markdown-CByV-4CU.js +70 -0
  133. package/dist/client/assets/{mermaid-parser.core-CUVdebAV.js → mermaid-parser.core-Bl0efOMt.js} +2 -2
  134. package/dist/client/assets/mermaid.core-BJRLUQZE.js +11 -0
  135. package/dist/client/assets/{mindmap-definition-FAOFIHXS-C9PQ38Al.js → mindmap-definition-FBJOCRG2-CRyHaHTk.js} +1 -1
  136. package/dist/client/assets/packet-AYTQ26CC-BSJEH9Lb.js +1 -0
  137. package/dist/client/assets/pdfium-RAgkpwfK.wasm +0 -0
  138. package/dist/client/assets/{pegDiagram-VL7TDLO6-BMZUrSaU.js → pegDiagram-XKGWAZYB-B31fKPR2.js} +1 -1
  139. package/dist/client/assets/pie-WAS4IAKB-qxv01Y8W.js +1 -0
  140. package/dist/client/assets/{pieDiagram-7S7Q4E2Y-h8kbCL9o.js → pieDiagram-E7YTZNPT-B5OZPb88.js} +1 -1
  141. package/dist/client/assets/preview-render-compiler-CrJIooVD.js +35 -0
  142. package/dist/client/assets/preview-render-compiler-O0JxBVNF.js +1 -0
  143. package/dist/client/assets/{preview-render-profile-CE-5rVTC.js → preview-render-profile-B4RWYWHt.js} +1 -1
  144. package/dist/client/assets/preview-render-worker-_QTxqKvP.js +60 -0
  145. package/dist/client/assets/prod-BiYPMR1i.js +177 -0
  146. package/dist/client/assets/prod-VxdGZ5Ly.js +1 -0
  147. package/dist/client/assets/{quadrantDiagram-CIZ2JOQS-CkElyfMi.js → quadrantDiagram-AXDQQJYC-Drrw14YH.js} +1 -1
  148. package/dist/client/assets/quick-switcher-controller-CaEjEtq_.js +1 -0
  149. package/dist/client/assets/radar-RG4KPBEZ-B6h6pWOs.js +1 -0
  150. package/dist/client/assets/railroad-74A4TZTK-CGtTWdGc.js +1 -0
  151. package/dist/client/assets/railroad-abnf-HS5TGJTU-Cq7y22IA.js +1 -0
  152. package/dist/client/assets/railroad-ebnf-LZEXJU2U-V0aQVv9m.js +1 -0
  153. package/dist/client/assets/railroad-peg-WCYAUIDC-BQiIsX_p.js +1 -0
  154. package/dist/client/assets/{railroadDiagram-AXF67PYL-c75gnikD.js → railroadDiagram-O6MQD6OU-BPifP0MB.js} +1 -1
  155. package/dist/client/assets/{requirementDiagram-LRYGKXZP-mNxKLuHS.js → requirementDiagram-EFPCY7ZU-CRZG6mRB.js} +2 -2
  156. package/dist/client/assets/{sankeyDiagram-W5VNT64P-0OhplksI.js → sankeyDiagram-P5KCCOFB-DvarEJpo.js} +1 -1
  157. package/dist/client/assets/{sequenceDiagram-SI44F4Z6-BJ_yAocC.js → sequenceDiagram-WJ2MYXX4-B4oWlb-w.js} +1 -1
  158. package/dist/client/assets/standalone-6z5x8CWe.js +29 -0
  159. package/dist/client/assets/{stateDiagram-OKZ733FA-Cc83O86s.js → stateDiagram-HBIQ2CUA-qbHIIqRZ.js} +1 -1
  160. package/dist/client/assets/stateDiagram-v2-4QOOHH4V-B--gjrit.js +1 -0
  161. package/dist/client/assets/swimlanes-XN3QIQJK-xB6CtUGj.js +1 -0
  162. package/dist/client/assets/swimlanesDiagram-VK2B7HYN-DO8XNXEg.js +8 -0
  163. package/dist/client/assets/{timeline-definition-Z64GVDOM-BWDAGTHz.js → timeline-definition-24CTP7MA-Lj_yTNlY.js} +1 -1
  164. package/dist/client/assets/treeView-Q6P3EWNA-BTTYcvrF.js +1 -0
  165. package/dist/client/assets/treemap-WGGIJYW6-Cg7tyfH8.js +1 -0
  166. package/dist/client/assets/{vault-api-client-_LpV1aIn.js → vault-api-client-BkwdFjx0.js} +2 -2
  167. package/dist/client/assets/vault-utils-DvoSftJT.js +22 -0
  168. package/dist/client/assets/{vennDiagram-T6HMQDX7-LOSsnlMB.js → vennDiagram-4TSXK5OY-BekRp5Y0.js} +1 -1
  169. package/dist/client/assets/wardley-WFR3VGLG-C6N1rULY.js +1 -0
  170. package/dist/client/assets/{wardleyDiagram-T6FBY63Y-BxII3foY.js → wardleyDiagram-VM6X3IG4-kdnV_Hdm.js} +1 -1
  171. package/dist/client/assets/worker-engine-m0rlHn8R-S8mpn_O0.js +17369 -0
  172. package/dist/client/assets/{xychartDiagram-ELKLHX3M-B-pTEjju.js → xychartDiagram-S5SC5T6Z-DufGwNSB.js} +3 -3
  173. package/dist/client/assets/yaml-9ltHPDNO.js +154 -0
  174. package/dist/client/drawio-editor.html +1 -1
  175. package/dist/client/excalidraw-editor.html +1 -1
  176. package/dist/client/export-document.html +3 -3
  177. package/dist/client/index.html +56 -24
  178. package/package.json +22 -18
  179. package/src/client/app/index.html +53 -21
  180. package/src/client/application/app-shell/comments-feature.js +2 -3
  181. package/src/client/application/app-shell/export-feature.js +17 -1
  182. package/src/client/application/app-shell/git-feature.js +30 -11
  183. package/src/client/application/app-shell/presence-feature.js +2 -2
  184. package/src/client/application/app-shell/ui-feature-identity.js +13 -0
  185. package/src/client/application/app-shell/ui-feature-shell.js +228 -9
  186. package/src/client/application/app-shell-elements.js +5 -0
  187. package/src/client/application/pdf-preview-controller.js +53 -970
  188. package/src/client/application/preview-render-compiler.js +3 -0
  189. package/src/client/application/quick-switcher-loader.js +1 -1
  190. package/src/client/application/workspace-coordinator.js +21 -4
  191. package/src/client/application/workspace-preview-controller.js +120 -5
  192. package/src/client/application/workspace-route-controller.js +4 -1
  193. package/src/client/assets/vendor/highlight/github-dark.min.css +2 -10
  194. package/src/client/assets/vendor/highlight/github.min.css +2 -10
  195. package/src/client/assets/vendor/jetbrains-mono/JetBrainsMono-Regular.woff2 +0 -0
  196. package/src/client/assets/vendor/jetbrains-mono/OFL.txt +93 -0
  197. package/src/client/bootstrap/collabmd-app-shell.js +31 -8
  198. package/src/client/domain/document-formatter.js +193 -0
  199. package/src/client/domain/file-search.js +73 -0
  200. package/src/client/domain/git-labels.js +5 -0
  201. package/src/client/domain/vault-paths.js +6 -4
  202. package/src/client/domain/wiki-link-completions.js +20 -20
  203. package/src/client/drawio-editor.js +8 -3
  204. package/src/client/excalidraw-editor.js +163 -6
  205. package/src/client/export/export-host.js +57 -36
  206. package/src/client/export/export-page.js +20 -6
  207. package/src/client/export/export-pipeline.js +322 -22
  208. package/src/client/infrastructure/editor-session.js +4 -0
  209. package/src/client/infrastructure/editor-view-adapter.js +60 -14
  210. package/src/client/presentation/bases-preview-controller.js +35 -11
  211. package/src/client/presentation/comment-ui/comment-ui-render.js +4 -0
  212. package/src/client/presentation/comment-ui-controller.js +2 -0
  213. package/src/client/presentation/excalidraw-embed-controller.js +7 -0
  214. package/src/client/presentation/file-action-controller.js +15 -2
  215. package/src/client/presentation/file-explorer-controller.js +3 -5
  216. package/src/client/presentation/file-explorer-view.js +43 -45
  217. package/src/client/presentation/file-history-view-controller.js +6 -1
  218. package/src/client/presentation/file-tree-state.js +5 -14
  219. package/src/client/presentation/git-diff-view-controller.js +36 -11
  220. package/src/client/presentation/git-panel-controller.js +12 -8
  221. package/src/client/presentation/quick-switcher-controller.js +92 -153
  222. package/src/client/styles/components/scrollbars.css +10 -0
  223. package/src/client/styles/export-document.css +44 -29
  224. package/src/client/styles/features/collaboration-presence.css +13 -1
  225. package/src/client/styles/features/preview-markdown.css +79 -0
  226. package/src/client/styles/features/quick-switcher.css +8 -0
  227. package/src/client/styles/foundation/themes.css +1 -0
  228. package/src/client/styles/foundation/tokens.css +8 -0
  229. package/src/client/styles/layout/shell.css +73 -339
  230. package/src/client/styles/layout/sidebar.css +42 -4
  231. package/src/client/styles/layout/view-modes.css +2 -0
  232. package/src/client/styles/overrides/codemirror.css +31 -19
  233. package/src/client/styles/overrides/highlightjs.css +20 -4
  234. package/src/client/styles/surfaces/excalidraw-editor.css +42 -0
  235. package/src/domain/file-kind.js +12 -1
  236. package/src/server/domain/ripgrep-search-service.js +110 -37
  237. package/src/server/infrastructure/git/git-service.js +23 -11
  238. package/src/server/infrastructure/http/create-git-api-command-handler.js +3 -1
  239. package/src/server/infrastructure/http/create-structurizr-api-handler.js +4 -2
  240. package/src/server/infrastructure/persistence/path-utils.js +10 -4
  241. package/src/server/infrastructure/persistence/vault-file-store.js +2 -0
  242. package/src/server/infrastructure/structurizr/structurizr-workspace-service.js +33 -7
  243. package/dist/client/assets/architecture-TIHT7OUA-CsHfPErM.js +0 -1
  244. package/dist/client/assets/base-CxPEiHuy.js +0 -1
  245. package/dist/client/assets/blockDiagram-VBNYF7ZC-CipJt9Jl.js +0 -132
  246. package/dist/client/assets/c4Diagram-5PPSVZJV-B5ktVx6g.js +0 -10
  247. package/dist/client/assets/channel-4Jkmq93I.js +0 -1
  248. package/dist/client/assets/chunk-7BUUIJ7U-Bb538aSH.js +0 -1
  249. package/dist/client/assets/chunk-7Z6QIM7H-BP9Pdsm9.js +0 -10
  250. package/dist/client/assets/chunk-ERSWUTIN-hHdvnhaY.js +0 -41
  251. package/dist/client/assets/chunk-I66GZJ75-CVFiiffQ.js +0 -127
  252. package/dist/client/assets/chunk-J7OUQ5F2-4CEb7pvH.js +0 -2
  253. package/dist/client/assets/chunk-JQJVKLGR-cx8X_24v.js +0 -156
  254. package/dist/client/assets/chunk-QR6OTTB3-CDAA_5Ib.js +0 -62
  255. package/dist/client/assets/chunk-RYQCIY6F-D-a0zH8K.js +0 -1
  256. package/dist/client/assets/chunk-UBXNYLIW-Bhk-PbFK.js +0 -1
  257. package/dist/client/assets/chunk-W5SLKNZC-BdNSCxZ9.js +0 -1
  258. package/dist/client/assets/chunk-WRU74C26-h6_XkD3_.js +0 -70
  259. package/dist/client/assets/chunk-ZIRB5QZD-O39ZBUk3.js +0 -32
  260. package/dist/client/assets/classDiagram-JCYQIIEL-L7lsyW_w.js +0 -1
  261. package/dist/client/assets/classDiagram-v2-OCEON4UE-L7lsyW_w.js +0 -1
  262. package/dist/client/assets/comment-ui-shared-BC4pt4w8.js +0 -7
  263. package/dist/client/assets/cynefin-VYW2F7L2-Ds2Fw2Y0.js +0 -1
  264. package/dist/client/assets/dagre-Buvkdvvj.js +0 -1
  265. package/dist/client/assets/dagre-VZM6K2ZE-L1xArOGs.js +0 -4
  266. package/dist/client/assets/dist-B5Jf1SLZ.js +0 -2
  267. package/dist/client/assets/dist-BKW4PALF.js +0 -1
  268. package/dist/client/assets/dist-BWcHHOx-.js +0 -1
  269. package/dist/client/assets/dist-DaYdhcVs.js +0 -1
  270. package/dist/client/assets/dist-Kh2COra_.js +0 -1
  271. package/dist/client/assets/dist-tOaegDYS2.js +0 -1
  272. package/dist/client/assets/editor-session-Cm9ekeNo.js +0 -54
  273. package/dist/client/assets/embedded-editor-base-CwTkk911.css +0 -1
  274. package/dist/client/assets/erDiagram-JOGREHBK-cYUpEKpf.js +0 -85
  275. package/dist/client/assets/es-DXGV3RHd.js +0 -22
  276. package/dist/client/assets/eventmodeling-45OFAUF4-CLEiVx5s.js +0 -1
  277. package/dist/client/assets/excalidraw-editor-B46zsTFI.js +0 -9
  278. package/dist/client/assets/excalidraw-editor-Bt7gMSoh.css +0 -1
  279. package/dist/client/assets/excalidrawEditor-CT2pHq7e.js +0 -2
  280. package/dist/client/assets/export-page-A7NHawJY.js +0 -39
  281. package/dist/client/assets/exportDocument-B2L8fe34.css +0 -1
  282. package/dist/client/assets/exportDocument-BaqcOAAW.js +0 -2
  283. package/dist/client/assets/file-kind-BbqgGzpc.js +0 -1
  284. package/dist/client/assets/flowDiagram-UKHOOZJN-D0ypP0Ez.js +0 -1
  285. package/dist/client/assets/gitGraph-TEB2WS4Q-CZFBqwW-.js +0 -1
  286. package/dist/client/assets/github-dark.min-Z5exjX_n.css +0 -1
  287. package/dist/client/assets/github.min-D2BCvnWf.css +0 -1
  288. package/dist/client/assets/image-QXRYHUNP-SWEV8H0J.js +0 -1
  289. package/dist/client/assets/index-ClbjMBVt.js +0 -2
  290. package/dist/client/assets/index-Pum8ET-5.css +0 -1
  291. package/dist/client/assets/info-DKCQHKI2--1InqQeT.js +0 -1
  292. package/dist/client/assets/main-B3uPyqic.js +0 -893
  293. package/dist/client/assets/map-BaFkSB1l.js +0 -1
  294. package/dist/client/assets/mermaid.core-DN-lF51U.js +0 -11
  295. package/dist/client/assets/packet-7NZHBO7P-BGMXB0XL.js +0 -1
  296. package/dist/client/assets/pdf-BL9Pr2D5.js +0 -54
  297. package/dist/client/assets/pdf.worker-CLesOks4.mjs +0 -64857
  298. package/dist/client/assets/pdf_viewer-FVkkThnz.js +0 -551
  299. package/dist/client/assets/pie-RZYD4A2V-DgYg3tCt.js +0 -1
  300. package/dist/client/assets/preview-render-compiler-BD-XhkS2.js +0 -35
  301. package/dist/client/assets/preview-render-compiler-BKedaMxx.js +0 -1
  302. package/dist/client/assets/preview-render-worker-CiPqc9n6.js +0 -60
  303. package/dist/client/assets/prod-Dy20X7eT.js +0 -1
  304. package/dist/client/assets/prod-hnwh7wRM.js +0 -177
  305. package/dist/client/assets/quick-switcher-controller-fqOYR7qR.js +0 -12
  306. package/dist/client/assets/radar-I7S5WNFK-Ag-zp_Rq.js +0 -1
  307. package/dist/client/assets/railroad-3IZDKUUU-aibJDIDV.js +0 -1
  308. package/dist/client/assets/railroad-abnf-AHOZXSZD-BBMEuvIl.js +0 -1
  309. package/dist/client/assets/railroad-ebnf-EBAXGLYW-Be1nseej.js +0 -1
  310. package/dist/client/assets/railroad-peg-LSFZ7HO6-DIvD-Eyb.js +0 -1
  311. package/dist/client/assets/stateDiagram-v2-UEYNNEHI-B1-BufUk.js +0 -1
  312. package/dist/client/assets/swimlanes-SLNWSIFB-oVTWEJIj.js +0 -2
  313. package/dist/client/assets/swimlanesDiagram-ULZ7WXOC-C5OTVwEu.js +0 -8
  314. package/dist/client/assets/treeView-QDETBFTQ-uHZ-3Fz8.js +0 -1
  315. package/dist/client/assets/treemap-6X3UGDF4-BnUKyy3l.js +0 -1
  316. package/dist/client/assets/vault-utils-wdQ0RNR9.js +0 -1
  317. package/dist/client/assets/wardley-OPB4EBWU-Da7TLC2F.js +0 -1
@@ -13,17 +13,7 @@ function flattenTree(nodes, files = [], searchEntries = [], fileEntries = []) {
13
13
  type: node.type,
14
14
  });
15
15
 
16
- if (
17
- node.type === 'file'
18
- || node.type === 'base'
19
- || node.type === 'excalidraw'
20
- || node.type === 'drawio'
21
- || node.type === 'mermaid'
22
- || node.type === 'plantuml'
23
- || node.type === 'structurizr'
24
- || node.type === 'pdf'
25
- || node.type === 'image'
26
- ) {
16
+ if (node.type !== 'directory') {
27
17
  files.push(node.path);
28
18
  fileEntries.push({
29
19
  mtimeMs: node.mtimeMs,
@@ -104,16 +94,17 @@ export class FileTreeState {
104
94
  }
105
95
 
106
96
  setSearchQuery(value) {
107
- this.searchQuery = String(value ?? '').trim().toLowerCase();
97
+ this.searchQuery = String(value ?? '');
108
98
  }
109
99
 
110
100
  getSearchMatches() {
111
- if (!this.searchQuery) {
101
+ const normalizedQuery = this.searchQuery.trim().toLowerCase();
102
+ if (!normalizedQuery) {
112
103
  return [];
113
104
  }
114
105
 
115
106
  return this.flatSearchEntries
116
- .filter((entry) => entry.lowerPath.includes(this.searchQuery))
107
+ .filter((entry) => entry.type !== 'directory' && entry.lowerPath.includes(normalizedQuery))
117
108
  .map((entry) => ({
118
109
  name: entry.name,
119
110
  path: entry.path,
@@ -1,4 +1,5 @@
1
1
  import { escapeHtml } from '../domain/vault-utils.js';
2
+ import { commitButtonLabel } from '../domain/git-labels.js';
2
3
  import { getVaultPathLeaf, getVaultPathParent } from '../domain/vault-paths.js';
3
4
  import { resolveApiUrl } from '../domain/runtime-paths.js';
4
5
  import { renderDrawioViewer } from './drawio-viewer.js';
@@ -456,6 +457,7 @@ export class GitDiffViewController {
456
457
  this.data = null;
457
458
  this.currentIndex = 0;
458
459
  this.activeFilePath = null;
460
+ this.workspaceFilePath = null;
459
461
  this.fileCache = new Map();
460
462
  this.fileErrors = new Map();
461
463
  this.loadingFiles = new Set();
@@ -507,12 +509,12 @@ export class GitDiffViewController {
507
509
  return;
508
510
  }
509
511
 
510
- const currentFile = this.getCurrentFile();
511
- if (!currentFile?.path) {
512
+ const filePath = this.getCurrentFile()?.path ?? this.workspaceFilePath;
513
+ if (!filePath) {
512
514
  return;
513
515
  }
514
516
 
515
- this.onOpenFile?.(currentFile.path);
517
+ this.onOpenFile?.(filePath);
516
518
  });
517
519
  this.primaryActionButton?.addEventListener('click', () => {
518
520
  if (!this.isActive) {
@@ -611,6 +613,7 @@ export class GitDiffViewController {
611
613
  this.layoutMode = 'focused';
612
614
  this.currentIndex = 0;
613
615
  this.activeFilePath = null;
616
+ this.workspaceFilePath = null;
614
617
  this.fileCache.clear();
615
618
  this.fileErrors.clear();
616
619
  this.loadingFiles.clear();
@@ -624,7 +627,6 @@ export class GitDiffViewController {
624
627
  this.commitMeta = null;
625
628
  this.historyFilePath = null;
626
629
  this.pendingAction = null;
627
- this.repoStatus = null;
628
630
  }
629
631
 
630
632
  async openWorkspaceDiff({ filePath = null, scope = 'all' } = {}) {
@@ -636,6 +638,7 @@ export class GitDiffViewController {
636
638
  this.commitMeta = null;
637
639
  this.historyFilePath = null;
638
640
  this.activeFilePath = filePath;
641
+ this.workspaceFilePath = filePath;
639
642
  this.fileCache.clear();
640
643
  this.fileErrors.clear();
641
644
  this.loadingFiles.clear();
@@ -688,6 +691,7 @@ export class GitDiffViewController {
688
691
  this.commitMeta = null;
689
692
  this.historyFilePath = String(historyFilePath ?? '').trim() || null;
690
693
  this.activeFilePath = path || null;
694
+ this.workspaceFilePath = null;
691
695
  this.fileCache.clear();
692
696
  this.fileErrors.clear();
693
697
  this.loadingFiles.clear();
@@ -792,6 +796,7 @@ export class GitDiffViewController {
792
796
  }
793
797
  this.page?.classList.remove('hidden');
794
798
  if (this.content) {
799
+ // pi-lens-ignore: ast-grep:no-inner-html-js
795
800
  this.content.innerHTML = `<div class="diff-empty-state">${escapeHtml(message)}</div>`;
796
801
  }
797
802
  this.data = null;
@@ -804,6 +809,7 @@ export class GitDiffViewController {
804
809
  }
805
810
  this.page?.classList.remove('hidden');
806
811
  if (this.content) {
812
+ // pi-lens-ignore: ast-grep:no-inner-html-js
807
813
  this.content.innerHTML = `<div class="diff-empty-state">${escapeHtml(message)}</div>`;
808
814
  }
809
815
  this.syncToolbar();
@@ -1240,10 +1246,14 @@ export class GitDiffViewController {
1240
1246
  const headerStats = fileKind === 'image'
1241
1247
  ? `<span class="diff-file-header-kind">${escapeHtml(getImageFormat(file.path))} image</span>`
1242
1248
  : `<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>`;
1249
+ const inclusionStatus = file.hasStagedChanges
1250
+ ? '<span class="ui-pill-badge ui-pill-badge--muted" aria-label="Included in next commit">✓ Included</span>'
1251
+ : '';
1243
1252
  return `
1244
1253
  <div class="diff-file-header">
1245
1254
  <span class="diff-file-path">${escapeHtml(file.path)}</span>
1246
1255
  <span class="ui-status-badge ${badgeClass(file.status)}">${escapeHtml(file.status)}</span>
1256
+ ${inclusionStatus}
1247
1257
  ${headerStats}
1248
1258
  </div>
1249
1259
  `;
@@ -1837,8 +1847,8 @@ export class GitDiffViewController {
1837
1847
  this.fileIndicator.textContent = `${totalFiles} file${totalFiles === 1 ? '' : 's'}`;
1838
1848
  } else {
1839
1849
  this.fileIndicator.textContent = totalFiles > 0
1840
- ? `${visibleIndex} / ${totalFiles} files`
1841
- : '0 / 0 files';
1850
+ ? `${visibleIndex} of ${totalFiles}`
1851
+ : '0 files';
1842
1852
  }
1843
1853
  }
1844
1854
 
@@ -1859,6 +1869,7 @@ export class GitDiffViewController {
1859
1869
  const deletions = allFilesAreImages
1860
1870
  ? 0
1861
1871
  : Math.max(0, rawDeletions - imageLineStats.deletions);
1872
+ // pi-lens-ignore: ast-grep:no-inner-html-js
1862
1873
  this.stats.innerHTML = `
1863
1874
  <span class="ui-stat-token ui-stat-token--add diff-stats-add">+${additions}</span>
1864
1875
  <span class="ui-stat-token ui-stat-token--del diff-stats-del">-${deletions}</span>
@@ -1874,8 +1885,10 @@ export class GitDiffViewController {
1874
1885
  });
1875
1886
 
1876
1887
  const hasCurrentFile = Boolean(this.getCurrentFile()?.path);
1888
+ const hasEditorFile = hasCurrentFile || Boolean(this.workspaceFilePath);
1877
1889
  const actionState = this.getCurrentActionState();
1878
1890
  const primaryAction = this.getPrimaryAction();
1891
+ const includedFileCount = Number(this.repoStatus?.summary?.staged || 0);
1879
1892
 
1880
1893
  this.backToHistoryButton?.classList.toggle('hidden', !isCommitSource);
1881
1894
  this.gitActionsGroup?.classList.toggle('hidden', isCommitSource);
@@ -1885,23 +1898,33 @@ export class GitDiffViewController {
1885
1898
  this.stats?.classList.remove('hidden');
1886
1899
 
1887
1900
  if (this.openEditorButton) {
1888
- this.openEditorButton.textContent = 'Open in Editor';
1901
+ this.openEditorButton.textContent = 'Open in editor';
1889
1902
  }
1890
- this.openEditorButton?.toggleAttribute('disabled', !hasCurrentFile || isCommitSource);
1903
+ this.openEditorButton?.toggleAttribute('disabled', !hasEditorFile || isCommitSource);
1891
1904
  if (this.primaryActionButton) {
1905
+ const emphasizeInclude = primaryAction === 'stage' && !actionState.canUnstage;
1906
+ this.primaryActionButton.classList.toggle('ui-button--primary', emphasizeInclude);
1907
+ this.primaryActionButton.classList.toggle('ui-button--secondary', !emphasizeInclude);
1908
+ this.primaryActionButton.classList.toggle('ui-button--surface', !emphasizeInclude);
1892
1909
  this.primaryActionButton.textContent = this.pendingAction === primaryAction
1893
1910
  ? 'Working...'
1894
1911
  : primaryAction === 'unstage'
1895
- ? 'Unstage'
1896
- : 'Stage';
1912
+ ? 'Remove'
1913
+ : 'Include in commit';
1897
1914
  this.primaryActionButton.toggleAttribute(
1898
1915
  'disabled',
1899
1916
  isCommitSource || !hasCurrentFile || !primaryAction || Boolean(this.pendingAction),
1900
1917
  );
1901
1918
  }
1902
1919
  if (this.commitButton) {
1903
- this.commitButton.textContent = this.pendingAction === 'commit' ? 'Working...' : 'Commit Staged';
1920
+ this.commitButton.textContent = this.pendingAction === 'commit'
1921
+ ? 'Working...'
1922
+ : commitButtonLabel(includedFileCount);
1904
1923
  }
1924
+ this.commitButton?.classList.toggle(
1925
+ 'hidden',
1926
+ isCommitSource || !hasCurrentFile || !actionState.canUnstage,
1927
+ );
1905
1928
  this.commitButton?.toggleAttribute(
1906
1929
  'disabled',
1907
1930
  isCommitSource || !actionState.canCommit || Boolean(this.pendingAction),
@@ -1934,8 +1957,10 @@ export class GitDiffViewController {
1934
1957
  this.drawioPreviewPayloads.clear();
1935
1958
 
1936
1959
  if (this.source === 'commit') {
1960
+ // pi-lens-ignore: ast-grep:no-inner-html-js
1937
1961
  this.content.innerHTML = `${this.renderCommitHeader()}${this.renderCommitBody()}`;
1938
1962
  } else {
1963
+ // pi-lens-ignore: ast-grep:no-inner-html-js
1939
1964
  this.content.innerHTML = this.renderFocusedFileBody();
1940
1965
  }
1941
1966
  this.syncToolbar();
@@ -1,4 +1,5 @@
1
1
  import { escapeHtml } from '../domain/vault-utils.js';
2
+ import { commitButtonLabel } from '../domain/git-labels.js';
2
3
  import { getVaultPathLeaf, getVaultPathParent } from '../domain/vault-paths.js';
3
4
  import { buttonClassNames } from './components/ui/button.js';
4
5
  import { segmentedButtonClassNames, segmentedControlClassNames } from './components/ui/segmented-control.js';
@@ -659,12 +660,13 @@ export class GitPanelController {
659
660
  }
660
661
 
661
662
  const isCollapsed = this.collapsedSections.has(section.key);
663
+ const label = section.key === 'staged' ? 'Included in Next Commit' : section.label;
662
664
 
663
665
  return `
664
666
  <section class="git-section">
665
667
  <button class="git-section-header" type="button" data-git-section-toggle="${escapeHtml(section.key)}">
666
668
  ${chevronSvg(isCollapsed)}
667
- ${escapeHtml(section.label)}
669
+ ${escapeHtml(label)}
668
670
  <span class="ui-pill-badge ui-pill-badge--count ui-pill-badge--muted git-section-count">${files.length}</span>
669
671
  </button>
670
672
  <div class="git-file-list${isCollapsed ? ' hidden' : ''}">
@@ -724,8 +726,8 @@ export class GitPanelController {
724
726
  const fullPath = String(file.path || '');
725
727
  const statusClass = badgeClass(file.status);
726
728
  const stageAction = file.scope === 'staged'
727
- ? { label: 'Unstage', value: 'unstage' }
728
- : { label: 'Stage', value: 'stage' };
729
+ ? { label: 'Remove from commit', value: 'unstage' }
730
+ : { label: 'Include in commit', value: 'stage' };
729
731
  const actionKey = `${stageAction.value}:${file.scope}:${file.path}`;
730
732
  const isPending = this.pendingActionKey === actionKey;
731
733
  const resetActionKey = `reset:${file.scope}:${file.path}`;
@@ -874,12 +876,12 @@ export class GitPanelController {
874
876
  .filter(Boolean)
875
877
  .join('');
876
878
  const hasChanges = Boolean(this.status.summary?.changedFiles);
877
- const hasStagedChanges = Number(this.status.summary?.staged || 0) > 0;
879
+ const includedFileCount = Number(this.status.summary?.staged || 0);
878
880
  const isCommitPending = this.pendingActionKey === 'commit-staged';
879
881
 
880
882
  return `
881
883
  ${pullBackupsMarkup}
882
- ${sectionMarkup || '<div class="git-panel-empty">No local changes</div>'}
884
+ ${sectionMarkup || `<div class="git-panel-empty">${this.searchQuery && hasChanges ? 'No changes match your filter.' : 'No local changes'}</div>`}
883
885
  ${hasChanges ? `
884
886
  <div class="git-panel-footer">
885
887
  <div class="git-panel-footer-actions">
@@ -891,9 +893,9 @@ export class GitPanelController {
891
893
  class="${buttonClassNames({ variant: 'primary', action: true, wide: true, extra: 'git-footer-commit-btn' })}"
892
894
  type="button"
893
895
  data-git-commit-staged
894
- ${!hasStagedChanges || isCommitPending ? 'disabled' : ''}
896
+ ${includedFileCount === 0 || isCommitPending ? 'disabled' : ''}
895
897
  >
896
- ${isCommitPending ? 'Working...' : 'Commit Staged'}
898
+ ${isCommitPending ? 'Working...' : commitButtonLabel(includedFileCount)}
897
899
  </button>
898
900
  </div>
899
901
  </div>
@@ -906,6 +908,7 @@ export class GitPanelController {
906
908
  return;
907
909
  }
908
910
 
911
+ // pi-lens-ignore: ast-grep:no-inner-html-js
909
912
  this.panel.innerHTML = `
910
913
  <div class="git-panel-empty ui-empty-state ui-empty-state--compact">
911
914
  <p class="ui-empty-state-copy">${escapeHtml(message)}</p>
@@ -933,6 +936,7 @@ export class GitPanelController {
933
936
  const isPullPending = this.pendingActionKey === 'sync:pull';
934
937
  const isPushPending = this.pendingActionKey === 'sync:push';
935
938
 
939
+ // pi-lens-ignore: ast-grep:no-inner-html-js
936
940
  this.panel.innerHTML = `
937
941
  <div class="git-branch-bar">
938
942
  <div class="git-branch-meta">
@@ -947,7 +951,7 @@ export class GitPanelController {
947
951
  class="${buttonClassNames({ variant: 'secondary', size: 'compact', pill: true, surface: true, extra: 'ui-action-pill' })}"
948
952
  type="button"
949
953
  data-git-sync-action="pull"
950
- title="${hasUpstream ? 'Pull remote changes (fast-forward only)' : 'No upstream branch configured'}"
954
+ title="${hasUpstream ? 'Pull remote changes' : 'No upstream branch configured'}"
951
955
  aria-label="Pull branch"
952
956
  ${!hasUpstream || isPullPending ? 'disabled' : ''}
953
957
  >
@@ -1,5 +1,4 @@
1
- import { stripVaultFileExtension } from '../../domain/file-kind.js';
2
- import { escapeHtml } from '../domain/vault-utils.js';
1
+ import { createFileSearchEntry, findFileSearchMatch } from '../domain/file-search.js';
3
2
  import { getVaultFileIconSvg } from './file-icon-svg.js';
4
3
  import {
5
4
  DEFAULT_SEARCH_DEBOUNCE_MS,
@@ -17,108 +16,15 @@ function getRawFileName(filePath) {
17
16
  return String(filePath ?? '').split('/').pop() || String(filePath ?? '');
18
17
  }
19
18
 
20
- function createCorpusEntry(filePath) {
21
- const displayName = stripVaultFileExtension(filePath);
22
- const fileName = displayName.split('/').pop() || displayName;
23
- const rawFileName = getRawFileName(filePath);
24
-
25
- return {
26
- displayName,
27
- fileName,
28
- filePath,
29
- lowerDisplayName: displayName.toLowerCase(),
30
- lowerFileName: fileName.toLowerCase(),
31
- lowerPath: String(filePath).toLowerCase(),
32
- lowerRawFileName: rawFileName.toLowerCase(),
33
- };
34
- }
35
-
36
19
  function getFileName(filePath) {
37
- const displayName = stripVaultFileExtension(filePath);
38
- return displayName.split('/').pop() || displayName;
20
+ return createFileSearchEntry(filePath).fileName;
39
21
  }
40
22
 
41
23
  function getDirPath(filePath) {
42
- const displayName = stripVaultFileExtension(filePath);
24
+ const displayName = createFileSearchEntry(filePath).displayName;
43
25
  return displayName.includes('/') ? displayName.substring(0, displayName.lastIndexOf('/')) : '';
44
26
  }
45
27
 
46
- function createRangeIndices(start, length) {
47
- return Array.from({ length }, (_, index) => start + index);
48
- }
49
-
50
- function findFuzzyMatch(text, query) {
51
- const indices = [];
52
- let queryIndex = 0;
53
- let score = 0;
54
- let consecutiveBonus = 0;
55
-
56
- for (let index = 0; index < text.length && queryIndex < query.length; index += 1) {
57
- if (text[index] !== query[queryIndex]) {
58
- consecutiveBonus = 0;
59
- continue;
60
- }
61
-
62
- queryIndex += 1;
63
- indices.push(index);
64
- consecutiveBonus += 1;
65
- score += consecutiveBonus;
66
-
67
- if (
68
- index === 0
69
- || text[index - 1] === '/'
70
- || text[index - 1] === '-'
71
- || text[index - 1] === '_'
72
- || text[index - 1] === ' '
73
- ) {
74
- score += 5;
75
- }
76
- }
77
-
78
- return queryIndex === query.length && score > 0
79
- ? { indices, score }
80
- : null;
81
- }
82
-
83
- function findCorpusMatch(entry, query) {
84
- const fileIndex = entry.lowerFileName.indexOf(query);
85
- if (fileIndex >= 0) {
86
- return {
87
- indices: createRangeIndices(fileIndex + (entry.displayName.length - entry.fileName.length), query.length),
88
- score: 100 + (1 / entry.fileName.length),
89
- };
90
- }
91
-
92
- const pathIndex = entry.lowerDisplayName.indexOf(query);
93
- if (pathIndex >= 0) {
94
- return {
95
- indices: createRangeIndices(pathIndex, query.length),
96
- score: 50 + (1 / entry.displayName.length),
97
- };
98
- }
99
-
100
- const rawFileIndex = entry.lowerRawFileName.indexOf(query);
101
- if (rawFileIndex >= 0) {
102
- return {
103
- indices: createRangeIndices(
104
- rawFileIndex + (entry.displayName.length - entry.fileName.length),
105
- query.length,
106
- ),
107
- score: 40 + (1 / entry.lowerRawFileName.length),
108
- };
109
- }
110
-
111
- const rawPathIndex = entry.lowerPath.indexOf(query);
112
- if (rawPathIndex >= 0) {
113
- return {
114
- indices: createRangeIndices(rawPathIndex, query.length),
115
- score: 25 + (1 / entry.lowerPath.length),
116
- };
117
- }
118
-
119
- return findFuzzyMatch(entry.lowerDisplayName, query);
120
- }
121
-
122
28
  function splitMatchIndices(entry, indices = []) {
123
29
  const fileNameStart = entry.displayName.length - entry.fileName.length;
124
30
  return {
@@ -129,42 +35,25 @@ function splitMatchIndices(entry, indices = []) {
129
35
  };
130
36
  }
131
37
 
132
- function highlightText(text, indices = []) {
133
- if (indices.length === 0) {
134
- return escapeHtml(text);
135
- }
136
-
38
+ function appendHighlightedText(element, text, indices = []) {
137
39
  const matchedIndices = new Set(indices);
138
- let result = '';
139
- let runStart = null;
140
40
  let cursor = 0;
141
41
 
142
- const appendRun = (runEnd) => {
143
- result += escapeHtml(text.slice(cursor, runStart));
144
- result += `<mark>${escapeHtml(text.slice(runStart, runEnd + 1))}</mark>`;
145
- cursor = runEnd + 1;
146
- runStart = null;
147
- };
148
-
149
- for (let index = 0; index < text.length; index += 1) {
150
- if (!matchedIndices.has(index)) {
151
- if (runStart !== null) {
152
- appendRun(index - 1);
153
- }
154
- continue;
155
- }
42
+ while (cursor < text.length) {
43
+ const matched = matchedIndices.has(cursor);
44
+ let end = cursor + 1;
45
+ while (end < text.length && matchedIndices.has(end) === matched) end += 1;
156
46
 
157
- if (runStart === null) {
158
- runStart = index;
47
+ const value = text.slice(cursor, end);
48
+ if (matched) {
49
+ const mark = document.createElement('mark');
50
+ mark.textContent = value;
51
+ element.append(mark);
52
+ } else {
53
+ element.append(value);
159
54
  }
55
+ cursor = end;
160
56
  }
161
-
162
- if (runStart !== null) {
163
- appendRun(text.length - 1);
164
- }
165
-
166
- result += escapeHtml(text.slice(cursor));
167
- return result;
168
57
  }
169
58
 
170
59
  export class QuickSwitcherController {
@@ -193,6 +82,12 @@ export class QuickSwitcherController {
193
82
  this.hint = document.getElementById('quickSwitcherHint');
194
83
  this.scope = document.getElementById('quickSwitcherScope');
195
84
  this.modeTabs = Array.from(document.querySelectorAll?.('[data-qs-mode]') ?? []);
85
+ const searchConfig = this.getSearchConfig?.() ?? {};
86
+ const textTab = this.modeTabs.find((tab) => tab.dataset.qsMode === 'text');
87
+ if (textTab && searchConfig.available === false) {
88
+ textTab.disabled = true;
89
+ textTab.setAttribute('title', searchConfig.unavailableReason || 'Text search is unavailable');
90
+ }
196
91
 
197
92
  this.filteredFiles = [];
198
93
  this.fileMatches = new Map();
@@ -219,9 +114,32 @@ export class QuickSwitcherController {
219
114
  if (e.target === this.overlay) this.close();
220
115
  });
221
116
 
222
- this.modeTabs.forEach((tab) => {
117
+ this.overlay?.addEventListener('keydown', (e) => {
118
+ if (e.key === 'Escape') {
119
+ e.preventDefault();
120
+ this.close();
121
+ }
122
+ });
123
+
124
+ this.modeTabs.forEach((tab, index) => {
223
125
  tab.addEventListener('click', () => {
224
126
  this.setMode(tab.dataset.qsMode === 'text' ? 'text' : 'files', { preserveInput: true });
127
+ this.input?.focus?.();
128
+ });
129
+ tab.addEventListener('keydown', (event) => {
130
+ if (event.key !== 'ArrowLeft' && event.key !== 'ArrowRight') {
131
+ return;
132
+ }
133
+
134
+ event.preventDefault();
135
+ const nextIndex = (index + (event.key === 'ArrowRight' ? 1 : -1) + this.modeTabs.length)
136
+ % this.modeTabs.length;
137
+ const nextTab = this.modeTabs[nextIndex];
138
+ if (nextTab.disabled) {
139
+ return;
140
+ }
141
+ this.setMode(nextTab.dataset.qsMode === 'text' ? 'text' : 'files', { preserveInput: true });
142
+ nextTab.focus();
225
143
  });
226
144
  });
227
145
 
@@ -243,13 +161,9 @@ export class QuickSwitcherController {
243
161
  e.preventDefault();
244
162
  this.confirmSelection();
245
163
  break;
246
- case 'Escape':
247
- e.preventDefault();
248
- this.close();
249
- break;
250
164
  case 'Tab':
251
165
  e.preventDefault();
252
- if (this.modeTabs.length > 1 && !e.shiftKey) {
166
+ if (this.modeTabs.filter((tab) => !tab.disabled).length > 1 && !e.shiftKey) {
253
167
  this.setMode(this.mode === 'files' ? 'text' : 'files', { preserveInput: true });
254
168
  } else {
255
169
  this.moveSelection(e.shiftKey ? -1 : 1);
@@ -269,6 +183,7 @@ export class QuickSwitcherController {
269
183
  this.selectedTextIndex = 0;
270
184
  this.overlay.classList.add('visible');
271
185
  this.overlay.setAttribute('aria-hidden', 'false');
186
+ this.input?.setAttribute('aria-expanded', 'true');
272
187
  this.setMode('files', { preserveInput: true });
273
188
 
274
189
  // The overlay transitions visibility hidden→visible over 120ms.
@@ -325,6 +240,7 @@ export class QuickSwitcherController {
325
240
  this.isOpen = false;
326
241
  this.overlay.classList.remove('visible');
327
242
  this.overlay.setAttribute('aria-hidden', 'true');
243
+ this.input?.setAttribute('aria-expanded', 'false');
328
244
  this.input.value = '';
329
245
  this.resultsList.innerHTML = '';
330
246
  this.setActiveDescendant('');
@@ -344,7 +260,8 @@ export class QuickSwitcherController {
344
260
  }
345
261
 
346
262
  setMode(mode = 'files', { preserveInput = false } = {}) {
347
- const normalizedMode = mode === 'text' ? 'text' : 'files';
263
+ const textTab = this.modeTabs.find((tab) => tab.dataset.qsMode === 'text');
264
+ const normalizedMode = mode === 'text' && !textTab?.disabled ? 'text' : 'files';
348
265
  this.mode = normalizedMode;
349
266
  this.selectedIndex = 0;
350
267
  this.selectedTextIndex = 0;
@@ -357,6 +274,7 @@ export class QuickSwitcherController {
357
274
  const isActive = tab.dataset.qsMode === normalizedMode;
358
275
  tab.classList.toggle('active', isActive);
359
276
  tab.setAttribute('aria-selected', isActive ? 'true' : 'false');
277
+ tab.setAttribute('tabindex', isActive ? '0' : '-1');
360
278
  });
361
279
 
362
280
  this.scope?.classList.toggle('hidden', normalizedMode !== 'text');
@@ -386,11 +304,12 @@ export class QuickSwitcherController {
386
304
  }
387
305
 
388
306
  filterFiles() {
389
- const query = this.input?.value.trim().toLowerCase() ?? '';
307
+ this.resultsList?.setAttribute('aria-busy', 'false');
308
+ const query = this.input?.value.trim().toLowerCase().replace(/\s+/gu, ' ') ?? '';
390
309
  const allFiles = this.getFileList?.() ?? [];
391
310
  if (allFiles !== this.lastFileListRef) {
392
311
  this.lastFileListRef = allFiles;
393
- this.fileCorpus = allFiles.map((filePath) => createCorpusEntry(filePath));
312
+ this.fileCorpus = allFiles.map((filePath) => createFileSearchEntry(filePath));
394
313
  }
395
314
 
396
315
  this.fileMatches.clear();
@@ -423,7 +342,7 @@ export class QuickSwitcherController {
423
342
  const ranked = [];
424
343
  let fileMatchCount = 0;
425
344
  this.fileCorpus.forEach((entry) => {
426
- const match = findCorpusMatch(entry, query);
345
+ const match = findFileSearchMatch(entry, query);
427
346
  if (!match) {
428
347
  return;
429
348
  }
@@ -517,11 +436,20 @@ export class QuickSwitcherController {
517
436
  const corpusEntry = this.fileCorpus.find((entry) => entry.filePath === filePath);
518
437
  const matchIndices = corpusEntry ? splitMatchIndices(corpusEntry, match?.indices) : { dirPath: [], fileName: [] };
519
438
 
520
- item.innerHTML = `
521
- ${getVaultFileIconSvg(filePath, { className: 'qs-result-icon' })}
522
- <span class="qs-result-name">${highlightText(fileName, matchIndices.fileName)}</span>
523
- ${dirPath ? `<span class="qs-result-path">${highlightText(dirPath, matchIndices.dirPath)}</span>` : ''}
524
- `;
439
+ const svg = getVaultFileIconSvg(filePath, { className: 'qs-result-icon' });
440
+ item.append(document.createRange().createContextualFragment(svg));
441
+ const name = document.createElement('span');
442
+ name.className = 'qs-result-name';
443
+ appendHighlightedText(name, fileName, matchIndices.fileName);
444
+ item.append(name);
445
+
446
+ if (dirPath) {
447
+ const path = document.createElement('span');
448
+ path.className = 'qs-result-path';
449
+ path.title = dirPath;
450
+ appendHighlightedText(path, dirPath, matchIndices.dirPath);
451
+ item.append(path);
452
+ }
525
453
 
526
454
  item.addEventListener('click', () => {
527
455
  this.selectedIndex = index;
@@ -606,11 +534,16 @@ export class QuickSwitcherController {
606
534
 
607
535
  const header = document.createElement('div');
608
536
  header.className = 'qs-text-group-header';
609
- header.innerHTML = `
610
- <span class="qs-text-file-name">${escapeHtml(getFileName(fileGroup.file))}</span>
611
- <span class="qs-text-file-meta">${escapeHtml(getDirPath(fileGroup.file))}</span>
612
- <span class="qs-text-count">${escapeHtml(formatMatchCount(fileGroup.matchCount, { truncated: fileGroup.truncated }))}</span>
613
- `;
537
+ [
538
+ ['qs-text-file-name', getFileName(fileGroup.file)],
539
+ ['qs-text-file-meta', getDirPath(fileGroup.file)],
540
+ ['qs-text-count', formatMatchCount(fileGroup.matchCount, { truncated: fileGroup.truncated })],
541
+ ].forEach(([className, text]) => {
542
+ const span = document.createElement('span');
543
+ span.className = className;
544
+ span.textContent = text;
545
+ header.append(span);
546
+ });
614
547
  group.appendChild(header);
615
548
 
616
549
  (fileGroup.snippets ?? []).forEach((snippet) => {
@@ -625,10 +558,13 @@ export class QuickSwitcherController {
625
558
  item.setAttribute('role', 'option');
626
559
  item.setAttribute('aria-selected', itemIndex === this.selectedTextIndex ? 'true' : 'false');
627
560
  item.dataset.textIndex = String(itemIndex);
628
- item.innerHTML = `
629
- <span class="qs-text-line">L${escapeHtml(String(snippet.line ?? 1))}</span>
630
- <span class="qs-text-snippet">${this.highlightSnippet(snippet)}</span>
631
- `;
561
+ const line = document.createElement('span');
562
+ line.className = 'qs-text-line';
563
+ line.textContent = `L${snippet.line ?? 1}`;
564
+ const snippetText = document.createElement('span');
565
+ snippetText.className = 'qs-text-snippet';
566
+ this.appendHighlightedSnippet(snippetText, snippet);
567
+ item.append(line, snippetText);
632
568
  item.addEventListener('click', () => {
633
569
  this.selectedTextIndex = itemIndex;
634
570
  this.confirmSelection();
@@ -648,11 +584,14 @@ export class QuickSwitcherController {
648
584
  this.updateSelection();
649
585
  }
650
586
 
651
- highlightSnippet(snippet = {}) {
587
+ appendHighlightedSnippet(element, snippet = {}) {
652
588
  const text = String(snippet.text ?? '');
653
589
  const start = Math.min(Math.max(Number(snippet.matchStart) || 0, 0), text.length);
654
590
  const end = Math.min(Math.max(Number(snippet.matchEnd) || start, start), text.length);
655
- return `${escapeHtml(text.slice(0, start))}<mark>${escapeHtml(text.slice(start, end))}</mark>${escapeHtml(text.slice(end))}`;
591
+ element.append(text.slice(0, start));
592
+ const mark = document.createElement('mark');
593
+ mark.textContent = text.slice(start, end);
594
+ element.append(mark, text.slice(end));
656
595
  }
657
596
 
658
597
  moveSelection(delta) {