@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
@@ -8,8 +8,14 @@ import type {
8
8
  } from '@/shared/lib/api';
9
9
  import {
10
10
  normalizePersistedWorkspaceFileTab,
11
+ retainWorkspaceFileTabs,
11
12
  toPersistedWorkspaceFileTab,
12
13
  } from '@/features/chat/features/workspace/utils/chat-workspace-file-tab-persistence.utils';
14
+ import {
15
+ CHAT_WORKSPACE_PANEL_DEFAULT_WIDTH,
16
+ normalizeChatWorkspacePanelWidth,
17
+ } from '@/features/chat/features/workspace/utils/chat-workspace-panel-layout.utils';
18
+ import { createWorkspaceNavigationEntryFromSnapshot } from '@/features/chat/features/workspace/utils/chat-thread-workspace-session.utils';
13
19
 
14
20
  export type ChatChildSessionTab = {
15
21
  sessionKey: string;
@@ -44,9 +50,21 @@ export type ChatWorkspaceFileTab = {
44
50
  fullLines?: ChatFileOperationLineViewModel[];
45
51
  };
46
52
 
53
+ export type ChatWorkspacePanelKind =
54
+ | 'overview'
55
+ | 'child-sessions'
56
+ | 'child-session'
57
+ | 'side-chat-draft'
58
+ | 'project-files'
59
+ | 'file'
60
+ | 'cron';
61
+
47
62
  export type ChatWorkspaceNavigationEntry =
63
+ | { kind: 'overview' }
64
+ | { kind: 'child-sessions' }
48
65
  | { kind: 'child-session'; key: string }
49
66
  | { kind: 'side-chat-draft'; key: string }
67
+ | { kind: 'project-files' }
50
68
  | { kind: 'file'; key: string }
51
69
  | { kind: 'cron' };
52
70
 
@@ -69,14 +87,16 @@ export type ChatThreadSnapshot = {
69
87
  parentSessionKey?: string | null;
70
88
  parentSessionLabel?: string | null;
71
89
  workspacePanelParentKey?: string | null;
72
- activeWorkspacePanelKind?: "child-session" | "side-chat-draft" | "file" | "cron" | null;
90
+ activeWorkspacePanelKind?: ChatWorkspacePanelKind | null;
73
91
  childSessionTabs: ChatChildSessionTab[];
74
92
  activeChildSessionKey?: string | null;
75
93
  activeSideChatDraft?: ChatWorkspaceSideChatDraft | null;
76
94
  workspaceFileTabs: ChatWorkspaceFileTab[];
77
95
  activeWorkspaceFileKey?: string | null;
96
+ closedWorkspaceTabEntries: ChatWorkspaceNavigationEntry[];
78
97
  workspaceNavigationHistory: ChatWorkspaceNavigationEntry[];
79
98
  workspaceNavigationHistoryIndex: number;
99
+ workspacePanelWidth: number;
80
100
  contextWindow?: SessionContextWindowView | null;
81
101
  };
82
102
 
@@ -96,8 +116,10 @@ type PersistedChatThreadStore = {
96
116
  activeChildSessionKey?: unknown;
97
117
  workspaceFileTabs?: unknown;
98
118
  activeWorkspaceFileKey?: unknown;
119
+ closedWorkspaceTabEntries?: unknown;
99
120
  workspaceNavigationHistory?: unknown;
100
121
  workspaceNavigationHistoryIndex?: unknown;
122
+ workspacePanelWidth?: unknown;
101
123
  };
102
124
  };
103
125
 
@@ -108,11 +130,13 @@ type PersistedChatWorkspaceSnapshot = Pick<
108
130
  | 'activeChildSessionKey'
109
131
  | 'workspaceFileTabs'
110
132
  | 'activeWorkspaceFileKey'
133
+ | 'closedWorkspaceTabEntries'
111
134
  | 'workspaceNavigationHistory'
112
135
  | 'workspaceNavigationHistoryIndex'
136
+ | 'workspacePanelWidth'
113
137
  >;
114
138
 
115
- type PersistedWorkspacePanelKind = 'child-session' | 'file' | 'cron';
139
+ type PersistedWorkspacePanelKind = Exclude<ChatWorkspacePanelKind, 'side-chat-draft'>;
116
140
 
117
141
  const initialSnapshot: ChatThreadSnapshot = {
118
142
  sessionTypeLabel: null,
@@ -139,8 +163,10 @@ const initialSnapshot: ChatThreadSnapshot = {
139
163
  activeSideChatDraft: null,
140
164
  workspaceFileTabs: [],
141
165
  activeWorkspaceFileKey: null,
166
+ closedWorkspaceTabEntries: [],
142
167
  workspaceNavigationHistory: [],
143
168
  workspaceNavigationHistoryIndex: 0,
169
+ workspacePanelWidth: CHAT_WORKSPACE_PANEL_DEFAULT_WIDTH,
144
170
  contextWindow: null
145
171
  };
146
172
 
@@ -163,7 +189,14 @@ function normalizeHistoryIndex(value: unknown, maxIndex: number): number {
163
189
  function isWorkspacePanelKind(
164
190
  value: unknown,
165
191
  ): value is PersistedWorkspacePanelKind {
166
- return value === 'child-session' || value === 'file' || value === 'cron';
192
+ return (
193
+ value === 'overview' ||
194
+ value === 'child-sessions' ||
195
+ value === 'child-session' ||
196
+ value === 'project-files' ||
197
+ value === 'file' ||
198
+ value === 'cron'
199
+ );
167
200
  }
168
201
 
169
202
  function normalizePersistedWorkspaceNavigationEntry(
@@ -172,8 +205,13 @@ function normalizePersistedWorkspaceNavigationEntry(
172
205
  if (!isRecord(value)) {
173
206
  return null;
174
207
  }
175
- if (value.kind === 'cron') {
176
- return { kind: 'cron' };
208
+ if (
209
+ value.kind === 'overview' ||
210
+ value.kind === 'child-sessions' ||
211
+ value.kind === 'project-files' ||
212
+ value.kind === 'cron'
213
+ ) {
214
+ return { kind: value.kind };
177
215
  }
178
216
  const key = normalizeOptionalString(value.key);
179
217
  if (!key || (value.kind !== 'child-session' && value.kind !== 'file')) {
@@ -185,50 +223,31 @@ function normalizePersistedWorkspaceNavigationEntry(
185
223
  };
186
224
  }
187
225
 
188
- function createWorkspaceNavigationEntryFromSnapshot({
189
- activeChildSessionKey,
190
- activeWorkspaceFileKey,
191
- activeWorkspacePanelKind,
192
- }: {
193
- activeWorkspacePanelKind: ChatThreadSnapshot['activeWorkspacePanelKind'];
194
- activeChildSessionKey: string | null;
195
- activeWorkspaceFileKey: string | null;
196
- }): ChatWorkspaceNavigationEntry | null {
197
- if (activeWorkspacePanelKind === 'cron') {
198
- return { kind: 'cron' };
199
- }
200
- if (activeWorkspacePanelKind === 'child-session' && activeChildSessionKey) {
201
- return {
202
- kind: 'child-session',
203
- key: activeChildSessionKey,
204
- };
205
- }
206
- if (activeWorkspacePanelKind === 'file' && activeWorkspaceFileKey) {
207
- return {
208
- kind: 'file',
209
- key: activeWorkspaceFileKey,
210
- };
211
- }
212
- return null;
213
- }
214
-
215
226
  function normalizePersistedWorkspaceSnapshot(
216
227
  value: unknown,
217
228
  ): PersistedChatWorkspaceSnapshot | null {
218
229
  if (!isRecord(value)) {
219
230
  return null;
220
231
  }
221
- const workspaceFileTabs = Array.isArray(value.workspaceFileTabs)
232
+ const workspacePanelParentKey = normalizeOptionalString(value.workspacePanelParentKey);
233
+ const activeWorkspaceFileKey = normalizeOptionalString(value.activeWorkspaceFileKey);
234
+ const normalizedWorkspaceFileTabs = Array.isArray(value.workspaceFileTabs)
222
235
  ? value.workspaceFileTabs
223
236
  .map(normalizePersistedWorkspaceFileTab)
224
237
  .filter((tab): tab is ChatWorkspaceFileTab => tab !== null)
225
- .slice(-CHAT_THREAD_MAX_PERSISTED_WORKSPACE_FILE_TABS)
226
238
  : [];
227
- const activeWorkspaceFileKey = normalizeOptionalString(value.activeWorkspaceFileKey);
239
+ const workspaceFileTabs = retainWorkspaceFileTabs(
240
+ normalizedWorkspaceFileTabs,
241
+ activeWorkspaceFileKey,
242
+ CHAT_THREAD_MAX_PERSISTED_WORKSPACE_FILE_TABS,
243
+ );
244
+ const sessionWorkspaceFileTabs = workspaceFileTabs.filter(
245
+ (tab) => tab.parentSessionKey === workspacePanelParentKey,
246
+ );
228
247
  const resolvedActiveWorkspaceFileKey =
229
- activeWorkspaceFileKey && workspaceFileTabs.some((tab) => tab.key === activeWorkspaceFileKey)
248
+ activeWorkspaceFileKey && sessionWorkspaceFileTabs.some((tab) => tab.key === activeWorkspaceFileKey)
230
249
  ? activeWorkspaceFileKey
231
- : workspaceFileTabs[0]?.key ?? null;
250
+ : sessionWorkspaceFileTabs[0]?.key ?? null;
232
251
  const activeWorkspacePanelKind = isWorkspacePanelKind(value.activeWorkspacePanelKind)
233
252
  ? value.activeWorkspacePanelKind
234
253
  : null;
@@ -237,9 +256,17 @@ function normalizePersistedWorkspaceSnapshot(
237
256
  ? null
238
257
  : activeWorkspacePanelKind;
239
258
  const activeChildSessionKey = normalizeOptionalString(value.activeChildSessionKey);
259
+ const closedWorkspaceTabEntries = Array.isArray(value.closedWorkspaceTabEntries)
260
+ ? value.closedWorkspaceTabEntries
261
+ .map(normalizePersistedWorkspaceNavigationEntry)
262
+ .filter((entry): entry is ChatWorkspaceNavigationEntry =>
263
+ entry !== null && entry.kind !== 'overview',
264
+ )
265
+ : [];
240
266
  const fallbackHistoryEntry = createWorkspaceNavigationEntryFromSnapshot({
241
267
  activeWorkspacePanelKind: resolvedActiveWorkspacePanelKind,
242
268
  activeChildSessionKey,
269
+ activeSideChatDraft: null,
243
270
  activeWorkspaceFileKey: resolvedActiveWorkspaceFileKey,
244
271
  });
245
272
  const normalizedNavigationHistory = Array.isArray(value.workspaceNavigationHistory)
@@ -265,13 +292,17 @@ function normalizePersistedWorkspaceSnapshot(
265
292
  : 0;
266
293
 
267
294
  return {
268
- workspacePanelParentKey: normalizeOptionalString(value.workspacePanelParentKey),
295
+ workspacePanelParentKey,
269
296
  activeWorkspacePanelKind: resolvedActiveWorkspacePanelKind,
270
297
  activeChildSessionKey,
271
298
  workspaceFileTabs,
272
299
  activeWorkspaceFileKey: resolvedActiveWorkspaceFileKey,
300
+ closedWorkspaceTabEntries,
273
301
  workspaceNavigationHistory,
274
302
  workspaceNavigationHistoryIndex,
303
+ workspacePanelWidth: normalizeChatWorkspacePanelWidth(
304
+ value.workspacePanelWidth,
305
+ ),
275
306
  };
276
307
  }
277
308
 
@@ -309,12 +340,18 @@ export const useChatThreadStore = create<ChatThreadStore>()(
309
340
  ? null
310
341
  : state.snapshot.activeWorkspacePanelKind,
311
342
  activeChildSessionKey: state.snapshot.activeChildSessionKey,
312
- workspaceFileTabs: state.snapshot.workspaceFileTabs
313
- .slice(-CHAT_THREAD_MAX_PERSISTED_WORKSPACE_FILE_TABS)
343
+ workspaceFileTabs: retainWorkspaceFileTabs(
344
+ state.snapshot.workspaceFileTabs,
345
+ state.snapshot.activeWorkspaceFileKey,
346
+ CHAT_THREAD_MAX_PERSISTED_WORKSPACE_FILE_TABS,
347
+ )
314
348
  .map(toPersistedWorkspaceFileTab),
315
349
  activeWorkspaceFileKey: state.snapshot.activeWorkspaceFileKey,
350
+ closedWorkspaceTabEntries: state.snapshot.closedWorkspaceTabEntries
351
+ .filter((entry) => entry.kind !== 'overview' && entry.kind !== 'side-chat-draft'),
316
352
  workspaceNavigationHistory,
317
353
  workspaceNavigationHistoryIndex,
354
+ workspacePanelWidth: state.snapshot.workspacePanelWidth,
318
355
  }
319
356
  };
320
357
  },
@@ -11,6 +11,8 @@ export type ChatSkillRecord = {
11
11
  key: string;
12
12
  label: string;
13
13
  scopeLabel?: string;
14
+ groupKey?: string;
15
+ groupLabel?: string;
14
16
  description?: string;
15
17
  descriptionZh?: string;
16
18
  badgeLabel?: string;
@@ -1,25 +1,42 @@
1
- import { fireEvent, render, screen } from '@testing-library/react';
2
- import { beforeEach, describe, expect, it } from 'vitest';
3
- import { AppearanceSettingsPage } from '@/features/settings/pages/appearance-settings-page';
4
- import { useSideDockStore } from '@/features/side-dock';
1
+ import { fireEvent, render, screen } from "@testing-library/react";
2
+ import { beforeEach, describe, expect, it } from "vitest";
3
+ import { AppearanceSettingsPage } from "@/features/settings/pages/appearance-settings-page";
4
+ import { useChatMessageLayoutStore } from "@/features/chat";
5
+ import { useSideDockStore } from "@/features/side-dock";
5
6
 
6
- describe('AppearanceSettingsPage', () => {
7
+ describe("AppearanceSettingsPage", () => {
7
8
  beforeEach(() => {
8
9
  window.localStorage.clear();
10
+ useChatMessageLayoutStore.getState().setLayout("card");
9
11
  useSideDockStore.getState().setVisible(true);
10
12
  useSideDockStore.getState().setPinnedItems([]);
11
13
  });
12
14
 
13
- it('reopens the SideDock from the appearance settings switch', () => {
15
+ it("reopens the SideDock from the appearance settings switch", () => {
14
16
  useSideDockStore.getState().setVisible(false);
15
17
 
16
18
  render(<AppearanceSettingsPage />);
17
19
 
18
- const visibilitySwitch = screen.getByRole('switch', { name: 'Show SideDock' });
19
- expect(visibilitySwitch.getAttribute('aria-checked')).toBe('false');
20
+ const visibilitySwitch = screen.getByRole("switch", {
21
+ name: "Show SideDock",
22
+ });
23
+ expect(visibilitySwitch.getAttribute("aria-checked")).toBe("false");
20
24
 
21
25
  fireEvent.click(visibilitySwitch);
22
26
 
23
27
  expect(useSideDockStore.getState().isVisible).toBe(true);
24
28
  });
29
+
30
+ it("switches the chat message layout from appearance settings", () => {
31
+ render(<AppearanceSettingsPage />);
32
+
33
+ const cardOption = screen.getByRole("radio", { name: /^Cards/ });
34
+ const flatOption = screen.getByRole("radio", { name: /^Flat/ });
35
+ expect(cardOption.getAttribute("aria-checked")).toBe("true");
36
+
37
+ fireEvent.click(flatOption);
38
+
39
+ expect(flatOption.getAttribute("aria-checked")).toBe("true");
40
+ expect(useChatMessageLayoutStore.getState().layout).toBe("flat");
41
+ });
25
42
  });
@@ -1,33 +1,112 @@
1
- import { Palette } from 'lucide-react';
2
- import { PageHeader, PageLayout } from '@/app/components/layout/page-layout';
3
- import { SettingRow } from '@/shared/components/settings/setting-row';
4
- import { Card, CardContent, CardHeader, CardTitle } from '@/shared/components/ui/card';
5
- import { Switch } from '@/shared/components/ui/switch';
6
- import { useSideDockStore } from '@/features/side-dock';
7
- import { t } from '@/shared/lib/i18n';
1
+ import type { ChatMessageLayout } from "@nextclaw/agent-chat-ui";
2
+ import { Check, Palette } from "lucide-react";
3
+ import { PageHeader, PageLayout } from "@/app/components/layout/page-layout";
4
+ import { useChatMessageLayoutStore } from "@/features/chat";
5
+ import { SettingRow } from "@/shared/components/settings/setting-row";
6
+ import {
7
+ Card,
8
+ CardContent,
9
+ CardHeader,
10
+ CardTitle,
11
+ } from "@/shared/components/ui/card";
12
+ import { Switch } from "@/shared/components/ui/switch";
13
+ import { useSideDockStore } from "@/features/side-dock";
14
+ import { t } from "@/shared/lib/i18n";
15
+ import { cn } from "@/shared/lib/utils";
16
+
17
+ const CHAT_MESSAGE_LAYOUT_OPTIONS: Array<{
18
+ value: ChatMessageLayout;
19
+ labelKey: string;
20
+ descriptionKey: string;
21
+ }> = [
22
+ {
23
+ value: "card",
24
+ labelKey: "chatMessageLayoutCard",
25
+ descriptionKey: "chatMessageLayoutCardDescription",
26
+ },
27
+ {
28
+ value: "flat",
29
+ labelKey: "chatMessageLayoutFlat",
30
+ descriptionKey: "chatMessageLayoutFlatDescription",
31
+ },
32
+ ];
8
33
 
9
34
  export function AppearanceSettingsPage() {
35
+ const messageLayout = useChatMessageLayoutStore((state) => state.layout);
36
+ const setMessageLayout = useChatMessageLayoutStore(
37
+ (state) => state.setLayout,
38
+ );
10
39
  const isSideDockVisible = useSideDockStore((state) => state.isVisible);
11
40
  const setSideDockVisible = useSideDockStore((state) => state.setVisible);
12
41
 
13
42
  return (
14
43
  <PageLayout>
15
- <PageHeader title={t('appearance')} />
44
+ <PageHeader title={t("appearance")} />
16
45
  <Card>
17
46
  <CardHeader>
18
47
  <CardTitle className="flex items-center gap-2">
19
48
  <Palette className="h-4 w-4" />
20
- {t('appearance')}
49
+ {t("appearance")}
21
50
  </CardTitle>
22
51
  </CardHeader>
23
- <CardContent>
52
+ <CardContent className="space-y-4">
53
+ <SettingRow
54
+ title={t("chatMessageLayoutTitle")}
55
+ description={t("chatMessageLayoutDescription")}
56
+ >
57
+ <div
58
+ role="radiogroup"
59
+ aria-label={t("chatMessageLayoutTitle")}
60
+ className="grid gap-2 sm:grid-cols-2"
61
+ >
62
+ {CHAT_MESSAGE_LAYOUT_OPTIONS.map((option) => {
63
+ const selected = option.value === messageLayout;
64
+ return (
65
+ <button
66
+ key={option.value}
67
+ type="button"
68
+ role="radio"
69
+ aria-checked={selected}
70
+ onClick={() => setMessageLayout(option.value)}
71
+ className={cn(
72
+ "flex min-w-0 items-start gap-3 rounded-xl border px-3 py-3 text-left transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/35",
73
+ selected
74
+ ? "border-primary/35 bg-primary/5 text-foreground"
75
+ : "border-border bg-card text-muted-foreground hover:border-border/80 hover:bg-muted/45 hover:text-foreground",
76
+ )}
77
+ >
78
+ <span
79
+ className={cn(
80
+ "mt-0.5 flex h-4 w-4 shrink-0 items-center justify-center rounded-full border",
81
+ selected
82
+ ? "border-primary bg-primary text-primary-foreground"
83
+ : "border-border bg-background",
84
+ )}
85
+ >
86
+ {selected ? (
87
+ <Check className="h-2.5 w-2.5" strokeWidth={3} />
88
+ ) : null}
89
+ </span>
90
+ <span className="min-w-0">
91
+ <span className="block text-sm font-medium">
92
+ {t(option.labelKey)}
93
+ </span>
94
+ <span className="mt-1 block text-xs leading-5 text-muted-foreground">
95
+ {t(option.descriptionKey)}
96
+ </span>
97
+ </span>
98
+ </button>
99
+ );
100
+ })}
101
+ </div>
102
+ </SettingRow>
24
103
  <SettingRow
25
- title={t('sideDockVisibilityTitle')}
26
- description={t('sideDockVisibilityDescription')}
104
+ title={t("sideDockVisibilityTitle")}
105
+ description={t("sideDockVisibilityDescription")}
27
106
  control={
28
107
  <Switch
29
108
  id="appearance-side-dock-visible"
30
- aria-label={t('sideDockVisibilityTitle')}
109
+ aria-label={t("sideDockVisibilityTitle")}
31
110
  checked={isSideDockVisible}
32
111
  onCheckedChange={setSideDockVisible}
33
112
  />
package/src/index.css CHANGED
@@ -736,11 +736,16 @@
736
736
  .chat-markdown h5,
737
737
  .chat-markdown h6 {
738
738
  margin: 0.2rem 0 0;
739
+ padding-bottom: 0.5em;
739
740
  font-weight: 700;
740
741
  line-height: 1.35;
741
742
  letter-spacing: -0.01em;
742
743
  }
743
744
 
745
+ .chat-markdown :is(h1, h2, h3, h4, h5, h6) + * {
746
+ margin-top: 0;
747
+ }
748
+
744
749
  .chat-markdown h1 {
745
750
  font-size: 1.22rem;
746
751
  }
@@ -1338,9 +1343,38 @@
1338
1343
  }
1339
1344
 
1340
1345
  .chat-markdown img {
1346
+ display: block;
1347
+ width: auto;
1348
+ max-width: min(100%, 32rem);
1349
+ height: auto;
1350
+ }
1351
+
1352
+ .chat-markdown [data-chat-message-image-preview] {
1353
+ margin-block: 0.72rem;
1354
+ }
1355
+
1356
+ .chat-markdown [data-chat-message-image-preview]:first-child,
1357
+ .chat-markdown [data-chat-message-image-preview]
1358
+ + [data-chat-message-image-preview] {
1359
+ margin-top: 0;
1360
+ }
1361
+
1362
+ .chat-markdown [data-chat-message-image-preview]:last-child {
1363
+ margin-bottom: 0;
1364
+ }
1365
+
1366
+ .chat-markdown [data-chat-image-row="three-column"] {
1367
+ display: grid;
1368
+ grid-template-columns: repeat(3, minmax(0, 1fr));
1369
+ align-items: start;
1370
+ gap: 0.72rem;
1371
+ }
1372
+
1373
+ .chat-markdown
1374
+ [data-chat-image-row="three-column"]
1375
+ > [data-chat-message-image-preview] {
1341
1376
  max-width: 100%;
1342
- border-radius: 0.75rem;
1343
- border: 1px solid var(--md-border);
1377
+ margin: 0;
1344
1378
  }
1345
1379
 
1346
1380
  .chat-markdown blockquote {
@@ -0,0 +1,61 @@
1
+ import { render } from "@testing-library/react";
2
+ import { describe, expect, it } from "vitest";
3
+ import { FileTypeIcon } from "@/shared/components/file-type-icon";
4
+
5
+ function readIcon(container: HTMLElement, name: string): SVGElement {
6
+ const icon = container.querySelector<SVGElement>(
7
+ `[data-file-type-icon="${name}"]`,
8
+ );
9
+ expect(icon).not.toBeNull();
10
+ expect(icon?.innerHTML).not.toContain("undefined");
11
+ expect(icon?.childElementCount).toBeGreaterThan(0);
12
+ return icon!;
13
+ }
14
+
15
+ describe("FileTypeIcon", () => {
16
+ it("renders recognizable SVG icons for common file types", () => {
17
+ const { container } = render(
18
+ <>
19
+ <FileTypeIcon fileName="src/app.tsx" />
20
+ <FileTypeIcon fileName="scripts/setup.js" />
21
+ <FileTypeIcon fileName="docs/guide.md" />
22
+ <FileTypeIcon fileName="assets/hero.png" />
23
+ <FileTypeIcon fileName="reports/data.xlsx" />
24
+ </>,
25
+ );
26
+
27
+ expect(readIcon(container, "react-typescript").tagName).toBe("svg");
28
+ expect(readIcon(container, "javascript").tagName).toBe("svg");
29
+ expect(readIcon(container, "markdown").tagName).toBe("svg");
30
+ expect(readIcon(container, "image").tagName).toBe("svg");
31
+ expect(readIcon(container, "excel").tagName).toBe("svg");
32
+ });
33
+
34
+ it("recognizes editor-style special file names", () => {
35
+ const { container } = render(
36
+ <>
37
+ <FileTypeIcon fileName="package.json" />
38
+ <FileTypeIcon fileName="Dockerfile" />
39
+ <FileTypeIcon fileName=".env.local" />
40
+ <FileTypeIcon fileName="vitest.unit.config.ts" />
41
+ <FileTypeIcon fileName="PHOTO.JPEG" />
42
+ </>,
43
+ );
44
+
45
+ expect(readIcon(container, "npm")).toBeTruthy();
46
+ expect(readIcon(container, "docker")).toBeTruthy();
47
+ expect(readIcon(container, "dotenv")).toBeTruthy();
48
+ expect(readIcon(container, "vitest")).toBeTruthy();
49
+ expect(readIcon(container, "image")).toBeTruthy();
50
+ });
51
+
52
+ it("uses the neutral SVG fallback and compact size predictably", () => {
53
+ const { container } = render(
54
+ <FileTypeIcon fileName="artifact.customtype" size="compact" />,
55
+ );
56
+
57
+ const fallback = readIcon(container, "default");
58
+ expect(fallback.classList.contains("h-3.5")).toBe(true);
59
+ expect(fallback.getAttribute("aria-hidden")).toBe("true");
60
+ });
61
+ });
@@ -100,7 +100,7 @@ describe('BrandHeader', () => {
100
100
  expect(version).toBeTruthy();
101
101
  expect(screen.getAllByText('v0.18.11')).toHaveLength(1);
102
102
  await user.hover(version);
103
- expect(await screen.findAllByText('v0.18.11')).toHaveLength(2);
103
+ expect((await screen.findByRole('tooltip')).textContent).toBe('v0.18.11');
104
104
  expect(screen.getByText('下载 50%')).toBeTruthy();
105
105
  expect(screen.queryByRole('button', { name: '更新' })).toBeNull();
106
106
  });
@@ -145,6 +145,7 @@ describe('BrandHeader', () => {
145
145
  });
146
146
 
147
147
  it('shows a warning icon with the blocked update reason instead of a visible failure label', async () => {
148
+ const user = userEvent.setup();
148
149
  useRuntimeUpdateStore.setState({
149
150
  supported: true,
150
151
  initialized: true,
@@ -180,13 +181,19 @@ describe('BrandHeader', () => {
180
181
  const issueIcon = screen.getByLabelText('更新被阻塞');
181
182
 
182
183
  expect(issueIcon.textContent).toBe('!');
183
- expect(issueIcon.getAttribute('title')).toContain('更新被阻塞');
184
- expect(issueIcon.getAttribute('title')).toContain('根因:缺少更新签名公钥,无法验证更新包来源');
185
- expect(issueIcon.getAttribute('title')).toContain('Runtime bundle updates require a configured update public key.');
186
- expect(issueIcon.getAttribute('title')).toContain('Set NEXTCLAW_UPDATE_BUNDLE_PUBLIC_KEY');
184
+ expect(issueIcon.hasAttribute('title')).toBe(false);
185
+
186
+ await user.hover(issueIcon);
187
+
188
+ const tooltip = await screen.findByRole('tooltip');
189
+ expect(tooltip.textContent).toContain('更新被阻塞');
190
+ expect(tooltip.textContent).toContain('根因:缺少更新签名公钥,无法验证更新包来源');
191
+ expect(tooltip.textContent).toContain('Runtime bundle updates require a configured update public key.');
192
+ expect(tooltip.textContent).toContain('Set NEXTCLAW_UPDATE_BUNDLE_PUBLIC_KEY');
187
193
  });
188
194
 
189
195
  it('uses the failed update wording only for failed snapshots', async () => {
196
+ const user = userEvent.setup();
190
197
  useRuntimeUpdateStore.setState({
191
198
  supported: true,
192
199
  initialized: true,
@@ -221,8 +228,11 @@ describe('BrandHeader', () => {
221
228
  const issueIcon = screen.getByLabelText('更新失败');
222
229
 
223
230
  expect(issueIcon.textContent).toBe('!');
224
- expect(issueIcon.getAttribute('title')).toContain('更新失败');
225
- expect(issueIcon.getAttribute('title')).toContain('runtime bundle sha256 mismatch');
231
+ await user.hover(issueIcon);
232
+
233
+ const tooltip = await screen.findByRole('tooltip');
234
+ expect(tooltip.textContent).toContain('更新失败');
235
+ expect(tooltip.textContent).toContain('runtime bundle sha256 mismatch');
226
236
  expect(screen.queryByText('更新被阻塞')).toBeNull();
227
237
  });
228
238
  });