@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,8 +1,9 @@
1
1
  import type { UpdateSnapshot } from '@nextclaw/shared';
2
2
  import { runtimeUpdateManager, useRuntimeUpdateStore } from '@/features/system-status';
3
3
  import { useAppMeta } from '@/shared/hooks/use-app-meta';
4
- import { type ReactNode, useState } from 'react';
4
+ import type { ReactNode } from 'react';
5
5
  import { RuntimeStatusEntry } from '@/app/components/layout/runtime-status-entry';
6
+ import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/shared/components/ui/tooltip';
6
7
  import { t } from '@/shared/lib/i18n';
7
8
  import { cn } from '@/shared/lib/utils';
8
9
 
@@ -57,33 +58,26 @@ function BrandVersionLabel({
57
58
  versionLabel: string;
58
59
  density: BrandHeaderProps['density'];
59
60
  }) {
60
- const [isTooltipOpen, setIsTooltipOpen] = useState(false);
61
61
  const isChromeDensity = density === 'chrome';
62
62
 
63
63
  return (
64
- <span
65
- className="relative min-w-0 flex-1"
66
- onMouseEnter={() => setIsTooltipOpen(true)}
67
- onMouseLeave={() => setIsTooltipOpen(false)}
68
- onFocus={() => setIsTooltipOpen(true)}
69
- onBlur={() => setIsTooltipOpen(false)}
70
- >
71
- <span
72
- tabIndex={0}
73
- aria-label={versionLabel}
74
- className={cn(
75
- 'block min-w-0 truncate font-medium text-gray-500 outline-none',
76
- isChromeDensity ? 'text-[12px]' : 'text-[12px]',
77
- )}
78
- >
79
- {versionLabel}
80
- </span>
81
- {isTooltipOpen ? (
82
- <span className="pointer-events-none absolute left-0 top-full z-[var(--z-tooltip)] mt-1 w-max max-w-none whitespace-nowrap rounded-md border bg-popover px-3 py-1.5 text-xs font-medium text-popover-foreground shadow-md">
83
- {versionLabel}
84
- </span>
85
- ) : null}
86
- </span>
64
+ <TooltipProvider delayDuration={250}>
65
+ <Tooltip>
66
+ <TooltipTrigger asChild>
67
+ <span
68
+ tabIndex={0}
69
+ aria-label={versionLabel}
70
+ className={cn(
71
+ 'block min-w-0 flex-1 truncate font-medium text-gray-500 outline-none',
72
+ isChromeDensity ? 'text-[12px]' : 'text-[12px]',
73
+ )}
74
+ >
75
+ {versionLabel}
76
+ </span>
77
+ </TooltipTrigger>
78
+ <TooltipContent side="bottom" className="text-xs">{versionLabel}</TooltipContent>
79
+ </Tooltip>
80
+ </TooltipProvider>
87
81
  );
88
82
  }
89
83
 
@@ -149,15 +143,21 @@ function RuntimeUpdateIssueIcon({ snapshot }: { snapshot: UpdateSnapshot }) {
149
143
  : null;
150
144
  const tooltip = [title, rootCause, diagnostic, recoveryCommand].filter(Boolean).join('\n');
151
145
  return (
152
- <span
153
- role="img"
154
- aria-label={title}
155
- title={tooltip}
156
- tabIndex={0}
157
- className="inline-flex h-5 w-5 shrink-0 items-center justify-center rounded-full bg-amber-50 text-[13px] font-bold leading-none text-amber-700 ring-1 ring-amber-100"
158
- >
159
- !
160
- </span>
146
+ <TooltipProvider delayDuration={250}>
147
+ <Tooltip>
148
+ <TooltipTrigger asChild>
149
+ <span
150
+ role="img"
151
+ aria-label={title}
152
+ tabIndex={0}
153
+ className="inline-flex h-5 w-5 shrink-0 items-center justify-center rounded-full bg-amber-50 text-[13px] font-bold leading-none text-amber-700 ring-1 ring-amber-100 outline-none focus-visible:ring-2 focus-visible:ring-amber-300"
154
+ >
155
+ !
156
+ </span>
157
+ </TooltipTrigger>
158
+ <TooltipContent side="bottom" className="max-w-80 whitespace-pre-line break-words text-xs leading-relaxed">{tooltip}</TooltipContent>
159
+ </Tooltip>
160
+ </TooltipProvider>
161
161
  );
162
162
  }
163
163
 
@@ -80,6 +80,25 @@ describe('DocBrowserManager layout notifications', () => {
80
80
 
81
81
  expect(onRightPanelOpened).toHaveBeenCalledTimes(1);
82
82
  });
83
+
84
+ it('persists the docked global right panel width across rehydration', async () => {
85
+ const { result } = renderHook(() => useDocBrowser(), { wrapper });
86
+
87
+ act(() => {
88
+ result.current.setDockedWidth(610);
89
+ });
90
+
91
+ await waitFor(() => {
92
+ expect(readPersistedDocBrowserSnapshot()).toMatchObject({
93
+ state: { snapshot: { dockedWidth: 610 } },
94
+ });
95
+ });
96
+
97
+ const savedState = window.localStorage.getItem(docBrowserStorageKey);
98
+ await resetMemoryThenRehydrate(savedState);
99
+
100
+ expect(useDocBrowserStore.getState().snapshot.dockedWidth).toBe(610);
101
+ });
83
102
  });
84
103
 
85
104
  describe('DocBrowserProvider dedupe keys', () => {
@@ -24,6 +24,7 @@ const { docBrowserState } = vi.hoisted<{
24
24
  docBrowserState: {
25
25
  isOpen: true,
26
26
  mode: "docked" as "docked" | "floating",
27
+ dockedWidth: 420,
27
28
  tabs: [
28
29
  {
29
30
  id: "docs",
@@ -58,6 +59,7 @@ const { docBrowserState } = vi.hoisted<{
58
59
  openNewTab: vi.fn(),
59
60
  close: vi.fn(),
60
61
  toggleMode: vi.fn(),
62
+ setDockedWidth: vi.fn(),
61
63
  goBack: vi.fn(),
62
64
  goForward: vi.fn(),
63
65
  navigate: vi.fn(),
@@ -156,6 +158,20 @@ describe("DocBrowser", () => {
156
158
  expect(screen.getByTestId("resizable-right-panel-handle")).toBeTruthy();
157
159
  });
158
160
 
161
+ it("commits the resized docked width to the global browser state owner", () => {
162
+ render(<DocBrowser />);
163
+
164
+ firePointerEvent(
165
+ screen.getByTestId("resizable-right-panel-handle"),
166
+ "pointerdown",
167
+ { clientX: 800, pointerId: 1 },
168
+ );
169
+ firePointerEvent(window, "pointermove", { clientX: 620, pointerId: 1 });
170
+ firePointerEvent(window, "pointerup", { clientX: 620, pointerId: 1 });
171
+
172
+ expect(docBrowserState.setDockedWidth).toHaveBeenCalledWith(600);
173
+ });
174
+
159
175
  it("uses a full viewport panel without desktop resize controls in fullscreen mode", () => {
160
176
  render(<DocBrowser displayMode="fullscreen" />);
161
177
 
@@ -57,6 +57,7 @@ export function DocBrowserProvider({
57
57
  openNewTab: manager.openNewTab,
58
58
  close: manager.close,
59
59
  toggleMode: manager.toggleMode,
60
+ setDockedWidth: manager.setDockedWidth,
60
61
  navigate: manager.navigate,
61
62
  syncUrl: manager.syncUrl,
62
63
  goBack: manager.goBack,
@@ -28,6 +28,10 @@ import { ResizableRightPanel } from '@/shared/components/resizable-right-panel/r
28
28
  import { cn } from '@/shared/lib/utils';
29
29
  import { t } from '@/shared/lib/i18n';
30
30
  import { GripVertical } from 'lucide-react';
31
+ import {
32
+ DOC_BROWSER_DOCKED_MAX_WIDTH,
33
+ DOC_BROWSER_DOCKED_MIN_WIDTH,
34
+ } from '@/shared/components/doc-browser/utils/doc-browser-state.utils';
31
35
 
32
36
  type DocBrowserProps = {
33
37
  customTabRenderers?: DocBrowserCustomTabRenderers;
@@ -159,6 +163,7 @@ export function DocBrowser({ customTabRenderers = {}, displayMode = 'desktop', d
159
163
  const {
160
164
  isOpen,
161
165
  mode,
166
+ dockedWidth,
162
167
  tabs,
163
168
  activeTabId,
164
169
  activeHistory,
@@ -169,6 +174,7 @@ export function DocBrowser({ customTabRenderers = {}, displayMode = 'desktop', d
169
174
  openNewTab,
170
175
  close,
171
176
  toggleMode,
177
+ setDockedWidth,
172
178
  goBack,
173
179
  goForward,
174
180
  navigate,
@@ -386,9 +392,11 @@ export function DocBrowser({ customTabRenderers = {}, displayMode = 'desktop', d
386
392
  return (
387
393
  <ResizableRightPanel
388
394
  data-testid="doc-browser-panel"
389
- defaultWidth={420}
390
- minWidth={320}
391
- maxWidth={860}
395
+ defaultWidth={dockedWidth}
396
+ width={dockedWidth}
397
+ minWidth={DOC_BROWSER_DOCKED_MIN_WIDTH}
398
+ maxWidth={DOC_BROWSER_DOCKED_MAX_WIDTH}
399
+ onWidthCommit={setDockedWidth}
392
400
  >
393
401
  {panelContent}
394
402
  </ResizableRightPanel>
@@ -11,6 +11,7 @@ import {
11
11
  createDocBrowserActiveHistoryEntry,
12
12
  createDefaultDocBrowserState,
13
13
  createDocBrowserTab,
14
+ normalizeDocBrowserDockedWidth,
14
15
  } from '@/shared/components/doc-browser/utils/doc-browser-state.utils';
15
16
  import {
16
17
  DOC_BROWSER_HOME_TAB_KIND,
@@ -297,6 +298,10 @@ export class DocBrowserManager {
297
298
  this.onRightPanelOpened?.();
298
299
  };
299
300
 
301
+ readonly setDockedWidth = (width: number): void => {
302
+ this.setSnapshot((prev) => ({ ...prev, dockedWidth: normalizeDocBrowserDockedWidth(width) }));
303
+ };
304
+
300
305
  readonly navigate = (url: string): void => {
301
306
  this.setSnapshot((prev) => {
302
307
  if (!prev.tabs.length) {
@@ -8,7 +8,10 @@ import type {
8
8
  DocBrowserStateUpdate,
9
9
  DocBrowserTab,
10
10
  } from '@/shared/components/doc-browser/types/doc-browser.types';
11
- import { createDefaultDocBrowserState } from '@/shared/components/doc-browser/utils/doc-browser-state.utils';
11
+ import {
12
+ createDefaultDocBrowserState,
13
+ normalizeDocBrowserDockedWidth,
14
+ } from '@/shared/components/doc-browser/utils/doc-browser-state.utils';
12
15
 
13
16
  const DOC_BROWSER_STORAGE_KEY = 'nextclaw.doc-browser.state';
14
17
  const DOC_BROWSER_STORAGE_VERSION = 1;
@@ -168,6 +171,7 @@ function normalizePersistedDocBrowserState(value: unknown): DocBrowserState | nu
168
171
  return {
169
172
  isOpen: value.isOpen === true,
170
173
  mode: isDocBrowserMode(value.mode) ? value.mode : 'docked',
174
+ dockedWidth: normalizeDocBrowserDockedWidth(value.dockedWidth),
171
175
  tabs,
172
176
  activeTabId: resolvedActiveTabId,
173
177
  activeHistory: resolvedActiveHistory,
@@ -198,6 +202,7 @@ export const useDocBrowserStore = create<DocBrowserStore>()(
198
202
  snapshot: {
199
203
  isOpen: state.snapshot.isOpen,
200
204
  mode: state.snapshot.mode,
205
+ dockedWidth: state.snapshot.dockedWidth,
201
206
  tabs: state.snapshot.tabs.slice(-DOC_BROWSER_MAX_PERSISTED_TABS),
202
207
  activeTabId: state.snapshot.activeTabId,
203
208
  activeHistory: state.snapshot.activeHistory,
@@ -76,6 +76,7 @@ export type DocBrowserOpenOptions = {
76
76
  export type DocBrowserState = {
77
77
  isOpen: boolean;
78
78
  mode: DocBrowserMode;
79
+ dockedWidth: number;
79
80
  tabs: DocBrowserTab[];
80
81
  activeTabId: string;
81
82
  activeHistory: DocBrowserActiveHistoryEntry[];
@@ -90,6 +91,7 @@ export type DocBrowserActions = {
90
91
  openNewTab: () => void;
91
92
  close: () => void;
92
93
  toggleMode: () => void;
94
+ setDockedWidth: (width: number) => void;
93
95
  navigate: (url: string) => void;
94
96
  syncUrl: (url: string) => void;
95
97
  goBack: () => void;
@@ -12,8 +12,18 @@ import {
12
12
  } from './doc-browser-url.utils';
13
13
  import { t } from '@/shared/lib/i18n';
14
14
 
15
+ export const DOC_BROWSER_DOCKED_DEFAULT_WIDTH = 420;
16
+ export const DOC_BROWSER_DOCKED_MIN_WIDTH = 320;
17
+ export const DOC_BROWSER_DOCKED_MAX_WIDTH = 860;
18
+
15
19
  let tabCounter = 0;
16
20
 
21
+ export function normalizeDocBrowserDockedWidth(value: unknown): number {
22
+ return typeof value === 'number' && Number.isFinite(value)
23
+ ? Math.min(DOC_BROWSER_DOCKED_MAX_WIDTH, Math.max(DOC_BROWSER_DOCKED_MIN_WIDTH, value))
24
+ : DOC_BROWSER_DOCKED_DEFAULT_WIDTH;
25
+ }
26
+
17
27
  function nextTabId(): string {
18
28
  tabCounter += 1;
19
29
  return `doc-tab-${Date.now()}-${tabCounter}`;
@@ -58,6 +68,7 @@ export function createDefaultDocBrowserState(): DocBrowserState {
58
68
  return {
59
69
  isOpen: false,
60
70
  mode: 'docked',
71
+ dockedWidth: DOC_BROWSER_DOCKED_DEFAULT_WIDTH,
61
72
  tabs: [initialTab],
62
73
  activeTabId: initialTab.id,
63
74
  activeHistory: [createDocBrowserActiveHistoryEntry(initialTab)],
@@ -0,0 +1,272 @@
1
+ import { Icon, type IconifyIcon } from "@iconify/react";
2
+ import defaultFileIcon from "@iconify/icons-vscode-icons/default-file";
3
+ import audioIcon from "@iconify/icons-vscode-icons/file-type-audio";
4
+ import bunIcon from "@iconify/icons-vscode-icons/file-type-bun";
5
+ import configIcon from "@iconify/icons-vscode-icons/file-type-config";
6
+ import cppIcon from "@iconify/icons-vscode-icons/file-type-cpp";
7
+ import csharpIcon from "@iconify/icons-vscode-icons/file-type-csharp";
8
+ import cssIcon from "@iconify/icons-vscode-icons/file-type-css";
9
+ import dockerIcon from "@iconify/icons-vscode-icons/file-type-docker";
10
+ import dotenvIcon from "@iconify/icons-vscode-icons/file-type-dotenv";
11
+ import eslintIcon from "@iconify/icons-vscode-icons/file-type-eslint";
12
+ import excelIcon from "@iconify/icons-vscode-icons/file-type-excel";
13
+ import fontIcon from "@iconify/icons-vscode-icons/file-type-font";
14
+ import gitIcon from "@iconify/icons-vscode-icons/file-type-git";
15
+ import goIcon from "@iconify/icons-vscode-icons/file-type-go";
16
+ import graphqlIcon from "@iconify/icons-vscode-icons/file-type-graphql";
17
+ import htmlIcon from "@iconify/icons-vscode-icons/file-type-html";
18
+ import imageIcon from "@iconify/icons-vscode-icons/file-type-image";
19
+ import javaIcon from "@iconify/icons-vscode-icons/file-type-java";
20
+ import javascriptIcon from "@iconify/icons-vscode-icons/file-type-js";
21
+ import jsonIcon from "@iconify/icons-vscode-icons/file-type-json";
22
+ import kotlinIcon from "@iconify/icons-vscode-icons/file-type-kotlin";
23
+ import licenseIcon from "@iconify/icons-vscode-icons/file-type-license";
24
+ import luaIcon from "@iconify/icons-vscode-icons/file-type-lua";
25
+ import makefileIcon from "@iconify/icons-vscode-icons/file-type-makefile";
26
+ import markdownIcon from "@iconify/icons-vscode-icons/file-type-markdown";
27
+ import npmIcon from "@iconify/icons-vscode-icons/file-type-npm";
28
+ import pdfIcon from "@iconify/icons-vscode-icons/file-type-pdf2";
29
+ import phpIcon from "@iconify/icons-vscode-icons/file-type-php";
30
+ import pnpmIcon from "@iconify/icons-vscode-icons/file-type-pnpm";
31
+ import powerpointIcon from "@iconify/icons-vscode-icons/file-type-powerpoint";
32
+ import prettierIcon from "@iconify/icons-vscode-icons/file-type-prettier";
33
+ import pythonIcon from "@iconify/icons-vscode-icons/file-type-python";
34
+ import reactJavascriptIcon from "@iconify/icons-vscode-icons/file-type-reactjs";
35
+ import reactTypescriptIcon from "@iconify/icons-vscode-icons/file-type-reactts";
36
+ import rubyIcon from "@iconify/icons-vscode-icons/file-type-ruby";
37
+ import rustIcon from "@iconify/icons-vscode-icons/file-type-rust";
38
+ import sassIcon from "@iconify/icons-vscode-icons/file-type-sass";
39
+ import shellIcon from "@iconify/icons-vscode-icons/file-type-shell";
40
+ import sqlIcon from "@iconify/icons-vscode-icons/file-type-sql";
41
+ import svelteIcon from "@iconify/icons-vscode-icons/file-type-svelte";
42
+ import svgIcon from "@iconify/icons-vscode-icons/file-type-svg";
43
+ import swiftIcon from "@iconify/icons-vscode-icons/file-type-swift";
44
+ import tailwindIcon from "@iconify/icons-vscode-icons/file-type-tailwind";
45
+ import textIcon from "@iconify/icons-vscode-icons/file-type-text";
46
+ import tomlIcon from "@iconify/icons-vscode-icons/file-type-toml";
47
+ import tsconfigIcon from "@iconify/icons-vscode-icons/file-type-tsconfig";
48
+ import typescriptIcon from "@iconify/icons-vscode-icons/file-type-typescript";
49
+ import videoIcon from "@iconify/icons-vscode-icons/file-type-video";
50
+ import viteIcon from "@iconify/icons-vscode-icons/file-type-vite";
51
+ import vitestIcon from "@iconify/icons-vscode-icons/file-type-vitest";
52
+ import vueIcon from "@iconify/icons-vscode-icons/file-type-vue";
53
+ import wordIcon from "@iconify/icons-vscode-icons/file-type-word";
54
+ import xmlIcon from "@iconify/icons-vscode-icons/file-type-xml";
55
+ import yamlIcon from "@iconify/icons-vscode-icons/file-type-yaml";
56
+ import yarnIcon from "@iconify/icons-vscode-icons/file-type-yarn";
57
+ import zipIcon from "@iconify/icons-vscode-icons/file-type-zip";
58
+ import { cn } from "@/shared/lib/utils";
59
+
60
+ type FileTypeIconProps = {
61
+ className?: string;
62
+ fileName: string;
63
+ size?: "compact" | "default";
64
+ };
65
+
66
+ type FileIconVisual = {
67
+ icon: IconifyIcon;
68
+ name: string;
69
+ };
70
+
71
+ type BundledIcon = IconifyIcon | { default: IconifyIcon };
72
+
73
+ type FileIconRule = FileIconVisual & {
74
+ extensions: readonly string[];
75
+ };
76
+
77
+ const SPECIAL_FILE_RULES: Array<FileIconVisual & { pattern: RegExp }> = [
78
+ {
79
+ icon: dockerIcon,
80
+ name: "docker",
81
+ pattern: /^(dockerfile|\.dockerignore)$/,
82
+ },
83
+ { icon: dotenvIcon, name: "dotenv", pattern: /^\.env(?:\.|$)/ },
84
+ {
85
+ icon: gitIcon,
86
+ name: "git",
87
+ pattern: /^\.git(?:attributes|ignore|modules)$/,
88
+ },
89
+ { icon: makefileIcon, name: "makefile", pattern: /^makefile$/ },
90
+ { icon: licenseIcon, name: "license", pattern: /^licen[cs]e(?:\.|$)/ },
91
+ { icon: markdownIcon, name: "markdown", pattern: /^readme(?:\.|$)/ },
92
+ { icon: npmIcon, name: "npm", pattern: /^package(?:-lock)?\.json$/ },
93
+ {
94
+ icon: pnpmIcon,
95
+ name: "pnpm",
96
+ pattern: /^(pnpm-lock\.yaml|pnpm-workspace\.yaml)$/,
97
+ },
98
+ { icon: yarnIcon, name: "yarn", pattern: /^yarn\.lock$/ },
99
+ { icon: bunIcon, name: "bun", pattern: /^bun\.lockb?$/ },
100
+ {
101
+ icon: tsconfigIcon,
102
+ name: "tsconfig",
103
+ pattern: /^tsconfig(?:\..+)?\.json$/,
104
+ },
105
+ { icon: viteIcon, name: "vite", pattern: /^vite\.config\./ },
106
+ { icon: vitestIcon, name: "vitest", pattern: /^vitest(?:\..+)?\.config\./ },
107
+ { icon: tailwindIcon, name: "tailwind", pattern: /^tailwind\.config\./ },
108
+ {
109
+ icon: eslintIcon,
110
+ name: "eslint",
111
+ pattern: /^(eslint\.config\.|\.eslintrc)/,
112
+ },
113
+ {
114
+ icon: prettierIcon,
115
+ name: "prettier",
116
+ pattern: /^(prettier\.config\.|\.prettierrc)/,
117
+ },
118
+ ];
119
+
120
+ const FILE_ICON_RULES: FileIconRule[] = [
121
+ { icon: reactTypescriptIcon, name: "react-typescript", extensions: ["tsx"] },
122
+ { icon: reactJavascriptIcon, name: "react-javascript", extensions: ["jsx"] },
123
+ {
124
+ icon: typescriptIcon,
125
+ name: "typescript",
126
+ extensions: ["ts", "mts", "cts"],
127
+ },
128
+ {
129
+ icon: javascriptIcon,
130
+ name: "javascript",
131
+ extensions: ["js", "mjs", "cjs"],
132
+ },
133
+ { icon: jsonIcon, name: "json", extensions: ["json", "json5", "jsonc"] },
134
+ { icon: markdownIcon, name: "markdown", extensions: ["md", "mdx"] },
135
+ { icon: htmlIcon, name: "html", extensions: ["html", "htm"] },
136
+ { icon: cssIcon, name: "css", extensions: ["css", "pcss", "less"] },
137
+ { icon: sassIcon, name: "sass", extensions: ["sass", "scss"] },
138
+ { icon: vueIcon, name: "vue", extensions: ["vue"] },
139
+ { icon: svelteIcon, name: "svelte", extensions: ["svelte"] },
140
+ { icon: pythonIcon, name: "python", extensions: ["py", "pyw"] },
141
+ { icon: goIcon, name: "go", extensions: ["go"] },
142
+ { icon: rustIcon, name: "rust", extensions: ["rs"] },
143
+ { icon: javaIcon, name: "java", extensions: ["java"] },
144
+ { icon: kotlinIcon, name: "kotlin", extensions: ["kt", "kts"] },
145
+ { icon: swiftIcon, name: "swift", extensions: ["swift"] },
146
+ { icon: cppIcon, name: "cpp", extensions: ["c", "cc", "cpp", "h", "hpp"] },
147
+ { icon: csharpIcon, name: "csharp", extensions: ["cs"] },
148
+ { icon: phpIcon, name: "php", extensions: ["php"] },
149
+ { icon: rubyIcon, name: "ruby", extensions: ["rb"] },
150
+ { icon: luaIcon, name: "lua", extensions: ["lua"] },
151
+ {
152
+ icon: shellIcon,
153
+ name: "shell",
154
+ extensions: ["sh", "bash", "zsh", "fish", "ps1"],
155
+ },
156
+ { icon: graphqlIcon, name: "graphql", extensions: ["graphql", "gql"] },
157
+ { icon: yamlIcon, name: "yaml", extensions: ["yaml", "yml"] },
158
+ { icon: tomlIcon, name: "toml", extensions: ["toml"] },
159
+ { icon: xmlIcon, name: "xml", extensions: ["xml"] },
160
+ { icon: sqlIcon, name: "sql", extensions: ["sql", "sqlite", "db"] },
161
+ { icon: svgIcon, name: "svg", extensions: ["svg"] },
162
+ {
163
+ icon: imageIcon,
164
+ name: "image",
165
+ extensions: [
166
+ "avif",
167
+ "bmp",
168
+ "gif",
169
+ "heic",
170
+ "ico",
171
+ "jpeg",
172
+ "jpg",
173
+ "png",
174
+ "tif",
175
+ "tiff",
176
+ "webp",
177
+ ],
178
+ },
179
+ {
180
+ icon: audioIcon,
181
+ name: "audio",
182
+ extensions: ["aac", "flac", "m4a", "mp3", "ogg", "opus", "wav", "weba"],
183
+ },
184
+ {
185
+ icon: videoIcon,
186
+ name: "video",
187
+ extensions: ["avi", "m4v", "mkv", "mov", "mp4", "webm", "wmv"],
188
+ },
189
+ { icon: pdfIcon, name: "pdf", extensions: ["pdf"] },
190
+ { icon: wordIcon, name: "word", extensions: ["doc", "docx", "odt", "rtf"] },
191
+ {
192
+ icon: excelIcon,
193
+ name: "excel",
194
+ extensions: ["csv", "numbers", "ods", "tsv", "xls", "xlsx"],
195
+ },
196
+ {
197
+ icon: powerpointIcon,
198
+ name: "powerpoint",
199
+ extensions: ["odp", "ppt", "pptx"],
200
+ },
201
+ {
202
+ icon: zipIcon,
203
+ name: "archive",
204
+ extensions: ["7z", "bz2", "gz", "rar", "tar", "tgz", "zip"],
205
+ },
206
+ {
207
+ icon: fontIcon,
208
+ name: "font",
209
+ extensions: ["eot", "otf", "ttf", "woff", "woff2"],
210
+ },
211
+ {
212
+ icon: configIcon,
213
+ name: "config",
214
+ extensions: ["cfg", "conf", "ini", "properties"],
215
+ },
216
+ { icon: textIcon, name: "text", extensions: ["log", "txt"] },
217
+ ];
218
+
219
+ const DEFAULT_FILE_VISUAL: FileIconVisual = {
220
+ icon: defaultFileIcon,
221
+ name: "default",
222
+ };
223
+
224
+ function readFileName(value: string): string {
225
+ return value.trim().split(/[\\/]/).filter(Boolean).pop() ?? value.trim();
226
+ }
227
+
228
+ function readFileExtension(fileName: string): string {
229
+ return /\.([a-z0-9]{1,16})$/i.exec(fileName)?.[1]?.toLowerCase() ?? "";
230
+ }
231
+
232
+ function resolveFileIconVisual(value: string): FileIconVisual {
233
+ const fileName = readFileName(value).toLowerCase();
234
+ const specialVisual = SPECIAL_FILE_RULES.find(({ pattern }) =>
235
+ pattern.test(fileName),
236
+ );
237
+ if (specialVisual) {
238
+ return specialVisual;
239
+ }
240
+
241
+ const extension = readFileExtension(fileName);
242
+ return (
243
+ FILE_ICON_RULES.find(({ extensions }) => extensions.includes(extension)) ??
244
+ DEFAULT_FILE_VISUAL
245
+ );
246
+ }
247
+
248
+ function resolveBundledIcon(icon: IconifyIcon): IconifyIcon {
249
+ const bundledIcon = icon as unknown as BundledIcon;
250
+ return "body" in bundledIcon ? bundledIcon : bundledIcon.default;
251
+ }
252
+
253
+ export function FileTypeIcon({
254
+ className,
255
+ fileName,
256
+ size = "default",
257
+ }: FileTypeIconProps) {
258
+ const visual = resolveFileIconVisual(fileName);
259
+
260
+ return (
261
+ <Icon
262
+ aria-hidden="true"
263
+ data-file-type-icon={visual.name}
264
+ icon={resolveBundledIcon(visual.icon)}
265
+ className={cn(
266
+ "shrink-0",
267
+ size === "compact" ? "h-3.5 w-3.5" : "h-4 w-4",
268
+ className,
269
+ )}
270
+ />
271
+ );
272
+ }
@@ -21,12 +21,14 @@ describe("ResizableRightPanel", () => {
21
21
  });
22
22
 
23
23
  it("resizes horizontally from the left handle and clamps to max width", () => {
24
+ const onWidthCommit = vi.fn();
24
25
  render(
25
26
  <ResizableRightPanel
26
27
  data-testid="right-panel"
27
28
  defaultWidth={420}
28
29
  minWidth={320}
29
30
  maxWidth={500}
31
+ onWidthCommit={onWidthCommit}
30
32
  >
31
33
  Content
32
34
  </ResizableRightPanel>,
@@ -46,6 +48,8 @@ describe("ResizableRightPanel", () => {
46
48
 
47
49
  firePointerEvent(window, "pointerup", { clientX: 600, pointerId: 1 });
48
50
 
51
+ expect(onWidthCommit).toHaveBeenCalledWith(500);
52
+
49
53
  expect(
50
54
  screen.queryByTestId("resizable-right-panel-resize-shield"),
51
55
  ).toBeNull();
@@ -64,6 +68,24 @@ describe("ResizableRightPanel", () => {
64
68
  expect(screen.getByTestId("right-panel").className).toContain("fixed");
65
69
  });
66
70
 
71
+ it("applies a persisted controlled width after store rehydration", () => {
72
+ const view = render(
73
+ <ResizableRightPanel data-testid="right-panel" width={480}>
74
+ Content
75
+ </ResizableRightPanel>,
76
+ );
77
+
78
+ expect(screen.getByTestId("right-panel").style.width).toBe("480px");
79
+
80
+ view.rerender(
81
+ <ResizableRightPanel data-testid="right-panel" width={620}>
82
+ Content
83
+ </ResizableRightPanel>,
84
+ );
85
+
86
+ expect(screen.getByTestId("right-panel").style.width).toBe("620px");
87
+ });
88
+
67
89
  it("can scope overlay positioning to its container", () => {
68
90
  render(
69
91
  <ResizableRightPanel
@@ -5,8 +5,10 @@ type ResizableRightPanelProps = ComponentPropsWithoutRef<"aside"> & {
5
5
  defaultWidth?: number;
6
6
  minWidth?: number;
7
7
  maxWidth?: number;
8
+ onWidthCommit?: (width: number) => void;
8
9
  overlay?: boolean;
9
10
  overlayScope?: "viewport" | "container";
11
+ width?: number;
10
12
  };
11
13
 
12
14
  export function ResizableRightPanel({
@@ -16,13 +18,18 @@ export function ResizableRightPanel({
16
18
  defaultWidth = 420,
17
19
  minWidth = 320,
18
20
  maxWidth = 860,
21
+ onWidthCommit,
19
22
  overlay = false,
20
23
  overlayScope = "viewport",
24
+ width: controlledWidth,
21
25
  ...props
22
26
  }: ResizableRightPanelProps) {
23
27
  const resizeRef = useRef<{ startX: number; startWidth: number } | null>(null);
28
+ const widthRef = useRef(controlledWidth ?? defaultWidth);
24
29
  const [isResizing, setIsResizing] = useState(false);
25
- const [width, setWidth] = useState(defaultWidth);
30
+ const [dragWidth, setDragWidth] = useState<number | null>(null);
31
+ const [uncontrolledWidth, setUncontrolledWidth] = useState(defaultWidth);
32
+ const width = dragWidth ?? controlledWidth ?? uncontrolledWidth;
26
33
  const overlayClassName = overlay
27
34
  ? overlayScope === "container"
28
35
  ? "absolute inset-0 z-30"
@@ -35,14 +42,22 @@ export function ResizableRightPanel({
35
42
  event.stopPropagation();
36
43
  event.currentTarget.setPointerCapture?.(event.pointerId);
37
44
  setIsResizing(true);
45
+ widthRef.current = width;
38
46
  resizeRef.current = { startX: event.clientX, startWidth: width };
39
47
  const onMove = (moveEvent: PointerEvent) => {
40
48
  const resizing = resizeRef.current;
41
49
  if (!resizing) return;
42
50
  const nextWidth = resizing.startWidth + resizing.startX - moveEvent.clientX;
43
- setWidth(Math.max(minWidth, Math.min(maxWidth, nextWidth)));
51
+ const clampedWidth = Math.max(minWidth, Math.min(maxWidth, nextWidth));
52
+ widthRef.current = clampedWidth;
53
+ setDragWidth(clampedWidth);
44
54
  };
45
55
  const onUp = () => {
56
+ if (controlledWidth === undefined) {
57
+ setUncontrolledWidth(widthRef.current);
58
+ }
59
+ onWidthCommit?.(widthRef.current);
60
+ setDragWidth(null);
46
61
  setIsResizing(false);
47
62
  resizeRef.current = null;
48
63
  window.removeEventListener("pointermove", onMove);