@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.
- package/CHANGELOG.md +28 -0
- package/dist/assets/{abnfDiagram-VRR7QNED-BcFJp_1X.js → abnfDiagram-VRR7QNED-DvTjgVMf.js} +1 -1
- package/dist/assets/{aiden0z-pptx-renderer.es-Dp2ezlQn.js → aiden0z-pptx-renderer.es-C1nfBAXR.js} +1 -1
- package/dist/assets/{api-CVjdSvJk.js → api-CuPWhNDM.js} +3 -3
- package/dist/assets/{appearance-settings-page-BOorfaUx.js → appearance-settings-page-DRjg6UP3.js} +1 -1
- package/dist/assets/{arc-CkgyPMyW.js → arc-BvhODBXv.js} +1 -1
- package/dist/assets/architecture-TIHT7OUA-Bjp9jf0h.js +1 -0
- package/dist/assets/{architectureDiagram-ZJ3FMSHR-CMd4cIjI.js → architectureDiagram-ZJ3FMSHR-BKO9RgNS.js} +1 -1
- package/dist/assets/{blockDiagram-677ZJIJ3-Bz3tbhet.js → blockDiagram-677ZJIJ3-DHpDXy7D.js} +1 -1
- package/dist/assets/book-open-B4ovWX14.js +1 -0
- package/dist/assets/{c4Diagram-LMCZKHZV-D9hFNAGv.js → c4Diagram-LMCZKHZV-BG_iQnDr.js} +1 -1
- package/dist/assets/channel-5T3pdR-s.js +1 -0
- package/dist/assets/{channels-list-page-DuZJvYB6.js → channels-list-page-B26GXq63.js} +2 -2
- package/dist/assets/{chat-page-8PTMgI0t.js → chat-page-QA0dpNyU.js} +1 -1
- package/dist/assets/chunk-2Q5K7J3B-DTZ3tGfE.js +1 -0
- package/dist/assets/{chunk-32BRIVSS-7NrjEITq.js → chunk-32BRIVSS-DR9M0LLx.js} +1 -1
- package/dist/assets/{chunk-52WLFC77-viIcqj9q.js → chunk-52WLFC77-698iC3BB.js} +1 -1
- package/dist/assets/{chunk-5FCAYU7R-Vsu4J-VM.js → chunk-5FCAYU7R-DikTj9Ey.js} +1 -1
- package/dist/assets/{chunk-5HE753X5-C-w6CQZK.js → chunk-5HE753X5-ko0o7_q0.js} +1 -1
- package/dist/assets/{chunk-5JV3BV7I-CuKgJQ4W.js → chunk-5JV3BV7I-CEHufhgo.js} +1 -1
- package/dist/assets/{chunk-5TONJI2A-CYeiWZKb.js → chunk-5TONJI2A-CGgno1cu.js} +1 -1
- package/dist/assets/{chunk-5VM5RSS4-DRCbfwIn.js → chunk-5VM5RSS4-DkiB4cUQ.js} +1 -1
- package/dist/assets/{chunk-7BUUIJ7U-BvydOcm3.js → chunk-7BUUIJ7U-DzptcytQ.js} +1 -1
- package/dist/assets/{chunk-BIQX33UG-RlppOXKo.js → chunk-BIQX33UG-DMRSQJvT.js} +1 -1
- package/dist/assets/{chunk-C7G6YPKG-DAizzYwr.js → chunk-C7G6YPKG-Bw1BwwuY.js} +1 -1
- package/dist/assets/{chunk-CQNSW5MT-5eChJHca.js → chunk-CQNSW5MT-C0z6eHBk.js} +1 -1
- package/dist/assets/{chunk-CYSBUYHQ-YHPr53fv.js → chunk-CYSBUYHQ-ByPyAouO.js} +1 -1
- package/dist/assets/{chunk-EMLP6XTP-CK4D7Hz5.js → chunk-EMLP6XTP-CJlAXWZg.js} +1 -1
- package/dist/assets/{chunk-EX3LRPZG-BmlDFIRW.js → chunk-EX3LRPZG-Dqq4zKJu.js} +1 -1
- package/dist/assets/{chunk-FWX5IMBZ-D0ksHZZ9.js → chunk-FWX5IMBZ-B0SNoCmi.js} +2 -2
- package/dist/assets/{chunk-HOUHSVGY-DPkq8FCm.js → chunk-HOUHSVGY-DG_GJih_.js} +1 -1
- package/dist/assets/{chunk-ICXQ74PX-FAatv5fC.js → chunk-ICXQ74PX-Drxk_6bE.js} +1 -1
- package/dist/assets/{chunk-JG7HCLWE-DLkIi4F7.js → chunk-JG7HCLWE-DdLFPpnd.js} +1 -1
- package/dist/assets/{chunk-JWPE2WC7-DawOjNE3.js → chunk-JWPE2WC7-B8M4G76F.js} +1 -1
- package/dist/assets/{chunk-MOJQB5TN-Di52TorW.js → chunk-MOJQB5TN-XP6FJHY_.js} +1 -1
- package/dist/assets/{chunk-MOZMSUNE-PfyK9w1-.js → chunk-MOZMSUNE-C_6x18Qw.js} +1 -1
- package/dist/assets/{chunk-OGEWGWER-C3jdH3Ty.js → chunk-OGEWGWER-N25BzCmG.js} +1 -1
- package/dist/assets/{chunk-OSBZ3O6U-CecHo-f0.js → chunk-OSBZ3O6U-ASDEm7y0.js} +1 -1
- package/dist/assets/{chunk-PUDLZKDR-f3IJh3sN.js → chunk-PUDLZKDR-CTlvAcn7.js} +1 -1
- package/dist/assets/{chunk-Q4XR5HBZ-DQFeUPxZ.js → chunk-Q4XR5HBZ-Da9BWfB6.js} +1 -1
- package/dist/assets/{chunk-QBLGF6JB-BhUeG0y2.js → chunk-QBLGF6JB-Bi6RpTJO.js} +1 -1
- package/dist/assets/{chunk-R7FJI6CG-C3XpZ-2u.js → chunk-R7FJI6CG-DkMsSPvK.js} +1 -1
- package/dist/assets/{chunk-RYQCIY6F-CAqa5klQ.js → chunk-RYQCIY6F-DZp0eU-j.js} +1 -1
- package/dist/assets/{chunk-U6XO7XAA-D_WXezeD.js → chunk-U6XO7XAA-vfinDSrj.js} +1 -1
- package/dist/assets/{chunk-V7JOEXUC-CZ70oOtp.js → chunk-V7JOEXUC-C9Nksccs.js} +1 -1
- package/dist/assets/chunk-VAUOI2AC-HLGIHQ6k.js +1 -0
- package/dist/assets/{chunk-VR4S4FIN-B7AHEYYk.js → chunk-VR4S4FIN-Xv6zfwXs.js} +1 -1
- package/dist/assets/{chunk-WYO6CB5R-Dn9To_OI.js → chunk-WYO6CB5R-mOJNzbWl.js} +2 -2
- package/dist/assets/chunk-XXDRQBXY-Dl0m6Ni-.js +1 -0
- package/dist/assets/{chunk-YOTPTUD7-BYEgRBk3.js → chunk-YOTPTUD7-1-aKSu4o.js} +1 -1
- package/dist/assets/{chunk-ZGVPDNZ5-C6YtZDU0.js → chunk-ZGVPDNZ5-OJ8veMpK.js} +1 -1
- package/dist/assets/{chunk-ZIRB5QZD-DMuYtnHF.js → chunk-ZIRB5QZD-SR_Nq_PP.js} +1 -1
- package/dist/assets/classDiagram-OUVF2IWQ-DcDnzGNX.js +1 -0
- package/dist/assets/classDiagram-v2-EOCWNBFH-BKXKMzKM.js +1 -0
- package/dist/assets/{config-split-page-UuhsVcQD.js → config-split-page-Box_-aBK.js} +1 -1
- package/dist/assets/{confirm-dialog-Bnosa8UF.js → confirm-dialog-uqcCxomz.js} +1 -1
- package/dist/assets/{cose-bilkent-JH36ORCC-Bc2Ex5Y3.js → cose-bilkent-JH36ORCC-NjVgz2Bw.js} +1 -1
- package/dist/assets/cynefin-VYW2F7L2-BWg7U2UD.js +1 -0
- package/dist/assets/{cynefinDiagram-TSTJHNR4-DVysQoGz.js → cynefinDiagram-TSTJHNR4-B_17LDqD.js} +1 -1
- package/dist/assets/{dagre-CMhQ1cAG.js → dagre-CMe101gk.js} +1 -1
- package/dist/assets/{dagre-VKFMJZFB-RYyWpPJZ.js → dagre-VKFMJZFB-qo8nLb3B.js} +1 -1
- package/dist/assets/{desktop-update-config-CM4lsX4R.js → desktop-update-config-DkDor159.js} +1 -1
- package/dist/assets/{diagram-FQU43EPY-BD6KONdF.js → diagram-FQU43EPY-Ba3XIklC.js} +1 -1
- package/dist/assets/{diagram-G47NLZAW-CGB2ptRR.js → diagram-G47NLZAW-CG45TcUx.js} +1 -1
- package/dist/assets/{diagram-NH7WQ7WH-DnkOI6cU.js → diagram-NH7WQ7WH-2F2HrwV-.js} +1 -1
- package/dist/assets/{diagram-OA4YK3LP-DF82RZ7P.js → diagram-OA4YK3LP-CXfWH9NZ.js} +1 -1
- package/dist/assets/{diagram-WEI45ONY-Be2v7Ck9.js → diagram-WEI45ONY-CH_4yepc.js} +1 -1
- package/dist/assets/{dist-D17RZAsz.js → dist-CZLIZntJ.js} +1 -1
- package/dist/assets/{dist-_zZ8P38T.js → dist-Co2G-YjN.js} +1 -1
- package/dist/assets/doc-browser-CbCBDdap.js +1 -0
- package/dist/assets/doc-browser-DUqckL2E.js +1 -0
- package/dist/assets/{doc-browser-context-CerkiK_N.js → doc-browser-context-aeZu0FHR.js} +1 -1
- package/dist/assets/{docx-preview-BqwAjrj3.js → docx-preview-CoM4JK7_.js} +1 -1
- package/dist/assets/{ebnfDiagram-CCIWWBDH-CP98OYk8.js → ebnfDiagram-CCIWWBDH-BwiG4Ea1.js} +1 -1
- package/dist/assets/{erDiagram-Q63AITRT-ZLlx8xfs.js → erDiagram-Q63AITRT-BML3hpnQ.js} +1 -1
- package/dist/assets/eventmodeling-45OFAUF4-SB-J4tpq.js +1 -0
- package/dist/assets/flowDiagram-23GEKE2U-BQ_u-eLM.js +1 -0
- package/dist/assets/{form-actions-Cmv23dN4.js → form-actions-C3Pa1HQb.js} +1 -1
- package/dist/assets/{ganttDiagram-NO4QXBWP-DXCDjHR2.js → ganttDiagram-NO4QXBWP-80enfWbD.js} +1 -1
- package/dist/assets/gitGraph-TEB2WS4Q-DPxNwsFd.js +1 -0
- package/dist/assets/{gitGraphDiagram-IHSO6WYX-l2HWrTJp.js → gitGraphDiagram-IHSO6WYX-CVLqXr-b.js} +1 -1
- package/dist/assets/index-Bsoscjk7.css +1 -0
- package/dist/assets/index-CCWgeWoR.js +109 -0
- package/dist/assets/info-DKCQHKI2-DNlNGH90.js +1 -0
- package/dist/assets/{infoDiagram-FWYZ7A6U-D-DjX-bn.js → infoDiagram-FWYZ7A6U-Dyvs2t0i.js} +1 -1
- package/dist/assets/{ishikawaDiagram-FXEZZL3T-y_Xy128F.js → ishikawaDiagram-FXEZZL3T-sSnxDKfh.js} +1 -1
- package/dist/assets/{journeyDiagram-5HDEW3XC-DJn5xYcQ.js → journeyDiagram-5HDEW3XC-D22V71Mf.js} +1 -1
- package/dist/assets/{kanban-definition-HUTT4EX6-DJVIOR21.js → kanban-definition-HUTT4EX6-D3ds0p-g.js} +1 -1
- package/dist/assets/{line-Cos7PIOj.js → line-CbyFs065.js} +1 -1
- package/dist/assets/{linear-BtDLqtzF.js → linear-4DKtnZBS.js} +1 -1
- package/dist/assets/loader-circle-DfbWUPdI.js +1 -0
- package/dist/assets/{map-BKnQyvd0.js → map-CcZzEoz6.js} +1 -1
- package/dist/assets/mcp-marketplace-page-B2D7ciJL.js +1 -0
- package/dist/assets/mcp-marketplace-page-BmqbIQQQ.js +9 -0
- package/dist/assets/{mermaid-parser.core-XUNZhr2W.js → mermaid-parser.core-DPmMOjXD.js} +2 -2
- package/dist/assets/{mermaid.core-ySrvolIe.js → mermaid.core-BSf-ddRa.js} +3 -3
- package/dist/assets/{mindmap-definition-LN4V7U3C-BypL9tei.js → mindmap-definition-LN4V7U3C-BUTwWPyx.js} +1 -1
- package/dist/assets/model-config-page-BXUoZEEZ.js +1 -0
- package/dist/assets/{navigation-link-k9rsmtC4.js → navigation-link-DngetDmN.js} +1 -1
- package/dist/assets/{ordinal-CxxVWxYU.js → ordinal-BgLYtOZE.js} +1 -1
- package/dist/assets/packet-7NZHBO7P-BpeiqkBZ.js +1 -0
- package/dist/assets/{pegDiagram-2B236MQR-D-U2KBGv.js → pegDiagram-2B236MQR-BEu_ZBjc.js} +1 -1
- package/dist/assets/pie-RZYD4A2V-Cy1yxsPd.js +1 -0
- package/dist/assets/{pieDiagram-ENE6RG2P-CBi3GusJ.js → pieDiagram-ENE6RG2P-CAwAwuBd.js} +1 -1
- package/dist/assets/plus-wNpOxBfP.js +1 -0
- package/dist/assets/{provider-scoped-model-input-DLSAB6a5.js → provider-scoped-model-input-C4a2TuV7.js} +1 -1
- package/dist/assets/{providers-config-page-DM5N0aeS.js → providers-config-page-CHSka1kQ.js} +1 -1
- package/dist/assets/{quadrantDiagram-ABIIQ3AL-H0ZuvnZL.js → quadrantDiagram-ABIIQ3AL-CwE6F-_2.js} +1 -1
- package/dist/assets/radar-I7S5WNFK-CEcPKCj8.js +1 -0
- package/dist/assets/railroad-3IZDKUUU-CW38eNDs.js +1 -0
- package/dist/assets/railroad-abnf-AHOZXSZD-ByrylcXf.js +1 -0
- package/dist/assets/railroad-ebnf-EBAXGLYW-CNB6GwP-.js +1 -0
- package/dist/assets/railroad-peg-LSFZ7HO6-x6hnqlBG.js +1 -0
- package/dist/assets/{railroadDiagram-RFXS5EU6-D7Y2EqSd.js → railroadDiagram-RFXS5EU6-xgLekU3B.js} +1 -1
- package/dist/assets/{react-BgRLh3ee.js → react-CPH0zySO.js} +1 -1
- package/dist/assets/{refresh-cw-axvJLmRG.js → refresh-cw-vTYZqC0b.js} +1 -1
- package/dist/assets/remote-DgfRmokf.js +1 -0
- package/dist/assets/{requirementDiagram-TGXJPOKE-BiXNX1NN.js → requirementDiagram-TGXJPOKE-CyVUzEX5.js} +1 -1
- package/dist/assets/rotate-cw-L_seGUwq.js +1 -0
- package/dist/assets/runtime-config-page-V75_zqXa.js +1 -0
- package/dist/assets/{sankeyDiagram-HTMAVEWB-qzDeLztW.js → sankeyDiagram-HTMAVEWB-JK6bFmYB.js} +1 -1
- package/dist/assets/{save-DbR4SGk8.js → save-lWb6TI69.js} +1 -1
- package/dist/assets/{search-M9lrb8id.js → search-BuUX_0KY.js} +1 -1
- package/dist/assets/{search-config-page-DmsID8FQ.js → search-config-page-rlkRJZCF.js} +1 -1
- package/dist/assets/{secrets-config-page-C10OwI0b.js → secrets-config-page-BxCEc8r5.js} +2 -2
- package/dist/assets/security-config-Cqxe13za.js +1 -0
- package/dist/assets/{select-D_YBzMdv.js → select-oalsltZV.js} +1 -1
- package/dist/assets/{sequenceDiagram-DBY2YBRQ-BtRbPQtL.js → sequenceDiagram-DBY2YBRQ-BR5IokXV.js} +1 -1
- package/dist/assets/{settings-2-zy40UwTo.js → settings-2-Bd9xiNAY.js} +1 -1
- package/dist/assets/{sizeCapture-X5ZJPWSS-CMYvM3SL.js → sizeCapture-X5ZJPWSS-SxG_YnMA.js} +1 -1
- package/dist/assets/skeleton-D72EdBNa.js +1 -0
- package/dist/assets/{src-Bw-mzbQb.js → src-vgz8E_-O.js} +1 -1
- package/dist/assets/{stateDiagram-2N3HPSRC-BJ-mFeOz.js → stateDiagram-2N3HPSRC-DD4OoYhn.js} +1 -1
- package/dist/assets/stateDiagram-v2-6OUMAXLB-ZC6dFORv.js +1 -0
- package/dist/assets/{swimlanes-5IMT3BWC-BsucVOO2.js → swimlanes-5IMT3BWC-BWkFeyCT.js} +2 -2
- package/dist/assets/swimlanesDiagram-G3AALYLV-CRkh51Im.js +8 -0
- package/dist/assets/{tag-chip-D7QRdcph.js → tag-chip-B324d_Xk.js} +1 -1
- package/dist/assets/{timeline-definition-FHXFAJF6-Bicewlzo.js → timeline-definition-FHXFAJF6-VTRPy6KA.js} +1 -1
- package/dist/assets/treeView-QDETBFTQ-Bw0DSCH7.js +1 -0
- package/dist/assets/treemap-6X3UGDF4-DFWBFcfh.js +1 -0
- package/dist/assets/{vennDiagram-L72KCM5P-BxOOnO9x.js → vennDiagram-L72KCM5P-Bvk42Pek.js} +1 -1
- package/dist/assets/wardley-OPB4EBWU-CoWturk0.js +1 -0
- package/dist/assets/{wardleyDiagram-EHGQE667-Cl7NltMv.js → wardleyDiagram-EHGQE667-DOFjx2s3.js} +1 -1
- package/dist/assets/x-Cps-je1K.js +1 -0
- package/dist/assets/{xychartDiagram-FW5EYKEG-DiMIxQ7N.js → xychartDiagram-FW5EYKEG-DY5yRTUc.js} +1 -1
- package/dist/index.html +22 -21
- package/package.json +9 -9
- package/src/features/apps/components/__tests__/app-packages-panel.test.tsx +43 -10
- package/src/features/apps/components/__tests__/apps-panel.test.tsx +18 -2
- package/src/features/apps/components/app-package-card.tsx +58 -4
- package/src/features/apps/components/app-packages-panel.tsx +60 -22
- package/src/features/apps/components/apps-panel.tsx +14 -3
- package/src/features/apps/hooks/use-app-packages.ts +5 -2
- package/src/features/apps/managers/__tests__/app-package-operation-settlement.manager.test.ts +5 -0
- package/src/features/apps/managers/app-package-operation-settlement.manager.ts +8 -2
- package/src/features/chat/features/message/utils/__tests__/chat-message-show-content-tool-card.utils.test.ts +46 -0
- package/src/features/chat/features/message/utils/chat-message-part.utils.ts +2 -0
- package/src/features/chat/features/message/utils/chat-message-tool-card.utils.ts +52 -2
- package/src/features/chat/features/ncp/hooks/__tests__/use-hydrated-ncp-agent.test.tsx +97 -10
- package/src/features/chat/features/ncp/hooks/use-ncp-session-conversation.ts +4 -0
- package/src/features/chat/features/ncp/hooks/use-ncp-session-queries.ts +10 -0
- package/src/features/chat/features/session/utils/__tests__/ncp-session-adapter.utils.test.ts +27 -0
- package/src/features/chat/features/session/utils/ncp-session-adapter.utils.ts +2 -1
- package/src/features/chat/features/workspace/components/__tests__/chat-session-child-sessions.test.tsx +51 -0
- package/src/features/chat/features/workspace/components/__tests__/chat-session-token-usage.test.tsx +90 -0
- package/src/features/chat/features/workspace/components/__tests__/chat-session-workspace-panel-content.test.tsx +9 -17
- package/src/features/chat/features/workspace/components/chat-session-workspace-panel-content.tsx +4 -45
- package/src/features/chat/features/workspace/components/child-sessions/chat-session-child-sessions.tsx +66 -0
- package/src/features/chat/features/workspace/components/overview/chat-session-token-usage.tsx +171 -0
- package/src/features/chat/types/chat-message.types.ts +5 -0
- package/src/features/service-apps/components/__tests__/service-apps-panel.test.tsx +32 -4
- package/src/features/service-apps/components/service-app-list-item.tsx +26 -12
- package/src/features/service-apps/components/service-apps-panel.tsx +23 -1
- package/src/shared/components/doc-browser/__tests__/doc-browser-tab-strip.test.tsx +78 -0
- package/src/shared/components/doc-browser/doc-browser-tab-strip.tsx +71 -3
- package/src/shared/components/ui/tab-strip/__tests__/compact-tab-strip.test.tsx +78 -1
- package/src/shared/components/ui/tab-strip/compact-tab-strip.tsx +97 -44
- package/src/shared/lib/api/__tests__/ncp-session-query-cache.utils.test.ts +2 -0
- package/src/shared/lib/api/ncp-session-query-cache.utils.ts +4 -0
- package/src/shared/lib/api/ncp-session.types.ts +3 -0
- package/src/shared/lib/api/types.ts +1 -0
- package/src/shared/lib/api/utils/ncp-session.utils.ts +8 -0
- package/src/shared/lib/i18n/locales/en-US/chat.json +19 -0
- package/src/shared/lib/i18n/locales/en-US/doc-browser.json +3 -0
- package/src/shared/lib/i18n/locales/zh-CN/chat.json +20 -1
- package/src/shared/lib/i18n/locales/zh-CN/doc-browser.json +3 -0
- package/dist/assets/architecture-TIHT7OUA-DO2x53FK.js +0 -1
- package/dist/assets/channel-nBvMaVyP.js +0 -1
- package/dist/assets/chunk-2Q5K7J3B-CJgqFDS4.js +0 -1
- package/dist/assets/chunk-VAUOI2AC-BGvh3yIx.js +0 -1
- package/dist/assets/chunk-XXDRQBXY-DaBxG5gs.js +0 -1
- package/dist/assets/classDiagram-OUVF2IWQ-P6N03G2R.js +0 -1
- package/dist/assets/classDiagram-v2-EOCWNBFH-B1_uLvMv.js +0 -1
- package/dist/assets/cynefin-VYW2F7L2-B-H8Qp-n.js +0 -1
- package/dist/assets/doc-browser-CiLvybSC.js +0 -1
- package/dist/assets/doc-browser-z7vdH3Ez.js +0 -1
- package/dist/assets/eventmodeling-45OFAUF4-YVtShyFA.js +0 -1
- package/dist/assets/flowDiagram-23GEKE2U-BeKXabVO.js +0 -1
- package/dist/assets/gitGraph-TEB2WS4Q-jTSiBj-p.js +0 -1
- package/dist/assets/index-CWdgBpSc.css +0 -1
- package/dist/assets/index-DlL8uKXu.js +0 -109
- package/dist/assets/info-DKCQHKI2-BIporNWb.js +0 -1
- package/dist/assets/loader-circle-vUsFtTfb.js +0 -1
- package/dist/assets/mcp-marketplace-page-BV8WB3_L.js +0 -1
- package/dist/assets/mcp-marketplace-page-DN_fqcLd.js +0 -9
- package/dist/assets/model-config-page-BxaeCMbJ.js +0 -1
- package/dist/assets/packet-7NZHBO7P-CuFMADV0.js +0 -1
- package/dist/assets/pie-RZYD4A2V-XUeIXhM9.js +0 -1
- package/dist/assets/plus-Dc760kHj.js +0 -1
- package/dist/assets/radar-I7S5WNFK-D-NNGh1k.js +0 -1
- package/dist/assets/railroad-3IZDKUUU-C67_uDaM.js +0 -1
- package/dist/assets/railroad-abnf-AHOZXSZD-BZ8Yflnd.js +0 -1
- package/dist/assets/railroad-ebnf-EBAXGLYW-B7zO7RoK.js +0 -1
- package/dist/assets/railroad-peg-LSFZ7HO6-R63PTgLz.js +0 -1
- package/dist/assets/remote-Cj1GBMaN.js +0 -1
- package/dist/assets/rotate-cw-CFByP2O8.js +0 -1
- package/dist/assets/runtime-config-page-WNzZyjMW.js +0 -1
- package/dist/assets/security-config-CvU5JxDT.js +0 -1
- package/dist/assets/skeleton-BmAtzjDM.js +0 -1
- package/dist/assets/stateDiagram-v2-6OUMAXLB-BE-HqxOK.js +0 -1
- package/dist/assets/swimlanesDiagram-G3AALYLV-Dr1H9LJH.js +0 -8
- package/dist/assets/treeView-QDETBFTQ-npJ5dbqS.js +0 -1
- package/dist/assets/treemap-6X3UGDF4-pYrBx6oa.js +0 -1
- package/dist/assets/wardley-OPB4EBWU-CmSRKLR8.js +0 -1
- package/dist/assets/x-D9Y9hf_r.js +0 -1
- /package/dist/assets/{array-BQVzeGRt.js → array-NADKd9aU.js} +0 -0
- /package/dist/assets/{chunk-KEIR6QF5-f9psyCXK.js → chunk-KEIR6QF5-BuyuzJJL.js} +0 -0
- /package/dist/assets/{chunk-Y2CYZVJY-D1mAU_Po.js → chunk-Y2CYZVJY-D_B2ZRNt.js} +0 -0
- /package/dist/assets/{config-hints-nV5HPOvq.js → config-hints-BcfVUMij.js} +0 -0
- /package/dist/assets/{cytoscape.esm-0uDtdD0l.js → cytoscape.esm-BANTlgza.js} +0 -0
- /package/dist/assets/{defaultLocale-DSWXGgt0.js → defaultLocale-C4_L0aEs.js} +0 -0
- /package/dist/assets/{dist--ta5rX12.js → dist-DAPEIJbz.js} +0 -0
- /package/dist/assets/{graphlib-CQ6QV4yP.js → graphlib-ChqXg0dI.js} +0 -0
- /package/dist/assets/{host-capabilities-Tk038i04.js → host-capabilities-D9C4CBK5.js} +0 -0
- /package/dist/assets/{init-Y6h9jGtZ.js → init-D8wZg9xq.js} +0 -0
- /package/dist/assets/{jszip.min-8hyJpgpx.js → jszip.min-CkbBfwAQ.js} +0 -0
- /package/dist/assets/{katex-ARhY9PS1.js → katex-TZJFa0J_.js} +0 -0
- /package/dist/assets/{middleware-DXsbf9xI.js → middleware-B3k-TSmZ.js} +0 -0
- /package/dist/assets/{path-10BjdyLh.js → path-DodfxcX7.js} +0 -0
- /package/dist/assets/{rough.esm-DxgC_14r.js → rough.esm-CUaa1Rbe.js} +0 -0
- /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
|
-
|
|
81
|
-
const
|
|
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
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
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
|
|
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: "
|
|
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="
|
|
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 {
|
|
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
|
-
"-
|
|
76
|
-
"group-
|
|
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
|
-
<
|
|
80
|
-
<
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
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
|
|
100
|
-
|
|
101
|
-
|
|
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
|
|
111
|
-
|
|
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
|
-
"
|
|
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
|
-
<
|
|
183
|
-
{tab
|
|
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;
|