@nextclaw/ui 0.26.4 → 0.27.0

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 (376) hide show
  1. package/CHANGELOG.md +63 -0
  2. package/dist/.vite/manifest.json +1271 -1227
  3. package/dist/assets/{abnfDiagram-VRR7QNED-Dcw10Yt1.js → abnfDiagram-VRR7QNED-DmeCRAkd.js} +1 -1
  4. package/dist/assets/account-panel-BYxGT4m4.js +1 -0
  5. package/dist/assets/{aiden0z-pptx-renderer.es--oNqetPA.js → aiden0z-pptx-renderer.es-ChX5DHqP.js} +1 -1
  6. package/dist/assets/api-CG22BskB.js +20 -0
  7. package/dist/assets/app-presenter-provider-CJFjhVYI.js +28 -0
  8. package/dist/assets/app-presenter-provider-D3BuuOyZ.css +1 -0
  9. package/dist/assets/appearance-settings-page-BcXr0vHp.js +1 -0
  10. package/dist/assets/{arc-DBRedCax.js → arc-Cu6Ols44.js} +1 -1
  11. package/dist/assets/architecture-TIHT7OUA-CLjV_xu4.js +1 -0
  12. package/dist/assets/{architectureDiagram-ZJ3FMSHR-Dufhtuv-.js → architectureDiagram-ZJ3FMSHR-DrNB0eLE.js} +1 -1
  13. package/dist/assets/{blockDiagram-677ZJIJ3-BUk0vtJ7.js → blockDiagram-677ZJIJ3-08rHt6DJ.js} +1 -1
  14. package/dist/assets/{book-open-CmRdT3N1.js → book-open-ClbJmmxP.js} +1 -1
  15. package/dist/assets/{c4Diagram-LMCZKHZV-CxDcVE9p.js → c4Diagram-LMCZKHZV-CCVgRG-u.js} +1 -1
  16. package/dist/assets/{card-CdRBL1E0.js → card-BdC01NFF.js} +1 -1
  17. package/dist/assets/channel-BgpbPyEH.js +1 -0
  18. package/dist/assets/channels-list-page-BrIvJlop.js +8 -0
  19. package/dist/assets/chunk-2Q5K7J3B-DjhMPqAC.js +1 -0
  20. package/dist/assets/{chunk-32BRIVSS-DSF3KElA.js → chunk-32BRIVSS-BeCaI-JN.js} +1 -1
  21. package/dist/assets/{chunk-52WLFC77-Costxzr3.js → chunk-52WLFC77-ZXIcYPYs.js} +1 -1
  22. package/dist/assets/{chunk-5FCAYU7R-rhqzGGZo.js → chunk-5FCAYU7R-DDwTPcy8.js} +1 -1
  23. package/dist/assets/{chunk-5HE753X5-BsEsmIao.js → chunk-5HE753X5-WygrMYPL.js} +1 -1
  24. package/dist/assets/{chunk-5JV3BV7I-Dqw_DCoN.js → chunk-5JV3BV7I-o8T3KZgk.js} +1 -1
  25. package/dist/assets/{chunk-5TONJI2A-Cft82fp8.js → chunk-5TONJI2A-BKFbUT7J.js} +1 -1
  26. package/dist/assets/{chunk-5VM5RSS4-DqNHn9Dl.js → chunk-5VM5RSS4-DYxCRV86.js} +1 -1
  27. package/dist/assets/{chunk-7BUUIJ7U-A5SBEMYY.js → chunk-7BUUIJ7U-CvSeJlKZ.js} +1 -1
  28. package/dist/assets/{chunk-BIQX33UG-1Ng3mTrH.js → chunk-BIQX33UG-BhH3RHCY.js} +1 -1
  29. package/dist/assets/{chunk-C7G6YPKG-DRJEy8ZI.js → chunk-C7G6YPKG-BTL0-xL3.js} +1 -1
  30. package/dist/assets/{chunk-CQNSW5MT-Bydv_J3J.js → chunk-CQNSW5MT-DWAU4QaD.js} +1 -1
  31. package/dist/assets/{chunk-CYSBUYHQ-HkMIg12u.js → chunk-CYSBUYHQ-B7ZBMXTx.js} +1 -1
  32. package/dist/assets/{chunk-EMLP6XTP-C5ndDvm3.js → chunk-EMLP6XTP-D92HvnHE.js} +1 -1
  33. package/dist/assets/{chunk-EX3LRPZG-DzeloZRo.js → chunk-EX3LRPZG-DfOGKwW8.js} +1 -1
  34. package/dist/assets/{chunk-FWX5IMBZ-n_2_CLTQ.js → chunk-FWX5IMBZ-DUuJUrXJ.js} +2 -2
  35. package/dist/assets/{chunk-HOUHSVGY-kx_slvu3.js → chunk-HOUHSVGY-i3MDyUY5.js} +1 -1
  36. package/dist/assets/{chunk-ICXQ74PX-sQLMURqL.js → chunk-ICXQ74PX-F_pmNX6t.js} +1 -1
  37. package/dist/assets/{chunk-JG7HCLWE-CMUn5Elc.js → chunk-JG7HCLWE-DygOelrU.js} +1 -1
  38. package/dist/assets/{chunk-JWPE2WC7-Cra3s-Uk.js → chunk-JWPE2WC7-BpiZMeRl.js} +1 -1
  39. package/dist/assets/{chunk-MOJQB5TN-umcrg5Bi.js → chunk-MOJQB5TN-CV10sB1I.js} +1 -1
  40. package/dist/assets/{chunk-MOZMSUNE-BnljD1ig.js → chunk-MOZMSUNE-Cf1WZNm0.js} +1 -1
  41. package/dist/assets/{chunk-OGEWGWER-BXx5pQjU.js → chunk-OGEWGWER-DuSw54VR.js} +1 -1
  42. package/dist/assets/{chunk-OSBZ3O6U-D-1qfdWu.js → chunk-OSBZ3O6U-_rt_XKSZ.js} +1 -1
  43. package/dist/assets/{chunk-PUDLZKDR-DLzX78LC.js → chunk-PUDLZKDR-BFPaAGNW.js} +1 -1
  44. package/dist/assets/{chunk-Q4XR5HBZ-DTK-rtW6.js → chunk-Q4XR5HBZ-GE0v_E5y.js} +1 -1
  45. package/dist/assets/{chunk-QBLGF6JB-CsFeIGfr.js → chunk-QBLGF6JB-BK_FpnlJ.js} +1 -1
  46. package/dist/assets/{chunk-R7FJI6CG-4xNYUeng.js → chunk-R7FJI6CG-CpYh6rGo.js} +1 -1
  47. package/dist/assets/{chunk-RYQCIY6F-DJiHiTE5.js → chunk-RYQCIY6F-htnxL_Cl.js} +1 -1
  48. package/dist/assets/{chunk-U6XO7XAA-BVYkauHz.js → chunk-U6XO7XAA-wDbE4BZ7.js} +1 -1
  49. package/dist/assets/{chunk-V7JOEXUC-BsazE8cS.js → chunk-V7JOEXUC-DDRSVFTB.js} +1 -1
  50. package/dist/assets/chunk-VAUOI2AC-GYimZYx9.js +1 -0
  51. package/dist/assets/{chunk-VR4S4FIN-7RoZQffO.js → chunk-VR4S4FIN-COdhHR1o.js} +1 -1
  52. package/dist/assets/{chunk-WYO6CB5R-DEuS6N1R.js → chunk-WYO6CB5R-DPDGhyF6.js} +3 -3
  53. package/dist/assets/chunk-XXDRQBXY-KKCf0HV5.js +1 -0
  54. package/dist/assets/{chunk-YOTPTUD7-9KBrosZL.js → chunk-YOTPTUD7-D8ir9Nx5.js} +1 -1
  55. package/dist/assets/{chunk-ZGVPDNZ5-IYgN9f5A.js → chunk-ZGVPDNZ5-BuNKD836.js} +1 -1
  56. package/dist/assets/{chunk-ZIRB5QZD-BUo0-zcp.js → chunk-ZIRB5QZD-DyHzq8jH.js} +1 -1
  57. package/dist/assets/{circle-dashed-HOgt3gIJ.js → circle-dashed-nDaMRmP-.js} +1 -1
  58. package/dist/assets/classDiagram-OUVF2IWQ-iRMPRR8M.js +1 -0
  59. package/dist/assets/classDiagram-v2-EOCWNBFH-BguFy_wj.js +1 -0
  60. package/dist/assets/{client-DKKbdJ6Q.js → client-DlFB5dW5.js} +1 -1
  61. package/dist/assets/config-split-page-C06fU9OQ.js +1 -0
  62. package/dist/assets/confirm-dialog-DpcmlXhd.js +1 -0
  63. package/dist/assets/{app-presenter-provider-jNGlmBLe.css → context-menu-DEcVZfaU.css} +1 -1
  64. package/dist/assets/context-menu-DKIEaY7f.js +211 -0
  65. package/dist/assets/{cose-bilkent-JH36ORCC-DhqbC1fR.js → cose-bilkent-JH36ORCC-B1I1_P6a.js} +1 -1
  66. package/dist/assets/{createLucideIcon-DE0sB9Nu.js → createLucideIcon-Lnt-4LMC.js} +1 -1
  67. package/dist/assets/cynefin-VYW2F7L2-CtbjXuCW.js +1 -0
  68. package/dist/assets/{cynefinDiagram-TSTJHNR4-C-TBRi5V.js → cynefinDiagram-TSTJHNR4-DcOnhnCo.js} +1 -1
  69. package/dist/assets/{dagre-VKFMJZFB-BmafJymp.js → dagre-VKFMJZFB-hAX89lg3.js} +1 -1
  70. package/dist/assets/{dagre-CrgsRasE.js → dagre-nSYhf9yY.js} +1 -1
  71. package/dist/assets/{desktop-capabilities-page-B9-B9k1A.js → desktop-capabilities-page-DPORyZAu.js} +1 -1
  72. package/dist/assets/desktop-update-config-BMTeWZmn.js +1 -0
  73. package/dist/assets/{diagram-FQU43EPY-BoOMwa6D.js → diagram-FQU43EPY-C1mRMD2k.js} +1 -1
  74. package/dist/assets/{diagram-G47NLZAW-Le7N6Uym.js → diagram-G47NLZAW-D2XBn5al.js} +1 -1
  75. package/dist/assets/{diagram-NH7WQ7WH-Fbpj3-MO.js → diagram-NH7WQ7WH-3LmRsmkC.js} +1 -1
  76. package/dist/assets/{diagram-OA4YK3LP-D3QuGGuu.js → diagram-OA4YK3LP-CFWn-osC.js} +1 -1
  77. package/dist/assets/{diagram-WEI45ONY-Cxu1uiN6.js → diagram-WEI45ONY-BqNRbUvU.js} +1 -1
  78. package/dist/assets/dialog-BNXSPMX_.js +1 -0
  79. package/dist/assets/dist-Cuiqqp1O.js +41 -0
  80. package/dist/assets/doc-browser-CDEapjmj.js +1 -0
  81. package/dist/assets/{doc-browser-context-ClLgMbxd.js → doc-browser-context-0QDWYorh.js} +1 -1
  82. package/dist/assets/{docx-preview-CkU2psnd.js → docx-preview-CunAATyC.js} +1 -1
  83. package/dist/assets/{download-CKqS1Ksj.js → download-f_sPcoLZ.js} +1 -1
  84. package/dist/assets/{ebnfDiagram-CCIWWBDH-CCAjuQM6.js → ebnfDiagram-CCIWWBDH-CYb8IwqD.js} +1 -1
  85. package/dist/assets/{erDiagram-Q63AITRT-BuzUAq-d.js → erDiagram-Q63AITRT-H47mUoeK.js} +1 -1
  86. package/dist/assets/eventmodeling-45OFAUF4-Djd6id_u.js +1 -0
  87. package/dist/assets/extensions-B-0xQz3L.js +1 -0
  88. package/dist/assets/external-link-CbVlnlNy.js +1 -0
  89. package/dist/assets/flowDiagram-23GEKE2U-DUrsM_ZL.js +1 -0
  90. package/dist/assets/{form-actions-CCoxz33Q.js → form-actions-BoTqELHB.js} +1 -1
  91. package/dist/assets/{ganttDiagram-NO4QXBWP-cJmWVY8F.js → ganttDiagram-NO4QXBWP-hSX45zfV.js} +1 -1
  92. package/dist/assets/gitGraph-TEB2WS4Q-CU6P_2Cm.js +1 -0
  93. package/dist/assets/{gitGraphDiagram-IHSO6WYX-F58o-vtP.js → gitGraphDiagram-IHSO6WYX-Br0UW2qg.js} +1 -1
  94. package/dist/assets/{i18n-OGmF0Ws_.js → i18n-B0V5NkFj.js} +2 -2
  95. package/dist/assets/inbox-Bna8iUy0.js +1 -0
  96. package/dist/assets/info-DKCQHKI2-Bj2P_7RC.js +1 -0
  97. package/dist/assets/{infoDiagram-FWYZ7A6U-Gt59b83C.js → infoDiagram-FWYZ7A6U-DQmP_67w.js} +1 -1
  98. package/dist/assets/{ishikawaDiagram-FXEZZL3T-1u4IAj1I.js → ishikawaDiagram-FXEZZL3T-BHAV2oes.js} +1 -1
  99. package/dist/assets/{journeyDiagram-5HDEW3XC-CCQsfV-5.js → journeyDiagram-5HDEW3XC-CFHax4_d.js} +1 -1
  100. package/dist/assets/{kanban-definition-HUTT4EX6-vlB6O_Zn.js → kanban-definition-HUTT4EX6-B4ZA9rp5.js} +1 -1
  101. package/dist/assets/katex-BprZ-Wwb.js +1 -0
  102. package/dist/assets/{keyboard-shortcuts-settings-page-D8rosOVh.js → keyboard-shortcuts-settings-page-BluWKrSo.js} +1 -1
  103. package/dist/assets/{line-D_bHubTL.js → line-BboM0HLF.js} +1 -1
  104. package/dist/assets/{linear-ChlwuS5y.js → linear-4K_M1oYM.js} +1 -1
  105. package/dist/assets/login-page-BMHW0Hh0.js +1 -0
  106. package/dist/assets/{login-page-BT3GV3Sb.js → login-page-JNr5JCEw.js} +1 -1
  107. package/dist/assets/main-BCX_Untx.css +1 -0
  108. package/dist/assets/main-C5kbmGUV.js +2 -0
  109. package/dist/assets/main-i18n-catalog.config-DO4wNT7T.js +1 -0
  110. package/dist/assets/{map-oGHTsIu8.js → map-26GpO1wn.js} +1 -1
  111. package/dist/assets/mcp-marketplace-page-CV-PHKTN.js +1 -0
  112. package/dist/assets/{mermaid-parser.core-CuRe1ZG4.js → mermaid-parser.core-BTJFnvRC.js} +2 -2
  113. package/dist/assets/{mermaid.core-u4YCFKRf.js → mermaid.core-B7ufoXgq.js} +3 -3
  114. package/dist/assets/{mindmap-definition-LN4V7U3C-DeJAQYTu.js → mindmap-definition-LN4V7U3C-BHyru6KE.js} +1 -1
  115. package/dist/assets/model-config-page-C-wbM6Fe.js +1 -0
  116. package/dist/assets/{navigation-link-DC-Pku0Y.js → navigation-link-DFLSVIut.js} +2 -2
  117. package/dist/assets/{ordinal-C6JFfkKN.js → ordinal-BqFe2ePF.js} +1 -1
  118. package/dist/assets/packet-7NZHBO7P-ti5ge2jr.js +1 -0
  119. package/dist/assets/panel-app-CSQmm_7E.js +1 -0
  120. package/dist/assets/panel-app-i18n-catalog.config-Bo52dPuF.js +1 -0
  121. package/dist/assets/panelStandalone-PuuMF65O.js +2 -0
  122. package/dist/assets/panelStandalone-XRlWxbRS.css +1 -0
  123. package/dist/assets/{pegDiagram-2B236MQR-nVMkxShH.js → pegDiagram-2B236MQR-PVTatU5j.js} +1 -1
  124. package/dist/assets/pie-RZYD4A2V-Bnep8TyV.js +1 -0
  125. package/dist/assets/{pieDiagram-ENE6RG2P-8aUH44QZ.js → pieDiagram-ENE6RG2P-CGc-QCc7.js} +1 -1
  126. package/dist/assets/plus-CywSlIbG.js +1 -0
  127. package/dist/assets/privacy-settings-page-CPdEiouE.js +1 -0
  128. package/dist/assets/projects-D_cd_t4G.js +1 -0
  129. package/dist/assets/provider-models-XRArsply.js +1 -0
  130. package/dist/assets/provider-scoped-model-input-C8JI-hus.js +1 -0
  131. package/dist/assets/providers-config-page-fyMlVgvq.js +1 -0
  132. package/dist/assets/puzzle-Bk3ud1KI.js +1 -0
  133. package/dist/assets/pwa-Cx6Fnox4.js +1 -0
  134. package/dist/assets/{quadrantDiagram-ABIIQ3AL-EY8hpFv8.js → quadrantDiagram-ABIIQ3AL-DGL36riv.js} +1 -1
  135. package/dist/assets/radar-I7S5WNFK-BYg-icAP.js +1 -0
  136. package/dist/assets/railroad-3IZDKUUU-_CHO-Fi1.js +1 -0
  137. package/dist/assets/railroad-abnf-AHOZXSZD-hbQDevHe.js +1 -0
  138. package/dist/assets/railroad-ebnf-EBAXGLYW-v4RZkOfi.js +1 -0
  139. package/dist/assets/railroad-peg-LSFZ7HO6-bC9BqkI5.js +1 -0
  140. package/dist/assets/{railroadDiagram-RFXS5EU6-CNhcIeji.js → railroadDiagram-RFXS5EU6-CyNwvhSb.js} +1 -1
  141. package/dist/assets/{refresh-cw-BG8VfcdA.js → refresh-cw-Ca2napNx.js} +1 -1
  142. package/dist/assets/remote-D5MBQNZE.js +1 -0
  143. package/dist/assets/{requirementDiagram-TGXJPOKE-D8Dcs-Rt.js → requirementDiagram-TGXJPOKE-C31NMKJ0.js} +1 -1
  144. package/dist/assets/{rotate-cw-DPEegUU2.js → rotate-cw-DXg7sNUE.js} +1 -1
  145. package/dist/assets/runtime-config-page--ivCxBT4.js +1 -0
  146. package/dist/assets/{sankeyDiagram-HTMAVEWB-HTrb6aa-.js → sankeyDiagram-HTMAVEWB-CwrQ_7PA.js} +1 -1
  147. package/dist/assets/{save-CPlctBjT.js → save-DNigywZ9.js} +1 -1
  148. package/dist/assets/search-U9C0oTQJ.js +1 -0
  149. package/dist/assets/search-config-page-Bdx_RR1q.js +1 -0
  150. package/dist/assets/secrets-config-page-CvMIcsfJ.js +3 -0
  151. package/dist/assets/security-config-CQmMqQ8v.js +1 -0
  152. package/dist/assets/select-DxwDeKUR.js +1 -0
  153. package/dist/assets/{sequenceDiagram-DBY2YBRQ-6f_4X16Y.js → sequenceDiagram-DBY2YBRQ-EFD482_y.js} +1 -1
  154. package/dist/assets/service-apps-panel-CiYO1Be4.js +1 -0
  155. package/dist/assets/{settings-2-CrGx9_De.js → settings-2-BrBUdRoY.js} +1 -1
  156. package/dist/assets/{sizeCapture-X5ZJPWSS-Ck9rsuwL.js → sizeCapture-X5ZJPWSS-TDvULTE1.js} +1 -1
  157. package/dist/assets/{src-C8AUg09O.js → src-CI2AvEMj.js} +1 -1
  158. package/dist/assets/{stateDiagram-2N3HPSRC-Cm33MLHu.js → stateDiagram-2N3HPSRC-JdPsIYHo.js} +1 -1
  159. package/dist/assets/stateDiagram-v2-6OUMAXLB-4_aAQfB_.js +1 -0
  160. package/dist/assets/{status-dot-Cb8ewlUr.js → status-dot-BbGsrkIX.js} +1 -1
  161. package/dist/assets/{swimlanes-5IMT3BWC-B107vmvM.js → swimlanes-5IMT3BWC-BnxpKeK_.js} +2 -2
  162. package/dist/assets/swimlanesDiagram-G3AALYLV-wCqfwsw-.js +8 -0
  163. package/dist/assets/tag-chip-BDbxcf6P.js +1 -0
  164. package/dist/assets/theme-provider-D98XGa-c.js +1 -0
  165. package/dist/assets/{timeline-definition-FHXFAJF6-CJEoAj33.js → timeline-definition-FHXFAJF6-2IL22-uC.js} +1 -1
  166. package/dist/assets/trash-2-DwSkRfeB.js +1 -0
  167. package/dist/assets/treeView-QDETBFTQ-BAyOb14Z.js +1 -0
  168. package/dist/assets/treemap-6X3UGDF4-DgPDM0zS.js +1 -0
  169. package/dist/assets/{use-config-BM5jC65L.js → use-config-D3cNzk2k.js} +1 -1
  170. package/dist/assets/{use-desktop-capabilities-ezKJwKqg.js → use-desktop-capabilities-D7odgaTB.js} +1 -1
  171. package/dist/assets/use-viewport-layout-BANU5E1t.js +1 -0
  172. package/dist/assets/{vennDiagram-L72KCM5P-DzsUHk5I.js → vennDiagram-L72KCM5P-Cjf6n5Va.js} +1 -1
  173. package/dist/assets/wardley-OPB4EBWU-eCElh8z5.js +1 -0
  174. package/dist/assets/{wardleyDiagram-EHGQE667-1NfFs5ua.js → wardleyDiagram-EHGQE667-yv4qKbBO.js} +1 -1
  175. package/dist/assets/x-BqGj6_Vq.js +1 -0
  176. package/dist/assets/{xychartDiagram-FW5EYKEG-Ci8AI3Qo.js → xychartDiagram-FW5EYKEG-Bmp8Ck8X.js} +1 -1
  177. package/dist/index.html +43 -106
  178. package/dist/panel-standalone.html +18 -12
  179. package/index.html +15 -66
  180. package/package.json +5 -5
  181. package/src/app/configs/__tests__/app-navigation.config.test.ts +4 -2
  182. package/src/app/configs/app-navigation.config.ts +7 -2
  183. package/src/app/styles/design-system.css +85 -1
  184. package/src/app/styles/island-theme.css +8 -6
  185. package/src/features/agents/components/agent-details-dialog.tsx +5 -3
  186. package/src/features/app-data/components/app-retained-data-section.tsx +2 -2
  187. package/src/features/apps/components/__tests__/app-marketplace-catalog.test.tsx +2 -2
  188. package/src/features/apps/components/__tests__/app-packages-panel.test.tsx +94 -23
  189. package/src/features/apps/components/__tests__/apps-panel.test.tsx +5 -3
  190. package/src/features/apps/components/app-document-access-section.tsx +17 -14
  191. package/src/features/apps/components/app-marketplace-catalog.tsx +66 -39
  192. package/src/features/apps/components/app-marketplace-cover.tsx +5 -2
  193. package/src/features/apps/components/app-marketplace-detail.tsx +9 -6
  194. package/src/features/apps/components/app-marketplace-operation.tsx +8 -3
  195. package/src/features/apps/components/app-marketplace-panel.tsx +168 -0
  196. package/src/features/apps/components/app-package-card.tsx +224 -147
  197. package/src/features/apps/components/app-package-details-dialog.tsx +103 -0
  198. package/src/features/apps/components/app-packages-panel.tsx +159 -144
  199. package/src/features/apps/components/apps-panel.tsx +40 -11
  200. package/src/features/chat/components/conversation/chat-conversation-header-section.tsx +1 -1
  201. package/src/features/chat/components/conversation/chat-conversation-header.tsx +1 -1
  202. package/src/features/chat/components/layout/chat-sidebar-toolbar.tsx +2 -2
  203. package/src/features/chat/components/layout/chat-sidebar-utility-menu.tsx +9 -7
  204. package/src/features/chat/features/conversation/components/floating-session-conversation.tsx +2 -3
  205. package/src/features/chat/features/conversation/components/session-conversation-area.tsx +1 -0
  206. package/src/features/chat/features/conversation/hooks/__tests__/use-session-conversation-input-state.test.tsx +33 -0
  207. package/src/features/chat/features/message/components/chat-message-list.container.tsx +0 -2
  208. package/src/features/chat/features/message/utils/__tests__/chat-message-process-summary.utils.test.ts +4 -6
  209. package/src/features/chat/features/message/utils/chat-message-execution-summary.utils.ts +3 -0
  210. package/src/features/chat/features/message/utils/chat-message-process-summary.utils.ts +2 -10
  211. package/src/features/chat/features/message/utils/chat-message-texts.utils.ts +2 -0
  212. package/src/features/chat/features/message/utils/chat-message-trigger-details.utils.ts +3 -0
  213. package/src/features/chat/features/session/components/chat-sidebar-project-groups.tsx +2 -1
  214. package/src/features/chat/features/session/components/session-header/chat-session-metadata-dialog.tsx +1 -1
  215. package/src/features/chat/features/session/components/session-header/chat-session-project-badge.tsx +2 -2
  216. package/src/features/chat/features/welcome/components/__tests__/chat-welcome.test.tsx +31 -6
  217. package/src/features/chat/features/welcome/components/chat-conversation-welcome.tsx +13 -1
  218. package/src/features/chat/features/welcome/components/chat-welcome-agent-picker.tsx +1 -1
  219. package/src/features/chat/features/welcome/components/chat-welcome-project-picker.tsx +1 -1
  220. package/src/features/chat/features/welcome/components/chat-welcome-prompt-suggestions.tsx +56 -13
  221. package/src/features/chat/features/welcome/components/chat-welcome-session-type-picker.tsx +3 -3
  222. package/src/features/chat/features/welcome/components/chat-welcome.tsx +14 -4
  223. package/src/features/chat/features/welcome/utils/__tests__/chat-welcome-continuation.utils.test.ts +40 -0
  224. package/src/features/chat/features/welcome/utils/chat-welcome-continuation.utils.ts +21 -0
  225. package/src/features/chat/features/workspace/components/chat-session-workspace-directory-browser.tsx +1 -1
  226. package/src/features/chat/features/workspace/components/chat-session-workspace-file-breadcrumbs.tsx +5 -9
  227. package/src/features/chat/features/workspace/components/chat-session-workspace-file-preview.tsx +6 -4
  228. package/src/features/chat/features/workspace/components/chat-session-workspace-panel-content.tsx +27 -27
  229. package/src/features/chat/features/workspace/components/chat-session-workspace-panel-nav.tsx +1 -1
  230. package/src/features/chat/features/workspace/components/overview/chat-session-continuous-attention.tsx +11 -11
  231. package/src/features/chat/features/workspace/components/overview/chat-session-token-usage.tsx +15 -15
  232. package/src/features/chat/features/workspace/components/project-files/workspace-directory-tree.tsx +2 -2
  233. package/src/features/chat/features/workspace/components/project-files/workspace-new-folder-tree-item.tsx +1 -1
  234. package/src/features/chat/features/workspace/components/project-files/workspace-project-files-toolbar.tsx +2 -2
  235. package/src/features/chat/features/workspace/components/session-cron-job-content.tsx +7 -4
  236. package/src/features/cron/components/cron-job-row.tsx +5 -4
  237. package/src/features/desktop-capabilities/components/desktop-authorization-dialog.tsx +5 -5
  238. package/src/features/inbox/components/inbox-reader-dialog.test.tsx +46 -6
  239. package/src/features/inbox/components/inbox-reader-dialog.tsx +38 -29
  240. package/src/features/inbox/managers/inbox.manager.test.ts +28 -0
  241. package/src/features/inbox/managers/inbox.manager.ts +18 -4
  242. package/src/features/inbox/pages/inbox-page.test.ts +16 -5
  243. package/src/features/inbox/pages/inbox-page.tsx +37 -29
  244. package/src/features/panel-apps/components/panel-app-list-item.tsx +4 -3
  245. package/src/features/panel-apps/components/panel-app-service-action-authorization-dialog.tsx +2 -2
  246. package/src/features/panel-apps/utils/panel-app-doc-browser.utils.tsx +5 -24
  247. package/src/features/projects/components/work/project-work-items.tsx +1 -1
  248. package/src/features/projects/pages/project-home-page.tsx +4 -2
  249. package/src/features/pwa/managers/__tests__/pwa-shell-theme.manager.test.ts +3 -3
  250. package/src/features/pwa/managers/pwa-shell-theme.manager.ts +8 -3
  251. package/src/features/right-panel-resources/components/__tests__/page-resource-icon.test.tsx +13 -2
  252. package/src/features/right-panel-resources/components/page-resource-icon.tsx +15 -7
  253. package/src/features/right-panel-resources/configs/right-panel-resource-routes.config.ts +1 -1
  254. package/src/features/right-panel-resources/index.ts +3 -0
  255. package/src/features/right-panel-resources/utils/__tests__/right-panel-resource-route-resolver.utils.test.ts +5 -0
  256. package/src/features/right-panel-resources/utils/right-panel-resource-uri.utils.ts +3 -2
  257. package/src/features/service-apps/components/service-action-row.tsx +4 -3
  258. package/src/features/service-apps/components/service-app-delete-dialog.tsx +2 -2
  259. package/src/features/service-apps/components/service-app-list-item.tsx +5 -4
  260. package/src/features/settings/components/config/provider-model-suggestions-panel.tsx +1 -1
  261. package/src/features/settings/pages/providers-config-page.tsx +3 -2
  262. package/src/index.css +42 -8
  263. package/src/platforms/mobile/components/__tests__/mobile-app-shell.test.tsx +9 -0
  264. package/src/platforms/mobile/components/mobile-app-shell.tsx +4 -2
  265. package/src/platforms/mobile/components/mobile-topbar.tsx +2 -2
  266. package/src/shared/components/doc-browser/doc-browser-tab-strip.tsx +2 -2
  267. package/src/shared/components/path-picker/server-path-picker-dialog.tsx +5 -3
  268. package/src/shared/components/ui/__tests__/dialog.test.tsx +37 -1
  269. package/src/shared/components/ui/actions/action-feedback.ts +1 -8
  270. package/src/shared/components/ui/actions/icon-action-button.tsx +1 -1
  271. package/src/shared/components/ui/confirm-dialog.tsx +1 -2
  272. package/src/shared/components/ui/context-menu/context-menu-items.tsx +2 -2
  273. package/src/shared/components/ui/dialog.tsx +29 -7
  274. package/src/shared/components/ui/popover.tsx +14 -3
  275. package/src/shared/components/ui/tab-strip/compact-tab-strip.tsx +5 -5
  276. package/src/shared/components/ui/tooltip.tsx +2 -1
  277. package/src/shared/components/workbench/__tests__/workbench-surface.test.tsx +21 -0
  278. package/src/shared/components/workbench/workbench-surface-toolbar.tsx +4 -0
  279. package/src/shared/components/workbench/workbench-surface.tsx +5 -2
  280. package/src/shared/lib/i18n/locales/en-US/chat.json +14 -9
  281. package/src/shared/lib/i18n/locales/en-US/core.json +5 -1
  282. package/src/shared/lib/i18n/locales/en-US/doc-browser.json +10 -5
  283. package/src/shared/lib/i18n/locales/en-US/inbox.json +1 -0
  284. package/src/shared/lib/i18n/locales/zh-CN/chat.json +14 -9
  285. package/src/shared/lib/i18n/locales/zh-CN/core.json +5 -1
  286. package/src/shared/lib/i18n/locales/zh-CN/doc-browser.json +11 -6
  287. package/src/shared/lib/i18n/locales/zh-CN/inbox.json +1 -0
  288. package/src/shared/lib/theme/index.test.ts +28 -1
  289. package/src/shared/lib/theme/index.ts +14 -3
  290. package/dist/assets/account-panel-DVi_jhkG.js +0 -1
  291. package/dist/assets/api-BZvNpFOS.js +0 -20
  292. package/dist/assets/app-presenter-provider-BcYL-VNe.js +0 -124
  293. package/dist/assets/appearance-settings-page-AZNBzLmT.js +0 -1
  294. package/dist/assets/architecture-TIHT7OUA-CvnC-LeA.js +0 -1
  295. package/dist/assets/channel-CpMstUPF.js +0 -1
  296. package/dist/assets/channels-list-page-eZN74FN3.js +0 -8
  297. package/dist/assets/chunk-2Q5K7J3B-Djp3VF0s.js +0 -1
  298. package/dist/assets/chunk-VAUOI2AC-BrbiIC1Q.js +0 -1
  299. package/dist/assets/chunk-XXDRQBXY-Cx9N40wF.js +0 -1
  300. package/dist/assets/classDiagram-OUVF2IWQ-DFN1v3PQ.js +0 -1
  301. package/dist/assets/classDiagram-v2-EOCWNBFH-DRvglADa.js +0 -1
  302. package/dist/assets/config-split-page-BA3V6b3Q.js +0 -1
  303. package/dist/assets/confirm-dialog-8vqYZ0wy.js +0 -1
  304. package/dist/assets/context-menu-DHVVdHzN.js +0 -1
  305. package/dist/assets/cynefin-VYW2F7L2-DXUGnpJH.js +0 -1
  306. package/dist/assets/desktop-update-config-Cfr3IRUe.js +0 -1
  307. package/dist/assets/dialog-BUfWO8hr.js +0 -5
  308. package/dist/assets/doc-browser-w8q1j0jL.js +0 -1
  309. package/dist/assets/es2015-BiB05Sav.js +0 -41
  310. package/dist/assets/eventmodeling-45OFAUF4-ZBfVS3ZC.js +0 -1
  311. package/dist/assets/extensions-vPD4dGqt.js +0 -1
  312. package/dist/assets/eye-bKzylu3p.js +0 -1
  313. package/dist/assets/flowDiagram-23GEKE2U-CqdsKk6x.js +0 -1
  314. package/dist/assets/gitGraph-TEB2WS4Q-fHYFd9Es.js +0 -1
  315. package/dist/assets/info-DKCQHKI2-CSaOL_bb.js +0 -1
  316. package/dist/assets/katex-B4FQSo9k.js +0 -1
  317. package/dist/assets/loader-circle-Clq1Gtb1.js +0 -1
  318. package/dist/assets/login-page-DKId3By8.js +0 -1
  319. package/dist/assets/main-bEWCGDHr.js +0 -2
  320. package/dist/assets/main-i18n-catalog.config-Bpu5GcwF.js +0 -1
  321. package/dist/assets/main-kqMRTwOs.css +0 -1
  322. package/dist/assets/mcp-marketplace-page-CZIP6j3L.js +0 -1
  323. package/dist/assets/model-config-page-D21a-BO0.js +0 -1
  324. package/dist/assets/packet-7NZHBO7P-D-YFTczs.js +0 -1
  325. package/dist/assets/panel-app-BYfQu_TS.js +0 -1
  326. package/dist/assets/panel-app-i18n-catalog.config-D44qmtfk.js +0 -1
  327. package/dist/assets/panelStandalone-BRg7B1EI.css +0 -1
  328. package/dist/assets/panelStandalone-CX8X2hjh.js +0 -2
  329. package/dist/assets/pie-RZYD4A2V-VEnYu9s5.js +0 -1
  330. package/dist/assets/privacy-settings-page-e0LFHZJm.js +0 -1
  331. package/dist/assets/projects-Rki7Fmth.js +0 -1
  332. package/dist/assets/provider-models-DFQgF4hS.js +0 -1
  333. package/dist/assets/provider-scoped-model-input-DaONL0j_.js +0 -1
  334. package/dist/assets/providers-config-page-CEOhQlMe.js +0 -1
  335. package/dist/assets/puzzle-D_TiFXOv.js +0 -1
  336. package/dist/assets/pwa-C9ac_1dg.js +0 -1
  337. package/dist/assets/radar-I7S5WNFK-BFNIekC1.js +0 -1
  338. package/dist/assets/railroad-3IZDKUUU-lVqJ3Hak.js +0 -1
  339. package/dist/assets/railroad-abnf-AHOZXSZD-DYaefdFC.js +0 -1
  340. package/dist/assets/railroad-ebnf-EBAXGLYW-CTAk6lVq.js +0 -1
  341. package/dist/assets/railroad-peg-LSFZ7HO6-Cob294tg.js +0 -1
  342. package/dist/assets/remote-DkSx5x4R.js +0 -1
  343. package/dist/assets/runtime-config-page-DDT1dVLt.js +0 -1
  344. package/dist/assets/search-config-page-CDwmd6eX.js +0 -1
  345. package/dist/assets/secrets-config-page-1OvgmwPv.js +0 -3
  346. package/dist/assets/security-config-Pv4CM-kw.js +0 -1
  347. package/dist/assets/select-Y9IEbx9x.js +0 -1
  348. package/dist/assets/service-apps-panel-DPrLeF3N.js +0 -1
  349. package/dist/assets/stateDiagram-v2-6OUMAXLB-BrNv-raG.js +0 -1
  350. package/dist/assets/swimlanesDiagram-G3AALYLV-ntcxaPXp.js +0 -8
  351. package/dist/assets/tag-chip-D-6aPuuh.js +0 -1
  352. package/dist/assets/theme-provider-BjF1Eowc.js +0 -1
  353. package/dist/assets/trash-2-DER2EJKK.js +0 -1
  354. package/dist/assets/treeView-QDETBFTQ-DbFmr-gw.js +0 -1
  355. package/dist/assets/treemap-6X3UGDF4-C8aY96pi.js +0 -1
  356. package/dist/assets/use-viewport-layout-vmbQUNT6.js +0 -1
  357. package/dist/assets/wardley-OPB4EBWU-D8d6DbbJ.js +0 -1
  358. package/dist/assets/x-DSz9cGhy.js +0 -1
  359. package/src/features/apps/components/app-marketplace-dialog.tsx +0 -168
  360. /package/dist/assets/{array-BV9OtnO8.js → array-DstOKG9P.js} +0 -0
  361. /package/dist/assets/{chunk-KEIR6QF5-Cx-8EJ4_.js → chunk-KEIR6QF5-0TA8nwgf.js} +0 -0
  362. /package/dist/assets/{chunk-Y2CYZVJY-Dy1ufvjf.js → chunk-Y2CYZVJY-rfGCG6pM.js} +0 -0
  363. /package/dist/assets/{config-hints-DbmiyTKg.js → config-hints-BfGoD7-o.js} +0 -0
  364. /package/dist/assets/{cytoscape.esm-CSCjuT4Z.js → cytoscape.esm-DdIcNEda.js} +0 -0
  365. /package/dist/assets/{defaultLocale-DWTiNsbT.js → defaultLocale-CzXix2iJ.js} +0 -0
  366. /package/dist/assets/{dist-BVCSkfWY.js → dist-D5dwMwKA.js} +0 -0
  367. /package/dist/assets/{graphlib-B1yb1Wl8.js → graphlib-BKOe0eFe.js} +0 -0
  368. /package/dist/assets/{host-capabilities-DuIFVsvM.js → host-capabilities-CnSVTaot.js} +0 -0
  369. /package/dist/assets/{init-BF6GH5oe.js → init-OCFb3zgY.js} +0 -0
  370. /package/dist/assets/{jszip.min-D12teKzP.js → jszip.min-B2uhSxvV.js} +0 -0
  371. /package/dist/assets/{katex-CsAM73R9.js → katex-dBrYsE7B.js} +0 -0
  372. /package/dist/assets/{middleware-B--6mAUM.js → middleware-CAXxl-af.js} +0 -0
  373. /package/dist/assets/{path-C56XkznJ.js → path-T5eoIRSJ.js} +0 -0
  374. /package/dist/assets/{preload-helper-ca-nBW7U.js → preload-helper-DWTEM3RW.js} +0 -0
  375. /package/dist/assets/{rough.esm-CGfeEbtE.js → rough.esm-DmGXmZEB.js} +0 -0
  376. /package/dist/assets/{xlsx-sds5nUpL.js → xlsx-CoYacou7.js} +0 -0
@@ -1,7 +1,6 @@
1
1
  import { PageResourceActionsMenu } from '@/features/right-panel-resources';
2
2
  import { pageResourceFromTarget } from '@/features/right-panel-resources';
3
3
  import { buildSessionPanelUrl } from '@/features/chat/features/session/utils/chat-session-route.utils';
4
- import { MessageSquare } from 'lucide-react';
5
4
  import { useNavigate } from 'react-router-dom';
6
5
  import { getAppPresenter } from '@/app/presenters/app.presenter';
7
6
  import { sessionSurfaceManager } from '@/features/chat/managers/session-surface.manager';
@@ -17,8 +16,8 @@ export function FloatingSessionConversation() {
17
16
  const presenter = getAppPresenter();
18
17
  if (!session) return null;
19
18
  return <WorkbenchSurface id={SESSION_WORKBENCH_SURFACE} manager={presenter.workbenchSurfaceManager}
20
- title={session.title} icon={<MessageSquare className="h-4 w-4 shrink-0" />} testId="floating-session-conversation" focusOnOpen
21
- navigation={<div className="flex min-w-0 flex-1 items-center gap-2"><span className="min-w-0 flex-1 truncate text-xs">{session.title}</span><PageResourceActionsMenu page={pageResourceFromTarget({ kind: 'chat-session', title: session.title, url: buildSessionPanelUrl(session.sessionKey), resourceUri: buildSessionPanelUrl(session.sessionKey), historyPolicy: 'none' })} /></div>}
19
+ title={session.title} testId="floating-session-conversation" focusOnOpen
20
+ moreActions={<PageResourceActionsMenu page={pageResourceFromTarget({ kind: 'chat-session', title: session.title, url: buildSessionPanelUrl(session.sessionKey), resourceUri: buildSessionPanelUrl(session.sessionKey), historyPolicy: 'none' })} />}
22
21
  onClose={sessionSurfaceManager.close}
23
22
  onPlace={() => sessionSurfaceManager.dock(session, presenter.docBrowserManager)}
24
23
  onOpenMain={() => { navigate(buildSessionPath(session.sessionKey)); sessionSurfaceManager.close(); }}>
@@ -437,6 +437,7 @@ export function SessionConversationArea(props: SessionConversationAreaProps) {
437
437
  onEditMessage={(payload) => controllerRef.current.editMessage(payload)}
438
438
  welcomeSlot={
439
439
  <ChatConversationWelcome
440
+ hasDraftContent={Boolean(inputSnapshot.text.trim() || inputSnapshot.attachments.length || inputSnapshot.selectedSkills.length)}
440
441
  inputSlot={
441
442
  <div className="space-y-2">
442
443
  {renderInput("embedded", t("chatWelcomeInputPlaceholder"))}
@@ -198,4 +198,37 @@ describe('useSessionConversationInputState session preferences', () => {
198
198
  rerender({ sessionKey: null });
199
199
  expect(result.current.inputSnapshot.text).toBe('新会话草稿');
200
200
  });
201
+
202
+ it('keeps a successfully cleared draft empty after switching away and back', () => {
203
+ const { result, rerender } = renderHook(
204
+ ({ sessionKey }: { sessionKey: string }) =>
205
+ useSessionConversationInputState(null, sessionKey),
206
+ { initialProps: { sessionKey: 'session-sent' } },
207
+ );
208
+
209
+ act(() => {
210
+ result.current.inputActions.syncComposer({
211
+ text: '已经发送的消息',
212
+ nodes: [],
213
+ selectedSkills: [],
214
+ skillRecords: [],
215
+ });
216
+ result.current.inputActions.resetComposer();
217
+ });
218
+ rerender({ sessionKey: 'session-unsent' });
219
+ act(() => {
220
+ result.current.inputActions.syncComposer({
221
+ text: '仍未发送的草稿',
222
+ nodes: [],
223
+ selectedSkills: [],
224
+ skillRecords: [],
225
+ });
226
+ });
227
+
228
+ rerender({ sessionKey: 'session-sent' });
229
+ expect(result.current.inputSnapshot.text).toBe('');
230
+
231
+ rerender({ sessionKey: 'session-unsent' });
232
+ expect(result.current.inputSnapshot.text).toBe('仍未发送的草稿');
233
+ });
201
234
  });
@@ -122,8 +122,6 @@ class ChatMessageViewModelAdapter {
122
122
  message,
123
123
  processedLabel,
124
124
  language,
125
- failedLabel: t("chatProcessSummaryError", language),
126
- stoppedLabel: t("chatProcessSummaryStopped", language),
127
125
  });
128
126
  const processSummaryLabel = processSummary?.label ?? null;
129
127
  const executionPresentation = buildChatMessageExecutionPresentation({
@@ -31,8 +31,6 @@ const baseAssistantMessage = {
31
31
 
32
32
  const summaryLabels = {
33
33
  processedLabel: "已处理",
34
- failedLabel: "处理失败",
35
- stoppedLabel: "已停止",
36
34
  language: "zh" as const,
37
35
  };
38
36
 
@@ -135,8 +133,8 @@ describe("buildChatMessageProcessSummary", () => {
135
133
  });
136
134
 
137
135
  it.each([
138
- ["failed", "处理失败"], ["aborted", "已停止"], ["completed", "已处理"],
139
- ] as const)("uses the recorded %s outcome", (outcome, expected) => {
136
+ ["failed", "已处理"], ["aborted", "已处理"], ["completed", "已处理"],
137
+ ] as const)("keeps the process summary neutral for a %s outcome", (outcome, expected) => {
140
138
  const message: NcpMessage = {
141
139
  ...baseAssistantMessage,
142
140
  metadata: { ai_execution: createUnavailableNcpAiExecutionMetadata({
@@ -146,9 +144,9 @@ describe("buildChatMessageProcessSummary", () => {
146
144
  expect(buildChatMessageProcessSummary({ ...summaryLabels, message })?.label).toBe(expected);
147
145
  });
148
146
 
149
- it("shows failure for an error message without execution metadata", () => {
147
+ it("keeps the process summary neutral for an error message", () => {
150
148
  expect(buildChatMessageProcessSummary({
151
149
  ...summaryLabels, message: { ...baseAssistantMessage, status: "error" },
152
- })?.label).toBe("处理失败");
150
+ })?.label).toBe("已处理");
153
151
  });
154
152
  });
@@ -1,4 +1,6 @@
1
1
  import type { ChatMessageMoreActionsViewModel } from "@nextclaw/agent-chat-ui";
2
+ import { createElement } from "react";
3
+ import { Braces } from "lucide-react";
2
4
  import {
3
5
  readNcpAiExecutionMetadata,
4
6
  type NcpAiExecutionMetadata,
@@ -159,6 +161,7 @@ export function buildChatMessageExecutionPresentation(params: {
159
161
  items: [
160
162
  {
161
163
  key: "ai-execution-metadata",
164
+ icon: createElement(Braces, { className: "h-4 w-4" }),
162
165
  label: labels.viewMetadata,
163
166
  dialog: {
164
167
  title: labels.metadataTitle,
@@ -1,12 +1,10 @@
1
- import { readNcpAiExecutionMetadata, type NcpMessage } from "@nextclaw/ncp";
1
+ import type { NcpMessage } from "@nextclaw/ncp";
2
2
  import type { ChatMessageProcessSummarySource } from "@/features/chat/types/chat-message.types";
3
3
  import { t, type I18nLanguage } from "@/shared/lib/i18n";
4
4
 
5
5
  type BuildChatMessageProcessSummaryParams = {
6
6
  message: NcpMessage;
7
7
  processedLabel: string;
8
- failedLabel: string;
9
- stoppedLabel: string;
10
8
  language: I18nLanguage;
11
9
  };
12
10
 
@@ -78,8 +76,6 @@ function formatLifecycleDuration(message: NcpMessage, language: I18nLanguage): s
78
76
  * and must not be mixed into this summary.
79
77
  */
80
78
  export function buildChatMessageProcessSummary({
81
- failedLabel,
82
- stoppedLabel,
83
79
  message,
84
80
  processedLabel,
85
81
  language,
@@ -88,11 +84,7 @@ export function buildChatMessageProcessSummary({
88
84
  return undefined;
89
85
  }
90
86
  const duration = formatLifecycleDuration(message, language);
91
- const outcome = readNcpAiExecutionMetadata(message.metadata)?.outcome;
92
- const statusLabel = message.status === "error" || outcome === "failed"
93
- ? failedLabel
94
- : outcome === "aborted" ? stoppedLabel : processedLabel;
95
87
  return {
96
- label: duration ? `${statusLabel} ${duration}` : statusLabel,
88
+ label: duration ? `${processedLabel} ${duration}` : processedLabel,
97
89
  };
98
90
  }
@@ -189,6 +189,8 @@ export function buildChatMessageTexts(
189
189
  copiedMessageLabel: t("chatMessageCopied"),
190
190
  excerptCharacterCountTemplate: t("chatWorkspaceExcerptCharacterCount"),
191
191
  mermaidDiagramLabel: t("chatMermaidDiagram"),
192
+ frontmatterLabel: t("chatMarkdownFrontmatter"),
193
+ detailsLabel: t("chatMarkdownDetails"),
192
194
  mermaidExpandLabel: t("chatMermaidExpand"),
193
195
  mermaidLoadingLabel: t("chatMermaidLoading"),
194
196
  mermaidRenderErrorLabel: t("chatMermaidRenderError"),
@@ -1,4 +1,6 @@
1
1
  import type { ChatMessageMoreActionsViewModel } from "@nextclaw/agent-chat-ui";
2
+ import { createElement } from "react";
3
+ import { Zap } from "lucide-react";
2
4
  import {
3
5
  readNcpAiExecutionMetadata,
4
6
  readNcpRunTriggerMetadata,
@@ -106,6 +108,7 @@ export function buildChatMessageTriggerDetails(params: {
106
108
  triggerLabel: labels.moreActions,
107
109
  items: [{
108
110
  key: "run-trigger-metadata",
111
+ icon: createElement(Zap, { className: "h-4 w-4" }),
109
112
  label: labels.viewTrigger,
110
113
  dialog: {
111
114
  title: labels.title,
@@ -175,6 +175,7 @@ export function ChatSidebarProjectGroups(props: ChatSidebarProjectGroupsProps) {
175
175
  {group.projectId ? (
176
176
  <Link
177
177
  to={`/projects/${encodeURIComponent(group.projectId)}/overview`}
178
+ state={{ returnTo: `${location.pathname}${location.search}` }}
178
179
  className="flex h-full min-w-0 flex-1 items-center gap-1.5 rounded-md px-1 text-left focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-border"
179
180
  aria-current={isSelected ? "page" : undefined}
180
181
  title={group.projectRoot}
@@ -261,7 +262,7 @@ export function ChatSidebarProjectGroups(props: ChatSidebarProjectGroupsProps) {
261
262
  onClick={(event) => event.stopPropagation()}
262
263
  />
263
264
  </PopoverTrigger>
264
- <ChatPopoverContent align="end" className="w-56 p-2">
265
+ <ChatPopoverContent align="end" variant="menu">
265
266
  <ChatSessionHeaderMenuItem
266
267
  icon={FolderMinus}
267
268
  label={t("projectsRemoveFromList")}
@@ -29,7 +29,7 @@ export function ChatSessionMetadataDialog({
29
29
  return (
30
30
  <Dialog open={open} onOpenChange={onOpenChange}>
31
31
  <DialogContent className="max-w-2xl gap-0 overflow-hidden p-0">
32
- <DialogHeader className="border-b border-border px-5 py-4 pr-12">
32
+ <DialogHeader className="border-b border-border px-5 py-4">
33
33
  <DialogTitle className="text-base">{t('chatSessionMetadataDialogTitle')}</DialogTitle>
34
34
  <DialogDescription className="sr-only">
35
35
  {t('chatSessionMetadataDialogDescription')}
@@ -56,8 +56,8 @@ export function ChatSessionProjectBadge({
56
56
  <PopoverTrigger asChild>
57
57
  <button
58
58
  type="button"
59
- title={projectRoot ?? undefined}
60
- className="min-w-0 max-w-[320px] shrink rounded-full border border-border bg-muted px-2 py-0.5 text-[11px] font-medium text-muted-foreground transition-colors hover:border-primary/35 hover:bg-[var(--interaction-hover)] hover:text-accent-foreground disabled:cursor-not-allowed disabled:opacity-60"
59
+ title={projectRoot ? `${projectName}\n${projectRoot}` : projectName}
60
+ className="min-w-0 max-w-[40%] shrink-0 rounded-full border border-border bg-muted px-2 py-0.5 text-[11px] font-medium text-muted-foreground transition-colors hover:bg-[var(--interaction-hover)] hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-border disabled:cursor-not-allowed disabled:opacity-60"
61
61
  aria-label={t('chatSessionSetProject')}
62
62
  disabled={isProjectPending}
63
63
  >
@@ -1,6 +1,8 @@
1
1
  import { fireEvent, render, screen, waitFor } from '@testing-library/react';
2
2
  import { describe, expect, it, vi } from 'vitest';
3
3
  import { ChatWelcome } from '@/features/chat/features/welcome/components/chat-welcome';
4
+ import { MemoryRouter } from 'react-router-dom';
5
+ import { buildSessionPath } from '@/features/chat/features/session/utils/chat-session-route.utils';
4
6
 
5
7
  vi.mock(
6
8
  '@/features/chat/features/session/components/session-header/chat-session-project-dialog',
@@ -18,12 +20,14 @@ function renderWelcome(
18
20
  overrides: Partial<Parameters<typeof ChatWelcome>[0]> = {},
19
21
  ) {
20
22
  return render(
23
+ <MemoryRouter>
21
24
  <ChatWelcome
22
25
  agents={[
23
26
  { id: 'main', displayName: 'Main' },
24
27
  { id: 'engineer', displayName: 'Engineer' },
25
28
  ]}
26
29
  projectOptions={[]}
30
+ selectedProjectRoot="/tmp/project"
27
31
  selectedAgentId="main"
28
32
  selectedSessionType="native"
29
33
  sessionTypeOptions={sessionTypeOptions}
@@ -31,11 +35,34 @@ function renderWelcome(
31
35
  onSelectPrompt={vi.fn()}
32
36
  onSelectSessionType={vi.fn()}
33
37
  {...overrides}
34
- />,
38
+ />
39
+ </MemoryRouter>,
35
40
  );
36
41
  }
37
42
 
38
43
  describe('ChatWelcome', () => {
44
+ it('offers an open-ended starting point outside an explicit project', () => {
45
+ renderWelcome({ selectedProjectRoot: null });
46
+ expect(screen.getByRole('button', { name: 'Talk about how I’ve been' })).toBeTruthy();
47
+ expect(screen.queryByRole('button', { name: 'Move this project forward' })).toBeNull();
48
+ });
49
+
50
+ it('links to the original session and can dismiss it without removing history', () => {
51
+ renderWelcome({ continuation: { sessionKey: 'session/one', title: 'A real conversation' } });
52
+ const link = screen.getByRole('link', { name: /A real conversation/ });
53
+ expect(link.getAttribute('href')).toBe(buildSessionPath('session/one'));
54
+ expect(screen.getByRole('button', { name: 'Talk about how I’ve been' })).toBeTruthy();
55
+ fireEvent.click(screen.getByRole('button', { name: 'Hide this conversation suggestion for now' }));
56
+ expect(screen.queryByRole('link')).toBeNull();
57
+ expect(screen.getByRole('button', { name: 'Move this project forward' })).toBeTruthy();
58
+ });
59
+
60
+ it('hides starting suggestions while composing but keeps context controls available', () => {
61
+ renderWelcome({ hasDraftContent: true });
62
+ expect(screen.queryByRole('button', { name: 'Move this project forward' })).toBeNull();
63
+ expect(screen.getByRole('combobox', { name: 'Draft agent' })).toBeTruthy();
64
+ expect(screen.getByRole('button', { name: 'Choose session type' })).toBeTruthy();
65
+ });
39
66
  it('renders a lightweight draft agent select and allows switching', () => {
40
67
  const onSelectAgent = vi.fn();
41
68
 
@@ -53,12 +80,10 @@ describe('ChatWelcome', () => {
53
80
  it('renders a single-purpose welcome surface with compact prompt suggestions', () => {
54
81
  renderWelcome({ agents: [{ id: 'main', displayName: 'Main' }] });
55
82
 
56
- expect(screen.getByRole('heading', { name: 'What would you like to get done?' })).toBeTruthy();
83
+ expect(screen.getByRole('heading', { name: 'Where shall we start today?' })).toBeTruthy();
57
84
  expect(screen.queryByText(/Describe the goal/)).toBeNull();
58
85
  expect(screen.getByRole('group', { name: 'Task context' })).toBeTruthy();
59
- const suggestion = screen.getByRole('button', { name: 'Review this project' });
60
- expect(suggestion.className).toContain('rounded-full');
61
- expect(suggestion.className).not.toContain('border');
86
+ const suggestion = screen.getByRole('button', { name: 'Move this project forward' });
62
87
  expect(suggestion.parentElement?.className).toContain('flex-wrap');
63
88
  });
64
89
 
@@ -67,7 +92,7 @@ describe('ChatWelcome', () => {
67
92
 
68
93
  renderWelcome({ onSelectPrompt });
69
94
 
70
- fireEvent.click(screen.getByRole('button', { name: 'Review this project' }));
95
+ fireEvent.click(screen.getByRole('button', { name: 'Move this project forward' }));
71
96
 
72
97
  expect(onSelectPrompt).toHaveBeenCalledWith(
73
98
  expect.stringContaining('next three concrete things'),
@@ -19,11 +19,13 @@ import { useAgents } from "@/shared/hooks/use-agents";
19
19
  import { useProjects } from "@/shared/hooks/use-projects";
20
20
  import type { NcpSessionSummaryView } from "@/shared/lib/api";
21
21
  import { normalizeSessionProjectRootValue } from "@/shared/lib/session-project";
22
+ import { resolveChatWelcomeContinuation } from "@/features/chat/features/welcome/utils/chat-welcome-continuation.utils";
22
23
 
23
24
  const EMPTY_NCP_SESSION_SUMMARIES: NcpSessionSummaryView[] = [];
24
25
 
25
26
  type ChatConversationWelcomeProps = {
26
27
  inputSlot: ReactNode;
28
+ hasDraftContent?: boolean;
27
29
  pendingProjectRoot: string | null;
28
30
  pendingSessionType: string;
29
31
  selectedSessionTypeValue: string | null;
@@ -34,6 +36,7 @@ type ChatConversationWelcomeProps = {
34
36
 
35
37
  export function ChatConversationWelcome({
36
38
  inputSlot,
39
+ hasDraftContent = false,
37
40
  pendingProjectRoot,
38
41
  pendingSessionType,
39
42
  selectedSessionTypeValue,
@@ -50,7 +53,9 @@ export function ChatConversationWelcome({
50
53
  (state) => state.snapshot.configQuery?.data ?? null,
51
54
  );
52
55
  const sessionSummaries = useChatQueryStore(
53
- (state) => state.snapshot.sessionsQuery?.data?.sessions ?? EMPTY_NCP_SESSION_SUMMARIES,
56
+ (state) => state.snapshot.sessionsQuery?.isSuccess
57
+ ? state.snapshot.sessionsQuery.data?.sessions ?? EMPTY_NCP_SESSION_SUMMARIES
58
+ : EMPTY_NCP_SESSION_SUMMARIES,
54
59
  );
55
60
  const sessionTypesData = useChatQueryStore(
56
61
  (state) => state.snapshot.sessionTypesQuery?.data ?? null,
@@ -104,6 +109,13 @@ export function ChatConversationWelcome({
104
109
  agents={availableAgents}
105
110
  defaultProjectRoot={defaultProjectRoot}
106
111
  inputSlot={inputSlot}
112
+ hasDraftContent={hasDraftContent}
113
+ continuation={resolveChatWelcomeContinuation({
114
+ sessions: sessionSummaries,
115
+ projectRoot: selectedProjectRoot ?? defaultProjectRoot,
116
+ defaultProjectRoot,
117
+ agentId: draftAgentId,
118
+ })}
107
119
  projectOptions={projectOptions}
108
120
  selectedAgentId={draftAgentId}
109
121
  selectedProjectRoot={selectedProjectRoot}
@@ -28,7 +28,7 @@ export function ChatWelcomeAgentPicker({
28
28
  <Select value={selectedAgentId} onValueChange={onSelectAgent}>
29
29
  <SelectTrigger
30
30
  aria-label={t('chatDraftAgentTitle')}
31
- className="h-auto w-auto min-w-0 gap-1 rounded-lg border-0 bg-transparent px-2 py-1.5 text-muted-foreground shadow-none hover:bg-[var(--interaction-hover)] hover:text-accent-foreground focus:ring-0"
31
+ className="h-9 w-auto min-w-0 gap-1.5 rounded-lg border-0 bg-transparent px-2 py-1.5 text-sm font-medium text-foreground/80 shadow-none hover:bg-[var(--interaction-hover)] hover:text-foreground focus:ring-1 focus:ring-border"
32
32
  >
33
33
  <span className="sr-only">{t('chatDraftAgentTitle')}</span>
34
34
  <div className="flex min-w-0 items-center gap-1.5">
@@ -43,7 +43,7 @@ export function ChatWelcomeProjectPicker({
43
43
  <PopoverTrigger asChild>
44
44
  <button
45
45
  type="button"
46
- className="inline-flex min-w-0 max-w-full items-center gap-1.5 rounded-lg px-2 py-1.5 text-left font-medium text-muted-foreground transition-colors hover:bg-[var(--interaction-hover)] hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-border"
46
+ className="inline-flex h-9 min-w-0 max-w-full items-center gap-1.5 rounded-lg px-2 py-1.5 text-left text-sm font-medium text-foreground/80 transition-colors hover:bg-[var(--interaction-hover)] hover:text-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-border"
47
47
  title={projectRoot ?? undefined}
48
48
  aria-label={t('chatWelcomeProjectPickerLabel')}
49
49
  disabled={!selectable || isSaving}
@@ -1,9 +1,14 @@
1
+ import { useState } from 'react';
2
+ import { Link } from 'react-router-dom';
3
+ import { ArrowUpRight, History, X } from 'lucide-react';
4
+ import { buildSessionPath } from '@/features/chat/features/session/utils/chat-session-route.utils';
5
+ import type { ChatWelcomeContinuation } from '@/features/chat/features/welcome/utils/chat-welcome-continuation.utils';
1
6
  import { t } from '@/shared/lib/i18n';
2
7
 
3
8
  const WELCOME_PROMPT_SUGGESTIONS = [
4
9
  {
5
- labelKey: 'chatWelcomeSuggestion1Label' as const,
6
- promptKey: 'chatWelcomeSuggestion1Prompt' as const,
10
+ labelKey: 'chatWelcomeOpenStartLabel' as const,
11
+ promptKey: 'chatWelcomeOpenStartPrompt' as const,
7
12
  },
8
13
  {
9
14
  labelKey: 'chatWelcomeSuggestion2Label' as const,
@@ -17,21 +22,59 @@ const WELCOME_PROMPT_SUGGESTIONS = [
17
22
 
18
23
  export function ChatWelcomePromptSuggestions({
19
24
  onSelectPrompt,
25
+ hasDraftContent = false,
26
+ hasProject = false,
27
+ continuation,
20
28
  }: {
21
29
  onSelectPrompt: (prompt: string) => void;
30
+ hasDraftContent?: boolean;
31
+ hasProject?: boolean;
32
+ continuation?: ChatWelcomeContinuation | null;
22
33
  }) {
34
+ const [dismissedSessionKey, setDismissedSessionKey] = useState<string | null>(null);
35
+ const showContinuation = continuation && continuation.sessionKey !== dismissedSessionKey;
23
36
  return (
24
- <div className="mt-5 flex flex-wrap justify-center gap-2">
25
- {WELCOME_PROMPT_SUGGESTIONS.map((suggestion) => (
26
- <button
27
- key={suggestion.labelKey}
28
- type="button"
29
- onClick={() => onSelectPrompt(t(suggestion.promptKey))}
30
- className="rounded-full bg-muted/55 px-3.5 py-2 text-xs font-medium text-muted-foreground transition-colors hover:bg-[var(--interaction-hover)] hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2"
31
- >
32
- {t(suggestion.labelKey)}
33
- </button>
34
- ))}
37
+ <div className={`mt-3 min-h-32 ${hasDraftContent ? 'invisible' : ''}`} aria-hidden={hasDraftContent || undefined}>
38
+ <div className="flex flex-wrap justify-center gap-x-2 gap-y-1">
39
+ {WELCOME_PROMPT_SUGGESTIONS.map((suggestion, index) => {
40
+ const projectStart = index === 2 && hasProject;
41
+ return (
42
+ <button
43
+ key={suggestion.labelKey}
44
+ type="button"
45
+ onClick={() => onSelectPrompt(t(projectStart ? 'chatWelcomeSuggestion1Prompt' : suggestion.promptKey))}
46
+ className="rounded-lg bg-muted/70 px-3 py-1.5 text-[13px] font-normal leading-5 text-foreground/80 transition-colors hover:bg-[var(--interaction-hover)] hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2"
47
+ >
48
+ {t(projectStart ? 'chatWelcomeSuggestion1Label' : suggestion.labelKey)}
49
+ </button>
50
+ );
51
+ })}
52
+ </div>
53
+ {continuation ? (
54
+ <div className={`mx-auto mt-5 flex min-w-0 max-w-lg items-center gap-1 border-t border-border/50 pt-3 ${showContinuation ? '' : 'invisible'}`} aria-hidden={!showContinuation || undefined}>
55
+ <Link
56
+ to={buildSessionPath(continuation.sessionKey)}
57
+ title={t('chatWelcomeContinueHint')}
58
+ className="flex min-w-0 flex-1 items-center gap-3 rounded-lg px-2 py-2 transition-colors hover:bg-[var(--interaction-hover)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
59
+ >
60
+ <History aria-hidden="true" className="h-4 w-4 shrink-0 text-muted-foreground" />
61
+ <span className="min-w-0 flex-1">
62
+ <span className="block text-xs text-muted-foreground">{t('chatWelcomeContinueLabel')}</span>
63
+ <span className="mt-1 block truncate text-sm text-foreground">{continuation.title}</span>
64
+ </span>
65
+ <ArrowUpRight aria-hidden="true" className="h-4 w-4 shrink-0 text-muted-foreground" />
66
+ </Link>
67
+ <button
68
+ type="button"
69
+ aria-label={t('chatWelcomeDismissContinue')}
70
+ title={t('chatWelcomeDismissContinue')}
71
+ onClick={() => setDismissedSessionKey(continuation.sessionKey)}
72
+ className="mr-1 flex h-9 w-9 shrink-0 items-center justify-center rounded-lg text-muted-foreground hover:bg-[var(--interaction-hover)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
73
+ >
74
+ <X aria-hidden="true" className="h-3.5 w-3.5" />
75
+ </button>
76
+ </div>
77
+ ) : null}
35
78
  </div>
36
79
  );
37
80
  }
@@ -1,5 +1,5 @@
1
1
  import { useState } from 'react';
2
- import { Bot, ChevronDown } from 'lucide-react';
2
+ import { Cpu, ChevronDown } from 'lucide-react';
3
3
  import { SessionContextIconNode } from '@/features/chat/features/session/components/session-context-icon';
4
4
  import { ChatSessionTypeOptionItem } from '@/features/chat/features/session-type/components/chat-session-type-option-item';
5
5
  import type { ChatSessionTypeOption } from '@/features/chat/features/session-type/utils/chat-session-type.utils';
@@ -39,7 +39,7 @@ export function ChatWelcomeSessionTypePicker({
39
39
  <PopoverTrigger asChild>
40
40
  <button
41
41
  type="button"
42
- className="inline-flex min-w-0 max-w-full items-center gap-1.5 rounded-lg px-2 py-1.5 text-left font-medium text-muted-foreground transition-colors hover:bg-[var(--interaction-hover)] hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-border"
42
+ className="inline-flex h-9 min-w-0 max-w-full items-center gap-1.5 rounded-lg px-2 py-1.5 text-left text-sm font-medium text-foreground/80 transition-colors hover:bg-[var(--interaction-hover)] hover:text-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-border"
43
43
  aria-label={t('chatWelcomeSessionTypePickerLabel')}
44
44
  >
45
45
  {selectedOption.icon?.src ? (
@@ -53,7 +53,7 @@ export function ChatWelcomeSessionTypePicker({
53
53
  className="h-4 w-4"
54
54
  />
55
55
  ) : (
56
- <Bot className="h-4 w-4 shrink-0" />
56
+ <Cpu className="h-3.5 w-3.5 shrink-0" />
57
57
  )}
58
58
  <span className="truncate">{selectedOption.label}</span>
59
59
  <ChevronDown className="h-3.5 w-3.5 shrink-0 text-muted-foreground/70" />
@@ -9,12 +9,15 @@ import type { ChatWelcomeProjectOption } from '@/features/chat/features/welcome/
9
9
  import type { ChatSessionTypeOption } from '@/features/chat/features/session-type/utils/chat-session-type.utils';
10
10
  import type { AgentProfileView } from '@/shared/lib/api';
11
11
  import { t } from '@/shared/lib/i18n';
12
+ import type { ChatWelcomeContinuation } from '@/features/chat/features/welcome/utils/chat-welcome-continuation.utils';
12
13
 
13
14
  type SessionTypeOption = ChatSessionTypeOption;
14
15
 
15
16
  type ChatWelcomeProps = {
16
17
  agents: AgentProfileView[];
17
18
  inputSlot?: ReactNode;
19
+ hasDraftContent?: boolean;
20
+ continuation?: ChatWelcomeContinuation | null;
18
21
  defaultProjectRoot?: string | null;
19
22
  projectOptions: readonly ChatWelcomeProjectOption[];
20
23
  selectedAgentId: string;
@@ -31,6 +34,8 @@ export function ChatWelcome({
31
34
  agents,
32
35
  defaultProjectRoot,
33
36
  inputSlot,
37
+ hasDraftContent = false,
38
+ continuation,
34
39
  projectOptions,
35
40
  selectedAgentId,
36
41
  selectedProjectRoot,
@@ -64,18 +69,18 @@ export function ChatWelcome({
64
69
  <div className="flex min-h-full items-center justify-center px-4 py-10 sm:p-8">
65
70
  <div className="w-full min-w-0 max-w-[min(680px,100%)]">
66
71
  <div className="text-center">
67
- <h2 className="text-2xl font-semibold leading-tight text-foreground sm:text-[2rem]">
72
+ <h2 className="text-2xl font-[number:var(--welcome-title-weight)] leading-tight text-[hsl(var(--welcome-title-foreground))] sm:text-[1.75rem]">
68
73
  {t('chatWelcomeTitle')}
69
74
  </h2>
70
75
  </div>
71
76
 
72
77
  {inputSlot ? <div className="mt-6">{inputSlot}</div> : null}
73
78
 
74
- <div className="mt-2 flex justify-center px-1">
79
+ <div className="mt-1.5 flex justify-center px-1">
75
80
  <div
76
81
  role="group"
77
82
  aria-label={t('chatWelcomeContextLabel')}
78
- className="flex min-w-0 max-w-full flex-wrap items-center justify-center gap-0.5 rounded-xl bg-muted/40 p-1 text-sm text-muted-foreground"
83
+ className="flex min-w-0 max-w-full flex-wrap items-center justify-center gap-x-1 gap-y-0.5 text-xs text-muted-foreground"
79
84
  >
80
85
  {onSelectProjectRoot ? (
81
86
  <ChatWelcomeProjectPicker
@@ -101,7 +106,12 @@ export function ChatWelcome({
101
106
  </div>
102
107
  </div>
103
108
 
104
- <ChatWelcomePromptSuggestions onSelectPrompt={onSelectPrompt} />
109
+ <ChatWelcomePromptSuggestions
110
+ hasDraftContent={hasDraftContent}
111
+ hasProject={Boolean(selectedProjectRoot)}
112
+ continuation={continuation}
113
+ onSelectPrompt={onSelectPrompt}
114
+ />
105
115
  </div>
106
116
 
107
117
  {onSelectProjectRoot ? (
@@ -0,0 +1,40 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import type { NcpSessionSummaryView } from '@/shared/lib/api';
3
+ import { resolveChatWelcomeContinuation } from '@/features/chat/features/welcome/utils/chat-welcome-continuation.utils';
4
+
5
+ const summary = (overrides: Partial<NcpSessionSummaryView> = {}): NcpSessionSummaryView => ({
6
+ sessionId: 'original', agentId: 'main', messageCount: 2,
7
+ updatedAt: '2026-09-16T10:00:00Z',
8
+ metadata: { label: 'Project plan', project_root: '/project' },
9
+ ...overrides,
10
+ });
11
+ const resolve = (sessions: NcpSessionSummaryView[], projectRoot = '/project') =>
12
+ resolveChatWelcomeContinuation({ sessions, projectRoot, defaultProjectRoot: '/default', agentId: 'main' });
13
+
14
+ describe('resolveChatWelcomeContinuation', () => {
15
+ it('selects by message activity without changing source ordering', () => {
16
+ const sessions = [summary({ sessionId: 'old', lastMessageAt: '2026-09-10T10:00:00Z' }), summary()];
17
+ expect(resolve(sessions)?.sessionKey).toBe('original');
18
+ expect(sessions[0].sessionId).toBe('old');
19
+ });
20
+
21
+ it('excludes other agents, projects, empty, untitled and child sessions', () => {
22
+ expect(resolve([
23
+ summary({ agentId: 'other' }), summary({ messageCount: 0 }),
24
+ summary({ metadata: { project_root: '/other', label: 'Other' } }),
25
+ summary({ metadata: { project_root: '/project' } }),
26
+ summary({ metadata: { project_root: '/project', label: 'Child', parentSessionId: 'parent' } }),
27
+ ])).toBeNull();
28
+ });
29
+
30
+ it('uses the default workspace for old sessions without project metadata', () => {
31
+ const sessions = [summary({ metadata: { label: 'Previous discussion' } })];
32
+ expect(resolve(sessions, '/default')?.title).toBe('Previous discussion');
33
+ expect(resolve(sessions)).toBeNull();
34
+ });
35
+
36
+ it('respects a working directory when project metadata is absent', () => {
37
+ expect(resolve([summary({ metadata: { label: 'Working directory' }, workingDir: '/elsewhere' })])).toBeNull();
38
+ expect(resolve([])).toBeNull();
39
+ });
40
+ });
@@ -0,0 +1,21 @@
1
+ import { adaptNcpSessionSummaries } from '@/features/chat/features/session/utils/ncp-session-adapter.utils';
2
+ import type { NcpSessionSummaryView } from '@/shared/lib/api';
3
+
4
+ export type ChatWelcomeContinuation = { sessionKey: string; title: string };
5
+
6
+ export function resolveChatWelcomeContinuation(params: {
7
+ sessions: readonly NcpSessionSummaryView[];
8
+ projectRoot: string | null;
9
+ defaultProjectRoot: string | null;
10
+ agentId: string;
11
+ }): ChatWelcomeContinuation | null {
12
+ const candidates = adaptNcpSessionSummaries([...params.sessions]).filter((session) =>
13
+ !session.isChildSession && session.messageCount > 0 && session.label?.trim() &&
14
+ (session.agentId ?? 'main') === params.agentId &&
15
+ (session.projectRoot ?? session.workingDir ?? params.defaultProjectRoot) === params.projectRoot,
16
+ );
17
+ const activityAt = (session: typeof candidates[number]) =>
18
+ Date.parse(session.lastMessageAt ?? session.updatedAt) || 0;
19
+ const recent = candidates.sort((a, b) => activityAt(b) - activityAt(a))[0];
20
+ return recent ? { sessionKey: recent.key, title: recent.label!.trim() } : null;
21
+ }
@@ -359,7 +359,7 @@ function WorkspaceDirectoryBrowserReady({
359
359
 
360
360
  const content = (
361
361
  <div
362
- className="flex h-full min-h-0 flex-col bg-white"
362
+ className="flex h-full min-h-0 flex-col bg-background"
363
363
  onContextMenu={(event) => {
364
364
  if (!(event.target as HTMLElement).closest('[data-workspace-tree-entry]')) clearSelection();
365
365
  }}