@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
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
// Changes — review-first git: turn-end capture (cold) or live diff (warm)
|
|
5
5
|
// Files — tree + inline editor (capture-backed cold, live warm)
|
|
6
6
|
// Terminal — interactive xterm wired to the box PTY (Channel-A projection)
|
|
7
|
-
//
|
|
7
|
+
// Browser — workspace-wide BrowserSessions, tabs, live frames, and input
|
|
8
|
+
// Computer — workspace ComputerSessions: native targets, frames, and input
|
|
8
9
|
// plus a machine-state chip in the dock header (the one truthful live/waking/
|
|
9
10
|
// offline indicator) and any host-injected extra tabs (Run/Debug in apps/web).
|
|
10
11
|
//
|
|
@@ -13,13 +14,23 @@
|
|
|
13
14
|
// (no `sonner` import), and every surface renders with package primitives + og
|
|
14
15
|
// tokens only. `apps/web` consumes this through the exact public surface an
|
|
15
16
|
// external embedder uses — that is criterion F1.
|
|
16
|
-
import {
|
|
17
|
+
import {
|
|
18
|
+
lazy,
|
|
19
|
+
type ReactNode,
|
|
20
|
+
Suspense,
|
|
21
|
+
useCallback,
|
|
22
|
+
useEffect,
|
|
23
|
+
useMemo,
|
|
24
|
+
useRef,
|
|
25
|
+
useState,
|
|
26
|
+
} from "react";
|
|
17
27
|
import type { SessionEvent } from "@opengeni/sdk";
|
|
18
28
|
import {
|
|
19
29
|
CircleCheckIcon,
|
|
20
30
|
CpuIcon,
|
|
21
31
|
FileCode2Icon,
|
|
22
32
|
GitCompareArrowsIcon,
|
|
33
|
+
Globe2Icon,
|
|
23
34
|
LoaderCircleIcon,
|
|
24
35
|
MonitorIcon,
|
|
25
36
|
RefreshCwIcon,
|
|
@@ -30,6 +41,9 @@ import {
|
|
|
30
41
|
import { type ClientOverride, useOpenGeni } from "../provider";
|
|
31
42
|
import { cn } from "../lib/cn";
|
|
32
43
|
import { xtermThemeFromTokens } from "../lib/xterm-theme";
|
|
44
|
+
import { sandboxAcceptsLiveIo } from "../lib/sandbox-liveness";
|
|
45
|
+
import type { BrowserFrameWebSocketFactory } from "../hooks/use-browser-frame-stream";
|
|
46
|
+
import type { ComputerFrameWebSocketFactory } from "../hooks/use-computer-frame-stream";
|
|
33
47
|
import { useSessionCapabilities } from "../hooks/use-session-capabilities";
|
|
34
48
|
import { useSandboxFiles } from "../hooks/use-sandbox-files";
|
|
35
49
|
import {
|
|
@@ -45,9 +59,18 @@ import type { MachineView } from "../types/machines";
|
|
|
45
59
|
import { SandboxFiles } from "./sandbox-files";
|
|
46
60
|
import { WorkbenchChanges } from "./workbench-changes";
|
|
47
61
|
import { SandboxTerminal, type XtermTheme } from "./sandbox-terminal";
|
|
48
|
-
import { DesktopViewer } from "./desktop-viewer";
|
|
49
62
|
import { WorkspaceDock, type WorkspaceDockProps, type WorkspaceTab } from "./workspace-dock";
|
|
50
63
|
|
|
64
|
+
const LazyBrowserViewer = lazy(async () => {
|
|
65
|
+
const viewers = await import("./interactive-workbench-viewers");
|
|
66
|
+
return { default: viewers.BrowserViewer };
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
const LazyComputerViewer = lazy(async () => {
|
|
70
|
+
const viewers = await import("./interactive-workbench-viewers");
|
|
71
|
+
return { default: viewers.ComputerViewer };
|
|
72
|
+
});
|
|
73
|
+
|
|
51
74
|
/** A host-routed notification (replaces the app-only `sonner` toast coupling). */
|
|
52
75
|
export type WorkspaceNotification = { kind: "error" | "info"; message: string };
|
|
53
76
|
|
|
@@ -55,6 +78,7 @@ export type WorkspaceNotification = { kind: "error" | "info"; message: string };
|
|
|
55
78
|
export const WORKBENCH_TAB_CHANGES = "changes";
|
|
56
79
|
export const WORKBENCH_TAB_FILES = "files";
|
|
57
80
|
export const WORKBENCH_TAB_TERMINAL = "terminal";
|
|
81
|
+
export const WORKBENCH_TAB_BROWSER = "browser";
|
|
58
82
|
export const WORKBENCH_TAB_DESKTOP = "desktop";
|
|
59
83
|
|
|
60
84
|
/**
|
|
@@ -66,6 +90,7 @@ export const WORKBENCH_SURFACES = [
|
|
|
66
90
|
WORKBENCH_TAB_CHANGES,
|
|
67
91
|
WORKBENCH_TAB_FILES,
|
|
68
92
|
WORKBENCH_TAB_TERMINAL,
|
|
93
|
+
WORKBENCH_TAB_BROWSER,
|
|
69
94
|
WORKBENCH_TAB_DESKTOP,
|
|
70
95
|
] as const;
|
|
71
96
|
|
|
@@ -110,6 +135,18 @@ function WorkbenchTabLabel({ icon, children }: { icon: ReactNode; children: Reac
|
|
|
110
135
|
);
|
|
111
136
|
}
|
|
112
137
|
|
|
138
|
+
function WorkbenchSurfaceLoading({ name }: { name: "Browser" | "Computer" }) {
|
|
139
|
+
return (
|
|
140
|
+
<CenteredState
|
|
141
|
+
icon={
|
|
142
|
+
<LoaderCircleIcon className="size-5 animate-spin motion-reduce:animate-none" aria-hidden />
|
|
143
|
+
}
|
|
144
|
+
>
|
|
145
|
+
<p className="text-og-sm font-medium text-og-fg">Opening {name}</p>
|
|
146
|
+
</CenteredState>
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
|
|
113
150
|
/**
|
|
114
151
|
* Decide a workspace tab from an already-local event log: the newest
|
|
115
152
|
* `workspace.revision.captured` announce carries the change surface stats, so
|
|
@@ -134,7 +171,19 @@ export function initialWorkspaceTab(
|
|
|
134
171
|
bestSeq = event.sequence;
|
|
135
172
|
bestFileCount = typeof stats?.fileCount === "number" ? stats.fileCount : 0;
|
|
136
173
|
}
|
|
137
|
-
|
|
174
|
+
if (bestFileCount > 0) return WORKBENCH_TAB_CHANGES;
|
|
175
|
+
|
|
176
|
+
let latestGitSeq = -1;
|
|
177
|
+
let committedOnly = false;
|
|
178
|
+
for (const event of events ?? []) {
|
|
179
|
+
if (event.type !== "git.changed" || event.sequence > bestSeq) continue;
|
|
180
|
+
if (event.sequence <= latestGitSeq) continue;
|
|
181
|
+
const payload = event.payload as { ahead?: number; reason?: string } | null;
|
|
182
|
+
latestGitSeq = event.sequence;
|
|
183
|
+
committedOnly =
|
|
184
|
+
(typeof payload?.ahead === "number" && payload.ahead > 0) || payload?.reason === "commit";
|
|
185
|
+
}
|
|
186
|
+
return committedOnly ? WORKBENCH_TAB_CHANGES : WORKBENCH_TAB_FILES;
|
|
138
187
|
}
|
|
139
188
|
|
|
140
189
|
/**
|
|
@@ -186,7 +235,7 @@ export type UseSandboxWorkspaceTabsOptions = ClientOverride & {
|
|
|
186
235
|
/** Live event log (usually `useSessionEvents().events`). */
|
|
187
236
|
events: SessionEvent[];
|
|
188
237
|
/**
|
|
189
|
-
* Built-in surfaces this host wants to expose. Omit for all
|
|
238
|
+
* Built-in surfaces this host wants to expose. Omit for all five. A disabled
|
|
190
239
|
* surface is behaviorally dormant: its data hooks, warm intents, stream
|
|
191
240
|
* attachment, and navigation callbacks are not activated.
|
|
192
241
|
*/
|
|
@@ -199,6 +248,10 @@ export type UseSandboxWorkspaceTabsOptions = ClientOverride & {
|
|
|
199
248
|
/** Host-routed notifications (mutation errors, desktop-consent failures). The
|
|
200
249
|
* package never imports a toast library — the host decides how to surface. */
|
|
201
250
|
onNotify?: ((notification: WorkspaceNotification) => void) | undefined;
|
|
251
|
+
/** Alternate frame transport for non-browser runtimes and deterministic tests. */
|
|
252
|
+
browserWebSocketFactory?: BrowserFrameWebSocketFactory | undefined;
|
|
253
|
+
/** Alternate Computer frame transport for non-browser runtimes and deterministic tests. */
|
|
254
|
+
computerWebSocketFactory?: ComputerFrameWebSocketFactory | undefined;
|
|
202
255
|
/** File requested by a Changes guard. The Files surface defers the read until
|
|
203
256
|
* the sandbox is live, then reveals this path. */
|
|
204
257
|
requestedFilePath?: string | null | undefined;
|
|
@@ -207,10 +260,14 @@ export type UseSandboxWorkspaceTabsOptions = ClientOverride & {
|
|
|
207
260
|
requestedFileRequestId?: string | number | null | undefined;
|
|
208
261
|
/** Route a guarded diff into the host's Files tab. */
|
|
209
262
|
onOpenFile?: ((path: string) => void) | undefined;
|
|
263
|
+
/** Navigate the host dock to one exact linked ComputerSession. */
|
|
264
|
+
onOpenComputerSession?: ((computerSessionId: string) => void) | undefined;
|
|
265
|
+
requestedComputerSessionId?: string | null | undefined;
|
|
266
|
+
requestedComputerRequestId?: string | number | null | undefined;
|
|
210
267
|
};
|
|
211
268
|
|
|
212
269
|
export type UseSandboxWorkspaceTabsResult = {
|
|
213
|
-
/** Changes | Files | Terminal | Desktop (capability-gated where noted). */
|
|
270
|
+
/** Changes | Files | Terminal | Browser | Desktop (capability-gated where noted). */
|
|
214
271
|
tabs: WorkspaceTab[];
|
|
215
272
|
/** The source-driven default tab: Changes when the first authoritative capture
|
|
216
273
|
* or live Git result has changes, else Files (a host `initialTab` overrides).
|
|
@@ -223,7 +280,6 @@ export type UseSandboxWorkspaceTabsResult = {
|
|
|
223
280
|
|
|
224
281
|
type SessionWarmIntents = {
|
|
225
282
|
sessionId: string;
|
|
226
|
-
watchDesktop: boolean;
|
|
227
283
|
warmTerminal: boolean;
|
|
228
284
|
warmFiles: boolean;
|
|
229
285
|
};
|
|
@@ -231,7 +287,6 @@ type SessionWarmIntents = {
|
|
|
231
287
|
function emptyWarmIntents(sessionId: string): SessionWarmIntents {
|
|
232
288
|
return {
|
|
233
289
|
sessionId,
|
|
234
|
-
watchDesktop: false,
|
|
235
290
|
warmTerminal: false,
|
|
236
291
|
warmFiles: false,
|
|
237
292
|
};
|
|
@@ -240,14 +295,26 @@ function emptyWarmIntents(sessionId: string): SessionWarmIntents {
|
|
|
240
295
|
/**
|
|
241
296
|
* Build the workbench tabs + the machine-chip model for one session. This is the
|
|
242
297
|
* dock "brain": capability negotiation, capture-backed cold reads, prewarm
|
|
243
|
-
* flags, desktop
|
|
298
|
+
* flags, automatic desktop acknowledgment, and the xterm theme observer — all
|
|
299
|
+
* package-local.
|
|
244
300
|
*/
|
|
245
301
|
export function useSandboxWorkspaceTabs(
|
|
246
302
|
options: UseSandboxWorkspaceTabsOptions,
|
|
247
303
|
): UseSandboxWorkspaceTabsResult {
|
|
248
304
|
const { client, workspaceId } = useOpenGeni(options);
|
|
249
|
-
const {
|
|
250
|
-
|
|
305
|
+
const {
|
|
306
|
+
sessionId,
|
|
307
|
+
events,
|
|
308
|
+
onNotify,
|
|
309
|
+
browserWebSocketFactory,
|
|
310
|
+
computerWebSocketFactory,
|
|
311
|
+
requestedFilePath,
|
|
312
|
+
requestedFileRequestId,
|
|
313
|
+
onOpenFile,
|
|
314
|
+
onOpenComputerSession,
|
|
315
|
+
requestedComputerSessionId,
|
|
316
|
+
requestedComputerRequestId,
|
|
317
|
+
} = options;
|
|
251
318
|
const initialTab = options.initialTab ?? null;
|
|
252
319
|
const activeTab = options.activeTab ?? initialTab;
|
|
253
320
|
const requestedSurfaces = options.surfaces ?? WORKBENCH_SURFACES;
|
|
@@ -255,9 +322,24 @@ export function useSandboxWorkspaceTabs(
|
|
|
255
322
|
const changesEnabled = surfaceSet.has(WORKBENCH_TAB_CHANGES);
|
|
256
323
|
const filesEnabled = surfaceSet.has(WORKBENCH_TAB_FILES);
|
|
257
324
|
const terminalEnabled = surfaceSet.has(WORKBENCH_TAB_TERMINAL);
|
|
325
|
+
const browserEnabled = surfaceSet.has(WORKBENCH_TAB_BROWSER);
|
|
258
326
|
const desktopEnabled = surfaceSet.has(WORKBENCH_TAB_DESKTOP);
|
|
327
|
+
const createLinkedComputer = useCallback(
|
|
328
|
+
async (name: string) => {
|
|
329
|
+
const response = await client.createComputerSession(workspaceId, {
|
|
330
|
+
operationId: crypto.randomUUID(),
|
|
331
|
+
sessionId,
|
|
332
|
+
name,
|
|
333
|
+
});
|
|
334
|
+
return {
|
|
335
|
+
id: response.session.id,
|
|
336
|
+
placement: response.session.placement,
|
|
337
|
+
};
|
|
338
|
+
},
|
|
339
|
+
[client, sessionId, workspaceId],
|
|
340
|
+
);
|
|
259
341
|
const workspaceDataEnabled = changesEnabled || filesEnabled;
|
|
260
|
-
const
|
|
342
|
+
const machineSurfaceEnabled = workspaceDataEnabled || terminalEnabled || desktopEnabled;
|
|
261
343
|
// Do not speculatively activate both remote workspace surfaces while the dock
|
|
262
344
|
// is still resolving its selected tab. Modal calls cannot be cancelled after
|
|
263
345
|
// dispatch, so that transient state otherwise creates an aborted duplicate
|
|
@@ -267,17 +349,17 @@ export function useSandboxWorkspaceTabs(
|
|
|
267
349
|
const filesActive = resolvedActiveTab === WORKBENCH_TAB_FILES;
|
|
268
350
|
const surfaceIdentity = WORKBENCH_SURFACES.filter((surface) => surfaceSet.has(surface)).join(",");
|
|
269
351
|
|
|
270
|
-
// The
|
|
352
|
+
// The two box-warming INTENTS, each off by default and each
|
|
271
353
|
// flipped true by a genuine user action (never on mount, never on a passive
|
|
272
|
-
// capture glance):
|
|
273
|
-
//
|
|
354
|
+
// capture glance): terminal engagement (`onActivate`) and a deliberate
|
|
355
|
+
// live-file open/edit in Files. Browsing capture-served
|
|
274
356
|
// Changes/Files warms nothing — that is the whole point of Refinement 1.
|
|
275
357
|
const [storedWarmIntents, setStoredWarmIntents] = useState<SessionWarmIntents>(() =>
|
|
276
358
|
emptyWarmIntents(sessionId),
|
|
277
359
|
);
|
|
278
360
|
const warmIntents =
|
|
279
361
|
storedWarmIntents.sessionId === sessionId ? storedWarmIntents : emptyWarmIntents(sessionId);
|
|
280
|
-
const {
|
|
362
|
+
const { warmTerminal, warmFiles } = warmIntents;
|
|
281
363
|
const requestWarmIntent = useCallback(
|
|
282
364
|
(intent: Exclude<keyof SessionWarmIntents, "sessionId">) => {
|
|
283
365
|
setStoredWarmIntents((previous) => {
|
|
@@ -294,7 +376,7 @@ export function useSandboxWorkspaceTabs(
|
|
|
294
376
|
workspaceId,
|
|
295
377
|
sessionId,
|
|
296
378
|
pollIntervalMs: 8000,
|
|
297
|
-
enabled:
|
|
379
|
+
enabled: machineSurfaceEnabled,
|
|
298
380
|
});
|
|
299
381
|
const activeMachine: MachineView | null =
|
|
300
382
|
machines.machines.find((m) => m.sandboxId === machines.activeSandboxId) ??
|
|
@@ -303,8 +385,8 @@ export function useSandboxWorkspaceTabs(
|
|
|
303
385
|
|
|
304
386
|
const caps = useSessionCapabilities(sessionId, {
|
|
305
387
|
events,
|
|
306
|
-
enabled:
|
|
307
|
-
attachDesktop:
|
|
388
|
+
enabled: machineSurfaceEnabled,
|
|
389
|
+
attachDesktop: false,
|
|
308
390
|
attachTerminal: terminalEnabled && warmTerminal,
|
|
309
391
|
// Explicit live-file intent only — NOT "the Files tab is open". A cold edit
|
|
310
392
|
// or guarded-file open wakes the box; a glance at the tree/diff does not.
|
|
@@ -312,6 +394,11 @@ export function useSandboxWorkspaceTabs(
|
|
|
312
394
|
});
|
|
313
395
|
const capabilities = caps.capabilities;
|
|
314
396
|
const liveness = capabilities?.liveness;
|
|
397
|
+
// A missing capability document is not evidence that the sandbox is live.
|
|
398
|
+
// Pass explicit null to live-I/O hooks so fast capture/event hydration cannot
|
|
399
|
+
// exploit their legacy "liveness omitted" compatibility path while negotiation
|
|
400
|
+
// is still pending.
|
|
401
|
+
const liveIoLiveness = liveness ?? null;
|
|
315
402
|
const fileSystemOn = capabilities?.FileSystem.available ?? false;
|
|
316
403
|
// The FS is writable only when it's live AND not read-only. A self-hosted box
|
|
317
404
|
// that's offline (or any read-only advertisement) or a capture-served cold tree
|
|
@@ -323,26 +410,23 @@ export function useSandboxWorkspaceTabs(
|
|
|
323
410
|
// Mutations are live-only. A capture is a review artifact, never a writable
|
|
324
411
|
// surrogate for a sleeping machine; the user must deliberately open the live
|
|
325
412
|
// workspace before create/rename/delete/edit controls appear.
|
|
326
|
-
const filesEditable =
|
|
327
|
-
fileSystemOn && !fsReadOnly && (liveness === "warm" || liveness === "draining");
|
|
413
|
+
const filesEditable = fileSystemOn && !fsReadOnly && sandboxAcceptsLiveIo(liveness);
|
|
328
414
|
const gitOn = capabilities?.Git.available ?? false;
|
|
329
415
|
const terminalOn = terminalEnabled && (capabilities?.Terminal.transport ?? null) !== null;
|
|
330
|
-
//
|
|
331
|
-
//
|
|
332
|
-
//
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
416
|
+
// A descriptor-only pty-ws cell intentionally has no short-lived URL until
|
|
417
|
+
// terminal intent acquires one. Do not mistake that grant boundary for a
|
|
418
|
+
// legacy HTTP PTY and eagerly open a second terminal. The fallback exists only
|
|
419
|
+
// for a genuinely firehose-transport backend with no typed degradation.
|
|
420
|
+
const ptyCapable =
|
|
421
|
+
warmTerminal &&
|
|
422
|
+
(capabilities?.Terminal.ptyCapable ?? false) &&
|
|
423
|
+
capabilities?.Terminal.transport === "sse-events" &&
|
|
424
|
+
capabilities?.Terminal.reason === null;
|
|
336
425
|
const terminal = useSandboxTerminal(sessionId, {
|
|
337
426
|
events: terminalEnabled ? events : [],
|
|
338
427
|
interactive: terminalEnabled && ptyCapable,
|
|
339
428
|
liveness,
|
|
340
429
|
});
|
|
341
|
-
const desktopAdvertised =
|
|
342
|
-
desktopEnabled &&
|
|
343
|
-
((capabilities?.DesktopStream.transport ?? null) !== null ||
|
|
344
|
-
capabilities?.DesktopStream.reason === "lease_cold");
|
|
345
|
-
|
|
346
430
|
// Lazy provisioning (#315) creates the box mid-turn on the first sandbox tool
|
|
347
431
|
// call, emitting sandbox.provision started→completed/failed on the live stream.
|
|
348
432
|
// The on-demand resting hook rests without polling, so when the box warms the
|
|
@@ -369,7 +453,7 @@ export function useSandboxWorkspaceTabs(
|
|
|
369
453
|
enabled: workspaceDataEnabled,
|
|
370
454
|
});
|
|
371
455
|
const captureAvailable = captureState.available;
|
|
372
|
-
const liveWorkspaceExpected = liveness
|
|
456
|
+
const liveWorkspaceExpected = sandboxAcceptsLiveIo(liveness);
|
|
373
457
|
// Capability negotiation and capture resolution are independent requests. A
|
|
374
458
|
// cold capability document can win that race by a few milliseconds; enabling
|
|
375
459
|
// the leaf hooks in that window would make them interpret `capture: null` as a
|
|
@@ -407,7 +491,7 @@ export function useSandboxWorkspaceTabs(
|
|
|
407
491
|
enabled: filesEnabled && (captureAvailable || (fileSystemOn && liveWorkspaceExpected)),
|
|
408
492
|
active: filesActive,
|
|
409
493
|
repoPaths,
|
|
410
|
-
liveness,
|
|
494
|
+
liveness: liveIoLiveness,
|
|
411
495
|
capture: captureState.capture,
|
|
412
496
|
// A reverted optimistic mutation (e.g. a 409 rename collision) surfaces as a
|
|
413
497
|
// host notification — the tree silently rolls back, the user sees why.
|
|
@@ -451,7 +535,7 @@ export function useSandboxWorkspaceTabs(
|
|
|
451
535
|
// dormant, avoiding the old duplicate full-tree read.
|
|
452
536
|
active: changesActive || resolvedActiveTab === null,
|
|
453
537
|
repoPaths,
|
|
454
|
-
liveness,
|
|
538
|
+
liveness: liveIoLiveness,
|
|
455
539
|
comparison: changesComparison,
|
|
456
540
|
capture: changesComparison === "staged" ? null : captureState.capture,
|
|
457
541
|
});
|
|
@@ -484,30 +568,6 @@ export function useSandboxWorkspaceTabs(
|
|
|
484
568
|
return () => observer.disconnect();
|
|
485
569
|
}, [terminalEnabled]);
|
|
486
570
|
|
|
487
|
-
async function acknowledgeAndWatch() {
|
|
488
|
-
try {
|
|
489
|
-
const shared = capabilities?.DesktopStream.shared ?? false;
|
|
490
|
-
await client.acknowledgeStream(workspaceId, sessionId, {
|
|
491
|
-
acknowledgeUnredacted: true,
|
|
492
|
-
acknowledgeShared: shared,
|
|
493
|
-
});
|
|
494
|
-
requestWarmIntent("watchDesktop");
|
|
495
|
-
caps.renegotiate();
|
|
496
|
-
} catch (error) {
|
|
497
|
-
onNotify?.({
|
|
498
|
-
kind: "error",
|
|
499
|
-
message: `Could not start the desktop stream: ${error instanceof Error ? error.message : String(error)}`,
|
|
500
|
-
});
|
|
501
|
-
}
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
// Re-warm WITHOUT re-acknowledging: the consent was already recorded, only the
|
|
505
|
-
// box drained back to cold. Idempotent — the viewer auto-warm de-dupes.
|
|
506
|
-
function rewarmDesktop() {
|
|
507
|
-
if (!watchDesktop) requestWarmIntent("watchDesktop");
|
|
508
|
-
caps.renegotiate();
|
|
509
|
-
}
|
|
510
|
-
|
|
511
571
|
const dirtyCount = git.diff.length;
|
|
512
572
|
|
|
513
573
|
// The one truthful machine indicator, derived from the live capability/liveness
|
|
@@ -517,10 +577,7 @@ export function useSandboxWorkspaceTabs(
|
|
|
517
577
|
capabilitiesState: caps.state,
|
|
518
578
|
activeMachineState: activeMachine?.state ?? null,
|
|
519
579
|
activeIsSelfhosted: activeMachine?.kind === "selfhosted",
|
|
520
|
-
wantsWarm:
|
|
521
|
-
(terminalEnabled && warmTerminal) ||
|
|
522
|
-
(desktopEnabled && watchDesktop) ||
|
|
523
|
-
(workspaceDataEnabled && warmFiles),
|
|
580
|
+
wantsWarm: (terminalEnabled && warmTerminal) || (workspaceDataEnabled && warmFiles),
|
|
524
581
|
capturedAt: captureState.capturedAt,
|
|
525
582
|
});
|
|
526
583
|
const workspaceWaking = chip.state === "waking";
|
|
@@ -543,7 +600,11 @@ export function useSandboxWorkspaceTabs(
|
|
|
543
600
|
}
|
|
544
601
|
const captureIsAuthoritative =
|
|
545
602
|
!liveWorkspaceExpected && (liveness !== undefined || caps.error !== null);
|
|
546
|
-
const
|
|
603
|
+
const captureHasChanges =
|
|
604
|
+
(captureState.fileCount ?? 0) > 0 ||
|
|
605
|
+
(captureState.capture?.repos.some((repo) => (repo.branchDiff?.length ?? 0) > 0) ?? false);
|
|
606
|
+
const captureUnavailable =
|
|
607
|
+
(captureState.fileCount === 0 && !captureHasChanges) || captureState.error !== null;
|
|
547
608
|
const implicitBranchFallbackPending =
|
|
548
609
|
changesComparison === "branch" &&
|
|
549
610
|
git.error !== null &&
|
|
@@ -563,7 +624,7 @@ export function useSandboxWorkspaceTabs(
|
|
|
563
624
|
captureState.available
|
|
564
625
|
) {
|
|
565
626
|
defaultTabRef.current.value = sourceDrivenDefaultTab(
|
|
566
|
-
|
|
627
|
+
captureHasChanges,
|
|
567
628
|
changesEnabled,
|
|
568
629
|
filesEnabled,
|
|
569
630
|
);
|
|
@@ -586,7 +647,7 @@ export function useSandboxWorkspaceTabs(
|
|
|
586
647
|
// A warm live read failed but the durable capture is intact: retain an
|
|
587
648
|
// immediate, deterministic review surface instead of hanging unresolved.
|
|
588
649
|
defaultTabRef.current.value = sourceDrivenDefaultTab(
|
|
589
|
-
|
|
650
|
+
captureHasChanges,
|
|
590
651
|
changesEnabled,
|
|
591
652
|
filesEnabled,
|
|
592
653
|
);
|
|
@@ -664,7 +725,7 @@ export function useSandboxWorkspaceTabs(
|
|
|
664
725
|
...(requestedFileRequestId !== null && requestedFileRequestId !== undefined
|
|
665
726
|
? { requestedPathRequestId: requestedFileRequestId }
|
|
666
727
|
: {}),
|
|
667
|
-
requestedPathReady: liveness
|
|
728
|
+
requestedPathReady: sandboxAcceptsLiveIo(liveness),
|
|
668
729
|
}
|
|
669
730
|
: {})}
|
|
670
731
|
// Ordinary capture browsing does not warm a box. The first edit — or an
|
|
@@ -683,6 +744,7 @@ export function useSandboxWorkspaceTabs(
|
|
|
683
744
|
content: (
|
|
684
745
|
<div className="h-full bg-og-bg p-1">
|
|
685
746
|
<SandboxTerminal
|
|
747
|
+
key={sessionId}
|
|
686
748
|
result={terminal}
|
|
687
749
|
terminalCapability={capabilities?.Terminal ?? null}
|
|
688
750
|
onActivate={() => requestWarmIntent("warmTerminal")}
|
|
@@ -696,25 +758,51 @@ export function useSandboxWorkspaceTabs(
|
|
|
696
758
|
});
|
|
697
759
|
}
|
|
698
760
|
|
|
699
|
-
//
|
|
700
|
-
|
|
761
|
+
// Browser — independent from sandbox capability negotiation. BrowserSessions
|
|
762
|
+
// are workspace resources and may live in this agent's sandbox, a peer's
|
|
763
|
+
// sandbox, a connected machine, or an external browser placement.
|
|
764
|
+
if (browserEnabled) {
|
|
765
|
+
list.push({
|
|
766
|
+
id: WORKBENCH_TAB_BROWSER,
|
|
767
|
+
label: <WorkbenchTabLabel icon={<Globe2Icon />}>Browser</WorkbenchTabLabel>,
|
|
768
|
+
content: (
|
|
769
|
+
<Suspense fallback={<WorkbenchSurfaceLoading name="Browser" />}>
|
|
770
|
+
<LazyBrowserViewer
|
|
771
|
+
key={sessionId}
|
|
772
|
+
client={client}
|
|
773
|
+
workspaceId={workspaceId}
|
|
774
|
+
sessionId={sessionId}
|
|
775
|
+
onNotify={onNotify}
|
|
776
|
+
{...(desktopEnabled ? { createLinkedComputer } : {})}
|
|
777
|
+
{...(onOpenComputerSession ? { onOpenComputer: onOpenComputerSession } : {})}
|
|
778
|
+
{...(browserWebSocketFactory ? { webSocketFactory: browserWebSocketFactory } : {})}
|
|
779
|
+
className="h-full"
|
|
780
|
+
/>
|
|
781
|
+
</Suspense>
|
|
782
|
+
),
|
|
783
|
+
});
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
// Computer — stable legacy tab id, new workspace resource body. Like Browser,
|
|
787
|
+
// it is independent from the selected agent's sandbox capability document.
|
|
788
|
+
if (desktopEnabled) {
|
|
701
789
|
list.push({
|
|
702
790
|
id: WORKBENCH_TAB_DESKTOP,
|
|
703
|
-
label: <WorkbenchTabLabel icon={<MonitorIcon />}>
|
|
704
|
-
badge: watchDesktop ? (
|
|
705
|
-
<span className="rounded-og-xs bg-og-status-running/20 px-1 text-og-xs text-og-status-running">
|
|
706
|
-
Live
|
|
707
|
-
</span>
|
|
708
|
-
) : undefined,
|
|
791
|
+
label: <WorkbenchTabLabel icon={<MonitorIcon />}>Computer</WorkbenchTabLabel>,
|
|
709
792
|
content: (
|
|
710
|
-
<
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
793
|
+
<Suspense fallback={<WorkbenchSurfaceLoading name="Computer" />}>
|
|
794
|
+
<LazyComputerViewer
|
|
795
|
+
key={sessionId}
|
|
796
|
+
client={client}
|
|
797
|
+
workspaceId={workspaceId}
|
|
798
|
+
sessionId={sessionId}
|
|
799
|
+
onNotify={onNotify}
|
|
800
|
+
requestedComputerSessionId={requestedComputerSessionId}
|
|
801
|
+
requestedComputerRequestId={requestedComputerRequestId}
|
|
802
|
+
{...(computerWebSocketFactory ? { webSocketFactory: computerWebSocketFactory } : {})}
|
|
803
|
+
className="h-full"
|
|
804
|
+
/>
|
|
805
|
+
</Suspense>
|
|
718
806
|
),
|
|
719
807
|
});
|
|
720
808
|
}
|
|
@@ -726,14 +814,22 @@ export function useSandboxWorkspaceTabs(
|
|
|
726
814
|
changesEnabled,
|
|
727
815
|
filesEnabled,
|
|
728
816
|
terminalOn,
|
|
729
|
-
|
|
817
|
+
browserEnabled,
|
|
818
|
+
desktopEnabled,
|
|
730
819
|
captureAvailable,
|
|
731
820
|
dirtyCount,
|
|
732
|
-
watchDesktop,
|
|
733
821
|
warmTerminal,
|
|
734
822
|
requestedFilePath,
|
|
735
823
|
requestedFileRequestId,
|
|
736
824
|
onOpenFile,
|
|
825
|
+
onOpenComputerSession,
|
|
826
|
+
requestedComputerSessionId,
|
|
827
|
+
requestedComputerRequestId,
|
|
828
|
+
onNotify,
|
|
829
|
+
browserWebSocketFactory,
|
|
830
|
+
computerWebSocketFactory,
|
|
831
|
+
client,
|
|
832
|
+
workspaceId,
|
|
737
833
|
liveness,
|
|
738
834
|
sessionId,
|
|
739
835
|
files,
|
|
@@ -747,13 +843,14 @@ export function useSandboxWorkspaceTabs(
|
|
|
747
843
|
caps.error,
|
|
748
844
|
caps.viewerCapReached,
|
|
749
845
|
requestWarmIntent,
|
|
846
|
+
createLinkedComputer,
|
|
750
847
|
]);
|
|
751
848
|
|
|
752
849
|
return {
|
|
753
850
|
tabs,
|
|
754
851
|
defaultTab,
|
|
755
852
|
machine: {
|
|
756
|
-
enabled:
|
|
853
|
+
enabled: machineSurfaceEnabled,
|
|
757
854
|
chip,
|
|
758
855
|
activeMachine,
|
|
759
856
|
loading: machines.loading,
|
|
@@ -771,7 +868,7 @@ export type SandboxWorkspaceProps = ClientOverride & {
|
|
|
771
868
|
primary: ReactNode;
|
|
772
869
|
/**
|
|
773
870
|
* Built-in surfaces this host wants to expose. Omit for Changes, Files,
|
|
774
|
-
* Terminal, and Desktop. Disabled surfaces remain behaviorally dormant.
|
|
871
|
+
* Terminal, Browser, and Desktop. Disabled surfaces remain behaviorally dormant.
|
|
775
872
|
*/
|
|
776
873
|
surfaces?: readonly SandboxWorkspaceSurface[] | undefined;
|
|
777
874
|
/** Host tabs injected BEFORE the workbench tabs (e.g. a "Run" landing tab). */
|
|
@@ -783,6 +880,10 @@ export type SandboxWorkspaceProps = ClientOverride & {
|
|
|
783
880
|
initialTab?: string | undefined;
|
|
784
881
|
/** Host-routed notifications (no toast dependency in the package). */
|
|
785
882
|
onNotify?: ((notification: WorkspaceNotification) => void) | undefined;
|
|
883
|
+
/** Alternate Browser frame transport for non-browser runtimes and deterministic tests. */
|
|
884
|
+
browserWebSocketFactory?: BrowserFrameWebSocketFactory | undefined;
|
|
885
|
+
/** Alternate Computer frame transport for non-browser runtimes and deterministic tests. */
|
|
886
|
+
computerWebSocketFactory?: ComputerFrameWebSocketFactory | undefined;
|
|
786
887
|
/** Controlled collapsed state for hosts with their own dock toggle. */
|
|
787
888
|
collapsed?: boolean | undefined;
|
|
788
889
|
onCollapsedChange?: ((collapsed: boolean) => void) | undefined;
|
|
@@ -814,6 +915,8 @@ export function SandboxWorkspace(props: SandboxWorkspaceProps): ReactNode {
|
|
|
814
915
|
trailingTabs,
|
|
815
916
|
initialTab,
|
|
816
917
|
onNotify,
|
|
918
|
+
browserWebSocketFactory,
|
|
919
|
+
computerWebSocketFactory,
|
|
817
920
|
collapsed,
|
|
818
921
|
onCollapsedChange,
|
|
819
922
|
showCollapseControl,
|
|
@@ -834,7 +937,13 @@ export function SandboxWorkspace(props: SandboxWorkspaceProps): ReactNode {
|
|
|
834
937
|
path: string;
|
|
835
938
|
requestId: number;
|
|
836
939
|
} | null>(null);
|
|
940
|
+
const [requestedComputer, setRequestedComputer] = useState<{
|
|
941
|
+
sessionId: string;
|
|
942
|
+
computerSessionId: string;
|
|
943
|
+
requestId: number;
|
|
944
|
+
} | null>(null);
|
|
837
945
|
const nextFileRequestId = useRef(0);
|
|
946
|
+
const nextComputerRequestId = useRef(0);
|
|
838
947
|
const selectedTab = storedSelection?.sessionId === sessionId ? storedSelection.tab : null;
|
|
839
948
|
const activeTabHint = selectedTab ?? initialTab ?? leadingTabs?.[0]?.id ?? null;
|
|
840
949
|
const openFile = useCallback(
|
|
@@ -845,6 +954,18 @@ export function SandboxWorkspace(props: SandboxWorkspaceProps): ReactNode {
|
|
|
845
954
|
},
|
|
846
955
|
[sessionId],
|
|
847
956
|
);
|
|
957
|
+
const openComputer = useCallback(
|
|
958
|
+
(computerSessionId: string) => {
|
|
959
|
+
nextComputerRequestId.current += 1;
|
|
960
|
+
setRequestedComputer({
|
|
961
|
+
sessionId,
|
|
962
|
+
computerSessionId,
|
|
963
|
+
requestId: nextComputerRequestId.current,
|
|
964
|
+
});
|
|
965
|
+
setStoredSelection({ sessionId, tab: WORKBENCH_TAB_DESKTOP });
|
|
966
|
+
},
|
|
967
|
+
[sessionId],
|
|
968
|
+
);
|
|
848
969
|
|
|
849
970
|
const {
|
|
850
971
|
tabs: workbenchTabs,
|
|
@@ -859,9 +980,16 @@ export function SandboxWorkspace(props: SandboxWorkspaceProps): ReactNode {
|
|
|
859
980
|
...(initialTab ? { initialTab } : {}),
|
|
860
981
|
activeTab: activeTabHint,
|
|
861
982
|
...(onNotify ? { onNotify } : {}),
|
|
983
|
+
...(browserWebSocketFactory ? { browserWebSocketFactory } : {}),
|
|
984
|
+
...(computerWebSocketFactory ? { computerWebSocketFactory } : {}),
|
|
862
985
|
requestedFilePath: requestedFile?.sessionId === sessionId ? requestedFile.path : null,
|
|
863
986
|
requestedFileRequestId: requestedFile?.sessionId === sessionId ? requestedFile.requestId : null,
|
|
864
987
|
onOpenFile: openFile,
|
|
988
|
+
onOpenComputerSession: openComputer,
|
|
989
|
+
requestedComputerSessionId:
|
|
990
|
+
requestedComputer?.sessionId === sessionId ? requestedComputer.computerSessionId : null,
|
|
991
|
+
requestedComputerRequestId:
|
|
992
|
+
requestedComputer?.sessionId === sessionId ? requestedComputer.requestId : null,
|
|
865
993
|
});
|
|
866
994
|
|
|
867
995
|
// A user's tab click wins forever; before that we follow the source-driven
|