@nextclaw/ui 0.24.1 → 0.24.3

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 (353) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/dist/.vite/manifest.json +2477 -0
  3. package/dist/assets/{abnfDiagram-VRR7QNED-BcFJp_1X.js → abnfDiagram-VRR7QNED-Kzlh3ZEC.js} +1 -1
  4. package/dist/assets/account-panel-BRTSwNvA.js +1 -0
  5. package/dist/assets/{aiden0z-pptx-renderer.es-Dp2ezlQn.js → aiden0z-pptx-renderer.es-k1xZLzeo.js} +1 -1
  6. package/dist/assets/api-BnUzdltV.js +13 -0
  7. package/dist/assets/app-data-Bjh_zM45.js +1 -0
  8. package/dist/assets/app-presenter-provider-CBt-lyr_.js +109 -0
  9. package/dist/assets/app-presenter-provider-D3BuuOyZ.css +1 -0
  10. package/dist/assets/appearance-settings-page-BrOYSgG0.js +1 -0
  11. package/dist/assets/{arc-CkgyPMyW.js → arc-CpivM836.js} +1 -1
  12. package/dist/assets/architecture-TIHT7OUA-CVRF_cYQ.js +1 -0
  13. package/dist/assets/{architectureDiagram-ZJ3FMSHR-CMd4cIjI.js → architectureDiagram-ZJ3FMSHR-ifjW1qjI.js} +1 -1
  14. package/dist/assets/{blockDiagram-677ZJIJ3-Bz3tbhet.js → blockDiagram-677ZJIJ3-C_zeJrKF.js} +1 -1
  15. package/dist/assets/{book-open-o2x7OLiO.js → book-open-xqEj1OLE.js} +1 -1
  16. package/dist/assets/boxes-D6uPnkGo.js +1 -0
  17. package/dist/assets/{c4Diagram-LMCZKHZV-D9hFNAGv.js → c4Diagram-LMCZKHZV-CpapRftp.js} +1 -1
  18. package/dist/assets/card-CS8su2Zh.js +1 -0
  19. package/dist/assets/channel-CBrWRr4N.js +1 -0
  20. package/dist/assets/channels-list-page-BE0TxB3B.js +8 -0
  21. package/dist/assets/chevron-down-BL-yMYcX.js +1 -0
  22. package/dist/assets/chunk-2Q5K7J3B-B8mmRk7d.js +1 -0
  23. package/dist/assets/{chunk-32BRIVSS-7NrjEITq.js → chunk-32BRIVSS-BKBwdmKR.js} +1 -1
  24. package/dist/assets/{chunk-52WLFC77-viIcqj9q.js → chunk-52WLFC77-BgZRCpDx.js} +1 -1
  25. package/dist/assets/{chunk-5FCAYU7R-Vsu4J-VM.js → chunk-5FCAYU7R-YWfOINJi.js} +1 -1
  26. package/dist/assets/{chunk-5HE753X5-C-w6CQZK.js → chunk-5HE753X5-BZ6I07d2.js} +1 -1
  27. package/dist/assets/{chunk-5JV3BV7I-CuKgJQ4W.js → chunk-5JV3BV7I-B7QYgZU5.js} +1 -1
  28. package/dist/assets/{chunk-5TONJI2A-CYeiWZKb.js → chunk-5TONJI2A-DjyQwgL2.js} +1 -1
  29. package/dist/assets/{chunk-5VM5RSS4-DRCbfwIn.js → chunk-5VM5RSS4-DheHV7X2.js} +1 -1
  30. package/dist/assets/{chunk-7BUUIJ7U-BvydOcm3.js → chunk-7BUUIJ7U-D6bq2PKF.js} +1 -1
  31. package/dist/assets/{chunk-BIQX33UG-RlppOXKo.js → chunk-BIQX33UG-CL9x0ibm.js} +1 -1
  32. package/dist/assets/{chunk-C7G6YPKG-DAizzYwr.js → chunk-C7G6YPKG-8z3B6RUH.js} +1 -1
  33. package/dist/assets/{chunk-CQNSW5MT-5eChJHca.js → chunk-CQNSW5MT-DpHkxY3n.js} +1 -1
  34. package/dist/assets/{chunk-CYSBUYHQ-YHPr53fv.js → chunk-CYSBUYHQ-DIJDLsdJ.js} +1 -1
  35. package/dist/assets/{chunk-EMLP6XTP-CK4D7Hz5.js → chunk-EMLP6XTP-B-LjAyVd.js} +1 -1
  36. package/dist/assets/{chunk-EX3LRPZG-BmlDFIRW.js → chunk-EX3LRPZG-BChuEPvO.js} +1 -1
  37. package/dist/assets/{chunk-FWX5IMBZ-D0ksHZZ9.js → chunk-FWX5IMBZ-fBFt0u3f.js} +2 -2
  38. package/dist/assets/{chunk-HOUHSVGY-DPkq8FCm.js → chunk-HOUHSVGY-MD0SpW5A.js} +1 -1
  39. package/dist/assets/{chunk-ICXQ74PX-FAatv5fC.js → chunk-ICXQ74PX-DvCcw_hz.js} +1 -1
  40. package/dist/assets/{chunk-JG7HCLWE-DLkIi4F7.js → chunk-JG7HCLWE-B8zqBnU9.js} +1 -1
  41. package/dist/assets/{chunk-JWPE2WC7-DawOjNE3.js → chunk-JWPE2WC7-C9efnkGb.js} +1 -1
  42. package/dist/assets/{chunk-MOJQB5TN-Di52TorW.js → chunk-MOJQB5TN-gXbfqoqq.js} +1 -1
  43. package/dist/assets/{chunk-MOZMSUNE-PfyK9w1-.js → chunk-MOZMSUNE-twIwdkqC.js} +1 -1
  44. package/dist/assets/{chunk-OGEWGWER-C3jdH3Ty.js → chunk-OGEWGWER-DdR0_cWK.js} +1 -1
  45. package/dist/assets/{chunk-OSBZ3O6U-CecHo-f0.js → chunk-OSBZ3O6U-D7D58Foa.js} +1 -1
  46. package/dist/assets/{chunk-PUDLZKDR-f3IJh3sN.js → chunk-PUDLZKDR-DPILgOVY.js} +1 -1
  47. package/dist/assets/{chunk-Q4XR5HBZ-DQFeUPxZ.js → chunk-Q4XR5HBZ-C0JJx7lW.js} +1 -1
  48. package/dist/assets/{chunk-QBLGF6JB-BhUeG0y2.js → chunk-QBLGF6JB-Dbsf69ng.js} +1 -1
  49. package/dist/assets/{chunk-R7FJI6CG-C3XpZ-2u.js → chunk-R7FJI6CG-Bb8n616-.js} +1 -1
  50. package/dist/assets/{chunk-RYQCIY6F-CAqa5klQ.js → chunk-RYQCIY6F-1HOsOrSl.js} +1 -1
  51. package/dist/assets/{chunk-U6XO7XAA-D_WXezeD.js → chunk-U6XO7XAA-BAFDVb1v.js} +1 -1
  52. package/dist/assets/{chunk-V7JOEXUC-CZ70oOtp.js → chunk-V7JOEXUC-8-g68lu5.js} +1 -1
  53. package/dist/assets/chunk-VAUOI2AC-Cpml0inz.js +1 -0
  54. package/dist/assets/{chunk-VR4S4FIN-B7AHEYYk.js → chunk-VR4S4FIN-P6UGmaYd.js} +1 -1
  55. package/dist/assets/{chunk-WYO6CB5R-Dn9To_OI.js → chunk-WYO6CB5R-6wjFTXNP.js} +2 -2
  56. package/dist/assets/chunk-XXDRQBXY-nCiqnCpF.js +1 -0
  57. package/dist/assets/{chunk-YOTPTUD7-BYEgRBk3.js → chunk-YOTPTUD7-CLYXVZAi.js} +1 -1
  58. package/dist/assets/{chunk-ZGVPDNZ5-C6YtZDU0.js → chunk-ZGVPDNZ5-BRTmSAhp.js} +1 -1
  59. package/dist/assets/{chunk-ZIRB5QZD-DMuYtnHF.js → chunk-ZIRB5QZD-CH_1eoSV.js} +1 -1
  60. package/dist/assets/circle-alert-lSa3LRvS.js +1 -0
  61. package/dist/assets/circle-dashed-CU3yApvP.js +1 -0
  62. package/dist/assets/classDiagram-OUVF2IWQ-C-DRqAaW.js +1 -0
  63. package/dist/assets/classDiagram-v2-EOCWNBFH-DXCcZ42U.js +1 -0
  64. package/dist/assets/client-BtGBwLTs.js +1 -0
  65. package/dist/assets/config-split-page-BxDwwAGa.js +1 -0
  66. package/dist/assets/confirm-dialog-S5JxZcS1.js +1 -0
  67. package/dist/assets/{cose-bilkent-JH36ORCC-Bc2Ex5Y3.js → cose-bilkent-JH36ORCC-L7w_qScX.js} +1 -1
  68. package/dist/assets/{createLucideIcon-rwwpqyks.js → createLucideIcon-BNKf0_kc.js} +1 -1
  69. package/dist/assets/cynefin-VYW2F7L2-2SOTgxxC.js +1 -0
  70. package/dist/assets/{cynefinDiagram-TSTJHNR4-DVysQoGz.js → cynefinDiagram-TSTJHNR4-Bay7uDSR.js} +1 -1
  71. package/dist/assets/{dagre-CMhQ1cAG.js → dagre-CJ_hX8xv.js} +1 -1
  72. package/dist/assets/{dagre-VKFMJZFB-RYyWpPJZ.js → dagre-VKFMJZFB-DDhk-2B3.js} +1 -1
  73. package/dist/assets/desktop-capabilities-page-BwHxJpbl.js +1 -0
  74. package/dist/assets/desktop-update-config-Bfg7OdJ2.js +1 -0
  75. package/dist/assets/{diagram-FQU43EPY-BD6KONdF.js → diagram-FQU43EPY-somFq5y8.js} +1 -1
  76. package/dist/assets/{diagram-G47NLZAW-CGB2ptRR.js → diagram-G47NLZAW-hzgyQsPd.js} +1 -1
  77. package/dist/assets/{diagram-NH7WQ7WH-DnkOI6cU.js → diagram-NH7WQ7WH-BkJ4gEQJ.js} +1 -1
  78. package/dist/assets/{diagram-OA4YK3LP-DF82RZ7P.js → diagram-OA4YK3LP-CS3ZkK4w.js} +1 -1
  79. package/dist/assets/{diagram-WEI45ONY-Be2v7Ck9.js → diagram-WEI45ONY-BApo-JHS.js} +1 -1
  80. package/dist/assets/dialog-Xe73hpQz.js +5 -0
  81. package/dist/assets/dist-BiIcnf9j.js +1 -0
  82. package/dist/assets/dist-C5I0v8Qn.js +1 -0
  83. package/dist/assets/{dist--ta5rX12.js → dist-Cnhwure9.js} +1 -1
  84. package/dist/assets/{dist-aNN3iQxV.js → dist-czPa8xFO.js} +1 -1
  85. package/dist/assets/doc-browser-BgRP0Wni.js +1 -0
  86. package/dist/assets/doc-browser-C8eqJLi_.js +1 -0
  87. package/dist/assets/doc-browser-context-BPCDFcC-.js +1 -0
  88. package/dist/assets/{docx-preview-BqwAjrj3.js → docx-preview-ByCKKIVT.js} +1 -1
  89. package/dist/assets/{ebnfDiagram-CCIWWBDH-CP98OYk8.js → ebnfDiagram-CCIWWBDH-DWjfeEie.js} +1 -1
  90. package/dist/assets/ellipsis-C5YXdOUx.js +1 -0
  91. package/dist/assets/{erDiagram-Q63AITRT-ZLlx8xfs.js → erDiagram-Q63AITRT-DR7vWYHM.js} +1 -1
  92. package/dist/assets/es2015-cuwbYI-l.js +41 -0
  93. package/dist/assets/eventmodeling-45OFAUF4-CXZNG56U.js +1 -0
  94. package/dist/assets/extensions-DgKQRebe.js +1 -0
  95. package/dist/assets/eye-C84inPcV.js +1 -0
  96. package/dist/assets/flowDiagram-23GEKE2U-2065oyKr.js +1 -0
  97. package/dist/assets/form-actions-AIdXOoCL.js +1 -0
  98. package/dist/assets/{ganttDiagram-NO4QXBWP-DXCDjHR2.js → ganttDiagram-NO4QXBWP-E3l9tuaL.js} +1 -1
  99. package/dist/assets/gitGraph-TEB2WS4Q-B0Ln3ZuK.js +1 -0
  100. package/dist/assets/{gitGraphDiagram-IHSO6WYX-l2HWrTJp.js → gitGraphDiagram-IHSO6WYX-DiMvbHKf.js} +1 -1
  101. package/dist/assets/i18n-CKvm9zyk.js +2 -0
  102. package/dist/assets/i18n-provider-CPV43pMX.js +1 -0
  103. package/dist/assets/info-DKCQHKI2-B06uqGM4.js +1 -0
  104. package/dist/assets/{infoDiagram-FWYZ7A6U-D-DjX-bn.js → infoDiagram-FWYZ7A6U-BZDL0NDJ.js} +1 -1
  105. package/dist/assets/{ishikawaDiagram-FXEZZL3T-y_Xy128F.js → ishikawaDiagram-FXEZZL3T-BZ9WvJb2.js} +1 -1
  106. package/dist/assets/{journeyDiagram-5HDEW3XC-DJn5xYcQ.js → journeyDiagram-5HDEW3XC-C3TZvKM8.js} +1 -1
  107. package/dist/assets/{jszip.min-8hyJpgpx.js → jszip.min-D_AtKL_8.js} +1 -1
  108. package/dist/assets/{kanban-definition-HUTT4EX6-DJVIOR21.js → kanban-definition-HUTT4EX6-9FqauDEi.js} +1 -1
  109. package/dist/assets/{line-Cos7PIOj.js → line-Ca49mj80.js} +1 -1
  110. package/dist/assets/{linear-BtDLqtzF.js → linear-DlCeG66S.js} +1 -1
  111. package/dist/assets/loader-circle-K1BDdHR5.js +1 -0
  112. package/dist/assets/login-page-ClcrvN04.js +1 -0
  113. package/dist/assets/login-page-DeDQjVxv.js +1 -0
  114. package/dist/assets/logo-badge-C-5-zim6.js +1 -0
  115. package/dist/assets/main-DGqlaAaI.css +1 -0
  116. package/dist/assets/main-i18n-catalog.config-CpNF5aVc.js +1 -0
  117. package/dist/assets/main-nMVfHb5K.js +2 -0
  118. package/dist/assets/{map-BKnQyvd0.js → map-WMnNq9UV.js} +1 -1
  119. package/dist/assets/mcp-marketplace-page-CWjJGp_D.js +10 -0
  120. package/dist/assets/mcp-marketplace-page-fvu2NcXT.js +1 -0
  121. package/dist/assets/{mermaid-parser.core-XUNZhr2W.js → mermaid-parser.core-B8xk4hpS.js} +2 -2
  122. package/dist/assets/{mermaid.core-ySrvolIe.js → mermaid.core-CFPiczKH.js} +3 -3
  123. package/dist/assets/{mindmap-definition-LN4V7U3C-BypL9tei.js → mindmap-definition-LN4V7U3C-CnxA52L6.js} +1 -1
  124. package/dist/assets/model-config-page-Dv_zE7HN.js +1 -0
  125. package/dist/assets/{navigation-link-C_6oj3zz.js → navigation-link-BAQjgx2F.js} +1 -1
  126. package/dist/assets/{ordinal-CxxVWxYU.js → ordinal-PgiFlfBJ.js} +1 -1
  127. package/dist/assets/packet-7NZHBO7P-CHx-igbz.js +1 -0
  128. package/dist/assets/panel-app-BUUle0gP.js +1 -0
  129. package/dist/assets/panel-app-i18n-catalog.config-BzxE3tZb.js +1 -0
  130. package/dist/assets/panelStandalone-CFRBHV94.js +2 -0
  131. package/dist/assets/panelStandalone-Y91OJkgV.css +1 -0
  132. package/dist/assets/{pegDiagram-2B236MQR-D-U2KBGv.js → pegDiagram-2B236MQR-A8oz7dTH.js} +1 -1
  133. package/dist/assets/pie-RZYD4A2V-DOaMc-x9.js +1 -0
  134. package/dist/assets/{pieDiagram-ENE6RG2P-CBi3GusJ.js → pieDiagram-ENE6RG2P-mtBnkYY0.js} +1 -1
  135. package/dist/assets/plus-LA8-jpxP.js +1 -0
  136. package/dist/assets/popover-NkrFlWHp.js +1 -0
  137. package/dist/assets/privacy-settings-page-D6uc9f-A.js +1 -0
  138. package/dist/assets/projects-BD1rG75R.js +1 -0
  139. package/dist/assets/provider-models-5PGHb6mP.js +1 -0
  140. package/dist/assets/provider-scoped-model-input-DUa_9uwg.js +1 -0
  141. package/dist/assets/providers-config-page-DAiqxlF_.js +1 -0
  142. package/dist/assets/puzzle-D6gulz7u.js +1 -0
  143. package/dist/assets/pwa-C_Zte1xe.js +1 -0
  144. package/dist/assets/{quadrantDiagram-ABIIQ3AL-H0ZuvnZL.js → quadrantDiagram-ABIIQ3AL-DE1VybGO.js} +1 -1
  145. package/dist/assets/radar-I7S5WNFK-CXsdndDk.js +1 -0
  146. package/dist/assets/railroad-3IZDKUUU-DxgXdrYP.js +1 -0
  147. package/dist/assets/railroad-abnf-AHOZXSZD-CR1tnyjz.js +1 -0
  148. package/dist/assets/railroad-ebnf-EBAXGLYW-CjjxZR2U.js +1 -0
  149. package/dist/assets/railroad-peg-LSFZ7HO6-DgVGJCHN.js +1 -0
  150. package/dist/assets/{railroadDiagram-RFXS5EU6-D7Y2EqSd.js → railroadDiagram-RFXS5EU6-u9EMg14B.js} +1 -1
  151. package/dist/assets/{refresh-cw-DsT2kvXi.js → refresh-cw-h9Xq3LY3.js} +1 -1
  152. package/dist/assets/remote-BO0QP7ua.js +1 -0
  153. package/dist/assets/{requirementDiagram-TGXJPOKE-BiXNX1NN.js → requirementDiagram-TGXJPOKE-BAWHAqEi.js} +1 -1
  154. package/dist/assets/{rotate-cw-BCAs6c3R.js → rotate-cw-B6xm41Wn.js} +1 -1
  155. package/dist/assets/runtime-config-page-w1UAkGF_.js +1 -0
  156. package/dist/assets/{sankeyDiagram-HTMAVEWB-qzDeLztW.js → sankeyDiagram-HTMAVEWB-aDy-jdwF.js} +1 -1
  157. package/dist/assets/{save-CadRMTMQ.js → save-fff_WpY2.js} +1 -1
  158. package/dist/assets/{search-B6tB4fzB.js → search-CUe41wjm.js} +1 -1
  159. package/dist/assets/search-config-page-3xbhRlW2.js +1 -0
  160. package/dist/assets/secrets-config-page-Dn8_rdOj.js +3 -0
  161. package/dist/assets/security-config-Y1nEGM3A.js +1 -0
  162. package/dist/assets/select-CG5YWnwe.js +1 -0
  163. package/dist/assets/{sequenceDiagram-DBY2YBRQ-BtRbPQtL.js → sequenceDiagram-DBY2YBRQ-DwHC4mbX.js} +1 -1
  164. package/dist/assets/service-apps-panel-CGc04aMn.js +1 -0
  165. package/dist/assets/settings-2-CE3ELQyk.js +1 -0
  166. package/dist/assets/{sizeCapture-X5ZJPWSS-CMYvM3SL.js → sizeCapture-X5ZJPWSS-oOMY0Fag.js} +1 -1
  167. package/dist/assets/skeleton-BL9h32W7.js +1 -0
  168. package/dist/assets/{src-Bw-mzbQb.js → src-CLPoo5iL.js} +1 -1
  169. package/dist/assets/{stateDiagram-2N3HPSRC-BJ-mFeOz.js → stateDiagram-2N3HPSRC-B0Epk-VR.js} +1 -1
  170. package/dist/assets/stateDiagram-v2-6OUMAXLB-Ct4zTw8P.js +1 -0
  171. package/dist/assets/{swimlanes-5IMT3BWC-BsucVOO2.js → swimlanes-5IMT3BWC-T0w-Iuzf.js} +2 -2
  172. package/dist/assets/swimlanesDiagram-G3AALYLV-CdmN0PBd.js +8 -0
  173. package/dist/assets/tabs-BuE0qKMh.js +1 -0
  174. package/dist/assets/{tag-chip-BZKk0UkK.js → tag-chip-BDzpHEli.js} +1 -1
  175. package/dist/assets/theme-provider-B6FinATQ.js +1 -0
  176. package/dist/assets/{timeline-definition-FHXFAJF6-Bicewlzo.js → timeline-definition-FHXFAJF6-BEonB5Np.js} +1 -1
  177. package/dist/assets/tooltip-DYcuWhuP.js +1 -0
  178. package/dist/assets/trash-2-BPPYt2NV.js +1 -0
  179. package/dist/assets/treeView-QDETBFTQ-Dad4_Wc7.js +1 -0
  180. package/dist/assets/treemap-6X3UGDF4-9_GWWZsY.js +1 -0
  181. package/dist/assets/use-config-BPUM2lbo.js +1 -0
  182. package/dist/assets/use-desktop-capabilities-BDjArQP3.js +1 -0
  183. package/dist/assets/use-panel-app-scroll-restoration-DsV1IJx-.js +1 -0
  184. package/dist/assets/use-viewport-layout-BS18drMT.js +1 -0
  185. package/dist/assets/utils-CBZPzC_b.js +8 -0
  186. package/dist/assets/{vennDiagram-L72KCM5P-BxOOnO9x.js → vennDiagram-L72KCM5P-BDvcOCo2.js} +1 -1
  187. package/dist/assets/wardley-OPB4EBWU-BXpvyee9.js +1 -0
  188. package/dist/assets/{wardleyDiagram-EHGQE667-Cl7NltMv.js → wardleyDiagram-EHGQE667-jMLMgjzE.js} +1 -1
  189. package/dist/assets/x-DdXOqLnX.js +1 -0
  190. package/dist/assets/{xychartDiagram-FW5EYKEG-DiMIxQ7N.js → xychartDiagram-FW5EYKEG-pzrpclEv.js} +1 -1
  191. package/dist/index.html +56 -25
  192. package/dist/panel-standalone.html +30 -0
  193. package/package.json +4 -4
  194. package/panel-standalone.html +13 -0
  195. package/panel-standalone.tailwind.config.js +16 -0
  196. package/src/app/components/app-presenter-provider.tsx +8 -1
  197. package/src/app/components/theme-provider.tsx +0 -5
  198. package/src/app/index.tsx +18 -4
  199. package/src/app/presenters/app.presenter.ts +4 -4
  200. package/src/app/styles/panel-app-standalone.css +31 -0
  201. package/src/app/test/vitest-setup.ts +3 -0
  202. package/src/features/account/hooks/use-auth.ts +7 -57
  203. package/src/features/account/index.ts +3 -2
  204. package/src/features/apps/components/__tests__/apps-panel.test.tsx +7 -5
  205. package/src/features/apps/components/apps-panel.tsx +9 -3
  206. package/src/features/chat/features/message/utils/__tests__/chat-message-timeline.utils.test.ts +10 -0
  207. package/src/features/chat/features/session/components/__tests__/chat-sidebar-project-groups.test.tsx +90 -6
  208. package/src/features/chat/features/session/components/chat-sidebar-project-groups.tsx +64 -2
  209. package/src/features/chat/features/session/utils/__tests__/chat-sidebar-session-groups.utils.test.ts +27 -2
  210. package/src/features/chat/features/session/utils/chat-sidebar-session-groups.utils.ts +0 -11
  211. package/src/features/panel-apps/components/__tests__/panel-app-list-item.test.tsx +18 -0
  212. package/src/features/panel-apps/components/__tests__/panel-app-open-standalone-menu-item.test.tsx +57 -0
  213. package/src/features/panel-apps/components/__tests__/panel-app-toolbar.test.tsx +15 -0
  214. package/src/features/panel-apps/components/panel-app-list-item.tsx +5 -0
  215. package/src/features/panel-apps/components/panel-app-open-standalone-menu-item.tsx +32 -0
  216. package/src/features/panel-apps/components/panel-app-runtime-surface.tsx +141 -0
  217. package/src/features/{service-apps/components/service-action-authorization-dialog.tsx → panel-apps/components/panel-app-service-action-authorization-dialog.tsx} +4 -4
  218. package/src/features/panel-apps/components/panel-app-toolbar.tsx +5 -0
  219. package/src/features/panel-apps/hooks/use-panel-app-client-grant.ts +2 -2
  220. package/src/features/panel-apps/hooks/{use-panel-app-main-runtime.ts → use-panel-app-runtime.ts} +7 -5
  221. package/src/features/panel-apps/hooks/use-panel-apps.ts +14 -0
  222. package/src/features/panel-apps/index.ts +5 -0
  223. package/src/features/panel-apps/managers/__tests__/panel-app-bridge.manager.test.ts +2 -2
  224. package/src/features/panel-apps/managers/panel-app-bridge.manager.ts +2 -2
  225. package/src/features/panel-apps/managers/panel-app-service-action-authorization.manager.ts +10 -0
  226. package/src/features/panel-apps/pages/__tests__/panel-app-main-page.test.tsx +12 -3
  227. package/src/features/panel-apps/pages/__tests__/panel-app-standalone-page.test.tsx +63 -0
  228. package/src/features/panel-apps/pages/panel-app-main-page.tsx +9 -121
  229. package/src/features/panel-apps/pages/panel-app-standalone-page.tsx +29 -0
  230. package/src/features/panel-apps/presenters/panel-app-host.presenter.ts +9 -0
  231. package/src/features/panel-apps/providers/panel-app-host.provider.tsx +45 -0
  232. package/src/features/panel-apps/routes/__tests__/panel-app-standalone.route.test.tsx +50 -0
  233. package/src/features/panel-apps/routes/panel-app-standalone.route.tsx +94 -0
  234. package/src/features/{service-apps/stores/service-action-authorization.store.ts → panel-apps/stores/panel-app-service-action-authorization.store.ts} +6 -6
  235. package/src/features/projects/AGENTS.md +1 -2
  236. package/src/features/projects/components/project-agreement.tsx +36 -77
  237. package/src/features/projects/components/project-artifacts.test.tsx +90 -62
  238. package/src/features/projects/components/project-artifacts.tsx +81 -160
  239. package/src/features/projects/components/project-materials.test.tsx +80 -0
  240. package/src/features/projects/components/project-overview.test.tsx +80 -0
  241. package/src/features/projects/components/project-overview.tsx +115 -56
  242. package/src/features/projects/components/project-skills.tsx +24 -14
  243. package/src/features/projects/components/work/project-work-item-drawer.test.tsx +3 -2
  244. package/src/features/projects/components/work/project-work-item-drawer.tsx +8 -10
  245. package/src/features/projects/components/work/project-work-items.test.tsx +180 -4
  246. package/src/features/projects/components/work/project-work-items.tsx +278 -50
  247. package/src/features/projects/components/work/project-work-state-settings.tsx +7 -3
  248. package/src/features/projects/hooks/use-project-materials.test.tsx +65 -0
  249. package/src/features/projects/hooks/use-project-materials.ts +24 -0
  250. package/src/features/projects/hooks/use-project-work.test.tsx +165 -7
  251. package/src/features/projects/hooks/use-project-work.ts +88 -9
  252. package/src/features/projects/pages/project-home-page.tsx +52 -89
  253. package/src/features/projects/pages/projects-page.test.tsx +81 -32
  254. package/src/features/projects/presenters/project-home.presenter.test.ts +1 -64
  255. package/src/features/projects/presenters/project-home.presenter.ts +17 -54
  256. package/src/features/projects/utils/project-artifact-view.utils.test.ts +19 -53
  257. package/src/features/projects/utils/project-artifact-view.utils.ts +17 -47
  258. package/src/features/projects/utils/project-work-state-label.utils.test.ts +28 -0
  259. package/src/features/projects/utils/project-work-state-label.utils.ts +20 -0
  260. package/src/features/service-apps/index.ts +3 -3
  261. package/src/main.tsx +3 -0
  262. package/src/shared/hooks/use-auth-status.ts +49 -0
  263. package/src/shared/hooks/use-projects.ts +15 -1
  264. package/src/shared/lib/api/projects/project.utils.ts +4 -0
  265. package/src/shared/lib/i18n/configs/main-i18n-catalog.config.ts +78 -0
  266. package/src/shared/lib/i18n/configs/panel-app-i18n-catalog.config.ts +12 -0
  267. package/src/shared/lib/i18n/index.ts +30 -82
  268. package/src/shared/lib/i18n/locales/en-US/doc-browser.json +0 -55
  269. package/src/shared/lib/i18n/locales/en-US/panel-app.json +59 -0
  270. package/src/shared/lib/i18n/locales/en-US/projects.json +24 -59
  271. package/src/shared/lib/i18n/locales/zh-CN/doc-browser.json +0 -55
  272. package/src/shared/lib/i18n/locales/zh-CN/panel-app.json +59 -0
  273. package/src/shared/lib/i18n/locales/zh-CN/projects.json +24 -59
  274. package/vite.config.ts +36 -1
  275. package/dist/assets/api-BqVIq0lF.js +0 -13
  276. package/dist/assets/appearance-settings-page-CTWn1eAT.js +0 -1
  277. package/dist/assets/architecture-TIHT7OUA-DO2x53FK.js +0 -1
  278. package/dist/assets/channel-nBvMaVyP.js +0 -1
  279. package/dist/assets/channels-list-page-Bzn-pzyJ.js +0 -8
  280. package/dist/assets/chunk-2Q5K7J3B-CJgqFDS4.js +0 -1
  281. package/dist/assets/chunk-VAUOI2AC-BGvh3yIx.js +0 -1
  282. package/dist/assets/chunk-XXDRQBXY-DaBxG5gs.js +0 -1
  283. package/dist/assets/classDiagram-OUVF2IWQ-P6N03G2R.js +0 -1
  284. package/dist/assets/classDiagram-v2-EOCWNBFH-B1_uLvMv.js +0 -1
  285. package/dist/assets/config-split-page-CnRbXXlh.js +0 -1
  286. package/dist/assets/confirm-dialog-B_Hbcra2.js +0 -5
  287. package/dist/assets/cynefin-VYW2F7L2-B-H8Qp-n.js +0 -1
  288. package/dist/assets/desktop-capabilities-page-C2ayYQTr.js +0 -1
  289. package/dist/assets/desktop-update-config-C13czpyL.js +0 -1
  290. package/dist/assets/dist-BsF3hfOi.js +0 -1
  291. package/dist/assets/doc-browser-D8dIOo9E.js +0 -1
  292. package/dist/assets/doc-browser-DcuOOtpP.js +0 -1
  293. package/dist/assets/doc-browser-context-Dhsit2Ma.js +0 -1
  294. package/dist/assets/eventmodeling-45OFAUF4-YVtShyFA.js +0 -1
  295. package/dist/assets/extensions-CD6xvKF_.js +0 -1
  296. package/dist/assets/flowDiagram-23GEKE2U-BeKXabVO.js +0 -1
  297. package/dist/assets/form-actions-CC4Wz_lL.js +0 -1
  298. package/dist/assets/gitGraph-TEB2WS4Q-jTSiBj-p.js +0 -1
  299. package/dist/assets/index-BTsbdtv6.js +0 -109
  300. package/dist/assets/index-Bcfy2kTh.css +0 -1
  301. package/dist/assets/info-DKCQHKI2-BIporNWb.js +0 -1
  302. package/dist/assets/loader-circle-B6aFbB-8.js +0 -1
  303. package/dist/assets/mcp-marketplace-page-BtUAypA7.js +0 -1
  304. package/dist/assets/mcp-marketplace-page-r2MmD9xO.js +0 -10
  305. package/dist/assets/model-config-page-DboxoT-I.js +0 -1
  306. package/dist/assets/packet-7NZHBO7P-CuFMADV0.js +0 -1
  307. package/dist/assets/pie-RZYD4A2V-XUeIXhM9.js +0 -1
  308. package/dist/assets/plus-DRsq9Kvs.js +0 -1
  309. package/dist/assets/privacy-settings-page-C2sDBqhb.js +0 -1
  310. package/dist/assets/projects-BcPcx-uD.js +0 -1
  311. package/dist/assets/providers-config-page-CmUFl9RF.js +0 -1
  312. package/dist/assets/radar-I7S5WNFK-D-NNGh1k.js +0 -1
  313. package/dist/assets/railroad-3IZDKUUU-C67_uDaM.js +0 -1
  314. package/dist/assets/railroad-abnf-AHOZXSZD-BZ8Yflnd.js +0 -1
  315. package/dist/assets/railroad-ebnf-EBAXGLYW-B7zO7RoK.js +0 -1
  316. package/dist/assets/railroad-peg-LSFZ7HO6-R63PTgLz.js +0 -1
  317. package/dist/assets/react-BjO9I4WZ.js +0 -8
  318. package/dist/assets/remote-Ci6x91ES.js +0 -1
  319. package/dist/assets/runtime-config-page-cQKNeAjL.js +0 -1
  320. package/dist/assets/search-config-page-C8lOIkxn.js +0 -1
  321. package/dist/assets/secrets-config-page-8clzAag2.js +0 -3
  322. package/dist/assets/security-config-DPbM6IVT.js +0 -1
  323. package/dist/assets/select-CO9S_C2C.js +0 -41
  324. package/dist/assets/skeleton-IHILNhIW.js +0 -1
  325. package/dist/assets/stateDiagram-v2-6OUMAXLB-BE-HqxOK.js +0 -1
  326. package/dist/assets/swimlanesDiagram-G3AALYLV-Dr1H9LJH.js +0 -8
  327. package/dist/assets/treeView-QDETBFTQ-npJ5dbqS.js +0 -1
  328. package/dist/assets/treemap-6X3UGDF4-pYrBx6oa.js +0 -1
  329. package/dist/assets/wardley-OPB4EBWU-CmSRKLR8.js +0 -1
  330. package/dist/assets/x-BuvHIb1d.js +0 -1
  331. package/src/features/projects/components/project-requests.tsx +0 -57
  332. package/src/features/projects/components/project-runs.tsx +0 -60
  333. package/src/features/projects/hooks/use-project-observation.test.tsx +0 -93
  334. package/src/features/projects/hooks/use-project-observation.ts +0 -115
  335. package/src/features/projects/utils/project-request-response.utils.test.ts +0 -44
  336. package/src/features/projects/utils/project-request-response.utils.ts +0 -39
  337. package/src/features/service-apps/managers/service-action-authorization.manager.ts +0 -10
  338. /package/dist/assets/{array-BQVzeGRt.js → array-BHDgItFN.js} +0 -0
  339. /package/dist/assets/{chunk-DseTPa7n.js → chunk-DF37yw5_.js} +0 -0
  340. /package/dist/assets/{chunk-KEIR6QF5-f9psyCXK.js → chunk-KEIR6QF5-DstBw0Hk.js} +0 -0
  341. /package/dist/assets/{chunk-Y2CYZVJY-D1mAU_Po.js → chunk-Y2CYZVJY-CA3ymQuH.js} +0 -0
  342. /package/dist/assets/{config-hints-nV5HPOvq.js → config-hints-CBPHrfYk.js} +0 -0
  343. /package/dist/assets/{cytoscape.esm-0uDtdD0l.js → cytoscape.esm-C7xr90o_.js} +0 -0
  344. /package/dist/assets/{defaultLocale-DSWXGgt0.js → defaultLocale-BG1oDVag.js} +0 -0
  345. /package/dist/assets/{graphlib-CQ6QV4yP.js → graphlib-CNMGfuTb.js} +0 -0
  346. /package/dist/assets/{host-capabilities-Tk038i04.js → host-capabilities-B107nwS3.js} +0 -0
  347. /package/dist/assets/{init-Y6h9jGtZ.js → init-wQIZuRSH.js} +0 -0
  348. /package/dist/assets/{katex-ARhY9PS1.js → katex-J8JuvgNl.js} +0 -0
  349. /package/dist/assets/{middleware-DXsbf9xI.js → middleware-0o3JF3tS.js} +0 -0
  350. /package/dist/assets/{path-10BjdyLh.js → path-N-C4MRP0.js} +0 -0
  351. /package/dist/assets/{preload-helper-D4M6sveU.js → preload-helper-ca-nBW7U.js} +0 -0
  352. /package/dist/assets/{rough.esm-DxgC_14r.js → rough.esm-BeWA068d.js} +0 -0
  353. /package/dist/assets/{xlsx-DrDuDNAa.js → xlsx-CwUXNtD3.js} +0 -0
@@ -1,69 +1,19 @@
1
- import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
2
- import { useEffect, useState } from 'react';
1
+ import { useMutation, useQueryClient } from '@tanstack/react-query';
3
2
  import {
4
- fetchAuthStatus,
5
3
  loginAuth,
6
4
  logoutAuth,
7
5
  setupAuth,
8
6
  updateAuthEnabled,
9
7
  updateAuthPassword
10
8
  } from '@/shared/lib/api';
11
- import type { AuthStatusView } from '@/shared/lib/api';
12
9
  import { toast } from 'sonner';
13
10
  import { t } from '@/shared/lib/i18n';
14
- import { isTransientRuntimeConnectionErrorMessage } from '@/shared/lib/transport';
15
-
16
- const AUTH_STATUS_BOOTSTRAP_PROBE_POLICY = {
17
- maxRetries: 8,
18
- startupTimeoutMs: 2_000,
19
- settledTimeoutMs: 5_000,
20
- retryBaseDelayMs: 500,
21
- retryMaxDelayMs: 3_000,
22
- } as const;
23
-
24
- export function isTransientAuthStatusBootstrapError(error: unknown): boolean {
25
- if (!(error instanceof Error)) {
26
- return false;
27
- }
28
- return isTransientRuntimeConnectionErrorMessage(error.message);
29
- }
30
-
31
- export function shouldRetryAuthStatusBootstrap(failureCount: number, error: unknown): boolean {
32
- if (failureCount >= AUTH_STATUS_BOOTSTRAP_PROBE_POLICY.maxRetries) {
33
- return false;
34
- }
35
- return isTransientAuthStatusBootstrapError(error);
36
- }
37
-
38
- export function resolveAuthStatusBootstrapRetryDelay(failureCount: number): number {
39
- return Math.min(
40
- AUTH_STATUS_BOOTSTRAP_PROBE_POLICY.retryMaxDelayMs,
41
- AUTH_STATUS_BOOTSTRAP_PROBE_POLICY.retryBaseDelayMs * 2 ** Math.max(0, failureCount - 1)
42
- );
43
- }
44
-
45
- export function useAuthStatus() {
46
- const [bootstrapSettled, setBootstrapSettled] = useState(false);
47
- const query = useQuery<AuthStatusView>({
48
- queryKey: ['auth-status'],
49
- queryFn: () => fetchAuthStatus({
50
- timeoutMs: bootstrapSettled
51
- ? AUTH_STATUS_BOOTSTRAP_PROBE_POLICY.settledTimeoutMs
52
- : AUTH_STATUS_BOOTSTRAP_PROBE_POLICY.startupTimeoutMs,
53
- }),
54
- staleTime: 5_000,
55
- retry: shouldRetryAuthStatusBootstrap,
56
- retryDelay: resolveAuthStatusBootstrapRetryDelay
57
- });
58
-
59
- useEffect(() => {
60
- if (query.isSuccess && !bootstrapSettled) {
61
- setBootstrapSettled(true);
62
- }
63
- }, [bootstrapSettled, query.isSuccess]);
64
-
65
- return query;
66
- }
11
+ export {
12
+ isTransientAuthStatusBootstrapError,
13
+ resolveAuthStatusBootstrapRetryDelay,
14
+ shouldRetryAuthStatusBootstrap,
15
+ useAuthStatus,
16
+ } from "@/shared/hooks/use-auth-status";
67
17
 
68
18
  function invalidateProtectedQueries(queryClient: ReturnType<typeof useQueryClient>): Promise<unknown[]> {
69
19
  return Promise.all([
@@ -1,4 +1,5 @@
1
- export { AccountPanel } from './components/account-panel';
2
- export { LoginPage } from './components/login-page';
3
1
  export { AccountManager } from './managers/account.manager';
4
2
  export { useAccountStore } from './stores/account.store';
3
+ export async function loadAccountPanel() {
4
+ return await import('./components/account-panel');
5
+ }
@@ -14,11 +14,13 @@ vi.mock('@/features/panel-apps', () => ({
14
14
  PanelAppsList: () => <div>panel-apps-content</div>,
15
15
  }));
16
16
  vi.mock('@/features/service-apps', () => ({
17
- ServiceAppsPanel: ({ onManagePackage }: { onManagePackage: (packageId: string) => void }) => (
18
- <button type="button" onClick={() => onManagePackage('nextclaw.personal-organizer')}>
19
- service-apps-content
20
- </button>
21
- ),
17
+ loadServiceAppsPanel: async () => ({
18
+ ServiceAppsPanel: ({ onManagePackage }: { onManagePackage: (packageId: string) => void }) => (
19
+ <button type="button" onClick={() => onManagePackage('nextclaw.personal-organizer')}>
20
+ service-apps-content
21
+ </button>
22
+ ),
23
+ }),
22
24
  }));
23
25
 
24
26
  function AppsPanelHarness() {
@@ -1,14 +1,18 @@
1
- import { useState } from 'react';
1
+ import { lazy, Suspense, useState } from 'react';
2
2
  import { AppWindow, Boxes, Server } from 'lucide-react';
3
3
  import type { PanelAppEntryView } from '@/shared/lib/api';
4
4
  import { AppPackagesPanel } from '@/features/apps/components/app-packages-panel';
5
5
  import { PanelAppsList } from '@/features/panel-apps';
6
- import { ServiceAppsPanel } from '@/features/service-apps';
6
+ import { loadServiceAppsPanel } from '@/features/service-apps';
7
7
  import { Tabs, TabsList, TabsTrigger } from '@/shared/components/ui/tabs';
8
8
  import { t } from '@/shared/lib/i18n';
9
9
 
10
10
  export type AppsPanelTab = 'apps' | 'panel-apps' | 'service-apps';
11
11
 
12
+ const ServiceAppsPanel = lazy(async () => ({
13
+ default: (await loadServiceAppsPanel()).ServiceAppsPanel,
14
+ }));
15
+
12
16
  export function AppsPanel({
13
17
  activeTab,
14
18
  onActiveTabChange,
@@ -54,7 +58,9 @@ export function AppsPanel({
54
58
  ) : activeTab === 'panel-apps' ? (
55
59
  <PanelAppsList onOpenPanelApp={onOpenPanelApp} />
56
60
  ) : (
57
- <ServiceAppsPanel onManagePackage={managePackage} />
61
+ <Suspense fallback={null}>
62
+ <ServiceAppsPanel onManagePackage={managePackage} />
63
+ </Suspense>
58
64
  )}
59
65
  </div>
60
66
  </div>
@@ -125,6 +125,16 @@ describe("chat message timeline visibility", () => {
125
125
  })).toBe(false);
126
126
  expect(isVisibleChatMessage(visibleMessage)).toBe(true);
127
127
  });
128
+
129
+ it("keeps a normal assistant reply visible when it explains the silent marker", () => {
130
+ expect(isVisibleChatMessage({
131
+ ...visibleMessage,
132
+ parts: [{
133
+ type: "text",
134
+ text: "空转 tick 调用一次服务,然后返回 `<noreply/>`,成本很低。",
135
+ }],
136
+ })).toBe(true);
137
+ });
128
138
  });
129
139
 
130
140
  describe("chat message continuation projection", () => {
@@ -9,7 +9,9 @@ import {
9
9
  import { useChatSessionListStore } from "@/features/chat/stores/chat-session-list.store";
10
10
 
11
11
  const mocks = vi.hoisted(() => ({
12
+ confirm: vi.fn(),
12
13
  createSession: vi.fn(),
14
+ removeProject: vi.fn(),
13
15
  toggleProjectCollapsed: vi.fn(),
14
16
  toggleProjectPinned: vi.fn(),
15
17
  }));
@@ -19,6 +21,18 @@ vi.mock("@/features/chat/components/providers/chat-presenter.provider", () => ({
19
21
  chatSessionListManager: mocks,
20
22
  }),
21
23
  }));
24
+ vi.mock("@/shared/hooks/use-projects", () => ({
25
+ useRemoveProject: () => ({
26
+ mutateAsync: mocks.removeProject,
27
+ isPending: false,
28
+ }),
29
+ }));
30
+ vi.mock("@/shared/hooks/use-confirm-dialog", () => ({
31
+ useConfirmDialog: () => ({
32
+ confirm: mocks.confirm,
33
+ ConfirmDialog: () => <div data-testid="confirm-dialog" />,
34
+ }),
35
+ }));
22
36
 
23
37
  const projectGroup: ChatSidebarProjectGroup = {
24
38
  projectId: "project-analysis",
@@ -50,9 +64,10 @@ function LocationProbe() {
50
64
  function renderProjectGroups(
51
65
  isPinned = false,
52
66
  group: ChatSidebarProjectGroup = projectGroup,
67
+ initialPath = "/chat",
53
68
  ) {
54
69
  return render(
55
- <MemoryRouter initialEntries={["/chat"]}>
70
+ <MemoryRouter initialEntries={[initialPath]}>
56
71
  <ChatSidebarProjectGroups
57
72
  groups={[{ ...group, isPinned }]}
58
73
  projectCronJobCountByRoot={new Map([[projectGroup.projectRoot, 2]])}
@@ -69,7 +84,11 @@ function renderProjectGroups(
69
84
 
70
85
  describe("ChatSidebarProjectGroups", () => {
71
86
  beforeEach(() => {
87
+ mocks.confirm.mockReset();
88
+ mocks.confirm.mockResolvedValue(true);
72
89
  mocks.createSession.mockReset();
90
+ mocks.removeProject.mockReset();
91
+ mocks.removeProject.mockResolvedValue(undefined);
73
92
  mocks.toggleProjectCollapsed.mockReset();
74
93
  mocks.toggleProjectPinned.mockReset();
75
94
  useChatSessionListStore.setState({
@@ -97,12 +116,16 @@ describe("ChatSidebarProjectGroups", () => {
97
116
  expect(projectRow?.className).toContain("hover:bg-gray-200/60");
98
117
  const projectContent = projectLink.parentElement;
99
118
  expect(projectContent?.classList.contains("pr-14")).toBe(false);
100
- expect(projectContent?.className).toContain("group-hover/project:pr-14");
119
+ expect(projectContent?.className).toContain("group-hover/project:pr-20");
101
120
  expect(projectContent?.className).toContain(
102
- "group-has-[[data-project-actions]:focus-within]/project:pr-14",
121
+ "group-has-[[data-project-actions]:focus-within]/project:pr-20",
122
+ );
123
+ expect(projectLink.querySelector("span")?.className).not.toContain(
124
+ "uppercase",
125
+ );
126
+ expect(projectLink.querySelector("span")?.className).toContain(
127
+ "text-[13px]",
103
128
  );
104
- expect(projectLink.querySelector("span")?.className).not.toContain("uppercase");
105
- expect(projectLink.querySelector("span")?.className).toContain("text-[13px]");
106
129
  expect(
107
130
  header.compareDocumentPosition(projectLink) &
108
131
  Node.DOCUMENT_POSITION_FOLLOWING,
@@ -159,7 +182,7 @@ describe("ChatSidebarProjectGroups", () => {
159
182
  expect(within(tooltip).getByText("2")).toBeTruthy();
160
183
  });
161
184
 
162
- it("keeps project creation and pinning in one trailing action cluster", () => {
185
+ it("keeps project creation, pinning, and more actions in one trailing cluster", () => {
163
186
  renderProjectGroups();
164
187
 
165
188
  fireEvent.click(screen.getByLabelText("Pin project"));
@@ -168,6 +191,67 @@ describe("ChatSidebarProjectGroups", () => {
168
191
  "/tmp/analysis-project",
169
192
  );
170
193
  expect(screen.getByLabelText("New Task · analysis-project")).not.toBeNull();
194
+ const pinButton = screen.getByLabelText("Pin project");
195
+ const moreButton = screen.getByLabelText(
196
+ "More actions for analysis-project",
197
+ );
198
+ expect(moreButton.querySelector(".lucide-ellipsis-vertical")).not.toBeNull();
199
+ expect(
200
+ pinButton.compareDocumentPosition(moreButton) &
201
+ Node.DOCUMENT_POSITION_FOLLOWING,
202
+ ).toBeTruthy();
203
+ });
204
+
205
+ it("removes a project from an ordinary more-actions menu after confirmation", async () => {
206
+ const user = userEvent.setup();
207
+ renderProjectGroups(
208
+ false,
209
+ projectGroup,
210
+ "/projects/project-analysis/overview",
211
+ );
212
+
213
+ await user.click(
214
+ screen.getByLabelText("More actions for analysis-project"),
215
+ );
216
+ const removeButton = await screen.findByRole("button", {
217
+ name: "Remove from project list",
218
+ });
219
+ expect(removeButton.className).not.toContain("text-destructive");
220
+ await user.click(removeButton);
221
+
222
+ await vi.waitFor(() =>
223
+ expect(mocks.confirm).toHaveBeenCalledWith(
224
+ expect.objectContaining({
225
+ description: expect.stringMatching(/local folder|本地目录/),
226
+ variant: "default",
227
+ }),
228
+ ),
229
+ );
230
+ await vi.waitFor(() =>
231
+ expect(mocks.removeProject).toHaveBeenCalledWith("project-analysis"),
232
+ );
233
+ await vi.waitFor(() =>
234
+ expect(screen.getByTestId("location").textContent).toBe("/projects"),
235
+ );
236
+ });
237
+
238
+ it("keeps the project when removal is cancelled from the menu", async () => {
239
+ mocks.confirm.mockResolvedValue(false);
240
+ const user = userEvent.setup();
241
+ renderProjectGroups();
242
+
243
+ await user.click(
244
+ screen.getByLabelText("More actions for analysis-project"),
245
+ );
246
+ await user.click(
247
+ await screen.findByRole("button", {
248
+ name: "Remove from project list",
249
+ }),
250
+ );
251
+
252
+ await vi.waitFor(() => expect(mocks.confirm).toHaveBeenCalledOnce());
253
+ expect(mocks.removeProject).not.toHaveBeenCalled();
254
+ expect(screen.getByRole("link", { name: "analysis-project" })).toBeTruthy();
171
255
  });
172
256
 
173
257
  it("uses the same pin control to show and clear the project pin state", () => {
@@ -1,11 +1,13 @@
1
1
  import { useMemo, useState, type ReactNode } from "react";
2
- import { Link, useLocation } from "react-router-dom";
2
+ import { Link, useLocation, useNavigate } from "react-router-dom";
3
3
  import {
4
4
  AlarmClock,
5
5
  ChevronDown,
6
6
  ChevronRight,
7
7
  Folder,
8
+ FolderMinus,
8
9
  MessageSquareText,
10
+ MoreVertical,
9
11
  Pin,
10
12
  Plus,
11
13
  } from "lucide-react";
@@ -22,6 +24,9 @@ import { t } from "@/shared/lib/i18n";
22
24
  import { ChatSidebarContextCard } from "@/features/chat/features/session/components/chat-sidebar-context-card";
23
25
  import { SessionRunBadge } from "@/features/chat/features/session/components/session-run-badge";
24
26
  import { cn } from "@/shared/lib/utils";
27
+ import { useRemoveProject } from "@/shared/hooks/use-projects";
28
+ import { useConfirmDialog } from "@/shared/hooks/use-confirm-dialog";
29
+ import { ChatSessionHeaderMenuItem } from "@/features/chat/features/session/components/session-header/chat-session-header-menu-item";
25
30
 
26
31
  export type { ChatSidebarProjectGroup };
27
32
 
@@ -57,10 +62,16 @@ export function ChatSidebarProjectGroups(props: ChatSidebarProjectGroupsProps) {
57
62
  } = props;
58
63
  const presenter = usePresenter();
59
64
  const location = useLocation();
65
+ const navigate = useNavigate();
66
+ const projectRemoval = useRemoveProject();
67
+ const { confirm, ConfirmDialog } = useConfirmDialog();
60
68
  const collapsedProjectRoots = useChatSessionListStore(
61
69
  (state) => state.snapshot.collapsedProjectRoots,
62
70
  );
63
71
  const [openProjectRoot, setOpenProjectRoot] = useState<string | null>(null);
72
+ const [openProjectMenuRoot, setOpenProjectMenuRoot] = useState<string | null>(
73
+ null,
74
+ );
64
75
  const preferredSessionType = useMemo(
65
76
  () =>
66
77
  resolveProjectGroupDefaultSessionType(
@@ -70,6 +81,25 @@ export function ChatSidebarProjectGroups(props: ChatSidebarProjectGroupsProps) {
70
81
  [defaultSessionType, sessionTypeOptions],
71
82
  );
72
83
  const supportsSessionTypeChoice = sessionTypeOptions.length > 1;
84
+ const removeProject = async (group: ChatSidebarProjectGroup) => {
85
+ if (!group.projectId) return;
86
+ setOpenProjectMenuRoot(null);
87
+ const confirmed = await confirm({
88
+ title: `${t("projectsRemoveConfirmTitle")} “${group.projectName}”`,
89
+ description: t("projectsRemoveConfirmDescription"),
90
+ confirmLabel: t("projectsRemoveConfirmAction"),
91
+ variant: "default",
92
+ });
93
+ if (!confirmed) return;
94
+ try {
95
+ await projectRemoval.mutateAsync(group.projectId);
96
+ if (location.pathname.startsWith(`/projects/${group.projectId}/`)) {
97
+ navigate("/projects");
98
+ }
99
+ } catch {
100
+ // The mutation owns the visible error and the project stays available.
101
+ }
102
+ };
73
103
 
74
104
  return (
75
105
  <div className="space-y-0.5">
@@ -116,7 +146,7 @@ export function ChatSidebarProjectGroups(props: ChatSidebarProjectGroupsProps) {
116
146
  >
117
147
  <div
118
148
  className={cn(
119
- "flex h-full w-full min-w-0 items-center group-hover/project:pr-14 group-has-[[data-project-actions]:focus-within]/project:pr-14",
149
+ "flex h-full w-full min-w-0 items-center group-hover/project:pr-20 group-has-[[data-project-actions]:focus-within]/project:pr-20",
120
150
  hasRunningSession && "pr-7",
121
151
  )}
122
152
  >
@@ -243,6 +273,37 @@ export function ChatSidebarProjectGroups(props: ChatSidebarProjectGroupsProps) {
243
273
  )
244
274
  }
245
275
  />
276
+ {group.projectId ? (
277
+ <Popover
278
+ open={openProjectMenuRoot === group.projectRoot}
279
+ onOpenChange={(nextOpen) =>
280
+ setOpenProjectMenuRoot(
281
+ nextOpen ? group.projectRoot : null,
282
+ )
283
+ }
284
+ >
285
+ <PopoverTrigger asChild>
286
+ <IconActionButton
287
+ icon={<MoreVertical className="h-3.5 w-3.5" />}
288
+ label={t("projectsMoreActions").replace(
289
+ "{name}",
290
+ group.projectName,
291
+ )}
292
+ tooltip={false}
293
+ disabled={projectRemoval.isPending}
294
+ onClick={(event) => event.stopPropagation()}
295
+ />
296
+ </PopoverTrigger>
297
+ <ChatPopoverContent align="end" className="w-56 p-2">
298
+ <ChatSessionHeaderMenuItem
299
+ icon={FolderMinus}
300
+ label={t("projectsRemoveFromList")}
301
+ disabled={projectRemoval.isPending}
302
+ onClick={() => void removeProject(group)}
303
+ />
304
+ </ChatPopoverContent>
305
+ </Popover>
306
+ ) : null}
246
307
  </div>
247
308
  </div>
248
309
  {isCollapsed ? null : (
@@ -253,6 +314,7 @@ export function ChatSidebarProjectGroups(props: ChatSidebarProjectGroupsProps) {
253
314
  </div>
254
315
  );
255
316
  })}
317
+ <ConfirmDialog key="project-remove-confirm-dialog" />
256
318
  </div>
257
319
  );
258
320
  }
@@ -10,7 +10,7 @@ describe("groupSessionsByProject", () => {
10
10
  template: "knowledge-base",
11
11
  createdAt: "2026-07-15T00:00:00.000Z",
12
12
  updatedAt: "2026-07-15T00:00:00.000Z",
13
- }]);
13
+ },]);
14
14
 
15
15
  expect(groups).toEqual([{
16
16
  projectId: "project-knowledge",
@@ -19,6 +19,31 @@ describe("groupSessionsByProject", () => {
19
19
  items: [],
20
20
  latestUpdatedAt: new Date("2026-07-15T00:00:00.000Z").getTime(),
21
21
  isPinned: false,
22
- }]);
22
+ },
23
+ ]);
24
+ });
25
+
26
+ it("keeps sessions for removed projects out of the project-first view", () => {
27
+ const groups = groupSessionsByProject(
28
+ [
29
+ {
30
+ session: {
31
+ key: "session:removed-project",
32
+ projectRoot: "/tmp/removed",
33
+ projectName: "Removed",
34
+ createdAt: "2026-07-15T00:00:00.000Z",
35
+ updatedAt: "2026-07-15T00:00:00.000Z",
36
+ sessionType: "native",
37
+ sessionTypeMutable: false,
38
+ messageCount: 1,
39
+ },
40
+ },
41
+ ],
42
+ new Set(),
43
+ new Set(),
44
+ [],
45
+ );
46
+
47
+ expect(groups).toEqual([]);
23
48
  });
24
49
  });
@@ -1,6 +1,5 @@
1
1
  import type { ProjectView, SessionEntryView } from "@/shared/lib/api";
2
2
  import type { NcpSessionListItemView } from "@/features/chat/features/ncp/hooks/use-ncp-session-list-view";
3
- import { getSessionProjectName } from "@/shared/lib/session-project";
4
3
  import { t } from "@/shared/lib/i18n";
5
4
 
6
5
  export type ChatSidebarDateGroup = {
@@ -127,16 +126,6 @@ export function groupSessionsByProject(
127
126
  );
128
127
  continue;
129
128
  }
130
- grouped.set(projectRoot, {
131
- projectRoot,
132
- projectName:
133
- item.session.projectName?.trim() ||
134
- getSessionProjectName(projectRoot) ||
135
- projectRoot,
136
- items: [item],
137
- latestUpdatedAt: updatedAt,
138
- isPinned: pinnedProjectRoots.has(projectRoot),
139
- });
140
129
  }
141
130
 
142
131
  return [...grouped.values()]
@@ -124,4 +124,22 @@ describe('PanelAppListItem', () => {
124
124
  preferences: { mainSidebar: false },
125
125
  });
126
126
  });
127
+
128
+ it('offers the standalone app link from the more-actions menu', async () => {
129
+ const user = userEvent.setup();
130
+ render(
131
+ <PanelAppListItem
132
+ deletePending={false}
133
+ entry={baseEntry}
134
+ favoritePending={false}
135
+ onDelete={vi.fn()}
136
+ onOpen={vi.fn()}
137
+ onToggleFavorite={vi.fn()}
138
+ />,
139
+ );
140
+
141
+ await user.click(screen.getByRole('button', { name: 'More panel app actions' }));
142
+ const link = screen.getByRole('link', { name: 'Open in New Tab' });
143
+ expect(link.getAttribute('href')).toBe('/apps/panel/demo/standalone');
144
+ });
127
145
  });
@@ -0,0 +1,57 @@
1
+ import { render, screen } from "@testing-library/react";
2
+ import { afterEach, describe, expect, it, vi } from "vitest";
3
+ import { PanelAppOpenStandaloneMenuItem } from "@/features/panel-apps/components/panel-app-open-standalone-menu-item";
4
+ import type { PanelAppEntryView } from "@/shared/lib/api";
5
+
6
+ const entry: PanelAppEntryView = {
7
+ appId: "publisher.todo board",
8
+ clientDeclared: false,
9
+ clientGranted: false,
10
+ contentPath: "/api/panel-apps/publisher.todo%20board/content",
11
+ createdAt: "2026-09-03T00:00:00.000Z",
12
+ favorite: false,
13
+ fileName: "todo.panel.html",
14
+ id: "todo",
15
+ kind: "single-file",
16
+ mainSidebar: false,
17
+ openCount: 0,
18
+ sizeBytes: 10,
19
+ title: "Todo",
20
+ updatedAt: "2026-09-03T00:00:00.000Z",
21
+ };
22
+
23
+ function setDesktopHost(value: Window["nextclawDesktop"] | undefined) {
24
+ Object.defineProperty(window, "nextclawDesktop", {
25
+ configurable: true,
26
+ value,
27
+ });
28
+ }
29
+
30
+ describe("PanelAppOpenStandaloneMenuItem", () => {
31
+ afterEach(() => {
32
+ setDesktopHost(undefined);
33
+ });
34
+
35
+ it("uses an auxiliary new-tab link that installed PWA navigation cannot capture", () => {
36
+ const onSelect = vi.fn();
37
+ setDesktopHost(undefined);
38
+ render(
39
+ <PanelAppOpenStandaloneMenuItem entry={entry} onSelect={onSelect} />,
40
+ );
41
+
42
+ const link = screen.getByRole("link", { name: "Open in New Tab" });
43
+ expect(link.getAttribute("href")).toBe(
44
+ "/apps/panel/publisher.todo%20board/standalone",
45
+ );
46
+ expect(link.getAttribute("target")).toBe("_blank");
47
+ expect(link.getAttribute("rel")).toBe("opener");
48
+ expect(link.getAttribute("referrerpolicy")).toBe("no-referrer");
49
+ });
50
+
51
+ it("describes the same link as opening in the browser inside Desktop", () => {
52
+ setDesktopHost({ platform: "darwin" } as Window["nextclawDesktop"]);
53
+ render(<PanelAppOpenStandaloneMenuItem entry={entry} />);
54
+
55
+ expect(screen.getByRole("link", { name: "Open in Browser" })).toBeTruthy();
56
+ });
57
+ });
@@ -82,4 +82,19 @@ describe('PanelAppToolbar', () => {
82
82
  preferences: { mainSidebar: false },
83
83
  });
84
84
  });
85
+
86
+ it('offers the standalone app link from the toolbar menu', async () => {
87
+ const user = userEvent.setup();
88
+ render(
89
+ <PanelAppToolbar
90
+ appTitle="墨爪助手"
91
+ entry={entry}
92
+ onRefresh={vi.fn()}
93
+ />,
94
+ );
95
+
96
+ await user.click(screen.getByRole('button', { name: 'More panel app actions' }));
97
+ const link = screen.getByRole('link', { name: 'Open in New Tab' });
98
+ expect(link.getAttribute('href')).toBe('/apps/panel/ink-assistant/standalone');
99
+ });
85
100
  });
@@ -3,6 +3,7 @@ import { MoreVertical, Star, Trash2, type LucideIcon } from 'lucide-react';
3
3
  import type { PanelAppEntryView } from '@/shared/lib/api';
4
4
  import { PanelAppIcon } from '@/features/panel-apps/components/panel-app-icon';
5
5
  import { PanelAppMainSidebarMenuItem } from '@/features/panel-apps/components/panel-app-main-sidebar-menu-item';
6
+ import { PanelAppOpenStandaloneMenuItem } from '@/features/panel-apps/components/panel-app-open-standalone-menu-item';
6
7
  import { ConfirmDialog } from '@/shared/components/ui/confirm-dialog';
7
8
  import { Popover, PopoverContent, PopoverTrigger } from '@/shared/components/ui/popover';
8
9
  import { getLanguage, getLocale, t } from '@/shared/lib/i18n';
@@ -76,6 +77,10 @@ export function PanelAppListItem({
76
77
  </button>
77
78
  </PopoverTrigger>
78
79
  <PopoverContent align="end" className="w-48 rounded-xl p-1.5">
80
+ <PanelAppOpenStandaloneMenuItem
81
+ entry={entry}
82
+ onSelect={() => setIsMenuOpen(false)}
83
+ />
79
84
  <PanelAppMainSidebarMenuItem
80
85
  entry={entry}
81
86
  onSelect={() => setIsMenuOpen(false)}
@@ -0,0 +1,32 @@
1
+ import { ExternalLink } from "lucide-react";
2
+ import type { PanelAppEntryView } from "@/shared/lib/api";
3
+ import { t } from "@/shared/lib/i18n";
4
+
5
+ export function PanelAppOpenStandaloneMenuItem({
6
+ entry,
7
+ onSelect,
8
+ }: {
9
+ entry: PanelAppEntryView;
10
+ onSelect?: () => void;
11
+ }) {
12
+ const label = typeof window !== "undefined" && window.nextclawDesktop
13
+ ? t("panelAppsOpenInBrowser")
14
+ : t("panelAppsOpenInNewTab");
15
+ const href = `/apps/panel/${encodeURIComponent(entry.appId)}/standalone`;
16
+
17
+ return (
18
+ <a
19
+ href={href}
20
+ target="_blank"
21
+ // Chrome captures non-auxiliary in-scope links into an installed PWA.
22
+ // `opener` keeps this trusted same-origin target auxiliary, so the current browser owns the new tab.
23
+ rel="opener"
24
+ referrerPolicy="no-referrer"
25
+ className="flex w-full items-center gap-2 rounded-lg px-2.5 py-2 text-left text-sm text-muted-foreground transition-colors hover:bg-[var(--interaction-hover)] hover:text-accent-foreground"
26
+ onClick={onSelect}
27
+ >
28
+ <ExternalLink className="h-4 w-4 shrink-0" />
29
+ <span>{label}</span>
30
+ </a>
31
+ );
32
+ }