@openhands/agent-canvas 1.10.0 → 1.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/README.windows.md +2 -2
- package/build/assets/{acp-providers-DLe_gVDY.js → acp-providers-BkL3HWa3.js} +1 -1
- package/build/assets/{active-backend-context-B0wI7OzM.js → active-backend-context-B_bg5h9O.js} +1 -1
- package/build/assets/{agent-context-settings-CaQtdNA-.js → agent-context-settings-CzWS1DgR.js} +1 -1
- package/build/assets/{agent-profiles-settings-CvuErjp6.js → agent-profiles-settings-hd2cAIZM.js} +1 -1
- package/build/assets/agent-server-conversation-service.api-BheU4HX3.js +86 -0
- package/build/assets/{agent-settings-UE-U4Xii.js → agent-settings-D2J1dKVd.js} +1 -1
- package/build/assets/agent-settings-D_SVrWhI.js +1 -0
- package/build/assets/{alert-banner-BMaOtTCG.js → alert-banner-lrK-2_0Y.js} +1 -1
- package/build/assets/{api-key-entry-screen-Cy66vT4D.js → api-key-entry-screen-D70oJj2c.js} +1 -1
- package/build/assets/app-settings-D0OKHB3M.js +1 -0
- package/build/assets/{automation-detail-fuITm6i3.js → automation-detail-C0SxWTnQ.js} +3 -3
- package/build/assets/{automation-export-CRq40mux.js → automation-export-BjQa_fT6.js} +1 -1
- package/build/assets/{automation-setup-route-EXDazOHW.js → automation-setup-route-BzCNpljP.js} +1 -1
- package/build/assets/{automation-templates-BArZsaxP.js → automation-templates-D9gs6ihu.js} +1 -1
- package/build/assets/{automations-list-CG4HKOTn.js → automations-list-dZg8zOCl.js} +1 -1
- package/build/assets/{back-nav-button-BZB6H7xz.js → back-nav-button-B3pYwmUJ.js} +1 -1
- package/build/assets/{backend-form-modal-B_lXjOGa.js → backend-form-modal-HwclKrUS.js} +1 -1
- package/build/assets/{backend-synced-settings-badge-B_NXJ3u1.js → backend-synced-settings-badge-YI3k09Wr.js} +1 -1
- package/build/assets/{badge-C64c-7PO.js → badge-Dpn3-LTV.js} +1 -1
- package/build/assets/{base-modal-C0RQPug5.js → base-modal-3NmIOKDm.js} +1 -1
- package/build/assets/{brand-button-DJmFCyMK.js → brand-button-CJcwjnfb.js} +1 -1
- package/build/assets/{browser-D3gd2n83.js → browser--eoQOo4M.js} +1 -1
- package/build/assets/{browser-tab-BicAWuJ1.js → browser-tab-4CPdj3J3.js} +1 -1
- package/build/assets/{chat-send-button-CCrH1E8u.js → chat-send-button-B8n6-DQA.js} +1 -1
- package/build/assets/{circle-plus-check-toggle-CB_sNERY.js → circle-plus-check-toggle-BVzv5EkZ.js} +1 -1
- package/build/assets/{cloud-funnel-analytics-XynamQGU.js → cloud-funnel-analytics-CcSez21r.js} +1 -1
- package/build/assets/{combobox-caret-Bwiifgjs.js → combobox-caret-BlmRwiM8.js} +1 -1
- package/build/assets/command-menu-BKOLj-TC.js +1 -0
- package/build/assets/{condenser-settings-cNGy75gq.js → condenser-settings-DLQogFnB.js} +1 -1
- package/build/assets/{confirmation-modal-BATFmFyP.js → confirmation-modal-DAH0XtOp.js} +1 -1
- package/build/assets/{context-menu-list-item-DhJCcBA0.js → context-menu-list-item-BQhmFVEK.js} +1 -1
- package/build/assets/{conversation-Csdxh9Xt.js → conversation-Cmyw9bFh.js} +4 -4
- package/build/assets/conversation-CvoGZ5vC.js +1 -0
- package/build/assets/conversation-panel-CvoGZ5vC.js +1 -0
- package/build/assets/{conversation-state-store-C2QzhPaK.js → conversation-state-store-CtgILxWF.js} +1 -1
- package/build/assets/{conversation-tab-empty-state-D3fb5xIA.js → conversation-tab-empty-state-C9sfDOlt.js} +1 -1
- package/build/assets/{copy-to-clipboard-button-c4MQwKGj.js → copy-to-clipboard-button-DatoElj8.js} +1 -1
- package/build/assets/{device-verify-oFpQoLgm.js → device-verify-BIjXeW9N.js} +1 -1
- package/build/assets/{dropdown-classes-f91ARMV1.js → dropdown-classes-zFW7qPSK.js} +1 -1
- package/build/assets/{entry.client-BCzY2Bf3.js → entry.client-EBn3ZtNP.js} +2 -2
- package/build/assets/{enum-filter-dropdown-HG3q3rkV.js → enum-filter-dropdown-CctILg5t.js} +1 -1
- package/build/assets/{environment-switch-overlay-CquZXolz.js → environment-switch-overlay-Cvmo6clk.js} +1 -1
- package/build/assets/{extensions-hub-BeweE0PW.js → extensions-hub-BZ3HYztk.js} +1 -1
- package/build/assets/{extensions-navigation-Dd58NlUF.js → extensions-navigation-Bnx32WAb.js} +1 -1
- package/build/assets/{files-tab-CLLG55-q.js → files-tab-D5zuU5TY.js} +1 -1
- package/build/assets/{form-control-classes-pHtZUI6K.js → form-control-classes-DLN3Qgub.js} +1 -1
- package/build/assets/format-model-name-BN_rx1Y3.js +1 -0
- package/build/assets/{format-relative-time-6-3uvOXe.js → format-relative-time-BerVamGl.js} +1 -1
- package/build/assets/{git-provider-icon-wg7ShB98.js → git-provider-icon-zuKz2cwo.js} +1 -1
- package/build/assets/{git-repo-dropdown-CWnaUyxL.js → git-repo-dropdown-B5VnMftu.js} +1 -1
- package/build/assets/git-repo-dropdown-Bo7ciSNX.js +1 -0
- package/build/assets/{highlighted-source-view-DBtI3por.js → highlighted-source-view-CgJfD5VT.js} +1 -1
- package/build/assets/home-DqyeeFA_.js +1 -0
- package/build/assets/{home-BpZQkDWM.js → home-Y0aDou-a.js} +1 -1
- package/build/assets/{home-store-DM4qw1lF.js → home-store-Cubdq0uP.js} +1 -1
- package/build/assets/index-home-DqyeeFA_.js +1 -0
- package/build/assets/install-server-modal-DW6dSyGr.js +1 -0
- package/build/assets/{kebab-menu-item-content-By16Tm_0.js → kebab-menu-item-content-CKydZrbH.js} +1 -1
- package/build/assets/{launch-C9uijypN.js → launch-c4GEP8oo.js} +1 -1
- package/build/assets/llm-not-configured-banner-DCE_0B4I.js +27 -0
- package/build/assets/llm-settings-8eWBF3Ij.js +1 -0
- package/build/assets/llm-settings-D9ko42Pi.js +1 -0
- package/build/assets/{llm-subscription-service-DwjJ8-_X.js → llm-subscription-service-B3K-97lP.js} +1 -1
- package/build/assets/{loading-spinner-6Yj-YyoS.js → loading-spinner-CN3riY7k.js} +1 -1
- package/build/assets/{manage-backends-modal-DrwErS0N.js → manage-backends-modal-DTCME_Pu.js} +1 -1
- package/build/assets/manifest-e1c3bc3b.js +1 -0
- package/build/assets/{manifest-subpage-layout-CtqVwIvJ.js → manifest-subpage-layout-Ds7G5GZ8.js} +1 -1
- package/build/assets/map-provider-7Bp2aih5.js +1 -0
- package/build/assets/markdown-renderer-DzvgdZey.js +8 -0
- package/build/assets/mcp-Da7wFU7A.js +9 -0
- package/build/assets/{mcp-marketplace-utils-BMnYz0Gc.js → mcp-marketplace-utils-DSLou4n7.js} +1 -1
- package/build/assets/messages-B2l0qLVk.js +40 -0
- package/build/assets/{modal-body-BHGCSAtj.js → modal-body-duRaxj_K.js} +1 -1
- package/build/assets/{modal-classes-DSTLuxkA.js → modal-classes-CLEXO6dj.js} +1 -1
- package/build/assets/{modal-close-button-dheWZ2ys.js → modal-close-button-D0aT02hs.js} +1 -1
- package/build/assets/npm-Cswluf6-.js +1 -0
- package/build/assets/{onboarding-5CZvRBbh.js → onboarding-BKbTYlW1.js} +1 -1
- package/build/assets/{onboarding-modal-CD2ehuxt.js → onboarding-modal-DrrGlEQq.js} +1 -1
- package/build/assets/{organization-service.api-Dsuoep0g.js → organization-service.api-DK8csGvK.js} +1 -1
- package/build/assets/{planner-tab-DeO7Thb0.js → planner-tab-D-mZ1I7n.js} +1 -1
- package/build/assets/{plugins-management-service-C9f75R6X.js → plugins-management-service-CywqyfLK.js} +1 -1
- package/build/assets/{profiles-service.api-Dl20_2IM.js → profiles-service.api-vcE_jJCe.js} +1 -1
- package/build/assets/{providers-CeuhK46N.js → providers-CdrPPm0z.js} +1 -1
- package/build/assets/{proxy-CIGI9zQC.js → proxy-b4aqL4SX.js} +1 -1
- package/build/assets/recommended-automations-launcher-DIYgUdMm.js +1 -0
- package/build/assets/{root-BQPNqNvD.js → root-CBBi6IAe.js} +2 -2
- package/build/assets/root-CRK9fOFd.css +1 -0
- package/build/assets/root-layout-BFN2rCOy.js +2 -0
- package/build/assets/{schema-field-z2vSLuLo.js → schema-field-Z-rMg9ti.js} +1 -1
- package/build/assets/{sdk-section-page-BnCXAytf.js → sdk-section-page-m_cjygxR.js} +1 -1
- package/build/assets/{secrets-service-DGUNlwQd.js → secrets-service-BHF4fxML.js} +1 -1
- package/build/assets/{secrets-settings-DUmML_dK.js → secrets-settings-aBGXtQhX.js} +1 -1
- package/build/assets/{segmented-toggle-eSofF7gn.js → segmented-toggle-SmvsR3jW.js} +1 -1
- package/build/assets/settings-CZPGNens.js +1 -0
- package/build/assets/{settings-dropdown-input-Cx4g7Xfl.js → settings-dropdown-input-TWIeiG02.js} +1 -1
- package/build/assets/{settings-index-BECdgecg.js → settings-index-Dtlegd6s.js} +1 -1
- package/build/assets/{settings-input-BQY115xH.js → settings-input-D61Vzp9e.js} +1 -1
- package/build/assets/{settings-list-classes-C9cLrrcc.js → settings-list-classes-sE0ASr1s.js} +1 -1
- package/build/assets/{settings-modal-ChBJNTdd.js → settings-modal-CalzJP2l.js} +1 -1
- package/build/assets/{settings-service.api-Dz9cZyvx.js → settings-service.api-DfPYaxZJ.js} +1 -1
- package/build/assets/{settings-switch-BIodMVz9.js → settings-switch-Drt-rF5s.js} +1 -1
- package/build/assets/{settings-utils-BL_Z9uIe.js → settings-utils-gw2ppAvw.js} +1 -1
- package/build/assets/{shared-conversation-CSF0aTuE.js → shared-conversation-BIAcuORk.js} +1 -1
- package/build/assets/{wrench-DRitTfwe.js → shield-check-D2cpEpv3.js} +1 -1
- package/build/assets/{sidebar-layout-DEycoFd_.js → sidebar-layout-Dxg6zc2K.js} +1 -1
- package/build/assets/{sidebar-mobile-menu-toggle-DeRvTupV.js → sidebar-mobile-menu-toggle-C6HpQ55l.js} +1 -1
- package/build/assets/{sidebar-nav-link-BjpQ-NVc.js → sidebar-nav-link-CxLoG-dG.js} +1 -1
- package/build/assets/sidebar-store-BJeYY-E9.js +1 -0
- package/build/assets/{skill-card-pill-row-BH6fPGba.js → skill-card-pill-row-B_BYBONu.js} +1 -1
- package/build/assets/{skill-icon-badge-D4uznfjU.js → skill-icon-badge-CN41Hy9l.js} +1 -1
- package/build/assets/{skill-scope-DH_AUUUP.js → skill-scope-Dt1V5X2p.js} +1 -1
- package/build/assets/{skills-plugins-vfvOk34h.js → skills-plugins-B-H0cCOP.js} +1 -1
- package/build/assets/skills-settings-DthdIDUb.js +2 -0
- package/build/assets/{styled-tooltip-CQ9JEFiO.js → styled-tooltip-C5C07RQC.js} +1 -1
- package/build/assets/{task-list-tab-DeF_uXdw.js → task-list-tab-BTde81Iu.js} +1 -1
- package/build/assets/{terminal-DRc4rup7.js → terminal-DRrJo-cR.js} +1 -1
- package/build/assets/{toggle-switch--8n8NeV_.js → toggle-switch-CIioisto.js} +1 -1
- package/build/assets/{typography-BSbUhJPn.js → typography-D9tiHct7.js} +1 -1
- package/build/assets/{use-acp-credential-form-BGKFi1VL.js → use-acp-credential-form-DGyo0QvL.js} +1 -1
- package/build/assets/{use-activate-agent-profile-2nvALjC-.js → use-activate-agent-profile-DrPzIKJF.js} +1 -1
- package/build/assets/{use-agent-profiles-BlQA67MW.js → use-agent-profiles-B37XWDLq.js} +1 -1
- package/build/assets/{use-can-manage-org-profiles-Clw5Dh1s.js → use-can-manage-org-profiles-BzE28H3x.js} +1 -1
- package/build/assets/{use-cloud-current-user-id-B8AQSQFM.js → use-cloud-current-user-id-U6HPjy50.js} +1 -1
- package/build/assets/{use-config-C3zGuUQI.js → use-config-DapX3pjC.js} +1 -1
- package/build/assets/{use-create-conversation-FaP0kUpg.js → use-create-conversation-CRf1dCm5.js} +1 -1
- package/build/assets/{use-create-secret-CgQ3pXd6.js → use-create-secret-DcpT3RPw.js} +1 -1
- package/build/assets/{use-llm-profiles-C-tDO6UB.js → use-llm-profiles-CHxqoHiN.js} +1 -1
- package/build/assets/{use-manifest-capabilities-DLP5wCxp.js → use-manifest-capabilities-zihHDbMW.js} +1 -1
- package/build/assets/{use-onboarding-completion-BiCdXvlU.js → use-onboarding-completion-6kWOW3ul.js} +1 -1
- package/build/assets/{use-plugins-marketplace-DRFNLBlr.js → use-plugins-marketplace-CqkR8kLE.js} +1 -1
- package/build/assets/{use-save-agent-profile-CaGVAudI.js → use-save-agent-profile-CH9_EXQG.js} +1 -1
- package/build/assets/{use-save-settings-C9B8Y-h3.js → use-save-settings-DIEurJ3D.js} +1 -1
- package/build/assets/{use-settings-3RcgdUuZ.js → use-settings-BmLzQjnC.js} +1 -1
- package/build/assets/use-settings-nav-items-DV9qgeu7.js +16 -0
- package/build/assets/{use-tracking-CTtkLAmx.js → use-tracking-salbPJxT.js} +1 -1
- package/build/assets/{use-user-conversation-Bei82QsZ.js → use-user-conversation-Dt-W6uNw.js} +1 -1
- package/build/assets/utils-DPQfkd-7.js +1 -0
- package/build/assets/{verification-settings-nD61Ijph.js → verification-settings-oi54wEc1.js} +1 -1
- package/build/assets/wrench-B1_H-tL1.js +1 -0
- package/build/index.html +4 -4
- package/build/locales/ar/openhands.json +39 -2
- package/build/locales/ca/openhands.json +39 -2
- package/build/locales/de/openhands.json +39 -2
- package/build/locales/en/openhands.json +39 -2
- package/build/locales/es/openhands.json +39 -2
- package/build/locales/fr/openhands.json +39 -2
- package/build/locales/it/openhands.json +39 -2
- package/build/locales/ja/openhands.json +39 -2
- package/build/locales/ko-KR/openhands.json +39 -2
- package/build/locales/no/openhands.json +39 -2
- package/build/locales/pt/openhands.json +39 -2
- package/build/locales/tr/openhands.json +39 -2
- package/build/locales/uk/openhands.json +39 -2
- package/build/locales/zh-CN/openhands.json +39 -2
- package/build/locales/zh-TW/openhands.json +39 -2
- package/config/defaults.json +1 -1
- package/dist/api/agent-server-adapter.cjs +3 -3
- package/dist/api/agent-server-adapter.cjs.map +1 -1
- package/dist/api/agent-server-adapter.d.ts +32 -5
- package/dist/api/agent-server-adapter.js +120 -91
- package/dist/api/agent-server-adapter.js.map +1 -1
- package/dist/api/cloud/conversation-service.api.cjs +1 -1
- package/dist/api/cloud/conversation-service.api.cjs.map +1 -1
- package/dist/api/cloud/conversation-service.api.d.ts +19 -2
- package/dist/api/cloud/conversation-service.api.js +51 -47
- package/dist/api/cloud/conversation-service.api.js.map +1 -1
- package/dist/api/conversation-file-upload.api.cjs +1 -1
- package/dist/api/conversation-file-upload.api.js +10 -10
- package/dist/api/conversation-service/agent-server-conversation-service.api.cjs +1 -1
- package/dist/api/conversation-service/agent-server-conversation-service.api.cjs.map +1 -1
- package/dist/api/conversation-service/agent-server-conversation-service.api.js +55 -61
- package/dist/api/conversation-service/agent-server-conversation-service.api.js.map +1 -1
- package/dist/api/event-service/event-service.api.cjs +1 -1
- package/dist/api/event-service/event-service.api.js +12 -12
- package/dist/api/launch-child-conversation-client-tool.cjs +47 -0
- package/dist/api/launch-child-conversation-client-tool.cjs.map +1 -0
- package/dist/api/launch-child-conversation-client-tool.d.ts +3 -0
- package/dist/api/launch-child-conversation-client-tool.js +93 -0
- package/dist/api/launch-child-conversation-client-tool.js.map +1 -0
- package/dist/api/open-hands.types.d.ts +1 -1
- package/dist/api/workspace-upload-path.cjs +1 -1
- package/dist/api/workspace-upload-path.js +8 -8
- package/dist/components/conversation-events/chat/event-content-helpers/get-action-event-title.cjs +1 -1
- package/dist/components/conversation-events/chat/event-content-helpers/get-action-event-title.cjs.map +1 -1
- package/dist/components/conversation-events/chat/event-content-helpers/get-action-event-title.js +24 -18
- package/dist/components/conversation-events/chat/event-content-helpers/get-action-event-title.js.map +1 -1
- package/dist/components/conversation-events/chat/event-content-helpers/get-event-content.cjs +1 -1
- package/dist/components/conversation-events/chat/event-content-helpers/get-event-content.cjs.map +1 -1
- package/dist/components/conversation-events/chat/event-content-helpers/get-event-content.js +5 -0
- package/dist/components/conversation-events/chat/event-content-helpers/get-event-content.js.map +1 -1
- package/dist/components/conversation-events/chat/event-content-helpers/should-render-event.cjs +2 -2
- package/dist/components/conversation-events/chat/event-content-helpers/should-render-event.cjs.map +1 -1
- package/dist/components/conversation-events/chat/event-content-helpers/should-render-event.js +11 -7
- package/dist/components/conversation-events/chat/event-content-helpers/should-render-event.js.map +1 -1
- package/dist/components/features/chat/components/chat-input-llm-profile-picker.cjs +1 -1
- package/dist/components/features/chat/components/chat-input-llm-profile-picker.cjs.map +1 -1
- package/dist/components/features/chat/components/chat-input-llm-profile-picker.js +61 -60
- package/dist/components/features/chat/components/chat-input-llm-profile-picker.js.map +1 -1
- package/dist/components/features/chat/model-messages.cjs +1 -1
- package/dist/components/features/chat/model-messages.cjs.map +1 -1
- package/dist/components/features/chat/model-messages.js +26 -25
- package/dist/components/features/chat/model-messages.js.map +1 -1
- package/dist/components/features/conversation-panel/conversation-card/conversation-card-footer.cjs +1 -2
- package/dist/components/features/conversation-panel/conversation-card/conversation-card-footer.cjs.map +1 -1
- package/dist/components/features/conversation-panel/conversation-card/conversation-card-footer.d.ts +2 -8
- package/dist/components/features/conversation-panel/conversation-card/conversation-card-footer.js +50 -53
- package/dist/components/features/conversation-panel/conversation-card/conversation-card-footer.js.map +1 -1
- package/dist/components/features/conversation-panel/conversation-card/conversation-card-meta-chip.cjs +2 -0
- package/dist/components/features/conversation-panel/conversation-card/conversation-card-meta-chip.cjs.map +1 -0
- package/dist/components/features/conversation-panel/conversation-card/conversation-card-meta-chip.d.ts +12 -0
- package/dist/components/features/conversation-panel/conversation-card/conversation-card-meta-chip.js +6 -0
- package/dist/components/features/conversation-panel/conversation-card/conversation-card-meta-chip.js.map +1 -0
- package/dist/components/features/conversation-panel/conversation-card/conversation-card-preview.cjs +1 -1
- package/dist/components/features/conversation-panel/conversation-card/conversation-card-preview.cjs.map +1 -1
- package/dist/components/features/conversation-panel/conversation-card/conversation-card-preview.d.ts +16 -1
- package/dist/components/features/conversation-panel/conversation-card/conversation-card-preview.js +95 -78
- package/dist/components/features/conversation-panel/conversation-card/conversation-card-preview.js.map +1 -1
- package/dist/components/features/conversation-panel/conversation-card/conversation-repo-link.cjs +1 -1
- package/dist/components/features/conversation-panel/conversation-card/conversation-repo-link.cjs.map +1 -1
- package/dist/components/features/conversation-panel/conversation-card/conversation-repo-link.js +40 -38
- package/dist/components/features/conversation-panel/conversation-card/conversation-repo-link.js.map +1 -1
- package/dist/components/features/conversation-panel/conversation-card/conversation-tag-chips.cjs +2 -0
- package/dist/components/features/conversation-panel/conversation-card/conversation-tag-chips.cjs.map +1 -0
- package/dist/components/features/conversation-panel/conversation-card/conversation-tag-chips.d.ts +14 -0
- package/dist/components/features/conversation-panel/conversation-card/conversation-tag-chips.js +167 -0
- package/dist/components/features/conversation-panel/conversation-card/conversation-tag-chips.js.map +1 -0
- package/dist/components/features/conversation-panel/conversation-card/conversation-tag-display.cjs +2 -0
- package/dist/components/features/conversation-panel/conversation-card/conversation-tag-display.cjs.map +1 -0
- package/dist/components/features/conversation-panel/conversation-card/conversation-tag-display.d.ts +63 -0
- package/dist/components/features/conversation-panel/conversation-card/conversation-tag-display.js +66 -0
- package/dist/components/features/conversation-panel/conversation-card/conversation-tag-display.js.map +1 -0
- package/dist/components/features/conversation-panel/conversation-card/conversation-tag-icons.cjs +2 -0
- package/dist/components/features/conversation-panel/conversation-card/conversation-tag-icons.cjs.map +1 -0
- package/dist/components/features/conversation-panel/conversation-card/conversation-tag-icons.d.ts +15 -0
- package/dist/components/features/conversation-panel/conversation-card/conversation-tag-icons.js +117 -0
- package/dist/components/features/conversation-panel/conversation-card/conversation-tag-icons.js.map +1 -0
- package/dist/components/features/conversation-panel/conversation-card/no-repository.cjs +1 -1
- package/dist/components/features/conversation-panel/conversation-card/no-repository.cjs.map +1 -1
- package/dist/components/features/conversation-panel/conversation-card/no-repository.js +30 -19
- package/dist/components/features/conversation-panel/conversation-card/no-repository.js.map +1 -1
- package/dist/components/features/conversation-panel/conversation-panel-filter-menu.cjs +1 -1
- package/dist/components/features/conversation-panel/conversation-panel-filter-menu.cjs.map +1 -1
- package/dist/components/features/conversation-panel/conversation-panel-filter-menu.d.ts +7 -2
- package/dist/components/features/conversation-panel/conversation-panel-filter-menu.js +146 -104
- package/dist/components/features/conversation-panel/conversation-panel-filter-menu.js.map +1 -1
- package/dist/components/features/conversation-panel/conversation-panel-list-helpers.cjs +1 -1
- package/dist/components/features/conversation-panel/conversation-panel-list-helpers.cjs.map +1 -1
- package/dist/components/features/conversation-panel/conversation-panel-list-helpers.d.ts +20 -0
- package/dist/components/features/conversation-panel/conversation-panel-list-helpers.js +53 -27
- package/dist/components/features/conversation-panel/conversation-panel-list-helpers.js.map +1 -1
- package/dist/components/features/conversation-panel/conversation-panel.cjs +1 -1
- package/dist/components/features/conversation-panel/conversation-panel.cjs.map +1 -1
- package/dist/components/features/conversation-panel/conversation-panel.js +226 -213
- package/dist/components/features/conversation-panel/conversation-panel.js.map +1 -1
- package/dist/components/features/settings/agent-canvas-update-card.cjs +1 -1
- package/dist/components/features/settings/agent-canvas-update-card.cjs.map +1 -1
- package/dist/components/features/settings/agent-canvas-update-card.d.ts +10 -5
- package/dist/components/features/settings/agent-canvas-update-card.js +223 -149
- package/dist/components/features/settings/agent-canvas-update-card.js.map +1 -1
- package/dist/components/features/settings/agent-canvas-version-modal.cjs +2 -0
- package/dist/components/features/settings/agent-canvas-version-modal.cjs.map +1 -0
- package/dist/components/features/settings/agent-canvas-version-modal.d.ts +11 -0
- package/dist/components/features/settings/agent-canvas-version-modal.js +166 -0
- package/dist/components/features/settings/agent-canvas-version-modal.js.map +1 -0
- package/dist/components/features/settings/agent-canvas-version-tile.cjs +2 -0
- package/dist/components/features/settings/agent-canvas-version-tile.cjs.map +1 -0
- package/dist/components/features/settings/agent-canvas-version-tile.d.ts +13 -0
- package/dist/components/features/settings/agent-canvas-version-tile.js +60 -0
- package/dist/components/features/settings/agent-canvas-version-tile.js.map +1 -0
- package/dist/components/features/settings/llm-profiles/profile-row.cjs +1 -1
- package/dist/components/features/settings/llm-profiles/profile-row.js +1 -0
- package/dist/components/features/settings/settings-desktop-sidebar.cjs +1 -1
- package/dist/components/features/settings/settings-desktop-sidebar.cjs.map +1 -1
- package/dist/components/features/settings/settings-desktop-sidebar.js +1 -1
- package/dist/components/features/settings/settings-desktop-sidebar.js.map +1 -1
- package/dist/components/features/settings/settings-mobile-drawer.cjs +1 -1
- package/dist/components/features/settings/settings-mobile-drawer.cjs.map +1 -1
- package/dist/components/features/settings/settings-mobile-drawer.js +1 -1
- package/dist/components/features/settings/settings-mobile-drawer.js.map +1 -1
- package/dist/components/features/sidebar/sidebar-rail-body.cjs +1 -1
- package/dist/components/features/sidebar/sidebar-rail-body.cjs.map +1 -1
- package/dist/components/features/sidebar/sidebar-rail-body.js +109 -108
- package/dist/components/features/sidebar/sidebar-rail-body.js.map +1 -1
- package/dist/components/features/skills/extensions-navigation.cjs +1 -1
- package/dist/components/features/skills/extensions-navigation.cjs.map +1 -1
- package/dist/components/features/skills/extensions-navigation.js +9 -9
- package/dist/components/features/skills/extensions-navigation.js.map +1 -1
- package/dist/components/shared/modals/settings/model-selector.cjs +1 -1
- package/dist/components/shared/modals/settings/model-selector.cjs.map +1 -1
- package/dist/components/shared/modals/settings/model-selector.js +154 -98
- package/dist/components/shared/modals/settings/model-selector.js.map +1 -1
- package/dist/config/defaults.cjs +1 -1
- package/dist/config/defaults.cjs.map +1 -1
- package/dist/config/defaults.js +1 -1
- package/dist/config/defaults.js.map +1 -1
- package/dist/constants/child-conversation.cjs +2 -0
- package/dist/constants/child-conversation.cjs.map +1 -0
- package/dist/constants/child-conversation.d.ts +29 -0
- package/dist/constants/child-conversation.js +6 -0
- package/dist/constants/child-conversation.js.map +1 -0
- package/dist/contexts/conversation-websocket-context.cjs +3 -3
- package/dist/contexts/conversation-websocket-context.cjs.map +1 -1
- package/dist/contexts/conversation-websocket-context.js +189 -188
- package/dist/contexts/conversation-websocket-context.js.map +1 -1
- package/dist/hooks/chat/use-model-interceptor.cjs +1 -1
- package/dist/hooks/chat/use-model-interceptor.js +6 -6
- package/dist/hooks/chat/use-slash-command.cjs +1 -1
- package/dist/hooks/chat/use-slash-command.cjs.map +1 -1
- package/dist/hooks/chat/use-slash-command.js +61 -60
- package/dist/hooks/chat/use-slash-command.js.map +1 -1
- package/dist/hooks/mutation/use-create-conversation.cjs +1 -1
- package/dist/hooks/mutation/use-create-conversation.js +6 -6
- package/dist/i18n/declaration.cjs +1 -1
- package/dist/i18n/declaration.cjs.map +1 -1
- package/dist/i18n/declaration.d.ts +38 -1
- package/dist/i18n/declaration.js +1 -1
- package/dist/i18n/declaration.js.map +1 -1
- package/dist/i18n/translation.cjs +2 -2
- package/dist/i18n/translation.cjs.map +1 -1
- package/dist/i18n/translation.js +644 -15
- package/dist/i18n/translation.js.map +1 -1
- package/dist/icons/docker.cjs +2 -0
- package/dist/icons/docker.cjs.map +1 -0
- package/dist/icons/docker.js +19 -0
- package/dist/icons/docker.js.map +1 -0
- package/dist/icons/npm.cjs +2 -0
- package/dist/icons/npm.cjs.map +1 -0
- package/dist/icons/npm.js +19 -0
- package/dist/icons/npm.js.map +1 -0
- package/dist/locales/ar/openhands.json +39 -2
- package/dist/locales/ca/openhands.json +39 -2
- package/dist/locales/de/openhands.json +39 -2
- package/dist/locales/en/openhands.json +39 -2
- package/dist/locales/es/openhands.json +39 -2
- package/dist/locales/fr/openhands.json +39 -2
- package/dist/locales/it/openhands.json +39 -2
- package/dist/locales/ja/openhands.json +39 -2
- package/dist/locales/ko-KR/openhands.json +39 -2
- package/dist/locales/no/openhands.json +39 -2
- package/dist/locales/pt/openhands.json +39 -2
- package/dist/locales/tr/openhands.json +39 -2
- package/dist/locales/uk/openhands.json +39 -2
- package/dist/locales/zh-CN/openhands.json +39 -2
- package/dist/locales/zh-TW/openhands.json +39 -2
- package/dist/node_modules/@openhands/typescript-client/dist/index.cjs +1 -1
- package/dist/node_modules/@openhands/typescript-client/dist/index.js +1 -1
- package/dist/node_modules/lucide-react/dist/esm/icons/briefcase.cjs +2 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/briefcase.cjs.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/briefcase.js +16 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/briefcase.js.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/building-2.cjs +2 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/building-2.cjs.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/building-2.js +27 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/building-2.js.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/circle-arrow-up.cjs +2 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/circle-arrow-up.cjs.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/circle-arrow-up.js +21 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/circle-arrow-up.js.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/circle-user-round.cjs +2 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/circle-user-round.cjs.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/circle-user-round.js +23 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/circle-user-round.js.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/cloud-cog.cjs +2 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/cloud-cog.cjs.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/cloud-cog.js +43 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/cloud-cog.js.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/earth.cjs +2 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/earth.cjs.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/earth.js +25 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/earth.js.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/flag.cjs +2 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/flag.cjs.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/flag.js +9 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/flag.js.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/folder-git-2.cjs +2 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/folder-git-2.cjs.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/folder-git-2.js +27 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/folder-git-2.js.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/folder-key.cjs +2 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/folder-key.cjs.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/folder-key.js +25 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/folder-key.js.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/git-pull-request.cjs +2 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/git-pull-request.cjs.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/git-pull-request.js +30 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/git-pull-request.js.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/hash.cjs +2 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/hash.cjs.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/hash.js +35 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/hash.js.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/house.cjs +2 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/house.cjs.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/house.js +12 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/house.js.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/id-card.cjs +2 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/id-card.cjs.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/id-card.js +33 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/id-card.js.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/key-round.cjs +2 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/key-round.cjs.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/key-round.js +15 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/key-round.js.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/layers.cjs +2 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/layers.cjs.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/layers.js +19 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/layers.js.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/link-2.cjs +2 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/link-2.cjs.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/link-2.js +22 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/link-2.js.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/mails.cjs +2 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/mails.cjs.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/mails.js +23 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/mails.js.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/messages-square.cjs +2 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/messages-square.cjs.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/messages-square.js +12 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/messages-square.js.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/plug.cjs +2 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/plug.cjs.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/plug.js +23 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/plug.js.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/square-kanban.cjs +2 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/square-kanban.cjs.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/square-kanban.js +27 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/square-kanban.js.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/ticket.cjs +2 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/ticket.cjs.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/ticket.js +23 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/ticket.js.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/users-round.cjs +2 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/users-round.cjs.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/users-round.js +21 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/users-round.js.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/waypoints.cjs +2 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/waypoints.cjs.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/waypoints.js +43 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/waypoints.js.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/webhook.cjs +2 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/webhook.cjs.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/webhook.js +19 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/webhook.js.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/workflow.cjs +2 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/workflow.cjs.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/workflow.js +27 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/workflow.js.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/wrench.cjs +2 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/wrench.cjs.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/wrench.js +9 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/wrench.js.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/zap.cjs +2 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/zap.cjs.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/zap.js +9 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/zap.js.map +1 -0
- package/dist/package.cjs +1 -1
- package/dist/package.cjs.map +1 -1
- package/dist/package.js +6 -3
- package/dist/package.js.map +1 -1
- package/dist/routes/app-settings.cjs +1 -1
- package/dist/routes/app-settings.cjs.map +1 -1
- package/dist/routes/app-settings.js +94 -93
- package/dist/routes/app-settings.js.map +1 -1
- package/dist/routes/llm-settings.cjs +1 -1
- package/dist/routes/llm-settings.cjs.map +1 -1
- package/dist/routes/llm-settings.js +131 -119
- package/dist/routes/llm-settings.js.map +1 -1
- package/dist/services/child-conversation-launch.cjs +2 -0
- package/dist/services/child-conversation-launch.cjs.map +1 -0
- package/dist/services/child-conversation-launch.d.ts +41 -0
- package/dist/services/child-conversation-launch.js +182 -0
- package/dist/services/child-conversation-launch.js.map +1 -0
- package/dist/stores/conversation-panel-preferences-store.cjs +1 -1
- package/dist/stores/conversation-panel-preferences-store.cjs.map +1 -1
- package/dist/stores/conversation-panel-preferences-store.d.ts +5 -1
- package/dist/stores/conversation-panel-preferences-store.js +8 -2
- package/dist/stores/conversation-panel-preferences-store.js.map +1 -1
- package/dist/types/agent-server/core/base/action.d.ts +17 -1
- package/dist/types/agent-server/core/base/base.d.ts +2 -1
- package/dist/types/agent-server/type-guards.cjs +1 -1
- package/dist/types/agent-server/type-guards.cjs.map +1 -1
- package/dist/types/agent-server/type-guards.d.ts +8 -1
- package/dist/types/agent-server/type-guards.js +4 -3
- package/dist/types/agent-server/type-guards.js.map +1 -1
- package/dist/types/automation.d.ts +14 -0
- package/dist/utils/custom-toast-handlers.cjs +1 -1
- package/dist/utils/custom-toast-handlers.cjs.map +1 -1
- package/dist/utils/custom-toast-handlers.js +18 -1
- package/dist/utils/custom-toast-handlers.js.map +1 -1
- package/dist/utils/format-model-name.cjs +1 -1
- package/dist/utils/format-model-name.cjs.map +1 -1
- package/dist/utils/format-model-name.d.ts +11 -0
- package/dist/utils/format-model-name.js +11 -3
- package/dist/utils/format-model-name.js.map +1 -1
- package/package.json +6 -3
- package/scripts/brand-dev-electron.mjs +244 -0
- package/scripts/check-translation-completeness.cjs +4 -0
- package/scripts/generate-icons.mjs +109 -0
- package/build/assets/agent-server-conversation-service.api-km_8sOVf.js +0 -41
- package/build/assets/agent-settings-nmO-KSXr.js +0 -1
- package/build/assets/app-settings-9ok42bV6.js +0 -1
- package/build/assets/command-menu-CA3IjGNc.js +0 -1
- package/build/assets/conversation-B6t6EiVp.js +0 -1
- package/build/assets/conversation-panel-B6t6EiVp.js +0 -1
- package/build/assets/copy-DaHuDtG4.js +0 -1
- package/build/assets/git-repo-dropdown-TzOuG5Ef.js +0 -1
- package/build/assets/home-OEx0XtfP.js +0 -1
- package/build/assets/index-home-OEx0XtfP.js +0 -1
- package/build/assets/install-server-modal-DnjZrJUy.js +0 -1
- package/build/assets/list-filter-C86CBRDE.js +0 -1
- package/build/assets/llm-not-configured-banner-DrBrhfN0.js +0 -27
- package/build/assets/llm-settings-CRsLqxgu.js +0 -1
- package/build/assets/llm-settings-RBm_3kNm.js +0 -1
- package/build/assets/manifest-c352d453.js +0 -1
- package/build/assets/map-provider-CXU6issd.js +0 -1
- package/build/assets/markdown-renderer-lsvsp7jn.js +0 -8
- package/build/assets/mcp-DD4hPTRE.js +0 -9
- package/build/assets/messages-Dt77e0qi.js +0 -40
- package/build/assets/recommended-automations-launcher-6xmbf7lh.js +0 -1
- package/build/assets/root-BeuHFY2s.css +0 -1
- package/build/assets/root-layout-Dl_2CDoq.js +0 -3
- package/build/assets/settings-Co-M5bK9.js +0 -1
- package/build/assets/sidebar-store-B04sX8aP.js +0 -1
- package/build/assets/skills-settings-DnbIu2eO.js +0 -2
- package/build/assets/use-settings-nav-items-y-Oyc93G.js +0 -16
- package/build/assets/utils-BX9XJ0IE.js +0 -1
- package/dist/node_modules/lucide-react/dist/esm/icons/chevron-up.cjs +0 -2
- package/dist/node_modules/lucide-react/dist/esm/icons/chevron-up.cjs.map +0 -1
- package/dist/node_modules/lucide-react/dist/esm/icons/chevron-up.js +0 -9
- package/dist/node_modules/lucide-react/dist/esm/icons/chevron-up.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"conversation-panel.js","names":[],"sources":["../../../../src/components/features/conversation-panel/conversation-panel.tsx"],"sourcesContent":["import React from \"react\";\nimport { Tooltip } from \"@heroui/react\";\nimport { useTranslation } from \"react-i18next\";\nimport { I18nKey } from \"#/i18n/declaration\";\nimport { useNavigation } from \"#/context/navigation-context\";\nimport { useActiveBackend } from \"#/contexts/active-backend-context\";\nimport { useBackendScopedPath } from \"#/hooks/use-backend-scoped-path\";\nimport { usePaginatedConversations } from \"#/hooks/query/use-paginated-conversations\";\nimport { useStartTasks } from \"#/hooks/query/use-start-tasks\";\nimport { useDeleteConversation } from \"#/hooks/mutation/use-delete-conversation\";\nimport { useUnifiedPauseConversation } from \"#/hooks/mutation/use-unified-stop-conversation\";\nimport { ConfirmDeleteModal } from \"./confirm-delete-modal\";\nimport { ConfirmStopModal } from \"./confirm-stop-modal\";\nimport { NavigationLink } from \"#/components/shared/navigation-link\";\nimport { ExitConversationModal } from \"./exit-conversation-modal\";\nimport { useClickOutsideElement } from \"#/hooks/use-click-outside-element\";\nimport { Provider } from \"#/types/settings\";\nimport { useUpdateConversation } from \"#/hooks/mutation/use-update-conversation\";\nimport {\n displayErrorToast,\n displaySuccessToast,\n} from \"#/utils/custom-toast-handlers\";\nimport { isExecutionActive } from \"#/utils/status\";\nimport { useCreateConversation } from \"#/hooks/mutation/use-create-conversation\";\nimport { useIsCreatingConversation } from \"#/hooks/use-is-creating-conversation\";\nimport { ConversationCard } from \"./conversation-card/conversation-card\";\nimport { ConversationCardPreview } from \"./conversation-card/conversation-card-preview\";\nimport { StartTaskCard } from \"./start-task-card/start-task-card\";\nimport { ConversationCardSkeleton } from \"./conversation-card/conversation-card-skeleton\";\nimport { CompactConversationRow } from \"./compact-conversation-row\";\nimport { useConversationPanelPreferencesStore } from \"#/stores/conversation-panel-preferences-store\";\nimport { cn } from \"#/utils/utils\";\nimport { ConversationPanelFilterMenu } from \"./conversation-panel-filter-menu\";\nimport { ConversationPanelNewThreadPicker } from \"./conversation-panel-new-thread-picker\";\nimport { ConversationGroupFolderList } from \"./conversation-group-folder-list\";\nimport { ConversationPanelPinnedSection } from \"./conversation-panel-pinned-section\";\nimport {\n applyGroupFolderOrder,\n filterOutPinnedConversations,\n groupConversations,\n resolvePinnedConversations,\n sortConversationsByField,\n type ConversationGroupLaunch,\n} from \"./conversation-panel-list-helpers\";\nimport { usePinnedConversationsStore } from \"#/stores/pinned-conversations-store\";\n\ninterface ConversationPanelProps {\n onClose?: () => void;\n /**\n * Render a minimal icon-only variant of each conversation row (used by the\n * collapsed sidebar). Each row is a single status dot with a hover preview\n * containing the full card content.\n */\n compact?: boolean;\n}\n\nconst noop = () => {};\n\nconst EMPTY_PINNED_CONVERSATION_IDS: readonly string[] = [];\n\nconst ONE_HOUR_MS = 60 * 60 * 1000;\n\nconst partitionByCutoff = <T extends { updated_at: string }>(\n items: readonly T[],\n): { recent: T[]; older: T[] } => {\n // The cutoff is intentionally relative to \"now\" each time the list is\n // recomputed, so conversations naturally age into the older bucket as the\n // conversations query refreshes.\n const cutoff = Date.now() - ONE_HOUR_MS;\n const recent: T[] = [];\n const older: T[] = [];\n for (const item of items) {\n const updatedAt = item.updated_at ? Date.parse(item.updated_at) : NaN;\n // Missing or unparseable timestamps stay in the \"recent\" bucket so we\n // do not accidentally hide them behind the older-conversations toggle.\n if (Number.isFinite(updatedAt) && updatedAt < cutoff) {\n older.push(item);\n } else {\n recent.push(item);\n }\n }\n return { recent, older };\n};\n\nexport function ConversationPanel({\n onClose,\n compact = false,\n}: ConversationPanelProps) {\n const { t } = useTranslation(\"openhands\");\n const { conversationId: currentConversationId, navigate } = useNavigation();\n const { backend: activeBackend } = useActiveBackend();\n const backendScopedPath = useBackendScopedPath();\n // Click-outside is only relevant in the legacy drawer mode where an\n // onClose handler is provided. When the panel is rendered inline (e.g.\n // as the always-visible conversation list pane), clicking outside should\n // not dismiss the list, so we pass a no-op callback in that case.\n const ref = useClickOutsideElement<HTMLDivElement>(onClose ?? noop);\n\n const [confirmDeleteModalVisible, setConfirmDeleteModalVisible] =\n React.useState(false);\n const [confirmStopModalVisible, setConfirmStopModalVisible] =\n React.useState(false);\n const [\n confirmExitConversationModalVisible,\n setConfirmExitConversationModalVisible,\n ] = React.useState(false);\n const [confirmDeleteAllVisible, setConfirmDeleteAllVisible] =\n React.useState(false);\n const showOlderConversations = useConversationPanelPreferencesStore(\n (state) => state.showOlderConversations,\n );\n const toggleShowOlderConversations = useConversationPanelPreferencesStore(\n (state) => state.toggleShowOlderConversations,\n );\n const showRepoBranchMetadata = useConversationPanelPreferencesStore(\n (state) => state.showRepoBranchMetadata,\n );\n const toggleShowRepoBranchMetadata = useConversationPanelPreferencesStore(\n (state) => state.toggleShowRepoBranchMetadata,\n );\n const showLlmProfiles = useConversationPanelPreferencesStore(\n (state) => state.showLlmProfiles,\n );\n const toggleShowLlmProfiles = useConversationPanelPreferencesStore(\n (state) => state.toggleShowLlmProfiles,\n );\n const showTagsMetadata = useConversationPanelPreferencesStore(\n (state) => state.showTagsMetadata,\n );\n const toggleShowTagsMetadata = useConversationPanelPreferencesStore(\n (state) => state.toggleShowTagsMetadata,\n );\n const showHoverMetadata = useConversationPanelPreferencesStore(\n (state) => state.showHoverMetadata,\n );\n const toggleShowHoverMetadata = useConversationPanelPreferencesStore(\n (state) => state.toggleShowHoverMetadata,\n );\n const organizeMode = useConversationPanelPreferencesStore(\n (state) => state.organizeMode,\n );\n const setOrganizeMode = useConversationPanelPreferencesStore(\n (state) => state.setOrganizeMode,\n );\n const conversationSort = useConversationPanelPreferencesStore(\n (state) => state.conversationSort,\n );\n const setConversationSort = useConversationPanelPreferencesStore(\n (state) => state.setConversationSort,\n );\n const threadScope = useConversationPanelPreferencesStore(\n (state) => state.threadScope,\n );\n const setThreadScope = useConversationPanelPreferencesStore(\n (state) => state.setThreadScope,\n );\n const groupFolderOrder = useConversationPanelPreferencesStore(\n (state) => state.groupFolderOrder,\n );\n const setGroupFolderOrder = useConversationPanelPreferencesStore(\n (state) => state.setGroupFolderOrder,\n );\n const [filterMenuOpen, setFilterMenuOpen] = React.useState(false);\n const [isListScrolled, setIsListScrolled] = React.useState(false);\n const filterMenuRef = useClickOutsideElement<HTMLDivElement>(() => {\n setFilterMenuOpen(false);\n });\n const [collapsedGroupIds, setCollapsedGroupIds] = React.useState<\n ReadonlySet<string>\n >(() => new Set());\n const [expandedGroupPreviewIds, setExpandedGroupPreviewIds] = React.useState<\n ReadonlySet<string>\n >(() => new Set());\n const [expandedPinnedPreview, setExpandedPinnedPreview] =\n React.useState(false);\n\n const pinnedIds = usePinnedConversationsStore(\n (state) =>\n state.pinsByBackendId[activeBackend.id] ?? EMPTY_PINNED_CONVERSATION_IDS,\n );\n const togglePin = usePinnedConversationsStore((state) => state.togglePin);\n const pruneMissingPinnedConversations = usePinnedConversationsStore(\n (state) => state.pruneMissingConversations,\n );\n\n const toggleGroupCollapsed = React.useCallback((groupId: string) => {\n setCollapsedGroupIds((prev) => {\n const next = new Set(prev);\n if (next.has(groupId)) {\n next.delete(groupId);\n } else {\n next.add(groupId);\n }\n return next;\n });\n }, []);\n\n const toggleGroupPreviewExpanded = React.useCallback((groupId: string) => {\n setExpandedGroupPreviewIds((prev) => {\n const next = new Set(prev);\n if (next.has(groupId)) {\n next.delete(groupId);\n } else {\n next.add(groupId);\n }\n return next;\n });\n }, []);\n\n React.useEffect(() => {\n if (organizeMode !== \"grouped\") {\n setCollapsedGroupIds(new Set());\n setExpandedGroupPreviewIds(new Set());\n }\n }, [organizeMode]);\n\n const scrollContainerRef = React.useRef<HTMLDivElement>(null);\n\n const [selectedConversationId, setSelectedConversationId] = React.useState<\n string | null\n >(null);\n const [selectedConversationTitle, setSelectedConversationTitle] =\n React.useState<string | null>(null);\n const [openContextMenuId, setOpenContextMenuId] = React.useState<\n string | null\n >(null);\n\n const {\n data,\n isLoading,\n isFetched,\n hasNextPage,\n isFetching,\n isFetchingNextPage,\n fetchNextPage,\n } = usePaginatedConversations();\n\n // Fetch in-progress start tasks\n const { data: startTasks } = useStartTasks();\n\n const conversations = React.useMemo(() => {\n const all = data?.pages.flatMap((page) => page.items) ?? [];\n // The 10s background refetch re-fetches every loaded page with the\n // `UPDATED_AT_DESC` cursor. If a conversation's `updated_at` shifts between\n // page fetches, a later page can overlap an earlier one and surface the\n // same conversation twice. Dedupe by id (keeping the first/freshest copy)\n // so the rendered count reflects real growth and React keys stay unique.\n const seen = new Set<string>();\n return all.filter((conversation) => {\n if (seen.has(conversation.id)) {\n return false;\n }\n seen.add(conversation.id);\n return true;\n });\n }, [data]);\n\n const pinnedConversations = React.useMemo(\n () => resolvePinnedConversations(pinnedIds, conversations),\n [conversations, pinnedIds],\n );\n\n React.useEffect(() => {\n if (!isFetched) {\n return;\n }\n pruneMissingPinnedConversations(\n activeBackend.id,\n conversations.map((conversation) => conversation.id),\n );\n }, [\n activeBackend.id,\n conversations,\n isFetched,\n pruneMissingPinnedConversations,\n ]);\n\n React.useEffect(() => {\n if (pinnedIds.length === 0) {\n setExpandedPinnedPreview(false);\n }\n }, [pinnedIds.length]);\n\n const scopedConversations = React.useMemo(() => {\n const scopeFiltered =\n threadScope === \"relevant\"\n ? conversations.filter((c) => isExecutionActive(c.execution_status))\n : conversations;\n\n // In the expanded panel, pinned conversations should only appear inside\n // the dedicated pinned section (not duplicated in grouped/flat lists).\n if (compact) {\n return scopeFiltered;\n }\n\n return filterOutPinnedConversations(scopeFiltered, pinnedIds);\n }, [compact, conversations, pinnedIds, threadScope]);\n\n const { recent: recentScoped, older: olderScoped } = React.useMemo(\n () => partitionByCutoff(scopedConversations),\n [scopedConversations],\n );\n\n // Sort the full visible set as one list. The recent/older partition is\n // still computed (it gates the \"Show older\" toggle and \"Load more\"\n // visibility), but the rendering must not use it as a visual boundary —\n // when sorting by `created`, a stale-but-recently-touched conversation\n // would otherwise land in `recent` and render above an actually-newer-\n // by-`created_at` conversation sitting in `older`.\n const sortedVisibleConversations = React.useMemo(() => {\n const visible = showOlderConversations\n ? [...recentScoped, ...olderScoped]\n : recentScoped;\n return sortConversationsByField(visible, conversationSort);\n }, [recentScoped, olderScoped, showOlderConversations, conversationSort]);\n\n const groupLabels = React.useMemo(\n () => ({\n emptyWorkspace: t(I18nKey.CONVERSATION_PANEL$NO_WORKSPACE),\n emptyRepository: t(I18nKey.CONVERSATION_PANEL$NO_REPOSITORY),\n }),\n [t],\n );\n\n const conversationGroups = React.useMemo(() => {\n if (compact || organizeMode !== \"grouped\") {\n return null;\n }\n // Use the unsorted partitions: groupConversations sorts each bucket\n // internally by `sortField`, so pre-sorting the merged input is wasted\n // work in grouped mode (the per-group sort overrides any global order).\n const merged = [\n ...recentScoped,\n ...(showOlderConversations ? olderScoped : []),\n ];\n return groupConversations(\n merged,\n activeBackend.kind,\n conversationSort,\n groupLabels,\n );\n }, [\n activeBackend.kind,\n compact,\n conversationSort,\n groupLabels,\n olderScoped,\n organizeMode,\n recentScoped,\n showOlderConversations,\n ]);\n\n const orderedConversationGroups = React.useMemo(() => {\n if (!conversationGroups) {\n return null;\n }\n return applyGroupFolderOrder(conversationGroups, groupFolderOrder);\n }, [conversationGroups, groupFolderOrder]);\n\n const conversationGroupIds = React.useMemo(\n () => conversationGroups?.map((group) => group.id) ?? [],\n [conversationGroups],\n );\n\n const compactVisibleConversations = React.useMemo(\n () =>\n sortConversationsByField(\n recentScoped.filter((conversation) =>\n isExecutionActive(conversation.execution_status),\n ),\n conversationSort,\n ),\n [conversationSort, recentScoped],\n );\n\n const visibleFlatCount = sortedVisibleConversations.length;\n\n const visibleGroupedCount = React.useMemo(() => {\n if (!orderedConversationGroups) {\n return 0;\n }\n return orderedConversationGroups.reduce(\n (n, g) => n + g.conversations.length,\n 0,\n );\n }, [orderedConversationGroups]);\n\n const listIsEffectivelyEmpty =\n organizeMode === \"grouped\" && !compact\n ? visibleGroupedCount === 0\n : visibleFlatCount === 0;\n\n // Number of conversations actually rendered in the list right now, in the\n // current organize mode. \"Load more\" succeeds only when this number grows.\n const visibleCount =\n organizeMode === \"grouped\" && !compact\n ? visibleGroupedCount\n : visibleFlatCount;\n\n // KNOWN ISSUE (unresolved as of 2026-05-29): users still report that the\n // sidebar \"Load more\" sometimes requires two clicks before new conversations\n // appear. The mitigation below (dedupe by id in `conversations`, plus the\n // floor-tracking driver that keeps fetching until the visible count grows)\n // reduced but did NOT fully eliminate the symptom in manual testing. Likely\n // remaining suspects to investigate next: the agent-server cursor pagination\n // returning an overlapping/short page under `UPDATED_AT_DESC` while the 10s\n // `refetchInterval` reorders pages (see `usePaginatedConversations`), or a\n // React Query state lag where `hasNextPage`/`isFetching` settle a render\n // after the click. If you pick this up, reproduce against a backend with\n // >40 conversations and watch the `/api/conversations/search` cursors.\n //\n // Robust \"Load more\" driver. A single click can fail to surface new rows for\n // two reasons: (1) `fetchNextPage()` is silently dropped while the 10s\n // background refetch is in flight, and (2) a fetched page can yield zero\n // *visible* rows (filtered out by the active scope, or deduped as overlap),\n // so the list does not appear to grow. We capture the visible count at click\n // time and keep fetching pages — once the query is idle — until the visible\n // count actually increases or there are no more pages.\n const [loadMoreFloor, setLoadMoreFloor] = React.useState<number | null>(null);\n const visibleCountRef = React.useRef(visibleCount);\n visibleCountRef.current = visibleCount;\n\n const requestLoadMore = React.useCallback(() => {\n if (hasNextPage) {\n setLoadMoreFloor(visibleCountRef.current);\n }\n }, [hasNextPage]);\n\n React.useEffect(() => {\n if (loadMoreFloor === null) {\n return;\n }\n // Goal met: the visible list grew past where it was when the user clicked.\n if (visibleCount > loadMoreFloor) {\n setLoadMoreFloor(null);\n return;\n }\n // Nothing more to fetch — stop waiting even if the list did not grow.\n if (!hasNextPage) {\n setLoadMoreFloor(null);\n return;\n }\n // Wait for any in-flight fetch (including the background refetch) to settle\n // before requesting the next page, otherwise the request is dropped.\n if (isFetching || isFetchingNextPage) {\n return;\n }\n fetchNextPage();\n }, [\n loadMoreFloor,\n visibleCount,\n hasNextPage,\n isFetching,\n isFetchingNextPage,\n fetchNextPage,\n ]);\n\n const isLoadingMore = loadMoreFloor !== null || isFetchingNextPage;\n\n const { mutate: deleteConversation, mutateAsync: deleteConversationAsync } =\n useDeleteConversation();\n const { mutate: pauseConversation } = useUnifiedPauseConversation();\n const { mutate: updateConversation } = useUpdateConversation();\n\n // The next page of conversations is loaded only via the explicit \"Load\n // more\" link rendered at the end of the list — there is no scroll-driven\n // pagination, which previously caused the panel to feel like it had stray\n // scrollable space at the bottom.\n const olderHidden = olderScoped.length > 0 && !showOlderConversations;\n // Compact mode also hides \"Load more\" — paginating into archived\n // conversations contradicts the \"active only\" intent of the icon rail.\n // Do not show when the visible list is empty (e.g. filters hide every\n // loaded conversation) — that state already shows \"No conversations found\".\n const showLoadMore =\n !!hasNextPage && !olderHidden && !compact && !listIsEffectivelyEmpty;\n\n const { mutate: createConversation } = useCreateConversation();\n const isCreatingConversationFlow = useIsCreatingConversation();\n\n const launchFromGroup = React.useCallback(\n (launch: ConversationGroupLaunch) => {\n if (isCreatingConversationFlow) return;\n createConversation(\n {\n workingDir: launch.workingDir,\n repository: launch.repository,\n entryPoint: \"sidebar_relaunch_project\",\n },\n {\n onSuccess: (data) => {\n navigate(`/conversations/${data.conversation_id}`);\n },\n },\n );\n },\n [createConversation, isCreatingConversationFlow, navigate],\n );\n\n const handleDeleteProject = React.useCallback(\n (conversationId: string, title: string) => {\n setConfirmDeleteModalVisible(true);\n setSelectedConversationId(conversationId);\n setSelectedConversationTitle(title);\n },\n [],\n );\n\n const handleStopConversation = React.useCallback((conversationId: string) => {\n setConfirmStopModalVisible(true);\n setSelectedConversationId(conversationId);\n }, []);\n\n const handleConversationTitleChange = React.useCallback(\n (conversationId: string, newTitle: string) => {\n updateConversation(\n { conversationId, newTitle },\n {\n onSuccess: () => {\n displaySuccessToast(t(I18nKey.CONVERSATION$TITLE_UPDATED));\n },\n },\n );\n },\n [t, updateConversation],\n );\n\n const handleConfirmDelete = () => {\n if (selectedConversationId) {\n deleteConversation(\n { conversationId: selectedConversationId },\n {\n onSuccess: () => {\n if (selectedConversationId === currentConversationId) {\n navigate(\"/conversations\");\n }\n },\n },\n );\n }\n };\n\n const handleConfirmStop = () => {\n if (selectedConversationId) {\n pauseConversation({\n conversationId: selectedConversationId,\n });\n }\n };\n\n const handleConfirmDeleteAll = async () => {\n const idsToDelete = conversations.map((c) => c.id);\n const results = await Promise.allSettled(\n idsToDelete.map((conversationId) =>\n deleteConversationAsync({ conversationId }),\n ),\n );\n\n const deletedIds = results.flatMap((result, index) =>\n result.status === \"fulfilled\" ? [idsToDelete[index]] : [],\n );\n const failedCount = results.length - deletedIds.length;\n\n if (\n currentConversationId !== null &&\n deletedIds.includes(currentConversationId)\n ) {\n navigate(\"/conversations\");\n }\n\n if (failedCount > 0) {\n displayErrorToast(\n `${failedCount} conversation${failedCount === 1 ? \"\" : \"s\"} could not be deleted.`,\n );\n }\n };\n\n const renderConversationCard = React.useCallback(\n (\n conversation: (typeof conversations)[number],\n options?: { inPinnedSection?: boolean },\n ) => {\n const isPinned = pinnedIds.includes(conversation.id);\n if (compact) {\n return (\n <CompactConversationRow\n key={conversation.id}\n conversationId={conversation.id}\n title={conversation.title ?? \"\"}\n selectedRepository={{\n selected_repository: conversation.selected_repository,\n selected_branch: conversation.selected_branch,\n git_provider: conversation.git_provider as Provider,\n }}\n executionStatus={conversation.execution_status}\n sandboxStatus={conversation.sandbox_status}\n lastUpdatedAt={conversation.updated_at}\n createdAt={conversation.created_at}\n workspaceWorkingDir={\n conversation.selected_workspace ??\n conversation.workspace?.working_dir\n }\n isActive={conversation.id === currentConversationId}\n onClose={onClose}\n showRepositoryMetadata={showRepoBranchMetadata}\n llmModel={conversation.llm_model}\n showLlmProfiles={showLlmProfiles}\n agentKind={conversation.agent_kind}\n acpServer={conversation.acp_server}\n tags={conversation.tags}\n showTags={showTagsMetadata}\n />\n );\n }\n return (\n <Tooltip\n key={conversation.id}\n placement=\"right-start\"\n delay={1000}\n closeDelay={100}\n isDisabled={\n !showHoverMetadata || openContextMenuId === conversation.id\n }\n disableAnimation={import.meta.env.MODE === \"test\"}\n className=\"rounded-xl border border-[var(--oh-border)] bg-base-secondary p-0 text-white shadow-xl\"\n content={\n <ConversationCardPreview\n title={conversation.title ?? \"\"}\n executionStatus={conversation.execution_status}\n sandboxStatus={conversation.sandbox_status}\n selectedRepository={{\n selected_repository: conversation.selected_repository,\n selected_branch: conversation.selected_branch,\n git_provider: conversation.git_provider as Provider,\n }}\n workspaceWorkingDir={\n conversation.selected_workspace ??\n conversation.workspace?.working_dir\n }\n llmModel={conversation.llm_model}\n createdAt={conversation.created_at}\n />\n }\n >\n <NavigationLink\n to={backendScopedPath(`/conversations/${conversation.id}`)}\n onClick={onClose}\n className={cn(\n \"block rounded-md transition-colors\",\n openContextMenuId !== conversation.id &&\n \"hover:bg-[var(--oh-surface)]\",\n (conversation.id === currentConversationId ||\n openContextMenuId === conversation.id) &&\n \"bg-[var(--oh-surface)]\",\n )}\n >\n <ConversationCard\n onDelete={() =>\n handleDeleteProject(conversation.id, conversation.title ?? \"\")\n }\n onStop={() => handleStopConversation(conversation.id)}\n onChangeTitle={(title) =>\n handleConversationTitleChange(conversation.id, title)\n }\n title={conversation.title ?? \"\"}\n selectedRepository={{\n selected_repository: conversation.selected_repository,\n selected_branch: conversation.selected_branch,\n git_provider: conversation.git_provider as Provider,\n }}\n lastUpdatedAt={conversation.updated_at}\n createdAt={conversation.created_at}\n executionStatus={conversation.execution_status}\n sandboxStatus={conversation.sandbox_status}\n conversationId={conversation.id}\n contextMenuOpen={openContextMenuId === conversation.id}\n onContextMenuToggle={(isOpen) =>\n setOpenContextMenuId(isOpen ? conversation.id : null)\n }\n isActive={conversation.id === currentConversationId}\n workspaceWorkingDir={\n conversation.selected_workspace ??\n conversation.workspace?.working_dir\n }\n showRepositoryMetadata={showRepoBranchMetadata}\n llmModel={conversation.llm_model}\n showLlmProfiles={showLlmProfiles}\n agentKind={conversation.agent_kind}\n acpServer={conversation.acp_server}\n tags={conversation.tags}\n showTags={showTagsMetadata}\n isPinned={isPinned}\n onTogglePin={() => togglePin(activeBackend.id, conversation.id)}\n alwaysShowPinIcon={isPinned && !options?.inPinnedSection}\n />\n </NavigationLink>\n </Tooltip>\n );\n },\n [\n activeBackend.id,\n compact,\n currentConversationId,\n handleConversationTitleChange,\n handleDeleteProject,\n handleStopConversation,\n onClose,\n openContextMenuId,\n pinnedIds,\n showRepoBranchMetadata,\n showLlmProfiles,\n showTagsMetadata,\n showHoverMetadata,\n togglePin,\n ],\n );\n\n // Standard layout: panel fills its slot in the sidebar; the inner scroll\n // child fills the panel and scrolls when its content overflows. Modals are\n // siblings of the scroll element and are `position: fixed`, so they don't\n // participate in the panel's scroll geometry.\n // Gate on `isLoading` / `!isFetched` (true only until the first fetch settles),\n // not `isFetching` — the latter flips back to true on every 10s background\n // refetch, causing the skeleton/empty-state to flicker when the list is empty.\n const showInitialSkeleton = isLoading || !isFetched;\n const showPinnedSection =\n !compact && !showInitialSkeleton && pinnedConversations.length > 0;\n const showEmptyState =\n isFetched &&\n !isLoading &&\n !compact &&\n listIsEffectivelyEmpty &&\n !showPinnedSection &&\n !startTasks?.length;\n\n const showConversationHeader = !compact;\n\n return (\n <div\n ref={ref}\n data-testid=\"conversation-panel\"\n className=\"flex h-full min-h-0 w-full flex-col\"\n >\n {showConversationHeader && (\n <div\n className={cn(\n // Pull flush to the sidebar edges: `-ml-2.5` matches aside `pl-2.5`;\n // width extends by that inset on the right now that aside is `pr-0`.\n \"-ml-2.5 w-[calc(100%+0.625rem)] max-w-none box-border border-b\",\n isListScrolled ? \"border-[var(--oh-border)]\" : \"border-transparent\",\n )}\n >\n <div\n data-testid=\"older-conversations-summary\"\n className=\"flex min-w-0 flex-nowrap items-center gap-x-2 py-2 pl-4 pr-2.5 text-[var(--oh-muted)]\"\n >\n <span className=\"min-w-0 truncate text-sm font-medium text-[var(--oh-muted)]\">\n {t(I18nKey.SIDEBAR$CONVERSATIONS)}\n </span>\n <div className=\"ml-auto flex shrink-0 items-center gap-0.5\">\n <ConversationPanelNewThreadPicker\n backendKind={activeBackend.kind}\n />\n <ConversationPanelFilterMenu\n filterMenuOpen={filterMenuOpen}\n setFilterMenuOpen={setFilterMenuOpen}\n menuRef={filterMenuRef}\n backendKind={activeBackend.kind}\n organizeMode={organizeMode}\n setOrganizeMode={setOrganizeMode}\n conversationSort={conversationSort}\n setConversationSort={setConversationSort}\n threadScope={threadScope}\n setThreadScope={setThreadScope}\n showOlderConversations={showOlderConversations}\n toggleShowOlderConversations={toggleShowOlderConversations}\n showRepoBranchMetadata={showRepoBranchMetadata}\n toggleShowRepoBranchMetadata={toggleShowRepoBranchMetadata}\n showLlmProfiles={showLlmProfiles}\n toggleShowLlmProfiles={toggleShowLlmProfiles}\n showTagsMetadata={showTagsMetadata}\n toggleShowTagsMetadata={toggleShowTagsMetadata}\n showHoverMetadata={showHoverMetadata}\n toggleShowHoverMetadata={toggleShowHoverMetadata}\n totalConversationsCount={conversations.length}\n onRequestDeleteAll={() => setConfirmDeleteAllVisible(true)}\n />\n </div>\n </div>\n </div>\n )}\n\n <div\n ref={scrollContainerRef}\n data-testid=\"conversation-panel-list-scroll\"\n onScroll={(event) => {\n setIsListScrolled(event.currentTarget.scrollTop > 0);\n }}\n className={cn(\n \"flex min-h-0 flex-1 flex-col overflow-y-auto overflow-x-hidden overscroll-contain custom-scrollbar-always\",\n !compact && \"conversation-panel-list-scroll\",\n )}\n >\n {showInitialSkeleton && <ConversationCardSkeleton compact={compact} />}\n\n {!compact && showEmptyState && (\n <div\n data-testid=\"conversation-panel-empty-state\"\n className=\"flex min-h-0 flex-1 flex-col items-center justify-center px-4 py-8\"\n >\n <p className=\"text-xs text-[var(--oh-muted)]\">\n {t(I18nKey.CONVERSATION$NO_CONVERSATIONS)}\n </p>\n </div>\n )}\n\n {showPinnedSection ? (\n <ConversationPanelPinnedSection\n pinnedConversations={pinnedConversations}\n isPreviewExpanded={expandedPinnedPreview}\n onTogglePreviewExpanded={() =>\n setExpandedPinnedPreview((current) => !current)\n }\n activeConversationId={currentConversationId}\n showDivider={!compact && organizeMode === \"chronological\"}\n renderConversationCard={(conversation) =>\n renderConversationCard(conversation, { inPinnedSection: true })\n }\n />\n ) : null}\n\n {/* Render in-progress start tasks first (skipped in compact mode —\n their rich card layout doesn't fit in the icon rail). */}\n {!compact &&\n startTasks?.map((task) => (\n <NavigationLink\n key={task.id}\n to={backendScopedPath(`/conversations/task-${task.id}`)}\n onClick={onClose}\n className=\"block\"\n >\n <StartTaskCard task={task} />\n </NavigationLink>\n ))}\n\n {!showInitialSkeleton && compact\n ? compactVisibleConversations.map((conversation) =>\n renderConversationCard(conversation),\n )\n : null}\n\n {!showInitialSkeleton &&\n !compact &&\n organizeMode === \"grouped\" &&\n orderedConversationGroups &&\n orderedConversationGroups.length > 0 ? (\n <ConversationGroupFolderList\n groups={orderedConversationGroups}\n groupIds={conversationGroupIds}\n groupFolderOrder={groupFolderOrder}\n setGroupFolderOrder={setGroupFolderOrder}\n collapsedGroupIds={collapsedGroupIds}\n expandedGroupPreviewIds={expandedGroupPreviewIds}\n onToggleGroupCollapsed={toggleGroupCollapsed}\n onToggleGroupPreviewExpanded={toggleGroupPreviewExpanded}\n isCreatingConversationFlow={isCreatingConversationFlow}\n activeConversationId={currentConversationId}\n onLaunchFromGroup={launchFromGroup}\n renderConversationCard={(conversation) =>\n renderConversationCard(conversation)\n }\n />\n ) : null}\n\n {!showInitialSkeleton &&\n !compact &&\n organizeMode === \"chronological\" ? (\n <div className=\"space-y-0.5\">\n {sortedVisibleConversations.map((conversation) =>\n renderConversationCard(conversation),\n )}\n </div>\n ) : null}\n\n {/* Explicit \"Load more\" trigger. Only shown when more pages exist\n *and* the older list is currently visible (or there are no older\n conversations to begin with) — otherwise the next page would be\n populated mostly with conversations the user has chosen to hide. */}\n {showLoadMore &&\n (isLoadingMore ? (\n <div className=\"py-1\">\n <ConversationCardSkeleton compact={compact} />\n </div>\n ) : (\n <div className=\"flex justify-center py-4\">\n <button\n type=\"button\"\n data-testid=\"load-more-conversations\"\n onClick={requestLoadMore}\n className=\"text-xs text-[var(--oh-muted)] hover:text-white\"\n >\n {t(I18nKey.CONVERSATION$LOAD_MORE)}\n </button>\n </div>\n ))}\n </div>\n\n {confirmDeleteModalVisible && (\n <ConfirmDeleteModal\n onConfirm={() => {\n handleConfirmDelete();\n setConfirmDeleteModalVisible(false);\n setSelectedConversationTitle(null);\n }}\n onCancel={() => {\n setConfirmDeleteModalVisible(false);\n setSelectedConversationTitle(null);\n }}\n conversationTitle={selectedConversationTitle ?? undefined}\n />\n )}\n\n {confirmDeleteAllVisible && (\n <ConfirmDeleteModal\n title={t(I18nKey.CONVERSATION$CONFIRM_DELETE_ALL_TITLE)}\n description={t(I18nKey.CONVERSATION$CONFIRM_DELETE_ALL_DESC, {\n count: conversations.length,\n })}\n onConfirm={async () => {\n await handleConfirmDeleteAll();\n setConfirmDeleteAllVisible(false);\n }}\n onCancel={() => setConfirmDeleteAllVisible(false)}\n />\n )}\n\n {confirmStopModalVisible && (\n <ConfirmStopModal\n onConfirm={() => {\n handleConfirmStop();\n setConfirmStopModalVisible(false);\n }}\n onCancel={() => setConfirmStopModalVisible(false)}\n />\n )}\n\n {confirmExitConversationModalVisible && (\n <ExitConversationModal\n onConfirm={() => {\n onClose?.();\n }}\n onClose={() => setConfirmExitConversationModalVisible(false)}\n onCancel={() => setConfirmExitConversationModalVisible(false)}\n />\n )}\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"conversation-panel.js","names":[],"sources":["../../../../src/components/features/conversation-panel/conversation-panel.tsx"],"sourcesContent":["import React from \"react\";\nimport { Tooltip } from \"@heroui/react\";\nimport { useTranslation } from \"react-i18next\";\nimport { I18nKey } from \"#/i18n/declaration\";\nimport { useNavigation } from \"#/context/navigation-context\";\nimport { useActiveBackend } from \"#/contexts/active-backend-context\";\nimport { useBackendScopedPath } from \"#/hooks/use-backend-scoped-path\";\nimport { usePaginatedConversations } from \"#/hooks/query/use-paginated-conversations\";\nimport { useStartTasks } from \"#/hooks/query/use-start-tasks\";\nimport { useDeleteConversation } from \"#/hooks/mutation/use-delete-conversation\";\nimport { useUnifiedPauseConversation } from \"#/hooks/mutation/use-unified-stop-conversation\";\nimport { ConfirmDeleteModal } from \"./confirm-delete-modal\";\nimport { ConfirmStopModal } from \"./confirm-stop-modal\";\nimport { NavigationLink } from \"#/components/shared/navigation-link\";\nimport { ExitConversationModal } from \"./exit-conversation-modal\";\nimport { useClickOutsideElement } from \"#/hooks/use-click-outside-element\";\nimport { Provider } from \"#/types/settings\";\nimport { useUpdateConversation } from \"#/hooks/mutation/use-update-conversation\";\nimport {\n displayErrorToast,\n displaySuccessToast,\n} from \"#/utils/custom-toast-handlers\";\nimport { isExecutionActive } from \"#/utils/status\";\nimport { useCreateConversation } from \"#/hooks/mutation/use-create-conversation\";\nimport { useIsCreatingConversation } from \"#/hooks/use-is-creating-conversation\";\nimport { ConversationCard } from \"./conversation-card/conversation-card\";\nimport { ConversationCardPreview } from \"./conversation-card/conversation-card-preview\";\nimport { StartTaskCard } from \"./start-task-card/start-task-card\";\nimport { ConversationCardSkeleton } from \"./conversation-card/conversation-card-skeleton\";\nimport { CompactConversationRow } from \"./compact-conversation-row\";\nimport { useConversationPanelPreferencesStore } from \"#/stores/conversation-panel-preferences-store\";\nimport { cn } from \"#/utils/utils\";\nimport { ConversationPanelFilterMenu } from \"./conversation-panel-filter-menu\";\nimport { ConversationPanelNewThreadPicker } from \"./conversation-panel-new-thread-picker\";\nimport { ConversationGroupFolderList } from \"./conversation-group-folder-list\";\nimport { ConversationPanelPinnedSection } from \"./conversation-panel-pinned-section\";\nimport {\n applyAutomationConversationFilter,\n applyGroupFolderOrder,\n collectAutomationNameFacets,\n filterOutPinnedConversations,\n groupConversations,\n resolvePinnedConversations,\n sortConversationsByField,\n type ConversationGroupLaunch,\n} from \"./conversation-panel-list-helpers\";\nimport { usePinnedConversationsStore } from \"#/stores/pinned-conversations-store\";\n\ninterface ConversationPanelProps {\n onClose?: () => void;\n /**\n * Render a minimal icon-only variant of each conversation row (used by the\n * collapsed sidebar). Each row is a single status dot with a hover preview\n * containing the full card content.\n */\n compact?: boolean;\n}\n\nconst noop = () => {};\n\nconst EMPTY_PINNED_CONVERSATION_IDS: readonly string[] = [];\n\nconst ONE_HOUR_MS = 60 * 60 * 1000;\n\nconst partitionByCutoff = <T extends { updated_at: string }>(\n items: readonly T[],\n): { recent: T[]; older: T[] } => {\n // The cutoff is intentionally relative to \"now\" each time the list is\n // recomputed, so conversations naturally age into the older bucket as the\n // conversations query refreshes.\n const cutoff = Date.now() - ONE_HOUR_MS;\n const recent: T[] = [];\n const older: T[] = [];\n for (const item of items) {\n const updatedAt = item.updated_at ? Date.parse(item.updated_at) : NaN;\n // Missing or unparseable timestamps stay in the \"recent\" bucket so we\n // do not accidentally hide them behind the older-conversations toggle.\n if (Number.isFinite(updatedAt) && updatedAt < cutoff) {\n older.push(item);\n } else {\n recent.push(item);\n }\n }\n return { recent, older };\n};\n\nexport function ConversationPanel({\n onClose,\n compact = false,\n}: ConversationPanelProps) {\n const { t } = useTranslation(\"openhands\");\n const { conversationId: currentConversationId, navigate } = useNavigation();\n const { backend: activeBackend } = useActiveBackend();\n const backendScopedPath = useBackendScopedPath();\n // Click-outside is only relevant in the legacy drawer mode where an\n // onClose handler is provided. When the panel is rendered inline (e.g.\n // as the always-visible conversation list pane), clicking outside should\n // not dismiss the list, so we pass a no-op callback in that case.\n const ref = useClickOutsideElement<HTMLDivElement>(onClose ?? noop);\n\n const [confirmDeleteModalVisible, setConfirmDeleteModalVisible] =\n React.useState(false);\n const [confirmStopModalVisible, setConfirmStopModalVisible] =\n React.useState(false);\n const [\n confirmExitConversationModalVisible,\n setConfirmExitConversationModalVisible,\n ] = React.useState(false);\n const [confirmDeleteAllVisible, setConfirmDeleteAllVisible] =\n React.useState(false);\n const showOlderConversations = useConversationPanelPreferencesStore(\n (state) => state.showOlderConversations,\n );\n const toggleShowOlderConversations = useConversationPanelPreferencesStore(\n (state) => state.toggleShowOlderConversations,\n );\n const showRepoBranchMetadata = useConversationPanelPreferencesStore(\n (state) => state.showRepoBranchMetadata,\n );\n const toggleShowRepoBranchMetadata = useConversationPanelPreferencesStore(\n (state) => state.toggleShowRepoBranchMetadata,\n );\n const showLlmProfiles = useConversationPanelPreferencesStore(\n (state) => state.showLlmProfiles,\n );\n const toggleShowLlmProfiles = useConversationPanelPreferencesStore(\n (state) => state.toggleShowLlmProfiles,\n );\n const showTagsMetadata = useConversationPanelPreferencesStore(\n (state) => state.showTagsMetadata,\n );\n const toggleShowTagsMetadata = useConversationPanelPreferencesStore(\n (state) => state.toggleShowTagsMetadata,\n );\n const showHoverMetadata = useConversationPanelPreferencesStore(\n (state) => state.showHoverMetadata,\n );\n const toggleShowHoverMetadata = useConversationPanelPreferencesStore(\n (state) => state.toggleShowHoverMetadata,\n );\n const organizeMode = useConversationPanelPreferencesStore(\n (state) => state.organizeMode,\n );\n const setOrganizeMode = useConversationPanelPreferencesStore(\n (state) => state.setOrganizeMode,\n );\n const conversationSort = useConversationPanelPreferencesStore(\n (state) => state.conversationSort,\n );\n const setConversationSort = useConversationPanelPreferencesStore(\n (state) => state.setConversationSort,\n );\n const threadScope = useConversationPanelPreferencesStore(\n (state) => state.threadScope,\n );\n const setThreadScope = useConversationPanelPreferencesStore(\n (state) => state.setThreadScope,\n );\n const automationFilterMode = useConversationPanelPreferencesStore(\n (state) => state.automationFilterMode,\n );\n const setAutomationFilterMode = useConversationPanelPreferencesStore(\n (state) => state.setAutomationFilterMode,\n );\n const selectedAutomationNames = useConversationPanelPreferencesStore(\n (state) => state.selectedAutomationNames,\n );\n const toggleAutomationName = useConversationPanelPreferencesStore(\n (state) => state.toggleAutomationName,\n );\n const groupFolderOrder = useConversationPanelPreferencesStore(\n (state) => state.groupFolderOrder,\n );\n const setGroupFolderOrder = useConversationPanelPreferencesStore(\n (state) => state.setGroupFolderOrder,\n );\n const [filterMenuOpen, setFilterMenuOpen] = React.useState(false);\n const [isListScrolled, setIsListScrolled] = React.useState(false);\n const filterMenuRef = useClickOutsideElement<HTMLDivElement>(() => {\n setFilterMenuOpen(false);\n });\n const [collapsedGroupIds, setCollapsedGroupIds] = React.useState<\n ReadonlySet<string>\n >(() => new Set());\n const [expandedGroupPreviewIds, setExpandedGroupPreviewIds] = React.useState<\n ReadonlySet<string>\n >(() => new Set());\n const [expandedPinnedPreview, setExpandedPinnedPreview] =\n React.useState(false);\n\n const pinnedIds = usePinnedConversationsStore(\n (state) =>\n state.pinsByBackendId[activeBackend.id] ?? EMPTY_PINNED_CONVERSATION_IDS,\n );\n const togglePin = usePinnedConversationsStore((state) => state.togglePin);\n const pruneMissingPinnedConversations = usePinnedConversationsStore(\n (state) => state.pruneMissingConversations,\n );\n\n const toggleGroupCollapsed = React.useCallback((groupId: string) => {\n setCollapsedGroupIds((prev) => {\n const next = new Set(prev);\n if (next.has(groupId)) {\n next.delete(groupId);\n } else {\n next.add(groupId);\n }\n return next;\n });\n }, []);\n\n const toggleGroupPreviewExpanded = React.useCallback((groupId: string) => {\n setExpandedGroupPreviewIds((prev) => {\n const next = new Set(prev);\n if (next.has(groupId)) {\n next.delete(groupId);\n } else {\n next.add(groupId);\n }\n return next;\n });\n }, []);\n\n React.useEffect(() => {\n if (organizeMode !== \"grouped\") {\n setCollapsedGroupIds(new Set());\n setExpandedGroupPreviewIds(new Set());\n }\n }, [organizeMode]);\n\n const scrollContainerRef = React.useRef<HTMLDivElement>(null);\n\n const [selectedConversationId, setSelectedConversationId] = React.useState<\n string | null\n >(null);\n const [selectedConversationTitle, setSelectedConversationTitle] =\n React.useState<string | null>(null);\n const [openContextMenuId, setOpenContextMenuId] = React.useState<\n string | null\n >(null);\n\n const {\n data,\n isLoading,\n isFetched,\n hasNextPage,\n isFetching,\n isFetchingNextPage,\n fetchNextPage,\n } = usePaginatedConversations();\n\n // Fetch in-progress start tasks\n const { data: startTasks } = useStartTasks();\n\n const conversations = React.useMemo(() => {\n const all = data?.pages.flatMap((page) => page.items) ?? [];\n // The 10s background refetch re-fetches every loaded page with the\n // `UPDATED_AT_DESC` cursor. If a conversation's `updated_at` shifts between\n // page fetches, a later page can overlap an earlier one and surface the\n // same conversation twice. Dedupe by id (keeping the first/freshest copy)\n // so the rendered count reflects real growth and React keys stay unique.\n const seen = new Set<string>();\n return all.filter((conversation) => {\n if (seen.has(conversation.id)) {\n return false;\n }\n seen.add(conversation.id);\n return true;\n });\n }, [data]);\n\n // Facets derive from the unfiltered list so the automation-name rows in the\n // filter menu don't vanish while a narrowing selection is active.\n const automationNameFacets = React.useMemo(\n () => collectAutomationNameFacets(conversations),\n [conversations],\n );\n\n const automationFilteredConversations = React.useMemo(\n () =>\n applyAutomationConversationFilter(\n conversations,\n automationFilterMode,\n selectedAutomationNames,\n automationNameFacets,\n ),\n [\n automationFilterMode,\n automationNameFacets,\n conversations,\n selectedAutomationNames,\n ],\n );\n\n const pinnedConversations = React.useMemo(\n () => resolvePinnedConversations(pinnedIds, conversations),\n [conversations, pinnedIds],\n );\n\n React.useEffect(() => {\n if (!isFetched) {\n return;\n }\n pruneMissingPinnedConversations(\n activeBackend.id,\n conversations.map((conversation) => conversation.id),\n );\n }, [\n activeBackend.id,\n conversations,\n isFetched,\n pruneMissingPinnedConversations,\n ]);\n\n React.useEffect(() => {\n if (pinnedIds.length === 0) {\n setExpandedPinnedPreview(false);\n }\n }, [pinnedIds.length]);\n\n const scopedConversations = React.useMemo(() => {\n // The pinned section intentionally bypasses the automation filter (same\n // exemption the thread scope has): a pin is an explicit user override.\n const scopeFiltered =\n threadScope === \"relevant\"\n ? automationFilteredConversations.filter((c) =>\n isExecutionActive(c.execution_status),\n )\n : automationFilteredConversations;\n\n // In the expanded panel, pinned conversations should only appear inside\n // the dedicated pinned section (not duplicated in grouped/flat lists).\n if (compact) {\n return scopeFiltered;\n }\n\n return filterOutPinnedConversations(scopeFiltered, pinnedIds);\n }, [automationFilteredConversations, compact, pinnedIds, threadScope]);\n\n const { recent: recentScoped, older: olderScoped } = React.useMemo(\n () => partitionByCutoff(scopedConversations),\n [scopedConversations],\n );\n\n // Sort the full visible set as one list. The recent/older partition is\n // still computed (it gates the \"Show older\" toggle and \"Load more\"\n // visibility), but the rendering must not use it as a visual boundary —\n // when sorting by `created`, a stale-but-recently-touched conversation\n // would otherwise land in `recent` and render above an actually-newer-\n // by-`created_at` conversation sitting in `older`.\n const sortedVisibleConversations = React.useMemo(() => {\n const visible = showOlderConversations\n ? [...recentScoped, ...olderScoped]\n : recentScoped;\n return sortConversationsByField(visible, conversationSort);\n }, [recentScoped, olderScoped, showOlderConversations, conversationSort]);\n\n const groupLabels = React.useMemo(\n () => ({\n emptyWorkspace: t(I18nKey.CONVERSATION_PANEL$NO_WORKSPACE),\n emptyRepository: t(I18nKey.CONVERSATION_PANEL$NO_REPOSITORY),\n }),\n [t],\n );\n\n const conversationGroups = React.useMemo(() => {\n if (compact || organizeMode !== \"grouped\") {\n return null;\n }\n // Use the unsorted partitions: groupConversations sorts each bucket\n // internally by `sortField`, so pre-sorting the merged input is wasted\n // work in grouped mode (the per-group sort overrides any global order).\n const merged = [\n ...recentScoped,\n ...(showOlderConversations ? olderScoped : []),\n ];\n return groupConversations(\n merged,\n activeBackend.kind,\n conversationSort,\n groupLabels,\n );\n }, [\n activeBackend.kind,\n compact,\n conversationSort,\n groupLabels,\n olderScoped,\n organizeMode,\n recentScoped,\n showOlderConversations,\n ]);\n\n const orderedConversationGroups = React.useMemo(() => {\n if (!conversationGroups) {\n return null;\n }\n return applyGroupFolderOrder(conversationGroups, groupFolderOrder);\n }, [conversationGroups, groupFolderOrder]);\n\n const conversationGroupIds = React.useMemo(\n () => conversationGroups?.map((group) => group.id) ?? [],\n [conversationGroups],\n );\n\n const compactVisibleConversations = React.useMemo(\n () =>\n sortConversationsByField(\n recentScoped.filter((conversation) =>\n isExecutionActive(conversation.execution_status),\n ),\n conversationSort,\n ),\n [conversationSort, recentScoped],\n );\n\n const visibleFlatCount = sortedVisibleConversations.length;\n\n const visibleGroupedCount = React.useMemo(() => {\n if (!orderedConversationGroups) {\n return 0;\n }\n return orderedConversationGroups.reduce(\n (n, g) => n + g.conversations.length,\n 0,\n );\n }, [orderedConversationGroups]);\n\n const listIsEffectivelyEmpty =\n organizeMode === \"grouped\" && !compact\n ? visibleGroupedCount === 0\n : visibleFlatCount === 0;\n\n // Attribution is exact: the automation filter step itself produced zero\n // rows out of a non-empty loaded set (not merely threadScope/older-cutoff\n // effects). Used to keep \"Load more\" reachable and to pick the empty-state\n // message.\n const emptyDueToAutomationFilter =\n listIsEffectivelyEmpty &&\n automationFilterMode !== \"all\" &&\n conversations.length > 0 &&\n automationFilteredConversations.length === 0;\n\n // Number of conversations actually rendered in the list right now, in the\n // current organize mode. \"Load more\" succeeds only when this number grows.\n const visibleCount =\n organizeMode === \"grouped\" && !compact\n ? visibleGroupedCount\n : visibleFlatCount;\n\n // KNOWN ISSUE (unresolved as of 2026-05-29): users still report that the\n // sidebar \"Load more\" sometimes requires two clicks before new conversations\n // appear. The mitigation below (dedupe by id in `conversations`, plus the\n // floor-tracking driver that keeps fetching until the visible count grows)\n // reduced but did NOT fully eliminate the symptom in manual testing. Likely\n // remaining suspects to investigate next: the agent-server cursor pagination\n // returning an overlapping/short page under `UPDATED_AT_DESC` while the 10s\n // `refetchInterval` reorders pages (see `usePaginatedConversations`), or a\n // React Query state lag where `hasNextPage`/`isFetching` settle a render\n // after the click. If you pick this up, reproduce against a backend with\n // >40 conversations and watch the `/api/conversations/search` cursors.\n //\n // Robust \"Load more\" driver. A single click can fail to surface new rows for\n // two reasons: (1) `fetchNextPage()` is silently dropped while the 10s\n // background refetch is in flight, and (2) a fetched page can yield zero\n // *visible* rows (filtered out by the active scope, or deduped as overlap),\n // so the list does not appear to grow. We capture the visible count at click\n // time and keep fetching pages — once the query is idle — until the visible\n // count actually increases or there are no more pages.\n const [loadMoreFloor, setLoadMoreFloor] = React.useState<number | null>(null);\n const visibleCountRef = React.useRef(visibleCount);\n visibleCountRef.current = visibleCount;\n\n const requestLoadMore = React.useCallback(() => {\n if (hasNextPage) {\n setLoadMoreFloor(visibleCountRef.current);\n }\n }, [hasNextPage]);\n\n React.useEffect(() => {\n if (loadMoreFloor === null) {\n return;\n }\n // Goal met: the visible list grew past where it was when the user clicked.\n if (visibleCount > loadMoreFloor) {\n setLoadMoreFloor(null);\n return;\n }\n // Nothing more to fetch — stop waiting even if the list did not grow.\n if (!hasNextPage) {\n setLoadMoreFloor(null);\n return;\n }\n // Wait for any in-flight fetch (including the background refetch) to settle\n // before requesting the next page, otherwise the request is dropped.\n if (isFetching || isFetchingNextPage) {\n return;\n }\n fetchNextPage();\n }, [\n loadMoreFloor,\n visibleCount,\n hasNextPage,\n isFetching,\n isFetchingNextPage,\n fetchNextPage,\n ]);\n\n const isLoadingMore = loadMoreFloor !== null || isFetchingNextPage;\n\n const { mutate: deleteConversation, mutateAsync: deleteConversationAsync } =\n useDeleteConversation();\n const { mutate: pauseConversation } = useUnifiedPauseConversation();\n const { mutate: updateConversation } = useUpdateConversation();\n\n // The next page of conversations is loaded only via the explicit \"Load\n // more\" link rendered at the end of the list — there is no scroll-driven\n // pagination, which previously caused the panel to feel like it had stray\n // scrollable space at the bottom.\n const olderHidden = olderScoped.length > 0 && !showOlderConversations;\n // Compact mode also hides \"Load more\" — paginating into archived\n // conversations contradicts the \"active only\" intent of the icon rail.\n // Do not show when the visible list is empty (that state already shows\n // \"No conversations found\") — unless the automation filter alone emptied\n // it while more pages exist: then the button must stay reachable so the\n // load-more driver can keep fetching until a matching run surfaces.\n const showLoadMore =\n !!hasNextPage &&\n !olderHidden &&\n !compact &&\n (!listIsEffectivelyEmpty || emptyDueToAutomationFilter);\n\n const { mutate: createConversation } = useCreateConversation();\n const isCreatingConversationFlow = useIsCreatingConversation();\n\n const launchFromGroup = React.useCallback(\n (launch: ConversationGroupLaunch) => {\n if (isCreatingConversationFlow) return;\n createConversation(\n {\n workingDir: launch.workingDir,\n repository: launch.repository,\n entryPoint: \"sidebar_relaunch_project\",\n },\n {\n onSuccess: (data) => {\n navigate(`/conversations/${data.conversation_id}`);\n },\n },\n );\n },\n [createConversation, isCreatingConversationFlow, navigate],\n );\n\n const handleDeleteProject = React.useCallback(\n (conversationId: string, title: string) => {\n setConfirmDeleteModalVisible(true);\n setSelectedConversationId(conversationId);\n setSelectedConversationTitle(title);\n },\n [],\n );\n\n const handleStopConversation = React.useCallback((conversationId: string) => {\n setConfirmStopModalVisible(true);\n setSelectedConversationId(conversationId);\n }, []);\n\n const handleConversationTitleChange = React.useCallback(\n (conversationId: string, newTitle: string) => {\n updateConversation(\n { conversationId, newTitle },\n {\n onSuccess: () => {\n displaySuccessToast(t(I18nKey.CONVERSATION$TITLE_UPDATED));\n },\n },\n );\n },\n [t, updateConversation],\n );\n\n const handleConfirmDelete = () => {\n if (selectedConversationId) {\n deleteConversation(\n { conversationId: selectedConversationId },\n {\n onSuccess: () => {\n if (selectedConversationId === currentConversationId) {\n navigate(\"/conversations\");\n }\n },\n },\n );\n }\n };\n\n const handleConfirmStop = () => {\n if (selectedConversationId) {\n pauseConversation({\n conversationId: selectedConversationId,\n });\n }\n };\n\n const handleConfirmDeleteAll = async () => {\n const idsToDelete = conversations.map((c) => c.id);\n const results = await Promise.allSettled(\n idsToDelete.map((conversationId) =>\n deleteConversationAsync({ conversationId }),\n ),\n );\n\n const deletedIds = results.flatMap((result, index) =>\n result.status === \"fulfilled\" ? [idsToDelete[index]] : [],\n );\n const failedCount = results.length - deletedIds.length;\n\n if (\n currentConversationId !== null &&\n deletedIds.includes(currentConversationId)\n ) {\n navigate(\"/conversations\");\n }\n\n if (failedCount > 0) {\n displayErrorToast(\n `${failedCount} conversation${failedCount === 1 ? \"\" : \"s\"} could not be deleted.`,\n );\n }\n };\n\n const renderConversationCard = React.useCallback(\n (\n conversation: (typeof conversations)[number],\n options?: { inPinnedSection?: boolean },\n ) => {\n const isPinned = pinnedIds.includes(conversation.id);\n if (compact) {\n return (\n <CompactConversationRow\n key={conversation.id}\n conversationId={conversation.id}\n title={conversation.title ?? \"\"}\n selectedRepository={{\n selected_repository: conversation.selected_repository,\n selected_branch: conversation.selected_branch,\n git_provider: conversation.git_provider as Provider,\n }}\n executionStatus={conversation.execution_status}\n sandboxStatus={conversation.sandbox_status}\n lastUpdatedAt={conversation.updated_at}\n createdAt={conversation.created_at}\n workspaceWorkingDir={\n conversation.selected_workspace ??\n conversation.workspace?.working_dir\n }\n isActive={conversation.id === currentConversationId}\n onClose={onClose}\n showRepositoryMetadata={showRepoBranchMetadata}\n llmModel={conversation.llm_model}\n showLlmProfiles={showLlmProfiles}\n agentKind={conversation.agent_kind}\n acpServer={conversation.acp_server}\n tags={conversation.tags}\n showTags={showTagsMetadata}\n />\n );\n }\n return (\n <Tooltip\n key={conversation.id}\n placement=\"right-start\"\n delay={1000}\n closeDelay={100}\n isDisabled={\n !showHoverMetadata || openContextMenuId === conversation.id\n }\n disableAnimation={import.meta.env.MODE === \"test\"}\n className=\"max-w-none overflow-visible rounded-xl border border-[var(--oh-border)] bg-base-secondary p-0 text-white shadow-xl\"\n content={\n <ConversationCardPreview\n title={conversation.title ?? \"\"}\n executionStatus={conversation.execution_status}\n sandboxStatus={conversation.sandbox_status}\n selectedRepository={{\n selected_repository: conversation.selected_repository,\n selected_branch: conversation.selected_branch,\n git_provider: conversation.git_provider as Provider,\n }}\n workspaceWorkingDir={\n conversation.selected_workspace ??\n conversation.workspace?.working_dir\n }\n llmModel={conversation.llm_model}\n agentKind={conversation.agent_kind}\n acpServer={conversation.acp_server}\n createdAt={conversation.created_at}\n tags={conversation.tags}\n />\n }\n >\n <NavigationLink\n to={backendScopedPath(`/conversations/${conversation.id}`)}\n onClick={onClose}\n className={cn(\n \"block rounded-md transition-colors\",\n openContextMenuId !== conversation.id &&\n \"hover:bg-[var(--oh-surface)]\",\n (conversation.id === currentConversationId ||\n openContextMenuId === conversation.id) &&\n \"bg-[var(--oh-surface)]\",\n )}\n >\n <ConversationCard\n onDelete={() =>\n handleDeleteProject(conversation.id, conversation.title ?? \"\")\n }\n onStop={() => handleStopConversation(conversation.id)}\n onChangeTitle={(title) =>\n handleConversationTitleChange(conversation.id, title)\n }\n title={conversation.title ?? \"\"}\n selectedRepository={{\n selected_repository: conversation.selected_repository,\n selected_branch: conversation.selected_branch,\n git_provider: conversation.git_provider as Provider,\n }}\n lastUpdatedAt={conversation.updated_at}\n createdAt={conversation.created_at}\n executionStatus={conversation.execution_status}\n sandboxStatus={conversation.sandbox_status}\n conversationId={conversation.id}\n contextMenuOpen={openContextMenuId === conversation.id}\n onContextMenuToggle={(isOpen) =>\n setOpenContextMenuId(isOpen ? conversation.id : null)\n }\n isActive={conversation.id === currentConversationId}\n workspaceWorkingDir={\n conversation.selected_workspace ??\n conversation.workspace?.working_dir\n }\n showRepositoryMetadata={showRepoBranchMetadata}\n llmModel={conversation.llm_model}\n showLlmProfiles={showLlmProfiles}\n agentKind={conversation.agent_kind}\n acpServer={conversation.acp_server}\n tags={conversation.tags}\n showTags={showTagsMetadata}\n isPinned={isPinned}\n onTogglePin={() => togglePin(activeBackend.id, conversation.id)}\n alwaysShowPinIcon={isPinned && !options?.inPinnedSection}\n />\n </NavigationLink>\n </Tooltip>\n );\n },\n [\n activeBackend.id,\n compact,\n currentConversationId,\n handleConversationTitleChange,\n handleDeleteProject,\n handleStopConversation,\n onClose,\n openContextMenuId,\n pinnedIds,\n showRepoBranchMetadata,\n showLlmProfiles,\n showTagsMetadata,\n showHoverMetadata,\n togglePin,\n ],\n );\n\n // Standard layout: panel fills its slot in the sidebar; the inner scroll\n // child fills the panel and scrolls when its content overflows. Modals are\n // siblings of the scroll element and are `position: fixed`, so they don't\n // participate in the panel's scroll geometry.\n // Gate on `isLoading` / `!isFetched` (true only until the first fetch settles),\n // not `isFetching` — the latter flips back to true on every 10s background\n // refetch, causing the skeleton/empty-state to flicker when the list is empty.\n const showInitialSkeleton = isLoading || !isFetched;\n const showPinnedSection =\n !compact && !showInitialSkeleton && pinnedConversations.length > 0;\n const showEmptyState =\n isFetched &&\n !isLoading &&\n !compact &&\n listIsEffectivelyEmpty &&\n !showPinnedSection &&\n !startTasks?.length;\n\n const showConversationHeader = !compact;\n\n return (\n <div\n ref={ref}\n data-testid=\"conversation-panel\"\n className=\"flex h-full min-h-0 w-full flex-col\"\n >\n {showConversationHeader && (\n <div\n className={cn(\n // Pull flush to the sidebar edges: `-ml-2.5` matches aside `pl-2.5`;\n // width extends by that inset on the right now that aside is `pr-0`.\n \"-ml-2.5 w-[calc(100%+0.625rem)] max-w-none box-border border-b\",\n isListScrolled ? \"border-[var(--oh-border)]\" : \"border-transparent\",\n )}\n >\n <div\n data-testid=\"older-conversations-summary\"\n className=\"flex min-w-0 flex-nowrap items-center gap-x-2 py-2 pl-4 pr-2.5 text-[var(--oh-muted)]\"\n >\n <span className=\"min-w-0 truncate text-sm font-medium text-[var(--oh-muted)]\">\n {t(I18nKey.SIDEBAR$CONVERSATIONS)}\n </span>\n <div className=\"ml-auto flex shrink-0 items-center gap-0.5\">\n <ConversationPanelNewThreadPicker\n backendKind={activeBackend.kind}\n />\n <ConversationPanelFilterMenu\n filterMenuOpen={filterMenuOpen}\n setFilterMenuOpen={setFilterMenuOpen}\n menuRef={filterMenuRef}\n backendKind={activeBackend.kind}\n organizeMode={organizeMode}\n setOrganizeMode={setOrganizeMode}\n conversationSort={conversationSort}\n setConversationSort={setConversationSort}\n threadScope={threadScope}\n setThreadScope={setThreadScope}\n automationFilterMode={automationFilterMode}\n setAutomationFilterMode={setAutomationFilterMode}\n selectedAutomationNames={selectedAutomationNames}\n onToggleAutomationName={toggleAutomationName}\n automationNameFacets={automationNameFacets}\n showOlderConversations={showOlderConversations}\n toggleShowOlderConversations={toggleShowOlderConversations}\n showRepoBranchMetadata={showRepoBranchMetadata}\n toggleShowRepoBranchMetadata={toggleShowRepoBranchMetadata}\n showLlmProfiles={showLlmProfiles}\n toggleShowLlmProfiles={toggleShowLlmProfiles}\n showTagsMetadata={showTagsMetadata}\n toggleShowTagsMetadata={toggleShowTagsMetadata}\n showHoverMetadata={showHoverMetadata}\n toggleShowHoverMetadata={toggleShowHoverMetadata}\n totalConversationsCount={conversations.length}\n onRequestDeleteAll={() => setConfirmDeleteAllVisible(true)}\n />\n </div>\n </div>\n </div>\n )}\n\n <div\n ref={scrollContainerRef}\n data-testid=\"conversation-panel-list-scroll\"\n onScroll={(event) => {\n setIsListScrolled(event.currentTarget.scrollTop > 0);\n }}\n className={cn(\n \"flex min-h-0 flex-1 flex-col overflow-y-auto overflow-x-hidden overscroll-contain custom-scrollbar-always\",\n !compact && \"conversation-panel-list-scroll\",\n )}\n >\n {showInitialSkeleton && <ConversationCardSkeleton compact={compact} />}\n\n {!compact && showEmptyState && (\n <div\n data-testid=\"conversation-panel-empty-state\"\n className=\"flex min-h-0 flex-1 flex-col items-center justify-center px-4 py-8\"\n >\n <p className=\"text-xs text-[var(--oh-muted)]\">\n {t(\n emptyDueToAutomationFilter\n ? I18nKey.CONVERSATION_PANEL$NO_AUTOMATION_MATCHES\n : I18nKey.CONVERSATION$NO_CONVERSATIONS,\n )}\n </p>\n </div>\n )}\n\n {showPinnedSection ? (\n <ConversationPanelPinnedSection\n pinnedConversations={pinnedConversations}\n isPreviewExpanded={expandedPinnedPreview}\n onTogglePreviewExpanded={() =>\n setExpandedPinnedPreview((current) => !current)\n }\n activeConversationId={currentConversationId}\n showDivider={!compact && organizeMode === \"chronological\"}\n renderConversationCard={(conversation) =>\n renderConversationCard(conversation, { inPinnedSection: true })\n }\n />\n ) : null}\n\n {/* Render in-progress start tasks first (skipped in compact mode —\n their rich card layout doesn't fit in the icon rail). */}\n {!compact &&\n startTasks?.map((task) => (\n <NavigationLink\n key={task.id}\n to={backendScopedPath(`/conversations/task-${task.id}`)}\n onClick={onClose}\n className=\"block\"\n >\n <StartTaskCard task={task} />\n </NavigationLink>\n ))}\n\n {!showInitialSkeleton && compact\n ? compactVisibleConversations.map((conversation) =>\n renderConversationCard(conversation),\n )\n : null}\n\n {!showInitialSkeleton &&\n !compact &&\n organizeMode === \"grouped\" &&\n orderedConversationGroups &&\n orderedConversationGroups.length > 0 ? (\n <ConversationGroupFolderList\n groups={orderedConversationGroups}\n groupIds={conversationGroupIds}\n groupFolderOrder={groupFolderOrder}\n setGroupFolderOrder={setGroupFolderOrder}\n collapsedGroupIds={collapsedGroupIds}\n expandedGroupPreviewIds={expandedGroupPreviewIds}\n onToggleGroupCollapsed={toggleGroupCollapsed}\n onToggleGroupPreviewExpanded={toggleGroupPreviewExpanded}\n isCreatingConversationFlow={isCreatingConversationFlow}\n activeConversationId={currentConversationId}\n onLaunchFromGroup={launchFromGroup}\n renderConversationCard={(conversation) =>\n renderConversationCard(conversation)\n }\n />\n ) : null}\n\n {!showInitialSkeleton &&\n !compact &&\n organizeMode === \"chronological\" ? (\n <div className=\"space-y-0.5\">\n {sortedVisibleConversations.map((conversation) =>\n renderConversationCard(conversation),\n )}\n </div>\n ) : null}\n\n {/* Explicit \"Load more\" trigger. Only shown when more pages exist\n *and* the older list is currently visible (or there are no older\n conversations to begin with) — otherwise the next page would be\n populated mostly with conversations the user has chosen to hide. */}\n {showLoadMore &&\n (isLoadingMore ? (\n <div className=\"py-1\">\n <ConversationCardSkeleton compact={compact} />\n </div>\n ) : (\n <div className=\"flex justify-center py-4\">\n <button\n type=\"button\"\n data-testid=\"load-more-conversations\"\n onClick={requestLoadMore}\n className=\"text-xs text-[var(--oh-muted)] hover:text-white\"\n >\n {t(I18nKey.CONVERSATION$LOAD_MORE)}\n </button>\n </div>\n ))}\n </div>\n\n {confirmDeleteModalVisible && (\n <ConfirmDeleteModal\n onConfirm={() => {\n handleConfirmDelete();\n setConfirmDeleteModalVisible(false);\n setSelectedConversationTitle(null);\n }}\n onCancel={() => {\n setConfirmDeleteModalVisible(false);\n setSelectedConversationTitle(null);\n }}\n conversationTitle={selectedConversationTitle ?? undefined}\n />\n )}\n\n {confirmDeleteAllVisible && (\n <ConfirmDeleteModal\n title={t(I18nKey.CONVERSATION$CONFIRM_DELETE_ALL_TITLE)}\n description={t(I18nKey.CONVERSATION$CONFIRM_DELETE_ALL_DESC, {\n count: conversations.length,\n })}\n onConfirm={async () => {\n await handleConfirmDeleteAll();\n setConfirmDeleteAllVisible(false);\n }}\n onCancel={() => setConfirmDeleteAllVisible(false)}\n />\n )}\n\n {confirmStopModalVisible && (\n <ConfirmStopModal\n onConfirm={() => {\n handleConfirmStop();\n setConfirmStopModalVisible(false);\n }}\n onCancel={() => setConfirmStopModalVisible(false)}\n />\n )}\n\n {confirmExitConversationModalVisible && (\n <ExitConversationModal\n onConfirm={() => {\n onClose?.();\n }}\n onClose={() => setConfirmExitConversationModalVisible(false)}\n onCancel={() => setConfirmExitConversationModalVisible(false)}\n />\n )}\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0DA,IAAM,WAAa,CAAC,GAEd,KAAmD,CAAC,GAEpD,IAAc,OAAU,KAExB,MACJ,MACgC;CAIhC,IAAM,IAAS,KAAK,IAAI,IAAI,GACtB,IAAc,CAAC,GACf,IAAa,CAAC;CACpB,KAAK,IAAM,KAAQ,GAAO;EACxB,IAAM,IAAY,EAAK,aAAa,KAAK,MAAM,EAAK,UAAU,IAAI;EAGlE,AAAI,OAAO,SAAS,CAAS,KAAK,IAAY,IAC5C,EAAM,KAAK,CAAI,IAEf,EAAO,KAAK,CAAI;CAEpB;CACA,OAAO;EAAE;EAAQ;CAAM;AACzB;AAEA,SAAgB,EAAkB,EAChC,YACA,aAAU,MACe;CACzB,IAAM,EAAE,SAAM,EAAe,WAAW,GAClC,EAAE,gBAAgB,GAAuB,gBAAa,EAAc,GACpE,EAAE,SAAS,MAAkB,GAAiB,GAC9C,KAAoB,GAAqB,GAKzC,KAAM,GAAuC,KAAW,EAAI,GAE5D,CAAC,IAA2B,KAChC,EAAM,SAAS,EAAK,GAChB,CAAC,IAAyB,KAC9B,EAAM,SAAS,EAAK,GAChB,CACJ,IACA,MACE,EAAM,SAAS,EAAK,GAClB,CAAC,IAAyB,KAC9B,EAAM,SAAS,EAAK,GAChB,IAAyB,GAC5B,MAAU,EAAM,sBACnB,GACM,KAA+B,GAClC,MAAU,EAAM,4BACnB,GACM,IAAyB,GAC5B,MAAU,EAAM,sBACnB,GACM,KAA+B,GAClC,MAAU,EAAM,4BACnB,GACM,IAAkB,GACrB,MAAU,EAAM,eACnB,GACM,KAAwB,GAC3B,MAAU,EAAM,qBACnB,GACM,IAAmB,GACtB,MAAU,EAAM,gBACnB,GACM,KAAyB,GAC5B,MAAU,EAAM,sBACnB,GACM,IAAoB,GACvB,MAAU,EAAM,iBACnB,GACM,KAA0B,GAC7B,MAAU,EAAM,uBACnB,GACM,IAAe,GAClB,MAAU,EAAM,YACnB,GACM,KAAkB,GACrB,MAAU,EAAM,eACnB,GACM,IAAmB,GACtB,MAAU,EAAM,gBACnB,GACM,KAAsB,GACzB,MAAU,EAAM,mBACnB,GACM,IAAc,GACjB,MAAU,EAAM,WACnB,GACM,KAAiB,GACpB,MAAU,EAAM,cACnB,GACM,IAAuB,GAC1B,MAAU,EAAM,oBACnB,GACM,KAA0B,GAC7B,MAAU,EAAM,uBACnB,GACM,IAA0B,GAC7B,MAAU,EAAM,uBACnB,GACM,KAAuB,GAC1B,MAAU,EAAM,oBACnB,GACM,IAAmB,GACtB,MAAU,EAAM,gBACnB,GACM,KAAsB,GACzB,MAAU,EAAM,mBACnB,GACM,CAAC,IAAgB,MAAqB,EAAM,SAAS,EAAK,GAC1D,CAAC,IAAgB,MAAqB,EAAM,SAAS,EAAK,GAC1D,KAAgB,SAA6C;EACjE,GAAkB,EAAK;CACzB,CAAC,GACK,CAAC,IAAmB,KAAwB,EAAM,+BAEhD,IAAI,IAAI,CAAC,GACX,CAAC,IAAyB,MAA8B,EAAM,+BAE5D,IAAI,IAAI,CAAC,GACX,CAAC,IAAuB,MAC5B,EAAM,SAAS,EAAK,GAEhB,IAAY,GACf,MACC,EAAM,gBAAgB,EAAc,OAAO,EAC/C,GACM,KAAY,GAA6B,MAAU,EAAM,SAAS,GAClE,KAAkC,GACrC,MAAU,EAAM,yBACnB,GAEM,KAAuB,EAAM,aAAa,MAAoB;EAClE,GAAsB,MAAS;GAC7B,IAAM,IAAO,IAAI,IAAI,CAAI;GAMzB,OALI,EAAK,IAAI,CAAO,IAClB,EAAK,OAAO,CAAO,IAEnB,EAAK,IAAI,CAAO,GAEX;EACT,CAAC;CACH,GAAG,CAAC,CAAC,GAEC,KAA6B,EAAM,aAAa,MAAoB;EACxE,IAA4B,MAAS;GACnC,IAAM,IAAO,IAAI,IAAI,CAAI;GAMzB,OALI,EAAK,IAAI,CAAO,IAClB,EAAK,OAAO,CAAO,IAEnB,EAAK,IAAI,CAAO,GAEX;EACT,CAAC;CACH,GAAG,CAAC,CAAC;CAEL,EAAM,gBAAgB;EACpB,AAAI,MAAiB,cACnB,kBAAqB,IAAI,IAAI,CAAC,GAC9B,mBAA2B,IAAI,IAAI,CAAC;CAExC,GAAG,CAAC,CAAY,CAAC;CAEjB,IAAM,KAAqB,EAAM,OAAuB,IAAI,GAEtD,CAAC,GAAwB,MAA6B,EAAM,SAEhE,IAAI,GACA,CAAC,IAA2B,KAChC,EAAM,SAAwB,IAAI,GAC9B,CAAC,GAAmB,MAAwB,EAAM,SAEtD,IAAI,GAEA,EACJ,UACA,eACA,cACA,gBACA,gBACA,uBACA,sBACE,GAA0B,GAGxB,EAAE,MAAM,OAAe,GAAc,GAErC,IAAgB,EAAM,cAAc;EACxC,IAAM,IAAM,IAAM,MAAM,SAAS,MAAS,EAAK,KAAK,KAAK,CAAC,GAMpD,oBAAO,IAAI,IAAY;EAC7B,OAAO,EAAI,QAAQ,MACb,EAAK,IAAI,EAAa,EAAE,IACnB,MAET,EAAK,IAAI,EAAa,EAAE,GACjB,GACR;CACH,GAAG,CAAC,EAAI,CAAC,GAIH,IAAuB,EAAM,cAC3B,GAA4B,CAAa,GAC/C,CAAC,CAAa,CAChB,GAEM,IAAkC,EAAM,cAE1C,GACE,GACA,GACA,GACA,CACF,GACF;EACE;EACA;EACA;EACA;CACF,CACF,GAEM,KAAsB,EAAM,cAC1B,GAA2B,GAAW,CAAa,GACzD,CAAC,GAAe,CAAS,CAC3B;CAiBA,AAfA,EAAM,gBAAgB;EACf,KAGL,GACE,EAAc,IACd,EAAc,KAAK,MAAiB,EAAa,EAAE,CACrD;CACF,GAAG;EACD,EAAc;EACd;EACA;EACA;CACF,CAAC,GAED,EAAM,gBAAgB;EACpB,AAAI,EAAU,WAAW,KACvB,GAAyB,EAAK;CAElC,GAAG,CAAC,EAAU,MAAM,CAAC;CAErB,IAAM,KAAsB,EAAM,cAAc;EAG9C,IAAM,IACJ,MAAgB,aACZ,EAAgC,QAAQ,MACtC,EAAkB,EAAE,gBAAgB,CACtC,IACA;EAQN,OAJI,IACK,IAGF,GAA6B,GAAe,CAAS;CAC9D,GAAG;EAAC;EAAiC;EAAS;EAAW;CAAW,CAAC,GAE/D,EAAE,QAAQ,GAAc,OAAO,MAAgB,EAAM,cACnD,GAAkB,EAAmB,GAC3C,CAAC,EAAmB,CACtB,GAQM,KAA6B,EAAM,cAIhC,GAHS,IACZ,CAAC,GAAG,GAAc,GAAG,CAAW,IAChC,GACqC,CAAgB,GACxD;EAAC;EAAc;EAAa;EAAwB;CAAgB,CAAC,GAElE,KAAc,EAAM,eACjB;EACL,gBAAgB,EAAE,EAAQ,+BAA+B;EACzD,iBAAiB,EAAE,EAAQ,gCAAgC;CAC7D,IACA,CAAC,CAAC,CACJ,GAEM,IAAqB,EAAM,cAC3B,KAAW,MAAiB,YACvB,OASF,GACL,CAJA,GAAG,GACH,GAAI,IAAyB,IAAc,CAAC,CAG5C,GACA,EAAc,MACd,GACA,EACF,GACC;EACD,EAAc;EACd;EACA;EACA;EACA;EACA;EACA;EACA;CACF,CAAC,GAEK,IAA4B,EAAM,cACjC,IAGE,GAAsB,GAAoB,CAAgB,IAFxD,MAGR,CAAC,GAAoB,CAAgB,CAAC,GAEnC,KAAuB,EAAM,cAC3B,GAAoB,KAAK,MAAU,EAAM,EAAE,KAAK,CAAC,GACvD,CAAC,CAAkB,CACrB,GAEM,KAA8B,EAAM,cAEtC,GACE,EAAa,QAAQ,MACnB,EAAkB,EAAa,gBAAgB,CACjD,GACA,CACF,GACF,CAAC,GAAkB,CAAY,CACjC,GAEM,KAAmB,GAA2B,QAE9C,KAAsB,EAAM,cAC3B,IAGE,EAA0B,QAC9B,GAAG,MAAM,IAAI,EAAE,cAAc,QAC9B,CACF,IALS,GAMR,CAAC,CAAyB,CAAC,GAExB,IACJ,MAAiB,aAAa,CAAC,IAC3B,OAAwB,IACxB,OAAqB,GAMrB,KACJ,KACA,MAAyB,SACzB,EAAc,SAAS,KACvB,EAAgC,WAAW,GAIvC,IACJ,MAAiB,aAAa,CAAC,IAC3B,KACA,IAqBA,CAAC,GAAe,KAAoB,EAAM,SAAwB,IAAI,GACtE,KAAkB,EAAM,OAAO,CAAY;CACjD,GAAgB,UAAU;CAE1B,IAAM,KAAkB,EAAM,kBAAkB;EAC9C,AAAI,KACF,EAAiB,GAAgB,OAAO;CAE5C,GAAG,CAAC,CAAW,CAAC;CAEhB,EAAM,gBAAgB;EAChB,UAAkB,MAItB;OAAI,IAAe,GAAe;IAChC,EAAiB,IAAI;IACrB;GACF;GAEA,IAAI,CAAC,GAAa;IAChB,EAAiB,IAAI;IACrB;GACF;GAGI,MAAc,KAGlB,GAAc;EAXd;CAYF,GAAG;EACD;EACA;EACA;EACA;EACA;EACA;CACF,CAAC;CAED,IAAM,KAAgB,MAAkB,QAAQ,GAE1C,EAAE,QAAQ,IAAoB,aAAa,OAC/C,GAAsB,GAClB,EAAE,QAAQ,OAAsB,GAA4B,GAC5D,EAAE,QAAQ,OAAuB,GAAsB,GAMvD,KAAc,EAAY,SAAS,KAAK,CAAC,GAOzC,KACJ,CAAC,CAAC,KACF,CAAC,MACD,CAAC,MACA,CAAC,KAA0B,KAExB,EAAE,QAAQ,OAAuB,GAAsB,GACvD,IAA6B,GAA0B,GAEvD,KAAkB,EAAM,aAC3B,MAAoC;EAC/B,KACJ,GACE;GACE,YAAY,EAAO;GACnB,YAAY,EAAO;GACnB,YAAY;EACd,GACA,EACE,YAAY,MAAS;GACnB,EAAS,kBAAkB,EAAK,iBAAiB;EACnD,EACF,CACF;CACF,GACA;EAAC;EAAoB;EAA4B;CAAQ,CAC3D,GAEM,KAAsB,EAAM,aAC/B,GAAwB,MAAkB;EAGzC,AAFA,EAA6B,EAAI,GACjC,GAA0B,CAAc,GACxC,EAA6B,CAAK;CACpC,GACA,CAAC,CACH,GAEM,KAAyB,EAAM,aAAa,MAA2B;EAE3E,AADA,EAA2B,EAAI,GAC/B,GAA0B,CAAc;CAC1C,GAAG,CAAC,CAAC,GAEC,KAAgC,EAAM,aACzC,GAAwB,MAAqB;EAC5C,GACE;GAAE;GAAgB;EAAS,GAC3B,EACE,iBAAiB;GACf,GAAoB,EAAE,EAAQ,0BAA0B,CAAC;EAC3D,EACF,CACF;CACF,GACA,CAAC,GAAG,EAAkB,CACxB,GAEM,WAA4B;EAChC,AAAI,KACF,GACE,EAAE,gBAAgB,EAAuB,GACzC,EACE,iBAAiB;GACf,AAAI,MAA2B,KAC7B,EAAS,gBAAgB;EAE7B,EACF,CACF;CAEJ,GAEM,WAA0B;EAC9B,AAAI,KACF,GAAkB,EAChB,gBAAgB,EAClB,CAAC;CAEL,GAEM,KAAyB,YAAY;EACzC,IAAM,IAAc,EAAc,KAAK,MAAM,EAAE,EAAE,GAC3C,IAAU,MAAM,QAAQ,WAC5B,EAAY,KAAK,MACf,GAAwB,EAAE,kBAAe,CAAC,CAC5C,CACF,GAEM,IAAa,EAAQ,SAAS,GAAQ,MAC1C,EAAO,WAAW,cAAc,CAAC,EAAY,EAAM,IAAI,CAAC,CAC1D,GACM,IAAc,EAAQ,SAAS,EAAW;EAShD,AANE,MAA0B,QAC1B,EAAW,SAAS,CAAqB,KAEzC,EAAS,gBAAgB,GAGvB,IAAc,KAChB,GACE,GAAG,EAAY,eAAe,MAAgB,IAAI,KAAK,IAAI,uBAC7D;CAEJ,GAEM,IAAyB,EAAM,aAEjC,GACA,MACG;EACH,IAAM,IAAW,EAAU,SAAS,EAAa,EAAE;EAgCnD,OA/BI,IAEA,kBAAC,IAAD;GAEE,gBAAgB,EAAa;GAC7B,OAAO,EAAa,SAAS;GAC7B,oBAAoB;IAClB,qBAAqB,EAAa;IAClC,iBAAiB,EAAa;IAC9B,cAAc,EAAa;GAC7B;GACA,iBAAiB,EAAa;GAC9B,eAAe,EAAa;GAC5B,eAAe,EAAa;GAC5B,WAAW,EAAa;GACxB,qBACE,EAAa,sBACb,EAAa,WAAW;GAE1B,UAAU,EAAa,OAAO;GACrB;GACT,wBAAwB;GACxB,UAAU,EAAa;GACN;GACjB,WAAW,EAAa;GACxB,WAAW,EAAa;GACxB,MAAM,EAAa;GACnB,UAAU;EACX,GAzBM,EAAa,EAyBnB,IAIH,kBAAC,IAAD;GAEE,WAAU;GACV,OAAO;GACP,YAAY;GACZ,YACE,CAAC,KAAqB,MAAsB,EAAa;GAE3D,kBAAkB;GAClB,WAAU;GACV,SACE,kBAAC,IAAD;IACE,OAAO,EAAa,SAAS;IAC7B,iBAAiB,EAAa;IAC9B,eAAe,EAAa;IAC5B,oBAAoB;KAClB,qBAAqB,EAAa;KAClC,iBAAiB,EAAa;KAC9B,cAAc,EAAa;IAC7B;IACA,qBACE,EAAa,sBACb,EAAa,WAAW;IAE1B,UAAU,EAAa;IACvB,WAAW,EAAa;IACxB,WAAW,EAAa;IACxB,WAAW,EAAa;IACxB,MAAM,EAAa;GACpB,CAAA;aAGH,kBAAC,IAAD;IACE,IAAI,GAAkB,kBAAkB,EAAa,IAAI;IACzD,SAAS;IACT,WAAW,EACT,sCACA,MAAsB,EAAa,MACjC,iCACD,EAAa,OAAO,KACnB,MAAsB,EAAa,OACnC,wBACJ;cAEA,kBAAC,IAAD;KACE,gBACE,GAAoB,EAAa,IAAI,EAAa,SAAS,EAAE;KAE/D,cAAc,GAAuB,EAAa,EAAE;KACpD,gBAAgB,MACd,GAA8B,EAAa,IAAI,CAAK;KAEtD,OAAO,EAAa,SAAS;KAC7B,oBAAoB;MAClB,qBAAqB,EAAa;MAClC,iBAAiB,EAAa;MAC9B,cAAc,EAAa;KAC7B;KACA,eAAe,EAAa;KAC5B,WAAW,EAAa;KACxB,iBAAiB,EAAa;KAC9B,eAAe,EAAa;KAC5B,gBAAgB,EAAa;KAC7B,iBAAiB,MAAsB,EAAa;KACpD,sBAAsB,MACpB,GAAqB,IAAS,EAAa,KAAK,IAAI;KAEtD,UAAU,EAAa,OAAO;KAC9B,qBACE,EAAa,sBACb,EAAa,WAAW;KAE1B,wBAAwB;KACxB,UAAU,EAAa;KACN;KACjB,WAAW,EAAa;KACxB,WAAW,EAAa;KACxB,MAAM,EAAa;KACnB,UAAU;KACA;KACV,mBAAmB,GAAU,EAAc,IAAI,EAAa,EAAE;KAC9D,mBAAmB,KAAY,CAAC,GAAS;IAC1C,CAAA;GACa,CAAA;EACT,GAnFF,EAAa,EAmFX;CAEb,GACA;EACE,EAAc;EACd;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF,CACF,GASM,IAAsB,MAAa,CAAC,GACpC,IACJ,CAAC,KAAW,CAAC,KAAuB,GAAoB,SAAS,GAC7D,KACJ,KACA,CAAC,MACD,CAAC,KACD,KACA,CAAC,KACD,CAAC,IAAY;CAIf,OACE,kBAAC,OAAD;EACO;EACL,eAAY;EACZ,WAAU;YAHZ;GAKG,CAR2B,KAS1B,kBAAC,OAAD;IACE,WAAW,EAGT,kEACA,KAAiB,8BAA8B,oBACjD;cAEA,kBAAC,OAAD;KACE,eAAY;KACZ,WAAU;eAFZ,CAIE,kBAAC,QAAD;MAAM,WAAU;gBACb,EAAE,EAAQ,qBAAqB;KAC5B,CAAA,GACN,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,IAAD,EACE,aAAa,EAAc,KAC5B,CAAA,GACD,kBAAC,IAAD;OACkB;OACG;OACnB,SAAS;OACT,aAAa,EAAc;OACb;OACG;OACC;OACG;OACR;OACG;OACM;OACG;OACA;OACzB,wBAAwB;OACF;OACE;OACM;OACN;OACM;OACb;OACM;OACL;OACM;OACL;OACM;OACzB,yBAAyB,EAAc;OACvC,0BAA0B,EAA2B,EAAI;MAC1D,CAAA,CACE;OACF;;GACF,CAAA;GAGP,kBAAC,OAAD;IACE,KAAK;IACL,eAAY;IACZ,WAAW,MAAU;KACnB,GAAkB,EAAM,cAAc,YAAY,CAAC;IACrD;IACA,WAAW,EACT,6GACA,CAAC,KAAW,gCACd;cATF;KAWG,KAAuB,kBAAC,IAAD,EAAmC,WAAU,CAAA;KAEpE,CAAC,KAAW,MACX,kBAAC,OAAD;MACE,eAAY;MACZ,WAAU;gBAEV,kBAAC,KAAD;OAAG,WAAU;iBACV,EACC,KACI,EAAQ,2CACR,EAAQ,6BACd;MACC,CAAA;KACA,CAAA;KAGN,IACC,kBAAC,IAAD;MACuB;MACrB,mBAAmB;MACnB,+BACE,IAA0B,MAAY,CAAC,CAAO;MAEhD,sBAAsB;MACtB,aAAa,CAAC,KAAW,MAAiB;MAC1C,yBAAyB,MACvB,EAAuB,GAAc,EAAE,iBAAiB,GAAK,CAAC;KAEjE,CAAA,IACC;KAIH,CAAC,KACA,IAAY,KAAK,MACf,kBAAC,IAAD;MAEE,IAAI,GAAkB,uBAAuB,EAAK,IAAI;MACtD,SAAS;MACT,WAAU;gBAEV,kBAAC,IAAD,EAAqB,QAAO,CAAA;KACd,GANT,EAAK,EAMI,CACjB;KAEF,CAAC,KAAuB,IACrB,GAA4B,KAAK,MAC/B,EAAuB,CAAY,CACrC,IACA;KAEH,CAAC,KACF,CAAC,KACD,MAAiB,aACjB,KACA,EAA0B,SAAS,IACjC,kBAAC,IAAD;MACE,QAAQ;MACR,UAAU;MACQ;MACG;MACF;MACM;MACzB,wBAAwB;MACxB,8BAA8B;MACF;MAC5B,sBAAsB;MACtB,mBAAmB;MACnB,yBAAyB,MACvB,EAAuB,CAAY;KAEtC,CAAA,IACC;KAEH,CAAC,KACF,CAAC,KACD,MAAiB,kBACf,kBAAC,OAAD;MAAK,WAAU;gBACZ,GAA2B,KAAK,MAC/B,EAAuB,CAAY,CACrC;KACG,CAAA,IACH;KAMH,OACE,KACC,kBAAC,OAAD;MAAK,WAAU;gBACb,kBAAC,IAAD,EAAmC,WAAU,CAAA;KAC1C,CAAA,IAEL,kBAAC,OAAD;MAAK,WAAU;gBACb,kBAAC,UAAD;OACE,MAAK;OACL,eAAY;OACZ,SAAS;OACT,WAAU;iBAET,EAAE,EAAQ,sBAAsB;MAC3B,CAAA;KACL,CAAA;IAEN;;GAEJ,MACC,kBAAC,GAAD;IACE,iBAAiB;KAGf,AAFA,GAAoB,GACpB,EAA6B,EAAK,GAClC,EAA6B,IAAI;IACnC;IACA,gBAAgB;KAEd,AADA,EAA6B,EAAK,GAClC,EAA6B,IAAI;IACnC;IACA,mBAAmB,MAA6B,KAAA;GACjD,CAAA;GAGF,MACC,kBAAC,GAAD;IACE,OAAO,EAAE,EAAQ,qCAAqC;IACtD,aAAa,EAAE,EAAQ,sCAAsC,EAC3D,OAAO,EAAc,OACvB,CAAC;IACD,WAAW,YAAY;KAErB,AADA,MAAM,GAAuB,GAC7B,EAA2B,EAAK;IAClC;IACA,gBAAgB,EAA2B,EAAK;GACjD,CAAA;GAGF,MACC,kBAAC,IAAD;IACE,iBAAiB;KAEf,AADA,GAAkB,GAClB,EAA2B,EAAK;IAClC;IACA,gBAAgB,EAA2B,EAAK;GACjD,CAAA;GAGF,MACC,kBAAC,IAAD;IACE,iBAAiB;KACf,IAAU;IACZ;IACA,eAAe,GAAuC,EAAK;IAC3D,gBAAgB,GAAuC,EAAK;GAC7D,CAAA;EAEA;;AAET"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
require("../../../_virtual/_rolldown/runtime.cjs");const e=require("../../../node_modules/react-i18next/dist/es/useTranslation.cjs"),t=require("../../../i18n/declaration.cjs"),n=require("../../../node_modules/lucide-react/dist/esm/icons/
|
|
1
|
+
require("../../../_virtual/_rolldown/runtime.cjs");const e=require("../../../node_modules/react-i18next/dist/es/useTranslation.cjs"),t=require("../../../i18n/declaration.cjs"),n=require("../../../node_modules/lucide-react/dist/esm/icons/check.cjs"),r=require("../../../node_modules/lucide-react/dist/esm/icons/circle-arrow-up.cjs"),i=require("../../../node_modules/lucide-react/dist/esm/icons/circle-check.cjs"),a=require("../../../node_modules/lucide-react/dist/esm/icons/copy.cjs"),o=require("../../../node_modules/lucide-react/dist/esm/icons/external-link.cjs"),s=require("../../../node_modules/lucide-react/dist/esm/icons/refresh-cw.cjs"),c=require("../../../utils/utils.cjs"),l=require("../../../api/agent-server-config.cjs"),u=require("../../../api/agent-server-compatibility.cjs"),d=require("../../../api/client-source.cjs"),f=require("../../shared/modals/modal-backdrop.cjs"),p=require("../../shared/modals/modal-body.cjs"),m=require("../../shared/modals/confirmation-modals/base-modal.cjs"),h=require("../../shared/modals/modal-close-button.cjs"),g=require("./brand-button.cjs"),_=require("../../../api/agent-canvas-updates.cjs"),v=require("../../../hooks/query/use-latest-agent-canvas-version.cjs"),y=require("../../../icons/docker.cjs"),b=require("../../../icons/npm.cjs");let x=require("react"),S=require("react/jsx-runtime");var C=2e3,w=[`npm`,`docker`];function T(e){return e===`npm`?t.I18nKey.SETTINGS$VERSION_NPM_RECOMMENDED:t.I18nKey.SETTINGS$VERSION_DOCKER}function E({tab:e}){return e===`npm`?(0,S.jsx)(b.default,{className:`size-4 shrink-0`,"aria-hidden":!0}):(0,S.jsx)(y.default,{className:`size-5 shrink-0`,"aria-hidden":!0})}function D(){let{t:r}=e.useTranslation(`openhands`),[i,o]=(0,x.useState)(`npm`),[s,l]=(0,x.useState)(!1),u=(0,x.useRef)(null),d=i===`npm`?_.AGENT_CANVAS_UPDATE_COMMANDS.npm:_.AGENT_CANVAS_UPDATE_COMMANDS.docker;(0,x.useEffect)(()=>()=>{u.current!==null&&window.clearTimeout(u.current)},[]),(0,x.useEffect)(()=>{l(!1),u.current!==null&&(window.clearTimeout(u.current),u.current=null)},[i]);let f=(0,x.useCallback)(()=>{navigator.clipboard?.writeText(d),l(!0),u.current!==null&&window.clearTimeout(u.current),u.current=window.setTimeout(()=>{l(!1),u.current=null},C)},[d]);return(0,S.jsxs)(`div`,{className:`w-full`,children:[(0,S.jsx)(`div`,{className:`flex justify-center`,children:w.map(e=>(0,S.jsxs)(`button`,{type:`button`,onClick:()=>o(e),className:c.cn(`inline-flex items-center gap-1.5 px-3 pb-2 text-sm font-medium`,i===e?`border-b border-white text-white`:`text-[var(--oh-muted)] hover:text-white`),children:[(0,S.jsx)(E,{tab:e}),r(T(e))]},e))}),(0,S.jsxs)(`div`,{className:`flex items-center gap-3 rounded-lg border border-[var(--oh-border)] bg-[var(--oh-surface-deep)] px-4 py-3`,children:[(0,S.jsx)(`code`,{"data-testid":i===`npm`?`agent-canvas-update-command-npm`:`agent-canvas-update-command-docker`,className:`min-w-0 flex-1 overflow-visible whitespace-nowrap font-mono text-sm text-white`,children:d}),(0,S.jsx)(`button`,{type:`button`,"data-testid":`copy-to-clipboard`,onClick:f,"aria-label":r(s?t.I18nKey.BUTTON$COPIED:t.I18nKey.SETTINGS$VERSION_COPY_COMMAND),disabled:s,className:`shrink-0 text-[var(--oh-muted)] hover:text-white disabled:hover:text-[var(--oh-muted)]`,children:s?(0,S.jsx)(n.Check,{className:`size-4 text-[var(--oh-status-success)]`,"aria-hidden":!0}):(0,S.jsx)(a.Copy,{className:`size-4`,"aria-hidden":!0})})]})]})}function O({onClose:n,latestVersion:a,isPending:l,isFetching:u,updateAvailable:v,upToDate:y,onCheckForUpdates:b}){let{t:x}=e.useTranslation(`openhands`);return(0,S.jsx)(f.ModalBackdrop,{onClose:n,children:(0,S.jsxs)(p.ModalBody,{width:`md`,className:`relative flex max-h-[80vh] flex-col items-start overflow-auto border border-[var(--oh-border)]`,testID:`agent-canvas-update-modal`,children:[(0,S.jsx)(h.ModalCloseButton,{onClose:n,testId:`close-agent-canvas-update-modal`}),(0,S.jsxs)(`div`,{className:`flex w-full flex-col gap-2 pr-10`,children:[(0,S.jsx)(m.BaseModalTitle,{title:x(t.I18nKey.SETTINGS$APP_UPDATE_CARD_TITLE)}),(0,S.jsxs)(`div`,{className:`flex items-center gap-1.5 text-xs`,children:[(0,S.jsx)(`span`,{className:`text-[var(--oh-text-dim)]`,children:x(t.I18nKey.SETTINGS$APP_UPDATE_VERSION_LABEL)}),(0,S.jsx)(`span`,{className:`text-white`,children:d.AGENT_CANVAS_CLIENT_VERSION})]})]}),(0,S.jsxs)(`div`,{className:`flex w-full flex-col gap-3`,children:[(0,S.jsx)(g.BrandButton,{testId:`agent-canvas-update-check-button`,type:`button`,variant:`secondary`,className:`w-full`,isDisabled:u,"aria-busy":u,onClick:b,startContent:(0,S.jsx)(s.RefreshCw,{className:c.cn(`size-4`,u&&`animate-spin`),"aria-hidden":!0}),children:x(t.I18nKey.SETTINGS$APP_UPDATE_CHECK_BUTTON)}),(0,S.jsx)(`div`,{"data-testid":`agent-canvas-update-status`,className:`w-full`,children:l||u?(0,S.jsx)(`span`,{className:`text-xs text-[var(--oh-muted)]`,children:x(t.I18nKey.SETTINGS$APP_UPDATE_CHECKING)}):v?(0,S.jsxs)(`div`,{className:`flex items-start gap-2 rounded-lg border border-[#3B82F6]/30 bg-[#1E3A5F] px-3 py-2 text-xs text-[#3B82F6]`,children:[(0,S.jsx)(r.CircleArrowUp,{className:`size-4 shrink-0`,"aria-hidden":!0}),(0,S.jsxs)(`span`,{children:[x(t.I18nKey.SETTINGS$APP_UPDATE_AVAILABLE_MESSAGE,{version:a}),` `,(0,S.jsxs)(`a`,{"data-testid":`agent-canvas-update-release-notes`,href:_.AGENT_CANVAS_RELEASE_NOTES_URL,target:`_blank`,rel:`noopener noreferrer`,className:`inline-flex items-center gap-1.5 whitespace-nowrap font-medium text-white hover:text-[var(--oh-text)]`,children:[x(t.I18nKey.SETTINGS$VERSION_RELEASE_NOTES),(0,S.jsx)(o.ExternalLink,{className:`size-3.5 shrink-0`,"aria-hidden":!0})]})]})]}):y?(0,S.jsxs)(`div`,{className:`flex items-start gap-2 rounded-lg border border-[var(--oh-status-success)]/30 bg-[var(--oh-status-success)]/10 px-3 py-2 text-xs text-[var(--oh-status-success)]`,children:[(0,S.jsx)(i.CircleCheck,{className:`size-4 shrink-0`,"aria-hidden":!0}),(0,S.jsxs)(`span`,{children:[x(t.I18nKey.SETTINGS$APP_UPDATE_LATEST_MESSAGE),` `,(0,S.jsxs)(`a`,{"data-testid":`agent-canvas-update-release-notes`,href:_.AGENT_CANVAS_RELEASE_NOTES_URL,target:`_blank`,rel:`noopener noreferrer`,className:`inline-flex items-center gap-1.5 whitespace-nowrap font-medium text-white hover:text-[var(--oh-text)]`,children:[x(t.I18nKey.SETTINGS$VERSION_RELEASE_NOTES),(0,S.jsx)(o.ExternalLink,{className:`size-3.5 shrink-0`,"aria-hidden":!0})]})]})]}):(0,S.jsx)(`span`,{className:`text-xs text-[var(--oh-muted)]`,children:x(t.I18nKey.SETTINGS$APP_UPDATE_CHECK_FAILED)})}),v?(0,S.jsxs)(`div`,{className:`flex w-full flex-col border-t border-[var(--oh-border)] pt-3`,children:[(0,S.jsx)(`span`,{className:`text-sm font-medium text-white`,children:x(t.I18nKey.SETTINGS$APP_UPDATE_HOW_TO_UPDATE)}),(0,S.jsx)(`span`,{className:`mt-1 mb-4 text-sm text-[var(--oh-text-dim)]`,children:x(t.I18nKey.SETTINGS$APP_UPDATE_RUN_COMMANDS)}),(0,S.jsx)(D,{})]}):null]})]})})}function k({hideWhenUpToDate:n=!1}){let{t:a}=e.useTranslation(`openhands`),[o,s]=(0,x.useState)(!1),f=l.getLockedCloudHost()!==null,p=v.useLatestAgentCanvasVersion({enabled:!f});if(f)return null;let m=p.data,h=m===void 0?null:u.compareAgentServerVersions(m,d.AGENT_CANVAS_CLIENT_VERSION),g=h===1,_=h!==null&&h<=0;return n&&!g?null:(0,S.jsxs)(S.Fragment,{children:[(0,S.jsx)(`section`,{"data-testid":`agent-canvas-update-card`,children:(0,S.jsxs)(`button`,{type:`button`,onClick:()=>s(!0),"aria-haspopup":`dialog`,"data-testid":`agent-canvas-update-toggle`,className:`flex w-full cursor-pointer flex-col gap-1 rounded-md border border-[var(--oh-border)] bg-base-secondary px-3 py-2 text-left hover:bg-[var(--oh-surface-raised)]`,children:[(0,S.jsxs)(`span`,{className:`flex w-full items-center gap-2`,children:[(0,S.jsx)(`span`,{className:`flex-1 truncate text-sm font-semibold leading-5 text-white`,children:a(t.I18nKey.SETTINGS$APP_UPDATE_CARD_TITLE)}),h!==null&&(0,S.jsxs)(`span`,{"data-testid":`agent-canvas-update-badge`,className:c.cn(`inline-flex shrink-0 items-center gap-1 text-[10px] font-medium leading-none`,g?`rounded-full border border-transparent bg-[#1E3A5F] px-1.5 py-0.5 text-[#3B82F6]`:`text-success`),children:[g?(0,S.jsx)(r.CircleArrowUp,{className:`size-3 shrink-0`,"aria-hidden":!0}):(0,S.jsx)(i.CircleCheck,{className:`size-3 shrink-0`,"aria-hidden":!0}),a(g?t.I18nKey.SETTINGS$APP_UPDATE_BADGE_UPDATE_AVAILABLE:t.I18nKey.SETTINGS$APP_UPDATE_BADGE_UP_TO_DATE)]})]}),(0,S.jsxs)(`span`,{className:`flex items-center gap-1.5 text-xs leading-5 text-[var(--oh-muted)]`,children:[(0,S.jsx)(`span`,{children:a(t.I18nKey.SETTINGS$APP_UPDATE_VERSION_LABEL)}),(0,S.jsx)(`span`,{className:`text-white`,children:d.AGENT_CANVAS_CLIENT_VERSION})]})]})}),o&&(0,S.jsx)(O,{onClose:()=>s(!1),latestVersion:m,isPending:p.isPending,isFetching:p.isFetching,updateAvailable:g,upToDate:_,onCheckForUpdates:()=>p.refetch()})]})}exports.AgentCanvasUpdateCard=k;
|
|
2
2
|
//# sourceMappingURL=agent-canvas-update-card.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-canvas-update-card.cjs","names":[],"sources":["../../../../src/components/features/settings/agent-canvas-update-card.tsx"],"sourcesContent":["import { useId, useState } from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport {\n ChevronDown,\n ChevronUp,\n CircleCheck,\n ExternalLink,\n Info,\n RefreshCw,\n} from \"lucide-react\";\nimport {\n AGENT_CANVAS_RELEASE_NOTES_URL,\n AGENT_CANVAS_UPDATE_COMMANDS,\n} from \"#/api/agent-canvas-updates\";\nimport { compareAgentServerVersions } from \"#/api/agent-server-compatibility\";\nimport { getLockedCloudHost } from \"#/api/agent-server-config\";\nimport { AGENT_CANVAS_CLIENT_VERSION } from \"#/api/client-source\";\nimport { BrandButton } from \"#/components/features/settings/brand-button\";\nimport { CopyToClipboardButton } from \"#/components/shared/buttons/copy-to-clipboard-button\";\nimport { useLatestAgentCanvasVersion } from \"#/hooks/query/use-latest-agent-canvas-version\";\nimport { I18nKey } from \"#/i18n/declaration\";\nimport { cn } from \"#/utils/utils\";\n\nfunction UpdateCommandRow({\n command,\n testId,\n}: {\n command: string;\n testId: string;\n}) {\n const [copied, setCopied] = useState(false);\n\n const handleCopy = () => {\n navigator.clipboard.writeText(command);\n setCopied(true);\n setTimeout(() => setCopied(false), 2000);\n };\n\n return (\n <div className=\"flex items-center gap-2 rounded-lg bg-[var(--oh-surface-deep)] px-2 py-1.5\">\n <code\n data-testid={testId}\n className=\"min-w-0 flex-1 select-all break-all font-mono text-[11px] leading-4 text-white\"\n >\n {command}\n </code>\n <CopyToClipboardButton\n isHidden={false}\n isDisabled={false}\n onClick={handleCopy}\n mode={copied ? \"copied\" : \"copy\"}\n />\n </div>\n );\n}\n\n/**\n * Information-only version/update card for the settings navigation surfaces.\n * Never blocks or toasts; check failures degrade to a quiet inline message.\n * Hidden in locked-to-Cloud deployments — the hosted canvas has no npm/docker\n * install to update.\n */\nexport function AgentCanvasUpdateCard() {\n const { t } = useTranslation(\"openhands\");\n const [expanded, setExpanded] = useState(false);\n // The card is mounted on multiple settings surfaces at once; ids must not\n // collide across instances.\n const detailsId = useId();\n const isLockedToCloud = getLockedCloudHost() !== null;\n const latestVersionQuery = useLatestAgentCanvasVersion({\n enabled: !isLockedToCloud,\n });\n\n if (isLockedToCloud) return null;\n\n const latestVersion = latestVersionQuery.data;\n const comparison =\n latestVersion !== undefined\n ? compareAgentServerVersions(latestVersion, AGENT_CANVAS_CLIENT_VERSION)\n : null;\n const updateAvailable = comparison === 1;\n // 0 or -1: running the npm latest or newer (local dev build).\n const upToDate = comparison !== null && comparison <= 0;\n const ChevronIcon = expanded ? ChevronUp : ChevronDown;\n\n return (\n <section\n data-testid=\"agent-canvas-update-card\"\n className=\"flex flex-col gap-2 rounded-xl border border-[var(--oh-border)] bg-[var(--oh-surface-raised)] p-3\"\n >\n <button\n type=\"button\"\n onClick={() => setExpanded((prev) => !prev)}\n aria-expanded={expanded}\n aria-controls={detailsId}\n data-testid=\"agent-canvas-update-toggle\"\n className=\"flex w-full cursor-pointer items-center gap-2 text-left\"\n >\n <span className=\"flex-1 truncate text-xs font-medium text-white\">\n {t(I18nKey.SETTINGS$APP_UPDATE_CARD_TITLE)}\n </span>\n {comparison !== null && (\n <span\n data-testid=\"agent-canvas-update-badge\"\n className={cn(\n \"inline-flex shrink-0 items-center rounded-full border border-[var(--oh-border)] bg-[var(--oh-surface)] px-1.5 py-0.5 text-[10px] font-medium leading-none\",\n updateAvailable ? \"text-warning\" : \"text-success\",\n )}\n >\n {t(\n updateAvailable\n ? I18nKey.SETTINGS$APP_UPDATE_BADGE_UPDATE_AVAILABLE\n : I18nKey.SETTINGS$APP_UPDATE_BADGE_UP_TO_DATE,\n )}\n </span>\n )}\n <ChevronIcon\n className=\"size-4 shrink-0 text-[var(--oh-muted)]\"\n aria-hidden\n />\n </button>\n\n <div className=\"flex items-center justify-between text-xs\">\n <span className=\"text-[var(--oh-text-dim)]\">\n {t(I18nKey.SETTINGS$APP_UPDATE_VERSION_LABEL)}\n </span>\n <span className=\"font-mono text-white\">\n {AGENT_CANVAS_CLIENT_VERSION}\n </span>\n </div>\n\n {expanded && (\n <div id={detailsId} className=\"flex flex-col gap-3 pt-1\">\n <div\n data-testid=\"agent-canvas-update-status\"\n className=\"flex items-start gap-2 text-xs\"\n >\n {latestVersionQuery.isPending ? (\n <span className=\"text-[var(--oh-muted)]\">\n {t(I18nKey.SETTINGS$APP_UPDATE_CHECKING)}\n </span>\n ) : updateAvailable ? (\n <>\n <Info className=\"size-4 shrink-0 text-warning\" aria-hidden />\n <span className=\"text-warning\">\n {t(I18nKey.SETTINGS$APP_UPDATE_AVAILABLE_MESSAGE, {\n version: latestVersion,\n })}\n </span>\n </>\n ) : upToDate ? (\n <>\n <CircleCheck\n className=\"size-4 shrink-0 text-success\"\n aria-hidden\n />\n <span className=\"text-success\">\n {t(I18nKey.SETTINGS$APP_UPDATE_LATEST_MESSAGE)}\n </span>\n </>\n ) : (\n <span className=\"text-[var(--oh-muted)]\">\n {t(I18nKey.SETTINGS$APP_UPDATE_CHECK_FAILED)}\n </span>\n )}\n </div>\n\n <BrandButton\n testId=\"agent-canvas-update-check-button\"\n type=\"button\"\n variant=\"secondary\"\n className=\"w-full\"\n isDisabled={latestVersionQuery.isFetching}\n aria-busy={latestVersionQuery.isFetching}\n onClick={() => latestVersionQuery.refetch()}\n startContent={\n <RefreshCw\n className={cn(\n \"size-4\",\n latestVersionQuery.isFetching && \"animate-spin\",\n )}\n aria-hidden\n />\n }\n >\n {t(I18nKey.SETTINGS$APP_UPDATE_CHECK_BUTTON)}\n </BrandButton>\n\n <div className=\"flex flex-col gap-2 rounded-lg border border-[var(--oh-border-subtle)] p-3 text-xs\">\n <div className=\"flex items-center gap-1.5 text-white\">\n <Info className=\"size-4 shrink-0\" aria-hidden />\n <span className=\"font-medium\">\n {t(I18nKey.SETTINGS$APP_UPDATE_HOW_TO_UPDATE)}\n </span>\n </div>\n <span className=\"text-[var(--oh-text-dim)]\">\n {t(I18nKey.SETTINGS$APP_UPDATE_RUN_COMMANDS)}\n </span>\n <UpdateCommandRow\n command={AGENT_CANVAS_UPDATE_COMMANDS.npm}\n testId=\"agent-canvas-update-command-npm\"\n />\n <span className=\"text-center text-[var(--oh-text-dim)]\">\n {t(I18nKey.SETTINGS$APP_UPDATE_OR)}\n </span>\n <UpdateCommandRow\n command={AGENT_CANVAS_UPDATE_COMMANDS.docker}\n testId=\"agent-canvas-update-command-docker\"\n />\n </div>\n\n <a\n data-testid=\"agent-canvas-update-release-notes\"\n href={AGENT_CANVAS_RELEASE_NOTES_URL}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"inline-flex items-center gap-1 self-start text-xs text-[var(--oh-accent)] underline\"\n >\n {t(I18nKey.SETTINGS$APP_UPDATE_RELEASE_NOTES)}\n <ExternalLink size={12} aria-hidden />\n </a>\n </div>\n )}\n </section>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"agent-canvas-update-card.cjs","names":[],"sources":["../../../../src/components/features/settings/agent-canvas-update-card.tsx"],"sourcesContent":["import { useCallback, useEffect, useRef, useState } from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport {\n ArrowUpCircle,\n Check,\n CircleCheck,\n Copy,\n ExternalLink,\n RefreshCw,\n} from \"lucide-react\";\nimport {\n AGENT_CANVAS_RELEASE_NOTES_URL,\n AGENT_CANVAS_UPDATE_COMMANDS,\n} from \"#/api/agent-canvas-updates\";\nimport { compareAgentServerVersions } from \"#/api/agent-server-compatibility\";\nimport { getLockedCloudHost } from \"#/api/agent-server-config\";\nimport { AGENT_CANVAS_CLIENT_VERSION } from \"#/api/client-source\";\nimport { BrandButton } from \"#/components/features/settings/brand-button\";\nimport { BaseModalTitle } from \"#/components/shared/modals/confirmation-modals/base-modal\";\nimport { ModalBackdrop } from \"#/components/shared/modals/modal-backdrop\";\nimport { ModalBody } from \"#/components/shared/modals/modal-body\";\nimport { ModalCloseButton } from \"#/components/shared/modals/modal-close-button\";\nimport { useLatestAgentCanvasVersion } from \"#/hooks/query/use-latest-agent-canvas-version\";\nimport DockerIcon from \"#/icons/docker.svg?react\";\nimport NpmIcon from \"#/icons/npm.svg?react\";\nimport { I18nKey } from \"#/i18n/declaration\";\nimport { cn } from \"#/utils/utils\";\n\nconst COPY_FEEDBACK_MS = 2000;\n\ntype UpdateCommandTab = \"npm\" | \"docker\";\n\nconst UPDATE_COMMAND_TABS: UpdateCommandTab[] = [\"npm\", \"docker\"];\n\nfunction getUpdateCommandTabLabelKey(tab: UpdateCommandTab): I18nKey {\n return tab === \"npm\"\n ? I18nKey.SETTINGS$VERSION_NPM_RECOMMENDED\n : I18nKey.SETTINGS$VERSION_DOCKER;\n}\n\nfunction UpdateCommandTabIcon({ tab }: { tab: UpdateCommandTab }) {\n if (tab === \"npm\") {\n return <NpmIcon className=\"size-4 shrink-0\" aria-hidden />;\n }\n return <DockerIcon className=\"size-5 shrink-0\" aria-hidden />;\n}\n\nfunction UpdateCommandTabs() {\n const { t } = useTranslation(\"openhands\");\n const [selectedTab, setSelectedTab] = useState<UpdateCommandTab>(\"npm\");\n const [copied, setCopied] = useState(false);\n const copiedTimeoutRef = useRef<number | null>(null);\n const command =\n selectedTab === \"npm\"\n ? AGENT_CANVAS_UPDATE_COMMANDS.npm\n : AGENT_CANVAS_UPDATE_COMMANDS.docker;\n\n useEffect(\n () => () => {\n if (copiedTimeoutRef.current !== null) {\n window.clearTimeout(copiedTimeoutRef.current);\n }\n },\n [],\n );\n\n useEffect(() => {\n setCopied(false);\n if (copiedTimeoutRef.current !== null) {\n window.clearTimeout(copiedTimeoutRef.current);\n copiedTimeoutRef.current = null;\n }\n }, [selectedTab]);\n\n const copyCommand = useCallback(() => {\n void navigator.clipboard?.writeText(command);\n setCopied(true);\n if (copiedTimeoutRef.current !== null) {\n window.clearTimeout(copiedTimeoutRef.current);\n }\n copiedTimeoutRef.current = window.setTimeout(() => {\n setCopied(false);\n copiedTimeoutRef.current = null;\n }, COPY_FEEDBACK_MS);\n }, [command]);\n\n return (\n <div className=\"w-full\">\n <div className=\"flex justify-center\">\n {UPDATE_COMMAND_TABS.map((tab) => (\n <button\n key={tab}\n type=\"button\"\n onClick={() => setSelectedTab(tab)}\n className={cn(\n \"inline-flex items-center gap-1.5 px-3 pb-2 text-sm font-medium\",\n selectedTab === tab\n ? \"border-b border-white text-white\"\n : \"text-[var(--oh-muted)] hover:text-white\",\n )}\n >\n <UpdateCommandTabIcon tab={tab} />\n {t(getUpdateCommandTabLabelKey(tab))}\n </button>\n ))}\n </div>\n <div className=\"flex items-center gap-3 rounded-lg border border-[var(--oh-border)] bg-[var(--oh-surface-deep)] px-4 py-3\">\n <code\n data-testid={\n selectedTab === \"npm\"\n ? \"agent-canvas-update-command-npm\"\n : \"agent-canvas-update-command-docker\"\n }\n className=\"min-w-0 flex-1 overflow-visible whitespace-nowrap font-mono text-sm text-white\"\n >\n {command}\n </code>\n <button\n type=\"button\"\n data-testid=\"copy-to-clipboard\"\n onClick={copyCommand}\n aria-label={t(\n copied\n ? I18nKey.BUTTON$COPIED\n : I18nKey.SETTINGS$VERSION_COPY_COMMAND,\n )}\n disabled={copied}\n className=\"shrink-0 text-[var(--oh-muted)] hover:text-white disabled:hover:text-[var(--oh-muted)]\"\n >\n {copied ? (\n <Check\n className=\"size-4 text-[var(--oh-status-success)]\"\n aria-hidden\n />\n ) : (\n <Copy className=\"size-4\" aria-hidden />\n )}\n </button>\n </div>\n </div>\n );\n}\n\ninterface AgentCanvasUpdateModalProps {\n onClose: () => void;\n latestVersion: string | undefined;\n isPending: boolean;\n isFetching: boolean;\n updateAvailable: boolean;\n upToDate: boolean;\n onCheckForUpdates: () => void;\n}\n\nfunction AgentCanvasUpdateModal({\n onClose,\n latestVersion,\n isPending,\n isFetching,\n updateAvailable,\n upToDate,\n onCheckForUpdates,\n}: AgentCanvasUpdateModalProps) {\n const { t } = useTranslation(\"openhands\");\n\n return (\n <ModalBackdrop onClose={onClose}>\n <ModalBody\n width=\"md\"\n className=\"relative flex max-h-[80vh] flex-col items-start overflow-auto border border-[var(--oh-border)]\"\n testID=\"agent-canvas-update-modal\"\n >\n <ModalCloseButton\n onClose={onClose}\n testId=\"close-agent-canvas-update-modal\"\n />\n <div className=\"flex w-full flex-col gap-2 pr-10\">\n <BaseModalTitle title={t(I18nKey.SETTINGS$APP_UPDATE_CARD_TITLE)} />\n <div className=\"flex items-center gap-1.5 text-xs\">\n <span className=\"text-[var(--oh-text-dim)]\">\n {t(I18nKey.SETTINGS$APP_UPDATE_VERSION_LABEL)}\n </span>\n <span className=\"text-white\">{AGENT_CANVAS_CLIENT_VERSION}</span>\n </div>\n </div>\n\n <div className=\"flex w-full flex-col gap-3\">\n <BrandButton\n testId=\"agent-canvas-update-check-button\"\n type=\"button\"\n variant=\"secondary\"\n className=\"w-full\"\n isDisabled={isFetching}\n aria-busy={isFetching}\n onClick={onCheckForUpdates}\n startContent={\n <RefreshCw\n className={cn(\"size-4\", isFetching && \"animate-spin\")}\n aria-hidden\n />\n }\n >\n {t(I18nKey.SETTINGS$APP_UPDATE_CHECK_BUTTON)}\n </BrandButton>\n\n <div data-testid=\"agent-canvas-update-status\" className=\"w-full\">\n {isPending || isFetching ? (\n <span className=\"text-xs text-[var(--oh-muted)]\">\n {t(I18nKey.SETTINGS$APP_UPDATE_CHECKING)}\n </span>\n ) : updateAvailable ? (\n <div className=\"flex items-start gap-2 rounded-lg border border-[#3B82F6]/30 bg-[#1E3A5F] px-3 py-2 text-xs text-[#3B82F6]\">\n <ArrowUpCircle className=\"size-4 shrink-0\" aria-hidden />\n <span>\n {t(I18nKey.SETTINGS$APP_UPDATE_AVAILABLE_MESSAGE, {\n version: latestVersion,\n })}{\" \"}\n <a\n data-testid=\"agent-canvas-update-release-notes\"\n href={AGENT_CANVAS_RELEASE_NOTES_URL}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"inline-flex items-center gap-1.5 whitespace-nowrap font-medium text-white hover:text-[var(--oh-text)]\"\n >\n {t(I18nKey.SETTINGS$VERSION_RELEASE_NOTES)}\n <ExternalLink className=\"size-3.5 shrink-0\" aria-hidden />\n </a>\n </span>\n </div>\n ) : upToDate ? (\n <div className=\"flex items-start gap-2 rounded-lg border border-[var(--oh-status-success)]/30 bg-[var(--oh-status-success)]/10 px-3 py-2 text-xs text-[var(--oh-status-success)]\">\n <CircleCheck className=\"size-4 shrink-0\" aria-hidden />\n <span>\n {t(I18nKey.SETTINGS$APP_UPDATE_LATEST_MESSAGE)}{\" \"}\n <a\n data-testid=\"agent-canvas-update-release-notes\"\n href={AGENT_CANVAS_RELEASE_NOTES_URL}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"inline-flex items-center gap-1.5 whitespace-nowrap font-medium text-white hover:text-[var(--oh-text)]\"\n >\n {t(I18nKey.SETTINGS$VERSION_RELEASE_NOTES)}\n <ExternalLink className=\"size-3.5 shrink-0\" aria-hidden />\n </a>\n </span>\n </div>\n ) : (\n <span className=\"text-xs text-[var(--oh-muted)]\">\n {t(I18nKey.SETTINGS$APP_UPDATE_CHECK_FAILED)}\n </span>\n )}\n </div>\n\n {updateAvailable ? (\n <div className=\"flex w-full flex-col border-t border-[var(--oh-border)] pt-3\">\n <span className=\"text-sm font-medium text-white\">\n {t(I18nKey.SETTINGS$APP_UPDATE_HOW_TO_UPDATE)}\n </span>\n <span className=\"mt-1 mb-4 text-sm text-[var(--oh-text-dim)]\">\n {t(I18nKey.SETTINGS$APP_UPDATE_RUN_COMMANDS)}\n </span>\n <UpdateCommandTabs />\n </div>\n ) : null}\n </div>\n </ModalBody>\n </ModalBackdrop>\n );\n}\n\ninterface AgentCanvasUpdateCardProps {\n /** When true, omit the card unless an update is available (main sidebar). */\n hideWhenUpToDate?: boolean;\n}\n\n/**\n * Information-only version/update card for settings and the main sidebar.\n * Opens update details in a modal. Never blocks or toasts; check failures\n * degrade to a quiet inline message. Hidden in locked-to-Cloud deployments —\n * the hosted canvas has no npm/docker install to update.\n */\nexport function AgentCanvasUpdateCard({\n hideWhenUpToDate = false,\n}: AgentCanvasUpdateCardProps) {\n const { t } = useTranslation(\"openhands\");\n const [isModalOpen, setIsModalOpen] = useState(false);\n const isLockedToCloud = getLockedCloudHost() !== null;\n const latestVersionQuery = useLatestAgentCanvasVersion({\n enabled: !isLockedToCloud,\n });\n\n if (isLockedToCloud) return null;\n\n const latestVersion = latestVersionQuery.data;\n const comparison =\n latestVersion !== undefined\n ? compareAgentServerVersions(latestVersion, AGENT_CANVAS_CLIENT_VERSION)\n : null;\n const updateAvailable = comparison === 1;\n // 0 or -1: running the npm latest or newer (local dev build).\n const upToDate = comparison !== null && comparison <= 0;\n\n if (hideWhenUpToDate && !updateAvailable) return null;\n\n return (\n <>\n <section data-testid=\"agent-canvas-update-card\">\n <button\n type=\"button\"\n onClick={() => setIsModalOpen(true)}\n aria-haspopup=\"dialog\"\n data-testid=\"agent-canvas-update-toggle\"\n className=\"flex w-full cursor-pointer flex-col gap-1 rounded-md border border-[var(--oh-border)] bg-base-secondary px-3 py-2 text-left hover:bg-[var(--oh-surface-raised)]\"\n >\n <span className=\"flex w-full items-center gap-2\">\n <span className=\"flex-1 truncate text-sm font-semibold leading-5 text-white\">\n {t(I18nKey.SETTINGS$APP_UPDATE_CARD_TITLE)}\n </span>\n {comparison !== null && (\n <span\n data-testid=\"agent-canvas-update-badge\"\n className={cn(\n \"inline-flex shrink-0 items-center gap-1 text-[10px] font-medium leading-none\",\n updateAvailable\n ? \"rounded-full border border-transparent bg-[#1E3A5F] px-1.5 py-0.5 text-[#3B82F6]\"\n : \"text-success\",\n )}\n >\n {updateAvailable ? (\n <ArrowUpCircle className=\"size-3 shrink-0\" aria-hidden />\n ) : (\n <CircleCheck className=\"size-3 shrink-0\" aria-hidden />\n )}\n {t(\n updateAvailable\n ? I18nKey.SETTINGS$APP_UPDATE_BADGE_UPDATE_AVAILABLE\n : I18nKey.SETTINGS$APP_UPDATE_BADGE_UP_TO_DATE,\n )}\n </span>\n )}\n </span>\n\n <span className=\"flex items-center gap-1.5 text-xs leading-5 text-[var(--oh-muted)]\">\n <span>{t(I18nKey.SETTINGS$APP_UPDATE_VERSION_LABEL)}</span>\n <span className=\"text-white\">{AGENT_CANVAS_CLIENT_VERSION}</span>\n </span>\n </button>\n </section>\n\n {isModalOpen && (\n <AgentCanvasUpdateModal\n onClose={() => setIsModalOpen(false)}\n latestVersion={latestVersion}\n isPending={latestVersionQuery.isPending}\n isFetching={latestVersionQuery.isFetching}\n updateAvailable={updateAvailable}\n upToDate={upToDate}\n onCheckForUpdates={() => latestVersionQuery.refetch()}\n />\n )}\n </>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1,7 +1,12 @@
|
|
|
1
|
+
interface AgentCanvasUpdateCardProps {
|
|
2
|
+
/** When true, omit the card unless an update is available (main sidebar). */
|
|
3
|
+
hideWhenUpToDate?: boolean;
|
|
4
|
+
}
|
|
1
5
|
/**
|
|
2
|
-
* Information-only version/update card for the
|
|
3
|
-
* Never blocks or toasts; check failures
|
|
4
|
-
* Hidden in locked-to-Cloud deployments —
|
|
5
|
-
* install to update.
|
|
6
|
+
* Information-only version/update card for settings and the main sidebar.
|
|
7
|
+
* Opens update details in a modal. Never blocks or toasts; check failures
|
|
8
|
+
* degrade to a quiet inline message. Hidden in locked-to-Cloud deployments —
|
|
9
|
+
* the hosted canvas has no npm/docker install to update.
|
|
6
10
|
*/
|
|
7
|
-
export declare function AgentCanvasUpdateCard(): import("react").JSX.Element | null;
|
|
11
|
+
export declare function AgentCanvasUpdateCard({ hideWhenUpToDate, }: AgentCanvasUpdateCardProps): import("react").JSX.Element | null;
|
|
12
|
+
export {};
|