@nextclaw/ui 0.27.0 → 0.27.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 (320) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/dist/.vite/manifest.json +1246 -1175
  3. package/dist/assets/{abnfDiagram-VRR7QNED-DmeCRAkd.js → abnfDiagram-VRR7QNED-BT0_ycaq.js} +1 -1
  4. package/dist/assets/account-panel-svU9I6E6.js +1 -0
  5. package/dist/assets/agents-CtI89gpb.js +1 -0
  6. package/dist/assets/agents-DWygCmAK.js +1 -0
  7. package/dist/assets/{aiden0z-pptx-renderer.es-ChX5DHqP.js → aiden0z-pptx-renderer.es-ClCaf_Wh.js} +1 -1
  8. package/dist/assets/api-CI6_8Qt1.js +20 -0
  9. package/dist/assets/app-presenter-provider-_UK6sArj.js +28 -0
  10. package/dist/assets/appearance-settings-page-BhedtMx9.js +1 -0
  11. package/dist/assets/apps-C4c4NXqB.js +1 -0
  12. package/dist/assets/{arc-Cu6Ols44.js → arc-cmSdpq1Z.js} +1 -1
  13. package/dist/assets/architecture-TIHT7OUA-BImoGh10.js +1 -0
  14. package/dist/assets/{architectureDiagram-ZJ3FMSHR-DrNB0eLE.js → architectureDiagram-ZJ3FMSHR-aYglVeoS.js} +1 -1
  15. package/dist/assets/{blockDiagram-677ZJIJ3-08rHt6DJ.js → blockDiagram-677ZJIJ3-Di_PBl5q.js} +1 -1
  16. package/dist/assets/{book-open-ClbJmmxP.js → book-open-i_BjUBqL.js} +1 -1
  17. package/dist/assets/{c4Diagram-LMCZKHZV-CCVgRG-u.js → c4Diagram-LMCZKHZV-CfuxybOL.js} +1 -1
  18. package/dist/assets/{card-BdC01NFF.js → card-DokH7OBp.js} +1 -1
  19. package/dist/assets/channel-BDQry0ob.js +1 -0
  20. package/dist/assets/{channels-list-page-BrIvJlop.js → channels-list-page-B7G9lHUm.js} +2 -2
  21. package/dist/assets/chunk-2Q5K7J3B-DkgSFfIy.js +1 -0
  22. package/dist/assets/{chunk-32BRIVSS-BeCaI-JN.js → chunk-32BRIVSS-CCGc4qg4.js} +1 -1
  23. package/dist/assets/{chunk-52WLFC77-ZXIcYPYs.js → chunk-52WLFC77-aJoxU2Ow.js} +1 -1
  24. package/dist/assets/{chunk-5FCAYU7R-DDwTPcy8.js → chunk-5FCAYU7R-CGwtZSax.js} +1 -1
  25. package/dist/assets/{chunk-5HE753X5-WygrMYPL.js → chunk-5HE753X5-DqaMSMeS.js} +1 -1
  26. package/dist/assets/{chunk-5JV3BV7I-o8T3KZgk.js → chunk-5JV3BV7I-DnabINcd.js} +1 -1
  27. package/dist/assets/{chunk-5TONJI2A-BKFbUT7J.js → chunk-5TONJI2A-BhpKv1k2.js} +1 -1
  28. package/dist/assets/{chunk-5VM5RSS4-DYxCRV86.js → chunk-5VM5RSS4-DvkRPit_.js} +1 -1
  29. package/dist/assets/{chunk-7BUUIJ7U-CvSeJlKZ.js → chunk-7BUUIJ7U-CgDQOLnC.js} +1 -1
  30. package/dist/assets/{chunk-BIQX33UG-BhH3RHCY.js → chunk-BIQX33UG-9FEv5b7T.js} +1 -1
  31. package/dist/assets/{chunk-C7G6YPKG-BTL0-xL3.js → chunk-C7G6YPKG-CNGFAFYL.js} +1 -1
  32. package/dist/assets/{chunk-CQNSW5MT-DWAU4QaD.js → chunk-CQNSW5MT-DovTZx0M.js} +1 -1
  33. package/dist/assets/{chunk-CYSBUYHQ-B7ZBMXTx.js → chunk-CYSBUYHQ-DpLEgYa1.js} +1 -1
  34. package/dist/assets/{chunk-EMLP6XTP-D92HvnHE.js → chunk-EMLP6XTP-D4NuN01W.js} +1 -1
  35. package/dist/assets/{chunk-EX3LRPZG-DfOGKwW8.js → chunk-EX3LRPZG-D8Jf4kc2.js} +1 -1
  36. package/dist/assets/{chunk-FWX5IMBZ-DUuJUrXJ.js → chunk-FWX5IMBZ-BdHymAs0.js} +2 -2
  37. package/dist/assets/{chunk-HOUHSVGY-i3MDyUY5.js → chunk-HOUHSVGY-Dd_Ycn9Z.js} +1 -1
  38. package/dist/assets/{chunk-ICXQ74PX-F_pmNX6t.js → chunk-ICXQ74PX-BRPUu3ff.js} +1 -1
  39. package/dist/assets/{chunk-JG7HCLWE-DygOelrU.js → chunk-JG7HCLWE-DnCasQja.js} +1 -1
  40. package/dist/assets/{chunk-JWPE2WC7-BpiZMeRl.js → chunk-JWPE2WC7-C3CkbEj1.js} +1 -1
  41. package/dist/assets/{chunk-MOJQB5TN-CV10sB1I.js → chunk-MOJQB5TN-Pi--K4-1.js} +1 -1
  42. package/dist/assets/{chunk-MOZMSUNE-Cf1WZNm0.js → chunk-MOZMSUNE-3VTspvlh.js} +1 -1
  43. package/dist/assets/{chunk-OGEWGWER-DuSw54VR.js → chunk-OGEWGWER-BetLLyJt.js} +1 -1
  44. package/dist/assets/{chunk-OSBZ3O6U-_rt_XKSZ.js → chunk-OSBZ3O6U-D1VcupAj.js} +1 -1
  45. package/dist/assets/{chunk-PUDLZKDR-BFPaAGNW.js → chunk-PUDLZKDR-Cy4G8SjV.js} +1 -1
  46. package/dist/assets/{chunk-Q4XR5HBZ-GE0v_E5y.js → chunk-Q4XR5HBZ-UYxXzi7_.js} +1 -1
  47. package/dist/assets/{chunk-QBLGF6JB-BK_FpnlJ.js → chunk-QBLGF6JB-CHiGeRY1.js} +1 -1
  48. package/dist/assets/{chunk-R7FJI6CG-CpYh6rGo.js → chunk-R7FJI6CG-C5kVJus8.js} +1 -1
  49. package/dist/assets/{chunk-RYQCIY6F-htnxL_Cl.js → chunk-RYQCIY6F-C6cuuEBn.js} +1 -1
  50. package/dist/assets/{chunk-U6XO7XAA-wDbE4BZ7.js → chunk-U6XO7XAA-CM0EK7Y0.js} +1 -1
  51. package/dist/assets/{chunk-V7JOEXUC-DDRSVFTB.js → chunk-V7JOEXUC-DWJZIsME.js} +1 -1
  52. package/dist/assets/chunk-VAUOI2AC-CsTIB8qD.js +1 -0
  53. package/dist/assets/{chunk-VR4S4FIN-COdhHR1o.js → chunk-VR4S4FIN-CY5KYeEi.js} +1 -1
  54. package/dist/assets/{chunk-WYO6CB5R-DPDGhyF6.js → chunk-WYO6CB5R-B30N3ZP8.js} +3 -3
  55. package/dist/assets/chunk-XXDRQBXY-DK6dz0Q3.js +1 -0
  56. package/dist/assets/{chunk-YOTPTUD7-D8ir9Nx5.js → chunk-YOTPTUD7-CBu82JpO.js} +1 -1
  57. package/dist/assets/{chunk-ZGVPDNZ5-BuNKD836.js → chunk-ZGVPDNZ5-BN5NX5qA.js} +1 -1
  58. package/dist/assets/{chunk-ZIRB5QZD-DyHzq8jH.js → chunk-ZIRB5QZD-DcDXK29L.js} +1 -1
  59. package/dist/assets/{circle-dashed-nDaMRmP-.js → circle-dashed-Cx8CIEbg.js} +1 -1
  60. package/dist/assets/classDiagram-OUVF2IWQ-BYygHCrp.js +1 -0
  61. package/dist/assets/classDiagram-v2-EOCWNBFH-BgQ0XKYz.js +1 -0
  62. package/dist/assets/{client-DlFB5dW5.js → client-CUJUci3r.js} +1 -1
  63. package/dist/assets/{config-split-page-C06fU9OQ.js → config-split-page-hY1xug28.js} +1 -1
  64. package/dist/assets/{confirm-dialog-DpcmlXhd.js → confirm-dialog-B-ejV666.js} +1 -1
  65. package/dist/assets/{context-menu-DKIEaY7f.js → context-menu-dGVv8klA.js} +9 -9
  66. package/dist/assets/{cose-bilkent-JH36ORCC-B1I1_P6a.js → cose-bilkent-JH36ORCC-CmeJVoTw.js} +1 -1
  67. package/dist/assets/{createLucideIcon-Lnt-4LMC.js → createLucideIcon-reFMl-W-.js} +1 -1
  68. package/dist/assets/cron-DkYSmE-f.js +1 -0
  69. package/dist/assets/cynefin-VYW2F7L2-DKbotNWS.js +1 -0
  70. package/dist/assets/{cynefinDiagram-TSTJHNR4-DcOnhnCo.js → cynefinDiagram-TSTJHNR4-DGOUMgPP.js} +1 -1
  71. package/dist/assets/{dagre-nSYhf9yY.js → dagre-CMxSkbJJ.js} +1 -1
  72. package/dist/assets/{dagre-VKFMJZFB-hAX89lg3.js → dagre-VKFMJZFB-3YUBEXwo.js} +1 -1
  73. package/dist/assets/{desktop-capabilities-page-DPORyZAu.js → desktop-capabilities-page-CYJTQ3nC.js} +1 -1
  74. package/dist/assets/{desktop-update-config-BMTeWZmn.js → desktop-update-config-guKAozXO.js} +1 -1
  75. package/dist/assets/{diagram-FQU43EPY-C1mRMD2k.js → diagram-FQU43EPY-Br1g--ww.js} +1 -1
  76. package/dist/assets/{diagram-G47NLZAW-D2XBn5al.js → diagram-G47NLZAW-D1wJCBgr.js} +1 -1
  77. package/dist/assets/{diagram-NH7WQ7WH-3LmRsmkC.js → diagram-NH7WQ7WH-Cgg2auLH.js} +1 -1
  78. package/dist/assets/{diagram-OA4YK3LP-CFWn-osC.js → diagram-OA4YK3LP-R-wHiWqh.js} +1 -1
  79. package/dist/assets/{diagram-WEI45ONY-BqNRbUvU.js → diagram-WEI45ONY-CsNnKV0P.js} +1 -1
  80. package/dist/assets/{dialog-BNXSPMX_.js → dialog-DLOwdjT1.js} +1 -1
  81. package/dist/assets/{dist-Cuiqqp1O.js → dist-ZaFFmAGl.js} +1 -1
  82. package/dist/assets/doc-browser-B0zg0EXA.js +1 -0
  83. package/dist/assets/doc-browser-context-CTmOmuqa.js +1 -0
  84. package/dist/assets/{docx-preview-CunAATyC.js → docx-preview-kPn_EtAr.js} +1 -1
  85. package/dist/assets/{download-f_sPcoLZ.js → download-DovI3Yvf.js} +1 -1
  86. package/dist/assets/{ebnfDiagram-CCIWWBDH-CYb8IwqD.js → ebnfDiagram-CCIWWBDH-DCyhIPdi.js} +1 -1
  87. package/dist/assets/{erDiagram-Q63AITRT-H47mUoeK.js → erDiagram-Q63AITRT-D0ZJYAUf.js} +1 -1
  88. package/dist/assets/eventmodeling-45OFAUF4-SIhbIFIw.js +1 -0
  89. package/dist/assets/{extensions-B-0xQz3L.js → extensions-BZ3qrM8M.js} +1 -1
  90. package/dist/assets/{external-link-CbVlnlNy.js → external-link-DUD3AbsF.js} +1 -1
  91. package/dist/assets/flowDiagram-23GEKE2U-DdAjkU7I.js +1 -0
  92. package/dist/assets/{form-actions-BoTqELHB.js → form-actions-B8VniHts.js} +1 -1
  93. package/dist/assets/{ganttDiagram-NO4QXBWP-hSX45zfV.js → ganttDiagram-NO4QXBWP-CiXDcXZD.js} +1 -1
  94. package/dist/assets/gitGraph-TEB2WS4Q-DnMNpESZ.js +1 -0
  95. package/dist/assets/{gitGraphDiagram-IHSO6WYX-Br0UW2qg.js → gitGraphDiagram-IHSO6WYX-BSiIV48S.js} +1 -1
  96. package/dist/assets/{i18n-B0V5NkFj.js → i18n-IGQbjBqS.js} +2 -2
  97. package/dist/assets/inbox-CJBCLB1X.js +1 -0
  98. package/dist/assets/{inbox-Bna8iUy0.js → inbox-ClAG7SI_.js} +1 -1
  99. package/dist/assets/info-DKCQHKI2-BSm-69p_.js +1 -0
  100. package/dist/assets/{infoDiagram-FWYZ7A6U-DQmP_67w.js → infoDiagram-FWYZ7A6U-CCIgBCVq.js} +1 -1
  101. package/dist/assets/{ishikawaDiagram-FXEZZL3T-BHAV2oes.js → ishikawaDiagram-FXEZZL3T-nQ_baN5u.js} +1 -1
  102. package/dist/assets/{journeyDiagram-5HDEW3XC-CFHax4_d.js → journeyDiagram-5HDEW3XC-ABueIPzj.js} +1 -1
  103. package/dist/assets/{kanban-definition-HUTT4EX6-B4ZA9rp5.js → kanban-definition-HUTT4EX6-8FKS3fmQ.js} +1 -1
  104. package/dist/assets/katex-DPgHtix3.js +1 -0
  105. package/dist/assets/keyboard-shortcuts-settings-page-B5oxyPS2.js +1 -0
  106. package/dist/assets/{line-BboM0HLF.js → line-Biw8iEUz.js} +1 -1
  107. package/dist/assets/{linear-4K_M1oYM.js → linear-D7CWUHTF.js} +1 -1
  108. package/dist/assets/login-page-BxSMwUsc.js +1 -0
  109. package/dist/assets/{login-page-JNr5JCEw.js → login-page-DYaozpzz.js} +1 -1
  110. package/dist/assets/main-D_zmSVS1.css +1 -0
  111. package/dist/assets/main-gFUvLF57.js +2 -0
  112. package/dist/assets/main-i18n-catalog.config-itg3Gvzp.js +1 -0
  113. package/dist/assets/{map-26GpO1wn.js → map-CDu47gTf.js} +1 -1
  114. package/dist/assets/marketplace-DsAf_rnt.js +1 -0
  115. package/dist/assets/mcp-marketplace-page-BqUz09cj.js +1 -0
  116. package/dist/assets/{mermaid-parser.core-BTJFnvRC.js → mermaid-parser.core-Cc9wjl82.js} +2 -2
  117. package/dist/assets/{mermaid.core-B7ufoXgq.js → mermaid.core-GWlaVWz1.js} +3 -3
  118. package/dist/assets/{mindmap-definition-LN4V7U3C-BHyru6KE.js → mindmap-definition-LN4V7U3C-oqnKsz-R.js} +1 -1
  119. package/dist/assets/{model-config-page-C-wbM6Fe.js → model-config-page-Cduyz2Hy.js} +1 -1
  120. package/dist/assets/{navigation-link-DFLSVIut.js → navigation-link-D-JSWFxj.js} +1 -1
  121. package/dist/assets/{ordinal-BqFe2ePF.js → ordinal-DImbxqBz.js} +1 -1
  122. package/dist/assets/packet-7NZHBO7P-1YQvC6mT.js +1 -0
  123. package/dist/assets/panel-app-i18n-catalog.config-BfLHr1tc.js +1 -0
  124. package/dist/assets/panel-apps-Bg5Fzm3T.js +1 -0
  125. package/dist/assets/panelStandalone-oHxbXvYt.js +2 -0
  126. package/dist/assets/{pegDiagram-2B236MQR-PVTatU5j.js → pegDiagram-2B236MQR-DYAB8CEz.js} +1 -1
  127. package/dist/assets/pie-RZYD4A2V-BGoNOSwu.js +1 -0
  128. package/dist/assets/{pieDiagram-ENE6RG2P-CGc-QCc7.js → pieDiagram-ENE6RG2P-DGsH0Kk5.js} +1 -1
  129. package/dist/assets/plus-DUdl68Eo.js +1 -0
  130. package/dist/assets/{privacy-settings-page-CPdEiouE.js → privacy-settings-page-DG9E8N-F.js} +1 -1
  131. package/dist/assets/projects-DvHyoKIF.js +1 -0
  132. package/dist/assets/{provider-models-XRArsply.js → provider-models-COzjYl29.js} +1 -1
  133. package/dist/assets/{provider-scoped-model-input-C8JI-hus.js → provider-scoped-model-input-Cvy74uj8.js} +1 -1
  134. package/dist/assets/{providers-config-page-fyMlVgvq.js → providers-config-page-7rCwVnhh.js} +1 -1
  135. package/dist/assets/{puzzle-Bk3ud1KI.js → puzzle-C5Wfpjk_.js} +1 -1
  136. package/dist/assets/{pwa-Cx6Fnox4.js → pwa-CAg_9zFs.js} +1 -1
  137. package/dist/assets/{quadrantDiagram-ABIIQ3AL-DGL36riv.js → quadrantDiagram-ABIIQ3AL-BegGcT3l.js} +1 -1
  138. package/dist/assets/radar-I7S5WNFK-uPku9RRq.js +1 -0
  139. package/dist/assets/railroad-3IZDKUUU-DHnmbuIo.js +1 -0
  140. package/dist/assets/railroad-abnf-AHOZXSZD-DiZs--Iw.js +1 -0
  141. package/dist/assets/railroad-ebnf-EBAXGLYW-Nr53gnNE.js +1 -0
  142. package/dist/assets/railroad-peg-LSFZ7HO6-Cm_PuwJS.js +1 -0
  143. package/dist/assets/{railroadDiagram-RFXS5EU6-CyNwvhSb.js → railroadDiagram-RFXS5EU6-B3yJ2yxE.js} +1 -1
  144. package/dist/assets/{refresh-cw-Ca2napNx.js → refresh-cw-OhwaPDn1.js} +1 -1
  145. package/dist/assets/remote-Dhr4K1oK.js +1 -0
  146. package/dist/assets/{requirementDiagram-TGXJPOKE-C31NMKJ0.js → requirementDiagram-TGXJPOKE-DtEyoEXG.js} +1 -1
  147. package/dist/assets/{rotate-cw-DXg7sNUE.js → rotate-cw-aBponvp7.js} +1 -1
  148. package/dist/assets/runtime-config-page-FI2pk3HH.js +1 -0
  149. package/dist/assets/{sankeyDiagram-HTMAVEWB-CwrQ_7PA.js → sankeyDiagram-HTMAVEWB-h9AYS2UB.js} +1 -1
  150. package/dist/assets/{save-DNigywZ9.js → save-394QCMXn.js} +1 -1
  151. package/dist/assets/{search-U9C0oTQJ.js → search-BG2qAlNZ.js} +1 -1
  152. package/dist/assets/{search-config-page-Bdx_RR1q.js → search-config-page-D4LeiB9y.js} +1 -1
  153. package/dist/assets/{secrets-config-page-CvMIcsfJ.js → secrets-config-page-BzGcr5Vw.js} +2 -2
  154. package/dist/assets/security-config-vD_V0BQF.js +1 -0
  155. package/dist/assets/{select-DxwDeKUR.js → select-xUXYV0Gr.js} +1 -1
  156. package/dist/assets/{sequenceDiagram-DBY2YBRQ-EFD482_y.js → sequenceDiagram-DBY2YBRQ-D-uxCkob.js} +1 -1
  157. package/dist/assets/service-apps-panel-BTqXn0pg.js +1 -0
  158. package/dist/assets/{settings-2-BrBUdRoY.js → settings-2-CWOGRRaA.js} +1 -1
  159. package/dist/assets/{sizeCapture-X5ZJPWSS-TDvULTE1.js → sizeCapture-X5ZJPWSS-DBzZytQo.js} +1 -1
  160. package/dist/assets/{src-CI2AvEMj.js → src-zrGbZsfc.js} +1 -1
  161. package/dist/assets/{stateDiagram-2N3HPSRC-JdPsIYHo.js → stateDiagram-2N3HPSRC-DyDy7AHq.js} +1 -1
  162. package/dist/assets/stateDiagram-v2-6OUMAXLB-CtkpO6aA.js +1 -0
  163. package/dist/assets/{status-dot-BbGsrkIX.js → status-dot-CYifnSnt.js} +1 -1
  164. package/dist/assets/{swimlanes-5IMT3BWC-BnxpKeK_.js → swimlanes-5IMT3BWC-BzOduS38.js} +2 -2
  165. package/dist/assets/swimlanesDiagram-G3AALYLV-BSdi6mvi.js +8 -0
  166. package/dist/assets/{tag-chip-BDbxcf6P.js → tag-chip-CjM6lyeh.js} +1 -1
  167. package/dist/assets/theme-provider-BToFZGZG.js +1 -0
  168. package/dist/assets/{timeline-definition-FHXFAJF6-2IL22-uC.js → timeline-definition-FHXFAJF6-CmgnEF2t.js} +1 -1
  169. package/dist/assets/{trash-2-DwSkRfeB.js → trash-2-DvJ6duOW.js} +1 -1
  170. package/dist/assets/treeView-QDETBFTQ-DBoqypPR.js +1 -0
  171. package/dist/assets/treemap-6X3UGDF4-BMeL3P0q.js +1 -0
  172. package/dist/assets/use-config-COusaY5p.js +1 -0
  173. package/dist/assets/{use-desktop-capabilities-D7odgaTB.js → use-desktop-capabilities-B-u6ojjW.js} +1 -1
  174. package/dist/assets/{use-viewport-layout-BANU5E1t.js → use-viewport-layout-IKKGpjzF.js} +1 -1
  175. package/dist/assets/{vennDiagram-L72KCM5P-Cjf6n5Va.js → vennDiagram-L72KCM5P-DwS7D2kg.js} +1 -1
  176. package/dist/assets/wardley-OPB4EBWU-Dqxe7zN6.js +1 -0
  177. package/dist/assets/{wardleyDiagram-EHGQE667-yv4qKbBO.js → wardleyDiagram-EHGQE667-B4X8OOL4.js} +1 -1
  178. package/dist/assets/{x-BqGj6_Vq.js → x-E9VB4HQx.js} +1 -1
  179. package/dist/assets/{xychartDiagram-FW5EYKEG-Bmp8Ck8X.js → xychartDiagram-FW5EYKEG-BjRQ_0nx.js} +1 -1
  180. package/dist/index.html +41 -40
  181. package/dist/panel-standalone.html +16 -16
  182. package/package.json +6 -6
  183. package/src/features/agents/components/__tests__/agents-page.test.tsx +8 -5
  184. package/src/features/agents/components/agent-details-dialog.tsx +19 -15
  185. package/src/features/agents/components/agents-page.tsx +27 -38
  186. package/src/features/app-data/components/app-retained-data-section.tsx +2 -2
  187. package/src/features/apps/components/app-marketplace-catalog.tsx +5 -5
  188. package/src/features/apps/components/app-marketplace-detail.tsx +11 -2
  189. package/src/features/apps/components/app-packages-panel.tsx +4 -4
  190. package/src/features/apps/components/apps-panel.tsx +3 -1
  191. package/src/features/chat/components/providers/chat-runtime.provider.tsx +1 -1
  192. package/src/features/chat/features/conversation/components/__tests__/session-conversation-area.test.tsx +0 -1
  193. package/src/features/chat/features/conversation/components/__tests__/session-conversation-input.streaming.test.tsx +13 -2
  194. package/src/features/chat/features/conversation/components/session-conversation-area.tsx +8 -1
  195. package/src/features/chat/features/conversation/components/session-conversation-input-top-slot.tsx +44 -0
  196. package/src/features/chat/features/conversation/components/session-conversation-input.tsx +4 -38
  197. package/src/features/chat/features/conversation/hooks/__tests__/use-session-conversation-controller.test.tsx +127 -10
  198. package/src/features/chat/features/conversation/hooks/__tests__/use-session-conversation-input-state.test.tsx +12 -2
  199. package/src/features/chat/features/conversation/hooks/use-session-conversation-controller.ts +88 -73
  200. package/src/features/chat/features/conversation/hooks/use-session-conversation-input-query.ts +5 -15
  201. package/src/features/chat/features/conversation/hooks/use-session-conversation-input-state.ts +53 -13
  202. package/src/features/chat/features/conversation/hooks/use-session-submission-recovery.ts +83 -0
  203. package/src/features/chat/features/conversation/types/session-conversation-input.types.ts +38 -0
  204. package/src/features/chat/features/input/utils/__tests__/ncp-chat-input-availability.utils.test.ts +1 -29
  205. package/src/features/chat/features/input/utils/ncp-chat-input-availability.utils.ts +1 -8
  206. package/src/features/chat/features/message/components/__tests__/chat-message-list-skill-preview.container.test.tsx +14 -1
  207. package/src/features/chat/features/message/hooks/use-chat-inline-token-actions.ts +7 -2
  208. package/src/features/chat/features/session/utils/__tests__/chat-session-route.utils.test.ts +26 -0
  209. package/src/features/chat/features/session/utils/chat-session-preference-governance.utils.ts +14 -8
  210. package/src/features/chat/features/session/utils/chat-session-route.utils.ts +9 -12
  211. package/src/features/chat/features/workspace/components/__tests__/chat-session-workspace-file-preview.selection.test.tsx +14 -2
  212. package/src/features/chat/features/workspace/components/__tests__/session-cron-job-content.test.tsx +5 -1
  213. package/src/features/chat/features/workspace/components/chat-session-workspace-file-preview.tsx +1 -1
  214. package/src/features/chat/features/workspace/components/session-cron-job-content.tsx +6 -22
  215. package/src/features/chat/index.ts +1 -0
  216. package/src/features/chat/pages/__tests__/ncp-chat-page.test.ts +37 -47
  217. package/src/features/chat/pages/ncp-chat-page.tsx +3 -3
  218. package/src/features/chat/stores/__tests__/chat-composer-draft.store.test.ts +67 -1
  219. package/src/features/chat/stores/chat-composer-draft.store.ts +230 -29
  220. package/src/features/cron/components/__tests__/cron-config.test.tsx +8 -2
  221. package/src/features/cron/components/cron-config.tsx +6 -17
  222. package/src/features/cron/components/cron-job-detail-dialog.tsx +10 -9
  223. package/src/features/cron/components/cron-job-resource.tsx +51 -0
  224. package/src/features/cron/index.ts +1 -0
  225. package/src/features/inbox/pages/inbox-page.tsx +8 -5
  226. package/src/features/marketplace/components/__tests__/marketplace-page-detail.test.tsx +1 -0
  227. package/src/features/marketplace/components/__tests__/marketplace-page.test.tsx +1 -0
  228. package/src/features/marketplace/components/mcp/__tests__/mcp-marketplace-page.test.tsx +2 -0
  229. package/src/features/marketplace/components/mcp/mcp-marketplace-card.tsx +2 -2
  230. package/src/features/marketplace/components/mcp/mcp-marketplace-page.tsx +23 -1
  231. package/src/features/marketplace/hooks/use-marketplace-item-detail.ts +11 -0
  232. package/src/features/panel-apps/index.ts +1 -0
  233. package/src/features/projects/components/work/project-work-item-drawer.tsx +7 -10
  234. package/src/features/projects/pages/project-home-page.tsx +18 -23
  235. package/src/features/projects/pages/projects-page.test.tsx +4 -1
  236. package/src/features/right-panel-resources/components/__tests__/system-object-resource.test.tsx +74 -6
  237. package/src/features/right-panel-resources/components/native-object-resource.tsx +134 -0
  238. package/src/features/right-panel-resources/components/page-resource-icon.tsx +1 -1
  239. package/src/features/right-panel-resources/components/system-object-resource.tsx +66 -1
  240. package/src/features/right-panel-resources/components/workspace-file-resource.tsx +9 -0
  241. package/src/features/right-panel-resources/configs/right-panel-resource-routes.config.ts +11 -21
  242. package/src/features/right-panel-resources/managers/__tests__/page-resource.manager.test.ts +14 -4
  243. package/src/features/right-panel-resources/managers/page-resource.manager.ts +38 -21
  244. package/src/features/right-panel-resources/utils/__tests__/right-panel-resource-route-resolver.utils.test.ts +12 -0
  245. package/src/features/service-apps/components/service-apps-panel.tsx +5 -2
  246. package/src/shared/components/doc-browser/__tests__/doc-browser-context.test.tsx +16 -0
  247. package/src/shared/components/doc-browser/__tests__/doc-browser.test.tsx +2 -2
  248. package/src/shared/components/doc-browser/__tests__/resource-link-interceptor.test.tsx +29 -1
  249. package/src/shared/components/doc-browser/stores/doc-browser.store.ts +13 -7
  250. package/src/shared/components/doc-browser/utils/doc-browser-url.utils.ts +2 -1
  251. package/src/shared/components/ui/skeleton.tsx +16 -2
  252. package/src/shared/lib/i18n/locales/en-US/chat.json +8 -0
  253. package/src/shared/lib/i18n/locales/zh-CN/chat.json +8 -0
  254. package/src/shared/lib/transport/__tests__/local-transport.service.test.ts +24 -0
  255. package/src/shared/lib/transport/local-transport.service.ts +8 -6
  256. package/dist/assets/account-panel-BYxGT4m4.js +0 -1
  257. package/dist/assets/api-CG22BskB.js +0 -20
  258. package/dist/assets/app-presenter-provider-CJFjhVYI.js +0 -28
  259. package/dist/assets/appearance-settings-page-BcXr0vHp.js +0 -1
  260. package/dist/assets/architecture-TIHT7OUA-CLjV_xu4.js +0 -1
  261. package/dist/assets/channel-BgpbPyEH.js +0 -1
  262. package/dist/assets/chunk-2Q5K7J3B-DjhMPqAC.js +0 -1
  263. package/dist/assets/chunk-VAUOI2AC-GYimZYx9.js +0 -1
  264. package/dist/assets/chunk-XXDRQBXY-KKCf0HV5.js +0 -1
  265. package/dist/assets/classDiagram-OUVF2IWQ-iRMPRR8M.js +0 -1
  266. package/dist/assets/classDiagram-v2-EOCWNBFH-BguFy_wj.js +0 -1
  267. package/dist/assets/cynefin-VYW2F7L2-CtbjXuCW.js +0 -1
  268. package/dist/assets/doc-browser-CDEapjmj.js +0 -1
  269. package/dist/assets/doc-browser-context-0QDWYorh.js +0 -1
  270. package/dist/assets/eventmodeling-45OFAUF4-Djd6id_u.js +0 -1
  271. package/dist/assets/flowDiagram-23GEKE2U-DUrsM_ZL.js +0 -1
  272. package/dist/assets/gitGraph-TEB2WS4Q-CU6P_2Cm.js +0 -1
  273. package/dist/assets/info-DKCQHKI2-Bj2P_7RC.js +0 -1
  274. package/dist/assets/katex-BprZ-Wwb.js +0 -1
  275. package/dist/assets/keyboard-shortcuts-settings-page-BluWKrSo.js +0 -1
  276. package/dist/assets/login-page-BMHW0Hh0.js +0 -1
  277. package/dist/assets/main-BCX_Untx.css +0 -1
  278. package/dist/assets/main-C5kbmGUV.js +0 -2
  279. package/dist/assets/main-i18n-catalog.config-DO4wNT7T.js +0 -1
  280. package/dist/assets/mcp-marketplace-page-CV-PHKTN.js +0 -1
  281. package/dist/assets/packet-7NZHBO7P-ti5ge2jr.js +0 -1
  282. package/dist/assets/panel-app-i18n-catalog.config-Bo52dPuF.js +0 -1
  283. package/dist/assets/panelStandalone-PuuMF65O.js +0 -2
  284. package/dist/assets/pie-RZYD4A2V-Bnep8TyV.js +0 -1
  285. package/dist/assets/plus-CywSlIbG.js +0 -1
  286. package/dist/assets/projects-D_cd_t4G.js +0 -1
  287. package/dist/assets/radar-I7S5WNFK-BYg-icAP.js +0 -1
  288. package/dist/assets/railroad-3IZDKUUU-_CHO-Fi1.js +0 -1
  289. package/dist/assets/railroad-abnf-AHOZXSZD-hbQDevHe.js +0 -1
  290. package/dist/assets/railroad-ebnf-EBAXGLYW-v4RZkOfi.js +0 -1
  291. package/dist/assets/railroad-peg-LSFZ7HO6-bC9BqkI5.js +0 -1
  292. package/dist/assets/remote-D5MBQNZE.js +0 -1
  293. package/dist/assets/runtime-config-page--ivCxBT4.js +0 -1
  294. package/dist/assets/security-config-CQmMqQ8v.js +0 -1
  295. package/dist/assets/service-apps-panel-CiYO1Be4.js +0 -1
  296. package/dist/assets/stateDiagram-v2-6OUMAXLB-4_aAQfB_.js +0 -1
  297. package/dist/assets/swimlanesDiagram-G3AALYLV-wCqfwsw-.js +0 -8
  298. package/dist/assets/theme-provider-D98XGa-c.js +0 -1
  299. package/dist/assets/treeView-QDETBFTQ-BAyOb14Z.js +0 -1
  300. package/dist/assets/treemap-6X3UGDF4-DgPDM0zS.js +0 -1
  301. package/dist/assets/use-config-D3cNzk2k.js +0 -1
  302. package/dist/assets/wardley-OPB4EBWU-eCElh8z5.js +0 -1
  303. /package/dist/assets/{array-DstOKG9P.js → array-DsNX3jRj.js} +0 -0
  304. /package/dist/assets/{chunk-KEIR6QF5-0TA8nwgf.js → chunk-KEIR6QF5-BOAXXeyv.js} +0 -0
  305. /package/dist/assets/{chunk-Y2CYZVJY-rfGCG6pM.js → chunk-Y2CYZVJY-CHjSjwoV.js} +0 -0
  306. /package/dist/assets/{config-hints-BfGoD7-o.js → config-hints-CaB3I7KP.js} +0 -0
  307. /package/dist/assets/{cytoscape.esm-DdIcNEda.js → cytoscape.esm-j4J9a91l.js} +0 -0
  308. /package/dist/assets/{defaultLocale-CzXix2iJ.js → defaultLocale-Bmxjp-V_.js} +0 -0
  309. /package/dist/assets/{dist-D5dwMwKA.js → dist-DwLj1Wg5.js} +0 -0
  310. /package/dist/assets/{graphlib-BKOe0eFe.js → graphlib-CC0HTn1v.js} +0 -0
  311. /package/dist/assets/{host-capabilities-CnSVTaot.js → host-capabilities-B107nwS3.js} +0 -0
  312. /package/dist/assets/{init-OCFb3zgY.js → init-Biz9Rus1.js} +0 -0
  313. /package/dist/assets/{jszip.min-B2uhSxvV.js → jszip.min-DQvBVV_R.js} +0 -0
  314. /package/dist/assets/{katex-dBrYsE7B.js → katex-BzT-4KBu.js} +0 -0
  315. /package/dist/assets/{middleware-CAXxl-af.js → middleware-B--6mAUM.js} +0 -0
  316. /package/dist/assets/{navigation-history-CzErJuPG.js → navigation-history-CCnxsAKR.js} +0 -0
  317. /package/dist/assets/{panel-app-CSQmm_7E.js → panel-app-BBTg0tVp.js} +0 -0
  318. /package/dist/assets/{path-T5eoIRSJ.js → path-4azsBPZv.js} +0 -0
  319. /package/dist/assets/{rough.esm-DmGXmZEB.js → rough.esm-w1lBArN6.js} +0 -0
  320. /package/dist/assets/{xlsx-CoYacou7.js → xlsx--SB9KlFl.js} +0 -0
@@ -7,7 +7,7 @@ import {
7
7
  import type { SessionEntryView, ThinkingLevel } from '@/shared/lib/api';
8
8
  import type { ChatModelOption } from '@/features/chat/types/chat-input.types';
9
9
  import {
10
- resolveRecentSessionPreferredValue,
10
+ resolveRecentSessionPreferenceSnapshot,
11
11
  resolveSelectedModelValue,
12
12
  resolveSelectedThinkingLevelValue
13
13
  } from '@/features/chat/features/session/utils/chat-session-preference-governance.utils';
@@ -461,50 +461,54 @@ describe('shouldClearPendingProjectRootOverride', () => {
461
461
  });
462
462
  });
463
463
 
464
- describe('resolveRecentSessionPreferredModel', () => {
465
- it('returns the most recent preferred model from the same runtime', () => {
464
+ describe('resolveRecentSessionPreferenceSnapshot', () => {
465
+ it('returns the model and thinking level from the same recent runtime session', () => {
466
466
  const sessions = [
467
467
  createSession({
468
468
  key: 'native-1',
469
469
  sessionType: 'native',
470
470
  preferredModel: 'anthropic/claude-sonnet-4',
471
+ preferredThinking: 'low',
471
472
  updatedAt: '2026-03-18T01:00:00.000Z'
472
473
  }),
473
474
  createSession({
474
475
  key: 'codex-1',
475
476
  sessionType: 'codex',
476
477
  preferredModel: 'openai/gpt-5',
478
+ preferredThinking: 'high',
477
479
  updatedAt: '2026-03-18T03:00:00.000Z'
478
480
  }),
479
481
  createSession({
480
482
  key: 'codex-2',
481
483
  sessionType: 'codex',
482
484
  preferredModel: 'anthropic/claude-sonnet-4',
485
+ preferredThinking: 'medium',
483
486
  updatedAt: '2026-03-18T02:00:00.000Z'
484
487
  })
485
488
  ];
486
489
 
487
490
  expect(
488
- resolveRecentSessionPreferredValue<string>({
491
+ resolveRecentSessionPreferenceSnapshot({
489
492
  sessions,
490
493
  selectedSessionKey: 'draft',
491
- sessionType: 'codex',
492
- readPreference: (session) => session.preferredModel?.trim() || undefined
494
+ sessionType: 'codex'
493
495
  })
494
- ).toBe('openai/gpt-5');
496
+ ).toEqual({ model: 'openai/gpt-5', thinking: 'high' });
495
497
  });
496
498
 
497
- it('ignores the currently selected session and sessions without preferred models', () => {
499
+ it('ignores the current session and does not borrow thinking from an older model', () => {
498
500
  const sessions = [
499
501
  createSession({
500
502
  key: 'codex-current',
501
503
  sessionType: 'codex',
502
504
  preferredModel: 'openai/gpt-5',
505
+ preferredThinking: 'high',
503
506
  updatedAt: '2026-03-18T03:00:00.000Z'
504
507
  }),
505
508
  createSession({
506
- key: 'codex-empty',
509
+ key: 'codex-thinking-only',
507
510
  sessionType: 'codex',
511
+ preferredThinking: 'high',
508
512
  updatedAt: '2026-03-18T04:00:00.000Z'
509
513
  }),
510
514
  createSession({
@@ -512,17 +516,37 @@ describe('resolveRecentSessionPreferredModel', () => {
512
516
  sessionType: 'codex',
513
517
  preferredModel: 'anthropic/claude-sonnet-4',
514
518
  updatedAt: '2026-03-18T02:00:00.000Z'
519
+ }),
520
+ createSession({
521
+ key: 'codex-older-thinking',
522
+ sessionType: 'codex',
523
+ preferredModel: 'openai/gpt-5',
524
+ preferredThinking: 'medium',
525
+ updatedAt: '2026-03-18T01:00:00.000Z'
515
526
  })
516
527
  ];
517
528
 
518
529
  expect(
519
- resolveRecentSessionPreferredValue<string>({
530
+ resolveRecentSessionPreferenceSnapshot({
520
531
  sessions,
521
532
  selectedSessionKey: 'codex-current',
522
- sessionType: 'codex',
523
- readPreference: (session) => session.preferredModel?.trim() || undefined
533
+ sessionType: 'codex'
524
534
  })
525
- ).toBe('anthropic/claude-sonnet-4');
535
+ ).toEqual({ model: 'anthropic/claude-sonnet-4' });
536
+ });
537
+
538
+ it('returns no fallback when the runtime has no persisted model', () => {
539
+ expect(
540
+ resolveRecentSessionPreferenceSnapshot({
541
+ sessions: [createSession({
542
+ key: 'codex-thinking-only',
543
+ sessionType: 'codex',
544
+ preferredThinking: 'high'
545
+ })],
546
+ selectedSessionKey: 'draft',
547
+ sessionType: 'codex'
548
+ })
549
+ ).toBeUndefined();
526
550
  });
527
551
  });
528
552
 
@@ -587,37 +611,3 @@ describe('resolveSelectedThinkingLevelValue', () => {
587
611
  ).toBe('medium');
588
612
  });
589
613
  });
590
-
591
- describe('resolveRecentSessionPreferredThinking', () => {
592
- it('returns the most recent preferred thinking from the same runtime', () => {
593
- const sessions = [
594
- createSession({
595
- key: 'native-1',
596
- sessionType: 'native',
597
- preferredThinking: 'low',
598
- updatedAt: '2026-03-18T01:00:00.000Z'
599
- }),
600
- createSession({
601
- key: 'codex-1',
602
- sessionType: 'codex',
603
- preferredThinking: 'high',
604
- updatedAt: '2026-03-18T03:00:00.000Z'
605
- }),
606
- createSession({
607
- key: 'codex-2',
608
- sessionType: 'codex',
609
- preferredThinking: 'medium',
610
- updatedAt: '2026-03-18T02:00:00.000Z'
611
- })
612
- ];
613
-
614
- expect(
615
- resolveRecentSessionPreferredValue<ThinkingLevel>({
616
- sessions,
617
- selectedSessionKey: 'draft',
618
- sessionType: 'codex',
619
- readPreference: (session) => session.preferredThinking ?? undefined
620
- })
621
- ).toBe('high');
622
- });
623
- });
@@ -1,4 +1,4 @@
1
- import { useParams } from "react-router-dom";
1
+ import { useLocation } from "react-router-dom";
2
2
  import {
3
3
  ChatPageLayout,
4
4
  type ChatPageProps,
@@ -10,9 +10,9 @@ import { useUiShowContentEvent } from "@/features/chat/features/ncp/hooks/use-ui
10
10
 
11
11
  export function NcpChatPage({ view }: ChatPageProps) {
12
12
  const presenter = usePresenter();
13
- const { sessionId } = useParams<{ sessionId?: string }>();
13
+ const { pathname } = useLocation();
14
14
  useChatSessionSync({
15
- routeSessionKey: parseSessionKeyFromRoute(sessionId),
15
+ routeSessionKey: parseSessionKeyFromRoute(pathname.startsWith('/chat/') ? pathname.slice('/chat/'.length) : undefined),
16
16
  syncRouteSessionSelection: presenter.chatSessionListManager.syncRouteSessionSelection,
17
17
  });
18
18
  useUiShowContentEvent();
@@ -26,7 +26,73 @@ const EMPTY_DRAFT: ChatComposerDraftSnapshot = {
26
26
  describe('chat composer draft store', () => {
27
27
  beforeEach(() => {
28
28
  window.localStorage.clear();
29
- useChatComposerDraftStore.setState({ drafts: {} });
29
+ useChatComposerDraftStore.setState({ drafts: {}, submissions: {}, recentSubmittedNodes: {} });
30
+ });
31
+
32
+ const submittedNodes = [{ id: 'text-1', type: 'text' as const, text: 'sent text' }];
33
+ const submission = {
34
+ envelope: {
35
+ sessionId: 'session-a',
36
+ idempotencyKey: 'user-one',
37
+ message: {
38
+ id: 'user-one', sessionId: 'session-a', role: 'user' as const,
39
+ status: 'final' as const, timestamp: '2026-09-24T00:00:00.000Z',
40
+ parts: [{ type: 'text' as const, text: 'sent text' }],
41
+ },
42
+ },
43
+ composer: { text: 'sent text', nodes: submittedNodes, attachments: [], selectedSkills: [], skillRecords: [] },
44
+ status: 'sending' as const,
45
+ };
46
+
47
+ it('keeps submitted text out of the draft across late editor updates and acceptance', () => {
48
+ const store = useChatComposerDraftStore.getState();
49
+ const key = 'session:session-a';
50
+ store.updateDraft(key, EMPTY_DRAFT, (draft) => ({ ...draft, ...submission.composer }));
51
+ store.beginSubmission(key, EMPTY_DRAFT, submission);
52
+ expect(useChatComposerDraftStore.getState().drafts[key]?.text).toBe('');
53
+ store.updateDraft(key, EMPTY_DRAFT, (draft) => ({
54
+ ...draft, nodes: [{ ...submittedNodes[0]!, text: '' }], text: '',
55
+ }));
56
+ store.updateDraft(key, EMPTY_DRAFT, (draft) => ({ ...draft, ...submission.composer }));
57
+ expect(useChatComposerDraftStore.getState().drafts[key]?.text).toBe('');
58
+ store.acceptSubmission(key, 'user-one');
59
+ store.updateDraft(key, EMPTY_DRAFT, (draft) => ({ ...draft, ...submission.composer }));
60
+ expect(useChatComposerDraftStore.getState().drafts[key]?.text).toBe('');
61
+ store.updateDraft(key, EMPTY_DRAFT, (draft) => ({
62
+ ...draft, text: 'new input', nodes: [{ ...submittedNodes[0]!, text: 'new input' }],
63
+ }));
64
+ expect(useChatComposerDraftStore.getState().drafts[key]?.text).toBe('new input');
65
+ });
66
+
67
+ it('persists an interrupted send separately from the composer draft', async () => {
68
+ const store = useChatComposerDraftStore.getState();
69
+ const key = 'session:session-a';
70
+ store.beginSubmission(key, EMPTY_DRAFT, submission);
71
+ const persisted = window.localStorage.getItem(STORAGE_KEY)!;
72
+ useChatComposerDraftStore.setState({ drafts: {}, submissions: {}, recentSubmittedNodes: {} });
73
+ window.localStorage.setItem(STORAGE_KEY, persisted);
74
+ await useChatComposerDraftStore.persist.rehydrate();
75
+ const restored = useChatComposerDraftStore.getState();
76
+ expect(restored.drafts[key]?.text).toBe('');
77
+ expect(restored.submissions[key]?.status).toBe('uncertain');
78
+ expect(restored.submissions[key]?.envelope.message.id).toBe('user-one');
79
+ });
80
+
81
+ it('restores a rejected send only when it cannot overwrite newer input', () => {
82
+ const store = useChatComposerDraftStore.getState();
83
+ const key = 'session:session-a';
84
+ store.beginSubmission(key, EMPTY_DRAFT, submission);
85
+ store.failSubmission(key, 'user-one', 'rejected', 'rejected');
86
+ expect(useChatComposerDraftStore.getState().drafts[key]?.text).toBe('sent text');
87
+ expect(useChatComposerDraftStore.getState().submissions[key]).toBeUndefined();
88
+
89
+ store.beginSubmission(key, EMPTY_DRAFT, submission);
90
+ store.updateDraft(key, EMPTY_DRAFT, (draft) => ({
91
+ ...draft, text: 'new input', nodes: [{ ...submittedNodes[0]!, text: 'new input' }],
92
+ }));
93
+ store.failSubmission(key, 'user-one', 'rejected', 'rejected');
94
+ expect(useChatComposerDraftStore.getState().drafts[key]?.text).toBe('new input');
95
+ expect(useChatComposerDraftStore.getState().submissions[key]?.status).toBe('rejected');
30
96
  });
31
97
 
32
98
  it('uses one fixed partition for the uncreated session', () => {
@@ -1,11 +1,12 @@
1
- import type { ChatComposerNode } from '@nextclaw/agent-chat-ui';
2
- import type { NcpDraftAttachment } from '@nextclaw/ncp-react';
3
- import { create } from 'zustand';
4
- import { createJSONStorage, persist } from 'zustand/middleware';
1
+ import type { ChatComposerNode } from "@nextclaw/agent-chat-ui";
2
+ import type { NcpDraftAttachment } from "@nextclaw/ncp-react";
3
+ import type { NcpAgentSendEnvelope } from "@nextclaw/ncp";
4
+ import { create, type StoreApi } from "zustand";
5
+ import { createJSONStorage, persist } from "zustand/middleware";
5
6
 
6
- import type { ThinkingLevel } from '@/shared/lib/api';
7
+ import type { ThinkingLevel } from "@/shared/lib/api";
7
8
 
8
- export const CHAT_NEW_SESSION_DRAFT_KEY = 'new-session';
9
+ export const CHAT_NEW_SESSION_DRAFT_KEY = "new-session";
9
10
 
10
11
  type ChatComposerDraftSkillSelection = {
11
12
  readonly ref: string;
@@ -26,21 +27,57 @@ export type ChatComposerDraftSnapshot = {
26
27
  readonly sendError: string | null;
27
28
  };
28
29
 
30
+ export type ChatComposerSubmission = {
31
+ readonly envelope: NcpAgentSendEnvelope;
32
+ readonly composer: Pick<
33
+ ChatComposerDraftSnapshot,
34
+ "text" | "nodes" | "selectedSkills" | "skillRecords" | "attachments"
35
+ >;
36
+ readonly status: "sending" | "uncertain" | "rejected";
37
+ };
38
+
29
39
  type ChatComposerDraftStore = {
30
40
  drafts: Record<string, ChatComposerDraftSnapshot>;
31
- ensureDraft: (draftKey: string, initialSnapshot: ChatComposerDraftSnapshot) => void;
41
+ submissions: Record<string, ChatComposerSubmission>;
42
+ recentSubmittedNodes: Record<string, string>;
43
+ ensureDraft: (
44
+ draftKey: string,
45
+ initialSnapshot: ChatComposerDraftSnapshot,
46
+ ) => void;
32
47
  updateDraft: (
33
48
  draftKey: string,
34
49
  initialSnapshot: ChatComposerDraftSnapshot,
35
- update: (
36
- snapshot: ChatComposerDraftSnapshot,
37
- ) => ChatComposerDraftSnapshot,
50
+ update: (snapshot: ChatComposerDraftSnapshot) => ChatComposerDraftSnapshot,
38
51
  ) => void;
52
+ beginSubmission: (
53
+ draftKey: string,
54
+ initialSnapshot: ChatComposerDraftSnapshot,
55
+ submission: ChatComposerSubmission,
56
+ ) => void;
57
+ acceptSubmission: (draftKey: string, messageId: string) => void;
58
+ failSubmission: (
59
+ draftKey: string,
60
+ messageId: string,
61
+ status: "uncertain" | "rejected",
62
+ message: string,
63
+ ) => void;
64
+ restoreSubmission: (draftKey: string, messageId: string) => void;
65
+ discardSubmission: (draftKey: string, messageId: string) => void;
39
66
  };
40
67
 
41
- const CHAT_COMPOSER_DRAFT_STORAGE_KEY = 'nextclaw.chat.composer-drafts';
68
+ const CHAT_COMPOSER_DRAFT_STORAGE_KEY = "nextclaw.chat.composer-drafts";
42
69
  const CHAT_COMPOSER_DRAFT_STORAGE_VERSION = 1;
43
70
 
71
+ const isComposerEmpty = (draft: ChatComposerDraftSnapshot): boolean =>
72
+ draft.attachments.length === 0 &&
73
+ draft.selectedSkills.length === 0 &&
74
+ !draft.text.trim();
75
+
76
+ const restoreComposer = (
77
+ draft: ChatComposerDraftSnapshot,
78
+ submission: ChatComposerSubmission,
79
+ ): ChatComposerDraftSnapshot => ({ ...draft, ...submission.composer });
80
+
44
81
  export function resolveChatComposerDraftKey(sessionKey: string | null): string {
45
82
  const normalizedSessionKey = sessionKey?.trim();
46
83
  return normalizedSessionKey
@@ -48,28 +85,180 @@ export function resolveChatComposerDraftKey(sessionKey: string | null): string {
48
85
  : CHAT_NEW_SESSION_DRAFT_KEY;
49
86
  }
50
87
 
88
+ class ChatComposerDraftActions {
89
+ constructor(
90
+ private readonly set: StoreApi<ChatComposerDraftStore>["setState"],
91
+ ) {}
92
+
93
+ ensureDraft: ChatComposerDraftStore["ensureDraft"] = (
94
+ draftKey,
95
+ initialSnapshot,
96
+ ) => {
97
+ this.set((state) =>
98
+ state.drafts[draftKey]
99
+ ? state
100
+ : {
101
+ drafts: {
102
+ ...state.drafts,
103
+ [draftKey]: initialSnapshot,
104
+ },
105
+ },
106
+ );
107
+ };
108
+
109
+ updateDraft: ChatComposerDraftStore["updateDraft"] = (
110
+ draftKey,
111
+ initialSnapshot,
112
+ update,
113
+ ) => {
114
+ this.set((state) => {
115
+ const current = state.drafts[draftKey] ?? initialSnapshot;
116
+ const next = update(current);
117
+ const submittedNodes = state.recentSubmittedNodes[draftKey];
118
+ if (
119
+ submittedNodes &&
120
+ isComposerEmpty(current) &&
121
+ !isComposerEmpty(next) &&
122
+ JSON.stringify(next.nodes) === submittedNodes
123
+ ) {
124
+ return state;
125
+ }
126
+ return {
127
+ drafts: { ...state.drafts, [draftKey]: next },
128
+ recentSubmittedNodes:
129
+ submittedNodes && !isComposerEmpty(next)
130
+ ? Object.fromEntries(
131
+ Object.entries(state.recentSubmittedNodes).filter(
132
+ ([key]) => key !== draftKey,
133
+ ),
134
+ )
135
+ : state.recentSubmittedNodes,
136
+ };
137
+ });
138
+ };
139
+
140
+ beginSubmission: ChatComposerDraftStore["beginSubmission"] = (
141
+ draftKey,
142
+ initialSnapshot,
143
+ submission,
144
+ ) => {
145
+ this.set((state) => ({
146
+ drafts: {
147
+ ...state.drafts,
148
+ [draftKey]: {
149
+ ...(state.drafts[draftKey] ?? initialSnapshot),
150
+ text: "",
151
+ nodes: [],
152
+ selectedSkills: [],
153
+ skillRecords: [],
154
+ attachments: [],
155
+ sendError: null,
156
+ },
157
+ },
158
+ submissions: { ...state.submissions, [draftKey]: submission },
159
+ recentSubmittedNodes: {
160
+ ...state.recentSubmittedNodes,
161
+ [draftKey]: JSON.stringify(submission.composer.nodes),
162
+ },
163
+ }));
164
+ };
165
+
166
+ acceptSubmission: ChatComposerDraftStore["acceptSubmission"] = (
167
+ draftKey,
168
+ messageId,
169
+ ) => {
170
+ this.set((state) => {
171
+ if (state.submissions[draftKey]?.envelope.message.id !== messageId)
172
+ return state;
173
+ const submissions = { ...state.submissions };
174
+ delete submissions[draftKey];
175
+ return { submissions };
176
+ });
177
+ };
178
+
179
+ failSubmission: ChatComposerDraftStore["failSubmission"] = (
180
+ draftKey,
181
+ messageId,
182
+ status,
183
+ message,
184
+ ) => {
185
+ this.set((state) => {
186
+ const submission = state.submissions[draftKey];
187
+ if (!submission || submission.envelope.message.id !== messageId)
188
+ return state;
189
+ const draft = state.drafts[draftKey];
190
+ if (!draft) return state;
191
+ const shouldRestore = status === "rejected" && isComposerEmpty(draft);
192
+ const submissions = { ...state.submissions };
193
+ if (shouldRestore) delete submissions[draftKey];
194
+ else submissions[draftKey] = { ...submission, status };
195
+ const recentSubmittedNodes = { ...state.recentSubmittedNodes };
196
+ if (shouldRestore) delete recentSubmittedNodes[draftKey];
197
+ return {
198
+ submissions,
199
+ recentSubmittedNodes,
200
+ drafts: {
201
+ ...state.drafts,
202
+ [draftKey]: {
203
+ ...(shouldRestore ? restoreComposer(draft, submission) : draft),
204
+ sendError: message,
205
+ },
206
+ },
207
+ };
208
+ });
209
+ };
210
+
211
+ restoreSubmission: ChatComposerDraftStore["restoreSubmission"] = (
212
+ draftKey,
213
+ messageId,
214
+ ) => {
215
+ this.set((state) => {
216
+ const submission = state.submissions[draftKey];
217
+ const draft = state.drafts[draftKey];
218
+ if (
219
+ !submission ||
220
+ submission.status !== "rejected" ||
221
+ submission.envelope.message.id !== messageId ||
222
+ !draft ||
223
+ !isComposerEmpty(draft)
224
+ )
225
+ return state;
226
+ const submissions = { ...state.submissions };
227
+ delete submissions[draftKey];
228
+ const recentSubmittedNodes = { ...state.recentSubmittedNodes };
229
+ delete recentSubmittedNodes[draftKey];
230
+ return {
231
+ submissions,
232
+ recentSubmittedNodes,
233
+ drafts: {
234
+ ...state.drafts,
235
+ [draftKey]: restoreComposer(draft, submission),
236
+ },
237
+ };
238
+ });
239
+ };
240
+
241
+ discardSubmission: ChatComposerDraftStore["discardSubmission"] = (
242
+ draftKey,
243
+ messageId,
244
+ ) => {
245
+ this.set((state) => {
246
+ if (state.submissions[draftKey]?.envelope.message.id !== messageId)
247
+ return state;
248
+ const submissions = { ...state.submissions };
249
+ delete submissions[draftKey];
250
+ return { submissions };
251
+ });
252
+ };
253
+ }
254
+
51
255
  export const useChatComposerDraftStore = create<ChatComposerDraftStore>()(
52
256
  persist(
53
257
  (set) => ({
54
258
  drafts: {},
55
- ensureDraft: (draftKey, initialSnapshot) => {
56
- set((state) => state.drafts[draftKey]
57
- ? state
58
- : {
59
- drafts: {
60
- ...state.drafts,
61
- [draftKey]: initialSnapshot,
62
- },
63
- });
64
- },
65
- updateDraft: (draftKey, initialSnapshot, update) => {
66
- set((state) => ({
67
- drafts: {
68
- ...state.drafts,
69
- [draftKey]: update(state.drafts[draftKey] ?? initialSnapshot),
70
- },
71
- }));
72
- },
259
+ submissions: {},
260
+ recentSubmittedNodes: {},
261
+ ...new ChatComposerDraftActions(set),
73
262
  }),
74
263
  {
75
264
  name: CHAT_COMPOSER_DRAFT_STORAGE_KEY,
@@ -86,6 +275,18 @@ export const useChatComposerDraftStore = create<ChatComposerDraftStore>()(
86
275
  },
87
276
  ]),
88
277
  ),
278
+ submissions: Object.fromEntries(
279
+ Object.entries(state.submissions).map(([draftKey, submission]) => [
280
+ draftKey,
281
+ {
282
+ ...submission,
283
+ status:
284
+ submission.status === "sending"
285
+ ? "uncertain"
286
+ : submission.status,
287
+ },
288
+ ]),
289
+ ),
89
290
  }),
90
291
  },
91
292
  ),
@@ -7,8 +7,10 @@ import type { CronJobView } from "@/shared/lib/api";
7
7
  import { setLanguage } from "@/shared/lib/i18n";
8
8
  import { buildSessionPath } from "@/features/chat";
9
9
  import { CronConfig } from "@/features/cron";
10
+ import { CronJobResource } from "@/features/cron/components/cron-job-resource";
10
11
 
11
12
  const mocks = vi.hoisted(() => ({
13
+ openResource: vi.fn(),
12
14
  confirm: vi.fn(async () => true),
13
15
  createSession: vi.fn(),
14
16
  deleteJob: vi.fn(),
@@ -26,6 +28,7 @@ const mocks = vi.hoisted(() => ({
26
28
  isLoading: false,
27
29
  },
28
30
  }));
31
+ vi.mock("@/app/components/app-presenter-provider", () => ({ useAppPresenter: () => ({ pageResourceManager: { open: mocks.openResource } }) }));
29
32
 
30
33
  vi.mock("@/features/chat", async () => {
31
34
  const routeUtils = await vi.importActual<{
@@ -275,12 +278,15 @@ describe("CronConfig", () => {
275
278
 
276
279
  it("exposes the bound session destination from task details", async () => {
277
280
  const user = userEvent.setup();
278
- renderCronConfig();
281
+ const view = renderCronConfig();
279
282
 
280
283
  await user.click(
281
284
  screen.getByRole("button", { name: "查看任务详情 Agent 产品雷达" }),
282
285
  );
283
- expect(screen.getByRole("dialog")).toBeTruthy();
286
+ expect(mocks.openResource).toHaveBeenCalled();
287
+ view.unmount();
288
+ render(<MemoryRouter><CronJobResource jobId="agent-radar" /></MemoryRouter>);
289
+ expect(screen.getByTestId("cron-job-detail")).toBeTruthy();
284
290
  expect(screen.getByText("当前系统只保存最近一次执行快照。")).toBeTruthy();
285
291
 
286
292
  expect(
@@ -11,7 +11,9 @@ import {
11
11
 
12
12
  import { PageHeader, PageLayout } from "@/app/components/layout/page-layout";
13
13
  import { usePresenter } from "@/features/chat";
14
- import { CronJobDetailDialog } from "@/features/cron/components/cron-job-detail-dialog";
14
+ import { useAppPresenter } from "@/app/components/app-presenter-provider";
15
+ import { useNavigate } from "react-router-dom";
16
+ import { pageResourceFromSystemObject } from "@/features/right-panel-resources";
15
17
  import { CronJobRow } from "@/features/cron/components/cron-job-row";
16
18
  import {
17
19
  CronTaskComposer,
@@ -146,12 +148,13 @@ function CronPagination({
146
148
  }
147
149
 
148
150
  export function CronConfig() {
151
+ const app = useAppPresenter();
152
+ const navigate = useNavigate();
149
153
  const [composerPrompt, setComposerPrompt] = useState("");
150
154
  const [query, setQuery] = useState("");
151
155
  const [status, setStatus] = useState<CronListStatus>("all");
152
156
  const [page, setPage] = useState(0);
153
157
  const [expandedJobId, setExpandedJobId] = useState<string | null>(null);
154
- const [selectedJobId, setSelectedJobId] = useState<string | null>(null);
155
158
  const deferredQuery = useDeferredValue(query.trim());
156
159
  const composerRef = useRef<HTMLInputElement>(null);
157
160
  const presenter = usePresenter();
@@ -166,7 +169,6 @@ export function CronConfig() {
166
169
  const jobs = cronQuery.data?.jobs ?? [];
167
170
  const total = cronQuery.data?.total ?? 0;
168
171
  const summary = cronQuery.data?.summary;
169
- const selectedJob = jobs.find((job) => job.id === selectedJobId) ?? null;
170
172
 
171
173
  const startDraftConversation = () => {
172
174
  const prompt = composerPrompt.trim();
@@ -185,7 +187,6 @@ export function CronConfig() {
185
187
  const handleDelete = (job: CronJobView) => {
186
188
  void cronActions.deleteJob(job, () => {
187
189
  setExpandedJobId(null);
188
- setSelectedJobId(null);
189
190
  if (jobs.length === 1 && page > 0) {
190
191
  setPage(page - 1);
191
192
  }
@@ -333,7 +334,7 @@ export function CronConfig() {
333
334
  expanded={expandedJobId === job.id}
334
335
  job={job}
335
336
  onDelete={handleDelete}
336
- onEdit={(item) => setSelectedJobId(item.id)}
337
+ onEdit={(item) => app.pageResourceManager.open(pageResourceFromSystemObject("cron-job", item.id, item.name || item.id), "default", navigate)}
337
338
  onRun={handleRun}
338
339
  onToggleExpanded={handleToggleExpanded}
339
340
  onToggle={handleToggle}
@@ -409,18 +410,6 @@ export function CronConfig() {
409
410
  </>
410
411
  )}
411
412
 
412
- <CronJobDetailDialog
413
- job={selectedJob}
414
- open={Boolean(selectedJob)}
415
- onOpenChange={(open) => {
416
- if (!open) {
417
- setSelectedJobId(null);
418
- }
419
- }}
420
- onDelete={handleDelete}
421
- onRun={handleRun}
422
- onToggle={handleToggle}
423
- />
424
413
  <cronActions.ConfirmDialog />
425
414
  </PageLayout>
426
415
  );