cli-jaw 2.0.6 → 2.0.13

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 (294) hide show
  1. package/README.ja.md +24 -11
  2. package/README.ko.md +24 -11
  3. package/README.md +95 -11
  4. package/README.zh-CN.md +24 -11
  5. package/dist/bin/cli-jaw.js +4 -1
  6. package/dist/bin/cli-jaw.js.map +1 -1
  7. package/dist/bin/commands/browser-web-ai.js +2 -2
  8. package/dist/bin/commands/browser-web-ai.js.map +1 -1
  9. package/dist/bin/commands/doctor.js +12 -2
  10. package/dist/bin/commands/doctor.js.map +1 -1
  11. package/dist/bin/commands/init.js +4 -2
  12. package/dist/bin/commands/init.js.map +1 -1
  13. package/dist/bin/commands/project.js +167 -0
  14. package/dist/bin/commands/project.js.map +1 -0
  15. package/dist/bin/commands/tui/types.js +20 -4
  16. package/dist/bin/commands/tui/types.js.map +1 -1
  17. package/dist/bin/postinstall.js +226 -24
  18. package/dist/bin/postinstall.js.map +1 -1
  19. package/dist/lib/mime-detect.js +5 -0
  20. package/dist/lib/mime-detect.js.map +1 -1
  21. package/dist/scripts/fresh-install-smoke.js +78 -6
  22. package/dist/scripts/fresh-install-smoke.js.map +1 -1
  23. package/dist/src/agent/agy-runtime.js +15 -0
  24. package/dist/src/agent/agy-runtime.js.map +1 -0
  25. package/dist/src/agent/args.js +44 -5
  26. package/dist/src/agent/args.js.map +1 -1
  27. package/dist/src/agent/cli-helpers.js +1 -1
  28. package/dist/src/agent/cli-helpers.js.map +1 -1
  29. package/dist/src/agent/lifecycle-handler.js +6 -3
  30. package/dist/src/agent/lifecycle-handler.js.map +1 -1
  31. package/dist/src/agent/session-persistence.js +1 -1
  32. package/dist/src/agent/session-persistence.js.map +1 -1
  33. package/dist/src/agent/smoke-detector.js +3 -0
  34. package/dist/src/agent/smoke-detector.js.map +1 -1
  35. package/dist/src/agent/spawn/resume.js +2 -0
  36. package/dist/src/agent/spawn/resume.js.map +1 -1
  37. package/dist/src/agent/spawn-env.js +6 -0
  38. package/dist/src/agent/spawn-env.js.map +1 -1
  39. package/dist/src/agent/spawn.js +120 -15
  40. package/dist/src/agent/spawn.js.map +1 -1
  41. package/dist/src/browser/web-ai/capability-observation-presets.js +1 -1
  42. package/dist/src/browser/web-ai/capability-observation-presets.js.map +1 -1
  43. package/dist/src/browser/web-ai/capability-registry.js +6 -5
  44. package/dist/src/browser/web-ai/capability-registry.js.map +1 -1
  45. package/dist/src/browser/web-ai/chatgpt-attachments.js +7 -0
  46. package/dist/src/browser/web-ai/chatgpt-attachments.js.map +1 -1
  47. package/dist/src/browser/web-ai/context-pack/builder.js +19 -4
  48. package/dist/src/browser/web-ai/context-pack/builder.js.map +1 -1
  49. package/dist/src/browser/web-ai/context-pack/zip-writer.js +116 -0
  50. package/dist/src/browser/web-ai/context-pack/zip-writer.js.map +1 -0
  51. package/dist/src/browser/web-ai/gemini-live.js +21 -3
  52. package/dist/src/browser/web-ai/gemini-live.js.map +1 -1
  53. package/dist/src/browser/web-ai/gemini-model.js +26 -8
  54. package/dist/src/browser/web-ai/gemini-model.js.map +1 -1
  55. package/dist/src/cli/commands.js +2 -0
  56. package/dist/src/cli/commands.js.map +1 -1
  57. package/dist/src/cli/handlers-project.js +55 -0
  58. package/dist/src/cli/handlers-project.js.map +1 -0
  59. package/dist/src/cli/readiness.js +6 -1
  60. package/dist/src/cli/readiness.js.map +1 -1
  61. package/dist/src/cli/registry.js +11 -0
  62. package/dist/src/cli/registry.js.map +1 -1
  63. package/dist/src/core/claude-install.js +10 -1
  64. package/dist/src/core/claude-install.js.map +1 -1
  65. package/dist/src/core/config.js +63 -7
  66. package/dist/src/core/config.js.map +1 -1
  67. package/dist/src/core/db.js +11 -4
  68. package/dist/src/core/db.js.map +1 -1
  69. package/dist/src/core/runtime-settings.js +4 -1
  70. package/dist/src/core/runtime-settings.js.map +1 -1
  71. package/dist/src/manager/metadata.js +7 -2
  72. package/dist/src/manager/metadata.js.map +1 -1
  73. package/dist/src/manager/notes/routes.js +7 -0
  74. package/dist/src/manager/notes/routes.js.map +1 -1
  75. package/dist/src/manager/notes/store.js +72 -0
  76. package/dist/src/manager/notes/store.js.map +1 -1
  77. package/dist/src/manager/registry.js +1 -1
  78. package/dist/src/manager/registry.js.map +1 -1
  79. package/dist/src/manager/scan.js +2 -0
  80. package/dist/src/manager/scan.js.map +1 -1
  81. package/dist/src/messaging/send.js +1 -1
  82. package/dist/src/messaging/send.js.map +1 -1
  83. package/dist/src/orchestrator/distribute.js +25 -4
  84. package/dist/src/orchestrator/distribute.js.map +1 -1
  85. package/dist/src/orchestrator/pipeline.js +21 -5
  86. package/dist/src/orchestrator/pipeline.js.map +1 -1
  87. package/dist/src/orchestrator/state-machine.js +7 -1
  88. package/dist/src/orchestrator/state-machine.js.map +1 -1
  89. package/dist/src/orchestrator/workspace-context.js +49 -14
  90. package/dist/src/orchestrator/workspace-context.js.map +1 -1
  91. package/dist/src/routes/messaging.js +1 -1
  92. package/dist/src/routes/messaging.js.map +1 -1
  93. package/dist/src/routes/orchestrate.js +11 -4
  94. package/dist/src/routes/orchestrate.js.map +1 -1
  95. package/dist/src/routes/settings.js +73 -8
  96. package/dist/src/routes/settings.js.map +1 -1
  97. package/dist/src/security/path-guards.js +35 -14
  98. package/dist/src/security/path-guards.js.map +1 -1
  99. package/dist/src/types/cli-engine.js +1 -0
  100. package/dist/src/types/cli-engine.js.map +1 -1
  101. package/package.json +19 -4
  102. package/public/css/chat.css +6 -2
  103. package/public/dist/assets/{AdvancedExport-CBvz4_IZ.js → AdvancedExport-CZEVPqHb.js} +1 -1
  104. package/public/dist/assets/{Agent-DlUqCMXJ.js → Agent-BBrJGd5m.js} +1 -1
  105. package/public/dist/assets/{Browser-66BpLQck.js → Browser-dBaU9G9D.js} +1 -1
  106. package/public/dist/assets/{ChannelsDiscord-BifT2Dum.js → ChannelsDiscord-CbmWms9-.js} +1 -1
  107. package/public/dist/assets/{ChannelsTelegram-CK6tYQ8k.js → ChannelsTelegram-BD4Eyztg.js} +1 -1
  108. package/public/dist/assets/{DashboardMeta-Cc03HT5R.js → DashboardMeta-LVAL7TtE.js} +1 -1
  109. package/public/dist/assets/{Display-arOjcMQZ.js → Display-CFm1UmEo.js} +1 -1
  110. package/public/dist/assets/{Employees-YbW-mI67.js → Employees-DcMChleU.js} +1 -1
  111. package/public/dist/assets/{Heartbeat-CW4eIhtJ.js → Heartbeat-BoobN35p.js} +1 -1
  112. package/public/dist/assets/{Mcp-ZQ6ARQb6.js → Mcp-9NU4YkKO.js} +1 -1
  113. package/public/dist/assets/{Memory-BAUWJMqc.js → Memory-uZT30fww.js} +1 -1
  114. package/public/dist/assets/{MilkdownWysiwygEditor-IAqFtXvM.js → MilkdownWysiwygEditor-Otw40DDF.js} +1 -1
  115. package/public/dist/assets/{ModelProvider-DUtmFh0J.js → ModelProvider-CsIOyoZP.js} +1 -1
  116. package/public/dist/assets/{Network-B_4E82zQ.js → Network-D7hGwnyG.js} +1 -1
  117. package/public/dist/assets/NotesGraphView-CJuk9lKn.js +1 -0
  118. package/public/dist/assets/Permissions-CcIBptg3.js +1 -0
  119. package/public/dist/assets/{Permissions-BkADXjQ4.js → Permissions-DwdU3wi8.js} +1 -1
  120. package/public/dist/assets/{Profile-BcnTCSYM.js → Profile-BNTKD_Ex.js} +1 -1
  121. package/public/dist/assets/{Prompts-CUs_RoRE.js → Prompts-D_mCfYiu.js} +1 -1
  122. package/public/dist/assets/{SpeechKeys-qxc-ROWN.js → SpeechKeys-BTXCM-Fs.js} +1 -1
  123. package/public/dist/assets/agent-meta-BC7mLqo6.js +1 -0
  124. package/public/dist/assets/{app-CGqTcIeF.js → app-C7MAcAMu.js} +5 -5
  125. package/public/dist/assets/app-CkFBKUf8.css +1 -0
  126. package/public/dist/assets/{architectureDiagram-3BPJPVTR-kYIVzmWe.js → architectureDiagram-3BPJPVTR-CJCPEsxo.js} +1 -1
  127. package/public/dist/assets/{blockDiagram-GPEHLZMM-B204CPhe.js → blockDiagram-GPEHLZMM-CO1bkn0E.js} +2 -2
  128. package/public/dist/assets/{c4Diagram-AAUBKEIU-DAv6TAjg.js → c4Diagram-AAUBKEIU-D2R36Xu6.js} +1 -1
  129. package/public/dist/assets/{chunk-3OPIFGDE-DOAvk9x8.js → chunk-3OPIFGDE-DixnEkZw.js} +1 -1
  130. package/public/dist/assets/chunk-55IACEB6-DhWWzzdj.js +1 -0
  131. package/public/dist/assets/{chunk-5ZQYHXKU-Bd-RFO5i.js → chunk-5ZQYHXKU-Bli4k6GI.js} +2 -2
  132. package/public/dist/assets/{chunk-727SXJPM-BrsRzj8z.js → chunk-727SXJPM-sp4W8bhf.js} +1 -1
  133. package/public/dist/assets/{chunk-AQP2D5EJ-Di6Gstk-.js → chunk-AQP2D5EJ-Co6ucdGq.js} +1 -1
  134. package/public/dist/assets/{chunk-KSCS5N6A-D9a4ZkOy.js → chunk-KSCS5N6A-Beq1s5eO.js} +2 -2
  135. package/public/dist/assets/{chunk-L5ZTLDWV-D208QbfC.js → chunk-L5ZTLDWV-DfUlBdXN.js} +1 -1
  136. package/public/dist/assets/{chunk-LZXEDZCA-CE2KFduj.js → chunk-LZXEDZCA-B_yP4UKw.js} +1 -1
  137. package/public/dist/assets/{chunk-ND2GUHAM-zZ7h1CPf.js → chunk-ND2GUHAM-3qbNb7I9.js} +1 -1
  138. package/public/dist/assets/{chunk-O5CBEL6O-BTuvXKFi.js → chunk-O5CBEL6O-D-jDqsN2.js} +1 -1
  139. package/public/dist/assets/{chunk-WU5MYG2G-C3fKlSvP.js → chunk-WU5MYG2G-DlBJtD7-.js} +1 -1
  140. package/public/dist/assets/classDiagram-4FO5ZUOK-DmPJvqRM.js +1 -0
  141. package/public/dist/assets/classDiagram-v2-Q7XG4LA2-ECmTUt7U.js +1 -0
  142. package/public/dist/assets/constants-BXWl7LTi.js +1 -0
  143. package/public/dist/assets/{cose-bilkent-S5V4N54A-D1zmarou.js → cose-bilkent-S5V4N54A-Bt9AAJCx.js} +1 -1
  144. package/public/dist/assets/{dagre-BM42HDAG-BVkBttzA.js → dagre-BM42HDAG-CXEA1wvZ.js} +1 -1
  145. package/public/dist/assets/{dagre-DnoxfNXr.js → dagre-Ve-IAfyz.js} +1 -1
  146. package/public/dist/assets/{diagram-2AECGRRQ-Bs32KPnT.js → diagram-2AECGRRQ-CCB0OKc2.js} +1 -1
  147. package/public/dist/assets/{diagram-5GNKFQAL-YDYgPOQj.js → diagram-5GNKFQAL-Cki9HABy.js} +1 -1
  148. package/public/dist/assets/{diagram-KO2AKTUF-1tIdFl26.js → diagram-KO2AKTUF-DNBDjE2o.js} +1 -1
  149. package/public/dist/assets/{diagram-LMA3HP47-B1y15kav.js → diagram-LMA3HP47-Cy23RDPa.js} +1 -1
  150. package/public/dist/assets/{diagram-OG6HWLK6-C25j1jGg.js → diagram-OG6HWLK6-CT_YF8cA.js} +1 -1
  151. package/public/dist/assets/dist-3R3GxjCf.js +13 -0
  152. package/public/dist/assets/{dist-D0PYXGt2.js → dist-BLd7xdyq.js} +1 -1
  153. package/public/dist/assets/{dist-fJAyK82z.js → dist-BaRrwDy3.js} +1 -1
  154. package/public/dist/assets/{dist-Rs_Khtjb.js → dist-Bbsp4OF7.js} +1 -1
  155. package/public/dist/assets/{dist-Cv8S3FhF.js → dist-BkBGlG70.js} +1 -1
  156. package/public/dist/assets/{dist-0v1UKkQ3.js → dist-BkJ_1mj9.js} +1 -1
  157. package/public/dist/assets/{dist-BQNQk-1M.js → dist-BxCRVW5D.js} +1 -1
  158. package/public/dist/assets/{dist-DDDFJ8jG.js → dist-C-v7Rm67.js} +1 -1
  159. package/public/dist/assets/dist-CNgDEAy8.js +1 -0
  160. package/public/dist/assets/dist-CO_W5rr2.js +1 -0
  161. package/public/dist/assets/{dist-vQskrHxt.js → dist-ChAUfTbO.js} +1 -1
  162. package/public/dist/assets/{dist-CjPin7Mr.js → dist-D-k-lzQT.js} +1 -1
  163. package/public/dist/assets/dist-D1hPukqY.js +1 -0
  164. package/public/dist/assets/{dist-C1VqWrYZ.js → dist-D5Qx-jQQ.js} +1 -1
  165. package/public/dist/assets/{dist-DTQW91xt.js → dist-D7VNzYBv.js} +1 -1
  166. package/public/dist/assets/{dist-DHQI31dn.js → dist-DAxnodoa.js} +1 -1
  167. package/public/dist/assets/{dist-CnIrGAPx.js → dist-DHSyODkR.js} +1 -1
  168. package/public/dist/assets/{dist-CE4s7vg82.js → dist-DaYTPjbb2.js} +1 -1
  169. package/public/dist/assets/{dist-jcptfm_T.js → dist-DlWi9tVO.js} +1 -1
  170. package/public/dist/assets/{dist-D66X8iAn.js → dist-DuuoJfRB.js} +1 -1
  171. package/public/dist/assets/{dist-DzT7frM2.js → dist-Dv36UUXz.js} +1 -1
  172. package/public/dist/assets/{dist-BVpa_e6W.js → dist-F4CVhitP.js} +1 -1
  173. package/public/dist/assets/{dist-Qs7HHNCC.js → dist-LHbJYy3N.js} +1 -1
  174. package/public/dist/assets/{dist-Ck6PnIlo.js → dist-VIlx7JdU.js} +1 -1
  175. package/public/dist/assets/{dist-D5GUqT-6.js → dist-cgDt2PmY.js} +1 -1
  176. package/public/dist/assets/dist-eW2LjRIK.js +1 -0
  177. package/public/dist/assets/{dist-Dm7RAaUo.js → dist-vGn3_sWA.js} +1 -1
  178. package/public/dist/assets/{employees-DRmX2RDd.js → employees-BmxY8Mw3.js} +1 -1
  179. package/public/dist/assets/{erDiagram-TEJ5UH35-CRvCbztR.js → erDiagram-TEJ5UH35-Cq7oHdXg.js} +1 -1
  180. package/public/dist/assets/{flowDiagram-I6XJVG4X-CzvVnib_.js → flowDiagram-I6XJVG4X-D8CdEnfM.js} +1 -1
  181. package/public/dist/assets/ganttDiagram-6RSMTGT7-B1rzKXi_.js +292 -0
  182. package/public/dist/assets/{gitGraphDiagram-PVQCEYII-CbKdM7nW.js → gitGraphDiagram-PVQCEYII-CqLh-joi.js} +1 -1
  183. package/public/dist/assets/{graphlib-BLOO0H5r.js → graphlib-W5bESv3F.js} +1 -1
  184. package/public/dist/assets/{infoDiagram-5YYISTIA-BrZmg1At.js → infoDiagram-5YYISTIA-C1zVxumj.js} +1 -1
  185. package/public/dist/assets/{ishikawaDiagram-YF4QCWOH-BDWXqJi5.js → ishikawaDiagram-YF4QCWOH-x-9J6xPM.js} +1 -1
  186. package/public/dist/assets/{journeyDiagram-JHISSGLW-CPFRd_M3.js → journeyDiagram-JHISSGLW-BDYzHrtn.js} +1 -1
  187. package/public/dist/assets/{kanban-definition-UN3LZRKU-BcQbBplS.js → kanban-definition-UN3LZRKU-BJ-06i6Z.js} +1 -1
  188. package/public/dist/assets/manager-B2NWzG7j.css +1 -0
  189. package/public/dist/assets/manager-C5e1IxEN.js +25 -0
  190. package/public/dist/assets/memory-BqqibBIE.js +1 -0
  191. package/public/dist/assets/{memory-BA-ki3gn.js → memory-CRL72HB-.js} +1 -1
  192. package/public/dist/assets/mermaid-loader-Dqb4ZUz6.js +1 -0
  193. package/public/dist/assets/{mermaid.core-ArqR5iFa.js → mermaid.core-D8VnfThU.js} +2 -2
  194. package/public/dist/assets/mermaid.core-DYHvk6Pd.js +1 -0
  195. package/public/dist/assets/{mindmap-definition-RKZ34NQL-C35cGkNU.js → mindmap-definition-RKZ34NQL-D8NDuIhn.js} +1 -1
  196. package/public/dist/assets/{pieDiagram-4H26LBE5-B8C_hoqJ.js → pieDiagram-4H26LBE5-CpBePil_.js} +3 -3
  197. package/public/dist/assets/{quadrantDiagram-W4KKPZXB-BEPrine5.js → quadrantDiagram-W4KKPZXB-C1WWpn88.js} +1 -1
  198. package/public/dist/assets/{render-C7_e0J_X.js → render-o_YEiCEO.js} +2 -2
  199. package/public/dist/assets/{requirementDiagram-4Y6WPE33-ClDDon9c.js → requirementDiagram-4Y6WPE33-C_D5VbNk.js} +1 -1
  200. package/public/dist/assets/{sankeyDiagram-5OEKKPKP-Bjs3OE-1.js → sankeyDiagram-5OEKKPKP--RH3Es8R.js} +3 -3
  201. package/public/dist/assets/{sequenceDiagram-3UESZ5HK-BM8IAaEe.js → sequenceDiagram-3UESZ5HK-CVI4AjhN.js} +1 -1
  202. package/public/dist/assets/settings-B0RlyAkI.js +1 -0
  203. package/public/dist/assets/settings-CCXRL5Ar.js +46 -0
  204. package/public/dist/assets/sidebar-DRJffqO1.js +14 -0
  205. package/public/dist/assets/skills-CFdzXbtK.js +1 -0
  206. package/public/dist/assets/{skills-Dq7fD8I3.js → skills-DbRVI-zf.js} +1 -1
  207. package/public/dist/assets/slash-commands-BUM6u-Ex.js +1 -0
  208. package/public/dist/assets/{slash-commands-DRsKtuGT.js → slash-commands-C2YBMaQV.js} +1 -1
  209. package/public/dist/assets/{stateDiagram-AJRCARHV-BvNABuXE.js → stateDiagram-AJRCARHV-4bx1rPZ3.js} +1 -1
  210. package/public/dist/assets/stateDiagram-v2-BHNVJYJU-CrfGPzHb.js +1 -0
  211. package/public/dist/assets/{timeline-definition-PNZ67QCA-CIvTvac3.js → timeline-definition-PNZ67QCA-DeQoGRE2.js} +1 -1
  212. package/public/dist/assets/{trace-drawer-DDFdU07_.js → trace-drawer-CIVBXyRH.js} +1 -1
  213. package/public/dist/assets/ui-C3ArwMhv.js +140 -0
  214. package/public/dist/assets/ui-CboUuc_E.js +1 -0
  215. package/public/dist/assets/vendor-utils-rVejrfMR.js +1 -0
  216. package/public/dist/assets/{vennDiagram-CIIHVFJN-C5cbS2KC.js → vennDiagram-CIIHVFJN-CejyJK0L.js} +1 -1
  217. package/public/dist/assets/{wardleyDiagram-YWT4CUSO-BsRBBGFP.js → wardleyDiagram-YWT4CUSO-DqXV0sa1.js} +1 -1
  218. package/public/dist/assets/wiki-link-suggestions-e5BzJAZH.js +23 -0
  219. package/public/dist/assets/{xychartDiagram-2RQKCTM6-B6rqui6F.js → xychartDiagram-2RQKCTM6-CWLoVl2b.js} +2 -2
  220. package/public/dist/index.html +20 -2
  221. package/public/dist/manager/index.html +2 -2
  222. package/public/index.html +18 -0
  223. package/public/js/constants.ts +12 -1
  224. package/public/js/features/settings-cli-status.ts +27 -4
  225. package/public/js/features/settings-core.ts +28 -15
  226. package/public/js/features/settings-types.ts +1 -0
  227. package/public/js/features/ui-status.ts +3 -0
  228. package/public/js/ws.ts +5 -1
  229. package/public/manager/src/SidebarRailRouter.tsx +5 -2
  230. package/public/manager/src/api.ts +13 -0
  231. package/public/manager/src/components/WorkbenchHeader.tsx +10 -0
  232. package/public/manager/src/manager-notes.css +228 -0
  233. package/public/manager/src/notes/NotesBacklinksPanel.tsx +91 -0
  234. package/public/manager/src/notes/NotesCommandPalette.tsx +212 -0
  235. package/public/manager/src/notes/NotesGraphView.tsx +145 -0
  236. package/public/manager/src/notes/NotesSidebar.tsx +183 -5
  237. package/public/manager/src/notes/NotesToolbar.tsx +9 -3
  238. package/public/manager/src/notes/NotesWorkspace.tsx +140 -4
  239. package/public/manager/src/notes/notes-api.ts +2 -0
  240. package/public/manager/src/notes/notes-command-registry.tsx +79 -0
  241. package/public/manager/src/notes/notes-quick-switcher.css +151 -0
  242. package/public/manager/src/notes/notes-shortcuts.ts +23 -0
  243. package/public/manager/src/notes/notes-types.ts +1 -1
  244. package/public/manager/src/notes/template-engine.ts +27 -0
  245. package/public/manager/src/settings/pages/components/agent/agent-meta.ts +7 -1
  246. package/public/manager/src/types.ts +2 -1
  247. package/scripts/audit-fresh-install-evidence.mjs +278 -0
  248. package/scripts/collect-fresh-install-evidence.sh +459 -0
  249. package/scripts/fresh-install-smoke.ts +85 -6
  250. package/scripts/install-officecli.ps1 +14 -1
  251. package/scripts/install-risk-gate.mjs +161 -0
  252. package/scripts/install-wsl.sh +78 -16
  253. package/scripts/install.sh +272 -72
  254. package/scripts/postinstall-guard.cjs +2 -2
  255. package/scripts/release-preview.sh +25 -3
  256. package/scripts/release.sh +37 -8
  257. package/scripts/require-release-evidence.mjs +236 -0
  258. package/scripts/verify-fresh-install.sh +120 -0
  259. package/scripts/verify-release-evidence.mjs +158 -0
  260. package/public/dist/assets/Permissions-DvUCB8wA.js +0 -1
  261. package/public/dist/assets/agent-meta-Du8y6mSM.js +0 -1
  262. package/public/dist/assets/app-Dpk6cE6C.css +0 -1
  263. package/public/dist/assets/chunk-55IACEB6-Dx2JkQLw.js +0 -1
  264. package/public/dist/assets/classDiagram-4FO5ZUOK-Cb-qL5u3.js +0 -1
  265. package/public/dist/assets/classDiagram-v2-Q7XG4LA2-BPhGdj15.js +0 -1
  266. package/public/dist/assets/constants-BxG09J6S.js +0 -1
  267. package/public/dist/assets/dist-BMi_buEb.js +0 -1
  268. package/public/dist/assets/dist-BkrbW_LS.js +0 -1
  269. package/public/dist/assets/dist-C3vh7d65.js +0 -13
  270. package/public/dist/assets/dist-D815C1Fv.js +0 -1
  271. package/public/dist/assets/dist-sOV2kBhQ.js +0 -1
  272. package/public/dist/assets/ganttDiagram-6RSMTGT7-BbUYP6iv.js +0 -292
  273. package/public/dist/assets/manager-B5OmizBL.css +0 -1
  274. package/public/dist/assets/manager-Cs3eMBcx.js +0 -25
  275. package/public/dist/assets/memory-DLQbyBA3.js +0 -1
  276. package/public/dist/assets/mermaid-loader-BVPsKrpr.js +0 -1
  277. package/public/dist/assets/mermaid.core-BAwu4U2Y.js +0 -1
  278. package/public/dist/assets/settings-DjyKdqev.js +0 -1
  279. package/public/dist/assets/settings-pUJzyeX2.js +0 -42
  280. package/public/dist/assets/sidebar-CO9Qjj6v.js +0 -14
  281. package/public/dist/assets/skills-CX3qJ77s.js +0 -1
  282. package/public/dist/assets/slash-commands-UbS8w9ij.js +0 -1
  283. package/public/dist/assets/stateDiagram-v2-BHNVJYJU-CCIo81qy.js +0 -1
  284. package/public/dist/assets/ui-CeBmBBZM.js +0 -140
  285. package/public/dist/assets/ui-TMXjvH0r.js +0 -1
  286. package/public/dist/assets/vendor-utils-BnxL60_-.js +0 -1
  287. package/public/dist/assets/wiki-link-suggestions-BHwNQXT_.js +0 -23
  288. /package/public/dist/assets/{HealthBadge-DEPFkcA0.js → HealthBadge-CfLTPTPW.js} +0 -0
  289. /package/public/dist/assets/{InlineWarn-DoJS7AgM.js → InlineWarn-C7ISyzDI.js} +0 -0
  290. /package/public/dist/assets/{fields-DUawAHWZ.js → fields-BwD_NGxe.js} +0 -0
  291. /package/public/dist/assets/{locale-BHMJIzyw.js → locale-DT1WRaeJ.js} +0 -0
  292. /package/public/dist/assets/{page-shell-CVAobxbP.js → page-shell-uNOxwdbr.js} +0 -0
  293. /package/public/dist/assets/{path-utils-r2bJIu28.js → path-utils-h2GAj3hH.js} +0 -0
  294. /package/public/dist/assets/{provider-icons-dxT69zGg.js → provider-icons-ClVd2suJ.js} +0 -0
@@ -0,0 +1,91 @@
1
+ import { useCallback, useState, type ReactNode } from 'react';
2
+ import type { NoteLinkRef } from '../types';
3
+
4
+ type NotesBacklinksPanelProps = {
5
+ backlinks: NoteLinkRef[];
6
+ onNavigate: (path: string) => void;
7
+ };
8
+
9
+ function backlinkTitle(path: string): string {
10
+ const name = path.split('/').pop() ?? path;
11
+ return name.endsWith('.md') ? name.slice(0, -3) : name;
12
+ }
13
+
14
+ function highlightWikilink(context: string): ReactNode[] {
15
+ const parts: ReactNode[] = [];
16
+ const regex = /\[\[([^\]]+)\]\]/g;
17
+ let lastIndex = 0;
18
+ let match: RegExpExecArray | null;
19
+ let key = 0;
20
+ while ((match = regex.exec(context)) !== null) {
21
+ if (match.index > lastIndex) {
22
+ parts.push(context.slice(lastIndex, match.index));
23
+ }
24
+ parts.push(
25
+ <mark key={key++} className="backlink-highlight">
26
+ {'[[' + match[1] + ']]'}
27
+ </mark>,
28
+ );
29
+ lastIndex = regex.lastIndex;
30
+ }
31
+ if (lastIndex < context.length) {
32
+ parts.push(context.slice(lastIndex));
33
+ }
34
+ return parts;
35
+ }
36
+
37
+ export function NotesBacklinksPanel(props: NotesBacklinksPanelProps) {
38
+ const [collapsed, setCollapsed] = useState(false);
39
+
40
+ const handleClick = useCallback((path: string) => {
41
+ props.onNavigate(path);
42
+ }, [props.onNavigate]);
43
+
44
+ const count = props.backlinks.length;
45
+
46
+ return (
47
+ <div className="notes-backlinks-panel">
48
+ <button
49
+ className="notes-backlinks-header"
50
+ onClick={() => setCollapsed(c => !c)}
51
+ >
52
+ <span className="notes-backlinks-chevron">
53
+ {collapsed ? '▶' : '▼'}
54
+ </span>
55
+ <span>Backlinks</span>
56
+ <span className="notes-backlinks-count">{count}</span>
57
+ </button>
58
+ {!collapsed && (
59
+ <div className="notes-backlinks-list">
60
+ {count === 0 && (
61
+ <div className="notes-backlinks-empty">
62
+ No backlinks found
63
+ </div>
64
+ )}
65
+ {props.backlinks.map((entry, i) => {
66
+ const context = entry.raw.length > 120
67
+ ? entry.raw.slice(0, 120) + '...'
68
+ : entry.raw;
69
+ return (
70
+ <button
71
+ key={`${entry.sourcePath}:${entry.line}:${i}`}
72
+ className="notes-backlink-entry"
73
+ onClick={() => handleClick(entry.sourcePath)}
74
+ >
75
+ <span className="notes-backlink-title">
76
+ {backlinkTitle(entry.sourcePath)}
77
+ </span>
78
+ <span className="notes-backlink-line">
79
+ L{entry.line}
80
+ </span>
81
+ <span className="notes-backlink-context">
82
+ {highlightWikilink(context)}
83
+ </span>
84
+ </button>
85
+ );
86
+ })}
87
+ </div>
88
+ )}
89
+ </div>
90
+ );
91
+ }
@@ -0,0 +1,212 @@
1
+ import { useEffect, useMemo, useRef, useState } from 'react';
2
+ import { useNoteCommands, type NoteCommand } from './notes-command-registry';
3
+ import { isCommandPaletteShortcut } from './notes-shortcuts';
4
+
5
+ type NotesCommandPaletteProps = {
6
+ active: boolean;
7
+ };
8
+
9
+ type NoteCommandResult = {
10
+ command: NoteCommand;
11
+ score: number;
12
+ };
13
+
14
+ function normalize(value: string): string {
15
+ return value.trim().toLowerCase();
16
+ }
17
+
18
+ function fuzzyScore(value: string, query: string): number | null {
19
+ const haystack = normalize(value);
20
+ const needle = normalize(query);
21
+ if (!needle) return 1;
22
+ if (haystack.startsWith(needle)) return 1000 - haystack.length;
23
+ const contains = haystack.indexOf(needle);
24
+ if (contains >= 0) return 700 - contains;
25
+
26
+ let lastIndex = -1;
27
+ let consecutive = 0;
28
+ let score = 0;
29
+ for (const char of needle) {
30
+ const index = haystack.indexOf(char, lastIndex + 1);
31
+ if (index < 0) return null;
32
+ consecutive = index === lastIndex + 1 ? consecutive + 1 : 0;
33
+ score += 4 + consecutive * 3 - Math.min(index, 40) * 0.1;
34
+ lastIndex = index;
35
+ }
36
+ return score;
37
+ }
38
+
39
+ function scoreCommand(command: NoteCommand, query: string): NoteCommandResult | null {
40
+ const candidates = [
41
+ { value: command.label, boost: 120 },
42
+ { value: command.section, boost: 40 },
43
+ ...(command.keywords || []).map(keyword => ({ value: keyword, boost: 80 })),
44
+ ];
45
+
46
+ let best: NoteCommandResult | null = null;
47
+ for (const candidate of candidates) {
48
+ const fieldScore = fuzzyScore(candidate.value, query);
49
+ if (fieldScore == null) continue;
50
+ const score = fieldScore + candidate.boost;
51
+ if (!best || score > best.score) best = { command, score };
52
+ }
53
+ return best;
54
+ }
55
+
56
+ export function filterNoteCommands(commands: NoteCommand[], query: string): NoteCommandResult[] {
57
+ return commands
58
+ .map(command => scoreCommand(command, query.trim()))
59
+ .filter((result): result is NoteCommandResult => Boolean(result))
60
+ .sort((a, b) => {
61
+ if (b.score !== a.score) return b.score - a.score;
62
+ const section = a.command.section.localeCompare(b.command.section);
63
+ if (section !== 0) return section;
64
+ return a.command.label.localeCompare(b.command.label);
65
+ });
66
+ }
67
+
68
+ function nextIndex(current: number, delta: number, count: number): number {
69
+ if (count <= 0) return 0;
70
+ return Math.max(0, Math.min(count - 1, current + delta));
71
+ }
72
+
73
+ export function NotesCommandPalette(props: NotesCommandPaletteProps) {
74
+ const commands = useNoteCommands();
75
+ const [open, setOpen] = useState(false);
76
+ const [query, setQuery] = useState('');
77
+ const [activeIndex, setActiveIndex] = useState(0);
78
+ const inputRef = useRef<HTMLInputElement | null>(null);
79
+
80
+ const results = useMemo(() => filterNoteCommands(commands, query), [commands, query]);
81
+ const activeId = results[activeIndex] ? `notes-command-palette-option-${activeIndex}` : undefined;
82
+
83
+ useEffect(() => {
84
+ if (!props.active) {
85
+ setOpen(false);
86
+ return;
87
+ }
88
+ function handleShortcut(event: KeyboardEvent): void {
89
+ if (!isCommandPaletteShortcut(event)) return;
90
+ event.preventDefault();
91
+ setOpen(current => !current);
92
+ }
93
+ window.addEventListener('keydown', handleShortcut);
94
+ return () => window.removeEventListener('keydown', handleShortcut);
95
+ }, [props.active]);
96
+
97
+ useEffect(() => {
98
+ if (!open) return;
99
+ setQuery('');
100
+ setActiveIndex(0);
101
+ requestAnimationFrame(() => inputRef.current?.focus());
102
+ }, [open]);
103
+
104
+ useEffect(() => {
105
+ if (activeIndex >= results.length) setActiveIndex(Math.max(0, results.length - 1));
106
+ }, [activeIndex, results.length]);
107
+
108
+ useEffect(() => {
109
+ const active = activeId ? document.getElementById(activeId) : null;
110
+ active?.scrollIntoView({ block: 'nearest' });
111
+ }, [activeId]);
112
+
113
+ function executeCommand(command: NoteCommand): void {
114
+ if (command.disabled) return;
115
+ setOpen(false);
116
+ try {
117
+ void Promise.resolve(command.run()).catch(error => {
118
+ console.warn('[notes-command-palette]', error);
119
+ });
120
+ } catch (error) {
121
+ console.warn('[notes-command-palette]', error);
122
+ }
123
+ }
124
+
125
+ function runActive(): void {
126
+ const command = results[activeIndex]?.command;
127
+ if (command) executeCommand(command);
128
+ }
129
+
130
+ function handleInputKey(event: React.KeyboardEvent<HTMLInputElement>): void {
131
+ if ((event.metaKey || event.ctrlKey) && event.key.toLowerCase() === 'p') {
132
+ event.preventDefault();
133
+ event.stopPropagation();
134
+ return;
135
+ }
136
+ if (event.key === 'ArrowDown') {
137
+ event.preventDefault();
138
+ setActiveIndex(index => nextIndex(index, 1, results.length));
139
+ } else if (event.key === 'ArrowUp') {
140
+ event.preventDefault();
141
+ setActiveIndex(index => nextIndex(index, -1, results.length));
142
+ } else if (event.key === 'Enter') {
143
+ event.preventDefault();
144
+ runActive();
145
+ } else if (event.key === 'Escape') {
146
+ event.preventDefault();
147
+ setOpen(false);
148
+ }
149
+ }
150
+
151
+ if (!open) return null;
152
+
153
+ return (
154
+ <div className="notes-command-palette-backdrop" role="presentation" onClick={() => setOpen(false)} data-notes-palette>
155
+ <section
156
+ className="notes-command-palette"
157
+ role="dialog"
158
+ aria-modal="true"
159
+ aria-label="Notes command palette"
160
+ onClick={event => event.stopPropagation()}
161
+ onKeyDown={event => event.stopPropagation()}
162
+ >
163
+ <input
164
+ ref={inputRef}
165
+ className="notes-command-palette-input"
166
+ type="search"
167
+ placeholder="Run command"
168
+ value={query}
169
+ onChange={event => { setQuery(event.currentTarget.value); setActiveIndex(0); }}
170
+ onKeyDown={handleInputKey}
171
+ aria-label="Run Notes command"
172
+ aria-controls="notes-command-palette-results"
173
+ aria-activedescendant={activeId}
174
+ />
175
+ <div
176
+ id="notes-command-palette-results"
177
+ className="notes-command-palette-list"
178
+ role="listbox"
179
+ aria-label="Matching commands"
180
+ >
181
+ {results.map((result, index) => {
182
+ const command = result.command;
183
+ const active = index === activeIndex;
184
+ return (
185
+ <button
186
+ id={`notes-command-palette-option-${index}`}
187
+ key={command.id}
188
+ type="button"
189
+ role="option"
190
+ aria-selected={active}
191
+ aria-disabled={command.disabled || undefined}
192
+ className={`notes-command-palette-item${active ? ' is-active' : ''}${command.disabled ? ' is-disabled' : ''}`}
193
+ onMouseEnter={() => setActiveIndex(index)}
194
+ onClick={() => executeCommand(command)}
195
+ >
196
+ <span className="notes-command-palette-title">{command.label}</span>
197
+ <span className="notes-command-palette-meta">{command.disabledReason || command.section}</span>
198
+ {command.shortcut && <kbd className="notes-command-palette-shortcut">{command.shortcut}</kbd>}
199
+ </button>
200
+ );
201
+ })}
202
+ {results.length === 0 && <p className="notes-command-palette-empty">No matching commands.</p>}
203
+ </div>
204
+ <footer className="notes-command-palette-footer">
205
+ <span><kbd>Up/Down</kbd> move</span>
206
+ <span><kbd>Enter</kbd> run</span>
207
+ <span><kbd>Esc</kbd> close</span>
208
+ </footer>
209
+ </section>
210
+ </div>
211
+ );
212
+ }
@@ -0,0 +1,145 @@
1
+ import { useEffect, useRef } from 'react';
2
+ import * as d3 from 'd3';
3
+ import type { NoteGraphNode, NoteGraphEdge } from '../types';
4
+
5
+ type NotesGraphViewProps = {
6
+ graph: { nodes: NoteGraphNode[]; edges: NoteGraphEdge[] };
7
+ selectedPath: string | null;
8
+ onNavigate: (path: string) => void;
9
+ };
10
+
11
+ type SimNode = NoteGraphNode & d3.SimulationNodeDatum;
12
+ type SimEdge = { source: SimNode; target: SimNode; raw: string };
13
+
14
+ function connectionCount(nodeId: string, edges: NoteGraphEdge[]): number {
15
+ return edges.filter(e => e.source === nodeId || e.target === nodeId).length;
16
+ }
17
+
18
+ function nodeColor(node: NoteGraphNode, selectedPath: string | null): string {
19
+ if (node.id === selectedPath) return 'var(--accent-primary, #8ab4ff)';
20
+ if (node.kind === 'missing') return 'var(--status-error, #f87171)';
21
+ if (node.kind === 'ambiguous') return 'var(--status-warning, #fbbf24)';
22
+ return 'var(--text-tertiary, #888)';
23
+ }
24
+
25
+ export function NotesGraphView(props: NotesGraphViewProps) {
26
+ const svgRef = useRef<SVGSVGElement | null>(null);
27
+ const navigateRef = useRef(props.onNavigate);
28
+ navigateRef.current = props.onNavigate;
29
+
30
+ const { graph, selectedPath } = props;
31
+
32
+ useEffect(() => {
33
+ if (!svgRef.current || graph.nodes.length === 0) return;
34
+ const svg = d3.select(svgRef.current);
35
+ svg.selectAll('*').remove();
36
+
37
+ const width = svgRef.current.clientWidth || 800;
38
+ const height = svgRef.current.clientHeight || 600;
39
+
40
+ const nodes: SimNode[] = graph.nodes.map(n => ({ ...n }));
41
+ const nodeById = new Map(nodes.map(n => [n.id, n]));
42
+ const edges: SimEdge[] = graph.edges
43
+ .filter(e => nodeById.has(e.source) && nodeById.has(e.target))
44
+ .map(e => ({
45
+ source: nodeById.get(e.source)!,
46
+ target: nodeById.get(e.target)!,
47
+ raw: e.raw,
48
+ }));
49
+
50
+ const maxConn = d3.max(nodes, n => connectionCount(n.id, graph.edges)) ?? 1;
51
+ const radiusScale = d3.scaleSqrt().domain([0, maxConn]).range([4, 16]);
52
+
53
+ const g = svg.append('g');
54
+
55
+ const zoom = d3.zoom<SVGSVGElement, unknown>()
56
+ .scaleExtent([0.2, 5])
57
+ .on('zoom', (event) => { g.attr('transform', event.transform); });
58
+ svg.call(zoom);
59
+
60
+ const simulation = d3.forceSimulation(nodes)
61
+ .force('link', d3.forceLink<SimNode, SimEdge>(edges).id((d) => d.id).distance(80))
62
+ .force('charge', d3.forceManyBody().strength(-120))
63
+ .force('center', d3.forceCenter(width / 2, height / 2))
64
+ .force('collision', d3.forceCollide<SimNode>().radius((d) =>
65
+ radiusScale(connectionCount(d.id, graph.edges)) + 4,
66
+ ));
67
+
68
+ const link = g.append('g')
69
+ .selectAll('line')
70
+ .data(edges)
71
+ .join('line')
72
+ .attr('stroke', 'var(--border-secondary, #444)')
73
+ .attr('stroke-width', 1)
74
+ .attr('stroke-opacity', 0.5);
75
+
76
+ const node = g.append('g')
77
+ .selectAll<SVGGElement, SimNode>('g')
78
+ .data(nodes)
79
+ .join('g')
80
+ .attr('class', 'graph-node')
81
+ .attr('cursor', 'pointer')
82
+ .on('click', (_event: MouseEvent, d: SimNode) => {
83
+ if (d.kind === 'note') navigateRef.current(d.id);
84
+ });
85
+
86
+ node.append('circle')
87
+ .attr('r', d => radiusScale(connectionCount(d.id, graph.edges)))
88
+ .attr('fill', d => nodeColor(d, selectedPath))
89
+ .attr('stroke', d => d.id === selectedPath ? 'var(--accent-hover, #aac8ff)' : 'none')
90
+ .attr('stroke-width', 2);
91
+
92
+ node.append('text')
93
+ .text(d => d.title)
94
+ .attr('dx', d => radiusScale(connectionCount(d.id, graph.edges)) + 4)
95
+ .attr('dy', '0.35em')
96
+ .attr('fill', 'var(--text-secondary, #aaa)')
97
+ .attr('font-size', '11px')
98
+ .attr('pointer-events', 'none');
99
+
100
+ const drag = d3.drag<SVGGElement, SimNode>()
101
+ .on('start', (event, d) => {
102
+ if (!event.active) simulation.alphaTarget(0.3).restart();
103
+ d.fx = d.x;
104
+ d.fy = d.y;
105
+ })
106
+ .on('drag', (event, d) => {
107
+ d.fx = event.x;
108
+ d.fy = event.y;
109
+ })
110
+ .on('end', (event, d) => {
111
+ if (!event.active) simulation.alphaTarget(0);
112
+ d.fx = null;
113
+ d.fy = null;
114
+ });
115
+ node.call(drag);
116
+
117
+ simulation.on('tick', () => {
118
+ link
119
+ .attr('x1', (d) => (d.source as SimNode).x ?? 0)
120
+ .attr('y1', (d) => (d.source as SimNode).y ?? 0)
121
+ .attr('x2', (d) => (d.target as SimNode).x ?? 0)
122
+ .attr('y2', (d) => (d.target as SimNode).y ?? 0);
123
+ node.attr('transform', (d) => `translate(${d.x ?? 0},${d.y ?? 0})`);
124
+ });
125
+
126
+ return () => { simulation.stop(); };
127
+ }, [graph, selectedPath]);
128
+
129
+ if (graph.nodes.length === 0) {
130
+ return (
131
+ <div className="notes-graph-view notes-graph-empty">
132
+ No notes to display
133
+ </div>
134
+ );
135
+ }
136
+
137
+ return (
138
+ <div className="notes-graph-view">
139
+ <svg ref={svgRef} className="notes-graph-svg" width="100%" height="100%" />
140
+ <div className="notes-graph-info">
141
+ {graph.nodes.filter(n => n.kind === 'note').length} notes · {graph.edges.length} links
142
+ </div>
143
+ </div>
144
+ );
145
+ }
@@ -1,12 +1,13 @@
1
- import { useEffect, useState, type CSSProperties } from 'react';
2
- import { createNoteFile, createNoteFolder, renameNotePath, trashNotePath } from './notes-api';
1
+ import { useEffect, useMemo, useState, type CSSProperties } from 'react';
2
+ import { createNoteFile, createNoteFolder, fetchNoteTemplate, fetchNoteTemplates, renameNotePath, trashNotePath } from './notes-api';
3
+ import { applyTemplate } from './template-engine';
3
4
  import { NewFolderIcon, NewNoteIcon, NotesFileTree, RefreshIcon } from './NotesFileTree';
4
5
  import { NotesSearchSidebar } from './NotesSearchSidebar';
6
+ import { useRegisterNoteCommands, type NoteCommand } from './notes-command-registry';
5
7
  import { publishInvalidation } from '../sync/invalidation-bus';
8
+ import { DashboardApiError, type NoteTemplate } from '../api';
6
9
  import type { NotesTreeEntry } from './notes-types';
7
-
8
10
  export type NotesSidebarMode = 'files' | 'search';
9
-
10
11
  type NotesSidebarProps = {
11
12
  tree: NotesTreeEntry[];
12
13
  loading: boolean;
@@ -94,19 +95,108 @@ function SearchIcon() {
94
95
  );
95
96
  }
96
97
 
98
+ function hasFile(entries: NotesTreeEntry[], name: string): boolean {
99
+ return entries.some(e => e.kind === 'file' && e.name === name);
100
+ }
101
+
97
102
  export function NotesSidebar(props: NotesSidebarProps) {
98
103
  const style = { '--notes-tree-width': `${props.treeWidth}px` } as CSSProperties;
99
104
  const [error, setError] = useState<string | null>(null);
100
105
  const [status, setStatus] = useState<string | null>(null);
101
106
  const [selectedFolderPath, setSelectedFolderPath] = useState<string | null>(null);
107
+ const [templates, setTemplates] = useState<NoteTemplate[]>([]);
108
+ const [templatesLoaded, setTemplatesLoaded] = useState<'pending' | 'ok' | 'error'>('pending');
109
+ const [pendingNewNote, setPendingNewNote] = useState<string | null>(null);
110
+
111
+ useEffect(() => {
112
+ void fetchNoteTemplates()
113
+ .then(t => { setTemplates(t); setTemplatesLoaded('ok'); })
114
+ .catch(() => { setTemplatesLoaded('error'); });
115
+ }, []);
116
+
117
+ async function openToday(): Promise<void> {
118
+ const now = new Date();
119
+ const pad = (n: number) => String(n).padStart(2, '0');
120
+ const todayName = `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())}.md`;
121
+ if (hasFile(props.tree, todayName)) {
122
+ props.onSelectedPathChange(todayName);
123
+ setError(null);
124
+ return;
125
+ }
126
+ if (templatesLoaded === 'pending') {
127
+ setError('Templates are still loading. Please try again.');
128
+ return;
129
+ }
130
+ if (templatesLoaded === 'error') {
131
+ setError('Template list failed to load. Cannot determine daily template.');
132
+ return;
133
+ }
134
+ try {
135
+ setStatus(null);
136
+ setError(null);
137
+ const dailyTemplate = templates.find(t => t.name === 'daily');
138
+ let content = '';
139
+ if (dailyTemplate) {
140
+ try {
141
+ const tmpl = await fetchNoteTemplate(dailyTemplate.name);
142
+ content = applyTemplate(tmpl.content, { title: todayName.replace(/\.md$/, '') });
143
+ } catch {
144
+ setError('Daily template failed to load. Note was not created.');
145
+ return;
146
+ }
147
+ }
148
+ const created = await createNoteFile(todayName, content);
149
+ props.onSelectedPathChange(created.path);
150
+ await props.onRefreshTree(created.path);
151
+ publishInvalidation({ topics: ['notes'], reason: 'note:created', source: 'ui', sourceId: 'notes-sidebar' });
152
+ } catch (err) {
153
+ if (err instanceof DashboardApiError && err.status === 409 && err.code === 'note_path_exists') {
154
+ setError(null);
155
+ props.onSelectedPathChange(todayName);
156
+ await props.onRefreshTree(todayName);
157
+ return;
158
+ }
159
+ setError((err as Error).message);
160
+ }
161
+ }
102
162
 
103
163
  async function createNote(): Promise<void> {
164
+ if (templatesLoaded === 'pending') {
165
+ setError('Templates are still loading. Please try again.');
166
+ return;
167
+ }
168
+ if (templatesLoaded === 'error') {
169
+ setError('Template list failed to load. Cannot offer template picker.');
170
+ return;
171
+ }
104
172
  const fallback = selectedFolderPath ? `${selectedFolderPath}/untitled.md` : 'untitled.md';
105
173
  const name = window.prompt('Note path', fallback);
106
174
  if (!name) return;
175
+ const notePath = name.endsWith('.md') ? name : `${name}.md`;
176
+ if (templates.length > 0) {
177
+ setPendingNewNote(notePath);
178
+ return;
179
+ }
180
+ await finishCreateNote(notePath, null);
181
+ }
182
+
183
+ async function finishCreateNote(notePath: string, templateName: string | null): Promise<void> {
184
+ setPendingNewNote(null);
185
+ let content = '';
186
+ if (templateName) {
187
+ try {
188
+ const tmpl = await fetchNoteTemplate(templateName);
189
+ const title = (notePath.endsWith('.md') ? notePath.slice(0, -3) : notePath).split('/').pop() ?? '';
190
+ content = applyTemplate(tmpl.content, { title });
191
+ } catch {
192
+ setError(`Template "${templateName}" failed to load. Note was not created.`);
193
+ return;
194
+ }
195
+ }
107
196
  try {
108
197
  setStatus(null);
109
- const created = await createNoteFile(name.endsWith('.md') ? name : `${name}.md`, '');
198
+ setError(null);
199
+ const created = await createNoteFile(notePath, content);
110
200
  props.onSelectedPathChange(created.path);
111
201
  await props.onRefreshTree(created.path);
112
202
  publishInvalidation({ topics: ['notes'], reason: 'note:created', source: 'ui', sourceId: 'notes-sidebar' });
@@ -251,6 +341,77 @@ export function NotesSidebar(props: NotesSidebarProps) {
251
341
  }
252
342
  }
253
343
 
344
+ const templateDisabledReason = templatesLoaded === 'pending'
345
+ ? 'Templates are still loading'
346
+ : templatesLoaded === 'error'
347
+ ? 'Templates failed to load'
348
+ : undefined;
349
+ const sidebarCommands = useMemo<NoteCommand[]>(() => [
350
+ {
351
+ id: 'sidebar:new-note',
352
+ section: 'Create',
353
+ label: 'New note',
354
+ shortcut: 'Alt+N',
355
+ keywords: ['create', 'file'],
356
+ disabled: templatesLoaded !== 'ok',
357
+ disabledReason: templateDisabledReason,
358
+ run: () => void createNote(),
359
+ },
360
+ {
361
+ id: 'sidebar:new-folder',
362
+ section: 'Create',
363
+ label: 'New folder',
364
+ keywords: ['directory'],
365
+ run: () => void createFolder(),
366
+ },
367
+ {
368
+ id: 'sidebar:today',
369
+ section: 'Create',
370
+ label: 'Open today',
371
+ keywords: ['daily', 'journal'],
372
+ disabled: templatesLoaded !== 'ok',
373
+ disabledReason: templateDisabledReason,
374
+ run: () => void openToday(),
375
+ },
376
+ {
377
+ id: 'sidebar:search',
378
+ section: 'Navigate',
379
+ label: 'Search notes',
380
+ shortcut: 'Cmd+Shift+F',
381
+ run: props.onOpenSearch,
382
+ },
383
+ {
384
+ id: 'sidebar:refresh',
385
+ section: 'File',
386
+ label: 'Refresh notes',
387
+ disabled: props.loading,
388
+ disabledReason: 'Refresh already running',
389
+ run: () => void props.onRefreshTree(),
390
+ },
391
+ {
392
+ id: 'sidebar:clear-tag-filter',
393
+ section: 'Navigate',
394
+ label: 'Clear tag filter',
395
+ disabled: !props.tagFilter,
396
+ disabledReason: 'No active tag filter',
397
+ run: props.onClearTagFilter,
398
+ },
399
+ ], [
400
+ props.loading,
401
+ props.onClearTagFilter,
402
+ props.onOpenSearch,
403
+ props.onRefreshTree,
404
+ props.onSelectedPathChange,
405
+ props.selectedPath,
406
+ props.tagFilter,
407
+ props.tree,
408
+ selectedFolderPath,
409
+ templateDisabledReason,
410
+ templates,
411
+ templatesLoaded,
412
+ ]);
413
+ useRegisterNoteCommands(sidebarCommands);
414
+
254
415
  return (
255
416
  <aside className="notes-tree" style={style}>
256
417
  {(props.error || error) && <section className="state error-state">{props.error || error}</section>}
@@ -295,6 +456,23 @@ export function NotesSidebar(props: NotesSidebarProps) {
295
456
  </button>
296
457
  </div>
297
458
  )}
459
+ {pendingNewNote && (
460
+ <div className="notes-template-picker">
461
+ <div className="notes-template-picker-label">Template for {pathName(pendingNewNote)}</div>
462
+ <div className="notes-template-picker-options">
463
+ <button type="button" className="notes-template-picker-option" onClick={() => void finishCreateNote(pendingNewNote, null)}>Blank</button>
464
+ {templates.map(t => (
465
+ <button key={t.name} type="button" className="notes-template-picker-option" onClick={() => void finishCreateNote(pendingNewNote, t.name)}>{t.name}</button>
466
+ ))}
467
+ </div>
468
+ <button type="button" className="notes-template-picker-cancel" onClick={() => setPendingNewNote(null)}>Cancel</button>
469
+ </div>
470
+ )}
471
+ <div className="notes-today-bar">
472
+ <button type="button" className="notes-today-button" onClick={() => void openToday()}>
473
+ Today
474
+ </button>
475
+ </div>
298
476
  <NotesFileTree
299
477
  entries={props.tree}
300
478
  selectedPath={props.selectedPath}