@nextclaw/ui 0.15.3 → 0.15.5

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 (314) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/dist/assets/abnfDiagram-VRR7QNED-BYpK0F5F.js +1 -0
  3. package/dist/assets/aiden0z-pptx-renderer.es-D87ywFdC.js +94 -0
  4. package/dist/assets/api-IlX_CbwU.js +13 -0
  5. package/dist/assets/app-presenter-provider-Bbl_MVbg.js +106 -0
  6. package/dist/assets/app-presenter-provider-D3BuuOyZ.css +1 -0
  7. package/dist/assets/appearance-settings-page-80fLa4-9.js +1 -0
  8. package/dist/assets/arc-DFELHWM2.js +1 -0
  9. package/dist/assets/architecture-TIHT7OUA-ClwVFq-K.js +1 -0
  10. package/dist/assets/architectureDiagram-ZJ3FMSHR-CvRhdovL.js +36 -0
  11. package/dist/assets/array-BqYdCIE6.js +1 -0
  12. package/dist/assets/blockDiagram-677ZJIJ3-DVYNkbR_.js +132 -0
  13. package/dist/assets/{book-open-C3mJGwiy.js → book-open-CN7Shpij.js} +1 -1
  14. package/dist/assets/c4Diagram-LMCZKHZV-B1w-iwRe.js +10 -0
  15. package/dist/assets/channel-BEHOSFc5.js +1 -0
  16. package/dist/assets/channels-list-page-R7M6pzp6.js +8 -0
  17. package/dist/assets/chat-page-152YuwfU.js +1 -0
  18. package/dist/assets/chunk-2Q5K7J3B-DrIhesSX.js +1 -0
  19. package/dist/assets/chunk-32BRIVSS-kG3lg0bC.js +1 -0
  20. package/dist/assets/chunk-52WLFC77-BLeB7iGk.js +10 -0
  21. package/dist/assets/chunk-5FCAYU7R-BQga1Rr7.js +1 -0
  22. package/dist/assets/chunk-5HE753X5-DU81hkcF.js +1 -0
  23. package/dist/assets/chunk-5JV3BV7I-C2bAx0m7.js +1 -0
  24. package/dist/assets/chunk-5TONJI2A-BExIIgWY.js +2 -0
  25. package/dist/assets/chunk-5VM5RSS4-C3XB05yh.js +15 -0
  26. package/dist/assets/chunk-7BUUIJ7U-JiyRtrGu.js +1 -0
  27. package/dist/assets/chunk-BIQX33UG-BZRV20pI.js +1 -0
  28. package/dist/assets/chunk-C7G6YPKG-AsyDSQ9j.js +1 -0
  29. package/dist/assets/chunk-CQNSW5MT-iN-fiXVA.js +1 -0
  30. package/dist/assets/chunk-CYSBUYHQ-DhC_TRzI.js +1 -0
  31. package/dist/assets/chunk-DseTPa7n.js +1 -0
  32. package/dist/assets/chunk-EMLP6XTP-QFLK8R6d.js +1 -0
  33. package/dist/assets/chunk-EX3LRPZG-DzUwhkCz.js +231 -0
  34. package/dist/assets/chunk-FWX5IMBZ-DFelpZBy.js +2 -0
  35. package/dist/assets/chunk-HOUHSVGY-BMPwFEwN.js +1 -0
  36. package/dist/assets/chunk-ICXQ74PX-DH8NJMok.js +2 -0
  37. package/dist/assets/chunk-JG7HCLWE-D-95AsO4.js +2 -0
  38. package/dist/assets/chunk-JWPE2WC7-DIaTv8kx.js +1 -0
  39. package/dist/assets/chunk-KEIR6QF5-A3jEqwzn.js +161 -0
  40. package/dist/assets/chunk-MOJQB5TN-BPFLzWgj.js +88 -0
  41. package/dist/assets/chunk-MOZMSUNE-DR_fzxG0.js +1 -0
  42. package/dist/assets/chunk-OGEWGWER-TIcEoPPH.js +1 -0
  43. package/dist/assets/chunk-OSBZ3O6U-BALNvSfG.js +1 -0
  44. package/dist/assets/chunk-PUDLZKDR-I-EXhf_k.js +156 -0
  45. package/dist/assets/chunk-Q4XR5HBZ-uvIGstBS.js +70 -0
  46. package/dist/assets/chunk-QBLGF6JB-r-TjLdxa.js +1 -0
  47. package/dist/assets/chunk-R7FJI6CG-N-1LDgvy.js +1 -0
  48. package/dist/assets/chunk-RYQCIY6F-jxNASx65.js +1 -0
  49. package/dist/assets/chunk-U6XO7XAA-Cphbuc7q.js +2 -0
  50. package/dist/assets/chunk-V7JOEXUC-BLEuBf1s.js +206 -0
  51. package/dist/assets/chunk-VAUOI2AC-Dpcz6BhT.js +1 -0
  52. package/dist/assets/chunk-VR4S4FIN-BAUbR0ke.js +1 -0
  53. package/dist/assets/chunk-WYO6CB5R-Do6SJrnY.js +127 -0
  54. package/dist/assets/chunk-XXDRQBXY-C2QjjwYX.js +1 -0
  55. package/dist/assets/chunk-Y2CYZVJY-BGrd8tLy.js +1 -0
  56. package/dist/assets/chunk-YOTPTUD7-CQqvMTCr.js +1 -0
  57. package/dist/assets/chunk-ZGVPDNZ5-B7kWWaXv.js +62 -0
  58. package/dist/assets/chunk-ZIRB5QZD-DTU0cDNh.js +32 -0
  59. package/dist/assets/classDiagram-OUVF2IWQ-DESMaY3_.js +1 -0
  60. package/dist/assets/classDiagram-v2-EOCWNBFH-DwOlByYH.js +1 -0
  61. package/dist/assets/config-split-page-BlfvfmTm.js +1 -0
  62. package/dist/assets/confirm-dialog-Dt8Kdbgp.js +5 -0
  63. package/dist/assets/cose-bilkent-JH36ORCC-B9LrtV-q.js +1 -0
  64. package/dist/assets/cynefin-VYW2F7L2-CzjeeOq9.js +1 -0
  65. package/dist/assets/cynefinDiagram-TSTJHNR4-BfjvW4su.js +62 -0
  66. package/dist/assets/cytoscape.esm-CRW8q92n.js +321 -0
  67. package/dist/assets/dagre-DkiXZKkL.js +1 -0
  68. package/dist/assets/dagre-VKFMJZFB-B1pQKQSY.js +4 -0
  69. package/dist/assets/defaultLocale-Cdv9x6q7.js +1 -0
  70. package/dist/assets/desktop-update-config-DreMJu9z.js +1 -0
  71. package/dist/assets/diagram-FQU43EPY-cB4EZdaP.js +3 -0
  72. package/dist/assets/diagram-G47NLZAW-CtW8im1B.js +24 -0
  73. package/dist/assets/diagram-NH7WQ7WH-BDCtrCCm.js +24 -0
  74. package/dist/assets/diagram-OA4YK3LP-DEMJ42Gt.js +30 -0
  75. package/dist/assets/diagram-WEI45ONY-CdCcxSYU.js +41 -0
  76. package/dist/assets/dist-D5ycbwCo.js +41 -0
  77. package/dist/assets/dist-dw-gAfM_.js +1 -0
  78. package/dist/assets/doc-browser-C9u7N9vH.js +1 -0
  79. package/dist/assets/doc-browser-DJQ5XPsi2.js +1 -0
  80. package/dist/assets/doc-browser-context-CCdW9TJf.js +1 -0
  81. package/dist/assets/docx-preview-zis3U0Fa.js +20 -0
  82. package/dist/assets/download-CyPgglKr.js +1 -0
  83. package/dist/assets/ebnfDiagram-CCIWWBDH-DBZETBwE.js +1 -0
  84. package/dist/assets/erDiagram-Q63AITRT-y2zDXprP.js +85 -0
  85. package/dist/assets/eventmodeling-45OFAUF4-C38BqG8w.js +1 -0
  86. package/dist/assets/{external-link-CU0kzzIP.js → external-link-BahW1sl3.js} +1 -1
  87. package/dist/assets/eye-D8gGXMkD.js +1 -0
  88. package/dist/assets/flowDiagram-23GEKE2U-BoKk8RRq.js +1 -0
  89. package/dist/assets/folder-DGWqFDvE.js +1 -0
  90. package/dist/assets/{form-actions-BvCp_0lS.js → form-actions-D2imZLnU.js} +1 -1
  91. package/dist/assets/ganttDiagram-NO4QXBWP-D2DP344e.js +292 -0
  92. package/dist/assets/gitGraph-TEB2WS4Q-IsQHHJuS.js +1 -0
  93. package/dist/assets/gitGraphDiagram-IHSO6WYX-D37uiwA2.js +106 -0
  94. package/dist/assets/graphlib-BcNFQr5y.js +1 -0
  95. package/dist/assets/index-0av6WO9-.css +1 -0
  96. package/dist/assets/index-CT0iYlxV.js +2 -0
  97. package/dist/assets/info-DKCQHKI2-ofy2qlHI.js +1 -0
  98. package/dist/assets/infoDiagram-FWYZ7A6U-CG-WAIRi.js +2 -0
  99. package/dist/assets/init-D8TjIqfm.js +1 -0
  100. package/dist/assets/ishikawaDiagram-FXEZZL3T-WeCTnW0f.js +70 -0
  101. package/dist/assets/journeyDiagram-5HDEW3XC-DUFBW9Td.js +139 -0
  102. package/dist/assets/jszip.min-CP5uPNQa.js +2 -0
  103. package/dist/assets/kanban-definition-HUTT4EX6-B0lxgMpg.js +89 -0
  104. package/dist/assets/katex-wEmrEFTg.js +257 -0
  105. package/dist/assets/line-B78frP_c.js +1 -0
  106. package/dist/assets/linear-C6lVEcb2.js +1 -0
  107. package/dist/assets/loader-circle-DtKek-qG.js +1 -0
  108. package/dist/assets/logo-badge-CuPetu-v.js +1 -0
  109. package/dist/assets/map-Bdri5UL5.js +1 -0
  110. package/dist/assets/mcp-marketplace-page-4qMwFDpz.js +1 -0
  111. package/dist/assets/mcp-marketplace-page-BbxN5aZF.js +9 -0
  112. package/dist/assets/mermaid-parser.core-Cue18vLo.js +4 -0
  113. package/dist/assets/mermaid.core-CXwWRh5i.js +11 -0
  114. package/dist/assets/mindmap-definition-LN4V7U3C-CFxz0P0-.js +96 -0
  115. package/dist/assets/model-config-page-DiJcvBML.js +1 -0
  116. package/dist/assets/ordinal-Cbd8t4LB.js +1 -0
  117. package/dist/assets/packet-7NZHBO7P-Rfopfa1o.js +1 -0
  118. package/dist/assets/path-DzBnMI1r.js +1 -0
  119. package/dist/assets/pegDiagram-2B236MQR-B_Qt9zyT.js +1 -0
  120. package/dist/assets/pie-RZYD4A2V-CJR1xKEv.js +1 -0
  121. package/dist/assets/pieDiagram-ENE6RG2P-CRQdSuwS.js +39 -0
  122. package/dist/assets/plus-BwgE-Zwf.js +1 -0
  123. package/dist/assets/popover-Btp7KVc3.js +1 -0
  124. package/dist/assets/preload-helper-D4M6sveU.js +1 -0
  125. package/dist/assets/provider-models-BoUhFo9R.js +1 -0
  126. package/dist/assets/provider-scoped-model-input-DZMtoAJG.js +1 -0
  127. package/dist/assets/providers-config-page-DfiWveEF.js +1 -0
  128. package/dist/assets/quadrantDiagram-ABIIQ3AL-D5kPpttq.js +7 -0
  129. package/dist/assets/radar-I7S5WNFK-CDg8Z-1r.js +1 -0
  130. package/dist/assets/railroad-3IZDKUUU-BHnNmjxO.js +1 -0
  131. package/dist/assets/railroad-abnf-AHOZXSZD-DGtNcusW.js +1 -0
  132. package/dist/assets/railroad-ebnf-EBAXGLYW-BRFtsQmm.js +1 -0
  133. package/dist/assets/railroad-peg-LSFZ7HO6-Bj5IypCA.js +1 -0
  134. package/dist/assets/railroadDiagram-RFXS5EU6-DGh_TGlQ.js +1 -0
  135. package/dist/assets/react-BY-U3h3t.js +8 -0
  136. package/dist/assets/{refresh-cw-CILd1vAP.js → refresh-cw-Cme5qFej.js} +1 -1
  137. package/dist/assets/remote-BaVECEZw.js +1 -0
  138. package/dist/assets/requirementDiagram-TGXJPOKE-EmFph0H_.js +84 -0
  139. package/dist/assets/rotate-cw-CMRZxIkH.js +1 -0
  140. package/dist/assets/rough.esm-wJ1d3nQl.js +1 -0
  141. package/dist/assets/runtime-config-page-wygwvMB4.js +1 -0
  142. package/dist/assets/sankeyDiagram-HTMAVEWB-BXWB5Ugi.js +40 -0
  143. package/dist/assets/{save-ByJqcPTP.js → save-DNg2yJTS.js} +1 -1
  144. package/dist/assets/{search-DVIV6pfD.js → search-3ykfrAFt.js} +1 -1
  145. package/dist/assets/search-config-page-DrVKa_Ls.js +1 -0
  146. package/dist/assets/secrets-config-page-B39uQ5kF.js +3 -0
  147. package/dist/assets/security-config-BPzet76z.js +1 -0
  148. package/dist/assets/select-B5EecXJt.js +1 -0
  149. package/dist/assets/sequenceDiagram-DBY2YBRQ-ClKnO7v0.js +162 -0
  150. package/dist/assets/setting-row-Qv1Ps79v.js +1 -0
  151. package/dist/assets/{settings-2-CH5shGg_.js → settings-2-D-3um9Az.js} +1 -1
  152. package/dist/assets/sizeCapture-X5ZJPWSS-DSo-t5ou.js +1 -0
  153. package/dist/assets/{skeleton-DeX1FKw7.js → skeleton-BgkzQd4U.js} +1 -1
  154. package/dist/assets/src-Dad2ZbCt.js +1 -0
  155. package/dist/assets/stateDiagram-2N3HPSRC-BvAalBfz.js +1 -0
  156. package/dist/assets/stateDiagram-v2-6OUMAXLB-CfMU38yc.js +1 -0
  157. package/dist/assets/swimlanes-5IMT3BWC-DdVqVa1a.js +2 -0
  158. package/dist/assets/swimlanesDiagram-G3AALYLV-D9Zb9X57.js +8 -0
  159. package/dist/assets/tabs-BVD8gp3y.js +1 -0
  160. package/dist/assets/tag-chip-0qn-f3Bo.js +1 -0
  161. package/dist/assets/timeline-definition-FHXFAJF6-D4ar8IQY.js +120 -0
  162. package/dist/assets/trash-2-cnsMppRC.js +1 -0
  163. package/dist/assets/treeView-QDETBFTQ-Uy7M0WvH.js +1 -0
  164. package/dist/assets/treemap-6X3UGDF4-BJfGV7KX.js +1 -0
  165. package/dist/assets/use-config-D6yx0lb0.js +1 -0
  166. package/dist/assets/use-confirm-dialog-C_o0eQeH.js +1 -0
  167. package/dist/assets/use-viewport-layout-BWav4KCL.js +1 -0
  168. package/dist/assets/vennDiagram-L72KCM5P-CDXJ_i3p.js +34 -0
  169. package/dist/assets/wardley-OPB4EBWU-q263iF3K.js +1 -0
  170. package/dist/assets/wardleyDiagram-EHGQE667-D-dSuT_7.js +78 -0
  171. package/dist/assets/x-B3o1dWR5.js +1 -0
  172. package/dist/assets/xlsx-DUqJhg5u.js +64 -0
  173. package/dist/assets/xychartDiagram-FW5EYKEG-C9JCzsZF.js +7 -0
  174. package/dist/index.html +35 -28
  175. package/package.json +11 -6
  176. package/src/features/chat/components/chat-popover-content.tsx +1 -0
  177. package/src/features/chat/components/conversation/__tests__/chat-conversation-content.test.tsx +13 -0
  178. package/src/features/chat/components/conversation/__tests__/chat-conversation-header-section.test.tsx +21 -11
  179. package/src/features/chat/components/conversation/__tests__/chat-conversation-header.test.tsx +2 -2
  180. package/src/features/chat/components/conversation/__tests__/chat-conversation-workspace-section.test.tsx +28 -1
  181. package/src/features/chat/components/conversation/chat-conversation-content.tsx +5 -4
  182. package/src/features/chat/components/conversation/chat-conversation-header-section.tsx +9 -18
  183. package/src/features/chat/components/conversation/chat-conversation-track.tsx +35 -0
  184. package/src/features/chat/components/conversation/chat-conversation-workspace-section.tsx +2 -0
  185. package/src/features/chat/components/layout/chat-sidebar-toolbar.tsx +7 -7
  186. package/src/features/chat/components/layout/chat-sidebar-utility-menu.tsx +3 -14
  187. package/src/features/chat/features/conversation/components/__tests__/session-conversation-input.streaming.test.tsx +20 -1
  188. package/src/features/chat/features/conversation/components/session-conversation-input.tsx +97 -65
  189. package/src/features/chat/features/input/input-surface-plugins/__tests__/slash-command-plugin.test.ts +43 -0
  190. package/src/features/chat/features/input/input-surface-plugins/slash-command-plugin.utils.ts +14 -10
  191. package/src/features/chat/features/input/utils/__tests__/chat-input-bar.utils.test.ts +51 -0
  192. package/src/features/chat/features/input/utils/chat-input-bar.utils.ts +61 -21
  193. package/src/features/chat/features/message/components/__tests__/chat-message-list.container.test.tsx +42 -10
  194. package/src/features/chat/features/message/components/chat-inline-file-preview.tsx +80 -0
  195. package/src/features/chat/features/message/components/chat-message-list.container.tsx +66 -56
  196. package/src/features/chat/features/message/utils/__tests__/chat-message-file-operation.utils.test.ts +30 -0
  197. package/src/features/chat/features/message/utils/chat-message-texts.utils.ts +2 -0
  198. package/src/features/chat/features/session/components/__tests__/chat-sidebar-session-item.test.tsx +37 -0
  199. package/src/features/chat/features/session/components/chat-sidebar-project-groups.tsx +4 -3
  200. package/src/features/chat/features/session/components/chat-sidebar-session-item.tsx +3 -8
  201. package/src/features/chat/features/session/components/session-header/__tests__/chat-session-header-actions.test.tsx +65 -20
  202. package/src/features/chat/features/session/components/session-header/chat-session-header-actions.tsx +26 -28
  203. package/src/features/chat/features/session/components/session-header/chat-session-project-badge.tsx +3 -3
  204. package/src/features/chat/features/session/components/session-header/chat-session-title-switcher.tsx +3 -3
  205. package/src/features/chat/features/welcome/components/chat-welcome-project-picker.tsx +3 -3
  206. package/src/features/chat/features/welcome/components/chat-welcome-session-type-picker.tsx +3 -3
  207. package/src/features/chat/features/workspace/components/__tests__/chat-session-workspace-file-preview.test.tsx +424 -129
  208. package/src/features/chat/features/workspace/components/__tests__/chat-session-workspace-panel-content.test.tsx +113 -2
  209. package/src/features/chat/features/workspace/components/__tests__/chat-session-workspace-panel.test.tsx +3 -1
  210. package/src/features/chat/features/workspace/components/chat-session-workspace-directory-browser.tsx +197 -44
  211. package/src/features/chat/features/workspace/components/chat-session-workspace-file-breadcrumb-browser.tsx +9 -3
  212. package/src/features/chat/features/workspace/components/chat-session-workspace-file-breadcrumbs.tsx +13 -22
  213. package/src/features/chat/features/workspace/components/chat-session-workspace-file-preview.tsx +96 -194
  214. package/src/features/chat/features/workspace/components/chat-session-workspace-panel-content.tsx +239 -1
  215. package/src/features/chat/features/workspace/components/chat-session-workspace-panel-nav.tsx +61 -7
  216. package/src/features/chat/features/workspace/components/chat-session-workspace-panel.tsx +34 -8
  217. package/src/features/chat/features/workspace/components/file-content-preview/use-workspace-file-buffer.ts +42 -0
  218. package/src/features/chat/features/workspace/components/file-content-preview/workspace-document-preview-state.tsx +18 -0
  219. package/src/features/chat/features/workspace/components/file-content-preview/workspace-docx-preview.css +89 -0
  220. package/src/features/chat/features/workspace/components/file-content-preview/workspace-docx-preview.tsx +87 -0
  221. package/src/features/chat/features/workspace/components/file-content-preview/workspace-presentation-preview.tsx +82 -0
  222. package/src/features/chat/features/workspace/components/file-content-preview/workspace-spreadsheet-preview.tsx +180 -0
  223. package/src/features/chat/features/workspace/components/workspace-file-content-preview.tsx +191 -0
  224. package/src/features/chat/features/workspace/hooks/use-chat-conversation-workspace-state.ts +1 -4
  225. package/src/features/chat/features/workspace/utils/__tests__/chat-workspace-panel-view-model.utils.test.ts +157 -13
  226. package/src/features/chat/features/workspace/utils/chat-thread-workspace-session.utils.ts +151 -2
  227. package/src/features/chat/features/workspace/utils/chat-workspace-file-tab-persistence.utils.ts +17 -0
  228. package/src/features/chat/features/workspace/utils/chat-workspace-file-tab.utils.ts +66 -0
  229. package/src/features/chat/features/workspace/utils/chat-workspace-file-viewer.utils.ts +49 -0
  230. package/src/features/chat/features/workspace/utils/chat-workspace-panel-layout.utils.ts +13 -0
  231. package/src/features/chat/features/workspace/utils/chat-workspace-panel-view-model.utils.ts +196 -43
  232. package/src/features/chat/index.ts +1 -0
  233. package/src/features/chat/managers/__tests__/chat-thread-workspace.manager.test.ts +101 -0
  234. package/src/features/chat/managers/__tests__/chat-thread.manager.test.ts +48 -60
  235. package/src/features/chat/managers/chat-session-list.manager.ts +1 -0
  236. package/src/features/chat/managers/chat-thread.manager.ts +137 -162
  237. package/src/features/chat/pages/ncp-chat-page.tsx +1 -2
  238. package/src/features/chat/stores/__tests__/chat-thread.store.test.ts +87 -1
  239. package/src/features/chat/stores/chat-message-layout.store.test.ts +37 -0
  240. package/src/features/chat/stores/chat-message-layout.store.ts +41 -0
  241. package/src/features/chat/stores/chat-thread.store.ts +77 -40
  242. package/src/features/chat/types/chat-input-bar.types.ts +2 -0
  243. package/src/features/settings/pages/__tests__/appearance-settings-page.test.tsx +25 -8
  244. package/src/features/settings/pages/appearance-settings-page.tsx +92 -13
  245. package/src/index.css +36 -2
  246. package/src/shared/components/__tests__/file-type-icon.test.tsx +61 -0
  247. package/src/shared/components/common/__tests__/brand-header.test.tsx +17 -7
  248. package/src/shared/components/common/brand-header.tsx +34 -34
  249. package/src/shared/components/doc-browser/__tests__/doc-browser-context.test.tsx +19 -0
  250. package/src/shared/components/doc-browser/__tests__/doc-browser.test.tsx +16 -0
  251. package/src/shared/components/doc-browser/doc-browser-context.tsx +1 -0
  252. package/src/shared/components/doc-browser/doc-browser.tsx +11 -3
  253. package/src/shared/components/doc-browser/managers/doc-browser.manager.ts +5 -0
  254. package/src/shared/components/doc-browser/stores/doc-browser.store.ts +6 -1
  255. package/src/shared/components/doc-browser/types/doc-browser.types.ts +2 -0
  256. package/src/shared/components/doc-browser/utils/doc-browser-state.utils.ts +11 -0
  257. package/src/shared/components/file-type-icon.tsx +272 -0
  258. package/src/shared/components/resizable-right-panel/__tests__/resizable-right-panel.test.tsx +22 -0
  259. package/src/shared/components/resizable-right-panel/resizable-right-panel.tsx +17 -2
  260. package/src/shared/components/ui/tab-strip/__tests__/compact-tab-strip.test.tsx +38 -0
  261. package/src/shared/components/ui/tab-strip/compact-tab-strip.tsx +82 -2
  262. package/src/shared/hooks/use-server-path-read.ts +10 -5
  263. package/src/shared/lib/api/__tests__/server-path.utils.test.ts +28 -0
  264. package/src/shared/lib/api/types.ts +3 -3
  265. package/src/shared/lib/api/utils/server-path.utils.ts +20 -20
  266. package/src/shared/lib/i18n/locales/en-US/chat.json +25 -1
  267. package/src/shared/lib/i18n/locales/en-US/core.json +6 -0
  268. package/src/shared/lib/i18n/locales/zh-CN/chat.json +25 -1
  269. package/src/shared/lib/i18n/locales/zh-CN/core.json +6 -0
  270. package/src/shared/lib/session-project/__tests__/workspace-file-breadcrumb.test.ts +1 -4
  271. package/src/shared/lib/session-project/workspace-file-breadcrumb.utils.ts +1 -18
  272. package/dist/assets/api-iNBpppzj.js +0 -13
  273. package/dist/assets/appearance-settings-page-DMffQFR3.js +0 -1
  274. package/dist/assets/channels-list-page-CCtp0BRS.js +0 -8
  275. package/dist/assets/chat-page-H5a0dWvO.js +0 -1
  276. package/dist/assets/config-split-page-CrOJgB-Y.js +0 -1
  277. package/dist/assets/confirm-dialog-Chv50USy.js +0 -5
  278. package/dist/assets/desktop-update-config-KzTuXRf_.js +0 -1
  279. package/dist/assets/dist-BunzmC4B.js +0 -1
  280. package/dist/assets/dist-ChmGwvtK.js +0 -1
  281. package/dist/assets/doc-browser-DNRL4Zvd.js +0 -1
  282. package/dist/assets/doc-browser-Dg0pmkOg.js +0 -1
  283. package/dist/assets/doc-browser-context-BHY-0FSD.js +0 -1
  284. package/dist/assets/es2015-DQZ5JWwK.js +0 -41
  285. package/dist/assets/i18n-provider-Cu8XyZb_.js +0 -1
  286. package/dist/assets/index-CzfBdvK1.css +0 -1
  287. package/dist/assets/index-DGqHne1Q.js +0 -104
  288. package/dist/assets/loader-circle-CDSFuzmk.js +0 -1
  289. package/dist/assets/mcp-marketplace-page-BeEQK0DL.js +0 -9
  290. package/dist/assets/mcp-marketplace-page-D6idOTLp.js +0 -1
  291. package/dist/assets/model-config-page-bNNo-XyG.js +0 -1
  292. package/dist/assets/plus-D4DsdLN-.js +0 -1
  293. package/dist/assets/provider-scoped-model-input-IlgQCsoT.js +0 -1
  294. package/dist/assets/providers-config-page-wLo6CAJw.js +0 -1
  295. package/dist/assets/react-BPIsQIlO.js +0 -8
  296. package/dist/assets/remote-uKEYp7AR.js +0 -1
  297. package/dist/assets/rotate-cw-wPFPg1kH.js +0 -1
  298. package/dist/assets/runtime-config-page-DDn1sP4G.js +0 -1
  299. package/dist/assets/search-config-page-B-Cf4iiB.js +0 -1
  300. package/dist/assets/secrets-config-page-DPKsY8Iw.js +0 -3
  301. package/dist/assets/security-config-e9SSBlKr.js +0 -1
  302. package/dist/assets/select-BdgczSNw.js +0 -1
  303. package/dist/assets/setting-row-BkTVUTKx.js +0 -1
  304. package/dist/assets/side-dock-BsBvcJ9i.js +0 -3
  305. package/dist/assets/switch-Cv8Ekb3x.js +0 -1
  306. package/dist/assets/tag-chip-RB9uKcoC.js +0 -1
  307. package/dist/assets/tooltip-oQlEN-l6.js +0 -1
  308. package/dist/assets/use-config-l3W7y8Qz.js +0 -1
  309. package/dist/assets/use-confirm-dialog-BAxaVOJI.js +0 -1
  310. package/dist/assets/x-BBMffEN0.js +0 -1
  311. package/src/features/chat/components/layout/__tests__/chat-sidebar-utility-menu.test.tsx +0 -55
  312. /package/dist/assets/{config-hints-C_iP1zl_.js → config-hints-LxG80Uj5.js} +0 -0
  313. /package/dist/assets/{host-capabilities-9bB_998Z.js → host-capabilities-CFbAfrrA.js} +0 -0
  314. /package/dist/assets/{middleware-BILWbJE4.js → middleware-CAXxl-af.js} +0 -0
@@ -1,12 +1,26 @@
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,
5
+ ChatWorkspacePanelKind,
4
6
  ChatWorkspaceFileTab,
5
7
  ChatWorkspaceSideChatDraft,
6
8
  } from "@/features/chat/stores/chat-thread.store";
7
9
  import { t } from "@/shared/lib/i18n";
10
+ import {
11
+ resolveAlternateWorkspaceFileViewer,
12
+ resolveWorkspaceFileViewer,
13
+ type ChatWorkspaceFileViewer,
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";
8
16
 
9
17
  export type WorkspaceSelection =
18
+ | {
19
+ kind: "overview";
20
+ }
21
+ | {
22
+ kind: "child-sessions";
23
+ }
10
24
  | {
11
25
  kind: "child-session";
12
26
  tab: ResolvedChildSessionTab;
@@ -15,6 +29,9 @@ export type WorkspaceSelection =
15
29
  kind: "side-chat-draft";
16
30
  draft: ChatWorkspaceSideChatDraft;
17
31
  }
32
+ | {
33
+ kind: "project-files";
34
+ }
18
35
  | {
19
36
  kind: "file";
20
37
  file: ChatWorkspaceFileTab;
@@ -25,13 +42,20 @@ export type WorkspaceSelection =
25
42
 
26
43
  export type WorkspaceTabViewModel = {
27
44
  key: string;
28
- kind: "child-session" | "side-chat-draft" | "file" | "cron";
45
+ kind: ChatWorkspacePanelKind;
29
46
  title: string;
30
47
  tooltip: string;
31
48
  active: boolean;
32
49
  agentId?: string | null;
50
+ fileName?: string | null;
33
51
  showUnreadDot?: boolean;
34
52
  viewMode?: "preview" | "diff";
53
+ isRenderedPreview?: boolean;
54
+ alternateViewerAction?: {
55
+ label: string;
56
+ viewer: ChatWorkspaceFileViewer;
57
+ onSelect: () => void;
58
+ } | null;
35
59
  onSelect: () => void;
36
60
  onClose?: () => void;
37
61
  };
@@ -42,13 +66,12 @@ export function readWorkspaceFileTitle(file: ChatWorkspaceFileTab): string {
42
66
  }
43
67
 
44
68
  export function resolveWorkspaceSelection(params: {
45
- activePanelKind?: "child-session" | "side-chat-draft" | "file" | "cron" | null;
69
+ activePanelKind?: ChatWorkspacePanelKind | null;
46
70
  activeChildSessionKey: string | null;
47
71
  activeSideChatDraft: ChatWorkspaceSideChatDraft | null;
48
72
  activeWorkspaceFileKey: string | null;
49
73
  childSessionTabs: ResolvedChildSessionTab[];
50
74
  workspaceFileTabs: readonly ChatWorkspaceFileTab[];
51
- sessionCronJobCount: number;
52
75
  }): WorkspaceSelection | null {
53
76
  const {
54
77
  activePanelKind,
@@ -57,10 +80,21 @@ export function resolveWorkspaceSelection(params: {
57
80
  activeWorkspaceFileKey,
58
81
  childSessionTabs,
59
82
  workspaceFileTabs,
60
- sessionCronJobCount,
61
83
  } = params;
62
84
 
63
- if (activePanelKind === "cron" && sessionCronJobCount > 0) {
85
+ if (activePanelKind === "overview") {
86
+ return { kind: "overview" };
87
+ }
88
+
89
+ if (activePanelKind === "child-sessions") {
90
+ return { kind: "child-sessions" };
91
+ }
92
+
93
+ if (activePanelKind === "project-files") {
94
+ return { kind: "project-files" };
95
+ }
96
+
97
+ if (activePanelKind === "cron") {
64
98
  return { kind: "cron" };
65
99
  }
66
100
 
@@ -116,38 +150,128 @@ export function resolveWorkspaceSelection(params: {
116
150
  };
117
151
  }
118
152
 
119
- if (sessionCronJobCount > 0) {
120
- return { kind: "cron" };
121
- }
122
-
123
153
  return null;
124
154
  }
125
155
 
126
- export function buildWorkspaceTabsViewModel(params: {
156
+ type WorkspaceTabsViewModelParams = {
127
157
  resolvedChildTabs: ResolvedChildSessionTab[];
128
158
  activeSideChatDraft: ChatWorkspaceSideChatDraft | null;
159
+ closedWorkspaceTabEntries: readonly ChatWorkspaceNavigationEntry[];
129
160
  workspaceFileTabs: readonly ChatWorkspaceFileTab[];
130
- sessionCronJobCount: number;
131
161
  activeSelection: WorkspaceSelection | null;
132
162
  optimisticReadAtBySessionKey: Record<string, string>;
133
163
  onSelectSession: (sessionKey: string) => void;
134
164
  onSelectFile: (fileKey: string) => void;
135
- onCloseFile: (fileKey: string) => void;
165
+ onOpenFileViewer: (fileKey: string, viewer: ChatWorkspaceFileViewer) => void;
166
+ onCloseTab: (entry: ChatWorkspaceNavigationEntry) => void;
167
+ onSelectOverview: () => void;
168
+ onSelectChildSessions: () => void;
169
+ onSelectProjectFiles: () => void;
136
170
  onSelectCronJobs: () => void;
137
- }): WorkspaceTabViewModel[] {
171
+ };
172
+
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[] = [
201
+ {
202
+ key: "overview",
203
+ kind: "overview",
204
+ title: t("chatWorkspaceOverview"),
205
+ tooltip: t("chatWorkspaceOverview"),
206
+ active: activeSelection?.kind === "overview",
207
+ onSelect: onSelectOverview,
208
+ },
209
+ {
210
+ key: "child-sessions",
211
+ kind: "child-sessions",
212
+ title: t("chatWorkspaceChildSessions"),
213
+ tooltip: t("chatWorkspaceChildSessions"),
214
+ active: activeSelection?.kind === "child-sessions",
215
+ onSelect: onSelectChildSessions,
216
+ onClose: () => onCloseTab({ kind: "child-sessions" }),
217
+ },
218
+ {
219
+ key: "cron:session",
220
+ kind: "cron",
221
+ title: t("chatWorkspaceSessionCronJobs"),
222
+ tooltip: t("chatWorkspaceSessionCronJobs"),
223
+ active: activeSelection?.kind === "cron",
224
+ onSelect: onSelectCronJobs,
225
+ onClose: () => onCloseTab({ kind: "cron" }),
226
+ },
227
+ {
228
+ key: "project-files",
229
+ kind: "project-files",
230
+ title: t("chatWorkspaceProjectFiles"),
231
+ tooltip: t("chatWorkspaceProjectFiles"),
232
+ active: activeSelection?.kind === "project-files",
233
+ onSelect: onSelectProjectFiles,
234
+ onClose: () => onCloseTab({ kind: "project-files" }),
235
+ },
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[] {
138
248
  const {
139
249
  activeSideChatDraft,
250
+ closedWorkspaceTabEntries,
140
251
  resolvedChildTabs,
141
252
  workspaceFileTabs,
142
- sessionCronJobCount,
143
253
  activeSelection,
144
254
  optimisticReadAtBySessionKey,
145
255
  onSelectSession,
146
256
  onSelectFile,
147
- onCloseFile,
257
+ onOpenFileViewer,
258
+ onCloseTab,
259
+ onSelectOverview,
260
+ onSelectChildSessions,
261
+ onSelectProjectFiles,
148
262
  onSelectCronJobs,
149
263
  } = params;
150
264
 
265
+ const workspacePages = buildWorkspacePageTabs({
266
+ activeSelection,
267
+ closedWorkspaceTabEntries,
268
+ onCloseTab,
269
+ onSelectChildSessions,
270
+ onSelectCronJobs,
271
+ onSelectOverview,
272
+ onSelectProjectFiles,
273
+ });
274
+
151
275
  const sideChatDraftTabs = activeSideChatDraft
152
276
  ? [
153
277
  {
@@ -159,11 +283,23 @@ export function buildWorkspaceTabsViewModel(params: {
159
283
  activeSelection?.kind === "side-chat-draft" &&
160
284
  activeSelection.draft.draftKey === activeSideChatDraft.draftKey,
161
285
  onSelect: () => undefined,
286
+ onClose: () => onCloseTab({
287
+ kind: "side-chat-draft",
288
+ key: activeSideChatDraft.draftKey,
289
+ }),
162
290
  },
163
291
  ]
164
292
  : [];
165
293
 
166
- 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) => {
167
303
  const optimisticReadAt = optimisticReadAtBySessionKey[tab.sessionKey];
168
304
  const effectiveReadAt =
169
305
  optimisticReadAt && tab.readAt
@@ -190,34 +326,51 @@ export function buildWorkspaceTabsViewModel(params: {
190
326
  runStatus: tab.runStatus,
191
327
  }),
192
328
  onSelect: () => onSelectSession(tab.sessionKey),
329
+ onClose: () => onCloseTab({
330
+ kind: "child-session",
331
+ key: tab.sessionKey,
332
+ }),
333
+ };
334
+ });
335
+
336
+ const fileTabs = workspaceFileTabs.map((file) => {
337
+ const viewer = file.viewMode === "preview"
338
+ ? resolveWorkspaceFileViewer(file.path, file.previewViewer)
339
+ : null;
340
+ const alternateViewer = file.viewMode === "preview"
341
+ ? resolveAlternateWorkspaceFileViewer(file.path, file.previewViewer)
342
+ : null;
343
+ const fileTitle = readWorkspaceFileTitle(file);
344
+ return {
345
+ key: `file:${file.key}`,
346
+ kind: "file" as const,
347
+ title: viewer === "rendered"
348
+ ? `${t("chatWorkspacePreview")}: ${fileTitle}`
349
+ : fileTitle,
350
+ tooltip: file.path,
351
+ fileName: file.path,
352
+ viewMode: file.viewMode,
353
+ isRenderedPreview: viewer === "rendered",
354
+ alternateViewerAction: alternateViewer
355
+ ? {
356
+ label: alternateViewer === "rendered"
357
+ ? t("chatWorkspaceOpenPreview")
358
+ : t("chatWorkspaceOpenSource"),
359
+ viewer: alternateViewer,
360
+ onSelect: () => onOpenFileViewer(file.key, alternateViewer),
361
+ }
362
+ : null,
363
+ active:
364
+ activeSelection?.kind === "file" && activeSelection.file.key === file.key,
365
+ onSelect: () => onSelectFile(file.key),
366
+ onClose: () => onCloseTab({ kind: "file", key: file.key }),
193
367
  };
194
368
  });
195
369
 
196
- const fileTabs = workspaceFileTabs.map((file) => ({
197
- key: `file:${file.key}`,
198
- kind: "file" as const,
199
- title: readWorkspaceFileTitle(file),
200
- tooltip: file.path,
201
- viewMode: file.viewMode,
202
- active:
203
- activeSelection?.kind === "file" && activeSelection.file.key === file.key,
204
- onSelect: () => onSelectFile(file.key),
205
- onClose: () => onCloseFile(file.key),
206
- }));
207
-
208
- const cronTab =
209
- sessionCronJobCount > 0
210
- ? [
211
- {
212
- key: "cron:session",
213
- kind: "cron" as const,
214
- title: t("chatWorkspaceSessionCronJobs"),
215
- tooltip: t("chatWorkspaceSessionCronJobs"),
216
- active: activeSelection?.kind === "cron",
217
- onSelect: onSelectCronJobs,
218
- },
219
- ]
220
- : [];
221
-
222
- return [...sideChatDraftTabs, ...childTabs, ...fileTabs, ...cronTab];
370
+ return [
371
+ ...workspacePages,
372
+ ...sideChatDraftTabs,
373
+ ...childTabs,
374
+ ...fileTabs,
375
+ ];
223
376
  }
@@ -6,6 +6,7 @@ export { ChatPresenter } from "./presenters/chat.presenter";
6
6
  export { ChatDraftIntentManager } from "./managers/chat-draft-intent.manager";
7
7
  export { useChatSessionListStore } from "./stores/chat-session-list.store";
8
8
  export { useChatThreadStore } from "./stores/chat-thread.store";
9
+ export { useChatMessageLayoutStore } from "./stores/chat-message-layout.store";
9
10
  export { useNcpChatSessionTypes } from "./features/session-type/hooks/use-ncp-chat-session-types";
10
11
  export {
11
12
  buildSessionTypeOptions,
@@ -0,0 +1,101 @@
1
+ import { beforeEach, describe, expect, it, vi } from 'vitest';
2
+ import { ChatThreadManager } from '@/features/chat/managers/chat-thread.manager';
3
+ import { useChatSessionListStore } from '@/features/chat/stores/chat-session-list.store';
4
+ import { useChatThreadStore } from '@/features/chat/stores/chat-thread.store';
5
+
6
+ function createUiManager(): ConstructorParameters<typeof ChatThreadManager>[0] {
7
+ return {
8
+ goToSession: vi.fn(),
9
+ } as unknown as ConstructorParameters<typeof ChatThreadManager>[0];
10
+ }
11
+
12
+ describe('ChatThreadManager workspace pages', () => {
13
+ beforeEach(() => {
14
+ useChatSessionListStore.setState({
15
+ snapshot: {
16
+ ...useChatSessionListStore.getState().snapshot,
17
+ selectedSessionKey: 'parent-session-1',
18
+ },
19
+ });
20
+ useChatThreadStore.setState({
21
+ snapshot: {
22
+ ...useChatThreadStore.getState().snapshot,
23
+ workspacePanelParentKey: null,
24
+ activeWorkspacePanelKind: null,
25
+ activeChildSessionKey: null,
26
+ activeWorkspaceFileKey: null,
27
+ closedWorkspaceTabEntries: [],
28
+ workspaceNavigationHistory: [],
29
+ workspaceNavigationHistoryIndex: 0,
30
+ },
31
+ });
32
+ });
33
+
34
+ it('opens the stable pages as navigable session pages', () => {
35
+ const onWorkspacePanelOpened = vi.fn();
36
+ const manager = new ChatThreadManager(
37
+ createUiManager(),
38
+ {} as ConstructorParameters<typeof ChatThreadManager>[1],
39
+ onWorkspacePanelOpened,
40
+ );
41
+
42
+ manager.openWorkspaceOverview('parent-session-1');
43
+ manager.openChildSessions('parent-session-1');
44
+ manager.openProjectFiles('parent-session-1');
45
+
46
+ expect(useChatThreadStore.getState().snapshot).toMatchObject({
47
+ workspacePanelParentKey: 'parent-session-1',
48
+ activeWorkspacePanelKind: 'project-files',
49
+ workspaceNavigationHistory: [
50
+ { kind: 'overview' },
51
+ { kind: 'child-sessions' },
52
+ { kind: 'project-files' },
53
+ ],
54
+ workspaceNavigationHistoryIndex: 2,
55
+ });
56
+ expect(onWorkspacePanelOpened).toHaveBeenCalledTimes(3);
57
+ });
58
+
59
+ it('toggles the current session workspace from the stable header action', () => {
60
+ const manager = new ChatThreadManager(
61
+ createUiManager(),
62
+ {} as ConstructorParameters<typeof ChatThreadManager>[1],
63
+ );
64
+
65
+ manager.toggleWorkspacePanel('parent-session-1');
66
+ expect(useChatThreadStore.getState().snapshot).toMatchObject({
67
+ workspacePanelParentKey: 'parent-session-1',
68
+ activeWorkspacePanelKind: 'overview',
69
+ });
70
+
71
+ manager.toggleWorkspacePanel('parent-session-1');
72
+ expect(useChatThreadStore.getState().snapshot).toMatchObject({
73
+ workspacePanelParentKey: null,
74
+ activeWorkspacePanelKind: null,
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
+ });
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
  },
@@ -315,7 +316,7 @@ describe('ChatThreadManager', () => {
315
316
  expect(useChatThreadStore.getState().snapshot.workspaceFileTabs).toEqual(
316
317
  expect.arrayContaining([
317
318
  expect.objectContaining({
318
- key: 'parent-session-1::preview::README.md',
319
+ key: 'parent-session-1::preview:rendered::README.md',
319
320
  path: 'README.md',
320
321
  viewMode: 'preview',
321
322
  }),
@@ -331,7 +332,31 @@ describe('ChatThreadManager', () => {
331
332
  });
332
333
 
333
334
  describe('ChatThreadManager workspace file tabs', () => {
334
- it('opens HTML previews in source mode when the viewer is omitted or auto', () => {
335
+ it.each([
336
+ { path: 'demo.html', previewViewer: undefined, expectedViewer: 'source' },
337
+ { path: 'auto.html', previewViewer: 'auto' as const, expectedViewer: 'source' },
338
+ { path: 'README.md', previewViewer: undefined, expectedViewer: 'rendered' },
339
+ { path: 'report.docx', previewViewer: 'auto' as const, expectedViewer: 'auto' },
340
+ ])('normalizes the viewer for $path', ({ path, previewViewer, expectedViewer }) => {
341
+ const manager = new ChatThreadManager(
342
+ createUiManager(),
343
+ {} as ConstructorParameters<typeof ChatThreadManager>[1],
344
+ );
345
+
346
+ manager.openFilePreview({
347
+ path,
348
+ label: path,
349
+ viewMode: 'preview',
350
+ previewViewer,
351
+ });
352
+
353
+ expect(useChatThreadStore.getState().snapshot.workspaceFileTabs[0]).toMatchObject({
354
+ path,
355
+ previewViewer: expectedViewer,
356
+ });
357
+ });
358
+
359
+ it('opens source and rendered previews for the same file as adjacent workspace tabs', () => {
335
360
  const manager = new ChatThreadManager(
336
361
  createUiManager(),
337
362
  {} as ConstructorParameters<typeof ChatThreadManager>[1],
@@ -341,10 +366,12 @@ describe('ChatThreadManager workspace file tabs', () => {
341
366
  path: 'demo.html',
342
367
  label: 'demo.html',
343
368
  viewMode: 'preview',
369
+ previewViewer: 'source',
344
370
  });
371
+ manager.openWorkspaceFileViewer('parent-session-1::preview::demo.html');
345
372
 
346
373
  expect(useChatThreadStore.getState().snapshot).toMatchObject({
347
- activeWorkspaceFileKey: 'parent-session-1::preview::demo.html',
374
+ activeWorkspaceFileKey: 'parent-session-1::preview:rendered::demo.html',
348
375
  workspaceFileTabs: [
349
376
  {
350
377
  key: 'parent-session-1::preview::demo.html',
@@ -352,67 +379,25 @@ describe('ChatThreadManager workspace file tabs', () => {
352
379
  viewMode: 'preview',
353
380
  previewViewer: 'source',
354
381
  },
355
- ],
356
- });
357
-
358
- manager.openFilePreview({
359
- path: 'auto.html',
360
- label: 'auto.html',
361
- viewMode: 'preview',
362
- previewViewer: 'auto',
363
- });
364
-
365
- expect(useChatThreadStore.getState().snapshot).toMatchObject({
366
- activeWorkspaceFileKey: 'parent-session-1::preview::auto.html',
367
- workspaceFileTabs: [
368
382
  {
369
- key: 'parent-session-1::preview::auto.html',
370
- path: 'auto.html',
383
+ key: 'parent-session-1::preview:rendered::demo.html',
384
+ path: 'demo.html',
371
385
  viewMode: 'preview',
372
- previewViewer: 'source',
386
+ previewViewer: 'rendered',
373
387
  },
374
- expect.objectContaining({
375
- key: 'parent-session-1::preview::demo.html',
376
- }),
377
388
  ],
378
389
  });
379
- });
380
390
 
381
- it('keeps source and rendered previews for the same file as separate workspace tabs', () => {
382
- const manager = new ChatThreadManager(
383
- createUiManager(),
384
- {} as ConstructorParameters<typeof ChatThreadManager>[1],
385
- );
386
-
387
- manager.openFilePreview({
388
- path: 'demo.html',
389
- label: 'demo.html',
390
- viewMode: 'preview',
391
- previewViewer: 'source',
392
- });
393
- manager.openFilePreview({
394
- path: 'demo.html',
395
- label: 'demo.html',
396
- viewMode: 'preview',
397
- previewViewer: 'rendered',
398
- });
391
+ manager.openWorkspaceFileViewer('parent-session-1::preview::demo.html');
392
+ expect(useChatThreadStore.getState().snapshot.workspaceFileTabs).toHaveLength(2);
399
393
 
394
+ manager.openWorkspaceFileViewer('parent-session-1::preview:rendered::demo.html');
400
395
  expect(useChatThreadStore.getState().snapshot).toMatchObject({
401
- activeWorkspaceFileKey: 'parent-session-1::preview:rendered::demo.html',
402
- workspaceFileTabs: [
403
- {
404
- key: 'parent-session-1::preview:rendered::demo.html',
405
- path: 'demo.html',
406
- viewMode: 'preview',
407
- previewViewer: 'rendered',
408
- },
409
- {
410
- key: 'parent-session-1::preview::demo.html',
411
- path: 'demo.html',
412
- viewMode: 'preview',
413
- previewViewer: 'source',
414
- },
415
- ],
396
+ activeWorkspaceFileKey: 'parent-session-1::preview::demo.html',
397
+ workspaceFileTabs: expect.arrayContaining([
398
+ expect.objectContaining({ previewViewer: 'source' }),
399
+ expect.objectContaining({ previewViewer: 'rendered' }),
400
+ ]),
416
401
  });
417
402
  });
418
403
  });
@@ -443,14 +428,14 @@ describe('ChatThreadManager workspace navigation', () => {
443
428
 
444
429
  expect(useChatThreadStore.getState().snapshot.workspaceNavigationHistory).toEqual([
445
430
  { kind: 'child-session', key: 'child-session-1' },
446
- { kind: 'file', key: 'parent-session-1::preview::README.md' },
431
+ { kind: 'file', key: 'parent-session-1::preview:rendered::README.md' },
447
432
  { kind: 'cron' },
448
433
  ]);
449
434
 
450
435
  manager.goBackWorkspacePanel();
451
436
  expect(useChatThreadStore.getState().snapshot).toMatchObject({
452
437
  activeWorkspacePanelKind: 'file',
453
- activeWorkspaceFileKey: 'parent-session-1::preview::README.md',
438
+ activeWorkspaceFileKey: 'parent-session-1::preview:rendered::README.md',
454
439
  workspaceNavigationHistoryIndex: 1,
455
440
  });
456
441
 
@@ -464,7 +449,7 @@ describe('ChatThreadManager workspace navigation', () => {
464
449
  manager.goForwardWorkspacePanel();
465
450
  expect(useChatThreadStore.getState().snapshot).toMatchObject({
466
451
  activeWorkspacePanelKind: 'file',
467
- activeWorkspaceFileKey: 'parent-session-1::preview::README.md',
452
+ activeWorkspaceFileKey: 'parent-session-1::preview:rendered::README.md',
468
453
  workspaceNavigationHistoryIndex: 1,
469
454
  });
470
455
  });
@@ -520,7 +505,10 @@ describe('ChatThreadManager workspace navigation', () => {
520
505
  label: 'README.md',
521
506
  viewMode: 'preview',
522
507
  });
523
- manager.closeWorkspaceFile('parent-session-1::preview::README.md');
508
+ manager.closeWorkspaceTab({
509
+ kind: 'file',
510
+ key: 'parent-session-1::preview:rendered::README.md',
511
+ });
524
512
 
525
513
  expect(useChatThreadStore.getState().snapshot).toMatchObject({
526
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
  });