@opengeni/react 0.46.1 → 0.52.1
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 +158 -14
- package/dist/artifacts-document.d.ts +3 -0
- package/dist/artifacts-document.js +17 -0
- package/dist/artifacts-document.js.map +1 -0
- package/dist/artifacts-presentation.d.ts +3 -0
- package/dist/artifacts-presentation.js +17 -0
- package/dist/artifacts-presentation.js.map +1 -0
- package/dist/artifacts-spreadsheet.d.ts +3 -0
- package/dist/artifacts-spreadsheet.js +17 -0
- package/dist/artifacts-spreadsheet.js.map +1 -0
- package/dist/artifacts.d.ts +2 -0
- package/dist/artifacts.js +170 -0
- package/dist/artifacts.js.map +1 -0
- package/dist/chunk-6BBWWOOU.js +82 -0
- package/dist/chunk-6BBWWOOU.js.map +1 -0
- package/dist/{chunk-HFO4ERGQ.js → chunk-724BW4TW.js} +586 -35
- package/dist/chunk-724BW4TW.js.map +1 -0
- package/dist/chunk-7I2KB3OK.js +3895 -0
- package/dist/chunk-7I2KB3OK.js.map +1 -0
- package/dist/chunk-7WCLAEFV.js +2828 -0
- package/dist/chunk-7WCLAEFV.js.map +1 -0
- package/dist/{chunk-U3CSCMWA.js → chunk-FK6VG4LL.js} +9 -13
- package/dist/chunk-FK6VG4LL.js.map +1 -0
- package/dist/{chunk-SRFUT2ZU.js → chunk-FWS3KC3Z.js} +120 -64
- package/dist/chunk-FWS3KC3Z.js.map +1 -0
- package/dist/{chunk-EKZH6IDG.js → chunk-HDBG7XGO.js} +62 -1
- package/dist/chunk-HDBG7XGO.js.map +1 -0
- package/dist/chunk-HUIKIDM5.js +2148 -0
- package/dist/chunk-HUIKIDM5.js.map +1 -0
- package/dist/{chunk-LGVMUIRV.js → chunk-JAGDUCZQ.js} +1053 -390
- package/dist/chunk-JAGDUCZQ.js.map +1 -0
- package/dist/{chunk-LWR4MXSS.js → chunk-JR3QFY6B.js} +420 -35
- package/dist/chunk-JR3QFY6B.js.map +1 -0
- package/dist/chunk-JVYQRRC4.js +53 -0
- package/dist/chunk-JVYQRRC4.js.map +1 -0
- package/dist/chunk-KCGXPP5V.js +157 -0
- package/dist/chunk-KCGXPP5V.js.map +1 -0
- package/dist/{chunk-EB67J347.js → chunk-KZ73RL76.js} +111 -31
- package/dist/chunk-KZ73RL76.js.map +1 -0
- package/dist/chunk-PEI6RANI.js +3234 -0
- package/dist/chunk-PEI6RANI.js.map +1 -0
- package/dist/{chunk-3PB7MIT6.js → chunk-ROIRTM37.js} +220 -72
- package/dist/chunk-ROIRTM37.js.map +1 -0
- package/dist/chunk-VZTQ73XT.js +86 -0
- package/dist/chunk-VZTQ73XT.js.map +1 -0
- package/dist/{chunk-VCBFUSUZ.js → chunk-WSK7G5LE.js} +72 -4
- package/dist/chunk-WSK7G5LE.js.map +1 -0
- package/dist/{chunk-FGZUCXZF.js → chunk-WYA65Y3F.js} +16 -16
- package/dist/chunk-WYA65Y3F.js.map +1 -0
- package/dist/{chunk-6EDV6YFE.js → chunk-X4S56Y2H.js} +147 -35
- package/dist/chunk-X4S56Y2H.js.map +1 -0
- package/dist/client.d.ts +7 -1
- package/dist/clipboard.d.ts +1 -0
- package/dist/clipboard.js +7 -0
- package/dist/clipboard.js.map +1 -0
- package/dist/commands/index.d.ts +3 -3
- package/dist/commands/registry.d.ts +1 -1
- package/dist/commands/types.d.ts +1 -1
- package/dist/components/approval-surface.d.ts +2 -2
- package/dist/components/artifacts/artifact-surface.d.ts +18 -0
- package/dist/components/artifacts/document-editor.d.ts +172 -0
- package/dist/components/artifacts/editable-artifact-ui.d.ts +13 -0
- package/dist/components/artifacts/editable-artifact-workbench.d.ts +49 -0
- package/dist/components/artifacts/editable-document.d.ts +24 -0
- package/dist/components/artifacts/editable-presentation.d.ts +24 -0
- package/dist/components/artifacts/editable-spreadsheet.d.ts +32 -0
- package/dist/components/artifacts/index.d.ts +8 -0
- package/dist/components/artifacts/presentation-editor.d.ts +297 -0
- package/dist/components/artifacts/spreadsheet-canvas.d.ts +120 -0
- package/dist/components/artifacts/spreadsheet-grid.d.ts +113 -0
- package/dist/components/browser-viewer.d.ts +29 -0
- package/dist/components/chat-composer.d.ts +13 -8
- package/dist/components/code-editor.d.ts +25 -3
- package/dist/components/command-palette.d.ts +2 -2
- package/dist/components/composer-transcription-control.d.ts +4 -2
- package/dist/components/composer.d.ts +29 -15
- package/dist/components/computer-viewer.d.ts +23 -0
- package/dist/components/copy-button.d.ts +2 -2
- package/dist/components/desktop-viewer.d.ts +25 -21
- package/dist/components/diff-view.d.ts +1 -1
- package/dist/components/enrollment-consent.d.ts +1 -1
- package/dist/components/enrollment-device-flow.d.ts +1 -1
- package/dist/components/file-browser.d.ts +2 -2
- package/dist/components/fleet-tile.d.ts +1 -1
- package/dist/components/generated-video-player.d.ts +10 -0
- package/dist/components/human-input-form.d.ts +1 -1
- package/dist/components/human-input-surface.d.ts +2 -2
- package/dist/components/interactive-workbench-viewers.d.ts +2 -0
- package/dist/components/machine-card.d.ts +2 -2
- package/dist/components/machine-dock-bar.d.ts +3 -3
- package/dist/components/machine-health-pill.d.ts +2 -2
- package/dist/components/machine-input-display.d.ts +1 -1
- package/dist/components/machine-metrics.d.ts +2 -2
- package/dist/components/machine-status-pill.d.ts +4 -4
- package/dist/components/machines/health.d.ts +1 -1
- package/dist/components/machines/machine-detail.d.ts +5 -3
- package/dist/components/machines/metric-history-chart.d.ts +1 -1
- package/dist/components/machines/metric-sparkline.d.ts +2 -2
- package/dist/components/machines/series.d.ts +2 -2
- package/dist/components/machines-dashboard.d.ts +5 -5
- package/dist/components/markdown.d.ts +1 -1
- package/dist/components/message-timeline.d.ts +22 -5
- package/dist/components/model-picker.d.ts +2 -2
- package/dist/components/model-policy-picker.d.ts +7 -7
- package/dist/components/pierre-diff.d.ts +1 -1
- package/dist/components/pierre-file.d.ts +1 -1
- package/dist/components/queue-draft-policy.d.ts +1 -1
- package/dist/components/queue-surface-implementation.d.ts +3 -3
- package/dist/components/queue-surface-state.d.ts +4 -4
- package/dist/components/queue-surface.d.ts +5 -5
- package/dist/components/sandbox-files.d.ts +5 -5
- package/dist/components/sandbox-terminal.d.ts +50 -4
- package/dist/components/sandbox-workspace.d.ts +30 -10
- package/dist/components/session-chrome.d.ts +4 -4
- package/dist/components/session-status.d.ts +2 -2
- package/dist/components/timeline-annotation-selection.d.ts +9 -0
- package/dist/components/timeline-annotations-dialog.d.ts +16 -0
- package/dist/components/timeline-annotations.d.ts +13 -0
- package/dist/components/tooltip.d.ts +4 -4
- package/dist/components/user-message-body.d.ts +2 -2
- package/dist/components/workbench-changes.d.ts +2 -2
- package/dist/components/workspace-dock.d.ts +3 -1
- package/dist/composer.d.ts +11 -11
- package/dist/composer.js +7 -7
- package/dist/hooks/internal.d.ts +7 -1
- package/dist/hooks/use-attached-browsers.d.ts +21 -0
- package/dist/hooks/use-available-models.d.ts +2 -2
- package/dist/hooks/use-billing-usage.d.ts +1 -1
- package/dist/hooks/use-browser-frame-stream.d.ts +28 -0
- package/dist/hooks/use-browser-identities.d.ts +26 -0
- package/dist/hooks/use-browser-session.d.ts +28 -0
- package/dist/hooks/use-browser-sessions.d.ts +32 -0
- package/dist/hooks/use-codex-accounts.d.ts +3 -3
- package/dist/hooks/use-composer.d.ts +36 -9
- package/dist/hooks/use-computer-frame-stream.d.ts +24 -0
- package/dist/hooks/use-computer-session.d.ts +28 -0
- package/dist/hooks/use-computer-sessions.d.ts +28 -0
- package/dist/hooks/use-desktop-stream.d.ts +1 -1
- package/dist/hooks/use-environments.d.ts +11 -5
- package/dist/hooks/use-file-attachments.d.ts +1 -1
- package/dist/hooks/use-goal.d.ts +2 -2
- package/dist/hooks/use-human-input.d.ts +2 -2
- package/dist/hooks/use-last-started-turn-policy.d.ts +2 -2
- package/dist/hooks/use-machine-chip.d.ts +2 -2
- package/dist/hooks/use-machines.d.ts +10 -3
- package/dist/hooks/use-packs.d.ts +7 -2
- package/dist/hooks/use-rigs.d.ts +1 -1
- package/dist/hooks/use-sandbox-files.d.ts +25 -4
- package/dist/hooks/use-sandbox-git.d.ts +13 -5
- package/dist/hooks/use-sandbox-terminal.d.ts +1 -1
- package/dist/hooks/use-scheduled-tasks.d.ts +1 -1
- package/dist/hooks/use-session-capabilities.d.ts +5 -4
- package/dist/hooks/use-session-control.d.ts +1 -1
- package/dist/hooks/use-session-events.d.ts +2 -2
- package/dist/hooks/use-session-lineage.d.ts +1 -1
- package/dist/hooks/use-session-mcp-approval-policy.d.ts +2 -2
- package/dist/hooks/use-session.d.ts +2 -2
- package/dist/hooks/use-slash-commands.d.ts +1 -1
- package/dist/hooks/use-terminal-stream.d.ts +8 -2
- package/dist/hooks/use-transcription.d.ts +2 -2
- package/dist/hooks/use-turn-queue.d.ts +2 -2
- package/dist/hooks/use-video-artifact-playback.d.ts +9 -0
- package/dist/hooks/use-voice-input.d.ts +6 -4
- package/dist/hooks/use-workspace-capture.d.ts +1 -1
- package/dist/hooks/use-workspace-edit.d.ts +1 -1
- package/dist/hooks/use-workspace-sessions.d.ts +1 -1
- package/dist/hooks/use-workspaces.d.ts +1 -1
- package/dist/index.d.ts +158 -154
- package/dist/index.js +1718 -908
- package/dist/index.js.map +1 -1
- package/dist/interaction.d.ts +23 -0
- package/dist/interaction.js +36 -0
- package/dist/interaction.js.map +1 -0
- package/dist/interactive-workbench-viewers-KHNOHWQE.js +13 -0
- package/dist/interactive-workbench-viewers-KHNOHWQE.js.map +1 -0
- package/dist/lib/composer-responsive-context.d.ts +9 -0
- package/dist/lib/relay-wire.d.ts +4 -0
- package/dist/lib/sandbox-liveness.d.ts +9 -0
- package/dist/lib/terminal-capability.d.ts +5 -0
- package/dist/lib/use-portal-token-style.d.ts +14 -3
- package/dist/lib/xterm-theme.d.ts +1 -1
- package/dist/machines.d.ts +30 -30
- package/dist/machines.js +3 -3
- package/dist/provider.d.ts +4 -4
- package/dist/{queue-surface-implementation-J4XJFKRW.js → queue-surface-implementation-2KVJTYXH.js} +117 -64
- package/dist/queue-surface-implementation-2KVJTYXH.js.map +1 -0
- package/dist/realtime/dropdown-menu.d.ts +8 -8
- package/dist/realtime/realtime-control.d.ts +15 -6
- package/dist/realtime.d.ts +4 -4
- package/dist/realtime.js +30 -33
- package/dist/realtime.js.map +1 -1
- package/dist/session-context.d.ts +19 -1
- package/dist/session-ui.d.ts +14 -14
- package/dist/session-ui.js +6 -5
- package/dist/session.d.ts +28 -28
- package/dist/session.js +5 -5
- package/dist/timeline/activity-rail.d.ts +6 -3
- package/dist/timeline/disclosure-context.d.ts +1 -1
- package/dist/timeline/fleet-decision-projection.d.ts +1 -1
- package/dist/timeline/fleet-decision-row.d.ts +3 -3
- package/dist/timeline/index.d.ts +19 -19
- package/dist/timeline/parsers.d.ts +6 -3
- package/dist/timeline/projection.d.ts +3 -3
- package/dist/timeline/registry.d.ts +10 -1
- package/dist/timeline/screenshot-lightbox.d.ts +2 -2
- package/dist/timeline/shared.d.ts +21 -10
- package/dist/timeline/tool-diff.d.ts +2 -2
- package/dist/timeline/tool-renderers.d.ts +1 -1
- package/dist/timeline/turn-summary.d.ts +3 -3
- package/dist/timeline/types.d.ts +42 -2
- package/dist/timeline-annotation-selection-3L7LHGG2.js +187 -0
- package/dist/timeline-annotation-selection-3L7LHGG2.js.map +1 -0
- package/dist/timeline-annotations-dialog-U7EVLR75.js +196 -0
- package/dist/timeline-annotations-dialog-U7EVLR75.js.map +1 -0
- package/dist/voice-recording-store.d.ts +8 -2
- package/package.json +52 -7
- package/src/artifacts-document.ts +31 -0
- package/src/artifacts-presentation.ts +36 -0
- package/src/artifacts-spreadsheet.ts +21 -0
- package/src/artifacts.ts +86 -0
- package/src/client.ts +90 -1
- package/src/clipboard.ts +2 -0
- package/src/components/artifacts/artifact-surface.tsx +93 -0
- package/src/components/artifacts/document-editor.tsx +2255 -0
- package/src/components/artifacts/editable-artifact-ui.tsx +89 -0
- package/src/components/artifacts/editable-artifact-workbench.tsx +203 -0
- package/src/components/artifacts/editable-document.tsx +657 -0
- package/src/components/artifacts/editable-presentation.tsx +1020 -0
- package/src/components/artifacts/editable-spreadsheet.tsx +733 -0
- package/src/components/artifacts/index.ts +99 -0
- package/src/components/artifacts/presentation-editor.tsx +3148 -0
- package/src/components/artifacts/spreadsheet-canvas.ts +799 -0
- package/src/components/artifacts/spreadsheet-grid.tsx +2129 -0
- package/src/components/browser-viewer.tsx +1592 -0
- package/src/components/chat-composer.tsx +25 -2
- package/src/components/code-editor.tsx +90 -31
- package/src/components/command-palette.tsx +1 -1
- package/src/components/composer-transcription-control.tsx +51 -22
- package/src/components/composer.tsx +49 -23
- package/src/components/computer-viewer.tsx +1139 -0
- package/src/components/copy-button.tsx +1 -1
- package/src/components/desktop-viewer.tsx +103 -75
- package/src/components/file-browser.tsx +39 -17
- package/src/components/generated-video-player.tsx +89 -0
- package/src/components/human-input-form.tsx +2 -2
- package/src/components/interactive-workbench-viewers.ts +2 -0
- package/src/components/machine-card.tsx +1 -1
- package/src/components/machine-input-display.ts +3 -0
- package/src/components/machines/machine-detail.tsx +39 -15
- package/src/components/machines-dashboard.tsx +7 -6
- package/src/components/message-timeline.tsx +327 -37
- package/src/components/model-picker.tsx +2 -2
- package/src/components/model-policy-picker.tsx +16 -15
- package/src/components/queue-surface-implementation.tsx +135 -75
- package/src/components/queue-surface-state.tsx +2 -2
- package/src/components/sandbox-files.tsx +142 -62
- package/src/components/sandbox-terminal.tsx +119 -35
- package/src/components/sandbox-workspace.tsx +394 -228
- package/src/components/session-chrome.tsx +67 -27
- package/src/components/timeline-annotation-selection.tsx +221 -0
- package/src/components/timeline-annotations-dialog.tsx +213 -0
- package/src/components/timeline-annotations.tsx +84 -0
- package/src/components/tooltip.tsx +13 -16
- package/src/components/user-message-body.tsx +1 -1
- package/src/components/workbench-changes.tsx +192 -35
- package/src/components/workspace-dock.tsx +13 -5
- package/src/composer.ts +3 -0
- package/src/hooks/internal.ts +132 -38
- package/src/hooks/use-attached-browsers.ts +152 -0
- package/src/hooks/use-browser-frame-stream.ts +316 -0
- package/src/hooks/use-browser-identities.ts +225 -0
- package/src/hooks/use-browser-session.ts +476 -0
- package/src/hooks/use-browser-sessions.ts +281 -0
- package/src/hooks/use-codex-accounts.ts +4 -4
- package/src/hooks/use-composer.ts +769 -24
- package/src/hooks/use-computer-frame-stream.ts +311 -0
- package/src/hooks/use-computer-session.ts +442 -0
- package/src/hooks/use-computer-sessions.ts +245 -0
- package/src/hooks/use-environments.ts +24 -5
- package/src/hooks/use-file-attachments.ts +70 -32
- package/src/hooks/use-goal.ts +17 -5
- package/src/hooks/use-machine-chip.ts +5 -3
- package/src/hooks/use-machines.ts +79 -3
- package/src/hooks/use-packs.ts +51 -0
- package/src/hooks/use-sandbox-files.ts +382 -88
- package/src/hooks/use-sandbox-git.ts +208 -46
- package/src/hooks/use-sandbox-terminal.ts +3 -2
- package/src/hooks/use-session-capabilities.ts +94 -70
- package/src/hooks/use-session-events.ts +18 -4
- package/src/hooks/use-terminal-stream.ts +102 -23
- package/src/hooks/use-transcription.ts +4 -14
- package/src/hooks/use-turn-queue.ts +17 -5
- package/src/hooks/use-video-artifact-playback.ts +28 -0
- package/src/hooks/use-voice-input.ts +163 -19
- package/src/hooks/use-windowed-sections.ts +9 -7
- package/src/hooks/use-workspace-edit.ts +2 -1
- package/src/index.ts +21 -6
- package/src/interaction.ts +71 -0
- package/src/lib/composer-responsive-context.ts +15 -0
- package/src/lib/relay-wire.ts +4 -0
- package/src/lib/sandbox-liveness.ts +11 -0
- package/src/lib/terminal-capability.ts +10 -0
- package/src/lib/use-portal-token-style.ts +60 -15
- package/src/provider.tsx +52 -7
- package/src/realtime/dropdown-menu.tsx +15 -19
- package/src/realtime/realtime-control.tsx +16 -5
- package/src/session-context.ts +90 -0
- package/src/timeline/activity-rail.tsx +31 -6
- package/src/timeline/index.ts +4 -1
- package/src/timeline/parsers.ts +46 -20
- package/src/timeline/projection.ts +233 -13
- package/src/timeline/registry.ts +19 -0
- package/src/timeline/screenshot-lightbox.tsx +30 -9
- package/src/timeline/shared.tsx +59 -11
- package/src/timeline/tool-renderers.tsx +421 -24
- package/src/timeline/turn-summary.tsx +2 -2
- package/src/timeline/types.ts +58 -2
- package/src/voice-recording-store.ts +15 -0
- package/styles/compiled.css +5298 -0
- package/styles/compiled.d.ts +2 -0
- package/styles/effective-tokens.css +27 -0
- package/styles/index.css +18 -0
- package/styles/responsive.css +208 -0
- package/styles/responsive.d.ts +2 -0
- package/styles/tokens.css +50 -17
- package/dist/chunk-3PB7MIT6.js.map +0 -1
- package/dist/chunk-6EDV6YFE.js.map +0 -1
- package/dist/chunk-EB67J347.js.map +0 -1
- package/dist/chunk-EKZH6IDG.js.map +0 -1
- package/dist/chunk-FGZUCXZF.js.map +0 -1
- package/dist/chunk-HFO4ERGQ.js.map +0 -1
- package/dist/chunk-JB5725BI.js +0 -48
- package/dist/chunk-JB5725BI.js.map +0 -1
- package/dist/chunk-LGVMUIRV.js.map +0 -1
- package/dist/chunk-LWR4MXSS.js.map +0 -1
- package/dist/chunk-SRFUT2ZU.js.map +0 -1
- package/dist/chunk-U3CSCMWA.js.map +0 -1
- package/dist/chunk-VCBFUSUZ.js.map +0 -1
- package/dist/fleet-decision-projection-SLNZMS42.js +0 -241
- package/dist/fleet-decision-projection-SLNZMS42.js.map +0 -1
- package/dist/queue-surface-implementation-J4XJFKRW.js.map +0 -1
|
@@ -96,7 +96,7 @@ export function CopyButton({
|
|
|
96
96
|
"inline-flex size-7 shrink-0 items-center justify-center rounded-og-sm pointer-coarse:size-11",
|
|
97
97
|
"text-og-fg-subtle transition-[opacity,color,background-color] duration-150",
|
|
98
98
|
"hover:bg-og-surface-2/80 hover:text-og-fg",
|
|
99
|
-
"focus-visible:opacity-100 focus-visible:outline-
|
|
99
|
+
"focus-visible:opacity-100 focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-og-accent/40",
|
|
100
100
|
reveal === "group-hover" &&
|
|
101
101
|
"opacity-0 group-hover/copy:opacity-100 group-focus-within/copy:opacity-100 pointer-coarse:opacity-70",
|
|
102
102
|
copied && "opacity-100 text-og-accent",
|
|
@@ -28,22 +28,26 @@ export type DesktopViewerProps = {
|
|
|
28
28
|
* Defaults to `new WebSocket(url)`. Mirrors `rfbFactory`. */
|
|
29
29
|
webSocketFactory?: DesktopWebSocketFactory | undefined;
|
|
30
30
|
/**
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
* been given. Call `onAccept` to record consent (the host wires it to
|
|
34
|
-
* `client.acknowledgeStream` + a re-negotiate). When omitted, a default
|
|
35
|
-
* banner is shown.
|
|
31
|
+
* @deprecated Desktop acknowledgment is automatic. Retained as an ignored
|
|
32
|
+
* compatibility prop so existing embedders keep compiling.
|
|
36
33
|
*/
|
|
37
34
|
renderConsentGate?: ((onAccept: () => void, shared: boolean) => ReactNode) | undefined;
|
|
38
|
-
/** Called when the default consent gate's accept button is pressed. */
|
|
39
|
-
onAcknowledge?: (() => void) | undefined;
|
|
40
35
|
/**
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
*
|
|
36
|
+
* Called once when the viewer mounts. Hosts use this to engage the desktop
|
|
37
|
+
* holder immediately when the Desktop tab opens.
|
|
38
|
+
*/
|
|
39
|
+
onActivate?: (() => void) | undefined;
|
|
40
|
+
/**
|
|
41
|
+
* Called automatically when the server requires acknowledgment of the
|
|
42
|
+
* un-redacted (and possibly shared) pixel plane. The viewer shows its ordinary
|
|
43
|
+
* opening state while this completes; no click-through gate is rendered.
|
|
44
|
+
*/
|
|
45
|
+
onAcknowledge?: (() => void | Promise<void>) | undefined;
|
|
46
|
+
/**
|
|
47
|
+
* Whether the host has the viewer attach engaged. Drives the cold-state
|
|
48
|
+
* behaviour: when watching, a cold-but-warmable lease AUTO-WARMS (and re-warms
|
|
49
|
+
* when the box drains) instead of dead-ending. Mounting the viewer counts as
|
|
50
|
+
* watching when this is omitted.
|
|
47
51
|
*/
|
|
48
52
|
watching?: boolean | undefined;
|
|
49
53
|
/**
|
|
@@ -51,8 +55,7 @@ export type DesktopViewerProps = {
|
|
|
51
55
|
* already been recorded — only the box drained). The host wires this to
|
|
52
56
|
* "engage the viewer attach + re-negotiate". Called automatically when a
|
|
53
57
|
* watched desktop is found cold-but-warmable, and behind the manual retry on
|
|
54
|
-
* the warming notice. Distinct from
|
|
55
|
-
* consent-bearing warm.
|
|
58
|
+
* the warming notice. Distinct from the automatic acknowledgment request.
|
|
56
59
|
*/
|
|
57
60
|
onWarm?: (() => void) | undefined;
|
|
58
61
|
/** Shown when transport is null (headless backend / degraded / disabled). */
|
|
@@ -75,13 +78,14 @@ export type DesktopViewerProps = {
|
|
|
75
78
|
/**
|
|
76
79
|
* The derived desktop surface state. A single source of truth so the overlay,
|
|
77
80
|
* the scrim, the auto-warm effect, and the watchdog all agree. Priority order
|
|
78
|
-
* (highest first): viewer-cap → unavailable →
|
|
79
|
-
* → error → connecting → connected.
|
|
81
|
+
* (highest first): viewer-cap → unavailable → acknowledgment-error → warming →
|
|
82
|
+
* connect-failed → error → opening/connecting → connected.
|
|
80
83
|
*/
|
|
81
84
|
type DesktopUiState =
|
|
82
85
|
| "viewer_cap" // 429 — the per-session live-viewer limit is reached.
|
|
83
86
|
| "unavailable" // genuinely unsupported (headless/policy/os/backend/not-provisioned).
|
|
84
|
-
| "
|
|
87
|
+
| "acknowledgment_error" // automatic un-redacted/shared acknowledgment failed.
|
|
88
|
+
| "opening" // acknowledgment or viewer attach is in flight.
|
|
85
89
|
| "warming" // cold-but-warmable: box not running yet (we auto-warm + spin).
|
|
86
90
|
| "connecting" // url is live, RFB negotiating/handshaking (we spin, with a watchdog).
|
|
87
91
|
| "connect_failed" // watchdog fired: url present but never connected.
|
|
@@ -122,10 +126,11 @@ function isHardUnavailable(reason: CapabilityUnavailableReason | null): boolean
|
|
|
122
126
|
* reason).
|
|
123
127
|
*
|
|
124
128
|
* Warming: a cold-but-warmable lease (`reason: "lease_cold"`) is NOT a dead end.
|
|
125
|
-
* When the
|
|
126
|
-
*
|
|
127
|
-
* cold it re-warms.
|
|
128
|
-
* keep a clear,
|
|
129
|
+
* When the viewer is mounted, it asks the host to engage the desktop and records
|
|
130
|
+
* any required acknowledgment automatically. A cold-but-warmable box then
|
|
131
|
+
* (re)warms through `onWarm`; if it later drains to cold it re-warms.
|
|
132
|
+
* Genuinely-unavailable surfaces (headless/policy/os/backend) keep a clear,
|
|
133
|
+
* static unavailable notice.
|
|
129
134
|
*/
|
|
130
135
|
export function DesktopViewer({
|
|
131
136
|
capability,
|
|
@@ -134,7 +139,7 @@ export function DesktopViewer({
|
|
|
134
139
|
scaleViewport,
|
|
135
140
|
rfbFactory,
|
|
136
141
|
webSocketFactory,
|
|
137
|
-
|
|
142
|
+
onActivate,
|
|
138
143
|
onAcknowledge,
|
|
139
144
|
watching,
|
|
140
145
|
onWarm,
|
|
@@ -146,7 +151,13 @@ export function DesktopViewer({
|
|
|
146
151
|
className,
|
|
147
152
|
}: DesktopViewerProps) {
|
|
148
153
|
const containerRef = useRef<HTMLDivElement | null>(null);
|
|
149
|
-
const
|
|
154
|
+
const onActivateRef = useRef(onActivate);
|
|
155
|
+
const onAcknowledgeRef = useRef(onAcknowledge);
|
|
156
|
+
onActivateRef.current = onActivate;
|
|
157
|
+
onAcknowledgeRef.current = onAcknowledge;
|
|
158
|
+
const acknowledgmentAttemptedRef = useRef(false);
|
|
159
|
+
const [acknowledgmentError, setAcknowledgmentError] = useState<Error | null>(null);
|
|
160
|
+
const [acknowledgmentRetryNonce, setAcknowledgmentRetryNonce] = useState(0);
|
|
150
161
|
// Local control state when not externally controlled. The server gate
|
|
151
162
|
// (`capability.mode`) is the hard ceiling — a read-only deployment can never
|
|
152
163
|
// be flipped to interactive regardless of this toggle.
|
|
@@ -156,16 +167,16 @@ export function DesktopViewer({
|
|
|
156
167
|
const wantControl = externallyControlled ? interactive : takeControl;
|
|
157
168
|
const inControl = Boolean(wantControl) && serverAllowsControl;
|
|
158
169
|
|
|
159
|
-
//
|
|
160
|
-
//
|
|
161
|
-
//
|
|
162
|
-
const isWatching = watching ??
|
|
170
|
+
// Mounting this component means the Desktop surface is being viewed. A host
|
|
171
|
+
// may still pass its holder state explicitly while the activation callback
|
|
172
|
+
// flips that state on the first mount.
|
|
173
|
+
const isWatching = watching ?? true;
|
|
163
174
|
|
|
164
175
|
// ── Decide the rendered state (before touching the stream hook) ─────────────
|
|
165
176
|
const transportNull = !capability || capability.transport === null;
|
|
166
177
|
const reason = capability?.reason ?? null;
|
|
167
|
-
const
|
|
168
|
-
capability?.requiresAcknowledgment === true && capability.acknowledged !== true
|
|
178
|
+
const needsAcknowledgment =
|
|
179
|
+
capability?.requiresAcknowledgment === true && capability.acknowledged !== true;
|
|
169
180
|
// No live address yet on an otherwise-live transport (post-ack, mid-warm).
|
|
170
181
|
const noLiveAddress = Boolean(capability) && !transportNull && !capability!.url;
|
|
171
182
|
|
|
@@ -176,9 +187,45 @@ export function DesktopViewer({
|
|
|
176
187
|
// Genuinely-unavailable: transport null for a HARD reason (not lease_cold).
|
|
177
188
|
const hardUnavailable = transportNull && isHardUnavailable(reason);
|
|
178
189
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
190
|
+
// Selecting Desktop mounts the viewer, which is itself the user's intent to
|
|
191
|
+
// open it. Engage the holder immediately — no separate "watch" click.
|
|
192
|
+
useEffect(() => {
|
|
193
|
+
onActivateRef.current?.();
|
|
194
|
+
}, []);
|
|
195
|
+
|
|
196
|
+
// Preserve the server-side acknowledgment/audit contract without making the
|
|
197
|
+
// user click through a disclosure card. One automatic attempt runs for each
|
|
198
|
+
// unacknowledged episode; a genuine request failure gets an explicit retry.
|
|
199
|
+
useEffect(() => {
|
|
200
|
+
if (!needsAcknowledgment) {
|
|
201
|
+
acknowledgmentAttemptedRef.current = false;
|
|
202
|
+
setAcknowledgmentError(null);
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
if (acknowledgmentAttemptedRef.current) return;
|
|
206
|
+
acknowledgmentAttemptedRef.current = true;
|
|
207
|
+
setAcknowledgmentError(null);
|
|
208
|
+
const acknowledge = onAcknowledgeRef.current;
|
|
209
|
+
if (!acknowledge) {
|
|
210
|
+
setAcknowledgmentError(new Error("Desktop acknowledgment is not configured."));
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
let active = true;
|
|
214
|
+
void Promise.resolve()
|
|
215
|
+
.then(() => acknowledge())
|
|
216
|
+
.catch((cause) => {
|
|
217
|
+
if (!active) return;
|
|
218
|
+
setAcknowledgmentError(cause instanceof Error ? cause : new Error(String(cause)));
|
|
219
|
+
});
|
|
220
|
+
return () => {
|
|
221
|
+
active = false;
|
|
222
|
+
};
|
|
223
|
+
}, [needsAcknowledgment, acknowledgmentRetryNonce]);
|
|
224
|
+
|
|
225
|
+
const retryAcknowledgment = () => {
|
|
226
|
+
acknowledgmentAttemptedRef.current = false;
|
|
227
|
+
setAcknowledgmentError(null);
|
|
228
|
+
setAcknowledgmentRetryNonce((nonce) => nonce + 1);
|
|
182
229
|
};
|
|
183
230
|
|
|
184
231
|
// Release control WITHOUT ever swallowing a key the desktop needs. Esc, and
|
|
@@ -218,7 +265,8 @@ export function DesktopViewer({
|
|
|
218
265
|
// Do NOT open a socket while the viewer-cap (429) notice is showing — the slot
|
|
219
266
|
// is already exhausted, so connecting would only burn a doomed attempt (and in
|
|
220
267
|
// tests leak an unhandled ws error from the never-resolving tunnel URL).
|
|
221
|
-
const connectCapability =
|
|
268
|
+
const connectCapability =
|
|
269
|
+
!transportNull && !needsAcknowledgment && !viewerCapReached ? capability : null;
|
|
222
270
|
const stream = useDesktopStream({
|
|
223
271
|
capability: connectCapability,
|
|
224
272
|
containerRef,
|
|
@@ -238,21 +286,19 @@ export function DesktopViewer({
|
|
|
238
286
|
|
|
239
287
|
// ── AUTO-WARM ───────────────────────────────────────────────────────────────
|
|
240
288
|
// When the user is watching and the desktop is cold-but-warmable, ask the host
|
|
241
|
-
// to (re)warm the box. This covers BOTH (a) the
|
|
289
|
+
// to (re)warm the box. This covers BOTH (a) the desktop was just opened and
|
|
242
290
|
// (b) a previously-warm box that drained back to cold under a live viewer. We
|
|
243
291
|
// fire once per distinct cold episode (keyed on the lease epoch + url) so we
|
|
244
292
|
// don't spam the attach while a single warm is in flight. `onWarm` is the
|
|
245
|
-
// no-re-ack path;
|
|
246
|
-
// first warm still rides consent and subsequent drains fall back to it.
|
|
293
|
+
// no-re-ack path; acknowledgment remains a separate automatic request.
|
|
247
294
|
const warmKeyRef = useRef<string | null>(null);
|
|
248
295
|
useEffect(() => {
|
|
249
|
-
if (!isWatching || !coldWarmable ||
|
|
296
|
+
if (!isWatching || !coldWarmable || needsAcknowledgment || viewerCapReached) {
|
|
250
297
|
// Reset the de-dupe once we leave the cold episode so a future drain warms.
|
|
251
298
|
if (!coldWarmable) warmKeyRef.current = null;
|
|
252
299
|
return;
|
|
253
300
|
}
|
|
254
|
-
|
|
255
|
-
if (!warm) return;
|
|
301
|
+
if (!onWarm) return;
|
|
256
302
|
// Key the de-dupe on the cell fields available to the viewer: while cold,
|
|
257
303
|
// `reason`/`url`/`expiresAt` are stable, so we warm exactly once; once the box
|
|
258
304
|
// warms the cell changes (url+expiresAt minted) and `coldWarmable` flips false,
|
|
@@ -260,10 +306,10 @@ export function DesktopViewer({
|
|
|
260
306
|
const key = `${capability?.reason ?? ""}:${capability?.url ?? ""}:${capability?.expiresAt ?? ""}`;
|
|
261
307
|
if (warmKeyRef.current === key) return; // already kicked this episode.
|
|
262
308
|
warmKeyRef.current = key;
|
|
263
|
-
|
|
309
|
+
onWarm();
|
|
264
310
|
// capability identity is the trigger; leaseEpoch/expiresAt key the episode.
|
|
265
311
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
266
|
-
}, [isWatching, coldWarmable,
|
|
312
|
+
}, [isWatching, coldWarmable, needsAcknowledgment, viewerCapReached, capability, onWarm]);
|
|
267
313
|
|
|
268
314
|
// ── TAB RE-ATTACH ───────────────────────────────────────────────────────────
|
|
269
315
|
// The "stuck Watching, never connects" bug: the RFB socket never (re)fires when
|
|
@@ -331,7 +377,8 @@ export function DesktopViewer({
|
|
|
331
377
|
let uiState: DesktopUiState;
|
|
332
378
|
if (viewerCapReached) uiState = "viewer_cap";
|
|
333
379
|
else if (hardUnavailable) uiState = "unavailable";
|
|
334
|
-
else if (
|
|
380
|
+
else if (acknowledgmentError) uiState = "acknowledgment_error";
|
|
381
|
+
else if (needsAcknowledgment) uiState = "opening";
|
|
335
382
|
else if (coldWarmable) uiState = "warming";
|
|
336
383
|
else if (connected) uiState = "connected";
|
|
337
384
|
else if (stream.error) uiState = "error";
|
|
@@ -340,7 +387,7 @@ export function DesktopViewer({
|
|
|
340
387
|
|
|
341
388
|
// An overlay blocks the surface for every state except connected; the toggle
|
|
342
389
|
// and the live scrim are suppressed under an overlay.
|
|
343
|
-
const overlayShown = uiState !== "connected" && uiState !== "connecting";
|
|
390
|
+
const overlayShown = uiState !== "connected" && uiState !== "connecting" && uiState !== "opening";
|
|
344
391
|
const showToggle = showControlToggle && !overlayShown;
|
|
345
392
|
|
|
346
393
|
let overlay: ReactNode = null;
|
|
@@ -358,11 +405,11 @@ export function DesktopViewer({
|
|
|
358
405
|
renderUnavailable?.(reason) ??
|
|
359
406
|
defaultNotice("Desktop unavailable", unavailableCopy(reason));
|
|
360
407
|
break;
|
|
361
|
-
case "
|
|
362
|
-
overlay =
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
408
|
+
case "acknowledgment_error":
|
|
409
|
+
overlay = defaultNotice(
|
|
410
|
+
"Couldn’t open desktop",
|
|
411
|
+
acknowledgmentError?.message ?? "The desktop acknowledgment failed.",
|
|
412
|
+
retryAcknowledgment,
|
|
366
413
|
);
|
|
367
414
|
break;
|
|
368
415
|
case "warming":
|
|
@@ -382,6 +429,7 @@ export function DesktopViewer({
|
|
|
382
429
|
reconnect,
|
|
383
430
|
);
|
|
384
431
|
break;
|
|
432
|
+
case "opening":
|
|
385
433
|
case "connecting":
|
|
386
434
|
case "connected":
|
|
387
435
|
overlay = null;
|
|
@@ -420,7 +468,7 @@ export function DesktopViewer({
|
|
|
420
468
|
rectangle before the first framebuffer paints. Only shown in the
|
|
421
469
|
`connecting` UI-state (every other non-connected state has an explicit
|
|
422
470
|
overlay). A spinner + transitional copy makes it feel live. */}
|
|
423
|
-
{uiState === "connecting" && (
|
|
471
|
+
{(uiState === "opening" || uiState === "connecting") && (
|
|
424
472
|
<div className="pointer-events-none absolute inset-0 flex flex-col items-center justify-center gap-3 text-og-fg-subtle">
|
|
425
473
|
<span className="relative flex items-center justify-center">
|
|
426
474
|
<MonitorIcon className="size-8 opacity-30" strokeWidth={1.5} />
|
|
@@ -429,7 +477,9 @@ export function DesktopViewer({
|
|
|
429
477
|
strokeWidth={1.25}
|
|
430
478
|
/>
|
|
431
479
|
</span>
|
|
432
|
-
<span className="text-og-sm">
|
|
480
|
+
<span className="text-og-sm">
|
|
481
|
+
{uiState === "opening" ? "Opening the desktop…" : "Connecting to the desktop…"}
|
|
482
|
+
</span>
|
|
433
483
|
</div>
|
|
434
484
|
)}
|
|
435
485
|
|
|
@@ -507,7 +557,7 @@ function TakeControlCallToAction({
|
|
|
507
557
|
"border-og-border",
|
|
508
558
|
"bg-og-bg/85 backdrop-blur-md",
|
|
509
559
|
"shadow-og-lg",
|
|
510
|
-
"outline-
|
|
560
|
+
"outline-hidden transition-all duration-150 ease-out",
|
|
511
561
|
"focus-visible:ring-2 focus-visible:ring-og-accent focus-visible:ring-offset-2 focus-visible:ring-offset-og-bg",
|
|
512
562
|
disabled
|
|
513
563
|
? "cursor-not-allowed opacity-60"
|
|
@@ -568,7 +618,7 @@ function InControlBar({ shared, onRelease }: { shared: boolean; onRelease: () =>
|
|
|
568
618
|
className={cn(
|
|
569
619
|
"inline-flex items-center gap-1.5 rounded-og-sm border px-2.5 py-1 text-og-xs font-semibold shadow-og-md transition-colors",
|
|
570
620
|
"border-og-accent bg-og-bg/90 text-og-fg backdrop-blur-sm",
|
|
571
|
-
"outline-
|
|
621
|
+
"outline-hidden hover:bg-og-accent hover:text-og-accent-fg focus-visible:ring-2 focus-visible:ring-og-accent",
|
|
572
622
|
)}
|
|
573
623
|
>
|
|
574
624
|
<MonitorIcon className="size-3.5" strokeWidth={2} aria-hidden />
|
|
@@ -630,28 +680,6 @@ function WarmingNotice({ onRetry }: { onRetry?: (() => void) | undefined }) {
|
|
|
630
680
|
);
|
|
631
681
|
}
|
|
632
682
|
|
|
633
|
-
function DefaultConsentGate({ shared, onAccept }: { shared: boolean; onAccept: () => void }) {
|
|
634
|
-
return (
|
|
635
|
-
<div className="max-w-sm rounded-og-lg border border-og-border bg-og-bg p-4 text-center text-og-base text-og-fg">
|
|
636
|
-
<div className="mb-1 font-medium">Watch the live desktop?</div>
|
|
637
|
-
<p className="mb-3 text-og-sm text-og-fg-subtle">
|
|
638
|
-
The desktop pixel stream is <strong>un-redacted</strong> — it can show secrets the agent
|
|
639
|
-
prints on screen.
|
|
640
|
-
{shared
|
|
641
|
-
? " This sandbox is shared: you'll also see other sessions' agents on the same screen."
|
|
642
|
-
: ""}
|
|
643
|
-
</p>
|
|
644
|
-
<button
|
|
645
|
-
type="button"
|
|
646
|
-
onClick={onAccept}
|
|
647
|
-
className="rounded-og-sm bg-og-accent px-3 py-1.5 text-og-sm font-medium text-og-accent-fg pointer-coarse:min-h-11"
|
|
648
|
-
>
|
|
649
|
-
I understand — show the desktop
|
|
650
|
-
</button>
|
|
651
|
-
</div>
|
|
652
|
-
);
|
|
653
|
-
}
|
|
654
|
-
|
|
655
683
|
function defaultNotice(title: string, body: string, onRetry?: () => void): ReactNode {
|
|
656
684
|
return (
|
|
657
685
|
<div className="max-w-sm rounded-og-lg border border-og-border bg-og-bg p-4 text-center text-og-base text-og-fg">
|
|
@@ -24,7 +24,11 @@ import {
|
|
|
24
24
|
import { AlertDialog } from "radix-ui";
|
|
25
25
|
import { VList, type VListHandle } from "virtua";
|
|
26
26
|
import { cn } from "../lib/cn";
|
|
27
|
-
import {
|
|
27
|
+
import {
|
|
28
|
+
type PortalTokenStyle,
|
|
29
|
+
usePortalTokenSource,
|
|
30
|
+
usePortalTokenStyle,
|
|
31
|
+
} from "../lib/use-portal-token-style";
|
|
28
32
|
import { useUnicodeFallbackFonts } from "../lib/use-unicode-fonts";
|
|
29
33
|
import type { FileTreeNode, UseSandboxFilesResult } from "../hooks/use-sandbox-files";
|
|
30
34
|
|
|
@@ -155,10 +159,11 @@ export function FileBrowser({
|
|
|
155
159
|
const [busy, setBusy] = useState(false);
|
|
156
160
|
const [coarsePointer, setCoarsePointer] = useState(false);
|
|
157
161
|
const treeId = useId();
|
|
158
|
-
const
|
|
162
|
+
const container = usePortalTokenSource<HTMLDivElement>();
|
|
163
|
+
const containerRef = container.currentRef;
|
|
159
164
|
const deleteReturnFocusRef = useRef<HTMLElement | null>(null);
|
|
160
165
|
const vlistRef = useRef<VListHandle>(null);
|
|
161
|
-
const portalTokenStyle = usePortalTokenStyle(
|
|
166
|
+
const portalTokenStyle = usePortalTokenStyle(container.source);
|
|
162
167
|
const getTreeElement = useCallback(
|
|
163
168
|
() => (typeof document === "undefined" ? null : document.getElementById(treeId)),
|
|
164
169
|
[treeId],
|
|
@@ -569,6 +574,7 @@ export function FileBrowser({
|
|
|
569
574
|
runDelete,
|
|
570
575
|
supportsMutation,
|
|
571
576
|
coarsePointer,
|
|
577
|
+
containerRef,
|
|
572
578
|
rowIndexByPath,
|
|
573
579
|
treeId,
|
|
574
580
|
],
|
|
@@ -594,7 +600,13 @@ export function FileBrowser({
|
|
|
594
600
|
// Retry lives next to the state it explains (the degraded/empty notice), so do
|
|
595
601
|
// not render a second toolbar retry for the same failure.
|
|
596
602
|
const showToolbar = supportsMutation || result.loading;
|
|
597
|
-
|
|
603
|
+
// Virtua caches the previous visible range when a lazy-expanded subtree is
|
|
604
|
+
// inserted in the middle of a short list. The spacer grows but later root
|
|
605
|
+
// siblings can remain unmounted until a scroll/resize (for example,
|
|
606
|
+
// `.bash_history` vanished after expanding `repositories/<repo>`). Small
|
|
607
|
+
// trees do not benefit from virtualization, so render them directly and keep
|
|
608
|
+
// Virtua only for genuinely large workspaces.
|
|
609
|
+
const usesVirtualTree = !showErrorEmpty && !showEmpty && renderItems.length > 500;
|
|
598
610
|
|
|
599
611
|
// Render a SINGLE node row (no recursion — children are separate flat items the
|
|
600
612
|
// virtualizer renders). Returns the row element; the caller assigns the key.
|
|
@@ -854,7 +866,7 @@ export function FileBrowser({
|
|
|
854
866
|
type="button"
|
|
855
867
|
onClick={() => void result.refresh()}
|
|
856
868
|
disabled={result.loading}
|
|
857
|
-
className="inline-flex min-h-7 shrink-0 items-center gap-1 rounded-og-sm border border-og-border bg-og-surface-1 px-2 font-medium text-og-fg transition-colors hover:border-og-border-strong focus-visible:outline-
|
|
869
|
+
className="inline-flex min-h-7 shrink-0 items-center gap-1 rounded-og-sm border border-og-border bg-og-surface-1 px-2 font-medium text-og-fg transition-colors hover:border-og-border-strong focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-og-accent disabled:cursor-not-allowed disabled:opacity-50 pointer-coarse:min-h-11"
|
|
858
870
|
>
|
|
859
871
|
<RefreshCwIcon
|
|
860
872
|
className={cn("size-3", result.loading && "animate-spin motion-reduce:animate-none")}
|
|
@@ -868,13 +880,7 @@ export function FileBrowser({
|
|
|
868
880
|
{/* The tree itself. The root is a drop target so a node can be moved to "". */}
|
|
869
881
|
{/* biome-ignore lint/a11y/noNoninteractiveTabindex: the tree owns keyboard nav */}
|
|
870
882
|
<div
|
|
871
|
-
ref={
|
|
872
|
-
id={usesVirtualTree ? undefined : treeId}
|
|
873
|
-
role={usesVirtualTree ? undefined : "tree"}
|
|
874
|
-
aria-label={usesVirtualTree ? undefined : "Workspace files"}
|
|
875
|
-
aria-activedescendant={usesVirtualTree ? undefined : activeDescendantId}
|
|
876
|
-
tabIndex={usesVirtualTree ? undefined : 0}
|
|
877
|
-
aria-multiselectable={usesVirtualTree ? undefined : false}
|
|
883
|
+
ref={container.ref}
|
|
878
884
|
onFocus={(event) => {
|
|
879
885
|
if ((event.target as HTMLElement).id === treeId && cursorIndex < 0) {
|
|
880
886
|
const first = flatRows[0];
|
|
@@ -901,7 +907,7 @@ export function FileBrowser({
|
|
|
901
907
|
: undefined
|
|
902
908
|
}
|
|
903
909
|
className={cn(
|
|
904
|
-
"flex min-h-0 min-w-0 flex-1 flex-col p-1
|
|
910
|
+
"flex min-h-0 min-w-0 flex-1 flex-col p-1",
|
|
905
911
|
dragOver === "" && "ring-1 ring-inset ring-og-accent",
|
|
906
912
|
)}
|
|
907
913
|
data-opengeni-file-tree
|
|
@@ -929,6 +935,22 @@ export function FileBrowser({
|
|
|
929
935
|
<div className="p-2 text-og-sm text-og-fg-subtle">
|
|
930
936
|
{emptyState ?? "This directory is empty"}
|
|
931
937
|
</div>
|
|
938
|
+
) : !usesVirtualTree ? (
|
|
939
|
+
<div
|
|
940
|
+
id={treeId}
|
|
941
|
+
role="tree"
|
|
942
|
+
aria-label="Workspace files"
|
|
943
|
+
aria-activedescendant={activeDescendantId}
|
|
944
|
+
aria-multiselectable={false}
|
|
945
|
+
tabIndex={0}
|
|
946
|
+
className="min-h-0 min-w-0 flex-1 overflow-auto outline-hidden focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-og-accent"
|
|
947
|
+
>
|
|
948
|
+
{renderItems.map((item) => (
|
|
949
|
+
<div key={itemKey(item)} className="min-w-0">
|
|
950
|
+
{renderItem(item)}
|
|
951
|
+
</div>
|
|
952
|
+
))}
|
|
953
|
+
</div>
|
|
932
954
|
) : (
|
|
933
955
|
// Virtualized: only the rows in/near the viewport mount, so a 3k-node
|
|
934
956
|
// tree stays responsive. The flat item list already carries the
|
|
@@ -941,7 +963,7 @@ export function FileBrowser({
|
|
|
941
963
|
aria-activedescendant={activeDescendantId}
|
|
942
964
|
aria-multiselectable={false}
|
|
943
965
|
tabIndex={0}
|
|
944
|
-
className="min-h-0 flex-1 outline-
|
|
966
|
+
className="min-h-0 flex-1 outline-hidden focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-og-accent"
|
|
945
967
|
itemSize={coarsePointer ? 44 : 28}
|
|
946
968
|
ssrCount={Math.min(32, renderItems.length)}
|
|
947
969
|
>
|
|
@@ -1041,9 +1063,9 @@ function DeleteDialog({
|
|
|
1041
1063
|
}}
|
|
1042
1064
|
>
|
|
1043
1065
|
<AlertDialog.Portal container={typeof document === "undefined" ? null : document.body}>
|
|
1044
|
-
<AlertDialog.Overlay className="fixed inset-0 z-[100] bg-black/45 backdrop-blur-[2px]" />
|
|
1066
|
+
<AlertDialog.Overlay className="og-root fixed inset-0 z-[100] bg-black/45 backdrop-blur-[2px]" />
|
|
1045
1067
|
<AlertDialog.Content
|
|
1046
|
-
className="og-root fixed left-1/2 top-1/2 z-[101] max-h-[calc(100dvh-2rem)] w-[calc(100%-2rem)] max-w-sm -translate-x-1/2 -translate-y-1/2 overflow-y-auto rounded-og-lg border border-og-border-strong bg-og-surface-1 p-4 text-og-fg shadow-2xl outline-
|
|
1068
|
+
className="og-root fixed left-1/2 top-1/2 z-[101] max-h-[calc(100dvh-2rem)] w-[calc(100%-2rem)] max-w-sm -translate-x-1/2 -translate-y-1/2 overflow-y-auto rounded-og-lg border border-og-border-strong bg-og-surface-1 p-4 text-og-fg shadow-2xl outline-hidden"
|
|
1047
1069
|
style={portalTokenStyle}
|
|
1048
1070
|
onOpenAutoFocus={(event) => {
|
|
1049
1071
|
event.preventDefault();
|
|
@@ -1208,7 +1230,7 @@ function InlineInput({
|
|
|
1208
1230
|
className={cn(
|
|
1209
1231
|
"min-w-0 flex-1 rounded-og-sm border bg-og-bg px-1 py-0 text-og-sm",
|
|
1210
1232
|
"border-og-accent text-og-fg",
|
|
1211
|
-
"outline-
|
|
1233
|
+
"outline-hidden",
|
|
1212
1234
|
)}
|
|
1213
1235
|
/>
|
|
1214
1236
|
</div>
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import type { GeneratedVideoReceipt, VideoArtifactPlaybackSource } from "@opengeni/sdk";
|
|
2
|
+
import { useEffect, useState } from "react";
|
|
3
|
+
import { cn } from "../lib/cn";
|
|
4
|
+
import type { VideoArtifactPlaybackLoader } from "../timeline";
|
|
5
|
+
|
|
6
|
+
export type GeneratedVideoPlayerProps = {
|
|
7
|
+
receipt: GeneratedVideoReceipt;
|
|
8
|
+
loadPlaybackSource: VideoArtifactPlaybackLoader;
|
|
9
|
+
className?: string | undefined;
|
|
10
|
+
label?: string | undefined;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
type SourceState =
|
|
14
|
+
| { kind: "loading" }
|
|
15
|
+
| { kind: "ready"; source: VideoArtifactPlaybackSource }
|
|
16
|
+
| { kind: "error"; message: string };
|
|
17
|
+
|
|
18
|
+
/** Native, Range-based playback. Video bytes never pass through React or the SDK heap. */
|
|
19
|
+
export function GeneratedVideoPlayer({
|
|
20
|
+
receipt,
|
|
21
|
+
loadPlaybackSource,
|
|
22
|
+
className,
|
|
23
|
+
label = "Generated video",
|
|
24
|
+
}: GeneratedVideoPlayerProps) {
|
|
25
|
+
const [retry, setRetry] = useState(0);
|
|
26
|
+
const [state, setState] = useState<SourceState>({ kind: "loading" });
|
|
27
|
+
|
|
28
|
+
useEffect(() => {
|
|
29
|
+
const controller = new AbortController();
|
|
30
|
+
setState({ kind: "loading" });
|
|
31
|
+
void loadPlaybackSource(receipt.artifact.artifactId, controller.signal).then(
|
|
32
|
+
(source) => {
|
|
33
|
+
if (!controller.signal.aborted) setState({ kind: "ready", source });
|
|
34
|
+
},
|
|
35
|
+
(error: unknown) => {
|
|
36
|
+
if (controller.signal.aborted) return;
|
|
37
|
+
setState({
|
|
38
|
+
kind: "error",
|
|
39
|
+
message: error instanceof Error ? error.message : "Playback is unavailable.",
|
|
40
|
+
});
|
|
41
|
+
},
|
|
42
|
+
);
|
|
43
|
+
return () => controller.abort();
|
|
44
|
+
}, [loadPlaybackSource, receipt.artifact.artifactId, retry]);
|
|
45
|
+
|
|
46
|
+
if (state.kind === "loading") {
|
|
47
|
+
return (
|
|
48
|
+
<div
|
|
49
|
+
aria-label={`Loading ${label.toLowerCase()}`}
|
|
50
|
+
className={cn(
|
|
51
|
+
"aspect-video w-full animate-pulse rounded-og-md bg-og-surface-2 motion-reduce:animate-none",
|
|
52
|
+
className,
|
|
53
|
+
)}
|
|
54
|
+
/>
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
if (state.kind === "error") {
|
|
58
|
+
return (
|
|
59
|
+
<div
|
|
60
|
+
role="status"
|
|
61
|
+
className={cn(
|
|
62
|
+
"rounded-og-md border border-og-status-failed/30 bg-og-status-failed/5 px-3 py-2 text-og-sm text-og-status-failed",
|
|
63
|
+
className,
|
|
64
|
+
)}
|
|
65
|
+
>
|
|
66
|
+
{state.message}
|
|
67
|
+
</div>
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return (
|
|
72
|
+
<video
|
|
73
|
+
key={state.source.url}
|
|
74
|
+
aria-label={label}
|
|
75
|
+
className={cn(
|
|
76
|
+
"max-h-[32rem] w-full rounded-og-md bg-black object-contain shadow-sm",
|
|
77
|
+
className,
|
|
78
|
+
)}
|
|
79
|
+
controls
|
|
80
|
+
playsInline
|
|
81
|
+
preload="metadata"
|
|
82
|
+
src={state.source.url}
|
|
83
|
+
onError={() => {
|
|
84
|
+
if (retry === 0) setRetry(1);
|
|
85
|
+
else setState({ kind: "error", message: "Video playback failed." });
|
|
86
|
+
}}
|
|
87
|
+
/>
|
|
88
|
+
);
|
|
89
|
+
}
|
|
@@ -559,7 +559,7 @@ function QuestionControls({
|
|
|
559
559
|
aria-describedby={describedBy}
|
|
560
560
|
autoFocus={autoFocus}
|
|
561
561
|
rows={2}
|
|
562
|
-
className="min-h-14 w-full resize-y rounded-og-md border border-og-border bg-og-surface-1 px-3 py-2 text-og-sm text-og-fg outline-
|
|
562
|
+
className="min-h-14 w-full resize-y rounded-og-md border border-og-border bg-og-surface-1 px-3 py-2 text-og-sm text-og-fg outline-hidden placeholder:text-og-fg-subtle focus:border-og-accent"
|
|
563
563
|
/>
|
|
564
564
|
) : (
|
|
565
565
|
<div
|
|
@@ -651,7 +651,7 @@ function QuestionControls({
|
|
|
651
651
|
other: event.target.value,
|
|
652
652
|
}))
|
|
653
653
|
}
|
|
654
|
-
className="mt-1.5 w-full rounded-og-sm border border-og-border bg-og-surface-1 px-2 py-1.5 text-og-sm text-og-fg outline-
|
|
654
|
+
className="mt-1.5 w-full rounded-og-sm border border-og-border bg-og-surface-1 px-2 py-1.5 text-og-sm text-og-fg outline-hidden focus:border-og-accent disabled:opacity-50"
|
|
655
655
|
/>
|
|
656
656
|
</span>
|
|
657
657
|
</label>
|
|
@@ -81,7 +81,7 @@ export function MachineCard({
|
|
|
81
81
|
"og-root group relative flex flex-col gap-3 overflow-hidden rounded-og-lg border border-og-border",
|
|
82
82
|
"bg-og-surface-1 p-4 shadow-og-sm transition-colors",
|
|
83
83
|
openable &&
|
|
84
|
-
"cursor-pointer hover:border-og-border-strong focus-visible:border-og-accent/60 focus-visible:outline-
|
|
84
|
+
"cursor-pointer hover:border-og-border-strong focus-visible:border-og-accent/60 focus-visible:outline-hidden",
|
|
85
85
|
machine.active && "border-og-accent/40",
|
|
86
86
|
className,
|
|
87
87
|
)}
|
|
@@ -6,6 +6,7 @@ export const MACHINE_INPUT_META: Record<MachineInputMember["kind"], string> = {
|
|
|
6
6
|
agent_message: "Agent update",
|
|
7
7
|
agent_steer_instruction: "Agent direction",
|
|
8
8
|
child_terminal_result: "Agent finished",
|
|
9
|
+
media_generation_result: "Video update",
|
|
9
10
|
};
|
|
10
11
|
|
|
11
12
|
/**
|
|
@@ -32,6 +33,8 @@ export function machineInputBatchLabel(members: readonly MachineInputMember[]):
|
|
|
32
33
|
return n === 1 ? "Agent update" : `${n} agent updates`;
|
|
33
34
|
case "agent_steer_instruction":
|
|
34
35
|
return n === 1 ? "Agent direction" : `${n} agent directions`;
|
|
36
|
+
case "media_generation_result":
|
|
37
|
+
return n === 1 ? "Video ready" : `${n} video updates`;
|
|
35
38
|
}
|
|
36
39
|
}
|
|
37
40
|
const parts = [...counts.entries()].map(([kind, count]) => {
|