@opengeni/react 0.15.0 → 0.20.0
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 +110 -10
- package/dist/{chunk-TOJR776I.js → chunk-6BXBGJ3B.js} +45 -369
- package/dist/chunk-6BXBGJ3B.js.map +1 -0
- package/dist/chunk-6UT5OC3P.js +2133 -0
- package/dist/chunk-6UT5OC3P.js.map +1 -0
- package/dist/chunk-EBTT3MYN.js +92 -0
- package/dist/chunk-EBTT3MYN.js.map +1 -0
- package/dist/chunk-HHFA4AFX.js +425 -0
- package/dist/chunk-HHFA4AFX.js.map +1 -0
- package/dist/chunk-LAZ2A743.js +2393 -0
- package/dist/chunk-LAZ2A743.js.map +1 -0
- package/dist/chunk-MRSECXRP.js +563 -0
- package/dist/chunk-MRSECXRP.js.map +1 -0
- package/dist/chunk-TK7G6XLT.js +18 -0
- package/dist/chunk-TK7G6XLT.js.map +1 -0
- package/dist/composer-DvUaa5ki.d.ts +585 -0
- package/dist/composer.d.ts +6 -0
- package/dist/composer.js +56 -0
- package/dist/composer.js.map +1 -0
- package/dist/index.d.ts +1288 -1785
- package/dist/index.js +7015 -8806
- package/dist/index.js.map +1 -1
- package/dist/machines.d.ts +412 -3
- package/dist/machines.js +3 -1
- package/dist/noto-sans-arabic-loader-IA2T4X2A.js +21 -0
- package/dist/noto-sans-arabic-loader-IA2T4X2A.js.map +1 -0
- package/dist/noto-sans-jp-loader.generated-HFGLYBR3.js +292 -0
- package/dist/noto-sans-jp-loader.generated-HFGLYBR3.js.map +1 -0
- package/dist/queue-surface-implementation-NQMV2ML3.js +890 -0
- package/dist/queue-surface-implementation-NQMV2ML3.js.map +1 -0
- package/dist/session-Gd4iVPYw.d.ts +425 -0
- package/dist/session-context-DIFFlXKc.d.ts +52 -0
- package/dist/session.d.ts +4 -0
- package/dist/session.js +50 -0
- package/dist/session.js.map +1 -0
- package/dist/use-composer-BCfm4mzX.d.ts +112 -0
- package/package.json +20 -4
- package/src/approvals.ts +5 -3
- package/src/client.ts +40 -0
- package/src/components/approval-surface.tsx +198 -0
- package/src/components/chat-composer.tsx +94 -993
- package/src/components/code-editor.tsx +104 -26
- package/src/components/command-palette.tsx +8 -2
- package/src/components/composer-transcription-control.tsx +211 -0
- package/src/components/composer.tsx +1536 -0
- package/src/components/desktop-viewer.tsx +3 -3
- package/src/components/enrollment-consent.tsx +2 -2
- package/src/components/file-browser.tsx +367 -29
- package/src/components/human-input-form.tsx +417 -0
- package/src/components/machine-card.tsx +1 -1
- package/src/components/machines-dashboard.tsx +3 -3
- package/src/components/message-timeline.tsx +15 -7
- package/src/components/model-picker.tsx +12 -3
- package/src/components/pierre-diff.tsx +32 -6
- package/src/components/queue-surface-implementation.tsx +1041 -0
- package/src/components/queue-surface-state.tsx +94 -0
- package/src/components/queue-surface.tsx +41 -559
- package/src/components/sandbox-files.tsx +138 -14
- package/src/components/sandbox-terminal.tsx +8 -2
- package/src/components/sandbox-workspace.tsx +339 -119
- package/src/components/workbench-changes.tsx +84 -33
- package/src/components/workspace-dock.tsx +188 -18
- package/src/composer.ts +60 -0
- package/src/hooks/internal.ts +115 -34
- package/src/hooks/use-composer.ts +335 -104
- package/src/hooks/use-human-input.ts +131 -0
- package/src/hooks/use-machine-chip.ts +2 -2
- package/src/hooks/use-machines.ts +27 -11
- package/src/hooks/use-sandbox-files.ts +310 -62
- package/src/hooks/use-sandbox-git.ts +154 -40
- package/src/hooks/use-sandbox-terminal.ts +28 -6
- package/src/hooks/use-session-capabilities.ts +38 -9
- package/src/hooks/use-session-control.ts +37 -8
- package/src/hooks/use-session-events.ts +522 -48
- package/src/hooks/use-slash-commands.ts +38 -38
- package/src/hooks/use-transcription.ts +757 -0
- package/src/hooks/use-turn-queue.ts +134 -66
- package/src/hooks/use-windowed-sections.ts +2 -2
- package/src/hooks/use-workspace-capture.ts +146 -40
- package/src/hooks/use-workspace-edit.ts +50 -14
- package/src/hooks/use-workspace-sessions.ts +3 -0
- package/src/human-input.ts +72 -0
- package/src/index.ts +76 -6
- package/src/lib/noto-sans-arabic-loader.ts +21 -0
- package/src/lib/noto-sans-jp-loader.generated.ts +295 -0
- package/src/lib/use-portal-token-style.ts +51 -0
- package/src/lib/use-unicode-fonts.ts +57 -0
- package/src/provider.tsx +4 -69
- package/src/session-context.ts +135 -0
- package/src/session.ts +79 -0
- package/src/timeline/parsers.ts +35 -0
- package/src/timeline/projection.ts +12 -4
- package/src/timeline/tool-renderers.tsx +41 -0
- package/src/timeline/turn-summary.tsx +2 -2
- package/src/timeline/types.ts +16 -8
- package/styles/index.css +56 -0
- package/styles/index.d.ts +2 -0
- package/styles/tokens.css +11 -6
- package/styles/tokens.d.ts +2 -0
- package/dist/chunk-TOJR776I.js.map +0 -1
- package/dist/machines-BpdwuQcD.d.ts +0 -449
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
import type { FsReadResponse } from "@opengeni/sdk";
|
|
2
1
|
import { FileCode2Icon, FileWarningIcon, LoaderCircleIcon } from "lucide-react";
|
|
3
2
|
import { type ReactNode, useCallback, useEffect, useRef, useState } from "react";
|
|
4
3
|
import { cn } from "../lib/cn";
|
|
5
4
|
import { useThemeType } from "../lib/use-theme-type";
|
|
6
|
-
import
|
|
5
|
+
import {
|
|
6
|
+
CapturedFileUnavailableError,
|
|
7
|
+
type UseSandboxFilesResult,
|
|
8
|
+
} from "../hooks/use-sandbox-files";
|
|
7
9
|
import type { UseSandboxGitResult } from "../hooks/use-sandbox-git";
|
|
8
10
|
import { CodeEditor } from "./code-editor";
|
|
9
11
|
import { FileBrowser } from "./file-browser";
|
|
@@ -36,6 +38,15 @@ export type SandboxFilesProps = {
|
|
|
36
38
|
requestedPathRequestId?: string | number | undefined;
|
|
37
39
|
/** False while the parent is waking a cold sandbox for `requestedPath`. */
|
|
38
40
|
requestedPathReady?: boolean | undefined;
|
|
41
|
+
/** The machine is cold and no durable capture exists yet. Render an explicit
|
|
42
|
+
* wake gate instead of an empty-tree lie or an implicit Channel-A read. */
|
|
43
|
+
workspaceResting?: boolean | undefined;
|
|
44
|
+
/** A deliberate wake has started but the live file surface is not ready yet. */
|
|
45
|
+
workspaceWaking?: boolean | undefined;
|
|
46
|
+
/** Whether live reads are currently authoritative. */
|
|
47
|
+
liveWorkspaceReady?: boolean | undefined;
|
|
48
|
+
/** Deliberately wake the machine to read content absent from the capture. */
|
|
49
|
+
onWakeWorkspace?: (() => void) | undefined;
|
|
39
50
|
themeType?: "dark" | "light" | undefined;
|
|
40
51
|
className?: string | undefined;
|
|
41
52
|
};
|
|
@@ -57,6 +68,10 @@ export function SandboxFiles({
|
|
|
57
68
|
requestedPath,
|
|
58
69
|
requestedPathRequestId,
|
|
59
70
|
requestedPathReady = true,
|
|
71
|
+
workspaceResting = false,
|
|
72
|
+
workspaceWaking = false,
|
|
73
|
+
liveWorkspaceReady = true,
|
|
74
|
+
onWakeWorkspace,
|
|
60
75
|
themeType,
|
|
61
76
|
className,
|
|
62
77
|
}: SandboxFilesProps) {
|
|
@@ -64,6 +79,8 @@ export function SandboxFiles({
|
|
|
64
79
|
// View vs Edit for the selected file. Resets to View on every new selection so
|
|
65
80
|
// opening a file never lands you in a stale dirty editor for a different path.
|
|
66
81
|
const [editMode, setEditMode] = useState(false);
|
|
82
|
+
const [liveRequestedPath, setLiveRequestedPath] = useState<string | null>(null);
|
|
83
|
+
const [viewReloadRevision, setViewReloadRevision] = useState(0);
|
|
67
84
|
const pendingRequestRef = useRef<string | number | null>(null);
|
|
68
85
|
const handledRequestRef = useRef<string | number | null>(null);
|
|
69
86
|
const requestKey = requestedPath ? (requestedPathRequestId ?? requestedPath) : null;
|
|
@@ -111,7 +128,7 @@ export function SandboxFiles({
|
|
|
111
128
|
// writable). Nothing is auto-selected — the pane waits for a tree click, so the
|
|
112
129
|
// Files tab opens as a calm browser, not a diff.
|
|
113
130
|
const viewPath = selected;
|
|
114
|
-
const fileView = useFileView(viewPath, files.readFile);
|
|
131
|
+
const fileView = useFileView(viewPath, files.readFile, viewReloadRevision);
|
|
115
132
|
|
|
116
133
|
// Selecting a (different) file always returns to View — never drop the user into
|
|
117
134
|
// an editor whose buffer belongs to the previously-selected path. Manual
|
|
@@ -124,6 +141,8 @@ export function SandboxFiles({
|
|
|
124
141
|
}
|
|
125
142
|
setSelected(path);
|
|
126
143
|
setEditMode(false);
|
|
144
|
+
setLiveRequestedPath(null);
|
|
145
|
+
setViewReloadRevision(0);
|
|
127
146
|
}, []);
|
|
128
147
|
|
|
129
148
|
// A tree file is editable only when it is a real, fully-loaded text file: not
|
|
@@ -139,6 +158,12 @@ export function SandboxFiles({
|
|
|
139
158
|
!fileView.truncated &&
|
|
140
159
|
fileView.content !== null;
|
|
141
160
|
const showEditor = canEdit && editMode;
|
|
161
|
+
const captureFileUnavailable =
|
|
162
|
+
fileView.error instanceof CapturedFileUnavailableError ? fileView.error : null;
|
|
163
|
+
const waitingForSelectedFile =
|
|
164
|
+
liveRequestedPath === viewPath &&
|
|
165
|
+
(!liveWorkspaceReady || files.loading) &&
|
|
166
|
+
captureFileUnavailable !== null;
|
|
142
167
|
|
|
143
168
|
if (!fileSystemAvailable) {
|
|
144
169
|
return (
|
|
@@ -146,12 +171,43 @@ export function SandboxFiles({
|
|
|
146
171
|
className={className}
|
|
147
172
|
icon={<FileWarningIcon className="size-5" aria-hidden />}
|
|
148
173
|
title="Files unavailable"
|
|
174
|
+
announce="alert"
|
|
149
175
|
>
|
|
150
176
|
This sandbox does not expose a file system.
|
|
151
177
|
</Notice>
|
|
152
178
|
);
|
|
153
179
|
}
|
|
154
180
|
|
|
181
|
+
if (workspaceResting || workspaceWaking) {
|
|
182
|
+
return (
|
|
183
|
+
<div className={cn("h-full", className)} data-opengeni-workspace-resting>
|
|
184
|
+
<Notice
|
|
185
|
+
icon={
|
|
186
|
+
workspaceWaking ? (
|
|
187
|
+
<LoaderCircleIcon
|
|
188
|
+
className="size-5 animate-spin motion-reduce:animate-none"
|
|
189
|
+
aria-hidden
|
|
190
|
+
/>
|
|
191
|
+
) : (
|
|
192
|
+
<FileCode2Icon className="size-5" aria-hidden />
|
|
193
|
+
)
|
|
194
|
+
}
|
|
195
|
+
title={workspaceWaking ? "Waking workspace" : "Workspace is resting"}
|
|
196
|
+
announce="status"
|
|
197
|
+
>
|
|
198
|
+
<p>
|
|
199
|
+
{workspaceWaking
|
|
200
|
+
? "Connecting to the live file system…"
|
|
201
|
+
: "No captured revision is available yet. Wake the sandbox to browse its current files."}
|
|
202
|
+
</p>
|
|
203
|
+
{!workspaceWaking && onWakeWorkspace ? (
|
|
204
|
+
<WakeButton onClick={onWakeWorkspace}>Open live workspace</WakeButton>
|
|
205
|
+
) : null}
|
|
206
|
+
</Notice>
|
|
207
|
+
</div>
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
|
|
155
211
|
return (
|
|
156
212
|
<div ref={rootRef} className={cn("flex h-full min-h-0 min-w-0 flex-col", className)}>
|
|
157
213
|
{/* Branch + dirty header (context; NOT the changed-files list). */}
|
|
@@ -214,16 +270,54 @@ export function SandboxFiles({
|
|
|
214
270
|
path={viewPath}
|
|
215
271
|
initialContents={fileView.content}
|
|
216
272
|
themeType={resolvedTheme}
|
|
217
|
-
onSave={(contents) =>
|
|
273
|
+
onSave={(contents) =>
|
|
274
|
+
files.writeFile(viewPath, contents, { expectedContent: fileView.content! })
|
|
275
|
+
}
|
|
276
|
+
onOverwrite={(contents) => files.writeFile(viewPath, contents, { force: true })}
|
|
277
|
+
onReload={() => setViewReloadRevision((revision) => revision + 1)}
|
|
218
278
|
{...(onEditIntent ? { onEditIntent } : {})}
|
|
219
279
|
className="h-full"
|
|
220
280
|
/>
|
|
281
|
+
) : waitingForSelectedFile ? (
|
|
282
|
+
<Notice
|
|
283
|
+
icon={
|
|
284
|
+
<LoaderCircleIcon
|
|
285
|
+
className="size-5 animate-spin motion-reduce:animate-none"
|
|
286
|
+
aria-hidden
|
|
287
|
+
/>
|
|
288
|
+
}
|
|
289
|
+
title="Waking workspace"
|
|
290
|
+
announce="status"
|
|
291
|
+
>
|
|
292
|
+
Opening {viewPath} when the live file system is ready…
|
|
293
|
+
</Notice>
|
|
294
|
+
) : captureFileUnavailable ? (
|
|
295
|
+
<Notice icon={<FileCode2Icon className="size-5" aria-hidden />} title="On machine">
|
|
296
|
+
<p>
|
|
297
|
+
{captureFileUnavailable.reason === "too-large"
|
|
298
|
+
? "This file is larger than the captured preview limit."
|
|
299
|
+
: captureFileUnavailable.reason === "content-missing"
|
|
300
|
+
? "The captured copy is no longer available."
|
|
301
|
+
: "This file was indexed, but it was not changed in the captured turn."}
|
|
302
|
+
</p>
|
|
303
|
+
{onWakeWorkspace ? (
|
|
304
|
+
<WakeButton
|
|
305
|
+
onClick={() => {
|
|
306
|
+
setLiveRequestedPath(viewPath);
|
|
307
|
+
if (liveWorkspaceReady) void files.refresh();
|
|
308
|
+
else onWakeWorkspace();
|
|
309
|
+
}}
|
|
310
|
+
>
|
|
311
|
+
{liveWorkspaceReady ? "Retry live file" : "Open live file"}
|
|
312
|
+
</WakeButton>
|
|
313
|
+
) : null}
|
|
314
|
+
</Notice>
|
|
221
315
|
) : fileView.error ? (
|
|
222
|
-
<Notice>
|
|
316
|
+
<Notice announce="alert">
|
|
223
317
|
Could not open {viewPath}: {fileView.error.message}
|
|
224
318
|
</Notice>
|
|
225
319
|
) : fileView.loading ? (
|
|
226
|
-
<Notice>Loading {viewPath}…</Notice>
|
|
320
|
+
<Notice announce="status">Loading {viewPath}…</Notice>
|
|
227
321
|
) : fileView.isBinary ? (
|
|
228
322
|
<Notice>
|
|
229
323
|
{viewPath} is a binary file ({fileView.sizeBytes ?? 0} bytes).
|
|
@@ -255,7 +349,7 @@ export function SandboxFiles({
|
|
|
255
349
|
)}
|
|
256
350
|
</>
|
|
257
351
|
) : (
|
|
258
|
-
<Notice>Loading {viewPath}…</Notice>
|
|
352
|
+
<Notice announce="status">Loading {viewPath}…</Notice>
|
|
259
353
|
)
|
|
260
354
|
) : // Nothing selected — the tree shows the whole workspace; pick a file.
|
|
261
355
|
requestedPath && !requestedPathReady ? (
|
|
@@ -267,6 +361,7 @@ export function SandboxFiles({
|
|
|
267
361
|
/>
|
|
268
362
|
}
|
|
269
363
|
title="Waking sandbox"
|
|
364
|
+
announce="status"
|
|
270
365
|
>
|
|
271
366
|
Opening {requestedPath} when the live workspace is ready…
|
|
272
367
|
</Notice>
|
|
@@ -282,19 +377,36 @@ export function SandboxFiles({
|
|
|
282
377
|
);
|
|
283
378
|
}
|
|
284
379
|
|
|
380
|
+
function WakeButton({ children, onClick }: { children: ReactNode; onClick: () => void }) {
|
|
381
|
+
return (
|
|
382
|
+
<button
|
|
383
|
+
type="button"
|
|
384
|
+
onClick={onClick}
|
|
385
|
+
className="mt-1 inline-flex min-h-11 items-center justify-center rounded-og-md bg-og-accent px-3 py-2 text-og-sm font-medium text-og-on-accent shadow-sm transition-colors hover:bg-og-accent-hover focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-og-accent focus-visible:ring-offset-2 focus-visible:ring-offset-og-bg"
|
|
386
|
+
>
|
|
387
|
+
{children}
|
|
388
|
+
</button>
|
|
389
|
+
);
|
|
390
|
+
}
|
|
391
|
+
|
|
285
392
|
function GitHeader({ git, dirtyCount }: { git: UseSandboxGitResult; dirtyCount: number }) {
|
|
286
393
|
const dirty = dirtyCount > 0;
|
|
287
394
|
return (
|
|
288
395
|
<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">
|
|
289
396
|
<span
|
|
397
|
+
aria-hidden="true"
|
|
290
398
|
className={cn(
|
|
291
399
|
"size-2 shrink-0 rounded-full",
|
|
292
400
|
dirty ? "bg-og-status-running" : "bg-og-status-idle",
|
|
293
401
|
)}
|
|
294
|
-
title={dirty ? `${dirtyCount} changed` : "clean"}
|
|
295
402
|
/>
|
|
403
|
+
<span className="sr-only">
|
|
404
|
+
{dirty ? `Working tree has ${dirtyCount} changed files` : "Working tree clean"}
|
|
405
|
+
</span>
|
|
296
406
|
<span className="truncate font-og-mono text-og-fg">
|
|
297
|
-
{git.
|
|
407
|
+
{git.repoCount > 1
|
|
408
|
+
? `${git.repoCount} repositories`
|
|
409
|
+
: (git.branch ?? (git.isRepo ? "(detached)" : "no repo"))}
|
|
298
410
|
</span>
|
|
299
411
|
{(git.ahead > 0 || git.behind > 0) && (
|
|
300
412
|
<span className="flex shrink-0 items-center gap-1.5 text-og-xs text-og-fg-subtle">
|
|
@@ -303,7 +415,11 @@ function GitHeader({ git, dirtyCount }: { git: UseSandboxGitResult; dirtyCount:
|
|
|
303
415
|
</span>
|
|
304
416
|
)}
|
|
305
417
|
{dirty && (
|
|
306
|
-
<span
|
|
418
|
+
<span
|
|
419
|
+
aria-hidden="true"
|
|
420
|
+
data-contrast-audited
|
|
421
|
+
className="ml-auto shrink-0 text-og-xs text-og-fg-subtle"
|
|
422
|
+
>
|
|
307
423
|
{dirtyCount} changed
|
|
308
424
|
</span>
|
|
309
425
|
)}
|
|
@@ -328,7 +444,7 @@ function Segmented({
|
|
|
328
444
|
type="button"
|
|
329
445
|
onClick={() => onChange(opt.value)}
|
|
330
446
|
className={cn(
|
|
331
|
-
"min-h-7 rounded-og-xs px-1.5 py-0.5 text-og-xs focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-og-accent max-[1023px]:min-h-11 pointer-coarse:min-h-11",
|
|
447
|
+
"min-h-7 rounded-og-xs px-1.5 py-0.5 text-og-xs focus-visible:outline-none 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",
|
|
332
448
|
opt.value === value
|
|
333
449
|
? "bg-og-accent-soft text-og-fg"
|
|
334
450
|
: "text-og-fg-subtle hover:text-og-fg",
|
|
@@ -361,7 +477,8 @@ type FileViewState = {
|
|
|
361
477
|
*/
|
|
362
478
|
function useFileView(
|
|
363
479
|
path: string | null,
|
|
364
|
-
readFile:
|
|
480
|
+
readFile: UseSandboxFilesResult["readFile"],
|
|
481
|
+
reloadRevision = 0,
|
|
365
482
|
): FileViewState {
|
|
366
483
|
const [state, setState] = useState<FileViewState>({
|
|
367
484
|
content: null,
|
|
@@ -384,6 +501,7 @@ function useFileView(
|
|
|
384
501
|
return;
|
|
385
502
|
}
|
|
386
503
|
let cancelled = false;
|
|
504
|
+
const abort = new AbortController();
|
|
387
505
|
setState({
|
|
388
506
|
content: null,
|
|
389
507
|
isBinary: false,
|
|
@@ -392,7 +510,7 @@ function useFileView(
|
|
|
392
510
|
loading: true,
|
|
393
511
|
error: null,
|
|
394
512
|
});
|
|
395
|
-
void readFile(path)
|
|
513
|
+
void readFile(path, { signal: abort.signal })
|
|
396
514
|
.then((res) => {
|
|
397
515
|
if (cancelled) return;
|
|
398
516
|
const content = res.isBinary
|
|
@@ -422,8 +540,9 @@ function useFileView(
|
|
|
422
540
|
});
|
|
423
541
|
return () => {
|
|
424
542
|
cancelled = true;
|
|
543
|
+
abort.abort();
|
|
425
544
|
};
|
|
426
|
-
}, [path, readFile]);
|
|
545
|
+
}, [path, readFile, reloadRevision]);
|
|
427
546
|
return state;
|
|
428
547
|
}
|
|
429
548
|
|
|
@@ -447,14 +566,19 @@ function Notice({
|
|
|
447
566
|
className,
|
|
448
567
|
icon,
|
|
449
568
|
title,
|
|
569
|
+
announce,
|
|
450
570
|
}: {
|
|
451
571
|
children: ReactNode;
|
|
452
572
|
className?: string | undefined;
|
|
453
573
|
icon?: ReactNode | undefined;
|
|
454
574
|
title?: string | undefined;
|
|
575
|
+
announce?: "status" | "alert" | undefined;
|
|
455
576
|
}) {
|
|
456
577
|
return (
|
|
457
578
|
<div
|
|
579
|
+
role={announce}
|
|
580
|
+
aria-atomic={announce ? "true" : undefined}
|
|
581
|
+
aria-live={announce === "alert" ? "assertive" : announce === "status" ? "polite" : undefined}
|
|
458
582
|
className={cn(
|
|
459
583
|
"flex h-full items-center justify-center p-4 text-center text-og-sm text-og-fg-subtle",
|
|
460
584
|
className,
|
|
@@ -221,7 +221,13 @@ export function SandboxTerminal({
|
|
|
221
221
|
// status lines INSIDE xterm instead of an overlay — but only when there is no
|
|
222
222
|
// firehose transcript to show yet (otherwise the projected output is shown).
|
|
223
223
|
const warm = liveness === "warm" || liveness === "draining";
|
|
224
|
-
const booting =
|
|
224
|
+
const booting =
|
|
225
|
+
activated &&
|
|
226
|
+
!ptyMode &&
|
|
227
|
+
!warm &&
|
|
228
|
+
liveness !== null &&
|
|
229
|
+
liveness !== undefined &&
|
|
230
|
+
result.chunks.length === 0;
|
|
225
231
|
|
|
226
232
|
function handleActivate() {
|
|
227
233
|
if (!activated) setActivated(true);
|
|
@@ -502,7 +508,7 @@ export function SandboxTerminal({
|
|
|
502
508
|
writtenRef.current = new Set();
|
|
503
509
|
wroteFirehoseRef.current = false;
|
|
504
510
|
}}
|
|
505
|
-
className="rounded-og-sm px-1.5 py-0.5 text-og-xs hover:text-og-fg pointer-coarse:min-h-
|
|
511
|
+
className="rounded-og-sm px-1.5 py-0.5 text-og-xs hover:text-og-fg pointer-coarse:min-h-11"
|
|
506
512
|
>
|
|
507
513
|
Clear
|
|
508
514
|
</button>
|