@nextclaw/ui 0.15.25 → 0.15.27

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 (334) hide show
  1. package/CHANGELOG.md +34 -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-a_GaOFc7.js} +6 -6
  5. package/dist/assets/appearance-settings-page-DlGXLgff.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-BEEk4Uc9.js} +2 -2
  13. package/dist/assets/chat-page-DUQXXqQm.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-ps1Aod3g.js +1 -0
  55. package/dist/assets/confirm-dialog-DJSUNG33.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--zagjHic.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-CaYdVVeO.js → dist-BkTYNzm_.js} +1 -1
  68. package/dist/assets/{dist-BH7W5yCi.js → dist-DkR7_Dpl.js} +1 -1
  69. package/dist/assets/doc-browser-DahzJEWj.js +1 -0
  70. package/dist/assets/doc-browser-Q67TOGVS.js +1 -0
  71. package/dist/assets/doc-browser-context-BQim8SqD.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-0c0aw_bl.js → form-actions-B2-bpFie.js} +1 -1
  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-Ba_o5fu9.js +109 -0
  82. package/dist/assets/index-DSLK7zgR.css +1 -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-DyXZU92l.js +1 -0
  91. package/dist/assets/{map-CcZzEoz6.js → map-BKnQyvd0.js} +1 -1
  92. package/dist/assets/mcp-marketplace-page-B0dBE5CV.js +1 -0
  93. package/dist/assets/mcp-marketplace-page-w8panjZG.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-rrhFaque.js +1 -0
  98. package/dist/assets/{navigation-link-CVy0_kVl.js → navigation-link-mpHFXwRr.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-Dhb4KuMH.js +1 -0
  105. package/dist/assets/provider-scoped-model-input-CFsfWEt9.js +1 -0
  106. package/dist/assets/providers-config-page-D-TYFZsF.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-BlXwvOLo.js +8 -0
  115. package/dist/assets/{refresh-cw-j2bl79Ea.js → refresh-cw-JFuyDj_P.js} +1 -1
  116. package/dist/assets/remote-BwVv_Qq-.js +1 -0
  117. package/dist/assets/{requirementDiagram-TGXJPOKE-CyVUzEX5.js → requirementDiagram-TGXJPOKE-BiXNX1NN.js} +1 -1
  118. package/dist/assets/rotate-cw-DjbbiIW_.js +1 -0
  119. package/dist/assets/runtime-config-page-Br9j9Vxe.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-BL8P8CLX.js} +1 -1
  122. package/dist/assets/{search-CkOm65W_.js → search-bZL75Pgc.js} +1 -1
  123. package/dist/assets/search-config-page-B3fvzMd1.js +1 -0
  124. package/dist/assets/secrets-config-page-ClwTxtXd.js +3 -0
  125. package/dist/assets/security-config-DmJw9QZA.js +1 -0
  126. package/dist/assets/{select-Ea7ffKCE.js → select-rUZ8nIZu.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-BI4SCkxQ.js} +1 -1
  129. package/dist/assets/{sizeCapture-X5ZJPWSS-SxG_YnMA.js → sizeCapture-X5ZJPWSS-CMYvM3SL.js} +1 -1
  130. package/dist/assets/skeleton-CVmEPG43.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-BdI_yIOA.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-BcEayhV6.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/app/components/layout/app-layout.tsx +38 -2
  148. package/src/app/hooks/__tests__/use-app-event-consumers.test.ts +20 -0
  149. package/src/app/hooks/use-app-event-consumers.ts +21 -0
  150. package/src/app/presenters/app.presenter.ts +6 -0
  151. package/src/features/apps/components/__tests__/app-packages-panel.test.tsx +312 -0
  152. package/src/features/apps/components/__tests__/apps-panel.test.tsx +58 -0
  153. package/src/features/apps/components/app-artwork.tsx +49 -0
  154. package/src/features/apps/components/app-marketplace-catalog.tsx +332 -0
  155. package/src/features/apps/components/app-marketplace-cover.tsx +64 -0
  156. package/src/features/apps/components/app-marketplace-detail.tsx +161 -0
  157. package/src/features/apps/components/app-marketplace-dialog.tsx +152 -0
  158. package/src/features/apps/components/app-marketplace-operation.tsx +84 -0
  159. package/src/features/apps/components/app-package-card.tsx +417 -0
  160. package/src/features/apps/components/app-packages-panel.tsx +373 -0
  161. package/src/features/apps/components/apps-panel.tsx +31 -23
  162. package/src/features/apps/hooks/use-app-marketplace.test.ts +144 -0
  163. package/src/features/apps/hooks/use-app-marketplace.ts +65 -0
  164. package/src/features/apps/hooks/use-app-packages.ts +114 -0
  165. package/src/features/apps/index.ts +1 -0
  166. package/src/features/apps/managers/__tests__/app-package-operation-settlement.manager.test.ts +81 -0
  167. package/src/features/apps/managers/app-package-operation-settlement.manager.ts +80 -0
  168. package/src/features/apps/services/app-marketplace.service.ts +223 -0
  169. package/src/features/apps/types/app-marketplace.types.ts +75 -0
  170. package/src/features/chat/components/chat-sidebar-list-mode-switch.tsx +27 -4
  171. package/src/features/chat/components/layout/__tests__/chat-sidebar-session-area.test.tsx +36 -5
  172. package/src/features/chat/components/layout/chat-sidebar-desktop-layout.tsx +12 -13
  173. package/src/features/chat/features/conversation/components/__tests__/session-conversation-area.test.tsx +49 -1
  174. package/src/features/chat/features/conversation/components/__tests__/session-conversation-input.streaming.test.tsx +68 -7
  175. package/src/features/chat/features/conversation/components/session-conversation-area.tsx +25 -4
  176. package/src/features/chat/features/conversation/components/session-conversation-input.tsx +3 -0
  177. package/src/features/chat/features/conversation/components/session-queued-input-rows.tsx +24 -14
  178. package/src/features/chat/features/conversation/hooks/__tests__/use-session-conversation-controller.test.tsx +53 -0
  179. package/src/features/chat/features/conversation/hooks/__tests__/use-session-run-queue.test.tsx +18 -0
  180. package/src/features/chat/features/conversation/hooks/use-chat-composer-reference-intent.ts +4 -1
  181. package/src/features/chat/features/conversation/hooks/use-session-conversation-controller.ts +126 -32
  182. package/src/features/chat/features/conversation/hooks/use-session-run-queue.ts +12 -2
  183. package/src/features/chat/features/conversation/hooks/use-system-object-reference-select.ts +27 -0
  184. package/src/features/chat/features/conversation/utils/session-message-composer.utils.ts +5 -0
  185. package/src/features/chat/features/input/hooks/__tests__/use-chat-input-surface-state.test.tsx +102 -0
  186. package/src/features/chat/features/input/hooks/use-chat-input-surface-state.ts +71 -120
  187. package/src/features/chat/features/input/input-surface-plugins/__tests__/context-reference-plugin.test.ts +265 -18
  188. package/src/features/chat/features/input/input-surface-plugins/chat-input-product-plugin-adapters.types.ts +7 -2
  189. package/src/features/chat/features/input/input-surface-plugins/chat-input-product-plugin-adapters.utils.ts +124 -0
  190. package/src/features/chat/features/input/input-surface-plugins/context-reference-items.utils.ts +99 -15
  191. package/src/features/chat/features/input/input-surface-plugins/context-reference-plugin.utils.ts +143 -139
  192. package/src/features/chat/features/input/input-surface-plugins/panel-app-input-surface-items.utils.ts +116 -0
  193. package/src/features/chat/features/input/input-surface-plugins/slash-command-plugin.utils.ts +1 -1
  194. package/src/features/chat/features/input/utils/__tests__/chat-inline-token.utils.test.ts +84 -1
  195. package/src/features/chat/features/input/utils/chat-composer-token-protocol.utils.ts +10 -0
  196. package/src/features/chat/features/input/utils/chat-inline-token-composer.utils.ts +148 -0
  197. package/src/features/chat/features/input/utils/chat-inline-token.utils.ts +55 -101
  198. package/src/features/chat/features/input/utils/chat-system-object-reference.utils.test.ts +44 -0
  199. package/src/features/chat/features/input/utils/chat-system-object-reference.utils.ts +46 -0
  200. package/src/features/chat/features/message/hooks/use-chat-inline-token-actions.ts +8 -1
  201. package/src/features/chat/features/workspace/components/__tests__/chat-session-workspace-panel-content.test.tsx +155 -18
  202. package/src/features/chat/features/workspace/components/__tests__/chat-session-workspace-text-selection.test.tsx +12 -8
  203. package/src/features/chat/features/workspace/components/chat-session-workspace-directory-browser.tsx +80 -62
  204. package/src/features/chat/features/workspace/components/chat-session-workspace-panel-content.tsx +9 -2
  205. package/src/features/chat/features/workspace/components/project-files/workspace-directory-tree.tsx +83 -13
  206. package/src/features/chat/features/workspace/components/project-files/workspace-project-tree-view.tsx +93 -0
  207. package/src/features/chat/features/workspace/hooks/use-workspace-explorer-layout.ts +8 -11
  208. package/src/features/chat/features/workspace/hooks/use-workspace-file-actions.ts +68 -46
  209. package/src/features/chat/features/workspace/stores/__tests__/workspace-project-tree.store.test.ts +38 -0
  210. package/src/features/chat/features/workspace/stores/workspace-project-tree.store.ts +160 -0
  211. package/src/features/chat/index.ts +2 -1
  212. package/src/features/chat/managers/__tests__/chat-composer-intent.manager.test.ts +29 -0
  213. package/src/features/chat/managers/chat-composer-intent.manager.ts +29 -2
  214. package/src/features/chat/managers/chat-draft-intent.manager.ts +18 -2
  215. package/src/features/chat/presenters/chat.presenter.ts +2 -2
  216. package/src/features/chat/stores/__tests__/chat-thread.store.test.ts +6 -0
  217. package/src/features/chat/stores/chat-thread.store.ts +6 -0
  218. package/src/features/inbox/components/inbox-reader-dialog.test.tsx +38 -5
  219. package/src/features/inbox/components/inbox-reader-dialog.tsx +5 -3
  220. package/src/features/inbox/managers/inbox.manager.test.ts +28 -3
  221. package/src/features/inbox/managers/inbox.manager.ts +14 -4
  222. package/src/features/inbox/pages/inbox-page.test.ts +121 -2
  223. package/src/features/inbox/pages/inbox-page.tsx +5 -3
  224. package/src/features/marketplace/components/__tests__/marketplace-page.test.tsx +85 -1
  225. package/src/features/marketplace/hooks/__tests__/use-marketplace.test.tsx +44 -1
  226. package/src/features/marketplace/hooks/use-marketplace-item-actions.ts +23 -2
  227. package/src/features/marketplace/hooks/use-marketplace.ts +10 -0
  228. package/src/features/marketplace/index.ts +4 -0
  229. package/src/features/panel-apps/components/__tests__/panel-apps-list.test.tsx +1 -1
  230. package/src/features/panel-apps/components/panel-apps-list.tsx +1 -6
  231. package/src/features/panel-apps/index.ts +5 -1
  232. package/src/features/panel-apps/utils/__tests__/panel-app-doc-browser.utils.test.ts +13 -6
  233. package/src/features/panel-apps/utils/panel-app-doc-browser.utils.tsx +6 -3
  234. package/src/features/right-panel-resources/index.ts +1 -0
  235. package/src/features/right-panel-resources/utils/__tests__/right-panel-resource-uri.utils.test.ts +34 -0
  236. package/src/features/right-panel-resources/utils/right-panel-resource-uri.utils.ts +36 -4
  237. package/src/features/service-apps/components/service-apps-panel.tsx +3 -17
  238. package/src/features/system-status/components/__tests__/desktop-update-config.test.tsx +20 -1
  239. package/src/features/system-status/components/desktop-update-config.tsx +14 -13
  240. package/src/features/system-status/managers/__tests__/runtime-update.manager.test.ts +99 -0
  241. package/src/features/system-status/managers/runtime-update.manager.ts +22 -0
  242. package/src/features/system-status/stores/runtime-update.store.ts +1 -0
  243. package/src/platforms/desktop/components/desktop-app-shell.tsx +5 -0
  244. package/src/platforms/mobile/components/mobile-app-shell.tsx +9 -1
  245. package/src/shared/components/actions/__tests__/navigation-link.test.tsx +12 -0
  246. package/src/shared/components/actions/navigation-link.tsx +6 -3
  247. package/src/shared/components/doc-browser/__tests__/doc-browser.test.tsx +34 -0
  248. package/src/shared/components/doc-browser/doc-browser-tab-strip.tsx +5 -0
  249. package/src/shared/components/doc-browser/doc-browser.tsx +11 -1
  250. package/src/shared/components/doc-browser/managers/doc-browser.manager.ts +13 -0
  251. package/src/shared/components/ui/actions/__tests__/icon-action-button.test.tsx +15 -0
  252. package/src/shared/components/ui/actions/icon-action-button.tsx +4 -2
  253. package/src/shared/components/ui/tabs.tsx +24 -1
  254. package/src/shared/hooks/use-server-path-watch.ts +42 -0
  255. package/src/shared/hooks/use-system-object-references.ts +22 -0
  256. package/src/shared/lib/api/server-path/server-path.types.ts +10 -0
  257. package/src/shared/lib/api/types.ts +13 -0
  258. package/src/shared/lib/api/utils/server-path.utils.ts +10 -0
  259. package/src/shared/lib/i18n/locales/en-US/chat.json +17 -9
  260. package/src/shared/lib/i18n/locales/en-US/desktop-update.json +7 -4
  261. package/src/shared/lib/i18n/locales/en-US/doc-browser.json +82 -0
  262. package/src/shared/lib/i18n/locales/en-US/marketplace.json +3 -0
  263. package/src/shared/lib/i18n/locales/zh-CN/chat.json +19 -11
  264. package/src/shared/lib/i18n/locales/zh-CN/desktop-update.json +7 -4
  265. package/src/shared/lib/i18n/locales/zh-CN/doc-browser.json +82 -0
  266. package/src/shared/lib/i18n/locales/zh-CN/marketplace.json +3 -0
  267. package/vite.config.ts +32 -1
  268. package/dist/assets/appearance-settings-page-BYbvl2wj.js +0 -1
  269. package/dist/assets/architecture-TIHT7OUA-Bjp9jf0h.js +0 -1
  270. package/dist/assets/channel-5T3pdR-s.js +0 -1
  271. package/dist/assets/chat-page-C5E-LLVd.js +0 -1
  272. package/dist/assets/chunk-2Q5K7J3B-DTZ3tGfE.js +0 -1
  273. package/dist/assets/chunk-VAUOI2AC-HLGIHQ6k.js +0 -1
  274. package/dist/assets/chunk-XXDRQBXY-Dl0m6Ni-.js +0 -1
  275. package/dist/assets/classDiagram-OUVF2IWQ-DcDnzGNX.js +0 -1
  276. package/dist/assets/classDiagram-v2-EOCWNBFH-BKXKMzKM.js +0 -1
  277. package/dist/assets/config-split-page-CA_a0AJ5.js +0 -1
  278. package/dist/assets/confirm-dialog-CxZfenjK.js +0 -5
  279. package/dist/assets/cynefin-VYW2F7L2-BWg7U2UD.js +0 -1
  280. package/dist/assets/desktop-update-config-BEKC8Ly9.js +0 -1
  281. package/dist/assets/doc-browser-Cl2ldE3f.js +0 -1
  282. package/dist/assets/doc-browser-DXCSd63E.js +0 -1
  283. package/dist/assets/doc-browser-context-FDPs3gJf.js +0 -1
  284. package/dist/assets/eventmodeling-45OFAUF4-SB-J4tpq.js +0 -1
  285. package/dist/assets/flowDiagram-23GEKE2U-BQ_u-eLM.js +0 -1
  286. package/dist/assets/gitGraph-TEB2WS4Q-DPxNwsFd.js +0 -1
  287. package/dist/assets/i18n-provider-DkITJNz8.js +0 -1
  288. package/dist/assets/index-CQ2LcI2p.css +0 -1
  289. package/dist/assets/index-CrdfvBVg.js +0 -107
  290. package/dist/assets/info-DKCQHKI2-DNlNGH90.js +0 -1
  291. package/dist/assets/loader-circle-DHtUxCIA.js +0 -1
  292. package/dist/assets/mcp-marketplace-page-Bc_jI5iH.js +0 -9
  293. package/dist/assets/mcp-marketplace-page-CzCieD9r.js +0 -1
  294. package/dist/assets/model-config-page-C4jw-pcj.js +0 -1
  295. package/dist/assets/packet-7NZHBO7P-BpeiqkBZ.js +0 -1
  296. package/dist/assets/pie-RZYD4A2V-Cy1yxsPd.js +0 -1
  297. package/dist/assets/plus-CG1RwZQq.js +0 -1
  298. package/dist/assets/provider-scoped-model-input-CmH4BFvc.js +0 -1
  299. package/dist/assets/providers-config-page-CJQjwzaw.js +0 -1
  300. package/dist/assets/radar-I7S5WNFK-CEcPKCj8.js +0 -1
  301. package/dist/assets/railroad-3IZDKUUU-CW38eNDs.js +0 -1
  302. package/dist/assets/railroad-abnf-AHOZXSZD-ByrylcXf.js +0 -1
  303. package/dist/assets/railroad-ebnf-EBAXGLYW-CNB6GwP-.js +0 -1
  304. package/dist/assets/railroad-peg-LSFZ7HO6-x6hnqlBG.js +0 -1
  305. package/dist/assets/react-DjFBVA0n.js +0 -8
  306. package/dist/assets/remote-BIlpD3yL.js +0 -1
  307. package/dist/assets/rotate-cw-bKG8zH8M.js +0 -1
  308. package/dist/assets/runtime-config-page-B_x2Glz5.js +0 -1
  309. package/dist/assets/search-config-page-BaIijtmO.js +0 -1
  310. package/dist/assets/secrets-config-page-CJjtdFAl.js +0 -3
  311. package/dist/assets/security-config-DB8apj09.js +0 -1
  312. package/dist/assets/skeleton-Bt6zxcAB.js +0 -1
  313. package/dist/assets/stateDiagram-v2-6OUMAXLB-ZC6dFORv.js +0 -1
  314. package/dist/assets/swimlanesDiagram-G3AALYLV-CRkh51Im.js +0 -8
  315. package/dist/assets/treeView-QDETBFTQ-Bw0DSCH7.js +0 -1
  316. package/dist/assets/treemap-6X3UGDF4-DFWBFcfh.js +0 -1
  317. package/dist/assets/wardley-OPB4EBWU-CoWturk0.js +0 -1
  318. package/dist/assets/x-CowtCu9Z.js +0 -1
  319. /package/dist/assets/{array-NADKd9aU.js → array-BQVzeGRt.js} +0 -0
  320. /package/dist/assets/{chunk-KEIR6QF5-BuyuzJJL.js → chunk-KEIR6QF5-f9psyCXK.js} +0 -0
  321. /package/dist/assets/{chunk-Y2CYZVJY-D_B2ZRNt.js → chunk-Y2CYZVJY-D1mAU_Po.js} +0 -0
  322. /package/dist/assets/{config-hints-BcfVUMij.js → config-hints-nV5HPOvq.js} +0 -0
  323. /package/dist/assets/{cytoscape.esm-BANTlgza.js → cytoscape.esm-0uDtdD0l.js} +0 -0
  324. /package/dist/assets/{defaultLocale-C4_L0aEs.js → defaultLocale-DSWXGgt0.js} +0 -0
  325. /package/dist/assets/{dist-DAPEIJbz.js → dist--ta5rX12.js} +0 -0
  326. /package/dist/assets/{graphlib-ChqXg0dI.js → graphlib-CQ6QV4yP.js} +0 -0
  327. /package/dist/assets/{host-capabilities-D9C4CBK5.js → host-capabilities-Tk038i04.js} +0 -0
  328. /package/dist/assets/{init-D8wZg9xq.js → init-Y6h9jGtZ.js} +0 -0
  329. /package/dist/assets/{jszip.min-CkbBfwAQ.js → jszip.min-8hyJpgpx.js} +0 -0
  330. /package/dist/assets/{katex-TZJFa0J_.js → katex-ARhY9PS1.js} +0 -0
  331. /package/dist/assets/{middleware-B3k-TSmZ.js → middleware-DXsbf9xI.js} +0 -0
  332. /package/dist/assets/{path-DodfxcX7.js → path-10BjdyLh.js} +0 -0
  333. /package/dist/assets/{rough.esm-CUaa1Rbe.js → rough.esm-DxgC_14r.js} +0 -0
  334. /package/dist/assets/{xlsx-CxByi7bR.js → xlsx-DrDuDNAa.js} +0 -0
@@ -5,6 +5,7 @@ import { ChatSessionWorkspacePanelContent } from '@/features/chat/features/works
5
5
  import type { ResolvedChildSessionTab } from '@/features/chat/features/ncp/hooks/use-ncp-child-session-tabs-view';
6
6
  import { useChatThreadStore } from '@/features/chat/stores/chat-thread.store';
7
7
  import { CHAT_WORKSPACE_EXPLORER_DEFAULT_WIDTH } from '@/features/chat/features/workspace/utils/chat-workspace-panel-layout.utils';
8
+ import { useWorkspaceProjectTreeStore } from '@/features/chat/features/workspace/stores/workspace-project-tree.store';
8
9
 
9
10
  const mocks = vi.hoisted(() => ({
10
11
  browseEntries: [
@@ -27,7 +28,9 @@ const mocks = vi.hoisted(() => ({
27
28
  deleteServerPathEntry: vi.fn(),
28
29
  renameServerPathEntry: vi.fn(),
29
30
  uploadServerPathFiles: vi.fn(),
31
+ watchDirectories: vi.fn(),
30
32
  refetchBrowse: vi.fn(),
33
+ refetchQueries: vi.fn(),
31
34
  requestDirectoryReference: vi.fn(),
32
35
  requestFileReference: vi.fn(),
33
36
  openChildSessions: vi.fn(),
@@ -41,6 +44,15 @@ const mocks = vi.hoisted(() => ({
41
44
  scrollIntoView: vi.fn(),
42
45
  }));
43
46
 
47
+ vi.mock('@/shared/hooks/use-server-path-watch', () => ({
48
+ useServerPathWatch: mocks.watchDirectories,
49
+ }));
50
+
51
+ vi.mock('@tanstack/react-query', async (importOriginal) => ({
52
+ ...(await importOriginal()),
53
+ useQueryClient: () => ({ refetchQueries: mocks.refetchQueries }),
54
+ }));
55
+
44
56
  function firePointerEvent(
45
57
  target: Window | Document | Node | Element,
46
58
  type: string,
@@ -168,7 +180,10 @@ beforeEach(() => {
168
180
  overwritten: false,
169
181
  });
170
182
  mocks.refetchBrowse.mockResolvedValue(undefined);
183
+ mocks.refetchQueries.mockResolvedValue(undefined);
184
+ useWorkspaceProjectTreeStore.setState({ trees: {} });
171
185
  useChatThreadStore.getState().setSnapshot({
186
+ workspaceExplorerOpen: false,
172
187
  workspaceExplorerWidth: CHAT_WORKSPACE_EXPLORER_DEFAULT_WIDTH,
173
188
  });
174
189
  });
@@ -474,7 +489,7 @@ it('creates a project folder from the root toolbar and refreshes the tree', asyn
474
489
  parentPath: '/Users/peiwang/Projects/nextbot',
475
490
  name: 'research',
476
491
  });
477
- expect(mocks.refetchBrowse).toHaveBeenCalled();
492
+ expect(mocks.refetchQueries).toHaveBeenCalled();
478
493
  expect(document.activeElement).toBe(screen.getByRole('button', { name: 'research' }));
479
494
  expect(mocks.scrollIntoView).toHaveBeenCalledTimes(2);
480
495
  });
@@ -563,7 +578,7 @@ it('uploads multiple project files from the root actions menu', async () => {
563
578
  files: [file],
564
579
  overwrite: undefined,
565
580
  });
566
- expect(mocks.refetchBrowse).toHaveBeenCalled();
581
+ expect(mocks.refetchQueries).toHaveBeenCalled();
567
582
  });
568
583
  });
569
584
 
@@ -593,11 +608,83 @@ it('requires destructive confirmation before deleting a project file', async ()
593
608
  path: '/private/resolved-worktree/package.json',
594
609
  });
595
610
  expect(mocks.removeWorkspacePath).toHaveBeenCalledWith('/private/resolved-worktree/package.json');
596
- expect(mocks.refetchBrowse).toHaveBeenCalled();
611
+ expect(mocks.refetchQueries).toHaveBeenCalled();
597
612
  });
598
613
  });
599
614
 
600
- it('preserves project tree expansion and scroll while visiting another workspace tab', async () => {
615
+ it('refreshes the root and every active expanded directory query', async () => {
616
+ const user = userEvent.setup();
617
+ render(
618
+ <ChatSessionWorkspacePanelContent
619
+ activeSelection={{ kind: 'project-files' }}
620
+ childSessionTabs={[]}
621
+ filePreviewRefreshVersion={0}
622
+ sessionKey="parent-1"
623
+ sessionCronJobs={[]}
624
+ sessionProjectRoot="/Users/peiwang/Projects/nextbot"
625
+ sessionWorkingDir="/Users/peiwang/Projects/nextbot"
626
+ />,
627
+ );
628
+
629
+ await user.click(screen.getByRole('button', { name: 'src' }));
630
+ await user.click(screen.getByRole('button', { name: 'Refresh project files' }));
631
+
632
+ expect(mocks.refetchQueries).toHaveBeenCalledTimes(1);
633
+ const request = mocks.refetchQueries.mock.calls[0]?.[0] as {
634
+ predicate: (query: { queryKey: readonly unknown[] }) => boolean;
635
+ type: string;
636
+ };
637
+ expect(request.type).toBe('active');
638
+ expect(request.predicate({ queryKey: ['server-path-browse', '/Users/peiwang/Projects/nextbot', '', true] })).toBe(true);
639
+ expect(request.predicate({ queryKey: ['server-path-browse', '/Users/peiwang/Projects/nextbot/src', '', true] })).toBe(true);
640
+ expect(request.predicate({ queryKey: ['server-path-browse', '/tmp/another-project', '', true] })).toBe(false);
641
+ });
642
+
643
+ it('collapses all persisted directories in one action', async () => {
644
+ const user = userEvent.setup();
645
+ render(
646
+ <ChatSessionWorkspacePanelContent
647
+ activeSelection={{ kind: 'project-files' }}
648
+ childSessionTabs={[]}
649
+ filePreviewRefreshVersion={0}
650
+ sessionKey="parent-1"
651
+ sessionCronJobs={[]}
652
+ sessionProjectRoot="/Users/peiwang/Projects/nextbot"
653
+ sessionWorkingDir="/Users/peiwang/Projects/nextbot"
654
+ />,
655
+ );
656
+
657
+ const sourceDirectory = screen.getByRole('treeitem', { name: 'Open directory: src' });
658
+ await user.click(screen.getByRole('button', { name: 'src' }));
659
+ expect(sourceDirectory.getAttribute('aria-expanded')).toBe('true');
660
+ await user.click(screen.getByRole('button', { name: 'Collapse all' }));
661
+ expect(sourceDirectory.getAttribute('aria-expanded')).toBe('false');
662
+ });
663
+
664
+ it('restores expansion and scroll from project view state after remounting', async () => {
665
+ const user = userEvent.setup();
666
+ const props = {
667
+ activeSelection: { kind: 'project-files' as const },
668
+ childSessionTabs: [],
669
+ filePreviewRefreshVersion: 0,
670
+ sessionKey: 'parent-1',
671
+ sessionCronJobs: [],
672
+ sessionProjectRoot: '/Users/peiwang/Projects/nextbot',
673
+ sessionWorkingDir: '/Users/peiwang/Projects/nextbot',
674
+ };
675
+ const first = render(<ChatSessionWorkspacePanelContent {...props} />);
676
+ await user.click(screen.getByRole('button', { name: 'src' }));
677
+ const firstTree = screen.getByRole('tree', { name: 'Project files' });
678
+ firstTree.scrollTop = 144;
679
+ fireEvent.scroll(firstTree);
680
+ first.unmount();
681
+
682
+ render(<ChatSessionWorkspacePanelContent {...props} />);
683
+ expect(screen.getAllByRole('treeitem', { name: 'Open directory: src' })[0]?.getAttribute('aria-expanded')).toBe('true');
684
+ expect(screen.getByRole('tree', { name: 'Project files' }).scrollTop).toBe(144);
685
+ });
686
+
687
+ it('selects and reveals the active file when it is opened outside the Explorer', async () => {
601
688
  const user = userEvent.setup();
602
689
  const projectFilesProps = {
603
690
  activeSelection: { kind: 'project-files' as const },
@@ -609,24 +696,69 @@ it('preserves project tree expansion and scroll while visiting another workspace
609
696
  sessionWorkingDir: '/Users/peiwang/Projects/nextbot',
610
697
  };
611
698
  const { rerender } = render(<ChatSessionWorkspacePanelContent {...projectFilesProps} />);
612
- const tree = screen.getByRole('tree', { name: 'Project files' });
613
- const sourceDirectory = screen.getByRole('treeitem', {
614
- name: 'Open directory: src',
615
- });
699
+ const sourceDirectory = screen.getByRole('treeitem', { name: 'Open directory: src' });
700
+ const packageEntry = screen.getByRole('treeitem', { name: 'Open file: package.json' });
616
701
  await user.click(screen.getByRole('button', { name: 'src' }));
617
- tree.scrollTop = 128;
618
- expect(sourceDirectory.getAttribute('aria-expanded')).toBe('true');
702
+ expect(sourceDirectory.getAttribute('aria-selected')).toBe('true');
619
703
 
620
- rerender(<ChatSessionWorkspacePanelContent {...projectFilesProps} activeSelection={{ kind: 'overview' }} />);
621
- rerender(<ChatSessionWorkspacePanelContent {...projectFilesProps} />);
704
+ rerender(
705
+ <ChatSessionWorkspacePanelContent
706
+ {...projectFilesProps}
707
+ activeSelection={{
708
+ kind: 'file',
709
+ file: {
710
+ key: 'parent-1::preview::package.json',
711
+ parentSessionKey: 'parent-1',
712
+ path: 'package.json',
713
+ label: 'package.json',
714
+ viewMode: 'preview',
715
+ previewViewer: 'source',
716
+ },
717
+ }}
718
+ />,
719
+ );
622
720
 
623
- expect(screen.getByRole('tree', { name: 'Project files' })).toBe(tree);
624
- expect(sourceDirectory.isConnected).toBe(true);
625
- expect(sourceDirectory.getAttribute('aria-expanded')).toBe('true');
626
- expect(tree.scrollTop).toBe(128);
721
+ await waitFor(() => {
722
+ expect(packageEntry.getAttribute('aria-selected')).toBe('true');
723
+ expect(sourceDirectory.getAttribute('aria-selected')).toBe('false');
724
+ });
725
+ expect(mocks.scrollIntoView).toHaveBeenCalled();
726
+ expect(document.activeElement).not.toBe(screen.getByRole('button', { name: 'package.json' }));
627
727
  });
628
728
 
629
- it('reuses the Explorer beside a file and persists a dragged compact width', () => {
729
+ it('expands active-file ancestors like an auto-revealing Explorer', async () => {
730
+ useChatThreadStore.getState().setSnapshot({ workspaceExplorerOpen: true });
731
+ render(
732
+ <ChatSessionWorkspacePanelContent
733
+ activeSelection={{
734
+ kind: 'file',
735
+ file: {
736
+ key: 'parent-1::preview::src/index.ts',
737
+ parentSessionKey: 'parent-1',
738
+ path: '/Users/peiwang/Projects/nextbot/src/index.ts',
739
+ label: 'index.ts',
740
+ viewMode: 'preview',
741
+ previewViewer: 'source',
742
+ },
743
+ }}
744
+ childSessionTabs={[]}
745
+ filePreviewRefreshVersion={0}
746
+ sessionKey="parent-1"
747
+ sessionCronJobs={[]}
748
+ sessionProjectRoot="/Users/peiwang/Projects/nextbot"
749
+ sessionWorkingDir="/Users/peiwang/Projects/nextbot"
750
+ />,
751
+ );
752
+
753
+ const sourceDirectory = within(screen.getByTestId('workspace-shared-explorer')).getAllByRole('treeitem', {
754
+ name: 'Open directory: src',
755
+ })[0];
756
+ await waitFor(() => {
757
+ expect(sourceDirectory.getAttribute('aria-expanded')).toBe('true');
758
+ });
759
+ });
760
+
761
+ it('keeps the file Explorer closed by default and reuses it after an explicit open', () => {
630
762
  const projectFilesProps = {
631
763
  activeSelection: { kind: 'project-files' as const },
632
764
  childSessionTabs: [],
@@ -659,6 +791,9 @@ it('reuses the Explorer beside a file and persists a dragged compact width', ()
659
791
  const explorer = screen.getByTestId('workspace-shared-explorer');
660
792
  const breadcrumbs = screen.getByTestId('workspace-file-breadcrumbs');
661
793
  const toggle = screen.getByTestId('workspace-explorer-toggle');
794
+ expect(explorer.getAttribute('aria-hidden')).toBe('true');
795
+ fireEvent.click(toggle);
796
+ expect(useChatThreadStore.getState().snapshot.workspaceExplorerOpen).toBe(true);
662
797
  expect(screen.getByRole('tree', { name: 'Project files' })).toBe(tree);
663
798
  expect(explorer.getAttribute('data-mode')).toBe('side');
664
799
  expect(explorer.style.width).toBe('224px');
@@ -674,7 +809,7 @@ it('reuses the Explorer beside a file and persists a dragged compact width', ()
674
809
  expect(mocks.setWorkspaceExplorerWidth).toHaveBeenCalledWith(300);
675
810
  });
676
811
 
677
- it('uses an overlay only below the compact threshold and closes it without unmounting', () => {
812
+ it('uses an overlay only below the compact threshold and persists closing it without unmounting', () => {
678
813
  HTMLElement.prototype.getBoundingClientRect = () => ({
679
814
  bottom: 600,
680
815
  height: 600,
@@ -686,6 +821,7 @@ it('uses an overlay only below the compact threshold and closes it without unmou
686
821
  y: 0,
687
822
  toJSON: () => ({}),
688
823
  });
824
+ useChatThreadStore.getState().setSnapshot({ workspaceExplorerOpen: true });
689
825
  render(
690
826
  <ChatSessionWorkspacePanelContent
691
827
  activeSelection={{
@@ -714,6 +850,7 @@ it('uses an overlay only below the compact threshold and closes it without unmou
714
850
  expect(screen.queryByTestId('workspace-explorer-resize-handle')).toBeNull();
715
851
 
716
852
  fireEvent.click(screen.getByTestId('workspace-explorer-scrim'));
853
+ expect(useChatThreadStore.getState().snapshot.workspaceExplorerOpen).toBe(false);
717
854
  expect(explorer.getAttribute('aria-hidden')).toBe('true');
718
855
  expect(tree.isConnected).toBe(true);
719
856
  });
@@ -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();
@@ -1,4 +1,5 @@
1
- import { useState, type MouseEventHandler, type ReactNode } from 'react';
1
+ import { useMemo, type ReactNode } from 'react';
2
+ import { useQueryClient } from '@tanstack/react-query';
2
3
  import type { ChatFileOpenActionViewModel } from '@nextclaw/agent-chat-ui';
3
4
  import { ChevronsUp, Copy, FilePlus2, FolderPlus, LocateFixed, Upload, RefreshCw } from 'lucide-react';
4
5
  import { toast } from 'sonner';
@@ -10,6 +11,10 @@ import {
10
11
  } from '@/features/chat/features/workspace/components/project-files/workspace-directory-tree';
11
12
  import { WorkspaceNewFolderTreeItem } from '@/features/chat/features/workspace/components/project-files/workspace-new-folder-tree-item';
12
13
  import { WorkspaceProjectFilesToolbar } from '@/features/chat/features/workspace/components/project-files/workspace-project-files-toolbar';
14
+ import {
15
+ isWorkspaceProjectBrowseQuery,
16
+ WorkspaceProjectTree,
17
+ } from '@/features/chat/features/workspace/components/project-files/workspace-project-tree-view';
13
18
  import {
14
19
  useWorkspaceProjectFilesController,
15
20
  type RefreshDirectory,
@@ -18,12 +23,18 @@ import {
18
23
  } from '@/features/chat/features/workspace/hooks/use-workspace-file-actions';
19
24
  import { ContextMenu, type ContextMenuGroup } from '@/shared/components/ui/context-menu/context-menu';
20
25
  import { useConfirmDialog } from '@/shared/hooks/use-confirm-dialog';
26
+ import { useServerPathWatch } from '@/shared/hooks/use-server-path-watch';
21
27
  import type { useServerPathBrowse } from '@/shared/hooks/use-server-path-browse';
22
28
  import type { ServerPathEntryView } from '@/shared/lib/api';
23
29
  import { hostCapabilityManager } from '@/shared/lib/host-capabilities';
24
30
  import { t } from '@/shared/lib/i18n';
31
+ import {
32
+ normalizeWorkspaceProjectTreeRootKey,
33
+ useWorkspaceProjectTreeStore,
34
+ } from '@/features/chat/features/workspace/stores/workspace-project-tree.store';
25
35
 
26
36
  type ChatSessionWorkspaceDirectoryBrowserProps = {
37
+ activeRelativePath?: string | null;
27
38
  browseQuery: ReturnType<typeof useServerPathBrowse>;
28
39
  onFileOpen: (action: ChatFileOpenActionViewModel) => void;
29
40
  sessionKey?: string | null;
@@ -31,13 +42,15 @@ type ChatSessionWorkspaceDirectoryBrowserProps = {
31
42
  showRoot?: boolean;
32
43
  };
33
44
 
45
+ const EMPTY_EXPANDED_PATHS: readonly string[] = [];
46
+
34
47
  function readPathName(path: string): string {
35
48
  return path.split(/[\\/]/).filter(Boolean).pop() ?? path;
36
49
  }
37
50
 
38
51
  function WorkspaceDirectoryEntries({
52
+ activeRelativePath,
39
53
  busy,
40
- collapseVersion,
41
54
  createTarget,
42
55
  entries,
43
56
  fallbackParentTarget,
@@ -58,9 +71,10 @@ function WorkspaceDirectoryEntries({
58
71
  revealPath,
59
72
  selectedPath,
60
73
  showRoot,
74
+ treeRootKey,
61
75
  }: {
76
+ activeRelativePath: string | null;
62
77
  busy: boolean;
63
- collapseVersion: number;
64
78
  createTarget: WorkspaceDirectoryActionTarget | null;
65
79
  entries: readonly ServerPathEntryView[];
66
80
  fallbackParentTarget: WorkspaceDirectoryActionTarget;
@@ -81,16 +95,17 @@ function WorkspaceDirectoryEntries({
81
95
  revealPath: string | null;
82
96
  selectedPath: string | null;
83
97
  showRoot: boolean;
98
+ treeRootKey: string | null;
84
99
  }) {
85
100
  if (entries.length === 0) {
86
101
  return <div className="px-4 py-8 text-center text-xs text-gray-400">{t('chatWorkspaceDirectoryEmpty')}</div>;
87
102
  }
88
103
  return entries.map((entry) => (
89
104
  <WorkspaceDirectoryTreeEntry
105
+ activeRelativePath={activeRelativePath}
90
106
  key={entry.path}
91
107
  browseParentRefresh={refresh}
92
108
  busy={busy}
93
- collapseVersion={collapseVersion}
94
109
  createTarget={createTarget}
95
110
  entry={entry}
96
111
  level={showRoot ? 1 : 0}
@@ -109,6 +124,7 @@ function WorkspaceDirectoryEntries({
109
124
  renderCreateFolder={renderCreateFolder}
110
125
  revealPath={revealPath}
111
126
  selectedPath={selectedPath}
127
+ treeRootKey={treeRootKey}
112
128
  />
113
129
  ));
114
130
  }
@@ -203,46 +219,8 @@ function buildRootContextMenuGroups({
203
219
  ];
204
220
  }
205
221
 
206
- function WorkspaceProjectTree({
207
- children,
208
- currentPath,
209
- onBlankContext,
210
- onContextMenu,
211
- rootDraft,
212
- showRoot,
213
- }: {
214
- children: ReactNode;
215
- currentPath: string | null;
216
- onBlankContext: () => void;
217
- onContextMenu?: MouseEventHandler<HTMLDivElement>;
218
- rootDraft: ReactNode;
219
- showRoot: boolean;
220
- }) {
221
- return (
222
- <div
223
- data-testid="workspace-directory-browser"
224
- role="tree"
225
- tabIndex={-1}
226
- aria-label={t('chatWorkspaceProjectFiles')}
227
- className="min-h-0 flex-1 overflow-auto py-1 custom-scrollbar"
228
- onContextMenu={(event) => {
229
- if (!(event.target as HTMLElement).closest('[data-workspace-tree-entry]')) onBlankContext();
230
- onContextMenu?.(event);
231
- }}
232
- >
233
- {showRoot && currentPath ? (
234
- <>
235
- {rootDraft}
236
- {children}
237
- </>
238
- ) : (
239
- children
240
- )}
241
- </div>
242
- );
243
- }
244
-
245
222
  function WorkspaceDirectoryBrowserReady({
223
+ activeRelativePath,
246
224
  browseQuery,
247
225
  ConfirmDialog,
248
226
  controller,
@@ -254,6 +232,7 @@ function WorkspaceDirectoryBrowserReady({
254
232
  rootPath,
255
233
  showRoot,
256
234
  }: {
235
+ activeRelativePath: string | null;
257
236
  browseQuery: ReturnType<typeof useServerPathBrowse>;
258
237
  ConfirmDialog: () => ReactNode;
259
238
  controller: ReturnType<typeof useWorkspaceProjectFilesController>;
@@ -265,33 +244,43 @@ function WorkspaceDirectoryBrowserReady({
265
244
  rootPath: string | null;
266
245
  showRoot: boolean;
267
246
  }) {
247
+ const queryClient = useQueryClient();
268
248
  const {
269
- actions,
270
249
  cancelCreateFolder,
271
250
  clearSelection,
272
251
  completeReveal,
273
252
  submitCreate,
274
- createError,
275
- createKind,
276
- createName,
277
- createTarget,
278
253
  deleteEntry,
279
- fileInputRef,
280
254
  openCreateFile,
281
255
  openCreateFolder,
282
256
  requestUpload,
283
257
  renameEntry,
284
- revealPath,
285
258
  selectEntry,
286
- selectedCreateTarget,
287
- selectedPath,
288
259
  setCreateError,
289
260
  setCreateName,
290
261
  uploadFiles,
291
- } = controller;
292
- const busy = actions.pendingAction !== null;
293
- const [collapseVersion, setCollapseVersion] = useState(0);
294
- const rootRefresh = () => browseQuery.refetch();
262
+ } = controller.actions;
263
+ const { fileInputRef } = controller.refs;
264
+ const {
265
+ createError,
266
+ createKind,
267
+ createName,
268
+ createTarget,
269
+ pendingAction,
270
+ revealPath,
271
+ selectedCreateTarget,
272
+ selectedPath,
273
+ } = controller.state;
274
+ const busy = pendingAction !== null;
275
+ const treeRootKey = normalizeWorkspaceProjectTreeRootKey(rootPath);
276
+ const collapseAll = useWorkspaceProjectTreeStore((state) => state.collapseAll);
277
+ const rootRefresh = () =>
278
+ rootPath
279
+ ? queryClient.refetchQueries({
280
+ predicate: (query) => isWorkspaceProjectBrowseQuery(query.queryKey, rootPath),
281
+ type: 'active',
282
+ })
283
+ : browseQuery.refetch();
295
284
  const rootTarget: WorkspaceDirectoryActionTarget | null = rootPath
296
285
  ? { label: rootLabel, path: rootPath, refresh: rootRefresh }
297
286
  : null;
@@ -300,7 +289,7 @@ function WorkspaceDirectoryBrowserReady({
300
289
  <WorkspaceNewFolderTreeItem
301
290
  error={createError}
302
291
  kind={createKind}
303
- isCreating={actions.pendingAction === 'create-directory' || actions.pendingAction === 'create-file'}
292
+ isCreating={pendingAction === 'create-directory' || pendingAction === 'create-file'}
304
293
  level={level}
305
294
  name={createName}
306
295
  onCancel={cancelCreateFolder}
@@ -318,8 +307,8 @@ function WorkspaceDirectoryBrowserReady({
318
307
  };
319
308
  const treeEntries = (
320
309
  <WorkspaceDirectoryEntries
310
+ activeRelativePath={activeRelativePath}
321
311
  busy={busy}
322
- collapseVersion={collapseVersion}
323
312
  createTarget={createTarget}
324
313
  entries={entries}
325
314
  fallbackParentTarget={fallbackParentTarget}
@@ -340,6 +329,7 @@ function WorkspaceDirectoryBrowserReady({
340
329
  revealPath={revealPath}
341
330
  selectedPath={selectedPath}
342
331
  showRoot={false}
332
+ treeRootKey={treeRootKey || null}
343
333
  />
344
334
  );
345
335
  const tree = (
@@ -355,10 +345,12 @@ function WorkspaceDirectoryBrowserReady({
355
345
  const rootMenuGroups = rootTarget
356
346
  ? buildRootContextMenuGroups({
357
347
  busy,
358
- onCollapseAll: () => setCollapseVersion((value) => value + 1),
348
+ onCollapseAll: () => {
349
+ if (treeRootKey) collapseAll(treeRootKey);
350
+ },
359
351
  onCreateFile: () => openCreateFile(rootTarget),
360
352
  onCreateFolder: () => openCreateFolder(rootTarget),
361
- onRefresh: () => void browseQuery.refetch(),
353
+ onRefresh: () => void rootRefresh(),
362
354
  onRevealPath,
363
355
  onUpload: () => requestUpload(rootTarget),
364
356
  target: rootTarget,
@@ -376,10 +368,12 @@ function WorkspaceDirectoryBrowserReady({
376
368
  <WorkspaceProjectFilesToolbar
377
369
  disabled={busy}
378
370
  rootLabel={rootLabel}
379
- onCollapseAll={() => setCollapseVersion((value) => value + 1)}
371
+ onCollapseAll={() => {
372
+ if (treeRootKey) collapseAll(treeRootKey);
373
+ }}
380
374
  onNewFile={() => openCreateFile(toolbarTarget)}
381
375
  onNewFolder={() => openCreateFolder(toolbarTarget)}
382
- onRefresh={() => void browseQuery.refetch()}
376
+ onRefresh={() => void rootRefresh()}
383
377
  />
384
378
  ) : null}
385
379
  {rootPath ? (
@@ -406,6 +400,7 @@ function WorkspaceDirectoryBrowserReady({
406
400
  }
407
401
 
408
402
  export function ChatSessionWorkspaceDirectoryBrowser({
403
+ activeRelativePath = null,
409
404
  browseQuery,
410
405
  onFileOpen,
411
406
  sessionKey,
@@ -417,8 +412,30 @@ export function ChatSessionWorkspaceDirectoryBrowser({
417
412
  const entries = browseQuery.data?.entries ?? [];
418
413
  const errorMessage = readBrowseError(browseQuery);
419
414
  const rootPath = showRoot ? (browseQuery.data?.currentPath ?? null) : null;
415
+ const treeRootKey = normalizeWorkspaceProjectTreeRootKey(rootPath);
416
+ const expandedPaths = useWorkspaceProjectTreeStore((state) =>
417
+ treeRootKey ? (state.trees[treeRootKey]?.expandedPaths ?? EMPTY_EXPANDED_PATHS) : EMPTY_EXPANDED_PATHS,
418
+ );
419
+ const watchedDirectories = useMemo(() => {
420
+ if (!rootPath) return [];
421
+ const separator = rootPath.includes('\\') && !rootPath.includes('/') ? '\\' : '/';
422
+ const normalizedRoot = rootPath.replace(/[\\/]+$/g, '');
423
+ const expandedPathSet = new Set(expandedPaths);
424
+ const visibleExpandedPaths = expandedPaths.filter((relativePath) => {
425
+ const segments = relativePath.split('/');
426
+ return segments.slice(0, -1).every((_, index) => expandedPathSet.has(segments.slice(0, index + 1).join('/')));
427
+ });
428
+ return [
429
+ rootPath,
430
+ ...visibleExpandedPaths.map(
431
+ (relativePath) => `${normalizedRoot}${separator}${relativePath.replace(/[\\/]+/g, separator)}`,
432
+ ),
433
+ ];
434
+ }, [expandedPaths, rootPath]);
435
+ useServerPathWatch(watchedDirectories);
420
436
  const rootLabel = rootPath ? readPathName(rootPath) : '';
421
437
  const controller = useWorkspaceProjectFilesController({
438
+ activeRelativePath,
422
439
  confirm,
423
440
  onEntryDeleted: presenter.chatThreadManager.removeWorkspacePath,
424
441
  onEntryRenamed: presenter.chatThreadManager.renameWorkspacePath,
@@ -457,6 +474,7 @@ export function ChatSessionWorkspaceDirectoryBrowser({
457
474
  }
458
475
  return (
459
476
  <WorkspaceDirectoryBrowserReady
477
+ activeRelativePath={activeRelativePath}
460
478
  browseQuery={browseQuery}
461
479
  ConfirmDialog={ConfirmDialog}
462
480
  controller={controller}
@@ -24,6 +24,7 @@ import {
24
24
  } from '@/features/chat/features/workspace/utils/chat-workspace-panel-layout.utils';
25
25
  import { useWorkspaceExplorerLayout } from '@/features/chat/features/workspace/hooks/use-workspace-explorer-layout';
26
26
  import { t } from '@/shared/lib/i18n';
27
+ import { resolveWorkspaceRelativePath } from '@/shared/lib/session-project';
27
28
  import { cn } from '@/shared/lib/utils';
28
29
  import { IconActionButton } from '@/shared/components/ui/actions/icon-action-button';
29
30
 
@@ -175,11 +176,13 @@ function WorkspaceChildSessions({ childSessionTabs }: { childSessionTabs: readon
175
176
  }
176
177
 
177
178
  function WorkspaceProjectFiles({
179
+ activePath,
178
180
  onFileOpen,
179
181
  projectRoot,
180
182
  sessionKey,
181
183
  workingDir,
182
184
  }: {
185
+ activePath: string | null;
183
186
  onFileOpen?: (action: ChatFileOpenActionViewModel) => void;
184
187
  projectRoot: string | null;
185
188
  sessionKey: string | null;
@@ -203,6 +206,9 @@ function WorkspaceProjectFiles({
203
206
 
204
207
  return (
205
208
  <ChatSessionWorkspaceDirectoryBrowser
209
+ activeRelativePath={
210
+ activePath ? resolveWorkspaceRelativePath({ path: activePath, sessionProjectRoot: rootPath }) : null
211
+ }
206
212
  key={rootPath}
207
213
  browseQuery={browseQuery}
208
214
  onFileOpen={onFileOpen ?? presenter.chatThreadManager.openFilePreview}
@@ -384,7 +390,7 @@ export function ChatSessionWorkspacePanelContent({
384
390
  onResizeStart,
385
391
  setExplorerOpen,
386
392
  showExplorer,
387
- } = useWorkspaceExplorerLayout({ fileActive, projectFilesActive, sessionProjectRoot, sessionWorkingDir });
393
+ } = useWorkspaceExplorerLayout({ fileActive, projectFilesActive });
388
394
 
389
395
  return (
390
396
  <div
@@ -414,6 +420,7 @@ export function ChatSessionWorkspacePanelContent({
414
420
  aria-hidden={!showExplorer}
415
421
  >
416
422
  <WorkspaceProjectFiles
423
+ activePath={fileActive ? activeSelection.file.path : null}
417
424
  onFileOpen={(action) => {
418
425
  if (compact) setExplorerOpen(false);
419
426
  presenter.chatThreadManager.openFilePreview(action);
@@ -445,7 +452,7 @@ export function ChatSessionWorkspacePanelContent({
445
452
  fileActive
446
453
  ? {
447
454
  open: explorerOpen,
448
- onToggle: () => setExplorerOpen((value) => !value),
455
+ onToggle: () => setExplorerOpen(!explorerOpen),
449
456
  }
450
457
  : undefined
451
458
  }