@tangle-network/agent-app 0.45.35 → 0.45.37

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.
@@ -56,7 +56,16 @@ function ProviderLogo({ provider, size = 16 }) {
56
56
  }
57
57
 
58
58
  // src/web-react/controls.tsx
59
- import { useEffect, useMemo, useRef, useState } from "react";
59
+ import {
60
+ useCallback,
61
+ useEffect,
62
+ useId,
63
+ useLayoutEffect,
64
+ useMemo,
65
+ useRef,
66
+ useState
67
+ } from "react";
68
+ import { createPortal } from "react-dom";
60
69
  import { Fragment, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
61
70
  function ChevronDown({ className }) {
62
71
  return /* @__PURE__ */ jsx2("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: /* @__PURE__ */ jsx2("path", { d: "m6 9 6 6 6-6" }) });
@@ -88,10 +97,18 @@ function CheckGlyph({ className }) {
88
97
  function usePopover(open, setOpen) {
89
98
  const containerRef = useRef(null);
90
99
  const triggerRef = useRef(null);
100
+ const panelRef = useRef(null);
91
101
  useEffect(() => {
92
102
  if (!open) return;
93
103
  function onMouseDown(e) {
94
- if (containerRef.current && !containerRef.current.contains(e.target)) setOpen(false);
104
+ const target = e.target;
105
+ if (containerRef.current?.contains(target)) return;
106
+ const panel = panelRef.current;
107
+ if (panel?.contains(target)) return;
108
+ const ownPath = panel?.getAttribute(POPOVER_SURFACE_ATTR);
109
+ const hitPath = target instanceof Element ? target.closest(`[${POPOVER_SURFACE_ATTR}]`)?.getAttribute(POPOVER_SURFACE_ATTR) : null;
110
+ if (ownPath && hitPath && (hitPath === ownPath || hitPath.startsWith(`${ownPath}${POPOVER_PATH_SEPARATOR}`))) return;
111
+ setOpen(false);
95
112
  }
96
113
  function onKeyDown(e) {
97
114
  if (e.key === "Escape") {
@@ -109,6 +126,7 @@ function usePopover(open, setOpen) {
109
126
  return {
110
127
  containerRef,
111
128
  triggerRef,
129
+ panelRef,
112
130
  triggerProps: {
113
131
  ref: triggerRef,
114
132
  "aria-haspopup": true,
@@ -116,6 +134,91 @@ function usePopover(open, setOpen) {
116
134
  }
117
135
  };
118
136
  }
137
+ var POPOVER_GAP = 8;
138
+ var POPOVER_VIEWPORT_MARGIN = 16;
139
+ var POPOVER_MIN_HEIGHT = 120;
140
+ var POPOVER_SURFACE_ATTR = "data-agent-app-popover";
141
+ var POPOVER_PATH_SEPARATOR = "/";
142
+ var useBrowserLayoutEffect = typeof document !== "undefined" ? useLayoutEffect : useEffect;
143
+ function PopoverSurface({
144
+ open,
145
+ triggerRef,
146
+ panelRef,
147
+ className,
148
+ role,
149
+ id,
150
+ matchTriggerWidth,
151
+ children
152
+ }) {
153
+ const surfaceId = useId();
154
+ const [style, setStyle] = useState(() => ({
155
+ position: "fixed",
156
+ top: 0,
157
+ left: 0,
158
+ visibility: "hidden"
159
+ }));
160
+ const place = useCallback(() => {
161
+ const trigger = triggerRef.current;
162
+ const panel = panelRef.current;
163
+ if (!trigger || !panel) return;
164
+ const anchor = trigger.getBoundingClientRect();
165
+ const viewportWidth = window.innerWidth;
166
+ const viewportHeight = window.innerHeight;
167
+ const contentHeight = panel.scrollHeight;
168
+ const panelWidth = panel.offsetWidth;
169
+ const roomAbove = anchor.top - POPOVER_GAP - POPOVER_VIEWPORT_MARGIN;
170
+ const roomBelow = viewportHeight - anchor.bottom - POPOVER_GAP - POPOVER_VIEWPORT_MARGIN;
171
+ const above = contentHeight <= roomAbove || roomAbove >= roomBelow;
172
+ const maxHeight = Math.max(POPOVER_MIN_HEIGHT, above ? roomAbove : roomBelow);
173
+ const height = Math.min(contentHeight, maxHeight);
174
+ const top = above ? Math.max(POPOVER_VIEWPORT_MARGIN, anchor.top - POPOVER_GAP - height) : anchor.bottom + POPOVER_GAP;
175
+ const rightBound = Math.max(POPOVER_VIEWPORT_MARGIN, viewportWidth - panelWidth - POPOVER_VIEWPORT_MARGIN);
176
+ const left = Math.min(Math.max(POPOVER_VIEWPORT_MARGIN, anchor.left), rightBound);
177
+ setStyle({
178
+ position: "fixed",
179
+ top,
180
+ left,
181
+ maxHeight,
182
+ visibility: "visible",
183
+ ...matchTriggerWidth ? { minWidth: anchor.width } : {}
184
+ });
185
+ }, [matchTriggerWidth, panelRef, triggerRef]);
186
+ useBrowserLayoutEffect(() => {
187
+ if (!open) {
188
+ setStyle({ position: "fixed", top: 0, left: 0, visibility: "hidden" });
189
+ return;
190
+ }
191
+ place();
192
+ }, [open, place]);
193
+ useEffect(() => {
194
+ if (!open) return;
195
+ const onViewportChange = () => place();
196
+ window.addEventListener("scroll", onViewportChange, true);
197
+ window.addEventListener("resize", onViewportChange);
198
+ return () => {
199
+ window.removeEventListener("scroll", onViewportChange, true);
200
+ window.removeEventListener("resize", onViewportChange);
201
+ };
202
+ }, [open, place]);
203
+ if (!open || typeof document === "undefined") return null;
204
+ const ownerPath = triggerRef.current?.closest?.(`[${POPOVER_SURFACE_ATTR}]`)?.getAttribute(POPOVER_SURFACE_ATTR);
205
+ const path = ownerPath ? `${ownerPath}${POPOVER_PATH_SEPARATOR}${surfaceId}` : surfaceId;
206
+ return createPortal(
207
+ /* @__PURE__ */ jsx2(
208
+ "div",
209
+ {
210
+ ref: panelRef,
211
+ id,
212
+ role,
213
+ style,
214
+ ...{ [POPOVER_SURFACE_ATTR]: path },
215
+ className: `z-[1000] ${className ?? ""}`,
216
+ children
217
+ }
218
+ ),
219
+ document.body
220
+ );
221
+ }
119
222
  var POPOVER_OPTION_FOCUS = "focus-visible:[outline-offset:-2px]";
120
223
  var OVERLAY_SHADOW = "shadow-[0_1px_2px_hsl(var(--foreground)/0.05),0_12px_28px_hsl(var(--foreground)/0.07)] dark:shadow-[0_1px_2px_hsl(var(--foreground)/0.14),0_12px_28px_hsl(var(--foreground)/0.22)]";
121
224
  function usePending() {
@@ -191,19 +294,12 @@ function ModelRow({
191
294
  function ModelPicker({ value, onChange, models, loading, renderProviderBadge, recommendedLabel = "Recommended", priorityGroup }) {
192
295
  const [open, setOpen] = useState(false);
193
296
  const [query, setQuery] = useState("");
194
- const { containerRef, triggerProps } = usePopover(open, setOpen);
297
+ const { containerRef, triggerRef, panelRef, triggerProps } = usePopover(open, setOpen);
195
298
  const inputRef = useRef(null);
196
- const popoverRef = useRef(null);
299
+ const panelId = useId();
197
300
  useEffect(() => {
198
301
  if (open) inputRef.current?.focus();
199
302
  }, [open]);
200
- useEffect(() => {
201
- if (!open) return;
202
- const el = popoverRef.current;
203
- if (!el) return;
204
- const overflowRight = el.getBoundingClientRect().right - (window.innerWidth - 16);
205
- el.style.transform = overflowRight > 0 ? `translateX(-${Math.ceil(overflowRight)}px)` : "";
206
- }, [open]);
207
303
  const selected = models.find((m) => m.id === value);
208
304
  const filtered = useMemo(() => {
209
305
  const q = query.trim().toLowerCase();
@@ -236,6 +332,7 @@ function ModelPicker({ value, onChange, models, loading, renderProviderBadge, re
236
332
  {
237
333
  type: "button",
238
334
  ...triggerProps,
335
+ "aria-controls": open ? panelId : void 0,
239
336
  onClick: () => setOpen(!open),
240
337
  className: "inline-flex items-center gap-1.5 rounded-full border border-border bg-card px-3 py-1.5 text-sm font-medium text-foreground transition hover:bg-accent",
241
338
  children: [
@@ -245,44 +342,54 @@ function ModelPicker({ value, onChange, models, loading, renderProviderBadge, re
245
342
  ]
246
343
  }
247
344
  ),
248
- open && /* @__PURE__ */ jsxs2("div", { ref: popoverRef, className: `absolute bottom-full left-0 z-50 mb-2 w-[420px] max-w-[calc(100vw-2rem)] overflow-hidden rounded-xl border border-border bg-popover ${OVERLAY_SHADOW}`, children: [
249
- /* @__PURE__ */ jsx2("div", { className: "border-b border-border px-3 py-2", children: /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-2 rounded-lg border border-border bg-background px-3 py-2", children: [
250
- /* @__PURE__ */ jsx2(SearchGlyph, { className: "h-3.5 w-3.5 text-muted-foreground" }),
251
- /* @__PURE__ */ jsx2(
252
- "input",
253
- {
254
- ref: inputRef,
255
- type: "text",
256
- value: query,
257
- onChange: (e) => setQuery(e.target.value),
258
- placeholder: "Search models...",
259
- className: "flex-1 bg-transparent text-sm placeholder:text-muted-foreground"
260
- }
261
- )
262
- ] }) }),
263
- /* @__PURE__ */ jsxs2("div", { className: "max-h-[400px] overflow-y-auto p-1 pb-2", children: [
264
- loading && /* @__PURE__ */ jsx2("div", { className: "px-3 py-4 text-center text-sm text-muted-foreground", children: "Loading models..." }),
265
- !loading && filtered && /* @__PURE__ */ jsxs2(Fragment, { children: [
266
- filtered.length === 0 && /* @__PURE__ */ jsx2("div", { className: "px-3 py-4 text-center text-sm text-muted-foreground", children: "No models match your search" }),
267
- filtered.map((m) => /* @__PURE__ */ jsx2(ModelRow, { model: m, selected: m.id === value, onSelect: () => select(m.id), renderProviderBadge }, m.id))
268
- ] }),
269
- !loading && !filtered && models.length === 0 && /* @__PURE__ */ jsx2("div", { className: "px-3 py-4 text-center text-sm text-muted-foreground", children: "No models available" }),
270
- !loading && !filtered && models.length > 0 && /* @__PURE__ */ jsxs2(Fragment, { children: [
271
- priorityGroup && sections.priority.length > 0 && /* @__PURE__ */ jsxs2(Fragment, { children: [
272
- /* @__PURE__ */ jsx2(SectionHeader, { children: priorityGroup.label }),
273
- sections.priority.map((m) => /* @__PURE__ */ jsx2(ModelRow, { model: m, selected: m.id === value, onSelect: () => select(m.id), renderProviderBadge }, m.id))
274
- ] }),
275
- sections.recommended.length > 0 && /* @__PURE__ */ jsxs2(Fragment, { children: [
276
- /* @__PURE__ */ jsx2(SectionHeader, { children: recommendedLabel }),
277
- sections.recommended.map((m) => /* @__PURE__ */ jsx2(ModelRow, { model: m, selected: m.id === value, onSelect: () => select(m.id), renderProviderBadge }, m.id))
278
- ] }),
279
- sections.byProvider.map((g) => /* @__PURE__ */ jsxs2("div", { children: [
280
- /* @__PURE__ */ jsx2(SectionHeader, { children: g.provider }),
281
- g.items.map((m) => /* @__PURE__ */ jsx2(ModelRow, { model: m, selected: m.id === value, onSelect: () => select(m.id), renderProviderBadge }, m.id))
282
- ] }, g.provider))
283
- ] })
284
- ] })
285
- ] })
345
+ /* @__PURE__ */ jsxs2(
346
+ PopoverSurface,
347
+ {
348
+ open,
349
+ id: panelId,
350
+ triggerRef,
351
+ panelRef,
352
+ className: `flex w-[420px] max-w-[calc(100vw-2rem)] flex-col overflow-hidden rounded-xl border border-border bg-popover ${OVERLAY_SHADOW}`,
353
+ children: [
354
+ /* @__PURE__ */ jsx2("div", { className: "shrink-0 border-b border-border px-3 py-2", children: /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-2 rounded-lg border border-border bg-background px-3 py-2", children: [
355
+ /* @__PURE__ */ jsx2(SearchGlyph, { className: "h-3.5 w-3.5 text-muted-foreground" }),
356
+ /* @__PURE__ */ jsx2(
357
+ "input",
358
+ {
359
+ ref: inputRef,
360
+ type: "text",
361
+ value: query,
362
+ onChange: (e) => setQuery(e.target.value),
363
+ placeholder: "Search models...",
364
+ className: "flex-1 bg-transparent text-sm placeholder:text-muted-foreground"
365
+ }
366
+ )
367
+ ] }) }),
368
+ /* @__PURE__ */ jsxs2("div", { className: "max-h-[400px] min-h-0 overflow-y-auto p-1 pb-2", children: [
369
+ loading && /* @__PURE__ */ jsx2("div", { className: "px-3 py-4 text-center text-sm text-muted-foreground", children: "Loading models..." }),
370
+ !loading && filtered && /* @__PURE__ */ jsxs2(Fragment, { children: [
371
+ filtered.length === 0 && /* @__PURE__ */ jsx2("div", { className: "px-3 py-4 text-center text-sm text-muted-foreground", children: "No models match your search" }),
372
+ filtered.map((m) => /* @__PURE__ */ jsx2(ModelRow, { model: m, selected: m.id === value, onSelect: () => select(m.id), renderProviderBadge }, m.id))
373
+ ] }),
374
+ !loading && !filtered && models.length === 0 && /* @__PURE__ */ jsx2("div", { className: "px-3 py-4 text-center text-sm text-muted-foreground", children: "No models available" }),
375
+ !loading && !filtered && models.length > 0 && /* @__PURE__ */ jsxs2(Fragment, { children: [
376
+ priorityGroup && sections.priority.length > 0 && /* @__PURE__ */ jsxs2(Fragment, { children: [
377
+ /* @__PURE__ */ jsx2(SectionHeader, { children: priorityGroup.label }),
378
+ sections.priority.map((m) => /* @__PURE__ */ jsx2(ModelRow, { model: m, selected: m.id === value, onSelect: () => select(m.id), renderProviderBadge }, m.id))
379
+ ] }),
380
+ sections.recommended.length > 0 && /* @__PURE__ */ jsxs2(Fragment, { children: [
381
+ /* @__PURE__ */ jsx2(SectionHeader, { children: recommendedLabel }),
382
+ sections.recommended.map((m) => /* @__PURE__ */ jsx2(ModelRow, { model: m, selected: m.id === value, onSelect: () => select(m.id), renderProviderBadge }, m.id))
383
+ ] }),
384
+ sections.byProvider.map((g) => /* @__PURE__ */ jsxs2("div", { children: [
385
+ /* @__PURE__ */ jsx2(SectionHeader, { children: g.provider }),
386
+ g.items.map((m) => /* @__PURE__ */ jsx2(ModelRow, { model: m, selected: m.id === value, onSelect: () => select(m.id), renderProviderBadge }, m.id))
387
+ ] }, g.provider))
388
+ ] })
389
+ ] })
390
+ ]
391
+ }
392
+ )
286
393
  ] });
287
394
  }
288
395
  var DEFAULT_EFFORT_LEVELS = [
@@ -314,7 +421,8 @@ function EffortMeter({ fill, className }) {
314
421
  }
315
422
  function EffortPicker({ value, onChange, levels = DEFAULT_EFFORT_LEVELS, label = "Thinking" }) {
316
423
  const [open, setOpen] = useState(false);
317
- const { containerRef, triggerProps } = usePopover(open, setOpen);
424
+ const { containerRef, triggerRef, panelRef, triggerProps } = usePopover(open, setOpen);
425
+ const panelId = useId();
318
426
  const selected = levels.find((l) => l.id === value) ?? levels[2] ?? levels[0];
319
427
  return /* @__PURE__ */ jsxs2("div", { ref: containerRef, className: "relative inline-flex", children: [
320
428
  /* @__PURE__ */ jsxs2(
@@ -322,6 +430,7 @@ function EffortPicker({ value, onChange, levels = DEFAULT_EFFORT_LEVELS, label =
322
430
  {
323
431
  type: "button",
324
432
  ...triggerProps,
433
+ "aria-controls": open ? panelId : void 0,
325
434
  onClick: () => setOpen(!open),
326
435
  title: label ? `${label} \u2014 how hard the agent reasons before answering` : "Reasoning effort",
327
436
  className: "inline-flex min-h-[36px] items-center gap-1.5 rounded-full border border-border bg-card px-3 py-1.5 text-sm font-medium text-foreground transition hover:bg-accent",
@@ -339,31 +448,42 @@ function EffortPicker({ value, onChange, levels = DEFAULT_EFFORT_LEVELS, label =
339
448
  ]
340
449
  }
341
450
  ),
342
- open && /* @__PURE__ */ jsx2("div", { role: "menu", className: `absolute bottom-full left-0 z-50 mb-2 w-44 overflow-hidden rounded-xl border border-border bg-popover p-1 ${OVERLAY_SHADOW}`, children: levels.map((l) => /* @__PURE__ */ jsxs2(
343
- "button",
451
+ /* @__PURE__ */ jsx2(
452
+ PopoverSurface,
344
453
  {
345
- type: "button",
346
- role: "menuitemradio",
347
- "aria-checked": l.id === value,
348
- onClick: () => {
349
- onChange(l.id);
350
- setOpen(false);
351
- },
352
- className: `flex min-h-[40px] w-full items-center gap-2 rounded-md px-3 py-2 text-left text-sm transition ${POPOVER_OPTION_FOCUS} ${l.id === value ? "bg-primary/10 font-medium" : "hover:bg-accent"}`,
353
- children: [
354
- /* @__PURE__ */ jsx2(BrainGlyph, { className: "h-3.5 w-3.5 shrink-0 text-muted-foreground" }),
355
- /* @__PURE__ */ jsx2("span", { className: "truncate", children: l.label }),
356
- /* @__PURE__ */ jsx2(EffortMeter, { fill: effortMeterFill(l.id, levels), className: "ml-auto text-foreground" }),
357
- l.id === value && /* @__PURE__ */ jsx2(CheckGlyph, { className: "h-3.5 w-3.5 shrink-0 text-primary" })
358
- ]
359
- },
360
- l.id
361
- )) })
454
+ open,
455
+ id: panelId,
456
+ role: "menu",
457
+ triggerRef,
458
+ panelRef,
459
+ className: `w-44 overflow-y-auto rounded-xl border border-border bg-popover p-1 ${OVERLAY_SHADOW}`,
460
+ children: levels.map((l) => /* @__PURE__ */ jsxs2(
461
+ "button",
462
+ {
463
+ type: "button",
464
+ role: "menuitemradio",
465
+ "aria-checked": l.id === value,
466
+ onClick: () => {
467
+ onChange(l.id);
468
+ setOpen(false);
469
+ },
470
+ className: `flex min-h-[40px] w-full items-center gap-2 rounded-md px-3 py-2 text-left text-sm transition ${POPOVER_OPTION_FOCUS} ${l.id === value ? "bg-primary/10 font-medium" : "hover:bg-accent"}`,
471
+ children: [
472
+ /* @__PURE__ */ jsx2(BrainGlyph, { className: "h-3.5 w-3.5 shrink-0 text-muted-foreground" }),
473
+ /* @__PURE__ */ jsx2("span", { className: "truncate", children: l.label }),
474
+ /* @__PURE__ */ jsx2(EffortMeter, { fill: effortMeterFill(l.id, levels), className: "ml-auto text-foreground" }),
475
+ l.id === value && /* @__PURE__ */ jsx2(CheckGlyph, { className: "h-3.5 w-3.5 shrink-0 text-primary" })
476
+ ]
477
+ },
478
+ l.id
479
+ ))
480
+ }
481
+ )
362
482
  ] });
363
483
  }
364
484
 
365
485
  // src/web-react/use-composer-attachments.ts
366
- import { useCallback, useEffect as useEffect2, useMemo as useMemo2, useRef as useRef2, useState as useState2 } from "react";
486
+ import { useCallback as useCallback2, useEffect as useEffect2, useMemo as useMemo2, useRef as useRef2, useState as useState2 } from "react";
367
487
  function newId() {
368
488
  const cryptoObject = globalThis.crypto;
369
489
  if (typeof cryptoObject?.randomUUID === "function") return cryptoObject.randomUUID();
@@ -416,7 +536,7 @@ function useComposerAttachments(options) {
416
536
  const [staged, setStagedState] = useState2([]);
417
537
  const stagedRef = useRef2([]);
418
538
  const controllersRef = useRef2(/* @__PURE__ */ new Map());
419
- const setStaged = useCallback(
539
+ const setStaged = useCallback2(
420
540
  (updater) => {
421
541
  const next = typeof updater === "function" ? updater(stagedRef.current) : updater;
422
542
  stagedRef.current = next;
@@ -424,7 +544,7 @@ function useComposerAttachments(options) {
424
544
  },
425
545
  []
426
546
  );
427
- const upload = useCallback(
547
+ const upload = useCallback2(
428
548
  async (id, file, name) => {
429
549
  const opts = optionsRef.current;
430
550
  setStaged(
@@ -487,7 +607,7 @@ function useComposerAttachments(options) {
487
607
  },
488
608
  [setStaged]
489
609
  );
490
- const addFiles = useCallback(
610
+ const addFiles = useCallback2(
491
611
  async (files) => {
492
612
  const opts = optionsRef.current;
493
613
  const enabled2 = opts.enabled ?? true;
@@ -567,7 +687,7 @@ function useComposerAttachments(options) {
567
687
  },
568
688
  [setStaged, upload]
569
689
  );
570
- const retry = useCallback(
690
+ const retry = useCallback2(
571
691
  (id) => {
572
692
  const entry = stagedRef.current.find((s) => s.id === id);
573
693
  if (!entry) return;
@@ -575,7 +695,7 @@ function useComposerAttachments(options) {
575
695
  },
576
696
  [upload]
577
697
  );
578
- const removeAttachment = useCallback(
698
+ const removeAttachment = useCallback2(
579
699
  (id) => {
580
700
  controllersRef.current.get(id)?.abort();
581
701
  controllersRef.current.delete(id);
@@ -585,7 +705,7 @@ function useComposerAttachments(options) {
585
705
  },
586
706
  [setStaged]
587
707
  );
588
- const clear = useCallback(() => {
708
+ const clear = useCallback2(() => {
589
709
  for (const controller of controllersRef.current.values()) controller.abort();
590
710
  controllersRef.current.clear();
591
711
  for (const entry of stagedRef.current) {
@@ -723,7 +843,7 @@ function HarnessGlyph({ harness, className }) {
723
843
  }
724
844
 
725
845
  // src/web-react/agent-session-controls.tsx
726
- import { useMemo as useMemo3, useState as useState3 } from "react";
846
+ import { useId as useId2, useMemo as useMemo3, useState as useState3 } from "react";
727
847
  import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
728
848
  var HARNESS_LABELS = {
729
849
  opencode: "OpenCode (any model)",
@@ -759,7 +879,8 @@ function HarnessPicker({
759
879
  available
760
880
  }) {
761
881
  const [open, setOpen] = useState3(false);
762
- const { containerRef, triggerProps } = usePopover(open, setOpen);
882
+ const { containerRef, triggerRef, panelRef, triggerProps } = usePopover(open, setOpen);
883
+ const panelId = useId2();
763
884
  const options = available ?? Object.keys(HARNESS_LABELS);
764
885
  return /* @__PURE__ */ jsxs4("div", { ref: containerRef, className: "relative inline-flex", children: [
765
886
  /* @__PURE__ */ jsxs4(
@@ -767,6 +888,7 @@ function HarnessPicker({
767
888
  {
768
889
  type: "button",
769
890
  ...triggerProps,
891
+ "aria-controls": open ? panelId : void 0,
770
892
  onClick: () => setOpen(!open),
771
893
  title: "Agent backend",
772
894
  className: `inline-flex w-full items-center justify-between gap-1.5 rounded-lg border border-border bg-card px-3 py-1.5 text-sm font-medium text-foreground transition hover:bg-accent ${FOCUS_RING}`,
@@ -779,25 +901,37 @@ function HarnessPicker({
779
901
  ]
780
902
  }
781
903
  ),
782
- open && /* @__PURE__ */ jsx4("div", { role: "menu", className: `absolute bottom-full left-0 z-50 mb-2 max-h-64 w-full min-w-[248px] overflow-y-auto rounded-xl border border-border bg-popover p-1 ${OVERLAY_SHADOW}`, children: options.map((h) => /* @__PURE__ */ jsxs4(
783
- "button",
904
+ /* @__PURE__ */ jsx4(
905
+ PopoverSurface,
784
906
  {
785
- type: "button",
786
- role: "menuitemradio",
787
- "aria-checked": h === value,
788
- onClick: () => {
789
- onChange(h);
790
- setOpen(false);
791
- },
792
- className: `flex w-full items-center gap-2.5 rounded-md px-3 py-2 text-left text-sm transition ${FOCUS_RING} ${h === value ? "bg-primary/10 font-medium" : "hover:bg-accent"}`,
793
- children: [
794
- /* @__PURE__ */ jsx4(HarnessGlyph, { harness: h, className: "h-4 w-4 shrink-0 text-foreground" }),
795
- /* @__PURE__ */ jsx4("span", { className: "truncate", children: harnessLabel(h) }),
796
- h === value && /* @__PURE__ */ jsx4(CheckGlyph, { className: "ml-auto h-3.5 w-3.5 shrink-0 text-primary" })
797
- ]
798
- },
799
- h
800
- )) })
907
+ open,
908
+ id: panelId,
909
+ role: "menu",
910
+ triggerRef,
911
+ panelRef,
912
+ matchTriggerWidth: true,
913
+ className: `max-h-64 min-w-[248px] overflow-y-auto rounded-xl border border-border bg-popover p-1 ${OVERLAY_SHADOW}`,
914
+ children: options.map((h) => /* @__PURE__ */ jsxs4(
915
+ "button",
916
+ {
917
+ type: "button",
918
+ role: "menuitemradio",
919
+ "aria-checked": h === value,
920
+ onClick: () => {
921
+ onChange(h);
922
+ setOpen(false);
923
+ },
924
+ className: `flex w-full items-center gap-2.5 rounded-md px-3 py-2 text-left text-sm transition ${FOCUS_RING} ${h === value ? "bg-primary/10 font-medium" : "hover:bg-accent"}`,
925
+ children: [
926
+ /* @__PURE__ */ jsx4(HarnessGlyph, { harness: h, className: "h-4 w-4 shrink-0 text-foreground" }),
927
+ /* @__PURE__ */ jsx4("span", { className: "truncate", children: harnessLabel(h) }),
928
+ h === value && /* @__PURE__ */ jsx4(CheckGlyph, { className: "ml-auto h-3.5 w-3.5 shrink-0 text-primary" })
929
+ ]
930
+ },
931
+ h
932
+ ))
933
+ }
934
+ )
801
935
  ] });
802
936
  }
803
937
  function useCoherentHandlers(props) {
@@ -832,7 +966,8 @@ function AgentSessionControls(props) {
832
966
  } = props;
833
967
  const { onModel, onHarness } = useCoherentHandlers(props);
834
968
  const [open, setOpen] = useState3(false);
835
- const { containerRef: popoverRef, triggerProps } = usePopover(open, setOpen);
969
+ const { containerRef: popoverRef, triggerRef, panelRef, triggerProps } = usePopover(open, setOpen);
970
+ const panelId = useId2();
836
971
  const selectedModel = models.find((m) => m.id === model);
837
972
  const showEffort = selectedModel?.supportsReasoning ?? true;
838
973
  const modelPicker = /* @__PURE__ */ jsx4(
@@ -861,6 +996,7 @@ function AgentSessionControls(props) {
861
996
  {
862
997
  type: "button",
863
998
  ...triggerProps,
999
+ "aria-controls": open ? panelId : void 0,
864
1000
  onClick: () => setOpen(!open),
865
1001
  title: "Model settings \u2014 pick the agent backend and how hard it thinks",
866
1002
  className: `flex h-8 w-8 items-center justify-center rounded-full text-muted-foreground transition-colors hover:bg-muted hover:text-foreground data-[state=open]:bg-muted ${FOCUS_RING}`,
@@ -868,18 +1004,28 @@ function AgentSessionControls(props) {
868
1004
  children: /* @__PURE__ */ jsx4(GearGlyph, { className: "h-4 w-4" })
869
1005
  }
870
1006
  ),
871
- open && /* @__PURE__ */ jsxs4("div", { className: `absolute bottom-full left-0 z-50 mb-2 w-72 space-y-3 rounded-xl border border-border bg-popover p-3 ${OVERLAY_SHADOW}`, children: [
872
- showHarness && /* @__PURE__ */ jsxs4("div", { className: "space-y-1.5", children: [
873
- /* @__PURE__ */ jsx4("p", { className: "text-xs font-medium text-foreground", children: "Agent backend" }),
874
- /* @__PURE__ */ jsx4(HarnessPicker, { value: harness, onChange: onHarness, available: availableHarnesses }),
875
- /* @__PURE__ */ jsx4("p", { className: "text-[11px] leading-snug text-muted-foreground", children: "The engine that runs the agent. Switching it keeps your model choice compatible." })
876
- ] }),
877
- showEffort && /* @__PURE__ */ jsxs4("div", { className: "space-y-1.5", children: [
878
- /* @__PURE__ */ jsx4("p", { className: "text-xs font-medium text-foreground", children: "Thinking" }),
879
- /* @__PURE__ */ jsx4(EffortPicker, { value: effort, onChange: onEffortChange, levels: effortLevels, label: "" }),
880
- /* @__PURE__ */ jsx4("p", { className: "text-[11px] leading-snug text-muted-foreground", children: "How hard the agent thinks before answering. Higher is slower but more thorough." })
881
- ] })
882
- ] })
1007
+ /* @__PURE__ */ jsxs4(
1008
+ PopoverSurface,
1009
+ {
1010
+ open,
1011
+ id: panelId,
1012
+ triggerRef,
1013
+ panelRef,
1014
+ className: `w-72 space-y-3 overflow-y-auto rounded-xl border border-border bg-popover p-3 ${OVERLAY_SHADOW}`,
1015
+ children: [
1016
+ showHarness && /* @__PURE__ */ jsxs4("div", { className: "space-y-1.5", children: [
1017
+ /* @__PURE__ */ jsx4("p", { className: "text-xs font-medium text-foreground", children: "Agent backend" }),
1018
+ /* @__PURE__ */ jsx4(HarnessPicker, { value: harness, onChange: onHarness, available: availableHarnesses }),
1019
+ /* @__PURE__ */ jsx4("p", { className: "text-[11px] leading-snug text-muted-foreground", children: "The engine that runs the agent. Switching it keeps your model choice compatible." })
1020
+ ] }),
1021
+ showEffort && /* @__PURE__ */ jsxs4("div", { className: "space-y-1.5", children: [
1022
+ /* @__PURE__ */ jsx4("p", { className: "text-xs font-medium text-foreground", children: "Thinking" }),
1023
+ /* @__PURE__ */ jsx4(EffortPicker, { value: effort, onChange: onEffortChange, levels: effortLevels, label: "" }),
1024
+ /* @__PURE__ */ jsx4("p", { className: "text-[11px] leading-snug text-muted-foreground", children: "How hard the agent thinks before answering. Higher is slower but more thorough." })
1025
+ ] })
1026
+ ]
1027
+ }
1028
+ )
883
1029
  ] })
884
1030
  ] });
885
1031
  }
@@ -888,6 +1034,8 @@ export {
888
1034
  ProviderLogo,
889
1035
  ChevronDown,
890
1036
  usePopover,
1037
+ POPOVER_SURFACE_ATTR,
1038
+ PopoverSurface,
891
1039
  POPOVER_OPTION_FOCUS,
892
1040
  OVERLAY_SHADOW,
893
1041
  usePending,
@@ -901,4 +1049,4 @@ export {
901
1049
  HarnessGlyph,
902
1050
  AgentSessionControls
903
1051
  };
904
- //# sourceMappingURL=chunk-MIZMWCKW.js.map
1052
+ //# sourceMappingURL=chunk-3AXSERRK.js.map