@nextclaw/ui 0.19.2 → 0.19.3
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.
- package/CHANGELOG.md +14 -1
- package/dist/assets/{abnfDiagram-VRR7QNED-DvTjgVMf.js → abnfDiagram-VRR7QNED-DrbfiNNF.js} +1 -1
- package/dist/assets/{aiden0z-pptx-renderer.es-C1nfBAXR.js → aiden0z-pptx-renderer.es-DdLb2IVc.js} +1 -1
- package/dist/assets/{api-BH6Oslx4.js → api-CUSlBpt_.js} +3 -3
- package/dist/assets/{appearance-settings-page-zhgfcGgW.js → appearance-settings-page-cTuZdROP.js} +1 -1
- package/dist/assets/{arc-BvhODBXv.js → arc-BXlMc9B0.js} +1 -1
- package/dist/assets/architecture-TIHT7OUA-Bq0i9WiE.js +1 -0
- package/dist/assets/{architectureDiagram-ZJ3FMSHR-BKO9RgNS.js → architectureDiagram-ZJ3FMSHR-DXAJxxO0.js} +1 -1
- package/dist/assets/{blockDiagram-677ZJIJ3-DHpDXy7D.js → blockDiagram-677ZJIJ3-i4Kz7zU0.js} +1 -1
- package/dist/assets/{book-open-DGTS6oEO.js → book-open-BJ-xAIp9.js} +1 -1
- package/dist/assets/{c4Diagram-LMCZKHZV-BG_iQnDr.js → c4Diagram-LMCZKHZV-BDc0TkPd.js} +1 -1
- package/dist/assets/channel-iCaZQ87u.js +1 -0
- package/dist/assets/{channels-list-page-3D5lhZoT.js → channels-list-page-nRHOasWA.js} +2 -2
- package/dist/assets/chunk-2Q5K7J3B-DFQtlnJS.js +1 -0
- package/dist/assets/{chunk-32BRIVSS-DR9M0LLx.js → chunk-32BRIVSS-DuReei0Z.js} +1 -1
- package/dist/assets/{chunk-52WLFC77-698iC3BB.js → chunk-52WLFC77-D8APq0tm.js} +1 -1
- package/dist/assets/{chunk-5FCAYU7R-DikTj9Ey.js → chunk-5FCAYU7R-BsPrNXNS.js} +1 -1
- package/dist/assets/{chunk-5HE753X5-ko0o7_q0.js → chunk-5HE753X5-BRtkOPoJ.js} +1 -1
- package/dist/assets/{chunk-5JV3BV7I-CEHufhgo.js → chunk-5JV3BV7I-ChPc8e8U.js} +1 -1
- package/dist/assets/{chunk-5TONJI2A-CGgno1cu.js → chunk-5TONJI2A-CzTjlJGb.js} +1 -1
- package/dist/assets/{chunk-5VM5RSS4-DkiB4cUQ.js → chunk-5VM5RSS4-xsv_22jR.js} +1 -1
- package/dist/assets/{chunk-7BUUIJ7U-DzptcytQ.js → chunk-7BUUIJ7U-CwECujme.js} +1 -1
- package/dist/assets/{chunk-BIQX33UG-DMRSQJvT.js → chunk-BIQX33UG-DQAm173p.js} +1 -1
- package/dist/assets/{chunk-C7G6YPKG-Bw1BwwuY.js → chunk-C7G6YPKG-BkpajeQb.js} +1 -1
- package/dist/assets/{chunk-CQNSW5MT-C0z6eHBk.js → chunk-CQNSW5MT-ND65pWRA.js} +1 -1
- package/dist/assets/{chunk-CYSBUYHQ-ByPyAouO.js → chunk-CYSBUYHQ-vMu6ijxG.js} +1 -1
- package/dist/assets/{chunk-EMLP6XTP-CJlAXWZg.js → chunk-EMLP6XTP-CEaeW3gH.js} +1 -1
- package/dist/assets/{chunk-EX3LRPZG-Dqq4zKJu.js → chunk-EX3LRPZG-a4ER-HDr.js} +1 -1
- package/dist/assets/{chunk-FWX5IMBZ-B0SNoCmi.js → chunk-FWX5IMBZ-C_8fQssn.js} +2 -2
- package/dist/assets/{chunk-HOUHSVGY-DG_GJih_.js → chunk-HOUHSVGY-CEvw-PEd.js} +1 -1
- package/dist/assets/{chunk-ICXQ74PX-Drxk_6bE.js → chunk-ICXQ74PX-BEtF0fGY.js} +1 -1
- package/dist/assets/{chunk-JG7HCLWE-DdLFPpnd.js → chunk-JG7HCLWE-DGRrVp8-.js} +1 -1
- package/dist/assets/{chunk-JWPE2WC7-B8M4G76F.js → chunk-JWPE2WC7-DRqpp_nM.js} +1 -1
- package/dist/assets/{chunk-MOJQB5TN-XP6FJHY_.js → chunk-MOJQB5TN-BDEriOXs.js} +1 -1
- package/dist/assets/{chunk-MOZMSUNE-C_6x18Qw.js → chunk-MOZMSUNE-JWRNzdnw.js} +1 -1
- package/dist/assets/{chunk-OGEWGWER-N25BzCmG.js → chunk-OGEWGWER-DS2VTgCx.js} +1 -1
- package/dist/assets/{chunk-OSBZ3O6U-ASDEm7y0.js → chunk-OSBZ3O6U-3LiYTBUB.js} +1 -1
- package/dist/assets/{chunk-PUDLZKDR-CTlvAcn7.js → chunk-PUDLZKDR-o9M2s_SO.js} +1 -1
- package/dist/assets/{chunk-Q4XR5HBZ-Da9BWfB6.js → chunk-Q4XR5HBZ-DTB7Ma2N.js} +1 -1
- package/dist/assets/{chunk-QBLGF6JB-Bi6RpTJO.js → chunk-QBLGF6JB-DpkxZJL5.js} +1 -1
- package/dist/assets/{chunk-R7FJI6CG-DkMsSPvK.js → chunk-R7FJI6CG-CRejkHJU.js} +1 -1
- package/dist/assets/{chunk-RYQCIY6F-DZp0eU-j.js → chunk-RYQCIY6F-B1boBjLt.js} +1 -1
- package/dist/assets/{chunk-U6XO7XAA-vfinDSrj.js → chunk-U6XO7XAA-DgKa97bj.js} +1 -1
- package/dist/assets/{chunk-V7JOEXUC-C9Nksccs.js → chunk-V7JOEXUC-DE56eh0N.js} +1 -1
- package/dist/assets/chunk-VAUOI2AC-CF9A-TeE.js +1 -0
- package/dist/assets/{chunk-VR4S4FIN-Xv6zfwXs.js → chunk-VR4S4FIN-BzomPZq2.js} +1 -1
- package/dist/assets/{chunk-WYO6CB5R-mOJNzbWl.js → chunk-WYO6CB5R-C6gy8rSd.js} +2 -2
- package/dist/assets/chunk-XXDRQBXY-BRxdfVCU.js +1 -0
- package/dist/assets/{chunk-YOTPTUD7-1-aKSu4o.js → chunk-YOTPTUD7-BK98yfl9.js} +1 -1
- package/dist/assets/{chunk-ZGVPDNZ5-OJ8veMpK.js → chunk-ZGVPDNZ5-DFXAhwUT.js} +1 -1
- package/dist/assets/{chunk-ZIRB5QZD-SR_Nq_PP.js → chunk-ZIRB5QZD-Bbu6BiNC.js} +1 -1
- package/dist/assets/classDiagram-OUVF2IWQ-Bo28fPi-.js +1 -0
- package/dist/assets/classDiagram-v2-EOCWNBFH-BOVN9wUz.js +1 -0
- package/dist/assets/{config-split-page-B55XD_ZY.js → config-split-page-DO1Oyb-r.js} +1 -1
- package/dist/assets/{confirm-dialog-BCCreI_f.js → confirm-dialog-CCae9Fr4.js} +1 -1
- package/dist/assets/{cose-bilkent-JH36ORCC-NjVgz2Bw.js → cose-bilkent-JH36ORCC-BV8KR4uF.js} +1 -1
- package/dist/assets/cynefin-VYW2F7L2-B7rF3JRp.js +1 -0
- package/dist/assets/{cynefinDiagram-TSTJHNR4-B_17LDqD.js → cynefinDiagram-TSTJHNR4-Ckd6nU9t.js} +1 -1
- package/dist/assets/{dagre-CMe101gk.js → dagre-D_T-FW5i.js} +1 -1
- package/dist/assets/{dagre-VKFMJZFB-qo8nLb3B.js → dagre-VKFMJZFB-CyDaQpD4.js} +1 -1
- package/dist/assets/desktop-update-config-XSV-DjJW.js +1 -0
- package/dist/assets/{diagram-FQU43EPY-Ba3XIklC.js → diagram-FQU43EPY-CghnwS0H.js} +1 -1
- package/dist/assets/{diagram-G47NLZAW-CG45TcUx.js → diagram-G47NLZAW-DK4DJ3Kx.js} +1 -1
- package/dist/assets/{diagram-NH7WQ7WH-2F2HrwV-.js → diagram-NH7WQ7WH-CUgDkZCJ.js} +1 -1
- package/dist/assets/{diagram-OA4YK3LP-CXfWH9NZ.js → diagram-OA4YK3LP-vO2gyH6n.js} +1 -1
- package/dist/assets/{diagram-WEI45ONY-CH_4yepc.js → diagram-WEI45ONY-DbSBRDSr.js} +1 -1
- package/dist/assets/{dist-IRWcJpfm.js → dist-1N9UpL3c.js} +1 -1
- package/dist/assets/{dist-BYSUQaO2.js → dist-C-2H76Cv.js} +1 -1
- package/dist/assets/doc-browser-BHFCZIoL.js +1 -0
- package/dist/assets/{doc-browser-D8rNrWHk.js → doc-browser-CRlpqMwH.js} +1 -1
- package/dist/assets/{doc-browser-context-DjTbIbQc.js → doc-browser-context-C0Bns1J6.js} +1 -1
- package/dist/assets/{docx-preview-CoM4JK7_.js → docx-preview-DfxIm9IB.js} +1 -1
- package/dist/assets/{ebnfDiagram-CCIWWBDH-BwiG4Ea1.js → ebnfDiagram-CCIWWBDH-W7CAcK7r.js} +1 -1
- package/dist/assets/{erDiagram-Q63AITRT-BML3hpnQ.js → erDiagram-Q63AITRT-LA0JWL8-.js} +1 -1
- package/dist/assets/eventmodeling-45OFAUF4-DzQLXa3s.js +1 -0
- package/dist/assets/flowDiagram-23GEKE2U-C81eexJZ.js +1 -0
- package/dist/assets/{form-actions-C8hazgNP.js → form-actions-CMxhPact.js} +1 -1
- package/dist/assets/{ganttDiagram-NO4QXBWP-80enfWbD.js → ganttDiagram-NO4QXBWP-_PiTPavJ.js} +1 -1
- package/dist/assets/gitGraph-TEB2WS4Q-Dt3ZqpFD.js +1 -0
- package/dist/assets/{gitGraphDiagram-IHSO6WYX-CVLqXr-b.js → gitGraphDiagram-IHSO6WYX-DsRB1ZMU.js} +1 -1
- package/dist/assets/index-DdYOaKm7.js +109 -0
- package/dist/assets/index-DxCGKhAw.css +1 -0
- package/dist/assets/info-DKCQHKI2-CCCE7dXC.js +1 -0
- package/dist/assets/{infoDiagram-FWYZ7A6U-Dyvs2t0i.js → infoDiagram-FWYZ7A6U-wIRraYx5.js} +1 -1
- package/dist/assets/{ishikawaDiagram-FXEZZL3T-sSnxDKfh.js → ishikawaDiagram-FXEZZL3T-CNzsrcrh.js} +1 -1
- package/dist/assets/{journeyDiagram-5HDEW3XC-D22V71Mf.js → journeyDiagram-5HDEW3XC-Dy9huHxy.js} +1 -1
- package/dist/assets/{kanban-definition-HUTT4EX6-D3ds0p-g.js → kanban-definition-HUTT4EX6-BOCIN365.js} +1 -1
- package/dist/assets/{line-CbyFs065.js → line-BxsizOTs.js} +1 -1
- package/dist/assets/{linear-4DKtnZBS.js → linear-C_YHf92I.js} +1 -1
- package/dist/assets/loader-circle-B-UdXjl4.js +1 -0
- package/dist/assets/{map-CcZzEoz6.js → map-CobcjgZ8.js} +1 -1
- package/dist/assets/mcp-marketplace-page-BUIGtSda.js +1 -0
- package/dist/assets/mcp-marketplace-page-CStb8ml2.js +9 -0
- package/dist/assets/{mermaid-parser.core-DPmMOjXD.js → mermaid-parser.core-D7lu2RbA.js} +2 -2
- package/dist/assets/{mermaid.core-BSf-ddRa.js → mermaid.core-CbfW2Rk8.js} +3 -3
- package/dist/assets/{mindmap-definition-LN4V7U3C-BUTwWPyx.js → mindmap-definition-LN4V7U3C-DUjGXlIC.js} +1 -1
- package/dist/assets/model-config-page-BkfUYm95.js +1 -0
- package/dist/assets/{navigation-link-kGAcL2N2.js → navigation-link-v3FaWcIr.js} +1 -1
- package/dist/assets/{ordinal-BgLYtOZE.js → ordinal-CjoUCHto.js} +1 -1
- package/dist/assets/packet-7NZHBO7P-D19yoxFV.js +1 -0
- package/dist/assets/{pegDiagram-2B236MQR-BEu_ZBjc.js → pegDiagram-2B236MQR-BQY2DgiP.js} +1 -1
- package/dist/assets/pie-RZYD4A2V-hMaJbLrn.js +1 -0
- package/dist/assets/{pieDiagram-ENE6RG2P-CAwAwuBd.js → pieDiagram-ENE6RG2P-D18owe4D.js} +1 -1
- package/dist/assets/plus-Dw9QFnKr.js +1 -0
- package/dist/assets/{privacy-settings-page-v9LTFvJe.js → privacy-settings-page-CYM1Dxwo.js} +1 -1
- package/dist/assets/providers-config-page-fxL4I4bz.js +1 -0
- package/dist/assets/{quadrantDiagram-ABIIQ3AL-CwE6F-_2.js → quadrantDiagram-ABIIQ3AL-B8F7YoTA.js} +1 -1
- package/dist/assets/radar-I7S5WNFK-C8BkvScA.js +1 -0
- package/dist/assets/railroad-3IZDKUUU-CozZOitC.js +1 -0
- package/dist/assets/railroad-abnf-AHOZXSZD-BqIoFXFi.js +1 -0
- package/dist/assets/railroad-ebnf-EBAXGLYW-CD2e3dng.js +1 -0
- package/dist/assets/railroad-peg-LSFZ7HO6-DoKlRUOF.js +1 -0
- package/dist/assets/{railroadDiagram-RFXS5EU6-xgLekU3B.js → railroadDiagram-RFXS5EU6-RU8GpxN5.js} +1 -1
- package/dist/assets/{react-Dwvp5yeX.js → react-Dlz_IxoP.js} +1 -1
- package/dist/assets/{refresh-cw-C_oj_h_s.js → refresh-cw-CK4I5UFv.js} +1 -1
- package/dist/assets/remote-B0pPgtC6.js +1 -0
- package/dist/assets/{requirementDiagram-TGXJPOKE-CyVUzEX5.js → requirementDiagram-TGXJPOKE-ubPOmmNi.js} +1 -1
- package/dist/assets/rotate-cw-Z48dmh13.js +1 -0
- package/dist/assets/runtime-config-page-C7vksFVD.js +1 -0
- package/dist/assets/{sankeyDiagram-HTMAVEWB-JK6bFmYB.js → sankeyDiagram-HTMAVEWB-8L_hXJWW.js} +1 -1
- package/dist/assets/{save-BZE6H02N.js → save-Bo37cIFb.js} +1 -1
- package/dist/assets/{search-C7Tj40kC.js → search-CZiEm7YD.js} +1 -1
- package/dist/assets/search-config-page-DKdEf7m9.js +1 -0
- package/dist/assets/{secrets-config-page-B__VLAEQ.js → secrets-config-page-COlMqSfL.js} +2 -2
- package/dist/assets/security-config-CitLihXA.js +1 -0
- package/dist/assets/{select-fZ5tifar.js → select-BACzapV3.js} +1 -1
- package/dist/assets/{sequenceDiagram-DBY2YBRQ-BR5IokXV.js → sequenceDiagram-DBY2YBRQ-Dom6Qor5.js} +1 -1
- package/dist/assets/{sizeCapture-X5ZJPWSS-SxG_YnMA.js → sizeCapture-X5ZJPWSS-cZcFSpLJ.js} +1 -1
- package/dist/assets/skeleton-B2PCAZJG.js +1 -0
- package/dist/assets/{src-vgz8E_-O.js → src-BE9E1xcR.js} +1 -1
- package/dist/assets/{stateDiagram-2N3HPSRC-DD4OoYhn.js → stateDiagram-2N3HPSRC-CHrpxmGO.js} +1 -1
- package/dist/assets/stateDiagram-v2-6OUMAXLB-b1pZQlDD.js +1 -0
- package/dist/assets/{swimlanes-5IMT3BWC-BWkFeyCT.js → swimlanes-5IMT3BWC-D6nJS2xQ.js} +2 -2
- package/dist/assets/swimlanesDiagram-G3AALYLV-CowxgWVn.js +8 -0
- package/dist/assets/{tag-chip-CwYZcRoe.js → tag-chip-BnamTQVX.js} +1 -1
- package/dist/assets/{timeline-definition-FHXFAJF6-VTRPy6KA.js → timeline-definition-FHXFAJF6-DJNn-HQO.js} +1 -1
- package/dist/assets/treeView-QDETBFTQ-BQlYkJbz.js +1 -0
- package/dist/assets/treemap-6X3UGDF4-DzTOd1bd.js +1 -0
- package/dist/assets/{vennDiagram-L72KCM5P-Bvk42Pek.js → vennDiagram-L72KCM5P-CCEzHoXt.js} +1 -1
- package/dist/assets/wardley-OPB4EBWU-D80jQc0y.js +1 -0
- package/dist/assets/{wardleyDiagram-EHGQE667-DOFjx2s3.js → wardleyDiagram-EHGQE667-BhyVxVXt.js} +1 -1
- package/dist/assets/x-BzYEzkLA.js +1 -0
- package/dist/assets/{xychartDiagram-FW5EYKEG-DY5yRTUc.js → xychartDiagram-FW5EYKEG-CacW_246.js} +1 -1
- package/dist/index.html +70 -22
- package/index.html +49 -1
- package/package.json +8 -8
- package/src/app/index.tsx +1 -3
- package/src/app/managers/__tests__/viewport-layout.manager.test.ts +12 -0
- package/src/app/managers/viewport-layout.manager.ts +12 -0
- package/src/app/stores/viewport-layout.store.ts +23 -11
- package/src/features/apps/components/__tests__/app-marketplace-catalog.test.tsx +161 -0
- package/src/features/apps/components/app-marketplace-catalog.tsx +36 -7
- package/src/features/apps/components/app-marketplace-detail.tsx +29 -6
- package/src/features/apps/components/app-marketplace-dialog.tsx +71 -55
- package/src/features/apps/components/app-marketplace-operation.tsx +128 -16
- package/src/features/apps/components/app-packages-panel.tsx +1 -0
- package/src/features/apps/utils/app-marketplace-platform.utils.test.ts +59 -0
- package/src/features/apps/utils/app-marketplace-platform.utils.ts +33 -0
- package/src/features/chat/features/conversation/components/__tests__/session-conversation-area.test.tsx +6 -0
- package/src/features/chat/features/conversation/components/__tests__/session-conversation-input.streaming.test.tsx +6 -2
- package/src/features/chat/features/conversation/components/__tests__/session-queued-input-rows.test.tsx +46 -0
- package/src/features/chat/features/conversation/components/session-conversation-area.tsx +15 -2
- package/src/features/chat/features/conversation/components/session-conversation-input.tsx +5 -3
- package/src/features/chat/features/conversation/components/session-queued-input-rows.tsx +59 -5
- package/src/features/chat/features/conversation/hooks/__tests__/use-session-conversation-controller.test.tsx +35 -1
- package/src/features/chat/features/conversation/hooks/__tests__/use-session-run-queue.test.tsx +25 -19
- package/src/features/chat/features/conversation/hooks/use-session-conversation-controller.ts +148 -134
- package/src/features/chat/features/conversation/hooks/use-session-pending-input-actions.ts +135 -0
- package/src/features/chat/features/conversation/hooks/use-session-run-queue.ts +19 -4
- package/src/features/chat/features/conversation/utils/__tests__/session-queued-input.utils.test.ts +52 -2
- package/src/features/chat/features/conversation/utils/session-queued-input.utils.ts +46 -5
- package/src/features/chat/features/message/utils/chat-message-texts.utils.ts +1 -0
- package/src/features/chat/features/ncp/hooks/__tests__/use-hydrated-ncp-agent.test.tsx +102 -0
- package/src/features/chat/features/ncp/hooks/__tests__/use-ncp-session-conversation.test.tsx +117 -1
- package/src/features/chat/features/ncp/hooks/use-ncp-session-conversation.ts +2 -2
- package/src/features/chat/features/ncp/hooks/use-ncp-session-message-history.ts +31 -81
- package/src/features/chat/features/ncp/hooks/use-ncp-session-seed-loader.ts +134 -0
- package/src/features/chat/features/session/utils/ncp-session-adapter.utils.ts +1 -6
- package/src/features/panel-apps/components/__tests__/panel-app-list-item.test.tsx +8 -0
- package/src/features/panel-apps/components/__tests__/panel-app-main-sidebar-nav.test.tsx +99 -13
- package/src/features/panel-apps/components/panel-app-main-sidebar-menu-item.tsx +4 -0
- package/src/features/panel-apps/components/panel-app-main-sidebar-nav.tsx +166 -31
- package/src/shared/lib/api/utils/ncp-attachments.utils.ts +8 -1
- package/src/shared/lib/api/utils/ncp-session.utils.ts +1 -0
- package/src/shared/lib/i18n/locales/en-US/chat.json +3 -0
- package/src/shared/lib/i18n/locales/en-US/doc-browser.json +11 -1
- package/src/shared/lib/i18n/locales/zh-CN/chat.json +3 -0
- package/src/shared/lib/i18n/locales/zh-CN/doc-browser.json +11 -1
- package/src/vite-env.d.ts +9 -0
- package/dist/assets/architecture-TIHT7OUA-Bjp9jf0h.js +0 -1
- package/dist/assets/channel-5T3pdR-s.js +0 -1
- package/dist/assets/chat-page-BXu3hnOK.js +0 -1
- package/dist/assets/chunk-2Q5K7J3B-DTZ3tGfE.js +0 -1
- package/dist/assets/chunk-VAUOI2AC-HLGIHQ6k.js +0 -1
- package/dist/assets/chunk-XXDRQBXY-Dl0m6Ni-.js +0 -1
- package/dist/assets/classDiagram-OUVF2IWQ-DcDnzGNX.js +0 -1
- package/dist/assets/classDiagram-v2-EOCWNBFH-BKXKMzKM.js +0 -1
- package/dist/assets/cynefin-VYW2F7L2-BWg7U2UD.js +0 -1
- package/dist/assets/desktop-update-config-Blol-Umu.js +0 -1
- package/dist/assets/doc-browser-BExPq1Hk.js +0 -1
- package/dist/assets/eventmodeling-45OFAUF4-SB-J4tpq.js +0 -1
- package/dist/assets/flowDiagram-23GEKE2U-BQ_u-eLM.js +0 -1
- package/dist/assets/gitGraph-TEB2WS4Q-DPxNwsFd.js +0 -1
- package/dist/assets/index-Btj8yN4T.css +0 -1
- package/dist/assets/index-CHcGqqBe.js +0 -109
- package/dist/assets/info-DKCQHKI2-DNlNGH90.js +0 -1
- package/dist/assets/loader-circle-C75DUmmm.js +0 -1
- package/dist/assets/mcp-marketplace-page-CIMRYgOY.js +0 -9
- package/dist/assets/mcp-marketplace-page-D2NlhG1k.js +0 -1
- package/dist/assets/model-config-page-ChR-ENjp.js +0 -1
- package/dist/assets/packet-7NZHBO7P-BpeiqkBZ.js +0 -1
- package/dist/assets/pie-RZYD4A2V-Cy1yxsPd.js +0 -1
- package/dist/assets/plus-TzSIMKWW.js +0 -1
- package/dist/assets/provider-scoped-model-input-DiSpKSk0.js +0 -1
- package/dist/assets/providers-config-page-BvXa03cp.js +0 -1
- package/dist/assets/radar-I7S5WNFK-CEcPKCj8.js +0 -1
- package/dist/assets/railroad-3IZDKUUU-CW38eNDs.js +0 -1
- package/dist/assets/railroad-abnf-AHOZXSZD-ByrylcXf.js +0 -1
- package/dist/assets/railroad-ebnf-EBAXGLYW-CNB6GwP-.js +0 -1
- package/dist/assets/railroad-peg-LSFZ7HO6-x6hnqlBG.js +0 -1
- package/dist/assets/remote-BgYduCQR.js +0 -1
- package/dist/assets/rotate-cw-BT8HxlcB.js +0 -1
- package/dist/assets/runtime-config-page-BuYof2Ty.js +0 -1
- package/dist/assets/search-config-page-CQTJOsOf.js +0 -1
- package/dist/assets/security-config-CtJqaPVL.js +0 -1
- package/dist/assets/settings-2-BHGgNlOH.js +0 -1
- package/dist/assets/skeleton-CJLc1CWn.js +0 -1
- package/dist/assets/stateDiagram-v2-6OUMAXLB-ZC6dFORv.js +0 -1
- package/dist/assets/swimlanesDiagram-G3AALYLV-CRkh51Im.js +0 -8
- package/dist/assets/treeView-QDETBFTQ-Bw0DSCH7.js +0 -1
- package/dist/assets/treemap-6X3UGDF4-DFWBFcfh.js +0 -1
- package/dist/assets/wardley-OPB4EBWU-CoWturk0.js +0 -1
- package/dist/assets/x-BMl7cucN.js +0 -1
- /package/dist/assets/{array-NADKd9aU.js → array-COEOjKUv.js} +0 -0
- /package/dist/assets/{chunk-KEIR6QF5-BuyuzJJL.js → chunk-KEIR6QF5-D7oHAcry.js} +0 -0
- /package/dist/assets/{chunk-Y2CYZVJY-D_B2ZRNt.js → chunk-Y2CYZVJY-08IUpgTP.js} +0 -0
- /package/dist/assets/{config-hints-BcfVUMij.js → config-hints-BoE41XDI.js} +0 -0
- /package/dist/assets/{cytoscape.esm-BANTlgza.js → cytoscape.esm-HifWufkp.js} +0 -0
- /package/dist/assets/{defaultLocale-C4_L0aEs.js → defaultLocale-CA-_SsC1.js} +0 -0
- /package/dist/assets/{dist-DAPEIJbz.js → dist-C60rvfy8.js} +0 -0
- /package/dist/assets/{graphlib-ChqXg0dI.js → graphlib-CGYCNsoJ.js} +0 -0
- /package/dist/assets/{host-capabilities-D9C4CBK5.js → host-capabilities-DPNRAjIj.js} +0 -0
- /package/dist/assets/{init-D8wZg9xq.js → init-C3plFVsG.js} +0 -0
- /package/dist/assets/{jszip.min-CkbBfwAQ.js → jszip.min-CeYs17Gn.js} +0 -0
- /package/dist/assets/{katex-TZJFa0J_.js → katex-DRThweSG.js} +0 -0
- /package/dist/assets/{middleware-B3k-TSmZ.js → middleware-D05fK5Sb.js} +0 -0
- /package/dist/assets/{path-DodfxcX7.js → path-BKtKxQ6Y.js} +0 -0
- /package/dist/assets/{rough.esm-CUaa1Rbe.js → rough.esm-DurL7-u7.js} +0 -0
- /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 {
|
|
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
|
-
|
|
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={
|
|
92
|
+
disabled={disabled}
|
|
30
93
|
onClick={onAction}
|
|
31
94
|
className="shrink-0 rounded-full px-4"
|
|
32
95
|
>
|
|
33
|
-
{
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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=
|
|
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=
|
|
50
|
-
style={{ width:
|
|
161
|
+
className="h-full rounded-full bg-primary transition-[width] duration-300"
|
|
162
|
+
style={{ width: `${progress}%` }}
|
|
51
163
|
/>
|
|
52
164
|
</div>
|
|
53
|
-
<p className=
|
|
54
|
-
{
|
|
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: /
|
|
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
|
-
|
|
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={
|
|
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
|
-
:
|
|
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 {
|
|
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
|
-
<
|
|
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
|
-
|
|
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)}
|