@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
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { FileCode2Icon, FileWarningIcon, LoaderCircleIcon } from "lucide-react";
|
|
2
2
|
import { type ReactNode, useCallback, useEffect, useRef, useState } from "react";
|
|
3
|
+
import { Group, Panel, Separator } from "react-resizable-panels";
|
|
3
4
|
import { cn } from "../lib/cn";
|
|
4
5
|
import { useThemeType } from "../lib/use-theme-type";
|
|
5
6
|
import {
|
|
6
7
|
CapturedFileUnavailableError,
|
|
8
|
+
type SandboxFilesGitSummary,
|
|
7
9
|
type UseSandboxFilesResult,
|
|
8
10
|
} from "../hooks/use-sandbox-files";
|
|
9
11
|
import type { UseSandboxGitResult } from "../hooks/use-sandbox-git";
|
|
@@ -14,8 +16,8 @@ import { PierreFile } from "./pierre-file";
|
|
|
14
16
|
export type SandboxFilesProps = {
|
|
15
17
|
/** From `useSandboxFiles(...)`. */
|
|
16
18
|
files: UseSandboxFilesResult;
|
|
17
|
-
/**
|
|
18
|
-
git
|
|
19
|
+
/** Compatibility fallback for callers that have not yet adopted `files.gitSummary`. */
|
|
20
|
+
git?: UseSandboxGitResult | undefined;
|
|
19
21
|
/** @deprecated Diffs live in the dedicated Changes tab now; accepted for
|
|
20
22
|
* source-compat but unused (the Files surface is a browser + viewer). */
|
|
21
23
|
stagedGit?: UseSandboxGitResult | undefined;
|
|
@@ -111,15 +113,29 @@ export function SandboxFiles({
|
|
|
111
113
|
const [wide, setWide] = useState(false);
|
|
112
114
|
useEffect(() => {
|
|
113
115
|
const el = rootRef.current;
|
|
114
|
-
if (!el
|
|
115
|
-
const
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
116
|
+
if (!el) return;
|
|
117
|
+
const update = () => setWide(el.getBoundingClientRect().width >= 720);
|
|
118
|
+
update();
|
|
119
|
+
if (typeof ResizeObserver === "undefined") return;
|
|
120
|
+
const observer = new ResizeObserver(update);
|
|
119
121
|
observer.observe(el);
|
|
120
|
-
|
|
122
|
+
// WorkspaceDock maximization changes its persistent surface from a sized
|
|
123
|
+
// panel child to `fixed inset-0`. Chromium can retain the child's observer
|
|
124
|
+
// size across that containing-block change even though its visible bounding
|
|
125
|
+
// box is now viewport-wide. Follow the surface mode as a deterministic
|
|
126
|
+
// second signal so the tree/viewer orientation matches what is painted.
|
|
127
|
+
const surface = el.closest("[data-workspace-surface]");
|
|
128
|
+
const surfaceObserver =
|
|
129
|
+
surface && typeof MutationObserver !== "undefined" ? new MutationObserver(update) : null;
|
|
130
|
+
surfaceObserver?.observe(surface!, {
|
|
131
|
+
attributes: true,
|
|
132
|
+
attributeFilter: ["class", "style"],
|
|
133
|
+
});
|
|
134
|
+
return () => {
|
|
135
|
+
observer.disconnect();
|
|
136
|
+
surfaceObserver?.disconnect();
|
|
137
|
+
};
|
|
121
138
|
}, []);
|
|
122
|
-
|
|
123
139
|
// Resolve the effective viewer theme from the host palette (the `data-og-theme`
|
|
124
140
|
// attribute the demo/app sets), defaulting to dark, unless the caller forced one.
|
|
125
141
|
const resolvedTheme = useThemeType(themeType);
|
|
@@ -128,7 +144,11 @@ export function SandboxFiles({
|
|
|
128
144
|
// writable). Nothing is auto-selected — the pane waits for a tree click, so the
|
|
129
145
|
// Files tab opens as a calm browser, not a diff.
|
|
130
146
|
const viewPath = selected;
|
|
131
|
-
const fileView = useFileView(
|
|
147
|
+
const fileView = useFileView(
|
|
148
|
+
viewPath,
|
|
149
|
+
files.readFile,
|
|
150
|
+
(files.contentRevision ?? 0) + viewReloadRevision,
|
|
151
|
+
);
|
|
132
152
|
|
|
133
153
|
// Selecting a (different) file always returns to View — never drop the user into
|
|
134
154
|
// an editor whose buffer belongs to the previously-selected path. Manual
|
|
@@ -165,19 +185,6 @@ export function SandboxFiles({
|
|
|
165
185
|
(!liveWorkspaceReady || files.loading) &&
|
|
166
186
|
captureFileUnavailable !== null;
|
|
167
187
|
|
|
168
|
-
if (!fileSystemAvailable) {
|
|
169
|
-
return (
|
|
170
|
-
<Notice
|
|
171
|
-
className={className}
|
|
172
|
-
icon={<FileWarningIcon className="size-5" aria-hidden />}
|
|
173
|
-
title="Files unavailable"
|
|
174
|
-
announce="alert"
|
|
175
|
-
>
|
|
176
|
-
This sandbox does not expose a file system.
|
|
177
|
-
</Notice>
|
|
178
|
-
);
|
|
179
|
-
}
|
|
180
|
-
|
|
181
188
|
if (workspaceResting || workspaceWaking) {
|
|
182
189
|
return (
|
|
183
190
|
<div className={cn("h-full", className)} data-opengeni-workspace-resting>
|
|
@@ -208,19 +215,51 @@ export function SandboxFiles({
|
|
|
208
215
|
);
|
|
209
216
|
}
|
|
210
217
|
|
|
218
|
+
if (!fileSystemAvailable) {
|
|
219
|
+
return (
|
|
220
|
+
<Notice
|
|
221
|
+
className={className}
|
|
222
|
+
icon={<FileWarningIcon className="size-5" aria-hidden />}
|
|
223
|
+
title="Files unavailable"
|
|
224
|
+
announce="alert"
|
|
225
|
+
>
|
|
226
|
+
This sandbox does not expose a file system.
|
|
227
|
+
</Notice>
|
|
228
|
+
);
|
|
229
|
+
}
|
|
230
|
+
|
|
211
231
|
return (
|
|
212
232
|
<div ref={rootRef} className={cn("flex h-full min-h-0 min-w-0 flex-col", className)}>
|
|
213
233
|
{/* Branch + dirty header (context; NOT the changed-files list). */}
|
|
214
|
-
<GitHeader
|
|
234
|
+
<GitHeader
|
|
235
|
+
loading={
|
|
236
|
+
Boolean(files.loading || files.gitLoading || files.source === null || git?.loading) &&
|
|
237
|
+
files.gitSummary === null
|
|
238
|
+
}
|
|
239
|
+
git={
|
|
240
|
+
files.gitSummary ?? {
|
|
241
|
+
branch: git?.branch ?? null,
|
|
242
|
+
isRepo: git?.isRepo ?? false,
|
|
243
|
+
repoCount: git?.repoCount ?? 0,
|
|
244
|
+
ahead: git?.ahead ?? 0,
|
|
245
|
+
behind: git?.behind ?? 0,
|
|
246
|
+
dirtyCount: git?.diff.length ?? 0,
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
/>
|
|
215
250
|
|
|
216
|
-
<
|
|
251
|
+
<Group
|
|
252
|
+
orientation={wide ? "horizontal" : "vertical"}
|
|
253
|
+
className={cn("min-h-0 flex-1", wide ? "flex-row" : "flex-col")}
|
|
254
|
+
>
|
|
217
255
|
{/* Tree pane: the full lazy file tree. A fixed left column when wide, a top
|
|
218
256
|
band when narrow. */}
|
|
219
|
-
<
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
257
|
+
<Panel
|
|
258
|
+
id="sandbox-files-tree"
|
|
259
|
+
defaultSize={wide ? "280px" : "40%"}
|
|
260
|
+
minSize={wide ? "180px" : "120px"}
|
|
261
|
+
maxSize={wide ? "60%" : "70%"}
|
|
262
|
+
className="flex min-h-0 min-w-0 flex-col"
|
|
224
263
|
>
|
|
225
264
|
<FileBrowser
|
|
226
265
|
result={files}
|
|
@@ -230,16 +269,26 @@ export function SandboxFiles({
|
|
|
230
269
|
emptyState="This directory is empty"
|
|
231
270
|
className="min-w-0 flex-1"
|
|
232
271
|
/>
|
|
233
|
-
</
|
|
272
|
+
</Panel>
|
|
273
|
+
|
|
274
|
+
<Separator
|
|
275
|
+
aria-label="Resize file tree"
|
|
276
|
+
className={cn(
|
|
277
|
+
"group relative z-10 shrink-0 cursor-col-resize outline-hidden focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-og-accent",
|
|
278
|
+
"w-px bg-og-border after:absolute after:inset-y-0 after:left-1/2 after:w-3 after:-translate-x-1/2",
|
|
279
|
+
"hover:bg-og-accent focus-visible:bg-og-accent data-[separator-state=dragging]:bg-og-accent",
|
|
280
|
+
!wide &&
|
|
281
|
+
"h-px w-full cursor-row-resize after:inset-x-0 after:inset-y-auto after:top-1/2 after:h-3 after:w-full after:-translate-x-0 after:-translate-y-1/2",
|
|
282
|
+
)}
|
|
283
|
+
/>
|
|
234
284
|
|
|
235
285
|
{/* Viewer pane: the selected file's contents (read-only) or the editor.
|
|
236
286
|
Fills the remaining width when side-by-side, sits below the tree when
|
|
237
287
|
stacked. */}
|
|
238
|
-
<
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
)}
|
|
288
|
+
<Panel
|
|
289
|
+
id="sandbox-files-viewer"
|
|
290
|
+
minSize={wide ? "240px" : "160px"}
|
|
291
|
+
className={cn("flex min-h-0 min-w-0 flex-col", !wide && "border-t border-og-border")}
|
|
243
292
|
>
|
|
244
293
|
<div className="flex shrink-0 items-center justify-between gap-2 border-b border-og-border bg-og-surface-1 px-2 py-1">
|
|
245
294
|
<span
|
|
@@ -270,8 +319,8 @@ export function SandboxFiles({
|
|
|
270
319
|
path={viewPath}
|
|
271
320
|
initialContents={fileView.content}
|
|
272
321
|
themeType={resolvedTheme}
|
|
273
|
-
onSave={(contents) =>
|
|
274
|
-
files.writeFile(viewPath, contents, { expectedContent:
|
|
322
|
+
onSave={(contents, expectedContents) =>
|
|
323
|
+
files.writeFile(viewPath, contents, { expectedContent: expectedContents })
|
|
275
324
|
}
|
|
276
325
|
onOverwrite={(contents) => files.writeFile(viewPath, contents, { force: true })}
|
|
277
326
|
onReload={() => setViewReloadRevision((revision) => revision + 1)}
|
|
@@ -371,8 +420,8 @@ export function SandboxFiles({
|
|
|
371
420
|
</Notice>
|
|
372
421
|
)}
|
|
373
422
|
</div>
|
|
374
|
-
</
|
|
375
|
-
</
|
|
423
|
+
</Panel>
|
|
424
|
+
</Group>
|
|
376
425
|
</div>
|
|
377
426
|
);
|
|
378
427
|
}
|
|
@@ -382,15 +431,30 @@ function WakeButton({ children, onClick }: { children: ReactNode; onClick: () =>
|
|
|
382
431
|
<button
|
|
383
432
|
type="button"
|
|
384
433
|
onClick={onClick}
|
|
385
|
-
className="mt-1 inline-flex min-h-11 items-center justify-center rounded-og-md bg-og-accent-deep px-3 py-2 text-og-sm font-medium text-og-accent-fg shadow-sm transition-opacity hover:opacity-90 focus-visible:outline-
|
|
434
|
+
className="mt-1 inline-flex min-h-11 items-center justify-center rounded-og-md bg-og-accent-deep px-3 py-2 text-og-sm font-medium text-og-accent-fg shadow-sm transition-opacity hover:opacity-90 focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-og-accent focus-visible:ring-offset-2 focus-visible:ring-offset-og-bg"
|
|
386
435
|
>
|
|
387
436
|
{children}
|
|
388
437
|
</button>
|
|
389
438
|
);
|
|
390
439
|
}
|
|
391
440
|
|
|
392
|
-
function GitHeader({ git,
|
|
393
|
-
|
|
441
|
+
function GitHeader({ git, loading }: { git: SandboxFilesGitSummary; loading: boolean }) {
|
|
442
|
+
if (loading) {
|
|
443
|
+
return (
|
|
444
|
+
<div
|
|
445
|
+
className="flex shrink-0 items-center gap-2 border-b border-og-border bg-og-surface-1 px-2 py-1 text-og-sm text-og-fg-muted"
|
|
446
|
+
role="status"
|
|
447
|
+
aria-live="polite"
|
|
448
|
+
>
|
|
449
|
+
<LoaderCircleIcon
|
|
450
|
+
className="size-3.5 shrink-0 animate-spin motion-reduce:animate-none"
|
|
451
|
+
aria-hidden
|
|
452
|
+
/>
|
|
453
|
+
<span>Loading repository…</span>
|
|
454
|
+
</div>
|
|
455
|
+
);
|
|
456
|
+
}
|
|
457
|
+
const dirty = git.dirtyCount > 0;
|
|
394
458
|
return (
|
|
395
459
|
<div className="flex shrink-0 items-center gap-2 border-b border-og-border bg-og-surface-1 px-2 py-1 text-og-sm">
|
|
396
460
|
<span
|
|
@@ -401,7 +465,7 @@ function GitHeader({ git, dirtyCount }: { git: UseSandboxGitResult; dirtyCount:
|
|
|
401
465
|
)}
|
|
402
466
|
/>
|
|
403
467
|
<span className="sr-only">
|
|
404
|
-
{dirty ? `
|
|
468
|
+
{dirty ? `Uncommitted changes in ${git.dirtyCount} files` : "No uncommitted changes"}
|
|
405
469
|
</span>
|
|
406
470
|
<span className="truncate font-og-mono text-og-fg">
|
|
407
471
|
{git.repoCount > 1
|
|
@@ -420,7 +484,7 @@ function GitHeader({ git, dirtyCount }: { git: UseSandboxGitResult; dirtyCount:
|
|
|
420
484
|
data-contrast-audited
|
|
421
485
|
className="ml-auto shrink-0 text-og-xs text-og-fg-subtle"
|
|
422
486
|
>
|
|
423
|
-
{dirtyCount} changed
|
|
487
|
+
{git.dirtyCount} changed
|
|
424
488
|
</span>
|
|
425
489
|
)}
|
|
426
490
|
</div>
|
|
@@ -444,7 +508,7 @@ function Segmented({
|
|
|
444
508
|
type="button"
|
|
445
509
|
onClick={() => onChange(opt.value)}
|
|
446
510
|
className={cn(
|
|
447
|
-
"min-h-7 rounded-og-xs px-1.5 py-0.5 text-og-xs focus-visible:outline-
|
|
511
|
+
"min-h-7 rounded-og-xs px-1.5 py-0.5 text-og-xs focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-og-accent max-[1023px]:min-h-11 max-[1023px]:min-w-11 pointer-coarse:min-h-11 pointer-coarse:min-w-11",
|
|
448
512
|
opt.value === value
|
|
449
513
|
? "bg-og-accent-soft text-og-fg"
|
|
450
514
|
: "text-og-fg-subtle hover:text-og-fg",
|
|
@@ -480,6 +544,7 @@ function useFileView(
|
|
|
480
544
|
readFile: UseSandboxFilesResult["readFile"],
|
|
481
545
|
reloadRevision = 0,
|
|
482
546
|
): FileViewState {
|
|
547
|
+
const previousPathRef = useRef<string | null>(null);
|
|
483
548
|
const [state, setState] = useState<FileViewState>({
|
|
484
549
|
content: null,
|
|
485
550
|
isBinary: false,
|
|
@@ -490,6 +555,7 @@ function useFileView(
|
|
|
490
555
|
});
|
|
491
556
|
useEffect(() => {
|
|
492
557
|
if (!path) {
|
|
558
|
+
previousPathRef.current = null;
|
|
493
559
|
setState({
|
|
494
560
|
content: null,
|
|
495
561
|
isBinary: false,
|
|
@@ -500,16 +566,25 @@ function useFileView(
|
|
|
500
566
|
});
|
|
501
567
|
return;
|
|
502
568
|
}
|
|
569
|
+
const pathChanged = previousPathRef.current !== path;
|
|
570
|
+
previousPathRef.current = path;
|
|
503
571
|
let cancelled = false;
|
|
504
572
|
const abort = new AbortController();
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
573
|
+
if (pathChanged) {
|
|
574
|
+
setState({
|
|
575
|
+
content: null,
|
|
576
|
+
isBinary: false,
|
|
577
|
+
truncated: false,
|
|
578
|
+
sizeBytes: null,
|
|
579
|
+
loading: true,
|
|
580
|
+
error: null,
|
|
581
|
+
});
|
|
582
|
+
} else {
|
|
583
|
+
// Keep the last successful preview visible while a remote Modal read
|
|
584
|
+
// revalidates it. Replacing useful content with a multi-second spinner
|
|
585
|
+
// makes a live workspace feel slower and hides the state being refreshed.
|
|
586
|
+
setState((previous) => ({ ...previous, error: null }));
|
|
587
|
+
}
|
|
513
588
|
void readFile(path, { signal: abort.signal })
|
|
514
589
|
.then((res) => {
|
|
515
590
|
if (cancelled) return;
|
|
@@ -529,14 +604,19 @@ function useFileView(
|
|
|
529
604
|
})
|
|
530
605
|
.catch((cause) => {
|
|
531
606
|
if (cancelled) return;
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
607
|
+
const error = cause instanceof Error ? cause : new Error(String(cause));
|
|
608
|
+
setState((previous) =>
|
|
609
|
+
pathChanged
|
|
610
|
+
? {
|
|
611
|
+
content: null,
|
|
612
|
+
isBinary: false,
|
|
613
|
+
truncated: false,
|
|
614
|
+
sizeBytes: null,
|
|
615
|
+
loading: false,
|
|
616
|
+
error,
|
|
617
|
+
}
|
|
618
|
+
: { ...previous, loading: false, error },
|
|
619
|
+
);
|
|
540
620
|
});
|
|
541
621
|
return () => {
|
|
542
622
|
cancelled = true;
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import type { TerminalCapability } from "@opengeni/sdk";
|
|
2
2
|
import { type ReactNode, useEffect, useRef, useState } from "react";
|
|
3
3
|
import { cn } from "../lib/cn";
|
|
4
|
-
import { useTerminalStream } from "../hooks/use-terminal-stream";
|
|
4
|
+
import { type TerminalStreamStatus, useTerminalStream } from "../hooks/use-terminal-stream";
|
|
5
5
|
import type { UseSandboxTerminalResult } from "../hooks/use-sandbox-terminal";
|
|
6
6
|
import { resolveTerminalFont, xtermThemeFromTokens } from "../lib/xterm-theme";
|
|
7
|
+
import { sandboxAcceptsLiveIo } from "../lib/sandbox-liveness";
|
|
8
|
+
import { terminalCanAcquirePty } from "../lib/terminal-capability";
|
|
7
9
|
import { attachRenderer, type RendererLoaders, type RendererTier } from "../lib/xterm-renderer";
|
|
8
10
|
|
|
9
11
|
/**
|
|
@@ -60,7 +62,7 @@ export type SandboxTerminalProps = {
|
|
|
60
62
|
fontSize?: number | undefined;
|
|
61
63
|
/**
|
|
62
64
|
* Lease liveness (`cold` | `warming` | `warm` | `draining`). Drives the
|
|
63
|
-
* boot-in-terminal status lines: once the user has
|
|
65
|
+
* boot-in-terminal status lines: once the user has engaged the terminal and
|
|
64
66
|
* the box is not yet warm, a styled "● waking machine — Ns…" line is rendered
|
|
65
67
|
* INSIDE xterm (no overlay/spinner) until the live PTY connects.
|
|
66
68
|
*/
|
|
@@ -110,6 +112,51 @@ type XtermLike = {
|
|
|
110
112
|
};
|
|
111
113
|
type FitAddonLike = { fit: () => void };
|
|
112
114
|
|
|
115
|
+
export type TerminalSurfaceState =
|
|
116
|
+
| "loading"
|
|
117
|
+
| "connecting"
|
|
118
|
+
| "error"
|
|
119
|
+
| "waking"
|
|
120
|
+
| "output-only"
|
|
121
|
+
| "interactive";
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Stable semantic state for hosts and browser acceptance. The xterm helper
|
|
125
|
+
* textarea exists before the live PTY socket is ready, so its presence alone
|
|
126
|
+
* must never be treated as proof that keystrokes can reach the sandbox.
|
|
127
|
+
*/
|
|
128
|
+
export function terminalSurfaceState(input: {
|
|
129
|
+
ready: boolean;
|
|
130
|
+
acceptsInput: boolean;
|
|
131
|
+
inputPending: boolean;
|
|
132
|
+
ptyStatus: TerminalStreamStatus;
|
|
133
|
+
booting: boolean;
|
|
134
|
+
}): TerminalSurfaceState {
|
|
135
|
+
if (!input.ready) return "loading";
|
|
136
|
+
if (input.acceptsInput) return "interactive";
|
|
137
|
+
if (input.inputPending) return "connecting";
|
|
138
|
+
if (input.ptyStatus === "connecting") return "connecting";
|
|
139
|
+
if (input.ptyStatus === "error") return "error";
|
|
140
|
+
if (input.booting) return "waking";
|
|
141
|
+
return "output-only";
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export function subscribeTerminalInput(
|
|
145
|
+
term: Pick<XtermLike, "onData"> | null,
|
|
146
|
+
sink: ((data: string) => void) | null,
|
|
147
|
+
enabled: boolean,
|
|
148
|
+
): { dispose: () => void } | null {
|
|
149
|
+
if (!enabled || !term || !sink) return null;
|
|
150
|
+
return term.onData(sink);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/** Clear the transient wake line and put the first live prompt at cell 1,1.
|
|
154
|
+
* `Terminal.clear()` removes scrollback but deliberately preserves the cursor,
|
|
155
|
+
* which lets the first PTY frame append to the old wake message. */
|
|
156
|
+
export function clearTerminalBootStatus(term: Pick<XtermLike, "write">): void {
|
|
157
|
+
term.write("\r\x1b[2K\x1b[2J\x1b[H");
|
|
158
|
+
}
|
|
159
|
+
|
|
113
160
|
/** Debug seam (dev/evidence only, never a typed public API): a global hook the
|
|
114
161
|
* screenshot harness reads to probe the settled renderer tier + resolved font
|
|
115
162
|
* without mounting a real WebGL context in unit tests. */
|
|
@@ -186,41 +233,61 @@ export function SandboxTerminal({
|
|
|
186
233
|
const wroteFirehoseRef = useRef(false);
|
|
187
234
|
const bootActiveRef = useRef(false);
|
|
188
235
|
const [ready, setReady] = useState(false);
|
|
236
|
+
const [inputReady, setInputReady] = useState(false);
|
|
189
237
|
const [activated, setActivated] = useState(false);
|
|
190
238
|
|
|
191
239
|
// ── Interactive transport switch ─────────────────────────────────────────────
|
|
192
|
-
const
|
|
193
|
-
|
|
194
|
-
const ptyOutputQueueRef = useRef<string[]>([]);
|
|
240
|
+
const ptyDescriptor = terminalCapability?.transport === "pty-ws";
|
|
241
|
+
const ptyAttachable = terminalCanAcquirePty(terminalCapability);
|
|
195
242
|
const {
|
|
196
243
|
status: ptyStatus,
|
|
197
244
|
write: ptyWrite,
|
|
198
245
|
resize: resizePty,
|
|
199
246
|
connected: ptyConnected,
|
|
200
247
|
} = useTerminalStream({
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
248
|
+
// Connect only after xterm exists. This makes the renderer the sole output
|
|
249
|
+
// owner from the first websocket frame and removes an otherwise unbounded
|
|
250
|
+
// pre-mount output queue during large command bursts.
|
|
251
|
+
capability:
|
|
252
|
+
ptyAttachable && ready
|
|
253
|
+
? {
|
|
254
|
+
transport: "pty-ws",
|
|
255
|
+
url: ptyDescriptor ? terminalCapability!.url : null,
|
|
256
|
+
token: ptyDescriptor ? terminalCapability!.token : null,
|
|
257
|
+
expiresAt: ptyDescriptor ? terminalCapability!.expiresAt : null,
|
|
258
|
+
}
|
|
259
|
+
: null,
|
|
208
260
|
onOutput: (data) => {
|
|
209
|
-
|
|
210
|
-
if (term) term.write(data);
|
|
211
|
-
else ptyOutputQueueRef.current.push(data);
|
|
261
|
+
termRef.current?.write(data);
|
|
212
262
|
},
|
|
213
263
|
});
|
|
214
264
|
|
|
215
|
-
//
|
|
216
|
-
//
|
|
217
|
-
|
|
218
|
-
|
|
265
|
+
// A pty-ws capability exclusively owns the screen even while connecting or
|
|
266
|
+
// after a visible connection failure; never silently switch one terminal UI
|
|
267
|
+
// between two PTYs. Input is enabled during the handshake because the stream
|
|
268
|
+
// hook buffers it strictly and flushes only after ttyd auth succeeds.
|
|
269
|
+
const ptyMode = ptyDescriptor;
|
|
270
|
+
// Keep xterm stdin live for an attachable descriptor even before its URL has
|
|
271
|
+
// arrived. Focus/click triggers the grant; any immediately following input is
|
|
272
|
+
// bounded in the stream hook instead of disappearing in that HTTP interval.
|
|
273
|
+
const ptyCapturesInput = ptyAttachable && ptyStatus !== "error";
|
|
274
|
+
const interactive = !readOnly && (ptyAttachable ? ptyCapturesInput : result.write !== null);
|
|
275
|
+
// Public "interactive" means the mounted input subscription can reach a live
|
|
276
|
+
// PTY now. Before that point xterm still captures into the bounded queue, but
|
|
277
|
+
// acceptance and embedders see an explicit connecting state.
|
|
278
|
+
const acceptsInput = inputReady && (!ptyAttachable || ptyConnected);
|
|
279
|
+
const inputPending = inputReady && ptyAttachable && !ptyConnected && activated;
|
|
280
|
+
let terminalModeLabel: string | null;
|
|
281
|
+
if (readOnly) terminalModeLabel = "read-only";
|
|
282
|
+
else if (!ptyAttachable) terminalModeLabel = acceptsInput ? null : "output only";
|
|
283
|
+
else if (ptyStatus === "error") terminalModeLabel = "connection error";
|
|
284
|
+
else if (acceptsInput) terminalModeLabel = null;
|
|
285
|
+
else terminalModeLabel = activated ? "connecting" : "connect on input";
|
|
219
286
|
|
|
220
|
-
// Boot-in-terminal: after the user
|
|
287
|
+
// Boot-in-terminal: after the user engages a not-yet-warm box, show styled
|
|
221
288
|
// status lines INSIDE xterm instead of an overlay — but only when there is no
|
|
222
289
|
// firehose transcript to show yet (otherwise the projected output is shown).
|
|
223
|
-
const warm = liveness
|
|
290
|
+
const warm = sandboxAcceptsLiveIo(liveness);
|
|
224
291
|
const booting =
|
|
225
292
|
activated &&
|
|
226
293
|
!ptyMode &&
|
|
@@ -228,6 +295,13 @@ export function SandboxTerminal({
|
|
|
228
295
|
liveness !== null &&
|
|
229
296
|
liveness !== undefined &&
|
|
230
297
|
result.chunks.length === 0;
|
|
298
|
+
const surfaceState = terminalSurfaceState({
|
|
299
|
+
ready,
|
|
300
|
+
acceptsInput,
|
|
301
|
+
inputPending,
|
|
302
|
+
ptyStatus,
|
|
303
|
+
booting,
|
|
304
|
+
});
|
|
231
305
|
|
|
232
306
|
function handleActivate() {
|
|
233
307
|
if (!activated) setActivated(true);
|
|
@@ -345,10 +419,6 @@ export function SandboxTerminal({
|
|
|
345
419
|
requestAnimationFrame(() => {
|
|
346
420
|
if (disposed) return;
|
|
347
421
|
settle();
|
|
348
|
-
if (ptyOutputQueueRef.current.length > 0) {
|
|
349
|
-
for (const data of ptyOutputQueueRef.current) term.write(data);
|
|
350
|
-
ptyOutputQueueRef.current = [];
|
|
351
|
-
}
|
|
352
422
|
el.setAttribute("data-og-term-ready", "true");
|
|
353
423
|
if (typeof globalThis.__OG_TERMINAL_DEBUG__ === "function") {
|
|
354
424
|
globalThis.__OG_TERMINAL_DEBUG__({
|
|
@@ -407,7 +477,7 @@ export function SandboxTerminal({
|
|
|
407
477
|
if (!booting) {
|
|
408
478
|
if (bootActiveRef.current) {
|
|
409
479
|
bootActiveRef.current = false;
|
|
410
|
-
if (!wroteFirehoseRef.current) term
|
|
480
|
+
if (!wroteFirehoseRef.current) clearTerminalBootStatus(term);
|
|
411
481
|
}
|
|
412
482
|
return;
|
|
413
483
|
}
|
|
@@ -443,12 +513,19 @@ export function SandboxTerminal({
|
|
|
443
513
|
// socket; firehose mode uses the legacy ptyWrite-over-HTTP fn.
|
|
444
514
|
useEffect(() => {
|
|
445
515
|
const term = termRef.current;
|
|
446
|
-
if (!ready || !term || !interactive)
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
516
|
+
if (!ready || !term || !interactive) {
|
|
517
|
+
setInputReady(false);
|
|
518
|
+
return;
|
|
519
|
+
}
|
|
520
|
+
const sink = ptyAttachable ? ptyWrite : result.write;
|
|
521
|
+
if (!sink) {
|
|
522
|
+
setInputReady(false);
|
|
523
|
+
return;
|
|
524
|
+
}
|
|
525
|
+
const sub = subscribeTerminalInput(term, sink, true);
|
|
526
|
+
setInputReady(sub !== null);
|
|
527
|
+
return () => sub?.dispose();
|
|
528
|
+
}, [ready, interactive, ptyAttachable, ptyWrite, result.write]);
|
|
452
529
|
|
|
453
530
|
// In PTY mode, tell ttyd the window size on the xterm resize event.
|
|
454
531
|
useEffect(() => {
|
|
@@ -496,9 +573,12 @@ export function SandboxTerminal({
|
|
|
496
573
|
<span className="truncate font-og-mono">pty: {shell ?? "shell"}</span>
|
|
497
574
|
</span>
|
|
498
575
|
<span className="flex shrink-0 items-center gap-2">
|
|
499
|
-
{
|
|
500
|
-
<span
|
|
501
|
-
|
|
576
|
+
{terminalModeLabel && (
|
|
577
|
+
<span
|
|
578
|
+
className="rounded-og-sm bg-og-surface-2 px-1.5 py-0.5 text-og-xs uppercase tracking-wide"
|
|
579
|
+
role="status"
|
|
580
|
+
>
|
|
581
|
+
{terminalModeLabel}
|
|
502
582
|
</span>
|
|
503
583
|
)}
|
|
504
584
|
<button
|
|
@@ -532,6 +612,10 @@ export function SandboxTerminal({
|
|
|
532
612
|
className="h-full w-full"
|
|
533
613
|
style={{ visibility: ready ? "visible" : "hidden" }}
|
|
534
614
|
data-opengeni-terminal
|
|
615
|
+
data-opengeni-terminal-ready={ready ? "true" : "false"}
|
|
616
|
+
data-opengeni-terminal-interactive={surfaceState === "interactive" ? "true" : "false"}
|
|
617
|
+
data-opengeni-terminal-state={surfaceState}
|
|
618
|
+
data-opengeni-terminal-status={ptyAttachable ? ptyStatus : "firehose"}
|
|
535
619
|
/>
|
|
536
620
|
</div>
|
|
537
621
|
</div>
|