@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
@@ -1,6 +1,7 @@
1
1
  import { useCallback, useEffect, useMemo, type SetStateAction } from 'react';
2
2
  import type { ChatComposerNode } from '@nextclaw/agent-chat-ui';
3
3
  import type { NcpDraftAttachment } from '@nextclaw/ncp-react';
4
+ import type { NcpAgentSendEnvelope } from '@nextclaw/ncp';
4
5
 
5
6
  import type { ThinkingLevel } from '@/shared/lib/api';
6
7
  import { DEFAULT_SESSION_TYPE } from '@/features/chat/features/session-type/utils/chat-session-type.utils';
@@ -10,6 +11,7 @@ import {
10
11
  resolveChatComposerDraftKey,
11
12
  useChatComposerDraftStore,
12
13
  type ChatComposerDraftSnapshot,
14
+ type ChatComposerSubmission,
13
15
  } from '@/features/chat/stores/chat-composer-draft.store';
14
16
  import {
15
17
  useSessionConversationPreferenceActions,
@@ -38,6 +40,7 @@ export type SessionConversationInputSnapshot = SessionConversationComposerState
38
40
  readonly pendingProjectRoot: string | null;
39
41
  readonly composerFocusRequestId: number;
40
42
  readonly sendError: string | null;
43
+ readonly pendingSubmission?: ChatComposerSubmission | null;
41
44
  };
42
45
 
43
46
  type SessionConversationOwnedInputSnapshot = ChatComposerDraftSnapshot;
@@ -49,7 +52,11 @@ export type SessionConversationInputPatch =
49
52
  export type SessionConversationInputActions = {
50
53
  readonly update: (patch: SessionConversationInputPatch) => void;
51
54
  readonly syncComposer: (composer: SessionConversationComposerState) => void;
52
- readonly resetComposer: () => void;
55
+ readonly beginSubmission: (envelope: NcpAgentSendEnvelope, composer: ChatComposerSubmission['composer']) => void;
56
+ readonly acceptSubmission: (messageId: string) => void;
57
+ readonly failSubmission: (messageId: string, status: 'uncertain' | 'rejected', message: string) => void;
58
+ readonly restoreSubmission: (messageId: string) => void;
59
+ readonly discardSubmission: (messageId: string) => void;
53
60
  readonly restoreComposer: (
54
61
  composer: SessionConversationComposerState & {
55
62
  readonly attachments?: readonly NcpDraftAttachment[];
@@ -176,6 +183,7 @@ function usePersistedConversationInputSnapshot(
176
183
  const snapshot = useChatComposerDraftStore(
177
184
  (state) => state.drafts[draftKey] ?? initialSnapshot,
178
185
  );
186
+ const pendingSubmission = useChatComposerDraftStore((state) => state.submissions[draftKey] ?? null);
179
187
 
180
188
  useEffect(() => {
181
189
  useChatComposerDraftStore.getState().ensureDraft(draftKey, initialSnapshot);
@@ -196,7 +204,35 @@ function usePersistedConversationInputSnapshot(
196
204
  });
197
205
  }, [draftKey, initialSnapshot]);
198
206
 
199
- return { draftKey, snapshot, update };
207
+ const beginSubmission = useCallback((
208
+ envelope: NcpAgentSendEnvelope,
209
+ composer: ChatComposerSubmission['composer'],
210
+ ) => {
211
+ useChatComposerDraftStore.getState().beginSubmission(draftKey, snapshot, {
212
+ envelope, composer, status: 'sending',
213
+ });
214
+ }, [draftKey, snapshot]);
215
+ const acceptSubmission = useCallback((messageId: string) => {
216
+ useChatComposerDraftStore.getState().acceptSubmission(draftKey, messageId);
217
+ }, [draftKey]);
218
+ const failSubmission = useCallback((
219
+ messageId: string,
220
+ status: 'uncertain' | 'rejected',
221
+ message: string,
222
+ ) => {
223
+ useChatComposerDraftStore.getState().failSubmission(draftKey, messageId, status, message);
224
+ }, [draftKey]);
225
+ const restoreSubmission = useCallback((messageId: string) => {
226
+ useChatComposerDraftStore.getState().restoreSubmission(draftKey, messageId);
227
+ }, [draftKey]);
228
+ const discardSubmission = useCallback((messageId: string) => {
229
+ useChatComposerDraftStore.getState().discardSubmission(draftKey, messageId);
230
+ }, [draftKey]);
231
+
232
+ return {
233
+ draftKey, snapshot, pendingSubmission, update,
234
+ beginSubmission, acceptSubmission, failSubmission, restoreSubmission, discardSubmission,
235
+ };
200
236
  }
201
237
 
202
238
  export const useSessionConversationInputState = (
@@ -204,7 +240,10 @@ export const useSessionConversationInputState = (
204
240
  sessionKey: string | null = null,
205
241
  ) => {
206
242
  const pendingProjectRoot = useChatThreadStore((state) => state.snapshot.draftProjectRoot);
207
- const { draftKey, snapshot, update } = usePersistedConversationInputSnapshot(
243
+ const {
244
+ draftKey, snapshot, pendingSubmission, update,
245
+ beginSubmission, acceptSubmission, failSubmission, restoreSubmission, discardSubmission,
246
+ } = usePersistedConversationInputSnapshot(
208
247
  initialPrompt,
209
248
  sessionKey,
210
249
  );
@@ -219,14 +258,6 @@ export const useSessionConversationInputState = (
219
258
  });
220
259
  }, [update]);
221
260
 
222
- const resetComposer = useCallback(() => {
223
- update({
224
- ...EMPTY_COMPOSER_STATE,
225
- attachments: [],
226
- sendError: null,
227
- });
228
- }, [update]);
229
-
230
261
  const restoreComposer = useCallback((
231
262
  composer: SessionConversationComposerState & {
232
263
  readonly attachments?: readonly NcpDraftAttachment[];
@@ -309,7 +340,11 @@ export const useSessionConversationInputState = (
309
340
  const actions = useMemo<SessionConversationInputActions>(() => ({
310
341
  update,
311
342
  syncComposer,
312
- resetComposer,
343
+ beginSubmission,
344
+ acceptSubmission,
345
+ failSubmission,
346
+ restoreSubmission,
347
+ discardSubmission,
313
348
  restoreComposer,
314
349
  applyPromptSuggestion,
315
350
  requestComposerFocusAtEnd,
@@ -325,7 +360,11 @@ export const useSessionConversationInputState = (
325
360
  }), [
326
361
  update,
327
362
  syncComposer,
328
- resetComposer,
363
+ beginSubmission,
364
+ acceptSubmission,
365
+ failSubmission,
366
+ restoreSubmission,
367
+ discardSubmission,
329
368
  restoreComposer,
330
369
  applyPromptSuggestion,
331
370
  requestComposerFocusAtEnd,
@@ -344,6 +383,7 @@ export const useSessionConversationInputState = (
344
383
  inputSnapshot: {
345
384
  ...snapshot,
346
385
  pendingProjectRoot,
386
+ pendingSubmission,
347
387
  },
348
388
  inputActions: actions,
349
389
  };
@@ -0,0 +1,83 @@
1
+ import { useCallback, useEffect, useRef } from 'react';
2
+ import type { NcpAgentSendEnvelope, NcpRunHandle } from '@nextclaw/ncp';
3
+ import { NcpHttpSendError } from '@nextclaw/ncp-http-agent-client';
4
+ import { fetchNcpSessionMessages } from '@/shared/lib/api';
5
+ import { t } from '@/shared/lib/i18n';
6
+ import type { ChatComposerSubmission } from '@/features/chat/stores/chat-composer-draft.store';
7
+
8
+ async function hasAcceptedSubmission(submission: ChatComposerSubmission): Promise<boolean> {
9
+ const sessionId = submission.envelope.sessionId?.trim();
10
+ if (!sessionId) return false;
11
+ try {
12
+ const response = await fetchNcpSessionMessages(sessionId, { limit: 100 });
13
+ return response.messages.some((message) => message.id === submission.envelope.message.id);
14
+ } catch {
15
+ return false;
16
+ }
17
+ }
18
+
19
+ export function useSessionSubmissionRecovery(params: {
20
+ readonly agent: { send: (envelope: NcpAgentSendEnvelope) => Promise<NcpRunHandle | null> };
21
+ readonly submission?: ChatComposerSubmission | null;
22
+ readonly sessionKey: string | null;
23
+ readonly onSessionMaterialized?: (sessionKey: string) => void;
24
+ readonly acceptSubmission: (messageId: string) => void;
25
+ readonly failSubmission: (messageId: string, status: 'uncertain' | 'rejected', message: string) => void;
26
+ readonly restoreSubmission: (messageId: string) => void;
27
+ readonly discardSubmission: (messageId: string) => void;
28
+ readonly setSendError: (message: string | null) => void;
29
+ }) {
30
+ const {
31
+ agent, submission, sessionKey, onSessionMaterialized,
32
+ acceptSubmission, failSubmission, restoreSubmission, discardSubmission, setSendError,
33
+ } = params;
34
+ const retryPendingSubmission = useCallback(async () => {
35
+ if (!submission || submission.status !== 'uncertain') return;
36
+ const { envelope } = submission;
37
+ if (await hasAcceptedSubmission(submission)) {
38
+ acceptSubmission(envelope.message.id);
39
+ if (!sessionKey && envelope.sessionId) onSessionMaterialized?.(envelope.sessionId);
40
+ return;
41
+ }
42
+ if (!envelope.sessionId) {
43
+ setSendError(t('chatSendResultUnknownChild'));
44
+ return;
45
+ }
46
+ try {
47
+ const handle = await agent.send(envelope);
48
+ if (!handle) throw new Error(t('chatSendFailed'));
49
+ acceptSubmission(envelope.message.id);
50
+ if (!sessionKey) onSessionMaterialized?.(handle.sessionId);
51
+ } catch (error) {
52
+ const message = error instanceof Error ? error.message : String(error);
53
+ failSubmission(envelope.message.id,
54
+ error instanceof NcpHttpSendError && error.sendRejected ? 'rejected' : 'uncertain',
55
+ message);
56
+ }
57
+ }, [acceptSubmission, agent, failSubmission, onSessionMaterialized, sessionKey, setSendError, submission]);
58
+
59
+ const checkedSubmissionRef = useRef<string | null>(null);
60
+ useEffect(() => {
61
+ if (!submission || submission.status !== 'uncertain' ||
62
+ checkedSubmissionRef.current === submission.envelope.message.id) return;
63
+ checkedSubmissionRef.current = submission.envelope.message.id;
64
+ void hasAcceptedSubmission(submission).then((accepted) => {
65
+ if (accepted) {
66
+ acceptSubmission(submission.envelope.message.id);
67
+ if (!sessionKey && submission.envelope.sessionId) {
68
+ onSessionMaterialized?.(submission.envelope.sessionId);
69
+ }
70
+ }
71
+ });
72
+ }, [acceptSubmission, onSessionMaterialized, sessionKey, submission]);
73
+
74
+ return {
75
+ retryPendingSubmission,
76
+ restorePendingSubmission: () => {
77
+ if (submission) restoreSubmission(submission.envelope.message.id);
78
+ },
79
+ discardPendingSubmission: () => {
80
+ if (submission?.status === 'rejected') discardSubmission(submission.envelope.message.id);
81
+ },
82
+ };
83
+ }
@@ -0,0 +1,38 @@
1
+ import type { ChatContextWindowIndicator } from '@nextclaw/agent-chat-ui';
2
+ import type { SessionConversationQueuedInput } from '@/features/chat/features/conversation/hooks/use-session-conversation-controller';
3
+ import type { useSessionConversationInputQuery } from '@/features/chat/features/conversation/hooks/use-session-conversation-input-query';
4
+ import type {
5
+ SessionConversationInputActions,
6
+ SessionConversationInputSnapshot,
7
+ } from '@/features/chat/features/conversation/hooks/use-session-conversation-input-state';
8
+
9
+ export type SessionConversationInputController = {
10
+ readonly canEditQueuedInput: boolean;
11
+ readonly canStopGeneration: boolean;
12
+ readonly deleteQueuedInput: (id: string) => void;
13
+ readonly editQueuedInput: (id: string) => void;
14
+ readonly isSending: boolean;
15
+ readonly queuedInputs: readonly SessionConversationQueuedInput[];
16
+ readonly primaryAction: 'continue' | 'send';
17
+ readonly sendDisabled: boolean;
18
+ readonly stopDisabled: boolean;
19
+ readonly send: () => Promise<void> | void;
20
+ readonly sendSteering: () => Promise<void> | void;
21
+ readonly sendPresetMessage: (message: string) => Promise<void> | void;
22
+ readonly retryPendingSubmission: () => Promise<void> | void;
23
+ readonly restorePendingSubmission: () => void;
24
+ readonly discardPendingSubmission: () => void;
25
+ readonly stop: () => Promise<void> | void;
26
+ readonly steerQueuedInput: (id: string) => void;
27
+ };
28
+
29
+ export type SessionConversationInputProps = {
30
+ readonly contextWindow: ChatContextWindowIndicator | null;
31
+ readonly controller: SessionConversationInputController;
32
+ readonly inputActions: SessionConversationInputActions;
33
+ readonly inputQuery: ReturnType<typeof useSessionConversationInputQuery>;
34
+ readonly inputSnapshot: SessionConversationInputSnapshot;
35
+ readonly onContextCompactingChange?: (sessionId: string, isCompacting: boolean) => void;
36
+ readonly placeholder?: string;
37
+ readonly surface?: 'default' | 'embedded';
38
+ };
@@ -23,7 +23,7 @@ function createSnapshot(overrides: Partial<AvailabilitySnapshot> = {}): Availabi
23
23
  }
24
24
 
25
25
  describe('ncp-chat-input-availability.utils', () => {
26
- it('keeps the composer editable during cold start while runtime blocking still prevents send', () => {
26
+ it('keeps editing and sending available when runtime readiness is misreported', () => {
27
27
  const snapshot = createSnapshot({
28
28
  isProviderStateResolved: false,
29
29
  modelOptions: [],
@@ -35,25 +35,7 @@ describe('ncp-chat-input-availability.utils', () => {
35
35
  expect(isNcpChatComposerDisabled(snapshot)).toBe(false);
36
36
  expect(
37
37
  isNcpChatSendDisabled({
38
- snapshot,
39
38
  hasSendableDraft: true,
40
- isRuntimeBlocked: true,
41
- })
42
- ).toBe(true);
43
- });
44
-
45
- it('does not block send only because model options have not loaded yet', () => {
46
- const snapshot = createSnapshot({
47
- isProviderStateResolved: false,
48
- modelOptions: [],
49
- sessionTypeUnavailable: false,
50
- });
51
-
52
- expect(
53
- isNcpChatSendDisabled({
54
- snapshot,
55
- hasSendableDraft: true,
56
- isRuntimeBlocked: false,
57
39
  })
58
40
  ).toBe(false);
59
41
  });
@@ -81,25 +63,15 @@ describe('ncp-chat-input-availability.utils', () => {
81
63
  expect(isNcpChatComposerDisabled(snapshot)).toBe(false);
82
64
  expect(
83
65
  isNcpChatSendDisabled({
84
- snapshot,
85
66
  hasSendableDraft: true,
86
- isRuntimeBlocked: false,
87
67
  })
88
68
  ).toBe(false);
89
69
  });
90
70
 
91
71
  it('blocks send when there is no sendable draft', () => {
92
- const snapshot = createSnapshot({
93
- isProviderStateResolved: true,
94
- modelOptions: [],
95
- sessionTypeUnavailable: true,
96
- });
97
-
98
72
  expect(
99
73
  isNcpChatSendDisabled({
100
- snapshot,
101
74
  hasSendableDraft: false,
102
- isRuntimeBlocked: false,
103
75
  })
104
76
  ).toBe(true);
105
77
  });
@@ -30,13 +30,6 @@ export function isNcpChatComposerDisabled(
30
30
 
31
31
  export function isNcpChatSendDisabled(params: {
32
32
  hasSendableDraft: boolean;
33
- snapshot: ChatInputAvailabilitySnapshot;
34
- isRuntimeBlocked: boolean;
35
33
  }): boolean {
36
- const { hasSendableDraft, isRuntimeBlocked } = params;
37
-
38
- return (
39
- isRuntimeBlocked ||
40
- !hasSendableDraft
41
- );
34
+ return !params.hasSendableDraft;
42
35
  }
@@ -23,6 +23,8 @@ function ChatMessageListContainer({
23
23
  }
24
24
 
25
25
  const captures = vi.hoisted(() => ({
26
+ resolveResource: vi.fn(),
27
+ openResource: vi.fn(),
26
28
  renders: [] as Array<{
27
29
  onInlineTokenClick?: (token: unknown) => void;
28
30
  }>,
@@ -98,6 +100,8 @@ vi.mock("@/shared/lib/i18n", () => ({
98
100
  }));
99
101
 
100
102
  beforeEach(() => {
103
+ captures.resolveResource.mockReset();
104
+ captures.openResource.mockReset();
101
105
  captures.renders = [];
102
106
  captures.openFilePreview.mockReset();
103
107
  captures.fetchSessionSkills.mockReset();
@@ -116,6 +120,15 @@ function clickSkill(token: Record<string, unknown>, sessionKey?: string): void {
116
120
  captures.renders[captures.renders.length - 1]?.onInlineTokenClick?.(token);
117
121
  }
118
122
 
123
+ it("opens a quoted object through the resource opener instead of treating its URI as a local path", () => {
124
+ const page = { uri: "nextclaw://objects/agent/philosopher" };
125
+ captures.resolveResource.mockReturnValue(page);
126
+ clickSkill({ kind: "workspace_excerpt", path: page.uri, excerpt: "Description", label: "Philosopher", startLine: null, endLine: null });
127
+ expect(captures.resolveResource).toHaveBeenCalledWith(page.uri);
128
+ expect(captures.openResource).toHaveBeenCalledWith(page, "default", expect.any(Function));
129
+ expect(captures.openFilePreview).not.toHaveBeenCalled();
130
+ });
131
+
119
132
  it("opens a persisted skill path without consulting the session catalog", () => {
120
133
  clickSkill({
121
134
  kind: "skill", ref: "global:/skills/review", name: "review", source: "global",
@@ -170,4 +183,4 @@ it("shows an error when a persisted v1 skill is no longer resolvable", async ()
170
183
  });
171
184
 
172
185
  vi.mock("react-router-dom", async (importOriginal) => ({ ...(await importOriginal<object>()), useNavigate: () => vi.fn() }));
173
- vi.mock("@/app/components/app-presenter-provider", () => ({ useAppPresenter: () => ({ pageResourceManager: { resolve: vi.fn(), open: vi.fn() } }) }));
186
+ vi.mock("@/app/components/app-presenter-provider", () => ({ useAppPresenter: () => ({ pageResourceManager: { resolve: captures.resolveResource, open: captures.openResource } }) }));
@@ -30,8 +30,13 @@ export function useChatInlineTokenActions(params: {
30
30
  const projectRoot = selectedSession?.projectRoot ?? selectedSession?.workingDir;
31
31
 
32
32
  const handleInlineTokenClick = useCallback((token: ChatInlineTokenViewModel) => {
33
- if ((token.kind === CHAT_UI_RESOURCE_TOKEN_KIND || token.kind === CHAT_SYSTEM_OBJECT_TOKEN_KIND) && "key" in token) {
34
- const page = appPresenter.pageResourceManager.resolve(token.key);
33
+ const resourceUri = (token.kind === CHAT_UI_RESOURCE_TOKEN_KIND || token.kind === CHAT_SYSTEM_OBJECT_TOKEN_KIND) && "key" in token
34
+ ? token.key
35
+ : token.kind === CHAT_WORKSPACE_EXCERPT_TOKEN_KIND && "excerpt" in token && token.path.startsWith("nextclaw://")
36
+ ? token.path
37
+ : null;
38
+ if (resourceUri) {
39
+ const page = appPresenter.pageResourceManager.resolve(resourceUri);
35
40
  if (page) appPresenter.pageResourceManager.open(page, 'default', navigate);
36
41
  return;
37
42
  }
@@ -1,11 +1,37 @@
1
1
  import { describe, expect, it } from 'vitest';
2
2
  import {
3
3
  CHAT_DRAFT_SESSION_PATH,
4
+ buildSessionPanelUrl,
5
+ parseSessionKeyFromPanelUrl,
6
+ encodeSessionRouteId,
4
7
  buildSessionPath,
5
8
  parseSessionKeyFromRoute,
6
9
  } from '@/features/chat/features/session/utils/chat-session-route.utils';
7
10
 
8
11
  describe('chat session route utils', () => {
12
+ it.each(['ncp-mu2x4zdy-9wzl0otb', 'agent:main:会话/一 %2F', 'sid_literal', 'draft'])(
13
+ 'round-trips resource and browser links for %s', (id) => {
14
+ expect(buildSessionPanelUrl(id)).toBe(`nextclaw://sessions/${encodeURIComponent(id)}`);
15
+ expect(parseSessionKeyFromPanelUrl(buildSessionPanelUrl(id))).toBe(id);
16
+ expect(parseSessionKeyFromRoute(buildSessionPath(id).slice('/chat/'.length))).toBe(id);
17
+ expect(parseSessionKeyFromPanelUrl(`nextclaw://chat-session/${encodeSessionRouteId(id)}`)).toBe(id);
18
+ },
19
+ );
20
+ it('keeps ordinary IDs readable in the address bar', () => {
21
+ expect(buildSessionPath('ncp-mu2x4zdy-9wzl0otb')).toBe('/chat/ncp-mu2x4zdy-9wzl0otb');
22
+ });
23
+ it.each(['sessions', 'chat-session', 'objects/chat-session', 'objects/chat-sessions'])(
24
+ 'recovers already delivered %s links', (namespace) => {
25
+ expect(parseSessionKeyFromPanelUrl(`nextclaw://${namespace}/ncp-mu2x4zdy-9wzl0otb`)).toBe('ncp-mu2x4zdy-9wzl0otb');
26
+ },
27
+ );
28
+ it.each(['nextclaw://sessions/', 'nextclaw://sessions/a/b', 'nextclaw://sessions/%ZZ',
29
+ 'nextclaw://sessions/%20', 'nextclaw://chat-session/sid_%%%','nextclaw://sessions/a?other=b',
30
+ 'https://sessions/a', 'nextclaw://user@sessions/a', 'nextclaw://objects/project/a'])(
31
+ 'rejects malformed or unrelated session links: %s', (uri) => {
32
+ expect(parseSessionKeyFromPanelUrl(uri)).toBeNull();
33
+ },
34
+ );
9
35
  it('keeps the draft route separate from real session keys', () => {
10
36
  expect(CHAT_DRAFT_SESSION_PATH).toBe('/chat/draft');
11
37
  expect(parseSessionKeyFromRoute('draft')).toBeNull();
@@ -129,15 +129,17 @@ export function resolveSelectedThinkingLevelValue(params: {
129
129
  });
130
130
  }
131
131
 
132
- export function resolveRecentSessionPreferredValue<T>(params: {
132
+ export function resolveRecentSessionPreferenceSnapshot(params: {
133
133
  sessions: readonly SessionEntryView[];
134
134
  selectedSessionKey?: string | null;
135
135
  sessionType?: string | null;
136
- readPreference: (session: SessionEntryView) => T | null | undefined;
137
- }): T | undefined {
138
- const { sessions, selectedSessionKey, sessionType, readPreference } = params;
136
+ }): {
137
+ model: string;
138
+ thinking?: ThinkingLevel;
139
+ } | undefined {
140
+ const { sessions, selectedSessionKey, sessionType } = params;
139
141
  const targetSessionType = normalizeSessionType(sessionType);
140
- let bestValue: T | undefined;
142
+ let bestValue: { model: string; thinking?: ThinkingLevel } | undefined;
141
143
  let bestTimestamp = Number.NEGATIVE_INFINITY;
142
144
  for (const session of sessions) {
143
145
  if (session.key === selectedSessionKey) {
@@ -146,14 +148,18 @@ export function resolveRecentSessionPreferredValue<T>(params: {
146
148
  if (normalizeSessionType(session.sessionType) !== targetSessionType) {
147
149
  continue;
148
150
  }
149
- const value = readPreference(session);
150
- if (value === null || value === undefined) {
151
+ const model = session.preferredModel?.trim() || undefined;
152
+ const thinking = session.preferredThinking ?? undefined;
153
+ if (model === undefined) {
151
154
  continue;
152
155
  }
153
156
  const updatedAtTimestamp = Date.parse(session.updatedAt);
154
157
  const comparableTimestamp = Number.isFinite(updatedAtTimestamp) ? updatedAtTimestamp : Number.NEGATIVE_INFINITY;
155
158
  if (bestValue === undefined || comparableTimestamp > bestTimestamp) {
156
- bestValue = value;
159
+ bestValue = {
160
+ model,
161
+ ...(thinking ? { thinking } : {}),
162
+ };
157
163
  bestTimestamp = comparableTimestamp;
158
164
  }
159
165
  }
@@ -1,16 +1,9 @@
1
+ import { createSessionResourceUri, parseSessionResourceUri } from '@nextclaw/shared';
2
+
1
3
  const SESSION_ROUTE_PREFIX = 'sid_';
2
4
  export const CHAT_SESSION_PANEL_KIND = 'chat-session';
3
- const SESSION_PANEL_PREFIX = 'nextclaw://chat-session/';
4
-
5
- export function buildSessionPanelUrl(sessionKey: string): string {
6
- return SESSION_PANEL_PREFIX + encodeSessionRouteId(sessionKey);
7
- }
8
-
9
- export function parseSessionKeyFromPanelUrl(url: string): string | null {
10
- return url.startsWith(SESSION_PANEL_PREFIX)
11
- ? decodeSessionRouteId(url.slice(SESSION_PANEL_PREFIX.length))
12
- : null;
13
- }
5
+ export const buildSessionPanelUrl = createSessionResourceUri;
6
+ export const parseSessionKeyFromPanelUrl = parseSessionResourceUri;
14
7
  export const CHAT_DRAFT_SESSION_PATH = '/chat/draft';
15
8
  const DRAFT_SESSION_ROUTE_ID = CHAT_DRAFT_SESSION_PATH.slice('/chat/'.length);
16
9
 
@@ -55,5 +48,9 @@ export function parseSessionKeyFromRoute(routeValue?: string): string | null {
55
48
  }
56
49
 
57
50
  export function buildSessionPath(sessionKey: string): string {
58
- return `/chat/${encodeSessionRouteId(sessionKey)}`;
51
+ // Keep reserved historical route tokens unambiguous; ordinary IDs stay readable.
52
+ const routeId = sessionKey === DRAFT_SESSION_ROUTE_ID || sessionKey.startsWith(SESSION_ROUTE_PREFIX)
53
+ ? encodeSessionRouteId(sessionKey)
54
+ : encodeURIComponent(sessionKey);
55
+ return `/chat/${routeId}`;
59
56
  }
@@ -1,6 +1,6 @@
1
1
  import type { ReactNode } from "react";
2
- import { fireEvent, render, screen } from "@testing-library/react";
3
- import { describe, expect, it, vi } from "vitest";
2
+ import { cleanup, fireEvent, render, screen } from "@testing-library/react";
3
+ import { afterEach, describe, expect, it, vi } from "vitest";
4
4
  import { ChatSessionWorkspaceFilePreview } from "@/features/chat/features/workspace/components/chat-session-workspace-file-preview";
5
5
 
6
6
  const onTextExcerptAdd = vi.fn();
@@ -37,6 +37,18 @@ vi.mock("@nextclaw/agent-chat-ui", () => ({
37
37
  }));
38
38
 
39
39
  describe("ChatSessionWorkspaceFilePreview selection", () => {
40
+ afterEach(cleanup);
41
+
42
+ it("does not offer an action when no composer callback is connected", () => {
43
+ render(<ChatSessionWorkspaceFilePreview
44
+ file={{ key: "boot", parentSessionKey: null, path: "BOOT.md", label: "BOOT.md", viewMode: "preview" }}
45
+ sessionProjectRoot={null}
46
+ sessionWorkingDir={null}
47
+ onFileOpen={vi.fn()}
48
+ />);
49
+ expect(screen.queryByRole("button", { name: "select excerpt" })).toBeNull();
50
+ });
51
+
40
52
  it("uses the resolved absolute path when a shell-style workspace root cannot be compared in the browser", () => {
41
53
  render(
42
54
  <ChatSessionWorkspaceFilePreview
@@ -8,10 +8,14 @@ import { setLanguage } from "@/shared/lib/i18n";
8
8
  import { SessionCronJobContent } from "@/features/chat/features/workspace/components/session-cron-job-content";
9
9
 
10
10
  const mocks = vi.hoisted(() => ({
11
+ openResource: vi.fn(),
11
12
  deleteJob: vi.fn(),
12
13
  runJob: vi.fn(),
13
14
  toggleJob: vi.fn(),
14
15
  }));
16
+ vi.mock("@/app/components/app-presenter-provider", () => ({ useAppPresenter: () => ({ pageResourceManager: { open: mocks.openResource } }) }));
17
+ vi.mock("react-router-dom", () => ({ useNavigate: () => vi.fn() }));
18
+ vi.mock("@/features/right-panel-resources", () => ({ pageResourceFromSystemObject: (type: string, id: string) => ({ uri: `nextclaw://objects/${type}/${id}` }) }));
15
19
 
16
20
  vi.mock("@/features/cron", () => ({
17
21
  CronJobDetailDialog: ({ job, open }: { job: CronJobView | null; open: boolean }) => (
@@ -73,6 +77,6 @@ describe("SessionCronJobContent", () => {
73
77
  expect(mocks.runJob).toHaveBeenCalledWith(job);
74
78
 
75
79
  await user.click(screen.getByRole("button", { name: "查看任务详情" }));
76
- expect(screen.getByRole("dialog").textContent).toContain(job.payload.message);
80
+ expect(mocks.openResource).toHaveBeenCalledWith({ uri: "nextclaw://objects/cron-job/paint-daily" }, "default", expect.any(Function));
77
81
  });
78
82
  });
@@ -455,7 +455,7 @@ export function ChatSessionWorkspaceFilePreview({
455
455
  <div className="flex-1 min-h-0 overflow-hidden">
456
456
  {file.viewMode === 'diff' ? (
457
457
  <WorkspaceDiffBody diffBlock={diffBlock} fileKey={file.key} />
458
- ) : excerptPath && previewText ? (
458
+ ) : excerptPath && previewText && onTextExcerptAdd ? (
459
459
  <WorkspaceTextSelectionMenu
460
460
  fileLabel={file.label?.trim() || excerptPath.split('/').at(-1) || excerptPath}
461
461
  filePath={excerptPath}
@@ -11,9 +11,11 @@ import {
11
11
  } from "lucide-react";
12
12
 
13
13
  import {
14
- CronJobDetailDialog,
15
14
  useCronJobActions,
16
15
  } from "@/features/cron";
16
+ import { useAppPresenter } from "@/app/components/app-presenter-provider";
17
+ import { useNavigate } from "react-router-dom";
18
+ import { pageResourceFromSystemObject } from "@/features/right-panel-resources";
17
19
  import type { CronJobView } from "@/shared/lib/api";
18
20
  import { Button } from "@/shared/components/ui/button";
19
21
  import {
@@ -227,9 +229,9 @@ export function SessionCronJobContent({
227
229
  readonly isLoading?: boolean;
228
230
  readonly onRetry?: () => void;
229
231
  }) {
230
- const [selectedJobId, setSelectedJobId] = useState<string | null>(null);
232
+ const app = useAppPresenter();
233
+ const navigate = useNavigate();
231
234
  const cronActions = useCronJobActions();
232
- const selectedJob = jobs.find((job) => job.id === selectedJobId) ?? null;
233
235
 
234
236
  return (
235
237
  <div className="h-full overflow-y-auto custom-scrollbar px-4 py-4">
@@ -271,29 +273,11 @@ export function SessionCronJobContent({
271
273
  key={job.id}
272
274
  cronActions={cronActions}
273
275
  job={job}
274
- onOpenDetails={(item) => setSelectedJobId(item.id)}
276
+ onOpenDetails={(item) => app.pageResourceManager.open(pageResourceFromSystemObject("cron-job", item.id, item.name || item.id), "default", navigate)}
275
277
  />
276
278
  ))}
277
279
  </div>
278
280
  )}
279
- <CronJobDetailDialog
280
- job={selectedJob}
281
- open={Boolean(selectedJob)}
282
- onOpenChange={(open) => {
283
- if (!open) {
284
- setSelectedJobId(null);
285
- }
286
- }}
287
- onDelete={(job) => {
288
- void cronActions.deleteJob(job, () => setSelectedJobId(null));
289
- }}
290
- onRun={(job) => {
291
- void cronActions.runJob(job);
292
- }}
293
- onToggle={(job, enabled) => {
294
- void cronActions.toggleJob(job, enabled);
295
- }}
296
- />
297
281
  <cronActions.ConfirmDialog />
298
282
  </div>
299
283
  );
@@ -39,3 +39,4 @@ export { ChatSessionWorkspaceFilePreview } from './features/workspace/components
39
39
  export { createWorkspaceFileTab, createWorkspaceFileViewerTab } from './features/workspace/utils/chat-workspace-file-tab.utils';
40
40
  export { WORKSPACE_FILE_PANEL_KIND, resolveFileResourceTarget, createWorkspaceFilePanelTarget, readWorkspaceFilePanelView } from './features/workspace/utils/workspace-file-panel-route.utils';
41
41
  export { resolveAlternateWorkspaceFileViewer } from './features/workspace/utils/chat-workspace-file-viewer.utils';
42
+ export { WORKSPACE_TEXT_EXCERPT_MAX_CHARACTERS, type WorkspaceTextExcerpt } from './features/workspace/utils/workspace-text-excerpt.utils';