@nextclaw/ui 0.24.1 → 0.24.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 (333) hide show
  1. package/CHANGELOG.md +12 -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-BxMlLXYH.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-Utt-xzrT.js +13 -0
  7. package/dist/assets/app-data-DYXDL6X2.js +1 -0
  8. package/dist/assets/app-presenter-provider-CLsFiXvV.js +109 -0
  9. package/dist/assets/app-presenter-provider-D3BuuOyZ.css +1 -0
  10. package/dist/assets/appearance-settings-page-BfYznAK5.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-DDW_3XpM.js} +1 -1
  16. package/dist/assets/boxes-DwJeuK_h.js +1 -0
  17. package/dist/assets/{c4Diagram-LMCZKHZV-D9hFNAGv.js → c4Diagram-LMCZKHZV-CpapRftp.js} +1 -1
  18. package/dist/assets/card-IevY2nm-.js +1 -0
  19. package/dist/assets/channel-CBrWRr4N.js +1 -0
  20. package/dist/assets/channels-list-page-DitnqbMR.js +8 -0
  21. package/dist/assets/chevron-down-An7Km-t4.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-CDXqELuo.js +1 -0
  61. package/dist/assets/circle-dashed-CapXEpiV.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-B0krol7A.js +1 -0
  65. package/dist/assets/config-split-page-BtnkqWVp.js +1 -0
  66. package/dist/assets/confirm-dialog-DFt4jpN7.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-BTHK0Ot9.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-CCf14WId.js +1 -0
  74. package/dist/assets/desktop-update-config-DVoVgfFi.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-BFN_1DBH.js +5 -0
  81. package/dist/assets/dist-Bc9hOVNN.js +1 -0
  82. package/dist/assets/dist-CEhIDAnr.js +1 -0
  83. package/dist/assets/{dist--ta5rX12.js → dist-Cnhwure9.js} +1 -1
  84. package/dist/assets/{dist-aNN3iQxV.js → dist-hkVPP1th.js} +1 -1
  85. package/dist/assets/doc-browser-BNUxmwD6.js +1 -0
  86. package/dist/assets/doc-browser-context-C4yQrKrH.js +1 -0
  87. package/dist/assets/doc-browser-w4eGZXLu.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-BRI6ROeu.js +1 -0
  91. package/dist/assets/{erDiagram-Q63AITRT-ZLlx8xfs.js → erDiagram-Q63AITRT-DR7vWYHM.js} +1 -1
  92. package/dist/assets/es2015-MOCbM0Eu.js +41 -0
  93. package/dist/assets/eventmodeling-45OFAUF4-CXZNG56U.js +1 -0
  94. package/dist/assets/extensions-DxUBMtHW.js +1 -0
  95. package/dist/assets/eye-cnynUDGt.js +1 -0
  96. package/dist/assets/flowDiagram-23GEKE2U-2065oyKr.js +1 -0
  97. package/dist/assets/form-actions-gQ0JEUwB.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-B9yXA8KO.js +2 -0
  102. package/dist/assets/i18n-provider-BYBJua91.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-rWr5Yo0E.js +1 -0
  112. package/dist/assets/login-page-CfMSkOsg.js +1 -0
  113. package/dist/assets/login-page-CqrHhzaN.js +1 -0
  114. package/dist/assets/logo-badge-Cr03YceJ.js +1 -0
  115. package/dist/assets/main-8cVkMfgE.css +1 -0
  116. package/dist/assets/main-Bys58GxY.js +2 -0
  117. package/dist/assets/main-i18n-catalog.config-BSq9yiUQ.js +1 -0
  118. package/dist/assets/{map-BKnQyvd0.js → map-WMnNq9UV.js} +1 -1
  119. package/dist/assets/mcp-marketplace-page-CifiVgFD.js +10 -0
  120. package/dist/assets/mcp-marketplace-page-DX5VE3mU.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-C6jVNbSj.js +1 -0
  125. package/dist/assets/{navigation-link-C_6oj3zz.js → navigation-link-kbCLRSr_.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-C2St67SP.js +1 -0
  130. package/dist/assets/panelStandalone-DMBnYyTw.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-BHnKwuPq.js +1 -0
  136. package/dist/assets/popover-XVDgjxcl.js +1 -0
  137. package/dist/assets/privacy-settings-page-BN27MBVA.js +1 -0
  138. package/dist/assets/projects-DV3UuJMS.js +1 -0
  139. package/dist/assets/provider-models-5PGHb6mP.js +1 -0
  140. package/dist/assets/provider-scoped-model-input-BGZMCThn.js +1 -0
  141. package/dist/assets/providers-config-page-D5aBwbOD.js +1 -0
  142. package/dist/assets/puzzle-D5Dhacmb.js +1 -0
  143. package/dist/assets/pwa-ClQK56Qe.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-Bw5BY7vp.js} +1 -1
  152. package/dist/assets/remote-Bl005fzu.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-BksCLAeE.js} +1 -1
  155. package/dist/assets/runtime-config-page-CmktQcMW.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-CVi8VWzb.js} +1 -1
  158. package/dist/assets/{search-B6tB4fzB.js → search-DN-f7IIC.js} +1 -1
  159. package/dist/assets/search-config-page-BmuZTVxs.js +1 -0
  160. package/dist/assets/secrets-config-page-NPHpg9sb.js +3 -0
  161. package/dist/assets/security-config-Djptuyf2.js +1 -0
  162. package/dist/assets/select-CmDTqk2C.js +1 -0
  163. package/dist/assets/{sequenceDiagram-DBY2YBRQ-BtRbPQtL.js → sequenceDiagram-DBY2YBRQ-DwHC4mbX.js} +1 -1
  164. package/dist/assets/service-apps-panel-BYGJ9_pO.js +1 -0
  165. package/dist/assets/settings-2-B62YtZon.js +1 -0
  166. package/dist/assets/{sizeCapture-X5ZJPWSS-CMYvM3SL.js → sizeCapture-X5ZJPWSS-oOMY0Fag.js} +1 -1
  167. package/dist/assets/skeleton-Eh64l4T2.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-Bk_mG7ql.js +1 -0
  174. package/dist/assets/{tag-chip-BZKk0UkK.js → tag-chip-DgqKlI5D.js} +1 -1
  175. package/dist/assets/theme-provider-CeBY2pyu.js +1 -0
  176. package/dist/assets/{timeline-definition-FHXFAJF6-Bicewlzo.js → timeline-definition-FHXFAJF6-BEonB5Np.js} +1 -1
  177. package/dist/assets/tooltip-CgGbBdYf.js +1 -0
  178. package/dist/assets/trash-2-CVOev8yc.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-b2DN67gs.js +1 -0
  182. package/dist/assets/use-desktop-capabilities-B3tLU1yl.js +1 -0
  183. package/dist/assets/use-panel-app-scroll-restoration-BekzoX6z.js +1 -0
  184. package/dist/assets/use-viewport-layout-CUA3jQsA.js +1 -0
  185. package/dist/assets/utils-UBHTdG5Q.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-C0iaOsRW.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 +3 -3
  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/session/utils/__tests__/chat-sidebar-session-groups.utils.test.ts +27 -2
  207. package/src/features/chat/features/session/utils/chat-sidebar-session-groups.utils.ts +0 -11
  208. package/src/features/panel-apps/components/__tests__/panel-app-list-item.test.tsx +18 -0
  209. package/src/features/panel-apps/components/__tests__/panel-app-open-standalone-menu-item.test.tsx +56 -0
  210. package/src/features/panel-apps/components/__tests__/panel-app-toolbar.test.tsx +15 -0
  211. package/src/features/panel-apps/components/panel-app-list-item.tsx +5 -0
  212. package/src/features/panel-apps/components/panel-app-open-standalone-menu-item.tsx +29 -0
  213. package/src/features/panel-apps/components/panel-app-runtime-surface.tsx +141 -0
  214. package/src/features/{service-apps/components/service-action-authorization-dialog.tsx → panel-apps/components/panel-app-service-action-authorization-dialog.tsx} +4 -4
  215. package/src/features/panel-apps/components/panel-app-toolbar.tsx +5 -0
  216. package/src/features/panel-apps/hooks/use-panel-app-client-grant.ts +2 -2
  217. package/src/features/panel-apps/hooks/{use-panel-app-main-runtime.ts → use-panel-app-runtime.ts} +7 -5
  218. package/src/features/panel-apps/hooks/use-panel-apps.ts +14 -0
  219. package/src/features/panel-apps/index.ts +5 -0
  220. package/src/features/panel-apps/managers/__tests__/panel-app-bridge.manager.test.ts +2 -2
  221. package/src/features/panel-apps/managers/panel-app-bridge.manager.ts +2 -2
  222. package/src/features/panel-apps/managers/panel-app-service-action-authorization.manager.ts +10 -0
  223. package/src/features/panel-apps/pages/__tests__/panel-app-main-page.test.tsx +12 -3
  224. package/src/features/panel-apps/pages/__tests__/panel-app-standalone-page.test.tsx +63 -0
  225. package/src/features/panel-apps/pages/panel-app-main-page.tsx +9 -121
  226. package/src/features/panel-apps/pages/panel-app-standalone-page.tsx +29 -0
  227. package/src/features/panel-apps/presenters/panel-app-host.presenter.ts +9 -0
  228. package/src/features/panel-apps/providers/panel-app-host.provider.tsx +45 -0
  229. package/src/features/panel-apps/routes/__tests__/panel-app-standalone.route.test.tsx +50 -0
  230. package/src/features/panel-apps/routes/panel-app-standalone.route.tsx +94 -0
  231. package/src/features/{service-apps/stores/service-action-authorization.store.ts → panel-apps/stores/panel-app-service-action-authorization.store.ts} +6 -6
  232. package/src/features/projects/components/project-overview.test.tsx +70 -0
  233. package/src/features/projects/components/project-overview.tsx +114 -56
  234. package/src/features/projects/components/work/project-work-item-drawer.tsx +8 -10
  235. package/src/features/projects/components/work/project-work-items.test.tsx +168 -4
  236. package/src/features/projects/components/work/project-work-items.tsx +277 -49
  237. package/src/features/projects/components/work/project-work-state-settings.tsx +7 -3
  238. package/src/features/projects/hooks/use-project-work.test.tsx +108 -6
  239. package/src/features/projects/hooks/use-project-work.ts +82 -9
  240. package/src/features/projects/pages/project-home-page.tsx +45 -6
  241. package/src/features/projects/pages/projects-page.test.tsx +56 -0
  242. package/src/features/projects/utils/project-artifact-view.utils.test.ts +30 -18
  243. package/src/features/projects/utils/project-artifact-view.utils.ts +29 -12
  244. package/src/features/projects/utils/project-work-state-label.utils.test.ts +28 -0
  245. package/src/features/projects/utils/project-work-state-label.utils.ts +20 -0
  246. package/src/features/service-apps/index.ts +3 -3
  247. package/src/main.tsx +3 -0
  248. package/src/shared/hooks/use-auth-status.ts +49 -0
  249. package/src/shared/hooks/use-projects.ts +15 -1
  250. package/src/shared/lib/api/projects/project.utils.ts +4 -0
  251. package/src/shared/lib/i18n/configs/main-i18n-catalog.config.ts +78 -0
  252. package/src/shared/lib/i18n/configs/panel-app-i18n-catalog.config.ts +12 -0
  253. package/src/shared/lib/i18n/index.ts +30 -82
  254. package/src/shared/lib/i18n/locales/en-US/doc-browser.json +0 -55
  255. package/src/shared/lib/i18n/locales/en-US/panel-app.json +59 -0
  256. package/src/shared/lib/i18n/locales/en-US/projects.json +16 -0
  257. package/src/shared/lib/i18n/locales/zh-CN/doc-browser.json +0 -55
  258. package/src/shared/lib/i18n/locales/zh-CN/panel-app.json +59 -0
  259. package/src/shared/lib/i18n/locales/zh-CN/projects.json +16 -0
  260. package/vite.config.ts +36 -1
  261. package/dist/assets/api-BqVIq0lF.js +0 -13
  262. package/dist/assets/appearance-settings-page-CTWn1eAT.js +0 -1
  263. package/dist/assets/architecture-TIHT7OUA-DO2x53FK.js +0 -1
  264. package/dist/assets/channel-nBvMaVyP.js +0 -1
  265. package/dist/assets/channels-list-page-Bzn-pzyJ.js +0 -8
  266. package/dist/assets/chunk-2Q5K7J3B-CJgqFDS4.js +0 -1
  267. package/dist/assets/chunk-VAUOI2AC-BGvh3yIx.js +0 -1
  268. package/dist/assets/chunk-XXDRQBXY-DaBxG5gs.js +0 -1
  269. package/dist/assets/classDiagram-OUVF2IWQ-P6N03G2R.js +0 -1
  270. package/dist/assets/classDiagram-v2-EOCWNBFH-B1_uLvMv.js +0 -1
  271. package/dist/assets/config-split-page-CnRbXXlh.js +0 -1
  272. package/dist/assets/confirm-dialog-B_Hbcra2.js +0 -5
  273. package/dist/assets/cynefin-VYW2F7L2-B-H8Qp-n.js +0 -1
  274. package/dist/assets/desktop-capabilities-page-C2ayYQTr.js +0 -1
  275. package/dist/assets/desktop-update-config-C13czpyL.js +0 -1
  276. package/dist/assets/dist-BsF3hfOi.js +0 -1
  277. package/dist/assets/doc-browser-D8dIOo9E.js +0 -1
  278. package/dist/assets/doc-browser-DcuOOtpP.js +0 -1
  279. package/dist/assets/doc-browser-context-Dhsit2Ma.js +0 -1
  280. package/dist/assets/eventmodeling-45OFAUF4-YVtShyFA.js +0 -1
  281. package/dist/assets/extensions-CD6xvKF_.js +0 -1
  282. package/dist/assets/flowDiagram-23GEKE2U-BeKXabVO.js +0 -1
  283. package/dist/assets/form-actions-CC4Wz_lL.js +0 -1
  284. package/dist/assets/gitGraph-TEB2WS4Q-jTSiBj-p.js +0 -1
  285. package/dist/assets/index-BTsbdtv6.js +0 -109
  286. package/dist/assets/index-Bcfy2kTh.css +0 -1
  287. package/dist/assets/info-DKCQHKI2-BIporNWb.js +0 -1
  288. package/dist/assets/loader-circle-B6aFbB-8.js +0 -1
  289. package/dist/assets/mcp-marketplace-page-BtUAypA7.js +0 -1
  290. package/dist/assets/mcp-marketplace-page-r2MmD9xO.js +0 -10
  291. package/dist/assets/model-config-page-DboxoT-I.js +0 -1
  292. package/dist/assets/packet-7NZHBO7P-CuFMADV0.js +0 -1
  293. package/dist/assets/pie-RZYD4A2V-XUeIXhM9.js +0 -1
  294. package/dist/assets/plus-DRsq9Kvs.js +0 -1
  295. package/dist/assets/privacy-settings-page-C2sDBqhb.js +0 -1
  296. package/dist/assets/projects-BcPcx-uD.js +0 -1
  297. package/dist/assets/providers-config-page-CmUFl9RF.js +0 -1
  298. package/dist/assets/radar-I7S5WNFK-D-NNGh1k.js +0 -1
  299. package/dist/assets/railroad-3IZDKUUU-C67_uDaM.js +0 -1
  300. package/dist/assets/railroad-abnf-AHOZXSZD-BZ8Yflnd.js +0 -1
  301. package/dist/assets/railroad-ebnf-EBAXGLYW-B7zO7RoK.js +0 -1
  302. package/dist/assets/railroad-peg-LSFZ7HO6-R63PTgLz.js +0 -1
  303. package/dist/assets/react-BjO9I4WZ.js +0 -8
  304. package/dist/assets/remote-Ci6x91ES.js +0 -1
  305. package/dist/assets/runtime-config-page-cQKNeAjL.js +0 -1
  306. package/dist/assets/search-config-page-C8lOIkxn.js +0 -1
  307. package/dist/assets/secrets-config-page-8clzAag2.js +0 -3
  308. package/dist/assets/security-config-DPbM6IVT.js +0 -1
  309. package/dist/assets/select-CO9S_C2C.js +0 -41
  310. package/dist/assets/skeleton-IHILNhIW.js +0 -1
  311. package/dist/assets/stateDiagram-v2-6OUMAXLB-BE-HqxOK.js +0 -1
  312. package/dist/assets/swimlanesDiagram-G3AALYLV-Dr1H9LJH.js +0 -8
  313. package/dist/assets/treeView-QDETBFTQ-npJ5dbqS.js +0 -1
  314. package/dist/assets/treemap-6X3UGDF4-pYrBx6oa.js +0 -1
  315. package/dist/assets/wardley-OPB4EBWU-CmSRKLR8.js +0 -1
  316. package/dist/assets/x-BuvHIb1d.js +0 -1
  317. package/src/features/service-apps/managers/service-action-authorization.manager.ts +0 -10
  318. /package/dist/assets/{array-BQVzeGRt.js → array-BHDgItFN.js} +0 -0
  319. /package/dist/assets/{chunk-DseTPa7n.js → chunk-DF37yw5_.js} +0 -0
  320. /package/dist/assets/{chunk-KEIR6QF5-f9psyCXK.js → chunk-KEIR6QF5-DstBw0Hk.js} +0 -0
  321. /package/dist/assets/{chunk-Y2CYZVJY-D1mAU_Po.js → chunk-Y2CYZVJY-CA3ymQuH.js} +0 -0
  322. /package/dist/assets/{config-hints-nV5HPOvq.js → config-hints-CBPHrfYk.js} +0 -0
  323. /package/dist/assets/{cytoscape.esm-0uDtdD0l.js → cytoscape.esm-C7xr90o_.js} +0 -0
  324. /package/dist/assets/{defaultLocale-DSWXGgt0.js → defaultLocale-BG1oDVag.js} +0 -0
  325. /package/dist/assets/{graphlib-CQ6QV4yP.js → graphlib-CNMGfuTb.js} +0 -0
  326. /package/dist/assets/{host-capabilities-Tk038i04.js → host-capabilities-B107nwS3.js} +0 -0
  327. /package/dist/assets/{init-Y6h9jGtZ.js → init-wQIZuRSH.js} +0 -0
  328. /package/dist/assets/{katex-ARhY9PS1.js → katex-J8JuvgNl.js} +0 -0
  329. /package/dist/assets/{middleware-DXsbf9xI.js → middleware-0o3JF3tS.js} +0 -0
  330. /package/dist/assets/{path-10BjdyLh.js → path-N-C4MRP0.js} +0 -0
  331. /package/dist/assets/{preload-helper-D4M6sveU.js → preload-helper-ca-nBW7U.js} +0 -0
  332. /package/dist/assets/{rough.esm-DxgC_14r.js → rough.esm-BeWA068d.js} +0 -0
  333. /package/dist/assets/{xlsx-DrDuDNAa.js → xlsx-CwUXNtD3.js} +0 -0
@@ -1,9 +1,15 @@
1
1
  import { useEffect } from "react";
2
- import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
2
+ import {
3
+ useInfiniteQuery,
4
+ useMutation,
5
+ useQuery,
6
+ useQueryClient,
7
+ } from "@tanstack/react-query";
3
8
  import type {
4
9
  CreateProjectWorkItemInput,
5
10
  CreateProjectWorkStateInput,
6
11
  ProjectWorkState,
12
+ ProjectWorkListInput,
7
13
  UpdateProjectWorkItemInput,
8
14
  UpdateProjectWorkStateInput,
9
15
  } from "@nextclaw/client-sdk";
@@ -14,6 +20,10 @@ export const projectWorkQueryKey = (projectId: string) =>
14
20
  ["project-work", projectId] as const;
15
21
  export const projectWorkSummaryQueryKey = (projectId: string) =>
16
22
  ["project-work-summary", projectId] as const;
23
+ export const projectWorkStatesQueryKey = (projectId: string) =>
24
+ ["project-work-states", projectId] as const;
25
+ export const projectWorkArtifactsQueryKey = (projectId: string) =>
26
+ ["project-work-artifacts", projectId] as const;
17
27
  export const projectWorkItemQueryKey = (
18
28
  projectId: string,
19
29
  workItemId: string,
@@ -25,19 +35,22 @@ export const projectWorkActivityQueryKey = (
25
35
 
26
36
  export function useProjectWork(
27
37
  projectId: string | null,
28
- includeDeleted = false,
38
+ input: Omit<ProjectWorkListInput, "cursor"> = {},
29
39
  ) {
30
- useProjectWorkEvents(projectId);
31
- return useQuery({
32
- queryKey: [...projectWorkQueryKey(projectId ?? ""), includeDeleted],
40
+ return useInfiniteQuery({
41
+ queryKey: [...projectWorkQueryKey(projectId ?? ""), input],
33
42
  enabled: Boolean(projectId),
34
- queryFn: async () =>
35
- await nextclawClient.projects.listWork(projectId!, includeDeleted),
43
+ initialPageParam: null as string | null,
44
+ queryFn: async ({ pageParam }) =>
45
+ await nextclawClient.projects.listWork(projectId!, {
46
+ ...input,
47
+ ...(pageParam ? { cursor: pageParam } : {}),
48
+ }),
49
+ getNextPageParam: (lastPage) => lastPage.nextCursor,
36
50
  });
37
51
  }
38
52
 
39
53
  export function useProjectWorkSummary(projectId: string | null) {
40
- useProjectWorkEvents(projectId);
41
54
  return useQuery({
42
55
  queryKey: projectWorkSummaryQueryKey(projectId ?? ""),
43
56
  enabled: Boolean(projectId),
@@ -46,6 +59,26 @@ export function useProjectWorkSummary(projectId: string | null) {
46
59
  });
47
60
  }
48
61
 
62
+ export function useProjectWorkStates(projectId: string | null) {
63
+ return useQuery({
64
+ queryKey: projectWorkStatesQueryKey(projectId ?? ""),
65
+ enabled: Boolean(projectId),
66
+ queryFn: async () =>
67
+ await nextclawClient.projects.listWorkStates(projectId!),
68
+ });
69
+ }
70
+
71
+ export function useProjectRecentArtifacts(projectId: string | null, limit = 5) {
72
+ return useQuery({
73
+ queryKey: [...projectWorkArtifactsQueryKey(projectId ?? ""), limit],
74
+ enabled: Boolean(projectId),
75
+ queryFn: async () =>
76
+ await nextclawClient.projects.listRecentWorkArtifacts(projectId!, {
77
+ limit,
78
+ }),
79
+ });
80
+ }
81
+
49
82
  export function useProjectWorkItem(
50
83
  projectId: string | null,
51
84
  workItemId: string | null,
@@ -84,6 +117,12 @@ export function useProjectWorkActions(projectId: string) {
84
117
  queryClient.invalidateQueries({
85
118
  queryKey: projectWorkSummaryQueryKey(projectId),
86
119
  }),
120
+ queryClient.invalidateQueries({
121
+ queryKey: projectWorkStatesQueryKey(projectId),
122
+ }),
123
+ queryClient.invalidateQueries({
124
+ queryKey: projectWorkArtifactsQueryKey(projectId),
125
+ }),
87
126
  ...(workItemId
88
127
  ? [
89
128
  queryClient.invalidateQueries({
@@ -181,7 +220,7 @@ export function useProjectWorkActions(projectId: string) {
181
220
  };
182
221
  }
183
222
 
184
- function useProjectWorkEvents(projectId: string | null): void {
223
+ export function useProjectWorkEvents(projectId: string | null): void {
185
224
  const queryClient = useQueryClient();
186
225
  useEffect(() => {
187
226
  if (!projectId) return;
@@ -193,6 +232,12 @@ function useProjectWorkEvents(projectId: string | null): void {
193
232
  void queryClient.invalidateQueries({
194
233
  queryKey: projectWorkSummaryQueryKey(projectId),
195
234
  });
235
+ void queryClient.invalidateQueries({
236
+ queryKey: projectWorkStatesQueryKey(projectId),
237
+ });
238
+ void queryClient.invalidateQueries({
239
+ queryKey: projectWorkArtifactsQueryKey(projectId),
240
+ });
196
241
  if (event.workItemId) {
197
242
  void queryClient.invalidateQueries({
198
243
  queryKey: projectWorkItemQueryKey(projectId, event.workItemId),
@@ -213,3 +258,31 @@ export function sortProjectWorkStates(
213
258
  left.position - right.position || left.name.localeCompare(right.name),
214
259
  );
215
260
  }
261
+
262
+ const LIST_CATEGORY_ORDER: Readonly<
263
+ Record<ProjectWorkState["category"], number>
264
+ > = {
265
+ started: 0,
266
+ unstarted: 1,
267
+ backlog: 2,
268
+ completed: 3,
269
+ canceled: 4,
270
+ };
271
+
272
+ export function sortProjectWorkStatesForList(
273
+ states: ProjectWorkState[],
274
+ ): ProjectWorkState[] {
275
+ return [...states].sort((left, right) => {
276
+ const categoryOrder =
277
+ LIST_CATEGORY_ORDER[left.category] - LIST_CATEGORY_ORDER[right.category];
278
+ if (categoryOrder !== 0) return categoryOrder;
279
+ if (left.category === "completed" || left.category === "canceled") {
280
+ return (
281
+ left.position - right.position || left.name.localeCompare(right.name)
282
+ );
283
+ }
284
+ return (
285
+ right.position - left.position || left.name.localeCompare(right.name)
286
+ );
287
+ });
288
+ }
@@ -3,9 +3,11 @@ import { useNavigate, useParams } from "react-router-dom";
3
3
  import { useMutation, useQueryClient } from "@tanstack/react-query";
4
4
  import type { ObservedRequest } from "@nextclaw/client-sdk";
5
5
  import { toast } from "sonner";
6
- import { useProjects } from "@/shared/hooks/use-projects";
6
+ import { useProjects, useRemoveProject } from "@/shared/hooks/use-projects";
7
7
  import { useViewportLayout } from "@/app/hooks/use-viewport-layout";
8
8
  import { t } from "@/shared/lib/i18n";
9
+ import { Button } from "@/shared/components/ui/button";
10
+ import { useConfirmDialog } from "@/shared/hooks/use-confirm-dialog";
9
11
  import {
10
12
  Tabs,
11
13
  TabsContent,
@@ -36,6 +38,7 @@ import {
36
38
  sendProjectRequestResponse,
37
39
  type ProjectRequestDecision,
38
40
  } from "@/features/projects/utils/project-request-response.utils";
41
+ import { joinProjectPath } from "@/features/projects/utils/project-artifact-view.utils";
39
42
 
40
43
  const PROJECT_TABS: ProjectHomeTab[] = [
41
44
  "overview",
@@ -55,6 +58,8 @@ export function ProjectsPage() {
55
58
  const queryClient = useQueryClient();
56
59
  const { isMobile } = useViewportLayout();
57
60
  const projects = useProjects();
61
+ const projectRemoval = useRemoveProject();
62
+ const { confirm, ConfirmDialog } = useConfirmDialog();
58
63
  const [selectedWorkItemId, setSelectedWorkItemId] = useState<string | null>(
59
64
  null,
60
65
  );
@@ -100,6 +105,22 @@ export function ProjectsPage() {
100
105
  prompt: request.prompt,
101
106
  });
102
107
  };
108
+ const removeSelectedProject = async () => {
109
+ if (!selectedProject) return;
110
+ const confirmed = await confirm({
111
+ title: `${t("projectsRemoveConfirmTitle")} “${selectedProject.name}”`,
112
+ description: t("projectsRemoveConfirmDescription"),
113
+ confirmLabel: t("projectsRemoveConfirmAction"),
114
+ variant: "destructive",
115
+ });
116
+ if (!confirmed) return;
117
+ try {
118
+ await projectRemoval.mutateAsync(selectedProject.id);
119
+ navigate("/projects");
120
+ } catch {
121
+ // The mutation owns the visible error and the current project stays open.
122
+ }
123
+ };
103
124
 
104
125
  if (projects.isLoading)
105
126
  return (
@@ -138,11 +159,22 @@ export function ProjectsPage() {
138
159
  <>
139
160
  <main className="h-full min-w-0 flex-1 overflow-y-auto p-3 sm:p-4 md:p-6">
140
161
  <div className="mx-auto max-w-6xl space-y-4">
141
- <header className="rounded-2xl border border-border/60 bg-card p-3 sm:p-4">
142
- <h1 className="text-lg font-semibold">{selectedProject.name}</h1>
143
- <p className="mt-1 break-all text-xs text-muted-foreground">
144
- {selectedProject.rootPath}
145
- </p>
162
+ <header className="flex flex-col gap-3 rounded-2xl border border-border/60 bg-card p-3 sm:flex-row sm:items-start sm:justify-between sm:p-4">
163
+ <div className="min-w-0">
164
+ <h1 className="text-lg font-semibold">{selectedProject.name}</h1>
165
+ <p className="mt-1 break-all text-xs text-muted-foreground">
166
+ {selectedProject.rootPath}
167
+ </p>
168
+ </div>
169
+ <Button
170
+ type="button"
171
+ variant="destructive"
172
+ size="sm"
173
+ disabled={projectRemoval.isPending}
174
+ onClick={() => void removeSelectedProject()}
175
+ >
176
+ {t("projectsRemoveFromList")}
177
+ </Button>
146
178
  </header>
147
179
  {snapshot ? (
148
180
  <ProjectRequests
@@ -171,6 +203,12 @@ export function ProjectsPage() {
171
203
  <TabsContent value="overview" className="mt-4">
172
204
  <ProjectOverview
173
205
  projectId={selectedProject.id}
206
+ onOpenArtifact={(path, label) =>
207
+ openProjectFile(
208
+ joinProjectPath(selectedProject.rootPath, path),
209
+ label,
210
+ )
211
+ }
174
212
  onOpenWorkItem={setSelectedWorkItemId}
175
213
  />
176
214
  </TabsContent>
@@ -229,6 +267,7 @@ export function ProjectsPage() {
229
267
  sessionKey={null}
230
268
  projectRoot={selectedProject.rootPath}
231
269
  />
270
+ <ConfirmDialog />
232
271
  </>
233
272
  );
234
273
  }
@@ -5,13 +5,25 @@ import { beforeEach, describe, expect, it, vi } from "vitest";
5
5
  import { ProjectsPage } from "./project-home-page";
6
6
 
7
7
  const mocks = vi.hoisted(() => ({
8
+ confirm: vi.fn(),
8
9
  drawerProps: vi.fn(),
10
+ removeProject: vi.fn(),
9
11
  useProjectObservation: vi.fn(),
10
12
  useProjects: vi.fn(),
11
13
  }));
12
14
 
13
15
  vi.mock("@/shared/hooks/use-projects", () => ({
14
16
  useProjects: mocks.useProjects,
17
+ useRemoveProject: () => ({
18
+ mutateAsync: mocks.removeProject,
19
+ isPending: false,
20
+ }),
21
+ }));
22
+ vi.mock("@/shared/hooks/use-confirm-dialog", () => ({
23
+ useConfirmDialog: () => ({
24
+ confirm: mocks.confirm,
25
+ ConfirmDialog: () => <div data-testid="confirm-dialog" />,
26
+ }),
15
27
  }));
16
28
  vi.mock("@/features/projects/hooks/use-project-observation", () => ({
17
29
  projectObservationQueryKey: (projectId: string) => [
@@ -80,6 +92,10 @@ function renderPage(path: string) {
80
92
  describe("ProjectsPage", () => {
81
93
  beforeEach(() => {
82
94
  mocks.drawerProps.mockReset();
95
+ mocks.confirm.mockReset();
96
+ mocks.confirm.mockResolvedValue(true);
97
+ mocks.removeProject.mockReset();
98
+ mocks.removeProject.mockResolvedValue(undefined);
83
99
  mocks.useProjects.mockReturnValue({
84
100
  data: {
85
101
  projects: [
@@ -134,4 +150,44 @@ describe("ProjectsPage", () => {
134
150
  expect(mocks.useProjectObservation).toHaveBeenCalledWith(null, null, false);
135
151
  expect(screen.getByText(/Choose a project|选择项目/)).toBeTruthy();
136
152
  });
153
+
154
+ it("explains the impact before removing a project and returns to project selection", async () => {
155
+ renderPage("/projects/project-1/overview");
156
+
157
+ fireEvent.click(
158
+ screen.getByRole("button", {
159
+ name: /Remove from project list|从项目列表移除/,
160
+ }),
161
+ );
162
+
163
+ await vi.waitFor(() =>
164
+ expect(mocks.confirm).toHaveBeenCalledWith(
165
+ expect.objectContaining({
166
+ description: expect.stringMatching(/local folder|本地目录/),
167
+ variant: "destructive",
168
+ }),
169
+ ),
170
+ );
171
+ await vi.waitFor(() =>
172
+ expect(mocks.removeProject).toHaveBeenCalledWith("project-1"),
173
+ );
174
+ await vi.waitFor(() =>
175
+ expect(screen.getByText(/Choose a project|选择项目/)).toBeTruthy(),
176
+ );
177
+ });
178
+
179
+ it("keeps the project when removal is cancelled", async () => {
180
+ mocks.confirm.mockResolvedValue(false);
181
+ renderPage("/projects/project-1/overview");
182
+
183
+ fireEvent.click(
184
+ screen.getByRole("button", {
185
+ name: /Remove from project list|从项目列表移除/,
186
+ }),
187
+ );
188
+
189
+ await vi.waitFor(() => expect(mocks.confirm).toHaveBeenCalledOnce());
190
+ expect(mocks.removeProject).not.toHaveBeenCalled();
191
+ expect(screen.getByText("Research")).toBeTruthy();
192
+ });
137
193
  });
@@ -1,6 +1,7 @@
1
1
  import { describe, expect, it } from "vitest";
2
2
  import {
3
3
  formatProjectRelativeTime,
4
+ joinProjectPath,
4
5
  sortProjectArtifacts,
5
6
  } from "./project-artifact-view.utils";
6
7
 
@@ -37,27 +38,38 @@ const artifacts = [
37
38
 
38
39
  describe("project artifact view", () => {
39
40
  it("prioritizes recent updates by default and supports alternate sort orders", () => {
40
- expect(sortProjectArtifacts(artifacts).map((artifact) => artifact.id)).toEqual([
41
- "newer-update",
42
- "older-update",
43
- "untimed",
44
- ]);
45
- expect(sortProjectArtifacts(artifacts, "created-desc").map((artifact) => artifact.id)).toEqual([
46
- "newer-update",
47
- "older-update",
48
- "untimed",
49
- ]);
50
- expect(sortProjectArtifacts(artifacts, "name").map((artifact) => artifact.id)).toEqual([
51
- "untimed",
52
- "newer-update",
53
- "older-update",
54
- ]);
41
+ expect(
42
+ sortProjectArtifacts(artifacts).map((artifact) => artifact.id),
43
+ ).toEqual(["newer-update", "older-update", "untimed"]);
44
+ expect(
45
+ sortProjectArtifacts(artifacts, "created-desc").map(
46
+ (artifact) => artifact.id,
47
+ ),
48
+ ).toEqual(["newer-update", "older-update", "untimed"]);
49
+ expect(
50
+ sortProjectArtifacts(artifacts, "name").map((artifact) => artifact.id),
51
+ ).toEqual(["untimed", "newer-update", "older-update"]);
55
52
  });
56
53
 
57
54
  it("renders recent timestamps in people-friendly language", () => {
58
55
  const now = new Date("2026-08-30T12:00:00.000Z");
59
- expect(formatProjectRelativeTime("2026-08-30T11:57:00.000Z", now)).toBe("3 minutes ago");
60
- expect(formatProjectRelativeTime("2026-08-30T09:00:00.000Z", now)).toBe("3 hours ago");
61
- expect(formatProjectRelativeTime("2026-08-29T12:00:00.000Z", now)).toBe("Yesterday");
56
+ expect(formatProjectRelativeTime("2026-08-30T11:57:00.000Z", now)).toBe(
57
+ "3 minutes ago",
58
+ );
59
+ expect(formatProjectRelativeTime("2026-08-30T09:00:00.000Z", now)).toBe(
60
+ "3 hours ago",
61
+ );
62
+ expect(formatProjectRelativeTime("2026-08-29T12:00:00.000Z", now)).toBe(
63
+ "Yesterday",
64
+ );
65
+ });
66
+
67
+ it("joins project-relative artifact paths across host path styles", () => {
68
+ expect(joinProjectPath("/tmp/project", "docs/design.md")).toBe(
69
+ "/tmp/project/docs/design.md",
70
+ );
71
+ expect(joinProjectPath("C:\\project\\", "docs/design.md")).toBe(
72
+ "C:\\project\\docs/design.md",
73
+ );
62
74
  });
63
75
  });
@@ -7,7 +7,17 @@ export type ProjectArtifactSort =
7
7
  | "created-desc"
8
8
  | "name";
9
9
 
10
- export const DEFAULT_PROJECT_ARTIFACT_SORT: ProjectArtifactSort = "updated-desc";
10
+ export const DEFAULT_PROJECT_ARTIFACT_SORT: ProjectArtifactSort =
11
+ "updated-desc";
12
+
13
+ export function joinProjectPath(
14
+ rootPath: string,
15
+ relativePath: string,
16
+ ): string {
17
+ const separator =
18
+ rootPath.endsWith("/") || rootPath.endsWith("\\") ? "" : "/";
19
+ return `${rootPath}${separator}${relativePath}`;
20
+ }
11
21
 
12
22
  function timestamp(value?: string): number {
13
23
  if (!value) return Number.NEGATIVE_INFINITY;
@@ -30,33 +40,40 @@ export function sortProjectArtifacts(
30
40
  if (sort === "name") return byName(left, right);
31
41
  const leftTimestamp = timestamp(
32
42
  sort === "created-desc"
33
- ? left.fileCreatedAt ?? left.fileUpdatedAt
34
- : left.fileUpdatedAt ?? left.fileCreatedAt,
43
+ ? (left.fileCreatedAt ?? left.fileUpdatedAt)
44
+ : (left.fileUpdatedAt ?? left.fileCreatedAt),
35
45
  );
36
46
  const rightTimestamp = timestamp(
37
47
  sort === "created-desc"
38
- ? right.fileCreatedAt ?? right.fileUpdatedAt
39
- : right.fileUpdatedAt ?? right.fileCreatedAt,
48
+ ? (right.fileCreatedAt ?? right.fileUpdatedAt)
49
+ : (right.fileUpdatedAt ?? right.fileCreatedAt),
40
50
  );
41
- const order = sort === "updated-asc"
42
- ? leftTimestamp - rightTimestamp
43
- : rightTimestamp - leftTimestamp;
51
+ const order =
52
+ sort === "updated-asc"
53
+ ? leftTimestamp - rightTimestamp
54
+ : rightTimestamp - leftTimestamp;
44
55
  return order || byName(left, right);
45
56
  });
46
57
  }
47
58
 
48
- export function formatProjectRelativeTime(value?: string, now: Date = new Date()): string {
59
+ export function formatProjectRelativeTime(
60
+ value?: string,
61
+ now: Date = new Date(),
62
+ ): string {
49
63
  if (!value) return "-";
50
64
  const date = new Date(value);
51
65
  const differenceMs = now.getTime() - date.getTime();
52
66
  if (Number.isNaN(date.getTime())) return value;
53
67
  if (differenceMs < 60_000) return t("projectsTimeJustNow");
54
68
  const minutes = Math.floor(differenceMs / 60_000);
55
- if (minutes < 60) return t("projectsTimeMinutesAgo").replace("{count}", String(minutes));
69
+ if (minutes < 60)
70
+ return t("projectsTimeMinutesAgo").replace("{count}", String(minutes));
56
71
  const hours = Math.floor(minutes / 60);
57
- if (hours < 24) return t("projectsTimeHoursAgo").replace("{count}", String(hours));
72
+ if (hours < 24)
73
+ return t("projectsTimeHoursAgo").replace("{count}", String(hours));
58
74
  const days = Math.floor(hours / 24);
59
75
  if (days === 1) return t("projectsTimeYesterday");
60
- if (days < 7) return t("projectsTimeDaysAgo").replace("{count}", String(days));
76
+ if (days < 7)
77
+ return t("projectsTimeDaysAgo").replace("{count}", String(days));
61
78
  return formatDateShort(value);
62
79
  }
@@ -0,0 +1,28 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { getProjectWorkStateLabel } from "./project-work-state-label.utils";
3
+
4
+ describe("getProjectWorkStateLabel", () => {
5
+ it.each([
6
+ ["Backlog", "待整理"],
7
+ ["Planned", "已计划"],
8
+ ["In Progress", "进行中"],
9
+ ["In Review", "评审中"],
10
+ ["Awaiting Acceptance", "等待验收"],
11
+ ["Completed", "已完成"],
12
+ ["Canceled", "已取消"],
13
+ ])("localizes the built-in %s state", (name, expected) => {
14
+ expect(getProjectWorkStateLabel(name, "zh")).toBe(expected);
15
+ });
16
+
17
+ it("keeps custom state names unchanged", () => {
18
+ expect(getProjectWorkStateLabel("Ready to publish", "zh")).toBe(
19
+ "Ready to publish",
20
+ );
21
+ });
22
+
23
+ it("keeps the built-in English labels in English", () => {
24
+ expect(getProjectWorkStateLabel("Awaiting Acceptance", "en")).toBe(
25
+ "Awaiting Acceptance",
26
+ );
27
+ });
28
+ });
@@ -0,0 +1,20 @@
1
+ import { t, type I18nLanguage } from "@/shared/lib/i18n";
2
+
3
+ const DEFAULT_STATE_MESSAGE_KEYS: Readonly<Record<string, string>> = {
4
+ Backlog: "projectsWorkState_backlog",
5
+ Planned: "projectsWorkState_planned",
6
+ "In Progress": "projectsWorkState_inProgress",
7
+ "In Review": "projectsWorkState_inReview",
8
+ "Awaiting Acceptance": "projectsWorkState_awaitingAcceptance",
9
+ Completed: "projectsWorkState_completed",
10
+ Canceled: "projectsWorkState_canceled",
11
+ };
12
+
13
+ export function getProjectWorkStateLabel(
14
+ name: string,
15
+ language?: I18nLanguage,
16
+ ): string {
17
+ const messageKey = DEFAULT_STATE_MESSAGE_KEYS[name];
18
+ if (!messageKey) return name;
19
+ return language ? t(messageKey, language) : t(messageKey);
20
+ }
@@ -1,3 +1,3 @@
1
- export { ServiceActionAuthorizationDialog } from '@/features/service-apps/components/service-action-authorization-dialog';
2
- export { ServiceActionAuthorizationManager } from '@/features/service-apps/managers/service-action-authorization.manager';
3
- export { ServiceAppsPanel } from '@/features/service-apps/components/service-apps-panel';
1
+ export async function loadServiceAppsPanel() {
2
+ return await import('@/features/service-apps/components/service-apps-panel');
3
+ }
package/src/main.tsx CHANGED
@@ -4,8 +4,11 @@ import { BrowserRouter, MemoryRouter } from 'react-router-dom';
4
4
  import App from './app';
5
5
  import { I18nProvider } from '@/components/providers/I18nProvider';
6
6
  import { ThemeProvider } from '@/components/providers/ThemeProvider';
7
+ import { installMainI18nCatalog } from '@/shared/lib/i18n';
7
8
  import './index.css';
8
9
 
10
+ await installMainI18nCatalog();
11
+
9
12
  const AppRouter = window.nextclawDesktop?.platform === 'win32' ? MemoryRouter : BrowserRouter;
10
13
  const root = createRoot(document.getElementById('root')!);
11
14
 
@@ -0,0 +1,49 @@
1
+ import { useRef } from 'react';
2
+ import { useQuery } from "@tanstack/react-query";
3
+ import { fetchAuthStatus, type AuthStatusView } from "@/shared/lib/api";
4
+ import { isTransientRuntimeConnectionErrorMessage } from "@/shared/lib/transport";
5
+
6
+ const AUTH_STATUS_BOOTSTRAP_PROBE_POLICY = {
7
+ maxRetries: 8,
8
+ startupTimeoutMs: 2_000,
9
+ settledTimeoutMs: 5_000,
10
+ retryBaseDelayMs: 500,
11
+ retryMaxDelayMs: 3_000,
12
+ } as const;
13
+
14
+ export function isTransientAuthStatusBootstrapError(error: unknown): boolean {
15
+ return error instanceof Error && isTransientRuntimeConnectionErrorMessage(error.message);
16
+ }
17
+
18
+ export function shouldRetryAuthStatusBootstrap(failureCount: number, error: unknown): boolean {
19
+ return failureCount < AUTH_STATUS_BOOTSTRAP_PROBE_POLICY.maxRetries &&
20
+ isTransientAuthStatusBootstrapError(error);
21
+ }
22
+
23
+ export function resolveAuthStatusBootstrapRetryDelay(failureCount: number): number {
24
+ return Math.min(
25
+ AUTH_STATUS_BOOTSTRAP_PROBE_POLICY.retryMaxDelayMs,
26
+ AUTH_STATUS_BOOTSTRAP_PROBE_POLICY.retryBaseDelayMs * 2 ** Math.max(0, failureCount - 1),
27
+ );
28
+ }
29
+
30
+ export function useAuthStatus() {
31
+ const bootstrapSettled = useRef(false);
32
+ const query = useQuery<AuthStatusView>({
33
+ queryKey: ["auth-status"],
34
+ queryFn: async () => {
35
+ const status = await fetchAuthStatus({
36
+ timeoutMs: bootstrapSettled.current
37
+ ? AUTH_STATUS_BOOTSTRAP_PROBE_POLICY.settledTimeoutMs
38
+ : AUTH_STATUS_BOOTSTRAP_PROBE_POLICY.startupTimeoutMs,
39
+ });
40
+ bootstrapSettled.current = true;
41
+ return status;
42
+ },
43
+ staleTime: 5_000,
44
+ retry: shouldRetryAuthStatusBootstrap,
45
+ retryDelay: resolveAuthStatusBootstrapRetryDelay,
46
+ });
47
+
48
+ return query;
49
+ }
@@ -1,6 +1,6 @@
1
1
  import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
2
2
  import { toast } from "sonner";
3
- import { addExistingProject, createProject, fetchProjects } from "@/shared/lib/api";
3
+ import { addExistingProject, createProject, fetchProjects, removeProject } from "@/shared/lib/api";
4
4
  import { t } from "@/shared/lib/i18n";
5
5
 
6
6
  const PROJECTS_QUERY_KEY = ["projects"] as const;
@@ -41,3 +41,17 @@ export function useAddExistingProject() {
41
41
  },
42
42
  });
43
43
  }
44
+
45
+ export function useRemoveProject() {
46
+ const queryClient = useQueryClient();
47
+ return useMutation({
48
+ mutationFn: removeProject,
49
+ onSuccess: async () => {
50
+ await queryClient.invalidateQueries({ queryKey: PROJECTS_QUERY_KEY });
51
+ toast.success(t("projectsRemoveSuccess"));
52
+ },
53
+ onError: (error: Error) => {
54
+ toast.error(`${t("projectsRemoveFailed")}: ${error.message}`);
55
+ },
56
+ });
57
+ }
@@ -19,3 +19,7 @@ export async function addExistingProject(
19
19
  ): Promise<ProjectView> {
20
20
  return await nextclawClient.projects.addExisting(input);
21
21
  }
22
+
23
+ export async function removeProject(projectId: string): Promise<ProjectView> {
24
+ return await nextclawClient.projects.remove(projectId, projectId);
25
+ }