@nextclaw/ui 0.19.2 → 0.19.4

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 (249) hide show
  1. package/CHANGELOG.md +20 -1
  2. package/dist/assets/{abnfDiagram-VRR7QNED-DvTjgVMf.js → abnfDiagram-VRR7QNED-DrbfiNNF.js} +1 -1
  3. package/dist/assets/{aiden0z-pptx-renderer.es-C1nfBAXR.js → aiden0z-pptx-renderer.es-DdLb2IVc.js} +1 -1
  4. package/dist/assets/{api-BH6Oslx4.js → api-CUSlBpt_.js} +3 -3
  5. package/dist/assets/{appearance-settings-page-zhgfcGgW.js → appearance-settings-page-cTuZdROP.js} +1 -1
  6. package/dist/assets/{arc-BvhODBXv.js → arc-BXlMc9B0.js} +1 -1
  7. package/dist/assets/architecture-TIHT7OUA-Bq0i9WiE.js +1 -0
  8. package/dist/assets/{architectureDiagram-ZJ3FMSHR-BKO9RgNS.js → architectureDiagram-ZJ3FMSHR-DXAJxxO0.js} +1 -1
  9. package/dist/assets/{blockDiagram-677ZJIJ3-DHpDXy7D.js → blockDiagram-677ZJIJ3-i4Kz7zU0.js} +1 -1
  10. package/dist/assets/{book-open-DGTS6oEO.js → book-open-BJ-xAIp9.js} +1 -1
  11. package/dist/assets/{c4Diagram-LMCZKHZV-BG_iQnDr.js → c4Diagram-LMCZKHZV-BDc0TkPd.js} +1 -1
  12. package/dist/assets/channel-iCaZQ87u.js +1 -0
  13. package/dist/assets/{channels-list-page-3D5lhZoT.js → channels-list-page-nRHOasWA.js} +2 -2
  14. package/dist/assets/chunk-2Q5K7J3B-DFQtlnJS.js +1 -0
  15. package/dist/assets/{chunk-32BRIVSS-DR9M0LLx.js → chunk-32BRIVSS-DuReei0Z.js} +1 -1
  16. package/dist/assets/{chunk-52WLFC77-698iC3BB.js → chunk-52WLFC77-D8APq0tm.js} +1 -1
  17. package/dist/assets/{chunk-5FCAYU7R-DikTj9Ey.js → chunk-5FCAYU7R-BsPrNXNS.js} +1 -1
  18. package/dist/assets/{chunk-5HE753X5-ko0o7_q0.js → chunk-5HE753X5-BRtkOPoJ.js} +1 -1
  19. package/dist/assets/{chunk-5JV3BV7I-CEHufhgo.js → chunk-5JV3BV7I-ChPc8e8U.js} +1 -1
  20. package/dist/assets/{chunk-5TONJI2A-CGgno1cu.js → chunk-5TONJI2A-CzTjlJGb.js} +1 -1
  21. package/dist/assets/{chunk-5VM5RSS4-DkiB4cUQ.js → chunk-5VM5RSS4-xsv_22jR.js} +1 -1
  22. package/dist/assets/{chunk-7BUUIJ7U-DzptcytQ.js → chunk-7BUUIJ7U-CwECujme.js} +1 -1
  23. package/dist/assets/{chunk-BIQX33UG-DMRSQJvT.js → chunk-BIQX33UG-DQAm173p.js} +1 -1
  24. package/dist/assets/{chunk-C7G6YPKG-Bw1BwwuY.js → chunk-C7G6YPKG-BkpajeQb.js} +1 -1
  25. package/dist/assets/{chunk-CQNSW5MT-C0z6eHBk.js → chunk-CQNSW5MT-ND65pWRA.js} +1 -1
  26. package/dist/assets/{chunk-CYSBUYHQ-ByPyAouO.js → chunk-CYSBUYHQ-vMu6ijxG.js} +1 -1
  27. package/dist/assets/{chunk-EMLP6XTP-CJlAXWZg.js → chunk-EMLP6XTP-CEaeW3gH.js} +1 -1
  28. package/dist/assets/{chunk-EX3LRPZG-Dqq4zKJu.js → chunk-EX3LRPZG-a4ER-HDr.js} +1 -1
  29. package/dist/assets/{chunk-FWX5IMBZ-B0SNoCmi.js → chunk-FWX5IMBZ-C_8fQssn.js} +2 -2
  30. package/dist/assets/{chunk-HOUHSVGY-DG_GJih_.js → chunk-HOUHSVGY-CEvw-PEd.js} +1 -1
  31. package/dist/assets/{chunk-ICXQ74PX-Drxk_6bE.js → chunk-ICXQ74PX-BEtF0fGY.js} +1 -1
  32. package/dist/assets/{chunk-JG7HCLWE-DdLFPpnd.js → chunk-JG7HCLWE-DGRrVp8-.js} +1 -1
  33. package/dist/assets/{chunk-JWPE2WC7-B8M4G76F.js → chunk-JWPE2WC7-DRqpp_nM.js} +1 -1
  34. package/dist/assets/{chunk-MOJQB5TN-XP6FJHY_.js → chunk-MOJQB5TN-BDEriOXs.js} +1 -1
  35. package/dist/assets/{chunk-MOZMSUNE-C_6x18Qw.js → chunk-MOZMSUNE-JWRNzdnw.js} +1 -1
  36. package/dist/assets/{chunk-OGEWGWER-N25BzCmG.js → chunk-OGEWGWER-DS2VTgCx.js} +1 -1
  37. package/dist/assets/{chunk-OSBZ3O6U-ASDEm7y0.js → chunk-OSBZ3O6U-3LiYTBUB.js} +1 -1
  38. package/dist/assets/{chunk-PUDLZKDR-CTlvAcn7.js → chunk-PUDLZKDR-o9M2s_SO.js} +1 -1
  39. package/dist/assets/{chunk-Q4XR5HBZ-Da9BWfB6.js → chunk-Q4XR5HBZ-DTB7Ma2N.js} +1 -1
  40. package/dist/assets/{chunk-QBLGF6JB-Bi6RpTJO.js → chunk-QBLGF6JB-DpkxZJL5.js} +1 -1
  41. package/dist/assets/{chunk-R7FJI6CG-DkMsSPvK.js → chunk-R7FJI6CG-CRejkHJU.js} +1 -1
  42. package/dist/assets/{chunk-RYQCIY6F-DZp0eU-j.js → chunk-RYQCIY6F-B1boBjLt.js} +1 -1
  43. package/dist/assets/{chunk-U6XO7XAA-vfinDSrj.js → chunk-U6XO7XAA-DgKa97bj.js} +1 -1
  44. package/dist/assets/{chunk-V7JOEXUC-C9Nksccs.js → chunk-V7JOEXUC-DE56eh0N.js} +1 -1
  45. package/dist/assets/chunk-VAUOI2AC-CF9A-TeE.js +1 -0
  46. package/dist/assets/{chunk-VR4S4FIN-Xv6zfwXs.js → chunk-VR4S4FIN-BzomPZq2.js} +1 -1
  47. package/dist/assets/{chunk-WYO6CB5R-mOJNzbWl.js → chunk-WYO6CB5R-C6gy8rSd.js} +2 -2
  48. package/dist/assets/chunk-XXDRQBXY-BRxdfVCU.js +1 -0
  49. package/dist/assets/{chunk-YOTPTUD7-1-aKSu4o.js → chunk-YOTPTUD7-BK98yfl9.js} +1 -1
  50. package/dist/assets/{chunk-ZGVPDNZ5-OJ8veMpK.js → chunk-ZGVPDNZ5-DFXAhwUT.js} +1 -1
  51. package/dist/assets/{chunk-ZIRB5QZD-SR_Nq_PP.js → chunk-ZIRB5QZD-Bbu6BiNC.js} +1 -1
  52. package/dist/assets/classDiagram-OUVF2IWQ-Bo28fPi-.js +1 -0
  53. package/dist/assets/classDiagram-v2-EOCWNBFH-BOVN9wUz.js +1 -0
  54. package/dist/assets/{config-split-page-B55XD_ZY.js → config-split-page-DO1Oyb-r.js} +1 -1
  55. package/dist/assets/{confirm-dialog-BCCreI_f.js → confirm-dialog-CCae9Fr4.js} +1 -1
  56. package/dist/assets/{cose-bilkent-JH36ORCC-NjVgz2Bw.js → cose-bilkent-JH36ORCC-BV8KR4uF.js} +1 -1
  57. package/dist/assets/cynefin-VYW2F7L2-B7rF3JRp.js +1 -0
  58. package/dist/assets/{cynefinDiagram-TSTJHNR4-B_17LDqD.js → cynefinDiagram-TSTJHNR4-Ckd6nU9t.js} +1 -1
  59. package/dist/assets/{dagre-CMe101gk.js → dagre-D_T-FW5i.js} +1 -1
  60. package/dist/assets/{dagre-VKFMJZFB-qo8nLb3B.js → dagre-VKFMJZFB-CyDaQpD4.js} +1 -1
  61. package/dist/assets/desktop-update-config-XSV-DjJW.js +1 -0
  62. package/dist/assets/{diagram-FQU43EPY-Ba3XIklC.js → diagram-FQU43EPY-CghnwS0H.js} +1 -1
  63. package/dist/assets/{diagram-G47NLZAW-CG45TcUx.js → diagram-G47NLZAW-DK4DJ3Kx.js} +1 -1
  64. package/dist/assets/{diagram-NH7WQ7WH-2F2HrwV-.js → diagram-NH7WQ7WH-CUgDkZCJ.js} +1 -1
  65. package/dist/assets/{diagram-OA4YK3LP-CXfWH9NZ.js → diagram-OA4YK3LP-vO2gyH6n.js} +1 -1
  66. package/dist/assets/{diagram-WEI45ONY-CH_4yepc.js → diagram-WEI45ONY-DbSBRDSr.js} +1 -1
  67. package/dist/assets/{dist-IRWcJpfm.js → dist-1N9UpL3c.js} +1 -1
  68. package/dist/assets/{dist-BYSUQaO2.js → dist-C-2H76Cv.js} +1 -1
  69. package/dist/assets/doc-browser-BHFCZIoL.js +1 -0
  70. package/dist/assets/{doc-browser-D8rNrWHk.js → doc-browser-CRlpqMwH.js} +1 -1
  71. package/dist/assets/{doc-browser-context-DjTbIbQc.js → doc-browser-context-C0Bns1J6.js} +1 -1
  72. package/dist/assets/{docx-preview-CoM4JK7_.js → docx-preview-DfxIm9IB.js} +1 -1
  73. package/dist/assets/{ebnfDiagram-CCIWWBDH-BwiG4Ea1.js → ebnfDiagram-CCIWWBDH-W7CAcK7r.js} +1 -1
  74. package/dist/assets/{erDiagram-Q63AITRT-BML3hpnQ.js → erDiagram-Q63AITRT-LA0JWL8-.js} +1 -1
  75. package/dist/assets/eventmodeling-45OFAUF4-DzQLXa3s.js +1 -0
  76. package/dist/assets/flowDiagram-23GEKE2U-C81eexJZ.js +1 -0
  77. package/dist/assets/{form-actions-C8hazgNP.js → form-actions-CMxhPact.js} +1 -1
  78. package/dist/assets/{ganttDiagram-NO4QXBWP-80enfWbD.js → ganttDiagram-NO4QXBWP-_PiTPavJ.js} +1 -1
  79. package/dist/assets/gitGraph-TEB2WS4Q-Dt3ZqpFD.js +1 -0
  80. package/dist/assets/{gitGraphDiagram-IHSO6WYX-CVLqXr-b.js → gitGraphDiagram-IHSO6WYX-DsRB1ZMU.js} +1 -1
  81. package/dist/assets/index-DdYOaKm7.js +109 -0
  82. package/dist/assets/index-DxCGKhAw.css +1 -0
  83. package/dist/assets/info-DKCQHKI2-CCCE7dXC.js +1 -0
  84. package/dist/assets/{infoDiagram-FWYZ7A6U-Dyvs2t0i.js → infoDiagram-FWYZ7A6U-wIRraYx5.js} +1 -1
  85. package/dist/assets/{ishikawaDiagram-FXEZZL3T-sSnxDKfh.js → ishikawaDiagram-FXEZZL3T-CNzsrcrh.js} +1 -1
  86. package/dist/assets/{journeyDiagram-5HDEW3XC-D22V71Mf.js → journeyDiagram-5HDEW3XC-Dy9huHxy.js} +1 -1
  87. package/dist/assets/{kanban-definition-HUTT4EX6-D3ds0p-g.js → kanban-definition-HUTT4EX6-BOCIN365.js} +1 -1
  88. package/dist/assets/{line-CbyFs065.js → line-BxsizOTs.js} +1 -1
  89. package/dist/assets/{linear-4DKtnZBS.js → linear-C_YHf92I.js} +1 -1
  90. package/dist/assets/loader-circle-B-UdXjl4.js +1 -0
  91. package/dist/assets/{map-CcZzEoz6.js → map-CobcjgZ8.js} +1 -1
  92. package/dist/assets/mcp-marketplace-page-BUIGtSda.js +1 -0
  93. package/dist/assets/mcp-marketplace-page-CStb8ml2.js +9 -0
  94. package/dist/assets/{mermaid-parser.core-DPmMOjXD.js → mermaid-parser.core-D7lu2RbA.js} +2 -2
  95. package/dist/assets/{mermaid.core-BSf-ddRa.js → mermaid.core-CbfW2Rk8.js} +3 -3
  96. package/dist/assets/{mindmap-definition-LN4V7U3C-BUTwWPyx.js → mindmap-definition-LN4V7U3C-DUjGXlIC.js} +1 -1
  97. package/dist/assets/model-config-page-BkfUYm95.js +1 -0
  98. package/dist/assets/{navigation-link-kGAcL2N2.js → navigation-link-v3FaWcIr.js} +1 -1
  99. package/dist/assets/{ordinal-BgLYtOZE.js → ordinal-CjoUCHto.js} +1 -1
  100. package/dist/assets/packet-7NZHBO7P-D19yoxFV.js +1 -0
  101. package/dist/assets/{pegDiagram-2B236MQR-BEu_ZBjc.js → pegDiagram-2B236MQR-BQY2DgiP.js} +1 -1
  102. package/dist/assets/pie-RZYD4A2V-hMaJbLrn.js +1 -0
  103. package/dist/assets/{pieDiagram-ENE6RG2P-CAwAwuBd.js → pieDiagram-ENE6RG2P-D18owe4D.js} +1 -1
  104. package/dist/assets/plus-Dw9QFnKr.js +1 -0
  105. package/dist/assets/{privacy-settings-page-v9LTFvJe.js → privacy-settings-page-CYM1Dxwo.js} +1 -1
  106. package/dist/assets/providers-config-page-fxL4I4bz.js +1 -0
  107. package/dist/assets/{quadrantDiagram-ABIIQ3AL-CwE6F-_2.js → quadrantDiagram-ABIIQ3AL-B8F7YoTA.js} +1 -1
  108. package/dist/assets/radar-I7S5WNFK-C8BkvScA.js +1 -0
  109. package/dist/assets/railroad-3IZDKUUU-CozZOitC.js +1 -0
  110. package/dist/assets/railroad-abnf-AHOZXSZD-BqIoFXFi.js +1 -0
  111. package/dist/assets/railroad-ebnf-EBAXGLYW-CD2e3dng.js +1 -0
  112. package/dist/assets/railroad-peg-LSFZ7HO6-DoKlRUOF.js +1 -0
  113. package/dist/assets/{railroadDiagram-RFXS5EU6-xgLekU3B.js → railroadDiagram-RFXS5EU6-RU8GpxN5.js} +1 -1
  114. package/dist/assets/{react-Dwvp5yeX.js → react-Dlz_IxoP.js} +1 -1
  115. package/dist/assets/{refresh-cw-C_oj_h_s.js → refresh-cw-CK4I5UFv.js} +1 -1
  116. package/dist/assets/remote-B0pPgtC6.js +1 -0
  117. package/dist/assets/{requirementDiagram-TGXJPOKE-CyVUzEX5.js → requirementDiagram-TGXJPOKE-ubPOmmNi.js} +1 -1
  118. package/dist/assets/rotate-cw-Z48dmh13.js +1 -0
  119. package/dist/assets/runtime-config-page-C7vksFVD.js +1 -0
  120. package/dist/assets/{sankeyDiagram-HTMAVEWB-JK6bFmYB.js → sankeyDiagram-HTMAVEWB-8L_hXJWW.js} +1 -1
  121. package/dist/assets/{save-BZE6H02N.js → save-Bo37cIFb.js} +1 -1
  122. package/dist/assets/{search-C7Tj40kC.js → search-CZiEm7YD.js} +1 -1
  123. package/dist/assets/search-config-page-DKdEf7m9.js +1 -0
  124. package/dist/assets/{secrets-config-page-B__VLAEQ.js → secrets-config-page-COlMqSfL.js} +2 -2
  125. package/dist/assets/security-config-CitLihXA.js +1 -0
  126. package/dist/assets/{select-fZ5tifar.js → select-BACzapV3.js} +1 -1
  127. package/dist/assets/{sequenceDiagram-DBY2YBRQ-BR5IokXV.js → sequenceDiagram-DBY2YBRQ-Dom6Qor5.js} +1 -1
  128. package/dist/assets/{sizeCapture-X5ZJPWSS-SxG_YnMA.js → sizeCapture-X5ZJPWSS-cZcFSpLJ.js} +1 -1
  129. package/dist/assets/skeleton-B2PCAZJG.js +1 -0
  130. package/dist/assets/{src-vgz8E_-O.js → src-BE9E1xcR.js} +1 -1
  131. package/dist/assets/{stateDiagram-2N3HPSRC-DD4OoYhn.js → stateDiagram-2N3HPSRC-CHrpxmGO.js} +1 -1
  132. package/dist/assets/stateDiagram-v2-6OUMAXLB-b1pZQlDD.js +1 -0
  133. package/dist/assets/{swimlanes-5IMT3BWC-BWkFeyCT.js → swimlanes-5IMT3BWC-D6nJS2xQ.js} +2 -2
  134. package/dist/assets/swimlanesDiagram-G3AALYLV-CowxgWVn.js +8 -0
  135. package/dist/assets/{tag-chip-CwYZcRoe.js → tag-chip-BnamTQVX.js} +1 -1
  136. package/dist/assets/{timeline-definition-FHXFAJF6-VTRPy6KA.js → timeline-definition-FHXFAJF6-DJNn-HQO.js} +1 -1
  137. package/dist/assets/treeView-QDETBFTQ-BQlYkJbz.js +1 -0
  138. package/dist/assets/treemap-6X3UGDF4-DzTOd1bd.js +1 -0
  139. package/dist/assets/{vennDiagram-L72KCM5P-Bvk42Pek.js → vennDiagram-L72KCM5P-CCEzHoXt.js} +1 -1
  140. package/dist/assets/wardley-OPB4EBWU-D80jQc0y.js +1 -0
  141. package/dist/assets/{wardleyDiagram-EHGQE667-DOFjx2s3.js → wardleyDiagram-EHGQE667-BhyVxVXt.js} +1 -1
  142. package/dist/assets/x-BzYEzkLA.js +1 -0
  143. package/dist/assets/{xychartDiagram-FW5EYKEG-DY5yRTUc.js → xychartDiagram-FW5EYKEG-CacW_246.js} +1 -1
  144. package/dist/index.html +70 -22
  145. package/index.html +49 -1
  146. package/package.json +8 -8
  147. package/src/app/index.tsx +1 -3
  148. package/src/app/managers/__tests__/viewport-layout.manager.test.ts +12 -0
  149. package/src/app/managers/viewport-layout.manager.ts +12 -0
  150. package/src/app/stores/viewport-layout.store.ts +23 -11
  151. package/src/features/apps/components/__tests__/app-marketplace-catalog.test.tsx +161 -0
  152. package/src/features/apps/components/app-marketplace-catalog.tsx +36 -7
  153. package/src/features/apps/components/app-marketplace-detail.tsx +29 -6
  154. package/src/features/apps/components/app-marketplace-dialog.tsx +71 -55
  155. package/src/features/apps/components/app-marketplace-operation.tsx +128 -16
  156. package/src/features/apps/components/app-packages-panel.tsx +1 -0
  157. package/src/features/apps/utils/app-marketplace-platform.utils.test.ts +59 -0
  158. package/src/features/apps/utils/app-marketplace-platform.utils.ts +33 -0
  159. package/src/features/chat/features/conversation/components/__tests__/session-conversation-area.test.tsx +6 -0
  160. package/src/features/chat/features/conversation/components/__tests__/session-conversation-input.streaming.test.tsx +6 -2
  161. package/src/features/chat/features/conversation/components/__tests__/session-queued-input-rows.test.tsx +46 -0
  162. package/src/features/chat/features/conversation/components/session-conversation-area.tsx +15 -2
  163. package/src/features/chat/features/conversation/components/session-conversation-input.tsx +5 -3
  164. package/src/features/chat/features/conversation/components/session-queued-input-rows.tsx +59 -5
  165. package/src/features/chat/features/conversation/hooks/__tests__/use-session-conversation-controller.test.tsx +35 -1
  166. package/src/features/chat/features/conversation/hooks/__tests__/use-session-run-queue.test.tsx +25 -19
  167. package/src/features/chat/features/conversation/hooks/use-session-conversation-controller.ts +148 -134
  168. package/src/features/chat/features/conversation/hooks/use-session-pending-input-actions.ts +135 -0
  169. package/src/features/chat/features/conversation/hooks/use-session-run-queue.ts +19 -4
  170. package/src/features/chat/features/conversation/utils/__tests__/session-queued-input.utils.test.ts +52 -2
  171. package/src/features/chat/features/conversation/utils/session-queued-input.utils.ts +46 -5
  172. package/src/features/chat/features/message/utils/chat-message-texts.utils.ts +1 -0
  173. package/src/features/chat/features/ncp/hooks/__tests__/use-hydrated-ncp-agent.test.tsx +102 -0
  174. package/src/features/chat/features/ncp/hooks/__tests__/use-ncp-session-conversation.test.tsx +117 -1
  175. package/src/features/chat/features/ncp/hooks/use-ncp-session-conversation.ts +2 -2
  176. package/src/features/chat/features/ncp/hooks/use-ncp-session-message-history.ts +31 -81
  177. package/src/features/chat/features/ncp/hooks/use-ncp-session-seed-loader.ts +134 -0
  178. package/src/features/chat/features/session/utils/ncp-session-adapter.utils.ts +1 -6
  179. package/src/features/panel-apps/components/__tests__/panel-app-list-item.test.tsx +8 -0
  180. package/src/features/panel-apps/components/__tests__/panel-app-main-sidebar-nav.test.tsx +99 -13
  181. package/src/features/panel-apps/components/panel-app-main-sidebar-menu-item.tsx +4 -0
  182. package/src/features/panel-apps/components/panel-app-main-sidebar-nav.tsx +166 -31
  183. package/src/shared/lib/api/utils/ncp-attachments.utils.ts +8 -1
  184. package/src/shared/lib/api/utils/ncp-session.utils.ts +1 -0
  185. package/src/shared/lib/i18n/locales/en-US/chat.json +3 -0
  186. package/src/shared/lib/i18n/locales/en-US/doc-browser.json +11 -1
  187. package/src/shared/lib/i18n/locales/zh-CN/chat.json +3 -0
  188. package/src/shared/lib/i18n/locales/zh-CN/doc-browser.json +11 -1
  189. package/src/vite-env.d.ts +9 -0
  190. package/dist/assets/architecture-TIHT7OUA-Bjp9jf0h.js +0 -1
  191. package/dist/assets/channel-5T3pdR-s.js +0 -1
  192. package/dist/assets/chat-page-BXu3hnOK.js +0 -1
  193. package/dist/assets/chunk-2Q5K7J3B-DTZ3tGfE.js +0 -1
  194. package/dist/assets/chunk-VAUOI2AC-HLGIHQ6k.js +0 -1
  195. package/dist/assets/chunk-XXDRQBXY-Dl0m6Ni-.js +0 -1
  196. package/dist/assets/classDiagram-OUVF2IWQ-DcDnzGNX.js +0 -1
  197. package/dist/assets/classDiagram-v2-EOCWNBFH-BKXKMzKM.js +0 -1
  198. package/dist/assets/cynefin-VYW2F7L2-BWg7U2UD.js +0 -1
  199. package/dist/assets/desktop-update-config-Blol-Umu.js +0 -1
  200. package/dist/assets/doc-browser-BExPq1Hk.js +0 -1
  201. package/dist/assets/eventmodeling-45OFAUF4-SB-J4tpq.js +0 -1
  202. package/dist/assets/flowDiagram-23GEKE2U-BQ_u-eLM.js +0 -1
  203. package/dist/assets/gitGraph-TEB2WS4Q-DPxNwsFd.js +0 -1
  204. package/dist/assets/index-Btj8yN4T.css +0 -1
  205. package/dist/assets/index-CHcGqqBe.js +0 -109
  206. package/dist/assets/info-DKCQHKI2-DNlNGH90.js +0 -1
  207. package/dist/assets/loader-circle-C75DUmmm.js +0 -1
  208. package/dist/assets/mcp-marketplace-page-CIMRYgOY.js +0 -9
  209. package/dist/assets/mcp-marketplace-page-D2NlhG1k.js +0 -1
  210. package/dist/assets/model-config-page-ChR-ENjp.js +0 -1
  211. package/dist/assets/packet-7NZHBO7P-BpeiqkBZ.js +0 -1
  212. package/dist/assets/pie-RZYD4A2V-Cy1yxsPd.js +0 -1
  213. package/dist/assets/plus-TzSIMKWW.js +0 -1
  214. package/dist/assets/provider-scoped-model-input-DiSpKSk0.js +0 -1
  215. package/dist/assets/providers-config-page-BvXa03cp.js +0 -1
  216. package/dist/assets/radar-I7S5WNFK-CEcPKCj8.js +0 -1
  217. package/dist/assets/railroad-3IZDKUUU-CW38eNDs.js +0 -1
  218. package/dist/assets/railroad-abnf-AHOZXSZD-ByrylcXf.js +0 -1
  219. package/dist/assets/railroad-ebnf-EBAXGLYW-CNB6GwP-.js +0 -1
  220. package/dist/assets/railroad-peg-LSFZ7HO6-x6hnqlBG.js +0 -1
  221. package/dist/assets/remote-BgYduCQR.js +0 -1
  222. package/dist/assets/rotate-cw-BT8HxlcB.js +0 -1
  223. package/dist/assets/runtime-config-page-BuYof2Ty.js +0 -1
  224. package/dist/assets/search-config-page-CQTJOsOf.js +0 -1
  225. package/dist/assets/security-config-CtJqaPVL.js +0 -1
  226. package/dist/assets/settings-2-BHGgNlOH.js +0 -1
  227. package/dist/assets/skeleton-CJLc1CWn.js +0 -1
  228. package/dist/assets/stateDiagram-v2-6OUMAXLB-ZC6dFORv.js +0 -1
  229. package/dist/assets/swimlanesDiagram-G3AALYLV-CRkh51Im.js +0 -8
  230. package/dist/assets/treeView-QDETBFTQ-Bw0DSCH7.js +0 -1
  231. package/dist/assets/treemap-6X3UGDF4-DFWBFcfh.js +0 -1
  232. package/dist/assets/wardley-OPB4EBWU-CoWturk0.js +0 -1
  233. package/dist/assets/x-BMl7cucN.js +0 -1
  234. /package/dist/assets/{array-NADKd9aU.js → array-COEOjKUv.js} +0 -0
  235. /package/dist/assets/{chunk-KEIR6QF5-BuyuzJJL.js → chunk-KEIR6QF5-D7oHAcry.js} +0 -0
  236. /package/dist/assets/{chunk-Y2CYZVJY-D_B2ZRNt.js → chunk-Y2CYZVJY-08IUpgTP.js} +0 -0
  237. /package/dist/assets/{config-hints-BcfVUMij.js → config-hints-BoE41XDI.js} +0 -0
  238. /package/dist/assets/{cytoscape.esm-BANTlgza.js → cytoscape.esm-HifWufkp.js} +0 -0
  239. /package/dist/assets/{defaultLocale-C4_L0aEs.js → defaultLocale-CA-_SsC1.js} +0 -0
  240. /package/dist/assets/{dist-DAPEIJbz.js → dist-C60rvfy8.js} +0 -0
  241. /package/dist/assets/{graphlib-ChqXg0dI.js → graphlib-CGYCNsoJ.js} +0 -0
  242. /package/dist/assets/{host-capabilities-D9C4CBK5.js → host-capabilities-DPNRAjIj.js} +0 -0
  243. /package/dist/assets/{init-D8wZg9xq.js → init-C3plFVsG.js} +0 -0
  244. /package/dist/assets/{jszip.min-CkbBfwAQ.js → jszip.min-CeYs17Gn.js} +0 -0
  245. /package/dist/assets/{katex-TZJFa0J_.js → katex-DRThweSG.js} +0 -0
  246. /package/dist/assets/{middleware-B3k-TSmZ.js → middleware-D05fK5Sb.js} +0 -0
  247. /package/dist/assets/{path-DodfxcX7.js → path-BKtKxQ6Y.js} +0 -0
  248. /package/dist/assets/{rough.esm-CUaa1Rbe.js → rough.esm-DurL7-u7.js} +0 -0
  249. /package/dist/assets/{xlsx-CxByi7bR.js → xlsx-Sg8fZ6nH.js} +0 -0
@@ -1,57 +1,169 @@
1
1
  import type { AppPackageOperationView } from '@nextclaw/client-sdk';
2
2
  import { Check, LoaderCircle } from 'lucide-react';
3
3
  import { Button } from '@/shared/components/ui/button';
4
+ import {
5
+ Tooltip,
6
+ TooltipContent,
7
+ TooltipTrigger,
8
+ } from '@/shared/components/ui/tooltip';
4
9
  import { t } from '@/shared/lib/i18n';
5
- import { cn } from '@/shared/lib/utils';
10
+ import type { AppMarketplaceInstallability } from '@/features/apps/utils/app-marketplace-platform.utils';
11
+
12
+ export type MarketplaceCompatibilityPresentation = {
13
+ actionLabel: string;
14
+ badgeLabel?: string;
15
+ reason: string;
16
+ statusLabel: string;
17
+ };
18
+
19
+ export function readMarketplaceCompatibilityPresentation(
20
+ installability: AppMarketplaceInstallability,
21
+ supportedPlatforms: string,
22
+ ): MarketplaceCompatibilityPresentation | undefined {
23
+ if (installability.status === 'compatible') return undefined;
24
+ if (installability.status === 'unknown') {
25
+ return {
26
+ actionLabel: t('appPackagesTemporarilyUnavailable'),
27
+ reason: t('appPackagesCompatibilityUnknownReason'),
28
+ statusLabel: t('appPackagesCompatibilityUnknown'),
29
+ };
30
+ }
31
+ return {
32
+ actionLabel: t('appPackagesUnavailable'),
33
+ badgeLabel: t('appPackagesPlatformOnly').replace('{platforms}', supportedPlatforms),
34
+ reason: installability.reason === 'unsupported-os'
35
+ ? t('appPackagesUnsupportedOsReason').replace('{platforms}', supportedPlatforms)
36
+ : t('appPackagesUnsupportedTargetReason').replace('{platforms}', supportedPlatforms),
37
+ statusLabel: t('appPackagesUnavailableOnDevice'),
38
+ };
39
+ }
40
+
41
+ export function MarketplaceCompatibilityBadge({
42
+ presentation,
43
+ }: {
44
+ presentation?: MarketplaceCompatibilityPresentation;
45
+ }) {
46
+ if (!presentation?.badgeLabel) return null;
47
+ return (
48
+ <span className="mt-1.5 inline-flex rounded-full bg-amber-500/12 px-2 py-0.5 text-[10px] font-semibold text-amber-700 ring-1 ring-inset ring-amber-500/20 dark:text-amber-300">
49
+ {presentation.badgeLabel}
50
+ </span>
51
+ );
52
+ }
53
+
54
+ export function MarketplaceCompatibilityStatus({
55
+ blocked,
56
+ presentation,
57
+ className,
58
+ }: {
59
+ blocked: boolean;
60
+ presentation?: MarketplaceCompatibilityPresentation;
61
+ className?: string;
62
+ }) {
63
+ if (!blocked || !presentation) return null;
64
+ return <p className={className}>{presentation.statusLabel}</p>;
65
+ }
6
66
 
7
67
  export function MarketplaceInstallButton({
8
68
  active,
9
69
  canUpdate,
10
- failed,
11
70
  installed,
12
71
  isStarting,
13
72
  onAction,
14
73
  operation,
74
+ unavailableLabel,
75
+ unavailableReason,
15
76
  }: {
16
77
  active: boolean;
17
78
  canUpdate: boolean;
18
- failed: boolean;
19
79
  installed: boolean;
20
80
  isStarting: boolean;
21
81
  onAction: () => void;
22
82
  operation?: AppPackageOperationView;
83
+ unavailableLabel?: string;
84
+ unavailableReason?: string;
23
85
  }) {
24
- return (
86
+ const disabled = Boolean(unavailableReason) || installed && !canUpdate || active || isStarting;
87
+ const button = (
25
88
  <Button
26
89
  type="button"
27
90
  size="sm"
28
91
  variant={installed && !canUpdate || active ? 'secondary' : 'default'}
29
- disabled={installed && !canUpdate || active || isStarting}
92
+ disabled={disabled}
30
93
  onClick={onAction}
31
94
  className="shrink-0 rounded-full px-4"
32
95
  >
33
- {installed && !canUpdate ? (
34
- <><Check className="mr-1.5 h-3.5 w-3.5" />{t('appPackagesMarketplaceInstalled')}</>
35
- ) : active || isStarting ? (
36
- <><LoaderCircle className="mr-1.5 h-3.5 w-3.5 animate-spin" />{operation ? operationStatusLabel(operation) : t('appPackagesPreparing')}</>
37
- ) : canUpdate ? t('appPackagesUpdate') : failed ? t('appPackagesRetry') : t('appPackagesInstall')}
96
+ {renderInstallButtonContent({
97
+ active,
98
+ canUpdate,
99
+ installed,
100
+ isStarting,
101
+ operation,
102
+ unavailableLabel,
103
+ unavailableReason,
104
+ })}
38
105
  </Button>
39
106
  );
107
+ if (!unavailableReason) return button;
108
+ return (
109
+ <Tooltip>
110
+ <TooltipTrigger asChild>
111
+ <span tabIndex={0} className="inline-flex cursor-not-allowed rounded-full focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring">
112
+ {button}
113
+ </span>
114
+ </TooltipTrigger>
115
+ <TooltipContent side="top" className="max-w-64 text-xs">
116
+ {unavailableReason}
117
+ </TooltipContent>
118
+ </Tooltip>
119
+ );
120
+ }
121
+
122
+ function renderInstallButtonContent({
123
+ active,
124
+ canUpdate,
125
+ installed,
126
+ isStarting,
127
+ operation,
128
+ unavailableLabel,
129
+ unavailableReason,
130
+ }: {
131
+ active: boolean;
132
+ canUpdate: boolean;
133
+ installed: boolean;
134
+ isStarting: boolean;
135
+ operation?: AppPackageOperationView;
136
+ unavailableLabel?: string;
137
+ unavailableReason?: string;
138
+ }) {
139
+ if (unavailableReason) return unavailableLabel ?? t('appPackagesUnavailable');
140
+ if (installed && !canUpdate) {
141
+ return <><Check className="mr-1.5 h-3.5 w-3.5" />{t('appPackagesMarketplaceInstalled')}</>;
142
+ }
143
+ if (active || isStarting) {
144
+ return (
145
+ <>
146
+ <LoaderCircle className="mr-1.5 h-3.5 w-3.5 animate-spin" />
147
+ {operation ? operationStatusLabel(operation) : t('appPackagesPreparing')}
148
+ </>
149
+ );
150
+ }
151
+ if (canUpdate) return t('appPackagesUpdate');
152
+ return t('appPackagesInstall');
40
153
  }
41
154
 
42
155
  export function OperationProgress({ operation }: { operation: AppPackageOperationView }) {
43
156
  const progress = Math.max(4, Math.round((operation.completedSteps / operation.totalSteps) * 100));
44
- const failed = operation.status === 'failed' || operation.status === 'interrupted';
45
157
  return (
46
- <div className="mt-2" role={failed ? 'alert' : 'status'} aria-live="polite">
158
+ <div className="mt-2" role="status" aria-live="polite">
47
159
  <div className="h-1 overflow-hidden rounded-full bg-muted">
48
160
  <div
49
- className={cn('h-full rounded-full transition-[width] duration-300', failed ? 'bg-destructive' : 'bg-primary')}
50
- style={{ width: failed ? '100%' : `${progress}%` }}
161
+ className="h-full rounded-full bg-primary transition-[width] duration-300"
162
+ style={{ width: `${progress}%` }}
51
163
  />
52
164
  </div>
53
- <p className={cn('mt-1.5 truncate text-[11px]', failed ? 'text-destructive' : 'text-muted-foreground')}>
54
- {failed ? operation.error ?? t('appPackagesActionFailed') : operationStatusLabel(operation)}
165
+ <p className="mt-1.5 truncate text-[11px] text-muted-foreground">
166
+ {operationStatusLabel(operation)}
55
167
  </p>
56
168
  </div>
57
169
  );
@@ -206,6 +206,7 @@ export function AppPackagesPanel({
206
206
 
207
207
  <AppMarketplaceDialog
208
208
  error={lifecycle.error instanceof Error ? lifecycle.error : null}
209
+ hostTarget={appPackages.data?.hostTarget}
209
210
  installedPackages={appPackages.data?.entries ?? []}
210
211
  startingSource={lifecycle.variables?.action === 'install'
211
212
  ? lifecycle.variables.source
@@ -0,0 +1,59 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import {
3
+ getAppMarketplaceInstallabilityRank,
4
+ resolveAppMarketplaceInstallability,
5
+ } from '@/features/apps/utils/app-marketplace-platform.utils';
6
+
7
+ const MAC_TARGET = {
8
+ key: 'darwin-arm64',
9
+ operatingSystem: 'darwin',
10
+ architecture: 'arm64',
11
+ } as const;
12
+
13
+ describe('app marketplace platform compatibility', () => {
14
+ it('treats universal apps as compatible without requiring host target data', () => {
15
+ expect(resolveAppMarketplaceInstallability({
16
+ mode: 'universal',
17
+ targets: [],
18
+ operatingSystems: [],
19
+ }, undefined)).toEqual({ status: 'compatible' });
20
+ });
21
+
22
+ it('matches targeted apps by the canonical host target key', () => {
23
+ expect(resolveAppMarketplaceInstallability({
24
+ mode: 'targeted',
25
+ targets: ['darwin-arm64'],
26
+ operatingSystems: ['darwin'],
27
+ }, MAC_TARGET)).toEqual({ status: 'compatible' });
28
+ });
29
+
30
+ it('distinguishes unsupported operating systems from unsupported architectures', () => {
31
+ expect(resolveAppMarketplaceInstallability({
32
+ mode: 'targeted',
33
+ targets: ['linux-x64-gnu'],
34
+ operatingSystems: ['linux'],
35
+ }, MAC_TARGET)).toEqual({ status: 'incompatible', reason: 'unsupported-os' });
36
+
37
+ expect(resolveAppMarketplaceInstallability({
38
+ mode: 'targeted',
39
+ targets: ['darwin-x64'],
40
+ operatingSystems: ['darwin'],
41
+ }, MAC_TARGET)).toEqual({ status: 'incompatible', reason: 'unsupported-target' });
42
+ });
43
+
44
+ it('fails closed when the host target cannot be confirmed', () => {
45
+ const installability = resolveAppMarketplaceInstallability({
46
+ mode: 'targeted',
47
+ targets: ['linux-x64-gnu'],
48
+ operatingSystems: ['linux'],
49
+ }, undefined);
50
+
51
+ expect(installability).toEqual({ status: 'unknown' });
52
+ expect(getAppMarketplaceInstallabilityRank(installability)).toBe(1);
53
+ expect(getAppMarketplaceInstallabilityRank({ status: 'compatible' })).toBe(0);
54
+ expect(getAppMarketplaceInstallabilityRank({
55
+ status: 'incompatible',
56
+ reason: 'unsupported-os',
57
+ })).toBe(2);
58
+ });
59
+ });
@@ -1,3 +1,4 @@
1
+ import type { AppPackageHostTarget } from '@nextclaw/client-sdk';
1
2
  import type { AppMarketplaceItemView } from '@/features/apps/types/app-marketplace.types';
2
3
 
3
4
  const OPERATING_SYSTEM_LABELS: Record<'darwin' | 'linux' | 'win32', string> = {
@@ -17,3 +18,35 @@ export function formatAppMarketplacePlatforms(
17
18
  .map((operatingSystem) => OPERATING_SYSTEM_LABELS[operatingSystem])
18
19
  .join(' · ');
19
20
  }
21
+
22
+ export type AppMarketplaceInstallability =
23
+ | { status: 'compatible' }
24
+ | { status: 'incompatible'; reason: 'unsupported-os' | 'unsupported-target' }
25
+ | { status: 'unknown' };
26
+
27
+ export function resolveAppMarketplaceInstallability(
28
+ availability: AppMarketplaceItemView['availability'],
29
+ hostTarget: AppPackageHostTarget | undefined,
30
+ ): AppMarketplaceInstallability {
31
+ if (!availability || availability.mode === 'universal') {
32
+ return { status: 'compatible' };
33
+ }
34
+ if (!hostTarget) {
35
+ return { status: 'unknown' };
36
+ }
37
+ if (availability.targets.includes(hostTarget.key)) {
38
+ return { status: 'compatible' };
39
+ }
40
+ if (!availability.operatingSystems.includes(hostTarget.operatingSystem)) {
41
+ return { status: 'incompatible', reason: 'unsupported-os' };
42
+ }
43
+ return { status: 'incompatible', reason: 'unsupported-target' };
44
+ }
45
+
46
+ export function getAppMarketplaceInstallabilityRank(
47
+ installability: AppMarketplaceInstallability,
48
+ ): number {
49
+ if (installability.status === 'compatible') return 0;
50
+ if (installability.status === 'unknown') return 1;
51
+ return 2;
52
+ }
@@ -72,6 +72,7 @@ const mocks = vi.hoisted(() => {
72
72
  skillRecords: [],
73
73
  };
74
74
  const controller = {
75
+ canEditQueuedInput: true,
75
76
  canStopGeneration: true,
76
77
  deleteQueuedInput: vi.fn(),
77
78
  editQueuedInput: vi.fn(),
@@ -79,9 +80,11 @@ const mocks = vi.hoisted(() => {
79
80
  isSending: true,
80
81
  queuedInputs: [],
81
82
  send: vi.fn(),
83
+ sendSteering: vi.fn(),
82
84
  sendDisabled: true,
83
85
  stop: vi.fn(),
84
86
  stopDisabled: false,
87
+ steerQueuedInput: vi.fn(),
85
88
  };
86
89
  const agent = {
87
90
  visibleMessages: [] as unknown[],
@@ -194,8 +197,11 @@ vi.mock(
194
197
  () => ({
195
198
  useSessionRunQueue: () => ({
196
199
  inputs: [],
200
+ pendingInputs: [],
197
201
  isLoading: false,
202
+ refreshQueuedInputs: vi.fn(async () => []),
198
203
  removeQueuedInput: vi.fn(async () => null),
204
+ steerQueuedInput: vi.fn(async () => null),
199
205
  }),
200
206
  }),
201
207
  );
@@ -232,6 +232,7 @@ const presenter = {
232
232
  } as unknown as ChatPresenterLike;
233
233
 
234
234
  const controller: SessionConversationInputController = {
235
+ canEditQueuedInput: true,
235
236
  canStopGeneration: true,
236
237
  deleteQueuedInput: vi.fn(),
237
238
  editQueuedInput: vi.fn(),
@@ -239,10 +240,12 @@ const controller: SessionConversationInputController = {
239
240
  primaryAction: 'send',
240
241
  queuedInputs: [],
241
242
  send: vi.fn(),
243
+ sendSteering: vi.fn(),
242
244
  sendPresetMessage: vi.fn(),
243
245
  sendDisabled: true,
244
246
  stop: vi.fn(),
245
247
  stopDisabled: false,
248
+ steerQueuedInput: vi.fn(),
246
249
  };
247
250
 
248
251
  function createStreamingInputSnapshot(
@@ -645,6 +648,7 @@ function AttachmentSubmitHarness({
645
648
  inputs: [],
646
649
  refreshQueuedInputs,
647
650
  removeQueuedInput: async () => null,
651
+ steerQueuedInput: async () => null,
648
652
  },
649
653
  selectedAgentId: 'main',
650
654
  sessionKey: 'session-attachment',
@@ -686,7 +690,7 @@ describe('SessionConversationInput attachment submit', () => {
686
690
  );
687
691
 
688
692
  await act(async () => {
689
- fireEvent.click(screen.getByRole('button', { name: /Queue|排队发送/ }));
693
+ fireEvent.click(screen.getByRole('button', { name: /Send|发送/ }));
690
694
  await Promise.resolve();
691
695
  });
692
696
 
@@ -697,7 +701,7 @@ describe('SessionConversationInput attachment submit', () => {
697
701
  expect(refreshQueuedInputs).not.toHaveBeenCalled();
698
702
 
699
703
  await act(async () => {
700
- resolveSend({ ...createAttachmentRunHandle(), runId: null });
704
+ resolveSend({ ...createAttachmentRunHandle(), delivery: 'queued', runId: null });
701
705
  await Promise.resolve();
702
706
  });
703
707
 
@@ -0,0 +1,46 @@
1
+ import { fireEvent, render, screen } from '@testing-library/react';
2
+ import { describe, expect, it, vi } from 'vitest';
3
+
4
+ import { I18nProvider } from '@/app/components/i18n-provider';
5
+ import { SessionQueuedInputRows } from '@/features/chat/features/conversation/components/session-queued-input-rows';
6
+
7
+ describe('SessionQueuedInputRows', () => {
8
+ it('renders queued image thumbnails and file attachments without losing row actions', () => {
9
+ const editQueuedInput = vi.fn();
10
+ render(
11
+ <I18nProvider>
12
+ <SessionQueuedInputRows
13
+ controller={{
14
+ canEditQueuedInput: true,
15
+ deleteQueuedInput: vi.fn(),
16
+ editQueuedInput,
17
+ queuedInputs: [{
18
+ attachments: [
19
+ {
20
+ mimeType: 'image/png',
21
+ name: 'diagram.png',
22
+ previewUrl: 'data:image/png;base64,aW1hZ2U=',
23
+ },
24
+ {
25
+ mimeType: 'application/pdf',
26
+ name: 'brief.pdf',
27
+ },
28
+ ],
29
+ id: 'queued-rich-content',
30
+ preview: '结合附件继续',
31
+ }],
32
+ steerQueuedInput: vi.fn(),
33
+ }}
34
+ />
35
+ </I18nProvider>,
36
+ );
37
+
38
+ expect(screen.getByRole('img', { name: 'diagram.png' }).getAttribute('src')).toBe(
39
+ 'data:image/png;base64,aW1hZ2U=',
40
+ );
41
+ expect(screen.getByText('brief.pdf')).toBeTruthy();
42
+ expect(screen.getByText('结合附件继续')).toBeTruthy();
43
+ fireEvent.click(screen.getByRole('button', { name: 'Edit queued input' }));
44
+ expect(editQueuedInput).toHaveBeenCalledWith('queued-rich-content');
45
+ });
46
+ });
@@ -318,12 +318,22 @@ export function SessionConversationArea(props: SessionConversationAreaProps) {
318
318
  restoreComposer: inputActions.restoreComposer,
319
319
  setSendError: inputActions.setSendError,
320
320
  });
321
+ const conversationMessages = useMemo(() => {
322
+ const durableMessageIds = new Set(agent.visibleMessages.map(({ id }) => id));
323
+ const pendingSteeringMessages = runQueue.pendingInputs.flatMap((input) =>
324
+ input.placement === "steering" && !durableMessageIds.has(input.message.id)
325
+ ? [{ ...input.message, status: "pending" as const }]
326
+ : []
327
+ );
328
+ return [...agent.visibleMessages, ...pendingSteeringMessages];
329
+ }, [agent.visibleMessages, runQueue.pendingInputs]);
321
330
  const controllerRef = useRef(controller);
322
331
  useEffect(() => {
323
332
  controllerRef.current = controller;
324
333
  }, [controller]);
325
334
  const inputController = useMemo<SessionConversationInputController>(
326
335
  () => ({
336
+ canEditQueuedInput: controller.canEditQueuedInput,
327
337
  canStopGeneration: controller.canStopGeneration,
328
338
  deleteQueuedInput: (id: string) =>
329
339
  controllerRef.current.deleteQueuedInput(id),
@@ -333,14 +343,17 @@ export function SessionConversationArea(props: SessionConversationAreaProps) {
333
343
  primaryAction: controller.primaryAction,
334
344
  queuedInputs: controller.queuedInputs,
335
345
  send: () => controllerRef.current.send(),
346
+ sendSteering: () => controllerRef.current.sendSteering(),
336
347
  sendPresetMessage: (message: string) =>
337
348
  controllerRef.current.sendPresetMessage(message).catch(() => undefined),
338
349
  sendDisabled: controller.sendDisabled,
339
350
  stop: () => controllerRef.current.stop(),
340
351
  stopDisabled: controller.stopDisabled,
352
+ steerQueuedInput: (id: string) => controllerRef.current.steerQueuedInput(id),
341
353
  }),
342
354
  [
343
355
  controller.canStopGeneration,
356
+ controller.canEditQueuedInput,
344
357
  controller.isSending,
345
358
  controller.primaryAction,
346
359
  controller.queuedInputs,
@@ -404,7 +417,7 @@ export function SessionConversationArea(props: SessionConversationAreaProps) {
404
417
  const showWelcome =
405
418
  showWelcomeForDraft &&
406
419
  !sessionKey &&
407
- agent.visibleMessages.length === 0 &&
420
+ conversationMessages.length === 0 &&
408
421
  !agent.isHydrating &&
409
422
  !controller.isSending;
410
423
 
@@ -423,7 +436,7 @@ export function SessionConversationArea(props: SessionConversationAreaProps) {
423
436
  sessionKey && compactingSessionIds.has(sessionKey),
424
437
  )}
425
438
  bottomSlot={showWelcome ? null : conversationFailureSlot}
426
- messages={agent.visibleMessages}
439
+ messages={conversationMessages}
427
440
  messageDetailStates={agent.messageDetailStates}
428
441
  sessionKey={sessionKey}
429
442
  showWelcome={showWelcome}
@@ -71,6 +71,7 @@ type SessionConversationInputQuery = ReturnType<typeof useSessionConversationInp
71
71
  type SkillSource = SessionSkillEntryView['source'];
72
72
 
73
73
  export type SessionConversationInputController = {
74
+ readonly canEditQueuedInput: boolean;
74
75
  readonly canStopGeneration: boolean;
75
76
  readonly deleteQueuedInput: (id: string) => void;
76
77
  readonly editQueuedInput: (id: string) => void;
@@ -80,8 +81,10 @@ export type SessionConversationInputController = {
80
81
  readonly sendDisabled: boolean;
81
82
  readonly stopDisabled: boolean;
82
83
  readonly send: () => Promise<void> | void;
84
+ readonly sendSteering: () => Promise<void> | void;
83
85
  readonly sendPresetMessage: (message: string) => Promise<void> | void;
84
86
  readonly stop: () => Promise<void> | void;
87
+ readonly steerQueuedInput: (id: string) => void;
85
88
  };
86
89
 
87
90
  function toSkillRecords(
@@ -458,13 +461,12 @@ export const SessionConversationInput = memo(function SessionConversationInput(p
458
461
  stopHint: t('chatStopUnavailable'),
459
462
  sendButtonLabel: controller.primaryAction === 'continue'
460
463
  ? t('chatContinueRun')
461
- : controller.isSending
462
- ? t('chatQueueSend')
463
- : t('chatSend'),
464
+ : t('chatSend'),
464
465
  sendIcon: controller.primaryAction,
465
466
  stopButtonLabel: t('chatStop'),
466
467
  contextWindow,
467
468
  onSend: controller.send,
469
+ onAlternateSend: controller.sendSteering,
468
470
  onStop: controller.stop,
469
471
  },
470
472
  }}
@@ -1,5 +1,12 @@
1
1
  import type { ReactNode } from 'react';
2
- import { CornerDownRight, LoaderCircle, Pencil, Trash2 } from 'lucide-react';
2
+ import {
3
+ CornerDownRight,
4
+ Forward,
5
+ LoaderCircle,
6
+ Paperclip,
7
+ Pencil,
8
+ Trash2,
9
+ } from 'lucide-react';
3
10
 
4
11
  import {
5
12
  Tooltip,
@@ -13,9 +20,11 @@ import type {
13
20
  } from '@/features/chat/features/conversation/hooks/use-session-conversation-controller';
14
21
 
15
22
  type SessionQueuedInputRowsController = {
23
+ readonly canEditQueuedInput: boolean;
16
24
  readonly deleteQueuedInput: (id: string) => void;
17
25
  readonly editQueuedInput: (id: string) => void;
18
26
  readonly queuedInputs: readonly SessionConversationQueuedInput[];
27
+ readonly steerQueuedInput: (id: string) => void;
19
28
  };
20
29
 
21
30
  type SessionQueuedInputRowsProps = {
@@ -29,6 +38,44 @@ type QueuedInputIconButtonProps = {
29
38
  readonly onClick: () => void;
30
39
  };
31
40
 
41
+ function QueuedInputContent({ item }: { readonly item: SessionConversationQueuedInput }) {
42
+ const attachments = item.attachments ?? [];
43
+ const visibleAttachments = attachments.slice(0, 3);
44
+ return (
45
+ <div className="flex min-w-0 flex-1 items-center gap-2">
46
+ {visibleAttachments.map((attachment, index) => attachment.previewUrl ? (
47
+ <img
48
+ key={`${attachment.name}-${index}`}
49
+ alt={attachment.name}
50
+ loading="lazy"
51
+ src={attachment.previewUrl}
52
+ title={attachment.name}
53
+ className="h-9 w-9 shrink-0 rounded-md border border-border/70 object-cover"
54
+ />
55
+ ) : (
56
+ <span
57
+ key={`${attachment.name}-${index}`}
58
+ title={attachment.name}
59
+ className="inline-flex min-w-0 max-w-36 shrink items-center gap-1 rounded-md bg-muted px-2 py-1 text-xs text-muted-foreground"
60
+ >
61
+ <Paperclip className="h-3 w-3 shrink-0" />
62
+ <span className="truncate">{attachment.name}</span>
63
+ </span>
64
+ ))}
65
+ {attachments.length > visibleAttachments.length ? (
66
+ <span className="shrink-0 text-xs text-muted-foreground">
67
+ +{attachments.length - visibleAttachments.length}
68
+ </span>
69
+ ) : null}
70
+ {item.preview || attachments.length === 0 ? (
71
+ <span className="min-w-0 flex-1 truncate font-medium text-foreground/80">
72
+ {item.preview || t('chatQueuedBannerAttachmentFallback')}
73
+ </span>
74
+ ) : null}
75
+ </div>
76
+ );
77
+ }
78
+
32
79
  function QueuedInputIconButton({
33
80
  children,
34
81
  disabled = false,
@@ -71,9 +118,7 @@ export function SessionQueuedInputRows({
71
118
  className="flex min-h-8 min-w-0 items-center gap-2 text-sm"
72
119
  >
73
120
  <CornerDownRight className="h-4 w-4 shrink-0 text-muted-foreground/70" />
74
- <span className="min-w-0 flex-1 truncate font-medium text-foreground/80">
75
- {item.preview || t('chatQueuedBannerAttachmentFallback')}
76
- </span>
121
+ <QueuedInputContent item={item} />
77
122
  {item.isSubmitting ? (
78
123
  <span
79
124
  aria-label={t('chatQueuedSubmitting')}
@@ -85,11 +130,20 @@ export function SessionQueuedInputRows({
85
130
  ) : (
86
131
  <div className="flex shrink-0 items-center gap-1.5">
87
132
  <QueuedInputIconButton
88
- label={t('chatQueuedEdit')}
133
+ disabled={!controller.canEditQueuedInput}
134
+ label={controller.canEditQueuedInput
135
+ ? t('chatQueuedEdit')
136
+ : t('chatQueuedEditComposerNotEmpty')}
89
137
  onClick={() => controller.editQueuedInput(item.id)}
90
138
  >
91
139
  <Pencil className="h-4 w-4" />
92
140
  </QueuedInputIconButton>
141
+ <QueuedInputIconButton
142
+ label={t('chatQueuedSteer')}
143
+ onClick={() => controller.steerQueuedInput(item.id)}
144
+ >
145
+ <Forward className="h-4 w-4" />
146
+ </QueuedInputIconButton>
93
147
  <QueuedInputIconButton
94
148
  label={t('chatQueuedDelete')}
95
149
  onClick={() => controller.deleteQueuedInput(item.id)}