mulmoclaude 0.9.0 → 0.9.2

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 (458) hide show
  1. package/README.md +95 -38
  2. package/bin/mulmoclaude.js +1 -2
  3. package/client/assets/JsonEditor-CPJfn76E.css +1 -0
  4. package/client/assets/JsonEditor-CoWGJG3Y.js +10 -0
  5. package/client/assets/abnfDiagram-VRR7QNED-6nNByj6v-By7GJ3xW.js +1 -0
  6. package/client/assets/abnfDiagram-VRR7QNED-BrQlVixL.js +1 -0
  7. package/client/assets/arc-BXutyUAX.js +1 -0
  8. package/client/assets/arc-zT0wd74V-DulGJNnb.js +1 -0
  9. package/client/assets/architecture-TIHT7OUA-CYMWc3UT-C7HoFzWN.js +1 -0
  10. package/client/assets/architecture-TIHT7OUA-aml8u-G9.js +1 -0
  11. package/client/assets/architectureDiagram-ZJ3FMSHR-Bgnyaj_i-CCpEsCHN.js +36 -0
  12. package/client/assets/architectureDiagram-ZJ3FMSHR-DJTFpPjB.js +36 -0
  13. package/client/assets/array-BifhSqXX.js +1 -0
  14. package/client/assets/array-CApNbSU9-BifhSqXX.js +1 -0
  15. package/client/assets/blockDiagram-677ZJIJ3-DQ35o5E4-BXS11D3J.js +132 -0
  16. package/client/assets/blockDiagram-677ZJIJ3-aYvCODxv.js +132 -0
  17. package/client/assets/c4Diagram-LMCZKHZV-Bau6RMBb.js +10 -0
  18. package/client/assets/c4Diagram-LMCZKHZV-ClWZeiWo-DXGnOOUf.js +10 -0
  19. package/client/assets/channel-D9VSi_QV.js +1 -0
  20. package/client/assets/channel-Di5rtkx0-C3gZcytF.js +1 -0
  21. package/client/assets/chunk-2Q5K7J3B-C1jixKkw.js +1 -0
  22. package/client/assets/chunk-2Q5K7J3B-DfKpwyl9-DTxwf5E9.js +1 -0
  23. package/client/assets/chunk-32BRIVSS-CCt9wtYd-Cwa_IP35.js +1 -0
  24. package/client/assets/chunk-32BRIVSS-CxzHeys5.js +1 -0
  25. package/client/assets/chunk-52WLFC77-BtNjbTdw.js +10 -0
  26. package/client/assets/chunk-52WLFC77-FbBbR4uI-wsNC5d-f.js +10 -0
  27. package/client/assets/chunk-5VM5RSS4-BYnnUiN6-C4rOnu9A.js +15 -0
  28. package/client/assets/chunk-5VM5RSS4-ZNzvKenW.js +15 -0
  29. package/client/assets/chunk-7BUUIJ7U-Bb538aSH.js +1 -0
  30. package/client/assets/chunk-7BUUIJ7U-hh8aCuGX-mGEpPzQy.js +1 -0
  31. package/client/assets/chunk-C7G6YPKG-BE8ehsnc.js +1 -0
  32. package/client/assets/chunk-C7G6YPKG-C87hlS9c-BitbJpTa.js +1 -0
  33. package/client/assets/{chunk-D8eiyYIV-BY16KEZc.js → chunk-D8eiyYIV-BeeEsA1B.js} +1 -1
  34. package/client/assets/chunk-EX3LRPZG-Bhgskzyy.js +231 -0
  35. package/client/assets/chunk-EX3LRPZG-BqGqMXLN-Cl2Eo1_u.js +231 -0
  36. package/client/assets/chunk-FWX5IMBZ-BkbSAAuW-C2r4AuAO.js +2 -0
  37. package/client/assets/chunk-FWX5IMBZ-D9sskZb_.js +2 -0
  38. package/client/assets/chunk-HOUHSVGY-CBCXk7rb.js +1 -0
  39. package/client/assets/chunk-HOUHSVGY-Cxu0eDlh-owg_DcY0.js +1 -0
  40. package/client/assets/chunk-ICXQ74PX-DTqPpDQ9.js +2 -0
  41. package/client/assets/chunk-ICXQ74PX-hiraF_Xj-CdaEwVin.js +2 -0
  42. package/client/assets/chunk-JWPE2WC7-BQ3zXr2k-DSd3Ct95.js +1 -0
  43. package/client/assets/chunk-JWPE2WC7-DVXcaiue.js +1 -0
  44. package/client/assets/chunk-KEIR6QF5-DNzq6p3w.js +161 -0
  45. package/client/assets/chunk-MOJQB5TN-CqxshQHA-Cn_EpDyZ.js +88 -0
  46. package/client/assets/chunk-MOJQB5TN-D1s_zUGz.js +88 -0
  47. package/client/assets/chunk-OGEWGWER-CjCr7ceX-Dfk5-70y.js +1 -0
  48. package/client/assets/chunk-OGEWGWER-DzfhxoQd.js +1 -0
  49. package/client/assets/chunk-PUDLZKDR-BZlebNhn.js +156 -0
  50. package/client/assets/chunk-PUDLZKDR-Dx6M-vz1-CLyVurOz.js +156 -0
  51. package/client/assets/chunk-Q4XR5HBZ-BcJb7mwA.js +70 -0
  52. package/client/assets/chunk-Q4XR5HBZ-CZd-9lTB-qPEYoRNc.js +11 -0
  53. package/client/assets/chunk-RYQCIY6F-B_IgMG7T.js +1 -0
  54. package/client/assets/chunk-RYQCIY6F-YkbemkCt-CX4fncTQ.js +1 -0
  55. package/client/assets/chunk-V7JOEXUC-Buce04o6.js +206 -0
  56. package/client/assets/chunk-V7JOEXUC-rI0xlC_O-C607vJXy.js +206 -0
  57. package/client/assets/chunk-VAUOI2AC-CxhkzXN0.js +1 -0
  58. package/client/assets/chunk-VAUOI2AC-DrcykVNK-CSROJWdE.js +1 -0
  59. package/client/assets/chunk-VR4S4FIN-D5afNrsS.js +1 -0
  60. package/client/assets/chunk-VR4S4FIN-O6iF8Yvf-CLIwrgqU.js +1 -0
  61. package/client/assets/chunk-WYO6CB5R-B83L_z6I-Cs8mg9If.js +127 -0
  62. package/client/assets/chunk-WYO6CB5R-BXTIdTMw.js +125 -0
  63. package/client/assets/chunk-XXDRQBXY-DFXH_lWn-B5HjCp2c.js +1 -0
  64. package/client/assets/chunk-XXDRQBXY-DXMD6ofA.js +1 -0
  65. package/client/assets/chunk-Y2CYZVJY-Bdt8pFDJ-DsF7k-Jl.js +1 -0
  66. package/client/assets/chunk-Y2CYZVJY-DsF7k-Jl.js +1 -0
  67. package/client/assets/chunk-ZGVPDNZ5-BpFv9JSP-CHYySMjV.js +62 -0
  68. package/client/assets/chunk-ZGVPDNZ5-CYusI0J_.js +62 -0
  69. package/client/assets/chunk-ZIRB5QZD-Biy6ZNFG-D1maAiXb.js +32 -0
  70. package/client/assets/chunk-ZIRB5QZD-C6fEPe3t.js +32 -0
  71. package/client/assets/classDiagram-OUVF2IWQ-BgAZMSbT-BgxEE1D3.js +1 -0
  72. package/client/assets/classDiagram-OUVF2IWQ-CBD3Eidl.js +1 -0
  73. package/client/assets/classDiagram-v2-EOCWNBFH-CBD3Eidl.js +1 -0
  74. package/client/assets/classDiagram-v2-EOCWNBFH-DxHTyui1-BgxEE1D3.js +1 -0
  75. package/client/assets/cose-bilkent-JH36ORCC-CuSL2tA8-aMIqISfj.js +1 -0
  76. package/client/assets/cose-bilkent-JH36ORCC-WAJDBXv1.js +1 -0
  77. package/client/assets/cynefin-VYW2F7L2-DH2qkJKw.js +1 -0
  78. package/client/assets/cynefin-VYW2F7L2-DqA3n9nY-BNWhIK_n.js +1 -0
  79. package/client/assets/cynefinDiagram-TSTJHNR4-CUvPV9MV.js +62 -0
  80. package/client/assets/cynefinDiagram-TSTJHNR4-x0-0sQ15-Cq1gFcFu.js +62 -0
  81. package/client/assets/cytoscape.esm-CzdwbRaj-Djp6vQyU.js +321 -0
  82. package/client/assets/cytoscape.esm-Djp6vQyU.js +321 -0
  83. package/client/assets/dagre-CXRCoUWR.js +1 -0
  84. package/client/assets/dagre-VKFMJZFB-CQdfl-bx-CzZZuace.js +4 -0
  85. package/client/assets/dagre-VKFMJZFB-C_9IS7mB.js +4 -0
  86. package/client/assets/dagre-wczQIDso-CfevN9RO.js +1 -0
  87. package/client/assets/defaultLocale-C8Fc0cco.js +1 -0
  88. package/client/assets/defaultLocale-DUNguUWs-BbDo_yJX.js +1 -0
  89. package/client/assets/diagram-FQU43EPY-BOSB6VUb-isPdQ8WX.js +3 -0
  90. package/client/assets/diagram-FQU43EPY-BP8N00-b.js +3 -0
  91. package/client/assets/diagram-G47NLZAW-DLXrcXsN-BIqj7RKy.js +24 -0
  92. package/client/assets/diagram-G47NLZAW-ulE1JlWG.js +24 -0
  93. package/client/assets/diagram-NH7WQ7WH-BMQp1rkF-D6-fOq_v.js +24 -0
  94. package/client/assets/diagram-NH7WQ7WH-D_IXrL3i.js +24 -0
  95. package/client/assets/diagram-OA4YK3LP-C-pC6Eyu.js +30 -0
  96. package/client/assets/diagram-OA4YK3LP-D1wQ0vUj-BNi6QLCK.js +30 -0
  97. package/client/assets/diagram-WEI45ONY-Di9m35i-.js +41 -0
  98. package/client/assets/diagram-WEI45ONY-RR0DpF8R-BS3nrb3w.js +41 -0
  99. package/client/assets/dist-BQBs5pjy-BmtZ7Oc2.js +1 -0
  100. package/client/assets/dist-CQ3HaWOk.js +1 -0
  101. package/client/assets/ebnfDiagram-CCIWWBDH-Cbwnim2x.js +1 -0
  102. package/client/assets/ebnfDiagram-CCIWWBDH-M123uVJ8-9-dq55nQ.js +1 -0
  103. package/client/assets/erDiagram-Q63AITRT-BWx_-PXG-CWGfG4z5.js +85 -0
  104. package/client/assets/erDiagram-Q63AITRT-CMbtO3Sm.js +85 -0
  105. package/client/assets/eventmodeling-45OFAUF4--32SIpkL.js +1 -0
  106. package/client/assets/eventmodeling-45OFAUF4-_BVSjAXf-DQdL0Icr.js +1 -0
  107. package/client/assets/flowDiagram-23GEKE2U-BeOc_anm-CuVzKmmU.js +1 -0
  108. package/client/assets/flowDiagram-23GEKE2U-Dofa_qxG.js +1 -0
  109. package/client/assets/ganttDiagram-NO4QXBWP-BOoJ1eTw-TWoNmSvq.js +292 -0
  110. package/client/assets/ganttDiagram-NO4QXBWP-BgoAVKuc.js +292 -0
  111. package/client/assets/gitGraph-TEB2WS4Q-CH12KLTN-Bz4frAhV.js +1 -0
  112. package/client/assets/gitGraph-TEB2WS4Q-DIMvNvqt.js +1 -0
  113. package/client/assets/gitGraphDiagram-IHSO6WYX-B2CJhk_G-ClOKkjxw.js +106 -0
  114. package/client/assets/gitGraphDiagram-IHSO6WYX-Dmb6KnPz.js +106 -0
  115. package/client/assets/graphlib-B8gBHxth.js +1 -0
  116. package/client/assets/graphlib-hY-1btwe-DQjxxcnr.js +1 -0
  117. package/client/assets/{html2canvas-CDGcmOD3-CKJ6vKPo.js → html2canvas-CDGcmOD3-DRL9pFVl.js} +2 -2
  118. package/client/assets/{index-9lmYSaus.js → index-Dc0R-HW5.js} +129 -190
  119. package/client/assets/index-Dxo1Zdd-.css +2 -0
  120. package/client/assets/{index.es-DqtpmBm8-DFXjJgCa.js → index.es-DqtpmBm8-EQk3NgR8.js} +1 -1
  121. package/client/assets/info-DKCQHKI2-Cbw3mbiK-GsDZz9IO.js +1 -0
  122. package/client/assets/info-DKCQHKI2-ViCqobGo.js +1 -0
  123. package/client/assets/infoDiagram-FWYZ7A6U-HKV7LIG-.js +2 -0
  124. package/client/assets/infoDiagram-FWYZ7A6U-Mp1X3pBP-ATJv87Ur.js +2 -0
  125. package/client/assets/init-D6jRqBbL.js +1 -0
  126. package/client/assets/init-DEsX3bhM-D6jRqBbL.js +1 -0
  127. package/client/assets/ishikawaDiagram-FXEZZL3T-BNG7tkJu-CGCeOIlJ.js +70 -0
  128. package/client/assets/ishikawaDiagram-FXEZZL3T-CVDUj46f.js +70 -0
  129. package/client/assets/journeyDiagram-5HDEW3XC-BA-ESGLP.js +139 -0
  130. package/client/assets/journeyDiagram-5HDEW3XC-Dbp_hY9X-Bx-IoCe9.js +139 -0
  131. package/client/assets/kanban-definition-HUTT4EX6-Bqaet01L.js +89 -0
  132. package/client/assets/kanban-definition-HUTT4EX6-DSTc5u3q-C0JGckt1.js +89 -0
  133. package/client/assets/katex-CddkPoXu.js +257 -0
  134. package/client/assets/katex-M0IxphGf-CddkPoXu.js +257 -0
  135. package/client/assets/lib-4Tgx7rAy.js +114 -0
  136. package/client/assets/line-B1wBwzrY-f5wxpqoF.js +1 -0
  137. package/client/assets/line-CkyHfW7d.js +1 -0
  138. package/client/assets/linear-B9fuEF4c.js +1 -0
  139. package/client/assets/linear-lrGinF5_-CqNBync7.js +1 -0
  140. package/client/assets/map-CgrwEyH7-DWQFomlZ.js +1 -0
  141. package/client/assets/map-DsCK-0Cs.js +1 -0
  142. package/client/assets/marked.esm-CIU5FDdN.js +64 -0
  143. package/client/assets/marp-CSq0PPfK.js +3448 -0
  144. package/client/assets/material-symbols-outlined-DdRFxZLh.woff2 +0 -0
  145. package/client/assets/mermaid-parser.core-DC7NPJ_M-Ca6XzwfM.js +166 -0
  146. package/client/assets/mermaid-parser.core-SAwSf4_o.js +7 -0
  147. package/client/assets/mermaid.core-BBEQfkdJ.js +11 -0
  148. package/client/assets/mermaid.core-DZM3Ha-E-BxOo-RYG.js +11 -0
  149. package/client/assets/mindmap-definition-LN4V7U3C-B4BN0efL.js +96 -0
  150. package/client/assets/mindmap-definition-LN4V7U3C-DYtgcMsY-DynyLTNv.js +96 -0
  151. package/client/assets/ordinal-B9_Llu10-CU85fhaB.js +1 -0
  152. package/client/assets/ordinal-CopWnP7w.js +1 -0
  153. package/client/assets/packet-7NZHBO7P-CY4XRk9N.js +1 -0
  154. package/client/assets/packet-7NZHBO7P-lwb58iYx--vqSg65J.js +1 -0
  155. package/client/assets/path-BWPyau1x.js +1 -0
  156. package/client/assets/path-COivZ1Gw-C5riojK2.js +1 -0
  157. package/client/assets/pegDiagram-2B236MQR-BOKErjUu.js +1 -0
  158. package/client/assets/pegDiagram-2B236MQR-C43eIpKM-Bi_5Sxjt.js +1 -0
  159. package/client/assets/pie-RZYD4A2V-6BHB7bAx.js +1 -0
  160. package/client/assets/pie-RZYD4A2V-B1UWb4Gu-Dmn2hLWH.js +1 -0
  161. package/client/assets/pieDiagram-ENE6RG2P-BkTqgJyR-Bcdm19kR.js +39 -0
  162. package/client/assets/pieDiagram-ENE6RG2P-DtNjvz-U.js +39 -0
  163. package/client/assets/preload-helper-CZgWQFsJ.js +1 -0
  164. package/client/assets/purify.es-DY32g7DN.js +3 -0
  165. package/client/assets/quadrantDiagram-ABIIQ3AL-BQmAJL2v.js +7 -0
  166. package/client/assets/quadrantDiagram-ABIIQ3AL-Bm1Zjm45-B3orqx_f.js +7 -0
  167. package/client/assets/radar-I7S5WNFK-7CKcb_l--CqKdnR0v.js +1 -0
  168. package/client/assets/radar-I7S5WNFK-R7Jhjfdt.js +1 -0
  169. package/client/assets/railroad-3IZDKUUU-BepSPyHI.js +1 -0
  170. package/client/assets/railroad-3IZDKUUU-gCySKdnW-CKU26HTP.js +1 -0
  171. package/client/assets/railroad-abnf-AHOZXSZD-BlsHsp3c.js +1 -0
  172. package/client/assets/railroad-abnf-AHOZXSZD-BophH4r--CGkGHq54.js +1 -0
  173. package/client/assets/railroad-ebnf-EBAXGLYW-AZNjl_Zu-B7bn_PF3.js +1 -0
  174. package/client/assets/railroad-ebnf-EBAXGLYW-B4fMcGEH.js +1 -0
  175. package/client/assets/railroad-peg-LSFZ7HO6-B1ZrjJIu.js +1 -0
  176. package/client/assets/railroad-peg-LSFZ7HO6-BSiEEyeb-CP_B-N5g.js +1 -0
  177. package/client/assets/railroadDiagram-RFXS5EU6-8cz0Iby3.js +1 -0
  178. package/client/assets/railroadDiagram-RFXS5EU6-BkfbdeAs-Di6jH5I6.js +1 -0
  179. package/client/assets/requirementDiagram-TGXJPOKE-CrGTTjYg-DmIC7Shp.js +84 -0
  180. package/client/assets/requirementDiagram-TGXJPOKE-Dlqoe9TY.js +84 -0
  181. package/client/assets/rough.esm-CSKSodPl.js +1 -0
  182. package/client/assets/rough.esm-wVmwlXu7-CSKSodPl.js +1 -0
  183. package/client/assets/runtime-protocol-vue-WG3JLsjz.js +1 -0
  184. package/client/assets/runtime-vue-lHsY5zMj.js +1 -0
  185. package/client/assets/sankeyDiagram-HTMAVEWB-BkTKgu8Q.js +40 -0
  186. package/client/assets/sankeyDiagram-HTMAVEWB-rWXPf03Z-CjV9pCSJ.js +40 -0
  187. package/client/assets/{schemas-D_RbFtuQ.js → schemas-DGvl73AE.js} +3 -3
  188. package/client/assets/sequenceDiagram-DBY2YBRQ-BpuXUcFy.js +162 -0
  189. package/client/assets/sequenceDiagram-DBY2YBRQ-nkJYWO2m-BqS_sTCH.js +162 -0
  190. package/client/assets/sizeCapture-X5ZJPWSS-B0uUizjq.js +1 -0
  191. package/client/assets/sizeCapture-X5ZJPWSS-F5SadAuT-VHIb4_NU.js +1 -0
  192. package/client/assets/src-Brzfja-q-DNc4Or4z.js +1 -0
  193. package/client/assets/src-Cqa3Jy1j.js +1 -0
  194. package/client/assets/stateDiagram-2N3HPSRC-CdlGXuo7.js +1 -0
  195. package/client/assets/stateDiagram-2N3HPSRC-T4-clK8b-DQRiB4va.js +1 -0
  196. package/client/assets/stateDiagram-v2-6OUMAXLB-BN8Jr3ZM.js +1 -0
  197. package/client/assets/stateDiagram-v2-6OUMAXLB-DIp7nhRd-DbrOhUn_.js +1 -0
  198. package/client/assets/swimlanes-5IMT3BWC-HmQNEntu-CNBgZdur.js +2 -0
  199. package/client/assets/swimlanes-5IMT3BWC-iEJg5gki.js +2 -0
  200. package/client/assets/swimlanesDiagram-G3AALYLV-BeoZhwg7-DpqgeNRA.js +8 -0
  201. package/client/assets/swimlanesDiagram-G3AALYLV-o-sS6aEP.js +8 -0
  202. package/client/assets/timeline-definition-FHXFAJF6-Bjh-7QmL.js +120 -0
  203. package/client/assets/timeline-definition-FHXFAJF6-CNc9jSTP-BKNSymgY.js +120 -0
  204. package/client/assets/treeView-QDETBFTQ-D3cqRl6k.js +1 -0
  205. package/client/assets/treeView-QDETBFTQ-nIQcG1h9-O_ri9HxD.js +1 -0
  206. package/client/assets/treemap-6X3UGDF4-BnsvC8yL-Cfsty1e3.js +1 -0
  207. package/client/assets/treemap-6X3UGDF4-DGkZcvDG.js +1 -0
  208. package/client/assets/v4-B29ayslu.js +1 -0
  209. package/client/assets/vennDiagram-L72KCM5P-Cz3dbe7b.js +34 -0
  210. package/client/assets/vennDiagram-L72KCM5P-Dr3pTJ_0-C38xwcVo.js +34 -0
  211. package/client/assets/vue-C6d2VveO.js +1 -0
  212. package/client/assets/vue-i18n-CL-ejuNu.js +3 -0
  213. package/client/assets/vue.runtime.esm-bundler-BeoTfMNc.js +4 -0
  214. package/client/assets/wardley-OPB4EBWU-8Odxkx6V-CCDIVuBs.js +1 -0
  215. package/client/assets/wardley-OPB4EBWU-CZsCDvKk.js +1 -0
  216. package/client/assets/wardleyDiagram-EHGQE667-DSFc7ZZa-mlWXRiWx.js +78 -0
  217. package/client/assets/wardleyDiagram-EHGQE667-DiyoyO_X.js +78 -0
  218. package/client/assets/xychartDiagram-FW5EYKEG-BP0Nn4Pp-DRRiStGZ.js +7 -0
  219. package/client/assets/xychartDiagram-FW5EYKEG-DhY1_UGM.js +7 -0
  220. package/client/index.html +16 -12
  221. package/package.json +17 -19
  222. package/server/agent/backend/types.ts +1 -1
  223. package/server/agent/backgroundSessions.ts +43 -0
  224. package/server/agent/config.ts +3 -2
  225. package/server/agent/mcp-tools/manageCollection.ts +4 -4
  226. package/server/agent/sandboxMounts.ts +1 -1
  227. package/server/agent/stream.ts +1 -3
  228. package/server/api/auth/viewToken.ts +1 -1
  229. package/server/api/bridge/sessionRole.ts +47 -0
  230. package/server/api/routes/agent.ts +79 -2
  231. package/server/api/routes/collections.ts +60 -7
  232. package/server/api/routes/collectionsRegistry.ts +113 -0
  233. package/server/api/routes/dashboard.ts +47 -0
  234. package/server/api/routes/feeds.ts +2 -2
  235. package/server/api/routes/files.ts +1 -2
  236. package/server/api/routes/pdf.ts +48 -9
  237. package/server/api/routes/plugins.ts +2 -2
  238. package/server/api/routes/remoteHost.ts +55 -0
  239. package/server/api/routes/sessions.ts +20 -11
  240. package/server/api/routes/share.ts +105 -0
  241. package/server/api/routes/transcribe.ts +1 -1
  242. package/server/api/routes/wiki/history.ts +2 -2
  243. package/server/api/routes/wiki.ts +29 -177
  244. package/server/api/sandboxStatus.ts +1 -1
  245. package/server/build/dispatcher.mjs +25 -14928
  246. package/server/events/collection-change.ts +4 -4
  247. package/server/events/file-change.ts +2 -2
  248. package/server/events/scheduler-adapter.ts +3 -3
  249. package/server/events/task-manager/index.ts +3 -3
  250. package/server/index.ts +53 -17
  251. package/server/notifier/engine.ts +3 -3
  252. package/server/notifier/types.ts +2 -2
  253. package/server/plugins/html-builtin.ts +15 -1
  254. package/server/prompts/system/system.md +4 -0
  255. package/server/remoteHost/auth.ts +29 -0
  256. package/server/remoteHost/commandChannel.ts +56 -0
  257. package/server/remoteHost/firebase.ts +16 -0
  258. package/server/remoteHost/handlers/collectionPage.ts +29 -0
  259. package/server/remoteHost/handlers/getCollection.ts +32 -0
  260. package/server/remoteHost/handlers/getFeed.ts +35 -0
  261. package/server/remoteHost/handlers/index.ts +19 -0
  262. package/server/remoteHost/handlers/listCollections.ts +29 -0
  263. package/server/remoteHost/handlers/listFeeds.ts +37 -0
  264. package/server/remoteHost/handlers/listShortcuts.ts +24 -0
  265. package/server/remoteHost/handlers/startChat.ts +78 -0
  266. package/server/remoteHost/hostRunner.ts +122 -0
  267. package/server/remoteHost/index.ts +117 -0
  268. package/server/system/config.ts +1 -1
  269. package/server/system/credentials.ts +2 -3
  270. package/server/system/docker.ts +9 -8
  271. package/server/system/env.ts +8 -0
  272. package/server/system/whisper/index.ts +4 -4
  273. package/server/utils/claudeConfigPath.ts +57 -0
  274. package/server/utils/files/dashboard-io.ts +87 -0
  275. package/server/utils/files/session-io.ts +11 -0
  276. package/server/utils/markdown/frontmatter.ts +8 -4
  277. package/server/utils/regex.ts +4 -3
  278. package/server/utils/share/packHtml.ts +108 -0
  279. package/server/utils/share/rewriteAssets.ts +178 -0
  280. package/server/workspace/chat-index/paths.ts +8 -2
  281. package/server/workspace/chat-index/summarizer.ts +23 -15
  282. package/server/workspace/collections/configure.ts +5 -3
  283. package/server/workspace/collections/index.ts +8 -6
  284. package/server/workspace/collections/notifications.ts +3 -3
  285. package/server/workspace/collections/types.ts +2 -2
  286. package/server/workspace/collections/watcher.ts +2 -2
  287. package/server/workspace/feeds/configure.ts +24 -0
  288. package/server/workspace/hooks/handlers/skillBridge.ts +2 -2
  289. package/server/workspace/hooks/handlers/wikiSnapshot.ts +1 -1
  290. package/server/workspace/paths.ts +8 -1
  291. package/server/workspace/skills/catalog.ts +1 -3
  292. package/server/workspace/skills/external/catalog.ts +1 -3
  293. package/server/workspace/skills/paths.ts +4 -3
  294. package/server/workspace/skills/writer.ts +1 -4
  295. package/server/workspace/skills-preset.ts +3 -3
  296. package/server/workspace/wiki-pages/io.ts +2 -2
  297. package/server/workspace/workspace.ts +2 -2
  298. package/src/App.vue +41 -18
  299. package/src/components/ChatInput.vue +1 -1
  300. package/src/components/DashboardView.vue +372 -0
  301. package/src/components/FileContentRenderer.vue +32 -7
  302. package/src/components/JsonEditor.vue +1 -1
  303. package/src/components/PluginLauncher.vue +122 -79
  304. package/src/components/RemoteHostControl.vue +159 -0
  305. package/src/components/SessionCountBadges.vue +32 -0
  306. package/src/components/SessionHeaderControls.vue +1 -8
  307. package/src/components/SessionHistoryPanel.vue +2 -0
  308. package/src/components/SessionHistoryToggleButton.vue +1 -15
  309. package/src/components/SidebarHeader.vue +2 -0
  310. package/src/components/collectionTypes.ts +2 -2
  311. package/src/composables/accountingHost.ts +24 -0
  312. package/src/composables/collections/uiHost.ts +17 -2
  313. package/src/composables/useDashboard.ts +191 -0
  314. package/src/composables/useMarkdownZip.ts +51 -0
  315. package/src/composables/useNotifications.ts +1 -4
  316. package/src/composables/useSharePack.ts +60 -0
  317. package/src/composables/useTranslatedQueries.ts +15 -95
  318. package/src/composables/useTranslatedStrings.ts +68 -0
  319. package/src/composables/useVoiceInput.ts +3 -3
  320. package/src/config/apiRoutes.ts +57 -9
  321. package/src/config/createFilePolicy.ts +1 -1
  322. package/src/config/firebase.ts +12 -0
  323. package/src/config/firebaseConfig.ts +18 -0
  324. package/src/config/historyFilters.ts +7 -0
  325. package/src/config/pubsubChannels.ts +7 -27
  326. package/src/config/roles.ts +12 -10
  327. package/src/config/workspacePaths.ts +10 -0
  328. package/src/index.css +1 -1
  329. package/src/lang/de.ts +39 -220
  330. package/src/lang/en.ts +39 -191
  331. package/src/lang/es.ts +39 -218
  332. package/src/lang/fr.ts +39 -220
  333. package/src/lang/ja.ts +39 -216
  334. package/src/lang/ko.ts +39 -216
  335. package/src/lang/pt-BR.ts +39 -217
  336. package/src/lang/zh.ts +39 -215
  337. package/src/main.ts +3 -0
  338. package/src/plugins/accounting/definition.ts +12 -8
  339. package/src/plugins/accounting/index.ts +7 -4
  340. package/src/plugins/accounting/meta.ts +23 -60
  341. package/src/plugins/manageSkills/View.vue +9 -1
  342. package/src/plugins/photoLocations/View.vue +2 -2
  343. package/src/plugins/presentCollection/definition.ts +2 -2
  344. package/src/plugins/presentCollection/plugin.ts +2 -2
  345. package/src/plugins/presentCollection/types.ts +2 -2
  346. package/src/plugins/presentHtml/definition.ts +1 -1
  347. package/src/plugins/presentMulmoScript/View.vue +2 -2
  348. package/src/plugins/presentMulmoScript/helpers.ts +1 -1
  349. package/src/plugins/presentSVG/View.vue +2 -2
  350. package/src/plugins/scheduler/TasksTab.vue +1 -2
  351. package/src/plugins/skill/View.vue +7 -3
  352. package/src/plugins/spreadsheet/View.vue +3 -3
  353. package/src/plugins/spreadsheet/engine/functions/logical.ts +8 -1
  354. package/src/plugins/textResponse/View.vue +43 -16
  355. package/src/plugins/wiki/View.vue +29 -2
  356. package/src/plugins/wiki/components/WikiGraphView.vue +1 -1
  357. package/src/plugins/wiki/components/WikiPageBody.vue +4 -1
  358. package/src/plugins/wiki/helpers.ts +13 -81
  359. package/src/plugins/wiki/index.ts +1 -1
  360. package/src/router/guards.ts +1 -1
  361. package/src/router/index.ts +12 -0
  362. package/src/router/pageRoutes.ts +2 -0
  363. package/src/types/dashboard.ts +40 -0
  364. package/src/types/session.ts +3 -0
  365. package/src/utils/blobDownload.ts +19 -0
  366. package/src/utils/collections/presentSeed.ts +1 -1
  367. package/src/utils/html/customViewSrcdoc.ts +31 -3
  368. package/src/utils/html/previewCsp.ts +1 -1
  369. package/src/utils/id.ts +1 -1
  370. package/src/utils/image/htmlSrcAttrs.ts +1 -1
  371. package/src/utils/markdown/frontmatter.ts +12 -7
  372. package/src/utils/markdown/highlight.ts +22 -0
  373. package/src/utils/markdown/marpCustomSize.ts +2 -2
  374. package/src/utils/markdown/mermaidExtension.ts +56 -0
  375. package/src/utils/markdown/mermaidRender.ts +147 -0
  376. package/src/utils/markdown/setup.ts +17 -4
  377. package/src/utils/markdown/useMermaid.ts +33 -0
  378. package/src/utils/session/longRunning.ts +33 -0
  379. package/src/utils/session/mergeSessions.ts +1 -1
  380. package/client/assets/JsonEditor-Di5xGeZY.css +0 -1
  381. package/client/assets/JsonEditor-o5--tPQH.js +0 -10
  382. package/client/assets/index-tOu5ArRZ.css +0 -2
  383. package/client/assets/lib-D6Xy0IFc.js +0 -114
  384. package/client/assets/marp-D6GXA-EB.js +0 -3452
  385. package/client/assets/material-symbols-outlined-DtIK7AQn.woff2 +0 -0
  386. package/client/assets/runtime-protocol-vue-pU0Mw7Zm.js +0 -1
  387. package/client/assets/runtime-vue-fFYhnNg3.js +0 -1
  388. package/client/assets/vue-UDIWDtr8.js +0 -1
  389. package/client/assets/vue-i18n-CQbxVmNs.js +0 -3
  390. package/client/assets/vue.runtime.esm-bundler-BTyIdNAI.js +0 -4
  391. package/server/accounting/accountNormalize.ts +0 -32
  392. package/server/accounting/defaultAccounts.ts +0 -87
  393. package/server/accounting/eventPublisher.ts +0 -52
  394. package/server/accounting/journal.ts +0 -252
  395. package/server/accounting/openingBalances.ts +0 -114
  396. package/server/accounting/report.ts +0 -237
  397. package/server/accounting/service.ts +0 -718
  398. package/server/accounting/snapshotCache.ts +0 -334
  399. package/server/accounting/timeSeries.ts +0 -265
  400. package/server/accounting/types.ts +0 -148
  401. package/server/api/routes/accounting.ts +0 -373
  402. package/server/api/routes/wiki/frontmatter.ts +0 -34
  403. package/server/api/routes/wiki/pageIndex.ts +0 -53
  404. package/server/system/logs/aaa +0 -737
  405. package/server/system/logs/bb +0 -446
  406. package/server/utils/files/accounting-io.ts +0 -294
  407. package/server/workspace/feeds/engine.ts +0 -143
  408. package/server/workspace/feeds/fetch/httpClient.ts +0 -127
  409. package/server/workspace/feeds/fetch/rssParser.ts +0 -117
  410. package/server/workspace/feeds/index.ts +0 -8
  411. package/server/workspace/feeds/ingestTypes.ts +0 -62
  412. package/server/workspace/feeds/pathResolver.ts +0 -74
  413. package/server/workspace/feeds/paths.ts +0 -30
  414. package/server/workspace/feeds/projectItem.ts +0 -92
  415. package/server/workspace/feeds/registry.ts +0 -43
  416. package/server/workspace/feeds/retrievers/httpJson.ts +0 -19
  417. package/server/workspace/feeds/retrievers/index.ts +0 -27
  418. package/server/workspace/feeds/retrievers/registerAll.ts +0 -5
  419. package/server/workspace/feeds/retrievers/rss.ts +0 -24
  420. package/server/workspace/feeds/state.ts +0 -55
  421. package/src/composables/useAccountingChannel.ts +0 -58
  422. package/src/lib/wiki-page/graph.ts +0 -108
  423. package/src/lib/wiki-page/index-parse.ts +0 -221
  424. package/src/lib/wiki-page/link.ts +0 -62
  425. package/src/lib/wiki-page/lint.ts +0 -105
  426. package/src/lib/wiki-page/paths.ts +0 -35
  427. package/src/lib/wiki-page/slug.ts +0 -54
  428. package/src/plugins/accounting/Preview.vue +0 -103
  429. package/src/plugins/accounting/View.vue +0 -633
  430. package/src/plugins/accounting/actions.ts +0 -34
  431. package/src/plugins/accounting/api.ts +0 -301
  432. package/src/plugins/accounting/components/AccountEditor.vue +0 -250
  433. package/src/plugins/accounting/components/AccountRow.vue +0 -50
  434. package/src/plugins/accounting/components/AccountsList.vue +0 -102
  435. package/src/plugins/accounting/components/AccountsModal.vue +0 -301
  436. package/src/plugins/accounting/components/BalanceSheet.vue +0 -186
  437. package/src/plugins/accounting/components/BookSettings.vue +0 -284
  438. package/src/plugins/accounting/components/BookSwitcher.vue +0 -78
  439. package/src/plugins/accounting/components/DateRangePicker.vue +0 -140
  440. package/src/plugins/accounting/components/JournalEntryForm.vue +0 -505
  441. package/src/plugins/accounting/components/JournalList.vue +0 -554
  442. package/src/plugins/accounting/components/Ledger.vue +0 -206
  443. package/src/plugins/accounting/components/NewBookForm.vue +0 -211
  444. package/src/plugins/accounting/components/OpeningBalancesForm.vue +0 -272
  445. package/src/plugins/accounting/components/ProfitLoss.vue +0 -160
  446. package/src/plugins/accounting/components/accountDraft.ts +0 -13
  447. package/src/plugins/accounting/components/accountNumbering.ts +0 -103
  448. package/src/plugins/accounting/components/accountValidation.ts +0 -75
  449. package/src/plugins/accounting/components/useLatestRequest.ts +0 -44
  450. package/src/plugins/accounting/countries.ts +0 -158
  451. package/src/plugins/accounting/currencies.ts +0 -77
  452. package/src/plugins/accounting/dates.ts +0 -51
  453. package/src/plugins/accounting/fiscalYear.ts +0 -136
  454. package/src/plugins/accounting/timeSeriesEnums.ts +0 -16
  455. package/src/plugins/wiki/route.ts +0 -137
  456. /package/client/assets/{_plugin-vue_export-helper-B67ILkmu.js → _plugin-vue_export-helper-BDNMzG2s.js} +0 -0
  457. /package/client/assets/{purify.es-B27wDFIb-51iYcXuK.js → purify.es-B27wDFIb-Bu4Grnl0.js} +0 -0
  458. /package/client/assets/{typeof-DBp4T-Ny-z2wCIsir.js → typeof-DBp4T-Ny-B5XbjTb1.js} +0 -0
@@ -121,10 +121,7 @@ export interface DeleteSkillInput {
121
121
  }
122
122
 
123
123
  export type DeleteResult =
124
- | { kind: "deleted"; name: string }
125
- | { kind: "invalid-slug"; slug: string }
126
- | { kind: "not-found"; name: string }
127
- | { kind: "user-scope"; name: string };
124
+ { kind: "deleted"; name: string } | { kind: "invalid-slug"; slug: string } | { kind: "not-found"; name: string } | { kind: "user-scope"; name: string };
128
125
 
129
126
  /**
130
127
  * Remove a project-scope skill (SKILL.md + its containing folder).
@@ -1,9 +1,9 @@
1
1
  // Host shim — the preset-skill sync logic + the bundled assets (helps + preset
2
- // skills) now live in @mulmoclaude/workspace-setup (shared with MulmoTerminal so the
2
+ // skills) now live in @mulmoclaude/core/workspace-setup (shared with MulmoTerminal so the
3
3
  // two apps seed a fresh workspace identically). Re-exported here so existing SERVER
4
4
  // importers (the catalog, collections/configure) keep their import paths.
5
5
  //
6
- // NOTE for Vue/browser code: import `isPresetSlug` from "@mulmoclaude/workspace-setup/slug"
6
+ // NOTE for Vue/browser code: import `isPresetSlug` from "@mulmoclaude/core/workspace-setup/slug"
7
7
  // (browser-safe) — NOT from here. This `.` re-export pulls in node:fs via the sync code.
8
8
  export {
9
9
  syncPresetSkills,
@@ -13,4 +13,4 @@ export {
13
13
  type SyncPresetSkillsResult,
14
14
  type SyncActivePresetSkillsOptions,
15
15
  type SyncActivePresetSkillsResult,
16
- } from "@mulmoclaude/workspace-setup";
16
+ } from "@mulmoclaude/core/workspace-setup";
@@ -23,8 +23,8 @@ import { mkdir, writeFile } from "node:fs/promises";
23
23
  import { readTextSafe } from "../../utils/files/safe.js";
24
24
  import { writeFileAtomic } from "../../utils/files/atomic.js";
25
25
  import { mergeFrontmatter, parseFrontmatter, serializeWithFrontmatter } from "../../utils/markdown/frontmatter.js";
26
- import { isSafeSlug } from "../../../src/lib/wiki-page/slug.js";
27
- import { wikiSlugFromAbsPath } from "../../../src/lib/wiki-page/paths.js";
26
+ import { isSafeSlug } from "@mulmoclaude/core/wiki";
27
+ import { wikiSlugFromAbsPath } from "@mulmoclaude/core/wiki/paths";
28
28
  import { workspacePath as defaultWorkspacePath } from "../workspace.js";
29
29
  import { WORKSPACE_DIRS } from "../paths.js";
30
30
  import { appendSnapshot } from "./snapshot.js";
@@ -5,9 +5,9 @@ import { log } from "../system/logger/index.js";
5
5
  import { EAGER_WORKSPACE_DIRS, WORKSPACE_PATHS, workspacePath } from "./paths.js";
6
6
  import { readWorkspaceTextSync, writeWorkspaceTextSync } from "../utils/files/workspace-io.js";
7
7
  import { loadCustomDirs, ensureCustomDirs } from "./custom-dirs.js";
8
- // Helps + preset skills + their sync logic now live in @mulmoclaude/workspace-setup
8
+ // Helps + preset skills + their sync logic now live in @mulmoclaude/core/workspace-setup
9
9
  // (shared with MulmoTerminal); the bundled source dirs come from the package.
10
- import { seedHelps, presetSkillsAssetDir, syncActivePresetSkills, syncPresetSkills } from "@mulmoclaude/workspace-setup";
10
+ import { seedHelps, presetSkillsAssetDir, syncActivePresetSkills, syncPresetSkills } from "@mulmoclaude/core/workspace-setup";
11
11
 
12
12
  // Re-exported so existing callers (`import { workspacePath } from
13
13
  // "./workspace.js"`) keep working. See workspace-paths.ts for the
package/src/App.vue CHANGED
@@ -21,25 +21,28 @@
21
21
  :active-tool-name="selectedResult?.toolName ?? null"
22
22
  :active-view-mode="currentPage"
23
23
  :shortcuts="shortcuts"
24
+ :active-session-count="activeSessionCount"
25
+ :unread-count="unreadCount"
24
26
  @navigate="onPluginNavigate"
25
27
  @navigate-shortcut="onShortcutNavigate"
28
+ @navigate-chat="handleHomeClick"
26
29
  />
27
30
  </div>
28
31
  </div>
29
- <!-- Row 2: role selector + session tabs. Shown whenever the
30
- side panel is hidden Row 2 and the side panel are
31
- mutually exclusive. The header-controls wrapper is pinned
32
- to 264px (w-72 minus px-3 padding on each side) so that
32
+ <!-- Row 2: role selector + session tabs. Chat-only chrome — shown
33
+ on /chat whenever the side panel is hidden (Row 2 and the
34
+ side panel are mutually exclusive). Off /chat the whole row
35
+ is gone; the always-visible Chat button in Row 1 is the way
36
+ back into a conversation. The header-controls wrapper is
37
+ pinned to 264px (w-72 minus px-3 padding on each side) so
33
38
  RoleSelector / + / toggle occupy the exact same x-range as
34
39
  they do inside the open side panel — toggling the panel
35
40
  therefore doesn't shift those controls. -->
36
- <div v-if="!sidePanelVisible" class="flex items-center gap-2 px-3 py-2 border-b border-gray-100">
41
+ <div v-if="isChatPage && !sidePanelVisible" class="flex items-center gap-2 px-3 py-2 border-b border-gray-100">
37
42
  <div class="w-[264px] shrink-0">
38
43
  <SessionHeaderControls
39
44
  :roles="roles"
40
45
  :side-panel-visible="sidePanelVisible"
41
- :active-session-count="activeSessionCount"
42
- :unread-count="unreadCount"
43
46
  @role-change="onRoleChange"
44
47
  @new-session="handleNewSessionClick"
45
48
  @update:side-panel-visible="setSidePanelVisible"
@@ -53,12 +56,12 @@
53
56
  <div class="flex flex-1 min-h-0">
54
57
  <!-- Session-history side panel. Opt-in column to the left of
55
58
  the chat sidebar / canvas, toggled via
56
- SessionHistoryToggleButton. Renders on every page when
57
- `sidePanelVisible` is true. Row 2 of the top bar hides when
58
- the panel is open — the panel's own header supplies the
59
- role selector + new-session button instead. -->
59
+ SessionHistoryToggleButton. Chat-only chrome renders on
60
+ /chat when `sidePanelVisible` is true. Row 2 of the top bar
61
+ hides when the panel is open — the panel's own header
62
+ supplies the role selector + new-session button instead. -->
60
63
  <div
61
- v-if="sidePanelVisible"
64
+ v-if="isChatPage && sidePanelVisible"
62
65
  class="relative border-r border-gray-200 bg-white text-gray-900 flex flex-col min-w-0 overflow-hidden"
63
66
  :class="sidePanelExpanded ? 'flex-1' : 'w-72 flex-shrink-0'"
64
67
  data-testid="session-history-side-panel"
@@ -72,8 +75,6 @@
72
75
  <SessionHeaderControls
73
76
  :roles="roles"
74
77
  :side-panel-visible="sidePanelVisible"
75
- :active-session-count="activeSessionCount"
76
- :unread-count="unreadCount"
77
78
  @role-change="onRoleChange"
78
79
  @new-session="handleNewSessionClick"
79
80
  @update:side-panel-visible="setSidePanelVisibleAndCollapse"
@@ -211,6 +212,11 @@
211
212
  variants; standalone routes are wrapped here against the
212
213
  same `pkg-name + endpoints` pair so the `useRuntime()`
213
214
  call resolves. -->
215
+ <!-- Dashboard — grid of favorite (pinned) collections, each a
216
+ live embedded view. Host component (no PluginScopedRoot):
217
+ CollectionView talks to /api/collections directly, same as
218
+ the collections index below. -->
219
+ <DashboardView v-else-if="currentPage === 'dashboard'" />
214
220
  <FilesView v-else-if="currentPage === 'files'" :refresh-token="filesRefreshToken" @load-session="handleSessionSelect" />
215
221
  <PluginScopedRoot v-else-if="currentPage === 'automations'" pkg-name="scheduler" :endpoints="API_ROUTES.scheduler">
216
222
  <AutomationsView />
@@ -228,6 +234,13 @@
228
234
  directly. -->
229
235
  <CollectionView v-else-if="currentPage === 'collections' && route.params.slug" :key="String(route.params.slug)" />
230
236
  <CollectionsIndexView v-else-if="currentPage === 'collections'" />
237
+ <!-- Accounting — the double-entry bookkeeping app. Host
238
+ component (no PluginScopedRoot): the View talks to
239
+ /api/accounting via the host's apiCall directly and never
240
+ calls `useRuntime()`, so it needs no plugin scope. Mounted
241
+ without a `selected-result` prop — standalone it self-fetches
242
+ the book list and auto-selects a book on mount. -->
243
+ <AccountingView v-else-if="currentPage === 'accounting'" />
231
244
  <!-- Debug page (encore plan PR 1 follow-up). The View ships
232
245
  inside the @mulmoclaude/debug-plugin runtime package; we
233
246
  look it up by tool name and render the registered
@@ -301,7 +314,7 @@
301
314
  <!-- Global confirm dialog. Renders the module-global confirm state opened
302
315
  via useConfirm()/the collection plugin's confirm() capability — mounted
303
316
  here at the app root so it survives any single view (CollectionView used
304
- to render its own before moving into @mulmoclaude/collection-plugin). -->
317
+ to render its own before moving into @mulmoclaude/core/collection). -->
305
318
  <ConfirmModal />
306
319
  </div>
307
320
  </template>
@@ -324,11 +337,13 @@ import SessionHistoryPanel from "./components/SessionHistoryPanel.vue";
324
337
  import SessionSidebar from "./components/SessionSidebar.vue";
325
338
  import ThinkingIndicator from "./components/ThinkingIndicator.vue";
326
339
  import PluginLauncher from "./components/PluginLauncher.vue";
340
+ import DashboardView from "./components/DashboardView.vue";
327
341
  import StackView from "./components/StackView.vue";
328
342
  import FilesView from "./components/FilesView.vue";
329
343
  import AutomationsView from "./plugins/scheduler/AutomationsView.vue";
330
344
  import WikiView from "./plugins/wiki/View.vue";
331
- import { buildWikiRouteParams } from "./plugins/wiki/route";
345
+ import { AccountingView } from "@mulmoclaude/accounting-plugin/vue";
346
+ import { buildWikiRouteParams } from "@mulmoclaude/core/wiki";
332
347
  import { CollectionView, CollectionsIndexView, FeedsView } from "@mulmoclaude/collection-plugin/vue";
333
348
  import PluginScopedRoot from "./components/PluginScopedRoot.vue";
334
349
  import SettingsModal from "./components/SettingsModal.vue";
@@ -371,7 +386,7 @@ import ConfirmModal from "./components/ConfirmModal.vue";
371
386
  import { useNotifications } from "./composables/useNotifications";
372
387
  import { collectionNotifiedSeverities } from "./utils/collections/notifiedItems";
373
388
  import { installCollectionAppBindings } from "./composables/collections/uiHost";
374
- import type { CollectionsListResponse } from "@mulmoclaude/collection-plugin";
389
+ import type { CollectionsListResponse } from "@mulmoclaude/core/collection";
375
390
  import { useHealth } from "./composables/useHealth";
376
391
  import { useSessionHistory } from "./composables/useSessionHistory";
377
392
  import { useRightSidebar } from "./composables/useRightSidebar";
@@ -588,7 +603,7 @@ const showSettings = ref(false);
588
603
  // When the Settings modal closes, re-check voice-input availability: the
589
604
  // user may have just enabled it / started the model download, and the
590
605
  // mic button should appear without a reload (the composable then polls
591
- // until the download finishes). See plans/feat-voice-input.md.
606
+ // until the download finishes). See plans/done/feat-voice-input.md.
592
607
  function onSettingsOpenChange(open: boolean): void {
593
608
  showSettings.value = open;
594
609
  if (!open) chatInputRef.value?.refreshVoiceAvailability()?.catch(() => undefined);
@@ -678,10 +693,18 @@ const canvasDropHandlers = computed(() =>
678
693
  // don't persist empty sessions on the server. Fires true → false only;
679
694
  // an empty → /chat transition is handled by the route-params watcher
680
695
  // and onMounted.
696
+ //
697
+ // Also collapse the side panel's transient full-width mode. The panel
698
+ // itself is chat-only chrome (`isChatPage && sidePanelVisible`), so it
699
+ // unmounts off /chat — but the canvas/sidebar stay gated by
700
+ // `!sidePanelExpanded`. Without this reset, leaving /chat while expanded
701
+ // would unmount the panel AND keep the canvas hidden, blanking plugin
702
+ // pages until the panel is collapsed again.
681
703
  watch(isChatPage, (isChat, wasChat) => {
682
704
  if (!(wasChat && !isChat)) return;
683
705
  removeCurrentIfEmpty();
684
706
  currentSessionId.value = "";
707
+ sidePanelExpanded.value = false;
685
708
  });
686
709
 
687
710
  function handleSessionSelect(sessionId: string): void {
@@ -161,7 +161,7 @@ const emit = defineEmits<{
161
161
 
162
162
  // Local voice input (Mac-only). The mic button is hidden unless the
163
163
  // backend reports voice input ready; transcripts are appended to the
164
- // input for review, never auto-sent. See plans/feat-voice-input.md.
164
+ // input for review, never auto-sent. See plans/done/feat-voice-input.md.
165
165
  function insertTranscript(text: string): void {
166
166
  const current = props.modelValue;
167
167
  const next = current.trim().length > 0 ? `${current.trimEnd()} ${text}` : text;
@@ -0,0 +1,372 @@
1
+ <template>
2
+ <div class="h-full overflow-auto p-4" :class="{ 'select-none': isResizing || isReordering }" data-testid="dashboard-view">
3
+ <!-- Empty state: no pinned collections yet. -->
4
+ <div v-if="favoriteSlugs.length === 0" class="h-full flex flex-col items-center justify-center text-center text-slate-500 gap-2">
5
+ <span class="material-icons text-4xl text-slate-300">dashboard</span>
6
+ <p class="text-sm font-medium">{{ t("dashboard.empty") }}</p>
7
+ <p class="text-xs">{{ t("dashboard.emptyHint") }}</p>
8
+ </div>
9
+
10
+ <!-- Two-column grid of favorite collections. Each tile shows a live
11
+ embedded CollectionView in the tile's chosen view mode. Height is
12
+ per grid ROW (both side-by-side tiles share it), set via the
13
+ resize handle and stored positionally in the layout. -->
14
+ <div v-else class="grid gap-4 sm:grid-cols-2" data-testid="dashboard-grid">
15
+ <section
16
+ v-for="(tile, index) in tiles"
17
+ v-show="metaFor(tile.slug)"
18
+ :key="tile.slug"
19
+ class="flex flex-col border border-slate-200 rounded-lg bg-white overflow-hidden shadow-sm transition-opacity"
20
+ :class="{ 'ring-2 ring-indigo-400': dropIndex === index && dragIndex !== index, 'opacity-50': dragIndex === index }"
21
+ :data-dashboard-index="index"
22
+ :data-testid="`dashboard-tile-${tile.slug}`"
23
+ >
24
+ <!-- Tile header: drag handle + title/icon (opens full view) + view picker. -->
25
+ <header class="flex items-center gap-2 px-3 py-2 border-b border-slate-100 bg-slate-50">
26
+ <span
27
+ class="material-icons text-base text-slate-400 cursor-grab select-none touch-none"
28
+ :title="t('dashboard.dragHint')"
29
+ :aria-label="t('dashboard.dragHint')"
30
+ data-testid="dashboard-tile-drag"
31
+ @pointerdown.prevent="onReorderStart(index, $event)"
32
+ >drag_indicator</span
33
+ >
34
+ <button
35
+ type="button"
36
+ class="flex items-center gap-1.5 min-w-0 flex-1 text-left text-sm font-semibold text-slate-700 hover:text-indigo-600"
37
+ :title="t('dashboard.openFull')"
38
+ @click="onTitleActivate($event, tile.slug)"
39
+ @dblclick="openFull(tile.slug)"
40
+ >
41
+ <span class="material-symbols-outlined text-base flex-none">{{ metaFor(tile.slug)?.icon || "apps" }}</span>
42
+ <span class="truncate">{{ metaFor(tile.slug)?.title || tile.slug }}</span>
43
+ </button>
44
+ <select
45
+ class="flex-none text-xs bg-white border border-slate-200 rounded px-1.5 py-1 text-slate-600 focus:outline-none focus:ring-2 focus:ring-indigo-500/20"
46
+ :value="effectiveView(tile)"
47
+ :aria-label="t('dashboard.viewPickerLabel')"
48
+ :data-testid="`dashboard-tile-view-${tile.slug}`"
49
+ @change="onPickView(tile.slug, $event)"
50
+ >
51
+ <option v-for="mode in modesFor(tile.slug)" :key="mode" :value="mode">{{ modeLabel(tile.slug, mode) }}</option>
52
+ </select>
53
+ </header>
54
+ <!-- Live embedded view. `:key` remounts the view when the mode
55
+ changes so the new initial view takes effect. Height is the
56
+ grid ROW's height (shared by both side-by-side tiles), set via
57
+ the resize handle below. -->
58
+ <div class="overflow-auto" :style="{ height: `${rowHeight(index)}px` }">
59
+ <CollectionView
60
+ :key="`${tile.slug}:${effectiveView(tile)}`"
61
+ :slug="tile.slug"
62
+ :initial-view="effectiveView(tile)"
63
+ hide-view-toggle
64
+ hide-header
65
+ hide-search
66
+ />
67
+ </div>
68
+ <!-- Resize handle: drag vertically to set this row's height. -->
69
+ <div
70
+ class="h-2 flex-none cursor-row-resize bg-slate-100 hover:bg-indigo-200 transition-colors flex items-center justify-center"
71
+ :title="t('dashboard.resizeHint')"
72
+ :aria-label="t('dashboard.resizeHint')"
73
+ :data-testid="`dashboard-tile-resize-${tile.slug}`"
74
+ @pointerdown.prevent="onResizeStart(index, $event)"
75
+ >
76
+ <span class="h-0.5 w-8 rounded-full bg-slate-300"></span>
77
+ </div>
78
+ </section>
79
+ </div>
80
+ </div>
81
+ </template>
82
+
83
+ <script setup lang="ts">
84
+ import { computed, onBeforeUnmount, onMounted, ref, watch } from "vue";
85
+ import { useI18n } from "vue-i18n";
86
+ import { useRouter } from "vue-router";
87
+ import { CollectionView, applicableViewModes, customViewKey, type CollectionViewMode } from "@mulmoclaude/collection-plugin/vue";
88
+ import { useShortcuts } from "../composables/useShortcuts";
89
+ import { useDashboard } from "../composables/useDashboard";
90
+ import { apiGet } from "../utils/api";
91
+ import { API_ROUTES } from "../config/apiRoutes";
92
+ import { PAGE_ROUTES } from "../router/pageRoutes";
93
+ import type { DashboardTile } from "../types/dashboard";
94
+ import type { CollectionDetailResponse, CollectionSchema } from "./collectionTypes";
95
+
96
+ const { t } = useI18n();
97
+ const router = useRouter();
98
+
99
+ const { shortcuts, load: loadShortcuts } = useShortcuts();
100
+ const { tiles, rowHeights, load: loadDashboard, reconcile, setTiles, setViewMode, setRowHeight } = useDashboard();
101
+
102
+ // Favorites = pinned COLLECTION shortcuts (feeds are excluded — the
103
+ // dashboard is a collections surface). The cached title/icon ride along
104
+ // so a tile renders without an extra fetch.
105
+ const collectionFavorites = computed(() => shortcuts.value.filter((entry) => entry.kind === "collection"));
106
+ const favoriteSlugs = computed(() => collectionFavorites.value.map((entry) => entry.slug));
107
+ const metaFor = (slug: string) => collectionFavorites.value.find((entry) => entry.slug === slug);
108
+
109
+ // Per-slug schema, fetched lazily to drive each tile's view picker.
110
+ const schemas = ref<Record<string, CollectionSchema>>({});
111
+
112
+ async function loadSchema(slug: string): Promise<void> {
113
+ if (schemas.value[slug]) return;
114
+ const url = API_ROUTES.collections.detail.replace(":slug", encodeURIComponent(slug));
115
+ const result = await apiGet<CollectionDetailResponse>(url);
116
+ if (result.ok) schemas.value = { ...schemas.value, [slug]: result.data.collection.schema };
117
+ }
118
+
119
+ function modesFor(slug: string): CollectionViewMode[] {
120
+ const schema = schemas.value[slug];
121
+ return schema ? applicableViewModes(schema) : ["table"];
122
+ }
123
+
124
+ /** The view a tile opens in: its stored mode when still applicable, else
125
+ * the first available mode (table). */
126
+ function effectiveView(tile: DashboardTile): CollectionViewMode {
127
+ const modes = modesFor(tile.slug);
128
+ return tile.viewMode && modes.includes(tile.viewMode as CollectionViewMode) ? (tile.viewMode as CollectionViewMode) : (modes[0] ?? "table");
129
+ }
130
+
131
+ const BUILTIN_LABEL_KEYS: Record<string, string> = {
132
+ table: "dashboard.viewTable",
133
+ calendar: "dashboard.viewCalendar",
134
+ kanban: "dashboard.viewKanban",
135
+ };
136
+
137
+ function modeLabel(slug: string, mode: CollectionViewMode): string {
138
+ const builtin = BUILTIN_LABEL_KEYS[mode];
139
+ if (builtin) return t(builtin);
140
+ // Custom view: use the author-authored label from the schema.
141
+ const view = schemas.value[slug]?.views?.find((entry) => customViewKey(entry.id) === mode);
142
+ return view?.label ?? mode;
143
+ }
144
+
145
+ function onPickView(slug: string, event: Event): void {
146
+ const { value } = event.target as HTMLSelectElement;
147
+ void setViewMode(slug, value);
148
+ }
149
+
150
+ function openFull(slug: string): void {
151
+ router.push({ name: PAGE_ROUTES.collections, params: { slug } }).catch(() => {});
152
+ }
153
+
154
+ // Keyboard (Enter/Space on a native <button>) and assistive-tech "activate"
155
+ // both dispatch a click with detail 0, whereas a real mouse click has
156
+ // detail >= 1. Act only on the former so keyboard/AT users can open the full
157
+ // view, while a plain mouse single-click does NOT navigate — only the
158
+ // double-click does (the requested pointer UX).
159
+ function onTitleActivate(event: MouseEvent, slug: string): void {
160
+ if (event.detail === 0) openFull(slug);
161
+ }
162
+
163
+ // ── Drag-to-reorder via pointer events. Native HTML5 DnD conflicted with
164
+ // the embedded views' own card drag-and-drop (e.g. kanban), so drops
165
+ // over a tile body were swallowed — pointer + elementFromPoint avoids
166
+ // that entirely, the same robust approach as the row resize. ──
167
+ const dragIndex = ref<number | null>(null);
168
+ const dropIndex = ref<number | null>(null);
169
+ const isReordering = ref(false);
170
+ let reorder: { startIndex: number; handle: HTMLElement; pointerId: number } | null = null;
171
+
172
+ /** The tile index under a viewport point, or null when over no tile. */
173
+ function tileIndexAtPoint(clientX: number, clientY: number): number | null {
174
+ const section = document.elementFromPoint(clientX, clientY)?.closest<HTMLElement>("[data-dashboard-index]");
175
+ if (!section) return null;
176
+ const index = Number(section.dataset.dashboardIndex);
177
+ return Number.isNaN(index) ? null : index;
178
+ }
179
+
180
+ function onReorderMove(event: PointerEvent): void {
181
+ if (!reorder) return;
182
+ event.preventDefault();
183
+ dropIndex.value = tileIndexAtPoint(event.clientX, event.clientY);
184
+ }
185
+
186
+ function teardownReorder(): void {
187
+ window.removeEventListener("pointermove", onReorderMove);
188
+ window.removeEventListener("pointerup", onReorderEnd);
189
+ window.removeEventListener("pointercancel", onReorderEnd);
190
+ isReordering.value = false;
191
+ }
192
+
193
+ function onReorderEnd(): void {
194
+ teardownReorder();
195
+ if (!reorder) return;
196
+ const { startIndex, handle, pointerId } = reorder;
197
+ const target = dropIndex.value;
198
+ reorder = null;
199
+ dragIndex.value = null;
200
+ dropIndex.value = null;
201
+ try {
202
+ handle.releasePointerCapture(pointerId);
203
+ } catch {
204
+ // Capture may already be gone (pointercancel) — ignore.
205
+ }
206
+ if (target === null || target === startIndex) return;
207
+ const next = [...tiles.value];
208
+ const [moved] = next.splice(startIndex, 1);
209
+ next.splice(target, 0, moved);
210
+ void setTiles(next);
211
+ }
212
+
213
+ function onReorderStart(index: number, event: PointerEvent): void {
214
+ const handle = event.currentTarget as HTMLElement;
215
+ try {
216
+ handle.setPointerCapture(event.pointerId);
217
+ } catch {
218
+ // Pointer capture unsupported — fall back to plain window listeners.
219
+ }
220
+ reorder = { startIndex: index, handle, pointerId: event.pointerId };
221
+ dragIndex.value = index;
222
+ isReordering.value = true;
223
+ window.addEventListener("pointermove", onReorderMove);
224
+ window.addEventListener("pointerup", onReorderEnd);
225
+ window.addEventListener("pointercancel", onReorderEnd);
226
+ }
227
+
228
+ // ── Drag-to-resize, per grid ROW (height is positional, not per tile) ──
229
+ const DEFAULT_HEIGHT = 320;
230
+ const MIN_HEIGHT = 160;
231
+ const MAX_HEIGHT = 900;
232
+
233
+ // Column count tracks the grid's responsive breakpoint (`sm:grid-cols-2`):
234
+ // 1 column below 640px, 2 at/above. Row grouping must follow it so a
235
+ // stacked tile in the single-column layout resizes on its own row instead
236
+ // of sharing a height with its desktop pair.
237
+ const SM_BREAKPOINT = "(min-width: 640px)";
238
+ const columns = ref(2);
239
+ let columnQuery: MediaQueryList | null = null;
240
+
241
+ function syncColumns(): void {
242
+ columns.value = columnQuery?.matches ? 2 : 1;
243
+ }
244
+
245
+ function rowOf(index: number): number {
246
+ return Math.floor(index / columns.value);
247
+ }
248
+
249
+ // Live height during a drag (smooth, not yet persisted), keyed by
250
+ // `"<columns>:<row>"` so an in-flight value can't bleed across layouts.
251
+ const liveRowHeights = ref<Record<string, number>>({});
252
+
253
+ function liveKey(cols: number, row: number): string {
254
+ return `${cols}:${row}`;
255
+ }
256
+
257
+ function rowHeight(index: number): number {
258
+ const row = rowOf(index);
259
+ const live = liveRowHeights.value[liveKey(columns.value, row)];
260
+ if (live !== undefined) return live;
261
+ const stored = rowHeights.value[String(columns.value)]?.[row];
262
+ return stored && stored > 0 ? stored : DEFAULT_HEIGHT;
263
+ }
264
+
265
+ // True while a drag is in progress — disables text selection so the drag
266
+ // doesn't accidentally select tile content.
267
+ const isResizing = ref(false);
268
+
269
+ interface ResizeState {
270
+ cols: number;
271
+ row: number;
272
+ startY: number;
273
+ startHeight: number;
274
+ handle: HTMLElement;
275
+ pointerId: number;
276
+ }
277
+ let resize: ResizeState | null = null;
278
+ // Latest pointer Y, applied once per frame (rAF-coalesced) so a flood of
279
+ // pointermove events can't thrash re-renders of the embedded views.
280
+ let pendingY: number | null = null;
281
+ let rafId = 0;
282
+
283
+ function applyResize(): void {
284
+ rafId = 0;
285
+ if (!resize || pendingY === null) return;
286
+ const next = Math.min(MAX_HEIGHT, Math.max(MIN_HEIGHT, resize.startHeight + (pendingY - resize.startY)));
287
+ liveRowHeights.value = { ...liveRowHeights.value, [liveKey(resize.cols, resize.row)]: next };
288
+ }
289
+
290
+ function onResizeMove(event: PointerEvent): void {
291
+ if (!resize) return;
292
+ event.preventDefault();
293
+ pendingY = event.clientY;
294
+ if (!rafId) rafId = requestAnimationFrame(applyResize);
295
+ }
296
+
297
+ function clearLiveRow(cols: number, row: number): void {
298
+ const key = liveKey(cols, row);
299
+ liveRowHeights.value = Object.fromEntries(Object.entries(liveRowHeights.value).filter(([entry]) => entry !== key));
300
+ }
301
+
302
+ // Persist the final height, then drop the live override so the stored
303
+ // value (now identical) takes over without a flash.
304
+ async function persistRowHeight(cols: number, row: number, height: number): Promise<void> {
305
+ await setRowHeight(cols, row, Math.round(height));
306
+ clearLiveRow(cols, row);
307
+ }
308
+
309
+ function teardownResize(): void {
310
+ window.removeEventListener("pointermove", onResizeMove);
311
+ window.removeEventListener("pointerup", onResizeEnd);
312
+ window.removeEventListener("pointercancel", onResizeEnd);
313
+ if (rafId) cancelAnimationFrame(rafId);
314
+ rafId = 0;
315
+ pendingY = null;
316
+ isResizing.value = false;
317
+ }
318
+
319
+ function onResizeEnd(): void {
320
+ teardownResize();
321
+ if (!resize) return;
322
+ const { cols, row, handle, pointerId } = resize;
323
+ resize = null;
324
+ try {
325
+ handle.releasePointerCapture(pointerId);
326
+ } catch {
327
+ // Capture may already be gone (pointercancel) — ignore.
328
+ }
329
+ const height = liveRowHeights.value[liveKey(cols, row)];
330
+ if (height !== undefined) void persistRowHeight(cols, row, height);
331
+ }
332
+
333
+ function onResizeStart(index: number, event: PointerEvent): void {
334
+ const handle = event.currentTarget as HTMLElement;
335
+ // Pointer capture routes every subsequent move/up to the handle even
336
+ // when the cursor passes over the embedded view (incl. iframes), which
337
+ // is what made the drag drop events and feel unstable.
338
+ try {
339
+ handle.setPointerCapture(event.pointerId);
340
+ } catch {
341
+ // Pointer capture unsupported — fall back to plain window listeners.
342
+ }
343
+ resize = { cols: columns.value, row: rowOf(index), startY: event.clientY, startHeight: rowHeight(index), handle, pointerId: event.pointerId };
344
+ isResizing.value = true;
345
+ window.addEventListener("pointermove", onResizeMove);
346
+ window.addEventListener("pointerup", onResizeEnd);
347
+ window.addEventListener("pointercancel", onResizeEnd);
348
+ }
349
+
350
+ onBeforeUnmount(() => {
351
+ teardownResize();
352
+ teardownReorder();
353
+ columnQuery?.removeEventListener("change", syncColumns);
354
+ });
355
+
356
+ // First load: fold favorites into the stored layout once both lists are
357
+ // authoritatively loaded (so an empty pre-load list never prunes tiles).
358
+ onMounted(async () => {
359
+ columnQuery = window.matchMedia(SM_BREAKPOINT);
360
+ syncColumns();
361
+ columnQuery.addEventListener("change", syncColumns);
362
+ await Promise.all([loadShortcuts(), loadDashboard()]);
363
+ await reconcile(favoriteSlugs.value);
364
+ });
365
+
366
+ // Later pin/unpin while on the page re-reconciles. Non-immediate so it
367
+ // can't fire on the empty pre-load list.
368
+ watch(favoriteSlugs, (slugs) => void reconcile(slugs));
369
+
370
+ // Fetch each tile's schema as the layout settles.
371
+ watch(tiles, (list) => list.forEach((tile) => void loadSchema(tile.slug)), { immediate: true });
372
+ </script>
@@ -127,13 +127,24 @@
127
127
  paths don't resolve under `srcdoc` (base URL is
128
128
  `about:srcdoc`), but that's the historical behavior
129
129
  for non-`artifacts/html/` HTML. -->
130
- <iframe
131
- v-else-if="isHtml && htmlPreviewUrl"
132
- :src="htmlPreviewUrl"
133
- class="w-full h-full border-0"
134
- sandbox="allow-scripts"
135
- :title="t('fileContentRenderer.htmlPreview')"
136
- />
130
+ <div v-else-if="isHtml && htmlPreviewUrl" class="relative w-full h-full">
131
+ <!-- Only `.html` is packable (server gates on `isHtmlPath`); a
132
+ `.htm` preview shows no button so the action can't 400. -->
133
+ <div v-if="canDownloadZip" class="absolute top-2 right-2 z-10 flex flex-col items-end gap-1">
134
+ <button
135
+ class="px-2 py-1 text-xs rounded border border-gray-300 bg-white/90 text-gray-600 hover:bg-gray-50 disabled:opacity-50 shadow-sm"
136
+ :title="t('fileContentRenderer.downloadZip')"
137
+ :disabled="packing"
138
+ data-testid="file-html-download"
139
+ @click="downloadHtmlZip"
140
+ >
141
+ <span class="material-icons text-sm align-middle">download</span>
142
+ {{ t("fileContentRenderer.download") }}
143
+ </button>
144
+ <span v-if="packFailed" class="text-xs text-red-600 bg-white/90 px-1 rounded" role="alert">{{ t("fileContentRenderer.downloadError") }}</span>
145
+ </div>
146
+ <iframe :src="htmlPreviewUrl" class="w-full h-full border-0" sandbox="allow-scripts" :title="t('fileContentRenderer.htmlPreview')" />
147
+ </div>
137
148
  <iframe
138
149
  v-else-if="isHtml"
139
150
  :srcdoc="sandboxedHtml"
@@ -247,6 +258,7 @@ import type { JsonToken, JsonlLine } from "../utils/format/jsonSyntax";
247
258
  import { formatScalarField, type MarkdownDocView } from "../composables/useMarkdownDoc";
248
259
  import { rewriteMarkdownImageRefs } from "../utils/image/rewriteMarkdownImageRefs";
249
260
  import { API_ROUTES } from "../config/apiRoutes";
261
+ import { useSharePack } from "../composables/useSharePack";
250
262
  import { descriptorForPath, jsonEditableByPolicy } from "../config/systemFileDescriptors";
251
263
  import { isMarpDocument } from "../utils/markdown/marpDetect";
252
264
  import { buildPdfFilename } from "../utils/files/filename";
@@ -289,6 +301,19 @@ const emit = defineEmits<{
289
301
 
290
302
  const systemDescriptor = computed(() => (props.selectedPath ? descriptorForPath(props.selectedPath) : null));
291
303
 
304
+ const { packing, packFailed, downloadZip, reset: resetSharePack } = useSharePack();
305
+ // `htmlPreviewUrl` also matches `.htm`, but the pack route only accepts
306
+ // canonical `.html`, so gate the button on `.html` to match server policy.
307
+ const canDownloadZip = computed(() => Boolean(props.isHtml && props.htmlPreviewUrl && props.selectedPath?.toLowerCase().endsWith(".html")));
308
+ function downloadHtmlZip() {
309
+ if (props.selectedPath) void downloadZip(props.selectedPath);
310
+ }
311
+ // Drop a stale error banner when the user navigates to another file.
312
+ watch(
313
+ () => props.selectedPath,
314
+ () => resetSharePack(),
315
+ );
316
+
292
317
  const marpMode = computed(() => Boolean(props.mdFrontmatter && isMarpDocument(props.mdFrontmatter.meta)));
293
318
 
294
319
  const marpBaseDir = computed(() => {
@@ -104,7 +104,7 @@ function createState(doc: string): EditorState {
104
104
  labelCompartment.of(EditorView.contentAttributes.of({ "aria-label": props.editorLabel })),
105
105
  EditorView.theme({
106
106
  "&": { fontSize: "12px" },
107
- ".cm-content": { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace" },
107
+ ".cm-content": { fontFamily: 'ui-monospace, SFMono-Regular, Menlo, Consolas, "MS Gothic", "BIZ UDGothic", monospace' },
108
108
  "&.cm-focused": { outline: "2px solid rgb(96 165 250)" },
109
109
  ".cm-scroller": { overflow: "auto" },
110
110
  }),