@nextclaw/ui 0.24.3 → 0.25.1
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 +39 -0
- package/dist/.vite/manifest.json +1309 -991
- package/dist/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
- package/dist/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
- package/dist/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
- package/dist/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
- package/dist/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
- package/dist/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
- package/dist/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
- package/dist/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
- package/dist/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
- package/dist/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
- package/dist/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
- package/dist/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
- package/dist/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
- package/dist/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
- package/dist/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
- package/dist/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
- package/dist/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
- package/dist/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
- package/dist/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
- package/dist/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
- package/dist/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
- package/dist/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
- package/dist/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
- package/dist/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
- package/dist/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
- package/dist/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
- package/dist/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
- package/dist/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
- package/dist/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
- package/dist/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
- package/dist/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
- package/dist/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
- package/dist/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
- package/dist/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
- package/dist/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
- package/dist/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
- package/dist/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
- package/dist/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
- package/dist/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
- package/dist/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
- package/dist/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
- package/dist/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
- package/dist/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
- package/dist/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
- package/dist/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
- package/dist/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
- package/dist/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
- package/dist/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
- package/dist/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
- package/dist/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
- package/dist/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
- package/dist/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
- package/dist/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
- package/dist/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
- package/dist/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
- package/dist/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
- package/dist/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
- package/dist/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
- package/dist/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
- package/dist/assets/{abnfDiagram-VRR7QNED-Kzlh3ZEC.js → abnfDiagram-VRR7QNED-DX_u5BBX.js} +1 -1
- package/dist/assets/{account-panel-BRTSwNvA.js → account-panel-QhOMbAQP.js} +1 -1
- package/dist/assets/{aiden0z-pptx-renderer.es-k1xZLzeo.js → aiden0z-pptx-renderer.es-C-E9e5yB.js} +1 -1
- package/dist/assets/{api-BnUzdltV.js → api-BWFOIoUm.js} +5 -5
- package/dist/assets/{app-data-Bjh_zM45.js → app-data-BnrEdmg2.js} +1 -1
- package/dist/assets/app-presenter-provider-CDRBc_lQ.js +115 -0
- package/dist/assets/app-presenter-provider-jNGlmBLe.css +1 -0
- package/dist/assets/{appearance-settings-page-BrOYSgG0.js → appearance-settings-page-C5z5sOkC.js} +1 -1
- package/dist/assets/{arc-CpivM836.js → arc-Cxwa624o.js} +1 -1
- package/dist/assets/architecture-TIHT7OUA-DEt0Abz9.js +1 -0
- package/dist/assets/{architectureDiagram-ZJ3FMSHR-ifjW1qjI.js → architectureDiagram-ZJ3FMSHR-D8mCj0H1.js} +1 -1
- package/dist/assets/{blockDiagram-677ZJIJ3-C_zeJrKF.js → blockDiagram-677ZJIJ3-CbGC1IHl.js} +1 -1
- package/dist/assets/{c4Diagram-LMCZKHZV-CpapRftp.js → c4Diagram-LMCZKHZV-BQhGC1Vc.js} +1 -1
- package/dist/assets/channel-CxQkbwRD.js +1 -0
- package/dist/assets/{channels-list-page-BE0TxB3B.js → channels-list-page-CzXYZZtK.js} +1 -1
- package/dist/assets/chunk-2Q5K7J3B-ClVhziok.js +1 -0
- package/dist/assets/{chunk-32BRIVSS-BKBwdmKR.js → chunk-32BRIVSS-B74ZZwYG.js} +1 -1
- package/dist/assets/{chunk-52WLFC77-BgZRCpDx.js → chunk-52WLFC77-BvmxW_f_.js} +1 -1
- package/dist/assets/{chunk-5FCAYU7R-YWfOINJi.js → chunk-5FCAYU7R-DQvBRnwL.js} +1 -1
- package/dist/assets/{chunk-5HE753X5-BZ6I07d2.js → chunk-5HE753X5-CDIH0_y3.js} +1 -1
- package/dist/assets/{chunk-5JV3BV7I-B7QYgZU5.js → chunk-5JV3BV7I-CcevlKd-.js} +1 -1
- package/dist/assets/{chunk-5TONJI2A-DjyQwgL2.js → chunk-5TONJI2A-_3dxVdnY.js} +1 -1
- package/dist/assets/{chunk-5VM5RSS4-DheHV7X2.js → chunk-5VM5RSS4-pGO5vvo-.js} +1 -1
- package/dist/assets/{chunk-7BUUIJ7U-D6bq2PKF.js → chunk-7BUUIJ7U-DEKF5uAN.js} +1 -1
- package/dist/assets/{chunk-BIQX33UG-CL9x0ibm.js → chunk-BIQX33UG-CjBFgq2r.js} +1 -1
- package/dist/assets/{chunk-C7G6YPKG-8z3B6RUH.js → chunk-C7G6YPKG-e_K9OGTz.js} +1 -1
- package/dist/assets/{chunk-CQNSW5MT-DpHkxY3n.js → chunk-CQNSW5MT-C2IiHyHz.js} +1 -1
- package/dist/assets/{chunk-CYSBUYHQ-DIJDLsdJ.js → chunk-CYSBUYHQ-DCC6KmGp.js} +1 -1
- package/dist/assets/{chunk-EMLP6XTP-B-LjAyVd.js → chunk-EMLP6XTP-BYGgekQ0.js} +1 -1
- package/dist/assets/{chunk-EX3LRPZG-BChuEPvO.js → chunk-EX3LRPZG-FGcwuVsA.js} +1 -1
- package/dist/assets/{chunk-FWX5IMBZ-fBFt0u3f.js → chunk-FWX5IMBZ-CV3Ih_Os.js} +2 -2
- package/dist/assets/{chunk-HOUHSVGY-MD0SpW5A.js → chunk-HOUHSVGY-CXpq_hfV.js} +1 -1
- package/dist/assets/{chunk-ICXQ74PX-DvCcw_hz.js → chunk-ICXQ74PX-CZIpjupq.js} +1 -1
- package/dist/assets/{chunk-JG7HCLWE-B8zqBnU9.js → chunk-JG7HCLWE-CdgwBpW7.js} +1 -1
- package/dist/assets/{chunk-JWPE2WC7-C9efnkGb.js → chunk-JWPE2WC7-BPjAZpOQ.js} +1 -1
- package/dist/assets/{chunk-MOJQB5TN-gXbfqoqq.js → chunk-MOJQB5TN-DbJDm8to.js} +1 -1
- package/dist/assets/{chunk-MOZMSUNE-twIwdkqC.js → chunk-MOZMSUNE-BUa1fqKn.js} +1 -1
- package/dist/assets/{chunk-OGEWGWER-DdR0_cWK.js → chunk-OGEWGWER-CtnzLgRu.js} +1 -1
- package/dist/assets/{chunk-OSBZ3O6U-D7D58Foa.js → chunk-OSBZ3O6U-fUgdpTUM.js} +1 -1
- package/dist/assets/{chunk-PUDLZKDR-DPILgOVY.js → chunk-PUDLZKDR-CtDWZT9-.js} +1 -1
- package/dist/assets/{chunk-Q4XR5HBZ-C0JJx7lW.js → chunk-Q4XR5HBZ-BxnK2qxr.js} +1 -1
- package/dist/assets/{chunk-QBLGF6JB-Dbsf69ng.js → chunk-QBLGF6JB-CH4UKoCF.js} +1 -1
- package/dist/assets/{chunk-R7FJI6CG-Bb8n616-.js → chunk-R7FJI6CG-BxwFoF1v.js} +1 -1
- package/dist/assets/{chunk-RYQCIY6F-1HOsOrSl.js → chunk-RYQCIY6F-DpHAmq7O.js} +1 -1
- package/dist/assets/{chunk-U6XO7XAA-BAFDVb1v.js → chunk-U6XO7XAA-BjH2VUHU.js} +1 -1
- package/dist/assets/{chunk-V7JOEXUC-8-g68lu5.js → chunk-V7JOEXUC-DKJZ3HGj.js} +1 -1
- package/dist/assets/chunk-VAUOI2AC-DZQxkIsv.js +1 -0
- package/dist/assets/{chunk-VR4S4FIN-P6UGmaYd.js → chunk-VR4S4FIN-Cf-o2GtI.js} +1 -1
- package/dist/assets/{chunk-WYO6CB5R-6wjFTXNP.js → chunk-WYO6CB5R-zeNXGm20.js} +3 -2
- package/dist/assets/chunk-XXDRQBXY-BkExtw5C.js +1 -0
- package/dist/assets/{chunk-YOTPTUD7-CLYXVZAi.js → chunk-YOTPTUD7-Ck87Kqxe.js} +1 -1
- package/dist/assets/{chunk-ZGVPDNZ5-BRTmSAhp.js → chunk-ZGVPDNZ5-BbfK6Bfd.js} +1 -1
- package/dist/assets/{chunk-ZIRB5QZD-CH_1eoSV.js → chunk-ZIRB5QZD-BDpzh4Yl.js} +1 -1
- package/dist/assets/classDiagram-OUVF2IWQ-Bd6apBb-.js +1 -0
- package/dist/assets/classDiagram-v2-EOCWNBFH-CYxUZGCI.js +1 -0
- package/dist/assets/{config-split-page-BxDwwAGa.js → config-split-page-CxLWGwdt.js} +1 -1
- package/dist/assets/{confirm-dialog-S5JxZcS1.js → confirm-dialog-BWS7gA_z.js} +1 -1
- package/dist/assets/{cose-bilkent-JH36ORCC-L7w_qScX.js → cose-bilkent-JH36ORCC-CQfMxKCT.js} +1 -1
- package/dist/assets/cynefin-VYW2F7L2-CV0bFLH9.js +1 -0
- package/dist/assets/{cynefinDiagram-TSTJHNR4-Bay7uDSR.js → cynefinDiagram-TSTJHNR4-CxFTiC-p.js} +1 -1
- package/dist/assets/{dagre-CJ_hX8xv.js → dagre-CaBNMcB1.js} +1 -1
- package/dist/assets/{dagre-VKFMJZFB-DDhk-2B3.js → dagre-VKFMJZFB-S_7Hc7bI.js} +1 -1
- package/dist/assets/{desktop-capabilities-page-BwHxJpbl.js → desktop-capabilities-page-BJNGkq4q.js} +1 -1
- package/dist/assets/{desktop-update-config-Bfg7OdJ2.js → desktop-update-config-7tBum3MA.js} +1 -1
- package/dist/assets/{diagram-FQU43EPY-somFq5y8.js → diagram-FQU43EPY-C7zENvFb.js} +1 -1
- package/dist/assets/{diagram-G47NLZAW-hzgyQsPd.js → diagram-G47NLZAW-DqYTCqZA.js} +1 -1
- package/dist/assets/{diagram-NH7WQ7WH-BkJ4gEQJ.js → diagram-NH7WQ7WH-BfSJtXRc.js} +1 -1
- package/dist/assets/{diagram-OA4YK3LP-CS3ZkK4w.js → diagram-OA4YK3LP-CFO-j67H.js} +1 -1
- package/dist/assets/{diagram-WEI45ONY-BApo-JHS.js → diagram-WEI45ONY-DBQDbTaJ.js} +1 -1
- package/dist/assets/{doc-browser-C8eqJLi_.js → doc-browser-BNiqa-zm.js} +1 -1
- package/dist/assets/doc-browser-DZDqghNc.js +1 -0
- package/dist/assets/{doc-browser-context-BPCDFcC-.js → doc-browser-context-Dp0dEMyT.js} +1 -1
- package/dist/assets/{docx-preview-ByCKKIVT.js → docx-preview-B4-aDfvO.js} +1 -1
- package/dist/assets/{ebnfDiagram-CCIWWBDH-DWjfeEie.js → ebnfDiagram-CCIWWBDH-CfDNIXFN.js} +1 -1
- package/dist/assets/{erDiagram-Q63AITRT-DR7vWYHM.js → erDiagram-Q63AITRT-D95rFXMM.js} +1 -1
- package/dist/assets/eventmodeling-45OFAUF4-BzkLWWdP.js +1 -0
- package/dist/assets/{extensions-DgKQRebe.js → extensions-WWEYpaUh.js} +1 -1
- package/dist/assets/flowDiagram-23GEKE2U-ukkCsB0j.js +1 -0
- package/dist/assets/{ganttDiagram-NO4QXBWP-E3l9tuaL.js → ganttDiagram-NO4QXBWP-Bawg0P0r.js} +1 -1
- package/dist/assets/gitGraph-TEB2WS4Q-BYFhT0Q5.js +1 -0
- package/dist/assets/{gitGraphDiagram-IHSO6WYX-DiMvbHKf.js → gitGraphDiagram-IHSO6WYX-BbKh9nbL.js} +1 -1
- package/dist/assets/{i18n-CKvm9zyk.js → i18n-ClHx6jwj.js} +2 -2
- package/dist/assets/{i18n-provider-CPV43pMX.js → i18n-provider-C1NxogO4.js} +1 -1
- package/dist/assets/info-DKCQHKI2-CTmlJdcH.js +1 -0
- package/dist/assets/{infoDiagram-FWYZ7A6U-BZDL0NDJ.js → infoDiagram-FWYZ7A6U-DPffZXYh.js} +1 -1
- package/dist/assets/{ishikawaDiagram-FXEZZL3T-BZ9WvJb2.js → ishikawaDiagram-FXEZZL3T-CBMsLwZ2.js} +1 -1
- package/dist/assets/{journeyDiagram-5HDEW3XC-C3TZvKM8.js → journeyDiagram-5HDEW3XC-Mz5U4OzU.js} +1 -1
- package/dist/assets/{kanban-definition-HUTT4EX6-9FqauDEi.js → kanban-definition-HUTT4EX6-FXZq2k19.js} +1 -1
- package/dist/assets/{katex-J8JuvgNl.js → katex-B26hhTOT.js} +15 -15
- package/dist/assets/katex-DbZC06Q5.js +1 -0
- package/dist/assets/keyboard-shortcuts-settings-page-CS9BFOBW.js +1 -0
- package/dist/assets/{line-Ca49mj80.js → line-BZwCgT9K.js} +1 -1
- package/dist/assets/{linear-DlCeG66S.js → linear-CTe9mKUw.js} +1 -1
- package/dist/assets/{login-page-DeDQjVxv.js → login-page-CL0SrBCf.js} +1 -1
- package/dist/assets/login-page-Dq5nPUCU.js +1 -0
- package/dist/assets/main-CLwCjK9U.css +1 -0
- package/dist/assets/main-CvVBCOlN.js +2 -0
- package/dist/assets/main-i18n-catalog.config-D4qFo4OB.js +1 -0
- package/dist/assets/{map-WMnNq9UV.js → map-Ci48ZwAp.js} +1 -1
- package/dist/assets/mcp-marketplace-page-CATnpZ2Q.js +1 -0
- package/dist/assets/{mcp-marketplace-page-CWjJGp_D.js → mcp-marketplace-page-D75hPmzO.js} +1 -1
- package/dist/assets/{mermaid-parser.core-B8xk4hpS.js → mermaid-parser.core-CzPD3U_z.js} +2 -2
- package/dist/assets/{mermaid.core-CFPiczKH.js → mermaid.core-geIMNPbV.js} +3 -3
- package/dist/assets/{mindmap-definition-LN4V7U3C-CnxA52L6.js → mindmap-definition-LN4V7U3C-xqlKxsr_.js} +1 -1
- package/dist/assets/{model-config-page-Dv_zE7HN.js → model-config-page-DFfcV9lu.js} +1 -1
- package/dist/assets/{ordinal-PgiFlfBJ.js → ordinal-MxnX8yhv.js} +1 -1
- package/dist/assets/packet-7NZHBO7P-BbrI0ZlN.js +1 -0
- package/dist/assets/panel-app-B39rJPj8.js +1 -0
- package/dist/assets/panel-app-i18n-catalog.config-oUwmxoE7.js +1 -0
- package/dist/assets/{panelStandalone-CFRBHV94.js → panelStandalone-D2CqTyi_.js} +2 -2
- package/dist/assets/{pegDiagram-2B236MQR-A8oz7dTH.js → pegDiagram-2B236MQR-D50MFTN1.js} +1 -1
- package/dist/assets/pie-RZYD4A2V-B7zE1ZIr.js +1 -0
- package/dist/assets/{pieDiagram-ENE6RG2P-mtBnkYY0.js → pieDiagram-ENE6RG2P-CpA13oiC.js} +1 -1
- package/dist/assets/{privacy-settings-page-D6uc9f-A.js → privacy-settings-page-CE-xF9bo.js} +1 -1
- package/dist/assets/projects-BF0wpY4X.js +1 -0
- package/dist/assets/{provider-scoped-model-input-DUa_9uwg.js → provider-scoped-model-input-BvZitHkT.js} +1 -1
- package/dist/assets/{providers-config-page-DAiqxlF_.js → providers-config-page-cDeyluVG.js} +1 -1
- package/dist/assets/{pwa-C_Zte1xe.js → pwa-dXn0s_lD.js} +1 -1
- package/dist/assets/{quadrantDiagram-ABIIQ3AL-DE1VybGO.js → quadrantDiagram-ABIIQ3AL-CYCgaj4l.js} +1 -1
- package/dist/assets/radar-I7S5WNFK-1X_q11Ed.js +1 -0
- package/dist/assets/railroad-3IZDKUUU-CIXnuFyE.js +1 -0
- package/dist/assets/railroad-abnf-AHOZXSZD-D6ImKl2r.js +1 -0
- package/dist/assets/railroad-ebnf-EBAXGLYW-2j9d3twU.js +1 -0
- package/dist/assets/railroad-peg-LSFZ7HO6-DMVaJGbm.js +1 -0
- package/dist/assets/{railroadDiagram-RFXS5EU6-u9EMg14B.js → railroadDiagram-RFXS5EU6-4LKDGlXN.js} +1 -1
- package/dist/assets/remote-CV5wQVVj.js +1 -0
- package/dist/assets/{requirementDiagram-TGXJPOKE-BAWHAqEi.js → requirementDiagram-TGXJPOKE-CxNOU0JH.js} +1 -1
- package/dist/assets/runtime-config-page-DNo4dzS1.js +1 -0
- package/dist/assets/{sankeyDiagram-HTMAVEWB-aDy-jdwF.js → sankeyDiagram-HTMAVEWB-DbCBDe2S.js} +1 -1
- package/dist/assets/{search-config-page-3xbhRlW2.js → search-config-page-CA0HxVy0.js} +1 -1
- package/dist/assets/{secrets-config-page-Dn8_rdOj.js → secrets-config-page-iT1wamQp.js} +1 -1
- package/dist/assets/security-config-B38PLI3M.js +1 -0
- package/dist/assets/{sequenceDiagram-DBY2YBRQ-DwHC4mbX.js → sequenceDiagram-DBY2YBRQ-DA6W2BS_.js} +1 -1
- package/dist/assets/{service-apps-panel-CGc04aMn.js → service-apps-panel-CU0tFrqT.js} +1 -1
- package/dist/assets/{sizeCapture-X5ZJPWSS-oOMY0Fag.js → sizeCapture-X5ZJPWSS-T_q_d_VW.js} +1 -1
- package/dist/assets/{src-CLPoo5iL.js → src-CYpUlx-4.js} +1 -1
- package/dist/assets/{stateDiagram-2N3HPSRC-B0Epk-VR.js → stateDiagram-2N3HPSRC-mi8z2G0a.js} +1 -1
- package/dist/assets/stateDiagram-v2-6OUMAXLB-OlIMT57r.js +1 -0
- package/dist/assets/{swimlanes-5IMT3BWC-T0w-Iuzf.js → swimlanes-5IMT3BWC-DkBEhWE4.js} +2 -2
- package/dist/assets/swimlanesDiagram-G3AALYLV-sLThrh07.js +8 -0
- package/dist/assets/{tag-chip-BDzpHEli.js → tag-chip-CY0Hqk_l.js} +1 -1
- package/dist/assets/{theme-provider-B6FinATQ.js → theme-provider-BvpXGXWH.js} +1 -1
- package/dist/assets/{timeline-definition-FHXFAJF6-BEonB5Np.js → timeline-definition-FHXFAJF6-BoFmqBw1.js} +1 -1
- package/dist/assets/treeView-QDETBFTQ-CCFyazDE.js +1 -0
- package/dist/assets/treemap-6X3UGDF4-D6wvq1Hz.js +1 -0
- package/dist/assets/{use-config-BPUM2lbo.js → use-config-C8ZkQiTc.js} +1 -1
- package/dist/assets/{use-desktop-capabilities-BDjArQP3.js → use-desktop-capabilities-DR9snbeo.js} +1 -1
- package/dist/assets/{vennDiagram-L72KCM5P-BDvcOCo2.js → vennDiagram-L72KCM5P-BlZ3Or5U.js} +1 -1
- package/dist/assets/wardley-OPB4EBWU-BRzaRyFf.js +1 -0
- package/dist/assets/{wardleyDiagram-EHGQE667-jMLMgjzE.js → wardleyDiagram-EHGQE667-yDfr33i3.js} +1 -1
- package/dist/assets/{xychartDiagram-FW5EYKEG-pzrpclEv.js → xychartDiagram-FW5EYKEG-mIXwIGAQ.js} +1 -1
- package/dist/index.html +24 -23
- package/dist/panel-standalone.html +6 -6
- package/package.json +8 -8
- package/src/app/configs/app-navigation.config.ts +10 -3
- package/src/app/index.tsx +7 -0
- package/src/features/chat/components/providers/chat-presenter.provider.tsx +2 -0
- package/src/features/chat/features/conversation/components/__tests__/session-conversation-area.test.tsx +12 -0
- package/src/features/chat/features/conversation/components/__tests__/session-conversation-input.streaming.test.tsx +13 -1
- package/src/features/chat/features/conversation/components/__tests__/session-voice-input.test-utils.ts +52 -0
- package/src/features/chat/features/conversation/components/chat-voice-input-panel.tsx +70 -0
- package/src/features/chat/features/conversation/components/chat-voice-shortcut-control.tsx +33 -0
- package/src/features/chat/features/conversation/components/session-conversation-area.tsx +1 -11
- package/src/features/chat/features/conversation/components/session-conversation-input.tsx +29 -5
- package/src/features/chat/features/conversation/hooks/__tests__/use-chat-voice-input.test.tsx +185 -0
- package/src/features/chat/features/conversation/hooks/use-chat-voice-input.ts +52 -0
- package/src/features/chat/features/input/utils/__tests__/chat-input-bar.utils.test.ts +13 -6
- package/src/features/chat/features/input/utils/chat-input-toolbar.utils.ts +28 -12
- package/src/features/chat/features/message/utils/__tests__/chat-message-process-summary.utils.test.ts +19 -1
- package/src/features/chat/features/message/utils/__tests__/chat-message.utils.test.ts +7 -6
- package/src/features/chat/features/message/utils/chat-message-part.utils.ts +4 -1
- package/src/features/chat/features/ncp/hooks/__tests__/use-ncp-agent-runtime.test.tsx +20 -0
- package/src/features/chat/index.ts +1 -0
- package/src/features/chat/managers/chat-voice-input.manager.ts +174 -0
- package/src/features/chat/presenters/chat.presenter.ts +2 -0
- package/src/features/chat/stores/chat-voice-shortcut.store.ts +31 -0
- package/src/features/desktop-capabilities/index.ts +1 -0
- package/src/features/settings/pages/keyboard-shortcuts-settings-page.tsx +15 -0
- package/src/platforms/mobile/components/mobile-settings-shell.tsx +2 -1
- package/src/shared/lib/api/__tests__/ncp-session-query-cache.utils.test.ts +109 -1
- package/src/shared/lib/api/ncp-session-query-cache.utils.ts +71 -2
- package/src/shared/lib/i18n/locales/en-US/chat.json +32 -1
- package/src/shared/lib/i18n/locales/en-US/core.json +2 -0
- package/src/shared/lib/i18n/locales/zh-CN/chat.json +32 -1
- package/src/shared/lib/i18n/locales/zh-CN/core.json +2 -0
- package/src/shared/lib/transport/__tests__/browser-realtime-recovery.service.test.ts +42 -0
- package/src/shared/lib/transport/__tests__/local-transport.service.test.ts +85 -0
- package/src/shared/lib/transport/__tests__/remote.transport.test.ts +54 -1
- package/src/shared/lib/transport/browser-realtime-recovery.service.ts +35 -0
- package/src/shared/lib/transport/local-transport.service.ts +32 -0
- package/src/shared/lib/transport/remote-transport.service.ts +30 -0
- package/dist/assets/app-presenter-provider-CBt-lyr_.js +0 -109
- package/dist/assets/app-presenter-provider-D3BuuOyZ.css +0 -1
- package/dist/assets/architecture-TIHT7OUA-CVRF_cYQ.js +0 -1
- package/dist/assets/channel-CBrWRr4N.js +0 -1
- package/dist/assets/chunk-2Q5K7J3B-B8mmRk7d.js +0 -1
- package/dist/assets/chunk-VAUOI2AC-Cpml0inz.js +0 -1
- package/dist/assets/chunk-XXDRQBXY-nCiqnCpF.js +0 -1
- package/dist/assets/classDiagram-OUVF2IWQ-C-DRqAaW.js +0 -1
- package/dist/assets/classDiagram-v2-EOCWNBFH-DXCcZ42U.js +0 -1
- package/dist/assets/cynefin-VYW2F7L2-2SOTgxxC.js +0 -1
- package/dist/assets/doc-browser-BgRP0Wni.js +0 -1
- package/dist/assets/eventmodeling-45OFAUF4-CXZNG56U.js +0 -1
- package/dist/assets/flowDiagram-23GEKE2U-2065oyKr.js +0 -1
- package/dist/assets/gitGraph-TEB2WS4Q-B0Ln3ZuK.js +0 -1
- package/dist/assets/info-DKCQHKI2-B06uqGM4.js +0 -1
- package/dist/assets/login-page-ClcrvN04.js +0 -1
- package/dist/assets/main-DGqlaAaI.css +0 -1
- package/dist/assets/main-i18n-catalog.config-CpNF5aVc.js +0 -1
- package/dist/assets/main-nMVfHb5K.js +0 -2
- package/dist/assets/mcp-marketplace-page-fvu2NcXT.js +0 -1
- package/dist/assets/packet-7NZHBO7P-CHx-igbz.js +0 -1
- package/dist/assets/panel-app-BUUle0gP.js +0 -1
- package/dist/assets/panel-app-i18n-catalog.config-BzxE3tZb.js +0 -1
- package/dist/assets/pie-RZYD4A2V-DOaMc-x9.js +0 -1
- package/dist/assets/projects-BD1rG75R.js +0 -1
- package/dist/assets/radar-I7S5WNFK-CXsdndDk.js +0 -1
- package/dist/assets/railroad-3IZDKUUU-DxgXdrYP.js +0 -1
- package/dist/assets/railroad-abnf-AHOZXSZD-CR1tnyjz.js +0 -1
- package/dist/assets/railroad-ebnf-EBAXGLYW-CjjxZR2U.js +0 -1
- package/dist/assets/railroad-peg-LSFZ7HO6-DgVGJCHN.js +0 -1
- package/dist/assets/remote-BO0QP7ua.js +0 -1
- package/dist/assets/runtime-config-page-w1UAkGF_.js +0 -1
- package/dist/assets/security-config-Y1nEGM3A.js +0 -1
- package/dist/assets/stateDiagram-v2-6OUMAXLB-Ct4zTw8P.js +0 -1
- package/dist/assets/swimlanesDiagram-G3AALYLV-CdmN0PBd.js +0 -8
- package/dist/assets/treeView-QDETBFTQ-Dad4_Wc7.js +0 -1
- package/dist/assets/treemap-6X3UGDF4-9_GWWZsY.js +0 -1
- package/dist/assets/wardley-OPB4EBWU-BXpvyee9.js +0 -1
- /package/dist/assets/{array-BHDgItFN.js → array-D0DyKZdT.js} +0 -0
- /package/dist/assets/{card-CS8su2Zh.js → card-BhfAZv5L.js} +0 -0
- /package/dist/assets/{chunk-KEIR6QF5-DstBw0Hk.js → chunk-KEIR6QF5-CuZFOa6q.js} +0 -0
- /package/dist/assets/{chunk-Y2CYZVJY-CA3ymQuH.js → chunk-Y2CYZVJY-Frut-1TF.js} +0 -0
- /package/dist/assets/{config-hints-CBPHrfYk.js → config-hints-B_C9DAp5.js} +0 -0
- /package/dist/assets/{cytoscape.esm-C7xr90o_.js → cytoscape.esm-BibuhnZD.js} +0 -0
- /package/dist/assets/{defaultLocale-BG1oDVag.js → defaultLocale-q7sRUXPc.js} +0 -0
- /package/dist/assets/{dist-Cnhwure9.js → dist-DhMddrrH.js} +0 -0
- /package/dist/assets/{form-actions-AIdXOoCL.js → form-actions-BRESe-m_.js} +0 -0
- /package/dist/assets/{graphlib-CNMGfuTb.js → graphlib-BM63WsW3.js} +0 -0
- /package/dist/assets/{init-wQIZuRSH.js → init-CRMkf9sL.js} +0 -0
- /package/dist/assets/{jszip.min-D_AtKL_8.js → jszip.min-6thm2J_a.js} +0 -0
- /package/dist/assets/{path-N-C4MRP0.js → path-BFMmafCM.js} +0 -0
- /package/dist/assets/{provider-models-5PGHb6mP.js → provider-models-hrHMcHqg.js} +0 -0
- /package/dist/assets/{rough.esm-BeWA068d.js → rough.esm-5Tjchx1D.js} +0 -0
- /package/dist/assets/{skeleton-BL9h32W7.js → skeleton-Drvn83YW.js} +0 -0
- /package/dist/assets/{use-panel-app-scroll-restoration-DsV1IJx-.js → use-panel-app-scroll-restoration-ZnicIfo3.js} +0 -0
- /package/dist/assets/{xlsx-CwUXNtD3.js → xlsx-BC8ibjXK.js} +0 -0
|
@@ -31,6 +31,22 @@ function buildDiscoveredModelGroups(options: ChatModelRecord[]) {
|
|
|
31
31
|
return [...groups.values()];
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
+
function buildRemainingModelGroups(options: ChatModelRecord[], allModelsLabel: string) {
|
|
35
|
+
const groups = new Map<string, { key: string; label: string; options: Array<{ value: string; label: string }> }>();
|
|
36
|
+
for (const option of options) {
|
|
37
|
+
const separatorIndex = option.value.indexOf('/');
|
|
38
|
+
const providerKey = separatorIndex > 0 ? option.value.slice(0, separatorIndex) : "other";
|
|
39
|
+
const group = groups.get(providerKey) ?? {
|
|
40
|
+
key: `provider-${providerKey}`,
|
|
41
|
+
label: option.providerLabel.trim() || allModelsLabel,
|
|
42
|
+
options: [],
|
|
43
|
+
};
|
|
44
|
+
group.options.push({ value: option.value, label: option.modelLabel.trim() });
|
|
45
|
+
groups.set(providerKey, group);
|
|
46
|
+
}
|
|
47
|
+
return [...groups.values()];
|
|
48
|
+
}
|
|
49
|
+
|
|
34
50
|
export function toChatModelRecords(snapshotModels: ChatModelOption[]): ChatModelRecord[] {
|
|
35
51
|
return snapshotModels.map((model) => ({
|
|
36
52
|
value: model.value,
|
|
@@ -183,8 +199,9 @@ export function buildModelToolbarSelect({
|
|
|
183
199
|
const remainingOptions = modelOptions.filter(
|
|
184
200
|
(option) => !favoriteValueSet.has(option.value) && !recentValueSet.has(option.value),
|
|
185
201
|
);
|
|
186
|
-
const
|
|
187
|
-
|
|
202
|
+
const groups = [
|
|
203
|
+
...(favoriteOptions.length > 0
|
|
204
|
+
? [
|
|
188
205
|
{
|
|
189
206
|
key: "favorite-models",
|
|
190
207
|
label: texts.favoriteModelsLabel,
|
|
@@ -193,6 +210,10 @@ export function buildModelToolbarSelect({
|
|
|
193
210
|
label: formatModelOptionLabel(option),
|
|
194
211
|
})),
|
|
195
212
|
},
|
|
213
|
+
]
|
|
214
|
+
: []),
|
|
215
|
+
...(recentOptions.length > 0
|
|
216
|
+
? [
|
|
196
217
|
{
|
|
197
218
|
key: "recent-models",
|
|
198
219
|
label: texts.recentModelsLabel,
|
|
@@ -201,16 +222,11 @@ export function buildModelToolbarSelect({
|
|
|
201
222
|
label: formatModelOptionLabel(option),
|
|
202
223
|
})),
|
|
203
224
|
},
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
label: formatModelOptionLabel(option),
|
|
210
|
-
})),
|
|
211
|
-
},
|
|
212
|
-
].filter((group) => group.options.length > 0)
|
|
213
|
-
: undefined;
|
|
225
|
+
]
|
|
226
|
+
: []),
|
|
227
|
+
...buildRemainingModelGroups(remainingOptions, texts.allModelsLabel),
|
|
228
|
+
].filter((group) => group.options.length > 0);
|
|
229
|
+
const optionGroups = groups.length > 0 ? groups : undefined;
|
|
214
230
|
const discoveryOptions = discoveredModelOptions ?? [];
|
|
215
231
|
|
|
216
232
|
return {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { NcpEventType, type NcpMessage } from "@nextclaw/ncp";
|
|
2
|
+
import { DefaultNcpAgentConversationStateManager } from "@nextclaw/ncp-toolkit";
|
|
2
3
|
import { describe, expect, it } from "vitest";
|
|
3
4
|
import { buildChatMessageProcessSummary } from "@/features/chat/features/message/utils/chat-message-process-summary.utils";
|
|
4
5
|
|
|
@@ -29,6 +30,23 @@ const baseAssistantMessage = {
|
|
|
29
30
|
} satisfies NcpMessage;
|
|
30
31
|
|
|
31
32
|
describe("buildChatMessageProcessSummary", () => {
|
|
33
|
+
it("shows elapsed time from the real completed-message then run-finished event sequence", async () => {
|
|
34
|
+
const manager = new DefaultNcpAgentConversationStateManager();
|
|
35
|
+
await manager.dispatchBatch([
|
|
36
|
+
{ type: NcpEventType.RunStarted, payload: { sessionId: "session-1", runId: "run-1" } },
|
|
37
|
+
{ type: NcpEventType.MessageCompleted, payload: { sessionId: "session-1", message: baseAssistantMessage } },
|
|
38
|
+
{
|
|
39
|
+
type: NcpEventType.RunFinished,
|
|
40
|
+
payload: {
|
|
41
|
+
sessionId: "session-1", runId: "run-1", messageId: baseAssistantMessage.id,
|
|
42
|
+
startedAt: "2026-03-31T10:00:00.000Z", endedAt: "2026-03-31T10:03:51.000Z",
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
]);
|
|
46
|
+
const message = manager.getSnapshot().messages[0]!;
|
|
47
|
+
expect(buildChatMessageProcessSummary({ message, processedLabel: "已处理" })?.label).toBe("已处理 3m 51s");
|
|
48
|
+
});
|
|
49
|
+
|
|
32
50
|
it("does not invent duration when lifecycle timing is absent", () => {
|
|
33
51
|
expect(
|
|
34
52
|
buildChatMessageProcessSummary({
|
|
@@ -242,10 +242,11 @@ it("preserves full generic tool args for the expanded body while keeping the hea
|
|
|
242
242
|
|
|
243
243
|
it("keeps structured terminal results as structured data instead of raw json output", () => {
|
|
244
244
|
const terminalResult = {
|
|
245
|
-
|
|
246
|
-
command: "
|
|
247
|
-
|
|
248
|
-
|
|
245
|
+
ok: true,
|
|
246
|
+
command: "nextclaw --version",
|
|
247
|
+
exitCode: 0,
|
|
248
|
+
stdout: "0.48.3\n",
|
|
249
|
+
stderr: "",
|
|
249
250
|
};
|
|
250
251
|
|
|
251
252
|
const adapted = adapt([
|
|
@@ -259,7 +260,7 @@ it("keeps structured terminal results as structured data instead of raw json out
|
|
|
259
260
|
status: ToolInvocationStatus.RESULT,
|
|
260
261
|
toolCallId: "call-terminal-result",
|
|
261
262
|
toolName: "command_execution",
|
|
262
|
-
args: '{"command":"
|
|
263
|
+
args: '{"command":"nextclaw --version"}',
|
|
263
264
|
result: terminalResult,
|
|
264
265
|
},
|
|
265
266
|
},
|
|
@@ -271,7 +272,7 @@ it("keeps structured terminal results as structured data instead of raw json out
|
|
|
271
272
|
type: "tool-card",
|
|
272
273
|
card: {
|
|
273
274
|
toolName: "command_execution",
|
|
274
|
-
summary: "command:
|
|
275
|
+
summary: "command: nextclaw --version",
|
|
275
276
|
output: undefined,
|
|
276
277
|
outputData: terminalResult,
|
|
277
278
|
statusTone: "success",
|
|
@@ -174,7 +174,10 @@ function buildToolInvocationPart(
|
|
|
174
174
|
detail,
|
|
175
175
|
inputData: fileOperationCardData ? undefined : invocation.parsedArgs ?? invocation.args,
|
|
176
176
|
text: shouldShowRawResult && rawResult ? rawResult : undefined,
|
|
177
|
-
outputData:
|
|
177
|
+
outputData:
|
|
178
|
+
shouldShowRawResult || shouldHideStructuredTerminalJson
|
|
179
|
+
? invocation.result
|
|
180
|
+
: undefined,
|
|
178
181
|
callId: invocation.toolCallId || undefined,
|
|
179
182
|
toolCallId: invocation.toolCallId || undefined,
|
|
180
183
|
...(invocation.execution ? { execution: invocation.execution } : {}),
|
|
@@ -513,6 +513,26 @@ describe("useNcpAgentRuntime", () => {
|
|
|
513
513
|
});
|
|
514
514
|
|
|
515
515
|
describe("useNcpAgentRuntime commands and live stream", () => {
|
|
516
|
+
it("keeps the run active after message completion and settles immediately on the terminal event", async () => {
|
|
517
|
+
const client = new DeferredSendClient();
|
|
518
|
+
const manager = new DefaultNcpAgentConversationStateManager();
|
|
519
|
+
const { result } = renderHook(() => useNcpAgentRuntime({ sessionId: "session-1", client, manager }));
|
|
520
|
+
await act(async () => {
|
|
521
|
+
await client.emit({ type: NcpEventType.RunStarted, payload: { sessionId: "session-1", runId: "run-1" } });
|
|
522
|
+
await client.emit({ type: NcpEventType.MessageCompleted, payload: {
|
|
523
|
+
sessionId: "session-1",
|
|
524
|
+
message: { id: "assistant-1", sessionId: "session-1", role: "assistant", status: "final", timestamp: now, parts: [{ type: "text", text: "Done" }] },
|
|
525
|
+
} });
|
|
526
|
+
});
|
|
527
|
+
await waitFor(() => expect(result.current.visibleMessages[0]?.status).toBe("final"));
|
|
528
|
+
expect(result.current.isRunning).toBe(true);
|
|
529
|
+
await act(async () => {
|
|
530
|
+
await client.emit({ type: NcpEventType.RunFinished, payload: { sessionId: "session-1", runId: "run-1", messageId: "assistant-1" } });
|
|
531
|
+
});
|
|
532
|
+
await waitFor(() => expect(result.current.isRunning).toBe(false));
|
|
533
|
+
expect(result.current.isSending).toBe(false);
|
|
534
|
+
});
|
|
535
|
+
|
|
516
536
|
beforeEach(() => {
|
|
517
537
|
vi.clearAllMocks();
|
|
518
538
|
});
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import type { ChatComposerDictationSession } from '@nextclaw/agent-chat-ui';
|
|
2
|
+
|
|
3
|
+
export type VoicePhase = 'idle' | 'ready' | 'starting' | 'recording' | 'stopping' | 'error';
|
|
4
|
+
export type VoiceError = 'unsupported' | 'permission' | 'network' | 'audio-capture' | 'no-speech' | 'interrupted' | 'timeout' | 'failed';
|
|
5
|
+
export type VoiceSnapshot = { phase: VoicePhase; text: string; interim: string; seconds: number; error: VoiceError | null };
|
|
6
|
+
export type SpeechRecognitionLike = {
|
|
7
|
+
lang: string;
|
|
8
|
+
continuous: boolean;
|
|
9
|
+
interimResults: boolean;
|
|
10
|
+
start: () => void;
|
|
11
|
+
stop: () => void;
|
|
12
|
+
abort: () => void;
|
|
13
|
+
onstart: (() => void) | null;
|
|
14
|
+
onresult: ((event: { results: ArrayLike<{ isFinal: boolean; 0: { transcript: string } }> }) => void) | null;
|
|
15
|
+
onend: (() => void) | null;
|
|
16
|
+
onerror: ((event: { error: string }) => void) | null;
|
|
17
|
+
};
|
|
18
|
+
type RecognitionConstructor = new () => SpeechRecognitionLike;
|
|
19
|
+
const createBrowserRecognition = (): SpeechRecognitionLike | null => {
|
|
20
|
+
const host = window as Window & { SpeechRecognition?: RecognitionConstructor; webkitSpeechRecognition?: RecognitionConstructor };
|
|
21
|
+
const Constructor = host.SpeechRecognition ?? host.webkitSpeechRecognition;
|
|
22
|
+
return Constructor ? new Constructor() : null;
|
|
23
|
+
};
|
|
24
|
+
const initialSnapshot = (): VoiceSnapshot => ({ phase: 'idle', text: '', interim: '', seconds: 0, error: null });
|
|
25
|
+
const isActive = (phase: VoicePhase) => ['starting', 'recording', 'stopping'].includes(phase);
|
|
26
|
+
|
|
27
|
+
/** Owns one explicit dictation session; never writes to the chat draft or sends messages. */
|
|
28
|
+
export class ChatVoiceInputManager {
|
|
29
|
+
private snapshot = initialSnapshot();
|
|
30
|
+
private listeners = new Set<() => void>();
|
|
31
|
+
private recognition: SpeechRecognitionLike | null = null;
|
|
32
|
+
private deadline: ReturnType<typeof setTimeout> | undefined;
|
|
33
|
+
private ticker: ReturnType<typeof setInterval> | undefined;
|
|
34
|
+
private context = '';
|
|
35
|
+
private settingsRequested = false;
|
|
36
|
+
private draft: ChatComposerDictationSession | null = null;
|
|
37
|
+
|
|
38
|
+
constructor(private readonly createRecognition = createBrowserRecognition) {}
|
|
39
|
+
|
|
40
|
+
getSnapshot = (): VoiceSnapshot => this.snapshot;
|
|
41
|
+
subscribe = (listener: () => void): (() => void) => {
|
|
42
|
+
this.listeners.add(listener);
|
|
43
|
+
return () => { this.listeners.delete(listener); };
|
|
44
|
+
};
|
|
45
|
+
private update = (patch: Partial<VoiceSnapshot>): void => {
|
|
46
|
+
this.snapshot = { ...this.snapshot, ...patch };
|
|
47
|
+
this.listeners.forEach((listener) => listener());
|
|
48
|
+
};
|
|
49
|
+
open = (context: string): void => {
|
|
50
|
+
if (this.snapshot.phase !== 'idle') return;
|
|
51
|
+
this.context = context;
|
|
52
|
+
this.update({ ...initialSnapshot(), phase: 'ready' });
|
|
53
|
+
};
|
|
54
|
+
openSettings = (): void => {
|
|
55
|
+
this.settingsRequested = true;
|
|
56
|
+
if (this.snapshot.phase === 'recording') { this.finish(); return; }
|
|
57
|
+
if (this.snapshot.phase === 'stopping') return;
|
|
58
|
+
const { context } = this;
|
|
59
|
+
this.acceptDraft();
|
|
60
|
+
this.open(context);
|
|
61
|
+
};
|
|
62
|
+
start = (language: string, context: string, draft: ChatComposerDictationSession | null): void => {
|
|
63
|
+
if (isActive(this.snapshot.phase)) return;
|
|
64
|
+
if (!draft) return;
|
|
65
|
+
this.release();
|
|
66
|
+
this.context = context;
|
|
67
|
+
this.draft = draft;
|
|
68
|
+
this.update({ ...initialSnapshot(), phase: 'starting' });
|
|
69
|
+
try {
|
|
70
|
+
const recognition = this.createRecognition();
|
|
71
|
+
if (!recognition) { this.fail('unsupported'); return; }
|
|
72
|
+
this.recognition = recognition;
|
|
73
|
+
recognition.lang = language === 'zh' ? 'zh-CN' : 'en-US';
|
|
74
|
+
recognition.continuous = true;
|
|
75
|
+
recognition.interimResults = true;
|
|
76
|
+
this.connect(recognition);
|
|
77
|
+
this.setDeadline(15000);
|
|
78
|
+
recognition.start();
|
|
79
|
+
} catch { this.fail('failed'); }
|
|
80
|
+
};
|
|
81
|
+
private connect = (recognition: SpeechRecognitionLike): void => {
|
|
82
|
+
recognition.onstart = () => {
|
|
83
|
+
if (this.recognition !== recognition || this.snapshot.phase !== 'starting') return;
|
|
84
|
+
clearTimeout(this.deadline);
|
|
85
|
+
this.update({ phase: 'recording' });
|
|
86
|
+
const started = Date.now();
|
|
87
|
+
this.ticker = setInterval(() => {
|
|
88
|
+
const seconds = Math.floor((Date.now() - started) / 1000);
|
|
89
|
+
this.update({ seconds });
|
|
90
|
+
if (seconds >= 60) this.finish();
|
|
91
|
+
}, 1000);
|
|
92
|
+
};
|
|
93
|
+
recognition.onresult = (event) => {
|
|
94
|
+
if (this.recognition !== recognition) return;
|
|
95
|
+
const finals: string[] = [];
|
|
96
|
+
const interim: string[] = [];
|
|
97
|
+
for (let i = 0; i < event.results.length; i += 1) {
|
|
98
|
+
const result = event.results[i];
|
|
99
|
+
const text = result[0]?.transcript?.trim();
|
|
100
|
+
if (text) (result.isFinal ? finals : interim).push(text);
|
|
101
|
+
}
|
|
102
|
+
this.update({ text: finals.join(' '), interim: interim.join(' ') });
|
|
103
|
+
this.draft?.update(this.snapshot.text, this.snapshot.interim);
|
|
104
|
+
};
|
|
105
|
+
recognition.onerror = ({ error }) => {
|
|
106
|
+
if (this.recognition !== recognition) return;
|
|
107
|
+
const reason: VoiceError = error === 'not-allowed' || error === 'service-not-allowed'
|
|
108
|
+
? 'permission'
|
|
109
|
+
: error === 'network' || error === 'audio-capture' || error === 'no-speech' ? error : 'failed';
|
|
110
|
+
this.fail(reason);
|
|
111
|
+
};
|
|
112
|
+
recognition.onend = () => {
|
|
113
|
+
if (this.recognition !== recognition) return;
|
|
114
|
+
const text = this.snapshot.text.trim();
|
|
115
|
+
this.release(false);
|
|
116
|
+
if (!text) { this.fail('no-speech'); return; }
|
|
117
|
+
this.draft?.commit(text);
|
|
118
|
+
this.draft = null;
|
|
119
|
+
const { settingsRequested, context } = this;
|
|
120
|
+
this.cancel();
|
|
121
|
+
if (settingsRequested) this.open(context);
|
|
122
|
+
};
|
|
123
|
+
};
|
|
124
|
+
finish = (): void => {
|
|
125
|
+
if (this.snapshot.phase === 'starting') { this.cancel(); return; }
|
|
126
|
+
if (this.snapshot.phase !== 'recording' || !this.recognition) return;
|
|
127
|
+
clearInterval(this.ticker);
|
|
128
|
+
this.update({ phase: 'stopping' });
|
|
129
|
+
this.setDeadline(8000);
|
|
130
|
+
try { this.recognition.stop(); } catch { this.fail('failed'); }
|
|
131
|
+
};
|
|
132
|
+
interrupt = (): void => {
|
|
133
|
+
if (isActive(this.snapshot.phase)) this.fail('interrupted');
|
|
134
|
+
};
|
|
135
|
+
acceptDraft = (): void => {
|
|
136
|
+
this.draft?.commit();
|
|
137
|
+
this.draft = null;
|
|
138
|
+
this.cancel();
|
|
139
|
+
};
|
|
140
|
+
cancel = (): void => {
|
|
141
|
+
this.settingsRequested = false;
|
|
142
|
+
this.release();
|
|
143
|
+
this.context = '';
|
|
144
|
+
this.draft?.cancel();
|
|
145
|
+
this.draft = null;
|
|
146
|
+
this.update(initialSnapshot());
|
|
147
|
+
};
|
|
148
|
+
private fail = (error: VoiceError): void => {
|
|
149
|
+
this.release();
|
|
150
|
+
if (this.snapshot.text) this.draft?.commit(this.snapshot.text);
|
|
151
|
+
else this.draft?.cancel();
|
|
152
|
+
this.draft = null;
|
|
153
|
+
this.update(this.settingsRequested ? { ...initialSnapshot(), phase: 'ready' } : { phase: 'error', error, interim: '' });
|
|
154
|
+
this.settingsRequested = false;
|
|
155
|
+
};
|
|
156
|
+
private setDeadline = (milliseconds: number): void => {
|
|
157
|
+
clearTimeout(this.deadline);
|
|
158
|
+
this.deadline = setTimeout(() => this.fail('timeout'), milliseconds);
|
|
159
|
+
};
|
|
160
|
+
private release = (abort = true): void => {
|
|
161
|
+
clearTimeout(this.deadline);
|
|
162
|
+
clearInterval(this.ticker);
|
|
163
|
+
const { recognition } = this;
|
|
164
|
+
this.recognition = null;
|
|
165
|
+
if (!recognition) return;
|
|
166
|
+
recognition.onstart = null;
|
|
167
|
+
recognition.onend = null;
|
|
168
|
+
recognition.onerror = null;
|
|
169
|
+
recognition.onresult = null;
|
|
170
|
+
if (abort) {
|
|
171
|
+
try { recognition.abort(); } catch { /* Already released by the browser. */ }
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
}
|
|
@@ -4,8 +4,10 @@ import { ChatQueryManager } from '@/features/chat/managers/chat-query.manager';
|
|
|
4
4
|
import { ChatThreadManager } from '@/features/chat/managers/chat-thread.manager';
|
|
5
5
|
import type { ChatComposerIntentManager } from '@/features/chat/managers/chat-composer-intent.manager';
|
|
6
6
|
import type { AppPresenter } from '@/app/presenters/app.presenter';
|
|
7
|
+
import { ChatVoiceInputManager } from '@/features/chat/managers/chat-voice-input.manager';
|
|
7
8
|
|
|
8
9
|
export class ChatPresenter {
|
|
10
|
+
readonly chatVoiceInputManager = new ChatVoiceInputManager();
|
|
9
11
|
readonly chatUiManager: ChatUiManager;
|
|
10
12
|
readonly chatSessionListManager: ChatSessionListManager;
|
|
11
13
|
readonly chatQueryManager: ChatQueryManager;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { create } from 'zustand';
|
|
2
|
+
import { createJSONStorage, persist } from 'zustand/middleware';
|
|
3
|
+
|
|
4
|
+
export type VoiceShortcut = { code: string; ctrl: boolean; meta: boolean; alt: boolean; shift: boolean };
|
|
5
|
+
export function isVoiceShortcut(value: unknown): value is VoiceShortcut {
|
|
6
|
+
if (!value || typeof value !== 'object') return false;
|
|
7
|
+
const key = value as VoiceShortcut;
|
|
8
|
+
const unmodified = key.ctrl === false && key.meta === false && key.alt === false && key.shift === false;
|
|
9
|
+
return typeof key.code === 'string' && (/^Key[A-Z]$/.test(key.code) || /^F(?:[6-9]|10)$/.test(key.code)) &&
|
|
10
|
+
typeof key.ctrl === 'boolean' && typeof key.meta === 'boolean' &&
|
|
11
|
+
typeof key.alt === 'boolean' && typeof key.shift === 'boolean' &&
|
|
12
|
+
(unmodified || (key.alt && key.ctrl !== key.meta));
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
type ShortcutState = { shortcut: VoiceShortcut | null; setShortcut: (shortcut: VoiceShortcut | null) => void };
|
|
16
|
+
export const useChatVoiceShortcutStore = create<ShortcutState>()(persist(
|
|
17
|
+
(set) => ({ shortcut: null, setShortcut: (shortcut) => set({ shortcut }) }),
|
|
18
|
+
{
|
|
19
|
+
name: 'nextclaw.chat.voice-shortcut', version: 1,
|
|
20
|
+
storage: createJSONStorage(() => window.localStorage),
|
|
21
|
+
partialize: (state) => ({ shortcut: state.shortcut }),
|
|
22
|
+
merge: (persisted, current) => {
|
|
23
|
+
const value = (persisted as { shortcut?: unknown } | null)?.shortcut;
|
|
24
|
+
return { ...current, shortcut: isVoiceShortcut(value) ? value : null };
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
));
|
|
28
|
+
|
|
29
|
+
export function voiceShortcutLabel(shortcut: VoiceShortcut): string {
|
|
30
|
+
return [shortcut.ctrl && 'Ctrl', shortcut.meta && '⌘', shortcut.alt && 'Alt/⌥', shortcut.shift && 'Shift', shortcut.code.replace(/^Key/, '')].filter(Boolean).join(' + ');
|
|
31
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { useViewportLayout } from '@/app/hooks/use-viewport-layout';
|
|
2
|
+
import { ChatVoiceShortcutControl } from '@/features/chat';
|
|
3
|
+
import { SettingsPage } from '@/shared/components/settings/settings-page';
|
|
4
|
+
import { SettingRow, SettingsGroup, SettingsSection } from '@/shared/components/settings/setting-row';
|
|
5
|
+
import { t } from '@/shared/lib/i18n';
|
|
6
|
+
|
|
7
|
+
export function KeyboardShortcutsSettingsPage() {
|
|
8
|
+
const { isMobile } = useViewportLayout();
|
|
9
|
+
return <SettingsPage title={t('keyboardShortcuts')}>
|
|
10
|
+
{isMobile ? <p className='text-sm text-muted-foreground'>{t('keyboardShortcutsDesktopOnly')}</p> :
|
|
11
|
+
<SettingsSection><SettingsGroup>
|
|
12
|
+
<SettingRow title={t('chatInputVoice')} layout='stacked'><ChatVoiceShortcutControl /></SettingRow>
|
|
13
|
+
</SettingsGroup></SettingsSection>}
|
|
14
|
+
</SettingsPage>;
|
|
15
|
+
}
|
|
@@ -2,11 +2,12 @@ import { NavLink } from "react-router-dom";
|
|
|
2
2
|
import { getSettingsNavItems } from "@/app/configs/app-navigation.config";
|
|
3
3
|
import { t } from "@/shared/lib/i18n";
|
|
4
4
|
import { cn } from "@/shared/lib/utils";
|
|
5
|
-
import { useDesktopCapabilityAvailability } from "@/features/desktop-capabilities
|
|
5
|
+
import { useDesktopCapabilityAvailability } from "@/features/desktop-capabilities";
|
|
6
6
|
|
|
7
7
|
export function MobileSettingsShell() {
|
|
8
8
|
const desktopCapabilityAvailable = useDesktopCapabilityAvailability();
|
|
9
9
|
const settingsNavItems = getSettingsNavItems(t, {
|
|
10
|
+
includeKeyboardShortcuts: false,
|
|
10
11
|
includeDesktopCapabilities: desktopCapabilityAvailable,
|
|
11
12
|
});
|
|
12
13
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { QueryClient } from '@tanstack/react-query';
|
|
1
|
+
import { QueryClient, type InfiniteData } from '@tanstack/react-query';
|
|
2
2
|
import { describe, expect, it } from 'vitest';
|
|
3
3
|
import {
|
|
4
4
|
applyNcpSessionRealtimeEvent,
|
|
@@ -262,3 +262,111 @@ describe('ncp-session-query-cache', () => {
|
|
|
262
262
|
expect(queryClient.getQueryData<NcpSessionsListView>(['ncp-sessions', 200, 'peer-b'])?.sessions).toEqual([]);
|
|
263
263
|
});
|
|
264
264
|
});
|
|
265
|
+
|
|
266
|
+
describe('paginated ncp session realtime cache', () => {
|
|
267
|
+
it('updates existing paginated summaries without invalidating the session pages', () => {
|
|
268
|
+
const queryClient = new QueryClient();
|
|
269
|
+
const queryKey = ['ncp-session-pages', 1, null] as const;
|
|
270
|
+
const current = createSessionsList();
|
|
271
|
+
queryClient.setQueryData<InfiniteData<NcpSessionsListView>>(queryKey, {
|
|
272
|
+
pages: [
|
|
273
|
+
{ ...current, sessions: [current.sessions[0]!], page: 1, pageSize: 1, hasMore: true },
|
|
274
|
+
{ ...current, sessions: [current.sessions[1]!], page: 2, pageSize: 1, hasMore: false }
|
|
275
|
+
],
|
|
276
|
+
pageParams: [1, 2]
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
applyNcpSessionRealtimeEvent(queryClient, {
|
|
280
|
+
type: 'session.summary.upsert',
|
|
281
|
+
payload: {
|
|
282
|
+
summary: {
|
|
283
|
+
...current.sessions[1]!,
|
|
284
|
+
updatedAt: '2026-03-29T11:00:00.000Z',
|
|
285
|
+
lastMessageAt: '2026-03-29T11:00:00.000Z',
|
|
286
|
+
messageCount: 3
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
const updated = queryClient.getQueryData<InfiniteData<NcpSessionsListView>>(queryKey);
|
|
292
|
+
expect(updated?.pages.map((page) => page.sessions[0]?.sessionId)).toEqual(['session-2', 'session-1']);
|
|
293
|
+
expect(queryClient.getQueryState(queryKey)?.isInvalidated).toBe(false);
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
it('invalidates paginated session queries when an upsert changes their membership', () => {
|
|
297
|
+
const queryClient = new QueryClient();
|
|
298
|
+
const queryKey = ['ncp-session-pages', 100, null] as const;
|
|
299
|
+
queryClient.setQueryData<InfiniteData<NcpSessionsListView>>(queryKey, {
|
|
300
|
+
pages: [{ ...createSessionsList(), page: 1, pageSize: 100, hasMore: false }],
|
|
301
|
+
pageParams: [1]
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
applyNcpSessionRealtimeEvent(queryClient, {
|
|
305
|
+
type: 'session.summary.upsert',
|
|
306
|
+
payload: {
|
|
307
|
+
summary: {
|
|
308
|
+
sessionId: 'session-3',
|
|
309
|
+
messageCount: 1,
|
|
310
|
+
updatedAt: '2026-03-29T12:00:00.000Z',
|
|
311
|
+
status: 'idle'
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
expect(queryClient.getQueryState(queryKey)?.isInvalidated).toBe(true);
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
it('patches paginated run status without invalidating the session pages', () => {
|
|
320
|
+
const queryClient = new QueryClient();
|
|
321
|
+
const queryKey = ['ncp-session-pages', 100, null] as const;
|
|
322
|
+
queryClient.setQueryData<InfiniteData<NcpSessionsListView>>(queryKey, {
|
|
323
|
+
pages: [{ ...createSessionsList(), page: 1, pageSize: 100, hasMore: false }],
|
|
324
|
+
pageParams: [1]
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
applyNcpSessionRealtimeEvent(queryClient, {
|
|
328
|
+
type: 'session.run-status',
|
|
329
|
+
payload: { sessionKey: 'session-2', status: 'running' }
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
const updated = queryClient.getQueryData<InfiniteData<NcpSessionsListView>>(queryKey);
|
|
333
|
+
expect(updated?.pages[0]?.sessions[1]).toMatchObject({
|
|
334
|
+
sessionId: 'session-2',
|
|
335
|
+
messageCount: 2,
|
|
336
|
+
status: 'running'
|
|
337
|
+
});
|
|
338
|
+
expect(queryClient.getQueryState(queryKey)?.isInvalidated).toBe(false);
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
it('invalidates filtered paginated queries when an upsert may change search membership', () => {
|
|
342
|
+
const queryClient = new QueryClient();
|
|
343
|
+
const queryKey = ['ncp-session-pages', 100, 'journal'] as const;
|
|
344
|
+
queryClient.setQueryData<InfiniteData<NcpSessionsListView>>(queryKey, {
|
|
345
|
+
pages: [{ ...createSessionsList(), page: 1, pageSize: 100, hasMore: false }],
|
|
346
|
+
pageParams: [1]
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
applyNcpSessionRealtimeEvent(queryClient, {
|
|
350
|
+
type: 'session.summary.upsert',
|
|
351
|
+
payload: { summary: { ...createSessionsList().sessions[0]!, metadata: { label: 'Journal' } } }
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
expect(queryClient.getQueryState(queryKey)?.isInvalidated).toBe(true);
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
it('invalidates paginated queries after deletion changes their membership', () => {
|
|
358
|
+
const queryClient = new QueryClient();
|
|
359
|
+
const queryKey = ['ncp-session-pages', 100, null] as const;
|
|
360
|
+
queryClient.setQueryData<InfiniteData<NcpSessionsListView>>(queryKey, {
|
|
361
|
+
pages: [{ ...createSessionsList(), page: 1, pageSize: 100, hasMore: false }],
|
|
362
|
+
pageParams: [1]
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
applyNcpSessionRealtimeEvent(queryClient, {
|
|
366
|
+
type: 'session.summary.delete',
|
|
367
|
+
payload: { sessionKey: 'session-1' }
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
expect(queryClient.getQueryState(queryKey)?.isInvalidated).toBe(true);
|
|
371
|
+
});
|
|
372
|
+
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { QueryClient } from '@tanstack/react-query';
|
|
1
|
+
import type { InfiniteData, QueryClient } from '@tanstack/react-query';
|
|
2
2
|
import type { NcpSessionSummaryView, NcpSessionsListView, WsEvent } from '@/shared/lib/api';
|
|
3
3
|
|
|
4
4
|
function readSessionActivityAt(summary: NcpSessionSummaryView): string {
|
|
@@ -26,6 +26,73 @@ function queryKeyAcceptsSessionSummary(queryKey: readonly unknown[], summary: Nc
|
|
|
26
26
|
return !peerId || summary.peerId === peerId;
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
+
function updateSessionPages(
|
|
30
|
+
current: InfiniteData<NcpSessionsListView> | undefined,
|
|
31
|
+
update: (sessions: NcpSessionsListView) => NcpSessionsListView | undefined
|
|
32
|
+
): InfiniteData<NcpSessionsListView> | undefined {
|
|
33
|
+
if (!current) return current;
|
|
34
|
+
const pageLengths = current.pages.map((page) => page.sessions.length);
|
|
35
|
+
const sessions = current.pages.flatMap((page) => page.sessions);
|
|
36
|
+
const updated = update({
|
|
37
|
+
sessions,
|
|
38
|
+
total: current.pages[0]?.total ?? sessions.length
|
|
39
|
+
});
|
|
40
|
+
if (!updated) return current;
|
|
41
|
+
let offset = 0;
|
|
42
|
+
return {
|
|
43
|
+
...current,
|
|
44
|
+
pages: current.pages.map((page, index) => {
|
|
45
|
+
const nextOffset = offset + (pageLengths[index] ?? 0);
|
|
46
|
+
const nextPage = {
|
|
47
|
+
...page,
|
|
48
|
+
sessions: updated.sessions.slice(offset, nextOffset)
|
|
49
|
+
};
|
|
50
|
+
offset = nextOffset;
|
|
51
|
+
return nextPage;
|
|
52
|
+
})
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function updateExistingNcpSessionSummaryPages(
|
|
57
|
+
queryClient: QueryClient | undefined,
|
|
58
|
+
summary: NcpSessionSummaryView
|
|
59
|
+
): void {
|
|
60
|
+
if (!queryClient) return;
|
|
61
|
+
const entries = queryClient.getQueriesData<InfiniteData<NcpSessionsListView>>({
|
|
62
|
+
queryKey: ['ncp-session-pages']
|
|
63
|
+
});
|
|
64
|
+
for (const [queryKey, current] of entries) {
|
|
65
|
+
const query = typeof queryKey[2] === 'string' ? queryKey[2].trim() : '';
|
|
66
|
+
const containsSession = current?.pages.some((page) =>
|
|
67
|
+
page.sessions.some((session) => session.sessionId === summary.sessionId)
|
|
68
|
+
);
|
|
69
|
+
if (query || !containsSession) {
|
|
70
|
+
void queryClient.invalidateQueries({ queryKey, exact: true });
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
queryClient.setQueryData<InfiniteData<NcpSessionsListView>>(
|
|
74
|
+
queryKey,
|
|
75
|
+
(value) => updateSessionPages(
|
|
76
|
+
value,
|
|
77
|
+
(sessions) => upsertNcpSessionSummaryList(sessions, summary)
|
|
78
|
+
)
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function updateNcpSessionRunStatusPages(
|
|
84
|
+
queryClient: QueryClient | undefined,
|
|
85
|
+
payload: { sessionKey: string; status: 'running' | 'idle' }
|
|
86
|
+
): void {
|
|
87
|
+
queryClient?.setQueriesData<InfiniteData<NcpSessionsListView>>(
|
|
88
|
+
{ queryKey: ['ncp-session-pages'] },
|
|
89
|
+
(current) => updateSessionPages(
|
|
90
|
+
current,
|
|
91
|
+
(sessions) => updateNcpSessionRunStatusList(sessions, payload)
|
|
92
|
+
)
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
|
|
29
96
|
export function upsertNcpSessionSummaryList(
|
|
30
97
|
current: NcpSessionsListView | undefined,
|
|
31
98
|
summary: NcpSessionSummaryView
|
|
@@ -149,13 +216,14 @@ export function applyNcpSessionRealtimeEvent(
|
|
|
149
216
|
queryClient: QueryClient | undefined,
|
|
150
217
|
event: Extract<WsEvent, { type: 'session.run-status' | 'session.summary.upsert' | 'session.summary.delete' }>
|
|
151
218
|
): void {
|
|
152
|
-
void queryClient?.invalidateQueries({ queryKey: ['ncp-session-pages'] });
|
|
153
219
|
if (event.type === 'session.run-status') {
|
|
154
220
|
updateNcpSessionRunStatusInQueryClient(queryClient, event.payload);
|
|
221
|
+
updateNcpSessionRunStatusPages(queryClient, event.payload);
|
|
155
222
|
return;
|
|
156
223
|
}
|
|
157
224
|
if (event.type === 'session.summary.upsert') {
|
|
158
225
|
upsertNcpSessionSummaryInQueryClient(queryClient, event.payload.summary);
|
|
226
|
+
updateExistingNcpSessionSummaryPages(queryClient, event.payload.summary);
|
|
159
227
|
void queryClient?.invalidateQueries({
|
|
160
228
|
queryKey: ['ncp-session-token-usage', event.payload.summary.sessionId],
|
|
161
229
|
exact: true,
|
|
@@ -164,4 +232,5 @@ export function applyNcpSessionRealtimeEvent(
|
|
|
164
232
|
}
|
|
165
233
|
|
|
166
234
|
deleteNcpSessionSummaryInQueryClient(queryClient, event.payload.sessionKey);
|
|
235
|
+
void queryClient?.invalidateQueries({ queryKey: ['ncp-session-pages'] });
|
|
167
236
|
}
|