@tangle-network/sandbox-ui 0.95.1 → 0.97.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.
@@ -13,11 +13,12 @@ import {
13
13
  PickerMenuSearch,
14
14
  PickerMenuSection,
15
15
  PickerMenuTag,
16
+ isTextChatModel,
16
17
  pickerMenuBodyClass,
17
18
  pickerMenuContentClass,
18
19
  pickerMenuItemClass,
19
20
  stripBrandPrefix
20
- } from "./chunk-FWSBQLFG.js";
21
+ } from "./chunk-WMVUJ5SA.js";
21
22
  import {
22
23
  ModelBrandStack,
23
24
  canonicalModelId,
@@ -190,18 +191,38 @@ import {
190
191
  reasoningEffortsFor
191
192
  } from "@tangle-network/agent-interface";
192
193
  import * as DropdownMenu2 from "@radix-ui/react-dropdown-menu";
193
- import { ChevronDown as ChevronDown3, Lock, Plus as Plus2, SlidersHorizontal } from "lucide-react";
194
+ import { ChevronDown as ChevronDown3, SlidersHorizontal } from "lucide-react";
195
+ import * as React5 from "react";
196
+
197
+ // src/chat/harness-model-compat.ts
198
+ import {
199
+ harnessSupportsModel,
200
+ snapModelToHarness as snapModelIdToHarness
201
+ } from "@tangle-network/agent-interface";
202
+ import { modelProvider, snapHarnessToModel } from "@tangle-network/agent-interface";
203
+ function isModelCompatibleWithHarness(harness, modelId) {
204
+ return harnessSupportsModel(harness, modelId);
205
+ }
206
+ function snapModelToHarness(harness, modelId, models) {
207
+ return snapModelIdToHarness(harness, modelId, models.map(canonicalModelId));
208
+ }
209
+
210
+ // src/chat/informative-lock.tsx
211
+ import { Lock, Plus } from "lucide-react";
194
212
  import * as React3 from "react";
195
213
 
196
214
  // src/lib/use-click-outside.ts
197
215
  import * as React from "react";
198
- function useClickOutside(onOutside) {
216
+ function useClickOutside(onOutside, extras) {
199
217
  const ref = React.useRef(null);
218
+ const extrasRef = React.useRef(extras);
219
+ extrasRef.current = extras;
200
220
  React.useEffect(() => {
201
221
  function handler(event) {
202
- if (ref.current && !ref.current.contains(event.target)) {
203
- onOutside();
204
- }
222
+ const target = event.target;
223
+ if (!ref.current || ref.current.contains(target)) return;
224
+ if (extrasRef.current?.some((extra) => extra.current?.contains(target))) return;
225
+ onOutside();
205
226
  }
206
227
  document.addEventListener("mousedown", handler);
207
228
  return () => document.removeEventListener("mousedown", handler);
@@ -209,23 +230,178 @@ function useClickOutside(onOutside) {
209
230
  return ref;
210
231
  }
211
232
 
212
- // src/chat/harness-model-compat.ts
213
- import {
214
- harnessSupportsModel,
215
- snapModelToHarness as snapModelIdToHarness
216
- } from "@tangle-network/agent-interface";
217
- import { modelProvider, snapHarnessToModel } from "@tangle-network/agent-interface";
218
- function isModelCompatibleWithHarness(harness, modelId) {
219
- return harnessSupportsModel(harness, modelId);
220
- }
221
- function snapModelToHarness(harness, modelId, models) {
222
- return snapModelIdToHarness(harness, modelId, models.map(canonicalModelId));
233
+ // src/chat/anchored-popover.tsx
234
+ import * as React2 from "react";
235
+ import { createPortal } from "react-dom";
236
+ import { jsx as jsx2 } from "react/jsx-runtime";
237
+ var AnchoredPopover = React2.forwardRef(
238
+ function AnchoredPopover2({ anchorRef, side, children, className, style, ...rest }, forwardedRef) {
239
+ const innerRef = React2.useRef(null);
240
+ const setRefs = (node) => {
241
+ innerRef.current = node;
242
+ if (typeof forwardedRef === "function") forwardedRef(node);
243
+ else if (forwardedRef) forwardedRef.current = node;
244
+ };
245
+ const [position, setPosition] = React2.useState(null);
246
+ const place = React2.useCallback(() => {
247
+ const anchor = anchorRef.current;
248
+ if (!anchor) return;
249
+ const rect = anchor.getBoundingClientRect();
250
+ const width = innerRef.current?.offsetWidth ?? 0;
251
+ const margin = 8;
252
+ const left = Math.max(
253
+ margin,
254
+ Math.min(rect.left, window.innerWidth - width - margin)
255
+ );
256
+ setPosition(
257
+ side === "top" ? { left, bottom: window.innerHeight - rect.top + margin } : { left, top: rect.bottom + margin }
258
+ );
259
+ }, [anchorRef, side]);
260
+ React2.useLayoutEffect(() => {
261
+ place();
262
+ window.addEventListener("resize", place);
263
+ window.addEventListener("scroll", place, true);
264
+ return () => {
265
+ window.removeEventListener("resize", place);
266
+ window.removeEventListener("scroll", place, true);
267
+ };
268
+ }, [place]);
269
+ return createPortal(
270
+ /* @__PURE__ */ jsx2(
271
+ "div",
272
+ {
273
+ ...rest,
274
+ ref: setRefs,
275
+ className: cn("fixed z-50", className),
276
+ style: {
277
+ ...style,
278
+ pointerEvents: "auto",
279
+ ...position ?? { visibility: "hidden", left: 0, top: 0 }
280
+ },
281
+ children
282
+ }
283
+ ),
284
+ document.body
285
+ );
286
+ }
287
+ );
288
+
289
+ // src/chat/informative-lock.tsx
290
+ import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
291
+ function InformativeLock({
292
+ children,
293
+ ariaLabel,
294
+ lockTitle,
295
+ lockBody,
296
+ newChatLabel,
297
+ onNewChat,
298
+ className,
299
+ triggerClassName,
300
+ popoverClassName,
301
+ side = "top"
302
+ }) {
303
+ const [open, setOpen] = React3.useState(false);
304
+ const closeTimer = React3.useRef(null);
305
+ const popoverRef = React3.useRef(null);
306
+ const ref = useClickOutside(() => setOpen(false), [popoverRef]);
307
+ const cancelClose = React3.useCallback(() => {
308
+ if (closeTimer.current) {
309
+ clearTimeout(closeTimer.current);
310
+ closeTimer.current = null;
311
+ }
312
+ }, []);
313
+ const scheduleClose = React3.useCallback(() => {
314
+ cancelClose();
315
+ closeTimer.current = setTimeout(() => setOpen(false), 150);
316
+ }, [cancelClose]);
317
+ React3.useEffect(() => cancelClose, [cancelClose]);
318
+ return /* @__PURE__ */ jsxs2(
319
+ "div",
320
+ {
321
+ ref,
322
+ className: cn("relative inline-flex", className),
323
+ onMouseEnter: () => {
324
+ cancelClose();
325
+ setOpen(true);
326
+ },
327
+ onMouseLeave: scheduleClose,
328
+ onKeyDown: (event) => {
329
+ if (event.key === "Escape") setOpen(false);
330
+ },
331
+ children: [
332
+ /* @__PURE__ */ jsx3(
333
+ "button",
334
+ {
335
+ type: "button",
336
+ onClick: () => setOpen(true),
337
+ "data-state": open ? "open" : "closed",
338
+ "aria-label": ariaLabel,
339
+ "aria-haspopup": "dialog",
340
+ "aria-expanded": open,
341
+ className: cn(
342
+ "inline-flex h-8 items-center gap-1.5 rounded-lg border border-[var(--md3-outline-variant)] bg-surface-container px-2.5 opacity-60",
343
+ "text-xs font-medium text-foreground shadow-sm transition-colors",
344
+ "hover:border-[var(--md3-outline-variant)] hover:bg-surface-container-high focus:outline-none focus-visible:ring-1 focus-visible:ring-ring",
345
+ "data-[state=open]:border-[var(--md3-outline-variant)] data-[state=open]:bg-surface-container-high",
346
+ triggerClassName
347
+ ),
348
+ children
349
+ }
350
+ ),
351
+ open && /* @__PURE__ */ jsxs2(
352
+ AnchoredPopover,
353
+ {
354
+ ref: popoverRef,
355
+ anchorRef: ref,
356
+ side,
357
+ role: "dialog",
358
+ onMouseEnter: () => {
359
+ cancelClose();
360
+ setOpen(true);
361
+ },
362
+ onMouseLeave: scheduleClose,
363
+ className: cn(
364
+ "w-72 rounded-[var(--radius-md)] border border-[var(--md3-outline-variant)] bg-surface-container-highest p-3",
365
+ "shadow-[0_8px_30px_rgba(0,0,0,0.45)] ring-1 ring-[#ffffff14]",
366
+ popoverClassName
367
+ ),
368
+ children: [
369
+ /* @__PURE__ */ jsxs2("p", { className: "flex items-start gap-2 text-sm font-medium text-foreground", children: [
370
+ /* @__PURE__ */ jsx3(Lock, { className: "mt-0.5 h-3.5 w-3.5 shrink-0 text-muted-foreground" }),
371
+ /* @__PURE__ */ jsx3("span", { children: lockTitle })
372
+ ] }),
373
+ /* @__PURE__ */ jsx3("p", { className: "mt-1.5 text-xs text-muted-foreground", children: lockBody }),
374
+ /* @__PURE__ */ jsxs2(
375
+ "button",
376
+ {
377
+ type: "button",
378
+ onClick: () => {
379
+ setOpen(false);
380
+ onNewChat();
381
+ },
382
+ className: cn(
383
+ "mt-3 flex w-full items-center justify-center gap-1.5 rounded-md px-2.5 py-2 text-sm font-medium text-foreground",
384
+ "bg-primary/10 ring-1 ring-inset ring-primary/25 transition-colors",
385
+ "hover:bg-primary/15 focus:outline-none focus-visible:ring-1 focus-visible:ring-ring"
386
+ ),
387
+ children: [
388
+ /* @__PURE__ */ jsx3(Plus, { className: "h-4 w-4" }),
389
+ newChatLabel
390
+ ]
391
+ }
392
+ )
393
+ ]
394
+ }
395
+ )
396
+ ]
397
+ }
398
+ );
223
399
  }
224
400
 
225
401
  // src/chat/agent-profile-picker.tsx
226
- import { Bot, Check, ChevronDown as ChevronDown2, Pencil, Plus, Trash2 } from "lucide-react";
227
- import * as React2 from "react";
228
- import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
402
+ import { Bot, Check, ChevronDown as ChevronDown2, Pencil, Plus as Plus2, Trash2 } from "lucide-react";
403
+ import * as React4 from "react";
404
+ import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
229
405
  function profileSubtitle(profile) {
230
406
  if (profile.description) return profile.description;
231
407
  const count = profile.capabilities?.length ?? 0;
@@ -240,21 +416,33 @@ function AgentProfilePicker({
240
416
  onUpdate,
241
417
  onDelete,
242
418
  disabled,
419
+ locked,
420
+ lockReason,
421
+ onNewChat,
243
422
  className,
423
+ triggerClassName,
424
+ popoverClassName,
244
425
  side = "top"
245
426
  }) {
246
- const [open, setOpen] = React2.useState(false);
247
- const [composing, setComposing] = React2.useState(
427
+ const [open, setOpen] = React4.useState(false);
428
+ const [composing, setComposing] = React4.useState(
248
429
  null
249
430
  );
431
+ const popoverRef = React4.useRef(null);
250
432
  const ref = useClickOutside(() => {
251
433
  setOpen(false);
252
434
  setComposing(null);
253
- });
435
+ }, [popoverRef]);
254
436
  const canAuthor = Boolean(capabilities && (onCreate || onUpdate));
255
437
  const builtins = profiles.filter((profile) => profile.builtin);
256
438
  const custom = profiles.filter((profile) => !profile.builtin);
257
439
  const selected = profiles.find((profile) => profile.id === value);
440
+ const selectedName = selected?.name ?? value;
441
+ React4.useEffect(() => {
442
+ if (!locked) return;
443
+ setOpen(false);
444
+ setComposing(null);
445
+ }, [locked]);
258
446
  function select(id) {
259
447
  onChange(id);
260
448
  setOpen(false);
@@ -264,38 +452,64 @@ function AgentProfilePicker({
264
452
  else await onCreate?.(draft);
265
453
  setComposing(null);
266
454
  }
267
- return /* @__PURE__ */ jsxs2("div", { ref, className: cn("relative inline-flex", className), children: [
268
- /* @__PURE__ */ jsxs2(
455
+ if (locked && onNewChat) {
456
+ return /* @__PURE__ */ jsxs3(
457
+ InformativeLock,
458
+ {
459
+ ariaLabel: "Agent profile (locked)",
460
+ lockTitle: `Profile fixed to ${selectedName} for this conversation.`,
461
+ lockBody: "Conversations stay on one profile.",
462
+ newChatLabel: "New chat to switch profile",
463
+ onNewChat,
464
+ className,
465
+ triggerClassName,
466
+ popoverClassName,
467
+ side,
468
+ children: [
469
+ /* @__PURE__ */ jsx4(Bot, { className: "h-3.5 w-3.5 text-muted-foreground" }),
470
+ /* @__PURE__ */ jsx4("span", { className: "max-w-[140px] truncate", children: selectedName })
471
+ ]
472
+ }
473
+ );
474
+ }
475
+ return /* @__PURE__ */ jsxs3("div", { ref, className: cn("relative inline-flex", className), children: [
476
+ /* @__PURE__ */ jsxs3(
269
477
  "button",
270
478
  {
271
479
  type: "button",
272
- disabled,
480
+ disabled: disabled || locked,
481
+ title: locked ? lockReason : void 0,
273
482
  onClick: () => setOpen((v) => !v),
274
483
  className: cn(
275
484
  "inline-flex h-8 items-center gap-1.5 rounded-lg border border-[var(--md3-outline-variant)] bg-surface-container px-2.5",
276
485
  "text-xs font-medium text-foreground shadow-sm transition-colors",
277
486
  "hover:border-[var(--md3-outline-variant)] hover:bg-surface-container-high focus:outline-none focus-visible:ring-1 focus-visible:ring-ring",
278
487
  "data-[state=open]:border-[var(--md3-outline-variant)] data-[state=open]:bg-surface-container-high",
279
- "disabled:cursor-not-allowed disabled:opacity-60"
488
+ "disabled:cursor-not-allowed disabled:opacity-60",
489
+ triggerClassName
280
490
  ),
281
491
  "data-state": open ? "open" : "closed",
282
492
  "aria-label": "Agent profile",
283
493
  children: [
284
- /* @__PURE__ */ jsx2(Bot, { className: "h-3.5 w-3.5 text-muted-foreground" }),
285
- /* @__PURE__ */ jsx2("span", { className: "max-w-[140px] truncate", children: selected?.name ?? value }),
286
- /* @__PURE__ */ jsx2(ChevronDown2, { className: "h-3.5 w-3.5 text-muted-foreground" })
494
+ /* @__PURE__ */ jsx4(Bot, { className: "h-3.5 w-3.5 text-muted-foreground" }),
495
+ /* @__PURE__ */ jsx4("span", { className: "max-w-[140px] truncate", children: selectedName }),
496
+ !locked && /* @__PURE__ */ jsx4(ChevronDown2, { className: "ml-auto h-3.5 w-3.5 shrink-0 text-muted-foreground" })
287
497
  ]
288
498
  }
289
499
  ),
290
- open && /* @__PURE__ */ jsx2(
291
- "div",
500
+ open && !locked && /* @__PURE__ */ jsx4(
501
+ AnchoredPopover,
292
502
  {
503
+ ref: popoverRef,
504
+ anchorRef: ref,
505
+ side,
293
506
  className: cn(
294
- "absolute left-0 z-50 w-[320px] overflow-hidden rounded-[var(--radius-md)] border border-[var(--md3-outline-variant)] bg-surface-container-highest p-1",
507
+ "w-[min(320px,calc(100vw-2rem))] overflow-hidden rounded-[var(--radius-md)] border border-[var(--md3-outline-variant)] bg-surface-container-highest p-1",
295
508
  "shadow-[0_8px_30px_rgba(0,0,0,0.45)] ring-1 ring-[#ffffff14]",
296
- side === "top" ? "bottom-full mb-2" : "top-full mt-2"
509
+ popoverClassName
297
510
  ),
298
- children: composing ? /* @__PURE__ */ jsx2(
511
+ "data-testid": "agent-profile-popover",
512
+ children: composing ? /* @__PURE__ */ jsx4(
299
513
  ProfileForm,
300
514
  {
301
515
  capabilities: capabilities ?? [],
@@ -303,9 +517,9 @@ function AgentProfilePicker({
303
517
  onCancel: () => setComposing(null),
304
518
  onSave: save
305
519
  }
306
- ) : /* @__PURE__ */ jsxs2("div", { className: "max-h-[min(60vh,420px)] overflow-y-auto", children: [
307
- /* @__PURE__ */ jsx2(SectionHeader, { children: "Modes" }),
308
- builtins.map((profile) => /* @__PURE__ */ jsx2(
520
+ ) : /* @__PURE__ */ jsxs3("div", { className: "max-h-[min(60vh,420px)] overflow-y-auto", children: [
521
+ /* @__PURE__ */ jsx4(SectionHeader, { children: "Modes" }),
522
+ builtins.map((profile) => /* @__PURE__ */ jsx4(
309
523
  ProfileRow,
310
524
  {
311
525
  active: profile.id === value,
@@ -315,8 +529,8 @@ function AgentProfilePicker({
315
529
  },
316
530
  profile.id
317
531
  )),
318
- custom.length > 0 && /* @__PURE__ */ jsx2(SectionHeader, { children: "Your agents" }),
319
- custom.map((profile) => /* @__PURE__ */ jsx2(
532
+ custom.length > 0 && /* @__PURE__ */ jsx4(SectionHeader, { children: "Your agents" }),
533
+ custom.map((profile) => /* @__PURE__ */ jsx4(
320
534
  ProfileRow,
321
535
  {
322
536
  active: profile.id === value,
@@ -328,14 +542,14 @@ function AgentProfilePicker({
328
542
  },
329
543
  profile.id
330
544
  )),
331
- canAuthor && /* @__PURE__ */ jsxs2(
545
+ canAuthor && /* @__PURE__ */ jsxs3(
332
546
  "button",
333
547
  {
334
548
  type: "button",
335
549
  onClick: () => setComposing("new"),
336
550
  className: "mt-1 flex w-full items-center gap-2 rounded-md px-2.5 py-2 text-left text-sm text-[var(--accent-text)] transition-colors hover:bg-accent/40",
337
551
  children: [
338
- /* @__PURE__ */ jsx2(Plus, { className: "h-4 w-4" }),
552
+ /* @__PURE__ */ jsx4(Plus2, { className: "h-4 w-4" }),
339
553
  " New agent\u2026"
340
554
  ]
341
555
  }
@@ -346,7 +560,7 @@ function AgentProfilePicker({
346
560
  ] });
347
561
  }
348
562
  function SectionHeader({ children }) {
349
- return /* @__PURE__ */ jsx2("div", { className: "px-2.5 pb-1 pt-2 text-[10px] font-semibold uppercase tracking-wide text-muted-foreground/70", children });
563
+ return /* @__PURE__ */ jsx4("div", { className: "px-2.5 pb-1 pt-2 text-[10px] font-semibold uppercase tracking-wide text-muted-foreground/70", children });
350
564
  }
351
565
  function ProfileRow({
352
566
  active,
@@ -356,7 +570,7 @@ function ProfileRow({
356
570
  onEdit,
357
571
  onDelete
358
572
  }) {
359
- return /* @__PURE__ */ jsxs2(
573
+ return /* @__PURE__ */ jsxs3(
360
574
  "div",
361
575
  {
362
576
  className: cn(
@@ -364,40 +578,40 @@ function ProfileRow({
364
578
  active ? "bg-[var(--accent-surface-soft)] text-[var(--accent-text)]" : "hover:bg-accent/40"
365
579
  ),
366
580
  children: [
367
- /* @__PURE__ */ jsxs2(
581
+ /* @__PURE__ */ jsxs3(
368
582
  "button",
369
583
  {
370
584
  type: "button",
371
585
  onClick: onSelect,
372
586
  className: "flex min-w-0 flex-1 items-center gap-2 text-left",
373
587
  children: [
374
- /* @__PURE__ */ jsx2("span", { className: "flex h-4 w-4 shrink-0 items-center justify-center", children: active && /* @__PURE__ */ jsx2(Check, { className: "h-3.5 w-3.5" }) }),
375
- /* @__PURE__ */ jsxs2("span", { className: "min-w-0", children: [
376
- /* @__PURE__ */ jsx2("span", { className: "block truncate font-medium", children: title }),
377
- /* @__PURE__ */ jsx2("span", { className: "block truncate text-xs text-muted-foreground", children: subtitle })
588
+ /* @__PURE__ */ jsx4("span", { className: "flex h-4 w-4 shrink-0 items-center justify-center", children: active && /* @__PURE__ */ jsx4(Check, { className: "h-3.5 w-3.5" }) }),
589
+ /* @__PURE__ */ jsxs3("span", { className: "min-w-0", children: [
590
+ /* @__PURE__ */ jsx4("span", { className: "block truncate font-medium", children: title }),
591
+ /* @__PURE__ */ jsx4("span", { className: "block truncate text-xs text-muted-foreground", children: subtitle })
378
592
  ] })
379
593
  ]
380
594
  }
381
595
  ),
382
- (onEdit || onDelete) && /* @__PURE__ */ jsxs2("span", { className: "flex shrink-0 items-center gap-1 opacity-0 transition-opacity group-hover:opacity-100", children: [
383
- onEdit && /* @__PURE__ */ jsx2(
596
+ (onEdit || onDelete) && /* @__PURE__ */ jsxs3("span", { className: "flex shrink-0 items-center gap-1 opacity-0 transition-opacity group-hover:opacity-100", children: [
597
+ onEdit && /* @__PURE__ */ jsx4(
384
598
  "button",
385
599
  {
386
600
  type: "button",
387
601
  onClick: onEdit,
388
602
  title: "Edit",
389
603
  className: "rounded p-1 text-muted-foreground hover:text-foreground",
390
- children: /* @__PURE__ */ jsx2(Pencil, { className: "h-3 w-3" })
604
+ children: /* @__PURE__ */ jsx4(Pencil, { className: "h-3 w-3" })
391
605
  }
392
606
  ),
393
- onDelete && /* @__PURE__ */ jsx2(
607
+ onDelete && /* @__PURE__ */ jsx4(
394
608
  "button",
395
609
  {
396
610
  type: "button",
397
611
  onClick: onDelete,
398
612
  title: "Delete",
399
613
  className: "rounded p-1 text-muted-foreground hover:text-[var(--status-error,#ff4d6d)]",
400
- children: /* @__PURE__ */ jsx2(Trash2, { className: "h-3 w-3" })
614
+ children: /* @__PURE__ */ jsx4(Trash2, { className: "h-3 w-3" })
401
615
  }
402
616
  )
403
617
  ] })
@@ -411,16 +625,16 @@ function ProfileForm({
411
625
  onCancel,
412
626
  onSave
413
627
  }) {
414
- const [name, setName] = React2.useState(initial?.name ?? "");
415
- const [caps, setCaps] = React2.useState(
628
+ const [name, setName] = React4.useState(initial?.name ?? "");
629
+ const [caps, setCaps] = React4.useState(
416
630
  initial?.capabilities ? [...initial.capabilities] : capabilities.map((c) => c.id)
417
631
  );
418
- const [instructions, setInstructions] = React2.useState(initial?.instructions ?? "");
632
+ const [instructions, setInstructions] = React4.useState(initial?.instructions ?? "");
419
633
  const toggle = (id) => setCaps(
420
634
  (prev) => prev.includes(id) ? prev.filter((c) => c !== id) : [...prev, id]
421
635
  );
422
- return /* @__PURE__ */ jsxs2("div", { className: "space-y-3 p-2.5", children: [
423
- /* @__PURE__ */ jsx2(
636
+ return /* @__PURE__ */ jsxs3("div", { className: "space-y-3 p-2.5", children: [
637
+ /* @__PURE__ */ jsx4(
424
638
  "input",
425
639
  {
426
640
  autoFocus: true,
@@ -430,14 +644,14 @@ function ProfileForm({
430
644
  className: "w-full rounded-lg border border-[var(--md3-outline-variant)] bg-surface-container px-3 py-2 text-sm outline-none focus:border-primary"
431
645
  }
432
646
  ),
433
- capabilities.length > 0 && /* @__PURE__ */ jsxs2("div", { children: [
434
- /* @__PURE__ */ jsx2("p", { className: "mb-1.5 text-[10px] font-semibold uppercase tracking-wide text-muted-foreground/70", children: "Tools" }),
435
- /* @__PURE__ */ jsx2("div", { className: "space-y-0.5", children: capabilities.map((cap) => /* @__PURE__ */ jsxs2(
647
+ capabilities.length > 0 && /* @__PURE__ */ jsxs3("div", { children: [
648
+ /* @__PURE__ */ jsx4("p", { className: "mb-1.5 text-[10px] font-semibold uppercase tracking-wide text-muted-foreground/70", children: "Tools" }),
649
+ /* @__PURE__ */ jsx4("div", { className: "space-y-0.5", children: capabilities.map((cap) => /* @__PURE__ */ jsxs3(
436
650
  "label",
437
651
  {
438
652
  className: "flex cursor-pointer items-start gap-2 rounded-md px-2 py-1.5 hover:bg-accent/40",
439
653
  children: [
440
- /* @__PURE__ */ jsx2(
654
+ /* @__PURE__ */ jsx4(
441
655
  "input",
442
656
  {
443
657
  type: "checkbox",
@@ -446,16 +660,16 @@ function ProfileForm({
446
660
  className: "mt-0.5"
447
661
  }
448
662
  ),
449
- /* @__PURE__ */ jsxs2("span", { className: "min-w-0", children: [
450
- /* @__PURE__ */ jsx2("span", { className: "block text-sm font-medium", children: cap.label }),
451
- cap.description && /* @__PURE__ */ jsx2("span", { className: "block text-xs text-muted-foreground", children: cap.description })
663
+ /* @__PURE__ */ jsxs3("span", { className: "min-w-0", children: [
664
+ /* @__PURE__ */ jsx4("span", { className: "block text-sm font-medium", children: cap.label }),
665
+ cap.description && /* @__PURE__ */ jsx4("span", { className: "block text-xs text-muted-foreground", children: cap.description })
452
666
  ] })
453
667
  ]
454
668
  },
455
669
  cap.id
456
670
  )) })
457
671
  ] }),
458
- /* @__PURE__ */ jsx2(
672
+ /* @__PURE__ */ jsx4(
459
673
  "textarea",
460
674
  {
461
675
  value: instructions,
@@ -465,8 +679,8 @@ function ProfileForm({
465
679
  className: "w-full resize-none rounded-lg border border-[var(--md3-outline-variant)] bg-surface-container px-3 py-2 text-sm outline-none focus:border-primary"
466
680
  }
467
681
  ),
468
- /* @__PURE__ */ jsxs2("div", { className: "flex justify-end gap-2", children: [
469
- /* @__PURE__ */ jsx2(
682
+ /* @__PURE__ */ jsxs3("div", { className: "flex justify-end gap-2", children: [
683
+ /* @__PURE__ */ jsx4(
470
684
  "button",
471
685
  {
472
686
  type: "button",
@@ -475,7 +689,7 @@ function ProfileForm({
475
689
  children: "Cancel"
476
690
  }
477
691
  ),
478
- /* @__PURE__ */ jsx2(
692
+ /* @__PURE__ */ jsx4(
479
693
  "button",
480
694
  {
481
695
  type: "button",
@@ -495,7 +709,7 @@ function ProfileForm({
495
709
  }
496
710
 
497
711
  // src/chat/agent-session-controls.tsx
498
- import { Fragment as Fragment2, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
712
+ import { Fragment as Fragment2, jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
499
713
  function harnessAutonomyNote(harness) {
500
714
  const ownModel = !harnessHonorsModel(harness);
501
715
  const ownEffort = !harnessHonorsEffort(harness);
@@ -519,106 +733,6 @@ function harnessGroup(option) {
519
733
  return harnessAutonomyNote(option.type) === null ? "steerable" : "fixed";
520
734
  }
521
735
  var HARNESS_SEARCH_THRESHOLD = 7;
522
- function InformativeLock({
523
- type,
524
- label,
525
- lockTitle,
526
- lockBody,
527
- newChatLabel,
528
- onNewChat
529
- }) {
530
- const [open, setOpen] = React3.useState(false);
531
- const closeTimer = React3.useRef(null);
532
- const ref = useClickOutside(() => setOpen(false));
533
- const cancelClose = React3.useCallback(() => {
534
- if (closeTimer.current) {
535
- clearTimeout(closeTimer.current);
536
- closeTimer.current = null;
537
- }
538
- }, []);
539
- const scheduleClose = React3.useCallback(() => {
540
- cancelClose();
541
- closeTimer.current = setTimeout(() => setOpen(false), 150);
542
- }, [cancelClose]);
543
- React3.useEffect(() => cancelClose, [cancelClose]);
544
- return /* @__PURE__ */ jsxs3(
545
- "div",
546
- {
547
- ref,
548
- className: "relative inline-flex",
549
- onMouseEnter: () => {
550
- cancelClose();
551
- setOpen(true);
552
- },
553
- onMouseLeave: scheduleClose,
554
- onKeyDown: (event) => {
555
- if (event.key === "Escape") setOpen(false);
556
- },
557
- children: [
558
- /* @__PURE__ */ jsxs3(
559
- "button",
560
- {
561
- type: "button",
562
- onClick: () => setOpen(true),
563
- "data-state": open ? "open" : "closed",
564
- "aria-label": "Agent harness (locked)",
565
- "aria-haspopup": "dialog",
566
- "aria-expanded": open,
567
- className: cn(
568
- // Dimmed to read as locked (the harness is bound to this session),
569
- // matching the disabled model picker — the logo stays so the chip is
570
- // still recognizably this harness; the popover explains the lock.
571
- "inline-flex h-8 items-center gap-1.5 rounded-lg border border-[var(--md3-outline-variant)] bg-surface-container px-2.5 opacity-60",
572
- "text-xs font-medium text-foreground shadow-sm transition-colors",
573
- "hover:border-[var(--md3-outline-variant)] hover:bg-surface-container-high focus:outline-none focus-visible:ring-1 focus-visible:ring-ring",
574
- "data-[state=open]:border-[var(--md3-outline-variant)] data-[state=open]:bg-surface-container-high"
575
- ),
576
- children: [
577
- /* @__PURE__ */ jsx3(HarnessLogo, { type, size: 16 }),
578
- /* @__PURE__ */ jsx3("span", { children: label })
579
- ]
580
- }
581
- ),
582
- open && /* @__PURE__ */ jsxs3(
583
- "div",
584
- {
585
- role: "dialog",
586
- className: cn(
587
- "absolute bottom-full left-0 z-50 mb-2 w-72 rounded-[var(--radius-md)] border border-[var(--md3-outline-variant)] bg-surface-container-highest p-3",
588
- "shadow-[0_8px_30px_rgba(0,0,0,0.45)] ring-1 ring-[#ffffff14]"
589
- ),
590
- children: [
591
- /* @__PURE__ */ jsxs3("p", { className: "flex items-start gap-2 text-sm font-medium text-foreground", children: [
592
- /* @__PURE__ */ jsx3(Lock, { className: "mt-0.5 h-3.5 w-3.5 shrink-0 text-muted-foreground" }),
593
- /* @__PURE__ */ jsx3("span", { children: lockTitle ?? `Agent fixed to ${label} for this conversation.` })
594
- ] }),
595
- /* @__PURE__ */ jsx3("p", { className: "mt-1.5 text-xs text-muted-foreground", children: lockBody ?? "Conversations stay on one agent." }),
596
- /* @__PURE__ */ jsxs3(
597
- "button",
598
- {
599
- type: "button",
600
- onClick: () => {
601
- setOpen(false);
602
- onNewChat();
603
- },
604
- className: cn(
605
- "mt-3 flex w-full items-center justify-center gap-1.5 rounded-md px-2.5 py-2 text-sm font-medium text-foreground",
606
- "bg-primary/10 ring-1 ring-inset ring-primary/25 transition-colors",
607
- "hover:bg-primary/15 focus:outline-none focus-visible:ring-1 focus-visible:ring-ring"
608
- ),
609
- children: [
610
- /* @__PURE__ */ jsx3(Plus2, { className: "h-4 w-4" }),
611
- newChatLabel ?? "New chat to switch agent"
612
- ]
613
- }
614
- )
615
- ]
616
- }
617
- )
618
- ]
619
- }
620
- );
621
- }
622
736
  function HarnessDropdown({
623
737
  value,
624
738
  onChange,
@@ -640,16 +754,16 @@ function HarnessDropdown({
640
754
  );
641
755
  const options = HARNESS_OPTIONS.filter((h) => allowed.has(h.type));
642
756
  const selected = options.find((option) => option.type === value);
643
- const [query, setQuery] = React3.useState("");
757
+ const [query, setQuery] = React5.useState("");
644
758
  const showSearch = options.length > HARNESS_SEARCH_THRESHOLD;
645
- const filtered = React3.useMemo(() => {
759
+ const filtered = React5.useMemo(() => {
646
760
  const q = query.trim().toLowerCase();
647
761
  if (!q) return options;
648
762
  return options.filter(
649
763
  (o) => o.label.toLowerCase().includes(q) || o.type.toLowerCase().includes(q) || (o.description?.toLowerCase() ?? "").includes(q)
650
764
  );
651
765
  }, [options, query]);
652
- const sections = React3.useMemo(() => {
766
+ const sections = React5.useMemo(() => {
653
767
  const byGroup = /* @__PURE__ */ new Map();
654
768
  for (const option of filtered) {
655
769
  const key = harnessGroup(option);
@@ -664,26 +778,31 @@ function HarnessDropdown({
664
778
  })).filter((section) => section.items.length > 0);
665
779
  }, [filtered]);
666
780
  if (locked && onNewChat) {
667
- return /* @__PURE__ */ jsx3(
781
+ const label = selected?.label ?? value;
782
+ return /* @__PURE__ */ jsxs4(
668
783
  InformativeLock,
669
784
  {
670
- type: value,
671
- label: selected?.label ?? value,
672
- lockTitle,
673
- lockBody,
674
- newChatLabel,
675
- onNewChat
785
+ ariaLabel: "Agent harness (locked)",
786
+ lockTitle: lockTitle ?? `Agent fixed to ${label} for this conversation.`,
787
+ lockBody: lockBody ?? "Conversations stay on one agent.",
788
+ newChatLabel: newChatLabel ?? "New chat to switch agent",
789
+ onNewChat,
790
+ triggerClassName,
791
+ children: [
792
+ /* @__PURE__ */ jsx5(HarnessLogo, { type: value, size: 16 }),
793
+ /* @__PURE__ */ jsx5("span", { children: label })
794
+ ]
676
795
  }
677
796
  );
678
797
  }
679
- return /* @__PURE__ */ jsxs3(
798
+ return /* @__PURE__ */ jsxs4(
680
799
  DropdownMenu2.Root,
681
800
  {
682
801
  onOpenChange: (nextOpen) => {
683
802
  if (!nextOpen) setQuery("");
684
803
  },
685
804
  children: [
686
- /* @__PURE__ */ jsx3(DropdownMenu2.Trigger, { asChild: true, children: /* @__PURE__ */ jsxs3(
805
+ /* @__PURE__ */ jsx5(DropdownMenu2.Trigger, { asChild: true, children: /* @__PURE__ */ jsxs4(
687
806
  "button",
688
807
  {
689
808
  type: "button",
@@ -699,13 +818,13 @@ function HarnessDropdown({
699
818
  ),
700
819
  "aria-label": "Agent harness",
701
820
  children: [
702
- /* @__PURE__ */ jsx3(HarnessLogo, { type: value, size: 16, className: "shrink-0" }),
703
- /* @__PURE__ */ jsx3("span", { className: "truncate", children: selected?.label ?? value }),
704
- !locked && /* @__PURE__ */ jsx3(ChevronDown3, { className: "ml-auto h-3.5 w-3.5 shrink-0 text-muted-foreground" })
821
+ /* @__PURE__ */ jsx5(HarnessLogo, { type: value, size: 16, className: "shrink-0" }),
822
+ /* @__PURE__ */ jsx5("span", { className: "truncate", children: selected?.label ?? value }),
823
+ !locked && /* @__PURE__ */ jsx5(ChevronDown3, { className: "ml-auto h-3.5 w-3.5 shrink-0 text-muted-foreground" })
705
824
  ]
706
825
  }
707
826
  ) }),
708
- /* @__PURE__ */ jsx3(DropdownMenu2.Portal, { children: /* @__PURE__ */ jsxs3(
827
+ /* @__PURE__ */ jsx5(DropdownMenu2.Portal, { children: /* @__PURE__ */ jsxs4(
709
828
  DropdownMenu2.Content,
710
829
  {
711
830
  side,
@@ -715,7 +834,7 @@ function HarnessDropdown({
715
834
  sideOffset: 6,
716
835
  className: pickerMenuContentClass,
717
836
  children: [
718
- showSearch && /* @__PURE__ */ jsx3(
837
+ showSearch && /* @__PURE__ */ jsx5(
719
838
  PickerMenuSearch,
720
839
  {
721
840
  value: query,
@@ -723,7 +842,7 @@ function HarnessDropdown({
723
842
  placeholder: "Search agents..."
724
843
  }
725
844
  ),
726
- /* @__PURE__ */ jsx3("div", { className: pickerMenuBodyClass, children: sections.length === 0 ? /* @__PURE__ */ jsx3("div", { className: "px-3 py-8 text-center text-xs text-muted-foreground", children: "No agents match." }) : sections.map((section) => /* @__PURE__ */ jsx3(PickerMenuSection, { label: section.label, children: section.items.map((option) => /* @__PURE__ */ jsxs3(
845
+ /* @__PURE__ */ jsx5("div", { className: pickerMenuBodyClass, children: sections.length === 0 ? /* @__PURE__ */ jsx5("div", { className: "px-3 py-8 text-center text-xs text-muted-foreground", children: "No agents match." }) : sections.map((section) => /* @__PURE__ */ jsx5(PickerMenuSection, { label: section.label, children: section.items.map((option) => /* @__PURE__ */ jsxs4(
727
846
  DropdownMenu2.Item,
728
847
  {
729
848
  onSelect: (event) => {
@@ -734,7 +853,7 @@ function HarnessDropdown({
734
853
  active: option.type === value
735
854
  }),
736
855
  children: [
737
- /* @__PURE__ */ jsx3(
856
+ /* @__PURE__ */ jsx5(
738
857
  HarnessLogo,
739
858
  {
740
859
  type: option.type,
@@ -742,18 +861,18 @@ function HarnessDropdown({
742
861
  className: "mt-0.5"
743
862
  }
744
863
  ),
745
- /* @__PURE__ */ jsxs3("div", { className: "min-w-0 flex-1", children: [
746
- /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-1.5", children: [
747
- /* @__PURE__ */ jsx3("span", { className: "min-w-0 truncate text-sm font-medium", children: option.label }),
748
- harnessAutonomyNote(option.type) && /* @__PURE__ */ jsx3(PickerMenuTag, { children: harnessAutonomyNote(option.type) })
864
+ /* @__PURE__ */ jsxs4("div", { className: "min-w-0 flex-1", children: [
865
+ /* @__PURE__ */ jsxs4("div", { className: "flex items-center gap-1.5", children: [
866
+ /* @__PURE__ */ jsx5("span", { className: "min-w-0 truncate text-sm font-medium", children: option.label }),
867
+ harnessAutonomyNote(option.type) && /* @__PURE__ */ jsx5(PickerMenuTag, { children: harnessAutonomyNote(option.type) })
749
868
  ] }),
750
- /* @__PURE__ */ jsx3(PickerMenuMeta, { parts: [option.description] })
869
+ /* @__PURE__ */ jsx5(PickerMenuMeta, { parts: [option.description] })
751
870
  ] })
752
871
  ]
753
872
  },
754
873
  option.type
755
874
  )) }, section.key)) }),
756
- showSearch && /* @__PURE__ */ jsxs3(PickerMenuFooter, { children: [
875
+ showSearch && /* @__PURE__ */ jsxs4(PickerMenuFooter, { children: [
757
876
  filtered.length,
758
877
  " of ",
759
878
  options.length,
@@ -826,20 +945,27 @@ function AgentSessionControls({
826
945
  }
827
946
  if (nextHarness) clampEffort(nextHarness, nextModelId);
828
947
  };
829
- const visibleModels = model && restrictModelsToHarness && harness ? model.models.filter(
948
+ const chatModels = model && context === "chat" && model.modalities === void 0 ? model.models.filter(isTextChatModel) : model?.models;
949
+ const visibleModels = model && restrictModelsToHarness && harness ? (chatModels ?? []).filter(
830
950
  (entry) => isModelCompatibleWithHarness(
831
951
  harness.value,
832
952
  canonicalModelId(entry)
833
953
  )
834
- ) : model?.models;
954
+ ) : chatModels;
835
955
  const harnessAvailable = harness?.available ?? (context === "chat" ? chatCapableHarnesses : void 0);
836
956
  const collapsed = layout === "gear" || layout === "combined";
837
957
  const menuSide = collapsed ? "left" : "bottom";
838
958
  const menuAlign = collapsed ? "end" : "start";
839
959
  const collapsedTriggerClass = layout === "combined" ? "w-full bg-transparent shadow-none hover:bg-muted/60 data-[state=open]:bg-muted/60" : collapsed ? "w-full" : void 0;
960
+ const secondaryTriggerClass = collapsed ? collapsedTriggerClass : cn(
961
+ "border-transparent bg-transparent text-muted-foreground shadow-none",
962
+ "hover:border-transparent hover:bg-muted/50 hover:text-foreground",
963
+ "data-[state=open]:border-transparent data-[state=open]:bg-muted/50 data-[state=open]:text-foreground"
964
+ );
965
+ const modelTriggerClass = collapsed ? cn(collapsedTriggerClass, "h-8 rounded-lg px-2.5 text-xs") : void 0;
840
966
  const inlineForceDown = menuPlacement === "down" && layout === "inline";
841
967
  const avoidCollisions = inlineForceDown ? false : void 0;
842
- const harnessNode = harness && /* @__PURE__ */ jsx3(
968
+ const harnessNode = harness && /* @__PURE__ */ jsx5(
843
969
  HarnessDropdown,
844
970
  {
845
971
  ...harness,
@@ -848,10 +974,10 @@ function AgentSessionControls({
848
974
  side: menuSide,
849
975
  align: menuAlign,
850
976
  avoidCollisions,
851
- triggerClassName: collapsedTriggerClass
977
+ triggerClassName: secondaryTriggerClass
852
978
  }
853
979
  );
854
- const profileNode = profile && /* @__PURE__ */ jsx3(
980
+ const profileNode = profile && /* @__PURE__ */ jsx5(
855
981
  AgentProfilePicker,
856
982
  {
857
983
  value: profile.value,
@@ -862,10 +988,15 @@ function AgentSessionControls({
862
988
  onUpdate: profile.onUpdate,
863
989
  onDelete: profile.onDelete,
864
990
  disabled: profile.disabled,
865
- side: inlineForceDown || collapsed ? "bottom" : "top"
991
+ locked: profile.locked,
992
+ lockReason: profile.lockReason,
993
+ onNewChat: profile.onNewChat,
994
+ side: inlineForceDown || collapsed ? "bottom" : "top",
995
+ triggerClassName: secondaryTriggerClass,
996
+ popoverClassName: collapsed ? "w-full" : void 0
866
997
  }
867
998
  );
868
- const modelNode = model && /* @__PURE__ */ jsx3(
999
+ const modelNode = model && /* @__PURE__ */ jsx5(
869
1000
  ModelPicker,
870
1001
  {
871
1002
  variant: "pill",
@@ -876,15 +1007,17 @@ function AgentSessionControls({
876
1007
  loading: model.loading,
877
1008
  popular: model.popular,
878
1009
  recents: model.recents,
1010
+ modalities: model.modalities,
1011
+ excludeProviders: model.excludeProviders,
879
1012
  disabled: model.disabled || (visibleModels ?? []).length === 0 || !selectorHonorsModel,
880
1013
  side: collapsed ? void 0 : "bottom",
881
1014
  avoidCollisions,
882
- triggerClassName: collapsedTriggerClass
1015
+ triggerClassName: modelTriggerClass
883
1016
  }
884
1017
  );
885
1018
  const modelReasoning = modelReasoningCapability(model?.models, model?.value);
886
1019
  const reasoningOptions = (harness && HARNESS_REASONING_OPTIONS[harness.value]) ?? reasoning?.options;
887
- const reasoningNode = reasoning && /* @__PURE__ */ jsx3(
1020
+ const reasoningNode = reasoning && /* @__PURE__ */ jsx5(
888
1021
  ReasoningLevelPicker,
889
1022
  {
890
1023
  value: reasoning.value,
@@ -894,11 +1027,11 @@ function AgentSessionControls({
894
1027
  available: harness ? reasoningEffortsFor(harness.value, modelReasoning) : reasoning.available,
895
1028
  side: collapsed ? void 0 : "bottom",
896
1029
  avoidCollisions,
897
- triggerClassName: collapsedTriggerClass
1030
+ triggerClassName: secondaryTriggerClass
898
1031
  }
899
1032
  );
900
1033
  if (layout === "gear") {
901
- return /* @__PURE__ */ jsx3(
1034
+ return /* @__PURE__ */ jsx5(
902
1035
  GearControls,
903
1036
  {
904
1037
  className,
@@ -917,8 +1050,8 @@ function AgentSessionControls({
917
1050
  segments.push({
918
1051
  key: "harness",
919
1052
  label: harnessLabel,
920
- node: /* @__PURE__ */ jsxs3("span", { className: "flex items-center gap-1.5", children: [
921
- /* @__PURE__ */ jsx3(HarnessLogo, { type: harness.value, size: 16, className: "shrink-0" }),
1053
+ node: /* @__PURE__ */ jsxs4("span", { className: "flex items-center gap-1.5", children: [
1054
+ /* @__PURE__ */ jsx5(HarnessLogo, { type: harness.value, size: 16, className: "shrink-0" }),
922
1055
  harnessLabel
923
1056
  ] })
924
1057
  });
@@ -935,8 +1068,8 @@ function AgentSessionControls({
935
1068
  segments.push({
936
1069
  key: "model",
937
1070
  label: modelLabel,
938
- node: /* @__PURE__ */ jsxs3("span", { className: "flex items-center gap-1.5", children: [
939
- identity && /* @__PURE__ */ jsx3(ModelBrandStack, { identity, size: "sm" }),
1071
+ node: /* @__PURE__ */ jsxs4("span", { className: "flex items-center gap-1.5", children: [
1072
+ identity && /* @__PURE__ */ jsx5(ModelBrandStack, { identity, size: "sm" }),
940
1073
  modelLabel
941
1074
  ] })
942
1075
  });
@@ -948,14 +1081,14 @@ function AgentSessionControls({
948
1081
  segments.push({
949
1082
  key: "effort",
950
1083
  label: effortLabel,
951
- node: /* @__PURE__ */ jsxs3("span", { className: "flex items-center gap-1.5", children: [
952
- /* @__PURE__ */ jsx3("span", { className: "flex w-3.5 shrink-0 justify-center", children: /* @__PURE__ */ jsx3(ReasoningGlyph, { level: reasoning.value }) }),
1084
+ node: /* @__PURE__ */ jsxs4("span", { className: "flex items-center gap-1.5", children: [
1085
+ /* @__PURE__ */ jsx5("span", { className: "flex w-3.5 shrink-0 justify-center", children: /* @__PURE__ */ jsx5(ReasoningGlyph, { level: reasoning.value }) }),
953
1086
  effortLabel
954
1087
  ] })
955
1088
  });
956
1089
  }
957
- const summary = /* @__PURE__ */ jsx3("span", { className: "flex items-center gap-1.5", children: segments.map((segment, index) => /* @__PURE__ */ jsxs3(React3.Fragment, { children: [
958
- index > 0 && /* @__PURE__ */ jsx3(
1090
+ const summary = /* @__PURE__ */ jsx5("span", { className: "flex items-center gap-1.5", children: segments.map((segment, index) => /* @__PURE__ */ jsxs4(React5.Fragment, { children: [
1091
+ index > 0 && /* @__PURE__ */ jsx5(
959
1092
  "span",
960
1093
  {
961
1094
  className: "text-base leading-none text-muted-foreground/60",
@@ -965,7 +1098,7 @@ function AgentSessionControls({
965
1098
  ),
966
1099
  segment.node
967
1100
  ] }, segment.key)) });
968
- return /* @__PURE__ */ jsx3(
1101
+ return /* @__PURE__ */ jsx5(
969
1102
  SummaryControls,
970
1103
  {
971
1104
  className,
@@ -980,7 +1113,7 @@ function AgentSessionControls({
980
1113
  }
981
1114
  );
982
1115
  }
983
- return /* @__PURE__ */ jsxs3(
1116
+ return /* @__PURE__ */ jsxs4(
984
1117
  "div",
985
1118
  {
986
1119
  className: cn("flex flex-wrap items-center gap-2", className),
@@ -990,7 +1123,7 @@ function AgentSessionControls({
990
1123
  harnessNode,
991
1124
  modelNode,
992
1125
  reasoningNode,
993
- trailing && /* @__PURE__ */ jsx3("div", { className: "ml-auto flex items-center gap-2", children: trailing })
1126
+ trailing && /* @__PURE__ */ jsx5("div", { className: "ml-auto flex items-center gap-2", children: trailing })
994
1127
  ]
995
1128
  }
996
1129
  );
@@ -1003,11 +1136,11 @@ function GearControls({
1003
1136
  reasoningNode,
1004
1137
  trailing
1005
1138
  }) {
1006
- return /* @__PURE__ */ jsx3(
1139
+ return /* @__PURE__ */ jsx5(
1007
1140
  CollapsibleControls,
1008
1141
  {
1009
1142
  className,
1010
- trigger: /* @__PURE__ */ jsx3(SlidersHorizontal, { className: "h-4 w-4 text-muted-foreground" }),
1143
+ trigger: /* @__PURE__ */ jsx5(SlidersHorizontal, { className: "h-4 w-4 text-muted-foreground" }),
1011
1144
  triggerClassName: cn(
1012
1145
  "inline-flex h-8 w-8 items-center justify-center rounded-lg border border-[var(--md3-outline-variant)] bg-surface-container",
1013
1146
  "text-foreground shadow-sm transition-colors",
@@ -1031,8 +1164,8 @@ function GearSection({
1031
1164
  label,
1032
1165
  children
1033
1166
  }) {
1034
- return /* @__PURE__ */ jsxs3("div", { className: "flex flex-col gap-1", children: [
1035
- /* @__PURE__ */ jsx3("span", { className: "px-0.5 text-[10px] font-medium uppercase tracking-wide text-muted-foreground/70", children: label }),
1167
+ return /* @__PURE__ */ jsxs4("div", { className: "flex flex-col gap-1", children: [
1168
+ /* @__PURE__ */ jsx5("span", { className: "px-0.5 text-[10px] font-medium uppercase tracking-wide text-muted-foreground/70", children: label }),
1036
1169
  children
1037
1170
  ] });
1038
1171
  }
@@ -1047,13 +1180,13 @@ function SummaryControls({
1047
1180
  reasoningNode,
1048
1181
  trailing
1049
1182
  }) {
1050
- return /* @__PURE__ */ jsx3(
1183
+ return /* @__PURE__ */ jsx5(
1051
1184
  CollapsibleControls,
1052
1185
  {
1053
1186
  className,
1054
- trigger: /* @__PURE__ */ jsxs3(Fragment2, { children: [
1187
+ trigger: /* @__PURE__ */ jsxs4(Fragment2, { children: [
1055
1188
  summary,
1056
- /* @__PURE__ */ jsx3(ChevronDown3, { className: "h-3.5 w-3.5 shrink-0 text-muted-foreground" })
1189
+ /* @__PURE__ */ jsx5(ChevronDown3, { className: "h-3.5 w-3.5 shrink-0 text-muted-foreground" })
1057
1190
  ] }),
1058
1191
  triggerTitle: title,
1059
1192
  triggerClassName: cn(
@@ -1088,15 +1221,15 @@ function CollapsibleControls({
1088
1221
  reasoningNode,
1089
1222
  trailing
1090
1223
  }) {
1091
- return /* @__PURE__ */ jsxs3(
1224
+ return /* @__PURE__ */ jsxs4(
1092
1225
  "div",
1093
1226
  {
1094
1227
  className: cn("flex items-center gap-2", className),
1095
1228
  "data-testid": "agent-session-controls",
1096
1229
  children: [
1097
- trailing && /* @__PURE__ */ jsx3("div", { className: "flex items-center gap-2", children: trailing }),
1098
- /* @__PURE__ */ jsxs3(DropdownMenu2.Root, { children: [
1099
- /* @__PURE__ */ jsx3(DropdownMenu2.Trigger, { asChild: true, children: /* @__PURE__ */ jsx3(
1230
+ trailing && /* @__PURE__ */ jsx5("div", { className: "flex items-center gap-2", children: trailing }),
1231
+ /* @__PURE__ */ jsxs4(DropdownMenu2.Root, { children: [
1232
+ /* @__PURE__ */ jsx5(DropdownMenu2.Trigger, { asChild: true, children: /* @__PURE__ */ jsx5(
1100
1233
  "button",
1101
1234
  {
1102
1235
  type: "button",
@@ -1106,7 +1239,7 @@ function CollapsibleControls({
1106
1239
  children: trigger
1107
1240
  }
1108
1241
  ) }),
1109
- /* @__PURE__ */ jsx3(DropdownMenu2.Portal, { children: /* @__PURE__ */ jsxs3(
1242
+ /* @__PURE__ */ jsx5(DropdownMenu2.Portal, { children: /* @__PURE__ */ jsxs4(
1110
1243
  DropdownMenu2.Content,
1111
1244
  {
1112
1245
  side: contentSide,
@@ -1128,10 +1261,10 @@ function CollapsibleControls({
1128
1261
  contentClassName
1129
1262
  ),
1130
1263
  children: [
1131
- profileNode && /* @__PURE__ */ jsx3(GearSection, { label: "Agent", children: profileNode }),
1132
- harnessNode && /* @__PURE__ */ jsx3(GearSection, { label: "Harness", children: harnessNode }),
1133
- modelNode && /* @__PURE__ */ jsx3(GearSection, { label: "Model", children: modelNode }),
1134
- reasoningNode && /* @__PURE__ */ jsx3(GearSection, { label: "Effort", children: reasoningNode })
1264
+ profileNode && /* @__PURE__ */ jsx5(GearSection, { label: "Agent", children: profileNode }),
1265
+ harnessNode && /* @__PURE__ */ jsx5(GearSection, { label: "Harness", children: harnessNode }),
1266
+ modelNode && /* @__PURE__ */ jsx5(GearSection, { label: "Model", children: modelNode }),
1267
+ reasoningNode && /* @__PURE__ */ jsx5(GearSection, { label: "Effort", children: reasoningNode })
1135
1268
  ]
1136
1269
  }
1137
1270
  ) })
@@ -1152,7 +1285,7 @@ import {
1152
1285
  Upload,
1153
1286
  X
1154
1287
  } from "lucide-react";
1155
- import * as React4 from "react";
1288
+ import * as React6 from "react";
1156
1289
 
1157
1290
  // src/chat/attachment-validation.ts
1158
1291
  function isAcceptedType(file, accept) {
@@ -1206,7 +1339,7 @@ function validateComposerFiles(files, config) {
1206
1339
  }
1207
1340
 
1208
1341
  // src/chat/agent-composer.tsx
1209
- import { Fragment as Fragment3, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
1342
+ import { Fragment as Fragment3, jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
1210
1343
  var IMAGE_EXTENSION_BY_MIME = {
1211
1344
  "image/png": "png",
1212
1345
  "image/jpeg": "jpg",
@@ -1225,7 +1358,7 @@ function imageExtension(file) {
1225
1358
  const fromName = /\.([a-z0-9]+)$/i.exec(file.name)?.[1];
1226
1359
  return fromName?.toLowerCase() ?? "png";
1227
1360
  }
1228
- var MentionEditor = React4.lazy(() => import("./mention-editor-3Q6MYOIU.js"));
1361
+ var MentionEditor = React6.lazy(() => import("./mention-editor-3Q6MYOIU.js"));
1229
1362
  function AgentComposer({
1230
1363
  value,
1231
1364
  onChange,
@@ -1260,24 +1393,24 @@ function AgentComposer({
1260
1393
  sendLabel = "Send",
1261
1394
  autoFocus
1262
1395
  }) {
1263
- const textareaRef = React4.useRef(null);
1264
- const richFocusRef = React4.useRef(null);
1265
- const fileInputRef = React4.useRef(null);
1266
- const folderInputRef = React4.useRef(null);
1267
- const [dragOver, setDragOver] = React4.useState(false);
1268
- const dragDepth = React4.useRef(0);
1269
- const pastedImageCount = React4.useRef(0);
1270
- const resize = React4.useCallback(
1396
+ const textareaRef = React6.useRef(null);
1397
+ const richFocusRef = React6.useRef(null);
1398
+ const fileInputRef = React6.useRef(null);
1399
+ const folderInputRef = React6.useRef(null);
1400
+ const [dragOver, setDragOver] = React6.useState(false);
1401
+ const dragDepth = React6.useRef(0);
1402
+ const pastedImageCount = React6.useRef(0);
1403
+ const resize = React6.useCallback(
1271
1404
  (el) => {
1272
1405
  el.style.height = "auto";
1273
1406
  el.style.height = `${Math.min(el.scrollHeight, maxHeight)}px`;
1274
1407
  },
1275
1408
  [maxHeight]
1276
1409
  );
1277
- React4.useEffect(() => {
1410
+ React6.useEffect(() => {
1278
1411
  if (textareaRef.current) resize(textareaRef.current);
1279
1412
  }, [value, resize]);
1280
- React4.useEffect(() => {
1413
+ React6.useEffect(() => {
1281
1414
  if (!focusShortcut || disabled) return;
1282
1415
  function onKeyDown(event) {
1283
1416
  if ((event.metaKey || event.ctrlKey) && event.key.toLowerCase() === "l") {
@@ -1294,7 +1427,7 @@ function AgentComposer({
1294
1427
  if (!canSend) return;
1295
1428
  onSubmit();
1296
1429
  };
1297
- const registerRichFocus = React4.useCallback((focus) => {
1430
+ const registerRichFocus = React6.useCallback((focus) => {
1298
1431
  richFocusRef.current = focus;
1299
1432
  }, []);
1300
1433
  const dropEnabled = Boolean(onAttach);
@@ -1369,13 +1502,13 @@ function AgentComposer({
1369
1502
  "hover:bg-surface-container-highest hover:text-foreground focus:outline-none focus-visible:ring-1 focus-visible:ring-ring",
1370
1503
  "disabled:cursor-not-allowed disabled:opacity-50"
1371
1504
  );
1372
- return /* @__PURE__ */ jsxs4("div", { className: cn("relative", className), children: [
1373
- dragOver && /* @__PURE__ */ jsx4("div", { className: "pointer-events-none absolute inset-0 z-10 flex items-center justify-center rounded-2xl border-2 border-dashed border-primary/50 bg-surface-container-high/95", children: /* @__PURE__ */ jsxs4("div", { className: "text-center", children: [
1374
- /* @__PURE__ */ jsx4("span", { className: "mx-auto mb-2 flex size-11 items-center justify-center rounded-xl bg-primary/10 text-primary", children: /* @__PURE__ */ jsx4(Upload, { className: "size-5" }) }),
1375
- /* @__PURE__ */ jsx4("p", { className: "font-semibold text-foreground text-sm", children: dropTitle }),
1376
- /* @__PURE__ */ jsx4("p", { className: "mt-0.5 text-muted-foreground text-xs", children: dropDescription })
1505
+ return /* @__PURE__ */ jsxs5("div", { className: cn("relative", className), children: [
1506
+ dragOver && /* @__PURE__ */ jsx6("div", { className: "pointer-events-none absolute inset-0 z-10 flex items-center justify-center rounded-2xl border-2 border-dashed border-primary/50 bg-surface-container-high/95", children: /* @__PURE__ */ jsxs5("div", { className: "text-center", children: [
1507
+ /* @__PURE__ */ jsx6("span", { className: "mx-auto mb-2 flex size-11 items-center justify-center rounded-xl bg-primary/10 text-primary", children: /* @__PURE__ */ jsx6(Upload, { className: "size-5" }) }),
1508
+ /* @__PURE__ */ jsx6("p", { className: "font-semibold text-foreground text-sm", children: dropTitle }),
1509
+ /* @__PURE__ */ jsx6("p", { className: "mt-0.5 text-muted-foreground text-xs", children: dropDescription })
1377
1510
  ] }) }),
1378
- /* @__PURE__ */ jsxs4(
1511
+ /* @__PURE__ */ jsxs5(
1379
1512
  "div",
1380
1513
  {
1381
1514
  "data-testid": "agent-composer",
@@ -1389,10 +1522,10 @@ function AgentComposer({
1389
1522
  disabled && "opacity-60"
1390
1523
  ),
1391
1524
  children: [
1392
- attachments.length > 0 && /* @__PURE__ */ jsx4("div", { className: "flex flex-wrap gap-1.5", children: [...folderChips, ...fileChips].map((file) => {
1525
+ attachments.length > 0 && /* @__PURE__ */ jsx6("div", { className: "flex flex-wrap gap-1.5", children: [...folderChips, ...fileChips].map((file) => {
1393
1526
  const isError = file.status === "error";
1394
1527
  const isPending = file.status === "pending";
1395
- return /* @__PURE__ */ jsxs4(
1528
+ return /* @__PURE__ */ jsxs5(
1396
1529
  "span",
1397
1530
  {
1398
1531
  title: isError ? file.errorMessage : void 0,
@@ -1402,40 +1535,40 @@ function AgentComposer({
1402
1535
  isPending && "opacity-60"
1403
1536
  ),
1404
1537
  children: [
1405
- file.kind !== "folder" && file.previewUrl ? /* @__PURE__ */ jsx4(
1538
+ file.kind !== "folder" && file.previewUrl ? /* @__PURE__ */ jsx6(
1406
1539
  "img",
1407
1540
  {
1408
1541
  src: file.previewUrl,
1409
1542
  alt: "",
1410
1543
  className: "size-8 rounded object-cover"
1411
1544
  }
1412
- ) : file.kind === "folder" ? /* @__PURE__ */ jsx4(Folder, { className: "size-3 shrink-0" }) : /* @__PURE__ */ jsx4(Paperclip, { className: "size-3 shrink-0" }),
1413
- /* @__PURE__ */ jsx4("span", { className: "max-w-[150px] truncate", children: file.name }),
1414
- file.fileCount !== void 0 && /* @__PURE__ */ jsxs4("span", { className: "text-muted-foreground", children: [
1545
+ ) : file.kind === "folder" ? /* @__PURE__ */ jsx6(Folder, { className: "size-3 shrink-0" }) : /* @__PURE__ */ jsx6(Paperclip, { className: "size-3 shrink-0" }),
1546
+ /* @__PURE__ */ jsx6("span", { className: "max-w-[150px] truncate", children: file.name }),
1547
+ file.fileCount !== void 0 && /* @__PURE__ */ jsxs5("span", { className: "text-muted-foreground", children: [
1415
1548
  "(",
1416
1549
  file.fileCount,
1417
1550
  ")"
1418
1551
  ] }),
1419
- file.status === "uploading" && /* @__PURE__ */ jsx4(Loader2, { className: "size-3 animate-spin" }),
1420
- isError && file.errorMessage && /* @__PURE__ */ jsx4("span", { className: "max-w-[150px] truncate text-[var(--status-error,#ff4d6d)]/80", children: file.errorMessage }),
1421
- isError && onRetryFile && /* @__PURE__ */ jsx4(
1552
+ file.status === "uploading" && /* @__PURE__ */ jsx6(Loader2, { className: "size-3 animate-spin" }),
1553
+ isError && file.errorMessage && /* @__PURE__ */ jsx6("span", { className: "max-w-[150px] truncate text-[var(--status-error,#ff4d6d)]/80", children: file.errorMessage }),
1554
+ isError && onRetryFile && /* @__PURE__ */ jsx6(
1422
1555
  "button",
1423
1556
  {
1424
1557
  type: "button",
1425
1558
  "aria-label": `Retry upload ${file.name}`,
1426
1559
  onClick: () => onRetryFile(file.id),
1427
1560
  className: "rounded p-0.5 text-muted-foreground transition-colors hover:text-foreground focus:outline-none focus-visible:ring-1 focus-visible:ring-ring",
1428
- children: /* @__PURE__ */ jsx4(RotateCcw, { className: "size-3" })
1561
+ children: /* @__PURE__ */ jsx6(RotateCcw, { className: "size-3" })
1429
1562
  }
1430
1563
  ),
1431
- onRemoveFile && /* @__PURE__ */ jsx4(
1564
+ onRemoveFile && /* @__PURE__ */ jsx6(
1432
1565
  "button",
1433
1566
  {
1434
1567
  type: "button",
1435
1568
  "aria-label": `Remove ${file.name}`,
1436
1569
  onClick: () => onRemoveFile(file.id),
1437
1570
  className: "rounded p-0.5 text-muted-foreground transition-colors hover:text-foreground focus:outline-none focus-visible:ring-1 focus-visible:ring-ring",
1438
- children: /* @__PURE__ */ jsx4(X, { className: "size-3" })
1571
+ children: /* @__PURE__ */ jsx6(X, { className: "size-3" })
1439
1572
  }
1440
1573
  )
1441
1574
  ]
@@ -1443,10 +1576,10 @@ function AgentComposer({
1443
1576
  file.id
1444
1577
  );
1445
1578
  }) }),
1446
- mention ? /* @__PURE__ */ jsx4(
1447
- React4.Suspense,
1579
+ mention ? /* @__PURE__ */ jsx6(
1580
+ React6.Suspense,
1448
1581
  {
1449
- fallback: /* @__PURE__ */ jsx4(
1582
+ fallback: /* @__PURE__ */ jsx6(
1450
1583
  "textarea",
1451
1584
  {
1452
1585
  rows: minRows,
@@ -1460,7 +1593,7 @@ function AgentComposer({
1460
1593
  )
1461
1594
  }
1462
1595
  ),
1463
- children: /* @__PURE__ */ jsx4(
1596
+ children: /* @__PURE__ */ jsx6(
1464
1597
  MentionEditor,
1465
1598
  {
1466
1599
  value,
@@ -1477,7 +1610,7 @@ function AgentComposer({
1477
1610
  }
1478
1611
  )
1479
1612
  }
1480
- ) : /* @__PURE__ */ jsx4(
1613
+ ) : /* @__PURE__ */ jsx6(
1481
1614
  "textarea",
1482
1615
  {
1483
1616
  ref: textareaRef,
@@ -1502,9 +1635,9 @@ function AgentComposer({
1502
1635
  )
1503
1636
  }
1504
1637
  ),
1505
- /* @__PURE__ */ jsxs4("div", { className: "flex items-end gap-2", children: [
1506
- onAttach && /* @__PURE__ */ jsxs4(Fragment3, { children: [
1507
- /* @__PURE__ */ jsx4(
1638
+ /* @__PURE__ */ jsxs5("div", { className: "flex items-end gap-2", children: [
1639
+ onAttach && /* @__PURE__ */ jsxs5(Fragment3, { children: [
1640
+ /* @__PURE__ */ jsx6(
1508
1641
  "button",
1509
1642
  {
1510
1643
  type: "button",
@@ -1513,10 +1646,10 @@ function AgentComposer({
1513
1646
  "aria-label": "Attach files",
1514
1647
  title: "Attach files",
1515
1648
  className: attachButtonClass,
1516
- children: /* @__PURE__ */ jsx4(Paperclip, { className: "size-4" })
1649
+ children: /* @__PURE__ */ jsx6(Paperclip, { className: "size-4" })
1517
1650
  }
1518
1651
  ),
1519
- /* @__PURE__ */ jsx4(
1652
+ /* @__PURE__ */ jsx6(
1520
1653
  "input",
1521
1654
  {
1522
1655
  ref: fileInputRef,
@@ -1532,8 +1665,8 @@ function AgentComposer({
1532
1665
  }
1533
1666
  )
1534
1667
  ] }),
1535
- onAttachFolder && /* @__PURE__ */ jsxs4(Fragment3, { children: [
1536
- /* @__PURE__ */ jsx4(
1668
+ onAttachFolder && /* @__PURE__ */ jsxs5(Fragment3, { children: [
1669
+ /* @__PURE__ */ jsx6(
1537
1670
  "button",
1538
1671
  {
1539
1672
  type: "button",
@@ -1542,10 +1675,10 @@ function AgentComposer({
1542
1675
  "aria-label": "Attach folder",
1543
1676
  title: "Attach folder",
1544
1677
  className: attachButtonClass,
1545
- children: /* @__PURE__ */ jsx4(Folder, { className: "size-4" })
1678
+ children: /* @__PURE__ */ jsx6(Folder, { className: "size-4" })
1546
1679
  }
1547
1680
  ),
1548
- /* @__PURE__ */ jsx4(
1681
+ /* @__PURE__ */ jsx6(
1549
1682
  "input",
1550
1683
  {
1551
1684
  ref: folderInputRef,
@@ -1561,8 +1694,8 @@ function AgentComposer({
1561
1694
  }
1562
1695
  )
1563
1696
  ] }),
1564
- /* @__PURE__ */ jsxs4("div", { className: "flex min-w-0 flex-1 items-center gap-2 overflow-x-auto overscroll-x-contain [-ms-overflow-style:none] [scrollbar-width:none] [&::-webkit-scrollbar]:hidden", children: [
1565
- controls ?? /* @__PURE__ */ jsx4(
1697
+ /* @__PURE__ */ jsxs5("div", { className: "flex min-w-0 flex-1 items-center gap-2 overflow-x-auto overscroll-x-contain [-ms-overflow-style:none] [scrollbar-width:none] [&::-webkit-scrollbar]:hidden", children: [
1698
+ controls ?? /* @__PURE__ */ jsx6(
1566
1699
  AgentSessionControls,
1567
1700
  {
1568
1701
  context,
@@ -1572,9 +1705,9 @@ function AgentComposer({
1572
1705
  reasoning
1573
1706
  }
1574
1707
  ),
1575
- trailing && /* @__PURE__ */ jsx4("div", { className: "ml-auto flex shrink-0 items-center gap-2", children: trailing })
1708
+ trailing && /* @__PURE__ */ jsx6("div", { className: "ml-auto flex shrink-0 items-center gap-2", children: trailing })
1576
1709
  ] }),
1577
- busy && onCancel ? /* @__PURE__ */ jsx4(
1710
+ busy && onCancel ? /* @__PURE__ */ jsx6(
1578
1711
  "button",
1579
1712
  {
1580
1713
  type: "button",
@@ -1585,9 +1718,9 @@ function AgentComposer({
1585
1718
  "bg-[var(--status-error,#ff4d6d)]/15 text-[var(--status-error,#ff4d6d)] transition-colors",
1586
1719
  "hover:bg-[var(--status-error,#ff4d6d)]/25"
1587
1720
  ),
1588
- children: /* @__PURE__ */ jsx4(Square, { className: "size-3.5 fill-current" })
1721
+ children: /* @__PURE__ */ jsx6(Square, { className: "size-3.5 fill-current" })
1589
1722
  }
1590
- ) : /* @__PURE__ */ jsx4(
1723
+ ) : /* @__PURE__ */ jsx6(
1591
1724
  "button",
1592
1725
  {
1593
1726
  type: "button",
@@ -1598,26 +1731,26 @@ function AgentComposer({
1598
1731
  "flex size-9 shrink-0 items-center justify-center rounded-full bg-primary text-primary-foreground",
1599
1732
  "transition-opacity hover:opacity-90 disabled:opacity-40"
1600
1733
  ),
1601
- children: busy ? /* @__PURE__ */ jsx4(Loader2, { className: "size-4 animate-spin" }) : /* @__PURE__ */ jsx4(ArrowUp, { className: "size-4" })
1734
+ children: busy ? /* @__PURE__ */ jsx6(Loader2, { className: "size-4 animate-spin" }) : /* @__PURE__ */ jsx6(ArrowUp, { className: "size-4" })
1602
1735
  }
1603
1736
  )
1604
1737
  ] })
1605
1738
  ]
1606
1739
  }
1607
1740
  ),
1608
- focusShortcut && /* @__PURE__ */ jsx4("div", { className: "mt-1.5 flex justify-end px-1", children: /* @__PURE__ */ jsxs4("span", { className: "text-muted-foreground text-xs", children: [
1609
- /* @__PURE__ */ jsx4("kbd", { className: "rounded border border-[var(--md3-outline-variant)] bg-surface-container px-1 py-0.5 text-[10px]", children: "Cmd" }),
1610
- /* @__PURE__ */ jsx4("kbd", { className: "ml-0.5 rounded border border-[var(--md3-outline-variant)] bg-surface-container px-1 py-0.5 text-[10px]", children: "L" }),
1611
- /* @__PURE__ */ jsx4("span", { className: "ml-1", children: "to focus" })
1741
+ focusShortcut && /* @__PURE__ */ jsx6("div", { className: "mt-1.5 flex justify-end px-1", children: /* @__PURE__ */ jsxs5("span", { className: "text-muted-foreground text-xs", children: [
1742
+ /* @__PURE__ */ jsx6("kbd", { className: "rounded border border-[var(--md3-outline-variant)] bg-surface-container px-1 py-0.5 text-[10px]", children: "Cmd" }),
1743
+ /* @__PURE__ */ jsx6("kbd", { className: "ml-0.5 rounded border border-[var(--md3-outline-variant)] bg-surface-container px-1 py-0.5 text-[10px]", children: "L" }),
1744
+ /* @__PURE__ */ jsx6("span", { className: "ml-1", children: "to focus" })
1612
1745
  ] }) })
1613
1746
  ] });
1614
1747
  }
1615
1748
 
1616
1749
  // src/chat/artifact-agent-dock.tsx
1617
- import * as React5 from "react";
1750
+ import * as React7 from "react";
1618
1751
  import { Sparkles as Sparkles2, Send, Square as Square2, X as X2 } from "lucide-react";
1619
1752
  import { ChatMessage } from "@tangle-network/ui/chat";
1620
- import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
1753
+ import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
1621
1754
  function defaultScopeLabel(scope) {
1622
1755
  if (scope.label) return scope.label;
1623
1756
  switch (scope.kind) {
@@ -1663,23 +1796,23 @@ function ArtifactAgentDock({
1663
1796
  onError,
1664
1797
  className
1665
1798
  }) {
1666
- const [threadId, setThreadId] = React5.useState(null);
1667
- const [messages, setMessages] = React5.useState([]);
1668
- const [composer, setComposer] = React5.useState(defaultPrompt ?? "");
1669
- const [streamContent, setStreamContent] = React5.useState("");
1670
- const [sending, setSending] = React5.useState(false);
1671
- const [loading, setLoading] = React5.useState(false);
1672
- const abortRef = React5.useRef(null);
1673
- const scrollRef = React5.useRef(null);
1674
- const scopeTokenRef = React5.useRef(0);
1675
- const reportError = React5.useCallback(
1799
+ const [threadId, setThreadId] = React7.useState(null);
1800
+ const [messages, setMessages] = React7.useState([]);
1801
+ const [composer, setComposer] = React7.useState(defaultPrompt ?? "");
1802
+ const [streamContent, setStreamContent] = React7.useState("");
1803
+ const [sending, setSending] = React7.useState(false);
1804
+ const [loading, setLoading] = React7.useState(false);
1805
+ const abortRef = React7.useRef(null);
1806
+ const scrollRef = React7.useRef(null);
1807
+ const scopeTokenRef = React7.useRef(0);
1808
+ const reportError = React7.useCallback(
1676
1809
  (message) => {
1677
1810
  if (onError) onError(message);
1678
1811
  else if (typeof console !== "undefined") console.error("[ArtifactAgentDock]", message);
1679
1812
  },
1680
1813
  [onError]
1681
1814
  );
1682
- React5.useEffect(() => {
1815
+ React7.useEffect(() => {
1683
1816
  if (!open) return;
1684
1817
  scopeTokenRef.current += 1;
1685
1818
  abortRef.current?.abort();
@@ -1715,11 +1848,11 @@ function ArtifactAgentDock({
1715
1848
  cancelled = true;
1716
1849
  };
1717
1850
  }, [open, scope.kind, scope.key, transport, reportError]);
1718
- React5.useEffect(() => {
1851
+ React7.useEffect(() => {
1719
1852
  if (!scrollRef.current) return;
1720
1853
  scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
1721
1854
  }, [messages.length, streamContent]);
1722
- const send = React5.useCallback(async () => {
1855
+ const send = React7.useCallback(async () => {
1723
1856
  const text = composer.trim();
1724
1857
  if (!text || !threadId || sending) return;
1725
1858
  const token = scopeTokenRef.current;
@@ -1769,7 +1902,7 @@ function ArtifactAgentDock({
1769
1902
  if (abortRef.current === controller) abortRef.current = null;
1770
1903
  }
1771
1904
  }, [composer, threadId, sending, transport, reportError]);
1772
- const cancel = React5.useCallback(() => {
1905
+ const cancel = React7.useCallback(() => {
1773
1906
  if (threadId && transport.cancel) {
1774
1907
  try {
1775
1908
  void Promise.resolve(transport.cancel({ threadId })).catch(() => {
@@ -1783,7 +1916,7 @@ function ArtifactAgentDock({
1783
1916
  }, [threadId, transport]);
1784
1917
  if (!open) return null;
1785
1918
  const heading = defaultScopeLabel(scope);
1786
- return /* @__PURE__ */ jsxs5(
1919
+ return /* @__PURE__ */ jsxs6(
1787
1920
  "aside",
1788
1921
  {
1789
1922
  className: cn(
@@ -1792,38 +1925,38 @@ function ArtifactAgentDock({
1792
1925
  ),
1793
1926
  "aria-label": `Agent chat about ${heading}`,
1794
1927
  children: [
1795
- /* @__PURE__ */ jsxs5("header", { className: "flex items-center justify-between border-b border-[var(--md3-outline-variant)] bg-surface-container-low px-4 py-3", children: [
1796
- /* @__PURE__ */ jsxs5("div", { className: "flex min-w-0 items-center gap-2", children: [
1797
- /* @__PURE__ */ jsx5(Sparkles2, { className: "h-4 w-4 shrink-0 text-muted-foreground" }),
1798
- /* @__PURE__ */ jsxs5("div", { className: "min-w-0", children: [
1799
- /* @__PURE__ */ jsx5("div", { className: "text-[10px] font-medium uppercase tracking-wider text-muted-foreground", children: "Discussing" }),
1800
- /* @__PURE__ */ jsx5("div", { className: "truncate text-sm font-semibold text-foreground", children: heading })
1928
+ /* @__PURE__ */ jsxs6("header", { className: "flex items-center justify-between border-b border-[var(--md3-outline-variant)] bg-surface-container-low px-4 py-3", children: [
1929
+ /* @__PURE__ */ jsxs6("div", { className: "flex min-w-0 items-center gap-2", children: [
1930
+ /* @__PURE__ */ jsx7(Sparkles2, { className: "h-4 w-4 shrink-0 text-muted-foreground" }),
1931
+ /* @__PURE__ */ jsxs6("div", { className: "min-w-0", children: [
1932
+ /* @__PURE__ */ jsx7("div", { className: "text-[10px] font-medium uppercase tracking-wider text-muted-foreground", children: "Discussing" }),
1933
+ /* @__PURE__ */ jsx7("div", { className: "truncate text-sm font-semibold text-foreground", children: heading })
1801
1934
  ] })
1802
1935
  ] }),
1803
- /* @__PURE__ */ jsx5(
1936
+ /* @__PURE__ */ jsx7(
1804
1937
  "button",
1805
1938
  {
1806
1939
  type: "button",
1807
1940
  onClick: () => onOpenChange(false),
1808
1941
  "aria-label": "Close artifact chat",
1809
1942
  className: "rounded-md p-1 text-muted-foreground transition-colors hover:bg-surface-container-high hover:text-foreground",
1810
- children: /* @__PURE__ */ jsx5(X2, { className: "h-4 w-4" })
1943
+ children: /* @__PURE__ */ jsx7(X2, { className: "h-4 w-4" })
1811
1944
  }
1812
1945
  )
1813
1946
  ] }),
1814
- /* @__PURE__ */ jsx5("div", { ref: scrollRef, className: "flex-1 overflow-y-auto px-3 py-4", children: loading ? /* @__PURE__ */ jsx5("div", { className: "px-3 py-8 text-center text-xs text-muted-foreground", children: "Loading conversation\u2026" }) : messages.length === 0 && !streamContent ? /* @__PURE__ */ jsxs5("div", { className: "px-3 py-12 text-center", children: [
1815
- /* @__PURE__ */ jsx5(Sparkles2, { className: "mx-auto mb-3 h-5 w-5 text-muted-foreground/40" }),
1816
- /* @__PURE__ */ jsxs5("p", { className: "text-sm text-foreground", children: [
1947
+ /* @__PURE__ */ jsx7("div", { ref: scrollRef, className: "flex-1 overflow-y-auto px-3 py-4", children: loading ? /* @__PURE__ */ jsx7("div", { className: "px-3 py-8 text-center text-xs text-muted-foreground", children: "Loading conversation\u2026" }) : messages.length === 0 && !streamContent ? /* @__PURE__ */ jsxs6("div", { className: "px-3 py-12 text-center", children: [
1948
+ /* @__PURE__ */ jsx7(Sparkles2, { className: "mx-auto mb-3 h-5 w-5 text-muted-foreground/40" }),
1949
+ /* @__PURE__ */ jsxs6("p", { className: "text-sm text-foreground", children: [
1817
1950
  "Ask the agent about this ",
1818
1951
  scope.kind === "vault-file" ? "document" : scope.kind,
1819
1952
  "."
1820
1953
  ] }),
1821
- /* @__PURE__ */ jsx5("p", { className: "mt-1 text-xs text-muted-foreground", children: "It can read the current contents and propose edits, rewrites, or follow-ups." })
1822
- ] }) : /* @__PURE__ */ jsxs5("div", { className: "space-y-3", children: [
1823
- messages.map((m) => /* @__PURE__ */ jsx5(ChatMessage, { role: m.role, content: m.content, timestamp: m.createdAt }, m.id)),
1824
- streamContent && /* @__PURE__ */ jsx5(ChatMessage, { role: "assistant", content: streamContent, isStreaming: true })
1954
+ /* @__PURE__ */ jsx7("p", { className: "mt-1 text-xs text-muted-foreground", children: "It can read the current contents and propose edits, rewrites, or follow-ups." })
1955
+ ] }) : /* @__PURE__ */ jsxs6("div", { className: "space-y-3", children: [
1956
+ messages.map((m) => /* @__PURE__ */ jsx7(ChatMessage, { role: m.role, content: m.content, timestamp: m.createdAt }, m.id)),
1957
+ streamContent && /* @__PURE__ */ jsx7(ChatMessage, { role: "assistant", content: streamContent, isStreaming: true })
1825
1958
  ] }) }),
1826
- /* @__PURE__ */ jsx5(
1959
+ /* @__PURE__ */ jsx7(
1827
1960
  "form",
1828
1961
  {
1829
1962
  className: "border-t border-[var(--md3-outline-variant)] bg-surface-container-low p-3",
@@ -1831,8 +1964,8 @@ function ArtifactAgentDock({
1831
1964
  e.preventDefault();
1832
1965
  void send();
1833
1966
  },
1834
- children: /* @__PURE__ */ jsxs5("div", { className: "flex items-end gap-2", children: [
1835
- /* @__PURE__ */ jsx5(
1967
+ children: /* @__PURE__ */ jsxs6("div", { className: "flex items-end gap-2", children: [
1968
+ /* @__PURE__ */ jsx7(
1836
1969
  "textarea",
1837
1970
  {
1838
1971
  value: composer,
@@ -1849,23 +1982,23 @@ function ArtifactAgentDock({
1849
1982
  className: "min-h-[44px] flex-1 resize-none rounded-[20px] border border-[var(--md3-outline-variant)] bg-surface-dim px-3.5 py-2 text-sm text-foreground placeholder:text-muted-foreground transition-colors focus:outline-none focus:border-[var(--border-accent)] disabled:opacity-50"
1850
1983
  }
1851
1984
  ),
1852
- sending ? /* @__PURE__ */ jsx5(
1985
+ sending ? /* @__PURE__ */ jsx7(
1853
1986
  "button",
1854
1987
  {
1855
1988
  type: "button",
1856
1989
  onClick: cancel,
1857
1990
  "aria-label": "Cancel",
1858
1991
  className: "inline-flex h-9 w-9 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-surface-container-high hover:text-foreground",
1859
- children: /* @__PURE__ */ jsx5(Square2, { className: "h-4 w-4" })
1992
+ children: /* @__PURE__ */ jsx7(Square2, { className: "h-4 w-4" })
1860
1993
  }
1861
- ) : /* @__PURE__ */ jsx5(
1994
+ ) : /* @__PURE__ */ jsx7(
1862
1995
  "button",
1863
1996
  {
1864
1997
  type: "submit",
1865
1998
  disabled: !composer.trim() || !threadId,
1866
1999
  "aria-label": "Send",
1867
2000
  className: "inline-flex h-9 w-9 items-center justify-center rounded-full bg-primary text-primary-foreground transition-colors hover:bg-primary/90 disabled:opacity-50",
1868
- children: /* @__PURE__ */ jsx5(Send, { className: "h-4 w-4" })
2001
+ children: /* @__PURE__ */ jsx7(Send, { className: "h-4 w-4" })
1869
2002
  }
1870
2003
  )
1871
2004
  ] })