@tangle-network/agent-app 0.45.63 → 0.45.65

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.
@@ -10,18 +10,23 @@ import {
10
10
  ChevronDown,
11
11
  OVERLAY_SHADOW,
12
12
  POPOVER_OPTION_FOCUS,
13
+ POPOVER_SURFACE_ATTR,
13
14
  PopoverSurface,
15
+ filterAcceptedFiles,
16
+ renamePastedImages,
14
17
  usePending,
15
18
  usePopover
16
- } from "./chunk-FZKNVQYP.js";
19
+ } from "./chunk-23J72UUA.js";
17
20
  import {
18
21
  AsyncView
19
22
  } from "./chunk-3UBAO3N5.js";
20
23
  import {
21
24
  UNTITLED_SESSION_LABEL,
25
+ filterCommandPaletteItems,
26
+ groupCommandPaletteItems,
22
27
  mergeSessionPages,
23
28
  sessionLabel
24
- } from "./chunk-PC2WYTK7.js";
29
+ } from "./chunk-EDTWGSQT.js";
25
30
  import {
26
31
  attachmentPartsFromMessageParts
27
32
  } from "./chunk-5ZTFZBS6.js";
@@ -41,7 +46,7 @@ import {
41
46
  } from "./chunk-YJMCRXQQ.js";
42
47
 
43
48
  // src/web-react/index.tsx
44
- import { useEffect as useEffect12, useMemo as useMemo7, useRef as useRef12, useState as useState16, memo } from "react";
49
+ import { useEffect as useEffect14, useMemo as useMemo9, useRef as useRef14, useState as useState18, memo } from "react";
45
50
  import { InlineToolItem, RunRowShell } from "@tangle-network/ui/run";
46
51
 
47
52
  // src/web-react/smooth-text.ts
@@ -1408,11 +1413,147 @@ async function streamChatTurn(opts) {
1408
1413
 
1409
1414
  // src/web-react/chat-composer.tsx
1410
1415
  import {
1411
- useCallback as useCallback2,
1412
- useEffect as useEffect5,
1413
- useRef as useRef5,
1414
- useState as useState7
1416
+ useCallback as useCallback3,
1417
+ useEffect as useEffect6,
1418
+ useMemo as useMemo3,
1419
+ useId,
1420
+ useRef as useRef6,
1421
+ useState as useState8
1415
1422
  } from "react";
1423
+
1424
+ // src/web-react/use-dictation.ts
1425
+ import { useCallback as useCallback2, useEffect as useEffect5, useRef as useRef5, useState as useState7 } from "react";
1426
+ var PREFERRED_MIME_TYPES = ["audio/webm;codecs=opus", "audio/webm", "audio/mp4"];
1427
+ function pickDictationMimeType() {
1428
+ if (typeof MediaRecorder === "undefined" || typeof MediaRecorder.isTypeSupported !== "function") {
1429
+ return void 0;
1430
+ }
1431
+ for (const type of PREFERRED_MIME_TYPES) {
1432
+ if (MediaRecorder.isTypeSupported(type)) return type;
1433
+ }
1434
+ return void 0;
1435
+ }
1436
+ function detectDictationSupport() {
1437
+ return typeof navigator !== "undefined" && typeof navigator.mediaDevices?.getUserMedia === "function" && typeof MediaRecorder !== "undefined";
1438
+ }
1439
+ function dictationErrorMessage(error) {
1440
+ if (error instanceof DOMException) {
1441
+ if (error.name === "NotAllowedError") return "Microphone access was denied \u2014 allow it in the browser to dictate.";
1442
+ if (error.name === "NotFoundError") return "No microphone found on this device.";
1443
+ }
1444
+ return "Could not start recording.";
1445
+ }
1446
+ function formatDictationElapsed(totalSeconds) {
1447
+ const safe = Number.isFinite(totalSeconds) && totalSeconds > 0 ? Math.floor(totalSeconds) : 0;
1448
+ const minutes = Math.floor(safe / 60);
1449
+ const seconds = safe % 60;
1450
+ return `${minutes}:${String(seconds).padStart(2, "0")}`;
1451
+ }
1452
+ function releaseStream(stream) {
1453
+ for (const track of stream.getTracks()) track.stop();
1454
+ }
1455
+ function useDictation({ onDictate, onError }) {
1456
+ const [supported] = useState7(detectDictationSupport);
1457
+ const [recording, setRecording] = useState7(false);
1458
+ const [elapsedSeconds, setElapsedSeconds] = useState7(0);
1459
+ const sessionRef = useRef5(null);
1460
+ const cancelPendingStartRef = useRef5(null);
1461
+ const callbacksRef = useRef5({ onDictate, onError });
1462
+ callbacksRef.current = { onDictate, onError };
1463
+ useEffect5(() => {
1464
+ if (!recording) return;
1465
+ setElapsedSeconds(0);
1466
+ const id = setInterval(() => setElapsedSeconds((s) => s + 1), 1e3);
1467
+ return () => clearInterval(id);
1468
+ }, [recording]);
1469
+ const teardown = useCallback2((cancelled) => {
1470
+ const session = sessionRef.current;
1471
+ if (session === null) return;
1472
+ session.cancelled = session.cancelled || cancelled;
1473
+ sessionRef.current = null;
1474
+ releaseStream(session.stream);
1475
+ setRecording(false);
1476
+ }, []);
1477
+ const stop = useCallback2(() => {
1478
+ cancelPendingStartRef.current?.();
1479
+ cancelPendingStartRef.current = null;
1480
+ const session = sessionRef.current;
1481
+ if (session === null || session.cancelled) return;
1482
+ if (session.recorder.state !== "inactive") session.recorder.stop();
1483
+ }, []);
1484
+ const start = useCallback2(() => {
1485
+ if (!supported) return;
1486
+ if (sessionRef.current !== null || cancelPendingStartRef.current !== null) return;
1487
+ let pendingCancelled = false;
1488
+ cancelPendingStartRef.current = () => {
1489
+ pendingCancelled = true;
1490
+ };
1491
+ navigator.mediaDevices.getUserMedia({ audio: true }).then(
1492
+ (stream) => {
1493
+ cancelPendingStartRef.current = null;
1494
+ if (pendingCancelled) {
1495
+ releaseStream(stream);
1496
+ return;
1497
+ }
1498
+ const mimeType = pickDictationMimeType();
1499
+ const recorder = new MediaRecorder(stream, mimeType === void 0 ? void 0 : { mimeType });
1500
+ const session = {
1501
+ stream,
1502
+ recorder,
1503
+ chunks: [],
1504
+ mimeType: recorder.mimeType || mimeType || "",
1505
+ startedAt: Date.now(),
1506
+ cancelled: false
1507
+ };
1508
+ sessionRef.current = session;
1509
+ recorder.ondataavailable = (event) => {
1510
+ if (event.data.size > 0) session.chunks.push(event.data);
1511
+ };
1512
+ recorder.onstop = () => {
1513
+ teardown(session.cancelled);
1514
+ if (session.cancelled) return;
1515
+ const blob = new Blob(session.chunks, { type: session.mimeType });
1516
+ if (blob.size === 0) {
1517
+ callbacksRef.current.onError?.("Nothing was recorded.");
1518
+ return;
1519
+ }
1520
+ const durationSeconds = Math.max(0, Math.round((Date.now() - session.startedAt) / 1e3));
1521
+ callbacksRef.current.onDictate({ blob, mimeType: session.mimeType, durationSeconds });
1522
+ };
1523
+ recorder.onerror = () => {
1524
+ teardown(true);
1525
+ callbacksRef.current.onError?.("Recording stopped unexpectedly.");
1526
+ };
1527
+ recorder.start();
1528
+ setRecording(true);
1529
+ },
1530
+ (error) => {
1531
+ cancelPendingStartRef.current = null;
1532
+ if (pendingCancelled) return;
1533
+ callbacksRef.current.onError?.(dictationErrorMessage(error));
1534
+ }
1535
+ );
1536
+ }, [supported, teardown]);
1537
+ useEffect5(
1538
+ () => () => {
1539
+ cancelPendingStartRef.current?.();
1540
+ cancelPendingStartRef.current = null;
1541
+ const session = sessionRef.current;
1542
+ if (session === null) return;
1543
+ session.cancelled = true;
1544
+ sessionRef.current = null;
1545
+ try {
1546
+ if (session.recorder.state !== "inactive") session.recorder.stop();
1547
+ } finally {
1548
+ releaseStream(session.stream);
1549
+ }
1550
+ },
1551
+ []
1552
+ );
1553
+ return { supported, recording, elapsedSeconds, start, stop };
1554
+ }
1555
+
1556
+ // src/web-react/chat-composer.tsx
1416
1557
  import { Fragment as Fragment2, jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
1417
1558
  var IS_APPLE_PLATFORM = typeof navigator !== "undefined" && /Mac|iPhone|iPad|iPod/i.test(navigator.platform);
1418
1559
  function SendGlyph({ className }) {
@@ -1436,10 +1577,24 @@ function FolderGlyph({ className }) {
1436
1577
  function CloseGlyph({ className }) {
1437
1578
  return /* @__PURE__ */ jsx7("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", "aria-hidden": true, children: /* @__PURE__ */ jsx7("path", { d: "M18 6 6 18M6 6l12 12" }) });
1438
1579
  }
1580
+ function RetryGlyph({ className }) {
1581
+ return /* @__PURE__ */ jsxs5("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: [
1582
+ /* @__PURE__ */ jsx7("path", { d: "M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8" }),
1583
+ /* @__PURE__ */ jsx7("path", { d: "M3 3v5h5" })
1584
+ ] });
1585
+ }
1439
1586
  function UploadGlyph({ className }) {
1440
1587
  return /* @__PURE__ */ jsx7("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: /* @__PURE__ */ jsx7("path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4M17 8l-5-5-5 5M12 3v12" }) });
1441
1588
  }
1442
- var MAX_HEIGHT = 168;
1589
+ function MicGlyph({ className }) {
1590
+ return /* @__PURE__ */ jsxs5("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: [
1591
+ /* @__PURE__ */ jsx7("rect", { x: "9", y: "2", width: "6", height: "12", rx: "3" }),
1592
+ /* @__PURE__ */ jsx7("path", { d: "M5 10v1a7 7 0 0 0 14 0v-1M12 18v4" })
1593
+ ] });
1594
+ }
1595
+ var DEFAULT_MAX_HEIGHT = 168;
1596
+ var LINE_HEIGHT = 24;
1597
+ var TEXTAREA_PADDING_Y = 8;
1443
1598
  var DEFAULT_SEND_FAILURE = "Message not sent. Your draft is still here \u2014 try again.";
1444
1599
  function isRejectedOutcome(outcome) {
1445
1600
  return typeof outcome === "object" && outcome !== null && outcome.ok === false;
@@ -1477,9 +1632,22 @@ function ChatComposer({
1477
1632
  onAttachFolder,
1478
1633
  pendingFiles = [],
1479
1634
  onRemoveFile,
1635
+ onRetryFile,
1480
1636
  accept,
1637
+ onRejectFiles,
1481
1638
  dropTitle = "Drop files to add context",
1482
1639
  dropDescription = "They attach to your next message.",
1640
+ contextItems = [],
1641
+ canSubmitAttachmentsOnly = false,
1642
+ attachmentsNotReadyMessage,
1643
+ canSubmitWhileBusy = false,
1644
+ autoFocus,
1645
+ minRows = 2,
1646
+ maxHeight = DEFAULT_MAX_HEIGHT,
1647
+ trailing,
1648
+ slashCommands,
1649
+ onDictate,
1650
+ onDictateError,
1483
1651
  focusShortcut = true,
1484
1652
  floating = false,
1485
1653
  sendLabel = "Send",
@@ -1487,31 +1655,48 @@ function ChatComposer({
1487
1655
  className
1488
1656
  }) {
1489
1657
  const isControlled = value !== void 0;
1490
- const [internal, setInternal] = useState7(initialValue ?? "");
1658
+ const [internal, setInternal] = useState8(initialValue ?? "");
1491
1659
  const text = isControlled ? value : internal;
1492
- const textRef = useRef5(text);
1660
+ const textRef = useRef6(text);
1493
1661
  textRef.current = text;
1494
- const textareaRef = useRef5(null);
1495
- const fileInputRef = useRef5(null);
1496
- const folderInputRef = useRef5(null);
1497
- const [dragOver, setDragOver] = useState7(false);
1498
- const dragDepth = useRef5(0);
1499
- const setText = useCallback2(
1662
+ const textareaRef = useRef6(null);
1663
+ const fileInputRef = useRef6(null);
1664
+ const folderInputRef = useRef6(null);
1665
+ const [dragOver, setDragOver] = useState8(false);
1666
+ const dragDepth = useRef6(0);
1667
+ const pastedImageCount = useRef6(0);
1668
+ const setText = useCallback3(
1500
1669
  (next) => {
1501
1670
  if (!isControlled) setInternal(next);
1502
1671
  onValueChange?.(next);
1503
1672
  },
1504
1673
  [isControlled, onValueChange]
1505
1674
  );
1506
- useEffect5(() => {
1675
+ const [dictateError, setDictateError] = useState8(null);
1676
+ const handleDictated = useCallback3(
1677
+ (audio) => {
1678
+ setDictateError(null);
1679
+ onDictate?.(audio);
1680
+ },
1681
+ [onDictate]
1682
+ );
1683
+ const handleDictateError = useCallback3(
1684
+ (message) => {
1685
+ setDictateError(message);
1686
+ onDictateError?.(message);
1687
+ },
1688
+ [onDictateError]
1689
+ );
1690
+ const dictation = useDictation({ onDictate: handleDictated, onError: handleDictateError });
1691
+ useEffect6(() => {
1507
1692
  const el = textareaRef.current;
1508
1693
  if (!el) return;
1509
1694
  el.style.height = "auto";
1510
- el.style.height = `${Math.min(el.scrollHeight, MAX_HEIGHT)}px`;
1511
- }, [text]);
1512
- const prevSeedRef = useRef5(null);
1513
- const pendingCaretRef = useRef5(null);
1514
- useEffect5(() => {
1695
+ el.style.height = `${Math.min(el.scrollHeight, maxHeight)}px`;
1696
+ }, [text, maxHeight, minRows]);
1697
+ const prevSeedRef = useRef6(null);
1698
+ const pendingCaretRef = useRef6(null);
1699
+ useEffect6(() => {
1515
1700
  const prev = prevSeedRef.current;
1516
1701
  prevSeedRef.current = seed ?? null;
1517
1702
  if (seed == null || seed === prev || isControlled) return;
@@ -1525,7 +1710,7 @@ function ChatComposer({
1525
1710
  pendingCaretRef.current = seed;
1526
1711
  }
1527
1712
  }, [seed, setText, onSeedApplied, isControlled]);
1528
- useEffect5(() => {
1713
+ useEffect6(() => {
1529
1714
  if (pendingCaretRef.current == null || pendingCaretRef.current !== text)
1530
1715
  return;
1531
1716
  pendingCaretRef.current = null;
@@ -1534,8 +1719,8 @@ function ChatComposer({
1534
1719
  el.focus();
1535
1720
  el.setSelectionRange(text.length, text.length);
1536
1721
  }, [text]);
1537
- const restoreCaretRef = useRef5(null);
1538
- useEffect5(() => {
1722
+ const restoreCaretRef = useRef6(null);
1723
+ useEffect6(() => {
1539
1724
  const pending = restoreCaretRef.current;
1540
1725
  if (!pending || pending.text !== text) return;
1541
1726
  restoreCaretRef.current = null;
@@ -1546,7 +1731,7 @@ function ChatComposer({
1546
1731
  const end = Math.min(pending.end, text.length);
1547
1732
  el.setSelectionRange(start, end);
1548
1733
  }, [text]);
1549
- useEffect5(() => {
1734
+ useEffect6(() => {
1550
1735
  if (!focusShortcut || disabled) return;
1551
1736
  function onKeyDown(e) {
1552
1737
  if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === "l") {
@@ -1557,11 +1742,12 @@ function ChatComposer({
1557
1742
  document.addEventListener("keydown", onKeyDown);
1558
1743
  return () => document.removeEventListener("keydown", onKeyDown);
1559
1744
  }, [focusShortcut, disabled]);
1560
- const readyFileCount = pendingFiles.filter((f) => f.status === "ready").length;
1561
- const hasSendable = text.trim().length > 0 || readyFileCount > 0;
1562
- const canSend = hasSendable && !isStreaming && !disabled;
1563
- const [failedSend, setFailedSend] = useState7(null);
1564
- const failSend = useCallback2(
1745
+ const sendableFiles = canSubmitAttachmentsOnly ? pendingFiles : pendingFiles.filter((f) => f.status === "ready");
1746
+ const hasSendable = text.trim().length > 0 || sendableFiles.length > 0;
1747
+ const sendBlockedByStream = isStreaming && !canSubmitWhileBusy;
1748
+ const canSend = hasSendable && !sendBlockedByStream && !disabled;
1749
+ const [failedSend, setFailedSend] = useState8(null);
1750
+ const failSend = useCallback3(
1565
1751
  (error, draft, trimmed, parts, caret) => {
1566
1752
  const message = sendFailureText(error, sendFailureMessage);
1567
1753
  const restored = textRef.current === "";
@@ -1580,7 +1766,7 @@ function ChatComposer({
1580
1766
  },
1581
1767
  [onSendFailed, sendFailureMessage, setText]
1582
1768
  );
1583
- const dispatchSend = useCallback2(
1769
+ const dispatchSend = useCallback3(
1584
1770
  (draft, trimmed, parts, caret) => {
1585
1771
  let outcome;
1586
1772
  try {
@@ -1602,11 +1788,17 @@ function ChatComposer({
1602
1788
  },
1603
1789
  [onSend, onSendParts, failSend]
1604
1790
  );
1605
- const send = useCallback2(() => {
1791
+ const send = useCallback3(() => {
1606
1792
  const trimmed = text.trim();
1607
- if (isStreaming || disabled) return;
1793
+ if (sendBlockedByStream || disabled) return;
1608
1794
  const readyFiles = pendingFiles.filter((f) => f.status === "ready");
1609
- if (!trimmed && readyFiles.length === 0) return;
1795
+ const sendable = canSubmitAttachmentsOnly ? pendingFiles : readyFiles;
1796
+ if (!trimmed && sendable.length === 0) return;
1797
+ if (!trimmed && readyFiles.length === 0) {
1798
+ const message = attachmentsNotReadyMessage ?? (pendingFiles.some((f) => f.status === "error") ? "Retry or remove the failed attachment before sending." : "Wait for the attachment to finish uploading.");
1799
+ setFailedSend({ message, text: "", trimmed: "", parts: [], restored: true });
1800
+ return;
1801
+ }
1610
1802
  const parts = onSendParts ? readyFiles.filter((f) => f.part).map((f) => f.part) : [];
1611
1803
  const el = textareaRef.current;
1612
1804
  const caret = { start: el?.selectionStart ?? text.length, end: el?.selectionEnd ?? text.length };
@@ -1614,36 +1806,151 @@ function ChatComposer({
1614
1806
  setText("");
1615
1807
  textRef.current = "";
1616
1808
  dispatchSend(text, trimmed, parts, caret);
1617
- }, [text, isStreaming, disabled, onSendParts, pendingFiles, setText, dispatchSend]);
1618
- const retryFailedSend = useCallback2(() => {
1809
+ }, [
1810
+ text,
1811
+ sendBlockedByStream,
1812
+ disabled,
1813
+ canSubmitAttachmentsOnly,
1814
+ attachmentsNotReadyMessage,
1815
+ onSendParts,
1816
+ pendingFiles,
1817
+ setText,
1818
+ dispatchSend
1819
+ ]);
1820
+ const retryFailedSend = useCallback3(() => {
1619
1821
  const failure = failedSend;
1620
- if (!failure || isStreaming || disabled) return;
1822
+ if (!failure || sendBlockedByStream || disabled) return;
1621
1823
  setFailedSend(null);
1622
1824
  const caret = { start: failure.text.length, end: failure.text.length };
1623
1825
  dispatchSend(failure.text, failure.trimmed, failure.parts, caret);
1624
- }, [failedSend, isStreaming, disabled, dispatchSend]);
1826
+ }, [failedSend, sendBlockedByStream, disabled, dispatchSend]);
1827
+ const slashPanelRef = useRef6(null);
1828
+ const cardRef = useRef6(null);
1829
+ const slashListId = useId();
1830
+ const [slashActive, setSlashActive] = useState8(0);
1831
+ const [slashDismissedFor, setSlashDismissedFor] = useState8(null);
1832
+ const slashToken = slashCommands && slashCommands.length > 0 ? /^\/(\S*)$/.exec(text)?.[1] : void 0;
1833
+ const slashOpen = slashToken !== void 0 && text !== slashDismissedFor;
1834
+ const slashItems = useMemo3(
1835
+ () => (slashCommands ?? []).map((command) => ({
1836
+ id: command.name,
1837
+ group: "Commands",
1838
+ label: `/${command.name}`,
1839
+ description: command.description,
1840
+ keywords: [command.name, command.description]
1841
+ })),
1842
+ [slashCommands]
1843
+ );
1844
+ const slashFiltered = useMemo3(
1845
+ () => slashToken === void 0 ? [] : filterCommandPaletteItems(slashItems, slashToken),
1846
+ [slashItems, slashToken]
1847
+ );
1848
+ const slashActiveIndex = slashFiltered.length === 0 ? 0 : Math.min(slashActive, slashFiltered.length - 1);
1849
+ useEffect6(() => {
1850
+ setSlashActive(0);
1851
+ }, [slashToken]);
1852
+ useEffect6(() => {
1853
+ if (!slashOpen) return;
1854
+ document.getElementById(`${slashListId}-${slashActiveIndex}`)?.scrollIntoView?.({ block: "nearest" });
1855
+ }, [slashOpen, slashActiveIndex, slashListId]);
1856
+ useEffect6(() => {
1857
+ if (!slashOpen) return;
1858
+ function onMouseDown(e) {
1859
+ const target = e.target;
1860
+ if (cardRef.current?.contains(target)) return;
1861
+ if (slashPanelRef.current?.contains(target)) return;
1862
+ setSlashDismissedFor(textRef.current);
1863
+ }
1864
+ document.addEventListener("mousedown", onMouseDown);
1865
+ return () => document.removeEventListener("mousedown", onMouseDown);
1866
+ }, [slashOpen]);
1867
+ const pickSlash = useCallback3(
1868
+ (name) => {
1869
+ const command = slashCommands?.find((c) => c.name === name);
1870
+ setText("");
1871
+ setSlashDismissedFor(null);
1872
+ command?.run();
1873
+ },
1874
+ [slashCommands, setText]
1875
+ );
1625
1876
  const handleKeyDown = (e) => {
1626
1877
  if (e.nativeEvent.isComposing) return;
1878
+ if (slashOpen) {
1879
+ if (e.key === "ArrowDown") {
1880
+ e.preventDefault();
1881
+ if (slashFiltered.length > 0) setSlashActive((slashActiveIndex + 1) % slashFiltered.length);
1882
+ return;
1883
+ }
1884
+ if (e.key === "ArrowUp") {
1885
+ e.preventDefault();
1886
+ if (slashFiltered.length > 0)
1887
+ setSlashActive((slashActiveIndex - 1 + slashFiltered.length) % slashFiltered.length);
1888
+ return;
1889
+ }
1890
+ if (e.key === "Enter" && !e.shiftKey || e.key === "Tab") {
1891
+ const item = slashFiltered[slashActiveIndex];
1892
+ if (item) {
1893
+ e.preventDefault();
1894
+ pickSlash(item.id);
1895
+ return;
1896
+ }
1897
+ }
1898
+ if (e.key === "Escape") {
1899
+ e.preventDefault();
1900
+ setSlashDismissedFor(text);
1901
+ return;
1902
+ }
1903
+ }
1627
1904
  if (e.key === "Enter" && !e.shiftKey) {
1628
1905
  e.preventDefault();
1629
1906
  send();
1630
1907
  }
1631
1908
  };
1909
+ const deliverFiles = useCallback3(
1910
+ (files, original) => {
1911
+ if (!onAttach || files.length === 0) return;
1912
+ const { accepted, rejected } = filterAcceptedFiles(files, accept);
1913
+ if (rejected.length > 0) onRejectFiles?.(rejected);
1914
+ if (accepted.length === 0) return;
1915
+ const unchanged = accepted.length === original.length && accepted.every((file, i) => file === original[i]);
1916
+ if (unchanged) {
1917
+ onAttach(original);
1918
+ return;
1919
+ }
1920
+ const transfer = new DataTransfer();
1921
+ for (const file of accepted) transfer.items.add(file);
1922
+ onAttach(transfer.files);
1923
+ },
1924
+ [onAttach, onRejectFiles, accept]
1925
+ );
1632
1926
  const handleFileChange = (e) => {
1633
- if (e.target.files?.length) onAttach?.(e.target.files);
1927
+ if (e.target.files?.length) deliverFiles(Array.from(e.target.files), e.target.files);
1634
1928
  e.target.value = "";
1635
1929
  };
1930
+ const handlePaste = (e) => {
1931
+ if (!onAttach) return;
1932
+ const clipboardFiles = e.clipboardData?.files;
1933
+ if (!clipboardFiles || clipboardFiles.length === 0) return;
1934
+ e.preventDefault();
1935
+ const { files, nextIndex } = renamePastedImages(
1936
+ Array.from(clipboardFiles),
1937
+ pastedImageCount.current,
1938
+ pendingFiles.map((f) => f.name)
1939
+ );
1940
+ pastedImageCount.current = nextIndex;
1941
+ deliverFiles(files, clipboardFiles);
1942
+ };
1636
1943
  const handleFolderChange = (e) => {
1637
1944
  if (e.target.files?.length) (onAttachFolder ?? onAttach)?.(e.target.files);
1638
1945
  e.target.value = "";
1639
1946
  };
1640
- const handleDragEnter = useCallback2((e) => {
1947
+ const handleDragEnter = useCallback3((e) => {
1641
1948
  e.preventDefault();
1642
1949
  e.stopPropagation();
1643
1950
  dragDepth.current++;
1644
1951
  if (e.dataTransfer?.types.includes("Files")) setDragOver(true);
1645
1952
  }, []);
1646
- const handleDragLeave = useCallback2((e) => {
1953
+ const handleDragLeave = useCallback3((e) => {
1647
1954
  e.preventDefault();
1648
1955
  e.stopPropagation();
1649
1956
  dragDepth.current--;
@@ -1652,21 +1959,21 @@ function ChatComposer({
1652
1959
  setDragOver(false);
1653
1960
  }
1654
1961
  }, []);
1655
- const handleDragOver = useCallback2((e) => {
1962
+ const handleDragOver = useCallback3((e) => {
1656
1963
  e.preventDefault();
1657
1964
  e.stopPropagation();
1658
1965
  if (e.dataTransfer) e.dataTransfer.dropEffect = "copy";
1659
1966
  }, []);
1660
- const handleDrop = useCallback2(
1967
+ const handleDrop = useCallback3(
1661
1968
  (e) => {
1662
1969
  e.preventDefault();
1663
1970
  e.stopPropagation();
1664
1971
  dragDepth.current = 0;
1665
1972
  setDragOver(false);
1666
1973
  const files = e.dataTransfer?.files;
1667
- if (files?.length) onAttach?.(files);
1974
+ if (files?.length) deliverFiles(Array.from(files), files);
1668
1975
  },
1669
- [onAttach]
1976
+ [deliverFiles]
1670
1977
  );
1671
1978
  const folderChips = pendingFiles.filter((f) => f.kind === "folder");
1672
1979
  const fileChips = pendingFiles.filter((f) => f.kind !== "folder");
@@ -1687,6 +1994,27 @@ function ChatComposer({
1687
1994
  /* @__PURE__ */ jsx7("p", { className: "mt-0.5 text-xs text-muted-foreground", children: dropDescription })
1688
1995
  ] }) }),
1689
1996
  showAbove && /* @__PURE__ */ jsx7("div", { className: "mb-1.5 flex flex-wrap items-center gap-1.5 px-1", children: controls }),
1997
+ dictateError && /* @__PURE__ */ jsxs5(
1998
+ "div",
1999
+ {
2000
+ role: "alert",
2001
+ "data-testid": "composer-dictate-error",
2002
+ className: "mb-2 flex items-start gap-2 rounded-xl border border-destructive/40 bg-destructive/10 px-3 py-2 text-xs text-destructive",
2003
+ children: [
2004
+ /* @__PURE__ */ jsx7("span", { className: "min-w-0 flex-1", children: dictateError }),
2005
+ /* @__PURE__ */ jsx7(
2006
+ "button",
2007
+ {
2008
+ type: "button",
2009
+ "aria-label": "Dismiss dictation error",
2010
+ onClick: () => setDictateError(null),
2011
+ className: "shrink-0 font-medium underline-offset-2 hover:underline focus:outline-none focus-visible:ring-2 focus-visible:ring-destructive/50",
2012
+ children: "Dismiss"
2013
+ }
2014
+ )
2015
+ ]
2016
+ }
2017
+ ),
1690
2018
  failedSend && /* @__PURE__ */ jsxs5(
1691
2019
  "div",
1692
2020
  {
@@ -1722,7 +2050,7 @@ function ChatComposer({
1722
2050
  type: "button",
1723
2051
  "aria-label": "Retry sending the unsent message",
1724
2052
  onClick: retryFailedSend,
1725
- disabled: isStreaming || disabled,
2053
+ disabled: sendBlockedByStream || disabled,
1726
2054
  className: "mt-1.5 font-medium underline-offset-2 hover:underline disabled:cursor-not-allowed disabled:opacity-50 focus:outline-none focus-visible:ring-2 focus-visible:ring-destructive/50",
1727
2055
  children: "Retry"
1728
2056
  }
@@ -1731,36 +2059,73 @@ function ChatComposer({
1731
2059
  ]
1732
2060
  }
1733
2061
  ),
1734
- pendingFiles.length > 0 && /* @__PURE__ */ jsx7("div", { className: "mb-2 flex flex-wrap gap-1.5", children: [...folderChips, ...fileChips].map((f) => /* @__PURE__ */ jsxs5(
2062
+ contextItems.length > 0 && /* @__PURE__ */ jsx7("div", { "aria-label": "Message context", className: "mb-2 flex min-w-0 flex-wrap gap-1.5", children: contextItems.map((item) => /* @__PURE__ */ jsxs5(
1735
2063
  "span",
1736
2064
  {
1737
- className: `inline-flex items-center gap-1.5 rounded-full border px-2.5 py-1 text-xs ${f.status === "error" ? "border-destructive/40 text-destructive" : "border-border bg-secondary text-foreground"}`,
2065
+ className: "inline-flex min-w-0 max-w-full items-center gap-1.5 rounded-md border border-primary/30 bg-primary/10 px-2.5 py-1 text-xs text-primary",
1738
2066
  children: [
1739
- f.kind === "folder" ? /* @__PURE__ */ jsx7(FolderGlyph, { className: "h-3 w-3 shrink-0" }) : /* @__PURE__ */ jsx7(PaperclipGlyph, { className: "h-3 w-3 shrink-0" }),
1740
- /* @__PURE__ */ jsx7("span", { className: "max-w-[150px] truncate", children: f.name }),
1741
- f.fileCount !== void 0 && /* @__PURE__ */ jsxs5("span", { className: "text-muted-foreground", children: [
1742
- "(",
1743
- f.fileCount,
1744
- ")"
1745
- ] }),
1746
- f.status === "uploading" && /* @__PURE__ */ jsx7("span", { className: "h-3 w-3 animate-spin rounded-full border-2 border-primary border-t-transparent" }),
1747
- onRemoveFile && /* @__PURE__ */ jsx7(
2067
+ item.icon && /* @__PURE__ */ jsx7("span", { className: "shrink-0", "aria-hidden": true, children: item.icon }),
2068
+ /* @__PURE__ */ jsx7("span", { className: "min-w-0 truncate", children: item.label }),
2069
+ item.onRemove && /* @__PURE__ */ jsx7(
1748
2070
  "button",
1749
2071
  {
1750
2072
  type: "button",
1751
- "aria-label": `Remove ${f.name}`,
1752
- onClick: () => onRemoveFile(f.id),
1753
- className: "rounded p-0.5 text-muted-foreground transition hover:text-foreground focus:outline-none focus-visible:ring-2 focus-visible:ring-ring",
2073
+ "aria-label": `Remove context ${item.label}`,
2074
+ onClick: item.onRemove,
2075
+ className: "shrink-0 rounded p-0.5 text-primary/70 transition hover:text-primary focus:outline-none focus-visible:ring-2 focus-visible:ring-ring",
1754
2076
  children: /* @__PURE__ */ jsx7(CloseGlyph, { className: "h-3 w-3" })
1755
2077
  }
1756
2078
  )
1757
2079
  ]
1758
2080
  },
1759
- f.id
2081
+ item.id
1760
2082
  )) }),
2083
+ pendingFiles.length > 0 && /* @__PURE__ */ jsx7("div", { className: "mb-2 flex flex-wrap gap-1.5", children: [...folderChips, ...fileChips].map((f) => {
2084
+ const isError = f.status === "error";
2085
+ return /* @__PURE__ */ jsxs5(
2086
+ "span",
2087
+ {
2088
+ title: isError ? f.errorMessage : void 0,
2089
+ className: `inline-flex items-center gap-1.5 rounded-full border px-2.5 py-1 text-xs ${isError ? "border-destructive/40 text-destructive" : "border-border bg-secondary text-foreground"} ${f.status === "pending" ? "opacity-60" : ""}`,
2090
+ children: [
2091
+ f.kind !== "folder" && f.previewUrl ? /* @__PURE__ */ jsx7("img", { src: f.previewUrl, alt: "", className: "h-8 w-8 shrink-0 rounded object-cover" }) : f.kind === "folder" ? /* @__PURE__ */ jsx7(FolderGlyph, { className: "h-3 w-3 shrink-0" }) : /* @__PURE__ */ jsx7(PaperclipGlyph, { className: "h-3 w-3 shrink-0" }),
2092
+ /* @__PURE__ */ jsx7("span", { className: "max-w-[150px] truncate", children: f.name }),
2093
+ f.fileCount !== void 0 && /* @__PURE__ */ jsxs5("span", { className: "text-muted-foreground", children: [
2094
+ "(",
2095
+ f.fileCount,
2096
+ ")"
2097
+ ] }),
2098
+ f.status === "uploading" && /* @__PURE__ */ jsx7("span", { className: "h-3 w-3 animate-spin rounded-full border-2 border-primary border-t-transparent" }),
2099
+ isError && f.errorMessage && /* @__PURE__ */ jsx7("span", { className: "max-w-[150px] truncate text-destructive/80", children: f.errorMessage }),
2100
+ isError && onRetryFile && /* @__PURE__ */ jsx7(
2101
+ "button",
2102
+ {
2103
+ type: "button",
2104
+ "aria-label": `Retry upload ${f.name}`,
2105
+ onClick: () => onRetryFile(f.id),
2106
+ className: "rounded p-0.5 text-muted-foreground transition hover:text-foreground focus:outline-none focus-visible:ring-2 focus-visible:ring-ring",
2107
+ children: /* @__PURE__ */ jsx7(RetryGlyph, { className: "h-3 w-3" })
2108
+ }
2109
+ ),
2110
+ onRemoveFile && /* @__PURE__ */ jsx7(
2111
+ "button",
2112
+ {
2113
+ type: "button",
2114
+ "aria-label": `Remove ${f.name}`,
2115
+ onClick: () => onRemoveFile(f.id),
2116
+ className: "rounded p-0.5 text-muted-foreground transition hover:text-foreground focus:outline-none focus-visible:ring-2 focus-visible:ring-ring",
2117
+ children: /* @__PURE__ */ jsx7(CloseGlyph, { className: "h-3 w-3" })
2118
+ }
2119
+ )
2120
+ ]
2121
+ },
2122
+ f.id
2123
+ );
2124
+ }) }),
1761
2125
  /* @__PURE__ */ jsxs5(
1762
2126
  "div",
1763
2127
  {
2128
+ ref: cardRef,
1764
2129
  "data-testid": "composer-card",
1765
2130
  className: `flex flex-col gap-1.5 rounded-2xl border border-card-edge bg-card px-3 py-2.5 transition focus-within:border-primary/40 focus-within:ring-2 focus-within:ring-primary/15 ${floating ? "shadow-raised" : ""}`,
1766
2131
  children: [
@@ -1771,11 +2136,14 @@ function ChatComposer({
1771
2136
  value: text,
1772
2137
  onChange: (e) => setText(e.target.value),
1773
2138
  onKeyDown: handleKeyDown,
2139
+ onPaste: onAttach ? handlePaste : void 0,
1774
2140
  placeholder,
1775
2141
  disabled,
1776
- rows: 2,
2142
+ autoFocus,
2143
+ rows: minRows,
2144
+ style: { minHeight: minRows * LINE_HEIGHT + TEXTAREA_PADDING_Y, maxHeight },
1777
2145
  "aria-label": "Message input",
1778
- className: "max-h-[168px] min-h-[56px] w-full resize-none bg-transparent px-1.5 py-1 text-base leading-6 text-foreground outline-none placeholder:text-muted-foreground disabled:opacity-50"
2146
+ className: "w-full resize-none bg-transparent px-1.5 py-1 text-base leading-6 text-foreground outline-none placeholder:text-muted-foreground disabled:opacity-50"
1779
2147
  }
1780
2148
  ),
1781
2149
  /* @__PURE__ */ jsxs5("div", { className: "flex items-end gap-2", children: [
@@ -1827,6 +2195,42 @@ function ChatComposer({
1827
2195
  children: showInline && controls
1828
2196
  }
1829
2197
  ),
2198
+ trailing && /* @__PURE__ */ jsx7("div", { "data-testid": "composer-trailing", className: "flex shrink-0 items-center gap-1.5", children: trailing }),
2199
+ onDictate && dictation.supported ? dictation.recording ? /* @__PURE__ */ jsxs5("div", { className: "flex shrink-0 items-center gap-1.5", children: [
2200
+ /* @__PURE__ */ jsx7("span", { "aria-hidden": "true", className: "h-2 w-2 animate-pulse rounded-full bg-destructive" }),
2201
+ /* @__PURE__ */ jsx7(
2202
+ "span",
2203
+ {
2204
+ "aria-hidden": "true",
2205
+ "data-testid": "composer-dictate-elapsed",
2206
+ className: "text-xs tabular-nums text-muted-foreground",
2207
+ children: formatDictationElapsed(dictation.elapsedSeconds)
2208
+ }
2209
+ ),
2210
+ /* @__PURE__ */ jsx7("span", { role: "status", className: "sr-only", children: "Recording" }),
2211
+ /* @__PURE__ */ jsx7(
2212
+ "button",
2213
+ {
2214
+ type: "button",
2215
+ onClick: dictation.stop,
2216
+ "aria-label": "Stop dictation",
2217
+ title: "Stop dictation",
2218
+ className: "shrink-0 rounded-lg p-2 text-destructive transition hover:bg-accent focus:outline-none focus-visible:ring-2 focus-visible:ring-ring",
2219
+ children: /* @__PURE__ */ jsx7(StopGlyph, { className: "h-4 w-4" })
2220
+ }
2221
+ )
2222
+ ] }) : /* @__PURE__ */ jsx7(
2223
+ "button",
2224
+ {
2225
+ type: "button",
2226
+ onClick: dictation.start,
2227
+ disabled,
2228
+ "aria-label": "Dictate message",
2229
+ title: "Dictate message",
2230
+ className: "shrink-0 rounded-lg p-2 text-muted-foreground transition hover:bg-accent hover:text-foreground disabled:opacity-50 focus:outline-none focus-visible:ring-2 focus-visible:ring-ring",
2231
+ children: /* @__PURE__ */ jsx7(MicGlyph, { className: "h-4 w-4" })
2232
+ }
2233
+ ) : null,
1830
2234
  isStreaming ? sendVariant === "icon" ? /* @__PURE__ */ jsx7(
1831
2235
  "button",
1832
2236
  {
@@ -1878,6 +2282,38 @@ function ChatComposer({
1878
2282
  ]
1879
2283
  }
1880
2284
  ),
2285
+ /* @__PURE__ */ jsxs5(
2286
+ PopoverSurface,
2287
+ {
2288
+ open: slashOpen,
2289
+ id: slashListId,
2290
+ role: "listbox",
2291
+ triggerRef: textareaRef,
2292
+ panelRef: slashPanelRef,
2293
+ className: `w-80 overflow-y-auto rounded-xl border border-card-edge bg-popover p-1 ${OVERLAY_SHADOW}`,
2294
+ children: [
2295
+ slashFiltered.length === 0 && /* @__PURE__ */ jsx7("div", { className: "px-3 py-4 text-center text-sm text-muted-foreground", children: "No matching commands" }),
2296
+ slashFiltered.map((item, index) => /* @__PURE__ */ jsxs5(
2297
+ "button",
2298
+ {
2299
+ type: "button",
2300
+ role: "option",
2301
+ "aria-selected": index === slashActiveIndex,
2302
+ id: `${slashListId}-${index}`,
2303
+ onMouseDown: (e) => e.preventDefault(),
2304
+ onMouseMove: () => setSlashActive(index),
2305
+ onClick: () => pickSlash(item.id),
2306
+ className: `flex w-full items-center gap-2.5 rounded-md px-3 py-2.5 text-left text-sm transition ${POPOVER_OPTION_FOCUS} ${index === slashActiveIndex ? "bg-accent" : "hover:bg-accent"}`,
2307
+ children: [
2308
+ /* @__PURE__ */ jsx7("span", { className: "shrink-0 font-medium text-foreground", children: item.label }),
2309
+ /* @__PURE__ */ jsx7("span", { className: "truncate text-xs text-muted-foreground", children: item.description })
2310
+ ]
2311
+ },
2312
+ item.id
2313
+ ))
2314
+ ]
2315
+ }
2316
+ ),
1881
2317
  focusShortcut && /* @__PURE__ */ jsx7("div", { className: "mt-1.5 flex justify-end px-1", children: /* @__PURE__ */ jsxs5("span", { className: "text-xs text-muted-foreground", children: [
1882
2318
  /* @__PURE__ */ jsx7("kbd", { className: "rounded border border-border bg-background px-1 py-0.5 text-xs", children: IS_APPLE_PLATFORM ? "Cmd" : "Ctrl" }),
1883
2319
  /* @__PURE__ */ jsx7("kbd", { className: "ml-0.5 rounded border border-border bg-background px-1 py-0.5 text-xs", children: "L" }),
@@ -1889,7 +2325,7 @@ function ChatComposer({
1889
2325
  }
1890
2326
 
1891
2327
  // src/web-react/durable-plan-flow.ts
1892
- import { useCallback as useCallback3, useEffect as useEffect6, useRef as useRef6, useState as useState8 } from "react";
2328
+ import { useCallback as useCallback4, useEffect as useEffect7, useRef as useRef7, useState as useState9 } from "react";
1893
2329
  var DurablePlanClientError = class extends Error {
1894
2330
  constructor(message, status, code, currentPlan) {
1895
2331
  super(message);
@@ -1976,14 +2412,14 @@ function createDurablePlanDecisionClient(options) {
1976
2412
  };
1977
2413
  }
1978
2414
  function useDurablePlanFlow(options) {
1979
- const [plan, setPlan] = useState8(options.plan);
1980
- const [deciding, setDeciding] = useState8(null);
1981
- const [restoring, setRestoring] = useState8(false);
1982
- const [error, setError] = useState8(null);
1983
- const attachments = useRef6(/* @__PURE__ */ new Map());
1984
- const decisionInFlight = useRef6(false);
1985
- useEffect6(() => setPlan(options.plan), [options.plan]);
1986
- const apply = useCallback3(async (result) => {
2415
+ const [plan, setPlan] = useState9(options.plan);
2416
+ const [deciding, setDeciding] = useState9(null);
2417
+ const [restoring, setRestoring] = useState9(false);
2418
+ const [error, setError] = useState9(null);
2419
+ const attachments = useRef7(/* @__PURE__ */ new Map());
2420
+ const decisionInFlight = useRef7(false);
2421
+ useEffect7(() => setPlan(options.plan), [options.plan]);
2422
+ const apply = useCallback4(async (result) => {
1987
2423
  setPlan(result.plan);
1988
2424
  options.onUpdated?.(result.plan);
1989
2425
  const receipt = result.followUp;
@@ -1996,7 +2432,7 @@ function useDurablePlanFlow(options) {
1996
2432
  }
1997
2433
  await pending;
1998
2434
  }, [options.attachFollowUp, options.onUpdated]);
1999
- const decide = useCallback3(async (decision, feedback) => {
2435
+ const decide = useCallback4(async (decision, feedback) => {
2000
2436
  if (decisionInFlight.current) return null;
2001
2437
  decisionInFlight.current = true;
2002
2438
  setDeciding(decision);
@@ -2022,7 +2458,7 @@ function useDurablePlanFlow(options) {
2022
2458
  setDeciding(null);
2023
2459
  }
2024
2460
  }, [apply, options.client, options.onUpdated, plan.planId, plan.revision]);
2025
- const restore = useCallback3(async () => {
2461
+ const restore = useCallback4(async () => {
2026
2462
  setRestoring(true);
2027
2463
  setError(null);
2028
2464
  try {
@@ -2151,7 +2587,7 @@ function createDurableInteractionAnswerSubmitter(options) {
2151
2587
  }
2152
2588
 
2153
2589
  // src/web-react/use-chat-interactions.ts
2154
- import { useCallback as useCallback4, useMemo as useMemo3, useState as useState9 } from "react";
2590
+ import { useCallback as useCallback5, useMemo as useMemo4, useState as useState10 } from "react";
2155
2591
  function hasPendingContentDuplicate(list, interaction) {
2156
2592
  if (interaction.status !== "pending") return false;
2157
2593
  const signature = questionInteractionContentSignature(interaction);
@@ -2230,34 +2666,34 @@ function hydrateChatInteractions(list, persisted) {
2230
2666
  return persisted.reduce(upsertChatInteraction, list);
2231
2667
  }
2232
2668
  function useChatInteractions(options = {}) {
2233
- const [interactions, setInteractions] = useState9([]);
2234
- const upsert = useCallback4((interaction) => {
2669
+ const [interactions, setInteractions] = useState10([]);
2670
+ const upsert = useCallback5((interaction) => {
2235
2671
  setInteractions((prev) => upsertChatInteraction(prev, interaction));
2236
2672
  }, []);
2237
- const applyCancel = useCallback4((cancel) => {
2673
+ const applyCancel = useCallback5((cancel) => {
2238
2674
  setInteractions((prev) => cancelChatInteraction(prev, cancel));
2239
2675
  }, []);
2240
- const markResolved = useCallback4((id, status, answers) => {
2676
+ const markResolved = useCallback5((id, status, answers) => {
2241
2677
  setInteractions((prev) => resolveChatInteraction(prev, id, status, answers));
2242
2678
  }, []);
2243
- const restore = useCallback4((outstanding, restoreOptions) => {
2679
+ const restore = useCallback5((outstanding, restoreOptions) => {
2244
2680
  setInteractions((prev) => restoreChatInteractions(prev, outstanding, {
2245
2681
  mode: restoreOptions?.mode ?? options.mode
2246
2682
  }));
2247
2683
  }, [options.mode]);
2248
- const hydrate = useCallback4((persisted) => {
2684
+ const hydrate = useCallback5((persisted) => {
2249
2685
  setInteractions((prev) => hydrateChatInteractions(prev, persisted));
2250
2686
  }, []);
2251
- const terminalizePending = useCallback4((status) => {
2687
+ const terminalizePending = useCallback5((status) => {
2252
2688
  setInteractions((prev) => terminalizePendingChatInteractions(prev, status));
2253
2689
  }, []);
2254
- const reset = useCallback4(() => setInteractions([]), []);
2255
- const pending = useMemo3(() => interactions.filter((item) => item.status === "pending"), [interactions]);
2690
+ const reset = useCallback5(() => setInteractions([]), []);
2691
+ const pending = useMemo4(() => interactions.filter((item) => item.status === "pending"), [interactions]);
2256
2692
  return { interactions, pending, upsert, applyCancel, markResolved, restore, hydrate, terminalizePending, reset };
2257
2693
  }
2258
2694
 
2259
2695
  // src/web-react/use-file-mentions.ts
2260
- import { useCallback as useCallback5, useMemo as useMemo4, useRef as useRef7, useState as useState10 } from "react";
2696
+ import { useCallback as useCallback6, useMemo as useMemo5, useRef as useRef8, useState as useState11 } from "react";
2261
2697
  var FILE_MENTION_KIND = "file";
2262
2698
  function toMentionItem(file) {
2263
2699
  return { id: file.path, label: file.name, detail: file.path, kind: FILE_MENTION_KIND };
@@ -2315,12 +2751,12 @@ function useFileMentions(options) {
2315
2751
  emptyText = DEFAULT_MENTION_EMPTY_TEXT
2316
2752
  } = options;
2317
2753
  const fetchImpl = options.fetchImpl ?? fetch;
2318
- const [state, setState] = useState10({ kind: "idle" });
2319
- const stateRef = useRef7(state);
2754
+ const [state, setState] = useState11({ kind: "idle" });
2755
+ const stateRef = useRef8(state);
2320
2756
  stateRef.current = state;
2321
- const inFlightRef = useRef7(null);
2322
- const [mentions, setMentions] = useState10([]);
2323
- const load = useCallback5(() => {
2757
+ const inFlightRef = useRef8(null);
2758
+ const [mentions, setMentions] = useState11([]);
2759
+ const load = useCallback6(() => {
2324
2760
  if (inFlightRef.current) return inFlightRef.current;
2325
2761
  if (stateRef.current.kind === "idle") {
2326
2762
  stateRef.current = { kind: "loading" };
@@ -2352,10 +2788,10 @@ function useFileMentions(options) {
2352
2788
  inFlightRef.current = attempt;
2353
2789
  return attempt;
2354
2790
  }, [fetchImpl, indexUrl]);
2355
- const refresh = useCallback5(async () => {
2791
+ const refresh = useCallback6(async () => {
2356
2792
  await load();
2357
2793
  }, [load]);
2358
- const fetchItems = useCallback5(
2794
+ const fetchItems = useCallback6(
2359
2795
  async (query) => {
2360
2796
  let current = stateRef.current;
2361
2797
  if (current.kind === "idle" || current.kind === "loading") {
@@ -2370,11 +2806,11 @@ function useFileMentions(options) {
2370
2806
  },
2371
2807
  [load, limit, refreshAfterMs]
2372
2808
  );
2373
- const onMentionsChange = useCallback5((items) => {
2809
+ const onMentionsChange = useCallback6((items) => {
2374
2810
  setMentions(items.filter((item) => item.kind === void 0 || item.kind === FILE_MENTION_KIND).map(toFileMention));
2375
2811
  }, []);
2376
- const clearMentions = useCallback5(() => setMentions([]), []);
2377
- const mention = useMemo4(
2812
+ const clearMentions = useCallback6(() => setMentions([]), []);
2813
+ const mention = useMemo5(
2378
2814
  () => ({
2379
2815
  fetchItems,
2380
2816
  onMentionsChange,
@@ -2428,7 +2864,7 @@ function segmentMentionContent(content, parts) {
2428
2864
  }
2429
2865
 
2430
2866
  // src/web-react/mission-activity.tsx
2431
- import { useCallback as useCallback6, useEffect as useEffect7, useState as useState11 } from "react";
2867
+ import { useCallback as useCallback7, useEffect as useEffect8, useState as useState12 } from "react";
2432
2868
  import { Fragment as Fragment3, jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
2433
2869
  var LIVE_STATUSES = /* @__PURE__ */ new Set(["pending", "running"]);
2434
2870
  var OK_STATUSES = /* @__PURE__ */ new Set(["completed", "done", "succeeded"]);
@@ -2488,8 +2924,8 @@ function CopyGlyph({ className }) {
2488
2924
  ] });
2489
2925
  }
2490
2926
  function TraceIdCopy({ traceId }) {
2491
- const [copied, setCopied] = useState11(false);
2492
- const copy = useCallback6(() => {
2927
+ const [copied, setCopied] = useState12(false);
2928
+ const copy = useCallback7(() => {
2493
2929
  void navigator.clipboard?.writeText(traceId).then(
2494
2930
  () => {
2495
2931
  setCopied(true);
@@ -2581,7 +3017,7 @@ function LaneRow({ run, staggerIndex }) {
2581
3017
  ] });
2582
3018
  }
2583
3019
  function MissionActivityLane({ activity, startedAt, nowMs }) {
2584
- const [expanded, setExpanded] = useState11(false);
3020
+ const [expanded, setExpanded] = useState12(false);
2585
3021
  if (activity.length === 0) return null;
2586
3022
  return /* @__PURE__ */ jsxs6("div", { className: "mt-1 border-l border-border pl-3", children: [
2587
3023
  activity.map((run, index) => /* @__PURE__ */ jsx8(LaneRow, { run, staggerIndex: index }, run.taskId)),
@@ -2614,7 +3050,7 @@ function ActivityRow({
2614
3050
  staggerIndex
2615
3051
  }) {
2616
3052
  const arrival = useArrivalStyle(staggerIndex);
2617
- const [open, setOpen] = useState11(false);
3053
+ const [open, setOpen] = useState12(false);
2618
3054
  const tone = activityTone(record.status);
2619
3055
  const cost = formatActivityCost(record.costUsd);
2620
3056
  const duration = formatActivityDuration(record.durationMs);
@@ -2654,11 +3090,11 @@ function ActivityRow({
2654
3090
  ] });
2655
3091
  }
2656
3092
  function AgentActivityPanel({ fetchActivity, renderMissionRef, title = "Agent activity", emptyLabel = "No agent runs yet." }) {
2657
- const [rows, setRows] = useState11([]);
2658
- const [cursor, setCursor] = useState11(void 0);
2659
- const [status, setStatus] = useState11("loading");
2660
- const [error, setError] = useState11(null);
2661
- const load = useCallback6(
3093
+ const [rows, setRows] = useState12([]);
3094
+ const [cursor, setCursor] = useState12(void 0);
3095
+ const [status, setStatus] = useState12("loading");
3096
+ const [error, setError] = useState12(null);
3097
+ const load = useCallback7(
2662
3098
  async (from) => {
2663
3099
  setStatus("loading");
2664
3100
  setError(null);
@@ -2674,7 +3110,7 @@ function AgentActivityPanel({ fetchActivity, renderMissionRef, title = "Agent ac
2674
3110
  },
2675
3111
  [fetchActivity]
2676
3112
  );
2677
- useEffect7(() => {
3113
+ useEffect8(() => {
2678
3114
  void load();
2679
3115
  }, [load]);
2680
3116
  const loading = status === "loading";
@@ -2711,7 +3147,7 @@ function AgentActivityPanel({ fetchActivity, renderMissionRef, title = "Agent ac
2711
3147
  }
2712
3148
 
2713
3149
  // src/web-react/provenance.tsx
2714
- import { useCallback as useCallback7, useEffect as useEffect8, useId, useRef as useRef8, useState as useState12 } from "react";
3150
+ import { useCallback as useCallback8, useEffect as useEffect9, useId as useId2, useRef as useRef9, useState as useState13 } from "react";
2715
3151
 
2716
3152
  // src/web-react/provenance-model.ts
2717
3153
  var PROVENANCE_BASES = ["extracted", "entered", "computed", "asserted"];
@@ -3005,10 +3441,10 @@ function ProvenanceValue({
3005
3441
  missingValueLabel = DEFAULT_MISSING_VALUE_LABEL,
3006
3442
  className
3007
3443
  }) {
3008
- const [open, setOpen] = useState12(defaultOpen);
3009
- const triggerRef = useRef8(null);
3010
- const rootRef = useRef8(null);
3011
- const panelId = useId();
3444
+ const [open, setOpen] = useState13(defaultOpen);
3445
+ const triggerRef = useRef9(null);
3446
+ const rootRef = useRef9(null);
3447
+ const panelId = useId2();
3012
3448
  const standing = rollUpProvenanceStanding(record, confidencePolicy);
3013
3449
  const basisMeta = provenanceBasisMeta(record.basis);
3014
3450
  const standingMeta = provenanceStandingMeta(standing);
@@ -3017,7 +3453,7 @@ function ProvenanceValue({
3017
3453
  const sources = record.sources ?? [];
3018
3454
  const inputs = record.inputs ?? [];
3019
3455
  const hasValue = record.display.trim() !== "";
3020
- const onKeyDown = useCallback7(
3456
+ const onKeyDown = useCallback8(
3021
3457
  (event) => {
3022
3458
  if (event.key !== "Escape" || !open) return;
3023
3459
  event.stopPropagation();
@@ -3026,11 +3462,11 @@ function ProvenanceValue({
3026
3462
  },
3027
3463
  [open]
3028
3464
  );
3029
- const onToggle = useCallback7(() => {
3465
+ const onToggle = useCallback8(() => {
3030
3466
  if (!open) closeTrailsOutside(rootRef.current);
3031
3467
  setOpen(!open);
3032
3468
  }, [open]);
3033
- useEffect8(() => {
3469
+ useEffect9(() => {
3034
3470
  const root = rootRef.current;
3035
3471
  if (!open || root === null) return;
3036
3472
  const entry = { root, close: () => setOpen(false) };
@@ -3286,12 +3722,12 @@ function SeatPaywall({
3286
3722
 
3287
3723
  // src/web-react/session-history.tsx
3288
3724
  import {
3289
- useCallback as useCallback8,
3290
- useEffect as useEffect9,
3291
- useId as useId2,
3292
- useMemo as useMemo5,
3293
- useRef as useRef9,
3294
- useState as useState13
3725
+ useCallback as useCallback9,
3726
+ useEffect as useEffect10,
3727
+ useId as useId3,
3728
+ useMemo as useMemo6,
3729
+ useRef as useRef10,
3730
+ useState as useState14
3295
3731
  } from "react";
3296
3732
  import { Fragment as Fragment5, jsx as jsx11, jsxs as jsxs9 } from "react/jsx-runtime";
3297
3733
  function rethrowAsync(error) {
@@ -3300,13 +3736,13 @@ function rethrowAsync(error) {
3300
3736
  });
3301
3737
  }
3302
3738
  function useInfiniteScroll(onLoadMore, { enabled, root, rootMargin = "300px" }) {
3303
- const [sentinel, setSentinel] = useState13(null);
3304
- const sentinelRef = useCallback8((node) => setSentinel(node), []);
3305
- const onLoadMoreRef = useRef9(onLoadMore);
3306
- useEffect9(() => {
3739
+ const [sentinel, setSentinel] = useState14(null);
3740
+ const sentinelRef = useCallback9((node) => setSentinel(node), []);
3741
+ const onLoadMoreRef = useRef10(onLoadMore);
3742
+ useEffect10(() => {
3307
3743
  onLoadMoreRef.current = onLoadMore;
3308
3744
  }, [onLoadMore]);
3309
- useEffect9(() => {
3745
+ useEffect10(() => {
3310
3746
  if (!sentinel || !enabled) return;
3311
3747
  if (typeof IntersectionObserver === "undefined") return;
3312
3748
  const observer = new IntersectionObserver(
@@ -3335,24 +3771,24 @@ function useSessionHistory({
3335
3771
  initialPage,
3336
3772
  defaultSort = "newest"
3337
3773
  }) {
3338
- const [items, setItems] = useState13(initialPage.items);
3339
- const [nextCursor, setNextCursor] = useState13(initialPage.nextCursor ?? null);
3340
- const [phase, setPhase] = useState13("idle");
3341
- const [reloadKey, setReloadKey] = useState13(0);
3342
- const seqRef = useRef9(0);
3343
- const resetAbortRef = useRef9(null);
3344
- const loadMoreAbortRef = useRef9(null);
3345
- const loadingMoreRef = useRef9(false);
3346
- const lastOpRef = useRef9("first");
3347
- const nextCursorRef = useRef9(nextCursor);
3774
+ const [items, setItems] = useState14(initialPage.items);
3775
+ const [nextCursor, setNextCursor] = useState14(initialPage.nextCursor ?? null);
3776
+ const [phase, setPhase] = useState14("idle");
3777
+ const [reloadKey, setReloadKey] = useState14(0);
3778
+ const seqRef = useRef10(0);
3779
+ const resetAbortRef = useRef10(null);
3780
+ const loadMoreAbortRef = useRef10(null);
3781
+ const loadingMoreRef = useRef10(false);
3782
+ const lastOpRef = useRef10("first");
3783
+ const nextCursorRef = useRef10(nextCursor);
3348
3784
  nextCursorRef.current = nextCursor;
3349
- const viewRef = useRef9({ q, sort, fetchPage });
3785
+ const viewRef = useRef10({ q, sort, fetchPage });
3350
3786
  viewRef.current = { q, sort, fetchPage };
3351
- const seedRef = useRef9(initialPage);
3787
+ const seedRef = useRef10(initialPage);
3352
3788
  seedRef.current = initialPage;
3353
3789
  const isDefaultView = q === "" && sort === defaultSort;
3354
- const seedKey = useMemo5(() => seedSignature(initialPage), [initialPage]);
3355
- useEffect9(() => {
3790
+ const seedKey = useMemo6(() => seedSignature(initialPage), [initialPage]);
3791
+ useEffect10(() => {
3356
3792
  resetAbortRef.current?.abort();
3357
3793
  loadMoreAbortRef.current?.abort();
3358
3794
  loadingMoreRef.current = false;
@@ -3383,7 +3819,7 @@ function useSessionHistory({
3383
3819
  })();
3384
3820
  return () => controller.abort();
3385
3821
  }, [q, sort, seedKey, isDefaultView, reloadKey]);
3386
- const loadMore = useCallback8(() => {
3822
+ const loadMore = useCallback9(() => {
3387
3823
  const cursor = nextCursorRef.current;
3388
3824
  if (!cursor || loadingMoreRef.current) return;
3389
3825
  const { q: currentQ, sort: currentSort, fetchPage: currentFetch } = viewRef.current;
@@ -3408,14 +3844,14 @@ function useSessionHistory({
3408
3844
  }
3409
3845
  })();
3410
3846
  }, []);
3411
- const retry = useCallback8(() => {
3847
+ const retry = useCallback9(() => {
3412
3848
  if (lastOpRef.current === "more") loadMore();
3413
3849
  else setReloadKey((key) => key + 1);
3414
3850
  }, [loadMore]);
3415
- const reload = useCallback8(() => {
3851
+ const reload = useCallback9(() => {
3416
3852
  setReloadKey((key) => key + 1);
3417
3853
  }, []);
3418
- useEffect9(
3854
+ useEffect10(
3419
3855
  () => () => {
3420
3856
  resetAbortRef.current?.abort();
3421
3857
  loadMoreAbortRef.current?.abort();
@@ -3456,21 +3892,21 @@ function useSessionActions({
3456
3892
  labels
3457
3893
  }) {
3458
3894
  const text = { ...DEFAULT_LABELS, ...labels };
3459
- const [renameTarget, setRenameTarget] = useState13(null);
3460
- const [renameValue, setRenameValue] = useState13("");
3461
- const [deleteTarget, setDeleteTarget] = useState13(null);
3462
- const [busy, setBusy] = useState13(false);
3463
- const [error, setError] = useState13(null);
3464
- const openRename = useCallback8((session) => {
3895
+ const [renameTarget, setRenameTarget] = useState14(null);
3896
+ const [renameValue, setRenameValue] = useState14("");
3897
+ const [deleteTarget, setDeleteTarget] = useState14(null);
3898
+ const [busy, setBusy] = useState14(false);
3899
+ const [error, setError] = useState14(null);
3900
+ const openRename = useCallback9((session) => {
3465
3901
  setError(null);
3466
3902
  setRenameTarget(session);
3467
3903
  setRenameValue(session.title ?? "");
3468
3904
  }, []);
3469
- const openDelete = useCallback8((session) => {
3905
+ const openDelete = useCallback9((session) => {
3470
3906
  setError(null);
3471
3907
  setDeleteTarget(session);
3472
3908
  }, []);
3473
- const submitRename = useCallback8(async () => {
3909
+ const submitRename = useCallback9(async () => {
3474
3910
  if (!renameTarget) return;
3475
3911
  const title = renameValue.trim();
3476
3912
  if (!title || title === renameTarget.title) {
@@ -3492,7 +3928,7 @@ function useSessionActions({
3492
3928
  setBusy(false);
3493
3929
  }
3494
3930
  }, [renameTarget, renameValue, renameSession, notify, onChanged, text.renamed, text.renameFailed]);
3495
- const confirmDelete = useCallback8(async () => {
3931
+ const confirmDelete = useCallback9(async () => {
3496
3932
  if (!deleteTarget) return;
3497
3933
  const deletingCurrent = currentSessionId != null && deleteTarget.id === currentSessionId;
3498
3934
  setBusy(true);
@@ -3587,7 +4023,7 @@ function SessionDialog({
3587
4023
  busy,
3588
4024
  error
3589
4025
  }) {
3590
- useEffect9(() => {
4026
+ useEffect10(() => {
3591
4027
  const onKey = (e) => {
3592
4028
  if (e.key === "Escape" && !busy) onClose();
3593
4029
  };
@@ -3677,7 +4113,7 @@ function SessionHistoryPanel({
3677
4113
  contentWidth = "reading",
3678
4114
  className
3679
4115
  }) {
3680
- const scrollRef = useRef9(null);
4116
+ const scrollRef = useRef10(null);
3681
4117
  const sentinelRef = useInfiniteScroll(history.loadMore, {
3682
4118
  enabled: history.hasMore && !history.isLoadingMore && !history.isError,
3683
4119
  root: scrollRef,
@@ -3686,15 +4122,15 @@ function SessionHistoryPanel({
3686
4122
  const searchTerm = query.trim();
3687
4123
  const isSearching = searchTerm.length > 0;
3688
4124
  const column = contentWidth === "full" ? "w-full" : "mx-auto w-full max-w-4xl";
3689
- const [selectedIds, setSelectedIds] = useState13(/* @__PURE__ */ new Set());
3690
- const [ageDays, setAgeDays] = useState13("30");
3691
- const [bulkTarget, setBulkTarget] = useState13(null);
3692
- const [bulkBusy, setBulkBusy] = useState13(false);
3693
- const [bulkError, setBulkError] = useState13(null);
3694
- useEffect9(() => {
4125
+ const [selectedIds, setSelectedIds] = useState14(/* @__PURE__ */ new Set());
4126
+ const [ageDays, setAgeDays] = useState14("30");
4127
+ const [bulkTarget, setBulkTarget] = useState14(null);
4128
+ const [bulkBusy, setBulkBusy] = useState14(false);
4129
+ const [bulkError, setBulkError] = useState14(null);
4130
+ useEffect10(() => {
3695
4131
  setSelectedIds(/* @__PURE__ */ new Set());
3696
4132
  }, [searchTerm, sort]);
3697
- useEffect9(() => {
4133
+ useEffect10(() => {
3698
4134
  const visible = new Set(history.items.map((item) => item.id));
3699
4135
  setSelectedIds((current) => {
3700
4136
  const next = new Set([...current].filter((id) => visible.has(id)));
@@ -3705,7 +4141,7 @@ function SessionHistoryPanel({
3705
4141
  const allVisibleSelected = history.items.length > 0 && history.items.every((item) => selectedIds.has(item.id));
3706
4142
  const parsedAgeDays = Number(ageDays);
3707
4143
  const validAgeDays = Number.isInteger(parsedAgeDays) && parsedAgeDays >= 1 && parsedAgeDays <= 36500;
3708
- const openBulkAction = useCallback8((action) => {
4144
+ const openBulkAction = useCallback9((action) => {
3709
4145
  const verb = deleteLabel.toLowerCase();
3710
4146
  if (action.kind === "selected") {
3711
4147
  setBulkTarget({
@@ -3722,7 +4158,7 @@ function SessionHistoryPanel({
3722
4158
  body: "This applies to every matching session in this workspace, including sessions not currently loaded in this list."
3723
4159
  });
3724
4160
  }, [deleteLabel]);
3725
- const confirmBulkAction = useCallback8(async () => {
4161
+ const confirmBulkAction = useCallback9(async () => {
3726
4162
  if (!bulkTarget || !onBulkAction) return;
3727
4163
  setBulkBusy(true);
3728
4164
  setBulkError(null);
@@ -3970,8 +4406,8 @@ function SessionRow({
3970
4406
  selected,
3971
4407
  onSelectedChange
3972
4408
  }) {
3973
- const [menuOpen, setMenuOpen] = useState13(false);
3974
- const panelId = useId2();
4409
+ const [menuOpen, setMenuOpen] = useState14(false);
4410
+ const panelId = useId3();
3975
4411
  const { containerRef, triggerRef, panelRef, triggerProps } = usePopover(menuOpen, setMenuOpen);
3976
4412
  const extras = extraActions?.(session) ?? [];
3977
4413
  const hasMenu = Boolean(onRename) || Boolean(onDelete) || extras.length > 0;
@@ -4074,12 +4510,12 @@ function SessionRow({
4074
4510
  import {
4075
4511
  Fragment as Fragment6,
4076
4512
  isValidElement,
4077
- useCallback as useCallback9,
4078
- useEffect as useEffect10,
4079
- useId as useId3,
4080
- useMemo as useMemo6,
4081
- useRef as useRef10,
4082
- useState as useState14
4513
+ useCallback as useCallback10,
4514
+ useEffect as useEffect11,
4515
+ useId as useId4,
4516
+ useMemo as useMemo7,
4517
+ useRef as useRef11,
4518
+ useState as useState15
4083
4519
  } from "react";
4084
4520
 
4085
4521
  // src/web-react/record-grid-model.ts
@@ -4098,6 +4534,32 @@ function isRecordGridCellApplicable(column, values) {
4098
4534
  function sameRecordGridValue(a, b) {
4099
4535
  return Object.is(a ?? null, b ?? null);
4100
4536
  }
4537
+ function diffRecordGridProposal(rows, proposal) {
4538
+ const updates = proposal.updates ?? {};
4539
+ const removals = new Set(proposal.removals ?? []);
4540
+ const additions = proposal.additions ?? [];
4541
+ const liveIds = new Set(rows.map((row) => row.id));
4542
+ const diffs = [];
4543
+ for (const row of rows) {
4544
+ if (removals.has(row.id)) {
4545
+ diffs.push({ rowId: row.id, kind: "removed", cells: [], row });
4546
+ continue;
4547
+ }
4548
+ const patch = updates[row.id];
4549
+ if (patch === void 0) continue;
4550
+ const cells = [];
4551
+ for (const [columnId, after] of Object.entries(patch)) {
4552
+ const before = row.values[columnId] ?? null;
4553
+ if (!sameRecordGridValue(before, after)) cells.push({ columnId, before, after });
4554
+ }
4555
+ if (cells.length > 0) diffs.push({ rowId: row.id, kind: "changed", cells, row });
4556
+ }
4557
+ for (const row of additions) {
4558
+ if (liveIds.has(row.id)) continue;
4559
+ diffs.push({ rowId: row.id, kind: "added", cells: [], row });
4560
+ }
4561
+ return diffs;
4562
+ }
4101
4563
  var NUMERIC = /^[+-]?(?:\d+(?:\.\d+)?|\.\d+)$/;
4102
4564
  var ISO_DATE = /^\d{4}-\d{2}-\d{2}$/;
4103
4565
  function parseNumericText(raw) {
@@ -4456,6 +4918,11 @@ function RecordGrid({
4456
4918
  onCreate,
4457
4919
  onUpdate,
4458
4920
  onDelete,
4921
+ proposed,
4922
+ onAcceptRow,
4923
+ onRejectRow,
4924
+ onAcceptAll,
4925
+ onRejectAll,
4459
4926
  newRowDefaults,
4460
4927
  addLabel = "Add row",
4461
4928
  locale,
@@ -4463,40 +4930,57 @@ function RecordGrid({
4463
4930
  loadingRowCount = 3,
4464
4931
  className
4465
4932
  }) {
4466
- const fieldPrefix = useId3();
4467
- const [overlay, setOverlay] = useState14(EMPTY_RECORD_GRID_OVERLAY);
4468
- const [editing, setEditingState] = useState14(null);
4469
- const [cellErrors, setCellErrors] = useState14({});
4470
- const [rowErrors, setRowErrors] = useState14({});
4471
- const [pendingRows, setPendingRows] = useState14({});
4472
- const [focus, setFocus] = useState14(null);
4473
- const [openSource, setOpenSource] = useState14(null);
4474
- const [confirmDelete, setConfirmDelete] = useState14(null);
4475
- const [adding, setAdding] = useState14(false);
4476
- const [draft, setDraft] = useState14({ ...newRowDefaults ?? {} });
4477
- const [draftErrors, setDraftErrors] = useState14({});
4478
- const [draftError, setDraftError] = useState14(null);
4479
- const [creating, setCreating] = useState14(false);
4480
- const cellRefs = useRef10(/* @__PURE__ */ new Map());
4481
- const settling = useRef10(false);
4482
- const draftCounter = useRef10(0);
4483
- const editingRef = useRef10(null);
4484
- const setEditing = useCallback9((next) => {
4933
+ const fieldPrefix = useId4();
4934
+ const [overlay, setOverlay] = useState15(EMPTY_RECORD_GRID_OVERLAY);
4935
+ const [editing, setEditingState] = useState15(null);
4936
+ const [cellErrors, setCellErrors] = useState15({});
4937
+ const [rowErrors, setRowErrors] = useState15({});
4938
+ const [pendingRows, setPendingRows] = useState15({});
4939
+ const [focus, setFocus] = useState15(null);
4940
+ const [openSource, setOpenSource] = useState15(null);
4941
+ const [confirmDelete, setConfirmDelete] = useState15(null);
4942
+ const [adding, setAdding] = useState15(false);
4943
+ const [draft, setDraft] = useState15({ ...newRowDefaults ?? {} });
4944
+ const [draftErrors, setDraftErrors] = useState15({});
4945
+ const [draftError, setDraftError] = useState15(null);
4946
+ const [creating, setCreating] = useState15(false);
4947
+ const cellRefs = useRef11(/* @__PURE__ */ new Map());
4948
+ const settling = useRef11(false);
4949
+ const draftCounter = useRef11(0);
4950
+ const editingRef = useRef11(null);
4951
+ const setEditing = useCallback10((next) => {
4485
4952
  editingRef.current = next;
4486
4953
  setEditingState(next);
4487
4954
  }, []);
4488
4955
  const callerRows = state.status === "ready" || state.status === "empty" ? state.value : EMPTY_RECORD_GRID_ROWS;
4489
- useEffect10(() => {
4956
+ useEffect11(() => {
4490
4957
  setOverlay((current) => pruneRecordGridOverlay(callerRows, current));
4491
4958
  }, [callerRows]);
4492
- const visibleRows = useMemo6(() => projectRecordGridRows(callerRows, overlay), [callerRows, overlay]);
4493
- const activeFocus = useMemo6(() => {
4959
+ const visibleRows = useMemo7(() => projectRecordGridRows(callerRows, overlay), [callerRows, overlay]);
4960
+ const diffs = useMemo7(
4961
+ () => proposed === void 0 ? null : diffRecordGridProposal(visibleRows, proposed),
4962
+ [proposed, visibleRows]
4963
+ );
4964
+ const reviewing = diffs !== null && diffs.length > 0;
4965
+ const diffByRow = useMemo7(() => new Map((diffs ?? []).map((diff) => [diff.rowId, diff])), [diffs]);
4966
+ const diffCellByKey = useMemo7(() => {
4967
+ const map = /* @__PURE__ */ new Map();
4968
+ for (const diff of diffs ?? []) {
4969
+ for (const cell of diff.cells) map.set(cellKey(diff.rowId, cell.columnId), cell);
4970
+ }
4971
+ return map;
4972
+ }, [diffs]);
4973
+ const addedRows = useMemo7(
4974
+ () => (diffs ?? []).filter((diff) => diff.kind === "added").map((diff) => diff.row),
4975
+ [diffs]
4976
+ );
4977
+ const activeFocus = useMemo7(() => {
4494
4978
  if (focus === null) return null;
4495
4979
  if (!visibleRows.some((row) => row.id === focus.rowId)) return null;
4496
4980
  if (!columns.some((column) => column.id === focus.columnId)) return null;
4497
4981
  return focus;
4498
4982
  }, [columns, focus, visibleRows]);
4499
- const setCellError = useCallback9((key, message) => {
4983
+ const setCellError = useCallback10((key, message) => {
4500
4984
  setCellErrors((current) => {
4501
4985
  if (message === null) {
4502
4986
  if (!(key in current)) return current;
@@ -4508,7 +4992,7 @@ function RecordGrid({
4508
4992
  return { ...current, [key]: message };
4509
4993
  });
4510
4994
  }, []);
4511
- const setRowError = useCallback9((rowId, message) => {
4995
+ const setRowError = useCallback10((rowId, message) => {
4512
4996
  setRowErrors((current) => {
4513
4997
  if (message === null) {
4514
4998
  if (!(rowId in current)) return current;
@@ -4519,7 +5003,7 @@ function RecordGrid({
4519
5003
  return { ...current, [rowId]: message };
4520
5004
  });
4521
5005
  }, []);
4522
- const setRowPending = useCallback9((rowId, pending) => {
5006
+ const setRowPending = useCallback10((rowId, pending) => {
4523
5007
  setPendingRows((current) => {
4524
5008
  if (pending) return rowId in current ? current : { ...current, [rowId]: true };
4525
5009
  if (!(rowId in current)) return current;
@@ -4528,11 +5012,11 @@ function RecordGrid({
4528
5012
  return next;
4529
5013
  });
4530
5014
  }, []);
4531
- const focusCell = useCallback9((rowId, columnId) => {
5015
+ const focusCell = useCallback10((rowId, columnId) => {
4532
5016
  setFocus({ rowId, columnId });
4533
5017
  cellRefs.current.get(cellKey(rowId, columnId))?.focus();
4534
5018
  }, []);
4535
- const beginEdit = useCallback9(
5019
+ const beginEdit = useCallback10(
4536
5020
  (row, column) => {
4537
5021
  setCellError(cellKey(row.id, column.id), null);
4538
5022
  setEditing({
@@ -4543,7 +5027,7 @@ function RecordGrid({
4543
5027
  },
4544
5028
  [setCellError, setEditing]
4545
5029
  );
4546
- const applyCellWrite = useCallback9(
5030
+ const applyCellWrite = useCallback10(
4547
5031
  async (row, column, value) => {
4548
5032
  if (!onUpdate) return;
4549
5033
  const values = { ...row.values, [column.id]: value };
@@ -4571,7 +5055,7 @@ function RecordGrid({
4571
5055
  },
4572
5056
  [locale, onUpdate, setRowError, setRowPending]
4573
5057
  );
4574
- const commitEdit = useCallback9(
5058
+ const commitEdit = useCallback10(
4575
5059
  async (row, column, text) => {
4576
5060
  const open = editingRef.current;
4577
5061
  if (open === null || open.rowId !== row.id || open.columnId !== column.id) return;
@@ -4595,7 +5079,7 @@ function RecordGrid({
4595
5079
  },
4596
5080
  [applyCellWrite, setCellError, setEditing]
4597
5081
  );
4598
- const cancelEdit = useCallback9(
5082
+ const cancelEdit = useCallback10(
4599
5083
  (row, column) => {
4600
5084
  setCellError(cellKey(row.id, column.id), null);
4601
5085
  setEditing(null);
@@ -4603,7 +5087,7 @@ function RecordGrid({
4603
5087
  },
4604
5088
  [focusCell, setCellError, setEditing]
4605
5089
  );
4606
- const performDelete = useCallback9(
5090
+ const performDelete = useCallback10(
4607
5091
  async (row) => {
4608
5092
  if (!onDelete) return;
4609
5093
  setConfirmDelete(null);
@@ -4621,16 +5105,16 @@ function RecordGrid({
4621
5105
  },
4622
5106
  [columns, onDelete, setRowError]
4623
5107
  );
4624
- const resetDraft = useCallback9(() => {
5108
+ const resetDraft = useCallback10(() => {
4625
5109
  setDraft({ ...newRowDefaults ?? {} });
4626
5110
  setDraftErrors({});
4627
5111
  setDraftError(null);
4628
5112
  }, [newRowDefaults]);
4629
- const openAdd = useCallback9(() => {
5113
+ const openAdd = useCallback10(() => {
4630
5114
  resetDraft();
4631
5115
  setAdding(true);
4632
5116
  }, [resetDraft]);
4633
- const submitDraft = useCallback9(async () => {
5117
+ const submitDraft = useCallback10(async () => {
4634
5118
  if (!onCreate) return;
4635
5119
  const validated = validateRecordGridRow(columns, draft);
4636
5120
  if (!validated.succeeded) {
@@ -4660,7 +5144,7 @@ function RecordGrid({
4660
5144
  setOverlay((current) => withoutRecordGridCreated(current, draftId));
4661
5145
  setDraftError(outcome.error);
4662
5146
  }, [columns, draft, fieldPrefix, onCreate, resetDraft]);
4663
- const handleGridKeyDown = useCallback9(
5147
+ const handleGridKeyDown = useCallback10(
4664
5148
  (event) => {
4665
5149
  if (editing !== null) return;
4666
5150
  const target = event.target;
@@ -4674,7 +5158,7 @@ function RecordGrid({
4674
5158
  const row = visibleRows[rowIndex];
4675
5159
  const column = columns[columnIndex];
4676
5160
  if (!row || !column) return;
4677
- if (!onUpdate || column.editable === false || row.readOnly === true) return;
5161
+ if (reviewing || !onUpdate || column.editable === false || row.readOnly === true) return;
4678
5162
  if (column.kind === "boolean") return;
4679
5163
  if (!isRecordGridCellApplicable(column, row.values)) return;
4680
5164
  event.preventDefault();
@@ -4696,7 +5180,7 @@ function RecordGrid({
4696
5180
  if (!destinationRow || !destinationColumn) return;
4697
5181
  focusCell(destinationRow.id, destinationColumn.id);
4698
5182
  },
4699
- [beginEdit, columns, editing, focusCell, onUpdate, visibleRows]
5183
+ [beginEdit, columns, editing, focusCell, onUpdate, reviewing, visibleRows]
4700
5184
  );
4701
5185
  if (state.status === "idle" || state.status === "loading") {
4702
5186
  return /* @__PURE__ */ jsxs10("div", { className: `space-y-3 ${className ?? ""}`, children: [
@@ -4736,7 +5220,7 @@ function RecordGrid({
4736
5220
  ] })
4737
5221
  ] });
4738
5222
  }
4739
- const addForm = adding && onCreate ? /* @__PURE__ */ jsx12(
5223
+ const addForm = adding && onCreate && !reviewing ? /* @__PURE__ */ jsx12(
4740
5224
  AddRecordForm,
4741
5225
  {
4742
5226
  columns,
@@ -4754,7 +5238,7 @@ function RecordGrid({
4754
5238
  }
4755
5239
  }
4756
5240
  ) : null;
4757
- if (visibleRows.length === 0) {
5241
+ if (visibleRows.length === 0 && !reviewing) {
4758
5242
  return /* @__PURE__ */ jsxs10("div", { className: `space-y-3 ${className ?? ""}`, children: [
4759
5243
  toolbar,
4760
5244
  addForm ?? /* @__PURE__ */ jsxs10("div", { className: "rounded-xl border border-dashed border-border px-6 py-10 text-center", children: [
@@ -4784,9 +5268,55 @@ function RecordGrid({
4784
5268
  ] });
4785
5269
  }
4786
5270
  const hasFooter = columns.some((column) => column.footerValue !== void 0);
4787
- const columnSpan = columns.length + (onDelete ? 1 : 0);
5271
+ const showActionsColumn = reviewing || onDelete !== void 0;
5272
+ const columnSpan = columns.length + (showActionsColumn ? 1 : 0);
5273
+ const changedCount = (diffs ?? []).filter((diff) => diff.kind === "changed").length;
5274
+ const addedCount = addedRows.length;
5275
+ const removedCount = (diffs ?? []).filter((diff) => diff.kind === "removed").length;
5276
+ const changedCellCount = (diffs ?? []).reduce((total, diff) => total + diff.cells.length, 0);
5277
+ const reviewSummary = [
5278
+ changedCount > 0 ? `${changedCount} changed (${changedCellCount} ${changedCellCount === 1 ? "cell" : "cells"})` : null,
5279
+ addedCount > 0 ? `${addedCount} added` : null,
5280
+ removedCount > 0 ? `${removedCount} removed` : null
5281
+ ].filter((part) => part !== null).join(" \xB7 ");
5282
+ const reviewBar = reviewing ? /* @__PURE__ */ jsxs10(
5283
+ "div",
5284
+ {
5285
+ "data-record-grid-review": "",
5286
+ className: "flex flex-wrap items-center justify-between gap-3 rounded-xl border border-card-edge bg-card px-4 py-2.5",
5287
+ children: [
5288
+ /* @__PURE__ */ jsxs10("div", { className: "min-w-0", children: [
5289
+ /* @__PURE__ */ jsx12("p", { className: "text-[11px] font-semibold uppercase tracking-[0.05em] text-muted-foreground", children: "Proposed changes" }),
5290
+ /* @__PURE__ */ jsx12("p", { className: "mt-0.5 text-xs tabular-nums text-muted-foreground", children: reviewSummary })
5291
+ ] }),
5292
+ (onAcceptAll || onRejectAll) && /* @__PURE__ */ jsxs10("div", { className: "flex items-center gap-2", children: [
5293
+ onRejectAll && /* @__PURE__ */ jsx12(
5294
+ "button",
5295
+ {
5296
+ type: "button",
5297
+ "aria-label": `Reject all proposed changes to ${caption}`,
5298
+ onClick: onRejectAll,
5299
+ className: "rounded-md border border-border px-3 py-1.5 text-xs font-medium text-muted-foreground transition hover:bg-accent",
5300
+ children: "Reject all"
5301
+ }
5302
+ ),
5303
+ onAcceptAll && /* @__PURE__ */ jsx12(
5304
+ "button",
5305
+ {
5306
+ type: "button",
5307
+ "aria-label": `Accept all proposed changes to ${caption}`,
5308
+ onClick: onAcceptAll,
5309
+ className: "rounded-md bg-success/10 px-3 py-1.5 text-xs font-medium text-success transition hover:bg-success/20",
5310
+ children: "Accept all"
5311
+ }
5312
+ )
5313
+ ] })
5314
+ ]
5315
+ }
5316
+ ) : null;
4788
5317
  return /* @__PURE__ */ jsxs10("div", { className: `space-y-3 ${className ?? ""}`, children: [
4789
5318
  toolbar,
5319
+ reviewBar,
4790
5320
  /* @__PURE__ */ jsx12("div", { className: "overflow-x-auto rounded-xl border border-card-edge bg-card", children: /* @__PURE__ */ jsxs10(
4791
5321
  "table",
4792
5322
  {
@@ -4806,149 +5336,222 @@ function RecordGrid({
4806
5336
  },
4807
5337
  column.id
4808
5338
  )),
4809
- onDelete && /* @__PURE__ */ jsx12("th", { role: "columnheader", scope: "col", className: "w-px px-3 py-2 font-medium", children: /* @__PURE__ */ jsx12("span", { className: "sr-only", children: "Row actions" }) })
5339
+ showActionsColumn && /* @__PURE__ */ jsx12("th", { role: "columnheader", scope: "col", className: "w-px px-3 py-2 font-medium", children: /* @__PURE__ */ jsx12("span", { className: "sr-only", children: reviewing ? "Review" : "Row actions" }) })
4810
5340
  ] }) }),
4811
- /* @__PURE__ */ jsx12("tbody", { children: visibleRows.map((row) => {
4812
- const rowLabel = recordGridRowLabel(columns, row);
4813
- const pending = row.id in pendingRows;
4814
- const rowError = rowErrors[row.id];
4815
- return /* @__PURE__ */ jsxs10(Fragment6, { children: [
4816
- /* @__PURE__ */ jsxs10("tr", { role: "row", "aria-busy": pending, className: `border-b border-border ${pending ? "opacity-60" : ""}`, children: [
4817
- columns.map((column) => {
4818
- const key = cellKey(row.id, column.id);
4819
- const applicable = isRecordGridCellApplicable(column, row.values);
4820
- const editable = onUpdate !== void 0 && column.editable !== false && row.readOnly !== true && applicable;
4821
- const value = row.values[column.id] ?? null;
4822
- const isEditing = editing?.rowId === row.id && editing.columnId === column.id;
4823
- const cellError = cellErrors[key];
4824
- const active = activeFocus === null ? row.id === visibleRows[0]?.id && column.id === columns[0]?.id : activeFocus.rowId === row.id && activeFocus.columnId === column.id;
4825
- const source = row.sources?.[column.id];
4826
- const errorId = `${fieldPrefix}-cell-error-${row.id}-${column.id}`;
4827
- if (isEditing && editable) {
4828
- return /* @__PURE__ */ jsxs10("td", { role: "gridcell", className: `px-3 py-1.5 ${alignmentClass(column)}`, children: [
4829
- /* @__PURE__ */ jsx12(
4830
- CellEditor,
5341
+ /* @__PURE__ */ jsxs10("tbody", { children: [
5342
+ visibleRows.map((row) => {
5343
+ const rowLabel = recordGridRowLabel(columns, row);
5344
+ const pending = row.id in pendingRows;
5345
+ const rowError = rowErrors[row.id];
5346
+ const rowDiff = reviewing ? diffByRow.get(row.id) : void 0;
5347
+ const removedRow = rowDiff?.kind === "removed";
5348
+ return /* @__PURE__ */ jsxs10(Fragment6, { children: [
5349
+ /* @__PURE__ */ jsxs10(
5350
+ "tr",
5351
+ {
5352
+ role: "row",
5353
+ "aria-busy": pending,
5354
+ "data-record-grid-diff": rowDiff?.kind,
5355
+ className: `border-b border-border ${pending ? "opacity-60" : ""} ${removedRow ? "bg-destructive/[0.06]" : ""}`,
5356
+ children: [
5357
+ columns.map((column) => {
5358
+ const key = cellKey(row.id, column.id);
5359
+ const applicable = isRecordGridCellApplicable(column, row.values);
5360
+ const editable = !reviewing && onUpdate !== void 0 && column.editable !== false && row.readOnly !== true && applicable;
5361
+ const value = row.values[column.id] ?? null;
5362
+ const isEditing = editing?.rowId === row.id && editing.columnId === column.id;
5363
+ const cellError = cellErrors[key];
5364
+ const active = activeFocus === null ? row.id === visibleRows[0]?.id && column.id === columns[0]?.id : activeFocus.rowId === row.id && activeFocus.columnId === column.id;
5365
+ const source = row.sources?.[column.id];
5366
+ const errorId = `${fieldPrefix}-cell-error-${row.id}-${column.id}`;
5367
+ const cellDiff = rowDiff?.kind === "changed" ? diffCellByKey.get(key) : void 0;
5368
+ if (isEditing && editable) {
5369
+ return /* @__PURE__ */ jsxs10("td", { role: "gridcell", className: `px-3 py-1.5 ${alignmentClass(column)}`, children: [
5370
+ /* @__PURE__ */ jsx12(
5371
+ CellEditor,
5372
+ {
5373
+ column,
5374
+ rowLabel,
5375
+ text: editing.text,
5376
+ invalid: cellError !== void 0,
5377
+ describedBy: cellError === void 0 ? void 0 : errorId,
5378
+ onText: (text) => setEditing({ rowId: row.id, columnId: column.id, text }),
5379
+ onCommit: (text) => void commitEdit(row, column, text),
5380
+ onCancel: () => cancelEdit(row, column)
5381
+ }
5382
+ ),
5383
+ cellError !== void 0 && /* @__PURE__ */ jsx12("p", { id: errorId, role: "alert", className: "mt-1 text-xs leading-snug text-destructive", children: cellError })
5384
+ ] }, column.id);
5385
+ }
5386
+ if (column.kind === "boolean" && editable) {
5387
+ return /* @__PURE__ */ jsx12("td", { role: "gridcell", className: `px-3 py-2 ${alignmentClass(column)}`, children: /* @__PURE__ */ jsx12(
5388
+ "input",
5389
+ {
5390
+ type: "checkbox",
5391
+ checked: value === true,
5392
+ "aria-label": `${column.header}, ${rowLabel}`,
5393
+ "data-record-grid-row": row.id,
5394
+ "data-record-grid-column": column.id,
5395
+ tabIndex: active ? 0 : -1,
5396
+ ref: (node) => {
5397
+ cellRefs.current.set(key, node);
5398
+ },
5399
+ onFocus: () => setFocus({ rowId: row.id, columnId: column.id }),
5400
+ onChange: (event) => void applyCellWrite(row, column, event.target.checked),
5401
+ className: "h-4 w-4 rounded border-border accent-primary"
5402
+ }
5403
+ ) }, column.id);
5404
+ }
5405
+ const display = applicable ? formatRecordGridValue(column, value, locale) : "";
5406
+ return /* @__PURE__ */ jsx12(
5407
+ "td",
5408
+ {
5409
+ role: "gridcell",
5410
+ "aria-readonly": editable ? void 0 : true,
5411
+ "data-record-grid-row": row.id,
5412
+ "data-record-grid-column": column.id,
5413
+ tabIndex: active ? 0 : -1,
5414
+ ref: (node) => {
5415
+ cellRefs.current.set(key, node);
5416
+ },
5417
+ onFocus: () => setFocus({ rowId: row.id, columnId: column.id }),
5418
+ onClick: () => {
5419
+ if (editable) beginEdit(row, column);
5420
+ },
5421
+ className: `px-3 py-2 outline-none focus:ring-2 focus:ring-inset focus:ring-primary/50 ${alignmentClass(
5422
+ column
5423
+ )} ${editable ? "cursor-text" : ""}`,
5424
+ children: /* @__PURE__ */ jsxs10("span", { className: "inline-flex max-w-full items-center gap-1.5", children: [
5425
+ column.id === columns[0]?.id && removedRow && /* @__PURE__ */ jsx12("span", { className: "inline-flex shrink-0 rounded border border-destructive/60 px-1 py-px text-[11px] font-semibold uppercase tracking-[0.05em] text-destructive", children: "Remove" }),
5426
+ cellDiff ? /* @__PURE__ */ jsxs10("span", { className: "inline-flex max-w-full flex-wrap items-baseline gap-x-1.5", children: [
5427
+ /* @__PURE__ */ jsx12("span", { className: "tabular-nums text-destructive line-through decoration-destructive/60", children: formatRecordGridValue(column, cellDiff.before, locale) || "\u2014" }),
5428
+ /* @__PURE__ */ jsx12("span", { "aria-hidden": "true", className: "text-muted-foreground", children: "\u2192" }),
5429
+ /* @__PURE__ */ jsx12("span", { className: "tabular-nums font-medium text-success", children: formatRecordGridValue(column, cellDiff.after, locale) || "\u2014" })
5430
+ ] }) : /* @__PURE__ */ jsx12(
5431
+ "span",
5432
+ {
5433
+ className: removedRow ? "truncate text-destructive line-through decoration-destructive/60" : display === "" ? "text-muted-foreground" : "truncate text-foreground",
5434
+ children: display === "" ? applicable ? "\u2014" : "n/a" : display
5435
+ }
5436
+ ),
5437
+ source && /* @__PURE__ */ jsx12(
5438
+ SourceMarker,
5439
+ {
5440
+ panelId: `${fieldPrefix}-source-${row.id}-${column.id}`,
5441
+ columnHeader: column.header,
5442
+ rowLabel,
5443
+ source,
5444
+ open: openSource === key,
5445
+ onToggle: () => setOpenSource((current) => current === key ? null : key)
5446
+ }
5447
+ )
5448
+ ] })
5449
+ },
5450
+ column.id
5451
+ );
5452
+ }),
5453
+ showActionsColumn && /* @__PURE__ */ jsx12("td", { role: "gridcell", className: "px-3 py-2 text-right", children: reviewing ? rowDiff && /* @__PURE__ */ jsx12(
5454
+ ReviewActions,
4831
5455
  {
4832
- column,
5456
+ rowId: row.id,
5457
+ kind: rowDiff.kind,
4833
5458
  rowLabel,
4834
- text: editing.text,
4835
- invalid: cellError !== void 0,
4836
- describedBy: cellError === void 0 ? void 0 : errorId,
4837
- onText: (text) => setEditing({ rowId: row.id, columnId: column.id, text }),
4838
- onCommit: (text) => void commitEdit(row, column, text),
4839
- onCancel: () => cancelEdit(row, column)
5459
+ onAccept: onAcceptRow,
5460
+ onReject: onRejectRow
4840
5461
  }
4841
- ),
4842
- cellError !== void 0 && /* @__PURE__ */ jsx12("p", { id: errorId, role: "alert", className: "mt-1 text-xs leading-snug text-destructive", children: cellError })
4843
- ] }, column.id);
4844
- }
4845
- if (column.kind === "boolean" && editable) {
4846
- return /* @__PURE__ */ jsx12("td", { role: "gridcell", className: `px-3 py-2 ${alignmentClass(column)}`, children: /* @__PURE__ */ jsx12(
4847
- "input",
4848
- {
4849
- type: "checkbox",
4850
- checked: value === true,
4851
- "aria-label": `${column.header}, ${rowLabel}`,
4852
- "data-record-grid-row": row.id,
4853
- "data-record-grid-column": column.id,
4854
- tabIndex: active ? 0 : -1,
4855
- ref: (node) => {
4856
- cellRefs.current.set(key, node);
4857
- },
4858
- onFocus: () => setFocus({ rowId: row.id, columnId: column.id }),
4859
- onChange: (event) => void applyCellWrite(row, column, event.target.checked),
4860
- className: "h-4 w-4 rounded border-border accent-primary"
4861
- }
4862
- ) }, column.id);
5462
+ ) : row.readOnly === true ? null : confirmDelete === row.id ? /* @__PURE__ */ jsxs10("span", { className: "inline-flex items-center gap-1.5", children: [
5463
+ /* @__PURE__ */ jsx12(
5464
+ "button",
5465
+ {
5466
+ type: "button",
5467
+ "aria-label": `Confirm delete ${rowLabel}`,
5468
+ onClick: () => void performDelete(row),
5469
+ className: "rounded-md bg-destructive/10 px-2 py-1 text-xs font-medium text-destructive transition hover:bg-destructive/20",
5470
+ children: "Delete"
5471
+ }
5472
+ ),
5473
+ /* @__PURE__ */ jsx12(
5474
+ "button",
5475
+ {
5476
+ type: "button",
5477
+ "aria-label": `Keep ${rowLabel}`,
5478
+ onClick: () => setConfirmDelete(null),
5479
+ className: "rounded-md border border-border px-2 py-1 text-xs font-medium text-muted-foreground transition hover:bg-accent",
5480
+ children: "Cancel"
5481
+ }
5482
+ )
5483
+ ] }) : /* @__PURE__ */ jsx12(
5484
+ "button",
5485
+ {
5486
+ type: "button",
5487
+ "aria-label": `Delete ${rowLabel}`,
5488
+ onClick: () => setConfirmDelete(row.id),
5489
+ className: "rounded-md p-1.5 text-muted-foreground transition hover:bg-destructive/10 hover:text-destructive",
5490
+ children: /* @__PURE__ */ jsxs10(
5491
+ "svg",
5492
+ {
5493
+ viewBox: "0 0 24 24",
5494
+ className: "h-3.5 w-3.5",
5495
+ fill: "none",
5496
+ stroke: "currentColor",
5497
+ strokeWidth: "2",
5498
+ strokeLinecap: "round",
5499
+ strokeLinejoin: "round",
5500
+ "aria-hidden": true,
5501
+ children: [
5502
+ /* @__PURE__ */ jsx12("polyline", { points: "3 6 5 6 21 6" }),
5503
+ /* @__PURE__ */ jsx12("path", { d: "M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" })
5504
+ ]
5505
+ }
5506
+ )
5507
+ }
5508
+ ) })
5509
+ ]
4863
5510
  }
4864
- const display = applicable ? formatRecordGridValue(column, value, locale) : "";
4865
- return /* @__PURE__ */ jsx12(
4866
- "td",
4867
- {
4868
- role: "gridcell",
4869
- "aria-readonly": editable ? void 0 : true,
4870
- "data-record-grid-row": row.id,
4871
- "data-record-grid-column": column.id,
4872
- tabIndex: active ? 0 : -1,
4873
- ref: (node) => {
4874
- cellRefs.current.set(key, node);
4875
- },
4876
- onFocus: () => setFocus({ rowId: row.id, columnId: column.id }),
4877
- onClick: () => {
4878
- if (editable) beginEdit(row, column);
4879
- },
4880
- className: `px-3 py-2 outline-none focus:ring-2 focus:ring-inset focus:ring-primary/50 ${alignmentClass(
4881
- column
4882
- )} ${editable ? "cursor-text" : ""}`,
4883
- children: /* @__PURE__ */ jsxs10("span", { className: "inline-flex max-w-full items-center gap-1.5", children: [
4884
- /* @__PURE__ */ jsx12("span", { className: display === "" ? "text-muted-foreground" : "truncate text-foreground", children: display === "" ? applicable ? "\u2014" : "n/a" : display }),
4885
- source && /* @__PURE__ */ jsx12(
4886
- SourceMarker,
5511
+ ),
5512
+ rowError !== void 0 && /* @__PURE__ */ jsx12("tr", { role: "row", className: "border-b border-border", children: /* @__PURE__ */ jsx12("td", { role: "gridcell", colSpan: columnSpan, className: "px-3 pb-2", children: /* @__PURE__ */ jsx12("p", { role: "alert", className: "rounded-md bg-destructive/10 px-2.5 py-1.5 text-xs text-destructive", children: rowError }) }) })
5513
+ ] }, row.id);
5514
+ }),
5515
+ reviewing && addedRows.map((row) => {
5516
+ const rowLabel = recordGridRowLabel(columns, row);
5517
+ return /* @__PURE__ */ jsxs10(
5518
+ "tr",
5519
+ {
5520
+ role: "row",
5521
+ "data-record-grid-diff": "added",
5522
+ className: "border-b border-border bg-success/[0.06]",
5523
+ children: [
5524
+ columns.map((column, columnIndex) => {
5525
+ const applicable = isRecordGridCellApplicable(column, row.values);
5526
+ const value = row.values[column.id] ?? null;
5527
+ const display = applicable ? formatRecordGridValue(column, value, locale) : "";
5528
+ return /* @__PURE__ */ jsx12("td", { role: "gridcell", className: `px-3 py-2 ${alignmentClass(column)}`, children: /* @__PURE__ */ jsxs10("span", { className: "inline-flex max-w-full items-center gap-1.5", children: [
5529
+ columnIndex === 0 && /* @__PURE__ */ jsx12("span", { className: "inline-flex shrink-0 rounded border border-success/60 px-1 py-px text-[11px] font-semibold uppercase tracking-[0.05em] text-success", children: "New" }),
5530
+ /* @__PURE__ */ jsx12(
5531
+ "span",
4887
5532
  {
4888
- panelId: `${fieldPrefix}-source-${row.id}-${column.id}`,
4889
- columnHeader: column.header,
4890
- rowLabel,
4891
- source,
4892
- open: openSource === key,
4893
- onToggle: () => setOpenSource((current) => current === key ? null : key)
5533
+ className: `tabular-nums ${display === "" ? "text-muted-foreground" : "truncate text-foreground"}`,
5534
+ children: display === "" ? applicable ? "\u2014" : "n/a" : display
4894
5535
  }
4895
5536
  )
4896
- ] })
4897
- },
4898
- column.id
4899
- );
4900
- }),
4901
- onDelete && /* @__PURE__ */ jsx12("td", { role: "gridcell", className: "px-3 py-2 text-right", children: row.readOnly === true ? null : confirmDelete === row.id ? /* @__PURE__ */ jsxs10("span", { className: "inline-flex items-center gap-1.5", children: [
4902
- /* @__PURE__ */ jsx12(
4903
- "button",
4904
- {
4905
- type: "button",
4906
- "aria-label": `Confirm delete ${rowLabel}`,
4907
- onClick: () => void performDelete(row),
4908
- className: "rounded-md bg-destructive/10 px-2 py-1 text-xs font-medium text-destructive transition hover:bg-destructive/20",
4909
- children: "Delete"
4910
- }
4911
- ),
4912
- /* @__PURE__ */ jsx12(
4913
- "button",
4914
- {
4915
- type: "button",
4916
- "aria-label": `Keep ${rowLabel}`,
4917
- onClick: () => setConfirmDelete(null),
4918
- className: "rounded-md border border-border px-2 py-1 text-xs font-medium text-muted-foreground transition hover:bg-accent",
4919
- children: "Cancel"
4920
- }
4921
- )
4922
- ] }) : /* @__PURE__ */ jsx12(
4923
- "button",
4924
- {
4925
- type: "button",
4926
- "aria-label": `Delete ${rowLabel}`,
4927
- onClick: () => setConfirmDelete(row.id),
4928
- className: "rounded-md p-1.5 text-muted-foreground transition hover:bg-destructive/10 hover:text-destructive",
4929
- children: /* @__PURE__ */ jsxs10(
4930
- "svg",
5537
+ ] }) }, column.id);
5538
+ }),
5539
+ /* @__PURE__ */ jsx12("td", { role: "gridcell", className: "px-3 py-2 text-right", children: /* @__PURE__ */ jsx12(
5540
+ ReviewActions,
4931
5541
  {
4932
- viewBox: "0 0 24 24",
4933
- className: "h-3.5 w-3.5",
4934
- fill: "none",
4935
- stroke: "currentColor",
4936
- strokeWidth: "2",
4937
- strokeLinecap: "round",
4938
- strokeLinejoin: "round",
4939
- "aria-hidden": true,
4940
- children: [
4941
- /* @__PURE__ */ jsx12("polyline", { points: "3 6 5 6 21 6" }),
4942
- /* @__PURE__ */ jsx12("path", { d: "M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" })
4943
- ]
5542
+ rowId: row.id,
5543
+ kind: "added",
5544
+ rowLabel,
5545
+ onAccept: onAcceptRow,
5546
+ onReject: onRejectRow
4944
5547
  }
4945
- )
4946
- }
4947
- ) })
4948
- ] }),
4949
- rowError !== void 0 && /* @__PURE__ */ jsx12("tr", { role: "row", className: "border-b border-border", children: /* @__PURE__ */ jsx12("td", { role: "gridcell", colSpan: columnSpan, className: "px-3 pb-2", children: /* @__PURE__ */ jsx12("p", { role: "alert", className: "rounded-md bg-destructive/10 px-2.5 py-1.5 text-xs text-destructive", children: rowError }) }) })
4950
- ] }, row.id);
4951
- }) }),
5548
+ ) })
5549
+ ]
5550
+ },
5551
+ row.id
5552
+ );
5553
+ })
5554
+ ] }),
4952
5555
  hasFooter && /* @__PURE__ */ jsx12("tfoot", { children: /* @__PURE__ */ jsxs10("tr", { role: "row", className: "border-t-2 border-border", children: [
4953
5556
  columns.map((column) => /* @__PURE__ */ jsx12(
4954
5557
  "td",
@@ -4959,12 +5562,12 @@ function RecordGrid({
4959
5562
  },
4960
5563
  column.id
4961
5564
  )),
4962
- onDelete && /* @__PURE__ */ jsx12("td", { role: "gridcell" })
5565
+ showActionsColumn && /* @__PURE__ */ jsx12("td", { role: "gridcell" })
4963
5566
  ] }) })
4964
5567
  ]
4965
5568
  }
4966
5569
  ) }),
4967
- onCreate && (addForm ?? /* @__PURE__ */ jsxs10(
5570
+ onCreate && !reviewing && (addForm ?? /* @__PURE__ */ jsxs10(
4968
5571
  "button",
4969
5572
  {
4970
5573
  type: "button",
@@ -4994,6 +5597,31 @@ function RecordGrid({
4994
5597
  ))
4995
5598
  ] });
4996
5599
  }
5600
+ function ReviewActions({ rowId, kind, rowLabel, onAccept, onReject }) {
5601
+ const noun = kind === "changed" ? `proposed change to ${rowLabel}` : kind === "added" ? `new row ${rowLabel}` : `removal of ${rowLabel}`;
5602
+ return /* @__PURE__ */ jsxs10("span", { className: "inline-flex items-center gap-1.5", children: [
5603
+ onReject && /* @__PURE__ */ jsx12(
5604
+ "button",
5605
+ {
5606
+ type: "button",
5607
+ "aria-label": `Reject ${noun}`,
5608
+ onClick: () => onReject(rowId),
5609
+ className: "rounded-md border border-border px-2 py-1 text-xs font-medium text-muted-foreground transition hover:bg-accent",
5610
+ children: "Reject"
5611
+ }
5612
+ ),
5613
+ onAccept && /* @__PURE__ */ jsx12(
5614
+ "button",
5615
+ {
5616
+ type: "button",
5617
+ "aria-label": `Accept ${noun}`,
5618
+ onClick: () => onAccept(rowId),
5619
+ className: "rounded-md bg-success/10 px-2 py-1 text-xs font-medium text-success transition hover:bg-success/20",
5620
+ children: "Accept"
5621
+ }
5622
+ )
5623
+ ] });
5624
+ }
4997
5625
  function CellEditor({ column, rowLabel, text, invalid, describedBy, onText, onCommit, onCancel }) {
4998
5626
  const shared = {
4999
5627
  "aria-label": `${column.header}, ${rowLabel}`,
@@ -5066,7 +5694,7 @@ function CellEditor({ column, rowLabel, text, invalid, describedBy, onText, onCo
5066
5694
  }
5067
5695
  function SourceMarker({ panelId, columnHeader, rowLabel, source, open, onToggle }) {
5068
5696
  const basis = source.basis ?? "asserted";
5069
- const setOpen = useCallback9(
5697
+ const setOpen = useCallback10(
5070
5698
  (next) => {
5071
5699
  if (!next) onToggle();
5072
5700
  },
@@ -5150,7 +5778,7 @@ function AddRecordForm({
5150
5778
  onSubmit,
5151
5779
  onCancel
5152
5780
  }) {
5153
- const groups = useMemo6(() => groupColumns(columns), [columns]);
5781
+ const groups = useMemo7(() => groupColumns(columns), [columns]);
5154
5782
  return /* @__PURE__ */ jsxs10(
5155
5783
  "form",
5156
5784
  {
@@ -5311,6 +5939,196 @@ function DraftField({ column, id, value, invalid, describedBy, onValue }) {
5311
5939
  );
5312
5940
  }
5313
5941
 
5942
+ // src/web-react/command-palette.tsx
5943
+ import {
5944
+ useCallback as useCallback11,
5945
+ useEffect as useEffect12,
5946
+ useId as useId5,
5947
+ useMemo as useMemo8,
5948
+ useRef as useRef12,
5949
+ useState as useState16
5950
+ } from "react";
5951
+ import { createPortal } from "react-dom";
5952
+ import { Fragment as Fragment7, jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
5953
+ function SearchGlyph({ className }) {
5954
+ return /* @__PURE__ */ jsxs11("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: [
5955
+ /* @__PURE__ */ jsx13("circle", { cx: "11", cy: "11", r: "8" }),
5956
+ /* @__PURE__ */ jsx13("path", { d: "m21 21-4.3-4.3" })
5957
+ ] });
5958
+ }
5959
+ function CommandPalette({
5960
+ items,
5961
+ onSelect,
5962
+ open: controlledOpen,
5963
+ onOpenChange,
5964
+ hotkey = true,
5965
+ loading = false,
5966
+ initialQuery,
5967
+ placeholder = "Search sessions and commands\u2026",
5968
+ emptyMessage,
5969
+ label = "Command palette"
5970
+ }) {
5971
+ const [internalOpen, setInternalOpen] = useState16(false);
5972
+ const open = controlledOpen ?? internalOpen;
5973
+ const setOpen = useCallback11(
5974
+ (next) => {
5975
+ if (controlledOpen === void 0) setInternalOpen(next);
5976
+ onOpenChange?.(next);
5977
+ },
5978
+ [controlledOpen, onOpenChange]
5979
+ );
5980
+ const [query, setQuery] = useState16(initialQuery ?? "");
5981
+ const [active, setActive] = useState16(0);
5982
+ const inputRef = useRef12(null);
5983
+ const surfaceId = useId5();
5984
+ const listId = `${surfaceId}-list`;
5985
+ const flat = useMemo8(() => filterCommandPaletteItems(items, query), [items, query]);
5986
+ const sections = useMemo8(() => groupCommandPaletteItems(flat), [flat]);
5987
+ const activeIndex = flat.length === 0 ? 0 : Math.min(active, flat.length - 1);
5988
+ const activeId = flat.length > 0 ? `${listId}-${activeIndex}` : void 0;
5989
+ useEffect12(() => {
5990
+ if (!hotkey) return;
5991
+ function onKeyDown(e) {
5992
+ if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === "k") {
5993
+ e.preventDefault();
5994
+ setOpen(!open);
5995
+ }
5996
+ }
5997
+ document.addEventListener("keydown", onKeyDown);
5998
+ return () => document.removeEventListener("keydown", onKeyDown);
5999
+ }, [hotkey, open, setOpen]);
6000
+ const restoreFocusRef = useRef12(null);
6001
+ useEffect12(() => {
6002
+ if (open) {
6003
+ restoreFocusRef.current = document.activeElement;
6004
+ inputRef.current?.focus();
6005
+ return;
6006
+ }
6007
+ setQuery(initialQuery ?? "");
6008
+ setActive(0);
6009
+ const restore = restoreFocusRef.current;
6010
+ restoreFocusRef.current = null;
6011
+ if (restore instanceof HTMLElement) restore.focus();
6012
+ }, [open]);
6013
+ useEffect12(() => {
6014
+ if (!open || !activeId) return;
6015
+ document.getElementById(activeId)?.scrollIntoView?.({ block: "nearest" });
6016
+ }, [open, activeId]);
6017
+ const choose = useCallback11(
6018
+ (item) => {
6019
+ onSelect(item);
6020
+ setOpen(false);
6021
+ },
6022
+ [onSelect, setOpen]
6023
+ );
6024
+ const handleKeyDown = (e) => {
6025
+ if (e.key === "ArrowDown") {
6026
+ e.preventDefault();
6027
+ if (flat.length > 0) setActive((activeIndex + 1) % flat.length);
6028
+ } else if (e.key === "ArrowUp") {
6029
+ e.preventDefault();
6030
+ if (flat.length > 0) setActive((activeIndex - 1 + flat.length) % flat.length);
6031
+ } else if (e.key === "Enter") {
6032
+ e.preventDefault();
6033
+ const item = flat[activeIndex];
6034
+ if (item) choose(item);
6035
+ } else if (e.key === "Escape") {
6036
+ e.preventDefault();
6037
+ setOpen(false);
6038
+ }
6039
+ };
6040
+ if (!open || typeof document === "undefined") return null;
6041
+ let rowIndex = -1;
6042
+ return createPortal(
6043
+ /* @__PURE__ */ jsxs11(Fragment7, { children: [
6044
+ /* @__PURE__ */ jsx13(
6045
+ "div",
6046
+ {
6047
+ "aria-hidden": true,
6048
+ "data-testid": "command-palette-backdrop",
6049
+ onMouseDown: () => setOpen(false),
6050
+ className: "fixed inset-0 z-[999] bg-background/80"
6051
+ }
6052
+ ),
6053
+ /* @__PURE__ */ jsx13("div", { className: "pointer-events-none fixed inset-x-0 top-[15%] z-[1000] flex justify-center px-4", children: /* @__PURE__ */ jsxs11(
6054
+ "div",
6055
+ {
6056
+ role: "dialog",
6057
+ "aria-modal": "true",
6058
+ "aria-label": label,
6059
+ ...{ [POPOVER_SURFACE_ATTR]: surfaceId },
6060
+ className: `agent-pop-in pointer-events-auto flex max-h-[70vh] w-[560px] max-w-full flex-col overflow-hidden rounded-xl border border-card-edge bg-popover ${OVERLAY_SHADOW}`,
6061
+ children: [
6062
+ /* @__PURE__ */ jsxs11("div", { className: "flex shrink-0 items-center gap-2 border-b border-border px-3 py-2.5", children: [
6063
+ /* @__PURE__ */ jsx13(SearchGlyph, { className: "h-4 w-4 shrink-0 text-muted-foreground" }),
6064
+ /* @__PURE__ */ jsx13(
6065
+ "input",
6066
+ {
6067
+ ref: inputRef,
6068
+ type: "text",
6069
+ role: "combobox",
6070
+ "aria-expanded": true,
6071
+ "aria-controls": listId,
6072
+ "aria-activedescendant": activeId,
6073
+ "aria-label": label,
6074
+ value: query,
6075
+ onChange: (e) => {
6076
+ setQuery(e.target.value);
6077
+ setActive(0);
6078
+ },
6079
+ onKeyDown: handleKeyDown,
6080
+ placeholder,
6081
+ className: "flex-1 bg-transparent text-sm text-foreground placeholder:text-muted-foreground"
6082
+ }
6083
+ )
6084
+ ] }),
6085
+ /* @__PURE__ */ jsxs11("div", { role: "listbox", id: listId, className: "min-h-0 flex-1 overflow-y-auto p-1 pb-2", children: [
6086
+ loading && /* @__PURE__ */ jsx13("div", { className: "px-3 py-4 text-center text-sm text-muted-foreground", children: "Loading\u2026" }),
6087
+ !loading && flat.length === 0 && /* @__PURE__ */ jsx13("div", { className: "px-3 py-4 text-center text-sm text-muted-foreground", children: emptyMessage ?? (query.trim() ? `No results for \u201C${query.trim()}\u201D` : "Nothing here yet") }),
6088
+ !loading && sections.map((section) => /* @__PURE__ */ jsxs11("div", { children: [
6089
+ /* @__PURE__ */ jsx13("div", { className: "px-3 pb-1 pt-3 text-xs font-semibold uppercase tracking-wide text-muted-foreground", children: section.group }),
6090
+ section.items.map((item) => {
6091
+ rowIndex += 1;
6092
+ const index = rowIndex;
6093
+ return /* @__PURE__ */ jsxs11(
6094
+ "div",
6095
+ {
6096
+ id: `${listId}-${index}`,
6097
+ role: "option",
6098
+ "aria-selected": index === activeIndex,
6099
+ onMouseMove: () => setActive(index),
6100
+ onClick: () => choose(item),
6101
+ className: `flex w-full cursor-pointer items-center gap-2.5 rounded-md px-3 py-2.5 text-left text-sm ${index === activeIndex ? "bg-accent" : ""}`,
6102
+ children: [
6103
+ /* @__PURE__ */ jsx13("span", { className: "truncate text-foreground", children: item.label }),
6104
+ item.description && /* @__PURE__ */ jsx13("span", { className: "truncate text-xs text-muted-foreground", children: item.description }),
6105
+ item.hint && /* @__PURE__ */ jsx13("span", { className: "ml-auto shrink-0 text-xs tabular-nums text-muted-foreground", children: item.hint })
6106
+ ]
6107
+ },
6108
+ item.id
6109
+ );
6110
+ })
6111
+ ] }, section.group))
6112
+ ] }),
6113
+ /* @__PURE__ */ jsxs11("div", { className: "flex shrink-0 items-center justify-between border-t border-border px-3 py-2 text-xs text-muted-foreground", children: [
6114
+ /* @__PURE__ */ jsx13("span", { className: "tabular-nums", children: query.trim() ? `${flat.length} of ${items.length}` : `${items.length} items` }),
6115
+ /* @__PURE__ */ jsxs11("span", { className: "flex items-center gap-1.5", children: [
6116
+ /* @__PURE__ */ jsx13("kbd", { className: "rounded border border-border bg-background px-1 py-0.5", children: "\u2191\u2193" }),
6117
+ /* @__PURE__ */ jsx13("span", { children: "navigate" }),
6118
+ /* @__PURE__ */ jsx13("kbd", { className: "ml-1.5 rounded border border-border bg-background px-1 py-0.5", children: "\u21B5" }),
6119
+ /* @__PURE__ */ jsx13("span", { children: "select" }),
6120
+ /* @__PURE__ */ jsx13("kbd", { className: "ml-1.5 rounded border border-border bg-background px-1 py-0.5", children: "esc" }),
6121
+ /* @__PURE__ */ jsx13("span", { children: "close" })
6122
+ ] })
6123
+ ] })
6124
+ ]
6125
+ }
6126
+ ) })
6127
+ ] }),
6128
+ document.body
6129
+ );
6130
+ }
6131
+
5314
6132
  // src/web-react/class-names.ts
5315
6133
  function joinClasses(...parts) {
5316
6134
  const kept = [];
@@ -5323,7 +6141,7 @@ function joinClasses(...parts) {
5323
6141
  }
5324
6142
 
5325
6143
  // src/web-react/sparkline.tsx
5326
- import { jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
6144
+ import { jsx as jsx14, jsxs as jsxs12 } from "react/jsx-runtime";
5327
6145
  var DEFAULT_SPARKLINE_WIDTH = 96;
5328
6146
  var DEFAULT_SPARKLINE_HEIGHT = 24;
5329
6147
  var DEFAULT_INSET = 2.5;
@@ -5429,21 +6247,21 @@ function Sparkline({
5429
6247
  const geometry = sparklineGeometry(values, { width, height });
5430
6248
  const accessibleName = sparklineLabel(values, { label, format });
5431
6249
  if (geometry.points.length === 0) {
5432
- return /* @__PURE__ */ jsxs11(
6250
+ return /* @__PURE__ */ jsxs12(
5433
6251
  "span",
5434
6252
  {
5435
6253
  "data-sparkline": geometry.gaps > 0 ? "unavailable" : "empty",
5436
6254
  className: joinClasses("text-[11px] text-muted-foreground", className),
5437
6255
  children: [
5438
- /* @__PURE__ */ jsx13("span", { className: "sr-only", children: accessibleName }),
5439
- /* @__PURE__ */ jsx13("span", { "aria-hidden": "true", children: geometry.gaps > 0 ? unavailableLabel : emptyLabel })
6256
+ /* @__PURE__ */ jsx14("span", { className: "sr-only", children: accessibleName }),
6257
+ /* @__PURE__ */ jsx14("span", { "aria-hidden": "true", children: geometry.gaps > 0 ? unavailableLabel : emptyLabel })
5440
6258
  ]
5441
6259
  }
5442
6260
  );
5443
6261
  }
5444
6262
  const drawsLine = geometry.segments.some((segment) => segment.length > 1);
5445
6263
  const end = geometry.points[geometry.points.length - 1];
5446
- return /* @__PURE__ */ jsxs11(
6264
+ return /* @__PURE__ */ jsxs12(
5447
6265
  "svg",
5448
6266
  {
5449
6267
  role: "img",
@@ -5460,7 +6278,7 @@ function Sparkline({
5460
6278
  geometry.segments.map((segment, index) => {
5461
6279
  const key = `segment-${index}`;
5462
6280
  if (segment.length > 1) {
5463
- return /* @__PURE__ */ jsx13(
6281
+ return /* @__PURE__ */ jsx14(
5464
6282
  "polyline",
5465
6283
  {
5466
6284
  points: sparklinePointsAttribute(segment),
@@ -5476,9 +6294,9 @@ function Sparkline({
5476
6294
  }
5477
6295
  const only = segment[0];
5478
6296
  if (only.x === end.x && only.y === end.y) return null;
5479
- return /* @__PURE__ */ jsx13("circle", { cx: only.x, cy: only.y, r: DOT_RADIUS, fill: "currentColor" }, key);
6297
+ return /* @__PURE__ */ jsx14("circle", { cx: only.x, cy: only.y, r: DOT_RADIUS, fill: "currentColor" }, key);
5480
6298
  }),
5481
- /* @__PURE__ */ jsx13("circle", { cx: end.x, cy: end.y, r: DOT_RADIUS, fill: "currentColor" })
6299
+ /* @__PURE__ */ jsx14("circle", { cx: end.x, cy: end.y, r: DOT_RADIUS, fill: "currentColor" })
5482
6300
  ]
5483
6301
  }
5484
6302
  );
@@ -5487,12 +6305,12 @@ function Sparkline({
5487
6305
  // src/web-react/insight-card.tsx
5488
6306
  import {
5489
6307
  isValidElement as isValidElement2,
5490
- useCallback as useCallback10,
5491
- useEffect as useEffect11,
5492
- useRef as useRef11,
5493
- useState as useState15
6308
+ useCallback as useCallback12,
6309
+ useEffect as useEffect13,
6310
+ useRef as useRef13,
6311
+ useState as useState17
5494
6312
  } from "react";
5495
- import { Fragment as Fragment7, jsx as jsx14, jsxs as jsxs12 } from "react/jsx-runtime";
6313
+ import { Fragment as Fragment8, jsx as jsx15, jsxs as jsxs13 } from "react/jsx-runtime";
5496
6314
  function insightDelta(value, previous) {
5497
6315
  if (typeof value !== "number" || !Number.isFinite(value)) return null;
5498
6316
  if (typeof previous !== "number" || !Number.isFinite(previous)) return null;
@@ -5525,6 +6343,7 @@ var DIRECTION_GLYPH = { up: "\u2191", down: "\u2193", flat: "\u2192" };
5525
6343
  var INSIGHT_UNAVAILABLE_GLYPH = "\u2014";
5526
6344
  var INSIGHT_UNAVAILABLE_LABEL = "Not available";
5527
6345
  function InsightCard({
6346
+ eyebrow,
5528
6347
  title,
5529
6348
  value,
5530
6349
  unit,
@@ -5544,7 +6363,7 @@ function InsightCard({
5544
6363
  const tone = delta ? insightDeltaTone(delta.direction, polarity) : "neutral";
5545
6364
  const unavailable = typeof value === "number" && !Number.isFinite(value);
5546
6365
  const shown = typeof value === "number" ? format(value) : value;
5547
- return /* @__PURE__ */ jsxs12(
6366
+ return /* @__PURE__ */ jsxs13(
5548
6367
  "article",
5549
6368
  {
5550
6369
  "data-insight-card": "",
@@ -5552,39 +6371,40 @@ function InsightCard({
5552
6371
  className: joinClasses("agent-arrive flex h-full flex-col rounded-xl border border-card-edge bg-card p-4", className),
5553
6372
  style,
5554
6373
  children: [
5555
- /* @__PURE__ */ jsxs12("div", { className: "flex items-baseline justify-between gap-2", children: [
5556
- /* @__PURE__ */ jsx14("h3", { className: "text-[13px] font-medium text-muted-foreground", children: title }),
6374
+ eyebrow ? /* @__PURE__ */ jsx15("p", { "data-insight-eyebrow": "", className: "mb-0.5 text-[11px] font-semibold uppercase tracking-[0.05em] text-muted-foreground", children: eyebrow }) : null,
6375
+ /* @__PURE__ */ jsxs13("div", { className: "flex items-baseline justify-between gap-2", children: [
6376
+ /* @__PURE__ */ jsx15("h3", { className: "text-[13px] font-medium text-muted-foreground", children: title }),
5557
6377
  live ? (
5558
6378
  // No `data-motion` opt-out: the word is the signal and the sweep is
5559
6379
  // emphasis, so the reduced-motion floor reaches this like everything
5560
6380
  // else and leaves a static, legible label.
5561
- /* @__PURE__ */ jsx14("span", { className: "agent-shimmer shrink-0 text-[11px] font-medium", "data-insight-live": "", children: liveLabel })
6381
+ /* @__PURE__ */ jsx15("span", { className: "agent-shimmer shrink-0 text-[11px] font-medium", "data-insight-live": "", children: liveLabel })
5562
6382
  ) : null
5563
6383
  ] }),
5564
- /* @__PURE__ */ jsx14("p", { className: "mt-1 flex items-baseline gap-1", children: unavailable ? /* @__PURE__ */ jsxs12("span", { "data-insight-value": "unavailable", className: "text-xl font-semibold text-muted-foreground", children: [
5565
- /* @__PURE__ */ jsx14("span", { "aria-hidden": "true", children: INSIGHT_UNAVAILABLE_GLYPH }),
5566
- /* @__PURE__ */ jsx14("span", { className: "sr-only", children: INSIGHT_UNAVAILABLE_LABEL })
5567
- ] }) : /* @__PURE__ */ jsxs12(Fragment7, { children: [
5568
- /* @__PURE__ */ jsx14("span", { className: "text-xl font-semibold tabular-nums text-foreground", children: shown }),
5569
- unit ? /* @__PURE__ */ jsx14("span", { className: "text-[11px] text-muted-foreground", children: unit }) : null
6384
+ /* @__PURE__ */ jsx15("p", { className: "mt-1 flex items-baseline gap-1", children: unavailable ? /* @__PURE__ */ jsxs13("span", { "data-insight-value": "unavailable", className: "text-xl font-semibold text-muted-foreground", children: [
6385
+ /* @__PURE__ */ jsx15("span", { "aria-hidden": "true", children: INSIGHT_UNAVAILABLE_GLYPH }),
6386
+ /* @__PURE__ */ jsx15("span", { className: "sr-only", children: INSIGHT_UNAVAILABLE_LABEL })
6387
+ ] }) : /* @__PURE__ */ jsxs13(Fragment8, { children: [
6388
+ /* @__PURE__ */ jsx15("span", { className: "text-xl font-semibold tabular-nums text-foreground", children: shown }),
6389
+ unit ? /* @__PURE__ */ jsx15("span", { className: "text-[11px] text-muted-foreground", children: unit }) : null
5570
6390
  ] }) }),
5571
- delta ? /* @__PURE__ */ jsxs12("p", { "data-insight-delta": delta.direction, className: `mt-0.5 text-[11px] font-medium ${TONE_CLASS[tone]}`, children: [
5572
- /* @__PURE__ */ jsxs12("span", { "aria-hidden": "true", children: [
6391
+ delta ? /* @__PURE__ */ jsxs13("p", { "data-insight-delta": delta.direction, className: `mt-0.5 text-[11px] font-medium ${TONE_CLASS[tone]}`, children: [
6392
+ /* @__PURE__ */ jsxs13("span", { "aria-hidden": "true", children: [
5573
6393
  DIRECTION_GLYPH[delta.direction],
5574
6394
  " "
5575
6395
  ] }),
5576
6396
  formatInsightDelta(delta, format)
5577
6397
  ] }) : null,
5578
- description ? /* @__PURE__ */ jsx14("p", { className: "mt-1 text-[11px] text-muted-foreground", children: description }) : null,
5579
- series ? /* @__PURE__ */ jsx14("div", { className: "mt-2 text-muted-foreground", children: /* @__PURE__ */ jsx14(Sparkline, { values: series, label: seriesLabel ?? title, format }) }) : null,
5580
- action ? /* @__PURE__ */ jsx14("div", { className: "mt-3", children: renderInsightAction(action) }) : null
6398
+ description ? /* @__PURE__ */ jsx15("p", { className: "mt-1 text-[11px] text-muted-foreground", children: description }) : null,
6399
+ series ? /* @__PURE__ */ jsx15("div", { className: "mt-2 text-muted-foreground", children: /* @__PURE__ */ jsx15(Sparkline, { values: series, label: seriesLabel ?? title, format }) }) : null,
6400
+ action ? /* @__PURE__ */ jsx15("div", { className: "mt-3", children: renderInsightAction(action) }) : null
5581
6401
  ]
5582
6402
  }
5583
6403
  );
5584
6404
  }
5585
6405
  function renderInsightAction(action) {
5586
6406
  if (isValidElement2(action)) return action;
5587
- return /* @__PURE__ */ jsx14(
6407
+ return /* @__PURE__ */ jsx15(
5588
6408
  "button",
5589
6409
  {
5590
6410
  type: "button",
@@ -5653,15 +6473,15 @@ function InsightDeck({
5653
6473
  className,
5654
6474
  onPageChange
5655
6475
  }) {
5656
- const [page, setPage] = useState15(0);
5657
- const [held, setHeld] = useState15(null);
6476
+ const [page, setPage] = useState17(0);
6477
+ const [held, setHeld] = useState17(null);
5658
6478
  const answered = state.status === "error" || state.status === "empty";
5659
6479
  const carried = state.status === "ready" ? state.value : answered ? null : held;
5660
6480
  if (carried !== held) setHeld(carried);
5661
6481
  const shown = carried !== null && state.status !== "ready" ? { status: "ready", value: carried, retry: state.retry } : state;
5662
6482
  const refreshing = shown !== state;
5663
- const reported = useRef11(0);
5664
- const settlePage = useCallback10(
6483
+ const reported = useRef13(0);
6484
+ const settlePage = useCallback12(
5665
6485
  (next) => {
5666
6486
  if (reported.current === next) return;
5667
6487
  reported.current = next;
@@ -5669,7 +6489,7 @@ function InsightDeck({
5669
6489
  },
5670
6490
  [onPageChange]
5671
6491
  );
5672
- return /* @__PURE__ */ jsx14(
6492
+ return /* @__PURE__ */ jsx15(
5673
6493
  AsyncView,
5674
6494
  {
5675
6495
  state: shown,
@@ -5677,7 +6497,7 @@ function InsightDeck({
5677
6497
  loadingLabel,
5678
6498
  retryLabel,
5679
6499
  className,
5680
- children: (insights) => /* @__PURE__ */ jsx14(
6500
+ children: (insights) => /* @__PURE__ */ jsx15(
5681
6501
  InsightPages,
5682
6502
  {
5683
6503
  insights,
@@ -5744,18 +6564,18 @@ function InsightPages({
5744
6564
  const pageCount = insightPageCount(insights.length, size);
5745
6565
  const current = Math.min(Math.max(page, 0), pageCount - 1);
5746
6566
  const visible = insightPageSlice(insights, current, size);
5747
- const sectionRef = useRef11(null);
5748
- const listRef = useRef11(null);
5749
- const recoverFocus = useRef11(false);
5750
- useEffect11(() => {
6567
+ const sectionRef = useRef13(null);
6568
+ const listRef = useRef13(null);
6569
+ const recoverFocus = useRef13(false);
6570
+ useEffect13(() => {
5751
6571
  onPageSettled(current);
5752
6572
  }, [current, onPageSettled]);
5753
- useEffect11(() => {
6573
+ useEffect13(() => {
5754
6574
  if (!recoverFocus.current) return;
5755
6575
  recoverFocus.current = false;
5756
6576
  sectionRef.current?.focus();
5757
6577
  }, [current]);
5758
- const goTo = useCallback10(
6578
+ const goTo = useCallback12(
5759
6579
  (next) => {
5760
6580
  const clamped = Math.min(Math.max(next, 0), pageCount - 1);
5761
6581
  if (clamped === current) return false;
@@ -5790,7 +6610,7 @@ function InsightPages({
5790
6610
  }
5791
6611
  if (moved) event.preventDefault();
5792
6612
  };
5793
- return /* @__PURE__ */ jsxs12(
6613
+ return /* @__PURE__ */ jsxs13(
5794
6614
  "section",
5795
6615
  {
5796
6616
  ref: sectionRef,
@@ -5802,7 +6622,7 @@ function InsightPages({
5802
6622
  tabIndex: pageCount > 1 ? 0 : void 0,
5803
6623
  "aria-keyshortcuts": pageCount > 1 ? "ArrowLeft ArrowRight PageUp PageDown Home End" : void 0,
5804
6624
  children: [
5805
- /* @__PURE__ */ jsx14("ul", { ref: listRef, className: "grid gap-3 sm:grid-cols-2 lg:grid-cols-3", children: visible.map(({ id, style, ...card }, index) => (
6625
+ /* @__PURE__ */ jsx15("ul", { ref: listRef, className: "grid gap-3 sm:grid-cols-2 lg:grid-cols-3", children: visible.map(({ id, style, ...card }, index) => (
5806
6626
  // The page index is in the key on purpose: a page turn is an arrival,
5807
6627
  // and reusing the node would swap the text under a card that never
5808
6628
  // moved. Remounting replays `.agent-arrive` with the new stagger.
@@ -5813,10 +6633,10 @@ function InsightPages({
5813
6633
  // does not arrive a second time. The key does BOTH jobs — but only
5814
6634
  // because the deck now keeps this subtree mounted across a reload
5815
6635
  // (see `InsightDeck`); a key is never compared across a teardown.
5816
- /* @__PURE__ */ jsx14("li", { children: /* @__PURE__ */ jsx14(InsightCard, { ...card, style: staggerStyle(index, style) }) }, `${current}:${id}`)
6636
+ /* @__PURE__ */ jsx15("li", { children: /* @__PURE__ */ jsx15(InsightCard, { ...card, style: staggerStyle(index, style) }) }, `${current}:${id}`)
5817
6637
  )) }),
5818
- /* @__PURE__ */ jsxs12("div", { className: pageCount > 1 ? "flex items-center justify-between gap-2" : void 0, children: [
5819
- /* @__PURE__ */ jsxs12(
6638
+ /* @__PURE__ */ jsxs13("div", { className: pageCount > 1 ? "flex items-center justify-between gap-2" : void 0, children: [
6639
+ /* @__PURE__ */ jsxs13(
5820
6640
  "p",
5821
6641
  {
5822
6642
  role: "status",
@@ -5830,8 +6650,8 @@ function InsightPages({
5830
6650
  ]
5831
6651
  }
5832
6652
  ),
5833
- pageCount > 1 ? /* @__PURE__ */ jsxs12("div", { className: "flex items-center gap-1", children: [
5834
- /* @__PURE__ */ jsx14(PagerButton, { label: "Previous insights", glyph: "\u2039", atEnd: current === 0, onClick: () => goTo(current - 1) }),
6653
+ pageCount > 1 ? /* @__PURE__ */ jsxs13("div", { className: "flex items-center gap-1", children: [
6654
+ /* @__PURE__ */ jsx15(PagerButton, { label: "Previous insights", glyph: "\u2039", atEnd: current === 0, onClick: () => goTo(current - 1) }),
5835
6655
  pageCount <= MAX_PAGE_DOTS ? Array.from({ length: pageCount }, (_, index) => (
5836
6656
  // WCAG 2.2 SC 2.5.8 wants a 24x24 CSS px target. The dot stays
5837
6657
  // 8px because a 24px dot is a different control; the BUTTON
@@ -5839,7 +6659,7 @@ function InsightPages({
5839
6659
  // and the span is the graphic. The Spacing exception cannot
5840
6660
  // rescue the bare dot — at a 12px pitch the 24px circle around
5841
6661
  // each centre overlaps its neighbour's.
5842
- /* @__PURE__ */ jsx14(
6662
+ /* @__PURE__ */ jsx15(
5843
6663
  "button",
5844
6664
  {
5845
6665
  type: "button",
@@ -5847,7 +6667,7 @@ function InsightPages({
5847
6667
  "aria-current": index === current ? "page" : void 0,
5848
6668
  onClick: () => goTo(index),
5849
6669
  className: "group flex h-6 w-6 shrink-0 items-center justify-center rounded-full",
5850
- children: /* @__PURE__ */ jsx14(
6670
+ children: /* @__PURE__ */ jsx15(
5851
6671
  "span",
5852
6672
  {
5853
6673
  "aria-hidden": "true",
@@ -5861,7 +6681,7 @@ function InsightPages({
5861
6681
  index
5862
6682
  )
5863
6683
  )) : null,
5864
- /* @__PURE__ */ jsx14(
6684
+ /* @__PURE__ */ jsx15(
5865
6685
  PagerButton,
5866
6686
  {
5867
6687
  label: "Next insights",
@@ -5882,7 +6702,7 @@ function PagerButton({
5882
6702
  atEnd,
5883
6703
  onClick
5884
6704
  }) {
5885
- return /* @__PURE__ */ jsx14(
6705
+ return /* @__PURE__ */ jsx15(
5886
6706
  "button",
5887
6707
  {
5888
6708
  type: "button",
@@ -5892,13 +6712,13 @@ function PagerButton({
5892
6712
  if (!atEnd) onClick();
5893
6713
  },
5894
6714
  className: `flex h-6 w-6 items-center justify-center rounded-md border border-border text-xs text-muted-foreground transition ${atEnd ? "opacity-40" : "hover:bg-accent hover:text-foreground"}`,
5895
- children: /* @__PURE__ */ jsx14("span", { "aria-hidden": "true", children: glyph })
6715
+ children: /* @__PURE__ */ jsx15("span", { "aria-hidden": "true", children: glyph })
5896
6716
  }
5897
6717
  );
5898
6718
  }
5899
6719
 
5900
6720
  // src/web-react/index.tsx
5901
- import { Fragment as Fragment8, jsx as jsx15, jsxs as jsxs13 } from "react/jsx-runtime";
6721
+ import { Fragment as Fragment9, jsx as jsx16, jsxs as jsxs14 } from "react/jsx-runtime";
5902
6722
  function formatModelCost(msg, models) {
5903
6723
  if (msg.promptTokens == null && msg.completionTokens == null) return null;
5904
6724
  const pricing = models.find((m) => m.id === msg.modelUsed)?.pricing;
@@ -5917,41 +6737,41 @@ function formatTokensPerSecond(msg) {
5917
6737
  return `${Math.round(msg.completionTokens / (msg.durationMs / 1e3))} tok/s`;
5918
6738
  }
5919
6739
  function RunDrillIn({ run, onClose }) {
5920
- return /* @__PURE__ */ jsxs13("div", { className: `fixed inset-y-0 right-0 z-50 flex w-[480px] max-w-full flex-col border-l border-card-edge bg-popover ${OVERLAY_SHADOW}`, children: [
5921
- /* @__PURE__ */ jsxs13("div", { className: "flex items-center gap-2 border-b border-border px-4 py-3", children: [
5922
- /* @__PURE__ */ jsx15(
6740
+ return /* @__PURE__ */ jsxs14("div", { className: `fixed inset-y-0 right-0 z-50 flex w-[480px] max-w-full flex-col border-l border-card-edge bg-popover ${OVERLAY_SHADOW}`, children: [
6741
+ /* @__PURE__ */ jsxs14("div", { className: "flex items-center gap-2 border-b border-border px-4 py-3", children: [
6742
+ /* @__PURE__ */ jsx16(
5923
6743
  "span",
5924
6744
  {
5925
6745
  className: `h-2 w-2 shrink-0 rounded-full ${run.status === "running" ? "bg-warning" : run.status === "error" ? "bg-destructive" : "bg-success"}`
5926
6746
  }
5927
6747
  ),
5928
- /* @__PURE__ */ jsxs13("div", { className: "min-w-0 flex-1", children: [
5929
- /* @__PURE__ */ jsx15("p", { className: "truncate text-[15px] font-semibold", children: run.title }),
5930
- /* @__PURE__ */ jsx15("p", { className: "truncate font-mono text-xs text-muted-foreground", children: run.toolName })
6748
+ /* @__PURE__ */ jsxs14("div", { className: "min-w-0 flex-1", children: [
6749
+ /* @__PURE__ */ jsx16("p", { className: "truncate text-[15px] font-semibold", children: run.title }),
6750
+ /* @__PURE__ */ jsx16("p", { className: "truncate font-mono text-xs text-muted-foreground", children: run.toolName })
5931
6751
  ] }),
5932
- /* @__PURE__ */ jsx15(
6752
+ /* @__PURE__ */ jsx16(
5933
6753
  "button",
5934
6754
  {
5935
6755
  type: "button",
5936
6756
  onClick: onClose,
5937
6757
  "aria-label": "Close",
5938
6758
  className: "rounded-md p-1.5 text-muted-foreground transition hover:bg-accent hover:text-foreground",
5939
- children: /* @__PURE__ */ jsx15("svg", { className: "h-4 w-4", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", "aria-hidden": true, children: /* @__PURE__ */ jsx15("path", { d: "M18 6 6 18M6 6l12 12" }) })
6759
+ children: /* @__PURE__ */ jsx16("svg", { className: "h-4 w-4", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", "aria-hidden": true, children: /* @__PURE__ */ jsx16("path", { d: "M18 6 6 18M6 6l12 12" }) })
5940
6760
  }
5941
6761
  )
5942
6762
  ] }),
5943
- /* @__PURE__ */ jsxs13("div", { className: "flex-1 space-y-3 overflow-y-auto p-4", children: [
5944
- run.steps.length === 0 && /* @__PURE__ */ jsx15("p", { className: "text-sm text-muted-foreground", children: "No steps recorded yet." }),
5945
- run.steps.map((step, i) => /* @__PURE__ */ jsxs13("div", { className: "rounded-lg border border-card-edge bg-card", children: [
5946
- /* @__PURE__ */ jsxs13("div", { className: "flex items-baseline gap-2 border-b border-border px-3 py-1.5", children: [
5947
- /* @__PURE__ */ jsx15("span", { className: `font-mono text-xs ${step.status === "error" ? "text-destructive" : "text-muted-foreground"}`, children: step.status === "error" ? "\u2717" : "$" }),
5948
- /* @__PURE__ */ jsx15("code", { className: "min-w-0 flex-1 truncate font-mono text-xs", children: step.label }),
5949
- /* @__PURE__ */ jsx15("span", { className: "shrink-0 text-xs tabular-nums text-muted-foreground", children: new Date(step.at).toLocaleTimeString([], { hour: "numeric", minute: "2-digit" }) })
6763
+ /* @__PURE__ */ jsxs14("div", { className: "flex-1 space-y-3 overflow-y-auto p-4", children: [
6764
+ run.steps.length === 0 && /* @__PURE__ */ jsx16("p", { className: "text-sm text-muted-foreground", children: "No steps recorded yet." }),
6765
+ run.steps.map((step, i) => /* @__PURE__ */ jsxs14("div", { className: "rounded-lg border border-card-edge bg-card", children: [
6766
+ /* @__PURE__ */ jsxs14("div", { className: "flex items-baseline gap-2 border-b border-border px-3 py-1.5", children: [
6767
+ /* @__PURE__ */ jsx16("span", { className: `font-mono text-xs ${step.status === "error" ? "text-destructive" : "text-muted-foreground"}`, children: step.status === "error" ? "\u2717" : "$" }),
6768
+ /* @__PURE__ */ jsx16("code", { className: "min-w-0 flex-1 truncate font-mono text-xs", children: step.label }),
6769
+ /* @__PURE__ */ jsx16("span", { className: "shrink-0 text-xs tabular-nums text-muted-foreground", children: new Date(step.at).toLocaleTimeString([], { hour: "numeric", minute: "2-digit" }) })
5950
6770
  ] }),
5951
- step.detail && /* @__PURE__ */ jsx15("pre", { className: "max-h-48 overflow-auto whitespace-pre-wrap px-3 py-2 font-mono text-xs leading-relaxed text-muted-foreground", children: step.detail })
6771
+ step.detail && /* @__PURE__ */ jsx16("pre", { className: "max-h-48 overflow-auto whitespace-pre-wrap px-3 py-2 font-mono text-xs leading-relaxed text-muted-foreground", children: step.detail })
5952
6772
  ] }, i))
5953
6773
  ] }),
5954
- /* @__PURE__ */ jsx15("p", { className: "border-t border-border px-4 py-2 text-xs text-muted-foreground", children: "Read-only transcript \u2014 reply in the main chat." })
6774
+ /* @__PURE__ */ jsx16("p", { className: "border-t border-border px-4 py-2 text-xs text-muted-foreground", children: "Read-only transcript \u2014 reply in the main chat." })
5955
6775
  ] });
5956
6776
  }
5957
6777
  function pendingApprovalOf(call) {
@@ -5967,23 +6787,23 @@ function ChatEmptyState({
5967
6787
  }) {
5968
6788
  const doorCount = Math.min(doors?.length ?? 0, 3);
5969
6789
  const doorsGridClass = doorCount === 1 ? "mx-auto max-w-sm sm:grid-cols-1" : doorCount === 2 ? "sm:grid-cols-2" : "sm:grid-cols-3";
5970
- return /* @__PURE__ */ jsxs13("div", { className: "mx-auto flex w-full max-w-2xl flex-col items-center px-6 py-12 text-center sm:py-20", children: [
5971
- /* @__PURE__ */ jsx15("span", { className: "mb-5 inline-flex h-14 w-14 items-center justify-center rounded-2xl bg-primary/10 ring-1 ring-primary/15", children: /* @__PURE__ */ jsx15(BrandMark, { size: 32, className: "shrink-0" }) }),
5972
- /* @__PURE__ */ jsx15("p", { className: "text-xs font-semibold uppercase tracking-[0.05em] text-muted-foreground", children: productName }),
5973
- /* @__PURE__ */ jsx15("h2", { className: "mt-1.5 text-balance text-2xl font-semibold leading-tight text-foreground", children: headline }),
5974
- subline && /* @__PURE__ */ jsx15("p", { className: "mt-3 max-w-md text-[15px] leading-relaxed text-muted-foreground", children: subline }),
5975
- doors && doors.length > 0 && /* @__PURE__ */ jsx15("div", { className: `mt-7 grid w-full gap-2.5 ${doorsGridClass}`, children: doors.slice(0, 3).map((door, i) => /* @__PURE__ */ jsxs13(
6790
+ return /* @__PURE__ */ jsxs14("div", { className: "mx-auto flex w-full max-w-2xl flex-col items-center px-6 py-12 text-center sm:py-20", children: [
6791
+ /* @__PURE__ */ jsx16("span", { className: "mb-5 inline-flex h-14 w-14 items-center justify-center rounded-2xl bg-primary/10 ring-1 ring-primary/15", children: /* @__PURE__ */ jsx16(BrandMark, { size: 32, className: "shrink-0" }) }),
6792
+ /* @__PURE__ */ jsx16("p", { className: "text-xs font-semibold uppercase tracking-[0.05em] text-muted-foreground", children: productName }),
6793
+ /* @__PURE__ */ jsx16("h2", { className: "mt-1.5 text-balance text-2xl font-semibold leading-tight text-foreground", children: headline }),
6794
+ subline && /* @__PURE__ */ jsx16("p", { className: "mt-3 max-w-md text-[15px] leading-relaxed text-muted-foreground", children: subline }),
6795
+ doors && doors.length > 0 && /* @__PURE__ */ jsx16("div", { className: `mt-7 grid w-full gap-2.5 ${doorsGridClass}`, children: doors.slice(0, 3).map((door, i) => /* @__PURE__ */ jsxs14(
5976
6796
  "button",
5977
6797
  {
5978
6798
  type: "button",
5979
6799
  onClick: door.onSelect,
5980
6800
  className: "group flex min-h-[44px] flex-col items-start rounded-xl border border-border bg-card px-4 py-3 text-left transition hover:border-primary/40 hover:bg-accent",
5981
6801
  children: [
5982
- /* @__PURE__ */ jsxs13("span", { className: "flex items-center gap-2 text-sm font-semibold text-foreground", children: [
6802
+ /* @__PURE__ */ jsxs14("span", { className: "flex items-center gap-2 text-sm font-semibold text-foreground", children: [
5983
6803
  door.icon,
5984
6804
  door.label
5985
6805
  ] }),
5986
- door.description && /* @__PURE__ */ jsx15("span", { className: "mt-0.5 text-[12px] leading-snug text-muted-foreground", children: door.description })
6806
+ door.description && /* @__PURE__ */ jsx16("span", { className: "mt-0.5 text-[12px] leading-snug text-muted-foreground", children: door.description })
5987
6807
  ]
5988
6808
  },
5989
6809
  i
@@ -5992,26 +6812,26 @@ function ChatEmptyState({
5992
6812
  }
5993
6813
  function ToolGlyph({ name, className }) {
5994
6814
  if (name.startsWith("sandbox_")) {
5995
- return /* @__PURE__ */ jsxs13("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: [
5996
- /* @__PURE__ */ jsx15("polyline", { points: "4 17 10 11 4 5" }),
5997
- /* @__PURE__ */ jsx15("line", { x1: "12", y1: "19", x2: "20", y2: "19" })
6815
+ return /* @__PURE__ */ jsxs14("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: [
6816
+ /* @__PURE__ */ jsx16("polyline", { points: "4 17 10 11 4 5" }),
6817
+ /* @__PURE__ */ jsx16("line", { x1: "12", y1: "19", x2: "20", y2: "19" })
5998
6818
  ] });
5999
6819
  }
6000
6820
  if (name === "submit_proposal") {
6001
- return /* @__PURE__ */ jsxs13("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: [
6002
- /* @__PURE__ */ jsx15("path", { d: "M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" }),
6003
- /* @__PURE__ */ jsx15("path", { d: "M14 2v6h6M9 15l2 2 4-4" })
6821
+ return /* @__PURE__ */ jsxs14("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: [
6822
+ /* @__PURE__ */ jsx16("path", { d: "M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" }),
6823
+ /* @__PURE__ */ jsx16("path", { d: "M14 2v6h6M9 15l2 2 4-4" })
6004
6824
  ] });
6005
6825
  }
6006
6826
  if (name === "schedule_followup") {
6007
- return /* @__PURE__ */ jsxs13("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", "aria-hidden": true, children: [
6008
- /* @__PURE__ */ jsx15("circle", { cx: "12", cy: "12", r: "9" }),
6009
- /* @__PURE__ */ jsx15("path", { d: "M12 7v5l3 3" })
6827
+ return /* @__PURE__ */ jsxs14("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", "aria-hidden": true, children: [
6828
+ /* @__PURE__ */ jsx16("circle", { cx: "12", cy: "12", r: "9" }),
6829
+ /* @__PURE__ */ jsx16("path", { d: "M12 7v5l3 3" })
6010
6830
  ] });
6011
6831
  }
6012
- return /* @__PURE__ */ jsxs13("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: [
6013
- /* @__PURE__ */ jsx15("path", { d: "M12 3v3m0 12v3M3 12h3m12 0h3" }),
6014
- /* @__PURE__ */ jsx15("circle", { cx: "12", cy: "12", r: "4" })
6832
+ return /* @__PURE__ */ jsxs14("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: [
6833
+ /* @__PURE__ */ jsx16("path", { d: "M12 3v3m0 12v3M3 12h3m12 0h3" }),
6834
+ /* @__PURE__ */ jsx16("circle", { cx: "12", cy: "12", r: "4" })
6015
6835
  ] });
6016
6836
  }
6017
6837
  function toolOutcomeOf(call) {
@@ -6097,40 +6917,40 @@ function truncate(v, max = 240) {
6097
6917
  function KvRows({ data }) {
6098
6918
  const entries = Object.entries(data).filter(([, v]) => v !== void 0 && v !== null && v !== "");
6099
6919
  if (!entries.length) return null;
6100
- return /* @__PURE__ */ jsx15("dl", { className: "grid grid-cols-[auto_1fr] gap-x-3 gap-y-1", children: entries.map(([k, v]) => /* @__PURE__ */ jsxs13("div", { className: "contents", children: [
6101
- /* @__PURE__ */ jsx15("dt", { className: "font-mono text-xs text-muted-foreground", children: k }),
6102
- /* @__PURE__ */ jsx15("dd", { className: "min-w-0 whitespace-pre-wrap break-words font-mono text-xs text-muted-foreground", children: truncate(v) })
6920
+ return /* @__PURE__ */ jsx16("dl", { className: "grid grid-cols-[auto_1fr] gap-x-3 gap-y-1", children: entries.map(([k, v]) => /* @__PURE__ */ jsxs14("div", { className: "contents", children: [
6921
+ /* @__PURE__ */ jsx16("dt", { className: "font-mono text-xs text-muted-foreground", children: k }),
6922
+ /* @__PURE__ */ jsx16("dd", { className: "min-w-0 whitespace-pre-wrap break-words font-mono text-xs text-muted-foreground", children: truncate(v) })
6103
6923
  ] }, k)) });
6104
6924
  }
6105
6925
  function ShellDetail({ call }) {
6106
6926
  const outcome = toolOutcomeOf(call);
6107
6927
  const r = outcome?.result ?? {};
6108
- return /* @__PURE__ */ jsxs13("div", { className: "overflow-hidden rounded-md bg-zinc-900 font-mono text-xs leading-relaxed", children: [
6109
- /* @__PURE__ */ jsxs13("div", { className: "flex items-center gap-2 px-3 pt-2 text-zinc-400", children: [
6110
- /* @__PURE__ */ jsx15("span", { className: "select-none text-zinc-500", children: "$" }),
6111
- /* @__PURE__ */ jsx15("span", { className: "min-w-0 flex-1 truncate text-zinc-200", children: String(call.args?.command ?? "") }),
6112
- r.exitCode != null && /* @__PURE__ */ jsxs13("span", { className: r.exitCode === 0 ? "text-success" : "text-destructive", children: [
6928
+ return /* @__PURE__ */ jsxs14("div", { className: "overflow-hidden rounded-md bg-zinc-900 font-mono text-xs leading-relaxed", children: [
6929
+ /* @__PURE__ */ jsxs14("div", { className: "flex items-center gap-2 px-3 pt-2 text-zinc-400", children: [
6930
+ /* @__PURE__ */ jsx16("span", { className: "select-none text-zinc-500", children: "$" }),
6931
+ /* @__PURE__ */ jsx16("span", { className: "min-w-0 flex-1 truncate text-zinc-200", children: String(call.args?.command ?? "") }),
6932
+ r.exitCode != null && /* @__PURE__ */ jsxs14("span", { className: r.exitCode === 0 ? "text-success" : "text-destructive", children: [
6113
6933
  "exit ",
6114
6934
  r.exitCode
6115
6935
  ] })
6116
6936
  ] }),
6117
- /* @__PURE__ */ jsx15("pre", { className: "max-h-56 overflow-auto whitespace-pre-wrap px-3 pb-2.5 pt-1.5 text-zinc-300", children: outcome?.ok === false ? outcome.message ?? "failed" : [r.stdout, r.stderr].filter(Boolean).join("\n") || "(no output)" })
6937
+ /* @__PURE__ */ jsx16("pre", { className: "max-h-56 overflow-auto whitespace-pre-wrap px-3 pb-2.5 pt-1.5 text-zinc-300", children: outcome?.ok === false ? outcome.message ?? "failed" : [r.stdout, r.stderr].filter(Boolean).join("\n") || "(no output)" })
6118
6938
  ] });
6119
6939
  }
6120
6940
  function DefaultToolDetail({ call }) {
6121
6941
  const result = call.result;
6122
6942
  const envelope = typeof result === "object" && result !== null ? result : null;
6123
- return /* @__PURE__ */ jsxs13("div", { className: "space-y-2", children: [
6124
- call.args && Object.keys(call.args).length > 0 && /* @__PURE__ */ jsxs13("div", { children: [
6125
- /* @__PURE__ */ jsx15("p", { className: "mb-1 text-xs font-semibold uppercase tracking-wide text-muted-foreground", children: "Called with" }),
6126
- /* @__PURE__ */ jsx15(KvRows, { data: call.args })
6943
+ return /* @__PURE__ */ jsxs14("div", { className: "space-y-2", children: [
6944
+ call.args && Object.keys(call.args).length > 0 && /* @__PURE__ */ jsxs14("div", { children: [
6945
+ /* @__PURE__ */ jsx16("p", { className: "mb-1 text-xs font-semibold uppercase tracking-wide text-muted-foreground", children: "Called with" }),
6946
+ /* @__PURE__ */ jsx16(KvRows, { data: call.args })
6127
6947
  ] }),
6128
- envelope ? /* @__PURE__ */ jsxs13("div", { children: [
6129
- /* @__PURE__ */ jsx15("p", { className: "mb-1 text-xs font-semibold uppercase tracking-wide text-muted-foreground", children: envelope.ok === false ? "Failed" : "Result" }),
6130
- envelope.ok === false ? /* @__PURE__ */ jsx15("p", { className: "text-xs text-destructive", children: envelope.message ?? "Tool failed" }) : envelope.result && typeof envelope.result === "object" ? /* @__PURE__ */ jsx15(KvRows, { data: envelope.result }) : envelope.result != null ? /* @__PURE__ */ jsx15("p", { className: "font-mono text-xs text-muted-foreground", children: truncate(envelope.result) }) : null
6131
- ] }) : result != null ? /* @__PURE__ */ jsxs13("div", { children: [
6132
- /* @__PURE__ */ jsx15("p", { className: "mb-1 text-xs font-semibold uppercase tracking-wide text-muted-foreground", children: "Result" }),
6133
- /* @__PURE__ */ jsx15("p", { className: "font-mono text-xs text-muted-foreground", children: truncate(result) })
6948
+ envelope ? /* @__PURE__ */ jsxs14("div", { children: [
6949
+ /* @__PURE__ */ jsx16("p", { className: "mb-1 text-xs font-semibold uppercase tracking-wide text-muted-foreground", children: envelope.ok === false ? "Failed" : "Result" }),
6950
+ envelope.ok === false ? /* @__PURE__ */ jsx16("p", { className: "text-xs text-destructive", children: envelope.message ?? "Tool failed" }) : envelope.result && typeof envelope.result === "object" ? /* @__PURE__ */ jsx16(KvRows, { data: envelope.result }) : envelope.result != null ? /* @__PURE__ */ jsx16("p", { className: "font-mono text-xs text-muted-foreground", children: truncate(envelope.result) }) : null
6951
+ ] }) : result != null ? /* @__PURE__ */ jsxs14("div", { children: [
6952
+ /* @__PURE__ */ jsx16("p", { className: "mb-1 text-xs font-semibold uppercase tracking-wide text-muted-foreground", children: "Result" }),
6953
+ /* @__PURE__ */ jsx16("p", { className: "font-mono text-xs text-muted-foreground", children: truncate(result) })
6134
6954
  ] }) : null
6135
6955
  ] });
6136
6956
  }
@@ -6141,24 +6961,24 @@ function ProposalCard({
6141
6961
  approval,
6142
6962
  renderers
6143
6963
  }) {
6144
- const [expanded, setExpanded] = useState16(false);
6964
+ const [expanded, setExpanded] = useState18(false);
6145
6965
  const { summary, meta, typeSlug } = proposalPreview(call);
6146
6966
  const custom = renderers?.[call.name]?.(call, message);
6147
6967
  const { pending: deciding, run: decide } = usePending();
6148
- return /* @__PURE__ */ jsxs13("div", { className: "w-full max-w-full rounded-xl border border-warning/50 bg-warning/[0.06] text-sm shadow-sm ring-1 ring-warning/10", children: [
6149
- /* @__PURE__ */ jsxs13("div", { className: "flex items-start gap-2.5 px-4 pt-3.5", children: [
6150
- /* @__PURE__ */ jsx15("span", { className: "mt-0.5 inline-flex h-6 w-6 shrink-0 items-center justify-center rounded-full bg-warning/15 text-warning", children: /* @__PURE__ */ jsx15(ToolGlyph, { name: call.name, className: "h-3.5 w-3.5" }) }),
6151
- /* @__PURE__ */ jsxs13("div", { className: "min-w-0 flex-1", children: [
6152
- /* @__PURE__ */ jsx15("p", { className: "text-xs font-semibold uppercase tracking-[0.05em] text-warning-strong", children: approval ? "Needs your approval" : "Awaiting approval" }),
6153
- /* @__PURE__ */ jsx15("p", { className: "mt-0.5 text-[15px] font-semibold leading-snug text-foreground", children: friendlyToolTitle(call) }),
6154
- summary && /* @__PURE__ */ jsx15("p", { className: "mt-1 text-xs leading-relaxed text-muted-foreground", children: summary }),
6155
- typeSlug && /* @__PURE__ */ jsx15("p", { className: "mt-1 font-mono text-xs text-muted-foreground", children: typeSlug }),
6156
- meta.length > 0 && /* @__PURE__ */ jsx15("div", { className: "mt-1.5 flex flex-wrap items-center gap-1.5", children: meta.map((m, i) => /* @__PURE__ */ jsx15("span", { className: "rounded-full bg-secondary px-2 py-0.5 text-xs font-medium text-muted-foreground", children: m }, i)) })
6968
+ return /* @__PURE__ */ jsxs14("div", { className: "w-full max-w-full rounded-xl border border-warning/50 bg-warning/[0.06] text-sm shadow-sm ring-1 ring-warning/10", children: [
6969
+ /* @__PURE__ */ jsxs14("div", { className: "flex items-start gap-2.5 px-4 pt-3.5", children: [
6970
+ /* @__PURE__ */ jsx16("span", { className: "mt-0.5 inline-flex h-6 w-6 shrink-0 items-center justify-center rounded-full bg-warning/15 text-warning", children: /* @__PURE__ */ jsx16(ToolGlyph, { name: call.name, className: "h-3.5 w-3.5" }) }),
6971
+ /* @__PURE__ */ jsxs14("div", { className: "min-w-0 flex-1", children: [
6972
+ /* @__PURE__ */ jsx16("p", { className: "text-xs font-semibold uppercase tracking-[0.05em] text-warning-strong", children: approval ? "Needs your approval" : "Awaiting approval" }),
6973
+ /* @__PURE__ */ jsx16("p", { className: "mt-0.5 text-[15px] font-semibold leading-snug text-foreground", children: friendlyToolTitle(call) }),
6974
+ summary && /* @__PURE__ */ jsx16("p", { className: "mt-1 text-xs leading-relaxed text-muted-foreground", children: summary }),
6975
+ typeSlug && /* @__PURE__ */ jsx16("p", { className: "mt-1 font-mono text-xs text-muted-foreground", children: typeSlug }),
6976
+ meta.length > 0 && /* @__PURE__ */ jsx16("div", { className: "mt-1.5 flex flex-wrap items-center gap-1.5", children: meta.map((m, i) => /* @__PURE__ */ jsx16("span", { className: "rounded-full bg-secondary px-2 py-0.5 text-xs font-medium text-muted-foreground", children: m }, i)) })
6157
6977
  ] })
6158
6978
  ] }),
6159
- /* @__PURE__ */ jsxs13("div", { className: "flex flex-wrap items-center gap-2 px-4 pb-3.5 pt-3", children: [
6160
- approval && /* @__PURE__ */ jsxs13(Fragment8, { children: [
6161
- /* @__PURE__ */ jsx15(
6979
+ /* @__PURE__ */ jsxs14("div", { className: "flex flex-wrap items-center gap-2 px-4 pb-3.5 pt-3", children: [
6980
+ approval && /* @__PURE__ */ jsxs14(Fragment9, { children: [
6981
+ /* @__PURE__ */ jsx16(
6162
6982
  "button",
6163
6983
  {
6164
6984
  type: "button",
@@ -6168,7 +6988,7 @@ function ProposalCard({
6168
6988
  children: "Approve & run"
6169
6989
  }
6170
6990
  ),
6171
- /* @__PURE__ */ jsx15(
6991
+ /* @__PURE__ */ jsx16(
6172
6992
  "button",
6173
6993
  {
6174
6994
  type: "button",
@@ -6179,7 +6999,7 @@ function ProposalCard({
6179
6999
  }
6180
7000
  )
6181
7001
  ] }),
6182
- /* @__PURE__ */ jsxs13(
7002
+ /* @__PURE__ */ jsxs14(
6183
7003
  "button",
6184
7004
  {
6185
7005
  type: "button",
@@ -6188,12 +7008,12 @@ function ProposalCard({
6188
7008
  className: "ml-auto inline-flex items-center gap-1 rounded text-[12px] font-medium text-muted-foreground transition hover:text-foreground",
6189
7009
  children: [
6190
7010
  expanded ? "Hide details" : "View details",
6191
- /* @__PURE__ */ jsx15(ChevronDown, { className: `h-3 w-3 transition-transform ${expanded ? "rotate-180" : ""}` })
7011
+ /* @__PURE__ */ jsx16(ChevronDown, { className: `h-3 w-3 transition-transform ${expanded ? "rotate-180" : ""}` })
6192
7012
  ]
6193
7013
  }
6194
7014
  )
6195
7015
  ] }),
6196
- expanded && /* @__PURE__ */ jsx15("div", { className: "border-t border-warning/20 px-4 py-3 text-xs", children: custom ?? /* @__PURE__ */ jsx15(DefaultToolDetail, { call }) })
7016
+ expanded && /* @__PURE__ */ jsx16("div", { className: "border-t border-warning/20 px-4 py-3 text-xs", children: custom ?? /* @__PURE__ */ jsx16(DefaultToolDetail, { call }) })
6197
7017
  ] });
6198
7018
  }
6199
7019
  function formatFollowupWhen(when) {
@@ -6208,14 +7028,14 @@ function FollowupCard({ call }) {
6208
7028
  const when = typeof a.when === "string" ? a.when : typeof a.at === "string" ? a.at : typeof a.schedule === "string" ? a.schedule : null;
6209
7029
  const failed = toolCallFailed(call);
6210
7030
  const errorText = failed ? toolOutcomeOf(call)?.message ?? "Scheduling failed" : null;
6211
- return /* @__PURE__ */ jsx15("div", { className: "flex items-start gap-2", children: /* @__PURE__ */ jsxs13("div", { className: "min-w-0 flex-1 overflow-hidden rounded-[var(--radius-lg)] border border-[var(--border-subtle)] bg-[var(--md3-surface-container)]", children: [
6212
- /* @__PURE__ */ jsxs13("div", { className: "flex w-full items-center gap-2.5 px-3 py-2", children: [
6213
- /* @__PURE__ */ jsx15("span", { className: "flex h-6 w-6 shrink-0 items-center justify-center rounded-[var(--radius-sm)] border border-border bg-muted text-muted-foreground", children: /* @__PURE__ */ jsx15(ToolGlyph, { name: call.name, className: "h-3.5 w-3.5" }) }),
6214
- /* @__PURE__ */ jsx15("span", { className: "shrink-0 whitespace-nowrap text-xs font-medium text-foreground", children: friendlyToolTitle(call) }),
6215
- when && /* @__PURE__ */ jsx15("span", { title: when, className: "hidden min-w-0 flex-1 truncate font-mono text-xs tabular-nums text-muted-foreground sm:inline", children: formatFollowupWhen(when) }),
6216
- /* @__PURE__ */ jsx15("span", { className: "ml-auto flex shrink-0 items-center gap-1.5", children: call.status === "running" ? /* @__PURE__ */ jsx15("svg", { className: "h-3 w-3 shrink-0 animate-spin text-[var(--accent-text)]", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", "aria-hidden": true, children: /* @__PURE__ */ jsx15("path", { d: "M21 12a9 9 0 1 1-6.219-8.56", strokeLinecap: "round" }) }) : /* @__PURE__ */ jsx15("span", { className: `h-1.5 w-1.5 shrink-0 rounded-full ${failed ? "bg-[var(--surface-danger-text)]" : "bg-[var(--surface-success-text)]"}` }) })
7031
+ return /* @__PURE__ */ jsx16("div", { className: "flex items-start gap-2", children: /* @__PURE__ */ jsxs14("div", { className: "min-w-0 flex-1 overflow-hidden rounded-[var(--radius-lg)] border border-[var(--border-subtle)] bg-[var(--md3-surface-container)]", children: [
7032
+ /* @__PURE__ */ jsxs14("div", { className: "flex w-full items-center gap-2.5 px-3 py-2", children: [
7033
+ /* @__PURE__ */ jsx16("span", { className: "flex h-6 w-6 shrink-0 items-center justify-center rounded-[var(--radius-sm)] border border-border bg-muted text-muted-foreground", children: /* @__PURE__ */ jsx16(ToolGlyph, { name: call.name, className: "h-3.5 w-3.5" }) }),
7034
+ /* @__PURE__ */ jsx16("span", { className: "shrink-0 whitespace-nowrap text-xs font-medium text-foreground", children: friendlyToolTitle(call) }),
7035
+ when && /* @__PURE__ */ jsx16("span", { title: when, className: "hidden min-w-0 flex-1 truncate font-mono text-xs tabular-nums text-muted-foreground sm:inline", children: formatFollowupWhen(when) }),
7036
+ /* @__PURE__ */ jsx16("span", { className: "ml-auto flex shrink-0 items-center gap-1.5", children: call.status === "running" ? /* @__PURE__ */ jsx16("svg", { className: "h-3 w-3 shrink-0 animate-spin text-[var(--accent-text)]", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", "aria-hidden": true, children: /* @__PURE__ */ jsx16("path", { d: "M21 12a9 9 0 1 1-6.219-8.56", strokeLinecap: "round" }) }) : /* @__PURE__ */ jsx16("span", { className: `h-1.5 w-1.5 shrink-0 rounded-full ${failed ? "bg-[var(--surface-danger-text)]" : "bg-[var(--surface-success-text)]"}` }) })
6217
7037
  ] }),
6218
- errorText && /* @__PURE__ */ jsx15("div", { className: "border-t border-border px-3 py-2 text-xs text-[var(--surface-danger-text)]", children: errorText })
7038
+ errorText && /* @__PURE__ */ jsx16("div", { className: "border-t border-border px-3 py-2 text-xs text-[var(--surface-danger-text)]", children: errorText })
6219
7039
  ] }) });
6220
7040
  }
6221
7041
  function toolRowTitle(call) {
@@ -6238,10 +7058,10 @@ function ToolCallCard({
6238
7058
  const arrival = useArrivalStyle(staggerIndex ?? 0);
6239
7059
  const pending = call.status === "done" ? pendingApprovalOf(call) : null;
6240
7060
  const kind = blockKindOf(call);
6241
- const arrive = (row) => /* @__PURE__ */ jsx15("div", { className: "agent-arrive", style: arrival, children: row });
7061
+ const arrive = (row) => /* @__PURE__ */ jsx16("div", { className: "agent-arrive", style: arrival, children: row });
6242
7062
  if (pending) {
6243
7063
  return arrive(
6244
- /* @__PURE__ */ jsx15(
7064
+ /* @__PURE__ */ jsx16(
6245
7065
  ProposalCard,
6246
7066
  {
6247
7067
  call,
@@ -6254,18 +7074,18 @@ function ToolCallCard({
6254
7074
  );
6255
7075
  }
6256
7076
  if (kind === "followup") {
6257
- return arrive(/* @__PURE__ */ jsx15(FollowupCard, { call }));
7077
+ return arrive(/* @__PURE__ */ jsx16(FollowupCard, { call }));
6258
7078
  }
6259
7079
  const custom = renderers?.[call.name]?.(call, message);
6260
7080
  return arrive(
6261
- /* @__PURE__ */ jsx15(
7081
+ /* @__PURE__ */ jsx16(
6262
7082
  InlineToolItem,
6263
7083
  {
6264
7084
  part: chatToolCallPart(call),
6265
7085
  title: toolRowTitle(call),
6266
7086
  description: toolRowDescription(call),
6267
- renderToolDetail: () => custom ?? (call.name === "sandbox_run_command" ? /* @__PURE__ */ jsx15(ShellDetail, { call }) : /* @__PURE__ */ jsx15(DefaultToolDetail, { call })),
6268
- actions: onOpenRun && call.name.startsWith("sandbox_") ? /* @__PURE__ */ jsx15(
7087
+ renderToolDetail: () => custom ?? (call.name === "sandbox_run_command" ? /* @__PURE__ */ jsx16(ShellDetail, { call }) : /* @__PURE__ */ jsx16(DefaultToolDetail, { call })),
7088
+ actions: onOpenRun && call.name.startsWith("sandbox_") ? /* @__PURE__ */ jsx16(
6269
7089
  "button",
6270
7090
  {
6271
7091
  type: "button",
@@ -6273,9 +7093,9 @@ function ToolCallCard({
6273
7093
  "aria-label": "Open full transcript",
6274
7094
  title: "Open full transcript",
6275
7095
  className: "rounded-md p-1.5 text-muted-foreground transition hover:bg-accent hover:text-foreground",
6276
- children: /* @__PURE__ */ jsxs13("svg", { className: "h-3.5 w-3.5", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: [
6277
- /* @__PURE__ */ jsx15("path", { d: "M7 17 17 7" }),
6278
- /* @__PURE__ */ jsx15("path", { d: "M7 7h10v10" })
7096
+ children: /* @__PURE__ */ jsxs14("svg", { className: "h-3.5 w-3.5", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: [
7097
+ /* @__PURE__ */ jsx16("path", { d: "M7 17 17 7" }),
7098
+ /* @__PURE__ */ jsx16("path", { d: "M7 7h10v10" })
6279
7099
  ] })
6280
7100
  }
6281
7101
  ) : void 0
@@ -6284,7 +7104,7 @@ function ToolCallCard({
6284
7104
  );
6285
7105
  }
6286
7106
  function StreamingCaret() {
6287
- return /* @__PURE__ */ jsx15(
7107
+ return /* @__PURE__ */ jsx16(
6288
7108
  "span",
6289
7109
  {
6290
7110
  className: "ml-0.5 inline-block h-[1.1em] w-[3px] translate-y-[2px] animate-[agent-caret_1s_step-end_infinite] rounded-sm bg-foreground/70",
@@ -6301,7 +7121,7 @@ function SegmentText({
6301
7121
  messageClassName
6302
7122
  }) {
6303
7123
  const text = useSmoothText(content, streaming);
6304
- const body = useMemo7(() => renderBody(text), [renderBody, text]);
7124
+ const body = useMemo9(() => renderBody(text), [renderBody, text]);
6305
7125
  if (!content.trim() && !showCaret) return null;
6306
7126
  return (
6307
7127
  // A settled run arrives from a short blur; the LIVE run does not, because
@@ -6309,9 +7129,9 @@ function SegmentText({
6309
7129
  // the container on top of that makes the paragraph shimmer while it types.
6310
7130
  // The distinction is what separates "the answer materialised" from "the
6311
7131
  // log was appended to".
6312
- /* @__PURE__ */ jsxs13("div", { className: `${messageClassName}${streaming ? "" : " agent-stream-in"}`, children: [
7132
+ /* @__PURE__ */ jsxs14("div", { className: `${messageClassName}${streaming ? "" : " agent-stream-in"}`, children: [
6313
7133
  body,
6314
- showCaret && /* @__PURE__ */ jsx15(StreamingCaret, {})
7134
+ showCaret && /* @__PURE__ */ jsx16(StreamingCaret, {})
6315
7135
  ] })
6316
7136
  );
6317
7137
  }
@@ -6336,7 +7156,7 @@ function SegmentedBody({
6336
7156
  const leftoverToolCalls = (msg.toolCalls ?? []).filter(
6337
7157
  (tc) => !segmentToolIds.has(tc.id)
6338
7158
  );
6339
- const renderToolCard = (call, index) => /* @__PURE__ */ jsx15(
7159
+ const renderToolCard = (call, index) => /* @__PURE__ */ jsx16(
6340
7160
  ToolCallCard,
6341
7161
  {
6342
7162
  call,
@@ -6364,7 +7184,7 @@ function SegmentedBody({
6364
7184
  for (const g of groups) {
6365
7185
  if (g.kind === "text") {
6366
7186
  children.push(
6367
- /* @__PURE__ */ jsx15(
7187
+ /* @__PURE__ */ jsx16(
6368
7188
  SegmentText,
6369
7189
  {
6370
7190
  content: g.content,
@@ -6380,13 +7200,13 @@ function SegmentedBody({
6380
7200
  }
6381
7201
  if (!streaming && g.calls.length >= COLLAPSE_TOOL_RUN_AT && !g.calls.some(isImportantTool)) {
6382
7202
  children.push(
6383
- /* @__PURE__ */ jsxs13("details", { children: [
6384
- /* @__PURE__ */ jsxs13("summary", { className: "cursor-pointer select-none rounded-md py-0.5 text-xs font-medium text-muted-foreground [transition:color_var(--motion-control)] hover:text-foreground", children: [
7203
+ /* @__PURE__ */ jsxs14("details", { children: [
7204
+ /* @__PURE__ */ jsxs14("summary", { className: "cursor-pointer select-none rounded-md py-0.5 text-xs font-medium text-muted-foreground [transition:color_var(--motion-control)] hover:text-foreground", children: [
6385
7205
  "Worked through ",
6386
7206
  g.calls.length,
6387
7207
  " steps"
6388
7208
  ] }),
6389
- /* @__PURE__ */ jsx15("div", { className: "mt-1.5 flex flex-col gap-1.5", children: g.calls.map(renderToolCard) })
7209
+ /* @__PURE__ */ jsx16("div", { className: "mt-1.5 flex flex-col gap-1.5", children: g.calls.map(renderToolCard) })
6390
7210
  ] }, `tools-fold-${g.index}`)
6391
7211
  );
6392
7212
  continue;
@@ -6395,9 +7215,9 @@ function SegmentedBody({
6395
7215
  }
6396
7216
  leftoverToolCalls.forEach((call, index) => children.push(renderToolCard(call, index)));
6397
7217
  if (streaming && segments[lastIndex]?.kind === "tool") {
6398
- children.push(/* @__PURE__ */ jsx15(StreamingCaret, {}, "streaming-caret"));
7218
+ children.push(/* @__PURE__ */ jsx16(StreamingCaret, {}, "streaming-caret"));
6399
7219
  }
6400
- return /* @__PURE__ */ jsx15("div", { className: "flex flex-col gap-2", children });
7220
+ return /* @__PURE__ */ jsx16("div", { className: "flex flex-col gap-2", children });
6401
7221
  }
6402
7222
  var QUIET_META_LANE_CLASS = "mt-1 flex h-[18px] items-center gap-2 text-xs tabular-nums text-muted-foreground opacity-0 transition-opacity duration-150 group-hover:opacity-100 group-focus-within:opacity-100 motion-reduce:transition-none [@media(hover:none)]:opacity-100";
6403
7223
  function copyTextOf(msg) {
@@ -6406,9 +7226,9 @@ function copyTextOf(msg) {
6406
7226
  return msg.content;
6407
7227
  }
6408
7228
  function CopyMessageButton({ text }) {
6409
- const [copied, setCopied] = useState16(false);
6410
- const timerRef = useRef12(null);
6411
- useEffect12(
7229
+ const [copied, setCopied] = useState18(false);
7230
+ const timerRef = useRef14(null);
7231
+ useEffect14(
6412
7232
  () => () => {
6413
7233
  if (timerRef.current !== null) clearTimeout(timerRef.current);
6414
7234
  },
@@ -6427,7 +7247,7 @@ function CopyMessageButton({ text }) {
6427
7247
  }
6428
7248
  );
6429
7249
  };
6430
- return /* @__PURE__ */ jsx15(
7250
+ return /* @__PURE__ */ jsx16(
6431
7251
  "button",
6432
7252
  {
6433
7253
  type: "button",
@@ -6435,9 +7255,9 @@ function CopyMessageButton({ text }) {
6435
7255
  "aria-label": "Copy message",
6436
7256
  title: "Copy message",
6437
7257
  className: "rounded p-0.5 text-muted-foreground transition hover:bg-accent hover:text-foreground focus:outline-none focus-visible:ring-2 focus-visible:ring-ring",
6438
- children: copied ? /* @__PURE__ */ jsx15("svg", { className: "h-3.5 w-3.5", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: /* @__PURE__ */ jsx15("polyline", { points: "20 6 9 17 4 12" }) }) : /* @__PURE__ */ jsxs13("svg", { className: "h-3.5 w-3.5", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: [
6439
- /* @__PURE__ */ jsx15("rect", { x: "9", y: "9", width: "13", height: "13", rx: "2" }),
6440
- /* @__PURE__ */ jsx15("path", { d: "M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" })
7258
+ children: copied ? /* @__PURE__ */ jsx16("svg", { className: "h-3.5 w-3.5", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: /* @__PURE__ */ jsx16("polyline", { points: "20 6 9 17 4 12" }) }) : /* @__PURE__ */ jsxs14("svg", { className: "h-3.5 w-3.5", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: [
7259
+ /* @__PURE__ */ jsx16("rect", { x: "9", y: "9", width: "13", height: "13", rx: "2" }),
7260
+ /* @__PURE__ */ jsx16("path", { d: "M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" })
6441
7261
  ] })
6442
7262
  }
6443
7263
  );
@@ -6460,34 +7280,34 @@ function AssistantMessageImpl({
6460
7280
  }) {
6461
7281
  const content = useSmoothText(msg.content, streaming);
6462
7282
  const reasoning = useSmoothText(msg.reasoning ?? "", streaming);
6463
- const body = useMemo7(() => renderBody(content), [renderBody, content]);
7283
+ const body = useMemo9(() => renderBody(content), [renderBody, content]);
6464
7284
  const segments = msg.segments;
6465
7285
  const hasAnswerText = content !== "" || (segments?.some((s) => s.kind === "text" && s.content.trim() !== "") ?? false);
6466
- const reasoningScrollRef = useRef12(null);
6467
- const thinkStartRef = useRef12(null);
6468
- const thinkMsRef = useRef12(null);
7286
+ const reasoningScrollRef = useRef14(null);
7287
+ const thinkStartRef = useRef14(null);
7288
+ const thinkMsRef = useRef14(null);
6469
7289
  if (streaming && reasoning && !hasAnswerText && thinkStartRef.current === null) {
6470
7290
  thinkStartRef.current = performance.now();
6471
7291
  }
6472
7292
  if (hasAnswerText && thinkStartRef.current !== null && thinkMsRef.current === null) {
6473
7293
  thinkMsRef.current = performance.now() - thinkStartRef.current;
6474
7294
  }
6475
- useEffect12(() => {
7295
+ useEffect14(() => {
6476
7296
  const el = reasoningScrollRef.current;
6477
7297
  if (el && streaming && !hasAnswerText) el.scrollTop = el.scrollHeight;
6478
7298
  }, [reasoning, streaming, hasAnswerText]);
6479
7299
  const thinkingSeconds = useThinkingSeconds(
6480
7300
  streaming && !!reasoning && !hasAnswerText
6481
7301
  );
6482
- const [reasoningToggled, setReasoningToggled] = useState16(null);
7302
+ const [reasoningToggled, setReasoningToggled] = useState18(null);
6483
7303
  const reasoningOpen = reasoningToggled ?? !hasAnswerText;
6484
7304
  const quiet = chrome === "quiet";
6485
- return /* @__PURE__ */ jsxs13("div", { className: `mx-auto w-full max-w-3xl px-6 ${quiet ? "group pb-1 pt-3" : "py-3"}`, children: [
6486
- !quiet && /* @__PURE__ */ jsxs13("div", { className: "mb-1 flex items-baseline gap-2 text-xs tabular-nums text-muted-foreground", children: [
6487
- /* @__PURE__ */ jsx15("span", { className: "font-semibold uppercase tracking-[0.05em]", children: agentLabel }),
6488
- msg.modelUsed && /* @__PURE__ */ jsx15("span", { className: "font-mono normal-case", children: msg.modelUsed }),
6489
- formatTokensPerSecond(msg) && /* @__PURE__ */ jsx15("span", { children: formatTokensPerSecond(msg) }),
6490
- formatModelCost(msg, models) && /* @__PURE__ */ jsx15("span", { children: formatModelCost(msg, models) })
7305
+ return /* @__PURE__ */ jsxs14("div", { className: `mx-auto w-full max-w-3xl px-6 ${quiet ? "group pb-1 pt-3" : "py-3"}`, children: [
7306
+ !quiet && /* @__PURE__ */ jsxs14("div", { className: "mb-1 flex items-baseline gap-2 text-xs tabular-nums text-muted-foreground", children: [
7307
+ /* @__PURE__ */ jsx16("span", { className: "font-semibold uppercase tracking-[0.05em]", children: agentLabel }),
7308
+ msg.modelUsed && /* @__PURE__ */ jsx16("span", { className: "font-mono normal-case", children: msg.modelUsed }),
7309
+ formatTokensPerSecond(msg) && /* @__PURE__ */ jsx16("span", { children: formatTokensPerSecond(msg) }),
7310
+ formatModelCost(msg, models) && /* @__PURE__ */ jsx16("span", { children: formatModelCost(msg, models) })
6491
7311
  ] }),
6492
7312
  reasoning && // The canonical run-row grammar (RunRowShell — the same shell the tool
6493
7313
  // rows compose): one family of rows instead of a bespoke disclosure per
@@ -6497,12 +7317,12 @@ function AssistantMessageImpl({
6497
7317
  // and a click outranks the default from then on — the contract the old
6498
7318
  // hand-rolled disclosure had, now enforced through the shell's
6499
7319
  // controlled `open`.
6500
- /* @__PURE__ */ jsx15(
7320
+ /* @__PURE__ */ jsx16(
6501
7321
  RunRowShell,
6502
7322
  {
6503
7323
  className: "mb-2",
6504
- icon: /* @__PURE__ */ jsx15(BrainGlyph, { className: "h-3.5 w-3.5" }),
6505
- title: !hasAnswerText ? /* @__PURE__ */ jsxs13("span", { className: "agent-shimmer", "data-motion": "essential", children: [
7324
+ icon: /* @__PURE__ */ jsx16(BrainGlyph, { className: "h-3.5 w-3.5" }),
7325
+ title: !hasAnswerText ? /* @__PURE__ */ jsxs14("span", { className: "agent-shimmer", "data-motion": "essential", children: [
6506
7326
  "Thinking",
6507
7327
  thinkingSeconds >= 1 ? ` \xB7 ${thinkingSeconds}s` : "\u2026"
6508
7328
  ] }) : thinkMsRef.current != null ? (
@@ -6517,7 +7337,7 @@ function AssistantMessageImpl({
6517
7337
  status: hasAnswerText ? "idle" : "running",
6518
7338
  open: reasoningOpen,
6519
7339
  onOpenChange: (next) => setReasoningToggled(next),
6520
- children: /* @__PURE__ */ jsx15(
7340
+ children: /* @__PURE__ */ jsx16(
6521
7341
  "div",
6522
7342
  {
6523
7343
  ref: reasoningScrollRef,
@@ -6527,7 +7347,7 @@ function AssistantMessageImpl({
6527
7347
  )
6528
7348
  }
6529
7349
  ),
6530
- segments && segments.length > 0 ? /* @__PURE__ */ jsx15(
7350
+ segments && segments.length > 0 ? /* @__PURE__ */ jsx16(
6531
7351
  SegmentedBody,
6532
7352
  {
6533
7353
  segments,
@@ -6539,12 +7359,12 @@ function AssistantMessageImpl({
6539
7359
  toolRenderers,
6540
7360
  messageClassName
6541
7361
  }
6542
- ) : /* @__PURE__ */ jsxs13(Fragment8, { children: [
6543
- /* @__PURE__ */ jsxs13("div", { className: messageClassName, children: [
7362
+ ) : /* @__PURE__ */ jsxs14(Fragment9, { children: [
7363
+ /* @__PURE__ */ jsxs14("div", { className: messageClassName, children: [
6544
7364
  body,
6545
- streaming && content && !msg.toolCalls?.length && /* @__PURE__ */ jsx15(StreamingCaret, {})
7365
+ streaming && content && !msg.toolCalls?.length && /* @__PURE__ */ jsx16(StreamingCaret, {})
6546
7366
  ] }),
6547
- msg.toolCalls && msg.toolCalls.length > 0 && /* @__PURE__ */ jsx15("div", { className: "mt-2 flex flex-col gap-1.5", children: msg.toolCalls.map((tc, index) => /* @__PURE__ */ jsx15(
7367
+ msg.toolCalls && msg.toolCalls.length > 0 && /* @__PURE__ */ jsx16("div", { className: "mt-2 flex flex-col gap-1.5", children: msg.toolCalls.map((tc, index) => /* @__PURE__ */ jsx16(
6548
7368
  ToolCallCard,
6549
7369
  {
6550
7370
  call: tc,
@@ -6557,7 +7377,7 @@ function AssistantMessageImpl({
6557
7377
  tc.id
6558
7378
  )) })
6559
7379
  ] }),
6560
- durableCards && msg.parts && /* @__PURE__ */ jsx15(
7380
+ durableCards && msg.parts && /* @__PURE__ */ jsx16(
6561
7381
  DurableChatCards,
6562
7382
  {
6563
7383
  ...durableCards,
@@ -6566,7 +7386,7 @@ function AssistantMessageImpl({
6566
7386
  className: "mt-3"
6567
7387
  }
6568
7388
  ),
6569
- workProductCards && workProductPartsFromMessageParts(msg.parts).map((part) => /* @__PURE__ */ jsx15(
7389
+ workProductCards && workProductPartsFromMessageParts(msg.parts).map((part) => /* @__PURE__ */ jsx16(
6570
7390
  WorkProductCard,
6571
7391
  {
6572
7392
  part,
@@ -6576,7 +7396,7 @@ function AssistantMessageImpl({
6576
7396
  `${part.ref.id}:${part.ref.version}`
6577
7397
  )),
6578
7398
  renderExtras?.(msg),
6579
- resolveAttachmentUrl && attachmentPartsFromMessageParts(msg.parts).length > 0 && /* @__PURE__ */ jsx15("div", { className: "mt-2", children: /* @__PURE__ */ jsx15(
7399
+ resolveAttachmentUrl && attachmentPartsFromMessageParts(msg.parts).length > 0 && /* @__PURE__ */ jsx16("div", { className: "mt-2", children: /* @__PURE__ */ jsx16(
6580
7400
  MessageAttachments,
6581
7401
  {
6582
7402
  parts: attachmentPartsFromMessageParts(msg.parts),
@@ -6584,18 +7404,18 @@ function AssistantMessageImpl({
6584
7404
  justify: "start"
6585
7405
  }
6586
7406
  ) }),
6587
- quiet && /* @__PURE__ */ jsxs13("div", { "data-testid": "message-meta-lane", className: QUIET_META_LANE_CLASS, children: [
6588
- /* @__PURE__ */ jsx15(CopyMessageButton, { text: copyTextOf(msg) }),
6589
- msg.modelUsed && /* @__PURE__ */ jsx15("span", { className: "font-mono", children: msg.modelUsed }),
6590
- formatTokensPerSecond(msg) && /* @__PURE__ */ jsx15("span", { children: formatTokensPerSecond(msg) }),
6591
- formatModelCost(msg, models) && /* @__PURE__ */ jsx15("span", { children: formatModelCost(msg, models) })
7407
+ quiet && /* @__PURE__ */ jsxs14("div", { "data-testid": "message-meta-lane", className: QUIET_META_LANE_CLASS, children: [
7408
+ /* @__PURE__ */ jsx16(CopyMessageButton, { text: copyTextOf(msg) }),
7409
+ msg.modelUsed && /* @__PURE__ */ jsx16("span", { className: "font-mono", children: msg.modelUsed }),
7410
+ formatTokensPerSecond(msg) && /* @__PURE__ */ jsx16("span", { children: formatTokensPerSecond(msg) }),
7411
+ formatModelCost(msg, models) && /* @__PURE__ */ jsx16("span", { children: formatModelCost(msg, models) })
6592
7412
  ] })
6593
7413
  ] });
6594
7414
  }
6595
7415
  var AssistantMessage = memo(AssistantMessageImpl);
6596
7416
  function useThinkingSeconds(active) {
6597
- const [seconds, setSeconds] = useState16(0);
6598
- useEffect12(() => {
7417
+ const [seconds, setSeconds] = useState18(0);
7418
+ useEffect14(() => {
6599
7419
  if (!active) return;
6600
7420
  setSeconds(0);
6601
7421
  const id = setInterval(() => setSeconds((s) => s + 1), 1e3);
@@ -6605,23 +7425,23 @@ function useThinkingSeconds(active) {
6605
7425
  }
6606
7426
  function ThinkingRow({ agentLabel, chrome = "labeled" }) {
6607
7427
  const seconds = useThinkingSeconds(true);
6608
- return /* @__PURE__ */ jsxs13("div", { className: "mx-auto w-full max-w-3xl px-6 py-3", children: [
6609
- chrome !== "quiet" && /* @__PURE__ */ jsx15("p", { className: "mb-1 text-xs font-semibold uppercase tracking-[0.05em] text-muted-foreground", children: agentLabel }),
6610
- /* @__PURE__ */ jsxs13("div", { className: "flex items-center gap-2 text-[15px] text-muted-foreground", children: [
6611
- /* @__PURE__ */ jsx15("svg", { className: "h-4 w-4 animate-spin", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", "aria-hidden": true, children: /* @__PURE__ */ jsx15("path", { d: "M21 12a9 9 0 1 1-6.219-8.56", strokeLinecap: "round" }) }),
7428
+ return /* @__PURE__ */ jsxs14("div", { className: "mx-auto w-full max-w-3xl px-6 py-3", children: [
7429
+ chrome !== "quiet" && /* @__PURE__ */ jsx16("p", { className: "mb-1 text-xs font-semibold uppercase tracking-[0.05em] text-muted-foreground", children: agentLabel }),
7430
+ /* @__PURE__ */ jsxs14("div", { className: "flex items-center gap-2 text-[15px] text-muted-foreground", children: [
7431
+ /* @__PURE__ */ jsx16("svg", { className: "h-4 w-4 animate-spin", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", "aria-hidden": true, children: /* @__PURE__ */ jsx16("path", { d: "M21 12a9 9 0 1 1-6.219-8.56", strokeLinecap: "round" }) }),
6612
7432
  "Thinking",
6613
7433
  seconds >= 3 ? ` \xB7 ${seconds}s` : "..."
6614
7434
  ] })
6615
7435
  ] });
6616
7436
  }
6617
7437
  function StreamErrorRow({ message, onRetry }) {
6618
- return /* @__PURE__ */ jsx15("div", { className: "mx-auto w-full max-w-3xl px-6 py-3", children: /* @__PURE__ */ jsxs13("div", { role: "alert", className: "flex items-start gap-2.5 rounded-lg border border-destructive/40 bg-destructive/5 px-3 py-2.5 text-sm text-destructive", children: [
6619
- /* @__PURE__ */ jsxs13("svg", { className: "mt-0.5 h-4 w-4 shrink-0", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: [
6620
- /* @__PURE__ */ jsx15("circle", { cx: "12", cy: "12", r: "9" }),
6621
- /* @__PURE__ */ jsx15("path", { d: "M12 8v4m0 4h.01" })
7438
+ return /* @__PURE__ */ jsx16("div", { className: "mx-auto w-full max-w-3xl px-6 py-3", children: /* @__PURE__ */ jsxs14("div", { role: "alert", className: "flex items-start gap-2.5 rounded-lg border border-destructive/40 bg-destructive/5 px-3 py-2.5 text-sm text-destructive", children: [
7439
+ /* @__PURE__ */ jsxs14("svg", { className: "mt-0.5 h-4 w-4 shrink-0", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: [
7440
+ /* @__PURE__ */ jsx16("circle", { cx: "12", cy: "12", r: "9" }),
7441
+ /* @__PURE__ */ jsx16("path", { d: "M12 8v4m0 4h.01" })
6622
7442
  ] }),
6623
- /* @__PURE__ */ jsx15("span", { className: "min-w-0 flex-1 break-words", children: message }),
6624
- onRetry && /* @__PURE__ */ jsx15(
7443
+ /* @__PURE__ */ jsx16("span", { className: "min-w-0 flex-1 break-words", children: message }),
7444
+ onRetry && /* @__PURE__ */ jsx16(
6625
7445
  "button",
6626
7446
  {
6627
7447
  type: "button",
@@ -6655,33 +7475,33 @@ function ChatMessages({
6655
7475
  workProductCards
6656
7476
  }) {
6657
7477
  const messageClassName = messageSize === "large" ? "agent-app-message-copy text-[17px] leading-[1.6]" : "agent-app-message-copy text-base leading-[1.6]";
6658
- const renderBody = useMemo7(
6659
- () => renderMarkdown ?? ((content) => /* @__PURE__ */ jsx15("p", { className: "whitespace-pre-wrap", children: content })),
7478
+ const renderBody = useMemo9(
7479
+ () => renderMarkdown ?? ((content) => /* @__PURE__ */ jsx16("p", { className: "whitespace-pre-wrap", children: content })),
6660
7480
  [renderMarkdown]
6661
7481
  );
6662
7482
  const lastIsUser = messages[messages.length - 1]?.role === "user";
6663
7483
  const quiet = chrome === "quiet";
6664
7484
  if (messages.length === 0 && !loading && !error) {
6665
- const empty = renderEmpty ? renderEmpty() : /* @__PURE__ */ jsx15(ChatEmptyState, { ...emptyState });
6666
- return /* @__PURE__ */ jsxs13(Fragment8, { children: [
7485
+ const empty = renderEmpty ? renderEmpty() : /* @__PURE__ */ jsx16(ChatEmptyState, { ...emptyState });
7486
+ return /* @__PURE__ */ jsxs14(Fragment9, { children: [
6667
7487
  header,
6668
7488
  empty
6669
7489
  ] });
6670
7490
  }
6671
- return /* @__PURE__ */ jsxs13(Fragment8, { children: [
7491
+ return /* @__PURE__ */ jsxs14(Fragment9, { children: [
6672
7492
  header,
6673
7493
  messages.map(
6674
- (msg) => msg.role === "user" ? /* @__PURE__ */ jsxs13("div", { className: `mx-auto w-full max-w-3xl px-6 ${quiet ? "group pb-1 pt-3" : "py-3"}`, children: [
6675
- /* @__PURE__ */ jsxs13("div", { className: `ml-auto w-fit ${quiet ? "max-w-[72%]" : "max-w-[85%]"}`, children: [
6676
- !quiet && /* @__PURE__ */ jsx15("p", { className: "mb-1 text-right text-xs font-semibold uppercase tracking-[0.05em] text-muted-foreground", children: userLabel }),
6677
- /* @__PURE__ */ jsx15(
7494
+ (msg) => msg.role === "user" ? /* @__PURE__ */ jsxs14("div", { className: `mx-auto w-full max-w-3xl px-6 ${quiet ? "group pb-1 pt-3" : "py-3"}`, children: [
7495
+ /* @__PURE__ */ jsxs14("div", { className: `ml-auto w-fit ${quiet ? "max-w-[72%]" : "max-w-[85%]"}`, children: [
7496
+ !quiet && /* @__PURE__ */ jsx16("p", { className: "mb-1 text-right text-xs font-semibold uppercase tracking-[0.05em] text-muted-foreground", children: userLabel }),
7497
+ /* @__PURE__ */ jsx16(
6678
7498
  "div",
6679
7499
  {
6680
7500
  className: quiet ? `rounded-2xl bg-[color-mix(in_srgb,hsl(var(--secondary))_65%,hsl(var(--background)))] px-4 py-2.5 ${messageClassName}` : `rounded-2xl rounded-tr-md bg-primary/10 px-4 py-2.5 ${messageClassName}`,
6681
- children: /* @__PURE__ */ jsx15("p", { className: "whitespace-pre-wrap", children: msg.content })
7501
+ children: /* @__PURE__ */ jsx16("p", { className: "whitespace-pre-wrap", children: msg.content })
6682
7502
  }
6683
7503
  ),
6684
- resolveAttachmentUrl && attachmentPartsFromMessageParts(msg.parts).length > 0 && /* @__PURE__ */ jsx15("div", { className: "mt-1.5", children: /* @__PURE__ */ jsx15(
7504
+ resolveAttachmentUrl && attachmentPartsFromMessageParts(msg.parts).length > 0 && /* @__PURE__ */ jsx16("div", { className: "mt-1.5", children: /* @__PURE__ */ jsx16(
6685
7505
  MessageAttachments,
6686
7506
  {
6687
7507
  parts: attachmentPartsFromMessageParts(msg.parts),
@@ -6690,8 +7510,8 @@ function ChatMessages({
6690
7510
  }
6691
7511
  ) })
6692
7512
  ] }),
6693
- quiet && /* @__PURE__ */ jsx15("div", { "data-testid": "message-meta-lane", className: `${QUIET_META_LANE_CLASS} justify-end`, children: /* @__PURE__ */ jsx15(CopyMessageButton, { text: msg.content }) })
6694
- ] }, msg.id) : /* @__PURE__ */ jsx15(
7513
+ quiet && /* @__PURE__ */ jsx16("div", { "data-testid": "message-meta-lane", className: `${QUIET_META_LANE_CLASS} justify-end`, children: /* @__PURE__ */ jsx16(CopyMessageButton, { text: msg.content }) })
7514
+ ] }, msg.id) : /* @__PURE__ */ jsx16(
6695
7515
  AssistantMessage,
6696
7516
  {
6697
7517
  msg,
@@ -6712,8 +7532,8 @@ function ChatMessages({
6712
7532
  msg.id
6713
7533
  )
6714
7534
  ),
6715
- loading && lastIsUser && /* @__PURE__ */ jsx15(ThinkingRow, { agentLabel, chrome }),
6716
- error && !loading && /* @__PURE__ */ jsx15(StreamErrorRow, { message: error, onRetry })
7535
+ loading && lastIsUser && /* @__PURE__ */ jsx16(ThinkingRow, { agentLabel, chrome }),
7536
+ error && !loading && /* @__PURE__ */ jsx16(StreamErrorRow, { message: error, onRetry })
6717
7537
  ] });
6718
7538
  }
6719
7539
 
@@ -6748,6 +7568,10 @@ export {
6748
7568
  dispatchChatStreamLine,
6749
7569
  consumeChatStream,
6750
7570
  streamChatTurn,
7571
+ pickDictationMimeType,
7572
+ dictationErrorMessage,
7573
+ formatDictationElapsed,
7574
+ useDictation,
6751
7575
  ChatComposer,
6752
7576
  DurablePlanClientError,
6753
7577
  createDurablePlanDecisionClient,
@@ -6803,6 +7627,7 @@ export {
6803
7627
  recordGridFail,
6804
7628
  isRecordGridCellApplicable,
6805
7629
  sameRecordGridValue,
7630
+ diffRecordGridProposal,
6806
7631
  parseRecordGridInput,
6807
7632
  validateRecordGridCell,
6808
7633
  readRecordGridCell,
@@ -6822,6 +7647,7 @@ export {
6822
7647
  withoutRecordGridRemoved,
6823
7648
  pruneRecordGridOverlay,
6824
7649
  RecordGrid,
7650
+ CommandPalette,
6825
7651
  DEFAULT_SPARKLINE_WIDTH,
6826
7652
  DEFAULT_SPARKLINE_HEIGHT,
6827
7653
  DEFAULT_SPARKLINE_LABEL,
@@ -6851,4 +7677,4 @@ export {
6851
7677
  useThinkingSeconds,
6852
7678
  ChatMessages
6853
7679
  };
6854
- //# sourceMappingURL=chunk-FOXGPGXF.js.map
7680
+ //# sourceMappingURL=chunk-AWM4H5XR.js.map