@nextclaw/ui 0.15.4 → 0.15.6

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 (300) hide show
  1. package/CHANGELOG.md +41 -0
  2. package/dist/assets/abnfDiagram-VRR7QNED-BYpK0F5F.js +1 -0
  3. package/dist/assets/{aiden0z-pptx-renderer.es-Bd99vut2.js → aiden0z-pptx-renderer.es-D87ywFdC.js} +1 -1
  4. package/dist/assets/api-Dyb5rgh0.js +13 -0
  5. package/dist/assets/app-presenter-provider-7jV0nPiL.js +106 -0
  6. package/dist/assets/{appearance-settings-page-fLIJbx1C.js → appearance-settings-page-DFm3BbEb.js} +1 -1
  7. package/dist/assets/arc-DFELHWM2.js +1 -0
  8. package/dist/assets/architecture-TIHT7OUA-ClwVFq-K.js +1 -0
  9. package/dist/assets/architectureDiagram-ZJ3FMSHR-CvRhdovL.js +36 -0
  10. package/dist/assets/array-BqYdCIE6.js +1 -0
  11. package/dist/assets/blockDiagram-677ZJIJ3-DVYNkbR_.js +132 -0
  12. package/dist/assets/{book-open-DWUjS6aW.js → book-open-L6LCuqOr.js} +1 -1
  13. package/dist/assets/c4Diagram-LMCZKHZV-B1w-iwRe.js +10 -0
  14. package/dist/assets/channel-BEHOSFc5.js +1 -0
  15. package/dist/assets/{channels-list-page-CvCTJraa.js → channels-list-page-DOaPKNJA.js} +2 -2
  16. package/dist/assets/{chat-page-B42UUIFR.js → chat-page-Cw9uDhD_.js} +1 -1
  17. package/dist/assets/chunk-2Q5K7J3B-DrIhesSX.js +1 -0
  18. package/dist/assets/chunk-32BRIVSS-kG3lg0bC.js +1 -0
  19. package/dist/assets/chunk-52WLFC77-BLeB7iGk.js +10 -0
  20. package/dist/assets/chunk-5FCAYU7R-BQga1Rr7.js +1 -0
  21. package/dist/assets/chunk-5HE753X5-DU81hkcF.js +1 -0
  22. package/dist/assets/chunk-5JV3BV7I-C2bAx0m7.js +1 -0
  23. package/dist/assets/chunk-5TONJI2A-BExIIgWY.js +2 -0
  24. package/dist/assets/chunk-5VM5RSS4-C3XB05yh.js +15 -0
  25. package/dist/assets/chunk-7BUUIJ7U-JiyRtrGu.js +1 -0
  26. package/dist/assets/chunk-BIQX33UG-BZRV20pI.js +1 -0
  27. package/dist/assets/chunk-C7G6YPKG-AsyDSQ9j.js +1 -0
  28. package/dist/assets/chunk-CQNSW5MT-iN-fiXVA.js +1 -0
  29. package/dist/assets/chunk-CYSBUYHQ-DhC_TRzI.js +1 -0
  30. package/dist/assets/chunk-EMLP6XTP-QFLK8R6d.js +1 -0
  31. package/dist/assets/chunk-EX3LRPZG-DzUwhkCz.js +231 -0
  32. package/dist/assets/chunk-FWX5IMBZ-DFelpZBy.js +2 -0
  33. package/dist/assets/chunk-HOUHSVGY-BMPwFEwN.js +1 -0
  34. package/dist/assets/chunk-ICXQ74PX-DH8NJMok.js +2 -0
  35. package/dist/assets/chunk-JG7HCLWE-D-95AsO4.js +2 -0
  36. package/dist/assets/chunk-JWPE2WC7-DIaTv8kx.js +1 -0
  37. package/dist/assets/chunk-KEIR6QF5-A3jEqwzn.js +161 -0
  38. package/dist/assets/chunk-MOJQB5TN-BPFLzWgj.js +88 -0
  39. package/dist/assets/chunk-MOZMSUNE-DR_fzxG0.js +1 -0
  40. package/dist/assets/chunk-OGEWGWER-TIcEoPPH.js +1 -0
  41. package/dist/assets/chunk-OSBZ3O6U-BALNvSfG.js +1 -0
  42. package/dist/assets/chunk-PUDLZKDR-I-EXhf_k.js +156 -0
  43. package/dist/assets/chunk-Q4XR5HBZ-uvIGstBS.js +70 -0
  44. package/dist/assets/chunk-QBLGF6JB-r-TjLdxa.js +1 -0
  45. package/dist/assets/chunk-R7FJI6CG-N-1LDgvy.js +1 -0
  46. package/dist/assets/chunk-RYQCIY6F-jxNASx65.js +1 -0
  47. package/dist/assets/chunk-U6XO7XAA-Cphbuc7q.js +2 -0
  48. package/dist/assets/chunk-V7JOEXUC-BLEuBf1s.js +206 -0
  49. package/dist/assets/chunk-VAUOI2AC-Dpcz6BhT.js +1 -0
  50. package/dist/assets/chunk-VR4S4FIN-BAUbR0ke.js +1 -0
  51. package/dist/assets/chunk-WYO6CB5R-Do6SJrnY.js +127 -0
  52. package/dist/assets/chunk-XXDRQBXY-C2QjjwYX.js +1 -0
  53. package/dist/assets/chunk-Y2CYZVJY-BGrd8tLy.js +1 -0
  54. package/dist/assets/chunk-YOTPTUD7-CQqvMTCr.js +1 -0
  55. package/dist/assets/chunk-ZGVPDNZ5-B7kWWaXv.js +62 -0
  56. package/dist/assets/chunk-ZIRB5QZD-DTU0cDNh.js +32 -0
  57. package/dist/assets/classDiagram-OUVF2IWQ-DESMaY3_.js +1 -0
  58. package/dist/assets/classDiagram-v2-EOCWNBFH-DwOlByYH.js +1 -0
  59. package/dist/assets/{config-split-page-DBgt9S8b.js → config-split-page-CqlqjnCK.js} +1 -1
  60. package/dist/assets/{confirm-dialog-GSXIvoRU.js → confirm-dialog-bAp8NU93.js} +1 -1
  61. package/dist/assets/cose-bilkent-JH36ORCC-B9LrtV-q.js +1 -0
  62. package/dist/assets/cynefin-VYW2F7L2-CzjeeOq9.js +1 -0
  63. package/dist/assets/cynefinDiagram-TSTJHNR4-BfjvW4su.js +62 -0
  64. package/dist/assets/cytoscape.esm-CRW8q92n.js +321 -0
  65. package/dist/assets/dagre-DkiXZKkL.js +1 -0
  66. package/dist/assets/dagre-VKFMJZFB-B1pQKQSY.js +4 -0
  67. package/dist/assets/defaultLocale-Cdv9x6q7.js +1 -0
  68. package/dist/assets/desktop-update-config-2SjMfCMl.js +1 -0
  69. package/dist/assets/diagram-FQU43EPY-cB4EZdaP.js +3 -0
  70. package/dist/assets/diagram-G47NLZAW-CtW8im1B.js +24 -0
  71. package/dist/assets/diagram-NH7WQ7WH-BDCtrCCm.js +24 -0
  72. package/dist/assets/diagram-OA4YK3LP-DEMJ42Gt.js +30 -0
  73. package/dist/assets/diagram-WEI45ONY-CdCcxSYU.js +41 -0
  74. package/dist/assets/{dist-DC7f4TJH.js → dist-CrtbGLlh.js} +1 -1
  75. package/dist/assets/dist-dw-gAfM_.js +1 -0
  76. package/dist/assets/doc-browser-A5IRyDXQ2.js +1 -0
  77. package/dist/assets/{doc-browser-D0monHjG.js → doc-browser-BmnQa30-.js} +1 -1
  78. package/dist/assets/{doc-browser-context-CGwEOhBy.js → doc-browser-context-BlpUUFrR.js} +1 -1
  79. package/dist/assets/{docx-preview-eFH9DRsS.js → docx-preview-zis3U0Fa.js} +1 -1
  80. package/dist/assets/{download-DiP9zx-J.js → download-BhZZNXD8.js} +1 -1
  81. package/dist/assets/ebnfDiagram-CCIWWBDH-DBZETBwE.js +1 -0
  82. package/dist/assets/erDiagram-Q63AITRT-y2zDXprP.js +85 -0
  83. package/dist/assets/eventmodeling-45OFAUF4-C38BqG8w.js +1 -0
  84. package/dist/assets/{external-link-C6ZeSPef.js → external-link-B9Uoq5J7.js} +1 -1
  85. package/dist/assets/{eye-suvKmtwn.js → eye-K5knauEM.js} +1 -1
  86. package/dist/assets/flowDiagram-23GEKE2U-BoKk8RRq.js +1 -0
  87. package/dist/assets/{folder-CMELbQGG.js → folder-BIYZZWJv.js} +1 -1
  88. package/dist/assets/{form-actions-CUAzpNe_.js → form-actions-CKprWFeM.js} +1 -1
  89. package/dist/assets/ganttDiagram-NO4QXBWP-D2DP344e.js +292 -0
  90. package/dist/assets/gitGraph-TEB2WS4Q-IsQHHJuS.js +1 -0
  91. package/dist/assets/gitGraphDiagram-IHSO6WYX-D37uiwA2.js +106 -0
  92. package/dist/assets/graphlib-BcNFQr5y.js +1 -0
  93. package/dist/assets/index-BVn702m4.css +1 -0
  94. package/dist/assets/index-CFxJ1Fkg.js +2 -0
  95. package/dist/assets/info-DKCQHKI2-ofy2qlHI.js +1 -0
  96. package/dist/assets/infoDiagram-FWYZ7A6U-CG-WAIRi.js +2 -0
  97. package/dist/assets/init-D8TjIqfm.js +1 -0
  98. package/dist/assets/ishikawaDiagram-FXEZZL3T-WeCTnW0f.js +70 -0
  99. package/dist/assets/journeyDiagram-5HDEW3XC-DUFBW9Td.js +139 -0
  100. package/dist/assets/kanban-definition-HUTT4EX6-B0lxgMpg.js +89 -0
  101. package/dist/assets/katex-wEmrEFTg.js +257 -0
  102. package/dist/assets/line-B78frP_c.js +1 -0
  103. package/dist/assets/linear-C6lVEcb2.js +1 -0
  104. package/dist/assets/loader-circle-Bj2RMrCR.js +1 -0
  105. package/dist/assets/{logo-badge-Cu5UGH_4.js → logo-badge-BQ-XFZod.js} +1 -1
  106. package/dist/assets/map-Bdri5UL5.js +1 -0
  107. package/dist/assets/mcp-marketplace-page-BfA7A3n4.js +9 -0
  108. package/dist/assets/mcp-marketplace-page-CXHYO95j.js +1 -0
  109. package/dist/assets/mermaid-parser.core-Cue18vLo.js +4 -0
  110. package/dist/assets/mermaid.core-CXwWRh5i.js +11 -0
  111. package/dist/assets/mindmap-definition-LN4V7U3C-CFxz0P0-.js +96 -0
  112. package/dist/assets/{model-config-page-CWP6pyBm.js → model-config-page-BbWbW_qM.js} +1 -1
  113. package/dist/assets/ordinal-Cbd8t4LB.js +1 -0
  114. package/dist/assets/packet-7NZHBO7P-Rfopfa1o.js +1 -0
  115. package/dist/assets/path-DzBnMI1r.js +1 -0
  116. package/dist/assets/pegDiagram-2B236MQR-B_Qt9zyT.js +1 -0
  117. package/dist/assets/pie-RZYD4A2V-CJR1xKEv.js +1 -0
  118. package/dist/assets/pieDiagram-ENE6RG2P-CRQdSuwS.js +39 -0
  119. package/dist/assets/plus-TmFdwO3P.js +1 -0
  120. package/dist/assets/{popover-DImguRok.js → popover-CX9zFsuc.js} +1 -1
  121. package/dist/assets/preload-helper-D4M6sveU.js +1 -0
  122. package/dist/assets/{provider-scoped-model-input-CE7p4SbJ.js → provider-scoped-model-input-B-9_DtIP.js} +1 -1
  123. package/dist/assets/providers-config-page-xwIjGur2.js +1 -0
  124. package/dist/assets/quadrantDiagram-ABIIQ3AL-D5kPpttq.js +7 -0
  125. package/dist/assets/radar-I7S5WNFK-CDg8Z-1r.js +1 -0
  126. package/dist/assets/railroad-3IZDKUUU-BHnNmjxO.js +1 -0
  127. package/dist/assets/railroad-abnf-AHOZXSZD-DGtNcusW.js +1 -0
  128. package/dist/assets/railroad-ebnf-EBAXGLYW-BRFtsQmm.js +1 -0
  129. package/dist/assets/railroad-peg-LSFZ7HO6-Bj5IypCA.js +1 -0
  130. package/dist/assets/railroadDiagram-RFXS5EU6-DGh_TGlQ.js +1 -0
  131. package/dist/assets/{react-5sjAT_6d.js → react-DsgudwpT.js} +1 -1
  132. package/dist/assets/{refresh-cw-CHLBzWWj.js → refresh-cw-kXdvbhFf.js} +1 -1
  133. package/dist/assets/remote-B9F2uCk9.js +1 -0
  134. package/dist/assets/requirementDiagram-TGXJPOKE-EmFph0H_.js +84 -0
  135. package/dist/assets/rotate-cw-DEc6aihr.js +1 -0
  136. package/dist/assets/rough.esm-wJ1d3nQl.js +1 -0
  137. package/dist/assets/runtime-config-page-DTsi7czJ.js +1 -0
  138. package/dist/assets/sankeyDiagram-HTMAVEWB-BXWB5Ugi.js +40 -0
  139. package/dist/assets/{save-BABzyrjB.js → save-C9qpz2_2.js} +1 -1
  140. package/dist/assets/{search-BTqxG3ZG.js → search-_kEi29qo.js} +1 -1
  141. package/dist/assets/{search-config-page-BkKKmxmS.js → search-config-page-H7ug8f4D.js} +1 -1
  142. package/dist/assets/{secrets-config-page-CWJMyu-k.js → secrets-config-page-C1_6rINu.js} +2 -2
  143. package/dist/assets/security-config-Cr2ERKLu.js +1 -0
  144. package/dist/assets/{select-TYZDBNmq.js → select-MJKhKAWR.js} +1 -1
  145. package/dist/assets/sequenceDiagram-DBY2YBRQ-ClKnO7v0.js +162 -0
  146. package/dist/assets/{setting-row-DR23Z_5I.js → setting-row-DA3tJuew.js} +1 -1
  147. package/dist/assets/{settings-2-D8WWcBnw.js → settings-2-D_y7zXJz.js} +1 -1
  148. package/dist/assets/sizeCapture-X5ZJPWSS-DSo-t5ou.js +1 -0
  149. package/dist/assets/{skeleton-CloHeg0S.js → skeleton-0uTLUhm9.js} +1 -1
  150. package/dist/assets/src-Dad2ZbCt.js +1 -0
  151. package/dist/assets/stateDiagram-2N3HPSRC-BvAalBfz.js +1 -0
  152. package/dist/assets/stateDiagram-v2-6OUMAXLB-CfMU38yc.js +1 -0
  153. package/dist/assets/swimlanes-5IMT3BWC-DdVqVa1a.js +2 -0
  154. package/dist/assets/swimlanesDiagram-G3AALYLV-D9Zb9X57.js +8 -0
  155. package/dist/assets/{tabs-CX-ckWoo.js → tabs-h44XPocd.js} +1 -1
  156. package/dist/assets/{tag-chip-mCc23Rhb.js → tag-chip-bRZ04q3C.js} +1 -1
  157. package/dist/assets/timeline-definition-FHXFAJF6-D4ar8IQY.js +120 -0
  158. package/dist/assets/{trash-2-SAW1Z4Po.js → trash-2-BRpSE-Wt.js} +1 -1
  159. package/dist/assets/treeView-QDETBFTQ-Uy7M0WvH.js +1 -0
  160. package/dist/assets/treemap-6X3UGDF4-BJfGV7KX.js +1 -0
  161. package/dist/assets/use-config-CwDEAl6f.js +1 -0
  162. package/dist/assets/{use-confirm-dialog-C95F6sT1.js → use-confirm-dialog-BbnWw-8c.js} +1 -1
  163. package/dist/assets/{use-viewport-layout-C6m3Nte7.js → use-viewport-layout-CqgIMDQL.js} +1 -1
  164. package/dist/assets/vennDiagram-L72KCM5P-CDXJ_i3p.js +34 -0
  165. package/dist/assets/wardley-OPB4EBWU-q263iF3K.js +1 -0
  166. package/dist/assets/wardleyDiagram-EHGQE667-D-dSuT_7.js +78 -0
  167. package/dist/assets/x-DdEDL6ju.js +1 -0
  168. package/dist/assets/xychartDiagram-FW5EYKEG-C9JCzsZF.js +7 -0
  169. package/dist/index.html +33 -32
  170. package/package.json +8 -8
  171. package/src/features/chat/components/chat-popover-content.tsx +1 -26
  172. package/src/features/chat/components/conversation/__tests__/chat-conversation-header-section.test.tsx +84 -1
  173. package/src/features/chat/components/conversation/chat-conversation-header-section.tsx +13 -2
  174. package/src/features/chat/components/conversation/chat-conversation-workspace-section.tsx +1 -0
  175. package/src/features/chat/components/layout/__tests__/chat-sidebar-test.utils.ts +22 -0
  176. package/src/features/chat/components/layout/__tests__/chat-sidebar.test.tsx +13 -24
  177. package/src/features/chat/components/layout/chat-sidebar-desktop-layout.tsx +16 -4
  178. package/src/features/chat/components/layout/chat-sidebar.tsx +38 -2
  179. package/src/features/chat/features/conversation/components/__tests__/session-conversation-area.test.tsx +1 -0
  180. package/src/features/chat/features/conversation/components/__tests__/session-conversation-input.streaming.test.tsx +52 -2
  181. package/src/features/chat/features/conversation/components/session-conversation-area.tsx +22 -0
  182. package/src/features/chat/features/conversation/components/session-conversation-input.tsx +36 -20
  183. package/src/features/chat/features/conversation/hooks/__tests__/use-session-conversation-input-state.test.tsx +55 -0
  184. package/src/features/chat/features/conversation/hooks/use-session-conversation-input-state.ts +12 -11
  185. package/src/features/chat/features/conversation/hooks/use-session-conversation-preference-actions.ts +94 -0
  186. package/src/features/chat/features/input/hooks/__tests__/use-chat-input-surface-state.test.tsx +37 -0
  187. package/src/features/chat/features/input/hooks/use-chat-input-surface-state.ts +140 -63
  188. package/src/features/chat/features/input/input-surface-plugins/__tests__/context-reference-plugin.test.ts +175 -0
  189. package/src/features/chat/features/input/input-surface-plugins/__tests__/slash-command-plugin.test.ts +66 -0
  190. package/src/features/chat/features/input/input-surface-plugins/chat-input-product-plugin-adapters.types.ts +8 -1
  191. package/src/features/chat/features/input/input-surface-plugins/context-reference-plugin.utils.ts +274 -0
  192. package/src/features/chat/features/input/input-surface-plugins/slash-command-plugin.utils.ts +18 -11
  193. package/src/features/chat/features/input/utils/__tests__/chat-composer-state.utils.test.ts +26 -0
  194. package/src/features/chat/features/input/utils/__tests__/chat-inline-token.utils.test.ts +50 -4
  195. package/src/features/chat/features/input/utils/__tests__/chat-input-bar.utils.test.ts +51 -0
  196. package/src/features/chat/features/input/utils/chat-composer-state.utils.ts +4 -10
  197. package/src/features/chat/features/input/utils/chat-composer-token-protocol.utils.ts +29 -0
  198. package/src/features/chat/features/input/utils/chat-inline-token.utils.ts +66 -8
  199. package/src/features/chat/features/input/utils/chat-input-bar.utils.ts +62 -21
  200. package/src/features/chat/features/message/components/__tests__/chat-message-list.container.test.tsx +42 -10
  201. package/src/features/chat/features/message/components/chat-inline-file-preview.tsx +80 -0
  202. package/src/features/chat/features/message/components/chat-message-list.container.tsx +55 -60
  203. package/src/features/chat/features/message/utils/chat-message-texts.utils.ts +2 -0
  204. package/src/features/chat/features/ncp/hooks/__tests__/use-hydrated-ncp-agent.test.tsx +41 -3
  205. package/src/features/chat/features/project/components/__tests__/chat-project-create-dialog.test.tsx +67 -0
  206. package/src/features/chat/features/project/components/chat-project-create-dialog.tsx +203 -0
  207. package/src/features/chat/features/session/components/session-header/__tests__/chat-session-header-actions.test.tsx +7 -7
  208. package/src/features/chat/features/session/components/session-header/__tests__/chat-session-project-dialog.test.tsx +47 -0
  209. package/src/features/chat/features/session/components/session-header/chat-session-header-actions.tsx +6 -3
  210. package/src/features/chat/features/session/components/session-header/chat-session-project-badge.tsx +3 -0
  211. package/src/features/chat/features/session/components/session-header/chat-session-project-dialog.tsx +3 -0
  212. package/src/features/chat/features/session/components/session-header/chat-session-switcher-groups.tsx +245 -0
  213. package/src/features/chat/features/session/components/session-header/chat-session-title-switcher.tsx +81 -127
  214. package/src/features/chat/features/session/utils/__tests__/chat-sidebar-session-groups.utils.test.ts +22 -0
  215. package/src/features/chat/features/session/utils/chat-run-metadata.utils.ts +6 -6
  216. package/src/features/chat/features/session/utils/chat-sidebar-session-groups.utils.ts +12 -1
  217. package/src/features/chat/features/welcome/components/chat-conversation-welcome.tsx +3 -0
  218. package/src/features/chat/features/welcome/components/chat-welcome.tsx +1 -0
  219. package/src/features/chat/features/welcome/utils/__tests__/chat-welcome-project-options.utils.test.ts +20 -0
  220. package/src/features/chat/features/welcome/utils/chat-welcome-project-options.utils.ts +15 -2
  221. package/src/features/chat/features/workspace/components/__tests__/chat-session-workspace-file-preview.test.tsx +22 -24
  222. package/src/features/chat/features/workspace/components/__tests__/chat-session-workspace-panel.test.tsx +2 -1
  223. package/src/features/chat/features/workspace/components/chat-session-workspace-file-breadcrumbs.tsx +6 -17
  224. package/src/features/chat/features/workspace/components/chat-session-workspace-file-preview.tsx +41 -19
  225. package/src/features/chat/features/workspace/components/chat-session-workspace-panel-nav.tsx +5 -1
  226. package/src/features/chat/features/workspace/components/chat-session-workspace-panel.tsx +5 -1
  227. package/src/features/chat/features/workspace/utils/__tests__/chat-workspace-panel-view-model.utils.test.ts +104 -3
  228. package/src/features/chat/features/workspace/utils/chat-thread-workspace-session.utils.ts +83 -0
  229. package/src/features/chat/features/workspace/utils/chat-workspace-file-tab.utils.ts +4 -1
  230. package/src/features/chat/features/workspace/utils/chat-workspace-panel-view-model.utils.ts +96 -25
  231. package/src/features/chat/managers/__tests__/chat-thread-workspace.manager.test.ts +25 -0
  232. package/src/features/chat/managers/__tests__/chat-thread.manager.test.ts +5 -1
  233. package/src/features/chat/managers/chat-session-list.manager.ts +1 -0
  234. package/src/features/chat/managers/chat-thread.manager.ts +15 -37
  235. package/src/features/chat/pages/__tests__/ncp-chat-page.test.ts +23 -0
  236. package/src/features/chat/stores/__tests__/chat-thread.store.test.ts +9 -0
  237. package/src/features/chat/stores/chat-thread.store.ts +16 -36
  238. package/src/features/chat/types/chat-input-bar.types.ts +2 -0
  239. package/src/features/settings/components/config/provider-form.tsx +0 -4
  240. package/src/features/settings/utils/__tests__/provider-form-support.utils.test.ts +7 -9
  241. package/src/features/settings/utils/provider-form-model.utils.ts +0 -4
  242. package/src/features/system-status/hooks/use-current-version-release-notes-link.ts +33 -0
  243. package/src/features/system-status/index.ts +9 -0
  244. package/src/features/system-status/utils/update-release-notes.utils.ts +44 -3
  245. package/src/index.css +10 -16
  246. package/src/shared/components/common/__tests__/brand-header.test.tsx +127 -10
  247. package/src/shared/components/common/brand-header.tsx +142 -56
  248. package/src/shared/components/path-picker/__tests__/server-path-picker-dialog.test.tsx +288 -38
  249. package/src/shared/components/path-picker/server-path-picker-dialog.tsx +290 -199
  250. package/src/shared/components/path-picker/server-path-picker-directory-list.tsx +103 -0
  251. package/src/shared/components/path-picker/server-path-picker-footer.tsx +58 -0
  252. package/src/shared/components/path-picker/server-path-picker-locations.tsx +148 -0
  253. package/src/shared/components/path-picker/server-path-picker-navigation-bar.tsx +198 -0
  254. package/src/shared/components/path-picker/server-path-picker-new-folder.tsx +59 -0
  255. package/src/shared/components/path-picker/server-path-picker-toolbar.tsx +25 -0
  256. package/src/shared/hooks/use-projects.ts +28 -0
  257. package/src/shared/hooks/use-server-path-create-directory.ts +6 -0
  258. package/src/shared/hooks/use-server-path-read.ts +10 -5
  259. package/src/shared/hooks/use-server-path-search.ts +40 -0
  260. package/src/shared/lib/api/index.ts +1 -0
  261. package/src/shared/lib/api/projects/project.utils.ts +14 -0
  262. package/src/shared/lib/api/types.ts +30 -3
  263. package/src/shared/lib/api/utils/server-path.utils.ts +22 -1
  264. package/src/shared/lib/i18n/locales/en-US/chat.json +43 -2
  265. package/src/shared/lib/i18n/locales/en-US/core.json +2 -3
  266. package/src/shared/lib/i18n/locales/en-US/desktop-update.json +4 -0
  267. package/src/shared/lib/i18n/locales/en-US/path-picker.json +25 -0
  268. package/src/shared/lib/i18n/locales/zh-CN/chat.json +43 -2
  269. package/src/shared/lib/i18n/locales/zh-CN/core.json +2 -3
  270. package/src/shared/lib/i18n/locales/zh-CN/desktop-update.json +4 -0
  271. package/src/shared/lib/i18n/locales/zh-CN/path-picker.json +25 -0
  272. package/src/shared/lib/session-project/__tests__/workspace-file-breadcrumb.test.ts +1 -4
  273. package/src/shared/lib/session-project/workspace-file-breadcrumb.utils.ts +1 -18
  274. package/dist/assets/api-BOXOrpcJ.js +0 -13
  275. package/dist/assets/app-presenter-provider-CkHLbvZQ.js +0 -106
  276. package/dist/assets/desktop-update-config-7bmNeWIC.js +0 -1
  277. package/dist/assets/doc-browser-CgdoLUT92.js +0 -1
  278. package/dist/assets/index-BFeNDQ4R.css +0 -1
  279. package/dist/assets/index-D_6U-M94.js +0 -2
  280. package/dist/assets/loader-circle-tYHStefV.js +0 -1
  281. package/dist/assets/mcp-marketplace-page-BUzHDwW6.js +0 -9
  282. package/dist/assets/mcp-marketplace-page-ohUAdfqJ.js +0 -1
  283. package/dist/assets/plus-tywizkeY.js +0 -1
  284. package/dist/assets/providers-config-page-BKPo8SjH.js +0 -1
  285. package/dist/assets/remote-DXJryUxB.js +0 -1
  286. package/dist/assets/rotate-cw-BefO5brU.js +0 -1
  287. package/dist/assets/runtime-config-page-1Axv4QGu.js +0 -1
  288. package/dist/assets/security-config-DeQUIXfi.js +0 -1
  289. package/dist/assets/use-config-BucdaK23.js +0 -1
  290. package/dist/assets/x-DtCPHwCu.js +0 -1
  291. package/src/features/chat/components/__tests__/chat-popover-content.test.tsx +0 -41
  292. package/src/features/chat/components/layout/__tests__/chat-sidebar-utility-menu.test.tsx +0 -55
  293. package/src/features/chat/features/input/input-surface-plugins/__tests__/panel-app-reference-plugin.test.ts +0 -77
  294. package/src/features/chat/features/input/input-surface-plugins/panel-app-reference-plugin.utils.ts +0 -144
  295. /package/dist/assets/{config-hints-fDrYfl0l.js → config-hints-LxG80Uj5.js} +0 -0
  296. /package/dist/assets/{host-capabilities-zZvY9U63.js → host-capabilities-CFbAfrrA.js} +0 -0
  297. /package/dist/assets/{jszip.min-Das2IAnb.js → jszip.min-CP5uPNQa.js} +0 -0
  298. /package/dist/assets/{middleware-BLMHv9YF.js → middleware-CAXxl-af.js} +0 -0
  299. /package/dist/assets/{provider-models-C_yOh6DE.js → provider-models-BoUhFo9R.js} +0 -0
  300. /package/dist/assets/{xlsx-B6zVt1RK.js → xlsx-DUqJhg5u.js} +0 -0
@@ -22,10 +22,9 @@ describe('provider form model defaults', () => {
22
22
  });
23
23
 
24
24
  it('serializes local model ids with the providerId prefix', () => {
25
- expect(serializeModelsForSave(['gpt-5.5', 'openai-work/gpt-5.4'], 'openai-work')).toEqual([
26
- 'openai-work/gpt-5.5',
27
- 'openai-work/gpt-5.4'
28
- ]);
25
+ expect(
26
+ serializeModelsForSave(['gpt-5.5', 'openai-work/gpt-5.4', 'bedrock/claude-fable-5'], 'openai-work')
27
+ ).toEqual(['openai-work/gpt-5.5', 'openai-work/gpt-5.4', 'openai-work/bedrock/claude-fable-5']);
29
28
  });
30
29
 
31
30
  it('builds a minimal save payload from changed provider fields', () => {
@@ -57,15 +56,14 @@ describe('provider form model defaults', () => {
57
56
  });
58
57
  });
59
58
 
60
- it('adds local model drafts without accepting provider prefixes', () => {
59
+ it('adds provider-local model ids with nested namespace segments', () => {
61
60
  expect(addProviderLocalModel([], ' gpt-5.5 ', ['openai-work'])).toEqual({
62
61
  models: ['gpt-5.5'],
63
62
  draft: ''
64
63
  });
65
- expect(addProviderLocalModel([], ' other/gpt-5.5 ', ['openai-work'])).toEqual({
66
- models: [],
67
- draft: ' other/gpt-5.5 ',
68
- errorKey: 'providerModelInvalidProviderPrefix'
64
+ expect(addProviderLocalModel([], ' bedrock/claude-fable-5 ', ['openrouter'])).toEqual({
65
+ models: ['bedrock/claude-fable-5'],
66
+ draft: ''
69
67
  });
70
68
  });
71
69
 
@@ -8,7 +8,6 @@ import {
8
8
  type AddProviderModelResult = {
9
9
  models: string[];
10
10
  draft: string;
11
- errorKey?: 'providerModelInvalidProviderPrefix';
12
11
  };
13
12
 
14
13
  export function addProviderLocalModel(models: string[], draft: string, aliases: string[]): AddProviderModelResult {
@@ -16,9 +15,6 @@ export function addProviderLocalModel(models: string[], draft: string, aliases:
16
15
  if (!next) {
17
16
  return { models, draft };
18
17
  }
19
- if (next.includes('/')) {
20
- return { models, draft, errorKey: 'providerModelInvalidProviderPrefix' };
21
- }
22
18
  if (models.includes(next)) {
23
19
  return { models, draft: '' };
24
20
  }
@@ -0,0 +1,33 @@
1
+ import { useQuery } from '@tanstack/react-query';
2
+ import { getLanguage } from '@/shared/lib/i18n';
3
+ import {
4
+ fetchReleaseNotesData,
5
+ resolveReleaseNotesHtmlUrl,
6
+ resolveVersionReleaseNotesDataUrl,
7
+ type ReleaseNotesLink,
8
+ type ReleaseNotesLocale
9
+ } from '@/features/system-status/utils/update-release-notes.utils';
10
+
11
+ function getReleaseNotesLocale(): ReleaseNotesLocale {
12
+ return getLanguage() === 'zh' ? 'zh-CN' : 'en-US';
13
+ }
14
+
15
+ export function useCurrentVersionReleaseNotesLink(productVersion: string | undefined): ReleaseNotesLink | null {
16
+ const dataUrl = resolveVersionReleaseNotesDataUrl(productVersion);
17
+ const locale = getReleaseNotesLocale();
18
+ const releaseNotesQuery = useQuery({
19
+ queryKey: ['current-release-notes', dataUrl],
20
+ queryFn: async () => await fetchReleaseNotesData(dataUrl ?? ''),
21
+ enabled: Boolean(dataUrl),
22
+ retry: false,
23
+ staleTime: 5 * 60 * 1000
24
+ });
25
+ const url = resolveReleaseNotesHtmlUrl(releaseNotesQuery.data, locale);
26
+ const version = productVersion?.trim();
27
+ return url && version
28
+ ? {
29
+ url,
30
+ versionLabel: `v${version}`
31
+ }
32
+ : null;
33
+ }
@@ -2,7 +2,16 @@ export { useRuntimeControlPanelView, useRuntimeStatusBadgeView, useSystemStatus,
2
2
  export { systemStatusManager } from './managers/system-status.manager';
3
3
  export { isTransientRuntimeConnectionErrorMessage } from '@/shared/lib/transport';
4
4
  export { runtimeUpdateManager } from './managers/runtime-update.manager';
5
+ export { useCurrentVersionReleaseNotesLink } from './hooks/use-current-version-release-notes-link';
6
+ export {
7
+ fetchReleaseNotesData,
8
+ resolveReleaseNotesHtmlUrl,
9
+ resolveUpdateReleaseNotesLink,
10
+ resolveVersionReleaseNotesDataUrl,
11
+ } from './utils/update-release-notes.utils';
5
12
  export type { SystemStatusState, SystemStatusView } from './types/system-status.types';
6
13
  export { useSystemStatusStore } from './stores/system-status.store';
7
14
  export { useRuntimeUpdateStore } from './stores/runtime-update.store';
15
+ export type { RuntimeUpdateBusyAction } from './stores/runtime-update.store';
16
+ export type { ReleaseNotesLink, ReleaseNotesLocale } from './utils/update-release-notes.utils';
8
17
  export { SecurityConfig } from './components/security-config';
@@ -4,11 +4,20 @@ export type ReleaseNotesLocale = 'zh-CN' | 'en-US';
4
4
 
5
5
  export type ReleaseNotesText = Partial<Record<ReleaseNotesLocale, string>>;
6
6
 
7
+ export type ReleaseNotesLink = {
8
+ url: string;
9
+ versionLabel: string;
10
+ };
11
+
7
12
  export type ReleaseNotesItem = {
8
13
  title: ReleaseNotesText;
9
14
  body?: ReleaseNotesText;
10
15
  };
11
16
 
17
+ export type ReleaseNotesLinks = {
18
+ html?: ReleaseNotesText;
19
+ };
20
+
12
21
  export type ReleaseNotesSection = {
13
22
  kind: string;
14
23
  title?: ReleaseNotesText;
@@ -18,9 +27,12 @@ export type ReleaseNotesSection = {
18
27
  export type ReleaseNotesPayload = {
19
28
  title?: ReleaseNotesText;
20
29
  summary?: ReleaseNotesText;
30
+ links?: ReleaseNotesLinks;
21
31
  sections: ReleaseNotesSection[];
22
32
  };
23
33
 
34
+ const DEFAULT_RELEASE_NOTES_BASE_URL = 'https://docs.nextclaw.io/';
35
+
24
36
  export function readReleaseNotesText(
25
37
  value: ReleaseNotesText | undefined,
26
38
  locale: ReleaseNotesLocale
@@ -33,17 +45,40 @@ export function readReleaseNotesText(
33
45
  export function resolveReleaseNotesDataUrl(snapshot: UpdateSnapshot): string | null {
34
46
  const releaseNotesUrl = snapshot.releaseNotesUrl?.trim();
35
47
  const version = (snapshot.availableVersion ?? snapshot.downloadedVersion)?.trim();
36
- if (!releaseNotesUrl || !version || !/^[0-9A-Za-z.-]+$/.test(version)) {
48
+ if (!releaseNotesUrl) {
37
49
  return null;
38
50
  }
51
+ return resolveVersionReleaseNotesDataUrl(version, releaseNotesUrl);
52
+ }
39
53
 
54
+ export function resolveVersionReleaseNotesDataUrl(version: string | null | undefined, baseUrl = DEFAULT_RELEASE_NOTES_BASE_URL): string | null {
55
+ const normalizedVersion = version?.trim();
56
+ if (!normalizedVersion || !/^[0-9A-Za-z.-]+$/.test(normalizedVersion)) {
57
+ return null;
58
+ }
40
59
  try {
41
- return new URL(`/release-notes/nextclaw-v${version}.json`, releaseNotesUrl).toString();
60
+ return new URL(`/release-notes/nextclaw-v${normalizedVersion}.json`, baseUrl).toString();
42
61
  } catch {
43
62
  return null;
44
63
  }
45
64
  }
46
65
 
66
+ export function resolveReleaseNotesHtmlUrl(payload: ReleaseNotesPayload | undefined, locale: ReleaseNotesLocale): string | null {
67
+ return readReleaseNotesText(payload?.links?.html, locale);
68
+ }
69
+
70
+ export function resolveUpdateReleaseNotesLink(snapshot: UpdateSnapshot): ReleaseNotesLink | null {
71
+ const url = snapshot.releaseNotesUrl?.trim();
72
+ const version = (snapshot.downloadedVersion ?? snapshot.availableVersion)?.trim();
73
+ if (!url || !version) {
74
+ return null;
75
+ }
76
+ return {
77
+ url,
78
+ versionLabel: `v${version}`
79
+ };
80
+ }
81
+
47
82
  function isRecord(value: unknown): value is Record<string, unknown> {
48
83
  return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
49
84
  }
@@ -70,12 +105,18 @@ function isReleaseNotesSection(value: unknown): value is ReleaseNotesSection {
70
105
  && (value.title === undefined || isReleaseNotesText(value.title));
71
106
  }
72
107
 
108
+ function isReleaseNotesLinks(value: unknown): value is ReleaseNotesLinks {
109
+ return isRecord(value)
110
+ && (value.html === undefined || isReleaseNotesText(value.html));
111
+ }
112
+
73
113
  function isReleaseNotesPayload(value: unknown): value is ReleaseNotesPayload {
74
114
  return isRecord(value)
75
115
  && Array.isArray(value.sections)
76
116
  && value.sections.every(isReleaseNotesSection)
77
117
  && (value.title === undefined || isReleaseNotesText(value.title))
78
- && (value.summary === undefined || isReleaseNotesText(value.summary));
118
+ && (value.summary === undefined || isReleaseNotesText(value.summary))
119
+ && (value.links === undefined || isReleaseNotesLinks(value.links));
79
120
  }
80
121
 
81
122
  export async function fetchReleaseNotesData(url: string): Promise<ReleaseNotesPayload> {
package/src/index.css CHANGED
@@ -719,8 +719,9 @@
719
719
  --md-table-head-bg: hsl(var(--muted) / 0.84);
720
720
  }
721
721
 
722
+ /* The root flow owns external spacing; individual blocks only own their surface. */
722
723
  .chat-markdown > * + * {
723
- margin-top: 0.72rem;
724
+ margin-block-start: 0.75rem;
724
725
  }
725
726
 
726
727
  .chat-markdown p,
@@ -729,21 +730,19 @@
729
730
  color: var(--md-text);
730
731
  }
731
732
 
732
- .chat-markdown h1,
733
- .chat-markdown h2,
734
- .chat-markdown h3,
735
- .chat-markdown h4,
736
- .chat-markdown h5,
737
- .chat-markdown h6 {
738
- margin: 0.2rem 0 0;
739
- padding-bottom: 0.5em;
733
+ .chat-markdown :is(h1, h2, h3, h4, h5, h6) {
734
+ margin: 0;
740
735
  font-weight: 700;
741
736
  line-height: 1.35;
742
737
  letter-spacing: -0.01em;
743
738
  }
744
739
 
745
- .chat-markdown :is(h1, h2, h3, h4, h5, h6) + * {
746
- margin-top: 0;
740
+ .chat-markdown > :is(h1, h2, h3, h4, h5, h6):not(:first-child) {
741
+ margin-block-start: 1.25rem;
742
+ }
743
+
744
+ .chat-markdown > :is(h1, h2, h3, h4, h5, h6) + * {
745
+ margin-block-start: 0.5rem;
747
746
  }
748
747
 
749
748
  .chat-markdown h1 {
@@ -766,7 +765,6 @@
766
765
 
767
766
  .chat-markdown ul,
768
767
  .chat-markdown ol {
769
- margin: 0.35rem 0;
770
768
  padding-left: 1.5rem;
771
769
  list-style-position: outside;
772
770
  }
@@ -839,7 +837,6 @@
839
837
  .chat-markdown hr {
840
838
  border: 0;
841
839
  border-top: 1px solid var(--md-border);
842
- margin: 0.92rem 0;
843
840
  }
844
841
 
845
842
  .chat-markdown a {
@@ -874,7 +871,6 @@
874
871
  }
875
872
 
876
873
  .chat-codeblock {
877
- margin: 0.2rem 0 0.1rem;
878
874
  border: 1px solid var(--md-code-toolbar-border);
879
875
  border-radius: 0.5rem;
880
876
  overflow: hidden;
@@ -1299,7 +1295,6 @@
1299
1295
  }
1300
1296
 
1301
1297
  .chat-table-wrap {
1302
- margin: 0.15rem 0;
1303
1298
  border: 1px solid var(--md-border);
1304
1299
  border-radius: 0.72rem;
1305
1300
  overflow-x: auto;
@@ -1378,7 +1373,6 @@
1378
1373
  }
1379
1374
 
1380
1375
  .chat-markdown blockquote {
1381
- margin: 0.2rem 0;
1382
1376
  border-left: 3px solid var(--md-blockquote-border);
1383
1377
  border-radius: 0 0.55rem 0.55rem 0;
1384
1378
  padding: 0.45rem 0.72rem;
@@ -10,7 +10,9 @@ import { setLanguage } from '@/shared/lib/i18n';
10
10
 
11
11
  const mocks = vi.hoisted(() => ({
12
12
  applyDownloadedUpdate: vi.fn(),
13
- downloadUpdate: vi.fn()
13
+ downloadUpdate: vi.fn(),
14
+ fetchReleaseNotes: vi.fn(),
15
+ openExternalUrl: vi.fn()
14
16
  }));
15
17
 
16
18
  vi.mock('@/features/system-status', async () => {
@@ -25,7 +27,30 @@ vi.mock('@/features/system-status', async () => {
25
27
  };
26
28
  });
27
29
 
28
- function renderBrandHeader() {
30
+ vi.mock('@/shared/lib/host-capabilities', () => ({
31
+ hostCapabilityManager: {
32
+ openExternalUrl: mocks.openExternalUrl,
33
+ },
34
+ }));
35
+
36
+ function createReleaseNotesResponse(urls?: { zh?: string; en?: string }): Response {
37
+ return new Response(JSON.stringify({
38
+ links: urls
39
+ ? {
40
+ html: {
41
+ ...(urls.zh ? { 'zh-CN': urls.zh } : {}),
42
+ ...(urls.en ? { 'en-US': urls.en } : {})
43
+ }
44
+ }
45
+ : undefined,
46
+ sections: []
47
+ }), {
48
+ headers: { 'content-type': 'application/json' },
49
+ status: 200
50
+ });
51
+ }
52
+
53
+ function renderBrandHeader(options: { productVersion?: string } = {}) {
29
54
  const queryClient = new QueryClient({
30
55
  defaultOptions: {
31
56
  queries: {
@@ -35,7 +60,7 @@ function renderBrandHeader() {
35
60
  });
36
61
  queryClient.setQueryData(['app-meta'], {
37
62
  name: 'NextClaw',
38
- productVersion: '0.18.11'
63
+ productVersion: options.productVersion ?? '0.18.11'
39
64
  });
40
65
  return render(
41
66
  <QueryClientProvider client={queryClient}>
@@ -57,6 +82,10 @@ describe('BrandHeader', () => {
57
82
  });
58
83
  mocks.applyDownloadedUpdate.mockReset();
59
84
  mocks.downloadUpdate.mockReset();
85
+ mocks.fetchReleaseNotes.mockReset();
86
+ mocks.fetchReleaseNotes.mockResolvedValue(createReleaseNotesResponse());
87
+ mocks.openExternalUrl.mockReset();
88
+ vi.stubGlobal('fetch', mocks.fetchReleaseNotes);
60
89
  });
61
90
 
62
91
  it('shows update progress next to the product version', async () => {
@@ -100,11 +129,89 @@ describe('BrandHeader', () => {
100
129
  expect(version).toBeTruthy();
101
130
  expect(screen.getAllByText('v0.18.11')).toHaveLength(1);
102
131
  await user.hover(version);
103
- expect(await screen.findAllByText('v0.18.11')).toHaveLength(2);
132
+ expect((await screen.findByRole('tooltip')).textContent).toBe('v0.18.11');
104
133
  expect(screen.getByText('下载 50%')).toBeTruthy();
105
134
  expect(screen.queryByRole('button', { name: '更新' })).toBeNull();
106
135
  });
107
136
 
137
+ it('opens release notes from the current version tooltip when that version has docs notes', async () => {
138
+ const user = userEvent.setup();
139
+ mocks.fetchReleaseNotes.mockResolvedValueOnce(createReleaseNotesResponse({
140
+ zh: 'https://docs.nextclaw.io/zh/notes/2026-07-15-nextclaw-v0-23-0',
141
+ en: 'https://docs.nextclaw.io/en/notes/2026-07-15-nextclaw-v0-23-0'
142
+ }));
143
+
144
+ renderBrandHeader({ productVersion: '0.23.0' });
145
+
146
+ const version = await screen.findByRole('button', {
147
+ name: '当前版本 v0.23.0,查看 v0.23.0 更新说明'
148
+ });
149
+
150
+ await user.hover(version);
151
+ expect((await screen.findByRole('tooltip')).textContent).toContain('点击查看 v0.23.0 更新说明');
152
+
153
+ await user.click(version);
154
+
155
+ expect(mocks.fetchReleaseNotes).toHaveBeenCalledWith(
156
+ 'https://docs.nextclaw.io/release-notes/nextclaw-v0.23.0.json',
157
+ expect.objectContaining({ cache: 'no-store' })
158
+ );
159
+ expect(mocks.openExternalUrl).toHaveBeenCalledWith('https://docs.nextclaw.io/zh/notes/2026-07-15-nextclaw-v0-23-0');
160
+ });
161
+
162
+ it('keeps update target release notes available without replacing the current version link', async () => {
163
+ const user = userEvent.setup();
164
+ mocks.fetchReleaseNotes.mockResolvedValueOnce(createReleaseNotesResponse({
165
+ zh: 'https://docs.nextclaw.io/zh/notes/2026-07-14-nextclaw-v0-18-11'
166
+ }));
167
+ useRuntimeUpdateStore.setState({
168
+ supported: true,
169
+ initialized: true,
170
+ busyAction: null,
171
+ snapshot: {
172
+ status: 'update-available',
173
+ installationKind: 'npm-runtime-bundle',
174
+ channel: 'stable',
175
+ hostVersion: '0.18.11',
176
+ currentVersion: '0.18.11',
177
+ availableVersion: '0.18.12',
178
+ downloadedVersion: null,
179
+ minimumHostVersion: null,
180
+ releaseNotesUrl: 'https://docs.nextclaw.io/zh/notes/2026-07-15-nextclaw-v0-18-12',
181
+ lastCheckedAt: null,
182
+ progress: null,
183
+ canAutoDownload: true,
184
+ canApplyInApp: false,
185
+ requiresRestart: false,
186
+ blockReason: null,
187
+ recoveryCommand: null,
188
+ errorMessage: null,
189
+ preferences: {
190
+ automaticChecks: true,
191
+ autoDownload: true
192
+ }
193
+ }
194
+ });
195
+
196
+ renderBrandHeader();
197
+
198
+ const currentVersion = await screen.findByRole('button', {
199
+ name: '当前版本 v0.18.11,查看 v0.18.11 更新说明'
200
+ });
201
+ await user.click(currentVersion);
202
+ expect(mocks.openExternalUrl).toHaveBeenCalledWith('https://docs.nextclaw.io/zh/notes/2026-07-14-nextclaw-v0-18-11');
203
+
204
+ await user.click(screen.getByRole('button', { name: '下载' }));
205
+ expect(mocks.downloadUpdate).toHaveBeenCalledTimes(1);
206
+
207
+ const updateReleaseNotes = screen.getByRole('button', { name: '查看 v0.18.12 更新说明' });
208
+ await user.hover(updateReleaseNotes);
209
+ expect((await screen.findByRole('tooltip')).textContent).toBe('查看 v0.18.12 更新说明');
210
+
211
+ await user.click(updateReleaseNotes);
212
+ expect(mocks.openExternalUrl).toHaveBeenCalledWith('https://docs.nextclaw.io/zh/notes/2026-07-15-nextclaw-v0-18-12');
213
+ });
214
+
108
215
  it('applies the downloaded update from the version-adjacent update button', async () => {
109
216
  const user = userEvent.setup();
110
217
  useRuntimeUpdateStore.setState({
@@ -145,6 +252,7 @@ describe('BrandHeader', () => {
145
252
  });
146
253
 
147
254
  it('shows a warning icon with the blocked update reason instead of a visible failure label', async () => {
255
+ const user = userEvent.setup();
148
256
  useRuntimeUpdateStore.setState({
149
257
  supported: true,
150
258
  initialized: true,
@@ -180,13 +288,19 @@ describe('BrandHeader', () => {
180
288
  const issueIcon = screen.getByLabelText('更新被阻塞');
181
289
 
182
290
  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');
291
+ expect(issueIcon.hasAttribute('title')).toBe(false);
292
+
293
+ await user.hover(issueIcon);
294
+
295
+ const tooltip = await screen.findByRole('tooltip');
296
+ expect(tooltip.textContent).toContain('更新被阻塞');
297
+ expect(tooltip.textContent).toContain('根因:缺少更新签名公钥,无法验证更新包来源');
298
+ expect(tooltip.textContent).toContain('Runtime bundle updates require a configured update public key.');
299
+ expect(tooltip.textContent).toContain('Set NEXTCLAW_UPDATE_BUNDLE_PUBLIC_KEY');
187
300
  });
188
301
 
189
302
  it('uses the failed update wording only for failed snapshots', async () => {
303
+ const user = userEvent.setup();
190
304
  useRuntimeUpdateStore.setState({
191
305
  supported: true,
192
306
  initialized: true,
@@ -221,8 +335,11 @@ describe('BrandHeader', () => {
221
335
  const issueIcon = screen.getByLabelText('更新失败');
222
336
 
223
337
  expect(issueIcon.textContent).toBe('!');
224
- expect(issueIcon.getAttribute('title')).toContain('更新失败');
225
- expect(issueIcon.getAttribute('title')).toContain('runtime bundle sha256 mismatch');
338
+ await user.hover(issueIcon);
339
+
340
+ const tooltip = await screen.findByRole('tooltip');
341
+ expect(tooltip.textContent).toContain('更新失败');
342
+ expect(tooltip.textContent).toContain('runtime bundle sha256 mismatch');
226
343
  expect(screen.queryByText('更新被阻塞')).toBeNull();
227
344
  });
228
345
  });