@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
@@ -1,5 +1,6 @@
1
1
  import { afterEach, expect, it, vi } from 'vitest';
2
2
  import { checkMicrophoneAccess, classifyMicrophoneError, voicePermissionInstructionKeys } from './chat-voice-permissions.utils';
3
+ import { setLanguage, t } from '@/shared/lib/i18n';
3
4
 
4
5
  afterEach(() => vi.unstubAllGlobals());
5
6
 
@@ -20,8 +21,11 @@ it('rechecks access and immediately releases every captured track', async () =>
20
21
  expect(stop).toHaveBeenCalledTimes(2);
21
22
  });
22
23
 
23
- it('reports unavailable media API and an actual denial without caching', async () => {
24
+ it('distinguishes insecure HTTP from an unsupported media API and an actual denial', async () => {
25
+ vi.stubGlobal('isSecureContext', false);
24
26
  vi.stubGlobal('navigator', {});
27
+ expect(await checkMicrophoneAccess()).toBe('insecure-context');
28
+ vi.stubGlobal('isSecureContext', true);
25
29
  expect(await checkMicrophoneAccess()).toBe('unsupported');
26
30
  vi.stubGlobal('navigator', { mediaDevices: { getUserMedia: vi.fn().mockRejectedValue(new DOMException('', 'NotAllowedError')) } });
27
31
  expect(await checkMicrophoneAccess()).toBe('permission');
@@ -34,3 +38,10 @@ it.each([
34
38
  expect(voicePermissionInstructionKeys(ua)).toEqual(['chatInputVoicePermissionBrowser', `chatInputVoicePermission${platform}`]);
35
39
  expect(voicePermissionInstructionKeys(`${ua} Electron`)[0]).toBe('chatInputVoicePermissionDesktop');
36
40
  });
41
+
42
+ it('explains the VPS HTTPS recovery path in both supported languages', () => {
43
+ setLanguage('zh');
44
+ expect(t('chatInputVoiceInsecureContext')).toContain('VPS 配置 HTTPS');
45
+ setLanguage('en');
46
+ expect(t('chatInputVoiceInsecureContext')).toContain('Configure HTTPS for a VPS');
47
+ });
@@ -1,4 +1,8 @@
1
- export type MicrophoneError = 'permission' | 'no-device' | 'device-busy' | 'unsupported' | 'failed';
1
+ export type MicrophoneError = 'insecure-context' | 'permission' | 'no-device' | 'device-busy' | 'unsupported' | 'failed';
2
+
3
+ export function isMicrophoneSecureContext(): boolean {
4
+ return globalThis.isSecureContext !== false;
5
+ }
2
6
 
3
7
  export function classifyMicrophoneError(error: unknown): MicrophoneError {
4
8
  const name = error instanceof DOMException || error instanceof Error ? error.name : '';
@@ -11,6 +15,7 @@ export function classifyMicrophoneError(error: unknown): MicrophoneError {
11
15
 
12
16
  /** Check on explicit start/retry; release every track even if the panel was closed. */
13
17
  export async function checkMicrophoneAccess(): Promise<MicrophoneError | null> {
18
+ if (!isMicrophoneSecureContext()) return 'insecure-context';
14
19
  if (!navigator.mediaDevices?.getUserMedia) return 'unsupported';
15
20
  try {
16
21
  const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
@@ -159,8 +159,14 @@ export function CronJobRow({
159
159
  </button>
160
160
 
161
161
  <TooltipProvider delayDuration={250}>
162
- <div className="relative flex shrink-0 items-center">
163
- <div className="absolute right-11 top-1/2 flex -translate-y-1/2 items-center gap-0.5 opacity-100 transition-opacity duration-150 [@media(hover:hover)]:pointer-events-none [@media(hover:hover)]:opacity-0 [@media(hover:hover)]:group-focus-within:pointer-events-auto [@media(hover:hover)]:group-focus-within:opacity-100 [@media(hover:hover)]:group-hover:pointer-events-auto [@media(hover:hover)]:group-hover:opacity-100">
162
+ <div className="relative flex shrink-0 items-center gap-1">
163
+ <Switch
164
+ checked={job.enabled}
165
+ onCheckedChange={(checked) => onToggle(job, checked)}
166
+ aria-label={job.enabled ? t("cronDisable") : t("cronEnable")}
167
+ />
168
+
169
+ <div className="flex items-center gap-0.5 opacity-100 transition-opacity duration-150 [@media(hover:hover)]:pointer-events-none [@media(hover:hover)]:opacity-0 [@media(hover:hover)]:group-focus-within:pointer-events-auto [@media(hover:hover)]:group-focus-within:opacity-100 [@media(hover:hover)]:group-hover:pointer-events-auto [@media(hover:hover)]:group-hover:opacity-100">
164
170
  <Tooltip>
165
171
  <TooltipTrigger asChild>
166
172
  <Button
@@ -192,8 +198,7 @@ export function CronJobRow({
192
198
  </TooltipTrigger>
193
199
  <TooltipContent>{t("cronMoreActions")}</TooltipContent>
194
200
  </Tooltip>
195
- <PopoverContent align="end" sideOffset={4} className="w-56 p-1">
196
- <PageResourceActionItems page={pageResourceFromSystemObject("cron-job", job.id, jobLabel)} onSelect={() => setMenuOpen(false)} />
201
+ <PopoverContent align="end" sideOffset={4} variant="menu">
197
202
  <div className="my-1 h-px bg-border" />
198
203
  <button
199
204
  type="button"
@@ -217,14 +222,11 @@ export function CronJobRow({
217
222
  <Trash2 className="h-3.5 w-3.5" />
218
223
  {t("delete")}
219
224
  </button>
225
+ <div className="my-1 h-px bg-border" />
226
+ <PageResourceActionItems page={pageResourceFromSystemObject("cron-job", job.id, jobLabel)} onSelect={() => setMenuOpen(false)} />
220
227
  </PopoverContent>
221
228
  </Popover>
222
229
  </div>
223
- <Switch
224
- checked={job.enabled}
225
- onCheckedChange={(checked) => onToggle(job, checked)}
226
- aria-label={job.enabled ? t("cronDisable") : t("cronEnable")}
227
- />
228
230
  </div>
229
231
  </TooltipProvider>
230
232
  </div>
@@ -153,8 +153,10 @@ export function InboxReaderDialog() {
153
153
  {formatDateTime(activeDelivery.createdAt)}
154
154
  </time>
155
155
  </div>
156
- {activeDelivery && <PageResourceActionsMenu page={pageResourceFromSystemObject("inbox-delivery", activeDelivery.id, activeDelivery.title)} />}
157
- {positionControls}
156
+ <div className="flex shrink-0 items-center gap-1">
157
+ {positionControls}
158
+ <PageResourceActionsMenu page={pageResourceFromSystemObject("inbox-delivery", activeDelivery.id, activeDelivery.title)} />
159
+ </div>
158
160
  </div>
159
161
  <DialogDescription className="sr-only">
160
162
  {activeDelivery.summary ?? t("inboxNoSummary")}
@@ -115,14 +115,17 @@ function InboxListPane({
115
115
  {filteredDeliveries.length > 0 ? (
116
116
  <ul className="space-y-0.5">
117
117
  {filteredDeliveries.map((delivery) => (
118
- <li key={delivery.id}>
118
+ <li key={delivery.id} className={cn(
119
+ "group/page-row relative rounded-lg hover:bg-[var(--interaction-hover)] focus-within:bg-[var(--interaction-hover)] has-[[data-context-menu-open]]:bg-[var(--interaction-hover)]",
120
+ activeDeliveryId === delivery.id && "bg-[var(--interaction-selection)]",
121
+ )}>
119
122
  <Link
120
123
  to={`/inbox/${encodeURIComponent(delivery.id)}`}
121
124
  className={cn(
122
- "block rounded-lg px-2.5 py-2 text-[13px] transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-border",
125
+ "block rounded-lg px-2.5 py-2 pr-10 text-[13px] transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-border",
123
126
  activeDeliveryId === delivery.id
124
- ? "bg-[var(--interaction-selection)] text-foreground"
125
- : "text-foreground/80 hover:bg-[var(--interaction-hover)] hover:text-foreground",
127
+ ? "text-foreground"
128
+ : "text-foreground/80 hover:text-foreground",
126
129
  )}
127
130
  >
128
131
  <h2 className={cn(
@@ -131,7 +134,6 @@ function InboxListPane({
131
134
  )}>
132
135
  {delivery.title}
133
136
  </h2>
134
- <PageResourceActionsMenu page={pageResourceFromSystemObject("inbox-delivery", delivery.id, delivery.title)} />
135
137
  <div className="mt-0.5 flex items-center gap-2 text-[11px] leading-4 text-muted-foreground/65">
136
138
  <p className="min-w-0 flex-1 truncate">
137
139
  {delivery.summary ?? t("inboxNoSummary")}
@@ -148,6 +150,9 @@ function InboxListPane({
148
150
  )}
149
151
  </div>
150
152
  </Link>
153
+ <span className="absolute right-1 top-1.5">
154
+ <PageResourceActionsMenu revealOnHover page={pageResourceFromSystemObject("inbox-delivery", delivery.id, delivery.title)} />
155
+ </span>
151
156
  </li>
152
157
  ))}
153
158
  </ul>
@@ -60,7 +60,6 @@ export function McpMarketplaceCard(props: {
60
60
  </div>
61
61
 
62
62
  <div className="flex shrink-0 flex-col gap-2">
63
- {record?.id && <PageResourceActionsMenu page={pageResourceFromSystemObject("mcp-server", record.id, name)} />}
64
63
  {!installed && item && onInstall ? (
65
64
  <Button
66
65
  type="button"
@@ -124,6 +123,7 @@ export function McpMarketplaceCard(props: {
124
123
  ) : null}
125
124
  </>
126
125
  ) : null}
126
+ {record?.id && <PageResourceActionsMenu page={pageResourceFromSystemObject("mcp-server", record.id, name)} />}
127
127
  </div>
128
128
  </div>
129
129
  </article>
@@ -98,9 +98,10 @@ describe('PanelAppListItem', () => {
98
98
  />,
99
99
  );
100
100
 
101
- expect(screen.queryByRole('button', { name: 'Pin to left sidebar' })).toBeNull();
101
+ expect(screen.queryByRole('menuitem', { name: 'Pin to left sidebar' })).toBeNull();
102
102
  await user.click(screen.getByRole('button', { name: 'More panel app actions' }));
103
- await user.click(screen.getByRole('button', { name: 'Pin to left sidebar' }));
103
+ await user.click(screen.getByRole('menuitem', { name: 'Layout and position' }));
104
+ await user.click(screen.getByRole('menuitem', { name: 'Pin to left sidebar' }));
104
105
  expect(mainSidebarMutation.mutate).toHaveBeenCalledWith({
105
106
  id: 'demo',
106
107
  preferences: { mainSidebar: true },
@@ -120,9 +121,10 @@ describe('PanelAppListItem', () => {
120
121
  onToggleFavorite={vi.fn()}
121
122
  />,
122
123
  );
123
- expect(screen.queryByRole('button', { name: 'Unpin from left sidebar' })).toBeNull();
124
+ expect(screen.queryByRole('menuitem', { name: 'Unpin from left sidebar' })).toBeNull();
124
125
  await user.click(screen.getByRole('button', { name: 'More panel app actions' }));
125
- await user.click(screen.getByRole('button', { name: 'Unpin from left sidebar' }));
126
+ await user.click(screen.getByRole('menuitem', { name: 'Layout and position' }));
127
+ await user.click(screen.getByRole('menuitem', { name: 'Unpin from left sidebar' }));
126
128
  expect(mainSidebarMutation.mutate).toHaveBeenLastCalledWith({
127
129
  id: 'demo',
128
130
  preferences: { mainSidebar: false },
@@ -51,9 +51,10 @@ describe('PanelAppToolbar', () => {
51
51
  expect(screen.getByText('墨爪助手')).toBeTruthy();
52
52
  expect(screen.queryByRole('button', { name: 'Apps' })).toBeNull();
53
53
 
54
- expect(screen.queryByRole('button', { name: 'Pin to left sidebar' })).toBeNull();
54
+ expect(screen.queryByRole('menuitem', { name: 'Pin to left sidebar' })).toBeNull();
55
55
  await user.click(screen.getByRole('button', { name: 'More panel app actions' }));
56
- const addButton = screen.getByRole('button', { name: 'Pin to left sidebar' });
56
+ await user.click(screen.getByRole('menuitem', { name: 'Layout and position' }));
57
+ const addButton = screen.getByRole('menuitem', { name: 'Pin to left sidebar' });
57
58
  expect(addButton.getAttribute('aria-pressed')).toBe('false');
58
59
  await user.click(addButton);
59
60
 
@@ -61,7 +62,7 @@ describe('PanelAppToolbar', () => {
61
62
  id: 'ink-assistant',
62
63
  preferences: { mainSidebar: true },
63
64
  });
64
- expect(screen.queryByRole('button', { name: 'Pin to left sidebar' })).toBeNull();
65
+ expect(screen.queryByRole('menuitem', { name: 'Pin to left sidebar' })).toBeNull();
65
66
  });
66
67
 
67
68
  it('uses the same menu item to remove an app already in the main sidebar', async () => {
@@ -76,7 +77,8 @@ describe('PanelAppToolbar', () => {
76
77
  );
77
78
 
78
79
  await user.click(screen.getByRole('button', { name: 'More panel app actions' }));
79
- const removeButton = screen.getByRole('button', { name: 'Unpin from left sidebar' });
80
+ await user.click(screen.getByRole('menuitem', { name: 'Layout and position' }));
81
+ const removeButton = screen.getByRole('menuitem', { name: 'Unpin from left sidebar' });
80
82
  expect(removeButton.getAttribute('aria-pressed')).toBe('true');
81
83
  await user.click(removeButton);
82
84
 
@@ -76,7 +76,7 @@ export function PanelAppListItem({
76
76
  <MoreVertical className="h-4 w-4" />
77
77
  </button>
78
78
  </PopoverTrigger>
79
- <PopoverContent align="end" className="w-48 rounded-xl p-1.5">
79
+ <PopoverContent align="end" variant="menu">
80
80
  <PanelAppOpenStandaloneMenuItem
81
81
  entry={entry}
82
82
  onSelect={() => setIsMenuOpen(false)}
@@ -30,6 +30,12 @@ export function PanelAppToolbar({
30
30
  >
31
31
  {appTitle}
32
32
  </span>
33
+ <IconActionButton
34
+ icon={<RefreshCw className="h-3.5 w-3.5" />}
35
+ label={t('panelAppsRefreshCurrent')}
36
+ onClick={onRefresh}
37
+ size="sm"
38
+ />
33
39
  {entry ? (
34
40
  <Popover open={isMenuOpen} onOpenChange={setIsMenuOpen}>
35
41
  <PopoverTrigger asChild>
@@ -39,7 +45,7 @@ export function PanelAppToolbar({
39
45
  size="sm"
40
46
  />
41
47
  </PopoverTrigger>
42
- <PopoverContent align="end" className="w-48 rounded-xl p-1.5">
48
+ <PopoverContent align="end" variant="menu">
43
49
  <PanelAppOpenStandaloneMenuItem
44
50
  entry={entry}
45
51
  onSelect={() => setIsMenuOpen(false)}
@@ -51,12 +57,6 @@ export function PanelAppToolbar({
51
57
  </PopoverContent>
52
58
  </Popover>
53
59
  ) : null}
54
- <IconActionButton
55
- icon={<RefreshCw className="h-3.5 w-3.5" />}
56
- label={t('panelAppsRefreshCurrent')}
57
- onClick={onRefresh}
58
- size="sm"
59
- />
60
60
  </div>
61
61
  );
62
62
  }
@@ -35,10 +35,10 @@ export function ProjectSkills({
35
35
  {skills.length ? (
36
36
  <div className="grid min-w-0 gap-3 md:grid-cols-2">
37
37
  {skills.map((skill) => (
38
- <div key={skill.ref} className="relative group/page-row">
38
+ <div key={skill.ref} className="relative group/page-row rounded-xl border border-border/60 transition-colors hover:border-border hover:bg-[var(--interaction-hover)] focus-within:bg-[var(--interaction-hover)] has-[[data-context-menu-open]]:bg-[var(--interaction-hover)]">
39
39
  <button
40
40
  type="button"
41
- className="w-full min-w-0 rounded-xl border border-border/60 p-3 text-left transition-colors hover:border-border hover:bg-muted/30 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-border"
41
+ className="w-full min-w-0 rounded-xl p-3 text-left focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-border"
42
42
  onClick={() => onOpen(skill)}
43
43
  >
44
44
  <h3 className="pr-7 text-sm font-medium">{skill.name}</h3>
@@ -8,6 +8,7 @@ import { PageResourceSidebarNav } from "@/features/right-panel-resources/compone
8
8
  import { usePageNavigationStore } from "@/features/right-panel-resources/stores/page-navigation.store";
9
9
  import { AppPresenterProvider } from "@/app/components/app-presenter-provider";
10
10
  import type { PanelAppEntryView } from "@/shared/lib/api";
11
+ import { SidebarNavLinkItem } from "@/app/components/layout/sidebar-items";
11
12
 
12
13
  const mocks = vi.hoisted(() => ({
13
14
  entries: [] as PanelAppEntryView[],
@@ -123,10 +124,10 @@ describe("shared left pages", () => {
123
124
  expect(link.contains(menu)).toBe(false);
124
125
  expect(menu.querySelector(".lucide-ellipsis-vertical")).not.toBeNull();
125
126
  expect(menu.parentElement?.className).toContain(
126
- "group-hover/page-row:opacity-100",
127
+ "group-hover/sidebar-item:opacity-100",
127
128
  );
128
129
  expect(menu.parentElement?.className).toContain(
129
- "group-focus-within/page-row:opacity-100",
130
+ "group-focus-within/sidebar-item:opacity-100",
130
131
  );
131
132
  expect(menu.parentElement?.className).toContain(
132
133
  "data-[context-menu-open]:opacity-100",
@@ -136,6 +137,8 @@ describe("shared left pages", () => {
136
137
  expect(menu.parentElement?.hasAttribute("data-context-menu-open")).toBe(
137
138
  true,
138
139
  );
140
+ expect(screen.queryByRole("menuitem", { name: "Unpin from left sidebar" })).toBeNull();
141
+ await userEvent.click(screen.getByRole("menuitem", { name: "Layout and position" }));
139
142
  await userEvent.click(
140
143
  screen.getByRole("menuitem", { name: "Unpin from left sidebar" }),
141
144
  );
@@ -155,4 +158,26 @@ describe("shared left pages", () => {
155
158
  it("omits an empty group", () => {
156
159
  expect(renderNav().container.firstChild).toBeNull();
157
160
  });
161
+ it("shares one whole-row surface with primary navigation and keeps actions inside that surface", () => {
162
+ mocks.entries = [createEntry({ mainSidebar: true })];
163
+ render(
164
+ <MemoryRouter><AppPresenterProvider>
165
+ <SidebarNavLinkItem to="/inbox" label="Inbox" density="compact" />
166
+ <PageResourceSidebarNav isCollapsed={false} />
167
+ </AppPresenterProvider></MemoryRouter>,
168
+ );
169
+ const primary = screen.getByRole("link", { name: "Inbox" });
170
+ const page = screen.getByRole("link", { name: "Demo" });
171
+ expect(page.parentElement?.className).toBe(primary.parentElement?.className);
172
+ expect(page.parentElement?.contains(screen.getByRole("button", { name: "Page actions" }))).toBe(true);
173
+ expect(page.className).toContain("pr-9");
174
+ });
175
+ it("distinguishes resources sharing a pathname by their query identity", () => {
176
+ render(<MemoryRouter initialEntries={["/resource?uri=one"]}>
177
+ <SidebarNavLinkItem to="/resource?uri=one" label="One" />
178
+ <SidebarNavLinkItem to="/resource?uri=two" label="Two" />
179
+ </MemoryRouter>);
180
+ expect(screen.getByRole("link", { name: "One" }).getAttribute("aria-current")).toBe("page");
181
+ expect(screen.getByRole("link", { name: "Two" }).hasAttribute("aria-current")).toBe(false);
182
+ });
158
183
  });
@@ -2,6 +2,7 @@ import { MoreVertical } from "lucide-react";
2
2
  import {
3
3
  ContextMenu,
4
4
  ContextMenuTrigger,
5
+ ContextMenuItems,
5
6
  } from "@/shared/components/ui/context-menu/context-menu";
6
7
  import { IconActionButton } from "@/shared/components/ui/actions/icon-action-button";
7
8
  import { usePageResourceActions } from "@/features/right-panel-resources/hooks/use-page-resource-actions";
@@ -21,9 +22,9 @@ export function PageResourceActionsMenu({
21
22
  <ContextMenu groups={actions(page)} label={t("pageActions")}>
22
23
  <span
23
24
  className={cn(
24
- "inline-flex",
25
+ "inline-flex shrink-0",
25
26
  revealOnHover &&
26
- "pointer-events-none opacity-0 transition-opacity group-hover/page-row:pointer-events-auto group-hover/page-row:opacity-100 group-focus-within/page-row:pointer-events-auto group-focus-within/page-row:opacity-100 data-[context-menu-open]:pointer-events-auto data-[context-menu-open]:opacity-100 [@media(hover:none)]:pointer-events-auto [@media(hover:none)]:opacity-100",
27
+ "pointer-events-none opacity-0 transition-opacity group-hover/sidebar-item:pointer-events-auto group-hover/sidebar-item:opacity-100 group-focus-within/sidebar-item:pointer-events-auto group-focus-within/sidebar-item:opacity-100 data-[context-menu-open]:pointer-events-auto data-[context-menu-open]:opacity-100 [@media(hover:none)]:pointer-events-auto [@media(hover:none)]:opacity-100",
27
28
  )}
28
29
  >
29
30
  <ContextMenuTrigger>
@@ -47,25 +48,6 @@ export function PageResourceActionItems({
47
48
  }) {
48
49
  const actions = usePageResourceActions();
49
50
  return (
50
- <>
51
- {actions(page)
52
- .flatMap((group) => group.items)
53
- .map((item) => (
54
- <button
55
- key={item.key}
56
- type="button"
57
- disabled={item.disabled}
58
- aria-pressed={item.pressed}
59
- className="flex w-full items-center gap-2 rounded-lg px-2.5 py-2 text-left text-sm text-muted-foreground hover:bg-muted hover:text-foreground disabled:opacity-50"
60
- onClick={() => {
61
- onSelect?.();
62
- item.onSelect?.();
63
- }}
64
- >
65
- {item.icon}
66
- {item.label}
67
- </button>
68
- ))}
69
- </>
51
+ <ContextMenuItems groups={actions(page)} onClose={() => onSelect?.()} />
70
52
  );
71
53
  }
@@ -1,6 +1,7 @@
1
1
  import { useState } from "react";
2
2
  import { ChevronDown, PanelsTopLeft } from "lucide-react";
3
- import { NavLink } from "react-router-dom";
3
+ import { SidebarNavLinkItem } from "@/app/components/layout/sidebar-items";
4
+ import { SIDEBAR_RAIL_CONTROL_CLASS, SIDEBAR_RAIL_SURFACE_CLASS } from "@/app/components/layout/sidebar-rail.styles";
4
5
  import { usePanelApps } from "@/features/panel-apps";
5
6
  import { createPanelAppRightPanelResourceTarget } from "@/features/right-panel-resources/utils/right-panel-resource-uri.utils";
6
7
  import {
@@ -34,22 +35,18 @@ function PageResourceSidebarItems({
34
35
  {pages.map((page) => (
35
36
  <li
36
37
  key={page.uri}
37
- className="group/page-row flex items-center gap-1 rounded-lg hover:bg-muted/60"
38
38
  >
39
- <NavLink
39
+ <SidebarNavLinkItem
40
40
  to={pageResourceMainPath(page)}
41
- onClick={() => {
41
+ label={page.title}
42
+ density="compact"
43
+ iconNode={<PageResourceIcon uri={page.uri} icon={page.target.dockIcon} />}
44
+ actions={<PageResourceActionsMenu page={page} revealOnHover />}
45
+ onNavigate={() => {
42
46
  app.pageResourceManager.remember(page);
43
47
  onNavigate?.();
44
48
  }}
45
- className={({ isActive }) =>
46
- `flex min-w-0 flex-1 items-center gap-2 rounded-lg px-2.5 py-1.5 text-[13px] ${isActive ? "bg-primary/10 font-medium text-primary" : "text-muted-foreground hover:text-foreground"}`
47
- }
48
- >
49
- <PageResourceIcon uri={page.uri} icon={page.target.dockIcon} />
50
- <span className="truncate">{page.title}</span>
51
- </NavLink>
52
- <PageResourceActionsMenu page={page} revealOnHover />
49
+ />
53
50
  </li>
54
51
  ))}
55
52
  </ul>
@@ -85,7 +82,7 @@ export function PageResourceSidebarNav({
85
82
  <button
86
83
  type="button"
87
84
  aria-label={t("pageSidebar")}
88
- className="mx-auto my-1 flex h-9 w-9 items-center justify-center rounded-lg hover:bg-muted"
85
+ className={`mx-auto my-1 flex items-center justify-center ${SIDEBAR_RAIL_CONTROL_CLASS} ${SIDEBAR_RAIL_SURFACE_CLASS}`}
89
86
  >
90
87
  <PanelsTopLeft className="h-4 w-4" />
91
88
  </button>
@@ -14,9 +14,21 @@ import {
14
14
  } from "@/features/right-panel-resources/utils/right-panel-resource-uri.utils";
15
15
  import { pageResourceTab } from "@/features/right-panel-resources/managers/page-resource.manager";
16
16
  import type { PageResource } from "@/features/right-panel-resources/types/page-resource.types";
17
- import type { ContextMenuGroup } from "@/shared/components/ui/context-menu/context-menu";
17
+ import type {
18
+ ContextMenuGroup,
19
+ ContextMenuItem,
20
+ } from "@/shared/components/ui/context-menu/context-menu";
18
21
  import { t } from "@/shared/lib/i18n";
19
22
  import { toast } from "sonner";
23
+ import { createElement } from "react";
24
+ import {
25
+ LayoutPanelLeft,
26
+ Link,
27
+ MessageSquarePlus,
28
+ PanelLeft,
29
+ PanelsTopLeft,
30
+ Pin,
31
+ } from "lucide-react";
20
32
 
21
33
  export function usePageResourceActions() {
22
34
  const app = useAppPresenter();
@@ -47,7 +59,12 @@ export function usePageResourceActions() {
47
59
  const items = [
48
60
  {
49
61
  key: "open-main",
50
- label: t(page.target.kind === "workspace" ? "workbenchMaximize" : "workbenchOpenMain"),
62
+ icon: createElement(PanelsTopLeft, { className: "h-4 w-4" }),
63
+ label: t(
64
+ page.target.kind === "workspace"
65
+ ? "workbenchMaximize"
66
+ : "workbenchOpenMain",
67
+ ),
51
68
  onSelect: () => open("main"),
52
69
  },
53
70
  ...(page.target.kind === "route"
@@ -55,17 +72,20 @@ export function usePageResourceActions() {
55
72
  : [
56
73
  {
57
74
  key: "open-sidebar",
75
+ icon: createElement(PanelLeft, { className: "h-4 w-4" }),
58
76
  label: t("workbenchDock"),
59
77
  onSelect: () => open("sidebar"),
60
78
  },
61
79
  {
62
80
  key: "open-floating",
81
+ icon: createElement(PanelsTopLeft, { className: "h-4 w-4" }),
63
82
  label: t("workbenchFloatView"),
64
83
  onSelect: () => open("floating"),
65
84
  },
66
85
  ]),
67
86
  {
68
87
  key: "pin-left",
88
+ icon: createElement(Pin, { className: "h-4 w-4" }),
69
89
  pressed: pinned,
70
90
  label: t(pinned ? "pageUnpinLeft" : "pagePinLeft"),
71
91
  disabled: Boolean(entry && updatePreferences.isPending),
@@ -85,6 +105,7 @@ export function usePageResourceActions() {
85
105
  ? [
86
106
  {
87
107
  key: "add-to-chat",
108
+ icon: createElement(MessageSquarePlus, { className: "h-4 w-4" }),
88
109
  label: t("docBrowserAddToChat"),
89
110
  restoreFocus: false,
90
111
  onSelect: () =>
@@ -94,6 +115,7 @@ export function usePageResourceActions() {
94
115
  : []),
95
116
  {
96
117
  key: "copy-uri",
118
+ icon: createElement(Link, { className: "h-4 w-4" }),
97
119
  label: t("pageCopyUri"),
98
120
  onSelect: () => {
99
121
  void navigator.clipboard
@@ -103,7 +125,6 @@ export function usePageResourceActions() {
103
125
  },
104
126
  ];
105
127
  return [
106
- { key: "page", items },
107
128
  ...(viewer
108
129
  ? [
109
130
  {
@@ -132,6 +153,38 @@ export function usePageResourceActions() {
132
153
  },
133
154
  ]
134
155
  : []),
156
+ ...groupPageResourceActions(items),
135
157
  ];
136
158
  };
137
159
  }
160
+
161
+ function groupPageResourceActions(
162
+ items: ContextMenuItem[],
163
+ ): ContextMenuGroup[] {
164
+ return [
165
+ {
166
+ key: "page-reference",
167
+ items: items.filter(
168
+ (item) => item.key === "add-to-chat" || item.key === "copy-uri",
169
+ ),
170
+ },
171
+ {
172
+ key: "page-layout",
173
+ items: [
174
+ {
175
+ key: "layout",
176
+ label: t("pageLayoutActions"),
177
+ icon: createElement(LayoutPanelLeft, { className: "h-4 w-4" }),
178
+ children: [
179
+ {
180
+ key: "placement",
181
+ items: items.filter(
182
+ (item) => item.key !== "add-to-chat" && item.key !== "copy-uri",
183
+ ),
184
+ },
185
+ ],
186
+ },
187
+ ],
188
+ },
189
+ ];
190
+ }
@@ -26,7 +26,6 @@ export type {
26
26
 
27
27
  export { PageResourceIcon } from './components/page-resource-icon';
28
28
  export { PageResourceActionsMenu, PageResourceActionItems } from './components/page-resource-actions-menu';
29
- export { CurrentPageActions } from './components/current-page-actions';
30
29
  export { PageResourceSidebarNav } from './components/page-resource-sidebar-nav';
31
30
  export { SYSTEM_OBJECT_RESOURCE_RENDERERS } from './components/system-object-resource';
32
31
  export { WORKSPACE_FILE_PANEL_RENDERERS } from './components/workspace-file-resource';
@@ -167,8 +167,7 @@ export function ServiceAppListItem({
167
167
  {t("serviceAppsMoreActions")}
168
168
  </TooltipContent>
169
169
  </Tooltip>
170
- <PopoverContent align="end" className="w-56 rounded-xl p-1.5">
171
- <PageResourceActionItems page={pageResourceFromSystemObject("service-app", app.id, app.title)} onSelect={() => setIsMenuOpen(false)} />
170
+ <PopoverContent align="end" variant="menu">
172
171
  <div className="my-1 h-px bg-border" />
173
172
  <ServiceAppMenuItem
174
173
  disabled={!canDisconnectRuntime}
@@ -197,6 +196,8 @@ export function ServiceAppListItem({
197
196
  onClick={openDeleteDialog}
198
197
  />
199
198
  )}
199
+ <div className="my-1 h-px bg-border" />
200
+ <PageResourceActionItems page={pageResourceFromSystemObject("service-app", app.id, app.title)} onSelect={() => setIsMenuOpen(false)} />
200
201
  </PopoverContent>
201
202
  </Popover>
202
203
  </div>
@@ -181,6 +181,46 @@ describe('DesktopUpdateConfig', () => {
181
181
  expect(screen.getByText('fetch failed: getaddrinfo ENOTFOUND updates.nextclaw.io')).toBeTruthy();
182
182
  });
183
183
 
184
+ it('offers a manual Portable Edition download when in-app updates are blocked', () => {
185
+ useRuntimeUpdateStore.setState((state) => ({
186
+ ...state,
187
+ snapshot: state.snapshot
188
+ ? {
189
+ ...state.snapshot,
190
+ status: 'blocked',
191
+ installationKind: 'desktop-bundle',
192
+ blockReason: 'unsupported-installation',
193
+ errorMessage: 'Portable Edition does not support in-app updates yet. Download a newer Portable Edition and keep the data directory.'
194
+ }
195
+ : null
196
+ }));
197
+
198
+ renderDesktopUpdateConfig();
199
+
200
+ const downloadLink = screen.getByRole('link', { name: '下载最新 Portable Edition' });
201
+ expect(downloadLink.getAttribute('href')).toBe('https://github.com/Peiiii/nextclaw/releases');
202
+ expect(downloadLink.getAttribute('target')).toBe('_blank');
203
+ });
204
+
205
+ it('does not offer a Portable Edition download for non-desktop update blocks', () => {
206
+ useRuntimeUpdateStore.setState((state) => ({
207
+ ...state,
208
+ snapshot: state.snapshot
209
+ ? {
210
+ ...state.snapshot,
211
+ status: 'blocked',
212
+ installationKind: 'npm-runtime-bundle',
213
+ blockReason: 'unsupported-installation',
214
+ errorMessage: 'This runtime does not support updates.'
215
+ }
216
+ : null
217
+ }));
218
+
219
+ renderDesktopUpdateConfig();
220
+
221
+ expect(screen.queryByRole('link', { name: '下载最新 Portable Edition' })).toBeNull();
222
+ });
223
+
184
224
  it('loads structured release notes from the docs JSON endpoint', async () => {
185
225
  const fetchMock = vi.fn(async () => new Response(JSON.stringify({
186
226
  schemaVersion: 1,