@openhands/agent-canvas 1.15.0 → 1.16.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 +15 -1
- package/README.windows.md +2 -2
- package/build/assets/{acp-providers-BKvmTf82.js → acp-providers-CX3Lh9gj.js} +1 -1
- package/build/assets/active-backend-context-CrHTlgvN.js +2 -0
- package/build/assets/{agent-context-settings-D3PjSTU1.js → agent-context-settings-DSZlXrLj.js} +1 -1
- package/build/assets/{agent-profiles-settings-D4cY5U2T.js → agent-profiles-settings-Dzgb7G9z.js} +1 -1
- package/build/assets/agent-server-conversation-service.api-tnqNjbpE.js +86 -0
- package/build/assets/agent-settings-DLtsqaOs.js +2 -0
- package/build/assets/agent-settings-fFvR-XC2.js +1 -0
- package/build/assets/alert-banner-HP6mX-Vr.js +1 -0
- package/build/assets/{api-error-message-BJyfWp6p.js → api-error-message-B-XdSk3b.js} +1 -1
- package/build/assets/api-key-entry-screen-qTzrmJcr.js +1 -0
- package/build/assets/{app-settings-CbHqzQus.js → app-settings-dbnneVaV.js} +1 -1
- package/build/assets/automation-detail-BmwlCUAB.js +3 -0
- package/build/assets/{automation-export-BRopEHMu.js → automation-export-D3a3of4G.js} +1 -1
- package/build/assets/{automation-git-sync-CzyS4cn1.js → automation-git-sync-CprN5DcU.js} +1 -1
- package/build/assets/automation-setup-route-C40kXjMY.js +1 -0
- package/build/assets/{automation-templates-C_0FZgLG.js → automation-templates-BRppNh99.js} +1 -1
- package/build/assets/automations-list-DuNcRawl.js +1 -0
- package/build/assets/{back-nav-button-Dm9tIjVf.js → back-nav-button-Cjl_hoxq.js} +1 -1
- package/build/assets/backend-form-modal-lhGwzXFI.js +1 -0
- package/build/assets/{backend-synced-settings-badge-3fOwg6Bd.js → backend-synced-settings-badge-DPkz30BE.js} +1 -1
- package/build/assets/{badge-D4SqUCsR.js → badge-BU3S0VtE.js} +1 -1
- package/build/assets/{base-modal-C7_BDl_U.js → base-modal-D_L0oEbG.js} +1 -1
- package/build/assets/{bot-C-0Wu7vQ.js → bot-dMYHVfYM.js} +1 -1
- package/build/assets/{brand-button-Dxv6PDeN.js → brand-button-BAfk4wGP.js} +1 -1
- package/build/assets/browser-DSAo0tz1.js +46 -0
- package/build/assets/{browser-tab-BAl4ckJE.js → browser-tab-C9vy56R5.js} +1 -1
- package/build/assets/canvas-extension-page-dx1gFrfW.js +1 -0
- package/build/assets/canvas-extensions-8A-XhpgP.js +1 -0
- package/build/assets/canvas-extensions-runtime-2_x5Ezmi.js +1 -0
- package/build/assets/{chat-send-button-DWwR2cgI.js → chat-send-button-MUQqCZCS.js} +1 -1
- package/build/assets/check-CS0vmpQQ.js +1 -0
- package/build/assets/chevron-down-e1QBpjVy.js +1 -0
- package/build/assets/chevron-right-ZG4R5G4Q.js +1 -0
- package/build/assets/circle-check-DzWLNsbE.js +1 -0
- package/build/assets/{circle-plus-check-toggle-CBh_qomE.js → circle-plus-check-toggle-BO_kFZt3.js} +1 -1
- package/build/assets/{cloud-funnel-analytics-287iIERo.js → cloud-funnel-analytics-CztBCCPp.js} +1 -1
- package/build/assets/{cog-C4rrw-ZK.js → cog-D-MPQW6G.js} +1 -1
- package/build/assets/command-menu--UIlGL72.js +1 -0
- package/build/assets/{commits-tab-DZtJgaDM.js → commits-tab-AnZwLvh-.js} +1 -1
- package/build/assets/{condenser-settings-BcNmYR4s.js → condenser-settings-DmEOje-7.js} +1 -1
- package/build/assets/{confirmation-modal-Bk4JRcCM.js → confirmation-modal-Dv1ktp8K.js} +1 -1
- package/build/assets/{context-menu-list-item-lVYETtY5.js → context-menu-list-item-I86vKcmM.js} +1 -1
- package/build/assets/conversation-BbGrAO4Z.js +93 -0
- package/build/assets/conversation-mYT6NAoj.js +1 -0
- package/build/assets/conversation-panel-mYT6NAoj.js +1 -0
- package/build/assets/conversation-state-store-B0ImPLHO.js +1 -0
- package/build/assets/{conversation-tab-empty-state-HJtkmHZj.js → conversation-tab-empty-state-FIbh_lc8.js} +1 -1
- package/build/assets/{copy-to-clipboard-button-Cjkswvnj.js → copy-to-clipboard-button-Ca8iSkkA.js} +1 -1
- package/build/assets/{device-verify-EZhHhBT7.js → device-verify-Bhn2e_Ye.js} +1 -1
- package/build/assets/{dropdown-classes-DJe-E6MC.js → dropdown-classes-Bc1djN0-.js} +1 -1
- package/build/assets/{edit-automation-modal-D-urtv8n.js → edit-automation-modal-DadGbJ2u.js} +1 -1
- package/build/assets/empty-state-BoGVDecT.js +1 -0
- package/build/assets/{entry.client-C7Bi2tkU.js → entry.client-CqyPPjOq.js} +2 -2
- package/build/assets/{enum-filter-dropdown-DliX_Lo0.js → enum-filter-dropdown-Bgca129f.js} +1 -1
- package/build/assets/{environment-switch-overlay-CEKrFiMw.js → environment-switch-overlay-BmBRT7gv.js} +1 -1
- package/build/assets/{error-state-BALXiBNy.js → error-state-Body8Ngd.js} +1 -1
- package/build/assets/extensions-hub-CcDqq_vR.js +1 -0
- package/build/assets/extensions-navigation-Cr4yzYk4.js +1 -0
- package/build/assets/{external-link-DFpYunfj.js → external-link-DKUlnCQV.js} +1 -1
- package/build/assets/files-tab-D4o2VyE_.js +1 -0
- package/build/assets/{form-control-classes-CGw40z5h.js → form-control-classes-Bkh5FGrD.js} +1 -1
- package/build/assets/format-model-name-CasAP3nd.js +1 -0
- package/build/assets/get-git-path-ChKDVHZp.js +1 -0
- package/build/assets/{git-repo-dropdown-ai8qaNkD.js → git-repo-dropdown-4ROKl2cB.js} +1 -1
- package/build/assets/git-repo-dropdown-BkqzkMmM.js +1 -0
- package/build/assets/{highlighted-source-view-C3sxykQi.js → highlighted-source-view-BFRxGT_Q.js} +1 -1
- package/build/assets/home-ChpSuepX.js +1 -0
- package/build/assets/home-DIsB440S.js +1 -0
- package/build/assets/home-automation-run-tooltip-BVNDWjAH.js +1 -0
- package/build/assets/home-store-CY9Ej0ux.js +1 -0
- package/build/assets/index-home-BdlrFIif.js +1 -0
- package/build/assets/info-B6RQawoW.js +1 -0
- package/build/assets/install-server-modal-Dpy89p4g.js +1 -0
- package/build/assets/kebab-menu-item-content-D8hSNGmm.js +1 -0
- package/build/assets/{key-Cn5za3rg.js → key-BmVyxs-2.js} +1 -1
- package/build/assets/{launch-C1ieiOGe.js → launch-IrTDEFCs.js} +1 -1
- package/build/assets/list-filter-DsEK7NFC.js +1 -0
- package/build/assets/{list-todo-Cuu-STFc.js → list-todo-apgLVjMw.js} +1 -1
- package/build/assets/llm-not-configured-banner-CIFABh3K.js +27 -0
- package/build/assets/llm-settings-B9juXhIa.js +1 -0
- package/build/assets/llm-settings-De1d5dfr.js +1 -0
- package/build/assets/llm-subscription-service-BpJ0ouQo.js +1 -0
- package/build/assets/loader-circle-CC1au6hc.js +1 -0
- package/build/assets/{loading-spinner-0L3ZTToZ.js → loading-spinner-BY9N3pOe.js} +1 -1
- package/build/assets/manage-backends-modal-BrPV3oB-.js +1 -0
- package/build/assets/manifest-80fb1c29.js +1 -0
- package/build/assets/{manifest-subpage-layout-BGhiUQbA.js → manifest-subpage-layout-CAOYzrYp.js} +1 -1
- package/build/assets/{map-provider-B9LTm23I.js → map-provider-Cu3Zgumb.js} +1 -1
- package/build/assets/markdown-renderer-BzTlxI8x.js +8 -0
- package/build/assets/{mcp-CKbR4Vft.js → mcp-BTs_SXfZ.js} +1 -1
- package/build/assets/{mcp-marketplace-utils-C13fFccR.js → mcp-marketplace-utils-AIBzzHTO.js} +1 -1
- package/build/assets/{mcp-page-Bm4K2NQq.js → mcp-page-BEBYqH_K.js} +2 -2
- package/build/assets/messages-DKV9cRc2.js +41 -0
- package/build/assets/{modal-body-CMOc2tLl.js → modal-body-BxZjsju9.js} +1 -1
- package/build/assets/{modal-classes-BAb4Y_BG.js → modal-classes-C8eycCwG.js} +1 -1
- package/build/assets/{modal-close-button-CkJ0Ktjj.js → modal-close-button-DDzdRvCs.js} +1 -1
- package/build/assets/{mouse-pointer-click-CsgRs_-r.js → mouse-pointer-click-Co2uyaPA.js} +1 -1
- package/build/assets/onboarding-DV_OXoeP.js +1 -0
- package/build/assets/onboarding-modal-rkyBYkeQ.js +1 -0
- package/build/assets/{organization-service.api-C8XpQ0pM.js → organization-service.api-DrYNkEN_.js} +1 -1
- package/build/assets/{pencil-Clh1Wjvh.js → pencil-47jAZH_V.js} +1 -1
- package/build/assets/pin-By41Vl5J.js +1 -0
- package/build/assets/{planner-tab-DQvDU3ap.js → planner-tab-C9uZmhme.js} +1 -1
- package/build/assets/{plug-BuQhHSSG.js → plug-CG5HFcNG.js} +1 -1
- package/build/assets/plugins-management-service-BMJgC3Ga.js +1 -0
- package/build/assets/plus-C7Slq-0I.js +1 -0
- package/build/assets/profiles-service.api-ChoKrL0l.js +1 -0
- package/build/assets/providers-BhMcY5nv.js +1 -0
- package/build/assets/proxy-9Fe1v5na.js +1 -0
- package/build/assets/{puzzle-A9pGxxai.js → puzzle-JM_QaRm0.js} +1 -1
- package/build/assets/recommended-automations-launcher-6ABrI9Cy.js +1 -0
- package/build/assets/{refresh-cw-Bh69DyYO.js → refresh-cw-3q3Frrii.js} +1 -1
- package/build/assets/root-Dc8YmC9F.js +2 -0
- package/build/assets/root-VDaypvK4.css +1 -0
- package/build/assets/root-layout-CXkhSPYh.js +2 -0
- package/build/assets/schema-field-D-y9-LDb.js +1 -0
- package/build/assets/{sdk-section-page-HTffvm7Q.js → sdk-section-page-BlFiizlG.js} +1 -1
- package/build/assets/{secret-form-HTUbYw5o.js → secret-form-BB7the8W.js} +1 -1
- package/build/assets/secrets-service-ByZVENbp.js +1 -0
- package/build/assets/{secrets-settings-BizaTEr1.js → secrets-settings-B_GpxKW2.js} +1 -1
- package/build/assets/{settings-BlmrkwRi.js → settings-C0EWuBtA.js} +1 -1
- package/build/assets/settings-CKqk8ZnH.js +1 -0
- package/build/assets/{settings-dropdown-input-tM0A0ipy.js → settings-dropdown-input-BcwylHs2.js} +1 -1
- package/build/assets/{settings-index-CKq0b0Vg.js → settings-index-5w5UW2K3.js} +1 -1
- package/build/assets/{settings-input-C03KUoSC.js → settings-input-Db-1g8Ql.js} +1 -1
- package/build/assets/{settings-list-classes-BJ0g8rWj.js → settings-list-classes-BzeSZbF2.js} +1 -1
- package/build/assets/{settings-modal-AzEF1QPP.js → settings-modal-DfJ3tcJQ.js} +1 -1
- package/build/assets/settings-service.api-DJ1r62Zr.js +1 -0
- package/build/assets/{settings-switch-CQE0EGC-.js → settings-switch-CS-dJDmA.js} +1 -1
- package/build/assets/{settings-utils-ClYOkbbI.js → settings-utils-UYpEdPuD.js} +1 -1
- package/build/assets/shared-conversation-CA5ISEDy.js +1 -0
- package/build/assets/{shield-check-CFDViW82.js → shield-check-D2aCRd16.js} +1 -1
- package/build/assets/{sidebar-layout-D3hE08wK.js → sidebar-layout-D6fS8-Gl.js} +1 -1
- package/build/assets/{sidebar-mobile-menu-toggle-BOtyJHwE.js → sidebar-mobile-menu-toggle-C_fb0Jki.js} +1 -1
- package/build/assets/sidebar-nav-link-DQQcMf1A.js +1 -0
- package/build/assets/{sidebar-onboarding-checklist-storage-3qYflszY.js → sidebar-onboarding-checklist-storage-BBkeI6Bp.js} +1 -1
- package/build/assets/sidebar-store-DGqHkVLw.js +1 -0
- package/build/assets/{skill-card-pill-row-CFxjoqxn.js → skill-card-pill-row-B27-xyig.js} +1 -1
- package/build/assets/skill-enablement-DXeaImBz.js +1 -0
- package/build/assets/skill-filter-w__3UCEE.js +2 -0
- package/build/assets/{skill-icon-badge-CPQJ6A-I.js → skill-icon-badge-RQkq8wvl.js} +1 -1
- package/build/assets/skill-scope-B42H-XOc.js +1 -0
- package/build/assets/skills-plugins-tgF8EiXC.js +1 -0
- package/build/assets/skills-settings-BgJ9PaOe.js +1 -0
- package/build/assets/{styled-tooltip-Dl06rkpY.js → styled-tooltip-C4jf5Fv9.js} +1 -1
- package/build/assets/{task-list-tab-04GNQuii.js → task-list-tab-C5zcaktH.js} +1 -1
- package/build/assets/{terminal-Cj0iHIW7.js → terminal-CNElN42a.js} +2 -2
- package/build/assets/{toggle-switch-Fd_QGz0r.js → toggle-switch-ChOLXv2n.js} +1 -1
- package/build/assets/{trash-2-Dl8_RGz0.js → trash-2-D7K26eDO.js} +1 -1
- package/build/assets/{trash-_RMt7Ayc.js → trash-D-HYiVYM.js} +1 -1
- package/build/assets/{triangle-alert-SM-q4NU_.js → triangle-alert-CtdXWHUI.js} +1 -1
- package/build/assets/{typography-SgFy466J.js → typography-v1KN0tzi.js} +1 -1
- package/build/assets/usage-tab-CVDffLPt.js +1 -0
- package/build/assets/use-acp-credential-form-XIQmpX6M.js +1 -0
- package/build/assets/{use-activate-agent-profile-B2NS8nA5.js → use-activate-agent-profile-BLfmvDiG.js} +1 -1
- package/build/assets/use-agent-profiles-Bd9Bvvp1.js +1 -0
- package/build/assets/{use-automation-health-BxXdzZMP.js → use-automation-health-E6NZi9rb.js} +1 -1
- package/build/assets/{use-automations-zYb0dUny.js → use-automations-xijQKIl9.js} +1 -1
- package/build/assets/{use-can-manage-org-profiles-BWMahtUh.js → use-can-manage-org-profiles-CrJP5262.js} +1 -1
- package/build/assets/use-canvas-extensions-Bi693ujJ.js +1 -0
- package/build/assets/{use-cloud-current-user-id-CPHMDG-C.js → use-cloud-current-user-id-CDfAHpPX.js} +1 -1
- package/build/assets/use-config-A9QovgXO.js +1 -0
- package/build/assets/{use-create-conversation-DwN5vv2f.js → use-create-conversation-Dx3l7mFZ.js} +1 -1
- package/build/assets/use-create-secret-B9cUFWRG.js +1 -0
- package/build/assets/{use-get-secrets-BT4ihRwj.js → use-get-secrets-K3jmk-LH.js} +1 -1
- package/build/assets/use-is-authed-nTBcM7dE.js +1 -0
- package/build/assets/use-llm-configured-BV9nZ1_j.js +1 -0
- package/build/assets/use-llm-profiles-BO-P4Oyo.js +1 -0
- package/build/assets/use-manifest-capabilities-Cw_flwEz.js +1 -0
- package/build/assets/use-onboarding-completion-D8U7k4Qb.js +1 -0
- package/build/assets/use-pinned-home-route-CckOZrmb.js +1 -0
- package/build/assets/use-plugins-marketplace-7i94Zu3Z.js +1 -0
- package/build/assets/use-save-agent-profile-BUjSsDXK.js +1 -0
- package/build/assets/{use-save-settings-CUZW6yXP.js → use-save-settings-DYqkKv7T.js} +1 -1
- package/build/assets/{use-settings-C5VhGpqd.js → use-settings-DSXLbvBl.js} +1 -1
- package/build/assets/{use-settings-nav-items-DHaUu1Dg.js → use-settings-nav-items-CwRw-CmX.js} +1 -1
- package/build/assets/use-tracking-D53y3TH9.js +3 -0
- package/build/assets/{use-user-conversation-DT0_QCnL.js → use-user-conversation-B9Gebw_g.js} +1 -1
- package/build/assets/{useInfiniteQuery-CgeUQPfs.js → useInfiniteQuery-C926xzY4.js} +1 -1
- package/build/assets/{useMutation-DFnzkf6f.js → useMutation-B-qc391N.js} +1 -1
- package/build/assets/{useQueries-BhbrGEf1.js → useQueries-DlbhlH9I.js} +1 -1
- package/build/assets/{useQuery-BXWbZBZl.js → useQuery-BSTgq1rl.js} +1 -1
- package/build/assets/utils-CN5ZfZy1.js +1 -0
- package/build/assets/{vendor~conversation-panel~conversation-CTvDDiXg.js → vendor~conversation-panel~conversation-C8Fn4XOf.js} +1 -1
- package/build/assets/{vendor~entry.client-BFE7Llz9.js → vendor~entry.client-CoCfUXUF.js} +1 -1
- package/build/assets/{vendor~entry.client-BaKt_PIp.js → vendor~entry.client-DA2izTjq.js} +1 -1
- package/build/assets/{vendor~entry.client~root~root-layout~index-home~home~conversation-panel~conversation~launch~kepq2zme-ZixYGy6E.js → vendor~entry.client~root~root-layout~index-home~home~conversation-panel~conversation~launch~czjee3a3-DqZDXfg2.js} +1 -1
- package/build/assets/{vendor~index-home~home~conversation-panel~conversation-BTyukeM-.js → vendor~index-home~home~conversation-panel~conversation-hWywRBaR.js} +1 -1
- package/build/assets/{vendor~root-layout-BlUaZ18v.js → vendor~root-layout-C9Ju9DQg.js} +1 -1
- package/build/assets/{vendor~root-layout~conversation-panel~conversation-BYAWJwQX.js → vendor~root-layout~conversation-panel~conversation-BMbMgaGu.js} +1 -1
- package/build/assets/{vendor~root-layout~conversation-panel~conversation~skills-settings~automations-list~shared-~bovqqk8e-CHaLEBVR.js → vendor~root-layout~conversation-panel~conversation~skills-settings~automations-list~shared-~bovqqk8e-BWir3rJs.js} +1 -1
- package/build/assets/vendor~root-layout~index-home~home~conversation-panel~conversation-j5MFqfSy.js +1 -0
- package/build/assets/vendor~root-layout~index-home~home~conversation-panel~conversation~mcp~automations-list~aut~ibfbmr9f-cDuKr-WD.js +1 -0
- package/build/assets/{vendor~root~root-layout~index-home~home~conversation-panel~conversation~launch~extensions-h~iw7io29p-Dz-MAyNb.js → vendor~root~root-layout~index-home~home~conversation-panel~conversation~launch~extensions-h~iyia17eu-Chf7vmKB.js} +1 -1
- package/build/assets/{vendor~root~root-layout~index-home~home~conversation-panel~conversation~launch~extensions-h~hjcjabwg-Dtu7BpYk.js → vendor~root~root-layout~index-home~home~conversation-panel~conversation~launch~extensions-h~nj1hli3r-DP9wmsjo.js} +2 -2
- package/build/assets/{vendor~root~root-layout~index-home~home~conversation-panel~conversation~launch~skills-setti~eo2nf80n-DonTKfQD.js → vendor~root~root-layout~index-home~home~conversation-panel~conversation~launch~skills-setti~eo2nf80n-CUmIwQCN.js} +117 -13
- package/build/assets/{vendor~root~root-layout~index-home~home~conversation-panel~conversation~launch~skills-setti~jz4f6vo2-BgxDvUcf.js → vendor~root~root-layout~index-home~home~conversation-panel~conversation~launch~skills-setti~jz4f6vo2-C7HiY4Ae.js} +42 -3
- package/build/assets/{verification-settings-Bj6bo097.js → verification-settings-gw_q6vkL.js} +1 -1
- package/build/assets/{wrench-hi7Q13dF.js → wrench-CUEI1krB.js} +1 -1
- package/build/assets/{x-DkgltWa0.js → x-DJdhbdmt.js} +1 -1
- package/build/index.html +4 -4
- package/build/locales/ar/openhands.json +32 -16
- package/build/locales/ca/openhands.json +32 -16
- package/build/locales/de/openhands.json +32 -16
- package/build/locales/en/openhands.json +32 -16
- package/build/locales/es/openhands.json +32 -16
- package/build/locales/fr/openhands.json +32 -16
- package/build/locales/it/openhands.json +32 -16
- package/build/locales/ja/openhands.json +32 -16
- package/build/locales/ko-KR/openhands.json +32 -16
- package/build/locales/no/openhands.json +32 -16
- package/build/locales/pt/openhands.json +32 -16
- package/build/locales/tr/openhands.json +32 -16
- package/build/locales/uk/openhands.json +32 -16
- package/build/locales/zh-CN/openhands.json +32 -16
- package/build/locales/zh-TW/openhands.json +32 -16
- package/config/defaults.json +9 -5
- package/dist/api/agent-profiles-service/profile-field-support.d.ts +26 -0
- 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.js +118 -116
- package/dist/api/agent-server-adapter.js.map +1 -1
- package/dist/api/agent-server-compatibility.cjs +1 -1
- package/dist/api/agent-server-compatibility.cjs.map +1 -1
- package/dist/api/agent-server-compatibility.d.ts +21 -0
- package/dist/api/agent-server-compatibility.js +52 -38
- package/dist/api/agent-server-compatibility.js.map +1 -1
- package/dist/api/agent-server-config.cjs +1 -1
- package/dist/api/agent-server-config.cjs.map +1 -1
- package/dist/api/agent-server-config.d.ts +26 -0
- package/dist/api/agent-server-config.js +18 -5
- package/dist/api/agent-server-config.js.map +1 -1
- package/dist/api/canvas-extensions-service.cjs +1 -0
- package/dist/api/canvas-extensions-service.d.ts +22 -0
- package/dist/api/canvas-extensions-service.js +4 -0
- 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 +6 -0
- package/dist/api/cloud/conversation-service.api.js +14 -6
- 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.d.ts +11 -0
- package/dist/api/conversation-service/agent-server-conversation-service.api.js +129 -118
- 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/git-service/agent-server-git-service.api.cjs +1 -1
- package/dist/api/git-service/agent-server-git-service.api.js +8 -8
- package/dist/api/runtime-service/agent-server-runtime-service.cjs +1 -1
- package/dist/api/runtime-service/agent-server-runtime-service.js +8 -8
- package/dist/api/settings-service/settings-service.api.cjs +1 -1
- package/dist/api/settings-service/settings-service.api.cjs.map +1 -1
- package/dist/api/settings-service/settings-service.api.d.ts +3 -2
- package/dist/api/settings-service/settings-service.api.js +2 -1
- package/dist/api/settings-service/settings-service.api.js.map +1 -1
- package/dist/components/conversation-events/chat/event-message-components/event-group.cjs +1 -1
- package/dist/components/conversation-events/chat/event-message-components/event-group.cjs.map +1 -1
- package/dist/components/conversation-events/chat/event-message-components/event-group.js +40 -36
- package/dist/components/conversation-events/chat/event-message-components/event-group.js.map +1 -1
- package/dist/components/features/automations/automation-card.cjs +1 -1
- package/dist/components/features/automations/automation-card.cjs.map +1 -1
- package/dist/components/features/automations/automation-card.js +114 -104
- package/dist/components/features/automations/automation-card.js.map +1 -1
- package/dist/components/features/automations/detail/run-phase.cjs +2 -0
- package/dist/components/features/automations/detail/run-phase.cjs.map +1 -0
- package/dist/components/features/automations/detail/run-phase.d.ts +89 -0
- package/dist/components/features/automations/detail/run-phase.js +72 -0
- package/dist/components/features/automations/detail/run-phase.js.map +1 -0
- package/dist/components/features/backends/backend-form-modal.cjs +1 -1
- package/dist/components/features/backends/backend-form-modal.cjs.map +1 -1
- package/dist/components/features/backends/backend-form-modal.js +5 -11
- package/dist/components/features/backends/backend-form-modal.js.map +1 -1
- package/dist/components/features/canvas-extensions/add-canvas-extension-modal.d.ts +6 -0
- package/dist/components/features/canvas-extensions/canvas-extension-card.d.ts +9 -0
- package/dist/components/features/canvas-extensions/canvas-extensions-runtime.cjs +2 -0
- package/dist/components/features/canvas-extensions/canvas-extensions-runtime.cjs.map +1 -0
- package/dist/components/features/canvas-extensions/canvas-extensions-runtime.d.ts +23 -0
- package/dist/components/features/canvas-extensions/canvas-extensions-runtime.js +19 -0
- package/dist/components/features/canvas-extensions/canvas-extensions-runtime.js.map +1 -0
- package/dist/components/features/chat/chat-interface.cjs +2 -2
- package/dist/components/features/chat/chat-interface.cjs.map +1 -1
- package/dist/components/features/chat/chat-interface.js +171 -168
- package/dist/components/features/chat/chat-interface.js.map +1 -1
- package/dist/components/features/chat/chat-markdown-path-code.cjs +3 -0
- package/dist/components/features/chat/chat-markdown-path-code.cjs.map +1 -0
- package/dist/components/features/chat/chat-markdown-path-code.d.ts +27 -0
- package/dist/components/features/chat/chat-markdown-path-code.js +72 -0
- package/dist/components/features/chat/chat-markdown-path-code.js.map +1 -0
- package/dist/components/features/chat/path-component.cjs +1 -1
- package/dist/components/features/chat/path-component.cjs.map +1 -1
- package/dist/components/features/chat/path-component.d.ts +8 -2
- package/dist/components/features/chat/path-component.js +33 -21
- package/dist/components/features/chat/path-component.js.map +1 -1
- package/dist/components/features/chat/tool-visualizers/file-editor/file-editor.cjs +1 -1
- package/dist/components/features/chat/tool-visualizers/file-editor/file-editor.cjs.map +1 -1
- package/dist/components/features/chat/tool-visualizers/file-editor/file-editor.js +38 -43
- package/dist/components/features/chat/tool-visualizers/file-editor/file-editor.js.map +1 -1
- package/dist/components/features/chat/tool-visualizers/primitives/file-path-chip.cjs +1 -1
- package/dist/components/features/chat/tool-visualizers/primitives/file-path-chip.cjs.map +1 -1
- package/dist/components/features/chat/tool-visualizers/primitives/file-path-chip.d.ts +8 -5
- package/dist/components/features/chat/tool-visualizers/primitives/file-path-chip.js +22 -16
- package/dist/components/features/chat/tool-visualizers/primitives/file-path-chip.js.map +1 -1
- package/dist/components/features/chat/user-message-body.cjs +1 -1
- package/dist/components/features/chat/user-message-body.cjs.map +1 -1
- package/dist/components/features/chat/user-message-body.d.ts +4 -0
- package/dist/components/features/chat/user-message-body.js +30 -24
- package/dist/components/features/chat/user-message-body.js.map +1 -1
- package/dist/components/features/conversation/conversation-overview-skills-panel.cjs +1 -1
- package/dist/components/features/conversation/conversation-overview-skills-panel.cjs.map +1 -1
- package/dist/components/features/conversation/conversation-overview-skills-panel.js +63 -81
- package/dist/components/features/conversation/conversation-overview-skills-panel.js.map +1 -1
- package/dist/components/features/conversation/conversation-tabs/conversation-tabs.cjs +1 -1
- package/dist/components/features/conversation/conversation-tabs/conversation-tabs.cjs.map +1 -1
- package/dist/components/features/conversation/conversation-tabs/conversation-tabs.js +50 -50
- package/dist/components/features/conversation/conversation-tabs/conversation-tabs.js.map +1 -1
- package/dist/components/features/conversation/conversation-tabs/drawer-vscode-link.cjs +1 -1
- package/dist/components/features/conversation/conversation-tabs/drawer-vscode-link.cjs.map +1 -1
- package/dist/components/features/conversation/conversation-tabs/drawer-vscode-link.d.ts +1 -1
- package/dist/components/features/conversation/conversation-tabs/drawer-vscode-link.js +3 -3
- package/dist/components/features/conversation/conversation-tabs/drawer-vscode-link.js.map +1 -1
- package/dist/components/features/conversation-panel/skills-modal.cjs +1 -1
- package/dist/components/features/conversation-panel/skills-modal.cjs.map +1 -1
- package/dist/components/features/conversation-panel/skills-modal.js +29 -28
- package/dist/components/features/conversation-panel/skills-modal.js.map +1 -1
- package/dist/components/features/files-tab/file-content-viewer.cjs +1 -1
- package/dist/components/features/files-tab/file-content-viewer.js +5 -5
- package/dist/components/features/home/featured-automations/home-automation-run-tooltip.cjs +1 -1
- package/dist/components/features/home/featured-automations/home-automation-run-tooltip.cjs.map +1 -1
- package/dist/components/features/home/featured-automations/home-automation-run-tooltip.js +51 -29
- package/dist/components/features/home/featured-automations/home-automation-run-tooltip.js.map +1 -1
- package/dist/components/features/onboarding/onboarding-host.d.ts +3 -4
- package/dist/components/features/onboarding/steps/setup-llm-step.d.ts +5 -9
- package/dist/components/features/settings/acp-auth-status-banner.d.ts +23 -6
- package/dist/components/features/settings/llm-profiles/llm-settings-local-view.cjs +1 -1
- package/dist/components/features/settings/llm-profiles/llm-settings-local-view.js +1 -0
- package/dist/components/features/settings/llm-profiles/provider-connection-modal.cjs +1 -1
- package/dist/components/features/settings/llm-profiles/provider-connection-modal.js +3 -0
- package/dist/components/features/settings/sdk-settings/field-help.cjs +1 -1
- package/dist/components/features/settings/sdk-settings/field-help.cjs.map +1 -1
- package/dist/components/features/settings/sdk-settings/field-help.d.ts +1 -1
- package/dist/components/features/settings/sdk-settings/field-help.js +1 -1
- package/dist/components/features/settings/sdk-settings/field-help.js.map +1 -1
- package/dist/components/features/settings/sdk-settings/sdk-section-page.cjs +1 -1
- package/dist/components/features/settings/sdk-settings/sdk-section-page.js +6 -6
- package/dist/components/features/settings/sdk-settings/sdk-section-page.js.map +1 -1
- package/dist/components/features/sidebar/sidebar-nav-link.cjs +1 -1
- package/dist/components/features/sidebar/sidebar-nav-link.cjs.map +1 -1
- package/dist/components/features/sidebar/sidebar-nav-link.d.ts +10 -1
- package/dist/components/features/sidebar/sidebar-nav-link.js +54 -36
- package/dist/components/features/sidebar/sidebar-nav-link.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 +143 -120
- package/dist/components/features/sidebar/sidebar-rail-body.js.map +1 -1
- package/dist/components/features/sidebar/sidebar.cjs +1 -1
- package/dist/components/features/sidebar/sidebar.cjs.map +1 -1
- package/dist/components/features/sidebar/sidebar.js +1 -1
- package/dist/components/features/sidebar/sidebar.js.map +1 -1
- package/dist/components/features/skills/build-skill-pills.cjs +1 -1
- package/dist/components/features/skills/build-skill-pills.cjs.map +1 -1
- package/dist/components/features/skills/build-skill-pills.js +60 -52
- package/dist/components/features/skills/build-skill-pills.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 +54 -43
- package/dist/components/features/skills/extensions-navigation.js.map +1 -1
- package/dist/components/features/skills/skill-filter.cjs +1 -1
- package/dist/components/features/skills/skill-filter.cjs.map +1 -1
- package/dist/components/features/skills/skill-filter.d.ts +8 -3
- package/dist/components/features/skills/skill-filter.js +69 -52
- package/dist/components/features/skills/skill-filter.js.map +1 -1
- package/dist/components/shared/modals/settings/settings-form.cjs +1 -1
- package/dist/components/shared/modals/settings/settings-form.js +8 -8
- package/dist/config/defaults.cjs +1 -1
- package/dist/config/defaults.cjs.map +1 -1
- package/dist/config/defaults.js +9 -5
- package/dist/config/defaults.js.map +1 -1
- 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 +5 -5
- package/dist/extensions/canvas-extension-module-loader.d.ts +11 -0
- package/dist/hooks/mutation/use-manage-canvas-extensions.d.ts +12 -0
- package/dist/hooks/query/concurrency-limiter.cjs +2 -0
- package/dist/hooks/query/concurrency-limiter.cjs.map +1 -0
- package/dist/hooks/query/concurrency-limiter.d.ts +22 -0
- package/dist/hooks/query/concurrency-limiter.js +29 -0
- package/dist/hooks/query/concurrency-limiter.js.map +1 -0
- package/dist/hooks/query/query-keys.cjs.map +1 -1
- package/dist/hooks/query/query-keys.d.ts +4 -0
- package/dist/hooks/query/query-keys.js.map +1 -1
- package/dist/hooks/query/use-active-conversation.cjs +1 -1
- package/dist/hooks/query/use-active-conversation.js +4 -4
- package/dist/hooks/query/use-automations.cjs.map +1 -1
- package/dist/hooks/query/use-automations.js.map +1 -1
- package/dist/hooks/query/use-backends-health.cjs +1 -1
- package/dist/hooks/query/use-backends-health.cjs.map +1 -1
- package/dist/hooks/query/use-backends-health.d.ts +1 -1
- package/dist/hooks/query/use-backends-health.js +50 -64
- package/dist/hooks/query/use-backends-health.js.map +1 -1
- package/dist/hooks/query/use-canvas-extensions.cjs +1 -0
- package/dist/hooks/query/use-canvas-extensions.d.ts +5 -0
- package/dist/hooks/query/use-canvas-extensions.js +3 -0
- package/dist/hooks/query/use-commit-changes.cjs +1 -1
- package/dist/hooks/query/use-commit-changes.js +4 -4
- package/dist/hooks/query/use-latest-automation-runs.cjs +1 -1
- package/dist/hooks/query/use-latest-automation-runs.js +1 -0
- package/dist/hooks/query/use-paginated-conversations.cjs +1 -1
- package/dist/hooks/query/use-paginated-conversations.cjs.map +1 -1
- package/dist/hooks/query/use-paginated-conversations.js +19 -16
- package/dist/hooks/query/use-paginated-conversations.js.map +1 -1
- package/dist/hooks/query/use-search-providers.cjs +1 -1
- package/dist/hooks/query/use-search-providers.cjs.map +1 -1
- package/dist/hooks/query/use-search-providers.js +17 -10
- package/dist/hooks/query/use-search-providers.js.map +1 -1
- package/dist/hooks/query/use-unified-get-git-changes.cjs +1 -1
- package/dist/hooks/query/use-unified-get-git-changes.js +4 -4
- package/dist/hooks/query/use-unified-git-commits.cjs +1 -1
- package/dist/hooks/query/use-unified-git-commits.js +4 -4
- package/dist/hooks/query/use-unified-git-diff.cjs +1 -1
- package/dist/hooks/query/use-unified-git-diff.js +4 -4
- package/dist/hooks/query/use-unified-vscode-url.cjs +1 -1
- package/dist/hooks/query/use-unified-vscode-url.cjs.map +1 -1
- package/dist/hooks/query/use-unified-vscode-url.d.ts +1 -0
- package/dist/hooks/query/use-unified-vscode-url.js +40 -26
- package/dist/hooks/query/use-unified-vscode-url.js.map +1 -1
- package/dist/hooks/query/use-workspace-file-content.cjs +1 -1
- package/dist/hooks/query/use-workspace-file-content.js +5 -5
- package/dist/hooks/query/use-workspace-files.cjs +1 -1
- package/dist/hooks/query/use-workspace-files.js +6 -6
- package/dist/hooks/use-acp-credential-form.d.ts +6 -0
- package/dist/hooks/use-conversation-overview-git-diff-stats.cjs +1 -1
- package/dist/hooks/use-conversation-overview-git-diff-stats.js +4 -4
- package/dist/hooks/use-pinned-home-route.cjs +2 -0
- package/dist/hooks/use-pinned-home-route.cjs.map +1 -0
- package/dist/hooks/use-pinned-home-route.d.ts +33 -0
- package/dist/hooks/use-pinned-home-route.js +34 -0
- package/dist/hooks/use-pinned-home-route.js.map +1 -0
- package/dist/hooks/use-skill-enablement.cjs +2 -0
- package/dist/hooks/use-skill-enablement.cjs.map +1 -0
- package/dist/hooks/use-skill-enablement.d.ts +14 -0
- package/dist/hooks/use-skill-enablement.js +80 -0
- package/dist/hooks/use-skill-enablement.js.map +1 -0
- package/dist/i18n/declaration.cjs +1 -1
- package/dist/i18n/declaration.cjs.map +1 -1
- package/dist/i18n/declaration.d.ts +29 -13
- package/dist/i18n/declaration.js +1 -1
- package/dist/i18n/declaration.js.map +1 -1
- package/dist/i18n/translation.cjs +3 -3
- package/dist/i18n/translation.cjs.map +1 -1
- package/dist/i18n/translation.js +538 -266
- package/dist/i18n/translation.js.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.js +15 -14
- package/dist/lib/index.cjs +1 -1
- package/dist/lib/index.d.ts +1 -0
- package/dist/lib/index.js +1 -0
- package/dist/locales/ar/openhands.json +32 -16
- package/dist/locales/ca/openhands.json +32 -16
- package/dist/locales/de/openhands.json +32 -16
- package/dist/locales/en/openhands.json +32 -16
- package/dist/locales/es/openhands.json +32 -16
- package/dist/locales/fr/openhands.json +32 -16
- package/dist/locales/it/openhands.json +32 -16
- package/dist/locales/ja/openhands.json +32 -16
- package/dist/locales/ko-KR/openhands.json +32 -16
- package/dist/locales/no/openhands.json +32 -16
- package/dist/locales/pt/openhands.json +32 -16
- package/dist/locales/tr/openhands.json +32 -16
- package/dist/locales/uk/openhands.json +32 -16
- package/dist/locales/zh-CN/openhands.json +32 -16
- package/dist/locales/zh-TW/openhands.json +32 -16
- package/dist/manifests/automation-interface.cjs +1 -1
- package/dist/manifests/automation-interface.cjs.map +1 -1
- package/dist/manifests/automation-interface.d.ts +6 -0
- package/dist/manifests/automation-interface.js +11 -8
- package/dist/manifests/automation-interface.js.map +1 -1
- package/dist/node_modules/@openhands/extensions/automations/bundle-index.cjs +45 -4
- package/dist/node_modules/@openhands/extensions/automations/bundle-index.cjs.map +1 -1
- package/dist/node_modules/@openhands/extensions/automations/bundle-index.js +1 -1
- package/dist/node_modules/@openhands/extensions/automations/bundle-index.js.map +1 -1
- package/dist/node_modules/@openhands/extensions/automations/catalog/github-agents-md-maintainer/manifest.cjs +1 -1
- package/dist/node_modules/@openhands/extensions/automations/catalog/github-agents-md-maintainer/manifest.cjs.map +1 -1
- package/dist/node_modules/@openhands/extensions/automations/catalog/github-agents-md-maintainer/manifest.js +5 -0
- package/dist/node_modules/@openhands/extensions/automations/catalog/github-agents-md-maintainer/manifest.js.map +1 -1
- package/dist/node_modules/@openhands/extensions/automations/catalog/github-issue-to-pr/manifest.cjs +1 -1
- package/dist/node_modules/@openhands/extensions/automations/catalog/github-issue-to-pr/manifest.cjs.map +1 -1
- package/dist/node_modules/@openhands/extensions/automations/catalog/github-issue-to-pr/manifest.js +5 -0
- package/dist/node_modules/@openhands/extensions/automations/catalog/github-issue-to-pr/manifest.js.map +1 -1
- package/dist/node_modules/@openhands/extensions/automations/catalog/github-pr-reviewer/manifest.cjs +1 -1
- package/dist/node_modules/@openhands/extensions/automations/catalog/github-pr-reviewer/manifest.cjs.map +1 -1
- package/dist/node_modules/@openhands/extensions/automations/catalog/github-pr-reviewer/manifest.js +5 -0
- package/dist/node_modules/@openhands/extensions/automations/catalog/github-pr-reviewer/manifest.js.map +1 -1
- package/dist/node_modules/@openhands/extensions/automations/catalog/github-repo-monitor/manifest.cjs +1 -1
- package/dist/node_modules/@openhands/extensions/automations/catalog/github-repo-monitor/manifest.cjs.map +1 -1
- package/dist/node_modules/@openhands/extensions/automations/catalog/github-repo-monitor/manifest.js +5 -0
- package/dist/node_modules/@openhands/extensions/automations/catalog/github-repo-monitor/manifest.js.map +1 -1
- package/dist/node_modules/@openhands/extensions/automations/catalog/jira-issue-to-bitbucket-pr/manifest.cjs +9 -0
- package/dist/node_modules/@openhands/extensions/automations/catalog/jira-issue-to-bitbucket-pr/manifest.cjs.map +1 -0
- package/dist/node_modules/@openhands/extensions/automations/catalog/jira-issue-to-bitbucket-pr/manifest.js +21 -0
- package/dist/node_modules/@openhands/extensions/automations/catalog/jira-issue-to-bitbucket-pr/manifest.js.map +1 -0
- package/dist/node_modules/@openhands/extensions/automations/catalog/jira-issue-to-gitlab-mr/manifest.cjs +9 -0
- package/dist/node_modules/@openhands/extensions/automations/catalog/jira-issue-to-gitlab-mr/manifest.cjs.map +1 -0
- package/dist/node_modules/@openhands/extensions/automations/catalog/jira-issue-to-gitlab-mr/manifest.js +21 -0
- package/dist/node_modules/@openhands/extensions/automations/catalog/jira-issue-to-gitlab-mr/manifest.js.map +1 -0
- package/dist/node_modules/@openhands/extensions/automations/catalog/linear-issue-to-bitbucket-pr/manifest.cjs +9 -0
- package/dist/node_modules/@openhands/extensions/automations/catalog/linear-issue-to-bitbucket-pr/manifest.cjs.map +1 -0
- package/dist/node_modules/@openhands/extensions/automations/catalog/linear-issue-to-bitbucket-pr/manifest.js +21 -0
- package/dist/node_modules/@openhands/extensions/automations/catalog/linear-issue-to-bitbucket-pr/manifest.js.map +1 -0
- package/dist/node_modules/@openhands/extensions/automations/catalog/linear-issue-to-github-pr/manifest.cjs +9 -0
- package/dist/node_modules/@openhands/extensions/automations/catalog/linear-issue-to-github-pr/manifest.cjs.map +1 -0
- package/dist/node_modules/@openhands/extensions/automations/catalog/linear-issue-to-github-pr/manifest.js +21 -0
- package/dist/node_modules/@openhands/extensions/automations/catalog/linear-issue-to-github-pr/manifest.js.map +1 -0
- package/dist/node_modules/@openhands/extensions/automations/catalog/linear-issue-to-gitlab-mr/manifest.cjs +9 -0
- package/dist/node_modules/@openhands/extensions/automations/catalog/linear-issue-to-gitlab-mr/manifest.cjs.map +1 -0
- package/dist/node_modules/@openhands/extensions/automations/catalog/linear-issue-to-gitlab-mr/manifest.js +21 -0
- package/dist/node_modules/@openhands/extensions/automations/catalog/linear-issue-to-gitlab-mr/manifest.js.map +1 -0
- package/dist/node_modules/@openhands/extensions/automations/catalog/news-digest/manifest.cjs +1 -1
- package/dist/node_modules/@openhands/extensions/automations/catalog/news-digest/manifest.cjs.map +1 -1
- package/dist/node_modules/@openhands/extensions/automations/catalog/news-digest/manifest.js +5 -0
- package/dist/node_modules/@openhands/extensions/automations/catalog/news-digest/manifest.js.map +1 -1
- package/dist/node_modules/@openhands/extensions/automations/catalog/qa-changes/manifest.cjs +30 -0
- package/dist/node_modules/@openhands/extensions/automations/catalog/qa-changes/manifest.cjs.map +1 -0
- package/dist/node_modules/@openhands/extensions/automations/catalog/qa-changes/manifest.js +65 -0
- package/dist/node_modules/@openhands/extensions/automations/catalog/qa-changes/manifest.js.map +1 -0
- package/dist/node_modules/@openhands/extensions/automations/catalog-index.cjs +1 -1
- package/dist/node_modules/@openhands/extensions/automations/catalog-index.cjs.map +1 -1
- package/dist/node_modules/@openhands/extensions/automations/catalog-index.js +21 -9
- package/dist/node_modules/@openhands/extensions/automations/catalog-index.js.map +1 -1
- package/dist/node_modules/@openhands/extensions/integrations/catalog/datadog.cjs +1 -1
- package/dist/node_modules/@openhands/extensions/integrations/catalog/datadog.cjs.map +1 -1
- package/dist/node_modules/@openhands/extensions/integrations/catalog/datadog.js +2 -2
- package/dist/node_modules/@openhands/extensions/integrations/catalog/datadog.js.map +1 -1
- package/dist/node_modules/@openhands/extensions/integrations/catalog/gitlab.cjs +2 -0
- package/dist/node_modules/@openhands/extensions/integrations/catalog/gitlab.cjs.map +1 -0
- package/dist/node_modules/@openhands/extensions/integrations/catalog/gitlab.js +40 -0
- package/dist/node_modules/@openhands/extensions/integrations/catalog/gitlab.js.map +1 -0
- package/dist/node_modules/@openhands/extensions/integrations/catalog/notion.cjs +1 -1
- package/dist/node_modules/@openhands/extensions/integrations/catalog/notion.cjs.map +1 -1
- package/dist/node_modules/@openhands/extensions/integrations/catalog/notion.js +1 -1
- package/dist/node_modules/@openhands/extensions/integrations/catalog/notion.js.map +1 -1
- package/dist/node_modules/@openhands/extensions/integrations/catalog-index.cjs +1 -1
- package/dist/node_modules/@openhands/extensions/integrations/catalog-index.cjs.map +1 -1
- package/dist/node_modules/@openhands/extensions/integrations/catalog-index.js +74 -72
- package/dist/node_modules/@openhands/extensions/integrations/catalog-index.js.map +1 -1
- package/dist/node_modules/@openhands/extensions/skills/index.cjs +42 -3
- package/dist/node_modules/@openhands/extensions/skills/index.cjs.map +1 -1
- package/dist/node_modules/@openhands/extensions/skills/index.js +22 -3
- package/dist/node_modules/@openhands/extensions/skills/index.js.map +1 -1
- package/dist/node_modules/@openhands/typescript-client/dist/conversation/remote-conversation.cjs +1 -1
- package/dist/node_modules/@openhands/typescript-client/dist/conversation/remote-conversation.js +1 -1
- package/dist/node_modules/@openhands/typescript-client/dist/conversation/remote-state.cjs +1 -1
- package/dist/node_modules/@openhands/typescript-client/dist/conversation/remote-state.cjs.map +1 -1
- package/dist/node_modules/@openhands/typescript-client/dist/conversation/remote-state.js +8 -7
- package/dist/node_modules/@openhands/typescript-client/dist/conversation/remote-state.js.map +1 -1
- package/dist/node_modules/@openhands/typescript-client/dist/index.cjs +1 -1
- package/dist/node_modules/@openhands/typescript-client/dist/index.js +2 -2
- package/dist/node_modules/lucide-react/dist/esm/icons/blocks.cjs +2 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/blocks.cjs.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/blocks.js +16 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/blocks.js.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/panels-top-left.cjs +2 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/panels-top-left.cjs.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/panels-top-left.js +23 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/panels-top-left.js.map +1 -0
- package/dist/package.cjs +1 -1
- package/dist/package.cjs.map +1 -1
- package/dist/package.js +3 -3
- package/dist/package.js.map +1 -1
- package/dist/routes/agent-settings.d.ts +10 -0
- package/dist/routes/app-settings.cjs +1 -1
- package/dist/routes/app-settings.cjs.map +1 -1
- package/dist/routes/app-settings.js +7 -7
- package/dist/routes/files-tab.cjs +1 -1
- package/dist/routes/files-tab.cjs.map +1 -1
- package/dist/routes/files-tab.js +14 -14
- package/dist/routes/files-tab.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 +122 -114
- package/dist/routes/llm-settings.js.map +1 -1
- package/dist/services/canvas-ui.cjs +1 -1
- package/dist/services/canvas-ui.cjs.map +1 -1
- package/dist/services/canvas-ui.d.ts +5 -0
- package/dist/services/canvas-ui.js +24 -10
- package/dist/services/canvas-ui.js.map +1 -1
- package/dist/services/settings.cjs +1 -1
- package/dist/services/settings.cjs.map +1 -1
- package/dist/services/settings.js +3 -2
- package/dist/services/settings.js.map +1 -1
- package/dist/types/automation.cjs.map +1 -1
- package/dist/types/automation.d.ts +20 -0
- package/dist/types/automation.js.map +1 -1
- package/dist/types/canvas-extension.cjs +2 -0
- package/dist/types/canvas-extension.cjs.map +1 -0
- package/dist/types/canvas-extension.d.ts +77 -0
- package/dist/types/canvas-extension.js +6 -0
- package/dist/types/canvas-extension.js.map +1 -0
- package/dist/types/settings.d.ts +7 -0
- package/dist/ui/help-link.cjs +1 -1
- package/dist/ui/help-link.cjs.map +1 -1
- package/dist/ui/help-link.d.ts +6 -1
- package/dist/ui/help-link.js +32 -18
- package/dist/ui/help-link.js.map +1 -1
- package/dist/utils/acp-auth-display.d.ts +35 -0
- package/dist/utils/cache-utils.cjs +1 -1
- package/dist/utils/cache-utils.js +5 -5
- 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 +7 -3
- package/dist/utils/format-model-name.js +6 -11
- package/dist/utils/format-model-name.js.map +1 -1
- package/dist/utils/format-relative-time.cjs +1 -1
- package/dist/utils/format-relative-time.cjs.map +1 -1
- package/dist/utils/format-relative-time.d.ts +7 -0
- package/dist/utils/format-relative-time.js +7 -2
- package/dist/utils/format-relative-time.js.map +1 -1
- package/dist/utils/path-utils.cjs +1 -1
- package/dist/utils/path-utils.cjs.map +1 -1
- package/dist/utils/path-utils.d.ts +15 -3
- package/dist/utils/path-utils.js +29 -3
- package/dist/utils/path-utils.js.map +1 -1
- package/dist/utils/skill-enablement.cjs +2 -0
- package/dist/utils/skill-enablement.cjs.map +1 -0
- package/dist/utils/skill-enablement.d.ts +51 -0
- package/dist/utils/skill-enablement.js +31 -0
- package/dist/utils/skill-enablement.js.map +1 -0
- package/dist/utils/vscode-origin.cjs +2 -0
- package/dist/utils/vscode-origin.cjs.map +1 -0
- package/dist/utils/vscode-origin.d.ts +55 -0
- package/dist/utils/vscode-origin.js +27 -0
- package/dist/utils/vscode-origin.js.map +1 -0
- package/dist/utils/workspace-mode.d.ts +4 -0
- package/package.json +3 -3
- package/scripts/check-sdk-version-sync.mjs +6 -6
- package/scripts/check-translation-completeness.cjs +1 -0
- package/scripts/dev-extra-backend.mjs +28 -9
- package/scripts/dev-safe.mjs +81 -8
- package/scripts/dev-static.mjs +35 -37
- package/scripts/dev-with-automation.mjs +82 -1
- package/scripts/ingress.mjs +27 -1
- package/scripts/static-server.mjs +74 -1
- package/tools/canvas_ui_tool.py +19 -0
- package/build/assets/active-backend-context-DKFSMbn3.js +0 -2
- package/build/assets/agent-server-conversation-service.api-DozDKIW_.js +0 -86
- package/build/assets/agent-settings-351vXWc9.js +0 -2
- package/build/assets/agent-settings-Ce7BDZ6I.js +0 -1
- package/build/assets/alert-banner-Kyy1YWZU.js +0 -1
- package/build/assets/api-key-entry-screen-INIxqAMs.js +0 -1
- package/build/assets/automation-detail-DXDhUwPq.js +0 -3
- package/build/assets/automation-setup-route-FRr05f_g.js +0 -1
- package/build/assets/automations-list-CERuQWE6.js +0 -1
- package/build/assets/backend-form-modal-DK78-jn1.js +0 -1
- package/build/assets/browser-11etCQ04.js +0 -7
- package/build/assets/check-Cf2YoT90.js +0 -1
- package/build/assets/chevron-down-C3OQ-ehx.js +0 -1
- package/build/assets/chevron-right-CN1uUiJ1.js +0 -1
- package/build/assets/circle-check-g5yeGMd-.js +0 -1
- package/build/assets/command-menu-BZQ8S3Ke.js +0 -1
- package/build/assets/conversation-CchMzpGl.js +0 -1
- package/build/assets/conversation-Dpzftz57.js +0 -93
- package/build/assets/conversation-panel-CchMzpGl.js +0 -1
- package/build/assets/conversation-state-store-a9d2fxRc.js +0 -1
- package/build/assets/empty-state-CvaVahVr.js +0 -1
- package/build/assets/extensions-hub-CFduxBnk.js +0 -1
- package/build/assets/extensions-navigation-BeC224yX.js +0 -1
- package/build/assets/files-tab-CL3J9b3p.js +0 -1
- package/build/assets/format-model-name-BNPKVsIq.js +0 -1
- package/build/assets/get-git-path-DlAJa_i6.js +0 -1
- package/build/assets/git-repo-dropdown-DGMEhz7d.js +0 -1
- package/build/assets/home-BCGlNJCA.js +0 -1
- package/build/assets/home-DB1pRqTC.js +0 -1
- package/build/assets/home-automation-run-tooltip-CooFQRIG.js +0 -1
- package/build/assets/home-store-Dl1dPE58.js +0 -1
- package/build/assets/index-home-DB1pRqTC.js +0 -1
- package/build/assets/info-uYSVzX86.js +0 -1
- package/build/assets/install-server-modal-BbJdhzet.js +0 -1
- package/build/assets/kebab-menu-item-content-BYokkRHi.js +0 -1
- package/build/assets/list-filter-OFkpRrmi.js +0 -1
- package/build/assets/llm-not-configured-banner-BqqYnJ1t.js +0 -27
- package/build/assets/llm-settings-CnDbbmVW.js +0 -1
- package/build/assets/llm-settings-S8Yjg__1.js +0 -1
- package/build/assets/llm-subscription-service-BpPfzW4x.js +0 -1
- package/build/assets/loader-circle-nvrwthvj.js +0 -1
- package/build/assets/manage-backends-modal-BCUPswyl.js +0 -1
- package/build/assets/manifest-58597dde.js +0 -1
- package/build/assets/markdown-renderer-Cb2sBWtV.js +0 -8
- package/build/assets/messages-BTN5thLe.js +0 -40
- package/build/assets/onboarding-Cx_kaiD1.js +0 -1
- package/build/assets/onboarding-modal-vMwn7ihv.js +0 -1
- package/build/assets/plugins-management-service-BjYeF0b0.js +0 -1
- package/build/assets/plus-CEKne-Yp.js +0 -1
- package/build/assets/profiles-service.api-QB-T_u-g.js +0 -1
- package/build/assets/providers-FfSZ5TXP.js +0 -1
- package/build/assets/proxy-tcFL2yTQ.js +0 -1
- package/build/assets/recommended-automations-launcher-Bw8uC4V6.js +0 -1
- package/build/assets/root-BLeQfBEq.css +0 -1
- package/build/assets/root-ZKKYPR1Z.js +0 -2
- package/build/assets/root-layout-luJoE9IG.js +0 -2
- package/build/assets/schema-field-Csw5fhtN.js +0 -1
- package/build/assets/secrets-service-BTor9uAU.js +0 -1
- package/build/assets/settings-DcNPxz0v.js +0 -1
- package/build/assets/settings-service.api-B2agVfjL.js +0 -1
- package/build/assets/shared-conversation-B55pCyBG.js +0 -1
- package/build/assets/sidebar-nav-link-CPzhyHQg.js +0 -1
- package/build/assets/sidebar-store-DtpU7zaI.js +0 -1
- package/build/assets/skill-filter-CWZ5kjcg.js +0 -2
- package/build/assets/skill-scope-CV1dkAbW.js +0 -1
- package/build/assets/skills-plugins-DGHULTq6.js +0 -1
- package/build/assets/skills-settings-CM1lFv3e.js +0 -1
- package/build/assets/usage-tab-BcfWV6db.js +0 -1
- package/build/assets/use-acp-credential-form-BsSW5NHG.js +0 -1
- package/build/assets/use-agent-profiles-DWBPC0Si.js +0 -1
- package/build/assets/use-config-Bft35DOK.js +0 -1
- package/build/assets/use-create-secret-BSxJMxHp.js +0 -1
- package/build/assets/use-is-authed-CFGkY3-Z.js +0 -1
- package/build/assets/use-llm-configured-BI6obxsc.js +0 -1
- package/build/assets/use-llm-profiles-djGAhTWL.js +0 -1
- package/build/assets/use-manifest-capabilities-3z0-1G59.js +0 -1
- package/build/assets/use-onboarding-completion-BXpyUSsJ.js +0 -1
- package/build/assets/use-plugins-marketplace-DI4_pK56.js +0 -1
- package/build/assets/use-save-agent-profile-DdBSTdpN.js +0 -1
- package/build/assets/use-tracking-D5t9otIF.js +0 -3
- package/build/assets/utils-DaCIEEQP.js +0 -1
- package/build/assets/vendor~index-home~home~conversation-panel~conversation~alert-banner-B3qC_oj8.js +0 -1
- package/build/assets/vendor~root-layout~index-home~home~conversation-panel~conversation~mcp~automations-list~aut~ibfbmr9f-B2OLKL7N.js +0 -1
- package/dist/components/features/tips/random-tip.d.ts +0 -2
- package/dist/utils/tips.d.ts +0 -7
- package/dist/utils/workspace-relative-path.cjs +0 -2
- package/dist/utils/workspace-relative-path.cjs.map +0 -1
- package/dist/utils/workspace-relative-path.d.ts +0 -10
- package/dist/utils/workspace-relative-path.js +0 -22
- package/dist/utils/workspace-relative-path.js.map +0 -1
- /package/build/assets/{vendor~entry.client~root~root-layout~index-home~home~conversation-panel~conversation~launch~ew3u153i-rfV8mQ9m.js → vendor~entry.client~root~root-layout~index-home~home~conversation-panel~conversation~launch~ew9ih1s8-rfV8mQ9m.js} +0 -0
- /package/build/assets/{vendor~entry.client~root~root-layout~index-home~home~conversation-panel~conversation~launch~ox0jfsa0-cHFhpUBq.js → vendor~entry.client~root~root-layout~index-home~home~conversation-panel~conversation~launch~i58xkef6-cHFhpUBq.js} +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chat-interface.js","names":[],"sources":["../../../../src/components/features/chat/chat-interface.tsx"],"sourcesContent":["import React from \"react\";\nimport { useNavigate } from \"react-router\";\nimport { useTracking } from \"#/hooks/use-tracking\";\nimport { useTranslation } from \"react-i18next\";\nimport { isAcpAuthErrorCode } from \"#/utils/acp-error-codes\";\nimport { convertImageToBase64 } from \"#/utils/convert-image-to-base-64\";\nimport { createChatMessage } from \"#/services/chat-service\";\nimport { BtwMessages } from \"./btw-messages\";\nimport { GoalStatusBanner } from \"./goal-status-banner\";\nimport { ModelMessages } from \"./model-messages\";\nimport { useModelStore } from \"#/stores/model-store\";\nimport { useGoalStore } from \"#/stores/goal-store\";\nimport { InteractiveChatBox } from \"./interactive-chat-box\";\nimport { AgentState } from \"#/types/agent-state\";\nimport { useFilteredEvents } from \"#/hooks/use-filtered-events\";\nimport { useScrollToBottom } from \"#/hooks/use-scroll-to-bottom\";\nimport { useLoadOlderEvents } from \"#/hooks/use-load-older-events\";\nimport { TypingIndicator } from \"./typing-indicator\";\nimport { ChatSuggestions } from \"./chat-suggestions\";\nimport { ScrollProvider } from \"#/context/scroll-context\";\nimport { useInitialQueryStore } from \"#/stores/initial-query-store\";\nimport { useSendMessage } from \"#/hooks/use-send-message\";\nimport { useAgentState } from \"#/hooks/use-agent-state\";\nimport { useIsArchivedConversation } from \"#/hooks/use-is-archived-conversation\";\nimport { useHandleBuildPlanClick } from \"#/hooks/use-handle-build-plan-click\";\n\nimport { ScrollToBottomButton } from \"#/components/shared/buttons/scroll-to-bottom-button\";\nimport { LoadingSpinner } from \"#/components/shared/loading-spinner\";\nimport { ChatMessagesSkeleton } from \"./chat-messages-skeleton\";\nimport { displayErrorToast } from \"#/utils/custom-toast-handlers\";\nimport { useErrorMessageStore } from \"#/stores/error-message-store\";\nimport { useOptimisticUserMessageStore } from \"#/stores/optimistic-user-message-store\";\nimport { SERVER_CONNECTION_ERROR_MESSAGE } from \"#/constants/server-connection-error\";\nimport { ErrorMessageBanner } from \"./error-message-banner\";\nimport { SkillInstallRestartBanner } from \"./skill-install-restart-banner\";\nimport { LlmNotConfiguredBanner } from \"#/components/features/home/llm-not-configured-banner\";\nimport { useLlmConfigured } from \"#/hooks/use-llm-configured\";\nimport { Messages } from \"#/components/conversation-events/chat/messages\";\nimport { PendingUserMessages } from \"./pending-user-messages\";\nimport { useUnifiedUploadFiles } from \"#/hooks/mutation/use-unified-upload-files\";\nimport { validateFiles } from \"#/utils/file-validation\";\nimport { useConversationStore } from \"#/stores/conversation-store\";\nimport ConfirmationModeEnabled from \"./confirmation-mode-enabled\";\nimport { useTaskPolling } from \"#/hooks/query/use-task-polling\";\nimport { matchesPendingConversationId } from \"#/utils/pending-task-message-link\";\nimport { useConversationWebSocket } from \"#/contexts/conversation-websocket-context\";\nimport ChatStatusIndicator from \"./chat-status-indicator\";\nimport { getStatusColor, getStatusText } from \"#/utils/utils\";\nimport { useNewConversationCommand } from \"#/hooks/mutation/use-new-conversation-command\";\nimport { useOptionalConversationId } from \"#/hooks/use-conversation-id\";\nimport { useActiveConversation } from \"#/hooks/query/use-active-conversation\";\nimport { I18nKey } from \"#/i18n/declaration\";\nimport { hasConversationStarted } from \"./components/resolve-picker-kind\";\n\nfunction getEntryPoint(\n hasRepository: boolean | null,\n hasReplayJson: boolean | null,\n): string {\n if (hasRepository) return \"github\";\n if (hasReplayJson) return \"replay\";\n return \"direct\";\n}\n\nexport function ChatInterface() {\n const { trackInitialQuerySubmitted, trackUserMessageSent } = useTracking();\n const { setMessageToSend, conversationMode, planContent } =\n useConversationStore();\n const {\n errorMessage,\n errorCode,\n errorClassification,\n removeErrorMessage,\n setErrorMessage,\n } = useErrorMessageStore();\n const navigate = useNavigate();\n const { isTask, taskStatus, taskDetail } = useTaskPolling();\n // Hide empty-state chrome for the entire `/conversations/task-{uuid}` route,\n // including the brief READY window before redirect completes.\n const isProvisioningTask = isTask;\n const conversationWebSocket = useConversationWebSocket();\n const { send } = useSendMessage();\n const {\n renderableEvents,\n allConversationEvents,\n totalEvents,\n hasSubstantiveAgentActions,\n userEventsExist,\n } = useFilteredEvents();\n const enqueuePendingMessage = useOptimisticUserMessageStore(\n (state) => state.enqueuePendingMessage,\n );\n const markPendingMessageError = useOptimisticUserMessageStore(\n (state) => state.markPendingMessageError,\n );\n const pendingMessages = useOptimisticUserMessageStore(\n (state) => state.pendingMessages,\n );\n const { t } = useTranslation(\"openhands\");\n const scrollRef = React.useRef<HTMLDivElement>(null);\n const {\n scrollDomToBottom,\n onChatBodyScroll,\n hitBottom,\n autoScroll,\n setAutoScroll,\n setHitBottom,\n } = useScrollToBottom(scrollRef);\n const {\n mutate: newConversationCommand,\n isPending: isNewConversationPending,\n } = useNewConversationCommand();\n\n const { curAgentState } = useAgentState();\n const { handleBuildPlanClick } = useHandleBuildPlanClick();\n\n // Cloud conversations whose sandbox is MISSING or ERROR are read-only:\n // the sandbox is gone and cannot be resumed, so we hide the chat input\n // and show an explanatory banner. For local backends sandbox_status is\n // always null, so this is effectively a no-op for non-cloud use.\n const { data: activeConversation } = useActiveConversation();\n const sandboxStatus = activeConversation?.sandbox_status ?? null;\n const isArchivedConversation = useIsArchivedConversation();\n\n // Block sending in a resumed conversation that has no usable LLM, and show\n // the same setup banner as the home screen so the dead end is explained.\n const { isConfigured: isLlmConfigured, isLoading: isLlmConfigLoading } =\n useLlmConfigured();\n const llmBlocked = !isLlmConfigLoading && !isLlmConfigured;\n\n // Disable Build button while agent is running (streaming)\n const isAgentRunning =\n curAgentState === AgentState.RUNNING ||\n curAgentState === AgentState.LOADING;\n\n // Global keyboard shortcut for Build button (Cmd+Enter / Ctrl+Enter)\n // This is placed here instead of PlanPreview to avoid duplicate listeners\n // when multiple PlanPreview components exist in the chat.\n // Gated on the same conditions as the Build button (ConversationTabs'\n // `isBuildDisabled`) so it cannot fire outside the plan flow.\n React.useEffect(() => {\n if (isAgentRunning || conversationMode !== \"plan\" || !planContent) {\n return undefined;\n }\n\n const handleKeyDown = (event: KeyboardEvent) => {\n // Check for Cmd+Enter (Mac) or Ctrl+Enter (Windows/Linux)\n if ((event.metaKey || event.ctrlKey) && event.key === \"Enter\") {\n event.preventDefault();\n event.stopPropagation();\n handleBuildPlanClick(event);\n scrollDomToBottom();\n }\n };\n\n document.addEventListener(\"keydown\", handleKeyDown);\n\n return () => {\n document.removeEventListener(\"keydown\", handleKeyDown);\n };\n }, [\n isAgentRunning,\n conversationMode,\n planContent,\n handleBuildPlanClick,\n scrollDomToBottom,\n ]);\n\n const { selectedRepository, replayJson } = useInitialQueryStore();\n const { conversationId } = useOptionalConversationId();\n\n // The live goal banner renders in the scroll stream but advances via store\n // updates (in-progress goal events are filtered out of `renderableEvents`),\n // so the bottom-following effect would not react to it. This key changes as\n // an active loop appears and advances each round; feeding it into that effect\n // keeps the banner in view when the user is pinned to the bottom.\n const activeGoalScrollKey = useGoalStore((s) => {\n const goal = conversationId\n ? s.statusByConversation[conversationId]\n : undefined;\n return goal?.active ? `${goal.iteration}:${goal.status}` : null;\n });\n const { mutateAsync: uploadFiles } = useUnifiedUploadFiles();\n\n // Lazy \"scroll up to load older events\" backfill. Initial REST fetch only\n // returns the most recent page; this hook paginates older events into the\n // store on demand so the chat doesn't load (potentially) thousands of\n // events on first render.\n const {\n isLoading: isLoadingOlderEvents,\n hasMore: hasMoreOlderEvents,\n loadOlder,\n } = useLoadOlderEvents(conversationId);\n\n // Trigger `loadOlder` and preserve the visual scroll position once the\n // older page is merged in (otherwise prepending events would jump the\n // chat far down). We fire from three places to cover the cases the\n // browser's scroll event misses:\n //\n // - `onScroll`: normal \"user scrolled near the top\" path.\n // - `onWheel`: user is already pinned at scrollTop=0 and tries to\n // wheel further up — no scroll event fires past 0.\n // - effect: content is shorter than the viewport (no scrollbar\n // at all), so the user has nothing to scroll. Re-runs\n // as more pages arrive until there's overflow or the\n // server runs out of older events.\n const SCROLL_TOP_THRESHOLD_PX = 80;\n const preserveScrollPosition = React.useRef<{\n scrollHeight: number;\n scrollTop: number;\n } | null>(null);\n const maybeLoadOlder = React.useCallback(\n (target: HTMLElement) => {\n if (isProvisioningTask || isLoadingOlderEvents || !hasMoreOlderEvents) {\n return;\n }\n\n const atTop = target.scrollTop <= SCROLL_TOP_THRESHOLD_PX;\n const noOverflow =\n target.scrollHeight <= target.clientHeight + SCROLL_TOP_THRESHOLD_PX;\n if (!atTop && !noOverflow) return;\n\n preserveScrollPosition.current = {\n scrollHeight: target.scrollHeight,\n scrollTop: target.scrollTop,\n };\n loadOlder().catch((error) => {\n preserveScrollPosition.current = null;\n const message =\n error instanceof Error && error.message\n ? error.message\n : t(I18nKey.ERROR$GENERIC);\n setErrorMessage(message);\n });\n },\n [\n hasMoreOlderEvents,\n isLoadingOlderEvents,\n isProvisioningTask,\n loadOlder,\n setErrorMessage,\n t,\n ],\n );\n\n const handleWheelForPagination = React.useCallback(\n (e: React.WheelEvent<HTMLDivElement>) => {\n // Browsers don't dispatch a scroll event when scrollTop is already\n // 0 and the user wheels upward, so onScroll alone misses this case.\n if (e.deltaY < 0 && e.currentTarget.scrollTop <= 0) {\n maybeLoadOlder(e.currentTarget);\n }\n },\n [maybeLoadOlder],\n );\n\n const hasPendingUserMessages = React.useMemo(\n () =>\n conversationId\n ? pendingMessages.some((message) =>\n matchesPendingConversationId(\n conversationId,\n message.conversationId,\n ),\n )\n : false,\n [pendingMessages, conversationId],\n );\n\n const hasModelEntries = useModelStore((s) =>\n conversationId\n ? (s.entriesByConversation[conversationId]?.length ?? 0) > 0\n : false,\n );\n const hasStartedConversation = hasConversationStarted({\n isLoadingHistory: conversationWebSocket?.isLoadingHistory === true,\n hasUserEvents: userEventsExist,\n hasPendingUserMessages,\n hasSubstantiveAgentActions,\n hasModelEntries,\n });\n\n // Show V1 messages immediately if events exist in store (e.g., remount),\n // if the user already has a locally-tracked pending bubble (home-page cloud\n // submit while history/WS catch up), or once loading completes. This\n // replaces the old transition-observation pattern (useState + useEffect\n // watching loading→loaded) which always showed skeleton on remount because\n // local state initialized to false.\n const showConversationMessages =\n allConversationEvents.length > 0 ||\n hasPendingUserMessages ||\n !conversationWebSocket?.isLoadingHistory;\n\n const isReturningToConversation = !!conversationId;\n // Only show loading skeleton when genuinely loading AND no events in store yet.\n // If events exist (e.g., remount after data was already fetched), skip skeleton.\n const isHistoryLoading = !showConversationMessages;\n const isChatLoading = isHistoryLoading && !isTask;\n\n const handleSendMessage = async (\n content: string,\n originalImages: File[],\n originalFiles: File[],\n ) => {\n // Handle /new command for V1 conversations\n if (content.trim() === \"/new\") {\n if (!conversationId) {\n displayErrorToast(t(I18nKey.CONVERSATION$CLEAR_NO_ID));\n return;\n }\n if (totalEvents === 0) {\n displayErrorToast(t(I18nKey.CONVERSATION$CLEAR_EMPTY));\n return;\n }\n if (isNewConversationPending) {\n return;\n }\n newConversationCommand();\n return;\n }\n\n // Create mutable copies of the arrays\n const images = [...originalImages];\n const files = [...originalFiles];\n if (totalEvents === 0) {\n trackInitialQuerySubmitted({\n entryPoint: getEntryPoint(\n selectedRepository !== null,\n replayJson !== null,\n ),\n queryCharacterLength: content.length,\n replayJsonSize: replayJson?.length,\n });\n } else {\n trackUserMessageSent({\n sessionMessageCount: totalEvents,\n currentMessageLength: content.length,\n });\n }\n\n // Validate file sizes before any processing\n const allFiles = [...images, ...files];\n const validation = validateFiles(allFiles);\n\n if (!validation.isValid) {\n displayErrorToast(`Error: ${validation.errorMessage}`);\n return; // Stop processing if validation fails\n }\n\n const promises = images.map((image) => convertImageToBase64(image));\n const imageUrls = await Promise.all(promises);\n\n const timestamp = new Date().toISOString();\n\n const { skipped_files: skippedFiles, uploaded_files: uploadedFiles } =\n files.length > 0\n ? await uploadFiles({ conversationId: conversationId!, files })\n : { skipped_files: [], uploaded_files: [] };\n\n skippedFiles.forEach((f) => displayErrorToast(f.reason));\n\n const filePrompt = `${t(I18nKey.CHAT_INTERFACE$AUGMENTED_PROMPT_FILES_TITLE)}: ${uploadedFiles.join(\"\\n\\n\")}`;\n const prompt =\n uploadedFiles.length > 0 ? `${content}\\n\\n${filePrompt}` : content;\n\n // Enqueue the message into the local pending queue with status \"sending\"\n // so the user immediately sees it in the chat with a faded treatment. The\n // entry is removed when the WebSocket echoes back the corresponding\n // `UserMessageEvent`. If the API call to send the message fails, the entry\n // is flipped to \"error\" with a retry link.\n const pendingId = enqueuePendingMessage({\n conversationId: conversationId!,\n // `text` is what the user sees in the bubble; `content` is what we\n // actually hand to the server (the prompt may include an appended\n // \"Files uploaded: …\" block) and is what the echo will be matched\n // against. They're different when there are file attachments.\n text: content,\n content: prompt,\n imageUrls,\n fileUrls: uploadedFiles,\n timestamp,\n });\n // Submitting a new prompt should always pull the chat back to the\n // latest message even if the user had scrolled up. This also re-arms\n // autoScroll so the streamed agent reply auto-follows.\n scrollDomToBottom();\n setMessageToSend(\"\");\n\n try {\n await send(\n createChatMessage(prompt, imageUrls, uploadedFiles, timestamp),\n );\n } catch (sendError) {\n const sendErrorMessage =\n sendError instanceof Error\n ? sendError.message\n : t(I18nKey.CHAT_INTERFACE$FAILED_TO_SEND_MESSAGE);\n markPendingMessageError(pendingId, sendErrorMessage);\n }\n };\n\n // Auto-scroll to bottom when new messages arrive — but only if the user is\n // already pinned to the bottom. Scrolling up to load older events also\n // grows `renderableEvents`, and we don't want to yank the user back to the\n // bottom in that case.\n React.useEffect(() => {\n // If a \"load older\" was just triggered, restore the scroll position so\n // the conversation appears to extend upward instead of jumping.\n if (preserveScrollPosition.current && scrollRef.current) {\n const { scrollHeight: prevHeight, scrollTop: prevTop } =\n preserveScrollPosition.current;\n const dom = scrollRef.current;\n const delta = dom.scrollHeight - prevHeight;\n if (delta > 0) {\n dom.scrollTop = prevTop + delta;\n }\n preserveScrollPosition.current = null;\n return;\n }\n\n if (autoScroll) {\n scrollDomToBottom();\n }\n // Note: We intentionally exclude autoScroll from deps because we only want\n // to scroll when message content changes, not when autoScroll state changes.\n }, [\n renderableEvents.length,\n hasPendingUserMessages,\n activeGoalScrollKey,\n scrollDomToBottom,\n ]);\n\n // Auto-load older events when the chat content doesn't overflow the\n // scroll area (no scrollbar to drag, no wheel events past 0). We\n // re-run only when the rendered list grows or `hasMore` flips, NOT\n // when `maybeLoadOlder` re-creates: the underlying hook's `loadOlder`\n // ref changes whenever its internal `isLoading` toggles, so depending\n // on `maybeLoadOlder` would re-fire the effect on every failed page\n // and tight-loop until the server recovered. Driving off\n // `renderableEvents.length` instead means a successful page (events\n // grow) chains the next request, while a failed page (events\n // unchanged) waits for the user to retry.\n const maybeLoadOlderRef = React.useRef(maybeLoadOlder);\n React.useEffect(() => {\n maybeLoadOlderRef.current = maybeLoadOlder;\n });\n React.useEffect(() => {\n const target = scrollRef.current;\n if (!target) return;\n maybeLoadOlderRef.current(target);\n }, [renderableEvents.length, hasMoreOlderEvents]);\n\n // Create a ScrollProvider with the scroll hook values\n const scrollProviderValue = {\n scrollRef,\n autoScroll,\n setAutoScroll,\n scrollDomToBottom,\n hitBottom,\n setHitBottom,\n onChatBodyScroll,\n };\n\n // Get server status indicator props\n const isStartingStatus =\n curAgentState === AgentState.LOADING || curAgentState === AgentState.INIT;\n const isStopStatus = curAgentState === AgentState.STOPPED;\n const isPausing = curAgentState === AgentState.PAUSED;\n const serverStatusColor = getStatusColor({\n isPausing,\n isTask,\n taskStatus,\n isStartingStatus,\n isStopStatus,\n curAgentState,\n });\n const serverStatusText = getStatusText({\n isPausing,\n isTask,\n taskStatus,\n taskDetail,\n isStartingStatus,\n isStopStatus,\n curAgentState,\n errorMessage,\n t,\n });\n\n return (\n <ScrollProvider value={scrollProviderValue}>\n <div\n className=\"relative flex h-full flex-col justify-between px-4\"\n data-testid=\"chat-interface\"\n >\n {!hasSubstantiveAgentActions &&\n !hasPendingUserMessages &&\n !userEventsExist &&\n !hasModelEntries &&\n !isChatLoading &&\n !isProvisioningTask &&\n totalEvents === 0 &&\n !isArchivedConversation &&\n // With no usable LLM the suggestions can't be acted on (the input is\n // disabled). They're also a `pointer-events-auto` overlay that would\n // sit over the LlmNotConfiguredBanner below and swallow clicks on its\n // setup button — so hide them and let the banner be the lone CTA.\n !llmBlocked && (\n <ChatSuggestions\n onSuggestionsClick={(message) => setMessageToSend(message)}\n />\n )}\n {/* Note: We only hide chat suggestions when there's a user message */}\n\n <div\n ref={scrollRef}\n data-testid=\"chat-scroll-container\"\n onScroll={(e) => {\n onChatBodyScroll(e.currentTarget);\n maybeLoadOlder(e.currentTarget);\n }}\n onWheel={handleWheelForPagination}\n className=\"custom-scrollbar-always flex min-h-0 grow flex-col gap-2 overflow-x-hidden overflow-y-auto px-0 pt-4 pb-8 md:px-4\"\n >\n {isChatLoading && isReturningToConversation && (\n <ChatMessagesSkeleton />\n )}\n\n {isChatLoading && !isReturningToConversation && (\n <div className=\"flex justify-center\" data-testid=\"loading-spinner\">\n <LoadingSpinner size=\"small\" />\n </div>\n )}\n\n {isLoadingOlderEvents && (\n <div\n className=\"flex items-center justify-center gap-2 py-3 text-sm text-neutral-400\"\n data-testid=\"loading-older-events\"\n >\n <LoadingSpinner size=\"small\" />\n <span>{t(I18nKey.CHAT_INTERFACE$FETCHING_OLDER_MESSAGES)}</span>\n </div>\n )}\n\n {/*\n * Render whenever there's anything to display. Previously this\n * was gated on `conversationUserEventsExist`, but with the lazy\n * \"50 most recent\" REST fetch the initial window may not include\n * any `source: \"user\"` events (long agent runs between user\n * turns). That left the chat blank, leaving the user nothing to\n * scroll — which is why \"scroll up to load older\" appeared\n * broken. The empty-state ChatSuggestions block above still\n * keeps its own gate (`!userEventsExist && !hasSubstantiveAgentActions`)\n * so brand-new conversations show suggestions, not an empty chat.\n */}\n {/* /model entries created before any event is rendered are\n anchored to `null` and live above the message list. */}\n <ModelMessages conversationId={conversationId} anchorEventId={null} />\n\n {showConversationMessages && renderableEvents.length > 0 && (\n <Messages\n messages={renderableEvents}\n allEvents={allConversationEvents}\n />\n )}\n\n {/*\n Render the local pending-message queue independently so messages\n the user just submitted show up immediately (with a faded \"sending\"\n treatment) even before any real conversation event has come back\n from the server. Entries drain (FIFO) when the matching\n UserMessageEvent echoes back over the WebSocket, so this never\n double-renders alongside the real event list.\n */}\n <PendingUserMessages />\n\n {/* Goal-loop status sits at the end of the message flow — above the\n composer and its typing indicator — so progress stays in view. */}\n <GoalStatusBanner conversationId={conversationId} />\n </div>\n\n <div className=\"flex shrink-0 flex-col gap-[6px] pb-4\">\n <SkillInstallRestartBanner conversationId={conversationId} />\n <BtwMessages conversationId={conversationId} />\n {errorMessage && (\n <ErrorMessageBanner\n message={errorMessage}\n code={errorCode}\n classification={errorClassification}\n onDismiss={removeErrorMessage}\n onRetry={\n errorMessage === SERVER_CONNECTION_ERROR_MESSAGE\n ? () => conversationWebSocket?.reconnect()\n : undefined\n }\n onReauth={\n isAcpAuthErrorCode(errorCode)\n ? () => navigate(\"/settings/agents\")\n : undefined\n }\n />\n )}\n\n {llmBlocked && !isArchivedConversation && <LlmNotConfiguredBanner />}\n\n {isArchivedConversation ? (\n // Archived / sandbox-error: show a read-only notice in place of\n // the chat input. The conversation history above is still visible.\n <div\n data-testid=\"archived-conversation-banner\"\n className=\"mx-1 px-4 py-3 rounded-lg bg-[var(--oh-surface)] border border-[var(--oh-border-subtle)]\"\n >\n <p className=\"text-xs font-semibold text-[var(--oh-foreground)]\">\n {sandboxStatus === \"ERROR\"\n ? t(I18nKey.CHAT_INTERFACE$ERROR_SANDBOX_TITLE)\n : t(I18nKey.CHAT_INTERFACE$ARCHIVED_SANDBOX_TITLE)}\n </p>\n <p className=\"text-xs text-[var(--oh-muted)] mt-0.5\">\n {sandboxStatus === \"ERROR\"\n ? t(I18nKey.CHAT_INTERFACE$ERROR_SANDBOX_DESCRIPTION)\n : t(I18nKey.CHAT_INTERFACE$ARCHIVED_SANDBOX_DESCRIPTION)}\n </p>\n </div>\n ) : (\n <div className=\"relative\">\n <div className=\"pointer-events-none absolute inset-x-0 bottom-full mb-1 z-20\">\n <div className=\"flex justify-between relative\">\n <div className=\"flex items-end gap-1 pointer-events-auto\">\n <ConfirmationModeEnabled />\n {isStartingStatus && (\n <ChatStatusIndicator\n statusColor={serverStatusColor}\n status={serverStatusText}\n />\n )}\n </div>\n\n {!hitBottom ? (\n <div className=\"absolute left-1/2 transform -translate-x-1/2 bottom-0 pointer-events-auto\">\n <ScrollToBottomButton onClick={scrollDomToBottom} />\n </div>\n ) : (\n curAgentState === AgentState.RUNNING && (\n <div className=\"pointer-events-none absolute inset-x-9 bottom-0 flex justify-center\">\n <TypingIndicator events={allConversationEvents} />\n </div>\n )\n )}\n </div>\n </div>\n\n <InteractiveChatBox\n onSubmit={handleSendMessage}\n disabled={isNewConversationPending || llmBlocked}\n hasStartedConversation={hasStartedConversation}\n />\n </div>\n )}\n </div>\n </div>\n </ScrollProvider>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"chat-interface.js","names":[],"sources":["../../../../src/components/features/chat/chat-interface.tsx"],"sourcesContent":["import React from \"react\";\nimport { useNavigate } from \"react-router\";\nimport { useTracking } from \"#/hooks/use-tracking\";\nimport { useTranslation } from \"react-i18next\";\nimport { isAcpAuthErrorCode } from \"#/utils/acp-error-codes\";\nimport { convertImageToBase64 } from \"#/utils/convert-image-to-base-64\";\nimport { createChatMessage } from \"#/services/chat-service\";\nimport { BtwMessages } from \"./btw-messages\";\nimport { GoalStatusBanner } from \"./goal-status-banner\";\nimport { ModelMessages } from \"./model-messages\";\nimport { useModelStore } from \"#/stores/model-store\";\nimport { useGoalStore } from \"#/stores/goal-store\";\nimport { InteractiveChatBox } from \"./interactive-chat-box\";\nimport { AgentState } from \"#/types/agent-state\";\nimport { useFilteredEvents } from \"#/hooks/use-filtered-events\";\nimport { useScrollToBottom } from \"#/hooks/use-scroll-to-bottom\";\nimport { useLoadOlderEvents } from \"#/hooks/use-load-older-events\";\nimport { useAutoRefreshFilesOnEdit } from \"#/hooks/use-auto-refresh-files-on-edit\";\nimport { WorkspaceFilesForChatProvider } from \"./chat-markdown-path-code\";\nimport { TypingIndicator } from \"./typing-indicator\";\nimport { ChatSuggestions } from \"./chat-suggestions\";\nimport { ScrollProvider } from \"#/context/scroll-context\";\nimport { useInitialQueryStore } from \"#/stores/initial-query-store\";\nimport { useSendMessage } from \"#/hooks/use-send-message\";\nimport { useAgentState } from \"#/hooks/use-agent-state\";\nimport { useIsArchivedConversation } from \"#/hooks/use-is-archived-conversation\";\nimport { useHandleBuildPlanClick } from \"#/hooks/use-handle-build-plan-click\";\n\nimport { ScrollToBottomButton } from \"#/components/shared/buttons/scroll-to-bottom-button\";\nimport { LoadingSpinner } from \"#/components/shared/loading-spinner\";\nimport { ChatMessagesSkeleton } from \"./chat-messages-skeleton\";\nimport { displayErrorToast } from \"#/utils/custom-toast-handlers\";\nimport { useErrorMessageStore } from \"#/stores/error-message-store\";\nimport { useOptimisticUserMessageStore } from \"#/stores/optimistic-user-message-store\";\nimport { SERVER_CONNECTION_ERROR_MESSAGE } from \"#/constants/server-connection-error\";\nimport { ErrorMessageBanner } from \"./error-message-banner\";\nimport { SkillInstallRestartBanner } from \"./skill-install-restart-banner\";\nimport { LlmNotConfiguredBanner } from \"#/components/features/home/llm-not-configured-banner\";\nimport { useLlmConfigured } from \"#/hooks/use-llm-configured\";\nimport { Messages } from \"#/components/conversation-events/chat/messages\";\nimport { PendingUserMessages } from \"./pending-user-messages\";\nimport { useUnifiedUploadFiles } from \"#/hooks/mutation/use-unified-upload-files\";\nimport { validateFiles } from \"#/utils/file-validation\";\nimport { useConversationStore } from \"#/stores/conversation-store\";\nimport ConfirmationModeEnabled from \"./confirmation-mode-enabled\";\nimport { useTaskPolling } from \"#/hooks/query/use-task-polling\";\nimport { matchesPendingConversationId } from \"#/utils/pending-task-message-link\";\nimport { useConversationWebSocket } from \"#/contexts/conversation-websocket-context\";\nimport ChatStatusIndicator from \"./chat-status-indicator\";\nimport { getStatusColor, getStatusText } from \"#/utils/utils\";\nimport { useNewConversationCommand } from \"#/hooks/mutation/use-new-conversation-command\";\nimport { useOptionalConversationId } from \"#/hooks/use-conversation-id\";\nimport { useActiveConversation } from \"#/hooks/query/use-active-conversation\";\nimport { I18nKey } from \"#/i18n/declaration\";\nimport { hasConversationStarted } from \"./components/resolve-picker-kind\";\n\nfunction getEntryPoint(\n hasRepository: boolean | null,\n hasReplayJson: boolean | null,\n): string {\n if (hasRepository) return \"github\";\n if (hasReplayJson) return \"replay\";\n return \"direct\";\n}\n\nexport function ChatInterface() {\n useAutoRefreshFilesOnEdit();\n\n const { trackInitialQuerySubmitted, trackUserMessageSent } = useTracking();\n const { setMessageToSend, conversationMode, planContent } =\n useConversationStore();\n const {\n errorMessage,\n errorCode,\n errorClassification,\n removeErrorMessage,\n setErrorMessage,\n } = useErrorMessageStore();\n const navigate = useNavigate();\n const { isTask, taskStatus, taskDetail } = useTaskPolling();\n // Hide empty-state chrome for the entire `/conversations/task-{uuid}` route,\n // including the brief READY window before redirect completes.\n const isProvisioningTask = isTask;\n const conversationWebSocket = useConversationWebSocket();\n const { send } = useSendMessage();\n const {\n renderableEvents,\n allConversationEvents,\n totalEvents,\n hasSubstantiveAgentActions,\n userEventsExist,\n } = useFilteredEvents();\n const enqueuePendingMessage = useOptimisticUserMessageStore(\n (state) => state.enqueuePendingMessage,\n );\n const markPendingMessageError = useOptimisticUserMessageStore(\n (state) => state.markPendingMessageError,\n );\n const pendingMessages = useOptimisticUserMessageStore(\n (state) => state.pendingMessages,\n );\n const { t } = useTranslation(\"openhands\");\n const scrollRef = React.useRef<HTMLDivElement>(null);\n const {\n scrollDomToBottom,\n onChatBodyScroll,\n hitBottom,\n autoScroll,\n setAutoScroll,\n setHitBottom,\n } = useScrollToBottom(scrollRef);\n const {\n mutate: newConversationCommand,\n isPending: isNewConversationPending,\n } = useNewConversationCommand();\n\n const { curAgentState } = useAgentState();\n const { handleBuildPlanClick } = useHandleBuildPlanClick();\n\n // Cloud conversations whose sandbox is MISSING or ERROR are read-only:\n // the sandbox is gone and cannot be resumed, so we hide the chat input\n // and show an explanatory banner. For local backends sandbox_status is\n // always null, so this is effectively a no-op for non-cloud use.\n const { data: activeConversation } = useActiveConversation();\n const sandboxStatus = activeConversation?.sandbox_status ?? null;\n const isArchivedConversation = useIsArchivedConversation();\n\n // Block sending in a resumed conversation that has no usable LLM, and show\n // the same setup banner as the home screen so the dead end is explained.\n const { isConfigured: isLlmConfigured, isLoading: isLlmConfigLoading } =\n useLlmConfigured();\n const llmBlocked = !isLlmConfigLoading && !isLlmConfigured;\n\n // Disable Build button while agent is running (streaming)\n const isAgentRunning =\n curAgentState === AgentState.RUNNING ||\n curAgentState === AgentState.LOADING;\n\n // Global keyboard shortcut for Build button (Cmd+Enter / Ctrl+Enter)\n // This is placed here instead of PlanPreview to avoid duplicate listeners\n // when multiple PlanPreview components exist in the chat.\n // Gated on the same conditions as the Build button (ConversationTabs'\n // `isBuildDisabled`) so it cannot fire outside the plan flow.\n React.useEffect(() => {\n if (isAgentRunning || conversationMode !== \"plan\" || !planContent) {\n return undefined;\n }\n\n const handleKeyDown = (event: KeyboardEvent) => {\n // Check for Cmd+Enter (Mac) or Ctrl+Enter (Windows/Linux)\n if ((event.metaKey || event.ctrlKey) && event.key === \"Enter\") {\n event.preventDefault();\n event.stopPropagation();\n handleBuildPlanClick(event);\n scrollDomToBottom();\n }\n };\n\n document.addEventListener(\"keydown\", handleKeyDown);\n\n return () => {\n document.removeEventListener(\"keydown\", handleKeyDown);\n };\n }, [\n isAgentRunning,\n conversationMode,\n planContent,\n handleBuildPlanClick,\n scrollDomToBottom,\n ]);\n\n const { selectedRepository, replayJson } = useInitialQueryStore();\n const { conversationId } = useOptionalConversationId();\n\n // The live goal banner renders in the scroll stream but advances via store\n // updates (in-progress goal events are filtered out of `renderableEvents`),\n // so the bottom-following effect would not react to it. This key changes as\n // an active loop appears and advances each round; feeding it into that effect\n // keeps the banner in view when the user is pinned to the bottom.\n const activeGoalScrollKey = useGoalStore((s) => {\n const goal = conversationId\n ? s.statusByConversation[conversationId]\n : undefined;\n return goal?.active ? `${goal.iteration}:${goal.status}` : null;\n });\n const { mutateAsync: uploadFiles } = useUnifiedUploadFiles();\n\n // Lazy \"scroll up to load older events\" backfill. Initial REST fetch only\n // returns the most recent page; this hook paginates older events into the\n // store on demand so the chat doesn't load (potentially) thousands of\n // events on first render.\n const {\n isLoading: isLoadingOlderEvents,\n hasMore: hasMoreOlderEvents,\n loadOlder,\n } = useLoadOlderEvents(conversationId);\n\n // Trigger `loadOlder` and preserve the visual scroll position once the\n // older page is merged in (otherwise prepending events would jump the\n // chat far down). We fire from three places to cover the cases the\n // browser's scroll event misses:\n //\n // - `onScroll`: normal \"user scrolled near the top\" path.\n // - `onWheel`: user is already pinned at scrollTop=0 and tries to\n // wheel further up — no scroll event fires past 0.\n // - effect: content is shorter than the viewport (no scrollbar\n // at all), so the user has nothing to scroll. Re-runs\n // as more pages arrive until there's overflow or the\n // server runs out of older events.\n const SCROLL_TOP_THRESHOLD_PX = 80;\n const preserveScrollPosition = React.useRef<{\n scrollHeight: number;\n scrollTop: number;\n } | null>(null);\n const maybeLoadOlder = React.useCallback(\n (target: HTMLElement) => {\n if (isProvisioningTask || isLoadingOlderEvents || !hasMoreOlderEvents) {\n return;\n }\n\n const atTop = target.scrollTop <= SCROLL_TOP_THRESHOLD_PX;\n const noOverflow =\n target.scrollHeight <= target.clientHeight + SCROLL_TOP_THRESHOLD_PX;\n if (!atTop && !noOverflow) return;\n\n preserveScrollPosition.current = {\n scrollHeight: target.scrollHeight,\n scrollTop: target.scrollTop,\n };\n loadOlder().catch((error) => {\n preserveScrollPosition.current = null;\n const message =\n error instanceof Error && error.message\n ? error.message\n : t(I18nKey.ERROR$GENERIC);\n setErrorMessage(message);\n });\n },\n [\n hasMoreOlderEvents,\n isLoadingOlderEvents,\n isProvisioningTask,\n loadOlder,\n setErrorMessage,\n t,\n ],\n );\n\n const handleWheelForPagination = React.useCallback(\n (e: React.WheelEvent<HTMLDivElement>) => {\n // Browsers don't dispatch a scroll event when scrollTop is already\n // 0 and the user wheels upward, so onScroll alone misses this case.\n if (e.deltaY < 0 && e.currentTarget.scrollTop <= 0) {\n maybeLoadOlder(e.currentTarget);\n }\n },\n [maybeLoadOlder],\n );\n\n const hasPendingUserMessages = React.useMemo(\n () =>\n conversationId\n ? pendingMessages.some((message) =>\n matchesPendingConversationId(\n conversationId,\n message.conversationId,\n ),\n )\n : false,\n [pendingMessages, conversationId],\n );\n\n const hasModelEntries = useModelStore((s) =>\n conversationId\n ? (s.entriesByConversation[conversationId]?.length ?? 0) > 0\n : false,\n );\n const hasStartedConversation = hasConversationStarted({\n isLoadingHistory: conversationWebSocket?.isLoadingHistory === true,\n hasUserEvents: userEventsExist,\n hasPendingUserMessages,\n hasSubstantiveAgentActions,\n hasModelEntries,\n });\n\n // Show V1 messages immediately if events exist in store (e.g., remount),\n // if the user already has a locally-tracked pending bubble (home-page cloud\n // submit while history/WS catch up), or once loading completes. This\n // replaces the old transition-observation pattern (useState + useEffect\n // watching loading→loaded) which always showed skeleton on remount because\n // local state initialized to false.\n const showConversationMessages =\n allConversationEvents.length > 0 ||\n hasPendingUserMessages ||\n !conversationWebSocket?.isLoadingHistory;\n\n const isReturningToConversation = !!conversationId;\n // Only show loading skeleton when genuinely loading AND no events in store yet.\n // If events exist (e.g., remount after data was already fetched), skip skeleton.\n const isHistoryLoading = !showConversationMessages;\n const isChatLoading = isHistoryLoading && !isTask;\n\n const handleSendMessage = async (\n content: string,\n originalImages: File[],\n originalFiles: File[],\n ) => {\n // Handle /new command for V1 conversations\n if (content.trim() === \"/new\") {\n if (!conversationId) {\n displayErrorToast(t(I18nKey.CONVERSATION$CLEAR_NO_ID));\n return;\n }\n if (totalEvents === 0) {\n displayErrorToast(t(I18nKey.CONVERSATION$CLEAR_EMPTY));\n return;\n }\n if (isNewConversationPending) {\n return;\n }\n newConversationCommand();\n return;\n }\n\n // Create mutable copies of the arrays\n const images = [...originalImages];\n const files = [...originalFiles];\n if (totalEvents === 0) {\n trackInitialQuerySubmitted({\n entryPoint: getEntryPoint(\n selectedRepository !== null,\n replayJson !== null,\n ),\n queryCharacterLength: content.length,\n replayJsonSize: replayJson?.length,\n });\n } else {\n trackUserMessageSent({\n sessionMessageCount: totalEvents,\n currentMessageLength: content.length,\n });\n }\n\n // Validate file sizes before any processing\n const allFiles = [...images, ...files];\n const validation = validateFiles(allFiles);\n\n if (!validation.isValid) {\n displayErrorToast(`Error: ${validation.errorMessage}`);\n return; // Stop processing if validation fails\n }\n\n const promises = images.map((image) => convertImageToBase64(image));\n const imageUrls = await Promise.all(promises);\n\n const timestamp = new Date().toISOString();\n\n const { skipped_files: skippedFiles, uploaded_files: uploadedFiles } =\n files.length > 0\n ? await uploadFiles({ conversationId: conversationId!, files })\n : { skipped_files: [], uploaded_files: [] };\n\n skippedFiles.forEach((f) => displayErrorToast(f.reason));\n\n const filePrompt = `${t(I18nKey.CHAT_INTERFACE$AUGMENTED_PROMPT_FILES_TITLE)}: ${uploadedFiles.join(\"\\n\\n\")}`;\n const prompt =\n uploadedFiles.length > 0 ? `${content}\\n\\n${filePrompt}` : content;\n\n // Enqueue the message into the local pending queue with status \"sending\"\n // so the user immediately sees it in the chat with a faded treatment. The\n // entry is removed when the WebSocket echoes back the corresponding\n // `UserMessageEvent`. If the API call to send the message fails, the entry\n // is flipped to \"error\" with a retry link.\n const pendingId = enqueuePendingMessage({\n conversationId: conversationId!,\n // `text` is what the user sees in the bubble; `content` is what we\n // actually hand to the server (the prompt may include an appended\n // \"Files uploaded: …\" block) and is what the echo will be matched\n // against. They're different when there are file attachments.\n text: content,\n content: prompt,\n imageUrls,\n fileUrls: uploadedFiles,\n timestamp,\n });\n // Submitting a new prompt should always pull the chat back to the\n // latest message even if the user had scrolled up. This also re-arms\n // autoScroll so the streamed agent reply auto-follows.\n scrollDomToBottom();\n setMessageToSend(\"\");\n\n try {\n await send(\n createChatMessage(prompt, imageUrls, uploadedFiles, timestamp),\n );\n } catch (sendError) {\n const sendErrorMessage =\n sendError instanceof Error\n ? sendError.message\n : t(I18nKey.CHAT_INTERFACE$FAILED_TO_SEND_MESSAGE);\n markPendingMessageError(pendingId, sendErrorMessage);\n }\n };\n\n // Auto-scroll to bottom when new messages arrive — but only if the user is\n // already pinned to the bottom. Scrolling up to load older events also\n // grows `renderableEvents`, and we don't want to yank the user back to the\n // bottom in that case.\n React.useEffect(() => {\n // If a \"load older\" was just triggered, restore the scroll position so\n // the conversation appears to extend upward instead of jumping.\n if (preserveScrollPosition.current && scrollRef.current) {\n const { scrollHeight: prevHeight, scrollTop: prevTop } =\n preserveScrollPosition.current;\n const dom = scrollRef.current;\n const delta = dom.scrollHeight - prevHeight;\n if (delta > 0) {\n dom.scrollTop = prevTop + delta;\n }\n preserveScrollPosition.current = null;\n return;\n }\n\n if (autoScroll) {\n scrollDomToBottom();\n }\n // Note: We intentionally exclude autoScroll from deps because we only want\n // to scroll when message content changes, not when autoScroll state changes.\n }, [\n renderableEvents.length,\n hasPendingUserMessages,\n activeGoalScrollKey,\n scrollDomToBottom,\n ]);\n\n // Auto-load older events when the chat content doesn't overflow the\n // scroll area (no scrollbar to drag, no wheel events past 0). We\n // re-run only when the rendered list grows or `hasMore` flips, NOT\n // when `maybeLoadOlder` re-creates: the underlying hook's `loadOlder`\n // ref changes whenever its internal `isLoading` toggles, so depending\n // on `maybeLoadOlder` would re-fire the effect on every failed page\n // and tight-loop until the server recovered. Driving off\n // `renderableEvents.length` instead means a successful page (events\n // grow) chains the next request, while a failed page (events\n // unchanged) waits for the user to retry.\n const maybeLoadOlderRef = React.useRef(maybeLoadOlder);\n React.useEffect(() => {\n maybeLoadOlderRef.current = maybeLoadOlder;\n });\n React.useEffect(() => {\n const target = scrollRef.current;\n if (!target) return;\n maybeLoadOlderRef.current(target);\n }, [renderableEvents.length, hasMoreOlderEvents]);\n\n // Create a ScrollProvider with the scroll hook values\n const scrollProviderValue = {\n scrollRef,\n autoScroll,\n setAutoScroll,\n scrollDomToBottom,\n hitBottom,\n setHitBottom,\n onChatBodyScroll,\n };\n\n // Get server status indicator props\n const isStartingStatus =\n curAgentState === AgentState.LOADING || curAgentState === AgentState.INIT;\n const isStopStatus = curAgentState === AgentState.STOPPED;\n const isPausing = curAgentState === AgentState.PAUSED;\n const serverStatusColor = getStatusColor({\n isPausing,\n isTask,\n taskStatus,\n isStartingStatus,\n isStopStatus,\n curAgentState,\n });\n const serverStatusText = getStatusText({\n isPausing,\n isTask,\n taskStatus,\n taskDetail,\n isStartingStatus,\n isStopStatus,\n curAgentState,\n errorMessage,\n t,\n });\n\n return (\n <WorkspaceFilesForChatProvider>\n <ScrollProvider value={scrollProviderValue}>\n <div\n className=\"relative flex h-full flex-col justify-between px-4\"\n data-testid=\"chat-interface\"\n >\n {!hasSubstantiveAgentActions &&\n !hasPendingUserMessages &&\n !userEventsExist &&\n !hasModelEntries &&\n !isChatLoading &&\n !isProvisioningTask &&\n totalEvents === 0 &&\n !isArchivedConversation &&\n // With no usable LLM the suggestions can't be acted on (the input is\n // disabled). They're also a `pointer-events-auto` overlay that would\n // sit over the LlmNotConfiguredBanner below and swallow clicks on its\n // setup button — so hide them and let the banner be the lone CTA.\n !llmBlocked && (\n <ChatSuggestions\n onSuggestionsClick={(message) => setMessageToSend(message)}\n />\n )}\n {/* Note: We only hide chat suggestions when there's a user message */}\n\n <div\n ref={scrollRef}\n data-testid=\"chat-scroll-container\"\n onScroll={(e) => {\n onChatBodyScroll(e.currentTarget);\n maybeLoadOlder(e.currentTarget);\n }}\n onWheel={handleWheelForPagination}\n className=\"custom-scrollbar-always flex min-h-0 grow flex-col gap-2 overflow-x-hidden overflow-y-auto px-0 pt-4 pb-8 md:px-4\"\n >\n {isChatLoading && isReturningToConversation && (\n <ChatMessagesSkeleton />\n )}\n\n {isChatLoading && !isReturningToConversation && (\n <div\n className=\"flex justify-center\"\n data-testid=\"loading-spinner\"\n >\n <LoadingSpinner size=\"small\" />\n </div>\n )}\n\n {isLoadingOlderEvents && (\n <div\n className=\"flex items-center justify-center gap-2 py-3 text-sm text-neutral-400\"\n data-testid=\"loading-older-events\"\n >\n <LoadingSpinner size=\"small\" />\n <span>{t(I18nKey.CHAT_INTERFACE$FETCHING_OLDER_MESSAGES)}</span>\n </div>\n )}\n\n {/*\n * Render whenever there's anything to display. Previously this\n * was gated on `conversationUserEventsExist`, but with the lazy\n * \"50 most recent\" REST fetch the initial window may not include\n * any `source: \"user\"` events (long agent runs between user\n * turns). That left the chat blank, leaving the user nothing to\n * scroll — which is why \"scroll up to load older\" appeared\n * broken. The empty-state ChatSuggestions block above still\n * keeps its own gate (`!userEventsExist && !hasSubstantiveAgentActions`)\n * so brand-new conversations show suggestions, not an empty chat.\n */}\n {/* /model entries created before any event is rendered are\n anchored to `null` and live above the message list. */}\n <ModelMessages\n conversationId={conversationId}\n anchorEventId={null}\n />\n\n {showConversationMessages && renderableEvents.length > 0 && (\n <Messages\n messages={renderableEvents}\n allEvents={allConversationEvents}\n />\n )}\n\n {/*\n Render the local pending-message queue independently so messages\n the user just submitted show up immediately (with a faded \"sending\"\n treatment) even before any real conversation event has come back\n from the server. Entries drain (FIFO) when the matching\n UserMessageEvent echoes back over the WebSocket, so this never\n double-renders alongside the real event list.\n */}\n <PendingUserMessages />\n\n {/* Goal-loop status sits at the end of the message flow — above the\n composer and its typing indicator — so progress stays in view. */}\n <GoalStatusBanner conversationId={conversationId} />\n </div>\n\n <div className=\"flex shrink-0 flex-col gap-[6px] pb-4\">\n <SkillInstallRestartBanner conversationId={conversationId} />\n <BtwMessages conversationId={conversationId} />\n {errorMessage && (\n <ErrorMessageBanner\n message={errorMessage}\n code={errorCode}\n classification={errorClassification}\n onDismiss={removeErrorMessage}\n onRetry={\n errorMessage === SERVER_CONNECTION_ERROR_MESSAGE\n ? () => conversationWebSocket?.reconnect()\n : undefined\n }\n onReauth={\n isAcpAuthErrorCode(errorCode)\n ? () => navigate(\"/settings/agents\")\n : undefined\n }\n />\n )}\n\n {llmBlocked && !isArchivedConversation && (\n <LlmNotConfiguredBanner />\n )}\n\n {isArchivedConversation ? (\n // Archived / sandbox-error: show a read-only notice in place of\n // the chat input. The conversation history above is still visible.\n <div\n data-testid=\"archived-conversation-banner\"\n className=\"mx-1 px-4 py-3 rounded-lg bg-[var(--oh-surface)] border border-[var(--oh-border-subtle)]\"\n >\n <p className=\"text-xs font-semibold text-[var(--oh-foreground)]\">\n {sandboxStatus === \"ERROR\"\n ? t(I18nKey.CHAT_INTERFACE$ERROR_SANDBOX_TITLE)\n : t(I18nKey.CHAT_INTERFACE$ARCHIVED_SANDBOX_TITLE)}\n </p>\n <p className=\"text-xs text-[var(--oh-muted)] mt-0.5\">\n {sandboxStatus === \"ERROR\"\n ? t(I18nKey.CHAT_INTERFACE$ERROR_SANDBOX_DESCRIPTION)\n : t(I18nKey.CHAT_INTERFACE$ARCHIVED_SANDBOX_DESCRIPTION)}\n </p>\n </div>\n ) : (\n <div className=\"relative\">\n <div className=\"pointer-events-none absolute inset-x-0 bottom-full mb-1 z-20\">\n <div className=\"flex justify-between relative\">\n <div className=\"flex items-end gap-1 pointer-events-auto\">\n <ConfirmationModeEnabled />\n {isStartingStatus && (\n <ChatStatusIndicator\n statusColor={serverStatusColor}\n status={serverStatusText}\n />\n )}\n </div>\n\n {!hitBottom ? (\n <div className=\"absolute left-1/2 transform -translate-x-1/2 bottom-0 pointer-events-auto\">\n <ScrollToBottomButton onClick={scrollDomToBottom} />\n </div>\n ) : (\n curAgentState === AgentState.RUNNING && (\n <div className=\"pointer-events-none absolute inset-x-9 bottom-0 flex justify-center\">\n <TypingIndicator events={allConversationEvents} />\n </div>\n )\n )}\n </div>\n </div>\n\n <InteractiveChatBox\n onSubmit={handleSendMessage}\n disabled={isNewConversationPending || llmBlocked}\n hasStartedConversation={hasStartedConversation}\n />\n </div>\n )}\n </div>\n </div>\n </ScrollProvider>\n </WorkspaceFilesForChatProvider>\n );\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
const e=require("../../../_virtual/_rolldown/runtime.cjs"),t=require("../../../utils/utils.cjs"),n=require("../../../hooks/use-conversation-id.cjs"),r=require("../../../api/conversation-service/conversation-service.api.cjs"),i=require("../../../utils/path-utils.cjs"),a=require("../../../services/canvas-ui.cjs"),o=require("../markdown/code.cjs"),s=require("../markdown/anchor.cjs"),c=require("../../../hooks/query/use-workspace-files.cjs");let l=require("react");l=e.__toESM(l,1);let u=require("react/jsx-runtime");var d=(0,l.createContext)(!1),f=(0,l.createContext)(void 0);function p({children:e}){let{data:t}=c.useWorkspaceFiles();return(0,u.jsx)(f.Provider,{value:t,children:e})}function m(e){if(e==null||typeof e==`boolean`)return``;if(typeof e==`string`||typeof e==`number`)return String(e);if(Array.isArray(e)){let t=e.map(m);return t.some(e=>e===null)?null:t.join(``)}return null}function h(e){let t=(0,l.useContext)(f);if(!t?.length||!i.looksLikeWorkspaceFilePath(e))return null;let n=r.default.getCurrentConversation()?.workspace?.working_dir,a=i.toFilesTabPath(e,n);return a&&t.includes(a)?a:null}function g({path:e,className:r,children:i}){let{conversationId:o}=n.useOptionalConversationId();return(0,u.jsx)(`button`,{type:`button`,"data-testid":`markdown-file-path-link`,title:e,className:t.cn(r,`cursor-pointer rounded border border-surface-raised bg-surface-raised px-[0.4em] py-[0.2em] font-mono text-foreground hover:underline`),onClick:t=>{t.stopPropagation(),a.openWorkspaceFile(e,o)},children:i})}function _(e){let{children:t,className:n}=e,r=(0,l.useContext)(d),i=/language-(\w+)/.exec(n||``),a=String(t).replace(/\n$/,``),s=String(t).includes(`
|
|
2
|
+
`),c=h(!i&&!s&&!r?a:``);return c?(0,u.jsx)(g,{path:c,className:n,children:t}):o.code(e)}function v(e){let{children:t}=e,n=(0,l.useContext)(d),r=m(t)?.trim()??``,i=h(n?``:r);return i?(0,u.jsx)(g,{path:i,children:t}):(0,u.jsx)(`strong`,{children:t})}function y(e){return(0,u.jsx)(d.Provider,{value:!0,children:s.anchor(e)})}exports.ChatAnchor=y,exports.ChatCode=_,exports.ChatStrong=v,exports.WorkspaceFilesForChatProvider=p;
|
|
3
|
+
//# sourceMappingURL=chat-markdown-path-code.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chat-markdown-path-code.cjs","names":[],"sources":["../../../../src/components/features/chat/chat-markdown-path-code.tsx"],"sourcesContent":["import React, { createContext, useContext } from \"react\";\nimport type { ExtraProps } from \"react-markdown\";\nimport ConversationService from \"#/api/conversation-service/conversation-service.api\";\nimport { useOptionalConversationId } from \"#/hooks/use-conversation-id\";\nimport { useWorkspaceFiles } from \"#/hooks/query/use-workspace-files\";\nimport { openWorkspaceFile } from \"#/services/canvas-ui\";\nimport { looksLikeWorkspaceFilePath, toFilesTabPath } from \"#/utils/path-utils\";\nimport { cn } from \"#/utils/utils\";\nimport { code as defaultCode } from \"../markdown/code\";\nimport { anchor as defaultAnchor } from \"../markdown/anchor\";\n\n/** True while rendering Markdown descendants of an existing `<a>`. */\nconst InMarkdownLinkContext = createContext(false);\n\n/**\n * Workspace paths available for chat Markdown linking. Absent outside\n * ChatInterface (e.g. unit tests) so ChatCode never requires QueryClient.\n */\nexport const WorkspaceFilesForChatContext = createContext<string[] | undefined>(\n undefined,\n);\n\n/** Fetches workspace files once for the chat tree and exposes them to path links. */\nexport function WorkspaceFilesForChatProvider({\n children,\n}: {\n children: React.ReactNode;\n}) {\n const { data } = useWorkspaceFiles();\n return (\n <WorkspaceFilesForChatContext.Provider value={data}>\n {children}\n </WorkspaceFilesForChatContext.Provider>\n );\n}\n\ntype CodeProps = React.ClassAttributes<HTMLElement> &\n React.HTMLAttributes<HTMLElement> &\n ExtraProps;\n\ntype AnchorProps = React.ClassAttributes<HTMLAnchorElement> &\n React.AnchorHTMLAttributes<HTMLAnchorElement> &\n ExtraProps;\n\ntype StrongProps = React.ClassAttributes<HTMLElement> &\n React.HTMLAttributes<HTMLElement> &\n ExtraProps;\n\n/** Flatten React children to plain text, or null if mixed nodes. */\nfunction getPlainText(children: React.ReactNode): string | null {\n if (children == null || typeof children === \"boolean\") return \"\";\n if (typeof children === \"string\" || typeof children === \"number\") {\n return String(children);\n }\n if (Array.isArray(children)) {\n const parts = children.map(getPlainText);\n if (parts.some((part) => part === null)) return null;\n return parts.join(\"\");\n }\n return null;\n}\n\n/**\n * Only link paths that currently exist in the conversation workspace.\n * workingDir comes from ConversationService — same source as navigate_to_file.\n */\nfunction useExistingWorkspacePath(candidate: string): string | null {\n const files = useContext(WorkspaceFilesForChatContext);\n if (!files?.length || !looksLikeWorkspaceFilePath(candidate)) return null;\n\n const workingDir =\n ConversationService.getCurrentConversation()?.workspace?.working_dir;\n const normalized = toFilesTabPath(candidate, workingDir);\n if (!normalized) return null;\n return files.includes(normalized) ? normalized : null;\n}\n\nfunction ChatMarkdownPathLink({\n path,\n className,\n children,\n}: {\n path: string;\n className?: string;\n children: React.ReactNode;\n}) {\n const { conversationId } = useOptionalConversationId();\n\n return (\n <button\n type=\"button\"\n data-testid=\"markdown-file-path-link\"\n title={path}\n className={cn(\n className,\n \"cursor-pointer rounded border border-surface-raised bg-surface-raised px-[0.4em] py-[0.2em] font-mono text-foreground hover:underline\",\n )}\n onClick={(event) => {\n event.stopPropagation();\n openWorkspaceFile(path, conversationId);\n }}\n >\n {children}\n </button>\n );\n}\n\n/**\n * Chat-only inline code: existing workspace paths become Files-drawer buttons.\n * Nested under an existing Markdown link → stay plain `<code>`.\n */\nexport function ChatCode(props: CodeProps) {\n const { children, className } = props;\n const inLink = useContext(InMarkdownLinkContext);\n const match = /language-(\\w+)/.exec(className || \"\");\n const codeString = String(children).replace(/\\n$/, \"\");\n const isMultiline = String(children).includes(\"\\n\");\n const existingPath = useExistingWorkspacePath(\n !match && !isMultiline && !inLink ? codeString : \"\",\n );\n\n if (existingPath) {\n return (\n <ChatMarkdownPathLink path={existingPath} className={className}>\n {children}\n </ChatMarkdownPathLink>\n );\n }\n\n return defaultCode(props);\n}\n\n/**\n * Agents often emphasize paths with `**file.md**` instead of backticks.\n * Link those only when the whole strong span is an existing workspace file.\n */\nexport function ChatStrong(props: StrongProps) {\n const { children } = props;\n const inLink = useContext(InMarkdownLinkContext);\n const text = getPlainText(children)?.trim() ?? \"\";\n const existingPath = useExistingWorkspacePath(!inLink ? text : \"\");\n\n if (existingPath) {\n return (\n <ChatMarkdownPathLink path={existingPath}>\n {children}\n </ChatMarkdownPathLink>\n );\n }\n\n return <strong>{children}</strong>;\n}\n\n/** Marks descendants so nested path tokens are not turned into a button. */\nexport function ChatAnchor(props: AnchorProps) {\n return (\n <InMarkdownLinkContext.Provider value>\n {defaultAnchor(props)}\n </InMarkdownLinkContext.Provider>\n );\n}\n"],"mappings":"ogBAYA,IAAM,GAAA,EAAA,EAAA,eAAsC,EAAK,EAMpC,GAAA,EAAA,EAAA,eACX,IAAA,EACF,EAGA,SAAgB,EAA8B,CAC5C,YAGC,CACD,GAAM,CAAE,QAAS,EAAA,kBAAkB,EACnC,OACE,EAAA,EAAA,KAAC,EAA6B,SAA9B,CAAuC,MAAO,EAC3C,UACoC,CAAA,CAE3C,CAeA,SAAS,EAAa,EAA0C,CAC9D,GAAI,GAAY,MAAQ,OAAO,GAAa,UAAW,MAAO,GAC9D,GAAI,OAAO,GAAa,UAAY,OAAO,GAAa,SACtD,OAAO,OAAO,CAAQ,EAExB,GAAI,MAAM,QAAQ,CAAQ,EAAG,CAC3B,IAAM,EAAQ,EAAS,IAAI,CAAY,EAEvC,OADI,EAAM,KAAM,GAAS,IAAS,IAAI,EAAU,KACzC,EAAM,KAAK,EAAE,CACtB,CACA,OAAO,IACT,CAMA,SAAS,EAAyB,EAAkC,CAClE,IAAM,GAAA,EAAA,EAAA,YAAmB,CAA4B,EACrD,GAAI,CAAC,GAAO,QAAU,CAAC,EAAA,2BAA2B,CAAS,EAAG,OAAO,KAErE,IAAM,EACJ,EAAA,QAAoB,uBAAuB,GAAG,WAAW,YACrD,EAAa,EAAA,eAAe,EAAW,CAAU,EAEvD,OADK,GACE,EAAM,SAAS,CAAU,EAAI,EAAa,IACnD,CAEA,SAAS,EAAqB,CAC5B,OACA,YACA,YAKC,CACD,GAAM,CAAE,kBAAmB,EAAA,0BAA0B,EAErD,OACE,EAAA,EAAA,KAAC,SAAD,CACE,KAAK,SACL,cAAY,0BACZ,MAAO,EACP,UAAW,EAAA,GACT,EACA,uIACF,EACA,QAAU,GAAU,CAClB,EAAM,gBAAgB,EACtB,EAAA,kBAAkB,EAAM,CAAc,CACxC,EAEC,UACK,CAAA,CAEZ,CAMA,SAAgB,EAAS,EAAkB,CACzC,GAAM,CAAE,WAAU,aAAc,EAC1B,GAAA,EAAA,EAAA,YAAoB,CAAqB,EACzC,EAAQ,iBAAiB,KAAK,GAAa,EAAE,EAC7C,EAAa,OAAO,CAAQ,EAAE,QAAQ,MAAO,EAAE,EAC/C,EAAc,OAAO,CAAQ,EAAE,SAAS;CAAI,EAC5C,EAAe,EACnB,CAAC,GAAS,CAAC,GAAe,CAAC,EAAS,EAAa,EACnD,EAUA,OARI,GAEA,EAAA,EAAA,KAAC,EAAD,CAAsB,KAAM,EAAyB,YAClD,UACmB,CAAA,EAInB,EAAA,KAAY,CAAK,CAC1B,CAMA,SAAgB,EAAW,EAAoB,CAC7C,GAAM,CAAE,YAAa,EACf,GAAA,EAAA,EAAA,YAAoB,CAAqB,EACzC,EAAO,EAAa,CAAQ,GAAG,KAAK,GAAK,GACzC,EAAe,EAA0B,EAAgB,GAAP,CAAS,EAUjE,OARI,GAEA,EAAA,EAAA,KAAC,EAAD,CAAsB,KAAM,EACzB,UACmB,CAAA,GAInB,EAAA,EAAA,KAAC,SAAD,CAAS,UAAiB,CAAA,CACnC,CAGA,SAAgB,EAAW,EAAoB,CAC7C,OACE,EAAA,EAAA,KAAC,EAAsB,SAAvB,CAAgC,MAAA,YAC7B,EAAA,OAAc,CAAK,CACU,CAAA,CAEpC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import type { ExtraProps } from "react-markdown";
|
|
3
|
+
/**
|
|
4
|
+
* Workspace paths available for chat Markdown linking. Absent outside
|
|
5
|
+
* ChatInterface (e.g. unit tests) so ChatCode never requires QueryClient.
|
|
6
|
+
*/
|
|
7
|
+
export declare const WorkspaceFilesForChatContext: React.Context<string[] | undefined>;
|
|
8
|
+
/** Fetches workspace files once for the chat tree and exposes them to path links. */
|
|
9
|
+
export declare function WorkspaceFilesForChatProvider({ children, }: {
|
|
10
|
+
children: React.ReactNode;
|
|
11
|
+
}): React.JSX.Element;
|
|
12
|
+
type CodeProps = React.ClassAttributes<HTMLElement> & React.HTMLAttributes<HTMLElement> & ExtraProps;
|
|
13
|
+
type AnchorProps = React.ClassAttributes<HTMLAnchorElement> & React.AnchorHTMLAttributes<HTMLAnchorElement> & ExtraProps;
|
|
14
|
+
type StrongProps = React.ClassAttributes<HTMLElement> & React.HTMLAttributes<HTMLElement> & ExtraProps;
|
|
15
|
+
/**
|
|
16
|
+
* Chat-only inline code: existing workspace paths become Files-drawer buttons.
|
|
17
|
+
* Nested under an existing Markdown link → stay plain `<code>`.
|
|
18
|
+
*/
|
|
19
|
+
export declare function ChatCode(props: CodeProps): React.JSX.Element;
|
|
20
|
+
/**
|
|
21
|
+
* Agents often emphasize paths with `**file.md**` instead of backticks.
|
|
22
|
+
* Link those only when the whole strong span is an existing workspace file.
|
|
23
|
+
*/
|
|
24
|
+
export declare function ChatStrong(props: StrongProps): React.JSX.Element;
|
|
25
|
+
/** Marks descendants so nested path tokens are not turned into a button. */
|
|
26
|
+
export declare function ChatAnchor(props: AnchorProps): React.JSX.Element;
|
|
27
|
+
export {};
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { cn as e } from "../../../utils/utils.js";
|
|
2
|
+
import { useOptionalConversationId as t } from "../../../hooks/use-conversation-id.js";
|
|
3
|
+
import n from "../../../api/conversation-service/conversation-service.api.js";
|
|
4
|
+
import { looksLikeWorkspaceFilePath as r, toFilesTabPath as i } from "../../../utils/path-utils.js";
|
|
5
|
+
import { openWorkspaceFile as a } from "../../../services/canvas-ui.js";
|
|
6
|
+
import { code as o } from "../markdown/code.js";
|
|
7
|
+
import { anchor as s } from "../markdown/anchor.js";
|
|
8
|
+
import { useWorkspaceFiles as c } from "../../../hooks/query/use-workspace-files.js";
|
|
9
|
+
import { createContext as l, useContext as u } from "react";
|
|
10
|
+
import { jsx as d } from "react/jsx-runtime";
|
|
11
|
+
//#region src/components/features/chat/chat-markdown-path-code.tsx
|
|
12
|
+
var f = l(!1), p = l(void 0);
|
|
13
|
+
function m({ children: e }) {
|
|
14
|
+
let { data: t } = c();
|
|
15
|
+
return /* @__PURE__ */ d(p.Provider, {
|
|
16
|
+
value: t,
|
|
17
|
+
children: e
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
function h(e) {
|
|
21
|
+
if (e == null || typeof e == "boolean") return "";
|
|
22
|
+
if (typeof e == "string" || typeof e == "number") return String(e);
|
|
23
|
+
if (Array.isArray(e)) {
|
|
24
|
+
let t = e.map(h);
|
|
25
|
+
return t.some((e) => e === null) ? null : t.join("");
|
|
26
|
+
}
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
function g(e) {
|
|
30
|
+
let t = u(p);
|
|
31
|
+
if (!t?.length || !r(e)) return null;
|
|
32
|
+
let a = n.getCurrentConversation()?.workspace?.working_dir, o = i(e, a);
|
|
33
|
+
return o && t.includes(o) ? o : null;
|
|
34
|
+
}
|
|
35
|
+
function _({ path: n, className: r, children: i }) {
|
|
36
|
+
let { conversationId: o } = t();
|
|
37
|
+
return /* @__PURE__ */ d("button", {
|
|
38
|
+
type: "button",
|
|
39
|
+
"data-testid": "markdown-file-path-link",
|
|
40
|
+
title: n,
|
|
41
|
+
className: e(r, "cursor-pointer rounded border border-surface-raised bg-surface-raised px-[0.4em] py-[0.2em] font-mono text-foreground hover:underline"),
|
|
42
|
+
onClick: (e) => {
|
|
43
|
+
e.stopPropagation(), a(n, o);
|
|
44
|
+
},
|
|
45
|
+
children: i
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
function v(e) {
|
|
49
|
+
let { children: t, className: n } = e, r = u(f), i = /language-(\w+)/.exec(n || ""), a = String(t).replace(/\n$/, ""), s = String(t).includes("\n"), c = g(!i && !s && !r ? a : "");
|
|
50
|
+
return c ? /* @__PURE__ */ d(_, {
|
|
51
|
+
path: c,
|
|
52
|
+
className: n,
|
|
53
|
+
children: t
|
|
54
|
+
}) : o(e);
|
|
55
|
+
}
|
|
56
|
+
function y(e) {
|
|
57
|
+
let { children: t } = e, n = u(f), r = h(t)?.trim() ?? "", i = g(n ? "" : r);
|
|
58
|
+
return i ? /* @__PURE__ */ d(_, {
|
|
59
|
+
path: i,
|
|
60
|
+
children: t
|
|
61
|
+
}) : /* @__PURE__ */ d("strong", { children: t });
|
|
62
|
+
}
|
|
63
|
+
function b(e) {
|
|
64
|
+
return /* @__PURE__ */ d(f.Provider, {
|
|
65
|
+
value: !0,
|
|
66
|
+
children: s(e)
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
//#endregion
|
|
70
|
+
export { b as ChatAnchor, v as ChatCode, y as ChatStrong, m as WorkspaceFilesForChatProvider };
|
|
71
|
+
|
|
72
|
+
//# sourceMappingURL=chat-markdown-path-code.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chat-markdown-path-code.js","names":[],"sources":["../../../../src/components/features/chat/chat-markdown-path-code.tsx"],"sourcesContent":["import React, { createContext, useContext } from \"react\";\nimport type { ExtraProps } from \"react-markdown\";\nimport ConversationService from \"#/api/conversation-service/conversation-service.api\";\nimport { useOptionalConversationId } from \"#/hooks/use-conversation-id\";\nimport { useWorkspaceFiles } from \"#/hooks/query/use-workspace-files\";\nimport { openWorkspaceFile } from \"#/services/canvas-ui\";\nimport { looksLikeWorkspaceFilePath, toFilesTabPath } from \"#/utils/path-utils\";\nimport { cn } from \"#/utils/utils\";\nimport { code as defaultCode } from \"../markdown/code\";\nimport { anchor as defaultAnchor } from \"../markdown/anchor\";\n\n/** True while rendering Markdown descendants of an existing `<a>`. */\nconst InMarkdownLinkContext = createContext(false);\n\n/**\n * Workspace paths available for chat Markdown linking. Absent outside\n * ChatInterface (e.g. unit tests) so ChatCode never requires QueryClient.\n */\nexport const WorkspaceFilesForChatContext = createContext<string[] | undefined>(\n undefined,\n);\n\n/** Fetches workspace files once for the chat tree and exposes them to path links. */\nexport function WorkspaceFilesForChatProvider({\n children,\n}: {\n children: React.ReactNode;\n}) {\n const { data } = useWorkspaceFiles();\n return (\n <WorkspaceFilesForChatContext.Provider value={data}>\n {children}\n </WorkspaceFilesForChatContext.Provider>\n );\n}\n\ntype CodeProps = React.ClassAttributes<HTMLElement> &\n React.HTMLAttributes<HTMLElement> &\n ExtraProps;\n\ntype AnchorProps = React.ClassAttributes<HTMLAnchorElement> &\n React.AnchorHTMLAttributes<HTMLAnchorElement> &\n ExtraProps;\n\ntype StrongProps = React.ClassAttributes<HTMLElement> &\n React.HTMLAttributes<HTMLElement> &\n ExtraProps;\n\n/** Flatten React children to plain text, or null if mixed nodes. */\nfunction getPlainText(children: React.ReactNode): string | null {\n if (children == null || typeof children === \"boolean\") return \"\";\n if (typeof children === \"string\" || typeof children === \"number\") {\n return String(children);\n }\n if (Array.isArray(children)) {\n const parts = children.map(getPlainText);\n if (parts.some((part) => part === null)) return null;\n return parts.join(\"\");\n }\n return null;\n}\n\n/**\n * Only link paths that currently exist in the conversation workspace.\n * workingDir comes from ConversationService — same source as navigate_to_file.\n */\nfunction useExistingWorkspacePath(candidate: string): string | null {\n const files = useContext(WorkspaceFilesForChatContext);\n if (!files?.length || !looksLikeWorkspaceFilePath(candidate)) return null;\n\n const workingDir =\n ConversationService.getCurrentConversation()?.workspace?.working_dir;\n const normalized = toFilesTabPath(candidate, workingDir);\n if (!normalized) return null;\n return files.includes(normalized) ? normalized : null;\n}\n\nfunction ChatMarkdownPathLink({\n path,\n className,\n children,\n}: {\n path: string;\n className?: string;\n children: React.ReactNode;\n}) {\n const { conversationId } = useOptionalConversationId();\n\n return (\n <button\n type=\"button\"\n data-testid=\"markdown-file-path-link\"\n title={path}\n className={cn(\n className,\n \"cursor-pointer rounded border border-surface-raised bg-surface-raised px-[0.4em] py-[0.2em] font-mono text-foreground hover:underline\",\n )}\n onClick={(event) => {\n event.stopPropagation();\n openWorkspaceFile(path, conversationId);\n }}\n >\n {children}\n </button>\n );\n}\n\n/**\n * Chat-only inline code: existing workspace paths become Files-drawer buttons.\n * Nested under an existing Markdown link → stay plain `<code>`.\n */\nexport function ChatCode(props: CodeProps) {\n const { children, className } = props;\n const inLink = useContext(InMarkdownLinkContext);\n const match = /language-(\\w+)/.exec(className || \"\");\n const codeString = String(children).replace(/\\n$/, \"\");\n const isMultiline = String(children).includes(\"\\n\");\n const existingPath = useExistingWorkspacePath(\n !match && !isMultiline && !inLink ? codeString : \"\",\n );\n\n if (existingPath) {\n return (\n <ChatMarkdownPathLink path={existingPath} className={className}>\n {children}\n </ChatMarkdownPathLink>\n );\n }\n\n return defaultCode(props);\n}\n\n/**\n * Agents often emphasize paths with `**file.md**` instead of backticks.\n * Link those only when the whole strong span is an existing workspace file.\n */\nexport function ChatStrong(props: StrongProps) {\n const { children } = props;\n const inLink = useContext(InMarkdownLinkContext);\n const text = getPlainText(children)?.trim() ?? \"\";\n const existingPath = useExistingWorkspacePath(!inLink ? text : \"\");\n\n if (existingPath) {\n return (\n <ChatMarkdownPathLink path={existingPath}>\n {children}\n </ChatMarkdownPathLink>\n );\n }\n\n return <strong>{children}</strong>;\n}\n\n/** Marks descendants so nested path tokens are not turned into a button. */\nexport function ChatAnchor(props: AnchorProps) {\n return (\n <InMarkdownLinkContext.Provider value>\n {defaultAnchor(props)}\n </InMarkdownLinkContext.Provider>\n );\n}\n"],"mappings":";;;;;;;;;;;AAYA,IAAM,IAAwB,EAAc,EAAK,GAMpC,IAA+B,EAC1C,KAAA,CACF;AAGA,SAAgB,EAA8B,EAC5C,eAGC;CACD,IAAM,EAAE,YAAS,EAAkB;CACnC,OACE,kBAAC,EAA6B,UAA9B;EAAuC,OAAO;EAC3C;CACoC,CAAA;AAE3C;AAeA,SAAS,EAAa,GAA0C;CAC9D,IAAI,KAAY,QAAQ,OAAO,KAAa,WAAW,OAAO;CAC9D,IAAI,OAAO,KAAa,YAAY,OAAO,KAAa,UACtD,OAAO,OAAO,CAAQ;CAExB,IAAI,MAAM,QAAQ,CAAQ,GAAG;EAC3B,IAAM,IAAQ,EAAS,IAAI,CAAY;EAEvC,OADI,EAAM,MAAM,MAAS,MAAS,IAAI,IAAU,OACzC,EAAM,KAAK,EAAE;CACtB;CACA,OAAO;AACT;AAMA,SAAS,EAAyB,GAAkC;CAClE,IAAM,IAAQ,EAAW,CAA4B;CACrD,IAAI,CAAC,GAAO,UAAU,CAAC,EAA2B,CAAS,GAAG,OAAO;CAErE,IAAM,IACJ,EAAoB,uBAAuB,GAAG,WAAW,aACrD,IAAa,EAAe,GAAW,CAAU;CAEvD,OADK,KACE,EAAM,SAAS,CAAU,IAAI,IAAa;AACnD;AAEA,SAAS,EAAqB,EAC5B,SACA,cACA,eAKC;CACD,IAAM,EAAE,sBAAmB,EAA0B;CAErD,OACE,kBAAC,UAAD;EACE,MAAK;EACL,eAAY;EACZ,OAAO;EACP,WAAW,EACT,GACA,uIACF;EACA,UAAU,MAAU;GAElB,AADA,EAAM,gBAAgB,GACtB,EAAkB,GAAM,CAAc;EACxC;EAEC;CACK,CAAA;AAEZ;AAMA,SAAgB,EAAS,GAAkB;CACzC,IAAM,EAAE,aAAU,iBAAc,GAC1B,IAAS,EAAW,CAAqB,GACzC,IAAQ,iBAAiB,KAAK,KAAa,EAAE,GAC7C,IAAa,OAAO,CAAQ,EAAE,QAAQ,OAAO,EAAE,GAC/C,IAAc,OAAO,CAAQ,EAAE,SAAS,IAAI,GAC5C,IAAe,EACnB,CAAC,KAAS,CAAC,KAAe,CAAC,IAAS,IAAa,EACnD;CAUA,OARI,IAEA,kBAAC,GAAD;EAAsB,MAAM;EAAyB;EAClD;CACmB,CAAA,IAInB,EAAY,CAAK;AAC1B;AAMA,SAAgB,EAAW,GAAoB;CAC7C,IAAM,EAAE,gBAAa,GACf,IAAS,EAAW,CAAqB,GACzC,IAAO,EAAa,CAAQ,GAAG,KAAK,KAAK,IACzC,IAAe,EAA0B,IAAgB,KAAP,CAAS;CAUjE,OARI,IAEA,kBAAC,GAAD;EAAsB,MAAM;EACzB;CACmB,CAAA,IAInB,kBAAC,UAAD,EAAS,YAAiB,CAAA;AACnC;AAGA,SAAgB,EAAW,GAAoB;CAC7C,OACE,kBAAC,EAAsB,UAAvB;EAAgC,OAAA;YAC7B,EAAc,CAAK;CACU,CAAA;AAEpC"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
require("../../../_virtual/_rolldown/runtime.cjs");const e=require("../../../utils/event-logger.cjs");let
|
|
1
|
+
require("../../../_virtual/_rolldown/runtime.cjs");const e=require("../../../hooks/use-conversation-id.cjs"),t=require("../../../services/canvas-ui.cjs"),n=require("../../../utils/event-logger.cjs");let r=require("react"),i=require("react/jsx-runtime");var a=(0,r.createContext)(!0),o=e=>{let t=document.createElement(`textarea`);return t.innerHTML=e,t.value},s=e=>e?e.endsWith(`/`)||e.endsWith(`\\`)?!0:!(e.split(/[/\\]/).pop()||``).includes(`.`):!1,c=e=>{if(!e)return``;let t=e.split(/[/\\]/),n=t[t.length-1];return s(e)&&!n.endsWith(`/`)?`${n}/`:n};function l(s){let{children:l}=s,{conversationId:u}=e.useOptionalConversationId(),d=(0,r.useContext)(a),f=e=>{try{let n=o(e),r=c(n);return d?(0,i.jsx)(`button`,{type:`button`,"data-testid":`path-component-link`,className:`cursor-pointer font-mono font-normal tracking-tight hover:underline`,title:n,onClick:e=>{e.stopPropagation(),t.openWorkspaceFile(n,u)},children:r}):(0,i.jsx)(`span`,{className:`font-mono font-normal tracking-tight`,title:n,children:r})}catch(t){return n.default.error(String(t)),(0,i.jsx)(`span`,{className:`font-mono font-normal tracking-tight`,children:e})}};return Array.isArray(l)?(0,i.jsx)(`span`,{className:`font-normal tracking-tight`,children:l.map((e,t)=>typeof e==`string`?(0,i.jsx)(`span`,{children:f(e)},`${e}-${t}`):e)}):typeof l==`string`?(0,i.jsx)(`span`,{className:`font-normal tracking-tight`,children:f(l)}):(0,i.jsx)(`span`,{className:`font-mono font-normal tracking-tight`,children:l})}exports.PathComponent=l,exports.PathInteractiveContext=a;
|
|
2
2
|
//# sourceMappingURL=path-component.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"path-component.cjs","names":[],"sources":["../../../../src/components/features/chat/path-component.tsx"],"sourcesContent":["import { ReactNode } from \"react\";\nimport EventLogger from \"#/utils/event-logger\";\n\n/**\n * Decodes HTML entities in a string\n * @param text The text to decode\n * @returns The decoded text\n */\nconst decodeHtmlEntities = (text: string): string => {\n const textarea = document.createElement(\"textarea\");\n textarea.innerHTML = text;\n return textarea.value;\n};\n\n/**\n * Checks if a path is likely a directory\n * @param path The full path\n * @returns True if the path is likely a directory\n */\nconst isLikelyDirectory = (path: string): boolean => {\n if (!path) return false;\n // Check if path already ends with a slash\n if (path.endsWith(\"/\") || path.endsWith(\"\\\\\")) return true;\n // Check if path has no extension (simple heuristic)\n const lastPart = path.split(/[/\\\\]/).pop() || \"\";\n // If the last part has no dots, it's likely a directory\n return !lastPart.includes(\".\");\n};\n\n/**\n * Extracts the filename from a path\n * @param path The full path\n * @returns The filename (last part of the path)\n */\nconst extractFilename = (path: string): string => {\n if (!path) return \"\";\n // Handle both Unix and Windows paths\n const parts = path.split(/[/\\\\]/);\n const filename = parts[parts.length - 1];\n\n // Add trailing slash for directories\n if (isLikelyDirectory(path) && !filename.endsWith(\"/\")) {\n return `${filename}/`;\n }\n\n return filename;\n};\n\n/**\n *
|
|
1
|
+
{"version":3,"file":"path-component.cjs","names":[],"sources":["../../../../src/components/features/chat/path-component.tsx"],"sourcesContent":["import { createContext, ReactNode, useContext } from \"react\";\nimport { useOptionalConversationId } from \"#/hooks/use-conversation-id\";\nimport { openWorkspaceFile } from \"#/services/canvas-ui\";\nimport EventLogger from \"#/utils/event-logger\";\n\n/**\n * When false, PathComponent renders a non-interactive span. EventGroup's\n * header wraps titles in a toggle <button>, so nested path buttons would be\n * invalid markup — that context sets this to false.\n */\nexport const PathInteractiveContext = createContext(true);\n\n/**\n * Decodes HTML entities in a string\n * @param text The text to decode\n * @returns The decoded text\n */\nconst decodeHtmlEntities = (text: string): string => {\n const textarea = document.createElement(\"textarea\");\n textarea.innerHTML = text;\n return textarea.value;\n};\n\n/**\n * Checks if a path is likely a directory\n * @param path The full path\n * @returns True if the path is likely a directory\n */\nconst isLikelyDirectory = (path: string): boolean => {\n if (!path) return false;\n // Check if path already ends with a slash\n if (path.endsWith(\"/\") || path.endsWith(\"\\\\\")) return true;\n // Check if path has no extension (simple heuristic)\n const lastPart = path.split(/[/\\\\]/).pop() || \"\";\n // If the last part has no dots, it's likely a directory\n return !lastPart.includes(\".\");\n};\n\n/**\n * Extracts the filename from a path\n * @param path The full path\n * @returns The filename (last part of the path)\n */\nconst extractFilename = (path: string): string => {\n if (!path) return \"\";\n // Handle both Unix and Windows paths\n const parts = path.split(/[/\\\\]/);\n const filename = parts[parts.length - 1];\n\n // Add trailing slash for directories\n if (isLikelyDirectory(path) && !filename.endsWith(\"/\")) {\n return `${filename}/`;\n }\n\n return filename;\n};\n\n/**\n * Displays only the filename, with the full path on hover.\n * Click opens the Files drawer on that path (when interactive).\n */\nfunction PathComponent(props: { children?: ReactNode }) {\n const { children } = props;\n const { conversationId } = useOptionalConversationId();\n const interactive = useContext(PathInteractiveContext);\n\n const processPath = (path: string) => {\n try {\n const decodedPath = decodeHtmlEntities(path);\n const filename = extractFilename(decodedPath);\n if (!interactive) {\n return (\n <span\n className=\"font-mono font-normal tracking-tight\"\n title={decodedPath}\n >\n {filename}\n </span>\n );\n }\n return (\n <button\n type=\"button\"\n data-testid=\"path-component-link\"\n className=\"cursor-pointer font-mono font-normal tracking-tight hover:underline\"\n title={decodedPath}\n onClick={(event) => {\n event.stopPropagation();\n openWorkspaceFile(decodedPath, conversationId);\n }}\n >\n {filename}\n </button>\n );\n } catch (e) {\n EventLogger.error(String(e));\n return (\n <span className=\"font-mono font-normal tracking-tight\">{path}</span>\n );\n }\n };\n\n if (Array.isArray(children)) {\n const processedChildren = children.map((child, index) =>\n typeof child === \"string\" ? (\n <span key={`${child}-${index}`}>{processPath(child)}</span>\n ) : (\n child\n ),\n );\n\n return (\n <span className=\"font-normal tracking-tight\">{processedChildren}</span>\n );\n }\n\n if (typeof children === \"string\") {\n return (\n <span className=\"font-normal tracking-tight\">\n {processPath(children)}\n </span>\n );\n }\n\n return (\n <span className=\"font-mono font-normal tracking-tight\">{children}</span>\n );\n}\n\nexport { PathComponent, isLikelyDirectory };\n"],"mappings":"6PAUA,IAAa,GAAA,EAAA,EAAA,eAAuC,EAAI,EAOlD,EAAsB,GAAyB,CACnD,IAAM,EAAW,SAAS,cAAc,UAAU,EAElD,MADA,GAAS,UAAY,EACd,EAAS,KAClB,EAOM,EAAqB,GACpB,EAED,EAAK,SAAS,GAAG,GAAK,EAAK,SAAS,IAAI,EAAU,GAI/C,EAFU,EAAK,MAAM,OAAO,EAAE,IAAI,GAAK,IAE7B,SAAS,GAAG,EANX,GAcd,EAAmB,GAAyB,CAChD,GAAI,CAAC,EAAM,MAAO,GAElB,IAAM,EAAQ,EAAK,MAAM,OAAO,EAC1B,EAAW,EAAM,EAAM,OAAS,GAOtC,OAJI,EAAkB,CAAI,GAAK,CAAC,EAAS,SAAS,GAAG,EAC5C,GAAG,EAAS,GAGd,CACT,EAMA,SAAS,EAAc,EAAiC,CACtD,GAAM,CAAE,YAAa,EACf,CAAE,kBAAmB,EAAA,0BAA0B,EAC/C,GAAA,EAAA,EAAA,YAAyB,CAAsB,EAE/C,EAAe,GAAiB,CACpC,GAAI,CACF,IAAM,EAAc,EAAmB,CAAI,EACrC,EAAW,EAAgB,CAAW,EAW5C,OAVK,GAWH,EAAA,EAAA,KAAC,SAAD,CACE,KAAK,SACL,cAAY,sBACZ,UAAU,sEACV,MAAO,EACP,QAAU,GAAU,CAClB,EAAM,gBAAgB,EACtB,EAAA,kBAAkB,EAAa,CAAc,CAC/C,WAEC,CACK,CAAA,GApBN,EAAA,EAAA,KAAC,OAAD,CACE,UAAU,uCACV,MAAO,WAEN,CACG,CAAA,CAiBZ,OAAS,EAAG,CAEV,OADA,EAAA,QAAY,MAAM,OAAO,CAAC,CAAC,GAEzB,EAAA,EAAA,KAAC,OAAD,CAAM,UAAU,gDAAwC,CAAW,CAAA,CAEvE,CACF,EAwBA,OAtBI,MAAM,QAAQ,CAAQ,GAUtB,EAAA,EAAA,KAAC,OAAD,CAAM,UAAU,sCATQ,EAAS,KAAK,EAAO,IAC7C,OAAO,GAAU,UACf,EAAA,EAAA,KAAC,OAAD,CAAA,SAAiC,EAAY,CAAK,CAAQ,EAA/C,GAAG,EAAM,GAAG,GAAmC,EAE1D,CAK4C,CAAwB,CAAA,EAItE,OAAO,GAAa,UAEpB,EAAA,EAAA,KAAC,OAAD,CAAM,UAAU,sCACb,EAAY,CAAQ,CACjB,CAAA,GAKR,EAAA,EAAA,KAAC,OAAD,CAAM,UAAU,uCAAwC,UAAe,CAAA,CAE3E"}
|
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
import { ReactNode } from "react";
|
|
2
|
+
/**
|
|
3
|
+
* When false, PathComponent renders a non-interactive span. EventGroup's
|
|
4
|
+
* header wraps titles in a toggle <button>, so nested path buttons would be
|
|
5
|
+
* invalid markup — that context sets this to false.
|
|
6
|
+
*/
|
|
7
|
+
export declare const PathInteractiveContext: import("react").Context<boolean>;
|
|
2
8
|
/**
|
|
3
9
|
* Checks if a path is likely a directory
|
|
4
10
|
* @param path The full path
|
|
@@ -6,8 +12,8 @@ import { ReactNode } from "react";
|
|
|
6
12
|
*/
|
|
7
13
|
declare const isLikelyDirectory: (path: string) => boolean;
|
|
8
14
|
/**
|
|
9
|
-
*
|
|
10
|
-
*
|
|
15
|
+
* Displays only the filename, with the full path on hover.
|
|
16
|
+
* Click opens the Files drawer on that path (when interactive).
|
|
11
17
|
*/
|
|
12
18
|
declare function PathComponent(props: {
|
|
13
19
|
children?: ReactNode;
|
|
@@ -1,42 +1,54 @@
|
|
|
1
|
-
import e from "../../../
|
|
2
|
-
import {
|
|
1
|
+
import { useOptionalConversationId as e } from "../../../hooks/use-conversation-id.js";
|
|
2
|
+
import { openWorkspaceFile as t } from "../../../services/canvas-ui.js";
|
|
3
|
+
import n from "../../../utils/event-logger.js";
|
|
4
|
+
import { createContext as r, useContext as i } from "react";
|
|
5
|
+
import { jsx as a } from "react/jsx-runtime";
|
|
3
6
|
//#region src/components/features/chat/path-component.tsx
|
|
4
|
-
var
|
|
7
|
+
var o = r(!0), s = (e) => {
|
|
5
8
|
let t = document.createElement("textarea");
|
|
6
9
|
return t.innerHTML = e, t.value;
|
|
7
|
-
},
|
|
10
|
+
}, c = (e) => e ? e.endsWith("/") || e.endsWith("\\") ? !0 : !(e.split(/[/\\]/).pop() || "").includes(".") : !1, l = (e) => {
|
|
8
11
|
if (!e) return "";
|
|
9
12
|
let t = e.split(/[/\\]/), n = t[t.length - 1];
|
|
10
|
-
return
|
|
13
|
+
return c(e) && !n.endsWith("/") ? `${n}/` : n;
|
|
11
14
|
};
|
|
12
|
-
function
|
|
13
|
-
let { children:
|
|
15
|
+
function u(r) {
|
|
16
|
+
let { children: c } = r, { conversationId: u } = e(), d = i(o), f = (e) => {
|
|
14
17
|
try {
|
|
15
|
-
let e = n
|
|
16
|
-
return /* @__PURE__ */
|
|
18
|
+
let n = s(e), r = l(n);
|
|
19
|
+
return d ? /* @__PURE__ */ a("button", {
|
|
20
|
+
type: "button",
|
|
21
|
+
"data-testid": "path-component-link",
|
|
22
|
+
className: "cursor-pointer font-mono font-normal tracking-tight hover:underline",
|
|
23
|
+
title: n,
|
|
24
|
+
onClick: (e) => {
|
|
25
|
+
e.stopPropagation(), t(n, u);
|
|
26
|
+
},
|
|
27
|
+
children: r
|
|
28
|
+
}) : /* @__PURE__ */ a("span", {
|
|
17
29
|
className: "font-mono font-normal tracking-tight",
|
|
18
|
-
title:
|
|
19
|
-
children:
|
|
30
|
+
title: n,
|
|
31
|
+
children: r
|
|
20
32
|
});
|
|
21
|
-
} catch (
|
|
22
|
-
return
|
|
33
|
+
} catch (t) {
|
|
34
|
+
return n.error(String(t)), /* @__PURE__ */ a("span", {
|
|
23
35
|
className: "font-mono font-normal tracking-tight",
|
|
24
|
-
children:
|
|
36
|
+
children: e
|
|
25
37
|
});
|
|
26
38
|
}
|
|
27
39
|
};
|
|
28
|
-
return Array.isArray(
|
|
40
|
+
return Array.isArray(c) ? /* @__PURE__ */ a("span", {
|
|
29
41
|
className: "font-normal tracking-tight",
|
|
30
|
-
children:
|
|
31
|
-
}) : typeof
|
|
42
|
+
children: c.map((e, t) => typeof e == "string" ? /* @__PURE__ */ a("span", { children: f(e) }, `${e}-${t}`) : e)
|
|
43
|
+
}) : typeof c == "string" ? /* @__PURE__ */ a("span", {
|
|
32
44
|
className: "font-normal tracking-tight",
|
|
33
|
-
children:
|
|
34
|
-
}) : /* @__PURE__ */
|
|
45
|
+
children: f(c)
|
|
46
|
+
}) : /* @__PURE__ */ a("span", {
|
|
35
47
|
className: "font-mono font-normal tracking-tight",
|
|
36
|
-
children:
|
|
48
|
+
children: c
|
|
37
49
|
});
|
|
38
50
|
}
|
|
39
51
|
//#endregion
|
|
40
|
-
export {
|
|
52
|
+
export { u as PathComponent, o as PathInteractiveContext };
|
|
41
53
|
|
|
42
54
|
//# sourceMappingURL=path-component.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"path-component.js","names":[],"sources":["../../../../src/components/features/chat/path-component.tsx"],"sourcesContent":["import { ReactNode } from \"react\";\nimport EventLogger from \"#/utils/event-logger\";\n\n/**\n * Decodes HTML entities in a string\n * @param text The text to decode\n * @returns The decoded text\n */\nconst decodeHtmlEntities = (text: string): string => {\n const textarea = document.createElement(\"textarea\");\n textarea.innerHTML = text;\n return textarea.value;\n};\n\n/**\n * Checks if a path is likely a directory\n * @param path The full path\n * @returns True if the path is likely a directory\n */\nconst isLikelyDirectory = (path: string): boolean => {\n if (!path) return false;\n // Check if path already ends with a slash\n if (path.endsWith(\"/\") || path.endsWith(\"\\\\\")) return true;\n // Check if path has no extension (simple heuristic)\n const lastPart = path.split(/[/\\\\]/).pop() || \"\";\n // If the last part has no dots, it's likely a directory\n return !lastPart.includes(\".\");\n};\n\n/**\n * Extracts the filename from a path\n * @param path The full path\n * @returns The filename (last part of the path)\n */\nconst extractFilename = (path: string): string => {\n if (!path) return \"\";\n // Handle both Unix and Windows paths\n const parts = path.split(/[/\\\\]/);\n const filename = parts[parts.length - 1];\n\n // Add trailing slash for directories\n if (isLikelyDirectory(path) && !filename.endsWith(\"/\")) {\n return `${filename}/`;\n }\n\n return filename;\n};\n\n/**\n *
|
|
1
|
+
{"version":3,"file":"path-component.js","names":[],"sources":["../../../../src/components/features/chat/path-component.tsx"],"sourcesContent":["import { createContext, ReactNode, useContext } from \"react\";\nimport { useOptionalConversationId } from \"#/hooks/use-conversation-id\";\nimport { openWorkspaceFile } from \"#/services/canvas-ui\";\nimport EventLogger from \"#/utils/event-logger\";\n\n/**\n * When false, PathComponent renders a non-interactive span. EventGroup's\n * header wraps titles in a toggle <button>, so nested path buttons would be\n * invalid markup — that context sets this to false.\n */\nexport const PathInteractiveContext = createContext(true);\n\n/**\n * Decodes HTML entities in a string\n * @param text The text to decode\n * @returns The decoded text\n */\nconst decodeHtmlEntities = (text: string): string => {\n const textarea = document.createElement(\"textarea\");\n textarea.innerHTML = text;\n return textarea.value;\n};\n\n/**\n * Checks if a path is likely a directory\n * @param path The full path\n * @returns True if the path is likely a directory\n */\nconst isLikelyDirectory = (path: string): boolean => {\n if (!path) return false;\n // Check if path already ends with a slash\n if (path.endsWith(\"/\") || path.endsWith(\"\\\\\")) return true;\n // Check if path has no extension (simple heuristic)\n const lastPart = path.split(/[/\\\\]/).pop() || \"\";\n // If the last part has no dots, it's likely a directory\n return !lastPart.includes(\".\");\n};\n\n/**\n * Extracts the filename from a path\n * @param path The full path\n * @returns The filename (last part of the path)\n */\nconst extractFilename = (path: string): string => {\n if (!path) return \"\";\n // Handle both Unix and Windows paths\n const parts = path.split(/[/\\\\]/);\n const filename = parts[parts.length - 1];\n\n // Add trailing slash for directories\n if (isLikelyDirectory(path) && !filename.endsWith(\"/\")) {\n return `${filename}/`;\n }\n\n return filename;\n};\n\n/**\n * Displays only the filename, with the full path on hover.\n * Click opens the Files drawer on that path (when interactive).\n */\nfunction PathComponent(props: { children?: ReactNode }) {\n const { children } = props;\n const { conversationId } = useOptionalConversationId();\n const interactive = useContext(PathInteractiveContext);\n\n const processPath = (path: string) => {\n try {\n const decodedPath = decodeHtmlEntities(path);\n const filename = extractFilename(decodedPath);\n if (!interactive) {\n return (\n <span\n className=\"font-mono font-normal tracking-tight\"\n title={decodedPath}\n >\n {filename}\n </span>\n );\n }\n return (\n <button\n type=\"button\"\n data-testid=\"path-component-link\"\n className=\"cursor-pointer font-mono font-normal tracking-tight hover:underline\"\n title={decodedPath}\n onClick={(event) => {\n event.stopPropagation();\n openWorkspaceFile(decodedPath, conversationId);\n }}\n >\n {filename}\n </button>\n );\n } catch (e) {\n EventLogger.error(String(e));\n return (\n <span className=\"font-mono font-normal tracking-tight\">{path}</span>\n );\n }\n };\n\n if (Array.isArray(children)) {\n const processedChildren = children.map((child, index) =>\n typeof child === \"string\" ? (\n <span key={`${child}-${index}`}>{processPath(child)}</span>\n ) : (\n child\n ),\n );\n\n return (\n <span className=\"font-normal tracking-tight\">{processedChildren}</span>\n );\n }\n\n if (typeof children === \"string\") {\n return (\n <span className=\"font-normal tracking-tight\">\n {processPath(children)}\n </span>\n );\n }\n\n return (\n <span className=\"font-mono font-normal tracking-tight\">{children}</span>\n );\n}\n\nexport { PathComponent, isLikelyDirectory };\n"],"mappings":";;;;;;AAUA,IAAa,IAAyB,EAAc,EAAI,GAOlD,KAAsB,MAAyB;CACnD,IAAM,IAAW,SAAS,cAAc,UAAU;CAElD,OADA,EAAS,YAAY,GACd,EAAS;AAClB,GAOM,KAAqB,MACpB,IAED,EAAK,SAAS,GAAG,KAAK,EAAK,SAAS,IAAI,IAAU,KAI/C,EAFU,EAAK,MAAM,OAAO,EAAE,IAAI,KAAK,IAE7B,SAAS,GAAG,IANX,IAcd,KAAmB,MAAyB;CAChD,IAAI,CAAC,GAAM,OAAO;CAElB,IAAM,IAAQ,EAAK,MAAM,OAAO,GAC1B,IAAW,EAAM,EAAM,SAAS;CAOtC,OAJI,EAAkB,CAAI,KAAK,CAAC,EAAS,SAAS,GAAG,IAC5C,GAAG,EAAS,KAGd;AACT;AAMA,SAAS,EAAc,GAAiC;CACtD,IAAM,EAAE,gBAAa,GACf,EAAE,sBAAmB,EAA0B,GAC/C,IAAc,EAAW,CAAsB,GAE/C,KAAe,MAAiB;EACpC,IAAI;GACF,IAAM,IAAc,EAAmB,CAAI,GACrC,IAAW,EAAgB,CAAW;GAW5C,OAVK,IAWH,kBAAC,UAAD;IACE,MAAK;IACL,eAAY;IACZ,WAAU;IACV,OAAO;IACP,UAAU,MAAU;KAElB,AADA,EAAM,gBAAgB,GACtB,EAAkB,GAAa,CAAc;IAC/C;cAEC;GACK,CAAA,IApBN,kBAAC,QAAD;IACE,WAAU;IACV,OAAO;cAEN;GACG,CAAA;EAiBZ,SAAS,GAAG;GAEV,OADA,EAAY,MAAM,OAAO,CAAC,CAAC,GAEzB,kBAAC,QAAD;IAAM,WAAU;cAAwC;GAAW,CAAA;EAEvE;CACF;CAwBA,OAtBI,MAAM,QAAQ,CAAQ,IAUtB,kBAAC,QAAD;EAAM,WAAU;YATQ,EAAS,KAAK,GAAO,MAC7C,OAAO,KAAU,WACf,kBAAC,QAAD,EAAA,UAAiC,EAAY,CAAK,EAAQ,GAA/C,GAAG,EAAM,GAAG,GAAmC,IAE1D,CAK4C;CAAwB,CAAA,IAItE,OAAO,KAAa,WAEpB,kBAAC,QAAD;EAAM,WAAU;YACb,EAAY,CAAQ;CACjB,CAAA,IAKR,kBAAC,QAAD;EAAM,WAAU;EAAwC;CAAe,CAAA;AAE3E"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require("../../../../../_virtual/_rolldown/runtime.cjs"),t=require("../../../../../hooks/use-conversation-id.cjs"),n=require("../../../../../
|
|
1
|
+
const e=require("../../../../../_virtual/_rolldown/runtime.cjs"),t=require("../../../../../hooks/use-conversation-id.cjs"),n=require("../../../../../services/canvas-ui.cjs"),r=require("../define.cjs"),i=require("../text-content.cjs"),a=require("../primitives/code-block.cjs"),o=require("../../../../../utils/get-language-from-path.cjs"),s=require("../primitives/diff-view.cjs"),c=require("../primitives/file-path-chip.cjs"),l=require("../../../../../utils/is-markdown-file-path.cjs"),u=require("../primitives/markdown-file-preview.cjs");let d=require("react");d=e.__toESM(d,1);let f=require("react/jsx-runtime");var p=({action:e,observation:t})=>t?.observation.path??e?.action.path??``;function m({action:e,observation:t,onOpenFile:n}){let r=p({action:e,observation:t}),d=t?.observation.command??e?.action.command,m=o.getLanguageFromPath(r),h=e?.action.view_range,g=d===`view`&&h?`${h[0]}-${h[1]}`:void 0,_=r?(0,f.jsx)(c.FilePathChip,{path:r,range:g,onClick:n}):null,v=e=>r&&d===`create`&&l.isMarkdownFilePath(r)?{chip:null,body:(0,f.jsx)(u.MarkdownFilePreview,{content:e,path:r,onView:n})}:{chip:_,body:(0,f.jsx)(a.CodeBlock,{code:e,language:m})};if(t){let e=t.observation,n=null,r=_;if(e.error)n=(0,f.jsx)(`span`,{className:`whitespace-pre-wrap text-xs text-danger`,children:e.error});else if(e.old_content!=null&&e.new_content!=null)n=(0,f.jsx)(s.DiffView,{oldText:e.old_content,newText:e.new_content});else{let t=e.new_content||e.output||(e.content?i.textFromContent(e.content):``);if(t){let e=v(t);r=e.chip,n=e.body}}return(0,f.jsxs)(`div`,{className:`flex flex-col gap-2`,children:[r,n]})}if(e){let t=e.action,n=null,r=_;if(t.command===`create`&&t.file_text){let e=v(t.file_text);r=e.chip,n=e.body}else (t.command===`str_replace`||t.command===`insert`)&&t.new_str!=null&&(n=(0,f.jsx)(s.DiffView,{oldText:t.old_str??``,newText:t.new_str}));return(0,f.jsxs)(`div`,{className:`flex flex-col gap-2`,children:[r,n]})}return null}function h({conversationId:e,...t}){let r=p(t),i=r&&t.observation?()=>n.openWorkspaceFile(r,e):void 0;return(0,f.jsx)(m,{...t,onOpenFile:i})}var g=r.defineVisualizer({actionKinds:[`FileEditorAction`,`StrReplaceEditorAction`],observationKinds:[`FileEditorObservation`,`StrReplaceEditorObservation`],Body:function(e){let{conversationId:n}=t.useOptionalConversationId();return n?(0,f.jsx)(h,{...e,conversationId:n}):(0,f.jsx)(m,{...e})}});exports.fileEditorVisualizer=g;
|
|
2
2
|
//# sourceMappingURL=file-editor.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file-editor.cjs","names":[],"sources":["../../../../../../src/components/features/chat/tool-visualizers/file-editor/file-editor.tsx"],"sourcesContent":["/**\n * File-editor visualizer for `file_editor` / `str_replace_editor` tools.\n *\n * Observation card: error → message; `str_replace`/`insert` → diff of the file\n * before vs after; `create`/`view` → the file content. Action card (shown while\n * the edit is in flight): `create` → new content; `str_replace` → diff of the\n * replaced snippet; `view`/`undo_edit` → just the path + range.\n *\n * Created Markdown artifacts get a height-clipped rich preview with a View bar\n * that opens the Files drawer, instead of dumping the full source into a code\n * block. Reads/edits of `.md` files keep the normal CodeBlock / DiffView path.\n */\nimport React from \"react\";\nimport { getLanguageFromPath } from \"#/utils/get-language-from-path\";\nimport {
|
|
1
|
+
{"version":3,"file":"file-editor.cjs","names":[],"sources":["../../../../../../src/components/features/chat/tool-visualizers/file-editor/file-editor.tsx"],"sourcesContent":["/**\n * File-editor visualizer for `file_editor` / `str_replace_editor` tools.\n *\n * Observation card: error → message; `str_replace`/`insert` → diff of the file\n * before vs after; `create`/`view` → the file content. Action card (shown while\n * the edit is in flight): `create` → new content; `str_replace` → diff of the\n * replaced snippet; `view`/`undo_edit` → just the path + range.\n *\n * Created Markdown artifacts get a height-clipped rich preview with a View bar\n * that opens the Files drawer, instead of dumping the full source into a code\n * block. Reads/edits of `.md` files keep the normal CodeBlock / DiffView path.\n */\nimport React from \"react\";\nimport { getLanguageFromPath } from \"#/utils/get-language-from-path\";\nimport { useOptionalConversationId } from \"#/hooks/use-conversation-id\";\nimport { openWorkspaceFile } from \"#/services/canvas-ui\";\nimport type {\n FileEditorAction,\n StrReplaceEditorAction,\n} from \"#/types/agent-server/core/base/action\";\nimport type {\n FileEditorObservation,\n StrReplaceEditorObservation,\n} from \"#/types/agent-server/core/base/observation\";\nimport { defineVisualizer, VisualizerProps } from \"../define\";\nimport { textFromContent } from \"../text-content\";\nimport { CodeBlock } from \"../primitives/code-block\";\nimport { DiffView } from \"../primitives/diff-view\";\nimport { FilePathChip } from \"../primitives/file-path-chip\";\nimport {\n isMarkdownFilePath,\n MarkdownFilePreview,\n} from \"../primitives/markdown-file-preview\";\n\ntype FileEditorCardProps = VisualizerProps<\n FileEditorAction | StrReplaceEditorAction,\n FileEditorObservation | StrReplaceEditorObservation\n>;\n\n/** The path the event touched; observations win because they are authoritative. */\nconst resolvePath = ({ action, observation }: FileEditorCardProps): string =>\n observation?.observation.path ?? action?.action.path ?? \"\";\n\ninterface FileEditorCardBodyProps extends FileEditorCardProps {\n /**\n * Deep-links the file into the Files drawer. Absent outside a conversation\n * route, where there is no drawer to open; the card then stays read-only.\n * Also omitted for in-flight creates so View does not open a missing file.\n */\n onOpenFile?: () => void;\n}\n\nfunction FileEditorCardBody({\n action,\n observation,\n onOpenFile,\n}: FileEditorCardBodyProps) {\n const path = resolvePath({ action, observation });\n const command = observation?.observation.command ?? action?.action.command;\n const language = getLanguageFromPath(path);\n\n const viewRange = action?.action.view_range;\n const range =\n command === \"view\" && viewRange\n ? `${viewRange[0]}-${viewRange[1]}`\n : undefined;\n const chip = path ? (\n <FilePathChip path={path} range={range} onClick={onOpenFile} />\n ) : null;\n\n const renderFileContent = (content: string) => {\n // Only created Markdown artifacts get the rich preview — `view` returns\n // `cat -n` numbered snippets that must stay in a CodeBlock.\n if (path && command === \"create\" && isMarkdownFilePath(path)) {\n // Markdown artifacts own their card (clipped preview + optional View),\n // so skip the separate path chip to avoid a duplicate filename affordance.\n return {\n chip: null as React.ReactNode,\n body: (\n <MarkdownFilePreview\n content={content}\n path={path}\n onView={onOpenFile}\n />\n ),\n };\n }\n return {\n chip,\n body: <CodeBlock code={content} language={language} />,\n };\n };\n\n if (observation) {\n const obs = observation.observation;\n let body: React.ReactNode = null;\n let leadingChip: React.ReactNode = chip;\n if (obs.error) {\n body = (\n <span className=\"whitespace-pre-wrap text-xs text-danger\">\n {obs.error}\n </span>\n );\n } else if (obs.old_content != null && obs.new_content != null) {\n // Nullish, not truthy: an empty string is a valid side of the diff —\n // clearing a file or inserting into an empty file must still render it.\n body = <DiffView oldText={obs.old_content} newText={obs.new_content} />;\n } else {\n // `view` returns the snippet the agent saw in `content` (the `cat -n`\n // output) rather than `output`/`new_content`, so fall back to it.\n // Mirrors the markdown path's \"prefer content for view\" handling.\n const content =\n obs.new_content ||\n obs.output ||\n (obs.content ? textFromContent(obs.content) : \"\");\n if (content) {\n const rendered = renderFileContent(content);\n leadingChip = rendered.chip;\n body = rendered.body;\n }\n }\n return (\n <div className=\"flex flex-col gap-2\">\n {leadingChip}\n {body}\n </div>\n );\n }\n\n if (action) {\n const act = action.action;\n let body: React.ReactNode = null;\n let leadingChip: React.ReactNode = chip;\n if (act.command === \"create\" && act.file_text) {\n const rendered = renderFileContent(act.file_text);\n leadingChip = rendered.chip;\n body = rendered.body;\n } else if (\n (act.command === \"str_replace\" || act.command === \"insert\") &&\n act.new_str != null\n ) {\n // `insert` carries only `new_str`, no `old_str`. Key on `new_str` and\n // default `old_str` to \"\" so an in-flight insert shows an addition diff\n // instead of nothing.\n body = <DiffView oldText={act.old_str ?? \"\"} newText={act.new_str} />;\n }\n return (\n <div className=\"flex flex-col gap-2\">\n {leadingChip}\n {body}\n </div>\n );\n }\n\n return null;\n}\n\n/**\n * Conversation-scoped wrapper: owns the Files-drawer deep link. Split from the\n * card so conversation-only navigation never runs outside a conversation route.\n */\nfunction FileEditorCardWithDrawerLink({\n conversationId,\n ...props\n}: FileEditorCardProps & { conversationId: string }) {\n const path = resolvePath(props);\n\n // Only deep-link once the observation exists — the file is not guaranteed\n // to be on disk while the create action is still in flight.\n const onOpenFile =\n path && props.observation\n ? () => openWorkspaceFile(path, conversationId)\n : undefined;\n\n return <FileEditorCardBody {...props} onOpenFile={onOpenFile} />;\n}\n\nexport const fileEditorVisualizer = defineVisualizer({\n actionKinds: [\"FileEditorAction\", \"StrReplaceEditorAction\"],\n observationKinds: [\"FileEditorObservation\", \"StrReplaceEditorObservation\"],\n Body: function FileEditorBody(props) {\n const { conversationId } = useOptionalConversationId();\n\n return conversationId ? (\n <FileEditorCardWithDrawerLink\n {...props}\n conversationId={conversationId}\n />\n ) : (\n <FileEditorCardBody {...props} />\n );\n },\n});\n"],"mappings":"omBAwCA,IAAM,GAAe,CAAE,SAAQ,iBAC7B,GAAa,YAAY,MAAQ,GAAQ,OAAO,MAAQ,GAW1D,SAAS,EAAmB,CAC1B,SACA,cACA,cAC0B,CAC1B,IAAM,EAAO,EAAY,CAAE,SAAQ,aAAY,CAAC,EAC1C,EAAU,GAAa,YAAY,SAAW,GAAQ,OAAO,QAC7D,EAAW,EAAA,oBAAoB,CAAI,EAEnC,EAAY,GAAQ,OAAO,WAC3B,EACJ,IAAY,QAAU,EAClB,GAAG,EAAU,GAAG,GAAG,EAAU,KAC7B,IAAA,GACA,EAAO,GACX,EAAA,EAAA,KAAC,EAAA,aAAD,CAAoB,OAAa,QAAO,QAAS,CAAa,CAAA,EAC5D,KAEE,EAAqB,GAGrB,GAAQ,IAAY,UAAY,EAAA,mBAAmB,CAAI,EAGlD,CACL,KAAM,KACN,MACE,EAAA,EAAA,KAAC,EAAA,oBAAD,CACW,UACH,OACN,OAAQ,CACT,CAAA,CAEL,EAEK,CACL,OACA,MAAM,EAAA,EAAA,KAAC,EAAA,UAAD,CAAW,KAAM,EAAmB,UAAW,CAAA,CACvD,EAGF,GAAI,EAAa,CACf,IAAM,EAAM,EAAY,YACpB,EAAwB,KACxB,EAA+B,EACnC,GAAI,EAAI,MACN,GACE,EAAA,EAAA,KAAC,OAAD,CAAM,UAAU,mDACb,EAAI,KACD,CAAA,OAEH,GAAI,EAAI,aAAe,MAAQ,EAAI,aAAe,KAGvD,GAAO,EAAA,EAAA,KAAC,EAAA,SAAD,CAAU,QAAS,EAAI,YAAa,QAAS,EAAI,WAAc,CAAA,MACjE,CAIL,IAAM,EACJ,EAAI,aACJ,EAAI,SACH,EAAI,QAAU,EAAA,gBAAgB,EAAI,OAAO,EAAI,IAChD,GAAI,EAAS,CACX,IAAM,EAAW,EAAkB,CAAO,EAC1C,EAAc,EAAS,KACvB,EAAO,EAAS,IAClB,CACF,CACA,OACE,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,+BAAf,CACG,EACA,CACE,GAET,CAEA,GAAI,EAAQ,CACV,IAAM,EAAM,EAAO,OACf,EAAwB,KACxB,EAA+B,EACnC,GAAI,EAAI,UAAY,UAAY,EAAI,UAAW,CAC7C,IAAM,EAAW,EAAkB,EAAI,SAAS,EAChD,EAAc,EAAS,KACvB,EAAO,EAAS,IAClB,OACG,EAAI,UAAY,eAAiB,EAAI,UAAY,WAClD,EAAI,SAAW,OAKf,GAAO,EAAA,EAAA,KAAC,EAAA,SAAD,CAAU,QAAS,EAAI,SAAW,GAAI,QAAS,EAAI,OAAU,CAAA,GAEtE,OACE,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,+BAAf,CACG,EACA,CACE,GAET,CAEA,OAAO,IACT,CAMA,SAAS,EAA6B,CACpC,iBACA,GAAG,GACgD,CACnD,IAAM,EAAO,EAAY,CAAK,EAIxB,EACJ,GAAQ,EAAM,gBACJ,EAAA,kBAAkB,EAAM,CAAc,EAC5C,IAAA,GAEN,OAAO,EAAA,EAAA,KAAC,EAAD,CAAoB,GAAI,EAAmB,YAAa,CAAA,CACjE,CAEA,IAAa,EAAuB,EAAA,iBAAiB,CACnD,YAAa,CAAC,mBAAoB,wBAAwB,EAC1D,iBAAkB,CAAC,wBAAyB,6BAA6B,EACzE,KAAM,SAAwB,EAAO,CACnC,GAAM,CAAE,kBAAmB,EAAA,0BAA0B,EAErD,OAAO,GACL,EAAA,EAAA,KAAC,EAAD,CACE,GAAI,EACY,gBACjB,CAAA,GAED,EAAA,EAAA,KAAC,EAAD,CAAoB,GAAI,CAAQ,CAAA,CAEpC,CACF,CAAC"}
|