@nextclaw/ui 0.26.0 → 0.26.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (308) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/dist/.vite/manifest.json +1143 -1133
  3. package/dist/assets/{abnfDiagram-VRR7QNED-Bjrmb5-o.js → abnfDiagram-VRR7QNED-Dcw10Yt1.js} +1 -1
  4. package/dist/assets/{account-panel-C1S3xtUB.js → account-panel-ZM2qzKF4.js} +1 -1
  5. package/dist/assets/{aiden0z-pptx-renderer.es-DO4DCX3e.js → aiden0z-pptx-renderer.es--oNqetPA.js} +1 -1
  6. package/dist/assets/api-Cjq-o1VN.js +20 -0
  7. package/dist/assets/app-presenter-provider-C02YPoMr.js +124 -0
  8. package/dist/assets/{appearance-settings-page-BljTr6nB.js → appearance-settings-page-B4zouKzg.js} +1 -1
  9. package/dist/assets/{arc-g4XaP3S4.js → arc-DBRedCax.js} +1 -1
  10. package/dist/assets/architecture-TIHT7OUA-CvnC-LeA.js +1 -0
  11. package/dist/assets/{architectureDiagram-ZJ3FMSHR-CAP7YDux.js → architectureDiagram-ZJ3FMSHR-Dufhtuv-.js} +1 -1
  12. package/dist/assets/{blockDiagram-677ZJIJ3-DhaP0zUG.js → blockDiagram-677ZJIJ3-BUk0vtJ7.js} +1 -1
  13. package/dist/assets/{book-open-vxpV2eSM.js → book-open-AteA89HX.js} +1 -1
  14. package/dist/assets/{c4Diagram-LMCZKHZV-DHCQJJkD.js → c4Diagram-LMCZKHZV-CxDcVE9p.js} +1 -1
  15. package/dist/assets/{card-D9NYhSdf.js → card-BLXvBstA.js} +1 -1
  16. package/dist/assets/channel-CpMstUPF.js +1 -0
  17. package/dist/assets/{channels-list-page-BiHA1zZd.js → channels-list-page-fj8kMo-T.js} +1 -1
  18. package/dist/assets/chunk-2Q5K7J3B-Djp3VF0s.js +1 -0
  19. package/dist/assets/{chunk-32BRIVSS-BcQlxl4v.js → chunk-32BRIVSS-DSF3KElA.js} +1 -1
  20. package/dist/assets/{chunk-52WLFC77-ClPpG-2S.js → chunk-52WLFC77-Costxzr3.js} +1 -1
  21. package/dist/assets/{chunk-5FCAYU7R-B_qmYOdX.js → chunk-5FCAYU7R-rhqzGGZo.js} +1 -1
  22. package/dist/assets/{chunk-5HE753X5-DTO-V56q.js → chunk-5HE753X5-BsEsmIao.js} +1 -1
  23. package/dist/assets/{chunk-5JV3BV7I-CU1SOaaG.js → chunk-5JV3BV7I-Dqw_DCoN.js} +1 -1
  24. package/dist/assets/{chunk-5TONJI2A-CzSDkWNa.js → chunk-5TONJI2A-Cft82fp8.js} +1 -1
  25. package/dist/assets/{chunk-5VM5RSS4-ChkfqIRE.js → chunk-5VM5RSS4-DqNHn9Dl.js} +1 -1
  26. package/dist/assets/{chunk-7BUUIJ7U-Du3Ozi62.js → chunk-7BUUIJ7U-A5SBEMYY.js} +1 -1
  27. package/dist/assets/{chunk-BIQX33UG-C-HVafHB.js → chunk-BIQX33UG-1Ng3mTrH.js} +1 -1
  28. package/dist/assets/{chunk-C7G6YPKG-BSjZ86D0.js → chunk-C7G6YPKG-DRJEy8ZI.js} +1 -1
  29. package/dist/assets/{chunk-CQNSW5MT-1D1W2lF6.js → chunk-CQNSW5MT-Bydv_J3J.js} +1 -1
  30. package/dist/assets/{chunk-CYSBUYHQ-ISpyj_lK.js → chunk-CYSBUYHQ-HkMIg12u.js} +1 -1
  31. package/dist/assets/{chunk-EMLP6XTP-Cp8iUBt2.js → chunk-EMLP6XTP-C5ndDvm3.js} +1 -1
  32. package/dist/assets/{chunk-EX3LRPZG-BN5RzF5j.js → chunk-EX3LRPZG-DzeloZRo.js} +1 -1
  33. package/dist/assets/{chunk-FWX5IMBZ-DecJDW4r.js → chunk-FWX5IMBZ-n_2_CLTQ.js} +2 -2
  34. package/dist/assets/{chunk-HOUHSVGY-BE_XNgGk.js → chunk-HOUHSVGY-kx_slvu3.js} +1 -1
  35. package/dist/assets/{chunk-ICXQ74PX-BFOKMkWV.js → chunk-ICXQ74PX-sQLMURqL.js} +1 -1
  36. package/dist/assets/{chunk-JG7HCLWE-DL-pl8VB.js → chunk-JG7HCLWE-CMUn5Elc.js} +1 -1
  37. package/dist/assets/{chunk-JWPE2WC7-B-bcwfmh.js → chunk-JWPE2WC7-Cra3s-Uk.js} +1 -1
  38. package/dist/assets/{chunk-MOJQB5TN-BC0taFro.js → chunk-MOJQB5TN-umcrg5Bi.js} +1 -1
  39. package/dist/assets/{chunk-MOZMSUNE-RrhfU6MO.js → chunk-MOZMSUNE-BnljD1ig.js} +1 -1
  40. package/dist/assets/{chunk-OGEWGWER-TuI7SR7i.js → chunk-OGEWGWER-BXx5pQjU.js} +1 -1
  41. package/dist/assets/{chunk-OSBZ3O6U-C5jMBKlU.js → chunk-OSBZ3O6U-D-1qfdWu.js} +1 -1
  42. package/dist/assets/{chunk-PUDLZKDR-C5lMBn0K.js → chunk-PUDLZKDR-DLzX78LC.js} +1 -1
  43. package/dist/assets/{chunk-Q4XR5HBZ-B-kzUbwT.js → chunk-Q4XR5HBZ-DTK-rtW6.js} +1 -1
  44. package/dist/assets/{chunk-QBLGF6JB-CRb_WAEj.js → chunk-QBLGF6JB-CsFeIGfr.js} +1 -1
  45. package/dist/assets/{chunk-R7FJI6CG-De6Y7ozO.js → chunk-R7FJI6CG-4xNYUeng.js} +1 -1
  46. package/dist/assets/{chunk-RYQCIY6F-Igv92q8k.js → chunk-RYQCIY6F-DJiHiTE5.js} +1 -1
  47. package/dist/assets/{chunk-U6XO7XAA-bCjRshkT.js → chunk-U6XO7XAA-BVYkauHz.js} +1 -1
  48. package/dist/assets/{chunk-V7JOEXUC-0J1s_Kgq.js → chunk-V7JOEXUC-BsazE8cS.js} +1 -1
  49. package/dist/assets/chunk-VAUOI2AC-BrbiIC1Q.js +1 -0
  50. package/dist/assets/{chunk-VR4S4FIN-DX6lXnNx.js → chunk-VR4S4FIN-7RoZQffO.js} +1 -1
  51. package/dist/assets/{chunk-WYO6CB5R-DLqMXfFC.js → chunk-WYO6CB5R-DEuS6N1R.js} +1 -1
  52. package/dist/assets/chunk-XXDRQBXY-Cx9N40wF.js +1 -0
  53. package/dist/assets/{chunk-YOTPTUD7-CY4obsV0.js → chunk-YOTPTUD7-9KBrosZL.js} +1 -1
  54. package/dist/assets/{chunk-ZGVPDNZ5-BleImZKe.js → chunk-ZGVPDNZ5-IYgN9f5A.js} +1 -1
  55. package/dist/assets/{chunk-ZIRB5QZD-C1TfdjpX.js → chunk-ZIRB5QZD-BUo0-zcp.js} +1 -1
  56. package/dist/assets/{circle-dashed-CEEssYIw.js → circle-dashed-LSYZTzM7.js} +1 -1
  57. package/dist/assets/classDiagram-OUVF2IWQ-DFN1v3PQ.js +1 -0
  58. package/dist/assets/classDiagram-v2-EOCWNBFH-DRvglADa.js +1 -0
  59. package/dist/assets/{client-C6YQx7A-.js → client-DudCRJw4.js} +1 -1
  60. package/dist/assets/{config-split-page-DKPelvjR.js → config-split-page-G7Kt2BnV.js} +1 -1
  61. package/dist/assets/confirm-dialog-DShT64F6.js +1 -0
  62. package/dist/assets/context-menu-rd7b_kVD.js +1 -0
  63. package/dist/assets/{cose-bilkent-JH36ORCC-CVgUHXkj.js → cose-bilkent-JH36ORCC-DhqbC1fR.js} +1 -1
  64. package/dist/assets/{createLucideIcon-CF8U9dh7.js → createLucideIcon-D7hvIHV-.js} +1 -1
  65. package/dist/assets/cynefin-VYW2F7L2-DXUGnpJH.js +1 -0
  66. package/dist/assets/{cynefinDiagram-TSTJHNR4-cdfImbfI.js → cynefinDiagram-TSTJHNR4-C-TBRi5V.js} +1 -1
  67. package/dist/assets/{dagre-CHNN9L9I.js → dagre-CrgsRasE.js} +1 -1
  68. package/dist/assets/{dagre-VKFMJZFB-PscaENwr.js → dagre-VKFMJZFB-BmafJymp.js} +1 -1
  69. package/dist/assets/{desktop-capabilities-page-BENHdKfk.js → desktop-capabilities-page-CzqzFZUY.js} +1 -1
  70. package/dist/assets/desktop-update-config-CgeQ0Ffo.js +1 -0
  71. package/dist/assets/{diagram-FQU43EPY-CH_wye41.js → diagram-FQU43EPY-BoOMwa6D.js} +1 -1
  72. package/dist/assets/{diagram-G47NLZAW-Dtjpmi6D.js → diagram-G47NLZAW-Le7N6Uym.js} +1 -1
  73. package/dist/assets/{diagram-NH7WQ7WH-UdjLHyaz.js → diagram-NH7WQ7WH-Fbpj3-MO.js} +1 -1
  74. package/dist/assets/{diagram-OA4YK3LP-DYsToEaL.js → diagram-OA4YK3LP-D3QuGGuu.js} +1 -1
  75. package/dist/assets/{diagram-WEI45ONY-CA7kuIer.js → diagram-WEI45ONY-Cxu1uiN6.js} +1 -1
  76. package/dist/assets/dialog-C7glmAQJ.js +5 -0
  77. package/dist/assets/{doc-browser-context-CPLWVFLd.js → doc-browser-context-BlhBAOLH.js} +1 -1
  78. package/dist/assets/doc-browser-dBiR9Qi0.js +1 -0
  79. package/dist/assets/{docx-preview-D--EsUWi.js → docx-preview-CkU2psnd.js} +1 -1
  80. package/dist/assets/download-BYgECE0n.js +1 -0
  81. package/dist/assets/{ebnfDiagram-CCIWWBDH-BrjaoOuq.js → ebnfDiagram-CCIWWBDH-CCAjuQM6.js} +1 -1
  82. package/dist/assets/{erDiagram-Q63AITRT-nZQa2kCM.js → erDiagram-Q63AITRT-BuzUAq-d.js} +1 -1
  83. package/dist/assets/es2015-CsjAAQ6X.js +41 -0
  84. package/dist/assets/eventmodeling-45OFAUF4-ZBfVS3ZC.js +1 -0
  85. package/dist/assets/{extensions-CsWT7x3v.js → extensions-qYL-NpL3.js} +1 -1
  86. package/dist/assets/{eye-DJtw5KJ5.js → eye-BBuTzJ53.js} +1 -1
  87. package/dist/assets/flowDiagram-23GEKE2U-CqdsKk6x.js +1 -0
  88. package/dist/assets/{form-actions-DDQk0END.js → form-actions-CLGWO52v.js} +1 -1
  89. package/dist/assets/{ganttDiagram-NO4QXBWP-CyFZHIHN.js → ganttDiagram-NO4QXBWP-cJmWVY8F.js} +1 -1
  90. package/dist/assets/gitGraph-TEB2WS4Q-fHYFd9Es.js +1 -0
  91. package/dist/assets/{gitGraphDiagram-IHSO6WYX-Dfpq3z2M.js → gitGraphDiagram-IHSO6WYX-F58o-vtP.js} +1 -1
  92. package/dist/assets/{i18n-BjPkAKMQ.js → i18n-qBGEbcW6.js} +2 -2
  93. package/dist/assets/info-DKCQHKI2-CSaOL_bb.js +1 -0
  94. package/dist/assets/{infoDiagram-FWYZ7A6U-Diiz0k1E.js → infoDiagram-FWYZ7A6U-Gt59b83C.js} +1 -1
  95. package/dist/assets/{ishikawaDiagram-FXEZZL3T-B3R3SN4M.js → ishikawaDiagram-FXEZZL3T-1u4IAj1I.js} +1 -1
  96. package/dist/assets/{journeyDiagram-5HDEW3XC-DFOLKiNv.js → journeyDiagram-5HDEW3XC-CCQsfV-5.js} +1 -1
  97. package/dist/assets/{kanban-definition-HUTT4EX6-BMXpDp3g.js → kanban-definition-HUTT4EX6-vlB6O_Zn.js} +1 -1
  98. package/dist/assets/{keyboard-shortcuts-settings-page-hqTP7akj.js → keyboard-shortcuts-settings-page-B-wK_6zG.js} +1 -1
  99. package/dist/assets/{line-64_FhCoJ.js → line-D_bHubTL.js} +1 -1
  100. package/dist/assets/{linear-aF1fg7bK.js → linear-ChlwuS5y.js} +1 -1
  101. package/dist/assets/loader-circle-BA7EMkih.js +1 -0
  102. package/dist/assets/{login-page-BUwshAq4.js → login-page-Cd84lpVV.js} +1 -1
  103. package/dist/assets/login-page-VdMyNkFf.js +1 -0
  104. package/dist/assets/main-CR0SrTBf.js +2 -0
  105. package/dist/assets/main-DXH6EdXI.css +1 -0
  106. package/dist/assets/main-i18n-catalog.config-Btp_OowF.js +1 -0
  107. package/dist/assets/{map-BC3Amra3.js → map-oGHTsIu8.js} +1 -1
  108. package/dist/assets/mcp-marketplace-page-jacRwiI-.js +1 -0
  109. package/dist/assets/{mermaid-parser.core-DdMcZwnE.js → mermaid-parser.core-CuRe1ZG4.js} +2 -2
  110. package/dist/assets/{mermaid.core-BHdiEBbI.js → mermaid.core-u4YCFKRf.js} +3 -3
  111. package/dist/assets/{mindmap-definition-LN4V7U3C-CtHVaZyV.js → mindmap-definition-LN4V7U3C-DeJAQYTu.js} +1 -1
  112. package/dist/assets/{model-config-page-BSsHOhV7.js → model-config-page-D0DQlSDa.js} +1 -1
  113. package/dist/assets/{navigation-link-D-fxdv0i.js → navigation-link-32iM1-e6.js} +1 -1
  114. package/dist/assets/{ordinal-6b3jw4cz.js → ordinal-C6JFfkKN.js} +1 -1
  115. package/dist/assets/packet-7NZHBO7P-D-YFTczs.js +1 -0
  116. package/dist/assets/panel-app-CC0bVSu4.js +1 -0
  117. package/dist/assets/panel-app-i18n-catalog.config-LUSBp5Ce.js +1 -0
  118. package/dist/assets/{panelStandalone-BeI0Ndsx.js → panelStandalone-C9EKaApN.js} +2 -2
  119. package/dist/assets/{pegDiagram-2B236MQR-BtbG3Bgu.js → pegDiagram-2B236MQR-nVMkxShH.js} +1 -1
  120. package/dist/assets/pie-RZYD4A2V-VEnYu9s5.js +1 -0
  121. package/dist/assets/{pieDiagram-ENE6RG2P-FmLl2hzN.js → pieDiagram-ENE6RG2P-8aUH44QZ.js} +1 -1
  122. package/dist/assets/{privacy-settings-page-pW_1naDz.js → privacy-settings-page-Bxxx12OO.js} +1 -1
  123. package/dist/assets/projects-D56yAPNT.js +1 -0
  124. package/dist/assets/provider-models-C3frdXeW.js +1 -0
  125. package/dist/assets/{provider-scoped-model-input-CVbtT8L9.js → provider-scoped-model-input-CYetrPYZ.js} +1 -1
  126. package/dist/assets/providers-config-page-DESB1h8g.js +1 -0
  127. package/dist/assets/{puzzle-kKiVft9-.js → puzzle-CWHfFyYN.js} +1 -1
  128. package/dist/assets/pwa-CiGUjpcd.js +1 -0
  129. package/dist/assets/{quadrantDiagram-ABIIQ3AL-7-OzMNpF.js → quadrantDiagram-ABIIQ3AL-EY8hpFv8.js} +1 -1
  130. package/dist/assets/radar-I7S5WNFK-BFNIekC1.js +1 -0
  131. package/dist/assets/railroad-3IZDKUUU-lVqJ3Hak.js +1 -0
  132. package/dist/assets/railroad-abnf-AHOZXSZD-DYaefdFC.js +1 -0
  133. package/dist/assets/railroad-ebnf-EBAXGLYW-CTAk6lVq.js +1 -0
  134. package/dist/assets/railroad-peg-LSFZ7HO6-Cob294tg.js +1 -0
  135. package/dist/assets/{railroadDiagram-RFXS5EU6-B_UVtLJO.js → railroadDiagram-RFXS5EU6-CNhcIeji.js} +1 -1
  136. package/dist/assets/{refresh-cw-BeG0y6yZ.js → refresh-cw-y3L6PyLt.js} +1 -1
  137. package/dist/assets/remote-DiGFV8nP.js +1 -0
  138. package/dist/assets/{requirementDiagram-TGXJPOKE-D8TO2YeZ.js → requirementDiagram-TGXJPOKE-D8Dcs-Rt.js} +1 -1
  139. package/dist/assets/{rotate-cw-Ci2dcTO5.js → rotate-cw-CNE9qSHi.js} +1 -1
  140. package/dist/assets/{runtime-config-page-DCXN0nkS.js → runtime-config-page-5o98T-TD.js} +1 -1
  141. package/dist/assets/{sankeyDiagram-HTMAVEWB-CUzIcq0B.js → sankeyDiagram-HTMAVEWB-HTrb6aa-.js} +1 -1
  142. package/dist/assets/{save-Cr895abt.js → save-B0p5a0ET.js} +1 -1
  143. package/dist/assets/{search-config-page-CNQd481d.js → search-config-page-CpGcnIzi.js} +1 -1
  144. package/dist/assets/{secrets-config-page--q7M58L8.js → secrets-config-page-C4VTiW3r.js} +1 -1
  145. package/dist/assets/security-config-C5-RQVpS.js +1 -0
  146. package/dist/assets/select-CctTN205.js +1 -0
  147. package/dist/assets/{sequenceDiagram-DBY2YBRQ-kqJzXbwu.js → sequenceDiagram-DBY2YBRQ-6f_4X16Y.js} +1 -1
  148. package/dist/assets/service-apps-panel-CKE5HKNY.js +1 -0
  149. package/dist/assets/{settings-2-IodCcaXd.js → settings-2-EPflrNk6.js} +1 -1
  150. package/dist/assets/{sizeCapture-X5ZJPWSS-D9ISogq1.js → sizeCapture-X5ZJPWSS-Ck9rsuwL.js} +1 -1
  151. package/dist/assets/{src-Bc0PoUJ1.js → src-C8AUg09O.js} +1 -1
  152. package/dist/assets/{stateDiagram-2N3HPSRC-CqH5xc9W.js → stateDiagram-2N3HPSRC-Cm33MLHu.js} +1 -1
  153. package/dist/assets/stateDiagram-v2-6OUMAXLB-BrNv-raG.js +1 -0
  154. package/dist/assets/{status-dot-DUYSKRCU.js → status-dot-CensgJZC.js} +1 -1
  155. package/dist/assets/{swimlanes-5IMT3BWC-mwejV-rW.js → swimlanes-5IMT3BWC-B107vmvM.js} +2 -2
  156. package/dist/assets/swimlanesDiagram-G3AALYLV-ntcxaPXp.js +8 -0
  157. package/dist/assets/{tag-chip-CQU51ST3.js → tag-chip-Bjzb4-qW.js} +1 -1
  158. package/dist/assets/{theme-provider-D-zZkXxS.js → theme-provider-hlle7Skk.js} +1 -1
  159. package/dist/assets/{timeline-definition-FHXFAJF6-CuikIDlX.js → timeline-definition-FHXFAJF6-CJEoAj33.js} +1 -1
  160. package/dist/assets/{trash-2-BDdqDh6E.js → trash-2-C5KH5Rw9.js} +1 -1
  161. package/dist/assets/treeView-QDETBFTQ-DbFmr-gw.js +1 -0
  162. package/dist/assets/treemap-6X3UGDF4-C8aY96pi.js +1 -0
  163. package/dist/assets/{use-config-Bxx7DdaL.js → use-config-CPl_F79t.js} +1 -1
  164. package/dist/assets/use-desktop-capabilities-CEg9-DrQ.js +1 -0
  165. package/dist/assets/{use-viewport-layout-CDcca0YL.js → use-viewport-layout-CPZUmTPi.js} +1 -1
  166. package/dist/assets/{vennDiagram-L72KCM5P-BD0P0qHh.js → vennDiagram-L72KCM5P-DzsUHk5I.js} +1 -1
  167. package/dist/assets/wardley-OPB4EBWU-D8d6DbbJ.js +1 -0
  168. package/dist/assets/{wardleyDiagram-EHGQE667-C6ma3t5D.js → wardleyDiagram-EHGQE667-1NfFs5ua.js} +1 -1
  169. package/dist/assets/x--xxUvHKW.js +1 -0
  170. package/dist/assets/{xychartDiagram-FW5EYKEG-D7E40NBk.js → xychartDiagram-FW5EYKEG-Ci8AI3Qo.js} +1 -1
  171. package/dist/index.html +37 -36
  172. package/dist/panel-standalone.html +11 -11
  173. package/package.json +8 -8
  174. package/src/app/components/layout/__tests__/app-layout.test.tsx +0 -1
  175. package/src/app/components/layout/__tests__/sidebar.layout.test.tsx +36 -16
  176. package/src/app/components/layout/sidebar-items.tsx +27 -18
  177. package/src/app/components/layout/sidebar-rail.styles.ts +11 -2
  178. package/src/app/components/layout/sidebar.tsx +3 -2
  179. package/src/app/configs/app-navigation.config.ts +39 -86
  180. package/src/features/agents/components/agent-details-dialog.tsx +3 -3
  181. package/src/features/agents/components/agents-page.tsx +5 -4
  182. package/src/features/chat/components/layout/__tests__/chat-page-shell.test.tsx +0 -2
  183. package/src/features/chat/components/layout/chat-page-shell.tsx +0 -2
  184. package/src/features/chat/components/layout/chat-sidebar-desktop-layout.tsx +0 -1
  185. package/src/features/chat/features/conversation/components/chat-voice-input-panel.tsx +3 -2
  186. package/src/features/chat/features/conversation/hooks/__tests__/use-chat-voice-input.test.tsx +4 -0
  187. package/src/features/chat/features/message/components/__tests__/chat-message-list.container.test.tsx +32 -47
  188. package/src/features/chat/features/message/components/chat-message-list.container.tsx +6 -10
  189. package/src/features/chat/features/message/utils/__tests__/chat-message-process-summary.utils.test.ts +52 -6
  190. package/src/features/chat/features/message/utils/chat-message-process-summary.utils.ts +22 -37
  191. package/src/features/chat/features/session/components/__tests__/chat-sidebar-project-groups.test.tsx +1 -1
  192. package/src/features/chat/features/session/components/__tests__/chat-sidebar-session-item.test.tsx +2 -1
  193. package/src/features/chat/features/session/components/chat-sidebar-project-groups.tsx +4 -2
  194. package/src/features/chat/features/session/components/chat-sidebar-session-item.tsx +9 -6
  195. package/src/features/chat/features/session/components/session-header/__tests__/chat-session-header-actions.test.tsx +2 -2
  196. package/src/features/chat/features/session/components/session-header/chat-session-header-actions.tsx +17 -17
  197. package/src/features/chat/features/session/components/session-header/chat-session-header-menu-item.tsx +4 -4
  198. package/src/features/chat/features/session/components/session-header/chat-session-more-actions-menu.tsx +13 -6
  199. package/src/features/chat/features/workspace/components/chat-session-workspace-panel.tsx +1 -1
  200. package/src/features/chat/managers/chat-voice-input.manager.ts +5 -3
  201. package/src/features/chat/utils/chat-voice-permissions.utils.test.ts +12 -1
  202. package/src/features/chat/utils/chat-voice-permissions.utils.ts +6 -1
  203. package/src/features/cron/components/cron-job-row.tsx +11 -9
  204. package/src/features/inbox/components/inbox-reader-dialog.tsx +4 -2
  205. package/src/features/inbox/pages/inbox-page.tsx +10 -5
  206. package/src/features/marketplace/components/mcp/mcp-marketplace-card.tsx +1 -1
  207. package/src/features/panel-apps/components/__tests__/panel-app-list-item.test.tsx +6 -4
  208. package/src/features/panel-apps/components/__tests__/panel-app-toolbar.test.tsx +6 -4
  209. package/src/features/panel-apps/components/panel-app-list-item.tsx +1 -1
  210. package/src/features/panel-apps/components/panel-app-toolbar.tsx +7 -7
  211. package/src/features/projects/components/project-skills.tsx +2 -2
  212. package/src/features/right-panel-resources/components/__tests__/page-resource-sidebar-nav.test.tsx +27 -2
  213. package/src/features/right-panel-resources/components/page-resource-actions-menu.tsx +4 -22
  214. package/src/features/right-panel-resources/components/page-resource-sidebar-nav.tsx +10 -13
  215. package/src/features/right-panel-resources/hooks/use-page-resource-actions.ts +56 -3
  216. package/src/features/right-panel-resources/index.ts +0 -1
  217. package/src/features/service-apps/components/service-app-list-item.tsx +3 -2
  218. package/src/features/system-status/components/__tests__/desktop-update-config.test.tsx +40 -0
  219. package/src/features/system-status/components/desktop-update-config.tsx +19 -1
  220. package/src/platforms/desktop/components/__tests__/desktop-app-shell.test.tsx +0 -1
  221. package/src/platforms/desktop/components/desktop-app-shell.tsx +0 -2
  222. package/src/platforms/mobile/components/mobile-app-shell.tsx +0 -2
  223. package/src/platforms/mobile/components/mobile-settings-shell.tsx +8 -3
  224. package/src/shared/components/ui/context-menu/__tests__/context-menu.test.tsx +83 -1
  225. package/src/shared/components/ui/context-menu/context-menu-items.tsx +173 -0
  226. package/src/shared/components/ui/context-menu/context-menu.tsx +93 -114
  227. package/src/shared/components/ui/dialog.tsx +4 -0
  228. package/src/shared/components/ui/overlays/sheet.tsx +4 -0
  229. package/src/shared/components/ui/popover.tsx +10 -4
  230. package/src/shared/lib/i18n/locales/en-US/chat.json +8 -1
  231. package/src/shared/lib/i18n/locales/en-US/core.json +3 -1
  232. package/src/shared/lib/i18n/locales/en-US/desktop-update.json +1 -0
  233. package/src/shared/lib/i18n/locales/zh-CN/chat.json +8 -1
  234. package/src/shared/lib/i18n/locales/zh-CN/core.json +11 -9
  235. package/src/shared/lib/i18n/locales/zh-CN/desktop-update.json +1 -0
  236. package/src/shared/lib/transport/__tests__/browser-realtime-recovery.service.test.ts +27 -2
  237. package/src/shared/lib/transport/__tests__/local-transport.service.test.ts +20 -0
  238. package/src/shared/lib/transport/__tests__/remote.transport.test.ts +26 -0
  239. package/src/shared/lib/transport/browser-realtime-recovery.service.ts +32 -5
  240. package/src/shared/lib/transport/local-transport.service.ts +1 -6
  241. package/src/shared/lib/transport/remote-transport.service.ts +22 -1
  242. package/dist/assets/api-D9e9cHtW.js +0 -20
  243. package/dist/assets/app-presenter-provider-J9IgTbc9.js +0 -124
  244. package/dist/assets/architecture-TIHT7OUA-BWywYH_m.js +0 -1
  245. package/dist/assets/channel-1nKIAEXr.js +0 -1
  246. package/dist/assets/chunk-2Q5K7J3B-CAwv72v4.js +0 -1
  247. package/dist/assets/chunk-VAUOI2AC-DnaryNw2.js +0 -1
  248. package/dist/assets/chunk-XXDRQBXY-BToo1uqb.js +0 -1
  249. package/dist/assets/classDiagram-OUVF2IWQ-D_TusZGx.js +0 -1
  250. package/dist/assets/classDiagram-v2-EOCWNBFH-B1NFZTmS.js +0 -1
  251. package/dist/assets/confirm-dialog-BCGeh61X.js +0 -1
  252. package/dist/assets/cynefin-VYW2F7L2-CJZSs5s_.js +0 -1
  253. package/dist/assets/desktop-update-config-BJMTQMRS.js +0 -1
  254. package/dist/assets/dialog-DQXou6pg.js +0 -5
  255. package/dist/assets/dist-CiC9Fd9J.js +0 -1
  256. package/dist/assets/doc-browser-CCYS_csH.js +0 -1
  257. package/dist/assets/es2015-DHRpDU-q.js +0 -41
  258. package/dist/assets/eventmodeling-45OFAUF4-rc7kUq4V.js +0 -1
  259. package/dist/assets/flowDiagram-23GEKE2U-DAjaeGO8.js +0 -1
  260. package/dist/assets/gitGraph-TEB2WS4Q-Ce0gf6ps.js +0 -1
  261. package/dist/assets/info-DKCQHKI2-Cp-Tr78m.js +0 -1
  262. package/dist/assets/loader-circle-B-4WwzBj.js +0 -1
  263. package/dist/assets/login-page-CdlI6B8e.js +0 -1
  264. package/dist/assets/main-BVYdyT60.css +0 -1
  265. package/dist/assets/main-DYEALeoQ.js +0 -2
  266. package/dist/assets/main-i18n-catalog.config-1o2hjZ0m.js +0 -1
  267. package/dist/assets/mcp-marketplace-page-CRRQT6SP.js +0 -1
  268. package/dist/assets/packet-7NZHBO7P-BOJuCraS.js +0 -1
  269. package/dist/assets/panel-app-2EACRB8p.js +0 -1
  270. package/dist/assets/panel-app-i18n-catalog.config-kQO7QAwH.js +0 -1
  271. package/dist/assets/pie-RZYD4A2V-D-FEO81x.js +0 -1
  272. package/dist/assets/projects-k6SLda_A.js +0 -1
  273. package/dist/assets/provider-models-mVzuEQBS.js +0 -1
  274. package/dist/assets/providers-config-page-rBlVrHkb.js +0 -1
  275. package/dist/assets/pwa-BgGM4kkY.js +0 -1
  276. package/dist/assets/radar-I7S5WNFK-Bq3eK1vK.js +0 -1
  277. package/dist/assets/railroad-3IZDKUUU-C3uZ1esg.js +0 -1
  278. package/dist/assets/railroad-abnf-AHOZXSZD-D3iZd1KT.js +0 -1
  279. package/dist/assets/railroad-ebnf-EBAXGLYW-Dl0FWqZu.js +0 -1
  280. package/dist/assets/railroad-peg-LSFZ7HO6-B51SbzGp.js +0 -1
  281. package/dist/assets/remote-wYWqir2w.js +0 -1
  282. package/dist/assets/security-config-CJb2pK2O.js +0 -1
  283. package/dist/assets/select-Bq-UmfCK.js +0 -1
  284. package/dist/assets/service-apps-panel-iLLEByRF.js +0 -1
  285. package/dist/assets/stateDiagram-v2-6OUMAXLB-CcOGfDdx.js +0 -1
  286. package/dist/assets/swimlanesDiagram-G3AALYLV-BMI2k8RZ.js +0 -8
  287. package/dist/assets/treeView-QDETBFTQ-C0_07QI1.js +0 -1
  288. package/dist/assets/treemap-6X3UGDF4-Djpx6ccG.js +0 -1
  289. package/dist/assets/use-desktop-capabilities-BTgdFgJy.js +0 -1
  290. package/dist/assets/wardley-OPB4EBWU-DZVFxu81.js +0 -1
  291. package/dist/assets/x-LUUwl8rK.js +0 -1
  292. package/src/features/right-panel-resources/components/current-page-actions.tsx +0 -36
  293. /package/dist/assets/{array-B0sEvCP9.js → array-BV9OtnO8.js} +0 -0
  294. /package/dist/assets/{chunk-KEIR6QF5-BeO7fxDQ.js → chunk-KEIR6QF5-Cx-8EJ4_.js} +0 -0
  295. /package/dist/assets/{chunk-Y2CYZVJY-JQfgEV36.js → chunk-Y2CYZVJY-Dy1ufvjf.js} +0 -0
  296. /package/dist/assets/{config-hints-D3OuwoEY.js → config-hints-DbmiyTKg.js} +0 -0
  297. /package/dist/assets/{cytoscape.esm-CzJ9uXgi.js → cytoscape.esm-CSCjuT4Z.js} +0 -0
  298. /package/dist/assets/{defaultLocale-USmzX3VG.js → defaultLocale-DWTiNsbT.js} +0 -0
  299. /package/dist/assets/{dist-BYF6XVna.js → dist-BVCSkfWY.js} +0 -0
  300. /package/dist/assets/{graphlib-DEFMkHdW.js → graphlib-B1yb1Wl8.js} +0 -0
  301. /package/dist/assets/{host-capabilities-C0MmWd3M.js → host-capabilities-DuIFVsvM.js} +0 -0
  302. /package/dist/assets/{init-04ErALoz.js → init-BF6GH5oe.js} +0 -0
  303. /package/dist/assets/{jszip.min-BnQ5NRy2.js → jszip.min-D12teKzP.js} +0 -0
  304. /package/dist/assets/{middleware-CAXxl-af.js → middleware-B--6mAUM.js} +0 -0
  305. /package/dist/assets/{navigation-history-CyawyVz0.js → navigation-history-CzErJuPG.js} +0 -0
  306. /package/dist/assets/{path-CzxQ9n2G.js → path-C56XkznJ.js} +0 -0
  307. /package/dist/assets/{rough.esm-pT9fdsW3.js → rough.esm-CGfeEbtE.js} +0 -0
  308. /package/dist/assets/{xlsx-BRpCSiE4.js → xlsx-sds5nUpL.js} +0 -0
@@ -8,9 +8,11 @@ import {
8
8
  type MouseEvent,
9
9
  type ReactElement,
10
10
  type ReactNode,
11
- } from 'react';
12
- import { createPortal } from 'react-dom';
13
- import { cn } from '@/shared/lib/utils';
11
+ } from "react";
12
+ import { createPortal } from "react-dom";
13
+ import { cn } from "@/shared/lib/utils";
14
+ import { ContextMenuItems, CONTEXT_MENU_SURFACE_CLASS } from "./context-menu-items";
15
+ export { ContextMenuItems } from "./context-menu-items";
14
16
 
15
17
  type ContextMenuItemBase = {
16
18
  key: string;
@@ -25,15 +27,23 @@ type ContextMenuItemBase = {
25
27
  export type ContextMenuItem = ContextMenuItemBase &
26
28
  (
27
29
  | {
30
+ children?: never;
28
31
  download?: string;
29
32
  href: string;
30
33
  onSelect?: () => void;
31
34
  }
32
35
  | {
36
+ children?: never;
33
37
  download?: never;
34
38
  href?: never;
35
39
  onSelect: () => void;
36
40
  }
41
+ | {
42
+ children: readonly ContextMenuGroup[];
43
+ download?: never;
44
+ href?: never;
45
+ onSelect?: never;
46
+ }
37
47
  );
38
48
 
39
49
  export type ContextMenuGroup = {
@@ -42,7 +52,7 @@ export type ContextMenuGroup = {
42
52
  };
43
53
 
44
54
  type ContextMenuPosition = {
45
- align: 'start' | 'end';
55
+ align: "start" | "end";
46
56
  x: number;
47
57
  y: number;
48
58
  trigger: HTMLElement;
@@ -53,10 +63,18 @@ type ContextMenuController = {
53
63
  toggleFromButton: (trigger: HTMLElement) => void;
54
64
  };
55
65
 
56
- const ContextMenuControllerContext = createContext<ContextMenuController | null>(null);
66
+ const ContextMenuControllerContext =
67
+ createContext<ContextMenuController | null>(null);
57
68
 
58
69
  const CONTEXT_MENU_EDGE_GAP = 8;
59
70
 
71
+ /** Radix dismisses overlays during capture, before the inner menu handles Escape. */
72
+ export function deferEscapeToNestedMenu(event: KeyboardEvent): boolean {
73
+ if (!(event.target instanceof Element) || !event.target.closest('[data-menu-escape-owner]')) return false;
74
+ event.preventDefault();
75
+ return true;
76
+ }
77
+
60
78
  function ContextMenuSurface({
61
79
  groups,
62
80
  label,
@@ -70,7 +88,8 @@ function ContextMenuSurface({
70
88
  }) {
71
89
  const menuRef = useRef<HTMLDivElement | null>(null);
72
90
  // Keep modal menus inside the dialog's focus and pointer-event boundary.
73
- const portalHost = position.trigger.closest<HTMLElement>('[role="dialog"]') ?? document.body;
91
+ const portalHost =
92
+ position.trigger.closest<HTMLElement>('[role="dialog"]') ?? document.body;
74
93
  const isModal = portalHost !== document.body;
75
94
 
76
95
  useLayoutEffect(() => {
@@ -78,124 +97,77 @@ function ContextMenuSurface({
78
97
  if (!menu) {
79
98
  return;
80
99
  }
81
- const bounds = isModal ? portalHost.getBoundingClientRect() : { left: 0, top: 0, right: window.innerWidth, bottom: window.innerHeight, height: window.innerHeight };
82
- menu.style.maxHeight = `${Math.max(0, bounds.height - CONTEXT_MENU_EDGE_GAP * 2)}px`;
83
- const rect = menu.getBoundingClientRect();
84
- const preferredLeft = position.align === 'end' ? position.x - rect.width : position.x;
85
- const left = Math.max(
86
- bounds.left + CONTEXT_MENU_EDGE_GAP,
87
- Math.min(preferredLeft, bounds.right - rect.width - CONTEXT_MENU_EDGE_GAP),
88
- );
89
- const top = Math.max(
90
- bounds.top + CONTEXT_MENU_EDGE_GAP,
91
- Math.min(position.y, bounds.bottom - rect.height - CONTEXT_MENU_EDGE_GAP),
92
- );
93
- menu.style.left = `${left - bounds.left}px`;
94
- menu.style.top = `${top - bounds.top}px`;
95
- menu.querySelector<HTMLElement>('[role="menuitem"]:not(:disabled)')?.focus();
100
+ const reposition = () => {
101
+ const bounds = isModal
102
+ ? portalHost.getBoundingClientRect()
103
+ : {
104
+ left: 0,
105
+ top: 0,
106
+ right: window.innerWidth,
107
+ bottom: window.innerHeight,
108
+ height: window.innerHeight,
109
+ };
110
+ menu.style.maxHeight = `${Math.max(0, bounds.height - CONTEXT_MENU_EDGE_GAP * 2)}px`;
111
+ const rect = menu.getBoundingClientRect();
112
+ const preferredLeft =
113
+ position.align === "end" ? position.x - rect.width : position.x;
114
+ const left = Math.max(
115
+ bounds.left + CONTEXT_MENU_EDGE_GAP,
116
+ Math.min(
117
+ preferredLeft,
118
+ bounds.right - rect.width - CONTEXT_MENU_EDGE_GAP,
119
+ ),
120
+ );
121
+ const top = Math.max(
122
+ bounds.top + CONTEXT_MENU_EDGE_GAP,
123
+ Math.min(
124
+ position.y,
125
+ bounds.bottom - rect.height - CONTEXT_MENU_EDGE_GAP,
126
+ ),
127
+ );
128
+ menu.style.left = `${left - bounds.left}px`;
129
+ menu.style.top = `${top - bounds.top}px`;
130
+ };
131
+ reposition();
132
+ menu
133
+ .querySelector<HTMLElement>('[role="menuitem"]:not(:disabled)')
134
+ ?.focus();
135
+ const observer = new ResizeObserver(reposition);
136
+ observer.observe(menu);
137
+ return () => observer.disconnect();
96
138
  }, [position, portalHost, isModal]);
97
139
 
98
- const moveFocus = (direction: 1 | -1) => {
99
- const items = Array.from(menuRef.current?.querySelectorAll<HTMLElement>('[role="menuitem"]:not(:disabled)') ?? []);
100
- if (items.length === 0) {
101
- return;
102
- }
103
- const currentIndex = items.indexOf(document.activeElement as HTMLElement);
104
- const nextIndex = currentIndex < 0 ? 0 : (currentIndex + direction + items.length) % items.length;
105
- items[nextIndex]?.focus();
106
- };
107
-
108
140
  return createPortal(
109
141
  <div
110
- className={cn("pointer-events-auto inset-0 z-[var(--z-tooltip)]", isModal ? "absolute" : "fixed")}
142
+ className={cn(
143
+ "pointer-events-auto inset-0 z-[var(--z-tooltip)]",
144
+ isModal ? "absolute" : "fixed",
145
+ )}
146
+ data-context-menu-layer=""
111
147
  onContextMenu={(event) => event.preventDefault()}
112
148
  onPointerDown={() => onClose(true)}
113
149
  >
114
150
  <div
115
151
  ref={menuRef}
116
152
  role="menu"
153
+ data-menu-escape-owner=""
117
154
  data-theme-overlay="menu"
118
155
  aria-label={label}
119
- className={cn("min-w-52 max-w-72 overflow-y-auto rounded-xl border border-border bg-popover p-1 text-popover-foreground shadow-[0_18px_48px_-20px_rgba(15,23,42,0.42)]", isModal ? "absolute" : "fixed")}
156
+ className={cn(
157
+ CONTEXT_MENU_SURFACE_CLASS,
158
+ isModal ? "absolute" : "fixed",
159
+ )}
120
160
  style={{ left: position.x, top: position.y }}
121
161
  onPointerDown={(event) => event.stopPropagation()}
122
162
  onKeyDown={(event) => {
123
- if (event.key === 'Escape') {
163
+ if (event.key === "Escape") {
124
164
  event.preventDefault();
125
165
  event.stopPropagation();
126
166
  onClose(true);
127
- } else if (event.key === 'ArrowDown') {
128
- event.preventDefault();
129
- moveFocus(1);
130
- } else if (event.key === 'ArrowUp') {
131
- event.preventDefault();
132
- moveFocus(-1);
133
- } else if (event.key === 'Home' || event.key === 'End') {
134
- event.preventDefault();
135
- const items = menuRef.current?.querySelectorAll<HTMLElement>('[role="menuitem"]:not(:disabled)');
136
- items?.[event.key === 'Home' ? 0 : items.length - 1]?.focus();
137
167
  }
138
168
  }}
139
169
  >
140
- {groups.map((group, groupIndex) => (
141
- <div key={group.key} role="group">
142
- {groupIndex > 0 ? <div className="my-1 h-px bg-border" /> : null}
143
- {group.items.map((item) => {
144
- const itemClassName = cn(
145
- 'flex h-8 w-full items-center gap-2 rounded-lg px-2.5 text-left text-[13px] font-medium outline-none transition-colors',
146
- item.destructive
147
- ? 'text-destructive hover:bg-destructive/10 focus-visible:bg-destructive/10'
148
- : 'text-foreground hover:bg-muted focus-visible:bg-muted',
149
- 'disabled:pointer-events-none disabled:opacity-45',
150
- );
151
- const content = (
152
- <>
153
- {item.icon ? (
154
- <span
155
- className={cn(
156
- 'inline-flex h-4 w-4 shrink-0 items-center justify-center',
157
- item.destructive ? 'text-destructive' : 'text-muted-foreground',
158
- )}
159
- >
160
- {item.icon}
161
- </span>
162
- ) : null}
163
- <span className="min-w-0 flex-1 truncate">{item.label}</span>
164
- </>
165
- );
166
- return item.href ? (
167
- <a
168
- key={item.key}
169
- role="menuitem"
170
- className={itemClassName}
171
- href={item.href}
172
- download={item.download}
173
- onClick={() => {
174
- item.onSelect?.();
175
- onClose(false);
176
- }}
177
- >
178
- {content}
179
- </a>
180
- ) : (
181
- <button
182
- key={item.key}
183
- type="button"
184
- role="menuitem"
185
- disabled={item.disabled}
186
- aria-pressed={item.pressed}
187
- className={itemClassName}
188
- onClick={() => {
189
- item.onSelect?.();
190
- onClose(item.restoreFocus !== false);
191
- }}
192
- >
193
- {content}
194
- </button>
195
- );
196
- })}
197
- </div>
198
- ))}
170
+ <ContextMenuItems groups={groups} onClose={onClose} />
199
171
  </div>
200
172
  </div>,
201
173
  portalHost,
@@ -208,7 +180,7 @@ export function ContextMenu({
208
180
  label,
209
181
  }: {
210
182
  children: ReactElement<{
211
- 'data-context-menu-open'?: string;
183
+ "data-context-menu-open"?: string;
212
184
  onContextMenu?: (event: MouseEvent) => void;
213
185
  }>;
214
186
  groups: readonly ContextMenuGroup[];
@@ -234,7 +206,7 @@ export function ContextMenu({
234
206
  }
235
207
  const bounds = trigger.getBoundingClientRect();
236
208
  setPosition({
237
- align: 'end',
209
+ align: "end",
238
210
  x: bounds.right,
239
211
  y: bounds.bottom,
240
212
  trigger,
@@ -242,7 +214,7 @@ export function ContextMenu({
242
214
  };
243
215
 
244
216
  const child = cloneElement(children, {
245
- 'data-context-menu-open': position ? '' : undefined,
217
+ "data-context-menu-open": position ? "" : undefined,
246
218
  onContextMenu: (event: MouseEvent) => {
247
219
  children.props.onContextMenu?.(event);
248
220
  if (event.defaultPrevented || visibleGroups.length === 0) {
@@ -252,7 +224,7 @@ export function ContextMenu({
252
224
  const trigger = event.currentTarget as HTMLElement;
253
225
  const bounds = trigger.getBoundingClientRect();
254
226
  setPosition({
255
- align: 'start',
227
+ align: "start",
256
228
  x: event.clientX || bounds.left,
257
229
  y: event.clientY || bounds.bottom,
258
230
  trigger,
@@ -261,10 +233,17 @@ export function ContextMenu({
261
233
  });
262
234
 
263
235
  return (
264
- <ContextMenuControllerContext.Provider value={{ isOpen: Boolean(position), toggleFromButton }}>
236
+ <ContextMenuControllerContext.Provider
237
+ value={{ isOpen: Boolean(position), toggleFromButton }}
238
+ >
265
239
  {child}
266
240
  {position ? (
267
- <ContextMenuSurface groups={visibleGroups} label={label} position={position} onClose={closeMenu} />
241
+ <ContextMenuSurface
242
+ groups={visibleGroups}
243
+ label={label}
244
+ position={position}
245
+ onClose={closeMenu}
246
+ />
268
247
  ) : null}
269
248
  </ContextMenuControllerContext.Provider>
270
249
  );
@@ -274,19 +253,19 @@ export function ContextMenuTrigger({
274
253
  children,
275
254
  }: {
276
255
  children: ReactElement<{
277
- 'aria-expanded'?: boolean;
278
- 'aria-haspopup'?: 'menu';
256
+ "aria-expanded"?: boolean;
257
+ "aria-haspopup"?: "menu";
279
258
  onClick?: (event: MouseEvent) => void;
280
259
  }>;
281
260
  }) {
282
261
  const controller = useContext(ContextMenuControllerContext);
283
262
  if (!controller) {
284
- throw new Error('ContextMenuTrigger must be rendered inside ContextMenu');
263
+ throw new Error("ContextMenuTrigger must be rendered inside ContextMenu");
285
264
  }
286
265
 
287
266
  return cloneElement(children, {
288
- 'aria-expanded': controller.isOpen,
289
- 'aria-haspopup': 'menu',
267
+ "aria-expanded": controller.isOpen,
268
+ "aria-haspopup": "menu",
290
269
  onClick: (event: MouseEvent) => {
291
270
  children.props.onClick?.(event);
292
271
  if (event.defaultPrevented) {
@@ -3,6 +3,7 @@ import * as DialogPrimitive from "@radix-ui/react-dialog"
3
3
  import { X } from "lucide-react"
4
4
 
5
5
  import { cn } from "@/shared/lib/utils"
6
+ import { deferEscapeToNestedMenu } from './context-menu/context-menu';
6
7
 
7
8
  const Dialog = DialogPrimitive.Root
8
9
 
@@ -42,6 +43,9 @@ const DialogContent = React.forwardRef<
42
43
  className
43
44
  )}
44
45
  {...props}
46
+ onEscapeKeyDown={(event) => {
47
+ if (!deferEscapeToNestedMenu(event)) props.onEscapeKeyDown?.(event);
48
+ }}
45
49
  >
46
50
  {children}
47
51
  <DialogPrimitive.Close className="absolute right-4 top-4 rounded-lg p-1 opacity-70 transition-all duration-fast hover:bg-[var(--interaction-hover)] hover:text-accent-foreground hover:opacity-100 focus:outline-none focus-visible:ring-1 focus-visible:ring-border disabled:pointer-events-none">
@@ -4,6 +4,7 @@ import { cva, type VariantProps } from "class-variance-authority";
4
4
  import { X } from "lucide-react";
5
5
 
6
6
  import { cn } from "@/shared/lib/utils";
7
+ import { deferEscapeToNestedMenu } from '@/shared/components/ui/context-menu/context-menu';
7
8
 
8
9
  const Sheet = SheetPrimitive.Root;
9
10
  const SheetTrigger = SheetPrimitive.Trigger;
@@ -63,6 +64,9 @@ const SheetContent = React.forwardRef<
63
64
  data-theme-overlay="sheet"
64
65
  className={cn(sheetVariants({ side }), className)}
65
66
  {...props}
67
+ onEscapeKeyDown={(event) => {
68
+ if (!deferEscapeToNestedMenu(event)) props.onEscapeKeyDown?.(event);
69
+ }}
66
70
  >
67
71
  {children}
68
72
  <SheetPrimitive.Close className="absolute right-4 top-4 rounded-lg p-1.5 text-muted-foreground opacity-75 transition-colors hover:bg-muted hover:text-foreground hover:opacity-100 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-border disabled:pointer-events-none">
@@ -2,6 +2,8 @@ import * as React from 'react';
2
2
  import * as PopoverPrimitive from '@radix-ui/react-popover';
3
3
 
4
4
  import { cn } from '@/shared/lib/utils';
5
+ import { deferEscapeToNestedMenu } from './context-menu/context-menu';
6
+ import { CONTEXT_MENU_SURFACE_CLASS } from './context-menu/context-menu-items';
5
7
 
6
8
  const FLOATING_CONTENT_AVAILABLE_HEIGHT_GAP = '2rem';
7
9
 
@@ -19,21 +21,25 @@ const PopoverAnchor = PopoverPrimitive.Anchor;
19
21
 
20
22
  const PopoverContent = React.forwardRef<
21
23
  React.ElementRef<typeof PopoverPrimitive.Content>,
22
- React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Content>
23
- >(({ className, sideOffset = 8, align = 'start', collisionPadding = 12, style, ...props }, ref) => (
24
+ React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Content> & { variant?: 'default' | 'menu' }
25
+ >(({ className, variant = 'default', sideOffset = 8, align = 'start', collisionPadding = 12, style, ...props }, ref) => (
24
26
  <PopoverPrimitive.Portal>
25
27
  <PopoverPrimitive.Content
26
28
  ref={ref}
27
- data-theme-overlay="popover"
29
+ data-theme-overlay={variant === 'menu' ? 'menu' : 'popover'}
28
30
  sideOffset={sideOffset}
29
31
  align={align}
30
32
  collisionPadding={collisionPadding}
31
33
  className={cn(
32
34
  'z-[var(--z-popover,10100)] w-72 overflow-x-hidden overflow-y-auto rounded-2xl border border-border bg-popover p-4 text-popover-foreground shadow-lg animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
33
- className
35
+ className,
36
+ variant === 'menu' && CONTEXT_MENU_SURFACE_CLASS
34
37
  )}
35
38
  style={{ maxHeight: POPOVER_CONTENT_MAX_HEIGHT, ...style }}
36
39
  {...props}
40
+ onEscapeKeyDown={(event) => {
41
+ if (!deferEscapeToNestedMenu(event)) props.onEscapeKeyDown?.(event);
42
+ }}
37
43
  />
38
44
  </PopoverPrimitive.Portal>
39
45
  ));
@@ -1,4 +1,5 @@
1
1
  {
2
+ "pageLayoutActions": "Layout and position",
2
3
  "pagePinLeft": "Pin to left sidebar",
3
4
  "pageUnpinLeft": "Unpin from left sidebar",
4
5
  "pageCopyUri": "Copy resource link",
@@ -281,7 +282,12 @@
281
282
  "chatHistoryLoadFailed": "Couldn’t load earlier messages",
282
283
  "chatHistoryRetry": "Retry",
283
284
  "chatProcessSummaryProcessed": "Processed",
284
- "chatProcessSummaryToolCalls": "{count} tool calls",
285
+ "chatProcessSummaryError": "Failed",
286
+ "chatProcessSummaryStopped": "Stopped",
287
+ "chatProcessDurationHoursMinutes": "{hours}h {minutes}m",
288
+ "chatProcessDurationHours": "{hours}h",
289
+ "chatProcessDurationMinutesSeconds": "{minutes}m {seconds}s",
290
+ "chatProcessDurationSeconds": "{seconds}s",
285
291
  "chatToolPayloadLoading": "Loading details",
286
292
  "chatToolPayloadLoadFailed": "Couldn’t load details. Try again",
287
293
  "chatToolActivityShowMore": "Show next {count} ({remaining} remaining)",
@@ -692,6 +698,7 @@
692
698
  "chatInputVoiceNoDevice": "No microphone was detected. Connect or enable an input device, then check again.",
693
699
  "chatInputVoiceDeviceBusy": "The microphone cannot be read. Another app or a system restriction may be preventing access. Close other recording apps, check your input device and retry.",
694
700
  "chatInputVoiceServiceDenied": "The browser denied access to speech recognition. Check its speech service settings or organization policy, or use a browser that supports speech recognition.",
701
+ "chatInputVoiceInsecureContext": "This page is using an insecure HTTP address, so the browser disables microphone access. Configure HTTPS for a VPS; localhost or 127.0.0.1 is available only for access from the same machine.",
695
702
  "chatInputVoiceUnsupported": "Speech input is not supported in this browser.",
696
703
  "chatInputVoiceNoSpeech": "Could not hear you. Please try again.",
697
704
  "chatInputVoiceFailed": "Voice input failed. Please try again.",
@@ -13,7 +13,9 @@
13
13
  "advanced": "Advanced",
14
14
  "settings": "Settings",
15
15
  "settingsGroupBasic": "Basic Configuration",
16
- "settingsGroupAdvanced": "Advanced Configuration",
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.",
@@ -1,4 +1,5 @@
1
1
  {
2
+ "pageLayoutActions": "布局与位置",
2
3
  "pagePinLeft": "固定到左侧页面",
3
4
  "pageUnpinLeft": "从左侧页面移除",
4
5
  "pageCopyUri": "复制资源链接",
@@ -281,7 +282,12 @@
281
282
  "chatHistoryLoadFailed": "较早消息加载失败",
282
283
  "chatHistoryRetry": "重试",
283
284
  "chatProcessSummaryProcessed": "已处理",
284
- "chatProcessSummaryToolCalls": "{count} 次工具调用",
285
+ "chatProcessSummaryError": "处理失败",
286
+ "chatProcessSummaryStopped": "已停止",
287
+ "chatProcessDurationHoursMinutes": "{hours}小时{minutes}分钟",
288
+ "chatProcessDurationHours": "{hours}小时",
289
+ "chatProcessDurationMinutesSeconds": "{minutes}分钟{seconds}秒",
290
+ "chatProcessDurationSeconds": "{seconds}秒",
285
291
  "chatToolPayloadLoading": "正在加载详情",
286
292
  "chatToolPayloadLoadFailed": "详情加载失败,点击重试",
287
293
  "chatToolActivityShowMore": "继续显示 {count} 项(剩余 {remaining} 项)",
@@ -692,6 +698,7 @@
692
698
  "chatInputVoiceNoDevice": "没有检测到麦克风。请连接或启用输入设备,再检测重试。",
693
699
  "chatInputVoiceDeviceBusy": "麦克风无法读取,可能被其他应用占用或被系统限制。请关闭其他录音应用,检查输入设备后重试。",
694
700
  "chatInputVoiceServiceDenied": "浏览器拒绝了语音识别服务。请检查浏览器的语音服务设置或组织策略,或使用支持语音识别的浏览器。",
701
+ "chatInputVoiceInsecureContext": "当前页面通过不安全的 HTTP 地址打开,浏览器会禁用麦克风。请为 VPS 配置 HTTPS;仅在本机访问时可使用 localhost 或 127.0.0.1。",
695
702
  "chatInputVoiceUnsupported": "当前浏览器不支持语音输入。",
696
703
  "chatInputVoiceNoSpeech": "没有听清,请再试一次。",
697
704
  "chatInputVoiceFailed": "语音输入失败,请重试。",
@@ -3,7 +3,7 @@
3
3
  "model": "模型",
4
4
  "searchChannels": "搜索渠道",
5
5
  "providers": "提供商",
6
- "extensions": "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
- "settingsGroupAdvanced": "高级配置",
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": "Extensions 管理",
143
- "extensionsPageDescription": "查看已发现的 Extension、能力和当前运行状态。",
144
+ "extensionsPageTitle": "扩展管理",
145
+ "extensionsPageDescription": "查看已发现的扩展、能力和当前运行状态。",
144
146
  "extensionsRefresh": "刷新",
145
- "extensionsLifecycleDescription": "Extension 由运行时按渠道和会话持续关注关系按需启停;这里集中查看全局能力和运行状态。",
146
- "extensionsLoading": "正在加载 Extensions…",
147
- "extensionsLoadFailed": "Extensions 加载失败。",
147
+ "extensionsLifecycleDescription": "扩展由运行时按渠道和会话持续关注关系按需启停;这里集中查看全局能力和运行状态。",
148
+ "extensionsLoading": "正在加载扩展…",
149
+ "extensionsLoadFailed": "扩展加载失败。",
148
150
  "extensionsRetry": "重试",
149
- "extensionsEmpty": "暂无已发现的 Extension",
150
- "extensionsEmptyDescription": "安装或放置 Extension 后,重新刷新此页面。",
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": "根因:当前安装形态或更新源配置不支持自动更新;请安装支持自动更新的版本,或由维护者补齐更新源配置。",
@@ -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 only when the page can actively reconnect', () => {
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
  });