@opengeni/react 0.48.0 → 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 +90 -2
- 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-FWXGSNXT.js → chunk-724BW4TW.js} +585 -34
- 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-I4MPFDRP.js → chunk-FWS3KC3Z.js} +3 -3
- 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-XCNTXWBB.js → chunk-JAGDUCZQ.js} +854 -340
- package/dist/chunk-JAGDUCZQ.js.map +1 -0
- package/dist/{chunk-LHLYWG5R.js → chunk-JR3QFY6B.js} +413 -37
- 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-PEI6RANI.js +3234 -0
- package/dist/chunk-PEI6RANI.js.map +1 -0
- package/dist/{chunk-NJ7M2B6Z.js → chunk-ROIRTM37.js} +18 -10
- package/dist/{chunk-NJ7M2B6Z.js.map → chunk-ROIRTM37.js.map} +1 -1
- package/dist/{chunk-O4VXOBGF.js → chunk-WSK7G5LE.js} +70 -2
- package/dist/chunk-WSK7G5LE.js.map +1 -0
- package/dist/{chunk-OL6QVXGF.js → chunk-X4S56Y2H.js} +27 -7
- package/dist/{chunk-OL6QVXGF.js.map → chunk-X4S56Y2H.js.map} +1 -1
- 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 +8 -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 +2 -2
- package/dist/components/composer.d.ts +17 -17
- 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 +3 -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 +2 -2
- package/dist/components/markdown.d.ts +1 -1
- package/dist/components/message-timeline.d.ts +12 -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 +3 -3
- package/dist/components/sandbox-terminal.d.ts +21 -4
- package/dist/components/sandbox-workspace.d.ts +25 -9
- 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 +1 -1
- package/dist/composer.d.ts +11 -11
- package/dist/composer.js +5 -5
- package/dist/hooks/internal.d.ts +1 -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 +2 -2
- 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 +1 -1
- 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 +4 -4
- 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 +6 -4
- package/dist/hooks/use-sandbox-git.d.ts +6 -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-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 +1 -1
- 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 -155
- package/dist/index.js +906 -462
- 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/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/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-U5JFG7HT.js → queue-surface-implementation-2KVJTYXH.js} +100 -48
- 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 +6 -6
- package/dist/realtime.d.ts +4 -4
- package/dist/realtime.js +3 -3
- package/dist/session-context.d.ts +19 -1
- package/dist/session-ui.d.ts +14 -14
- package/dist/session-ui.js +5 -4
- package/dist/session.d.ts +28 -28
- package/dist/session.js +5 -5
- package/dist/timeline/activity-rail.d.ts +5 -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 +4 -1
- package/dist/timeline/projection.d.ts +3 -3
- package/dist/timeline/registry.d.ts +8 -2
- 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 +39 -3
- 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/package.json +36 -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 +87 -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 +13 -0
- package/src/components/code-editor.tsx +89 -30
- package/src/components/composer-transcription-control.tsx +10 -6
- package/src/components/computer-viewer.tsx +1139 -0
- package/src/components/desktop-viewer.tsx +101 -73
- package/src/components/generated-video-player.tsx +89 -0
- package/src/components/interactive-workbench-viewers.ts +2 -0
- package/src/components/machine-input-display.ts +3 -0
- package/src/components/message-timeline.tsx +289 -32
- package/src/components/queue-surface-implementation.tsx +116 -60
- package/src/components/sandbox-files.tsx +2 -2
- package/src/components/sandbox-terminal.tsx +106 -41
- package/src/components/sandbox-workspace.tsx +215 -87
- package/src/components/session-chrome.tsx +53 -13
- 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/workbench-changes.tsx +188 -31
- 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-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-machine-chip.ts +5 -3
- package/src/hooks/use-machines.ts +33 -3
- package/src/hooks/use-packs.ts +51 -0
- package/src/hooks/use-sandbox-files.ts +98 -55
- package/src/hooks/use-sandbox-git.ts +93 -38
- package/src/hooks/use-sandbox-terminal.ts +3 -2
- package/src/hooks/use-session-capabilities.ts +59 -46
- package/src/hooks/use-terminal-stream.ts +102 -23
- package/src/hooks/use-video-artifact-playback.ts +28 -0
- package/src/hooks/use-workspace-edit.ts +2 -1
- package/src/index.ts +10 -0
- package/src/interaction.ts +71 -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/session-context.ts +90 -0
- package/src/timeline/activity-rail.tsx +19 -3
- package/src/timeline/index.ts +3 -0
- package/src/timeline/parsers.ts +14 -1
- package/src/timeline/projection.ts +211 -14
- package/src/timeline/registry.ts +13 -1
- package/src/timeline/screenshot-lightbox.tsx +19 -9
- package/src/timeline/shared.tsx +56 -8
- package/src/timeline/tool-renderers.tsx +303 -50
- package/src/timeline/types.ts +55 -3
- package/styles/compiled.css +248 -15
- package/styles/index.css +15 -0
- package/styles/responsive.css +13 -1
- package/dist/chunk-EKZH6IDG.js.map +0 -1
- package/dist/chunk-FWXGSNXT.js.map +0 -1
- package/dist/chunk-LHLYWG5R.js.map +0 -1
- package/dist/chunk-O4VXOBGF.js.map +0 -1
- package/dist/chunk-XCNTXWBB.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-U5JFG7HT.js.map +0 -1
- /package/dist/{chunk-I4MPFDRP.js.map → chunk-FWS3KC3Z.js.map} +0 -0
|
@@ -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
|
|
|
@@ -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">
|
|
@@ -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
|
+
}
|
|
@@ -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]) => {
|