@opengeni/react 0.57.0 → 2.2.0-canary.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +36 -11
- package/dist/artifacts-spreadsheet.js +1 -1
- package/dist/artifacts.js +1 -1
- package/dist/browser-viewer-GXUICIAG.js +14 -0
- package/dist/{chunk-R3AZOAB5.js → chunk-4MDLEHXL.js} +9 -115
- package/dist/chunk-4MDLEHXL.js.map +1 -0
- package/dist/{chunk-Y7OEDUJ5.js → chunk-5RZN3DD5.js} +1014 -420
- package/dist/chunk-5RZN3DD5.js.map +1 -0
- package/dist/chunk-5YK2ZDIG.js +116 -0
- package/dist/chunk-5YK2ZDIG.js.map +1 -0
- package/dist/chunk-H5YGYNXS.js +329 -0
- package/dist/chunk-H5YGYNXS.js.map +1 -0
- package/dist/{chunk-7WCLAEFV.js → chunk-ICA6R6TW.js} +2 -2
- package/dist/{chunk-7WCLAEFV.js.map → chunk-ICA6R6TW.js.map} +1 -1
- package/dist/{chunk-IGC6A2ZI.js → chunk-JOAVA2TW.js} +589 -217
- package/dist/chunk-JOAVA2TW.js.map +1 -0
- package/dist/{chunk-5UQPNLIO.js → chunk-LTCHJ4MS.js} +9 -9
- package/dist/chunk-LTCHJ4MS.js.map +1 -0
- package/dist/{chunk-C5ZPSSS5.js → chunk-NEXLLTP4.js} +2 -1
- package/dist/{chunk-C5ZPSSS5.js.map → chunk-NEXLLTP4.js.map} +1 -1
- package/dist/chunk-QGBJ6GHG.js +2300 -0
- package/dist/chunk-QGBJ6GHG.js.map +1 -0
- package/dist/{chunk-B6WPZSTT.js → chunk-UOLJWT5Y.js} +78 -16
- package/dist/chunk-UOLJWT5Y.js.map +1 -0
- package/dist/{chunk-MKPIAGVL.js → chunk-W6NFJXSA.js} +8 -19
- package/dist/chunk-W6NFJXSA.js.map +1 -0
- package/dist/{chunk-XBZWOM2K.js → chunk-X4A66EWZ.js} +8 -3
- package/dist/chunk-X4A66EWZ.js.map +1 -0
- package/dist/chunk-X4JAFD5R.js +3907 -0
- package/dist/chunk-X4JAFD5R.js.map +1 -0
- package/dist/{chunk-6KLX2RH5.js → chunk-XSA2QAKP.js} +334 -19
- package/dist/chunk-XSA2QAKP.js.map +1 -0
- package/dist/{chunk-HA46CYQK.js → chunk-XXWH2JLC.js} +792 -206
- package/dist/chunk-XXWH2JLC.js.map +1 -0
- package/dist/client.d.ts +2 -2
- package/dist/components/browser-viewer.d.ts +7 -1
- package/dist/components/computer-viewer.d.ts +4 -1
- package/dist/components/enrollment-consent.d.ts +6 -3
- package/dist/components/file-browser.d.ts +7 -1
- package/dist/components/human-input-form.d.ts +1 -1
- package/dist/components/human-input-surface.d.ts +1 -1
- package/dist/components/machine-card.d.ts +4 -1
- package/dist/components/machines/machine-detail.d.ts +4 -1
- package/dist/components/machines/machine-operation-policy.d.ts +11 -0
- package/dist/components/machines-dashboard.d.ts +3 -1
- package/dist/components/markdown.d.ts +20 -1
- package/dist/components/message-timeline.d.ts +1 -1
- package/dist/components/model-policy-picker.d.ts +2 -0
- package/dist/components/pierre-file.d.ts +1 -1
- package/dist/components/sandbox-files.d.ts +8 -2
- package/dist/components/sandbox-workspace.d.ts +33 -3
- package/dist/components/session-chrome.d.ts +15 -3
- package/dist/components/workspace-dock.d.ts +6 -2
- package/dist/composer.js +5 -5
- package/dist/computer-viewer-ZOP4Q7A7.js +16 -0
- package/dist/computer-viewer-ZOP4Q7A7.js.map +1 -0
- package/dist/hooks/channel-cache.d.ts +23 -0
- package/dist/hooks/shared-machines-list.d.ts +14 -0
- package/dist/hooks/use-attached-browsers.d.ts +2 -1
- package/dist/hooks/use-channels.d.ts +1 -0
- package/dist/hooks/use-composer.d.ts +37 -9
- package/dist/hooks/use-machine-chip.d.ts +3 -3
- package/dist/hooks/use-machines.d.ts +19 -1
- package/dist/hooks/use-sandbox-files.d.ts +1 -0
- package/dist/hooks/use-session-background-commands.d.ts +14 -0
- package/dist/hooks/use-turn-queue.d.ts +12 -0
- package/dist/hooks/use-workspace-sessions.d.ts +1 -0
- package/dist/index.d.ts +7 -5
- package/dist/index.js +1096 -623
- package/dist/index.js.map +1 -1
- package/dist/interaction.js +29 -181
- package/dist/interaction.js.map +1 -1
- package/dist/lib/interaction-errors.d.ts +3 -0
- package/dist/machines.d.ts +4 -2
- package/dist/machines.js +10 -2
- package/dist/model-policy.d.ts +1 -1
- package/dist/model-policy.js +1 -1
- package/dist/platform-activity-row-BJHUHXAT.js +169 -0
- package/dist/platform-activity-row-BJHUHXAT.js.map +1 -0
- package/dist/{queue-surface-implementation-2KVJTYXH.js → queue-surface-implementation-BSKUWHRI.js} +76 -16
- package/dist/queue-surface-implementation-BSKUWHRI.js.map +1 -0
- package/dist/realtime/realtime-control.d.ts +10 -3
- package/dist/realtime.js +138 -29
- package/dist/realtime.js.map +1 -1
- package/dist/session-ui.d.ts +1 -1
- package/dist/session-ui.js +6 -2
- package/dist/session.d.ts +2 -2
- package/dist/session.js +4 -4
- package/dist/timeline/index.d.ts +1 -1
- package/dist/timeline/platform-activity-row.d.ts +15 -0
- package/dist/timeline/screenshot-lightbox.d.ts +1 -1
- package/dist/timeline/shared.d.ts +4 -2
- package/dist/timeline/types.d.ts +48 -5
- package/dist/types/machines.d.ts +2 -2
- package/package.json +3 -3
- package/src/client.ts +12 -1
- package/src/components/artifacts/editable-spreadsheet.tsx +1 -1
- package/src/components/browser-viewer.tsx +361 -58
- package/src/components/chat-composer.tsx +1 -1
- package/src/components/composer.tsx +6 -6
- package/src/components/computer-viewer.tsx +368 -115
- package/src/components/enrollment-consent.tsx +64 -6
- package/src/components/file-browser.tsx +117 -2
- package/src/components/human-input-form.tsx +91 -55
- package/src/components/human-input-surface.tsx +1 -1
- package/src/components/machine-card.tsx +111 -0
- package/src/components/machine-input-display.ts +15 -0
- package/src/components/machines/machine-detail.tsx +47 -0
- package/src/components/machines/machine-operation-policy.tsx +179 -0
- package/src/components/machines-dashboard.tsx +6 -0
- package/src/components/markdown.tsx +174 -20
- package/src/components/message-timeline.tsx +162 -17
- package/src/components/model-policy-picker.tsx +9 -17
- package/src/components/pierre-file.tsx +1 -1
- package/src/components/queue-surface-implementation.tsx +82 -16
- package/src/components/sandbox-files.tsx +135 -19
- package/src/components/sandbox-workspace.tsx +204 -92
- package/src/components/session-chrome.tsx +415 -208
- package/src/components/session-status.tsx +1 -1
- package/src/components/user-message-body.tsx +168 -48
- package/src/components/workspace-dock.tsx +141 -175
- package/src/hooks/channel-cache.ts +104 -0
- package/src/hooks/shared-machines-list.ts +239 -0
- package/src/hooks/use-attached-browsers.ts +5 -1
- package/src/hooks/use-browser-frame-stream.ts +6 -1
- package/src/hooks/use-browser-session.ts +6 -4
- package/src/hooks/use-browser-sessions.ts +7 -1
- package/src/hooks/use-channels.ts +32 -10
- package/src/hooks/use-composer.ts +760 -291
- package/src/hooks/use-computer-frame-stream.ts +14 -9
- package/src/hooks/use-computer-session.ts +16 -14
- package/src/hooks/use-computer-sessions.ts +8 -1
- package/src/hooks/use-last-started-turn-policy.ts +1 -1
- package/src/hooks/use-machine-chip.ts +23 -6
- package/src/hooks/use-machines.ts +104 -6
- package/src/hooks/use-sandbox-files.ts +195 -140
- package/src/hooks/use-sandbox-git.ts +58 -29
- package/src/hooks/use-session-background-commands.ts +56 -0
- package/src/hooks/use-session-capabilities.ts +18 -0
- package/src/hooks/use-turn-queue.ts +111 -12
- package/src/hooks/use-workspace-sessions.ts +5 -1
- package/src/index.ts +26 -4
- package/src/lib/interaction-errors.ts +18 -0
- package/src/machines.ts +9 -1
- package/src/model-policy.ts +10 -2
- package/src/realtime/realtime-control.tsx +200 -37
- package/src/session-context.ts +1 -0
- package/src/session-ui.ts +6 -1
- package/src/session.ts +9 -1
- package/src/timeline/activity-rail.tsx +20 -75
- package/src/timeline/index.ts +4 -0
- package/src/timeline/platform-activity-row.tsx +201 -0
- package/src/timeline/projection.ts +476 -23
- package/src/timeline/screenshot-lightbox.tsx +48 -14
- package/src/timeline/shared.tsx +8 -2
- package/src/timeline/tool-renderers.tsx +104 -6
- package/src/timeline/turn-summary.tsx +6 -1
- package/src/timeline/types.ts +75 -2
- package/src/types/machines.ts +10 -0
- package/styles/compiled.css +115 -104
- package/dist/chunk-5UQPNLIO.js.map +0 -1
- package/dist/chunk-6KLX2RH5.js.map +0 -1
- package/dist/chunk-B6WPZSTT.js.map +0 -1
- package/dist/chunk-HA46CYQK.js.map +0 -1
- package/dist/chunk-IGC6A2ZI.js.map +0 -1
- package/dist/chunk-LLOLUHVG.js +0 -5937
- package/dist/chunk-LLOLUHVG.js.map +0 -1
- package/dist/chunk-MKPIAGVL.js.map +0 -1
- package/dist/chunk-R3AZOAB5.js.map +0 -1
- package/dist/chunk-XBZWOM2K.js.map +0 -1
- package/dist/chunk-Y7OEDUJ5.js.map +0 -1
- package/dist/interactive-workbench-viewers-TOHBXJEE.js +0 -15
- package/dist/queue-surface-implementation-2KVJTYXH.js.map +0 -1
- /package/dist/{interactive-workbench-viewers-TOHBXJEE.js.map → browser-viewer-GXUICIAG.js.map} +0 -0
package/dist/index.js
CHANGED
|
@@ -24,7 +24,7 @@ import {
|
|
|
24
24
|
useSessionLineage,
|
|
25
25
|
useSessionMcpApprovalPolicy,
|
|
26
26
|
useTurnQueue
|
|
27
|
-
} from "./chunk-
|
|
27
|
+
} from "./chunk-UOLJWT5Y.js";
|
|
28
28
|
import {
|
|
29
29
|
CONNECTION_STATUS_META,
|
|
30
30
|
ConnectionDot,
|
|
@@ -32,6 +32,9 @@ import {
|
|
|
32
32
|
EnrollmentConsent,
|
|
33
33
|
EnrollmentDeviceFlow,
|
|
34
34
|
HEALTH_TOKEN,
|
|
35
|
+
MACHINES_COMPOSER_POLL_MS,
|
|
36
|
+
MACHINES_DASHBOARD_POLL_MS,
|
|
37
|
+
MACHINES_SESSION_POLL_MS,
|
|
35
38
|
MACHINE_STATE_BADGE_META,
|
|
36
39
|
METRICS,
|
|
37
40
|
METRIC_WINDOWS,
|
|
@@ -40,6 +43,7 @@ import {
|
|
|
40
43
|
MachineDockBar,
|
|
41
44
|
MachineHealthPill,
|
|
42
45
|
MachineMetrics,
|
|
46
|
+
MachineOperationPolicyEditor,
|
|
43
47
|
MachineStatusPill,
|
|
44
48
|
MachinesDashboard,
|
|
45
49
|
MetricHistoryChart,
|
|
@@ -52,7 +56,7 @@ import {
|
|
|
52
56
|
healthPulses,
|
|
53
57
|
pointsFor,
|
|
54
58
|
useMachines
|
|
55
|
-
} from "./chunk-
|
|
59
|
+
} from "./chunk-XXWH2JLC.js";
|
|
56
60
|
import {
|
|
57
61
|
Actions,
|
|
58
62
|
AttachButton,
|
|
@@ -105,14 +109,14 @@ import {
|
|
|
105
109
|
useSlashCommands,
|
|
106
110
|
useTranscription,
|
|
107
111
|
useVoiceInput
|
|
108
|
-
} from "./chunk-
|
|
112
|
+
} from "./chunk-LTCHJ4MS.js";
|
|
109
113
|
import {
|
|
110
114
|
FILE_ONLY_MESSAGE_TEXT,
|
|
111
115
|
composeSendInput,
|
|
112
116
|
shouldSteerOnKey,
|
|
113
117
|
shouldSubmitOnKey,
|
|
114
118
|
useComposer
|
|
115
|
-
} from "./chunk-
|
|
119
|
+
} from "./chunk-JOAVA2TW.js";
|
|
116
120
|
import {
|
|
117
121
|
BillingClassMark,
|
|
118
122
|
ModelPolicyPicker,
|
|
@@ -121,7 +125,7 @@ import {
|
|
|
121
125
|
PickerBackHeader,
|
|
122
126
|
PickerNavRow,
|
|
123
127
|
defaultModelPolicyPickerMessages
|
|
124
|
-
} from "./chunk-
|
|
128
|
+
} from "./chunk-W6NFJXSA.js";
|
|
125
129
|
import {
|
|
126
130
|
advancedSourceSummary,
|
|
127
131
|
availabilityReasonLabel,
|
|
@@ -139,7 +143,7 @@ import {
|
|
|
139
143
|
projectPickerRows,
|
|
140
144
|
runnableLatencyModesForModel,
|
|
141
145
|
sortPickerRows
|
|
142
|
-
} from "./chunk-
|
|
146
|
+
} from "./chunk-X4A66EWZ.js";
|
|
143
147
|
import {
|
|
144
148
|
ActivityDisclosure,
|
|
145
149
|
ActivityRail,
|
|
@@ -185,8 +189,13 @@ import {
|
|
|
185
189
|
parseExecBannerSessionId,
|
|
186
190
|
parseFreeformApplyPatch,
|
|
187
191
|
parseToolArgs,
|
|
192
|
+
prefersReducedMotion,
|
|
188
193
|
rawTypeOf,
|
|
189
194
|
sandboxCommandExitCode,
|
|
195
|
+
sandboxFileLocationFromHref,
|
|
196
|
+
sandboxFilePathFromHref,
|
|
197
|
+
sessionChromeGoalPillExplanation,
|
|
198
|
+
sessionChromeGoalPillLabel,
|
|
190
199
|
sessionChromeGoalPillState,
|
|
191
200
|
stripExecBanner,
|
|
192
201
|
tailPeek,
|
|
@@ -196,7 +205,7 @@ import {
|
|
|
196
205
|
useThemeType,
|
|
197
206
|
userMessageLikelyNeedsDisclosure,
|
|
198
207
|
v4aToGitFileDiff
|
|
199
|
-
} from "./chunk-
|
|
208
|
+
} from "./chunk-5RZN3DD5.js";
|
|
200
209
|
import {
|
|
201
210
|
buildTimeline,
|
|
202
211
|
creditExhaustedFromEvents,
|
|
@@ -204,7 +213,7 @@ import {
|
|
|
204
213
|
groupTimeline,
|
|
205
214
|
sessionStatusFromEvents,
|
|
206
215
|
toolDisplayName
|
|
207
|
-
} from "./chunk-
|
|
216
|
+
} from "./chunk-XSA2QAKP.js";
|
|
208
217
|
import {
|
|
209
218
|
TimelineAnnotationsChip
|
|
210
219
|
} from "./chunk-WSK7G5LE.js";
|
|
@@ -220,15 +229,17 @@ import {
|
|
|
220
229
|
} from "./chunk-VZTQ73XT.js";
|
|
221
230
|
import {
|
|
222
231
|
DesktopViewer,
|
|
232
|
+
useDesktopStream,
|
|
233
|
+
useRelayFrameStream
|
|
234
|
+
} from "./chunk-4MDLEHXL.js";
|
|
235
|
+
import {
|
|
223
236
|
STREAM_KIND_PTY,
|
|
224
237
|
STREAM_ROLE_CLIENT,
|
|
225
238
|
decodeStreamFrame,
|
|
226
239
|
decodeStreamOpenAck,
|
|
227
240
|
encodeStreamFrame,
|
|
228
|
-
encodeStreamOpen
|
|
229
|
-
|
|
230
|
-
useRelayFrameStream
|
|
231
|
-
} from "./chunk-R3AZOAB5.js";
|
|
241
|
+
encodeStreamOpen
|
|
242
|
+
} from "./chunk-5YK2ZDIG.js";
|
|
232
243
|
import {
|
|
233
244
|
useMutationRunner,
|
|
234
245
|
usePageLiveActivity,
|
|
@@ -238,7 +249,7 @@ import {
|
|
|
238
249
|
import {
|
|
239
250
|
useOpenGeni,
|
|
240
251
|
useOpenGeniClient
|
|
241
|
-
} from "./chunk-
|
|
252
|
+
} from "./chunk-NEXLLTP4.js";
|
|
242
253
|
import {
|
|
243
254
|
COMPOSER_PAYMENT_REQUIRED_MESSAGE,
|
|
244
255
|
CREDIT_EXHAUSTION_MESSAGE,
|
|
@@ -289,7 +300,7 @@ function policyFromTurn(turn) {
|
|
|
289
300
|
return {
|
|
290
301
|
model: turn.model,
|
|
291
302
|
reasoningEffort: turn.reasoningEffort,
|
|
292
|
-
latencyMode: turn.latencyMode
|
|
303
|
+
latencyMode: turn.latencyMode,
|
|
293
304
|
turnId: turn.id
|
|
294
305
|
};
|
|
295
306
|
}
|
|
@@ -361,6 +372,7 @@ function useWorkspaceSessions(options = {}) {
|
|
|
361
372
|
const cursor = options.cursor;
|
|
362
373
|
const search = options.search;
|
|
363
374
|
const pinsOnly = options.pinsOnly;
|
|
375
|
+
const archivedOnly = options.archivedOnly;
|
|
364
376
|
const enabled = options.enabled ?? true;
|
|
365
377
|
const queryKey = [
|
|
366
378
|
workspaceId,
|
|
@@ -368,7 +380,8 @@ function useWorkspaceSessions(options = {}) {
|
|
|
368
380
|
parentSessionId === null ? "null" : parentSessionId ?? "",
|
|
369
381
|
cursor ?? "",
|
|
370
382
|
search ?? "",
|
|
371
|
-
pinsOnly ? "1" : ""
|
|
383
|
+
pinsOnly ? "1" : "",
|
|
384
|
+
archivedOnly ? "archived" : "active"
|
|
372
385
|
].join("\0");
|
|
373
386
|
const previousQueryKey = useRef(queryKey);
|
|
374
387
|
const queryKeyTransition = previousQueryKey.current !== queryKey;
|
|
@@ -383,10 +396,11 @@ function useWorkspaceSessions(options = {}) {
|
|
|
383
396
|
...parentSessionId !== void 0 ? { parentSessionId } : {},
|
|
384
397
|
...cursor !== void 0 ? { cursor } : {},
|
|
385
398
|
...search !== void 0 ? { search } : {},
|
|
386
|
-
...pinsOnly ? { pinsOnly: true } : {}
|
|
399
|
+
...pinsOnly ? { pinsOnly: true } : {},
|
|
400
|
+
...archivedOnly ? { archivedOnly: true } : {}
|
|
387
401
|
})
|
|
388
402
|
}),
|
|
389
|
-
[client, workspaceId, limit, parentSessionId, cursor, search, pinsOnly, queryKey]
|
|
403
|
+
[client, workspaceId, limit, parentSessionId, cursor, search, pinsOnly, archivedOnly, queryKey]
|
|
390
404
|
);
|
|
391
405
|
const state = usePolledValue(load, {
|
|
392
406
|
pollIntervalMs: options.pollIntervalMs,
|
|
@@ -413,11 +427,48 @@ function useWorkspaceSessions(options = {}) {
|
|
|
413
427
|
};
|
|
414
428
|
}
|
|
415
429
|
|
|
430
|
+
// src/hooks/use-session-background-commands.ts
|
|
431
|
+
import { useCallback as useCallback4 } from "react";
|
|
432
|
+
function useSessionBackgroundCommands(sessionId, options = {}) {
|
|
433
|
+
const { client, workspaceId } = useOpenGeni(options);
|
|
434
|
+
const enabled = (options.enabled ?? true) && Boolean(sessionId);
|
|
435
|
+
const load = useCallback4(async () => {
|
|
436
|
+
if (!sessionId) return { commands: [] };
|
|
437
|
+
if (!client.listSessionBackgroundCommands) {
|
|
438
|
+
throw new Error("The configured OpenGeni client does not support background commands");
|
|
439
|
+
}
|
|
440
|
+
return await client.listSessionBackgroundCommands(workspaceId, sessionId);
|
|
441
|
+
}, [client, workspaceId, sessionId]);
|
|
442
|
+
const state = usePolledValue(load, {
|
|
443
|
+
enabled,
|
|
444
|
+
pollIntervalMs: options.pollIntervalMs
|
|
445
|
+
});
|
|
446
|
+
const refresh = state.refresh;
|
|
447
|
+
const cancel = useCallback4(
|
|
448
|
+
async (commandId) => {
|
|
449
|
+
if (!sessionId) return;
|
|
450
|
+
if (!client.cancelSessionBackgroundCommand) {
|
|
451
|
+
throw new Error("The configured OpenGeni client does not support background commands");
|
|
452
|
+
}
|
|
453
|
+
await client.cancelSessionBackgroundCommand(workspaceId, sessionId, commandId);
|
|
454
|
+
await refresh();
|
|
455
|
+
},
|
|
456
|
+
[client, workspaceId, sessionId, refresh]
|
|
457
|
+
);
|
|
458
|
+
return {
|
|
459
|
+
commands: state.data?.commands ?? [],
|
|
460
|
+
loading: state.loading,
|
|
461
|
+
error: state.error,
|
|
462
|
+
refresh,
|
|
463
|
+
cancel
|
|
464
|
+
};
|
|
465
|
+
}
|
|
466
|
+
|
|
416
467
|
// src/hooks/use-environments.ts
|
|
417
|
-
import { useCallback as
|
|
468
|
+
import { useCallback as useCallback5, useMemo as useMemo2 } from "react";
|
|
418
469
|
function useVariableSets(options = {}) {
|
|
419
470
|
const { client, workspaceId } = useOpenGeni(options);
|
|
420
|
-
const load =
|
|
471
|
+
const load = useCallback5(
|
|
421
472
|
async () => await client.listVariableSets(workspaceId),
|
|
422
473
|
[client, workspaceId]
|
|
423
474
|
);
|
|
@@ -426,7 +477,7 @@ function useVariableSets(options = {}) {
|
|
|
426
477
|
enabled: options.enabled
|
|
427
478
|
});
|
|
428
479
|
const { run, mutating, mutationError, clearMutationError } = useMutationRunner();
|
|
429
|
-
const create =
|
|
480
|
+
const create = useCallback5(
|
|
430
481
|
async (request) => {
|
|
431
482
|
const result = await run(() => client.createVariableSet(workspaceId, request));
|
|
432
483
|
if (result) {
|
|
@@ -436,7 +487,7 @@ function useVariableSets(options = {}) {
|
|
|
436
487
|
},
|
|
437
488
|
[client, workspaceId, run, refresh]
|
|
438
489
|
);
|
|
439
|
-
const update =
|
|
490
|
+
const update = useCallback5(
|
|
440
491
|
async (variableSetId, request) => {
|
|
441
492
|
const result = await run(() => client.updateVariableSet(workspaceId, variableSetId, request));
|
|
442
493
|
if (result) {
|
|
@@ -446,7 +497,7 @@ function useVariableSets(options = {}) {
|
|
|
446
497
|
},
|
|
447
498
|
[client, workspaceId, run, refresh]
|
|
448
499
|
);
|
|
449
|
-
const remove =
|
|
500
|
+
const remove = useCallback5(
|
|
450
501
|
async (variableSetId) => {
|
|
451
502
|
const result = await run(async () => {
|
|
452
503
|
await client.deleteVariableSet(workspaceId, variableSetId);
|
|
@@ -459,11 +510,11 @@ function useVariableSets(options = {}) {
|
|
|
459
510
|
},
|
|
460
511
|
[client, workspaceId, run, refresh]
|
|
461
512
|
);
|
|
462
|
-
const readVariable =
|
|
513
|
+
const readVariable = useCallback5(
|
|
463
514
|
async (variableSetId, name) => await run(() => client.getVariableSetVariable(workspaceId, variableSetId, name)),
|
|
464
515
|
[client, workspaceId, run]
|
|
465
516
|
);
|
|
466
|
-
const setVariable =
|
|
517
|
+
const setVariable = useCallback5(
|
|
467
518
|
async (variableSetId, name, value) => {
|
|
468
519
|
const result = await run(
|
|
469
520
|
() => client.setVariableSetVariable(workspaceId, variableSetId, name, value)
|
|
@@ -475,7 +526,7 @@ function useVariableSets(options = {}) {
|
|
|
475
526
|
},
|
|
476
527
|
[client, workspaceId, run, refresh]
|
|
477
528
|
);
|
|
478
|
-
const deleteVariable =
|
|
529
|
+
const deleteVariable = useCallback5(
|
|
479
530
|
async (variableSetId, name) => {
|
|
480
531
|
const result = await run(async () => {
|
|
481
532
|
await client.deleteVariableSetVariable(workspaceId, variableSetId, name);
|
|
@@ -527,29 +578,105 @@ function useEnvironments(options = {}) {
|
|
|
527
578
|
}
|
|
528
579
|
|
|
529
580
|
// src/hooks/use-channels.ts
|
|
530
|
-
import { useCallback as
|
|
581
|
+
import { useCallback as useCallback6, useMemo as useMemo3, useSyncExternalStore } from "react";
|
|
582
|
+
|
|
583
|
+
// src/hooks/channel-cache.ts
|
|
584
|
+
var EMPTY_CHANNELS = [];
|
|
585
|
+
var EMPTY_SNAPSHOT = {
|
|
586
|
+
channels: EMPTY_CHANNELS,
|
|
587
|
+
ready: false
|
|
588
|
+
};
|
|
589
|
+
var channelCaches = /* @__PURE__ */ new WeakMap();
|
|
590
|
+
function compareChannels(left, right) {
|
|
591
|
+
return Number(right.pinned) - Number(left.pinned) || left.sortOrder - right.sortOrder || left.id.localeCompare(right.id);
|
|
592
|
+
}
|
|
593
|
+
function orderedChannels(channels) {
|
|
594
|
+
return [...channels].sort(compareChannels);
|
|
595
|
+
}
|
|
596
|
+
function upsertChannel(channels, channel) {
|
|
597
|
+
const next = channels.filter((candidate) => candidate.id !== channel.id);
|
|
598
|
+
next.push(channel);
|
|
599
|
+
return orderedChannels(next);
|
|
600
|
+
}
|
|
601
|
+
function createChannelCacheStore() {
|
|
602
|
+
let base = null;
|
|
603
|
+
let snapshot = EMPTY_SNAPSHOT;
|
|
604
|
+
let nextLoadTicket = 0;
|
|
605
|
+
let acceptedLoadTicket = 0;
|
|
606
|
+
const pending = /* @__PURE__ */ new Map();
|
|
607
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
608
|
+
const publish = () => {
|
|
609
|
+
let channels = base ?? EMPTY_CHANNELS;
|
|
610
|
+
for (const { channel } of pending.values()) {
|
|
611
|
+
channels = upsertChannel(channels, channel);
|
|
612
|
+
}
|
|
613
|
+
snapshot = { channels, ready: base !== null || pending.size > 0 };
|
|
614
|
+
for (const listener of listeners) listener();
|
|
615
|
+
};
|
|
616
|
+
return {
|
|
617
|
+
getSnapshot: () => snapshot,
|
|
618
|
+
subscribe: (listener) => {
|
|
619
|
+
listeners.add(listener);
|
|
620
|
+
return () => listeners.delete(listener);
|
|
621
|
+
},
|
|
622
|
+
beginLoad: () => ++nextLoadTicket,
|
|
623
|
+
acceptLoad: (ticket, channels) => {
|
|
624
|
+
if (ticket < acceptedLoadTicket) return;
|
|
625
|
+
acceptedLoadTicket = ticket;
|
|
626
|
+
base = orderedChannels(channels);
|
|
627
|
+
for (const [id, created] of pending) {
|
|
628
|
+
if (ticket > created.committedAfterLoad) pending.delete(id);
|
|
629
|
+
}
|
|
630
|
+
publish();
|
|
631
|
+
},
|
|
632
|
+
publishCreated: (channel) => {
|
|
633
|
+
pending.set(channel.id, { channel, committedAfterLoad: nextLoadTicket });
|
|
634
|
+
publish();
|
|
635
|
+
}
|
|
636
|
+
};
|
|
637
|
+
}
|
|
638
|
+
function channelCacheFor(client, workspaceId) {
|
|
639
|
+
let byWorkspace = channelCaches.get(client);
|
|
640
|
+
if (!byWorkspace) {
|
|
641
|
+
byWorkspace = /* @__PURE__ */ new Map();
|
|
642
|
+
channelCaches.set(client, byWorkspace);
|
|
643
|
+
}
|
|
644
|
+
let cache = byWorkspace.get(workspaceId);
|
|
645
|
+
if (!cache) {
|
|
646
|
+
cache = createChannelCacheStore();
|
|
647
|
+
byWorkspace.set(workspaceId, cache);
|
|
648
|
+
}
|
|
649
|
+
return cache;
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
// src/hooks/use-channels.ts
|
|
531
653
|
function useChannels(options = {}) {
|
|
532
654
|
const { client, workspaceId } = useOpenGeni(options);
|
|
533
|
-
const
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
655
|
+
const cache = useMemo3(() => channelCacheFor(client, workspaceId), [client, workspaceId]);
|
|
656
|
+
const snapshot = useSyncExternalStore(cache.subscribe, cache.getSnapshot, cache.getSnapshot);
|
|
657
|
+
const load = useCallback6(async () => {
|
|
658
|
+
const ticket = cache.beginLoad();
|
|
659
|
+
const channels = await client.listChannels(workspaceId);
|
|
660
|
+
cache.acceptLoad(ticket, channels);
|
|
661
|
+
return channels;
|
|
662
|
+
}, [cache, client, workspaceId]);
|
|
663
|
+
const { loading, error, refresh } = usePolledValue(load, {
|
|
538
664
|
pollIntervalMs: options.pollIntervalMs,
|
|
539
665
|
enabled: options.enabled
|
|
540
666
|
});
|
|
541
|
-
const { run, mutating, mutationError, clearMutationError } = useMutationRunner();
|
|
542
|
-
const create =
|
|
667
|
+
const { run, mutating, mutationError, clearMutationError } = useMutationRunner(cache);
|
|
668
|
+
const create = useCallback6(
|
|
543
669
|
async (request) => {
|
|
544
670
|
const result = await run(() => client.createChannel(workspaceId, request));
|
|
545
671
|
if (result) {
|
|
672
|
+
cache.publishCreated(result);
|
|
546
673
|
await refresh();
|
|
547
674
|
}
|
|
548
675
|
return result;
|
|
549
676
|
},
|
|
550
|
-
[client, workspaceId, run, refresh]
|
|
677
|
+
[cache, client, workspaceId, run, refresh]
|
|
551
678
|
);
|
|
552
|
-
const update =
|
|
679
|
+
const update = useCallback6(
|
|
553
680
|
async (channelId, request) => {
|
|
554
681
|
const result = await run(() => client.updateChannel(workspaceId, channelId, request));
|
|
555
682
|
if (result) {
|
|
@@ -559,7 +686,7 @@ function useChannels(options = {}) {
|
|
|
559
686
|
},
|
|
560
687
|
[client, workspaceId, run, refresh]
|
|
561
688
|
);
|
|
562
|
-
const remove =
|
|
689
|
+
const remove = useCallback6(
|
|
563
690
|
async (channelId) => {
|
|
564
691
|
const result = await run(async () => {
|
|
565
692
|
await client.deleteChannel(workspaceId, channelId);
|
|
@@ -572,19 +699,30 @@ function useChannels(options = {}) {
|
|
|
572
699
|
},
|
|
573
700
|
[client, workspaceId, run, refresh]
|
|
574
701
|
);
|
|
575
|
-
const
|
|
702
|
+
const reorder = useCallback6(
|
|
703
|
+
async (channelIds) => {
|
|
704
|
+
const result = await run(() => client.reorderChannels(workspaceId, { channelIds }));
|
|
705
|
+
if (result) await refresh();
|
|
706
|
+
return result;
|
|
707
|
+
},
|
|
708
|
+
[client, workspaceId, run, refresh]
|
|
709
|
+
);
|
|
710
|
+
const moveSession = useCallback6(
|
|
576
711
|
async (sessionId, channelId) => {
|
|
577
712
|
return await run(() => client.updateSessionChannel(workspaceId, sessionId, { channelId }));
|
|
578
713
|
},
|
|
579
714
|
[client, workspaceId, run]
|
|
580
715
|
);
|
|
581
716
|
return {
|
|
582
|
-
|
|
583
|
-
|
|
717
|
+
// Cache snapshots replace arrays instead of mutating them, so preserve the
|
|
718
|
+
// stable reference expected from the previous polled-value implementation.
|
|
719
|
+
channels: snapshot.channels,
|
|
720
|
+
loading: loading && !snapshot.ready,
|
|
584
721
|
error,
|
|
585
722
|
refresh,
|
|
586
723
|
create,
|
|
587
724
|
update,
|
|
725
|
+
reorder,
|
|
588
726
|
remove,
|
|
589
727
|
moveSession,
|
|
590
728
|
mutating,
|
|
@@ -594,16 +732,16 @@ function useChannels(options = {}) {
|
|
|
594
732
|
}
|
|
595
733
|
|
|
596
734
|
// src/hooks/use-rigs.ts
|
|
597
|
-
import { useCallback as
|
|
735
|
+
import { useCallback as useCallback7 } from "react";
|
|
598
736
|
function useRigs(options = {}) {
|
|
599
737
|
const { client, workspaceId } = useOpenGeni(options);
|
|
600
|
-
const load =
|
|
738
|
+
const load = useCallback7(async () => await client.listRigs(workspaceId), [client, workspaceId]);
|
|
601
739
|
const { data, loading, error, refresh } = usePolledValue(load, {
|
|
602
740
|
pollIntervalMs: options.pollIntervalMs,
|
|
603
741
|
enabled: options.enabled
|
|
604
742
|
});
|
|
605
743
|
const { run, mutating, mutationError, clearMutationError } = useMutationRunner();
|
|
606
|
-
const create =
|
|
744
|
+
const create = useCallback7(
|
|
607
745
|
async (request) => {
|
|
608
746
|
const result = await run(() => client.createRig(workspaceId, request));
|
|
609
747
|
if (result) {
|
|
@@ -613,7 +751,7 @@ function useRigs(options = {}) {
|
|
|
613
751
|
},
|
|
614
752
|
[client, workspaceId, run, refresh]
|
|
615
753
|
);
|
|
616
|
-
const update =
|
|
754
|
+
const update = useCallback7(
|
|
617
755
|
async (rigId, request) => {
|
|
618
756
|
const result = await run(() => client.updateRig(workspaceId, rigId, request));
|
|
619
757
|
if (result) {
|
|
@@ -623,7 +761,7 @@ function useRigs(options = {}) {
|
|
|
623
761
|
},
|
|
624
762
|
[client, workspaceId, run, refresh]
|
|
625
763
|
);
|
|
626
|
-
const remove =
|
|
764
|
+
const remove = useCallback7(
|
|
627
765
|
async (rigId) => {
|
|
628
766
|
const result = await run(async () => {
|
|
629
767
|
await client.deleteRig(workspaceId, rigId);
|
|
@@ -636,13 +774,13 @@ function useRigs(options = {}) {
|
|
|
636
774
|
},
|
|
637
775
|
[client, workspaceId, run, refresh]
|
|
638
776
|
);
|
|
639
|
-
const listVersions =
|
|
777
|
+
const listVersions = useCallback7(
|
|
640
778
|
async (rigId) => {
|
|
641
779
|
return await run(() => client.listRigVersions(workspaceId, rigId));
|
|
642
780
|
},
|
|
643
781
|
[client, workspaceId, run]
|
|
644
782
|
);
|
|
645
|
-
const activateVersion =
|
|
783
|
+
const activateVersion = useCallback7(
|
|
646
784
|
async (rigId, versionId) => {
|
|
647
785
|
const result = await run(() => client.activateRigVersion(workspaceId, rigId, versionId));
|
|
648
786
|
if (result) {
|
|
@@ -652,13 +790,13 @@ function useRigs(options = {}) {
|
|
|
652
790
|
},
|
|
653
791
|
[client, workspaceId, run, refresh]
|
|
654
792
|
);
|
|
655
|
-
const listChanges =
|
|
793
|
+
const listChanges = useCallback7(
|
|
656
794
|
async (rigId) => {
|
|
657
795
|
return await run(() => client.listRigChanges(workspaceId, rigId));
|
|
658
796
|
},
|
|
659
797
|
[client, workspaceId, run]
|
|
660
798
|
);
|
|
661
|
-
const proposeChange =
|
|
799
|
+
const proposeChange = useCallback7(
|
|
662
800
|
async (rigId, request) => {
|
|
663
801
|
const result = await run(() => client.proposeRigChange(workspaceId, rigId, request));
|
|
664
802
|
if (result) {
|
|
@@ -687,7 +825,7 @@ function useRigs(options = {}) {
|
|
|
687
825
|
}
|
|
688
826
|
function useRig(rigId, options = {}) {
|
|
689
827
|
const { client, workspaceId } = useOpenGeni(options);
|
|
690
|
-
const load =
|
|
828
|
+
const load = useCallback7(
|
|
691
829
|
async () => await client.getRig(workspaceId, rigId),
|
|
692
830
|
[client, workspaceId, rigId]
|
|
693
831
|
);
|
|
@@ -696,7 +834,7 @@ function useRig(rigId, options = {}) {
|
|
|
696
834
|
enabled: options.enabled
|
|
697
835
|
});
|
|
698
836
|
const { run, mutating, mutationError, clearMutationError } = useMutationRunner();
|
|
699
|
-
const update =
|
|
837
|
+
const update = useCallback7(
|
|
700
838
|
async (request) => {
|
|
701
839
|
const result = await run(() => client.updateRig(workspaceId, rigId, request));
|
|
702
840
|
if (result) {
|
|
@@ -706,14 +844,14 @@ function useRig(rigId, options = {}) {
|
|
|
706
844
|
},
|
|
707
845
|
[client, workspaceId, rigId, run, refresh]
|
|
708
846
|
);
|
|
709
|
-
const remove =
|
|
847
|
+
const remove = useCallback7(async () => {
|
|
710
848
|
const result = await run(async () => {
|
|
711
849
|
await client.deleteRig(workspaceId, rigId);
|
|
712
850
|
return true;
|
|
713
851
|
});
|
|
714
852
|
return result === true;
|
|
715
853
|
}, [client, workspaceId, rigId, run]);
|
|
716
|
-
const activateVersion =
|
|
854
|
+
const activateVersion = useCallback7(
|
|
717
855
|
async (versionId) => {
|
|
718
856
|
const result = await run(() => client.activateRigVersion(workspaceId, rigId, versionId));
|
|
719
857
|
if (result) {
|
|
@@ -723,7 +861,7 @@ function useRig(rigId, options = {}) {
|
|
|
723
861
|
},
|
|
724
862
|
[client, workspaceId, rigId, run, refresh]
|
|
725
863
|
);
|
|
726
|
-
const proposeChange =
|
|
864
|
+
const proposeChange = useCallback7(
|
|
727
865
|
async (request) => {
|
|
728
866
|
const result = await run(() => client.proposeRigChange(workspaceId, rigId, request));
|
|
729
867
|
if (result) {
|
|
@@ -733,7 +871,7 @@ function useRig(rigId, options = {}) {
|
|
|
733
871
|
},
|
|
734
872
|
[client, workspaceId, rigId, run, refresh]
|
|
735
873
|
);
|
|
736
|
-
const verifyChange =
|
|
874
|
+
const verifyChange = useCallback7(
|
|
737
875
|
async (changeId) => {
|
|
738
876
|
const result = await run(() => client.verifyRigChange(workspaceId, rigId, changeId));
|
|
739
877
|
if (result) {
|
|
@@ -743,7 +881,7 @@ function useRig(rigId, options = {}) {
|
|
|
743
881
|
},
|
|
744
882
|
[client, workspaceId, rigId, run, refresh]
|
|
745
883
|
);
|
|
746
|
-
const promoteChange =
|
|
884
|
+
const promoteChange = useCallback7(
|
|
747
885
|
async (changeId) => {
|
|
748
886
|
const result = await run(() => client.promoteRigChange(workspaceId, rigId, changeId));
|
|
749
887
|
if (result) {
|
|
@@ -753,7 +891,7 @@ function useRig(rigId, options = {}) {
|
|
|
753
891
|
},
|
|
754
892
|
[client, workspaceId, rigId, run, refresh]
|
|
755
893
|
);
|
|
756
|
-
const verify =
|
|
894
|
+
const verify = useCallback7(async () => {
|
|
757
895
|
return await run(() => client.verifyRig(workspaceId, rigId));
|
|
758
896
|
}, [client, workspaceId, rigId, run]);
|
|
759
897
|
return {
|
|
@@ -775,7 +913,7 @@ function useRig(rigId, options = {}) {
|
|
|
775
913
|
}
|
|
776
914
|
function useRigVersions(rigId, options = {}) {
|
|
777
915
|
const { client, workspaceId } = useOpenGeni(options);
|
|
778
|
-
const load =
|
|
916
|
+
const load = useCallback7(
|
|
779
917
|
async () => await client.listRigVersions(workspaceId, rigId),
|
|
780
918
|
[client, workspaceId, rigId]
|
|
781
919
|
);
|
|
@@ -792,7 +930,7 @@ function useRigVersions(rigId, options = {}) {
|
|
|
792
930
|
}
|
|
793
931
|
function useRigChanges(rigId, options = {}) {
|
|
794
932
|
const { client, workspaceId } = useOpenGeni(options);
|
|
795
|
-
const load =
|
|
933
|
+
const load = useCallback7(
|
|
796
934
|
async () => await client.listRigChanges(workspaceId, rigId),
|
|
797
935
|
[client, workspaceId, rigId]
|
|
798
936
|
);
|
|
@@ -809,17 +947,17 @@ function useRigChanges(rigId, options = {}) {
|
|
|
809
947
|
}
|
|
810
948
|
|
|
811
949
|
// src/hooks/use-packs.ts
|
|
812
|
-
import { useCallback as
|
|
950
|
+
import { useCallback as useCallback8 } from "react";
|
|
813
951
|
var EMPTY_INSTALLATIONS = [];
|
|
814
952
|
function usePacks(options = {}) {
|
|
815
953
|
const { client, workspaceId } = useOpenGeni(options);
|
|
816
|
-
const load =
|
|
954
|
+
const load = useCallback8(async () => await client.listPacks(workspaceId), [client, workspaceId]);
|
|
817
955
|
const { data, loading, error, refresh } = usePolledValue(load, {
|
|
818
956
|
pollIntervalMs: options.pollIntervalMs,
|
|
819
957
|
enabled: options.enabled
|
|
820
958
|
});
|
|
821
959
|
const { run, mutating, mutationError, clearMutationError } = useMutationRunner();
|
|
822
|
-
const register =
|
|
960
|
+
const register = useCallback8(
|
|
823
961
|
async (manifest) => {
|
|
824
962
|
const result = await run(() => client.registerPack(workspaceId, manifest));
|
|
825
963
|
if (result) {
|
|
@@ -829,7 +967,7 @@ function usePacks(options = {}) {
|
|
|
829
967
|
},
|
|
830
968
|
[client, workspaceId, run, refresh]
|
|
831
969
|
);
|
|
832
|
-
const enable =
|
|
970
|
+
const enable = useCallback8(
|
|
833
971
|
async (packId, request = {}) => {
|
|
834
972
|
const result = await run(() => client.enablePack(workspaceId, packId, request));
|
|
835
973
|
if (result) {
|
|
@@ -839,11 +977,11 @@ function usePacks(options = {}) {
|
|
|
839
977
|
},
|
|
840
978
|
[client, workspaceId, run, refresh]
|
|
841
979
|
);
|
|
842
|
-
const previewInstallation =
|
|
980
|
+
const previewInstallation = useCallback8(
|
|
843
981
|
async (packId, request = {}) => await run(() => client.previewPackInstallation(workspaceId, packId, request)),
|
|
844
982
|
[client, workspaceId, run]
|
|
845
983
|
);
|
|
846
|
-
const install =
|
|
984
|
+
const install = useCallback8(
|
|
847
985
|
async (packId, request) => {
|
|
848
986
|
const result = await run(() => client.installPack(workspaceId, packId, request));
|
|
849
987
|
if (result) await refresh();
|
|
@@ -851,11 +989,11 @@ function usePacks(options = {}) {
|
|
|
851
989
|
},
|
|
852
990
|
[client, workspaceId, run, refresh]
|
|
853
991
|
);
|
|
854
|
-
const previewUninstall =
|
|
992
|
+
const previewUninstall = useCallback8(
|
|
855
993
|
async (packId) => await run(() => client.previewPackUninstall(workspaceId, packId)),
|
|
856
994
|
[client, workspaceId, run]
|
|
857
995
|
);
|
|
858
|
-
const uninstall =
|
|
996
|
+
const uninstall = useCallback8(
|
|
859
997
|
async (packId, request) => {
|
|
860
998
|
const result = await run(() => client.uninstallPack(workspaceId, packId, request));
|
|
861
999
|
if (result) await refresh();
|
|
@@ -863,7 +1001,7 @@ function usePacks(options = {}) {
|
|
|
863
1001
|
},
|
|
864
1002
|
[client, workspaceId, run, refresh]
|
|
865
1003
|
);
|
|
866
|
-
const remove =
|
|
1004
|
+
const remove = useCallback8(
|
|
867
1005
|
async (packId) => {
|
|
868
1006
|
const result = await run(async () => {
|
|
869
1007
|
await client.deletePack(workspaceId, packId);
|
|
@@ -877,7 +1015,7 @@ function usePacks(options = {}) {
|
|
|
877
1015
|
[client, workspaceId, run, refresh]
|
|
878
1016
|
);
|
|
879
1017
|
const installations = data?.installations ?? EMPTY_INSTALLATIONS;
|
|
880
|
-
const installationFor =
|
|
1018
|
+
const installationFor = useCallback8(
|
|
881
1019
|
(packId) => installations.find((installation) => installation.packId === packId) ?? null,
|
|
882
1020
|
[installations]
|
|
883
1021
|
);
|
|
@@ -902,16 +1040,16 @@ function usePacks(options = {}) {
|
|
|
902
1040
|
}
|
|
903
1041
|
|
|
904
1042
|
// src/hooks/use-workspaces.ts
|
|
905
|
-
import { useCallback as
|
|
1043
|
+
import { useCallback as useCallback9 } from "react";
|
|
906
1044
|
function useWorkspaces(options = {}) {
|
|
907
1045
|
const client = useOpenGeniClient(options);
|
|
908
|
-
const load =
|
|
1046
|
+
const load = useCallback9(async () => await client.listWorkspaces(), [client]);
|
|
909
1047
|
const { data, loading, error, refresh } = usePolledValue(load, {
|
|
910
1048
|
pollIntervalMs: options.pollIntervalMs,
|
|
911
1049
|
enabled: options.enabled
|
|
912
1050
|
});
|
|
913
1051
|
const { run, mutating, mutationError, clearMutationError } = useMutationRunner();
|
|
914
|
-
const create =
|
|
1052
|
+
const create = useCallback9(
|
|
915
1053
|
async (request) => {
|
|
916
1054
|
const result = await run(() => client.createWorkspace(request));
|
|
917
1055
|
if (result) {
|
|
@@ -921,7 +1059,7 @@ function useWorkspaces(options = {}) {
|
|
|
921
1059
|
},
|
|
922
1060
|
[client, run, refresh]
|
|
923
1061
|
);
|
|
924
|
-
const update =
|
|
1062
|
+
const update = useCallback9(
|
|
925
1063
|
async (workspaceId, request) => {
|
|
926
1064
|
const result = await run(() => client.updateWorkspace(workspaceId, request));
|
|
927
1065
|
if (result) {
|
|
@@ -945,12 +1083,12 @@ function useWorkspaces(options = {}) {
|
|
|
945
1083
|
}
|
|
946
1084
|
|
|
947
1085
|
// src/hooks/use-billing-usage.ts
|
|
948
|
-
import { useCallback as
|
|
1086
|
+
import { useCallback as useCallback10 } from "react";
|
|
949
1087
|
function useBillingUsage(options = {}) {
|
|
950
1088
|
const client = useOpenGeniClient(options);
|
|
951
1089
|
const accountId = options.accountId;
|
|
952
1090
|
const workspaceId = options.workspaceId;
|
|
953
|
-
const load =
|
|
1091
|
+
const load = useCallback10(
|
|
954
1092
|
async () => await client.getBillingUsage({
|
|
955
1093
|
...accountId !== void 0 ? { accountId } : {},
|
|
956
1094
|
...workspaceId !== void 0 ? { workspaceId } : {}
|
|
@@ -971,10 +1109,10 @@ function useBillingUsage(options = {}) {
|
|
|
971
1109
|
}
|
|
972
1110
|
|
|
973
1111
|
// src/hooks/use-available-models.ts
|
|
974
|
-
import { useCallback as
|
|
1112
|
+
import { useCallback as useCallback11 } from "react";
|
|
975
1113
|
function useAvailableModels(options = {}) {
|
|
976
1114
|
const client = useOpenGeniClient(options);
|
|
977
|
-
const load =
|
|
1115
|
+
const load = useCallback11(async () => await client.getClientConfig(), [client]);
|
|
978
1116
|
const state = usePolledValue(load, {
|
|
979
1117
|
pollIntervalMs: options.pollIntervalMs,
|
|
980
1118
|
enabled: options.enabled
|
|
@@ -989,7 +1127,7 @@ function useAvailableModels(options = {}) {
|
|
|
989
1127
|
}
|
|
990
1128
|
function useWorkspaceModelCatalog(options) {
|
|
991
1129
|
const client = useOpenGeniClient(options);
|
|
992
|
-
const load =
|
|
1130
|
+
const load = useCallback11(async () => {
|
|
993
1131
|
if (!options.workspaceId) {
|
|
994
1132
|
return { models: [], defaultModel: null };
|
|
995
1133
|
}
|
|
@@ -1022,7 +1160,7 @@ import {
|
|
|
1022
1160
|
OpenGeniApiError,
|
|
1023
1161
|
applyUrlRotation
|
|
1024
1162
|
} from "@opengeni/sdk";
|
|
1025
|
-
import { useCallback as
|
|
1163
|
+
import { useCallback as useCallback12, useEffect as useEffect3, useRef as useRef2, useState } from "react";
|
|
1026
1164
|
|
|
1027
1165
|
// src/lib/sandbox-liveness.ts
|
|
1028
1166
|
function sandboxAcceptsLiveIo(liveness) {
|
|
@@ -1072,7 +1210,7 @@ function useSessionCapabilities(sessionId, options = {}) {
|
|
|
1072
1210
|
const epochRef = useRef2(0);
|
|
1073
1211
|
const viewerIdRef = useRef2(null);
|
|
1074
1212
|
const previousIdentityRef = useRef2(identityKey);
|
|
1075
|
-
const renegotiate =
|
|
1213
|
+
const renegotiate = useCallback12(() => {
|
|
1076
1214
|
setNonce((n) => n + 1);
|
|
1077
1215
|
}, []);
|
|
1078
1216
|
useEffect3(() => {
|
|
@@ -1158,6 +1296,11 @@ function useSessionCapabilities(sessionId, options = {}) {
|
|
|
1158
1296
|
pollUntilWarm();
|
|
1159
1297
|
}).catch((cause) => {
|
|
1160
1298
|
if (!cancelled) {
|
|
1299
|
+
if (isSandboxOwnershipDisabledError(cause)) {
|
|
1300
|
+
setState("on-demand");
|
|
1301
|
+
setError(null);
|
|
1302
|
+
return;
|
|
1303
|
+
}
|
|
1161
1304
|
setState("error");
|
|
1162
1305
|
setError(cause instanceof Error ? cause : new Error(String(cause)));
|
|
1163
1306
|
}
|
|
@@ -1260,6 +1403,11 @@ function useSessionCapabilities(sessionId, options = {}) {
|
|
|
1260
1403
|
}
|
|
1261
1404
|
} catch (cause) {
|
|
1262
1405
|
if (cancelled) return;
|
|
1406
|
+
if (isSandboxOwnershipDisabledError(cause)) {
|
|
1407
|
+
setState("on-demand");
|
|
1408
|
+
setError(null);
|
|
1409
|
+
return;
|
|
1410
|
+
}
|
|
1263
1411
|
if (cause instanceof OpenGeniApiError && cause.status === 403) {
|
|
1264
1412
|
setState("error");
|
|
1265
1413
|
setError(new Error("not permitted to view this session's sandbox"));
|
|
@@ -1324,6 +1472,9 @@ function useSessionCapabilities(sessionId, options = {}) {
|
|
|
1324
1472
|
renegotiate
|
|
1325
1473
|
};
|
|
1326
1474
|
}
|
|
1475
|
+
function isSandboxOwnershipDisabledError(error) {
|
|
1476
|
+
return error instanceof OpenGeniApiError && error.status === 404 && error.message.includes("sandbox ownership is not enabled");
|
|
1477
|
+
}
|
|
1327
1478
|
|
|
1328
1479
|
// src/hooks/use-terminal-stream.ts
|
|
1329
1480
|
import {
|
|
@@ -1334,7 +1485,7 @@ import {
|
|
|
1334
1485
|
ttydResizeFrame,
|
|
1335
1486
|
TtydServerCommand
|
|
1336
1487
|
} from "@opengeni/sdk";
|
|
1337
|
-
import { useCallback as
|
|
1488
|
+
import { useCallback as useCallback13, useEffect as useEffect4, useMemo as useMemo4, useRef as useRef3, useState as useState2 } from "react";
|
|
1338
1489
|
var MAX_PENDING_TERMINAL_INPUT_CODE_UNITS = 64 * 1024;
|
|
1339
1490
|
var TERMINAL_CONNECT_EXPIRY_SKEW_MS = 5e3;
|
|
1340
1491
|
var TERMINAL_RECONNECT_INITIAL_MS = 100;
|
|
@@ -1647,7 +1798,7 @@ function useTerminalStream(options) {
|
|
|
1647
1798
|
closeSocket(socket);
|
|
1648
1799
|
};
|
|
1649
1800
|
}, [transport, url, token, expiresAt, reconnectGeneration]);
|
|
1650
|
-
const write =
|
|
1801
|
+
const write = useCallback13((data) => {
|
|
1651
1802
|
const currentTransport = transportRef.current;
|
|
1652
1803
|
const ws = wsRef.current;
|
|
1653
1804
|
if (ws && ws.readyState === WebSocket.OPEN && socketAuthenticatedRef.current) {
|
|
@@ -1675,7 +1826,7 @@ function useTerminalStream(options) {
|
|
|
1675
1826
|
pendingInputRef.current += data;
|
|
1676
1827
|
}
|
|
1677
1828
|
}, []);
|
|
1678
|
-
const resize =
|
|
1829
|
+
const resize = useCallback13((cols, rows) => {
|
|
1679
1830
|
if (cols <= 0 || rows <= 0) return;
|
|
1680
1831
|
sizeRef.current = { cols, rows };
|
|
1681
1832
|
const ws = wsRef.current;
|
|
@@ -1686,7 +1837,7 @@ function useTerminalStream(options) {
|
|
|
1686
1837
|
}
|
|
1687
1838
|
}
|
|
1688
1839
|
}, []);
|
|
1689
|
-
const disconnect =
|
|
1840
|
+
const disconnect = useCallback13(() => {
|
|
1690
1841
|
const ws = wsRef.current;
|
|
1691
1842
|
wsRef.current = null;
|
|
1692
1843
|
pendingInputRef.current = "";
|
|
@@ -1697,14 +1848,14 @@ function useTerminalStream(options) {
|
|
|
1697
1848
|
if (ws) intentionalCloseRef.current.add(ws);
|
|
1698
1849
|
closeSocket(ws);
|
|
1699
1850
|
}, []);
|
|
1700
|
-
return
|
|
1851
|
+
return useMemo4(() => {
|
|
1701
1852
|
return { connected: status === "open", status, write, resize, disconnect };
|
|
1702
1853
|
}, [status, write, resize, disconnect]);
|
|
1703
1854
|
}
|
|
1704
1855
|
|
|
1705
1856
|
// src/hooks/use-sandbox-terminal.ts
|
|
1706
1857
|
import { OpenGeniApiError as OpenGeniApiError2 } from "@opengeni/sdk";
|
|
1707
|
-
import { useCallback as
|
|
1858
|
+
import { useCallback as useCallback14, useEffect as useEffect5, useMemo as useMemo5, useRef as useRef4, useState as useState3 } from "react";
|
|
1708
1859
|
function useSandboxTerminal(sessionId, options) {
|
|
1709
1860
|
const { client, workspaceId } = useOpenGeni(options);
|
|
1710
1861
|
const includeAgentFirehose = options.includeAgentFirehose ?? true;
|
|
@@ -1755,7 +1906,7 @@ function useSandboxTerminal(sessionId, options) {
|
|
|
1755
1906
|
});
|
|
1756
1907
|
};
|
|
1757
1908
|
}, [interactive, client, workspaceId, sessionId, ptyFilter, boxWarm, reopenNonce]);
|
|
1758
|
-
const { chunks, openPty, supportsInput } =
|
|
1909
|
+
const { chunks, openPty, supportsInput } = useMemo5(() => {
|
|
1759
1910
|
const out = [];
|
|
1760
1911
|
const open = /* @__PURE__ */ new Map();
|
|
1761
1912
|
let lastOpened = null;
|
|
@@ -1808,7 +1959,7 @@ function useSandboxTerminal(sessionId, options) {
|
|
|
1808
1959
|
const identityMatches = stateIdentity === identityKey;
|
|
1809
1960
|
const activePtyId = identityMatches ? openedPtyId ?? openPty : null;
|
|
1810
1961
|
const canWrite = openedPtyId !== null || supportsInput;
|
|
1811
|
-
const write =
|
|
1962
|
+
const write = useMemo5(() => {
|
|
1812
1963
|
if (!activePtyId || !canWrite || !sessionId) return null;
|
|
1813
1964
|
const writeIdentity = identityKey;
|
|
1814
1965
|
return (data) => {
|
|
@@ -1820,7 +1971,7 @@ function useSandboxTerminal(sessionId, options) {
|
|
|
1820
1971
|
});
|
|
1821
1972
|
};
|
|
1822
1973
|
}, [client, workspaceId, sessionId, activePtyId, canWrite, openedPtyId, identityKey]);
|
|
1823
|
-
const close =
|
|
1974
|
+
const close = useCallback14(() => {
|
|
1824
1975
|
if (!activePtyId || !sessionId) return;
|
|
1825
1976
|
void client.terminalPtyClose(workspaceId, sessionId, { ptyId: activePtyId }).catch(() => {
|
|
1826
1977
|
});
|
|
@@ -1836,7 +1987,7 @@ function useSandboxTerminal(sessionId, options) {
|
|
|
1836
1987
|
}
|
|
1837
1988
|
|
|
1838
1989
|
// src/hooks/use-sandbox-files.ts
|
|
1839
|
-
import { useCallback as
|
|
1990
|
+
import { useCallback as useCallback15, useEffect as useEffect6, useMemo as useMemo6, useRef as useRef5, useState as useState4 } from "react";
|
|
1840
1991
|
var CapturedFileUnavailableError = class extends Error {
|
|
1841
1992
|
constructor(path, reason) {
|
|
1842
1993
|
super(
|
|
@@ -1859,12 +2010,19 @@ var FileWriteConflictError = class extends Error {
|
|
|
1859
2010
|
code = "file_write_conflict";
|
|
1860
2011
|
};
|
|
1861
2012
|
function normalizeRoot(root) {
|
|
1862
|
-
const
|
|
2013
|
+
const slashNormalized = root.replaceAll("\\", "/").replace(/^\.\/+/, "");
|
|
2014
|
+
if (slashNormalized === "/") return "/";
|
|
2015
|
+
const normalized = slashNormalized.replace(/\/+$/, "");
|
|
1863
2016
|
return normalized === "." ? "" : normalized;
|
|
1864
2017
|
}
|
|
1865
|
-
function
|
|
2018
|
+
function joinTreeRoot(root, path) {
|
|
2019
|
+
if (!root || path.startsWith("/")) return path;
|
|
2020
|
+
return root === "/" ? `/${path}` : `${root}/${path}`;
|
|
2021
|
+
}
|
|
2022
|
+
function qualifyStatusPath(root, path, treeRoot) {
|
|
1866
2023
|
const normalized = normalizeRoot(root);
|
|
1867
|
-
|
|
2024
|
+
const workspaceRelative = joinTreeRoot(normalized, path);
|
|
2025
|
+
return joinTreeRoot(normalizeRoot(treeRoot), workspaceRelative);
|
|
1868
2026
|
}
|
|
1869
2027
|
function summarizeStatuses(statuses) {
|
|
1870
2028
|
const repositories = statuses.filter(({ status }) => status.isRepo);
|
|
@@ -1881,6 +2039,10 @@ function parentOf(path) {
|
|
|
1881
2039
|
const i = path.lastIndexOf("/");
|
|
1882
2040
|
return i <= 0 ? "" : path.slice(0, i);
|
|
1883
2041
|
}
|
|
2042
|
+
function parentWithinTree(path, rootPath) {
|
|
2043
|
+
const parent = parentOf(path);
|
|
2044
|
+
return parent === "" && normalizeRoot(rootPath) === "/" && path.startsWith("/") ? "/" : parent;
|
|
2045
|
+
}
|
|
1884
2046
|
function leafOf(path) {
|
|
1885
2047
|
const i = path.lastIndexOf("/");
|
|
1886
2048
|
return i < 0 ? path : path.slice(i + 1);
|
|
@@ -1938,37 +2100,29 @@ function findNodeByPath(nodes, path) {
|
|
|
1938
2100
|
}
|
|
1939
2101
|
return void 0;
|
|
1940
2102
|
}
|
|
1941
|
-
|
|
1942
|
-
const paths = [""];
|
|
1943
|
-
const walk = (list) => {
|
|
1944
|
-
for (const node of list) {
|
|
1945
|
-
if (node.kind !== "dir" || node.children === void 0) continue;
|
|
1946
|
-
paths.push(node.path);
|
|
1947
|
-
walk(node.children);
|
|
1948
|
-
}
|
|
1949
|
-
};
|
|
1950
|
-
walk(nodes);
|
|
1951
|
-
return paths;
|
|
1952
|
-
}
|
|
2103
|
+
var FS_LIST_BATCH_REQUEST_LIMIT = 16;
|
|
1953
2104
|
function initialDirectoryFrontier(rootPath, repoPaths) {
|
|
1954
|
-
const
|
|
2105
|
+
const root = normalizeRoot(rootPath);
|
|
2106
|
+
const rootParts = root.split("/").filter(Boolean);
|
|
1955
2107
|
const paths = /* @__PURE__ */ new Set();
|
|
1956
2108
|
for (const repoPath of repoPaths) {
|
|
1957
|
-
const
|
|
2109
|
+
const canonicalRepoPath = joinTreeRoot(root, normalizeRoot(repoPath));
|
|
2110
|
+
const parts = canonicalRepoPath.split("/").filter(Boolean);
|
|
1958
2111
|
if (rootParts.some((part, index) => parts[index] !== part)) continue;
|
|
1959
2112
|
for (let depth = rootParts.length + 1; depth <= parts.length; depth += 1) {
|
|
1960
|
-
|
|
2113
|
+
const path = parts.slice(0, depth).join("/");
|
|
2114
|
+
paths.add(root.startsWith("/") ? `/${path}` : path);
|
|
1961
2115
|
}
|
|
1962
2116
|
}
|
|
1963
2117
|
return [...paths].sort((a, b) => a.split("/").length - b.split("/").length || a.localeCompare(b)).slice(0, 8);
|
|
1964
2118
|
}
|
|
1965
|
-
function parentIsLoaded(nodes, parent) {
|
|
1966
|
-
if (parent ===
|
|
2119
|
+
function parentIsLoaded(nodes, parent, rootPath = "") {
|
|
2120
|
+
if (parent === normalizeRoot(rootPath)) return true;
|
|
1967
2121
|
const node = findNodeByPath(nodes, parent);
|
|
1968
2122
|
return Boolean(node && node.kind === "dir" && node.children !== void 0);
|
|
1969
2123
|
}
|
|
1970
|
-
function insertNode(nodes, parent, child) {
|
|
1971
|
-
if (parent ===
|
|
2124
|
+
function insertNode(nodes, parent, child, rootPath = "") {
|
|
2125
|
+
if (parent === normalizeRoot(rootPath)) {
|
|
1972
2126
|
if (nodes.some((n) => n.path === child.path)) return nodes;
|
|
1973
2127
|
return sortNodes([...nodes, child]);
|
|
1974
2128
|
}
|
|
@@ -1979,14 +2133,14 @@ function insertNode(nodes, parent, child) {
|
|
|
1979
2133
|
return { ...node, children: sortNodes([...node.children, child]) };
|
|
1980
2134
|
}
|
|
1981
2135
|
if (node.kind === "dir" && node.children && parent.startsWith(`${node.path}/`)) {
|
|
1982
|
-
return { ...node, children: insertNode(node.children, parent, child) };
|
|
2136
|
+
return { ...node, children: insertNode(node.children, parent, child, rootPath) };
|
|
1983
2137
|
}
|
|
1984
2138
|
return node;
|
|
1985
2139
|
});
|
|
1986
2140
|
}
|
|
1987
|
-
function removeNode(nodes, path) {
|
|
1988
|
-
const parent =
|
|
1989
|
-
if (parent ===
|
|
2141
|
+
function removeNode(nodes, path, rootPath = "") {
|
|
2142
|
+
const parent = parentWithinTree(path, rootPath);
|
|
2143
|
+
if (parent === normalizeRoot(rootPath)) {
|
|
1990
2144
|
if (!nodes.some((n) => n.path === path)) return nodes;
|
|
1991
2145
|
return nodes.filter((n) => n.path !== path);
|
|
1992
2146
|
}
|
|
@@ -1999,7 +2153,7 @@ function removeNode(nodes, path) {
|
|
|
1999
2153
|
};
|
|
2000
2154
|
}
|
|
2001
2155
|
if (node.kind === "dir" && node.children && parent.startsWith(`${node.path}/`)) {
|
|
2002
|
-
return { ...node, children: removeNode(node.children, path) };
|
|
2156
|
+
return { ...node, children: removeNode(node.children, path, rootPath) };
|
|
2003
2157
|
}
|
|
2004
2158
|
return node;
|
|
2005
2159
|
});
|
|
@@ -2070,7 +2224,7 @@ function useSandboxFiles(sessionId, options = {}) {
|
|
|
2070
2224
|
const repoPathsKey = [
|
|
2071
2225
|
...new Set((options.repoPaths?.length ? options.repoPaths : [rootPath]).map(normalizeRoot))
|
|
2072
2226
|
].join("\0");
|
|
2073
|
-
const repoPaths =
|
|
2227
|
+
const repoPaths = useMemo6(() => repoPathsKey.split("\0"), [repoPathsKey]);
|
|
2074
2228
|
const capture = options.capture ?? null;
|
|
2075
2229
|
const isLive2 = sandboxAcceptsLiveIo(options.liveness);
|
|
2076
2230
|
const acceptsEventReads = options.liveness === void 0 || isLive2;
|
|
@@ -2098,13 +2252,13 @@ function useSandboxFiles(sessionId, options = {}) {
|
|
|
2098
2252
|
const lastSeqRef = useRef5(0);
|
|
2099
2253
|
const pendingParentsRef = useRef5(/* @__PURE__ */ new Set());
|
|
2100
2254
|
const pendingGitRef = useRef5(false);
|
|
2101
|
-
const
|
|
2255
|
+
const eventReconcileInFlightRef = useRef5(null);
|
|
2256
|
+
const eventReconcilePendingRef = useRef5(false);
|
|
2102
2257
|
const debounceRef = useRef5(null);
|
|
2103
|
-
const lastCommandRefreshAtRef = useRef5(0);
|
|
2104
2258
|
const ownRevisionsRef = useRef5(/* @__PURE__ */ new Set());
|
|
2105
2259
|
const onMutationError = options.onMutationError;
|
|
2106
2260
|
treeRef.current = tree;
|
|
2107
|
-
const applyStatus =
|
|
2261
|
+
const applyStatus = useCallback15((nodes) => {
|
|
2108
2262
|
const overlay = statusRef.current;
|
|
2109
2263
|
const walk = (list) => {
|
|
2110
2264
|
let changed = false;
|
|
@@ -2127,7 +2281,7 @@ function useSandboxFiles(sessionId, options = {}) {
|
|
|
2127
2281
|
};
|
|
2128
2282
|
return walk(nodes);
|
|
2129
2283
|
}, []);
|
|
2130
|
-
const refresh =
|
|
2284
|
+
const refresh = useCallback15(async () => {
|
|
2131
2285
|
if (!sessionId) return;
|
|
2132
2286
|
refreshAbortRef.current?.abort();
|
|
2133
2287
|
const refreshAbort = new AbortController();
|
|
@@ -2142,21 +2296,6 @@ function useSandboxFiles(sessionId, options = {}) {
|
|
|
2142
2296
|
path,
|
|
2143
2297
|
depth: 1
|
|
2144
2298
|
}));
|
|
2145
|
-
const statusPromise = repoPaths.length > 0 ? client.gitReadBatch(
|
|
2146
|
-
workspaceId,
|
|
2147
|
-
sessionId,
|
|
2148
|
-
{
|
|
2149
|
-
requests: repoPaths.map((root) => ({
|
|
2150
|
-
status: { path: root }
|
|
2151
|
-
}))
|
|
2152
|
-
},
|
|
2153
|
-
{ signal: refreshAbort.signal }
|
|
2154
|
-
).then(
|
|
2155
|
-
(batch) => batch.results.map((result, index) => ({
|
|
2156
|
-
root: repoPaths[index] ?? "",
|
|
2157
|
-
status: result.status
|
|
2158
|
-
}))
|
|
2159
|
-
).catch(() => null) : Promise.resolve([]);
|
|
2160
2299
|
const listedBatch = await client.fsListBatch(
|
|
2161
2300
|
workspaceId,
|
|
2162
2301
|
sessionId,
|
|
@@ -2183,7 +2322,21 @@ function useSandboxFiles(sessionId, options = {}) {
|
|
|
2183
2322
|
sourceRef.current = "live";
|
|
2184
2323
|
setSource("live");
|
|
2185
2324
|
setLoading(false);
|
|
2186
|
-
const statuses = await
|
|
2325
|
+
const statuses = repoPaths.length > 0 ? await client.gitReadBatch(
|
|
2326
|
+
workspaceId,
|
|
2327
|
+
sessionId,
|
|
2328
|
+
{
|
|
2329
|
+
requests: repoPaths.map((root) => ({
|
|
2330
|
+
status: { path: root }
|
|
2331
|
+
}))
|
|
2332
|
+
},
|
|
2333
|
+
{ signal: refreshAbort.signal }
|
|
2334
|
+
).then(
|
|
2335
|
+
(batch) => batch.results.map((result, index) => ({
|
|
2336
|
+
root: repoPaths[index] ?? "",
|
|
2337
|
+
status: result.status
|
|
2338
|
+
}))
|
|
2339
|
+
).catch(() => null) : [];
|
|
2187
2340
|
if (refreshGenerationRef.current !== generation) return;
|
|
2188
2341
|
if (statuses) {
|
|
2189
2342
|
const overlay = /* @__PURE__ */ new Map();
|
|
@@ -2191,7 +2344,7 @@ function useSandboxFiles(sessionId, options = {}) {
|
|
|
2191
2344
|
for (const file of status.files) {
|
|
2192
2345
|
const code = file.worktree ?? file.index;
|
|
2193
2346
|
const mapped = code ? GIT_STATUS_TO_TREE[code] : void 0;
|
|
2194
|
-
if (mapped) overlay.set(qualifyStatusPath(root, file.path), mapped);
|
|
2347
|
+
if (mapped) overlay.set(qualifyStatusPath(root, file.path, rootPath), mapped);
|
|
2195
2348
|
}
|
|
2196
2349
|
}
|
|
2197
2350
|
statusRef.current = overlay;
|
|
@@ -2211,7 +2364,7 @@ function useSandboxFiles(sessionId, options = {}) {
|
|
|
2211
2364
|
if (refreshAbortRef.current === refreshAbort) refreshAbortRef.current = null;
|
|
2212
2365
|
}
|
|
2213
2366
|
}, [client, workspaceId, sessionId, rootPath, repoPaths, applyStatus]);
|
|
2214
|
-
const seedFromCapture =
|
|
2367
|
+
const seedFromCapture = useCallback15(
|
|
2215
2368
|
(manifest) => {
|
|
2216
2369
|
const overlay = /* @__PURE__ */ new Map();
|
|
2217
2370
|
for (const file of manifest.files) {
|
|
@@ -2239,7 +2392,7 @@ function useSandboxFiles(sessionId, options = {}) {
|
|
|
2239
2392
|
},
|
|
2240
2393
|
[applyStatus]
|
|
2241
2394
|
);
|
|
2242
|
-
const expand =
|
|
2395
|
+
const expand = useCallback15(
|
|
2243
2396
|
async (path) => {
|
|
2244
2397
|
if (!sessionId) return;
|
|
2245
2398
|
if (source === "capture" && capture) return;
|
|
@@ -2276,7 +2429,7 @@ function useSandboxFiles(sessionId, options = {}) {
|
|
|
2276
2429
|
},
|
|
2277
2430
|
[client, workspaceId, sessionId, capture, source, applyStatus]
|
|
2278
2431
|
);
|
|
2279
|
-
const readFile =
|
|
2432
|
+
const readFile = useCallback15(
|
|
2280
2433
|
async (path, requestOptions = {}) => {
|
|
2281
2434
|
if (!sessionId) throw new Error("no session");
|
|
2282
2435
|
const identitySignal = identityAbortRef.current.signal;
|
|
@@ -2351,19 +2504,20 @@ function useSandboxFiles(sessionId, options = {}) {
|
|
|
2351
2504
|
},
|
|
2352
2505
|
[client, workspaceId, sessionId, capture, source]
|
|
2353
2506
|
);
|
|
2354
|
-
const reconcilePath =
|
|
2507
|
+
const reconcilePath = useCallback15(
|
|
2355
2508
|
async (path, requestSignal) => {
|
|
2356
2509
|
if (!sessionId) return;
|
|
2357
2510
|
const identityGeneration = identityGenerationRef.current;
|
|
2358
2511
|
const identitySignal = identityAbortRef.current.signal;
|
|
2359
2512
|
const signal = requestSignal ? AbortSignal.any([identitySignal, requestSignal]) : identitySignal;
|
|
2360
|
-
if (!parentIsLoaded(treeRef.current, path)) return;
|
|
2513
|
+
if (!parentIsLoaded(treeRef.current, path, rootPath)) return;
|
|
2361
2514
|
try {
|
|
2362
2515
|
const listed = await client.fsList(workspaceId, sessionId, { path, depth: 1 }, { signal });
|
|
2363
2516
|
if (identityGenerationRef.current !== identityGeneration) return;
|
|
2364
2517
|
const children = (listed.root.children ?? []).map((node) => fsNodeToTree(node));
|
|
2365
|
-
if (path ===
|
|
2366
|
-
|
|
2518
|
+
if (path === normalizeRoot(rootPath)) {
|
|
2519
|
+
setTree((prev) => applyStatus(mergeRootChildren(prev, children)));
|
|
2520
|
+
} else
|
|
2367
2521
|
setTree((prev) => {
|
|
2368
2522
|
const existing = findNodeByPath(prev, path);
|
|
2369
2523
|
const merged = existing?.children ? mergeChildren(existing.children, children) : children;
|
|
@@ -2372,9 +2526,50 @@ function useSandboxFiles(sessionId, options = {}) {
|
|
|
2372
2526
|
} catch {
|
|
2373
2527
|
}
|
|
2374
2528
|
},
|
|
2375
|
-
[client, workspaceId, sessionId, applyStatus]
|
|
2529
|
+
[client, workspaceId, sessionId, rootPath, applyStatus]
|
|
2530
|
+
);
|
|
2531
|
+
const reconcilePaths = useCallback15(
|
|
2532
|
+
async (requestedPaths, requestSignal) => {
|
|
2533
|
+
if (!sessionId) return;
|
|
2534
|
+
const identityGeneration = identityGenerationRef.current;
|
|
2535
|
+
const identitySignal = identityAbortRef.current.signal;
|
|
2536
|
+
const signal = requestSignal ? AbortSignal.any([identitySignal, requestSignal]) : identitySignal;
|
|
2537
|
+
const paths = [...new Set(requestedPaths)].filter(
|
|
2538
|
+
(path) => parentIsLoaded(treeRef.current, path, rootPath)
|
|
2539
|
+
);
|
|
2540
|
+
for (let offset = 0; offset < paths.length; offset += FS_LIST_BATCH_REQUEST_LIMIT) {
|
|
2541
|
+
const chunk = paths.slice(offset, offset + FS_LIST_BATCH_REQUEST_LIMIT);
|
|
2542
|
+
try {
|
|
2543
|
+
const listed = await client.fsListBatch(
|
|
2544
|
+
workspaceId,
|
|
2545
|
+
sessionId,
|
|
2546
|
+
{ requests: chunk.map((path) => ({ path, depth: 1 })) },
|
|
2547
|
+
{ signal }
|
|
2548
|
+
);
|
|
2549
|
+
if (identityGenerationRef.current !== identityGeneration) return;
|
|
2550
|
+
setTree((previous) => {
|
|
2551
|
+
let next = previous;
|
|
2552
|
+
for (const [index, path] of chunk.entries()) {
|
|
2553
|
+
const result = listed.results[index];
|
|
2554
|
+
if (!result) continue;
|
|
2555
|
+
const children = (result.root.children ?? []).map((node) => fsNodeToTree(node));
|
|
2556
|
+
if (path === normalizeRoot(rootPath)) {
|
|
2557
|
+
next = mergeRootChildren(next, children);
|
|
2558
|
+
} else {
|
|
2559
|
+
const existing = findNodeByPath(next, path);
|
|
2560
|
+
const merged = existing?.children ? mergeChildren(existing.children, children) : children;
|
|
2561
|
+
next = replaceChildren(next, path, merged);
|
|
2562
|
+
}
|
|
2563
|
+
}
|
|
2564
|
+
return applyStatus(next);
|
|
2565
|
+
});
|
|
2566
|
+
} catch {
|
|
2567
|
+
}
|
|
2568
|
+
}
|
|
2569
|
+
},
|
|
2570
|
+
[client, workspaceId, sessionId, rootPath, applyStatus]
|
|
2376
2571
|
);
|
|
2377
|
-
const runOptimistic =
|
|
2572
|
+
const runOptimistic = useCallback15(
|
|
2378
2573
|
async (opName, apply, op, reconcileParents) => {
|
|
2379
2574
|
const identityGeneration = identityGenerationRef.current;
|
|
2380
2575
|
const snapshot = treeRef.current;
|
|
@@ -2404,12 +2599,12 @@ function useSandboxFiles(sessionId, options = {}) {
|
|
|
2404
2599
|
},
|
|
2405
2600
|
[applyStatus, reconcilePath, onMutationError]
|
|
2406
2601
|
);
|
|
2407
|
-
const writeFile =
|
|
2602
|
+
const writeFile = useCallback15(
|
|
2408
2603
|
async (path, content, writeOptions = {}) => {
|
|
2409
2604
|
if (!sessionId) throw new Error("no session");
|
|
2410
2605
|
const identityGeneration = identityGenerationRef.current;
|
|
2411
2606
|
const identitySignal = identityAbortRef.current.signal;
|
|
2412
|
-
const parent =
|
|
2607
|
+
const parent = parentWithinTree(path, rootPath);
|
|
2413
2608
|
const exists = Boolean(findNodeByPath(tree, path));
|
|
2414
2609
|
const node = {
|
|
2415
2610
|
path,
|
|
@@ -2419,7 +2614,7 @@ function useSandboxFiles(sessionId, options = {}) {
|
|
|
2419
2614
|
};
|
|
2420
2615
|
return await runOptimistic(
|
|
2421
2616
|
"write",
|
|
2422
|
-
(nodes) => exists ? nodes : insertNode(nodes, parent, node),
|
|
2617
|
+
(nodes) => exists ? nodes : insertNode(nodes, parent, node, rootPath),
|
|
2423
2618
|
async () => {
|
|
2424
2619
|
if (!writeOptions.force && writeOptions.expectedContent !== void 0) {
|
|
2425
2620
|
const live = await client.fsRead(
|
|
@@ -2448,12 +2643,12 @@ function useSandboxFiles(sessionId, options = {}) {
|
|
|
2448
2643
|
exists ? [] : [parent]
|
|
2449
2644
|
);
|
|
2450
2645
|
},
|
|
2451
|
-
[client, workspaceId, sessionId, tree, runOptimistic]
|
|
2646
|
+
[client, workspaceId, sessionId, tree, rootPath, runOptimistic]
|
|
2452
2647
|
);
|
|
2453
|
-
const createFile =
|
|
2648
|
+
const createFile = useCallback15(
|
|
2454
2649
|
async (path) => {
|
|
2455
2650
|
if (!sessionId) throw new Error("no session");
|
|
2456
|
-
const parent =
|
|
2651
|
+
const parent = parentWithinTree(path, rootPath);
|
|
2457
2652
|
const node = {
|
|
2458
2653
|
path,
|
|
2459
2654
|
name: leafOf(path),
|
|
@@ -2462,7 +2657,7 @@ function useSandboxFiles(sessionId, options = {}) {
|
|
|
2462
2657
|
};
|
|
2463
2658
|
await runOptimistic(
|
|
2464
2659
|
"create file",
|
|
2465
|
-
(nodes) => insertNode(nodes, parent, node),
|
|
2660
|
+
(nodes) => insertNode(nodes, parent, node, rootPath),
|
|
2466
2661
|
() => client.fsWrite(workspaceId, sessionId, {
|
|
2467
2662
|
path,
|
|
2468
2663
|
content: "",
|
|
@@ -2471,12 +2666,12 @@ function useSandboxFiles(sessionId, options = {}) {
|
|
|
2471
2666
|
[parent]
|
|
2472
2667
|
);
|
|
2473
2668
|
},
|
|
2474
|
-
[client, workspaceId, sessionId, runOptimistic]
|
|
2669
|
+
[client, workspaceId, sessionId, rootPath, runOptimistic]
|
|
2475
2670
|
);
|
|
2476
|
-
const createDir =
|
|
2671
|
+
const createDir = useCallback15(
|
|
2477
2672
|
async (path) => {
|
|
2478
2673
|
if (!sessionId) throw new Error("no session");
|
|
2479
|
-
const parent =
|
|
2674
|
+
const parent = parentWithinTree(path, rootPath);
|
|
2480
2675
|
const node = {
|
|
2481
2676
|
path,
|
|
2482
2677
|
name: leafOf(path),
|
|
@@ -2485,37 +2680,37 @@ function useSandboxFiles(sessionId, options = {}) {
|
|
|
2485
2680
|
};
|
|
2486
2681
|
await runOptimistic(
|
|
2487
2682
|
"create folder",
|
|
2488
|
-
(nodes) => insertNode(nodes, parent, node),
|
|
2683
|
+
(nodes) => insertNode(nodes, parent, node, rootPath),
|
|
2489
2684
|
() => client.fsMkdir(workspaceId, sessionId, { path, recursive: true }),
|
|
2490
2685
|
[parent]
|
|
2491
2686
|
);
|
|
2492
2687
|
},
|
|
2493
|
-
[client, workspaceId, sessionId, runOptimistic]
|
|
2688
|
+
[client, workspaceId, sessionId, rootPath, runOptimistic]
|
|
2494
2689
|
);
|
|
2495
|
-
const deleteEntry =
|
|
2690
|
+
const deleteEntry = useCallback15(
|
|
2496
2691
|
async (path, recursive = false) => {
|
|
2497
2692
|
if (!sessionId) throw new Error("no session");
|
|
2498
2693
|
await runOptimistic(
|
|
2499
2694
|
"delete",
|
|
2500
|
-
(nodes) => removeNode(nodes, path),
|
|
2695
|
+
(nodes) => removeNode(nodes, path, rootPath),
|
|
2501
2696
|
() => client.fsDelete(workspaceId, sessionId, { path, recursive }),
|
|
2502
|
-
[
|
|
2697
|
+
[parentWithinTree(path, rootPath)]
|
|
2503
2698
|
);
|
|
2504
2699
|
},
|
|
2505
|
-
[client, workspaceId, sessionId, runOptimistic]
|
|
2700
|
+
[client, workspaceId, sessionId, rootPath, runOptimistic]
|
|
2506
2701
|
);
|
|
2507
|
-
const moveEntry =
|
|
2702
|
+
const moveEntry = useCallback15(
|
|
2508
2703
|
async (path, newPath, opts) => {
|
|
2509
2704
|
if (!sessionId) throw new Error("no session");
|
|
2510
|
-
const from =
|
|
2511
|
-
const to =
|
|
2705
|
+
const from = parentWithinTree(path, rootPath);
|
|
2706
|
+
const to = parentWithinTree(newPath, rootPath);
|
|
2512
2707
|
await runOptimistic(
|
|
2513
2708
|
"move",
|
|
2514
2709
|
(nodes) => {
|
|
2515
2710
|
const moving = findNodeByPath(nodes, path);
|
|
2516
2711
|
if (!moving) return nodes;
|
|
2517
2712
|
const moved = repathNode(moving, path, newPath);
|
|
2518
|
-
return insertNode(removeNode(nodes, path), to, moved);
|
|
2713
|
+
return insertNode(removeNode(nodes, path, rootPath), to, moved, rootPath);
|
|
2519
2714
|
},
|
|
2520
2715
|
() => client.fsMove(workspaceId, sessionId, {
|
|
2521
2716
|
path,
|
|
@@ -2525,7 +2720,7 @@ function useSandboxFiles(sessionId, options = {}) {
|
|
|
2525
2720
|
to === from ? [from] : [from, to]
|
|
2526
2721
|
);
|
|
2527
2722
|
},
|
|
2528
|
-
[client, workspaceId, sessionId, runOptimistic]
|
|
2723
|
+
[client, workspaceId, sessionId, rootPath, runOptimistic]
|
|
2529
2724
|
);
|
|
2530
2725
|
const captureRef = useRef5(capture);
|
|
2531
2726
|
captureRef.current = capture;
|
|
@@ -2540,14 +2735,14 @@ function useSandboxFiles(sessionId, options = {}) {
|
|
|
2540
2735
|
const identityChanged = previousIdentityRef.current !== identityKey;
|
|
2541
2736
|
previousIdentityRef.current = identityKey;
|
|
2542
2737
|
if (identityChanged || !enabled) {
|
|
2738
|
+
if (identityChanged) eventReconcileInFlightRef.current = null;
|
|
2543
2739
|
identityAbortRef.current.abort();
|
|
2544
2740
|
identityAbortRef.current = new AbortController();
|
|
2545
2741
|
identityGenerationRef.current += 1;
|
|
2546
2742
|
lastSeqRef.current = 0;
|
|
2547
2743
|
pendingParentsRef.current = /* @__PURE__ */ new Set();
|
|
2548
2744
|
pendingGitRef.current = false;
|
|
2549
|
-
|
|
2550
|
-
lastCommandRefreshAtRef.current = 0;
|
|
2745
|
+
eventReconcilePendingRef.current = false;
|
|
2551
2746
|
if (debounceRef.current) {
|
|
2552
2747
|
clearTimeout(debounceRef.current);
|
|
2553
2748
|
debounceRef.current = null;
|
|
@@ -2588,6 +2783,7 @@ function useSandboxFiles(sessionId, options = {}) {
|
|
|
2588
2783
|
refreshAbortRef.current = null;
|
|
2589
2784
|
refreshGenerationRef.current += 1;
|
|
2590
2785
|
eventReadAbortRef.current.abort();
|
|
2786
|
+
eventReconcilePendingRef.current = false;
|
|
2591
2787
|
};
|
|
2592
2788
|
}, [
|
|
2593
2789
|
enabled,
|
|
@@ -2599,7 +2795,7 @@ function useSandboxFiles(sessionId, options = {}) {
|
|
|
2599
2795
|
refresh,
|
|
2600
2796
|
seedFromCapture
|
|
2601
2797
|
]);
|
|
2602
|
-
const refreshGitOverlay =
|
|
2798
|
+
const refreshGitOverlay = useCallback15(
|
|
2603
2799
|
async (requestSignal) => {
|
|
2604
2800
|
if (!sessionId) return;
|
|
2605
2801
|
const identityGeneration = identityGenerationRef.current;
|
|
@@ -2623,7 +2819,7 @@ function useSandboxFiles(sessionId, options = {}) {
|
|
|
2623
2819
|
for (const file of status.files) {
|
|
2624
2820
|
const code = file.worktree ?? file.index;
|
|
2625
2821
|
const mapped = code ? GIT_STATUS_TO_TREE[code] : void 0;
|
|
2626
|
-
if (mapped) overlay.set(qualifyStatusPath(root, file.path), mapped);
|
|
2822
|
+
if (mapped) overlay.set(qualifyStatusPath(root, file.path, rootPath), mapped);
|
|
2627
2823
|
}
|
|
2628
2824
|
}
|
|
2629
2825
|
statusRef.current = overlay;
|
|
@@ -2634,8 +2830,38 @@ function useSandboxFiles(sessionId, options = {}) {
|
|
|
2634
2830
|
if (identityGenerationRef.current === identityGeneration) setGitLoading(false);
|
|
2635
2831
|
}
|
|
2636
2832
|
},
|
|
2637
|
-
[client, workspaceId, sessionId, repoPaths, applyStatus]
|
|
2833
|
+
[client, workspaceId, sessionId, repoPaths, rootPath, applyStatus]
|
|
2638
2834
|
);
|
|
2835
|
+
const performEventReconcile = useCallback15(async () => {
|
|
2836
|
+
const parents = pendingParentsRef.current;
|
|
2837
|
+
pendingParentsRef.current = /* @__PURE__ */ new Set();
|
|
2838
|
+
const wantGit = pendingGitRef.current;
|
|
2839
|
+
pendingGitRef.current = false;
|
|
2840
|
+
const signal = eventReadAbortRef.current.signal;
|
|
2841
|
+
const paths = [...parents];
|
|
2842
|
+
await reconcilePaths(paths, signal);
|
|
2843
|
+
if (wantGit) await refreshGitOverlay(signal);
|
|
2844
|
+
}, [reconcilePaths, refreshGitOverlay]);
|
|
2845
|
+
const performEventReconcileRef = useRef5(performEventReconcile);
|
|
2846
|
+
performEventReconcileRef.current = performEventReconcile;
|
|
2847
|
+
const reconcilePendingEvents = useCallback15(async () => {
|
|
2848
|
+
eventReconcilePendingRef.current = true;
|
|
2849
|
+
const existing = eventReconcileInFlightRef.current;
|
|
2850
|
+
if (existing) return await existing;
|
|
2851
|
+
const identityGeneration = identityGenerationRef.current;
|
|
2852
|
+
const run = (async () => {
|
|
2853
|
+
while (eventReconcilePendingRef.current && identityGenerationRef.current === identityGeneration) {
|
|
2854
|
+
eventReconcilePendingRef.current = false;
|
|
2855
|
+
await performEventReconcileRef.current();
|
|
2856
|
+
}
|
|
2857
|
+
})();
|
|
2858
|
+
eventReconcileInFlightRef.current = run;
|
|
2859
|
+
try {
|
|
2860
|
+
await run;
|
|
2861
|
+
} finally {
|
|
2862
|
+
if (eventReconcileInFlightRef.current === run) eventReconcileInFlightRef.current = null;
|
|
2863
|
+
}
|
|
2864
|
+
}, []);
|
|
2639
2865
|
const events = options.events;
|
|
2640
2866
|
useEffect6(() => {
|
|
2641
2867
|
if (!enabled || !events) return;
|
|
@@ -2649,12 +2875,11 @@ function useSandboxFiles(sessionId, options = {}) {
|
|
|
2649
2875
|
}
|
|
2650
2876
|
pendingParentsRef.current = /* @__PURE__ */ new Set();
|
|
2651
2877
|
pendingGitRef.current = false;
|
|
2652
|
-
|
|
2878
|
+
eventReconcilePendingRef.current = false;
|
|
2653
2879
|
return;
|
|
2654
2880
|
}
|
|
2655
2881
|
let sawNew = false;
|
|
2656
2882
|
let contentChanged = false;
|
|
2657
|
-
let sawCommandDelta = false;
|
|
2658
2883
|
for (const event of events) {
|
|
2659
2884
|
if (event.sequence <= lastSeqRef.current) continue;
|
|
2660
2885
|
if (event.type === "fs.changed") {
|
|
@@ -2666,8 +2891,10 @@ function useSandboxFiles(sessionId, options = {}) {
|
|
|
2666
2891
|
continue;
|
|
2667
2892
|
contentChanged = true;
|
|
2668
2893
|
for (const change of payload.changes ?? []) {
|
|
2669
|
-
pendingParentsRef.current.add(
|
|
2670
|
-
if (change.oldPath)
|
|
2894
|
+
pendingParentsRef.current.add(parentWithinTree(change.path, rootPath));
|
|
2895
|
+
if (change.oldPath) {
|
|
2896
|
+
pendingParentsRef.current.add(parentWithinTree(change.oldPath, rootPath));
|
|
2897
|
+
}
|
|
2671
2898
|
}
|
|
2672
2899
|
} else if (event.type === "git.changed") {
|
|
2673
2900
|
sawNew = true;
|
|
@@ -2675,55 +2902,25 @@ function useSandboxFiles(sessionId, options = {}) {
|
|
|
2675
2902
|
if (payload && typeof payload === "object" && typeof payload.revision === "number" && ownRevisionsRef.current.has(payload.revision))
|
|
2676
2903
|
continue;
|
|
2677
2904
|
pendingGitRef.current = true;
|
|
2678
|
-
} else if (event.type === "agent.toolCall.output") {
|
|
2679
|
-
sawNew = true;
|
|
2680
|
-
contentChanged = true;
|
|
2681
|
-
pendingAgentToolRef.current = true;
|
|
2682
|
-
pendingGitRef.current = true;
|
|
2683
|
-
} else if (event.type === "sandbox.command.output.delta") {
|
|
2684
|
-
sawNew = true;
|
|
2685
|
-
contentChanged = true;
|
|
2686
|
-
sawCommandDelta = true;
|
|
2687
|
-
pendingAgentToolRef.current = true;
|
|
2688
|
-
pendingGitRef.current = true;
|
|
2689
2905
|
}
|
|
2690
2906
|
}
|
|
2691
2907
|
for (const event of events)
|
|
2692
2908
|
if (event.sequence > lastSeqRef.current) lastSeqRef.current = event.sequence;
|
|
2693
2909
|
if (!sawNew) return;
|
|
2694
|
-
if (contentChanged
|
|
2910
|
+
if (contentChanged) setContentRevision((revision) => revision + 1);
|
|
2695
2911
|
if (debounceRef.current) clearTimeout(debounceRef.current);
|
|
2696
2912
|
const identityGeneration = identityGenerationRef.current;
|
|
2697
|
-
const delay = sawCommandDelta ? Math.max(0, 1e3 - (Date.now() - lastCommandRefreshAtRef.current)) : 150;
|
|
2698
2913
|
debounceRef.current = setTimeout(() => {
|
|
2699
2914
|
debounceRef.current = null;
|
|
2700
2915
|
if (identityGenerationRef.current !== identityGeneration) return;
|
|
2701
2916
|
if (!acceptsEventReadsRef.current) {
|
|
2702
2917
|
pendingParentsRef.current = /* @__PURE__ */ new Set();
|
|
2703
2918
|
pendingGitRef.current = false;
|
|
2704
|
-
pendingAgentToolRef.current = false;
|
|
2705
2919
|
return;
|
|
2706
2920
|
}
|
|
2707
|
-
|
|
2708
|
-
|
|
2709
|
-
|
|
2710
|
-
}
|
|
2711
|
-
const parents = pendingParentsRef.current;
|
|
2712
|
-
pendingParentsRef.current = /* @__PURE__ */ new Set();
|
|
2713
|
-
const wantGit = pendingGitRef.current;
|
|
2714
|
-
pendingGitRef.current = false;
|
|
2715
|
-
const wantAgentTool = pendingAgentToolRef.current;
|
|
2716
|
-
pendingAgentToolRef.current = false;
|
|
2717
|
-
const eventReadSignal = eventReadAbortRef.current.signal;
|
|
2718
|
-
void (async () => {
|
|
2719
|
-
const paths = wantAgentTool ? loadedDirectoryPaths(treeRef.current) : [...parents];
|
|
2720
|
-
await Promise.all([
|
|
2721
|
-
wantGit ? refreshGitOverlay(eventReadSignal) : Promise.resolve(),
|
|
2722
|
-
Promise.all(paths.map((path) => reconcilePath(path, eventReadSignal)))
|
|
2723
|
-
]);
|
|
2724
|
-
})();
|
|
2725
|
-
}, delay);
|
|
2726
|
-
}, [enabled, active, acceptsEventReads, events, reconcilePath, refreshGitOverlay]);
|
|
2921
|
+
void reconcilePendingEvents();
|
|
2922
|
+
}, 150);
|
|
2923
|
+
}, [enabled, active, acceptsEventReads, events, rootPath, reconcilePendingEvents]);
|
|
2727
2924
|
useEffect6(() => {
|
|
2728
2925
|
if (active && acceptsEventReads) return;
|
|
2729
2926
|
if (debounceRef.current) {
|
|
@@ -2732,13 +2929,14 @@ function useSandboxFiles(sessionId, options = {}) {
|
|
|
2732
2929
|
}
|
|
2733
2930
|
pendingParentsRef.current = /* @__PURE__ */ new Set();
|
|
2734
2931
|
pendingGitRef.current = false;
|
|
2735
|
-
|
|
2932
|
+
eventReconcilePendingRef.current = false;
|
|
2736
2933
|
}, [active, acceptsEventReads]);
|
|
2737
2934
|
useEffect6(
|
|
2738
2935
|
() => () => {
|
|
2739
2936
|
refreshAbortRef.current?.abort();
|
|
2740
2937
|
identityAbortRef.current.abort();
|
|
2741
2938
|
eventReadAbortRef.current.abort();
|
|
2939
|
+
eventReconcilePendingRef.current = false;
|
|
2742
2940
|
identityGenerationRef.current += 1;
|
|
2743
2941
|
if (debounceRef.current) clearTimeout(debounceRef.current);
|
|
2744
2942
|
},
|
|
@@ -2767,7 +2965,7 @@ function useSandboxFiles(sessionId, options = {}) {
|
|
|
2767
2965
|
}
|
|
2768
2966
|
|
|
2769
2967
|
// src/hooks/use-sandbox-git.ts
|
|
2770
|
-
import { useCallback as
|
|
2968
|
+
import { useCallback as useCallback16, useEffect as useEffect7, useMemo as useMemo7, useRef as useRef6, useState as useState5 } from "react";
|
|
2771
2969
|
function normalizeRoot2(root) {
|
|
2772
2970
|
const normalized = root.replaceAll("\\", "/").replace(/^\.\/+/, "").replace(/\/+$/, "");
|
|
2773
2971
|
return normalized === "." ? "" : normalized;
|
|
@@ -2833,7 +3031,7 @@ function useSandboxGit(sessionId, options = {}) {
|
|
|
2833
3031
|
const repoPathsKey = uniqueRoots(
|
|
2834
3032
|
options.repoPaths && options.repoPaths.length > 0 ? options.repoPaths : [repoPath]
|
|
2835
3033
|
).join("\0");
|
|
2836
|
-
const repoPaths =
|
|
3034
|
+
const repoPaths = useMemo7(() => repoPathsKey.split("\0"), [repoPathsKey]);
|
|
2837
3035
|
const comparison = options.comparison ?? (options.staged ? "staged" : "working");
|
|
2838
3036
|
const hasAdvertisedRepoPaths = (options.repoPaths?.length ?? 0) > 0;
|
|
2839
3037
|
const capture = comparison === "staged" ? null : options.capture ?? null;
|
|
@@ -2856,11 +3054,13 @@ function useSandboxGit(sessionId, options = {}) {
|
|
|
2856
3054
|
const [stateIdentity, setStateIdentity] = useState5(identityKey);
|
|
2857
3055
|
const refreshGenerationRef = useRef6(0);
|
|
2858
3056
|
const refreshAbortRef = useRef6(null);
|
|
3057
|
+
const refreshInFlightRef = useRef6(null);
|
|
3058
|
+
const refreshPendingRef = useRef6(false);
|
|
3059
|
+
const refreshWorkerEpochRef = useRef6(0);
|
|
2859
3060
|
const lastChangeRef = useRef6(0);
|
|
2860
|
-
const
|
|
2861
|
-
const
|
|
3061
|
+
const eventRefreshTimerRef = useRef6(null);
|
|
3062
|
+
const performRefresh = useCallback16(async () => {
|
|
2862
3063
|
if (!sessionId) return;
|
|
2863
|
-
refreshAbortRef.current?.abort();
|
|
2864
3064
|
const refreshAbort = new AbortController();
|
|
2865
3065
|
refreshAbortRef.current = refreshAbort;
|
|
2866
3066
|
const generation = refreshGenerationRef.current += 1;
|
|
@@ -2971,7 +3171,27 @@ function useSandboxGit(sessionId, options = {}) {
|
|
|
2971
3171
|
workspaceWide,
|
|
2972
3172
|
comparison
|
|
2973
3173
|
]);
|
|
2974
|
-
const
|
|
3174
|
+
const performRefreshRef = useRef6(performRefresh);
|
|
3175
|
+
performRefreshRef.current = performRefresh;
|
|
3176
|
+
const refresh = useCallback16(async () => {
|
|
3177
|
+
refreshPendingRef.current = true;
|
|
3178
|
+
const existing = refreshInFlightRef.current;
|
|
3179
|
+
if (existing) return await existing;
|
|
3180
|
+
const workerEpoch = refreshWorkerEpochRef.current;
|
|
3181
|
+
const run = (async () => {
|
|
3182
|
+
while (refreshPendingRef.current && refreshWorkerEpochRef.current === workerEpoch) {
|
|
3183
|
+
refreshPendingRef.current = false;
|
|
3184
|
+
await performRefreshRef.current();
|
|
3185
|
+
}
|
|
3186
|
+
})();
|
|
3187
|
+
refreshInFlightRef.current = run;
|
|
3188
|
+
try {
|
|
3189
|
+
await run;
|
|
3190
|
+
} finally {
|
|
3191
|
+
if (refreshInFlightRef.current === run) refreshInFlightRef.current = null;
|
|
3192
|
+
}
|
|
3193
|
+
}, []);
|
|
3194
|
+
const seedFromCapture = useCallback16(
|
|
2975
3195
|
(manifest) => {
|
|
2976
3196
|
const repositories = workspaceWide ? manifest.repos : [repoForPath(manifest, repoPath)].filter(Boolean);
|
|
2977
3197
|
if (comparison === "branch" && repositories.some((repo) => repo.branchDiff === void 0)) {
|
|
@@ -3015,9 +3235,14 @@ function useSandboxGit(sessionId, options = {}) {
|
|
|
3015
3235
|
useEffect7(() => {
|
|
3016
3236
|
refreshAbortRef.current?.abort();
|
|
3017
3237
|
refreshAbortRef.current = null;
|
|
3238
|
+
refreshPendingRef.current = false;
|
|
3018
3239
|
refreshGenerationRef.current += 1;
|
|
3019
3240
|
const identityChanged = previousIdentityRef.current !== identityKey;
|
|
3020
3241
|
previousIdentityRef.current = identityKey;
|
|
3242
|
+
if (identityChanged) {
|
|
3243
|
+
refreshWorkerEpochRef.current += 1;
|
|
3244
|
+
refreshInFlightRef.current = null;
|
|
3245
|
+
}
|
|
3021
3246
|
if (identityChanged || !enabled) {
|
|
3022
3247
|
lastChangeRef.current = 0;
|
|
3023
3248
|
setDiff([]);
|
|
@@ -3049,6 +3274,7 @@ function useSandboxGit(sessionId, options = {}) {
|
|
|
3049
3274
|
return () => {
|
|
3050
3275
|
refreshAbortRef.current?.abort();
|
|
3051
3276
|
refreshAbortRef.current = null;
|
|
3277
|
+
refreshPendingRef.current = false;
|
|
3052
3278
|
refreshGenerationRef.current += 1;
|
|
3053
3279
|
};
|
|
3054
3280
|
}, [
|
|
@@ -3064,9 +3290,9 @@ function useSandboxGit(sessionId, options = {}) {
|
|
|
3064
3290
|
const events = options.events;
|
|
3065
3291
|
useEffect7(() => {
|
|
3066
3292
|
if (!enabled || !events) {
|
|
3067
|
-
if (
|
|
3068
|
-
clearTimeout(
|
|
3069
|
-
|
|
3293
|
+
if (eventRefreshTimerRef.current) {
|
|
3294
|
+
clearTimeout(eventRefreshTimerRef.current);
|
|
3295
|
+
eventRefreshTimerRef.current = null;
|
|
3070
3296
|
}
|
|
3071
3297
|
return;
|
|
3072
3298
|
}
|
|
@@ -3074,42 +3300,34 @@ function useSandboxGit(sessionId, options = {}) {
|
|
|
3074
3300
|
for (const event of events) {
|
|
3075
3301
|
if (event.sequence > lastChangeRef.current) lastChangeRef.current = event.sequence;
|
|
3076
3302
|
}
|
|
3077
|
-
if (
|
|
3078
|
-
clearTimeout(
|
|
3079
|
-
|
|
3303
|
+
if (eventRefreshTimerRef.current) {
|
|
3304
|
+
clearTimeout(eventRefreshTimerRef.current);
|
|
3305
|
+
eventRefreshTimerRef.current = null;
|
|
3080
3306
|
}
|
|
3081
3307
|
return;
|
|
3082
3308
|
}
|
|
3083
3309
|
let latest = lastChangeRef.current;
|
|
3084
|
-
let
|
|
3085
|
-
let commandDelta = false;
|
|
3310
|
+
let changed = false;
|
|
3086
3311
|
for (const event of events) {
|
|
3087
3312
|
if (event.sequence <= latest) continue;
|
|
3088
|
-
if (event.type === "git.changed"
|
|
3089
|
-
latest = event.sequence;
|
|
3090
|
-
immediate = true;
|
|
3091
|
-
} else if (event.type === "sandbox.command.output.delta") {
|
|
3313
|
+
if (event.type === "git.changed") {
|
|
3092
3314
|
latest = event.sequence;
|
|
3093
|
-
|
|
3315
|
+
changed = true;
|
|
3094
3316
|
}
|
|
3095
3317
|
}
|
|
3096
3318
|
if (latest > lastChangeRef.current) {
|
|
3097
3319
|
lastChangeRef.current = latest;
|
|
3098
|
-
if (
|
|
3099
|
-
|
|
3100
|
-
|
|
3320
|
+
if (!changed) return;
|
|
3321
|
+
if (eventRefreshTimerRef.current) clearTimeout(eventRefreshTimerRef.current);
|
|
3322
|
+
eventRefreshTimerRef.current = setTimeout(() => {
|
|
3323
|
+
eventRefreshTimerRef.current = null;
|
|
3101
3324
|
if (acceptsEventReadsRef.current) void refresh();
|
|
3102
|
-
}
|
|
3103
|
-
commandRefreshTimerRef.current = setTimeout(() => {
|
|
3104
|
-
commandRefreshTimerRef.current = null;
|
|
3105
|
-
if (acceptsEventReadsRef.current) void refresh();
|
|
3106
|
-
}, 1e3);
|
|
3107
|
-
}
|
|
3325
|
+
}, 150);
|
|
3108
3326
|
}
|
|
3109
3327
|
}, [enabled, active, acceptsEventReads, events, refresh]);
|
|
3110
3328
|
useEffect7(
|
|
3111
3329
|
() => () => {
|
|
3112
|
-
if (
|
|
3330
|
+
if (eventRefreshTimerRef.current) clearTimeout(eventRefreshTimerRef.current);
|
|
3113
3331
|
},
|
|
3114
3332
|
[]
|
|
3115
3333
|
);
|
|
@@ -3132,7 +3350,7 @@ function useSandboxGit(sessionId, options = {}) {
|
|
|
3132
3350
|
}
|
|
3133
3351
|
|
|
3134
3352
|
// src/hooks/use-workspace-capture.ts
|
|
3135
|
-
import { useCallback as
|
|
3353
|
+
import { useCallback as useCallback17, useEffect as useEffect8, useRef as useRef7, useState as useState6 } from "react";
|
|
3136
3354
|
var REVISION_CAPTURED = "workspace.revision.captured";
|
|
3137
3355
|
var REVISION_DEGRADED = "workspace.revision.degraded";
|
|
3138
3356
|
var MANIFEST_FETCH_TIMEOUT_MS = 15e3;
|
|
@@ -3182,7 +3400,7 @@ function useWorkspaceCapture(sessionId, options = {}) {
|
|
|
3182
3400
|
const generationRef = useRef7(0);
|
|
3183
3401
|
const requestAbortRef = useRef7(null);
|
|
3184
3402
|
const lastSeqRef = useRef7(0);
|
|
3185
|
-
const refresh =
|
|
3403
|
+
const refresh = useCallback17(async () => {
|
|
3186
3404
|
if (!sessionId) return;
|
|
3187
3405
|
requestAbortRef.current?.abort();
|
|
3188
3406
|
const requestAbort = new AbortController();
|
|
@@ -3327,7 +3545,7 @@ function useWorkspaceCapture(sessionId, options = {}) {
|
|
|
3327
3545
|
}
|
|
3328
3546
|
|
|
3329
3547
|
// src/hooks/use-workspace-edit.ts
|
|
3330
|
-
import { useCallback as
|
|
3548
|
+
import { useCallback as useCallback18, useEffect as useEffect9, useRef as useRef8, useState as useState7 } from "react";
|
|
3331
3549
|
function isLive(liveness) {
|
|
3332
3550
|
return sandboxAcceptsLiveIo(liveness);
|
|
3333
3551
|
}
|
|
@@ -3370,7 +3588,7 @@ function useWorkspaceEdit(sessionId, options = {}) {
|
|
|
3370
3588
|
readAbortRef.current = null;
|
|
3371
3589
|
};
|
|
3372
3590
|
}, [identityKey]);
|
|
3373
|
-
const edit =
|
|
3591
|
+
const edit = useCallback18(
|
|
3374
3592
|
(content) => {
|
|
3375
3593
|
if (stateIdentity !== identityKey) return;
|
|
3376
3594
|
if (offline) return;
|
|
@@ -3386,7 +3604,7 @@ function useWorkspaceEdit(sessionId, options = {}) {
|
|
|
3386
3604
|
},
|
|
3387
3605
|
[offline, live, onWarmRequested, stateIdentity, identityKey]
|
|
3388
3606
|
);
|
|
3389
|
-
const doFlush =
|
|
3607
|
+
const doFlush = useCallback18(
|
|
3390
3608
|
async (force) => {
|
|
3391
3609
|
if (stateIdentity !== identityKey) return;
|
|
3392
3610
|
if (!sessionId || !path) return;
|
|
@@ -3443,10 +3661,10 @@ function useWorkspaceEdit(sessionId, options = {}) {
|
|
|
3443
3661
|
if (flush === "flushed" || flush === "conflict" || flush === "flushing") return;
|
|
3444
3662
|
void doFlush(false);
|
|
3445
3663
|
}, [offline, live, buffer, flush, doFlush]);
|
|
3446
|
-
const overwrite =
|
|
3664
|
+
const overwrite = useCallback18(async () => {
|
|
3447
3665
|
await doFlush(true);
|
|
3448
3666
|
}, [doFlush]);
|
|
3449
|
-
const discard =
|
|
3667
|
+
const discard = useCallback18(() => {
|
|
3450
3668
|
setBuffer(null);
|
|
3451
3669
|
setWantsWarm(false);
|
|
3452
3670
|
setFlush("idle");
|
|
@@ -3486,7 +3704,7 @@ function useWorkspaceEdit(sessionId, options = {}) {
|
|
|
3486
3704
|
}
|
|
3487
3705
|
|
|
3488
3706
|
// src/hooks/use-machine-chip.ts
|
|
3489
|
-
import { useMemo as
|
|
3707
|
+
import { useMemo as useMemo8 } from "react";
|
|
3490
3708
|
function formatAsOf(capturedAt, now) {
|
|
3491
3709
|
const then = Date.parse(capturedAt);
|
|
3492
3710
|
if (Number.isNaN(then)) return "recently";
|
|
@@ -3502,17 +3720,29 @@ function formatAsOf(capturedAt, now) {
|
|
|
3502
3720
|
function deriveMachineChip(input) {
|
|
3503
3721
|
const now = input.now ?? Date.now();
|
|
3504
3722
|
const asOf = input.capturedAt ?? null;
|
|
3505
|
-
const
|
|
3723
|
+
const managedRestingLabel = asOf ? `Sleeping \xB7 saved ${formatAsOf(asOf, now)}` : "Sleeping";
|
|
3724
|
+
const offlineLabel = "Offline";
|
|
3506
3725
|
const selfhostedOnline = input.activeIsSelfhosted === true && input.activeMachineState != null && connectionStatusForState(input.activeMachineState) === "online";
|
|
3507
3726
|
if (sandboxAcceptsLiveIo(input.liveness) || selfhostedOnline) {
|
|
3508
3727
|
return { state: "live", label: "Live", asOf: null };
|
|
3509
3728
|
}
|
|
3510
3729
|
const selfhostedOffline = input.activeIsSelfhosted === true && input.activeMachineState === "offline";
|
|
3730
|
+
if (input.capabilitiesState === "error") {
|
|
3731
|
+
return {
|
|
3732
|
+
state: "offline",
|
|
3733
|
+
label: input.activeIsSelfhosted === true ? offlineLabel : managedRestingLabel,
|
|
3734
|
+
asOf
|
|
3735
|
+
};
|
|
3736
|
+
}
|
|
3511
3737
|
const machineWarming = input.activeMachineState === "reconnecting" || input.activeMachineState === "enrolling";
|
|
3512
3738
|
if (!selfhostedOffline && (input.capabilitiesState === "negotiating" || input.wantsWarm === true || machineWarming)) {
|
|
3513
3739
|
return { state: "waking", label: "Waking\u2026", asOf };
|
|
3514
3740
|
}
|
|
3515
|
-
return {
|
|
3741
|
+
return {
|
|
3742
|
+
state: "offline",
|
|
3743
|
+
label: input.activeIsSelfhosted === true ? offlineLabel : managedRestingLabel,
|
|
3744
|
+
asOf
|
|
3745
|
+
};
|
|
3516
3746
|
}
|
|
3517
3747
|
function useMachineChip(input) {
|
|
3518
3748
|
const {
|
|
@@ -3524,7 +3754,7 @@ function useMachineChip(input) {
|
|
|
3524
3754
|
capturedAt,
|
|
3525
3755
|
now
|
|
3526
3756
|
} = input;
|
|
3527
|
-
return
|
|
3757
|
+
return useMemo8(
|
|
3528
3758
|
() => deriveMachineChip({
|
|
3529
3759
|
liveness,
|
|
3530
3760
|
capabilitiesState,
|
|
@@ -3805,7 +4035,7 @@ function ChatComposer({
|
|
|
3805
4035
|
{
|
|
3806
4036
|
layout: true,
|
|
3807
4037
|
transition: { duration: 0.36, ease: [0.22, 1, 0.36, 1] },
|
|
3808
|
-
className: "inline-flex min-w-0 items-center gap-1.5",
|
|
4038
|
+
className: "inline-flex min-w-0 flex-1 items-center gap-1.5",
|
|
3809
4039
|
children: controlsStart
|
|
3810
4040
|
}
|
|
3811
4041
|
) : null
|
|
@@ -4468,11 +4698,11 @@ import {
|
|
|
4468
4698
|
Trash2Icon
|
|
4469
4699
|
} from "lucide-react";
|
|
4470
4700
|
import {
|
|
4471
|
-
useCallback as
|
|
4701
|
+
useCallback as useCallback19,
|
|
4472
4702
|
useEffect as useEffect13,
|
|
4473
4703
|
useId as useId2,
|
|
4474
4704
|
useLayoutEffect as useLayoutEffect2,
|
|
4475
|
-
useMemo as
|
|
4705
|
+
useMemo as useMemo10,
|
|
4476
4706
|
useRef as useRef11,
|
|
4477
4707
|
useState as useState10
|
|
4478
4708
|
} from "react";
|
|
@@ -4480,11 +4710,11 @@ import { AlertDialog } from "radix-ui";
|
|
|
4480
4710
|
import { VList } from "virtua";
|
|
4481
4711
|
|
|
4482
4712
|
// src/lib/use-unicode-fonts.ts
|
|
4483
|
-
import { useEffect as useEffect12, useMemo as
|
|
4713
|
+
import { useEffect as useEffect12, useMemo as useMemo9 } from "react";
|
|
4484
4714
|
var japaneseScript = /[\u3040-\u30ff\u3400-\u4dbf\u4e00-\u9fff\uf900-\ufaff]/u;
|
|
4485
4715
|
var arabicScript = /[\u0600-\u06ff\u0750-\u077f\u0870-\u08ff\ufb50-\ufdff\ufe70-\ufefc]/u;
|
|
4486
4716
|
function useUnicodeFallbackFonts(values) {
|
|
4487
|
-
const content =
|
|
4717
|
+
const content = useMemo9(() => collectUnicodeContent(values), [values]);
|
|
4488
4718
|
useEffect12(() => {
|
|
4489
4719
|
if (content.japanese) {
|
|
4490
4720
|
void import("./noto-sans-jp-loader.generated-HFGLYBR3.js").then(({ loadNotoSansJp }) => loadNotoSansJp(content.japanese)).catch(() => void 0);
|
|
@@ -4535,21 +4765,49 @@ function itemKey(item) {
|
|
|
4535
4765
|
return `residue:${item.path}`;
|
|
4536
4766
|
}
|
|
4537
4767
|
}
|
|
4768
|
+
function pathIsInsideDirectory(path, directory) {
|
|
4769
|
+
if (!path.startsWith(directory) || path === directory) return false;
|
|
4770
|
+
if (directory.endsWith("/") || directory.endsWith("\\")) return true;
|
|
4771
|
+
const boundary = path[directory.length];
|
|
4772
|
+
return boundary === "/" || boundary === "\\";
|
|
4773
|
+
}
|
|
4538
4774
|
function findNode(nodes, path) {
|
|
4539
4775
|
for (const node of nodes) {
|
|
4540
4776
|
if (node.path === path) return node;
|
|
4541
|
-
if (node.kind === "dir" && node.children && path
|
|
4777
|
+
if (node.kind === "dir" && node.children && pathIsInsideDirectory(path, node.path)) {
|
|
4542
4778
|
const hit = findNode(node.children, path);
|
|
4543
4779
|
if (hit) return hit;
|
|
4544
4780
|
}
|
|
4545
4781
|
}
|
|
4546
4782
|
return void 0;
|
|
4547
4783
|
}
|
|
4784
|
+
function traceRevealPath(nodes, targetPath) {
|
|
4785
|
+
for (const node of nodes) {
|
|
4786
|
+
if (node.path === targetPath) {
|
|
4787
|
+
return {
|
|
4788
|
+
directories: node.kind === "dir" ? [node.path] : [],
|
|
4789
|
+
frontier: node.kind === "dir" && node.children === void 0 ? node.path : null
|
|
4790
|
+
};
|
|
4791
|
+
}
|
|
4792
|
+
if (node.kind !== "dir" || !pathIsInsideDirectory(targetPath, node.path)) continue;
|
|
4793
|
+
if (node.children === void 0) {
|
|
4794
|
+
return { directories: [node.path], frontier: node.path };
|
|
4795
|
+
}
|
|
4796
|
+
const child = traceRevealPath(node.children, targetPath);
|
|
4797
|
+
return {
|
|
4798
|
+
directories: [node.path, ...child.directories],
|
|
4799
|
+
frontier: child.frontier
|
|
4800
|
+
};
|
|
4801
|
+
}
|
|
4802
|
+
return { directories: [], frontier: null };
|
|
4803
|
+
}
|
|
4548
4804
|
function FileBrowser({
|
|
4549
4805
|
result,
|
|
4550
4806
|
fallback,
|
|
4551
4807
|
onSelectFile,
|
|
4552
4808
|
selectedPath,
|
|
4809
|
+
revealPath,
|
|
4810
|
+
revealPathRequestId,
|
|
4553
4811
|
renderNode,
|
|
4554
4812
|
emptyState,
|
|
4555
4813
|
editable = true,
|
|
@@ -4566,13 +4824,16 @@ function FileBrowser({
|
|
|
4566
4824
|
const [pendingDelete, setPendingDelete] = useState10(null);
|
|
4567
4825
|
const [busy, setBusy] = useState10(false);
|
|
4568
4826
|
const [coarsePointer, setCoarsePointer] = useState10(false);
|
|
4827
|
+
const [revealRequest, setRevealRequest] = useState10(null);
|
|
4828
|
+
const handledRevealRequestRef = useRef11(null);
|
|
4829
|
+
const attemptedRevealPathsRef = useRef11(/* @__PURE__ */ new Set());
|
|
4569
4830
|
const treeId = useId2();
|
|
4570
4831
|
const container = usePortalTokenSource();
|
|
4571
4832
|
const containerRef = container.currentRef;
|
|
4572
4833
|
const deleteReturnFocusRef = useRef11(null);
|
|
4573
4834
|
const vlistRef = useRef11(null);
|
|
4574
4835
|
const portalTokenStyle = usePortalTokenStyle(container.source);
|
|
4575
|
-
const getTreeElement =
|
|
4836
|
+
const getTreeElement = useCallback19(
|
|
4576
4837
|
() => typeof document === "undefined" ? null : document.getElementById(treeId),
|
|
4577
4838
|
[treeId]
|
|
4578
4839
|
);
|
|
@@ -4590,8 +4851,24 @@ function FileBrowser({
|
|
|
4590
4851
|
return () => query.removeListener(update);
|
|
4591
4852
|
}
|
|
4592
4853
|
}, []);
|
|
4854
|
+
const effectiveRevealRequestId = revealPath ? revealPathRequestId ?? revealPath : null;
|
|
4855
|
+
useEffect13(() => {
|
|
4856
|
+
if (!revealPath || effectiveRevealRequestId === null) {
|
|
4857
|
+
setRevealRequest(null);
|
|
4858
|
+
return;
|
|
4859
|
+
}
|
|
4860
|
+
const handled = handledRevealRequestRef.current;
|
|
4861
|
+
if (handled?.path === revealPath && handled.requestId === effectiveRevealRequestId) return;
|
|
4862
|
+
handledRevealRequestRef.current = {
|
|
4863
|
+
path: revealPath,
|
|
4864
|
+
requestId: effectiveRevealRequestId
|
|
4865
|
+
};
|
|
4866
|
+
attemptedRevealPathsRef.current = /* @__PURE__ */ new Set();
|
|
4867
|
+
setActive(null);
|
|
4868
|
+
setRevealRequest({ path: revealPath, requestId: effectiveRevealRequestId });
|
|
4869
|
+
}, [effectiveRevealRequestId, revealPath]);
|
|
4593
4870
|
const cursor = active ?? selectedPath ?? null;
|
|
4594
|
-
const expand =
|
|
4871
|
+
const expand = useCallback19(
|
|
4595
4872
|
async (path) => {
|
|
4596
4873
|
const node = findNode(result.tree, path);
|
|
4597
4874
|
if (node && node.kind === "dir" && node.children === void 0) {
|
|
@@ -4609,14 +4886,36 @@ function FileBrowser({
|
|
|
4609
4886
|
},
|
|
4610
4887
|
[result]
|
|
4611
4888
|
);
|
|
4612
|
-
const open =
|
|
4889
|
+
const open = useCallback19(
|
|
4613
4890
|
(path) => {
|
|
4614
4891
|
setExpanded((prev) => new Set(prev).add(path));
|
|
4615
4892
|
void expand(path);
|
|
4616
4893
|
},
|
|
4617
4894
|
[expand]
|
|
4618
4895
|
);
|
|
4619
|
-
|
|
4896
|
+
useEffect13(() => {
|
|
4897
|
+
if (!revealRequest) return;
|
|
4898
|
+
const trace = traceRevealPath(result.tree, revealRequest.path);
|
|
4899
|
+
if (trace.directories.length > 0) {
|
|
4900
|
+
setExpanded((previous) => {
|
|
4901
|
+
const next = new Set(previous);
|
|
4902
|
+
let changed = false;
|
|
4903
|
+
for (const path of trace.directories) {
|
|
4904
|
+
if (next.has(path)) continue;
|
|
4905
|
+
next.add(path);
|
|
4906
|
+
changed = true;
|
|
4907
|
+
}
|
|
4908
|
+
return changed ? next : previous;
|
|
4909
|
+
});
|
|
4910
|
+
}
|
|
4911
|
+
const frontier = trace.frontier;
|
|
4912
|
+
if (!frontier || attemptedRevealPathsRef.current.has(frontier) || loadingPaths.has(frontier) || result.expandingPaths.has(frontier)) {
|
|
4913
|
+
return;
|
|
4914
|
+
}
|
|
4915
|
+
attemptedRevealPathsRef.current.add(frontier);
|
|
4916
|
+
void expand(frontier);
|
|
4917
|
+
}, [expand, loadingPaths, result.expandingPaths, result.tree, revealRequest]);
|
|
4918
|
+
const toggle = useCallback19(
|
|
4620
4919
|
async (node) => {
|
|
4621
4920
|
if (node.kind !== "dir") return;
|
|
4622
4921
|
const isOpen = expanded.has(node.path);
|
|
@@ -4631,7 +4930,7 @@ function FileBrowser({
|
|
|
4631
4930
|
[expanded, expand]
|
|
4632
4931
|
);
|
|
4633
4932
|
const cold = result.source !== "live";
|
|
4634
|
-
const renderItems =
|
|
4933
|
+
const renderItems = useMemo10(() => {
|
|
4635
4934
|
const items = [];
|
|
4636
4935
|
if (draftCreate && draftCreate.parent === "") {
|
|
4637
4936
|
items.push({ type: "create", parent: "", kind: draftCreate.kind, depth: 0 });
|
|
@@ -4662,22 +4961,22 @@ function FileBrowser({
|
|
|
4662
4961
|
walk(result.tree, 0);
|
|
4663
4962
|
return items;
|
|
4664
4963
|
}, [result.tree, expanded, draftCreate, loadingPaths, result.expandingPaths, cold]);
|
|
4665
|
-
const unicodeFontPaths =
|
|
4964
|
+
const unicodeFontPaths = useMemo10(
|
|
4666
4965
|
() => renderItems.flatMap((item) => item.type === "node" ? [item.node.path] : []),
|
|
4667
4966
|
[renderItems]
|
|
4668
4967
|
);
|
|
4669
4968
|
useUnicodeFallbackFonts(unicodeFontPaths);
|
|
4670
|
-
const flatRows =
|
|
4969
|
+
const flatRows = useMemo10(
|
|
4671
4970
|
() => renderItems.flatMap(
|
|
4672
4971
|
(item) => item.type === "node" ? [{ node: item.node, depth: item.depth }] : []
|
|
4673
4972
|
),
|
|
4674
4973
|
[renderItems]
|
|
4675
4974
|
);
|
|
4676
|
-
const rowIndexByPath =
|
|
4975
|
+
const rowIndexByPath = useMemo10(
|
|
4677
4976
|
() => new Map(flatRows.map((row, index) => [row.node.path, index])),
|
|
4678
4977
|
[flatRows]
|
|
4679
4978
|
);
|
|
4680
|
-
const treePositionByPath =
|
|
4979
|
+
const treePositionByPath = useMemo10(() => {
|
|
4681
4980
|
const positions = /* @__PURE__ */ new Map();
|
|
4682
4981
|
const visit = (nodes) => {
|
|
4683
4982
|
nodes.forEach((node, index) => {
|
|
@@ -4691,7 +4990,7 @@ function FileBrowser({
|
|
|
4691
4990
|
const cursorIndex = cursor ? rowIndexByPath.get(cursor) ?? -1 : -1;
|
|
4692
4991
|
const activeDescendantId = cursorIndex >= 0 ? `${treeId}-item-${cursorIndex}` : void 0;
|
|
4693
4992
|
const supportsMutation = editable && result.source === "live" && result.error === null && !result.loading;
|
|
4694
|
-
const performDelete =
|
|
4993
|
+
const performDelete = useCallback19(
|
|
4695
4994
|
async (node) => {
|
|
4696
4995
|
if (!supportsMutation) return;
|
|
4697
4996
|
const recursive = node.kind === "dir";
|
|
@@ -4705,7 +5004,7 @@ function FileBrowser({
|
|
|
4705
5004
|
},
|
|
4706
5005
|
[supportsMutation, result]
|
|
4707
5006
|
);
|
|
4708
|
-
const runDelete =
|
|
5007
|
+
const runDelete = useCallback19(
|
|
4709
5008
|
async (node, returnFocus) => {
|
|
4710
5009
|
if (!supportsMutation) return;
|
|
4711
5010
|
setMenu(null);
|
|
@@ -4718,7 +5017,7 @@ function FileBrowser({
|
|
|
4718
5017
|
},
|
|
4719
5018
|
[supportsMutation, confirmDelete, performDelete]
|
|
4720
5019
|
);
|
|
4721
|
-
const closePendingDelete =
|
|
5020
|
+
const closePendingDelete = useCallback19(
|
|
4722
5021
|
(restoreTrigger) => {
|
|
4723
5022
|
const returnTarget = restoreTrigger ? deleteReturnFocusRef.current : getTreeElement();
|
|
4724
5023
|
deleteReturnFocusRef.current = null;
|
|
@@ -4732,7 +5031,7 @@ function FileBrowser({
|
|
|
4732
5031
|
},
|
|
4733
5032
|
[getTreeElement]
|
|
4734
5033
|
);
|
|
4735
|
-
const commitCreate =
|
|
5034
|
+
const commitCreate = useCallback19(
|
|
4736
5035
|
async (name) => {
|
|
4737
5036
|
const draft = draftCreate;
|
|
4738
5037
|
setDraftCreate(null);
|
|
@@ -4757,7 +5056,7 @@ function FileBrowser({
|
|
|
4757
5056
|
},
|
|
4758
5057
|
[draftCreate, supportsMutation, result, open, onSelectFile]
|
|
4759
5058
|
);
|
|
4760
|
-
const commitRename =
|
|
5059
|
+
const commitRename = useCallback19(
|
|
4761
5060
|
async (name) => {
|
|
4762
5061
|
const draft = draftRename;
|
|
4763
5062
|
setDraftRename(null);
|
|
@@ -4780,7 +5079,7 @@ function FileBrowser({
|
|
|
4780
5079
|
},
|
|
4781
5080
|
[draftRename, supportsMutation, result, selectedPath, active, onSelectFile]
|
|
4782
5081
|
);
|
|
4783
|
-
const startCreate =
|
|
5082
|
+
const startCreate = useCallback19(
|
|
4784
5083
|
(kind) => {
|
|
4785
5084
|
if (!supportsMutation) return;
|
|
4786
5085
|
const anchor = cursor ? findNode(result.tree, cursor) : void 0;
|
|
@@ -4792,7 +5091,7 @@ function FileBrowser({
|
|
|
4792
5091
|
},
|
|
4793
5092
|
[supportsMutation, cursor, result.tree, open]
|
|
4794
5093
|
);
|
|
4795
|
-
const startRename =
|
|
5094
|
+
const startRename = useCallback19(
|
|
4796
5095
|
(node) => {
|
|
4797
5096
|
if (!supportsMutation) return;
|
|
4798
5097
|
setDraftCreate(null);
|
|
@@ -4801,7 +5100,7 @@ function FileBrowser({
|
|
|
4801
5100
|
},
|
|
4802
5101
|
[supportsMutation]
|
|
4803
5102
|
);
|
|
4804
|
-
const onDropOnto =
|
|
5103
|
+
const onDropOnto = useCallback19(
|
|
4805
5104
|
async (targetDir, sourcePath) => {
|
|
4806
5105
|
setDragOver(null);
|
|
4807
5106
|
if (!supportsMutation || !sourcePath) return;
|
|
@@ -4825,7 +5124,7 @@ function FileBrowser({
|
|
|
4825
5124
|
},
|
|
4826
5125
|
[supportsMutation, result, selectedPath, active, onSelectFile]
|
|
4827
5126
|
);
|
|
4828
|
-
const onKeyDown =
|
|
5127
|
+
const onKeyDown = useCallback19(
|
|
4829
5128
|
(e) => {
|
|
4830
5129
|
if (draftCreate || draftRename) return;
|
|
4831
5130
|
if (flatRows.length === 0) return;
|
|
@@ -5130,6 +5429,21 @@ function FileBrowser({
|
|
|
5130
5429
|
const index = renderItems.findIndex((it) => it.type === "node" && it.node.path === cursor);
|
|
5131
5430
|
if (index >= 0) vlistRef.current?.scrollToIndex(index);
|
|
5132
5431
|
}, [cursor, renderItems]);
|
|
5432
|
+
useEffect13(() => {
|
|
5433
|
+
if (!revealRequest) return;
|
|
5434
|
+
const index = renderItems.findIndex(
|
|
5435
|
+
(item) => item.type === "node" && item.node.path === revealRequest.path
|
|
5436
|
+
);
|
|
5437
|
+
if (index < 0) return;
|
|
5438
|
+
if (usesVirtualTree) {
|
|
5439
|
+
vlistRef.current?.scrollToIndex(index);
|
|
5440
|
+
} else {
|
|
5441
|
+
document.getElementById(`${treeId}-item-${index}`)?.scrollIntoView?.({ block: "nearest" });
|
|
5442
|
+
}
|
|
5443
|
+
setRevealRequest(
|
|
5444
|
+
(current) => current?.path === revealRequest.path && current.requestId === revealRequest.requestId ? null : current
|
|
5445
|
+
);
|
|
5446
|
+
}, [renderItems, revealRequest, treeId, usesVirtualTree]);
|
|
5133
5447
|
return /* @__PURE__ */ jsxs5("div", { className: cn("flex min-h-0 min-w-0 flex-col", className), children: [
|
|
5134
5448
|
showToolbar && /* @__PURE__ */ jsxs5(
|
|
5135
5449
|
"div",
|
|
@@ -5619,18 +5933,18 @@ function ContextMenu({
|
|
|
5619
5933
|
import { ArrowUpRightIcon, FileWarningIcon, HistoryIcon } from "lucide-react";
|
|
5620
5934
|
import {
|
|
5621
5935
|
Component,
|
|
5622
|
-
useCallback as
|
|
5936
|
+
useCallback as useCallback21,
|
|
5623
5937
|
useEffect as useEffect15,
|
|
5624
5938
|
useId as useId3,
|
|
5625
5939
|
useLayoutEffect as useLayoutEffect3,
|
|
5626
|
-
useMemo as
|
|
5940
|
+
useMemo as useMemo12,
|
|
5627
5941
|
useRef as useRef13,
|
|
5628
5942
|
useState as useState12
|
|
5629
5943
|
} from "react";
|
|
5630
5944
|
import { VList as VList2 } from "virtua";
|
|
5631
5945
|
|
|
5632
5946
|
// src/hooks/use-windowed-sections.ts
|
|
5633
|
-
import { useCallback as
|
|
5947
|
+
import { useCallback as useCallback20, useEffect as useEffect14, useMemo as useMemo11, useRef as useRef12, useState as useState11 } from "react";
|
|
5634
5948
|
function computeWindowRange(heights, scrollTop, viewport, overscan) {
|
|
5635
5949
|
const n = heights.length;
|
|
5636
5950
|
if (n === 0) return { start: 0, end: 0 };
|
|
@@ -5677,7 +5991,7 @@ function useWindowedSections(opts) {
|
|
|
5677
5991
|
start: 0,
|
|
5678
5992
|
end: Math.min(count, overscan + 1)
|
|
5679
5993
|
}));
|
|
5680
|
-
const recompute =
|
|
5994
|
+
const recompute = useCallback20(() => {
|
|
5681
5995
|
const el = scrollRef.current;
|
|
5682
5996
|
if (!el) return;
|
|
5683
5997
|
const next = computeWindowRange(heightsRef.current, el.scrollTop, el.clientHeight, overscan);
|
|
@@ -5704,7 +6018,7 @@ function useWindowedSections(opts) {
|
|
|
5704
6018
|
if (raf && typeof cancelAnimationFrame === "function") cancelAnimationFrame(raf);
|
|
5705
6019
|
};
|
|
5706
6020
|
}, [recompute, count]);
|
|
5707
|
-
const measure =
|
|
6021
|
+
const measure = useCallback20((index, height) => {
|
|
5708
6022
|
if (height <= 0) return;
|
|
5709
6023
|
setHeights((prev) => {
|
|
5710
6024
|
if (Math.abs((prev[index] ?? 0) - height) < 1) return prev;
|
|
@@ -5713,7 +6027,7 @@ function useWindowedSections(opts) {
|
|
|
5713
6027
|
return next;
|
|
5714
6028
|
});
|
|
5715
6029
|
}, []);
|
|
5716
|
-
const offsets =
|
|
6030
|
+
const offsets = useMemo11(() => {
|
|
5717
6031
|
const out = new Array(count + 1);
|
|
5718
6032
|
out[0] = 0;
|
|
5719
6033
|
for (let i = 0; i < count; i++) out[i + 1] = (out[i] ?? 0) + Math.max(heights[i] ?? 0, 0);
|
|
@@ -5722,7 +6036,7 @@ function useWindowedSections(opts) {
|
|
|
5722
6036
|
useEffect14(() => {
|
|
5723
6037
|
recompute();
|
|
5724
6038
|
}, [offsets, recompute]);
|
|
5725
|
-
const scrollToIndex =
|
|
6039
|
+
const scrollToIndex = useCallback20(
|
|
5726
6040
|
(index) => {
|
|
5727
6041
|
const el = scrollRef.current;
|
|
5728
6042
|
if (!el) return;
|
|
@@ -5931,12 +6245,12 @@ function WorkbenchChanges({
|
|
|
5931
6245
|
const [layout, setLayout] = useState12("unified");
|
|
5932
6246
|
const [activePath, setActivePath] = useState12(null);
|
|
5933
6247
|
const pickerId = useId3();
|
|
5934
|
-
const unicodeFontPaths =
|
|
6248
|
+
const unicodeFontPaths = useMemo12(
|
|
5935
6249
|
() => diff.flatMap((file) => file.oldPath ? [file.path, file.oldPath] : [file.path]),
|
|
5936
6250
|
[diff]
|
|
5937
6251
|
);
|
|
5938
6252
|
useUnicodeFallbackFonts(unicodeFontPaths);
|
|
5939
|
-
const { orderedFiles, rows, grouped } =
|
|
6253
|
+
const { orderedFiles, rows, grouped } = useMemo12(() => buildRail(diff), [diff]);
|
|
5940
6254
|
const selectedIndex = orderedFiles.findIndex((file) => file.path === activePath);
|
|
5941
6255
|
const activeIndex = selectedIndex >= 0 ? selectedIndex : 0;
|
|
5942
6256
|
useChangesLayoutEffect(() => {
|
|
@@ -5966,9 +6280,9 @@ function WorkbenchChanges({
|
|
|
5966
6280
|
return () => query.removeListener(update);
|
|
5967
6281
|
}
|
|
5968
6282
|
}, []);
|
|
5969
|
-
const additions =
|
|
5970
|
-
const deletions =
|
|
5971
|
-
const estimateHeight =
|
|
6283
|
+
const additions = useMemo12(() => diff.reduce((sum, f) => sum + f.additions, 0), [diff]);
|
|
6284
|
+
const deletions = useMemo12(() => diff.reduce((sum, f) => sum + f.deletions, 0), [diff]);
|
|
6285
|
+
const estimateHeight = useCallback21(
|
|
5972
6286
|
(index) => {
|
|
5973
6287
|
const file = orderedFiles[index];
|
|
5974
6288
|
return file ? estimateSectionHeight(file) : HEADER_PX + LINE_PX;
|
|
@@ -5980,14 +6294,14 @@ function WorkbenchChanges({
|
|
|
5980
6294
|
estimateHeight,
|
|
5981
6295
|
overscan: OVERSCAN
|
|
5982
6296
|
});
|
|
5983
|
-
const jumpTo =
|
|
6297
|
+
const jumpTo = useCallback21(
|
|
5984
6298
|
(index) => {
|
|
5985
6299
|
setActivePath(orderedFiles[index]?.path ?? null);
|
|
5986
6300
|
windowed.scrollToIndex(index);
|
|
5987
6301
|
},
|
|
5988
6302
|
[orderedFiles, windowed]
|
|
5989
6303
|
);
|
|
5990
|
-
const onPaneScroll =
|
|
6304
|
+
const onPaneScroll = useCallback21(() => {
|
|
5991
6305
|
const el = windowed.scrollRef.current;
|
|
5992
6306
|
if (!el) {
|
|
5993
6307
|
return;
|
|
@@ -6450,9 +6764,9 @@ function FileSkeleton() {
|
|
|
6450
6764
|
// src/components/code-editor.tsx
|
|
6451
6765
|
import { Loader2Icon, SaveIcon } from "lucide-react";
|
|
6452
6766
|
import {
|
|
6453
|
-
useCallback as
|
|
6767
|
+
useCallback as useCallback22,
|
|
6454
6768
|
useEffect as useEffect17,
|
|
6455
|
-
useMemo as
|
|
6769
|
+
useMemo as useMemo13,
|
|
6456
6770
|
useRef as useRef14,
|
|
6457
6771
|
useState as useState14
|
|
6458
6772
|
} from "react";
|
|
@@ -6549,7 +6863,7 @@ function CodeEditor({
|
|
|
6549
6863
|
setSaveError(null);
|
|
6550
6864
|
setSavedTick(false);
|
|
6551
6865
|
}, [path]);
|
|
6552
|
-
const noteEdit =
|
|
6866
|
+
const noteEdit = useCallback22(
|
|
6553
6867
|
(next) => {
|
|
6554
6868
|
setBuffer((current) => ({ ...current, value: next }));
|
|
6555
6869
|
setSavedTick(false);
|
|
@@ -6560,7 +6874,7 @@ function CodeEditor({
|
|
|
6560
6874
|
},
|
|
6561
6875
|
[baseline, onEditIntent]
|
|
6562
6876
|
);
|
|
6563
|
-
const langKey =
|
|
6877
|
+
const langKey = useMemo13(() => languageForPath(path), [path]);
|
|
6564
6878
|
useEffect17(() => {
|
|
6565
6879
|
let cancelled = false;
|
|
6566
6880
|
void (async () => {
|
|
@@ -6603,7 +6917,7 @@ function CodeEditor({
|
|
|
6603
6917
|
}, [langKey]);
|
|
6604
6918
|
const saveRef = useRef14(() => {
|
|
6605
6919
|
});
|
|
6606
|
-
const save =
|
|
6920
|
+
const save = useCallback22(async () => {
|
|
6607
6921
|
if (readOnly || saveInFlightRef.current) return;
|
|
6608
6922
|
const snapshot = value;
|
|
6609
6923
|
if (snapshot === baseline) return;
|
|
@@ -6624,7 +6938,7 @@ function CodeEditor({
|
|
|
6624
6938
|
setSaving(false);
|
|
6625
6939
|
}
|
|
6626
6940
|
}, [readOnly, value, baseline, onSave, path]);
|
|
6627
|
-
const overwrite =
|
|
6941
|
+
const overwrite = useCallback22(async () => {
|
|
6628
6942
|
if (readOnly || !onOverwrite || saveInFlightRef.current) return;
|
|
6629
6943
|
const snapshot = value;
|
|
6630
6944
|
if (snapshot === baseline) return;
|
|
@@ -6654,14 +6968,14 @@ function CodeEditor({
|
|
|
6654
6968
|
return () => clearTimeout(t);
|
|
6655
6969
|
}, [savedTick]);
|
|
6656
6970
|
const fileName = path.split("/").filter(Boolean).pop() ?? path;
|
|
6657
|
-
const cmExtensions =
|
|
6971
|
+
const cmExtensions = useMemo13(() => {
|
|
6658
6972
|
if (!bundle) return [];
|
|
6659
6973
|
const exts = [bundle.saveKeymapExtension(() => saveRef.current())];
|
|
6660
6974
|
if (bundle.languageExtension) exts.push(bundle.languageExtension);
|
|
6661
6975
|
return exts;
|
|
6662
6976
|
}, [bundle]);
|
|
6663
6977
|
const Editor = bundle?.Editor;
|
|
6664
|
-
const reload =
|
|
6978
|
+
const reload = useCallback22(() => {
|
|
6665
6979
|
setBuffer({ path, value: initialContents, baseline: initialContents });
|
|
6666
6980
|
editIntentFiredRef.current = false;
|
|
6667
6981
|
setSaveError(null);
|
|
@@ -6817,7 +7131,7 @@ function EditorSkeleton() {
|
|
|
6817
7131
|
|
|
6818
7132
|
// src/components/sandbox-files.tsx
|
|
6819
7133
|
import { FileCode2Icon, FileWarningIcon as FileWarningIcon2, LoaderCircleIcon } from "lucide-react";
|
|
6820
|
-
import { useCallback as
|
|
7134
|
+
import { useCallback as useCallback23, useEffect as useEffect18, useRef as useRef15, useState as useState15 } from "react";
|
|
6821
7135
|
import { Group, Panel, Separator } from "react-resizable-panels";
|
|
6822
7136
|
import { Fragment as Fragment3, jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
6823
7137
|
function SandboxFiles({
|
|
@@ -6827,7 +7141,10 @@ function SandboxFiles({
|
|
|
6827
7141
|
usePierre = true,
|
|
6828
7142
|
editable = true,
|
|
6829
7143
|
onEditIntent,
|
|
7144
|
+
initialSelectedPath,
|
|
7145
|
+
onSelectedPathChange,
|
|
6830
7146
|
requestedPath,
|
|
7147
|
+
requestedLine,
|
|
6831
7148
|
requestedPathRequestId,
|
|
6832
7149
|
requestedPathReady = true,
|
|
6833
7150
|
workspaceResting = false,
|
|
@@ -6837,10 +7154,13 @@ function SandboxFiles({
|
|
|
6837
7154
|
themeType,
|
|
6838
7155
|
className
|
|
6839
7156
|
}) {
|
|
6840
|
-
const [selected, setSelected] = useState15(null);
|
|
7157
|
+
const [selected, setSelected] = useState15(() => initialSelectedPath ?? null);
|
|
6841
7158
|
const [editMode, setEditMode] = useState15(false);
|
|
7159
|
+
const [focusLine, setFocusLine] = useState15(null);
|
|
7160
|
+
const [treeRevealRequest, setTreeRevealRequest] = useState15(null);
|
|
6842
7161
|
const [liveRequestedPath, setLiveRequestedPath] = useState15(null);
|
|
6843
7162
|
const [viewReloadRevision, setViewReloadRevision] = useState15(0);
|
|
7163
|
+
const viewerScrollRef = useRef15(null);
|
|
6844
7164
|
const pendingRequestRef = useRef15(null);
|
|
6845
7165
|
const handledRequestRef = useRef15(null);
|
|
6846
7166
|
const requestKey = requestedPath ? requestedPathRequestId ?? requestedPath : null;
|
|
@@ -6848,6 +7168,7 @@ function SandboxFiles({
|
|
|
6848
7168
|
if (!requestedPath || requestKey === null) {
|
|
6849
7169
|
pendingRequestRef.current = null;
|
|
6850
7170
|
handledRequestRef.current = null;
|
|
7171
|
+
setTreeRevealRequest(null);
|
|
6851
7172
|
return;
|
|
6852
7173
|
}
|
|
6853
7174
|
if (handledRequestRef.current === requestKey) {
|
|
@@ -6855,13 +7176,18 @@ function SandboxFiles({
|
|
|
6855
7176
|
}
|
|
6856
7177
|
if (!requestedPathReady) {
|
|
6857
7178
|
pendingRequestRef.current = requestKey;
|
|
7179
|
+
setTreeRevealRequest(null);
|
|
6858
7180
|
return;
|
|
6859
7181
|
}
|
|
6860
7182
|
handledRequestRef.current = requestKey;
|
|
6861
7183
|
pendingRequestRef.current = null;
|
|
6862
7184
|
setSelected(requestedPath);
|
|
7185
|
+
onSelectedPathChange?.(requestedPath);
|
|
6863
7186
|
setEditMode(false);
|
|
6864
|
-
|
|
7187
|
+
setFocusLine(requestedLine != null && requestedLine > 0 ? requestedLine : null);
|
|
7188
|
+
setTreeRevealRequest({ path: requestedPath, requestId: requestKey });
|
|
7189
|
+
setViewReloadRevision((revision) => revision + 1);
|
|
7190
|
+
}, [onSelectedPathChange, requestKey, requestedLine, requestedPath, requestedPathReady]);
|
|
6865
7191
|
const rootRef = useRef15(null);
|
|
6866
7192
|
const [wide, setWide] = useState15(false);
|
|
6867
7193
|
useEffect18(() => {
|
|
@@ -6888,18 +7214,29 @@ function SandboxFiles({
|
|
|
6888
7214
|
const fileView = useFileView(
|
|
6889
7215
|
viewPath,
|
|
6890
7216
|
files.readFile,
|
|
6891
|
-
|
|
7217
|
+
`${files.contentRevision ?? 0}:${viewReloadRevision}`
|
|
6892
7218
|
);
|
|
6893
|
-
|
|
6894
|
-
if (
|
|
6895
|
-
|
|
6896
|
-
pendingRequestRef.current = null;
|
|
7219
|
+
useEffect18(() => {
|
|
7220
|
+
if (focusLine === null && viewReloadRevision > 0 && viewerScrollRef.current) {
|
|
7221
|
+
viewerScrollRef.current.scrollTop = 0;
|
|
6897
7222
|
}
|
|
6898
|
-
|
|
6899
|
-
|
|
6900
|
-
|
|
6901
|
-
|
|
6902
|
-
|
|
7223
|
+
}, [focusLine, viewReloadRevision]);
|
|
7224
|
+
const selectFile = useCallback23(
|
|
7225
|
+
(path) => {
|
|
7226
|
+
if (pendingRequestRef.current !== null) {
|
|
7227
|
+
handledRequestRef.current = pendingRequestRef.current;
|
|
7228
|
+
pendingRequestRef.current = null;
|
|
7229
|
+
}
|
|
7230
|
+
setSelected(path);
|
|
7231
|
+
onSelectedPathChange?.(path);
|
|
7232
|
+
setEditMode(false);
|
|
7233
|
+
setFocusLine(null);
|
|
7234
|
+
setTreeRevealRequest(null);
|
|
7235
|
+
setLiveRequestedPath(null);
|
|
7236
|
+
setViewReloadRevision(0);
|
|
7237
|
+
},
|
|
7238
|
+
[onSelectedPathChange]
|
|
7239
|
+
);
|
|
6903
7240
|
const canEdit = editable && viewPath !== null && !fileView.loading && fileView.error === null && !fileView.isBinary && !fileView.truncated && fileView.content !== null;
|
|
6904
7241
|
const showEditor = canEdit && editMode;
|
|
6905
7242
|
const captureFileUnavailable = fileView.error instanceof CapturedFileUnavailableError ? fileView.error : null;
|
|
@@ -6970,6 +7307,10 @@ function SandboxFiles({
|
|
|
6970
7307
|
{
|
|
6971
7308
|
result: files,
|
|
6972
7309
|
selectedPath: selected ?? void 0,
|
|
7310
|
+
...treeRevealRequest ? {
|
|
7311
|
+
revealPath: treeRevealRequest.path,
|
|
7312
|
+
revealPathRequestId: treeRevealRequest.requestId
|
|
7313
|
+
} : {},
|
|
6973
7314
|
onSelectFile: selectFile,
|
|
6974
7315
|
editable,
|
|
6975
7316
|
emptyState: "This directory is empty",
|
|
@@ -7003,7 +7344,7 @@ function SandboxFiles({
|
|
|
7003
7344
|
{
|
|
7004
7345
|
"data-opengeni-selected-file": true,
|
|
7005
7346
|
className: "min-w-0 truncate font-og-mono text-og-xs text-og-fg-muted",
|
|
7006
|
-
children: selected
|
|
7347
|
+
children: selected ? focusLine !== null ? `${selected}:${focusLine}` : selected : "No file selected"
|
|
7007
7348
|
}
|
|
7008
7349
|
),
|
|
7009
7350
|
canEdit && /* @__PURE__ */ jsx10(
|
|
@@ -7018,106 +7359,122 @@ function SandboxFiles({
|
|
|
7018
7359
|
}
|
|
7019
7360
|
)
|
|
7020
7361
|
] }),
|
|
7021
|
-
/* @__PURE__ */ jsx10(
|
|
7022
|
-
|
|
7023
|
-
{
|
|
7024
|
-
path: viewPath,
|
|
7025
|
-
initialContents: fileView.content,
|
|
7026
|
-
themeType: resolvedTheme,
|
|
7027
|
-
onSave: (contents, expectedContents) => files.writeFile(viewPath, contents, { expectedContent: expectedContents }),
|
|
7028
|
-
onOverwrite: (contents) => files.writeFile(viewPath, contents, { force: true }),
|
|
7029
|
-
onReload: () => setViewReloadRevision((revision) => revision + 1),
|
|
7030
|
-
...onEditIntent ? { onEditIntent } : {},
|
|
7031
|
-
className: "h-full"
|
|
7032
|
-
},
|
|
7033
|
-
viewPath
|
|
7034
|
-
) : waitingForSelectedFile ? /* @__PURE__ */ jsxs8(
|
|
7035
|
-
Notice,
|
|
7362
|
+
/* @__PURE__ */ jsx10(
|
|
7363
|
+
"div",
|
|
7036
7364
|
{
|
|
7037
|
-
|
|
7038
|
-
|
|
7365
|
+
ref: viewerScrollRef,
|
|
7366
|
+
className: "min-h-0 flex-1 overflow-auto",
|
|
7367
|
+
"data-opengeni-file-viewer-scroll": true,
|
|
7368
|
+
children: viewPath ? showEditor && fileView.content !== null ? /* @__PURE__ */ jsx10(
|
|
7369
|
+
CodeEditor,
|
|
7370
|
+
{
|
|
7371
|
+
path: viewPath,
|
|
7372
|
+
initialContents: fileView.content,
|
|
7373
|
+
themeType: resolvedTheme,
|
|
7374
|
+
onSave: (contents, expectedContents) => files.writeFile(viewPath, contents, { expectedContent: expectedContents }),
|
|
7375
|
+
onOverwrite: (contents) => files.writeFile(viewPath, contents, { force: true }),
|
|
7376
|
+
onReload: () => setViewReloadRevision((revision) => revision + 1),
|
|
7377
|
+
...onEditIntent ? { onEditIntent } : {},
|
|
7378
|
+
className: "h-full"
|
|
7379
|
+
},
|
|
7380
|
+
viewPath
|
|
7381
|
+
) : waitingForSelectedFile ? /* @__PURE__ */ jsxs8(
|
|
7382
|
+
Notice,
|
|
7039
7383
|
{
|
|
7040
|
-
|
|
7041
|
-
|
|
7384
|
+
icon: /* @__PURE__ */ jsx10(
|
|
7385
|
+
LoaderCircleIcon,
|
|
7386
|
+
{
|
|
7387
|
+
className: "size-5 animate-spin motion-reduce:animate-none",
|
|
7388
|
+
"aria-hidden": true
|
|
7389
|
+
}
|
|
7390
|
+
),
|
|
7391
|
+
title: "Waking workspace",
|
|
7392
|
+
announce: "status",
|
|
7393
|
+
children: [
|
|
7394
|
+
"Opening ",
|
|
7395
|
+
viewPath,
|
|
7396
|
+
" when the live file system is ready\u2026"
|
|
7397
|
+
]
|
|
7042
7398
|
}
|
|
7043
|
-
),
|
|
7044
|
-
|
|
7045
|
-
|
|
7046
|
-
|
|
7047
|
-
|
|
7399
|
+
) : captureFileUnavailable ? /* @__PURE__ */ jsxs8(Notice, { icon: /* @__PURE__ */ jsx10(FileCode2Icon, { className: "size-5", "aria-hidden": true }), title: "On machine", children: [
|
|
7400
|
+
/* @__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." }),
|
|
7401
|
+
onWakeWorkspace ? /* @__PURE__ */ jsx10(
|
|
7402
|
+
WakeButton,
|
|
7403
|
+
{
|
|
7404
|
+
onClick: () => {
|
|
7405
|
+
setLiveRequestedPath(viewPath);
|
|
7406
|
+
if (liveWorkspaceReady) void files.refresh();
|
|
7407
|
+
else onWakeWorkspace();
|
|
7408
|
+
},
|
|
7409
|
+
children: liveWorkspaceReady ? "Retry live file" : "Open live file"
|
|
7410
|
+
}
|
|
7411
|
+
) : null
|
|
7412
|
+
] }) : fileView.error ? /* @__PURE__ */ jsxs8(Notice, { announce: "alert", children: [
|
|
7413
|
+
"Could not open ",
|
|
7048
7414
|
viewPath,
|
|
7049
|
-
"
|
|
7050
|
-
|
|
7051
|
-
|
|
7052
|
-
|
|
7053
|
-
|
|
7054
|
-
|
|
7055
|
-
|
|
7056
|
-
|
|
7057
|
-
|
|
7058
|
-
|
|
7059
|
-
|
|
7060
|
-
|
|
7061
|
-
|
|
7062
|
-
|
|
7063
|
-
|
|
7064
|
-
|
|
7065
|
-
|
|
7066
|
-
|
|
7067
|
-
|
|
7068
|
-
": ",
|
|
7069
|
-
fileView.error.message
|
|
7070
|
-
] }) : fileView.loading ? /* @__PURE__ */ jsxs8(Notice, { announce: "status", children: [
|
|
7071
|
-
"Loading ",
|
|
7072
|
-
viewPath,
|
|
7073
|
-
"\u2026"
|
|
7074
|
-
] }) : fileView.isBinary ? /* @__PURE__ */ jsxs8(Notice, { children: [
|
|
7075
|
-
viewPath,
|
|
7076
|
-
" is a binary file (",
|
|
7077
|
-
fileView.sizeBytes ?? 0,
|
|
7078
|
-
" bytes)."
|
|
7079
|
-
] }) : fileView.content !== null ? /* @__PURE__ */ jsxs8(Fragment3, { children: [
|
|
7080
|
-
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: [
|
|
7081
|
-
"Large file \u2014 showing a truncated preview (",
|
|
7082
|
-
fileView.sizeBytes ?? 0,
|
|
7083
|
-
" bytes loaded). Editing is disabled to avoid corrupting the file."
|
|
7084
|
-
] }),
|
|
7085
|
-
usePierre ? /* @__PURE__ */ jsx10(
|
|
7086
|
-
PierreFile,
|
|
7087
|
-
{
|
|
7088
|
-
path: viewPath,
|
|
7089
|
-
contents: fileView.content,
|
|
7090
|
-
themeType: resolvedTheme,
|
|
7091
|
-
fallback: /* @__PURE__ */ jsx10("pre", { className: "overflow-auto whitespace-pre p-2 font-og-mono text-og-sm text-og-fg", children: fileView.content }),
|
|
7092
|
-
className: "p-1"
|
|
7093
|
-
}
|
|
7094
|
-
) : /* @__PURE__ */ jsx10("pre", { className: "overflow-auto whitespace-pre p-2 font-og-mono text-og-sm text-og-fg", children: fileView.content })
|
|
7095
|
-
] }) : /* @__PURE__ */ jsxs8(Notice, { announce: "status", children: [
|
|
7096
|
-
"Loading ",
|
|
7097
|
-
viewPath,
|
|
7098
|
-
"\u2026"
|
|
7099
|
-
] }) : (
|
|
7100
|
-
// Nothing selected — the tree shows the whole workspace; pick a file.
|
|
7101
|
-
requestedPath && !requestedPathReady ? /* @__PURE__ */ jsxs8(
|
|
7102
|
-
Notice,
|
|
7103
|
-
{
|
|
7104
|
-
icon: /* @__PURE__ */ jsx10(
|
|
7105
|
-
LoaderCircleIcon,
|
|
7415
|
+
": ",
|
|
7416
|
+
fileView.error.message
|
|
7417
|
+
] }) : fileView.loading ? /* @__PURE__ */ jsxs8(Notice, { announce: "status", children: [
|
|
7418
|
+
"Loading ",
|
|
7419
|
+
viewPath,
|
|
7420
|
+
"\u2026"
|
|
7421
|
+
] }) : fileView.isBinary ? /* @__PURE__ */ jsxs8(Notice, { children: [
|
|
7422
|
+
viewPath,
|
|
7423
|
+
" is a binary file (",
|
|
7424
|
+
fileView.sizeBytes ?? 0,
|
|
7425
|
+
" bytes)."
|
|
7426
|
+
] }) : fileView.content !== null ? /* @__PURE__ */ jsxs8(Fragment3, { children: [
|
|
7427
|
+
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: [
|
|
7428
|
+
"Large file \u2014 showing a truncated preview (",
|
|
7429
|
+
fileView.sizeBytes ?? 0,
|
|
7430
|
+
" bytes loaded). Editing is disabled to avoid corrupting the file."
|
|
7431
|
+
] }),
|
|
7432
|
+
usePierre && focusLine === null ? /* @__PURE__ */ jsx10(
|
|
7433
|
+
PierreFile,
|
|
7106
7434
|
{
|
|
7107
|
-
|
|
7108
|
-
|
|
7435
|
+
path: viewPath,
|
|
7436
|
+
contents: fileView.content,
|
|
7437
|
+
themeType: resolvedTheme,
|
|
7438
|
+
fallback: /* @__PURE__ */ jsx10("pre", { className: "overflow-auto whitespace-pre p-2 font-og-mono text-og-sm text-og-fg", children: fileView.content }),
|
|
7439
|
+
className: "p-1"
|
|
7440
|
+
},
|
|
7441
|
+
`${viewPath}:${viewReloadRevision}`
|
|
7442
|
+
) : focusLine !== null ? /* @__PURE__ */ jsx10(
|
|
7443
|
+
LineNumberedFile,
|
|
7444
|
+
{
|
|
7445
|
+
path: viewPath,
|
|
7446
|
+
contents: fileView.content,
|
|
7447
|
+
focusLine
|
|
7109
7448
|
}
|
|
7110
|
-
),
|
|
7111
|
-
|
|
7112
|
-
|
|
7113
|
-
|
|
7114
|
-
|
|
7115
|
-
|
|
7116
|
-
|
|
7117
|
-
|
|
7118
|
-
|
|
7119
|
-
|
|
7120
|
-
|
|
7449
|
+
) : /* @__PURE__ */ jsx10("pre", { className: "overflow-auto whitespace-pre p-2 font-og-mono text-og-sm text-og-fg", children: fileView.content })
|
|
7450
|
+
] }) : /* @__PURE__ */ jsxs8(Notice, { announce: "status", children: [
|
|
7451
|
+
"Loading ",
|
|
7452
|
+
viewPath,
|
|
7453
|
+
"\u2026"
|
|
7454
|
+
] }) : (
|
|
7455
|
+
// Nothing selected — the tree shows the whole workspace; pick a file.
|
|
7456
|
+
requestedPath && !requestedPathReady ? /* @__PURE__ */ jsxs8(
|
|
7457
|
+
Notice,
|
|
7458
|
+
{
|
|
7459
|
+
icon: /* @__PURE__ */ jsx10(
|
|
7460
|
+
LoaderCircleIcon,
|
|
7461
|
+
{
|
|
7462
|
+
className: "size-5 animate-spin motion-reduce:animate-none",
|
|
7463
|
+
"aria-hidden": true
|
|
7464
|
+
}
|
|
7465
|
+
),
|
|
7466
|
+
title: "Waking sandbox",
|
|
7467
|
+
announce: "status",
|
|
7468
|
+
children: [
|
|
7469
|
+
"Opening ",
|
|
7470
|
+
requestedPath,
|
|
7471
|
+
" when the live workspace is ready\u2026"
|
|
7472
|
+
]
|
|
7473
|
+
}
|
|
7474
|
+
) : /* @__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." })
|
|
7475
|
+
)
|
|
7476
|
+
}
|
|
7477
|
+
)
|
|
7121
7478
|
]
|
|
7122
7479
|
}
|
|
7123
7480
|
)
|
|
@@ -7137,6 +7494,62 @@ function WakeButton({ children, onClick }) {
|
|
|
7137
7494
|
}
|
|
7138
7495
|
);
|
|
7139
7496
|
}
|
|
7497
|
+
function LineNumberedFile({
|
|
7498
|
+
path,
|
|
7499
|
+
contents,
|
|
7500
|
+
focusLine
|
|
7501
|
+
}) {
|
|
7502
|
+
const lines = contents.split("\n");
|
|
7503
|
+
const inRange = focusLine >= 1 && focusLine <= lines.length;
|
|
7504
|
+
const targetRef = useRef15(null);
|
|
7505
|
+
useEffect18(() => {
|
|
7506
|
+
targetRef.current?.scrollIntoView({
|
|
7507
|
+
block: "center",
|
|
7508
|
+
behavior: prefersReducedMotion() ? "auto" : "smooth"
|
|
7509
|
+
});
|
|
7510
|
+
}, [path, focusLine, contents]);
|
|
7511
|
+
return /* @__PURE__ */ jsxs8(
|
|
7512
|
+
"div",
|
|
7513
|
+
{
|
|
7514
|
+
className: "h-full min-h-0 overflow-auto font-og-mono text-og-sm text-og-fg",
|
|
7515
|
+
role: "region",
|
|
7516
|
+
"aria-label": inRange ? `${path} at line ${focusLine}` : path,
|
|
7517
|
+
children: [
|
|
7518
|
+
!inRange ? /* @__PURE__ */ jsxs8(
|
|
7519
|
+
"p",
|
|
7520
|
+
{
|
|
7521
|
+
className: "border-b border-og-border bg-og-surface-1 px-2 py-1 text-og-xs text-og-fg-muted",
|
|
7522
|
+
role: "status",
|
|
7523
|
+
children: [
|
|
7524
|
+
"Line ",
|
|
7525
|
+
focusLine,
|
|
7526
|
+
" is past the end of this file."
|
|
7527
|
+
]
|
|
7528
|
+
}
|
|
7529
|
+
) : null,
|
|
7530
|
+
/* @__PURE__ */ jsx10("div", { children: lines.map((text, index) => {
|
|
7531
|
+
const lineNumber = index + 1;
|
|
7532
|
+
const focused = inRange && lineNumber === focusLine;
|
|
7533
|
+
return /* @__PURE__ */ jsxs8(
|
|
7534
|
+
"div",
|
|
7535
|
+
{
|
|
7536
|
+
ref: focused ? targetRef : void 0,
|
|
7537
|
+
"data-opengeni-file-line": lineNumber,
|
|
7538
|
+
...focused ? { "data-opengeni-focus-line": "" } : {},
|
|
7539
|
+
"aria-current": focused ? "location" : void 0,
|
|
7540
|
+
className: cn("flex gap-3 px-2 py-0.5 leading-6", focused && "bg-og-accent-soft"),
|
|
7541
|
+
children: [
|
|
7542
|
+
/* @__PURE__ */ jsx10("span", { className: "w-11 shrink-0 select-none text-right tabular-nums text-og-fg-subtle", children: lineNumber }),
|
|
7543
|
+
/* @__PURE__ */ jsx10("pre", { className: "min-w-0 flex-1 whitespace-pre-wrap break-words", children: text || " " })
|
|
7544
|
+
]
|
|
7545
|
+
},
|
|
7546
|
+
lineNumber
|
|
7547
|
+
);
|
|
7548
|
+
}) })
|
|
7549
|
+
]
|
|
7550
|
+
}
|
|
7551
|
+
);
|
|
7552
|
+
}
|
|
7140
7553
|
function GitHeader({ git, loading }) {
|
|
7141
7554
|
if (loading) {
|
|
7142
7555
|
return /* @__PURE__ */ jsxs8(
|
|
@@ -7318,7 +7731,7 @@ function Notice({
|
|
|
7318
7731
|
children: /* @__PURE__ */ jsxs8("div", { className: "flex max-w-sm flex-col items-center gap-2.5", children: [
|
|
7319
7732
|
icon ? /* @__PURE__ */ jsx10("span", { className: "grid size-10 place-items-center rounded-og-lg border border-og-border bg-og-surface-1 text-og-fg-muted shadow-sm", children: icon }) : null,
|
|
7320
7733
|
title ? /* @__PURE__ */ jsx10("p", { className: "font-medium text-og-fg", children: title }) : null,
|
|
7321
|
-
/* @__PURE__ */ jsx10("div", { className: "leading-5", children })
|
|
7734
|
+
/* @__PURE__ */ jsx10("div", { "data-contrast-audited": true, className: "leading-5", children })
|
|
7322
7735
|
] })
|
|
7323
7736
|
}
|
|
7324
7737
|
);
|
|
@@ -7326,7 +7739,7 @@ function Notice({
|
|
|
7326
7739
|
|
|
7327
7740
|
// src/components/workspace-dock.tsx
|
|
7328
7741
|
import {
|
|
7329
|
-
useCallback as
|
|
7742
|
+
useCallback as useCallback24,
|
|
7330
7743
|
useEffect as useEffect19,
|
|
7331
7744
|
useId as useId4,
|
|
7332
7745
|
useLayoutEffect as useLayoutEffect4,
|
|
@@ -7388,7 +7801,7 @@ function WorkspaceDock({
|
|
|
7388
7801
|
onActiveTabChange,
|
|
7389
7802
|
collapsed: collapsedProp,
|
|
7390
7803
|
onCollapsedChange,
|
|
7391
|
-
showCollapseControl =
|
|
7804
|
+
showCollapseControl = true,
|
|
7392
7805
|
headerAccessory,
|
|
7393
7806
|
mobileLeadingControl,
|
|
7394
7807
|
autoSaveId = "og.session.dock",
|
|
@@ -7414,19 +7827,35 @@ function WorkspaceDock({
|
|
|
7414
7827
|
storage: getLayoutStorage(),
|
|
7415
7828
|
id: autoSaveId
|
|
7416
7829
|
});
|
|
7830
|
+
const expandedDefaultLayout = defaultLayout && (defaultLayout.dock ?? 0) > 1 ? defaultLayout : void 0;
|
|
7831
|
+
const lastExpandedSizeRef = useRef16(expandedDefaultLayout?.dock ?? defaultSize);
|
|
7832
|
+
const layoutIdentityRef = useRef16(autoSaveId);
|
|
7833
|
+
if (layoutIdentityRef.current !== autoSaveId) {
|
|
7834
|
+
layoutIdentityRef.current = autoSaveId;
|
|
7835
|
+
lastExpandedSizeRef.current = expandedDefaultLayout?.dock ?? defaultSize;
|
|
7836
|
+
}
|
|
7837
|
+
const persistExpandedLayout = useCallback24(
|
|
7838
|
+
(layout, meta) => {
|
|
7839
|
+
const dockSize = layout.dock ?? 0;
|
|
7840
|
+
if (dockSize <= 1) return;
|
|
7841
|
+
lastExpandedSizeRef.current = dockSize;
|
|
7842
|
+
onLayoutChanged(layout, meta);
|
|
7843
|
+
},
|
|
7844
|
+
[onLayoutChanged]
|
|
7845
|
+
);
|
|
7417
7846
|
const requestedTab = activeTab ?? internalTab;
|
|
7418
7847
|
const tabIds = tabs.map((tab) => tab.id);
|
|
7419
7848
|
const firstTabId = tabs[0]?.id ?? "";
|
|
7420
7849
|
const requestedTabIsValid = tabIds.includes(requestedTab);
|
|
7421
7850
|
const current = requestedTabIsValid ? requestedTab : firstTabId;
|
|
7422
|
-
const setTab =
|
|
7851
|
+
const setTab = useCallback24(
|
|
7423
7852
|
(id) => {
|
|
7424
7853
|
setInternalTab(id);
|
|
7425
7854
|
onActiveTabChange?.(id);
|
|
7426
7855
|
},
|
|
7427
7856
|
[onActiveTabChange]
|
|
7428
7857
|
);
|
|
7429
|
-
const setCollapsed =
|
|
7858
|
+
const setCollapsed = useCallback24(
|
|
7430
7859
|
(next) => {
|
|
7431
7860
|
setInternalCollapsed((previous) => previous === next ? previous : next);
|
|
7432
7861
|
onCollapsedChange?.(next);
|
|
@@ -7441,7 +7870,7 @@ function WorkspaceDock({
|
|
|
7441
7870
|
dockPanelRef.current?.collapse();
|
|
7442
7871
|
setMaximized(false);
|
|
7443
7872
|
} else {
|
|
7444
|
-
dockPanelRef.current?.
|
|
7873
|
+
dockPanelRef.current?.resize(`${lastExpandedSizeRef.current}%`);
|
|
7445
7874
|
}
|
|
7446
7875
|
}, [collapsedProp, dockPanelRef]);
|
|
7447
7876
|
useEffect19(() => {
|
|
@@ -7470,7 +7899,13 @@ function WorkspaceDock({
|
|
|
7470
7899
|
return;
|
|
7471
7900
|
}
|
|
7472
7901
|
const target = returnFocusRef.current;
|
|
7473
|
-
if (target?.isConnected)
|
|
7902
|
+
if (target?.isConnected) {
|
|
7903
|
+
target.focus();
|
|
7904
|
+
return;
|
|
7905
|
+
}
|
|
7906
|
+
rootRef.current?.querySelector(
|
|
7907
|
+
'[data-workspace-primary] button:not([disabled]), [data-workspace-primary] a[href], [data-workspace-primary] input:not([disabled]), [data-workspace-primary] textarea:not([disabled]), [data-workspace-primary] select:not([disabled]), [data-workspace-primary] [tabindex]:not([tabindex="-1"])'
|
|
7908
|
+
)?.focus();
|
|
7474
7909
|
});
|
|
7475
7910
|
return () => cancelAnimationFrame(frame);
|
|
7476
7911
|
}, [collapsed, hostControlled, narrow]);
|
|
@@ -7492,11 +7927,11 @@ function WorkspaceDock({
|
|
|
7492
7927
|
setTab(firstTabId);
|
|
7493
7928
|
}
|
|
7494
7929
|
}, [firstTabId, requestedTabIsValid, setTab]);
|
|
7495
|
-
const collapse =
|
|
7930
|
+
const collapse = useCallback24(() => {
|
|
7496
7931
|
dockPanelRef.current?.collapse();
|
|
7497
7932
|
setCollapsed(true);
|
|
7498
7933
|
}, [dockPanelRef, setCollapsed]);
|
|
7499
|
-
const expand =
|
|
7934
|
+
const expand = useCallback24(() => {
|
|
7500
7935
|
dockPanelRef.current?.expand();
|
|
7501
7936
|
setCollapsed(false);
|
|
7502
7937
|
}, [dockPanelRef, setCollapsed]);
|
|
@@ -7581,7 +8016,7 @@ function WorkspaceDock({
|
|
|
7581
8016
|
onTab: setTab,
|
|
7582
8017
|
leading: mobileLeadingControl,
|
|
7583
8018
|
accessory: headerAccessory,
|
|
7584
|
-
controls: /* @__PURE__ */ jsx11(ChromeButton, { onClick: collapse, title: "
|
|
8019
|
+
controls: /* @__PURE__ */ jsx11(ChromeButton, { onClick: collapse, title: "Hide workspace", label: "Hide workspace", children: /* @__PURE__ */ jsx11(XIcon2, { className: "size-4" }) })
|
|
7585
8020
|
}
|
|
7586
8021
|
)
|
|
7587
8022
|
}
|
|
@@ -7618,7 +8053,7 @@ function WorkspaceDock({
|
|
|
7618
8053
|
children: maximized ? /* @__PURE__ */ jsx11(Minimize2Icon, { className: "size-3.5" }) : /* @__PURE__ */ jsx11(Maximize2Icon, { className: "size-3.5" })
|
|
7619
8054
|
}
|
|
7620
8055
|
),
|
|
7621
|
-
hostControlled && !showCollapseControl ? null : /* @__PURE__ */ jsx11(ChromeButton, { onClick: collapse, title: "
|
|
8056
|
+
hostControlled && !showCollapseControl ? null : /* @__PURE__ */ jsx11(ChromeButton, { onClick: collapse, title: "Hide workspace", label: "Hide workspace", children: /* @__PURE__ */ jsx11(PanelRightCloseIcon, { className: "size-3.5" }) })
|
|
7622
8057
|
] })
|
|
7623
8058
|
}
|
|
7624
8059
|
);
|
|
@@ -7628,8 +8063,8 @@ function WorkspaceDock({
|
|
|
7628
8063
|
{
|
|
7629
8064
|
orientation: "horizontal",
|
|
7630
8065
|
className: "min-h-0 flex-1",
|
|
7631
|
-
...
|
|
7632
|
-
onLayoutChanged,
|
|
8066
|
+
...expandedDefaultLayout ? { defaultLayout: expandedDefaultLayout } : {},
|
|
8067
|
+
onLayoutChanged: persistExpandedLayout,
|
|
7633
8068
|
children: [
|
|
7634
8069
|
/* @__PURE__ */ jsx11(Panel2, { id: "primary", minSize: "30%", className: "min-h-0 min-w-0", children: /* @__PURE__ */ jsx11(
|
|
7635
8070
|
"div",
|
|
@@ -7725,17 +8160,14 @@ function DockChrome({
|
|
|
7725
8160
|
const active = tabs.find((t) => t.id === current) ?? tabs[0];
|
|
7726
8161
|
const tabsetId = useId4();
|
|
7727
8162
|
const tabRefs = useRef16([]);
|
|
7728
|
-
const tabListRef = useRef16(null);
|
|
7729
8163
|
const [visitedTabs, setVisitedTabs] = useState16(() => /* @__PURE__ */ new Set());
|
|
7730
|
-
const [tabOverflow, setTabOverflow] = useState16({ start: false, end: false });
|
|
7731
8164
|
const activeId = active?.id ?? "";
|
|
7732
|
-
const activeTabIndex = tabs.findIndex((tab) => tab.id === activeId);
|
|
7733
8165
|
useDockLayoutEffect(() => {
|
|
7734
8166
|
const node = chromeRef.current;
|
|
7735
8167
|
if (!node) return;
|
|
7736
8168
|
const update = () => {
|
|
7737
8169
|
const desktop = typeof window.matchMedia !== "function" || window.matchMedia("(min-width: 1024px)").matches;
|
|
7738
|
-
const next = desktop && node.getBoundingClientRect().width <
|
|
8170
|
+
const next = desktop && node.getBoundingClientRect().width < 440;
|
|
7739
8171
|
setCompact((previous) => previous === next ? previous : next);
|
|
7740
8172
|
};
|
|
7741
8173
|
update();
|
|
@@ -7747,10 +8179,6 @@ function DockChrome({
|
|
|
7747
8179
|
observer.observe(node);
|
|
7748
8180
|
return () => observer.disconnect();
|
|
7749
8181
|
}, []);
|
|
7750
|
-
const keepActiveTabVisible = useCallback23(() => {
|
|
7751
|
-
const selected = activeTabIndex >= 0 ? tabRefs.current[activeTabIndex] : null;
|
|
7752
|
-
selected?.scrollIntoView({ block: "nearest", inline: "nearest" });
|
|
7753
|
-
}, [activeTabIndex]);
|
|
7754
8182
|
useEffect19(() => {
|
|
7755
8183
|
if (!activeId) return;
|
|
7756
8184
|
setVisitedTabs((previous) => {
|
|
@@ -7760,35 +8188,6 @@ function DockChrome({
|
|
|
7760
8188
|
return next;
|
|
7761
8189
|
});
|
|
7762
8190
|
}, [activeId]);
|
|
7763
|
-
useDockLayoutEffect(() => {
|
|
7764
|
-
keepActiveTabVisible();
|
|
7765
|
-
}, [activeId, keepActiveTabVisible]);
|
|
7766
|
-
useDockLayoutEffect(() => {
|
|
7767
|
-
const list = tabListRef.current;
|
|
7768
|
-
if (!list) return;
|
|
7769
|
-
const update = () => {
|
|
7770
|
-
const max = Math.max(0, list.scrollWidth - list.clientWidth);
|
|
7771
|
-
const next = { start: list.scrollLeft > 1, end: list.scrollLeft < max - 1 };
|
|
7772
|
-
setTabOverflow(
|
|
7773
|
-
(previous) => previous.start === next.start && previous.end === next.end ? previous : next
|
|
7774
|
-
);
|
|
7775
|
-
};
|
|
7776
|
-
update();
|
|
7777
|
-
list.addEventListener("scroll", update, { passive: true });
|
|
7778
|
-
const observer = typeof ResizeObserver === "undefined" ? null : new ResizeObserver(() => {
|
|
7779
|
-
keepActiveTabVisible();
|
|
7780
|
-
update();
|
|
7781
|
-
});
|
|
7782
|
-
observer?.observe(list);
|
|
7783
|
-
for (const tab of tabRefs.current) {
|
|
7784
|
-
if (tab) observer?.observe(tab);
|
|
7785
|
-
}
|
|
7786
|
-
return () => {
|
|
7787
|
-
list.removeEventListener("scroll", update);
|
|
7788
|
-
observer?.disconnect();
|
|
7789
|
-
};
|
|
7790
|
-
}, [activeId, keepActiveTabVisible, tabs.length]);
|
|
7791
|
-
const tabMask = tabOverflow.start && tabOverflow.end ? "linear-gradient(to right, transparent 0, black 14px, black calc(100% - 14px), transparent 100%)" : tabOverflow.start ? "linear-gradient(to right, transparent 0, black 14px, black 100%)" : tabOverflow.end ? "linear-gradient(to right, black 0, black calc(100% - 14px), transparent 100%)" : void 0;
|
|
7792
8191
|
const activateTab = (index) => {
|
|
7793
8192
|
const tab = tabs[index];
|
|
7794
8193
|
if (!tab) {
|
|
@@ -7800,128 +8199,114 @@ function DockChrome({
|
|
|
7800
8199
|
const onTabKeyDown = (event, index) => {
|
|
7801
8200
|
if (tabs.length === 0) return;
|
|
7802
8201
|
let nextIndex = null;
|
|
7803
|
-
if (event.key === "
|
|
7804
|
-
else if (event.key === "
|
|
8202
|
+
if (event.key === "ArrowDown") nextIndex = (index + 1) % tabs.length;
|
|
8203
|
+
else if (event.key === "ArrowUp") nextIndex = (index - 1 + tabs.length) % tabs.length;
|
|
7805
8204
|
else if (event.key === "Home") nextIndex = 0;
|
|
7806
8205
|
else if (event.key === "End") nextIndex = tabs.length - 1;
|
|
7807
8206
|
if (nextIndex === null) return;
|
|
7808
8207
|
event.preventDefault();
|
|
7809
8208
|
activateTab(nextIndex);
|
|
7810
8209
|
};
|
|
7811
|
-
return /* @__PURE__ */ jsxs9(
|
|
7812
|
-
|
|
7813
|
-
|
|
7814
|
-
|
|
7815
|
-
|
|
7816
|
-
|
|
7817
|
-
|
|
7818
|
-
|
|
7819
|
-
|
|
7820
|
-
|
|
7821
|
-
|
|
7822
|
-
|
|
7823
|
-
|
|
7824
|
-
|
|
7825
|
-
"div",
|
|
7826
|
-
{
|
|
7827
|
-
className: cn(
|
|
7828
|
-
"flex min-w-0 shrink-0 items-center max-[1023px]:min-h-11 min-[640px]:max-[1023px]:col-start-1 min-[640px]:max-[1023px]:row-start-1",
|
|
7829
|
-
compact && "hidden"
|
|
7830
|
-
),
|
|
7831
|
-
children: leading ?? /* @__PURE__ */ jsx11("span", { className: "hidden truncate px-1 text-og-sm font-semibold text-og-fg max-[1023px]:inline", children: "Workspace" })
|
|
7832
|
-
}
|
|
7833
|
-
),
|
|
8210
|
+
return /* @__PURE__ */ jsxs9(
|
|
8211
|
+
"div",
|
|
8212
|
+
{
|
|
8213
|
+
ref: chromeRef,
|
|
8214
|
+
"data-dock-chrome": true,
|
|
8215
|
+
"data-compact": compact ? "true" : void 0,
|
|
8216
|
+
className: "flex h-full min-h-0 min-w-0 flex-col",
|
|
8217
|
+
children: [
|
|
8218
|
+
/* @__PURE__ */ jsxs9("div", { className: "flex min-h-10 shrink-0 items-center gap-2 border-b border-og-border px-2 max-[1023px]:min-h-12", children: [
|
|
8219
|
+
/* @__PURE__ */ jsx11("div", { className: "flex min-w-0 flex-1 items-center gap-2", children: leading ?? /* @__PURE__ */ jsx11("span", { className: "hidden truncate text-og-sm font-semibold text-og-fg max-[1023px]:inline", children: "Workspace" }) }),
|
|
8220
|
+
/* @__PURE__ */ jsx11("div", { className: "min-w-0 shrink text-og-fg-muted", children: accessory }),
|
|
8221
|
+
/* @__PURE__ */ jsx11("div", { className: "flex shrink-0 items-center gap-0.5 text-og-fg-subtle", children: controls })
|
|
8222
|
+
] }),
|
|
8223
|
+
/* @__PURE__ */ jsxs9("div", { className: "flex min-h-0 min-w-0 flex-1", children: [
|
|
7834
8224
|
/* @__PURE__ */ jsx11(
|
|
7835
8225
|
"div",
|
|
7836
8226
|
{
|
|
7837
|
-
ref: tabListRef,
|
|
7838
8227
|
className: cn(
|
|
7839
|
-
"flex
|
|
7840
|
-
compact
|
|
8228
|
+
"flex shrink-0 flex-col gap-1 overflow-x-hidden overflow-y-auto border-r border-og-border bg-og-surface-1/45 p-1.5",
|
|
8229
|
+
compact ? "w-12" : "w-32 max-[1023px]:w-36"
|
|
7841
8230
|
),
|
|
7842
8231
|
role: "tablist",
|
|
7843
|
-
"aria-
|
|
7844
|
-
|
|
7845
|
-
|
|
7846
|
-
|
|
7847
|
-
|
|
7848
|
-
|
|
7849
|
-
|
|
7850
|
-
|
|
7851
|
-
|
|
7852
|
-
|
|
7853
|
-
|
|
8232
|
+
"aria-label": "Workspace panels",
|
|
8233
|
+
"aria-orientation": "vertical",
|
|
8234
|
+
children: tabs.map((tab, index) => {
|
|
8235
|
+
const tabName = tab.ariaLabel ?? (typeof tab.label === "string" ? tab.label : tab.id);
|
|
8236
|
+
return /* @__PURE__ */ jsxs9(
|
|
8237
|
+
"button",
|
|
8238
|
+
{
|
|
8239
|
+
ref: (node) => {
|
|
8240
|
+
tabRefs.current[index] = node;
|
|
8241
|
+
},
|
|
8242
|
+
id: `${tabsetId}-tab-${index}`,
|
|
8243
|
+
type: "button",
|
|
8244
|
+
role: "tab",
|
|
8245
|
+
"aria-label": compact && tab.icon ? tabName : void 0,
|
|
8246
|
+
title: compact && tab.icon ? tabName : void 0,
|
|
8247
|
+
"aria-selected": tab.id === current,
|
|
8248
|
+
"aria-controls": `${tabsetId}-panel-${index}`,
|
|
8249
|
+
tabIndex: tab.id === current ? 0 : -1,
|
|
8250
|
+
onClick: () => onTab(tab.id),
|
|
8251
|
+
onKeyDown: (event) => onTabKeyDown(event, index),
|
|
8252
|
+
className: cn(
|
|
8253
|
+
"group relative flex min-h-9 w-full items-center gap-2 rounded-og-md px-2 text-left 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",
|
|
8254
|
+
compact && "justify-center px-1",
|
|
8255
|
+
tab.id === current ? "bg-og-accent-soft text-og-fg shadow-[inset_2px_0_0_var(--og-color-accent)]" : "text-og-fg-subtle hover:bg-og-surface-2 hover:text-og-fg"
|
|
8256
|
+
),
|
|
8257
|
+
children: [
|
|
8258
|
+
tab.icon ? /* @__PURE__ */ jsx11(
|
|
8259
|
+
"span",
|
|
8260
|
+
{
|
|
8261
|
+
className: "flex size-4 shrink-0 items-center justify-center [&>svg]:size-4",
|
|
8262
|
+
"aria-hidden": true,
|
|
8263
|
+
children: tab.icon
|
|
8264
|
+
}
|
|
8265
|
+
) : null,
|
|
8266
|
+
!compact || !tab.icon ? /* @__PURE__ */ jsx11("span", { "data-contrast-audited": true, className: "min-w-0 flex-1 truncate", children: tab.label }) : null,
|
|
8267
|
+
tab.badge ? /* @__PURE__ */ jsx11(
|
|
8268
|
+
"span",
|
|
8269
|
+
{
|
|
8270
|
+
className: cn("shrink-0", compact && "absolute -right-0.5 -top-0.5 scale-90"),
|
|
8271
|
+
children: tab.badge
|
|
8272
|
+
}
|
|
8273
|
+
) : null
|
|
8274
|
+
]
|
|
7854
8275
|
},
|
|
7855
|
-
id
|
|
7856
|
-
|
|
7857
|
-
|
|
7858
|
-
"aria-selected": tab.id === current,
|
|
7859
|
-
"aria-controls": `${tabsetId}-panel-${index}`,
|
|
7860
|
-
tabIndex: tab.id === current ? 0 : -1,
|
|
7861
|
-
onClick: () => onTab(tab.id),
|
|
7862
|
-
onKeyDown: (event) => onTabKeyDown(event, index),
|
|
7863
|
-
className: cn(
|
|
7864
|
-
"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",
|
|
7865
|
-
tab.id === current ? "bg-og-accent-soft text-og-fg" : "text-og-fg-subtle hover:text-og-fg"
|
|
7866
|
-
),
|
|
7867
|
-
children: [
|
|
7868
|
-
/* @__PURE__ */ jsx11("span", { "data-contrast-audited": true, children: tab.label }),
|
|
7869
|
-
tab.badge
|
|
7870
|
-
]
|
|
7871
|
-
},
|
|
7872
|
-
tab.id
|
|
7873
|
-
))
|
|
7874
|
-
}
|
|
7875
|
-
),
|
|
7876
|
-
/* @__PURE__ */ jsx11(
|
|
7877
|
-
"div",
|
|
7878
|
-
{
|
|
7879
|
-
className: cn(
|
|
7880
|
-
"flex min-w-0 shrink-0 items-center justify-self-end min-[640px]:max-[1023px]:col-start-3 min-[640px]:max-[1023px]:row-start-1",
|
|
7881
|
-
compact && "col-start-1 row-start-1 justify-self-start"
|
|
7882
|
-
),
|
|
7883
|
-
children: accessory
|
|
8276
|
+
tab.id
|
|
8277
|
+
);
|
|
8278
|
+
})
|
|
7884
8279
|
}
|
|
7885
8280
|
),
|
|
7886
|
-
/* @__PURE__ */ jsx11(
|
|
7887
|
-
|
|
7888
|
-
|
|
7889
|
-
|
|
7890
|
-
|
|
7891
|
-
|
|
7892
|
-
|
|
7893
|
-
|
|
7894
|
-
|
|
7895
|
-
|
|
7896
|
-
|
|
7897
|
-
|
|
7898
|
-
|
|
7899
|
-
|
|
7900
|
-
|
|
7901
|
-
|
|
7902
|
-
|
|
7903
|
-
|
|
7904
|
-
|
|
7905
|
-
|
|
7906
|
-
"aria-labelledby": `${tabsetId}-tab-${index}`,
|
|
7907
|
-
className: "h-full min-h-0 min-w-0 overflow-hidden",
|
|
7908
|
-
hidden: !selected,
|
|
7909
|
-
role: "tabpanel",
|
|
7910
|
-
children: shouldMount ? tab.content : null
|
|
7911
|
-
},
|
|
7912
|
-
tab.id
|
|
7913
|
-
);
|
|
7914
|
-
}) : /* @__PURE__ */ jsx11("div", { className: "h-full min-h-0 min-w-0", "aria-label": "Workspace", role: "tabpanel" }) })
|
|
7915
|
-
] });
|
|
8281
|
+
/* @__PURE__ */ jsx11("div", { className: "min-h-0 min-w-0 flex-1 overflow-hidden", children: tabs.length > 0 ? tabs.map((tab, index) => {
|
|
8282
|
+
const selected = tab.id === activeId;
|
|
8283
|
+
const shouldMount = selected || visitedTabs.has(tab.id);
|
|
8284
|
+
return /* @__PURE__ */ jsx11(
|
|
8285
|
+
"div",
|
|
8286
|
+
{
|
|
8287
|
+
id: `${tabsetId}-panel-${index}`,
|
|
8288
|
+
"aria-labelledby": `${tabsetId}-tab-${index}`,
|
|
8289
|
+
className: "h-full min-h-0 min-w-0 overflow-hidden",
|
|
8290
|
+
hidden: !selected,
|
|
8291
|
+
role: "tabpanel",
|
|
8292
|
+
children: shouldMount ? tab.content : null
|
|
8293
|
+
},
|
|
8294
|
+
tab.id
|
|
8295
|
+
);
|
|
8296
|
+
}) : /* @__PURE__ */ jsx11("div", { className: "h-full min-h-0 min-w-0", "aria-label": "Workspace", role: "tabpanel" }) })
|
|
8297
|
+
] })
|
|
8298
|
+
]
|
|
8299
|
+
}
|
|
8300
|
+
);
|
|
7916
8301
|
}
|
|
7917
8302
|
|
|
7918
8303
|
// src/components/sandbox-workspace.tsx
|
|
7919
8304
|
import {
|
|
7920
8305
|
lazy as lazy2,
|
|
7921
8306
|
Suspense as Suspense2,
|
|
7922
|
-
useCallback as
|
|
8307
|
+
useCallback as useCallback25,
|
|
7923
8308
|
useEffect as useEffect20,
|
|
7924
|
-
useMemo as
|
|
8309
|
+
useMemo as useMemo14,
|
|
7925
8310
|
useRef as useRef17,
|
|
7926
8311
|
useState as useState17
|
|
7927
8312
|
} from "react";
|
|
@@ -7939,12 +8324,12 @@ import {
|
|
|
7939
8324
|
} from "lucide-react";
|
|
7940
8325
|
import { jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
7941
8326
|
var LazyBrowserViewer = lazy2(async () => {
|
|
7942
|
-
const
|
|
7943
|
-
return { default:
|
|
8327
|
+
const { BrowserViewer } = await import("./browser-viewer-GXUICIAG.js");
|
|
8328
|
+
return { default: BrowserViewer };
|
|
7944
8329
|
});
|
|
7945
8330
|
var LazyComputerViewer = lazy2(async () => {
|
|
7946
|
-
const
|
|
7947
|
-
return { default:
|
|
8331
|
+
const { ComputerViewer } = await import("./computer-viewer-ZOP4Q7A7.js");
|
|
8332
|
+
return { default: ComputerViewer };
|
|
7948
8333
|
});
|
|
7949
8334
|
var WORKBENCH_TAB_CHANGES = "changes";
|
|
7950
8335
|
var WORKBENCH_TAB_FILES = "files";
|
|
@@ -7979,12 +8364,6 @@ function captureDegradedMessage(reason) {
|
|
|
7979
8364
|
return "Workspace capture is incomplete because repository discovery failed. Live files remain authoritative.";
|
|
7980
8365
|
}
|
|
7981
8366
|
}
|
|
7982
|
-
function WorkbenchTabLabel({ icon, children }) {
|
|
7983
|
-
return /* @__PURE__ */ jsxs10("span", { className: "inline-flex items-center gap-1.5", children: [
|
|
7984
|
-
/* @__PURE__ */ jsx12("span", { className: "[&>svg]:size-3.5", "aria-hidden": true, children: icon }),
|
|
7985
|
-
/* @__PURE__ */ jsx12("span", { children })
|
|
7986
|
-
] });
|
|
7987
|
-
}
|
|
7988
8367
|
function WorkbenchSurfaceLoading({ name }) {
|
|
7989
8368
|
return /* @__PURE__ */ jsx12(
|
|
7990
8369
|
CenteredState,
|
|
@@ -8037,6 +8416,29 @@ function sandboxProvisionInFlight(events) {
|
|
|
8037
8416
|
}
|
|
8038
8417
|
return inFlight;
|
|
8039
8418
|
}
|
|
8419
|
+
function workspaceTurnInFlight(events) {
|
|
8420
|
+
const activeTurnIds = /* @__PURE__ */ new Set();
|
|
8421
|
+
let anonymousTurnInFlight = false;
|
|
8422
|
+
let sessionOwnsWorkspace = false;
|
|
8423
|
+
for (const event of events) {
|
|
8424
|
+
if (event.type === "session.status.changed") {
|
|
8425
|
+
const status = event.payload && typeof event.payload === "object" && !Array.isArray(event.payload) ? event.payload.status : null;
|
|
8426
|
+
sessionOwnsWorkspace = status === "running" || status === "recovering";
|
|
8427
|
+
continue;
|
|
8428
|
+
}
|
|
8429
|
+
if (event.type === "turn.started") {
|
|
8430
|
+
if (event.turnId) activeTurnIds.add(event.turnId);
|
|
8431
|
+
else anonymousTurnInFlight = true;
|
|
8432
|
+
continue;
|
|
8433
|
+
}
|
|
8434
|
+
if (event.type !== "turn.completed" && event.type !== "turn.failed" && event.type !== "turn.cancelled" && event.type !== "turn.superseded") {
|
|
8435
|
+
continue;
|
|
8436
|
+
}
|
|
8437
|
+
if (event.turnId) activeTurnIds.delete(event.turnId);
|
|
8438
|
+
else anonymousTurnInFlight = false;
|
|
8439
|
+
}
|
|
8440
|
+
return sessionOwnsWorkspace || anonymousTurnInFlight || activeTurnIds.size > 0;
|
|
8441
|
+
}
|
|
8040
8442
|
function emptyWarmIntents(sessionId) {
|
|
8041
8443
|
return {
|
|
8042
8444
|
sessionId,
|
|
@@ -8052,9 +8454,17 @@ function useSandboxWorkspaceTabs(options) {
|
|
|
8052
8454
|
onNotify,
|
|
8053
8455
|
browserWebSocketFactory,
|
|
8054
8456
|
computerWebSocketFactory,
|
|
8457
|
+
browserExtensionSetupUrl,
|
|
8055
8458
|
requestedFilePath,
|
|
8056
8459
|
requestedFileRequestId,
|
|
8460
|
+
requestedFileLine,
|
|
8057
8461
|
onOpenFile,
|
|
8462
|
+
initialFilePath,
|
|
8463
|
+
onFilePathChange,
|
|
8464
|
+
initialBrowserSessionId,
|
|
8465
|
+
onBrowserSessionIdChange,
|
|
8466
|
+
initialComputerSessionId,
|
|
8467
|
+
onComputerSessionIdChange,
|
|
8058
8468
|
onOpenComputerSession,
|
|
8059
8469
|
requestedComputerSessionId,
|
|
8060
8470
|
requestedComputerRequestId
|
|
@@ -8069,7 +8479,7 @@ function useSandboxWorkspaceTabs(options) {
|
|
|
8069
8479
|
const terminalEnabled = surfaceSet.has(WORKBENCH_TAB_TERMINAL);
|
|
8070
8480
|
const browserEnabled = surfaceSet.has(WORKBENCH_TAB_BROWSER);
|
|
8071
8481
|
const desktopEnabled = surfaceSet.has(WORKBENCH_TAB_DESKTOP);
|
|
8072
|
-
const createLinkedComputer =
|
|
8482
|
+
const createLinkedComputer = useCallback25(
|
|
8073
8483
|
async (name, placement) => {
|
|
8074
8484
|
const response = await client.createComputerSession(workspaceId, {
|
|
8075
8485
|
operationId: crypto.randomUUID(),
|
|
@@ -8078,7 +8488,7 @@ function useSandboxWorkspaceTabs(options) {
|
|
|
8078
8488
|
...placement ? { placement } : {}
|
|
8079
8489
|
});
|
|
8080
8490
|
if (response.operation.state !== "completed" || response.session.lifecycle !== "active") {
|
|
8081
|
-
throw new Error(response.operation.error?.message ?? "The
|
|
8491
|
+
throw new Error(response.operation.error?.message ?? "The desktop could not be opened.");
|
|
8082
8492
|
}
|
|
8083
8493
|
return {
|
|
8084
8494
|
id: response.session.id,
|
|
@@ -8092,13 +8502,14 @@ function useSandboxWorkspaceTabs(options) {
|
|
|
8092
8502
|
const resolvedActiveTab = activeTab ?? initialTab;
|
|
8093
8503
|
const changesActive = resolvedActiveTab === WORKBENCH_TAB_CHANGES;
|
|
8094
8504
|
const filesActive = resolvedActiveTab === WORKBENCH_TAB_FILES;
|
|
8505
|
+
const turnInFlight = workspaceTurnInFlight(events);
|
|
8095
8506
|
const surfaceIdentity = WORKBENCH_SURFACES.filter((surface) => surfaceSet.has(surface)).join(",");
|
|
8096
8507
|
const [storedWarmIntents, setStoredWarmIntents] = useState17(
|
|
8097
8508
|
() => emptyWarmIntents(sessionId)
|
|
8098
8509
|
);
|
|
8099
8510
|
const warmIntents = storedWarmIntents.sessionId === sessionId ? storedWarmIntents : emptyWarmIntents(sessionId);
|
|
8100
8511
|
const { warmTerminal, warmFiles } = warmIntents;
|
|
8101
|
-
const requestWarmIntent =
|
|
8512
|
+
const requestWarmIntent = useCallback25(
|
|
8102
8513
|
(intent) => {
|
|
8103
8514
|
setStoredWarmIntents((previous) => {
|
|
8104
8515
|
const current = previous.sessionId === sessionId ? previous : emptyWarmIntents(sessionId);
|
|
@@ -8107,10 +8518,13 @@ function useSandboxWorkspaceTabs(options) {
|
|
|
8107
8518
|
},
|
|
8108
8519
|
[sessionId]
|
|
8109
8520
|
);
|
|
8521
|
+
useEffect20(() => {
|
|
8522
|
+
if (requestedFilePath) requestWarmIntent("warmFiles");
|
|
8523
|
+
}, [requestWarmIntent, requestedFilePath, requestedFileRequestId]);
|
|
8110
8524
|
const machines = useMachines({
|
|
8111
8525
|
workspaceId,
|
|
8112
8526
|
sessionId,
|
|
8113
|
-
pollIntervalMs:
|
|
8527
|
+
pollIntervalMs: MACHINES_SESSION_POLL_MS,
|
|
8114
8528
|
enabled: machineSurfaceEnabled
|
|
8115
8529
|
});
|
|
8116
8530
|
const activeMachine = machines.machines.find((m) => m.sandboxId === machines.activeSandboxId) ?? machines.machines.find((m) => m.active) ?? null;
|
|
@@ -8158,7 +8572,7 @@ function useSandboxWorkspaceTabs(options) {
|
|
|
8158
8572
|
const captureAvailable = captureState.available;
|
|
8159
8573
|
const liveWorkspaceExpected = sandboxAcceptsLiveIo(liveness);
|
|
8160
8574
|
const capturePending = !liveWorkspaceExpected && (captureState.fileCount === null || captureState.loading);
|
|
8161
|
-
const repoPaths =
|
|
8575
|
+
const repoPaths = useMemo14(() => {
|
|
8162
8576
|
const advertised = capabilities?.Git.repos ?? [];
|
|
8163
8577
|
if (advertised.length > 0) return advertised;
|
|
8164
8578
|
return captureState.capture?.repos.map((repo) => repo.root) ?? [];
|
|
@@ -8181,7 +8595,14 @@ function useSandboxWorkspaceTabs(options) {
|
|
|
8181
8595
|
// No passive Channel-A reads while cold, even after a conclusive capture
|
|
8182
8596
|
// miss. A missing capture gets an explicit "Open live workspace" gate.
|
|
8183
8597
|
enabled: filesEnabled && (captureAvailable || fileSystemOn && liveWorkspaceExpected),
|
|
8184
|
-
|
|
8598
|
+
// A cold sandbox can become warm while the first agent command still owns
|
|
8599
|
+
// startup. Do not race that command with an automatic Files read. Keeping
|
|
8600
|
+
// this fence at turn scope also avoids unsafe millisecond gaps between
|
|
8601
|
+
// sequential tool calls; rendered state and explicit user actions remain.
|
|
8602
|
+
active: filesActive && !turnInFlight,
|
|
8603
|
+
// A deliberate canonical absolute-path open browses in the selected target's
|
|
8604
|
+
// advertised namespace, so the authoritative tree and link share exact paths.
|
|
8605
|
+
...requestedFilePath?.startsWith("/") && capabilities?.FileSystem.root ? { rootPath: capabilities.FileSystem.root } : {},
|
|
8185
8606
|
repoPaths,
|
|
8186
8607
|
liveness: liveIoLiveness,
|
|
8187
8608
|
capture: captureState.capture,
|
|
@@ -8195,17 +8616,18 @@ function useSandboxWorkspaceTabs(options) {
|
|
|
8195
8616
|
const [storedChangesComparison, setStoredChangesComparison] = useState17(null);
|
|
8196
8617
|
const captureSupportsBranch = captureState.capture !== null && captureState.capture.repos.length > 0 && captureState.capture.repos.every((repo) => repo.branchDiff !== void 0);
|
|
8197
8618
|
const changesComparison = storedChangesComparison?.sessionId === sessionId ? storedChangesComparison.value : liveWorkspaceExpected || captureSupportsBranch ? "branch" : "working";
|
|
8198
|
-
const setChangesComparison =
|
|
8619
|
+
const setChangesComparison = useCallback25(
|
|
8199
8620
|
(value) => setStoredChangesComparison({ sessionId, value }),
|
|
8200
8621
|
[sessionId]
|
|
8201
8622
|
);
|
|
8202
8623
|
const git = useSandboxGit(sessionId, {
|
|
8203
8624
|
events,
|
|
8204
8625
|
enabled: changesComparison === "working" ? workspaceDataEnabled && (captureAvailable || gitOn && liveWorkspaceExpected) : changesComparison === "branch" ? workspaceDataEnabled && (captureSupportsBranch || gitOn && liveWorkspaceExpected) : workspaceDataEnabled && gitOn && liveWorkspaceExpected,
|
|
8205
|
-
//
|
|
8206
|
-
//
|
|
8207
|
-
//
|
|
8208
|
-
|
|
8626
|
+
// Live Git belongs to the visible Changes surface. In particular, do not
|
|
8627
|
+
// turn an unresolved/default tab into a hidden provider read: that races the
|
|
8628
|
+
// agent's own sandbox commands and used to refresh after every tool output
|
|
8629
|
+
// even while Files was visibly selected.
|
|
8630
|
+
active: changesActive && !turnInFlight,
|
|
8209
8631
|
repoPaths,
|
|
8210
8632
|
liveness: liveIoLiveness,
|
|
8211
8633
|
comparison: changesComparison,
|
|
@@ -8246,48 +8668,31 @@ function useSandboxWorkspaceTabs(options) {
|
|
|
8246
8668
|
if (defaultTabRef.current.identity !== defaultIdentity) {
|
|
8247
8669
|
defaultTabRef.current = { identity: defaultIdentity, value: null };
|
|
8248
8670
|
}
|
|
8249
|
-
const captureIsAuthoritative = !liveWorkspaceExpected && (liveness !== void 0 || caps.error !== null);
|
|
8250
8671
|
const captureHasChanges = (captureState.fileCount ?? 0) > 0 || (captureState.capture?.repos.some((repo) => (repo.branchDiff?.length ?? 0) > 0) ?? false);
|
|
8251
8672
|
const captureUnavailable = captureState.fileCount === 0 && !captureHasChanges || captureState.error !== null;
|
|
8252
|
-
const implicitBranchFallbackPending = changesComparison === "branch" && git.error !== null && storedChangesComparison?.sessionId !== sessionId;
|
|
8253
8673
|
if (defaultTabRef.current.value === null) {
|
|
8254
8674
|
if (initialTab && (!isWorkbenchSurface(initialTab) || surfaceSet.has(initialTab))) {
|
|
8255
8675
|
defaultTabRef.current.value = initialTab;
|
|
8256
|
-
} else if (
|
|
8257
|
-
defaultTabRef.current.value = sourceDrivenDefaultTab(
|
|
8258
|
-
git.diff.length > 0,
|
|
8259
|
-
changesEnabled,
|
|
8260
|
-
filesEnabled
|
|
8261
|
-
);
|
|
8262
|
-
} else if (captureIsAuthoritative && captureState.fileCount !== null && captureState.available) {
|
|
8676
|
+
} else if (captureState.fileCount !== null && captureState.available) {
|
|
8263
8677
|
defaultTabRef.current.value = sourceDrivenDefaultTab(
|
|
8264
8678
|
captureHasChanges,
|
|
8265
8679
|
changesEnabled,
|
|
8266
8680
|
filesEnabled
|
|
8267
8681
|
);
|
|
8268
|
-
} else if (
|
|
8269
|
-
defaultTabRef.current.value = sourceDrivenDefaultTab(true, changesEnabled, filesEnabled);
|
|
8270
|
-
} else if (captureUnavailable && caps.error) {
|
|
8682
|
+
} else if (captureUnavailable && initialWorkspaceTab(events) === WORKBENCH_TAB_CHANGES) {
|
|
8271
8683
|
defaultTabRef.current.value = sourceDrivenDefaultTab(true, changesEnabled, filesEnabled);
|
|
8272
|
-
} else if (
|
|
8273
|
-
defaultTabRef.current.value = sourceDrivenDefaultTab(false, changesEnabled, filesEnabled);
|
|
8274
|
-
} else if (captureState.available && git.error && captureState.fileCount !== null) {
|
|
8275
|
-
defaultTabRef.current.value = sourceDrivenDefaultTab(
|
|
8276
|
-
captureHasChanges,
|
|
8277
|
-
changesEnabled,
|
|
8278
|
-
filesEnabled
|
|
8279
|
-
);
|
|
8280
|
-
} else if (captureUnavailable && git.error && !implicitBranchFallbackPending) {
|
|
8684
|
+
} else if (captureState.fileCount !== null || captureState.error !== null) {
|
|
8281
8685
|
defaultTabRef.current.value = sourceDrivenDefaultTab(false, changesEnabled, filesEnabled);
|
|
8282
8686
|
}
|
|
8283
8687
|
}
|
|
8284
8688
|
const defaultTab = defaultTabRef.current.value;
|
|
8285
|
-
const tabs =
|
|
8689
|
+
const tabs = useMemo14(() => {
|
|
8286
8690
|
const list = [];
|
|
8287
8691
|
if (changesEnabled)
|
|
8288
8692
|
list.push({
|
|
8289
8693
|
id: WORKBENCH_TAB_CHANGES,
|
|
8290
|
-
label:
|
|
8694
|
+
label: "Changes",
|
|
8695
|
+
icon: /* @__PURE__ */ jsx12(GitCompareArrowsIcon, {}),
|
|
8291
8696
|
badge: dirtyCount > 0 ? /* @__PURE__ */ jsx12(DirtyBadge, { count: dirtyCount }) : void 0,
|
|
8292
8697
|
content: /* @__PURE__ */ jsx12(
|
|
8293
8698
|
ChangesTabBody,
|
|
@@ -8316,7 +8721,8 @@ function useSandboxWorkspaceTabs(options) {
|
|
|
8316
8721
|
if (filesEnabled)
|
|
8317
8722
|
list.push({
|
|
8318
8723
|
id: WORKBENCH_TAB_FILES,
|
|
8319
|
-
label:
|
|
8724
|
+
label: "Files",
|
|
8725
|
+
icon: /* @__PURE__ */ jsx12(FileCode2Icon2, {}),
|
|
8320
8726
|
content: /* @__PURE__ */ jsx12(
|
|
8321
8727
|
SandboxFiles,
|
|
8322
8728
|
{
|
|
@@ -8331,9 +8737,12 @@ function useSandboxWorkspaceTabs(options) {
|
|
|
8331
8737
|
...requestedFilePath ? {
|
|
8332
8738
|
requestedPath: requestedFilePath,
|
|
8333
8739
|
...requestedFileRequestId !== null && requestedFileRequestId !== void 0 ? { requestedPathRequestId: requestedFileRequestId } : {},
|
|
8334
|
-
requestedPathReady: sandboxAcceptsLiveIo(liveness)
|
|
8740
|
+
requestedPathReady: sandboxAcceptsLiveIo(liveness),
|
|
8741
|
+
...requestedFileLine != null && requestedFileLine > 0 ? { requestedLine: requestedFileLine } : {}
|
|
8335
8742
|
} : {},
|
|
8336
8743
|
onEditIntent: () => requestWarmIntent("warmFiles"),
|
|
8744
|
+
initialSelectedPath: initialFilePath,
|
|
8745
|
+
onSelectedPathChange: onFilePathChange,
|
|
8337
8746
|
className: "h-full"
|
|
8338
8747
|
},
|
|
8339
8748
|
sessionId
|
|
@@ -8342,7 +8751,8 @@ function useSandboxWorkspaceTabs(options) {
|
|
|
8342
8751
|
if (terminalOn) {
|
|
8343
8752
|
list.push({
|
|
8344
8753
|
id: WORKBENCH_TAB_TERMINAL,
|
|
8345
|
-
label:
|
|
8754
|
+
label: "Terminal",
|
|
8755
|
+
icon: /* @__PURE__ */ jsx12(SquareTerminalIcon, {}),
|
|
8346
8756
|
content: /* @__PURE__ */ jsx12("div", { className: "h-full bg-og-bg p-1", children: /* @__PURE__ */ jsx12(
|
|
8347
8757
|
SandboxTerminal,
|
|
8348
8758
|
{
|
|
@@ -8362,7 +8772,8 @@ function useSandboxWorkspaceTabs(options) {
|
|
|
8362
8772
|
if (browserEnabled) {
|
|
8363
8773
|
list.push({
|
|
8364
8774
|
id: WORKBENCH_TAB_BROWSER,
|
|
8365
|
-
label:
|
|
8775
|
+
label: "Browser",
|
|
8776
|
+
icon: /* @__PURE__ */ jsx12(Globe2Icon, {}),
|
|
8366
8777
|
content: /* @__PURE__ */ jsx12(Suspense2, { fallback: /* @__PURE__ */ jsx12(WorkbenchSurfaceLoading, { name: "Browser" }), children: /* @__PURE__ */ jsx12(
|
|
8367
8778
|
LazyBrowserViewer,
|
|
8368
8779
|
{
|
|
@@ -8371,9 +8782,12 @@ function useSandboxWorkspaceTabs(options) {
|
|
|
8371
8782
|
sessionId,
|
|
8372
8783
|
enabled: workspaceVisible,
|
|
8373
8784
|
onNotify,
|
|
8785
|
+
initialBrowserSessionId,
|
|
8786
|
+
onBrowserSessionIdChange,
|
|
8374
8787
|
...desktopEnabled ? { createLinkedComputer } : {},
|
|
8375
8788
|
...onOpenComputerSession ? { onOpenComputer: onOpenComputerSession } : {},
|
|
8376
8789
|
...browserWebSocketFactory ? { webSocketFactory: browserWebSocketFactory } : {},
|
|
8790
|
+
...browserExtensionSetupUrl ? { browserExtensionSetupUrl } : {},
|
|
8377
8791
|
className: "h-full"
|
|
8378
8792
|
},
|
|
8379
8793
|
sessionId
|
|
@@ -8383,8 +8797,9 @@ function useSandboxWorkspaceTabs(options) {
|
|
|
8383
8797
|
if (desktopEnabled) {
|
|
8384
8798
|
list.push({
|
|
8385
8799
|
id: WORKBENCH_TAB_DESKTOP,
|
|
8386
|
-
label:
|
|
8387
|
-
|
|
8800
|
+
label: "Desktop",
|
|
8801
|
+
icon: /* @__PURE__ */ jsx12(MonitorIcon, {}),
|
|
8802
|
+
content: /* @__PURE__ */ jsx12(Suspense2, { fallback: /* @__PURE__ */ jsx12(WorkbenchSurfaceLoading, { name: "Desktop" }), children: /* @__PURE__ */ jsx12(
|
|
8388
8803
|
LazyComputerViewer,
|
|
8389
8804
|
{
|
|
8390
8805
|
client,
|
|
@@ -8392,6 +8807,8 @@ function useSandboxWorkspaceTabs(options) {
|
|
|
8392
8807
|
sessionId,
|
|
8393
8808
|
enabled: workspaceVisible,
|
|
8394
8809
|
onNotify,
|
|
8810
|
+
initialComputerSessionId,
|
|
8811
|
+
onComputerSessionIdChange,
|
|
8395
8812
|
requestedComputerSessionId,
|
|
8396
8813
|
requestedComputerRequestId,
|
|
8397
8814
|
...computerWebSocketFactory ? { webSocketFactory: computerWebSocketFactory } : {},
|
|
@@ -8414,7 +8831,14 @@ function useSandboxWorkspaceTabs(options) {
|
|
|
8414
8831
|
warmTerminal,
|
|
8415
8832
|
requestedFilePath,
|
|
8416
8833
|
requestedFileRequestId,
|
|
8834
|
+
requestedFileLine,
|
|
8417
8835
|
onOpenFile,
|
|
8836
|
+
initialFilePath,
|
|
8837
|
+
onFilePathChange,
|
|
8838
|
+
initialBrowserSessionId,
|
|
8839
|
+
onBrowserSessionIdChange,
|
|
8840
|
+
initialComputerSessionId,
|
|
8841
|
+
onComputerSessionIdChange,
|
|
8418
8842
|
onOpenComputerSession,
|
|
8419
8843
|
requestedComputerSessionId,
|
|
8420
8844
|
requestedComputerRequestId,
|
|
@@ -8462,9 +8886,18 @@ function SandboxWorkspace(props) {
|
|
|
8462
8886
|
leadingTabs,
|
|
8463
8887
|
trailingTabs,
|
|
8464
8888
|
initialTab,
|
|
8889
|
+
onActiveTabChange,
|
|
8890
|
+
initialFilePath,
|
|
8891
|
+
onFilePathChange,
|
|
8892
|
+
openFileRequest,
|
|
8893
|
+
initialBrowserSessionId,
|
|
8894
|
+
onBrowserSessionIdChange,
|
|
8895
|
+
initialComputerSessionId,
|
|
8896
|
+
onComputerSessionIdChange,
|
|
8465
8897
|
onNotify,
|
|
8466
8898
|
browserWebSocketFactory,
|
|
8467
8899
|
computerWebSocketFactory,
|
|
8900
|
+
browserExtensionSetupUrl,
|
|
8468
8901
|
collapsed,
|
|
8469
8902
|
onCollapsedChange,
|
|
8470
8903
|
showCollapseControl,
|
|
@@ -8482,15 +8915,29 @@ function SandboxWorkspace(props) {
|
|
|
8482
8915
|
const nextComputerRequestId = useRef17(0);
|
|
8483
8916
|
const selectedTab = storedSelection?.sessionId === sessionId ? storedSelection.tab : null;
|
|
8484
8917
|
const activeTabHint = selectedTab ?? initialTab ?? leadingTabs?.[0]?.id ?? null;
|
|
8485
|
-
const openFile =
|
|
8486
|
-
(path) => {
|
|
8487
|
-
|
|
8488
|
-
|
|
8918
|
+
const openFile = useCallback25(
|
|
8919
|
+
(path, line) => {
|
|
8920
|
+
setRequestedFile({
|
|
8921
|
+
sessionId,
|
|
8922
|
+
path,
|
|
8923
|
+
line: line != null && line > 0 ? line : null,
|
|
8924
|
+
requestId: ++nextFileRequestId.current
|
|
8925
|
+
});
|
|
8489
8926
|
setStoredSelection({ sessionId, tab: WORKBENCH_TAB_FILES });
|
|
8927
|
+
onActiveTabChange?.(WORKBENCH_TAB_FILES);
|
|
8928
|
+
onFilePathChange?.(path);
|
|
8929
|
+
onCollapsedChange?.(false);
|
|
8490
8930
|
},
|
|
8491
|
-
[sessionId]
|
|
8931
|
+
[onActiveTabChange, onCollapsedChange, onFilePathChange, sessionId]
|
|
8492
8932
|
);
|
|
8493
|
-
const
|
|
8933
|
+
const handledOpenFileRequestId = useRef17(null);
|
|
8934
|
+
useEffect20(() => {
|
|
8935
|
+
if (!openFileRequest) return;
|
|
8936
|
+
if (handledOpenFileRequestId.current === openFileRequest.requestId) return;
|
|
8937
|
+
handledOpenFileRequestId.current = openFileRequest.requestId;
|
|
8938
|
+
openFile(openFileRequest.path, openFileRequest.line);
|
|
8939
|
+
}, [openFile, openFileRequest]);
|
|
8940
|
+
const openComputer = useCallback25(
|
|
8494
8941
|
(computerSessionId) => {
|
|
8495
8942
|
nextComputerRequestId.current += 1;
|
|
8496
8943
|
setRequestedComputer({
|
|
@@ -8499,8 +8946,10 @@ function SandboxWorkspace(props) {
|
|
|
8499
8946
|
requestId: nextComputerRequestId.current
|
|
8500
8947
|
});
|
|
8501
8948
|
setStoredSelection({ sessionId, tab: WORKBENCH_TAB_DESKTOP });
|
|
8949
|
+
onActiveTabChange?.(WORKBENCH_TAB_DESKTOP);
|
|
8950
|
+
onComputerSessionIdChange?.(computerSessionId);
|
|
8502
8951
|
},
|
|
8503
|
-
[sessionId]
|
|
8952
|
+
[onActiveTabChange, onComputerSessionIdChange, sessionId]
|
|
8504
8953
|
);
|
|
8505
8954
|
const {
|
|
8506
8955
|
tabs: workbenchTabs,
|
|
@@ -8518,8 +8967,16 @@ function SandboxWorkspace(props) {
|
|
|
8518
8967
|
...onNotify ? { onNotify } : {},
|
|
8519
8968
|
...browserWebSocketFactory ? { browserWebSocketFactory } : {},
|
|
8520
8969
|
...computerWebSocketFactory ? { computerWebSocketFactory } : {},
|
|
8970
|
+
...browserExtensionSetupUrl ? { browserExtensionSetupUrl } : {},
|
|
8971
|
+
initialFilePath,
|
|
8972
|
+
onFilePathChange,
|
|
8973
|
+
initialBrowserSessionId,
|
|
8974
|
+
onBrowserSessionIdChange,
|
|
8975
|
+
initialComputerSessionId,
|
|
8976
|
+
onComputerSessionIdChange,
|
|
8521
8977
|
requestedFilePath: requestedFile?.sessionId === sessionId ? requestedFile.path : null,
|
|
8522
8978
|
requestedFileRequestId: requestedFile?.sessionId === sessionId ? requestedFile.requestId : null,
|
|
8979
|
+
requestedFileLine: requestedFile?.sessionId === sessionId ? requestedFile.line : null,
|
|
8523
8980
|
onOpenFile: openFile,
|
|
8524
8981
|
onOpenComputerSession: openComputer,
|
|
8525
8982
|
requestedComputerSessionId: requestedComputer?.sessionId === sessionId ? requestedComputer.computerSessionId : null,
|
|
@@ -8528,10 +8985,17 @@ function SandboxWorkspace(props) {
|
|
|
8528
8985
|
const tabs = [...leadingTabs ?? [], ...workbenchTabs, ...trailingTabs ?? []];
|
|
8529
8986
|
const preferredTab = selectedTab ?? defaultTab;
|
|
8530
8987
|
const activeTab = preferredTab && tabs.some((tab) => tab.id === preferredTab) ? preferredTab : tabs[0]?.id;
|
|
8531
|
-
const selectTab =
|
|
8532
|
-
(tab) =>
|
|
8533
|
-
|
|
8988
|
+
const selectTab = useCallback25(
|
|
8989
|
+
(tab) => {
|
|
8990
|
+
setStoredSelection({ sessionId, tab });
|
|
8991
|
+
onActiveTabChange?.(tab);
|
|
8992
|
+
},
|
|
8993
|
+
[onActiveTabChange, sessionId]
|
|
8534
8994
|
);
|
|
8995
|
+
useEffect20(() => {
|
|
8996
|
+
if (selectedTab !== null || defaultTab === null) return;
|
|
8997
|
+
setStoredSelection({ sessionId, tab: defaultTab });
|
|
8998
|
+
}, [defaultTab, selectedTab, sessionId]);
|
|
8535
8999
|
return /* @__PURE__ */ jsx12(
|
|
8536
9000
|
WorkspaceDock,
|
|
8537
9001
|
{
|
|
@@ -8786,7 +9250,7 @@ function CenteredState({
|
|
|
8786
9250
|
}
|
|
8787
9251
|
|
|
8788
9252
|
// src/hooks/use-codex-accounts.ts
|
|
8789
|
-
import { useCallback as
|
|
9253
|
+
import { useCallback as useCallback26, useState as useState18 } from "react";
|
|
8790
9254
|
function isCodexAccountEvent(event) {
|
|
8791
9255
|
return event.type === "codex.account.switched";
|
|
8792
9256
|
}
|
|
@@ -8807,7 +9271,7 @@ function useCodexAccounts(options = {}) {
|
|
|
8807
9271
|
const codexClient = options.codexClient ?? client;
|
|
8808
9272
|
const sessionId = options.sessionId;
|
|
8809
9273
|
const sharedEvents = options.events;
|
|
8810
|
-
const load =
|
|
9274
|
+
const load = useCallback26(async () => {
|
|
8811
9275
|
const accountsP = codexClient.listCodexAccounts(workspaceId);
|
|
8812
9276
|
const sessionP = sessionId && codexClient.getSession ? codexClient.getSession(workspaceId, sessionId).catch(() => null) : Promise.resolve(null);
|
|
8813
9277
|
const [acc, session] = await Promise.all([accountsP, sessionP]);
|
|
@@ -8841,7 +9305,7 @@ function useCodexAccounts(options = {}) {
|
|
|
8841
9305
|
...sharedEvents !== void 0 ? { events: sharedEvents } : {}
|
|
8842
9306
|
}
|
|
8843
9307
|
);
|
|
8844
|
-
const pin =
|
|
9308
|
+
const pin = useCallback26(
|
|
8845
9309
|
async (target) => {
|
|
8846
9310
|
if (!sessionId || !codexClient.pinSessionCodexAccount) {
|
|
8847
9311
|
return false;
|
|
@@ -8857,7 +9321,7 @@ function useCodexAccounts(options = {}) {
|
|
|
8857
9321
|
},
|
|
8858
9322
|
[codexClient, workspaceId, sessionId, runMutation, refresh]
|
|
8859
9323
|
);
|
|
8860
|
-
const refreshUsage =
|
|
9324
|
+
const refreshUsage = useCallback26(async () => {
|
|
8861
9325
|
if (!codexClient.refreshCodexUsage) {
|
|
8862
9326
|
return false;
|
|
8863
9327
|
}
|
|
@@ -8889,7 +9353,7 @@ function useCodexAccounts(options = {}) {
|
|
|
8889
9353
|
}
|
|
8890
9354
|
|
|
8891
9355
|
// src/hooks/use-supergrok-accounts.ts
|
|
8892
|
-
import { useCallback as
|
|
9356
|
+
import { useCallback as useCallback27 } from "react";
|
|
8893
9357
|
var EMPTY = {
|
|
8894
9358
|
accounts: [],
|
|
8895
9359
|
activeAccountId: null,
|
|
@@ -8902,7 +9366,7 @@ var EMPTY = {
|
|
|
8902
9366
|
function useSuperGrokAccounts(options = {}) {
|
|
8903
9367
|
const { client, workspaceId } = useOpenGeni(options);
|
|
8904
9368
|
const supergrokClient = options.supergrokClient ?? client;
|
|
8905
|
-
const load =
|
|
9369
|
+
const load = useCallback27(
|
|
8906
9370
|
async () => await supergrokClient.listSuperGrokAccounts(workspaceId),
|
|
8907
9371
|
[supergrokClient, workspaceId]
|
|
8908
9372
|
);
|
|
@@ -8911,7 +9375,7 @@ function useSuperGrokAccounts(options = {}) {
|
|
|
8911
9375
|
pollIntervalMs: options.pollIntervalMs
|
|
8912
9376
|
});
|
|
8913
9377
|
const { run, mutating: activating, mutationError } = useMutationRunner();
|
|
8914
|
-
const activate =
|
|
9378
|
+
const activate = useCallback27(
|
|
8915
9379
|
async (accountId) => {
|
|
8916
9380
|
if (!supergrokClient.activateSuperGrokAccount) return false;
|
|
8917
9381
|
const result = await run(async () => {
|
|
@@ -8970,6 +9434,9 @@ export {
|
|
|
8970
9434
|
INITIAL_TRANSCRIPTION_CONTROL_STATE,
|
|
8971
9435
|
IndexedDbVoiceRecordingStore,
|
|
8972
9436
|
LightboxProvider,
|
|
9437
|
+
MACHINES_COMPOSER_POLL_MS,
|
|
9438
|
+
MACHINES_DASHBOARD_POLL_MS,
|
|
9439
|
+
MACHINES_SESSION_POLL_MS,
|
|
8973
9440
|
MACHINE_STATE_BADGE_META,
|
|
8974
9441
|
METRICS,
|
|
8975
9442
|
METRIC_WINDOWS,
|
|
@@ -8978,6 +9445,7 @@ export {
|
|
|
8978
9445
|
MachineDockBar,
|
|
8979
9446
|
MachineHealthPill,
|
|
8980
9447
|
MachineMetrics,
|
|
9448
|
+
MachineOperationPolicyEditor,
|
|
8981
9449
|
MachineStatusPill,
|
|
8982
9450
|
MachinesDashboard,
|
|
8983
9451
|
Markdown,
|
|
@@ -9120,6 +9588,10 @@ export {
|
|
|
9120
9588
|
rawTypeOf,
|
|
9121
9589
|
runnableLatencyModesForModel,
|
|
9122
9590
|
sandboxCommandExitCode,
|
|
9591
|
+
sandboxFileLocationFromHref,
|
|
9592
|
+
sandboxFilePathFromHref,
|
|
9593
|
+
sessionChromeGoalPillExplanation,
|
|
9594
|
+
sessionChromeGoalPillLabel,
|
|
9123
9595
|
sessionChromeGoalPillState,
|
|
9124
9596
|
sessionDisplayTitle,
|
|
9125
9597
|
sessionStatusFromEvents,
|
|
@@ -9165,6 +9637,7 @@ export {
|
|
|
9165
9637
|
useSandboxWorkspaceTabs,
|
|
9166
9638
|
useScheduledTasks,
|
|
9167
9639
|
useSession,
|
|
9640
|
+
useSessionBackgroundCommands,
|
|
9168
9641
|
useSessionCapabilities,
|
|
9169
9642
|
useSessionControl,
|
|
9170
9643
|
useSessionEvents,
|