@nextclaw/ui 0.15.25 → 0.15.26

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 (288) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/dist/assets/{abnfDiagram-VRR7QNED-DvTjgVMf.js → abnfDiagram-VRR7QNED-BcFJp_1X.js} +1 -1
  3. package/dist/assets/{aiden0z-pptx-renderer.es-C1nfBAXR.js → aiden0z-pptx-renderer.es-Dp2ezlQn.js} +1 -1
  4. package/dist/assets/{api-FK7tikI1.js → api-CK3Fro7z.js} +6 -6
  5. package/dist/assets/appearance-settings-page-zqwWO8Dy.js +1 -0
  6. package/dist/assets/{arc-BvhODBXv.js → arc-CkgyPMyW.js} +1 -1
  7. package/dist/assets/architecture-TIHT7OUA-DO2x53FK.js +1 -0
  8. package/dist/assets/{architectureDiagram-ZJ3FMSHR-BKO9RgNS.js → architectureDiagram-ZJ3FMSHR-CMd4cIjI.js} +1 -1
  9. package/dist/assets/{blockDiagram-677ZJIJ3-DHpDXy7D.js → blockDiagram-677ZJIJ3-Bz3tbhet.js} +1 -1
  10. package/dist/assets/{c4Diagram-LMCZKHZV-BG_iQnDr.js → c4Diagram-LMCZKHZV-D9hFNAGv.js} +1 -1
  11. package/dist/assets/channel-nBvMaVyP.js +1 -0
  12. package/dist/assets/{channels-list-page-CocdcIko.js → channels-list-page-CVCZBlCL.js} +2 -2
  13. package/dist/assets/chat-page-D4wehRTz.js +1 -0
  14. package/dist/assets/chunk-2Q5K7J3B-CJgqFDS4.js +1 -0
  15. package/dist/assets/{chunk-32BRIVSS-DR9M0LLx.js → chunk-32BRIVSS-7NrjEITq.js} +1 -1
  16. package/dist/assets/{chunk-52WLFC77-698iC3BB.js → chunk-52WLFC77-viIcqj9q.js} +1 -1
  17. package/dist/assets/{chunk-5FCAYU7R-DikTj9Ey.js → chunk-5FCAYU7R-Vsu4J-VM.js} +1 -1
  18. package/dist/assets/{chunk-5HE753X5-ko0o7_q0.js → chunk-5HE753X5-C-w6CQZK.js} +1 -1
  19. package/dist/assets/{chunk-5JV3BV7I-CEHufhgo.js → chunk-5JV3BV7I-CuKgJQ4W.js} +1 -1
  20. package/dist/assets/{chunk-5TONJI2A-CGgno1cu.js → chunk-5TONJI2A-CYeiWZKb.js} +1 -1
  21. package/dist/assets/{chunk-5VM5RSS4-DkiB4cUQ.js → chunk-5VM5RSS4-DRCbfwIn.js} +1 -1
  22. package/dist/assets/{chunk-7BUUIJ7U-DzptcytQ.js → chunk-7BUUIJ7U-BvydOcm3.js} +1 -1
  23. package/dist/assets/{chunk-BIQX33UG-DMRSQJvT.js → chunk-BIQX33UG-RlppOXKo.js} +1 -1
  24. package/dist/assets/{chunk-C7G6YPKG-Bw1BwwuY.js → chunk-C7G6YPKG-DAizzYwr.js} +1 -1
  25. package/dist/assets/{chunk-CQNSW5MT-C0z6eHBk.js → chunk-CQNSW5MT-5eChJHca.js} +1 -1
  26. package/dist/assets/{chunk-CYSBUYHQ-ByPyAouO.js → chunk-CYSBUYHQ-YHPr53fv.js} +1 -1
  27. package/dist/assets/{chunk-EMLP6XTP-CJlAXWZg.js → chunk-EMLP6XTP-CK4D7Hz5.js} +1 -1
  28. package/dist/assets/{chunk-EX3LRPZG-Dqq4zKJu.js → chunk-EX3LRPZG-BmlDFIRW.js} +1 -1
  29. package/dist/assets/{chunk-FWX5IMBZ-B0SNoCmi.js → chunk-FWX5IMBZ-D0ksHZZ9.js} +2 -2
  30. package/dist/assets/{chunk-HOUHSVGY-DG_GJih_.js → chunk-HOUHSVGY-DPkq8FCm.js} +1 -1
  31. package/dist/assets/{chunk-ICXQ74PX-Drxk_6bE.js → chunk-ICXQ74PX-FAatv5fC.js} +1 -1
  32. package/dist/assets/{chunk-JG7HCLWE-DdLFPpnd.js → chunk-JG7HCLWE-DLkIi4F7.js} +1 -1
  33. package/dist/assets/{chunk-JWPE2WC7-B8M4G76F.js → chunk-JWPE2WC7-DawOjNE3.js} +1 -1
  34. package/dist/assets/{chunk-MOJQB5TN-XP6FJHY_.js → chunk-MOJQB5TN-Di52TorW.js} +1 -1
  35. package/dist/assets/{chunk-MOZMSUNE-C_6x18Qw.js → chunk-MOZMSUNE-PfyK9w1-.js} +1 -1
  36. package/dist/assets/{chunk-OGEWGWER-N25BzCmG.js → chunk-OGEWGWER-C3jdH3Ty.js} +1 -1
  37. package/dist/assets/{chunk-OSBZ3O6U-ASDEm7y0.js → chunk-OSBZ3O6U-CecHo-f0.js} +1 -1
  38. package/dist/assets/{chunk-PUDLZKDR-CTlvAcn7.js → chunk-PUDLZKDR-f3IJh3sN.js} +1 -1
  39. package/dist/assets/{chunk-Q4XR5HBZ-Da9BWfB6.js → chunk-Q4XR5HBZ-DQFeUPxZ.js} +1 -1
  40. package/dist/assets/{chunk-QBLGF6JB-Bi6RpTJO.js → chunk-QBLGF6JB-BhUeG0y2.js} +1 -1
  41. package/dist/assets/{chunk-R7FJI6CG-DkMsSPvK.js → chunk-R7FJI6CG-C3XpZ-2u.js} +1 -1
  42. package/dist/assets/{chunk-RYQCIY6F-DZp0eU-j.js → chunk-RYQCIY6F-CAqa5klQ.js} +1 -1
  43. package/dist/assets/{chunk-U6XO7XAA-vfinDSrj.js → chunk-U6XO7XAA-D_WXezeD.js} +1 -1
  44. package/dist/assets/{chunk-V7JOEXUC-C9Nksccs.js → chunk-V7JOEXUC-CZ70oOtp.js} +1 -1
  45. package/dist/assets/chunk-VAUOI2AC-BGvh3yIx.js +1 -0
  46. package/dist/assets/{chunk-VR4S4FIN-Xv6zfwXs.js → chunk-VR4S4FIN-B7AHEYYk.js} +1 -1
  47. package/dist/assets/{chunk-WYO6CB5R-mOJNzbWl.js → chunk-WYO6CB5R-Dn9To_OI.js} +2 -2
  48. package/dist/assets/chunk-XXDRQBXY-DaBxG5gs.js +1 -0
  49. package/dist/assets/{chunk-YOTPTUD7-1-aKSu4o.js → chunk-YOTPTUD7-BYEgRBk3.js} +1 -1
  50. package/dist/assets/{chunk-ZGVPDNZ5-OJ8veMpK.js → chunk-ZGVPDNZ5-C6YtZDU0.js} +1 -1
  51. package/dist/assets/{chunk-ZIRB5QZD-SR_Nq_PP.js → chunk-ZIRB5QZD-DMuYtnHF.js} +1 -1
  52. package/dist/assets/classDiagram-OUVF2IWQ-P6N03G2R.js +1 -0
  53. package/dist/assets/classDiagram-v2-EOCWNBFH-B1_uLvMv.js +1 -0
  54. package/dist/assets/config-split-page-CceNFvlY.js +1 -0
  55. package/dist/assets/confirm-dialog-CWeNYWZY.js +5 -0
  56. package/dist/assets/{cose-bilkent-JH36ORCC-NjVgz2Bw.js → cose-bilkent-JH36ORCC-Bc2Ex5Y3.js} +1 -1
  57. package/dist/assets/cynefin-VYW2F7L2-B-H8Qp-n.js +1 -0
  58. package/dist/assets/{cynefinDiagram-TSTJHNR4-B_17LDqD.js → cynefinDiagram-TSTJHNR4-DVysQoGz.js} +1 -1
  59. package/dist/assets/{dagre-CMe101gk.js → dagre-CMhQ1cAG.js} +1 -1
  60. package/dist/assets/{dagre-VKFMJZFB-qo8nLb3B.js → dagre-VKFMJZFB-RYyWpPJZ.js} +1 -1
  61. package/dist/assets/desktop-update-config-Dirc3bpt.js +1 -0
  62. package/dist/assets/{diagram-FQU43EPY-Ba3XIklC.js → diagram-FQU43EPY-BD6KONdF.js} +1 -1
  63. package/dist/assets/{diagram-G47NLZAW-CG45TcUx.js → diagram-G47NLZAW-CGB2ptRR.js} +1 -1
  64. package/dist/assets/{diagram-NH7WQ7WH-2F2HrwV-.js → diagram-NH7WQ7WH-DnkOI6cU.js} +1 -1
  65. package/dist/assets/{diagram-OA4YK3LP-CXfWH9NZ.js → diagram-OA4YK3LP-DF82RZ7P.js} +1 -1
  66. package/dist/assets/{diagram-WEI45ONY-CH_4yepc.js → diagram-WEI45ONY-Be2v7Ck9.js} +1 -1
  67. package/dist/assets/{dist-BH7W5yCi.js → dist-AYxkFx2B.js} +1 -1
  68. package/dist/assets/{dist-CaYdVVeO.js → dist-D9xtGgdL.js} +1 -1
  69. package/dist/assets/doc-browser-4Sud6dWw.js +1 -0
  70. package/dist/assets/doc-browser-D4x_yH5F.js +1 -0
  71. package/dist/assets/doc-browser-context-Cb7Z0j90.js +1 -0
  72. package/dist/assets/{docx-preview-CoM4JK7_.js → docx-preview-BqwAjrj3.js} +1 -1
  73. package/dist/assets/{ebnfDiagram-CCIWWBDH-BwiG4Ea1.js → ebnfDiagram-CCIWWBDH-CP98OYk8.js} +1 -1
  74. package/dist/assets/{erDiagram-Q63AITRT-BML3hpnQ.js → erDiagram-Q63AITRT-ZLlx8xfs.js} +1 -1
  75. package/dist/assets/eventmodeling-45OFAUF4-YVtShyFA.js +1 -0
  76. package/dist/assets/flowDiagram-23GEKE2U-BeKXabVO.js +1 -0
  77. package/dist/assets/form-actions-B2q4jgjE.js +1 -0
  78. package/dist/assets/{ganttDiagram-NO4QXBWP-80enfWbD.js → ganttDiagram-NO4QXBWP-DXCDjHR2.js} +1 -1
  79. package/dist/assets/gitGraph-TEB2WS4Q-jTSiBj-p.js +1 -0
  80. package/dist/assets/{gitGraphDiagram-IHSO6WYX-CVLqXr-b.js → gitGraphDiagram-IHSO6WYX-l2HWrTJp.js} +1 -1
  81. package/dist/assets/index-CdyTsSBu.css +1 -0
  82. package/dist/assets/index-Das_bw2h.js +107 -0
  83. package/dist/assets/info-DKCQHKI2-BIporNWb.js +1 -0
  84. package/dist/assets/{infoDiagram-FWYZ7A6U-Dyvs2t0i.js → infoDiagram-FWYZ7A6U-D-DjX-bn.js} +1 -1
  85. package/dist/assets/{ishikawaDiagram-FXEZZL3T-sSnxDKfh.js → ishikawaDiagram-FXEZZL3T-y_Xy128F.js} +1 -1
  86. package/dist/assets/{journeyDiagram-5HDEW3XC-D22V71Mf.js → journeyDiagram-5HDEW3XC-DJn5xYcQ.js} +1 -1
  87. package/dist/assets/{kanban-definition-HUTT4EX6-D3ds0p-g.js → kanban-definition-HUTT4EX6-DJVIOR21.js} +1 -1
  88. package/dist/assets/{line-CbyFs065.js → line-Cos7PIOj.js} +1 -1
  89. package/dist/assets/{linear-4DKtnZBS.js → linear-BtDLqtzF.js} +1 -1
  90. package/dist/assets/loader-circle-CpmBdaon.js +1 -0
  91. package/dist/assets/{map-CcZzEoz6.js → map-BKnQyvd0.js} +1 -1
  92. package/dist/assets/mcp-marketplace-page-CdOY3Sop.js +1 -0
  93. package/dist/assets/mcp-marketplace-page-DvjrfqEt.js +9 -0
  94. package/dist/assets/{mermaid-parser.core-DPmMOjXD.js → mermaid-parser.core-XUNZhr2W.js} +2 -2
  95. package/dist/assets/{mermaid.core-BSf-ddRa.js → mermaid.core-ySrvolIe.js} +3 -3
  96. package/dist/assets/{mindmap-definition-LN4V7U3C-BUTwWPyx.js → mindmap-definition-LN4V7U3C-BypL9tei.js} +1 -1
  97. package/dist/assets/model-config-page-DfOLyQqR.js +1 -0
  98. package/dist/assets/{navigation-link-CVy0_kVl.js → navigation-link-MiiyoIjH.js} +2 -2
  99. package/dist/assets/{ordinal-BgLYtOZE.js → ordinal-CxxVWxYU.js} +1 -1
  100. package/dist/assets/packet-7NZHBO7P-CuFMADV0.js +1 -0
  101. package/dist/assets/{pegDiagram-2B236MQR-BEu_ZBjc.js → pegDiagram-2B236MQR-D-U2KBGv.js} +1 -1
  102. package/dist/assets/pie-RZYD4A2V-XUeIXhM9.js +1 -0
  103. package/dist/assets/{pieDiagram-ENE6RG2P-CAwAwuBd.js → pieDiagram-ENE6RG2P-CBi3GusJ.js} +1 -1
  104. package/dist/assets/plus-C8FkjpSZ.js +1 -0
  105. package/dist/assets/provider-scoped-model-input-DXzjr5l0.js +1 -0
  106. package/dist/assets/providers-config-page-CaO8WfZH.js +1 -0
  107. package/dist/assets/{quadrantDiagram-ABIIQ3AL-CwE6F-_2.js → quadrantDiagram-ABIIQ3AL-H0ZuvnZL.js} +1 -1
  108. package/dist/assets/radar-I7S5WNFK-D-NNGh1k.js +1 -0
  109. package/dist/assets/railroad-3IZDKUUU-C67_uDaM.js +1 -0
  110. package/dist/assets/railroad-abnf-AHOZXSZD-BZ8Yflnd.js +1 -0
  111. package/dist/assets/railroad-ebnf-EBAXGLYW-B7zO7RoK.js +1 -0
  112. package/dist/assets/railroad-peg-LSFZ7HO6-R63PTgLz.js +1 -0
  113. package/dist/assets/{railroadDiagram-RFXS5EU6-xgLekU3B.js → railroadDiagram-RFXS5EU6-D7Y2EqSd.js} +1 -1
  114. package/dist/assets/react-C8XFFvuz.js +8 -0
  115. package/dist/assets/{refresh-cw-j2bl79Ea.js → refresh-cw-a2VVeeAF.js} +1 -1
  116. package/dist/assets/remote-BJHXa4mB.js +1 -0
  117. package/dist/assets/{requirementDiagram-TGXJPOKE-CyVUzEX5.js → requirementDiagram-TGXJPOKE-BiXNX1NN.js} +1 -1
  118. package/dist/assets/rotate-cw-DJ7UF3F2.js +1 -0
  119. package/dist/assets/runtime-config-page-BuA4A02z.js +1 -0
  120. package/dist/assets/{sankeyDiagram-HTMAVEWB-JK6bFmYB.js → sankeyDiagram-HTMAVEWB-qzDeLztW.js} +1 -1
  121. package/dist/assets/{save-BKNrtK81.js → save-CGm23Yww.js} +1 -1
  122. package/dist/assets/{search-CkOm65W_.js → search-CH2t--sH.js} +1 -1
  123. package/dist/assets/search-config-page-BAmYqoAz.js +1 -0
  124. package/dist/assets/{secrets-config-page-CJjtdFAl.js → secrets-config-page-BlPOuql_.js} +2 -2
  125. package/dist/assets/security-config-CbDiY2xr.js +1 -0
  126. package/dist/assets/{select-Ea7ffKCE.js → select-DnxxDpcV.js} +2 -2
  127. package/dist/assets/{sequenceDiagram-DBY2YBRQ-BR5IokXV.js → sequenceDiagram-DBY2YBRQ-BtRbPQtL.js} +1 -1
  128. package/dist/assets/{settings-2-CtyN3m_V.js → settings-2-agnMYc8a.js} +1 -1
  129. package/dist/assets/{sizeCapture-X5ZJPWSS-SxG_YnMA.js → sizeCapture-X5ZJPWSS-CMYvM3SL.js} +1 -1
  130. package/dist/assets/skeleton-XpqeWL62.js +1 -0
  131. package/dist/assets/{src-vgz8E_-O.js → src-Bw-mzbQb.js} +1 -1
  132. package/dist/assets/{stateDiagram-2N3HPSRC-DD4OoYhn.js → stateDiagram-2N3HPSRC-BJ-mFeOz.js} +1 -1
  133. package/dist/assets/stateDiagram-v2-6OUMAXLB-BE-HqxOK.js +1 -0
  134. package/dist/assets/{swimlanes-5IMT3BWC-BWkFeyCT.js → swimlanes-5IMT3BWC-BsucVOO2.js} +2 -2
  135. package/dist/assets/swimlanesDiagram-G3AALYLV-Dr1H9LJH.js +8 -0
  136. package/dist/assets/{tag-chip-C9l4xsKp.js → tag-chip-CPOc8b2N.js} +1 -1
  137. package/dist/assets/{timeline-definition-FHXFAJF6-VTRPy6KA.js → timeline-definition-FHXFAJF6-Bicewlzo.js} +1 -1
  138. package/dist/assets/treeView-QDETBFTQ-npJ5dbqS.js +1 -0
  139. package/dist/assets/treemap-6X3UGDF4-pYrBx6oa.js +1 -0
  140. package/dist/assets/{vennDiagram-L72KCM5P-Bvk42Pek.js → vennDiagram-L72KCM5P-BxOOnO9x.js} +1 -1
  141. package/dist/assets/wardley-OPB4EBWU-CmSRKLR8.js +1 -0
  142. package/dist/assets/{wardleyDiagram-EHGQE667-DOFjx2s3.js → wardleyDiagram-EHGQE667-Cl7NltMv.js} +1 -1
  143. package/dist/assets/x-odTHAtLZ.js +1 -0
  144. package/dist/assets/{xychartDiagram-FW5EYKEG-DY5yRTUc.js → xychartDiagram-FW5EYKEG-DiMIxQ7N.js} +1 -1
  145. package/dist/index.html +21 -22
  146. package/package.json +6 -6
  147. package/src/features/apps/components/__tests__/app-packages-panel.test.tsx +234 -0
  148. package/src/features/apps/components/app-marketplace-dialog.tsx +262 -0
  149. package/src/features/apps/components/app-package-card.tsx +268 -0
  150. package/src/features/apps/components/app-packages-panel.tsx +301 -0
  151. package/src/features/apps/components/apps-panel.tsx +11 -4
  152. package/src/features/apps/hooks/use-app-marketplace.ts +95 -0
  153. package/src/features/apps/hooks/use-app-packages.ts +59 -0
  154. package/src/features/chat/components/chat-sidebar-list-mode-switch.tsx +27 -4
  155. package/src/features/chat/components/layout/__tests__/chat-sidebar-session-area.test.tsx +36 -5
  156. package/src/features/chat/components/layout/chat-sidebar-desktop-layout.tsx +12 -13
  157. package/src/features/chat/features/conversation/components/__tests__/session-conversation-area.test.tsx +49 -1
  158. package/src/features/chat/features/conversation/components/__tests__/session-conversation-input.streaming.test.tsx +4 -4
  159. package/src/features/chat/features/conversation/components/session-conversation-area.tsx +25 -4
  160. package/src/features/chat/features/conversation/components/session-conversation-input.tsx +3 -0
  161. package/src/features/chat/features/conversation/hooks/use-system-object-reference-select.ts +27 -0
  162. package/src/features/chat/features/conversation/utils/session-message-composer.utils.ts +5 -0
  163. package/src/features/chat/features/input/hooks/__tests__/use-chat-input-surface-state.test.tsx +102 -0
  164. package/src/features/chat/features/input/hooks/use-chat-input-surface-state.ts +71 -120
  165. package/src/features/chat/features/input/input-surface-plugins/__tests__/context-reference-plugin.test.ts +265 -18
  166. package/src/features/chat/features/input/input-surface-plugins/chat-input-product-plugin-adapters.types.ts +7 -2
  167. package/src/features/chat/features/input/input-surface-plugins/chat-input-product-plugin-adapters.utils.ts +124 -0
  168. package/src/features/chat/features/input/input-surface-plugins/context-reference-items.utils.ts +99 -15
  169. package/src/features/chat/features/input/input-surface-plugins/context-reference-plugin.utils.ts +143 -139
  170. package/src/features/chat/features/input/input-surface-plugins/panel-app-input-surface-items.utils.ts +116 -0
  171. package/src/features/chat/features/input/input-surface-plugins/slash-command-plugin.utils.ts +1 -1
  172. package/src/features/chat/features/input/utils/__tests__/chat-inline-token.utils.test.ts +53 -1
  173. package/src/features/chat/features/input/utils/chat-composer-token-protocol.utils.ts +5 -0
  174. package/src/features/chat/features/input/utils/chat-inline-token-composer.utils.ts +134 -0
  175. package/src/features/chat/features/input/utils/chat-inline-token.utils.ts +30 -101
  176. package/src/features/chat/features/input/utils/chat-system-object-reference.utils.test.ts +44 -0
  177. package/src/features/chat/features/input/utils/chat-system-object-reference.utils.ts +46 -0
  178. package/src/features/chat/features/workspace/components/__tests__/chat-session-workspace-panel-content.test.tsx +82 -2
  179. package/src/features/chat/features/workspace/components/__tests__/chat-session-workspace-text-selection.test.tsx +12 -8
  180. package/src/features/chat/features/workspace/components/chat-session-workspace-directory-browser.tsx +24 -12
  181. package/src/features/chat/features/workspace/components/chat-session-workspace-panel-content.tsx +9 -2
  182. package/src/features/chat/features/workspace/components/project-files/workspace-directory-tree.tsx +52 -4
  183. package/src/features/chat/features/workspace/hooks/use-workspace-explorer-layout.ts +8 -11
  184. package/src/features/chat/features/workspace/hooks/use-workspace-file-actions.ts +68 -46
  185. package/src/features/chat/index.ts +1 -1
  186. package/src/features/chat/managers/chat-draft-intent.manager.ts +18 -2
  187. package/src/features/chat/stores/__tests__/chat-thread.store.test.ts +6 -0
  188. package/src/features/chat/stores/chat-thread.store.ts +6 -0
  189. package/src/features/inbox/components/inbox-reader-dialog.test.tsx +38 -5
  190. package/src/features/inbox/components/inbox-reader-dialog.tsx +5 -3
  191. package/src/features/inbox/managers/inbox.manager.test.ts +28 -3
  192. package/src/features/inbox/managers/inbox.manager.ts +14 -4
  193. package/src/features/inbox/pages/inbox-page.test.ts +121 -2
  194. package/src/features/inbox/pages/inbox-page.tsx +5 -3
  195. package/src/features/marketplace/components/__tests__/marketplace-page.test.tsx +85 -1
  196. package/src/features/marketplace/hooks/__tests__/use-marketplace.test.tsx +44 -1
  197. package/src/features/marketplace/hooks/use-marketplace-item-actions.ts +23 -2
  198. package/src/features/marketplace/hooks/use-marketplace.ts +10 -0
  199. package/src/features/marketplace/index.ts +4 -0
  200. package/src/features/panel-apps/index.ts +5 -1
  201. package/src/features/panel-apps/utils/__tests__/panel-app-doc-browser.utils.test.ts +13 -6
  202. package/src/features/panel-apps/utils/panel-app-doc-browser.utils.tsx +6 -3
  203. package/src/features/right-panel-resources/index.ts +1 -0
  204. package/src/features/right-panel-resources/utils/right-panel-resource-uri.utils.ts +7 -4
  205. package/src/features/system-status/components/__tests__/desktop-update-config.test.tsx +20 -1
  206. package/src/features/system-status/components/desktop-update-config.tsx +14 -13
  207. package/src/features/system-status/managers/__tests__/runtime-update.manager.test.ts +99 -0
  208. package/src/features/system-status/managers/runtime-update.manager.ts +22 -0
  209. package/src/features/system-status/stores/runtime-update.store.ts +1 -0
  210. package/src/shared/components/ui/actions/__tests__/icon-action-button.test.tsx +15 -0
  211. package/src/shared/components/ui/actions/icon-action-button.tsx +4 -2
  212. package/src/shared/hooks/use-system-object-references.ts +22 -0
  213. package/src/shared/lib/api/types.ts +10 -0
  214. package/src/shared/lib/i18n/locales/en-US/chat.json +16 -9
  215. package/src/shared/lib/i18n/locales/en-US/desktop-update.json +7 -4
  216. package/src/shared/lib/i18n/locales/en-US/doc-browser.json +40 -0
  217. package/src/shared/lib/i18n/locales/en-US/marketplace.json +3 -0
  218. package/src/shared/lib/i18n/locales/zh-CN/chat.json +18 -11
  219. package/src/shared/lib/i18n/locales/zh-CN/desktop-update.json +7 -4
  220. package/src/shared/lib/i18n/locales/zh-CN/doc-browser.json +40 -0
  221. package/src/shared/lib/i18n/locales/zh-CN/marketplace.json +3 -0
  222. package/dist/assets/appearance-settings-page-BYbvl2wj.js +0 -1
  223. package/dist/assets/architecture-TIHT7OUA-Bjp9jf0h.js +0 -1
  224. package/dist/assets/channel-5T3pdR-s.js +0 -1
  225. package/dist/assets/chat-page-C5E-LLVd.js +0 -1
  226. package/dist/assets/chunk-2Q5K7J3B-DTZ3tGfE.js +0 -1
  227. package/dist/assets/chunk-VAUOI2AC-HLGIHQ6k.js +0 -1
  228. package/dist/assets/chunk-XXDRQBXY-Dl0m6Ni-.js +0 -1
  229. package/dist/assets/classDiagram-OUVF2IWQ-DcDnzGNX.js +0 -1
  230. package/dist/assets/classDiagram-v2-EOCWNBFH-BKXKMzKM.js +0 -1
  231. package/dist/assets/config-split-page-CA_a0AJ5.js +0 -1
  232. package/dist/assets/confirm-dialog-CxZfenjK.js +0 -5
  233. package/dist/assets/cynefin-VYW2F7L2-BWg7U2UD.js +0 -1
  234. package/dist/assets/desktop-update-config-BEKC8Ly9.js +0 -1
  235. package/dist/assets/doc-browser-Cl2ldE3f.js +0 -1
  236. package/dist/assets/doc-browser-DXCSd63E.js +0 -1
  237. package/dist/assets/doc-browser-context-FDPs3gJf.js +0 -1
  238. package/dist/assets/eventmodeling-45OFAUF4-SB-J4tpq.js +0 -1
  239. package/dist/assets/flowDiagram-23GEKE2U-BQ_u-eLM.js +0 -1
  240. package/dist/assets/form-actions-0c0aw_bl.js +0 -1
  241. package/dist/assets/gitGraph-TEB2WS4Q-DPxNwsFd.js +0 -1
  242. package/dist/assets/i18n-provider-DkITJNz8.js +0 -1
  243. package/dist/assets/index-CQ2LcI2p.css +0 -1
  244. package/dist/assets/index-CrdfvBVg.js +0 -107
  245. package/dist/assets/info-DKCQHKI2-DNlNGH90.js +0 -1
  246. package/dist/assets/loader-circle-DHtUxCIA.js +0 -1
  247. package/dist/assets/mcp-marketplace-page-Bc_jI5iH.js +0 -9
  248. package/dist/assets/mcp-marketplace-page-CzCieD9r.js +0 -1
  249. package/dist/assets/model-config-page-C4jw-pcj.js +0 -1
  250. package/dist/assets/packet-7NZHBO7P-BpeiqkBZ.js +0 -1
  251. package/dist/assets/pie-RZYD4A2V-Cy1yxsPd.js +0 -1
  252. package/dist/assets/plus-CG1RwZQq.js +0 -1
  253. package/dist/assets/provider-scoped-model-input-CmH4BFvc.js +0 -1
  254. package/dist/assets/providers-config-page-CJQjwzaw.js +0 -1
  255. package/dist/assets/radar-I7S5WNFK-CEcPKCj8.js +0 -1
  256. package/dist/assets/railroad-3IZDKUUU-CW38eNDs.js +0 -1
  257. package/dist/assets/railroad-abnf-AHOZXSZD-ByrylcXf.js +0 -1
  258. package/dist/assets/railroad-ebnf-EBAXGLYW-CNB6GwP-.js +0 -1
  259. package/dist/assets/railroad-peg-LSFZ7HO6-x6hnqlBG.js +0 -1
  260. package/dist/assets/react-DjFBVA0n.js +0 -8
  261. package/dist/assets/remote-BIlpD3yL.js +0 -1
  262. package/dist/assets/rotate-cw-bKG8zH8M.js +0 -1
  263. package/dist/assets/runtime-config-page-B_x2Glz5.js +0 -1
  264. package/dist/assets/search-config-page-BaIijtmO.js +0 -1
  265. package/dist/assets/security-config-DB8apj09.js +0 -1
  266. package/dist/assets/skeleton-Bt6zxcAB.js +0 -1
  267. package/dist/assets/stateDiagram-v2-6OUMAXLB-ZC6dFORv.js +0 -1
  268. package/dist/assets/swimlanesDiagram-G3AALYLV-CRkh51Im.js +0 -8
  269. package/dist/assets/treeView-QDETBFTQ-Bw0DSCH7.js +0 -1
  270. package/dist/assets/treemap-6X3UGDF4-DFWBFcfh.js +0 -1
  271. package/dist/assets/wardley-OPB4EBWU-CoWturk0.js +0 -1
  272. package/dist/assets/x-CowtCu9Z.js +0 -1
  273. /package/dist/assets/{array-NADKd9aU.js → array-BQVzeGRt.js} +0 -0
  274. /package/dist/assets/{chunk-KEIR6QF5-BuyuzJJL.js → chunk-KEIR6QF5-f9psyCXK.js} +0 -0
  275. /package/dist/assets/{chunk-Y2CYZVJY-D_B2ZRNt.js → chunk-Y2CYZVJY-D1mAU_Po.js} +0 -0
  276. /package/dist/assets/{config-hints-BcfVUMij.js → config-hints-nV5HPOvq.js} +0 -0
  277. /package/dist/assets/{cytoscape.esm-BANTlgza.js → cytoscape.esm-0uDtdD0l.js} +0 -0
  278. /package/dist/assets/{defaultLocale-C4_L0aEs.js → defaultLocale-DSWXGgt0.js} +0 -0
  279. /package/dist/assets/{dist-DAPEIJbz.js → dist--ta5rX12.js} +0 -0
  280. /package/dist/assets/{graphlib-ChqXg0dI.js → graphlib-CQ6QV4yP.js} +0 -0
  281. /package/dist/assets/{host-capabilities-D9C4CBK5.js → host-capabilities-Tk038i04.js} +0 -0
  282. /package/dist/assets/{init-D8wZg9xq.js → init-Y6h9jGtZ.js} +0 -0
  283. /package/dist/assets/{jszip.min-CkbBfwAQ.js → jszip.min-8hyJpgpx.js} +0 -0
  284. /package/dist/assets/{katex-TZJFa0J_.js → katex-ARhY9PS1.js} +0 -0
  285. /package/dist/assets/{middleware-B3k-TSmZ.js → middleware-DXsbf9xI.js} +0 -0
  286. /package/dist/assets/{path-DodfxcX7.js → path-10BjdyLh.js} +0 -0
  287. /package/dist/assets/{rough.esm-CUaa1Rbe.js → rough.esm-DxgC_14r.js} +0 -0
  288. /package/dist/assets/{xlsx-CxByi7bR.js → xlsx-DrDuDNAa.js} +0 -0
@@ -6,7 +6,7 @@ import { useInboxStore } from "@/features/inbox/stores/inbox.store";
6
6
 
7
7
  const mocks = vi.hoisted(() => ({
8
8
  delete: vi.fn(),
9
- continueInChat: vi.fn(),
9
+ resolveReference: vi.fn(),
10
10
  list: vi.fn(),
11
11
  on: vi.fn(() => vi.fn()),
12
12
  updateState: vi.fn(),
@@ -17,10 +17,12 @@ vi.mock("@/shared/lib/api", () => ({
17
17
  eventBus: { on: mocks.on },
18
18
  inboxDeliveries: {
19
19
  delete: mocks.delete,
20
- continueInChat: mocks.continueInChat,
21
20
  list: mocks.list,
22
21
  updateState: mocks.updateState,
23
22
  },
23
+ systemObjectReferences: {
24
+ resolve: mocks.resolveReference,
25
+ },
24
26
  },
25
27
  }));
26
28
 
@@ -36,7 +38,6 @@ const delivery: InboxDelivery = {
36
38
  presentedAt: null,
37
39
  readAt: null,
38
40
  archivedAt: null,
39
- conversationSessionId: null,
40
41
  };
41
42
 
42
43
  describe("InboxManager", () => {
@@ -57,6 +58,19 @@ describe("InboxManager", () => {
57
58
  presentedAt: action === "present" || action === "read" ? "2026-08-06T01:00:00.000Z" : null,
58
59
  readAt: action === "read" ? "2026-08-06T01:00:00.000Z" : null,
59
60
  }));
61
+ mocks.resolveReference.mockResolvedValue({
62
+ uri: "nextclaw://objects/inbox-delivery/delivery-1",
63
+ objectType: "inbox-delivery",
64
+ objectId: "delivery-1",
65
+ label: "Brief",
66
+ description: null,
67
+ updatedAt: delivery.updatedAt,
68
+ version: "version-1",
69
+ assetUri: "asset://store/report",
70
+ fileName: "brief.md",
71
+ mimeType: "text/markdown",
72
+ sizeBytes: 7,
73
+ });
60
74
  });
61
75
 
62
76
  it("auto-opens the oldest unpresented item and only marks it presented", async () => {
@@ -83,4 +97,15 @@ describe("InboxManager", () => {
83
97
  expect(mocks.updateState).toHaveBeenCalledTimes(1);
84
98
  expect(mocks.updateState).toHaveBeenCalledWith(delivery.id, "present");
85
99
  });
100
+
101
+ it("prepares a generic immutable report reference before marking it read", async () => {
102
+ const manager = new InboxManager(new QueryClient());
103
+ const result = await manager.prepareChatReference(delivery.id);
104
+
105
+ expect(mocks.resolveReference).toHaveBeenCalledWith(
106
+ "nextclaw://objects/inbox-delivery/delivery-1",
107
+ );
108
+ expect(mocks.updateState).toHaveBeenCalledWith(delivery.id, "read");
109
+ expect(result.reference.assetUri).toBe("asset://store/report");
110
+ });
86
111
  });
@@ -5,6 +5,11 @@ import type {
5
5
  InboxDeliveryStateAction,
6
6
  } from "@nextclaw/shared";
7
7
  import { eventKeys } from "@nextclaw/shared";
8
+ import {
9
+ createSystemObjectReferenceUri,
10
+ SYSTEM_OBJECT_TYPE_INBOX_DELIVERY,
11
+ type SystemObjectResolvedReference,
12
+ } from "@nextclaw/shared";
8
13
  import { appQueryClient } from "@/app-query-client";
9
14
  import { useInboxStore } from "@/features/inbox/stores/inbox.store";
10
15
  import { nextclawClient } from "@/shared/lib/api";
@@ -104,11 +109,16 @@ export class InboxManager {
104
109
  return deleted;
105
110
  };
106
111
 
107
- continueInChat = async (deliveryId: string) => {
108
- const result = await nextclawClient.inboxDeliveries.continueInChat(deliveryId);
109
- this.replaceCachedDelivery(result.delivery);
112
+ prepareChatReference = async (deliveryId: string): Promise<{
113
+ delivery: InboxDelivery;
114
+ reference: SystemObjectResolvedReference;
115
+ }> => {
116
+ const reference = await nextclawClient.systemObjectReferences.resolve(
117
+ createSystemObjectReferenceUri(SYSTEM_OBJECT_TYPE_INBOX_DELIVERY, deliveryId),
118
+ );
119
+ const delivery = await this.updateState(deliveryId, "read");
110
120
  this.closeReader();
111
- return result;
121
+ return { delivery, reference };
112
122
  };
113
123
 
114
124
  private updateState = async (
@@ -1,5 +1,76 @@
1
- import { describe, expect, it } from "vitest";
2
- import { resolveInboxFilter } from "@/features/inbox/pages/inbox-page";
1
+ import { fireEvent, render, screen, waitFor } from "@testing-library/react";
2
+ import { createElement } from "react";
3
+ import { MemoryRouter, Route, Routes, useLocation } from "react-router-dom";
4
+ import { beforeEach, describe, expect, it, vi } from "vitest";
5
+ import { CHAT_DRAFT_SESSION_PATH } from "@/features/chat";
6
+ import { InboxPage, resolveInboxFilter } from "@/features/inbox/pages/inbox-page";
7
+ import { t } from "@/shared/lib/i18n";
8
+
9
+ const mocks = vi.hoisted(() => ({
10
+ prepareChatReference: vi.fn(),
11
+ requestSystemObjectReference: vi.fn(),
12
+ }));
13
+
14
+ vi.mock("@/app/components/app-presenter-provider", () => ({
15
+ useAppPresenter: () => ({
16
+ chatDraftIntentManager: {
17
+ requestSystemObjectReference: mocks.requestSystemObjectReference,
18
+ },
19
+ inboxManager: {
20
+ archive: vi.fn(),
21
+ delete: vi.fn(),
22
+ markRead: vi.fn(),
23
+ markUnread: vi.fn(),
24
+ prepareChatReference: mocks.prepareChatReference,
25
+ restore: vi.fn(),
26
+ },
27
+ }),
28
+ }));
29
+
30
+ vi.mock("@/app/hooks/use-viewport-layout", () => ({
31
+ useViewportLayout: () => ({ isMobile: false }),
32
+ }));
33
+
34
+ vi.mock("@/features/inbox/hooks/use-inbox-deliveries", () => ({
35
+ useInboxDeliveries: () => ({
36
+ data: {
37
+ deliveries: [{
38
+ id: "delivery-1",
39
+ title: "A considered report",
40
+ summary: "A concise summary",
41
+ content: "# Finding",
42
+ contentType: "markdown",
43
+ source: { kind: "agent", agentId: null, sessionId: null, toolCallId: null, filePath: null },
44
+ createdAt: "2026-08-06T00:00:00.000Z",
45
+ updatedAt: "2026-08-06T00:00:00.000Z",
46
+ presentedAt: "2026-08-06T00:01:00.000Z",
47
+ readAt: "2026-08-06T00:02:00.000Z",
48
+ archivedAt: null,
49
+ }],
50
+ },
51
+ isError: false,
52
+ }),
53
+ }));
54
+
55
+ vi.mock("@/features/inbox/components/inbox-delivery-content", () => ({
56
+ InboxDeliveryContent: ({ content }: { content: string }) =>
57
+ createElement("article", null, content),
58
+ }));
59
+
60
+ vi.mock("@/shared/hooks/use-confirm-dialog", () => ({
61
+ useConfirmDialog: () => ({
62
+ confirm: vi.fn(async () => true),
63
+ ConfirmDialog: () => null,
64
+ }),
65
+ }));
66
+
67
+ function CurrentPath() {
68
+ return createElement(
69
+ "span",
70
+ { "data-testid": "current-path" },
71
+ useLocation().pathname,
72
+ );
73
+ }
3
74
 
4
75
  const readDelivery = {
5
76
  archivedAt: null,
@@ -26,3 +97,51 @@ describe("resolveInboxFilter", () => {
26
97
  expect(resolveInboxFilter([unreadDelivery], "archived")).toBe("archived");
27
98
  });
28
99
  });
100
+
101
+ describe("InboxPage", () => {
102
+ beforeEach(() => {
103
+ vi.clearAllMocks();
104
+ mocks.prepareChatReference.mockResolvedValue({
105
+ reference: {
106
+ uri: "nextclaw://objects/inbox-delivery/delivery-1",
107
+ label: "A considered report",
108
+ },
109
+ });
110
+ });
111
+
112
+ it("opens a draft with the same visible system object intent as the reader dialog", async () => {
113
+ render(
114
+ createElement(
115
+ MemoryRouter,
116
+ { initialEntries: ["/inbox/delivery-1"] },
117
+ createElement(
118
+ Routes,
119
+ null,
120
+ createElement(Route, {
121
+ path: "/inbox/:deliveryId",
122
+ element: createElement(
123
+ "div",
124
+ null,
125
+ createElement(InboxPage),
126
+ createElement(CurrentPath),
127
+ ),
128
+ }),
129
+ createElement(Route, {
130
+ path: "*",
131
+ element: createElement(CurrentPath),
132
+ }),
133
+ ),
134
+ ),
135
+ );
136
+
137
+ fireEvent.click(screen.getByRole("button", { name: t("inboxContinueChat") }));
138
+
139
+ await waitFor(() => {
140
+ expect(screen.getByTestId("current-path").textContent).toBe(CHAT_DRAFT_SESSION_PATH);
141
+ });
142
+ expect(mocks.prepareChatReference).toHaveBeenCalledWith("delivery-1");
143
+ expect(mocks.requestSystemObjectReference).toHaveBeenCalledWith(
144
+ expect.objectContaining({ uri: "nextclaw://objects/inbox-delivery/delivery-1" }),
145
+ );
146
+ });
147
+ });
@@ -5,6 +5,7 @@ import { Link, useNavigate, useParams } from "react-router-dom";
5
5
  import { useAppPresenter } from "@/app/components/app-presenter-provider";
6
6
  import { PageHeader } from "@/app/components/layout/page-layout";
7
7
  import { useViewportLayout } from "@/app/hooks/use-viewport-layout";
8
+ import { CHAT_DRAFT_SESSION_PATH } from "@/features/chat";
8
9
  import { InboxDeliveryContent } from "@/features/inbox/components/inbox-delivery-content";
9
10
  import { useInboxDeliveries } from "@/features/inbox/hooks/use-inbox-deliveries";
10
11
  import { Button } from "@/shared/components/ui/button";
@@ -246,7 +247,7 @@ export function InboxPage() {
246
247
  const navigate = useNavigate();
247
248
  const { deliveryId } = useParams<{ deliveryId?: string }>();
248
249
  const { isMobile } = useViewportLayout();
249
- const { inboxManager } = useAppPresenter();
250
+ const { chatDraftIntentManager, inboxManager } = useAppPresenter();
250
251
  const deliveriesQuery = useInboxDeliveries();
251
252
  const { confirm, ConfirmDialog } = useConfirmDialog();
252
253
  const [selectedFilter, setSelectedFilter] = useState<InboxFilter | null>(null);
@@ -287,8 +288,9 @@ export function InboxPage() {
287
288
  return;
288
289
  }
289
290
  void runAction("continue", async () => {
290
- const result = await inboxManager.continueInChat(activeDelivery.id);
291
- navigate(`/chat/${encodeURIComponent(result.sessionId)}`);
291
+ const { reference } = await inboxManager.prepareChatReference(activeDelivery.id);
292
+ chatDraftIntentManager.requestSystemObjectReference(reference);
293
+ navigate(CHAT_DRAFT_SESSION_PATH);
292
294
  });
293
295
  };
294
296
 
@@ -1,5 +1,6 @@
1
- import { fireEvent, render, screen } from "@testing-library/react";
1
+ import { fireEvent, render, screen, waitFor } from "@testing-library/react";
2
2
  import { MarketplacePage } from "@/features/marketplace";
3
+ import { NextClawClientError } from "@nextclaw/client-sdk";
3
4
  import type {
4
5
  MarketplaceInstalledView,
5
6
  MarketplaceItemSummary,
@@ -82,6 +83,8 @@ vi.mock("@/shared/lib/host-capabilities", () => ({
82
83
  }));
83
84
 
84
85
  vi.mock("@/features/marketplace/hooks/use-marketplace", () => ({
86
+ isMarketplaceSkillLocalChangesError: (error: { code?: string }) =>
87
+ error?.code === "MARKETPLACE_SKILL_LOCAL_CHANGES",
85
88
  useMarketplaceItems: () => mocks.itemsQuery,
86
89
  useMarketplaceRecentItems: () => mocks.recentItemsQuery,
87
90
  useMarketplaceSkillScenes: () => ({
@@ -160,6 +163,43 @@ function createInstalledQuery(
160
163
  };
161
164
  }
162
165
 
166
+ function prepareInstalledSkillUpdate() {
167
+ const item = createMarketplaceItem();
168
+ mocks.itemsQuery = createItemsQuery({
169
+ data: {
170
+ total: 1,
171
+ page: 1,
172
+ pageSize: 20,
173
+ totalPages: 1,
174
+ sort: "relevance",
175
+ items: [item],
176
+ } satisfies MarketplaceListView,
177
+ });
178
+ mocks.installedQuery = createInstalledQuery({
179
+ data: {
180
+ type: "skill",
181
+ total: 1,
182
+ specs: [item.slug],
183
+ records: [{
184
+ type: "skill",
185
+ id: item.slug,
186
+ spec: item.slug,
187
+ source: "workspace",
188
+ origin: "marketplace",
189
+ catalogSlug: item.slug,
190
+ enabled: true,
191
+ }],
192
+ } satisfies MarketplaceInstalledView,
193
+ });
194
+ mocks.manageMutation.mutateAsync.mockRejectedValueOnce(
195
+ new NextClawClientError({
196
+ message: "Local skill files changed since install: web-search",
197
+ status: 409,
198
+ code: "MARKETPLACE_SKILL_LOCAL_CHANGES",
199
+ }),
200
+ );
201
+ }
202
+
163
203
  describe("MarketplacePage", () => {
164
204
  beforeEach(() => {
165
205
  mocks.navigate.mockReset();
@@ -364,4 +404,48 @@ describe("MarketplacePage", () => {
364
404
 
365
405
  expect(mocks.openExternalUrl).toHaveBeenCalledWith("https://skillhub.cn/");
366
406
  });
407
+
408
+ it("asks before overwriting local skill changes and retries the update with force", async () => {
409
+ prepareInstalledSkillUpdate();
410
+ mocks.manageMutation.mutateAsync.mockResolvedValueOnce({});
411
+ mocks.confirm.mockResolvedValueOnce(true);
412
+
413
+ render(<MarketplacePage forcedType="skills" />);
414
+ fireEvent.click(screen.getByRole("button", { name: "Update" }));
415
+
416
+ await waitFor(() => expect(mocks.confirm).toHaveBeenCalledWith({
417
+ title: "Overwrite local changes and update?",
418
+ description: "This skill was changed after installation. Continuing replaces local changes in its skill directory with the latest marketplace version. Canceling leaves the current files unchanged.",
419
+ confirmLabel: "Overwrite and Update",
420
+ variant: "destructive",
421
+ }));
422
+ await waitFor(() => expect(mocks.manageMutation.mutateAsync).toHaveBeenCalledTimes(2));
423
+ expect(mocks.manageMutation.mutateAsync).toHaveBeenNthCalledWith(1, {
424
+ type: "skill",
425
+ action: "update",
426
+ id: "web-search",
427
+ spec: "web-search",
428
+ });
429
+ expect(mocks.manageMutation.mutateAsync).toHaveBeenNthCalledWith(2, {
430
+ type: "skill",
431
+ action: "update",
432
+ id: "web-search",
433
+ spec: "web-search",
434
+ force: true,
435
+ });
436
+ });
437
+
438
+ it("keeps local skill changes untouched when overwrite is canceled", async () => {
439
+ prepareInstalledSkillUpdate();
440
+ mocks.confirm.mockResolvedValueOnce(false);
441
+
442
+ render(<MarketplacePage forcedType="skills" />);
443
+ fireEvent.click(screen.getByRole("button", { name: "Update" }));
444
+
445
+ await waitFor(() => expect(mocks.confirm).toHaveBeenCalledTimes(1));
446
+ expect(mocks.manageMutation.mutateAsync).toHaveBeenCalledTimes(1);
447
+ expect(mocks.manageMutation.mutateAsync).not.toHaveBeenCalledWith(
448
+ expect.objectContaining({ force: true }),
449
+ );
450
+ });
367
451
  });
@@ -1,14 +1,25 @@
1
1
  import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
2
- import { renderHook, waitFor } from "@testing-library/react";
2
+ import { act, renderHook, waitFor } from "@testing-library/react";
3
3
  import type { ReactNode } from "react";
4
4
  import {
5
+ useManageMarketplaceItem,
5
6
  useMarketplaceItems,
6
7
  useMarketplaceRecentItems,
7
8
  } from "@/features/marketplace/hooks/use-marketplace";
9
+ import { NextClawClientError } from "@nextclaw/client-sdk";
8
10
  import type { MarketplaceListView } from "@/shared/lib/api";
9
11
 
10
12
  const mocks = vi.hoisted(() => ({
11
13
  fetchMarketplaceItems: vi.fn(),
14
+ manageMarketplaceItem: vi.fn(),
15
+ toastError: vi.fn(),
16
+ }));
17
+
18
+ vi.mock("sonner", () => ({
19
+ toast: {
20
+ error: mocks.toastError,
21
+ success: vi.fn(),
22
+ },
12
23
  }));
13
24
 
14
25
  vi.mock("@/shared/lib/api", async () => {
@@ -16,6 +27,7 @@ vi.mock("@/shared/lib/api", async () => {
16
27
  return {
17
28
  ...(actual as object),
18
29
  fetchMarketplaceItems: mocks.fetchMarketplaceItems,
30
+ manageMarketplaceItem: mocks.manageMarketplaceItem,
19
31
  };
20
32
  });
21
33
 
@@ -132,3 +144,34 @@ describe("useMarketplaceItems", () => {
132
144
  });
133
145
  });
134
146
  });
147
+
148
+ describe("useManageMarketplaceItem", () => {
149
+ beforeEach(() => {
150
+ mocks.manageMarketplaceItem.mockReset();
151
+ mocks.toastError.mockReset();
152
+ });
153
+
154
+ it("leaves local-change conflicts for the explicit overwrite confirmation", async () => {
155
+ const conflict = new NextClawClientError({
156
+ message: "Local skill files changed since install: web-search",
157
+ status: 409,
158
+ code: "MARKETPLACE_SKILL_LOCAL_CHANGES",
159
+ });
160
+ mocks.manageMarketplaceItem.mockRejectedValueOnce(conflict);
161
+
162
+ const { result } = renderHook(() => useManageMarketplaceItem(), {
163
+ wrapper: createWrapper(),
164
+ });
165
+
166
+ await act(async () => {
167
+ await expect(result.current.mutateAsync({
168
+ type: "skill",
169
+ action: "update",
170
+ id: "web-search",
171
+ spec: "web-search",
172
+ })).rejects.toBe(conflict);
173
+ });
174
+
175
+ expect(mocks.toastError).not.toHaveBeenCalled();
176
+ });
177
+ });
@@ -4,6 +4,7 @@ import type {
4
4
  MarketplaceManageAction,
5
5
  } from "@/shared/lib/api";
6
6
  import {
7
+ isMarketplaceSkillLocalChangesError,
7
8
  useInstallMarketplaceItem,
8
9
  useManageMarketplaceItem,
9
10
  } from "@/features/marketplace/hooks/use-marketplace";
@@ -76,12 +77,32 @@ export function useMarketplaceItemActions() {
76
77
  setManagingTargets((previous) => new Map(previous).set(targetId, action));
77
78
 
78
79
  try {
79
- await manageMutation.mutateAsync({
80
+ const request = {
80
81
  type: record.type,
81
82
  action,
82
83
  id: targetId,
83
84
  spec: record.spec,
84
- });
85
+ } as const;
86
+ try {
87
+ await manageMutation.mutateAsync(request);
88
+ } catch (error) {
89
+ if (action !== "update" || !isMarketplaceSkillLocalChangesError(error)) {
90
+ return;
91
+ }
92
+ const confirmed = await confirm({
93
+ title: t("marketplaceUpdateConflictTitle"),
94
+ description: t("marketplaceUpdateConflictDescription"),
95
+ confirmLabel: t("marketplaceUpdateConflictConfirm"),
96
+ variant: "destructive",
97
+ });
98
+ if (!confirmed) {
99
+ return;
100
+ }
101
+ await manageMutation.mutateAsync({
102
+ ...request,
103
+ force: true,
104
+ });
105
+ }
85
106
  } finally {
86
107
  setManagingTargets((previous) => {
87
108
  const next = new Map(previous);
@@ -24,8 +24,15 @@ import type {
24
24
  } from '@/shared/lib/api';
25
25
  import { collapseMarketplaceListPages } from '@/features/marketplace/utils/marketplace-list-pages.utils';
26
26
  import { useMemo } from 'react';
27
+ import { NextClawClientError } from '@nextclaw/client-sdk';
27
28
 
28
29
  const MARKETPLACE_BACKGROUND_REFRESH_MS = 30_000;
30
+ export const MARKETPLACE_SKILL_LOCAL_CHANGES_ERROR_CODE = 'MARKETPLACE_SKILL_LOCAL_CHANGES';
31
+
32
+ export function isMarketplaceSkillLocalChangesError(error: unknown): boolean {
33
+ return error instanceof NextClawClientError
34
+ && error.code === MARKETPLACE_SKILL_LOCAL_CHANGES_ERROR_CODE;
35
+ }
29
36
 
30
37
  export function useMarketplaceItems(params: MarketplaceListParams) {
31
38
  const query = useInfiniteQuery({
@@ -209,6 +216,9 @@ export function useManageMarketplaceItem() {
209
216
  toast.success(result.message || fallback);
210
217
  },
211
218
  onError: (error: Error) => {
219
+ if (isMarketplaceSkillLocalChangesError(error)) {
220
+ return;
221
+ }
212
222
  toast.error(error.message || t('marketplaceOperationFailed'));
213
223
  }
214
224
  });
@@ -3,6 +3,10 @@ export { McpMarketplacePage } from './components/mcp/mcp-marketplace-page';
3
3
  export {
4
4
  MARKETPLACE_DETAIL_DOC_BROWSER_RENDERERS,
5
5
  } from './components/marketplace-detail-doc';
6
+ export {
7
+ buildLocaleFallbacks,
8
+ pickLocalizedText,
9
+ } from './components/marketplace-localization';
6
10
  export {
7
11
  useInstallMarketplaceItem,
8
12
  useManageMarketplaceItem,
@@ -1,5 +1,9 @@
1
1
  export { PanelAppsList } from './components/panel-apps-list';
2
- export { usePanelApps } from './hooks/use-panel-apps';
2
+ export {
3
+ useGrantPanelAppClient,
4
+ usePanelApps,
5
+ useRecordPanelAppOpened,
6
+ } from './hooks/use-panel-apps';
3
7
  export { PanelAppBridgeManager } from './managers/panel-app-bridge.manager';
4
8
  export { findPanelAppEntryByDisplayId } from './utils/panel-app-entry-match.utils';
5
9
  export { PANEL_APP_IFRAME_SANDBOX, focusPanelAppIframe } from './utils/panel-app-iframe.utils';
@@ -4,9 +4,16 @@ import {
4
4
  } from '@/features/panel-apps/utils/panel-app-doc-browser.utils';
5
5
 
6
6
  describe('panel app doc browser URL helpers', () => {
7
- it('uses the root apps URL for the default panel apps tab', () => {
8
- expect(createAppsPanelUrl('panel-apps')).toBe('nextclaw://apps');
9
- expect(getAppsPanelTabFromUrl('nextclaw://apps')).toBe('panel-apps');
7
+ it('uses the root apps URL for the package library', () => {
8
+ expect(createAppsPanelUrl('apps')).toBe('nextclaw://apps');
9
+ expect(getAppsPanelTabFromUrl('nextclaw://apps')).toBe('apps');
10
+ });
11
+
12
+ it('round-trips the developer panel apps tab', () => {
13
+ const url = createAppsPanelUrl('panel-apps');
14
+
15
+ expect(url).toBe('nextclaw://apps?tab=panel-apps');
16
+ expect(getAppsPanelTabFromUrl(url)).toBe('panel-apps');
10
17
  });
11
18
 
12
19
  it('round-trips the service apps tab through the hidden apps URL', () => {
@@ -16,8 +23,8 @@ describe('panel app doc browser URL helpers', () => {
16
23
  expect(getAppsPanelTabFromUrl(url)).toBe('service-apps');
17
24
  });
18
25
 
19
- it('falls back to panel apps for unknown URL state', () => {
20
- expect(getAppsPanelTabFromUrl('nextclaw://apps?tab=unknown')).toBe('panel-apps');
21
- expect(getAppsPanelTabFromUrl('not a url')).toBe('panel-apps');
26
+ it('falls back to the package library for unknown URL state', () => {
27
+ expect(getAppsPanelTabFromUrl('nextclaw://apps?tab=unknown')).toBe('apps');
28
+ expect(getAppsPanelTabFromUrl('not a url')).toBe('apps');
22
29
  });
23
30
  });
@@ -10,6 +10,7 @@ import {
10
10
  RIGHT_PANEL_APPS_URL,
11
11
  RIGHT_PANEL_HOME_TAB_KIND,
12
12
  RIGHT_PANEL_PANEL_APP_TAB_KIND,
13
+ RIGHT_PANEL_PANEL_APPS_URL,
13
14
  RIGHT_PANEL_SERVICE_APPS_URL,
14
15
  RightPanelResourceHomePage,
15
16
  } from '@/features/right-panel-resources';
@@ -18,14 +19,16 @@ import { PANEL_APP_IFRAME_SANDBOX, focusPanelAppIframe } from './panel-app-ifram
18
19
 
19
20
  export const APPS_TAB_KIND = RIGHT_PANEL_APPS_TAB_KIND;
20
21
  export const PANEL_APP_TAB_KIND = RIGHT_PANEL_PANEL_APP_TAB_KIND;
21
- const DEFAULT_APPS_PANEL_TAB: AppsPanelTab = 'panel-apps';
22
+ const DEFAULT_APPS_PANEL_TAB: AppsPanelTab = 'apps';
22
23
 
23
24
  function isAppsPanelTab(value: unknown): value is AppsPanelTab {
24
- return value === 'panel-apps' || value === 'service-apps';
25
+ return value === 'apps' || value === 'panel-apps' || value === 'service-apps';
25
26
  }
26
27
 
27
28
  export function createAppsPanelUrl(tab: AppsPanelTab = DEFAULT_APPS_PANEL_TAB): string {
28
- return tab === DEFAULT_APPS_PANEL_TAB ? RIGHT_PANEL_APPS_URL : RIGHT_PANEL_SERVICE_APPS_URL;
29
+ if (tab === 'panel-apps') return RIGHT_PANEL_PANEL_APPS_URL;
30
+ if (tab === 'service-apps') return RIGHT_PANEL_SERVICE_APPS_URL;
31
+ return RIGHT_PANEL_APPS_URL;
29
32
  }
30
33
 
31
34
  export function getAppsPanelTabFromUrl(url: string): AppsPanelTab {
@@ -15,5 +15,6 @@ export {
15
15
  RIGHT_PANEL_HOME_TAB_KIND,
16
16
  RIGHT_PANEL_HOME_URL,
17
17
  RIGHT_PANEL_PANEL_APP_TAB_KIND,
18
+ RIGHT_PANEL_PANEL_APPS_URL,
18
19
  RIGHT_PANEL_SERVICE_APPS_URL,
19
20
  } from '@/features/right-panel-resources/utils/right-panel-resource-uri.utils';
@@ -6,19 +6,22 @@ export const RIGHT_PANEL_HOME_TAB_KIND = 'home';
6
6
  export const RIGHT_PANEL_HOME_URL = 'nextclaw://new-tab';
7
7
  export const RIGHT_PANEL_APPS_TAB_KIND = 'apps';
8
8
  export const RIGHT_PANEL_APPS_URL = 'nextclaw://apps';
9
+ export const RIGHT_PANEL_PANEL_APPS_URL = `${RIGHT_PANEL_APPS_URL}?tab=panel-apps`;
9
10
  export const RIGHT_PANEL_SERVICE_APPS_URL = `${RIGHT_PANEL_APPS_URL}?tab=service-apps`;
10
11
  export const RIGHT_PANEL_PANEL_APP_TAB_KIND = 'panel-app';
11
12
 
12
- export type RightPanelAppsTab = 'panel-apps' | 'service-apps';
13
+ export type RightPanelAppsTab = 'apps' | 'panel-apps' | 'service-apps';
13
14
 
14
- const DEFAULT_RIGHT_PANEL_APPS_TAB: RightPanelAppsTab = 'panel-apps';
15
+ const DEFAULT_RIGHT_PANEL_APPS_TAB: RightPanelAppsTab = 'apps';
15
16
 
16
17
  function isRightPanelAppsTab(value: unknown): value is RightPanelAppsTab {
17
- return value === 'panel-apps' || value === 'service-apps';
18
+ return value === 'apps' || value === 'panel-apps' || value === 'service-apps';
18
19
  }
19
20
 
20
21
  export function createRightPanelAppsUrl(tab: RightPanelAppsTab = DEFAULT_RIGHT_PANEL_APPS_TAB): string {
21
- return tab === DEFAULT_RIGHT_PANEL_APPS_TAB ? RIGHT_PANEL_APPS_URL : RIGHT_PANEL_SERVICE_APPS_URL;
22
+ if (tab === 'panel-apps') return RIGHT_PANEL_PANEL_APPS_URL;
23
+ if (tab === 'service-apps') return RIGHT_PANEL_SERVICE_APPS_URL;
24
+ return RIGHT_PANEL_APPS_URL;
22
25
  }
23
26
 
24
27
  export function getRightPanelAppsTabFromUrl(url: string): RightPanelAppsTab {
@@ -11,6 +11,7 @@ const mocks = vi.hoisted(() => ({
11
11
  start: vi.fn(),
12
12
  stop: vi.fn(),
13
13
  checkForUpdates: vi.fn(),
14
+ updateNow: vi.fn(),
14
15
  downloadUpdate: vi.fn(),
15
16
  applyDownloadedUpdate: vi.fn(),
16
17
  updateChannel: vi.fn()
@@ -48,6 +49,7 @@ describe('DesktopUpdateConfig', () => {
48
49
  mocks.start.mockReset();
49
50
  mocks.stop.mockReset();
50
51
  mocks.checkForUpdates.mockReset();
52
+ mocks.updateNow.mockReset();
51
53
  mocks.downloadUpdate.mockReset();
52
54
  mocks.applyDownloadedUpdate.mockReset();
53
55
  mocks.updateChannel.mockReset();
@@ -101,12 +103,29 @@ describe('DesktopUpdateConfig', () => {
101
103
  expect(screen.getByText('当前更新通道')).toBeTruthy();
102
104
  expect(screen.getAllByText('Beta').length).toBeGreaterThan(0);
103
105
  expect(screen.getByText('当前正在跟随 Beta 通道')).toBeTruthy();
104
- expect(screen.getByText('系统每两小时自动检查更新,下载和应用都由你手动确认。')).toBeTruthy();
106
+ expect(screen.getByText('系统每两小时自动检查更新;是否开始更新由你确认。')).toBeTruthy();
105
107
  expect(screen.queryByText('自动检查更新')).toBeNull();
106
108
  expect(screen.queryByText('发现更新后自动后台下载')).toBeNull();
107
109
  expect(screen.getByText('切回 Stable 后不会立刻强制降级;只有当 Stable 追平或超过当前版本时,才会继续提供 Stable 更新。')).toBeTruthy();
108
110
  });
109
111
 
112
+ it('uses one update action instead of separate download and apply actions', async () => {
113
+ const user = userEvent.setup();
114
+ useRuntimeUpdateStore.setState((state) => ({
115
+ ...state,
116
+ snapshot: state.snapshot ? { ...state.snapshot, status: 'update-available' } : null
117
+ }));
118
+
119
+ renderDesktopUpdateConfig();
120
+
121
+ expect(screen.queryByRole('button', { name: '下载更新' })).toBeNull();
122
+ const updateButton = screen.getByRole('button', { name: '立即更新' });
123
+ await user.click(updateButton);
124
+ expect(mocks.updateNow).toHaveBeenCalledTimes(1);
125
+ expect(mocks.downloadUpdate).not.toHaveBeenCalled();
126
+ expect(mocks.applyDownloadedUpdate).not.toHaveBeenCalled();
127
+ });
128
+
110
129
  it('sends the newly selected release channel to the desktop update manager', async () => {
111
130
  const user = userEvent.setup();
112
131