@nextclaw/ui 0.25.4 → 0.26.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 +61 -0
- package/dist/.vite/manifest.json +1158 -1392
- package/dist/assets/{abnfDiagram-VRR7QNED-DX_u5BBX.js → abnfDiagram-VRR7QNED-Dcw10Yt1.js} +1 -1
- package/dist/assets/account-panel-ZM2qzKF4.js +1 -0
- package/dist/assets/{aiden0z-pptx-renderer.es-C-E9e5yB.js → aiden0z-pptx-renderer.es--oNqetPA.js} +1 -1
- package/dist/assets/api-Cjq-o1VN.js +20 -0
- package/dist/assets/app-presenter-provider-C02YPoMr.js +124 -0
- package/dist/assets/appearance-settings-page-B4zouKzg.js +1 -0
- package/dist/assets/{arc-Cxwa624o.js → arc-DBRedCax.js} +1 -1
- package/dist/assets/architecture-TIHT7OUA-CvnC-LeA.js +1 -0
- package/dist/assets/{architectureDiagram-ZJ3FMSHR-D8mCj0H1.js → architectureDiagram-ZJ3FMSHR-Dufhtuv-.js} +1 -1
- package/dist/assets/{blockDiagram-677ZJIJ3-CbGC1IHl.js → blockDiagram-677ZJIJ3-BUk0vtJ7.js} +1 -1
- package/dist/assets/{book-open-xqEj1OLE.js → book-open-AteA89HX.js} +1 -1
- package/dist/assets/{c4Diagram-LMCZKHZV-BQhGC1Vc.js → c4Diagram-LMCZKHZV-CxDcVE9p.js} +1 -1
- package/dist/assets/card-BLXvBstA.js +1 -0
- package/dist/assets/channel-CpMstUPF.js +1 -0
- package/dist/assets/channels-list-page-fj8kMo-T.js +8 -0
- package/dist/assets/chunk-2Q5K7J3B-Djp3VF0s.js +1 -0
- package/dist/assets/{chunk-32BRIVSS-B74ZZwYG.js → chunk-32BRIVSS-DSF3KElA.js} +1 -1
- package/dist/assets/{chunk-52WLFC77-BvmxW_f_.js → chunk-52WLFC77-Costxzr3.js} +1 -1
- package/dist/assets/{chunk-5FCAYU7R-DQvBRnwL.js → chunk-5FCAYU7R-rhqzGGZo.js} +1 -1
- package/dist/assets/{chunk-5HE753X5-CDIH0_y3.js → chunk-5HE753X5-BsEsmIao.js} +1 -1
- package/dist/assets/{chunk-5JV3BV7I-CcevlKd-.js → chunk-5JV3BV7I-Dqw_DCoN.js} +1 -1
- package/dist/assets/{chunk-5TONJI2A-_3dxVdnY.js → chunk-5TONJI2A-Cft82fp8.js} +1 -1
- package/dist/assets/{chunk-5VM5RSS4-pGO5vvo-.js → chunk-5VM5RSS4-DqNHn9Dl.js} +1 -1
- package/dist/assets/{chunk-7BUUIJ7U-DEKF5uAN.js → chunk-7BUUIJ7U-A5SBEMYY.js} +1 -1
- package/dist/assets/{chunk-BIQX33UG-CjBFgq2r.js → chunk-BIQX33UG-1Ng3mTrH.js} +1 -1
- package/dist/assets/{chunk-C7G6YPKG-e_K9OGTz.js → chunk-C7G6YPKG-DRJEy8ZI.js} +1 -1
- package/dist/assets/{chunk-CQNSW5MT-C2IiHyHz.js → chunk-CQNSW5MT-Bydv_J3J.js} +1 -1
- package/dist/assets/{chunk-CYSBUYHQ-DCC6KmGp.js → chunk-CYSBUYHQ-HkMIg12u.js} +1 -1
- package/dist/assets/{chunk-EMLP6XTP-BYGgekQ0.js → chunk-EMLP6XTP-C5ndDvm3.js} +1 -1
- package/dist/assets/{chunk-EX3LRPZG-FGcwuVsA.js → chunk-EX3LRPZG-DzeloZRo.js} +1 -1
- package/dist/assets/{chunk-FWX5IMBZ-CV3Ih_Os.js → chunk-FWX5IMBZ-n_2_CLTQ.js} +2 -2
- package/dist/assets/{chunk-HOUHSVGY-CXpq_hfV.js → chunk-HOUHSVGY-kx_slvu3.js} +1 -1
- package/dist/assets/{chunk-ICXQ74PX-CZIpjupq.js → chunk-ICXQ74PX-sQLMURqL.js} +1 -1
- package/dist/assets/{chunk-JG7HCLWE-CdgwBpW7.js → chunk-JG7HCLWE-CMUn5Elc.js} +1 -1
- package/dist/assets/{chunk-JWPE2WC7-BPjAZpOQ.js → chunk-JWPE2WC7-Cra3s-Uk.js} +1 -1
- package/dist/assets/{chunk-MOJQB5TN-DbJDm8to.js → chunk-MOJQB5TN-umcrg5Bi.js} +1 -1
- package/dist/assets/{chunk-MOZMSUNE-BUa1fqKn.js → chunk-MOZMSUNE-BnljD1ig.js} +1 -1
- package/dist/assets/{chunk-OGEWGWER-CtnzLgRu.js → chunk-OGEWGWER-BXx5pQjU.js} +1 -1
- package/dist/assets/{chunk-OSBZ3O6U-fUgdpTUM.js → chunk-OSBZ3O6U-D-1qfdWu.js} +1 -1
- package/dist/assets/{chunk-PUDLZKDR-CtDWZT9-.js → chunk-PUDLZKDR-DLzX78LC.js} +1 -1
- package/dist/assets/{chunk-Q4XR5HBZ-BxnK2qxr.js → chunk-Q4XR5HBZ-DTK-rtW6.js} +1 -1
- package/dist/assets/{chunk-QBLGF6JB-CH4UKoCF.js → chunk-QBLGF6JB-CsFeIGfr.js} +1 -1
- package/dist/assets/{chunk-R7FJI6CG-BxwFoF1v.js → chunk-R7FJI6CG-4xNYUeng.js} +1 -1
- package/dist/assets/{chunk-RYQCIY6F-DpHAmq7O.js → chunk-RYQCIY6F-DJiHiTE5.js} +1 -1
- package/dist/assets/{chunk-U6XO7XAA-BjH2VUHU.js → chunk-U6XO7XAA-BVYkauHz.js} +1 -1
- package/dist/assets/{chunk-V7JOEXUC-DKJZ3HGj.js → chunk-V7JOEXUC-BsazE8cS.js} +1 -1
- package/dist/assets/chunk-VAUOI2AC-BrbiIC1Q.js +1 -0
- package/dist/assets/{chunk-VR4S4FIN-Cf-o2GtI.js → chunk-VR4S4FIN-7RoZQffO.js} +1 -1
- package/dist/assets/{chunk-WYO6CB5R-zeNXGm20.js → chunk-WYO6CB5R-DEuS6N1R.js} +3 -3
- package/dist/assets/chunk-XXDRQBXY-Cx9N40wF.js +1 -0
- package/dist/assets/{chunk-YOTPTUD7-Ck87Kqxe.js → chunk-YOTPTUD7-9KBrosZL.js} +1 -1
- package/dist/assets/{chunk-ZGVPDNZ5-BbfK6Bfd.js → chunk-ZGVPDNZ5-IYgN9f5A.js} +1 -1
- package/dist/assets/{chunk-ZIRB5QZD-BDpzh4Yl.js → chunk-ZIRB5QZD-BUo0-zcp.js} +1 -1
- package/dist/assets/{circle-dashed-CU3yApvP.js → circle-dashed-LSYZTzM7.js} +1 -1
- package/dist/assets/classDiagram-OUVF2IWQ-DFN1v3PQ.js +1 -0
- package/dist/assets/classDiagram-v2-EOCWNBFH-DRvglADa.js +1 -0
- package/dist/assets/client-DudCRJw4.js +1 -0
- package/dist/assets/config-split-page-G7Kt2BnV.js +1 -0
- package/dist/assets/confirm-dialog-DShT64F6.js +1 -0
- package/dist/assets/context-menu-rd7b_kVD.js +1 -0
- package/dist/assets/{cose-bilkent-JH36ORCC-CQfMxKCT.js → cose-bilkent-JH36ORCC-DhqbC1fR.js} +1 -1
- package/dist/assets/createLucideIcon-D7hvIHV-.js +1 -0
- package/dist/assets/cynefin-VYW2F7L2-DXUGnpJH.js +1 -0
- package/dist/assets/{cynefinDiagram-TSTJHNR4-CxFTiC-p.js → cynefinDiagram-TSTJHNR4-C-TBRi5V.js} +1 -1
- package/dist/assets/{dagre-CaBNMcB1.js → dagre-CrgsRasE.js} +1 -1
- package/dist/assets/{dagre-VKFMJZFB-S_7Hc7bI.js → dagre-VKFMJZFB-BmafJymp.js} +1 -1
- package/dist/assets/desktop-capabilities-page-CzqzFZUY.js +1 -0
- package/dist/assets/desktop-update-config-CgeQ0Ffo.js +1 -0
- package/dist/assets/{diagram-FQU43EPY-C7zENvFb.js → diagram-FQU43EPY-BoOMwa6D.js} +1 -1
- package/dist/assets/{diagram-G47NLZAW-DqYTCqZA.js → diagram-G47NLZAW-Le7N6Uym.js} +1 -1
- package/dist/assets/{diagram-NH7WQ7WH-BfSJtXRc.js → diagram-NH7WQ7WH-Fbpj3-MO.js} +1 -1
- package/dist/assets/{diagram-OA4YK3LP-CFO-j67H.js → diagram-OA4YK3LP-D3QuGGuu.js} +1 -1
- package/dist/assets/{diagram-WEI45ONY-DBQDbTaJ.js → diagram-WEI45ONY-Cxu1uiN6.js} +1 -1
- package/dist/assets/dialog-C7glmAQJ.js +5 -0
- package/dist/assets/doc-browser-context-BlhBAOLH.js +1 -0
- package/dist/assets/doc-browser-dBiR9Qi0.js +1 -0
- package/dist/assets/{docx-preview-B4-aDfvO.js → docx-preview-CkU2psnd.js} +1 -1
- package/dist/assets/download-BYgECE0n.js +1 -0
- package/dist/assets/{ebnfDiagram-CCIWWBDH-CfDNIXFN.js → ebnfDiagram-CCIWWBDH-CCAjuQM6.js} +1 -1
- package/dist/assets/{erDiagram-Q63AITRT-D95rFXMM.js → erDiagram-Q63AITRT-BuzUAq-d.js} +1 -1
- package/dist/assets/es2015-CsjAAQ6X.js +41 -0
- package/dist/assets/eventmodeling-45OFAUF4-ZBfVS3ZC.js +1 -0
- package/dist/assets/extensions-qYL-NpL3.js +1 -0
- package/dist/assets/{eye-C84inPcV.js → eye-BBuTzJ53.js} +1 -1
- package/dist/assets/flowDiagram-23GEKE2U-CqdsKk6x.js +1 -0
- package/dist/assets/form-actions-CLGWO52v.js +1 -0
- package/dist/assets/{ganttDiagram-NO4QXBWP-Bawg0P0r.js → ganttDiagram-NO4QXBWP-cJmWVY8F.js} +1 -1
- package/dist/assets/gitGraph-TEB2WS4Q-fHYFd9Es.js +1 -0
- package/dist/assets/{gitGraphDiagram-IHSO6WYX-BbKh9nbL.js → gitGraphDiagram-IHSO6WYX-F58o-vtP.js} +1 -1
- package/dist/assets/{i18n-20rSH40D.js → i18n-qBGEbcW6.js} +2 -2
- package/dist/assets/info-DKCQHKI2-CSaOL_bb.js +1 -0
- package/dist/assets/{infoDiagram-FWYZ7A6U-DPffZXYh.js → infoDiagram-FWYZ7A6U-Gt59b83C.js} +1 -1
- package/dist/assets/{ishikawaDiagram-FXEZZL3T-CBMsLwZ2.js → ishikawaDiagram-FXEZZL3T-1u4IAj1I.js} +1 -1
- package/dist/assets/{journeyDiagram-5HDEW3XC-Mz5U4OzU.js → journeyDiagram-5HDEW3XC-CCQsfV-5.js} +1 -1
- package/dist/assets/{kanban-definition-HUTT4EX6-FXZq2k19.js → kanban-definition-HUTT4EX6-vlB6O_Zn.js} +1 -1
- package/dist/assets/katex-B4FQSo9k.js +1 -0
- package/dist/assets/keyboard-shortcuts-settings-page-B-wK_6zG.js +1 -0
- package/dist/assets/{line-BZwCgT9K.js → line-D_bHubTL.js} +1 -1
- package/dist/assets/{linear-CTe9mKUw.js → linear-ChlwuS5y.js} +1 -1
- package/dist/assets/loader-circle-BA7EMkih.js +1 -0
- package/dist/assets/login-page-Cd84lpVV.js +1 -0
- package/dist/assets/login-page-VdMyNkFf.js +1 -0
- package/dist/assets/main-CR0SrTBf.js +2 -0
- package/dist/assets/main-DXH6EdXI.css +1 -0
- package/dist/assets/main-i18n-catalog.config-Btp_OowF.js +1 -0
- package/dist/assets/{map-Ci48ZwAp.js → map-oGHTsIu8.js} +1 -1
- package/dist/assets/mcp-marketplace-page-jacRwiI-.js +1 -0
- package/dist/assets/{mermaid-parser.core-CzPD3U_z.js → mermaid-parser.core-CuRe1ZG4.js} +2 -2
- package/dist/assets/{mermaid.core-geIMNPbV.js → mermaid.core-u4YCFKRf.js} +3 -3
- package/dist/assets/{mindmap-definition-LN4V7U3C-xqlKxsr_.js → mindmap-definition-LN4V7U3C-DeJAQYTu.js} +1 -1
- package/dist/assets/model-config-page-D0DQlSDa.js +1 -0
- package/dist/assets/navigation-history-CzErJuPG.js +1 -0
- package/dist/assets/navigation-link-32iM1-e6.js +3 -0
- package/dist/assets/{ordinal-MxnX8yhv.js → ordinal-C6JFfkKN.js} +1 -1
- package/dist/assets/packet-7NZHBO7P-D-YFTczs.js +1 -0
- package/dist/assets/panel-app-CC0bVSu4.js +1 -0
- package/dist/assets/panel-app-i18n-catalog.config-LUSBp5Ce.js +1 -0
- package/dist/assets/panelStandalone-C9EKaApN.js +2 -0
- package/dist/assets/{pegDiagram-2B236MQR-D50MFTN1.js → pegDiagram-2B236MQR-nVMkxShH.js} +1 -1
- package/dist/assets/pie-RZYD4A2V-VEnYu9s5.js +1 -0
- package/dist/assets/{pieDiagram-ENE6RG2P-CpA13oiC.js → pieDiagram-ENE6RG2P-8aUH44QZ.js} +1 -1
- package/dist/assets/privacy-settings-page-Bxxx12OO.js +1 -0
- package/dist/assets/projects-D56yAPNT.js +1 -0
- package/dist/assets/provider-models-C3frdXeW.js +1 -0
- package/dist/assets/provider-scoped-model-input-CYetrPYZ.js +1 -0
- package/dist/assets/providers-config-page-DESB1h8g.js +1 -0
- package/dist/assets/{puzzle-D6gulz7u.js → puzzle-CWHfFyYN.js} +1 -1
- package/dist/assets/pwa-CiGUjpcd.js +1 -0
- package/dist/assets/{quadrantDiagram-ABIIQ3AL-CYCgaj4l.js → quadrantDiagram-ABIIQ3AL-EY8hpFv8.js} +1 -1
- package/dist/assets/radar-I7S5WNFK-BFNIekC1.js +1 -0
- package/dist/assets/railroad-3IZDKUUU-lVqJ3Hak.js +1 -0
- package/dist/assets/railroad-abnf-AHOZXSZD-DYaefdFC.js +1 -0
- package/dist/assets/railroad-ebnf-EBAXGLYW-CTAk6lVq.js +1 -0
- package/dist/assets/railroad-peg-LSFZ7HO6-Cob294tg.js +1 -0
- package/dist/assets/{railroadDiagram-RFXS5EU6-4LKDGlXN.js → railroadDiagram-RFXS5EU6-CNhcIeji.js} +1 -1
- package/dist/assets/{refresh-cw-h9Xq3LY3.js → refresh-cw-y3L6PyLt.js} +1 -1
- package/dist/assets/remote-DiGFV8nP.js +1 -0
- package/dist/assets/{requirementDiagram-TGXJPOKE-CxNOU0JH.js → requirementDiagram-TGXJPOKE-D8Dcs-Rt.js} +1 -1
- package/dist/assets/{rotate-cw-B6xm41Wn.js → rotate-cw-CNE9qSHi.js} +1 -1
- package/dist/assets/runtime-config-page-5o98T-TD.js +1 -0
- package/dist/assets/{sankeyDiagram-HTMAVEWB-DbCBDe2S.js → sankeyDiagram-HTMAVEWB-HTrb6aa-.js} +1 -1
- package/dist/assets/{save-fff_WpY2.js → save-B0p5a0ET.js} +1 -1
- package/dist/assets/search-config-page-CpGcnIzi.js +1 -0
- package/dist/assets/secrets-config-page-C4VTiW3r.js +3 -0
- package/dist/assets/security-config-C5-RQVpS.js +1 -0
- package/dist/assets/select-CctTN205.js +1 -0
- package/dist/assets/{sequenceDiagram-DBY2YBRQ-DA6W2BS_.js → sequenceDiagram-DBY2YBRQ-6f_4X16Y.js} +1 -1
- package/dist/assets/service-apps-panel-CKE5HKNY.js +1 -0
- package/dist/assets/{settings-2-CE3ELQyk.js → settings-2-EPflrNk6.js} +1 -1
- package/dist/assets/{sizeCapture-X5ZJPWSS-T_q_d_VW.js → sizeCapture-X5ZJPWSS-Ck9rsuwL.js} +1 -1
- package/dist/assets/{src-CYpUlx-4.js → src-C8AUg09O.js} +1 -1
- package/dist/assets/{stateDiagram-2N3HPSRC-mi8z2G0a.js → stateDiagram-2N3HPSRC-Cm33MLHu.js} +1 -1
- package/dist/assets/stateDiagram-v2-6OUMAXLB-BrNv-raG.js +1 -0
- package/dist/assets/status-dot-CensgJZC.js +1 -0
- package/dist/assets/{swimlanes-5IMT3BWC-DkBEhWE4.js → swimlanes-5IMT3BWC-B107vmvM.js} +2 -2
- package/dist/assets/swimlanesDiagram-G3AALYLV-ntcxaPXp.js +8 -0
- package/dist/assets/tag-chip-Bjzb4-qW.js +1 -0
- package/dist/assets/theme-provider-hlle7Skk.js +1 -0
- package/dist/assets/{timeline-definition-FHXFAJF6-BoFmqBw1.js → timeline-definition-FHXFAJF6-CJEoAj33.js} +1 -1
- package/dist/assets/trash-2-C5KH5Rw9.js +1 -0
- package/dist/assets/treeView-QDETBFTQ-DbFmr-gw.js +1 -0
- package/dist/assets/treemap-6X3UGDF4-C8aY96pi.js +1 -0
- package/dist/assets/use-config-CPl_F79t.js +1 -0
- package/dist/assets/use-desktop-capabilities-CEg9-DrQ.js +1 -0
- package/dist/assets/use-viewport-layout-CPZUmTPi.js +1 -0
- package/dist/assets/{vennDiagram-L72KCM5P-BlZ3Or5U.js → vennDiagram-L72KCM5P-DzsUHk5I.js} +1 -1
- package/dist/assets/wardley-OPB4EBWU-D8d6DbbJ.js +1 -0
- package/dist/assets/{wardleyDiagram-EHGQE667-yDfr33i3.js → wardleyDiagram-EHGQE667-1NfFs5ua.js} +1 -1
- package/dist/assets/x--xxUvHKW.js +1 -0
- package/dist/assets/{xychartDiagram-FW5EYKEG-mIXwIGAQ.js → xychartDiagram-FW5EYKEG-Ci8AI3Qo.js} +1 -1
- package/dist/index.html +38 -54
- package/dist/panel-standalone.html +11 -15
- package/package.json +8 -8
- package/src/app/components/layout/__tests__/app-layout.test.tsx +7 -0
- package/src/app/components/layout/__tests__/sidebar.layout.test.tsx +36 -16
- package/src/app/components/layout/app-layout.tsx +18 -46
- package/src/app/components/layout/sidebar-items.tsx +27 -18
- package/src/app/components/layout/sidebar-rail.styles.ts +11 -2
- package/src/app/components/layout/sidebar.tsx +3 -2
- package/src/app/configs/__tests__/app-navigation.config.test.ts +4 -0
- package/src/app/configs/app-navigation.config.ts +41 -86
- package/src/app/configs/page-resource-renderers.config.ts +13 -0
- package/src/app/index.tsx +4 -0
- package/src/app/presenters/app.presenter.ts +13 -2
- package/src/features/agents/components/__tests__/agents-page.test.tsx +4 -0
- package/src/features/agents/components/agent-details-dialog.tsx +5 -2
- package/src/features/agents/components/agents-page.tsx +9 -4
- package/src/features/apps/components/app-package-card.tsx +2 -2
- package/src/features/chat/components/conversation/chat-conversation-content.tsx +32 -19
- package/src/features/chat/components/conversation/chat-conversation-header-section.tsx +1 -0
- package/src/features/chat/components/conversation/chat-conversation-header.tsx +1 -1
- package/src/features/chat/components/conversation/chat-conversation-panel.tsx +2 -2
- package/src/features/chat/components/conversation/chat-conversation-workspace-section.tsx +1 -0
- package/src/features/chat/components/layout/__tests__/chat-page-shell.test.tsx +5 -6
- package/src/features/chat/components/layout/__tests__/chat-sidebar-toolbar.test.tsx +14 -12
- package/src/features/chat/components/layout/chat-page-shell.tsx +31 -64
- package/src/features/chat/components/layout/chat-sidebar-desktop-layout.tsx +8 -5
- package/src/features/chat/components/layout/chat-sidebar-toolbar.tsx +64 -256
- package/src/features/chat/components/layout/chat-sidebar.tsx +8 -30
- package/src/features/chat/components/providers/chat-runtime.provider.tsx +62 -0
- package/src/features/chat/features/conversation/components/__tests__/floating-session-conversation.test.tsx +70 -0
- package/src/features/chat/features/conversation/components/chat-voice-input-panel.tsx +3 -2
- package/src/features/chat/features/conversation/components/floating-session-conversation.tsx +27 -0
- package/src/features/chat/features/conversation/components/session-conversation-area.tsx +1 -0
- package/src/features/chat/features/conversation/components/session-conversation-panel-tab.tsx +25 -0
- package/src/features/chat/features/conversation/hooks/__tests__/use-chat-voice-input.test.tsx +4 -0
- package/src/features/chat/features/conversation/hooks/use-chat-composer-reference-intent.ts +2 -1
- package/src/features/chat/features/message/components/__tests__/chat-inline-panel-app-card.test.tsx +2 -1
- package/src/features/chat/features/message/components/__tests__/chat-message-assistant-header.container.test.tsx +3 -0
- package/src/features/chat/features/message/components/__tests__/chat-message-list-skill-preview.container.test.tsx +3 -0
- package/src/features/chat/features/message/components/__tests__/chat-message-list.container.test.tsx +35 -47
- package/src/features/chat/features/message/components/chat-message-list.container.tsx +6 -10
- package/src/features/chat/features/message/hooks/use-chat-inline-token-actions.ts +7 -3
- package/src/features/chat/features/message/utils/__tests__/chat-message-process-summary.utils.test.ts +52 -6
- package/src/features/chat/features/message/utils/chat-message-process-summary.utils.ts +22 -37
- package/src/features/chat/features/session/components/__tests__/chat-sidebar-project-groups.test.tsx +1 -1
- package/src/features/chat/features/session/components/__tests__/chat-sidebar-session-item.test.tsx +21 -0
- package/src/features/chat/features/session/components/chat-mobile-session-row.tsx +61 -0
- package/src/features/chat/features/session/components/chat-sidebar-project-groups.tsx +4 -37
- package/src/features/chat/features/session/components/chat-sidebar-session-entry.tsx +2 -0
- package/src/features/chat/features/session/components/chat-sidebar-session-item.tsx +18 -7
- package/src/features/chat/features/session/components/chat-sidebar-session-list.tsx +5 -3
- package/src/features/chat/features/session/components/session-header/__tests__/chat-session-header-actions.test.tsx +10 -2
- package/src/features/chat/features/session/components/session-header/chat-session-header-actions.tsx +20 -17
- package/src/features/chat/features/session/components/session-header/chat-session-header-menu-item.tsx +4 -4
- package/src/features/chat/features/session/components/session-header/chat-session-more-actions-menu.tsx +18 -5
- package/src/features/chat/features/session/utils/chat-session-route.utils.ts +12 -0
- package/src/features/chat/features/workspace/components/__tests__/chat-session-child-sessions.test.tsx +28 -0
- package/src/features/chat/features/workspace/components/__tests__/chat-session-workspace-file-preview.test.tsx +9 -0
- package/src/features/chat/features/workspace/components/__tests__/chat-session-workspace-panel-content.test.tsx +6 -2
- package/src/features/chat/features/workspace/components/__tests__/chat-session-workspace-panel.test.tsx +49 -12
- package/src/features/chat/features/workspace/components/chat-session-workspace-file-preview.tsx +13 -6
- package/src/features/chat/features/workspace/components/chat-session-workspace-panel-content.tsx +6 -0
- package/src/features/chat/features/workspace/components/chat-session-workspace-panel-nav.tsx +21 -36
- package/src/features/chat/features/workspace/components/chat-session-workspace-panel.tsx +40 -31
- package/src/features/chat/features/workspace/components/child-sessions/chat-session-child-sessions.tsx +6 -1
- package/src/features/chat/features/workspace/components/project-files/workspace-directory-tree.tsx +2 -2
- package/src/features/chat/features/workspace/components/project-files/workspace-project-files-toolbar.tsx +2 -2
- package/src/features/chat/features/workspace/utils/__tests__/chat-workspace-panel-view-model.utils.test.ts +27 -0
- package/src/features/chat/features/workspace/utils/__tests__/workspace-file-panel-route.utils.test.ts +94 -0
- package/src/features/chat/features/workspace/utils/chat-workspace-file-tab-persistence.utils.ts +2 -0
- package/src/features/chat/features/workspace/utils/chat-workspace-file-tab.utils.ts +15 -0
- package/src/features/chat/features/workspace/utils/chat-workspace-panel-view-model.utils.ts +11 -0
- package/src/features/chat/features/workspace/utils/workspace-file-panel-route.utils.ts +147 -0
- package/src/features/chat/index.ts +20 -4
- package/src/features/chat/managers/__tests__/chat-completion-notification.manager.test.ts +8 -0
- package/src/features/chat/managers/__tests__/chat-thread-workspace.manager.test.ts +7 -2
- package/src/features/chat/managers/__tests__/session-surface.manager.test.ts +45 -0
- package/src/features/chat/managers/chat-completion-notification.manager.ts +5 -0
- package/src/features/chat/managers/chat-composer-intent.manager.ts +16 -0
- package/src/features/chat/managers/chat-thread.manager.ts +18 -27
- package/src/features/chat/managers/chat-voice-input.manager.ts +5 -3
- package/src/features/chat/managers/session-surface.manager.ts +46 -0
- package/src/features/chat/pages/__tests__/ncp-chat-page.test.tsx +23 -5
- package/src/features/chat/pages/ncp-chat-page.tsx +6 -86
- package/src/features/chat/presenters/chat.presenter.ts +2 -1
- package/src/features/chat/stores/__tests__/chat-thread.store.test.ts +2 -2
- package/src/features/chat/stores/chat-thread.store.ts +7 -1
- package/src/features/chat/stores/floating-session.store.ts +11 -0
- package/src/features/chat/utils/chat-voice-permissions.utils.test.ts +12 -1
- package/src/features/chat/utils/chat-voice-permissions.utils.ts +6 -1
- package/src/features/cron/components/__tests__/cron-config.test.tsx +4 -0
- package/src/features/cron/components/cron-job-detail-dialog.tsx +3 -1
- package/src/features/cron/components/cron-job-row.tsx +14 -8
- package/src/features/inbox/components/inbox-reader-dialog.test.tsx +3 -0
- package/src/features/inbox/components/inbox-reader-dialog.tsx +6 -1
- package/src/features/inbox/pages/inbox-page.test.ts +3 -0
- package/src/features/inbox/pages/inbox-page.tsx +12 -4
- package/src/features/marketplace/components/__tests__/marketplace-page-detail.test.tsx +5 -1
- package/src/features/marketplace/components/__tests__/marketplace-page.test.tsx +3 -0
- package/src/features/marketplace/components/curated-shelves/__tests__/marketplace-curated-scene-route.test.tsx +2 -0
- package/src/features/marketplace/components/marketplace-list-card.tsx +4 -0
- package/src/features/marketplace/components/mcp/__tests__/mcp-marketplace-page.test.tsx +2 -0
- package/src/features/marketplace/components/mcp/mcp-marketplace-card.tsx +3 -0
- package/src/features/notifications/components/__tests__/app-notification-toast.test.tsx +19 -0
- package/src/features/notifications/components/app-notification-toast.tsx +33 -15
- package/src/features/notifications/managers/app-notification.manager.ts +2 -0
- package/src/features/panel-apps/components/__tests__/panel-app-list-item.test.tsx +13 -4
- package/src/features/panel-apps/components/__tests__/panel-app-toolbar.test.tsx +13 -5
- package/src/features/panel-apps/components/panel-app-list-item.tsx +1 -1
- package/src/features/panel-apps/components/panel-app-main-sidebar-menu-item.tsx +5 -38
- package/src/features/panel-apps/components/panel-app-toolbar.tsx +7 -7
- package/src/features/panel-apps/index.ts +1 -1
- package/src/features/projects/components/project-materials.test.tsx +2 -0
- package/src/features/projects/components/project-skills.tsx +7 -3
- package/src/features/projects/components/work/project-work-item-drawer.test.tsx +2 -0
- package/src/features/projects/components/work/project-work-item-drawer.tsx +3 -1
- package/src/features/right-panel-resources/components/__tests__/page-resource-icon.test.tsx +67 -0
- package/src/features/right-panel-resources/components/__tests__/page-resource-sidebar-nav.test.tsx +183 -0
- package/src/features/right-panel-resources/components/__tests__/system-object-resource.test.tsx +42 -0
- package/src/features/right-panel-resources/components/page-resource-actions-menu.tsx +53 -0
- package/src/features/right-panel-resources/components/page-resource-icon.tsx +245 -0
- package/src/features/right-panel-resources/components/page-resource-sidebar-nav.tsx +119 -0
- package/src/features/right-panel-resources/components/system-object-resource.tsx +100 -0
- package/src/features/right-panel-resources/components/workspace-file-resource.tsx +56 -0
- package/src/features/right-panel-resources/configs/right-panel-resource-routes.config.ts +46 -3
- package/src/features/right-panel-resources/hooks/use-page-resource-actions.ts +190 -0
- package/src/features/right-panel-resources/index.ts +21 -8
- package/src/features/right-panel-resources/managers/__tests__/page-resource.manager.test.ts +149 -0
- package/src/features/right-panel-resources/managers/page-resource.manager.ts +342 -0
- package/src/features/right-panel-resources/pages/resource-page.tsx +114 -0
- package/src/features/right-panel-resources/stores/page-navigation.store.ts +55 -0
- package/src/features/right-panel-resources/types/page-resource.types.ts +11 -0
- package/src/features/right-panel-resources/utils/__tests__/right-panel-resource-route-resolver.utils.test.ts +10 -0
- package/src/features/right-panel-resources/utils/page-resource-identity.utils.ts +61 -0
- package/src/features/right-panel-resources/utils/right-panel-resource-uri.utils.ts +2 -6
- package/src/features/service-apps/components/__tests__/service-apps-panel.test.tsx +2 -0
- package/src/features/service-apps/components/service-app-list-item.tsx +6 -1
- package/src/features/settings/pages/providers-config-page.tsx +2 -2
- package/src/features/side-dock/components/__tests__/side-dock.test.tsx +9 -8
- package/src/features/side-dock/components/side-dock.tsx +14 -47
- package/src/features/side-dock/managers/side-dock.manager.ts +2 -0
- package/src/features/system-status/components/__tests__/desktop-update-config.test.tsx +40 -0
- package/src/features/system-status/components/desktop-update-config.tsx +19 -1
- package/src/platforms/desktop/components/__tests__/desktop-app-shell.test.tsx +2 -0
- package/src/platforms/desktop/components/desktop-app-shell.tsx +3 -14
- package/src/platforms/mobile/components/__tests__/mobile-app-shell.test.tsx +1 -0
- package/src/platforms/mobile/components/__tests__/mobile-bottom-nav.test.tsx +3 -2
- package/src/platforms/mobile/components/mobile-app-shell.tsx +3 -4
- package/src/platforms/mobile/components/mobile-bottom-nav.tsx +7 -7
- package/src/platforms/mobile/components/mobile-settings-shell.tsx +8 -3
- package/src/platforms/mobile/components/mobile-topbar.tsx +4 -4
- package/src/shared/components/doc-browser/__tests__/doc-browser-context.test.tsx +4 -3
- package/src/shared/components/doc-browser/__tests__/doc-browser-tab-strip.test.tsx +0 -5
- package/src/shared/components/doc-browser/__tests__/doc-browser.test.tsx +57 -73
- package/src/shared/components/doc-browser/__tests__/resource-link-interceptor.test.tsx +104 -0
- package/src/shared/components/doc-browser/doc-browser-context.tsx +5 -1
- package/src/shared/components/doc-browser/doc-browser-panel-parts.tsx +1 -1
- package/src/shared/components/doc-browser/doc-browser-tab-strip.tsx +6 -118
- package/src/shared/components/doc-browser/doc-browser.tsx +30 -194
- package/src/shared/components/doc-browser/hooks/use-doc-browser-scroll-restoration.ts +1 -1
- package/src/shared/components/doc-browser/managers/doc-browser.manager.ts +11 -1
- package/src/shared/components/doc-browser/stores/doc-browser.store.ts +3 -7
- package/src/shared/components/doc-browser/types/doc-browser.types.ts +8 -1
- package/src/shared/components/doc-browser/use-doc-link-interceptor.ts +30 -32
- package/src/shared/components/doc-browser/utils/doc-browser-state.utils.ts +6 -6
- package/src/shared/components/ui/context-menu/__tests__/context-menu.test.tsx +105 -1
- package/src/shared/components/ui/context-menu/context-menu-items.tsx +173 -0
- package/src/shared/components/ui/context-menu/context-menu.tsx +99 -112
- package/src/shared/components/ui/dialog.tsx +4 -0
- package/src/shared/components/ui/overlays/sheet.tsx +4 -0
- package/src/shared/components/ui/popover.tsx +10 -4
- package/src/shared/components/workbench/__tests__/workbench-surface.manager.test.ts +70 -0
- package/src/shared/components/workbench/__tests__/workbench-surface.test.tsx +111 -0
- package/src/shared/components/workbench/managers/workbench-surface.manager.ts +120 -0
- package/src/shared/components/workbench/stores/workbench-surface.store.ts +83 -0
- package/src/shared/components/workbench/types/workbench-surface.types.ts +32 -0
- package/src/shared/components/workbench/utils/workbench-surface-geometry.utils.ts +46 -0
- package/src/shared/components/workbench/workbench-surface-toolbar.tsx +102 -0
- package/src/shared/components/workbench/workbench-surface.tsx +401 -0
- package/src/shared/hooks/use-panel-app-scroll-restoration.test.tsx +19 -0
- package/src/shared/hooks/use-panel-app-scroll-restoration.ts +8 -6
- package/src/shared/hooks/use-scroll-restoration.test.tsx +39 -2
- package/src/shared/hooks/use-scroll-restoration.ts +33 -33
- package/src/shared/lib/api/marketplace.types.ts +1 -0
- package/src/shared/lib/i18n/locales/en-US/chat.json +46 -1
- package/src/shared/lib/i18n/locales/en-US/core.json +3 -1
- package/src/shared/lib/i18n/locales/en-US/desktop-update.json +1 -0
- package/src/shared/lib/i18n/locales/en-US/doc-browser.json +1 -1
- package/src/shared/lib/i18n/locales/zh-CN/chat.json +46 -1
- package/src/shared/lib/i18n/locales/zh-CN/core.json +11 -9
- package/src/shared/lib/i18n/locales/zh-CN/desktop-update.json +1 -0
- package/src/shared/lib/navigation-history/index.ts +1 -0
- package/src/shared/lib/navigation-history/navigation-history.utils.ts +1 -1
- package/src/shared/lib/navigation-history/scroll-restoration.manager.test.ts +11 -0
- package/src/shared/lib/navigation-history/scroll-restoration.manager.ts +36 -7
- package/src/shared/lib/navigation-history/view-memory-storage.store.test.ts +37 -0
- package/src/shared/lib/navigation-history/view-memory-storage.store.ts +21 -0
- package/src/shared/lib/transport/__tests__/browser-realtime-recovery.service.test.ts +27 -2
- package/src/shared/lib/transport/__tests__/local-transport.service.test.ts +20 -0
- package/src/shared/lib/transport/__tests__/remote.transport.test.ts +26 -0
- package/src/shared/lib/transport/browser-realtime-recovery.service.ts +32 -5
- package/src/shared/lib/transport/local-transport.service.ts +1 -6
- package/src/shared/lib/transport/remote-transport.service.ts +22 -1
- package/src/shared/lib/ui-document-title/index.ts +1 -0
- package/dist/assets/account-panel-BW4GbzQd.js +0 -1
- package/dist/assets/api-DqzQ-_3w.js +0 -13
- package/dist/assets/app-data-C54N2p8b.js +0 -1
- package/dist/assets/app-presenter-provider-CBZ_-ewx.js +0 -115
- package/dist/assets/appearance-settings-page-C-uZ2Wkf.js +0 -1
- package/dist/assets/architecture-TIHT7OUA-DEt0Abz9.js +0 -1
- package/dist/assets/boxes-D6uPnkGo.js +0 -1
- package/dist/assets/card-BhfAZv5L.js +0 -1
- package/dist/assets/channel-CxQkbwRD.js +0 -1
- package/dist/assets/channels-list-page-Bv7CVSqV.js +0 -8
- package/dist/assets/chevron-down-BL-yMYcX.js +0 -1
- package/dist/assets/chunk-2Q5K7J3B-ClVhziok.js +0 -1
- package/dist/assets/chunk-VAUOI2AC-DZQxkIsv.js +0 -1
- package/dist/assets/chunk-XXDRQBXY-BkExtw5C.js +0 -1
- package/dist/assets/circle-alert-lSa3LRvS.js +0 -1
- package/dist/assets/classDiagram-OUVF2IWQ-Bd6apBb-.js +0 -1
- package/dist/assets/classDiagram-v2-EOCWNBFH-CYxUZGCI.js +0 -1
- package/dist/assets/client-BtGBwLTs.js +0 -1
- package/dist/assets/config-split-page-DqAXKLmG.js +0 -1
- package/dist/assets/confirm-dialog-BTKCWMn6.js +0 -1
- package/dist/assets/createLucideIcon-BNKf0_kc.js +0 -1
- package/dist/assets/cynefin-VYW2F7L2-CV0bFLH9.js +0 -1
- package/dist/assets/desktop-capabilities-page-zLzCSB7A.js +0 -1
- package/dist/assets/desktop-update-config-PmofcFf6.js +0 -1
- package/dist/assets/dialog-Xe73hpQz.js +0 -5
- package/dist/assets/dist-BiIcnf9j.js +0 -1
- package/dist/assets/dist-C5I0v8Qn.js +0 -1
- package/dist/assets/dist-czPa8xFO.js +0 -1
- package/dist/assets/doc-browser-BJrre2S_.js +0 -1
- package/dist/assets/doc-browser-BPqA-zQx.js +0 -1
- package/dist/assets/doc-browser-context-TILuCY5c.js +0 -1
- package/dist/assets/ellipsis-C5YXdOUx.js +0 -1
- package/dist/assets/es2015-cuwbYI-l.js +0 -41
- package/dist/assets/eventmodeling-45OFAUF4-BzkLWWdP.js +0 -1
- package/dist/assets/extensions-CVUFK6Jj.js +0 -1
- package/dist/assets/flowDiagram-23GEKE2U-ukkCsB0j.js +0 -1
- package/dist/assets/form-actions-BRESe-m_.js +0 -1
- package/dist/assets/gitGraph-TEB2WS4Q-BYFhT0Q5.js +0 -1
- package/dist/assets/i18n-provider-BPRq6S7O.js +0 -1
- package/dist/assets/info-DKCQHKI2-CTmlJdcH.js +0 -1
- package/dist/assets/katex-DbZC06Q5.js +0 -1
- package/dist/assets/keyboard-shortcuts-settings-page-CScSXYS4.js +0 -1
- package/dist/assets/loader-circle-K1BDdHR5.js +0 -1
- package/dist/assets/login-page-Dlyvp-81.js +0 -1
- package/dist/assets/login-page-f8qQ4xKY.js +0 -1
- package/dist/assets/logo-badge-C-5-zim6.js +0 -1
- package/dist/assets/main-D2dWy8fM.css +0 -1
- package/dist/assets/main-DMNmwcJE.js +0 -2
- package/dist/assets/main-i18n-catalog.config-DZ-5ollN.js +0 -1
- package/dist/assets/mcp-marketplace-page-FfIfEo6w.js +0 -10
- package/dist/assets/mcp-marketplace-page-sH7VptN_.js +0 -1
- package/dist/assets/model-config-page-Bcm6-pCX.js +0 -1
- package/dist/assets/navigation-link-BAQjgx2F.js +0 -3
- package/dist/assets/packet-7NZHBO7P-BbrI0ZlN.js +0 -1
- package/dist/assets/panel-app-B39rJPj8.js +0 -1
- package/dist/assets/panel-app-i18n-catalog.config-p3qAY_Eq.js +0 -1
- package/dist/assets/panelStandalone-By-6dDXS.js +0 -2
- package/dist/assets/pie-RZYD4A2V-B7zE1ZIr.js +0 -1
- package/dist/assets/plus-LA8-jpxP.js +0 -1
- package/dist/assets/popover-NkrFlWHp.js +0 -1
- package/dist/assets/privacy-settings-page-B1sHLAc1.js +0 -1
- package/dist/assets/projects-Voql-cvx.js +0 -1
- package/dist/assets/provider-models-hrHMcHqg.js +0 -1
- package/dist/assets/provider-scoped-model-input-BQsugx1v.js +0 -1
- package/dist/assets/providers-config-page-BDEdHJbw.js +0 -1
- package/dist/assets/pwa-CzqfYjFx.js +0 -1
- package/dist/assets/radar-I7S5WNFK-1X_q11Ed.js +0 -1
- package/dist/assets/railroad-3IZDKUUU-CIXnuFyE.js +0 -1
- package/dist/assets/railroad-abnf-AHOZXSZD-D6ImKl2r.js +0 -1
- package/dist/assets/railroad-ebnf-EBAXGLYW-2j9d3twU.js +0 -1
- package/dist/assets/railroad-peg-LSFZ7HO6-DMVaJGbm.js +0 -1
- package/dist/assets/remote-BekqL5nA.js +0 -1
- package/dist/assets/runtime-config-page-BH9uF4u_.js +0 -1
- package/dist/assets/search-CUe41wjm.js +0 -1
- package/dist/assets/search-config-page-DWKlMHgH.js +0 -1
- package/dist/assets/secrets-config-page-DiQL088Y.js +0 -3
- package/dist/assets/security-config-DmTDKKY4.js +0 -1
- package/dist/assets/select-CG5YWnwe.js +0 -1
- package/dist/assets/service-apps-panel-CEnyYsQp.js +0 -1
- package/dist/assets/skeleton-Drvn83YW.js +0 -1
- package/dist/assets/stateDiagram-v2-6OUMAXLB-OlIMT57r.js +0 -1
- package/dist/assets/swimlanesDiagram-G3AALYLV-sLThrh07.js +0 -8
- package/dist/assets/tabs-BuE0qKMh.js +0 -1
- package/dist/assets/tag-chip-B1AYPOKL.js +0 -1
- package/dist/assets/theme-provider-DzvAi3pe.js +0 -1
- package/dist/assets/tooltip-DYcuWhuP.js +0 -1
- package/dist/assets/trash-2-BPPYt2NV.js +0 -1
- package/dist/assets/treeView-QDETBFTQ-CCFyazDE.js +0 -1
- package/dist/assets/treemap-6X3UGDF4-D6wvq1Hz.js +0 -1
- package/dist/assets/use-config-BEdbtpD4.js +0 -1
- package/dist/assets/use-desktop-capabilities-BkuRd2cY.js +0 -1
- package/dist/assets/use-panel-app-scroll-restoration-ZnicIfo3.js +0 -1
- package/dist/assets/use-viewport-layout-BS18drMT.js +0 -1
- package/dist/assets/utils-CBZPzC_b.js +0 -8
- package/dist/assets/wardley-OPB4EBWU-BRzaRyFf.js +0 -1
- package/dist/assets/x-DdXOqLnX.js +0 -1
- package/src/features/panel-apps/components/__tests__/panel-app-main-sidebar-nav.test.tsx +0 -219
- package/src/features/panel-apps/components/panel-app-main-sidebar-nav.tsx +0 -258
- package/src/shared/components/doc-browser/utils/doc-browser-floating-panel.utils.ts +0 -83
- /package/dist/assets/{array-D0DyKZdT.js → array-BV9OtnO8.js} +0 -0
- /package/dist/assets/{chunk-KEIR6QF5-CuZFOa6q.js → chunk-KEIR6QF5-Cx-8EJ4_.js} +0 -0
- /package/dist/assets/{chunk-Y2CYZVJY-Frut-1TF.js → chunk-Y2CYZVJY-Dy1ufvjf.js} +0 -0
- /package/dist/assets/{config-hints-B_C9DAp5.js → config-hints-DbmiyTKg.js} +0 -0
- /package/dist/assets/{cytoscape.esm-BibuhnZD.js → cytoscape.esm-CSCjuT4Z.js} +0 -0
- /package/dist/assets/{defaultLocale-q7sRUXPc.js → defaultLocale-DWTiNsbT.js} +0 -0
- /package/dist/assets/{dist-DhMddrrH.js → dist-BVCSkfWY.js} +0 -0
- /package/dist/assets/{graphlib-BM63WsW3.js → graphlib-B1yb1Wl8.js} +0 -0
- /package/dist/assets/{host-capabilities-B107nwS3.js → host-capabilities-DuIFVsvM.js} +0 -0
- /package/dist/assets/{init-CRMkf9sL.js → init-BF6GH5oe.js} +0 -0
- /package/dist/assets/{jszip.min-6thm2J_a.js → jszip.min-D12teKzP.js} +0 -0
- /package/dist/assets/{katex-B26hhTOT.js → katex-CsAM73R9.js} +0 -0
- /package/dist/assets/{middleware-0o3JF3tS.js → middleware-B--6mAUM.js} +0 -0
- /package/dist/assets/{path-BFMmafCM.js → path-C56XkznJ.js} +0 -0
- /package/dist/assets/{rough.esm-5Tjchx1D.js → rough.esm-CGfeEbtE.js} +0 -0
- /package/dist/assets/{xlsx-BC8ibjXK.js → xlsx-sds5nUpL.js} +0 -0
|
@@ -13,7 +13,9 @@
|
|
|
13
13
|
"advanced": "Advanced",
|
|
14
14
|
"settings": "Settings",
|
|
15
15
|
"settingsGroupBasic": "Basic Configuration",
|
|
16
|
-
"
|
|
16
|
+
"settingsGroupCommon": "Common Settings",
|
|
17
|
+
"settingsGroupSecurity": "Security & Privacy",
|
|
18
|
+
"settingsGroupSystem": "System & Extensions",
|
|
17
19
|
"appearance": "Appearance",
|
|
18
20
|
"keyboardShortcuts": "Keyboard shortcuts",
|
|
19
21
|
"keyboardShortcutsDesktopOnly": "Keyboard shortcuts can be configured on desktop. Use the microphone button on mobile.",
|
|
@@ -63,6 +63,7 @@
|
|
|
63
63
|
"desktopUpdatesDownloadProgressUnknown": "Downloading",
|
|
64
64
|
"desktopUpdatesBlockedTitle": "Update Blocked",
|
|
65
65
|
"desktopUpdatesBlockedDescription": "Resolve the current installation requirement before continuing the update.",
|
|
66
|
+
"desktopUpdatesManualPortableDownload": "Download Latest Portable Edition",
|
|
66
67
|
"desktopUpdatesBlockedRootCause.signature-verification-unavailable": "Root cause: missing update public key; install a configured build or ask the maintainer to set the key.",
|
|
67
68
|
"desktopUpdatesBlockedRootCause.host-too-old": "Root cause: the host is too old for this runtime bundle; upgrade the NextClaw launcher first.",
|
|
68
69
|
"desktopUpdatesBlockedRootCause.unsupported-installation": "Root cause: this installation or update source does not support auto-update; use a configured build.",
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"docBrowserCloseTab": "Close Tab",
|
|
19
19
|
"docBrowserTabUntitled": "Untitled",
|
|
20
20
|
"docBrowserTabMoreActions": "More tab actions",
|
|
21
|
-
"docBrowserAddToChat": "Add to
|
|
21
|
+
"docBrowserAddToChat": "Add to chat",
|
|
22
22
|
"sideDockPinCurrent": "Pin to SideDock",
|
|
23
23
|
"sideDockUnpinCurrent": "Remove from SideDock",
|
|
24
24
|
"sideDockBuiltInDocked": "Built-in shortcut",
|
|
@@ -1,4 +1,34 @@
|
|
|
1
1
|
{
|
|
2
|
+
"pageLayoutActions": "布局与位置",
|
|
3
|
+
"pagePinLeft": "固定到左侧页面",
|
|
4
|
+
"pageUnpinLeft": "从左侧页面移除",
|
|
5
|
+
"pageCopyUri": "复制资源链接",
|
|
6
|
+
"pageCopyFailed": "无法复制链接,请重试",
|
|
7
|
+
"pageActions": "页面操作",
|
|
8
|
+
"pageSidebar": "页面",
|
|
9
|
+
"resourcePageTitle": "资源",
|
|
10
|
+
"resourceRetry": "重试",
|
|
11
|
+
"resourceNotFound": "资源已删除或不存在",
|
|
12
|
+
"resourceInvalid": "此资源类型尚未接入,或 URI 格式不正确",
|
|
13
|
+
"resourceLoadFailed": "资源暂时加载失败,请重试",
|
|
14
|
+
"pageUnavailable": "页面不可用或链接不受支持",
|
|
15
|
+
"workbenchOpenMain": "在主区域打开",
|
|
16
|
+
"workbenchDock": "停靠到侧栏",
|
|
17
|
+
"workbenchFloatGroup": "悬浮此视图组",
|
|
18
|
+
"workbenchExpand": "展开视图",
|
|
19
|
+
"workbenchCollapse": "收起视图",
|
|
20
|
+
"workbenchRestoreSize": "还原大小",
|
|
21
|
+
"workbenchMaximize": "最大化",
|
|
22
|
+
"workbenchCloseView": "关闭视图",
|
|
23
|
+
"workbenchHideGroup": "隐藏视图组",
|
|
24
|
+
"workbenchResize": "调整侧栏宽度",
|
|
25
|
+
"workbenchGlobalGroup": "全局工作区",
|
|
26
|
+
"workbenchSessionGroup": "会话工作台",
|
|
27
|
+
"workbenchMoveGlobal": "移动到全局右侧栏",
|
|
28
|
+
"workbenchFloatView": "在悬浮窗中打开",
|
|
29
|
+
"workbenchCloseTab": "关闭标签",
|
|
30
|
+
"workbenchFileUnavailable": "此文件视图不可用,请从原会话重新打开。",
|
|
31
|
+
"chatSidebarCloseSearch": "关闭搜索",
|
|
2
32
|
"chatPageTitle": "Agent 对话",
|
|
3
33
|
"chatPageDescription": "在 UI 内直接与 Agent 交互,支持多会话与多 Agent 切换。",
|
|
4
34
|
"chatRefresh": "刷新",
|
|
@@ -252,7 +282,12 @@
|
|
|
252
282
|
"chatHistoryLoadFailed": "较早消息加载失败",
|
|
253
283
|
"chatHistoryRetry": "重试",
|
|
254
284
|
"chatProcessSummaryProcessed": "已处理",
|
|
255
|
-
"
|
|
285
|
+
"chatProcessSummaryError": "处理失败",
|
|
286
|
+
"chatProcessSummaryStopped": "已停止",
|
|
287
|
+
"chatProcessDurationHoursMinutes": "{hours}小时{minutes}分钟",
|
|
288
|
+
"chatProcessDurationHours": "{hours}小时",
|
|
289
|
+
"chatProcessDurationMinutesSeconds": "{minutes}分钟{seconds}秒",
|
|
290
|
+
"chatProcessDurationSeconds": "{seconds}秒",
|
|
256
291
|
"chatToolPayloadLoading": "正在加载详情",
|
|
257
292
|
"chatToolPayloadLoadFailed": "详情加载失败,点击重试",
|
|
258
293
|
"chatToolActivityShowMore": "继续显示 {count} 项(剩余 {remaining} 项)",
|
|
@@ -610,6 +645,15 @@
|
|
|
610
645
|
"chatBackgroundReplyFallbackTitle": "有新回复",
|
|
611
646
|
"chatBackgroundReplyFallbackPreview": "回复已完成,点击查看。",
|
|
612
647
|
"chatBackgroundReplyOpenAriaLabel": "打开“{title}”的新回复",
|
|
648
|
+
"chatFloatingExpand": "在悬浮窗中对话",
|
|
649
|
+
"chatFloatingConversation": "悬浮会话",
|
|
650
|
+
"chatFloatingMinimize": "收起会话",
|
|
651
|
+
"chatFloatingRestore": "展开会话",
|
|
652
|
+
"chatFloatingOpenMain": "转到主会话",
|
|
653
|
+
"chatFloatingClose": "关闭悬浮会话",
|
|
654
|
+
"chatPanelDock": "停靠到右侧栏",
|
|
655
|
+
"chatPanelOpen": "在右侧栏中对话",
|
|
656
|
+
"chatPanelUnavailable": "无法打开此会话,请从会话列表重新打开。",
|
|
613
657
|
"chatWelcomeTitle": "今天想完成什么?",
|
|
614
658
|
"chatWelcomeContextLabel": "任务上下文",
|
|
615
659
|
"chatWelcomeProjectPickerLabel": "选择工作目录",
|
|
@@ -654,6 +698,7 @@
|
|
|
654
698
|
"chatInputVoiceNoDevice": "没有检测到麦克风。请连接或启用输入设备,再检测重试。",
|
|
655
699
|
"chatInputVoiceDeviceBusy": "麦克风无法读取,可能被其他应用占用或被系统限制。请关闭其他录音应用,检查输入设备后重试。",
|
|
656
700
|
"chatInputVoiceServiceDenied": "浏览器拒绝了语音识别服务。请检查浏览器的语音服务设置或组织策略,或使用支持语音识别的浏览器。",
|
|
701
|
+
"chatInputVoiceInsecureContext": "当前页面通过不安全的 HTTP 地址打开,浏览器会禁用麦克风。请为 VPS 配置 HTTPS;仅在本机访问时可使用 localhost 或 127.0.0.1。",
|
|
657
702
|
"chatInputVoiceUnsupported": "当前浏览器不支持语音输入。",
|
|
658
703
|
"chatInputVoiceNoSpeech": "没有听清,请再试一次。",
|
|
659
704
|
"chatInputVoiceFailed": "语音输入失败,请重试。",
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"model": "模型",
|
|
4
4
|
"searchChannels": "搜索渠道",
|
|
5
5
|
"providers": "提供商",
|
|
6
|
-
"extensions": "
|
|
6
|
+
"extensions": "扩展",
|
|
7
7
|
"channels": "渠道",
|
|
8
8
|
"cron": "定时任务",
|
|
9
9
|
"secrets": "密钥管理",
|
|
@@ -13,7 +13,9 @@
|
|
|
13
13
|
"advanced": "高级",
|
|
14
14
|
"settings": "设置",
|
|
15
15
|
"settingsGroupBasic": "基础配置",
|
|
16
|
-
"
|
|
16
|
+
"settingsGroupCommon": "常用设置",
|
|
17
|
+
"settingsGroupSecurity": "安全与隐私",
|
|
18
|
+
"settingsGroupSystem": "系统与扩展",
|
|
17
19
|
"appearance": "外观",
|
|
18
20
|
"keyboardShortcuts": "快捷键",
|
|
19
21
|
"keyboardShortcutsDesktopOnly": "请在桌面端配置键盘快捷键。手机可使用麦克风按钮输入。",
|
|
@@ -139,15 +141,15 @@
|
|
|
139
141
|
"providersDefaultDescription": "为你的智能体配置 AI 服务",
|
|
140
142
|
"providersEmptyTitle": "尚未配置提供商",
|
|
141
143
|
"providersEmptyDescription": "添加一个 AI 提供商后即可开始使用。",
|
|
142
|
-
"extensionsPageTitle": "
|
|
143
|
-
"extensionsPageDescription": "
|
|
144
|
+
"extensionsPageTitle": "扩展管理",
|
|
145
|
+
"extensionsPageDescription": "查看已发现的扩展、能力和当前运行状态。",
|
|
144
146
|
"extensionsRefresh": "刷新",
|
|
145
|
-
"extensionsLifecycleDescription": "
|
|
146
|
-
"extensionsLoading": "
|
|
147
|
-
"extensionsLoadFailed": "
|
|
147
|
+
"extensionsLifecycleDescription": "扩展由运行时按渠道和会话持续关注关系按需启停;这里集中查看全局能力和运行状态。",
|
|
148
|
+
"extensionsLoading": "正在加载扩展…",
|
|
149
|
+
"extensionsLoadFailed": "扩展加载失败。",
|
|
148
150
|
"extensionsRetry": "重试",
|
|
149
|
-
"extensionsEmpty": "
|
|
150
|
-
"extensionsEmptyDescription": "
|
|
151
|
+
"extensionsEmpty": "暂无已发现的扩展",
|
|
152
|
+
"extensionsEmptyDescription": "安装或放置扩展后,重新刷新此页面。",
|
|
151
153
|
"extensionsTotal": "全部",
|
|
152
154
|
"extensionsRunning": "运行中",
|
|
153
155
|
"extensionsWithObservations": "支持持续关注",
|
|
@@ -63,6 +63,7 @@
|
|
|
63
63
|
"desktopUpdatesDownloadProgressUnknown": "正在下载",
|
|
64
64
|
"desktopUpdatesBlockedTitle": "更新暂时无法继续",
|
|
65
65
|
"desktopUpdatesBlockedDescription": "当前安装环境需要先处理阻塞项,之后才能继续更新。",
|
|
66
|
+
"desktopUpdatesManualPortableDownload": "下载最新 Portable Edition",
|
|
66
67
|
"desktopUpdatesBlockedRootCause.signature-verification-unavailable": "根因:缺少更新签名公钥,无法验证更新包来源;请安装包含正确更新配置的新版本,或由维护者配置更新公钥。",
|
|
67
68
|
"desktopUpdatesBlockedRootCause.host-too-old": "根因:当前宿主版本太旧,不满足新运行包的最低宿主版本要求;请先升级 NextClaw 启动器。",
|
|
68
69
|
"desktopUpdatesBlockedRootCause.unsupported-installation": "根因:当前安装形态或更新源配置不支持自动更新;请安装支持自动更新的版本,或由维护者补齐更新源配置。",
|
|
@@ -40,7 +40,7 @@ export function pushNavigationHistoryEntry<Entry>(
|
|
|
40
40
|
index: normalizedIndex,
|
|
41
41
|
};
|
|
42
42
|
}
|
|
43
|
-
const entries = [...history.entries.slice(0, normalizedIndex + 1), entry];
|
|
43
|
+
const entries = [...history.entries.slice(0, normalizedIndex + 1), entry].slice(-100);
|
|
44
44
|
return {
|
|
45
45
|
entries,
|
|
46
46
|
index: entries.length - 1,
|
|
@@ -35,3 +35,14 @@ describe("ScrollRestorationManager", () => {
|
|
|
35
35
|
expect(manager.read("second")).toBeNull();
|
|
36
36
|
});
|
|
37
37
|
});
|
|
38
|
+
|
|
39
|
+
it("restores reading memory after reload without reviving deleted entries", () => {
|
|
40
|
+
sessionStorage.clear();
|
|
41
|
+
const first = new ScrollRestorationManager(2, sessionStorage);
|
|
42
|
+
first.save('file:one', { x: 2, y: 200 });
|
|
43
|
+
first.save('file:two', { x: 0, y: 300 });
|
|
44
|
+
const reloaded = new ScrollRestorationManager(2, sessionStorage);
|
|
45
|
+
expect(reloaded.read('file:one')).toEqual({ x: 2, y: 200 });
|
|
46
|
+
reloaded.delete('file:one');
|
|
47
|
+
expect(new ScrollRestorationManager(2, sessionStorage).read('file:one')).toBeNull();
|
|
48
|
+
});
|
|
@@ -15,15 +15,37 @@ function normalizeKey(key: string): string | null {
|
|
|
15
15
|
return normalized ? normalized : null;
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
/**
|
|
18
|
+
/** Shared, bounded reading memory; optional session storage survives a page reload. */
|
|
19
19
|
export class ScrollRestorationManager {
|
|
20
20
|
private readonly positions = new Map<string, ScrollRestorationPosition>();
|
|
21
21
|
|
|
22
22
|
public constructor(
|
|
23
23
|
private readonly capacity = DEFAULT_SCROLL_RESTORATION_CAPACITY,
|
|
24
|
-
|
|
24
|
+
private readonly storage?: Storage,
|
|
25
|
+
) {
|
|
26
|
+
try {
|
|
27
|
+
const saved: unknown = JSON.parse(storage?.getItem('nextclaw.view-memory.v1') ?? 'null');
|
|
28
|
+
if (Array.isArray(saved)) for (const entry of saved.slice(-capacity)) {
|
|
29
|
+
if (!Array.isArray(entry) || typeof entry[0] !== 'string') continue;
|
|
30
|
+
const value = entry[1];
|
|
31
|
+
if (value && typeof value.x === 'number' && typeof value.y === 'number' && Number.isFinite(value.x) && Number.isFinite(value.y)) this.positions.set(entry[0], value);
|
|
32
|
+
}
|
|
33
|
+
} catch { /* Private mode, stale data or quota cannot block navigation. */ }
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
private persist = (): void => {
|
|
37
|
+
try {
|
|
38
|
+
const entries = [...this.positions];
|
|
39
|
+
let json = JSON.stringify(entries);
|
|
40
|
+
while (json.length > 512_000 && entries.length > 0) {
|
|
41
|
+
entries.shift();
|
|
42
|
+
json = JSON.stringify(entries);
|
|
43
|
+
}
|
|
44
|
+
this.storage?.setItem('nextclaw.view-memory.v1', json);
|
|
45
|
+
} catch { /* Keep the in-memory position when browser storage is unavailable. */ }
|
|
46
|
+
}
|
|
25
47
|
|
|
26
|
-
public save(key: string, position: ScrollRestorationPosition): void {
|
|
48
|
+
public save = (key: string, position: ScrollRestorationPosition): void => {
|
|
27
49
|
const normalizedKey = normalizeKey(key);
|
|
28
50
|
if (!normalizedKey) return;
|
|
29
51
|
|
|
@@ -38,23 +60,30 @@ export class ScrollRestorationManager {
|
|
|
38
60
|
if (!oldestKey) return;
|
|
39
61
|
this.positions.delete(oldestKey);
|
|
40
62
|
}
|
|
63
|
+
this.persist();
|
|
41
64
|
}
|
|
42
65
|
|
|
43
|
-
public read(key: string): ScrollRestorationPosition | null {
|
|
66
|
+
public read = (key: string): ScrollRestorationPosition | null => {
|
|
44
67
|
const normalizedKey = normalizeKey(key);
|
|
45
68
|
if (!normalizedKey) return null;
|
|
46
69
|
const position = this.positions.get(normalizedKey);
|
|
47
70
|
return position ? { ...position } : null;
|
|
48
71
|
}
|
|
49
72
|
|
|
50
|
-
public delete(key: string): void {
|
|
73
|
+
public delete = (key: string): void => {
|
|
51
74
|
const normalizedKey = normalizeKey(key);
|
|
52
75
|
if (normalizedKey) this.positions.delete(normalizedKey);
|
|
76
|
+
this.persist();
|
|
53
77
|
}
|
|
54
78
|
|
|
55
|
-
public clear(): void {
|
|
79
|
+
public clear = (): void => {
|
|
56
80
|
this.positions.clear();
|
|
81
|
+
this.persist();
|
|
57
82
|
}
|
|
58
83
|
}
|
|
59
84
|
|
|
60
|
-
|
|
85
|
+
function getReadingMemoryStorage(): Storage | undefined {
|
|
86
|
+
try { return typeof window === 'undefined' ? undefined : window.sessionStorage; } catch { return undefined; }
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export const scrollRestorationManager = new ScrollRestorationManager(DEFAULT_SCROLL_RESTORATION_CAPACITY, getReadingMemoryStorage());
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from "vitest";
|
|
2
|
+
import { ViewMemoryStorage } from "./view-memory-storage.store";
|
|
3
|
+
|
|
4
|
+
describe("best-effort view memory", () => {
|
|
5
|
+
it("tolerates access denial and quota failures without breaking view actions", () => {
|
|
6
|
+
const unavailable = new ViewMemoryStorage(() => {
|
|
7
|
+
throw new Error("denied");
|
|
8
|
+
});
|
|
9
|
+
expect(unavailable.getItem("view")).toBeNull();
|
|
10
|
+
expect(() => unavailable.setItem("view", "{}")).not.toThrow();
|
|
11
|
+
expect(() => unavailable.removeItem("view")).not.toThrow();
|
|
12
|
+
const quota = new ViewMemoryStorage(
|
|
13
|
+
() =>
|
|
14
|
+
({
|
|
15
|
+
getItem: () => null,
|
|
16
|
+
setItem: () => {
|
|
17
|
+
throw new Error("quota");
|
|
18
|
+
},
|
|
19
|
+
removeItem: vi.fn(),
|
|
20
|
+
}) as unknown as Storage,
|
|
21
|
+
);
|
|
22
|
+
expect(() => quota.setItem("view", "{}")).not.toThrow();
|
|
23
|
+
});
|
|
24
|
+
it("preserves the existing storage key and serialized state", () => {
|
|
25
|
+
const storage = {
|
|
26
|
+
getItem: vi.fn(() => '{"version":1}'),
|
|
27
|
+
setItem: vi.fn(),
|
|
28
|
+
removeItem: vi.fn(),
|
|
29
|
+
};
|
|
30
|
+
const memory = new ViewMemoryStorage(() => storage as unknown as Storage);
|
|
31
|
+
expect(memory.getItem("view")).toBe('{"version":1}');
|
|
32
|
+
memory.setItem("view", "snapshot");
|
|
33
|
+
memory.removeItem("view");
|
|
34
|
+
expect(storage.setItem).toHaveBeenCalledWith("view", "snapshot");
|
|
35
|
+
expect(storage.removeItem).toHaveBeenCalledWith("view");
|
|
36
|
+
});
|
|
37
|
+
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { StateStorage } from 'zustand/middleware';
|
|
2
|
+
|
|
3
|
+
/** Owns best-effort browser IO; denied storage never blocks view navigation. */
|
|
4
|
+
export class ViewMemoryStorage implements StateStorage {
|
|
5
|
+
constructor(private readonly resolveStorage: () => Storage = () => window.localStorage) {}
|
|
6
|
+
|
|
7
|
+
getItem = (key: string): string | null => {
|
|
8
|
+
try { return this.resolveStorage().getItem(key); }
|
|
9
|
+
catch { return null; }
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
setItem = (key: string, value: string): void => {
|
|
13
|
+
try { this.resolveStorage().setItem(key, value); }
|
|
14
|
+
catch { /* Keep the active in-memory view usable. */ }
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
removeItem = (key: string): void => {
|
|
18
|
+
try { this.resolveStorage().removeItem(key); }
|
|
19
|
+
catch { /* Storage may be blocked or full. */ }
|
|
20
|
+
};
|
|
21
|
+
}
|
|
@@ -1,12 +1,17 @@
|
|
|
1
|
-
import { afterEach, describe, expect, it, vi } from 'vitest';
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
2
|
import { BrowserRealtimeRecoveryService } from '@/shared/lib/transport/browser-realtime-recovery.service';
|
|
3
3
|
|
|
4
4
|
describe('BrowserRealtimeRecoveryService', () => {
|
|
5
|
+
beforeEach(() => {
|
|
6
|
+
vi.useFakeTimers();
|
|
7
|
+
});
|
|
8
|
+
|
|
5
9
|
afterEach(() => {
|
|
6
10
|
vi.restoreAllMocks();
|
|
11
|
+
vi.useRealTimers();
|
|
7
12
|
});
|
|
8
13
|
|
|
9
|
-
it('requests recovery
|
|
14
|
+
it('requests recovery for every supported foreground lifecycle signal', () => {
|
|
10
15
|
let visibilityState: DocumentVisibilityState = 'hidden';
|
|
11
16
|
vi.spyOn(document, 'visibilityState', 'get').mockImplementation(() => visibilityState);
|
|
12
17
|
const requestRecovery = vi.fn();
|
|
@@ -18,11 +23,19 @@ describe('BrowserRealtimeRecoveryService', () => {
|
|
|
18
23
|
|
|
19
24
|
visibilityState = 'visible';
|
|
20
25
|
document.dispatchEvent(new Event('visibilitychange'));
|
|
26
|
+
window.dispatchEvent(new PageTransitionEvent('pageshow', { persisted: true }));
|
|
27
|
+
window.dispatchEvent(new Event('focus'));
|
|
21
28
|
window.dispatchEvent(new Event('online'));
|
|
29
|
+
expect(requestRecovery).toHaveBeenCalledTimes(1);
|
|
30
|
+
|
|
31
|
+
vi.advanceTimersByTime(250);
|
|
32
|
+
window.dispatchEvent(new PageTransitionEvent('pageshow', { persisted: true }));
|
|
22
33
|
expect(requestRecovery).toHaveBeenCalledTimes(2);
|
|
23
34
|
|
|
24
35
|
recovery.stop();
|
|
25
36
|
document.dispatchEvent(new Event('visibilitychange'));
|
|
37
|
+
window.dispatchEvent(new PageTransitionEvent('pageshow', { persisted: true }));
|
|
38
|
+
window.dispatchEvent(new Event('focus'));
|
|
26
39
|
expect(requestRecovery).toHaveBeenCalledTimes(2);
|
|
27
40
|
});
|
|
28
41
|
|
|
@@ -39,4 +52,16 @@ describe('BrowserRealtimeRecoveryService', () => {
|
|
|
39
52
|
recovery.stop();
|
|
40
53
|
recovery.stop();
|
|
41
54
|
});
|
|
55
|
+
|
|
56
|
+
it('does not replace the first connection for an ordinary initial pageshow', () => {
|
|
57
|
+
vi.spyOn(document, 'visibilityState', 'get').mockReturnValue('visible');
|
|
58
|
+
const requestRecovery = vi.fn();
|
|
59
|
+
const recovery = new BrowserRealtimeRecoveryService(requestRecovery);
|
|
60
|
+
|
|
61
|
+
recovery.start();
|
|
62
|
+
window.dispatchEvent(new PageTransitionEvent('pageshow', { persisted: false }));
|
|
63
|
+
|
|
64
|
+
expect(requestRecovery).not.toHaveBeenCalled();
|
|
65
|
+
recovery.stop();
|
|
66
|
+
});
|
|
42
67
|
});
|
|
@@ -38,11 +38,13 @@ class MockWebSocket {
|
|
|
38
38
|
describe('LocalAppTransport browser connection recovery', () => {
|
|
39
39
|
beforeEach(() => {
|
|
40
40
|
MockWebSocket.instances = [];
|
|
41
|
+
vi.useFakeTimers();
|
|
41
42
|
vi.stubGlobal('WebSocket', MockWebSocket as unknown as typeof WebSocket);
|
|
42
43
|
});
|
|
43
44
|
|
|
44
45
|
afterEach(() => {
|
|
45
46
|
vi.restoreAllMocks();
|
|
47
|
+
vi.useRealTimers();
|
|
46
48
|
vi.unstubAllGlobals();
|
|
47
49
|
});
|
|
48
50
|
|
|
@@ -70,6 +72,7 @@ describe('LocalAppTransport browser connection recovery', () => {
|
|
|
70
72
|
|
|
71
73
|
expect(MockWebSocket.instances).toHaveLength(2);
|
|
72
74
|
expect(handler).toHaveBeenLastCalledWith({ type: 'connection.open', payload: {} });
|
|
75
|
+
vi.advanceTimersByTime(250);
|
|
73
76
|
window.dispatchEvent(new Event('online'));
|
|
74
77
|
const secondReplacement = MockWebSocket.instances[2];
|
|
75
78
|
secondReplacement?.open();
|
|
@@ -82,4 +85,21 @@ describe('LocalAppTransport browser connection recovery', () => {
|
|
|
82
85
|
window.dispatchEvent(new Event('online'));
|
|
83
86
|
expect(MockWebSocket.instances).toHaveLength(3);
|
|
84
87
|
});
|
|
88
|
+
|
|
89
|
+
it('replaces a frozen connecting socket when the page regains focus', () => {
|
|
90
|
+
vi.spyOn(document, 'visibilityState', 'get').mockReturnValue('visible');
|
|
91
|
+
const transport = new LocalAppTransport({ apiBase: 'https://vps.example.com' });
|
|
92
|
+
const handler = vi.fn();
|
|
93
|
+
const unsubscribe = transport.subscribe(handler);
|
|
94
|
+
const frozenSocket = MockWebSocket.instances[0];
|
|
95
|
+
|
|
96
|
+
window.dispatchEvent(new Event('focus'));
|
|
97
|
+
|
|
98
|
+
expect(frozenSocket?.close).toHaveBeenCalledTimes(1);
|
|
99
|
+
expect(MockWebSocket.instances).toHaveLength(2);
|
|
100
|
+
expect(handler).toHaveBeenCalledWith({ type: 'connection.close', payload: {} });
|
|
101
|
+
MockWebSocket.instances[1]?.open();
|
|
102
|
+
expect(handler).toHaveBeenLastCalledWith({ type: 'connection.open', payload: {} });
|
|
103
|
+
unsubscribe();
|
|
104
|
+
});
|
|
85
105
|
});
|
|
@@ -185,6 +185,7 @@ describe('RemoteSessionMultiplexTransport request path', () => {
|
|
|
185
185
|
await pendingFailure;
|
|
186
186
|
await Promise.resolve();
|
|
187
187
|
expect(replacementSocket?.sent).toHaveLength(0);
|
|
188
|
+
vi.advanceTimersByTime(250);
|
|
188
189
|
window.dispatchEvent(new Event('online'));
|
|
189
190
|
const secondReplacement = MockWebSocket.instances[2];
|
|
190
191
|
secondReplacement?.open();
|
|
@@ -197,4 +198,29 @@ describe('RemoteSessionMultiplexTransport request path', () => {
|
|
|
197
198
|
expect(MockWebSocket.instances).toHaveLength(3);
|
|
198
199
|
unsubscribe();
|
|
199
200
|
});
|
|
201
|
+
|
|
202
|
+
it('abandons a frozen connecting socket immediately when the page is shown again', async () => {
|
|
203
|
+
vi.spyOn(document, 'visibilityState', 'get').mockReturnValue('visible');
|
|
204
|
+
const transport = new RemoteSessionMultiplexTransport({
|
|
205
|
+
mode: 'remote',
|
|
206
|
+
protocolVersion: 1,
|
|
207
|
+
wsPath: '/_remote/ws'
|
|
208
|
+
}, 'https://remote.claw.cool');
|
|
209
|
+
|
|
210
|
+
const handler = vi.fn();
|
|
211
|
+
const unsubscribe = transport.subscribe(handler);
|
|
212
|
+
const frozenSocket = MockWebSocket.instances[0];
|
|
213
|
+
|
|
214
|
+
window.dispatchEvent(new PageTransitionEvent('pageshow', { persisted: true }));
|
|
215
|
+
await Promise.resolve();
|
|
216
|
+
|
|
217
|
+
expect(frozenSocket?.readyState).toBe(MockWebSocket.CLOSED);
|
|
218
|
+
expect(MockWebSocket.instances).toHaveLength(2);
|
|
219
|
+
expect(handler).toHaveBeenCalledWith({ type: 'connection.close', payload: {} });
|
|
220
|
+
|
|
221
|
+
MockWebSocket.instances[1]?.open();
|
|
222
|
+
await Promise.resolve();
|
|
223
|
+
expect(handler).toHaveBeenLastCalledWith({ type: 'connection.open', payload: {} });
|
|
224
|
+
unsubscribe();
|
|
225
|
+
});
|
|
200
226
|
});
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
const RECOVERY_SIGNAL_COOLDOWN_MS = 250;
|
|
2
|
+
|
|
1
3
|
export class BrowserRealtimeRecoveryService {
|
|
2
4
|
private started = false;
|
|
5
|
+
private recoveryCooldownId: number | null = null;
|
|
3
6
|
|
|
4
7
|
constructor(private readonly requestRecovery: () => void) {}
|
|
5
8
|
|
|
@@ -10,6 +13,8 @@ export class BrowserRealtimeRecoveryService {
|
|
|
10
13
|
this.started = true;
|
|
11
14
|
document.addEventListener('visibilitychange', this.handleVisibilityChange);
|
|
12
15
|
window.addEventListener('online', this.handleOnline);
|
|
16
|
+
window.addEventListener('pageshow', this.handlePageShow);
|
|
17
|
+
window.addEventListener('focus', this.handleForegroundSignal);
|
|
13
18
|
};
|
|
14
19
|
|
|
15
20
|
stop = (): void => {
|
|
@@ -19,17 +24,39 @@ export class BrowserRealtimeRecoveryService {
|
|
|
19
24
|
this.started = false;
|
|
20
25
|
document.removeEventListener('visibilitychange', this.handleVisibilityChange);
|
|
21
26
|
window.removeEventListener('online', this.handleOnline);
|
|
27
|
+
window.removeEventListener('pageshow', this.handlePageShow);
|
|
28
|
+
window.removeEventListener('focus', this.handleForegroundSignal);
|
|
29
|
+
if (this.recoveryCooldownId !== null) {
|
|
30
|
+
window.clearTimeout(this.recoveryCooldownId);
|
|
31
|
+
this.recoveryCooldownId = null;
|
|
32
|
+
}
|
|
22
33
|
};
|
|
23
34
|
|
|
24
35
|
private readonly handleVisibilityChange = (): void => {
|
|
25
|
-
|
|
26
|
-
this.requestRecovery();
|
|
27
|
-
}
|
|
36
|
+
this.requestRecoveryIfVisible();
|
|
28
37
|
};
|
|
29
38
|
|
|
30
39
|
private readonly handleOnline = (): void => {
|
|
31
|
-
|
|
32
|
-
|
|
40
|
+
this.requestRecoveryIfVisible();
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
private readonly handleForegroundSignal = (): void => {
|
|
44
|
+
this.requestRecoveryIfVisible();
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
private readonly handlePageShow = (event: PageTransitionEvent): void => {
|
|
48
|
+
if (event.persisted) {
|
|
49
|
+
this.requestRecoveryIfVisible();
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
private readonly requestRecoveryIfVisible = (): void => {
|
|
54
|
+
if (document.visibilityState !== 'visible' || this.recoveryCooldownId !== null) {
|
|
55
|
+
return;
|
|
33
56
|
}
|
|
57
|
+
this.requestRecovery();
|
|
58
|
+
this.recoveryCooldownId = window.setTimeout(() => {
|
|
59
|
+
this.recoveryCooldownId = null;
|
|
60
|
+
}, RECOVERY_SIGNAL_COOLDOWN_MS);
|
|
34
61
|
};
|
|
35
62
|
}
|
|
@@ -54,7 +54,6 @@ class LocalRealtimeGateway {
|
|
|
54
54
|
private socket: WebSocket | null = null;
|
|
55
55
|
private reconnectTimer: number | null = null;
|
|
56
56
|
private manualClose = false;
|
|
57
|
-
private replacingAfterBrowserRecovery = false;
|
|
58
57
|
private subscribers = new Set<EventHandler>();
|
|
59
58
|
private readonly browserRecovery = new BrowserRealtimeRecoveryService(() => {
|
|
60
59
|
this.replaceSocket();
|
|
@@ -95,7 +94,6 @@ class LocalRealtimeGateway {
|
|
|
95
94
|
this.socket = socket;
|
|
96
95
|
|
|
97
96
|
socket.onopen = () => {
|
|
98
|
-
this.replacingAfterBrowserRecovery = false;
|
|
99
97
|
this.emit({ type: 'connection.open', payload: {} });
|
|
100
98
|
};
|
|
101
99
|
|
|
@@ -113,7 +111,6 @@ class LocalRealtimeGateway {
|
|
|
113
111
|
};
|
|
114
112
|
|
|
115
113
|
socket.onclose = () => {
|
|
116
|
-
this.replacingAfterBrowserRecovery = false;
|
|
117
114
|
this.emit({ type: 'connection.close', payload: {} });
|
|
118
115
|
this.socket = null;
|
|
119
116
|
if (!this.manualClose && this.subscribers.size > 0) {
|
|
@@ -133,10 +130,9 @@ class LocalRealtimeGateway {
|
|
|
133
130
|
};
|
|
134
131
|
|
|
135
132
|
private replaceSocket = (): void => {
|
|
136
|
-
if (this.subscribers.size === 0
|
|
133
|
+
if (this.subscribers.size === 0) {
|
|
137
134
|
return;
|
|
138
135
|
}
|
|
139
|
-
this.replacingAfterBrowserRecovery = true;
|
|
140
136
|
if (this.reconnectTimer !== null) {
|
|
141
137
|
window.clearTimeout(this.reconnectTimer);
|
|
142
138
|
this.reconnectTimer = null;
|
|
@@ -156,7 +152,6 @@ class LocalRealtimeGateway {
|
|
|
156
152
|
|
|
157
153
|
private disconnect = (): void => {
|
|
158
154
|
this.manualClose = true;
|
|
159
|
-
this.replacingAfterBrowserRecovery = false;
|
|
160
155
|
if (this.reconnectTimer !== null) {
|
|
161
156
|
window.clearTimeout(this.reconnectTimer);
|
|
162
157
|
this.reconnectTimer = null;
|
|
@@ -87,9 +87,16 @@ function createId(prefix: string): string {
|
|
|
87
87
|
|
|
88
88
|
const REMOTE_REQUEST_TIMEOUT_MS = 15_000;
|
|
89
89
|
|
|
90
|
+
class RemoteBrowserRecoveryReplacementError extends Error {
|
|
91
|
+
constructor() {
|
|
92
|
+
super('Remote transport connection replaced after browser recovery.');
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
90
96
|
export class RemoteSessionMultiplexTransport implements AppTransport {
|
|
91
97
|
private socket: WebSocket | null = null;
|
|
92
98
|
private connectPromise: Promise<void> | null = null;
|
|
99
|
+
private rejectConnect: ((error: Error) => void) | null = null;
|
|
93
100
|
private connectTimeoutId: number | null = null;
|
|
94
101
|
private reconnectTimer: number | null = null;
|
|
95
102
|
private manualClose = false;
|
|
@@ -221,6 +228,9 @@ export class RemoteSessionMultiplexTransport implements AppTransport {
|
|
|
221
228
|
this.subscribers.add(handler);
|
|
222
229
|
this.browserRecovery.start();
|
|
223
230
|
void this.ensureSocket().catch((error) => {
|
|
231
|
+
if (error instanceof RemoteBrowserRecoveryReplacementError) {
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
224
234
|
handler({
|
|
225
235
|
type: 'connection.error',
|
|
226
236
|
payload: { message: error instanceof Error ? error.message : String(error) }
|
|
@@ -258,6 +268,7 @@ export class RemoteSessionMultiplexTransport implements AppTransport {
|
|
|
258
268
|
const wsUrl = resolveTransportWebSocketUrl(this.apiBase, this.runtime.wsPath);
|
|
259
269
|
this.manualClose = false;
|
|
260
270
|
this.connectPromise = new Promise<void>((innerResolve, innerReject) => {
|
|
271
|
+
this.rejectConnect = innerReject;
|
|
261
272
|
const socket = new WebSocket(wsUrl);
|
|
262
273
|
this.socket = socket;
|
|
263
274
|
let connectionOpened = false;
|
|
@@ -279,6 +290,7 @@ export class RemoteSessionMultiplexTransport implements AppTransport {
|
|
|
279
290
|
connectionOpened = true;
|
|
280
291
|
clearConnectTimeout();
|
|
281
292
|
this.connectPromise = null;
|
|
293
|
+
this.rejectConnect = null;
|
|
282
294
|
innerResolve();
|
|
283
295
|
this.emit({ type: 'connection.open', payload: {} });
|
|
284
296
|
};
|
|
@@ -304,6 +316,7 @@ export class RemoteSessionMultiplexTransport implements AppTransport {
|
|
|
304
316
|
const wasConnecting = this.connectPromise !== null;
|
|
305
317
|
this.socket = null;
|
|
306
318
|
this.connectPromise = null;
|
|
319
|
+
this.rejectConnect = null;
|
|
307
320
|
this.failPendingWork(new Error('Remote transport connection closed.'));
|
|
308
321
|
this.emit({ type: 'connection.close', payload: {} });
|
|
309
322
|
if (wasConnecting && !connectionOpened) {
|
|
@@ -330,21 +343,29 @@ export class RemoteSessionMultiplexTransport implements AppTransport {
|
|
|
330
343
|
}
|
|
331
344
|
|
|
332
345
|
private replaceSocket = (): void => {
|
|
333
|
-
if (this.subscribers.size === 0
|
|
346
|
+
if (this.subscribers.size === 0) {
|
|
334
347
|
return;
|
|
335
348
|
}
|
|
336
349
|
if (this.reconnectTimer !== null) {
|
|
337
350
|
window.clearTimeout(this.reconnectTimer);
|
|
338
351
|
this.reconnectTimer = null;
|
|
339
352
|
}
|
|
353
|
+
if (this.connectTimeoutId !== null) {
|
|
354
|
+
window.clearTimeout(this.connectTimeoutId);
|
|
355
|
+
this.connectTimeoutId = null;
|
|
356
|
+
}
|
|
340
357
|
const staleSocket = this.socket;
|
|
358
|
+
const rejectStaleConnect = this.rejectConnect;
|
|
341
359
|
this.socket = null;
|
|
360
|
+
this.connectPromise = null;
|
|
361
|
+
this.rejectConnect = null;
|
|
342
362
|
if (staleSocket) {
|
|
343
363
|
staleSocket.onopen = null;
|
|
344
364
|
staleSocket.onmessage = null;
|
|
345
365
|
staleSocket.onerror = null;
|
|
346
366
|
staleSocket.onclose = null;
|
|
347
367
|
staleSocket.close();
|
|
368
|
+
rejectStaleConnect?.(new RemoteBrowserRecoveryReplacementError());
|
|
348
369
|
this.failPendingWork(new Error('Remote transport connection replaced after browser recovery.'));
|
|
349
370
|
this.emit({ type: 'connection.close', payload: {} });
|
|
350
371
|
}
|