@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,15 +1,21 @@
1
- import { fireEvent, render, screen } from "@testing-library/react";
2
- import { beforeEach, describe, expect, it, vi } from "vitest";
1
+ import { fireEvent, render, screen, waitFor } from "@testing-library/react";
2
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
3
3
  import type {
4
4
  ChatFileOpenActionViewModel,
5
5
  ChatFileOperationBlockViewModel,
6
6
  } from "@nextclaw/agent-chat-ui";
7
7
  import { ChatSessionWorkspaceFilePreview } from "@/features/chat/features/workspace/components/chat-session-workspace-file-preview";
8
8
  import type { ChatWorkspaceFileTab } from "@/features/chat/stores/chat-thread.store";
9
+ import type { ServerPathReadView } from "@/shared/lib/api";
9
10
  import { t } from "@/shared/lib/i18n";
10
11
 
11
12
  const serverPathReadMock = vi.fn();
12
13
  const serverPathBrowseMock = vi.fn();
14
+ const renderDocxMock = vi.fn();
15
+ const readSpreadsheetMock = vi.fn();
16
+ const sheetToJsonMock = vi.fn();
17
+ const openPresentationMock = vi.fn();
18
+ const destroyPresentationMock = vi.fn();
13
19
 
14
20
  type RenderWorkspaceFilePreviewOptions = {
15
21
  file?: Partial<ChatWorkspaceFileTab>;
@@ -17,14 +23,10 @@ type RenderWorkspaceFilePreviewOptions = {
17
23
  refreshVersion?: number;
18
24
  sessionProjectRoot?: string | null;
19
25
  sessionWorkingDir?: string | null;
26
+ showBreadcrumbs?: boolean;
20
27
  };
21
28
 
22
- type TextReadDataOverrides = {
23
- languageHint?: string;
24
- resolvedPath?: string;
25
- text?: string;
26
- truncated?: boolean;
27
- };
29
+ type TextReadDataOverrides = Partial<ServerPathReadView>;
28
30
 
29
31
  vi.mock("@/shared/hooks/use-server-path-read", () => ({
30
32
  useServerPathRead: (...args: unknown[]) => serverPathReadMock(...args),
@@ -53,6 +55,24 @@ vi.mock("@nextclaw/agent-chat-ui", () => ({
53
55
  ),
54
56
  }));
55
57
 
58
+ vi.mock("docx-preview", () => ({
59
+ renderAsync: (...args: unknown[]) => renderDocxMock(...args),
60
+ }));
61
+
62
+ vi.mock("xlsx", () => ({
63
+ read: (...args: unknown[]) => readSpreadsheetMock(...args),
64
+ utils: {
65
+ sheet_to_json: (...args: unknown[]) => sheetToJsonMock(...args),
66
+ },
67
+ }));
68
+
69
+ vi.mock("@aiden0z/pptx-renderer", () => ({
70
+ PptxViewer: {
71
+ open: (...args: unknown[]) => openPresentationMock(...args),
72
+ },
73
+ RECOMMENDED_ZIP_LIMITS: { maxEntries: 4_000 },
74
+ }));
75
+
56
76
  function buildWorkspaceFile(
57
77
  overrides: Partial<ChatWorkspaceFileTab>,
58
78
  ): ChatWorkspaceFileTab {
@@ -73,6 +93,7 @@ function mockTextRead(overrides: TextReadDataOverrides = {}) {
73
93
  data: {
74
94
  kind: "text",
75
95
  resolvedPath: "/tmp/example.ts",
96
+ startLine: 1,
76
97
  text: "const answer = 42;\n",
77
98
  truncated: false,
78
99
  ...overrides,
@@ -86,87 +107,118 @@ function renderWorkspaceFilePreview({
86
107
  refreshVersion,
87
108
  sessionProjectRoot = "/tmp",
88
109
  sessionWorkingDir = "/tmp",
110
+ showBreadcrumbs,
89
111
  }: RenderWorkspaceFilePreviewOptions = {}) {
90
- render(
112
+ const view = render(
91
113
  <ChatSessionWorkspaceFilePreview
92
114
  file={buildWorkspaceFile(file ?? {})}
93
115
  sessionProjectRoot={sessionProjectRoot}
94
116
  sessionWorkingDir={sessionWorkingDir}
95
117
  refreshVersion={refreshVersion}
118
+ showBreadcrumbs={showBreadcrumbs}
96
119
  onFileOpen={onFileOpen}
97
120
  />,
98
121
  );
99
- return { onFileOpen };
122
+ return { ...view, onFileOpen };
100
123
  }
101
124
 
102
125
  function mockWorkspaceBreadcrumbBrowseTree() {
103
- serverPathBrowseMock.mockImplementation(({ path }: { path?: string | null }) => {
104
- if (path === "/tmp/workspace/src/components") {
105
- return {
106
- isLoading: false,
107
- error: null,
108
- data: {
109
- currentPath: "/tmp/workspace/src/components",
110
- parentPath: "/tmp/workspace/src",
111
- homePath: "/Users/demo",
112
- breadcrumbs: [
113
- { label: "workspace", path: "/tmp/workspace" },
114
- { label: "src", path: "/tmp/workspace/src" },
115
- { label: "components", path: "/tmp/workspace/src/components" },
116
- ],
117
- entries: [
118
- {
119
- name: "button.tsx",
120
- path: "/tmp/workspace/src/components/button.tsx",
121
- kind: "file",
122
- hidden: false,
123
- },
124
- ],
125
- },
126
- };
127
- }
126
+ serverPathBrowseMock.mockImplementation(
127
+ ({ path }: { path?: string | null }) => {
128
+ if (path === "/tmp/workspace/src/components") {
129
+ return {
130
+ isLoading: false,
131
+ error: null,
132
+ data: {
133
+ currentPath: "/tmp/workspace/src/components",
134
+ parentPath: "/tmp/workspace/src",
135
+ homePath: "/Users/demo",
136
+ breadcrumbs: [
137
+ { label: "workspace", path: "/tmp/workspace" },
138
+ { label: "src", path: "/tmp/workspace/src" },
139
+ { label: "components", path: "/tmp/workspace/src/components" },
140
+ ],
141
+ entries: [
142
+ {
143
+ name: "button.tsx",
144
+ path: "/tmp/workspace/src/components/button.tsx",
145
+ kind: "file",
146
+ hidden: false,
147
+ },
148
+ ],
149
+ },
150
+ };
151
+ }
152
+
153
+ if (path === "/tmp/workspace/src") {
154
+ return {
155
+ isLoading: false,
156
+ error: null,
157
+ data: {
158
+ currentPath: "/tmp/workspace/src",
159
+ parentPath: "/tmp/workspace",
160
+ homePath: "/Users/demo",
161
+ breadcrumbs: [
162
+ { label: "workspace", path: "/tmp/workspace" },
163
+ { label: "src", path: "/tmp/workspace/src" },
164
+ ],
165
+ entries: [
166
+ {
167
+ name: "components",
168
+ path: "/tmp/workspace/src/components",
169
+ kind: "directory",
170
+ hidden: false,
171
+ },
172
+ {
173
+ name: "index.ts",
174
+ path: "/tmp/workspace/src/index.ts",
175
+ kind: "file",
176
+ hidden: false,
177
+ },
178
+ ],
179
+ },
180
+ };
181
+ }
128
182
 
129
- if (path === "/tmp/workspace/src") {
130
183
  return {
131
184
  isLoading: false,
132
- error: null,
133
- data: {
134
- currentPath: "/tmp/workspace/src",
135
- parentPath: "/tmp/workspace",
136
- homePath: "/Users/demo",
137
- breadcrumbs: [
138
- { label: "workspace", path: "/tmp/workspace" },
139
- { label: "src", path: "/tmp/workspace/src" },
140
- ],
141
- entries: [
142
- {
143
- name: "components",
144
- path: "/tmp/workspace/src/components",
145
- kind: "directory",
146
- hidden: false,
147
- },
148
- {
149
- name: "index.ts",
150
- path: "/tmp/workspace/src/index.ts",
151
- kind: "file",
152
- hidden: false,
153
- },
154
- ],
155
- },
185
+ error: new Error("server path must point to a directory"),
186
+ data: null,
156
187
  };
157
- }
158
-
159
- return {
160
- isLoading: false,
161
- error: new Error("server path must point to a directory"),
162
- data: null,
163
- };
164
- });
188
+ },
189
+ );
165
190
  }
166
191
 
167
192
  beforeEach(() => {
168
193
  serverPathReadMock.mockReset();
169
194
  serverPathBrowseMock.mockReset();
195
+ renderDocxMock.mockReset();
196
+ renderDocxMock.mockResolvedValue(undefined);
197
+ readSpreadsheetMock.mockReset();
198
+ readSpreadsheetMock.mockReturnValue({
199
+ SheetNames: ["Summary", "Details"],
200
+ Sheets: { Summary: { name: "Summary" }, Details: { name: "Details" } },
201
+ });
202
+ sheetToJsonMock.mockReset();
203
+ sheetToJsonMock.mockImplementation((sheet: { name: string }) =>
204
+ sheet.name === "Summary"
205
+ ? [
206
+ ["City", "Temperature"],
207
+ ["Hangzhou", "32 C"],
208
+ ]
209
+ : [["Status"], ["Sunny"]],
210
+ );
211
+ openPresentationMock.mockReset();
212
+ destroyPresentationMock.mockReset();
213
+ openPresentationMock.mockResolvedValue({ destroy: destroyPresentationMock });
214
+ vi.stubGlobal(
215
+ "fetch",
216
+ vi
217
+ .fn()
218
+ .mockResolvedValue(
219
+ new Response(new Uint8Array([1, 2, 3]), { status: 200 }),
220
+ ),
221
+ );
170
222
  serverPathReadMock.mockReturnValue({
171
223
  isLoading: false,
172
224
  error: null,
@@ -179,6 +231,10 @@ beforeEach(() => {
179
231
  });
180
232
  });
181
233
 
234
+ afterEach(() => {
235
+ vi.unstubAllGlobals();
236
+ });
237
+
182
238
  describe("ChatSessionWorkspaceFilePreview rendering", () => {
183
239
  it("renders attachment content URLs as workspace-native media previews", () => {
184
240
  renderWorkspaceFilePreview({
@@ -205,25 +261,240 @@ describe("ChatSessionWorkspaceFilePreview rendering", () => {
205
261
  expect(image.getAttribute("alt")).toBe("photo.png");
206
262
  });
207
263
 
208
- it("offers download and system open actions for unsupported office attachments", () => {
264
+ it("renders local SVG files through the automatic server-content viewer", () => {
265
+ mockTextRead({
266
+ resolvedPath: "/tmp/project/diagram.svg",
267
+ text: '<svg xmlns="http://www.w3.org/2000/svg" />',
268
+ });
269
+ renderWorkspaceFilePreview({
270
+ file: {
271
+ path: "/tmp/project/diagram.svg",
272
+ label: "diagram.svg",
273
+ previewViewer: "auto",
274
+ },
275
+ });
276
+
277
+ const image = screen.getByTestId("workspace-content-image");
278
+ expect(image.getAttribute("src")).toContain(
279
+ "/api/server-paths/content?path=%2Ftmp%2Fproject%2Fdiagram.svg",
280
+ );
281
+ expect(screen.queryByTestId("file-code-surface")).toBeNull();
282
+ });
283
+
284
+ it("renders SVG source when source viewer is explicitly requested", () => {
285
+ mockTextRead({
286
+ resolvedPath: "/tmp/project/diagram.svg",
287
+ text: '<svg xmlns="http://www.w3.org/2000/svg" />',
288
+ });
289
+ renderWorkspaceFilePreview({
290
+ file: {
291
+ path: "/tmp/project/diagram.svg",
292
+ label: "diagram.svg",
293
+ previewViewer: "source",
294
+ },
295
+ });
296
+
297
+ expect(screen.queryByTestId("workspace-content-image")).toBeNull();
298
+ expect(screen.getByTestId("file-code-surface")).toBeTruthy();
299
+ });
300
+
301
+ it.each([
302
+ ["image", "sample.png", "workspace-content-image"],
303
+ ["audio", "sample.mp3", "workspace-content-audio"],
304
+ ["video", "sample.mp4", "workspace-content-video"],
305
+ ])(
306
+ "does not report truncated metadata for local %s content",
307
+ (_kind, path, testId) => {
308
+ mockTextRead({
309
+ kind: "binary",
310
+ resolvedPath: `/tmp/${path}`,
311
+ text: undefined,
312
+ truncated: true,
313
+ });
314
+ renderWorkspaceFilePreview({
315
+ file: { path, label: path, previewViewer: "auto" },
316
+ });
317
+
318
+ expect(screen.getByTestId(testId)).toBeTruthy();
319
+ expect(
320
+ screen.queryByText(t("chatWorkspacePreviewTruncated")),
321
+ ).toBeNull();
322
+ },
323
+ );
324
+ });
325
+
326
+ describe("ChatSessionWorkspaceFilePreview Office rendering", () => {
327
+ it("renders DOCX attachments inside the workspace preview", async () => {
328
+ renderDocxMock.mockImplementationOnce(
329
+ async (_data: ArrayBuffer, bodyContainer: HTMLDivElement) => {
330
+ const wrapper = document.createElement("div");
331
+ wrapper.className = "docx-wrapper";
332
+ const page = document.createElement("section");
333
+ page.className = "docx";
334
+ const table = document.createElement("table");
335
+ const colgroup = document.createElement("colgroup");
336
+ for (let index = 0; index < 6; index += 1) {
337
+ colgroup.append(document.createElement("col"));
338
+ }
339
+ table.append(colgroup);
340
+ page.append(table);
341
+ wrapper.append(page);
342
+ bodyContainer.append(wrapper);
343
+ },
344
+ );
209
345
  renderWorkspaceFilePreview({
210
346
  file: {
211
347
  key: "attachment-docx",
212
348
  path: "overview.zh-CN.docx",
213
349
  label: "overview.zh-CN.docx",
214
350
  viewMode: "preview",
215
- contentUrl: "/api/ncp/assets/content?uri=asset%3A%2F%2Fstore%2Foverview",
351
+ contentUrl:
352
+ "/api/ncp/assets/content?uri=asset%3A%2F%2Fstore%2Foverview",
216
353
  mimeType:
217
354
  "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
218
355
  },
219
356
  });
220
357
 
221
- expect(screen.getByTestId("workspace-content-unsupported")).toBeTruthy();
358
+ expect(screen.getByTestId("workspace-content-docx")).toBeTruthy();
359
+ await waitFor(() => expect(renderDocxMock).toHaveBeenCalled());
360
+ await waitFor(() =>
361
+ expect(screen.getByTestId("workspace-content-docx").className).toContain(
362
+ "workspace-docx-preview--reflow",
363
+ ),
364
+ );
365
+ expect(
366
+ screen
367
+ .getByTestId("workspace-content-docx")
368
+ .querySelector(".workspace-docx-wide-table > table"),
369
+ ).toBeTruthy();
370
+ expect(fetch).toHaveBeenCalledWith(
371
+ "/api/ncp/assets/content?uri=asset%3A%2F%2Fstore%2Foverview",
372
+ expect.objectContaining({ signal: expect.any(AbortSignal) }),
373
+ );
374
+ expect(screen.queryByTestId("workspace-content-unsupported")).toBeNull();
375
+ });
376
+
377
+ it("renders a relative local DOCX before binary metadata finishes loading", async () => {
378
+ mockTextRead({
379
+ kind: "binary",
380
+ resolvedPath: "/tmp/report.docx",
381
+ text: undefined,
382
+ });
383
+ renderWorkspaceFilePreview({
384
+ file: {
385
+ path: "report.docx",
386
+ label: "report.docx",
387
+ previewViewer: "auto",
388
+ },
389
+ sessionWorkingDir: "/tmp",
390
+ });
391
+
392
+ expect(screen.getByTestId("workspace-content-docx")).toBeTruthy();
393
+ await waitFor(() => expect(renderDocxMock).toHaveBeenCalled());
394
+ expect(fetch).toHaveBeenCalledWith(
395
+ expect.stringContaining("path=report.docx&basePath=%2Ftmp"),
396
+ expect.objectContaining({ signal: expect.any(AbortSignal) }),
397
+ );
398
+ expect(screen.queryByText(t("chatWorkspacePreviewUnsupported"))).toBeNull();
399
+ });
400
+
401
+ it("preserves DOCX page geometry when the document defines it", async () => {
402
+ renderDocxMock.mockImplementationOnce(
403
+ async (_data: ArrayBuffer, bodyContainer: HTMLDivElement) => {
404
+ const wrapper = document.createElement("div");
405
+ wrapper.className = "docx-wrapper";
406
+ const page = document.createElement("section");
407
+ page.className = "docx";
408
+ page.style.width = "612pt";
409
+ page.style.padding = "72pt";
410
+ wrapper.append(page);
411
+ bodyContainer.append(wrapper);
412
+ },
413
+ );
414
+ renderWorkspaceFilePreview({
415
+ file: {
416
+ key: "attachment-docx-with-page-layout",
417
+ path: "page-layout.docx",
418
+ label: "page-layout.docx",
419
+ viewMode: "preview",
420
+ contentUrl:
421
+ "/api/ncp/assets/content?uri=asset%3A%2F%2Fstore%2Fpage-layout",
422
+ mimeType:
423
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
424
+ },
425
+ });
426
+
427
+ await waitFor(() => expect(renderDocxMock).toHaveBeenCalled());
222
428
  expect(
223
- screen.getByTestId("workspace-content-unsupported").textContent,
224
- ).toContain("overview.zh-CN.docx");
429
+ screen.getByTestId("workspace-content-docx").className,
430
+ ).not.toContain("workspace-docx-preview--reflow");
431
+ });
432
+
433
+ it("renders Excel workbooks with sheet navigation", async () => {
434
+ renderWorkspaceFilePreview({
435
+ file: {
436
+ key: "attachment-xlsx",
437
+ path: "overview.xlsx",
438
+ label: "overview.xlsx",
439
+ viewMode: "preview",
440
+ contentUrl:
441
+ "/api/ncp/assets/content?uri=asset%3A%2F%2Fstore%2Foverview",
442
+ mimeType:
443
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
444
+ },
445
+ });
446
+
447
+ expect(screen.getByTestId("workspace-content-spreadsheet")).toBeTruthy();
448
+ await waitFor(() => expect(readSpreadsheetMock).toHaveBeenCalled());
449
+ expect(screen.getByText("Summary")).toBeTruthy();
450
+ expect(screen.getByText("Details")).toBeTruthy();
451
+ expect(screen.getByText("Hangzhou")).toBeTruthy();
452
+ fireEvent.click(screen.getByRole("button", { name: "Details" }));
453
+ expect(screen.getByText("Sunny")).toBeTruthy();
454
+ });
455
+
456
+ it("renders PowerPoint presentations in the workspace", async () => {
457
+ renderWorkspaceFilePreview({
458
+ file: {
459
+ key: "attachment-pptx",
460
+ path: "overview.pptx",
461
+ label: "overview.pptx",
462
+ viewMode: "preview",
463
+ contentUrl: "/api/ncp/assets/content?uri=asset%3A%2F%2Fstore%2Fslides",
464
+ mimeType:
465
+ "application/vnd.openxmlformats-officedocument.presentationml.presentation",
466
+ },
467
+ });
468
+
469
+ expect(screen.getByTestId("workspace-content-presentation")).toBeTruthy();
470
+ await waitFor(() => expect(openPresentationMock).toHaveBeenCalled());
471
+ expect(openPresentationMock).toHaveBeenCalledWith(
472
+ expect.any(ArrayBuffer),
473
+ expect.any(HTMLElement),
474
+ expect.objectContaining({
475
+ lazySlides: true,
476
+ lazyMedia: true,
477
+ pdfjs: false,
478
+ zipLimits: { maxEntries: 4_000 },
479
+ }),
480
+ );
481
+ });
482
+
483
+ it("offers download and system open actions for legacy PowerPoint attachments", () => {
484
+ renderWorkspaceFilePreview({
485
+ file: {
486
+ key: "attachment-ppt",
487
+ path: "overview.ppt",
488
+ label: "overview.ppt",
489
+ viewMode: "preview",
490
+ contentUrl:
491
+ "/api/ncp/assets/content?uri=asset%3A%2F%2Fstore%2Foverview",
492
+ mimeType: "application/vnd.ms-powerpoint",
493
+ },
494
+ });
495
+
496
+ expect(screen.getByTestId("workspace-content-unsupported")).toBeTruthy();
225
497
  expect(screen.getByText(t("chatWorkspacePreviewUnsupported"))).toBeTruthy();
226
- expect(screen.getByText(t("chatWorkspacePreviewUnsupportedHint"))).toBeTruthy();
227
498
 
228
499
  const download = screen.getByRole("link", {
229
500
  name: t("chatWorkspacePreviewDownload"),
@@ -231,7 +502,7 @@ describe("ChatSessionWorkspaceFilePreview rendering", () => {
231
502
  expect(download.getAttribute("href")).toBe(
232
503
  "/api/ncp/assets/content?uri=asset%3A%2F%2Fstore%2Foverview",
233
504
  );
234
- expect(download.getAttribute("download")).toBe("overview.zh-CN.docx");
505
+ expect(download.getAttribute("download")).toBe("overview.ppt");
235
506
 
236
507
  const openExternally = screen.getByRole("link", {
237
508
  name: t("chatWorkspacePreviewOpenExternally"),
@@ -241,29 +512,61 @@ describe("ChatSessionWorkspaceFilePreview rendering", () => {
241
512
  );
242
513
  expect(openExternally.getAttribute("target")).toBe("_blank");
243
514
  });
515
+ });
244
516
 
517
+ describe("ChatSessionWorkspaceFilePreview text rendering", () => {
245
518
  it("renders preview files inside a full-height workspace code surface", () => {
246
519
  mockTextRead();
247
520
  renderWorkspaceFilePreview();
248
521
 
249
- expect(screen.getByTestId("file-code-surface").getAttribute("data-layout")).toBe(
250
- "workspace",
251
- );
522
+ expect(
523
+ screen.getByTestId("file-code-surface").getAttribute("data-layout"),
524
+ ).toBe("workspace");
252
525
  });
253
526
 
254
- it("passes server language hints to the workspace code surface", () => {
255
- mockTextRead({ languageHint: "js", resolvedPath: "/tmp/example.js" });
527
+ it("passes server language hints and keeps location out of breadcrumbs", () => {
528
+ mockTextRead({
529
+ languageHint: "js",
530
+ resolvedPath: "/tmp/example.js",
531
+ });
256
532
  renderWorkspaceFilePreview({
257
533
  file: {
534
+ column: 4,
258
535
  label: "example.js",
536
+ line: 12,
259
537
  path: "/tmp/example.js",
260
538
  viewMode: "preview",
261
539
  },
262
540
  });
263
541
 
264
542
  expect(
265
- screen.getByTestId("file-code-surface").getAttribute("data-language-hint"),
543
+ screen
544
+ .getByTestId("file-code-surface")
545
+ .getAttribute("data-language-hint"),
266
546
  ).toBe("js");
547
+ expect(screen.queryByText("L12:4")).toBeNull();
548
+ });
549
+
550
+ it("renders Markdown by default and keeps source view explicit", () => {
551
+ mockTextRead({
552
+ kind: "markdown",
553
+ resolvedPath: "/tmp/README.md",
554
+ text: "# Hello",
555
+ });
556
+ const { unmount } = renderWorkspaceFilePreview({
557
+ file: { path: "/tmp/README.md" },
558
+ });
559
+
560
+ expect(screen.getByTestId("markdown-preview")).toBeTruthy();
561
+ expect(screen.queryByTestId("file-code-surface")).toBeNull();
562
+
563
+ unmount();
564
+ renderWorkspaceFilePreview({
565
+ file: { path: "/tmp/README.md", previewViewer: "source" },
566
+ });
567
+
568
+ expect(screen.queryByTestId("markdown-preview")).toBeNull();
569
+ expect(screen.getByTestId("file-code-surface")).toBeTruthy();
267
570
  });
268
571
 
269
572
  it("keeps HTML files in the source preview when preview viewer is automatic", () => {
@@ -301,7 +604,7 @@ describe("ChatSessionWorkspaceFilePreview rendering", () => {
301
604
  expect(frame.getAttribute("sandbox")).toBeNull();
302
605
  expect(frame.getAttribute("srcdoc")).toBeNull();
303
606
  expect(frame.getAttribute("src")).toContain(
304
- "/api/server-paths/content/__abs__/tmp/example.html",
607
+ "/api/server-paths/content?path=%2Ftmp%2Fexample.html",
305
608
  );
306
609
  expect(screen.queryByTestId("file-code-surface")).toBeNull();
307
610
  });
@@ -321,9 +624,9 @@ describe("ChatSessionWorkspaceFilePreview rendering", () => {
321
624
  refreshVersion: 3,
322
625
  });
323
626
 
324
- expect(screen.getByTestId("workspace-html-preview").getAttribute("src")).toContain(
325
- "refresh=3",
326
- );
627
+ expect(
628
+ screen.getByTestId("workspace-html-preview").getAttribute("src"),
629
+ ).toContain("refresh=3");
327
630
  });
328
631
 
329
632
  it("keeps HTML files in the source preview when source is requested", () => {
@@ -374,9 +677,9 @@ describe("ChatSessionWorkspaceFilePreview rendering", () => {
374
677
  },
375
678
  });
376
679
 
377
- expect(screen.getByTestId("file-code-surface").getAttribute("data-layout")).toBe(
378
- "workspace",
379
- );
680
+ expect(
681
+ screen.getByTestId("file-code-surface").getAttribute("data-layout"),
682
+ ).toBe("workspace");
380
683
  });
381
684
 
382
685
  it("does not repeat the preview badge inside the workspace header", () => {
@@ -389,7 +692,7 @@ describe("ChatSessionWorkspaceFilePreview rendering", () => {
389
692
  expect(screen.getByText("example.ts")).toBeTruthy();
390
693
  });
391
694
 
392
- it("renders directory entries and opens child directories or files", () => {
695
+ it("renders directory entries, expands child directories, and opens files", () => {
393
696
  serverPathReadMock.mockReturnValue({
394
697
  isLoading: false,
395
698
  error: new Error("server path must point to a file"),
@@ -443,18 +746,17 @@ describe("ChatSessionWorkspaceFilePreview rendering", () => {
443
746
  });
444
747
  expect(screen.getByTestId("workspace-directory-browser")).toBeTruthy();
445
748
 
446
- fireEvent.click(
447
- screen.getByRole("button", { name: "Open directory: components" }),
448
- );
449
- expect(onFileOpen).toHaveBeenCalledWith({
450
- path: "/tmp/workspace/src/components",
451
- label: "components",
452
- viewMode: "preview",
749
+ const componentsTreeItem = screen.getByRole("treeitem", {
750
+ name: "Open directory: components",
453
751
  });
752
+ const indexButton = screen.getByRole("button", { name: "index.ts" });
753
+ fireEvent.click(componentsTreeItem.querySelector("button")!);
754
+ expect(
755
+ componentsTreeItem.getAttribute("aria-expanded"),
756
+ ).toBe("true");
757
+ expect(onFileOpen).not.toHaveBeenCalled();
454
758
 
455
- fireEvent.click(
456
- screen.getByRole("button", { name: "Open file: index.ts" }),
457
- );
759
+ fireEvent.click(indexButton);
458
760
  expect(onFileOpen).toHaveBeenCalledWith({
459
761
  path: "/tmp/workspace/src/index.ts",
460
762
  label: "index.ts",
@@ -464,6 +766,14 @@ describe("ChatSessionWorkspaceFilePreview rendering", () => {
464
766
  });
465
767
 
466
768
  describe("ChatSessionWorkspaceFilePreview breadcrumbs", () => {
769
+ it("can hide workspace breadcrumbs for inline placements", () => {
770
+ mockTextRead();
771
+ renderWorkspaceFilePreview({ showBreadcrumbs: false });
772
+
773
+ expect(screen.queryByTestId("workspace-file-breadcrumbs")).toBeNull();
774
+ expect(screen.getByTestId("file-code-surface")).toBeTruthy();
775
+ });
776
+
467
777
  it("renders project-relative breadcrumbs when the file is inside the workspace", () => {
468
778
  mockTextRead({ resolvedPath: "/tmp/workspace/src/example.ts" });
469
779
  renderWorkspaceFilePreview({
@@ -477,9 +787,9 @@ describe("ChatSessionWorkspaceFilePreview breadcrumbs", () => {
477
787
  expect(
478
788
  screen.getByTestId("workspace-file-breadcrumb-scroll").className,
479
789
  ).toContain("py-1.5");
480
- expect(screen.getByTestId("workspace-file-breadcrumbs").className).toContain(
481
- "workspace-horizontal-scrollbar",
482
- );
790
+ expect(
791
+ screen.getByTestId("workspace-file-breadcrumbs").className,
792
+ ).toContain("workspace-horizontal-scrollbar");
483
793
  });
484
794
 
485
795
  it("browses from a breadcrumb segment and opens selected files", () => {
@@ -521,33 +831,18 @@ describe("ChatSessionWorkspaceFilePreview breadcrumbs", () => {
521
831
 
522
832
  fireEvent.click(screen.getByRole("button", { name: "src" }));
523
833
 
524
- expect(screen.getByTestId("workspace-breadcrumb-popover").className).toContain(
525
- "w-[22rem]",
526
- );
527
- expect(screen.getByTestId("workspace-breadcrumb-browser").className).toContain(
528
- "max-h-72",
529
- );
530
- expect(screen.getAllByRole("button", { name: "workspace" }).at(-1)?.className).toContain(
531
- "h-5",
532
- );
533
- expect(screen.getByRole("button", { name: "components" }).className).toContain(
534
- "h-6",
535
- );
536
- });
537
-
538
- it("keeps line and truncation metadata without the duplicated type badge", () => {
539
- mockTextRead({ truncated: true });
540
- renderWorkspaceFilePreview({
541
- file: {
542
- viewMode: "preview",
543
- line: 12,
544
- column: 4,
545
- },
546
- });
547
-
548
- expect(screen.getByText("L12:4")).toBeTruthy();
549
- expect(screen.getByText(t("chatWorkspacePreviewTruncated"))).toBeTruthy();
550
- expect(screen.queryByText(t("chatWorkspacePreview"))).toBeNull();
834
+ expect(
835
+ screen.getByTestId("workspace-breadcrumb-popover").className,
836
+ ).toContain("w-[22rem]");
837
+ expect(
838
+ screen.getByTestId("workspace-breadcrumb-browser").className,
839
+ ).toContain("max-h-72");
840
+ expect(
841
+ screen.getAllByRole("button", { name: "workspace" }).at(-1)?.className,
842
+ ).toContain("h-5");
843
+ expect(
844
+ screen.getByRole("button", { name: "components" }).className,
845
+ ).toContain("h-6");
551
846
  });
552
847
 
553
848
  it("uses the session working directory as the base path when no project root is set", () => {