@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
package/dist/index.js
CHANGED
|
@@ -1,58 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
CONNECTION_STATUS_META,
|
|
3
|
-
ConnectionDot,
|
|
4
|
-
ConnectionStatusPill,
|
|
5
|
-
EnrollmentConsent,
|
|
6
|
-
EnrollmentDeviceFlow,
|
|
7
|
-
HEALTH_TOKEN,
|
|
8
|
-
MACHINE_STATE_BADGE_META,
|
|
9
|
-
METRICS,
|
|
10
|
-
METRIC_WINDOWS,
|
|
11
|
-
MachineCard,
|
|
12
|
-
MachineDetail,
|
|
13
|
-
MachineDockBar,
|
|
14
|
-
MachineHealthPill,
|
|
15
|
-
MachineMetrics,
|
|
16
|
-
MachineStatusPill,
|
|
17
|
-
MachinesDashboard,
|
|
18
|
-
MetricHistoryChart,
|
|
19
|
-
MetricSparkline,
|
|
20
|
-
OpenGeniProvider,
|
|
21
|
-
SharedMachineDisclosure,
|
|
22
|
-
WINDOW_LABEL,
|
|
23
|
-
connectionStatusForState,
|
|
24
|
-
deriveHealth,
|
|
25
|
-
healthPulses,
|
|
26
|
-
pointsFor,
|
|
27
|
-
useMachines
|
|
28
|
-
} from "./chunk-6EDV6YFE.js";
|
|
29
|
-
import {
|
|
30
|
-
SESSION_EVENT_BROWSER_MAX_BYTES,
|
|
31
|
-
SESSION_EVENT_BROWSER_MAX_COUNT,
|
|
32
|
-
SESSION_EVENT_BROWSER_PENDING_MAX_BYTES,
|
|
33
|
-
SESSION_EVENT_BROWSER_PENDING_MAX_COUNT,
|
|
34
|
-
SESSION_EVENT_BROWSER_SINGLE_EVENT_MAX_BYTES,
|
|
35
|
-
approvalsFromRequiresAction,
|
|
36
|
-
boundBrowserSessionEventWindow,
|
|
37
|
-
humanInputRequestFromEvent,
|
|
38
|
-
isGoalEvent,
|
|
39
|
-
isHumanInputEvent,
|
|
40
|
-
isLineageRefreshEvent,
|
|
41
|
-
isSessionMcpApprovalPolicyEvent,
|
|
42
|
-
isTitleEvent,
|
|
43
|
-
isTurnQueueEvent,
|
|
44
|
-
projectPendingApprovals,
|
|
45
|
-
projectPendingHumanInputRequests,
|
|
46
|
-
useFileAttachments,
|
|
47
|
-
useGoal,
|
|
48
|
-
useHumanInputRequests,
|
|
49
|
-
useSession,
|
|
50
|
-
useSessionControl,
|
|
51
|
-
useSessionEvents,
|
|
52
|
-
useSessionLineage,
|
|
53
|
-
useSessionMcpApprovalPolicy,
|
|
54
|
-
useTurnQueue
|
|
55
|
-
} from "./chunk-SRFUT2ZU.js";
|
|
56
1
|
import {
|
|
57
2
|
ActivityDisclosure,
|
|
58
3
|
ActivityRail,
|
|
@@ -61,6 +6,7 @@ import {
|
|
|
61
6
|
CopyButton,
|
|
62
7
|
CopyHoverFrame,
|
|
63
8
|
DisclosureDefaultsProvider,
|
|
9
|
+
GeneratedVideoPlayer,
|
|
64
10
|
HumanInputForm,
|
|
65
11
|
HumanInputSurface,
|
|
66
12
|
LightboxProvider,
|
|
@@ -85,7 +31,6 @@ import {
|
|
|
85
31
|
applyPatchOps,
|
|
86
32
|
applyPatchOpsFromToolItem,
|
|
87
33
|
controlCaret,
|
|
88
|
-
copyTextToClipboard,
|
|
89
34
|
createDefaultToolRegistry,
|
|
90
35
|
createToolRegistry,
|
|
91
36
|
defaultHumanInputFormMessages,
|
|
@@ -99,11 +44,9 @@ import {
|
|
|
99
44
|
parseFreeformApplyPatch,
|
|
100
45
|
parseToolArgs,
|
|
101
46
|
rawTypeOf,
|
|
102
|
-
redactSecrets,
|
|
103
47
|
sandboxCommandExitCode,
|
|
104
48
|
sessionChromeGoalPillState,
|
|
105
49
|
stripExecBanner,
|
|
106
|
-
tableElementToTsv,
|
|
107
50
|
tailPeek,
|
|
108
51
|
unwrapMcpOutput,
|
|
109
52
|
useLightbox,
|
|
@@ -111,16 +54,7 @@ import {
|
|
|
111
54
|
useThemeType,
|
|
112
55
|
userMessageLikelyNeedsDisclosure,
|
|
113
56
|
v4aToGitFileDiff
|
|
114
|
-
} from "./chunk-
|
|
115
|
-
import "./chunk-VCBFUSUZ.js";
|
|
116
|
-
import {
|
|
117
|
-
buildTimeline,
|
|
118
|
-
creditExhaustedFromEvents,
|
|
119
|
-
extractSessionRef,
|
|
120
|
-
groupTimeline,
|
|
121
|
-
sessionStatusFromEvents,
|
|
122
|
-
toolDisplayName
|
|
123
|
-
} from "./chunk-LWR4MXSS.js";
|
|
57
|
+
} from "./chunk-JAGDUCZQ.js";
|
|
124
58
|
import {
|
|
125
59
|
Actions,
|
|
126
60
|
AttachButton,
|
|
@@ -173,19 +107,7 @@ import {
|
|
|
173
107
|
useSlashCommands,
|
|
174
108
|
useTranscription,
|
|
175
109
|
useVoiceInput
|
|
176
|
-
} from "./chunk-
|
|
177
|
-
import {
|
|
178
|
-
FILE_ONLY_MESSAGE_TEXT,
|
|
179
|
-
composeSendInput,
|
|
180
|
-
shouldSteerOnKey,
|
|
181
|
-
shouldSubmitOnKey,
|
|
182
|
-
useComposer
|
|
183
|
-
} from "./chunk-HFO4ERGQ.js";
|
|
184
|
-
import {
|
|
185
|
-
useMutationRunner,
|
|
186
|
-
usePolledValue,
|
|
187
|
-
useSessionEventTrigger
|
|
188
|
-
} from "./chunk-EB67J347.js";
|
|
110
|
+
} from "./chunk-ROIRTM37.js";
|
|
189
111
|
import {
|
|
190
112
|
BillingClassMark,
|
|
191
113
|
ModelPolicyPicker,
|
|
@@ -194,11 +116,7 @@ import {
|
|
|
194
116
|
PickerBackHeader,
|
|
195
117
|
PickerNavRow,
|
|
196
118
|
defaultModelPolicyPickerMessages
|
|
197
|
-
} from "./chunk-
|
|
198
|
-
import {
|
|
199
|
-
useOpenGeni,
|
|
200
|
-
useOpenGeniClient
|
|
201
|
-
} from "./chunk-EKZH6IDG.js";
|
|
119
|
+
} from "./chunk-WYA65Y3F.js";
|
|
202
120
|
import {
|
|
203
121
|
advancedSourceSummary,
|
|
204
122
|
availabilityReasonLabel,
|
|
@@ -222,7 +140,84 @@ import {
|
|
|
222
140
|
TooltipContent,
|
|
223
141
|
TooltipProvider,
|
|
224
142
|
TooltipTrigger
|
|
225
|
-
} from "./chunk-
|
|
143
|
+
} from "./chunk-FK6VG4LL.js";
|
|
144
|
+
import {
|
|
145
|
+
TimelineAnnotationsChip
|
|
146
|
+
} from "./chunk-WSK7G5LE.js";
|
|
147
|
+
import {
|
|
148
|
+
usePortalTokenSource,
|
|
149
|
+
usePortalTokenStyle
|
|
150
|
+
} from "./chunk-VZTQ73XT.js";
|
|
151
|
+
import {
|
|
152
|
+
CONNECTION_STATUS_META,
|
|
153
|
+
ConnectionDot,
|
|
154
|
+
ConnectionStatusPill,
|
|
155
|
+
EnrollmentConsent,
|
|
156
|
+
EnrollmentDeviceFlow,
|
|
157
|
+
HEALTH_TOKEN,
|
|
158
|
+
MACHINE_STATE_BADGE_META,
|
|
159
|
+
METRICS,
|
|
160
|
+
METRIC_WINDOWS,
|
|
161
|
+
MachineCard,
|
|
162
|
+
MachineDetail,
|
|
163
|
+
MachineDockBar,
|
|
164
|
+
MachineHealthPill,
|
|
165
|
+
MachineMetrics,
|
|
166
|
+
MachineStatusPill,
|
|
167
|
+
MachinesDashboard,
|
|
168
|
+
MetricHistoryChart,
|
|
169
|
+
MetricSparkline,
|
|
170
|
+
OpenGeniProvider,
|
|
171
|
+
SharedMachineDisclosure,
|
|
172
|
+
WINDOW_LABEL,
|
|
173
|
+
connectionStatusForState,
|
|
174
|
+
deriveHealth,
|
|
175
|
+
healthPulses,
|
|
176
|
+
pointsFor,
|
|
177
|
+
useMachines
|
|
178
|
+
} from "./chunk-X4S56Y2H.js";
|
|
179
|
+
import {
|
|
180
|
+
SESSION_EVENT_BROWSER_MAX_BYTES,
|
|
181
|
+
SESSION_EVENT_BROWSER_MAX_COUNT,
|
|
182
|
+
SESSION_EVENT_BROWSER_PENDING_MAX_BYTES,
|
|
183
|
+
SESSION_EVENT_BROWSER_PENDING_MAX_COUNT,
|
|
184
|
+
SESSION_EVENT_BROWSER_SINGLE_EVENT_MAX_BYTES,
|
|
185
|
+
approvalsFromRequiresAction,
|
|
186
|
+
boundBrowserSessionEventWindow,
|
|
187
|
+
humanInputRequestFromEvent,
|
|
188
|
+
isGoalEvent,
|
|
189
|
+
isHumanInputEvent,
|
|
190
|
+
isLineageRefreshEvent,
|
|
191
|
+
isSessionMcpApprovalPolicyEvent,
|
|
192
|
+
isTitleEvent,
|
|
193
|
+
isTurnQueueEvent,
|
|
194
|
+
projectPendingApprovals,
|
|
195
|
+
projectPendingHumanInputRequests,
|
|
196
|
+
useFileAttachments,
|
|
197
|
+
useGoal,
|
|
198
|
+
useHumanInputRequests,
|
|
199
|
+
useSession,
|
|
200
|
+
useSessionControl,
|
|
201
|
+
useSessionEvents,
|
|
202
|
+
useSessionLineage,
|
|
203
|
+
useSessionMcpApprovalPolicy,
|
|
204
|
+
useTurnQueue
|
|
205
|
+
} from "./chunk-FWS3KC3Z.js";
|
|
206
|
+
import {
|
|
207
|
+
buildTimeline,
|
|
208
|
+
creditExhaustedFromEvents,
|
|
209
|
+
extractSessionRef,
|
|
210
|
+
groupTimeline,
|
|
211
|
+
sessionStatusFromEvents,
|
|
212
|
+
toolDisplayName
|
|
213
|
+
} from "./chunk-JR3QFY6B.js";
|
|
214
|
+
import {
|
|
215
|
+
FILE_ONLY_MESSAGE_TEXT,
|
|
216
|
+
composeSendInput,
|
|
217
|
+
shouldSteerOnKey,
|
|
218
|
+
shouldSubmitOnKey,
|
|
219
|
+
useComposer
|
|
220
|
+
} from "./chunk-724BW4TW.js";
|
|
226
221
|
import {
|
|
227
222
|
COMPOSER_PAYMENT_REQUIRED_MESSAGE,
|
|
228
223
|
CREDIT_EXHAUSTION_MESSAGE,
|
|
@@ -237,11 +232,48 @@ import {
|
|
|
237
232
|
tryParseJson
|
|
238
233
|
} from "./chunk-LAKLF4PA.js";
|
|
239
234
|
import {
|
|
240
|
-
|
|
241
|
-
|
|
235
|
+
STREAM_KIND_DESKTOP,
|
|
236
|
+
STREAM_ROLE_CLIENT,
|
|
237
|
+
decodeStreamFrame,
|
|
238
|
+
decodeStreamOpenAck,
|
|
239
|
+
encodeStreamOpen
|
|
240
|
+
} from "./chunk-6BBWWOOU.js";
|
|
241
|
+
import {
|
|
242
|
+
useMutationRunner,
|
|
243
|
+
usePageLiveActivity,
|
|
244
|
+
usePolledValue,
|
|
245
|
+
useSessionEventTrigger
|
|
246
|
+
} from "./chunk-KZ73RL76.js";
|
|
247
|
+
import {
|
|
248
|
+
useOpenGeni,
|
|
249
|
+
useOpenGeniClient
|
|
250
|
+
} from "./chunk-HDBG7XGO.js";
|
|
242
251
|
import {
|
|
243
252
|
cn
|
|
244
253
|
} from "./chunk-22KP6LR5.js";
|
|
254
|
+
import {
|
|
255
|
+
copyTextToClipboard,
|
|
256
|
+
tableElementToTsv
|
|
257
|
+
} from "./chunk-JVYQRRC4.js";
|
|
258
|
+
|
|
259
|
+
// src/hooks/use-video-artifact-playback.ts
|
|
260
|
+
import { useMemo } from "react";
|
|
261
|
+
function useVideoArtifactPlaybackLoader(options = {}) {
|
|
262
|
+
const { client, workspaceId } = useOpenGeni(options);
|
|
263
|
+
return useMemo(
|
|
264
|
+
() => async (artifactId, signal) => {
|
|
265
|
+
if (typeof client.createVideoArtifactPlaybackSource !== "function") {
|
|
266
|
+
throw new Error("This OpenGeni host does not expose retained-video playback.");
|
|
267
|
+
}
|
|
268
|
+
return await client.createVideoArtifactPlaybackSource(
|
|
269
|
+
workspaceId,
|
|
270
|
+
artifactId,
|
|
271
|
+
signal ? { signal } : {}
|
|
272
|
+
);
|
|
273
|
+
},
|
|
274
|
+
[client, workspaceId]
|
|
275
|
+
);
|
|
276
|
+
}
|
|
245
277
|
|
|
246
278
|
// src/hooks/use-last-started-turn-policy.ts
|
|
247
279
|
import { useCallback, useEffect } from "react";
|
|
@@ -378,7 +410,7 @@ function useWorkspaceSessions(options = {}) {
|
|
|
378
410
|
}
|
|
379
411
|
|
|
380
412
|
// src/hooks/use-environments.ts
|
|
381
|
-
import { useCallback as useCallback4, useMemo } from "react";
|
|
413
|
+
import { useCallback as useCallback4, useMemo as useMemo2 } from "react";
|
|
382
414
|
function useVariableSets(options = {}) {
|
|
383
415
|
const { client, workspaceId } = useOpenGeni(options);
|
|
384
416
|
const load = useCallback4(
|
|
@@ -423,6 +455,10 @@ function useVariableSets(options = {}) {
|
|
|
423
455
|
},
|
|
424
456
|
[client, workspaceId, run, refresh]
|
|
425
457
|
);
|
|
458
|
+
const readVariable = useCallback4(
|
|
459
|
+
async (variableSetId, name) => await run(() => client.getVariableSetVariable(workspaceId, variableSetId, name)),
|
|
460
|
+
[client, workspaceId, run]
|
|
461
|
+
);
|
|
426
462
|
const setVariable = useCallback4(
|
|
427
463
|
async (variableSetId, name, value) => {
|
|
428
464
|
const result = await run(
|
|
@@ -456,6 +492,7 @@ function useVariableSets(options = {}) {
|
|
|
456
492
|
create,
|
|
457
493
|
update,
|
|
458
494
|
remove,
|
|
495
|
+
readVariable,
|
|
459
496
|
setVariable,
|
|
460
497
|
deleteVariable,
|
|
461
498
|
mutating,
|
|
@@ -464,7 +501,7 @@ function useVariableSets(options = {}) {
|
|
|
464
501
|
};
|
|
465
502
|
}
|
|
466
503
|
function useEnvironments(options = {}) {
|
|
467
|
-
const legacyClient =
|
|
504
|
+
const legacyClient = useMemo2(() => {
|
|
468
505
|
if (!options.client) {
|
|
469
506
|
return void 0;
|
|
470
507
|
}
|
|
@@ -478,7 +515,10 @@ function useEnvironments(options = {}) {
|
|
|
478
515
|
deleteVariableSetVariable: options.client.deleteEnvironmentVariable ?? options.client.deleteVariableSetVariable
|
|
479
516
|
};
|
|
480
517
|
}, [options.client]);
|
|
481
|
-
const result = useVariableSets({
|
|
518
|
+
const result = useVariableSets({
|
|
519
|
+
...options,
|
|
520
|
+
...legacyClient ? { client: legacyClient } : {}
|
|
521
|
+
});
|
|
482
522
|
return { ...result, environments: result.variableSets };
|
|
483
523
|
}
|
|
484
524
|
|
|
@@ -728,6 +768,30 @@ function usePacks(options = {}) {
|
|
|
728
768
|
},
|
|
729
769
|
[client, workspaceId, run, refresh]
|
|
730
770
|
);
|
|
771
|
+
const previewInstallation = useCallback6(
|
|
772
|
+
async (packId, request = {}) => await run(() => client.previewPackInstallation(workspaceId, packId, request)),
|
|
773
|
+
[client, workspaceId, run]
|
|
774
|
+
);
|
|
775
|
+
const install = useCallback6(
|
|
776
|
+
async (packId, request) => {
|
|
777
|
+
const result = await run(() => client.installPack(workspaceId, packId, request));
|
|
778
|
+
if (result) await refresh();
|
|
779
|
+
return result;
|
|
780
|
+
},
|
|
781
|
+
[client, workspaceId, run, refresh]
|
|
782
|
+
);
|
|
783
|
+
const previewUninstall = useCallback6(
|
|
784
|
+
async (packId) => await run(() => client.previewPackUninstall(workspaceId, packId)),
|
|
785
|
+
[client, workspaceId, run]
|
|
786
|
+
);
|
|
787
|
+
const uninstall = useCallback6(
|
|
788
|
+
async (packId, request) => {
|
|
789
|
+
const result = await run(() => client.uninstallPack(workspaceId, packId, request));
|
|
790
|
+
if (result) await refresh();
|
|
791
|
+
return result;
|
|
792
|
+
},
|
|
793
|
+
[client, workspaceId, run, refresh]
|
|
794
|
+
);
|
|
731
795
|
const remove = useCallback6(
|
|
732
796
|
async (packId) => {
|
|
733
797
|
const result = await run(async () => {
|
|
@@ -755,6 +819,10 @@ function usePacks(options = {}) {
|
|
|
755
819
|
refresh,
|
|
756
820
|
register,
|
|
757
821
|
enable,
|
|
822
|
+
previewInstallation,
|
|
823
|
+
install,
|
|
824
|
+
previewUninstall,
|
|
825
|
+
uninstall,
|
|
758
826
|
remove,
|
|
759
827
|
mutating,
|
|
760
828
|
mutationError,
|
|
@@ -884,16 +952,24 @@ import {
|
|
|
884
952
|
applyUrlRotation
|
|
885
953
|
} from "@opengeni/sdk";
|
|
886
954
|
import { useCallback as useCallback10, useEffect as useEffect3, useRef as useRef2, useState } from "react";
|
|
955
|
+
|
|
956
|
+
// src/lib/sandbox-liveness.ts
|
|
957
|
+
function sandboxAcceptsLiveIo(liveness) {
|
|
958
|
+
return liveness === "warm";
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
// src/lib/terminal-capability.ts
|
|
962
|
+
function terminalCanAcquirePty(cell) {
|
|
963
|
+
if (!cell?.ptyCapable) return false;
|
|
964
|
+
if (cell.transport === "pty-ws") return true;
|
|
965
|
+
return cell.reason === "lease_cold" || cell.reason === "not_provisioned";
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
// src/hooks/use-session-capabilities.ts
|
|
887
969
|
function desktopAttachable(cell) {
|
|
888
970
|
if (cell.transport !== null) return true;
|
|
889
971
|
return cell.reason === "lease_cold" || cell.reason === "not_provisioned" || cell.reason === null;
|
|
890
972
|
}
|
|
891
|
-
function terminalAttachable(cell) {
|
|
892
|
-
if (cell.transport === null) {
|
|
893
|
-
return cell.reason === "lease_cold" || cell.reason === "not_provisioned" || cell.reason === null;
|
|
894
|
-
}
|
|
895
|
-
return true;
|
|
896
|
-
}
|
|
897
973
|
function rotationsFrom(events) {
|
|
898
974
|
const out = [];
|
|
899
975
|
for (const event of events) {
|
|
@@ -906,6 +982,8 @@ function rotationsFrom(events) {
|
|
|
906
982
|
function useSessionCapabilities(sessionId, options = {}) {
|
|
907
983
|
const { client, workspaceId } = useOpenGeni(options);
|
|
908
984
|
const enabled = (options.enabled ?? true) && Boolean(sessionId);
|
|
985
|
+
const pageLive = usePageLiveActivity();
|
|
986
|
+
const lifecycleEnabled = enabled && pageLive;
|
|
909
987
|
const attachDesktop = options.attachDesktop ?? false;
|
|
910
988
|
const attachTerminal = options.attachTerminal ?? false;
|
|
911
989
|
const attachFiles = options.attachFiles ?? false;
|
|
@@ -940,11 +1018,15 @@ function useSessionCapabilities(sessionId, options = {}) {
|
|
|
940
1018
|
epochRef.current = 0;
|
|
941
1019
|
viewerIdRef.current = null;
|
|
942
1020
|
}
|
|
943
|
-
if (!
|
|
1021
|
+
if (!lifecycleEnabled || !sessionId) {
|
|
944
1022
|
setState("idle");
|
|
945
1023
|
setCapabilities(null);
|
|
1024
|
+
viewerIdRef.current = null;
|
|
1025
|
+
setViewerId(null);
|
|
946
1026
|
return;
|
|
947
1027
|
}
|
|
1028
|
+
viewerIdRef.current = null;
|
|
1029
|
+
setViewerId(null);
|
|
948
1030
|
let cancelled = false;
|
|
949
1031
|
let pollTimer = null;
|
|
950
1032
|
let heartbeatTimer = null;
|
|
@@ -953,7 +1035,7 @@ function useSessionCapabilities(sessionId, options = {}) {
|
|
|
953
1035
|
const startedAt = Date.now();
|
|
954
1036
|
const clearTimers = () => {
|
|
955
1037
|
if (pollTimer !== null) clearTimeout(pollTimer);
|
|
956
|
-
if (heartbeatTimer !== null)
|
|
1038
|
+
if (heartbeatTimer !== null) clearTimeout(heartbeatTimer);
|
|
957
1039
|
pollTimer = null;
|
|
958
1040
|
heartbeatTimer = null;
|
|
959
1041
|
};
|
|
@@ -966,19 +1048,26 @@ function useSessionCapabilities(sessionId, options = {}) {
|
|
|
966
1048
|
const startHeartbeat = (caps) => {
|
|
967
1049
|
if (!localViewerId || heartbeatTimer !== null) return;
|
|
968
1050
|
const intervalMs = caps.viewerHeartbeatIntervalMs > 0 ? caps.viewerHeartbeatIntervalMs : 3e4;
|
|
969
|
-
|
|
1051
|
+
const schedule = () => {
|
|
970
1052
|
if (cancelled || !localViewerId) return;
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
1053
|
+
heartbeatTimer = setTimeout(() => {
|
|
1054
|
+
heartbeatTimer = null;
|
|
1055
|
+
if (cancelled || !localViewerId) return;
|
|
1056
|
+
void client.heartbeatViewer(workspaceId, sessionId, localViewerId, {
|
|
1057
|
+
leaseEpoch: epochRef.current
|
|
1058
|
+
}).then((res) => {
|
|
1059
|
+
if (!res.alive && !cancelled) {
|
|
1060
|
+
renegotiate();
|
|
1061
|
+
}
|
|
1062
|
+
}).catch((cause) => {
|
|
1063
|
+
if (cancelled) return;
|
|
1064
|
+
if (cause instanceof OpenGeniApiError && (cause.status === 409 || cause.status === 410)) {
|
|
1065
|
+
renegotiate();
|
|
1066
|
+
}
|
|
1067
|
+
}).finally(schedule);
|
|
1068
|
+
}, intervalMs);
|
|
1069
|
+
};
|
|
1070
|
+
schedule();
|
|
982
1071
|
};
|
|
983
1072
|
const pollUntilWarm = () => {
|
|
984
1073
|
pollTimer = setTimeout(() => {
|
|
@@ -986,7 +1075,7 @@ function useSessionCapabilities(sessionId, options = {}) {
|
|
|
986
1075
|
void client.getStreamCapabilities(workspaceId, sessionId, { signal: requestAbort.signal }).then((caps) => {
|
|
987
1076
|
if (cancelled) return;
|
|
988
1077
|
settle(caps);
|
|
989
|
-
if (caps.liveness
|
|
1078
|
+
if (sandboxAcceptsLiveIo(caps.liveness)) {
|
|
990
1079
|
setState("ready");
|
|
991
1080
|
return;
|
|
992
1081
|
}
|
|
@@ -1015,18 +1104,25 @@ function useSessionCapabilities(sessionId, options = {}) {
|
|
|
1015
1104
|
if (cancelled) return;
|
|
1016
1105
|
settle(caps);
|
|
1017
1106
|
const wantDesktopAttach = attachDesktop && desktopAttachable(caps.DesktopStream);
|
|
1018
|
-
const wantTerminalAttach = attachTerminal &&
|
|
1107
|
+
const wantTerminalAttach = attachTerminal && terminalCanAcquirePty(caps.Terminal);
|
|
1019
1108
|
const wantFilesAttach = attachFiles && caps.FileSystem.available;
|
|
1020
1109
|
if (wantDesktopAttach || wantTerminalAttach || wantFilesAttach) {
|
|
1021
1110
|
try {
|
|
1022
1111
|
const holder = await client.attachViewer(workspaceId, sessionId, {
|
|
1023
|
-
desktop: wantDesktopAttach
|
|
1112
|
+
desktop: wantDesktopAttach,
|
|
1113
|
+
terminal: wantTerminalAttach,
|
|
1114
|
+
files: wantFilesAttach
|
|
1024
1115
|
});
|
|
1025
1116
|
if (cancelled) {
|
|
1026
1117
|
void client.detachViewer(workspaceId, sessionId, holder.viewerId).catch(() => {
|
|
1027
1118
|
});
|
|
1028
1119
|
return;
|
|
1029
1120
|
}
|
|
1121
|
+
if (wantDesktopAttach && (!holder.dataPlaneUrl || !holder.transport || !holder.client) || wantTerminalAttach && (!holder.terminalUrl || !holder.terminalTransport)) {
|
|
1122
|
+
await client.detachViewer(workspaceId, sessionId, holder.viewerId).catch(() => {
|
|
1123
|
+
});
|
|
1124
|
+
throw new Error("live plane unavailable");
|
|
1125
|
+
}
|
|
1030
1126
|
localViewerId = holder.viewerId;
|
|
1031
1127
|
viewerIdRef.current = holder.viewerId;
|
|
1032
1128
|
setViewerId(holder.viewerId);
|
|
@@ -1041,19 +1137,20 @@ function useSessionCapabilities(sessionId, options = {}) {
|
|
|
1041
1137
|
...prev.DesktopStream,
|
|
1042
1138
|
transport: holder.transport ?? prev.DesktopStream.transport,
|
|
1043
1139
|
client: holder.client ?? prev.DesktopStream.client,
|
|
1044
|
-
url: holder.dataPlaneUrl
|
|
1045
|
-
token: holder.streamToken
|
|
1046
|
-
expiresAt: holder.streamExpiresAt
|
|
1140
|
+
url: holder.dataPlaneUrl,
|
|
1141
|
+
token: holder.streamToken,
|
|
1142
|
+
expiresAt: holder.streamExpiresAt,
|
|
1047
1143
|
resolution: holder.resolution ?? prev.DesktopStream.resolution
|
|
1048
1144
|
} : prev.DesktopStream,
|
|
1049
|
-
Terminal:
|
|
1145
|
+
Terminal: wantTerminalAttach ? {
|
|
1050
1146
|
...prev.Terminal,
|
|
1051
|
-
transport: holder.terminalTransport,
|
|
1147
|
+
transport: holder.terminalTransport ?? prev.Terminal.transport,
|
|
1052
1148
|
url: holder.terminalUrl,
|
|
1053
|
-
token: holder.terminalToken
|
|
1149
|
+
token: holder.terminalToken,
|
|
1150
|
+
expiresAt: holder.terminalExpiresAt,
|
|
1054
1151
|
// A live pty-ws means the box is pty-capable for real.
|
|
1055
|
-
ptyCapable: true,
|
|
1056
|
-
reason: null
|
|
1152
|
+
ptyCapable: holder.terminalTransport ? true : prev.Terminal.ptyCapable,
|
|
1153
|
+
reason: holder.terminalTransport ? null : prev.Terminal.reason
|
|
1057
1154
|
} : prev.Terminal
|
|
1058
1155
|
} : prev
|
|
1059
1156
|
);
|
|
@@ -1072,6 +1169,8 @@ function useSessionCapabilities(sessionId, options = {}) {
|
|
|
1072
1169
|
setState("error");
|
|
1073
1170
|
setError(cause);
|
|
1074
1171
|
return;
|
|
1172
|
+
} else {
|
|
1173
|
+
throw cause;
|
|
1075
1174
|
}
|
|
1076
1175
|
} else {
|
|
1077
1176
|
throw cause;
|
|
@@ -1079,7 +1178,7 @@ function useSessionCapabilities(sessionId, options = {}) {
|
|
|
1079
1178
|
}
|
|
1080
1179
|
}
|
|
1081
1180
|
const warmUpRequested = wantDesktopAttach || wantTerminalAttach || wantFilesAttach;
|
|
1082
|
-
if (caps.liveness
|
|
1181
|
+
if (sandboxAcceptsLiveIo(caps.liveness) || localViewerId) {
|
|
1083
1182
|
setState("ready");
|
|
1084
1183
|
startHeartbeat(caps);
|
|
1085
1184
|
} else if (warmUpRequested) {
|
|
@@ -1113,7 +1212,7 @@ function useSessionCapabilities(sessionId, options = {}) {
|
|
|
1113
1212
|
client,
|
|
1114
1213
|
workspaceId,
|
|
1115
1214
|
sessionId,
|
|
1116
|
-
|
|
1215
|
+
lifecycleEnabled,
|
|
1117
1216
|
attachDesktop,
|
|
1118
1217
|
attachTerminal,
|
|
1119
1218
|
attachFiles,
|
|
@@ -1162,76 +1261,6 @@ import {
|
|
|
1162
1261
|
} from "@opengeni/sdk";
|
|
1163
1262
|
import { useEffect as useEffect5, useRef as useRef4, useState as useState3 } from "react";
|
|
1164
1263
|
|
|
1165
|
-
// src/lib/relay-wire.ts
|
|
1166
|
-
import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire";
|
|
1167
|
-
var STREAM_KIND_DESKTOP = 2;
|
|
1168
|
-
var STREAM_ROLE_CLIENT = 2;
|
|
1169
|
-
function encodeChannel(message, writer) {
|
|
1170
|
-
if (message.channelId !== "") writer.uint32(10).string(message.channelId);
|
|
1171
|
-
if (message.workspaceId !== "") writer.uint32(18).string(message.workspaceId);
|
|
1172
|
-
if (message.agentId !== "") writer.uint32(26).string(message.agentId);
|
|
1173
|
-
if (message.kind !== 0) writer.uint32(32).int32(message.kind);
|
|
1174
|
-
if (message.port !== 0) writer.uint32(40).uint32(message.port);
|
|
1175
|
-
return writer;
|
|
1176
|
-
}
|
|
1177
|
-
function encodeStreamOpen(message) {
|
|
1178
|
-
const writer = new BinaryWriter();
|
|
1179
|
-
encodeChannel(message.channel, writer.uint32(10).fork()).join();
|
|
1180
|
-
if (message.token !== "") writer.uint32(18).string(message.token);
|
|
1181
|
-
if (message.role !== 0) writer.uint32(24).int32(message.role);
|
|
1182
|
-
if (message.resumeFromSeq !== "0") writer.uint32(32).uint64(message.resumeFromSeq);
|
|
1183
|
-
return writer.finish();
|
|
1184
|
-
}
|
|
1185
|
-
function decodeAgentErrorMessage(reader, length) {
|
|
1186
|
-
const end = reader.pos + length;
|
|
1187
|
-
let message;
|
|
1188
|
-
while (reader.pos < end) {
|
|
1189
|
-
const tag = reader.uint32();
|
|
1190
|
-
if (tag >>> 3 === 2 && tag === 18) {
|
|
1191
|
-
message = reader.string();
|
|
1192
|
-
continue;
|
|
1193
|
-
}
|
|
1194
|
-
if ((tag & 7) === 4 || tag === 0) break;
|
|
1195
|
-
reader.skip(tag & 7);
|
|
1196
|
-
}
|
|
1197
|
-
return message !== void 0 ? { message } : {};
|
|
1198
|
-
}
|
|
1199
|
-
function decodeStreamOpenAck(bytes) {
|
|
1200
|
-
const reader = new BinaryReader(bytes);
|
|
1201
|
-
const end = reader.len;
|
|
1202
|
-
let accepted = false;
|
|
1203
|
-
let error;
|
|
1204
|
-
while (reader.pos < end) {
|
|
1205
|
-
const tag = reader.uint32();
|
|
1206
|
-
if (tag >>> 3 === 1 && tag === 8) {
|
|
1207
|
-
accepted = reader.bool();
|
|
1208
|
-
continue;
|
|
1209
|
-
}
|
|
1210
|
-
if (tag >>> 3 === 2 && tag === 18) {
|
|
1211
|
-
error = decodeAgentErrorMessage(reader, reader.uint32());
|
|
1212
|
-
continue;
|
|
1213
|
-
}
|
|
1214
|
-
if ((tag & 7) === 4 || tag === 0) break;
|
|
1215
|
-
reader.skip(tag & 7);
|
|
1216
|
-
}
|
|
1217
|
-
return error !== void 0 ? { accepted, error } : { accepted };
|
|
1218
|
-
}
|
|
1219
|
-
function decodeStreamFrame(bytes) {
|
|
1220
|
-
const reader = new BinaryReader(bytes);
|
|
1221
|
-
const end = reader.len;
|
|
1222
|
-
let data = new Uint8Array(0);
|
|
1223
|
-
while (reader.pos < end) {
|
|
1224
|
-
const tag = reader.uint32();
|
|
1225
|
-
if (tag >>> 3 === 3 && tag === 26) {
|
|
1226
|
-
data = new Uint8Array(reader.bytes());
|
|
1227
|
-
continue;
|
|
1228
|
-
}
|
|
1229
|
-
if ((tag & 7) === 4 || tag === 0) break;
|
|
1230
|
-
reader.skip(tag & 7);
|
|
1231
|
-
}
|
|
1232
|
-
return { data };
|
|
1233
|
-
}
|
|
1234
|
-
|
|
1235
1264
|
// src/hooks/use-relay-frame-stream.ts
|
|
1236
1265
|
import { useEffect as useEffect4, useRef as useRef3, useState as useState2 } from "react";
|
|
1237
1266
|
var TAG_OPEN = 1;
|
|
@@ -1562,7 +1591,15 @@ import {
|
|
|
1562
1591
|
ttydResizeFrame,
|
|
1563
1592
|
TtydServerCommand
|
|
1564
1593
|
} from "@opengeni/sdk";
|
|
1565
|
-
import { useEffect as useEffect6, useMemo as
|
|
1594
|
+
import { useEffect as useEffect6, useMemo as useMemo3, useRef as useRef5, useState as useState4 } from "react";
|
|
1595
|
+
var MAX_PENDING_TERMINAL_INPUT_CODE_UNITS = 64 * 1024;
|
|
1596
|
+
var TERMINAL_CONNECT_EXPIRY_SKEW_MS = 5e3;
|
|
1597
|
+
function terminalStreamCredentialUsable(capability, nowMs = Date.now()) {
|
|
1598
|
+
if (capability?.transport !== "pty-ws" || !capability.url) return false;
|
|
1599
|
+
if (!capability.expiresAt) return true;
|
|
1600
|
+
const expiresAt = Date.parse(capability.expiresAt);
|
|
1601
|
+
return Number.isFinite(expiresAt) && expiresAt > nowMs + TERMINAL_CONNECT_EXPIRY_SKEW_MS;
|
|
1602
|
+
}
|
|
1566
1603
|
function decodeFrame(data) {
|
|
1567
1604
|
if (typeof data === "string") {
|
|
1568
1605
|
return { command: data.charAt(0), payload: data.slice(1) };
|
|
@@ -1572,10 +1609,19 @@ function decodeFrame(data) {
|
|
|
1572
1609
|
const payload = bytes.length > 1 ? new TextDecoder().decode(bytes.subarray(1)) : "";
|
|
1573
1610
|
return { command, payload };
|
|
1574
1611
|
}
|
|
1612
|
+
function closeSocket(socket) {
|
|
1613
|
+
try {
|
|
1614
|
+
socket?.close();
|
|
1615
|
+
} catch {
|
|
1616
|
+
}
|
|
1617
|
+
}
|
|
1575
1618
|
function useTerminalStream(options) {
|
|
1576
1619
|
const { capability, onOutput, onTitle, initialCols, initialRows } = options;
|
|
1577
1620
|
const [status, setStatus] = useState4("closed");
|
|
1578
1621
|
const wsRef = useRef5(null);
|
|
1622
|
+
const pendingInputRef = useRef5("");
|
|
1623
|
+
const socketFailedRef = useRef5(false);
|
|
1624
|
+
const socketAuthenticatedRef = useRef5(false);
|
|
1579
1625
|
const sizeRef = useRef5({
|
|
1580
1626
|
cols: initialCols ?? 80,
|
|
1581
1627
|
rows: initialRows ?? 24
|
|
@@ -1587,9 +1633,22 @@ function useTerminalStream(options) {
|
|
|
1587
1633
|
const transport = capability?.transport ?? null;
|
|
1588
1634
|
const url = capability?.url ?? null;
|
|
1589
1635
|
const token = capability?.token ?? null;
|
|
1636
|
+
const expiresAt = capability?.expiresAt ?? null;
|
|
1590
1637
|
useEffect6(() => {
|
|
1591
1638
|
if (typeof window === "undefined" || typeof WebSocket === "undefined") return;
|
|
1592
|
-
if (transport !== "pty-ws"
|
|
1639
|
+
if (transport !== "pty-ws") {
|
|
1640
|
+
pendingInputRef.current = "";
|
|
1641
|
+
socketAuthenticatedRef.current = false;
|
|
1642
|
+
setStatus("closed");
|
|
1643
|
+
return;
|
|
1644
|
+
}
|
|
1645
|
+
socketFailedRef.current = false;
|
|
1646
|
+
socketAuthenticatedRef.current = false;
|
|
1647
|
+
if (!url) {
|
|
1648
|
+
setStatus("closed");
|
|
1649
|
+
return;
|
|
1650
|
+
}
|
|
1651
|
+
if (!terminalStreamCredentialUsable({ transport, url, expiresAt })) {
|
|
1593
1652
|
setStatus("closed");
|
|
1594
1653
|
return;
|
|
1595
1654
|
}
|
|
@@ -1599,6 +1658,7 @@ function useTerminalStream(options) {
|
|
|
1599
1658
|
try {
|
|
1600
1659
|
socket = new WebSocket(terminalSocketUrl({ url }), TTYD_SUBPROTOCOL);
|
|
1601
1660
|
} catch {
|
|
1661
|
+
socketFailedRef.current = true;
|
|
1602
1662
|
setStatus("error");
|
|
1603
1663
|
return;
|
|
1604
1664
|
}
|
|
@@ -1609,7 +1669,16 @@ function useTerminalStream(options) {
|
|
|
1609
1669
|
try {
|
|
1610
1670
|
socket.send(ttydAuthFrame({ columns: sizeRef.current.cols, rows: sizeRef.current.rows }));
|
|
1611
1671
|
socket.send(ttydResizeFrame(sizeRef.current.cols, sizeRef.current.rows));
|
|
1672
|
+
if (pendingInputRef.current.length > 0) {
|
|
1673
|
+
socket.send(ttydInputFrame(pendingInputRef.current));
|
|
1674
|
+
pendingInputRef.current = "";
|
|
1675
|
+
}
|
|
1676
|
+
socketAuthenticatedRef.current = true;
|
|
1612
1677
|
} catch {
|
|
1678
|
+
socketFailedRef.current = true;
|
|
1679
|
+
setStatus("error");
|
|
1680
|
+
closeSocket(socket);
|
|
1681
|
+
return;
|
|
1613
1682
|
}
|
|
1614
1683
|
setStatus("open");
|
|
1615
1684
|
};
|
|
@@ -1629,39 +1698,55 @@ function useTerminalStream(options) {
|
|
|
1629
1698
|
}
|
|
1630
1699
|
};
|
|
1631
1700
|
socket.onerror = () => {
|
|
1632
|
-
if (!disposed)
|
|
1701
|
+
if (!disposed) {
|
|
1702
|
+
socketFailedRef.current = true;
|
|
1703
|
+
setStatus("error");
|
|
1704
|
+
}
|
|
1633
1705
|
};
|
|
1634
1706
|
socket.onclose = () => {
|
|
1635
|
-
if (
|
|
1707
|
+
if (wsRef.current === socket) wsRef.current = null;
|
|
1708
|
+
socketAuthenticatedRef.current = false;
|
|
1709
|
+
if (!disposed) setStatus(socketFailedRef.current ? "error" : "closed");
|
|
1636
1710
|
};
|
|
1637
1711
|
return () => {
|
|
1638
1712
|
disposed = true;
|
|
1639
1713
|
wsRef.current = null;
|
|
1714
|
+
socketAuthenticatedRef.current = false;
|
|
1640
1715
|
socket.onopen = null;
|
|
1641
1716
|
socket.onmessage = null;
|
|
1642
1717
|
socket.onerror = null;
|
|
1643
1718
|
socket.onclose = null;
|
|
1644
|
-
|
|
1645
|
-
socket.close();
|
|
1646
|
-
} catch {
|
|
1647
|
-
}
|
|
1719
|
+
closeSocket(socket);
|
|
1648
1720
|
};
|
|
1649
|
-
}, [transport, url, token]);
|
|
1650
|
-
return
|
|
1721
|
+
}, [transport, url, token, expiresAt]);
|
|
1722
|
+
return useMemo3(() => {
|
|
1651
1723
|
const write = (data) => {
|
|
1652
1724
|
const ws = wsRef.current;
|
|
1653
|
-
if (ws && ws.readyState === WebSocket.OPEN) {
|
|
1725
|
+
if (ws && ws.readyState === WebSocket.OPEN && socketAuthenticatedRef.current) {
|
|
1654
1726
|
try {
|
|
1655
1727
|
ws.send(ttydInputFrame(data));
|
|
1656
1728
|
} catch {
|
|
1729
|
+
socketFailedRef.current = true;
|
|
1730
|
+
setStatus("error");
|
|
1731
|
+
closeSocket(ws);
|
|
1732
|
+
}
|
|
1733
|
+
} else if (transport === "pty-ws" && !socketFailedRef.current) {
|
|
1734
|
+
const nextSize = pendingInputRef.current.length + data.length;
|
|
1735
|
+
if (nextSize > MAX_PENDING_TERMINAL_INPUT_CODE_UNITS) {
|
|
1736
|
+
pendingInputRef.current = "";
|
|
1737
|
+
socketFailedRef.current = true;
|
|
1738
|
+
setStatus("error");
|
|
1739
|
+
closeSocket(ws);
|
|
1740
|
+
return;
|
|
1657
1741
|
}
|
|
1742
|
+
pendingInputRef.current += data;
|
|
1658
1743
|
}
|
|
1659
1744
|
};
|
|
1660
1745
|
const resize = (cols, rows) => {
|
|
1661
1746
|
if (cols <= 0 || rows <= 0) return;
|
|
1662
1747
|
sizeRef.current = { cols, rows };
|
|
1663
1748
|
const ws = wsRef.current;
|
|
1664
|
-
if (ws && ws.readyState === WebSocket.OPEN) {
|
|
1749
|
+
if (ws && ws.readyState === WebSocket.OPEN && socketAuthenticatedRef.current) {
|
|
1665
1750
|
try {
|
|
1666
1751
|
ws.send(ttydResizeFrame(cols, rows));
|
|
1667
1752
|
} catch {
|
|
@@ -1671,20 +1756,19 @@ function useTerminalStream(options) {
|
|
|
1671
1756
|
const disconnect = () => {
|
|
1672
1757
|
const ws = wsRef.current;
|
|
1673
1758
|
wsRef.current = null;
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
}
|
|
1759
|
+
pendingInputRef.current = "";
|
|
1760
|
+
socketFailedRef.current = false;
|
|
1761
|
+
socketAuthenticatedRef.current = false;
|
|
1762
|
+
setStatus("closed");
|
|
1763
|
+
closeSocket(ws);
|
|
1680
1764
|
};
|
|
1681
1765
|
return { connected: status === "open", status, write, resize, disconnect };
|
|
1682
|
-
}, [status]);
|
|
1766
|
+
}, [status, transport]);
|
|
1683
1767
|
}
|
|
1684
1768
|
|
|
1685
1769
|
// src/hooks/use-sandbox-terminal.ts
|
|
1686
1770
|
import { OpenGeniApiError as OpenGeniApiError2 } from "@opengeni/sdk";
|
|
1687
|
-
import { useCallback as useCallback11, useEffect as useEffect7, useMemo as
|
|
1771
|
+
import { useCallback as useCallback11, useEffect as useEffect7, useMemo as useMemo4, useRef as useRef6, useState as useState5 } from "react";
|
|
1688
1772
|
function useSandboxTerminal(sessionId, options) {
|
|
1689
1773
|
const { client, workspaceId } = useOpenGeni(options);
|
|
1690
1774
|
const includeAgentFirehose = options.includeAgentFirehose ?? true;
|
|
@@ -1704,7 +1788,7 @@ function useSandboxTerminal(sessionId, options) {
|
|
|
1704
1788
|
setOpenError(null);
|
|
1705
1789
|
}, [identityKey]);
|
|
1706
1790
|
const openInFlight = useRef6(false);
|
|
1707
|
-
const boxWarm = liveness === void 0 || liveness
|
|
1791
|
+
const boxWarm = liveness === void 0 || sandboxAcceptsLiveIo(liveness);
|
|
1708
1792
|
useEffect7(() => {
|
|
1709
1793
|
if (!interactive || !sessionId || ptyFilter || !boxWarm) {
|
|
1710
1794
|
setOpenError(null);
|
|
@@ -1735,7 +1819,7 @@ function useSandboxTerminal(sessionId, options) {
|
|
|
1735
1819
|
});
|
|
1736
1820
|
};
|
|
1737
1821
|
}, [interactive, client, workspaceId, sessionId, ptyFilter, boxWarm, reopenNonce]);
|
|
1738
|
-
const { chunks, openPty, supportsInput } =
|
|
1822
|
+
const { chunks, openPty, supportsInput } = useMemo4(() => {
|
|
1739
1823
|
const out = [];
|
|
1740
1824
|
const open = /* @__PURE__ */ new Map();
|
|
1741
1825
|
let lastOpened = null;
|
|
@@ -1788,7 +1872,7 @@ function useSandboxTerminal(sessionId, options) {
|
|
|
1788
1872
|
const identityMatches = stateIdentity === identityKey;
|
|
1789
1873
|
const activePtyId = identityMatches ? openedPtyId ?? openPty : null;
|
|
1790
1874
|
const canWrite = openedPtyId !== null || supportsInput;
|
|
1791
|
-
const write =
|
|
1875
|
+
const write = useMemo4(() => {
|
|
1792
1876
|
if (!activePtyId || !canWrite || !sessionId) return null;
|
|
1793
1877
|
const writeIdentity = identityKey;
|
|
1794
1878
|
return (data) => {
|
|
@@ -1816,7 +1900,7 @@ function useSandboxTerminal(sessionId, options) {
|
|
|
1816
1900
|
}
|
|
1817
1901
|
|
|
1818
1902
|
// src/hooks/use-sandbox-files.ts
|
|
1819
|
-
import { useCallback as useCallback12, useEffect as useEffect8, useRef as useRef7, useState as useState6 } from "react";
|
|
1903
|
+
import { useCallback as useCallback12, useEffect as useEffect8, useMemo as useMemo5, useRef as useRef7, useState as useState6 } from "react";
|
|
1820
1904
|
var CapturedFileUnavailableError = class extends Error {
|
|
1821
1905
|
constructor(path, reason) {
|
|
1822
1906
|
super(
|
|
@@ -1838,6 +1922,25 @@ var FileWriteConflictError = class extends Error {
|
|
|
1838
1922
|
}
|
|
1839
1923
|
code = "file_write_conflict";
|
|
1840
1924
|
};
|
|
1925
|
+
function normalizeRoot(root) {
|
|
1926
|
+
const normalized = root.replaceAll("\\", "/").replace(/^\.\/+/, "").replace(/\/+$/, "");
|
|
1927
|
+
return normalized === "." ? "" : normalized;
|
|
1928
|
+
}
|
|
1929
|
+
function qualifyStatusPath(root, path) {
|
|
1930
|
+
const normalized = normalizeRoot(root);
|
|
1931
|
+
return normalized ? `${normalized}/${path}` : path;
|
|
1932
|
+
}
|
|
1933
|
+
function summarizeStatuses(statuses) {
|
|
1934
|
+
const repositories = statuses.filter(({ status }) => status.isRepo);
|
|
1935
|
+
return {
|
|
1936
|
+
branch: repositories.length === 1 ? repositories[0]?.status.head ?? null : null,
|
|
1937
|
+
isRepo: repositories.length > 0,
|
|
1938
|
+
repoCount: repositories.length,
|
|
1939
|
+
ahead: repositories.reduce((sum, { status }) => sum + status.ahead, 0),
|
|
1940
|
+
behind: repositories.reduce((sum, { status }) => sum + status.behind, 0),
|
|
1941
|
+
dirtyCount: repositories.reduce((sum, { status }) => sum + status.files.length, 0)
|
|
1942
|
+
};
|
|
1943
|
+
}
|
|
1841
1944
|
function parentOf(path) {
|
|
1842
1945
|
const i = path.lastIndexOf("/");
|
|
1843
1946
|
return i <= 0 ? "" : path.slice(0, i);
|
|
@@ -1881,7 +1984,10 @@ function replaceChildren(nodes, targetPath, children) {
|
|
|
1881
1984
|
return { ...node, children };
|
|
1882
1985
|
}
|
|
1883
1986
|
if (node.kind === "dir" && node.children && targetPath.startsWith(`${node.path}/`)) {
|
|
1884
|
-
return {
|
|
1987
|
+
return {
|
|
1988
|
+
...node,
|
|
1989
|
+
children: replaceChildren(node.children, targetPath, children)
|
|
1990
|
+
};
|
|
1885
1991
|
}
|
|
1886
1992
|
return node;
|
|
1887
1993
|
});
|
|
@@ -1896,6 +2002,30 @@ function findNodeByPath(nodes, path) {
|
|
|
1896
2002
|
}
|
|
1897
2003
|
return void 0;
|
|
1898
2004
|
}
|
|
2005
|
+
function loadedDirectoryPaths(nodes) {
|
|
2006
|
+
const paths = [""];
|
|
2007
|
+
const walk = (list) => {
|
|
2008
|
+
for (const node of list) {
|
|
2009
|
+
if (node.kind !== "dir" || node.children === void 0) continue;
|
|
2010
|
+
paths.push(node.path);
|
|
2011
|
+
walk(node.children);
|
|
2012
|
+
}
|
|
2013
|
+
};
|
|
2014
|
+
walk(nodes);
|
|
2015
|
+
return paths;
|
|
2016
|
+
}
|
|
2017
|
+
function initialDirectoryFrontier(rootPath, repoPaths) {
|
|
2018
|
+
const rootParts = normalizeRoot(rootPath).split("/").filter(Boolean);
|
|
2019
|
+
const paths = /* @__PURE__ */ new Set();
|
|
2020
|
+
for (const repoPath of repoPaths) {
|
|
2021
|
+
const parts = normalizeRoot(repoPath).split("/").filter(Boolean);
|
|
2022
|
+
if (rootParts.some((part, index) => parts[index] !== part)) continue;
|
|
2023
|
+
for (let depth = rootParts.length + 1; depth <= parts.length; depth += 1) {
|
|
2024
|
+
paths.add(parts.slice(0, depth).join("/"));
|
|
2025
|
+
}
|
|
2026
|
+
}
|
|
2027
|
+
return [...paths].sort((a, b) => a.split("/").length - b.split("/").length || a.localeCompare(b)).slice(0, 8);
|
|
2028
|
+
}
|
|
1899
2029
|
function parentIsLoaded(nodes, parent) {
|
|
1900
2030
|
if (parent === "") return true;
|
|
1901
2031
|
const node = findNodeByPath(nodes, parent);
|
|
@@ -1927,7 +2057,10 @@ function removeNode(nodes, path) {
|
|
|
1927
2057
|
return nodes.map((node) => {
|
|
1928
2058
|
if (node.path === parent) {
|
|
1929
2059
|
if (node.kind !== "dir" || node.children === void 0) return node;
|
|
1930
|
-
return {
|
|
2060
|
+
return {
|
|
2061
|
+
...node,
|
|
2062
|
+
children: node.children.filter((n) => n.path !== path)
|
|
2063
|
+
};
|
|
1931
2064
|
}
|
|
1932
2065
|
if (node.kind === "dir" && node.children && parent.startsWith(`${node.path}/`)) {
|
|
1933
2066
|
return { ...node, children: removeNode(node.children, path) };
|
|
@@ -1996,15 +2129,27 @@ async function assertCapturedHash(bytes, expected) {
|
|
|
1996
2129
|
function useSandboxFiles(sessionId, options = {}) {
|
|
1997
2130
|
const { client, workspaceId } = useOpenGeni(options);
|
|
1998
2131
|
const enabled = (options.enabled ?? true) && Boolean(sessionId);
|
|
2132
|
+
const active = options.active ?? true;
|
|
1999
2133
|
const rootPath = options.rootPath ?? "";
|
|
2134
|
+
const repoPathsKey = [
|
|
2135
|
+
...new Set((options.repoPaths?.length ? options.repoPaths : [rootPath]).map(normalizeRoot))
|
|
2136
|
+
].join("\0");
|
|
2137
|
+
const repoPaths = useMemo5(() => repoPathsKey.split("\0"), [repoPathsKey]);
|
|
2000
2138
|
const capture = options.capture ?? null;
|
|
2001
|
-
const isLive2 = options.liveness
|
|
2002
|
-
const
|
|
2139
|
+
const isLive2 = sandboxAcceptsLiveIo(options.liveness);
|
|
2140
|
+
const acceptsEventReads = options.liveness === void 0 || isLive2;
|
|
2141
|
+
const acceptsEventReadsRef = useRef7(acceptsEventReads);
|
|
2142
|
+
acceptsEventReadsRef.current = acceptsEventReads;
|
|
2143
|
+
const hasLiveIoFence = options.liveness !== void 0;
|
|
2144
|
+
const identityKey = `${workspaceId}\0${sessionId ?? ""}\0${rootPath}\0${repoPathsKey}`;
|
|
2003
2145
|
const [tree, setTree] = useState6([]);
|
|
2004
2146
|
const treeRef = useRef7([]);
|
|
2005
2147
|
const [loading, setLoading] = useState6(false);
|
|
2006
2148
|
const [expandingPaths, setExpandingPaths] = useState6(/* @__PURE__ */ new Set());
|
|
2007
2149
|
const [error, setError] = useState6(null);
|
|
2150
|
+
const [gitSummary, setGitSummary] = useState6(null);
|
|
2151
|
+
const [gitLoading, setGitLoading] = useState6(false);
|
|
2152
|
+
const [contentRevision, setContentRevision] = useState6(0);
|
|
2008
2153
|
const [stateIdentity, setStateIdentity] = useState6(identityKey);
|
|
2009
2154
|
const [source, setSource] = useState6(null);
|
|
2010
2155
|
const sourceRef = useRef7(null);
|
|
@@ -2012,11 +2157,14 @@ function useSandboxFiles(sessionId, options = {}) {
|
|
|
2012
2157
|
const refreshGenerationRef = useRef7(0);
|
|
2013
2158
|
const refreshAbortRef = useRef7(null);
|
|
2014
2159
|
const identityAbortRef = useRef7(new AbortController());
|
|
2160
|
+
const eventReadAbortRef = useRef7(new AbortController());
|
|
2015
2161
|
const identityGenerationRef = useRef7(0);
|
|
2016
2162
|
const lastSeqRef = useRef7(0);
|
|
2017
2163
|
const pendingParentsRef = useRef7(/* @__PURE__ */ new Set());
|
|
2018
2164
|
const pendingGitRef = useRef7(false);
|
|
2165
|
+
const pendingAgentToolRef = useRef7(false);
|
|
2019
2166
|
const debounceRef = useRef7(null);
|
|
2167
|
+
const lastCommandRefreshAtRef = useRef7(0);
|
|
2020
2168
|
const ownRevisionsRef = useRef7(/* @__PURE__ */ new Set());
|
|
2021
2169
|
const onMutationError = options.onMutationError;
|
|
2022
2170
|
treeRef.current = tree;
|
|
@@ -2050,48 +2198,83 @@ function useSandboxFiles(sessionId, options = {}) {
|
|
|
2050
2198
|
refreshAbortRef.current = refreshAbort;
|
|
2051
2199
|
const generation = refreshGenerationRef.current += 1;
|
|
2052
2200
|
setLoading(true);
|
|
2201
|
+
setGitLoading(true);
|
|
2053
2202
|
setError(null);
|
|
2054
2203
|
try {
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
}
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2204
|
+
const frontierPaths = initialDirectoryFrontier(rootPath, repoPaths);
|
|
2205
|
+
const listRequests = [rootPath, ...frontierPaths].map((path) => ({
|
|
2206
|
+
path,
|
|
2207
|
+
depth: 1
|
|
2208
|
+
}));
|
|
2209
|
+
const statusPromise = repoPaths.length > 0 ? client.gitReadBatch(
|
|
2210
|
+
workspaceId,
|
|
2211
|
+
sessionId,
|
|
2212
|
+
{
|
|
2213
|
+
requests: repoPaths.map((root) => ({
|
|
2214
|
+
status: { path: root }
|
|
2215
|
+
}))
|
|
2216
|
+
},
|
|
2217
|
+
{ signal: refreshAbort.signal }
|
|
2218
|
+
).then(
|
|
2219
|
+
(batch) => batch.results.map((result, index) => ({
|
|
2220
|
+
root: repoPaths[index] ?? "",
|
|
2221
|
+
status: result.status
|
|
2222
|
+
}))
|
|
2223
|
+
).catch(() => null) : Promise.resolve([]);
|
|
2224
|
+
const listedBatch = await client.fsListBatch(
|
|
2075
2225
|
workspaceId,
|
|
2076
2226
|
sessionId,
|
|
2077
|
-
{
|
|
2227
|
+
{ requests: listRequests },
|
|
2078
2228
|
{ signal: refreshAbort.signal }
|
|
2079
2229
|
);
|
|
2080
2230
|
if (refreshGenerationRef.current !== generation) return;
|
|
2231
|
+
const listed = listedBatch.results[0];
|
|
2232
|
+
if (!listed) throw new Error("Workspace list batch did not return the root result.");
|
|
2081
2233
|
const children = (listed.root.children ?? []).map((node) => fsNodeToTree(node));
|
|
2082
|
-
setTree(
|
|
2083
|
-
|
|
2084
|
-
|
|
2234
|
+
setTree((prev) => {
|
|
2235
|
+
let hydrated = prev.length === 0 ? children : mergeRootChildren(prev, children);
|
|
2236
|
+
for (const [index, path] of frontierPaths.entries()) {
|
|
2237
|
+
const prefetched = listedBatch.results[index + 1];
|
|
2238
|
+
if (!prefetched) continue;
|
|
2239
|
+
hydrated = replaceChildren(
|
|
2240
|
+
hydrated,
|
|
2241
|
+
path,
|
|
2242
|
+
(prefetched.root.children ?? []).map((node) => fsNodeToTree(node))
|
|
2243
|
+
);
|
|
2244
|
+
}
|
|
2245
|
+
return applyStatus(hydrated);
|
|
2246
|
+
});
|
|
2085
2247
|
sourceRef.current = "live";
|
|
2086
2248
|
setSource("live");
|
|
2249
|
+
setLoading(false);
|
|
2250
|
+
const statuses = await statusPromise;
|
|
2251
|
+
if (refreshGenerationRef.current !== generation) return;
|
|
2252
|
+
if (statuses) {
|
|
2253
|
+
const overlay = /* @__PURE__ */ new Map();
|
|
2254
|
+
for (const { root, status } of statuses) {
|
|
2255
|
+
for (const file of status.files) {
|
|
2256
|
+
const code = file.worktree ?? file.index;
|
|
2257
|
+
const mapped = code ? GIT_STATUS_TO_TREE[code] : void 0;
|
|
2258
|
+
if (mapped) overlay.set(qualifyStatusPath(root, file.path), mapped);
|
|
2259
|
+
}
|
|
2260
|
+
}
|
|
2261
|
+
statusRef.current = overlay;
|
|
2262
|
+
setGitSummary(summarizeStatuses(statuses));
|
|
2263
|
+
} else {
|
|
2264
|
+
statusRef.current = /* @__PURE__ */ new Map();
|
|
2265
|
+
setGitSummary(null);
|
|
2266
|
+
}
|
|
2267
|
+
setGitLoading(false);
|
|
2268
|
+
setTree((prev) => applyStatus(prev));
|
|
2087
2269
|
} catch (cause) {
|
|
2088
2270
|
if (refreshGenerationRef.current !== generation) return;
|
|
2089
2271
|
setError(cause instanceof Error ? cause : new Error(String(cause)));
|
|
2090
2272
|
} finally {
|
|
2091
2273
|
if (refreshGenerationRef.current === generation) setLoading(false);
|
|
2274
|
+
if (refreshGenerationRef.current === generation) setGitLoading(false);
|
|
2092
2275
|
if (refreshAbortRef.current === refreshAbort) refreshAbortRef.current = null;
|
|
2093
2276
|
}
|
|
2094
|
-
}, [client, workspaceId, sessionId, rootPath, applyStatus]);
|
|
2277
|
+
}, [client, workspaceId, sessionId, rootPath, repoPaths, applyStatus]);
|
|
2095
2278
|
const seedFromCapture = useCallback12(
|
|
2096
2279
|
(manifest) => {
|
|
2097
2280
|
const overlay = /* @__PURE__ */ new Map();
|
|
@@ -2107,6 +2290,15 @@ function useSandboxFiles(sessionId, options = {}) {
|
|
|
2107
2290
|
);
|
|
2108
2291
|
sourceRef.current = "capture";
|
|
2109
2292
|
setSource("capture");
|
|
2293
|
+
setGitSummary({
|
|
2294
|
+
branch: manifest.repos.length === 1 ? manifest.repos[0]?.head ?? null : null,
|
|
2295
|
+
isRepo: manifest.repos.length > 0,
|
|
2296
|
+
repoCount: manifest.repos.length,
|
|
2297
|
+
ahead: manifest.repos.reduce((sum, repo) => sum + repo.ahead, 0),
|
|
2298
|
+
behind: manifest.repos.reduce((sum, repo) => sum + repo.behind, 0),
|
|
2299
|
+
dirtyCount: manifest.files.length
|
|
2300
|
+
});
|
|
2301
|
+
setGitLoading(false);
|
|
2110
2302
|
setError(null);
|
|
2111
2303
|
},
|
|
2112
2304
|
[applyStatus]
|
|
@@ -2224,18 +2416,14 @@ function useSandboxFiles(sessionId, options = {}) {
|
|
|
2224
2416
|
[client, workspaceId, sessionId, capture, source]
|
|
2225
2417
|
);
|
|
2226
2418
|
const reconcilePath = useCallback12(
|
|
2227
|
-
async (path) => {
|
|
2419
|
+
async (path, requestSignal) => {
|
|
2228
2420
|
if (!sessionId) return;
|
|
2229
2421
|
const identityGeneration = identityGenerationRef.current;
|
|
2230
2422
|
const identitySignal = identityAbortRef.current.signal;
|
|
2423
|
+
const signal = requestSignal ? AbortSignal.any([identitySignal, requestSignal]) : identitySignal;
|
|
2231
2424
|
if (!parentIsLoaded(treeRef.current, path)) return;
|
|
2232
2425
|
try {
|
|
2233
|
-
const listed = await client.fsList(
|
|
2234
|
-
workspaceId,
|
|
2235
|
-
sessionId,
|
|
2236
|
-
{ path, depth: 1 },
|
|
2237
|
-
{ signal: identitySignal }
|
|
2238
|
-
);
|
|
2426
|
+
const listed = await client.fsList(workspaceId, sessionId, { path, depth: 1 }, { signal });
|
|
2239
2427
|
if (identityGenerationRef.current !== identityGeneration) return;
|
|
2240
2428
|
const children = (listed.root.children ?? []).map((node) => fsNodeToTree(node));
|
|
2241
2429
|
if (path === "") setTree((prev) => applyStatus(mergeRootChildren(prev, children)));
|
|
@@ -2287,7 +2475,12 @@ function useSandboxFiles(sessionId, options = {}) {
|
|
|
2287
2475
|
const identitySignal = identityAbortRef.current.signal;
|
|
2288
2476
|
const parent = parentOf(path);
|
|
2289
2477
|
const exists = Boolean(findNodeByPath(tree, path));
|
|
2290
|
-
const node = {
|
|
2478
|
+
const node = {
|
|
2479
|
+
path,
|
|
2480
|
+
name: leafOf(path),
|
|
2481
|
+
kind: "file",
|
|
2482
|
+
size: content.length
|
|
2483
|
+
};
|
|
2291
2484
|
return await runOptimistic(
|
|
2292
2485
|
"write",
|
|
2293
2486
|
(nodes) => exists ? nodes : insertNode(nodes, parent, node),
|
|
@@ -2325,11 +2518,20 @@ function useSandboxFiles(sessionId, options = {}) {
|
|
|
2325
2518
|
async (path) => {
|
|
2326
2519
|
if (!sessionId) throw new Error("no session");
|
|
2327
2520
|
const parent = parentOf(path);
|
|
2328
|
-
const node = {
|
|
2521
|
+
const node = {
|
|
2522
|
+
path,
|
|
2523
|
+
name: leafOf(path),
|
|
2524
|
+
kind: "file",
|
|
2525
|
+
size: 0
|
|
2526
|
+
};
|
|
2329
2527
|
await runOptimistic(
|
|
2330
2528
|
"create file",
|
|
2331
2529
|
(nodes) => insertNode(nodes, parent, node),
|
|
2332
|
-
() => client.fsWrite(workspaceId, sessionId, {
|
|
2530
|
+
() => client.fsWrite(workspaceId, sessionId, {
|
|
2531
|
+
path,
|
|
2532
|
+
content: "",
|
|
2533
|
+
overwrite: false
|
|
2534
|
+
}),
|
|
2333
2535
|
[parent]
|
|
2334
2536
|
);
|
|
2335
2537
|
},
|
|
@@ -2339,7 +2541,12 @@ function useSandboxFiles(sessionId, options = {}) {
|
|
|
2339
2541
|
async (path) => {
|
|
2340
2542
|
if (!sessionId) throw new Error("no session");
|
|
2341
2543
|
const parent = parentOf(path);
|
|
2342
|
-
const node = {
|
|
2544
|
+
const node = {
|
|
2545
|
+
path,
|
|
2546
|
+
name: leafOf(path),
|
|
2547
|
+
kind: "dir",
|
|
2548
|
+
children: []
|
|
2549
|
+
};
|
|
2343
2550
|
await runOptimistic(
|
|
2344
2551
|
"create folder",
|
|
2345
2552
|
(nodes) => insertNode(nodes, parent, node),
|
|
@@ -2392,6 +2599,8 @@ function useSandboxFiles(sessionId, options = {}) {
|
|
|
2392
2599
|
refreshAbortRef.current?.abort();
|
|
2393
2600
|
refreshAbortRef.current = null;
|
|
2394
2601
|
refreshGenerationRef.current += 1;
|
|
2602
|
+
eventReadAbortRef.current.abort();
|
|
2603
|
+
eventReadAbortRef.current = new AbortController();
|
|
2395
2604
|
const identityChanged = previousIdentityRef.current !== identityKey;
|
|
2396
2605
|
previousIdentityRef.current = identityKey;
|
|
2397
2606
|
if (identityChanged || !enabled) {
|
|
@@ -2401,6 +2610,8 @@ function useSandboxFiles(sessionId, options = {}) {
|
|
|
2401
2610
|
lastSeqRef.current = 0;
|
|
2402
2611
|
pendingParentsRef.current = /* @__PURE__ */ new Set();
|
|
2403
2612
|
pendingGitRef.current = false;
|
|
2613
|
+
pendingAgentToolRef.current = false;
|
|
2614
|
+
lastCommandRefreshAtRef.current = 0;
|
|
2404
2615
|
if (debounceRef.current) {
|
|
2405
2616
|
clearTimeout(debounceRef.current);
|
|
2406
2617
|
debounceRef.current = null;
|
|
@@ -2415,6 +2626,9 @@ function useSandboxFiles(sessionId, options = {}) {
|
|
|
2415
2626
|
setSource(null);
|
|
2416
2627
|
setLoading(false);
|
|
2417
2628
|
setError(null);
|
|
2629
|
+
setGitSummary(null);
|
|
2630
|
+
setGitLoading(false);
|
|
2631
|
+
setContentRevision(0);
|
|
2418
2632
|
}
|
|
2419
2633
|
if (!enabled) {
|
|
2420
2634
|
return;
|
|
@@ -2423,42 +2637,88 @@ function useSandboxFiles(sessionId, options = {}) {
|
|
|
2423
2637
|
if (currentCapture && (identityChanged || !isLive2 || sourceRef.current !== "live")) {
|
|
2424
2638
|
seedFromCapture(currentCapture);
|
|
2425
2639
|
}
|
|
2426
|
-
if (
|
|
2640
|
+
if (!active) {
|
|
2641
|
+
if (!active) {
|
|
2642
|
+
setLoading(false);
|
|
2643
|
+
setGitLoading(false);
|
|
2644
|
+
}
|
|
2645
|
+
return;
|
|
2646
|
+
}
|
|
2647
|
+
if (isLive2 || !hasLiveIoFence && !currentCapture) {
|
|
2427
2648
|
void refresh();
|
|
2428
2649
|
}
|
|
2429
2650
|
return () => {
|
|
2430
2651
|
refreshAbortRef.current?.abort();
|
|
2431
2652
|
refreshAbortRef.current = null;
|
|
2432
2653
|
refreshGenerationRef.current += 1;
|
|
2654
|
+
eventReadAbortRef.current.abort();
|
|
2433
2655
|
};
|
|
2434
|
-
}, [
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
if (
|
|
2447
|
-
const
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2656
|
+
}, [
|
|
2657
|
+
enabled,
|
|
2658
|
+
active,
|
|
2659
|
+
isLive2,
|
|
2660
|
+
hasLiveIoFence,
|
|
2661
|
+
captureRevision,
|
|
2662
|
+
identityKey,
|
|
2663
|
+
refresh,
|
|
2664
|
+
seedFromCapture
|
|
2665
|
+
]);
|
|
2666
|
+
const refreshGitOverlay = useCallback12(
|
|
2667
|
+
async (requestSignal) => {
|
|
2668
|
+
if (!sessionId) return;
|
|
2669
|
+
const identityGeneration = identityGenerationRef.current;
|
|
2670
|
+
const identitySignal = identityAbortRef.current.signal;
|
|
2671
|
+
const signal = requestSignal ? AbortSignal.any([identitySignal, requestSignal]) : identitySignal;
|
|
2672
|
+
setGitLoading(true);
|
|
2673
|
+
try {
|
|
2674
|
+
const batch = await client.gitReadBatch(
|
|
2675
|
+
workspaceId,
|
|
2676
|
+
sessionId,
|
|
2677
|
+
{ requests: repoPaths.map((root) => ({ status: { path: root } })) },
|
|
2678
|
+
{ signal }
|
|
2679
|
+
);
|
|
2680
|
+
const statuses = batch.results.map((result, index) => ({
|
|
2681
|
+
root: repoPaths[index] ?? "",
|
|
2682
|
+
status: result.status
|
|
2683
|
+
}));
|
|
2684
|
+
if (identityGenerationRef.current !== identityGeneration) return;
|
|
2685
|
+
const overlay = /* @__PURE__ */ new Map();
|
|
2686
|
+
for (const { root, status } of statuses) {
|
|
2687
|
+
for (const file of status.files) {
|
|
2688
|
+
const code = file.worktree ?? file.index;
|
|
2689
|
+
const mapped = code ? GIT_STATUS_TO_TREE[code] : void 0;
|
|
2690
|
+
if (mapped) overlay.set(qualifyStatusPath(root, file.path), mapped);
|
|
2691
|
+
}
|
|
2692
|
+
}
|
|
2693
|
+
statusRef.current = overlay;
|
|
2694
|
+
setGitSummary(summarizeStatuses(statuses));
|
|
2695
|
+
setTree((prev) => applyStatus(prev));
|
|
2696
|
+
} catch {
|
|
2697
|
+
} finally {
|
|
2698
|
+
if (identityGenerationRef.current === identityGeneration) setGitLoading(false);
|
|
2452
2699
|
}
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
}
|
|
2457
|
-
}, [client, workspaceId, sessionId, rootPath, applyStatus]);
|
|
2700
|
+
},
|
|
2701
|
+
[client, workspaceId, sessionId, repoPaths, applyStatus]
|
|
2702
|
+
);
|
|
2458
2703
|
const events = options.events;
|
|
2459
2704
|
useEffect8(() => {
|
|
2460
2705
|
if (!enabled || !events) return;
|
|
2706
|
+
if (!active || !acceptsEventReads) {
|
|
2707
|
+
for (const event of events) {
|
|
2708
|
+
if (event.sequence > lastSeqRef.current) lastSeqRef.current = event.sequence;
|
|
2709
|
+
}
|
|
2710
|
+
if (debounceRef.current) {
|
|
2711
|
+
clearTimeout(debounceRef.current);
|
|
2712
|
+
debounceRef.current = null;
|
|
2713
|
+
}
|
|
2714
|
+
pendingParentsRef.current = /* @__PURE__ */ new Set();
|
|
2715
|
+
pendingGitRef.current = false;
|
|
2716
|
+
pendingAgentToolRef.current = false;
|
|
2717
|
+
return;
|
|
2718
|
+
}
|
|
2461
2719
|
let sawNew = false;
|
|
2720
|
+
let contentChanged = false;
|
|
2721
|
+
let sawCommandDelta = false;
|
|
2462
2722
|
for (const event of events) {
|
|
2463
2723
|
if (event.sequence <= lastSeqRef.current) continue;
|
|
2464
2724
|
if (event.type === "fs.changed") {
|
|
@@ -2468,6 +2728,7 @@ function useSandboxFiles(sessionId, options = {}) {
|
|
|
2468
2728
|
if (payload.source === "write") continue;
|
|
2469
2729
|
if (typeof payload.revision === "number" && ownRevisionsRef.current.has(payload.revision))
|
|
2470
2730
|
continue;
|
|
2731
|
+
contentChanged = true;
|
|
2471
2732
|
for (const change of payload.changes ?? []) {
|
|
2472
2733
|
pendingParentsRef.current.add(parentOf(change.path));
|
|
2473
2734
|
if (change.oldPath) pendingParentsRef.current.add(parentOf(change.oldPath));
|
|
@@ -2478,34 +2739,70 @@ function useSandboxFiles(sessionId, options = {}) {
|
|
|
2478
2739
|
if (payload && typeof payload === "object" && typeof payload.revision === "number" && ownRevisionsRef.current.has(payload.revision))
|
|
2479
2740
|
continue;
|
|
2480
2741
|
pendingGitRef.current = true;
|
|
2742
|
+
} else if (event.type === "agent.toolCall.output") {
|
|
2743
|
+
sawNew = true;
|
|
2744
|
+
contentChanged = true;
|
|
2745
|
+
pendingAgentToolRef.current = true;
|
|
2746
|
+
pendingGitRef.current = true;
|
|
2747
|
+
} else if (event.type === "sandbox.command.output.delta") {
|
|
2748
|
+
sawNew = true;
|
|
2749
|
+
contentChanged = true;
|
|
2750
|
+
sawCommandDelta = true;
|
|
2751
|
+
pendingAgentToolRef.current = true;
|
|
2752
|
+
pendingGitRef.current = true;
|
|
2481
2753
|
}
|
|
2482
2754
|
}
|
|
2483
2755
|
for (const event of events)
|
|
2484
2756
|
if (event.sequence > lastSeqRef.current) lastSeqRef.current = event.sequence;
|
|
2485
2757
|
if (!sawNew) return;
|
|
2758
|
+
if (contentChanged && !sawCommandDelta) setContentRevision((revision) => revision + 1);
|
|
2486
2759
|
if (debounceRef.current) clearTimeout(debounceRef.current);
|
|
2487
2760
|
const identityGeneration = identityGenerationRef.current;
|
|
2761
|
+
const delay = sawCommandDelta ? Math.max(0, 1e3 - (Date.now() - lastCommandRefreshAtRef.current)) : 150;
|
|
2488
2762
|
debounceRef.current = setTimeout(() => {
|
|
2489
2763
|
debounceRef.current = null;
|
|
2490
2764
|
if (identityGenerationRef.current !== identityGeneration) return;
|
|
2765
|
+
if (!acceptsEventReadsRef.current) {
|
|
2766
|
+
pendingParentsRef.current = /* @__PURE__ */ new Set();
|
|
2767
|
+
pendingGitRef.current = false;
|
|
2768
|
+
pendingAgentToolRef.current = false;
|
|
2769
|
+
return;
|
|
2770
|
+
}
|
|
2771
|
+
if (sawCommandDelta) {
|
|
2772
|
+
lastCommandRefreshAtRef.current = Date.now();
|
|
2773
|
+
setContentRevision((revision) => revision + 1);
|
|
2774
|
+
}
|
|
2491
2775
|
const parents = pendingParentsRef.current;
|
|
2492
2776
|
pendingParentsRef.current = /* @__PURE__ */ new Set();
|
|
2493
2777
|
const wantGit = pendingGitRef.current;
|
|
2494
2778
|
pendingGitRef.current = false;
|
|
2779
|
+
const wantAgentTool = pendingAgentToolRef.current;
|
|
2780
|
+
pendingAgentToolRef.current = false;
|
|
2781
|
+
const eventReadSignal = eventReadAbortRef.current.signal;
|
|
2495
2782
|
void (async () => {
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
}
|
|
2783
|
+
const paths = wantAgentTool ? loadedDirectoryPaths(treeRef.current) : [...parents];
|
|
2784
|
+
await Promise.all([
|
|
2785
|
+
wantGit ? refreshGitOverlay(eventReadSignal) : Promise.resolve(),
|
|
2786
|
+
Promise.all(paths.map((path) => reconcilePath(path, eventReadSignal)))
|
|
2787
|
+
]);
|
|
2502
2788
|
})();
|
|
2503
|
-
},
|
|
2504
|
-
}, [enabled, events, reconcilePath, refreshGitOverlay]);
|
|
2789
|
+
}, delay);
|
|
2790
|
+
}, [enabled, active, acceptsEventReads, events, reconcilePath, refreshGitOverlay]);
|
|
2791
|
+
useEffect8(() => {
|
|
2792
|
+
if (active && acceptsEventReads) return;
|
|
2793
|
+
if (debounceRef.current) {
|
|
2794
|
+
clearTimeout(debounceRef.current);
|
|
2795
|
+
debounceRef.current = null;
|
|
2796
|
+
}
|
|
2797
|
+
pendingParentsRef.current = /* @__PURE__ */ new Set();
|
|
2798
|
+
pendingGitRef.current = false;
|
|
2799
|
+
pendingAgentToolRef.current = false;
|
|
2800
|
+
}, [active, acceptsEventReads]);
|
|
2505
2801
|
useEffect8(
|
|
2506
2802
|
() => () => {
|
|
2507
2803
|
refreshAbortRef.current?.abort();
|
|
2508
2804
|
identityAbortRef.current.abort();
|
|
2805
|
+
eventReadAbortRef.current.abort();
|
|
2509
2806
|
identityGenerationRef.current += 1;
|
|
2510
2807
|
if (debounceRef.current) clearTimeout(debounceRef.current);
|
|
2511
2808
|
},
|
|
@@ -2523,25 +2820,28 @@ function useSandboxFiles(sessionId, options = {}) {
|
|
|
2523
2820
|
deleteEntry,
|
|
2524
2821
|
moveEntry,
|
|
2525
2822
|
refresh,
|
|
2823
|
+
contentRevision: identityMatches ? contentRevision : 0,
|
|
2526
2824
|
source: identityMatches ? source : null,
|
|
2527
2825
|
capturedAt: identityMatches && source === "capture" ? capture?.capturedAt ?? null : null,
|
|
2826
|
+
gitSummary: identityMatches ? gitSummary : null,
|
|
2827
|
+
gitLoading: identityMatches && gitLoading,
|
|
2528
2828
|
loading: identityMatches && loading,
|
|
2529
2829
|
error: identityMatches ? error : null
|
|
2530
2830
|
};
|
|
2531
2831
|
}
|
|
2532
2832
|
|
|
2533
2833
|
// src/hooks/use-sandbox-git.ts
|
|
2534
|
-
import { useCallback as useCallback13, useEffect as useEffect9, useMemo as
|
|
2535
|
-
function
|
|
2834
|
+
import { useCallback as useCallback13, useEffect as useEffect9, useMemo as useMemo6, useRef as useRef8, useState as useState7 } from "react";
|
|
2835
|
+
function normalizeRoot2(root) {
|
|
2536
2836
|
const normalized = root.replaceAll("\\", "/").replace(/^\.\/+/, "").replace(/\/+$/, "");
|
|
2537
2837
|
return normalized === "." ? "" : normalized;
|
|
2538
2838
|
}
|
|
2539
2839
|
function qualifyPath(repoRoot, path) {
|
|
2540
|
-
const root =
|
|
2840
|
+
const root = normalizeRoot2(repoRoot);
|
|
2541
2841
|
return root ? `${root}/${path}` : path;
|
|
2542
2842
|
}
|
|
2543
2843
|
function qualifyDiff(repoRoot, file) {
|
|
2544
|
-
const root =
|
|
2844
|
+
const root = normalizeRoot2(repoRoot);
|
|
2545
2845
|
return {
|
|
2546
2846
|
...file,
|
|
2547
2847
|
path: qualifyPath(root, file.path),
|
|
@@ -2550,11 +2850,12 @@ function qualifyDiff(repoRoot, file) {
|
|
|
2550
2850
|
};
|
|
2551
2851
|
}
|
|
2552
2852
|
function uniqueRoots(roots) {
|
|
2553
|
-
return [...new Set(roots.map(
|
|
2853
|
+
return [...new Set(roots.map(normalizeRoot2))].sort((a, b) => a.localeCompare(b));
|
|
2554
2854
|
}
|
|
2855
|
+
var GIT_READ_BATCH_REQUEST_LIMIT = 32;
|
|
2555
2856
|
function repoForPath(manifest, repoPath) {
|
|
2556
|
-
const want =
|
|
2557
|
-
const exact = manifest.repos.find((r) =>
|
|
2857
|
+
const want = normalizeRoot2(repoPath);
|
|
2858
|
+
const exact = manifest.repos.find((r) => normalizeRoot2(r.root) === want);
|
|
2558
2859
|
if (exact) return exact;
|
|
2559
2860
|
if (want === "" && manifest.repos.length === 1) return manifest.repos[0] ?? null;
|
|
2560
2861
|
return null;
|
|
@@ -2590,16 +2891,22 @@ function mergeDiffs(current, next) {
|
|
|
2590
2891
|
function useSandboxGit(sessionId, options = {}) {
|
|
2591
2892
|
const { client, workspaceId } = useOpenGeni(options);
|
|
2592
2893
|
const enabled = (options.enabled ?? true) && Boolean(sessionId);
|
|
2894
|
+
const active = options.active ?? true;
|
|
2593
2895
|
const repoPath = options.repoPath ?? "";
|
|
2594
2896
|
const workspaceWide = options.repoPaths !== void 0;
|
|
2595
2897
|
const repoPathsKey = uniqueRoots(
|
|
2596
2898
|
options.repoPaths && options.repoPaths.length > 0 ? options.repoPaths : [repoPath]
|
|
2597
2899
|
).join("\0");
|
|
2598
|
-
const repoPaths =
|
|
2599
|
-
const
|
|
2600
|
-
const
|
|
2601
|
-
const
|
|
2602
|
-
const
|
|
2900
|
+
const repoPaths = useMemo6(() => repoPathsKey.split("\0"), [repoPathsKey]);
|
|
2901
|
+
const comparison = options.comparison ?? (options.staged ? "staged" : "working");
|
|
2902
|
+
const hasAdvertisedRepoPaths = (options.repoPaths?.length ?? 0) > 0;
|
|
2903
|
+
const capture = comparison === "staged" ? null : options.capture ?? null;
|
|
2904
|
+
const isLive2 = sandboxAcceptsLiveIo(options.liveness);
|
|
2905
|
+
const acceptsEventReads = options.liveness === void 0 || isLive2;
|
|
2906
|
+
const acceptsEventReadsRef = useRef8(acceptsEventReads);
|
|
2907
|
+
acceptsEventReadsRef.current = acceptsEventReads;
|
|
2908
|
+
const hasLiveIoFence = options.liveness !== void 0;
|
|
2909
|
+
const identityKey = `${workspaceId}\0${sessionId ?? ""}\0${repoPathsKey}\0${workspaceWide}\0${comparison}`;
|
|
2603
2910
|
const [diff, setDiff] = useState7([]);
|
|
2604
2911
|
const [branch, setBranch] = useState7(null);
|
|
2605
2912
|
const [isRepo, setIsRepo] = useState7(false);
|
|
@@ -2614,6 +2921,7 @@ function useSandboxGit(sessionId, options = {}) {
|
|
|
2614
2921
|
const refreshGenerationRef = useRef8(0);
|
|
2615
2922
|
const refreshAbortRef = useRef8(null);
|
|
2616
2923
|
const lastChangeRef = useRef8(0);
|
|
2924
|
+
const commandRefreshTimerRef = useRef8(null);
|
|
2617
2925
|
const refresh = useCallback13(async () => {
|
|
2618
2926
|
if (!sessionId) return;
|
|
2619
2927
|
refreshAbortRef.current?.abort();
|
|
@@ -2623,7 +2931,52 @@ function useSandboxGit(sessionId, options = {}) {
|
|
|
2623
2931
|
setLoading(true);
|
|
2624
2932
|
setError(null);
|
|
2625
2933
|
try {
|
|
2626
|
-
const
|
|
2934
|
+
const diffRequest = (root) => client.gitDiff(
|
|
2935
|
+
workspaceId,
|
|
2936
|
+
sessionId,
|
|
2937
|
+
{
|
|
2938
|
+
path: root,
|
|
2939
|
+
...comparison === "branch" ? { fromRef: "origin/HEAD", includeUntracked: true } : comparison === "working" ? { fromRef: "HEAD", includeUntracked: true } : { staged: true, includeUntracked: false }
|
|
2940
|
+
},
|
|
2941
|
+
{ signal: refreshAbort.signal }
|
|
2942
|
+
);
|
|
2943
|
+
const prefetched = hasAdvertisedRepoPaths ? await (async () => {
|
|
2944
|
+
const prefetchedRepos = [];
|
|
2945
|
+
for (let offset = 0; offset < repoPaths.length; offset += GIT_READ_BATCH_REQUEST_LIMIT) {
|
|
2946
|
+
const roots2 = repoPaths.slice(offset, offset + GIT_READ_BATCH_REQUEST_LIMIT);
|
|
2947
|
+
const batch = await client.gitReadBatch(
|
|
2948
|
+
workspaceId,
|
|
2949
|
+
sessionId,
|
|
2950
|
+
{
|
|
2951
|
+
requests: roots2.map((root) => ({
|
|
2952
|
+
status: { path: root },
|
|
2953
|
+
diff: {
|
|
2954
|
+
path: root,
|
|
2955
|
+
...comparison === "branch" ? { fromRef: "origin/HEAD", includeUntracked: true } : comparison === "working" ? { fromRef: "HEAD", includeUntracked: true } : { staged: true, includeUntracked: false }
|
|
2956
|
+
}
|
|
2957
|
+
}))
|
|
2958
|
+
},
|
|
2959
|
+
{ signal: refreshAbort.signal }
|
|
2960
|
+
);
|
|
2961
|
+
if (batch.results.length !== roots2.length) {
|
|
2962
|
+
throw new Error(
|
|
2963
|
+
`Workspace Git batch returned ${batch.results.length} results for ${roots2.length} repositories.`
|
|
2964
|
+
);
|
|
2965
|
+
}
|
|
2966
|
+
batch.results.forEach((item, index) => {
|
|
2967
|
+
prefetchedRepos.push({
|
|
2968
|
+
root: roots2[index],
|
|
2969
|
+
status: item.status,
|
|
2970
|
+
result: item.diff ?? {
|
|
2971
|
+
files: [],
|
|
2972
|
+
revision: item.status.revision
|
|
2973
|
+
}
|
|
2974
|
+
});
|
|
2975
|
+
});
|
|
2976
|
+
}
|
|
2977
|
+
return prefetchedRepos;
|
|
2978
|
+
})() : null;
|
|
2979
|
+
const statuses = prefetched ?? await Promise.all(
|
|
2627
2980
|
repoPaths.map(async (root) => ({
|
|
2628
2981
|
root,
|
|
2629
2982
|
status: await client.gitStatus(
|
|
@@ -2648,19 +3001,11 @@ function useSandboxGit(sessionId, options = {}) {
|
|
|
2648
3001
|
setSource("live");
|
|
2649
3002
|
return;
|
|
2650
3003
|
}
|
|
3004
|
+
const prefetchedByRoot = new Map(prefetched?.map(({ root, result }) => [root, result]));
|
|
2651
3005
|
const results = await Promise.all(
|
|
2652
3006
|
repositories.map(async ({ root }) => ({
|
|
2653
3007
|
root,
|
|
2654
|
-
result: await
|
|
2655
|
-
workspaceId,
|
|
2656
|
-
sessionId,
|
|
2657
|
-
{
|
|
2658
|
-
path: root,
|
|
2659
|
-
staged,
|
|
2660
|
-
includeUntracked: !staged
|
|
2661
|
-
},
|
|
2662
|
-
{ signal: refreshAbort.signal }
|
|
2663
|
-
)
|
|
3008
|
+
result: prefetchedByRoot.get(root) ?? await diffRequest(root)
|
|
2664
3009
|
}))
|
|
2665
3010
|
);
|
|
2666
3011
|
if (refreshGenerationRef.current !== generation) return;
|
|
@@ -2681,10 +3026,21 @@ function useSandboxGit(sessionId, options = {}) {
|
|
|
2681
3026
|
if (refreshGenerationRef.current === generation) setLoading(false);
|
|
2682
3027
|
if (refreshAbortRef.current === refreshAbort) refreshAbortRef.current = null;
|
|
2683
3028
|
}
|
|
2684
|
-
}, [
|
|
3029
|
+
}, [
|
|
3030
|
+
client,
|
|
3031
|
+
workspaceId,
|
|
3032
|
+
sessionId,
|
|
3033
|
+
repoPaths,
|
|
3034
|
+
hasAdvertisedRepoPaths,
|
|
3035
|
+
workspaceWide,
|
|
3036
|
+
comparison
|
|
3037
|
+
]);
|
|
2685
3038
|
const seedFromCapture = useCallback13(
|
|
2686
3039
|
(manifest) => {
|
|
2687
3040
|
const repositories = workspaceWide ? manifest.repos : [repoForPath(manifest, repoPath)].filter(Boolean);
|
|
3041
|
+
if (comparison === "branch" && repositories.some((repo) => repo.branchDiff === void 0)) {
|
|
3042
|
+
return;
|
|
3043
|
+
}
|
|
2688
3044
|
if (repositories.length === 0) {
|
|
2689
3045
|
setIsRepo(false);
|
|
2690
3046
|
setBranch(null);
|
|
@@ -2700,20 +3056,21 @@ function useSandboxGit(sessionId, options = {}) {
|
|
|
2700
3056
|
setBranch(repositories.length === 1 ? repositories[0]?.head ?? null : null);
|
|
2701
3057
|
setAhead(repositories.reduce((sum, repo) => sum + repo.ahead, 0));
|
|
2702
3058
|
setBehind(repositories.reduce((sum, repo) => sum + repo.behind, 0));
|
|
2703
|
-
setRepoRoots(repositories.map((repo) =>
|
|
3059
|
+
setRepoRoots(repositories.map((repo) => normalizeRoot2(repo.root)));
|
|
2704
3060
|
setDiff(
|
|
2705
3061
|
(prev) => mergeDiffs(
|
|
2706
3062
|
prev,
|
|
2707
|
-
repositories.flatMap(
|
|
2708
|
-
|
|
2709
|
-
|
|
3063
|
+
repositories.flatMap((repo) => {
|
|
3064
|
+
const capturedDiff = comparison === "branch" ? repo.branchDiff ?? [] : repo.diff;
|
|
3065
|
+
return workspaceWide ? capturedDiff.map((file) => qualifyDiff(repo.root, file)) : capturedDiff;
|
|
3066
|
+
})
|
|
2710
3067
|
)
|
|
2711
3068
|
);
|
|
2712
3069
|
sourceRef.current = "capture";
|
|
2713
3070
|
setSource("capture");
|
|
2714
3071
|
setError(null);
|
|
2715
3072
|
},
|
|
2716
|
-
[repoPath, workspaceWide]
|
|
3073
|
+
[comparison, repoPath, workspaceWide]
|
|
2717
3074
|
);
|
|
2718
3075
|
const captureRef = useRef8(capture);
|
|
2719
3076
|
captureRef.current = capture;
|
|
@@ -2746,7 +3103,11 @@ function useSandboxGit(sessionId, options = {}) {
|
|
|
2746
3103
|
if (currentCapture && (identityChanged || !isLive2 || sourceRef.current !== "live")) {
|
|
2747
3104
|
seedFromCapture(currentCapture);
|
|
2748
3105
|
}
|
|
2749
|
-
if (
|
|
3106
|
+
if (!active) {
|
|
3107
|
+
if (!active) setLoading(false);
|
|
3108
|
+
return;
|
|
3109
|
+
}
|
|
3110
|
+
if (isLive2 || !hasLiveIoFence && !currentCapture) {
|
|
2750
3111
|
void refresh();
|
|
2751
3112
|
}
|
|
2752
3113
|
return () => {
|
|
@@ -2754,21 +3115,68 @@ function useSandboxGit(sessionId, options = {}) {
|
|
|
2754
3115
|
refreshAbortRef.current = null;
|
|
2755
3116
|
refreshGenerationRef.current += 1;
|
|
2756
3117
|
};
|
|
2757
|
-
}, [
|
|
3118
|
+
}, [
|
|
3119
|
+
enabled,
|
|
3120
|
+
active,
|
|
3121
|
+
isLive2,
|
|
3122
|
+
hasLiveIoFence,
|
|
3123
|
+
captureRevision,
|
|
3124
|
+
identityKey,
|
|
3125
|
+
refresh,
|
|
3126
|
+
seedFromCapture
|
|
3127
|
+
]);
|
|
2758
3128
|
const events = options.events;
|
|
2759
3129
|
useEffect9(() => {
|
|
2760
|
-
if (!enabled || !events)
|
|
3130
|
+
if (!enabled || !events) {
|
|
3131
|
+
if (commandRefreshTimerRef.current) {
|
|
3132
|
+
clearTimeout(commandRefreshTimerRef.current);
|
|
3133
|
+
commandRefreshTimerRef.current = null;
|
|
3134
|
+
}
|
|
3135
|
+
return;
|
|
3136
|
+
}
|
|
3137
|
+
if (!active || !acceptsEventReads) {
|
|
3138
|
+
for (const event of events) {
|
|
3139
|
+
if (event.sequence > lastChangeRef.current) lastChangeRef.current = event.sequence;
|
|
3140
|
+
}
|
|
3141
|
+
if (commandRefreshTimerRef.current) {
|
|
3142
|
+
clearTimeout(commandRefreshTimerRef.current);
|
|
3143
|
+
commandRefreshTimerRef.current = null;
|
|
3144
|
+
}
|
|
3145
|
+
return;
|
|
3146
|
+
}
|
|
2761
3147
|
let latest = lastChangeRef.current;
|
|
3148
|
+
let immediate = false;
|
|
3149
|
+
let commandDelta = false;
|
|
2762
3150
|
for (const event of events) {
|
|
2763
|
-
if (event.
|
|
3151
|
+
if (event.sequence <= latest) continue;
|
|
3152
|
+
if (event.type === "git.changed" || event.type === "agent.toolCall.output") {
|
|
3153
|
+
latest = event.sequence;
|
|
3154
|
+
immediate = true;
|
|
3155
|
+
} else if (event.type === "sandbox.command.output.delta") {
|
|
2764
3156
|
latest = event.sequence;
|
|
3157
|
+
commandDelta = true;
|
|
2765
3158
|
}
|
|
2766
3159
|
}
|
|
2767
3160
|
if (latest > lastChangeRef.current) {
|
|
2768
3161
|
lastChangeRef.current = latest;
|
|
2769
|
-
|
|
3162
|
+
if (immediate) {
|
|
3163
|
+
if (commandRefreshTimerRef.current) clearTimeout(commandRefreshTimerRef.current);
|
|
3164
|
+
commandRefreshTimerRef.current = null;
|
|
3165
|
+
if (acceptsEventReadsRef.current) void refresh();
|
|
3166
|
+
} else if (commandDelta && !commandRefreshTimerRef.current) {
|
|
3167
|
+
commandRefreshTimerRef.current = setTimeout(() => {
|
|
3168
|
+
commandRefreshTimerRef.current = null;
|
|
3169
|
+
if (acceptsEventReadsRef.current) void refresh();
|
|
3170
|
+
}, 1e3);
|
|
3171
|
+
}
|
|
2770
3172
|
}
|
|
2771
|
-
}, [enabled, events, refresh]);
|
|
3173
|
+
}, [enabled, active, acceptsEventReads, events, refresh]);
|
|
3174
|
+
useEffect9(
|
|
3175
|
+
() => () => {
|
|
3176
|
+
if (commandRefreshTimerRef.current) clearTimeout(commandRefreshTimerRef.current);
|
|
3177
|
+
},
|
|
3178
|
+
[]
|
|
3179
|
+
);
|
|
2772
3180
|
const identityMatches = enabled && stateIdentity === identityKey;
|
|
2773
3181
|
const visibleRepoRoots = identityMatches ? repoRoots : [];
|
|
2774
3182
|
return {
|
|
@@ -2985,7 +3393,7 @@ function useWorkspaceCapture(sessionId, options = {}) {
|
|
|
2985
3393
|
// src/hooks/use-workspace-edit.ts
|
|
2986
3394
|
import { useCallback as useCallback15, useEffect as useEffect11, useRef as useRef10, useState as useState9 } from "react";
|
|
2987
3395
|
function isLive(liveness) {
|
|
2988
|
-
return liveness
|
|
3396
|
+
return sandboxAcceptsLiveIo(liveness);
|
|
2989
3397
|
}
|
|
2990
3398
|
function useWorkspaceEdit(sessionId, options = {}) {
|
|
2991
3399
|
const { client, workspaceId } = useOpenGeni(options);
|
|
@@ -3142,7 +3550,7 @@ function useWorkspaceEdit(sessionId, options = {}) {
|
|
|
3142
3550
|
}
|
|
3143
3551
|
|
|
3144
3552
|
// src/hooks/use-machine-chip.ts
|
|
3145
|
-
import { useMemo as
|
|
3553
|
+
import { useMemo as useMemo7 } from "react";
|
|
3146
3554
|
function formatAsOf(capturedAt, now) {
|
|
3147
3555
|
const then = Date.parse(capturedAt);
|
|
3148
3556
|
if (Number.isNaN(then)) return "recently";
|
|
@@ -3159,7 +3567,7 @@ function deriveMachineChip(input) {
|
|
|
3159
3567
|
const now = input.now ?? Date.now();
|
|
3160
3568
|
const asOf = input.capturedAt ?? null;
|
|
3161
3569
|
const offlineLabel = asOf ? `Offline \u2014 as of ${formatAsOf(asOf, now)}` : "Offline";
|
|
3162
|
-
if (input.liveness
|
|
3570
|
+
if (sandboxAcceptsLiveIo(input.liveness)) {
|
|
3163
3571
|
return { state: "live", label: "Live", asOf: null };
|
|
3164
3572
|
}
|
|
3165
3573
|
const selfhostedOffline = input.activeIsSelfhosted === true && input.activeMachineState === "offline";
|
|
@@ -3179,7 +3587,7 @@ function useMachineChip(input) {
|
|
|
3179
3587
|
capturedAt,
|
|
3180
3588
|
now
|
|
3181
3589
|
} = input;
|
|
3182
|
-
return
|
|
3590
|
+
return useMemo7(
|
|
3183
3591
|
() => deriveMachineChip({
|
|
3184
3592
|
liveness,
|
|
3185
3593
|
capabilitiesState,
|
|
@@ -3350,6 +3758,7 @@ import { LayoutGroup, motion } from "motion/react";
|
|
|
3350
3758
|
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
3351
3759
|
function ChatComposer({
|
|
3352
3760
|
composer,
|
|
3761
|
+
responsiveBasis,
|
|
3353
3762
|
effectiveControl,
|
|
3354
3763
|
queuedAheadCount,
|
|
3355
3764
|
canControlWorkspace,
|
|
@@ -3397,7 +3806,7 @@ function ChatComposer({
|
|
|
3397
3806
|
attachments || models || controlsLeading || controlsStart || transcription
|
|
3398
3807
|
);
|
|
3399
3808
|
const stackActions = hasControls && Boolean(actionsStart);
|
|
3400
|
-
return /* @__PURE__ */ jsxs2(Root, { controller, className, children: [
|
|
3809
|
+
return /* @__PURE__ */ jsxs2(Root, { controller, responsiveBasis, className, children: [
|
|
3401
3810
|
/* @__PURE__ */ jsxs2(Frame, { children: [
|
|
3402
3811
|
/* @__PURE__ */ jsx2(CommandPalette2, {}),
|
|
3403
3812
|
/* @__PURE__ */ jsxs2(Surface, { children: [
|
|
@@ -3405,63 +3814,81 @@ function ChatComposer({
|
|
|
3405
3814
|
/* @__PURE__ */ jsx2(RestoredResources, {}),
|
|
3406
3815
|
/* @__PURE__ */ jsx2(Attachments, {}),
|
|
3407
3816
|
header,
|
|
3817
|
+
composer.annotations && composer.annotations.length > 0 ? /* @__PURE__ */ jsx2("div", { className: "px-3 pt-2 sm:px-4", children: /* @__PURE__ */ jsx2(
|
|
3818
|
+
TimelineAnnotationsChip,
|
|
3819
|
+
{
|
|
3820
|
+
annotations: composer.annotations,
|
|
3821
|
+
editable: true,
|
|
3822
|
+
focusAnnotationId: composer.annotationReviewTargetId,
|
|
3823
|
+
onFocusConsumed: composer.clearAnnotationReviewTarget,
|
|
3824
|
+
onUpdate: composer.updateAnnotation,
|
|
3825
|
+
onRemove: composer.removeAnnotation
|
|
3826
|
+
}
|
|
3827
|
+
) }) : null,
|
|
3408
3828
|
/* @__PURE__ */ jsx2(Input, { placeholder, autoFocus }),
|
|
3409
|
-
controller.confirmState ? /* @__PURE__ */ jsx2(Confirmation, {}) : /* @__PURE__ */ jsx2(
|
|
3410
|
-
|
|
3411
|
-
|
|
3412
|
-
|
|
3413
|
-
|
|
3414
|
-
|
|
3415
|
-
|
|
3416
|
-
|
|
3417
|
-
|
|
3418
|
-
|
|
3419
|
-
|
|
3420
|
-
|
|
3421
|
-
|
|
3422
|
-
|
|
3423
|
-
|
|
3424
|
-
) : null,
|
|
3425
|
-
models ? /* @__PURE__ */ jsx2(
|
|
3426
|
-
motion.span,
|
|
3427
|
-
{
|
|
3428
|
-
layout: true,
|
|
3429
|
-
transition: { duration: 0.36, ease: [0.22, 1, 0.36, 1] },
|
|
3430
|
-
className: "inline-flex min-w-0",
|
|
3431
|
-
children: /* @__PURE__ */ jsx2(
|
|
3432
|
-
ModelPicker2,
|
|
3829
|
+
controller.confirmState ? /* @__PURE__ */ jsx2(Confirmation, {}) : /* @__PURE__ */ jsx2(
|
|
3830
|
+
Footer,
|
|
3831
|
+
{
|
|
3832
|
+
"data-og-stack-actions": stackActions ? "" : void 0,
|
|
3833
|
+
className: stackActions ? "max-sm:flex-nowrap sm:flex-wrap" : void 0,
|
|
3834
|
+
children: /* @__PURE__ */ jsxs2(LayoutGroup, { id: "og-composer-footer", children: [
|
|
3835
|
+
hasControls ? /* @__PURE__ */ jsxs2(
|
|
3836
|
+
Controls,
|
|
3837
|
+
{
|
|
3838
|
+
className: stackActions ? "min-w-0 max-sm:flex-1 sm:w-auto" : void 0,
|
|
3839
|
+
children: [
|
|
3840
|
+
controlsLeading,
|
|
3841
|
+
/* @__PURE__ */ jsx2(AttachButton, { className: attachButtonClassName }),
|
|
3842
|
+
transcription ? /* @__PURE__ */ jsx2(
|
|
3843
|
+
ComposerTranscriptionControl,
|
|
3433
3844
|
{
|
|
3434
|
-
|
|
3435
|
-
|
|
3436
|
-
|
|
3845
|
+
...transcription,
|
|
3846
|
+
suppressed: transcriptionSuppressed,
|
|
3847
|
+
className: [transcription.className, transcriptionClassName].filter(Boolean).join(" ")
|
|
3437
3848
|
}
|
|
3438
|
-
)
|
|
3439
|
-
|
|
3440
|
-
|
|
3441
|
-
|
|
3442
|
-
|
|
3443
|
-
|
|
3444
|
-
|
|
3445
|
-
|
|
3446
|
-
|
|
3447
|
-
|
|
3448
|
-
|
|
3449
|
-
|
|
3450
|
-
|
|
3451
|
-
|
|
3452
|
-
|
|
3453
|
-
|
|
3454
|
-
|
|
3455
|
-
|
|
3456
|
-
|
|
3457
|
-
|
|
3458
|
-
|
|
3459
|
-
|
|
3460
|
-
|
|
3461
|
-
|
|
3462
|
-
|
|
3463
|
-
|
|
3464
|
-
|
|
3849
|
+
) : null,
|
|
3850
|
+
models ? /* @__PURE__ */ jsx2(
|
|
3851
|
+
motion.span,
|
|
3852
|
+
{
|
|
3853
|
+
layout: true,
|
|
3854
|
+
transition: { duration: 0.36, ease: [0.22, 1, 0.36, 1] },
|
|
3855
|
+
className: "inline-flex min-w-0",
|
|
3856
|
+
children: /* @__PURE__ */ jsx2(
|
|
3857
|
+
ModelPicker2,
|
|
3858
|
+
{
|
|
3859
|
+
models,
|
|
3860
|
+
value: selectedModel,
|
|
3861
|
+
onChange: onSelectModel
|
|
3862
|
+
}
|
|
3863
|
+
)
|
|
3864
|
+
}
|
|
3865
|
+
) : null,
|
|
3866
|
+
controlsStart ? /* @__PURE__ */ jsx2(
|
|
3867
|
+
motion.span,
|
|
3868
|
+
{
|
|
3869
|
+
layout: true,
|
|
3870
|
+
transition: { duration: 0.36, ease: [0.22, 1, 0.36, 1] },
|
|
3871
|
+
className: "inline-flex min-w-0 items-center gap-1.5",
|
|
3872
|
+
children: controlsStart
|
|
3873
|
+
}
|
|
3874
|
+
) : null
|
|
3875
|
+
]
|
|
3876
|
+
}
|
|
3877
|
+
) : /* @__PURE__ */ jsx2(Hint, { children: hint }),
|
|
3878
|
+
/* @__PURE__ */ jsxs2(
|
|
3879
|
+
Actions,
|
|
3880
|
+
{
|
|
3881
|
+
className: stackActions ? "max-sm:shrink-0 sm:w-auto sm:justify-end" : void 0,
|
|
3882
|
+
children: [
|
|
3883
|
+
actionsStart,
|
|
3884
|
+
/* @__PURE__ */ jsx2(PauseButton, {}),
|
|
3885
|
+
/* @__PURE__ */ jsx2(SendButton, {})
|
|
3886
|
+
]
|
|
3887
|
+
}
|
|
3888
|
+
)
|
|
3889
|
+
] })
|
|
3890
|
+
}
|
|
3891
|
+
)
|
|
3465
3892
|
] })
|
|
3466
3893
|
] }),
|
|
3467
3894
|
/* @__PURE__ */ jsx2(Help, {}),
|
|
@@ -3683,6 +4110,22 @@ async function attachRenderer(prefer, loaders, onTier) {
|
|
|
3683
4110
|
|
|
3684
4111
|
// src/components/sandbox-terminal.tsx
|
|
3685
4112
|
import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
4113
|
+
function terminalSurfaceState(input) {
|
|
4114
|
+
if (!input.ready) return "loading";
|
|
4115
|
+
if (input.acceptsInput) return "interactive";
|
|
4116
|
+
if (input.inputPending) return "connecting";
|
|
4117
|
+
if (input.ptyStatus === "connecting") return "connecting";
|
|
4118
|
+
if (input.ptyStatus === "error") return "error";
|
|
4119
|
+
if (input.booting) return "waking";
|
|
4120
|
+
return "output-only";
|
|
4121
|
+
}
|
|
4122
|
+
function subscribeTerminalInput(term, sink, enabled) {
|
|
4123
|
+
if (!enabled || !term || !sink) return null;
|
|
4124
|
+
return term.onData(sink);
|
|
4125
|
+
}
|
|
4126
|
+
function clearTerminalBootStatus(term) {
|
|
4127
|
+
term.write("\r\x1B[2K\x1B[2J\x1B[H");
|
|
4128
|
+
}
|
|
3686
4129
|
function forcedRendererFailures() {
|
|
3687
4130
|
const raw = typeof globalThis !== "undefined" ? globalThis.__OG_FORCE_RENDERER_FAIL__ : void 0;
|
|
3688
4131
|
const set = /* @__PURE__ */ new Set();
|
|
@@ -3715,30 +4158,49 @@ function SandboxTerminal({
|
|
|
3715
4158
|
const wroteFirehoseRef = useRef12(false);
|
|
3716
4159
|
const bootActiveRef = useRef12(false);
|
|
3717
4160
|
const [ready, setReady] = useState11(false);
|
|
4161
|
+
const [inputReady, setInputReady] = useState11(false);
|
|
3718
4162
|
const [activated, setActivated] = useState11(false);
|
|
3719
|
-
const
|
|
3720
|
-
const
|
|
4163
|
+
const ptyDescriptor = terminalCapability?.transport === "pty-ws";
|
|
4164
|
+
const ptyAttachable = terminalCanAcquirePty(terminalCapability);
|
|
3721
4165
|
const {
|
|
3722
4166
|
status: ptyStatus,
|
|
3723
4167
|
write: ptyWrite,
|
|
3724
4168
|
resize: resizePty,
|
|
3725
4169
|
connected: ptyConnected
|
|
3726
4170
|
} = useTerminalStream({
|
|
3727
|
-
|
|
3728
|
-
|
|
3729
|
-
|
|
3730
|
-
|
|
4171
|
+
// Connect only after xterm exists. This makes the renderer the sole output
|
|
4172
|
+
// owner from the first websocket frame and removes an otherwise unbounded
|
|
4173
|
+
// pre-mount output queue during large command bursts.
|
|
4174
|
+
capability: ptyAttachable && ready ? {
|
|
4175
|
+
transport: "pty-ws",
|
|
4176
|
+
url: ptyDescriptor ? terminalCapability.url : null,
|
|
4177
|
+
token: ptyDescriptor ? terminalCapability.token : null,
|
|
4178
|
+
expiresAt: ptyDescriptor ? terminalCapability.expiresAt : null
|
|
3731
4179
|
} : null,
|
|
3732
4180
|
onOutput: (data) => {
|
|
3733
|
-
|
|
3734
|
-
if (term) term.write(data);
|
|
3735
|
-
else ptyOutputQueueRef.current.push(data);
|
|
4181
|
+
termRef.current?.write(data);
|
|
3736
4182
|
}
|
|
3737
4183
|
});
|
|
3738
|
-
const ptyMode =
|
|
3739
|
-
const
|
|
3740
|
-
const
|
|
4184
|
+
const ptyMode = ptyDescriptor;
|
|
4185
|
+
const ptyCapturesInput = ptyAttachable && ptyStatus !== "error";
|
|
4186
|
+
const interactive = !readOnly && (ptyAttachable ? ptyCapturesInput : result.write !== null);
|
|
4187
|
+
const acceptsInput = inputReady && (!ptyAttachable || ptyConnected);
|
|
4188
|
+
const inputPending = inputReady && ptyAttachable && !ptyConnected && activated;
|
|
4189
|
+
let terminalModeLabel;
|
|
4190
|
+
if (readOnly) terminalModeLabel = "read-only";
|
|
4191
|
+
else if (!ptyAttachable) terminalModeLabel = acceptsInput ? null : "output only";
|
|
4192
|
+
else if (ptyStatus === "error") terminalModeLabel = "connection error";
|
|
4193
|
+
else if (acceptsInput) terminalModeLabel = null;
|
|
4194
|
+
else terminalModeLabel = activated ? "connecting" : "connect on input";
|
|
4195
|
+
const warm = sandboxAcceptsLiveIo(liveness);
|
|
3741
4196
|
const booting = activated && !ptyMode && !warm && liveness !== null && liveness !== void 0 && result.chunks.length === 0;
|
|
4197
|
+
const surfaceState = terminalSurfaceState({
|
|
4198
|
+
ready,
|
|
4199
|
+
acceptsInput,
|
|
4200
|
+
inputPending,
|
|
4201
|
+
ptyStatus,
|
|
4202
|
+
booting
|
|
4203
|
+
});
|
|
3742
4204
|
function handleActivate() {
|
|
3743
4205
|
if (!activated) setActivated(true);
|
|
3744
4206
|
onActivate?.();
|
|
@@ -3825,10 +4287,6 @@ function SandboxTerminal({
|
|
|
3825
4287
|
requestAnimationFrame(() => {
|
|
3826
4288
|
if (disposed) return;
|
|
3827
4289
|
settle();
|
|
3828
|
-
if (ptyOutputQueueRef.current.length > 0) {
|
|
3829
|
-
for (const data of ptyOutputQueueRef.current) term.write(data);
|
|
3830
|
-
ptyOutputQueueRef.current = [];
|
|
3831
|
-
}
|
|
3832
4290
|
el.setAttribute("data-og-term-ready", "true");
|
|
3833
4291
|
if (typeof globalThis.__OG_TERMINAL_DEBUG__ === "function") {
|
|
3834
4292
|
globalThis.__OG_TERMINAL_DEBUG__({
|
|
@@ -3873,7 +4331,7 @@ function SandboxTerminal({
|
|
|
3873
4331
|
if (!booting) {
|
|
3874
4332
|
if (bootActiveRef.current) {
|
|
3875
4333
|
bootActiveRef.current = false;
|
|
3876
|
-
if (!wroteFirehoseRef.current) term
|
|
4334
|
+
if (!wroteFirehoseRef.current) clearTerminalBootStatus(term);
|
|
3877
4335
|
}
|
|
3878
4336
|
return;
|
|
3879
4337
|
}
|
|
@@ -3900,12 +4358,19 @@ function SandboxTerminal({
|
|
|
3900
4358
|
}, [ready, result.chunks, ptyMode]);
|
|
3901
4359
|
useEffect13(() => {
|
|
3902
4360
|
const term = termRef.current;
|
|
3903
|
-
if (!ready || !term || !interactive)
|
|
3904
|
-
|
|
3905
|
-
|
|
3906
|
-
|
|
3907
|
-
|
|
3908
|
-
|
|
4361
|
+
if (!ready || !term || !interactive) {
|
|
4362
|
+
setInputReady(false);
|
|
4363
|
+
return;
|
|
4364
|
+
}
|
|
4365
|
+
const sink = ptyAttachable ? ptyWrite : result.write;
|
|
4366
|
+
if (!sink) {
|
|
4367
|
+
setInputReady(false);
|
|
4368
|
+
return;
|
|
4369
|
+
}
|
|
4370
|
+
const sub = subscribeTerminalInput(term, sink, true);
|
|
4371
|
+
setInputReady(sub !== null);
|
|
4372
|
+
return () => sub?.dispose();
|
|
4373
|
+
}, [ready, interactive, ptyAttachable, ptyWrite, result.write]);
|
|
3909
4374
|
useEffect13(() => {
|
|
3910
4375
|
const term = termRef.current;
|
|
3911
4376
|
if (!ready || !term || !ptyMode) return;
|
|
@@ -3951,7 +4416,14 @@ function SandboxTerminal({
|
|
|
3951
4416
|
] })
|
|
3952
4417
|
] }),
|
|
3953
4418
|
/* @__PURE__ */ jsxs4("span", { className: "flex shrink-0 items-center gap-2", children: [
|
|
3954
|
-
|
|
4419
|
+
terminalModeLabel && /* @__PURE__ */ jsx4(
|
|
4420
|
+
"span",
|
|
4421
|
+
{
|
|
4422
|
+
className: "rounded-og-sm bg-og-surface-2 px-1.5 py-0.5 text-og-xs uppercase tracking-wide",
|
|
4423
|
+
role: "status",
|
|
4424
|
+
children: terminalModeLabel
|
|
4425
|
+
}
|
|
4426
|
+
),
|
|
3955
4427
|
/* @__PURE__ */ jsx4(
|
|
3956
4428
|
"button",
|
|
3957
4429
|
{
|
|
@@ -3981,7 +4453,11 @@ function SandboxTerminal({
|
|
|
3981
4453
|
ref: containerRef,
|
|
3982
4454
|
className: "h-full w-full",
|
|
3983
4455
|
style: { visibility: ready ? "visible" : "hidden" },
|
|
3984
|
-
"data-opengeni-terminal": true
|
|
4456
|
+
"data-opengeni-terminal": true,
|
|
4457
|
+
"data-opengeni-terminal-ready": ready ? "true" : "false",
|
|
4458
|
+
"data-opengeni-terminal-interactive": surfaceState === "interactive" ? "true" : "false",
|
|
4459
|
+
"data-opengeni-terminal-state": surfaceState,
|
|
4460
|
+
"data-opengeni-terminal-status": ptyAttachable ? ptyStatus : "firehose"
|
|
3985
4461
|
}
|
|
3986
4462
|
)
|
|
3987
4463
|
]
|
|
@@ -4051,7 +4527,7 @@ import {
|
|
|
4051
4527
|
useEffect as useEffect15,
|
|
4052
4528
|
useId as useId2,
|
|
4053
4529
|
useLayoutEffect,
|
|
4054
|
-
useMemo as
|
|
4530
|
+
useMemo as useMemo9,
|
|
4055
4531
|
useRef as useRef13,
|
|
4056
4532
|
useState as useState12
|
|
4057
4533
|
} from "react";
|
|
@@ -4059,11 +4535,11 @@ import { AlertDialog } from "radix-ui";
|
|
|
4059
4535
|
import { VList } from "virtua";
|
|
4060
4536
|
|
|
4061
4537
|
// src/lib/use-unicode-fonts.ts
|
|
4062
|
-
import { useEffect as useEffect14, useMemo as
|
|
4538
|
+
import { useEffect as useEffect14, useMemo as useMemo8 } from "react";
|
|
4063
4539
|
var japaneseScript = /[\u3040-\u30ff\u3400-\u4dbf\u4e00-\u9fff\uf900-\ufaff]/u;
|
|
4064
4540
|
var arabicScript = /[\u0600-\u06ff\u0750-\u077f\u0870-\u08ff\ufb50-\ufdff\ufe70-\ufefc]/u;
|
|
4065
4541
|
function useUnicodeFallbackFonts(values) {
|
|
4066
|
-
const content =
|
|
4542
|
+
const content = useMemo8(() => collectUnicodeContent(values), [values]);
|
|
4067
4543
|
useEffect14(() => {
|
|
4068
4544
|
if (content.japanese) {
|
|
4069
4545
|
void import("./noto-sans-jp-loader.generated-HFGLYBR3.js").then(({ loadNotoSansJp }) => loadNotoSansJp(content.japanese)).catch(() => void 0);
|
|
@@ -4146,10 +4622,11 @@ function FileBrowser({
|
|
|
4146
4622
|
const [busy, setBusy] = useState12(false);
|
|
4147
4623
|
const [coarsePointer, setCoarsePointer] = useState12(false);
|
|
4148
4624
|
const treeId = useId2();
|
|
4149
|
-
const
|
|
4625
|
+
const container = usePortalTokenSource();
|
|
4626
|
+
const containerRef = container.currentRef;
|
|
4150
4627
|
const deleteReturnFocusRef = useRef13(null);
|
|
4151
4628
|
const vlistRef = useRef13(null);
|
|
4152
|
-
const portalTokenStyle = usePortalTokenStyle(
|
|
4629
|
+
const portalTokenStyle = usePortalTokenStyle(container.source);
|
|
4153
4630
|
const getTreeElement = useCallback16(
|
|
4154
4631
|
() => typeof document === "undefined" ? null : document.getElementById(treeId),
|
|
4155
4632
|
[treeId]
|
|
@@ -4209,7 +4686,7 @@ function FileBrowser({
|
|
|
4209
4686
|
[expanded, expand]
|
|
4210
4687
|
);
|
|
4211
4688
|
const cold = result.source !== "live";
|
|
4212
|
-
const renderItems =
|
|
4689
|
+
const renderItems = useMemo9(() => {
|
|
4213
4690
|
const items = [];
|
|
4214
4691
|
if (draftCreate && draftCreate.parent === "") {
|
|
4215
4692
|
items.push({ type: "create", parent: "", kind: draftCreate.kind, depth: 0 });
|
|
@@ -4240,22 +4717,22 @@ function FileBrowser({
|
|
|
4240
4717
|
walk(result.tree, 0);
|
|
4241
4718
|
return items;
|
|
4242
4719
|
}, [result.tree, expanded, draftCreate, loadingPaths, result.expandingPaths, cold]);
|
|
4243
|
-
const unicodeFontPaths =
|
|
4720
|
+
const unicodeFontPaths = useMemo9(
|
|
4244
4721
|
() => renderItems.flatMap((item) => item.type === "node" ? [item.node.path] : []),
|
|
4245
4722
|
[renderItems]
|
|
4246
4723
|
);
|
|
4247
4724
|
useUnicodeFallbackFonts(unicodeFontPaths);
|
|
4248
|
-
const flatRows =
|
|
4725
|
+
const flatRows = useMemo9(
|
|
4249
4726
|
() => renderItems.flatMap(
|
|
4250
4727
|
(item) => item.type === "node" ? [{ node: item.node, depth: item.depth }] : []
|
|
4251
4728
|
),
|
|
4252
4729
|
[renderItems]
|
|
4253
4730
|
);
|
|
4254
|
-
const rowIndexByPath =
|
|
4731
|
+
const rowIndexByPath = useMemo9(
|
|
4255
4732
|
() => new Map(flatRows.map((row, index) => [row.node.path, index])),
|
|
4256
4733
|
[flatRows]
|
|
4257
4734
|
);
|
|
4258
|
-
const treePositionByPath =
|
|
4735
|
+
const treePositionByPath = useMemo9(() => {
|
|
4259
4736
|
const positions = /* @__PURE__ */ new Map();
|
|
4260
4737
|
const visit = (nodes) => {
|
|
4261
4738
|
nodes.forEach((node, index) => {
|
|
@@ -4517,6 +4994,7 @@ function FileBrowser({
|
|
|
4517
4994
|
runDelete,
|
|
4518
4995
|
supportsMutation,
|
|
4519
4996
|
coarsePointer,
|
|
4997
|
+
containerRef,
|
|
4520
4998
|
rowIndexByPath,
|
|
4521
4999
|
treeId
|
|
4522
5000
|
]
|
|
@@ -4537,7 +5015,7 @@ function FileBrowser({
|
|
|
4537
5015
|
const showDegradedTree = result.error && result.tree.length > 0;
|
|
4538
5016
|
const showEmpty = !result.loading && result.tree.length === 0 && !draftCreate;
|
|
4539
5017
|
const showToolbar = supportsMutation || result.loading;
|
|
4540
|
-
const usesVirtualTree = !showErrorEmpty && !showEmpty;
|
|
5018
|
+
const usesVirtualTree = !showErrorEmpty && !showEmpty && renderItems.length > 500;
|
|
4541
5019
|
const renderNodeRow = (node, depth) => {
|
|
4542
5020
|
const isOpen = expanded.has(node.path);
|
|
4543
5021
|
if (renderNode) {
|
|
@@ -4772,7 +5250,7 @@ function FileBrowser({
|
|
|
4772
5250
|
type: "button",
|
|
4773
5251
|
onClick: () => void result.refresh(),
|
|
4774
5252
|
disabled: result.loading,
|
|
4775
|
-
className: "inline-flex min-h-7 shrink-0 items-center gap-1 rounded-og-sm border border-og-border bg-og-surface-1 px-2 font-medium text-og-fg transition-colors hover:border-og-border-strong focus-visible:outline-
|
|
5253
|
+
className: "inline-flex min-h-7 shrink-0 items-center gap-1 rounded-og-sm border border-og-border bg-og-surface-1 px-2 font-medium text-og-fg transition-colors hover:border-og-border-strong focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-og-accent disabled:cursor-not-allowed disabled:opacity-50 pointer-coarse:min-h-11",
|
|
4776
5254
|
children: [
|
|
4777
5255
|
/* @__PURE__ */ jsx5(
|
|
4778
5256
|
RefreshCwIcon,
|
|
@@ -4791,13 +5269,7 @@ function FileBrowser({
|
|
|
4791
5269
|
/* @__PURE__ */ jsx5(
|
|
4792
5270
|
"div",
|
|
4793
5271
|
{
|
|
4794
|
-
ref:
|
|
4795
|
-
id: usesVirtualTree ? void 0 : treeId,
|
|
4796
|
-
role: usesVirtualTree ? void 0 : "tree",
|
|
4797
|
-
"aria-label": usesVirtualTree ? void 0 : "Workspace files",
|
|
4798
|
-
"aria-activedescendant": usesVirtualTree ? void 0 : activeDescendantId,
|
|
4799
|
-
tabIndex: usesVirtualTree ? void 0 : 0,
|
|
4800
|
-
"aria-multiselectable": usesVirtualTree ? void 0 : false,
|
|
5272
|
+
ref: container.ref,
|
|
4801
5273
|
onFocus: (event) => {
|
|
4802
5274
|
if (event.target.id === treeId && cursorIndex < 0) {
|
|
4803
5275
|
const first = flatRows[0];
|
|
@@ -4816,7 +5288,7 @@ function FileBrowser({
|
|
|
4816
5288
|
void onDropOnto("", src);
|
|
4817
5289
|
} : void 0,
|
|
4818
5290
|
className: cn(
|
|
4819
|
-
"flex min-h-0 min-w-0 flex-1 flex-col p-1
|
|
5291
|
+
"flex min-h-0 min-w-0 flex-1 flex-col p-1",
|
|
4820
5292
|
dragOver === "" && "ring-1 ring-inset ring-og-accent"
|
|
4821
5293
|
),
|
|
4822
5294
|
"data-opengeni-file-tree": true,
|
|
@@ -4841,7 +5313,19 @@ function FileBrowser({
|
|
|
4841
5313
|
]
|
|
4842
5314
|
}
|
|
4843
5315
|
)
|
|
4844
|
-
] }) : showEmpty ? /* @__PURE__ */ jsx5("div", { className: "p-2 text-og-sm text-og-fg-subtle", children: emptyState ?? "This directory is empty" }) : (
|
|
5316
|
+
] }) : showEmpty ? /* @__PURE__ */ jsx5("div", { className: "p-2 text-og-sm text-og-fg-subtle", children: emptyState ?? "This directory is empty" }) : !usesVirtualTree ? /* @__PURE__ */ jsx5(
|
|
5317
|
+
"div",
|
|
5318
|
+
{
|
|
5319
|
+
id: treeId,
|
|
5320
|
+
role: "tree",
|
|
5321
|
+
"aria-label": "Workspace files",
|
|
5322
|
+
"aria-activedescendant": activeDescendantId,
|
|
5323
|
+
"aria-multiselectable": false,
|
|
5324
|
+
tabIndex: 0,
|
|
5325
|
+
className: "min-h-0 min-w-0 flex-1 overflow-auto outline-hidden focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-og-accent",
|
|
5326
|
+
children: renderItems.map((item) => /* @__PURE__ */ jsx5("div", { className: "min-w-0", children: renderItem(item) }, itemKey(item)))
|
|
5327
|
+
}
|
|
5328
|
+
) : (
|
|
4845
5329
|
// Virtualized: only the rows in/near the viewport mount, so a 3k-node
|
|
4846
5330
|
// tree stays responsive. The flat item list already carries the
|
|
4847
5331
|
// expanded subtree + synthetic (create/skeleton/residue) rows.
|
|
@@ -4855,7 +5339,7 @@ function FileBrowser({
|
|
|
4855
5339
|
"aria-activedescendant": activeDescendantId,
|
|
4856
5340
|
"aria-multiselectable": false,
|
|
4857
5341
|
tabIndex: 0,
|
|
4858
|
-
className: "min-h-0 flex-1 outline-
|
|
5342
|
+
className: "min-h-0 flex-1 outline-hidden focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-og-accent",
|
|
4859
5343
|
itemSize: coarsePointer ? 44 : 28,
|
|
4860
5344
|
ssrCount: Math.min(32, renderItems.length),
|
|
4861
5345
|
children: renderItems.map((item) => /* @__PURE__ */ jsx5("div", { className: "min-w-0", children: renderItem(item) }, itemKey(item)))
|
|
@@ -4941,11 +5425,11 @@ function DeleteDialog({
|
|
|
4941
5425
|
}
|
|
4942
5426
|
},
|
|
4943
5427
|
children: /* @__PURE__ */ jsxs5(AlertDialog.Portal, { container: typeof document === "undefined" ? null : document.body, children: [
|
|
4944
|
-
/* @__PURE__ */ jsx5(AlertDialog.Overlay, { className: "fixed inset-0 z-[100] bg-black/45 backdrop-blur-[2px]" }),
|
|
5428
|
+
/* @__PURE__ */ jsx5(AlertDialog.Overlay, { className: "og-root fixed inset-0 z-[100] bg-black/45 backdrop-blur-[2px]" }),
|
|
4945
5429
|
/* @__PURE__ */ jsxs5(
|
|
4946
5430
|
AlertDialog.Content,
|
|
4947
5431
|
{
|
|
4948
|
-
className: "og-root fixed left-1/2 top-1/2 z-[101] max-h-[calc(100dvh-2rem)] w-[calc(100%-2rem)] max-w-sm -translate-x-1/2 -translate-y-1/2 overflow-y-auto rounded-og-lg border border-og-border-strong bg-og-surface-1 p-4 text-og-fg shadow-2xl outline-
|
|
5432
|
+
className: "og-root fixed left-1/2 top-1/2 z-[101] max-h-[calc(100dvh-2rem)] w-[calc(100%-2rem)] max-w-sm -translate-x-1/2 -translate-y-1/2 overflow-y-auto rounded-og-lg border border-og-border-strong bg-og-surface-1 p-4 text-og-fg shadow-2xl outline-hidden",
|
|
4949
5433
|
style: portalTokenStyle,
|
|
4950
5434
|
onOpenAutoFocus: (event) => {
|
|
4951
5435
|
event.preventDefault();
|
|
@@ -5090,7 +5574,7 @@ function InlineInput({
|
|
|
5090
5574
|
className: cn(
|
|
5091
5575
|
"min-w-0 flex-1 rounded-og-sm border bg-og-bg px-1 py-0 text-og-sm",
|
|
5092
5576
|
"border-og-accent text-og-fg",
|
|
5093
|
-
"outline-
|
|
5577
|
+
"outline-hidden"
|
|
5094
5578
|
)
|
|
5095
5579
|
}
|
|
5096
5580
|
)
|
|
@@ -5189,18 +5673,19 @@ function ContextMenu({
|
|
|
5189
5673
|
// src/components/workbench-changes.tsx
|
|
5190
5674
|
import { ArrowUpRightIcon, FileWarningIcon, HistoryIcon } from "lucide-react";
|
|
5191
5675
|
import {
|
|
5676
|
+
Component,
|
|
5192
5677
|
useCallback as useCallback18,
|
|
5193
5678
|
useEffect as useEffect17,
|
|
5194
5679
|
useId as useId3,
|
|
5195
5680
|
useLayoutEffect as useLayoutEffect2,
|
|
5196
|
-
useMemo as
|
|
5681
|
+
useMemo as useMemo11,
|
|
5197
5682
|
useRef as useRef15,
|
|
5198
5683
|
useState as useState14
|
|
5199
5684
|
} from "react";
|
|
5200
5685
|
import { VList as VList2 } from "virtua";
|
|
5201
5686
|
|
|
5202
5687
|
// src/hooks/use-windowed-sections.ts
|
|
5203
|
-
import { useCallback as useCallback17, useEffect as useEffect16, useMemo as
|
|
5688
|
+
import { useCallback as useCallback17, useEffect as useEffect16, useMemo as useMemo10, useRef as useRef14, useState as useState13 } from "react";
|
|
5204
5689
|
function computeWindowRange(heights, scrollTop, viewport, overscan) {
|
|
5205
5690
|
const n = heights.length;
|
|
5206
5691
|
if (n === 0) return { start: 0, end: 0 };
|
|
@@ -5237,10 +5722,10 @@ function useWindowedSections(opts) {
|
|
|
5237
5722
|
);
|
|
5238
5723
|
useEffect16(() => {
|
|
5239
5724
|
setHeights((prev) => {
|
|
5240
|
-
|
|
5241
|
-
return
|
|
5725
|
+
const next = Array.from({ length: count }, (_, i) => estimateHeight(i));
|
|
5726
|
+
return prev.length === next.length && prev.every((height, index) => height === next[index]) ? prev : next;
|
|
5242
5727
|
});
|
|
5243
|
-
}, [count]);
|
|
5728
|
+
}, [count, estimateHeight]);
|
|
5244
5729
|
const heightsRef = useRef14(heights);
|
|
5245
5730
|
heightsRef.current = heights;
|
|
5246
5731
|
const [range, setRange] = useState13(() => ({
|
|
@@ -5283,7 +5768,7 @@ function useWindowedSections(opts) {
|
|
|
5283
5768
|
return next;
|
|
5284
5769
|
});
|
|
5285
5770
|
}, []);
|
|
5286
|
-
const offsets =
|
|
5771
|
+
const offsets = useMemo10(() => {
|
|
5287
5772
|
const out = new Array(count + 1);
|
|
5288
5773
|
out[0] = 0;
|
|
5289
5774
|
for (let i = 0; i < count; i++) out[i + 1] = (out[i] ?? 0) + Math.max(heights[i] ?? 0, 0);
|
|
@@ -5348,6 +5833,101 @@ function compactFileLabel(file) {
|
|
|
5348
5833
|
const parent = slash >= 0 ? file.path.slice(0, slash + 1) : "";
|
|
5349
5834
|
return `${STATUS_LETTER[file.status]} \xB7 ${basename}${parent ? ` \u2014 ${parent}` : ""}`;
|
|
5350
5835
|
}
|
|
5836
|
+
var ChangesRailVList = class extends Component {
|
|
5837
|
+
state = {
|
|
5838
|
+
renderedRowCount: this.props.rowCount,
|
|
5839
|
+
resetVersion: 0
|
|
5840
|
+
};
|
|
5841
|
+
host = null;
|
|
5842
|
+
listHandle = null;
|
|
5843
|
+
pendingFocusedPath = null;
|
|
5844
|
+
restoreFrame = null;
|
|
5845
|
+
static getDerivedStateFromProps(props, state) {
|
|
5846
|
+
if (props.rowCount === state.renderedRowCount) return null;
|
|
5847
|
+
return {
|
|
5848
|
+
renderedRowCount: props.rowCount,
|
|
5849
|
+
resetVersion: props.rowCount < state.renderedRowCount ? state.resetVersion + 1 : state.resetVersion
|
|
5850
|
+
};
|
|
5851
|
+
}
|
|
5852
|
+
getSnapshotBeforeUpdate(_previousProps, previousState) {
|
|
5853
|
+
if (previousState.resetVersion === this.state.resetVersion) return null;
|
|
5854
|
+
const scroller = this.host?.firstElementChild;
|
|
5855
|
+
if (!(scroller instanceof HTMLElement)) return null;
|
|
5856
|
+
const active = document.activeElement;
|
|
5857
|
+
const focusedRow = active instanceof HTMLElement && scroller.contains(active) ? active.closest("[data-rail-file-path]") : null;
|
|
5858
|
+
return {
|
|
5859
|
+
focusedPath: focusedRow?.dataset.railFilePath ?? this.pendingFocusedPath,
|
|
5860
|
+
scrollOffset: scroller.scrollTop
|
|
5861
|
+
};
|
|
5862
|
+
}
|
|
5863
|
+
componentDidUpdate(_previousProps, _previousState, snapshot) {
|
|
5864
|
+
if (!snapshot || !this.listHandle) return;
|
|
5865
|
+
this.cancelRestoreFrame();
|
|
5866
|
+
const maxOffset = Math.max(0, this.listHandle.scrollSize - this.listHandle.viewportSize);
|
|
5867
|
+
this.listHandle.scrollTo(Math.min(Math.max(snapshot.scrollOffset, 0), maxOffset));
|
|
5868
|
+
const focusedPath = snapshot.focusedPath;
|
|
5869
|
+
if (!focusedPath) return;
|
|
5870
|
+
this.pendingFocusedPath = focusedPath;
|
|
5871
|
+
this.continueFocusRestore();
|
|
5872
|
+
}
|
|
5873
|
+
componentWillUnmount() {
|
|
5874
|
+
this.cancelRestoreFrame();
|
|
5875
|
+
this.pendingFocusedPath = null;
|
|
5876
|
+
}
|
|
5877
|
+
cancelRestoreFrame() {
|
|
5878
|
+
if (this.restoreFrame === null) return;
|
|
5879
|
+
if (typeof cancelAnimationFrame === "function") cancelAnimationFrame(this.restoreFrame);
|
|
5880
|
+
this.restoreFrame = null;
|
|
5881
|
+
}
|
|
5882
|
+
continueFocusRestore = () => {
|
|
5883
|
+
const path = this.pendingFocusedPath;
|
|
5884
|
+
if (!path) return;
|
|
5885
|
+
if (this.restoreFocusedPath(path)) {
|
|
5886
|
+
this.pendingFocusedPath = null;
|
|
5887
|
+
return;
|
|
5888
|
+
}
|
|
5889
|
+
if (this.restoreFrame === null && typeof requestAnimationFrame === "function") {
|
|
5890
|
+
this.restoreFrame = requestAnimationFrame(() => {
|
|
5891
|
+
this.restoreFrame = null;
|
|
5892
|
+
this.continueFocusRestore();
|
|
5893
|
+
});
|
|
5894
|
+
}
|
|
5895
|
+
};
|
|
5896
|
+
restoreFocusedPath(path) {
|
|
5897
|
+
const host = this.host;
|
|
5898
|
+
if (!host) return true;
|
|
5899
|
+
const active = document.activeElement;
|
|
5900
|
+
if (active instanceof HTMLElement && active.isConnected && active !== document.body) {
|
|
5901
|
+
return true;
|
|
5902
|
+
}
|
|
5903
|
+
if (!this.props.files.some((file) => file.path === path)) return true;
|
|
5904
|
+
const row = Array.from(host.querySelectorAll("[data-rail-file-path]")).find(
|
|
5905
|
+
(candidate) => candidate.dataset.railFilePath === path
|
|
5906
|
+
);
|
|
5907
|
+
if (!row) return false;
|
|
5908
|
+
row.focus({ preventScroll: true });
|
|
5909
|
+
return true;
|
|
5910
|
+
}
|
|
5911
|
+
setHost = (host) => {
|
|
5912
|
+
this.host = host;
|
|
5913
|
+
};
|
|
5914
|
+
setListHandle = (handle) => {
|
|
5915
|
+
this.listHandle = handle;
|
|
5916
|
+
};
|
|
5917
|
+
render() {
|
|
5918
|
+
return /* @__PURE__ */ jsx6("div", { ref: this.setHost, "data-opengeni-changes-rail": true, className: this.props.className, children: /* @__PURE__ */ jsx6(
|
|
5919
|
+
VList2,
|
|
5920
|
+
{
|
|
5921
|
+
ref: this.setListHandle,
|
|
5922
|
+
className: "h-full",
|
|
5923
|
+
itemSize: this.props.coarsePointer ? 44 : 28,
|
|
5924
|
+
ssrCount: Math.min(30, this.props.rowCount),
|
|
5925
|
+
children: this.props.children
|
|
5926
|
+
},
|
|
5927
|
+
this.state.resetVersion
|
|
5928
|
+
) });
|
|
5929
|
+
}
|
|
5930
|
+
};
|
|
5351
5931
|
function buildRail(files) {
|
|
5352
5932
|
const roots = new Set(
|
|
5353
5933
|
files.flatMap((file) => file.repoRoot === void 0 ? [] : [file.repoRoot])
|
|
@@ -5406,12 +5986,12 @@ function WorkbenchChanges({
|
|
|
5406
5986
|
const [layout, setLayout] = useState14("unified");
|
|
5407
5987
|
const [activePath, setActivePath] = useState14(null);
|
|
5408
5988
|
const pickerId = useId3();
|
|
5409
|
-
const unicodeFontPaths =
|
|
5989
|
+
const unicodeFontPaths = useMemo11(
|
|
5410
5990
|
() => diff.flatMap((file) => file.oldPath ? [file.path, file.oldPath] : [file.path]),
|
|
5411
5991
|
[diff]
|
|
5412
5992
|
);
|
|
5413
5993
|
useUnicodeFallbackFonts(unicodeFontPaths);
|
|
5414
|
-
const { orderedFiles, rows, grouped } =
|
|
5994
|
+
const { orderedFiles, rows, grouped } = useMemo11(() => buildRail(diff), [diff]);
|
|
5415
5995
|
const selectedIndex = orderedFiles.findIndex((file) => file.path === activePath);
|
|
5416
5996
|
const activeIndex = selectedIndex >= 0 ? selectedIndex : 0;
|
|
5417
5997
|
useChangesLayoutEffect(() => {
|
|
@@ -5441,8 +6021,8 @@ function WorkbenchChanges({
|
|
|
5441
6021
|
return () => query.removeListener(update);
|
|
5442
6022
|
}
|
|
5443
6023
|
}, []);
|
|
5444
|
-
const additions =
|
|
5445
|
-
const deletions =
|
|
6024
|
+
const additions = useMemo11(() => diff.reduce((sum, f) => sum + f.additions, 0), [diff]);
|
|
6025
|
+
const deletions = useMemo11(() => diff.reduce((sum, f) => sum + f.deletions, 0), [diff]);
|
|
5446
6026
|
const estimateHeight = useCallback18(
|
|
5447
6027
|
(index) => {
|
|
5448
6028
|
const file = orderedFiles[index];
|
|
@@ -5555,27 +6135,38 @@ function WorkbenchChanges({
|
|
|
5555
6135
|
},
|
|
5556
6136
|
title: activeFile?.path,
|
|
5557
6137
|
"data-compact-file-picker": true,
|
|
5558
|
-
className: "h-11 w-full rounded-og-md border border-og-border bg-og-bg px-3 font-og-mono text-og-sm text-og-fg outline-
|
|
6138
|
+
className: "h-11 w-full rounded-og-md border border-og-border bg-og-bg px-3 font-og-mono text-og-sm text-og-fg outline-hidden transition-colors focus:border-og-accent focus:ring-2 focus:ring-og-accent-soft",
|
|
5559
6139
|
children: orderedFiles.map((file, index) => /* @__PURE__ */ jsx6("option", { value: index, children: compactFileLabel(file) }, file.path))
|
|
5560
6140
|
}
|
|
5561
6141
|
)
|
|
5562
6142
|
] }),
|
|
5563
|
-
/* @__PURE__ */ jsx6(
|
|
5564
|
-
|
|
6143
|
+
/* @__PURE__ */ jsx6(
|
|
6144
|
+
"div",
|
|
5565
6145
|
{
|
|
5566
|
-
|
|
5567
|
-
|
|
5568
|
-
|
|
5569
|
-
|
|
6146
|
+
"aria-label": "Changed file diff",
|
|
6147
|
+
className: "min-h-0 min-w-0 flex-1 overflow-auto outline-hidden focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-og-accent",
|
|
6148
|
+
"data-opengeni-changes-pane": true,
|
|
6149
|
+
role: "region",
|
|
6150
|
+
tabIndex: 0,
|
|
6151
|
+
children: activeFile ? /* @__PURE__ */ jsx6(
|
|
6152
|
+
DiffSection,
|
|
6153
|
+
{
|
|
6154
|
+
file: activeFile,
|
|
6155
|
+
layout: "unified",
|
|
6156
|
+
themeType: resolvedTheme,
|
|
6157
|
+
...onOpenFile ? { onOpenFile } : {}
|
|
6158
|
+
}
|
|
6159
|
+
) : null
|
|
5570
6160
|
}
|
|
5571
|
-
)
|
|
6161
|
+
)
|
|
5572
6162
|
] }) : /* @__PURE__ */ jsxs6("div", { className: "flex min-h-0 flex-1", children: [
|
|
5573
|
-
/* @__PURE__ */ jsx6(
|
|
5574
|
-
|
|
6163
|
+
/* @__PURE__ */ jsx6(
|
|
6164
|
+
ChangesRailVList,
|
|
5575
6165
|
{
|
|
5576
|
-
|
|
5577
|
-
|
|
5578
|
-
|
|
6166
|
+
rowCount: rows.length,
|
|
6167
|
+
coarsePointer,
|
|
6168
|
+
files: orderedFiles,
|
|
6169
|
+
className: "w-[clamp(12rem,28%,15rem)] shrink-0 border-r border-og-border bg-og-surface-1/35",
|
|
5579
6170
|
children: rows.map(
|
|
5580
6171
|
(row) => row.kind === "group" ? /* @__PURE__ */ jsxs6(
|
|
5581
6172
|
"div",
|
|
@@ -5600,13 +6191,16 @@ function WorkbenchChanges({
|
|
|
5600
6191
|
)
|
|
5601
6192
|
)
|
|
5602
6193
|
}
|
|
5603
|
-
)
|
|
6194
|
+
),
|
|
5604
6195
|
/* @__PURE__ */ jsx6(
|
|
5605
6196
|
"div",
|
|
5606
6197
|
{
|
|
5607
6198
|
ref: windowed.scrollRef,
|
|
5608
|
-
|
|
6199
|
+
"aria-label": "Changed files diff",
|
|
6200
|
+
className: "min-h-0 min-w-0 flex-1 overflow-auto outline-hidden focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-og-accent",
|
|
5609
6201
|
"data-opengeni-changes-pane": true,
|
|
6202
|
+
role: "region",
|
|
6203
|
+
tabIndex: 0,
|
|
5610
6204
|
children: /* @__PURE__ */ jsx6("div", { style: { position: "relative", height: windowed.totalHeight }, children: orderedFiles.map((file, index) => {
|
|
5611
6205
|
if (index < windowed.range.start || index >= windowed.range.end) return null;
|
|
5612
6206
|
return /* @__PURE__ */ jsx6(
|
|
@@ -5675,8 +6269,9 @@ function RailFileRow({
|
|
|
5675
6269
|
onClick,
|
|
5676
6270
|
title: file.path,
|
|
5677
6271
|
"data-rail-file": true,
|
|
6272
|
+
"data-rail-file-path": file.path,
|
|
5678
6273
|
className: cn(
|
|
5679
|
-
"relative flex min-h-7 w-full items-center gap-1.5 truncate px-2 py-0.5 text-left text-og-sm transition-colors hover:bg-og-surface-2 focus-visible:outline-
|
|
6274
|
+
"relative flex min-h-7 w-full items-center gap-1.5 truncate px-2 py-0.5 text-left text-og-sm transition-colors hover:bg-og-surface-2 focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-og-accent pointer-coarse:min-h-11",
|
|
5680
6275
|
grouped && "pl-3",
|
|
5681
6276
|
active && "bg-og-accent-soft text-og-fg before:absolute before:inset-y-1 before:left-0 before:w-0.5 before:rounded-r-full before:bg-og-accent"
|
|
5682
6277
|
),
|
|
@@ -5774,7 +6369,7 @@ function GuardBody({
|
|
|
5774
6369
|
{
|
|
5775
6370
|
type: "button",
|
|
5776
6371
|
onClick: () => onOpenFile(file.path),
|
|
5777
|
-
className: "inline-flex min-h-8 shrink-0 items-center gap-1.5 rounded-og-sm border border-og-border px-2 py-1 text-og-xs font-medium text-og-fg-muted transition-colors hover:border-og-border-strong hover:bg-og-surface-2 hover:text-og-fg focus-visible:outline-
|
|
6372
|
+
className: "inline-flex min-h-8 shrink-0 items-center gap-1.5 rounded-og-sm border border-og-border px-2 py-1 text-og-xs font-medium text-og-fg-muted transition-colors hover:border-og-border-strong hover:bg-og-surface-2 hover:text-og-fg focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-og-accent pointer-coarse:min-h-11",
|
|
5778
6373
|
children: [
|
|
5779
6374
|
"Open in Files",
|
|
5780
6375
|
/* @__PURE__ */ jsx6(ArrowUpRightIcon, { className: "size-3.5", "aria-hidden": true })
|
|
@@ -5793,7 +6388,7 @@ function LayoutToggle({
|
|
|
5793
6388
|
type: "button",
|
|
5794
6389
|
onClick: () => onChange(value),
|
|
5795
6390
|
className: cn(
|
|
5796
|
-
"min-h-7 rounded-og-xs px-1.5 py-0.5 text-og-xs capitalize focus-visible:outline-
|
|
6391
|
+
"min-h-7 rounded-og-xs px-1.5 py-0.5 text-og-xs capitalize focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-og-accent max-[1023px]:min-w-11 pointer-coarse:min-h-11 pointer-coarse:min-w-11",
|
|
5797
6392
|
layout === value ? "bg-og-accent-soft text-og-fg" : "text-og-fg-subtle hover:text-og-fg"
|
|
5798
6393
|
),
|
|
5799
6394
|
children: value
|
|
@@ -5912,7 +6507,7 @@ import { Loader2Icon, SaveIcon } from "lucide-react";
|
|
|
5912
6507
|
import {
|
|
5913
6508
|
useCallback as useCallback19,
|
|
5914
6509
|
useEffect as useEffect19,
|
|
5915
|
-
useMemo as
|
|
6510
|
+
useMemo as useMemo12,
|
|
5916
6511
|
useRef as useRef16,
|
|
5917
6512
|
useState as useState16
|
|
5918
6513
|
} from "react";
|
|
@@ -5960,6 +6555,19 @@ function languageForPath(path) {
|
|
|
5960
6555
|
return null;
|
|
5961
6556
|
}
|
|
5962
6557
|
}
|
|
6558
|
+
function reconcileEditorBuffer(current, incoming) {
|
|
6559
|
+
if (current.path === incoming.path && current.value !== current.baseline) {
|
|
6560
|
+
return current;
|
|
6561
|
+
}
|
|
6562
|
+
if (current.path === incoming.path && current.value === incoming.contents && current.baseline === incoming.contents) {
|
|
6563
|
+
return current;
|
|
6564
|
+
}
|
|
6565
|
+
return {
|
|
6566
|
+
path: incoming.path,
|
|
6567
|
+
value: incoming.contents,
|
|
6568
|
+
baseline: incoming.contents
|
|
6569
|
+
};
|
|
6570
|
+
}
|
|
5963
6571
|
function CodeEditor({
|
|
5964
6572
|
path,
|
|
5965
6573
|
initialContents,
|
|
@@ -5975,28 +6583,30 @@ function CodeEditor({
|
|
|
5975
6583
|
}) {
|
|
5976
6584
|
const [failed, setFailed] = useState16(false);
|
|
5977
6585
|
const [bundle, setBundle] = useState16(null);
|
|
5978
|
-
const [
|
|
5979
|
-
|
|
6586
|
+
const [buffer, setBuffer] = useState16(() => ({
|
|
6587
|
+
path,
|
|
6588
|
+
value: initialContents,
|
|
6589
|
+
baseline: initialContents
|
|
6590
|
+
}));
|
|
6591
|
+
const { value, baseline } = buffer;
|
|
5980
6592
|
const [saving, setSaving] = useState16(false);
|
|
5981
6593
|
const [saveError, setSaveError] = useState16(null);
|
|
5982
6594
|
const [savedTick, setSavedTick] = useState16(false);
|
|
6595
|
+
const saveInFlightRef = useRef16(false);
|
|
5983
6596
|
const dirty = value !== baseline;
|
|
5984
6597
|
const saveConflict = saveError !== null && saveError.code === "file_write_conflict";
|
|
5985
6598
|
const editIntentFiredRef = useRef16(false);
|
|
5986
|
-
const lastSeed = useRef16({ path, contents: initialContents });
|
|
5987
6599
|
useEffect19(() => {
|
|
5988
|
-
|
|
5989
|
-
|
|
5990
|
-
|
|
6600
|
+
setBuffer((current) => reconcileEditorBuffer(current, { path, contents: initialContents }));
|
|
6601
|
+
}, [path, initialContents]);
|
|
6602
|
+
useEffect19(() => {
|
|
5991
6603
|
editIntentFiredRef.current = false;
|
|
5992
|
-
setValue(initialContents);
|
|
5993
|
-
setBaseline(initialContents);
|
|
5994
6604
|
setSaveError(null);
|
|
5995
6605
|
setSavedTick(false);
|
|
5996
|
-
}, [path
|
|
6606
|
+
}, [path]);
|
|
5997
6607
|
const noteEdit = useCallback19(
|
|
5998
6608
|
(next) => {
|
|
5999
|
-
|
|
6609
|
+
setBuffer((current) => ({ ...current, value: next }));
|
|
6000
6610
|
setSavedTick(false);
|
|
6001
6611
|
if (!editIntentFiredRef.current && next !== baseline) {
|
|
6002
6612
|
editIntentFiredRef.current = true;
|
|
@@ -6005,7 +6615,7 @@ function CodeEditor({
|
|
|
6005
6615
|
},
|
|
6006
6616
|
[baseline, onEditIntent]
|
|
6007
6617
|
);
|
|
6008
|
-
const langKey =
|
|
6618
|
+
const langKey = useMemo12(() => languageForPath(path), [path]);
|
|
6009
6619
|
useEffect19(() => {
|
|
6010
6620
|
let cancelled = false;
|
|
6011
6621
|
void (async () => {
|
|
@@ -6049,38 +6659,44 @@ function CodeEditor({
|
|
|
6049
6659
|
const saveRef = useRef16(() => {
|
|
6050
6660
|
});
|
|
6051
6661
|
const save = useCallback19(async () => {
|
|
6052
|
-
if (readOnly) return;
|
|
6662
|
+
if (readOnly || saveInFlightRef.current) return;
|
|
6053
6663
|
const snapshot = value;
|
|
6054
6664
|
if (snapshot === baseline) return;
|
|
6665
|
+
saveInFlightRef.current = true;
|
|
6055
6666
|
setSaving(true);
|
|
6056
6667
|
setSaveError(null);
|
|
6057
6668
|
setSavedTick(false);
|
|
6058
6669
|
try {
|
|
6059
|
-
await onSave(snapshot);
|
|
6060
|
-
|
|
6061
|
-
|
|
6670
|
+
await onSave(snapshot, baseline);
|
|
6671
|
+
setBuffer(
|
|
6672
|
+
(current) => current.path === path ? { ...current, baseline: snapshot } : current
|
|
6673
|
+
);
|
|
6062
6674
|
setSavedTick(true);
|
|
6063
6675
|
} catch (cause) {
|
|
6064
6676
|
setSaveError(cause instanceof Error ? cause : new Error(String(cause)));
|
|
6065
6677
|
} finally {
|
|
6678
|
+
saveInFlightRef.current = false;
|
|
6066
6679
|
setSaving(false);
|
|
6067
6680
|
}
|
|
6068
6681
|
}, [readOnly, value, baseline, onSave, path]);
|
|
6069
6682
|
const overwrite = useCallback19(async () => {
|
|
6070
|
-
if (readOnly || !onOverwrite) return;
|
|
6683
|
+
if (readOnly || !onOverwrite || saveInFlightRef.current) return;
|
|
6071
6684
|
const snapshot = value;
|
|
6072
6685
|
if (snapshot === baseline) return;
|
|
6686
|
+
saveInFlightRef.current = true;
|
|
6073
6687
|
setSaving(true);
|
|
6074
6688
|
setSaveError(null);
|
|
6075
6689
|
setSavedTick(false);
|
|
6076
6690
|
try {
|
|
6077
6691
|
await onOverwrite(snapshot);
|
|
6078
|
-
|
|
6079
|
-
|
|
6692
|
+
setBuffer(
|
|
6693
|
+
(current) => current.path === path ? { ...current, baseline: snapshot } : current
|
|
6694
|
+
);
|
|
6080
6695
|
setSavedTick(true);
|
|
6081
6696
|
} catch (cause) {
|
|
6082
6697
|
setSaveError(cause instanceof Error ? cause : new Error(String(cause)));
|
|
6083
6698
|
} finally {
|
|
6699
|
+
saveInFlightRef.current = false;
|
|
6084
6700
|
setSaving(false);
|
|
6085
6701
|
}
|
|
6086
6702
|
}, [readOnly, onOverwrite, value, baseline, path]);
|
|
@@ -6093,18 +6709,26 @@ function CodeEditor({
|
|
|
6093
6709
|
return () => clearTimeout(t);
|
|
6094
6710
|
}, [savedTick]);
|
|
6095
6711
|
const fileName = path.split("/").filter(Boolean).pop() ?? path;
|
|
6096
|
-
const cmExtensions =
|
|
6712
|
+
const cmExtensions = useMemo12(() => {
|
|
6097
6713
|
if (!bundle) return [];
|
|
6098
6714
|
const exts = [bundle.saveKeymapExtension(() => saveRef.current())];
|
|
6099
6715
|
if (bundle.languageExtension) exts.push(bundle.languageExtension);
|
|
6100
6716
|
return exts;
|
|
6101
6717
|
}, [bundle]);
|
|
6102
6718
|
const Editor = bundle?.Editor;
|
|
6719
|
+
const reload = useCallback19(() => {
|
|
6720
|
+
setBuffer({ path, value: initialContents, baseline: initialContents });
|
|
6721
|
+
editIntentFiredRef.current = false;
|
|
6722
|
+
setSaveError(null);
|
|
6723
|
+
setSavedTick(false);
|
|
6724
|
+
onReload?.();
|
|
6725
|
+
}, [initialContents, onReload, path]);
|
|
6103
6726
|
return /* @__PURE__ */ jsxs7(
|
|
6104
6727
|
"div",
|
|
6105
6728
|
{
|
|
6106
6729
|
className: cn("flex h-full min-h-0 flex-col", className),
|
|
6107
6730
|
"data-opengeni-code-editor": true,
|
|
6731
|
+
"data-opengeni-editor-dirty": dirty ? "true" : "false",
|
|
6108
6732
|
style: editorVars,
|
|
6109
6733
|
children: [
|
|
6110
6734
|
/* @__PURE__ */ jsxs7("div", { className: "flex min-h-9 shrink-0 items-center gap-2 border-b border-og-border bg-og-surface-1 px-2 py-1", children: [
|
|
@@ -6131,7 +6755,7 @@ function CodeEditor({
|
|
|
6131
6755
|
children: saveError.message
|
|
6132
6756
|
}
|
|
6133
6757
|
),
|
|
6134
|
-
!saveError && savedTick && /* @__PURE__ */ jsx9("span", { className: "text-og-xs text-og-status-idle", children: "Saved" }),
|
|
6758
|
+
!saveError && savedTick && !dirty && /* @__PURE__ */ jsx9("span", { className: "text-og-xs text-og-status-idle", children: "Saved" }),
|
|
6135
6759
|
readOnly ? /* @__PURE__ */ jsx9("span", { className: "text-og-xs uppercase tracking-wide text-og-fg-subtle", children: "Read-only" }) : !saveConflict ? /* @__PURE__ */ jsxs7(
|
|
6136
6760
|
"button",
|
|
6137
6761
|
{
|
|
@@ -6168,8 +6792,7 @@ function CodeEditor({
|
|
|
6168
6792
|
{
|
|
6169
6793
|
type: "button",
|
|
6170
6794
|
onClick: () => {
|
|
6171
|
-
|
|
6172
|
-
onReload();
|
|
6795
|
+
reload();
|
|
6173
6796
|
},
|
|
6174
6797
|
className: "min-h-8 rounded-og-sm border border-og-border bg-og-surface-1 px-2 text-og-xs font-medium text-og-fg hover:bg-og-surface-2 pointer-coarse:min-h-11",
|
|
6175
6798
|
children: "Reload live"
|
|
@@ -6239,7 +6862,7 @@ function PlainTextarea({
|
|
|
6239
6862
|
}
|
|
6240
6863
|
},
|
|
6241
6864
|
"aria-label": "Plain text file editor",
|
|
6242
|
-
className: "h-full w-full resize-none bg-transparent p-2 font-og-mono text-og-sm text-og-fg outline-
|
|
6865
|
+
className: "h-full w-full resize-none bg-transparent p-2 font-og-mono text-og-sm text-og-fg outline-hidden"
|
|
6243
6866
|
}
|
|
6244
6867
|
);
|
|
6245
6868
|
}
|
|
@@ -6250,6 +6873,7 @@ function EditorSkeleton() {
|
|
|
6250
6873
|
// src/components/sandbox-files.tsx
|
|
6251
6874
|
import { FileCode2Icon, FileWarningIcon as FileWarningIcon2, LoaderCircleIcon } from "lucide-react";
|
|
6252
6875
|
import { useCallback as useCallback20, useEffect as useEffect20, useRef as useRef17, useState as useState17 } from "react";
|
|
6876
|
+
import { Group, Panel, Separator } from "react-resizable-panels";
|
|
6253
6877
|
import { Fragment as Fragment3, jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
6254
6878
|
function SandboxFiles({
|
|
6255
6879
|
files,
|
|
@@ -6297,17 +6921,30 @@ function SandboxFiles({
|
|
|
6297
6921
|
const [wide, setWide] = useState17(false);
|
|
6298
6922
|
useEffect20(() => {
|
|
6299
6923
|
const el = rootRef.current;
|
|
6300
|
-
if (!el
|
|
6301
|
-
const
|
|
6302
|
-
|
|
6303
|
-
|
|
6304
|
-
|
|
6924
|
+
if (!el) return;
|
|
6925
|
+
const update = () => setWide(el.getBoundingClientRect().width >= 720);
|
|
6926
|
+
update();
|
|
6927
|
+
if (typeof ResizeObserver === "undefined") return;
|
|
6928
|
+
const observer = new ResizeObserver(update);
|
|
6305
6929
|
observer.observe(el);
|
|
6306
|
-
|
|
6930
|
+
const surface = el.closest("[data-workspace-surface]");
|
|
6931
|
+
const surfaceObserver = surface && typeof MutationObserver !== "undefined" ? new MutationObserver(update) : null;
|
|
6932
|
+
surfaceObserver?.observe(surface, {
|
|
6933
|
+
attributes: true,
|
|
6934
|
+
attributeFilter: ["class", "style"]
|
|
6935
|
+
});
|
|
6936
|
+
return () => {
|
|
6937
|
+
observer.disconnect();
|
|
6938
|
+
surfaceObserver?.disconnect();
|
|
6939
|
+
};
|
|
6307
6940
|
}, []);
|
|
6308
6941
|
const resolvedTheme = useThemeType(themeType);
|
|
6309
6942
|
const viewPath = selected;
|
|
6310
|
-
const fileView = useFileView(
|
|
6943
|
+
const fileView = useFileView(
|
|
6944
|
+
viewPath,
|
|
6945
|
+
files.readFile,
|
|
6946
|
+
(files.contentRevision ?? 0) + viewReloadRevision
|
|
6947
|
+
);
|
|
6311
6948
|
const selectFile = useCallback20((path) => {
|
|
6312
6949
|
if (pendingRequestRef.current !== null) {
|
|
6313
6950
|
handledRequestRef.current = pendingRequestRef.current;
|
|
@@ -6322,18 +6959,6 @@ function SandboxFiles({
|
|
|
6322
6959
|
const showEditor = canEdit && editMode;
|
|
6323
6960
|
const captureFileUnavailable = fileView.error instanceof CapturedFileUnavailableError ? fileView.error : null;
|
|
6324
6961
|
const waitingForSelectedFile = liveRequestedPath === viewPath && (!liveWorkspaceReady || files.loading) && captureFileUnavailable !== null;
|
|
6325
|
-
if (!fileSystemAvailable) {
|
|
6326
|
-
return /* @__PURE__ */ jsx10(
|
|
6327
|
-
Notice,
|
|
6328
|
-
{
|
|
6329
|
-
className,
|
|
6330
|
-
icon: /* @__PURE__ */ jsx10(FileWarningIcon2, { className: "size-5", "aria-hidden": true }),
|
|
6331
|
-
title: "Files unavailable",
|
|
6332
|
-
announce: "alert",
|
|
6333
|
-
children: "This sandbox does not expose a file system."
|
|
6334
|
-
}
|
|
6335
|
-
);
|
|
6336
|
-
}
|
|
6337
6962
|
if (workspaceResting || workspaceWaking) {
|
|
6338
6963
|
return /* @__PURE__ */ jsx10("div", { className: cn("h-full", className), "data-opengeni-workspace-resting": true, children: /* @__PURE__ */ jsxs8(
|
|
6339
6964
|
Notice,
|
|
@@ -6354,162 +6979,206 @@ function SandboxFiles({
|
|
|
6354
6979
|
}
|
|
6355
6980
|
) });
|
|
6356
6981
|
}
|
|
6982
|
+
if (!fileSystemAvailable) {
|
|
6983
|
+
return /* @__PURE__ */ jsx10(
|
|
6984
|
+
Notice,
|
|
6985
|
+
{
|
|
6986
|
+
className,
|
|
6987
|
+
icon: /* @__PURE__ */ jsx10(FileWarningIcon2, { className: "size-5", "aria-hidden": true }),
|
|
6988
|
+
title: "Files unavailable",
|
|
6989
|
+
announce: "alert",
|
|
6990
|
+
children: "This sandbox does not expose a file system."
|
|
6991
|
+
}
|
|
6992
|
+
);
|
|
6993
|
+
}
|
|
6357
6994
|
return /* @__PURE__ */ jsxs8("div", { ref: rootRef, className: cn("flex h-full min-h-0 min-w-0 flex-col", className), children: [
|
|
6358
|
-
/* @__PURE__ */ jsx10(
|
|
6359
|
-
|
|
6360
|
-
|
|
6361
|
-
|
|
6362
|
-
{
|
|
6363
|
-
|
|
6364
|
-
|
|
6365
|
-
|
|
6366
|
-
|
|
6367
|
-
|
|
6368
|
-
|
|
6369
|
-
{
|
|
6370
|
-
result: files,
|
|
6371
|
-
selectedPath: selected ?? void 0,
|
|
6372
|
-
onSelectFile: selectFile,
|
|
6373
|
-
editable,
|
|
6374
|
-
emptyState: "This directory is empty",
|
|
6375
|
-
className: "min-w-0 flex-1"
|
|
6376
|
-
}
|
|
6377
|
-
)
|
|
6995
|
+
/* @__PURE__ */ jsx10(
|
|
6996
|
+
GitHeader,
|
|
6997
|
+
{
|
|
6998
|
+
loading: Boolean(files.loading || files.gitLoading || files.source === null || git?.loading) && files.gitSummary === null,
|
|
6999
|
+
git: files.gitSummary ?? {
|
|
7000
|
+
branch: git?.branch ?? null,
|
|
7001
|
+
isRepo: git?.isRepo ?? false,
|
|
7002
|
+
repoCount: git?.repoCount ?? 0,
|
|
7003
|
+
ahead: git?.ahead ?? 0,
|
|
7004
|
+
behind: git?.behind ?? 0,
|
|
7005
|
+
dirtyCount: git?.diff.length ?? 0
|
|
6378
7006
|
}
|
|
6379
|
-
|
|
6380
|
-
|
|
6381
|
-
|
|
6382
|
-
|
|
6383
|
-
|
|
6384
|
-
|
|
6385
|
-
|
|
6386
|
-
|
|
6387
|
-
|
|
6388
|
-
|
|
6389
|
-
|
|
6390
|
-
|
|
6391
|
-
|
|
6392
|
-
|
|
6393
|
-
|
|
6394
|
-
|
|
6395
|
-
|
|
6396
|
-
|
|
6397
|
-
canEdit && /* @__PURE__ */ jsx10(
|
|
6398
|
-
Segmented,
|
|
7007
|
+
}
|
|
7008
|
+
),
|
|
7009
|
+
/* @__PURE__ */ jsxs8(
|
|
7010
|
+
Group,
|
|
7011
|
+
{
|
|
7012
|
+
orientation: wide ? "horizontal" : "vertical",
|
|
7013
|
+
className: cn("min-h-0 flex-1", wide ? "flex-row" : "flex-col"),
|
|
7014
|
+
children: [
|
|
7015
|
+
/* @__PURE__ */ jsx10(
|
|
7016
|
+
Panel,
|
|
7017
|
+
{
|
|
7018
|
+
id: "sandbox-files-tree",
|
|
7019
|
+
defaultSize: wide ? "280px" : "40%",
|
|
7020
|
+
minSize: wide ? "180px" : "120px",
|
|
7021
|
+
maxSize: wide ? "60%" : "70%",
|
|
7022
|
+
className: "flex min-h-0 min-w-0 flex-col",
|
|
7023
|
+
children: /* @__PURE__ */ jsx10(
|
|
7024
|
+
FileBrowser,
|
|
6399
7025
|
{
|
|
6400
|
-
|
|
6401
|
-
|
|
6402
|
-
|
|
6403
|
-
|
|
6404
|
-
|
|
6405
|
-
|
|
7026
|
+
result: files,
|
|
7027
|
+
selectedPath: selected ?? void 0,
|
|
7028
|
+
onSelectFile: selectFile,
|
|
7029
|
+
editable,
|
|
7030
|
+
emptyState: "This directory is empty",
|
|
7031
|
+
className: "min-w-0 flex-1"
|
|
6406
7032
|
}
|
|
6407
7033
|
)
|
|
6408
|
-
|
|
6409
|
-
|
|
6410
|
-
|
|
6411
|
-
|
|
6412
|
-
|
|
6413
|
-
|
|
6414
|
-
|
|
6415
|
-
|
|
6416
|
-
|
|
6417
|
-
|
|
6418
|
-
|
|
6419
|
-
|
|
6420
|
-
|
|
6421
|
-
|
|
6422
|
-
|
|
6423
|
-
|
|
6424
|
-
|
|
6425
|
-
|
|
6426
|
-
|
|
7034
|
+
}
|
|
7035
|
+
),
|
|
7036
|
+
/* @__PURE__ */ jsx10(
|
|
7037
|
+
Separator,
|
|
7038
|
+
{
|
|
7039
|
+
"aria-label": "Resize file tree",
|
|
7040
|
+
className: cn(
|
|
7041
|
+
"group relative z-10 shrink-0 cursor-col-resize outline-hidden focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-og-accent",
|
|
7042
|
+
"w-px bg-og-border after:absolute after:inset-y-0 after:left-1/2 after:w-3 after:-translate-x-1/2",
|
|
7043
|
+
"hover:bg-og-accent focus-visible:bg-og-accent data-[separator-state=dragging]:bg-og-accent",
|
|
7044
|
+
!wide && "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"
|
|
7045
|
+
)
|
|
7046
|
+
}
|
|
7047
|
+
),
|
|
7048
|
+
/* @__PURE__ */ jsxs8(
|
|
7049
|
+
Panel,
|
|
7050
|
+
{
|
|
7051
|
+
id: "sandbox-files-viewer",
|
|
7052
|
+
minSize: wide ? "240px" : "160px",
|
|
7053
|
+
className: cn("flex min-h-0 min-w-0 flex-col", !wide && "border-t border-og-border"),
|
|
7054
|
+
children: [
|
|
7055
|
+
/* @__PURE__ */ jsxs8("div", { className: "flex shrink-0 items-center justify-between gap-2 border-b border-og-border bg-og-surface-1 px-2 py-1", children: [
|
|
7056
|
+
/* @__PURE__ */ jsx10(
|
|
7057
|
+
"span",
|
|
7058
|
+
{
|
|
7059
|
+
"data-opengeni-selected-file": true,
|
|
7060
|
+
className: "min-w-0 truncate font-og-mono text-og-xs text-og-fg-muted",
|
|
7061
|
+
children: selected ?? "No file selected"
|
|
7062
|
+
}
|
|
7063
|
+
),
|
|
7064
|
+
canEdit && /* @__PURE__ */ jsx10(
|
|
7065
|
+
Segmented,
|
|
7066
|
+
{
|
|
7067
|
+
options: [
|
|
7068
|
+
{ value: "view", label: "View" },
|
|
7069
|
+
{ value: "edit", label: "Edit" }
|
|
7070
|
+
],
|
|
7071
|
+
value: editMode ? "edit" : "view",
|
|
7072
|
+
onChange: (v) => setEditMode(v === "edit")
|
|
7073
|
+
}
|
|
7074
|
+
)
|
|
7075
|
+
] }),
|
|
7076
|
+
/* @__PURE__ */ jsx10("div", { className: "min-h-0 flex-1 overflow-auto", children: viewPath ? showEditor && fileView.content !== null ? /* @__PURE__ */ jsx10(
|
|
7077
|
+
CodeEditor,
|
|
6427
7078
|
{
|
|
6428
|
-
|
|
6429
|
-
|
|
7079
|
+
path: viewPath,
|
|
7080
|
+
initialContents: fileView.content,
|
|
7081
|
+
themeType: resolvedTheme,
|
|
7082
|
+
onSave: (contents, expectedContents) => files.writeFile(viewPath, contents, { expectedContent: expectedContents }),
|
|
7083
|
+
onOverwrite: (contents) => files.writeFile(viewPath, contents, { force: true }),
|
|
7084
|
+
onReload: () => setViewReloadRevision((revision) => revision + 1),
|
|
7085
|
+
...onEditIntent ? { onEditIntent } : {},
|
|
7086
|
+
className: "h-full"
|
|
7087
|
+
},
|
|
7088
|
+
viewPath
|
|
7089
|
+
) : waitingForSelectedFile ? /* @__PURE__ */ jsxs8(
|
|
7090
|
+
Notice,
|
|
7091
|
+
{
|
|
7092
|
+
icon: /* @__PURE__ */ jsx10(
|
|
7093
|
+
LoaderCircleIcon,
|
|
7094
|
+
{
|
|
7095
|
+
className: "size-5 animate-spin motion-reduce:animate-none",
|
|
7096
|
+
"aria-hidden": true
|
|
7097
|
+
}
|
|
7098
|
+
),
|
|
7099
|
+
title: "Waking workspace",
|
|
7100
|
+
announce: "status",
|
|
7101
|
+
children: [
|
|
7102
|
+
"Opening ",
|
|
7103
|
+
viewPath,
|
|
7104
|
+
" when the live file system is ready\u2026"
|
|
7105
|
+
]
|
|
6430
7106
|
}
|
|
6431
|
-
),
|
|
6432
|
-
|
|
6433
|
-
|
|
6434
|
-
|
|
6435
|
-
|
|
7107
|
+
) : captureFileUnavailable ? /* @__PURE__ */ jsxs8(Notice, { icon: /* @__PURE__ */ jsx10(FileCode2Icon, { className: "size-5", "aria-hidden": true }), title: "On machine", children: [
|
|
7108
|
+
/* @__PURE__ */ jsx10("p", { children: captureFileUnavailable.reason === "too-large" ? "This file is larger than the captured preview limit." : captureFileUnavailable.reason === "content-missing" ? "The captured copy is no longer available." : "This file was indexed, but it was not changed in the captured turn." }),
|
|
7109
|
+
onWakeWorkspace ? /* @__PURE__ */ jsx10(
|
|
7110
|
+
WakeButton,
|
|
7111
|
+
{
|
|
7112
|
+
onClick: () => {
|
|
7113
|
+
setLiveRequestedPath(viewPath);
|
|
7114
|
+
if (liveWorkspaceReady) void files.refresh();
|
|
7115
|
+
else onWakeWorkspace();
|
|
7116
|
+
},
|
|
7117
|
+
children: liveWorkspaceReady ? "Retry live file" : "Open live file"
|
|
7118
|
+
}
|
|
7119
|
+
) : null
|
|
7120
|
+
] }) : fileView.error ? /* @__PURE__ */ jsxs8(Notice, { announce: "alert", children: [
|
|
7121
|
+
"Could not open ",
|
|
6436
7122
|
viewPath,
|
|
6437
|
-
"
|
|
6438
|
-
|
|
6439
|
-
|
|
6440
|
-
|
|
6441
|
-
|
|
6442
|
-
|
|
6443
|
-
|
|
6444
|
-
|
|
6445
|
-
|
|
6446
|
-
|
|
6447
|
-
|
|
6448
|
-
|
|
6449
|
-
|
|
6450
|
-
|
|
6451
|
-
|
|
6452
|
-
|
|
6453
|
-
|
|
6454
|
-
|
|
6455
|
-
|
|
6456
|
-
": ",
|
|
6457
|
-
fileView.error.message
|
|
6458
|
-
] }) : fileView.loading ? /* @__PURE__ */ jsxs8(Notice, { announce: "status", children: [
|
|
6459
|
-
"Loading ",
|
|
6460
|
-
viewPath,
|
|
6461
|
-
"\u2026"
|
|
6462
|
-
] }) : fileView.isBinary ? /* @__PURE__ */ jsxs8(Notice, { children: [
|
|
6463
|
-
viewPath,
|
|
6464
|
-
" is a binary file (",
|
|
6465
|
-
fileView.sizeBytes ?? 0,
|
|
6466
|
-
" bytes)."
|
|
6467
|
-
] }) : fileView.content !== null ? /* @__PURE__ */ jsxs8(Fragment3, { children: [
|
|
6468
|
-
fileView.truncated && /* @__PURE__ */ jsxs8("div", { className: "border-b border-og-border bg-og-surface-1 px-2 py-1 text-og-xs text-og-status-running", children: [
|
|
6469
|
-
"Large file \u2014 showing a truncated preview (",
|
|
6470
|
-
fileView.sizeBytes ?? 0,
|
|
6471
|
-
" bytes loaded). Editing is disabled to avoid corrupting the file."
|
|
6472
|
-
] }),
|
|
6473
|
-
usePierre ? /* @__PURE__ */ jsx10(
|
|
6474
|
-
PierreFile,
|
|
6475
|
-
{
|
|
6476
|
-
path: viewPath,
|
|
6477
|
-
contents: fileView.content,
|
|
6478
|
-
themeType: resolvedTheme,
|
|
6479
|
-
fallback: /* @__PURE__ */ jsx10("pre", { className: "overflow-auto whitespace-pre p-2 font-og-mono text-og-sm text-og-fg", children: fileView.content }),
|
|
6480
|
-
className: "p-1"
|
|
6481
|
-
}
|
|
6482
|
-
) : /* @__PURE__ */ jsx10("pre", { className: "overflow-auto whitespace-pre p-2 font-og-mono text-og-sm text-og-fg", children: fileView.content })
|
|
6483
|
-
] }) : /* @__PURE__ */ jsxs8(Notice, { announce: "status", children: [
|
|
6484
|
-
"Loading ",
|
|
6485
|
-
viewPath,
|
|
6486
|
-
"\u2026"
|
|
6487
|
-
] }) : (
|
|
6488
|
-
// Nothing selected — the tree shows the whole workspace; pick a file.
|
|
6489
|
-
requestedPath && !requestedPathReady ? /* @__PURE__ */ jsxs8(
|
|
6490
|
-
Notice,
|
|
6491
|
-
{
|
|
6492
|
-
icon: /* @__PURE__ */ jsx10(
|
|
6493
|
-
LoaderCircleIcon,
|
|
7123
|
+
": ",
|
|
7124
|
+
fileView.error.message
|
|
7125
|
+
] }) : fileView.loading ? /* @__PURE__ */ jsxs8(Notice, { announce: "status", children: [
|
|
7126
|
+
"Loading ",
|
|
7127
|
+
viewPath,
|
|
7128
|
+
"\u2026"
|
|
7129
|
+
] }) : fileView.isBinary ? /* @__PURE__ */ jsxs8(Notice, { children: [
|
|
7130
|
+
viewPath,
|
|
7131
|
+
" is a binary file (",
|
|
7132
|
+
fileView.sizeBytes ?? 0,
|
|
7133
|
+
" bytes)."
|
|
7134
|
+
] }) : fileView.content !== null ? /* @__PURE__ */ jsxs8(Fragment3, { children: [
|
|
7135
|
+
fileView.truncated && /* @__PURE__ */ jsxs8("div", { className: "border-b border-og-border bg-og-surface-1 px-2 py-1 text-og-xs text-og-status-running", children: [
|
|
7136
|
+
"Large file \u2014 showing a truncated preview (",
|
|
7137
|
+
fileView.sizeBytes ?? 0,
|
|
7138
|
+
" bytes loaded). Editing is disabled to avoid corrupting the file."
|
|
7139
|
+
] }),
|
|
7140
|
+
usePierre ? /* @__PURE__ */ jsx10(
|
|
7141
|
+
PierreFile,
|
|
6494
7142
|
{
|
|
6495
|
-
|
|
6496
|
-
|
|
7143
|
+
path: viewPath,
|
|
7144
|
+
contents: fileView.content,
|
|
7145
|
+
themeType: resolvedTheme,
|
|
7146
|
+
fallback: /* @__PURE__ */ jsx10("pre", { className: "overflow-auto whitespace-pre p-2 font-og-mono text-og-sm text-og-fg", children: fileView.content }),
|
|
7147
|
+
className: "p-1"
|
|
6497
7148
|
}
|
|
6498
|
-
),
|
|
6499
|
-
|
|
6500
|
-
|
|
6501
|
-
|
|
6502
|
-
|
|
6503
|
-
|
|
6504
|
-
|
|
6505
|
-
|
|
6506
|
-
|
|
6507
|
-
|
|
6508
|
-
|
|
6509
|
-
|
|
6510
|
-
|
|
6511
|
-
|
|
6512
|
-
|
|
7149
|
+
) : /* @__PURE__ */ jsx10("pre", { className: "overflow-auto whitespace-pre p-2 font-og-mono text-og-sm text-og-fg", children: fileView.content })
|
|
7150
|
+
] }) : /* @__PURE__ */ jsxs8(Notice, { announce: "status", children: [
|
|
7151
|
+
"Loading ",
|
|
7152
|
+
viewPath,
|
|
7153
|
+
"\u2026"
|
|
7154
|
+
] }) : (
|
|
7155
|
+
// Nothing selected — the tree shows the whole workspace; pick a file.
|
|
7156
|
+
requestedPath && !requestedPathReady ? /* @__PURE__ */ jsxs8(
|
|
7157
|
+
Notice,
|
|
7158
|
+
{
|
|
7159
|
+
icon: /* @__PURE__ */ jsx10(
|
|
7160
|
+
LoaderCircleIcon,
|
|
7161
|
+
{
|
|
7162
|
+
className: "size-5 animate-spin motion-reduce:animate-none",
|
|
7163
|
+
"aria-hidden": true
|
|
7164
|
+
}
|
|
7165
|
+
),
|
|
7166
|
+
title: "Waking sandbox",
|
|
7167
|
+
announce: "status",
|
|
7168
|
+
children: [
|
|
7169
|
+
"Opening ",
|
|
7170
|
+
requestedPath,
|
|
7171
|
+
" when the live workspace is ready\u2026"
|
|
7172
|
+
]
|
|
7173
|
+
}
|
|
7174
|
+
) : /* @__PURE__ */ jsx10(Notice, { icon: /* @__PURE__ */ jsx10(FileCode2Icon, { className: "size-5", "aria-hidden": true }), title: "Choose a file", children: "Select a file in the tree to preview it." })
|
|
7175
|
+
) })
|
|
7176
|
+
]
|
|
7177
|
+
}
|
|
7178
|
+
)
|
|
7179
|
+
]
|
|
7180
|
+
}
|
|
7181
|
+
)
|
|
6513
7182
|
] });
|
|
6514
7183
|
}
|
|
6515
7184
|
function WakeButton({ children, onClick }) {
|
|
@@ -6518,13 +7187,33 @@ function WakeButton({ children, onClick }) {
|
|
|
6518
7187
|
{
|
|
6519
7188
|
type: "button",
|
|
6520
7189
|
onClick,
|
|
6521
|
-
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-
|
|
7190
|
+
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",
|
|
6522
7191
|
children
|
|
6523
7192
|
}
|
|
6524
7193
|
);
|
|
6525
7194
|
}
|
|
6526
|
-
function GitHeader({ git,
|
|
6527
|
-
|
|
7195
|
+
function GitHeader({ git, loading }) {
|
|
7196
|
+
if (loading) {
|
|
7197
|
+
return /* @__PURE__ */ jsxs8(
|
|
7198
|
+
"div",
|
|
7199
|
+
{
|
|
7200
|
+
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",
|
|
7201
|
+
role: "status",
|
|
7202
|
+
"aria-live": "polite",
|
|
7203
|
+
children: [
|
|
7204
|
+
/* @__PURE__ */ jsx10(
|
|
7205
|
+
LoaderCircleIcon,
|
|
7206
|
+
{
|
|
7207
|
+
className: "size-3.5 shrink-0 animate-spin motion-reduce:animate-none",
|
|
7208
|
+
"aria-hidden": true
|
|
7209
|
+
}
|
|
7210
|
+
),
|
|
7211
|
+
/* @__PURE__ */ jsx10("span", { children: "Loading repository\u2026" })
|
|
7212
|
+
]
|
|
7213
|
+
}
|
|
7214
|
+
);
|
|
7215
|
+
}
|
|
7216
|
+
const dirty = git.dirtyCount > 0;
|
|
6528
7217
|
return /* @__PURE__ */ jsxs8("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", children: [
|
|
6529
7218
|
/* @__PURE__ */ jsx10(
|
|
6530
7219
|
"span",
|
|
@@ -6536,7 +7225,7 @@ function GitHeader({ git, dirtyCount }) {
|
|
|
6536
7225
|
)
|
|
6537
7226
|
}
|
|
6538
7227
|
),
|
|
6539
|
-
/* @__PURE__ */ jsx10("span", { className: "sr-only", children: dirty ? `
|
|
7228
|
+
/* @__PURE__ */ jsx10("span", { className: "sr-only", children: dirty ? `Uncommitted changes in ${git.dirtyCount} files` : "No uncommitted changes" }),
|
|
6540
7229
|
/* @__PURE__ */ jsx10("span", { className: "truncate font-og-mono text-og-fg", children: git.repoCount > 1 ? `${git.repoCount} repositories` : git.branch ?? (git.isRepo ? "(detached)" : "no repo") }),
|
|
6541
7230
|
(git.ahead > 0 || git.behind > 0) && /* @__PURE__ */ jsxs8("span", { className: "flex shrink-0 items-center gap-1.5 text-og-xs text-og-fg-subtle", children: [
|
|
6542
7231
|
git.ahead > 0 && /* @__PURE__ */ jsxs8("span", { children: [
|
|
@@ -6555,7 +7244,7 @@ function GitHeader({ git, dirtyCount }) {
|
|
|
6555
7244
|
"data-contrast-audited": true,
|
|
6556
7245
|
className: "ml-auto shrink-0 text-og-xs text-og-fg-subtle",
|
|
6557
7246
|
children: [
|
|
6558
|
-
dirtyCount,
|
|
7247
|
+
git.dirtyCount,
|
|
6559
7248
|
" changed"
|
|
6560
7249
|
]
|
|
6561
7250
|
}
|
|
@@ -6573,7 +7262,7 @@ function Segmented({
|
|
|
6573
7262
|
type: "button",
|
|
6574
7263
|
onClick: () => onChange(opt.value),
|
|
6575
7264
|
className: cn(
|
|
6576
|
-
"min-h-7 rounded-og-xs px-1.5 py-0.5 text-og-xs focus-visible:outline-
|
|
7265
|
+
"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",
|
|
6577
7266
|
opt.value === value ? "bg-og-accent-soft text-og-fg" : "text-og-fg-subtle hover:text-og-fg"
|
|
6578
7267
|
),
|
|
6579
7268
|
children: opt.label
|
|
@@ -6582,6 +7271,7 @@ function Segmented({
|
|
|
6582
7271
|
)) });
|
|
6583
7272
|
}
|
|
6584
7273
|
function useFileView(path, readFile, reloadRevision = 0) {
|
|
7274
|
+
const previousPathRef = useRef17(null);
|
|
6585
7275
|
const [state, setState] = useState17({
|
|
6586
7276
|
content: null,
|
|
6587
7277
|
isBinary: false,
|
|
@@ -6592,6 +7282,7 @@ function useFileView(path, readFile, reloadRevision = 0) {
|
|
|
6592
7282
|
});
|
|
6593
7283
|
useEffect20(() => {
|
|
6594
7284
|
if (!path) {
|
|
7285
|
+
previousPathRef.current = null;
|
|
6595
7286
|
setState({
|
|
6596
7287
|
content: null,
|
|
6597
7288
|
isBinary: false,
|
|
@@ -6602,16 +7293,22 @@ function useFileView(path, readFile, reloadRevision = 0) {
|
|
|
6602
7293
|
});
|
|
6603
7294
|
return;
|
|
6604
7295
|
}
|
|
7296
|
+
const pathChanged = previousPathRef.current !== path;
|
|
7297
|
+
previousPathRef.current = path;
|
|
6605
7298
|
let cancelled = false;
|
|
6606
7299
|
const abort = new AbortController();
|
|
6607
|
-
|
|
6608
|
-
|
|
6609
|
-
|
|
6610
|
-
|
|
6611
|
-
|
|
6612
|
-
|
|
6613
|
-
|
|
6614
|
-
|
|
7300
|
+
if (pathChanged) {
|
|
7301
|
+
setState({
|
|
7302
|
+
content: null,
|
|
7303
|
+
isBinary: false,
|
|
7304
|
+
truncated: false,
|
|
7305
|
+
sizeBytes: null,
|
|
7306
|
+
loading: true,
|
|
7307
|
+
error: null
|
|
7308
|
+
});
|
|
7309
|
+
} else {
|
|
7310
|
+
setState((previous) => ({ ...previous, error: null }));
|
|
7311
|
+
}
|
|
6615
7312
|
void readFile(path, { signal: abort.signal }).then((res) => {
|
|
6616
7313
|
if (cancelled) return;
|
|
6617
7314
|
const content = res.isBinary ? null : res.encoding === "base64" ? decodeBase64Utf8(res.content) : res.content;
|
|
@@ -6625,14 +7322,17 @@ function useFileView(path, readFile, reloadRevision = 0) {
|
|
|
6625
7322
|
});
|
|
6626
7323
|
}).catch((cause) => {
|
|
6627
7324
|
if (cancelled) return;
|
|
6628
|
-
|
|
6629
|
-
|
|
6630
|
-
|
|
6631
|
-
|
|
6632
|
-
|
|
6633
|
-
|
|
6634
|
-
|
|
6635
|
-
|
|
7325
|
+
const error = cause instanceof Error ? cause : new Error(String(cause));
|
|
7326
|
+
setState(
|
|
7327
|
+
(previous) => pathChanged ? {
|
|
7328
|
+
content: null,
|
|
7329
|
+
isBinary: false,
|
|
7330
|
+
truncated: false,
|
|
7331
|
+
sizeBytes: null,
|
|
7332
|
+
loading: false,
|
|
7333
|
+
error
|
|
7334
|
+
} : { ...previous, loading: false, error }
|
|
7335
|
+
);
|
|
6636
7336
|
});
|
|
6637
7337
|
return () => {
|
|
6638
7338
|
cancelled = true;
|
|
@@ -6703,7 +7403,7 @@ function DesktopViewer({
|
|
|
6703
7403
|
scaleViewport,
|
|
6704
7404
|
rfbFactory,
|
|
6705
7405
|
webSocketFactory,
|
|
6706
|
-
|
|
7406
|
+
onActivate,
|
|
6707
7407
|
onAcknowledge,
|
|
6708
7408
|
watching,
|
|
6709
7409
|
onWarm,
|
|
@@ -6715,22 +7415,55 @@ function DesktopViewer({
|
|
|
6715
7415
|
className
|
|
6716
7416
|
}) {
|
|
6717
7417
|
const containerRef = useRef18(null);
|
|
6718
|
-
const
|
|
7418
|
+
const onActivateRef = useRef18(onActivate);
|
|
7419
|
+
const onAcknowledgeRef = useRef18(onAcknowledge);
|
|
7420
|
+
onActivateRef.current = onActivate;
|
|
7421
|
+
onAcknowledgeRef.current = onAcknowledge;
|
|
7422
|
+
const acknowledgmentAttemptedRef = useRef18(false);
|
|
7423
|
+
const [acknowledgmentError, setAcknowledgmentError] = useState18(null);
|
|
7424
|
+
const [acknowledgmentRetryNonce, setAcknowledgmentRetryNonce] = useState18(0);
|
|
6719
7425
|
const [takeControl, setTakeControl] = useState18(interactive ?? false);
|
|
6720
7426
|
const externallyControlled = interactive !== void 0;
|
|
6721
7427
|
const serverAllowsControl = capability?.mode !== "read-only";
|
|
6722
7428
|
const wantControl = externallyControlled ? interactive : takeControl;
|
|
6723
7429
|
const inControl = Boolean(wantControl) && serverAllowsControl;
|
|
6724
|
-
const isWatching = watching ??
|
|
7430
|
+
const isWatching = watching ?? true;
|
|
6725
7431
|
const transportNull = !capability || capability.transport === null;
|
|
6726
7432
|
const reason = capability?.reason ?? null;
|
|
6727
|
-
const
|
|
7433
|
+
const needsAcknowledgment = capability?.requiresAcknowledgment === true && capability.acknowledged !== true;
|
|
6728
7434
|
const noLiveAddress = Boolean(capability) && !transportNull && !capability.url;
|
|
6729
7435
|
const coldWarmable = transportNull && reason === "lease_cold" || !transportNull && noLiveAddress;
|
|
6730
7436
|
const hardUnavailable = transportNull && isHardUnavailable(reason);
|
|
6731
|
-
|
|
6732
|
-
|
|
6733
|
-
|
|
7437
|
+
useEffect21(() => {
|
|
7438
|
+
onActivateRef.current?.();
|
|
7439
|
+
}, []);
|
|
7440
|
+
useEffect21(() => {
|
|
7441
|
+
if (!needsAcknowledgment) {
|
|
7442
|
+
acknowledgmentAttemptedRef.current = false;
|
|
7443
|
+
setAcknowledgmentError(null);
|
|
7444
|
+
return;
|
|
7445
|
+
}
|
|
7446
|
+
if (acknowledgmentAttemptedRef.current) return;
|
|
7447
|
+
acknowledgmentAttemptedRef.current = true;
|
|
7448
|
+
setAcknowledgmentError(null);
|
|
7449
|
+
const acknowledge = onAcknowledgeRef.current;
|
|
7450
|
+
if (!acknowledge) {
|
|
7451
|
+
setAcknowledgmentError(new Error("Desktop acknowledgment is not configured."));
|
|
7452
|
+
return;
|
|
7453
|
+
}
|
|
7454
|
+
let active = true;
|
|
7455
|
+
void Promise.resolve().then(() => acknowledge()).catch((cause) => {
|
|
7456
|
+
if (!active) return;
|
|
7457
|
+
setAcknowledgmentError(cause instanceof Error ? cause : new Error(String(cause)));
|
|
7458
|
+
});
|
|
7459
|
+
return () => {
|
|
7460
|
+
active = false;
|
|
7461
|
+
};
|
|
7462
|
+
}, [needsAcknowledgment, acknowledgmentRetryNonce]);
|
|
7463
|
+
const retryAcknowledgment = () => {
|
|
7464
|
+
acknowledgmentAttemptedRef.current = false;
|
|
7465
|
+
setAcknowledgmentError(null);
|
|
7466
|
+
setAcknowledgmentRetryNonce((nonce) => nonce + 1);
|
|
6734
7467
|
};
|
|
6735
7468
|
useEffect21(() => {
|
|
6736
7469
|
if (!inControl || externallyControlled) return;
|
|
@@ -6745,7 +7478,7 @@ function DesktopViewer({
|
|
|
6745
7478
|
window.removeEventListener("keydown", onKey);
|
|
6746
7479
|
};
|
|
6747
7480
|
}, [inControl, externallyControlled]);
|
|
6748
|
-
const connectCapability = !transportNull && !
|
|
7481
|
+
const connectCapability = !transportNull && !needsAcknowledgment && !viewerCapReached ? capability : null;
|
|
6749
7482
|
const stream = useDesktopStream({
|
|
6750
7483
|
capability: connectCapability,
|
|
6751
7484
|
containerRef,
|
|
@@ -6760,17 +7493,16 @@ function DesktopViewer({
|
|
|
6760
7493
|
streamStateRef.current = stream.state;
|
|
6761
7494
|
const warmKeyRef = useRef18(null);
|
|
6762
7495
|
useEffect21(() => {
|
|
6763
|
-
if (!isWatching || !coldWarmable ||
|
|
7496
|
+
if (!isWatching || !coldWarmable || needsAcknowledgment || viewerCapReached) {
|
|
6764
7497
|
if (!coldWarmable) warmKeyRef.current = null;
|
|
6765
7498
|
return;
|
|
6766
7499
|
}
|
|
6767
|
-
|
|
6768
|
-
if (!warm) return;
|
|
7500
|
+
if (!onWarm) return;
|
|
6769
7501
|
const key = `${capability?.reason ?? ""}:${capability?.url ?? ""}:${capability?.expiresAt ?? ""}`;
|
|
6770
7502
|
if (warmKeyRef.current === key) return;
|
|
6771
7503
|
warmKeyRef.current = key;
|
|
6772
|
-
|
|
6773
|
-
}, [isWatching, coldWarmable,
|
|
7504
|
+
onWarm();
|
|
7505
|
+
}, [isWatching, coldWarmable, needsAcknowledgment, viewerCapReached, capability, onWarm]);
|
|
6774
7506
|
useEffect21(() => {
|
|
6775
7507
|
if (!hasLiveUrl || typeof document === "undefined") return;
|
|
6776
7508
|
const maybeReattach = () => {
|
|
@@ -6803,13 +7535,14 @@ function DesktopViewer({
|
|
|
6803
7535
|
let uiState;
|
|
6804
7536
|
if (viewerCapReached) uiState = "viewer_cap";
|
|
6805
7537
|
else if (hardUnavailable) uiState = "unavailable";
|
|
6806
|
-
else if (
|
|
7538
|
+
else if (acknowledgmentError) uiState = "acknowledgment_error";
|
|
7539
|
+
else if (needsAcknowledgment) uiState = "opening";
|
|
6807
7540
|
else if (coldWarmable) uiState = "warming";
|
|
6808
7541
|
else if (connected) uiState = "connected";
|
|
6809
7542
|
else if (stream.error) uiState = "error";
|
|
6810
7543
|
else if (connectTimedOut) uiState = "connect_failed";
|
|
6811
7544
|
else uiState = "connecting";
|
|
6812
|
-
const overlayShown = uiState !== "connected" && uiState !== "connecting";
|
|
7545
|
+
const overlayShown = uiState !== "connected" && uiState !== "connecting" && uiState !== "opening";
|
|
6813
7546
|
const showToggle = showControlToggle && !overlayShown;
|
|
6814
7547
|
let overlay = null;
|
|
6815
7548
|
switch (uiState) {
|
|
@@ -6822,8 +7555,12 @@ function DesktopViewer({
|
|
|
6822
7555
|
case "unavailable":
|
|
6823
7556
|
overlay = renderUnavailable?.(reason) ?? defaultNotice("Desktop unavailable", unavailableCopy(reason));
|
|
6824
7557
|
break;
|
|
6825
|
-
case "
|
|
6826
|
-
overlay =
|
|
7558
|
+
case "acknowledgment_error":
|
|
7559
|
+
overlay = defaultNotice(
|
|
7560
|
+
"Couldn\u2019t open desktop",
|
|
7561
|
+
acknowledgmentError?.message ?? "The desktop acknowledgment failed.",
|
|
7562
|
+
retryAcknowledgment
|
|
7563
|
+
);
|
|
6827
7564
|
break;
|
|
6828
7565
|
case "warming":
|
|
6829
7566
|
overlay = renderWarming?.() ?? /* @__PURE__ */ jsx11(WarmingNotice, {});
|
|
@@ -6842,6 +7579,7 @@ function DesktopViewer({
|
|
|
6842
7579
|
reconnect
|
|
6843
7580
|
);
|
|
6844
7581
|
break;
|
|
7582
|
+
case "opening":
|
|
6845
7583
|
case "connecting":
|
|
6846
7584
|
case "connected":
|
|
6847
7585
|
overlay = null;
|
|
@@ -6870,7 +7608,7 @@ function DesktopViewer({
|
|
|
6870
7608
|
"data-state": stream.state
|
|
6871
7609
|
}
|
|
6872
7610
|
),
|
|
6873
|
-
uiState === "connecting" && /* @__PURE__ */ jsxs9("div", { className: "pointer-events-none absolute inset-0 flex flex-col items-center justify-center gap-3 text-og-fg-subtle", children: [
|
|
7611
|
+
(uiState === "opening" || uiState === "connecting") && /* @__PURE__ */ jsxs9("div", { className: "pointer-events-none absolute inset-0 flex flex-col items-center justify-center gap-3 text-og-fg-subtle", children: [
|
|
6874
7612
|
/* @__PURE__ */ jsxs9("span", { className: "relative flex items-center justify-center", children: [
|
|
6875
7613
|
/* @__PURE__ */ jsx11(MonitorIcon, { className: "size-8 opacity-30", strokeWidth: 1.5 }),
|
|
6876
7614
|
/* @__PURE__ */ jsx11(
|
|
@@ -6881,7 +7619,7 @@ function DesktopViewer({
|
|
|
6881
7619
|
}
|
|
6882
7620
|
)
|
|
6883
7621
|
] }),
|
|
6884
|
-
/* @__PURE__ */ jsx11("span", { className: "text-og-sm", children: "Connecting to the desktop\u2026" })
|
|
7622
|
+
/* @__PURE__ */ jsx11("span", { className: "text-og-sm", children: uiState === "opening" ? "Opening the desktop\u2026" : "Connecting to the desktop\u2026" })
|
|
6885
7623
|
] }),
|
|
6886
7624
|
showToggle && !externallyControlled && inControl && /* @__PURE__ */ jsx11(
|
|
6887
7625
|
InControlBar,
|
|
@@ -6924,7 +7662,7 @@ function TakeControlCallToAction({
|
|
|
6924
7662
|
"border-og-border",
|
|
6925
7663
|
"bg-og-bg/85 backdrop-blur-md",
|
|
6926
7664
|
"shadow-og-lg",
|
|
6927
|
-
"outline-
|
|
7665
|
+
"outline-hidden transition-all duration-150 ease-out",
|
|
6928
7666
|
"focus-visible:ring-2 focus-visible:ring-og-accent focus-visible:ring-offset-2 focus-visible:ring-offset-og-bg",
|
|
6929
7667
|
disabled ? "cursor-not-allowed opacity-60" : cn(
|
|
6930
7668
|
"cursor-pointer opacity-90 hover:-translate-y-0.5 hover:opacity-100",
|
|
@@ -6972,7 +7710,7 @@ function InControlBar({ shared, onRelease }) {
|
|
|
6972
7710
|
className: cn(
|
|
6973
7711
|
"inline-flex items-center gap-1.5 rounded-og-sm border px-2.5 py-1 text-og-xs font-semibold shadow-og-md transition-colors",
|
|
6974
7712
|
"border-og-accent bg-og-bg/90 text-og-fg backdrop-blur-sm",
|
|
6975
|
-
"outline-
|
|
7713
|
+
"outline-hidden hover:bg-og-accent hover:text-og-accent-fg focus-visible:ring-2 focus-visible:ring-og-accent"
|
|
6976
7714
|
),
|
|
6977
7715
|
children: [
|
|
6978
7716
|
/* @__PURE__ */ jsx11(MonitorIcon, { className: "size-3.5", strokeWidth: 2, "aria-hidden": true }),
|
|
@@ -7019,26 +7757,6 @@ function WarmingNotice({ onRetry }) {
|
|
|
7019
7757
|
)
|
|
7020
7758
|
] });
|
|
7021
7759
|
}
|
|
7022
|
-
function DefaultConsentGate({ shared, onAccept }) {
|
|
7023
|
-
return /* @__PURE__ */ jsxs9("div", { className: "max-w-sm rounded-og-lg border border-og-border bg-og-bg p-4 text-center text-og-base text-og-fg", children: [
|
|
7024
|
-
/* @__PURE__ */ jsx11("div", { className: "mb-1 font-medium", children: "Watch the live desktop?" }),
|
|
7025
|
-
/* @__PURE__ */ jsxs9("p", { className: "mb-3 text-og-sm text-og-fg-subtle", children: [
|
|
7026
|
-
"The desktop pixel stream is ",
|
|
7027
|
-
/* @__PURE__ */ jsx11("strong", { children: "un-redacted" }),
|
|
7028
|
-
" \u2014 it can show secrets the agent prints on screen.",
|
|
7029
|
-
shared ? " This sandbox is shared: you'll also see other sessions' agents on the same screen." : ""
|
|
7030
|
-
] }),
|
|
7031
|
-
/* @__PURE__ */ jsx11(
|
|
7032
|
-
"button",
|
|
7033
|
-
{
|
|
7034
|
-
type: "button",
|
|
7035
|
-
onClick: onAccept,
|
|
7036
|
-
className: "rounded-og-sm bg-og-accent px-3 py-1.5 text-og-sm font-medium text-og-accent-fg pointer-coarse:min-h-11",
|
|
7037
|
-
children: "I understand \u2014 show the desktop"
|
|
7038
|
-
}
|
|
7039
|
-
)
|
|
7040
|
-
] });
|
|
7041
|
-
}
|
|
7042
7760
|
function defaultNotice(title, body, onRetry) {
|
|
7043
7761
|
return /* @__PURE__ */ jsxs9("div", { className: "max-w-sm rounded-og-lg border border-og-border bg-og-bg p-4 text-center text-og-base text-og-fg", children: [
|
|
7044
7762
|
/* @__PURE__ */ jsxs9("div", { className: "mb-1 flex items-center justify-center gap-1.5 font-medium", children: [
|
|
@@ -7075,9 +7793,9 @@ import {
|
|
|
7075
7793
|
XIcon as XIcon2
|
|
7076
7794
|
} from "lucide-react";
|
|
7077
7795
|
import {
|
|
7078
|
-
Group,
|
|
7079
|
-
Panel,
|
|
7080
|
-
Separator,
|
|
7796
|
+
Group as Group2,
|
|
7797
|
+
Panel as Panel2,
|
|
7798
|
+
Separator as Separator2,
|
|
7081
7799
|
useDefaultLayout,
|
|
7082
7800
|
usePanelRef
|
|
7083
7801
|
} from "react-resizable-panels";
|
|
@@ -7122,6 +7840,7 @@ function WorkspaceDock({
|
|
|
7122
7840
|
onActiveTabChange,
|
|
7123
7841
|
collapsed: collapsedProp,
|
|
7124
7842
|
onCollapsedChange,
|
|
7843
|
+
showCollapseControl = false,
|
|
7125
7844
|
headerAccessory,
|
|
7126
7845
|
mobileLeadingControl,
|
|
7127
7846
|
autoSaveId = "og.session.dock",
|
|
@@ -7298,6 +8017,11 @@ function WorkspaceDock({
|
|
|
7298
8017
|
className: "fixed inset-0 z-40 flex flex-col bg-og-bg",
|
|
7299
8018
|
hidden: collapsed,
|
|
7300
8019
|
style: {
|
|
8020
|
+
// Author utility classes can override the user-agent `[hidden]`
|
|
8021
|
+
// rule (for example this surface's `flex` class). Keep an inline
|
|
8022
|
+
// display guard so a collapsed mobile workspace is actually gone
|
|
8023
|
+
// visually as well as from the accessibility tree.
|
|
8024
|
+
display: collapsed ? "none" : void 0,
|
|
7301
8025
|
paddingTop: "env(safe-area-inset-top)",
|
|
7302
8026
|
paddingBottom: "env(safe-area-inset-bottom)"
|
|
7303
8027
|
},
|
|
@@ -7322,7 +8046,7 @@ function WorkspaceDock({
|
|
|
7322
8046
|
onClick: expand,
|
|
7323
8047
|
title: "Open workspace",
|
|
7324
8048
|
"aria-label": "Open workspace",
|
|
7325
|
-
className: "absolute right-3 top-3 z-30 inline-flex size-11 items-center justify-center rounded-og-md border border-og-border bg-og-surface-1 text-og-fg-muted shadow-lg transition-colors hover:border-og-border-strong hover:text-og-fg focus-visible:outline-
|
|
8049
|
+
className: "absolute right-3 top-3 z-30 inline-flex size-11 items-center justify-center rounded-og-md border border-og-border bg-og-surface-1 text-og-fg-muted shadow-lg transition-colors hover:border-og-border-strong hover:text-og-fg focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-og-accent",
|
|
7326
8050
|
style: { marginTop: "env(safe-area-inset-top)" },
|
|
7327
8051
|
children: /* @__PURE__ */ jsx12(ChevronsLeftRightIcon, { className: "size-4", "aria-hidden": true })
|
|
7328
8052
|
}
|
|
@@ -7346,20 +8070,20 @@ function WorkspaceDock({
|
|
|
7346
8070
|
children: maximized ? /* @__PURE__ */ jsx12(Minimize2Icon, { className: "size-3.5" }) : /* @__PURE__ */ jsx12(Maximize2Icon, { className: "size-3.5" })
|
|
7347
8071
|
}
|
|
7348
8072
|
),
|
|
7349
|
-
hostControlled ? null : /* @__PURE__ */ jsx12(ChromeButton, { onClick: collapse, title: "Collapse", label: "Collapse dock", children: /* @__PURE__ */ jsx12(PanelRightCloseIcon, { className: "size-3.5" }) })
|
|
8073
|
+
hostControlled && !showCollapseControl ? null : /* @__PURE__ */ jsx12(ChromeButton, { onClick: collapse, title: "Collapse", label: "Collapse dock", children: /* @__PURE__ */ jsx12(PanelRightCloseIcon, { className: "size-3.5" }) })
|
|
7350
8074
|
] })
|
|
7351
8075
|
}
|
|
7352
8076
|
);
|
|
7353
8077
|
return /* @__PURE__ */ jsxs10("div", { ref: rootRef, className: cn("relative flex h-full min-h-0 w-full min-w-0", className), children: [
|
|
7354
8078
|
/* @__PURE__ */ jsxs10(
|
|
7355
|
-
|
|
8079
|
+
Group2,
|
|
7356
8080
|
{
|
|
7357
8081
|
orientation: "horizontal",
|
|
7358
8082
|
className: "min-h-0 flex-1",
|
|
7359
8083
|
...defaultLayout ? { defaultLayout } : {},
|
|
7360
8084
|
onLayoutChanged,
|
|
7361
8085
|
children: [
|
|
7362
|
-
/* @__PURE__ */ jsx12(
|
|
8086
|
+
/* @__PURE__ */ jsx12(Panel2, { id: "primary", minSize: "30%", className: "min-h-0 min-w-0", children: /* @__PURE__ */ jsx12(
|
|
7363
8087
|
"div",
|
|
7364
8088
|
{
|
|
7365
8089
|
"data-workspace-primary": true,
|
|
@@ -7369,9 +8093,9 @@ function WorkspaceDock({
|
|
|
7369
8093
|
children: primary
|
|
7370
8094
|
}
|
|
7371
8095
|
) }),
|
|
7372
|
-
!collapsed && /* @__PURE__ */ jsx12(
|
|
8096
|
+
!collapsed && /* @__PURE__ */ jsx12(Separator2, { className: "group relative z-10 w-1.5 shrink-0 outline-hidden focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-og-accent max-[1023px]:-mx-[19px] max-[1023px]:w-11 pointer-coarse:-mx-[19px] pointer-coarse:w-11", children: /* @__PURE__ */ jsx12("span", { className: "absolute inset-y-0 left-1/2 w-px -translate-x-1/2 bg-og-border transition-colors group-hover:bg-og-accent group-focus-visible:bg-og-accent group-data-[separator-state=dragging]:bg-og-accent" }) }),
|
|
7373
8097
|
/* @__PURE__ */ jsx12(
|
|
7374
|
-
|
|
8098
|
+
Panel2,
|
|
7375
8099
|
{
|
|
7376
8100
|
id: "dock",
|
|
7377
8101
|
panelRef: dockPanelRef,
|
|
@@ -7435,7 +8159,7 @@ function ChromeButton({
|
|
|
7435
8159
|
onClick,
|
|
7436
8160
|
title,
|
|
7437
8161
|
"aria-label": label,
|
|
7438
|
-
className: "inline-flex size-7 items-center justify-center rounded-og-sm p-1 transition-colors hover:bg-og-surface-2 hover:text-og-fg focus-visible:outline-
|
|
8162
|
+
className: "inline-flex size-7 items-center justify-center rounded-og-sm p-1 transition-colors hover:bg-og-surface-2 hover:text-og-fg focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-og-accent max-[1023px]:size-11 pointer-coarse:size-11",
|
|
7439
8163
|
children
|
|
7440
8164
|
}
|
|
7441
8165
|
);
|
|
@@ -7547,7 +8271,7 @@ function DockChrome({
|
|
|
7547
8271
|
onClick: () => onTab(tab.id),
|
|
7548
8272
|
onKeyDown: (event) => onTabKeyDown(event, index),
|
|
7549
8273
|
className: cn(
|
|
7550
|
-
"flex min-h-7 shrink-0 items-center justify-center gap-1 rounded-og-sm px-2 py-1 text-og-xs font-medium transition-colors focus-visible:outline-
|
|
8274
|
+
"flex min-h-7 shrink-0 items-center justify-center gap-1 rounded-og-sm px-2 py-1 text-og-xs font-medium transition-colors 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",
|
|
7551
8275
|
tab.id === current ? "bg-og-accent-soft text-og-fg" : "text-og-fg-subtle hover:text-og-fg"
|
|
7552
8276
|
),
|
|
7553
8277
|
children: [
|
|
@@ -7582,25 +8306,46 @@ function DockChrome({
|
|
|
7582
8306
|
}
|
|
7583
8307
|
|
|
7584
8308
|
// src/components/sandbox-workspace.tsx
|
|
7585
|
-
import {
|
|
7586
|
-
|
|
8309
|
+
import {
|
|
8310
|
+
lazy as lazy2,
|
|
8311
|
+
Suspense as Suspense2,
|
|
8312
|
+
useCallback as useCallback22,
|
|
8313
|
+
useEffect as useEffect23,
|
|
8314
|
+
useMemo as useMemo13,
|
|
8315
|
+
useRef as useRef20,
|
|
8316
|
+
useState as useState20
|
|
8317
|
+
} from "react";
|
|
7587
8318
|
import {
|
|
7588
8319
|
CircleCheckIcon,
|
|
7589
8320
|
CpuIcon,
|
|
7590
|
-
|
|
8321
|
+
FileCode2Icon as FileCode2Icon2,
|
|
8322
|
+
GitCompareArrowsIcon,
|
|
8323
|
+
Globe2Icon,
|
|
7591
8324
|
LoaderCircleIcon as LoaderCircleIcon3,
|
|
8325
|
+
MonitorIcon as MonitorIcon2,
|
|
7592
8326
|
RefreshCwIcon as RefreshCwIcon2,
|
|
8327
|
+
SquareTerminalIcon,
|
|
7593
8328
|
TriangleAlertIcon as TriangleAlertIcon2
|
|
7594
8329
|
} from "lucide-react";
|
|
7595
8330
|
import { jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
8331
|
+
var LazyBrowserViewer = lazy2(async () => {
|
|
8332
|
+
const viewers = await import("./interactive-workbench-viewers-KHNOHWQE.js");
|
|
8333
|
+
return { default: viewers.BrowserViewer };
|
|
8334
|
+
});
|
|
8335
|
+
var LazyComputerViewer = lazy2(async () => {
|
|
8336
|
+
const viewers = await import("./interactive-workbench-viewers-KHNOHWQE.js");
|
|
8337
|
+
return { default: viewers.ComputerViewer };
|
|
8338
|
+
});
|
|
7596
8339
|
var WORKBENCH_TAB_CHANGES = "changes";
|
|
7597
8340
|
var WORKBENCH_TAB_FILES = "files";
|
|
7598
8341
|
var WORKBENCH_TAB_TERMINAL = "terminal";
|
|
8342
|
+
var WORKBENCH_TAB_BROWSER = "browser";
|
|
7599
8343
|
var WORKBENCH_TAB_DESKTOP = "desktop";
|
|
7600
8344
|
var WORKBENCH_SURFACES = [
|
|
7601
8345
|
WORKBENCH_TAB_CHANGES,
|
|
7602
8346
|
WORKBENCH_TAB_FILES,
|
|
7603
8347
|
WORKBENCH_TAB_TERMINAL,
|
|
8348
|
+
WORKBENCH_TAB_BROWSER,
|
|
7604
8349
|
WORKBENCH_TAB_DESKTOP
|
|
7605
8350
|
];
|
|
7606
8351
|
function isWorkbenchSurface(value) {
|
|
@@ -7624,6 +8369,24 @@ function captureDegradedMessage(reason) {
|
|
|
7624
8369
|
return "Workspace capture is incomplete because repository discovery failed. Live files remain authoritative.";
|
|
7625
8370
|
}
|
|
7626
8371
|
}
|
|
8372
|
+
function WorkbenchTabLabel({ icon, children }) {
|
|
8373
|
+
return /* @__PURE__ */ jsxs11("span", { className: "inline-flex items-center gap-1.5", children: [
|
|
8374
|
+
/* @__PURE__ */ jsx13("span", { className: "[&>svg]:size-3.5", "aria-hidden": true, children: icon }),
|
|
8375
|
+
/* @__PURE__ */ jsx13("span", { children })
|
|
8376
|
+
] });
|
|
8377
|
+
}
|
|
8378
|
+
function WorkbenchSurfaceLoading({ name }) {
|
|
8379
|
+
return /* @__PURE__ */ jsx13(
|
|
8380
|
+
CenteredState,
|
|
8381
|
+
{
|
|
8382
|
+
icon: /* @__PURE__ */ jsx13(LoaderCircleIcon3, { className: "size-5 animate-spin motion-reduce:animate-none", "aria-hidden": true }),
|
|
8383
|
+
children: /* @__PURE__ */ jsxs11("p", { className: "text-og-sm font-medium text-og-fg", children: [
|
|
8384
|
+
"Opening ",
|
|
8385
|
+
name
|
|
8386
|
+
] })
|
|
8387
|
+
}
|
|
8388
|
+
);
|
|
8389
|
+
}
|
|
7627
8390
|
function initialWorkspaceTab(events, override) {
|
|
7628
8391
|
if (override) return override;
|
|
7629
8392
|
let bestSeq = -1;
|
|
@@ -7635,7 +8398,17 @@ function initialWorkspaceTab(events, override) {
|
|
|
7635
8398
|
bestSeq = event.sequence;
|
|
7636
8399
|
bestFileCount = typeof stats?.fileCount === "number" ? stats.fileCount : 0;
|
|
7637
8400
|
}
|
|
7638
|
-
|
|
8401
|
+
if (bestFileCount > 0) return WORKBENCH_TAB_CHANGES;
|
|
8402
|
+
let latestGitSeq = -1;
|
|
8403
|
+
let committedOnly = false;
|
|
8404
|
+
for (const event of events ?? []) {
|
|
8405
|
+
if (event.type !== "git.changed" || event.sequence > bestSeq) continue;
|
|
8406
|
+
if (event.sequence <= latestGitSeq) continue;
|
|
8407
|
+
const payload = event.payload;
|
|
8408
|
+
latestGitSeq = event.sequence;
|
|
8409
|
+
committedOnly = typeof payload?.ahead === "number" && payload.ahead > 0 || payload?.reason === "commit";
|
|
8410
|
+
}
|
|
8411
|
+
return committedOnly ? WORKBENCH_TAB_CHANGES : WORKBENCH_TAB_FILES;
|
|
7639
8412
|
}
|
|
7640
8413
|
function sandboxProvisionInFlight(events) {
|
|
7641
8414
|
let inFlight = false;
|
|
@@ -7657,29 +8430,59 @@ function sandboxProvisionInFlight(events) {
|
|
|
7657
8430
|
function emptyWarmIntents(sessionId) {
|
|
7658
8431
|
return {
|
|
7659
8432
|
sessionId,
|
|
7660
|
-
watchDesktop: false,
|
|
7661
8433
|
warmTerminal: false,
|
|
7662
8434
|
warmFiles: false
|
|
7663
8435
|
};
|
|
7664
8436
|
}
|
|
7665
8437
|
function useSandboxWorkspaceTabs(options) {
|
|
7666
8438
|
const { client, workspaceId } = useOpenGeni(options);
|
|
7667
|
-
const {
|
|
8439
|
+
const {
|
|
8440
|
+
sessionId,
|
|
8441
|
+
events,
|
|
8442
|
+
onNotify,
|
|
8443
|
+
browserWebSocketFactory,
|
|
8444
|
+
computerWebSocketFactory,
|
|
8445
|
+
requestedFilePath,
|
|
8446
|
+
requestedFileRequestId,
|
|
8447
|
+
onOpenFile,
|
|
8448
|
+
onOpenComputerSession,
|
|
8449
|
+
requestedComputerSessionId,
|
|
8450
|
+
requestedComputerRequestId
|
|
8451
|
+
} = options;
|
|
7668
8452
|
const initialTab = options.initialTab ?? null;
|
|
8453
|
+
const activeTab = options.activeTab ?? initialTab;
|
|
7669
8454
|
const requestedSurfaces = options.surfaces ?? WORKBENCH_SURFACES;
|
|
7670
8455
|
const surfaceSet = new Set(requestedSurfaces);
|
|
7671
8456
|
const changesEnabled = surfaceSet.has(WORKBENCH_TAB_CHANGES);
|
|
7672
8457
|
const filesEnabled = surfaceSet.has(WORKBENCH_TAB_FILES);
|
|
7673
8458
|
const terminalEnabled = surfaceSet.has(WORKBENCH_TAB_TERMINAL);
|
|
8459
|
+
const browserEnabled = surfaceSet.has(WORKBENCH_TAB_BROWSER);
|
|
7674
8460
|
const desktopEnabled = surfaceSet.has(WORKBENCH_TAB_DESKTOP);
|
|
8461
|
+
const createLinkedComputer = useCallback22(
|
|
8462
|
+
async (name) => {
|
|
8463
|
+
const response = await client.createComputerSession(workspaceId, {
|
|
8464
|
+
operationId: crypto.randomUUID(),
|
|
8465
|
+
sessionId,
|
|
8466
|
+
name
|
|
8467
|
+
});
|
|
8468
|
+
return {
|
|
8469
|
+
id: response.session.id,
|
|
8470
|
+
placement: response.session.placement
|
|
8471
|
+
};
|
|
8472
|
+
},
|
|
8473
|
+
[client, sessionId, workspaceId]
|
|
8474
|
+
);
|
|
7675
8475
|
const workspaceDataEnabled = changesEnabled || filesEnabled;
|
|
7676
|
-
const
|
|
8476
|
+
const machineSurfaceEnabled = workspaceDataEnabled || terminalEnabled || desktopEnabled;
|
|
8477
|
+
const resolvedActiveTab = activeTab ?? initialTab;
|
|
8478
|
+
const changesActive = resolvedActiveTab === WORKBENCH_TAB_CHANGES;
|
|
8479
|
+
const filesActive = resolvedActiveTab === WORKBENCH_TAB_FILES;
|
|
7677
8480
|
const surfaceIdentity = WORKBENCH_SURFACES.filter((surface) => surfaceSet.has(surface)).join(",");
|
|
7678
8481
|
const [storedWarmIntents, setStoredWarmIntents] = useState20(
|
|
7679
8482
|
() => emptyWarmIntents(sessionId)
|
|
7680
8483
|
);
|
|
7681
8484
|
const warmIntents = storedWarmIntents.sessionId === sessionId ? storedWarmIntents : emptyWarmIntents(sessionId);
|
|
7682
|
-
const {
|
|
8485
|
+
const { warmTerminal, warmFiles } = warmIntents;
|
|
7683
8486
|
const requestWarmIntent = useCallback22(
|
|
7684
8487
|
(intent) => {
|
|
7685
8488
|
setStoredWarmIntents((previous) => {
|
|
@@ -7693,13 +8496,13 @@ function useSandboxWorkspaceTabs(options) {
|
|
|
7693
8496
|
workspaceId,
|
|
7694
8497
|
sessionId,
|
|
7695
8498
|
pollIntervalMs: 8e3,
|
|
7696
|
-
enabled:
|
|
8499
|
+
enabled: machineSurfaceEnabled
|
|
7697
8500
|
});
|
|
7698
8501
|
const activeMachine = machines.machines.find((m) => m.sandboxId === machines.activeSandboxId) ?? machines.machines.find((m) => m.active) ?? null;
|
|
7699
8502
|
const caps = useSessionCapabilities(sessionId, {
|
|
7700
8503
|
events,
|
|
7701
|
-
enabled:
|
|
7702
|
-
attachDesktop:
|
|
8504
|
+
enabled: machineSurfaceEnabled,
|
|
8505
|
+
attachDesktop: false,
|
|
7703
8506
|
attachTerminal: terminalEnabled && warmTerminal,
|
|
7704
8507
|
// Explicit live-file intent only — NOT "the Files tab is open". A cold edit
|
|
7705
8508
|
// or guarded-file open wakes the box; a glance at the tree/diff does not.
|
|
@@ -7707,19 +8510,18 @@ function useSandboxWorkspaceTabs(options) {
|
|
|
7707
8510
|
});
|
|
7708
8511
|
const capabilities = caps.capabilities;
|
|
7709
8512
|
const liveness = capabilities?.liveness;
|
|
8513
|
+
const liveIoLiveness = liveness ?? null;
|
|
7710
8514
|
const fileSystemOn = capabilities?.FileSystem.available ?? false;
|
|
7711
8515
|
const fsReadOnly = capabilities?.FileSystem.readOnly ?? false;
|
|
7712
|
-
const filesEditable = fileSystemOn && !fsReadOnly && (liveness
|
|
8516
|
+
const filesEditable = fileSystemOn && !fsReadOnly && sandboxAcceptsLiveIo(liveness);
|
|
7713
8517
|
const gitOn = capabilities?.Git.available ?? false;
|
|
7714
8518
|
const terminalOn = terminalEnabled && (capabilities?.Terminal.transport ?? null) !== null;
|
|
7715
|
-
const
|
|
7716
|
-
const ptyCapable = (capabilities?.Terminal.ptyCapable ?? false) && !ptyWsLive;
|
|
8519
|
+
const ptyCapable = warmTerminal && (capabilities?.Terminal.ptyCapable ?? false) && capabilities?.Terminal.transport === "sse-events" && capabilities?.Terminal.reason === null;
|
|
7717
8520
|
const terminal = useSandboxTerminal(sessionId, {
|
|
7718
8521
|
events: terminalEnabled ? events : [],
|
|
7719
8522
|
interactive: terminalEnabled && ptyCapable,
|
|
7720
8523
|
liveness
|
|
7721
8524
|
});
|
|
7722
|
-
const desktopAdvertised = desktopEnabled && ((capabilities?.DesktopStream.transport ?? null) !== null || capabilities?.DesktopStream.reason === "lease_cold");
|
|
7723
8525
|
const provisioning = sandboxProvisionInFlight(events);
|
|
7724
8526
|
const renegotiate = caps.renegotiate;
|
|
7725
8527
|
const provisioningRef = useRef20({ sessionId, provisioning });
|
|
@@ -7735,9 +8537,9 @@ function useSandboxWorkspaceTabs(options) {
|
|
|
7735
8537
|
enabled: workspaceDataEnabled
|
|
7736
8538
|
});
|
|
7737
8539
|
const captureAvailable = captureState.available;
|
|
7738
|
-
const liveWorkspaceExpected = liveness
|
|
8540
|
+
const liveWorkspaceExpected = sandboxAcceptsLiveIo(liveness);
|
|
7739
8541
|
const capturePending = !liveWorkspaceExpected && (captureState.fileCount === null || captureState.loading);
|
|
7740
|
-
const repoPaths =
|
|
8542
|
+
const repoPaths = useMemo13(() => {
|
|
7741
8543
|
const advertised = capabilities?.Git.repos ?? [];
|
|
7742
8544
|
if (advertised.length > 0) return advertised;
|
|
7743
8545
|
return captureState.capture?.repos.map((repo) => repo.root) ?? [];
|
|
@@ -7760,7 +8562,9 @@ function useSandboxWorkspaceTabs(options) {
|
|
|
7760
8562
|
// No passive Channel-A reads while cold, even after a conclusive capture
|
|
7761
8563
|
// miss. A missing capture gets an explicit "Open live workspace" gate.
|
|
7762
8564
|
enabled: filesEnabled && (captureAvailable || fileSystemOn && liveWorkspaceExpected),
|
|
7763
|
-
|
|
8565
|
+
active: filesActive,
|
|
8566
|
+
repoPaths,
|
|
8567
|
+
liveness: liveIoLiveness,
|
|
7764
8568
|
capture: captureState.capture,
|
|
7765
8569
|
// A reverted optimistic mutation (e.g. a 409 rename collision) surfaces as a
|
|
7766
8570
|
// host notification — the tree silently rolls back, the user sees why.
|
|
@@ -7769,23 +8573,30 @@ function useSandboxWorkspaceTabs(options) {
|
|
|
7769
8573
|
message: `Could not ${op}: ${error.message}`
|
|
7770
8574
|
})
|
|
7771
8575
|
});
|
|
8576
|
+
const [storedChangesComparison, setStoredChangesComparison] = useState20(null);
|
|
8577
|
+
const captureSupportsBranch = captureState.capture !== null && captureState.capture.repos.length > 0 && captureState.capture.repos.every((repo) => repo.branchDiff !== void 0);
|
|
8578
|
+
const changesComparison = storedChangesComparison?.sessionId === sessionId ? storedChangesComparison.value : liveWorkspaceExpected || captureSupportsBranch ? "branch" : "working";
|
|
8579
|
+
const setChangesComparison = useCallback22(
|
|
8580
|
+
(value) => setStoredChangesComparison({ sessionId, value }),
|
|
8581
|
+
[sessionId]
|
|
8582
|
+
);
|
|
7772
8583
|
const git = useSandboxGit(sessionId, {
|
|
7773
8584
|
events,
|
|
7774
|
-
enabled: workspaceDataEnabled && (captureAvailable || gitOn && liveWorkspaceExpected),
|
|
7775
|
-
|
|
7776
|
-
|
|
7777
|
-
|
|
7778
|
-
|
|
7779
|
-
const stagedGit = useSandboxGit(sessionId, {
|
|
7780
|
-
events,
|
|
7781
|
-
// Captures intentionally expose one combined review diff, not a staged
|
|
7782
|
-
// index. Never probe the live index while the lease is cold.
|
|
7783
|
-
enabled: filesEnabled && gitOn && liveWorkspaceExpected,
|
|
7784
|
-
staged: true,
|
|
8585
|
+
enabled: changesComparison === "working" ? workspaceDataEnabled && (captureAvailable || gitOn && liveWorkspaceExpected) : changesComparison === "branch" ? workspaceDataEnabled && (captureSupportsBranch || gitOn && liveWorkspaceExpected) : workspaceDataEnabled && gitOn && liveWorkspaceExpected,
|
|
8586
|
+
// Before the dock chooses its source-driven default, Git is the sole probe
|
|
8587
|
+
// that may need to resolve that choice on a live workspace. Files remains
|
|
8588
|
+
// dormant, avoiding the old duplicate full-tree read.
|
|
8589
|
+
active: changesActive || resolvedActiveTab === null,
|
|
7785
8590
|
repoPaths,
|
|
7786
|
-
liveness,
|
|
7787
|
-
|
|
8591
|
+
liveness: liveIoLiveness,
|
|
8592
|
+
comparison: changesComparison,
|
|
8593
|
+
capture: changesComparison === "staged" ? null : captureState.capture
|
|
7788
8594
|
});
|
|
8595
|
+
useEffect23(() => {
|
|
8596
|
+
if (changesComparison === "branch" && git.error && storedChangesComparison?.sessionId !== sessionId) {
|
|
8597
|
+
setStoredChangesComparison({ sessionId, value: "working" });
|
|
8598
|
+
}
|
|
8599
|
+
}, [changesComparison, git.error, sessionId, storedChangesComparison?.sessionId]);
|
|
7789
8600
|
const [xtermTheme, setXtermTheme] = useState20(void 0);
|
|
7790
8601
|
useEffect23(() => {
|
|
7791
8602
|
if (!terminalEnabled || typeof document === "undefined") return;
|
|
@@ -7798,33 +8609,13 @@ function useSandboxWorkspaceTabs(options) {
|
|
|
7798
8609
|
});
|
|
7799
8610
|
return () => observer.disconnect();
|
|
7800
8611
|
}, [terminalEnabled]);
|
|
7801
|
-
async function acknowledgeAndWatch() {
|
|
7802
|
-
try {
|
|
7803
|
-
const shared = capabilities?.DesktopStream.shared ?? false;
|
|
7804
|
-
await client.acknowledgeStream(workspaceId, sessionId, {
|
|
7805
|
-
acknowledgeUnredacted: true,
|
|
7806
|
-
acknowledgeShared: shared
|
|
7807
|
-
});
|
|
7808
|
-
requestWarmIntent("watchDesktop");
|
|
7809
|
-
caps.renegotiate();
|
|
7810
|
-
} catch (error) {
|
|
7811
|
-
onNotify?.({
|
|
7812
|
-
kind: "error",
|
|
7813
|
-
message: `Could not start the desktop stream: ${error instanceof Error ? error.message : String(error)}`
|
|
7814
|
-
});
|
|
7815
|
-
}
|
|
7816
|
-
}
|
|
7817
|
-
function rewarmDesktop() {
|
|
7818
|
-
if (!watchDesktop) requestWarmIntent("watchDesktop");
|
|
7819
|
-
caps.renegotiate();
|
|
7820
|
-
}
|
|
7821
8612
|
const dirtyCount = git.diff.length;
|
|
7822
8613
|
const chip = useMachineChip({
|
|
7823
8614
|
liveness,
|
|
7824
8615
|
capabilitiesState: caps.state,
|
|
7825
8616
|
activeMachineState: activeMachine?.state ?? null,
|
|
7826
8617
|
activeIsSelfhosted: activeMachine?.kind === "selfhosted",
|
|
7827
|
-
wantsWarm: terminalEnabled && warmTerminal ||
|
|
8618
|
+
wantsWarm: terminalEnabled && warmTerminal || workspaceDataEnabled && warmFiles,
|
|
7828
8619
|
capturedAt: captureState.capturedAt
|
|
7829
8620
|
});
|
|
7830
8621
|
const workspaceWaking = chip.state === "waking";
|
|
@@ -7837,7 +8628,9 @@ function useSandboxWorkspaceTabs(options) {
|
|
|
7837
8628
|
defaultTabRef.current = { identity: defaultIdentity, value: null };
|
|
7838
8629
|
}
|
|
7839
8630
|
const captureIsAuthoritative = !liveWorkspaceExpected && (liveness !== void 0 || caps.error !== null);
|
|
7840
|
-
const
|
|
8631
|
+
const captureHasChanges = (captureState.fileCount ?? 0) > 0 || (captureState.capture?.repos.some((repo) => (repo.branchDiff?.length ?? 0) > 0) ?? false);
|
|
8632
|
+
const captureUnavailable = captureState.fileCount === 0 && !captureHasChanges || captureState.error !== null;
|
|
8633
|
+
const implicitBranchFallbackPending = changesComparison === "branch" && git.error !== null && storedChangesComparison?.sessionId !== sessionId;
|
|
7841
8634
|
if (defaultTabRef.current.value === null) {
|
|
7842
8635
|
if (initialTab && (!isWorkbenchSurface(initialTab) || surfaceSet.has(initialTab))) {
|
|
7843
8636
|
defaultTabRef.current.value = initialTab;
|
|
@@ -7849,7 +8642,7 @@ function useSandboxWorkspaceTabs(options) {
|
|
|
7849
8642
|
);
|
|
7850
8643
|
} else if (captureIsAuthoritative && captureState.fileCount !== null && captureState.available) {
|
|
7851
8644
|
defaultTabRef.current.value = sourceDrivenDefaultTab(
|
|
7852
|
-
|
|
8645
|
+
captureHasChanges,
|
|
7853
8646
|
changesEnabled,
|
|
7854
8647
|
filesEnabled
|
|
7855
8648
|
);
|
|
@@ -7861,26 +8654,28 @@ function useSandboxWorkspaceTabs(options) {
|
|
|
7861
8654
|
defaultTabRef.current.value = sourceDrivenDefaultTab(false, changesEnabled, filesEnabled);
|
|
7862
8655
|
} else if (captureState.available && git.error && captureState.fileCount !== null) {
|
|
7863
8656
|
defaultTabRef.current.value = sourceDrivenDefaultTab(
|
|
7864
|
-
|
|
8657
|
+
captureHasChanges,
|
|
7865
8658
|
changesEnabled,
|
|
7866
8659
|
filesEnabled
|
|
7867
8660
|
);
|
|
7868
|
-
} else if (captureUnavailable && git.error) {
|
|
8661
|
+
} else if (captureUnavailable && git.error && !implicitBranchFallbackPending) {
|
|
7869
8662
|
defaultTabRef.current.value = sourceDrivenDefaultTab(false, changesEnabled, filesEnabled);
|
|
7870
8663
|
}
|
|
7871
8664
|
}
|
|
7872
8665
|
const defaultTab = defaultTabRef.current.value;
|
|
7873
|
-
const tabs =
|
|
8666
|
+
const tabs = useMemo13(() => {
|
|
7874
8667
|
const list = [];
|
|
7875
8668
|
if (changesEnabled)
|
|
7876
8669
|
list.push({
|
|
7877
8670
|
id: WORKBENCH_TAB_CHANGES,
|
|
7878
|
-
label: "Changes",
|
|
8671
|
+
label: /* @__PURE__ */ jsx13(WorkbenchTabLabel, { icon: /* @__PURE__ */ jsx13(GitCompareArrowsIcon, {}), children: "Changes" }),
|
|
7879
8672
|
badge: dirtyCount > 0 ? /* @__PURE__ */ jsx13(DirtyBadge, { count: dirtyCount }) : void 0,
|
|
7880
8673
|
content: /* @__PURE__ */ jsx13(
|
|
7881
8674
|
ChangesTabBody,
|
|
7882
8675
|
{
|
|
7883
8676
|
git,
|
|
8677
|
+
comparison: changesComparison,
|
|
8678
|
+
onComparisonChange: setChangesComparison,
|
|
7884
8679
|
captureAvailable,
|
|
7885
8680
|
captureRevision: captureState.revision,
|
|
7886
8681
|
capturePending,
|
|
@@ -7902,13 +8697,12 @@ function useSandboxWorkspaceTabs(options) {
|
|
|
7902
8697
|
if (filesEnabled)
|
|
7903
8698
|
list.push({
|
|
7904
8699
|
id: WORKBENCH_TAB_FILES,
|
|
7905
|
-
label: "Files",
|
|
8700
|
+
label: /* @__PURE__ */ jsx13(WorkbenchTabLabel, { icon: /* @__PURE__ */ jsx13(FileCode2Icon2, {}), children: "Files" }),
|
|
7906
8701
|
content: /* @__PURE__ */ jsx13(
|
|
7907
8702
|
SandboxFiles,
|
|
7908
8703
|
{
|
|
7909
8704
|
files,
|
|
7910
8705
|
git,
|
|
7911
|
-
stagedGit,
|
|
7912
8706
|
fileSystemAvailable: fileSystemOn || captureAvailable,
|
|
7913
8707
|
editable: filesEditable && files.source === "live" && files.error === null && !files.loading,
|
|
7914
8708
|
workspaceResting: !liveWorkspaceExpected && liveness !== void 0 && !capturePending && !captureAvailable && !workspaceWaking,
|
|
@@ -7918,7 +8712,7 @@ function useSandboxWorkspaceTabs(options) {
|
|
|
7918
8712
|
...requestedFilePath ? {
|
|
7919
8713
|
requestedPath: requestedFilePath,
|
|
7920
8714
|
...requestedFileRequestId !== null && requestedFileRequestId !== void 0 ? { requestedPathRequestId: requestedFileRequestId } : {},
|
|
7921
|
-
requestedPathReady: liveness
|
|
8715
|
+
requestedPathReady: sandboxAcceptsLiveIo(liveness)
|
|
7922
8716
|
} : {},
|
|
7923
8717
|
onEditIntent: () => requestWarmIntent("warmFiles"),
|
|
7924
8718
|
className: "h-full"
|
|
@@ -7929,7 +8723,7 @@ function useSandboxWorkspaceTabs(options) {
|
|
|
7929
8723
|
if (terminalOn) {
|
|
7930
8724
|
list.push({
|
|
7931
8725
|
id: WORKBENCH_TAB_TERMINAL,
|
|
7932
|
-
label: "Terminal",
|
|
8726
|
+
label: /* @__PURE__ */ jsx13(WorkbenchTabLabel, { icon: /* @__PURE__ */ jsx13(SquareTerminalIcon, {}), children: "Terminal" }),
|
|
7933
8727
|
content: /* @__PURE__ */ jsx13("div", { className: "h-full bg-og-bg p-1", children: /* @__PURE__ */ jsx13(
|
|
7934
8728
|
SandboxTerminal,
|
|
7935
8729
|
{
|
|
@@ -7940,26 +8734,49 @@ function useSandboxWorkspaceTabs(options) {
|
|
|
7940
8734
|
shell: capabilities?.Terminal.shell ?? void 0,
|
|
7941
8735
|
liveness,
|
|
7942
8736
|
...xtermTheme ? { theme: xtermTheme } : {}
|
|
7943
|
-
}
|
|
8737
|
+
},
|
|
8738
|
+
sessionId
|
|
7944
8739
|
) })
|
|
7945
8740
|
});
|
|
7946
8741
|
}
|
|
7947
|
-
if (
|
|
8742
|
+
if (browserEnabled) {
|
|
8743
|
+
list.push({
|
|
8744
|
+
id: WORKBENCH_TAB_BROWSER,
|
|
8745
|
+
label: /* @__PURE__ */ jsx13(WorkbenchTabLabel, { icon: /* @__PURE__ */ jsx13(Globe2Icon, {}), children: "Browser" }),
|
|
8746
|
+
content: /* @__PURE__ */ jsx13(Suspense2, { fallback: /* @__PURE__ */ jsx13(WorkbenchSurfaceLoading, { name: "Browser" }), children: /* @__PURE__ */ jsx13(
|
|
8747
|
+
LazyBrowserViewer,
|
|
8748
|
+
{
|
|
8749
|
+
client,
|
|
8750
|
+
workspaceId,
|
|
8751
|
+
sessionId,
|
|
8752
|
+
onNotify,
|
|
8753
|
+
...desktopEnabled ? { createLinkedComputer } : {},
|
|
8754
|
+
...onOpenComputerSession ? { onOpenComputer: onOpenComputerSession } : {},
|
|
8755
|
+
...browserWebSocketFactory ? { webSocketFactory: browserWebSocketFactory } : {},
|
|
8756
|
+
className: "h-full"
|
|
8757
|
+
},
|
|
8758
|
+
sessionId
|
|
8759
|
+
) })
|
|
8760
|
+
});
|
|
8761
|
+
}
|
|
8762
|
+
if (desktopEnabled) {
|
|
7948
8763
|
list.push({
|
|
7949
8764
|
id: WORKBENCH_TAB_DESKTOP,
|
|
7950
|
-
label: "
|
|
7951
|
-
|
|
7952
|
-
|
|
7953
|
-
DesktopViewer,
|
|
8765
|
+
label: /* @__PURE__ */ jsx13(WorkbenchTabLabel, { icon: /* @__PURE__ */ jsx13(MonitorIcon2, {}), children: "Computer" }),
|
|
8766
|
+
content: /* @__PURE__ */ jsx13(Suspense2, { fallback: /* @__PURE__ */ jsx13(WorkbenchSurfaceLoading, { name: "Computer" }), children: /* @__PURE__ */ jsx13(
|
|
8767
|
+
LazyComputerViewer,
|
|
7954
8768
|
{
|
|
7955
|
-
|
|
7956
|
-
|
|
7957
|
-
|
|
7958
|
-
|
|
7959
|
-
|
|
8769
|
+
client,
|
|
8770
|
+
workspaceId,
|
|
8771
|
+
sessionId,
|
|
8772
|
+
onNotify,
|
|
8773
|
+
requestedComputerSessionId,
|
|
8774
|
+
requestedComputerRequestId,
|
|
8775
|
+
...computerWebSocketFactory ? { webSocketFactory: computerWebSocketFactory } : {},
|
|
7960
8776
|
className: "h-full"
|
|
7961
|
-
}
|
|
7962
|
-
|
|
8777
|
+
},
|
|
8778
|
+
sessionId
|
|
8779
|
+
) })
|
|
7963
8780
|
});
|
|
7964
8781
|
}
|
|
7965
8782
|
return list;
|
|
@@ -7968,19 +8785,27 @@ function useSandboxWorkspaceTabs(options) {
|
|
|
7968
8785
|
changesEnabled,
|
|
7969
8786
|
filesEnabled,
|
|
7970
8787
|
terminalOn,
|
|
7971
|
-
|
|
8788
|
+
browserEnabled,
|
|
8789
|
+
desktopEnabled,
|
|
7972
8790
|
captureAvailable,
|
|
7973
8791
|
dirtyCount,
|
|
7974
|
-
watchDesktop,
|
|
7975
8792
|
warmTerminal,
|
|
7976
8793
|
requestedFilePath,
|
|
7977
8794
|
requestedFileRequestId,
|
|
7978
8795
|
onOpenFile,
|
|
8796
|
+
onOpenComputerSession,
|
|
8797
|
+
requestedComputerSessionId,
|
|
8798
|
+
requestedComputerRequestId,
|
|
8799
|
+
onNotify,
|
|
8800
|
+
browserWebSocketFactory,
|
|
8801
|
+
computerWebSocketFactory,
|
|
8802
|
+
client,
|
|
8803
|
+
workspaceId,
|
|
7979
8804
|
liveness,
|
|
7980
8805
|
sessionId,
|
|
7981
8806
|
files,
|
|
7982
8807
|
git,
|
|
7983
|
-
|
|
8808
|
+
changesComparison,
|
|
7984
8809
|
terminal,
|
|
7985
8810
|
xtermTheme,
|
|
7986
8811
|
capabilities,
|
|
@@ -7988,13 +8813,14 @@ function useSandboxWorkspaceTabs(options) {
|
|
|
7988
8813
|
caps.state,
|
|
7989
8814
|
caps.error,
|
|
7990
8815
|
caps.viewerCapReached,
|
|
7991
|
-
requestWarmIntent
|
|
8816
|
+
requestWarmIntent,
|
|
8817
|
+
createLinkedComputer
|
|
7992
8818
|
]);
|
|
7993
8819
|
return {
|
|
7994
8820
|
tabs,
|
|
7995
8821
|
defaultTab,
|
|
7996
8822
|
machine: {
|
|
7997
|
-
enabled:
|
|
8823
|
+
enabled: machineSurfaceEnabled,
|
|
7998
8824
|
chip,
|
|
7999
8825
|
activeMachine,
|
|
8000
8826
|
loading: machines.loading,
|
|
@@ -8013,8 +8839,11 @@ function SandboxWorkspace(props) {
|
|
|
8013
8839
|
trailingTabs,
|
|
8014
8840
|
initialTab,
|
|
8015
8841
|
onNotify,
|
|
8842
|
+
browserWebSocketFactory,
|
|
8843
|
+
computerWebSocketFactory,
|
|
8016
8844
|
collapsed,
|
|
8017
8845
|
onCollapsedChange,
|
|
8846
|
+
showCollapseControl,
|
|
8018
8847
|
mobileLeadingControl,
|
|
8019
8848
|
autoSaveId,
|
|
8020
8849
|
defaultSize,
|
|
@@ -8024,7 +8853,11 @@ function SandboxWorkspace(props) {
|
|
|
8024
8853
|
} = props;
|
|
8025
8854
|
const [storedSelection, setStoredSelection] = useState20(null);
|
|
8026
8855
|
const [requestedFile, setRequestedFile] = useState20(null);
|
|
8856
|
+
const [requestedComputer, setRequestedComputer] = useState20(null);
|
|
8027
8857
|
const nextFileRequestId = useRef20(0);
|
|
8858
|
+
const nextComputerRequestId = useRef20(0);
|
|
8859
|
+
const selectedTab = storedSelection?.sessionId === sessionId ? storedSelection.tab : null;
|
|
8860
|
+
const activeTabHint = selectedTab ?? initialTab ?? leadingTabs?.[0]?.id ?? null;
|
|
8028
8861
|
const openFile = useCallback22(
|
|
8029
8862
|
(path) => {
|
|
8030
8863
|
nextFileRequestId.current += 1;
|
|
@@ -8033,6 +8866,18 @@ function SandboxWorkspace(props) {
|
|
|
8033
8866
|
},
|
|
8034
8867
|
[sessionId]
|
|
8035
8868
|
);
|
|
8869
|
+
const openComputer = useCallback22(
|
|
8870
|
+
(computerSessionId) => {
|
|
8871
|
+
nextComputerRequestId.current += 1;
|
|
8872
|
+
setRequestedComputer({
|
|
8873
|
+
sessionId,
|
|
8874
|
+
computerSessionId,
|
|
8875
|
+
requestId: nextComputerRequestId.current
|
|
8876
|
+
});
|
|
8877
|
+
setStoredSelection({ sessionId, tab: WORKBENCH_TAB_DESKTOP });
|
|
8878
|
+
},
|
|
8879
|
+
[sessionId]
|
|
8880
|
+
);
|
|
8036
8881
|
const {
|
|
8037
8882
|
tabs: workbenchTabs,
|
|
8038
8883
|
machine,
|
|
@@ -8044,13 +8889,18 @@ function SandboxWorkspace(props) {
|
|
|
8044
8889
|
events,
|
|
8045
8890
|
...surfaces ? { surfaces } : {},
|
|
8046
8891
|
...initialTab ? { initialTab } : {},
|
|
8892
|
+
activeTab: activeTabHint,
|
|
8047
8893
|
...onNotify ? { onNotify } : {},
|
|
8894
|
+
...browserWebSocketFactory ? { browserWebSocketFactory } : {},
|
|
8895
|
+
...computerWebSocketFactory ? { computerWebSocketFactory } : {},
|
|
8048
8896
|
requestedFilePath: requestedFile?.sessionId === sessionId ? requestedFile.path : null,
|
|
8049
8897
|
requestedFileRequestId: requestedFile?.sessionId === sessionId ? requestedFile.requestId : null,
|
|
8050
|
-
onOpenFile: openFile
|
|
8898
|
+
onOpenFile: openFile,
|
|
8899
|
+
onOpenComputerSession: openComputer,
|
|
8900
|
+
requestedComputerSessionId: requestedComputer?.sessionId === sessionId ? requestedComputer.computerSessionId : null,
|
|
8901
|
+
requestedComputerRequestId: requestedComputer?.sessionId === sessionId ? requestedComputer.requestId : null
|
|
8051
8902
|
});
|
|
8052
8903
|
const tabs = [...leadingTabs ?? [], ...workbenchTabs, ...trailingTabs ?? []];
|
|
8053
|
-
const selectedTab = storedSelection?.sessionId === sessionId ? storedSelection.tab : null;
|
|
8054
8904
|
const preferredTab = selectedTab ?? defaultTab;
|
|
8055
8905
|
const activeTab = preferredTab && tabs.some((tab) => tab.id === preferredTab) ? preferredTab : tabs[0]?.id;
|
|
8056
8906
|
const selectTab = useCallback22(
|
|
@@ -8065,19 +8915,12 @@ function SandboxWorkspace(props) {
|
|
|
8065
8915
|
...activeTab !== void 0 ? { activeTab } : {},
|
|
8066
8916
|
onActiveTabChange: selectTab,
|
|
8067
8917
|
...machine.enabled ? {
|
|
8068
|
-
headerAccessory: /* @__PURE__ */ jsx13(
|
|
8069
|
-
MachineStateChip,
|
|
8070
|
-
{
|
|
8071
|
-
chip: machine.chip,
|
|
8072
|
-
activeMachine: machine.activeMachine,
|
|
8073
|
-
error: machine.error,
|
|
8074
|
-
onRetry: () => void machine.refresh()
|
|
8075
|
-
}
|
|
8076
|
-
)
|
|
8918
|
+
headerAccessory: /* @__PURE__ */ jsx13(MachineStateChip, { chip: machine.chip })
|
|
8077
8919
|
} : {},
|
|
8078
8920
|
...mobileLeadingControl !== void 0 ? { mobileLeadingControl } : {},
|
|
8079
8921
|
...collapsed !== void 0 ? { collapsed } : {},
|
|
8080
8922
|
...onCollapsedChange ? { onCollapsedChange } : {},
|
|
8923
|
+
...showCollapseControl !== void 0 ? { showCollapseControl } : {},
|
|
8081
8924
|
...autoSaveId !== void 0 ? { autoSaveId } : {},
|
|
8082
8925
|
...defaultSize !== void 0 ? { defaultSize } : {},
|
|
8083
8926
|
...minSize !== void 0 ? { minSize } : {},
|
|
@@ -8089,94 +8932,30 @@ function SandboxWorkspace(props) {
|
|
|
8089
8932
|
function DirtyBadge({ count }) {
|
|
8090
8933
|
return /* @__PURE__ */ jsx13("span", { className: "rounded-og-xs bg-og-accent-soft px-1 text-og-xs text-og-fg-muted", children: count });
|
|
8091
8934
|
}
|
|
8092
|
-
function MachineKindIcon({
|
|
8093
|
-
kind,
|
|
8094
|
-
className
|
|
8095
|
-
}) {
|
|
8096
|
-
const Icon = kind === "selfhosted" ? LaptopIcon : CpuIcon;
|
|
8097
|
-
return /* @__PURE__ */ jsx13(Icon, { className: cn("size-3.5 shrink-0 text-og-fg-subtle", className), "aria-hidden": true });
|
|
8098
|
-
}
|
|
8099
8935
|
function chipDotClass(state) {
|
|
8100
8936
|
if (state === "live") return "bg-og-status-running";
|
|
8101
8937
|
if (state === "waking") return "bg-og-status-idle animate-pulse motion-reduce:animate-none";
|
|
8102
8938
|
return "bg-og-fg-subtle";
|
|
8103
8939
|
}
|
|
8104
|
-
function MachineStateChip({
|
|
8105
|
-
|
|
8106
|
-
|
|
8107
|
-
|
|
8108
|
-
|
|
8109
|
-
|
|
8110
|
-
|
|
8111
|
-
|
|
8112
|
-
|
|
8113
|
-
|
|
8114
|
-
|
|
8115
|
-
|
|
8116
|
-
|
|
8117
|
-
|
|
8118
|
-
|
|
8119
|
-
|
|
8120
|
-
|
|
8121
|
-
|
|
8122
|
-
|
|
8123
|
-
{
|
|
8124
|
-
className: cn("size-1.5 shrink-0 rounded-full", chipDotClass(chip.state)),
|
|
8125
|
-
"aria-hidden": true
|
|
8126
|
-
}
|
|
8127
|
-
),
|
|
8128
|
-
/* @__PURE__ */ jsx13("span", { className: "max-w-[11rem] truncate", children: chip.label })
|
|
8129
|
-
]
|
|
8130
|
-
}
|
|
8131
|
-
) }),
|
|
8132
|
-
/* @__PURE__ */ jsx13(Popover.Portal, { forceMount: true, children: /* @__PURE__ */ jsxs11(
|
|
8133
|
-
Popover.Content,
|
|
8134
|
-
{
|
|
8135
|
-
forceMount: true,
|
|
8136
|
-
"data-machine-state-popover": true,
|
|
8137
|
-
align: "end",
|
|
8138
|
-
sideOffset: 6,
|
|
8139
|
-
collisionPadding: 8,
|
|
8140
|
-
className: "og-root z-50 w-64 max-w-[calc(100vw-1rem)] rounded-og-md border border-og-border bg-og-surface-1 p-3 text-og-sm text-og-fg shadow-lg outline-none",
|
|
8141
|
-
style: portalStyle,
|
|
8142
|
-
children: [
|
|
8143
|
-
/* @__PURE__ */ jsxs11("div", { className: "flex min-w-0 items-center gap-1.5", children: [
|
|
8144
|
-
/* @__PURE__ */ jsx13(MachineKindIcon, { kind: activeMachine?.kind }),
|
|
8145
|
-
/* @__PURE__ */ jsx13("span", { className: "min-w-0 truncate font-medium", children: activeMachine?.name ?? "Sandbox" })
|
|
8146
|
-
] }),
|
|
8147
|
-
/* @__PURE__ */ jsxs11("div", { className: "mt-2.5 flex items-center justify-between gap-2", children: [
|
|
8148
|
-
/* @__PURE__ */ jsx13("span", { className: "text-og-xs text-og-fg-subtle", children: "Connection" }),
|
|
8149
|
-
activeMachine ? /* @__PURE__ */ jsx13(
|
|
8150
|
-
ConnectionStatusPill,
|
|
8151
|
-
{
|
|
8152
|
-
status: connectionStatusForState(activeMachine.state),
|
|
8153
|
-
size: "sm"
|
|
8154
|
-
}
|
|
8155
|
-
) : /* @__PURE__ */ jsx13("span", { className: "text-og-xs text-og-fg-muted", children: chip.label })
|
|
8156
|
-
] }),
|
|
8157
|
-
chip.asOf ? /* @__PURE__ */ jsxs11("p", { className: "mt-2.5 text-og-xs leading-4 text-og-fg-subtle", children: [
|
|
8158
|
-
"Workspace shown as of ",
|
|
8159
|
-
formatAsOf(chip.asOf, Date.now()),
|
|
8160
|
-
" \u2014 the machine is not live."
|
|
8161
|
-
] }) : null,
|
|
8162
|
-
activeMachine && activeMachine.sharedSessionCount > 1 ? /* @__PURE__ */ jsx13("div", { className: "mt-2.5", children: /* @__PURE__ */ jsx13(
|
|
8163
|
-
SharedMachineDisclosure,
|
|
8164
|
-
{
|
|
8165
|
-
sharedSessionCount: activeMachine.sharedSessionCount,
|
|
8166
|
-
density: "full"
|
|
8167
|
-
}
|
|
8168
|
-
) }) : null,
|
|
8169
|
-
error ? /* @__PURE__ */ jsxs11("div", { className: "mt-2.5 space-y-1.5 border-t border-og-border pt-2.5", children: [
|
|
8170
|
-
/* @__PURE__ */ jsx13("p", { className: "text-og-xs leading-4 text-og-danger", children: "Couldn't reach the sandbox for this session." }),
|
|
8171
|
-
/* @__PURE__ */ jsxs11(DockActionButton, { onClick: onRetry, children: [
|
|
8172
|
-
/* @__PURE__ */ jsx13(RefreshCwIcon2, { className: "size-3" }),
|
|
8173
|
-
"Retry"
|
|
8174
|
-
] })
|
|
8175
|
-
] }) : null
|
|
8176
|
-
]
|
|
8177
|
-
}
|
|
8178
|
-
) })
|
|
8179
|
-
] });
|
|
8940
|
+
function MachineStateChip({ chip }) {
|
|
8941
|
+
return /* @__PURE__ */ jsxs11(
|
|
8942
|
+
"div",
|
|
8943
|
+
{
|
|
8944
|
+
role: "status",
|
|
8945
|
+
"aria-label": `Machine: ${chip.label}`,
|
|
8946
|
+
className: "inline-flex min-h-7 items-center gap-1.5 px-2 py-1 text-og-xs font-medium text-og-fg-muted max-[1023px]:min-h-11 pointer-coarse:min-h-11",
|
|
8947
|
+
children: [
|
|
8948
|
+
/* @__PURE__ */ jsx13(
|
|
8949
|
+
"span",
|
|
8950
|
+
{
|
|
8951
|
+
className: cn("size-1.5 shrink-0 rounded-full", chipDotClass(chip.state)),
|
|
8952
|
+
"aria-hidden": true
|
|
8953
|
+
}
|
|
8954
|
+
),
|
|
8955
|
+
/* @__PURE__ */ jsx13("span", { className: "max-w-[11rem] truncate", children: chip.label })
|
|
8956
|
+
]
|
|
8957
|
+
}
|
|
8958
|
+
);
|
|
8180
8959
|
}
|
|
8181
8960
|
function DockActionButton({ onClick, children }) {
|
|
8182
8961
|
return /* @__PURE__ */ jsx13(
|
|
@@ -8184,13 +8963,41 @@ function DockActionButton({ onClick, children }) {
|
|
|
8184
8963
|
{
|
|
8185
8964
|
type: "button",
|
|
8186
8965
|
onClick,
|
|
8187
|
-
className: "inline-flex items-center gap-1.5 rounded-og-sm border border-og-border px-2 py-1 text-og-xs font-medium text-og-fg-muted transition-colors hover:border-og-border-strong hover:text-og-fg focus-visible:outline-
|
|
8966
|
+
className: "inline-flex items-center gap-1.5 rounded-og-sm border border-og-border px-2 py-1 text-og-xs font-medium text-og-fg-muted transition-colors hover:border-og-border-strong hover:text-og-fg focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-og-accent max-[1023px]:min-h-11 pointer-coarse:min-h-11",
|
|
8188
8967
|
children
|
|
8189
8968
|
}
|
|
8190
8969
|
);
|
|
8191
8970
|
}
|
|
8192
8971
|
function ChangesTabBody({
|
|
8972
|
+
comparison,
|
|
8973
|
+
onComparisonChange,
|
|
8974
|
+
...props
|
|
8975
|
+
}) {
|
|
8976
|
+
return /* @__PURE__ */ jsxs11("div", { className: "flex h-full min-h-0 flex-col", children: [
|
|
8977
|
+
/* @__PURE__ */ jsx13("div", { className: "flex shrink-0 items-center gap-1 border-b border-og-border bg-og-surface-1 px-2 py-1.5", children: [
|
|
8978
|
+
["branch", "Branch"],
|
|
8979
|
+
["working", "Uncommitted"],
|
|
8980
|
+
["staged", "Staged"]
|
|
8981
|
+
].map(([value, label]) => /* @__PURE__ */ jsx13(
|
|
8982
|
+
"button",
|
|
8983
|
+
{
|
|
8984
|
+
type: "button",
|
|
8985
|
+
"aria-pressed": comparison === value,
|
|
8986
|
+
onClick: () => onComparisonChange(value),
|
|
8987
|
+
className: cn(
|
|
8988
|
+
"rounded-og-sm px-2 py-1 text-og-xs font-medium transition-colors focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-og-accent max-[1023px]:min-h-11 pointer-coarse:min-h-11",
|
|
8989
|
+
comparison === value ? "bg-og-surface-2 text-og-fg shadow-sm" : "text-og-fg-muted hover:bg-og-surface-2 hover:text-og-fg"
|
|
8990
|
+
),
|
|
8991
|
+
children: label
|
|
8992
|
+
},
|
|
8993
|
+
value
|
|
8994
|
+
)) }),
|
|
8995
|
+
/* @__PURE__ */ jsx13("div", { className: "min-h-0 flex-1", children: /* @__PURE__ */ jsx13(ChangesTabContent, { comparison, ...props }) })
|
|
8996
|
+
] });
|
|
8997
|
+
}
|
|
8998
|
+
function ChangesTabContent({
|
|
8193
8999
|
git,
|
|
9000
|
+
comparison,
|
|
8194
9001
|
captureAvailable,
|
|
8195
9002
|
captureRevision,
|
|
8196
9003
|
capturePending,
|
|
@@ -8221,7 +9028,7 @@ function ChangesTabBody({
|
|
|
8221
9028
|
type: "button",
|
|
8222
9029
|
onClick: () => void git.refresh(),
|
|
8223
9030
|
disabled: git.loading,
|
|
8224
|
-
className: "inline-flex min-h-7 shrink-0 items-center gap-1 rounded-og-sm border border-og-border bg-og-surface-1 px-2 font-medium text-og-fg transition-colors hover:border-og-border-strong focus-visible:outline-
|
|
9031
|
+
className: "inline-flex min-h-7 shrink-0 items-center gap-1 rounded-og-sm border border-og-border bg-og-surface-1 px-2 font-medium text-og-fg transition-colors hover:border-og-border-strong focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-og-accent disabled:cursor-not-allowed disabled:opacity-50 pointer-coarse:min-h-11",
|
|
8225
9032
|
children: [
|
|
8226
9033
|
/* @__PURE__ */ jsx13(
|
|
8227
9034
|
RefreshCwIcon2,
|
|
@@ -8277,7 +9084,7 @@ function ChangesTabBody({
|
|
|
8277
9084
|
}
|
|
8278
9085
|
);
|
|
8279
9086
|
}
|
|
8280
|
-
if (!liveWorkspaceExpected && !captureAvailable) {
|
|
9087
|
+
if (!liveWorkspaceExpected && (comparison !== "working" || !captureAvailable)) {
|
|
8281
9088
|
return /* @__PURE__ */ jsxs11(
|
|
8282
9089
|
CenteredState,
|
|
8283
9090
|
{
|
|
@@ -8290,7 +9097,7 @@ function ChangesTabBody({
|
|
|
8290
9097
|
) : /* @__PURE__ */ jsx13(CpuIcon, { className: "size-5", "aria-hidden": true }),
|
|
8291
9098
|
children: [
|
|
8292
9099
|
/* @__PURE__ */ jsx13("p", { className: "text-og-sm font-medium text-og-fg", children: workspaceWaking ? "Waking workspace" : "Workspace is resting" }),
|
|
8293
|
-
/* @__PURE__ */ jsx13("p", { className: "text-og-sm leading-5 text-og-fg-subtle", children: workspaceWaking ? "Connecting to the live working tree\u2026" : "No captured revision is available yet. Wake the sandbox to inspect
|
|
9100
|
+
/* @__PURE__ */ jsx13("p", { className: "text-og-sm leading-5 text-og-fg-subtle", children: workspaceWaking ? "Connecting to the live working tree\u2026" : comparison === "branch" ? "Wake the sandbox to compare this branch with the remote default branch." : comparison === "staged" ? "Wake the sandbox to inspect staged changes." : "No captured revision is available yet. Wake the sandbox to inspect uncommitted changes." }),
|
|
8294
9101
|
!workspaceWaking ? /* @__PURE__ */ jsxs11(DockActionButton, { onClick: onWake, children: [
|
|
8295
9102
|
/* @__PURE__ */ jsx13(CpuIcon, { className: "size-3" }),
|
|
8296
9103
|
"Open live workspace"
|
|
@@ -8318,8 +9125,8 @@ function ChangesTabBody({
|
|
|
8318
9125
|
);
|
|
8319
9126
|
}
|
|
8320
9127
|
return /* @__PURE__ */ jsxs11(CenteredState, { icon: /* @__PURE__ */ jsx13(CircleCheckIcon, { className: "size-5", "aria-hidden": true }), tone: "success", children: [
|
|
8321
|
-
/* @__PURE__ */ jsx13("p", { className: "text-og-sm font-medium text-og-fg", children: "
|
|
8322
|
-
/* @__PURE__ */ jsx13("p", { className: "text-og-sm leading-5 text-og-fg-subtle", children: "
|
|
9128
|
+
/* @__PURE__ */ jsx13("p", { className: "text-og-sm font-medium text-og-fg", children: comparison === "branch" ? "No branch changes" : comparison === "staged" ? "No staged changes" : "No uncommitted changes" }),
|
|
9129
|
+
/* @__PURE__ */ jsx13("p", { className: "text-og-sm leading-5 text-og-fg-subtle", children: comparison === "branch" ? "This branch matches the remote default branch." : comparison === "staged" ? "Stage files to review the next commit here." : "Local edits not yet committed will appear here." })
|
|
8323
9130
|
] });
|
|
8324
9131
|
}
|
|
8325
9132
|
function CenteredState({
|
|
@@ -8356,7 +9163,7 @@ function CenteredState({
|
|
|
8356
9163
|
// src/hooks/use-codex-accounts.ts
|
|
8357
9164
|
import { useCallback as useCallback23, useState as useState21 } from "react";
|
|
8358
9165
|
function isCodexAccountEvent(event) {
|
|
8359
|
-
return event.type === "codex.account.switched"
|
|
9166
|
+
return event.type === "codex.account.switched";
|
|
8360
9167
|
}
|
|
8361
9168
|
var EMPTY_SETTINGS = {
|
|
8362
9169
|
rotationEnabled: false,
|
|
@@ -8483,6 +9290,7 @@ export {
|
|
|
8483
9290
|
FileBrowser,
|
|
8484
9291
|
FileWriteConflictError,
|
|
8485
9292
|
FleetTile,
|
|
9293
|
+
GeneratedVideoPlayer,
|
|
8486
9294
|
HEALTH_TOKEN,
|
|
8487
9295
|
HumanInputForm,
|
|
8488
9296
|
HumanInputSurface,
|
|
@@ -8551,6 +9359,7 @@ export {
|
|
|
8551
9359
|
VoiceRecordingStorageUnavailableError,
|
|
8552
9360
|
WINDOW_LABEL,
|
|
8553
9361
|
WORKBENCH_SURFACES,
|
|
9362
|
+
WORKBENCH_TAB_BROWSER,
|
|
8554
9363
|
WORKBENCH_TAB_CHANGES,
|
|
8555
9364
|
WORKBENCH_TAB_DESKTOP,
|
|
8556
9365
|
WORKBENCH_TAB_FILES,
|
|
@@ -8636,7 +9445,6 @@ export {
|
|
|
8636
9445
|
projectPendingHumanInputRequests,
|
|
8637
9446
|
projectPickerRows,
|
|
8638
9447
|
rawTypeOf,
|
|
8639
|
-
redactSecrets,
|
|
8640
9448
|
runnableLatencyModesForModel,
|
|
8641
9449
|
sandboxCommandExitCode,
|
|
8642
9450
|
sessionChromeGoalPillState,
|
|
@@ -8671,6 +9479,7 @@ export {
|
|
|
8671
9479
|
useOpenGeni,
|
|
8672
9480
|
useOpenGeniClient,
|
|
8673
9481
|
usePacks,
|
|
9482
|
+
usePageLiveActivity,
|
|
8674
9483
|
useRelayFrameStream,
|
|
8675
9484
|
useRig,
|
|
8676
9485
|
useRigChanges,
|
|
@@ -8692,6 +9501,7 @@ export {
|
|
|
8692
9501
|
useTranscription,
|
|
8693
9502
|
useTurnQueue,
|
|
8694
9503
|
useVariableSets,
|
|
9504
|
+
useVideoArtifactPlaybackLoader,
|
|
8695
9505
|
useVoiceInput,
|
|
8696
9506
|
useWorkspaceCapture,
|
|
8697
9507
|
useWorkspaceEdit,
|