@nextclaw/ui 0.15.25 → 0.15.26

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 (288) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/dist/assets/{abnfDiagram-VRR7QNED-DvTjgVMf.js → abnfDiagram-VRR7QNED-BcFJp_1X.js} +1 -1
  3. package/dist/assets/{aiden0z-pptx-renderer.es-C1nfBAXR.js → aiden0z-pptx-renderer.es-Dp2ezlQn.js} +1 -1
  4. package/dist/assets/{api-FK7tikI1.js → api-CK3Fro7z.js} +6 -6
  5. package/dist/assets/appearance-settings-page-zqwWO8Dy.js +1 -0
  6. package/dist/assets/{arc-BvhODBXv.js → arc-CkgyPMyW.js} +1 -1
  7. package/dist/assets/architecture-TIHT7OUA-DO2x53FK.js +1 -0
  8. package/dist/assets/{architectureDiagram-ZJ3FMSHR-BKO9RgNS.js → architectureDiagram-ZJ3FMSHR-CMd4cIjI.js} +1 -1
  9. package/dist/assets/{blockDiagram-677ZJIJ3-DHpDXy7D.js → blockDiagram-677ZJIJ3-Bz3tbhet.js} +1 -1
  10. package/dist/assets/{c4Diagram-LMCZKHZV-BG_iQnDr.js → c4Diagram-LMCZKHZV-D9hFNAGv.js} +1 -1
  11. package/dist/assets/channel-nBvMaVyP.js +1 -0
  12. package/dist/assets/{channels-list-page-CocdcIko.js → channels-list-page-CVCZBlCL.js} +2 -2
  13. package/dist/assets/chat-page-D4wehRTz.js +1 -0
  14. package/dist/assets/chunk-2Q5K7J3B-CJgqFDS4.js +1 -0
  15. package/dist/assets/{chunk-32BRIVSS-DR9M0LLx.js → chunk-32BRIVSS-7NrjEITq.js} +1 -1
  16. package/dist/assets/{chunk-52WLFC77-698iC3BB.js → chunk-52WLFC77-viIcqj9q.js} +1 -1
  17. package/dist/assets/{chunk-5FCAYU7R-DikTj9Ey.js → chunk-5FCAYU7R-Vsu4J-VM.js} +1 -1
  18. package/dist/assets/{chunk-5HE753X5-ko0o7_q0.js → chunk-5HE753X5-C-w6CQZK.js} +1 -1
  19. package/dist/assets/{chunk-5JV3BV7I-CEHufhgo.js → chunk-5JV3BV7I-CuKgJQ4W.js} +1 -1
  20. package/dist/assets/{chunk-5TONJI2A-CGgno1cu.js → chunk-5TONJI2A-CYeiWZKb.js} +1 -1
  21. package/dist/assets/{chunk-5VM5RSS4-DkiB4cUQ.js → chunk-5VM5RSS4-DRCbfwIn.js} +1 -1
  22. package/dist/assets/{chunk-7BUUIJ7U-DzptcytQ.js → chunk-7BUUIJ7U-BvydOcm3.js} +1 -1
  23. package/dist/assets/{chunk-BIQX33UG-DMRSQJvT.js → chunk-BIQX33UG-RlppOXKo.js} +1 -1
  24. package/dist/assets/{chunk-C7G6YPKG-Bw1BwwuY.js → chunk-C7G6YPKG-DAizzYwr.js} +1 -1
  25. package/dist/assets/{chunk-CQNSW5MT-C0z6eHBk.js → chunk-CQNSW5MT-5eChJHca.js} +1 -1
  26. package/dist/assets/{chunk-CYSBUYHQ-ByPyAouO.js → chunk-CYSBUYHQ-YHPr53fv.js} +1 -1
  27. package/dist/assets/{chunk-EMLP6XTP-CJlAXWZg.js → chunk-EMLP6XTP-CK4D7Hz5.js} +1 -1
  28. package/dist/assets/{chunk-EX3LRPZG-Dqq4zKJu.js → chunk-EX3LRPZG-BmlDFIRW.js} +1 -1
  29. package/dist/assets/{chunk-FWX5IMBZ-B0SNoCmi.js → chunk-FWX5IMBZ-D0ksHZZ9.js} +2 -2
  30. package/dist/assets/{chunk-HOUHSVGY-DG_GJih_.js → chunk-HOUHSVGY-DPkq8FCm.js} +1 -1
  31. package/dist/assets/{chunk-ICXQ74PX-Drxk_6bE.js → chunk-ICXQ74PX-FAatv5fC.js} +1 -1
  32. package/dist/assets/{chunk-JG7HCLWE-DdLFPpnd.js → chunk-JG7HCLWE-DLkIi4F7.js} +1 -1
  33. package/dist/assets/{chunk-JWPE2WC7-B8M4G76F.js → chunk-JWPE2WC7-DawOjNE3.js} +1 -1
  34. package/dist/assets/{chunk-MOJQB5TN-XP6FJHY_.js → chunk-MOJQB5TN-Di52TorW.js} +1 -1
  35. package/dist/assets/{chunk-MOZMSUNE-C_6x18Qw.js → chunk-MOZMSUNE-PfyK9w1-.js} +1 -1
  36. package/dist/assets/{chunk-OGEWGWER-N25BzCmG.js → chunk-OGEWGWER-C3jdH3Ty.js} +1 -1
  37. package/dist/assets/{chunk-OSBZ3O6U-ASDEm7y0.js → chunk-OSBZ3O6U-CecHo-f0.js} +1 -1
  38. package/dist/assets/{chunk-PUDLZKDR-CTlvAcn7.js → chunk-PUDLZKDR-f3IJh3sN.js} +1 -1
  39. package/dist/assets/{chunk-Q4XR5HBZ-Da9BWfB6.js → chunk-Q4XR5HBZ-DQFeUPxZ.js} +1 -1
  40. package/dist/assets/{chunk-QBLGF6JB-Bi6RpTJO.js → chunk-QBLGF6JB-BhUeG0y2.js} +1 -1
  41. package/dist/assets/{chunk-R7FJI6CG-DkMsSPvK.js → chunk-R7FJI6CG-C3XpZ-2u.js} +1 -1
  42. package/dist/assets/{chunk-RYQCIY6F-DZp0eU-j.js → chunk-RYQCIY6F-CAqa5klQ.js} +1 -1
  43. package/dist/assets/{chunk-U6XO7XAA-vfinDSrj.js → chunk-U6XO7XAA-D_WXezeD.js} +1 -1
  44. package/dist/assets/{chunk-V7JOEXUC-C9Nksccs.js → chunk-V7JOEXUC-CZ70oOtp.js} +1 -1
  45. package/dist/assets/chunk-VAUOI2AC-BGvh3yIx.js +1 -0
  46. package/dist/assets/{chunk-VR4S4FIN-Xv6zfwXs.js → chunk-VR4S4FIN-B7AHEYYk.js} +1 -1
  47. package/dist/assets/{chunk-WYO6CB5R-mOJNzbWl.js → chunk-WYO6CB5R-Dn9To_OI.js} +2 -2
  48. package/dist/assets/chunk-XXDRQBXY-DaBxG5gs.js +1 -0
  49. package/dist/assets/{chunk-YOTPTUD7-1-aKSu4o.js → chunk-YOTPTUD7-BYEgRBk3.js} +1 -1
  50. package/dist/assets/{chunk-ZGVPDNZ5-OJ8veMpK.js → chunk-ZGVPDNZ5-C6YtZDU0.js} +1 -1
  51. package/dist/assets/{chunk-ZIRB5QZD-SR_Nq_PP.js → chunk-ZIRB5QZD-DMuYtnHF.js} +1 -1
  52. package/dist/assets/classDiagram-OUVF2IWQ-P6N03G2R.js +1 -0
  53. package/dist/assets/classDiagram-v2-EOCWNBFH-B1_uLvMv.js +1 -0
  54. package/dist/assets/config-split-page-CceNFvlY.js +1 -0
  55. package/dist/assets/confirm-dialog-CWeNYWZY.js +5 -0
  56. package/dist/assets/{cose-bilkent-JH36ORCC-NjVgz2Bw.js → cose-bilkent-JH36ORCC-Bc2Ex5Y3.js} +1 -1
  57. package/dist/assets/cynefin-VYW2F7L2-B-H8Qp-n.js +1 -0
  58. package/dist/assets/{cynefinDiagram-TSTJHNR4-B_17LDqD.js → cynefinDiagram-TSTJHNR4-DVysQoGz.js} +1 -1
  59. package/dist/assets/{dagre-CMe101gk.js → dagre-CMhQ1cAG.js} +1 -1
  60. package/dist/assets/{dagre-VKFMJZFB-qo8nLb3B.js → dagre-VKFMJZFB-RYyWpPJZ.js} +1 -1
  61. package/dist/assets/desktop-update-config-Dirc3bpt.js +1 -0
  62. package/dist/assets/{diagram-FQU43EPY-Ba3XIklC.js → diagram-FQU43EPY-BD6KONdF.js} +1 -1
  63. package/dist/assets/{diagram-G47NLZAW-CG45TcUx.js → diagram-G47NLZAW-CGB2ptRR.js} +1 -1
  64. package/dist/assets/{diagram-NH7WQ7WH-2F2HrwV-.js → diagram-NH7WQ7WH-DnkOI6cU.js} +1 -1
  65. package/dist/assets/{diagram-OA4YK3LP-CXfWH9NZ.js → diagram-OA4YK3LP-DF82RZ7P.js} +1 -1
  66. package/dist/assets/{diagram-WEI45ONY-CH_4yepc.js → diagram-WEI45ONY-Be2v7Ck9.js} +1 -1
  67. package/dist/assets/{dist-BH7W5yCi.js → dist-AYxkFx2B.js} +1 -1
  68. package/dist/assets/{dist-CaYdVVeO.js → dist-D9xtGgdL.js} +1 -1
  69. package/dist/assets/doc-browser-4Sud6dWw.js +1 -0
  70. package/dist/assets/doc-browser-D4x_yH5F.js +1 -0
  71. package/dist/assets/doc-browser-context-Cb7Z0j90.js +1 -0
  72. package/dist/assets/{docx-preview-CoM4JK7_.js → docx-preview-BqwAjrj3.js} +1 -1
  73. package/dist/assets/{ebnfDiagram-CCIWWBDH-BwiG4Ea1.js → ebnfDiagram-CCIWWBDH-CP98OYk8.js} +1 -1
  74. package/dist/assets/{erDiagram-Q63AITRT-BML3hpnQ.js → erDiagram-Q63AITRT-ZLlx8xfs.js} +1 -1
  75. package/dist/assets/eventmodeling-45OFAUF4-YVtShyFA.js +1 -0
  76. package/dist/assets/flowDiagram-23GEKE2U-BeKXabVO.js +1 -0
  77. package/dist/assets/form-actions-B2q4jgjE.js +1 -0
  78. package/dist/assets/{ganttDiagram-NO4QXBWP-80enfWbD.js → ganttDiagram-NO4QXBWP-DXCDjHR2.js} +1 -1
  79. package/dist/assets/gitGraph-TEB2WS4Q-jTSiBj-p.js +1 -0
  80. package/dist/assets/{gitGraphDiagram-IHSO6WYX-CVLqXr-b.js → gitGraphDiagram-IHSO6WYX-l2HWrTJp.js} +1 -1
  81. package/dist/assets/index-CdyTsSBu.css +1 -0
  82. package/dist/assets/index-Das_bw2h.js +107 -0
  83. package/dist/assets/info-DKCQHKI2-BIporNWb.js +1 -0
  84. package/dist/assets/{infoDiagram-FWYZ7A6U-Dyvs2t0i.js → infoDiagram-FWYZ7A6U-D-DjX-bn.js} +1 -1
  85. package/dist/assets/{ishikawaDiagram-FXEZZL3T-sSnxDKfh.js → ishikawaDiagram-FXEZZL3T-y_Xy128F.js} +1 -1
  86. package/dist/assets/{journeyDiagram-5HDEW3XC-D22V71Mf.js → journeyDiagram-5HDEW3XC-DJn5xYcQ.js} +1 -1
  87. package/dist/assets/{kanban-definition-HUTT4EX6-D3ds0p-g.js → kanban-definition-HUTT4EX6-DJVIOR21.js} +1 -1
  88. package/dist/assets/{line-CbyFs065.js → line-Cos7PIOj.js} +1 -1
  89. package/dist/assets/{linear-4DKtnZBS.js → linear-BtDLqtzF.js} +1 -1
  90. package/dist/assets/loader-circle-CpmBdaon.js +1 -0
  91. package/dist/assets/{map-CcZzEoz6.js → map-BKnQyvd0.js} +1 -1
  92. package/dist/assets/mcp-marketplace-page-CdOY3Sop.js +1 -0
  93. package/dist/assets/mcp-marketplace-page-DvjrfqEt.js +9 -0
  94. package/dist/assets/{mermaid-parser.core-DPmMOjXD.js → mermaid-parser.core-XUNZhr2W.js} +2 -2
  95. package/dist/assets/{mermaid.core-BSf-ddRa.js → mermaid.core-ySrvolIe.js} +3 -3
  96. package/dist/assets/{mindmap-definition-LN4V7U3C-BUTwWPyx.js → mindmap-definition-LN4V7U3C-BypL9tei.js} +1 -1
  97. package/dist/assets/model-config-page-DfOLyQqR.js +1 -0
  98. package/dist/assets/{navigation-link-CVy0_kVl.js → navigation-link-MiiyoIjH.js} +2 -2
  99. package/dist/assets/{ordinal-BgLYtOZE.js → ordinal-CxxVWxYU.js} +1 -1
  100. package/dist/assets/packet-7NZHBO7P-CuFMADV0.js +1 -0
  101. package/dist/assets/{pegDiagram-2B236MQR-BEu_ZBjc.js → pegDiagram-2B236MQR-D-U2KBGv.js} +1 -1
  102. package/dist/assets/pie-RZYD4A2V-XUeIXhM9.js +1 -0
  103. package/dist/assets/{pieDiagram-ENE6RG2P-CAwAwuBd.js → pieDiagram-ENE6RG2P-CBi3GusJ.js} +1 -1
  104. package/dist/assets/plus-C8FkjpSZ.js +1 -0
  105. package/dist/assets/provider-scoped-model-input-DXzjr5l0.js +1 -0
  106. package/dist/assets/providers-config-page-CaO8WfZH.js +1 -0
  107. package/dist/assets/{quadrantDiagram-ABIIQ3AL-CwE6F-_2.js → quadrantDiagram-ABIIQ3AL-H0ZuvnZL.js} +1 -1
  108. package/dist/assets/radar-I7S5WNFK-D-NNGh1k.js +1 -0
  109. package/dist/assets/railroad-3IZDKUUU-C67_uDaM.js +1 -0
  110. package/dist/assets/railroad-abnf-AHOZXSZD-BZ8Yflnd.js +1 -0
  111. package/dist/assets/railroad-ebnf-EBAXGLYW-B7zO7RoK.js +1 -0
  112. package/dist/assets/railroad-peg-LSFZ7HO6-R63PTgLz.js +1 -0
  113. package/dist/assets/{railroadDiagram-RFXS5EU6-xgLekU3B.js → railroadDiagram-RFXS5EU6-D7Y2EqSd.js} +1 -1
  114. package/dist/assets/react-C8XFFvuz.js +8 -0
  115. package/dist/assets/{refresh-cw-j2bl79Ea.js → refresh-cw-a2VVeeAF.js} +1 -1
  116. package/dist/assets/remote-BJHXa4mB.js +1 -0
  117. package/dist/assets/{requirementDiagram-TGXJPOKE-CyVUzEX5.js → requirementDiagram-TGXJPOKE-BiXNX1NN.js} +1 -1
  118. package/dist/assets/rotate-cw-DJ7UF3F2.js +1 -0
  119. package/dist/assets/runtime-config-page-BuA4A02z.js +1 -0
  120. package/dist/assets/{sankeyDiagram-HTMAVEWB-JK6bFmYB.js → sankeyDiagram-HTMAVEWB-qzDeLztW.js} +1 -1
  121. package/dist/assets/{save-BKNrtK81.js → save-CGm23Yww.js} +1 -1
  122. package/dist/assets/{search-CkOm65W_.js → search-CH2t--sH.js} +1 -1
  123. package/dist/assets/search-config-page-BAmYqoAz.js +1 -0
  124. package/dist/assets/{secrets-config-page-CJjtdFAl.js → secrets-config-page-BlPOuql_.js} +2 -2
  125. package/dist/assets/security-config-CbDiY2xr.js +1 -0
  126. package/dist/assets/{select-Ea7ffKCE.js → select-DnxxDpcV.js} +2 -2
  127. package/dist/assets/{sequenceDiagram-DBY2YBRQ-BR5IokXV.js → sequenceDiagram-DBY2YBRQ-BtRbPQtL.js} +1 -1
  128. package/dist/assets/{settings-2-CtyN3m_V.js → settings-2-agnMYc8a.js} +1 -1
  129. package/dist/assets/{sizeCapture-X5ZJPWSS-SxG_YnMA.js → sizeCapture-X5ZJPWSS-CMYvM3SL.js} +1 -1
  130. package/dist/assets/skeleton-XpqeWL62.js +1 -0
  131. package/dist/assets/{src-vgz8E_-O.js → src-Bw-mzbQb.js} +1 -1
  132. package/dist/assets/{stateDiagram-2N3HPSRC-DD4OoYhn.js → stateDiagram-2N3HPSRC-BJ-mFeOz.js} +1 -1
  133. package/dist/assets/stateDiagram-v2-6OUMAXLB-BE-HqxOK.js +1 -0
  134. package/dist/assets/{swimlanes-5IMT3BWC-BWkFeyCT.js → swimlanes-5IMT3BWC-BsucVOO2.js} +2 -2
  135. package/dist/assets/swimlanesDiagram-G3AALYLV-Dr1H9LJH.js +8 -0
  136. package/dist/assets/{tag-chip-C9l4xsKp.js → tag-chip-CPOc8b2N.js} +1 -1
  137. package/dist/assets/{timeline-definition-FHXFAJF6-VTRPy6KA.js → timeline-definition-FHXFAJF6-Bicewlzo.js} +1 -1
  138. package/dist/assets/treeView-QDETBFTQ-npJ5dbqS.js +1 -0
  139. package/dist/assets/treemap-6X3UGDF4-pYrBx6oa.js +1 -0
  140. package/dist/assets/{vennDiagram-L72KCM5P-Bvk42Pek.js → vennDiagram-L72KCM5P-BxOOnO9x.js} +1 -1
  141. package/dist/assets/wardley-OPB4EBWU-CmSRKLR8.js +1 -0
  142. package/dist/assets/{wardleyDiagram-EHGQE667-DOFjx2s3.js → wardleyDiagram-EHGQE667-Cl7NltMv.js} +1 -1
  143. package/dist/assets/x-odTHAtLZ.js +1 -0
  144. package/dist/assets/{xychartDiagram-FW5EYKEG-DY5yRTUc.js → xychartDiagram-FW5EYKEG-DiMIxQ7N.js} +1 -1
  145. package/dist/index.html +21 -22
  146. package/package.json +6 -6
  147. package/src/features/apps/components/__tests__/app-packages-panel.test.tsx +234 -0
  148. package/src/features/apps/components/app-marketplace-dialog.tsx +262 -0
  149. package/src/features/apps/components/app-package-card.tsx +268 -0
  150. package/src/features/apps/components/app-packages-panel.tsx +301 -0
  151. package/src/features/apps/components/apps-panel.tsx +11 -4
  152. package/src/features/apps/hooks/use-app-marketplace.ts +95 -0
  153. package/src/features/apps/hooks/use-app-packages.ts +59 -0
  154. package/src/features/chat/components/chat-sidebar-list-mode-switch.tsx +27 -4
  155. package/src/features/chat/components/layout/__tests__/chat-sidebar-session-area.test.tsx +36 -5
  156. package/src/features/chat/components/layout/chat-sidebar-desktop-layout.tsx +12 -13
  157. package/src/features/chat/features/conversation/components/__tests__/session-conversation-area.test.tsx +49 -1
  158. package/src/features/chat/features/conversation/components/__tests__/session-conversation-input.streaming.test.tsx +4 -4
  159. package/src/features/chat/features/conversation/components/session-conversation-area.tsx +25 -4
  160. package/src/features/chat/features/conversation/components/session-conversation-input.tsx +3 -0
  161. package/src/features/chat/features/conversation/hooks/use-system-object-reference-select.ts +27 -0
  162. package/src/features/chat/features/conversation/utils/session-message-composer.utils.ts +5 -0
  163. package/src/features/chat/features/input/hooks/__tests__/use-chat-input-surface-state.test.tsx +102 -0
  164. package/src/features/chat/features/input/hooks/use-chat-input-surface-state.ts +71 -120
  165. package/src/features/chat/features/input/input-surface-plugins/__tests__/context-reference-plugin.test.ts +265 -18
  166. package/src/features/chat/features/input/input-surface-plugins/chat-input-product-plugin-adapters.types.ts +7 -2
  167. package/src/features/chat/features/input/input-surface-plugins/chat-input-product-plugin-adapters.utils.ts +124 -0
  168. package/src/features/chat/features/input/input-surface-plugins/context-reference-items.utils.ts +99 -15
  169. package/src/features/chat/features/input/input-surface-plugins/context-reference-plugin.utils.ts +143 -139
  170. package/src/features/chat/features/input/input-surface-plugins/panel-app-input-surface-items.utils.ts +116 -0
  171. package/src/features/chat/features/input/input-surface-plugins/slash-command-plugin.utils.ts +1 -1
  172. package/src/features/chat/features/input/utils/__tests__/chat-inline-token.utils.test.ts +53 -1
  173. package/src/features/chat/features/input/utils/chat-composer-token-protocol.utils.ts +5 -0
  174. package/src/features/chat/features/input/utils/chat-inline-token-composer.utils.ts +134 -0
  175. package/src/features/chat/features/input/utils/chat-inline-token.utils.ts +30 -101
  176. package/src/features/chat/features/input/utils/chat-system-object-reference.utils.test.ts +44 -0
  177. package/src/features/chat/features/input/utils/chat-system-object-reference.utils.ts +46 -0
  178. package/src/features/chat/features/workspace/components/__tests__/chat-session-workspace-panel-content.test.tsx +82 -2
  179. package/src/features/chat/features/workspace/components/__tests__/chat-session-workspace-text-selection.test.tsx +12 -8
  180. package/src/features/chat/features/workspace/components/chat-session-workspace-directory-browser.tsx +24 -12
  181. package/src/features/chat/features/workspace/components/chat-session-workspace-panel-content.tsx +9 -2
  182. package/src/features/chat/features/workspace/components/project-files/workspace-directory-tree.tsx +52 -4
  183. package/src/features/chat/features/workspace/hooks/use-workspace-explorer-layout.ts +8 -11
  184. package/src/features/chat/features/workspace/hooks/use-workspace-file-actions.ts +68 -46
  185. package/src/features/chat/index.ts +1 -1
  186. package/src/features/chat/managers/chat-draft-intent.manager.ts +18 -2
  187. package/src/features/chat/stores/__tests__/chat-thread.store.test.ts +6 -0
  188. package/src/features/chat/stores/chat-thread.store.ts +6 -0
  189. package/src/features/inbox/components/inbox-reader-dialog.test.tsx +38 -5
  190. package/src/features/inbox/components/inbox-reader-dialog.tsx +5 -3
  191. package/src/features/inbox/managers/inbox.manager.test.ts +28 -3
  192. package/src/features/inbox/managers/inbox.manager.ts +14 -4
  193. package/src/features/inbox/pages/inbox-page.test.ts +121 -2
  194. package/src/features/inbox/pages/inbox-page.tsx +5 -3
  195. package/src/features/marketplace/components/__tests__/marketplace-page.test.tsx +85 -1
  196. package/src/features/marketplace/hooks/__tests__/use-marketplace.test.tsx +44 -1
  197. package/src/features/marketplace/hooks/use-marketplace-item-actions.ts +23 -2
  198. package/src/features/marketplace/hooks/use-marketplace.ts +10 -0
  199. package/src/features/marketplace/index.ts +4 -0
  200. package/src/features/panel-apps/index.ts +5 -1
  201. package/src/features/panel-apps/utils/__tests__/panel-app-doc-browser.utils.test.ts +13 -6
  202. package/src/features/panel-apps/utils/panel-app-doc-browser.utils.tsx +6 -3
  203. package/src/features/right-panel-resources/index.ts +1 -0
  204. package/src/features/right-panel-resources/utils/right-panel-resource-uri.utils.ts +7 -4
  205. package/src/features/system-status/components/__tests__/desktop-update-config.test.tsx +20 -1
  206. package/src/features/system-status/components/desktop-update-config.tsx +14 -13
  207. package/src/features/system-status/managers/__tests__/runtime-update.manager.test.ts +99 -0
  208. package/src/features/system-status/managers/runtime-update.manager.ts +22 -0
  209. package/src/features/system-status/stores/runtime-update.store.ts +1 -0
  210. package/src/shared/components/ui/actions/__tests__/icon-action-button.test.tsx +15 -0
  211. package/src/shared/components/ui/actions/icon-action-button.tsx +4 -2
  212. package/src/shared/hooks/use-system-object-references.ts +22 -0
  213. package/src/shared/lib/api/types.ts +10 -0
  214. package/src/shared/lib/i18n/locales/en-US/chat.json +16 -9
  215. package/src/shared/lib/i18n/locales/en-US/desktop-update.json +7 -4
  216. package/src/shared/lib/i18n/locales/en-US/doc-browser.json +40 -0
  217. package/src/shared/lib/i18n/locales/en-US/marketplace.json +3 -0
  218. package/src/shared/lib/i18n/locales/zh-CN/chat.json +18 -11
  219. package/src/shared/lib/i18n/locales/zh-CN/desktop-update.json +7 -4
  220. package/src/shared/lib/i18n/locales/zh-CN/doc-browser.json +40 -0
  221. package/src/shared/lib/i18n/locales/zh-CN/marketplace.json +3 -0
  222. package/dist/assets/appearance-settings-page-BYbvl2wj.js +0 -1
  223. package/dist/assets/architecture-TIHT7OUA-Bjp9jf0h.js +0 -1
  224. package/dist/assets/channel-5T3pdR-s.js +0 -1
  225. package/dist/assets/chat-page-C5E-LLVd.js +0 -1
  226. package/dist/assets/chunk-2Q5K7J3B-DTZ3tGfE.js +0 -1
  227. package/dist/assets/chunk-VAUOI2AC-HLGIHQ6k.js +0 -1
  228. package/dist/assets/chunk-XXDRQBXY-Dl0m6Ni-.js +0 -1
  229. package/dist/assets/classDiagram-OUVF2IWQ-DcDnzGNX.js +0 -1
  230. package/dist/assets/classDiagram-v2-EOCWNBFH-BKXKMzKM.js +0 -1
  231. package/dist/assets/config-split-page-CA_a0AJ5.js +0 -1
  232. package/dist/assets/confirm-dialog-CxZfenjK.js +0 -5
  233. package/dist/assets/cynefin-VYW2F7L2-BWg7U2UD.js +0 -1
  234. package/dist/assets/desktop-update-config-BEKC8Ly9.js +0 -1
  235. package/dist/assets/doc-browser-Cl2ldE3f.js +0 -1
  236. package/dist/assets/doc-browser-DXCSd63E.js +0 -1
  237. package/dist/assets/doc-browser-context-FDPs3gJf.js +0 -1
  238. package/dist/assets/eventmodeling-45OFAUF4-SB-J4tpq.js +0 -1
  239. package/dist/assets/flowDiagram-23GEKE2U-BQ_u-eLM.js +0 -1
  240. package/dist/assets/form-actions-0c0aw_bl.js +0 -1
  241. package/dist/assets/gitGraph-TEB2WS4Q-DPxNwsFd.js +0 -1
  242. package/dist/assets/i18n-provider-DkITJNz8.js +0 -1
  243. package/dist/assets/index-CQ2LcI2p.css +0 -1
  244. package/dist/assets/index-CrdfvBVg.js +0 -107
  245. package/dist/assets/info-DKCQHKI2-DNlNGH90.js +0 -1
  246. package/dist/assets/loader-circle-DHtUxCIA.js +0 -1
  247. package/dist/assets/mcp-marketplace-page-Bc_jI5iH.js +0 -9
  248. package/dist/assets/mcp-marketplace-page-CzCieD9r.js +0 -1
  249. package/dist/assets/model-config-page-C4jw-pcj.js +0 -1
  250. package/dist/assets/packet-7NZHBO7P-BpeiqkBZ.js +0 -1
  251. package/dist/assets/pie-RZYD4A2V-Cy1yxsPd.js +0 -1
  252. package/dist/assets/plus-CG1RwZQq.js +0 -1
  253. package/dist/assets/provider-scoped-model-input-CmH4BFvc.js +0 -1
  254. package/dist/assets/providers-config-page-CJQjwzaw.js +0 -1
  255. package/dist/assets/radar-I7S5WNFK-CEcPKCj8.js +0 -1
  256. package/dist/assets/railroad-3IZDKUUU-CW38eNDs.js +0 -1
  257. package/dist/assets/railroad-abnf-AHOZXSZD-ByrylcXf.js +0 -1
  258. package/dist/assets/railroad-ebnf-EBAXGLYW-CNB6GwP-.js +0 -1
  259. package/dist/assets/railroad-peg-LSFZ7HO6-x6hnqlBG.js +0 -1
  260. package/dist/assets/react-DjFBVA0n.js +0 -8
  261. package/dist/assets/remote-BIlpD3yL.js +0 -1
  262. package/dist/assets/rotate-cw-bKG8zH8M.js +0 -1
  263. package/dist/assets/runtime-config-page-B_x2Glz5.js +0 -1
  264. package/dist/assets/search-config-page-BaIijtmO.js +0 -1
  265. package/dist/assets/security-config-DB8apj09.js +0 -1
  266. package/dist/assets/skeleton-Bt6zxcAB.js +0 -1
  267. package/dist/assets/stateDiagram-v2-6OUMAXLB-ZC6dFORv.js +0 -1
  268. package/dist/assets/swimlanesDiagram-G3AALYLV-CRkh51Im.js +0 -8
  269. package/dist/assets/treeView-QDETBFTQ-Bw0DSCH7.js +0 -1
  270. package/dist/assets/treemap-6X3UGDF4-DFWBFcfh.js +0 -1
  271. package/dist/assets/wardley-OPB4EBWU-CoWturk0.js +0 -1
  272. package/dist/assets/x-CowtCu9Z.js +0 -1
  273. /package/dist/assets/{array-NADKd9aU.js → array-BQVzeGRt.js} +0 -0
  274. /package/dist/assets/{chunk-KEIR6QF5-BuyuzJJL.js → chunk-KEIR6QF5-f9psyCXK.js} +0 -0
  275. /package/dist/assets/{chunk-Y2CYZVJY-D_B2ZRNt.js → chunk-Y2CYZVJY-D1mAU_Po.js} +0 -0
  276. /package/dist/assets/{config-hints-BcfVUMij.js → config-hints-nV5HPOvq.js} +0 -0
  277. /package/dist/assets/{cytoscape.esm-BANTlgza.js → cytoscape.esm-0uDtdD0l.js} +0 -0
  278. /package/dist/assets/{defaultLocale-C4_L0aEs.js → defaultLocale-DSWXGgt0.js} +0 -0
  279. /package/dist/assets/{dist-DAPEIJbz.js → dist--ta5rX12.js} +0 -0
  280. /package/dist/assets/{graphlib-ChqXg0dI.js → graphlib-CQ6QV4yP.js} +0 -0
  281. /package/dist/assets/{host-capabilities-D9C4CBK5.js → host-capabilities-Tk038i04.js} +0 -0
  282. /package/dist/assets/{init-D8wZg9xq.js → init-Y6h9jGtZ.js} +0 -0
  283. /package/dist/assets/{jszip.min-CkbBfwAQ.js → jszip.min-8hyJpgpx.js} +0 -0
  284. /package/dist/assets/{katex-TZJFa0J_.js → katex-ARhY9PS1.js} +0 -0
  285. /package/dist/assets/{middleware-B3k-TSmZ.js → middleware-DXsbf9xI.js} +0 -0
  286. /package/dist/assets/{path-DodfxcX7.js → path-10BjdyLh.js} +0 -0
  287. /package/dist/assets/{rough.esm-CUaa1Rbe.js → rough.esm-DxgC_14r.js} +0 -0
  288. /package/dist/assets/{xlsx-CxByi7bR.js → xlsx-DrDuDNAa.js} +0 -0
@@ -0,0 +1,134 @@
1
+ import type { ChatComposerNode } from '@nextclaw/agent-chat-ui';
2
+ import {
3
+ CHAT_CONVERSATION_EXCERPT_TOKEN_KIND,
4
+ CHAT_PROJECT_TOKEN_KIND,
5
+ CHAT_SYSTEM_OBJECT_TOKEN_KIND,
6
+ CHAT_WORKSPACE_DIRECTORY_TOKEN_KIND,
7
+ CHAT_WORKSPACE_EXCERPT_TOKEN_KIND,
8
+ CHAT_WORKSPACE_FILE_TOKEN_KIND,
9
+ type ChatInlineTokenMetadata,
10
+ type ChatSkillSource,
11
+ } from '@nextclaw/shared';
12
+
13
+ import { serializeChatComposerTokenText } from './chat-composer-token-protocol.utils';
14
+ import { buildConversationExcerptInlineToken } from './chat-conversation-excerpt-token.utils';
15
+ import { readSystemObjectResolvedReference } from './chat-system-object-reference.utils';
16
+
17
+ export type ChatSkillReferenceSnapshot = {
18
+ ref: string;
19
+ name: string;
20
+ source: ChatSkillSource;
21
+ path: string;
22
+ };
23
+
24
+ function readOptionalString(value: unknown): string | null {
25
+ return typeof value === 'string' && value.trim() ? value.trim() : null;
26
+ }
27
+
28
+ function readOptionalLine(value: unknown): number | null {
29
+ return typeof value === 'number' && Number.isInteger(value) && value > 0
30
+ ? value
31
+ : null;
32
+ }
33
+
34
+ function dedupeInlineTokens(tokens: readonly ChatInlineTokenMetadata[]): ChatInlineTokenMetadata[] {
35
+ const seen = new Set<string>();
36
+ return tokens.filter((token) => {
37
+ const identity = 'ref' in token ? token.ref : token.key;
38
+ const dedupeKey = `${token.kind}:${identity}:${token.rawText}`;
39
+ if (seen.has(dedupeKey)) return false;
40
+ seen.add(dedupeKey);
41
+ return true;
42
+ });
43
+ }
44
+
45
+ function buildWorkspaceExcerptToken(
46
+ node: Extract<ChatComposerNode, { type: 'token' }>,
47
+ rawText: string,
48
+ ): ChatInlineTokenMetadata | null {
49
+ const path = readOptionalString(node.data?.path);
50
+ const excerpt = readOptionalString(node.data?.excerpt);
51
+ if (!path || !excerpt) return null;
52
+ return {
53
+ kind: CHAT_WORKSPACE_EXCERPT_TOKEN_KIND,
54
+ key: node.tokenKey,
55
+ path,
56
+ label: node.label,
57
+ excerpt,
58
+ startLine: readOptionalLine(node.data?.startLine),
59
+ endLine: readOptionalLine(node.data?.endLine),
60
+ rawText,
61
+ };
62
+ }
63
+
64
+ function buildStructuredReferenceToken(
65
+ node: Extract<ChatComposerNode, { type: 'token' }>,
66
+ rawText: string,
67
+ ): ChatInlineTokenMetadata | null | undefined {
68
+ if (node.tokenKind === CHAT_WORKSPACE_EXCERPT_TOKEN_KIND) {
69
+ return buildWorkspaceExcerptToken(node, rawText);
70
+ }
71
+ if (node.tokenKind === CHAT_CONVERSATION_EXCERPT_TOKEN_KIND) {
72
+ return buildConversationExcerptInlineToken(node, rawText);
73
+ }
74
+ if (node.tokenKind === CHAT_SYSTEM_OBJECT_TOKEN_KIND) {
75
+ const reference = readSystemObjectResolvedReference(node.data?.reference);
76
+ return reference?.uri === node.tokenKey
77
+ ? {
78
+ kind: CHAT_SYSTEM_OBJECT_TOKEN_KIND,
79
+ key: node.tokenKey,
80
+ label: node.label,
81
+ rawText,
82
+ reference,
83
+ }
84
+ : null;
85
+ }
86
+ return undefined;
87
+ }
88
+
89
+ export function buildInlineTokensFromComposer(
90
+ nodes: readonly ChatComposerNode[],
91
+ skillRecords: readonly ChatSkillReferenceSnapshot[] = [],
92
+ ): ChatInlineTokenMetadata[] {
93
+ const skillByRef = new Map(skillRecords.map((record) => [record.ref, record]));
94
+ const tokens: ChatInlineTokenMetadata[] = [];
95
+ for (const node of nodes) {
96
+ if (node.type !== 'token') continue;
97
+ const rawText = serializeChatComposerTokenText(node);
98
+ if (!rawText) continue;
99
+ if (node.tokenKind === 'skill') {
100
+ const skill = skillByRef.get(node.tokenKey);
101
+ if (skill?.path.trim()) {
102
+ tokens.push({
103
+ kind: 'skill',
104
+ ref: skill.ref,
105
+ name: skill.name,
106
+ source: skill.source,
107
+ path: skill.path,
108
+ label: node.label,
109
+ rawText,
110
+ });
111
+ }
112
+ continue;
113
+ }
114
+ if (node.tokenKind === CHAT_PROJECT_TOKEN_KIND) {
115
+ tokens.push({ kind: CHAT_PROJECT_TOKEN_KIND, key: node.tokenKey, label: node.label, rawText });
116
+ continue;
117
+ }
118
+ const structuredReference = buildStructuredReferenceToken(node, rawText);
119
+ if (structuredReference !== undefined) {
120
+ if (structuredReference) tokens.push(structuredReference);
121
+ continue;
122
+ }
123
+ if (
124
+ node.tokenKind === CHAT_WORKSPACE_FILE_TOKEN_KIND ||
125
+ node.tokenKind === CHAT_WORKSPACE_DIRECTORY_TOKEN_KIND
126
+ ) {
127
+ const workspaceKind = node.tokenKind === CHAT_WORKSPACE_FILE_TOKEN_KIND
128
+ ? CHAT_WORKSPACE_FILE_TOKEN_KIND
129
+ : CHAT_WORKSPACE_DIRECTORY_TOKEN_KIND;
130
+ tokens.push({ kind: workspaceKind, key: node.tokenKey, label: node.label, rawText });
131
+ }
132
+ }
133
+ return dedupeInlineTokens(tokens);
134
+ }
@@ -1,18 +1,23 @@
1
- import type { ChatComposerNode } from '@nextclaw/agent-chat-ui';
2
1
  import {
3
2
  CHAT_CONVERSATION_EXCERPT_TOKEN_KIND,
4
3
  CHAT_INLINE_TOKENS_METADATA_KEY,
5
4
  CHAT_INLINE_TOKENS_SCHEMA_VERSION,
6
5
  CHAT_PROJECT_TOKEN_KIND,
6
+ CHAT_SYSTEM_OBJECT_TOKEN_KIND,
7
7
  CHAT_WORKSPACE_DIRECTORY_TOKEN_KIND,
8
8
  CHAT_WORKSPACE_EXCERPT_TOKEN_KIND,
9
9
  CHAT_WORKSPACE_FILE_TOKEN_KIND,
10
10
  type ChatInlineTokenMetadata,
11
11
  type ChatInlineTokensMetadata,
12
12
  type ChatSkillSource,
13
+ type SystemObjectResolvedReference,
13
14
  } from '@nextclaw/shared';
14
- import { serializeChatComposerTokenText } from './chat-composer-token-protocol.utils';
15
- import { buildConversationExcerptInlineToken, readConversationExcerptInlineToken } from './chat-conversation-excerpt-token.utils';
15
+ import { readConversationExcerptInlineToken } from './chat-conversation-excerpt-token.utils';
16
+ import { readSystemObjectResolvedReference } from './chat-system-object-reference.utils';
17
+ export {
18
+ buildInlineTokensFromComposer,
19
+ type ChatSkillReferenceSnapshot,
20
+ } from './chat-inline-token-composer.utils';
16
21
 
17
22
  export { CHAT_INLINE_TOKENS_METADATA_KEY };
18
23
  const CHAT_PANEL_APP_TOKEN_PREFIX = '@panel-app:';
@@ -20,13 +25,7 @@ const CHAT_PANEL_APP_TOKEN_PATTERN = /@panel-app:([A-Za-z0-9_-]+)/g;
20
25
  const CHAT_PROJECT_TOKEN_PATTERN = /@project:([^\s]+)/g;
21
26
  const CHAT_WORKSPACE_FILE_TOKEN_PATTERN = /@file:([^\s]+)/g;
22
27
  const CHAT_WORKSPACE_DIRECTORY_TOKEN_PATTERN = /@folder:([^\s]+)/g;
23
-
24
- export type ChatSkillReferenceSnapshot = {
25
- ref: string;
26
- name: string;
27
- source: ChatSkillSource;
28
- path: string;
29
- };
28
+ const CHAT_SYSTEM_OBJECT_TOKEN_PATTERN = /@object:([^\s]+)/g;
30
29
 
31
30
  export type ChatInlineTokenSource =
32
31
  | {
@@ -62,6 +61,13 @@ export type ChatInlineTokenSource =
62
61
  key: string;
63
62
  label: string;
64
63
  rawText: string;
64
+ }
65
+ | {
66
+ kind: typeof CHAT_SYSTEM_OBJECT_TOKEN_KIND;
67
+ key: string;
68
+ label: string;
69
+ rawText: string;
70
+ reference: SystemObjectResolvedReference;
65
71
  };
66
72
 
67
73
  export function resolveWorkspaceReferencePath(params: {
@@ -114,100 +120,10 @@ function dedupeInlineTokens<T extends ChatInlineTokenSource>(tokens: readonly T[
114
120
  return output;
115
121
  }
116
122
 
117
- function appendWorkspaceExcerptToken(
118
- tokens: ChatInlineTokenMetadata[],
119
- node: Extract<ChatComposerNode, { type: 'token' }>,
120
- rawText: string,
121
- ): void {
122
- const path = readOptionalString(node.data?.path);
123
- const excerpt = readOptionalString(node.data?.excerpt);
124
- if (!path || !excerpt) return;
125
- tokens.push({
126
- kind: CHAT_WORKSPACE_EXCERPT_TOKEN_KIND,
127
- key: node.tokenKey,
128
- path,
129
- label: node.label,
130
- excerpt,
131
- startLine: readOptionalLine(node.data?.startLine),
132
- endLine: readOptionalLine(node.data?.endLine),
133
- rawText,
134
- });
135
- }
136
-
137
- export function buildInlineTokensFromComposer(
138
- nodes: readonly ChatComposerNode[],
139
- skillRecords: readonly ChatSkillReferenceSnapshot[] = [],
140
- ): ChatInlineTokenMetadata[] {
141
- const skillByRef = new Map(skillRecords.map((record) => [record.ref, record]));
142
- const tokens: ChatInlineTokenMetadata[] = [];
143
- for (const node of nodes) {
144
- if (node.type !== 'token') {
145
- continue;
146
- }
147
- if (
148
- node.tokenKind !== 'skill' &&
149
- node.tokenKind !== CHAT_PROJECT_TOKEN_KIND &&
150
- node.tokenKind !== CHAT_WORKSPACE_FILE_TOKEN_KIND &&
151
- node.tokenKind !== CHAT_WORKSPACE_DIRECTORY_TOKEN_KIND &&
152
- node.tokenKind !== CHAT_WORKSPACE_EXCERPT_TOKEN_KIND &&
153
- node.tokenKind !== CHAT_CONVERSATION_EXCERPT_TOKEN_KIND
154
- ) {
155
- continue;
156
- }
157
- const rawText = serializeChatComposerTokenText(node);
158
- if (!rawText) {
159
- continue;
160
- }
161
- if (node.tokenKind === 'skill') {
162
- const skill = skillByRef.get(node.tokenKey);
163
- if (!skill?.path.trim()) {
164
- continue;
165
- }
166
- tokens.push({
167
- kind: 'skill',
168
- ref: skill.ref,
169
- name: skill.name,
170
- source: skill.source,
171
- path: skill.path,
172
- label: node.label,
173
- rawText,
174
- });
175
- continue;
176
- }
177
- if (node.tokenKind === CHAT_PROJECT_TOKEN_KIND) {
178
- tokens.push({
179
- kind: CHAT_PROJECT_TOKEN_KIND,
180
- key: node.tokenKey,
181
- label: node.label,
182
- rawText,
183
- });
184
- continue;
185
- }
186
- if (node.tokenKind === CHAT_WORKSPACE_EXCERPT_TOKEN_KIND) {
187
- appendWorkspaceExcerptToken(tokens, node, rawText);
188
- continue;
189
- }
190
- if (node.tokenKind === CHAT_CONVERSATION_EXCERPT_TOKEN_KIND) {
191
- const token = buildConversationExcerptInlineToken(node, rawText);
192
- if (token) tokens.push(token);
193
- continue;
194
- }
195
- const workspaceKind = node.tokenKind === CHAT_WORKSPACE_FILE_TOKEN_KIND
196
- ? CHAT_WORKSPACE_FILE_TOKEN_KIND
197
- : CHAT_WORKSPACE_DIRECTORY_TOKEN_KIND;
198
- tokens.push({
199
- kind: workspaceKind,
200
- key: node.tokenKey,
201
- label: node.label,
202
- rawText,
203
- });
204
- }
205
- return dedupeInlineTokens(tokens);
206
- }
207
-
208
123
  function appendEncodedKeyTokens(params: {
209
124
  kind:
210
125
  | typeof CHAT_PROJECT_TOKEN_KIND
126
+ | typeof CHAT_SYSTEM_OBJECT_TOKEN_KIND
211
127
  | typeof CHAT_WORKSPACE_FILE_TOKEN_KIND
212
128
  | typeof CHAT_WORKSPACE_DIRECTORY_TOKEN_KIND;
213
129
  pattern: RegExp;
@@ -255,6 +171,12 @@ export function buildInlineTokensFromTextProtocol(text: string): ChatInlineToken
255
171
  text,
256
172
  tokens,
257
173
  });
174
+ appendEncodedKeyTokens({
175
+ kind: CHAT_SYSTEM_OBJECT_TOKEN_KIND,
176
+ pattern: CHAT_SYSTEM_OBJECT_TOKEN_PATTERN,
177
+ text,
178
+ tokens,
179
+ });
258
180
  appendEncodedKeyTokens({
259
181
  kind: CHAT_WORKSPACE_FILE_TOKEN_KIND,
260
182
  pattern: CHAT_WORKSPACE_FILE_TOKEN_PATTERN,
@@ -340,6 +262,13 @@ function readInlineTokenEntry(entry: unknown): ChatInlineTokenSource | null {
340
262
  if (kind === CHAT_CONVERSATION_EXCERPT_TOKEN_KIND) {
341
263
  return readConversationExcerptInlineToken({ entry, label, rawText });
342
264
  }
265
+ if (kind === CHAT_SYSTEM_OBJECT_TOKEN_KIND) {
266
+ const key = readOptionalString(entry.key);
267
+ const reference = readSystemObjectResolvedReference(entry.reference);
268
+ return key && reference && reference.uri === key
269
+ ? { kind, key, label, rawText, reference }
270
+ : null;
271
+ }
343
272
  const key = readOptionalString(entry.key);
344
273
  return key ? { kind, key, rawText, label } : null;
345
274
  }
@@ -0,0 +1,44 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { CHAT_SYSTEM_OBJECT_TOKEN_KIND, type SystemObjectResolvedReference } from '@nextclaw/shared';
3
+ import {
4
+ appendSystemObjectReferenceToken,
5
+ readSystemObjectReferenceFromToken,
6
+ } from './chat-system-object-reference.utils';
7
+
8
+ const reference: SystemObjectResolvedReference = {
9
+ uri: 'nextclaw://objects/cron-job/cron-1',
10
+ objectType: 'cron-job',
11
+ objectId: 'cron-1',
12
+ label: 'Daily review',
13
+ description: 'Review reports',
14
+ updatedAt: '2026-08-11T00:00:00.000Z',
15
+ version: 'a'.repeat(64),
16
+ assetUri: 'asset://store/cron-1',
17
+ fileName: 'daily-review.md',
18
+ mimeType: 'text/markdown',
19
+ sizeBytes: 42,
20
+ };
21
+
22
+ describe('system object reference composer utilities', () => {
23
+ it('creates one visible generic token and deduplicates the same snapshot', () => {
24
+ const first = appendSystemObjectReferenceToken([], reference);
25
+ const second = appendSystemObjectReferenceToken(first, reference);
26
+
27
+ expect(second).toHaveLength(first.length);
28
+ expect(second.find((node) => node.type === 'token')).toMatchObject({
29
+ tokenKind: CHAT_SYSTEM_OBJECT_TOKEN_KIND,
30
+ tokenKey: reference.uri,
31
+ label: reference.label,
32
+ });
33
+ expect(readSystemObjectReferenceFromToken(second[0]!)).toEqual(reference);
34
+ });
35
+
36
+ it('rejects metadata whose URI identity disagrees with its object fields', () => {
37
+ const [node] = appendSystemObjectReferenceToken([], {
38
+ ...reference,
39
+ objectId: 'different-cron',
40
+ });
41
+
42
+ expect(readSystemObjectReferenceFromToken(node!)).toBeNull();
43
+ });
44
+ });
@@ -0,0 +1,46 @@
1
+ import {
2
+ createChatComposerTokenNode,
3
+ normalizeChatComposerNodes,
4
+ type ChatComposerNode,
5
+ } from '@nextclaw/agent-chat-ui';
6
+ import {
7
+ CHAT_SYSTEM_OBJECT_TOKEN_KIND,
8
+ readSystemObjectResolvedReference,
9
+ type SystemObjectResolvedReference,
10
+ } from '@nextclaw/shared';
11
+
12
+ export { readSystemObjectResolvedReference } from '@nextclaw/shared';
13
+
14
+ export function createSystemObjectReferenceTokenNode(
15
+ reference: SystemObjectResolvedReference,
16
+ ): ChatComposerNode {
17
+ return createChatComposerTokenNode({
18
+ tokenKind: CHAT_SYSTEM_OBJECT_TOKEN_KIND,
19
+ tokenKey: reference.uri,
20
+ label: reference.label,
21
+ data: { reference },
22
+ });
23
+ }
24
+
25
+ export function appendSystemObjectReferenceToken(
26
+ nodes: readonly ChatComposerNode[],
27
+ reference: SystemObjectResolvedReference,
28
+ ): ChatComposerNode[] {
29
+ const exists = nodes.some((node) =>
30
+ node.type === 'token' &&
31
+ node.tokenKind === CHAT_SYSTEM_OBJECT_TOKEN_KIND &&
32
+ node.tokenKey === reference.uri &&
33
+ readSystemObjectResolvedReference(node.data?.reference)?.version === reference.version
34
+ );
35
+ return exists
36
+ ? normalizeChatComposerNodes([...nodes])
37
+ : normalizeChatComposerNodes([...nodes, createSystemObjectReferenceTokenNode(reference)]);
38
+ }
39
+
40
+ export function readSystemObjectReferenceFromToken(
41
+ node: ChatComposerNode,
42
+ ): SystemObjectResolvedReference | null {
43
+ return node.type === 'token' && node.tokenKind === CHAT_SYSTEM_OBJECT_TOKEN_KIND
44
+ ? readSystemObjectResolvedReference(node.data?.reference)
45
+ : null;
46
+ }
@@ -169,6 +169,7 @@ beforeEach(() => {
169
169
  });
170
170
  mocks.refetchBrowse.mockResolvedValue(undefined);
171
171
  useChatThreadStore.getState().setSnapshot({
172
+ workspaceExplorerOpen: false,
172
173
  workspaceExplorerWidth: CHAT_WORKSPACE_EXPLORER_DEFAULT_WIDTH,
173
174
  });
174
175
  });
@@ -626,7 +627,81 @@ it('preserves project tree expansion and scroll while visiting another workspace
626
627
  expect(tree.scrollTop).toBe(128);
627
628
  });
628
629
 
629
- it('reuses the Explorer beside a file and persists a dragged compact width', () => {
630
+ it('selects and reveals the active file when it is opened outside the Explorer', async () => {
631
+ const user = userEvent.setup();
632
+ const projectFilesProps = {
633
+ activeSelection: { kind: 'project-files' as const },
634
+ childSessionTabs: [],
635
+ filePreviewRefreshVersion: 0,
636
+ sessionKey: 'parent-1',
637
+ sessionCronJobs: [],
638
+ sessionProjectRoot: '/Users/peiwang/Projects/nextbot',
639
+ sessionWorkingDir: '/Users/peiwang/Projects/nextbot',
640
+ };
641
+ const { rerender } = render(<ChatSessionWorkspacePanelContent {...projectFilesProps} />);
642
+ const sourceDirectory = screen.getByRole('treeitem', { name: 'Open directory: src' });
643
+ const packageEntry = screen.getByRole('treeitem', { name: 'Open file: package.json' });
644
+ await user.click(screen.getByRole('button', { name: 'src' }));
645
+ expect(sourceDirectory.getAttribute('aria-selected')).toBe('true');
646
+
647
+ rerender(
648
+ <ChatSessionWorkspacePanelContent
649
+ {...projectFilesProps}
650
+ activeSelection={{
651
+ kind: 'file',
652
+ file: {
653
+ key: 'parent-1::preview::package.json',
654
+ parentSessionKey: 'parent-1',
655
+ path: 'package.json',
656
+ label: 'package.json',
657
+ viewMode: 'preview',
658
+ previewViewer: 'source',
659
+ },
660
+ }}
661
+ />,
662
+ );
663
+
664
+ await waitFor(() => {
665
+ expect(packageEntry.getAttribute('aria-selected')).toBe('true');
666
+ expect(sourceDirectory.getAttribute('aria-selected')).toBe('false');
667
+ });
668
+ expect(mocks.scrollIntoView).toHaveBeenCalled();
669
+ expect(document.activeElement).not.toBe(screen.getByRole('button', { name: 'package.json' }));
670
+ });
671
+
672
+ it('expands active-file ancestors like an auto-revealing Explorer', async () => {
673
+ useChatThreadStore.getState().setSnapshot({ workspaceExplorerOpen: true });
674
+ render(
675
+ <ChatSessionWorkspacePanelContent
676
+ activeSelection={{
677
+ kind: 'file',
678
+ file: {
679
+ key: 'parent-1::preview::src/index.ts',
680
+ parentSessionKey: 'parent-1',
681
+ path: '/Users/peiwang/Projects/nextbot/src/index.ts',
682
+ label: 'index.ts',
683
+ viewMode: 'preview',
684
+ previewViewer: 'source',
685
+ },
686
+ }}
687
+ childSessionTabs={[]}
688
+ filePreviewRefreshVersion={0}
689
+ sessionKey="parent-1"
690
+ sessionCronJobs={[]}
691
+ sessionProjectRoot="/Users/peiwang/Projects/nextbot"
692
+ sessionWorkingDir="/Users/peiwang/Projects/nextbot"
693
+ />,
694
+ );
695
+
696
+ const sourceDirectory = within(screen.getByTestId('workspace-shared-explorer')).getAllByRole('treeitem', {
697
+ name: 'Open directory: src',
698
+ })[0];
699
+ await waitFor(() => {
700
+ expect(sourceDirectory.getAttribute('aria-expanded')).toBe('true');
701
+ });
702
+ });
703
+
704
+ it('keeps the file Explorer closed by default and reuses it after an explicit open', () => {
630
705
  const projectFilesProps = {
631
706
  activeSelection: { kind: 'project-files' as const },
632
707
  childSessionTabs: [],
@@ -659,6 +734,9 @@ it('reuses the Explorer beside a file and persists a dragged compact width', ()
659
734
  const explorer = screen.getByTestId('workspace-shared-explorer');
660
735
  const breadcrumbs = screen.getByTestId('workspace-file-breadcrumbs');
661
736
  const toggle = screen.getByTestId('workspace-explorer-toggle');
737
+ expect(explorer.getAttribute('aria-hidden')).toBe('true');
738
+ fireEvent.click(toggle);
739
+ expect(useChatThreadStore.getState().snapshot.workspaceExplorerOpen).toBe(true);
662
740
  expect(screen.getByRole('tree', { name: 'Project files' })).toBe(tree);
663
741
  expect(explorer.getAttribute('data-mode')).toBe('side');
664
742
  expect(explorer.style.width).toBe('224px');
@@ -674,7 +752,7 @@ it('reuses the Explorer beside a file and persists a dragged compact width', ()
674
752
  expect(mocks.setWorkspaceExplorerWidth).toHaveBeenCalledWith(300);
675
753
  });
676
754
 
677
- it('uses an overlay only below the compact threshold and closes it without unmounting', () => {
755
+ it('uses an overlay only below the compact threshold and persists closing it without unmounting', () => {
678
756
  HTMLElement.prototype.getBoundingClientRect = () => ({
679
757
  bottom: 600,
680
758
  height: 600,
@@ -686,6 +764,7 @@ it('uses an overlay only below the compact threshold and closes it without unmou
686
764
  y: 0,
687
765
  toJSON: () => ({}),
688
766
  });
767
+ useChatThreadStore.getState().setSnapshot({ workspaceExplorerOpen: true });
689
768
  render(
690
769
  <ChatSessionWorkspacePanelContent
691
770
  activeSelection={{
@@ -714,6 +793,7 @@ it('uses an overlay only below the compact threshold and closes it without unmou
714
793
  expect(screen.queryByTestId('workspace-explorer-resize-handle')).toBeNull();
715
794
 
716
795
  fireEvent.click(screen.getByTestId('workspace-explorer-scrim'));
796
+ expect(useChatThreadStore.getState().snapshot.workspaceExplorerOpen).toBe(false);
717
797
  expect(explorer.getAttribute('aria-hidden')).toBe('true');
718
798
  expect(tree.isConnected).toBe(true);
719
799
  });
@@ -22,14 +22,18 @@ vi.mock("@/shared/hooks/use-server-path-browse", () => ({
22
22
  useServerPathBrowse: () => ({ data: null, error: null, isLoading: false }),
23
23
  }));
24
24
 
25
- vi.mock("@nextclaw/agent-chat-ui", () => ({
26
- ChatMessageMarkdown: ({ text }: { text: string }) => <div>{text}</div>,
27
- FileOperationCodeSurface: ({ block }: { block: ChatFileOperationBlockViewModel }) => (
28
- <div data-testid="json-source-preview">
29
- {block.lines.map(({ text }, index) => <span key={index}>{text}{"\n"}</span>)}
30
- </div>
31
- ),
32
- }));
25
+ vi.mock("@nextclaw/agent-chat-ui", async (importOriginal) => {
26
+ const actual = await importOriginal();
27
+ return {
28
+ ...(actual as object),
29
+ ChatMessageMarkdown: ({ text }: { text: string }) => <div>{text}</div>,
30
+ FileOperationCodeSurface: ({ block }: { block: ChatFileOperationBlockViewModel }) => (
31
+ <div data-testid="json-source-preview">
32
+ {block.lines.map(({ text }, index) => <span key={index}>{text}{"\n"}</span>)}
33
+ </div>
34
+ ),
35
+ };
36
+ });
33
37
 
34
38
  it("supports the first text selection in a JSON preview", async () => {
35
39
  const onTextExcerptAdd = vi.fn();
@@ -24,6 +24,7 @@ import { hostCapabilityManager } from '@/shared/lib/host-capabilities';
24
24
  import { t } from '@/shared/lib/i18n';
25
25
 
26
26
  type ChatSessionWorkspaceDirectoryBrowserProps = {
27
+ activeRelativePath?: string | null;
27
28
  browseQuery: ReturnType<typeof useServerPathBrowse>;
28
29
  onFileOpen: (action: ChatFileOpenActionViewModel) => void;
29
30
  sessionKey?: string | null;
@@ -36,6 +37,7 @@ function readPathName(path: string): string {
36
37
  }
37
38
 
38
39
  function WorkspaceDirectoryEntries({
40
+ activeRelativePath,
39
41
  busy,
40
42
  collapseVersion,
41
43
  createTarget,
@@ -59,6 +61,7 @@ function WorkspaceDirectoryEntries({
59
61
  selectedPath,
60
62
  showRoot,
61
63
  }: {
64
+ activeRelativePath: string | null;
62
65
  busy: boolean;
63
66
  collapseVersion: number;
64
67
  createTarget: WorkspaceDirectoryActionTarget | null;
@@ -87,6 +90,7 @@ function WorkspaceDirectoryEntries({
87
90
  }
88
91
  return entries.map((entry) => (
89
92
  <WorkspaceDirectoryTreeEntry
93
+ activeRelativePath={activeRelativePath}
90
94
  key={entry.path}
91
95
  browseParentRefresh={refresh}
92
96
  busy={busy}
@@ -243,6 +247,7 @@ function WorkspaceProjectTree({
243
247
  }
244
248
 
245
249
  function WorkspaceDirectoryBrowserReady({
250
+ activeRelativePath,
246
251
  browseQuery,
247
252
  ConfirmDialog,
248
253
  controller,
@@ -254,6 +259,7 @@ function WorkspaceDirectoryBrowserReady({
254
259
  rootPath,
255
260
  showRoot,
256
261
  }: {
262
+ activeRelativePath: string | null;
257
263
  browseQuery: ReturnType<typeof useServerPathBrowse>;
258
264
  ConfirmDialog: () => ReactNode;
259
265
  controller: ReturnType<typeof useWorkspaceProjectFilesController>;
@@ -266,30 +272,32 @@ function WorkspaceDirectoryBrowserReady({
266
272
  showRoot: boolean;
267
273
  }) {
268
274
  const {
269
- actions,
270
275
  cancelCreateFolder,
271
276
  clearSelection,
272
277
  completeReveal,
273
278
  submitCreate,
274
- createError,
275
- createKind,
276
- createName,
277
- createTarget,
278
279
  deleteEntry,
279
- fileInputRef,
280
280
  openCreateFile,
281
281
  openCreateFolder,
282
282
  requestUpload,
283
283
  renameEntry,
284
- revealPath,
285
284
  selectEntry,
286
- selectedCreateTarget,
287
- selectedPath,
288
285
  setCreateError,
289
286
  setCreateName,
290
287
  uploadFiles,
291
- } = controller;
292
- const busy = actions.pendingAction !== null;
288
+ } = controller.actions;
289
+ const { fileInputRef } = controller.refs;
290
+ const {
291
+ createError,
292
+ createKind,
293
+ createName,
294
+ createTarget,
295
+ pendingAction,
296
+ revealPath,
297
+ selectedCreateTarget,
298
+ selectedPath,
299
+ } = controller.state;
300
+ const busy = pendingAction !== null;
293
301
  const [collapseVersion, setCollapseVersion] = useState(0);
294
302
  const rootRefresh = () => browseQuery.refetch();
295
303
  const rootTarget: WorkspaceDirectoryActionTarget | null = rootPath
@@ -300,7 +308,7 @@ function WorkspaceDirectoryBrowserReady({
300
308
  <WorkspaceNewFolderTreeItem
301
309
  error={createError}
302
310
  kind={createKind}
303
- isCreating={actions.pendingAction === 'create-directory' || actions.pendingAction === 'create-file'}
311
+ isCreating={pendingAction === 'create-directory' || pendingAction === 'create-file'}
304
312
  level={level}
305
313
  name={createName}
306
314
  onCancel={cancelCreateFolder}
@@ -318,6 +326,7 @@ function WorkspaceDirectoryBrowserReady({
318
326
  };
319
327
  const treeEntries = (
320
328
  <WorkspaceDirectoryEntries
329
+ activeRelativePath={activeRelativePath}
321
330
  busy={busy}
322
331
  collapseVersion={collapseVersion}
323
332
  createTarget={createTarget}
@@ -406,6 +415,7 @@ function WorkspaceDirectoryBrowserReady({
406
415
  }
407
416
 
408
417
  export function ChatSessionWorkspaceDirectoryBrowser({
418
+ activeRelativePath = null,
409
419
  browseQuery,
410
420
  onFileOpen,
411
421
  sessionKey,
@@ -419,6 +429,7 @@ export function ChatSessionWorkspaceDirectoryBrowser({
419
429
  const rootPath = showRoot ? (browseQuery.data?.currentPath ?? null) : null;
420
430
  const rootLabel = rootPath ? readPathName(rootPath) : '';
421
431
  const controller = useWorkspaceProjectFilesController({
432
+ activeRelativePath,
422
433
  confirm,
423
434
  onEntryDeleted: presenter.chatThreadManager.removeWorkspacePath,
424
435
  onEntryRenamed: presenter.chatThreadManager.renameWorkspacePath,
@@ -457,6 +468,7 @@ export function ChatSessionWorkspaceDirectoryBrowser({
457
468
  }
458
469
  return (
459
470
  <WorkspaceDirectoryBrowserReady
471
+ activeRelativePath={activeRelativePath}
460
472
  browseQuery={browseQuery}
461
473
  ConfirmDialog={ConfirmDialog}
462
474
  controller={controller}