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
@@ -1,505 +0,0 @@
1
- <template>
2
- <form class="flex flex-col gap-3" data-testid="accounting-entry-form" @submit.prevent="onSubmit">
3
- <!-- Edit mode mounts inside the row's expanded detail panel,
4
- which already gives the user enough context (the row above
5
- shows date / kind / memo / lines). Hide the redundant
6
- "Edit journal entry" title there; the editBanner below
7
- still surfaces the void-and-replace consequence. -->
8
- <h3 v-if="!isEditing" class="text-base font-semibold">{{ t("pluginAccounting.entryForm.title") }}</h3>
9
- <div class="flex flex-wrap gap-3">
10
- <label class="text-xs text-gray-500 flex flex-col gap-1">
11
- {{ t("pluginAccounting.entryForm.dateLabel") }}
12
- <input v-model="date" type="date" required class="h-8 px-2 rounded border border-gray-300 text-sm bg-white" data-testid="accounting-entry-date" />
13
- </label>
14
- <label class="text-xs text-gray-500 flex flex-col gap-1 grow min-w-0">
15
- {{ t("pluginAccounting.entryForm.memoLabel") }}
16
- <input v-model="memo" type="text" class="h-8 px-2 rounded border border-gray-300 text-sm bg-white" data-testid="accounting-entry-memo" />
17
- </label>
18
- </div>
19
- <table class="w-full text-sm">
20
- <thead>
21
- <tr class="text-xs text-gray-500 border-b border-gray-200">
22
- <th class="text-left py-1 px-2">{{ t("pluginAccounting.entryForm.accountLabel") }}</th>
23
- <th class="text-right py-1 px-2 w-32">{{ t("pluginAccounting.entryForm.debitLabel") }}</th>
24
- <th class="text-right py-1 px-2 w-32">{{ t("pluginAccounting.entryForm.creditLabel") }}</th>
25
- <th v-if="anyTaxLine" class="text-left py-1 px-2 w-40">{{ t("pluginAccounting.entryForm.taxRegistrationIdLabel") }}</th>
26
- <th class="py-1 px-2"></th>
27
- </tr>
28
- </thead>
29
- <tbody>
30
- <tr v-for="(line, idx) in lines" :key="idx" class="border-b border-gray-100">
31
- <td class="py-1 px-2">
32
- <select
33
- v-model="line.accountCode"
34
- class="h-8 px-2 w-full rounded border border-gray-300 text-sm bg-white"
35
- :data-testid="`accounting-entry-line-account-${idx}`"
36
- >
37
- <option value="">{{ DASH }}</option>
38
- <option v-for="account in selectableAccounts" :key="account.code" :value="account.code">{{ formatAccountLabel(account) }}</option>
39
- </select>
40
- </td>
41
- <td class="py-1 px-2">
42
- <input
43
- v-model.number="line.debit"
44
- type="number"
45
- :step="step"
46
- min="0"
47
- class="h-8 px-2 w-full rounded border border-gray-300 text-sm text-right bg-white"
48
- :data-testid="`accounting-entry-line-debit-${idx}`"
49
- @input="onDebitInput(line)"
50
- />
51
- </td>
52
- <td class="py-1 px-2">
53
- <input
54
- v-model.number="line.credit"
55
- type="number"
56
- :step="step"
57
- min="0"
58
- class="h-8 px-2 w-full rounded border border-gray-300 text-sm text-right bg-white"
59
- :data-testid="`accounting-entry-line-credit-${idx}`"
60
- @input="onCreditInput(line)"
61
- />
62
- </td>
63
- <!-- Tax-registration ID column appears only when at least
64
- one line picks an input-tax account (14xx — see
65
- isTaxAccountCode). Within a column-visible row, the
66
- input itself only renders for the lines that actually
67
- pick a 14xx account; other lines render an empty cell
68
- so the row keeps its column alignment. -->
69
- <td v-if="anyTaxLine" class="py-1 px-2">
70
- <template v-if="isTaxLine(line)">
71
- <input
72
- v-model="line.taxRegistrationId"
73
- type="text"
74
- :maxlength="MAX_TAX_REGISTRATION_ID_LENGTH"
75
- :placeholder="t('pluginAccounting.entryForm.taxRegistrationIdPlaceholder')"
76
- :class="[
77
- 'h-8 px-2 w-full rounded border text-sm font-mono bg-white focus:outline-none',
78
- isTaxRegistrationIdInvalid(line)
79
- ? 'border-red-500 ring-1 ring-red-500'
80
- : isTaxRegistrationIdMissing(line)
81
- ? 'border-amber-500 ring-1 ring-amber-500'
82
- : 'border-gray-300 focus:ring-1 focus:ring-blue-500',
83
- ]"
84
- :data-testid="`accounting-entry-line-tax-registration-id-${idx}`"
85
- :aria-describedby="isTaxRegistrationIdMissing(line) ? `accounting-entry-line-tax-registration-id-warning-${idx}` : undefined"
86
- />
87
- <!-- Non-color cue for the amber border. Polite live
88
- region so screen readers are nudged when the
89
- user finishes typing an amount and the warning
90
- first appears, without interrupting other speech. -->
91
- <p
92
- v-if="isTaxRegistrationIdMissing(line)"
93
- :id="`accounting-entry-line-tax-registration-id-warning-${idx}`"
94
- class="text-xs text-amber-600 mt-1"
95
- role="status"
96
- aria-live="polite"
97
- :data-testid="`accounting-entry-line-tax-registration-id-warning-${idx}`"
98
- >
99
- {{ t("pluginAccounting.entryForm.taxRegistrationIdMissingWarning") }}
100
- </p>
101
- </template>
102
- </td>
103
- <td class="py-1 px-2 text-right">
104
- <button v-if="lines.length > 2" type="button" class="text-xs text-red-500 hover:underline" @click="lines.splice(idx, 1)">
105
- {{ t("pluginAccounting.entryForm.removeLine") }}
106
- </button>
107
- </td>
108
- </tr>
109
- </tbody>
110
- </table>
111
- <div class="flex items-center justify-between">
112
- <div class="flex items-center gap-2">
113
- <button
114
- type="button"
115
- class="h-8 px-2.5 flex items-center gap-1 rounded border border-gray-300 text-sm text-gray-600 hover:bg-gray-50"
116
- data-testid="accounting-entry-add-line"
117
- @click="addLine"
118
- >
119
- <span class="material-icons text-base">add</span>
120
- <span>{{ t("pluginAccounting.entryForm.addLine") }}</span>
121
- </button>
122
- <button
123
- type="button"
124
- class="h-8 px-2.5 flex items-center gap-1 rounded border border-gray-300 text-sm text-gray-600 hover:bg-gray-50"
125
- data-testid="accounting-entry-manage-accounts"
126
- @click="showAccountsModal = true"
127
- >
128
- <span class="material-icons text-base">tune</span>
129
- <span>{{ t("pluginAccounting.accounts.manageButton") }}</span>
130
- </button>
131
- </div>
132
- <span :class="balanced ? 'text-green-600' : 'text-red-500'" class="text-xs" data-testid="accounting-entry-balance">
133
- {{ balanced ? t("pluginAccounting.entryForm.balanced") : t("pluginAccounting.entryForm.imbalance", { amount: imbalanceText }) }}
134
- </span>
135
- </div>
136
- <p v-if="error" class="text-xs text-red-500" data-testid="accounting-entry-error">{{ error }}</p>
137
- <p v-if="successMessage" class="text-xs text-green-600" data-testid="accounting-entry-success">{{ successMessage }}</p>
138
- <div class="flex items-center justify-between gap-2">
139
- <!-- editBanner sits on the action row in edit mode (instead
140
- of as a separate paragraph above the form) so the panel
141
- is shorter and the user reads the void-and-replace
142
- consequence right next to the button that triggers it. -->
143
- <p v-if="isEditing" class="text-xs text-gray-500 flex-1 min-w-0" data-testid="accounting-entry-edit-banner">
144
- {{ t("pluginAccounting.entryForm.editBanner") }}
145
- </p>
146
- <span v-else></span>
147
- <div class="flex items-center gap-2">
148
- <button
149
- type="button"
150
- class="h-8 px-3 rounded border border-gray-300 text-sm text-gray-600 hover:bg-gray-50"
151
- :disabled="submitting"
152
- data-testid="accounting-entry-cancel-edit"
153
- @click="emit('cancel')"
154
- >
155
- {{ t("pluginAccounting.common.cancel") }}
156
- </button>
157
- <button
158
- type="submit"
159
- class="h-8 px-3 rounded bg-blue-600 hover:bg-blue-700 text-white text-sm disabled:opacity-50"
160
- :disabled="!balanced || submitting || editLocked"
161
- data-testid="accounting-entry-submit"
162
- >
163
- {{ submitButtonLabel }}
164
- </button>
165
- </div>
166
- </div>
167
- </form>
168
- <!-- Sibling of the parent <form> on purpose: the modal renders
169
- its own <form @submit.prevent> for the inline editor, and
170
- nesting <form>s is invalid HTML that breaks Enter-key submit
171
- routing in some browsers. Vue 3 multi-root templates let us
172
- keep the markup flat with no wrapper div. -->
173
- <AccountsModal v-if="showAccountsModal" :book-id="bookId" :accounts="accounts" @close="showAccountsModal = false" />
174
- </template>
175
-
176
- <script setup lang="ts">
177
- import { computed, ref, watch } from "vue";
178
- import { useI18n } from "vue-i18n";
179
- import { addEntries, voidEntry, type Account, type JournalEntry, type JournalLine } from "../api";
180
- import { formatAmount, inputStepFor } from "../currencies";
181
- import { localDateString } from "../dates";
182
- import { countryHasFeature, type SupportedCountryCode } from "../countries";
183
- import { isTaxAccountCode } from "./accountNumbering";
184
- import AccountsModal from "./AccountsModal.vue";
185
- import { errorMessage } from "../../../utils/errors";
186
-
187
- const { t } = useI18n();
188
-
189
- const props = defineProps<{ bookId: string; accounts: Account[]; currency: string; country?: SupportedCountryCode; entryToEdit?: JournalEntry | null }>();
190
- const emit = defineEmits<{ submitted: []; cancel: [] }>();
191
-
192
- const showAccountsModal = ref(false);
193
-
194
- const DASH = "—";
195
-
196
- function formatAccountLabel(account: Account): string {
197
- // Name first so type-to-search in the <select> matches the
198
- // human-meaningful word; the code goes in trailing parens.
199
- return `${account.name} (${account.code})`;
200
- }
201
-
202
- // Hide deactivated accounts from the entry dropdown — accounting
203
- // integrity requires keeping them in the chart of accounts (any
204
- // historical journal line still references the code), but new
205
- // entries should not be able to land on a soft-deleted account.
206
- const selectableAccounts = computed<Account[]>(() => props.accounts.filter((account) => account.active !== false));
207
- const selectableAccountCodes = computed<Set<string>>(() => new Set(selectableAccounts.value.map((account) => account.code)));
208
-
209
- interface FormLine {
210
- accountCode: string;
211
- debit: number | null;
212
- credit: number | null;
213
- taxRegistrationId: string;
214
- }
215
-
216
- // Mirrors server/accounting/journal.ts MAX_TAX_REGISTRATION_ID_LENGTH.
217
- // Duplicated rather than imported to keep the front-end bundle from
218
- // pulling in server modules (the existing client / server type alias
219
- // pattern in api.ts does the same — both sides own their copy of the
220
- // shape).
221
- const MAX_TAX_REGISTRATION_ID_LENGTH = 32;
222
-
223
- function blankLine(): FormLine {
224
- return { accountCode: "", debit: null, credit: null, taxRegistrationId: "" };
225
- }
226
-
227
- function isTaxRegistrationIdInvalid(line: FormLine): boolean {
228
- return line.taxRegistrationId.trim().length > MAX_TAX_REGISTRATION_ID_LENGTH;
229
- }
230
-
231
- function isTaxLine(line: FormLine): boolean {
232
- return line.accountCode !== "" && isTaxAccountCode(line.accountCode);
233
- }
234
-
235
- // Soft warning: a postable tax line in a jurisdiction the role
236
- // prompt requires a counterparty registration number for (JP, EU,
237
- // GB, IN, AU, NZ, CA — see COUNTRY_FEATURES.warnMissingTaxRegistrationId)
238
- // gets an amber border + helper text when the field is blank. The
239
- // form lets the user post anyway (some suppliers genuinely won't
240
- // have one), but the silent-strip in `toApiLines` no longer goes
241
- // unnoticed. `function` declarations hoist, so calling `isPostable`
242
- // here is fine even though it appears later in the file.
243
- function isTaxRegistrationIdMissing(line: FormLine): boolean {
244
- if (!isTaxLine(line)) return false;
245
- if (!isPostable(line)) return false;
246
- if (!countryHasFeature("warnMissingTaxRegistrationId", props.country)) return false;
247
- return line.taxRegistrationId.trim() === "";
248
- }
249
-
250
- const date = ref(localDateString());
251
- const memo = ref("");
252
- const lines = ref<FormLine[]>([blankLine(), blankLine()]);
253
- const submitting = ref(false);
254
- const error = ref<string | null>(null);
255
- const successMessage = ref<string | null>(null);
256
-
257
- const isEditing = computed<boolean>(() => Boolean(props.entryToEdit));
258
- const submitButtonLabel = computed<string>(() => {
259
- if (submitting.value) {
260
- return isEditing.value ? t("pluginAccounting.entryForm.updating") : t("pluginAccounting.entryForm.submitting");
261
- }
262
- return isEditing.value ? t("pluginAccounting.entryForm.update") : t("pluginAccounting.entryForm.submit");
263
- });
264
-
265
- // One-shot lock: once the user has clicked Update on an edit, the
266
- // submit button is dead until they Cancel (or land on a different
267
- // entry). Edit = void + addEntries as two sequential calls; if the
268
- // void succeeds and the add fails, a second Submit would try to
269
- // void an already-voided original. We don't add retry plumbing
270
- // for that — policy is "report the error, do not retry". The user
271
- // cancels out and re-enters manually.
272
- const editAttempted = ref(false);
273
- const editLocked = computed(() => isEditing.value && editAttempted.value);
274
-
275
- function addLine(): void {
276
- lines.value.push(blankLine());
277
- }
278
-
279
- // Toggling ensures a single line never has both sides set — the
280
- // server validates this too, but doing it on input prevents a
281
- // confusing UX where the running total goes negative as the user
282
- // types.
283
- function onDebitInput(line: FormLine): void {
284
- if (line.debit !== null && line.debit !== 0) line.credit = null;
285
- }
286
- function onCreditInput(line: FormLine): void {
287
- if (line.credit !== null && line.credit !== 0) line.debit = null;
288
- }
289
-
290
- // Imbalance is computed off lines that are *postable* (have an
291
- // accountCode + a positive amount). Without that filter,
292
- // `balanced` could be `true` even when `toApiLines()` would drop a
293
- // row, and the user would hit a confusing "needs ≥ 2 lines" error
294
- // from the server on submit.
295
- const imbalance = computed<number>(() => {
296
- let sum = 0;
297
- for (const line of lines.value) {
298
- if (!isPostable(line)) continue;
299
- if (isPositiveAmount(line.debit)) sum += line.debit;
300
- if (isPositiveAmount(line.credit)) sum -= line.credit;
301
- }
302
- return sum;
303
- });
304
- const hasAtLeastTwoPostableLines = computed(() => {
305
- let count = 0;
306
- for (const line of lines.value) {
307
- if (!isPostable(line)) continue;
308
- count += 1;
309
- if (count >= 2) return true;
310
- }
311
- return false;
312
- });
313
- // Show the tax-registration ID column only when at least one line
314
- // targets a 14xx (input-tax) account; otherwise the column is
315
- // wasted space for the typical entry that has no input-tax line.
316
- const anyTaxLine = computed(() => lines.value.some(isTaxLine));
317
- const hasTaxRegistrationIdError = computed(() => lines.value.some(isTaxRegistrationIdInvalid));
318
- const balanced = computed(() => Math.abs(imbalance.value) <= 0.005 && hasAtLeastTwoPostableLines.value && !hasTaxRegistrationIdError.value);
319
- const imbalanceText = computed(() => formatAmount(imbalance.value, props.currency));
320
- const step = computed(() => inputStepFor(props.currency));
321
-
322
- function isPositiveAmount(value: unknown): value is number {
323
- // Robust against the empty string `v-model.number` produces when
324
- // the user clears a previously-typed field — `"" ?? 0 === 0` is
325
- // false so a naive truthy check would let the empty input through
326
- // as a phantom zero amount.
327
- return typeof value === "number" && Number.isFinite(value) && value > 0;
328
- }
329
-
330
- function isPostable(line: FormLine): boolean {
331
- if (!line.accountCode) return false;
332
- // Defence-in-depth against a code that was selectable when the
333
- // user picked it but got deactivated mid-edit. Hiding the
334
- // option from the dropdown alone isn't enough — the form's
335
- // `accountCode` value is sticky, so a stale selection would
336
- // still be POSTed if the user just hits submit. Gating
337
- // postability here also flows through to `balanced` and
338
- // `hasAtLeastTwoPostableLines`, so the submit button disables
339
- // and the user gets immediate feedback.
340
- if (!selectableAccountCodes.value.has(line.accountCode)) return false;
341
- return isPositiveAmount(line.debit) || isPositiveAmount(line.credit);
342
- }
343
-
344
- function toApiLines(): JournalLine[] {
345
- const out: JournalLine[] = [];
346
- for (const line of lines.value) {
347
- if (!isPostable(line)) continue;
348
- const apiLine: JournalLine = { accountCode: line.accountCode };
349
- if (isPositiveAmount(line.debit)) apiLine.debit = line.debit;
350
- if (isPositiveAmount(line.credit)) apiLine.credit = line.credit;
351
- // Only persist taxRegistrationId on tax-related lines —
352
- // otherwise a value typed against an earlier account choice
353
- // would leak through after the user switched the line to a
354
- // non-tax account (the input field disappears but the form
355
- // state lingers).
356
- if (isTaxLine(line)) {
357
- const trimmedTaxId = line.taxRegistrationId.trim();
358
- if (trimmedTaxId !== "") apiLine.taxRegistrationId = trimmedTaxId;
359
- }
360
- out.push(apiLine);
361
- }
362
- return out;
363
- }
364
-
365
- async function onSubmit(): Promise<void> {
366
- if (submitting.value || !balanced.value || editLocked.value) return;
367
- submitting.value = true;
368
- error.value = null;
369
- successMessage.value = null;
370
- try {
371
- // Edit flow: void the original, then post the replacement.
372
- // Two sequential calls — not atomic, no retry. Marking
373
- // `editAttempted` *before* the void disables the submit button
374
- // for the rest of this edit session (the `editLocked` guard
375
- // and the button's :disabled both honour it), so a partial
376
- // failure can't trigger a second void on the already-voided
377
- // original. On any error: show the message, user must Cancel
378
- // and re-enter manually.
379
- const editingId = props.entryToEdit?.id;
380
- if (editingId) {
381
- editAttempted.value = true;
382
- const voidResult = await voidEntry({
383
- bookId: props.bookId,
384
- entryId: editingId,
385
- reason: t("pluginAccounting.entryForm.editVoidReason"),
386
- });
387
- if (!voidResult.ok) {
388
- error.value = voidResult.error;
389
- return;
390
- }
391
- }
392
- const result = await addEntries({
393
- bookId: props.bookId,
394
- entries: [
395
- {
396
- date: date.value,
397
- memo: memo.value.trim() || undefined,
398
- lines: toApiLines(),
399
- ...(editingId ? { replacesEntryId: editingId } : {}),
400
- },
401
- ],
402
- });
403
- if (!result.ok) {
404
- error.value = result.error;
405
- return;
406
- }
407
- successMessage.value = editingId ? t("pluginAccounting.entryForm.editSuccess") : t("pluginAccounting.entryForm.success");
408
- lines.value = [blankLine(), blankLine()];
409
- memo.value = "";
410
- emit("submitted");
411
- } catch (err) {
412
- // apiPost normally folds network / HTTP failures into
413
- // `result.ok = false`, so this branch should be rare. It's a
414
- // belt-and-braces guard against a runtime failure leaving the
415
- // submit button stuck — the user gets a visible error
416
- // instead of an unhandled rejection.
417
- error.value = errorMessage(err);
418
- } finally {
419
- submitting.value = false;
420
- }
421
- }
422
-
423
- // Reset the entire draft when bookId switches under us (rare but
424
- // possible via BookSwitcher while the form is open). Carrying the
425
- // previous book's lines and account codes into the new book is
426
- // the worst kind of silent failure — the new book might not even
427
- // have the same chart of accounts.
428
- watch(
429
- () => props.bookId,
430
- () => {
431
- lines.value = [blankLine(), blankLine()];
432
- memo.value = "";
433
- date.value = localDateString();
434
- error.value = null;
435
- successMessage.value = null;
436
- },
437
- );
438
-
439
- // Edit mode: when the parent hands us an entry to edit, pre-fill
440
- // every field so the user can tweak and resubmit. When it clears
441
- // the prop (after submit / cancel / book switch), wipe back to a
442
- // blank draft so the next "New entry" tab visit is fresh. Mapping
443
- // `entry.lines` (the wire shape with optional `debit` / `credit`)
444
- // onto `FormLine` (which uses nullable numbers + a string
445
- // taxRegistrationId) is straightforward — pad missing optionals
446
- // to null / "".
447
- watch(
448
- () => props.entryToEdit,
449
- (entry) => {
450
- error.value = null;
451
- successMessage.value = null;
452
- // Fresh edit (or exit from edit mode) → unlock the submit
453
- // button so the new edit session has a clean shot.
454
- editAttempted.value = false;
455
- if (!entry) {
456
- lines.value = [blankLine(), blankLine()];
457
- memo.value = "";
458
- date.value = localDateString();
459
- return;
460
- }
461
- date.value = entry.date;
462
- memo.value = entry.memo ?? "";
463
- lines.value = entry.lines.map((line) => ({
464
- accountCode: line.accountCode,
465
- debit: typeof line.debit === "number" ? line.debit : null,
466
- credit: typeof line.credit === "number" ? line.credit : null,
467
- taxRegistrationId: line.taxRegistrationId ?? "",
468
- }));
469
- if (lines.value.length < 2) {
470
- while (lines.value.length < 2) lines.value.push(blankLine());
471
- }
472
- },
473
- { immediate: true },
474
- );
475
-
476
- // If an account the user already picked gets deactivated mid-edit
477
- // (e.g. via the Manage Accounts modal in this form, or from
478
- // another tab via pubsub), clear the line's accountCode so the
479
- // <select> visibly resets to "—". Without this, the option is
480
- // gone but the form's bound value still holds the stale code,
481
- // which (a) leaves the user staring at a blank-looking select and
482
- // (b) used to slip through to submit before the isPostable guard
483
- // landed. Belt + suspenders.
484
- watch(selectableAccountCodes, (codes) => {
485
- for (const line of lines.value) {
486
- if (line.accountCode && !codes.has(line.accountCode)) line.accountCode = "";
487
- }
488
- });
489
- </script>
490
-
491
- <style scoped>
492
- /* Hide the WebKit / Firefox spin buttons on amount inputs. The
493
- step attribute still controls validation; this is purely UI.
494
- Accounting amount fields don't benefit from a spinner — users
495
- type the number and the up/down arrows just clutter the row. */
496
- input[type="number"]::-webkit-outer-spin-button,
497
- input[type="number"]::-webkit-inner-spin-button {
498
- -webkit-appearance: none;
499
- margin: 0;
500
- }
501
- input[type="number"] {
502
- -moz-appearance: textfield;
503
- appearance: textfield;
504
- }
505
- </style>