@opengeni/react 0.37.0 → 0.40.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 +88 -2
- package/dist/{chunk-7CJOAW3V.js → chunk-EB67J347.js} +1 -135
- package/dist/chunk-EB67J347.js.map +1 -0
- package/dist/chunk-EKZH6IDG.js +141 -0
- package/dist/chunk-EKZH6IDG.js.map +1 -0
- package/dist/{chunk-FSPDND3P.js → chunk-JALF5FI3.js} +8 -6
- package/dist/{chunk-FSPDND3P.js.map → chunk-JALF5FI3.js.map} +1 -1
- package/dist/{chunk-UCZPNXV3.js → chunk-KR2SK5GJ.js} +1292 -711
- package/dist/chunk-KR2SK5GJ.js.map +1 -0
- package/dist/{chunk-IP22SLO6.js → chunk-OMCFRWHL.js} +28 -441
- package/dist/chunk-OMCFRWHL.js.map +1 -0
- package/dist/{chunk-HJ4OQVGW.js → chunk-Q2NCKWTK.js} +10 -5
- package/dist/chunk-Q2NCKWTK.js.map +1 -0
- package/dist/{chunk-ALA3UGWB.js → chunk-SXIQK54Z.js} +8 -6
- package/dist/{chunk-ALA3UGWB.js.map → chunk-SXIQK54Z.js.map} +1 -1
- package/dist/chunk-WZT5G5OR.js +433 -0
- package/dist/chunk-WZT5G5OR.js.map +1 -0
- package/dist/client.d.ts +2 -0
- package/dist/components/chat-composer.d.ts +5 -1
- package/dist/components/human-input-form.d.ts +16 -6
- package/dist/components/human-input-surface.d.ts +19 -0
- package/dist/components/machine-input-display.d.ts +12 -0
- package/dist/components/message-timeline.d.ts +6 -1
- package/dist/components/session-chrome.d.ts +1 -0
- package/dist/components/tip-follow.d.ts +30 -0
- package/dist/composer.js +14 -11
- package/dist/hooks/use-composer.d.ts +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.js +2246 -2236
- package/dist/index.js.map +1 -1
- package/dist/machines.js +4 -3
- package/dist/realtime/dropdown-menu.d.ts +16 -0
- package/dist/realtime/realtime-control.d.ts +126 -0
- package/dist/realtime.d.ts +11 -0
- package/dist/realtime.js +1023 -0
- package/dist/realtime.js.map +1 -0
- package/dist/session-context.d.ts +7 -1
- package/dist/session-ui.d.ts +2 -0
- package/dist/session-ui.js +5 -3
- package/dist/session.js +4 -3
- package/dist/timeline/compute-label.d.ts +2 -0
- package/dist/timeline/index.d.ts +1 -0
- package/package.json +7 -2
- package/src/client.ts +13 -0
- package/src/components/chat-composer.tsx +11 -2
- package/src/components/composer-transcription-control.tsx +2 -2
- package/src/components/composer.tsx +14 -5
- package/src/components/human-input-form.tsx +506 -186
- package/src/components/human-input-surface.tsx +80 -0
- package/src/components/machine-input-display.ts +83 -0
- package/src/components/message-timeline.tsx +348 -308
- package/src/components/model-policy-picker.tsx +8 -3
- package/src/components/sandbox-files.tsx +1 -1
- package/src/components/session-chrome.tsx +3 -1
- package/src/components/tip-follow.ts +127 -6
- package/src/hooks/use-composer.ts +16 -13
- package/src/index.ts +4 -0
- package/src/realtime/dropdown-menu.tsx +123 -0
- package/src/realtime/realtime-control.tsx +1178 -0
- package/src/realtime.ts +27 -0
- package/src/session-context.ts +16 -0
- package/src/session-ui.ts +2 -0
- package/src/timeline/compute-label.tsx +18 -0
- package/src/timeline/index.ts +3 -0
- package/src/timeline/tool-renderers.tsx +232 -43
- package/styles/index.css +61 -0
- package/styles/tokens.css +2 -0
- package/dist/chunk-7CJOAW3V.js.map +0 -1
- package/dist/chunk-HJ4OQVGW.js.map +0 -1
- package/dist/chunk-IP22SLO6.js.map +0 -1
- package/dist/chunk-UCZPNXV3.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -25,7 +25,7 @@ import {
|
|
|
25
25
|
healthPulses,
|
|
26
26
|
pointsFor,
|
|
27
27
|
useMachines
|
|
28
|
-
} from "./chunk-
|
|
28
|
+
} from "./chunk-SXIQK54Z.js";
|
|
29
29
|
import {
|
|
30
30
|
SESSION_EVENT_BROWSER_MAX_BYTES,
|
|
31
31
|
SESSION_EVENT_BROWSER_MAX_COUNT,
|
|
@@ -52,7 +52,7 @@ import {
|
|
|
52
52
|
useSessionLineage,
|
|
53
53
|
useSessionMcpApprovalPolicy,
|
|
54
54
|
useTurnQueue
|
|
55
|
-
} from "./chunk-
|
|
55
|
+
} from "./chunk-JALF5FI3.js";
|
|
56
56
|
import {
|
|
57
57
|
ActivityDisclosure,
|
|
58
58
|
ActivityRail,
|
|
@@ -62,6 +62,7 @@ import {
|
|
|
62
62
|
CopyHoverFrame,
|
|
63
63
|
DisclosureDefaultsProvider,
|
|
64
64
|
HumanInputForm,
|
|
65
|
+
HumanInputSurface,
|
|
65
66
|
LightboxProvider,
|
|
66
67
|
Markdown,
|
|
67
68
|
MediaEmpty,
|
|
@@ -106,7 +107,8 @@ import {
|
|
|
106
107
|
useLightboxOptional,
|
|
107
108
|
useThemeType,
|
|
108
109
|
v4aToGitFileDiff
|
|
109
|
-
} from "./chunk-
|
|
110
|
+
} from "./chunk-KR2SK5GJ.js";
|
|
111
|
+
import "./chunk-YDNWMKXO.js";
|
|
110
112
|
import {
|
|
111
113
|
buildTimeline,
|
|
112
114
|
creditExhaustedFromEvents,
|
|
@@ -119,7 +121,6 @@ import {
|
|
|
119
121
|
Actions,
|
|
120
122
|
AttachButton,
|
|
121
123
|
Attachments,
|
|
122
|
-
BillingClassMark,
|
|
123
124
|
CommandPalette,
|
|
124
125
|
CommandPalette2,
|
|
125
126
|
ComposerTranscriptionControl,
|
|
@@ -133,14 +134,9 @@ import {
|
|
|
133
134
|
Input,
|
|
134
135
|
ModelPicker,
|
|
135
136
|
ModelPicker2,
|
|
136
|
-
ModelPolicyPicker,
|
|
137
|
-
ModelPolicyPickerMenu,
|
|
138
137
|
OPEN_WORKSTREAM_CONTROL_EVENT,
|
|
139
138
|
PauseButton,
|
|
140
139
|
PausedState,
|
|
141
|
-
PickerAnimatedPage,
|
|
142
|
-
PickerBackHeader,
|
|
143
|
-
PickerNavRow,
|
|
144
140
|
RestoredResources,
|
|
145
141
|
Root,
|
|
146
142
|
SendButton,
|
|
@@ -150,7 +146,6 @@ import {
|
|
|
150
146
|
argHint,
|
|
151
147
|
defaultChatComposerMessages,
|
|
152
148
|
defaultCommands,
|
|
153
|
-
defaultModelPolicyPickerMessages,
|
|
154
149
|
filterCommands,
|
|
155
150
|
firstMissingRequiredArg,
|
|
156
151
|
hasPermission,
|
|
@@ -161,21 +156,32 @@ import {
|
|
|
161
156
|
useSlashCommands,
|
|
162
157
|
useTranscription,
|
|
163
158
|
useVoiceInput
|
|
164
|
-
} from "./chunk-
|
|
159
|
+
} from "./chunk-OMCFRWHL.js";
|
|
165
160
|
import {
|
|
166
161
|
FILE_ONLY_MESSAGE_TEXT,
|
|
167
162
|
composeSendInput,
|
|
168
163
|
shouldSteerOnKey,
|
|
169
164
|
shouldSubmitOnKey,
|
|
170
165
|
useComposer
|
|
171
|
-
} from "./chunk-
|
|
166
|
+
} from "./chunk-Q2NCKWTK.js";
|
|
172
167
|
import {
|
|
173
168
|
useMutationRunner,
|
|
174
|
-
useOpenGeni,
|
|
175
|
-
useOpenGeniClient,
|
|
176
169
|
usePolledValue,
|
|
177
170
|
useSessionEventTrigger
|
|
178
|
-
} from "./chunk-
|
|
171
|
+
} from "./chunk-EB67J347.js";
|
|
172
|
+
import {
|
|
173
|
+
BillingClassMark,
|
|
174
|
+
ModelPolicyPicker,
|
|
175
|
+
ModelPolicyPickerMenu,
|
|
176
|
+
PickerAnimatedPage,
|
|
177
|
+
PickerBackHeader,
|
|
178
|
+
PickerNavRow,
|
|
179
|
+
defaultModelPolicyPickerMessages
|
|
180
|
+
} from "./chunk-WZT5G5OR.js";
|
|
181
|
+
import {
|
|
182
|
+
useOpenGeni,
|
|
183
|
+
useOpenGeniClient
|
|
184
|
+
} from "./chunk-EKZH6IDG.js";
|
|
179
185
|
import {
|
|
180
186
|
advancedSourceSummary,
|
|
181
187
|
availabilityReasonLabel,
|
|
@@ -201,9 +207,6 @@ import {
|
|
|
201
207
|
TooltipTrigger,
|
|
202
208
|
usePortalTokenStyle
|
|
203
209
|
} from "./chunk-ATSTR5P3.js";
|
|
204
|
-
import {
|
|
205
|
-
cn
|
|
206
|
-
} from "./chunk-TK7G6XLT.js";
|
|
207
210
|
import {
|
|
208
211
|
COMPOSER_PAYMENT_REQUIRED_MESSAGE,
|
|
209
212
|
CREDIT_EXHAUSTION_MESSAGE,
|
|
@@ -217,7 +220,9 @@ import {
|
|
|
217
220
|
truncate,
|
|
218
221
|
tryParseJson
|
|
219
222
|
} from "./chunk-LAKLF4PA.js";
|
|
220
|
-
import
|
|
223
|
+
import {
|
|
224
|
+
cn
|
|
225
|
+
} from "./chunk-TK7G6XLT.js";
|
|
221
226
|
|
|
222
227
|
// src/hooks/use-last-started-turn-policy.ts
|
|
223
228
|
import { useCallback, useEffect } from "react";
|
|
@@ -3333,8 +3338,10 @@ function ChatComposer({
|
|
|
3333
3338
|
disabled,
|
|
3334
3339
|
autoFocus,
|
|
3335
3340
|
hint,
|
|
3341
|
+
controlsLeading,
|
|
3336
3342
|
controlsStart,
|
|
3337
3343
|
actionsStart,
|
|
3344
|
+
attachButtonClassName,
|
|
3338
3345
|
transcription,
|
|
3339
3346
|
header,
|
|
3340
3347
|
onPaste,
|
|
@@ -3364,7 +3371,9 @@ function ChatComposer({
|
|
|
3364
3371
|
onPaste,
|
|
3365
3372
|
messages
|
|
3366
3373
|
});
|
|
3367
|
-
const hasControls = Boolean(
|
|
3374
|
+
const hasControls = Boolean(
|
|
3375
|
+
attachments || models || controlsLeading || controlsStart || transcription
|
|
3376
|
+
);
|
|
3368
3377
|
const stackActions = hasControls && Boolean(actionsStart);
|
|
3369
3378
|
return /* @__PURE__ */ jsxs2(Root, { controller, className, children: [
|
|
3370
3379
|
/* @__PURE__ */ jsxs2(Frame, { children: [
|
|
@@ -3377,7 +3386,8 @@ function ChatComposer({
|
|
|
3377
3386
|
/* @__PURE__ */ jsx2(Input, { placeholder, autoFocus }),
|
|
3378
3387
|
controller.confirmState ? /* @__PURE__ */ jsx2(Confirmation, {}) : /* @__PURE__ */ jsxs2(Footer, { className: stackActions ? "flex-wrap" : void 0, children: [
|
|
3379
3388
|
hasControls ? /* @__PURE__ */ jsxs2(Controls, { className: stackActions ? "w-full sm:w-auto" : void 0, children: [
|
|
3380
|
-
|
|
3389
|
+
controlsLeading,
|
|
3390
|
+
/* @__PURE__ */ jsx2(AttachButton, { className: attachButtonClassName }),
|
|
3381
3391
|
transcription ? /* @__PURE__ */ jsx2(ComposerTranscriptionControl, { ...transcription }) : null,
|
|
3382
3392
|
models ? /* @__PURE__ */ jsx2(ModelPicker2, { models, value: selectedModel, onChange: onSelectModel }) : null,
|
|
3383
3393
|
controlsStart
|
|
@@ -5112,2414 +5122,2412 @@ function ContextMenu({
|
|
|
5112
5122
|
);
|
|
5113
5123
|
}
|
|
5114
5124
|
|
|
5115
|
-
// src/components/
|
|
5116
|
-
import {
|
|
5117
|
-
function DiffView({
|
|
5118
|
-
diff,
|
|
5119
|
-
fallback,
|
|
5120
|
-
layout = "unified",
|
|
5121
|
-
themeType,
|
|
5122
|
-
isRepo = true,
|
|
5123
|
-
emptyState,
|
|
5124
|
-
className
|
|
5125
|
-
}) {
|
|
5126
|
-
if (diff.length === 0) {
|
|
5127
|
-
return /* @__PURE__ */ jsx6("div", { className: cn("p-3 text-og-sm text-og-fg-subtle", className), children: emptyState ?? (isRepo ? "No changes" : "No repository mounted") });
|
|
5128
|
-
}
|
|
5129
|
-
return /* @__PURE__ */ jsx6(
|
|
5130
|
-
PierreDiff,
|
|
5131
|
-
{
|
|
5132
|
-
diff,
|
|
5133
|
-
layout,
|
|
5134
|
-
themeType,
|
|
5135
|
-
...fallback !== void 0 ? { fallback } : {},
|
|
5136
|
-
className
|
|
5137
|
-
}
|
|
5138
|
-
);
|
|
5139
|
-
}
|
|
5140
|
-
|
|
5141
|
-
// src/components/pierre-file.tsx
|
|
5125
|
+
// src/components/workbench-changes.tsx
|
|
5126
|
+
import { ArrowUpRightIcon, FileWarningIcon, HistoryIcon } from "lucide-react";
|
|
5142
5127
|
import {
|
|
5143
|
-
|
|
5144
|
-
|
|
5145
|
-
|
|
5146
|
-
|
|
5128
|
+
useCallback as useCallback18,
|
|
5129
|
+
useEffect as useEffect17,
|
|
5130
|
+
useId as useId3,
|
|
5131
|
+
useLayoutEffect as useLayoutEffect2,
|
|
5132
|
+
useMemo as useMemo9,
|
|
5133
|
+
useRef as useRef15,
|
|
5134
|
+
useState as useState14
|
|
5147
5135
|
} from "react";
|
|
5148
|
-
import {
|
|
5149
|
-
|
|
5150
|
-
|
|
5151
|
-
|
|
5152
|
-
|
|
5153
|
-
|
|
5154
|
-
|
|
5155
|
-
|
|
5156
|
-
|
|
5157
|
-
|
|
5158
|
-
|
|
5159
|
-
|
|
5160
|
-
|
|
5161
|
-
|
|
5162
|
-
|
|
5163
|
-
|
|
5136
|
+
import { VList as VList2 } from "virtua";
|
|
5137
|
+
|
|
5138
|
+
// src/hooks/use-windowed-sections.ts
|
|
5139
|
+
import { useCallback as useCallback17, useEffect as useEffect16, useMemo as useMemo8, useRef as useRef14, useState as useState13 } from "react";
|
|
5140
|
+
function computeWindowRange(heights, scrollTop, viewport, overscan) {
|
|
5141
|
+
const n = heights.length;
|
|
5142
|
+
if (n === 0) return { start: 0, end: 0 };
|
|
5143
|
+
const top = Math.max(scrollTop, 0);
|
|
5144
|
+
const bottom = top + Math.max(viewport, 0);
|
|
5145
|
+
let firstVisible = -1;
|
|
5146
|
+
let lastVisible = -1;
|
|
5147
|
+
let acc = 0;
|
|
5148
|
+
let anchor = 0;
|
|
5149
|
+
for (let i = 0; i < n; i++) {
|
|
5150
|
+
const secTop = acc;
|
|
5151
|
+
const secBottom = secTop + Math.max(heights[i] ?? 0, 0);
|
|
5152
|
+
if (secTop <= top) anchor = i;
|
|
5153
|
+
if (secBottom > top && secTop < bottom) {
|
|
5154
|
+
if (firstVisible === -1) firstVisible = i;
|
|
5155
|
+
lastVisible = i;
|
|
5156
|
+
}
|
|
5157
|
+
acc = secBottom;
|
|
5158
|
+
}
|
|
5159
|
+
if (firstVisible === -1) {
|
|
5160
|
+
firstVisible = anchor;
|
|
5161
|
+
lastVisible = anchor;
|
|
5162
|
+
}
|
|
5163
|
+
return {
|
|
5164
|
+
start: Math.max(0, firstVisible - overscan),
|
|
5165
|
+
end: Math.min(n, lastVisible + 1 + overscan)
|
|
5166
|
+
};
|
|
5167
|
+
}
|
|
5168
|
+
function useWindowedSections(opts) {
|
|
5169
|
+
const { count, estimateHeight, overscan = 3 } = opts;
|
|
5170
|
+
const scrollRef = useRef14(null);
|
|
5171
|
+
const [heights, setHeights] = useState13(
|
|
5172
|
+
() => Array.from({ length: count }, (_, i) => estimateHeight(i))
|
|
5173
|
+
);
|
|
5164
5174
|
useEffect16(() => {
|
|
5165
|
-
|
|
5166
|
-
|
|
5167
|
-
|
|
5175
|
+
setHeights((prev) => {
|
|
5176
|
+
if (prev.length === count) return prev;
|
|
5177
|
+
return Array.from({ length: count }, (_, i) => prev[i] ?? estimateHeight(i));
|
|
5168
5178
|
});
|
|
5179
|
+
}, [count]);
|
|
5180
|
+
const heightsRef = useRef14(heights);
|
|
5181
|
+
heightsRef.current = heights;
|
|
5182
|
+
const [range, setRange] = useState13(() => ({
|
|
5183
|
+
start: 0,
|
|
5184
|
+
end: Math.min(count, overscan + 1)
|
|
5185
|
+
}));
|
|
5186
|
+
const recompute = useCallback17(() => {
|
|
5187
|
+
const el = scrollRef.current;
|
|
5188
|
+
if (!el) return;
|
|
5189
|
+
const next = computeWindowRange(heightsRef.current, el.scrollTop, el.clientHeight, overscan);
|
|
5190
|
+
setRange((prev) => prev.start === next.start && prev.end === next.end ? prev : next);
|
|
5191
|
+
}, [overscan]);
|
|
5192
|
+
useEffect16(() => {
|
|
5193
|
+
const el = scrollRef.current;
|
|
5194
|
+
if (!el) return;
|
|
5195
|
+
let raf = 0;
|
|
5196
|
+
const onScroll = () => {
|
|
5197
|
+
if (raf) return;
|
|
5198
|
+
raf = typeof requestAnimationFrame === "function" ? requestAnimationFrame(() => {
|
|
5199
|
+
raf = 0;
|
|
5200
|
+
recompute();
|
|
5201
|
+
}) : (recompute(), 0);
|
|
5202
|
+
};
|
|
5203
|
+
el.addEventListener("scroll", onScroll, { passive: true });
|
|
5204
|
+
const ro = typeof ResizeObserver !== "undefined" ? new ResizeObserver(() => recompute()) : null;
|
|
5205
|
+
ro?.observe(el);
|
|
5206
|
+
recompute();
|
|
5169
5207
|
return () => {
|
|
5170
|
-
|
|
5208
|
+
el.removeEventListener("scroll", onScroll);
|
|
5209
|
+
ro?.disconnect();
|
|
5210
|
+
if (raf && typeof cancelAnimationFrame === "function") cancelAnimationFrame(raf);
|
|
5171
5211
|
};
|
|
5212
|
+
}, [recompute, count]);
|
|
5213
|
+
const measure = useCallback17((index, height) => {
|
|
5214
|
+
if (height <= 0) return;
|
|
5215
|
+
setHeights((prev) => {
|
|
5216
|
+
if (Math.abs((prev[index] ?? 0) - height) < 1) return prev;
|
|
5217
|
+
const next = prev.slice();
|
|
5218
|
+
next[index] = height;
|
|
5219
|
+
return next;
|
|
5220
|
+
});
|
|
5172
5221
|
}, []);
|
|
5173
|
-
const
|
|
5174
|
-
|
|
5175
|
-
|
|
5176
|
-
|
|
5177
|
-
|
|
5178
|
-
|
|
5179
|
-
|
|
5180
|
-
|
|
5181
|
-
|
|
5182
|
-
|
|
5183
|
-
|
|
5184
|
-
|
|
5185
|
-
|
|
5222
|
+
const offsets = useMemo8(() => {
|
|
5223
|
+
const out = new Array(count + 1);
|
|
5224
|
+
out[0] = 0;
|
|
5225
|
+
for (let i = 0; i < count; i++) out[i + 1] = (out[i] ?? 0) + Math.max(heights[i] ?? 0, 0);
|
|
5226
|
+
return out;
|
|
5227
|
+
}, [heights, count]);
|
|
5228
|
+
useEffect16(() => {
|
|
5229
|
+
recompute();
|
|
5230
|
+
}, [offsets, recompute]);
|
|
5231
|
+
const scrollToIndex = useCallback17(
|
|
5232
|
+
(index) => {
|
|
5233
|
+
const el = scrollRef.current;
|
|
5234
|
+
if (!el) return;
|
|
5235
|
+
const clamped = Math.max(0, Math.min(count - 1, index));
|
|
5236
|
+
el.scrollTop = offsets[clamped] ?? 0;
|
|
5186
5237
|
},
|
|
5187
|
-
|
|
5188
|
-
};
|
|
5189
|
-
const pierreVars = {
|
|
5190
|
-
"--diffs-dark-bg": "var(--og-color-bg)",
|
|
5191
|
-
"--diffs-light-bg": "var(--og-color-bg)",
|
|
5192
|
-
"--diffs-bg-buffer-override": "var(--og-color-surface-1)",
|
|
5193
|
-
"--diffs-bg-separator-override": "var(--og-color-surface-1)",
|
|
5194
|
-
"--diffs-font-size": "var(--og-code-font-size)",
|
|
5195
|
-
"--diffs-line-height": "var(--og-code-line-height)"
|
|
5196
|
-
};
|
|
5197
|
-
return /* @__PURE__ */ jsx7("div", { className: cn("min-w-0", className), "data-opengeni-pierre-file": true, style: pierreVars, children: /* @__PURE__ */ jsx7(Suspense, { fallback: loading ?? /* @__PURE__ */ jsx7(FileSkeleton, {}), children: /* @__PURE__ */ jsx7(
|
|
5198
|
-
LazyFile,
|
|
5199
|
-
{
|
|
5200
|
-
file: { name, contents },
|
|
5201
|
-
options,
|
|
5202
|
-
...disableWorkerPool !== void 0 ? { disableWorkerPool } : {}
|
|
5203
|
-
}
|
|
5204
|
-
) }) });
|
|
5205
|
-
}
|
|
5206
|
-
function PlainFile({ name, contents }) {
|
|
5207
|
-
return /* @__PURE__ */ jsx7(
|
|
5208
|
-
"pre",
|
|
5209
|
-
{
|
|
5210
|
-
className: "overflow-auto whitespace-pre p-2 font-og-mono text-og-sm text-og-fg",
|
|
5211
|
-
"data-file": name,
|
|
5212
|
-
children: contents
|
|
5213
|
-
}
|
|
5238
|
+
[offsets, count]
|
|
5214
5239
|
);
|
|
5215
|
-
|
|
5216
|
-
|
|
5217
|
-
|
|
5240
|
+
return {
|
|
5241
|
+
scrollRef,
|
|
5242
|
+
range,
|
|
5243
|
+
offsets,
|
|
5244
|
+
totalHeight: offsets[count] ?? 0,
|
|
5245
|
+
measure,
|
|
5246
|
+
scrollToIndex
|
|
5247
|
+
};
|
|
5218
5248
|
}
|
|
5219
5249
|
|
|
5220
|
-
// src/components/
|
|
5221
|
-
import {
|
|
5222
|
-
|
|
5223
|
-
|
|
5224
|
-
|
|
5225
|
-
|
|
5226
|
-
|
|
5227
|
-
|
|
5228
|
-
|
|
5229
|
-
|
|
5230
|
-
|
|
5231
|
-
|
|
5232
|
-
|
|
5233
|
-
|
|
5234
|
-
|
|
5235
|
-
|
|
5236
|
-
|
|
5250
|
+
// src/components/workbench-changes.tsx
|
|
5251
|
+
import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
5252
|
+
var GROUP_THRESHOLD = 20;
|
|
5253
|
+
var OVERSCAN = 2;
|
|
5254
|
+
var HEADER_PX = 30;
|
|
5255
|
+
var LINE_PX = 18;
|
|
5256
|
+
var GUARD_BODY_PX = 52;
|
|
5257
|
+
var COMPACT_SURFACE_PX = 560;
|
|
5258
|
+
var useChangesLayoutEffect = typeof window === "undefined" ? useEffect17 : useLayoutEffect2;
|
|
5259
|
+
var STATUS_TINT2 = {
|
|
5260
|
+
added: "text-og-status-idle",
|
|
5261
|
+
modified: "text-og-status-running",
|
|
5262
|
+
deleted: "text-og-status-failed",
|
|
5263
|
+
renamed: "text-og-accent",
|
|
5264
|
+
copied: "text-og-accent",
|
|
5265
|
+
untracked: "text-og-fg-subtle",
|
|
5266
|
+
ignored: "text-og-fg-subtle",
|
|
5267
|
+
conflicted: "text-og-status-failed",
|
|
5268
|
+
typechange: "text-og-status-running"
|
|
5237
5269
|
};
|
|
5238
|
-
|
|
5239
|
-
|
|
5240
|
-
|
|
5241
|
-
|
|
5242
|
-
|
|
5243
|
-
|
|
5244
|
-
|
|
5245
|
-
|
|
5246
|
-
|
|
5247
|
-
|
|
5248
|
-
|
|
5249
|
-
|
|
5250
|
-
|
|
5251
|
-
|
|
5252
|
-
|
|
5253
|
-
|
|
5254
|
-
|
|
5255
|
-
|
|
5256
|
-
|
|
5257
|
-
|
|
5258
|
-
|
|
5259
|
-
|
|
5260
|
-
|
|
5261
|
-
|
|
5262
|
-
|
|
5263
|
-
|
|
5264
|
-
|
|
5265
|
-
|
|
5266
|
-
|
|
5267
|
-
|
|
5268
|
-
|
|
5269
|
-
|
|
5270
|
-
|
|
5270
|
+
var STATUS_LETTER = {
|
|
5271
|
+
added: "A",
|
|
5272
|
+
modified: "M",
|
|
5273
|
+
deleted: "D",
|
|
5274
|
+
renamed: "R",
|
|
5275
|
+
copied: "C",
|
|
5276
|
+
untracked: "U",
|
|
5277
|
+
ignored: "I",
|
|
5278
|
+
conflicted: "!",
|
|
5279
|
+
typechange: "T"
|
|
5280
|
+
};
|
|
5281
|
+
function compactFileLabel(file) {
|
|
5282
|
+
const slash = file.path.lastIndexOf("/");
|
|
5283
|
+
const basename = slash >= 0 ? file.path.slice(slash + 1) : file.path;
|
|
5284
|
+
const parent = slash >= 0 ? file.path.slice(0, slash + 1) : "";
|
|
5285
|
+
return `${STATUS_LETTER[file.status]} \xB7 ${basename}${parent ? ` \u2014 ${parent}` : ""}`;
|
|
5286
|
+
}
|
|
5287
|
+
function buildRail(files) {
|
|
5288
|
+
const roots = new Set(
|
|
5289
|
+
files.flatMap((file) => file.repoRoot === void 0 ? [] : [file.repoRoot])
|
|
5290
|
+
);
|
|
5291
|
+
const groupByRepo = roots.size > 1;
|
|
5292
|
+
if (!groupByRepo && files.length <= GROUP_THRESHOLD) {
|
|
5293
|
+
return {
|
|
5294
|
+
orderedFiles: files,
|
|
5295
|
+
rows: files.map((file, index) => ({ kind: "file", file, index })),
|
|
5296
|
+
grouped: false
|
|
5297
|
+
};
|
|
5298
|
+
}
|
|
5299
|
+
const groups = /* @__PURE__ */ new Map();
|
|
5300
|
+
for (const file of files) {
|
|
5301
|
+
const slash = file.path.indexOf("/");
|
|
5302
|
+
const key = groupByRepo ? file.repoRoot || "(workspace)" : slash > 0 ? file.path.slice(0, slash) : "(root)";
|
|
5303
|
+
const bucket = groups.get(key);
|
|
5304
|
+
if (bucket) bucket.push(file);
|
|
5305
|
+
else groups.set(key, [file]);
|
|
5306
|
+
}
|
|
5307
|
+
const labels = [...groups.keys()].sort((a, b) => a.localeCompare(b));
|
|
5308
|
+
const orderedFiles = [];
|
|
5309
|
+
const rows = [];
|
|
5310
|
+
for (const label of labels) {
|
|
5311
|
+
const bucket = (groups.get(label) ?? []).slice().sort((a, b) => a.path.localeCompare(b.path));
|
|
5312
|
+
rows.push({ kind: "group", label, count: bucket.length });
|
|
5313
|
+
for (const file of bucket) {
|
|
5314
|
+
rows.push({ kind: "file", file, index: orderedFiles.length });
|
|
5315
|
+
orderedFiles.push(file);
|
|
5316
|
+
}
|
|
5271
5317
|
}
|
|
5318
|
+
return { orderedFiles, rows, grouped: true };
|
|
5272
5319
|
}
|
|
5273
|
-
function
|
|
5274
|
-
|
|
5275
|
-
|
|
5276
|
-
|
|
5277
|
-
|
|
5278
|
-
|
|
5279
|
-
|
|
5280
|
-
|
|
5281
|
-
|
|
5282
|
-
|
|
5283
|
-
|
|
5320
|
+
function isGuarded(file) {
|
|
5321
|
+
return file.isBinary || file.truncated;
|
|
5322
|
+
}
|
|
5323
|
+
function estimateSectionHeight(file) {
|
|
5324
|
+
if (isGuarded(file)) return HEADER_PX + GUARD_BODY_PX;
|
|
5325
|
+
let lines = 0;
|
|
5326
|
+
for (const hunk of file.hunks) lines += hunk.lines.length + 1;
|
|
5327
|
+
return HEADER_PX + Math.max(lines, 1) * LINE_PX + 8;
|
|
5328
|
+
}
|
|
5329
|
+
function WorkbenchChanges({
|
|
5330
|
+
diff,
|
|
5331
|
+
source,
|
|
5332
|
+
capturedAt,
|
|
5333
|
+
captureRevision,
|
|
5334
|
+
themeType,
|
|
5335
|
+
onOpenFile,
|
|
5284
5336
|
className
|
|
5285
5337
|
}) {
|
|
5286
|
-
const
|
|
5287
|
-
const [
|
|
5288
|
-
const [
|
|
5289
|
-
const
|
|
5290
|
-
const [
|
|
5291
|
-
const [
|
|
5292
|
-
const
|
|
5293
|
-
const
|
|
5294
|
-
|
|
5295
|
-
|
|
5296
|
-
const lastSeed = useRef14({ path, contents: initialContents });
|
|
5297
|
-
useEffect17(() => {
|
|
5298
|
-
const seedChanged = lastSeed.current.path !== path || lastSeed.current.contents !== initialContents;
|
|
5299
|
-
if (!seedChanged) return;
|
|
5300
|
-
lastSeed.current = { path, contents: initialContents };
|
|
5301
|
-
editIntentFiredRef.current = false;
|
|
5302
|
-
setValue(initialContents);
|
|
5303
|
-
setBaseline(initialContents);
|
|
5304
|
-
setSaveError(null);
|
|
5305
|
-
setSavedTick(false);
|
|
5306
|
-
}, [path, initialContents]);
|
|
5307
|
-
const noteEdit = useCallback17(
|
|
5308
|
-
(next) => {
|
|
5309
|
-
setValue(next);
|
|
5310
|
-
setSavedTick(false);
|
|
5311
|
-
if (!editIntentFiredRef.current && next !== baseline) {
|
|
5312
|
-
editIntentFiredRef.current = true;
|
|
5313
|
-
onEditIntent?.();
|
|
5314
|
-
}
|
|
5315
|
-
},
|
|
5316
|
-
[baseline, onEditIntent]
|
|
5338
|
+
const rootRef = useRef15(null);
|
|
5339
|
+
const [compact, setCompact] = useState14(false);
|
|
5340
|
+
const [coarsePointer, setCoarsePointer] = useState14(false);
|
|
5341
|
+
const resolvedTheme = useThemeType(themeType);
|
|
5342
|
+
const [layout, setLayout] = useState14("unified");
|
|
5343
|
+
const [activePath, setActivePath] = useState14(null);
|
|
5344
|
+
const pickerId = useId3();
|
|
5345
|
+
const unicodeFontPaths = useMemo9(
|
|
5346
|
+
() => diff.flatMap((file) => file.oldPath ? [file.path, file.oldPath] : [file.path]),
|
|
5347
|
+
[diff]
|
|
5317
5348
|
);
|
|
5318
|
-
|
|
5319
|
-
|
|
5320
|
-
|
|
5321
|
-
|
|
5322
|
-
|
|
5323
|
-
|
|
5324
|
-
|
|
5325
|
-
|
|
5326
|
-
|
|
5327
|
-
languageExtension = await LANGUAGE_LOADERS[langKey]?.() ?? null;
|
|
5328
|
-
} catch {
|
|
5329
|
-
languageExtension = null;
|
|
5330
|
-
}
|
|
5331
|
-
}
|
|
5332
|
-
if (cancelled) return;
|
|
5333
|
-
const saveKeymapExtension = (run) => cmMod.Prec.highest(
|
|
5334
|
-
cmMod.keymap.of([
|
|
5335
|
-
{
|
|
5336
|
-
key: "Mod-s",
|
|
5337
|
-
preventDefault: true,
|
|
5338
|
-
run: () => {
|
|
5339
|
-
run();
|
|
5340
|
-
return true;
|
|
5341
|
-
}
|
|
5342
|
-
}
|
|
5343
|
-
])
|
|
5344
|
-
);
|
|
5345
|
-
setBundle({
|
|
5346
|
-
Editor: cmMod.default,
|
|
5347
|
-
saveKeymapExtension,
|
|
5348
|
-
languageExtension
|
|
5349
|
-
});
|
|
5350
|
-
setFailed(false);
|
|
5351
|
-
} catch {
|
|
5352
|
-
if (!cancelled) setFailed(true);
|
|
5353
|
-
}
|
|
5354
|
-
})();
|
|
5355
|
-
return () => {
|
|
5356
|
-
cancelled = true;
|
|
5349
|
+
useUnicodeFallbackFonts(unicodeFontPaths);
|
|
5350
|
+
const { orderedFiles, rows, grouped } = useMemo9(() => buildRail(diff), [diff]);
|
|
5351
|
+
const selectedIndex = orderedFiles.findIndex((file) => file.path === activePath);
|
|
5352
|
+
const activeIndex = selectedIndex >= 0 ? selectedIndex : 0;
|
|
5353
|
+
useChangesLayoutEffect(() => {
|
|
5354
|
+
const root = rootRef.current;
|
|
5355
|
+
if (!root || typeof ResizeObserver === "undefined") return;
|
|
5356
|
+
const update = (width) => {
|
|
5357
|
+
if (width > 0) setCompact(width < COMPACT_SURFACE_PX);
|
|
5357
5358
|
};
|
|
5358
|
-
|
|
5359
|
-
|
|
5359
|
+
update(root.getBoundingClientRect().width);
|
|
5360
|
+
const observer = new ResizeObserver((entries) => {
|
|
5361
|
+
update(entries[0]?.contentRect.width ?? root.getBoundingClientRect().width);
|
|
5362
|
+
});
|
|
5363
|
+
observer.observe(root);
|
|
5364
|
+
return () => observer.disconnect();
|
|
5365
|
+
}, []);
|
|
5366
|
+
useChangesLayoutEffect(() => {
|
|
5367
|
+
if (typeof window === "undefined" || typeof window.matchMedia !== "function") return;
|
|
5368
|
+
const query = window.matchMedia("(pointer: coarse)");
|
|
5369
|
+
const update = () => setCoarsePointer(query.matches);
|
|
5370
|
+
update();
|
|
5371
|
+
if (typeof query.addEventListener === "function") {
|
|
5372
|
+
query.addEventListener("change", update);
|
|
5373
|
+
return () => query.removeEventListener("change", update);
|
|
5374
|
+
}
|
|
5375
|
+
if (typeof query.addListener === "function") {
|
|
5376
|
+
query.addListener(update);
|
|
5377
|
+
return () => query.removeListener(update);
|
|
5378
|
+
}
|
|
5379
|
+
}, []);
|
|
5380
|
+
const additions = useMemo9(() => diff.reduce((sum, f) => sum + f.additions, 0), [diff]);
|
|
5381
|
+
const deletions = useMemo9(() => diff.reduce((sum, f) => sum + f.deletions, 0), [diff]);
|
|
5382
|
+
const estimateHeight = useCallback18(
|
|
5383
|
+
(index) => {
|
|
5384
|
+
const file = orderedFiles[index];
|
|
5385
|
+
return file ? estimateSectionHeight(file) : HEADER_PX + LINE_PX;
|
|
5386
|
+
},
|
|
5387
|
+
[orderedFiles]
|
|
5388
|
+
);
|
|
5389
|
+
const windowed = useWindowedSections({
|
|
5390
|
+
count: orderedFiles.length,
|
|
5391
|
+
estimateHeight,
|
|
5392
|
+
overscan: OVERSCAN
|
|
5360
5393
|
});
|
|
5361
|
-
const
|
|
5362
|
-
|
|
5363
|
-
|
|
5364
|
-
|
|
5365
|
-
|
|
5366
|
-
|
|
5367
|
-
|
|
5368
|
-
|
|
5369
|
-
|
|
5370
|
-
|
|
5371
|
-
|
|
5372
|
-
setSavedTick(true);
|
|
5373
|
-
} catch (cause) {
|
|
5374
|
-
setSaveError(cause instanceof Error ? cause : new Error(String(cause)));
|
|
5375
|
-
} finally {
|
|
5376
|
-
setSaving(false);
|
|
5394
|
+
const jumpTo = useCallback18(
|
|
5395
|
+
(index) => {
|
|
5396
|
+
setActivePath(orderedFiles[index]?.path ?? null);
|
|
5397
|
+
windowed.scrollToIndex(index);
|
|
5398
|
+
},
|
|
5399
|
+
[orderedFiles, windowed]
|
|
5400
|
+
);
|
|
5401
|
+
const onPaneScroll = useCallback18(() => {
|
|
5402
|
+
const el = windowed.scrollRef.current;
|
|
5403
|
+
if (!el) {
|
|
5404
|
+
return;
|
|
5377
5405
|
}
|
|
5378
|
-
|
|
5379
|
-
|
|
5380
|
-
|
|
5381
|
-
|
|
5382
|
-
|
|
5383
|
-
|
|
5384
|
-
setSaveError(null);
|
|
5385
|
-
setSavedTick(false);
|
|
5386
|
-
try {
|
|
5387
|
-
await onOverwrite(snapshot);
|
|
5388
|
-
setBaseline(snapshot);
|
|
5389
|
-
lastSeed.current = { path, contents: snapshot };
|
|
5390
|
-
setSavedTick(true);
|
|
5391
|
-
} catch (cause) {
|
|
5392
|
-
setSaveError(cause instanceof Error ? cause : new Error(String(cause)));
|
|
5393
|
-
} finally {
|
|
5394
|
-
setSaving(false);
|
|
5406
|
+
const top = el.scrollTop;
|
|
5407
|
+
const offsets = windowed.offsets;
|
|
5408
|
+
let idx = 0;
|
|
5409
|
+
for (let i = 0; i < orderedFiles.length; i++) {
|
|
5410
|
+
if ((offsets[i] ?? 0) <= top + 4) idx = i;
|
|
5411
|
+
else break;
|
|
5395
5412
|
}
|
|
5396
|
-
|
|
5397
|
-
|
|
5398
|
-
|
|
5399
|
-
};
|
|
5413
|
+
const nextPath = orderedFiles[idx]?.path ?? null;
|
|
5414
|
+
setActivePath((previous) => previous === nextPath ? previous : nextPath);
|
|
5415
|
+
}, [windowed, orderedFiles]);
|
|
5400
5416
|
useEffect17(() => {
|
|
5401
|
-
|
|
5402
|
-
|
|
5403
|
-
|
|
5404
|
-
|
|
5405
|
-
|
|
5406
|
-
const
|
|
5407
|
-
|
|
5408
|
-
const exts = [bundle.saveKeymapExtension(() => saveRef.current())];
|
|
5409
|
-
if (bundle.languageExtension) exts.push(bundle.languageExtension);
|
|
5410
|
-
return exts;
|
|
5411
|
-
}, [bundle]);
|
|
5412
|
-
const Editor = bundle?.Editor;
|
|
5417
|
+
const el = windowed.scrollRef.current;
|
|
5418
|
+
if (!el) return;
|
|
5419
|
+
el.addEventListener("scroll", onPaneScroll, { passive: true });
|
|
5420
|
+
return () => el.removeEventListener("scroll", onPaneScroll);
|
|
5421
|
+
}, [windowed.scrollRef, onPaneScroll]);
|
|
5422
|
+
const sourceBadge = describeSource(source, capturedAt, captureRevision ?? null);
|
|
5423
|
+
const activeFile = orderedFiles[activeIndex] ?? orderedFiles[0];
|
|
5413
5424
|
return /* @__PURE__ */ jsxs6(
|
|
5414
5425
|
"div",
|
|
5415
5426
|
{
|
|
5416
|
-
|
|
5417
|
-
"
|
|
5418
|
-
|
|
5427
|
+
ref: rootRef,
|
|
5428
|
+
className: cn("flex h-full min-h-0 min-w-0 flex-col", className),
|
|
5429
|
+
"data-workbench-changes-layout": compact ? "compact" : "rail",
|
|
5419
5430
|
children: [
|
|
5420
|
-
/* @__PURE__ */ jsxs6(
|
|
5421
|
-
/* @__PURE__ */ jsx8(
|
|
5422
|
-
"span",
|
|
5423
|
-
{
|
|
5424
|
-
className: cn(
|
|
5425
|
-
"size-1.5 shrink-0 rounded-full transition-colors",
|
|
5426
|
-
readOnly ? "bg-transparent" : dirty ? "bg-og-status-running" : "bg-og-status-idle"
|
|
5427
|
-
),
|
|
5428
|
-
title: readOnly ? "read-only" : dirty ? "unsaved changes" : "saved"
|
|
5429
|
-
}
|
|
5430
|
-
),
|
|
5431
|
-
/* @__PURE__ */ jsxs6("span", { className: "truncate font-og-mono text-og-xs text-og-fg-muted", children: [
|
|
5432
|
-
fileName,
|
|
5433
|
-
dirty && !readOnly ? " \u2022" : ""
|
|
5434
|
-
] }),
|
|
5435
|
-
/* @__PURE__ */ jsxs6("div", { className: "ml-auto flex shrink-0 items-center gap-2", children: [
|
|
5436
|
-
saveError && !saveConflict && /* @__PURE__ */ jsx8(
|
|
5437
|
-
"span",
|
|
5438
|
-
{
|
|
5439
|
-
className: "max-w-[220px] truncate text-og-xs text-og-status-failed",
|
|
5440
|
-
title: saveError.message,
|
|
5441
|
-
children: saveError.message
|
|
5442
|
-
}
|
|
5443
|
-
),
|
|
5444
|
-
!saveError && savedTick && /* @__PURE__ */ jsx8("span", { className: "text-og-xs text-og-status-idle", children: "Saved" }),
|
|
5445
|
-
readOnly ? /* @__PURE__ */ jsx8("span", { className: "text-og-xs uppercase tracking-wide text-og-fg-subtle", children: "Read-only" }) : !saveConflict ? /* @__PURE__ */ jsxs6(
|
|
5446
|
-
"button",
|
|
5447
|
-
{
|
|
5448
|
-
type: "button",
|
|
5449
|
-
onClick: () => void save(),
|
|
5450
|
-
disabled: saving || !dirty,
|
|
5451
|
-
className: cn(
|
|
5452
|
-
"flex items-center gap-1 rounded-og-sm border border-og-border px-1.5 py-0.5 text-og-xs pointer-coarse:min-h-11",
|
|
5453
|
-
saving || !dirty ? "cursor-default text-og-fg-subtle opacity-60" : "text-og-fg hover:bg-og-accent-soft"
|
|
5454
|
-
),
|
|
5455
|
-
title: "Save (\u2318/Ctrl+S)",
|
|
5456
|
-
children: [
|
|
5457
|
-
saving ? /* @__PURE__ */ jsx8(Loader2Icon, { className: "size-3 animate-spin" }) : /* @__PURE__ */ jsx8(SaveIcon, { className: "size-3" }),
|
|
5458
|
-
"Save"
|
|
5459
|
-
]
|
|
5460
|
-
}
|
|
5461
|
-
) : null
|
|
5462
|
-
] })
|
|
5463
|
-
] }),
|
|
5464
|
-
saveConflict ? /* @__PURE__ */ jsxs6(
|
|
5431
|
+
/* @__PURE__ */ jsxs6(
|
|
5465
5432
|
"div",
|
|
5466
5433
|
{
|
|
5467
|
-
className:
|
|
5468
|
-
|
|
5434
|
+
className: cn(
|
|
5435
|
+
"flex shrink-0 items-center justify-between gap-2 border-b border-og-border px-3 py-1.5",
|
|
5436
|
+
compact && source === "capture" && "flex-col items-stretch gap-1.5 py-2"
|
|
5437
|
+
),
|
|
5469
5438
|
children: [
|
|
5470
|
-
/* @__PURE__ */ jsxs6(
|
|
5471
|
-
|
|
5472
|
-
|
|
5473
|
-
|
|
5474
|
-
|
|
5475
|
-
|
|
5476
|
-
|
|
5477
|
-
"
|
|
5478
|
-
|
|
5479
|
-
|
|
5480
|
-
|
|
5481
|
-
|
|
5482
|
-
|
|
5483
|
-
|
|
5484
|
-
|
|
5485
|
-
|
|
5486
|
-
|
|
5487
|
-
|
|
5488
|
-
|
|
5489
|
-
|
|
5490
|
-
|
|
5491
|
-
|
|
5492
|
-
|
|
5493
|
-
|
|
5494
|
-
|
|
5495
|
-
|
|
5496
|
-
|
|
5497
|
-
|
|
5498
|
-
|
|
5439
|
+
/* @__PURE__ */ jsxs6(
|
|
5440
|
+
"span",
|
|
5441
|
+
{
|
|
5442
|
+
role: "status",
|
|
5443
|
+
"aria-atomic": "true",
|
|
5444
|
+
"aria-live": "polite",
|
|
5445
|
+
"data-contrast-audited": true,
|
|
5446
|
+
className: "min-w-0 text-og-xs text-og-fg-muted",
|
|
5447
|
+
children: [
|
|
5448
|
+
diff.length,
|
|
5449
|
+
" ",
|
|
5450
|
+
diff.length === 1 ? "file" : "files",
|
|
5451
|
+
" changed",
|
|
5452
|
+
/* @__PURE__ */ jsxs6("span", { "data-contrast-audited": true, className: "ml-2 text-og-status-idle", children: [
|
|
5453
|
+
"+",
|
|
5454
|
+
additions
|
|
5455
|
+
] }),
|
|
5456
|
+
/* @__PURE__ */ jsxs6("span", { "data-contrast-audited": true, className: "ml-1 text-og-status-failed", children: [
|
|
5457
|
+
"\u2212",
|
|
5458
|
+
deletions
|
|
5459
|
+
] })
|
|
5460
|
+
]
|
|
5461
|
+
}
|
|
5462
|
+
),
|
|
5463
|
+
/* @__PURE__ */ jsxs6(
|
|
5464
|
+
"div",
|
|
5465
|
+
{
|
|
5466
|
+
className: cn(
|
|
5467
|
+
"flex shrink-0 items-center gap-2",
|
|
5468
|
+
compact && source === "capture" && "self-start"
|
|
5469
|
+
),
|
|
5470
|
+
children: [
|
|
5471
|
+
compact ? null : /* @__PURE__ */ jsx6(LayoutToggle, { layout, onChange: setLayout }),
|
|
5472
|
+
/* @__PURE__ */ jsx6(SourceBadge, { source, capturedAt, label: sourceBadge })
|
|
5473
|
+
]
|
|
5474
|
+
}
|
|
5475
|
+
)
|
|
5499
5476
|
]
|
|
5500
5477
|
}
|
|
5501
|
-
)
|
|
5502
|
-
/* @__PURE__ */
|
|
5503
|
-
|
|
5504
|
-
|
|
5505
|
-
|
|
5506
|
-
|
|
5507
|
-
|
|
5508
|
-
|
|
5509
|
-
|
|
5510
|
-
|
|
5511
|
-
|
|
5512
|
-
|
|
5513
|
-
|
|
5514
|
-
|
|
5515
|
-
|
|
5516
|
-
|
|
5517
|
-
|
|
5518
|
-
|
|
5519
|
-
|
|
5520
|
-
|
|
5521
|
-
|
|
5522
|
-
|
|
5523
|
-
|
|
5478
|
+
),
|
|
5479
|
+
compact ? /* @__PURE__ */ jsxs6("div", { className: "flex min-h-0 flex-1 flex-col", children: [
|
|
5480
|
+
/* @__PURE__ */ jsxs6("div", { className: "shrink-0 border-b border-og-border bg-og-surface-1 p-2", children: [
|
|
5481
|
+
/* @__PURE__ */ jsx6("label", { className: "sr-only", htmlFor: pickerId, children: "Changed file" }),
|
|
5482
|
+
/* @__PURE__ */ jsx6(
|
|
5483
|
+
"select",
|
|
5484
|
+
{
|
|
5485
|
+
id: pickerId,
|
|
5486
|
+
"aria-label": "Changed file",
|
|
5487
|
+
value: activeIndex,
|
|
5488
|
+
onChange: (event) => {
|
|
5489
|
+
const index = Number(event.currentTarget.value);
|
|
5490
|
+
setActivePath(orderedFiles[index]?.path ?? null);
|
|
5491
|
+
},
|
|
5492
|
+
title: activeFile?.path,
|
|
5493
|
+
"data-compact-file-picker": true,
|
|
5494
|
+
className: "h-11 w-full rounded-og-md border border-og-border bg-og-bg px-3 font-og-mono text-og-sm text-og-fg outline-none transition-colors focus:border-og-accent focus:ring-2 focus:ring-og-accent-soft",
|
|
5495
|
+
children: orderedFiles.map((file, index) => /* @__PURE__ */ jsx6("option", { value: index, children: compactFileLabel(file) }, file.path))
|
|
5496
|
+
}
|
|
5497
|
+
)
|
|
5498
|
+
] }),
|
|
5499
|
+
/* @__PURE__ */ jsx6("div", { className: "min-h-0 min-w-0 flex-1 overflow-auto", "data-opengeni-changes-pane": true, children: activeFile ? /* @__PURE__ */ jsx6(
|
|
5500
|
+
DiffSection,
|
|
5501
|
+
{
|
|
5502
|
+
file: activeFile,
|
|
5503
|
+
layout: "unified",
|
|
5504
|
+
themeType: resolvedTheme,
|
|
5505
|
+
...onOpenFile ? { onOpenFile } : {}
|
|
5506
|
+
}
|
|
5507
|
+
) : null })
|
|
5508
|
+
] }) : /* @__PURE__ */ jsxs6("div", { className: "flex min-h-0 flex-1", children: [
|
|
5509
|
+
/* @__PURE__ */ jsx6("div", { className: "w-[clamp(12rem,28%,15rem)] shrink-0 border-r border-og-border bg-og-surface-1/35", children: /* @__PURE__ */ jsx6(
|
|
5510
|
+
VList2,
|
|
5511
|
+
{
|
|
5512
|
+
className: "h-full",
|
|
5513
|
+
itemSize: coarsePointer ? 44 : 28,
|
|
5514
|
+
ssrCount: Math.min(30, rows.length),
|
|
5515
|
+
children: rows.map(
|
|
5516
|
+
(row) => row.kind === "group" ? /* @__PURE__ */ jsxs6(
|
|
5517
|
+
"div",
|
|
5518
|
+
{
|
|
5519
|
+
"data-rail-group": true,
|
|
5520
|
+
className: "flex items-center gap-1.5 px-2 pb-0.5 pt-2 text-og-xs font-medium uppercase tracking-wide text-og-fg-subtle",
|
|
5521
|
+
children: [
|
|
5522
|
+
/* @__PURE__ */ jsx6("span", { className: "min-w-0 truncate", children: row.label }),
|
|
5523
|
+
/* @__PURE__ */ jsx6("span", { className: "shrink-0", children: row.count })
|
|
5524
|
+
]
|
|
5525
|
+
},
|
|
5526
|
+
`g:${row.label}`
|
|
5527
|
+
) : /* @__PURE__ */ jsx6(
|
|
5528
|
+
RailFileRow,
|
|
5529
|
+
{
|
|
5530
|
+
file: row.file,
|
|
5531
|
+
grouped,
|
|
5532
|
+
active: row.index === activeIndex,
|
|
5533
|
+
onClick: () => jumpTo(row.index)
|
|
5534
|
+
},
|
|
5535
|
+
row.file.path
|
|
5536
|
+
)
|
|
5537
|
+
)
|
|
5538
|
+
}
|
|
5539
|
+
) }),
|
|
5540
|
+
/* @__PURE__ */ jsx6(
|
|
5541
|
+
"div",
|
|
5542
|
+
{
|
|
5543
|
+
ref: windowed.scrollRef,
|
|
5544
|
+
className: "min-h-0 min-w-0 flex-1 overflow-auto",
|
|
5545
|
+
"data-opengeni-changes-pane": true,
|
|
5546
|
+
children: /* @__PURE__ */ jsx6("div", { style: { position: "relative", height: windowed.totalHeight }, children: orderedFiles.map((file, index) => {
|
|
5547
|
+
if (index < windowed.range.start || index >= windowed.range.end) return null;
|
|
5548
|
+
return /* @__PURE__ */ jsx6(
|
|
5549
|
+
MeasuredSection,
|
|
5550
|
+
{
|
|
5551
|
+
index,
|
|
5552
|
+
top: windowed.offsets[index] ?? 0,
|
|
5553
|
+
onMeasure: windowed.measure,
|
|
5554
|
+
children: /* @__PURE__ */ jsx6(
|
|
5555
|
+
DiffSection,
|
|
5556
|
+
{
|
|
5557
|
+
file,
|
|
5558
|
+
layout,
|
|
5559
|
+
themeType: resolvedTheme,
|
|
5560
|
+
...onOpenFile ? { onOpenFile } : {}
|
|
5561
|
+
}
|
|
5562
|
+
)
|
|
5563
|
+
},
|
|
5564
|
+
file.path
|
|
5565
|
+
);
|
|
5566
|
+
}) })
|
|
5567
|
+
}
|
|
5568
|
+
)
|
|
5569
|
+
] })
|
|
5524
5570
|
]
|
|
5525
5571
|
}
|
|
5526
5572
|
);
|
|
5527
5573
|
}
|
|
5528
|
-
|
|
5529
|
-
|
|
5530
|
-
|
|
5531
|
-
|
|
5532
|
-
function PlainTextarea({
|
|
5533
|
-
value,
|
|
5534
|
-
readOnly,
|
|
5535
|
-
onChange,
|
|
5536
|
-
onSave
|
|
5574
|
+
function SourceBadge({
|
|
5575
|
+
source,
|
|
5576
|
+
capturedAt,
|
|
5577
|
+
label
|
|
5537
5578
|
}) {
|
|
5538
|
-
|
|
5539
|
-
|
|
5540
|
-
|
|
5541
|
-
|
|
5542
|
-
|
|
5543
|
-
|
|
5544
|
-
|
|
5545
|
-
|
|
5546
|
-
|
|
5547
|
-
|
|
5548
|
-
|
|
5549
|
-
|
|
5550
|
-
|
|
5551
|
-
|
|
5552
|
-
className: "h-full w-full resize-none bg-transparent p-2 font-og-mono text-og-sm text-og-fg outline-none"
|
|
5553
|
-
}
|
|
5554
|
-
);
|
|
5555
|
-
}
|
|
5556
|
-
function EditorSkeleton() {
|
|
5557
|
-
return /* @__PURE__ */ jsx8("div", { className: "p-3 text-og-sm text-og-fg-subtle", children: "Loading editor\u2026" });
|
|
5579
|
+
if (source === "capture" && capturedAt) {
|
|
5580
|
+
return /* @__PURE__ */ jsxs6(
|
|
5581
|
+
"span",
|
|
5582
|
+
{
|
|
5583
|
+
className: "inline-flex items-center gap-1 rounded-og-xs border border-og-border px-1.5 py-px text-og-xs text-og-fg-muted",
|
|
5584
|
+
title: new Date(capturedAt).toLocaleString(),
|
|
5585
|
+
children: [
|
|
5586
|
+
/* @__PURE__ */ jsx6(HistoryIcon, { className: "size-3 shrink-0 text-og-status-running", "aria-hidden": true }),
|
|
5587
|
+
label
|
|
5588
|
+
]
|
|
5589
|
+
}
|
|
5590
|
+
);
|
|
5591
|
+
}
|
|
5592
|
+
return /* @__PURE__ */ jsx6("span", { className: "rounded-og-xs bg-og-surface-2 px-1.5 py-px text-og-xs text-og-fg-subtle", children: label });
|
|
5558
5593
|
}
|
|
5559
|
-
|
|
5560
|
-
|
|
5561
|
-
|
|
5562
|
-
|
|
5563
|
-
|
|
5564
|
-
function
|
|
5565
|
-
|
|
5566
|
-
|
|
5567
|
-
|
|
5568
|
-
|
|
5569
|
-
editable = true,
|
|
5570
|
-
onEditIntent,
|
|
5571
|
-
requestedPath,
|
|
5572
|
-
requestedPathRequestId,
|
|
5573
|
-
requestedPathReady = true,
|
|
5574
|
-
workspaceResting = false,
|
|
5575
|
-
workspaceWaking = false,
|
|
5576
|
-
liveWorkspaceReady = true,
|
|
5577
|
-
onWakeWorkspace,
|
|
5578
|
-
themeType,
|
|
5579
|
-
className
|
|
5594
|
+
function describeSource(source, capturedAt, revision) {
|
|
5595
|
+
if (source !== "capture" || !capturedAt) return "Live diff";
|
|
5596
|
+
const time = formatAsOf(capturedAt, Date.now());
|
|
5597
|
+
return revision !== null ? `as of turn ${revision} \xB7 ${time}` : `as of ${time}`;
|
|
5598
|
+
}
|
|
5599
|
+
function RailFileRow({
|
|
5600
|
+
file,
|
|
5601
|
+
grouped,
|
|
5602
|
+
active,
|
|
5603
|
+
onClick
|
|
5580
5604
|
}) {
|
|
5581
|
-
const
|
|
5582
|
-
const
|
|
5583
|
-
|
|
5584
|
-
|
|
5585
|
-
|
|
5586
|
-
|
|
5587
|
-
|
|
5588
|
-
|
|
5589
|
-
|
|
5590
|
-
|
|
5591
|
-
|
|
5592
|
-
|
|
5593
|
-
|
|
5594
|
-
|
|
5595
|
-
|
|
5596
|
-
|
|
5597
|
-
|
|
5598
|
-
|
|
5599
|
-
|
|
5605
|
+
const repoPrefix = file.repoRoot ? `${file.repoRoot}/` : null;
|
|
5606
|
+
const shown = grouped ? file.repoRoot !== void 0 ? repoPrefix && file.path.startsWith(repoPrefix) ? file.path.slice(repoPrefix.length) : file.path : file.path.slice(file.path.indexOf("/") + 1) || file.path : file.path;
|
|
5607
|
+
return /* @__PURE__ */ jsxs6(
|
|
5608
|
+
"button",
|
|
5609
|
+
{
|
|
5610
|
+
type: "button",
|
|
5611
|
+
onClick,
|
|
5612
|
+
title: file.path,
|
|
5613
|
+
"data-rail-file": true,
|
|
5614
|
+
className: cn(
|
|
5615
|
+
"relative flex min-h-7 w-full items-center gap-1.5 truncate px-2 py-0.5 text-left text-og-sm transition-colors hover:bg-og-surface-2 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-og-accent pointer-coarse:min-h-11",
|
|
5616
|
+
grouped && "pl-3",
|
|
5617
|
+
active && "bg-og-accent-soft text-og-fg before:absolute before:inset-y-1 before:left-0 before:w-0.5 before:rounded-r-full before:bg-og-accent"
|
|
5618
|
+
),
|
|
5619
|
+
children: [
|
|
5620
|
+
/* @__PURE__ */ jsx6(
|
|
5621
|
+
"span",
|
|
5622
|
+
{
|
|
5623
|
+
"data-contrast-audited": true,
|
|
5624
|
+
className: cn("w-3 shrink-0 text-center font-og-mono text-og-xs", STATUS_TINT2[file.status]),
|
|
5625
|
+
children: STATUS_LETTER[file.status]
|
|
5626
|
+
}
|
|
5627
|
+
),
|
|
5628
|
+
/* @__PURE__ */ jsx6("span", { className: "min-w-0 flex-1 truncate", children: shown }),
|
|
5629
|
+
/* @__PURE__ */ jsxs6("span", { className: "ml-auto flex shrink-0 items-center gap-1 pl-1 font-og-mono text-og-xs", children: [
|
|
5630
|
+
/* @__PURE__ */ jsxs6("span", { "data-contrast-audited": true, className: "text-og-status-idle", children: [
|
|
5631
|
+
"+",
|
|
5632
|
+
file.additions
|
|
5633
|
+
] }),
|
|
5634
|
+
/* @__PURE__ */ jsxs6("span", { "data-contrast-audited": true, className: "text-og-status-failed", children: [
|
|
5635
|
+
"\u2212",
|
|
5636
|
+
file.deletions
|
|
5637
|
+
] })
|
|
5638
|
+
] })
|
|
5639
|
+
]
|
|
5600
5640
|
}
|
|
5601
|
-
|
|
5602
|
-
|
|
5603
|
-
|
|
5604
|
-
|
|
5605
|
-
|
|
5606
|
-
|
|
5607
|
-
|
|
5608
|
-
|
|
5609
|
-
|
|
5610
|
-
|
|
5611
|
-
const
|
|
5612
|
-
|
|
5613
|
-
|
|
5614
|
-
|
|
5615
|
-
|
|
5616
|
-
|
|
5617
|
-
|
|
5618
|
-
|
|
5619
|
-
|
|
5620
|
-
|
|
5621
|
-
|
|
5622
|
-
|
|
5623
|
-
|
|
5624
|
-
|
|
5641
|
+
);
|
|
5642
|
+
}
|
|
5643
|
+
function MeasuredSection({
|
|
5644
|
+
index,
|
|
5645
|
+
top,
|
|
5646
|
+
onMeasure,
|
|
5647
|
+
children
|
|
5648
|
+
}) {
|
|
5649
|
+
const ref = useRef15(null);
|
|
5650
|
+
useEffect17(() => {
|
|
5651
|
+
const el = ref.current;
|
|
5652
|
+
if (!el) return;
|
|
5653
|
+
const report = () => onMeasure(index, el.offsetHeight);
|
|
5654
|
+
report();
|
|
5655
|
+
const ro = typeof ResizeObserver !== "undefined" ? new ResizeObserver(report) : null;
|
|
5656
|
+
ro?.observe(el);
|
|
5657
|
+
return () => ro?.disconnect();
|
|
5658
|
+
}, [index, onMeasure]);
|
|
5659
|
+
return /* @__PURE__ */ jsx6(
|
|
5660
|
+
"div",
|
|
5661
|
+
{
|
|
5662
|
+
ref,
|
|
5663
|
+
"data-diff-section": true,
|
|
5664
|
+
"data-diff-index": index,
|
|
5665
|
+
style: { position: "absolute", top, left: 0, right: 0 },
|
|
5666
|
+
children
|
|
5625
5667
|
}
|
|
5626
|
-
|
|
5627
|
-
|
|
5628
|
-
|
|
5629
|
-
|
|
5630
|
-
|
|
5631
|
-
|
|
5632
|
-
|
|
5633
|
-
|
|
5634
|
-
|
|
5635
|
-
|
|
5636
|
-
return /* @__PURE__ */
|
|
5637
|
-
|
|
5638
|
-
|
|
5639
|
-
className,
|
|
5640
|
-
|
|
5641
|
-
|
|
5642
|
-
|
|
5643
|
-
|
|
5644
|
-
|
|
5645
|
-
|
|
5668
|
+
);
|
|
5669
|
+
}
|
|
5670
|
+
function DiffSection({
|
|
5671
|
+
file,
|
|
5672
|
+
layout,
|
|
5673
|
+
themeType,
|
|
5674
|
+
onOpenFile
|
|
5675
|
+
}) {
|
|
5676
|
+
if (isGuarded(file)) {
|
|
5677
|
+
const renamed = file.oldPath && file.oldPath !== file.path;
|
|
5678
|
+
return /* @__PURE__ */ jsxs6("section", { className: "border-b border-og-border pb-1", children: [
|
|
5679
|
+
/* @__PURE__ */ jsxs6("header", { className: "flex items-center justify-between gap-2 bg-og-surface-1 px-3 py-1.5 text-og-sm", children: [
|
|
5680
|
+
/* @__PURE__ */ jsx6("span", { className: "min-w-0 truncate font-og-mono text-og-xs", children: renamed ? `${file.oldPath} \u2192 ${file.path}` : file.path }),
|
|
5681
|
+
/* @__PURE__ */ jsxs6("span", { className: "flex shrink-0 items-center gap-2 font-og-mono text-og-xs", children: [
|
|
5682
|
+
/* @__PURE__ */ jsxs6("span", { className: "text-og-status-idle", children: [
|
|
5683
|
+
"+",
|
|
5684
|
+
file.additions
|
|
5685
|
+
] }),
|
|
5686
|
+
/* @__PURE__ */ jsxs6("span", { className: "text-og-status-failed", children: [
|
|
5687
|
+
"\u2212",
|
|
5688
|
+
file.deletions
|
|
5689
|
+
] })
|
|
5690
|
+
] })
|
|
5691
|
+
] }),
|
|
5692
|
+
/* @__PURE__ */ jsx6(GuardBody, { file, ...onOpenFile ? { onOpenFile } : {} })
|
|
5693
|
+
] });
|
|
5646
5694
|
}
|
|
5647
|
-
|
|
5648
|
-
|
|
5649
|
-
|
|
5695
|
+
return /* @__PURE__ */ jsx6("section", { "data-pierre-section": true, className: "border-b border-og-border pb-2", children: /* @__PURE__ */ jsx6(PierreDiff, { diff: [file], layout, themeType, className: "px-1" }) });
|
|
5696
|
+
}
|
|
5697
|
+
function GuardBody({
|
|
5698
|
+
file,
|
|
5699
|
+
onOpenFile
|
|
5700
|
+
}) {
|
|
5701
|
+
const reason = file.isBinary ? "Binary file" : "Diff too large to show here";
|
|
5702
|
+
return /* @__PURE__ */ jsxs6("div", { className: "flex flex-wrap items-center gap-2 px-3 py-3 text-og-sm text-og-fg-subtle", children: [
|
|
5703
|
+
/* @__PURE__ */ jsx6(FileWarningIcon, { className: "size-3.5 shrink-0", "aria-hidden": true }),
|
|
5704
|
+
/* @__PURE__ */ jsxs6("span", { className: "min-w-0 flex-1", children: [
|
|
5705
|
+
reason,
|
|
5706
|
+
"."
|
|
5707
|
+
] }),
|
|
5708
|
+
onOpenFile ? /* @__PURE__ */ jsxs6(
|
|
5709
|
+
"button",
|
|
5650
5710
|
{
|
|
5651
|
-
|
|
5652
|
-
|
|
5653
|
-
|
|
5654
|
-
className: "size-5 animate-spin motion-reduce:animate-none",
|
|
5655
|
-
"aria-hidden": true
|
|
5656
|
-
}
|
|
5657
|
-
) : /* @__PURE__ */ jsx9(FileCode2Icon, { className: "size-5", "aria-hidden": true }),
|
|
5658
|
-
title: workspaceWaking ? "Waking workspace" : "Workspace is resting",
|
|
5659
|
-
announce: "status",
|
|
5711
|
+
type: "button",
|
|
5712
|
+
onClick: () => onOpenFile(file.path),
|
|
5713
|
+
className: "inline-flex min-h-8 shrink-0 items-center gap-1.5 rounded-og-sm border border-og-border px-2 py-1 text-og-xs font-medium text-og-fg-muted transition-colors hover:border-og-border-strong hover:bg-og-surface-2 hover:text-og-fg focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-og-accent pointer-coarse:min-h-11",
|
|
5660
5714
|
children: [
|
|
5661
|
-
|
|
5662
|
-
|
|
5715
|
+
"Open in Files",
|
|
5716
|
+
/* @__PURE__ */ jsx6(ArrowUpRightIcon, { className: "size-3.5", "aria-hidden": true })
|
|
5663
5717
|
]
|
|
5664
5718
|
}
|
|
5665
|
-
)
|
|
5666
|
-
}
|
|
5667
|
-
|
|
5668
|
-
|
|
5669
|
-
|
|
5670
|
-
|
|
5671
|
-
|
|
5672
|
-
|
|
5673
|
-
|
|
5674
|
-
|
|
5675
|
-
|
|
5676
|
-
|
|
5677
|
-
|
|
5678
|
-
|
|
5679
|
-
|
|
5680
|
-
result: files,
|
|
5681
|
-
selectedPath: selected ?? void 0,
|
|
5682
|
-
onSelectFile: selectFile,
|
|
5683
|
-
editable,
|
|
5684
|
-
emptyState: "This directory is empty",
|
|
5685
|
-
className: "min-w-0 flex-1"
|
|
5686
|
-
}
|
|
5687
|
-
)
|
|
5688
|
-
}
|
|
5719
|
+
) : null
|
|
5720
|
+
] });
|
|
5721
|
+
}
|
|
5722
|
+
function LayoutToggle({
|
|
5723
|
+
layout,
|
|
5724
|
+
onChange
|
|
5725
|
+
}) {
|
|
5726
|
+
return /* @__PURE__ */ jsx6("div", { className: "inline-flex items-center rounded-og-sm border border-og-border p-0.5", children: ["unified", "split"].map((value) => /* @__PURE__ */ jsx6(
|
|
5727
|
+
"button",
|
|
5728
|
+
{
|
|
5729
|
+
type: "button",
|
|
5730
|
+
onClick: () => onChange(value),
|
|
5731
|
+
className: cn(
|
|
5732
|
+
"min-h-7 rounded-og-xs px-1.5 py-0.5 text-og-xs capitalize focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-og-accent max-[1023px]:min-w-11 pointer-coarse:min-h-11 pointer-coarse:min-w-11",
|
|
5733
|
+
layout === value ? "bg-og-accent-soft text-og-fg" : "text-og-fg-subtle hover:text-og-fg"
|
|
5689
5734
|
),
|
|
5690
|
-
|
|
5691
|
-
|
|
5692
|
-
|
|
5693
|
-
|
|
5694
|
-
"flex min-h-0 min-w-0 flex-col",
|
|
5695
|
-
wide ? "flex-1" : "flex-[1.4] border-t border-og-border"
|
|
5696
|
-
),
|
|
5697
|
-
children: [
|
|
5698
|
-
/* @__PURE__ */ jsxs7("div", { className: "flex shrink-0 items-center justify-between gap-2 border-b border-og-border bg-og-surface-1 px-2 py-1", children: [
|
|
5699
|
-
/* @__PURE__ */ jsx9(
|
|
5700
|
-
"span",
|
|
5701
|
-
{
|
|
5702
|
-
"data-opengeni-selected-file": true,
|
|
5703
|
-
className: "min-w-0 truncate font-og-mono text-og-xs text-og-fg-muted",
|
|
5704
|
-
children: selected ?? "No file selected"
|
|
5705
|
-
}
|
|
5706
|
-
),
|
|
5707
|
-
canEdit && /* @__PURE__ */ jsx9(
|
|
5708
|
-
Segmented,
|
|
5709
|
-
{
|
|
5710
|
-
options: [
|
|
5711
|
-
{ value: "view", label: "View" },
|
|
5712
|
-
{ value: "edit", label: "Edit" }
|
|
5713
|
-
],
|
|
5714
|
-
value: editMode ? "edit" : "view",
|
|
5715
|
-
onChange: (v) => setEditMode(v === "edit")
|
|
5716
|
-
}
|
|
5717
|
-
)
|
|
5718
|
-
] }),
|
|
5719
|
-
/* @__PURE__ */ jsx9("div", { className: "min-h-0 flex-1 overflow-auto", children: viewPath ? showEditor && fileView.content !== null ? /* @__PURE__ */ jsx9(
|
|
5720
|
-
CodeEditor,
|
|
5721
|
-
{
|
|
5722
|
-
path: viewPath,
|
|
5723
|
-
initialContents: fileView.content,
|
|
5724
|
-
themeType: resolvedTheme,
|
|
5725
|
-
onSave: (contents) => files.writeFile(viewPath, contents, { expectedContent: fileView.content }),
|
|
5726
|
-
onOverwrite: (contents) => files.writeFile(viewPath, contents, { force: true }),
|
|
5727
|
-
onReload: () => setViewReloadRevision((revision) => revision + 1),
|
|
5728
|
-
...onEditIntent ? { onEditIntent } : {},
|
|
5729
|
-
className: "h-full"
|
|
5730
|
-
},
|
|
5731
|
-
viewPath
|
|
5732
|
-
) : waitingForSelectedFile ? /* @__PURE__ */ jsxs7(
|
|
5733
|
-
Notice,
|
|
5734
|
-
{
|
|
5735
|
-
icon: /* @__PURE__ */ jsx9(
|
|
5736
|
-
LoaderCircleIcon,
|
|
5737
|
-
{
|
|
5738
|
-
className: "size-5 animate-spin motion-reduce:animate-none",
|
|
5739
|
-
"aria-hidden": true
|
|
5740
|
-
}
|
|
5741
|
-
),
|
|
5742
|
-
title: "Waking workspace",
|
|
5743
|
-
announce: "status",
|
|
5744
|
-
children: [
|
|
5745
|
-
"Opening ",
|
|
5746
|
-
viewPath,
|
|
5747
|
-
" when the live file system is ready\u2026"
|
|
5748
|
-
]
|
|
5749
|
-
}
|
|
5750
|
-
) : captureFileUnavailable ? /* @__PURE__ */ jsxs7(Notice, { icon: /* @__PURE__ */ jsx9(FileCode2Icon, { className: "size-5", "aria-hidden": true }), title: "On machine", children: [
|
|
5751
|
-
/* @__PURE__ */ jsx9("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." }),
|
|
5752
|
-
onWakeWorkspace ? /* @__PURE__ */ jsx9(
|
|
5753
|
-
WakeButton,
|
|
5754
|
-
{
|
|
5755
|
-
onClick: () => {
|
|
5756
|
-
setLiveRequestedPath(viewPath);
|
|
5757
|
-
if (liveWorkspaceReady) void files.refresh();
|
|
5758
|
-
else onWakeWorkspace();
|
|
5759
|
-
},
|
|
5760
|
-
children: liveWorkspaceReady ? "Retry live file" : "Open live file"
|
|
5761
|
-
}
|
|
5762
|
-
) : null
|
|
5763
|
-
] }) : fileView.error ? /* @__PURE__ */ jsxs7(Notice, { announce: "alert", children: [
|
|
5764
|
-
"Could not open ",
|
|
5765
|
-
viewPath,
|
|
5766
|
-
": ",
|
|
5767
|
-
fileView.error.message
|
|
5768
|
-
] }) : fileView.loading ? /* @__PURE__ */ jsxs7(Notice, { announce: "status", children: [
|
|
5769
|
-
"Loading ",
|
|
5770
|
-
viewPath,
|
|
5771
|
-
"\u2026"
|
|
5772
|
-
] }) : fileView.isBinary ? /* @__PURE__ */ jsxs7(Notice, { children: [
|
|
5773
|
-
viewPath,
|
|
5774
|
-
" is a binary file (",
|
|
5775
|
-
fileView.sizeBytes ?? 0,
|
|
5776
|
-
" bytes)."
|
|
5777
|
-
] }) : fileView.content !== null ? /* @__PURE__ */ jsxs7(Fragment3, { children: [
|
|
5778
|
-
fileView.truncated && /* @__PURE__ */ jsxs7("div", { className: "border-b border-og-border bg-og-surface-1 px-2 py-1 text-og-xs text-og-status-running", children: [
|
|
5779
|
-
"Large file \u2014 showing a truncated preview (",
|
|
5780
|
-
fileView.sizeBytes ?? 0,
|
|
5781
|
-
" bytes loaded). Editing is disabled to avoid corrupting the file."
|
|
5782
|
-
] }),
|
|
5783
|
-
usePierre ? /* @__PURE__ */ jsx9(
|
|
5784
|
-
PierreFile,
|
|
5785
|
-
{
|
|
5786
|
-
path: viewPath,
|
|
5787
|
-
contents: fileView.content,
|
|
5788
|
-
themeType: resolvedTheme,
|
|
5789
|
-
fallback: /* @__PURE__ */ jsx9("pre", { className: "overflow-auto whitespace-pre p-2 font-og-mono text-og-sm text-og-fg", children: fileView.content }),
|
|
5790
|
-
className: "p-1"
|
|
5791
|
-
}
|
|
5792
|
-
) : /* @__PURE__ */ jsx9("pre", { className: "overflow-auto whitespace-pre p-2 font-og-mono text-og-sm text-og-fg", children: fileView.content })
|
|
5793
|
-
] }) : /* @__PURE__ */ jsxs7(Notice, { announce: "status", children: [
|
|
5794
|
-
"Loading ",
|
|
5795
|
-
viewPath,
|
|
5796
|
-
"\u2026"
|
|
5797
|
-
] }) : (
|
|
5798
|
-
// Nothing selected — the tree shows the whole workspace; pick a file.
|
|
5799
|
-
requestedPath && !requestedPathReady ? /* @__PURE__ */ jsxs7(
|
|
5800
|
-
Notice,
|
|
5801
|
-
{
|
|
5802
|
-
icon: /* @__PURE__ */ jsx9(
|
|
5803
|
-
LoaderCircleIcon,
|
|
5804
|
-
{
|
|
5805
|
-
className: "size-5 animate-spin motion-reduce:animate-none",
|
|
5806
|
-
"aria-hidden": true
|
|
5807
|
-
}
|
|
5808
|
-
),
|
|
5809
|
-
title: "Waking sandbox",
|
|
5810
|
-
announce: "status",
|
|
5811
|
-
children: [
|
|
5812
|
-
"Opening ",
|
|
5813
|
-
requestedPath,
|
|
5814
|
-
" when the live workspace is ready\u2026"
|
|
5815
|
-
]
|
|
5816
|
-
}
|
|
5817
|
-
) : /* @__PURE__ */ jsx9(Notice, { icon: /* @__PURE__ */ jsx9(FileCode2Icon, { className: "size-5", "aria-hidden": true }), title: "Choose a file", children: "Select a file in the tree to preview it." })
|
|
5818
|
-
) })
|
|
5819
|
-
]
|
|
5820
|
-
}
|
|
5821
|
-
)
|
|
5822
|
-
] })
|
|
5823
|
-
] });
|
|
5735
|
+
children: value
|
|
5736
|
+
},
|
|
5737
|
+
value
|
|
5738
|
+
)) });
|
|
5824
5739
|
}
|
|
5825
|
-
|
|
5826
|
-
|
|
5827
|
-
|
|
5740
|
+
|
|
5741
|
+
// src/components/diff-view.tsx
|
|
5742
|
+
import { jsx as jsx7 } from "react/jsx-runtime";
|
|
5743
|
+
function DiffView({
|
|
5744
|
+
diff,
|
|
5745
|
+
fallback,
|
|
5746
|
+
layout = "unified",
|
|
5747
|
+
themeType,
|
|
5748
|
+
isRepo = true,
|
|
5749
|
+
emptyState,
|
|
5750
|
+
className
|
|
5751
|
+
}) {
|
|
5752
|
+
if (diff.length === 0) {
|
|
5753
|
+
return /* @__PURE__ */ jsx7("div", { className: cn("p-3 text-og-sm text-og-fg-subtle", className), children: emptyState ?? (isRepo ? "No changes" : "No repository mounted") });
|
|
5754
|
+
}
|
|
5755
|
+
return /* @__PURE__ */ jsx7(
|
|
5756
|
+
PierreDiff,
|
|
5828
5757
|
{
|
|
5829
|
-
|
|
5830
|
-
|
|
5831
|
-
|
|
5832
|
-
|
|
5758
|
+
diff,
|
|
5759
|
+
layout,
|
|
5760
|
+
themeType,
|
|
5761
|
+
...fallback !== void 0 ? { fallback } : {},
|
|
5762
|
+
className
|
|
5833
5763
|
}
|
|
5834
5764
|
);
|
|
5835
5765
|
}
|
|
5836
|
-
|
|
5837
|
-
|
|
5838
|
-
|
|
5839
|
-
|
|
5840
|
-
|
|
5841
|
-
|
|
5842
|
-
|
|
5843
|
-
|
|
5844
|
-
|
|
5845
|
-
|
|
5846
|
-
|
|
5847
|
-
|
|
5848
|
-
|
|
5849
|
-
|
|
5850
|
-
|
|
5851
|
-
|
|
5852
|
-
|
|
5853
|
-
|
|
5854
|
-
|
|
5855
|
-
|
|
5856
|
-
|
|
5857
|
-
|
|
5858
|
-
git.behind
|
|
5859
|
-
] })
|
|
5860
|
-
] }),
|
|
5861
|
-
dirty && /* @__PURE__ */ jsxs7(
|
|
5862
|
-
"span",
|
|
5863
|
-
{
|
|
5864
|
-
"aria-hidden": "true",
|
|
5865
|
-
"data-contrast-audited": true,
|
|
5866
|
-
className: "ml-auto shrink-0 text-og-xs text-og-fg-subtle",
|
|
5867
|
-
children: [
|
|
5868
|
-
dirtyCount,
|
|
5869
|
-
" changed"
|
|
5870
|
-
]
|
|
5871
|
-
}
|
|
5872
|
-
)
|
|
5873
|
-
] });
|
|
5874
|
-
}
|
|
5875
|
-
function Segmented({
|
|
5876
|
-
options,
|
|
5877
|
-
value,
|
|
5878
|
-
onChange
|
|
5766
|
+
|
|
5767
|
+
// src/components/pierre-file.tsx
|
|
5768
|
+
import {
|
|
5769
|
+
lazy,
|
|
5770
|
+
Suspense,
|
|
5771
|
+
useEffect as useEffect18,
|
|
5772
|
+
useState as useState15
|
|
5773
|
+
} from "react";
|
|
5774
|
+
import { jsx as jsx8 } from "react/jsx-runtime";
|
|
5775
|
+
var LazyFile = lazy(async () => {
|
|
5776
|
+
const mod = await import("@pierre/diffs/react");
|
|
5777
|
+
return { default: mod.File };
|
|
5778
|
+
});
|
|
5779
|
+
function PierreFile({
|
|
5780
|
+
path,
|
|
5781
|
+
contents,
|
|
5782
|
+
themeType,
|
|
5783
|
+
theme,
|
|
5784
|
+
disableWorkerPool,
|
|
5785
|
+
loading,
|
|
5786
|
+
fallback,
|
|
5787
|
+
className
|
|
5879
5788
|
}) {
|
|
5880
|
-
|
|
5881
|
-
"button",
|
|
5882
|
-
{
|
|
5883
|
-
type: "button",
|
|
5884
|
-
onClick: () => onChange(opt.value),
|
|
5885
|
-
className: cn(
|
|
5886
|
-
"min-h-7 rounded-og-xs px-1.5 py-0.5 text-og-xs focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-og-accent max-[1023px]:min-h-11 max-[1023px]:min-w-11 pointer-coarse:min-h-11 pointer-coarse:min-w-11",
|
|
5887
|
-
opt.value === value ? "bg-og-accent-soft text-og-fg" : "text-og-fg-subtle hover:text-og-fg"
|
|
5888
|
-
),
|
|
5889
|
-
children: opt.label
|
|
5890
|
-
},
|
|
5891
|
-
opt.value
|
|
5892
|
-
)) });
|
|
5893
|
-
}
|
|
5894
|
-
function useFileView(path, readFile, reloadRevision = 0) {
|
|
5895
|
-
const [state, setState] = useState15({
|
|
5896
|
-
content: null,
|
|
5897
|
-
isBinary: false,
|
|
5898
|
-
truncated: false,
|
|
5899
|
-
sizeBytes: null,
|
|
5900
|
-
loading: false,
|
|
5901
|
-
error: null
|
|
5902
|
-
});
|
|
5789
|
+
const [failed, setFailed] = useState15(false);
|
|
5903
5790
|
useEffect18(() => {
|
|
5904
|
-
if (!path) {
|
|
5905
|
-
setState({
|
|
5906
|
-
content: null,
|
|
5907
|
-
isBinary: false,
|
|
5908
|
-
truncated: false,
|
|
5909
|
-
sizeBytes: null,
|
|
5910
|
-
loading: false,
|
|
5911
|
-
error: null
|
|
5912
|
-
});
|
|
5913
|
-
return;
|
|
5914
|
-
}
|
|
5915
5791
|
let cancelled = false;
|
|
5916
|
-
|
|
5917
|
-
|
|
5918
|
-
content: null,
|
|
5919
|
-
isBinary: false,
|
|
5920
|
-
truncated: false,
|
|
5921
|
-
sizeBytes: null,
|
|
5922
|
-
loading: true,
|
|
5923
|
-
error: null
|
|
5924
|
-
});
|
|
5925
|
-
void readFile(path, { signal: abort.signal }).then((res) => {
|
|
5926
|
-
if (cancelled) return;
|
|
5927
|
-
const content = res.isBinary ? null : res.encoding === "base64" ? decodeBase64Utf8(res.content) : res.content;
|
|
5928
|
-
setState({
|
|
5929
|
-
content,
|
|
5930
|
-
isBinary: res.isBinary,
|
|
5931
|
-
truncated: res.truncated,
|
|
5932
|
-
sizeBytes: res.sizeBytes,
|
|
5933
|
-
loading: false,
|
|
5934
|
-
error: null
|
|
5935
|
-
});
|
|
5936
|
-
}).catch((cause) => {
|
|
5937
|
-
if (cancelled) return;
|
|
5938
|
-
setState({
|
|
5939
|
-
content: null,
|
|
5940
|
-
isBinary: false,
|
|
5941
|
-
truncated: false,
|
|
5942
|
-
sizeBytes: null,
|
|
5943
|
-
loading: false,
|
|
5944
|
-
error: cause instanceof Error ? cause : new Error(String(cause))
|
|
5945
|
-
});
|
|
5792
|
+
void import("@pierre/diffs/react").catch(() => {
|
|
5793
|
+
if (!cancelled) setFailed(true);
|
|
5946
5794
|
});
|
|
5947
5795
|
return () => {
|
|
5948
5796
|
cancelled = true;
|
|
5949
|
-
abort.abort();
|
|
5950
5797
|
};
|
|
5951
|
-
}, [
|
|
5952
|
-
|
|
5953
|
-
|
|
5954
|
-
|
|
5955
|
-
try {
|
|
5956
|
-
if (typeof atob === "function") {
|
|
5957
|
-
const binary = atob(b64);
|
|
5958
|
-
const bytes = new Uint8Array(binary.length);
|
|
5959
|
-
for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
|
|
5960
|
-
return new TextDecoder().decode(bytes);
|
|
5961
|
-
}
|
|
5962
|
-
} catch {
|
|
5798
|
+
}, []);
|
|
5799
|
+
const name = path.split("/").filter(Boolean).pop() ?? path;
|
|
5800
|
+
if (failed) {
|
|
5801
|
+
return /* @__PURE__ */ jsx8("div", { className, children: fallback ?? /* @__PURE__ */ jsx8(PlainFile, { name, contents }) });
|
|
5963
5802
|
}
|
|
5964
|
-
|
|
5965
|
-
|
|
5966
|
-
|
|
5967
|
-
|
|
5968
|
-
|
|
5969
|
-
|
|
5970
|
-
|
|
5971
|
-
|
|
5972
|
-
}
|
|
5973
|
-
|
|
5974
|
-
"
|
|
5975
|
-
|
|
5976
|
-
|
|
5977
|
-
|
|
5978
|
-
|
|
5979
|
-
|
|
5980
|
-
|
|
5981
|
-
|
|
5982
|
-
|
|
5983
|
-
|
|
5984
|
-
|
|
5985
|
-
|
|
5986
|
-
|
|
5987
|
-
|
|
5988
|
-
|
|
5989
|
-
|
|
5990
|
-
}
|
|
5991
|
-
|
|
5992
|
-
|
|
5993
|
-
|
|
5994
|
-
|
|
5995
|
-
|
|
5996
|
-
|
|
5997
|
-
|
|
5998
|
-
|
|
5999
|
-
|
|
6000
|
-
|
|
6001
|
-
|
|
6002
|
-
|
|
6003
|
-
|
|
6004
|
-
|
|
5803
|
+
const options = {
|
|
5804
|
+
overflow: "scroll",
|
|
5805
|
+
stickyHeader: true,
|
|
5806
|
+
showLineNumbers: true,
|
|
5807
|
+
...theme ? { theme } : {
|
|
5808
|
+
theme: {
|
|
5809
|
+
dark: "github-dark-high-contrast",
|
|
5810
|
+
light: "github-light-high-contrast"
|
|
5811
|
+
}
|
|
5812
|
+
},
|
|
5813
|
+
themeType: themeType ?? "dark"
|
|
5814
|
+
};
|
|
5815
|
+
const pierreVars = {
|
|
5816
|
+
"--diffs-dark-bg": "var(--og-color-bg)",
|
|
5817
|
+
"--diffs-light-bg": "var(--og-color-bg)",
|
|
5818
|
+
"--diffs-bg-buffer-override": "var(--og-color-surface-1)",
|
|
5819
|
+
"--diffs-bg-separator-override": "var(--og-color-surface-1)",
|
|
5820
|
+
"--diffs-font-size": "var(--og-code-font-size)",
|
|
5821
|
+
"--diffs-line-height": "var(--og-code-line-height)"
|
|
5822
|
+
};
|
|
5823
|
+
return /* @__PURE__ */ jsx8("div", { className: cn("min-w-0", className), "data-opengeni-pierre-file": true, style: pierreVars, children: /* @__PURE__ */ jsx8(Suspense, { fallback: loading ?? /* @__PURE__ */ jsx8(FileSkeleton, {}), children: /* @__PURE__ */ jsx8(
|
|
5824
|
+
LazyFile,
|
|
5825
|
+
{
|
|
5826
|
+
file: { name, contents },
|
|
5827
|
+
options,
|
|
5828
|
+
...disableWorkerPool !== void 0 ? { disableWorkerPool } : {}
|
|
5829
|
+
}
|
|
5830
|
+
) }) });
|
|
5831
|
+
}
|
|
5832
|
+
function PlainFile({ name, contents }) {
|
|
5833
|
+
return /* @__PURE__ */ jsx8(
|
|
5834
|
+
"pre",
|
|
5835
|
+
{
|
|
5836
|
+
className: "overflow-auto whitespace-pre p-2 font-og-mono text-og-sm text-og-fg",
|
|
5837
|
+
"data-file": name,
|
|
5838
|
+
children: contents
|
|
5839
|
+
}
|
|
5840
|
+
);
|
|
5841
|
+
}
|
|
5842
|
+
function FileSkeleton() {
|
|
5843
|
+
return /* @__PURE__ */ jsx8("div", { className: "p-3 text-og-sm text-og-fg-subtle", children: "Loading file\u2026" });
|
|
5844
|
+
}
|
|
5845
|
+
|
|
5846
|
+
// src/components/code-editor.tsx
|
|
5847
|
+
import { Loader2Icon, SaveIcon } from "lucide-react";
|
|
5848
|
+
import {
|
|
5849
|
+
useCallback as useCallback19,
|
|
5850
|
+
useEffect as useEffect19,
|
|
5851
|
+
useMemo as useMemo10,
|
|
5852
|
+
useRef as useRef16,
|
|
5853
|
+
useState as useState16
|
|
5854
|
+
} from "react";
|
|
5855
|
+
import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
5856
|
+
var LANGUAGE_LOADERS = {
|
|
5857
|
+
javascript: async () => (await import("@codemirror/lang-javascript")).javascript({ jsx: true, typescript: true }),
|
|
5858
|
+
json: async () => (await import("@codemirror/lang-json")).json(),
|
|
5859
|
+
python: async () => (await import("@codemirror/lang-python")).python(),
|
|
5860
|
+
markdown: async () => (await import("@codemirror/lang-markdown")).markdown(),
|
|
5861
|
+
css: async () => (await import("@codemirror/lang-css")).css(),
|
|
5862
|
+
html: async () => (await import("@codemirror/lang-html")).html()
|
|
5863
|
+
};
|
|
5864
|
+
function languageForPath(path) {
|
|
5865
|
+
const name = path.split("/").pop() ?? path;
|
|
5866
|
+
const ext = name.includes(".") ? (name.split(".").pop() ?? "").toLowerCase() : "";
|
|
5867
|
+
switch (ext) {
|
|
5868
|
+
case "js":
|
|
5869
|
+
case "jsx":
|
|
5870
|
+
case "ts":
|
|
5871
|
+
case "tsx":
|
|
5872
|
+
case "mjs":
|
|
5873
|
+
case "cjs":
|
|
5874
|
+
return "javascript";
|
|
5875
|
+
case "json":
|
|
5876
|
+
case "jsonc":
|
|
5877
|
+
return "json";
|
|
5878
|
+
case "py":
|
|
5879
|
+
case "pyi":
|
|
5880
|
+
return "python";
|
|
5881
|
+
case "md":
|
|
5882
|
+
case "markdown":
|
|
5883
|
+
case "mdx":
|
|
5884
|
+
return "markdown";
|
|
5885
|
+
case "css":
|
|
5886
|
+
case "scss":
|
|
5887
|
+
case "less":
|
|
5888
|
+
return "css";
|
|
5889
|
+
case "html":
|
|
5890
|
+
case "htm":
|
|
5891
|
+
case "xml":
|
|
5892
|
+
case "svg":
|
|
5893
|
+
case "vue":
|
|
5894
|
+
return "html";
|
|
6005
5895
|
default:
|
|
6006
|
-
return
|
|
5896
|
+
return null;
|
|
6007
5897
|
}
|
|
6008
5898
|
}
|
|
6009
|
-
function
|
|
6010
|
-
|
|
6011
|
-
|
|
6012
|
-
|
|
6013
|
-
|
|
6014
|
-
|
|
6015
|
-
|
|
6016
|
-
|
|
6017
|
-
|
|
6018
|
-
|
|
6019
|
-
|
|
6020
|
-
renderUnavailable,
|
|
6021
|
-
renderWarming,
|
|
6022
|
-
renderViewerCap,
|
|
6023
|
-
viewerCapReached,
|
|
6024
|
-
connectTimeoutMs = 13e3,
|
|
5899
|
+
function CodeEditor({
|
|
5900
|
+
path,
|
|
5901
|
+
initialContents,
|
|
5902
|
+
onSave,
|
|
5903
|
+
onOverwrite,
|
|
5904
|
+
onReload,
|
|
5905
|
+
onEditIntent,
|
|
5906
|
+
readOnly = false,
|
|
5907
|
+
themeType = "dark",
|
|
5908
|
+
loading,
|
|
5909
|
+
fallback,
|
|
6025
5910
|
className
|
|
6026
5911
|
}) {
|
|
6027
|
-
const
|
|
6028
|
-
const [
|
|
6029
|
-
const [
|
|
6030
|
-
const
|
|
6031
|
-
const
|
|
6032
|
-
const
|
|
6033
|
-
const
|
|
6034
|
-
const
|
|
6035
|
-
const
|
|
6036
|
-
const
|
|
6037
|
-
const
|
|
6038
|
-
const noLiveAddress = Boolean(capability) && !transportNull && !capability.url;
|
|
6039
|
-
const coldWarmable = transportNull && reason === "lease_cold" || !transportNull && noLiveAddress;
|
|
6040
|
-
const hardUnavailable = transportNull && isHardUnavailable(reason);
|
|
6041
|
-
const accept = () => {
|
|
6042
|
-
setConsented(true);
|
|
6043
|
-
onAcknowledge?.();
|
|
6044
|
-
};
|
|
5912
|
+
const [failed, setFailed] = useState16(false);
|
|
5913
|
+
const [bundle, setBundle] = useState16(null);
|
|
5914
|
+
const [value, setValue] = useState16(initialContents);
|
|
5915
|
+
const [baseline, setBaseline] = useState16(initialContents);
|
|
5916
|
+
const [saving, setSaving] = useState16(false);
|
|
5917
|
+
const [saveError, setSaveError] = useState16(null);
|
|
5918
|
+
const [savedTick, setSavedTick] = useState16(false);
|
|
5919
|
+
const dirty = value !== baseline;
|
|
5920
|
+
const saveConflict = saveError !== null && saveError.code === "file_write_conflict";
|
|
5921
|
+
const editIntentFiredRef = useRef16(false);
|
|
5922
|
+
const lastSeed = useRef16({ path, contents: initialContents });
|
|
6045
5923
|
useEffect19(() => {
|
|
6046
|
-
|
|
6047
|
-
|
|
6048
|
-
|
|
6049
|
-
|
|
6050
|
-
|
|
5924
|
+
const seedChanged = lastSeed.current.path !== path || lastSeed.current.contents !== initialContents;
|
|
5925
|
+
if (!seedChanged) return;
|
|
5926
|
+
lastSeed.current = { path, contents: initialContents };
|
|
5927
|
+
editIntentFiredRef.current = false;
|
|
5928
|
+
setValue(initialContents);
|
|
5929
|
+
setBaseline(initialContents);
|
|
5930
|
+
setSaveError(null);
|
|
5931
|
+
setSavedTick(false);
|
|
5932
|
+
}, [path, initialContents]);
|
|
5933
|
+
const noteEdit = useCallback19(
|
|
5934
|
+
(next) => {
|
|
5935
|
+
setValue(next);
|
|
5936
|
+
setSavedTick(false);
|
|
5937
|
+
if (!editIntentFiredRef.current && next !== baseline) {
|
|
5938
|
+
editIntentFiredRef.current = true;
|
|
5939
|
+
onEditIntent?.();
|
|
6051
5940
|
}
|
|
6052
|
-
}
|
|
6053
|
-
|
|
5941
|
+
},
|
|
5942
|
+
[baseline, onEditIntent]
|
|
5943
|
+
);
|
|
5944
|
+
const langKey = useMemo10(() => languageForPath(path), [path]);
|
|
5945
|
+
useEffect19(() => {
|
|
5946
|
+
let cancelled = false;
|
|
5947
|
+
void (async () => {
|
|
5948
|
+
try {
|
|
5949
|
+
const cmMod = await import("@uiw/react-codemirror");
|
|
5950
|
+
let languageExtension = null;
|
|
5951
|
+
if (langKey) {
|
|
5952
|
+
try {
|
|
5953
|
+
languageExtension = await LANGUAGE_LOADERS[langKey]?.() ?? null;
|
|
5954
|
+
} catch {
|
|
5955
|
+
languageExtension = null;
|
|
5956
|
+
}
|
|
5957
|
+
}
|
|
5958
|
+
if (cancelled) return;
|
|
5959
|
+
const saveKeymapExtension = (run) => cmMod.Prec.highest(
|
|
5960
|
+
cmMod.keymap.of([
|
|
5961
|
+
{
|
|
5962
|
+
key: "Mod-s",
|
|
5963
|
+
preventDefault: true,
|
|
5964
|
+
run: () => {
|
|
5965
|
+
run();
|
|
5966
|
+
return true;
|
|
5967
|
+
}
|
|
5968
|
+
}
|
|
5969
|
+
])
|
|
5970
|
+
);
|
|
5971
|
+
setBundle({
|
|
5972
|
+
Editor: cmMod.default,
|
|
5973
|
+
saveKeymapExtension,
|
|
5974
|
+
languageExtension
|
|
5975
|
+
});
|
|
5976
|
+
setFailed(false);
|
|
5977
|
+
} catch {
|
|
5978
|
+
if (!cancelled) setFailed(true);
|
|
5979
|
+
}
|
|
5980
|
+
})();
|
|
6054
5981
|
return () => {
|
|
6055
|
-
|
|
5982
|
+
cancelled = true;
|
|
6056
5983
|
};
|
|
6057
|
-
}, [
|
|
6058
|
-
const
|
|
6059
|
-
const stream = useDesktopStream({
|
|
6060
|
-
capability: connectCapability,
|
|
6061
|
-
containerRef,
|
|
6062
|
-
interactive: inControl,
|
|
6063
|
-
...scaleViewport !== void 0 ? { scaleViewport } : {},
|
|
6064
|
-
...rfbFactory ? { rfbFactory } : {},
|
|
6065
|
-
...webSocketFactory ? { webSocketFactory } : {}
|
|
5984
|
+
}, [langKey]);
|
|
5985
|
+
const saveRef = useRef16(() => {
|
|
6066
5986
|
});
|
|
6067
|
-
const
|
|
6068
|
-
|
|
6069
|
-
|
|
6070
|
-
|
|
6071
|
-
|
|
6072
|
-
|
|
6073
|
-
|
|
6074
|
-
|
|
6075
|
-
|
|
5987
|
+
const save = useCallback19(async () => {
|
|
5988
|
+
if (readOnly) return;
|
|
5989
|
+
const snapshot = value;
|
|
5990
|
+
if (snapshot === baseline) return;
|
|
5991
|
+
setSaving(true);
|
|
5992
|
+
setSaveError(null);
|
|
5993
|
+
setSavedTick(false);
|
|
5994
|
+
try {
|
|
5995
|
+
await onSave(snapshot);
|
|
5996
|
+
setBaseline(snapshot);
|
|
5997
|
+
lastSeed.current = { path, contents: snapshot };
|
|
5998
|
+
setSavedTick(true);
|
|
5999
|
+
} catch (cause) {
|
|
6000
|
+
setSaveError(cause instanceof Error ? cause : new Error(String(cause)));
|
|
6001
|
+
} finally {
|
|
6002
|
+
setSaving(false);
|
|
6076
6003
|
}
|
|
6077
|
-
|
|
6078
|
-
|
|
6079
|
-
|
|
6080
|
-
|
|
6081
|
-
|
|
6082
|
-
|
|
6083
|
-
|
|
6084
|
-
|
|
6085
|
-
|
|
6086
|
-
|
|
6087
|
-
|
|
6088
|
-
|
|
6089
|
-
|
|
6090
|
-
|
|
6091
|
-
|
|
6092
|
-
|
|
6093
|
-
|
|
6094
|
-
|
|
6095
|
-
|
|
6096
|
-
|
|
6097
|
-
|
|
6098
|
-
const timer = setTimeout(() => setConnectTimedOut(true), connectTimeoutMs);
|
|
6099
|
-
return () => clearTimeout(timer);
|
|
6100
|
-
}, [
|
|
6101
|
-
hasLiveUrl,
|
|
6102
|
-
connected,
|
|
6103
|
-
connectTimeoutMs,
|
|
6104
|
-
connectCapability?.url,
|
|
6105
|
-
stream.error,
|
|
6106
|
-
reconnectNonce
|
|
6107
|
-
]);
|
|
6108
|
-
const reconnect = () => {
|
|
6109
|
-
setConnectTimedOut(false);
|
|
6110
|
-
setReconnectNonce((n) => n + 1);
|
|
6111
|
-
stream.reconnect();
|
|
6004
|
+
}, [readOnly, value, baseline, onSave, path]);
|
|
6005
|
+
const overwrite = useCallback19(async () => {
|
|
6006
|
+
if (readOnly || !onOverwrite) return;
|
|
6007
|
+
const snapshot = value;
|
|
6008
|
+
if (snapshot === baseline) return;
|
|
6009
|
+
setSaving(true);
|
|
6010
|
+
setSaveError(null);
|
|
6011
|
+
setSavedTick(false);
|
|
6012
|
+
try {
|
|
6013
|
+
await onOverwrite(snapshot);
|
|
6014
|
+
setBaseline(snapshot);
|
|
6015
|
+
lastSeed.current = { path, contents: snapshot };
|
|
6016
|
+
setSavedTick(true);
|
|
6017
|
+
} catch (cause) {
|
|
6018
|
+
setSaveError(cause instanceof Error ? cause : new Error(String(cause)));
|
|
6019
|
+
} finally {
|
|
6020
|
+
setSaving(false);
|
|
6021
|
+
}
|
|
6022
|
+
}, [readOnly, onOverwrite, value, baseline, path]);
|
|
6023
|
+
saveRef.current = () => {
|
|
6024
|
+
void save();
|
|
6112
6025
|
};
|
|
6113
|
-
|
|
6114
|
-
|
|
6115
|
-
|
|
6116
|
-
|
|
6117
|
-
|
|
6118
|
-
|
|
6119
|
-
|
|
6120
|
-
|
|
6121
|
-
|
|
6122
|
-
|
|
6123
|
-
|
|
6124
|
-
|
|
6125
|
-
|
|
6126
|
-
|
|
6127
|
-
overlay = renderViewerCap?.() ?? defaultNotice(
|
|
6128
|
-
"Too many viewers",
|
|
6129
|
-
"This session has reached its live-viewer limit. Try again shortly."
|
|
6130
|
-
);
|
|
6131
|
-
break;
|
|
6132
|
-
case "unavailable":
|
|
6133
|
-
overlay = renderUnavailable?.(reason) ?? defaultNotice("Desktop unavailable", unavailableCopy(reason));
|
|
6134
|
-
break;
|
|
6135
|
-
case "consent":
|
|
6136
|
-
overlay = renderConsentGate ? renderConsentGate(accept, capability?.shared ?? false) : /* @__PURE__ */ jsx10(DefaultConsentGate, { shared: capability?.shared ?? false, onAccept: accept });
|
|
6137
|
-
break;
|
|
6138
|
-
case "warming":
|
|
6139
|
-
overlay = renderWarming?.() ?? /* @__PURE__ */ jsx10(WarmingNotice, {});
|
|
6140
|
-
break;
|
|
6141
|
-
case "connect_failed":
|
|
6142
|
-
overlay = defaultNotice(
|
|
6143
|
-
"Couldn\u2019t connect",
|
|
6144
|
-
"The desktop is warm but the live stream didn\u2019t come up. This usually clears on a retry.",
|
|
6145
|
-
reconnect
|
|
6146
|
-
);
|
|
6147
|
-
break;
|
|
6148
|
-
case "error":
|
|
6149
|
-
overlay = defaultNotice(
|
|
6150
|
-
"Desktop disconnected",
|
|
6151
|
-
stream.error?.message ?? "The stream dropped.",
|
|
6152
|
-
reconnect
|
|
6153
|
-
);
|
|
6154
|
-
break;
|
|
6155
|
-
case "connecting":
|
|
6156
|
-
case "connected":
|
|
6157
|
-
overlay = null;
|
|
6158
|
-
break;
|
|
6159
|
-
}
|
|
6160
|
-
return /* @__PURE__ */ jsxs8(
|
|
6026
|
+
useEffect19(() => {
|
|
6027
|
+
if (!savedTick) return;
|
|
6028
|
+
const t = setTimeout(() => setSavedTick(false), 1800);
|
|
6029
|
+
return () => clearTimeout(t);
|
|
6030
|
+
}, [savedTick]);
|
|
6031
|
+
const fileName = path.split("/").filter(Boolean).pop() ?? path;
|
|
6032
|
+
const cmExtensions = useMemo10(() => {
|
|
6033
|
+
if (!bundle) return [];
|
|
6034
|
+
const exts = [bundle.saveKeymapExtension(() => saveRef.current())];
|
|
6035
|
+
if (bundle.languageExtension) exts.push(bundle.languageExtension);
|
|
6036
|
+
return exts;
|
|
6037
|
+
}, [bundle]);
|
|
6038
|
+
const Editor = bundle?.Editor;
|
|
6039
|
+
return /* @__PURE__ */ jsxs7(
|
|
6161
6040
|
"div",
|
|
6162
6041
|
{
|
|
6163
|
-
className: cn(
|
|
6164
|
-
|
|
6165
|
-
|
|
6166
|
-
className
|
|
6167
|
-
),
|
|
6168
|
-
style: { backgroundColor: DESKTOP_MEDIA_BACKGROUND },
|
|
6169
|
-
"data-opengeni-desktop": true,
|
|
6170
|
-
"data-state": stream.state,
|
|
6171
|
-
"data-ui-state": uiState,
|
|
6172
|
-
"data-in-control": inControl ? "true" : void 0,
|
|
6042
|
+
className: cn("flex h-full min-h-0 flex-col", className),
|
|
6043
|
+
"data-opengeni-code-editor": true,
|
|
6044
|
+
style: editorVars,
|
|
6173
6045
|
children: [
|
|
6174
|
-
/* @__PURE__ */
|
|
6175
|
-
|
|
6176
|
-
|
|
6177
|
-
|
|
6178
|
-
|
|
6179
|
-
|
|
6180
|
-
|
|
6181
|
-
|
|
6182
|
-
|
|
6183
|
-
|
|
6184
|
-
|
|
6185
|
-
|
|
6186
|
-
|
|
6187
|
-
|
|
6046
|
+
/* @__PURE__ */ jsxs7("div", { className: "flex min-h-9 shrink-0 items-center gap-2 border-b border-og-border bg-og-surface-1 px-2 py-1", children: [
|
|
6047
|
+
/* @__PURE__ */ jsx9(
|
|
6048
|
+
"span",
|
|
6049
|
+
{
|
|
6050
|
+
className: cn(
|
|
6051
|
+
"size-1.5 shrink-0 rounded-full transition-colors",
|
|
6052
|
+
readOnly ? "bg-transparent" : dirty ? "bg-og-status-running" : "bg-og-status-idle"
|
|
6053
|
+
),
|
|
6054
|
+
title: readOnly ? "read-only" : dirty ? "unsaved changes" : "saved"
|
|
6055
|
+
}
|
|
6056
|
+
),
|
|
6057
|
+
/* @__PURE__ */ jsxs7("span", { className: "truncate font-og-mono text-og-xs text-og-fg-muted", children: [
|
|
6058
|
+
fileName,
|
|
6059
|
+
dirty && !readOnly ? " \u2022" : ""
|
|
6060
|
+
] }),
|
|
6061
|
+
/* @__PURE__ */ jsxs7("div", { className: "ml-auto flex shrink-0 items-center gap-2", children: [
|
|
6062
|
+
saveError && !saveConflict && /* @__PURE__ */ jsx9(
|
|
6063
|
+
"span",
|
|
6188
6064
|
{
|
|
6189
|
-
className: "
|
|
6190
|
-
|
|
6065
|
+
className: "max-w-[220px] truncate text-og-xs text-og-status-failed",
|
|
6066
|
+
title: saveError.message,
|
|
6067
|
+
children: saveError.message
|
|
6191
6068
|
}
|
|
6192
|
-
)
|
|
6193
|
-
|
|
6194
|
-
|
|
6069
|
+
),
|
|
6070
|
+
!saveError && savedTick && /* @__PURE__ */ jsx9("span", { className: "text-og-xs text-og-status-idle", children: "Saved" }),
|
|
6071
|
+
readOnly ? /* @__PURE__ */ jsx9("span", { className: "text-og-xs uppercase tracking-wide text-og-fg-subtle", children: "Read-only" }) : !saveConflict ? /* @__PURE__ */ jsxs7(
|
|
6072
|
+
"button",
|
|
6073
|
+
{
|
|
6074
|
+
type: "button",
|
|
6075
|
+
onClick: () => void save(),
|
|
6076
|
+
disabled: saving || !dirty,
|
|
6077
|
+
className: cn(
|
|
6078
|
+
"flex items-center gap-1 rounded-og-sm border border-og-border px-1.5 py-0.5 text-og-xs pointer-coarse:min-h-11",
|
|
6079
|
+
saving || !dirty ? "cursor-default text-og-fg-subtle opacity-60" : "text-og-fg hover:bg-og-accent-soft"
|
|
6080
|
+
),
|
|
6081
|
+
title: "Save (\u2318/Ctrl+S)",
|
|
6082
|
+
children: [
|
|
6083
|
+
saving ? /* @__PURE__ */ jsx9(Loader2Icon, { className: "size-3 animate-spin" }) : /* @__PURE__ */ jsx9(SaveIcon, { className: "size-3" }),
|
|
6084
|
+
"Save"
|
|
6085
|
+
]
|
|
6086
|
+
}
|
|
6087
|
+
) : null
|
|
6088
|
+
] })
|
|
6195
6089
|
] }),
|
|
6196
|
-
|
|
6197
|
-
|
|
6090
|
+
saveConflict ? /* @__PURE__ */ jsxs7(
|
|
6091
|
+
"div",
|
|
6198
6092
|
{
|
|
6199
|
-
|
|
6200
|
-
|
|
6093
|
+
className: "flex shrink-0 flex-wrap items-center gap-x-3 gap-y-2 border-b border-og-status-failed/25 bg-og-status-failed/8 px-2 py-2",
|
|
6094
|
+
role: "alert",
|
|
6095
|
+
children: [
|
|
6096
|
+
/* @__PURE__ */ jsxs7("div", { className: "min-w-[12rem] flex-1 text-og-xs text-og-fg-muted", children: [
|
|
6097
|
+
/* @__PURE__ */ jsx9("span", { className: "font-medium text-og-status-failed", children: "File changed on machine." }),
|
|
6098
|
+
" ",
|
|
6099
|
+
"Reloading discards your edits; overwriting replaces the live version."
|
|
6100
|
+
] }),
|
|
6101
|
+
/* @__PURE__ */ jsxs7("div", { className: "ml-auto flex shrink-0 items-center gap-1.5", children: [
|
|
6102
|
+
onReload ? /* @__PURE__ */ jsx9(
|
|
6103
|
+
"button",
|
|
6104
|
+
{
|
|
6105
|
+
type: "button",
|
|
6106
|
+
onClick: () => {
|
|
6107
|
+
setSaveError(null);
|
|
6108
|
+
onReload();
|
|
6109
|
+
},
|
|
6110
|
+
className: "min-h-8 rounded-og-sm border border-og-border bg-og-surface-1 px-2 text-og-xs font-medium text-og-fg hover:bg-og-surface-2 pointer-coarse:min-h-11",
|
|
6111
|
+
children: "Reload live"
|
|
6112
|
+
}
|
|
6113
|
+
) : null,
|
|
6114
|
+
onOverwrite ? /* @__PURE__ */ jsx9(
|
|
6115
|
+
"button",
|
|
6116
|
+
{
|
|
6117
|
+
type: "button",
|
|
6118
|
+
onClick: () => void overwrite(),
|
|
6119
|
+
disabled: saving,
|
|
6120
|
+
className: "min-h-8 rounded-og-sm border border-og-status-failed/40 px-2 text-og-xs font-medium text-og-status-failed hover:bg-og-status-failed/10 disabled:opacity-50 pointer-coarse:min-h-11",
|
|
6121
|
+
children: "Overwrite"
|
|
6122
|
+
}
|
|
6123
|
+
) : null
|
|
6124
|
+
] })
|
|
6125
|
+
]
|
|
6201
6126
|
}
|
|
6202
|
-
),
|
|
6203
|
-
|
|
6204
|
-
|
|
6127
|
+
) : null,
|
|
6128
|
+
/* @__PURE__ */ jsx9("div", { className: "min-h-0 flex-1 overflow-auto", children: failed ? fallback ?? /* @__PURE__ */ jsx9(
|
|
6129
|
+
PlainTextarea,
|
|
6205
6130
|
{
|
|
6206
|
-
|
|
6207
|
-
|
|
6208
|
-
|
|
6131
|
+
value,
|
|
6132
|
+
readOnly,
|
|
6133
|
+
onChange: noteEdit,
|
|
6134
|
+
onSave: () => void save()
|
|
6209
6135
|
}
|
|
6210
|
-
)
|
|
6211
|
-
|
|
6136
|
+
) : Editor ? /* @__PURE__ */ jsx9(
|
|
6137
|
+
Editor,
|
|
6138
|
+
{
|
|
6139
|
+
value,
|
|
6140
|
+
theme: themeType === "light" ? "light" : "dark",
|
|
6141
|
+
editable: !readOnly,
|
|
6142
|
+
readOnly,
|
|
6143
|
+
basicSetup: true,
|
|
6144
|
+
extensions: cmExtensions,
|
|
6145
|
+
height: "100%",
|
|
6146
|
+
className: "og-cm-editor min-h-full text-og-sm",
|
|
6147
|
+
onChange: readOnly ? void 0 : noteEdit
|
|
6148
|
+
}
|
|
6149
|
+
) : loading ?? /* @__PURE__ */ jsx9(EditorSkeleton, {}) })
|
|
6212
6150
|
]
|
|
6213
6151
|
}
|
|
6214
6152
|
);
|
|
6215
6153
|
}
|
|
6216
|
-
|
|
6217
|
-
|
|
6218
|
-
|
|
6219
|
-
|
|
6154
|
+
var editorVars = {
|
|
6155
|
+
"--og-cm-bg": "var(--og-color-bg)",
|
|
6156
|
+
fontFamily: "var(--og-font-mono)"
|
|
6157
|
+
};
|
|
6158
|
+
function PlainTextarea({
|
|
6159
|
+
value,
|
|
6160
|
+
readOnly,
|
|
6161
|
+
onChange,
|
|
6162
|
+
onSave
|
|
6220
6163
|
}) {
|
|
6221
|
-
return (
|
|
6222
|
-
|
|
6223
|
-
|
|
6224
|
-
|
|
6225
|
-
|
|
6226
|
-
|
|
6227
|
-
|
|
6228
|
-
|
|
6229
|
-
|
|
6230
|
-
|
|
6231
|
-
|
|
6232
|
-
|
|
6233
|
-
|
|
6234
|
-
|
|
6235
|
-
|
|
6236
|
-
|
|
6237
|
-
"outline-none transition-all duration-150 ease-out",
|
|
6238
|
-
"focus-visible:ring-2 focus-visible:ring-og-accent focus-visible:ring-offset-2 focus-visible:ring-offset-og-bg",
|
|
6239
|
-
disabled ? "cursor-not-allowed opacity-60" : cn(
|
|
6240
|
-
"cursor-pointer opacity-90 hover:-translate-y-0.5 hover:opacity-100",
|
|
6241
|
-
"hover:border-og-accent",
|
|
6242
|
-
"hover:bg-og-accent/10"
|
|
6243
|
-
)
|
|
6244
|
-
),
|
|
6245
|
-
children: [
|
|
6246
|
-
/* @__PURE__ */ jsx10(
|
|
6247
|
-
"span",
|
|
6248
|
-
{
|
|
6249
|
-
className: cn(
|
|
6250
|
-
"flex size-9 shrink-0 items-center justify-center rounded-full transition-colors",
|
|
6251
|
-
"bg-og-accent",
|
|
6252
|
-
"text-og-accent-fg",
|
|
6253
|
-
disabled ? "" : "group-hover:scale-105"
|
|
6254
|
-
),
|
|
6255
|
-
children: /* @__PURE__ */ jsx10(MousePointerClickIcon, { className: "size-5", strokeWidth: 2 })
|
|
6256
|
-
}
|
|
6257
|
-
),
|
|
6258
|
-
/* @__PURE__ */ jsxs8("span", { className: "flex flex-col items-start leading-tight", children: [
|
|
6259
|
-
/* @__PURE__ */ jsx10("span", { className: "text-og-base font-semibold text-og-fg", children: "Take control" }),
|
|
6260
|
-
/* @__PURE__ */ jsx10("span", { className: "text-og-xs text-og-fg-subtle", children: disabled && disabledReason ? disabledReason : "Drive the mouse & keyboard" })
|
|
6261
|
-
] })
|
|
6262
|
-
]
|
|
6263
|
-
}
|
|
6264
|
-
) })
|
|
6164
|
+
return /* @__PURE__ */ jsx9(
|
|
6165
|
+
"textarea",
|
|
6166
|
+
{
|
|
6167
|
+
value,
|
|
6168
|
+
readOnly,
|
|
6169
|
+
spellCheck: false,
|
|
6170
|
+
onChange: (e) => onChange(e.target.value),
|
|
6171
|
+
onKeyDown: (event) => {
|
|
6172
|
+
if ((event.metaKey || event.ctrlKey) && event.key.toLowerCase() === "s") {
|
|
6173
|
+
event.preventDefault();
|
|
6174
|
+
onSave();
|
|
6175
|
+
}
|
|
6176
|
+
},
|
|
6177
|
+
"aria-label": "Plain text file editor",
|
|
6178
|
+
className: "h-full w-full resize-none bg-transparent p-2 font-og-mono text-og-sm text-og-fg outline-none"
|
|
6179
|
+
}
|
|
6265
6180
|
);
|
|
6266
6181
|
}
|
|
6267
|
-
function
|
|
6268
|
-
return /* @__PURE__ */
|
|
6269
|
-
|
|
6270
|
-
|
|
6271
|
-
|
|
6272
|
-
|
|
6273
|
-
|
|
6274
|
-
|
|
6275
|
-
|
|
6182
|
+
function EditorSkeleton() {
|
|
6183
|
+
return /* @__PURE__ */ jsx9("div", { className: "p-3 text-og-sm text-og-fg-subtle", children: "Loading editor\u2026" });
|
|
6184
|
+
}
|
|
6185
|
+
|
|
6186
|
+
// src/components/sandbox-files.tsx
|
|
6187
|
+
import { FileCode2Icon, FileWarningIcon as FileWarningIcon2, LoaderCircleIcon } from "lucide-react";
|
|
6188
|
+
import { useCallback as useCallback20, useEffect as useEffect20, useRef as useRef17, useState as useState17 } from "react";
|
|
6189
|
+
import { Fragment as Fragment3, jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
6190
|
+
function SandboxFiles({
|
|
6191
|
+
files,
|
|
6192
|
+
git,
|
|
6193
|
+
fileSystemAvailable = true,
|
|
6194
|
+
usePierre = true,
|
|
6195
|
+
editable = true,
|
|
6196
|
+
onEditIntent,
|
|
6197
|
+
requestedPath,
|
|
6198
|
+
requestedPathRequestId,
|
|
6199
|
+
requestedPathReady = true,
|
|
6200
|
+
workspaceResting = false,
|
|
6201
|
+
workspaceWaking = false,
|
|
6202
|
+
liveWorkspaceReady = true,
|
|
6203
|
+
onWakeWorkspace,
|
|
6204
|
+
themeType,
|
|
6205
|
+
className
|
|
6206
|
+
}) {
|
|
6207
|
+
const [selected, setSelected] = useState17(null);
|
|
6208
|
+
const [editMode, setEditMode] = useState17(false);
|
|
6209
|
+
const [liveRequestedPath, setLiveRequestedPath] = useState17(null);
|
|
6210
|
+
const [viewReloadRevision, setViewReloadRevision] = useState17(0);
|
|
6211
|
+
const pendingRequestRef = useRef17(null);
|
|
6212
|
+
const handledRequestRef = useRef17(null);
|
|
6213
|
+
const requestKey = requestedPath ? requestedPathRequestId ?? requestedPath : null;
|
|
6214
|
+
useEffect20(() => {
|
|
6215
|
+
if (!requestedPath || requestKey === null) {
|
|
6216
|
+
pendingRequestRef.current = null;
|
|
6217
|
+
handledRequestRef.current = null;
|
|
6218
|
+
return;
|
|
6219
|
+
}
|
|
6220
|
+
if (handledRequestRef.current === requestKey) {
|
|
6221
|
+
return;
|
|
6222
|
+
}
|
|
6223
|
+
if (!requestedPathReady) {
|
|
6224
|
+
pendingRequestRef.current = requestKey;
|
|
6225
|
+
return;
|
|
6226
|
+
}
|
|
6227
|
+
handledRequestRef.current = requestKey;
|
|
6228
|
+
pendingRequestRef.current = null;
|
|
6229
|
+
setSelected(requestedPath);
|
|
6230
|
+
setEditMode(false);
|
|
6231
|
+
}, [requestKey, requestedPath, requestedPathReady]);
|
|
6232
|
+
const rootRef = useRef17(null);
|
|
6233
|
+
const [wide, setWide] = useState17(false);
|
|
6234
|
+
useEffect20(() => {
|
|
6235
|
+
const el = rootRef.current;
|
|
6236
|
+
if (!el || typeof ResizeObserver === "undefined") return;
|
|
6237
|
+
const observer = new ResizeObserver((entries) => {
|
|
6238
|
+
const w = entries[0]?.contentRect.width ?? 0;
|
|
6239
|
+
setWide(w >= 720);
|
|
6240
|
+
});
|
|
6241
|
+
observer.observe(el);
|
|
6242
|
+
return () => observer.disconnect();
|
|
6243
|
+
}, []);
|
|
6244
|
+
const resolvedTheme = useThemeType(themeType);
|
|
6245
|
+
const viewPath = selected;
|
|
6246
|
+
const fileView = useFileView(viewPath, files.readFile, viewReloadRevision);
|
|
6247
|
+
const selectFile = useCallback20((path) => {
|
|
6248
|
+
if (pendingRequestRef.current !== null) {
|
|
6249
|
+
handledRequestRef.current = pendingRequestRef.current;
|
|
6250
|
+
pendingRequestRef.current = null;
|
|
6251
|
+
}
|
|
6252
|
+
setSelected(path);
|
|
6253
|
+
setEditMode(false);
|
|
6254
|
+
setLiveRequestedPath(null);
|
|
6255
|
+
setViewReloadRevision(0);
|
|
6256
|
+
}, []);
|
|
6257
|
+
const canEdit = editable && viewPath !== null && !fileView.loading && fileView.error === null && !fileView.isBinary && !fileView.truncated && fileView.content !== null;
|
|
6258
|
+
const showEditor = canEdit && editMode;
|
|
6259
|
+
const captureFileUnavailable = fileView.error instanceof CapturedFileUnavailableError ? fileView.error : null;
|
|
6260
|
+
const waitingForSelectedFile = liveRequestedPath === viewPath && (!liveWorkspaceReady || files.loading) && captureFileUnavailable !== null;
|
|
6261
|
+
if (!fileSystemAvailable) {
|
|
6262
|
+
return /* @__PURE__ */ jsx10(
|
|
6263
|
+
Notice,
|
|
6264
|
+
{
|
|
6265
|
+
className,
|
|
6266
|
+
icon: /* @__PURE__ */ jsx10(FileWarningIcon2, { className: "size-5", "aria-hidden": true }),
|
|
6267
|
+
title: "Files unavailable",
|
|
6268
|
+
announce: "alert",
|
|
6269
|
+
children: "This sandbox does not expose a file system."
|
|
6270
|
+
}
|
|
6271
|
+
);
|
|
6272
|
+
}
|
|
6273
|
+
if (workspaceResting || workspaceWaking) {
|
|
6274
|
+
return /* @__PURE__ */ jsx10("div", { className: cn("h-full", className), "data-opengeni-workspace-resting": true, children: /* @__PURE__ */ jsxs8(
|
|
6275
|
+
Notice,
|
|
6276
|
+
{
|
|
6277
|
+
icon: workspaceWaking ? /* @__PURE__ */ jsx10(
|
|
6278
|
+
LoaderCircleIcon,
|
|
6279
|
+
{
|
|
6280
|
+
className: "size-5 animate-spin motion-reduce:animate-none",
|
|
6281
|
+
"aria-hidden": true
|
|
6282
|
+
}
|
|
6283
|
+
) : /* @__PURE__ */ jsx10(FileCode2Icon, { className: "size-5", "aria-hidden": true }),
|
|
6284
|
+
title: workspaceWaking ? "Waking workspace" : "Workspace is resting",
|
|
6285
|
+
announce: "status",
|
|
6286
|
+
children: [
|
|
6287
|
+
/* @__PURE__ */ jsx10("p", { children: workspaceWaking ? "Connecting to the live file system\u2026" : "No captured revision is available yet. Wake the sandbox to browse its current files." }),
|
|
6288
|
+
!workspaceWaking && onWakeWorkspace ? /* @__PURE__ */ jsx10(WakeButton, { onClick: onWakeWorkspace, children: "Open live workspace" }) : null
|
|
6289
|
+
]
|
|
6290
|
+
}
|
|
6291
|
+
) });
|
|
6292
|
+
}
|
|
6293
|
+
return /* @__PURE__ */ jsxs8("div", { ref: rootRef, className: cn("flex h-full min-h-0 min-w-0 flex-col", className), children: [
|
|
6294
|
+
/* @__PURE__ */ jsx10(GitHeader, { git, dirtyCount: git.diff.length }),
|
|
6295
|
+
/* @__PURE__ */ jsxs8("div", { className: cn("flex min-h-0 flex-1", wide ? "flex-row" : "flex-col"), children: [
|
|
6296
|
+
/* @__PURE__ */ jsx10(
|
|
6297
|
+
"div",
|
|
6298
|
+
{
|
|
6299
|
+
className: cn(
|
|
6300
|
+
"flex min-h-0 flex-col",
|
|
6301
|
+
wide ? "w-[280px] shrink-0 border-r border-og-border" : "flex-1"
|
|
6302
|
+
),
|
|
6303
|
+
children: /* @__PURE__ */ jsx10(
|
|
6304
|
+
FileBrowser,
|
|
6305
|
+
{
|
|
6306
|
+
result: files,
|
|
6307
|
+
selectedPath: selected ?? void 0,
|
|
6308
|
+
onSelectFile: selectFile,
|
|
6309
|
+
editable,
|
|
6310
|
+
emptyState: "This directory is empty",
|
|
6311
|
+
className: "min-w-0 flex-1"
|
|
6312
|
+
}
|
|
6313
|
+
)
|
|
6314
|
+
}
|
|
6315
|
+
),
|
|
6276
6316
|
/* @__PURE__ */ jsxs8(
|
|
6277
|
-
"
|
|
6317
|
+
"div",
|
|
6278
6318
|
{
|
|
6279
|
-
type: "button",
|
|
6280
|
-
onClick: onRelease,
|
|
6281
|
-
title: "Return control (or press Ctrl+Alt+Shift)",
|
|
6282
6319
|
className: cn(
|
|
6283
|
-
"
|
|
6284
|
-
"
|
|
6285
|
-
"outline-none hover:bg-og-accent hover:text-og-accent-fg focus-visible:ring-2 focus-visible:ring-og-accent"
|
|
6320
|
+
"flex min-h-0 min-w-0 flex-col",
|
|
6321
|
+
wide ? "flex-1" : "flex-[1.4] border-t border-og-border"
|
|
6286
6322
|
),
|
|
6287
6323
|
children: [
|
|
6288
|
-
/* @__PURE__ */
|
|
6289
|
-
|
|
6324
|
+
/* @__PURE__ */ jsxs8("div", { className: "flex shrink-0 items-center justify-between gap-2 border-b border-og-border bg-og-surface-1 px-2 py-1", children: [
|
|
6325
|
+
/* @__PURE__ */ jsx10(
|
|
6326
|
+
"span",
|
|
6327
|
+
{
|
|
6328
|
+
"data-opengeni-selected-file": true,
|
|
6329
|
+
className: "min-w-0 truncate font-og-mono text-og-xs text-og-fg-muted",
|
|
6330
|
+
children: selected ?? "No file selected"
|
|
6331
|
+
}
|
|
6332
|
+
),
|
|
6333
|
+
canEdit && /* @__PURE__ */ jsx10(
|
|
6334
|
+
Segmented,
|
|
6335
|
+
{
|
|
6336
|
+
options: [
|
|
6337
|
+
{ value: "view", label: "View" },
|
|
6338
|
+
{ value: "edit", label: "Edit" }
|
|
6339
|
+
],
|
|
6340
|
+
value: editMode ? "edit" : "view",
|
|
6341
|
+
onChange: (v) => setEditMode(v === "edit")
|
|
6342
|
+
}
|
|
6343
|
+
)
|
|
6344
|
+
] }),
|
|
6345
|
+
/* @__PURE__ */ jsx10("div", { className: "min-h-0 flex-1 overflow-auto", children: viewPath ? showEditor && fileView.content !== null ? /* @__PURE__ */ jsx10(
|
|
6346
|
+
CodeEditor,
|
|
6347
|
+
{
|
|
6348
|
+
path: viewPath,
|
|
6349
|
+
initialContents: fileView.content,
|
|
6350
|
+
themeType: resolvedTheme,
|
|
6351
|
+
onSave: (contents) => files.writeFile(viewPath, contents, { expectedContent: fileView.content }),
|
|
6352
|
+
onOverwrite: (contents) => files.writeFile(viewPath, contents, { force: true }),
|
|
6353
|
+
onReload: () => setViewReloadRevision((revision) => revision + 1),
|
|
6354
|
+
...onEditIntent ? { onEditIntent } : {},
|
|
6355
|
+
className: "h-full"
|
|
6356
|
+
},
|
|
6357
|
+
viewPath
|
|
6358
|
+
) : waitingForSelectedFile ? /* @__PURE__ */ jsxs8(
|
|
6359
|
+
Notice,
|
|
6360
|
+
{
|
|
6361
|
+
icon: /* @__PURE__ */ jsx10(
|
|
6362
|
+
LoaderCircleIcon,
|
|
6363
|
+
{
|
|
6364
|
+
className: "size-5 animate-spin motion-reduce:animate-none",
|
|
6365
|
+
"aria-hidden": true
|
|
6366
|
+
}
|
|
6367
|
+
),
|
|
6368
|
+
title: "Waking workspace",
|
|
6369
|
+
announce: "status",
|
|
6370
|
+
children: [
|
|
6371
|
+
"Opening ",
|
|
6372
|
+
viewPath,
|
|
6373
|
+
" when the live file system is ready\u2026"
|
|
6374
|
+
]
|
|
6375
|
+
}
|
|
6376
|
+
) : captureFileUnavailable ? /* @__PURE__ */ jsxs8(Notice, { icon: /* @__PURE__ */ jsx10(FileCode2Icon, { className: "size-5", "aria-hidden": true }), title: "On machine", children: [
|
|
6377
|
+
/* @__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." }),
|
|
6378
|
+
onWakeWorkspace ? /* @__PURE__ */ jsx10(
|
|
6379
|
+
WakeButton,
|
|
6380
|
+
{
|
|
6381
|
+
onClick: () => {
|
|
6382
|
+
setLiveRequestedPath(viewPath);
|
|
6383
|
+
if (liveWorkspaceReady) void files.refresh();
|
|
6384
|
+
else onWakeWorkspace();
|
|
6385
|
+
},
|
|
6386
|
+
children: liveWorkspaceReady ? "Retry live file" : "Open live file"
|
|
6387
|
+
}
|
|
6388
|
+
) : null
|
|
6389
|
+
] }) : fileView.error ? /* @__PURE__ */ jsxs8(Notice, { announce: "alert", children: [
|
|
6390
|
+
"Could not open ",
|
|
6391
|
+
viewPath,
|
|
6392
|
+
": ",
|
|
6393
|
+
fileView.error.message
|
|
6394
|
+
] }) : fileView.loading ? /* @__PURE__ */ jsxs8(Notice, { announce: "status", children: [
|
|
6395
|
+
"Loading ",
|
|
6396
|
+
viewPath,
|
|
6397
|
+
"\u2026"
|
|
6398
|
+
] }) : fileView.isBinary ? /* @__PURE__ */ jsxs8(Notice, { children: [
|
|
6399
|
+
viewPath,
|
|
6400
|
+
" is a binary file (",
|
|
6401
|
+
fileView.sizeBytes ?? 0,
|
|
6402
|
+
" bytes)."
|
|
6403
|
+
] }) : fileView.content !== null ? /* @__PURE__ */ jsxs8(Fragment3, { children: [
|
|
6404
|
+
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: [
|
|
6405
|
+
"Large file \u2014 showing a truncated preview (",
|
|
6406
|
+
fileView.sizeBytes ?? 0,
|
|
6407
|
+
" bytes loaded). Editing is disabled to avoid corrupting the file."
|
|
6408
|
+
] }),
|
|
6409
|
+
usePierre ? /* @__PURE__ */ jsx10(
|
|
6410
|
+
PierreFile,
|
|
6411
|
+
{
|
|
6412
|
+
path: viewPath,
|
|
6413
|
+
contents: fileView.content,
|
|
6414
|
+
themeType: resolvedTheme,
|
|
6415
|
+
fallback: /* @__PURE__ */ jsx10("pre", { className: "overflow-auto whitespace-pre p-2 font-og-mono text-og-sm text-og-fg", children: fileView.content }),
|
|
6416
|
+
className: "p-1"
|
|
6417
|
+
}
|
|
6418
|
+
) : /* @__PURE__ */ jsx10("pre", { className: "overflow-auto whitespace-pre p-2 font-og-mono text-og-sm text-og-fg", children: fileView.content })
|
|
6419
|
+
] }) : /* @__PURE__ */ jsxs8(Notice, { announce: "status", children: [
|
|
6420
|
+
"Loading ",
|
|
6421
|
+
viewPath,
|
|
6422
|
+
"\u2026"
|
|
6423
|
+
] }) : (
|
|
6424
|
+
// Nothing selected — the tree shows the whole workspace; pick a file.
|
|
6425
|
+
requestedPath && !requestedPathReady ? /* @__PURE__ */ jsxs8(
|
|
6426
|
+
Notice,
|
|
6427
|
+
{
|
|
6428
|
+
icon: /* @__PURE__ */ jsx10(
|
|
6429
|
+
LoaderCircleIcon,
|
|
6430
|
+
{
|
|
6431
|
+
className: "size-5 animate-spin motion-reduce:animate-none",
|
|
6432
|
+
"aria-hidden": true
|
|
6433
|
+
}
|
|
6434
|
+
),
|
|
6435
|
+
title: "Waking sandbox",
|
|
6436
|
+
announce: "status",
|
|
6437
|
+
children: [
|
|
6438
|
+
"Opening ",
|
|
6439
|
+
requestedPath,
|
|
6440
|
+
" when the live workspace is ready\u2026"
|
|
6441
|
+
]
|
|
6442
|
+
}
|
|
6443
|
+
) : /* @__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." })
|
|
6444
|
+
) })
|
|
6290
6445
|
]
|
|
6291
6446
|
}
|
|
6292
6447
|
)
|
|
6293
6448
|
] })
|
|
6294
6449
|
] });
|
|
6295
6450
|
}
|
|
6296
|
-
function
|
|
6297
|
-
|
|
6298
|
-
|
|
6299
|
-
|
|
6300
|
-
|
|
6301
|
-
|
|
6302
|
-
|
|
6303
|
-
|
|
6304
|
-
|
|
6305
|
-
|
|
6306
|
-
case "disabled_by_policy":
|
|
6307
|
-
return "Desktop streaming is disabled on this deployment.";
|
|
6308
|
-
case "lease_cold":
|
|
6309
|
-
return "Waiting for the sandbox to start\u2026";
|
|
6310
|
-
default:
|
|
6311
|
-
return "The desktop isn\u2019t available for this sandbox.";
|
|
6312
|
-
}
|
|
6313
|
-
}
|
|
6314
|
-
function WarmingNotice({ onRetry }) {
|
|
6315
|
-
return /* @__PURE__ */ jsxs8("div", { className: "flex max-w-sm flex-col items-center gap-3 rounded-og-lg border border-og-border bg-og-bg/90 p-5 text-center text-og-base text-og-fg backdrop-blur-sm", children: [
|
|
6316
|
-
/* @__PURE__ */ jsx10(LoaderCircleIcon2, { className: "size-7 animate-og-spin text-og-accent", strokeWidth: 1.5 }),
|
|
6317
|
-
/* @__PURE__ */ jsxs8("div", { className: "space-y-1", children: [
|
|
6318
|
-
/* @__PURE__ */ jsx10("div", { className: "font-medium", children: "Warming the sandbox\u2026" }),
|
|
6319
|
-
/* @__PURE__ */ jsx10("p", { className: "text-og-sm text-og-fg-subtle", children: "Spinning up the desktop \u2014 this takes a few seconds." })
|
|
6320
|
-
] }),
|
|
6321
|
-
onRetry && /* @__PURE__ */ jsx10(
|
|
6322
|
-
"button",
|
|
6323
|
-
{
|
|
6324
|
-
type: "button",
|
|
6325
|
-
onClick: onRetry,
|
|
6326
|
-
className: "rounded-og-sm border border-og-border px-3 py-1.5 text-og-sm text-og-fg-muted transition-colors hover:text-og-fg pointer-coarse:min-h-11",
|
|
6327
|
-
children: "Taking too long? Retry"
|
|
6328
|
-
}
|
|
6329
|
-
)
|
|
6330
|
-
] });
|
|
6451
|
+
function WakeButton({ children, onClick }) {
|
|
6452
|
+
return /* @__PURE__ */ jsx10(
|
|
6453
|
+
"button",
|
|
6454
|
+
{
|
|
6455
|
+
type: "button",
|
|
6456
|
+
onClick,
|
|
6457
|
+
className: "mt-1 inline-flex min-h-11 items-center justify-center rounded-og-md bg-og-accent-deep px-3 py-2 text-og-sm font-medium text-og-accent-fg shadow-sm transition-opacity hover:opacity-90 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-og-accent focus-visible:ring-offset-2 focus-visible:ring-offset-og-bg",
|
|
6458
|
+
children
|
|
6459
|
+
}
|
|
6460
|
+
);
|
|
6331
6461
|
}
|
|
6332
|
-
function
|
|
6333
|
-
|
|
6334
|
-
|
|
6335
|
-
/* @__PURE__ */ jsxs8("p", { className: "mb-3 text-og-sm text-og-fg-subtle", children: [
|
|
6336
|
-
"The desktop pixel stream is ",
|
|
6337
|
-
/* @__PURE__ */ jsx10("strong", { children: "un-redacted" }),
|
|
6338
|
-
" \u2014 it can show secrets the agent prints on screen.",
|
|
6339
|
-
shared ? " This sandbox is shared: you'll also see other sessions' agents on the same screen." : ""
|
|
6340
|
-
] }),
|
|
6462
|
+
function GitHeader({ git, dirtyCount }) {
|
|
6463
|
+
const dirty = dirtyCount > 0;
|
|
6464
|
+
return /* @__PURE__ */ jsxs8("div", { className: "flex shrink-0 items-center gap-2 border-b border-og-border bg-og-surface-1 px-2 py-1 text-og-sm", children: [
|
|
6341
6465
|
/* @__PURE__ */ jsx10(
|
|
6342
|
-
"
|
|
6466
|
+
"span",
|
|
6343
6467
|
{
|
|
6344
|
-
|
|
6345
|
-
|
|
6346
|
-
|
|
6347
|
-
|
|
6468
|
+
"aria-hidden": "true",
|
|
6469
|
+
className: cn(
|
|
6470
|
+
"size-2 shrink-0 rounded-full",
|
|
6471
|
+
dirty ? "bg-og-status-running" : "bg-og-status-idle"
|
|
6472
|
+
)
|
|
6348
6473
|
}
|
|
6349
|
-
)
|
|
6350
|
-
|
|
6351
|
-
}
|
|
6352
|
-
|
|
6353
|
-
|
|
6354
|
-
|
|
6355
|
-
|
|
6356
|
-
|
|
6474
|
+
),
|
|
6475
|
+
/* @__PURE__ */ jsx10("span", { className: "sr-only", children: dirty ? `Working tree has ${dirtyCount} changed files` : "Working tree clean" }),
|
|
6476
|
+
/* @__PURE__ */ jsx10("span", { className: "truncate font-og-mono text-og-fg", children: git.repoCount > 1 ? `${git.repoCount} repositories` : git.branch ?? (git.isRepo ? "(detached)" : "no repo") }),
|
|
6477
|
+
(git.ahead > 0 || git.behind > 0) && /* @__PURE__ */ jsxs8("span", { className: "flex shrink-0 items-center gap-1.5 text-og-xs text-og-fg-subtle", children: [
|
|
6478
|
+
git.ahead > 0 && /* @__PURE__ */ jsxs8("span", { children: [
|
|
6479
|
+
"\u2191",
|
|
6480
|
+
git.ahead
|
|
6481
|
+
] }),
|
|
6482
|
+
git.behind > 0 && /* @__PURE__ */ jsxs8("span", { children: [
|
|
6483
|
+
"\u2193",
|
|
6484
|
+
git.behind
|
|
6485
|
+
] })
|
|
6357
6486
|
] }),
|
|
6358
|
-
/* @__PURE__ */
|
|
6359
|
-
|
|
6360
|
-
"button",
|
|
6487
|
+
dirty && /* @__PURE__ */ jsxs8(
|
|
6488
|
+
"span",
|
|
6361
6489
|
{
|
|
6362
|
-
|
|
6363
|
-
|
|
6364
|
-
className: "
|
|
6365
|
-
children:
|
|
6490
|
+
"aria-hidden": "true",
|
|
6491
|
+
"data-contrast-audited": true,
|
|
6492
|
+
className: "ml-auto shrink-0 text-og-xs text-og-fg-subtle",
|
|
6493
|
+
children: [
|
|
6494
|
+
dirtyCount,
|
|
6495
|
+
" changed"
|
|
6496
|
+
]
|
|
6366
6497
|
}
|
|
6367
6498
|
)
|
|
6368
6499
|
] });
|
|
6369
6500
|
}
|
|
6370
|
-
|
|
6371
|
-
|
|
6372
|
-
|
|
6373
|
-
|
|
6374
|
-
|
|
6375
|
-
|
|
6376
|
-
|
|
6377
|
-
|
|
6378
|
-
|
|
6379
|
-
|
|
6380
|
-
|
|
6381
|
-
|
|
6382
|
-
|
|
6383
|
-
|
|
6384
|
-
|
|
6385
|
-
|
|
6386
|
-
|
|
6387
|
-
|
|
6388
|
-
Group,
|
|
6389
|
-
Panel,
|
|
6390
|
-
Separator,
|
|
6391
|
-
useDefaultLayout,
|
|
6392
|
-
usePanelRef
|
|
6393
|
-
} from "react-resizable-panels";
|
|
6394
|
-
import { Fragment as Fragment4, jsx as jsx11, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
6395
|
-
var SERVER_LAYOUT_STORAGE = {
|
|
6396
|
-
getItem: () => null,
|
|
6397
|
-
setItem: () => {
|
|
6398
|
-
}
|
|
6399
|
-
};
|
|
6400
|
-
function getLayoutStorage() {
|
|
6401
|
-
if (typeof window === "undefined") return SERVER_LAYOUT_STORAGE;
|
|
6402
|
-
try {
|
|
6403
|
-
return window.localStorage;
|
|
6404
|
-
} catch {
|
|
6405
|
-
return SERVER_LAYOUT_STORAGE;
|
|
6406
|
-
}
|
|
6501
|
+
function Segmented({
|
|
6502
|
+
options,
|
|
6503
|
+
value,
|
|
6504
|
+
onChange
|
|
6505
|
+
}) {
|
|
6506
|
+
return /* @__PURE__ */ jsx10("div", { className: "flex flex-wrap items-center rounded-og-sm border border-og-border p-0.5", children: options.map((opt) => /* @__PURE__ */ jsx10(
|
|
6507
|
+
"button",
|
|
6508
|
+
{
|
|
6509
|
+
type: "button",
|
|
6510
|
+
onClick: () => onChange(opt.value),
|
|
6511
|
+
className: cn(
|
|
6512
|
+
"min-h-7 rounded-og-xs px-1.5 py-0.5 text-og-xs focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-og-accent max-[1023px]:min-h-11 max-[1023px]:min-w-11 pointer-coarse:min-h-11 pointer-coarse:min-w-11",
|
|
6513
|
+
opt.value === value ? "bg-og-accent-soft text-og-fg" : "text-og-fg-subtle hover:text-og-fg"
|
|
6514
|
+
),
|
|
6515
|
+
children: opt.label
|
|
6516
|
+
},
|
|
6517
|
+
opt.value
|
|
6518
|
+
)) });
|
|
6407
6519
|
}
|
|
6408
|
-
|
|
6409
|
-
|
|
6410
|
-
|
|
6411
|
-
|
|
6412
|
-
|
|
6413
|
-
|
|
6520
|
+
function useFileView(path, readFile, reloadRevision = 0) {
|
|
6521
|
+
const [state, setState] = useState17({
|
|
6522
|
+
content: null,
|
|
6523
|
+
isBinary: false,
|
|
6524
|
+
truncated: false,
|
|
6525
|
+
sizeBytes: null,
|
|
6526
|
+
loading: false,
|
|
6527
|
+
error: null
|
|
6528
|
+
});
|
|
6529
|
+
useEffect20(() => {
|
|
6530
|
+
if (!path) {
|
|
6531
|
+
setState({
|
|
6532
|
+
content: null,
|
|
6533
|
+
isBinary: false,
|
|
6534
|
+
truncated: false,
|
|
6535
|
+
sizeBytes: null,
|
|
6536
|
+
loading: false,
|
|
6537
|
+
error: null
|
|
6538
|
+
});
|
|
6414
6539
|
return;
|
|
6415
6540
|
}
|
|
6416
|
-
|
|
6417
|
-
const
|
|
6418
|
-
|
|
6419
|
-
|
|
6420
|
-
|
|
6421
|
-
|
|
6541
|
+
let cancelled = false;
|
|
6542
|
+
const abort = new AbortController();
|
|
6543
|
+
setState({
|
|
6544
|
+
content: null,
|
|
6545
|
+
isBinary: false,
|
|
6546
|
+
truncated: false,
|
|
6547
|
+
sizeBytes: null,
|
|
6548
|
+
loading: true,
|
|
6549
|
+
error: null
|
|
6550
|
+
});
|
|
6551
|
+
void readFile(path, { signal: abort.signal }).then((res) => {
|
|
6552
|
+
if (cancelled) return;
|
|
6553
|
+
const content = res.isBinary ? null : res.encoding === "base64" ? decodeBase64Utf8(res.content) : res.content;
|
|
6554
|
+
setState({
|
|
6555
|
+
content,
|
|
6556
|
+
isBinary: res.isBinary,
|
|
6557
|
+
truncated: res.truncated,
|
|
6558
|
+
sizeBytes: res.sizeBytes,
|
|
6559
|
+
loading: false,
|
|
6560
|
+
error: null
|
|
6561
|
+
});
|
|
6562
|
+
}).catch((cause) => {
|
|
6563
|
+
if (cancelled) return;
|
|
6564
|
+
setState({
|
|
6565
|
+
content: null,
|
|
6566
|
+
isBinary: false,
|
|
6567
|
+
truncated: false,
|
|
6568
|
+
sizeBytes: null,
|
|
6569
|
+
loading: false,
|
|
6570
|
+
error: cause instanceof Error ? cause : new Error(String(cause))
|
|
6571
|
+
});
|
|
6572
|
+
});
|
|
6573
|
+
return () => {
|
|
6574
|
+
cancelled = true;
|
|
6575
|
+
abort.abort();
|
|
6576
|
+
};
|
|
6577
|
+
}, [path, readFile, reloadRevision]);
|
|
6578
|
+
return state;
|
|
6579
|
+
}
|
|
6580
|
+
function decodeBase64Utf8(b64) {
|
|
6581
|
+
try {
|
|
6582
|
+
if (typeof atob === "function") {
|
|
6583
|
+
const binary = atob(b64);
|
|
6584
|
+
const bytes = new Uint8Array(binary.length);
|
|
6585
|
+
for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
|
|
6586
|
+
return new TextDecoder().decode(bytes);
|
|
6422
6587
|
}
|
|
6423
|
-
|
|
6424
|
-
|
|
6425
|
-
|
|
6426
|
-
return narrow;
|
|
6588
|
+
} catch {
|
|
6589
|
+
}
|
|
6590
|
+
return b64;
|
|
6427
6591
|
}
|
|
6428
|
-
function
|
|
6429
|
-
|
|
6430
|
-
|
|
6431
|
-
|
|
6432
|
-
|
|
6433
|
-
|
|
6434
|
-
onCollapsedChange,
|
|
6435
|
-
headerAccessory,
|
|
6436
|
-
mobileLeadingControl,
|
|
6437
|
-
autoSaveId = "og.session.dock",
|
|
6438
|
-
defaultSize = 34,
|
|
6439
|
-
minSize = 22,
|
|
6440
|
-
maxSize = 70,
|
|
6441
|
-
className
|
|
6592
|
+
function Notice({
|
|
6593
|
+
children,
|
|
6594
|
+
className,
|
|
6595
|
+
icon,
|
|
6596
|
+
title,
|
|
6597
|
+
announce
|
|
6442
6598
|
}) {
|
|
6443
|
-
|
|
6444
|
-
|
|
6445
|
-
|
|
6446
|
-
|
|
6447
|
-
|
|
6448
|
-
|
|
6449
|
-
|
|
6450
|
-
|
|
6451
|
-
|
|
6452
|
-
|
|
6453
|
-
|
|
6454
|
-
|
|
6455
|
-
|
|
6456
|
-
|
|
6457
|
-
|
|
6458
|
-
|
|
6459
|
-
});
|
|
6460
|
-
const requestedTab = activeTab ?? internalTab;
|
|
6461
|
-
const tabIds = tabs.map((tab) => tab.id);
|
|
6462
|
-
const firstTabId = tabs[0]?.id ?? "";
|
|
6463
|
-
const requestedTabIsValid = tabIds.includes(requestedTab);
|
|
6464
|
-
const current = requestedTabIsValid ? requestedTab : firstTabId;
|
|
6465
|
-
const setTab = useCallback19(
|
|
6466
|
-
(id) => {
|
|
6467
|
-
setInternalTab(id);
|
|
6468
|
-
onActiveTabChange?.(id);
|
|
6469
|
-
},
|
|
6470
|
-
[onActiveTabChange]
|
|
6471
|
-
);
|
|
6472
|
-
const setCollapsed = useCallback19(
|
|
6473
|
-
(next) => {
|
|
6474
|
-
setInternalCollapsed((previous) => previous === next ? previous : next);
|
|
6475
|
-
onCollapsedChange?.(next);
|
|
6476
|
-
},
|
|
6477
|
-
[onCollapsedChange]
|
|
6599
|
+
return /* @__PURE__ */ jsx10(
|
|
6600
|
+
"div",
|
|
6601
|
+
{
|
|
6602
|
+
role: announce,
|
|
6603
|
+
"aria-atomic": announce ? "true" : void 0,
|
|
6604
|
+
"aria-live": announce === "alert" ? "assertive" : announce === "status" ? "polite" : void 0,
|
|
6605
|
+
className: cn(
|
|
6606
|
+
"flex h-full items-center justify-center p-4 text-center text-og-sm text-og-fg-subtle",
|
|
6607
|
+
className
|
|
6608
|
+
),
|
|
6609
|
+
children: /* @__PURE__ */ jsxs8("div", { className: "flex max-w-sm flex-col items-center gap-2.5", children: [
|
|
6610
|
+
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,
|
|
6611
|
+
title ? /* @__PURE__ */ jsx10("p", { className: "font-medium text-og-fg", children: title }) : null,
|
|
6612
|
+
/* @__PURE__ */ jsx10("div", { className: "leading-5", children })
|
|
6613
|
+
] })
|
|
6614
|
+
}
|
|
6478
6615
|
);
|
|
6479
|
-
|
|
6480
|
-
|
|
6481
|
-
|
|
6482
|
-
|
|
6483
|
-
|
|
6484
|
-
|
|
6485
|
-
|
|
6486
|
-
|
|
6487
|
-
|
|
6488
|
-
|
|
6489
|
-
|
|
6490
|
-
|
|
6491
|
-
|
|
6492
|
-
|
|
6493
|
-
|
|
6494
|
-
|
|
6495
|
-
|
|
6496
|
-
|
|
6497
|
-
|
|
6498
|
-
|
|
6499
|
-
|
|
6500
|
-
|
|
6501
|
-
|
|
6502
|
-
|
|
6503
|
-
|
|
6504
|
-
|
|
6505
|
-
|
|
6506
|
-
|
|
6507
|
-
|
|
6508
|
-
|
|
6509
|
-
|
|
6510
|
-
|
|
6511
|
-
|
|
6512
|
-
|
|
6513
|
-
|
|
6514
|
-
|
|
6515
|
-
|
|
6516
|
-
|
|
6517
|
-
|
|
6518
|
-
|
|
6519
|
-
|
|
6520
|
-
|
|
6521
|
-
|
|
6522
|
-
|
|
6523
|
-
|
|
6524
|
-
|
|
6525
|
-
|
|
6526
|
-
|
|
6527
|
-
|
|
6528
|
-
|
|
6529
|
-
|
|
6530
|
-
|
|
6531
|
-
|
|
6532
|
-
|
|
6533
|
-
|
|
6534
|
-
|
|
6535
|
-
|
|
6536
|
-
}
|
|
6537
|
-
}, [firstTabId, requestedTabIsValid, setTab]);
|
|
6538
|
-
const collapse = useCallback19(() => {
|
|
6539
|
-
dockPanelRef.current?.collapse();
|
|
6540
|
-
setCollapsed(true);
|
|
6541
|
-
}, [dockPanelRef, setCollapsed]);
|
|
6542
|
-
const expand = useCallback19(() => {
|
|
6543
|
-
dockPanelRef.current?.expand();
|
|
6544
|
-
setCollapsed(false);
|
|
6545
|
-
}, [dockPanelRef, setCollapsed]);
|
|
6546
|
-
useEffect20(() => {
|
|
6547
|
-
const overlayOpen = maximized || narrow && !collapsed;
|
|
6548
|
-
if (!overlayOpen) return;
|
|
6616
|
+
}
|
|
6617
|
+
|
|
6618
|
+
// src/components/desktop-viewer.tsx
|
|
6619
|
+
import { LoaderCircleIcon as LoaderCircleIcon2, MonitorIcon, MousePointerClickIcon, WifiOffIcon } from "lucide-react";
|
|
6620
|
+
import { useEffect as useEffect21, useRef as useRef18, useState as useState18 } from "react";
|
|
6621
|
+
import { jsx as jsx11, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
6622
|
+
var DESKTOP_MEDIA_BACKGROUND = "#000";
|
|
6623
|
+
function isHardUnavailable(reason) {
|
|
6624
|
+
switch (reason) {
|
|
6625
|
+
case "backend_unsupported":
|
|
6626
|
+
case "os_unsupported":
|
|
6627
|
+
case "not_provisioned":
|
|
6628
|
+
case "disabled_by_policy":
|
|
6629
|
+
case "tier_headless":
|
|
6630
|
+
return true;
|
|
6631
|
+
default:
|
|
6632
|
+
return false;
|
|
6633
|
+
}
|
|
6634
|
+
}
|
|
6635
|
+
function DesktopViewer({
|
|
6636
|
+
capability,
|
|
6637
|
+
interactive,
|
|
6638
|
+
showControlToggle = true,
|
|
6639
|
+
scaleViewport,
|
|
6640
|
+
rfbFactory,
|
|
6641
|
+
webSocketFactory,
|
|
6642
|
+
renderConsentGate,
|
|
6643
|
+
onAcknowledge,
|
|
6644
|
+
watching,
|
|
6645
|
+
onWarm,
|
|
6646
|
+
renderUnavailable,
|
|
6647
|
+
renderWarming,
|
|
6648
|
+
renderViewerCap,
|
|
6649
|
+
viewerCapReached,
|
|
6650
|
+
connectTimeoutMs = 13e3,
|
|
6651
|
+
className
|
|
6652
|
+
}) {
|
|
6653
|
+
const containerRef = useRef18(null);
|
|
6654
|
+
const [consented, setConsented] = useState18(false);
|
|
6655
|
+
const [takeControl, setTakeControl] = useState18(interactive ?? false);
|
|
6656
|
+
const externallyControlled = interactive !== void 0;
|
|
6657
|
+
const serverAllowsControl = capability?.mode !== "read-only";
|
|
6658
|
+
const wantControl = externallyControlled ? interactive : takeControl;
|
|
6659
|
+
const inControl = Boolean(wantControl) && serverAllowsControl;
|
|
6660
|
+
const isWatching = watching ?? consented;
|
|
6661
|
+
const transportNull = !capability || capability.transport === null;
|
|
6662
|
+
const reason = capability?.reason ?? null;
|
|
6663
|
+
const needsAck = capability?.requiresAcknowledgment === true && capability.acknowledged !== true && !consented;
|
|
6664
|
+
const noLiveAddress = Boolean(capability) && !transportNull && !capability.url;
|
|
6665
|
+
const coldWarmable = transportNull && reason === "lease_cold" || !transportNull && noLiveAddress;
|
|
6666
|
+
const hardUnavailable = transportNull && isHardUnavailable(reason);
|
|
6667
|
+
const accept = () => {
|
|
6668
|
+
setConsented(true);
|
|
6669
|
+
onAcknowledge?.();
|
|
6670
|
+
};
|
|
6671
|
+
useEffect21(() => {
|
|
6672
|
+
if (!inControl || externallyControlled) return;
|
|
6549
6673
|
const onKey = (event) => {
|
|
6550
|
-
if (event.key === "
|
|
6551
|
-
if (maximized) setMaximized(false);
|
|
6552
|
-
else collapse();
|
|
6553
|
-
return;
|
|
6554
|
-
}
|
|
6555
|
-
if (event.key !== "Tab") return;
|
|
6556
|
-
const surface = rootRef.current?.querySelector(
|
|
6557
|
-
narrow ? '[role="dialog"]' : "[data-workspace-surface]"
|
|
6558
|
-
);
|
|
6559
|
-
if (!surface) return;
|
|
6560
|
-
const focusable = Array.from(surface.querySelectorAll("*")).filter(
|
|
6561
|
-
(element) => element.tabIndex >= 0 && !element.hasAttribute("disabled") && element.getClientRects().length > 0 && getComputedStyle(element).visibility !== "hidden"
|
|
6562
|
-
);
|
|
6563
|
-
const first = focusable[0];
|
|
6564
|
-
const last = focusable.at(-1);
|
|
6565
|
-
const active = document.activeElement;
|
|
6566
|
-
if (!first || !last) {
|
|
6567
|
-
event.preventDefault();
|
|
6568
|
-
surface.focus();
|
|
6569
|
-
return;
|
|
6570
|
-
}
|
|
6571
|
-
if (!surface.contains(active)) {
|
|
6572
|
-
event.preventDefault();
|
|
6573
|
-
(event.shiftKey ? last : first).focus();
|
|
6574
|
-
return;
|
|
6575
|
-
}
|
|
6576
|
-
if (event.shiftKey && active === first || !event.shiftKey && active === last) {
|
|
6674
|
+
if (event.ctrlKey && event.altKey && event.shiftKey && (event.key === "Control" || event.key === "Alt" || event.key === "Shift")) {
|
|
6577
6675
|
event.preventDefault();
|
|
6578
|
-
(
|
|
6579
|
-
return;
|
|
6676
|
+
setTakeControl(false);
|
|
6580
6677
|
}
|
|
6581
|
-
requestAnimationFrame(() => {
|
|
6582
|
-
const next = document.activeElement;
|
|
6583
|
-
if (!surface.contains(next)) (event.shiftKey ? last : first).focus();
|
|
6584
|
-
});
|
|
6585
6678
|
};
|
|
6586
6679
|
window.addEventListener("keydown", onKey);
|
|
6587
|
-
return () =>
|
|
6588
|
-
|
|
6589
|
-
|
|
6590
|
-
|
|
6591
|
-
|
|
6592
|
-
|
|
6593
|
-
|
|
6594
|
-
|
|
6595
|
-
|
|
6596
|
-
|
|
6597
|
-
|
|
6598
|
-
|
|
6599
|
-
|
|
6600
|
-
|
|
6601
|
-
|
|
6602
|
-
|
|
6603
|
-
|
|
6604
|
-
|
|
6605
|
-
|
|
6606
|
-
|
|
6607
|
-
|
|
6608
|
-
|
|
6609
|
-
|
|
6610
|
-
|
|
6611
|
-
|
|
6612
|
-
|
|
6613
|
-
|
|
6614
|
-
|
|
6615
|
-
|
|
6616
|
-
|
|
6617
|
-
|
|
6618
|
-
|
|
6619
|
-
|
|
6620
|
-
|
|
6621
|
-
|
|
6622
|
-
|
|
6623
|
-
|
|
6624
|
-
|
|
6625
|
-
|
|
6626
|
-
|
|
6627
|
-
|
|
6628
|
-
|
|
6629
|
-
|
|
6630
|
-
|
|
6631
|
-
|
|
6632
|
-
|
|
6633
|
-
|
|
6634
|
-
|
|
6635
|
-
|
|
6636
|
-
|
|
6637
|
-
|
|
6638
|
-
|
|
6639
|
-
|
|
6640
|
-
|
|
6680
|
+
return () => {
|
|
6681
|
+
window.removeEventListener("keydown", onKey);
|
|
6682
|
+
};
|
|
6683
|
+
}, [inControl, externallyControlled]);
|
|
6684
|
+
const connectCapability = !transportNull && !needsAck && !viewerCapReached ? capability : null;
|
|
6685
|
+
const stream = useDesktopStream({
|
|
6686
|
+
capability: connectCapability,
|
|
6687
|
+
containerRef,
|
|
6688
|
+
interactive: inControl,
|
|
6689
|
+
...scaleViewport !== void 0 ? { scaleViewport } : {},
|
|
6690
|
+
...rfbFactory ? { rfbFactory } : {},
|
|
6691
|
+
...webSocketFactory ? { webSocketFactory } : {}
|
|
6692
|
+
});
|
|
6693
|
+
const connected = stream.state === "connected";
|
|
6694
|
+
const hasLiveUrl = Boolean(connectCapability?.url);
|
|
6695
|
+
const streamStateRef = useRef18(stream.state);
|
|
6696
|
+
streamStateRef.current = stream.state;
|
|
6697
|
+
const warmKeyRef = useRef18(null);
|
|
6698
|
+
useEffect21(() => {
|
|
6699
|
+
if (!isWatching || !coldWarmable || needsAck || viewerCapReached) {
|
|
6700
|
+
if (!coldWarmable) warmKeyRef.current = null;
|
|
6701
|
+
return;
|
|
6702
|
+
}
|
|
6703
|
+
const warm = onWarm ?? onAcknowledge;
|
|
6704
|
+
if (!warm) return;
|
|
6705
|
+
const key = `${capability?.reason ?? ""}:${capability?.url ?? ""}:${capability?.expiresAt ?? ""}`;
|
|
6706
|
+
if (warmKeyRef.current === key) return;
|
|
6707
|
+
warmKeyRef.current = key;
|
|
6708
|
+
warm();
|
|
6709
|
+
}, [isWatching, coldWarmable, needsAck, viewerCapReached, capability, onWarm, onAcknowledge]);
|
|
6710
|
+
useEffect21(() => {
|
|
6711
|
+
if (!hasLiveUrl || typeof document === "undefined") return;
|
|
6712
|
+
const maybeReattach = () => {
|
|
6713
|
+
if (document.visibilityState !== "visible") return;
|
|
6714
|
+
if (streamStateRef.current === "idle") stream.reconnect();
|
|
6715
|
+
};
|
|
6716
|
+
document.addEventListener("visibilitychange", maybeReattach);
|
|
6717
|
+
return () => document.removeEventListener("visibilitychange", maybeReattach);
|
|
6718
|
+
}, [hasLiveUrl, connectCapability?.url]);
|
|
6719
|
+
const [connectTimedOut, setConnectTimedOut] = useState18(false);
|
|
6720
|
+
const [reconnectNonce, setReconnectNonce] = useState18(0);
|
|
6721
|
+
useEffect21(() => {
|
|
6722
|
+
setConnectTimedOut(false);
|
|
6723
|
+
if (!hasLiveUrl || connected || stream.error || connectTimeoutMs <= 0) return;
|
|
6724
|
+
const timer = setTimeout(() => setConnectTimedOut(true), connectTimeoutMs);
|
|
6725
|
+
return () => clearTimeout(timer);
|
|
6726
|
+
}, [
|
|
6727
|
+
hasLiveUrl,
|
|
6728
|
+
connected,
|
|
6729
|
+
connectTimeoutMs,
|
|
6730
|
+
connectCapability?.url,
|
|
6731
|
+
stream.error,
|
|
6732
|
+
reconnectNonce
|
|
6733
|
+
]);
|
|
6734
|
+
const reconnect = () => {
|
|
6735
|
+
setConnectTimedOut(false);
|
|
6736
|
+
setReconnectNonce((n) => n + 1);
|
|
6737
|
+
stream.reconnect();
|
|
6738
|
+
};
|
|
6739
|
+
let uiState;
|
|
6740
|
+
if (viewerCapReached) uiState = "viewer_cap";
|
|
6741
|
+
else if (hardUnavailable) uiState = "unavailable";
|
|
6742
|
+
else if (needsAck && !isWatching) uiState = "consent";
|
|
6743
|
+
else if (coldWarmable) uiState = "warming";
|
|
6744
|
+
else if (connected) uiState = "connected";
|
|
6745
|
+
else if (stream.error) uiState = "error";
|
|
6746
|
+
else if (connectTimedOut) uiState = "connect_failed";
|
|
6747
|
+
else uiState = "connecting";
|
|
6748
|
+
const overlayShown = uiState !== "connected" && uiState !== "connecting";
|
|
6749
|
+
const showToggle = showControlToggle && !overlayShown;
|
|
6750
|
+
let overlay = null;
|
|
6751
|
+
switch (uiState) {
|
|
6752
|
+
case "viewer_cap":
|
|
6753
|
+
overlay = renderViewerCap?.() ?? defaultNotice(
|
|
6754
|
+
"Too many viewers",
|
|
6755
|
+
"This session has reached its live-viewer limit. Try again shortly."
|
|
6756
|
+
);
|
|
6757
|
+
break;
|
|
6758
|
+
case "unavailable":
|
|
6759
|
+
overlay = renderUnavailable?.(reason) ?? defaultNotice("Desktop unavailable", unavailableCopy(reason));
|
|
6760
|
+
break;
|
|
6761
|
+
case "consent":
|
|
6762
|
+
overlay = renderConsentGate ? renderConsentGate(accept, capability?.shared ?? false) : /* @__PURE__ */ jsx11(DefaultConsentGate, { shared: capability?.shared ?? false, onAccept: accept });
|
|
6763
|
+
break;
|
|
6764
|
+
case "warming":
|
|
6765
|
+
overlay = renderWarming?.() ?? /* @__PURE__ */ jsx11(WarmingNotice, {});
|
|
6766
|
+
break;
|
|
6767
|
+
case "connect_failed":
|
|
6768
|
+
overlay = defaultNotice(
|
|
6769
|
+
"Couldn\u2019t connect",
|
|
6770
|
+
"The desktop is warm but the live stream didn\u2019t come up. This usually clears on a retry.",
|
|
6771
|
+
reconnect
|
|
6772
|
+
);
|
|
6773
|
+
break;
|
|
6774
|
+
case "error":
|
|
6775
|
+
overlay = defaultNotice(
|
|
6776
|
+
"Desktop disconnected",
|
|
6777
|
+
stream.error?.message ?? "The stream dropped.",
|
|
6778
|
+
reconnect
|
|
6779
|
+
);
|
|
6780
|
+
break;
|
|
6781
|
+
case "connecting":
|
|
6782
|
+
case "connected":
|
|
6783
|
+
overlay = null;
|
|
6784
|
+
break;
|
|
6641
6785
|
}
|
|
6642
|
-
|
|
6643
|
-
|
|
6786
|
+
return /* @__PURE__ */ jsxs9(
|
|
6787
|
+
"div",
|
|
6644
6788
|
{
|
|
6645
|
-
|
|
6646
|
-
|
|
6647
|
-
|
|
6648
|
-
|
|
6649
|
-
|
|
6789
|
+
className: cn(
|
|
6790
|
+
"relative h-full w-full overflow-hidden",
|
|
6791
|
+
inControl && "ring-2 ring-inset ring-og-accent",
|
|
6792
|
+
className
|
|
6793
|
+
),
|
|
6794
|
+
style: { backgroundColor: DESKTOP_MEDIA_BACKGROUND },
|
|
6795
|
+
"data-opengeni-desktop": true,
|
|
6796
|
+
"data-state": stream.state,
|
|
6797
|
+
"data-ui-state": uiState,
|
|
6798
|
+
"data-in-control": inControl ? "true" : void 0,
|
|
6799
|
+
children: [
|
|
6650
6800
|
/* @__PURE__ */ jsx11(
|
|
6651
|
-
|
|
6801
|
+
"div",
|
|
6652
6802
|
{
|
|
6653
|
-
|
|
6654
|
-
|
|
6655
|
-
|
|
6656
|
-
|
|
6803
|
+
ref: containerRef,
|
|
6804
|
+
className: "absolute inset-0 overflow-hidden",
|
|
6805
|
+
"data-opengeni-desktop-canvas": true,
|
|
6806
|
+
"data-state": stream.state
|
|
6657
6807
|
}
|
|
6658
6808
|
),
|
|
6659
|
-
|
|
6660
|
-
|
|
6809
|
+
uiState === "connecting" && /* @__PURE__ */ jsxs9("div", { className: "pointer-events-none absolute inset-0 flex flex-col items-center justify-center gap-3 text-og-fg-subtle", children: [
|
|
6810
|
+
/* @__PURE__ */ jsxs9("span", { className: "relative flex items-center justify-center", children: [
|
|
6811
|
+
/* @__PURE__ */ jsx11(MonitorIcon, { className: "size-8 opacity-30", strokeWidth: 1.5 }),
|
|
6812
|
+
/* @__PURE__ */ jsx11(
|
|
6813
|
+
LoaderCircleIcon2,
|
|
6814
|
+
{
|
|
6815
|
+
className: "absolute size-12 animate-og-spin text-og-accent opacity-70",
|
|
6816
|
+
strokeWidth: 1.25
|
|
6817
|
+
}
|
|
6818
|
+
)
|
|
6819
|
+
] }),
|
|
6820
|
+
/* @__PURE__ */ jsx11("span", { className: "text-og-sm", children: "Connecting to the desktop\u2026" })
|
|
6821
|
+
] }),
|
|
6822
|
+
showToggle && !externallyControlled && inControl && /* @__PURE__ */ jsx11(
|
|
6823
|
+
InControlBar,
|
|
6824
|
+
{
|
|
6825
|
+
shared: capability?.shared ?? false,
|
|
6826
|
+
onRelease: () => setTakeControl(false)
|
|
6827
|
+
}
|
|
6828
|
+
),
|
|
6829
|
+
showToggle && !externallyControlled && !inControl && connected && /* @__PURE__ */ jsx11(
|
|
6830
|
+
TakeControlCallToAction,
|
|
6831
|
+
{
|
|
6832
|
+
disabled: !serverAllowsControl,
|
|
6833
|
+
disabledReason: !serverAllowsControl ? capability?.client === "frames" ? "View-only \u2014 live control isn't available for this machine yet." : "This deployment streams the desktop read-only." : void 0,
|
|
6834
|
+
onTakeControl: () => setTakeControl(true)
|
|
6835
|
+
}
|
|
6836
|
+
),
|
|
6837
|
+
overlay && /* @__PURE__ */ jsx11("div", { className: "absolute inset-0 flex items-center justify-center p-4", children: overlay })
|
|
6838
|
+
]
|
|
6661
6839
|
}
|
|
6662
6840
|
);
|
|
6663
|
-
|
|
6664
|
-
|
|
6665
|
-
|
|
6841
|
+
}
|
|
6842
|
+
function TakeControlCallToAction({
|
|
6843
|
+
disabled,
|
|
6844
|
+
disabledReason,
|
|
6845
|
+
onTakeControl
|
|
6846
|
+
}) {
|
|
6847
|
+
return (
|
|
6848
|
+
// The wrapper spans the surface but is click-through (pointer-events-none); only
|
|
6849
|
+
// the button itself is interactive, so watchers can still see the desktop.
|
|
6850
|
+
/* @__PURE__ */ jsx11("div", { className: "pointer-events-none absolute inset-0 flex items-end justify-center pb-[8%]", children: /* @__PURE__ */ jsxs9(
|
|
6851
|
+
"button",
|
|
6666
6852
|
{
|
|
6667
|
-
|
|
6668
|
-
|
|
6669
|
-
|
|
6670
|
-
|
|
6853
|
+
type: "button",
|
|
6854
|
+
disabled,
|
|
6855
|
+
"aria-label": "Take control of the desktop",
|
|
6856
|
+
title: disabled ? disabledReason : "Take control of the desktop",
|
|
6857
|
+
onClick: onTakeControl,
|
|
6858
|
+
className: cn(
|
|
6859
|
+
"group pointer-events-auto flex items-center gap-3 rounded-og-lg border px-5 py-3",
|
|
6860
|
+
"border-og-border",
|
|
6861
|
+
"bg-og-bg/85 backdrop-blur-md",
|
|
6862
|
+
"shadow-og-lg",
|
|
6863
|
+
"outline-none transition-all duration-150 ease-out",
|
|
6864
|
+
"focus-visible:ring-2 focus-visible:ring-og-accent focus-visible:ring-offset-2 focus-visible:ring-offset-og-bg",
|
|
6865
|
+
disabled ? "cursor-not-allowed opacity-60" : cn(
|
|
6866
|
+
"cursor-pointer opacity-90 hover:-translate-y-0.5 hover:opacity-100",
|
|
6867
|
+
"hover:border-og-accent",
|
|
6868
|
+
"hover:bg-og-accent/10"
|
|
6869
|
+
)
|
|
6870
|
+
),
|
|
6671
6871
|
children: [
|
|
6672
|
-
/* @__PURE__ */ jsx11(Panel, { id: "primary", minSize: "30%", className: "min-h-0 min-w-0", children: /* @__PURE__ */ jsx11(
|
|
6673
|
-
"div",
|
|
6674
|
-
{
|
|
6675
|
-
"data-workspace-primary": true,
|
|
6676
|
-
"aria-hidden": maximized ? true : void 0,
|
|
6677
|
-
className: "h-full min-h-0 min-w-0",
|
|
6678
|
-
inert: maximized ? true : void 0,
|
|
6679
|
-
children: primary
|
|
6680
|
-
}
|
|
6681
|
-
) }),
|
|
6682
|
-
!collapsed && /* @__PURE__ */ jsx11(Separator, { className: "group relative z-10 w-1.5 shrink-0 outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-og-accent max-[1023px]:-mx-[19px] max-[1023px]:w-11 pointer-coarse:-mx-[19px] pointer-coarse:w-11", children: /* @__PURE__ */ jsx11("span", { className: "absolute inset-y-0 left-1/2 w-px -translate-x-1/2 bg-og-border transition-colors group-hover:bg-og-accent group-focus-visible:bg-og-accent group-data-[separator-state=dragging]:bg-og-accent" }) }),
|
|
6683
6872
|
/* @__PURE__ */ jsx11(
|
|
6684
|
-
|
|
6873
|
+
"span",
|
|
6685
6874
|
{
|
|
6686
|
-
|
|
6687
|
-
|
|
6688
|
-
|
|
6689
|
-
|
|
6690
|
-
|
|
6691
|
-
|
|
6692
|
-
|
|
6693
|
-
onResize: (size, _id, previousSize) => {
|
|
6694
|
-
const isCollapsed = size.asPercentage <= 1;
|
|
6695
|
-
const canInferCollapse = collapsedProp === void 0 || previousSize !== void 0;
|
|
6696
|
-
if (canInferCollapse && isCollapsed !== collapsed) {
|
|
6697
|
-
setCollapsed(isCollapsed);
|
|
6698
|
-
}
|
|
6699
|
-
},
|
|
6700
|
-
className: "min-h-0 min-w-0",
|
|
6701
|
-
children: /* @__PURE__ */ jsx11(
|
|
6702
|
-
"div",
|
|
6703
|
-
{
|
|
6704
|
-
"data-workspace-surface": true,
|
|
6705
|
-
role: maximized ? "dialog" : void 0,
|
|
6706
|
-
"aria-modal": maximized ? true : void 0,
|
|
6707
|
-
"aria-label": maximized ? "Workspace" : void 0,
|
|
6708
|
-
"aria-hidden": collapsed ? true : void 0,
|
|
6709
|
-
className: cn(
|
|
6710
|
-
"flex h-full min-h-0 min-w-0 flex-col bg-og-bg",
|
|
6711
|
-
maximized ? "fixed inset-0 z-40" : "border-l border-og-border",
|
|
6712
|
-
collapsed && "invisible pointer-events-none"
|
|
6713
|
-
),
|
|
6714
|
-
children: dockChrome
|
|
6715
|
-
}
|
|
6716
|
-
)
|
|
6875
|
+
className: cn(
|
|
6876
|
+
"flex size-9 shrink-0 items-center justify-center rounded-full transition-colors",
|
|
6877
|
+
"bg-og-accent",
|
|
6878
|
+
"text-og-accent-fg",
|
|
6879
|
+
disabled ? "" : "group-hover:scale-105"
|
|
6880
|
+
),
|
|
6881
|
+
children: /* @__PURE__ */ jsx11(MousePointerClickIcon, { className: "size-5", strokeWidth: 2 })
|
|
6717
6882
|
}
|
|
6718
|
-
)
|
|
6883
|
+
),
|
|
6884
|
+
/* @__PURE__ */ jsxs9("span", { className: "flex flex-col items-start leading-tight", children: [
|
|
6885
|
+
/* @__PURE__ */ jsx11("span", { className: "text-og-base font-semibold text-og-fg", children: "Take control" }),
|
|
6886
|
+
/* @__PURE__ */ jsx11("span", { className: "text-og-xs text-og-fg-subtle", children: disabled && disabledReason ? disabledReason : "Drive the mouse & keyboard" })
|
|
6887
|
+
] })
|
|
6719
6888
|
]
|
|
6720
6889
|
}
|
|
6721
|
-
)
|
|
6722
|
-
collapsed && !maximized && !hostControlled && /* @__PURE__ */ jsx11(
|
|
6723
|
-
"button",
|
|
6724
|
-
{
|
|
6725
|
-
ref: reopenRef,
|
|
6726
|
-
type: "button",
|
|
6727
|
-
onClick: expand,
|
|
6728
|
-
title: "Open workspace",
|
|
6729
|
-
className: "absolute inset-y-0 right-0 flex w-6 shrink-0 items-center justify-center border-l border-og-border bg-og-surface-1 text-og-fg-subtle hover:text-og-fg",
|
|
6730
|
-
children: /* @__PURE__ */ jsx11(ChevronsLeftRightIcon, { className: "size-3.5" })
|
|
6731
|
-
}
|
|
6732
|
-
)
|
|
6733
|
-
] });
|
|
6734
|
-
}
|
|
6735
|
-
function ChromeButton({
|
|
6736
|
-
onClick,
|
|
6737
|
-
title,
|
|
6738
|
-
label,
|
|
6739
|
-
children
|
|
6740
|
-
}) {
|
|
6741
|
-
return /* @__PURE__ */ jsx11(
|
|
6742
|
-
"button",
|
|
6743
|
-
{
|
|
6744
|
-
type: "button",
|
|
6745
|
-
onClick,
|
|
6746
|
-
title,
|
|
6747
|
-
"aria-label": label,
|
|
6748
|
-
className: "inline-flex size-7 items-center justify-center rounded-og-sm p-1 transition-colors hover:bg-og-surface-2 hover:text-og-fg focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-og-accent max-[1023px]:size-11 pointer-coarse:size-11",
|
|
6749
|
-
children
|
|
6750
|
-
}
|
|
6890
|
+
) })
|
|
6751
6891
|
);
|
|
6752
6892
|
}
|
|
6753
|
-
function
|
|
6754
|
-
|
|
6755
|
-
|
|
6756
|
-
|
|
6757
|
-
|
|
6758
|
-
|
|
6759
|
-
|
|
6760
|
-
|
|
6761
|
-
|
|
6762
|
-
|
|
6763
|
-
|
|
6764
|
-
const tabListRef = useRef17(null);
|
|
6765
|
-
const [visitedTabs, setVisitedTabs] = useState17(() => /* @__PURE__ */ new Set());
|
|
6766
|
-
const [tabOverflow, setTabOverflow] = useState17({ start: false, end: false });
|
|
6767
|
-
const activeId = active?.id ?? "";
|
|
6768
|
-
const activeTabIndex = tabs.findIndex((tab) => tab.id === activeId);
|
|
6769
|
-
const keepActiveTabVisible = useCallback19(() => {
|
|
6770
|
-
const selected = activeTabIndex >= 0 ? tabRefs.current[activeTabIndex] : null;
|
|
6771
|
-
selected?.scrollIntoView({ block: "nearest", inline: "nearest" });
|
|
6772
|
-
}, [activeTabIndex]);
|
|
6773
|
-
useEffect20(() => {
|
|
6774
|
-
if (!activeId) return;
|
|
6775
|
-
setVisitedTabs((previous) => {
|
|
6776
|
-
if (previous.has(activeId)) return previous;
|
|
6777
|
-
const next = new Set(previous);
|
|
6778
|
-
next.add(activeId);
|
|
6779
|
-
return next;
|
|
6780
|
-
});
|
|
6781
|
-
}, [activeId]);
|
|
6782
|
-
useDockLayoutEffect(() => {
|
|
6783
|
-
keepActiveTabVisible();
|
|
6784
|
-
}, [activeId, keepActiveTabVisible]);
|
|
6785
|
-
useDockLayoutEffect(() => {
|
|
6786
|
-
const list = tabListRef.current;
|
|
6787
|
-
if (!list) return;
|
|
6788
|
-
const update = () => {
|
|
6789
|
-
const max = Math.max(0, list.scrollWidth - list.clientWidth);
|
|
6790
|
-
const next = { start: list.scrollLeft > 1, end: list.scrollLeft < max - 1 };
|
|
6791
|
-
setTabOverflow(
|
|
6792
|
-
(previous) => previous.start === next.start && previous.end === next.end ? previous : next
|
|
6793
|
-
);
|
|
6794
|
-
};
|
|
6795
|
-
update();
|
|
6796
|
-
list.addEventListener("scroll", update, { passive: true });
|
|
6797
|
-
const observer = typeof ResizeObserver === "undefined" ? null : new ResizeObserver(() => {
|
|
6798
|
-
keepActiveTabVisible();
|
|
6799
|
-
update();
|
|
6800
|
-
});
|
|
6801
|
-
observer?.observe(list);
|
|
6802
|
-
for (const tab of tabRefs.current) {
|
|
6803
|
-
if (tab) observer?.observe(tab);
|
|
6804
|
-
}
|
|
6805
|
-
return () => {
|
|
6806
|
-
list.removeEventListener("scroll", update);
|
|
6807
|
-
observer?.disconnect();
|
|
6808
|
-
};
|
|
6809
|
-
}, [activeId, keepActiveTabVisible, tabs.length]);
|
|
6810
|
-
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;
|
|
6811
|
-
const activateTab = (index) => {
|
|
6812
|
-
const tab = tabs[index];
|
|
6813
|
-
if (!tab) {
|
|
6814
|
-
return;
|
|
6815
|
-
}
|
|
6816
|
-
onTab(tab.id);
|
|
6817
|
-
tabRefs.current[index]?.focus();
|
|
6818
|
-
};
|
|
6819
|
-
const onTabKeyDown = (event, index) => {
|
|
6820
|
-
if (tabs.length === 0) return;
|
|
6821
|
-
let nextIndex = null;
|
|
6822
|
-
if (event.key === "ArrowRight") nextIndex = (index + 1) % tabs.length;
|
|
6823
|
-
else if (event.key === "ArrowLeft") nextIndex = (index - 1 + tabs.length) % tabs.length;
|
|
6824
|
-
else if (event.key === "Home") nextIndex = 0;
|
|
6825
|
-
else if (event.key === "End") nextIndex = tabs.length - 1;
|
|
6826
|
-
if (nextIndex === null) return;
|
|
6827
|
-
event.preventDefault();
|
|
6828
|
-
activateTab(nextIndex);
|
|
6829
|
-
};
|
|
6830
|
-
return /* @__PURE__ */ jsxs9(Fragment4, { children: [
|
|
6831
|
-
/* @__PURE__ */ jsxs9("div", { className: "grid shrink-0 grid-cols-[auto_minmax(0,1fr)_auto_auto] items-center gap-x-2 border-b border-og-border px-1.5 py-1 max-[1023px]:grid-cols-[auto_minmax(0,1fr)_auto] max-[1023px]:gap-y-0 max-[1023px]:px-2 max-[1023px]:pb-1 max-[1023px]:pt-0 min-[640px]:max-[1023px]:grid-cols-[auto_minmax(0,1fr)_auto_auto]", children: [
|
|
6832
|
-
/* @__PURE__ */ jsx11("div", { className: "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", children: leading ?? /* @__PURE__ */ jsx11("span", { className: "hidden truncate px-1 text-og-sm font-semibold text-og-fg max-[1023px]:inline", children: "Workspace" }) }),
|
|
6833
|
-
/* @__PURE__ */ jsx11(
|
|
6834
|
-
"div",
|
|
6893
|
+
function InControlBar({ shared, onRelease }) {
|
|
6894
|
+
return /* @__PURE__ */ jsxs9("div", { className: "pointer-events-none absolute inset-x-0 top-0 flex items-center justify-between gap-2 p-2", children: [
|
|
6895
|
+
/* @__PURE__ */ jsxs9("span", { className: "pointer-events-auto inline-flex items-center gap-2 rounded-og-sm bg-og-accent px-2.5 py-1 text-og-xs font-medium text-og-accent-fg shadow-og-md", children: [
|
|
6896
|
+
/* @__PURE__ */ jsx11("span", { className: "size-1.5 animate-pulse rounded-full bg-current", "aria-hidden": true }),
|
|
6897
|
+
"You're in control",
|
|
6898
|
+
/* @__PURE__ */ jsx11("span", { className: "opacity-75", children: "\xB7 click Return control (or Ctrl+Alt+Shift)" })
|
|
6899
|
+
] }),
|
|
6900
|
+
/* @__PURE__ */ jsxs9("div", { className: "pointer-events-auto flex items-center gap-1.5", children: [
|
|
6901
|
+
shared && /* @__PURE__ */ jsx11("span", { className: "rounded-og-sm bg-og-status-failed/85 px-2 py-0.5 text-og-xs text-og-accent-fg", children: "Shared sandbox \u2014 others are watching" }),
|
|
6902
|
+
/* @__PURE__ */ jsxs9(
|
|
6903
|
+
"button",
|
|
6835
6904
|
{
|
|
6836
|
-
|
|
6837
|
-
|
|
6838
|
-
|
|
6839
|
-
|
|
6840
|
-
|
|
6841
|
-
|
|
6842
|
-
|
|
6843
|
-
|
|
6844
|
-
|
|
6845
|
-
|
|
6846
|
-
"
|
|
6847
|
-
|
|
6848
|
-
ref: (node) => {
|
|
6849
|
-
tabRefs.current[index] = node;
|
|
6850
|
-
},
|
|
6851
|
-
id: `${tabsetId}-tab-${index}`,
|
|
6852
|
-
type: "button",
|
|
6853
|
-
role: "tab",
|
|
6854
|
-
"aria-selected": tab.id === current,
|
|
6855
|
-
"aria-controls": `${tabsetId}-panel-${index}`,
|
|
6856
|
-
tabIndex: tab.id === current ? 0 : -1,
|
|
6857
|
-
onClick: () => onTab(tab.id),
|
|
6858
|
-
onKeyDown: (event) => onTabKeyDown(event, index),
|
|
6859
|
-
className: cn(
|
|
6860
|
-
"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-none focus-visible:ring-2 focus-visible:ring-og-accent max-[1023px]:min-h-11 max-[1023px]:min-w-11 pointer-coarse:min-h-11 pointer-coarse:min-w-11",
|
|
6861
|
-
tab.id === current ? "bg-og-accent-soft text-og-fg" : "text-og-fg-subtle hover:text-og-fg"
|
|
6862
|
-
),
|
|
6863
|
-
children: [
|
|
6864
|
-
/* @__PURE__ */ jsx11("span", { "data-contrast-audited": true, children: tab.label }),
|
|
6865
|
-
tab.badge
|
|
6866
|
-
]
|
|
6867
|
-
},
|
|
6868
|
-
tab.id
|
|
6869
|
-
))
|
|
6905
|
+
type: "button",
|
|
6906
|
+
onClick: onRelease,
|
|
6907
|
+
title: "Return control (or press Ctrl+Alt+Shift)",
|
|
6908
|
+
className: cn(
|
|
6909
|
+
"inline-flex items-center gap-1.5 rounded-og-sm border px-2.5 py-1 text-og-xs font-semibold shadow-og-md transition-colors",
|
|
6910
|
+
"border-og-accent bg-og-bg/90 text-og-fg backdrop-blur-sm",
|
|
6911
|
+
"outline-none hover:bg-og-accent hover:text-og-accent-fg focus-visible:ring-2 focus-visible:ring-og-accent"
|
|
6912
|
+
),
|
|
6913
|
+
children: [
|
|
6914
|
+
/* @__PURE__ */ jsx11(MonitorIcon, { className: "size-3.5", strokeWidth: 2, "aria-hidden": true }),
|
|
6915
|
+
"Return control"
|
|
6916
|
+
]
|
|
6870
6917
|
}
|
|
6871
|
-
)
|
|
6872
|
-
|
|
6873
|
-
|
|
6918
|
+
)
|
|
6919
|
+
] })
|
|
6920
|
+
] });
|
|
6921
|
+
}
|
|
6922
|
+
function unavailableCopy(reason) {
|
|
6923
|
+
switch (reason) {
|
|
6924
|
+
case "backend_unsupported":
|
|
6925
|
+
return "This sandbox backend cannot stream a desktop.";
|
|
6926
|
+
case "tier_headless":
|
|
6927
|
+
return "This deployment is headless \u2014 terminal, files, and diff only.";
|
|
6928
|
+
case "os_unsupported":
|
|
6929
|
+
return "The sandbox OS does not support a desktop stream.";
|
|
6930
|
+
case "not_provisioned":
|
|
6931
|
+
return "This sandbox doesn't have a desktop yet.";
|
|
6932
|
+
case "disabled_by_policy":
|
|
6933
|
+
return "Desktop streaming is disabled on this deployment.";
|
|
6934
|
+
case "lease_cold":
|
|
6935
|
+
return "Waiting for the sandbox to start\u2026";
|
|
6936
|
+
default:
|
|
6937
|
+
return "The desktop isn\u2019t available for this sandbox.";
|
|
6938
|
+
}
|
|
6939
|
+
}
|
|
6940
|
+
function WarmingNotice({ onRetry }) {
|
|
6941
|
+
return /* @__PURE__ */ jsxs9("div", { className: "flex max-w-sm flex-col items-center gap-3 rounded-og-lg border border-og-border bg-og-bg/90 p-5 text-center text-og-base text-og-fg backdrop-blur-sm", children: [
|
|
6942
|
+
/* @__PURE__ */ jsx11(LoaderCircleIcon2, { className: "size-7 animate-og-spin text-og-accent", strokeWidth: 1.5 }),
|
|
6943
|
+
/* @__PURE__ */ jsxs9("div", { className: "space-y-1", children: [
|
|
6944
|
+
/* @__PURE__ */ jsx11("div", { className: "font-medium", children: "Warming the sandbox\u2026" }),
|
|
6945
|
+
/* @__PURE__ */ jsx11("p", { className: "text-og-sm text-og-fg-subtle", children: "Spinning up the desktop \u2014 this takes a few seconds." })
|
|
6874
6946
|
] }),
|
|
6875
|
-
/* @__PURE__ */ jsx11(
|
|
6876
|
-
|
|
6877
|
-
|
|
6878
|
-
|
|
6879
|
-
|
|
6880
|
-
|
|
6881
|
-
|
|
6882
|
-
|
|
6883
|
-
|
|
6884
|
-
|
|
6885
|
-
|
|
6886
|
-
|
|
6887
|
-
|
|
6888
|
-
|
|
6889
|
-
|
|
6890
|
-
|
|
6947
|
+
onRetry && /* @__PURE__ */ jsx11(
|
|
6948
|
+
"button",
|
|
6949
|
+
{
|
|
6950
|
+
type: "button",
|
|
6951
|
+
onClick: onRetry,
|
|
6952
|
+
className: "rounded-og-sm border border-og-border px-3 py-1.5 text-og-sm text-og-fg-muted transition-colors hover:text-og-fg pointer-coarse:min-h-11",
|
|
6953
|
+
children: "Taking too long? Retry"
|
|
6954
|
+
}
|
|
6955
|
+
)
|
|
6956
|
+
] });
|
|
6957
|
+
}
|
|
6958
|
+
function DefaultConsentGate({ shared, onAccept }) {
|
|
6959
|
+
return /* @__PURE__ */ jsxs9("div", { className: "max-w-sm rounded-og-lg border border-og-border bg-og-bg p-4 text-center text-og-base text-og-fg", children: [
|
|
6960
|
+
/* @__PURE__ */ jsx11("div", { className: "mb-1 font-medium", children: "Watch the live desktop?" }),
|
|
6961
|
+
/* @__PURE__ */ jsxs9("p", { className: "mb-3 text-og-sm text-og-fg-subtle", children: [
|
|
6962
|
+
"The desktop pixel stream is ",
|
|
6963
|
+
/* @__PURE__ */ jsx11("strong", { children: "un-redacted" }),
|
|
6964
|
+
" \u2014 it can show secrets the agent prints on screen.",
|
|
6965
|
+
shared ? " This sandbox is shared: you'll also see other sessions' agents on the same screen." : ""
|
|
6966
|
+
] }),
|
|
6967
|
+
/* @__PURE__ */ jsx11(
|
|
6968
|
+
"button",
|
|
6969
|
+
{
|
|
6970
|
+
type: "button",
|
|
6971
|
+
onClick: onAccept,
|
|
6972
|
+
className: "rounded-og-sm bg-og-accent px-3 py-1.5 text-og-sm font-medium text-og-accent-fg pointer-coarse:min-h-11",
|
|
6973
|
+
children: "I understand \u2014 show the desktop"
|
|
6974
|
+
}
|
|
6975
|
+
)
|
|
6976
|
+
] });
|
|
6977
|
+
}
|
|
6978
|
+
function defaultNotice(title, body, onRetry) {
|
|
6979
|
+
return /* @__PURE__ */ jsxs9("div", { className: "max-w-sm rounded-og-lg border border-og-border bg-og-bg p-4 text-center text-og-base text-og-fg", children: [
|
|
6980
|
+
/* @__PURE__ */ jsxs9("div", { className: "mb-1 flex items-center justify-center gap-1.5 font-medium", children: [
|
|
6981
|
+
onRetry && /* @__PURE__ */ jsx11(WifiOffIcon, { className: "size-4 opacity-70", strokeWidth: 1.75 }),
|
|
6982
|
+
title
|
|
6983
|
+
] }),
|
|
6984
|
+
/* @__PURE__ */ jsx11("p", { className: "text-og-sm text-og-fg-subtle", children: body }),
|
|
6985
|
+
onRetry && /* @__PURE__ */ jsx11(
|
|
6986
|
+
"button",
|
|
6987
|
+
{
|
|
6988
|
+
type: "button",
|
|
6989
|
+
onClick: onRetry,
|
|
6990
|
+
className: "mt-3 rounded-og-sm border border-og-border px-3 py-1.5 text-og-sm transition-colors hover:border-og-accent pointer-coarse:min-h-11",
|
|
6991
|
+
children: "Reconnect"
|
|
6992
|
+
}
|
|
6993
|
+
)
|
|
6891
6994
|
] });
|
|
6892
6995
|
}
|
|
6893
6996
|
|
|
6894
|
-
// src/components/
|
|
6895
|
-
import { useCallback as useCallback22, useEffect as useEffect23, useMemo as useMemo11, useRef as useRef20, useState as useState20 } from "react";
|
|
6896
|
-
import { Popover } from "radix-ui";
|
|
6897
|
-
import {
|
|
6898
|
-
CircleCheckIcon,
|
|
6899
|
-
CpuIcon,
|
|
6900
|
-
LaptopIcon,
|
|
6901
|
-
LoaderCircleIcon as LoaderCircleIcon3,
|
|
6902
|
-
RefreshCwIcon as RefreshCwIcon2,
|
|
6903
|
-
TriangleAlertIcon as TriangleAlertIcon2
|
|
6904
|
-
} from "lucide-react";
|
|
6905
|
-
|
|
6906
|
-
// src/components/workbench-changes.tsx
|
|
6907
|
-
import { ArrowUpRightIcon, FileWarningIcon as FileWarningIcon2, HistoryIcon } from "lucide-react";
|
|
6997
|
+
// src/components/workspace-dock.tsx
|
|
6908
6998
|
import {
|
|
6909
6999
|
useCallback as useCallback21,
|
|
6910
7000
|
useEffect as useEffect22,
|
|
6911
7001
|
useId as useId4,
|
|
6912
|
-
useLayoutEffect as useLayoutEffect3,
|
|
6913
|
-
|
|
6914
|
-
|
|
6915
|
-
|
|
6916
|
-
|
|
6917
|
-
|
|
6918
|
-
|
|
6919
|
-
|
|
6920
|
-
|
|
6921
|
-
|
|
6922
|
-
|
|
6923
|
-
|
|
6924
|
-
|
|
6925
|
-
|
|
6926
|
-
|
|
6927
|
-
|
|
6928
|
-
|
|
6929
|
-
|
|
6930
|
-
|
|
6931
|
-
|
|
6932
|
-
|
|
6933
|
-
|
|
6934
|
-
if (secBottom > top && secTop < bottom) {
|
|
6935
|
-
if (firstVisible === -1) firstVisible = i;
|
|
6936
|
-
lastVisible = i;
|
|
6937
|
-
}
|
|
6938
|
-
acc = secBottom;
|
|
6939
|
-
}
|
|
6940
|
-
if (firstVisible === -1) {
|
|
6941
|
-
firstVisible = anchor;
|
|
6942
|
-
lastVisible = anchor;
|
|
6943
|
-
}
|
|
6944
|
-
return {
|
|
6945
|
-
start: Math.max(0, firstVisible - overscan),
|
|
6946
|
-
end: Math.min(n, lastVisible + 1 + overscan)
|
|
6947
|
-
};
|
|
6948
|
-
}
|
|
6949
|
-
function useWindowedSections(opts) {
|
|
6950
|
-
const { count, estimateHeight, overscan = 3 } = opts;
|
|
6951
|
-
const scrollRef = useRef18(null);
|
|
6952
|
-
const [heights, setHeights] = useState18(
|
|
6953
|
-
() => Array.from({ length: count }, (_, i) => estimateHeight(i))
|
|
6954
|
-
);
|
|
6955
|
-
useEffect21(() => {
|
|
6956
|
-
setHeights((prev) => {
|
|
6957
|
-
if (prev.length === count) return prev;
|
|
6958
|
-
return Array.from({ length: count }, (_, i) => prev[i] ?? estimateHeight(i));
|
|
6959
|
-
});
|
|
6960
|
-
}, [count]);
|
|
6961
|
-
const heightsRef = useRef18(heights);
|
|
6962
|
-
heightsRef.current = heights;
|
|
6963
|
-
const [range, setRange] = useState18(() => ({
|
|
6964
|
-
start: 0,
|
|
6965
|
-
end: Math.min(count, overscan + 1)
|
|
6966
|
-
}));
|
|
6967
|
-
const recompute = useCallback20(() => {
|
|
6968
|
-
const el = scrollRef.current;
|
|
6969
|
-
if (!el) return;
|
|
6970
|
-
const next = computeWindowRange(heightsRef.current, el.scrollTop, el.clientHeight, overscan);
|
|
6971
|
-
setRange((prev) => prev.start === next.start && prev.end === next.end ? prev : next);
|
|
6972
|
-
}, [overscan]);
|
|
6973
|
-
useEffect21(() => {
|
|
6974
|
-
const el = scrollRef.current;
|
|
6975
|
-
if (!el) return;
|
|
6976
|
-
let raf = 0;
|
|
6977
|
-
const onScroll = () => {
|
|
6978
|
-
if (raf) return;
|
|
6979
|
-
raf = typeof requestAnimationFrame === "function" ? requestAnimationFrame(() => {
|
|
6980
|
-
raf = 0;
|
|
6981
|
-
recompute();
|
|
6982
|
-
}) : (recompute(), 0);
|
|
6983
|
-
};
|
|
6984
|
-
el.addEventListener("scroll", onScroll, { passive: true });
|
|
6985
|
-
const ro = typeof ResizeObserver !== "undefined" ? new ResizeObserver(() => recompute()) : null;
|
|
6986
|
-
ro?.observe(el);
|
|
6987
|
-
recompute();
|
|
6988
|
-
return () => {
|
|
6989
|
-
el.removeEventListener("scroll", onScroll);
|
|
6990
|
-
ro?.disconnect();
|
|
6991
|
-
if (raf && typeof cancelAnimationFrame === "function") cancelAnimationFrame(raf);
|
|
6992
|
-
};
|
|
6993
|
-
}, [recompute, count]);
|
|
6994
|
-
const measure = useCallback20((index, height) => {
|
|
6995
|
-
if (height <= 0) return;
|
|
6996
|
-
setHeights((prev) => {
|
|
6997
|
-
if (Math.abs((prev[index] ?? 0) - height) < 1) return prev;
|
|
6998
|
-
const next = prev.slice();
|
|
6999
|
-
next[index] = height;
|
|
7000
|
-
return next;
|
|
7001
|
-
});
|
|
7002
|
-
}, []);
|
|
7003
|
-
const offsets = useMemo9(() => {
|
|
7004
|
-
const out = new Array(count + 1);
|
|
7005
|
-
out[0] = 0;
|
|
7006
|
-
for (let i = 0; i < count; i++) out[i + 1] = (out[i] ?? 0) + Math.max(heights[i] ?? 0, 0);
|
|
7007
|
-
return out;
|
|
7008
|
-
}, [heights, count]);
|
|
7009
|
-
useEffect21(() => {
|
|
7010
|
-
recompute();
|
|
7011
|
-
}, [offsets, recompute]);
|
|
7012
|
-
const scrollToIndex = useCallback20(
|
|
7013
|
-
(index) => {
|
|
7014
|
-
const el = scrollRef.current;
|
|
7015
|
-
if (!el) return;
|
|
7016
|
-
const clamped = Math.max(0, Math.min(count - 1, index));
|
|
7017
|
-
el.scrollTop = offsets[clamped] ?? 0;
|
|
7018
|
-
},
|
|
7019
|
-
[offsets, count]
|
|
7020
|
-
);
|
|
7021
|
-
return {
|
|
7022
|
-
scrollRef,
|
|
7023
|
-
range,
|
|
7024
|
-
offsets,
|
|
7025
|
-
totalHeight: offsets[count] ?? 0,
|
|
7026
|
-
measure,
|
|
7027
|
-
scrollToIndex
|
|
7028
|
-
};
|
|
7029
|
-
}
|
|
7030
|
-
|
|
7031
|
-
// src/components/workbench-changes.tsx
|
|
7032
|
-
import { jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
7033
|
-
var GROUP_THRESHOLD = 20;
|
|
7034
|
-
var OVERSCAN = 2;
|
|
7035
|
-
var HEADER_PX = 30;
|
|
7036
|
-
var LINE_PX = 18;
|
|
7037
|
-
var GUARD_BODY_PX = 52;
|
|
7038
|
-
var COMPACT_SURFACE_PX = 560;
|
|
7039
|
-
var useChangesLayoutEffect = typeof window === "undefined" ? useEffect22 : useLayoutEffect3;
|
|
7040
|
-
var STATUS_TINT2 = {
|
|
7041
|
-
added: "text-og-status-idle",
|
|
7042
|
-
modified: "text-og-status-running",
|
|
7043
|
-
deleted: "text-og-status-failed",
|
|
7044
|
-
renamed: "text-og-accent",
|
|
7045
|
-
copied: "text-og-accent",
|
|
7046
|
-
untracked: "text-og-fg-subtle",
|
|
7047
|
-
ignored: "text-og-fg-subtle",
|
|
7048
|
-
conflicted: "text-og-status-failed",
|
|
7049
|
-
typechange: "text-og-status-running"
|
|
7050
|
-
};
|
|
7051
|
-
var STATUS_LETTER = {
|
|
7052
|
-
added: "A",
|
|
7053
|
-
modified: "M",
|
|
7054
|
-
deleted: "D",
|
|
7055
|
-
renamed: "R",
|
|
7056
|
-
copied: "C",
|
|
7057
|
-
untracked: "U",
|
|
7058
|
-
ignored: "I",
|
|
7059
|
-
conflicted: "!",
|
|
7060
|
-
typechange: "T"
|
|
7061
|
-
};
|
|
7062
|
-
function compactFileLabel(file) {
|
|
7063
|
-
const slash = file.path.lastIndexOf("/");
|
|
7064
|
-
const basename = slash >= 0 ? file.path.slice(slash + 1) : file.path;
|
|
7065
|
-
const parent = slash >= 0 ? file.path.slice(0, slash + 1) : "";
|
|
7066
|
-
return `${STATUS_LETTER[file.status]} \xB7 ${basename}${parent ? ` \u2014 ${parent}` : ""}`;
|
|
7067
|
-
}
|
|
7068
|
-
function buildRail(files) {
|
|
7069
|
-
const roots = new Set(
|
|
7070
|
-
files.flatMap((file) => file.repoRoot === void 0 ? [] : [file.repoRoot])
|
|
7071
|
-
);
|
|
7072
|
-
const groupByRepo = roots.size > 1;
|
|
7073
|
-
if (!groupByRepo && files.length <= GROUP_THRESHOLD) {
|
|
7074
|
-
return {
|
|
7075
|
-
orderedFiles: files,
|
|
7076
|
-
rows: files.map((file, index) => ({ kind: "file", file, index })),
|
|
7077
|
-
grouped: false
|
|
7078
|
-
};
|
|
7079
|
-
}
|
|
7080
|
-
const groups = /* @__PURE__ */ new Map();
|
|
7081
|
-
for (const file of files) {
|
|
7082
|
-
const slash = file.path.indexOf("/");
|
|
7083
|
-
const key = groupByRepo ? file.repoRoot || "(workspace)" : slash > 0 ? file.path.slice(0, slash) : "(root)";
|
|
7084
|
-
const bucket = groups.get(key);
|
|
7085
|
-
if (bucket) bucket.push(file);
|
|
7086
|
-
else groups.set(key, [file]);
|
|
7002
|
+
useLayoutEffect as useLayoutEffect3,
|
|
7003
|
+
useRef as useRef19,
|
|
7004
|
+
useState as useState19
|
|
7005
|
+
} from "react";
|
|
7006
|
+
import {
|
|
7007
|
+
ChevronsLeftRightIcon,
|
|
7008
|
+
Maximize2Icon,
|
|
7009
|
+
Minimize2Icon,
|
|
7010
|
+
PanelRightCloseIcon,
|
|
7011
|
+
XIcon as XIcon2
|
|
7012
|
+
} from "lucide-react";
|
|
7013
|
+
import {
|
|
7014
|
+
Group,
|
|
7015
|
+
Panel,
|
|
7016
|
+
Separator,
|
|
7017
|
+
useDefaultLayout,
|
|
7018
|
+
usePanelRef
|
|
7019
|
+
} from "react-resizable-panels";
|
|
7020
|
+
import { Fragment as Fragment4, jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
7021
|
+
var SERVER_LAYOUT_STORAGE = {
|
|
7022
|
+
getItem: () => null,
|
|
7023
|
+
setItem: () => {
|
|
7087
7024
|
}
|
|
7088
|
-
|
|
7089
|
-
|
|
7090
|
-
|
|
7091
|
-
|
|
7092
|
-
|
|
7093
|
-
|
|
7094
|
-
|
|
7095
|
-
rows.push({ kind: "file", file, index: orderedFiles.length });
|
|
7096
|
-
orderedFiles.push(file);
|
|
7097
|
-
}
|
|
7025
|
+
};
|
|
7026
|
+
function getLayoutStorage() {
|
|
7027
|
+
if (typeof window === "undefined") return SERVER_LAYOUT_STORAGE;
|
|
7028
|
+
try {
|
|
7029
|
+
return window.localStorage;
|
|
7030
|
+
} catch {
|
|
7031
|
+
return SERVER_LAYOUT_STORAGE;
|
|
7098
7032
|
}
|
|
7099
|
-
return { orderedFiles, rows, grouped: true };
|
|
7100
|
-
}
|
|
7101
|
-
function isGuarded(file) {
|
|
7102
|
-
return file.isBinary || file.truncated;
|
|
7103
7033
|
}
|
|
7104
|
-
|
|
7105
|
-
|
|
7106
|
-
|
|
7107
|
-
|
|
7108
|
-
|
|
7034
|
+
var useDockLayoutEffect = typeof window === "undefined" ? useEffect22 : useLayoutEffect3;
|
|
7035
|
+
var DOCK_OVERLAY_BREAKPOINT = 1024;
|
|
7036
|
+
function useIsNarrow(maxWidth) {
|
|
7037
|
+
const [narrow, setNarrow] = useState19(false);
|
|
7038
|
+
useDockLayoutEffect(() => {
|
|
7039
|
+
if (typeof window === "undefined" || typeof window.matchMedia !== "function") {
|
|
7040
|
+
return;
|
|
7041
|
+
}
|
|
7042
|
+
const mql = window.matchMedia(`(max-width: ${maxWidth - 1}px)`);
|
|
7043
|
+
const update = () => setNarrow(mql.matches);
|
|
7044
|
+
update();
|
|
7045
|
+
if (typeof mql.addEventListener === "function") {
|
|
7046
|
+
mql.addEventListener("change", update);
|
|
7047
|
+
return () => mql.removeEventListener("change", update);
|
|
7048
|
+
}
|
|
7049
|
+
mql.addListener(update);
|
|
7050
|
+
return () => mql.removeListener(update);
|
|
7051
|
+
}, [maxWidth]);
|
|
7052
|
+
return narrow;
|
|
7109
7053
|
}
|
|
7110
|
-
function
|
|
7111
|
-
|
|
7112
|
-
|
|
7113
|
-
|
|
7114
|
-
|
|
7115
|
-
|
|
7116
|
-
|
|
7054
|
+
function WorkspaceDock({
|
|
7055
|
+
primary,
|
|
7056
|
+
tabs,
|
|
7057
|
+
activeTab,
|
|
7058
|
+
onActiveTabChange,
|
|
7059
|
+
collapsed: collapsedProp,
|
|
7060
|
+
onCollapsedChange,
|
|
7061
|
+
headerAccessory,
|
|
7062
|
+
mobileLeadingControl,
|
|
7063
|
+
autoSaveId = "og.session.dock",
|
|
7064
|
+
defaultSize = 34,
|
|
7065
|
+
minSize = 22,
|
|
7066
|
+
maxSize = 70,
|
|
7117
7067
|
className
|
|
7118
7068
|
}) {
|
|
7069
|
+
const narrow = useIsNarrow(DOCK_OVERLAY_BREAKPOINT);
|
|
7119
7070
|
const rootRef = useRef19(null);
|
|
7120
|
-
const
|
|
7121
|
-
const
|
|
7122
|
-
const
|
|
7123
|
-
const
|
|
7124
|
-
const [
|
|
7125
|
-
const
|
|
7126
|
-
const
|
|
7127
|
-
|
|
7128
|
-
|
|
7129
|
-
);
|
|
7130
|
-
|
|
7131
|
-
|
|
7132
|
-
|
|
7133
|
-
|
|
7134
|
-
|
|
7135
|
-
|
|
7136
|
-
|
|
7137
|
-
|
|
7138
|
-
|
|
7139
|
-
|
|
7140
|
-
|
|
7141
|
-
|
|
7142
|
-
|
|
7143
|
-
|
|
7144
|
-
observer.observe(root);
|
|
7145
|
-
return () => observer.disconnect();
|
|
7146
|
-
}, []);
|
|
7147
|
-
useChangesLayoutEffect(() => {
|
|
7148
|
-
if (typeof window === "undefined" || typeof window.matchMedia !== "function") return;
|
|
7149
|
-
const query = window.matchMedia("(pointer: coarse)");
|
|
7150
|
-
const update = () => setCoarsePointer(query.matches);
|
|
7151
|
-
update();
|
|
7152
|
-
if (typeof query.addEventListener === "function") {
|
|
7153
|
-
query.addEventListener("change", update);
|
|
7154
|
-
return () => query.removeEventListener("change", update);
|
|
7155
|
-
}
|
|
7156
|
-
if (typeof query.addListener === "function") {
|
|
7157
|
-
query.addListener(update);
|
|
7158
|
-
return () => query.removeListener(update);
|
|
7159
|
-
}
|
|
7160
|
-
}, []);
|
|
7161
|
-
const additions = useMemo10(() => diff.reduce((sum, f) => sum + f.additions, 0), [diff]);
|
|
7162
|
-
const deletions = useMemo10(() => diff.reduce((sum, f) => sum + f.deletions, 0), [diff]);
|
|
7163
|
-
const estimateHeight = useCallback21(
|
|
7164
|
-
(index) => {
|
|
7165
|
-
const file = orderedFiles[index];
|
|
7166
|
-
return file ? estimateSectionHeight(file) : HEADER_PX + LINE_PX;
|
|
7071
|
+
const reopenRef = useRef19(null);
|
|
7072
|
+
const returnFocusRef = useRef19(null);
|
|
7073
|
+
const initialNarrowFocusRef = useRef19(false);
|
|
7074
|
+
const dockPanelRef = usePanelRef();
|
|
7075
|
+
const [internalCollapsed, setInternalCollapsed] = useState19(false);
|
|
7076
|
+
const [maximized, setMaximized] = useState19(false);
|
|
7077
|
+
const [internalTab, setInternalTab] = useState19(tabs[0]?.id ?? "");
|
|
7078
|
+
const collapsed = collapsedProp ?? internalCollapsed;
|
|
7079
|
+
const hostControlled = collapsedProp !== void 0;
|
|
7080
|
+
const previousCollapsedRef = useRef19(collapsed);
|
|
7081
|
+
const { defaultLayout, onLayoutChanged } = useDefaultLayout({
|
|
7082
|
+
panelIds: ["primary", "dock"],
|
|
7083
|
+
storage: getLayoutStorage(),
|
|
7084
|
+
id: autoSaveId
|
|
7085
|
+
});
|
|
7086
|
+
const requestedTab = activeTab ?? internalTab;
|
|
7087
|
+
const tabIds = tabs.map((tab) => tab.id);
|
|
7088
|
+
const firstTabId = tabs[0]?.id ?? "";
|
|
7089
|
+
const requestedTabIsValid = tabIds.includes(requestedTab);
|
|
7090
|
+
const current = requestedTabIsValid ? requestedTab : firstTabId;
|
|
7091
|
+
const setTab = useCallback21(
|
|
7092
|
+
(id) => {
|
|
7093
|
+
setInternalTab(id);
|
|
7094
|
+
onActiveTabChange?.(id);
|
|
7167
7095
|
},
|
|
7168
|
-
[
|
|
7096
|
+
[onActiveTabChange]
|
|
7169
7097
|
);
|
|
7170
|
-
const
|
|
7171
|
-
|
|
7172
|
-
|
|
7173
|
-
|
|
7174
|
-
});
|
|
7175
|
-
const jumpTo = useCallback21(
|
|
7176
|
-
(index) => {
|
|
7177
|
-
setActivePath(orderedFiles[index]?.path ?? null);
|
|
7178
|
-
windowed.scrollToIndex(index);
|
|
7098
|
+
const setCollapsed = useCallback21(
|
|
7099
|
+
(next) => {
|
|
7100
|
+
setInternalCollapsed((previous) => previous === next ? previous : next);
|
|
7101
|
+
onCollapsedChange?.(next);
|
|
7179
7102
|
},
|
|
7180
|
-
[
|
|
7103
|
+
[onCollapsedChange]
|
|
7181
7104
|
);
|
|
7182
|
-
|
|
7183
|
-
|
|
7184
|
-
if (!el) {
|
|
7105
|
+
useDockLayoutEffect(() => {
|
|
7106
|
+
if (collapsedProp === void 0) {
|
|
7185
7107
|
return;
|
|
7186
7108
|
}
|
|
7187
|
-
|
|
7188
|
-
|
|
7189
|
-
|
|
7190
|
-
|
|
7191
|
-
|
|
7192
|
-
else break;
|
|
7109
|
+
if (collapsedProp) {
|
|
7110
|
+
dockPanelRef.current?.collapse();
|
|
7111
|
+
setMaximized(false);
|
|
7112
|
+
} else {
|
|
7113
|
+
dockPanelRef.current?.expand();
|
|
7193
7114
|
}
|
|
7194
|
-
|
|
7195
|
-
setActivePath((previous) => previous === nextPath ? previous : nextPath);
|
|
7196
|
-
}, [windowed, orderedFiles]);
|
|
7115
|
+
}, [collapsedProp, dockPanelRef]);
|
|
7197
7116
|
useEffect22(() => {
|
|
7198
|
-
const
|
|
7199
|
-
|
|
7200
|
-
|
|
7201
|
-
|
|
7202
|
-
|
|
7203
|
-
|
|
7204
|
-
|
|
7205
|
-
|
|
7206
|
-
|
|
7117
|
+
const previous = previousCollapsedRef.current;
|
|
7118
|
+
previousCollapsedRef.current = collapsed;
|
|
7119
|
+
if (previous === collapsed) return;
|
|
7120
|
+
if (!collapsed) {
|
|
7121
|
+
const active = document.activeElement;
|
|
7122
|
+
const workspaceSurface = rootRef.current?.querySelector(
|
|
7123
|
+
narrow ? '[role="dialog"]' : "[data-workspace-surface]"
|
|
7124
|
+
);
|
|
7125
|
+
if (active instanceof HTMLElement && !workspaceSurface?.contains(active)) {
|
|
7126
|
+
returnFocusRef.current = active;
|
|
7127
|
+
}
|
|
7128
|
+
if (narrow) {
|
|
7129
|
+
const frame2 = requestAnimationFrame(() => {
|
|
7130
|
+
rootRef.current?.querySelector('[role="dialog"] [role="tab"][aria-selected="true"]')?.focus();
|
|
7131
|
+
});
|
|
7132
|
+
return () => cancelAnimationFrame(frame2);
|
|
7133
|
+
}
|
|
7134
|
+
return;
|
|
7135
|
+
}
|
|
7136
|
+
const frame = requestAnimationFrame(() => {
|
|
7137
|
+
if (!hostControlled) {
|
|
7138
|
+
reopenRef.current?.focus();
|
|
7139
|
+
return;
|
|
7140
|
+
}
|
|
7141
|
+
const target = returnFocusRef.current;
|
|
7142
|
+
if (target?.isConnected) target.focus();
|
|
7143
|
+
});
|
|
7144
|
+
return () => cancelAnimationFrame(frame);
|
|
7145
|
+
}, [collapsed, hostControlled, narrow]);
|
|
7146
|
+
useEffect22(() => {
|
|
7147
|
+
if (!narrow || collapsed || initialNarrowFocusRef.current) return;
|
|
7148
|
+
initialNarrowFocusRef.current = true;
|
|
7149
|
+
const active = document.activeElement;
|
|
7150
|
+
const workspaceSurface = rootRef.current?.querySelector('[role="dialog"]');
|
|
7151
|
+
if (active instanceof HTMLElement && !workspaceSurface?.contains(active)) {
|
|
7152
|
+
returnFocusRef.current = active;
|
|
7153
|
+
}
|
|
7154
|
+
const frame = requestAnimationFrame(() => {
|
|
7155
|
+
workspaceSurface?.querySelector('[role="tab"][aria-selected="true"]')?.focus();
|
|
7156
|
+
});
|
|
7157
|
+
return () => cancelAnimationFrame(frame);
|
|
7158
|
+
}, [collapsed, narrow]);
|
|
7159
|
+
useEffect22(() => {
|
|
7160
|
+
if (firstTabId && !requestedTabIsValid) {
|
|
7161
|
+
setTab(firstTabId);
|
|
7162
|
+
}
|
|
7163
|
+
}, [firstTabId, requestedTabIsValid, setTab]);
|
|
7164
|
+
const collapse = useCallback21(() => {
|
|
7165
|
+
dockPanelRef.current?.collapse();
|
|
7166
|
+
setCollapsed(true);
|
|
7167
|
+
}, [dockPanelRef, setCollapsed]);
|
|
7168
|
+
const expand = useCallback21(() => {
|
|
7169
|
+
dockPanelRef.current?.expand();
|
|
7170
|
+
setCollapsed(false);
|
|
7171
|
+
}, [dockPanelRef, setCollapsed]);
|
|
7172
|
+
useEffect22(() => {
|
|
7173
|
+
const overlayOpen = maximized || narrow && !collapsed;
|
|
7174
|
+
if (!overlayOpen) return;
|
|
7175
|
+
const onKey = (event) => {
|
|
7176
|
+
if (event.key === "Escape") {
|
|
7177
|
+
if (maximized) setMaximized(false);
|
|
7178
|
+
else collapse();
|
|
7179
|
+
return;
|
|
7180
|
+
}
|
|
7181
|
+
if (event.key !== "Tab") return;
|
|
7182
|
+
const surface = rootRef.current?.querySelector(
|
|
7183
|
+
narrow ? '[role="dialog"]' : "[data-workspace-surface]"
|
|
7184
|
+
);
|
|
7185
|
+
if (!surface) return;
|
|
7186
|
+
const focusable = Array.from(surface.querySelectorAll("*")).filter(
|
|
7187
|
+
(element) => element.tabIndex >= 0 && !element.hasAttribute("disabled") && element.getClientRects().length > 0 && getComputedStyle(element).visibility !== "hidden"
|
|
7188
|
+
);
|
|
7189
|
+
const first = focusable[0];
|
|
7190
|
+
const last = focusable.at(-1);
|
|
7191
|
+
const active = document.activeElement;
|
|
7192
|
+
if (!first || !last) {
|
|
7193
|
+
event.preventDefault();
|
|
7194
|
+
surface.focus();
|
|
7195
|
+
return;
|
|
7196
|
+
}
|
|
7197
|
+
if (!surface.contains(active)) {
|
|
7198
|
+
event.preventDefault();
|
|
7199
|
+
(event.shiftKey ? last : first).focus();
|
|
7200
|
+
return;
|
|
7201
|
+
}
|
|
7202
|
+
if (event.shiftKey && active === first || !event.shiftKey && active === last) {
|
|
7203
|
+
event.preventDefault();
|
|
7204
|
+
(event.shiftKey ? last : first).focus();
|
|
7205
|
+
return;
|
|
7206
|
+
}
|
|
7207
|
+
requestAnimationFrame(() => {
|
|
7208
|
+
const next = document.activeElement;
|
|
7209
|
+
if (!surface.contains(next)) (event.shiftKey ? last : first).focus();
|
|
7210
|
+
});
|
|
7211
|
+
};
|
|
7212
|
+
window.addEventListener("keydown", onKey);
|
|
7213
|
+
return () => window.removeEventListener("keydown", onKey);
|
|
7214
|
+
}, [maximized, narrow, collapsed, collapse]);
|
|
7215
|
+
if (narrow) {
|
|
7216
|
+
return /* @__PURE__ */ jsxs10("div", { ref: rootRef, className: cn("relative flex h-full min-h-0 w-full min-w-0", className), children: [
|
|
7217
|
+
/* @__PURE__ */ jsx12(
|
|
7218
|
+
"div",
|
|
7219
|
+
{
|
|
7220
|
+
"data-workspace-primary": true,
|
|
7221
|
+
"aria-hidden": !collapsed ? true : void 0,
|
|
7222
|
+
className: "min-h-0 min-w-0 flex-1",
|
|
7223
|
+
inert: !collapsed ? true : void 0,
|
|
7224
|
+
children: primary
|
|
7225
|
+
}
|
|
7226
|
+
),
|
|
7227
|
+
/* @__PURE__ */ jsx12(
|
|
7228
|
+
"div",
|
|
7229
|
+
{
|
|
7230
|
+
"data-workspace-surface": true,
|
|
7231
|
+
role: "dialog",
|
|
7232
|
+
"aria-modal": "true",
|
|
7233
|
+
"aria-label": "Workspace",
|
|
7234
|
+
className: "fixed inset-0 z-40 flex flex-col bg-og-bg",
|
|
7235
|
+
hidden: collapsed,
|
|
7236
|
+
style: {
|
|
7237
|
+
paddingTop: "env(safe-area-inset-top)",
|
|
7238
|
+
paddingBottom: "env(safe-area-inset-bottom)"
|
|
7239
|
+
},
|
|
7240
|
+
children: /* @__PURE__ */ jsx12(
|
|
7241
|
+
DockChrome,
|
|
7242
|
+
{
|
|
7243
|
+
tabs,
|
|
7244
|
+
current,
|
|
7245
|
+
onTab: setTab,
|
|
7246
|
+
leading: mobileLeadingControl,
|
|
7247
|
+
accessory: headerAccessory,
|
|
7248
|
+
controls: /* @__PURE__ */ jsx12(ChromeButton, { onClick: collapse, title: "Close workspace", label: "Close workspace", children: /* @__PURE__ */ jsx12(XIcon2, { className: "size-4" }) })
|
|
7249
|
+
}
|
|
7250
|
+
)
|
|
7251
|
+
}
|
|
7252
|
+
),
|
|
7253
|
+
collapsed && !hostControlled ? /* @__PURE__ */ jsx12(
|
|
7254
|
+
"button",
|
|
7255
|
+
{
|
|
7256
|
+
ref: reopenRef,
|
|
7257
|
+
type: "button",
|
|
7258
|
+
onClick: expand,
|
|
7259
|
+
title: "Open workspace",
|
|
7260
|
+
"aria-label": "Open workspace",
|
|
7261
|
+
className: "absolute right-3 top-3 z-30 inline-flex size-11 items-center justify-center rounded-og-md border border-og-border bg-og-surface-1 text-og-fg-muted shadow-lg transition-colors hover:border-og-border-strong hover:text-og-fg focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-og-accent",
|
|
7262
|
+
style: { marginTop: "env(safe-area-inset-top)" },
|
|
7263
|
+
children: /* @__PURE__ */ jsx12(ChevronsLeftRightIcon, { className: "size-4", "aria-hidden": true })
|
|
7264
|
+
}
|
|
7265
|
+
) : null
|
|
7266
|
+
] });
|
|
7267
|
+
}
|
|
7268
|
+
const dockChrome = /* @__PURE__ */ jsx12(
|
|
7269
|
+
DockChrome,
|
|
7207
7270
|
{
|
|
7208
|
-
|
|
7209
|
-
|
|
7210
|
-
|
|
7211
|
-
|
|
7212
|
-
|
|
7213
|
-
|
|
7271
|
+
tabs,
|
|
7272
|
+
current,
|
|
7273
|
+
onTab: setTab,
|
|
7274
|
+
accessory: headerAccessory,
|
|
7275
|
+
controls: /* @__PURE__ */ jsxs10(Fragment4, { children: [
|
|
7276
|
+
/* @__PURE__ */ jsx12(
|
|
7277
|
+
ChromeButton,
|
|
7214
7278
|
{
|
|
7215
|
-
|
|
7216
|
-
|
|
7217
|
-
|
|
7218
|
-
),
|
|
7219
|
-
children: [
|
|
7220
|
-
/* @__PURE__ */ jsxs10(
|
|
7221
|
-
"span",
|
|
7222
|
-
{
|
|
7223
|
-
role: "status",
|
|
7224
|
-
"aria-atomic": "true",
|
|
7225
|
-
"aria-live": "polite",
|
|
7226
|
-
"data-contrast-audited": true,
|
|
7227
|
-
className: "min-w-0 text-og-xs text-og-fg-muted",
|
|
7228
|
-
children: [
|
|
7229
|
-
diff.length,
|
|
7230
|
-
" ",
|
|
7231
|
-
diff.length === 1 ? "file" : "files",
|
|
7232
|
-
" changed",
|
|
7233
|
-
/* @__PURE__ */ jsxs10("span", { "data-contrast-audited": true, className: "ml-2 text-og-status-idle", children: [
|
|
7234
|
-
"+",
|
|
7235
|
-
additions
|
|
7236
|
-
] }),
|
|
7237
|
-
/* @__PURE__ */ jsxs10("span", { "data-contrast-audited": true, className: "ml-1 text-og-status-failed", children: [
|
|
7238
|
-
"\u2212",
|
|
7239
|
-
deletions
|
|
7240
|
-
] })
|
|
7241
|
-
]
|
|
7242
|
-
}
|
|
7243
|
-
),
|
|
7244
|
-
/* @__PURE__ */ jsxs10(
|
|
7245
|
-
"div",
|
|
7246
|
-
{
|
|
7247
|
-
className: cn(
|
|
7248
|
-
"flex shrink-0 items-center gap-2",
|
|
7249
|
-
compact && source === "capture" && "self-start"
|
|
7250
|
-
),
|
|
7251
|
-
children: [
|
|
7252
|
-
compact ? null : /* @__PURE__ */ jsx12(LayoutToggle, { layout, onChange: setLayout }),
|
|
7253
|
-
/* @__PURE__ */ jsx12(SourceBadge, { source, capturedAt, label: sourceBadge })
|
|
7254
|
-
]
|
|
7255
|
-
}
|
|
7256
|
-
)
|
|
7257
|
-
]
|
|
7279
|
+
onClick: () => setMaximized((m) => !m),
|
|
7280
|
+
title: maximized ? "Restore (Esc)" : "Maximize",
|
|
7281
|
+
label: maximized ? "Restore dock" : "Maximize dock",
|
|
7282
|
+
children: maximized ? /* @__PURE__ */ jsx12(Minimize2Icon, { className: "size-3.5" }) : /* @__PURE__ */ jsx12(Maximize2Icon, { className: "size-3.5" })
|
|
7258
7283
|
}
|
|
7259
7284
|
),
|
|
7260
|
-
|
|
7261
|
-
|
|
7262
|
-
|
|
7263
|
-
|
|
7264
|
-
|
|
7265
|
-
|
|
7266
|
-
|
|
7267
|
-
|
|
7268
|
-
|
|
7269
|
-
|
|
7270
|
-
|
|
7271
|
-
|
|
7272
|
-
|
|
7273
|
-
|
|
7274
|
-
|
|
7275
|
-
className: "h-11 w-full rounded-og-md border border-og-border bg-og-bg px-3 font-og-mono text-og-sm text-og-fg outline-none transition-colors focus:border-og-accent focus:ring-2 focus:ring-og-accent-soft",
|
|
7276
|
-
children: orderedFiles.map((file, index) => /* @__PURE__ */ jsx12("option", { value: index, children: compactFileLabel(file) }, file.path))
|
|
7277
|
-
}
|
|
7278
|
-
)
|
|
7279
|
-
] }),
|
|
7280
|
-
/* @__PURE__ */ jsx12("div", { className: "min-h-0 min-w-0 flex-1 overflow-auto", "data-opengeni-changes-pane": true, children: activeFile ? /* @__PURE__ */ jsx12(
|
|
7281
|
-
DiffSection,
|
|
7282
|
-
{
|
|
7283
|
-
file: activeFile,
|
|
7284
|
-
layout: "unified",
|
|
7285
|
-
themeType: resolvedTheme,
|
|
7286
|
-
...onOpenFile ? { onOpenFile } : {}
|
|
7287
|
-
}
|
|
7288
|
-
) : null })
|
|
7289
|
-
] }) : /* @__PURE__ */ jsxs10("div", { className: "flex min-h-0 flex-1", children: [
|
|
7290
|
-
/* @__PURE__ */ jsx12("div", { className: "w-[clamp(12rem,28%,15rem)] shrink-0 border-r border-og-border bg-og-surface-1/35", children: /* @__PURE__ */ jsx12(
|
|
7291
|
-
VList2,
|
|
7285
|
+
hostControlled ? null : /* @__PURE__ */ jsx12(ChromeButton, { onClick: collapse, title: "Collapse", label: "Collapse dock", children: /* @__PURE__ */ jsx12(PanelRightCloseIcon, { className: "size-3.5" }) })
|
|
7286
|
+
] })
|
|
7287
|
+
}
|
|
7288
|
+
);
|
|
7289
|
+
return /* @__PURE__ */ jsxs10("div", { ref: rootRef, className: cn("relative flex h-full min-h-0 w-full min-w-0", className), children: [
|
|
7290
|
+
/* @__PURE__ */ jsxs10(
|
|
7291
|
+
Group,
|
|
7292
|
+
{
|
|
7293
|
+
orientation: "horizontal",
|
|
7294
|
+
className: "min-h-0 flex-1",
|
|
7295
|
+
...defaultLayout ? { defaultLayout } : {},
|
|
7296
|
+
onLayoutChanged,
|
|
7297
|
+
children: [
|
|
7298
|
+
/* @__PURE__ */ jsx12(Panel, { id: "primary", minSize: "30%", className: "min-h-0 min-w-0", children: /* @__PURE__ */ jsx12(
|
|
7299
|
+
"div",
|
|
7292
7300
|
{
|
|
7293
|
-
|
|
7294
|
-
|
|
7295
|
-
|
|
7296
|
-
|
|
7297
|
-
|
|
7298
|
-
"div",
|
|
7299
|
-
{
|
|
7300
|
-
"data-rail-group": true,
|
|
7301
|
-
className: "flex items-center gap-1.5 px-2 pb-0.5 pt-2 text-og-xs font-medium uppercase tracking-wide text-og-fg-subtle",
|
|
7302
|
-
children: [
|
|
7303
|
-
/* @__PURE__ */ jsx12("span", { className: "min-w-0 truncate", children: row.label }),
|
|
7304
|
-
/* @__PURE__ */ jsx12("span", { className: "shrink-0", children: row.count })
|
|
7305
|
-
]
|
|
7306
|
-
},
|
|
7307
|
-
`g:${row.label}`
|
|
7308
|
-
) : /* @__PURE__ */ jsx12(
|
|
7309
|
-
RailFileRow,
|
|
7310
|
-
{
|
|
7311
|
-
file: row.file,
|
|
7312
|
-
grouped,
|
|
7313
|
-
active: row.index === activeIndex,
|
|
7314
|
-
onClick: () => jumpTo(row.index)
|
|
7315
|
-
},
|
|
7316
|
-
row.file.path
|
|
7317
|
-
)
|
|
7318
|
-
)
|
|
7301
|
+
"data-workspace-primary": true,
|
|
7302
|
+
"aria-hidden": maximized ? true : void 0,
|
|
7303
|
+
className: "h-full min-h-0 min-w-0",
|
|
7304
|
+
inert: maximized ? true : void 0,
|
|
7305
|
+
children: primary
|
|
7319
7306
|
}
|
|
7320
7307
|
) }),
|
|
7308
|
+
!collapsed && /* @__PURE__ */ jsx12(Separator, { className: "group relative z-10 w-1.5 shrink-0 outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-og-accent max-[1023px]:-mx-[19px] max-[1023px]:w-11 pointer-coarse:-mx-[19px] pointer-coarse:w-11", children: /* @__PURE__ */ jsx12("span", { className: "absolute inset-y-0 left-1/2 w-px -translate-x-1/2 bg-og-border transition-colors group-hover:bg-og-accent group-focus-visible:bg-og-accent group-data-[separator-state=dragging]:bg-og-accent" }) }),
|
|
7321
7309
|
/* @__PURE__ */ jsx12(
|
|
7322
|
-
|
|
7310
|
+
Panel,
|
|
7323
7311
|
{
|
|
7324
|
-
|
|
7325
|
-
|
|
7326
|
-
|
|
7327
|
-
|
|
7328
|
-
|
|
7329
|
-
|
|
7330
|
-
|
|
7331
|
-
|
|
7332
|
-
|
|
7333
|
-
|
|
7334
|
-
|
|
7335
|
-
|
|
7336
|
-
|
|
7337
|
-
|
|
7338
|
-
|
|
7339
|
-
|
|
7340
|
-
|
|
7341
|
-
|
|
7342
|
-
|
|
7343
|
-
|
|
7344
|
-
|
|
7345
|
-
|
|
7346
|
-
|
|
7347
|
-
|
|
7312
|
+
id: "dock",
|
|
7313
|
+
panelRef: dockPanelRef,
|
|
7314
|
+
collapsible: true,
|
|
7315
|
+
collapsedSize: "0%",
|
|
7316
|
+
defaultSize: `${defaultSize}%`,
|
|
7317
|
+
minSize: `${minSize}%`,
|
|
7318
|
+
maxSize: `${maxSize}%`,
|
|
7319
|
+
onResize: (size, _id, previousSize) => {
|
|
7320
|
+
const isCollapsed = size.asPercentage <= 1;
|
|
7321
|
+
const canInferCollapse = collapsedProp === void 0 || previousSize !== void 0;
|
|
7322
|
+
if (canInferCollapse && isCollapsed !== collapsed) {
|
|
7323
|
+
setCollapsed(isCollapsed);
|
|
7324
|
+
}
|
|
7325
|
+
},
|
|
7326
|
+
className: "min-h-0 min-w-0",
|
|
7327
|
+
children: /* @__PURE__ */ jsx12(
|
|
7328
|
+
"div",
|
|
7329
|
+
{
|
|
7330
|
+
"data-workspace-surface": true,
|
|
7331
|
+
role: maximized ? "dialog" : void 0,
|
|
7332
|
+
"aria-modal": maximized ? true : void 0,
|
|
7333
|
+
"aria-label": maximized ? "Workspace" : void 0,
|
|
7334
|
+
"aria-hidden": collapsed ? true : void 0,
|
|
7335
|
+
className: cn(
|
|
7336
|
+
"flex h-full min-h-0 min-w-0 flex-col bg-og-bg",
|
|
7337
|
+
maximized ? "fixed inset-0 z-40" : "border-l border-og-border",
|
|
7338
|
+
collapsed && "invisible pointer-events-none"
|
|
7339
|
+
),
|
|
7340
|
+
children: dockChrome
|
|
7341
|
+
}
|
|
7342
|
+
)
|
|
7348
7343
|
}
|
|
7349
7344
|
)
|
|
7350
|
-
] })
|
|
7351
|
-
]
|
|
7352
|
-
}
|
|
7353
|
-
);
|
|
7354
|
-
}
|
|
7355
|
-
function SourceBadge({
|
|
7356
|
-
source,
|
|
7357
|
-
capturedAt,
|
|
7358
|
-
label
|
|
7359
|
-
}) {
|
|
7360
|
-
if (source === "capture" && capturedAt) {
|
|
7361
|
-
return /* @__PURE__ */ jsxs10(
|
|
7362
|
-
"span",
|
|
7363
|
-
{
|
|
7364
|
-
className: "inline-flex items-center gap-1 rounded-og-xs border border-og-border px-1.5 py-px text-og-xs text-og-fg-muted",
|
|
7365
|
-
title: new Date(capturedAt).toLocaleString(),
|
|
7366
|
-
children: [
|
|
7367
|
-
/* @__PURE__ */ jsx12(HistoryIcon, { className: "size-3 shrink-0 text-og-status-running", "aria-hidden": true }),
|
|
7368
|
-
label
|
|
7369
7345
|
]
|
|
7370
7346
|
}
|
|
7371
|
-
)
|
|
7372
|
-
|
|
7373
|
-
|
|
7374
|
-
|
|
7375
|
-
|
|
7376
|
-
|
|
7377
|
-
|
|
7378
|
-
|
|
7347
|
+
),
|
|
7348
|
+
collapsed && !maximized && !hostControlled && /* @__PURE__ */ jsx12(
|
|
7349
|
+
"button",
|
|
7350
|
+
{
|
|
7351
|
+
ref: reopenRef,
|
|
7352
|
+
type: "button",
|
|
7353
|
+
onClick: expand,
|
|
7354
|
+
title: "Open workspace",
|
|
7355
|
+
className: "absolute inset-y-0 right-0 flex w-6 shrink-0 items-center justify-center border-l border-og-border bg-og-surface-1 text-og-fg-subtle hover:text-og-fg",
|
|
7356
|
+
children: /* @__PURE__ */ jsx12(ChevronsLeftRightIcon, { className: "size-3.5" })
|
|
7357
|
+
}
|
|
7358
|
+
)
|
|
7359
|
+
] });
|
|
7379
7360
|
}
|
|
7380
|
-
function
|
|
7381
|
-
|
|
7382
|
-
|
|
7383
|
-
|
|
7384
|
-
|
|
7361
|
+
function ChromeButton({
|
|
7362
|
+
onClick,
|
|
7363
|
+
title,
|
|
7364
|
+
label,
|
|
7365
|
+
children
|
|
7385
7366
|
}) {
|
|
7386
|
-
|
|
7387
|
-
const shown = grouped ? file.repoRoot !== void 0 ? repoPrefix && file.path.startsWith(repoPrefix) ? file.path.slice(repoPrefix.length) : file.path : file.path.slice(file.path.indexOf("/") + 1) || file.path : file.path;
|
|
7388
|
-
return /* @__PURE__ */ jsxs10(
|
|
7367
|
+
return /* @__PURE__ */ jsx12(
|
|
7389
7368
|
"button",
|
|
7390
7369
|
{
|
|
7391
7370
|
type: "button",
|
|
7392
7371
|
onClick,
|
|
7393
|
-
title
|
|
7394
|
-
"
|
|
7395
|
-
className:
|
|
7396
|
-
|
|
7397
|
-
grouped && "pl-3",
|
|
7398
|
-
active && "bg-og-accent-soft text-og-fg before:absolute before:inset-y-1 before:left-0 before:w-0.5 before:rounded-r-full before:bg-og-accent"
|
|
7399
|
-
),
|
|
7400
|
-
children: [
|
|
7401
|
-
/* @__PURE__ */ jsx12(
|
|
7402
|
-
"span",
|
|
7403
|
-
{
|
|
7404
|
-
"data-contrast-audited": true,
|
|
7405
|
-
className: cn("w-3 shrink-0 text-center font-og-mono text-og-xs", STATUS_TINT2[file.status]),
|
|
7406
|
-
children: STATUS_LETTER[file.status]
|
|
7407
|
-
}
|
|
7408
|
-
),
|
|
7409
|
-
/* @__PURE__ */ jsx12("span", { className: "min-w-0 flex-1 truncate", children: shown }),
|
|
7410
|
-
/* @__PURE__ */ jsxs10("span", { className: "ml-auto flex shrink-0 items-center gap-1 pl-1 font-og-mono text-og-xs", children: [
|
|
7411
|
-
/* @__PURE__ */ jsxs10("span", { "data-contrast-audited": true, className: "text-og-status-idle", children: [
|
|
7412
|
-
"+",
|
|
7413
|
-
file.additions
|
|
7414
|
-
] }),
|
|
7415
|
-
/* @__PURE__ */ jsxs10("span", { "data-contrast-audited": true, className: "text-og-status-failed", children: [
|
|
7416
|
-
"\u2212",
|
|
7417
|
-
file.deletions
|
|
7418
|
-
] })
|
|
7419
|
-
] })
|
|
7420
|
-
]
|
|
7372
|
+
title,
|
|
7373
|
+
"aria-label": label,
|
|
7374
|
+
className: "inline-flex size-7 items-center justify-center rounded-og-sm p-1 transition-colors hover:bg-og-surface-2 hover:text-og-fg focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-og-accent max-[1023px]:size-11 pointer-coarse:size-11",
|
|
7375
|
+
children
|
|
7421
7376
|
}
|
|
7422
7377
|
);
|
|
7423
7378
|
}
|
|
7424
|
-
function
|
|
7425
|
-
|
|
7426
|
-
|
|
7427
|
-
|
|
7428
|
-
|
|
7379
|
+
function DockChrome({
|
|
7380
|
+
tabs,
|
|
7381
|
+
current,
|
|
7382
|
+
onTab,
|
|
7383
|
+
leading,
|
|
7384
|
+
accessory,
|
|
7385
|
+
controls
|
|
7429
7386
|
}) {
|
|
7430
|
-
const
|
|
7387
|
+
const active = tabs.find((t) => t.id === current) ?? tabs[0];
|
|
7388
|
+
const tabsetId = useId4();
|
|
7389
|
+
const tabRefs = useRef19([]);
|
|
7390
|
+
const tabListRef = useRef19(null);
|
|
7391
|
+
const [visitedTabs, setVisitedTabs] = useState19(() => /* @__PURE__ */ new Set());
|
|
7392
|
+
const [tabOverflow, setTabOverflow] = useState19({ start: false, end: false });
|
|
7393
|
+
const activeId = active?.id ?? "";
|
|
7394
|
+
const activeTabIndex = tabs.findIndex((tab) => tab.id === activeId);
|
|
7395
|
+
const keepActiveTabVisible = useCallback21(() => {
|
|
7396
|
+
const selected = activeTabIndex >= 0 ? tabRefs.current[activeTabIndex] : null;
|
|
7397
|
+
selected?.scrollIntoView({ block: "nearest", inline: "nearest" });
|
|
7398
|
+
}, [activeTabIndex]);
|
|
7431
7399
|
useEffect22(() => {
|
|
7432
|
-
|
|
7433
|
-
|
|
7434
|
-
|
|
7435
|
-
|
|
7436
|
-
|
|
7437
|
-
|
|
7438
|
-
|
|
7439
|
-
}, [
|
|
7440
|
-
|
|
7441
|
-
|
|
7442
|
-
|
|
7443
|
-
|
|
7444
|
-
|
|
7445
|
-
|
|
7446
|
-
|
|
7447
|
-
|
|
7400
|
+
if (!activeId) return;
|
|
7401
|
+
setVisitedTabs((previous) => {
|
|
7402
|
+
if (previous.has(activeId)) return previous;
|
|
7403
|
+
const next = new Set(previous);
|
|
7404
|
+
next.add(activeId);
|
|
7405
|
+
return next;
|
|
7406
|
+
});
|
|
7407
|
+
}, [activeId]);
|
|
7408
|
+
useDockLayoutEffect(() => {
|
|
7409
|
+
keepActiveTabVisible();
|
|
7410
|
+
}, [activeId, keepActiveTabVisible]);
|
|
7411
|
+
useDockLayoutEffect(() => {
|
|
7412
|
+
const list = tabListRef.current;
|
|
7413
|
+
if (!list) return;
|
|
7414
|
+
const update = () => {
|
|
7415
|
+
const max = Math.max(0, list.scrollWidth - list.clientWidth);
|
|
7416
|
+
const next = { start: list.scrollLeft > 1, end: list.scrollLeft < max - 1 };
|
|
7417
|
+
setTabOverflow(
|
|
7418
|
+
(previous) => previous.start === next.start && previous.end === next.end ? previous : next
|
|
7419
|
+
);
|
|
7420
|
+
};
|
|
7421
|
+
update();
|
|
7422
|
+
list.addEventListener("scroll", update, { passive: true });
|
|
7423
|
+
const observer = typeof ResizeObserver === "undefined" ? null : new ResizeObserver(() => {
|
|
7424
|
+
keepActiveTabVisible();
|
|
7425
|
+
update();
|
|
7426
|
+
});
|
|
7427
|
+
observer?.observe(list);
|
|
7428
|
+
for (const tab of tabRefs.current) {
|
|
7429
|
+
if (tab) observer?.observe(tab);
|
|
7448
7430
|
}
|
|
7449
|
-
|
|
7450
|
-
|
|
7451
|
-
|
|
7452
|
-
|
|
7453
|
-
|
|
7454
|
-
|
|
7455
|
-
|
|
7456
|
-
|
|
7457
|
-
|
|
7458
|
-
|
|
7459
|
-
|
|
7460
|
-
|
|
7461
|
-
|
|
7462
|
-
|
|
7463
|
-
|
|
7464
|
-
|
|
7465
|
-
|
|
7466
|
-
|
|
7467
|
-
|
|
7468
|
-
|
|
7469
|
-
|
|
7470
|
-
|
|
7471
|
-
|
|
7472
|
-
|
|
7473
|
-
|
|
7474
|
-
|
|
7475
|
-
|
|
7476
|
-
|
|
7477
|
-
|
|
7478
|
-
|
|
7479
|
-
|
|
7480
|
-
|
|
7481
|
-
|
|
7482
|
-
|
|
7483
|
-
|
|
7484
|
-
|
|
7485
|
-
|
|
7486
|
-
|
|
7487
|
-
|
|
7431
|
+
return () => {
|
|
7432
|
+
list.removeEventListener("scroll", update);
|
|
7433
|
+
observer?.disconnect();
|
|
7434
|
+
};
|
|
7435
|
+
}, [activeId, keepActiveTabVisible, tabs.length]);
|
|
7436
|
+
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;
|
|
7437
|
+
const activateTab = (index) => {
|
|
7438
|
+
const tab = tabs[index];
|
|
7439
|
+
if (!tab) {
|
|
7440
|
+
return;
|
|
7441
|
+
}
|
|
7442
|
+
onTab(tab.id);
|
|
7443
|
+
tabRefs.current[index]?.focus();
|
|
7444
|
+
};
|
|
7445
|
+
const onTabKeyDown = (event, index) => {
|
|
7446
|
+
if (tabs.length === 0) return;
|
|
7447
|
+
let nextIndex = null;
|
|
7448
|
+
if (event.key === "ArrowRight") nextIndex = (index + 1) % tabs.length;
|
|
7449
|
+
else if (event.key === "ArrowLeft") nextIndex = (index - 1 + tabs.length) % tabs.length;
|
|
7450
|
+
else if (event.key === "Home") nextIndex = 0;
|
|
7451
|
+
else if (event.key === "End") nextIndex = tabs.length - 1;
|
|
7452
|
+
if (nextIndex === null) return;
|
|
7453
|
+
event.preventDefault();
|
|
7454
|
+
activateTab(nextIndex);
|
|
7455
|
+
};
|
|
7456
|
+
return /* @__PURE__ */ jsxs10(Fragment4, { children: [
|
|
7457
|
+
/* @__PURE__ */ jsxs10("div", { className: "grid shrink-0 grid-cols-[auto_minmax(0,1fr)_auto_auto] items-center gap-x-2 border-b border-og-border px-1.5 py-1 max-[1023px]:grid-cols-[auto_minmax(0,1fr)_auto] max-[1023px]:gap-y-0 max-[1023px]:px-2 max-[1023px]:pb-1 max-[1023px]:pt-0 min-[640px]:max-[1023px]:grid-cols-[auto_minmax(0,1fr)_auto_auto]", children: [
|
|
7458
|
+
/* @__PURE__ */ jsx12("div", { className: "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", children: leading ?? /* @__PURE__ */ jsx12("span", { className: "hidden truncate px-1 text-og-sm font-semibold text-og-fg max-[1023px]:inline", children: "Workspace" }) }),
|
|
7459
|
+
/* @__PURE__ */ jsx12(
|
|
7460
|
+
"div",
|
|
7461
|
+
{
|
|
7462
|
+
ref: tabListRef,
|
|
7463
|
+
className: "flex min-w-0 items-center gap-0.5 overflow-x-auto [scrollbar-width:none] [&::-webkit-scrollbar]:hidden max-[1023px]:col-span-3 max-[1023px]:row-start-2 max-[1023px]:w-full min-[640px]:max-[1023px]:col-span-1 min-[640px]:max-[1023px]:col-start-2 min-[640px]:max-[1023px]:row-start-1",
|
|
7464
|
+
role: "tablist",
|
|
7465
|
+
"aria-orientation": "horizontal",
|
|
7466
|
+
style: {
|
|
7467
|
+
maskImage: tabMask,
|
|
7468
|
+
WebkitMaskImage: tabMask,
|
|
7469
|
+
scrollPaddingInline: "16px"
|
|
7470
|
+
},
|
|
7471
|
+
children: tabs.map((tab, index) => /* @__PURE__ */ jsxs10(
|
|
7472
|
+
"button",
|
|
7473
|
+
{
|
|
7474
|
+
ref: (node) => {
|
|
7475
|
+
tabRefs.current[index] = node;
|
|
7476
|
+
},
|
|
7477
|
+
id: `${tabsetId}-tab-${index}`,
|
|
7478
|
+
type: "button",
|
|
7479
|
+
role: "tab",
|
|
7480
|
+
"aria-selected": tab.id === current,
|
|
7481
|
+
"aria-controls": `${tabsetId}-panel-${index}`,
|
|
7482
|
+
tabIndex: tab.id === current ? 0 : -1,
|
|
7483
|
+
onClick: () => onTab(tab.id),
|
|
7484
|
+
onKeyDown: (event) => onTabKeyDown(event, index),
|
|
7485
|
+
className: cn(
|
|
7486
|
+
"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-none focus-visible:ring-2 focus-visible:ring-og-accent max-[1023px]:min-h-11 max-[1023px]:min-w-11 pointer-coarse:min-h-11 pointer-coarse:min-w-11",
|
|
7487
|
+
tab.id === current ? "bg-og-accent-soft text-og-fg" : "text-og-fg-subtle hover:text-og-fg"
|
|
7488
|
+
),
|
|
7489
|
+
children: [
|
|
7490
|
+
/* @__PURE__ */ jsx12("span", { "data-contrast-audited": true, children: tab.label }),
|
|
7491
|
+
tab.badge
|
|
7492
|
+
]
|
|
7493
|
+
},
|
|
7494
|
+
tab.id
|
|
7495
|
+
))
|
|
7496
|
+
}
|
|
7497
|
+
),
|
|
7498
|
+
/* @__PURE__ */ jsx12("div", { className: "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", children: accessory }),
|
|
7499
|
+
/* @__PURE__ */ jsx12("div", { className: "flex shrink-0 items-center gap-0.5 text-og-fg-subtle min-[640px]:max-[1023px]:col-start-4 min-[640px]:max-[1023px]:row-start-1", children: controls })
|
|
7488
7500
|
] }),
|
|
7489
|
-
|
|
7490
|
-
|
|
7491
|
-
|
|
7492
|
-
|
|
7493
|
-
|
|
7494
|
-
|
|
7495
|
-
|
|
7496
|
-
"
|
|
7497
|
-
|
|
7498
|
-
|
|
7499
|
-
|
|
7500
|
-
|
|
7501
|
+
/* @__PURE__ */ jsx12("div", { className: "min-h-0 min-w-0 flex-1 overflow-hidden", children: tabs.length > 0 ? tabs.map((tab, index) => {
|
|
7502
|
+
const selected = tab.id === activeId;
|
|
7503
|
+
const shouldMount = selected || visitedTabs.has(tab.id);
|
|
7504
|
+
return /* @__PURE__ */ jsx12(
|
|
7505
|
+
"div",
|
|
7506
|
+
{
|
|
7507
|
+
id: `${tabsetId}-panel-${index}`,
|
|
7508
|
+
"aria-labelledby": `${tabsetId}-tab-${index}`,
|
|
7509
|
+
className: "h-full min-h-0 min-w-0 overflow-hidden",
|
|
7510
|
+
hidden: !selected,
|
|
7511
|
+
role: "tabpanel",
|
|
7512
|
+
children: shouldMount ? tab.content : null
|
|
7513
|
+
},
|
|
7514
|
+
tab.id
|
|
7515
|
+
);
|
|
7516
|
+
}) : /* @__PURE__ */ jsx12("div", { className: "h-full min-h-0 min-w-0", "aria-label": "Workspace", role: "tabpanel" }) })
|
|
7501
7517
|
] });
|
|
7502
7518
|
}
|
|
7503
|
-
function LayoutToggle({
|
|
7504
|
-
layout,
|
|
7505
|
-
onChange
|
|
7506
|
-
}) {
|
|
7507
|
-
return /* @__PURE__ */ jsx12("div", { className: "inline-flex items-center rounded-og-sm border border-og-border p-0.5", children: ["unified", "split"].map((value) => /* @__PURE__ */ jsx12(
|
|
7508
|
-
"button",
|
|
7509
|
-
{
|
|
7510
|
-
type: "button",
|
|
7511
|
-
onClick: () => onChange(value),
|
|
7512
|
-
className: cn(
|
|
7513
|
-
"min-h-7 rounded-og-xs px-1.5 py-0.5 text-og-xs capitalize focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-og-accent max-[1023px]:min-w-11 pointer-coarse:min-h-11 pointer-coarse:min-w-11",
|
|
7514
|
-
layout === value ? "bg-og-accent-soft text-og-fg" : "text-og-fg-subtle hover:text-og-fg"
|
|
7515
|
-
),
|
|
7516
|
-
children: value
|
|
7517
|
-
},
|
|
7518
|
-
value
|
|
7519
|
-
)) });
|
|
7520
|
-
}
|
|
7521
7519
|
|
|
7522
7520
|
// src/components/sandbox-workspace.tsx
|
|
7521
|
+
import { useCallback as useCallback22, useEffect as useEffect23, useMemo as useMemo11, useRef as useRef20, useState as useState20 } from "react";
|
|
7522
|
+
import { Popover } from "radix-ui";
|
|
7523
|
+
import {
|
|
7524
|
+
CircleCheckIcon,
|
|
7525
|
+
CpuIcon,
|
|
7526
|
+
LaptopIcon,
|
|
7527
|
+
LoaderCircleIcon as LoaderCircleIcon3,
|
|
7528
|
+
RefreshCwIcon as RefreshCwIcon2,
|
|
7529
|
+
TriangleAlertIcon as TriangleAlertIcon2
|
|
7530
|
+
} from "lucide-react";
|
|
7523
7531
|
import { jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
7524
7532
|
var WORKBENCH_TAB_CHANGES = "changes";
|
|
7525
7533
|
var WORKBENCH_TAB_FILES = "files";
|
|
@@ -8413,6 +8421,7 @@ export {
|
|
|
8413
8421
|
FleetTile,
|
|
8414
8422
|
HEALTH_TOKEN,
|
|
8415
8423
|
HumanInputForm,
|
|
8424
|
+
HumanInputSurface,
|
|
8416
8425
|
INITIAL_TRANSCRIPTION_CONTROL_STATE,
|
|
8417
8426
|
LightboxProvider,
|
|
8418
8427
|
MACHINE_STATE_BADGE_META,
|
|
@@ -8471,6 +8480,7 @@ export {
|
|
|
8471
8480
|
WORKBENCH_TAB_DESKTOP,
|
|
8472
8481
|
WORKBENCH_TAB_FILES,
|
|
8473
8482
|
WORKBENCH_TAB_TERMINAL,
|
|
8483
|
+
WorkbenchChanges,
|
|
8474
8484
|
WorkspaceDock,
|
|
8475
8485
|
advancedSourceSummary,
|
|
8476
8486
|
answersFromDrafts,
|