@nextclaw/ui 0.26.0 → 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.
Files changed (308) hide show
  1. package/CHANGELOG.md +28 -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
@@ -10,6 +10,7 @@ import type { ChatMessageListProps } from "@nextclaw/agent-chat-ui";
10
10
  import { beforeEach, expect, it, vi } from "vitest";
11
11
  import { ChatMessageListContainer as RuntimeChatMessageListContainer } from "@/features/chat/features/message/components/chat-message-list.container";
12
12
  import { useChatQueryStore } from "@/features/chat/stores/ncp-chat-query.store";
13
+ import type * as I18nModule from "@/shared/lib/i18n";
13
14
 
14
15
  type AgentChatUiModule = {
15
16
  ChatMessageList: ComponentType<ChatMessageListProps>;
@@ -112,10 +113,15 @@ vi.mock("@/app/components/i18n-provider", () => ({
112
113
  useI18n: () => ({ language: captures.language }),
113
114
  }));
114
115
 
115
- vi.mock("@/shared/lib/i18n", () => ({
116
- formatDateTime: (value: string) => `formatted:${value}`,
117
- t: (key: string) => key,
118
- }));
116
+ vi.mock("@/shared/lib/i18n", async (importOriginal) => {
117
+ const actual = await importOriginal<typeof I18nModule>();
118
+ return {
119
+ ...actual,
120
+ formatDateTime: (value: string) => `formatted:${value}`,
121
+ t: (key: string) => key.startsWith("chatProcessDuration") || (captures.language === "zh" && key.startsWith("chatProcessSummary"))
122
+ ? actual.t(key, captures.language === "zh" ? "zh" : "en") : key,
123
+ };
124
+ });
119
125
 
120
126
  vi.mock(
121
127
  "@/features/chat/features/workspace/components/chat-session-workspace-file-preview",
@@ -325,51 +331,30 @@ it("adds a completed assistant process summary without inventing a duration", ()
325
331
  });
326
332
  });
327
333
 
328
- it("derives completed assistant process duration from message lifecycle", () => {
329
- const assistantMessage = {
330
- id: "assistant-process-result",
331
- sessionId: "session-1",
332
- role: "assistant",
333
- status: "final",
334
- timestamp: "2026-03-31T10:01:03.000Z",
335
- lifecycle: {
336
- startedAt: "2026-03-31T10:00:00.000Z",
337
- endedAt: "2026-03-31T10:03:51.000Z",
338
- },
334
+ it("renders a minimal Chinese history summary and still expands its process", () => {
335
+ captures.language = "zh";
336
+ captures.renderActualChatMessageList = true;
337
+ const message: NcpMessage = {
338
+ id: "assistant-minimal-summary", sessionId: "session-1", role: "assistant", status: "final",
339
+ timestamp: "2026-03-31T10:02:07.000Z",
340
+ lifecycle: { startedAt: "2026-03-31T10:00:00.000Z", endedAt: "2026-03-31T10:02:07.000Z" },
341
+ metadata: { nextclawUiHistoryToolPayloadSummary: {
342
+ toolCallCount: 67, toolNames: ["tool_schema", "projects_list", "project_work_list"],
343
+ } },
339
344
  parts: [
340
- {
341
- type: "reasoning",
342
- text: "Inspecting current state.",
343
- },
344
- {
345
- type: "tool-invocation",
346
- toolCallId: "tool-1",
347
- toolName: "exec_command",
348
- state: "result",
349
- args: '{"cmd":"git status"}',
350
- result: "clean",
351
- },
352
- {
353
- type: "text",
354
- text: "Done.",
355
- },
345
+ { type: "reasoning", text: "核对项目详情" },
346
+ { type: "tool-invocation", toolCallId: "tool-1", toolName: "projects_list", state: "result" },
347
+ { type: "text", text: "项目已整理" },
356
348
  ],
357
- } satisfies NcpMessage;
358
-
359
- render(
360
- <ChatMessageListContainer
361
- messages={[assistantMessage]}
362
- isSending={false}
363
- />,
364
- );
365
-
366
- const renderedMessages =
367
- captures.renders[captures.renders.length - 1]?.messages ?? [];
368
- expect(renderedMessages[0]).toMatchObject({
369
- processSummary: {
370
- label: "chatProcessSummaryProcessed 3m 51s",
371
- },
372
- });
349
+ };
350
+ render(<ChatMessageListContainer messages={[message]} isSending={false} />);
351
+ const summary = screen.getByRole("button", { name: "已处理 2分钟7秒" });
352
+ expect(summary.getAttribute("aria-expanded")).toBe("false");
353
+ expect(screen.queryByText(/67|tool_schema|project_work_list/)).toBeNull();
354
+ expect(screen.getByText("项目已整理")).toBeTruthy();
355
+ fireEvent.click(summary);
356
+ expect(summary.getAttribute("aria-expanded")).toBe("true");
357
+ expect(screen.getByRole("button", { name: "projects list" })).toBeTruthy();
373
358
  });
374
359
 
375
360
  it("keeps unavailable ai usage in metadata but out of the footer", () => {
@@ -66,7 +66,7 @@ import {
66
66
  import { useChatMessageActions } from "@/features/chat/features/message/hooks/use-chat-message-actions";
67
67
  import { useChatInlineTokenActions } from "@/features/chat/features/message/hooks/use-chat-inline-token-actions";
68
68
  import { buildServerPathContentUrl } from "@/shared/lib/api";
69
- import { formatDateTime, formatNumber, t } from "@/shared/lib/i18n";
69
+ import { formatDateTime, t, type I18nLanguage } from "@/shared/lib/i18n";
70
70
  import { cn } from "@/shared/lib/utils";
71
71
  import type { SessionMessageToolPayloadState } from "@/features/chat/features/ncp/hooks/use-ncp-session-message-history";
72
72
  import { ChatMessageObservationEvent } from "@/features/chat/features/message/components/chat-message-observation-event";
@@ -96,7 +96,7 @@ class ChatMessageViewModelAdapter {
96
96
  private readonly cache = new WeakMap<
97
97
  NcpMessage,
98
98
  {
99
- language: Parameters<typeof formatDateTime>[1];
99
+ language: I18nLanguage;
100
100
  processSummaryLabel: string | null;
101
101
  executionPresentationKey: string | null;
102
102
  triggerDetailsKey: string | null;
@@ -108,7 +108,7 @@ class ChatMessageViewModelAdapter {
108
108
  continuationRunning: boolean;
109
109
  executionLabels: ReturnType<typeof buildChatMessageExecutionLabels>;
110
110
  triggerLabels: ReturnType<typeof buildChatMessageTriggerLabels>;
111
- language: Parameters<typeof formatDateTime>[1];
111
+ language: I18nLanguage;
112
112
  processedLabel: string;
113
113
  rawMessages: readonly NcpMessage[];
114
114
  texts: ReturnType<typeof buildChatMessageAdapterTexts>;
@@ -120,13 +120,9 @@ class ChatMessageViewModelAdapter {
120
120
  const processSummary = buildChatMessageProcessSummary({
121
121
  message,
122
122
  processedLabel,
123
- formatDeferredToolSummary: (toolCallCount, toolNames) => {
124
- const countLabel = t("chatProcessSummaryToolCalls", language)
125
- .replace("{count}", formatNumber(toolCallCount, language));
126
- if (toolNames.length === 0) return countLabel;
127
- const separator = language === "zh" ? "、" : ", ";
128
- return `${countLabel} · ${toolNames.join(separator)}`;
129
- },
123
+ language,
124
+ failedLabel: t("chatProcessSummaryError", language),
125
+ stoppedLabel: t("chatProcessSummaryStopped", language),
130
126
  });
131
127
  const processSummaryLabel = processSummary?.label ?? null;
132
128
  const executionPresentation = buildChatMessageExecutionPresentation({
@@ -1,4 +1,4 @@
1
- import { NcpEventType, type NcpMessage } from "@nextclaw/ncp";
1
+ import { createUnavailableNcpAiExecutionMetadata, NcpEventType, type NcpMessage } from "@nextclaw/ncp";
2
2
  import { DefaultNcpAgentConversationStateManager } from "@nextclaw/ncp-toolkit";
3
3
  import { describe, expect, it } from "vitest";
4
4
  import { buildChatMessageProcessSummary } from "@/features/chat/features/message/utils/chat-message-process-summary.utils";
@@ -29,6 +29,13 @@ const baseAssistantMessage = {
29
29
  ],
30
30
  } satisfies NcpMessage;
31
31
 
32
+ const summaryLabels = {
33
+ processedLabel: "已处理",
34
+ failedLabel: "处理失败",
35
+ stoppedLabel: "已停止",
36
+ language: "zh" as const,
37
+ };
38
+
32
39
  describe("buildChatMessageProcessSummary", () => {
33
40
  it("shows elapsed time from the real completed-message then run-finished event sequence", async () => {
34
41
  const manager = new DefaultNcpAgentConversationStateManager();
@@ -44,12 +51,13 @@ describe("buildChatMessageProcessSummary", () => {
44
51
  },
45
52
  ]);
46
53
  const message = manager.getSnapshot().messages[0]!;
47
- expect(buildChatMessageProcessSummary({ message, processedLabel: "已处理" })?.label).toBe("已处理 3m 51s");
54
+ expect(buildChatMessageProcessSummary({ ...summaryLabels, message })?.label).toBe("已处理 3分钟51秒");
48
55
  });
49
56
 
50
57
  it("does not invent duration when lifecycle timing is absent", () => {
51
58
  expect(
52
59
  buildChatMessageProcessSummary({
60
+ ...summaryLabels,
53
61
  message: baseAssistantMessage,
54
62
  processedLabel: "Processed",
55
63
  }),
@@ -61,6 +69,8 @@ describe("buildChatMessageProcessSummary", () => {
61
69
  it("derives duration from lifecycle startedAt and endedAt", () => {
62
70
  expect(
63
71
  buildChatMessageProcessSummary({
72
+ ...summaryLabels,
73
+ language: "en",
64
74
  message: {
65
75
  ...baseAssistantMessage,
66
76
  lifecycle: {
@@ -77,6 +87,7 @@ describe("buildChatMessageProcessSummary", () => {
77
87
 
78
88
  it("stays free of tool-activity semantics", () => {
79
89
  const summary = buildChatMessageProcessSummary({
90
+ ...summaryLabels,
80
91
  message: baseAssistantMessage,
81
92
  processedLabel: "Processed",
82
93
  });
@@ -85,8 +96,9 @@ describe("buildChatMessageProcessSummary", () => {
85
96
  expect(summary?.label.toLowerCase()).not.toContain("read");
86
97
  });
87
98
 
88
- it("shows the bounded aggregate for a deferred history payload", () => {
99
+ it("keeps deferred history summaries identical to fully loaded messages", () => {
89
100
  const summary = buildChatMessageProcessSummary({
101
+ ...summaryLabels,
90
102
  message: {
91
103
  ...baseAssistantMessage,
92
104
  metadata: {
@@ -97,12 +109,46 @@ describe("buildChatMessageProcessSummary", () => {
97
109
  },
98
110
  },
99
111
  processedLabel: "Processed",
100
- formatDeferredToolSummary: (count, names) =>
101
- `${count} tool calls · ${names.join(", ")}`,
102
112
  });
103
113
 
104
114
  expect(summary?.label).toBe(
105
- "Processed · 500 tool calls · exec_command, read_file",
115
+ "Processed",
106
116
  );
107
117
  });
118
+
119
+ it.each([
120
+ [0, "0秒"], [7, "7秒"], [127, "2分钟7秒"],
121
+ [3600, "1小时"], [3720, "1小时2分钟"],
122
+ ])("localizes a %i-second duration without separators", (seconds, expected) => {
123
+ const startedAt = "2026-03-31T10:00:00.000Z";
124
+ const message: NcpMessage = {
125
+ ...baseAssistantMessage,
126
+ lifecycle: { startedAt, endedAt: new Date(Date.parse(startedAt) + seconds * 1000).toISOString() },
127
+ };
128
+ expect(buildChatMessageProcessSummary({ ...summaryLabels, message })?.label).toBe(`已处理 ${expected}`);
129
+ });
130
+
131
+ it.each(["pending", "streaming"] as const)("does not label %s messages as processed", (status) => {
132
+ expect(buildChatMessageProcessSummary({
133
+ ...summaryLabels, message: { ...baseAssistantMessage, status },
134
+ })).toBeUndefined();
135
+ });
136
+
137
+ it.each([
138
+ ["failed", "处理失败"], ["aborted", "已停止"], ["completed", "已处理"],
139
+ ] as const)("uses the recorded %s outcome", (outcome, expected) => {
140
+ const message: NcpMessage = {
141
+ ...baseAssistantMessage,
142
+ metadata: { ai_execution: createUnavailableNcpAiExecutionMetadata({
143
+ runId: "run-1", runtimeId: "native", model: "test", requestedModel: null, outcome,
144
+ }) },
145
+ };
146
+ expect(buildChatMessageProcessSummary({ ...summaryLabels, message })?.label).toBe(expected);
147
+ });
148
+
149
+ it("shows failure for an error message without execution metadata", () => {
150
+ expect(buildChatMessageProcessSummary({
151
+ ...summaryLabels, message: { ...baseAssistantMessage, status: "error" },
152
+ })?.label).toBe("处理失败");
153
+ });
108
154
  });
@@ -1,29 +1,15 @@
1
- import type { NcpMessage } from "@nextclaw/ncp";
1
+ import { readNcpAiExecutionMetadata, type NcpMessage } from "@nextclaw/ncp";
2
2
  import type { ChatMessageProcessSummarySource } from "@/features/chat/types/chat-message.types";
3
+ import { t, type I18nLanguage } from "@/shared/lib/i18n";
3
4
 
4
5
  type BuildChatMessageProcessSummaryParams = {
5
6
  message: NcpMessage;
6
7
  processedLabel: string;
7
- formatDeferredToolSummary?: (toolCallCount: number, toolNames: readonly string[]) => string;
8
+ failedLabel: string;
9
+ stoppedLabel: string;
10
+ language: I18nLanguage;
8
11
  };
9
12
 
10
- const UI_HISTORY_TOOL_PAYLOAD_SUMMARY_METADATA_KEY =
11
- "nextclawUiHistoryToolPayloadSummary";
12
-
13
- function readDeferredToolSummary(
14
- message: NcpMessage,
15
- ): { toolCallCount: number; toolNames: string[] } | null {
16
- const value = message.metadata?.[UI_HISTORY_TOOL_PAYLOAD_SUMMARY_METADATA_KEY];
17
- if (!value || typeof value !== "object" || Array.isArray(value)) return null;
18
- const record = value as Record<string, unknown>;
19
- const { toolCallCount } = record;
20
- if (!Number.isInteger(toolCallCount) || (toolCallCount as number) <= 0) return null;
21
- const toolNames = Array.isArray(record.toolNames)
22
- ? record.toolNames.filter((name): name is string => typeof name === "string" && name.trim().length > 0)
23
- : [];
24
- return { toolCallCount: toolCallCount as number, toolNames };
25
- }
26
-
27
13
  function isAssistantProcessPart(part: NcpMessage["parts"][number]): boolean {
28
14
  return part.type === "reasoning" || part.type === "tool-invocation";
29
15
  }
@@ -62,7 +48,7 @@ function hasCollapsibleAssistantProcess(message: NcpMessage): boolean {
62
48
  );
63
49
  }
64
50
 
65
- function formatLifecycleDuration(message: NcpMessage): string | null {
51
+ function formatLifecycleDuration(message: NcpMessage, language: I18nLanguage): string | null {
66
52
  const startedAt = message.lifecycle?.startedAt;
67
53
  const endedAt = message.lifecycle?.endedAt;
68
54
  if (!startedAt || !endedAt) {
@@ -77,13 +63,13 @@ function formatLifecycleDuration(message: NcpMessage): string | null {
77
63
  const hours = Math.floor(totalSeconds / 3600);
78
64
  const minutes = Math.floor((totalSeconds % 3600) / 60);
79
65
  const seconds = totalSeconds % 60;
80
- if (hours > 0) {
81
- return minutes > 0 ? `${hours}h ${minutes}m` : `${hours}h`;
82
- }
83
- if (minutes > 0) {
84
- return `${minutes}m ${seconds}s`;
85
- }
86
- return `${seconds}s`;
66
+ const key = hours > 0
67
+ ? (minutes > 0 ? "chatProcessDurationHoursMinutes" : "chatProcessDurationHours")
68
+ : (minutes > 0 ? "chatProcessDurationMinutesSeconds" : "chatProcessDurationSeconds");
69
+ return t(key, language)
70
+ .replace("{hours}", String(hours))
71
+ .replace("{minutes}", String(minutes))
72
+ .replace("{seconds}", String(seconds));
87
73
  }
88
74
 
89
75
  /**
@@ -92,22 +78,21 @@ function formatLifecycleDuration(message: NcpMessage): string | null {
92
78
  * and must not be mixed into this summary.
93
79
  */
94
80
  export function buildChatMessageProcessSummary({
95
- formatDeferredToolSummary,
81
+ failedLabel,
82
+ stoppedLabel,
96
83
  message,
97
84
  processedLabel,
85
+ language,
98
86
  }: BuildChatMessageProcessSummaryParams): ChatMessageProcessSummarySource | undefined {
99
87
  if (!hasCollapsibleAssistantProcess(message)) {
100
88
  return undefined;
101
89
  }
102
- const duration = formatLifecycleDuration(message);
103
- const baseLabel = duration ? `${processedLabel} ${duration}` : processedLabel;
104
- const deferredToolSummary = readDeferredToolSummary(message);
90
+ const duration = formatLifecycleDuration(message, language);
91
+ const outcome = readNcpAiExecutionMetadata(message.metadata)?.outcome;
92
+ const statusLabel = message.status === "error" || outcome === "failed"
93
+ ? failedLabel
94
+ : outcome === "aborted" ? stoppedLabel : processedLabel;
105
95
  return {
106
- label: deferredToolSummary && formatDeferredToolSummary
107
- ? `${baseLabel} · ${formatDeferredToolSummary(
108
- deferredToolSummary.toolCallCount,
109
- deferredToolSummary.toolNames,
110
- )}`
111
- : baseLabel,
96
+ label: duration ? `${statusLabel} ${duration}` : statusLabel,
112
97
  };
113
98
  }
@@ -113,7 +113,7 @@ describe("ChatSidebarProjectGroups", () => {
113
113
  const projectRow = projectLink.parentElement?.parentElement;
114
114
  expect(container.firstElementChild?.className).toBe("space-y-0.5");
115
115
  expect(projectRow?.className).toContain("h-8");
116
- expect(projectRow?.className).toContain("hover:bg-gray-200/60");
116
+ expect(projectRow?.className).toContain("hover:bg-[hsl(var(--gray-200)/0.6)]");
117
117
  const projectContent = projectLink.parentElement;
118
118
  expect(projectContent?.classList.contains("pr-14")).toBe(false);
119
119
  expect(projectContent?.className).toContain("group-hover/project:pr-20");
@@ -87,7 +87,8 @@ it("shows session actions only on hover or when an action owns focus", () => {
87
87
  it("opens any sidebar session as a floating conversation", async () => {
88
88
  const user = userEvent.setup();
89
89
  await user.click(screen.getByRole('button', { name: 'More actions' }));
90
- await user.click(screen.getByRole('button', { name: 'Open in floating window' }));
90
+ await user.click(screen.getByRole('menuitem', { name: 'Layout and position' }));
91
+ await user.click(screen.getByRole('menuitem', { name: 'Open in floating window' }));
91
92
  expect(useFloatingSessionStore.getState().session).toEqual({
92
93
  sessionKey: 'session:current', title: 'Current Task',
93
94
  });
@@ -1,4 +1,5 @@
1
1
  import { useMemo, useState, type ReactNode } from "react";
2
+ import { SIDEBAR_ITEM_SURFACE_CLASS, SIDEBAR_ITEM_ACTIVE_SURFACE_CLASS } from "@/app/components/layout/sidebar-rail.styles";
2
3
  import { Link, useLocation, useNavigate } from "react-router-dom";
3
4
  import {
4
5
  AlarmClock,
@@ -116,8 +117,9 @@ export function ChatSidebarProjectGroups(props: ChatSidebarProjectGroupsProps) {
116
117
  <div key={group.projectRoot}>
117
118
  <div
118
119
  className={cn(
119
- "group/project relative h-8 rounded-lg px-1 text-muted-foreground transition-colors hover:bg-gray-200/60 hover:text-gray-900",
120
- isSelected && "bg-gray-200/60 text-gray-900",
120
+ "group/project relative h-8 px-1",
121
+ SIDEBAR_ITEM_SURFACE_CLASS,
122
+ isSelected && SIDEBAR_ITEM_ACTIVE_SURFACE_CLASS,
121
123
  )}
122
124
  >
123
125
  <ChatSidebarContextCard
@@ -1,4 +1,5 @@
1
1
  import { AgentAvatar } from "@/shared/components/common/agent-avatar";
2
+ import { SIDEBAR_ITEM_SURFACE_CLASS, SIDEBAR_ITEM_ACTIVE_SURFACE_CLASS } from "@/app/components/layout/sidebar-rail.styles";
2
3
  import { ChatMobileSessionRow } from './chat-mobile-session-row';
3
4
  import { SessionContextIconNode } from "@/features/chat/features/session/components/session-context-icon";
4
5
  import { SessionRunBadge } from "@/features/chat/features/session/components/session-run-badge";
@@ -309,20 +310,22 @@ export function ChatSidebarSessionItem(props: ChatSidebarSessionItemProps) {
309
310
  onCancel,
310
311
  onTogglePinned,
311
312
  onDelete,
313
+ variant,
312
314
  } = props;
313
315
  return (
314
316
  <div
315
317
  className={cn(
316
- props.variant === 'mobile'
318
+ variant === 'mobile'
317
319
  ? 'w-full border-b border-border/50 bg-background text-left'
318
- : "w-full rounded-lg px-2.5 py-2 text-left text-[13px] transition-colors",
319
- active
320
+ : cn("w-full px-3 py-2 text-left text-[13px]", SIDEBAR_ITEM_SURFACE_CLASS, active && SIDEBAR_ITEM_ACTIVE_SURFACE_CLASS),
321
+ variant === 'mobile' && (active
320
322
  ? "bg-background/90 font-medium text-foreground"
321
- : "text-foreground/80 hover:bg-background/65 hover:text-foreground",
323
+ : "text-foreground/80 hover:bg-background/65 hover:text-foreground"),
324
+ active && "font-medium",
322
325
  )}
323
326
  >
324
327
  {isEditing ? (
325
- <div className={props.variant === 'mobile' ? 'p-4' : undefined}>
328
+ <div className={variant === 'mobile' ? 'p-4' : undefined}>
326
329
  <ChatSidebarSessionEditingView
327
330
  sessionKey={sessionKey}
328
331
  draftLabel={draftLabel}
@@ -332,7 +335,7 @@ export function ChatSidebarSessionItem(props: ChatSidebarSessionItemProps) {
332
335
  onCancel={onCancel}
333
336
  />
334
337
  </div>
335
- ) : props.variant === 'mobile' ? <ChatMobileSessionRow {...props} /> : (
338
+ ) : variant === 'mobile' ? <ChatMobileSessionRow {...props} /> : (
336
339
  <ChatSidebarSessionDisplayView
337
340
  sessionKey={sessionKey}
338
341
  active={active}
@@ -192,7 +192,7 @@ describe('ChatSessionHeaderActions', () => {
192
192
  expect(screen.getByRole('button', { name: 'Open session workspace' }).className).toContain('w-7');
193
193
  });
194
194
 
195
- it('places the workspace toggle at the far right of the visible header actions', () => {
195
+ it('places more actions at the far right of the visible header actions', () => {
196
196
  render(
197
197
  <ChatSessionHeaderActions
198
198
  sessionKey="session-actions"
@@ -208,7 +208,7 @@ describe('ChatSessionHeaderActions', () => {
208
208
  const menuButton = screen.getByRole('button', { name: 'More actions' });
209
209
  const workspaceButton = screen.getByRole('button', { name: 'Open session workspace' });
210
210
 
211
- expect(menuButton.compareDocumentPosition(workspaceButton)).toBe(
211
+ expect(workspaceButton.compareDocumentPosition(menuButton)).toBe(
212
212
  Node.DOCUMENT_POSITION_FOLLOWING,
213
213
  );
214
214
  });
@@ -61,6 +61,23 @@ export function ChatSessionHeaderActions({
61
61
 
62
62
  return (
63
63
  <div className={SESSION_HEADER_ACTION_GROUP_CLASS}>
64
+ <IconActionButton
65
+ icon={
66
+ isWorkspaceOpen ? (
67
+ <PanelRightClose className="h-4 w-4" />
68
+ ) : (
69
+ <PanelRightOpen className="h-4 w-4" />
70
+ )
71
+ }
72
+ label={t(
73
+ isWorkspaceOpen
74
+ ? 'chatSessionCloseWorkspace'
75
+ : 'chatSessionOpenWorkspace',
76
+ )}
77
+ aria-pressed={isWorkspaceOpen}
78
+ onClick={onToggleWorkspace}
79
+ disabled={isBusy}
80
+ />
64
81
  {sessionKey ? (
65
82
  <ChatSessionMoreActionsMenu
66
83
  sessionKey={sessionKey}
@@ -88,23 +105,6 @@ export function ChatSessionHeaderActions({
88
105
  />
89
106
  </ChatSessionMoreActionsMenu>
90
107
  ) : null}
91
- <IconActionButton
92
- icon={
93
- isWorkspaceOpen ? (
94
- <PanelRightClose className="h-4 w-4" />
95
- ) : (
96
- <PanelRightOpen className="h-4 w-4" />
97
- )
98
- }
99
- label={t(
100
- isWorkspaceOpen
101
- ? 'chatSessionCloseWorkspace'
102
- : 'chatSessionOpenWorkspace',
103
- )}
104
- aria-pressed={isWorkspaceOpen}
105
- onClick={onToggleWorkspace}
106
- disabled={isBusy}
107
- />
108
108
 
109
109
  {sessionKey ? (
110
110
  <>
@@ -20,15 +20,15 @@ export function ChatSessionHeaderMenuItem({
20
20
  <button
21
21
  type="button"
22
22
  className={cn(
23
- 'flex w-full items-center gap-2 rounded-xl px-3 py-2 text-left text-sm transition-colors disabled:cursor-not-allowed disabled:opacity-50',
23
+ 'flex h-8 w-full items-center gap-2 rounded-lg px-2.5 text-left text-[13px] font-medium outline-none transition-colors disabled:cursor-not-allowed disabled:opacity-50',
24
24
  destructive
25
- ? 'text-destructive hover:bg-destructive/10'
26
- : 'text-muted-foreground hover:bg-[var(--interaction-hover)] hover:text-accent-foreground'
25
+ ? 'text-destructive hover:bg-destructive/10 focus-visible:bg-destructive/10'
26
+ : 'text-foreground hover:bg-muted focus-visible:bg-muted'
27
27
  )}
28
28
  onClick={onClick}
29
29
  disabled={disabled}
30
30
  >
31
- <Icon className="h-4 w-4 shrink-0" />
31
+ <Icon className={cn("h-4 w-4 shrink-0", !destructive && "text-muted-foreground")} />
32
32
  <span>{label}</span>
33
33
  </button>
34
34
  );
@@ -1,7 +1,7 @@
1
1
  import { PageResourceActionItems } from '@/features/right-panel-resources';
2
2
  import { pageResourceFromTarget } from '@/features/right-panel-resources';
3
3
  import { buildSessionPanelUrl } from '@/features/chat/features/session/utils/chat-session-route.utils';
4
- import { useState, type ReactNode } from 'react';
4
+ import { Children, isValidElement, useState, type ReactNode } from 'react';
5
5
  import { copyText } from '@nextclaw/agent-chat-ui';
6
6
  import { Copy, MoreVertical } from 'lucide-react';
7
7
  import { toast } from 'sonner';
@@ -42,6 +42,9 @@ export function ChatSessionMoreActionsMenu({
42
42
  className,
43
43
  }: ChatSessionMoreActionsMenuProps) {
44
44
  const [isOpen, setIsOpen] = useState(false);
45
+ const menuItems = Children.toArray(children);
46
+ const isDestructive = (item: ReactNode) => isValidElement<{ destructive?: boolean }>(item) && item.props.destructive;
47
+ const destructiveItems = menuItems.filter(isDestructive);
45
48
 
46
49
  const handleCopySessionId = () => {
47
50
  setIsOpen(false);
@@ -54,7 +57,6 @@ export function ChatSessionMoreActionsMenu({
54
57
  <IconActionButton
55
58
  icon={<MoreVertical className="h-4 w-4" />}
56
59
  label={t('chatSessionMoreActions')}
57
- tooltip={false}
58
60
  size={triggerSize}
59
61
  tone={triggerTone}
60
62
  className={className}
@@ -62,17 +64,22 @@ export function ChatSessionMoreActionsMenu({
62
64
  onClick={(event) => event.stopPropagation()}
63
65
  />
64
66
  </PopoverTrigger>
65
- <ChatPopoverContent align="end" className="w-56 p-2">
66
- <div className="space-y-1" onClick={() => setIsOpen(false)}>
67
- <PageResourceActionItems page={pageResourceFromTarget({ kind: 'chat-session', title: sessionTitle || t('chatFloatingConversation'), url: buildSessionPanelUrl(sessionKey), resourceUri: buildSessionPanelUrl(sessionKey), historyPolicy: 'none' })} onSelect={() => setIsOpen(false)} />
67
+ <ChatPopoverContent align="end" variant="menu">
68
+ <div onClick={() => setIsOpen(false)}>
69
+ {menuItems.filter((item) => !isDestructive(item))}
68
70
  <ChatSessionHeaderMenuItem
69
71
  icon={Copy}
70
72
  label={t('chatSessionCopyId')}
71
73
  onClick={handleCopySessionId}
72
74
  disabled={disabled}
73
75
  />
74
- {children}
75
76
  </div>
77
+ <div className="my-1 h-px bg-border" />
78
+ <PageResourceActionItems page={pageResourceFromTarget({ kind: 'chat-session', title: sessionTitle || t('chatFloatingConversation'), url: buildSessionPanelUrl(sessionKey), resourceUri: buildSessionPanelUrl(sessionKey), historyPolicy: 'none' })} onSelect={() => setIsOpen(false)} />
79
+ {destructiveItems.length > 0 ? <>
80
+ <div className="my-1 h-px bg-border" />
81
+ <div onClick={() => setIsOpen(false)}>{destructiveItems}</div>
82
+ </> : null}
76
83
  </ChatPopoverContent>
77
84
  </Popover>
78
85
  );
@@ -205,7 +205,7 @@ export function ChatSessionWorkspacePanel({
205
205
  const file = workspaceFileTabs.find((item) => `file:${item.key}` === tab.key);
206
206
  const target = file ? createWorkspaceFilePanelTarget(file, { workingDir: sessionWorkingDir, projectRoot: sessionProjectRoot })
207
207
  : tab.sessionKey ? { kind: 'chat-session', title: tab.title, url: buildSessionPanelUrl(tab.sessionKey), resourceUri: buildSessionPanelUrl(tab.sessionKey), historyPolicy: 'none' as const } : tab.kind !== 'side-chat-draft' ? { kind: 'workspace', title: tab.title, url: `nextclaw://workspace?${new URLSearchParams({ session: sessionKey ?? '', page: tab.kind })}`, historyPolicy: 'none' as const } : null;
208
- return target ? [...pageActions(pageResourceFromTarget(target), 'workspace'), ...(tab.menuGroups ?? [])] : tab.menuGroups;
208
+ return target ? [...(tab.menuGroups ?? []), ...pageActions(pageResourceFromTarget(target), 'workspace')] : tab.menuGroups;
209
209
  })(),
210
210
  onMoveGlobal: tab.kind === 'file' ? () => presenter.chatThreadManager.moveFileToGlobal(tab.key.slice('file:'.length), { workingDir: sessionWorkingDir, projectRoot: sessionProjectRoot }, getAppPresenter().docBrowserManager)
211
211
  : tab.kind === 'child-session' && tab.sessionKey ? () => {
@@ -1,8 +1,8 @@
1
1
  import type { ChatComposerDictationSession } from '@nextclaw/agent-chat-ui';
2
- import { checkMicrophoneAccess, classifyMicrophoneError } from '@/features/chat/utils/chat-voice-permissions.utils';
2
+ import { checkMicrophoneAccess, classifyMicrophoneError, isMicrophoneSecureContext } from '@/features/chat/utils/chat-voice-permissions.utils';
3
3
 
4
4
  export type VoicePhase = 'idle' | 'ready' | 'starting' | 'recording' | 'stopping' | 'error';
5
- export type VoiceError = 'unsupported' | 'permission' | 'service-denied' | 'no-device' | 'device-busy' | 'network' | 'audio-capture' | 'no-speech' | 'interrupted' | 'timeout' | 'failed';
5
+ export type VoiceError = 'insecure-context' | 'unsupported' | 'permission' | 'service-denied' | 'no-device' | 'device-busy' | 'network' | 'audio-capture' | 'no-speech' | 'interrupted' | 'timeout' | 'failed';
6
6
  export type VoiceSnapshot = { phase: VoicePhase; text: string; interim: string; seconds: number; error: VoiceError | null };
7
7
  export type SpeechRecognitionLike = {
8
8
  lang: string;
@@ -38,7 +38,8 @@ export class ChatVoiceInputManager {
38
38
  private draft: ChatComposerDictationSession | null = null;
39
39
 
40
40
  constructor(private readonly createRecognition = createBrowserRecognition,
41
- private readonly checkMicrophone = checkMicrophoneAccess) {}
41
+ private readonly checkMicrophone = checkMicrophoneAccess,
42
+ private readonly isSecureContext = isMicrophoneSecureContext) {}
42
43
 
43
44
  getSnapshot = (): VoiceSnapshot => this.snapshot;
44
45
  subscribe = (listener: () => void): (() => void) => {
@@ -70,6 +71,7 @@ export class ChatVoiceInputManager {
70
71
  this.draft = draft;
71
72
  this.update({ ...initialSnapshot(), phase: 'starting' });
72
73
  try {
74
+ if (!this.isSecureContext()) { this.fail('insecure-context'); return; }
73
75
  const recognition = this.createRecognition();
74
76
  if (!recognition) { this.fail('unsupported'); return; }
75
77
  this.recognition = recognition;