@opengeni/react 3.1.0 → 3.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +769 -22
- package/dist/accounts.js +89 -11
- package/dist/accounts.js.map +1 -1
- package/dist/approvals.d.ts +2 -1
- package/dist/artifacts-document.js +5 -3
- package/dist/artifacts-presentation.js +5 -3
- package/dist/artifacts-spreadsheet.d.ts +1 -1
- package/dist/artifacts-spreadsheet.js +5 -3
- package/dist/artifacts.d.ts +5 -1
- package/dist/artifacts.js +536 -42
- package/dist/artifacts.js.map +1 -1
- package/dist/browser-viewer-ZWR6MWJ7.js +19 -0
- package/dist/capability-catalog-row.d.ts +20 -0
- package/dist/capability-logo-fallback.d.ts +3 -0
- package/dist/chat-interactive-block-BO2IWXVK.js +14 -0
- package/dist/chunk-2DDJ6YH6.js +41 -0
- package/dist/chunk-2DDJ6YH6.js.map +1 -0
- package/dist/chunk-2HVLQ26H.js +183 -0
- package/dist/chunk-2HVLQ26H.js.map +1 -0
- package/dist/{chunk-RGALCTWO.js → chunk-2I6R3GCZ.js} +4285 -3980
- package/dist/chunk-2I6R3GCZ.js.map +1 -0
- package/dist/{chunk-D3UIAJKY.js → chunk-2SFFYIH3.js} +1179 -166
- package/dist/chunk-2SFFYIH3.js.map +1 -0
- package/dist/chunk-3PVDYTTB.js +24 -0
- package/dist/chunk-3PVDYTTB.js.map +1 -0
- package/dist/chunk-3UZCWK46.js +298 -0
- package/dist/chunk-3UZCWK46.js.map +1 -0
- package/dist/chunk-47CKUXTW.js +293 -0
- package/dist/chunk-47CKUXTW.js.map +1 -0
- package/dist/chunk-4QYP6QUI.js +109 -0
- package/dist/chunk-4QYP6QUI.js.map +1 -0
- package/dist/chunk-5EY26VZ7.js +1053 -0
- package/dist/chunk-5EY26VZ7.js.map +1 -0
- package/dist/chunk-5I4VRWBB.js +56 -0
- package/dist/chunk-5I4VRWBB.js.map +1 -0
- package/dist/chunk-5WOR7UCL.js +191 -0
- package/dist/chunk-5WOR7UCL.js.map +1 -0
- package/dist/chunk-5YJLGOM5.js +51 -0
- package/dist/chunk-5YJLGOM5.js.map +1 -0
- package/dist/chunk-6JFN3OWG.js +386 -0
- package/dist/chunk-6JFN3OWG.js.map +1 -0
- package/dist/chunk-7QJT5O4S.js +35 -0
- package/dist/chunk-7QJT5O4S.js.map +1 -0
- package/dist/chunk-A55KV72M.js +402 -0
- package/dist/chunk-A55KV72M.js.map +1 -0
- package/dist/chunk-ADWCLI4G.js +2028 -0
- package/dist/chunk-ADWCLI4G.js.map +1 -0
- package/dist/chunk-B27E3V37.js +29 -0
- package/dist/chunk-B27E3V37.js.map +1 -0
- package/dist/chunk-B7TC2A33.js +83 -0
- package/dist/chunk-B7TC2A33.js.map +1 -0
- package/dist/chunk-BLL56F7R.js +385 -0
- package/dist/chunk-BLL56F7R.js.map +1 -0
- package/dist/chunk-BOFEQC6K.js +162 -0
- package/dist/chunk-BOFEQC6K.js.map +1 -0
- package/dist/chunk-CM7LVUKC.js +35 -0
- package/dist/chunk-CM7LVUKC.js.map +1 -0
- package/dist/chunk-CORUP6JB.js +122 -0
- package/dist/chunk-CORUP6JB.js.map +1 -0
- package/dist/chunk-CSX5FFKZ.js +35 -0
- package/dist/chunk-CSX5FFKZ.js.map +1 -0
- package/dist/chunk-EGVHJKGJ.js +3125 -0
- package/dist/chunk-EGVHJKGJ.js.map +1 -0
- package/dist/{chunk-NEXLLTP4.js → chunk-G76LSA76.js} +6 -5
- package/dist/chunk-G76LSA76.js.map +1 -0
- package/dist/{chunk-PEI6RANI.js → chunk-GQMRL52F.js} +311 -75
- package/dist/chunk-GQMRL52F.js.map +1 -0
- package/dist/{chunk-ELIYWBZR.js → chunk-GUUXOISA.js} +1113 -457
- package/dist/chunk-GUUXOISA.js.map +1 -0
- package/dist/{chunk-HUIKIDM5.js → chunk-I7X2P4FU.js} +225 -116
- package/dist/chunk-I7X2P4FU.js.map +1 -0
- package/dist/chunk-IGFZFLI2.js +1390 -0
- package/dist/chunk-IGFZFLI2.js.map +1 -0
- package/dist/{chunk-4MDLEHXL.js → chunk-IMSUVYQB.js} +26 -13
- package/dist/chunk-IMSUVYQB.js.map +1 -0
- package/dist/chunk-IOYHDEP3.js +38 -0
- package/dist/chunk-IOYHDEP3.js.map +1 -0
- package/dist/chunk-IZV5FTJC.js +55 -0
- package/dist/chunk-IZV5FTJC.js.map +1 -0
- package/dist/chunk-KF33GTWN.js +343 -0
- package/dist/chunk-KF33GTWN.js.map +1 -0
- package/dist/chunk-KSGQ5ODA.js +457 -0
- package/dist/chunk-KSGQ5ODA.js.map +1 -0
- package/dist/{chunk-OAM4WAAM.js → chunk-KZC6QRNR.js} +1513 -697
- package/dist/chunk-KZC6QRNR.js.map +1 -0
- package/dist/chunk-LJUMYUBO.js +218 -0
- package/dist/chunk-LJUMYUBO.js.map +1 -0
- package/dist/chunk-LSW2OJVV.js +380 -0
- package/dist/chunk-LSW2OJVV.js.map +1 -0
- package/dist/chunk-M357MHH3.js +2 -0
- package/dist/chunk-M357MHH3.js.map +1 -0
- package/dist/chunk-MK2TQO4Y.js +24 -0
- package/dist/chunk-MK2TQO4Y.js.map +1 -0
- package/dist/chunk-N7S72EXK.js +25 -0
- package/dist/chunk-N7S72EXK.js.map +1 -0
- package/dist/{chunk-XXWH2JLC.js → chunk-NKMYZFHA.js} +616 -621
- package/dist/chunk-NKMYZFHA.js.map +1 -0
- package/dist/chunk-NX4ADH73.js +14 -0
- package/dist/chunk-NX4ADH73.js.map +1 -0
- package/dist/chunk-O7F6UJO4.js +71 -0
- package/dist/chunk-O7F6UJO4.js.map +1 -0
- package/dist/chunk-OVB5WIG5.js +14064 -0
- package/dist/chunk-OVB5WIG5.js.map +1 -0
- package/dist/chunk-OYQQJL3J.js +16 -0
- package/dist/chunk-OYQQJL3J.js.map +1 -0
- package/dist/{chunk-H5YGYNXS.js → chunk-P4BNUO57.js} +60 -17
- package/dist/chunk-P4BNUO57.js.map +1 -0
- package/dist/{chunk-JVYQRRC4.js → chunk-PEHTX3V7.js} +21 -1
- package/dist/chunk-PEHTX3V7.js.map +1 -0
- package/dist/chunk-PMV53IMM.js +26 -0
- package/dist/chunk-PMV53IMM.js.map +1 -0
- package/dist/{chunk-KCGXPP5V.js → chunk-PYEOPA4M.js} +13 -7
- package/dist/chunk-PYEOPA4M.js.map +1 -0
- package/dist/{chunk-KZ73RL76.js → chunk-QHUE7E4N.js} +8 -3
- package/dist/chunk-QHUE7E4N.js.map +1 -0
- package/dist/chunk-QV3DJVRZ.js +11 -0
- package/dist/chunk-QV3DJVRZ.js.map +1 -0
- package/dist/chunk-R5SBNG7S.js +810 -0
- package/dist/chunk-R5SBNG7S.js.map +1 -0
- package/dist/chunk-RI42H5IU.js +326 -0
- package/dist/chunk-RI42H5IU.js.map +1 -0
- package/dist/chunk-S2QH6EZ5.js +211 -0
- package/dist/chunk-S2QH6EZ5.js.map +1 -0
- package/dist/chunk-SHLZMAJN.js +304 -0
- package/dist/chunk-SHLZMAJN.js.map +1 -0
- package/dist/chunk-TGXK4BAZ.js +293 -0
- package/dist/chunk-TGXK4BAZ.js.map +1 -0
- package/dist/{chunk-5YK2ZDIG.js → chunk-TNEZNMSN.js} +4 -10
- package/dist/chunk-TNEZNMSN.js.map +1 -0
- package/dist/{chunk-22KP6LR5.js → chunk-TXS65BB2.js} +3 -1
- package/dist/{chunk-22KP6LR5.js.map → chunk-TXS65BB2.js.map} +1 -1
- package/dist/chunk-UNJPZGNK.js +63 -0
- package/dist/chunk-UNJPZGNK.js.map +1 -0
- package/dist/{chunk-MZ2G2XBA.js → chunk-V5ZUJT2T.js} +205 -45
- package/dist/chunk-V5ZUJT2T.js.map +1 -0
- package/dist/chunk-VIUEOHXY.js +394 -0
- package/dist/chunk-VIUEOHXY.js.map +1 -0
- package/dist/chunk-WIUKB3LD.js +1911 -0
- package/dist/chunk-WIUKB3LD.js.map +1 -0
- package/dist/{chunk-ICA6R6TW.js → chunk-WNN7NO2N.js} +1451 -355
- package/dist/chunk-WNN7NO2N.js.map +1 -0
- package/dist/{chunk-YJFGAFBD.js → chunk-X5UVUIMF.js} +1892 -1223
- package/dist/chunk-X5UVUIMF.js.map +1 -0
- package/dist/chunk-XDA5QJE7.js +402 -0
- package/dist/chunk-XDA5QJE7.js.map +1 -0
- package/dist/{chunk-VZTQ73XT.js → chunk-XEA5BCMA.js} +3 -1
- package/dist/{chunk-VZTQ73XT.js.map → chunk-XEA5BCMA.js.map} +1 -1
- package/dist/chunk-XHF7OYAN.js +1150 -0
- package/dist/chunk-XHF7OYAN.js.map +1 -0
- package/dist/chunk-YSRBFJMQ.js +43 -0
- package/dist/chunk-YSRBFJMQ.js.map +1 -0
- package/dist/chunk-ZOZAPAUO.js +174 -0
- package/dist/chunk-ZOZAPAUO.js.map +1 -0
- package/dist/chunk-ZQV5W7YC.js +809 -0
- package/dist/chunk-ZQV5W7YC.js.map +1 -0
- package/dist/chunk-ZUWF4NF7.js +13 -0
- package/dist/chunk-ZUWF4NF7.js.map +1 -0
- package/dist/client.d.ts +16 -7
- package/dist/clipboard.js +3 -1
- package/dist/commands/types.d.ts +2 -0
- package/dist/components/anthropic-mark.d.ts +3 -0
- package/dist/components/approval-surface.d.ts +19 -6
- package/dist/components/artifacts/artifact-chrome.d.ts +120 -0
- package/dist/components/artifacts/artifact-sandbox.d.ts +30 -0
- package/dist/components/artifacts/artifact-surface.d.ts +4 -2
- package/dist/components/artifacts/chat-interactive-block.d.ts +29 -0
- package/dist/components/artifacts/deferred-chat-media.d.ts +15 -0
- package/dist/components/artifacts/document-editor.d.ts +4 -2
- package/dist/components/artifacts/editable-artifact-ui.d.ts +2 -1
- package/dist/components/artifacts/editable-artifact-view.d.ts +48 -0
- package/dist/components/artifacts/editable-document.d.ts +1 -1
- package/dist/components/artifacts/editable-presentation.d.ts +2 -2
- package/dist/components/artifacts/editable-spreadsheet.d.ts +8 -3
- package/dist/components/artifacts/index.d.ts +9 -2
- package/dist/components/artifacts/inline-html-document.d.ts +2 -0
- package/dist/components/artifacts/latest-task-runner.d.ts +15 -0
- package/dist/components/artifacts/presentation-editor.d.ts +6 -2
- package/dist/components/artifacts/published-html-artifact-frame.d.ts +23 -3
- package/dist/components/artifacts/session-artifact-viewer.d.ts +39 -0
- package/dist/components/artifacts/site-view.d.ts +26 -0
- package/dist/components/artifacts/spreadsheet-command-queue.d.ts +20 -0
- package/dist/components/artifacts/spreadsheet-download.d.ts +7 -0
- package/dist/components/artifacts/spreadsheet-general-display.d.ts +24 -0
- package/dist/components/artifacts/spreadsheet-grid.d.ts +10 -3
- package/dist/components/artifacts/spreadsheet-resize.d.ts +50 -0
- package/dist/components/artifacts/spreadsheet-submissions.d.ts +9 -0
- package/dist/components/artifacts/visualization-assets.gen.d.ts +2 -0
- package/dist/components/browser-select-control.d.ts +16 -0
- package/dist/components/browser-viewer.d.ts +11 -2
- package/dist/components/chat-composer.d.ts +21 -3
- package/dist/components/child-session-link.d.ts +9 -0
- package/dist/components/claude-mark.d.ts +3 -0
- package/dist/components/code-editor.d.ts +1 -4
- package/dist/components/composer-transcription-control.d.ts +7 -0
- package/dist/components/composer.d.ts +34 -67
- package/dist/components/computer-viewer.d.ts +1 -1
- package/dist/components/copy-button.d.ts +3 -1
- package/dist/components/desktop-viewer.d.ts +1 -1
- package/dist/components/fleet-tile.d.ts +2 -2
- package/dist/components/grok-mark.d.ts +26 -0
- package/dist/components/human-input-form.d.ts +28 -32
- package/dist/components/human-input-surface.d.ts +7 -2
- package/dist/components/linkified-text.d.ts +18 -0
- package/dist/components/machine-input-display.d.ts +22 -1
- package/dist/components/machines/agent-status-icon.d.ts +6 -0
- package/dist/components/machines/machine-detail.d.ts +7 -1
- package/dist/components/machines/machine-status.d.ts +48 -0
- package/dist/components/markdown-table-layout.d.ts +18 -0
- package/dist/components/markdown.d.ts +37 -4
- package/dist/components/message-timeline.d.ts +86 -7
- package/dist/components/model-mark.d.ts +29 -0
- package/dist/components/model-policy-picker-menu.d.ts +9 -0
- package/dist/components/model-policy-picker.d.ts +41 -25
- package/dist/components/new-conversation.d.ts +32 -0
- package/dist/components/open-geni-chat.d.ts +73 -0
- package/dist/components/open-geni-links.d.ts +68 -0
- package/dist/components/openrouter-mark.d.ts +6 -0
- package/dist/components/opper-mark.d.ts +7 -0
- package/dist/components/preview-loading.d.ts +2 -0
- package/dist/components/provider-recovery-notice.d.ts +16 -0
- package/dist/components/queue-item-content.d.ts +9 -0
- package/dist/components/rehype-secret-refs.d.ts +23 -0
- package/dist/components/remark-soft-line-breaks.d.ts +38 -0
- package/dist/components/sandbox-files.d.ts +3 -1
- package/dist/components/sandbox-workspace.d.ts +18 -0
- package/dist/components/secret-question-field.d.ts +31 -0
- package/dist/components/secret-ref-chip.d.ts +35 -0
- package/dist/components/session-capability-frame.d.ts +37 -0
- package/dist/components/session-chrome.d.ts +57 -8
- package/dist/components/session-commands-panel.d.ts +6 -0
- package/dist/components/session-commands.d.ts +6 -0
- package/dist/components/session-connection-request.d.ts +8 -0
- package/dist/components/session-conversation.d.ts +157 -0
- package/dist/components/session-list.d.ts +39 -0
- package/dist/components/session-mcp-capability-card.d.ts +41 -0
- package/dist/components/session-proxy-scope.d.ts +35 -0
- package/dist/components/session-status.d.ts +4 -1
- package/dist/components/session-usage-limit-notice.d.ts +39 -0
- package/dist/components/timeline-anchor.d.ts +24 -0
- package/dist/components/timeline-annotation-chrome.d.ts +25 -0
- package/dist/components/timeline-annotation-layout.d.ts +42 -0
- package/dist/components/timeline-annotation-markers.d.ts +8 -0
- package/dist/components/timeline-annotation-reveal-context.d.ts +4 -0
- package/dist/components/timeline-annotation-shared.d.ts +31 -0
- package/dist/components/timeline-annotations-dialog.d.ts +3 -2
- package/dist/components/timeline-annotations.d.ts +13 -3
- package/dist/components/timeline-reading-progress.d.ts +11 -0
- package/dist/components/timeline-search.d.ts +34 -0
- package/dist/components/timeline-settlement.d.ts +16 -0
- package/dist/components/tip-follow.d.ts +16 -0
- package/dist/components/tool-action-review.d.ts +47 -0
- package/dist/components/tool-review-history.d.ts +25 -0
- package/dist/components/usage-limit-notice.d.ts +49 -0
- package/dist/components/usage-member-list.d.ts +91 -0
- package/dist/components/usage-meter.d.ts +73 -0
- package/dist/components/use-viewer-menu-dismiss.d.ts +2 -0
- package/dist/components/user-message-body.d.ts +10 -1
- package/dist/composer-messages.d.ts +74 -0
- package/dist/composer-messages.js +9 -0
- package/dist/composer-messages.js.map +1 -0
- package/dist/composer-voice-events.d.ts +8 -0
- package/dist/composer.d.ts +5 -4
- package/dist/composer.js +26 -13
- package/dist/computer-viewer-GMLDXPON.js +20 -0
- package/dist/computer-viewer-GMLDXPON.js.map +1 -0
- package/dist/connect-account-identity.d.ts +8 -0
- package/dist/connect-accounts.d.ts +15 -0
- package/dist/connect-chooser.d.ts +18 -0
- package/dist/connect-panel.d.ts +18 -0
- package/dist/connect-setup.d.ts +18 -0
- package/dist/connect.d.ts +21 -0
- package/dist/connect.js +2149 -0
- package/dist/connect.js.map +1 -0
- package/dist/connection-catalog.d.ts +38 -0
- package/dist/connection-discovery.d.ts +12 -0
- package/dist/connection-installed.d.ts +17 -0
- package/dist/connection-logo.d.ts +9 -0
- package/dist/connection-service-logo.d.ts +8 -0
- package/dist/connection-service-presentation.d.ts +13 -0
- package/dist/connection-skeleton.d.ts +5 -0
- package/dist/connection-type-picker.d.ts +6 -0
- package/dist/conversation-timeline.d.ts +5 -0
- package/dist/desktop.d.ts +7 -0
- package/dist/desktop.js +27 -0
- package/dist/desktop.js.map +1 -0
- package/dist/device-authorization.d.ts +23 -0
- package/dist/diffs.d.ts +8 -0
- package/dist/diffs.js +15 -0
- package/dist/diffs.js.map +1 -0
- package/dist/discovery-cache.d.ts +5 -0
- package/dist/editor.d.ts +7 -0
- package/dist/editor.js +22 -0
- package/dist/editor.js.map +1 -0
- package/dist/embedded-new-voice-OCRNT6U5.js +18 -0
- package/dist/embedded-new-voice-OCRNT6U5.js.map +1 -0
- package/dist/embedded-voice-RPHPYVBT.js +18 -0
- package/dist/embedded-voice-RPHPYVBT.js.map +1 -0
- package/dist/{fleet-decision-row-GGCAT4E4.js → fleet-decision-row-SI63O5V4.js} +4 -1
- package/dist/fleet-decision-row-SI63O5V4.js.map +1 -0
- package/dist/hooks/latest-question.d.ts +25 -0
- package/dist/hooks/shared-machines-list.d.ts +4 -0
- package/dist/hooks/shared-query.d.ts +113 -0
- package/dist/hooks/use-attached-browsers.d.ts +1 -1
- package/dist/hooks/use-auth-runs.d.ts +1 -4
- package/dist/hooks/use-available-models.d.ts +1 -1
- package/dist/hooks/use-browser-frame-stream.d.ts +1 -1
- package/dist/hooks/use-browser-session.d.ts +12 -1
- package/dist/hooks/use-client-config-flags.d.ts +49 -0
- package/dist/hooks/use-codex-accounts.d.ts +59 -10
- package/dist/hooks/use-composer.d.ts +24 -2
- package/dist/hooks/use-computer-frame-stream.d.ts +1 -1
- package/dist/hooks/use-computer-input-posture.d.ts +13 -0
- package/dist/hooks/use-computer-session.d.ts +4 -0
- package/dist/hooks/use-connect.d.ts +23 -0
- package/dist/hooks/use-desktop-stream.d.ts +1 -1
- package/dist/hooks/use-file-attachments.d.ts +12 -0
- package/dist/hooks/use-goal.d.ts +2 -1
- package/dist/hooks/use-last-started-turn-policy.d.ts +5 -1
- package/dist/hooks/use-new-conversation.d.ts +56 -0
- package/dist/hooks/use-new-session-draft.d.ts +74 -0
- package/dist/hooks/use-realtime-voice-models.d.ts +7 -0
- package/dist/hooks/use-sandbox-files.d.ts +4 -1
- package/dist/hooks/use-session-conversation.d.ts +49 -0
- package/dist/hooks/use-session-events.d.ts +28 -7
- package/dist/hooks/use-session-mcp-approval-policy.d.ts +2 -2
- package/dist/hooks/use-session-model-account.d.ts +48 -0
- package/dist/hooks/use-supergrok-accounts.d.ts +7 -1
- package/dist/hooks/use-terminal-stream.d.ts +11 -5
- package/dist/hooks/use-usage.d.ts +40 -0
- package/dist/hooks/use-vaults.d.ts +53 -0
- package/dist/hooks/use-voice-input.d.ts +8 -1
- package/dist/hooks/use-workspace-edit.d.ts +4 -0
- package/dist/hooks/use-workspace-sessions.d.ts +22 -5
- package/dist/human-input-model.d.ts +122 -0
- package/dist/identity-link-accounts.d.ts +11 -0
- package/dist/identity-link-consent.d.ts +18 -0
- package/dist/index.d.ts +82 -20
- package/dist/index.js +2350 -6605
- package/dist/index.js.map +1 -1
- package/dist/interaction.d.ts +1 -1
- package/dist/interaction.js +14 -24
- package/dist/interaction.js.map +1 -1
- package/dist/latest-question-R77KWSUP.js +118 -0
- package/dist/latest-question-R77KWSUP.js.map +1 -0
- package/dist/lib/clipboard.d.ts +7 -0
- package/dist/lib/error-message.d.ts +7 -0
- package/dist/lib/format.d.ts +58 -4
- package/dist/lib/host-theme.d.ts +77 -0
- package/dist/lib/interaction-errors.d.ts +3 -0
- package/dist/lib/menu-styles.d.ts +22 -0
- package/dist/lib/notify-observer.d.ts +2 -0
- package/dist/lib/pierre-diffs-loader.d.ts +20 -0
- package/dist/lib/provider-recovery.d.ts +55 -0
- package/dist/lib/scrollbars.d.ts +11 -0
- package/dist/lib/terminal-input-readiness.d.ts +11 -0
- package/dist/lib/transient-retry.d.ts +25 -0
- package/dist/lib/workbench-peers.d.ts +36 -0
- package/dist/lib/xterm-theme.d.ts +7 -5
- package/dist/machines.js +9 -5
- package/dist/markdown-table-layout-3PV2UAAH.js +113 -0
- package/dist/markdown-table-layout-3PV2UAAH.js.map +1 -0
- package/dist/mcp-connection-status.d.ts +14 -0
- package/dist/model-mark-paths.d.ts +34 -0
- package/dist/model-mark-paths.js +16 -0
- package/dist/model-mark-paths.js.map +1 -0
- package/dist/model-picker-model.d.ts +111 -0
- package/dist/model-picker-model.js +25 -0
- package/dist/model-picker-model.js.map +1 -0
- package/dist/model-policy-picker-menu-7GJ5URPR.js +17 -0
- package/dist/model-policy-picker-menu-7GJ5URPR.js.map +1 -0
- package/dist/model-policy.d.ts +30 -3
- package/dist/model-policy.js +14 -1
- package/dist/native-previews.d.ts +28 -0
- package/dist/native-previews.js +82 -0
- package/dist/native-previews.js.map +1 -0
- package/dist/new-session-draft.d.ts +7 -0
- package/dist/new-session-draft.js +639 -0
- package/dist/new-session-draft.js.map +1 -0
- package/dist/notification-text.d.ts +27 -0
- package/dist/{noto-sans-arabic-loader-IA2T4X2A.js → noto-sans-arabic-loader-XSH2KYWJ.js} +3 -1
- package/dist/{noto-sans-arabic-loader-IA2T4X2A.js.map → noto-sans-arabic-loader-XSH2KYWJ.js.map} +1 -1
- package/dist/{noto-sans-jp-loader.generated-HFGLYBR3.js → noto-sans-jp-loader.generated-MR5L6V7K.js} +3 -1
- package/dist/{noto-sans-jp-loader.generated-HFGLYBR3.js.map → noto-sans-jp-loader.generated-MR5L6V7K.js.map} +1 -1
- package/dist/older-history.d.ts +3 -2
- package/dist/organization-model.d.ts +32 -0
- package/dist/organization-model.js +65 -0
- package/dist/organization-model.js.map +1 -0
- package/dist/{platform-activity-row-BJHUHXAT.js → platform-activity-row-5OCKOQWZ.js} +16 -78
- package/dist/platform-activity-row-5OCKOQWZ.js.map +1 -0
- package/dist/plugin-details.d.ts +14 -0
- package/dist/plugin-discovery.d.ts +25 -0
- package/dist/preview-loading-paint.d.ts +20 -0
- package/dist/provider.d.ts +25 -2
- package/dist/queue-presentation-model.d.ts +72 -0
- package/dist/queue-presentation-model.js +38 -0
- package/dist/queue-presentation-model.js.map +1 -0
- package/dist/{queue-surface-implementation-BSKUWHRI.js → queue-surface-implementation-GJBH7NKO.js} +65 -265
- package/dist/queue-surface-implementation-GJBH7NKO.js.map +1 -0
- package/dist/realtime/embedded-new-voice.d.ts +1 -0
- package/dist/realtime/embedded-voice.d.ts +1 -0
- package/dist/realtime/realtime-control.d.ts +4 -65
- package/dist/realtime/session-realtime.d.ts +78 -0
- package/dist/realtime.js +25 -1294
- package/dist/realtime.js.map +1 -1
- package/dist/sandbox-files-O27IPOZW.js +21 -0
- package/dist/sandbox-files-O27IPOZW.js.map +1 -0
- package/dist/sandbox-label-model.d.ts +30 -0
- package/dist/sandbox-label-model.js +20 -0
- package/dist/sandbox-label-model.js.map +1 -0
- package/dist/scrollbars.d.ts +1 -0
- package/dist/scrollbars.js +10 -0
- package/dist/scrollbars.js.map +1 -0
- package/dist/secret-reference-label.d.ts +14 -0
- package/dist/secrets-ui.d.ts +57 -0
- package/dist/session-agents-model.d.ts +10 -0
- package/dist/session-agents-model.js +25 -0
- package/dist/session-agents-model.js.map +1 -0
- package/dist/session-attention-model.d.ts +8 -0
- package/dist/session-attention-model.js +47 -0
- package/dist/session-attention-model.js.map +1 -0
- package/dist/session-auth-recommendation.d.ts +5 -0
- package/dist/session-capability-policy.d.ts +31 -0
- package/dist/session-feedback-model.d.ts +20 -0
- package/dist/session-feedback-model.js +56 -0
- package/dist/session-feedback-model.js.map +1 -0
- package/dist/session-list-model.d.ts +138 -0
- package/dist/session-list-model.js +274 -0
- package/dist/session-list-model.js.map +1 -0
- package/dist/session-realtime-7DJOIHQW.js +21 -0
- package/dist/session-realtime-7DJOIHQW.js.map +1 -0
- package/dist/session-realtime.d.ts +7 -0
- package/dist/session-realtime.js +19 -0
- package/dist/session-realtime.js.map +1 -0
- package/dist/session-status-model.d.ts +36 -0
- package/dist/session-ui.d.ts +39 -1
- package/dist/session-ui.js +126 -12
- package/dist/session-ui.js.map +1 -1
- package/dist/session.d.ts +8 -2
- package/dist/session.js +35 -20
- package/dist/site-snapshot.d.ts +14 -0
- package/dist/sites-ui.d.ts +24 -0
- package/dist/sites.d.ts +3 -0
- package/dist/sites.js +14 -0
- package/dist/sites.js.map +1 -0
- package/dist/skill-discovery.d.ts +47 -0
- package/dist/terminal.d.ts +8 -0
- package/dist/terminal.js +32 -0
- package/dist/terminal.js.map +1 -0
- package/dist/testing/session-scenarios.d.ts +10 -0
- package/dist/testing/session-scenarios.js +367 -0
- package/dist/testing/session-scenarios.js.map +1 -0
- package/dist/timeline/activity-rail.d.ts +11 -2
- package/dist/timeline/agent-identity.d.ts +30 -0
- package/dist/timeline/agent-row.d.ts +31 -0
- package/dist/timeline/allowance-exhausted-row.d.ts +24 -0
- package/dist/timeline/compaction-copy.d.ts +46 -0
- package/dist/timeline/entrance.d.ts +13 -1
- package/dist/timeline/focus-sequence.d.ts +9 -0
- package/dist/timeline/genie-copy.d.ts +3 -0
- package/dist/timeline/genie-loading.d.ts +47 -0
- package/dist/timeline/index.d.ts +10 -5
- package/dist/timeline/knowledge-receipt.d.ts +17 -0
- package/dist/timeline/message-sender.d.ts +19 -0
- package/dist/timeline/notice-presentation.d.ts +13 -0
- package/dist/timeline/parsers.d.ts +11 -0
- package/dist/timeline/patch-apply-command.d.ts +18 -0
- package/dist/timeline/platform-activity-presentation.d.ts +21 -0
- package/dist/timeline/platform-activity-row.d.ts +1 -2
- package/dist/timeline/presented-image.d.ts +11 -0
- package/dist/timeline/projection.d.ts +31 -2
- package/dist/timeline/provider-logo.d.ts +15 -0
- package/dist/timeline/question-nav-model.d.ts +23 -0
- package/dist/timeline/registry.d.ts +12 -1
- package/dist/timeline/retained-image.d.ts +18 -0
- package/dist/timeline/retained-loaders.d.ts +27 -0
- package/dist/timeline/rolling-activity.d.ts +11 -0
- package/dist/timeline/screenshot-lightbox.d.ts +23 -1
- package/dist/timeline/session-usage-limit-row.d.ts +11 -0
- package/dist/timeline/shared.d.ts +4 -1
- package/dist/timeline/startup-dispatch-details.d.ts +5 -0
- package/dist/timeline/startup-preference.d.ts +3 -0
- package/dist/timeline/startup-timings.d.ts +4 -0
- package/dist/timeline/tool-display-name.d.ts +5 -2
- package/dist/timeline/tool-presentation.d.ts +172 -0
- package/dist/timeline/turn-summary-model.d.ts +95 -0
- package/dist/timeline/turn-summary.d.ts +20 -54
- package/dist/timeline/types.d.ts +179 -7
- package/dist/timeline/work-presentation.d.ts +60 -0
- package/dist/timeline-annotation-markers-U3G66KUD.js +166 -0
- package/dist/timeline-annotation-markers-U3G66KUD.js.map +1 -0
- package/dist/timeline-annotation-selection-BI7QTPTZ.js +242 -0
- package/dist/timeline-annotation-selection-BI7QTPTZ.js.map +1 -0
- package/dist/timeline-annotations-dialog-FOMHSDTE.js +274 -0
- package/dist/timeline-annotations-dialog-FOMHSDTE.js.map +1 -0
- package/dist/timeline-model.d.ts +29 -0
- package/dist/timeline-model.js +254 -0
- package/dist/timeline-model.js.map +1 -0
- package/dist/usage/allowance-copy.d.ts +25 -0
- package/dist/usage/session-usage-limit.d.ts +88 -0
- package/dist/usage/summary.d.ts +35 -0
- package/dist/usage.d.ts +17 -0
- package/dist/usage.js +1187 -0
- package/dist/usage.js.map +1 -0
- package/dist/voice-recording-owner.d.ts +7 -0
- package/package.json +107 -7
- package/src/accounts.tsx +116 -14
- package/src/approvals.ts +8 -1
- package/src/artifacts-spreadsheet.ts +1 -0
- package/src/artifacts.ts +44 -0
- package/src/assets/README.md +8 -0
- package/src/assets/gmail.ico +0 -0
- package/src/assets/outlook.ico +0 -0
- package/src/capability-catalog-row.tsx +94 -0
- package/src/capability-logo-fallback.ts +22 -0
- package/src/client.ts +20 -31
- package/src/commands/registry.ts +18 -15
- package/src/commands/types.ts +2 -0
- package/src/components/anthropic-mark.tsx +10 -0
- package/src/components/approval-surface.tsx +401 -138
- package/src/components/artifacts/artifact-chrome.tsx +413 -0
- package/src/components/artifacts/artifact-sandbox.tsx +189 -0
- package/src/components/artifacts/artifact-surface.tsx +40 -20
- package/src/components/artifacts/chat-interactive-block.tsx +260 -0
- package/src/components/artifacts/deferred-chat-media.tsx +67 -0
- package/src/components/artifacts/document-editor.tsx +141 -16
- package/src/components/artifacts/editable-artifact-ui.tsx +4 -2
- package/src/components/artifacts/editable-artifact-view.tsx +197 -0
- package/src/components/artifacts/editable-artifact-workbench.tsx +5 -2
- package/src/components/artifacts/editable-document.tsx +37 -36
- package/src/components/artifacts/editable-presentation.tsx +36 -18
- package/src/components/artifacts/editable-spreadsheet.tsx +543 -103
- package/src/components/artifacts/index.ts +45 -0
- package/src/components/artifacts/inline-html-document.ts +24 -0
- package/src/components/artifacts/latest-task-runner.ts +49 -0
- package/src/components/artifacts/presentation-editor.tsx +323 -67
- package/src/components/artifacts/published-html-artifact-frame.tsx +122 -5
- package/src/components/artifacts/session-artifact-viewer.tsx +378 -0
- package/src/components/artifacts/site-view.tsx +121 -0
- package/src/components/artifacts/spreadsheet-canvas.ts +5 -10
- package/src/components/artifacts/spreadsheet-command-queue.ts +184 -0
- package/src/components/artifacts/spreadsheet-download.tsx +109 -0
- package/src/components/artifacts/spreadsheet-general-display.ts +43 -0
- package/src/components/artifacts/spreadsheet-grid.tsx +344 -144
- package/src/components/artifacts/spreadsheet-resize.tsx +421 -0
- package/src/components/artifacts/spreadsheet-submissions.tsx +46 -0
- package/src/components/artifacts/visualization-assets/base.css +1015 -0
- package/src/components/artifacts/visualization-assets/helpers.html +349 -0
- package/src/components/artifacts/visualization-assets.gen.ts +3 -0
- package/src/components/browser-select-control.tsx +312 -0
- package/src/components/browser-viewer.tsx +778 -318
- package/src/components/chat-composer.tsx +47 -5
- package/src/components/child-session-link.tsx +36 -0
- package/src/components/claude-mark.tsx +11 -0
- package/src/components/code-editor.tsx +13 -16
- package/src/components/composer-transcription-control.tsx +128 -30
- package/src/components/composer.tsx +404 -195
- package/src/components/computer-viewer.tsx +880 -253
- package/src/components/copy-button.tsx +13 -5
- package/src/components/desktop-viewer.tsx +1 -1
- package/src/components/enrollment-consent.tsx +1 -1
- package/src/components/enrollment-device-flow.tsx +1 -1
- package/src/components/file-browser.tsx +10 -14
- package/src/components/fleet-tile.tsx +9 -18
- package/src/components/generated-video-player.tsx +4 -2
- package/src/components/grok-mark.tsx +34 -0
- package/src/components/human-input-form.tsx +590 -283
- package/src/components/human-input-surface.tsx +11 -0
- package/src/components/interaction-intervention-banner.tsx +2 -2
- package/src/components/linkified-text.tsx +79 -0
- package/src/components/machine-card.tsx +35 -78
- package/src/components/machine-dock-bar.tsx +1 -1
- package/src/components/machine-input-display.ts +197 -3
- package/src/components/machines/agent-status-icon.tsx +31 -0
- package/src/components/machines/machine-detail.tsx +200 -31
- package/src/components/machines/machine-operation-policy.tsx +1 -1
- package/src/components/machines/machine-status.ts +241 -0
- package/src/components/machines-dashboard.tsx +2 -17
- package/src/components/markdown-table-layout.ts +143 -0
- package/src/components/markdown.tsx +466 -129
- package/src/components/message-timeline.tsx +2567 -728
- package/src/components/model-mark.tsx +102 -0
- package/src/components/model-picker.tsx +15 -4
- package/src/components/model-policy-picker-menu.tsx +371 -0
- package/src/components/model-policy-picker.tsx +256 -412
- package/src/components/new-conversation.tsx +221 -0
- package/src/components/open-geni-chat.tsx +309 -0
- package/src/components/open-geni-links.tsx +179 -0
- package/src/components/openrouter-mark.tsx +13 -0
- package/src/components/opper-mark.tsx +18 -0
- package/src/components/pierre-diff.tsx +41 -16
- package/src/components/pierre-file.tsx +35 -10
- package/src/components/preview-loading.tsx +109 -0
- package/src/components/provider-recovery-notice.tsx +49 -0
- package/src/components/queue-item-content.ts +14 -0
- package/src/components/queue-surface-implementation.tsx +51 -356
- package/src/components/queue-surface-state.tsx +3 -1
- package/src/components/rehype-secret-refs.ts +98 -0
- package/src/components/remark-soft-line-breaks.ts +166 -0
- package/src/components/sandbox-files.tsx +91 -15
- package/src/components/sandbox-terminal.tsx +84 -13
- package/src/components/sandbox-workspace.tsx +147 -51
- package/src/components/secret-question-field.tsx +324 -0
- package/src/components/secret-ref-chip.tsx +185 -0
- package/src/components/session-capability-frame.tsx +197 -0
- package/src/components/session-chrome.tsx +842 -251
- package/src/components/session-commands-panel.tsx +126 -0
- package/src/components/session-commands.tsx +18 -0
- package/src/components/session-connection-request.tsx +97 -0
- package/src/components/session-conversation.tsx +876 -0
- package/src/components/session-list.tsx +257 -0
- package/src/components/session-mcp-capability-card.tsx +680 -0
- package/src/components/session-proxy-scope.tsx +153 -0
- package/src/components/session-status.tsx +26 -17
- package/src/components/session-usage-limit-notice.tsx +174 -0
- package/src/components/timeline-anchor.tsx +119 -0
- package/src/components/timeline-annotation-chrome.tsx +196 -0
- package/src/components/timeline-annotation-layout.ts +268 -0
- package/src/components/timeline-annotation-markers.tsx +185 -0
- package/src/components/timeline-annotation-reveal-context.ts +9 -0
- package/src/components/timeline-annotation-selection.tsx +106 -54
- package/src/components/timeline-annotation-shared.ts +245 -0
- package/src/components/timeline-annotations-dialog.tsx +185 -128
- package/src/components/timeline-annotations.tsx +189 -45
- package/src/components/timeline-reading-progress.ts +80 -0
- package/src/components/timeline-search.tsx +168 -0
- package/src/components/timeline-settlement.ts +60 -0
- package/src/components/tip-follow.ts +52 -0
- package/src/components/tool-action-review.tsx +650 -0
- package/src/components/tool-review-history.tsx +88 -0
- package/src/components/tooltip.tsx +38 -5
- package/src/components/usage-limit-notice.tsx +179 -0
- package/src/components/usage-member-list.tsx +792 -0
- package/src/components/usage-meter.tsx +382 -0
- package/src/components/use-viewer-menu-dismiss.ts +33 -0
- package/src/components/user-message-body.tsx +27 -2
- package/src/components/workspace-dock.tsx +18 -1
- package/src/composer-messages.ts +150 -0
- package/src/composer-voice-events.ts +12 -0
- package/src/composer.ts +6 -1
- package/src/connect-account-identity.tsx +34 -0
- package/src/connect-accounts.tsx +233 -0
- package/src/connect-chooser.tsx +336 -0
- package/src/connect-panel.tsx +99 -0
- package/src/connect-setup.tsx +355 -0
- package/src/connect.ts +60 -0
- package/src/connection-assets.d.ts +4 -0
- package/src/connection-catalog.tsx +162 -0
- package/src/connection-discovery.tsx +238 -0
- package/src/connection-installed.tsx +51 -0
- package/src/connection-logo.tsx +37 -0
- package/src/connection-service-logo.tsx +55 -0
- package/src/connection-service-presentation.ts +46 -0
- package/src/connection-skeleton.tsx +16 -0
- package/src/connection-type-picker.tsx +36 -0
- package/src/conversation-timeline.ts +92 -0
- package/src/desktop.ts +24 -0
- package/src/device-authorization.tsx +98 -0
- package/src/diffs.ts +16 -0
- package/src/discovery-cache.ts +42 -0
- package/src/editor.ts +13 -0
- package/src/hooks/internal.ts +3 -2
- package/src/hooks/latest-question.ts +164 -0
- package/src/hooks/shared-machines-list.ts +34 -1
- package/src/hooks/shared-query.ts +721 -0
- package/src/hooks/use-attached-browsers.ts +1 -1
- package/src/hooks/use-auth-runs.ts +0 -27
- package/src/hooks/use-available-models.ts +21 -15
- package/src/hooks/use-browser-frame-stream.ts +19 -2
- package/src/hooks/use-browser-session.ts +680 -205
- package/src/hooks/use-browser-sessions.ts +32 -1
- package/src/hooks/use-client-config-flags.ts +118 -0
- package/src/hooks/use-codex-accounts.ts +247 -47
- package/src/hooks/use-composer.ts +276 -47
- package/src/hooks/use-computer-frame-stream.ts +25 -7
- package/src/hooks/use-computer-input-posture.ts +94 -0
- package/src/hooks/use-computer-session.ts +409 -95
- package/src/hooks/use-connect.ts +23 -0
- package/src/hooks/use-desktop-stream.ts +19 -28
- package/src/hooks/use-file-attachments.ts +80 -4
- package/src/hooks/use-goal.ts +107 -13
- package/src/hooks/use-last-started-turn-policy.ts +46 -14
- package/src/hooks/use-machines.ts +4 -0
- package/src/hooks/use-new-conversation.ts +374 -0
- package/src/hooks/use-new-session-draft.ts +813 -0
- package/src/hooks/use-realtime-voice-models.ts +46 -0
- package/src/hooks/use-sandbox-files.ts +68 -20
- package/src/hooks/use-session-background-commands.ts +22 -11
- package/src/hooks/use-session-capabilities.ts +3 -2
- package/src/hooks/use-session-conversation.ts +150 -0
- package/src/hooks/use-session-events.ts +703 -382
- package/src/hooks/use-session-lineage.ts +20 -16
- package/src/hooks/use-session-mcp-approval-policy.ts +13 -5
- package/src/hooks/use-session-model-account.ts +174 -0
- package/src/hooks/use-session.ts +53 -21
- package/src/hooks/use-slash-commands.ts +6 -11
- package/src/hooks/use-supergrok-accounts.ts +22 -8
- package/src/hooks/use-terminal-stream.ts +153 -27
- package/src/hooks/use-transcription.ts +6 -0
- package/src/hooks/use-turn-queue.ts +2 -1
- package/src/hooks/use-usage.ts +100 -0
- package/src/hooks/use-vaults.ts +153 -0
- package/src/hooks/use-video-artifact-playback.ts +1 -1
- package/src/hooks/use-voice-input.ts +127 -19
- package/src/hooks/use-workspace-capture.ts +3 -2
- package/src/hooks/use-workspace-edit.ts +23 -5
- package/src/hooks/use-workspace-sessions.ts +97 -32
- package/src/human-input-model.ts +359 -0
- package/src/identity-link-accounts.tsx +148 -0
- package/src/identity-link-consent.tsx +192 -0
- package/src/index.ts +262 -16
- package/src/interaction.ts +1 -0
- package/src/lib/clipboard.ts +29 -0
- package/src/lib/error-message.ts +37 -0
- package/src/lib/format.ts +213 -16
- package/src/lib/host-theme.ts +529 -0
- package/src/lib/interaction-errors.ts +15 -0
- package/src/lib/menu-styles.ts +44 -0
- package/src/lib/notify-observer.ts +15 -0
- package/src/lib/pierre-diffs-loader.ts +53 -0
- package/src/lib/provider-recovery.ts +243 -0
- package/src/lib/scrollbars.ts +30 -0
- package/src/lib/terminal-input-readiness.ts +51 -0
- package/src/lib/transient-retry.ts +99 -0
- package/src/lib/workbench-peers.ts +62 -0
- package/src/lib/xterm-theme.ts +23 -21
- package/src/mcp-connection-status.ts +35 -0
- package/src/model-mark-paths.ts +53 -0
- package/src/model-picker-model.ts +330 -0
- package/src/model-policy.ts +299 -74
- package/src/native-previews.ts +100 -0
- package/src/new-session-draft.ts +22 -0
- package/src/notification-text.ts +81 -0
- package/src/older-history.ts +22 -4
- package/src/organization-model.ts +118 -0
- package/src/plugin-details.tsx +185 -0
- package/src/plugin-discovery.tsx +196 -0
- package/src/preview-loading-paint.ts +68 -0
- package/src/provider.tsx +74 -15
- package/src/queue-presentation-model.ts +464 -0
- package/src/realtime/dropdown-menu.tsx +19 -12
- package/src/realtime/embedded-new-voice.tsx +1 -0
- package/src/realtime/embedded-voice.tsx +3 -0
- package/src/realtime/realtime-control.tsx +138 -501
- package/src/realtime/session-realtime.ts +566 -0
- package/src/sandbox-label-model.ts +53 -0
- package/src/scrollbars.ts +3 -0
- package/src/secret-reference-label.ts +43 -0
- package/src/secrets-ui.tsx +59 -0
- package/src/session-agents-model.ts +33 -0
- package/src/session-attention-model.ts +62 -0
- package/src/session-auth-recommendation.ts +48 -0
- package/src/session-capability-policy.ts +196 -0
- package/src/session-context.ts +3 -4
- package/src/session-feedback-model.ts +95 -0
- package/src/session-list-model.ts +434 -0
- package/src/session-realtime.ts +18 -0
- package/src/session-status-model.ts +99 -0
- package/src/session-ui.ts +98 -1
- package/src/session.ts +17 -1
- package/src/site-snapshot.ts +74 -0
- package/src/sites-ui.tsx +348 -0
- package/src/sites.ts +3 -0
- package/src/skill-discovery.tsx +208 -0
- package/src/terminal.ts +26 -0
- package/src/testing/session-scenarios.ts +404 -0
- package/src/timeline/activity-rail.tsx +242 -101
- package/src/timeline/agent-identity.tsx +84 -0
- package/src/timeline/agent-row.tsx +231 -0
- package/src/timeline/allowance-exhausted-row.tsx +86 -0
- package/src/timeline/compaction-copy.ts +115 -0
- package/src/timeline/entrance.tsx +28 -3
- package/src/timeline/fleet-decision-projection.ts +2 -1
- package/src/timeline/fleet-decision-row.tsx +1 -0
- package/src/timeline/focus-sequence.ts +44 -0
- package/src/timeline/genie-copy.ts +19 -0
- package/src/timeline/genie-loading.tsx +143 -0
- package/src/timeline/index.ts +30 -2
- package/src/timeline/knowledge-receipt.tsx +100 -0
- package/src/timeline/message-sender.tsx +71 -0
- package/src/timeline/notice-presentation.ts +48 -0
- package/src/timeline/parsers.ts +42 -2
- package/src/timeline/patch-apply-command.tsx +135 -0
- package/src/timeline/platform-activity-presentation.ts +166 -0
- package/src/timeline/platform-activity-row.tsx +15 -90
- package/src/timeline/presented-image.ts +63 -0
- package/src/timeline/projection.ts +1746 -76
- package/src/timeline/provider-logo.tsx +53 -0
- package/src/timeline/question-nav-model.ts +28 -0
- package/src/timeline/registry.ts +26 -4
- package/src/timeline/retained-image.ts +97 -0
- package/src/timeline/retained-loaders.ts +91 -0
- package/src/timeline/rolling-activity.tsx +80 -0
- package/src/timeline/screenshot-lightbox.tsx +228 -5
- package/src/timeline/session-usage-limit-row.tsx +42 -0
- package/src/timeline/shared.tsx +42 -15
- package/src/timeline/startup-dispatch-details.tsx +36 -0
- package/src/timeline/startup-preference.ts +33 -0
- package/src/timeline/startup-timings.tsx +145 -0
- package/src/timeline/tool-display-name.ts +19 -7
- package/src/timeline/tool-presentation.ts +1316 -0
- package/src/timeline/tool-renderers.tsx +835 -1138
- package/src/timeline/turn-summary-model.ts +343 -0
- package/src/timeline/turn-summary.tsx +272 -365
- package/src/timeline/types.ts +189 -6
- package/src/timeline/work-presentation.ts +189 -0
- package/src/timeline-model.ts +143 -0
- package/src/usage/allowance-copy.ts +64 -0
- package/src/usage/session-usage-limit.ts +266 -0
- package/src/usage/summary.ts +93 -0
- package/src/usage.ts +70 -0
- package/src/voice-recording-owner.ts +22 -0
- package/styles/compiled.css +2319 -748
- package/styles/connect.css +290 -0
- package/styles/connect.d.ts +1 -0
- package/styles/effective-tokens.css +1 -0
- package/styles/index.css +309 -9
- package/styles/responsive.css +99 -7
- package/styles/session-capability.css +44 -0
- package/styles/tokens.css +134 -66
- package/dist/browser-viewer-AEFHUYZK.js +0 -14
- package/dist/chunk-4MDLEHXL.js.map +0 -1
- package/dist/chunk-5YK2ZDIG.js.map +0 -1
- package/dist/chunk-D3UIAJKY.js.map +0 -1
- package/dist/chunk-ELIYWBZR.js.map +0 -1
- package/dist/chunk-H5YGYNXS.js.map +0 -1
- package/dist/chunk-HQVCKCLG.js +0 -9458
- package/dist/chunk-HQVCKCLG.js.map +0 -1
- package/dist/chunk-HUIKIDM5.js.map +0 -1
- package/dist/chunk-ICA6R6TW.js.map +0 -1
- package/dist/chunk-JVYQRRC4.js.map +0 -1
- package/dist/chunk-KCGXPP5V.js.map +0 -1
- package/dist/chunk-KZ73RL76.js.map +0 -1
- package/dist/chunk-LAKLF4PA.js +0 -141
- package/dist/chunk-LAKLF4PA.js.map +0 -1
- package/dist/chunk-MZ2G2XBA.js.map +0 -1
- package/dist/chunk-NEXLLTP4.js.map +0 -1
- package/dist/chunk-OAM4WAAM.js.map +0 -1
- package/dist/chunk-PEI6RANI.js.map +0 -1
- package/dist/chunk-RGALCTWO.js.map +0 -1
- package/dist/chunk-W6NFJXSA.js +0 -454
- package/dist/chunk-W6NFJXSA.js.map +0 -1
- package/dist/chunk-WSK7G5LE.js +0 -170
- package/dist/chunk-WSK7G5LE.js.map +0 -1
- package/dist/chunk-X4A66EWZ.js +0 -223
- package/dist/chunk-X4A66EWZ.js.map +0 -1
- package/dist/chunk-XXWH2JLC.js.map +0 -1
- package/dist/chunk-YFKJ7VZH.js +0 -268
- package/dist/chunk-YFKJ7VZH.js.map +0 -1
- package/dist/chunk-YJFGAFBD.js.map +0 -1
- package/dist/computer-viewer-FWEZISW4.js +0 -16
- package/dist/fleet-decision-row-GGCAT4E4.js.map +0 -1
- package/dist/hooks/use-environments.d.ts +0 -41
- package/dist/hooks/use-packs.d.ts +0 -32
- package/dist/platform-activity-row-BJHUHXAT.js.map +0 -1
- package/dist/queue-surface-implementation-BSKUWHRI.js.map +0 -1
- package/dist/timeline-annotation-selection-3L7LHGG2.js +0 -187
- package/dist/timeline-annotation-selection-3L7LHGG2.js.map +0 -1
- package/dist/timeline-annotations-dialog-U7EVLR75.js +0 -196
- package/dist/timeline-annotations-dialog-U7EVLR75.js.map +0 -1
- package/src/hooks/use-environments.ts +0 -182
- package/src/hooks/use-packs.ts +0 -157
- /package/dist/{browser-viewer-AEFHUYZK.js.map → browser-viewer-ZWR6MWJ7.js.map} +0 -0
- /package/dist/{computer-viewer-FWEZISW4.js.map → chat-interactive-block-BO2IWXVK.js.map} +0 -0
|
@@ -1,13 +1,25 @@
|
|
|
1
|
+
"use client";
|
|
1
2
|
import {
|
|
2
3
|
InteractionInterventionBanner,
|
|
4
|
+
isInteractionControlUnavailable,
|
|
3
5
|
isNonRetryableInteractionError,
|
|
4
6
|
isSourcePlacementChangedError,
|
|
5
7
|
useInteractionInterventions,
|
|
6
|
-
useInteractionInvalidation
|
|
7
|
-
|
|
8
|
+
useInteractionInvalidation,
|
|
9
|
+
useViewerMenuDismiss
|
|
10
|
+
} from "./chunk-P4BNUO57.js";
|
|
11
|
+
import {
|
|
12
|
+
copyTextToClipboard
|
|
13
|
+
} from "./chunk-PEHTX3V7.js";
|
|
8
14
|
import {
|
|
9
15
|
DesktopViewer
|
|
10
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-IMSUVYQB.js";
|
|
17
|
+
import {
|
|
18
|
+
usePageLiveActivity
|
|
19
|
+
} from "./chunk-QHUE7E4N.js";
|
|
20
|
+
import {
|
|
21
|
+
useEmbeddedComputerInteraction
|
|
22
|
+
} from "./chunk-G76LSA76.js";
|
|
11
23
|
import {
|
|
12
24
|
STREAM_CLOSE_REASON_NORMAL,
|
|
13
25
|
STREAM_KIND_COMPUTER,
|
|
@@ -16,19 +28,10 @@ import {
|
|
|
16
28
|
decodeStreamOpenAck,
|
|
17
29
|
encodeStreamClose,
|
|
18
30
|
encodeStreamOpen
|
|
19
|
-
} from "./chunk-
|
|
20
|
-
import {
|
|
21
|
-
usePageLiveActivity
|
|
22
|
-
} from "./chunk-KZ73RL76.js";
|
|
23
|
-
import {
|
|
24
|
-
useEmbeddedComputerInteraction
|
|
25
|
-
} from "./chunk-NEXLLTP4.js";
|
|
26
|
-
import {
|
|
27
|
-
copyTextToClipboard
|
|
28
|
-
} from "./chunk-JVYQRRC4.js";
|
|
31
|
+
} from "./chunk-TNEZNMSN.js";
|
|
29
32
|
import {
|
|
30
33
|
cn
|
|
31
|
-
} from "./chunk-
|
|
34
|
+
} from "./chunk-TXS65BB2.js";
|
|
32
35
|
|
|
33
36
|
// src/components/computer-viewer.tsx
|
|
34
37
|
import { interactionControlFailureFromError } from "@opengeni/sdk/interaction";
|
|
@@ -46,11 +49,12 @@ import {
|
|
|
46
49
|
RotateCcwIcon
|
|
47
50
|
} from "lucide-react";
|
|
48
51
|
import {
|
|
49
|
-
useCallback as
|
|
50
|
-
useEffect as
|
|
52
|
+
useCallback as useCallback5,
|
|
53
|
+
useEffect as useEffect5,
|
|
54
|
+
useLayoutEffect,
|
|
51
55
|
useMemo as useMemo2,
|
|
52
|
-
useRef as
|
|
53
|
-
useState as
|
|
56
|
+
useRef as useRef5,
|
|
57
|
+
useState as useState5
|
|
54
58
|
} from "react";
|
|
55
59
|
|
|
56
60
|
// src/hooks/use-computer-frame-stream.ts
|
|
@@ -144,9 +148,18 @@ function useComputerFrameStream(options) {
|
|
|
144
148
|
socketHandlers = null;
|
|
145
149
|
pendingFrame = null;
|
|
146
150
|
};
|
|
151
|
+
const clearRecoveryTimers = () => {
|
|
152
|
+
if (reconnectTimer) clearTimeout(reconnectTimer);
|
|
153
|
+
if (expiryTimer) clearTimeout(expiryTimer);
|
|
154
|
+
reconnectTimer = null;
|
|
155
|
+
expiryTimer = null;
|
|
156
|
+
};
|
|
147
157
|
const scheduleReconnect = () => {
|
|
148
158
|
if (disposed || reconnectTimer) return;
|
|
149
|
-
if (failures >= MAX_AUTOMATIC_RECONNECTS_WITHOUT_A_FRAME)
|
|
159
|
+
if (failures >= MAX_AUTOMATIC_RECONNECTS_WITHOUT_A_FRAME) {
|
|
160
|
+
clearRecoveryTimers();
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
150
163
|
const delay = Math.min(5e3, 250 * 2 ** Math.min(failures, 5));
|
|
151
164
|
failures += 1;
|
|
152
165
|
setResult((current) => ({ ...current, state: "reconnecting" }));
|
|
@@ -161,6 +174,7 @@ function useComputerFrameStream(options) {
|
|
|
161
174
|
setResult((current) => ({ ...current, state: "error", error }));
|
|
162
175
|
clearSocket();
|
|
163
176
|
if (reconnectAutomatically) scheduleReconnect();
|
|
177
|
+
else clearRecoveryTimers();
|
|
164
178
|
};
|
|
165
179
|
const onOpen = (source) => {
|
|
166
180
|
if (disposed || source !== socket) return;
|
|
@@ -264,8 +278,7 @@ function useComputerFrameStream(options) {
|
|
|
264
278
|
};
|
|
265
279
|
const onClose = (source) => {
|
|
266
280
|
if (disposed || source !== socket) return;
|
|
267
|
-
|
|
268
|
-
scheduleReconnect();
|
|
281
|
+
fail(new Error("Desktop view lost connection."));
|
|
269
282
|
};
|
|
270
283
|
const connectSocket = () => {
|
|
271
284
|
if (disposed) return;
|
|
@@ -320,13 +333,14 @@ function useComputerFrameStream(options) {
|
|
|
320
333
|
expiresInSeconds: 120,
|
|
321
334
|
...streamRef.current ? { stream: streamRef.current } : {}
|
|
322
335
|
},
|
|
323
|
-
{ signal: attachmentAbort.signal }
|
|
336
|
+
{ signal: attachmentAbort.signal, timeoutMs: 15e3 }
|
|
324
337
|
);
|
|
325
338
|
if (disposed) return;
|
|
326
339
|
if (attachment.computerSessionId !== computerSessionId || attachment.targetId !== targetId) {
|
|
327
340
|
throw new Error("desktop attachment does not match the requested resource");
|
|
328
341
|
}
|
|
329
342
|
controllerGeneration = attachment.controllerGeneration;
|
|
343
|
+
latestRef.current = { key: "", sequence: -1 };
|
|
330
344
|
activeAttachment = attachment;
|
|
331
345
|
activeStream = attachment.stream;
|
|
332
346
|
attachmentExpiresAt = Date.parse(attachment.expiresAt);
|
|
@@ -339,6 +353,7 @@ function useComputerFrameStream(options) {
|
|
|
339
353
|
computerSessionId: attachment.computerSessionId,
|
|
340
354
|
controllerGeneration: attachment.controllerGeneration,
|
|
341
355
|
targetId: attachment.targetId,
|
|
356
|
+
inputAllowed: attachment.inputAllowed,
|
|
342
357
|
stream: attachment.stream,
|
|
343
358
|
expiresAt: attachment.expiresAt
|
|
344
359
|
},
|
|
@@ -364,8 +379,7 @@ function useComputerFrameStream(options) {
|
|
|
364
379
|
disposed = true;
|
|
365
380
|
pendingFrame = null;
|
|
366
381
|
attachmentAbort.abort();
|
|
367
|
-
|
|
368
|
-
if (expiryTimer) clearTimeout(expiryTimer);
|
|
382
|
+
clearRecoveryTimers();
|
|
369
383
|
clearSocket(true);
|
|
370
384
|
};
|
|
371
385
|
}, [client, enabled, nonce, options.computerSessionId, options.targetId, workspaceId]);
|
|
@@ -394,27 +408,49 @@ function isPlacementGenerationLossError(cause) {
|
|
|
394
408
|
|
|
395
409
|
// src/hooks/use-computer-session.ts
|
|
396
410
|
import { useCallback as useCallback2, useEffect as useEffect2, useRef as useRef2, useState as useState2 } from "react";
|
|
411
|
+
var actionResultFences = /* @__PURE__ */ new WeakMap();
|
|
412
|
+
function isStaleComputerActionResult(value) {
|
|
413
|
+
return typeof value === "object" && value !== null && actionResultFences.get(value)?.() === false;
|
|
414
|
+
}
|
|
415
|
+
function computerActionReceiptError(receipt) {
|
|
416
|
+
const error = new Error(receipt.error?.message ?? "Desktop input did not complete.");
|
|
417
|
+
const fence = actionResultFences.get(receipt);
|
|
418
|
+
if (fence) actionResultFences.set(error, fence);
|
|
419
|
+
return error;
|
|
420
|
+
}
|
|
397
421
|
function useComputerSession(options) {
|
|
398
422
|
const { client, workspaceId } = useEmbeddedComputerInteraction(options);
|
|
399
423
|
const computerSessionId = options.computerSessionId;
|
|
400
424
|
const enabled = (options.enabled ?? true) && computerSessionId !== null;
|
|
425
|
+
const sourceRef = useRef2({
|
|
426
|
+
client,
|
|
427
|
+
workspaceId,
|
|
428
|
+
computerSessionId,
|
|
429
|
+
enabled
|
|
430
|
+
});
|
|
431
|
+
if (sourceRef.current.client !== client || sourceRef.current.workspaceId !== workspaceId || sourceRef.current.computerSessionId !== computerSessionId || sourceRef.current.enabled !== enabled) {
|
|
432
|
+
sourceRef.current = { client, workspaceId, computerSessionId, enabled };
|
|
433
|
+
}
|
|
434
|
+
const source = sourceRef.current;
|
|
401
435
|
const pageLive = usePageLiveActivity();
|
|
402
436
|
const pollIntervalMs = Math.max(750, options.pollIntervalMs ?? 2e3);
|
|
403
|
-
const [state, setState] = useState2(
|
|
404
|
-
|
|
405
|
-
);
|
|
406
|
-
const visible = state.computerSessionId === computerSessionId ? state : emptyState(computerSessionId, enabled);
|
|
437
|
+
const [state, setState] = useState2(() => emptyState(source, enabled));
|
|
438
|
+
const visible = state.source === source ? state : emptyState(source, enabled);
|
|
407
439
|
const refreshBlocked = isNonRetryableInteractionError(visible.error);
|
|
408
440
|
const selectedTargetIdRef = useRef2(visible.selectedTargetId);
|
|
409
441
|
selectedTargetIdRef.current = visible.selectedTargetId;
|
|
442
|
+
const selectionRevisionRef = useRef2(0);
|
|
443
|
+
const actionResultOrderRef = useRef2({ next: 0, settled: 0 });
|
|
410
444
|
const targetsRef = useRef2({
|
|
445
|
+
source,
|
|
411
446
|
computerSessionId,
|
|
412
447
|
targets: visible.targets
|
|
413
448
|
});
|
|
414
|
-
const observationRef = useRef2({ computerSessionId, observation: visible.observation });
|
|
415
|
-
if (targetsRef.current.
|
|
416
|
-
targetsRef.current = { computerSessionId, targets: visible.targets };
|
|
449
|
+
const observationRef = useRef2({ source, computerSessionId, observation: visible.observation });
|
|
450
|
+
if (targetsRef.current.source !== source) {
|
|
451
|
+
targetsRef.current = { source, computerSessionId, targets: visible.targets };
|
|
417
452
|
observationRef.current = {
|
|
453
|
+
source,
|
|
418
454
|
computerSessionId,
|
|
419
455
|
observation: visible.observation
|
|
420
456
|
};
|
|
@@ -428,20 +464,26 @@ function useComputerSession(options) {
|
|
|
428
464
|
});
|
|
429
465
|
const mutationRef = useRef2({
|
|
430
466
|
computerSessionId,
|
|
467
|
+
source,
|
|
431
468
|
count: 0
|
|
432
469
|
});
|
|
433
470
|
const mountedRef = useRef2(true);
|
|
471
|
+
const isCurrentSource = useCallback2(
|
|
472
|
+
() => mountedRef.current && sourceRef.current === source,
|
|
473
|
+
[source]
|
|
474
|
+
);
|
|
434
475
|
const invalidateRefresh = useCallback2(() => {
|
|
435
476
|
const id = requestRef.current.id + 1;
|
|
436
477
|
requestRef.current.controller?.abort();
|
|
437
478
|
requestRef.current = { id, controller: null };
|
|
438
479
|
}, []);
|
|
439
480
|
const refresh = useCallback2(async () => {
|
|
440
|
-
if (!enabled || !computerSessionId) return;
|
|
481
|
+
if (!enabled || !computerSessionId || !isCurrentSource()) return;
|
|
441
482
|
const id = requestRef.current.id + 1;
|
|
442
483
|
requestRef.current.controller?.abort();
|
|
443
484
|
const controller = new AbortController();
|
|
444
485
|
requestRef.current = { id, controller };
|
|
486
|
+
let discoveryCompleted = false;
|
|
445
487
|
try {
|
|
446
488
|
const [session, targetResponse] = await Promise.all([
|
|
447
489
|
client.getComputerSession(workspaceId, computerSessionId, {
|
|
@@ -451,56 +493,84 @@ function useComputerSession(options) {
|
|
|
451
493
|
signal: controller.signal
|
|
452
494
|
})
|
|
453
495
|
]);
|
|
454
|
-
|
|
496
|
+
discoveryCompleted = true;
|
|
497
|
+
if (!isCurrentSource() || requestRef.current.id !== id) return;
|
|
455
498
|
const targets = sortComputerTargets(targetResponse.targets);
|
|
456
|
-
const selected = chooseTarget(targets, selectedTargetIdRef.current
|
|
499
|
+
const selected = chooseTarget(targets, selectedTargetIdRef.current);
|
|
500
|
+
const selectedId = selected?.id ?? selectedTargetIdRef.current;
|
|
501
|
+
const previous = observationRef.current.source === source ? observationRef.current.observation : null;
|
|
502
|
+
const retainedObservation = selected && previous?.target.id === selected.id && previous.target.controllerGeneration === selected.controllerGeneration && previous.target.targetGeneration === selected.targetGeneration ? previous : null;
|
|
503
|
+
selectedTargetIdRef.current = selectedId;
|
|
504
|
+
targetsRef.current = { source, computerSessionId, targets };
|
|
505
|
+
observationRef.current = { source, computerSessionId, observation: retainedObservation };
|
|
506
|
+
setState(
|
|
507
|
+
(current) => isCurrentSource() && current.source === source && current.computerSessionId === computerSessionId ? {
|
|
508
|
+
...current,
|
|
509
|
+
session,
|
|
510
|
+
targets,
|
|
511
|
+
selectedTargetId: selectedId,
|
|
512
|
+
observation: retainedObservation,
|
|
513
|
+
loading: false,
|
|
514
|
+
error: null,
|
|
515
|
+
controlError: null
|
|
516
|
+
} : current
|
|
517
|
+
);
|
|
457
518
|
const observation = selected ? await client.observeComputerTarget(workspaceId, computerSessionId, selected.id, {
|
|
458
519
|
signal: controller.signal
|
|
459
520
|
}) : null;
|
|
460
|
-
if (!
|
|
461
|
-
selectedTargetIdRef.current =
|
|
462
|
-
targetsRef.current = { computerSessionId, targets };
|
|
463
|
-
observationRef.current = { computerSessionId, observation };
|
|
521
|
+
if (!isCurrentSource() || requestRef.current.id !== id) return;
|
|
522
|
+
selectedTargetIdRef.current = selectedId;
|
|
523
|
+
targetsRef.current = { source, computerSessionId, targets };
|
|
524
|
+
observationRef.current = { source, computerSessionId, observation };
|
|
464
525
|
setState(
|
|
465
|
-
(current) => current.computerSessionId === computerSessionId ? {
|
|
526
|
+
(current) => isCurrentSource() && current.source === source && current.computerSessionId === computerSessionId ? {
|
|
466
527
|
...current,
|
|
467
528
|
session,
|
|
468
529
|
targets,
|
|
469
|
-
selectedTargetId:
|
|
530
|
+
selectedTargetId: selectedId,
|
|
470
531
|
observation,
|
|
471
532
|
loading: false,
|
|
472
533
|
error: null
|
|
473
534
|
} : current
|
|
474
535
|
);
|
|
475
536
|
} catch (cause) {
|
|
476
|
-
if (controller.signal.aborted || !
|
|
537
|
+
if (controller.signal.aborted || !isCurrentSource() || requestRef.current.id !== id) return;
|
|
538
|
+
const error = cause instanceof Error ? cause : new Error(String(cause));
|
|
477
539
|
setState(
|
|
478
|
-
(current) => current.computerSessionId === computerSessionId ? {
|
|
540
|
+
(current) => isCurrentSource() && current.source === source && current.computerSessionId === computerSessionId ? {
|
|
479
541
|
...current,
|
|
480
542
|
loading: false,
|
|
481
|
-
error
|
|
543
|
+
error,
|
|
544
|
+
controlError: isInteractionControlUnavailable(
|
|
545
|
+
error,
|
|
546
|
+
discoveryCompleted ? "observation" : "control"
|
|
547
|
+
) ? error : current.controlError
|
|
482
548
|
} : current
|
|
483
549
|
);
|
|
484
550
|
} finally {
|
|
485
551
|
if (requestRef.current.id === id) requestRef.current = { id, controller: null };
|
|
486
552
|
}
|
|
487
|
-
}, [client, computerSessionId, enabled, workspaceId]);
|
|
553
|
+
}, [client, computerSessionId, enabled, isCurrentSource, source, workspaceId]);
|
|
488
554
|
useEffect2(() => {
|
|
489
555
|
mountedRef.current = true;
|
|
490
556
|
if (!enabled) {
|
|
491
557
|
invalidateRefresh();
|
|
492
|
-
setState(emptyState(
|
|
558
|
+
setState(emptyState(source, false));
|
|
493
559
|
return;
|
|
494
560
|
}
|
|
495
561
|
setState(
|
|
496
|
-
(current) => current.
|
|
562
|
+
(current) => current.source === source ? {
|
|
563
|
+
...current,
|
|
564
|
+
loading: true,
|
|
565
|
+
mutating: mutationRef.current.source === source && mutationRef.current.count > 0
|
|
566
|
+
} : emptyState(source, true)
|
|
497
567
|
);
|
|
498
568
|
void refresh();
|
|
499
569
|
return () => {
|
|
500
570
|
mountedRef.current = false;
|
|
501
571
|
invalidateRefresh();
|
|
502
572
|
};
|
|
503
|
-
}, [computerSessionId, enabled, invalidateRefresh, refresh]);
|
|
573
|
+
}, [computerSessionId, enabled, invalidateRefresh, refresh, source]);
|
|
504
574
|
useEffect2(() => {
|
|
505
575
|
if (!enabled || !pageLive || visible.mutating || refreshBlocked) return;
|
|
506
576
|
const timer = setInterval(() => {
|
|
@@ -523,47 +593,48 @@ function useComputerSession(options) {
|
|
|
523
593
|
}, [client, computerSessionId, enabled, pageLive, refresh, refreshBlocked, workspaceId]);
|
|
524
594
|
const runMutation = useCallback2(
|
|
525
595
|
async (scopeComputerSessionId, operation) => {
|
|
596
|
+
if (!isCurrentSource()) throw new Error("The desktop source is no longer selected.");
|
|
526
597
|
invalidateRefresh();
|
|
527
|
-
if (mutationRef.current.computerSessionId !== scopeComputerSessionId) {
|
|
598
|
+
if (mutationRef.current.computerSessionId !== scopeComputerSessionId || mutationRef.current.source !== source) {
|
|
528
599
|
mutationRef.current = {
|
|
529
600
|
computerSessionId: scopeComputerSessionId,
|
|
601
|
+
source,
|
|
530
602
|
count: 0
|
|
531
603
|
};
|
|
532
604
|
}
|
|
533
605
|
mutationRef.current.count += 1;
|
|
534
606
|
setState(
|
|
535
|
-
(current) => current.computerSessionId === scopeComputerSessionId ? { ...current, mutating: true, error: null } : current
|
|
607
|
+
(current) => isCurrentSource() && current.source === source && current.computerSessionId === scopeComputerSessionId ? { ...current, mutating: true, error: null } : current
|
|
536
608
|
);
|
|
537
609
|
try {
|
|
538
610
|
return await operation();
|
|
539
|
-
} catch (cause) {
|
|
540
|
-
const error = cause instanceof Error ? cause : new Error(String(cause));
|
|
541
|
-
setState(
|
|
542
|
-
(current) => current.computerSessionId === scopeComputerSessionId ? { ...current, error } : current
|
|
543
|
-
);
|
|
544
|
-
throw error;
|
|
545
611
|
} finally {
|
|
546
|
-
if (mutationRef.current.computerSessionId === scopeComputerSessionId) {
|
|
612
|
+
if (mutationRef.current.computerSessionId === scopeComputerSessionId && mutationRef.current.source === source) {
|
|
547
613
|
mutationRef.current.count = Math.max(0, mutationRef.current.count - 1);
|
|
548
614
|
const mutating = mutationRef.current.count > 0;
|
|
549
615
|
setState(
|
|
550
|
-
(current) => current.computerSessionId === scopeComputerSessionId ? { ...current, mutating } : current
|
|
616
|
+
(current) => isCurrentSource() && current.source === source && current.computerSessionId === scopeComputerSessionId ? { ...current, mutating } : current
|
|
551
617
|
);
|
|
552
618
|
}
|
|
553
619
|
}
|
|
554
620
|
},
|
|
555
|
-
[invalidateRefresh]
|
|
621
|
+
[invalidateRefresh, isCurrentSource, source]
|
|
556
622
|
);
|
|
557
623
|
const selectTarget = useCallback2(
|
|
558
624
|
async (targetId) => {
|
|
559
625
|
if (!computerSessionId) throw new Error("No desktop is selected.");
|
|
560
|
-
|
|
626
|
+
if (!isCurrentSource()) throw new Error("The desktop source is no longer selected.");
|
|
627
|
+
const target = targetsRef.current.source === source ? targetsRef.current.targets.find((candidate) => candidate.id === targetId) : null;
|
|
561
628
|
if (!target) throw new Error("The selected desktop target is no longer available.");
|
|
562
629
|
invalidateRefresh();
|
|
630
|
+
const readRequestId = requestRef.current.id;
|
|
631
|
+
const selectionRevision = ++selectionRevisionRef.current;
|
|
632
|
+
const isCurrentSelection = () => isCurrentSource() && selectionRevisionRef.current === selectionRevision;
|
|
633
|
+
const isCurrentSelectionRead = () => isCurrentSelection() && requestRef.current.id === readRequestId;
|
|
563
634
|
selectedTargetIdRef.current = target.id;
|
|
564
|
-
observationRef.current = { computerSessionId, observation: null };
|
|
635
|
+
observationRef.current = { source, computerSessionId, observation: null };
|
|
565
636
|
setState(
|
|
566
|
-
(current) => current.computerSessionId === computerSessionId ? {
|
|
637
|
+
(current) => isCurrentSelection() && current.source === source && current.computerSessionId === computerSessionId ? {
|
|
567
638
|
...current,
|
|
568
639
|
selectedTargetId: target.id,
|
|
569
640
|
observation: null,
|
|
@@ -577,36 +648,50 @@ function useComputerSession(options) {
|
|
|
577
648
|
computerSessionId,
|
|
578
649
|
target.id
|
|
579
650
|
);
|
|
580
|
-
if (selectedTargetIdRef.current !== target.id) return target;
|
|
581
|
-
observationRef.current = { computerSessionId, observation };
|
|
651
|
+
if (!isCurrentSelectionRead() || selectedTargetIdRef.current !== target.id) return target;
|
|
652
|
+
observationRef.current = { source, computerSessionId, observation };
|
|
582
653
|
setState(
|
|
583
|
-
(current) => current.computerSessionId === computerSessionId && current.selectedTargetId === target.id ? { ...current, observation, loading: false, error: null } : current
|
|
654
|
+
(current) => isCurrentSelection() && current.source === source && current.computerSessionId === computerSessionId && current.selectedTargetId === target.id ? { ...current, observation, loading: false, error: null } : current
|
|
584
655
|
);
|
|
585
656
|
return observation.target;
|
|
586
657
|
} catch (cause) {
|
|
587
658
|
const error = cause instanceof Error ? cause : new Error(String(cause));
|
|
588
|
-
|
|
589
|
-
(
|
|
590
|
-
|
|
659
|
+
if (isCurrentSelectionRead()) {
|
|
660
|
+
setState(
|
|
661
|
+
(current) => isCurrentSelection() && current.source === source && current.computerSessionId === computerSessionId && current.selectedTargetId === target.id ? {
|
|
662
|
+
...current,
|
|
663
|
+
loading: false,
|
|
664
|
+
error,
|
|
665
|
+
controlError: isInteractionControlUnavailable(error, "observation") ? error : current.controlError
|
|
666
|
+
} : current
|
|
667
|
+
);
|
|
668
|
+
}
|
|
591
669
|
throw error;
|
|
592
670
|
}
|
|
593
671
|
},
|
|
594
|
-
[client, computerSessionId, invalidateRefresh, workspaceId]
|
|
672
|
+
[client, computerSessionId, invalidateRefresh, isCurrentSource, source, workspaceId]
|
|
595
673
|
);
|
|
596
674
|
const dispatchAction = useCallback2(
|
|
597
675
|
async (action, operationId, frame) => {
|
|
598
676
|
if (!computerSessionId) throw new Error("No desktop is selected.");
|
|
677
|
+
if (!isCurrentSource()) throw new Error("The desktop source is no longer selected.");
|
|
599
678
|
if (frame && frame.computerSessionId !== computerSessionId) {
|
|
600
679
|
throw new Error("The displayed desktop frame belongs to another desktop session.");
|
|
601
680
|
}
|
|
602
|
-
const currentObservation = observationRef.current.computerSessionId === computerSessionId ? observationRef.current.observation : null;
|
|
603
|
-
const focusTarget = action.type === "focus" ? targetsRef.current.targets.find((candidate) => candidate.id === action.targetId) ?? null : null;
|
|
604
|
-
const frameTarget = frame
|
|
605
|
-
const
|
|
681
|
+
const currentObservation = observationRef.current.source === source && observationRef.current.computerSessionId === computerSessionId ? observationRef.current.observation : null;
|
|
682
|
+
const focusTarget = action.type === "focus" && targetsRef.current.source === source ? targetsRef.current.targets.find((candidate) => candidate.id === action.targetId) ?? null : null;
|
|
683
|
+
const frameTarget = frame && targetsRef.current.source === source ? currentObservation?.target.id === frame.targetId ? currentObservation.target : targetsRef.current.targets.find((candidate) => candidate.id === frame.targetId) ?? null : null;
|
|
684
|
+
const inputTarget = (action.type === "keyboard" || action.type === "clipboard") && targetsRef.current.source === source && targetsRef.current.computerSessionId === computerSessionId ? targetsRef.current.targets.find(
|
|
685
|
+
(candidate) => candidate.id === selectedTargetIdRef.current
|
|
686
|
+
) : null;
|
|
687
|
+
const target = frameTarget ?? focusTarget ?? currentObservation?.target ?? inputTarget ?? null;
|
|
606
688
|
if (!target) throw new Error("The desktop target is not ready for input.");
|
|
607
689
|
if (frame && frame.targetId !== selectedTargetIdRef.current) {
|
|
608
690
|
throw new Error("The displayed desktop frame is no longer selected.");
|
|
609
691
|
}
|
|
692
|
+
if (frame && (frame.controllerGeneration !== target.controllerGeneration || frame.targetGeneration !== target.targetGeneration)) {
|
|
693
|
+
throw new Error("The displayed desktop frame belongs to an earlier target generation.");
|
|
694
|
+
}
|
|
610
695
|
if (action.type === "pointer") {
|
|
611
696
|
const expectedFrameId = frame?.frameId ?? currentObservation?.frameId ?? null;
|
|
612
697
|
if (!expectedFrameId || action.frameId !== expectedFrameId) {
|
|
@@ -616,34 +701,114 @@ function useComputerSession(options) {
|
|
|
616
701
|
if (action.type === "semantic" && !currentObservation) {
|
|
617
702
|
throw new Error("The desktop accessibility tree is not ready for input.");
|
|
618
703
|
}
|
|
704
|
+
const selectedTargetId = selectedTargetIdRef.current;
|
|
705
|
+
const selectedTarget = targetsRef.current.targets.find(
|
|
706
|
+
(candidate) => candidate.id === selectedTargetId
|
|
707
|
+
);
|
|
708
|
+
const selectedFence = selectedTarget && {
|
|
709
|
+
id: selectedTarget.id,
|
|
710
|
+
computerSessionId: selectedTarget.computerSessionId,
|
|
711
|
+
controllerGeneration: selectedTarget.controllerGeneration,
|
|
712
|
+
targetGeneration: selectedTarget.targetGeneration
|
|
713
|
+
};
|
|
714
|
+
const selectionRevision = selectionRevisionRef.current;
|
|
715
|
+
const isCurrentView = () => isCurrentSource() && selectionRevisionRef.current === selectionRevision && selectedTargetIdRef.current === selectedTargetId && targetsRef.current.source === source && targetsRef.current.computerSessionId === computerSessionId && sameTargetFence(
|
|
716
|
+
selectedFence,
|
|
717
|
+
targetsRef.current.targets.find((candidate) => candidate.id === selectedTargetId)
|
|
718
|
+
);
|
|
719
|
+
const resultOrder = ++actionResultOrderRef.current.next;
|
|
720
|
+
let admitted = false;
|
|
721
|
+
let resultTargetId = selectedTargetId;
|
|
722
|
+
let resultFence = selectedFence;
|
|
723
|
+
const isCurrentResultView = () => isCurrentSource() && selectionRevisionRef.current === selectionRevision && selectedTargetIdRef.current === resultTargetId && targetsRef.current.source === source && sameTargetFence(
|
|
724
|
+
resultFence,
|
|
725
|
+
targetsRef.current.targets.find((candidate) => candidate.id === resultTargetId)
|
|
726
|
+
);
|
|
727
|
+
const canProjectResult = () => {
|
|
728
|
+
if (!admitted) {
|
|
729
|
+
if (!isCurrentView() || resultOrder < actionResultOrderRef.current.settled) return false;
|
|
730
|
+
actionResultOrderRef.current.settled = resultOrder;
|
|
731
|
+
admitted = true;
|
|
732
|
+
invalidateRefresh();
|
|
733
|
+
}
|
|
734
|
+
return isCurrentResultView() && resultOrder === actionResultOrderRef.current.settled;
|
|
735
|
+
};
|
|
736
|
+
const projectState = (update) => {
|
|
737
|
+
setState(
|
|
738
|
+
(current) => admitted && isCurrentResultView() && current.source === source && current.computerSessionId === computerSessionId && current.actionResultOrder <= resultOrder ? { ...update(current), actionResultOrder: resultOrder } : current
|
|
739
|
+
);
|
|
740
|
+
};
|
|
619
741
|
return await runMutation(computerSessionId, async () => {
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
const
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
742
|
+
try {
|
|
743
|
+
const receipt = await client.actInComputer(workspaceId, computerSessionId, {
|
|
744
|
+
operationId,
|
|
745
|
+
targetId: target.id,
|
|
746
|
+
expectedTargetGeneration: frame?.targetGeneration ?? target.targetGeneration,
|
|
747
|
+
expectedObservationId: action.type === "pointer" ? null : currentObservation?.observationId ?? null,
|
|
748
|
+
expectedFrameId: action.type === "pointer" ? action.frameId : null,
|
|
749
|
+
action
|
|
750
|
+
});
|
|
751
|
+
const receiptSettled = receipt.state === "completed" || receipt.state === "failed" || receipt.state === "outcome_unknown";
|
|
752
|
+
actionResultFences.set(
|
|
753
|
+
receipt,
|
|
754
|
+
() => admitted ? isCurrentResultView() && resultOrder === actionResultOrderRef.current.settled : isCurrentView() && resultOrder >= actionResultOrderRef.current.settled
|
|
755
|
+
);
|
|
756
|
+
if (!receiptSettled || !canProjectResult()) return receipt;
|
|
757
|
+
if (receipt.observation) {
|
|
758
|
+
const observation = receipt.observation;
|
|
759
|
+
observationRef.current = { source, computerSessionId, observation };
|
|
760
|
+
selectedTargetIdRef.current = observation.target.id;
|
|
761
|
+
targetsRef.current.targets = replaceTarget(
|
|
762
|
+
targetsRef.current.targets,
|
|
763
|
+
observation.target
|
|
764
|
+
);
|
|
765
|
+
resultTargetId = observation.target.id;
|
|
766
|
+
resultFence = {
|
|
767
|
+
id: observation.target.id,
|
|
768
|
+
computerSessionId: observation.target.computerSessionId,
|
|
769
|
+
controllerGeneration: observation.target.controllerGeneration,
|
|
770
|
+
targetGeneration: observation.target.targetGeneration
|
|
771
|
+
};
|
|
772
|
+
projectState((current) => ({
|
|
634
773
|
...current,
|
|
774
|
+
error: null,
|
|
775
|
+
controlError: null,
|
|
635
776
|
observation,
|
|
636
777
|
selectedTargetId: observation.target.id,
|
|
637
778
|
targets: replaceTarget(current.targets, observation.target)
|
|
638
|
-
}
|
|
779
|
+
}));
|
|
780
|
+
} else {
|
|
781
|
+
projectState((current) => ({ ...current, error: null, controlError: null }));
|
|
782
|
+
void refresh();
|
|
783
|
+
}
|
|
784
|
+
return receipt;
|
|
785
|
+
} catch (cause) {
|
|
786
|
+
const error = cause instanceof Error ? cause : new Error(String(cause));
|
|
787
|
+
actionResultFences.set(
|
|
788
|
+
error,
|
|
789
|
+
() => admitted && isCurrentResultView() && resultOrder === actionResultOrderRef.current.settled
|
|
639
790
|
);
|
|
640
|
-
|
|
641
|
-
|
|
791
|
+
if (canProjectResult()) {
|
|
792
|
+
projectState((current) => ({
|
|
793
|
+
...current,
|
|
794
|
+
error,
|
|
795
|
+
controlError: isInteractionControlUnavailable(error) ? error : current.controlError
|
|
796
|
+
}));
|
|
797
|
+
}
|
|
798
|
+
throw error;
|
|
642
799
|
}
|
|
643
|
-
return receipt;
|
|
644
800
|
});
|
|
645
801
|
},
|
|
646
|
-
[
|
|
802
|
+
[
|
|
803
|
+
client,
|
|
804
|
+
computerSessionId,
|
|
805
|
+
invalidateRefresh,
|
|
806
|
+
isCurrentSource,
|
|
807
|
+
refresh,
|
|
808
|
+
runMutation,
|
|
809
|
+
source,
|
|
810
|
+
workspaceId
|
|
811
|
+
]
|
|
647
812
|
);
|
|
648
813
|
const act = useCallback2(
|
|
649
814
|
async (action, operationId = crypto.randomUUID()) => await dispatchAction(action, operationId, null),
|
|
@@ -655,8 +820,39 @@ function useComputerSession(options) {
|
|
|
655
820
|
);
|
|
656
821
|
const readClipboard = useCallback2(async () => {
|
|
657
822
|
if (!computerSessionId) throw new Error("No desktop is selected.");
|
|
658
|
-
|
|
659
|
-
|
|
823
|
+
const selectionRevision = selectionRevisionRef.current;
|
|
824
|
+
const settledOrder = actionResultOrderRef.current.settled;
|
|
825
|
+
const selectedTargetId = selectedTargetIdRef.current;
|
|
826
|
+
const selectedTarget = targetsRef.current.targets.find(
|
|
827
|
+
(candidate) => candidate.id === selectedTargetId
|
|
828
|
+
);
|
|
829
|
+
const selectedFence = selectedTarget && {
|
|
830
|
+
id: selectedTarget.id,
|
|
831
|
+
computerSessionId: selectedTarget.computerSessionId,
|
|
832
|
+
controllerGeneration: selectedTarget.controllerGeneration,
|
|
833
|
+
targetGeneration: selectedTarget.targetGeneration
|
|
834
|
+
};
|
|
835
|
+
const isCurrentRead = () => isCurrentSource() && selectionRevisionRef.current === selectionRevision && actionResultOrderRef.current.settled === settledOrder && selectedTargetIdRef.current === selectedTargetId && targetsRef.current.source === source && sameTargetFence(
|
|
836
|
+
selectedFence,
|
|
837
|
+
targetsRef.current.targets.find((candidate) => candidate.id === selectedTargetId)
|
|
838
|
+
);
|
|
839
|
+
try {
|
|
840
|
+
const clipboard = await client.readComputerClipboard(workspaceId, computerSessionId);
|
|
841
|
+
actionResultFences.set(clipboard, isCurrentRead);
|
|
842
|
+
return clipboard;
|
|
843
|
+
} catch (cause) {
|
|
844
|
+
const error = cause instanceof Error ? cause : new Error(String(cause));
|
|
845
|
+
actionResultFences.set(error, isCurrentRead);
|
|
846
|
+
setState(
|
|
847
|
+
(current) => isCurrentRead() && current.source === source && current.computerSessionId === computerSessionId ? {
|
|
848
|
+
...current,
|
|
849
|
+
error,
|
|
850
|
+
controlError: isInteractionControlUnavailable(error) ? error : current.controlError
|
|
851
|
+
} : current
|
|
852
|
+
);
|
|
853
|
+
throw error;
|
|
854
|
+
}
|
|
855
|
+
}, [client, computerSessionId, isCurrentSource, source, workspaceId]);
|
|
660
856
|
return {
|
|
661
857
|
session: visible.session,
|
|
662
858
|
targets: visible.targets,
|
|
@@ -665,6 +861,7 @@ function useComputerSession(options) {
|
|
|
665
861
|
loading: visible.loading,
|
|
666
862
|
mutating: visible.mutating,
|
|
667
863
|
error: visible.error,
|
|
864
|
+
controlError: visible.controlError,
|
|
668
865
|
refresh,
|
|
669
866
|
readClipboard,
|
|
670
867
|
selectTarget,
|
|
@@ -672,25 +869,24 @@ function useComputerSession(options) {
|
|
|
672
869
|
actFromFrame
|
|
673
870
|
};
|
|
674
871
|
}
|
|
675
|
-
function emptyState(
|
|
872
|
+
function emptyState(source, loading) {
|
|
676
873
|
return {
|
|
677
|
-
|
|
874
|
+
source,
|
|
875
|
+
computerSessionId: source.computerSessionId,
|
|
678
876
|
session: null,
|
|
679
877
|
targets: [],
|
|
680
878
|
selectedTargetId: null,
|
|
681
879
|
observation: null,
|
|
682
880
|
loading,
|
|
683
881
|
mutating: false,
|
|
684
|
-
error: null
|
|
882
|
+
error: null,
|
|
883
|
+
controlError: null,
|
|
884
|
+
actionResultOrder: 0
|
|
685
885
|
};
|
|
686
886
|
}
|
|
687
|
-
function chooseTarget(targets, preferredId
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
if (platform === "linux") {
|
|
691
|
-
return targets.find((target) => target.focused && target.kind === "screen") ?? targets.find((target) => target.kind === "screen") ?? targets.find((target) => target.focused && target.kind === "window") ?? targets.find((target) => target.kind === "window") ?? targets[0] ?? null;
|
|
692
|
-
}
|
|
693
|
-
return targets.find((target) => target.focused && target.kind === "window") ?? targets.find((target) => target.kind === "window") ?? targets.find((target) => target.kind === "screen") ?? targets.find((target) => target.focused && target.kind === "app") ?? targets.find((target) => target.kind === "app") ?? targets[0] ?? null;
|
|
887
|
+
function chooseTarget(targets, preferredId) {
|
|
888
|
+
if (preferredId !== null) return targets.find((target) => target.id === preferredId) ?? null;
|
|
889
|
+
return targets.find((target) => target.focused && target.kind === "screen") ?? targets.find((target) => target.kind === "screen") ?? targets.find((target) => target.focused && target.kind === "window") ?? targets.find((target) => target.kind === "window") ?? targets.find((target) => target.focused && target.kind === "app") ?? targets.find((target) => target.kind === "app") ?? targets[0] ?? null;
|
|
694
890
|
}
|
|
695
891
|
function sortComputerTargets(targets) {
|
|
696
892
|
const rank = { window: 0, app: 1, screen: 2 };
|
|
@@ -704,9 +900,76 @@ function replaceTarget(targets, target) {
|
|
|
704
900
|
target
|
|
705
901
|
]);
|
|
706
902
|
}
|
|
903
|
+
function sameTargetFence(left, right) {
|
|
904
|
+
return left?.id === right?.id && left?.computerSessionId === right?.computerSessionId && left?.controllerGeneration === right?.controllerGeneration && left?.targetGeneration === right?.targetGeneration;
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
// src/hooks/use-computer-input-posture.ts
|
|
908
|
+
import { useCallback as useCallback3, useEffect as useEffect3, useRef as useRef3, useState as useState3 } from "react";
|
|
909
|
+
function useComputerInputPosture(options) {
|
|
910
|
+
const { client, workspaceId } = useEmbeddedComputerInteraction(options);
|
|
911
|
+
const { computerSessionId, controllerGeneration, enabled } = options;
|
|
912
|
+
const [nonce, setNonce] = useState3(0);
|
|
913
|
+
const [state, setState] = useState3(null);
|
|
914
|
+
const key = JSON.stringify([workspaceId, computerSessionId, controllerGeneration, enabled]);
|
|
915
|
+
const sourceRef = useRef3({ client, key });
|
|
916
|
+
if (sourceRef.current.client !== client || sourceRef.current.key !== key) {
|
|
917
|
+
sourceRef.current = { client, key };
|
|
918
|
+
}
|
|
919
|
+
const source = sourceRef.current;
|
|
920
|
+
const visible = state?.source === source && state.nonce === nonce ? state : null;
|
|
921
|
+
const refresh = useCallback3(() => setNonce((value) => value + 1), []);
|
|
922
|
+
useEffect3(() => {
|
|
923
|
+
if (!enabled || !computerSessionId || !controllerGeneration) return;
|
|
924
|
+
const controller = new AbortController();
|
|
925
|
+
let disposed = false;
|
|
926
|
+
setState({ source, nonce, inputAllowed: null, pending: true, error: null });
|
|
927
|
+
void (async () => {
|
|
928
|
+
try {
|
|
929
|
+
if (!client.getComputerInputPosture) {
|
|
930
|
+
throw new Error("Desktop input availability could not be verified.");
|
|
931
|
+
}
|
|
932
|
+
const posture = await client.getComputerInputPosture(workspaceId, computerSessionId, {
|
|
933
|
+
signal: controller.signal,
|
|
934
|
+
timeoutMs: 15e3
|
|
935
|
+
});
|
|
936
|
+
if (disposed || sourceRef.current !== source) return;
|
|
937
|
+
if (posture.computerSessionId !== computerSessionId || posture.controllerGeneration !== controllerGeneration || typeof posture.inputAllowed !== "boolean") {
|
|
938
|
+
throw new Error("Desktop input availability belongs to a different controller.");
|
|
939
|
+
}
|
|
940
|
+
setState({
|
|
941
|
+
source,
|
|
942
|
+
nonce,
|
|
943
|
+
inputAllowed: posture.inputAllowed,
|
|
944
|
+
pending: false,
|
|
945
|
+
error: null
|
|
946
|
+
});
|
|
947
|
+
} catch (cause) {
|
|
948
|
+
if (disposed || controller.signal.aborted || sourceRef.current !== source) return;
|
|
949
|
+
setState({
|
|
950
|
+
source,
|
|
951
|
+
nonce,
|
|
952
|
+
inputAllowed: null,
|
|
953
|
+
pending: false,
|
|
954
|
+
error: cause instanceof Error ? cause : new Error(String(cause))
|
|
955
|
+
});
|
|
956
|
+
}
|
|
957
|
+
})();
|
|
958
|
+
return () => {
|
|
959
|
+
disposed = true;
|
|
960
|
+
controller.abort();
|
|
961
|
+
};
|
|
962
|
+
}, [client, computerSessionId, controllerGeneration, enabled, nonce, source, workspaceId]);
|
|
963
|
+
return {
|
|
964
|
+
inputAllowed: visible?.inputAllowed ?? null,
|
|
965
|
+
pending: enabled && (visible?.pending ?? true),
|
|
966
|
+
error: visible?.error ?? null,
|
|
967
|
+
refresh
|
|
968
|
+
};
|
|
969
|
+
}
|
|
707
970
|
|
|
708
971
|
// src/hooks/use-computer-sessions.ts
|
|
709
|
-
import { useCallback as
|
|
972
|
+
import { useCallback as useCallback4, useEffect as useEffect4, useMemo, useRef as useRef4, useState as useState4 } from "react";
|
|
710
973
|
function useComputerSessions(options = {}) {
|
|
711
974
|
const {
|
|
712
975
|
client,
|
|
@@ -716,19 +979,19 @@ function useComputerSessions(options = {}) {
|
|
|
716
979
|
workspaceInteractionConnectionState
|
|
717
980
|
} = useEmbeddedComputerInteraction(options);
|
|
718
981
|
const enabled = options.enabled ?? true;
|
|
719
|
-
const [state, setState] =
|
|
720
|
-
const requestRef =
|
|
982
|
+
const [state, setState] = useState4(() => emptyState2(workspaceId, enabled));
|
|
983
|
+
const requestRef = useRef4({
|
|
721
984
|
id: 0,
|
|
722
985
|
controller: null
|
|
723
986
|
});
|
|
724
|
-
const mountedRef =
|
|
987
|
+
const mountedRef = useRef4(true);
|
|
725
988
|
const visible = state.workspaceId === workspaceId ? state : emptyState2(workspaceId, enabled);
|
|
726
|
-
const cancelLoad =
|
|
989
|
+
const cancelLoad = useCallback4(() => {
|
|
727
990
|
const id = requestRef.current.id + 1;
|
|
728
991
|
requestRef.current.controller?.abort();
|
|
729
992
|
requestRef.current = { id, controller: null };
|
|
730
993
|
}, []);
|
|
731
|
-
const load =
|
|
994
|
+
const load = useCallback4(
|
|
732
995
|
async (foreground) => {
|
|
733
996
|
if (!enabled) return;
|
|
734
997
|
const id = requestRef.current.id + 1;
|
|
@@ -771,8 +1034,8 @@ function useComputerSessions(options = {}) {
|
|
|
771
1034
|
},
|
|
772
1035
|
[client, enabled, workspaceId]
|
|
773
1036
|
);
|
|
774
|
-
const refresh =
|
|
775
|
-
|
|
1037
|
+
const refresh = useCallback4(async () => await load(false), [load]);
|
|
1038
|
+
useEffect4(() => {
|
|
776
1039
|
mountedRef.current = true;
|
|
777
1040
|
if (!enabled) {
|
|
778
1041
|
cancelLoad();
|
|
@@ -795,7 +1058,7 @@ function useComputerSessions(options = {}) {
|
|
|
795
1058
|
refresh,
|
|
796
1059
|
fallbackPollIntervalMs: options.pollIntervalMs
|
|
797
1060
|
});
|
|
798
|
-
|
|
1061
|
+
useEffect4(() => {
|
|
799
1062
|
if (!enabled || !options.sessionId) return;
|
|
800
1063
|
return registerSessionReconciler(
|
|
801
1064
|
options.sessionId,
|
|
@@ -803,7 +1066,7 @@ function useComputerSessions(options = {}) {
|
|
|
803
1066
|
refresh
|
|
804
1067
|
);
|
|
805
1068
|
}, [enabled, options.sessionId, refresh, registerSessionReconciler, workspaceId]);
|
|
806
|
-
const create =
|
|
1069
|
+
const create = useCallback4(
|
|
807
1070
|
async (request) => {
|
|
808
1071
|
const response = await client.createComputerSession(workspaceId, {
|
|
809
1072
|
...request,
|
|
@@ -814,7 +1077,7 @@ function useComputerSessions(options = {}) {
|
|
|
814
1077
|
},
|
|
815
1078
|
[client, workspaceId]
|
|
816
1079
|
);
|
|
817
|
-
const end =
|
|
1080
|
+
const end = useCallback4(
|
|
818
1081
|
async (computerSessionId, operationId = crypto.randomUUID()) => {
|
|
819
1082
|
const response = await client.endComputerSession(workspaceId, computerSessionId, {
|
|
820
1083
|
operationId
|
|
@@ -918,10 +1181,10 @@ function ComputerViewer({
|
|
|
918
1181
|
() => liveRelevant.length > 0 ? liveRelevant : recentRelevantFailure ? [recentRelevantFailure] : [],
|
|
919
1182
|
[liveRelevant, recentRelevantFailure]
|
|
920
1183
|
);
|
|
921
|
-
const [selection, setSelection] =
|
|
1184
|
+
const [selection, setSelection] = useState5(
|
|
922
1185
|
() => initialComputerSessionId ? { sessionId: initialComputerSessionId, pinned: true } : null
|
|
923
1186
|
);
|
|
924
|
-
const selectComputerSession =
|
|
1187
|
+
const selectComputerSession = useCallback5(
|
|
925
1188
|
(next) => {
|
|
926
1189
|
setSelection(next);
|
|
927
1190
|
onComputerSessionIdChange?.(next?.sessionId ?? null);
|
|
@@ -933,20 +1196,20 @@ function ComputerViewer({
|
|
|
933
1196
|
enabled,
|
|
934
1197
|
resourceKind: "computer_session"
|
|
935
1198
|
});
|
|
936
|
-
const [creating, setCreating] =
|
|
937
|
-
const [createError, setCreateError] =
|
|
938
|
-
const [showControls, setShowControls] =
|
|
939
|
-
const previousSessionIdRef =
|
|
940
|
-
const handledRequestRef =
|
|
941
|
-
const seenInterventionIdsRef =
|
|
942
|
-
const createInFlightRef =
|
|
943
|
-
const autoCreateSessionRef =
|
|
944
|
-
const emptyCatalogConfirmationRef =
|
|
945
|
-
const endedGenerationLossRef =
|
|
946
|
-
const recreatedPlacementLossRef =
|
|
947
|
-
const [suppressGenericCreate, setSuppressGenericCreate] =
|
|
1199
|
+
const [creating, setCreating] = useState5(false);
|
|
1200
|
+
const [createError, setCreateError] = useState5(null);
|
|
1201
|
+
const [showControls, setShowControls] = useState5(false);
|
|
1202
|
+
const previousSessionIdRef = useRef5(sessionId);
|
|
1203
|
+
const handledRequestRef = useRef5(null);
|
|
1204
|
+
const seenInterventionIdsRef = useRef5(/* @__PURE__ */ new Set());
|
|
1205
|
+
const createInFlightRef = useRef5(false);
|
|
1206
|
+
const autoCreateSessionRef = useRef5(null);
|
|
1207
|
+
const emptyCatalogConfirmationRef = useRef5(null);
|
|
1208
|
+
const endedGenerationLossRef = useRef5(/* @__PURE__ */ new Set());
|
|
1209
|
+
const recreatedPlacementLossRef = useRef5(/* @__PURE__ */ new Set());
|
|
1210
|
+
const [suppressGenericCreate, setSuppressGenericCreate] = useState5(false);
|
|
948
1211
|
const endStaleComputer = registry.end;
|
|
949
|
-
|
|
1212
|
+
useEffect5(() => {
|
|
950
1213
|
if (!enabled) return;
|
|
951
1214
|
for (const session of registry.sessions) {
|
|
952
1215
|
if (!isAttachedChromeGenerationLoss(session)) continue;
|
|
@@ -958,8 +1221,9 @@ function ComputerViewer({
|
|
|
958
1221
|
});
|
|
959
1222
|
}
|
|
960
1223
|
}, [enabled, endStaleComputer, registry.sessions]);
|
|
961
|
-
const notifyError =
|
|
1224
|
+
const notifyError = useCallback5(
|
|
962
1225
|
(cause, fallback) => {
|
|
1226
|
+
if (isStaleComputerActionResult(cause)) return;
|
|
963
1227
|
onNotify?.({
|
|
964
1228
|
kind: "error",
|
|
965
1229
|
message: cause instanceof Error ? cause.message : fallback
|
|
@@ -967,7 +1231,7 @@ function ComputerViewer({
|
|
|
967
1231
|
},
|
|
968
1232
|
[onNotify]
|
|
969
1233
|
);
|
|
970
|
-
|
|
1234
|
+
useEffect5(() => {
|
|
971
1235
|
if (previousSessionIdRef.current === sessionId) return;
|
|
972
1236
|
previousSessionIdRef.current = sessionId;
|
|
973
1237
|
handledRequestRef.current = null;
|
|
@@ -980,7 +1244,7 @@ function ComputerViewer({
|
|
|
980
1244
|
initialComputerSessionId ? { sessionId: initialComputerSessionId, pinned: true } : null
|
|
981
1245
|
);
|
|
982
1246
|
}, [initialComputerSessionId, sessionId]);
|
|
983
|
-
|
|
1247
|
+
useEffect5(() => {
|
|
984
1248
|
if (!enabled || registry.loading) return;
|
|
985
1249
|
const requested = requestedComputerSessionId ? liveSessions.find((session) => session.id === requestedComputerSessionId) ?? null : null;
|
|
986
1250
|
const requestKey = requested ? `${requestedComputerRequestId ?? "default"}:${requested.id}` : null;
|
|
@@ -1031,7 +1295,10 @@ function ComputerViewer({
|
|
|
1031
1295
|
computerSessionId: selection?.sessionId ?? null,
|
|
1032
1296
|
enabled: enabled && selection !== null && controllerReady
|
|
1033
1297
|
});
|
|
1034
|
-
|
|
1298
|
+
useEffect5(() => {
|
|
1299
|
+
setShowControls(computer.selectedTarget?.kind === "app");
|
|
1300
|
+
}, [sessionId, selection?.sessionId, computer.selectedTarget?.id, computer.selectedTarget?.kind]);
|
|
1301
|
+
useEffect5(() => {
|
|
1035
1302
|
if (!isSourcePlacementChangedError(computer.error, "computer_session")) return;
|
|
1036
1303
|
void refreshRegistry();
|
|
1037
1304
|
}, [computer.error, refreshRegistry]);
|
|
@@ -1052,15 +1319,46 @@ function ComputerViewer({
|
|
|
1052
1319
|
},
|
|
1053
1320
|
...webSocketFactory ? { webSocketFactory } : {}
|
|
1054
1321
|
});
|
|
1055
|
-
const displayedFrame = frames.frame && frames.frame.computerSessionId === selection?.sessionId && frames.frame.targetId === computer.selectedTarget?.id ? frames.frame : null;
|
|
1322
|
+
const displayedFrame = frames.frame && frames.frame.computerSessionId === selection?.sessionId && frames.frame.targetId === computer.selectedTarget?.id && frames.frame.targetGeneration === computer.selectedTarget?.targetGeneration ? frames.frame : null;
|
|
1056
1323
|
const rfbStream = frames.attachment?.stream.kind === "direct_rfb" ? frames.attachment.stream : null;
|
|
1324
|
+
const appTarget = computer.selectedTarget?.kind === "app";
|
|
1325
|
+
const appInputPosture = useComputerInputPosture({
|
|
1326
|
+
...override,
|
|
1327
|
+
computerSessionId: selection?.sessionId ?? null,
|
|
1328
|
+
controllerGeneration: computer.session?.controller?.controllerGeneration ?? null,
|
|
1329
|
+
enabled: enabled && controllerReady && appTarget
|
|
1330
|
+
});
|
|
1331
|
+
const inputPostureRef = useRef5({
|
|
1332
|
+
sessionId: selection?.sessionId ?? null,
|
|
1333
|
+
controllerGeneration: computer.session?.controller?.controllerGeneration ?? null,
|
|
1334
|
+
denied: false
|
|
1335
|
+
});
|
|
1336
|
+
const postureSessionId = selection?.sessionId ?? null;
|
|
1337
|
+
const postureController = computer.session?.controller?.controllerGeneration ?? null;
|
|
1338
|
+
if (inputPostureRef.current.sessionId !== postureSessionId || inputPostureRef.current.controllerGeneration !== postureController) {
|
|
1339
|
+
inputPostureRef.current = {
|
|
1340
|
+
sessionId: postureSessionId,
|
|
1341
|
+
controllerGeneration: postureController,
|
|
1342
|
+
denied: false
|
|
1343
|
+
};
|
|
1344
|
+
}
|
|
1345
|
+
if (frames.attachment?.computerSessionId === postureSessionId && frames.attachment.controllerGeneration === postureController) {
|
|
1346
|
+
inputPostureRef.current.denied = frames.attachment.inputAllowed === false;
|
|
1347
|
+
}
|
|
1348
|
+
const inputDenied = appTarget ? appInputPosture.inputAllowed !== true : inputPostureRef.current.denied;
|
|
1349
|
+
const attachmentInputAllowed = frames.attachment !== null && !inputDenied;
|
|
1350
|
+
const inputPosturePending = appTarget ? appInputPosture.pending : inputDenied && (frames.state === "attaching" || frames.state === "reconnecting");
|
|
1351
|
+
const machineLocked = selectedRegistrySession?.failureCode === "machine_locked";
|
|
1352
|
+
const controlUnavailable = computer.controlError !== null;
|
|
1353
|
+
const connectionState = controlUnavailable ? "error" : frames.state;
|
|
1354
|
+
const rfbInputEnabled = rfbStream?.inputAllowed === true && attachmentInputAllowed && !machineLocked && !controlUnavailable && computer.session?.capabilities?.pointerInput === true && computer.session?.capabilities?.keyboardInput === true;
|
|
1057
1355
|
const rfbCapability = useMemo2(() => {
|
|
1058
1356
|
if (!rfbStream || !frames.attachment) return null;
|
|
1059
1357
|
const bounds = computer.selectedTarget?.bounds;
|
|
1060
1358
|
return {
|
|
1061
1359
|
transport: "vnc-ws",
|
|
1062
1360
|
client: "novnc",
|
|
1063
|
-
mode: "interactive",
|
|
1361
|
+
mode: rfbInputEnabled ? "interactive" : "read-only",
|
|
1064
1362
|
url: rfbStream.url,
|
|
1065
1363
|
token: null,
|
|
1066
1364
|
expiresAt: frames.attachment.expiresAt,
|
|
@@ -1072,10 +1370,9 @@ function ComputerViewer({
|
|
|
1072
1370
|
sharedSessionIds: [],
|
|
1073
1371
|
reason: null
|
|
1074
1372
|
};
|
|
1075
|
-
}, [computer.selectedTarget?.bounds, frames.attachment, rfbStream]);
|
|
1076
|
-
const machineLocked = selectedRegistrySession?.failureCode === "machine_locked";
|
|
1373
|
+
}, [computer.selectedTarget?.bounds, frames.attachment, rfbInputEnabled, rfbStream]);
|
|
1077
1374
|
const currentIds = useMemo2(() => new Set(relevant.map((session) => session.id)), [relevant]);
|
|
1078
|
-
|
|
1375
|
+
useEffect5(() => {
|
|
1079
1376
|
for (const intervention of interventions.interventions) {
|
|
1080
1377
|
if (seenInterventionIdsRef.current.has(intervention.id)) continue;
|
|
1081
1378
|
seenInterventionIdsRef.current.add(intervention.id);
|
|
@@ -1085,7 +1382,7 @@ function ComputerViewer({
|
|
|
1085
1382
|
});
|
|
1086
1383
|
}
|
|
1087
1384
|
}, [interventions.interventions, onNotify]);
|
|
1088
|
-
const resolveIntervention =
|
|
1385
|
+
const resolveIntervention = useCallback5(
|
|
1089
1386
|
(intervention, outcome) => {
|
|
1090
1387
|
void interventions.resolve(intervention.id, {
|
|
1091
1388
|
expectedVersion: intervention.version,
|
|
@@ -1099,7 +1396,7 @@ function ComputerViewer({
|
|
|
1099
1396
|
},
|
|
1100
1397
|
[interventions, notifyError, onNotify]
|
|
1101
1398
|
);
|
|
1102
|
-
const createComputer =
|
|
1399
|
+
const createComputer = useCallback5(() => {
|
|
1103
1400
|
if (createInFlightRef.current) return;
|
|
1104
1401
|
createInFlightRef.current = true;
|
|
1105
1402
|
setCreateError(null);
|
|
@@ -1115,7 +1412,7 @@ function ComputerViewer({
|
|
|
1115
1412
|
setCreating(false);
|
|
1116
1413
|
});
|
|
1117
1414
|
}, [createSession, notifyError, selectComputerSession, sessionId]);
|
|
1118
|
-
|
|
1415
|
+
useEffect5(() => {
|
|
1119
1416
|
if (!enabled || registry.loading || registry.refreshing || liveRelevant.length > 0 || !connectedPlacementLoss || recreatedPlacementLossRef.current.has(connectedPlacementLoss.id)) {
|
|
1120
1417
|
return;
|
|
1121
1418
|
}
|
|
@@ -1132,13 +1429,13 @@ function ComputerViewer({
|
|
|
1132
1429
|
registry.refreshing,
|
|
1133
1430
|
sessionId
|
|
1134
1431
|
]);
|
|
1135
|
-
|
|
1432
|
+
useEffect5(() => {
|
|
1136
1433
|
if (relevant.length > 0) {
|
|
1137
1434
|
autoCreateSessionRef.current = sessionId;
|
|
1138
1435
|
emptyCatalogConfirmationRef.current = null;
|
|
1139
1436
|
}
|
|
1140
1437
|
}, [relevant.length, sessionId]);
|
|
1141
|
-
|
|
1438
|
+
useEffect5(() => {
|
|
1142
1439
|
if (!enabled || registry.loading || registry.refreshing || registry.error || relevant.length > 0 || suppressGenericCreate || autoCreateSessionRef.current === sessionId) {
|
|
1143
1440
|
return;
|
|
1144
1441
|
}
|
|
@@ -1160,41 +1457,62 @@ function ComputerViewer({
|
|
|
1160
1457
|
sessionId,
|
|
1161
1458
|
suppressGenericCreate
|
|
1162
1459
|
]);
|
|
1163
|
-
const perform =
|
|
1164
|
-
async (action, frame) => {
|
|
1460
|
+
const perform = useCallback5(
|
|
1461
|
+
async (action, frame, operationId) => {
|
|
1462
|
+
if (computer.controlError) throw computer.controlError;
|
|
1463
|
+
if (inputDenied) throw new Error("Desktop is view only. Refresh to check desktop controls.");
|
|
1464
|
+
let receipt;
|
|
1165
1465
|
if (action.type === "pointer") {
|
|
1166
1466
|
if (!frame) throw new Error("Desktop view is not ready for pointer input.");
|
|
1167
|
-
await actFromFrame(action, frame);
|
|
1467
|
+
receipt = await actFromFrame(action, frame, operationId);
|
|
1168
1468
|
} else {
|
|
1169
|
-
await act(action);
|
|
1469
|
+
receipt = await act(action, operationId);
|
|
1170
1470
|
}
|
|
1471
|
+
if (!isStaleComputerActionResult(receipt) && receipt.state !== "completed") {
|
|
1472
|
+
throw computerActionReceiptError(receipt);
|
|
1473
|
+
}
|
|
1474
|
+
return receipt;
|
|
1171
1475
|
},
|
|
1172
|
-
[act, actFromFrame]
|
|
1476
|
+
[act, actFromFrame, computer.controlError, inputDenied]
|
|
1173
1477
|
);
|
|
1174
|
-
const
|
|
1478
|
+
const reconnect = () => {
|
|
1479
|
+
void refreshComputer();
|
|
1480
|
+
if (appTarget) appInputPosture.refresh();
|
|
1481
|
+
else frames.reconnect();
|
|
1482
|
+
};
|
|
1483
|
+
const copyFromRfb = useCallback5(
|
|
1175
1484
|
(event) => {
|
|
1176
|
-
if (!rfbStream || computer.session?.capabilities?.clipboard !== true)
|
|
1485
|
+
if (!rfbInputEnabled || !rfbStream || computer.session?.capabilities?.clipboard !== true)
|
|
1486
|
+
return;
|
|
1177
1487
|
event.preventDefault();
|
|
1178
1488
|
event.stopPropagation();
|
|
1179
|
-
void
|
|
1489
|
+
void (async () => {
|
|
1490
|
+
const receipt = await perform({ type: "clipboard", operation: "copy" }, null);
|
|
1491
|
+
if (isStaleComputerActionResult(receipt)) return;
|
|
1492
|
+
const clipboard = await computer.readClipboard();
|
|
1493
|
+
if (isStaleComputerActionResult(receipt) || isStaleComputerActionResult(clipboard)) return;
|
|
1180
1494
|
if (clipboard.text && !await copyTextToClipboard(clipboard.text)) {
|
|
1181
1495
|
throw new Error("Desktop text could not be copied to the local clipboard");
|
|
1182
1496
|
}
|
|
1183
|
-
}).catch((cause) => notifyError(cause, "Could not copy from the desktop."));
|
|
1497
|
+
})().catch((cause) => notifyError(cause, "Could not copy from the desktop."));
|
|
1184
1498
|
},
|
|
1185
|
-
[computer, notifyError, perform, rfbStream]
|
|
1499
|
+
[computer, notifyError, perform, rfbInputEnabled, rfbStream]
|
|
1186
1500
|
);
|
|
1187
|
-
const pasteIntoRfb =
|
|
1501
|
+
const pasteIntoRfb = useCallback5(
|
|
1188
1502
|
(text) => {
|
|
1189
|
-
if (!rfbStream || computer.session?.capabilities?.clipboard !== true)
|
|
1503
|
+
if (!rfbInputEnabled || !rfbStream || computer.session?.capabilities?.clipboard !== true)
|
|
1504
|
+
return false;
|
|
1190
1505
|
void (async () => {
|
|
1191
|
-
await perform({ type: "clipboard", operation: "write", text }, null);
|
|
1192
|
-
|
|
1506
|
+
const receipt = await perform({ type: "clipboard", operation: "write", text }, null);
|
|
1507
|
+
if (isStaleComputerActionResult(receipt)) return;
|
|
1508
|
+
const clipboard = await computer.readClipboard();
|
|
1509
|
+
if (isStaleComputerActionResult(receipt) || isStaleComputerActionResult(clipboard)) return;
|
|
1510
|
+
assertExactComputerClipboard(clipboard, text);
|
|
1193
1511
|
await perform({ type: "clipboard", operation: "paste" }, null);
|
|
1194
1512
|
})().catch((cause) => notifyError(cause, "Could not paste into the desktop."));
|
|
1195
1513
|
return true;
|
|
1196
1514
|
},
|
|
1197
|
-
[computer, notifyError, perform, rfbStream]
|
|
1515
|
+
[computer, notifyError, perform, rfbInputEnabled, rfbStream]
|
|
1198
1516
|
);
|
|
1199
1517
|
const hideGenericCreate = suppressGenericCreate && liveRelevant.length === 0;
|
|
1200
1518
|
const generationLossSelected = selectedRegistrySession !== null && isAttachedChromeGenerationLoss(selectedRegistrySession);
|
|
@@ -1213,24 +1531,24 @@ function ComputerViewer({
|
|
|
1213
1531
|
if (liveSessions.length === 0) {
|
|
1214
1532
|
if (hideGenericCreate) {
|
|
1215
1533
|
return /* @__PURE__ */ jsx("div", { className: cn("grid h-full place-items-center bg-og-bg p-6", className), children: /* @__PURE__ */ jsxs("div", { className: "max-w-sm text-center", children: [
|
|
1216
|
-
/* @__PURE__ */ jsx(CircleAlertIcon, { className: "mx-auto size-5 text-og-
|
|
1534
|
+
/* @__PURE__ */ jsx(CircleAlertIcon, { className: "mx-auto size-5 text-og-danger" }),
|
|
1217
1535
|
/* @__PURE__ */ jsx("p", { className: "mt-3 text-og-menu font-medium text-og-fg", children: "Chrome reconnected\u2014open a fresh browser/desktop." }),
|
|
1218
|
-
/* @__PURE__ */ jsx("p", { className: "mt-1 text-og-control leading-5 text-og-muted", children: "This Chrome profile is live again, but the previous desktop cannot move to the new connection. Use Browser \u2192 New browser \u2192 Connected Chrome." })
|
|
1536
|
+
/* @__PURE__ */ jsx("p", { className: "mt-1 text-og-control leading-5 text-og-fg-muted", children: "This Chrome profile is live again, but the previous desktop cannot move to the new connection. Use Browser \u2192 New browser \u2192 Connected Chrome." })
|
|
1219
1537
|
] }) });
|
|
1220
1538
|
}
|
|
1221
1539
|
if (renderEmpty) return renderEmpty(createComputer, creating);
|
|
1222
1540
|
const error = createError ?? registry.error;
|
|
1223
1541
|
return /* @__PURE__ */ jsx("div", { className: cn("grid h-full place-items-center bg-og-bg p-6", className), children: /* @__PURE__ */ jsxs("div", { className: "max-w-sm text-center", children: [
|
|
1224
|
-
/* @__PURE__ */ jsx("span", { className: "mx-auto grid size-10 place-items-center rounded-og-lg border border-og-border bg-og-surface-1 text-og-muted", children: error ? /* @__PURE__ */ jsx(CircleAlertIcon, { className: "size-5 text-og-
|
|
1542
|
+
/* @__PURE__ */ jsx("span", { className: "mx-auto grid size-10 place-items-center rounded-og-lg border border-og-border bg-og-surface-1 text-og-fg-muted", children: error ? /* @__PURE__ */ jsx(CircleAlertIcon, { className: "size-5 text-og-danger" }) : /* @__PURE__ */ jsx(LoaderCircleIcon, { className: "size-5 animate-spin" }) }),
|
|
1225
1543
|
/* @__PURE__ */ jsx("p", { className: "mt-3 text-og-menu font-medium text-og-fg", children: error ? "Desktop didn\u2019t open" : "Opening desktop\u2026" }),
|
|
1226
|
-
/* @__PURE__ */ jsx("p", { className: "mt-1 text-og-control leading-5 text-og-muted", children: error?.message ?? "Preparing this agent\u2019s desktop. It will appear here when ready." }),
|
|
1544
|
+
/* @__PURE__ */ jsx("p", { className: "mt-1 text-og-control leading-5 text-og-fg-muted", children: error?.message ?? "Preparing this agent\u2019s desktop. It will appear here when ready." }),
|
|
1227
1545
|
error ? /* @__PURE__ */ jsxs(
|
|
1228
1546
|
"button",
|
|
1229
1547
|
{
|
|
1230
1548
|
type: "button",
|
|
1231
1549
|
onClick: createError ? createComputer : () => void refreshRegistry(),
|
|
1232
1550
|
disabled: creating || registry.refreshing,
|
|
1233
|
-
className: "mt-4 inline-flex h-8 items-center gap-1.5 rounded-og-sm bg-og-
|
|
1551
|
+
className: "mt-4 inline-flex h-8 items-center gap-1.5 rounded-og-sm border border-og-primary-border bg-og-primary text-og-primary-fg px-3 text-og-control font-medium transition hover:bg-og-primary-hover disabled:opacity-50",
|
|
1234
1552
|
children: [
|
|
1235
1553
|
creating || registry.refreshing ? /* @__PURE__ */ jsx(LoaderCircleIcon, { className: "size-3.5 animate-spin" }) : /* @__PURE__ */ jsx(RotateCcwIcon, { className: "size-3.5" }),
|
|
1236
1554
|
"Try again"
|
|
@@ -1239,122 +1557,167 @@ function ComputerViewer({
|
|
|
1239
1557
|
) : null
|
|
1240
1558
|
] }) });
|
|
1241
1559
|
}
|
|
1242
|
-
return /* @__PURE__ */ jsxs(
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
selectedSessionId: selection?.sessionId ?? null,
|
|
1249
|
-
creating,
|
|
1250
|
-
refreshing: registry.refreshing,
|
|
1251
|
-
interventionCounts,
|
|
1252
|
-
onSelect: (computerSessionId) => selectComputerSession({ sessionId: computerSessionId, pinned: true }),
|
|
1253
|
-
onFollow: () => {
|
|
1254
|
-
if (relevant[0]) selectComputerSession({ sessionId: relevant[0].id, pinned: false });
|
|
1255
|
-
},
|
|
1256
|
-
onCreate: hideGenericCreate ? void 0 : createComputer,
|
|
1257
|
-
onRefresh: () => void Promise.all([refreshRegistry(), refreshComputer()])
|
|
1258
|
-
}
|
|
1259
|
-
),
|
|
1260
|
-
/* @__PURE__ */ jsx(
|
|
1261
|
-
InteractionInterventionBanner,
|
|
1262
|
-
{
|
|
1263
|
-
interventions: selectedInterventions,
|
|
1264
|
-
activeTargetId: computer.selectedTarget?.id ?? null,
|
|
1265
|
-
mutating: interventions.mutating,
|
|
1266
|
-
onOpen: (intervention) => void computer.selectTarget(intervention.targetId).catch((cause) => notifyError(cause, "Could not open the requested desktop view.")),
|
|
1267
|
-
onResolve: resolveIntervention
|
|
1268
|
-
}
|
|
1269
|
-
),
|
|
1270
|
-
!selectedRegistrySession ? /* @__PURE__ */ jsx(
|
|
1271
|
-
ComputerUnselectedPanel,
|
|
1272
|
-
{
|
|
1273
|
-
peerCount: liveSessions.length,
|
|
1274
|
-
creating,
|
|
1275
|
-
error: createError,
|
|
1276
|
-
generationLoss: suppressGenericCreate,
|
|
1277
|
-
onCreate: createComputer
|
|
1278
|
-
}
|
|
1279
|
-
) : !controllerReady ? /* @__PURE__ */ jsx(
|
|
1280
|
-
ComputerLifecyclePanel,
|
|
1281
|
-
{
|
|
1282
|
-
session: selectedRegistrySession,
|
|
1283
|
-
creating,
|
|
1284
|
-
onRefresh: registry.refresh,
|
|
1285
|
-
onRetry: createComputer
|
|
1286
|
-
}
|
|
1287
|
-
) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1288
|
-
/* @__PURE__ */ jsx(
|
|
1289
|
-
ComputerTargetRail,
|
|
1290
|
-
{
|
|
1291
|
-
targets: computer.targets,
|
|
1292
|
-
selectedTargetId: computer.selectedTarget?.id ?? null,
|
|
1293
|
-
loading: computer.loading,
|
|
1294
|
-
onSelect: (targetId) => void computer.selectTarget(targetId).catch((cause) => notifyError(cause, "Could not switch desktop views."))
|
|
1295
|
-
}
|
|
1560
|
+
return /* @__PURE__ */ jsxs(
|
|
1561
|
+
"div",
|
|
1562
|
+
{
|
|
1563
|
+
className: cn(
|
|
1564
|
+
"@container/computer-viewer relative flex h-full min-h-0 min-w-0 flex-col overflow-hidden bg-og-bg",
|
|
1565
|
+
className
|
|
1296
1566
|
),
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1567
|
+
children: [
|
|
1568
|
+
/* @__PURE__ */ jsx(
|
|
1569
|
+
ComputerToolbar,
|
|
1300
1570
|
{
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1571
|
+
sessions: liveSessions,
|
|
1572
|
+
relevantSessionIds: currentIds,
|
|
1573
|
+
selectedSessionId: selection?.sessionId ?? null,
|
|
1574
|
+
creating,
|
|
1575
|
+
refreshing: registry.refreshing || inputPosturePending,
|
|
1576
|
+
interventionCounts,
|
|
1577
|
+
targets: controllerReady ? computer.targets : [],
|
|
1578
|
+
selectedTargetId: computer.selectedTarget?.id ?? null,
|
|
1579
|
+
targetsLoading: controllerReady && computer.loading,
|
|
1580
|
+
onSelectTarget: (targetId) => {
|
|
1581
|
+
void computer.selectTarget(targetId).catch((cause) => notifyError(cause, "Could not switch desktop views."));
|
|
1582
|
+
},
|
|
1583
|
+
onSelect: (computerSessionId) => selectComputerSession({ sessionId: computerSessionId, pinned: true }),
|
|
1584
|
+
onFollow: () => {
|
|
1585
|
+
if (relevant[0]) selectComputerSession({ sessionId: relevant[0].id, pinned: false });
|
|
1586
|
+
},
|
|
1587
|
+
onCreate: hideGenericCreate ? void 0 : createComputer,
|
|
1588
|
+
onRefresh: () => {
|
|
1589
|
+
if (appTarget) {
|
|
1590
|
+
appInputPosture.refresh();
|
|
1591
|
+
} else if ((connectionState === "error" || inputDenied) && !generationLossSelected && !generationLossFrames) {
|
|
1592
|
+
frames.reconnect();
|
|
1593
|
+
}
|
|
1594
|
+
void Promise.all([refreshRegistry(), refreshComputer()]);
|
|
1595
|
+
}
|
|
1308
1596
|
}
|
|
1309
|
-
)
|
|
1310
|
-
|
|
1597
|
+
),
|
|
1598
|
+
/* @__PURE__ */ jsx(
|
|
1599
|
+
InteractionInterventionBanner,
|
|
1311
1600
|
{
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
connectionError: frames.error ?? computer.error,
|
|
1318
|
-
mutating: computer.mutating,
|
|
1319
|
-
backgroundActions: computer.session?.capabilities?.backgroundActions === true,
|
|
1320
|
-
clipboardEnabled: computer.session?.capabilities?.clipboard === true,
|
|
1321
|
-
onAction: perform,
|
|
1322
|
-
onReadClipboard: computer.readClipboard,
|
|
1323
|
-
onReconnect: generationLossSelected || generationLossFrames ? () => void 0 : frames.reconnect,
|
|
1324
|
-
onError: (cause) => notifyError(cause, "Desktop input failed.")
|
|
1601
|
+
interventions: selectedInterventions,
|
|
1602
|
+
activeTargetId: computer.selectedTarget?.id ?? null,
|
|
1603
|
+
mutating: interventions.mutating,
|
|
1604
|
+
onOpen: (intervention) => void computer.selectTarget(intervention.targetId).catch((cause) => notifyError(cause, "Could not open the requested desktop view.")),
|
|
1605
|
+
onResolve: resolveIntervention
|
|
1325
1606
|
}
|
|
1326
1607
|
),
|
|
1327
|
-
|
|
1328
|
-
|
|
1608
|
+
!selectedRegistrySession ? /* @__PURE__ */ jsx(
|
|
1609
|
+
ComputerUnselectedPanel,
|
|
1329
1610
|
{
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1611
|
+
peerCount: liveSessions.length,
|
|
1612
|
+
creating,
|
|
1613
|
+
error: createError,
|
|
1614
|
+
generationLoss: suppressGenericCreate,
|
|
1615
|
+
onCreate: createComputer
|
|
1335
1616
|
}
|
|
1336
|
-
) :
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1617
|
+
) : !controllerReady ? /* @__PURE__ */ jsx(
|
|
1618
|
+
ComputerLifecyclePanel,
|
|
1619
|
+
{
|
|
1620
|
+
session: selectedRegistrySession,
|
|
1621
|
+
creating,
|
|
1622
|
+
onRefresh: registry.refresh,
|
|
1623
|
+
onRetry: createComputer
|
|
1624
|
+
}
|
|
1625
|
+
) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1626
|
+
/* @__PURE__ */ jsxs("div", { className: "flex min-h-0 flex-1 flex-col @xl/computer-viewer:flex-row", children: [
|
|
1627
|
+
rfbStream ? /* @__PURE__ */ jsxs("div", { className: "relative min-h-0 flex-1 bg-black", onCopyCapture: copyFromRfb, children: [
|
|
1628
|
+
/* @__PURE__ */ jsx(
|
|
1629
|
+
DesktopViewer,
|
|
1630
|
+
{
|
|
1631
|
+
capability: rfbCapability,
|
|
1632
|
+
interactive: rfbInputEnabled,
|
|
1633
|
+
showControlToggle: false,
|
|
1634
|
+
webSocketProtocols: rfbStream.protocols,
|
|
1635
|
+
onPasteText: pasteIntoRfb,
|
|
1636
|
+
targetPlatform: computer.session?.platform ?? null,
|
|
1637
|
+
className: "h-full"
|
|
1638
|
+
}
|
|
1639
|
+
),
|
|
1640
|
+
!rfbInputEnabled ? /* @__PURE__ */ jsx("div", { className: "pointer-events-none absolute bottom-3 left-1/2 -translate-x-1/2 rounded-full border border-white/10 bg-black/70 px-3 py-1 text-[11px] text-white/80 backdrop-blur", children: controlUnavailable ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1641
|
+
"Desktop controls unavailable",
|
|
1642
|
+
" ",
|
|
1643
|
+
/* @__PURE__ */ jsx(
|
|
1644
|
+
"button",
|
|
1645
|
+
{
|
|
1646
|
+
type: "button",
|
|
1647
|
+
onClick: reconnect,
|
|
1648
|
+
className: "pointer-events-auto ml-2 font-medium underline",
|
|
1649
|
+
children: "Reconnect"
|
|
1650
|
+
}
|
|
1651
|
+
)
|
|
1652
|
+
] }) : "View only \xB7 direct input unavailable" }) : null
|
|
1653
|
+
] }) : /* @__PURE__ */ jsx(
|
|
1654
|
+
ComputerViewport,
|
|
1655
|
+
{
|
|
1656
|
+
frame: machineLocked ? null : displayedFrame,
|
|
1657
|
+
observation: computer.observation,
|
|
1658
|
+
target: computer.selectedTarget,
|
|
1659
|
+
machineLocked,
|
|
1660
|
+
controlUnavailable,
|
|
1661
|
+
inputAllowed: !inputDenied,
|
|
1662
|
+
connectionState,
|
|
1663
|
+
connectionError: computer.controlError ?? appInputPosture.error ?? frames.error ?? computer.error,
|
|
1664
|
+
mutating: computer.mutating,
|
|
1665
|
+
backgroundActions: computer.session?.capabilities?.backgroundActions === true,
|
|
1666
|
+
backgroundInput: computer.session?.capabilities?.backgroundInput === true,
|
|
1667
|
+
allowForegroundInputWithinOwnedSeat: computer.session?.platform === "linux" && computer.session.adapter === "opengeni.cua.linux.v1" && computer.session.seatId === `linux-virtual:${computer.session.id}` && Boolean(computer.session.displayId),
|
|
1668
|
+
clipboardEnabled: attachmentInputAllowed && computer.session?.capabilities?.clipboard === true,
|
|
1669
|
+
pointerInput: attachmentInputAllowed && computer.session?.capabilities?.pointerInput === true,
|
|
1670
|
+
pointerClickContinuation: attachmentInputAllowed && computer.session?.capabilities?.pointerClickContinuation === true,
|
|
1671
|
+
keyboardInput: attachmentInputAllowed && computer.session?.capabilities?.keyboardInput === true,
|
|
1672
|
+
onAction: perform,
|
|
1673
|
+
onReadClipboard: computer.readClipboard,
|
|
1674
|
+
onReconnect: generationLossSelected || generationLossFrames ? () => void 0 : reconnect,
|
|
1675
|
+
onError: (cause) => notifyError(cause, "Desktop input failed.")
|
|
1676
|
+
},
|
|
1677
|
+
JSON.stringify([
|
|
1678
|
+
selection?.sessionId,
|
|
1679
|
+
computer.selectedTarget?.id,
|
|
1680
|
+
computer.selectedTarget?.targetGeneration,
|
|
1681
|
+
frames.attachment?.controllerGeneration
|
|
1682
|
+
])
|
|
1683
|
+
),
|
|
1684
|
+
showControls ? /* @__PURE__ */ jsx(
|
|
1685
|
+
ComputerSemanticPanel,
|
|
1686
|
+
{
|
|
1687
|
+
observation: computer.observation,
|
|
1688
|
+
mutating: computer.mutating || controlUnavailable || inputDenied,
|
|
1689
|
+
controlUnavailable,
|
|
1690
|
+
inputAllowed: !inputDenied,
|
|
1691
|
+
onAction: (action) => void perform(action, null).catch(
|
|
1692
|
+
(cause) => notifyError(cause, "Desktop action failed.")
|
|
1693
|
+
)
|
|
1694
|
+
}
|
|
1695
|
+
) : null
|
|
1696
|
+
] }),
|
|
1697
|
+
/* @__PURE__ */ jsx(
|
|
1698
|
+
ComputerStatusBar,
|
|
1699
|
+
{
|
|
1700
|
+
session: computer.session,
|
|
1701
|
+
target: computer.selectedTarget,
|
|
1702
|
+
connectionState,
|
|
1703
|
+
controlUnavailable,
|
|
1704
|
+
inputAllowed: !inputDenied,
|
|
1705
|
+
refreshing: registry.refreshing,
|
|
1706
|
+
showControls,
|
|
1707
|
+
controlCount: semanticControls(computer.observation).length,
|
|
1708
|
+
onToggleControls: () => setShowControls((current) => !current)
|
|
1709
|
+
}
|
|
1710
|
+
)
|
|
1711
|
+
] })
|
|
1712
|
+
]
|
|
1713
|
+
}
|
|
1714
|
+
);
|
|
1352
1715
|
}
|
|
1353
1716
|
function ComputerUnselectedPanel(props) {
|
|
1354
1717
|
return /* @__PURE__ */ jsx("div", { className: "grid min-h-0 flex-1 place-items-center bg-og-bg p-6 text-center", children: /* @__PURE__ */ jsxs("div", { className: "max-w-sm", children: [
|
|
1355
|
-
/* @__PURE__ */ jsx("span", { className: "mx-auto grid size-10 place-items-center rounded-og-md border border-og-border bg-og-surface-1 text-og-muted", children: props.error || props.generationLoss ? /* @__PURE__ */ jsx(CircleAlertIcon, { className: "size-4.5 text-og-
|
|
1718
|
+
/* @__PURE__ */ jsx("span", { className: "mx-auto grid size-10 place-items-center rounded-og-md border border-og-border bg-og-surface-1 text-og-fg-muted", children: props.error || props.generationLoss ? /* @__PURE__ */ jsx(CircleAlertIcon, { className: "size-4.5 text-og-danger" }) : /* @__PURE__ */ jsx(LoaderCircleIcon, { className: "size-4.5 animate-spin" }) }),
|
|
1356
1719
|
/* @__PURE__ */ jsx("p", { className: "mt-3 text-og-menu font-medium text-og-fg", children: props.generationLoss ? "Chrome reconnected\u2014open a fresh browser/desktop." : props.error ? "Desktop didn\u2019t open" : "Opening desktop\u2026" }),
|
|
1357
|
-
/* @__PURE__ */ jsx("p", { className: "mt-1 text-og-control leading-5 text-og-muted", children: props.generationLoss ? "This Chrome profile is live again, but the previous desktop cannot move to the new connection. Use Browser \u2192 New browser \u2192 Connected Chrome." : props.error?.message ?? (props.peerCount === 1 ? "Preparing this agent\u2019s desktop. One peer desktop remains available from the menu." : `Preparing this agent\u2019s desktop. ${props.peerCount} peer desktops remain available from the menu.`) }),
|
|
1720
|
+
/* @__PURE__ */ jsx("p", { className: "mt-1 text-og-control leading-5 text-og-fg-muted", children: props.generationLoss ? "This Chrome profile is live again, but the previous desktop cannot move to the new connection. Use Browser \u2192 New browser \u2192 Connected Chrome." : props.error?.message ?? (props.peerCount === 1 ? "Preparing this agent\u2019s desktop. One peer desktop remains available from the menu." : `Preparing this agent\u2019s desktop. ${props.peerCount} peer desktops remain available from the menu.`) }),
|
|
1358
1721
|
props.error && !props.generationLoss ? /* @__PURE__ */ jsxs(
|
|
1359
1722
|
"button",
|
|
1360
1723
|
{
|
|
@@ -1371,59 +1734,95 @@ function ComputerUnselectedPanel(props) {
|
|
|
1371
1734
|
] }) });
|
|
1372
1735
|
}
|
|
1373
1736
|
function ComputerToolbar(props) {
|
|
1374
|
-
const detailsRef =
|
|
1737
|
+
const detailsRef = useViewerMenuDismiss();
|
|
1375
1738
|
const selected = props.sessions.find((session) => session.id === props.selectedSessionId);
|
|
1376
1739
|
const current = props.sessions.filter((session) => props.relevantSessionIds.has(session.id));
|
|
1377
1740
|
const others = props.sessions.filter((session) => !props.relevantSessionIds.has(session.id));
|
|
1378
|
-
const
|
|
1379
|
-
props.onSelect(id);
|
|
1741
|
+
const closeMenu = (restoreFocus) => {
|
|
1380
1742
|
detailsRef.current?.removeAttribute("open");
|
|
1743
|
+
if (restoreFocus) detailsRef.current?.querySelector("summary")?.focus();
|
|
1381
1744
|
};
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1745
|
+
const choose = (id, restoreFocus) => {
|
|
1746
|
+
props.onSelect(id);
|
|
1747
|
+
closeMenu(restoreFocus);
|
|
1748
|
+
};
|
|
1749
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex h-10 shrink-0 items-center gap-1.5 border-b border-og-border bg-og-surface-1 px-2", children: [
|
|
1750
|
+
/* @__PURE__ */ jsxs("details", { ref: detailsRef, className: "min-w-0", children: [
|
|
1751
|
+
/* @__PURE__ */ jsxs("summary", { className: "flex h-7 max-w-32 cursor-pointer list-none items-center gap-2 rounded-og-sm px-2 text-og-control text-og-fg transition hover:bg-og-surface-2 @sm/computer-viewer:max-w-52 [&::-webkit-details-marker]:hidden", children: [
|
|
1752
|
+
/* @__PURE__ */ jsx(MonitorIcon, { className: "size-3.5 shrink-0 text-og-fg-muted" }),
|
|
1386
1753
|
/* @__PURE__ */ jsx("span", { className: "truncate font-medium", children: selected?.name === "Computer" ? "Desktop" : selected?.name ?? "Desktop" }),
|
|
1387
1754
|
(props.interventionCounts.get(selected?.id ?? "") ?? 0) > 0 ? /* @__PURE__ */ jsx("span", { className: "size-1.5 shrink-0 rounded-full bg-og-status-waiting" }) : null,
|
|
1388
|
-
/* @__PURE__ */ jsx(ChevronDownIcon, { className: "size-3 shrink-0 text-og-subtle" })
|
|
1755
|
+
/* @__PURE__ */ jsx(ChevronDownIcon, { className: "size-3 shrink-0 text-og-fg-subtle" })
|
|
1389
1756
|
] }),
|
|
1390
|
-
/* @__PURE__ */ jsxs("div", { className: "absolute left-
|
|
1391
|
-
/* @__PURE__ */
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1757
|
+
/* @__PURE__ */ jsxs("div", { className: "absolute left-2 top-10 z-30 flex max-h-[calc(100%-3rem)] w-72 max-w-[calc(100%-1rem)] flex-col overflow-hidden rounded-og-md border border-og-border bg-og-surface-1 p-1 shadow-xl", children: [
|
|
1758
|
+
/* @__PURE__ */ jsxs("div", { className: "min-h-0 max-h-96 overflow-y-auto overscroll-contain", children: [
|
|
1759
|
+
/* @__PURE__ */ jsx(
|
|
1760
|
+
ComputerSessionGroup,
|
|
1761
|
+
{
|
|
1762
|
+
label: "Current agent",
|
|
1763
|
+
sessions: current,
|
|
1764
|
+
selectedId: props.selectedSessionId,
|
|
1765
|
+
interventionCounts: props.interventionCounts,
|
|
1766
|
+
onSelect: choose
|
|
1767
|
+
}
|
|
1768
|
+
),
|
|
1769
|
+
/* @__PURE__ */ jsx(
|
|
1770
|
+
ComputerSessionGroup,
|
|
1771
|
+
{
|
|
1772
|
+
label: "Other agents",
|
|
1773
|
+
sessions: others,
|
|
1774
|
+
selectedId: props.selectedSessionId,
|
|
1775
|
+
interventionCounts: props.interventionCounts,
|
|
1776
|
+
onSelect: choose
|
|
1777
|
+
}
|
|
1778
|
+
)
|
|
1779
|
+
] }),
|
|
1780
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-1 flex shrink-0 gap-1 border-t border-og-border pt-1", children: [
|
|
1781
|
+
current.length > 0 ? /* @__PURE__ */ jsx(
|
|
1782
|
+
MenuButton,
|
|
1783
|
+
{
|
|
1784
|
+
onClick: (event) => {
|
|
1785
|
+
props.onFollow();
|
|
1786
|
+
closeMenu(event.detail === 0);
|
|
1787
|
+
},
|
|
1788
|
+
children: "Follow agent"
|
|
1789
|
+
}
|
|
1790
|
+
) : null,
|
|
1791
|
+
props.onCreate ? /* @__PURE__ */ jsxs(
|
|
1792
|
+
MenuButton,
|
|
1793
|
+
{
|
|
1794
|
+
onClick: (event) => {
|
|
1795
|
+
props.onCreate?.();
|
|
1796
|
+
closeMenu(event.detail === 0);
|
|
1797
|
+
},
|
|
1798
|
+
disabled: props.creating,
|
|
1799
|
+
children: [
|
|
1800
|
+
/* @__PURE__ */ jsx(PlusIcon, { className: "size-3.5" }),
|
|
1801
|
+
" New desktop"
|
|
1802
|
+
]
|
|
1803
|
+
}
|
|
1804
|
+
) : null
|
|
1417
1805
|
] })
|
|
1418
1806
|
] })
|
|
1419
1807
|
] }),
|
|
1420
|
-
/* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate text-og-xs text-og-subtle", children: selected ? `${platformLabel(selected)} \xB7 ${placementLabel(selected)}` : "" }),
|
|
1808
|
+
/* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate text-og-xs text-og-fg-subtle", children: selected ? `${platformLabel(selected)} \xB7 ${placementLabel(selected)}` : "" }),
|
|
1809
|
+
/* @__PURE__ */ jsx(
|
|
1810
|
+
ComputerTargetPicker,
|
|
1811
|
+
{
|
|
1812
|
+
targets: props.targets,
|
|
1813
|
+
selectedTargetId: props.selectedTargetId,
|
|
1814
|
+
loading: props.targetsLoading,
|
|
1815
|
+
onSelect: props.onSelectTarget
|
|
1816
|
+
}
|
|
1817
|
+
),
|
|
1421
1818
|
/* @__PURE__ */ jsx(
|
|
1422
1819
|
"button",
|
|
1423
1820
|
{
|
|
1424
1821
|
type: "button",
|
|
1425
1822
|
onClick: props.onRefresh,
|
|
1426
|
-
|
|
1823
|
+
disabled: props.refreshing,
|
|
1824
|
+
"aria-busy": props.refreshing,
|
|
1825
|
+
className: "grid size-7 place-items-center rounded-og-sm text-og-fg-muted transition hover:bg-og-surface-2 hover:text-og-fg",
|
|
1427
1826
|
"aria-label": "Refresh desktops",
|
|
1428
1827
|
children: /* @__PURE__ */ jsx(RefreshCwIcon, { className: cn("size-3.5", props.refreshing && "animate-spin") })
|
|
1429
1828
|
}
|
|
@@ -1434,7 +1833,7 @@ function ComputerToolbar(props) {
|
|
|
1434
1833
|
type: "button",
|
|
1435
1834
|
onClick: props.onCreate,
|
|
1436
1835
|
disabled: props.creating,
|
|
1437
|
-
className: "
|
|
1836
|
+
className: "hidden size-7 place-items-center rounded-og-sm text-og-fg-muted transition hover:bg-og-surface-2 hover:text-og-fg disabled:opacity-40 @sm/computer-viewer:grid",
|
|
1438
1837
|
"aria-label": "Open a new desktop",
|
|
1439
1838
|
children: props.creating ? /* @__PURE__ */ jsx(LoaderCircleIcon, { className: "size-3.5 animate-spin" }) : /* @__PURE__ */ jsx(PlusIcon, { className: "size-3.5" })
|
|
1440
1839
|
}
|
|
@@ -1444,14 +1843,15 @@ function ComputerToolbar(props) {
|
|
|
1444
1843
|
function ComputerSessionGroup(props) {
|
|
1445
1844
|
if (props.sessions.length === 0) return null;
|
|
1446
1845
|
return /* @__PURE__ */ jsxs("div", { className: "py-1", children: [
|
|
1447
|
-
/* @__PURE__ */ jsx("p", { className: "px-2 pb-1 text-[10px] font-medium uppercase tracking-[0.12em] text-og-subtle", children: props.label }),
|
|
1846
|
+
/* @__PURE__ */ jsx("p", { className: "px-2 pb-1 text-[10px] font-medium uppercase tracking-[0.12em] text-og-fg-subtle", children: props.label }),
|
|
1448
1847
|
props.sessions.map((session) => {
|
|
1449
1848
|
const interventionCount = props.interventionCounts.get(session.id) ?? 0;
|
|
1450
1849
|
return /* @__PURE__ */ jsxs(
|
|
1451
1850
|
"button",
|
|
1452
1851
|
{
|
|
1453
1852
|
type: "button",
|
|
1454
|
-
onClick: () => props.onSelect(session.id),
|
|
1853
|
+
onClick: (event) => props.onSelect(session.id, event.detail === 0),
|
|
1854
|
+
"aria-pressed": session.id === props.selectedId,
|
|
1455
1855
|
className: cn(
|
|
1456
1856
|
"flex w-full items-center gap-2 rounded-og-sm px-2 py-1.5 text-left transition hover:bg-og-surface-2",
|
|
1457
1857
|
session.id === props.selectedId && "bg-og-surface-2"
|
|
@@ -1468,7 +1868,7 @@ function ComputerSessionGroup(props) {
|
|
|
1468
1868
|
),
|
|
1469
1869
|
/* @__PURE__ */ jsxs("span", { className: "min-w-0 flex-1", children: [
|
|
1470
1870
|
/* @__PURE__ */ jsx("span", { className: "block truncate text-og-control text-og-fg", children: session.name }),
|
|
1471
|
-
/* @__PURE__ */ jsxs("span", { className: "block truncate text-og-xs text-og-subtle", children: [
|
|
1871
|
+
/* @__PURE__ */ jsxs("span", { className: "block truncate text-og-xs text-og-fg-subtle", children: [
|
|
1472
1872
|
platformLabel(session),
|
|
1473
1873
|
" \xB7 ",
|
|
1474
1874
|
placementLabel(session)
|
|
@@ -1489,32 +1889,81 @@ function MenuButton(props) {
|
|
|
1489
1889
|
type: "button",
|
|
1490
1890
|
onClick: props.onClick,
|
|
1491
1891
|
disabled: props.disabled,
|
|
1492
|
-
className: "flex h-7 flex-1 items-center justify-center gap-1 rounded-og-sm px-2 text-og-control text-og-muted transition hover:bg-og-surface-2 hover:text-og-fg disabled:opacity-50",
|
|
1892
|
+
className: "flex h-7 flex-1 items-center justify-center gap-1 rounded-og-sm px-2 text-og-control text-og-fg-muted transition hover:bg-og-surface-2 hover:text-og-fg disabled:opacity-50",
|
|
1493
1893
|
children: props.children
|
|
1494
1894
|
}
|
|
1495
1895
|
);
|
|
1496
1896
|
}
|
|
1497
|
-
function
|
|
1498
|
-
const
|
|
1499
|
-
|
|
1500
|
-
"
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1897
|
+
function ComputerTargetPicker(props) {
|
|
1898
|
+
const screens = props.targets.filter((target) => target.kind === "screen");
|
|
1899
|
+
const advancedTargets = props.targets.filter(
|
|
1900
|
+
(target) => target.kind === "app" || target.kind === "window" && isRenderableComputerView(target)
|
|
1901
|
+
);
|
|
1902
|
+
const advancedRef = useViewerMenuDismiss(advancedTargets.length > 0);
|
|
1903
|
+
const selected = props.targets.find((target) => target.id === props.selectedTargetId);
|
|
1904
|
+
const choose = (id, restoreFocus = false) => {
|
|
1905
|
+
props.onSelect(id);
|
|
1906
|
+
if (advancedRef.current?.open) {
|
|
1907
|
+
advancedRef.current.removeAttribute("open");
|
|
1908
|
+
if (restoreFocus) advancedRef.current.querySelector("summary")?.focus();
|
|
1909
|
+
}
|
|
1910
|
+
};
|
|
1911
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1912
|
+
screens.length > 1 ? /* @__PURE__ */ jsxs(
|
|
1913
|
+
"select",
|
|
1914
|
+
{
|
|
1915
|
+
"aria-label": "Screen",
|
|
1916
|
+
value: selected?.kind === "screen" ? selected.id : "",
|
|
1917
|
+
onChange: (event) => choose(event.currentTarget.value),
|
|
1918
|
+
className: "h-7 min-w-0 max-w-28 rounded-og-sm border border-og-border bg-og-surface-1 px-1.5 text-og-control text-og-fg outline-hidden focus:border-og-accent @sm/computer-viewer:max-w-40",
|
|
1919
|
+
children: [
|
|
1920
|
+
/* @__PURE__ */ jsx("option", { value: "", disabled: true, hidden: true, children: "Screen" }),
|
|
1921
|
+
screens.map((screen, index) => /* @__PURE__ */ jsx("option", { value: screen.id, children: screen.title || `Screen ${index + 1}` }, screen.id))
|
|
1922
|
+
]
|
|
1923
|
+
}
|
|
1924
|
+
) : screens[0] && selected?.kind !== "screen" ? /* @__PURE__ */ jsxs(
|
|
1925
|
+
"button",
|
|
1926
|
+
{
|
|
1927
|
+
type: "button",
|
|
1928
|
+
onClick: () => choose(screens[0].id),
|
|
1929
|
+
className: "flex h-7 shrink-0 items-center gap-1.5 rounded-og-sm px-2 text-og-control text-og-fg-muted transition hover:bg-og-surface-2 hover:text-og-fg",
|
|
1930
|
+
children: [
|
|
1931
|
+
/* @__PURE__ */ jsx(MonitorIcon, { className: "size-3.5" }),
|
|
1932
|
+
" Full desktop"
|
|
1933
|
+
]
|
|
1934
|
+
}
|
|
1935
|
+
) : null,
|
|
1936
|
+
advancedTargets.length > 0 ? /* @__PURE__ */ jsxs("details", { ref: advancedRef, className: "shrink-0", children: [
|
|
1937
|
+
/* @__PURE__ */ jsxs(
|
|
1938
|
+
"summary",
|
|
1939
|
+
{
|
|
1940
|
+
"aria-label": "Advanced desktop views",
|
|
1941
|
+
title: "Advanced desktop views",
|
|
1942
|
+
className: "flex h-7 cursor-pointer list-none items-center gap-1.5 rounded-og-sm px-2 text-og-control text-og-fg-muted transition hover:bg-og-surface-2 hover:text-og-fg [&::-webkit-details-marker]:hidden",
|
|
1943
|
+
children: [
|
|
1944
|
+
/* @__PURE__ */ jsx(PanelsTopLeftIcon, { className: "size-3.5" }),
|
|
1945
|
+
/* @__PURE__ */ jsx("span", { className: "hidden @sm/computer-viewer:inline", children: "Advanced" }),
|
|
1946
|
+
/* @__PURE__ */ jsx(ChevronDownIcon, { className: "size-3" })
|
|
1947
|
+
]
|
|
1948
|
+
}
|
|
1949
|
+
),
|
|
1950
|
+
/* @__PURE__ */ jsxs("div", { className: "absolute right-2 top-10 z-30 flex max-h-[calc(100%-3rem)] w-64 max-w-[calc(100%-1rem)] flex-col overflow-hidden rounded-og-md border border-og-border bg-og-surface-1 p-1 shadow-xl", children: [
|
|
1951
|
+
/* @__PURE__ */ jsx("p", { className: "shrink-0 px-2 py-1 text-og-xs text-og-fg-subtle", children: "Apps and windows" }),
|
|
1952
|
+
/* @__PURE__ */ jsx("div", { className: "min-h-0 max-h-80 overflow-y-auto overscroll-contain", children: advancedTargets.map((target) => /* @__PURE__ */ jsxs(
|
|
1506
1953
|
"button",
|
|
1507
1954
|
{
|
|
1508
1955
|
type: "button",
|
|
1509
|
-
onClick: () =>
|
|
1956
|
+
onClick: (event) => choose(target.id, event.detail === 0),
|
|
1957
|
+
"aria-pressed": target.id === props.selectedTargetId,
|
|
1510
1958
|
className: cn(
|
|
1511
|
-
"flex
|
|
1512
|
-
target.id === props.selectedTargetId
|
|
1959
|
+
"flex w-full min-w-0 items-center gap-2 rounded-og-sm px-2 py-1.5 text-left transition hover:bg-og-surface-2",
|
|
1960
|
+
target.id === props.selectedTargetId && "bg-og-surface-2"
|
|
1513
1961
|
),
|
|
1514
|
-
"aria-pressed": target.id === props.selectedTargetId,
|
|
1515
1962
|
children: [
|
|
1516
|
-
|
|
1517
|
-
|
|
1963
|
+
/* @__PURE__ */ jsxs("span", { className: "min-w-0 flex-1", children: [
|
|
1964
|
+
/* @__PURE__ */ jsx("span", { className: "block truncate text-og-control text-og-fg", children: target.title || target.applicationId || target.kind }),
|
|
1965
|
+
/* @__PURE__ */ jsx("span", { className: "block text-og-xs text-og-fg-subtle", children: target.kind === "app" ? "App controls" : "Window view" })
|
|
1966
|
+
] }),
|
|
1518
1967
|
target.focused ? /* @__PURE__ */ jsx(
|
|
1519
1968
|
"span",
|
|
1520
1969
|
{
|
|
@@ -1525,12 +1974,11 @@ function ComputerTargetRail(props) {
|
|
|
1525
1974
|
]
|
|
1526
1975
|
},
|
|
1527
1976
|
target.id
|
|
1528
|
-
))
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
);
|
|
1977
|
+
)) })
|
|
1978
|
+
] })
|
|
1979
|
+
] }) : null,
|
|
1980
|
+
props.loading ? /* @__PURE__ */ jsx(LoaderCircleIcon, { className: "size-3.5 shrink-0 animate-spin text-og-fg-muted" }) : null
|
|
1981
|
+
] });
|
|
1534
1982
|
}
|
|
1535
1983
|
function isRenderableComputerView(target) {
|
|
1536
1984
|
if (target.kind === "screen") return true;
|
|
@@ -1545,9 +1993,9 @@ function ComputerLifecyclePanel(props) {
|
|
|
1545
1993
|
const failed = ["failed", "lost", "repair_required"].includes(props.session.lifecycle);
|
|
1546
1994
|
const retryCreatesSession = ["failed", "lost"].includes(props.session.lifecycle);
|
|
1547
1995
|
return /* @__PURE__ */ jsx("div", { className: "grid min-h-0 flex-1 place-items-center bg-og-bg p-6", children: /* @__PURE__ */ jsxs("div", { className: "max-w-sm text-center", children: [
|
|
1548
|
-
failed ? /* @__PURE__ */ jsx(CircleAlertIcon, { className: "mx-auto size-5 text-og-
|
|
1996
|
+
failed ? /* @__PURE__ */ jsx(CircleAlertIcon, { className: "mx-auto size-5 text-og-danger" }) : /* @__PURE__ */ jsx(LoaderCircleIcon, { className: "mx-auto size-5 animate-spin text-og-fg-muted" }),
|
|
1549
1997
|
/* @__PURE__ */ jsx("p", { className: "mt-3 text-og-menu font-medium text-og-fg", children: generationLoss ? "Chrome reconnected\u2014open a fresh browser/desktop." : failed ? "Desktop needs attention" : lifecycleLabel(props.session.lifecycle) }),
|
|
1550
|
-
/* @__PURE__ */ jsx("p", { className: "mt-1 text-og-control leading-5 text-og-muted", children: generationLoss ? "This Chrome profile is live again, but the previous desktop cannot move to the new connection. Use Browser \u2192 New browser \u2192 Connected Chrome." : computerFailureMessage(props.session) ?? "The desktop is being prepared on its placement. It will appear here when ready." }),
|
|
1998
|
+
/* @__PURE__ */ jsx("p", { className: "mt-1 text-og-control leading-5 text-og-fg-muted", children: generationLoss ? "This Chrome profile is live again, but the previous desktop cannot move to the new connection. Use Browser \u2192 New browser \u2192 Connected Chrome." : computerFailureMessage(props.session) ?? "The desktop is being prepared on its placement. It will appear here when ready." }),
|
|
1551
1999
|
failed && !generationLoss ? /* @__PURE__ */ jsxs(
|
|
1552
2000
|
"button",
|
|
1553
2001
|
{
|
|
@@ -1564,25 +2012,65 @@ function ComputerLifecyclePanel(props) {
|
|
|
1564
2012
|
] }) });
|
|
1565
2013
|
}
|
|
1566
2014
|
function ComputerViewport(props) {
|
|
1567
|
-
const canvasRef =
|
|
1568
|
-
const inputRef =
|
|
1569
|
-
const
|
|
1570
|
-
const
|
|
1571
|
-
const
|
|
1572
|
-
const
|
|
1573
|
-
const
|
|
1574
|
-
const
|
|
1575
|
-
const
|
|
1576
|
-
const
|
|
1577
|
-
const
|
|
1578
|
-
const
|
|
1579
|
-
const
|
|
1580
|
-
const
|
|
2015
|
+
const canvasRef = useRef5(null);
|
|
2016
|
+
const inputRef = useRef5(null);
|
|
2017
|
+
const composingRef = useRef5(false);
|
|
2018
|
+
const pointerStartRef = useRef5(null);
|
|
2019
|
+
const clickTimerRef = useRef5(null);
|
|
2020
|
+
const lastClickRef = useRef5(null);
|
|
2021
|
+
const wheelRef = useRef5(null);
|
|
2022
|
+
const pendingTextRef = useRef5(null);
|
|
2023
|
+
const actionRef = useRef5(props.onAction);
|
|
2024
|
+
const targetRef = useRef5(props.target);
|
|
2025
|
+
const readClipboardRef = useRef5(props.onReadClipboard);
|
|
2026
|
+
const errorRef = useRef5(props.onError);
|
|
2027
|
+
const actionTailRef = useRef5(Promise.resolve());
|
|
2028
|
+
const actionQueueEpochRef = useRef5(0);
|
|
2029
|
+
const queuedTextRef = useRef5(null);
|
|
2030
|
+
const queuedFrameRef = useRef5(null);
|
|
2031
|
+
const currentFrameRef = useRef5(props.frame);
|
|
2032
|
+
const paintedFrameRef = useRef5(null);
|
|
2033
|
+
const [paintedFrame, setPaintedFrame] = useState5(null);
|
|
2034
|
+
const decodingFrameRef = useRef5(false);
|
|
2035
|
+
const mountedRef = useRef5(true);
|
|
1581
2036
|
actionRef.current = props.onAction;
|
|
2037
|
+
targetRef.current = props.target;
|
|
1582
2038
|
readClipboardRef.current = props.onReadClipboard;
|
|
1583
2039
|
errorRef.current = props.onError;
|
|
1584
|
-
const
|
|
1585
|
-
const
|
|
2040
|
+
const streamFailed = props.connectionState === "error";
|
|
2041
|
+
const rawInputEnabled = props.target?.kind !== "app" && !streamFailed && !props.controlUnavailable && !props.machineLocked && (props.backgroundInput || // CUA may activate within its allocated Linux desktop. This does not
|
|
2042
|
+
// grant foreground input to an attached physical desktop.
|
|
2043
|
+
props.allowForegroundInputWithinOwnedSeat || !props.backgroundActions || props.target?.kind === "screen" || props.target?.focused === true);
|
|
2044
|
+
const clearBufferedInput = useCallback5(() => {
|
|
2045
|
+
if (clickTimerRef.current) clearTimeout(clickTimerRef.current);
|
|
2046
|
+
if (wheelRef.current?.timer) clearTimeout(wheelRef.current.timer);
|
|
2047
|
+
if (pendingTextRef.current?.timer) clearTimeout(pendingTextRef.current.timer);
|
|
2048
|
+
clickTimerRef.current = null;
|
|
2049
|
+
wheelRef.current = null;
|
|
2050
|
+
pendingTextRef.current = null;
|
|
2051
|
+
queuedTextRef.current = null;
|
|
2052
|
+
pointerStartRef.current = null;
|
|
2053
|
+
lastClickRef.current = null;
|
|
2054
|
+
composingRef.current = false;
|
|
2055
|
+
if (inputRef.current) inputRef.current.value = "";
|
|
2056
|
+
}, []);
|
|
2057
|
+
const pointerInputEnabled = rawInputEnabled && props.pointerInput;
|
|
2058
|
+
const keyboardInputEnabled = rawInputEnabled && props.keyboardInput;
|
|
2059
|
+
useLayoutEffect(() => {
|
|
2060
|
+
const canvas = canvasRef.current;
|
|
2061
|
+
const viewport = canvas?.parentElement;
|
|
2062
|
+
if (!canvas || !viewport) return;
|
|
2063
|
+
const fit = () => fitComputerCanvas(canvas);
|
|
2064
|
+
fit();
|
|
2065
|
+
if (typeof ResizeObserver === "undefined") {
|
|
2066
|
+
window.addEventListener("resize", fit);
|
|
2067
|
+
return () => window.removeEventListener("resize", fit);
|
|
2068
|
+
}
|
|
2069
|
+
const observer = new ResizeObserver(fit);
|
|
2070
|
+
observer.observe(viewport);
|
|
2071
|
+
return () => observer.disconnect();
|
|
2072
|
+
}, []);
|
|
2073
|
+
const paintQueuedFrames = useCallback5(() => {
|
|
1586
2074
|
if (decodingFrameRef.current) return;
|
|
1587
2075
|
decodingFrameRef.current = true;
|
|
1588
2076
|
void (async () => {
|
|
@@ -1598,8 +2086,10 @@ function ComputerViewport(props) {
|
|
|
1598
2086
|
const bitmap = await createImageBitmap(blob);
|
|
1599
2087
|
try {
|
|
1600
2088
|
const canvas2 = canvasRef.current;
|
|
1601
|
-
if (mountedRef.current && canvas2) {
|
|
2089
|
+
if (mountedRef.current && canvas2 && currentFrameRef.current && sameComputerTarget(frame, currentFrameRef.current)) {
|
|
1602
2090
|
paintCanvas(canvas2, bitmap, frame.width, frame.height);
|
|
2091
|
+
paintedFrameRef.current = frame;
|
|
2092
|
+
setPaintedFrame(frame);
|
|
1603
2093
|
}
|
|
1604
2094
|
} finally {
|
|
1605
2095
|
bitmap.close();
|
|
@@ -1609,11 +2099,14 @@ function ComputerViewport(props) {
|
|
|
1609
2099
|
objectUrl = URL.createObjectURL(blob);
|
|
1610
2100
|
const image = await loadImage(objectUrl);
|
|
1611
2101
|
const canvas = canvasRef.current;
|
|
1612
|
-
if (mountedRef.current && canvas) {
|
|
2102
|
+
if (mountedRef.current && canvas && currentFrameRef.current && sameComputerTarget(frame, currentFrameRef.current)) {
|
|
1613
2103
|
paintCanvas(canvas, image, frame.width, frame.height);
|
|
2104
|
+
paintedFrameRef.current = frame;
|
|
2105
|
+
setPaintedFrame(frame);
|
|
1614
2106
|
}
|
|
1615
2107
|
} catch (cause) {
|
|
1616
|
-
if (mountedRef.current
|
|
2108
|
+
if (mountedRef.current && currentFrameRef.current && sameComputerTarget(frame, currentFrameRef.current))
|
|
2109
|
+
errorRef.current(cause);
|
|
1617
2110
|
} finally {
|
|
1618
2111
|
if (objectUrl) URL.revokeObjectURL(objectUrl);
|
|
1619
2112
|
}
|
|
@@ -1624,52 +2117,109 @@ function ComputerViewport(props) {
|
|
|
1624
2117
|
}
|
|
1625
2118
|
})();
|
|
1626
2119
|
}, []);
|
|
1627
|
-
|
|
2120
|
+
useLayoutEffect(() => {
|
|
1628
2121
|
mountedRef.current = true;
|
|
1629
2122
|
return () => {
|
|
1630
2123
|
mountedRef.current = false;
|
|
2124
|
+
actionQueueEpochRef.current += 1;
|
|
1631
2125
|
queuedFrameRef.current = null;
|
|
2126
|
+
clearBufferedInput();
|
|
1632
2127
|
};
|
|
1633
|
-
}, []);
|
|
1634
|
-
|
|
1635
|
-
if (!props.frame)
|
|
2128
|
+
}, [clearBufferedInput]);
|
|
2129
|
+
useLayoutEffect(() => {
|
|
2130
|
+
if (currentFrameRef.current && !props.frame) {
|
|
2131
|
+
actionQueueEpochRef.current += 1;
|
|
2132
|
+
clearBufferedInput();
|
|
2133
|
+
}
|
|
2134
|
+
currentFrameRef.current = props.frame;
|
|
2135
|
+
if (!props.frame) {
|
|
2136
|
+
queuedFrameRef.current = null;
|
|
2137
|
+
paintedFrameRef.current = null;
|
|
2138
|
+
setPaintedFrame(null);
|
|
2139
|
+
return;
|
|
2140
|
+
}
|
|
1636
2141
|
queuedFrameRef.current = props.frame;
|
|
1637
2142
|
paintQueuedFrames();
|
|
1638
|
-
}, [paintQueuedFrames, props.frame]);
|
|
1639
|
-
|
|
1640
|
-
(
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
2143
|
+
}, [clearBufferedInput, paintQueuedFrames, props.frame]);
|
|
2144
|
+
useLayoutEffect(() => {
|
|
2145
|
+
if (!rawInputEnabled || !props.pointerInput || !props.keyboardInput) {
|
|
2146
|
+
actionQueueEpochRef.current += 1;
|
|
2147
|
+
clearBufferedInput();
|
|
2148
|
+
}
|
|
2149
|
+
}, [clearBufferedInput, props.keyboardInput, props.pointerInput, rawInputEnabled]);
|
|
2150
|
+
const enqueue = useCallback5(
|
|
2151
|
+
(action, frame, after, continuation) => {
|
|
2152
|
+
const epoch = actionQueueEpochRef.current;
|
|
2153
|
+
const plainText = action.type === "keyboard" && action.action === "type";
|
|
2154
|
+
const queuedText = queuedTextRef.current;
|
|
2155
|
+
if (plainText && !frame && !after && !continuation && queuedText?.epoch === epoch && queuedText.delivery.completion === actionTailRef.current && queuedText.action.value.length + action.value.length <= 1e6) {
|
|
2156
|
+
queuedText.action.value += action.value;
|
|
2157
|
+
return queuedText.delivery;
|
|
2158
|
+
}
|
|
2159
|
+
queuedTextRef.current = null;
|
|
2160
|
+
const dispatch = actionRef.current;
|
|
2161
|
+
const admittedTarget = targetRef.current;
|
|
2162
|
+
const isCurrent = () => mountedRef.current && epoch === actionQueueEpochRef.current;
|
|
2163
|
+
const operationId = crypto.randomUUID();
|
|
2164
|
+
let markStarted;
|
|
2165
|
+
const started = new Promise((resolve) => {
|
|
2166
|
+
markStarted = resolve;
|
|
2167
|
+
});
|
|
2168
|
+
const previous = actionTailRef.current;
|
|
2169
|
+
const ready = continuation && previous === continuation.completion ? continuation.started : previous.then(() => true);
|
|
2170
|
+
const delivery = ready.then(async (firstStarted) => {
|
|
2171
|
+
if (queuedTextRef.current?.action === action) queuedTextRef.current = null;
|
|
2172
|
+
if (!firstStarted || !isCurrent()) {
|
|
2173
|
+
markStarted(false);
|
|
2174
|
+
return;
|
|
2175
|
+
}
|
|
2176
|
+
const pending = dispatch(action, frame, operationId);
|
|
2177
|
+
markStarted(true);
|
|
2178
|
+
const receipt = await pending;
|
|
2179
|
+
const isCurrentResult = () => isCurrent() && !isStaleComputerActionResult(receipt);
|
|
2180
|
+
if (!isCurrentResult()) return;
|
|
2181
|
+
const observedTarget = receipt.observation?.target;
|
|
2182
|
+
if (!admittedTarget || receipt.computerSessionId !== admittedTarget.computerSessionId || receipt.controllerGeneration !== admittedTarget.controllerGeneration || receipt.targetId !== admittedTarget.id || observedTarget && (observedTarget.computerSessionId !== admittedTarget.computerSessionId || observedTarget.controllerGeneration !== admittedTarget.controllerGeneration || observedTarget.id !== admittedTarget.id || observedTarget.targetGeneration !== admittedTarget.targetGeneration)) {
|
|
2183
|
+
actionQueueEpochRef.current += 1;
|
|
2184
|
+
clearBufferedInput();
|
|
2185
|
+
return;
|
|
2186
|
+
}
|
|
2187
|
+
await after?.(isCurrentResult);
|
|
2188
|
+
}).catch((cause) => {
|
|
2189
|
+
markStarted(false);
|
|
2190
|
+
if (!isCurrent() || isStaleComputerActionResult(cause)) return;
|
|
2191
|
+
actionQueueEpochRef.current += 1;
|
|
2192
|
+
clearBufferedInput();
|
|
2193
|
+
errorRef.current(cause);
|
|
2194
|
+
});
|
|
2195
|
+
const completion = continuation ? Promise.all([previous, delivery]).then(() => void 0) : delivery;
|
|
2196
|
+
actionTailRef.current = completion;
|
|
2197
|
+
const queued = { operationId, started, completion };
|
|
2198
|
+
if (plainText && !frame && !after && !continuation) {
|
|
2199
|
+
queuedTextRef.current = { action, epoch, delivery: queued };
|
|
2200
|
+
}
|
|
2201
|
+
return queued;
|
|
1653
2202
|
},
|
|
1654
|
-
[]
|
|
2203
|
+
[clearBufferedInput]
|
|
1655
2204
|
);
|
|
1656
|
-
const point =
|
|
2205
|
+
const point = useCallback5(
|
|
1657
2206
|
(frame, clientX, clientY) => computerPoint(canvasRef.current, frame, clientX, clientY),
|
|
1658
2207
|
[]
|
|
1659
2208
|
);
|
|
1660
|
-
const flushPendingText =
|
|
2209
|
+
const flushPendingText = useCallback5(() => {
|
|
1661
2210
|
const pending = pendingTextRef.current;
|
|
1662
2211
|
if (!pending) return;
|
|
1663
2212
|
clearTimeout(pending.timer);
|
|
1664
2213
|
pendingTextRef.current = null;
|
|
1665
2214
|
enqueue({ type: "keyboard", action: "type", value: pending.text }, null);
|
|
1666
2215
|
}, [enqueue]);
|
|
1667
|
-
const flushPendingClick =
|
|
2216
|
+
const flushPendingClick = useCallback5(() => {
|
|
1668
2217
|
const pending = lastClickRef.current;
|
|
1669
2218
|
if (!pending) return;
|
|
1670
2219
|
if (clickTimerRef.current) clearTimeout(clickTimerRef.current);
|
|
1671
2220
|
clickTimerRef.current = null;
|
|
1672
2221
|
lastClickRef.current = null;
|
|
2222
|
+
if (!pending.pending) return;
|
|
1673
2223
|
enqueue(
|
|
1674
2224
|
{
|
|
1675
2225
|
type: "pointer",
|
|
@@ -1681,23 +2231,47 @@ function ComputerViewport(props) {
|
|
|
1681
2231
|
pending.frame
|
|
1682
2232
|
);
|
|
1683
2233
|
}, [enqueue]);
|
|
2234
|
+
const flushPendingWheel = useCallback5(() => {
|
|
2235
|
+
const batch = wheelRef.current;
|
|
2236
|
+
if (!batch) return;
|
|
2237
|
+
if (batch.timer) clearTimeout(batch.timer);
|
|
2238
|
+
wheelRef.current = null;
|
|
2239
|
+
enqueue(
|
|
2240
|
+
{
|
|
2241
|
+
type: "pointer",
|
|
2242
|
+
frameId: batch.frame.frameId,
|
|
2243
|
+
action: "scroll",
|
|
2244
|
+
x: batch.x,
|
|
2245
|
+
y: batch.y,
|
|
2246
|
+
deltaX: batch.deltaX,
|
|
2247
|
+
deltaY: batch.deltaY
|
|
2248
|
+
},
|
|
2249
|
+
batch.frame
|
|
2250
|
+
);
|
|
2251
|
+
}, [enqueue]);
|
|
1684
2252
|
const pointerDown = (event) => {
|
|
1685
|
-
|
|
2253
|
+
const frame = paintedFrameRef.current;
|
|
2254
|
+
if (!frame || !props.pointerClickContinuation && props.mutating || !rawInputEnabled || event.button !== 0)
|
|
2255
|
+
return;
|
|
2256
|
+
event.preventDefault();
|
|
2257
|
+
if (keyboardInputEnabled) inputRef.current?.focus({ preventScroll: true });
|
|
2258
|
+
if (!pointerInputEnabled) return;
|
|
2259
|
+
flushPendingWheel();
|
|
1686
2260
|
flushPendingText();
|
|
1687
2261
|
pointerStartRef.current = {
|
|
1688
2262
|
x: event.clientX,
|
|
1689
2263
|
y: event.clientY,
|
|
1690
2264
|
pointerId: event.pointerId,
|
|
1691
|
-
frame
|
|
2265
|
+
frame
|
|
1692
2266
|
};
|
|
1693
2267
|
event.currentTarget.setPointerCapture?.(event.pointerId);
|
|
1694
|
-
inputRef.current?.focus({ preventScroll: true });
|
|
1695
2268
|
};
|
|
1696
2269
|
const pointerUp = (event) => {
|
|
1697
|
-
if (!
|
|
2270
|
+
if (!pointerInputEnabled) return;
|
|
1698
2271
|
const start = pointerStartRef.current;
|
|
1699
2272
|
pointerStartRef.current = null;
|
|
1700
2273
|
if (!start || start.pointerId !== event.pointerId) return;
|
|
2274
|
+
const canvasRect = event.currentTarget.getBoundingClientRect();
|
|
1701
2275
|
const from = point(start.frame, start.x, start.y);
|
|
1702
2276
|
const to = point(start.frame, event.clientX, event.clientY);
|
|
1703
2277
|
if (!from || !to) return;
|
|
@@ -1719,7 +2293,7 @@ function ComputerViewport(props) {
|
|
|
1719
2293
|
}
|
|
1720
2294
|
const now = Date.now();
|
|
1721
2295
|
const previous = lastClickRef.current;
|
|
1722
|
-
if (previous && now - previous.at < 280 && Math.hypot(previous.x - to.x, previous.y - to.y) < 6 && sameFrameFence(previous.frame, start.frame)) {
|
|
2296
|
+
if (previous && now - previous.at < 280 && previous.pending === !props.pointerClickContinuation && (props.pointerClickContinuation ? Math.hypot(previous.clientX - event.clientX, previous.clientY - event.clientY) < 6 && previous.canvasRect.left === canvasRect.left && previous.canvasRect.top === canvasRect.top && previous.canvasRect.width === canvasRect.width && previous.canvasRect.height === canvasRect.height && sameComputerTarget(previous.frame, start.frame) && previous.frame.width === start.frame.width && previous.frame.height === start.frame.height : Math.hypot(previous.x - to.x, previous.y - to.y) < 6 && sameFrameFence(previous.frame, start.frame))) {
|
|
1723
2297
|
if (clickTimerRef.current) clearTimeout(clickTimerRef.current);
|
|
1724
2298
|
clickTimerRef.current = null;
|
|
1725
2299
|
lastClickRef.current = null;
|
|
@@ -1727,15 +2301,45 @@ function ComputerViewport(props) {
|
|
|
1727
2301
|
{
|
|
1728
2302
|
type: "pointer",
|
|
1729
2303
|
frameId: start.frame.frameId,
|
|
1730
|
-
action: "double_click",
|
|
2304
|
+
action: props.pointerClickContinuation ? "click" : "double_click",
|
|
2305
|
+
...props.pointerClickContinuation ? { clickCount: 2 } : {},
|
|
2306
|
+
...previous.delivery ? { continuationOfOperationId: previous.delivery.operationId } : {},
|
|
2307
|
+
x: to.x,
|
|
2308
|
+
y: to.y
|
|
2309
|
+
},
|
|
2310
|
+
start.frame,
|
|
2311
|
+
void 0,
|
|
2312
|
+
previous.delivery ?? void 0
|
|
2313
|
+
);
|
|
2314
|
+
return;
|
|
2315
|
+
}
|
|
2316
|
+
flushPendingClick();
|
|
2317
|
+
lastClickRef.current = {
|
|
2318
|
+
at: now,
|
|
2319
|
+
x: to.x,
|
|
2320
|
+
y: to.y,
|
|
2321
|
+
frame: start.frame,
|
|
2322
|
+
clientX: event.clientX,
|
|
2323
|
+
clientY: event.clientY,
|
|
2324
|
+
canvasRect,
|
|
2325
|
+
pending: !props.pointerClickContinuation,
|
|
2326
|
+
delivery: null
|
|
2327
|
+
};
|
|
2328
|
+
if (props.pointerClickContinuation) {
|
|
2329
|
+
const delivery = enqueue(
|
|
2330
|
+
{
|
|
2331
|
+
type: "pointer",
|
|
2332
|
+
frameId: start.frame.frameId,
|
|
2333
|
+
action: "click",
|
|
2334
|
+
clickCount: 1,
|
|
1731
2335
|
x: to.x,
|
|
1732
2336
|
y: to.y
|
|
1733
2337
|
},
|
|
1734
2338
|
start.frame
|
|
1735
2339
|
);
|
|
2340
|
+
if (lastClickRef.current) lastClickRef.current.delivery = delivery;
|
|
1736
2341
|
return;
|
|
1737
2342
|
}
|
|
1738
|
-
lastClickRef.current = { at: now, x: to.x, y: to.y, frame: start.frame };
|
|
1739
2343
|
clickTimerRef.current = setTimeout(() => {
|
|
1740
2344
|
clickTimerRef.current = null;
|
|
1741
2345
|
lastClickRef.current = null;
|
|
@@ -1753,9 +2357,10 @@ function ComputerViewport(props) {
|
|
|
1753
2357
|
};
|
|
1754
2358
|
const contextMenu = (event) => {
|
|
1755
2359
|
event.preventDefault();
|
|
1756
|
-
if (!
|
|
1757
|
-
const frame =
|
|
2360
|
+
if (!pointerInputEnabled) return;
|
|
2361
|
+
const frame = paintedFrameRef.current;
|
|
1758
2362
|
if (!frame) return;
|
|
2363
|
+
flushPendingWheel();
|
|
1759
2364
|
flushPendingText();
|
|
1760
2365
|
flushPendingClick();
|
|
1761
2366
|
const at = point(frame, event.clientX, event.clientY);
|
|
@@ -1774,44 +2379,34 @@ function ComputerViewport(props) {
|
|
|
1774
2379
|
}
|
|
1775
2380
|
};
|
|
1776
2381
|
const wheel = (event) => {
|
|
1777
|
-
if (!
|
|
1778
|
-
const frame =
|
|
2382
|
+
if (!pointerInputEnabled) return;
|
|
2383
|
+
const frame = paintedFrameRef.current;
|
|
1779
2384
|
if (!frame) return;
|
|
1780
2385
|
const at = point(frame, event.clientX, event.clientY);
|
|
1781
2386
|
if (!at) return;
|
|
1782
2387
|
flushPendingText();
|
|
1783
2388
|
flushPendingClick();
|
|
1784
2389
|
event.preventDefault();
|
|
1785
|
-
|
|
1786
|
-
if (pending
|
|
2390
|
+
let pending = wheelRef.current;
|
|
2391
|
+
if (pending && (!sameComputerTarget(pending.frame, frame) || Math.hypot(pending.x - at.x, pending.y - at.y) > 6)) {
|
|
2392
|
+
flushPendingWheel();
|
|
2393
|
+
pending = null;
|
|
2394
|
+
}
|
|
1787
2395
|
wheelRef.current = {
|
|
1788
2396
|
x: at.x,
|
|
1789
2397
|
y: at.y,
|
|
1790
|
-
deltaX: (pending
|
|
1791
|
-
deltaY: (pending
|
|
2398
|
+
deltaX: (pending?.deltaX ?? 0) + event.deltaX,
|
|
2399
|
+
deltaY: (pending?.deltaY ?? 0) + event.deltaY,
|
|
1792
2400
|
frame,
|
|
1793
|
-
timer: setTimeout(
|
|
1794
|
-
const batch = wheelRef.current;
|
|
1795
|
-
wheelRef.current = null;
|
|
1796
|
-
if (batch) {
|
|
1797
|
-
enqueue(
|
|
1798
|
-
{
|
|
1799
|
-
type: "pointer",
|
|
1800
|
-
frameId: batch.frame.frameId,
|
|
1801
|
-
action: "scroll",
|
|
1802
|
-
x: batch.x,
|
|
1803
|
-
y: batch.y,
|
|
1804
|
-
deltaX: batch.deltaX,
|
|
1805
|
-
deltaY: batch.deltaY
|
|
1806
|
-
},
|
|
1807
|
-
batch.frame
|
|
1808
|
-
);
|
|
1809
|
-
}
|
|
1810
|
-
}, 45)
|
|
2401
|
+
timer: pending?.timer ?? setTimeout(flushPendingWheel, 45)
|
|
1811
2402
|
};
|
|
1812
2403
|
};
|
|
1813
2404
|
const keyDown = (event) => {
|
|
1814
|
-
if (!
|
|
2405
|
+
if (!keyboardInputEnabled) return;
|
|
2406
|
+
if (composingRef.current || event.nativeEvent.isComposing || event.nativeEvent.keyCode === 229) {
|
|
2407
|
+
return;
|
|
2408
|
+
}
|
|
2409
|
+
flushPendingWheel();
|
|
1815
2410
|
const command = event.metaKey || event.ctrlKey;
|
|
1816
2411
|
if (props.clipboardEnabled && command && !event.altKey && ["c", "v"].includes(event.key.toLowerCase())) {
|
|
1817
2412
|
flushPendingText();
|
|
@@ -1825,12 +2420,15 @@ function ComputerViewport(props) {
|
|
|
1825
2420
|
enqueue({ type: "keyboard", action: "press", value: key }, null);
|
|
1826
2421
|
};
|
|
1827
2422
|
const copy = (event) => {
|
|
1828
|
-
if (!props.clipboardEnabled || !
|
|
2423
|
+
if (!props.clipboardEnabled || !keyboardInputEnabled) return;
|
|
1829
2424
|
event.preventDefault();
|
|
2425
|
+
flushPendingWheel();
|
|
1830
2426
|
flushPendingText();
|
|
1831
2427
|
flushPendingClick();
|
|
1832
|
-
|
|
1833
|
-
|
|
2428
|
+
const readClipboard = readClipboardRef.current;
|
|
2429
|
+
enqueue({ type: "clipboard", operation: "copy" }, null, async (isCurrent) => {
|
|
2430
|
+
const clipboard = await readClipboard();
|
|
2431
|
+
if (!isCurrent()) return;
|
|
1834
2432
|
if (!clipboard.text) return;
|
|
1835
2433
|
if (!await copyTextToClipboard(clipboard.text)) {
|
|
1836
2434
|
throw new Error("Desktop text could not be copied to the local clipboard");
|
|
@@ -1838,18 +2436,25 @@ function ComputerViewport(props) {
|
|
|
1838
2436
|
});
|
|
1839
2437
|
};
|
|
1840
2438
|
const paste = (event) => {
|
|
1841
|
-
if (!props.clipboardEnabled || !
|
|
2439
|
+
if (!props.clipboardEnabled || !keyboardInputEnabled) return;
|
|
1842
2440
|
event.preventDefault();
|
|
2441
|
+
flushPendingWheel();
|
|
1843
2442
|
flushPendingText();
|
|
1844
2443
|
flushPendingClick();
|
|
1845
2444
|
const text = event.clipboardData.getData("text/plain");
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
2445
|
+
const readClipboard = readClipboardRef.current;
|
|
2446
|
+
const dispatch = actionRef.current;
|
|
2447
|
+
enqueue({ type: "clipboard", operation: "write", text }, null, async (isCurrent) => {
|
|
2448
|
+
const clipboard = await readClipboard();
|
|
2449
|
+
if (!isCurrent()) return;
|
|
2450
|
+
assertExactComputerClipboard(clipboard, text);
|
|
2451
|
+
await dispatch({ type: "clipboard", operation: "paste" }, null);
|
|
1849
2452
|
});
|
|
1850
2453
|
};
|
|
1851
|
-
const input = (value) => {
|
|
1852
|
-
if (
|
|
2454
|
+
const input = (value, nativeComposing = false) => {
|
|
2455
|
+
if (composingRef.current || nativeComposing) return;
|
|
2456
|
+
if (!value || !keyboardInputEnabled) return;
|
|
2457
|
+
flushPendingWheel();
|
|
1853
2458
|
flushPendingClick();
|
|
1854
2459
|
const pending = pendingTextRef.current;
|
|
1855
2460
|
if (pending) {
|
|
@@ -1864,7 +2469,11 @@ function ComputerViewport(props) {
|
|
|
1864
2469
|
}
|
|
1865
2470
|
if (inputRef.current) inputRef.current.value = "";
|
|
1866
2471
|
};
|
|
1867
|
-
const
|
|
2472
|
+
const compositionEnd = (event) => {
|
|
2473
|
+
composingRef.current = false;
|
|
2474
|
+
input(event.currentTarget.value || event.data);
|
|
2475
|
+
};
|
|
2476
|
+
const showCanvas = props.frame !== null && paintedFrame !== null && sameComputerTarget(props.frame, paintedFrame) && !props.machineLocked && !streamFailed;
|
|
1868
2477
|
return /* @__PURE__ */ jsxs("div", { className: "relative min-h-0 flex-1 overflow-hidden bg-black", children: [
|
|
1869
2478
|
/* @__PURE__ */ jsx(
|
|
1870
2479
|
"canvas",
|
|
@@ -1873,7 +2482,7 @@ function ComputerViewport(props) {
|
|
|
1873
2482
|
className: cn(
|
|
1874
2483
|
"absolute inset-0 m-auto max-h-full max-w-full touch-none focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-og-accent",
|
|
1875
2484
|
!showCanvas && "invisible",
|
|
1876
|
-
!
|
|
2485
|
+
!pointerInputEnabled && "cursor-default"
|
|
1877
2486
|
),
|
|
1878
2487
|
onPointerDown: pointerDown,
|
|
1879
2488
|
onPointerUp: pointerUp,
|
|
@@ -1890,7 +2499,11 @@ function ComputerViewport(props) {
|
|
|
1890
2499
|
{
|
|
1891
2500
|
ref: inputRef,
|
|
1892
2501
|
defaultValue: "",
|
|
1893
|
-
onInput: (event) => input(event.currentTarget.value),
|
|
2502
|
+
onInput: (event) => input(event.currentTarget.value, event.nativeEvent.isComposing),
|
|
2503
|
+
onCompositionStart: () => {
|
|
2504
|
+
composingRef.current = true;
|
|
2505
|
+
},
|
|
2506
|
+
onCompositionEnd: compositionEnd,
|
|
1894
2507
|
onKeyDown: keyDown,
|
|
1895
2508
|
onCopy: copy,
|
|
1896
2509
|
onPaste: paste,
|
|
@@ -1899,7 +2512,7 @@ function ComputerViewport(props) {
|
|
|
1899
2512
|
autoCapitalize: "off",
|
|
1900
2513
|
autoCorrect: "off",
|
|
1901
2514
|
spellCheck: false,
|
|
1902
|
-
disabled: !
|
|
2515
|
+
disabled: !keyboardInputEnabled
|
|
1903
2516
|
}
|
|
1904
2517
|
),
|
|
1905
2518
|
!showCanvas ? /* @__PURE__ */ jsx(
|
|
@@ -1907,6 +2520,8 @@ function ComputerViewport(props) {
|
|
|
1907
2520
|
{
|
|
1908
2521
|
observation: props.observation,
|
|
1909
2522
|
machineLocked: props.machineLocked,
|
|
2523
|
+
controlUnavailable: props.controlUnavailable,
|
|
2524
|
+
inputAllowed: props.inputAllowed,
|
|
1910
2525
|
connectionState: props.connectionState,
|
|
1911
2526
|
error: props.connectionError,
|
|
1912
2527
|
onAction: (action) => enqueue(action, null),
|
|
@@ -1917,16 +2532,16 @@ function ComputerViewport(props) {
|
|
|
1917
2532
|
/* @__PURE__ */ jsx(LoaderCircleIcon, { className: "size-3 animate-spin" }),
|
|
1918
2533
|
" Acting"
|
|
1919
2534
|
] }) : null,
|
|
1920
|
-
showCanvas && !rawInputEnabled ? /* @__PURE__ */ jsxs("div", { className: "absolute bottom-3 left-1/2 flex -translate-x-1/2 items-center gap-2 rounded-full border border-white/10 bg-black/70 py-1 pl-3 pr-1 text-[11px] text-white/80 backdrop-blur", children: [
|
|
1921
|
-
/* @__PURE__ */ jsx("span", { children: "Background view \xB7 use
|
|
1922
|
-
props.target ? /* @__PURE__ */ jsx(
|
|
2535
|
+
showCanvas && (!rawInputEnabled || !props.pointerInput || !props.keyboardInput) ? /* @__PURE__ */ jsxs("div", { className: "absolute bottom-3 left-1/2 flex w-max max-w-[calc(100%-1.5rem)] -translate-x-1/2 items-center gap-2 rounded-full border border-white/10 bg-black/70 py-1 pl-3 pr-1 text-[11px] text-white/80 backdrop-blur", children: [
|
|
2536
|
+
/* @__PURE__ */ jsx("span", { children: !rawInputEnabled && (props.pointerInput || props.keyboardInput) ? "Background view \xB7 use app controls" : !props.pointerInput && !props.keyboardInput ? "View only \xB7 mouse and keyboard unavailable" : props.keyboardInput ? "Keyboard only \xB7 mouse unavailable" : "Mouse only \xB7 keyboard unavailable" }),
|
|
2537
|
+
!rawInputEnabled && props.target && (props.pointerInput || props.keyboardInput) ? /* @__PURE__ */ jsx(
|
|
1923
2538
|
"button",
|
|
1924
2539
|
{
|
|
1925
2540
|
type: "button",
|
|
1926
2541
|
disabled: props.mutating,
|
|
1927
2542
|
onClick: () => enqueue({ type: "focus", targetId: props.target.id }, null),
|
|
1928
|
-
className: "rounded-full bg-white/10 px-2 py-0.5 font-medium text-white transition hover:bg-white/20 disabled:opacity-50",
|
|
1929
|
-
children: "
|
|
2543
|
+
className: "shrink-0 whitespace-nowrap rounded-full bg-white/10 px-2 py-0.5 font-medium text-white transition hover:bg-white/20 disabled:opacity-50",
|
|
2544
|
+
children: "Bring to front"
|
|
1930
2545
|
}
|
|
1931
2546
|
) : null
|
|
1932
2547
|
] }) : null
|
|
@@ -1940,21 +2555,23 @@ function assertExactComputerClipboard(clipboard, expected) {
|
|
|
1940
2555
|
function ComputerViewportFallback(props) {
|
|
1941
2556
|
if (props.machineLocked) {
|
|
1942
2557
|
return /* @__PURE__ */ jsx("div", { className: "absolute inset-0 grid place-items-center bg-og-bg p-6", children: /* @__PURE__ */ jsxs("div", { className: "max-w-sm text-center", children: [
|
|
1943
|
-
/* @__PURE__ */ jsx(LockKeyholeIcon, { className: "mx-auto size-6 text-og-muted" }),
|
|
2558
|
+
/* @__PURE__ */ jsx(LockKeyholeIcon, { className: "mx-auto size-6 text-og-fg-muted" }),
|
|
1944
2559
|
/* @__PURE__ */ jsx("p", { className: "mt-3 text-og-menu font-medium text-og-fg", children: "Machine locked" }),
|
|
1945
|
-
/* @__PURE__ */ jsx("p", { className: "mt-1 text-og-control leading-5 text-og-muted", children: "Live frames and input stay private until someone unlocks the machine." })
|
|
2560
|
+
/* @__PURE__ */ jsx("p", { className: "mt-1 text-og-control leading-5 text-og-fg-muted", children: "Live frames and input stay private until someone unlocks the machine." })
|
|
1946
2561
|
] }) });
|
|
1947
2562
|
}
|
|
2563
|
+
const appControls = props.observation?.target.kind === "app";
|
|
1948
2564
|
const controlFailure = interactionControlFailureFromError(props.error);
|
|
1949
|
-
const interactive =
|
|
1950
|
-
return /* @__PURE__ */ jsx("div", { className: "absolute inset-0 grid place-items-center bg-og-bg p-6", children: /* @__PURE__ */ jsxs("div", { className: "w-full max-w-md rounded-og-lg border border-og-border bg-og-surface-
|
|
2565
|
+
const interactive = semanticControls(props.observation).filter((node) => semanticAction(node) !== null).slice(0, 10);
|
|
2566
|
+
return /* @__PURE__ */ jsx("div", { className: "absolute inset-0 grid place-items-center bg-og-bg p-6", children: /* @__PURE__ */ jsxs("div", { className: "w-full max-w-md rounded-og-lg border border-og-border bg-og-surface-1 p-4 shadow-lg", children: [
|
|
1951
2567
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
1952
|
-
props.error ? /* @__PURE__ */ jsx(CircleAlertIcon, { className: "size-4 text-og-
|
|
1953
|
-
/* @__PURE__ */ jsx("p", { className: "text-og-menu font-medium text-og-fg", children: isAttachedChromeGenerationLossError(props.error) ? "Chrome reconnected\u2014open a fresh browser/desktop." : props.error ? "Live view disconnected" : computerConnectionLabel(props.connectionState) })
|
|
2568
|
+
props.error ? /* @__PURE__ */ jsx(CircleAlertIcon, { className: "size-4 text-og-danger" }) : appControls ? /* @__PURE__ */ jsx(PanelsTopLeftIcon, { className: "size-4 text-og-fg-muted" }) : /* @__PURE__ */ jsx(LoaderCircleIcon, { className: "size-4 animate-spin text-og-fg-muted" }),
|
|
2569
|
+
/* @__PURE__ */ jsx("p", { className: "text-og-menu font-medium text-og-fg", children: isAttachedChromeGenerationLossError(props.error) ? "Chrome reconnected\u2014open a fresh browser/desktop." : !props.inputAllowed ? "View only" : props.controlUnavailable ? "Desktop controls unavailable" : props.error ? "Live view disconnected" : appControls ? "App controls" : computerConnectionLabel(props.connectionState) })
|
|
1954
2570
|
] }),
|
|
1955
|
-
props.error ? /* @__PURE__ */ jsx("p", { className: "mt-2 text-og-control leading-5 text-og-muted", children: isAttachedChromeGenerationLossError(props.error) ? "Chrome reconnected\u2014open a fresh browser/desktop. Use Browser \u2192 New browser \u2192 Connected Chrome." : controlFailure?.message ?? props.error.message }) : null,
|
|
1956
|
-
|
|
1957
|
-
|
|
2571
|
+
props.error ? /* @__PURE__ */ jsx("p", { className: "mt-2 text-og-control leading-5 text-og-fg-muted", children: isAttachedChromeGenerationLossError(props.error) ? "Chrome reconnected\u2014open a fresh browser/desktop. Use Browser \u2192 New browser \u2192 Connected Chrome." : controlFailure?.message ?? props.error.message }) : null,
|
|
2572
|
+
!props.inputAllowed ? /* @__PURE__ */ jsx("p", { className: "mt-2 text-og-control leading-5 text-og-fg-muted", children: "Refresh to check desktop controls." }) : null,
|
|
2573
|
+
interactive.length > 0 && !props.controlUnavailable && props.inputAllowed ? /* @__PURE__ */ jsxs("div", { className: "mt-3 border-t border-og-border pt-3", children: [
|
|
2574
|
+
/* @__PURE__ */ jsx("p", { className: "mb-2 text-og-xs text-og-fg-subtle", children: "App controls remain available" }),
|
|
1958
2575
|
/* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1.5", children: interactive.map((node) => /* @__PURE__ */ jsx(
|
|
1959
2576
|
"button",
|
|
1960
2577
|
{
|
|
@@ -1974,20 +2591,21 @@ function ComputerViewportFallback(props) {
|
|
|
1974
2591
|
{
|
|
1975
2592
|
type: "button",
|
|
1976
2593
|
onClick: props.onReconnect,
|
|
1977
|
-
className: "mt-3 text-og-control font-medium
|
|
2594
|
+
className: "mt-3 text-og-control font-medium og-link",
|
|
1978
2595
|
children: controlFailure?.retryable === false ? "Try again" : "Reconnect"
|
|
1979
2596
|
}
|
|
1980
2597
|
) : null
|
|
1981
2598
|
] }) });
|
|
1982
2599
|
}
|
|
1983
2600
|
function ComputerSemanticPanel(props) {
|
|
1984
|
-
const nodes =
|
|
1985
|
-
return /* @__PURE__ */ jsxs("aside", { className: "w-
|
|
1986
|
-
/* @__PURE__ */ jsxs("div", { className: "mb-2 flex items-center gap-1.5 px-1 text-og-xs font-medium uppercase tracking-[0.1em] text-og-subtle", children: [
|
|
2601
|
+
const nodes = semanticControls(props.observation).slice(0, 100);
|
|
2602
|
+
return /* @__PURE__ */ jsxs("aside", { className: "box-border max-h-[40%] w-full shrink-0 overflow-y-auto border-t border-og-border bg-og-surface-1 p-2 @xl/computer-viewer:max-h-none @xl/computer-viewer:w-64 @xl/computer-viewer:border-t-0 @xl/computer-viewer:border-l", children: [
|
|
2603
|
+
/* @__PURE__ */ jsxs("div", { className: "mb-2 flex items-center gap-1.5 px-1 text-og-xs font-medium uppercase tracking-[0.1em] text-og-fg-subtle", children: [
|
|
1987
2604
|
/* @__PURE__ */ jsx(KeyboardIcon, { className: "size-3" }),
|
|
1988
|
-
"
|
|
2605
|
+
" App controls"
|
|
1989
2606
|
] }),
|
|
1990
|
-
|
|
2607
|
+
!props.inputAllowed ? /* @__PURE__ */ jsx("p", { className: "px-1 py-2 text-og-control leading-5 text-og-fg-muted", children: "View only \xB7 refresh to check desktop controls." }) : props.controlUnavailable ? /* @__PURE__ */ jsx("p", { className: "px-1 py-2 text-og-control leading-5 text-og-fg-muted", children: "Reconnect to use app controls." }) : null,
|
|
2608
|
+
nodes.length === 0 ? props.controlUnavailable ? null : /* @__PURE__ */ jsx("p", { className: "px-1 py-2 text-og-control leading-5 text-og-fg-muted", children: "This view has no accessibility controls. Use the live image instead." }) : /* @__PURE__ */ jsx("div", { className: "space-y-0.5", children: nodes.map((node) => /* @__PURE__ */ jsx(
|
|
1991
2609
|
ComputerSemanticControl,
|
|
1992
2610
|
{
|
|
1993
2611
|
node,
|
|
@@ -2003,8 +2621,8 @@ function ComputerSemanticControl(props) {
|
|
|
2003
2621
|
const editable = node.actions.includes("set_value");
|
|
2004
2622
|
const action = semanticAction(node);
|
|
2005
2623
|
const observedValue = semanticValue(node) ?? "";
|
|
2006
|
-
const [value, setValue] =
|
|
2007
|
-
|
|
2624
|
+
const [value, setValue] = useState5(() => observedValue);
|
|
2625
|
+
useEffect5(() => {
|
|
2008
2626
|
setValue(observedValue);
|
|
2009
2627
|
}, [node.ref, observedValue]);
|
|
2010
2628
|
if (editable) {
|
|
@@ -2014,6 +2632,7 @@ function ComputerSemanticControl(props) {
|
|
|
2014
2632
|
className: "rounded-og-sm px-2 py-1.5 hover:bg-og-surface-2",
|
|
2015
2633
|
onSubmit: (event) => {
|
|
2016
2634
|
event.preventDefault();
|
|
2635
|
+
if (props.mutating) return;
|
|
2017
2636
|
props.onAction({
|
|
2018
2637
|
type: "semantic",
|
|
2019
2638
|
locator: { kind: "ref", ref: node.ref },
|
|
@@ -2022,7 +2641,7 @@ function ComputerSemanticControl(props) {
|
|
|
2022
2641
|
});
|
|
2023
2642
|
},
|
|
2024
2643
|
children: [
|
|
2025
|
-
/* @__PURE__ */ jsx("label", { className: "block truncate text-[10px] uppercase text-og-subtle", children: node.name || node.identifier || node.role }),
|
|
2644
|
+
/* @__PURE__ */ jsx("label", { className: "block truncate text-[10px] uppercase text-og-fg-subtle", children: node.name || node.identifier || node.role }),
|
|
2026
2645
|
/* @__PURE__ */ jsxs("div", { className: "mt-1 flex gap-1", children: [
|
|
2027
2646
|
/* @__PURE__ */ jsx(
|
|
2028
2647
|
"input",
|
|
@@ -2058,7 +2677,7 @@ function ComputerSemanticControl(props) {
|
|
|
2058
2677
|
},
|
|
2059
2678
|
className: "flex w-full items-start gap-2 rounded-og-sm px-2 py-1.5 text-left transition hover:bg-og-surface-2 disabled:cursor-default disabled:hover:bg-transparent",
|
|
2060
2679
|
children: [
|
|
2061
|
-
/* @__PURE__ */ jsx("span", { className: "mt-0.5 text-[10px] uppercase text-og-subtle", children: node.role }),
|
|
2680
|
+
/* @__PURE__ */ jsx("span", { className: "mt-0.5 text-[10px] uppercase text-og-fg-subtle", children: node.role }),
|
|
2062
2681
|
/* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate text-og-control text-og-fg", children: node.name || semanticValue(node) || node.identifier || "Unnamed control" })
|
|
2063
2682
|
]
|
|
2064
2683
|
}
|
|
@@ -2066,7 +2685,7 @@ function ComputerSemanticControl(props) {
|
|
|
2066
2685
|
}
|
|
2067
2686
|
function ComputerStatusBar(props) {
|
|
2068
2687
|
const screen = props.target?.kind === "screen";
|
|
2069
|
-
return /* @__PURE__ */ jsxs("div", { className: "flex h-8 shrink-0 items-center gap-2 border-t border-og-border bg-og-surface-
|
|
2688
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex h-8 shrink-0 items-center gap-2 border-t border-og-border bg-og-surface-1 px-2 text-og-xs text-og-fg-subtle", children: [
|
|
2070
2689
|
/* @__PURE__ */ jsx(
|
|
2071
2690
|
"span",
|
|
2072
2691
|
{
|
|
@@ -2076,11 +2695,11 @@ function ComputerStatusBar(props) {
|
|
|
2076
2695
|
)
|
|
2077
2696
|
}
|
|
2078
2697
|
),
|
|
2079
|
-
/* @__PURE__ */ jsx("span", { children: props.connectionState === "live" ? "Live" : computerConnectionLabel(props.connectionState) }),
|
|
2080
|
-
/* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate", children: screen ? "Full screen \xB7 input may move pointer and focus" : props.session?.capabilities?.backgroundActions ? "
|
|
2698
|
+
/* @__PURE__ */ jsx("span", { children: !props.inputAllowed ? "View only" : props.controlUnavailable ? "Controls unavailable" : props.target?.kind === "app" ? "App controls" : props.connectionState === "live" ? "Live" : computerConnectionLabel(props.connectionState) }),
|
|
2699
|
+
/* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate", children: !props.inputAllowed ? "Refresh to check desktop controls" : props.controlUnavailable ? "Reconnect to use desktop input" : screen ? "Full screen \xB7 input may move pointer and focus" : props.target?.kind === "app" ? props.session?.capabilities?.backgroundActions ? "App \xB7 controls work in the background" : "App controls" : props.session?.capabilities?.backgroundInput ? "Window \xB7 clicks and typing stay in the background" : props.session?.capabilities?.backgroundActions ? "Window \xB7 app controls work in the background" : props.target?.kind ?? "Desktop" }),
|
|
2081
2700
|
screen ? /* @__PURE__ */ jsx(MousePointer2Icon, { className: "size-3", "aria-hidden": true }) : null,
|
|
2082
2701
|
props.refreshing ? /* @__PURE__ */ jsx(LoaderCircleIcon, { className: "size-3 animate-spin" }) : null,
|
|
2083
|
-
/* @__PURE__ */ jsxs(
|
|
2702
|
+
props.controlCount > 0 || props.showControls ? /* @__PURE__ */ jsxs(
|
|
2084
2703
|
"button",
|
|
2085
2704
|
{
|
|
2086
2705
|
type: "button",
|
|
@@ -2095,7 +2714,7 @@ function ComputerStatusBar(props) {
|
|
|
2095
2714
|
props.controlCount
|
|
2096
2715
|
]
|
|
2097
2716
|
}
|
|
2098
|
-
)
|
|
2717
|
+
) : null
|
|
2099
2718
|
] });
|
|
2100
2719
|
}
|
|
2101
2720
|
function ComputerNotice(props) {
|
|
@@ -2103,7 +2722,7 @@ function ComputerNotice(props) {
|
|
|
2103
2722
|
"div",
|
|
2104
2723
|
{
|
|
2105
2724
|
className: cn(
|
|
2106
|
-
"grid h-full place-items-center bg-og-bg text-og-control text-og-muted",
|
|
2725
|
+
"grid h-full place-items-center bg-og-bg text-og-control text-og-fg-muted",
|
|
2107
2726
|
props.className
|
|
2108
2727
|
),
|
|
2109
2728
|
children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
@@ -2113,11 +2732,30 @@ function ComputerNotice(props) {
|
|
|
2113
2732
|
}
|
|
2114
2733
|
);
|
|
2115
2734
|
}
|
|
2116
|
-
|
|
2735
|
+
var structuralRoles = /* @__PURE__ */ new Set([
|
|
2736
|
+
"frame",
|
|
2737
|
+
"panel",
|
|
2738
|
+
"section",
|
|
2739
|
+
"document",
|
|
2740
|
+
"document web",
|
|
2741
|
+
"static",
|
|
2742
|
+
"static text",
|
|
2743
|
+
"heading",
|
|
2744
|
+
"paragraph",
|
|
2745
|
+
"notification",
|
|
2746
|
+
"separator",
|
|
2747
|
+
"tool bar",
|
|
2748
|
+
"page tab list"
|
|
2749
|
+
]);
|
|
2750
|
+
function semanticControls(observation) {
|
|
2117
2751
|
if (observation?.semantic?.kind !== "snapshot") return [];
|
|
2118
2752
|
const result = [];
|
|
2119
2753
|
const visit = (node) => {
|
|
2120
|
-
|
|
2754
|
+
const role = node.role.trim().toLowerCase();
|
|
2755
|
+
const label = node.name?.trim() || node.identifier?.trim();
|
|
2756
|
+
if (node.actions.includes("set_value") || label && label.toLowerCase() !== role && !structuralRoles.has(role) && semanticAction(node) !== null) {
|
|
2757
|
+
result.push(node);
|
|
2758
|
+
}
|
|
2121
2759
|
node.children?.forEach(visit);
|
|
2122
2760
|
};
|
|
2123
2761
|
observation.semantic.roots.forEach(visit);
|
|
@@ -2138,11 +2776,24 @@ function semanticValue(node) {
|
|
|
2138
2776
|
return typeof node.value === "string" ? node.value : null;
|
|
2139
2777
|
}
|
|
2140
2778
|
function paintCanvas(canvas, image, width, height) {
|
|
2779
|
+
const dimensionsChanged = canvas.width !== width || canvas.height !== height;
|
|
2141
2780
|
canvas.width = width;
|
|
2142
2781
|
canvas.height = height;
|
|
2143
2782
|
const context = canvas.getContext("2d", { alpha: false });
|
|
2144
2783
|
if (!context) throw new Error("Desktop canvas is unavailable.");
|
|
2145
2784
|
context.drawImage(image, 0, 0, width, height);
|
|
2785
|
+
if (dimensionsChanged) fitComputerCanvas(canvas);
|
|
2786
|
+
}
|
|
2787
|
+
function fitComputerCanvas(canvas) {
|
|
2788
|
+
const viewport = canvas.parentElement;
|
|
2789
|
+
if (!viewport || canvas.width <= 0 || canvas.height <= 0) return;
|
|
2790
|
+
const scale = Math.min(
|
|
2791
|
+
viewport.clientWidth / canvas.width,
|
|
2792
|
+
viewport.clientHeight / canvas.height
|
|
2793
|
+
);
|
|
2794
|
+
if (!Number.isFinite(scale) || scale <= 0) return;
|
|
2795
|
+
canvas.style.width = `${canvas.width * scale}px`;
|
|
2796
|
+
canvas.style.height = `${canvas.height * scale}px`;
|
|
2146
2797
|
}
|
|
2147
2798
|
function loadImage(url) {
|
|
2148
2799
|
return new Promise((resolve, reject) => {
|
|
@@ -2189,7 +2840,10 @@ function computerKey(event) {
|
|
|
2189
2840
|
return parts.join("+");
|
|
2190
2841
|
}
|
|
2191
2842
|
function sameFrameFence(left, right) {
|
|
2192
|
-
return left.frameId === right.frameId && left
|
|
2843
|
+
return left.frameId === right.frameId && sameComputerTarget(left, right);
|
|
2844
|
+
}
|
|
2845
|
+
function sameComputerTarget(left, right) {
|
|
2846
|
+
return left.computerSessionId === right.computerSessionId && left.controllerGeneration === right.controllerGeneration && left.targetId === right.targetId && left.targetGeneration === right.targetGeneration;
|
|
2193
2847
|
}
|
|
2194
2848
|
function isLiveComputer2(session) {
|
|
2195
2849
|
return !["ending", "ended", "failed", "lost"].includes(session.lifecycle);
|
|
@@ -2202,6 +2856,8 @@ function computerFailureMessage(session) {
|
|
|
2202
2856
|
return "The desktop connection was lost. Try opening it again.";
|
|
2203
2857
|
case "controller_transition_expired":
|
|
2204
2858
|
return session.placement.kind === "attached_device" ? "Chrome reconnected\u2014open a fresh browser/desktop. Use Browser \u2192 New browser \u2192 Connected Chrome." : "The desktop connection expired. Open a new desktop.";
|
|
2859
|
+
case "idle_released":
|
|
2860
|
+
return "The desktop stopped after it went unused. Open a new desktop.";
|
|
2205
2861
|
case null:
|
|
2206
2862
|
return null;
|
|
2207
2863
|
default:
|
|
@@ -2291,10 +2947,10 @@ function computerConnectionLabel(state) {
|
|
|
2291
2947
|
}
|
|
2292
2948
|
|
|
2293
2949
|
export {
|
|
2294
|
-
useComputerFrameStream,
|
|
2295
|
-
useComputerSession,
|
|
2296
2950
|
useComputerSessions,
|
|
2951
|
+
useComputerSession,
|
|
2952
|
+
useComputerFrameStream,
|
|
2297
2953
|
ComputerViewer,
|
|
2298
2954
|
computerKey
|
|
2299
2955
|
};
|
|
2300
|
-
//# sourceMappingURL=chunk-
|
|
2956
|
+
//# sourceMappingURL=chunk-GUUXOISA.js.map
|