collabmd 0.1.47 → 0.1.49

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 (305) hide show
  1. package/README.md +70 -11
  2. package/bin/collabmd.js +3 -0
  3. package/dist/client/assets/CodeMirrorEditor-UCMRAMCV-C-nmgL5g.js +1 -0
  4. package/dist/client/assets/JetBrainsMono-Regular-BQaDgvhP.woff2 +0 -0
  5. package/dist/client/assets/abnfDiagram-VCTEODGH-CdVZqicp.js +1 -0
  6. package/dist/client/assets/{architectureDiagram-5GKGNRK7-I39sYjHI.js → architectureDiagram-5GKGNRK7-wUo0bXif.js} +3 -3
  7. package/dist/client/assets/babel-CY5KOv-I.js +16 -0
  8. package/dist/client/assets/{blockDiagram-NRAW4CY4-C6DtJy5Y.js → blockDiagram-I7D4REHJ-BRSK3m49.js} +4 -4
  9. package/dist/client/assets/browser-utils-DbXSMI-6.js +1 -0
  10. package/dist/client/assets/{c4Diagram-UCG6FXSJ-CtWtxS9_.js → c4Diagram-7LVT6UL2-yD2poxqw.js} +1 -1
  11. package/dist/client/assets/channel-Fa4GS_-F.js +1 -0
  12. package/dist/client/assets/chunk-2E4U76K2-BPnTT-J3.js +2 -0
  13. package/dist/client/assets/{chunk-TEH6E4GO-DVayPj-q.js → chunk-4HAMMTFA-DStbIfJ1.js} +20 -20
  14. package/dist/client/assets/{chunk-75Z2AOVW-CdFSaPjz.js → chunk-75Z2AOVW-C5pGd3W9.js} +1 -1
  15. package/dist/client/assets/{chunk-CLGD4ZFX-DQLgbQrv.js → chunk-CLGD4ZFX-BMeArk03.js} +1 -1
  16. package/dist/client/assets/chunk-DU6HZSFF-Zkp69c6c.js +127 -0
  17. package/dist/client/assets/{chunk-F27PBJKO-CNHIFhjo.js → chunk-F27PBJKO-D4xNct1r.js} +1 -1
  18. package/dist/client/assets/{chunk-GMAD6QVW-CbcBqPM5.js → chunk-GMAD6QVW-NZ6WpDb8.js} +1 -1
  19. package/dist/client/assets/{chunk-OBVCFTLP-BJR7u1jh.js → chunk-GVQU2GXP-D8SDprfX.js} +1 -1
  20. package/dist/client/assets/{chunk-G27WJ6UU-D9-eg537.js → chunk-IMKFNOWR-D8jEuU3Q.js} +2 -2
  21. package/dist/client/assets/{chunk-JQ64N6SF-DSFNH4bh.js → chunk-L3NEJ4N5-aijKP-eh.js} +1 -1
  22. package/dist/client/assets/chunk-OSK3NFVY-CCbLQAMa.js +10 -0
  23. package/dist/client/assets/{chunk-P2QGCYS3-BRpqY32H.js → chunk-P2QGCYS3-Oag_FdCq.js} +1 -1
  24. package/dist/client/assets/{chunk-POPQ4Y6H-Cb4SlVZF.js → chunk-POPQ4Y6H-CYnpIgXT.js} +1 -1
  25. package/dist/client/assets/{chunk-PWAF6VOD-Cz8IwBB5.js → chunk-PWAF6VOD-JSNrcBmP.js} +1 -1
  26. package/dist/client/assets/{chunk-RM4UPSZO-CWRwxNWJ.js → chunk-RM4UPSZO-DOWllDY8.js} +4 -4
  27. package/dist/client/assets/{chunk-RHFEMEQ7-CWY8cf4H.js → chunk-SHT3W25Y-BiQGljao.js} +5 -5
  28. package/dist/client/assets/{chunk-SVP7TREG-CyHOmcac.js → chunk-SVP7TREG-DB485R5x.js} +1 -1
  29. package/dist/client/assets/{chunk-LCL6LL3I-DmDkVukh.js → chunk-TICWLB2K-8rAVvoS3.js} +4 -4
  30. package/dist/client/assets/{chunk-CKBBP62Z-DG26UiBv.js → chunk-TLUHSLCS-DTY5v0aj.js} +2 -2
  31. package/dist/client/assets/classDiagram-ZZMXUADV-BsQWoR3Q.js +1 -0
  32. package/dist/client/assets/classDiagram-v2-VYDZK3BY-BsQWoR3Q.js +1 -0
  33. package/dist/client/assets/{comment-thread-store-CdcwrObc.js → comment-thread-store-CX98zR6q.js} +1 -1
  34. package/dist/client/assets/comment-threads-BnRwo5B9.js +4 -0
  35. package/dist/client/assets/{cynefinDiagram-5FMLGOSQ-CdTPi2yO.js → cynefinDiagram-5FMLGOSQ-rTz9RhFH.js} +1 -1
  36. package/dist/client/assets/{dagre-3AP2YEHR-5gtJtd0O.js → dagre-GXQ25YYZ-Cr67wLpQ.js} +2 -2
  37. package/dist/client/assets/{diagram-S7CK7UJ4-BudWz9WU.js → diagram-S7CK7UJ4-DywERe__.js} +2 -2
  38. package/dist/client/assets/{diagram-UQ7AKVKN-3v4rbH-s.js → diagram-UQ7AKVKN-CNYlXtu4.js} +1 -1
  39. package/dist/client/assets/diagram-VSXAHHWV-0H1a61Bn.js +3 -0
  40. package/dist/client/assets/{diagram-VX7I27RA-C_-rj0sw.js → diagram-VX7I27RA-CsJbsx5F.js} +1 -1
  41. package/dist/client/assets/{diagram-Z3DM3KII-B4yxavgj.js → diagram-Z3DM3KII-D0mLxEgI.js} +1 -1
  42. package/dist/client/assets/dist--hk9I5HQ.js +1 -0
  43. package/dist/client/assets/{dist-DutPuFmC.js → dist-B1XEa-Zs.js} +1 -1
  44. package/dist/client/assets/dist-Bd1JucQF.js +1 -0
  45. package/dist/client/assets/{dist-DZAAWTG-.js → dist-C0G3AaXG.js} +1 -1
  46. package/dist/client/assets/dist-ChRoo3IB.js +9 -0
  47. package/dist/client/assets/dist-Dm4w5UId.js +1 -0
  48. package/dist/client/assets/{dist-B_yQaEv_.js → dist-DpYDHR9p.js} +1 -1
  49. package/dist/client/assets/{dist-CietwTVS.js → dist-DzNmC81X.js} +1 -1
  50. package/dist/client/assets/{dist-DDUn5zTz.js → dist-GvvCNBe4.js} +1 -1
  51. package/dist/client/assets/dist-JaU3aRlD.js +19 -0
  52. package/dist/client/assets/{dist-Bw3VIE_v.js → dist-TW8iFRyW.js} +1 -1
  53. package/dist/client/assets/{dist-Dir5dDKZ.js → dist-jVU5Be5P.js} +1 -1
  54. package/dist/client/assets/document-formatter-rDDjRfZ2.js +2 -0
  55. package/dist/client/assets/drawioEditor-JubEEha7.js +9 -0
  56. package/dist/client/assets/ebnfDiagram-PWID7BFC-HeTxHpCU.js +1 -0
  57. package/dist/client/assets/editor-session-Bk6oIvSr.js +54 -0
  58. package/dist/client/assets/embedded-editor-base-BlCgz6mw.css +1 -0
  59. package/dist/client/assets/{erDiagram-SSCWMZ5O-C-iy6VkX.js → erDiagram-RLTQ6QDP-CbrDdd5P.js} +2 -2
  60. package/dist/client/assets/{es-CAsWR6hG.js → es-Bfdp096f.js} +13 -13
  61. package/dist/client/assets/estree-CuT_pB8K.js +44 -0
  62. package/dist/client/assets/excalidraw-element-link-YLIaTnVF.js +7 -0
  63. package/dist/client/assets/{excalidraw-scene-BOben2gr.js → excalidraw-scene-6uROebca.js} +1 -1
  64. package/dist/client/assets/{excalidraw-editor-BlcN6umJ.css → excalidrawEditor-BbSbI80a.css} +1 -1
  65. package/dist/client/assets/excalidrawEditor-dBMbRX_S.js +9 -0
  66. package/dist/client/assets/export-page-CRRQ2NEl.js +57 -0
  67. package/dist/client/assets/exportDocument-BlQRd9_m.js +2 -0
  68. package/dist/client/assets/exportDocument-CNSbjZwu.css +1 -0
  69. package/dist/client/assets/{file-icon-svg-CQAjlWjK.js → file-icon-svg-Y_kpF957.js} +1 -1
  70. package/dist/client/assets/file-kind-BwMIcZbi.js +1 -0
  71. package/dist/client/assets/file-search-DDLyyLDU.js +1 -0
  72. package/dist/client/assets/flowDiagram-HODETNUW-D5Gy0wmg.js +1 -0
  73. package/dist/client/assets/{ganttDiagram-EL5Y4UJY-DtKHAbez.js → ganttDiagram-EL5Y4UJY-BHgOYiZz.js} +1 -1
  74. package/dist/client/assets/{gitGraphDiagram-WWUBYQGX-oue8fQ5o.js → gitGraphDiagram-WWUBYQGX-Dl__X3Sk.js} +1 -1
  75. package/dist/client/assets/html-Cdnr2qzU.js +25 -0
  76. package/dist/client/assets/image-ZVKINSPA-DgzW5zAy.js +1 -0
  77. package/dist/client/assets/index-D3RER1SZ.js +1023 -0
  78. package/dist/client/assets/index-DREVKl_K.css +1 -0
  79. package/dist/client/assets/{infoDiagram-RXCK75RN-BMzo6KIn.js → infoDiagram-27XIBGKW-DBpoANC9.js} +1 -1
  80. package/dist/client/assets/{ishikawaDiagram-5VMMS53U-BIM3h287.js → ishikawaDiagram-5VMMS53U-BMPC6hfp.js} +1 -1
  81. package/dist/client/assets/{journeyDiagram-EYS64GPL-mF6Lh8O-.js → journeyDiagram-3NMN7TZE-B1sApUmM.js} +2 -2
  82. package/dist/client/assets/{kanban-definition-3QL26DDD-DLN2NyoR.js → kanban-definition-UXKFOSKX-C9yP4td1.js} +4 -4
  83. package/dist/client/assets/{line-BfTWJmgf.js → line-BFl3ujec.js} +1 -1
  84. package/dist/client/assets/markdown-CByV-4CU.js +70 -0
  85. package/dist/client/assets/{mermaid.core-CLZBlpIW.js → mermaid.core-BrY09czX.js} +5 -5
  86. package/dist/client/assets/mermaid.core-DfcNfOTv.js +1 -0
  87. package/dist/client/assets/{mindmap-definition-FBJOCRG2-CRyHaHTk.js → mindmap-definition-YA3MSWOX-BUQHcYbK.js} +8 -8
  88. package/dist/client/assets/pegDiagram-XKGWAZYB-Bi2Iml7Z.js +1 -0
  89. package/dist/client/assets/{pieDiagram-E7YTZNPT-B5OZPb88.js → pieDiagram-E7YTZNPT-_gYXnMoi.js} +1 -1
  90. package/dist/client/assets/preview-render-compiler-CfxJYldJ.js +41 -0
  91. package/dist/client/assets/preview-render-compiler-Cu8_i9iS.js +1 -0
  92. package/dist/client/assets/{preview-render-worker-BmN79bVc.js → preview-render-worker-c4Yi93qW.js} +41 -35
  93. package/dist/client/assets/prod-BiJL30sl.js +1 -0
  94. package/dist/client/assets/prod-qAk7rHa4.js +177 -0
  95. package/dist/client/assets/{quadrantDiagram-AXDQQJYC-Drrw14YH.js → quadrantDiagram-AXDQQJYC-DwexhEhd.js} +1 -1
  96. package/dist/client/assets/quick-switcher-controller-BoIK_wuG.js +1 -0
  97. package/dist/client/assets/railroadDiagram-O6MQD6OU-BVA5UbMu.js +1 -0
  98. package/dist/client/assets/{requirementDiagram-EFPCY7ZU-CRZG6mRB.js → requirementDiagram-BXWQKSXE-C4cKwiIw.js} +2 -2
  99. package/dist/client/assets/runtime-config-DcmnGcXU.js +1 -0
  100. package/dist/client/assets/{sankeyDiagram-P5KCCOFB-DvarEJpo.js → sankeyDiagram-P5KCCOFB-B5HHkRT2.js} +1 -1
  101. package/dist/client/assets/{sequenceDiagram-WJ2MYXX4-B4oWlb-w.js → sequenceDiagram-WJ2MYXX4-BX6jqJFD.js} +6 -6
  102. package/dist/client/assets/standalone-6z5x8CWe.js +29 -0
  103. package/dist/client/assets/{stateDiagram-HBIQ2CUA-qbHIIqRZ.js → stateDiagram-D77RDMKH-CmqxyRGx.js} +1 -1
  104. package/dist/client/assets/stateDiagram-v2-MP3YSRHH-zoLLQVyh.js +1 -0
  105. package/dist/client/assets/{swimlanes-XN3QIQJK-xB6CtUGj.js → swimlanes-42K2YHIH-Bmgb9Zld.js} +1 -1
  106. package/dist/client/assets/swimlanesDiagram-VR7AAH4N-DS-KQN37.js +8 -0
  107. package/dist/client/assets/{timeline-definition-24CTP7MA-Lj_yTNlY.js → timeline-definition-24CTP7MA-BA-lVH6P.js} +1 -1
  108. package/dist/client/assets/vault-api-client-iZLhMJKe.js +6 -0
  109. package/dist/client/assets/{vennDiagram-4TSXK5OY-BekRp5Y0.js → vennDiagram-4TSXK5OY-DNdUC_Xj.js} +1 -1
  110. package/dist/client/assets/{wardleyDiagram-VM6X3IG4-kdnV_Hdm.js → wardleyDiagram-VM6X3IG4-C2oY-YbF.js} +1 -1
  111. package/dist/client/assets/wiki-link-resolver-DFIwErjc.js +1 -0
  112. package/dist/client/assets/{xychartDiagram-S5SC5T6Z-DufGwNSB.js → xychartDiagram-S5SC5T6Z-CoIR8CBX.js} +1 -1
  113. package/dist/client/assets/yaml-9ltHPDNO.js +154 -0
  114. package/dist/client/assets/{yjs-provider-reset-guard-DE0vINDP.js → yjs-provider-reset-guard-9ul1qF6k.js} +1 -1
  115. package/dist/client/drawio-editor.html +11 -3
  116. package/dist/client/excalidraw-editor.html +45 -2
  117. package/dist/client/export-document.html +3 -3
  118. package/dist/client/index.html +106 -52
  119. package/integrations/pi/index.ts +94 -0
  120. package/package.json +41 -29
  121. package/src/client/app/drawio-editor.html +2 -2
  122. package/src/client/app/excalidraw-editor.html +2 -2
  123. package/src/client/app/export-document.html +1 -1
  124. package/src/client/app/index.html +88 -50
  125. package/src/client/app/main-entry.js +15 -1
  126. package/src/client/application/app-shell/chat-feature.js +7 -22
  127. package/src/client/application/app-shell/export-feature.js +17 -1
  128. package/src/client/application/app-shell/git-feature.js +51 -14
  129. package/src/client/application/app-shell/presence-feature.js +8 -25
  130. package/src/client/application/app-shell/ui-feature-identity.js +13 -0
  131. package/src/client/application/app-shell/ui-feature-shell.js +199 -99
  132. package/src/client/application/app-shell/ui-feature-sidebar.js +43 -7
  133. package/src/client/application/app-shell/ui-feature-tab-activity.js +19 -6
  134. package/src/client/application/app-shell/ui-feature-toolbar.js +16 -129
  135. package/src/client/application/app-shell-elements.js +8 -0
  136. package/src/client/application/preview-render-compiler.js +1 -87
  137. package/src/client/application/preview-render-scheduler.js +8 -24
  138. package/src/client/application/preview-renderer.js +2 -2
  139. package/src/client/application/webmcp-diagram-renderer.js +47 -0
  140. package/src/client/application/workspace-coordinator.js +23 -4
  141. package/src/client/application/workspace-preview-controller.js +120 -2
  142. package/src/client/application/workspace-route-controller.js +11 -1
  143. package/src/client/assets/vendor/jetbrains-mono/JetBrainsMono-Regular.woff2 +0 -0
  144. package/src/client/assets/vendor/jetbrains-mono/OFL.txt +93 -0
  145. package/src/client/bootstrap/collabmd-app-shell.js +99 -6
  146. package/src/client/domain/document-formatter.js +193 -0
  147. package/src/client/domain/excalidraw-element-link.js +73 -0
  148. package/src/client/domain/excalidraw-scene.js +21 -0
  149. package/src/client/domain/file-search.js +73 -0
  150. package/src/client/domain/git-labels.js +5 -0
  151. package/src/client/domain/hash-routes.js +8 -0
  152. package/src/client/domain/room.js +1 -5
  153. package/src/client/domain/runtime-paths.js +5 -0
  154. package/src/client/domain/vault-paths.js +6 -4
  155. package/src/client/domain/wiki-link-completions.js +20 -20
  156. package/src/client/drawio-editor.js +8 -3
  157. package/src/client/excalidraw-editor.js +287 -16
  158. package/src/client/export/export-host.js +59 -36
  159. package/src/client/export/export-page.js +14 -4
  160. package/src/client/export/export-pipeline.js +270 -42
  161. package/src/client/infrastructure/agent-connection-api-client.js +23 -0
  162. package/src/client/infrastructure/auth-client.js +3 -13
  163. package/src/client/infrastructure/editor-session.js +15 -0
  164. package/src/client/infrastructure/editor-view-adapter.js +73 -14
  165. package/src/client/infrastructure/excalidraw-room-client.js +80 -1
  166. package/src/client/infrastructure/git-api-client.js +39 -33
  167. package/src/client/infrastructure/lobby-presence.js +2 -5
  168. package/src/client/infrastructure/runtime-config.js +11 -1
  169. package/src/client/infrastructure/tab-activity-lock.js +2 -5
  170. package/src/client/infrastructure/vault-api-client.js +33 -26
  171. package/src/client/infrastructure/webmcp-tool-registry.js +283 -0
  172. package/src/client/infrastructure/workspace-sync-client.js +6 -4
  173. package/src/client/presentation/agent-connection-controller.js +255 -0
  174. package/src/client/presentation/backlinks-panel.js +3 -0
  175. package/src/client/presentation/bases-preview-controller.js +59 -26
  176. package/src/client/presentation/comment-ui/comment-ui-card.js +4 -2
  177. package/src/client/presentation/create-menu-presenter.js +19 -16
  178. package/src/client/presentation/excalidraw-embed-controller.js +164 -4
  179. package/src/client/presentation/file-action-controller.js +15 -2
  180. package/src/client/presentation/file-explorer-controller.js +3 -0
  181. package/src/client/presentation/file-explorer-view.js +71 -12
  182. package/src/client/presentation/file-history-view-controller.js +19 -12
  183. package/src/client/presentation/file-tree-state.js +1 -11
  184. package/src/client/presentation/git-diff-view-controller.js +70 -53
  185. package/src/client/presentation/git-panel-controller.js +86 -32
  186. package/src/client/presentation/image-lightbox-controller.js +20 -16
  187. package/src/client/presentation/layout-controller.js +3 -1
  188. package/src/client/presentation/quick-switcher-controller.js +74 -232
  189. package/src/client/presentation/quick-switcher-text-search.js +10 -14
  190. package/src/client/styles/components/navigation.css +1 -2
  191. package/src/client/styles/components/scrollbars.css +10 -0
  192. package/src/client/styles/export-document.css +44 -29
  193. package/src/client/styles/features/agent-connection.css +202 -0
  194. package/src/client/styles/features/collaboration-chat.css +8 -1
  195. package/src/client/styles/features/collaboration-dialogs.css +16 -1
  196. package/src/client/styles/features/collaboration-presence.css +30 -5
  197. package/src/client/styles/features/diff-toolbar.css +1 -1
  198. package/src/client/styles/features/media-lightbox.css +12 -3
  199. package/src/client/styles/features/preview-markdown.css +54 -0
  200. package/src/client/styles/features/quick-switcher.css +20 -11
  201. package/src/client/styles/foundation/themes.css +2 -0
  202. package/src/client/styles/foundation/tokens.css +8 -0
  203. package/src/client/styles/layout/editor-page.css +25 -11
  204. package/src/client/styles/layout/responsive.css +2 -20
  205. package/src/client/styles/layout/shell.css +77 -24
  206. package/src/client/styles/layout/sidebar.css +7 -4
  207. package/src/client/styles/layout/view-modes.css +2 -0
  208. package/src/client/styles/overrides/codemirror.css +31 -19
  209. package/src/client/styles/primitives/controls.css +21 -54
  210. package/src/client/styles/style.css +1 -0
  211. package/src/client/styles/surfaces/excalidraw-editor.css +42 -0
  212. package/src/domain/agent-tool-definitions.js +796 -0
  213. package/src/domain/collabmd-content-capabilities.js +149 -0
  214. package/src/domain/collaboration-protocol.js +2 -0
  215. package/src/domain/comment-threads.js +3 -5
  216. package/src/domain/diagram-source.js +50 -0
  217. package/src/domain/editable-content-revision.js +10 -0
  218. package/src/domain/editable-text.js +3 -0
  219. package/src/domain/exact-text-edits.js +69 -0
  220. package/src/domain/excalidraw-agent-scene.js +522 -0
  221. package/src/domain/excalidraw-agent-verification.js +460 -0
  222. package/src/domain/excalidraw-room-codec.js +1 -5
  223. package/src/domain/file-kind.js +12 -1
  224. package/src/domain/video-embed.js +76 -0
  225. package/src/server/application/agent-connection-service.js +178 -0
  226. package/src/server/application/agent-content-service.js +773 -0
  227. package/src/server/application/workspace-reconciliation.js +5 -7
  228. package/src/server/config/env.js +51 -1
  229. package/src/server/create-app-server.js +36 -0
  230. package/src/server/domain/bases/base-query-service.js +1 -5
  231. package/src/server/domain/collaboration/collaboration-room.js +121 -25
  232. package/src/server/domain/collaboration/protocol.js +4 -0
  233. package/src/server/domain/hosted-workspace.js +21 -0
  234. package/src/server/domain/markdown-reference-extractor.js +15 -1
  235. package/src/server/domain/ripgrep-search-service.js +110 -37
  236. package/src/server/infrastructure/git/git-service.js +19 -1
  237. package/src/server/infrastructure/git/history-service.js +1 -1
  238. package/src/server/infrastructure/git/local-exclude.js +25 -0
  239. package/src/server/infrastructure/git/parsers.js +19 -17
  240. package/src/server/infrastructure/git/path-utils.js +0 -16
  241. package/src/server/infrastructure/git/status-service.js +1 -1
  242. package/src/server/infrastructure/git/untracked-files.js +12 -21
  243. package/src/server/infrastructure/http/create-agent-api-handler.js +171 -0
  244. package/src/server/infrastructure/http/create-git-api-command-handler.js +32 -0
  245. package/src/server/infrastructure/http/create-request-handler.js +28 -0
  246. package/src/server/infrastructure/http/create-static-handler.js +6 -0
  247. package/src/server/infrastructure/mcp/create-agent-mcp-handler.js +199 -0
  248. package/src/server/infrastructure/persistence/agent-connection-store.js +120 -0
  249. package/src/server/infrastructure/persistence/path-utils.js +10 -4
  250. package/src/server/infrastructure/persistence/pull-backup-store.js +2 -27
  251. package/src/server/infrastructure/persistence/vault-file-store.js +11 -2
  252. package/src/server/shared/agent-tool-image.js +22 -0
  253. package/src/server/shared/agent-tool-rate-limiter.js +20 -0
  254. package/src/server/startup/git-remote-bootstrap.js +5 -33
  255. package/dist/client/assets/CodeMirrorEditor-UCMRAMCV-BPH4FJkv.js +0 -1
  256. package/dist/client/assets/abnfDiagram-VCTEODGH-BNDLzJ7L.js +0 -1
  257. package/dist/client/assets/browser-utils--H_DkyEF.js +0 -1
  258. package/dist/client/assets/channel-CaoKA_We.js +0 -1
  259. package/dist/client/assets/chunk-DSNCTWBM-8xkug7fQ.js +0 -2
  260. package/dist/client/assets/chunk-DU6HZSFF-COXDzJK1.js +0 -127
  261. package/dist/client/assets/chunk-R7TYR2AO-CTd0Yj98.js +0 -10
  262. package/dist/client/assets/classDiagram-DTDB5LWJ-7fEmn0zV.js +0 -1
  263. package/dist/client/assets/classDiagram-v2-JRS7N3AN-7fEmn0zV.js +0 -1
  264. package/dist/client/assets/comment-threads-9J6Em-_y.js +0 -4
  265. package/dist/client/assets/comment-ui-shared-DWkk9Vh3.js +0 -7
  266. package/dist/client/assets/diagram-VSXAHHWV-CQRVEZR7.js +0 -3
  267. package/dist/client/assets/dist-73RPdcyU.js +0 -1
  268. package/dist/client/assets/dist-CH-sQu3r.js +0 -1
  269. package/dist/client/assets/drawio-editor-BjrxTe3l.js +0 -9
  270. package/dist/client/assets/drawioEditor-DUVZjSSr.js +0 -2
  271. package/dist/client/assets/ebnfDiagram-PWID7BFC-OXa8VEp1.js +0 -1
  272. package/dist/client/assets/editor-session-DfZuRgB_.js +0 -54
  273. package/dist/client/assets/embedded-editor-base-DvMynhKu.css +0 -1
  274. package/dist/client/assets/excalidraw-editor-D7pnUCLH.js +0 -9
  275. package/dist/client/assets/excalidraw-mermaid-stub-BNckbX2n.js +0 -1
  276. package/dist/client/assets/excalidraw-mermaid-stub-DE5FTiZB.js +0 -1
  277. package/dist/client/assets/excalidrawEditor-B0jOK3lv.js +0 -2
  278. package/dist/client/assets/export-page-Bs3xE1fg.js +0 -57
  279. package/dist/client/assets/exportDocument-Dg9MjkIk.js +0 -2
  280. package/dist/client/assets/exportDocument-PnYU3v1s.css +0 -1
  281. package/dist/client/assets/file-kind-BbqgGzpc.js +0 -1
  282. package/dist/client/assets/flowDiagram-A5DVABFB-CiW1eN8C.js +0 -1
  283. package/dist/client/assets/image-ZVKINSPA-0zXSnE7M.js +0 -1
  284. package/dist/client/assets/index-BIpgxyGZ.css +0 -1
  285. package/dist/client/assets/index-Z5sI0Bj-.js +0 -2
  286. package/dist/client/assets/main-Dwnj9Gaq.js +0 -903
  287. package/dist/client/assets/pegDiagram-XKGWAZYB-B31fKPR2.js +0 -1
  288. package/dist/client/assets/preview-render-compiler-BApvlLLo.js +0 -35
  289. package/dist/client/assets/preview-render-compiler-BQ_3h9vD.js +0 -1
  290. package/dist/client/assets/preview-render-profile-CE-5rVTC.js +0 -1
  291. package/dist/client/assets/prod-BiYPMR1i.js +0 -177
  292. package/dist/client/assets/prod-VxdGZ5Ly.js +0 -1
  293. package/dist/client/assets/quick-switcher-controller-ButWPmCI.js +0 -12
  294. package/dist/client/assets/railroadDiagram-O6MQD6OU-BPifP0MB.js +0 -1
  295. package/dist/client/assets/runtime-config-BLrEKVO1.js +0 -1
  296. package/dist/client/assets/stateDiagram-v2-4QOOHH4V-B--gjrit.js +0 -1
  297. package/dist/client/assets/swimlanesDiagram-VK2B7HYN-DO8XNXEg.js +0 -8
  298. package/dist/client/assets/vault-api-client-_LpV1aIn.js +0 -6
  299. package/dist/client/assets/vault-utils-wdQ0RNR9.js +0 -1
  300. package/src/client/app/drawio-editor-entry.js +0 -1
  301. package/src/client/app/excalidraw-editor-entry.js +0 -1
  302. package/src/client/excalidraw-mermaid-stub.js +0 -7
  303. package/src/client/main.js +0 -16
  304. /package/dist/client/assets/{drawio-editor-UQQ8XHou.css → drawioEditor-UQQ8XHou.css} +0 -0
  305. /package/dist/client/assets/{sizeCapture-X5ZJPWSS-B0uUizjq.js → sizeCapture-INFHLROL-B0uUizjq.js} +0 -0
@@ -84,12 +84,10 @@ export const presenceFeature = {
84
84
  const effectiveConnectionState = getConnectedPresenceState(this);
85
85
  const hasUsers = this.globalUsers.length > 0;
86
86
  if (effectiveConnectionState.status !== 'connected') {
87
- this.presencePanelOpen = false;
87
+ this.closePresencePanel();
88
88
  }
89
89
 
90
90
  badge.type = 'button';
91
- badge.setAttribute('aria-controls', 'presencePanel');
92
- badge.setAttribute('aria-expanded', String(this.presencePanelOpen));
93
91
  badge.classList.toggle('is-active', this.presencePanelOpen);
94
92
 
95
93
  if (effectiveConnectionState.status === 'connected') {
@@ -173,12 +171,9 @@ export const presenceFeature = {
173
171
  overflow.className = 'user-avatar user-avatar-button user-avatar-overflow-trigger';
174
172
  overflow.style.backgroundColor = 'var(--color-surface-dynamic)';
175
173
  overflow.style.color = 'var(--color-text-muted)';
176
- overflow.setAttribute('aria-controls', 'presencePanel');
177
- overflow.setAttribute('aria-expanded', String(this.presencePanelOpen));
174
+ overflow.setAttribute('popovertarget', 'presencePanel');
178
175
  overflow.setAttribute('aria-label', `Show ${visibleUsers.length} online users`);
179
- overflow.setAttribute('data-presence-panel-trigger', 'true');
180
176
  overflow.title = `Show ${visibleUsers.length} online users`;
181
- overflow.addEventListener('click', () => this.openPresencePanel());
182
177
  const overflowLabel = document.createElement('span');
183
178
  overflowLabel.className = 'user-avatar-initial';
184
179
  overflowLabel.textContent = `+${visibleUsers.length - MAX_INLINE_AVATARS}`;
@@ -194,29 +189,19 @@ export const presenceFeature = {
194
189
  }
195
190
 
196
191
  this.presencePanelOpen = true;
197
- this.closeChatPanel?.();
198
- this.closeToolbarOverflowMenu?.();
192
+ if (!this.elements.presencePanel?.matches(':popover-open')) {
193
+ this.elements.presencePanel?.showPopover();
194
+ }
199
195
  this.renderAvatars();
200
196
  this.renderPresence();
201
197
  },
202
198
 
203
199
  closePresencePanel() {
204
- if (!this.presencePanelOpen) {
205
- return;
200
+ if (this.elements.presencePanel?.matches(':popover-open')) {
201
+ this.elements.presencePanel.hidePopover();
206
202
  }
207
-
208
203
  this.presencePanelOpen = false;
209
- this.renderAvatars();
210
- this.renderPresence();
211
- },
212
-
213
- togglePresencePanel() {
214
- if (this.presencePanelOpen) {
215
- this.closePresencePanel();
216
- return;
217
- }
218
-
219
- this.openPresencePanel();
204
+ this.renderAvatars?.();
220
205
  },
221
206
 
222
207
  renderPresencePanel() {
@@ -225,8 +210,6 @@ export const presenceFeature = {
225
210
  const status = this.elements.presencePanelStatus;
226
211
  if (!panel || !list) return;
227
212
 
228
- panel.classList.toggle('hidden', !this.presencePanelOpen);
229
- panel.setAttribute('aria-hidden', String(!this.presencePanelOpen));
230
213
  if (!this.presencePanelOpen) {
231
214
  return;
232
215
  }
@@ -44,6 +44,7 @@ function openDisplayNameDialog({ mode = 'edit' } = {}) {
44
44
  if (!dialog || !input) return;
45
45
 
46
46
  const isOnboarding = mode === 'onboarding';
47
+ this._displayNameDialogMode = mode;
47
48
  if (title) {
48
49
  title.textContent = isOnboarding ? 'Choose your display name' : 'Update display name';
49
50
  }
@@ -88,6 +89,17 @@ function promptForDisplayNameIfNeeded() {
88
89
  });
89
90
  }
90
91
 
92
+ /** @this {UiIdentityContext} */
93
+ function handleDisplayNameCancel() {
94
+ if (this._displayNameDialogMode === 'onboarding' && !this.getStoredUserName()) {
95
+ const guestName = this.getCurrentUserName();
96
+ if (guestName) {
97
+ this.preferences.setUserName(guestName);
98
+ }
99
+ }
100
+ this.elements.displayNameDialog?.close();
101
+ }
102
+
91
103
  /** @this {UiIdentityContext} */
92
104
  function handleDisplayNameSubmit() {
93
105
  if (this.isIdentityManagedByAuth()) {
@@ -164,6 +176,7 @@ export const uiFeatureIdentityMethods = {
164
176
  getCurrentUser,
165
177
  getCurrentUserName,
166
178
  getStoredUserName,
179
+ handleDisplayNameCancel,
167
180
  handleDisplayNameSubmit,
168
181
  isIdentityManagedByAuth,
169
182
  openDisplayNameDialog,
@@ -1,4 +1,4 @@
1
- import { supportsBacklinksForFilePath } from '../../../domain/file-kind.js';
1
+ import { getVaultFileKind, supportsBacklinksForFilePath } from '../../../domain/file-kind.js';
2
2
  import { isPlainQuickSwitcherShortcut } from '../../domain/keyboard-shortcuts.js';
3
3
  import { createFileRouteHash, isCollabMdHashRoute } from '../../domain/hash-routes.js';
4
4
 
@@ -189,6 +189,13 @@ function bindToolbarEvents() {
189
189
  this.closeToolbarOverflowMenu?.();
190
190
  });
191
191
 
192
+ this.elements.reviewFileChangesButton?.addEventListener('click', () => {
193
+ this.handleGitDiffSelection(this.currentFilePath, {
194
+ closeSidebarOnMobile: true,
195
+ scope: 'all',
196
+ });
197
+ });
198
+
192
199
  this.elements.fileHistoryButton?.addEventListener('click', () => {
193
200
  const route = this.navigation.getHashRoute();
194
201
  if (route.type === 'git-file-preview') {
@@ -210,7 +217,7 @@ function bindToolbarEvents() {
210
217
  /** @this {UiShellContext} */
211
218
  function bindDialogEvents() {
212
219
  this.elements.displayNameCancel?.addEventListener('click', () => {
213
- this.elements.displayNameDialog?.close();
220
+ this.handleDisplayNameCancel();
214
221
  });
215
222
 
216
223
  this.elements.gitCommitCancel?.addEventListener('click', () => {
@@ -230,7 +237,7 @@ function bindDialogEvents() {
230
237
  this.elements.gitCommitInput.value = '';
231
238
  }
232
239
  if (this.elements.gitCommitSubmit) {
233
- this.elements.gitCommitSubmit.textContent = 'Commit staged changes';
240
+ this.elements.gitCommitSubmit.textContent = 'Commit included files';
234
241
  }
235
242
  });
236
243
 
@@ -240,7 +247,7 @@ function bindDialogEvents() {
240
247
  this.elements.gitResetFileName.value = '';
241
248
  }
242
249
  if (this.elements.gitResetSubmit) {
243
- this.elements.gitResetSubmit.textContent = 'Reset File';
250
+ this.elements.gitResetSubmit.textContent = 'Reset file';
244
251
  }
245
252
  });
246
253
 
@@ -255,6 +262,19 @@ function bindDialogEvents() {
255
262
  });
256
263
  }
257
264
 
265
+ /** @this {UiShellContext} */
266
+ function focusEditor(event) {
267
+ event?.preventDefault?.();
268
+ const container = this.elements.editorContainer;
269
+ if (!container) return;
270
+
271
+ const target = container.querySelector('.cm-content, iframe, [contenteditable="true"]') ?? container;
272
+ if (target === container && !container.hasAttribute('tabindex')) {
273
+ container.setAttribute('tabindex', '-1');
274
+ }
275
+ target.focus();
276
+ }
277
+
258
278
  /** @this {UiShellContext} */
259
279
  function bindEvents() {
260
280
  const stopFollowing = () => this.stopFollowingUser?.();
@@ -263,6 +283,7 @@ function bindEvents() {
263
283
  this.elements.editorContainer?.addEventListener('wheel', stopFollowing, { passive: true });
264
284
  this.elements.previewContainer?.addEventListener('pointerdown', stopFollowing);
265
285
  this.elements.previewContainer?.addEventListener('wheel', stopFollowing, { passive: true });
286
+ this.elements.skipToEditor?.addEventListener('click', (event) => this.focusEditor(event));
266
287
 
267
288
  this.elements.emptyStateNewFileBtn?.addEventListener('click', () => {
268
289
  this.fileExplorer.actionController.openRootCreateMenu({
@@ -273,13 +294,24 @@ function bindEvents() {
273
294
  this.elements.emptyStateSearchBtn?.addEventListener('click', () => {
274
295
  void this.toggleQuickSwitcher();
275
296
  });
297
+ this.elements.presencePanel?.addEventListener('toggle', (event) => {
298
+ this.presencePanelOpen = event.newState === 'open';
299
+ this.renderAvatars?.();
300
+ this.renderPresence?.();
301
+ });
276
302
 
277
- this.elements.chatToggleButton?.addEventListener('click', () => {
278
- this.closePresencePanel?.();
279
- this.toggleChatPanel();
280
- this.closeToolbarOverflowMenu?.();
303
+ this.elements.chatPanel?.addEventListener('toggle', (event) => {
304
+ this.chatIsOpen = event.newState === 'open';
305
+ if (this.chatIsOpen) {
306
+ this.chatUnreadCount = 0;
307
+ }
308
+ this.renderChat?.({ messagesChanged: this.chatIsOpen, stickToBottom: this.chatIsOpen });
309
+ if (this.chatIsOpen) {
310
+ requestAnimationFrame(() => this.elements.chatInput?.focus());
311
+ }
281
312
  });
282
313
 
314
+
283
315
  this.elements.chatNotificationButton?.addEventListener('click', () => {
284
316
  void this.handleChatNotificationToggle();
285
317
  });
@@ -288,9 +320,6 @@ function bindEvents() {
288
320
  this.toggleChatNotificationMute();
289
321
  });
290
322
 
291
- this.elements.chatCloseButton?.addEventListener('click', () => {
292
- this.closeChatPanel({ restoreFocus: true });
293
- });
294
323
 
295
324
  this.elements.chatForm?.addEventListener('submit', (event) => {
296
325
  event.preventDefault();
@@ -320,21 +349,16 @@ function bindEvents() {
320
349
  this.toggleVimMode();
321
350
  });
322
351
 
323
- this.elements.userCount?.addEventListener('click', (event) => {
324
- event.preventDefault();
325
- this.togglePresencePanel?.();
326
- });
327
352
 
328
353
  this.elements.editorFindButton?.addEventListener('click', () => {
329
354
  this.runEditorCommand?.('openSearch');
330
355
  });
331
356
 
332
- this.elements.toolbarOverflowToggle?.addEventListener('click', (event) => {
333
- event.preventDefault();
334
- this.closePresencePanel?.();
335
- this.toggleToolbarOverflowMenu();
357
+ this.elements.editorFormatButton?.addEventListener('click', () => {
358
+ void this.formatCurrentDocument();
336
359
  });
337
360
 
361
+
338
362
  this.elements.toolbarOverflowMenu?.addEventListener('click', (event) => {
339
363
  const button = event.target instanceof Element ? event.target.closest('button') : null;
340
364
  if (button && button !== this.elements.toggleVimModeButton) {
@@ -342,6 +366,14 @@ function bindEvents() {
342
366
  }
343
367
  });
344
368
 
369
+ this.elements.toolbarOverflowMenu?.addEventListener('toggle', (event) => {
370
+ if (event.newState === 'closed') {
371
+ this.elements.toolbarOverflowMenu.querySelectorAll('details[open]').forEach((detail) => {
372
+ detail.removeAttribute('open');
373
+ });
374
+ }
375
+ });
376
+
345
377
  this.elements.previewContent?.addEventListener('click', (event) => {
346
378
  this.handlePreviewContentClick?.(event);
347
379
  });
@@ -374,47 +406,16 @@ function bindEvents() {
374
406
  this.setSidebarTab('comments');
375
407
  });
376
408
 
377
- document.addEventListener('pointerdown', (event) => {
378
- this.handleDocumentPointerDown(event);
409
+ this.elements.sidebarTabs?.addEventListener('keydown', (event) => {
410
+ this.handleSidebarTabsKeydown(event);
379
411
  });
380
412
 
413
+
381
414
  document.addEventListener('keydown', (event) => {
382
415
  this.handleDocumentKeydown(event);
383
416
  }, { capture: true });
384
417
  }
385
418
 
386
- /** @this {UiShellContext} */
387
- function handleDocumentPointerDown(event) {
388
- this.handleMarkdownToolbarDocumentPointerDown?.(event);
389
-
390
- const target = event.target instanceof Element ? event.target : null;
391
-
392
- if (
393
- this.toolbarOverflowOpen
394
- && !this.elements.toolbarOverflowMenu?.contains(target)
395
- && !this.elements.toolbarOverflowToggle?.contains(target)
396
- ) {
397
- this.closeToolbarOverflowMenu();
398
- }
399
-
400
- if (
401
- this.presencePanelOpen
402
- && !this.elements.presencePanel?.contains(target)
403
- && !target?.closest?.('[data-presence-panel-trigger="true"]')
404
- ) {
405
- this.closePresencePanel?.();
406
- }
407
-
408
- if (!this.chatIsOpen) {
409
- return;
410
- }
411
-
412
- if (this.elements.chatContainer?.contains(target)) {
413
- return;
414
- }
415
-
416
- this.closeChatPanel({ restoreFocus: false });
417
- }
418
419
 
419
420
  /** @this {UiShellContext} */
420
421
  function handleDocumentKeydown(event) {
@@ -422,20 +423,6 @@ function handleDocumentKeydown(event) {
422
423
  this.stopFollowingUser?.();
423
424
  }
424
425
 
425
- if (event.key === 'Escape' && this.toolbarOverflowOpen) {
426
- this.closeToolbarOverflowMenu();
427
- return;
428
- }
429
-
430
- if (event.key === 'Escape' && this.presencePanelOpen) {
431
- this.closePresencePanel?.();
432
- return;
433
- }
434
-
435
- if (event.key === 'Escape' && this.chatIsOpen) {
436
- this.closeChatPanel({ restoreFocus: true });
437
- return;
438
- }
439
426
 
440
427
  if (isPlainQuickSwitcherShortcut(event)) {
441
428
  event.preventDefault();
@@ -508,7 +495,7 @@ function getHeadingLinkLabel(heading) {
508
495
  return heading.textContent.trim();
509
496
  }
510
497
 
511
- headingClone.querySelectorAll('.preview-heading-link-button').forEach((button) => button.remove());
498
+ headingClone.querySelectorAll('.preview-heading-fold-button, .preview-heading-link-button').forEach((button) => button.remove());
512
499
  return headingClone.textContent.replace(/\s+/g, ' ').trim();
513
500
  }
514
501
 
@@ -549,6 +536,7 @@ function createPreviewHeadingLinkUrl(anchorId) {
549
536
  return '';
550
537
  }
551
538
 
539
+ // pi-lens-ignore: unchecked-throwing-call-js
552
540
  const url = new URL(window.location.href);
553
541
  url.hash = createFileRouteHash(this.currentFilePath, {
554
542
  anchor: anchorId,
@@ -594,6 +582,7 @@ function syncPreviewCodeCopyButtons() {
594
582
  const pre = code.parentElement;
595
583
  const wrapper = document.createElement('div');
596
584
  wrapper.className = 'preview-code-block';
585
+ // pi-lens-ignore: no-inner-html-js
597
586
  wrapper.innerHTML = `
598
587
  <div class="preview-code-actions">
599
588
  <button type="button" class="ui-button ui-button--secondary ui-button--compact preview-code-copy-button" aria-label="Copy code" title="Copy code">
@@ -605,6 +594,90 @@ function syncPreviewCodeCopyButtons() {
605
594
  });
606
595
  }
607
596
 
597
+ function applyPreviewHeadingFolds(previewContent) {
598
+ const parents = new Set(Array.from(previewContent?.querySelectorAll?.('.preview-heading-fold-button') ?? [])
599
+ .map((button) => button.closest('h1, h2, h3, h4, h5, h6')?.parentElement)
600
+ .filter(Boolean));
601
+
602
+ parents.forEach((parent) => {
603
+ const collapsedLevels = [];
604
+ Array.from(parent.children).forEach((child) => {
605
+ const level = /^H([1-6])$/.exec(child.tagName)?.[1];
606
+ if (level) {
607
+ const headingLevel = Number(level);
608
+ while (collapsedLevels.at(-1) >= headingLevel) {
609
+ collapsedLevels.pop();
610
+ }
611
+ child.hidden = collapsedLevels.length > 0;
612
+ if (child.dataset.previewHeadingCollapsed === 'true') {
613
+ collapsedLevels.push(headingLevel);
614
+ }
615
+ } else {
616
+ child.hidden = collapsedLevels.length > 0;
617
+ }
618
+ });
619
+ });
620
+ }
621
+
622
+ /** @this {UiShellContext} */
623
+ function unfoldPreviewHeading(target) {
624
+ if (!(target instanceof HTMLElement) || !/^H[1-6]$/.test(target.tagName)) {
625
+ return false;
626
+ }
627
+
628
+ const collapsedHeadings = [];
629
+ let ancestorLevel = Number(target.tagName[1]) + 1;
630
+ for (let heading = target; heading; heading = heading.previousElementSibling) {
631
+ const level = Number(/^H([1-6])$/.exec(heading.tagName)?.[1]);
632
+ if (!level || level >= ancestorLevel) {
633
+ continue;
634
+ }
635
+
636
+ ancestorLevel = level;
637
+ if (heading.dataset.previewHeadingCollapsed === 'true') {
638
+ collapsedHeadings.push(heading);
639
+ }
640
+ }
641
+
642
+ if (collapsedHeadings.length === 0) {
643
+ return false;
644
+ }
645
+
646
+ collapsedHeadings.forEach((heading) => delete heading.dataset.previewHeadingCollapsed);
647
+ this.syncPreviewHeadingFoldButtons();
648
+ this.scrollSyncController?.invalidatePreviewBlocks?.();
649
+ this.schedulePreviewLayoutSync?.({ delayMs: 0 });
650
+ this.refreshCommentUiLayout?.();
651
+ return true;
652
+ }
653
+
654
+ /** @this {UiShellContext} */
655
+ function syncPreviewHeadingFoldButtons() {
656
+ const headings = this.elements.previewContent?.querySelectorAll?.('h1, h2, h3, h4, h5, h6') ?? [];
657
+
658
+ Array.from(headings).forEach((heading) => {
659
+ if (!(heading instanceof HTMLElement)) {
660
+ return;
661
+ }
662
+
663
+ const headingLabel = getHeadingLinkLabel(heading);
664
+ let button = heading.querySelector(':scope > .preview-heading-fold-button');
665
+ if (!(button instanceof HTMLButtonElement)) {
666
+ button = document.createElement('button');
667
+ button.type = 'button';
668
+ button.className = 'ui-icon-button preview-heading-fold-button';
669
+ heading.prepend(button);
670
+ }
671
+
672
+ const collapsed = heading.dataset.previewHeadingCollapsed === 'true';
673
+ button.setAttribute('aria-expanded', String(!collapsed));
674
+ button.setAttribute('aria-label', `${collapsed ? 'Expand' : 'Collapse'} ${headingLabel}`);
675
+ button.setAttribute('title', `${collapsed ? 'Expand' : 'Collapse'} section`);
676
+ });
677
+
678
+ applyPreviewHeadingFolds(this.elements.previewContent);
679
+ }
680
+
608
681
  /** @this {UiShellContext} */
609
682
  function syncPreviewHeadingLinkButtons() {
610
683
  const headings = this.elements.previewContent?.querySelectorAll?.('h1[id], h2[id], h3[id], h4[id], h5[id], h6[id]') ?? [];
@@ -620,6 +693,7 @@ function syncPreviewHeadingLinkButtons() {
620
693
  button = document.createElement('button');
621
694
  button.type = 'button';
622
695
  button.className = 'ui-icon-button preview-heading-link-button';
696
+ // pi-lens-ignore: no-inner-html-js
623
697
  button.innerHTML = PREVIEW_HEADING_LINK_ICON;
624
698
  heading.appendChild(button);
625
699
  }
@@ -693,6 +767,18 @@ function handlePreviewContentClick(event) {
693
767
  return;
694
768
  }
695
769
 
770
+ const foldButton = event.target.closest('button.preview-heading-fold-button');
771
+ const heading = foldButton?.closest('h1, h2, h3, h4, h5, h6');
772
+ if (foldButton instanceof HTMLButtonElement && heading instanceof HTMLElement) {
773
+ event.preventDefault();
774
+ heading.dataset.previewHeadingCollapsed = String(heading.dataset.previewHeadingCollapsed !== 'true');
775
+ this.syncPreviewHeadingFoldButtons();
776
+ this.scrollSyncController?.invalidatePreviewBlocks?.();
777
+ this.schedulePreviewLayoutSync?.({ delayMs: 0 });
778
+ this.refreshCommentUiLayout?.();
779
+ return;
780
+ }
781
+
696
782
  const codeCopyButton = event.target.closest('button.preview-code-copy-button');
697
783
  if (codeCopyButton instanceof HTMLButtonElement) {
698
784
  event.preventDefault();
@@ -759,20 +845,6 @@ function handlePreviewContentClick(event) {
759
845
  this.session?.toggleTaskListItem?.(sourceLine);
760
846
  }
761
847
 
762
- /** @this {UiShellContext} */
763
- function setToolbarOverflowOpen(nextState) {
764
- this.toolbarOverflowOpen = Boolean(nextState);
765
- this.elements.toolbarOverflowToggle?.setAttribute('aria-expanded', String(this.toolbarOverflowOpen));
766
- this.elements.toolbarOverflowToggle?.classList.toggle('active', this.toolbarOverflowOpen);
767
- this.elements.toolbarOverflowToggle?.closest('.toolbar-right')?.classList.toggle('is-overflow-open', this.toolbarOverflowOpen);
768
-
769
- if (!this.toolbarOverflowOpen) {
770
- this.elements.toolbarOverflowMenu?.querySelectorAll('details[open]').forEach((detail) => {
771
- detail.removeAttribute('open');
772
- });
773
- }
774
- }
775
-
776
848
  /** @this {UiShellContext} */
777
849
  function syncToolbarOverflowVisibility() {
778
850
  const toggle = this.elements.toolbarOverflowToggle;
@@ -787,12 +859,9 @@ function syncToolbarOverflowVisibility() {
787
859
 
788
860
  /** @this {UiShellContext} */
789
861
  function closeToolbarOverflowMenu() {
790
- this.setToolbarOverflowOpen(false);
791
- }
792
-
793
- /** @this {UiShellContext} */
794
- function toggleToolbarOverflowMenu() {
795
- this.setToolbarOverflowOpen(!this.toolbarOverflowOpen);
862
+ if (this.elements.toolbarOverflowMenu?.matches(':popover-open')) {
863
+ this.elements.toolbarOverflowMenu.hidePopover();
864
+ }
796
865
  }
797
866
 
798
867
  /** @this {UiShellContext} */
@@ -846,6 +915,29 @@ function getStoredLineWrapping() {
846
915
  return this.preferences.getLineWrappingEnabled();
847
916
  }
848
917
 
918
+ /** @this {UiShellContext} */
919
+ async function formatCurrentDocument() {
920
+ const button = this.elements.editorFormatButton;
921
+ button?.setAttribute('disabled', '');
922
+
923
+ try {
924
+ const result = await this.session?.formatDocument?.(this.currentFilePath);
925
+ const messages = {
926
+ changed: 'Document changed while formatting. Try again.',
927
+ formatted: getVaultFileKind(this.currentFilePath) === 'plantuml'
928
+ ? 'PlantUML indentation applied'
929
+ : 'Document formatted',
930
+ unchanged: 'Document is already formatted',
931
+ unsupported: 'Formatting is unavailable for this file type',
932
+ };
933
+ this.toastController.show(messages[result] ?? messages.unsupported);
934
+ } catch (error) {
935
+ this.toastController.show(`Formatting failed: ${error?.message || 'Unknown error'}`, 5000);
936
+ } finally {
937
+ button?.removeAttribute('disabled');
938
+ }
939
+ }
940
+
849
941
  /** @this {UiShellContext} */
850
942
  function toggleVimMode() {
851
943
  const currentState = this.session?.isVimModeEnabled?.() ?? this.getStoredVimMode();
@@ -902,9 +994,12 @@ function showEditorLoading() {
902
994
  if (!this.elements.editorContainer) return;
903
995
  this.elements.editorContainer.classList.add('is-loading-editor');
904
996
  this.elements.editorContainer.innerHTML = `
905
- <div class="editor-loading" id="editorLoading">
906
- <div class="loading-spinner"></div>
907
- <span class="loading-text">Loading file...</span>
997
+ <div class="editor-loading" id="editorLoading" role="status" aria-live="polite" aria-label="Loading file…">
998
+ <div class="skeleton-line skeleton-line--title skeleton-line--break"></div>
999
+ <div class="skeleton-line skeleton-line--full"></div>
1000
+ <div class="skeleton-line skeleton-line--ninety"></div>
1001
+ <div class="skeleton-line skeleton-line--ninety-five"></div>
1002
+ <div class="skeleton-line skeleton-line--eighty"></div>
908
1003
  </div>`;
909
1004
  }
910
1005
 
@@ -916,13 +1011,17 @@ function hideEditorLoading() {
916
1011
  }
917
1012
 
918
1013
  /** @this {UiShellContext} */
919
- function showEditorLoadError() {
1014
+ function showEditorLoadError(message = 'Failed to load file') {
920
1015
  if (!this.elements.editorContainer) return;
1016
+ const loading = document.createElement('div');
1017
+ const text = document.createElement('span');
1018
+ loading.className = 'editor-loading';
1019
+ loading.id = 'editorLoading';
1020
+ text.className = 'loading-text';
1021
+ text.textContent = message;
1022
+ loading.appendChild(text);
921
1023
  this.elements.editorContainer.classList.remove('is-loading-editor');
922
- this.elements.editorContainer.innerHTML = `
923
- <div class="editor-loading" id="editorLoading">
924
- <span class="loading-text">Failed to load file</span>
925
- </div>`;
1024
+ this.elements.editorContainer.replaceChildren(loading);
926
1025
  }
927
1026
 
928
1027
  /** @this {UiShellContext} */
@@ -938,11 +1037,12 @@ export const uiFeatureShellMethods = {
938
1037
  copyPreviewCodeBlock,
939
1038
  copyPreviewHeadingLink,
940
1039
  createPreviewHeadingLinkUrl,
1040
+ focusEditor,
1041
+ formatCurrentDocument,
941
1042
  getStoredLineWrapping,
942
1043
  getStoredVimMode,
943
1044
  handleConnectionChange,
944
1045
  handleDocumentKeydown,
945
- handleDocumentPointerDown,
946
1046
  handlePreviewContentClick,
947
1047
  handleThemeChange,
948
1048
  hideEditorLoading,
@@ -954,16 +1054,16 @@ export const uiFeatureShellMethods = {
954
1054
  promptForVersionReload,
955
1055
  requestPreviewRouteAnchor,
956
1056
  scheduleBacklinkRefresh,
957
- setToolbarOverflowOpen,
958
1057
  showEditorLoadError,
959
1058
  showEditorLoading,
960
1059
  syncPreviewCodeCopyButtons,
1060
+ syncPreviewHeadingFoldButtons,
961
1061
  syncPreviewHeadingLinkButtons,
962
1062
  syncVisualViewportBounds,
963
1063
  syncToolbarOverflowVisibility,
964
1064
  syncVimModeToggle,
965
1065
  syncWrapToggle,
966
- toggleToolbarOverflowMenu,
967
1066
  toggleLineWrapping,
1067
+ unfoldPreviewHeading,
968
1068
  toggleVimMode,
969
1069
  };
@@ -153,6 +153,26 @@ function applySidebarVisibility(showSidebar) {
153
153
  }
154
154
  }
155
155
 
156
+ /** @this {UiSidebarContext} */
157
+ function handleSidebarTabsKeydown(event) {
158
+ if (!['ArrowLeft', 'ArrowRight', 'Home', 'End'].includes(event.key)) return;
159
+
160
+ const tabs = [
161
+ this.elements.filesSidebarTab,
162
+ this.elements.commentsSidebarTab,
163
+ this.elements.gitSidebarTab,
164
+ ].filter((tab) => tab && !tab.classList.contains('hidden'));
165
+ const currentIndex = Math.max(0, tabs.indexOf(event.target));
166
+ const nextIndex = event.key === 'Home'
167
+ ? 0
168
+ : event.key === 'End'
169
+ ? tabs.length - 1
170
+ : (currentIndex + (event.key === 'ArrowRight' ? 1 : -1) + tabs.length) % tabs.length;
171
+ event.preventDefault();
172
+ tabs[nextIndex]?.click();
173
+ tabs[nextIndex]?.focus();
174
+ }
175
+
156
176
  /** @this {UiSidebarContext} */
157
177
  function setSidebarTab(tab) {
158
178
  const nextTab = tab === 'git' && this.gitRepoAvailable
@@ -162,15 +182,30 @@ function setSidebarTab(tab) {
162
182
  : 'files';
163
183
  this.activeSidebarTab = nextTab;
164
184
 
165
- this.elements.filesSidebarTab?.classList.toggle('active', nextTab === 'files');
166
- this.elements.commentsSidebarTab?.classList.toggle('active', nextTab === 'comments');
167
- this.elements.gitSidebarTab?.classList.toggle('active', nextTab === 'git');
168
- document.getElementById('fileTree')?.classList.toggle('hidden', nextTab !== 'files');
185
+ [
186
+ [this.elements.filesSidebarTab, 'files'],
187
+ [this.elements.commentsSidebarTab, 'comments'],
188
+ [this.elements.gitSidebarTab, 'git'],
189
+ ].forEach(([button, buttonTab]) => {
190
+ const selected = nextTab === buttonTab;
191
+ button?.classList.toggle('active', selected);
192
+ button?.setAttribute('aria-selected', String(selected));
193
+ button?.setAttribute('tabindex', selected ? '0' : '-1');
194
+ });
195
+ const filePanel = document.getElementById('fileTree');
196
+ const commentPanel = this.elements.commentOverviewPanel;
197
+ const gitPanelElement = document.getElementById('gitPanel');
198
+ [
199
+ [filePanel, nextTab === 'files'],
200
+ [commentPanel, nextTab === 'comments'],
201
+ [gitPanelElement, nextTab === 'git'],
202
+ ].forEach(([panel, selected]) => {
203
+ panel?.classList.toggle('hidden', !selected);
204
+ panel?.setAttribute('aria-hidden', String(!selected));
205
+ });
169
206
  this.elements.fileSearch?.classList.toggle('hidden', nextTab !== 'files');
170
- this.elements.commentOverviewPanel?.classList.toggle('hidden', nextTab !== 'comments');
171
207
  this.elements.gitSearch?.classList.toggle('hidden', nextTab !== 'git');
172
- document.getElementById('gitPanel')?.classList.toggle('active', nextTab === 'git');
173
- document.getElementById('gitPanel')?.classList.toggle('hidden', nextTab !== 'git');
208
+ gitPanelElement?.classList.toggle('active', nextTab === 'git');
174
209
  this.gitPanel.setActive(nextTab === 'git');
175
210
  if (nextTab === 'comments') {
176
211
  this.refreshCommentOverviewForSidebarOpen?.();
@@ -180,6 +215,7 @@ function setSidebarTab(tab) {
180
215
  export const uiFeatureSidebarMethods = {
181
216
  applySidebarVisibility,
182
217
  closeSidebarOnMobile,
218
+ handleSidebarTabsKeydown,
183
219
  initializeSidebarResizer,
184
220
  isMobileViewport,
185
221
  restoreSidebarState,