@opengeni/react 0.13.0 → 0.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (113) hide show
  1. package/README.md +129 -23
  2. package/dist/chunk-6BXBGJ3B.js +1956 -0
  3. package/dist/chunk-6BXBGJ3B.js.map +1 -0
  4. package/dist/chunk-6UT5OC3P.js +2133 -0
  5. package/dist/chunk-6UT5OC3P.js.map +1 -0
  6. package/dist/chunk-EBTT3MYN.js +92 -0
  7. package/dist/chunk-EBTT3MYN.js.map +1 -0
  8. package/dist/chunk-HHFA4AFX.js +425 -0
  9. package/dist/chunk-HHFA4AFX.js.map +1 -0
  10. package/dist/chunk-LAZ2A743.js +2393 -0
  11. package/dist/chunk-LAZ2A743.js.map +1 -0
  12. package/dist/chunk-MRSECXRP.js +563 -0
  13. package/dist/chunk-MRSECXRP.js.map +1 -0
  14. package/dist/chunk-TK7G6XLT.js +18 -0
  15. package/dist/chunk-TK7G6XLT.js.map +1 -0
  16. package/dist/composer-DvUaa5ki.d.ts +585 -0
  17. package/dist/composer.d.ts +6 -0
  18. package/dist/composer.js +56 -0
  19. package/dist/composer.js.map +1 -0
  20. package/dist/index.d.ts +642 -1080
  21. package/dist/index.js +7672 -8005
  22. package/dist/index.js.map +1 -1
  23. package/dist/machines.d.ts +412 -3
  24. package/dist/machines.js +25 -1
  25. package/dist/noto-sans-arabic-loader-IA2T4X2A.js +21 -0
  26. package/dist/noto-sans-arabic-loader-IA2T4X2A.js.map +1 -0
  27. package/dist/noto-sans-jp-loader.generated-HFGLYBR3.js +292 -0
  28. package/dist/noto-sans-jp-loader.generated-HFGLYBR3.js.map +1 -0
  29. package/dist/queue-surface-implementation-NQMV2ML3.js +890 -0
  30. package/dist/queue-surface-implementation-NQMV2ML3.js.map +1 -0
  31. package/dist/session-Gd4iVPYw.d.ts +425 -0
  32. package/dist/session-context-DIFFlXKc.d.ts +52 -0
  33. package/dist/session.d.ts +4 -0
  34. package/dist/session.js +50 -0
  35. package/dist/session.js.map +1 -0
  36. package/dist/use-composer-BCfm4mzX.d.ts +112 -0
  37. package/package.json +23 -4
  38. package/src/approvals.ts +5 -3
  39. package/src/client.ts +50 -1
  40. package/src/components/approval-surface.tsx +198 -0
  41. package/src/components/chat-composer.tsx +104 -751
  42. package/src/components/code-editor.tsx +104 -26
  43. package/src/components/command-palette.tsx +8 -2
  44. package/src/components/composer-transcription-control.tsx +211 -0
  45. package/src/components/composer.tsx +1536 -0
  46. package/src/components/desktop-viewer.tsx +3 -3
  47. package/src/components/enrollment-consent.tsx +2 -2
  48. package/src/components/file-browser.tsx +367 -29
  49. package/src/components/human-input-form.tsx +417 -0
  50. package/src/components/machine-card.tsx +82 -16
  51. package/src/components/machine-health-pill.tsx +68 -0
  52. package/src/components/machine-metrics.tsx +10 -24
  53. package/src/components/machines/health.ts +146 -0
  54. package/src/components/machines/machine-detail.tsx +220 -0
  55. package/src/components/machines/metric-history-chart.tsx +298 -0
  56. package/src/components/machines/metric-sparkline.tsx +76 -0
  57. package/src/components/machines/series.ts +113 -0
  58. package/src/components/machines-dashboard.tsx +16 -4
  59. package/src/components/message-timeline.tsx +15 -7
  60. package/src/components/model-picker.tsx +12 -3
  61. package/src/components/pierre-diff.tsx +32 -6
  62. package/src/components/queue-surface-implementation.tsx +1041 -0
  63. package/src/components/queue-surface-state.tsx +94 -0
  64. package/src/components/queue-surface.tsx +60 -0
  65. package/src/components/sandbox-files.tsx +230 -21
  66. package/src/components/sandbox-terminal.tsx +8 -2
  67. package/src/components/sandbox-workspace.tsx +489 -135
  68. package/src/components/session-status.tsx +0 -6
  69. package/src/components/workbench-changes.tsx +145 -50
  70. package/src/components/workspace-dock.tsx +329 -68
  71. package/src/composer.ts +60 -0
  72. package/src/hooks/internal.ts +115 -34
  73. package/src/hooks/use-composer.ts +635 -74
  74. package/src/hooks/use-human-input.ts +131 -0
  75. package/src/hooks/use-machine-chip.ts +2 -2
  76. package/src/hooks/use-machines.ts +27 -11
  77. package/src/hooks/use-sandbox-files.ts +310 -62
  78. package/src/hooks/use-sandbox-git.ts +154 -40
  79. package/src/hooks/use-sandbox-terminal.ts +28 -6
  80. package/src/hooks/use-session-capabilities.ts +38 -9
  81. package/src/hooks/use-session-control.ts +43 -15
  82. package/src/hooks/use-session-events.ts +523 -48
  83. package/src/hooks/use-session-lineage.ts +15 -6
  84. package/src/hooks/use-session.ts +10 -2
  85. package/src/hooks/use-slash-commands.ts +38 -38
  86. package/src/hooks/use-transcription.ts +757 -0
  87. package/src/hooks/use-turn-queue.ts +273 -77
  88. package/src/hooks/use-windowed-sections.ts +2 -2
  89. package/src/hooks/use-workspace-capture.ts +146 -40
  90. package/src/hooks/use-workspace-edit.ts +50 -14
  91. package/src/hooks/use-workspace-sessions.ts +3 -0
  92. package/src/human-input.ts +72 -0
  93. package/src/index.ts +88 -12
  94. package/src/lib/noto-sans-arabic-loader.ts +21 -0
  95. package/src/lib/noto-sans-jp-loader.generated.ts +295 -0
  96. package/src/lib/use-portal-token-style.ts +51 -0
  97. package/src/lib/use-unicode-fonts.ts +57 -0
  98. package/src/machines.ts +16 -0
  99. package/src/provider.tsx +163 -41
  100. package/src/session-context.ts +135 -0
  101. package/src/session.ts +79 -0
  102. package/src/timeline/parsers.ts +78 -6
  103. package/src/timeline/projection.ts +36 -6
  104. package/src/timeline/tool-renderers.tsx +41 -0
  105. package/src/timeline/turn-summary.tsx +2 -2
  106. package/src/timeline/types.ts +16 -8
  107. package/styles/index.css +78 -0
  108. package/styles/index.d.ts +2 -0
  109. package/styles/tokens.css +11 -6
  110. package/styles/tokens.d.ts +2 -0
  111. package/dist/chunk-NFYVQWIB.js +0 -1377
  112. package/dist/chunk-NFYVQWIB.js.map +0 -1
  113. package/dist/machines-CnlMb7E-.d.ts +0 -329
@@ -0,0 +1,890 @@
1
+ import {
2
+ QueueErrorAlert,
3
+ QueueStoppingStatus
4
+ } from "./chunk-EBTT3MYN.js";
5
+
6
+ // src/components/queue-surface-implementation.tsx
7
+ import {
8
+ DndContext,
9
+ DragOverlay,
10
+ PointerSensor,
11
+ closestCenter,
12
+ useSensor,
13
+ useSensors
14
+ } from "@dnd-kit/core";
15
+ import {
16
+ SortableContext,
17
+ arrayMove,
18
+ useSortable,
19
+ verticalListSortingStrategy
20
+ } from "@dnd-kit/sortable";
21
+ import { CSS } from "@dnd-kit/utilities";
22
+ import {
23
+ ArrowDownToLineIcon,
24
+ ArrowUpToLineIcon,
25
+ ChevronDownIcon,
26
+ EllipsisIcon,
27
+ GripVerticalIcon,
28
+ Loader2Icon,
29
+ PencilIcon,
30
+ Trash2Icon,
31
+ ZapIcon
32
+ } from "lucide-react";
33
+ import {
34
+ useCallback,
35
+ useId,
36
+ useMemo,
37
+ useRef,
38
+ useState
39
+ } from "react";
40
+ import { DropdownMenu } from "radix-ui";
41
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
42
+ function queueComposerCheckoutEnabled(composer, readOnly) {
43
+ return !readOnly && composer !== void 0 && composer.draftPersistence !== "disabled";
44
+ }
45
+ function QueueSurface({
46
+ queue,
47
+ composer,
48
+ readOnly = false,
49
+ onRequestComposerFocus
50
+ }) {
51
+ const [open, setOpen] = useState(false);
52
+ const [replaceDraftFor, setReplaceDraftFor] = useState(null);
53
+ const [announcement, setAnnouncement] = useState("");
54
+ const [draggedTurnId, setDraggedTurnId] = useState(null);
55
+ const surfaceRef = useRef(null);
56
+ const [keyboardDrag, setKeyboardDrag] = useState(null);
57
+ const count = queue.queue.length;
58
+ const canEditInComposer = queueComposerCheckoutEnabled(composer, readOnly);
59
+ const collapsedPreview = useMemo(
60
+ () => queuePromptPreview(queue.queue[0]?.prompt ?? "", QUEUE_COLLAPSED_PREVIEW_CHARACTERS),
61
+ [queue.queue]
62
+ );
63
+ const sensors = useSensors(useSensor(PointerSensor, { activationConstraint: { distance: 6 } }));
64
+ const displayedQueue = useMemo(() => {
65
+ if (!keyboardDrag) return queue.queue;
66
+ const oldIndex = queue.queue.findIndex((turn) => turn.id === keyboardDrag.turnId);
67
+ if (oldIndex < 0) return queue.queue;
68
+ return arrayMove(queue.queue, oldIndex, keyboardDrag.projectedIndex);
69
+ }, [keyboardDrag, queue.queue]);
70
+ const ids = useMemo(() => displayedQueue.map((turn) => turn.id), [displayedQueue]);
71
+ const moveToIndex = useCallback(
72
+ async (turnId, nextIndex) => {
73
+ const oldIndex = queue.queue.findIndex((turn) => turn.id === turnId);
74
+ if (oldIndex < 0) return;
75
+ const boundedIndex = Math.max(0, Math.min(nextIndex, queue.queue.length - 1));
76
+ if (oldIndex === boundedIndex) return;
77
+ const ordered = arrayMove(queue.queue, oldIndex, boundedIndex);
78
+ const beforeTurnId = ordered[boundedIndex + 1]?.id ?? null;
79
+ const moved = await queue.moveTurn(turnId, beforeTurnId);
80
+ setAnnouncement(
81
+ moved ? `Queued prompt moved to position ${boundedIndex + 1}.` : "The queue changed before that prompt could be moved. Refreshed server order."
82
+ );
83
+ if (moved) focusQueueTurn(surfaceRef.current, turnId);
84
+ },
85
+ [queue]
86
+ );
87
+ const onDragEnd = useCallback(
88
+ (event) => {
89
+ setDraggedTurnId(null);
90
+ const activeId = String(event.active.id);
91
+ const overId = event.over ? String(event.over.id) : null;
92
+ if (!overId || activeId === overId) {
93
+ setAnnouncement("Queued prompt returned to its original position.");
94
+ return;
95
+ }
96
+ const nextIndex = queue.queue.findIndex((turn) => turn.id === overId);
97
+ if (nextIndex >= 0) void moveToIndex(activeId, nextIndex);
98
+ },
99
+ [moveToIndex, queue.queue]
100
+ );
101
+ const onDragStart = useCallback(
102
+ (event) => {
103
+ setKeyboardDrag(null);
104
+ const turnId = String(event.active.id);
105
+ const position = queue.queue.findIndex((turn) => turn.id === turnId) + 1;
106
+ setDraggedTurnId(turnId);
107
+ setAnnouncement(`Dragging queued prompt ${position} of ${queue.queue.length}.`);
108
+ },
109
+ [queue.queue]
110
+ );
111
+ const onHandleKeyDown = useCallback(
112
+ (event, turnId) => {
113
+ const canonicalIndex = queue.queue.findIndex((turn) => turn.id === turnId);
114
+ if (canonicalIndex < 0) return;
115
+ if (!keyboardDrag) {
116
+ if (event.key !== " ") return;
117
+ event.preventDefault();
118
+ setKeyboardDrag({ turnId, projectedIndex: canonicalIndex });
119
+ setAnnouncement(
120
+ `Lifted queued prompt ${canonicalIndex + 1} of ${count}. Use arrow keys to move it, then press Space to drop.`
121
+ );
122
+ return;
123
+ }
124
+ if (keyboardDrag.turnId !== turnId) return;
125
+ if (event.key === "Escape") {
126
+ event.preventDefault();
127
+ setKeyboardDrag(null);
128
+ setAnnouncement("Queue reorder cancelled.");
129
+ return;
130
+ }
131
+ if (event.key === " ") {
132
+ event.preventDefault();
133
+ const targetIndex = keyboardDrag.projectedIndex;
134
+ setAnnouncement(`Moving queued prompt to position ${targetIndex + 1}.`);
135
+ void moveToIndex(turnId, targetIndex).finally(() => setKeyboardDrag(null));
136
+ return;
137
+ }
138
+ const direction = event.key === "ArrowUp" ? -1 : event.key === "ArrowDown" ? 1 : 0;
139
+ const edge = event.key === "Home" ? 0 : event.key === "End" ? count - 1 : null;
140
+ if (direction === 0 && edge === null) return;
141
+ event.preventDefault();
142
+ const projectedIndex = Math.max(
143
+ 0,
144
+ Math.min(edge ?? keyboardDrag.projectedIndex + direction, count - 1)
145
+ );
146
+ setKeyboardDrag({ turnId, projectedIndex });
147
+ setAnnouncement(`Queued prompt projected to position ${projectedIndex + 1} of ${count}.`);
148
+ },
149
+ [count, keyboardDrag, moveToIndex, queue.queue]
150
+ );
151
+ const edit = useCallback(
152
+ async (turn, replaceDraft) => {
153
+ if (!composer || !canEditInComposer) return;
154
+ const restored = await queue.editTurn(turn.id, {
155
+ expectedDraftRevision: composer.draftRevision,
156
+ replaceDraft
157
+ });
158
+ if (!restored) return;
159
+ composer.applyDraft(restored);
160
+ setReplaceDraftFor(null);
161
+ setAnnouncement("Queued prompt moved back to the composer for editing.");
162
+ window.requestAnimationFrame(() => {
163
+ if (onRequestComposerFocus) {
164
+ onRequestComposerFocus();
165
+ return;
166
+ }
167
+ const input = document.querySelector(
168
+ 'textarea[aria-label="Message the agent"]'
169
+ );
170
+ input?.scrollIntoView({ block: "nearest", behavior: "smooth" });
171
+ input?.focus();
172
+ });
173
+ },
174
+ [canEditInComposer, composer, onRequestComposerFocus, queue]
175
+ );
176
+ const requestEdit = useCallback(
177
+ (turn) => {
178
+ if (!composer || !canEditInComposer) return;
179
+ const draftDirty = composer.value.length > 0 || composer.restoredResources.length > 0 || (composer.draft?.tools.length ?? 0) > 0 || composer.draft?.sourceTurnId !== null && composer.draft?.sourceTurnId !== void 0;
180
+ if (draftDirty) {
181
+ setReplaceDraftFor(turn.id);
182
+ } else {
183
+ void edit(turn, false);
184
+ }
185
+ },
186
+ [canEditInComposer, composer, edit]
187
+ );
188
+ if (count === 0 && !queue.stoppingPreviousAttempt && !queue.error && !queue.mutationError)
189
+ return null;
190
+ return /* @__PURE__ */ jsxs(
191
+ "div",
192
+ {
193
+ ref: surfaceRef,
194
+ className: "mx-auto mb-2 w-full max-w-3xl shrink-0 px-4 sm:px-6",
195
+ "data-testid": "queue-surface",
196
+ children: [
197
+ /* @__PURE__ */ jsxs("div", { className: "overflow-hidden rounded-lg border border-border bg-surface/80 shadow-sm", children: [
198
+ queue.stoppingPreviousAttempt ? /* @__PURE__ */ jsx(QueueStoppingStatus, { queue, dividerAfter: true }) : null,
199
+ /* @__PURE__ */ jsxs(
200
+ "button",
201
+ {
202
+ type: "button",
203
+ className: "flex w-full min-w-0 flex-wrap items-center gap-2 px-3 py-2 text-left outline-none transition-colors hover:bg-surface-2/60 focus-visible:bg-surface-2/60 focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring/40 pointer-coarse:min-h-[44px]",
204
+ onClick: () => setOpen((value) => !value),
205
+ "aria-description": !open && count > 0 ? collapsedPreview.summary : void 0,
206
+ "aria-expanded": open,
207
+ "aria-label": `${count} queued prompt${count === 1 ? "" : "s"}${readOnly ? " Read-only" : ""}`,
208
+ children: [
209
+ /* @__PURE__ */ jsx(
210
+ ChevronDownIcon,
211
+ {
212
+ "aria-hidden": "true",
213
+ className: `size-3.5 shrink-0 text-fg-subtle transition-transform ${open ? "rotate-180" : ""}`
214
+ }
215
+ ),
216
+ /* @__PURE__ */ jsxs("span", { className: "text-xs font-medium text-fg", children: [
217
+ count,
218
+ " queued prompt",
219
+ count === 1 ? "" : "s"
220
+ ] }),
221
+ readOnly ? /* @__PURE__ */ jsx("span", { className: "shrink-0 rounded border border-border px-1.5 py-0.5 text-2xs text-fg-subtle", children: "Read-only" }) : null,
222
+ !open && count > 0 ? /* @__PURE__ */ jsx(
223
+ "span",
224
+ {
225
+ "aria-hidden": "true",
226
+ className: `max-w-full truncate text-fg-muted ${collapsedPreview.collapsedVisual === collapsedPreview.summary ? "min-w-0 flex-1 text-xs" : "shrink-0 text-[10px]"}`,
227
+ "data-testid": "queue-collapsed-preview",
228
+ dir: "auto",
229
+ children: collapsedPreview.collapsedVisual
230
+ }
231
+ ) : null,
232
+ queue.loading ? /* @__PURE__ */ jsx(Loader2Icon, { className: "ml-auto size-3.5 animate-spin" }) : null
233
+ ]
234
+ }
235
+ ),
236
+ open && count > 0 && readOnly ? /* @__PURE__ */ jsx(
237
+ "ol",
238
+ {
239
+ className: "max-h-[min(30rem,60dvh)] divide-y divide-border overflow-y-auto overscroll-contain border-t border-border",
240
+ "aria-label": "Queued prompts",
241
+ "data-testid": "queue-list",
242
+ children: queue.queue.map((turn, index) => /* @__PURE__ */ jsx(
243
+ ReadOnlyQueueRow,
244
+ {
245
+ turn,
246
+ index,
247
+ onDisclosureChange: (expanded) => setAnnouncement(
248
+ `Full content for queued prompt ${index + 1} ${expanded ? "shown" : "hidden"}.`
249
+ )
250
+ },
251
+ turn.id
252
+ ))
253
+ }
254
+ ) : null,
255
+ open && count > 0 && !readOnly ? /* @__PURE__ */ jsxs(
256
+ DndContext,
257
+ {
258
+ sensors,
259
+ collisionDetection: closestCenter,
260
+ modifiers: [verticalOnly],
261
+ onDragStart,
262
+ onDragCancel: () => {
263
+ setDraggedTurnId(null);
264
+ setAnnouncement("Queue reorder cancelled.");
265
+ },
266
+ onDragEnd,
267
+ children: [
268
+ /* @__PURE__ */ jsx(SortableContext, { items: ids, strategy: verticalListSortingStrategy, children: /* @__PURE__ */ jsx(
269
+ "ol",
270
+ {
271
+ className: "max-h-[min(30rem,60dvh)] divide-y divide-border overflow-y-auto overscroll-contain border-t border-border",
272
+ "aria-label": "Queued prompts",
273
+ "data-testid": "queue-list",
274
+ children: displayedQueue.map((turn, index) => /* @__PURE__ */ jsx(
275
+ SortableQueueRow,
276
+ {
277
+ turn,
278
+ index,
279
+ count,
280
+ pending: queue.mutationFor(turn.id),
281
+ confirmingReplace: replaceDraftFor === turn.id,
282
+ keyboardDragging: keyboardDrag?.turnId === turn.id,
283
+ onHandleKeyDown: (event) => onHandleKeyDown(event, turn.id),
284
+ onMove: (nextIndex) => void moveToIndex(turn.id, nextIndex),
285
+ onEdit: canEditInComposer ? () => requestEdit(turn) : void 0,
286
+ onConfirmReplace: () => void edit(turn, true),
287
+ onCancelReplace: () => setReplaceDraftFor(null),
288
+ onSteer: () => {
289
+ void queue.steerTurn(turn.id).then((steered) => {
290
+ setAnnouncement(
291
+ steered ? "Queued prompt is now the next direction." : "That prompt changed before it could be steered."
292
+ );
293
+ if (steered) {
294
+ focusAfterQueueRemoval(surfaceRef.current, index, onRequestComposerFocus);
295
+ }
296
+ });
297
+ },
298
+ onDelete: () => {
299
+ void queue.removeTurn(turn.id).then((removed) => {
300
+ setAnnouncement(
301
+ removed ? "Queued prompt deleted." : "That prompt changed before it could be deleted."
302
+ );
303
+ if (removed) {
304
+ focusAfterQueueRemoval(surfaceRef.current, index, onRequestComposerFocus);
305
+ }
306
+ });
307
+ },
308
+ onDisclosureChange: (expanded) => setAnnouncement(
309
+ `Full content for queued prompt ${index + 1} ${expanded ? "shown" : "hidden"}.`
310
+ )
311
+ },
312
+ turn.id
313
+ ))
314
+ }
315
+ ) }),
316
+ /* @__PURE__ */ jsx(DragOverlay, { modifiers: [verticalOnly], children: draggedTurnId ? /* @__PURE__ */ jsx(
317
+ "div",
318
+ {
319
+ className: "max-h-20 w-[min(36rem,calc(100vw-2rem))] max-w-[calc(100vw-2rem)] overflow-hidden rounded-md border border-brand/40 bg-surface px-3 py-2 text-xs text-fg shadow-lg",
320
+ "data-testid": "queue-drag-overlay",
321
+ children: /* @__PURE__ */ jsx(
322
+ "p",
323
+ {
324
+ "aria-hidden": "true",
325
+ className: "line-clamp-3 break-all whitespace-pre-wrap [unicode-bidi:plaintext]",
326
+ dir: "auto",
327
+ children: queuePromptPreview(
328
+ queue.queue.find((turn) => turn.id === draggedTurnId)?.prompt ?? "",
329
+ QUEUE_ROW_PREVIEW_CHARACTERS
330
+ ).summary
331
+ }
332
+ )
333
+ }
334
+ ) : null })
335
+ ]
336
+ }
337
+ ) : null,
338
+ queue.error || queue.mutationError ? /* @__PURE__ */ jsx(QueueErrorAlert, { queue, dividerBefore: true }) : null
339
+ ] }),
340
+ /* @__PURE__ */ jsx("p", { className: "sr-only", "aria-live": "polite", "aria-atomic": "true", children: announcement })
341
+ ]
342
+ }
343
+ );
344
+ }
345
+ var QUEUE_ROW_PREVIEW_CHARACTERS = 360;
346
+ var QUEUE_COLLAPSED_PREVIEW_CHARACTERS = 180;
347
+ var QUEUE_PREVIEW_GRAPHEME_CONTEXT_CHARACTERS = 32;
348
+ var QUEUE_PREVIEW_REFERENCE_SAMPLE_CHARACTERS = 32;
349
+ var QUEUE_VISIBLE_END_IDENTITY_CHARACTERS = 18;
350
+ var QUEUE_PREVIEW_SEPARATOR = " \u2026 ";
351
+ var queuePreviewSegmenter = new Intl.Segmenter(void 0, { granularity: "grapheme" });
352
+ var queuePromptNonRenderingCodePoint = /[\p{White_Space}\p{Control}\p{Default_Ignorable_Code_Point}]/u;
353
+ function queuePromptPreview(prompt, maxCharacters) {
354
+ const wholePromptProbe = samplePromptStart(prompt, maxCharacters + 1);
355
+ if (!wholePromptProbe.truncated && wholePromptProbe.characters <= maxCharacters) {
356
+ const summary2 = replaceLoneSurrogates(wholePromptProbe.value);
357
+ return hasVisiblePromptContent(summary2) ? {
358
+ summary: summary2,
359
+ collapsedVisual: summary2,
360
+ visibleStart: summary2,
361
+ visibleIdentity: null,
362
+ visibleIdentityLabel: null,
363
+ isFallback: false
364
+ } : fallbackPromptPreview(prompt.length, wholePromptProbe.value, wholePromptProbe.value);
365
+ }
366
+ const suffixCharacters = Math.min(Math.floor(maxCharacters / 3), 120);
367
+ const prefixCharacters = maxCharacters - codePointLength(QUEUE_PREVIEW_SEPARATOR) - suffixCharacters;
368
+ const prefixSample = samplePromptStart(
369
+ prompt,
370
+ prefixCharacters + QUEUE_PREVIEW_GRAPHEME_CONTEXT_CHARACTERS
371
+ );
372
+ const suffixSample = samplePromptEnd(
373
+ prompt,
374
+ suffixCharacters + QUEUE_PREVIEW_GRAPHEME_CONTEXT_CHARACTERS
375
+ );
376
+ const prefixSegments = segmentPromptSample(prefixSample.value);
377
+ const suffixSegments = segmentPromptSample(suffixSample.value);
378
+ if (prefixSample.truncated) prefixSegments.pop();
379
+ if (suffixSample.truncated) {
380
+ let ambiguousSegment = suffixSegments.shift();
381
+ while (ambiguousSegment?.endsWith("\u200D") && suffixSegments.length > 0) {
382
+ ambiguousSegment = suffixSegments.shift();
383
+ }
384
+ while (suffixSegments[0] && startsWithRegionalIndicator(suffixSegments[0])) {
385
+ suffixSegments.shift();
386
+ }
387
+ }
388
+ const prefix = takeWholeGraphemesFromStart(prefixSegments, prefixCharacters);
389
+ const suffix = takeWholeGraphemesFromEnd(suffixSegments, suffixCharacters);
390
+ const reference = boundedPromptSampleReference(
391
+ prompt.length,
392
+ prefixSample.value,
393
+ suffixSample.value
394
+ );
395
+ if (!hasVisiblePromptContent(prefix) && !hasVisiblePromptContent(suffix)) {
396
+ return fallbackPromptPreview(prompt.length, prefixSample.value, suffixSample.value);
397
+ }
398
+ const safeSuffix = hasVisiblePromptContent(suffix) ? suffix : promptPreviewFallbackLabel(reference);
399
+ const summary = `${prefix}${QUEUE_PREVIEW_SEPARATOR}${safeSuffix}`;
400
+ if (!hasVisiblePromptContent(prefix)) {
401
+ return {
402
+ summary,
403
+ collapsedVisual: summary,
404
+ visibleStart: safeSuffix,
405
+ visibleIdentity: null,
406
+ visibleIdentityLabel: null,
407
+ isFallback: false
408
+ };
409
+ }
410
+ const endIdentity = takeWholeGraphemesFromEnd(
411
+ suffixSegments,
412
+ QUEUE_VISIBLE_END_IDENTITY_CHARACTERS
413
+ );
414
+ return {
415
+ summary,
416
+ collapsedVisual: summary,
417
+ visibleStart: prefix,
418
+ visibleIdentity: hasVisiblePromptContent(endIdentity) ? endIdentity : `ref ${reference}`,
419
+ visibleIdentityLabel: hasVisiblePromptContent(endIdentity) ? "End" : "Safe boundary",
420
+ isFallback: false
421
+ };
422
+ }
423
+ function fallbackPromptPreview(promptLength, startSample, endSample) {
424
+ const reference = boundedPromptSampleReference(promptLength, startSample, endSample);
425
+ const summary = promptPreviewFallbackLabel(reference);
426
+ return {
427
+ summary,
428
+ collapsedVisual: `Omitted \xB7 ${reference}`,
429
+ // The complete fallback remains the canonical accessible summary. Narrow
430
+ // collapsed/row layouts paint the bounded reference in a compact truthful
431
+ // form so ellipsis cannot hide the only identifying portion.
432
+ visibleStart: `Omitted \xB7 ${reference}`,
433
+ visibleIdentity: null,
434
+ visibleIdentityLabel: null,
435
+ isFallback: true
436
+ };
437
+ }
438
+ function promptPreviewFallbackLabel(reference) {
439
+ return `Content omitted at safe boundary \xB7 ref ${reference}`;
440
+ }
441
+ function boundedPromptSampleReference(promptLength, startSample, endSample) {
442
+ const head = samplePromptStart(startSample, QUEUE_PREVIEW_REFERENCE_SAMPLE_CHARACTERS).value;
443
+ const tail = samplePromptEnd(endSample, QUEUE_PREVIEW_REFERENCE_SAMPLE_CHARACTERS).value;
444
+ let hash = 2166136261;
445
+ for (const value of [String(promptLength), head, tail]) {
446
+ for (let index = 0; index < value.length; index += 1) {
447
+ hash ^= value.charCodeAt(index);
448
+ hash = Math.imul(hash, 16777619);
449
+ }
450
+ hash ^= 65535;
451
+ hash = Math.imul(hash, 16777619);
452
+ }
453
+ return (hash >>> 0).toString(16).padStart(8, "0").toUpperCase();
454
+ }
455
+ function hasVisiblePromptContent(value) {
456
+ for (const character of value) {
457
+ if (!queuePromptNonRenderingCodePoint.test(character)) return true;
458
+ }
459
+ return false;
460
+ }
461
+ function samplePromptStart(prompt, maxCharacters) {
462
+ let end = 0;
463
+ let characters = 0;
464
+ while (end < prompt.length && characters < maxCharacters) {
465
+ const first = prompt.charCodeAt(end);
466
+ end += isHighSurrogate(first) && end + 1 < prompt.length && isLowSurrogate(prompt.charCodeAt(end + 1)) ? 2 : 1;
467
+ characters += 1;
468
+ }
469
+ return { value: prompt.slice(0, end), characters, truncated: end < prompt.length };
470
+ }
471
+ function samplePromptEnd(prompt, maxCharacters) {
472
+ let start = prompt.length;
473
+ let characters = 0;
474
+ while (start > 0 && characters < maxCharacters) {
475
+ start -= 1;
476
+ if (isLowSurrogate(prompt.charCodeAt(start)) && start > 0 && isHighSurrogate(prompt.charCodeAt(start - 1))) {
477
+ start -= 1;
478
+ }
479
+ characters += 1;
480
+ }
481
+ return { value: prompt.slice(start), characters, truncated: start > 0 };
482
+ }
483
+ function segmentPromptSample(sample) {
484
+ return Array.from(
485
+ queuePreviewSegmenter.segment(replaceLoneSurrogates(sample)),
486
+ ({ segment }) => segment
487
+ );
488
+ }
489
+ function replaceLoneSurrogates(value) {
490
+ let sanitized = "";
491
+ for (let index = 0; index < value.length; index += 1) {
492
+ const current = value.charCodeAt(index);
493
+ if (isHighSurrogate(current) && index + 1 < value.length && isLowSurrogate(value.charCodeAt(index + 1))) {
494
+ sanitized += value.slice(index, index + 2);
495
+ index += 1;
496
+ } else if (isHighSurrogate(current) || isLowSurrogate(current)) {
497
+ sanitized += "\uFFFD";
498
+ } else {
499
+ sanitized += value[index];
500
+ }
501
+ }
502
+ return sanitized;
503
+ }
504
+ function takeWholeGraphemesFromStart(segments, maxCharacters) {
505
+ const selected = [];
506
+ let characters = 0;
507
+ for (const segment of segments) {
508
+ const segmentCharacters = codePointLength(segment);
509
+ if (characters + segmentCharacters > maxCharacters) break;
510
+ selected.push(segment);
511
+ characters += segmentCharacters;
512
+ }
513
+ while (selected.at(-1)?.trim().length === 0) selected.pop();
514
+ return selected.join("");
515
+ }
516
+ function takeWholeGraphemesFromEnd(segments, maxCharacters) {
517
+ const selected = [];
518
+ let characters = 0;
519
+ for (let index = segments.length - 1; index >= 0; index -= 1) {
520
+ const segment = segments[index];
521
+ if (!segment) continue;
522
+ const segmentCharacters = codePointLength(segment);
523
+ if (characters + segmentCharacters > maxCharacters) break;
524
+ selected.unshift(segment);
525
+ characters += segmentCharacters;
526
+ }
527
+ while (selected[0]?.trim().length === 0) selected.shift();
528
+ while (selected.at(-1)?.trim().length === 0) selected.pop();
529
+ return selected.join("");
530
+ }
531
+ function codePointLength(value) {
532
+ let characters = 0;
533
+ for (const _character of value) characters += 1;
534
+ return characters;
535
+ }
536
+ function startsWithRegionalIndicator(value) {
537
+ const codePoint = value.codePointAt(0);
538
+ return codePoint !== void 0 && codePoint >= 127462 && codePoint <= 127487;
539
+ }
540
+ function isHighSurrogate(codeUnit) {
541
+ return codeUnit >= 55296 && codeUnit <= 56319;
542
+ }
543
+ function isLowSurrogate(codeUnit) {
544
+ return codeUnit >= 56320 && codeUnit <= 57343;
545
+ }
546
+ function QueuePrompt({
547
+ prompt,
548
+ index,
549
+ onDisclosureChange
550
+ }) {
551
+ const [expanded, setExpanded] = useState(false);
552
+ const fullContentId = useId();
553
+ const preview = useMemo(() => queuePromptPreview(prompt, QUEUE_ROW_PREVIEW_CHARACTERS), [prompt]);
554
+ return /* @__PURE__ */ jsxs("div", { className: "w-full min-w-0 max-w-full", children: [
555
+ /* @__PURE__ */ jsx(
556
+ "div",
557
+ {
558
+ "aria-label": `Queued prompt ${index + 1} summary: ${preview.summary}`,
559
+ className: "max-w-full overflow-hidden text-xs leading-5 text-fg",
560
+ "data-testid": `queue-prompt-preview-${index + 1}`,
561
+ dir: "auto",
562
+ role: "note",
563
+ children: /* @__PURE__ */ jsxs("span", { "aria-hidden": "true", className: "flex min-w-0 max-w-full items-baseline gap-2 sm:block", children: [
564
+ /* @__PURE__ */ jsx(
565
+ "span",
566
+ {
567
+ className: `${preview.isFallback ? "" : "line-clamp-1 sm:line-clamp-2"} min-w-0 flex-1 whitespace-pre-wrap break-all [unicode-bidi:plaintext]`,
568
+ "data-testid": `queue-prompt-start-${index + 1}`,
569
+ dir: "auto",
570
+ children: preview.visibleStart
571
+ }
572
+ ),
573
+ preview.visibleIdentity && preview.visibleIdentityLabel ? /* @__PURE__ */ jsxs(
574
+ "span",
575
+ {
576
+ className: "flex min-w-0 max-w-[70%] shrink-0 items-center gap-1 text-2xs leading-4 text-fg-muted sm:mt-0.5 sm:max-w-full",
577
+ "data-testid": `queue-prompt-identity-row-${index + 1}`,
578
+ children: [
579
+ /* @__PURE__ */ jsx("span", { className: "shrink-0 font-medium text-fg-subtle", children: preview.visibleIdentityLabel }),
580
+ /* @__PURE__ */ jsx(
581
+ "span",
582
+ {
583
+ className: "min-w-0 overflow-hidden text-ellipsis whitespace-nowrap font-mono [unicode-bidi:plaintext]",
584
+ "data-testid": `queue-prompt-identity-${index + 1}`,
585
+ dir: "auto",
586
+ children: preview.visibleIdentity
587
+ }
588
+ )
589
+ ]
590
+ }
591
+ ) : null
592
+ ] })
593
+ }
594
+ ),
595
+ /* @__PURE__ */ jsxs(
596
+ "button",
597
+ {
598
+ type: "button",
599
+ "aria-controls": fullContentId,
600
+ "aria-expanded": expanded,
601
+ "aria-label": `${expanded ? "Hide" : "Show"} full content for queued prompt ${index + 1}`,
602
+ className: "mt-1 inline-flex min-h-7 min-w-0 max-w-full items-center gap-1 whitespace-normal rounded-md text-left text-2xs font-medium text-fg-muted outline-none transition-colors hover:text-fg focus-visible:ring-2 focus-visible:ring-ring/40 pointer-coarse:min-h-[44px]",
603
+ "data-testid": `queue-prompt-disclosure-${index + 1}`,
604
+ onClick: () => {
605
+ const next = !expanded;
606
+ setExpanded(next);
607
+ onDisclosureChange(next);
608
+ },
609
+ children: [
610
+ /* @__PURE__ */ jsx(
611
+ ChevronDownIcon,
612
+ {
613
+ "aria-hidden": "true",
614
+ className: `size-3 shrink-0 transition-transform ${expanded ? "rotate-180" : ""}`
615
+ }
616
+ ),
617
+ expanded ? "Hide full prompt" : "View full prompt"
618
+ ]
619
+ }
620
+ ),
621
+ expanded ? /* @__PURE__ */ jsx(
622
+ "pre",
623
+ {
624
+ id: fullContentId,
625
+ role: "region",
626
+ "aria-label": `Full content for queued prompt ${index + 1}`,
627
+ className: "mt-1.5 max-h-64 w-full min-w-0 max-w-full overflow-auto overscroll-contain rounded-md border border-border bg-surface-2/60 p-2 whitespace-pre-wrap break-all font-mono text-xs leading-5 text-fg [unicode-bidi:plaintext]",
628
+ "data-testid": `queue-prompt-full-${index + 1}`,
629
+ dir: "auto",
630
+ tabIndex: 0,
631
+ children: prompt
632
+ }
633
+ ) : null
634
+ ] });
635
+ }
636
+ function ReadOnlyQueueRow({
637
+ turn,
638
+ index,
639
+ onDisclosureChange
640
+ }) {
641
+ return /* @__PURE__ */ jsxs("li", { className: "flex min-w-0 items-start gap-2 bg-surface px-3 py-2", children: [
642
+ /* @__PURE__ */ jsx("span", { className: "mt-1 shrink-0 font-mono text-2xs text-fg-subtle", children: index + 1 }),
643
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
644
+ /* @__PURE__ */ jsx(QueuePrompt, { prompt: turn.prompt, index, onDisclosureChange }),
645
+ /* @__PURE__ */ jsxs("div", { className: "mt-1 flex min-w-0 items-center gap-2 overflow-hidden whitespace-nowrap text-2xs text-fg-subtle", children: [
646
+ turn.resources.length > 0 ? /* @__PURE__ */ jsxs("span", { className: "shrink-0", children: [
647
+ turn.resources.length,
648
+ " resource",
649
+ turn.resources.length === 1 ? "" : "s"
650
+ ] }) : null,
651
+ turn.tools.length > 0 ? /* @__PURE__ */ jsxs("span", { className: "shrink-0", children: [
652
+ turn.tools.length,
653
+ " tool",
654
+ turn.tools.length === 1 ? "" : "s"
655
+ ] }) : null,
656
+ /* @__PURE__ */ jsx("span", { className: "min-w-0 truncate", children: turn.model }),
657
+ /* @__PURE__ */ jsx("span", { className: "shrink-0", children: turn.reasoningEffort })
658
+ ] })
659
+ ] })
660
+ ] });
661
+ }
662
+ function SortableQueueRow({
663
+ turn,
664
+ index,
665
+ count,
666
+ pending,
667
+ confirmingReplace,
668
+ keyboardDragging,
669
+ onHandleKeyDown,
670
+ onMove,
671
+ onEdit,
672
+ onConfirmReplace,
673
+ onCancelReplace,
674
+ onSteer,
675
+ onDelete,
676
+ onDisclosureChange
677
+ }) {
678
+ const sortable = useSortable({
679
+ id: turn.id,
680
+ disabled: pending !== null || keyboardDragging
681
+ });
682
+ return /* @__PURE__ */ jsxs(
683
+ "li",
684
+ {
685
+ "data-queue-turn-id": turn.id,
686
+ ref: sortable.setNodeRef,
687
+ style: {
688
+ transform: CSS.Transform.toString(sortable.transform),
689
+ transition: sortable.transition
690
+ },
691
+ className: `bg-surface ${sortable.isDragging || keyboardDragging ? "relative z-10 shadow-lg ring-1 ring-brand/40" : ""}`,
692
+ children: [
693
+ /* @__PURE__ */ jsxs("div", { className: "grid min-w-0 grid-cols-[auto_minmax(0,1fr)] items-start gap-x-1.5 px-2 py-1.5 sm:grid-cols-[auto_auto_minmax(0,1fr)_auto] sm:gap-x-2 sm:px-3 sm:py-2", children: [
694
+ /* @__PURE__ */ jsx(
695
+ "button",
696
+ {
697
+ "data-queue-handle": true,
698
+ ref: sortable.setActivatorNodeRef,
699
+ type: "button",
700
+ ...sortable.attributes,
701
+ ...sortable.listeners,
702
+ onKeyDown: onHandleKeyDown,
703
+ disabled: pending !== null,
704
+ className: "col-start-1 row-start-1 mt-0.5 inline-flex size-7 shrink-0 touch-none items-center justify-center rounded-md text-fg-subtle hover:bg-surface-2 hover:text-fg focus-visible:ring-2 focus-visible:ring-ring/40 pointer-coarse:size-[44px]",
705
+ "aria-label": `Reorder queued prompt ${index + 1}`,
706
+ title: "Drag to reorder. Press Space, arrows, then Space to drop.",
707
+ children: /* @__PURE__ */ jsx(GripVerticalIcon, { className: "size-3.5" })
708
+ }
709
+ ),
710
+ /* @__PURE__ */ jsx("span", { className: "col-start-2 row-start-1 mt-1 shrink-0 font-mono text-2xs text-fg-subtle", children: index + 1 }),
711
+ /* @__PURE__ */ jsxs("div", { className: "col-span-full row-start-2 min-w-0 sm:col-span-1 sm:col-start-3 sm:row-start-1", children: [
712
+ /* @__PURE__ */ jsx(QueuePrompt, { prompt: turn.prompt, index, onDisclosureChange }),
713
+ /* @__PURE__ */ jsxs("div", { className: "mt-1 flex min-w-0 items-center gap-2 overflow-hidden whitespace-nowrap text-2xs text-fg-subtle", children: [
714
+ turn.resources.length > 0 ? /* @__PURE__ */ jsxs("span", { className: "shrink-0", children: [
715
+ turn.resources.length,
716
+ " resource",
717
+ turn.resources.length === 1 ? "" : "s"
718
+ ] }) : null,
719
+ turn.tools.length > 0 ? /* @__PURE__ */ jsxs("span", { className: "shrink-0", children: [
720
+ turn.tools.length,
721
+ " tool",
722
+ turn.tools.length === 1 ? "" : "s"
723
+ ] }) : null,
724
+ /* @__PURE__ */ jsx("span", { className: "min-w-0 truncate", children: turn.model }),
725
+ /* @__PURE__ */ jsx("span", { className: "shrink-0", children: turn.reasoningEffort })
726
+ ] })
727
+ ] }),
728
+ /* @__PURE__ */ jsxs("div", { className: "col-span-full row-start-3 flex min-w-0 items-start justify-end gap-1.5 sm:col-span-1 sm:col-start-4 sm:row-start-1 sm:gap-2", children: [
729
+ /* @__PURE__ */ jsxs(
730
+ "button",
731
+ {
732
+ type: "button",
733
+ disabled: pending !== null,
734
+ onClick: onSteer,
735
+ "aria-label": `Steer queued prompt ${index + 1}`,
736
+ title: "Make this the next direction",
737
+ className: "inline-flex h-7 shrink-0 items-center justify-center gap-1 rounded-md px-2 text-xs font-medium outline-none transition-colors hover:bg-surface-2 focus-visible:ring-2 focus-visible:ring-ring/40 disabled:pointer-events-none disabled:opacity-50 pointer-coarse:min-h-[44px] pointer-coarse:min-w-[44px]",
738
+ children: [
739
+ pending === "steer" ? /* @__PURE__ */ jsx(Loader2Icon, { className: "size-3.5 animate-spin" }) : /* @__PURE__ */ jsx(ZapIcon, { className: "size-3.5" }),
740
+ /* @__PURE__ */ jsx("span", { className: "hidden sm:inline", children: "Steer" })
741
+ ]
742
+ }
743
+ ),
744
+ /* @__PURE__ */ jsx(
745
+ "button",
746
+ {
747
+ type: "button",
748
+ disabled: pending !== null,
749
+ onClick: onDelete,
750
+ "aria-label": `Delete queued prompt ${index + 1}`,
751
+ title: "Delete this queued prompt",
752
+ className: "inline-flex size-7 shrink-0 items-center justify-center rounded-md outline-none transition-colors hover:bg-surface-2 hover:text-status-failed focus-visible:ring-2 focus-visible:ring-ring/40 disabled:pointer-events-none disabled:opacity-50 pointer-coarse:size-[44px]",
753
+ children: pending === "delete" ? /* @__PURE__ */ jsx(Loader2Icon, { className: "size-3.5 animate-spin" }) : /* @__PURE__ */ jsx(Trash2Icon, { className: "size-3.5" })
754
+ }
755
+ ),
756
+ /* @__PURE__ */ jsxs(DropdownMenu.Root, { children: [
757
+ /* @__PURE__ */ jsx(DropdownMenu.Trigger, { asChild: true, children: /* @__PURE__ */ jsx(
758
+ "button",
759
+ {
760
+ type: "button",
761
+ disabled: pending !== null,
762
+ "aria-label": `More actions for queued prompt ${index + 1}`,
763
+ className: "inline-flex size-7 shrink-0 items-center justify-center rounded-md outline-none transition-colors hover:bg-surface-2 focus-visible:ring-2 focus-visible:ring-ring/40 disabled:pointer-events-none disabled:opacity-50 pointer-coarse:size-[44px]",
764
+ children: pending && pending !== "steer" && pending !== "delete" ? /* @__PURE__ */ jsx(Loader2Icon, { className: "size-3.5 animate-spin" }) : /* @__PURE__ */ jsx(EllipsisIcon, { className: "size-3.5" })
765
+ }
766
+ ) }),
767
+ /* @__PURE__ */ jsx(DropdownMenu.Portal, { children: /* @__PURE__ */ jsxs(
768
+ DropdownMenu.Content,
769
+ {
770
+ align: "end",
771
+ sideOffset: 4,
772
+ className: "z-50 w-48 max-w-[calc(100vw-16px)] rounded-md border border-border bg-surface p-1 text-xs text-fg shadow-lg",
773
+ "data-testid": `queue-actions-menu-${index + 1}`,
774
+ children: [
775
+ onEdit ? /* @__PURE__ */ jsxs(Fragment, { children: [
776
+ /* @__PURE__ */ jsxs(
777
+ DropdownMenu.Item,
778
+ {
779
+ className: "flex min-w-0 cursor-default items-center gap-2 whitespace-normal break-words rounded-sm px-2 py-1.5 outline-none focus:bg-surface-2 pointer-coarse:min-h-[44px]",
780
+ onSelect: onEdit,
781
+ children: [
782
+ /* @__PURE__ */ jsx(PencilIcon, { className: "size-3.5" }),
783
+ " Edit in composer"
784
+ ]
785
+ }
786
+ ),
787
+ /* @__PURE__ */ jsx(DropdownMenu.Separator, { className: "my-1 h-px bg-border" })
788
+ ] }) : null,
789
+ /* @__PURE__ */ jsxs(
790
+ DropdownMenu.Item,
791
+ {
792
+ className: "flex min-w-0 cursor-default items-center gap-2 whitespace-normal break-words rounded-sm px-2 py-1.5 outline-none focus:bg-surface-2 data-[disabled]:opacity-50 pointer-coarse:min-h-[44px]",
793
+ disabled: index === 0,
794
+ onSelect: () => onMove(0),
795
+ children: [
796
+ /* @__PURE__ */ jsx(ArrowUpToLineIcon, { className: "size-3.5" }),
797
+ " Move to top"
798
+ ]
799
+ }
800
+ ),
801
+ /* @__PURE__ */ jsx(
802
+ DropdownMenu.Item,
803
+ {
804
+ className: "flex min-w-0 cursor-default items-center gap-2 whitespace-normal break-words rounded-sm px-2 py-1.5 outline-none focus:bg-surface-2 data-[disabled]:opacity-50 pointer-coarse:min-h-[44px]",
805
+ disabled: index === 0,
806
+ onSelect: () => onMove(index - 1),
807
+ children: "Move up"
808
+ }
809
+ ),
810
+ /* @__PURE__ */ jsx(
811
+ DropdownMenu.Item,
812
+ {
813
+ className: "flex min-w-0 cursor-default items-center gap-2 whitespace-normal break-words rounded-sm px-2 py-1.5 outline-none focus:bg-surface-2 data-[disabled]:opacity-50 pointer-coarse:min-h-[44px]",
814
+ disabled: index === count - 1,
815
+ onSelect: () => onMove(index + 1),
816
+ children: "Move down"
817
+ }
818
+ ),
819
+ /* @__PURE__ */ jsxs(
820
+ DropdownMenu.Item,
821
+ {
822
+ className: "flex min-w-0 cursor-default items-center gap-2 whitespace-normal break-words rounded-sm px-2 py-1.5 outline-none focus:bg-surface-2 data-[disabled]:opacity-50 pointer-coarse:min-h-[44px]",
823
+ disabled: index === count - 1,
824
+ onSelect: () => onMove(count - 1),
825
+ children: [
826
+ /* @__PURE__ */ jsx(ArrowDownToLineIcon, { className: "size-3.5" }),
827
+ " Move to bottom"
828
+ ]
829
+ }
830
+ )
831
+ ]
832
+ }
833
+ ) })
834
+ ] })
835
+ ] })
836
+ ] }),
837
+ confirmingReplace ? /* @__PURE__ */ jsxs("div", { className: "mx-3 mb-2 rounded-md border border-status-waiting/30 bg-status-waiting/10 p-2 text-xs text-fg", children: [
838
+ /* @__PURE__ */ jsx("p", { children: "Your composer already has a draft. Replace it with this queued prompt?" }),
839
+ /* @__PURE__ */ jsx("p", { className: "mt-0.5 text-fg-muted", children: "The current draft will be permanently discarded; this queued prompt is preserved until you confirm." }),
840
+ /* @__PURE__ */ jsxs("div", { className: "mt-2 flex justify-end gap-1.5", children: [
841
+ /* @__PURE__ */ jsx(
842
+ "button",
843
+ {
844
+ type: "button",
845
+ className: "rounded-md px-2 py-1 font-medium hover:bg-surface-2 focus-visible:ring-2 focus-visible:ring-ring/40",
846
+ onClick: onCancelReplace,
847
+ children: "Keep current draft"
848
+ }
849
+ ),
850
+ /* @__PURE__ */ jsx(
851
+ "button",
852
+ {
853
+ type: "button",
854
+ className: "rounded-md bg-brand px-2 py-1 font-medium text-white hover:bg-brand/90 focus-visible:ring-2 focus-visible:ring-ring/40",
855
+ onClick: onConfirmReplace,
856
+ children: "Replace and edit"
857
+ }
858
+ )
859
+ ] })
860
+ ] }) : null
861
+ ]
862
+ }
863
+ );
864
+ }
865
+ var verticalOnly = ({ transform }) => ({ ...transform, x: 0 });
866
+ function focusQueueTurn(surface, turnId) {
867
+ window.requestAnimationFrame(() => {
868
+ surface?.querySelector(`[data-queue-turn-id="${turnId}"] [data-queue-handle]`)?.focus();
869
+ });
870
+ }
871
+ function focusAfterQueueRemoval(surface, previousIndex, onRequestComposerFocus) {
872
+ window.requestAnimationFrame(() => {
873
+ const handles = surface?.querySelectorAll("[data-queue-handle]") ?? [];
874
+ const nearest = handles[Math.min(previousIndex, Math.max(0, handles.length - 1))];
875
+ if (nearest) {
876
+ nearest.focus();
877
+ return;
878
+ }
879
+ if (onRequestComposerFocus) {
880
+ onRequestComposerFocus();
881
+ return;
882
+ }
883
+ document.querySelector('textarea[aria-label="Message the agent"]')?.focus();
884
+ });
885
+ }
886
+ export {
887
+ QueueSurface,
888
+ queueComposerCheckoutEnabled
889
+ };
890
+ //# sourceMappingURL=queue-surface-implementation-NQMV2ML3.js.map