@nextclaw/ui 0.15.4 → 0.15.6

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 (300) hide show
  1. package/CHANGELOG.md +41 -0
  2. package/dist/assets/abnfDiagram-VRR7QNED-BYpK0F5F.js +1 -0
  3. package/dist/assets/{aiden0z-pptx-renderer.es-Bd99vut2.js → aiden0z-pptx-renderer.es-D87ywFdC.js} +1 -1
  4. package/dist/assets/api-Dyb5rgh0.js +13 -0
  5. package/dist/assets/app-presenter-provider-7jV0nPiL.js +106 -0
  6. package/dist/assets/{appearance-settings-page-fLIJbx1C.js → appearance-settings-page-DFm3BbEb.js} +1 -1
  7. package/dist/assets/arc-DFELHWM2.js +1 -0
  8. package/dist/assets/architecture-TIHT7OUA-ClwVFq-K.js +1 -0
  9. package/dist/assets/architectureDiagram-ZJ3FMSHR-CvRhdovL.js +36 -0
  10. package/dist/assets/array-BqYdCIE6.js +1 -0
  11. package/dist/assets/blockDiagram-677ZJIJ3-DVYNkbR_.js +132 -0
  12. package/dist/assets/{book-open-DWUjS6aW.js → book-open-L6LCuqOr.js} +1 -1
  13. package/dist/assets/c4Diagram-LMCZKHZV-B1w-iwRe.js +10 -0
  14. package/dist/assets/channel-BEHOSFc5.js +1 -0
  15. package/dist/assets/{channels-list-page-CvCTJraa.js → channels-list-page-DOaPKNJA.js} +2 -2
  16. package/dist/assets/{chat-page-B42UUIFR.js → chat-page-Cw9uDhD_.js} +1 -1
  17. package/dist/assets/chunk-2Q5K7J3B-DrIhesSX.js +1 -0
  18. package/dist/assets/chunk-32BRIVSS-kG3lg0bC.js +1 -0
  19. package/dist/assets/chunk-52WLFC77-BLeB7iGk.js +10 -0
  20. package/dist/assets/chunk-5FCAYU7R-BQga1Rr7.js +1 -0
  21. package/dist/assets/chunk-5HE753X5-DU81hkcF.js +1 -0
  22. package/dist/assets/chunk-5JV3BV7I-C2bAx0m7.js +1 -0
  23. package/dist/assets/chunk-5TONJI2A-BExIIgWY.js +2 -0
  24. package/dist/assets/chunk-5VM5RSS4-C3XB05yh.js +15 -0
  25. package/dist/assets/chunk-7BUUIJ7U-JiyRtrGu.js +1 -0
  26. package/dist/assets/chunk-BIQX33UG-BZRV20pI.js +1 -0
  27. package/dist/assets/chunk-C7G6YPKG-AsyDSQ9j.js +1 -0
  28. package/dist/assets/chunk-CQNSW5MT-iN-fiXVA.js +1 -0
  29. package/dist/assets/chunk-CYSBUYHQ-DhC_TRzI.js +1 -0
  30. package/dist/assets/chunk-EMLP6XTP-QFLK8R6d.js +1 -0
  31. package/dist/assets/chunk-EX3LRPZG-DzUwhkCz.js +231 -0
  32. package/dist/assets/chunk-FWX5IMBZ-DFelpZBy.js +2 -0
  33. package/dist/assets/chunk-HOUHSVGY-BMPwFEwN.js +1 -0
  34. package/dist/assets/chunk-ICXQ74PX-DH8NJMok.js +2 -0
  35. package/dist/assets/chunk-JG7HCLWE-D-95AsO4.js +2 -0
  36. package/dist/assets/chunk-JWPE2WC7-DIaTv8kx.js +1 -0
  37. package/dist/assets/chunk-KEIR6QF5-A3jEqwzn.js +161 -0
  38. package/dist/assets/chunk-MOJQB5TN-BPFLzWgj.js +88 -0
  39. package/dist/assets/chunk-MOZMSUNE-DR_fzxG0.js +1 -0
  40. package/dist/assets/chunk-OGEWGWER-TIcEoPPH.js +1 -0
  41. package/dist/assets/chunk-OSBZ3O6U-BALNvSfG.js +1 -0
  42. package/dist/assets/chunk-PUDLZKDR-I-EXhf_k.js +156 -0
  43. package/dist/assets/chunk-Q4XR5HBZ-uvIGstBS.js +70 -0
  44. package/dist/assets/chunk-QBLGF6JB-r-TjLdxa.js +1 -0
  45. package/dist/assets/chunk-R7FJI6CG-N-1LDgvy.js +1 -0
  46. package/dist/assets/chunk-RYQCIY6F-jxNASx65.js +1 -0
  47. package/dist/assets/chunk-U6XO7XAA-Cphbuc7q.js +2 -0
  48. package/dist/assets/chunk-V7JOEXUC-BLEuBf1s.js +206 -0
  49. package/dist/assets/chunk-VAUOI2AC-Dpcz6BhT.js +1 -0
  50. package/dist/assets/chunk-VR4S4FIN-BAUbR0ke.js +1 -0
  51. package/dist/assets/chunk-WYO6CB5R-Do6SJrnY.js +127 -0
  52. package/dist/assets/chunk-XXDRQBXY-C2QjjwYX.js +1 -0
  53. package/dist/assets/chunk-Y2CYZVJY-BGrd8tLy.js +1 -0
  54. package/dist/assets/chunk-YOTPTUD7-CQqvMTCr.js +1 -0
  55. package/dist/assets/chunk-ZGVPDNZ5-B7kWWaXv.js +62 -0
  56. package/dist/assets/chunk-ZIRB5QZD-DTU0cDNh.js +32 -0
  57. package/dist/assets/classDiagram-OUVF2IWQ-DESMaY3_.js +1 -0
  58. package/dist/assets/classDiagram-v2-EOCWNBFH-DwOlByYH.js +1 -0
  59. package/dist/assets/{config-split-page-DBgt9S8b.js → config-split-page-CqlqjnCK.js} +1 -1
  60. package/dist/assets/{confirm-dialog-GSXIvoRU.js → confirm-dialog-bAp8NU93.js} +1 -1
  61. package/dist/assets/cose-bilkent-JH36ORCC-B9LrtV-q.js +1 -0
  62. package/dist/assets/cynefin-VYW2F7L2-CzjeeOq9.js +1 -0
  63. package/dist/assets/cynefinDiagram-TSTJHNR4-BfjvW4su.js +62 -0
  64. package/dist/assets/cytoscape.esm-CRW8q92n.js +321 -0
  65. package/dist/assets/dagre-DkiXZKkL.js +1 -0
  66. package/dist/assets/dagre-VKFMJZFB-B1pQKQSY.js +4 -0
  67. package/dist/assets/defaultLocale-Cdv9x6q7.js +1 -0
  68. package/dist/assets/desktop-update-config-2SjMfCMl.js +1 -0
  69. package/dist/assets/diagram-FQU43EPY-cB4EZdaP.js +3 -0
  70. package/dist/assets/diagram-G47NLZAW-CtW8im1B.js +24 -0
  71. package/dist/assets/diagram-NH7WQ7WH-BDCtrCCm.js +24 -0
  72. package/dist/assets/diagram-OA4YK3LP-DEMJ42Gt.js +30 -0
  73. package/dist/assets/diagram-WEI45ONY-CdCcxSYU.js +41 -0
  74. package/dist/assets/{dist-DC7f4TJH.js → dist-CrtbGLlh.js} +1 -1
  75. package/dist/assets/dist-dw-gAfM_.js +1 -0
  76. package/dist/assets/doc-browser-A5IRyDXQ2.js +1 -0
  77. package/dist/assets/{doc-browser-D0monHjG.js → doc-browser-BmnQa30-.js} +1 -1
  78. package/dist/assets/{doc-browser-context-CGwEOhBy.js → doc-browser-context-BlpUUFrR.js} +1 -1
  79. package/dist/assets/{docx-preview-eFH9DRsS.js → docx-preview-zis3U0Fa.js} +1 -1
  80. package/dist/assets/{download-DiP9zx-J.js → download-BhZZNXD8.js} +1 -1
  81. package/dist/assets/ebnfDiagram-CCIWWBDH-DBZETBwE.js +1 -0
  82. package/dist/assets/erDiagram-Q63AITRT-y2zDXprP.js +85 -0
  83. package/dist/assets/eventmodeling-45OFAUF4-C38BqG8w.js +1 -0
  84. package/dist/assets/{external-link-C6ZeSPef.js → external-link-B9Uoq5J7.js} +1 -1
  85. package/dist/assets/{eye-suvKmtwn.js → eye-K5knauEM.js} +1 -1
  86. package/dist/assets/flowDiagram-23GEKE2U-BoKk8RRq.js +1 -0
  87. package/dist/assets/{folder-CMELbQGG.js → folder-BIYZZWJv.js} +1 -1
  88. package/dist/assets/{form-actions-CUAzpNe_.js → form-actions-CKprWFeM.js} +1 -1
  89. package/dist/assets/ganttDiagram-NO4QXBWP-D2DP344e.js +292 -0
  90. package/dist/assets/gitGraph-TEB2WS4Q-IsQHHJuS.js +1 -0
  91. package/dist/assets/gitGraphDiagram-IHSO6WYX-D37uiwA2.js +106 -0
  92. package/dist/assets/graphlib-BcNFQr5y.js +1 -0
  93. package/dist/assets/index-BVn702m4.css +1 -0
  94. package/dist/assets/index-CFxJ1Fkg.js +2 -0
  95. package/dist/assets/info-DKCQHKI2-ofy2qlHI.js +1 -0
  96. package/dist/assets/infoDiagram-FWYZ7A6U-CG-WAIRi.js +2 -0
  97. package/dist/assets/init-D8TjIqfm.js +1 -0
  98. package/dist/assets/ishikawaDiagram-FXEZZL3T-WeCTnW0f.js +70 -0
  99. package/dist/assets/journeyDiagram-5HDEW3XC-DUFBW9Td.js +139 -0
  100. package/dist/assets/kanban-definition-HUTT4EX6-B0lxgMpg.js +89 -0
  101. package/dist/assets/katex-wEmrEFTg.js +257 -0
  102. package/dist/assets/line-B78frP_c.js +1 -0
  103. package/dist/assets/linear-C6lVEcb2.js +1 -0
  104. package/dist/assets/loader-circle-Bj2RMrCR.js +1 -0
  105. package/dist/assets/{logo-badge-Cu5UGH_4.js → logo-badge-BQ-XFZod.js} +1 -1
  106. package/dist/assets/map-Bdri5UL5.js +1 -0
  107. package/dist/assets/mcp-marketplace-page-BfA7A3n4.js +9 -0
  108. package/dist/assets/mcp-marketplace-page-CXHYO95j.js +1 -0
  109. package/dist/assets/mermaid-parser.core-Cue18vLo.js +4 -0
  110. package/dist/assets/mermaid.core-CXwWRh5i.js +11 -0
  111. package/dist/assets/mindmap-definition-LN4V7U3C-CFxz0P0-.js +96 -0
  112. package/dist/assets/{model-config-page-CWP6pyBm.js → model-config-page-BbWbW_qM.js} +1 -1
  113. package/dist/assets/ordinal-Cbd8t4LB.js +1 -0
  114. package/dist/assets/packet-7NZHBO7P-Rfopfa1o.js +1 -0
  115. package/dist/assets/path-DzBnMI1r.js +1 -0
  116. package/dist/assets/pegDiagram-2B236MQR-B_Qt9zyT.js +1 -0
  117. package/dist/assets/pie-RZYD4A2V-CJR1xKEv.js +1 -0
  118. package/dist/assets/pieDiagram-ENE6RG2P-CRQdSuwS.js +39 -0
  119. package/dist/assets/plus-TmFdwO3P.js +1 -0
  120. package/dist/assets/{popover-DImguRok.js → popover-CX9zFsuc.js} +1 -1
  121. package/dist/assets/preload-helper-D4M6sveU.js +1 -0
  122. package/dist/assets/{provider-scoped-model-input-CE7p4SbJ.js → provider-scoped-model-input-B-9_DtIP.js} +1 -1
  123. package/dist/assets/providers-config-page-xwIjGur2.js +1 -0
  124. package/dist/assets/quadrantDiagram-ABIIQ3AL-D5kPpttq.js +7 -0
  125. package/dist/assets/radar-I7S5WNFK-CDg8Z-1r.js +1 -0
  126. package/dist/assets/railroad-3IZDKUUU-BHnNmjxO.js +1 -0
  127. package/dist/assets/railroad-abnf-AHOZXSZD-DGtNcusW.js +1 -0
  128. package/dist/assets/railroad-ebnf-EBAXGLYW-BRFtsQmm.js +1 -0
  129. package/dist/assets/railroad-peg-LSFZ7HO6-Bj5IypCA.js +1 -0
  130. package/dist/assets/railroadDiagram-RFXS5EU6-DGh_TGlQ.js +1 -0
  131. package/dist/assets/{react-5sjAT_6d.js → react-DsgudwpT.js} +1 -1
  132. package/dist/assets/{refresh-cw-CHLBzWWj.js → refresh-cw-kXdvbhFf.js} +1 -1
  133. package/dist/assets/remote-B9F2uCk9.js +1 -0
  134. package/dist/assets/requirementDiagram-TGXJPOKE-EmFph0H_.js +84 -0
  135. package/dist/assets/rotate-cw-DEc6aihr.js +1 -0
  136. package/dist/assets/rough.esm-wJ1d3nQl.js +1 -0
  137. package/dist/assets/runtime-config-page-DTsi7czJ.js +1 -0
  138. package/dist/assets/sankeyDiagram-HTMAVEWB-BXWB5Ugi.js +40 -0
  139. package/dist/assets/{save-BABzyrjB.js → save-C9qpz2_2.js} +1 -1
  140. package/dist/assets/{search-BTqxG3ZG.js → search-_kEi29qo.js} +1 -1
  141. package/dist/assets/{search-config-page-BkKKmxmS.js → search-config-page-H7ug8f4D.js} +1 -1
  142. package/dist/assets/{secrets-config-page-CWJMyu-k.js → secrets-config-page-C1_6rINu.js} +2 -2
  143. package/dist/assets/security-config-Cr2ERKLu.js +1 -0
  144. package/dist/assets/{select-TYZDBNmq.js → select-MJKhKAWR.js} +1 -1
  145. package/dist/assets/sequenceDiagram-DBY2YBRQ-ClKnO7v0.js +162 -0
  146. package/dist/assets/{setting-row-DR23Z_5I.js → setting-row-DA3tJuew.js} +1 -1
  147. package/dist/assets/{settings-2-D8WWcBnw.js → settings-2-D_y7zXJz.js} +1 -1
  148. package/dist/assets/sizeCapture-X5ZJPWSS-DSo-t5ou.js +1 -0
  149. package/dist/assets/{skeleton-CloHeg0S.js → skeleton-0uTLUhm9.js} +1 -1
  150. package/dist/assets/src-Dad2ZbCt.js +1 -0
  151. package/dist/assets/stateDiagram-2N3HPSRC-BvAalBfz.js +1 -0
  152. package/dist/assets/stateDiagram-v2-6OUMAXLB-CfMU38yc.js +1 -0
  153. package/dist/assets/swimlanes-5IMT3BWC-DdVqVa1a.js +2 -0
  154. package/dist/assets/swimlanesDiagram-G3AALYLV-D9Zb9X57.js +8 -0
  155. package/dist/assets/{tabs-CX-ckWoo.js → tabs-h44XPocd.js} +1 -1
  156. package/dist/assets/{tag-chip-mCc23Rhb.js → tag-chip-bRZ04q3C.js} +1 -1
  157. package/dist/assets/timeline-definition-FHXFAJF6-D4ar8IQY.js +120 -0
  158. package/dist/assets/{trash-2-SAW1Z4Po.js → trash-2-BRpSE-Wt.js} +1 -1
  159. package/dist/assets/treeView-QDETBFTQ-Uy7M0WvH.js +1 -0
  160. package/dist/assets/treemap-6X3UGDF4-BJfGV7KX.js +1 -0
  161. package/dist/assets/use-config-CwDEAl6f.js +1 -0
  162. package/dist/assets/{use-confirm-dialog-C95F6sT1.js → use-confirm-dialog-BbnWw-8c.js} +1 -1
  163. package/dist/assets/{use-viewport-layout-C6m3Nte7.js → use-viewport-layout-CqgIMDQL.js} +1 -1
  164. package/dist/assets/vennDiagram-L72KCM5P-CDXJ_i3p.js +34 -0
  165. package/dist/assets/wardley-OPB4EBWU-q263iF3K.js +1 -0
  166. package/dist/assets/wardleyDiagram-EHGQE667-D-dSuT_7.js +78 -0
  167. package/dist/assets/x-DdEDL6ju.js +1 -0
  168. package/dist/assets/xychartDiagram-FW5EYKEG-C9JCzsZF.js +7 -0
  169. package/dist/index.html +33 -32
  170. package/package.json +8 -8
  171. package/src/features/chat/components/chat-popover-content.tsx +1 -26
  172. package/src/features/chat/components/conversation/__tests__/chat-conversation-header-section.test.tsx +84 -1
  173. package/src/features/chat/components/conversation/chat-conversation-header-section.tsx +13 -2
  174. package/src/features/chat/components/conversation/chat-conversation-workspace-section.tsx +1 -0
  175. package/src/features/chat/components/layout/__tests__/chat-sidebar-test.utils.ts +22 -0
  176. package/src/features/chat/components/layout/__tests__/chat-sidebar.test.tsx +13 -24
  177. package/src/features/chat/components/layout/chat-sidebar-desktop-layout.tsx +16 -4
  178. package/src/features/chat/components/layout/chat-sidebar.tsx +38 -2
  179. package/src/features/chat/features/conversation/components/__tests__/session-conversation-area.test.tsx +1 -0
  180. package/src/features/chat/features/conversation/components/__tests__/session-conversation-input.streaming.test.tsx +52 -2
  181. package/src/features/chat/features/conversation/components/session-conversation-area.tsx +22 -0
  182. package/src/features/chat/features/conversation/components/session-conversation-input.tsx +36 -20
  183. package/src/features/chat/features/conversation/hooks/__tests__/use-session-conversation-input-state.test.tsx +55 -0
  184. package/src/features/chat/features/conversation/hooks/use-session-conversation-input-state.ts +12 -11
  185. package/src/features/chat/features/conversation/hooks/use-session-conversation-preference-actions.ts +94 -0
  186. package/src/features/chat/features/input/hooks/__tests__/use-chat-input-surface-state.test.tsx +37 -0
  187. package/src/features/chat/features/input/hooks/use-chat-input-surface-state.ts +140 -63
  188. package/src/features/chat/features/input/input-surface-plugins/__tests__/context-reference-plugin.test.ts +175 -0
  189. package/src/features/chat/features/input/input-surface-plugins/__tests__/slash-command-plugin.test.ts +66 -0
  190. package/src/features/chat/features/input/input-surface-plugins/chat-input-product-plugin-adapters.types.ts +8 -1
  191. package/src/features/chat/features/input/input-surface-plugins/context-reference-plugin.utils.ts +274 -0
  192. package/src/features/chat/features/input/input-surface-plugins/slash-command-plugin.utils.ts +18 -11
  193. package/src/features/chat/features/input/utils/__tests__/chat-composer-state.utils.test.ts +26 -0
  194. package/src/features/chat/features/input/utils/__tests__/chat-inline-token.utils.test.ts +50 -4
  195. package/src/features/chat/features/input/utils/__tests__/chat-input-bar.utils.test.ts +51 -0
  196. package/src/features/chat/features/input/utils/chat-composer-state.utils.ts +4 -10
  197. package/src/features/chat/features/input/utils/chat-composer-token-protocol.utils.ts +29 -0
  198. package/src/features/chat/features/input/utils/chat-inline-token.utils.ts +66 -8
  199. package/src/features/chat/features/input/utils/chat-input-bar.utils.ts +62 -21
  200. package/src/features/chat/features/message/components/__tests__/chat-message-list.container.test.tsx +42 -10
  201. package/src/features/chat/features/message/components/chat-inline-file-preview.tsx +80 -0
  202. package/src/features/chat/features/message/components/chat-message-list.container.tsx +55 -60
  203. package/src/features/chat/features/message/utils/chat-message-texts.utils.ts +2 -0
  204. package/src/features/chat/features/ncp/hooks/__tests__/use-hydrated-ncp-agent.test.tsx +41 -3
  205. package/src/features/chat/features/project/components/__tests__/chat-project-create-dialog.test.tsx +67 -0
  206. package/src/features/chat/features/project/components/chat-project-create-dialog.tsx +203 -0
  207. package/src/features/chat/features/session/components/session-header/__tests__/chat-session-header-actions.test.tsx +7 -7
  208. package/src/features/chat/features/session/components/session-header/__tests__/chat-session-project-dialog.test.tsx +47 -0
  209. package/src/features/chat/features/session/components/session-header/chat-session-header-actions.tsx +6 -3
  210. package/src/features/chat/features/session/components/session-header/chat-session-project-badge.tsx +3 -0
  211. package/src/features/chat/features/session/components/session-header/chat-session-project-dialog.tsx +3 -0
  212. package/src/features/chat/features/session/components/session-header/chat-session-switcher-groups.tsx +245 -0
  213. package/src/features/chat/features/session/components/session-header/chat-session-title-switcher.tsx +81 -127
  214. package/src/features/chat/features/session/utils/__tests__/chat-sidebar-session-groups.utils.test.ts +22 -0
  215. package/src/features/chat/features/session/utils/chat-run-metadata.utils.ts +6 -6
  216. package/src/features/chat/features/session/utils/chat-sidebar-session-groups.utils.ts +12 -1
  217. package/src/features/chat/features/welcome/components/chat-conversation-welcome.tsx +3 -0
  218. package/src/features/chat/features/welcome/components/chat-welcome.tsx +1 -0
  219. package/src/features/chat/features/welcome/utils/__tests__/chat-welcome-project-options.utils.test.ts +20 -0
  220. package/src/features/chat/features/welcome/utils/chat-welcome-project-options.utils.ts +15 -2
  221. package/src/features/chat/features/workspace/components/__tests__/chat-session-workspace-file-preview.test.tsx +22 -24
  222. package/src/features/chat/features/workspace/components/__tests__/chat-session-workspace-panel.test.tsx +2 -1
  223. package/src/features/chat/features/workspace/components/chat-session-workspace-file-breadcrumbs.tsx +6 -17
  224. package/src/features/chat/features/workspace/components/chat-session-workspace-file-preview.tsx +41 -19
  225. package/src/features/chat/features/workspace/components/chat-session-workspace-panel-nav.tsx +5 -1
  226. package/src/features/chat/features/workspace/components/chat-session-workspace-panel.tsx +5 -1
  227. package/src/features/chat/features/workspace/utils/__tests__/chat-workspace-panel-view-model.utils.test.ts +104 -3
  228. package/src/features/chat/features/workspace/utils/chat-thread-workspace-session.utils.ts +83 -0
  229. package/src/features/chat/features/workspace/utils/chat-workspace-file-tab.utils.ts +4 -1
  230. package/src/features/chat/features/workspace/utils/chat-workspace-panel-view-model.utils.ts +96 -25
  231. package/src/features/chat/managers/__tests__/chat-thread-workspace.manager.test.ts +25 -0
  232. package/src/features/chat/managers/__tests__/chat-thread.manager.test.ts +5 -1
  233. package/src/features/chat/managers/chat-session-list.manager.ts +1 -0
  234. package/src/features/chat/managers/chat-thread.manager.ts +15 -37
  235. package/src/features/chat/pages/__tests__/ncp-chat-page.test.ts +23 -0
  236. package/src/features/chat/stores/__tests__/chat-thread.store.test.ts +9 -0
  237. package/src/features/chat/stores/chat-thread.store.ts +16 -36
  238. package/src/features/chat/types/chat-input-bar.types.ts +2 -0
  239. package/src/features/settings/components/config/provider-form.tsx +0 -4
  240. package/src/features/settings/utils/__tests__/provider-form-support.utils.test.ts +7 -9
  241. package/src/features/settings/utils/provider-form-model.utils.ts +0 -4
  242. package/src/features/system-status/hooks/use-current-version-release-notes-link.ts +33 -0
  243. package/src/features/system-status/index.ts +9 -0
  244. package/src/features/system-status/utils/update-release-notes.utils.ts +44 -3
  245. package/src/index.css +10 -16
  246. package/src/shared/components/common/__tests__/brand-header.test.tsx +127 -10
  247. package/src/shared/components/common/brand-header.tsx +142 -56
  248. package/src/shared/components/path-picker/__tests__/server-path-picker-dialog.test.tsx +288 -38
  249. package/src/shared/components/path-picker/server-path-picker-dialog.tsx +290 -199
  250. package/src/shared/components/path-picker/server-path-picker-directory-list.tsx +103 -0
  251. package/src/shared/components/path-picker/server-path-picker-footer.tsx +58 -0
  252. package/src/shared/components/path-picker/server-path-picker-locations.tsx +148 -0
  253. package/src/shared/components/path-picker/server-path-picker-navigation-bar.tsx +198 -0
  254. package/src/shared/components/path-picker/server-path-picker-new-folder.tsx +59 -0
  255. package/src/shared/components/path-picker/server-path-picker-toolbar.tsx +25 -0
  256. package/src/shared/hooks/use-projects.ts +28 -0
  257. package/src/shared/hooks/use-server-path-create-directory.ts +6 -0
  258. package/src/shared/hooks/use-server-path-read.ts +10 -5
  259. package/src/shared/hooks/use-server-path-search.ts +40 -0
  260. package/src/shared/lib/api/index.ts +1 -0
  261. package/src/shared/lib/api/projects/project.utils.ts +14 -0
  262. package/src/shared/lib/api/types.ts +30 -3
  263. package/src/shared/lib/api/utils/server-path.utils.ts +22 -1
  264. package/src/shared/lib/i18n/locales/en-US/chat.json +43 -2
  265. package/src/shared/lib/i18n/locales/en-US/core.json +2 -3
  266. package/src/shared/lib/i18n/locales/en-US/desktop-update.json +4 -0
  267. package/src/shared/lib/i18n/locales/en-US/path-picker.json +25 -0
  268. package/src/shared/lib/i18n/locales/zh-CN/chat.json +43 -2
  269. package/src/shared/lib/i18n/locales/zh-CN/core.json +2 -3
  270. package/src/shared/lib/i18n/locales/zh-CN/desktop-update.json +4 -0
  271. package/src/shared/lib/i18n/locales/zh-CN/path-picker.json +25 -0
  272. package/src/shared/lib/session-project/__tests__/workspace-file-breadcrumb.test.ts +1 -4
  273. package/src/shared/lib/session-project/workspace-file-breadcrumb.utils.ts +1 -18
  274. package/dist/assets/api-BOXOrpcJ.js +0 -13
  275. package/dist/assets/app-presenter-provider-CkHLbvZQ.js +0 -106
  276. package/dist/assets/desktop-update-config-7bmNeWIC.js +0 -1
  277. package/dist/assets/doc-browser-CgdoLUT92.js +0 -1
  278. package/dist/assets/index-BFeNDQ4R.css +0 -1
  279. package/dist/assets/index-D_6U-M94.js +0 -2
  280. package/dist/assets/loader-circle-tYHStefV.js +0 -1
  281. package/dist/assets/mcp-marketplace-page-BUzHDwW6.js +0 -9
  282. package/dist/assets/mcp-marketplace-page-ohUAdfqJ.js +0 -1
  283. package/dist/assets/plus-tywizkeY.js +0 -1
  284. package/dist/assets/providers-config-page-BKPo8SjH.js +0 -1
  285. package/dist/assets/remote-DXJryUxB.js +0 -1
  286. package/dist/assets/rotate-cw-BefO5brU.js +0 -1
  287. package/dist/assets/runtime-config-page-1Axv4QGu.js +0 -1
  288. package/dist/assets/security-config-DeQUIXfi.js +0 -1
  289. package/dist/assets/use-config-BucdaK23.js +0 -1
  290. package/dist/assets/x-DtCPHwCu.js +0 -1
  291. package/src/features/chat/components/__tests__/chat-popover-content.test.tsx +0 -41
  292. package/src/features/chat/components/layout/__tests__/chat-sidebar-utility-menu.test.tsx +0 -55
  293. package/src/features/chat/features/input/input-surface-plugins/__tests__/panel-app-reference-plugin.test.ts +0 -77
  294. package/src/features/chat/features/input/input-surface-plugins/panel-app-reference-plugin.utils.ts +0 -144
  295. /package/dist/assets/{config-hints-fDrYfl0l.js → config-hints-LxG80Uj5.js} +0 -0
  296. /package/dist/assets/{host-capabilities-zZvY9U63.js → host-capabilities-CFbAfrrA.js} +0 -0
  297. /package/dist/assets/{jszip.min-Das2IAnb.js → jszip.min-CP5uPNQa.js} +0 -0
  298. /package/dist/assets/{middleware-BLMHv9YF.js → middleware-CAXxl-af.js} +0 -0
  299. /package/dist/assets/{provider-models-C_yOh6DE.js → provider-models-BoUhFo9R.js} +0 -0
  300. /package/dist/assets/{xlsx-B6zVt1RK.js → xlsx-DUqJhg5u.js} +0 -0
@@ -33,6 +33,37 @@ export function areWorkspaceNavigationEntriesEqual(
33
33
  );
34
34
  }
35
35
 
36
+ export function createWorkspaceNavigationEntryFromSnapshot(
37
+ snapshot: Pick<
38
+ ChatThreadSnapshot,
39
+ 'activeChildSessionKey' | 'activeSideChatDraft' | 'activeWorkspaceFileKey' | 'activeWorkspacePanelKind'
40
+ >,
41
+ ): ChatWorkspaceNavigationEntry | null {
42
+ const {
43
+ activeChildSessionKey,
44
+ activeSideChatDraft,
45
+ activeWorkspaceFileKey,
46
+ activeWorkspacePanelKind,
47
+ } = snapshot;
48
+ if (
49
+ activeWorkspacePanelKind === 'overview' ||
50
+ activeWorkspacePanelKind === 'child-sessions' ||
51
+ activeWorkspacePanelKind === 'project-files' ||
52
+ activeWorkspacePanelKind === 'cron'
53
+ ) {
54
+ return { kind: activeWorkspacePanelKind };
55
+ }
56
+ if (activeWorkspacePanelKind === 'child-session' && activeChildSessionKey) {
57
+ return { kind: 'child-session', key: activeChildSessionKey };
58
+ }
59
+ if (activeWorkspacePanelKind === 'side-chat-draft' && activeSideChatDraft) {
60
+ return { kind: 'side-chat-draft', key: activeSideChatDraft.draftKey };
61
+ }
62
+ return activeWorkspacePanelKind === 'file' && activeWorkspaceFileKey
63
+ ? { kind: 'file', key: activeWorkspaceFileKey }
64
+ : null;
65
+ }
66
+
36
67
  export function createWorkspaceSelectionPatch(
37
68
  entry: ChatWorkspaceNavigationEntry,
38
69
  snapshot: ChatThreadSnapshot,
@@ -88,6 +119,58 @@ export function createWorkspaceSelectionPatch(
88
119
  };
89
120
  }
90
121
 
122
+ export function closeWorkspaceTabSnapshot(
123
+ snapshot: ChatThreadSnapshot,
124
+ entry: ChatWorkspaceNavigationEntry,
125
+ ): Partial<ChatThreadSnapshot> | null {
126
+ if (entry.kind === 'overview') {
127
+ return null;
128
+ }
129
+ const history = filterNavigationHistoryEntries(
130
+ {
131
+ entries: snapshot.workspaceNavigationHistory,
132
+ index: snapshot.workspaceNavigationHistoryIndex,
133
+ },
134
+ (candidate) => !areWorkspaceNavigationEntriesEqual(candidate, entry),
135
+ { kind: 'overview' },
136
+ );
137
+ const shouldRememberClosure = entry.kind !== 'file' && entry.kind !== 'side-chat-draft';
138
+ const patch: Partial<ChatThreadSnapshot> = {
139
+ closedWorkspaceTabEntries: shouldRememberClosure
140
+ ? [
141
+ ...snapshot.closedWorkspaceTabEntries.filter(
142
+ (candidate) => !areWorkspaceNavigationEntriesEqual(candidate, entry),
143
+ ),
144
+ entry,
145
+ ]
146
+ : snapshot.closedWorkspaceTabEntries,
147
+ childSessionTabs: entry.kind === 'child-session'
148
+ ? snapshot.childSessionTabs.filter((tab) => tab.sessionKey !== entry.key)
149
+ : snapshot.childSessionTabs,
150
+ activeSideChatDraft: entry.kind === 'side-chat-draft'
151
+ ? null
152
+ : snapshot.activeSideChatDraft,
153
+ workspaceFileTabs: entry.kind === 'file'
154
+ ? snapshot.workspaceFileTabs.filter((tab) => tab.key !== entry.key)
155
+ : snapshot.workspaceFileTabs,
156
+ workspaceNavigationHistory: [...history.entries],
157
+ workspaceNavigationHistoryIndex: history.index,
158
+ };
159
+ const activeEntry = createWorkspaceNavigationEntryFromSnapshot(snapshot);
160
+ if (!activeEntry || !areWorkspaceNavigationEntriesEqual(activeEntry, entry)) {
161
+ return patch;
162
+ }
163
+ const restoreEntry = history.entries[history.index] ?? { kind: 'overview' };
164
+ const restorePatch = createWorkspaceSelectionPatch(restoreEntry, {
165
+ ...snapshot,
166
+ ...patch,
167
+ });
168
+ return {
169
+ ...patch,
170
+ ...(restorePatch ?? createWorkspaceSelectionPatch({ kind: 'overview' }, snapshot)),
171
+ };
172
+ }
173
+
91
174
  export function createSideChatDraft(parentSessionKey: string): ChatWorkspaceSideChatDraft {
92
175
  const randomPart =
93
176
  typeof globalThis.crypto?.randomUUID === 'function'
@@ -12,7 +12,10 @@ export function createWorkspaceFileTab(
12
12
  }
13
13
  const normalizedParentSessionKey = parentSessionKey?.trim() || null;
14
14
  const previewViewer = action.viewMode === 'preview'
15
- ? normalizeWorkspaceFilePreviewViewer(path, action.previewViewer)
15
+ ? normalizeWorkspaceFilePreviewViewer(
16
+ path,
17
+ action.previewViewer ?? (action.line ? 'source' : undefined),
18
+ )
16
19
  : action.previewViewer ?? null;
17
20
  const viewIdentity = action.viewMode === 'preview' && previewViewer === 'rendered'
18
21
  ? 'preview:rendered'
@@ -1,6 +1,7 @@
1
1
  import type { ResolvedChildSessionTab } from "@/features/chat/features/ncp/hooks/use-ncp-child-session-tabs-view";
2
2
  import { shouldShowUnreadSessionIndicator } from "@/features/chat/stores/chat-session-list.store";
3
3
  import type {
4
+ ChatWorkspaceNavigationEntry,
4
5
  ChatWorkspacePanelKind,
5
6
  ChatWorkspaceFileTab,
6
7
  ChatWorkspaceSideChatDraft,
@@ -11,6 +12,7 @@ import {
11
12
  resolveWorkspaceFileViewer,
12
13
  type ChatWorkspaceFileViewer,
13
14
  } from "@/features/chat/features/workspace/utils/chat-workspace-file-viewer.utils";
15
+ import { areWorkspaceNavigationEntriesEqual } from "@/features/chat/features/workspace/utils/chat-thread-workspace-session.utils";
14
16
 
15
17
  export type WorkspaceSelection =
16
18
  | {
@@ -151,41 +153,54 @@ export function resolveWorkspaceSelection(params: {
151
153
  return null;
152
154
  }
153
155
 
154
- export function buildWorkspaceTabsViewModel(params: {
156
+ type WorkspaceTabsViewModelParams = {
155
157
  resolvedChildTabs: ResolvedChildSessionTab[];
156
158
  activeSideChatDraft: ChatWorkspaceSideChatDraft | null;
159
+ closedWorkspaceTabEntries: readonly ChatWorkspaceNavigationEntry[];
157
160
  workspaceFileTabs: readonly ChatWorkspaceFileTab[];
158
161
  activeSelection: WorkspaceSelection | null;
159
162
  optimisticReadAtBySessionKey: Record<string, string>;
160
163
  onSelectSession: (sessionKey: string) => void;
161
164
  onSelectFile: (fileKey: string) => void;
162
165
  onOpenFileViewer: (fileKey: string, viewer: ChatWorkspaceFileViewer) => void;
163
- onCloseFile: (fileKey: string) => void;
166
+ onCloseTab: (entry: ChatWorkspaceNavigationEntry) => void;
164
167
  onSelectOverview: () => void;
165
168
  onSelectChildSessions: () => void;
166
169
  onSelectProjectFiles: () => void;
167
170
  onSelectCronJobs: () => void;
168
- }): WorkspaceTabViewModel[] {
169
- const {
170
- activeSideChatDraft,
171
- resolvedChildTabs,
172
- workspaceFileTabs,
173
- activeSelection,
174
- optimisticReadAtBySessionKey,
175
- onSelectSession,
176
- onSelectFile,
177
- onOpenFileViewer,
178
- onCloseFile,
179
- onSelectOverview,
180
- onSelectChildSessions,
181
- onSelectProjectFiles,
182
- onSelectCronJobs,
183
- } = params;
171
+ };
184
172
 
185
- const workspacePages = [
173
+ function createWorkspacePageTabEntry(
174
+ kind: ChatWorkspacePanelKind,
175
+ ): ChatWorkspaceNavigationEntry | null {
176
+ if (kind === "child-sessions" || kind === "cron" || kind === "project-files") {
177
+ return { kind };
178
+ }
179
+ return null;
180
+ }
181
+
182
+ function buildWorkspacePageTabs({
183
+ activeSelection,
184
+ closedWorkspaceTabEntries,
185
+ onCloseTab,
186
+ onSelectChildSessions,
187
+ onSelectCronJobs,
188
+ onSelectOverview,
189
+ onSelectProjectFiles,
190
+ }: Pick<
191
+ WorkspaceTabsViewModelParams,
192
+ | "activeSelection"
193
+ | "closedWorkspaceTabEntries"
194
+ | "onCloseTab"
195
+ | "onSelectChildSessions"
196
+ | "onSelectCronJobs"
197
+ | "onSelectOverview"
198
+ | "onSelectProjectFiles"
199
+ >): WorkspaceTabViewModel[] {
200
+ const tabs: WorkspaceTabViewModel[] = [
186
201
  {
187
202
  key: "overview",
188
- kind: "overview" as const,
203
+ kind: "overview",
189
204
  title: t("chatWorkspaceOverview"),
190
205
  tooltip: t("chatWorkspaceOverview"),
191
206
  active: activeSelection?.kind === "overview",
@@ -193,29 +208,69 @@ export function buildWorkspaceTabsViewModel(params: {
193
208
  },
194
209
  {
195
210
  key: "child-sessions",
196
- kind: "child-sessions" as const,
211
+ kind: "child-sessions",
197
212
  title: t("chatWorkspaceChildSessions"),
198
213
  tooltip: t("chatWorkspaceChildSessions"),
199
214
  active: activeSelection?.kind === "child-sessions",
200
215
  onSelect: onSelectChildSessions,
216
+ onClose: () => onCloseTab({ kind: "child-sessions" }),
201
217
  },
202
218
  {
203
219
  key: "cron:session",
204
- kind: "cron" as const,
220
+ kind: "cron",
205
221
  title: t("chatWorkspaceSessionCronJobs"),
206
222
  tooltip: t("chatWorkspaceSessionCronJobs"),
207
223
  active: activeSelection?.kind === "cron",
208
224
  onSelect: onSelectCronJobs,
225
+ onClose: () => onCloseTab({ kind: "cron" }),
209
226
  },
210
227
  {
211
228
  key: "project-files",
212
- kind: "project-files" as const,
229
+ kind: "project-files",
213
230
  title: t("chatWorkspaceProjectFiles"),
214
231
  tooltip: t("chatWorkspaceProjectFiles"),
215
232
  active: activeSelection?.kind === "project-files",
216
233
  onSelect: onSelectProjectFiles,
234
+ onClose: () => onCloseTab({ kind: "project-files" }),
217
235
  },
218
236
  ];
237
+ return tabs.filter((tab) => {
238
+ const entry = createWorkspacePageTabEntry(tab.kind);
239
+ return !entry || !closedWorkspaceTabEntries.some((candidate) =>
240
+ areWorkspaceNavigationEntriesEqual(candidate, entry),
241
+ );
242
+ });
243
+ }
244
+
245
+ export function buildWorkspaceTabsViewModel(
246
+ params: WorkspaceTabsViewModelParams,
247
+ ): WorkspaceTabViewModel[] {
248
+ const {
249
+ activeSideChatDraft,
250
+ closedWorkspaceTabEntries,
251
+ resolvedChildTabs,
252
+ workspaceFileTabs,
253
+ activeSelection,
254
+ optimisticReadAtBySessionKey,
255
+ onSelectSession,
256
+ onSelectFile,
257
+ onOpenFileViewer,
258
+ onCloseTab,
259
+ onSelectOverview,
260
+ onSelectChildSessions,
261
+ onSelectProjectFiles,
262
+ onSelectCronJobs,
263
+ } = params;
264
+
265
+ const workspacePages = buildWorkspacePageTabs({
266
+ activeSelection,
267
+ closedWorkspaceTabEntries,
268
+ onCloseTab,
269
+ onSelectChildSessions,
270
+ onSelectCronJobs,
271
+ onSelectOverview,
272
+ onSelectProjectFiles,
273
+ });
219
274
 
220
275
  const sideChatDraftTabs = activeSideChatDraft
221
276
  ? [
@@ -228,11 +283,23 @@ export function buildWorkspaceTabsViewModel(params: {
228
283
  activeSelection?.kind === "side-chat-draft" &&
229
284
  activeSelection.draft.draftKey === activeSideChatDraft.draftKey,
230
285
  onSelect: () => undefined,
286
+ onClose: () => onCloseTab({
287
+ kind: "side-chat-draft",
288
+ key: activeSideChatDraft.draftKey,
289
+ }),
231
290
  },
232
291
  ]
233
292
  : [];
234
293
 
235
- const childTabs = resolvedChildTabs.map((tab) => {
294
+ const childTabs = resolvedChildTabs.filter(
295
+ (tab) =>
296
+ !closedWorkspaceTabEntries.some((entry) =>
297
+ areWorkspaceNavigationEntriesEqual(entry, {
298
+ kind: "child-session",
299
+ key: tab.sessionKey,
300
+ }),
301
+ ),
302
+ ).map((tab) => {
236
303
  const optimisticReadAt = optimisticReadAtBySessionKey[tab.sessionKey];
237
304
  const effectiveReadAt =
238
305
  optimisticReadAt && tab.readAt
@@ -259,6 +326,10 @@ export function buildWorkspaceTabsViewModel(params: {
259
326
  runStatus: tab.runStatus,
260
327
  }),
261
328
  onSelect: () => onSelectSession(tab.sessionKey),
329
+ onClose: () => onCloseTab({
330
+ kind: "child-session",
331
+ key: tab.sessionKey,
332
+ }),
262
333
  };
263
334
  });
264
335
 
@@ -292,7 +363,7 @@ export function buildWorkspaceTabsViewModel(params: {
292
363
  active:
293
364
  activeSelection?.kind === "file" && activeSelection.file.key === file.key,
294
365
  onSelect: () => onSelectFile(file.key),
295
- onClose: () => onCloseFile(file.key),
366
+ onClose: () => onCloseTab({ kind: "file", key: file.key }),
296
367
  };
297
368
  });
298
369
 
@@ -24,6 +24,7 @@ describe('ChatThreadManager workspace pages', () => {
24
24
  activeWorkspacePanelKind: null,
25
25
  activeChildSessionKey: null,
26
26
  activeWorkspaceFileKey: null,
27
+ closedWorkspaceTabEntries: [],
27
28
  workspaceNavigationHistory: [],
28
29
  workspaceNavigationHistoryIndex: 0,
29
30
  },
@@ -73,4 +74,28 @@ describe('ChatThreadManager workspace pages', () => {
73
74
  activeWorkspacePanelKind: null,
74
75
  });
75
76
  });
77
+
78
+ it('closes a workspace page, restores overview, and reopens the page on demand', () => {
79
+ const manager = new ChatThreadManager(
80
+ createUiManager(),
81
+ {} as ConstructorParameters<typeof ChatThreadManager>[1],
82
+ );
83
+
84
+ manager.openWorkspaceOverview('parent-session-1');
85
+ manager.openProjectFiles('parent-session-1');
86
+ manager.closeWorkspaceTab({ kind: 'project-files' });
87
+
88
+ expect(useChatThreadStore.getState().snapshot).toMatchObject({
89
+ activeWorkspacePanelKind: 'overview',
90
+ closedWorkspaceTabEntries: [{ kind: 'project-files' }],
91
+ workspaceNavigationHistory: [{ kind: 'overview' }],
92
+ workspaceNavigationHistoryIndex: 0,
93
+ });
94
+
95
+ manager.openProjectFiles('parent-session-1');
96
+ expect(useChatThreadStore.getState().snapshot).toMatchObject({
97
+ activeWorkspacePanelKind: 'project-files',
98
+ closedWorkspaceTabEntries: [],
99
+ });
100
+ });
76
101
  });
@@ -60,6 +60,7 @@ beforeEach(() => {
60
60
  activeSideChatDraft: null,
61
61
  workspaceFileTabs: [],
62
62
  activeWorkspaceFileKey: null,
63
+ closedWorkspaceTabEntries: [],
63
64
  workspaceNavigationHistory: [],
64
65
  workspaceNavigationHistoryIndex: 0,
65
66
  },
@@ -504,7 +505,10 @@ describe('ChatThreadManager workspace navigation', () => {
504
505
  label: 'README.md',
505
506
  viewMode: 'preview',
506
507
  });
507
- manager.closeWorkspaceFile('parent-session-1::preview:rendered::README.md');
508
+ manager.closeWorkspaceTab({
509
+ kind: 'file',
510
+ key: 'parent-session-1::preview:rendered::README.md',
511
+ });
508
512
 
509
513
  expect(useChatThreadStore.getState().snapshot).toMatchObject({
510
514
  activeWorkspacePanelKind: 'child-session',
@@ -39,6 +39,7 @@ export class ChatSessionListManager {
39
39
  childSessionTabs: [],
40
40
  activeChildSessionKey: null,
41
41
  activeWorkspaceFileKey: null,
42
+ closedWorkspaceTabEntries: [],
42
43
  workspaceNavigationHistory: [],
43
44
  workspaceNavigationHistoryIndex: 0,
44
45
  });
@@ -24,13 +24,13 @@ import { normalizeChatWorkspacePanelWidth } from '@/features/chat/features/works
24
24
  import { t } from '@/shared/lib/i18n';
25
25
  import {
26
26
  areWorkspaceNavigationEntriesEqual,
27
+ closeWorkspaceTabSnapshot,
27
28
  createWorkspaceSelectionPatch,
28
29
  createSideChatDraft,
29
30
  materializeSideChatDraftSnapshot,
30
31
  upsertChildSessionTab,
31
32
  } from '@/features/chat/features/workspace/utils/chat-thread-workspace-session.utils';
32
33
  import {
33
- filterNavigationHistoryEntries,
34
34
  pushNavigationHistoryEntry,
35
35
  stepNavigationHistory,
36
36
  } from '@/shared/lib/navigation-history';
@@ -96,6 +96,7 @@ export class ChatThreadManager {
96
96
  activeSideChatDraft: null,
97
97
  workspaceFileTabs: [],
98
98
  activeWorkspaceFileKey: null,
99
+ closedWorkspaceTabEntries: [],
99
100
  workspaceNavigationHistory: [],
100
101
  workspaceNavigationHistoryIndex: 0,
101
102
  });
@@ -163,8 +164,16 @@ export class ChatThreadManager {
163
164
  entry,
164
165
  areWorkspaceNavigationEntriesEqual,
165
166
  );
167
+ const closedWorkspaceTabEntries =
168
+ patch.workspacePanelParentKey &&
169
+ patch.workspacePanelParentKey !== snapshot.workspacePanelParentKey
170
+ ? []
171
+ : snapshot.closedWorkspaceTabEntries.filter(
172
+ (candidate) => !areWorkspaceNavigationEntriesEqual(candidate, entry),
173
+ );
166
174
  useChatThreadStore.getState().setSnapshot({
167
175
  ...patch,
176
+ closedWorkspaceTabEntries,
168
177
  workspaceNavigationHistory: [...history.entries],
169
178
  workspaceNavigationHistoryIndex: history.index,
170
179
  });
@@ -461,44 +470,12 @@ export class ChatThreadManager {
461
470
  });
462
471
  };
463
472
 
464
- closeWorkspaceFile = (fileKey: string) => {
465
- const normalizedFileKey = fileKey.trim();
466
- if (!normalizedFileKey) {
467
- return;
468
- }
473
+ closeWorkspaceTab = (entry: ChatWorkspaceNavigationEntry) => {
469
474
  const { snapshot } = useChatThreadStore.getState();
470
- const { activeWorkspaceFileKey, workspaceFileTabs } = snapshot;
471
- const nextTabs = workspaceFileTabs.filter(
472
- (tab) => tab.key !== normalizedFileKey,
473
- );
474
- const history = filterNavigationHistoryEntries(
475
- {
476
- entries: snapshot.workspaceNavigationHistory,
477
- index: snapshot.workspaceNavigationHistoryIndex,
478
- },
479
- (entry) => entry.kind !== 'file' || entry.key !== normalizedFileKey,
480
- );
481
- const nextPatch: Partial<ChatThreadSnapshot> = {
482
- workspaceFileTabs: nextTabs,
483
- workspaceNavigationHistory: [...history.entries],
484
- workspaceNavigationHistoryIndex: history.index,
485
- };
486
- if (activeWorkspaceFileKey === normalizedFileKey) {
487
- const nextSnapshot = {
488
- ...snapshot,
489
- workspaceFileTabs: nextTabs,
490
- };
491
- const restoreEntry = history.entries[history.index];
492
- const restorePatch = restoreEntry
493
- ? createWorkspaceSelectionPatch(restoreEntry, nextSnapshot)
494
- : null;
495
- Object.assign(nextPatch, restorePatch ?? {
496
- activeWorkspacePanelKind: null,
497
- activeSideChatDraft: null,
498
- activeWorkspaceFileKey: null,
499
- });
475
+ const patch = closeWorkspaceTabSnapshot(snapshot, entry);
476
+ if (patch) {
477
+ useChatThreadStore.getState().setSnapshot(patch);
500
478
  }
501
- useChatThreadStore.getState().setSnapshot(nextPatch);
502
479
  };
503
480
 
504
481
  closeWorkspacePanel = () => {
@@ -508,6 +485,7 @@ export class ChatThreadManager {
508
485
  activeChildSessionKey: null,
509
486
  activeSideChatDraft: null,
510
487
  activeWorkspaceFileKey: null,
488
+ closedWorkspaceTabEntries: [],
511
489
  workspaceNavigationHistory: [],
512
490
  workspaceNavigationHistoryIndex: 0,
513
491
  });
@@ -215,6 +215,29 @@ describe('buildChatRunMetadata', () => {
215
215
  ).not.toHaveProperty('ui_inline_tokens');
216
216
  });
217
217
 
218
+ it('adds structured workspace references to run metadata', () => {
219
+ expect(
220
+ buildChatRunMetadata({
221
+ composerNodes: [
222
+ createChatComposerTokenNode({
223
+ tokenKind: 'workspace_file',
224
+ tokenKey: 'src/file name.ts',
225
+ label: 'file name.ts',
226
+ }),
227
+ ],
228
+ }),
229
+ ).toMatchObject({
230
+ ui_inline_tokens: [
231
+ {
232
+ kind: 'workspace_file',
233
+ key: 'src/file name.ts',
234
+ label: 'file name.ts',
235
+ rawText: '@file:src%2Ffile%20name.ts',
236
+ },
237
+ ],
238
+ });
239
+ });
240
+
218
241
  it('serializes child session materialization metadata for side chat drafts', () => {
219
242
  expect(
220
243
  buildChatRunMetadata({
@@ -28,6 +28,7 @@ function resetChatThreadStore() {
28
28
  activeSideChatDraft: null,
29
29
  workspaceFileTabs: [],
30
30
  activeWorkspaceFileKey: null,
31
+ closedWorkspaceTabEntries: [],
31
32
  workspaceNavigationHistory: [],
32
33
  workspaceNavigationHistoryIndex: 0,
33
34
  workspacePanelWidth: CHAT_WORKSPACE_PANEL_DEFAULT_WIDTH,
@@ -64,6 +65,10 @@ describe('chat thread workspace panel persistence', () => {
64
65
  },
65
66
  ],
66
67
  activeWorkspaceFileKey: 'session-1::preview::README.md',
68
+ closedWorkspaceTabEntries: [
69
+ { kind: 'project-files' },
70
+ { kind: 'child-session', key: 'child-session-2' },
71
+ ],
67
72
  workspaceNavigationHistory: [
68
73
  { kind: 'child-session', key: 'child-session-1' },
69
74
  { kind: 'file', key: 'session-1::preview::README.md' },
@@ -79,6 +84,10 @@ describe('chat thread workspace panel persistence', () => {
79
84
  workspacePanelParentKey: 'session-1',
80
85
  activeWorkspacePanelKind: 'file',
81
86
  activeWorkspaceFileKey: 'session-1::preview::README.md',
87
+ closedWorkspaceTabEntries: [
88
+ { kind: 'project-files' },
89
+ { kind: 'child-session', key: 'child-session-2' },
90
+ ],
82
91
  workspaceNavigationHistory: [
83
92
  { kind: 'child-session', key: 'child-session-1' },
84
93
  { kind: 'file', key: 'session-1::preview::README.md' },
@@ -15,6 +15,7 @@ import {
15
15
  CHAT_WORKSPACE_PANEL_DEFAULT_WIDTH,
16
16
  normalizeChatWorkspacePanelWidth,
17
17
  } from '@/features/chat/features/workspace/utils/chat-workspace-panel-layout.utils';
18
+ import { createWorkspaceNavigationEntryFromSnapshot } from '@/features/chat/features/workspace/utils/chat-thread-workspace-session.utils';
18
19
 
19
20
  export type ChatChildSessionTab = {
20
21
  sessionKey: string;
@@ -92,6 +93,7 @@ export type ChatThreadSnapshot = {
92
93
  activeSideChatDraft?: ChatWorkspaceSideChatDraft | null;
93
94
  workspaceFileTabs: ChatWorkspaceFileTab[];
94
95
  activeWorkspaceFileKey?: string | null;
96
+ closedWorkspaceTabEntries: ChatWorkspaceNavigationEntry[];
95
97
  workspaceNavigationHistory: ChatWorkspaceNavigationEntry[];
96
98
  workspaceNavigationHistoryIndex: number;
97
99
  workspacePanelWidth: number;
@@ -114,6 +116,7 @@ type PersistedChatThreadStore = {
114
116
  activeChildSessionKey?: unknown;
115
117
  workspaceFileTabs?: unknown;
116
118
  activeWorkspaceFileKey?: unknown;
119
+ closedWorkspaceTabEntries?: unknown;
117
120
  workspaceNavigationHistory?: unknown;
118
121
  workspaceNavigationHistoryIndex?: unknown;
119
122
  workspacePanelWidth?: unknown;
@@ -127,6 +130,7 @@ type PersistedChatWorkspaceSnapshot = Pick<
127
130
  | 'activeChildSessionKey'
128
131
  | 'workspaceFileTabs'
129
132
  | 'activeWorkspaceFileKey'
133
+ | 'closedWorkspaceTabEntries'
130
134
  | 'workspaceNavigationHistory'
131
135
  | 'workspaceNavigationHistoryIndex'
132
136
  | 'workspacePanelWidth'
@@ -159,6 +163,7 @@ const initialSnapshot: ChatThreadSnapshot = {
159
163
  activeSideChatDraft: null,
160
164
  workspaceFileTabs: [],
161
165
  activeWorkspaceFileKey: null,
166
+ closedWorkspaceTabEntries: [],
162
167
  workspaceNavigationHistory: [],
163
168
  workspaceNavigationHistoryIndex: 0,
164
169
  workspacePanelWidth: CHAT_WORKSPACE_PANEL_DEFAULT_WIDTH,
@@ -218,42 +223,6 @@ function normalizePersistedWorkspaceNavigationEntry(
218
223
  };
219
224
  }
220
225
 
221
- function createWorkspaceNavigationEntryFromSnapshot({
222
- activeChildSessionKey,
223
- activeWorkspaceFileKey,
224
- activeWorkspacePanelKind,
225
- }: {
226
- activeWorkspacePanelKind: ChatThreadSnapshot['activeWorkspacePanelKind'];
227
- activeChildSessionKey: string | null;
228
- activeWorkspaceFileKey: string | null;
229
- }): ChatWorkspaceNavigationEntry | null {
230
- if (activeWorkspacePanelKind === 'overview') {
231
- return { kind: 'overview' };
232
- }
233
- if (activeWorkspacePanelKind === 'child-sessions') {
234
- return { kind: 'child-sessions' };
235
- }
236
- if (activeWorkspacePanelKind === 'project-files') {
237
- return { kind: 'project-files' };
238
- }
239
- if (activeWorkspacePanelKind === 'cron') {
240
- return { kind: 'cron' };
241
- }
242
- if (activeWorkspacePanelKind === 'child-session' && activeChildSessionKey) {
243
- return {
244
- kind: 'child-session',
245
- key: activeChildSessionKey,
246
- };
247
- }
248
- if (activeWorkspacePanelKind === 'file' && activeWorkspaceFileKey) {
249
- return {
250
- kind: 'file',
251
- key: activeWorkspaceFileKey,
252
- };
253
- }
254
- return null;
255
- }
256
-
257
226
  function normalizePersistedWorkspaceSnapshot(
258
227
  value: unknown,
259
228
  ): PersistedChatWorkspaceSnapshot | null {
@@ -287,9 +256,17 @@ function normalizePersistedWorkspaceSnapshot(
287
256
  ? null
288
257
  : activeWorkspacePanelKind;
289
258
  const activeChildSessionKey = normalizeOptionalString(value.activeChildSessionKey);
259
+ const closedWorkspaceTabEntries = Array.isArray(value.closedWorkspaceTabEntries)
260
+ ? value.closedWorkspaceTabEntries
261
+ .map(normalizePersistedWorkspaceNavigationEntry)
262
+ .filter((entry): entry is ChatWorkspaceNavigationEntry =>
263
+ entry !== null && entry.kind !== 'overview',
264
+ )
265
+ : [];
290
266
  const fallbackHistoryEntry = createWorkspaceNavigationEntryFromSnapshot({
291
267
  activeWorkspacePanelKind: resolvedActiveWorkspacePanelKind,
292
268
  activeChildSessionKey,
269
+ activeSideChatDraft: null,
293
270
  activeWorkspaceFileKey: resolvedActiveWorkspaceFileKey,
294
271
  });
295
272
  const normalizedNavigationHistory = Array.isArray(value.workspaceNavigationHistory)
@@ -320,6 +297,7 @@ function normalizePersistedWorkspaceSnapshot(
320
297
  activeChildSessionKey,
321
298
  workspaceFileTabs,
322
299
  activeWorkspaceFileKey: resolvedActiveWorkspaceFileKey,
300
+ closedWorkspaceTabEntries,
323
301
  workspaceNavigationHistory,
324
302
  workspaceNavigationHistoryIndex,
325
303
  workspacePanelWidth: normalizeChatWorkspacePanelWidth(
@@ -369,6 +347,8 @@ export const useChatThreadStore = create<ChatThreadStore>()(
369
347
  )
370
348
  .map(toPersistedWorkspaceFileTab),
371
349
  activeWorkspaceFileKey: state.snapshot.activeWorkspaceFileKey,
350
+ closedWorkspaceTabEntries: state.snapshot.closedWorkspaceTabEntries
351
+ .filter((entry) => entry.kind !== 'overview' && entry.kind !== 'side-chat-draft'),
372
352
  workspaceNavigationHistory,
373
353
  workspaceNavigationHistoryIndex,
374
354
  workspacePanelWidth: state.snapshot.workspacePanelWidth,
@@ -11,6 +11,8 @@ export type ChatSkillRecord = {
11
11
  key: string;
12
12
  label: string;
13
13
  scopeLabel?: string;
14
+ groupKey?: string;
15
+ groupLabel?: string;
14
16
  description?: string;
15
17
  descriptionZh?: string;
16
18
  badgeLabel?: string;
@@ -230,10 +230,6 @@ export function ProviderForm({ providerName, onProviderDeleted }: ProviderFormPr
230
230
 
231
231
  const handleAddModel = () => {
232
232
  const result = addProviderLocalModel(models, modelDraft, providerModelAliases);
233
- if (result.errorKey) {
234
- toast.error(t('providerModelInvalidProviderPrefix'));
235
- return;
236
- }
237
233
  setModels(result.models);
238
234
  setModelDraft(result.draft);
239
235
  };