@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
@@ -4,6 +4,8 @@ import {
4
4
  buildWorkspaceTabsViewModel,
5
5
  resolveWorkspaceSelection,
6
6
  } from "@/features/chat/features/workspace/utils/chat-workspace-panel-view-model.utils";
7
+ import { createWorkspaceFileTab } from "@/features/chat/features/workspace/utils/chat-workspace-file-tab.utils";
8
+ import { t } from "@/shared/lib/i18n";
7
9
 
8
10
  function createChildTab(
9
11
  overrides: Partial<ResolvedChildSessionTab> = {},
@@ -26,6 +28,19 @@ function createChildTab(
26
28
  }
27
29
 
28
30
  describe("resolveWorkspaceSelection", () => {
31
+ it("keeps the explicit workspace overview even when no resources exist", () => {
32
+ expect(
33
+ resolveWorkspaceSelection({
34
+ activePanelKind: "overview",
35
+ activeChildSessionKey: null,
36
+ activeSideChatDraft: null,
37
+ activeWorkspaceFileKey: null,
38
+ childSessionTabs: [],
39
+ workspaceFileTabs: [],
40
+ }),
41
+ ).toEqual({ kind: "overview" });
42
+ });
43
+
29
44
  it("honors the explicit active panel kind before fallback order", () => {
30
45
  const childTab = createChildTab();
31
46
  const fileTab = {
@@ -43,7 +58,6 @@ describe("resolveWorkspaceSelection", () => {
43
58
  activeWorkspaceFileKey: fileTab.key,
44
59
  childSessionTabs: [childTab],
45
60
  workspaceFileTabs: [fileTab],
46
- sessionCronJobCount: 1,
47
61
  }),
48
62
  ).toMatchObject({
49
63
  kind: "file",
@@ -62,7 +76,6 @@ describe("resolveWorkspaceSelection", () => {
62
76
  activeWorkspaceFileKey: null,
63
77
  childSessionTabs: [childTab],
64
78
  workspaceFileTabs: [],
65
- sessionCronJobCount: 1,
66
79
  }),
67
80
  ).toMatchObject({
68
81
  kind: "child-session",
@@ -84,7 +97,6 @@ describe("resolveWorkspaceSelection", () => {
84
97
  activeWorkspaceFileKey: null,
85
98
  childSessionTabs: [createChildTab()],
86
99
  workspaceFileTabs: [],
87
- sessionCronJobCount: 0,
88
100
  }),
89
101
  ).toMatchObject({
90
102
  kind: "side-chat-draft",
@@ -93,7 +105,104 @@ describe("resolveWorkspaceSelection", () => {
93
105
  });
94
106
  });
95
107
 
108
+ describe("createWorkspaceFileTab", () => {
109
+ it("uses source view for located Markdown unless rendered view is explicit", () => {
110
+ const locatedSource = createWorkspaceFileTab(
111
+ {
112
+ path: "README.md",
113
+ viewMode: "preview",
114
+ line: 12,
115
+ },
116
+ "parent-1",
117
+ );
118
+ const explicitRendered = createWorkspaceFileTab(
119
+ {
120
+ path: "README.md",
121
+ viewMode: "preview",
122
+ previewViewer: "rendered",
123
+ line: 12,
124
+ },
125
+ "parent-1",
126
+ );
127
+
128
+ expect(locatedSource).toMatchObject({
129
+ previewViewer: "source",
130
+ line: 12,
131
+ });
132
+ expect(explicitRendered).toMatchObject({
133
+ previewViewer: "rendered",
134
+ line: 12,
135
+ });
136
+ });
137
+ });
138
+
96
139
  describe("buildWorkspaceTabsViewModel", () => {
140
+ it("keeps overview fixed while every other visible workspace tab is closable", () => {
141
+ const childTab = createChildTab();
142
+ const draft = {
143
+ draftKey: "draft-1",
144
+ parentSessionKey: "parent-1",
145
+ };
146
+ const fileTab = {
147
+ key: "file-1",
148
+ parentSessionKey: "parent-1",
149
+ path: "README.md",
150
+ viewMode: "preview" as const,
151
+ };
152
+ const onCloseTab = vi.fn();
153
+ const tabs = buildWorkspaceTabsViewModel({
154
+ resolvedChildTabs: [childTab],
155
+ activeSideChatDraft: draft,
156
+ closedWorkspaceTabEntries: [],
157
+ workspaceFileTabs: [fileTab],
158
+ activeSelection: { kind: "overview" },
159
+ optimisticReadAtBySessionKey: {},
160
+ onSelectSession: vi.fn(),
161
+ onSelectFile: vi.fn(),
162
+ onOpenFileViewer: vi.fn(),
163
+ onCloseTab,
164
+ onSelectOverview: vi.fn(),
165
+ onSelectChildSessions: vi.fn(),
166
+ onSelectProjectFiles: vi.fn(),
167
+ onSelectCronJobs: vi.fn(),
168
+ });
169
+
170
+ expect(tabs.find((tab) => tab.kind === "overview")?.onClose).toBeUndefined();
171
+ expect(
172
+ tabs.filter((tab) => tab.kind !== "overview").every((tab) => tab.onClose),
173
+ ).toBe(true);
174
+ tabs.find((tab) => tab.kind === "cron")?.onClose?.();
175
+ expect(onCloseTab).toHaveBeenCalledWith({ kind: "cron" });
176
+ });
177
+
178
+ it("does not project workspace tabs that the user closed", () => {
179
+ const tabs = buildWorkspaceTabsViewModel({
180
+ resolvedChildTabs: [createChildTab()],
181
+ activeSideChatDraft: null,
182
+ closedWorkspaceTabEntries: [
183
+ { kind: "project-files" },
184
+ { kind: "child-session", key: "child-1" },
185
+ ],
186
+ workspaceFileTabs: [],
187
+ activeSelection: { kind: "overview" },
188
+ optimisticReadAtBySessionKey: {},
189
+ onSelectSession: vi.fn(),
190
+ onSelectFile: vi.fn(),
191
+ onOpenFileViewer: vi.fn(),
192
+ onCloseTab: vi.fn(),
193
+ onSelectOverview: vi.fn(),
194
+ onSelectChildSessions: vi.fn(),
195
+ onSelectProjectFiles: vi.fn(),
196
+ onSelectCronJobs: vi.fn(),
197
+ });
198
+
199
+ expect(tabs.map((tab) => tab.key)).toEqual([
200
+ "overview",
201
+ "child-sessions",
202
+ "cron:session",
203
+ ]);
204
+ });
205
+
97
206
  it("builds active and unread state from resolved tabs", () => {
98
207
  const childTab = createChildTab({
99
208
  lastMessageAt: "2026-06-09T10:00:00.000Z",
@@ -104,17 +213,21 @@ describe("buildWorkspaceTabsViewModel", () => {
104
213
  const tabs = buildWorkspaceTabsViewModel({
105
214
  resolvedChildTabs: [childTab],
106
215
  activeSideChatDraft: null,
216
+ closedWorkspaceTabEntries: [],
107
217
  workspaceFileTabs: [],
108
- sessionCronJobCount: 0,
109
218
  activeSelection: null,
110
219
  optimisticReadAtBySessionKey: {},
111
220
  onSelectSession,
112
221
  onSelectFile: vi.fn(),
113
- onCloseFile: vi.fn(),
222
+ onOpenFileViewer: vi.fn(),
223
+ onCloseTab: vi.fn(),
224
+ onSelectOverview: vi.fn(),
225
+ onSelectChildSessions: vi.fn(),
226
+ onSelectProjectFiles: vi.fn(),
114
227
  onSelectCronJobs: vi.fn(),
115
228
  });
116
229
 
117
- expect(tabs[0]).toMatchObject({
230
+ expect(tabs[4]).toMatchObject({
118
231
  key: "child:child-1",
119
232
  kind: "child-session",
120
233
  title: "Child session",
@@ -122,7 +235,7 @@ describe("buildWorkspaceTabsViewModel", () => {
122
235
  showUnreadDot: true,
123
236
  });
124
237
 
125
- tabs[0]?.onSelect();
238
+ tabs[4]?.onSelect();
126
239
  expect(onSelectSession).toHaveBeenCalledWith("child-1");
127
240
  });
128
241
 
@@ -136,8 +249,8 @@ describe("buildWorkspaceTabsViewModel", () => {
136
249
  const tabs = buildWorkspaceTabsViewModel({
137
250
  resolvedChildTabs: [childTab],
138
251
  activeSideChatDraft: draft,
252
+ closedWorkspaceTabEntries: [],
139
253
  workspaceFileTabs: [],
140
- sessionCronJobCount: 0,
141
254
  activeSelection: {
142
255
  kind: "side-chat-draft",
143
256
  draft,
@@ -145,15 +258,23 @@ describe("buildWorkspaceTabsViewModel", () => {
145
258
  optimisticReadAtBySessionKey: {},
146
259
  onSelectSession: vi.fn(),
147
260
  onSelectFile: vi.fn(),
148
- onCloseFile: vi.fn(),
261
+ onOpenFileViewer: vi.fn(),
262
+ onCloseTab: vi.fn(),
263
+ onSelectOverview: vi.fn(),
264
+ onSelectChildSessions: vi.fn(),
265
+ onSelectProjectFiles: vi.fn(),
149
266
  onSelectCronJobs: vi.fn(),
150
267
  });
151
268
 
152
269
  expect(tabs.map((tab) => tab.key)).toEqual([
270
+ "overview",
271
+ "child-sessions",
272
+ "cron:session",
273
+ "project-files",
153
274
  "side-chat-draft:draft-1",
154
275
  "child:child-1",
155
276
  ]);
156
- expect(tabs[0]).toMatchObject({
277
+ expect(tabs[4]).toMatchObject({
157
278
  kind: "side-chat-draft",
158
279
  active: true,
159
280
  });
@@ -177,11 +298,12 @@ describe("buildWorkspaceTabsViewModel", () => {
177
298
  previewViewer: "source" as const,
178
299
  };
179
300
 
301
+ const onOpenFileViewer = vi.fn();
180
302
  const tabs = buildWorkspaceTabsViewModel({
181
303
  resolvedChildTabs: [],
182
304
  activeSideChatDraft: null,
305
+ closedWorkspaceTabEntries: [],
183
306
  workspaceFileTabs: [sourceTab, renderedTab],
184
- sessionCronJobCount: 0,
185
307
  activeSelection: {
186
308
  kind: "file",
187
309
  file: sourceTab,
@@ -189,19 +311,41 @@ describe("buildWorkspaceTabsViewModel", () => {
189
311
  optimisticReadAtBySessionKey: {},
190
312
  onSelectSession: vi.fn(),
191
313
  onSelectFile: vi.fn(),
192
- onCloseFile: vi.fn(),
314
+ onOpenFileViewer,
315
+ onCloseTab: vi.fn(),
316
+ onSelectOverview: vi.fn(),
317
+ onSelectChildSessions: vi.fn(),
318
+ onSelectProjectFiles: vi.fn(),
193
319
  onSelectCronJobs: vi.fn(),
194
320
  });
195
321
 
196
- expect(tabs).toEqual([
322
+ expect(tabs.slice(4)).toEqual([
197
323
  expect.objectContaining({
198
324
  key: "file:parent::preview::demo.html",
199
325
  active: true,
326
+ fileName: "demo.html",
327
+ title: "demo.html",
328
+ isRenderedPreview: false,
329
+ alternateViewerAction: expect.objectContaining({
330
+ label: t("chatWorkspaceOpenPreview"),
331
+ viewer: "rendered",
332
+ }),
200
333
  }),
201
334
  expect.objectContaining({
202
335
  key: "file:parent::preview:rendered::demo.html",
203
336
  active: false,
337
+ title: `${t("chatWorkspacePreview")}: demo.html`,
338
+ isRenderedPreview: true,
339
+ alternateViewerAction: expect.objectContaining({
340
+ label: t("chatWorkspaceOpenSource"),
341
+ viewer: "source",
342
+ }),
204
343
  }),
205
344
  ]);
345
+ tabs[4]?.alternateViewerAction?.onSelect();
346
+ expect(onOpenFileViewer).toHaveBeenCalledWith(
347
+ "parent::preview::demo.html",
348
+ "rendered",
349
+ );
206
350
  });
207
351
  });
@@ -16,10 +16,159 @@ export function areWorkspaceNavigationEntriesEqual(
16
16
  if (current.kind !== next.kind) {
17
17
  return false;
18
18
  }
19
- if (current.kind === 'cron') {
19
+ if (
20
+ current.kind === 'overview' ||
21
+ current.kind === 'child-sessions' ||
22
+ current.kind === 'project-files' ||
23
+ current.kind === 'cron'
24
+ ) {
20
25
  return true;
21
26
  }
22
- return next.kind !== 'cron' && current.key === next.key;
27
+ return (
28
+ next.kind !== 'overview' &&
29
+ next.kind !== 'child-sessions' &&
30
+ next.kind !== 'project-files' &&
31
+ next.kind !== 'cron' &&
32
+ current.key === next.key
33
+ );
34
+ }
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
+
67
+ export function createWorkspaceSelectionPatch(
68
+ entry: ChatWorkspaceNavigationEntry,
69
+ snapshot: ChatThreadSnapshot,
70
+ ): Partial<ChatThreadSnapshot> | null {
71
+ if (
72
+ entry.kind === 'overview' ||
73
+ entry.kind === 'child-sessions' ||
74
+ entry.kind === 'project-files'
75
+ ) {
76
+ return {
77
+ activeWorkspacePanelKind: entry.kind,
78
+ activeChildSessionKey: null,
79
+ activeSideChatDraft: null,
80
+ activeWorkspaceFileKey: null,
81
+ };
82
+ }
83
+ if (entry.kind === 'cron') {
84
+ return {
85
+ activeWorkspacePanelKind: 'cron',
86
+ activeChildSessionKey: null,
87
+ activeSideChatDraft: null,
88
+ activeWorkspaceFileKey: null,
89
+ };
90
+ }
91
+ if (entry.kind === 'file') {
92
+ if (!snapshot.workspaceFileTabs.some((tab) => tab.key === entry.key)) {
93
+ return null;
94
+ }
95
+ return {
96
+ activeWorkspacePanelKind: 'file',
97
+ activeChildSessionKey: null,
98
+ activeSideChatDraft: null,
99
+ activeWorkspaceFileKey: entry.key,
100
+ };
101
+ }
102
+ if (entry.kind === 'side-chat-draft') {
103
+ const { activeSideChatDraft } = snapshot;
104
+ if (activeSideChatDraft?.draftKey !== entry.key) {
105
+ return null;
106
+ }
107
+ return {
108
+ activeWorkspacePanelKind: 'side-chat-draft',
109
+ activeChildSessionKey: null,
110
+ activeSideChatDraft,
111
+ activeWorkspaceFileKey: null,
112
+ };
113
+ }
114
+ return {
115
+ activeWorkspacePanelKind: 'child-session',
116
+ activeChildSessionKey: entry.key,
117
+ activeSideChatDraft: null,
118
+ activeWorkspaceFileKey: null,
119
+ };
120
+ }
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
+ };
23
172
  }
24
173
 
25
174
  export function createSideChatDraft(parentSessionKey: string): ChatWorkspaceSideChatDraft {
@@ -85,3 +85,20 @@ export function toPersistedWorkspaceFileTab(
85
85
  newStartLine: tab.newStartLine,
86
86
  };
87
87
  }
88
+
89
+ export function retainWorkspaceFileTabs(
90
+ tabs: readonly ChatWorkspaceFileTab[],
91
+ activeWorkspaceFileKey: string | null | undefined,
92
+ maxTabs: number,
93
+ ): ChatWorkspaceFileTab[] {
94
+ const retained = tabs.slice(0, maxTabs);
95
+ const activeTab = activeWorkspaceFileKey
96
+ ? tabs.find((tab) => tab.key === activeWorkspaceFileKey)
97
+ : null;
98
+ if (!activeTab || retained.some((tab) => tab.key === activeTab.key)) {
99
+ return retained;
100
+ }
101
+ return retained.length < maxTabs
102
+ ? [...retained, activeTab]
103
+ : [...retained.slice(0, -1), activeTab];
104
+ }
@@ -0,0 +1,66 @@
1
+ import type { ChatFileOpenActionViewModel } from '@nextclaw/agent-chat-ui';
2
+ import type { ChatWorkspaceFileTab } from '@/features/chat/stores/chat-thread.store';
3
+ import { normalizeWorkspaceFilePreviewViewer } from './chat-workspace-file-viewer.utils';
4
+
5
+ export function createWorkspaceFileTab(
6
+ action: ChatFileOpenActionViewModel,
7
+ parentSessionKey: string | null,
8
+ ): ChatWorkspaceFileTab | null {
9
+ const path = action.path.trim();
10
+ if (!path) {
11
+ return null;
12
+ }
13
+ const normalizedParentSessionKey = parentSessionKey?.trim() || null;
14
+ const previewViewer = action.viewMode === 'preview'
15
+ ? normalizeWorkspaceFilePreviewViewer(
16
+ path,
17
+ action.previewViewer ?? (action.line ? 'source' : undefined),
18
+ )
19
+ : action.previewViewer ?? null;
20
+ const viewIdentity = action.viewMode === 'preview' && previewViewer === 'rendered'
21
+ ? 'preview:rendered'
22
+ : action.viewMode;
23
+ return {
24
+ key: `${normalizedParentSessionKey ?? 'draft'}::${viewIdentity}::${path}`,
25
+ parentSessionKey: normalizedParentSessionKey,
26
+ path,
27
+ label: action.label?.trim() || null,
28
+ viewMode: action.viewMode,
29
+ previewViewer,
30
+ line: action.line ?? null,
31
+ column: action.column ?? null,
32
+ rawText: action.rawText ?? null,
33
+ contentUrl: action.contentUrl?.trim() || null,
34
+ mimeType: action.mimeType?.trim() || null,
35
+ beforeText: action.beforeText ?? null,
36
+ afterText: action.afterText ?? null,
37
+ patchText: action.patchText ?? null,
38
+ oldStartLine: action.oldStartLine ?? null,
39
+ newStartLine: action.newStartLine ?? null,
40
+ fullLines: action.fullLines,
41
+ };
42
+ }
43
+
44
+ export function upsertWorkspaceFileTab(
45
+ tabs: readonly ChatWorkspaceFileTab[],
46
+ nextTab: ChatWorkspaceFileTab,
47
+ adjacentToKey?: string,
48
+ ): ChatWorkspaceFileTab[] {
49
+ const existingIndex = tabs.findIndex((tab) => tab.key === nextTab.key);
50
+ if (existingIndex !== -1) {
51
+ const nextTabs = [...tabs];
52
+ nextTabs[existingIndex] = { ...tabs[existingIndex], ...nextTab };
53
+ return nextTabs;
54
+ }
55
+ const adjacentIndex = adjacentToKey
56
+ ? tabs.findIndex((tab) => tab.key === adjacentToKey)
57
+ : -1;
58
+ if (adjacentIndex === -1) {
59
+ return [nextTab, ...tabs];
60
+ }
61
+ return [
62
+ ...tabs.slice(0, adjacentIndex + 1),
63
+ nextTab,
64
+ ...tabs.slice(adjacentIndex + 1),
65
+ ];
66
+ }
@@ -0,0 +1,49 @@
1
+ import type { ChatFilePreviewViewer } from "@nextclaw/agent-chat-ui";
2
+
3
+ export type ChatWorkspaceFileViewer = "source" | "rendered";
4
+
5
+ function resolveDualViewFileKind(path: string): "html" | "markdown" | null {
6
+ if (/\.html?$/i.test(path)) {
7
+ return "html";
8
+ }
9
+ if (/\.mdx?$/i.test(path)) {
10
+ return "markdown";
11
+ }
12
+ return null;
13
+ }
14
+
15
+ export function resolveWorkspaceFileViewer(
16
+ path: string,
17
+ viewer?: ChatFilePreviewViewer | null,
18
+ ): ChatWorkspaceFileViewer | null {
19
+ const kind = resolveDualViewFileKind(path);
20
+ if (kind === "html") {
21
+ return viewer === "rendered" ? "rendered" : "source";
22
+ }
23
+ if (kind === "markdown") {
24
+ return viewer === "source" ? "source" : "rendered";
25
+ }
26
+ if (viewer === "source" || viewer === "rendered") {
27
+ return viewer;
28
+ }
29
+ return null;
30
+ }
31
+
32
+ export function normalizeWorkspaceFilePreviewViewer(
33
+ path: string,
34
+ viewer?: ChatFilePreviewViewer | null,
35
+ ): ChatFilePreviewViewer | null {
36
+ return resolveWorkspaceFileViewer(path, viewer) ?? viewer ?? null;
37
+ }
38
+
39
+ export function resolveAlternateWorkspaceFileViewer(
40
+ path: string,
41
+ viewer?: ChatFilePreviewViewer | null,
42
+ ): ChatWorkspaceFileViewer | null {
43
+ if (!resolveDualViewFileKind(path)) {
44
+ return null;
45
+ }
46
+ return resolveWorkspaceFileViewer(path, viewer) === "source"
47
+ ? "rendered"
48
+ : "source";
49
+ }
@@ -0,0 +1,13 @@
1
+ export const CHAT_WORKSPACE_PANEL_DEFAULT_WIDTH = 480;
2
+ export const CHAT_WORKSPACE_PANEL_MIN_WIDTH = 360;
3
+ export const CHAT_WORKSPACE_PANEL_MAX_WIDTH = 860;
4
+
5
+ export function normalizeChatWorkspacePanelWidth(value: unknown): number {
6
+ if (typeof value !== 'number' || !Number.isFinite(value)) {
7
+ return CHAT_WORKSPACE_PANEL_DEFAULT_WIDTH;
8
+ }
9
+ return Math.max(
10
+ CHAT_WORKSPACE_PANEL_MIN_WIDTH,
11
+ Math.min(CHAT_WORKSPACE_PANEL_MAX_WIDTH, value),
12
+ );
13
+ }