@nextclaw/ui 0.17.0 → 0.17.2

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 (241) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/dist/assets/{abnfDiagram-VRR7QNED-BcFJp_1X.js → abnfDiagram-VRR7QNED-DvTjgVMf.js} +1 -1
  3. package/dist/assets/{aiden0z-pptx-renderer.es-Dp2ezlQn.js → aiden0z-pptx-renderer.es-C1nfBAXR.js} +1 -1
  4. package/dist/assets/{api-CVjdSvJk.js → api-CuPWhNDM.js} +3 -3
  5. package/dist/assets/{appearance-settings-page-BOorfaUx.js → appearance-settings-page-DRjg6UP3.js} +1 -1
  6. package/dist/assets/{arc-CkgyPMyW.js → arc-BvhODBXv.js} +1 -1
  7. package/dist/assets/architecture-TIHT7OUA-Bjp9jf0h.js +1 -0
  8. package/dist/assets/{architectureDiagram-ZJ3FMSHR-CMd4cIjI.js → architectureDiagram-ZJ3FMSHR-BKO9RgNS.js} +1 -1
  9. package/dist/assets/{blockDiagram-677ZJIJ3-Bz3tbhet.js → blockDiagram-677ZJIJ3-DHpDXy7D.js} +1 -1
  10. package/dist/assets/book-open-B4ovWX14.js +1 -0
  11. package/dist/assets/{c4Diagram-LMCZKHZV-D9hFNAGv.js → c4Diagram-LMCZKHZV-BG_iQnDr.js} +1 -1
  12. package/dist/assets/channel-5T3pdR-s.js +1 -0
  13. package/dist/assets/{channels-list-page-DuZJvYB6.js → channels-list-page-B26GXq63.js} +2 -2
  14. package/dist/assets/{chat-page-8PTMgI0t.js → chat-page-QA0dpNyU.js} +1 -1
  15. package/dist/assets/chunk-2Q5K7J3B-DTZ3tGfE.js +1 -0
  16. package/dist/assets/{chunk-32BRIVSS-7NrjEITq.js → chunk-32BRIVSS-DR9M0LLx.js} +1 -1
  17. package/dist/assets/{chunk-52WLFC77-viIcqj9q.js → chunk-52WLFC77-698iC3BB.js} +1 -1
  18. package/dist/assets/{chunk-5FCAYU7R-Vsu4J-VM.js → chunk-5FCAYU7R-DikTj9Ey.js} +1 -1
  19. package/dist/assets/{chunk-5HE753X5-C-w6CQZK.js → chunk-5HE753X5-ko0o7_q0.js} +1 -1
  20. package/dist/assets/{chunk-5JV3BV7I-CuKgJQ4W.js → chunk-5JV3BV7I-CEHufhgo.js} +1 -1
  21. package/dist/assets/{chunk-5TONJI2A-CYeiWZKb.js → chunk-5TONJI2A-CGgno1cu.js} +1 -1
  22. package/dist/assets/{chunk-5VM5RSS4-DRCbfwIn.js → chunk-5VM5RSS4-DkiB4cUQ.js} +1 -1
  23. package/dist/assets/{chunk-7BUUIJ7U-BvydOcm3.js → chunk-7BUUIJ7U-DzptcytQ.js} +1 -1
  24. package/dist/assets/{chunk-BIQX33UG-RlppOXKo.js → chunk-BIQX33UG-DMRSQJvT.js} +1 -1
  25. package/dist/assets/{chunk-C7G6YPKG-DAizzYwr.js → chunk-C7G6YPKG-Bw1BwwuY.js} +1 -1
  26. package/dist/assets/{chunk-CQNSW5MT-5eChJHca.js → chunk-CQNSW5MT-C0z6eHBk.js} +1 -1
  27. package/dist/assets/{chunk-CYSBUYHQ-YHPr53fv.js → chunk-CYSBUYHQ-ByPyAouO.js} +1 -1
  28. package/dist/assets/{chunk-EMLP6XTP-CK4D7Hz5.js → chunk-EMLP6XTP-CJlAXWZg.js} +1 -1
  29. package/dist/assets/{chunk-EX3LRPZG-BmlDFIRW.js → chunk-EX3LRPZG-Dqq4zKJu.js} +1 -1
  30. package/dist/assets/{chunk-FWX5IMBZ-D0ksHZZ9.js → chunk-FWX5IMBZ-B0SNoCmi.js} +2 -2
  31. package/dist/assets/{chunk-HOUHSVGY-DPkq8FCm.js → chunk-HOUHSVGY-DG_GJih_.js} +1 -1
  32. package/dist/assets/{chunk-ICXQ74PX-FAatv5fC.js → chunk-ICXQ74PX-Drxk_6bE.js} +1 -1
  33. package/dist/assets/{chunk-JG7HCLWE-DLkIi4F7.js → chunk-JG7HCLWE-DdLFPpnd.js} +1 -1
  34. package/dist/assets/{chunk-JWPE2WC7-DawOjNE3.js → chunk-JWPE2WC7-B8M4G76F.js} +1 -1
  35. package/dist/assets/{chunk-MOJQB5TN-Di52TorW.js → chunk-MOJQB5TN-XP6FJHY_.js} +1 -1
  36. package/dist/assets/{chunk-MOZMSUNE-PfyK9w1-.js → chunk-MOZMSUNE-C_6x18Qw.js} +1 -1
  37. package/dist/assets/{chunk-OGEWGWER-C3jdH3Ty.js → chunk-OGEWGWER-N25BzCmG.js} +1 -1
  38. package/dist/assets/{chunk-OSBZ3O6U-CecHo-f0.js → chunk-OSBZ3O6U-ASDEm7y0.js} +1 -1
  39. package/dist/assets/{chunk-PUDLZKDR-f3IJh3sN.js → chunk-PUDLZKDR-CTlvAcn7.js} +1 -1
  40. package/dist/assets/{chunk-Q4XR5HBZ-DQFeUPxZ.js → chunk-Q4XR5HBZ-Da9BWfB6.js} +1 -1
  41. package/dist/assets/{chunk-QBLGF6JB-BhUeG0y2.js → chunk-QBLGF6JB-Bi6RpTJO.js} +1 -1
  42. package/dist/assets/{chunk-R7FJI6CG-C3XpZ-2u.js → chunk-R7FJI6CG-DkMsSPvK.js} +1 -1
  43. package/dist/assets/{chunk-RYQCIY6F-CAqa5klQ.js → chunk-RYQCIY6F-DZp0eU-j.js} +1 -1
  44. package/dist/assets/{chunk-U6XO7XAA-D_WXezeD.js → chunk-U6XO7XAA-vfinDSrj.js} +1 -1
  45. package/dist/assets/{chunk-V7JOEXUC-CZ70oOtp.js → chunk-V7JOEXUC-C9Nksccs.js} +1 -1
  46. package/dist/assets/chunk-VAUOI2AC-HLGIHQ6k.js +1 -0
  47. package/dist/assets/{chunk-VR4S4FIN-B7AHEYYk.js → chunk-VR4S4FIN-Xv6zfwXs.js} +1 -1
  48. package/dist/assets/{chunk-WYO6CB5R-Dn9To_OI.js → chunk-WYO6CB5R-mOJNzbWl.js} +2 -2
  49. package/dist/assets/chunk-XXDRQBXY-Dl0m6Ni-.js +1 -0
  50. package/dist/assets/{chunk-YOTPTUD7-BYEgRBk3.js → chunk-YOTPTUD7-1-aKSu4o.js} +1 -1
  51. package/dist/assets/{chunk-ZGVPDNZ5-C6YtZDU0.js → chunk-ZGVPDNZ5-OJ8veMpK.js} +1 -1
  52. package/dist/assets/{chunk-ZIRB5QZD-DMuYtnHF.js → chunk-ZIRB5QZD-SR_Nq_PP.js} +1 -1
  53. package/dist/assets/classDiagram-OUVF2IWQ-DcDnzGNX.js +1 -0
  54. package/dist/assets/classDiagram-v2-EOCWNBFH-BKXKMzKM.js +1 -0
  55. package/dist/assets/{config-split-page-UuhsVcQD.js → config-split-page-Box_-aBK.js} +1 -1
  56. package/dist/assets/{confirm-dialog-Bnosa8UF.js → confirm-dialog-uqcCxomz.js} +1 -1
  57. package/dist/assets/{cose-bilkent-JH36ORCC-Bc2Ex5Y3.js → cose-bilkent-JH36ORCC-NjVgz2Bw.js} +1 -1
  58. package/dist/assets/cynefin-VYW2F7L2-BWg7U2UD.js +1 -0
  59. package/dist/assets/{cynefinDiagram-TSTJHNR4-DVysQoGz.js → cynefinDiagram-TSTJHNR4-B_17LDqD.js} +1 -1
  60. package/dist/assets/{dagre-CMhQ1cAG.js → dagre-CMe101gk.js} +1 -1
  61. package/dist/assets/{dagre-VKFMJZFB-RYyWpPJZ.js → dagre-VKFMJZFB-qo8nLb3B.js} +1 -1
  62. package/dist/assets/{desktop-update-config-CM4lsX4R.js → desktop-update-config-DkDor159.js} +1 -1
  63. package/dist/assets/{diagram-FQU43EPY-BD6KONdF.js → diagram-FQU43EPY-Ba3XIklC.js} +1 -1
  64. package/dist/assets/{diagram-G47NLZAW-CGB2ptRR.js → diagram-G47NLZAW-CG45TcUx.js} +1 -1
  65. package/dist/assets/{diagram-NH7WQ7WH-DnkOI6cU.js → diagram-NH7WQ7WH-2F2HrwV-.js} +1 -1
  66. package/dist/assets/{diagram-OA4YK3LP-DF82RZ7P.js → diagram-OA4YK3LP-CXfWH9NZ.js} +1 -1
  67. package/dist/assets/{diagram-WEI45ONY-Be2v7Ck9.js → diagram-WEI45ONY-CH_4yepc.js} +1 -1
  68. package/dist/assets/{dist-D17RZAsz.js → dist-CZLIZntJ.js} +1 -1
  69. package/dist/assets/{dist-_zZ8P38T.js → dist-Co2G-YjN.js} +1 -1
  70. package/dist/assets/doc-browser-CbCBDdap.js +1 -0
  71. package/dist/assets/doc-browser-DUqckL2E.js +1 -0
  72. package/dist/assets/{doc-browser-context-CerkiK_N.js → doc-browser-context-aeZu0FHR.js} +1 -1
  73. package/dist/assets/{docx-preview-BqwAjrj3.js → docx-preview-CoM4JK7_.js} +1 -1
  74. package/dist/assets/{ebnfDiagram-CCIWWBDH-CP98OYk8.js → ebnfDiagram-CCIWWBDH-BwiG4Ea1.js} +1 -1
  75. package/dist/assets/{erDiagram-Q63AITRT-ZLlx8xfs.js → erDiagram-Q63AITRT-BML3hpnQ.js} +1 -1
  76. package/dist/assets/eventmodeling-45OFAUF4-SB-J4tpq.js +1 -0
  77. package/dist/assets/flowDiagram-23GEKE2U-BQ_u-eLM.js +1 -0
  78. package/dist/assets/{form-actions-Cmv23dN4.js → form-actions-C3Pa1HQb.js} +1 -1
  79. package/dist/assets/{ganttDiagram-NO4QXBWP-DXCDjHR2.js → ganttDiagram-NO4QXBWP-80enfWbD.js} +1 -1
  80. package/dist/assets/gitGraph-TEB2WS4Q-DPxNwsFd.js +1 -0
  81. package/dist/assets/{gitGraphDiagram-IHSO6WYX-l2HWrTJp.js → gitGraphDiagram-IHSO6WYX-CVLqXr-b.js} +1 -1
  82. package/dist/assets/index-Bsoscjk7.css +1 -0
  83. package/dist/assets/index-CCWgeWoR.js +109 -0
  84. package/dist/assets/info-DKCQHKI2-DNlNGH90.js +1 -0
  85. package/dist/assets/{infoDiagram-FWYZ7A6U-D-DjX-bn.js → infoDiagram-FWYZ7A6U-Dyvs2t0i.js} +1 -1
  86. package/dist/assets/{ishikawaDiagram-FXEZZL3T-y_Xy128F.js → ishikawaDiagram-FXEZZL3T-sSnxDKfh.js} +1 -1
  87. package/dist/assets/{journeyDiagram-5HDEW3XC-DJn5xYcQ.js → journeyDiagram-5HDEW3XC-D22V71Mf.js} +1 -1
  88. package/dist/assets/{kanban-definition-HUTT4EX6-DJVIOR21.js → kanban-definition-HUTT4EX6-D3ds0p-g.js} +1 -1
  89. package/dist/assets/{line-Cos7PIOj.js → line-CbyFs065.js} +1 -1
  90. package/dist/assets/{linear-BtDLqtzF.js → linear-4DKtnZBS.js} +1 -1
  91. package/dist/assets/loader-circle-DfbWUPdI.js +1 -0
  92. package/dist/assets/{map-BKnQyvd0.js → map-CcZzEoz6.js} +1 -1
  93. package/dist/assets/mcp-marketplace-page-B2D7ciJL.js +1 -0
  94. package/dist/assets/mcp-marketplace-page-BmqbIQQQ.js +9 -0
  95. package/dist/assets/{mermaid-parser.core-XUNZhr2W.js → mermaid-parser.core-DPmMOjXD.js} +2 -2
  96. package/dist/assets/{mermaid.core-ySrvolIe.js → mermaid.core-BSf-ddRa.js} +3 -3
  97. package/dist/assets/{mindmap-definition-LN4V7U3C-BypL9tei.js → mindmap-definition-LN4V7U3C-BUTwWPyx.js} +1 -1
  98. package/dist/assets/model-config-page-BXUoZEEZ.js +1 -0
  99. package/dist/assets/{navigation-link-k9rsmtC4.js → navigation-link-DngetDmN.js} +1 -1
  100. package/dist/assets/{ordinal-CxxVWxYU.js → ordinal-BgLYtOZE.js} +1 -1
  101. package/dist/assets/packet-7NZHBO7P-BpeiqkBZ.js +1 -0
  102. package/dist/assets/{pegDiagram-2B236MQR-D-U2KBGv.js → pegDiagram-2B236MQR-BEu_ZBjc.js} +1 -1
  103. package/dist/assets/pie-RZYD4A2V-Cy1yxsPd.js +1 -0
  104. package/dist/assets/{pieDiagram-ENE6RG2P-CBi3GusJ.js → pieDiagram-ENE6RG2P-CAwAwuBd.js} +1 -1
  105. package/dist/assets/plus-wNpOxBfP.js +1 -0
  106. package/dist/assets/{provider-scoped-model-input-DLSAB6a5.js → provider-scoped-model-input-C4a2TuV7.js} +1 -1
  107. package/dist/assets/{providers-config-page-DM5N0aeS.js → providers-config-page-CHSka1kQ.js} +1 -1
  108. package/dist/assets/{quadrantDiagram-ABIIQ3AL-H0ZuvnZL.js → quadrantDiagram-ABIIQ3AL-CwE6F-_2.js} +1 -1
  109. package/dist/assets/radar-I7S5WNFK-CEcPKCj8.js +1 -0
  110. package/dist/assets/railroad-3IZDKUUU-CW38eNDs.js +1 -0
  111. package/dist/assets/railroad-abnf-AHOZXSZD-ByrylcXf.js +1 -0
  112. package/dist/assets/railroad-ebnf-EBAXGLYW-CNB6GwP-.js +1 -0
  113. package/dist/assets/railroad-peg-LSFZ7HO6-x6hnqlBG.js +1 -0
  114. package/dist/assets/{railroadDiagram-RFXS5EU6-D7Y2EqSd.js → railroadDiagram-RFXS5EU6-xgLekU3B.js} +1 -1
  115. package/dist/assets/{react-BgRLh3ee.js → react-CPH0zySO.js} +1 -1
  116. package/dist/assets/{refresh-cw-axvJLmRG.js → refresh-cw-vTYZqC0b.js} +1 -1
  117. package/dist/assets/remote-DgfRmokf.js +1 -0
  118. package/dist/assets/{requirementDiagram-TGXJPOKE-BiXNX1NN.js → requirementDiagram-TGXJPOKE-CyVUzEX5.js} +1 -1
  119. package/dist/assets/rotate-cw-L_seGUwq.js +1 -0
  120. package/dist/assets/runtime-config-page-V75_zqXa.js +1 -0
  121. package/dist/assets/{sankeyDiagram-HTMAVEWB-qzDeLztW.js → sankeyDiagram-HTMAVEWB-JK6bFmYB.js} +1 -1
  122. package/dist/assets/{save-DbR4SGk8.js → save-lWb6TI69.js} +1 -1
  123. package/dist/assets/{search-M9lrb8id.js → search-BuUX_0KY.js} +1 -1
  124. package/dist/assets/{search-config-page-DmsID8FQ.js → search-config-page-rlkRJZCF.js} +1 -1
  125. package/dist/assets/{secrets-config-page-C10OwI0b.js → secrets-config-page-BxCEc8r5.js} +2 -2
  126. package/dist/assets/security-config-Cqxe13za.js +1 -0
  127. package/dist/assets/{select-D_YBzMdv.js → select-oalsltZV.js} +1 -1
  128. package/dist/assets/{sequenceDiagram-DBY2YBRQ-BtRbPQtL.js → sequenceDiagram-DBY2YBRQ-BR5IokXV.js} +1 -1
  129. package/dist/assets/{settings-2-zy40UwTo.js → settings-2-Bd9xiNAY.js} +1 -1
  130. package/dist/assets/{sizeCapture-X5ZJPWSS-CMYvM3SL.js → sizeCapture-X5ZJPWSS-SxG_YnMA.js} +1 -1
  131. package/dist/assets/skeleton-D72EdBNa.js +1 -0
  132. package/dist/assets/{src-Bw-mzbQb.js → src-vgz8E_-O.js} +1 -1
  133. package/dist/assets/{stateDiagram-2N3HPSRC-BJ-mFeOz.js → stateDiagram-2N3HPSRC-DD4OoYhn.js} +1 -1
  134. package/dist/assets/stateDiagram-v2-6OUMAXLB-ZC6dFORv.js +1 -0
  135. package/dist/assets/{swimlanes-5IMT3BWC-BsucVOO2.js → swimlanes-5IMT3BWC-BWkFeyCT.js} +2 -2
  136. package/dist/assets/swimlanesDiagram-G3AALYLV-CRkh51Im.js +8 -0
  137. package/dist/assets/{tag-chip-D7QRdcph.js → tag-chip-B324d_Xk.js} +1 -1
  138. package/dist/assets/{timeline-definition-FHXFAJF6-Bicewlzo.js → timeline-definition-FHXFAJF6-VTRPy6KA.js} +1 -1
  139. package/dist/assets/treeView-QDETBFTQ-Bw0DSCH7.js +1 -0
  140. package/dist/assets/treemap-6X3UGDF4-DFWBFcfh.js +1 -0
  141. package/dist/assets/{vennDiagram-L72KCM5P-BxOOnO9x.js → vennDiagram-L72KCM5P-Bvk42Pek.js} +1 -1
  142. package/dist/assets/wardley-OPB4EBWU-CoWturk0.js +1 -0
  143. package/dist/assets/{wardleyDiagram-EHGQE667-Cl7NltMv.js → wardleyDiagram-EHGQE667-DOFjx2s3.js} +1 -1
  144. package/dist/assets/x-Cps-je1K.js +1 -0
  145. package/dist/assets/{xychartDiagram-FW5EYKEG-DiMIxQ7N.js → xychartDiagram-FW5EYKEG-DY5yRTUc.js} +1 -1
  146. package/dist/index.html +22 -21
  147. package/package.json +9 -9
  148. package/src/features/apps/components/__tests__/app-packages-panel.test.tsx +43 -10
  149. package/src/features/apps/components/__tests__/apps-panel.test.tsx +18 -2
  150. package/src/features/apps/components/app-package-card.tsx +58 -4
  151. package/src/features/apps/components/app-packages-panel.tsx +60 -22
  152. package/src/features/apps/components/apps-panel.tsx +14 -3
  153. package/src/features/apps/hooks/use-app-packages.ts +5 -2
  154. package/src/features/apps/managers/__tests__/app-package-operation-settlement.manager.test.ts +5 -0
  155. package/src/features/apps/managers/app-package-operation-settlement.manager.ts +8 -2
  156. package/src/features/chat/features/message/utils/__tests__/chat-message-show-content-tool-card.utils.test.ts +46 -0
  157. package/src/features/chat/features/message/utils/chat-message-part.utils.ts +2 -0
  158. package/src/features/chat/features/message/utils/chat-message-tool-card.utils.ts +52 -2
  159. package/src/features/chat/features/ncp/hooks/__tests__/use-hydrated-ncp-agent.test.tsx +97 -10
  160. package/src/features/chat/features/ncp/hooks/use-ncp-session-conversation.ts +4 -0
  161. package/src/features/chat/features/ncp/hooks/use-ncp-session-queries.ts +10 -0
  162. package/src/features/chat/features/session/utils/__tests__/ncp-session-adapter.utils.test.ts +27 -0
  163. package/src/features/chat/features/session/utils/ncp-session-adapter.utils.ts +2 -1
  164. package/src/features/chat/features/workspace/components/__tests__/chat-session-child-sessions.test.tsx +51 -0
  165. package/src/features/chat/features/workspace/components/__tests__/chat-session-token-usage.test.tsx +90 -0
  166. package/src/features/chat/features/workspace/components/__tests__/chat-session-workspace-panel-content.test.tsx +9 -17
  167. package/src/features/chat/features/workspace/components/chat-session-workspace-panel-content.tsx +4 -45
  168. package/src/features/chat/features/workspace/components/child-sessions/chat-session-child-sessions.tsx +66 -0
  169. package/src/features/chat/features/workspace/components/overview/chat-session-token-usage.tsx +171 -0
  170. package/src/features/chat/types/chat-message.types.ts +5 -0
  171. package/src/features/service-apps/components/__tests__/service-apps-panel.test.tsx +32 -4
  172. package/src/features/service-apps/components/service-app-list-item.tsx +26 -12
  173. package/src/features/service-apps/components/service-apps-panel.tsx +23 -1
  174. package/src/shared/components/doc-browser/__tests__/doc-browser-tab-strip.test.tsx +78 -0
  175. package/src/shared/components/doc-browser/doc-browser-tab-strip.tsx +71 -3
  176. package/src/shared/components/ui/tab-strip/__tests__/compact-tab-strip.test.tsx +78 -1
  177. package/src/shared/components/ui/tab-strip/compact-tab-strip.tsx +97 -44
  178. package/src/shared/lib/api/__tests__/ncp-session-query-cache.utils.test.ts +2 -0
  179. package/src/shared/lib/api/ncp-session-query-cache.utils.ts +4 -0
  180. package/src/shared/lib/api/ncp-session.types.ts +3 -0
  181. package/src/shared/lib/api/types.ts +1 -0
  182. package/src/shared/lib/api/utils/ncp-session.utils.ts +8 -0
  183. package/src/shared/lib/i18n/locales/en-US/chat.json +19 -0
  184. package/src/shared/lib/i18n/locales/en-US/doc-browser.json +3 -0
  185. package/src/shared/lib/i18n/locales/zh-CN/chat.json +20 -1
  186. package/src/shared/lib/i18n/locales/zh-CN/doc-browser.json +3 -0
  187. package/dist/assets/architecture-TIHT7OUA-DO2x53FK.js +0 -1
  188. package/dist/assets/channel-nBvMaVyP.js +0 -1
  189. package/dist/assets/chunk-2Q5K7J3B-CJgqFDS4.js +0 -1
  190. package/dist/assets/chunk-VAUOI2AC-BGvh3yIx.js +0 -1
  191. package/dist/assets/chunk-XXDRQBXY-DaBxG5gs.js +0 -1
  192. package/dist/assets/classDiagram-OUVF2IWQ-P6N03G2R.js +0 -1
  193. package/dist/assets/classDiagram-v2-EOCWNBFH-B1_uLvMv.js +0 -1
  194. package/dist/assets/cynefin-VYW2F7L2-B-H8Qp-n.js +0 -1
  195. package/dist/assets/doc-browser-CiLvybSC.js +0 -1
  196. package/dist/assets/doc-browser-z7vdH3Ez.js +0 -1
  197. package/dist/assets/eventmodeling-45OFAUF4-YVtShyFA.js +0 -1
  198. package/dist/assets/flowDiagram-23GEKE2U-BeKXabVO.js +0 -1
  199. package/dist/assets/gitGraph-TEB2WS4Q-jTSiBj-p.js +0 -1
  200. package/dist/assets/index-CWdgBpSc.css +0 -1
  201. package/dist/assets/index-DlL8uKXu.js +0 -109
  202. package/dist/assets/info-DKCQHKI2-BIporNWb.js +0 -1
  203. package/dist/assets/loader-circle-vUsFtTfb.js +0 -1
  204. package/dist/assets/mcp-marketplace-page-BV8WB3_L.js +0 -1
  205. package/dist/assets/mcp-marketplace-page-DN_fqcLd.js +0 -9
  206. package/dist/assets/model-config-page-BxaeCMbJ.js +0 -1
  207. package/dist/assets/packet-7NZHBO7P-CuFMADV0.js +0 -1
  208. package/dist/assets/pie-RZYD4A2V-XUeIXhM9.js +0 -1
  209. package/dist/assets/plus-Dc760kHj.js +0 -1
  210. package/dist/assets/radar-I7S5WNFK-D-NNGh1k.js +0 -1
  211. package/dist/assets/railroad-3IZDKUUU-C67_uDaM.js +0 -1
  212. package/dist/assets/railroad-abnf-AHOZXSZD-BZ8Yflnd.js +0 -1
  213. package/dist/assets/railroad-ebnf-EBAXGLYW-B7zO7RoK.js +0 -1
  214. package/dist/assets/railroad-peg-LSFZ7HO6-R63PTgLz.js +0 -1
  215. package/dist/assets/remote-Cj1GBMaN.js +0 -1
  216. package/dist/assets/rotate-cw-CFByP2O8.js +0 -1
  217. package/dist/assets/runtime-config-page-WNzZyjMW.js +0 -1
  218. package/dist/assets/security-config-CvU5JxDT.js +0 -1
  219. package/dist/assets/skeleton-BmAtzjDM.js +0 -1
  220. package/dist/assets/stateDiagram-v2-6OUMAXLB-BE-HqxOK.js +0 -1
  221. package/dist/assets/swimlanesDiagram-G3AALYLV-Dr1H9LJH.js +0 -8
  222. package/dist/assets/treeView-QDETBFTQ-npJ5dbqS.js +0 -1
  223. package/dist/assets/treemap-6X3UGDF4-pYrBx6oa.js +0 -1
  224. package/dist/assets/wardley-OPB4EBWU-CmSRKLR8.js +0 -1
  225. package/dist/assets/x-D9Y9hf_r.js +0 -1
  226. /package/dist/assets/{array-BQVzeGRt.js → array-NADKd9aU.js} +0 -0
  227. /package/dist/assets/{chunk-KEIR6QF5-f9psyCXK.js → chunk-KEIR6QF5-BuyuzJJL.js} +0 -0
  228. /package/dist/assets/{chunk-Y2CYZVJY-D1mAU_Po.js → chunk-Y2CYZVJY-D_B2ZRNt.js} +0 -0
  229. /package/dist/assets/{config-hints-nV5HPOvq.js → config-hints-BcfVUMij.js} +0 -0
  230. /package/dist/assets/{cytoscape.esm-0uDtdD0l.js → cytoscape.esm-BANTlgza.js} +0 -0
  231. /package/dist/assets/{defaultLocale-DSWXGgt0.js → defaultLocale-C4_L0aEs.js} +0 -0
  232. /package/dist/assets/{dist--ta5rX12.js → dist-DAPEIJbz.js} +0 -0
  233. /package/dist/assets/{graphlib-CQ6QV4yP.js → graphlib-ChqXg0dI.js} +0 -0
  234. /package/dist/assets/{host-capabilities-Tk038i04.js → host-capabilities-D9C4CBK5.js} +0 -0
  235. /package/dist/assets/{init-Y6h9jGtZ.js → init-D8wZg9xq.js} +0 -0
  236. /package/dist/assets/{jszip.min-8hyJpgpx.js → jszip.min-CkbBfwAQ.js} +0 -0
  237. /package/dist/assets/{katex-ARhY9PS1.js → katex-TZJFa0J_.js} +0 -0
  238. /package/dist/assets/{middleware-DXsbf9xI.js → middleware-B3k-TSmZ.js} +0 -0
  239. /package/dist/assets/{path-10BjdyLh.js → path-DodfxcX7.js} +0 -0
  240. /package/dist/assets/{rough.esm-DxgC_14r.js → rough.esm-CUaa1Rbe.js} +0 -0
  241. /package/dist/assets/{xlsx-DrDuDNAa.js → xlsx-CxByi7bR.js} +0 -0
@@ -7,6 +7,7 @@ import type {
7
7
  } from "@nextclaw/client-sdk";
8
8
  import {
9
9
  AlertTriangle,
10
+ Boxes,
10
11
  CheckCircle2,
11
12
  ChevronDown,
12
13
  CircleDashed,
@@ -51,6 +52,7 @@ export function ServiceAppListItem({
51
52
  isDiscovering,
52
53
  onActionsOpenChange,
53
54
  onDiscover,
55
+ onManagePackage,
54
56
  onDelete,
55
57
  onResetDelete,
56
58
  onRestart,
@@ -67,6 +69,7 @@ export function ServiceAppListItem({
67
69
  isDiscovering: boolean;
68
70
  onActionsOpenChange: (open: boolean) => void;
69
71
  onDiscover: (appId: string) => void;
72
+ onManagePackage: (packageId: string) => void;
70
73
  onDelete: (appId: string, purgeData: boolean, onSuccess: () => void) => void;
71
74
  onResetDelete: () => void;
72
75
  onRestart: (appId: string) => void;
@@ -76,10 +79,9 @@ export function ServiceAppListItem({
76
79
  const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false);
77
80
  const [isDiagnosticsOpen, setIsDiagnosticsOpen] = useState(false);
78
81
  const [purgeData, setPurgeData] = useState(false);
79
- const canConnectAndDiscover =
80
- app.status !== "starting" && app.status !== "stopped";
81
- const canDisconnectRuntime =
82
- app.status === "running" || app.status === "failed";
82
+ const canConnectAndDiscover = app.status !== "starting" && app.status !== "stopped";
83
+ const canDisconnectRuntime = app.status === "running" || app.status === "failed";
84
+ const managedPackageId = app.sourceKind === "package" ? app.packageId : undefined;
83
85
  const diagnostics = getServiceAppDiagnostics(app);
84
86
  const openDeleteDialog = () => {
85
87
  setIsMenuOpen(false);
@@ -87,7 +89,6 @@ export function ServiceAppListItem({
87
89
  onResetDelete();
88
90
  setIsDeleteDialogOpen(true);
89
91
  };
90
-
91
92
  return (
92
93
  <TooltipProvider delayDuration={250}>
93
94
  <section className="group bg-card transition-colors hover:bg-muted/25">
@@ -168,13 +169,24 @@ export function ServiceAppListItem({
168
169
  onRestart(app.id);
169
170
  }}
170
171
  />
171
- <ServiceAppMenuItem
172
- destructive
173
- disabled={deletePending}
174
- icon={Trash2}
175
- label={t("serviceAppsDelete")}
176
- onClick={openDeleteDialog}
177
- />
172
+ {managedPackageId ? (
173
+ <ServiceAppMenuItem
174
+ icon={Boxes}
175
+ label={t("serviceAppsManagePackage")}
176
+ onClick={() => {
177
+ setIsMenuOpen(false);
178
+ onManagePackage(managedPackageId);
179
+ }}
180
+ />
181
+ ) : (
182
+ <ServiceAppMenuItem
183
+ destructive
184
+ disabled={deletePending}
185
+ icon={Trash2}
186
+ label={t("serviceAppsDelete")}
187
+ onClick={openDeleteDialog}
188
+ />
189
+ )}
178
190
  </PopoverContent>
179
191
  </Popover>
180
192
  </div>
@@ -245,6 +257,7 @@ export function ServiceAppListItem({
245
257
  ) : null}
246
258
  </div>
247
259
 
260
+ {app.sourceKind !== "package" ? (
248
261
  <ServiceAppDeleteDialog
249
262
  appId={app.id}
250
263
  appTitle={app.title}
@@ -258,6 +271,7 @@ export function ServiceAppListItem({
258
271
  onOpenChange={setIsDeleteDialogOpen}
259
272
  onPurgeDataChange={setPurgeData}
260
273
  />
274
+ ) : null}
261
275
  </section>
262
276
  </TooltipProvider>
263
277
  );
@@ -27,7 +27,11 @@ import { t } from "@/shared/lib/i18n";
27
27
 
28
28
  type ServiceActionView = ServiceActionListView["actions"][number];
29
29
 
30
- export function ServiceAppsPanel() {
30
+ export function ServiceAppsPanel({
31
+ onManagePackage,
32
+ }: {
33
+ onManagePackage: (packageId: string) => void;
34
+ }) {
31
35
  const serviceApps = useServiceApps();
32
36
  const appData = useAppData();
33
37
  const serviceActions = useServiceActions();
@@ -77,6 +81,7 @@ export function ServiceAppsPanel() {
77
81
  }
78
82
 
79
83
  const apps: ServiceAppRecordView[] = serviceApps.data?.entries ?? [];
84
+ const diagnostics = serviceApps.data?.diagnostics ?? [];
80
85
  const actions: ServiceActionView[] = serviceActions.data?.actions ?? [];
81
86
  const grants: ServiceActionGrantView[] =
82
87
  serviceActionGrants.data?.grants ?? [];
@@ -115,6 +120,22 @@ export function ServiceAppsPanel() {
115
120
  </TooltipProvider>
116
121
  </div>
117
122
 
123
+ {diagnostics.length > 0 ? (
124
+ <div
125
+ role="alert"
126
+ className="mx-3 mt-3 rounded-xl border border-destructive/20 bg-destructive/5 px-3 py-2.5 text-xs text-destructive sm:mx-4"
127
+ >
128
+ <div className="font-medium">{t("serviceAppsLoadFailed")}</div>
129
+ <ul className="mt-1 space-y-1">
130
+ {diagnostics.map((diagnostic) => (
131
+ <li key={diagnostic.stagedPath} className="break-words">
132
+ {diagnostic.appId}: {diagnostic.message}
133
+ </li>
134
+ ))}
135
+ </ul>
136
+ </div>
137
+ ) : null}
138
+
118
139
  {apps.length === 0 ? (
119
140
  <div className="flex flex-1 items-center justify-center px-6 py-8 text-center">
120
141
  <div className="w-full max-w-xs">
@@ -154,6 +175,7 @@ export function ServiceAppsPanel() {
154
175
  }))
155
176
  }
156
177
  onDiscover={discover}
178
+ onManagePackage={onManagePackage}
157
179
  onDelete={(appId, purgeData, onSuccess) => void deleteServiceApp.mutate(
158
180
  { appId, purgeData },
159
181
  { onSuccess },
@@ -0,0 +1,78 @@
1
+ import { fireEvent, render, screen } from "@testing-library/react";
2
+ import { describe, expect, it, vi } from "vitest";
3
+ import { DocBrowserTabStrip } from "@/shared/components/doc-browser/doc-browser-tab-strip";
4
+ import type { DocBrowserTab } from "@/shared/components/doc-browser/doc-browser-context";
5
+
6
+ const docsTab: DocBrowserTab = {
7
+ id: "docs",
8
+ kind: "docs",
9
+ title: "Docs",
10
+ currentUrl: "nextclaw://docs",
11
+ history: ["nextclaw://docs"],
12
+ historyIndex: 0,
13
+ navVersion: 0,
14
+ };
15
+
16
+ function renderTabStrip(tabs: DocBrowserTab[]) {
17
+ return render(
18
+ <DocBrowserTabStrip
19
+ tabs={tabs}
20
+ activeTabId={tabs[0]?.id ?? ""}
21
+ canGoBack={false}
22
+ canGoForward={false}
23
+ isDocked={true}
24
+ isFullscreen={false}
25
+ onGoBack={vi.fn()}
26
+ onGoForward={vi.fn()}
27
+ onOpenNewTab={vi.fn()}
28
+ onSetActiveTab={vi.fn()}
29
+ onCloseTab={vi.fn()}
30
+ onClose={vi.fn()}
31
+ onDragStart={vi.fn()}
32
+ onToggleMode={vi.fn()}
33
+ />,
34
+ );
35
+ }
36
+
37
+ describe("DocBrowserTabStrip", () => {
38
+ it("keeps content-sized tabs and renders each tab's own icon", () => {
39
+ renderTabStrip([
40
+ docsTab,
41
+ {
42
+ ...docsTab,
43
+ id: "marketplace",
44
+ kind: "content",
45
+ title: "Marketplace",
46
+ currentUrl: "nextclaw://marketplace",
47
+ dockIcon: { type: "url", url: "/marketplace-icon.png" },
48
+ },
49
+ ]);
50
+
51
+ expect(
52
+ screen.getByRole("button", { name: "Docs" })
53
+ .querySelector(".lucide-book-open"),
54
+ ).toBeTruthy();
55
+ expect(
56
+ screen.getByRole("button", { name: "Marketplace" })
57
+ .querySelector('img[src="/marketplace-icon.png"]'),
58
+ ).toBeTruthy();
59
+
60
+ const marketplaceTab = screen.getByRole("button", { name: "Marketplace" })
61
+ .closest("[data-compact-tab-item]");
62
+ expect(marketplaceTab?.className).toContain("max-w-[220px]");
63
+ expect(marketplaceTab?.className).not.toContain("min-w-[");
64
+
65
+ fireEvent.error(
66
+ screen.getByRole("button", { name: "Marketplace" })
67
+ .querySelector('img[src="/marketplace-icon.png"]')!,
68
+ );
69
+ expect(
70
+ screen.getByRole("button", { name: "Marketplace" })
71
+ .querySelector(".lucide-app-window"),
72
+ ).toBeTruthy();
73
+ expect(
74
+ screen.getByRole("button", { name: "Marketplace" })
75
+ .querySelector("img"),
76
+ ).toBeNull();
77
+ });
78
+ });
@@ -1,13 +1,18 @@
1
- import type { PointerEvent } from "react";
1
+ import { useState, type PointerEvent } from "react";
2
2
  import {
3
+ AppWindow,
3
4
  ArrowLeft,
4
5
  ArrowRight,
6
+ BookOpen,
7
+ Boxes,
8
+ Github,
5
9
  PanelRightOpen,
6
10
  PictureInPicture2,
7
11
  Pin,
8
12
  PinOff,
9
13
  Plus,
10
14
  X,
15
+ type LucideIcon,
11
16
  } from "lucide-react";
12
17
  import type { DocBrowserDockState, DocBrowserTab } from "./doc-browser-context";
13
18
  import type { ContextMenuGroup } from "@/shared/components/ui/context-menu/context-menu";
@@ -39,6 +44,68 @@ type DocBrowserTabStripProps = {
39
44
  getTabMenuGroups?: (tab: DocBrowserTab) => readonly ContextMenuGroup[] | undefined;
40
45
  };
41
46
 
47
+ const DOC_BROWSER_BUILTIN_TAB_ICONS: Record<string, LucideIcon> = {
48
+ apps: Boxes,
49
+ docs: BookOpen,
50
+ github: Github,
51
+ "new-tab": Plus,
52
+ "panel-app": AppWindow,
53
+ "service-apps": AppWindow,
54
+ };
55
+
56
+ function DocBrowserUrlTabIcon({
57
+ fallbackIcon: FallbackIcon,
58
+ url,
59
+ }: {
60
+ fallbackIcon: LucideIcon;
61
+ url: string;
62
+ }) {
63
+ const [loadFailed, setLoadFailed] = useState(false);
64
+ return loadFailed ? (
65
+ <FallbackIcon className="h-3.5 w-3.5 text-muted-foreground" aria-hidden="true" />
66
+ ) : (
67
+ <img
68
+ src={url}
69
+ alt=""
70
+ className="h-3.5 w-3.5 rounded-sm object-cover"
71
+ onError={() => setLoadFailed(true)}
72
+ />
73
+ );
74
+ }
75
+
76
+ function DocBrowserTabIcon({ tab }: { tab: DocBrowserTab }) {
77
+ const { dockIcon } = tab;
78
+ const fallbackIcon = tab.kind === "docs"
79
+ ? BookOpen
80
+ : tab.kind === "home"
81
+ ? Plus
82
+ : tab.kind === "apps"
83
+ ? Boxes
84
+ : AppWindow;
85
+ const Icon = dockIcon?.type === "builtin"
86
+ ? (DOC_BROWSER_BUILTIN_TAB_ICONS[dockIcon.name] ?? fallbackIcon)
87
+ : fallbackIcon;
88
+
89
+ if (dockIcon?.type === "url") {
90
+ return (
91
+ <DocBrowserUrlTabIcon
92
+ key={dockIcon.url}
93
+ fallbackIcon={fallbackIcon}
94
+ url={dockIcon.url}
95
+ />
96
+ );
97
+ }
98
+ if (dockIcon?.type === "text") {
99
+ return (
100
+ <span className="max-w-3.5 truncate text-[10px] font-semibold leading-none" aria-hidden="true">
101
+ {dockIcon.value}
102
+ </span>
103
+ );
104
+ }
105
+
106
+ return <Icon className="h-3.5 w-3.5 text-muted-foreground" aria-hidden="true" />;
107
+ }
108
+
42
109
  function shouldBlockHeaderDrag(target: EventTarget | null): boolean {
43
110
  return target instanceof Element && Boolean(
44
111
  target.closest("button, a, input, textarea, select, [data-compact-tab-item]"),
@@ -80,8 +147,9 @@ export function DocBrowserTabStrip({
80
147
  label: tab.title || t("docBrowserTabUntitled"),
81
148
  active: tab.id === activeTabId,
82
149
  tooltip: tab.title,
150
+ leadingIcon: <DocBrowserTabIcon tab={tab} />,
83
151
  closeLabel: closeTabLabel,
84
- closePlacement: "trailing",
152
+ closePlacement: "leading-hover",
85
153
  onSelect: () => onSetActiveTab(tab.id),
86
154
  onClose: () => onCloseTab(tab.id),
87
155
  menuLabel: t("docBrowserTabMoreActions"),
@@ -132,7 +200,7 @@ export function DocBrowserTabStrip({
132
200
  tabsClassName="items-center gap-0.5"
133
201
  actionsClassName="h-full items-center gap-0.5"
134
202
  actionButtonClassName="rounded-md p-1.5 text-muted-foreground hover:bg-muted/60 hover:text-foreground disabled:opacity-50"
135
- tabBaseClassName="group inline-flex min-w-0 cursor-pointer items-center gap-0.5 h-7 px-2 rounded-md text-xs max-w-[220px] shrink-0 transition-colors"
203
+ tabBaseClassName="inline-flex cursor-pointer items-center gap-0.5 h-7 px-2 rounded-md text-xs max-w-[220px] shrink-0 transition-colors"
136
204
  activeTabClassName="bg-muted/80 text-foreground"
137
205
  inactiveTabClassName="text-muted-foreground hover:bg-muted/45 hover:text-foreground"
138
206
  labelClassName="px-0.5 text-xs font-normal"
@@ -113,7 +113,7 @@ describe("CompactTabStrip", () => {
113
113
  active: false,
114
114
  closeLabel: "Close preview",
115
115
  closePlacement: "leading-hover",
116
- leadingIcon: <span />,
116
+ leadingIcon: <span data-testid="preview-tab-icon" />,
117
117
  onClose,
118
118
  onSelect,
119
119
  },
@@ -126,6 +126,83 @@ describe("CompactTabStrip", () => {
126
126
 
127
127
  expect(onClose).toHaveBeenCalledTimes(1);
128
128
  expect(onSelect).not.toHaveBeenCalled();
129
+ expect(
130
+ screen.getByTestId("preview-tab-icon").parentElement?.className,
131
+ ).toContain("group-hover:opacity-0");
132
+ });
133
+
134
+ it("overlays the close action at the start without changing the tab width on hover", () => {
135
+ render(
136
+ <CompactTabStrip
137
+ tabs={[
138
+ {
139
+ key: "marketplace",
140
+ label: "NextClaw Marketplace",
141
+ active: true,
142
+ closeLabel: "Close tab",
143
+ closePlacement: "leading-hover",
144
+ menuLabel: "More tab actions",
145
+ menuGroups: [{ key: "tab", items: [{ key: "close", label: "Close", onSelect: vi.fn() }] }],
146
+ onClose: vi.fn(),
147
+ onSelect: vi.fn(),
148
+ },
149
+ ]}
150
+ actions={[]}
151
+ />,
152
+ );
153
+
154
+ const tabItem = screen.getByRole("button", { name: "NextClaw Marketplace" })
155
+ .closest("[data-compact-tab-item]");
156
+ const leadingActions = tabItem?.querySelector(
157
+ '[data-compact-tab-leading-actions=""]',
158
+ );
159
+ const trailingActions = tabItem?.querySelector(
160
+ '[data-compact-tab-trailing-actions=""]',
161
+ );
162
+ const trailingSpacer = tabItem?.querySelector(
163
+ '[data-compact-tab-trailing-spacer=""]',
164
+ );
165
+ const leadingSlot = tabItem?.querySelector(
166
+ '[data-compact-tab-leading-slot=""]',
167
+ );
168
+
169
+ expect(tabItem?.className).toContain("relative");
170
+ expect(tabItem?.className).toContain("max-w-[180px]");
171
+ expect(tabItem?.className).not.toContain("min-w-[");
172
+ expect(leadingSlot?.className).toContain("w-3.5");
173
+ expect(leadingSlot?.className).toContain("shrink-0");
174
+ expect(leadingActions?.className).toContain("absolute");
175
+ expect(leadingActions?.className).toContain("left-1");
176
+ expect(leadingActions?.className).toContain("opacity-0");
177
+ expect(leadingActions?.className).toContain("pointer-events-none");
178
+ expect(leadingActions?.className).not.toContain("bg-inherit");
179
+ expect(leadingActions?.className).toContain("group-hover:opacity-100");
180
+ expect(leadingActions?.className).toContain("group-focus-within:opacity-100");
181
+ expect(trailingActions?.className).toContain("absolute");
182
+ expect(trailingActions?.className).toContain("opacity-0");
183
+ expect(trailingActions?.className).toContain("pointer-events-none");
184
+ expect(trailingActions?.className).not.toContain("bg-inherit");
185
+ expect(trailingActions?.className).toContain("group-hover:opacity-100");
186
+ expect(trailingActions?.className).toContain("group-focus-within:opacity-100");
187
+ expect(trailingActions?.className).not.toContain("w-0");
188
+ expect(trailingActions?.className).not.toContain("transition-[width");
189
+ expect(trailingSpacer?.className).toContain("w-6");
190
+ expect(trailingSpacer?.className).toContain("shrink-0");
191
+ const closeButton = leadingActions?.querySelector<HTMLButtonElement>(
192
+ '[aria-label="Close tab"]',
193
+ );
194
+ const menuButton = trailingActions?.querySelector<HTMLButtonElement>(
195
+ '[aria-label="More tab actions"]',
196
+ );
197
+ expect(closeButton).toBeTruthy();
198
+ expect(menuButton).toBeTruthy();
199
+ expect(closeButton?.className).toBe(menuButton?.className);
200
+ expect(closeButton?.className).toContain("h-6");
201
+ expect(closeButton?.className).toContain("w-6");
202
+ expect(closeButton?.className).toContain("rounded-md");
203
+ expect(
204
+ trailingActions?.querySelector('[aria-label="Close tab"]'),
205
+ ).toBeNull();
129
206
  });
130
207
 
131
208
  it("opens a tab action menu without selecting the tab or restoring stale focus after an action", async () => {
@@ -1,6 +1,9 @@
1
1
  import * as React from "react";
2
2
  import { MoreVertical, X } from "lucide-react";
3
- import { IconActionButton } from "@/shared/components/ui/actions/icon-action-button";
3
+ import {
4
+ IconActionButton,
5
+ type IconActionButtonProps,
6
+ } from "@/shared/components/ui/actions/icon-action-button";
4
7
  import {
5
8
  ContextMenu,
6
9
  ContextMenuTrigger,
@@ -63,28 +66,94 @@ function closeCompactTab(event: React.MouseEvent<HTMLButtonElement>, onClose: ()
63
66
  onClose();
64
67
  }
65
68
 
69
+ function CompactTabActionButton(props: IconActionButtonProps) {
70
+ return <IconActionButton {...props} size="sm" tone="strong" />;
71
+ }
72
+
66
73
  function CompactTabMenu({ tab }: { tab: CompactTabStripTab }) {
67
74
  if (!tab.menuGroups?.some((group) => group.items.length > 0)) {
68
75
  return null;
69
76
  }
70
77
 
71
78
  const menuLabel = tab.menuLabel ?? tab.label;
79
+ return (
80
+ <div className="flex shrink-0">
81
+ <ContextMenuTrigger>
82
+ <CompactTabActionButton
83
+ icon={<MoreVertical className="h-3.5 w-3.5" />}
84
+ label={menuLabel}
85
+ />
86
+ </ContextMenuTrigger>
87
+ </div>
88
+ );
89
+ }
90
+
91
+ function CompactTabTrailingActions({ tab }: { tab: CompactTabStripTab }) {
92
+ const hasMenu = tab.menuGroups?.some((group) => group.items.length > 0) ?? false;
93
+ const hasTrailingClose = Boolean(
94
+ tab.onClose && tab.closePlacement !== "leading-hover",
95
+ );
96
+ const close = hasTrailingClose ? (
97
+ <CompactTabActionButton
98
+ icon={<X className="h-3.5 w-3.5" />}
99
+ label={tab.closeLabel ?? ""}
100
+ tooltip={tab.closeLabel ?? null}
101
+ onClick={(event) => closeCompactTab(event, tab.onClose!)}
102
+ />
103
+ ) : null;
104
+
105
+ if (!hasMenu && !hasTrailingClose) {
106
+ return null;
107
+ }
108
+
109
+ return (
110
+ <>
111
+ <span
112
+ aria-hidden="true"
113
+ data-compact-tab-trailing-spacer=""
114
+ className={cn(
115
+ "shrink-0",
116
+ hasMenu && hasTrailingClose
117
+ ? "w-[50px]"
118
+ : hasMenu
119
+ ? "w-6"
120
+ : "w-6",
121
+ )}
122
+ />
123
+ <div
124
+ data-compact-tab-trailing-actions=""
125
+ className={cn(
126
+ "pointer-events-none absolute inset-y-0 right-1 z-10 flex items-center gap-0.5 opacity-0",
127
+ "group-hover:pointer-events-auto group-hover:opacity-100 group-focus-within:pointer-events-auto group-focus-within:opacity-100",
128
+ "group-data-[context-menu-open]:pointer-events-auto group-data-[context-menu-open]:opacity-100",
129
+ )}
130
+ >
131
+ {hasMenu ? <CompactTabMenu tab={tab} /> : null}
132
+ {close}
133
+ </div>
134
+ </>
135
+ );
136
+ }
137
+
138
+ function CompactTabLeadingCloseOverlay({ tab }: { tab: CompactTabStripTab }) {
139
+ if (!tab.onClose || tab.closePlacement !== "leading-hover") {
140
+ return null;
141
+ }
142
+
72
143
  return (
73
144
  <div
145
+ data-compact-tab-leading-actions=""
74
146
  className={cn(
75
- "-ml-1.5 flex w-0 shrink-0 overflow-hidden opacity-0 transition-[width,margin,opacity] group-hover:ml-0 group-hover:w-5 group-hover:opacity-100 group-focus-within:ml-0 group-focus-within:w-5 group-focus-within:opacity-100",
76
- "group-data-[context-menu-open]:ml-0 group-data-[context-menu-open]:w-5 group-data-[context-menu-open]:opacity-100",
147
+ "pointer-events-none absolute inset-y-0 left-1 z-10 flex items-center opacity-0",
148
+ "group-hover:pointer-events-auto group-hover:opacity-100 group-focus-within:pointer-events-auto group-focus-within:opacity-100",
77
149
  )}
78
150
  >
79
- <ContextMenuTrigger>
80
- <button
81
- type="button"
82
- aria-label={menuLabel}
83
- className="inline-flex h-5 w-5 shrink-0 items-center justify-center rounded-md text-gray-400 transition-colors hover:bg-gray-200 hover:text-gray-700 focus-visible:bg-gray-200 focus-visible:text-gray-700 focus-visible:outline-none"
84
- >
85
- <MoreVertical className="h-3.5 w-3.5" />
86
- </button>
87
- </ContextMenuTrigger>
151
+ <CompactTabActionButton
152
+ icon={<X className="h-3.5 w-3.5" />}
153
+ label={tab.closeLabel ?? ""}
154
+ tooltip={tab.closeLabel ?? null}
155
+ onClick={(event) => closeCompactTab(event, tab.onClose!)}
156
+ />
88
157
  </div>
89
158
  );
90
159
  }
@@ -96,9 +165,9 @@ function CompactTabLabelButton({
96
165
  labelClassName?: string;
97
166
  tab: CompactTabStripTab;
98
167
  }) {
99
- const leadingIcon = tab.onClose && tab.closePlacement === "leading-hover"
100
- ? null
101
- : tab.leadingIcon;
168
+ const hasLeadingClose = Boolean(
169
+ tab.onClose && tab.closePlacement === "leading-hover",
170
+ );
102
171
  const button = (
103
172
  <button
104
173
  type="button"
@@ -106,9 +175,15 @@ function CompactTabLabelButton({
106
175
  aria-label={tab.label}
107
176
  className="flex min-w-0 flex-1 items-center gap-1.5 text-left"
108
177
  >
109
- {leadingIcon ? (
110
- <span className="inline-flex shrink-0 items-center justify-center">
111
- {leadingIcon}
178
+ {tab.leadingIcon || hasLeadingClose ? (
179
+ <span
180
+ data-compact-tab-leading-slot=""
181
+ className={cn(
182
+ "inline-flex h-3.5 w-3.5 shrink-0 items-center justify-center",
183
+ hasLeadingClose && "group-hover:opacity-0 group-focus-within:opacity-0",
184
+ )}
185
+ >
186
+ {tab.leadingIcon}
112
187
  </span>
113
188
  ) : null}
114
189
  <span className={cn("min-w-0 truncate text-[12px] font-medium", labelClassName, tab.labelClassName)}>
@@ -150,7 +225,6 @@ function CompactTabItem({
150
225
  | "labelClassName"
151
226
  | "tabBaseClassName"
152
227
  > & { itemRef?: React.Ref<HTMLDivElement>; tab: CompactTabStripTab }) {
153
- const leadingClose = tab.onClose && tab.closePlacement === "leading-hover";
154
228
  const item = (
155
229
  <div
156
230
  ref={itemRef}
@@ -158,38 +232,17 @@ function CompactTabItem({
158
232
  onClick={(event) => event.target === event.currentTarget && tab.onSelect()}
159
233
  className={cn(
160
234
  tabBaseClassName ??
161
- "group flex max-w-[180px] min-w-0 cursor-pointer items-center gap-1.5 border-r border-gray-200/70 border-b-2 px-2.5 py-2 transition-colors",
235
+ "flex max-w-[180px] cursor-pointer items-center gap-1.5 border-r border-gray-200/70 border-b-2 px-2.5 py-2 transition-colors",
236
+ "group relative",
162
237
  tab.active
163
238
  ? (activeTabClassName ?? "border-b-primary bg-white text-gray-900")
164
239
  : (inactiveTabClassName ??
165
240
  "border-b-transparent bg-gray-50/85 text-gray-500 hover:bg-gray-100"),
166
241
  )}
167
242
  >
168
- {leadingClose ? (
169
- <button
170
- type="button"
171
- onClick={(event) => closeCompactTab(event, tab.onClose!)}
172
- className="relative inline-flex h-5 w-5 shrink-0 items-center justify-center rounded-md text-gray-400 transition-colors hover:bg-gray-200 hover:text-gray-700"
173
- aria-label={tab.closeLabel}
174
- >
175
- <span className="flex items-center justify-center group-hover:hidden">
176
- {tab.leadingIcon}
177
- </span>
178
- <X className="hidden h-3.5 w-3.5 group-hover:block" />
179
- </button>
180
- ) : null}
181
243
  <CompactTabLabelButton tab={tab} labelClassName={labelClassName} />
182
- <CompactTabMenu tab={tab} />
183
- {tab.onClose && tab.closePlacement !== "leading-hover" ? (
184
- <IconActionButton
185
- size="sm"
186
- tone="strong"
187
- icon={<X className="h-3 w-3" />}
188
- label={tab.closeLabel ?? ""}
189
- tooltip={tab.closeLabel ?? null}
190
- onClick={(event) => closeCompactTab(event, tab.onClose!)}
191
- />
192
- ) : null}
244
+ <CompactTabLeadingCloseOverlay tab={tab} />
245
+ <CompactTabTrailingActions tab={tab} />
193
246
  </div>
194
247
  );
195
248
 
@@ -183,6 +183,7 @@ describe('ncp-session-query-cache', () => {
183
183
  it('applies realtime upsert/delete events to every ncp-sessions query cache entry', () => {
184
184
  const queryClient = new QueryClient();
185
185
  queryClient.setQueryData(['ncp-sessions', 200], createSessionsList());
186
+ queryClient.setQueryData(['ncp-session-token-usage', 'session-3'], { runCount: 1 });
186
187
 
187
188
  applyNcpSessionRealtimeEvent(queryClient, {
188
189
  type: 'session.run-status',
@@ -212,6 +213,7 @@ describe('ncp-session-query-cache', () => {
212
213
  expect(
213
214
  queryClient.getQueryData<NcpSessionsListView>(['ncp-sessions', 200])?.sessions.map((session) => session.sessionId)
214
215
  ).toEqual(['session-3', 'session-1', 'session-2']);
216
+ expect(queryClient.getQueryState(['ncp-session-token-usage', 'session-3'])?.isInvalidated).toBe(true);
215
217
 
216
218
  applyNcpSessionRealtimeEvent(queryClient, {
217
219
  type: 'session.summary.delete',
@@ -155,6 +155,10 @@ export function applyNcpSessionRealtimeEvent(
155
155
  }
156
156
  if (event.type === 'session.summary.upsert') {
157
157
  upsertNcpSessionSummaryInQueryClient(queryClient, event.payload.summary);
158
+ void queryClient?.invalidateQueries({
159
+ queryKey: ['ncp-session-token-usage', event.payload.summary.sessionId],
160
+ exact: true,
161
+ });
158
162
  return;
159
163
  }
160
164
 
@@ -4,6 +4,7 @@ import type {
4
4
  NcpSessionStatus,
5
5
  NcpSessionSummary,
6
6
  } from "@nextclaw/ncp";
7
+ import type { UiNcpSessionTokenUsageView } from "@nextclaw/client-sdk";
7
8
  import type { ThinkingLevel } from "./types";
8
9
 
9
10
  export type SessionTypeIconView = {
@@ -114,3 +115,5 @@ export type NcpSessionMessagesView = {
114
115
  total: number;
115
116
  pageInfo: NcpSessionMessagePageInfo;
116
117
  };
118
+
119
+ export type NcpSessionTokenUsageView = UiNcpSessionTokenUsageView;
@@ -24,6 +24,7 @@ export type {
24
24
  NcpSessionsListView,
25
25
  NcpMessageView,
26
26
  NcpSessionMessagesView,
27
+ NcpSessionTokenUsageView,
27
28
  SessionContextWindowView,
28
29
  } from './ncp-session.types';
29
30