@tangle-network/agent-app 0.45.62 → 0.45.64

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,6 +10,7 @@ import {
10
10
  ChevronDown,
11
11
  OVERLAY_SHADOW,
12
12
  POPOVER_OPTION_FOCUS,
13
+ POPOVER_SURFACE_ATTR,
13
14
  PopoverSurface,
14
15
  usePending,
15
16
  usePopover
@@ -19,9 +20,11 @@ import {
19
20
  } from "./chunk-3UBAO3N5.js";
20
21
  import {
21
22
  UNTITLED_SESSION_LABEL,
23
+ filterCommandPaletteItems,
24
+ groupCommandPaletteItems,
22
25
  mergeSessionPages,
23
26
  sessionLabel
24
- } from "./chunk-PC2WYTK7.js";
27
+ } from "./chunk-EDTWGSQT.js";
25
28
  import {
26
29
  attachmentPartsFromMessageParts
27
30
  } from "./chunk-5ZTFZBS6.js";
@@ -41,7 +44,7 @@ import {
41
44
  } from "./chunk-YJMCRXQQ.js";
42
45
 
43
46
  // src/web-react/index.tsx
44
- import { useEffect as useEffect12, useMemo as useMemo7, useRef as useRef12, useState as useState16, memo } from "react";
47
+ import { useEffect as useEffect14, useMemo as useMemo9, useRef as useRef14, useState as useState18, memo } from "react";
45
48
  import { InlineToolItem, RunRowShell } from "@tangle-network/ui/run";
46
49
 
47
50
  // src/web-react/smooth-text.ts
@@ -1408,11 +1411,147 @@ async function streamChatTurn(opts) {
1408
1411
 
1409
1412
  // src/web-react/chat-composer.tsx
1410
1413
  import {
1411
- useCallback as useCallback2,
1412
- useEffect as useEffect5,
1413
- useRef as useRef5,
1414
- useState as useState7
1414
+ useCallback as useCallback3,
1415
+ useEffect as useEffect6,
1416
+ useMemo as useMemo3,
1417
+ useId,
1418
+ useRef as useRef6,
1419
+ useState as useState8
1415
1420
  } from "react";
1421
+
1422
+ // src/web-react/use-dictation.ts
1423
+ import { useCallback as useCallback2, useEffect as useEffect5, useRef as useRef5, useState as useState7 } from "react";
1424
+ var PREFERRED_MIME_TYPES = ["audio/webm;codecs=opus", "audio/webm", "audio/mp4"];
1425
+ function pickDictationMimeType() {
1426
+ if (typeof MediaRecorder === "undefined" || typeof MediaRecorder.isTypeSupported !== "function") {
1427
+ return void 0;
1428
+ }
1429
+ for (const type of PREFERRED_MIME_TYPES) {
1430
+ if (MediaRecorder.isTypeSupported(type)) return type;
1431
+ }
1432
+ return void 0;
1433
+ }
1434
+ function detectDictationSupport() {
1435
+ return typeof navigator !== "undefined" && typeof navigator.mediaDevices?.getUserMedia === "function" && typeof MediaRecorder !== "undefined";
1436
+ }
1437
+ function dictationErrorMessage(error) {
1438
+ if (error instanceof DOMException) {
1439
+ if (error.name === "NotAllowedError") return "Microphone access was denied \u2014 allow it in the browser to dictate.";
1440
+ if (error.name === "NotFoundError") return "No microphone found on this device.";
1441
+ }
1442
+ return "Could not start recording.";
1443
+ }
1444
+ function formatDictationElapsed(totalSeconds) {
1445
+ const safe = Number.isFinite(totalSeconds) && totalSeconds > 0 ? Math.floor(totalSeconds) : 0;
1446
+ const minutes = Math.floor(safe / 60);
1447
+ const seconds = safe % 60;
1448
+ return `${minutes}:${String(seconds).padStart(2, "0")}`;
1449
+ }
1450
+ function releaseStream(stream) {
1451
+ for (const track of stream.getTracks()) track.stop();
1452
+ }
1453
+ function useDictation({ onDictate, onError }) {
1454
+ const [supported] = useState7(detectDictationSupport);
1455
+ const [recording, setRecording] = useState7(false);
1456
+ const [elapsedSeconds, setElapsedSeconds] = useState7(0);
1457
+ const sessionRef = useRef5(null);
1458
+ const cancelPendingStartRef = useRef5(null);
1459
+ const callbacksRef = useRef5({ onDictate, onError });
1460
+ callbacksRef.current = { onDictate, onError };
1461
+ useEffect5(() => {
1462
+ if (!recording) return;
1463
+ setElapsedSeconds(0);
1464
+ const id = setInterval(() => setElapsedSeconds((s) => s + 1), 1e3);
1465
+ return () => clearInterval(id);
1466
+ }, [recording]);
1467
+ const teardown = useCallback2((cancelled) => {
1468
+ const session = sessionRef.current;
1469
+ if (session === null) return;
1470
+ session.cancelled = session.cancelled || cancelled;
1471
+ sessionRef.current = null;
1472
+ releaseStream(session.stream);
1473
+ setRecording(false);
1474
+ }, []);
1475
+ const stop = useCallback2(() => {
1476
+ cancelPendingStartRef.current?.();
1477
+ cancelPendingStartRef.current = null;
1478
+ const session = sessionRef.current;
1479
+ if (session === null || session.cancelled) return;
1480
+ if (session.recorder.state !== "inactive") session.recorder.stop();
1481
+ }, []);
1482
+ const start = useCallback2(() => {
1483
+ if (!supported) return;
1484
+ if (sessionRef.current !== null || cancelPendingStartRef.current !== null) return;
1485
+ let pendingCancelled = false;
1486
+ cancelPendingStartRef.current = () => {
1487
+ pendingCancelled = true;
1488
+ };
1489
+ navigator.mediaDevices.getUserMedia({ audio: true }).then(
1490
+ (stream) => {
1491
+ cancelPendingStartRef.current = null;
1492
+ if (pendingCancelled) {
1493
+ releaseStream(stream);
1494
+ return;
1495
+ }
1496
+ const mimeType = pickDictationMimeType();
1497
+ const recorder = new MediaRecorder(stream, mimeType === void 0 ? void 0 : { mimeType });
1498
+ const session = {
1499
+ stream,
1500
+ recorder,
1501
+ chunks: [],
1502
+ mimeType: recorder.mimeType || mimeType || "",
1503
+ startedAt: Date.now(),
1504
+ cancelled: false
1505
+ };
1506
+ sessionRef.current = session;
1507
+ recorder.ondataavailable = (event) => {
1508
+ if (event.data.size > 0) session.chunks.push(event.data);
1509
+ };
1510
+ recorder.onstop = () => {
1511
+ teardown(session.cancelled);
1512
+ if (session.cancelled) return;
1513
+ const blob = new Blob(session.chunks, { type: session.mimeType });
1514
+ if (blob.size === 0) {
1515
+ callbacksRef.current.onError?.("Nothing was recorded.");
1516
+ return;
1517
+ }
1518
+ const durationSeconds = Math.max(0, Math.round((Date.now() - session.startedAt) / 1e3));
1519
+ callbacksRef.current.onDictate({ blob, mimeType: session.mimeType, durationSeconds });
1520
+ };
1521
+ recorder.onerror = () => {
1522
+ teardown(true);
1523
+ callbacksRef.current.onError?.("Recording stopped unexpectedly.");
1524
+ };
1525
+ recorder.start();
1526
+ setRecording(true);
1527
+ },
1528
+ (error) => {
1529
+ cancelPendingStartRef.current = null;
1530
+ if (pendingCancelled) return;
1531
+ callbacksRef.current.onError?.(dictationErrorMessage(error));
1532
+ }
1533
+ );
1534
+ }, [supported, teardown]);
1535
+ useEffect5(
1536
+ () => () => {
1537
+ cancelPendingStartRef.current?.();
1538
+ cancelPendingStartRef.current = null;
1539
+ const session = sessionRef.current;
1540
+ if (session === null) return;
1541
+ session.cancelled = true;
1542
+ sessionRef.current = null;
1543
+ try {
1544
+ if (session.recorder.state !== "inactive") session.recorder.stop();
1545
+ } finally {
1546
+ releaseStream(session.stream);
1547
+ }
1548
+ },
1549
+ []
1550
+ );
1551
+ return { supported, recording, elapsedSeconds, start, stop };
1552
+ }
1553
+
1554
+ // src/web-react/chat-composer.tsx
1416
1555
  import { Fragment as Fragment2, jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
1417
1556
  var IS_APPLE_PLATFORM = typeof navigator !== "undefined" && /Mac|iPhone|iPad|iPod/i.test(navigator.platform);
1418
1557
  function SendGlyph({ className }) {
@@ -1439,6 +1578,12 @@ function CloseGlyph({ className }) {
1439
1578
  function UploadGlyph({ className }) {
1440
1579
  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
1580
  }
1581
+ function MicGlyph({ className }) {
1582
+ return /* @__PURE__ */ jsxs5("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: [
1583
+ /* @__PURE__ */ jsx7("rect", { x: "9", y: "2", width: "6", height: "12", rx: "3" }),
1584
+ /* @__PURE__ */ jsx7("path", { d: "M5 10v1a7 7 0 0 0 14 0v-1M12 18v4" })
1585
+ ] });
1586
+ }
1442
1587
  var MAX_HEIGHT = 168;
1443
1588
  var DEFAULT_SEND_FAILURE = "Message not sent. Your draft is still here \u2014 try again.";
1444
1589
  function isRejectedOutcome(outcome) {
@@ -1480,6 +1625,9 @@ function ChatComposer({
1480
1625
  accept,
1481
1626
  dropTitle = "Drop files to add context",
1482
1627
  dropDescription = "They attach to your next message.",
1628
+ slashCommands,
1629
+ onDictate,
1630
+ onDictateError,
1483
1631
  focusShortcut = true,
1484
1632
  floating = false,
1485
1633
  sendLabel = "Send",
@@ -1487,31 +1635,47 @@ function ChatComposer({
1487
1635
  className
1488
1636
  }) {
1489
1637
  const isControlled = value !== void 0;
1490
- const [internal, setInternal] = useState7(initialValue ?? "");
1638
+ const [internal, setInternal] = useState8(initialValue ?? "");
1491
1639
  const text = isControlled ? value : internal;
1492
- const textRef = useRef5(text);
1640
+ const textRef = useRef6(text);
1493
1641
  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(
1642
+ const textareaRef = useRef6(null);
1643
+ const fileInputRef = useRef6(null);
1644
+ const folderInputRef = useRef6(null);
1645
+ const [dragOver, setDragOver] = useState8(false);
1646
+ const dragDepth = useRef6(0);
1647
+ const setText = useCallback3(
1500
1648
  (next) => {
1501
1649
  if (!isControlled) setInternal(next);
1502
1650
  onValueChange?.(next);
1503
1651
  },
1504
1652
  [isControlled, onValueChange]
1505
1653
  );
1506
- useEffect5(() => {
1654
+ const [dictateError, setDictateError] = useState8(null);
1655
+ const handleDictated = useCallback3(
1656
+ (audio) => {
1657
+ setDictateError(null);
1658
+ onDictate?.(audio);
1659
+ },
1660
+ [onDictate]
1661
+ );
1662
+ const handleDictateError = useCallback3(
1663
+ (message) => {
1664
+ setDictateError(message);
1665
+ onDictateError?.(message);
1666
+ },
1667
+ [onDictateError]
1668
+ );
1669
+ const dictation = useDictation({ onDictate: handleDictated, onError: handleDictateError });
1670
+ useEffect6(() => {
1507
1671
  const el = textareaRef.current;
1508
1672
  if (!el) return;
1509
1673
  el.style.height = "auto";
1510
1674
  el.style.height = `${Math.min(el.scrollHeight, MAX_HEIGHT)}px`;
1511
1675
  }, [text]);
1512
- const prevSeedRef = useRef5(null);
1513
- const pendingCaretRef = useRef5(null);
1514
- useEffect5(() => {
1676
+ const prevSeedRef = useRef6(null);
1677
+ const pendingCaretRef = useRef6(null);
1678
+ useEffect6(() => {
1515
1679
  const prev = prevSeedRef.current;
1516
1680
  prevSeedRef.current = seed ?? null;
1517
1681
  if (seed == null || seed === prev || isControlled) return;
@@ -1525,7 +1689,7 @@ function ChatComposer({
1525
1689
  pendingCaretRef.current = seed;
1526
1690
  }
1527
1691
  }, [seed, setText, onSeedApplied, isControlled]);
1528
- useEffect5(() => {
1692
+ useEffect6(() => {
1529
1693
  if (pendingCaretRef.current == null || pendingCaretRef.current !== text)
1530
1694
  return;
1531
1695
  pendingCaretRef.current = null;
@@ -1534,8 +1698,8 @@ function ChatComposer({
1534
1698
  el.focus();
1535
1699
  el.setSelectionRange(text.length, text.length);
1536
1700
  }, [text]);
1537
- const restoreCaretRef = useRef5(null);
1538
- useEffect5(() => {
1701
+ const restoreCaretRef = useRef6(null);
1702
+ useEffect6(() => {
1539
1703
  const pending = restoreCaretRef.current;
1540
1704
  if (!pending || pending.text !== text) return;
1541
1705
  restoreCaretRef.current = null;
@@ -1546,7 +1710,7 @@ function ChatComposer({
1546
1710
  const end = Math.min(pending.end, text.length);
1547
1711
  el.setSelectionRange(start, end);
1548
1712
  }, [text]);
1549
- useEffect5(() => {
1713
+ useEffect6(() => {
1550
1714
  if (!focusShortcut || disabled) return;
1551
1715
  function onKeyDown(e) {
1552
1716
  if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === "l") {
@@ -1560,8 +1724,8 @@ function ChatComposer({
1560
1724
  const readyFileCount = pendingFiles.filter((f) => f.status === "ready").length;
1561
1725
  const hasSendable = text.trim().length > 0 || readyFileCount > 0;
1562
1726
  const canSend = hasSendable && !isStreaming && !disabled;
1563
- const [failedSend, setFailedSend] = useState7(null);
1564
- const failSend = useCallback2(
1727
+ const [failedSend, setFailedSend] = useState8(null);
1728
+ const failSend = useCallback3(
1565
1729
  (error, draft, trimmed, parts, caret) => {
1566
1730
  const message = sendFailureText(error, sendFailureMessage);
1567
1731
  const restored = textRef.current === "";
@@ -1580,7 +1744,7 @@ function ChatComposer({
1580
1744
  },
1581
1745
  [onSendFailed, sendFailureMessage, setText]
1582
1746
  );
1583
- const dispatchSend = useCallback2(
1747
+ const dispatchSend = useCallback3(
1584
1748
  (draft, trimmed, parts, caret) => {
1585
1749
  let outcome;
1586
1750
  try {
@@ -1602,7 +1766,7 @@ function ChatComposer({
1602
1766
  },
1603
1767
  [onSend, onSendParts, failSend]
1604
1768
  );
1605
- const send = useCallback2(() => {
1769
+ const send = useCallback3(() => {
1606
1770
  const trimmed = text.trim();
1607
1771
  if (isStreaming || disabled) return;
1608
1772
  const readyFiles = pendingFiles.filter((f) => f.status === "ready");
@@ -1615,15 +1779,90 @@ function ChatComposer({
1615
1779
  textRef.current = "";
1616
1780
  dispatchSend(text, trimmed, parts, caret);
1617
1781
  }, [text, isStreaming, disabled, onSendParts, pendingFiles, setText, dispatchSend]);
1618
- const retryFailedSend = useCallback2(() => {
1782
+ const retryFailedSend = useCallback3(() => {
1619
1783
  const failure = failedSend;
1620
1784
  if (!failure || isStreaming || disabled) return;
1621
1785
  setFailedSend(null);
1622
1786
  const caret = { start: failure.text.length, end: failure.text.length };
1623
1787
  dispatchSend(failure.text, failure.trimmed, failure.parts, caret);
1624
1788
  }, [failedSend, isStreaming, disabled, dispatchSend]);
1789
+ const slashPanelRef = useRef6(null);
1790
+ const cardRef = useRef6(null);
1791
+ const slashListId = useId();
1792
+ const [slashActive, setSlashActive] = useState8(0);
1793
+ const [slashDismissedFor, setSlashDismissedFor] = useState8(null);
1794
+ const slashToken = slashCommands && slashCommands.length > 0 ? /^\/(\S*)$/.exec(text)?.[1] : void 0;
1795
+ const slashOpen = slashToken !== void 0 && text !== slashDismissedFor;
1796
+ const slashItems = useMemo3(
1797
+ () => (slashCommands ?? []).map((command) => ({
1798
+ id: command.name,
1799
+ group: "Commands",
1800
+ label: `/${command.name}`,
1801
+ description: command.description,
1802
+ keywords: [command.name, command.description]
1803
+ })),
1804
+ [slashCommands]
1805
+ );
1806
+ const slashFiltered = useMemo3(
1807
+ () => slashToken === void 0 ? [] : filterCommandPaletteItems(slashItems, slashToken),
1808
+ [slashItems, slashToken]
1809
+ );
1810
+ const slashActiveIndex = slashFiltered.length === 0 ? 0 : Math.min(slashActive, slashFiltered.length - 1);
1811
+ useEffect6(() => {
1812
+ setSlashActive(0);
1813
+ }, [slashToken]);
1814
+ useEffect6(() => {
1815
+ if (!slashOpen) return;
1816
+ document.getElementById(`${slashListId}-${slashActiveIndex}`)?.scrollIntoView?.({ block: "nearest" });
1817
+ }, [slashOpen, slashActiveIndex, slashListId]);
1818
+ useEffect6(() => {
1819
+ if (!slashOpen) return;
1820
+ function onMouseDown(e) {
1821
+ const target = e.target;
1822
+ if (cardRef.current?.contains(target)) return;
1823
+ if (slashPanelRef.current?.contains(target)) return;
1824
+ setSlashDismissedFor(textRef.current);
1825
+ }
1826
+ document.addEventListener("mousedown", onMouseDown);
1827
+ return () => document.removeEventListener("mousedown", onMouseDown);
1828
+ }, [slashOpen]);
1829
+ const pickSlash = useCallback3(
1830
+ (name) => {
1831
+ const command = slashCommands?.find((c) => c.name === name);
1832
+ setText("");
1833
+ setSlashDismissedFor(null);
1834
+ command?.run();
1835
+ },
1836
+ [slashCommands, setText]
1837
+ );
1625
1838
  const handleKeyDown = (e) => {
1626
1839
  if (e.nativeEvent.isComposing) return;
1840
+ if (slashOpen) {
1841
+ if (e.key === "ArrowDown") {
1842
+ e.preventDefault();
1843
+ if (slashFiltered.length > 0) setSlashActive((slashActiveIndex + 1) % slashFiltered.length);
1844
+ return;
1845
+ }
1846
+ if (e.key === "ArrowUp") {
1847
+ e.preventDefault();
1848
+ if (slashFiltered.length > 0)
1849
+ setSlashActive((slashActiveIndex - 1 + slashFiltered.length) % slashFiltered.length);
1850
+ return;
1851
+ }
1852
+ if (e.key === "Enter" && !e.shiftKey || e.key === "Tab") {
1853
+ const item = slashFiltered[slashActiveIndex];
1854
+ if (item) {
1855
+ e.preventDefault();
1856
+ pickSlash(item.id);
1857
+ return;
1858
+ }
1859
+ }
1860
+ if (e.key === "Escape") {
1861
+ e.preventDefault();
1862
+ setSlashDismissedFor(text);
1863
+ return;
1864
+ }
1865
+ }
1627
1866
  if (e.key === "Enter" && !e.shiftKey) {
1628
1867
  e.preventDefault();
1629
1868
  send();
@@ -1637,13 +1876,13 @@ function ChatComposer({
1637
1876
  if (e.target.files?.length) (onAttachFolder ?? onAttach)?.(e.target.files);
1638
1877
  e.target.value = "";
1639
1878
  };
1640
- const handleDragEnter = useCallback2((e) => {
1879
+ const handleDragEnter = useCallback3((e) => {
1641
1880
  e.preventDefault();
1642
1881
  e.stopPropagation();
1643
1882
  dragDepth.current++;
1644
1883
  if (e.dataTransfer?.types.includes("Files")) setDragOver(true);
1645
1884
  }, []);
1646
- const handleDragLeave = useCallback2((e) => {
1885
+ const handleDragLeave = useCallback3((e) => {
1647
1886
  e.preventDefault();
1648
1887
  e.stopPropagation();
1649
1888
  dragDepth.current--;
@@ -1652,12 +1891,12 @@ function ChatComposer({
1652
1891
  setDragOver(false);
1653
1892
  }
1654
1893
  }, []);
1655
- const handleDragOver = useCallback2((e) => {
1894
+ const handleDragOver = useCallback3((e) => {
1656
1895
  e.preventDefault();
1657
1896
  e.stopPropagation();
1658
1897
  if (e.dataTransfer) e.dataTransfer.dropEffect = "copy";
1659
1898
  }, []);
1660
- const handleDrop = useCallback2(
1899
+ const handleDrop = useCallback3(
1661
1900
  (e) => {
1662
1901
  e.preventDefault();
1663
1902
  e.stopPropagation();
@@ -1687,6 +1926,27 @@ function ChatComposer({
1687
1926
  /* @__PURE__ */ jsx7("p", { className: "mt-0.5 text-xs text-muted-foreground", children: dropDescription })
1688
1927
  ] }) }),
1689
1928
  showAbove && /* @__PURE__ */ jsx7("div", { className: "mb-1.5 flex flex-wrap items-center gap-1.5 px-1", children: controls }),
1929
+ dictateError && /* @__PURE__ */ jsxs5(
1930
+ "div",
1931
+ {
1932
+ role: "alert",
1933
+ "data-testid": "composer-dictate-error",
1934
+ 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",
1935
+ children: [
1936
+ /* @__PURE__ */ jsx7("span", { className: "min-w-0 flex-1", children: dictateError }),
1937
+ /* @__PURE__ */ jsx7(
1938
+ "button",
1939
+ {
1940
+ type: "button",
1941
+ "aria-label": "Dismiss dictation error",
1942
+ onClick: () => setDictateError(null),
1943
+ className: "shrink-0 font-medium underline-offset-2 hover:underline focus:outline-none focus-visible:ring-2 focus-visible:ring-destructive/50",
1944
+ children: "Dismiss"
1945
+ }
1946
+ )
1947
+ ]
1948
+ }
1949
+ ),
1690
1950
  failedSend && /* @__PURE__ */ jsxs5(
1691
1951
  "div",
1692
1952
  {
@@ -1761,6 +2021,7 @@ function ChatComposer({
1761
2021
  /* @__PURE__ */ jsxs5(
1762
2022
  "div",
1763
2023
  {
2024
+ ref: cardRef,
1764
2025
  "data-testid": "composer-card",
1765
2026
  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
2027
  children: [
@@ -1827,6 +2088,41 @@ function ChatComposer({
1827
2088
  children: showInline && controls
1828
2089
  }
1829
2090
  ),
2091
+ onDictate && dictation.supported ? dictation.recording ? /* @__PURE__ */ jsxs5("div", { className: "flex shrink-0 items-center gap-1.5", children: [
2092
+ /* @__PURE__ */ jsx7("span", { "aria-hidden": "true", className: "h-2 w-2 animate-pulse rounded-full bg-destructive" }),
2093
+ /* @__PURE__ */ jsx7(
2094
+ "span",
2095
+ {
2096
+ "aria-hidden": "true",
2097
+ "data-testid": "composer-dictate-elapsed",
2098
+ className: "text-xs tabular-nums text-muted-foreground",
2099
+ children: formatDictationElapsed(dictation.elapsedSeconds)
2100
+ }
2101
+ ),
2102
+ /* @__PURE__ */ jsx7("span", { role: "status", className: "sr-only", children: "Recording" }),
2103
+ /* @__PURE__ */ jsx7(
2104
+ "button",
2105
+ {
2106
+ type: "button",
2107
+ onClick: dictation.stop,
2108
+ "aria-label": "Stop dictation",
2109
+ title: "Stop dictation",
2110
+ className: "shrink-0 rounded-lg p-2 text-destructive transition hover:bg-accent focus:outline-none focus-visible:ring-2 focus-visible:ring-ring",
2111
+ children: /* @__PURE__ */ jsx7(StopGlyph, { className: "h-4 w-4" })
2112
+ }
2113
+ )
2114
+ ] }) : /* @__PURE__ */ jsx7(
2115
+ "button",
2116
+ {
2117
+ type: "button",
2118
+ onClick: dictation.start,
2119
+ disabled,
2120
+ "aria-label": "Dictate message",
2121
+ title: "Dictate message",
2122
+ 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",
2123
+ children: /* @__PURE__ */ jsx7(MicGlyph, { className: "h-4 w-4" })
2124
+ }
2125
+ ) : null,
1830
2126
  isStreaming ? sendVariant === "icon" ? /* @__PURE__ */ jsx7(
1831
2127
  "button",
1832
2128
  {
@@ -1878,6 +2174,38 @@ function ChatComposer({
1878
2174
  ]
1879
2175
  }
1880
2176
  ),
2177
+ /* @__PURE__ */ jsxs5(
2178
+ PopoverSurface,
2179
+ {
2180
+ open: slashOpen,
2181
+ id: slashListId,
2182
+ role: "listbox",
2183
+ triggerRef: textareaRef,
2184
+ panelRef: slashPanelRef,
2185
+ className: `w-80 overflow-y-auto rounded-xl border border-card-edge bg-popover p-1 ${OVERLAY_SHADOW}`,
2186
+ children: [
2187
+ slashFiltered.length === 0 && /* @__PURE__ */ jsx7("div", { className: "px-3 py-4 text-center text-sm text-muted-foreground", children: "No matching commands" }),
2188
+ slashFiltered.map((item, index) => /* @__PURE__ */ jsxs5(
2189
+ "button",
2190
+ {
2191
+ type: "button",
2192
+ role: "option",
2193
+ "aria-selected": index === slashActiveIndex,
2194
+ id: `${slashListId}-${index}`,
2195
+ onMouseDown: (e) => e.preventDefault(),
2196
+ onMouseMove: () => setSlashActive(index),
2197
+ onClick: () => pickSlash(item.id),
2198
+ 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"}`,
2199
+ children: [
2200
+ /* @__PURE__ */ jsx7("span", { className: "shrink-0 font-medium text-foreground", children: item.label }),
2201
+ /* @__PURE__ */ jsx7("span", { className: "truncate text-xs text-muted-foreground", children: item.description })
2202
+ ]
2203
+ },
2204
+ item.id
2205
+ ))
2206
+ ]
2207
+ }
2208
+ ),
1881
2209
  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
2210
  /* @__PURE__ */ jsx7("kbd", { className: "rounded border border-border bg-background px-1 py-0.5 text-xs", children: IS_APPLE_PLATFORM ? "Cmd" : "Ctrl" }),
1883
2211
  /* @__PURE__ */ jsx7("kbd", { className: "ml-0.5 rounded border border-border bg-background px-1 py-0.5 text-xs", children: "L" }),
@@ -1889,7 +2217,7 @@ function ChatComposer({
1889
2217
  }
1890
2218
 
1891
2219
  // src/web-react/durable-plan-flow.ts
1892
- import { useCallback as useCallback3, useEffect as useEffect6, useRef as useRef6, useState as useState8 } from "react";
2220
+ import { useCallback as useCallback4, useEffect as useEffect7, useRef as useRef7, useState as useState9 } from "react";
1893
2221
  var DurablePlanClientError = class extends Error {
1894
2222
  constructor(message, status, code, currentPlan) {
1895
2223
  super(message);
@@ -1976,14 +2304,14 @@ function createDurablePlanDecisionClient(options) {
1976
2304
  };
1977
2305
  }
1978
2306
  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) => {
2307
+ const [plan, setPlan] = useState9(options.plan);
2308
+ const [deciding, setDeciding] = useState9(null);
2309
+ const [restoring, setRestoring] = useState9(false);
2310
+ const [error, setError] = useState9(null);
2311
+ const attachments = useRef7(/* @__PURE__ */ new Map());
2312
+ const decisionInFlight = useRef7(false);
2313
+ useEffect7(() => setPlan(options.plan), [options.plan]);
2314
+ const apply = useCallback4(async (result) => {
1987
2315
  setPlan(result.plan);
1988
2316
  options.onUpdated?.(result.plan);
1989
2317
  const receipt = result.followUp;
@@ -1996,7 +2324,7 @@ function useDurablePlanFlow(options) {
1996
2324
  }
1997
2325
  await pending;
1998
2326
  }, [options.attachFollowUp, options.onUpdated]);
1999
- const decide = useCallback3(async (decision, feedback) => {
2327
+ const decide = useCallback4(async (decision, feedback) => {
2000
2328
  if (decisionInFlight.current) return null;
2001
2329
  decisionInFlight.current = true;
2002
2330
  setDeciding(decision);
@@ -2022,7 +2350,7 @@ function useDurablePlanFlow(options) {
2022
2350
  setDeciding(null);
2023
2351
  }
2024
2352
  }, [apply, options.client, options.onUpdated, plan.planId, plan.revision]);
2025
- const restore = useCallback3(async () => {
2353
+ const restore = useCallback4(async () => {
2026
2354
  setRestoring(true);
2027
2355
  setError(null);
2028
2356
  try {
@@ -2151,7 +2479,7 @@ function createDurableInteractionAnswerSubmitter(options) {
2151
2479
  }
2152
2480
 
2153
2481
  // src/web-react/use-chat-interactions.ts
2154
- import { useCallback as useCallback4, useMemo as useMemo3, useState as useState9 } from "react";
2482
+ import { useCallback as useCallback5, useMemo as useMemo4, useState as useState10 } from "react";
2155
2483
  function hasPendingContentDuplicate(list, interaction) {
2156
2484
  if (interaction.status !== "pending") return false;
2157
2485
  const signature = questionInteractionContentSignature(interaction);
@@ -2230,34 +2558,34 @@ function hydrateChatInteractions(list, persisted) {
2230
2558
  return persisted.reduce(upsertChatInteraction, list);
2231
2559
  }
2232
2560
  function useChatInteractions(options = {}) {
2233
- const [interactions, setInteractions] = useState9([]);
2234
- const upsert = useCallback4((interaction) => {
2561
+ const [interactions, setInteractions] = useState10([]);
2562
+ const upsert = useCallback5((interaction) => {
2235
2563
  setInteractions((prev) => upsertChatInteraction(prev, interaction));
2236
2564
  }, []);
2237
- const applyCancel = useCallback4((cancel) => {
2565
+ const applyCancel = useCallback5((cancel) => {
2238
2566
  setInteractions((prev) => cancelChatInteraction(prev, cancel));
2239
2567
  }, []);
2240
- const markResolved = useCallback4((id, status, answers) => {
2568
+ const markResolved = useCallback5((id, status, answers) => {
2241
2569
  setInteractions((prev) => resolveChatInteraction(prev, id, status, answers));
2242
2570
  }, []);
2243
- const restore = useCallback4((outstanding, restoreOptions) => {
2571
+ const restore = useCallback5((outstanding, restoreOptions) => {
2244
2572
  setInteractions((prev) => restoreChatInteractions(prev, outstanding, {
2245
2573
  mode: restoreOptions?.mode ?? options.mode
2246
2574
  }));
2247
2575
  }, [options.mode]);
2248
- const hydrate = useCallback4((persisted) => {
2576
+ const hydrate = useCallback5((persisted) => {
2249
2577
  setInteractions((prev) => hydrateChatInteractions(prev, persisted));
2250
2578
  }, []);
2251
- const terminalizePending = useCallback4((status) => {
2579
+ const terminalizePending = useCallback5((status) => {
2252
2580
  setInteractions((prev) => terminalizePendingChatInteractions(prev, status));
2253
2581
  }, []);
2254
- const reset = useCallback4(() => setInteractions([]), []);
2255
- const pending = useMemo3(() => interactions.filter((item) => item.status === "pending"), [interactions]);
2582
+ const reset = useCallback5(() => setInteractions([]), []);
2583
+ const pending = useMemo4(() => interactions.filter((item) => item.status === "pending"), [interactions]);
2256
2584
  return { interactions, pending, upsert, applyCancel, markResolved, restore, hydrate, terminalizePending, reset };
2257
2585
  }
2258
2586
 
2259
2587
  // src/web-react/use-file-mentions.ts
2260
- import { useCallback as useCallback5, useMemo as useMemo4, useRef as useRef7, useState as useState10 } from "react";
2588
+ import { useCallback as useCallback6, useMemo as useMemo5, useRef as useRef8, useState as useState11 } from "react";
2261
2589
  var FILE_MENTION_KIND = "file";
2262
2590
  function toMentionItem(file) {
2263
2591
  return { id: file.path, label: file.name, detail: file.path, kind: FILE_MENTION_KIND };
@@ -2315,12 +2643,12 @@ function useFileMentions(options) {
2315
2643
  emptyText = DEFAULT_MENTION_EMPTY_TEXT
2316
2644
  } = options;
2317
2645
  const fetchImpl = options.fetchImpl ?? fetch;
2318
- const [state, setState] = useState10({ kind: "idle" });
2319
- const stateRef = useRef7(state);
2646
+ const [state, setState] = useState11({ kind: "idle" });
2647
+ const stateRef = useRef8(state);
2320
2648
  stateRef.current = state;
2321
- const inFlightRef = useRef7(null);
2322
- const [mentions, setMentions] = useState10([]);
2323
- const load = useCallback5(() => {
2649
+ const inFlightRef = useRef8(null);
2650
+ const [mentions, setMentions] = useState11([]);
2651
+ const load = useCallback6(() => {
2324
2652
  if (inFlightRef.current) return inFlightRef.current;
2325
2653
  if (stateRef.current.kind === "idle") {
2326
2654
  stateRef.current = { kind: "loading" };
@@ -2352,10 +2680,10 @@ function useFileMentions(options) {
2352
2680
  inFlightRef.current = attempt;
2353
2681
  return attempt;
2354
2682
  }, [fetchImpl, indexUrl]);
2355
- const refresh = useCallback5(async () => {
2683
+ const refresh = useCallback6(async () => {
2356
2684
  await load();
2357
2685
  }, [load]);
2358
- const fetchItems = useCallback5(
2686
+ const fetchItems = useCallback6(
2359
2687
  async (query) => {
2360
2688
  let current = stateRef.current;
2361
2689
  if (current.kind === "idle" || current.kind === "loading") {
@@ -2370,11 +2698,11 @@ function useFileMentions(options) {
2370
2698
  },
2371
2699
  [load, limit, refreshAfterMs]
2372
2700
  );
2373
- const onMentionsChange = useCallback5((items) => {
2701
+ const onMentionsChange = useCallback6((items) => {
2374
2702
  setMentions(items.filter((item) => item.kind === void 0 || item.kind === FILE_MENTION_KIND).map(toFileMention));
2375
2703
  }, []);
2376
- const clearMentions = useCallback5(() => setMentions([]), []);
2377
- const mention = useMemo4(
2704
+ const clearMentions = useCallback6(() => setMentions([]), []);
2705
+ const mention = useMemo5(
2378
2706
  () => ({
2379
2707
  fetchItems,
2380
2708
  onMentionsChange,
@@ -2428,7 +2756,7 @@ function segmentMentionContent(content, parts) {
2428
2756
  }
2429
2757
 
2430
2758
  // src/web-react/mission-activity.tsx
2431
- import { useCallback as useCallback6, useEffect as useEffect7, useState as useState11 } from "react";
2759
+ import { useCallback as useCallback7, useEffect as useEffect8, useState as useState12 } from "react";
2432
2760
  import { Fragment as Fragment3, jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
2433
2761
  var LIVE_STATUSES = /* @__PURE__ */ new Set(["pending", "running"]);
2434
2762
  var OK_STATUSES = /* @__PURE__ */ new Set(["completed", "done", "succeeded"]);
@@ -2488,8 +2816,8 @@ function CopyGlyph({ className }) {
2488
2816
  ] });
2489
2817
  }
2490
2818
  function TraceIdCopy({ traceId }) {
2491
- const [copied, setCopied] = useState11(false);
2492
- const copy = useCallback6(() => {
2819
+ const [copied, setCopied] = useState12(false);
2820
+ const copy = useCallback7(() => {
2493
2821
  void navigator.clipboard?.writeText(traceId).then(
2494
2822
  () => {
2495
2823
  setCopied(true);
@@ -2581,7 +2909,7 @@ function LaneRow({ run, staggerIndex }) {
2581
2909
  ] });
2582
2910
  }
2583
2911
  function MissionActivityLane({ activity, startedAt, nowMs }) {
2584
- const [expanded, setExpanded] = useState11(false);
2912
+ const [expanded, setExpanded] = useState12(false);
2585
2913
  if (activity.length === 0) return null;
2586
2914
  return /* @__PURE__ */ jsxs6("div", { className: "mt-1 border-l border-border pl-3", children: [
2587
2915
  activity.map((run, index) => /* @__PURE__ */ jsx8(LaneRow, { run, staggerIndex: index }, run.taskId)),
@@ -2614,7 +2942,7 @@ function ActivityRow({
2614
2942
  staggerIndex
2615
2943
  }) {
2616
2944
  const arrival = useArrivalStyle(staggerIndex);
2617
- const [open, setOpen] = useState11(false);
2945
+ const [open, setOpen] = useState12(false);
2618
2946
  const tone = activityTone(record.status);
2619
2947
  const cost = formatActivityCost(record.costUsd);
2620
2948
  const duration = formatActivityDuration(record.durationMs);
@@ -2654,11 +2982,11 @@ function ActivityRow({
2654
2982
  ] });
2655
2983
  }
2656
2984
  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(
2985
+ const [rows, setRows] = useState12([]);
2986
+ const [cursor, setCursor] = useState12(void 0);
2987
+ const [status, setStatus] = useState12("loading");
2988
+ const [error, setError] = useState12(null);
2989
+ const load = useCallback7(
2662
2990
  async (from) => {
2663
2991
  setStatus("loading");
2664
2992
  setError(null);
@@ -2674,7 +3002,7 @@ function AgentActivityPanel({ fetchActivity, renderMissionRef, title = "Agent ac
2674
3002
  },
2675
3003
  [fetchActivity]
2676
3004
  );
2677
- useEffect7(() => {
3005
+ useEffect8(() => {
2678
3006
  void load();
2679
3007
  }, [load]);
2680
3008
  const loading = status === "loading";
@@ -2711,7 +3039,7 @@ function AgentActivityPanel({ fetchActivity, renderMissionRef, title = "Agent ac
2711
3039
  }
2712
3040
 
2713
3041
  // src/web-react/provenance.tsx
2714
- import { useCallback as useCallback7, useEffect as useEffect8, useId, useRef as useRef8, useState as useState12 } from "react";
3042
+ import { useCallback as useCallback8, useEffect as useEffect9, useId as useId2, useRef as useRef9, useState as useState13 } from "react";
2715
3043
 
2716
3044
  // src/web-react/provenance-model.ts
2717
3045
  var PROVENANCE_BASES = ["extracted", "entered", "computed", "asserted"];
@@ -3005,10 +3333,10 @@ function ProvenanceValue({
3005
3333
  missingValueLabel = DEFAULT_MISSING_VALUE_LABEL,
3006
3334
  className
3007
3335
  }) {
3008
- const [open, setOpen] = useState12(defaultOpen);
3009
- const triggerRef = useRef8(null);
3010
- const rootRef = useRef8(null);
3011
- const panelId = useId();
3336
+ const [open, setOpen] = useState13(defaultOpen);
3337
+ const triggerRef = useRef9(null);
3338
+ const rootRef = useRef9(null);
3339
+ const panelId = useId2();
3012
3340
  const standing = rollUpProvenanceStanding(record, confidencePolicy);
3013
3341
  const basisMeta = provenanceBasisMeta(record.basis);
3014
3342
  const standingMeta = provenanceStandingMeta(standing);
@@ -3017,7 +3345,7 @@ function ProvenanceValue({
3017
3345
  const sources = record.sources ?? [];
3018
3346
  const inputs = record.inputs ?? [];
3019
3347
  const hasValue = record.display.trim() !== "";
3020
- const onKeyDown = useCallback7(
3348
+ const onKeyDown = useCallback8(
3021
3349
  (event) => {
3022
3350
  if (event.key !== "Escape" || !open) return;
3023
3351
  event.stopPropagation();
@@ -3026,11 +3354,11 @@ function ProvenanceValue({
3026
3354
  },
3027
3355
  [open]
3028
3356
  );
3029
- const onToggle = useCallback7(() => {
3357
+ const onToggle = useCallback8(() => {
3030
3358
  if (!open) closeTrailsOutside(rootRef.current);
3031
3359
  setOpen(!open);
3032
3360
  }, [open]);
3033
- useEffect8(() => {
3361
+ useEffect9(() => {
3034
3362
  const root = rootRef.current;
3035
3363
  if (!open || root === null) return;
3036
3364
  const entry = { root, close: () => setOpen(false) };
@@ -3286,12 +3614,12 @@ function SeatPaywall({
3286
3614
 
3287
3615
  // src/web-react/session-history.tsx
3288
3616
  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
3617
+ useCallback as useCallback9,
3618
+ useEffect as useEffect10,
3619
+ useId as useId3,
3620
+ useMemo as useMemo6,
3621
+ useRef as useRef10,
3622
+ useState as useState14
3295
3623
  } from "react";
3296
3624
  import { Fragment as Fragment5, jsx as jsx11, jsxs as jsxs9 } from "react/jsx-runtime";
3297
3625
  function rethrowAsync(error) {
@@ -3300,13 +3628,13 @@ function rethrowAsync(error) {
3300
3628
  });
3301
3629
  }
3302
3630
  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(() => {
3631
+ const [sentinel, setSentinel] = useState14(null);
3632
+ const sentinelRef = useCallback9((node) => setSentinel(node), []);
3633
+ const onLoadMoreRef = useRef10(onLoadMore);
3634
+ useEffect10(() => {
3307
3635
  onLoadMoreRef.current = onLoadMore;
3308
3636
  }, [onLoadMore]);
3309
- useEffect9(() => {
3637
+ useEffect10(() => {
3310
3638
  if (!sentinel || !enabled) return;
3311
3639
  if (typeof IntersectionObserver === "undefined") return;
3312
3640
  const observer = new IntersectionObserver(
@@ -3335,24 +3663,24 @@ function useSessionHistory({
3335
3663
  initialPage,
3336
3664
  defaultSort = "newest"
3337
3665
  }) {
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);
3666
+ const [items, setItems] = useState14(initialPage.items);
3667
+ const [nextCursor, setNextCursor] = useState14(initialPage.nextCursor ?? null);
3668
+ const [phase, setPhase] = useState14("idle");
3669
+ const [reloadKey, setReloadKey] = useState14(0);
3670
+ const seqRef = useRef10(0);
3671
+ const resetAbortRef = useRef10(null);
3672
+ const loadMoreAbortRef = useRef10(null);
3673
+ const loadingMoreRef = useRef10(false);
3674
+ const lastOpRef = useRef10("first");
3675
+ const nextCursorRef = useRef10(nextCursor);
3348
3676
  nextCursorRef.current = nextCursor;
3349
- const viewRef = useRef9({ q, sort, fetchPage });
3677
+ const viewRef = useRef10({ q, sort, fetchPage });
3350
3678
  viewRef.current = { q, sort, fetchPage };
3351
- const seedRef = useRef9(initialPage);
3679
+ const seedRef = useRef10(initialPage);
3352
3680
  seedRef.current = initialPage;
3353
3681
  const isDefaultView = q === "" && sort === defaultSort;
3354
- const seedKey = useMemo5(() => seedSignature(initialPage), [initialPage]);
3355
- useEffect9(() => {
3682
+ const seedKey = useMemo6(() => seedSignature(initialPage), [initialPage]);
3683
+ useEffect10(() => {
3356
3684
  resetAbortRef.current?.abort();
3357
3685
  loadMoreAbortRef.current?.abort();
3358
3686
  loadingMoreRef.current = false;
@@ -3383,7 +3711,7 @@ function useSessionHistory({
3383
3711
  })();
3384
3712
  return () => controller.abort();
3385
3713
  }, [q, sort, seedKey, isDefaultView, reloadKey]);
3386
- const loadMore = useCallback8(() => {
3714
+ const loadMore = useCallback9(() => {
3387
3715
  const cursor = nextCursorRef.current;
3388
3716
  if (!cursor || loadingMoreRef.current) return;
3389
3717
  const { q: currentQ, sort: currentSort, fetchPage: currentFetch } = viewRef.current;
@@ -3408,14 +3736,14 @@ function useSessionHistory({
3408
3736
  }
3409
3737
  })();
3410
3738
  }, []);
3411
- const retry = useCallback8(() => {
3739
+ const retry = useCallback9(() => {
3412
3740
  if (lastOpRef.current === "more") loadMore();
3413
3741
  else setReloadKey((key) => key + 1);
3414
3742
  }, [loadMore]);
3415
- const reload = useCallback8(() => {
3743
+ const reload = useCallback9(() => {
3416
3744
  setReloadKey((key) => key + 1);
3417
3745
  }, []);
3418
- useEffect9(
3746
+ useEffect10(
3419
3747
  () => () => {
3420
3748
  resetAbortRef.current?.abort();
3421
3749
  loadMoreAbortRef.current?.abort();
@@ -3456,21 +3784,21 @@ function useSessionActions({
3456
3784
  labels
3457
3785
  }) {
3458
3786
  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) => {
3787
+ const [renameTarget, setRenameTarget] = useState14(null);
3788
+ const [renameValue, setRenameValue] = useState14("");
3789
+ const [deleteTarget, setDeleteTarget] = useState14(null);
3790
+ const [busy, setBusy] = useState14(false);
3791
+ const [error, setError] = useState14(null);
3792
+ const openRename = useCallback9((session) => {
3465
3793
  setError(null);
3466
3794
  setRenameTarget(session);
3467
3795
  setRenameValue(session.title ?? "");
3468
3796
  }, []);
3469
- const openDelete = useCallback8((session) => {
3797
+ const openDelete = useCallback9((session) => {
3470
3798
  setError(null);
3471
3799
  setDeleteTarget(session);
3472
3800
  }, []);
3473
- const submitRename = useCallback8(async () => {
3801
+ const submitRename = useCallback9(async () => {
3474
3802
  if (!renameTarget) return;
3475
3803
  const title = renameValue.trim();
3476
3804
  if (!title || title === renameTarget.title) {
@@ -3492,7 +3820,7 @@ function useSessionActions({
3492
3820
  setBusy(false);
3493
3821
  }
3494
3822
  }, [renameTarget, renameValue, renameSession, notify, onChanged, text.renamed, text.renameFailed]);
3495
- const confirmDelete = useCallback8(async () => {
3823
+ const confirmDelete = useCallback9(async () => {
3496
3824
  if (!deleteTarget) return;
3497
3825
  const deletingCurrent = currentSessionId != null && deleteTarget.id === currentSessionId;
3498
3826
  setBusy(true);
@@ -3587,7 +3915,7 @@ function SessionDialog({
3587
3915
  busy,
3588
3916
  error
3589
3917
  }) {
3590
- useEffect9(() => {
3918
+ useEffect10(() => {
3591
3919
  const onKey = (e) => {
3592
3920
  if (e.key === "Escape" && !busy) onClose();
3593
3921
  };
@@ -3677,7 +4005,7 @@ function SessionHistoryPanel({
3677
4005
  contentWidth = "reading",
3678
4006
  className
3679
4007
  }) {
3680
- const scrollRef = useRef9(null);
4008
+ const scrollRef = useRef10(null);
3681
4009
  const sentinelRef = useInfiniteScroll(history.loadMore, {
3682
4010
  enabled: history.hasMore && !history.isLoadingMore && !history.isError,
3683
4011
  root: scrollRef,
@@ -3686,15 +4014,15 @@ function SessionHistoryPanel({
3686
4014
  const searchTerm = query.trim();
3687
4015
  const isSearching = searchTerm.length > 0;
3688
4016
  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(() => {
4017
+ const [selectedIds, setSelectedIds] = useState14(/* @__PURE__ */ new Set());
4018
+ const [ageDays, setAgeDays] = useState14("30");
4019
+ const [bulkTarget, setBulkTarget] = useState14(null);
4020
+ const [bulkBusy, setBulkBusy] = useState14(false);
4021
+ const [bulkError, setBulkError] = useState14(null);
4022
+ useEffect10(() => {
3695
4023
  setSelectedIds(/* @__PURE__ */ new Set());
3696
4024
  }, [searchTerm, sort]);
3697
- useEffect9(() => {
4025
+ useEffect10(() => {
3698
4026
  const visible = new Set(history.items.map((item) => item.id));
3699
4027
  setSelectedIds((current) => {
3700
4028
  const next = new Set([...current].filter((id) => visible.has(id)));
@@ -3705,7 +4033,7 @@ function SessionHistoryPanel({
3705
4033
  const allVisibleSelected = history.items.length > 0 && history.items.every((item) => selectedIds.has(item.id));
3706
4034
  const parsedAgeDays = Number(ageDays);
3707
4035
  const validAgeDays = Number.isInteger(parsedAgeDays) && parsedAgeDays >= 1 && parsedAgeDays <= 36500;
3708
- const openBulkAction = useCallback8((action) => {
4036
+ const openBulkAction = useCallback9((action) => {
3709
4037
  const verb = deleteLabel.toLowerCase();
3710
4038
  if (action.kind === "selected") {
3711
4039
  setBulkTarget({
@@ -3722,7 +4050,7 @@ function SessionHistoryPanel({
3722
4050
  body: "This applies to every matching session in this workspace, including sessions not currently loaded in this list."
3723
4051
  });
3724
4052
  }, [deleteLabel]);
3725
- const confirmBulkAction = useCallback8(async () => {
4053
+ const confirmBulkAction = useCallback9(async () => {
3726
4054
  if (!bulkTarget || !onBulkAction) return;
3727
4055
  setBulkBusy(true);
3728
4056
  setBulkError(null);
@@ -3970,8 +4298,8 @@ function SessionRow({
3970
4298
  selected,
3971
4299
  onSelectedChange
3972
4300
  }) {
3973
- const [menuOpen, setMenuOpen] = useState13(false);
3974
- const panelId = useId2();
4301
+ const [menuOpen, setMenuOpen] = useState14(false);
4302
+ const panelId = useId3();
3975
4303
  const { containerRef, triggerRef, panelRef, triggerProps } = usePopover(menuOpen, setMenuOpen);
3976
4304
  const extras = extraActions?.(session) ?? [];
3977
4305
  const hasMenu = Boolean(onRename) || Boolean(onDelete) || extras.length > 0;
@@ -4074,12 +4402,12 @@ function SessionRow({
4074
4402
  import {
4075
4403
  Fragment as Fragment6,
4076
4404
  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
4405
+ useCallback as useCallback10,
4406
+ useEffect as useEffect11,
4407
+ useId as useId4,
4408
+ useMemo as useMemo7,
4409
+ useRef as useRef11,
4410
+ useState as useState15
4083
4411
  } from "react";
4084
4412
 
4085
4413
  // src/web-react/record-grid-model.ts
@@ -4098,6 +4426,32 @@ function isRecordGridCellApplicable(column, values) {
4098
4426
  function sameRecordGridValue(a, b) {
4099
4427
  return Object.is(a ?? null, b ?? null);
4100
4428
  }
4429
+ function diffRecordGridProposal(rows, proposal) {
4430
+ const updates = proposal.updates ?? {};
4431
+ const removals = new Set(proposal.removals ?? []);
4432
+ const additions = proposal.additions ?? [];
4433
+ const liveIds = new Set(rows.map((row) => row.id));
4434
+ const diffs = [];
4435
+ for (const row of rows) {
4436
+ if (removals.has(row.id)) {
4437
+ diffs.push({ rowId: row.id, kind: "removed", cells: [], row });
4438
+ continue;
4439
+ }
4440
+ const patch = updates[row.id];
4441
+ if (patch === void 0) continue;
4442
+ const cells = [];
4443
+ for (const [columnId, after] of Object.entries(patch)) {
4444
+ const before = row.values[columnId] ?? null;
4445
+ if (!sameRecordGridValue(before, after)) cells.push({ columnId, before, after });
4446
+ }
4447
+ if (cells.length > 0) diffs.push({ rowId: row.id, kind: "changed", cells, row });
4448
+ }
4449
+ for (const row of additions) {
4450
+ if (liveIds.has(row.id)) continue;
4451
+ diffs.push({ rowId: row.id, kind: "added", cells: [], row });
4452
+ }
4453
+ return diffs;
4454
+ }
4101
4455
  var NUMERIC = /^[+-]?(?:\d+(?:\.\d+)?|\.\d+)$/;
4102
4456
  var ISO_DATE = /^\d{4}-\d{2}-\d{2}$/;
4103
4457
  function parseNumericText(raw) {
@@ -4456,6 +4810,11 @@ function RecordGrid({
4456
4810
  onCreate,
4457
4811
  onUpdate,
4458
4812
  onDelete,
4813
+ proposed,
4814
+ onAcceptRow,
4815
+ onRejectRow,
4816
+ onAcceptAll,
4817
+ onRejectAll,
4459
4818
  newRowDefaults,
4460
4819
  addLabel = "Add row",
4461
4820
  locale,
@@ -4463,40 +4822,57 @@ function RecordGrid({
4463
4822
  loadingRowCount = 3,
4464
4823
  className
4465
4824
  }) {
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) => {
4825
+ const fieldPrefix = useId4();
4826
+ const [overlay, setOverlay] = useState15(EMPTY_RECORD_GRID_OVERLAY);
4827
+ const [editing, setEditingState] = useState15(null);
4828
+ const [cellErrors, setCellErrors] = useState15({});
4829
+ const [rowErrors, setRowErrors] = useState15({});
4830
+ const [pendingRows, setPendingRows] = useState15({});
4831
+ const [focus, setFocus] = useState15(null);
4832
+ const [openSource, setOpenSource] = useState15(null);
4833
+ const [confirmDelete, setConfirmDelete] = useState15(null);
4834
+ const [adding, setAdding] = useState15(false);
4835
+ const [draft, setDraft] = useState15({ ...newRowDefaults ?? {} });
4836
+ const [draftErrors, setDraftErrors] = useState15({});
4837
+ const [draftError, setDraftError] = useState15(null);
4838
+ const [creating, setCreating] = useState15(false);
4839
+ const cellRefs = useRef11(/* @__PURE__ */ new Map());
4840
+ const settling = useRef11(false);
4841
+ const draftCounter = useRef11(0);
4842
+ const editingRef = useRef11(null);
4843
+ const setEditing = useCallback10((next) => {
4485
4844
  editingRef.current = next;
4486
4845
  setEditingState(next);
4487
4846
  }, []);
4488
4847
  const callerRows = state.status === "ready" || state.status === "empty" ? state.value : EMPTY_RECORD_GRID_ROWS;
4489
- useEffect10(() => {
4848
+ useEffect11(() => {
4490
4849
  setOverlay((current) => pruneRecordGridOverlay(callerRows, current));
4491
4850
  }, [callerRows]);
4492
- const visibleRows = useMemo6(() => projectRecordGridRows(callerRows, overlay), [callerRows, overlay]);
4493
- const activeFocus = useMemo6(() => {
4851
+ const visibleRows = useMemo7(() => projectRecordGridRows(callerRows, overlay), [callerRows, overlay]);
4852
+ const diffs = useMemo7(
4853
+ () => proposed === void 0 ? null : diffRecordGridProposal(visibleRows, proposed),
4854
+ [proposed, visibleRows]
4855
+ );
4856
+ const reviewing = diffs !== null && diffs.length > 0;
4857
+ const diffByRow = useMemo7(() => new Map((diffs ?? []).map((diff) => [diff.rowId, diff])), [diffs]);
4858
+ const diffCellByKey = useMemo7(() => {
4859
+ const map = /* @__PURE__ */ new Map();
4860
+ for (const diff of diffs ?? []) {
4861
+ for (const cell of diff.cells) map.set(cellKey(diff.rowId, cell.columnId), cell);
4862
+ }
4863
+ return map;
4864
+ }, [diffs]);
4865
+ const addedRows = useMemo7(
4866
+ () => (diffs ?? []).filter((diff) => diff.kind === "added").map((diff) => diff.row),
4867
+ [diffs]
4868
+ );
4869
+ const activeFocus = useMemo7(() => {
4494
4870
  if (focus === null) return null;
4495
4871
  if (!visibleRows.some((row) => row.id === focus.rowId)) return null;
4496
4872
  if (!columns.some((column) => column.id === focus.columnId)) return null;
4497
4873
  return focus;
4498
4874
  }, [columns, focus, visibleRows]);
4499
- const setCellError = useCallback9((key, message) => {
4875
+ const setCellError = useCallback10((key, message) => {
4500
4876
  setCellErrors((current) => {
4501
4877
  if (message === null) {
4502
4878
  if (!(key in current)) return current;
@@ -4508,7 +4884,7 @@ function RecordGrid({
4508
4884
  return { ...current, [key]: message };
4509
4885
  });
4510
4886
  }, []);
4511
- const setRowError = useCallback9((rowId, message) => {
4887
+ const setRowError = useCallback10((rowId, message) => {
4512
4888
  setRowErrors((current) => {
4513
4889
  if (message === null) {
4514
4890
  if (!(rowId in current)) return current;
@@ -4519,7 +4895,7 @@ function RecordGrid({
4519
4895
  return { ...current, [rowId]: message };
4520
4896
  });
4521
4897
  }, []);
4522
- const setRowPending = useCallback9((rowId, pending) => {
4898
+ const setRowPending = useCallback10((rowId, pending) => {
4523
4899
  setPendingRows((current) => {
4524
4900
  if (pending) return rowId in current ? current : { ...current, [rowId]: true };
4525
4901
  if (!(rowId in current)) return current;
@@ -4528,11 +4904,11 @@ function RecordGrid({
4528
4904
  return next;
4529
4905
  });
4530
4906
  }, []);
4531
- const focusCell = useCallback9((rowId, columnId) => {
4907
+ const focusCell = useCallback10((rowId, columnId) => {
4532
4908
  setFocus({ rowId, columnId });
4533
4909
  cellRefs.current.get(cellKey(rowId, columnId))?.focus();
4534
4910
  }, []);
4535
- const beginEdit = useCallback9(
4911
+ const beginEdit = useCallback10(
4536
4912
  (row, column) => {
4537
4913
  setCellError(cellKey(row.id, column.id), null);
4538
4914
  setEditing({
@@ -4543,7 +4919,7 @@ function RecordGrid({
4543
4919
  },
4544
4920
  [setCellError, setEditing]
4545
4921
  );
4546
- const applyCellWrite = useCallback9(
4922
+ const applyCellWrite = useCallback10(
4547
4923
  async (row, column, value) => {
4548
4924
  if (!onUpdate) return;
4549
4925
  const values = { ...row.values, [column.id]: value };
@@ -4571,7 +4947,7 @@ function RecordGrid({
4571
4947
  },
4572
4948
  [locale, onUpdate, setRowError, setRowPending]
4573
4949
  );
4574
- const commitEdit = useCallback9(
4950
+ const commitEdit = useCallback10(
4575
4951
  async (row, column, text) => {
4576
4952
  const open = editingRef.current;
4577
4953
  if (open === null || open.rowId !== row.id || open.columnId !== column.id) return;
@@ -4595,7 +4971,7 @@ function RecordGrid({
4595
4971
  },
4596
4972
  [applyCellWrite, setCellError, setEditing]
4597
4973
  );
4598
- const cancelEdit = useCallback9(
4974
+ const cancelEdit = useCallback10(
4599
4975
  (row, column) => {
4600
4976
  setCellError(cellKey(row.id, column.id), null);
4601
4977
  setEditing(null);
@@ -4603,7 +4979,7 @@ function RecordGrid({
4603
4979
  },
4604
4980
  [focusCell, setCellError, setEditing]
4605
4981
  );
4606
- const performDelete = useCallback9(
4982
+ const performDelete = useCallback10(
4607
4983
  async (row) => {
4608
4984
  if (!onDelete) return;
4609
4985
  setConfirmDelete(null);
@@ -4621,16 +4997,16 @@ function RecordGrid({
4621
4997
  },
4622
4998
  [columns, onDelete, setRowError]
4623
4999
  );
4624
- const resetDraft = useCallback9(() => {
5000
+ const resetDraft = useCallback10(() => {
4625
5001
  setDraft({ ...newRowDefaults ?? {} });
4626
5002
  setDraftErrors({});
4627
5003
  setDraftError(null);
4628
5004
  }, [newRowDefaults]);
4629
- const openAdd = useCallback9(() => {
5005
+ const openAdd = useCallback10(() => {
4630
5006
  resetDraft();
4631
5007
  setAdding(true);
4632
5008
  }, [resetDraft]);
4633
- const submitDraft = useCallback9(async () => {
5009
+ const submitDraft = useCallback10(async () => {
4634
5010
  if (!onCreate) return;
4635
5011
  const validated = validateRecordGridRow(columns, draft);
4636
5012
  if (!validated.succeeded) {
@@ -4660,7 +5036,7 @@ function RecordGrid({
4660
5036
  setOverlay((current) => withoutRecordGridCreated(current, draftId));
4661
5037
  setDraftError(outcome.error);
4662
5038
  }, [columns, draft, fieldPrefix, onCreate, resetDraft]);
4663
- const handleGridKeyDown = useCallback9(
5039
+ const handleGridKeyDown = useCallback10(
4664
5040
  (event) => {
4665
5041
  if (editing !== null) return;
4666
5042
  const target = event.target;
@@ -4674,7 +5050,7 @@ function RecordGrid({
4674
5050
  const row = visibleRows[rowIndex];
4675
5051
  const column = columns[columnIndex];
4676
5052
  if (!row || !column) return;
4677
- if (!onUpdate || column.editable === false || row.readOnly === true) return;
5053
+ if (reviewing || !onUpdate || column.editable === false || row.readOnly === true) return;
4678
5054
  if (column.kind === "boolean") return;
4679
5055
  if (!isRecordGridCellApplicable(column, row.values)) return;
4680
5056
  event.preventDefault();
@@ -4696,7 +5072,7 @@ function RecordGrid({
4696
5072
  if (!destinationRow || !destinationColumn) return;
4697
5073
  focusCell(destinationRow.id, destinationColumn.id);
4698
5074
  },
4699
- [beginEdit, columns, editing, focusCell, onUpdate, visibleRows]
5075
+ [beginEdit, columns, editing, focusCell, onUpdate, reviewing, visibleRows]
4700
5076
  );
4701
5077
  if (state.status === "idle" || state.status === "loading") {
4702
5078
  return /* @__PURE__ */ jsxs10("div", { className: `space-y-3 ${className ?? ""}`, children: [
@@ -4736,7 +5112,7 @@ function RecordGrid({
4736
5112
  ] })
4737
5113
  ] });
4738
5114
  }
4739
- const addForm = adding && onCreate ? /* @__PURE__ */ jsx12(
5115
+ const addForm = adding && onCreate && !reviewing ? /* @__PURE__ */ jsx12(
4740
5116
  AddRecordForm,
4741
5117
  {
4742
5118
  columns,
@@ -4754,7 +5130,7 @@ function RecordGrid({
4754
5130
  }
4755
5131
  }
4756
5132
  ) : null;
4757
- if (visibleRows.length === 0) {
5133
+ if (visibleRows.length === 0 && !reviewing) {
4758
5134
  return /* @__PURE__ */ jsxs10("div", { className: `space-y-3 ${className ?? ""}`, children: [
4759
5135
  toolbar,
4760
5136
  addForm ?? /* @__PURE__ */ jsxs10("div", { className: "rounded-xl border border-dashed border-border px-6 py-10 text-center", children: [
@@ -4784,9 +5160,55 @@ function RecordGrid({
4784
5160
  ] });
4785
5161
  }
4786
5162
  const hasFooter = columns.some((column) => column.footerValue !== void 0);
4787
- const columnSpan = columns.length + (onDelete ? 1 : 0);
5163
+ const showActionsColumn = reviewing || onDelete !== void 0;
5164
+ const columnSpan = columns.length + (showActionsColumn ? 1 : 0);
5165
+ const changedCount = (diffs ?? []).filter((diff) => diff.kind === "changed").length;
5166
+ const addedCount = addedRows.length;
5167
+ const removedCount = (diffs ?? []).filter((diff) => diff.kind === "removed").length;
5168
+ const changedCellCount = (diffs ?? []).reduce((total, diff) => total + diff.cells.length, 0);
5169
+ const reviewSummary = [
5170
+ changedCount > 0 ? `${changedCount} changed (${changedCellCount} ${changedCellCount === 1 ? "cell" : "cells"})` : null,
5171
+ addedCount > 0 ? `${addedCount} added` : null,
5172
+ removedCount > 0 ? `${removedCount} removed` : null
5173
+ ].filter((part) => part !== null).join(" \xB7 ");
5174
+ const reviewBar = reviewing ? /* @__PURE__ */ jsxs10(
5175
+ "div",
5176
+ {
5177
+ "data-record-grid-review": "",
5178
+ className: "flex flex-wrap items-center justify-between gap-3 rounded-xl border border-card-edge bg-card px-4 py-2.5",
5179
+ children: [
5180
+ /* @__PURE__ */ jsxs10("div", { className: "min-w-0", children: [
5181
+ /* @__PURE__ */ jsx12("p", { className: "text-[11px] font-semibold uppercase tracking-[0.05em] text-muted-foreground", children: "Proposed changes" }),
5182
+ /* @__PURE__ */ jsx12("p", { className: "mt-0.5 text-xs tabular-nums text-muted-foreground", children: reviewSummary })
5183
+ ] }),
5184
+ (onAcceptAll || onRejectAll) && /* @__PURE__ */ jsxs10("div", { className: "flex items-center gap-2", children: [
5185
+ onRejectAll && /* @__PURE__ */ jsx12(
5186
+ "button",
5187
+ {
5188
+ type: "button",
5189
+ "aria-label": `Reject all proposed changes to ${caption}`,
5190
+ onClick: onRejectAll,
5191
+ className: "rounded-md border border-border px-3 py-1.5 text-xs font-medium text-muted-foreground transition hover:bg-accent",
5192
+ children: "Reject all"
5193
+ }
5194
+ ),
5195
+ onAcceptAll && /* @__PURE__ */ jsx12(
5196
+ "button",
5197
+ {
5198
+ type: "button",
5199
+ "aria-label": `Accept all proposed changes to ${caption}`,
5200
+ onClick: onAcceptAll,
5201
+ className: "rounded-md bg-success/10 px-3 py-1.5 text-xs font-medium text-success transition hover:bg-success/20",
5202
+ children: "Accept all"
5203
+ }
5204
+ )
5205
+ ] })
5206
+ ]
5207
+ }
5208
+ ) : null;
4788
5209
  return /* @__PURE__ */ jsxs10("div", { className: `space-y-3 ${className ?? ""}`, children: [
4789
5210
  toolbar,
5211
+ reviewBar,
4790
5212
  /* @__PURE__ */ jsx12("div", { className: "overflow-x-auto rounded-xl border border-card-edge bg-card", children: /* @__PURE__ */ jsxs10(
4791
5213
  "table",
4792
5214
  {
@@ -4806,149 +5228,222 @@ function RecordGrid({
4806
5228
  },
4807
5229
  column.id
4808
5230
  )),
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" }) })
5231
+ 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
5232
  ] }) }),
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,
5233
+ /* @__PURE__ */ jsxs10("tbody", { children: [
5234
+ visibleRows.map((row) => {
5235
+ const rowLabel = recordGridRowLabel(columns, row);
5236
+ const pending = row.id in pendingRows;
5237
+ const rowError = rowErrors[row.id];
5238
+ const rowDiff = reviewing ? diffByRow.get(row.id) : void 0;
5239
+ const removedRow = rowDiff?.kind === "removed";
5240
+ return /* @__PURE__ */ jsxs10(Fragment6, { children: [
5241
+ /* @__PURE__ */ jsxs10(
5242
+ "tr",
5243
+ {
5244
+ role: "row",
5245
+ "aria-busy": pending,
5246
+ "data-record-grid-diff": rowDiff?.kind,
5247
+ className: `border-b border-border ${pending ? "opacity-60" : ""} ${removedRow ? "bg-destructive/[0.06]" : ""}`,
5248
+ children: [
5249
+ columns.map((column) => {
5250
+ const key = cellKey(row.id, column.id);
5251
+ const applicable = isRecordGridCellApplicable(column, row.values);
5252
+ const editable = !reviewing && onUpdate !== void 0 && column.editable !== false && row.readOnly !== true && applicable;
5253
+ const value = row.values[column.id] ?? null;
5254
+ const isEditing = editing?.rowId === row.id && editing.columnId === column.id;
5255
+ const cellError = cellErrors[key];
5256
+ const active = activeFocus === null ? row.id === visibleRows[0]?.id && column.id === columns[0]?.id : activeFocus.rowId === row.id && activeFocus.columnId === column.id;
5257
+ const source = row.sources?.[column.id];
5258
+ const errorId = `${fieldPrefix}-cell-error-${row.id}-${column.id}`;
5259
+ const cellDiff = rowDiff?.kind === "changed" ? diffCellByKey.get(key) : void 0;
5260
+ if (isEditing && editable) {
5261
+ return /* @__PURE__ */ jsxs10("td", { role: "gridcell", className: `px-3 py-1.5 ${alignmentClass(column)}`, children: [
5262
+ /* @__PURE__ */ jsx12(
5263
+ CellEditor,
5264
+ {
5265
+ column,
5266
+ rowLabel,
5267
+ text: editing.text,
5268
+ invalid: cellError !== void 0,
5269
+ describedBy: cellError === void 0 ? void 0 : errorId,
5270
+ onText: (text) => setEditing({ rowId: row.id, columnId: column.id, text }),
5271
+ onCommit: (text) => void commitEdit(row, column, text),
5272
+ onCancel: () => cancelEdit(row, column)
5273
+ }
5274
+ ),
5275
+ cellError !== void 0 && /* @__PURE__ */ jsx12("p", { id: errorId, role: "alert", className: "mt-1 text-xs leading-snug text-destructive", children: cellError })
5276
+ ] }, column.id);
5277
+ }
5278
+ if (column.kind === "boolean" && editable) {
5279
+ return /* @__PURE__ */ jsx12("td", { role: "gridcell", className: `px-3 py-2 ${alignmentClass(column)}`, children: /* @__PURE__ */ jsx12(
5280
+ "input",
5281
+ {
5282
+ type: "checkbox",
5283
+ checked: value === true,
5284
+ "aria-label": `${column.header}, ${rowLabel}`,
5285
+ "data-record-grid-row": row.id,
5286
+ "data-record-grid-column": column.id,
5287
+ tabIndex: active ? 0 : -1,
5288
+ ref: (node) => {
5289
+ cellRefs.current.set(key, node);
5290
+ },
5291
+ onFocus: () => setFocus({ rowId: row.id, columnId: column.id }),
5292
+ onChange: (event) => void applyCellWrite(row, column, event.target.checked),
5293
+ className: "h-4 w-4 rounded border-border accent-primary"
5294
+ }
5295
+ ) }, column.id);
5296
+ }
5297
+ const display = applicable ? formatRecordGridValue(column, value, locale) : "";
5298
+ return /* @__PURE__ */ jsx12(
5299
+ "td",
5300
+ {
5301
+ role: "gridcell",
5302
+ "aria-readonly": editable ? void 0 : true,
5303
+ "data-record-grid-row": row.id,
5304
+ "data-record-grid-column": column.id,
5305
+ tabIndex: active ? 0 : -1,
5306
+ ref: (node) => {
5307
+ cellRefs.current.set(key, node);
5308
+ },
5309
+ onFocus: () => setFocus({ rowId: row.id, columnId: column.id }),
5310
+ onClick: () => {
5311
+ if (editable) beginEdit(row, column);
5312
+ },
5313
+ className: `px-3 py-2 outline-none focus:ring-2 focus:ring-inset focus:ring-primary/50 ${alignmentClass(
5314
+ column
5315
+ )} ${editable ? "cursor-text" : ""}`,
5316
+ children: /* @__PURE__ */ jsxs10("span", { className: "inline-flex max-w-full items-center gap-1.5", children: [
5317
+ 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" }),
5318
+ cellDiff ? /* @__PURE__ */ jsxs10("span", { className: "inline-flex max-w-full flex-wrap items-baseline gap-x-1.5", children: [
5319
+ /* @__PURE__ */ jsx12("span", { className: "tabular-nums text-destructive line-through decoration-destructive/60", children: formatRecordGridValue(column, cellDiff.before, locale) || "\u2014" }),
5320
+ /* @__PURE__ */ jsx12("span", { "aria-hidden": "true", className: "text-muted-foreground", children: "\u2192" }),
5321
+ /* @__PURE__ */ jsx12("span", { className: "tabular-nums font-medium text-success", children: formatRecordGridValue(column, cellDiff.after, locale) || "\u2014" })
5322
+ ] }) : /* @__PURE__ */ jsx12(
5323
+ "span",
5324
+ {
5325
+ className: removedRow ? "truncate text-destructive line-through decoration-destructive/60" : display === "" ? "text-muted-foreground" : "truncate text-foreground",
5326
+ children: display === "" ? applicable ? "\u2014" : "n/a" : display
5327
+ }
5328
+ ),
5329
+ source && /* @__PURE__ */ jsx12(
5330
+ SourceMarker,
5331
+ {
5332
+ panelId: `${fieldPrefix}-source-${row.id}-${column.id}`,
5333
+ columnHeader: column.header,
5334
+ rowLabel,
5335
+ source,
5336
+ open: openSource === key,
5337
+ onToggle: () => setOpenSource((current) => current === key ? null : key)
5338
+ }
5339
+ )
5340
+ ] })
5341
+ },
5342
+ column.id
5343
+ );
5344
+ }),
5345
+ showActionsColumn && /* @__PURE__ */ jsx12("td", { role: "gridcell", className: "px-3 py-2 text-right", children: reviewing ? rowDiff && /* @__PURE__ */ jsx12(
5346
+ ReviewActions,
4831
5347
  {
4832
- column,
5348
+ rowId: row.id,
5349
+ kind: rowDiff.kind,
4833
5350
  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)
5351
+ onAccept: onAcceptRow,
5352
+ onReject: onRejectRow
4840
5353
  }
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);
5354
+ ) : row.readOnly === true ? null : confirmDelete === row.id ? /* @__PURE__ */ jsxs10("span", { className: "inline-flex items-center gap-1.5", children: [
5355
+ /* @__PURE__ */ jsx12(
5356
+ "button",
5357
+ {
5358
+ type: "button",
5359
+ "aria-label": `Confirm delete ${rowLabel}`,
5360
+ onClick: () => void performDelete(row),
5361
+ className: "rounded-md bg-destructive/10 px-2 py-1 text-xs font-medium text-destructive transition hover:bg-destructive/20",
5362
+ children: "Delete"
5363
+ }
5364
+ ),
5365
+ /* @__PURE__ */ jsx12(
5366
+ "button",
5367
+ {
5368
+ type: "button",
5369
+ "aria-label": `Keep ${rowLabel}`,
5370
+ onClick: () => setConfirmDelete(null),
5371
+ className: "rounded-md border border-border px-2 py-1 text-xs font-medium text-muted-foreground transition hover:bg-accent",
5372
+ children: "Cancel"
5373
+ }
5374
+ )
5375
+ ] }) : /* @__PURE__ */ jsx12(
5376
+ "button",
5377
+ {
5378
+ type: "button",
5379
+ "aria-label": `Delete ${rowLabel}`,
5380
+ onClick: () => setConfirmDelete(row.id),
5381
+ className: "rounded-md p-1.5 text-muted-foreground transition hover:bg-destructive/10 hover:text-destructive",
5382
+ children: /* @__PURE__ */ jsxs10(
5383
+ "svg",
5384
+ {
5385
+ viewBox: "0 0 24 24",
5386
+ className: "h-3.5 w-3.5",
5387
+ fill: "none",
5388
+ stroke: "currentColor",
5389
+ strokeWidth: "2",
5390
+ strokeLinecap: "round",
5391
+ strokeLinejoin: "round",
5392
+ "aria-hidden": true,
5393
+ children: [
5394
+ /* @__PURE__ */ jsx12("polyline", { points: "3 6 5 6 21 6" }),
5395
+ /* @__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" })
5396
+ ]
5397
+ }
5398
+ )
5399
+ }
5400
+ ) })
5401
+ ]
4863
5402
  }
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,
5403
+ ),
5404
+ 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 }) }) })
5405
+ ] }, row.id);
5406
+ }),
5407
+ reviewing && addedRows.map((row) => {
5408
+ const rowLabel = recordGridRowLabel(columns, row);
5409
+ return /* @__PURE__ */ jsxs10(
5410
+ "tr",
5411
+ {
5412
+ role: "row",
5413
+ "data-record-grid-diff": "added",
5414
+ className: "border-b border-border bg-success/[0.06]",
5415
+ children: [
5416
+ columns.map((column, columnIndex) => {
5417
+ const applicable = isRecordGridCellApplicable(column, row.values);
5418
+ const value = row.values[column.id] ?? null;
5419
+ const display = applicable ? formatRecordGridValue(column, value, locale) : "";
5420
+ 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: [
5421
+ 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" }),
5422
+ /* @__PURE__ */ jsx12(
5423
+ "span",
4887
5424
  {
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)
5425
+ className: `tabular-nums ${display === "" ? "text-muted-foreground" : "truncate text-foreground"}`,
5426
+ children: display === "" ? applicable ? "\u2014" : "n/a" : display
4894
5427
  }
4895
5428
  )
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",
5429
+ ] }) }, column.id);
5430
+ }),
5431
+ /* @__PURE__ */ jsx12("td", { role: "gridcell", className: "px-3 py-2 text-right", children: /* @__PURE__ */ jsx12(
5432
+ ReviewActions,
4931
5433
  {
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
- ]
5434
+ rowId: row.id,
5435
+ kind: "added",
5436
+ rowLabel,
5437
+ onAccept: onAcceptRow,
5438
+ onReject: onRejectRow
4944
5439
  }
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
- }) }),
5440
+ ) })
5441
+ ]
5442
+ },
5443
+ row.id
5444
+ );
5445
+ })
5446
+ ] }),
4952
5447
  hasFooter && /* @__PURE__ */ jsx12("tfoot", { children: /* @__PURE__ */ jsxs10("tr", { role: "row", className: "border-t-2 border-border", children: [
4953
5448
  columns.map((column) => /* @__PURE__ */ jsx12(
4954
5449
  "td",
@@ -4959,12 +5454,12 @@ function RecordGrid({
4959
5454
  },
4960
5455
  column.id
4961
5456
  )),
4962
- onDelete && /* @__PURE__ */ jsx12("td", { role: "gridcell" })
5457
+ showActionsColumn && /* @__PURE__ */ jsx12("td", { role: "gridcell" })
4963
5458
  ] }) })
4964
5459
  ]
4965
5460
  }
4966
5461
  ) }),
4967
- onCreate && (addForm ?? /* @__PURE__ */ jsxs10(
5462
+ onCreate && !reviewing && (addForm ?? /* @__PURE__ */ jsxs10(
4968
5463
  "button",
4969
5464
  {
4970
5465
  type: "button",
@@ -4994,6 +5489,31 @@ function RecordGrid({
4994
5489
  ))
4995
5490
  ] });
4996
5491
  }
5492
+ function ReviewActions({ rowId, kind, rowLabel, onAccept, onReject }) {
5493
+ const noun = kind === "changed" ? `proposed change to ${rowLabel}` : kind === "added" ? `new row ${rowLabel}` : `removal of ${rowLabel}`;
5494
+ return /* @__PURE__ */ jsxs10("span", { className: "inline-flex items-center gap-1.5", children: [
5495
+ onReject && /* @__PURE__ */ jsx12(
5496
+ "button",
5497
+ {
5498
+ type: "button",
5499
+ "aria-label": `Reject ${noun}`,
5500
+ onClick: () => onReject(rowId),
5501
+ className: "rounded-md border border-border px-2 py-1 text-xs font-medium text-muted-foreground transition hover:bg-accent",
5502
+ children: "Reject"
5503
+ }
5504
+ ),
5505
+ onAccept && /* @__PURE__ */ jsx12(
5506
+ "button",
5507
+ {
5508
+ type: "button",
5509
+ "aria-label": `Accept ${noun}`,
5510
+ onClick: () => onAccept(rowId),
5511
+ className: "rounded-md bg-success/10 px-2 py-1 text-xs font-medium text-success transition hover:bg-success/20",
5512
+ children: "Accept"
5513
+ }
5514
+ )
5515
+ ] });
5516
+ }
4997
5517
  function CellEditor({ column, rowLabel, text, invalid, describedBy, onText, onCommit, onCancel }) {
4998
5518
  const shared = {
4999
5519
  "aria-label": `${column.header}, ${rowLabel}`,
@@ -5066,7 +5586,7 @@ function CellEditor({ column, rowLabel, text, invalid, describedBy, onText, onCo
5066
5586
  }
5067
5587
  function SourceMarker({ panelId, columnHeader, rowLabel, source, open, onToggle }) {
5068
5588
  const basis = source.basis ?? "asserted";
5069
- const setOpen = useCallback9(
5589
+ const setOpen = useCallback10(
5070
5590
  (next) => {
5071
5591
  if (!next) onToggle();
5072
5592
  },
@@ -5150,7 +5670,7 @@ function AddRecordForm({
5150
5670
  onSubmit,
5151
5671
  onCancel
5152
5672
  }) {
5153
- const groups = useMemo6(() => groupColumns(columns), [columns]);
5673
+ const groups = useMemo7(() => groupColumns(columns), [columns]);
5154
5674
  return /* @__PURE__ */ jsxs10(
5155
5675
  "form",
5156
5676
  {
@@ -5311,6 +5831,196 @@ function DraftField({ column, id, value, invalid, describedBy, onValue }) {
5311
5831
  );
5312
5832
  }
5313
5833
 
5834
+ // src/web-react/command-palette.tsx
5835
+ import {
5836
+ useCallback as useCallback11,
5837
+ useEffect as useEffect12,
5838
+ useId as useId5,
5839
+ useMemo as useMemo8,
5840
+ useRef as useRef12,
5841
+ useState as useState16
5842
+ } from "react";
5843
+ import { createPortal } from "react-dom";
5844
+ import { Fragment as Fragment7, jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
5845
+ function SearchGlyph({ className }) {
5846
+ return /* @__PURE__ */ jsxs11("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: [
5847
+ /* @__PURE__ */ jsx13("circle", { cx: "11", cy: "11", r: "8" }),
5848
+ /* @__PURE__ */ jsx13("path", { d: "m21 21-4.3-4.3" })
5849
+ ] });
5850
+ }
5851
+ function CommandPalette({
5852
+ items,
5853
+ onSelect,
5854
+ open: controlledOpen,
5855
+ onOpenChange,
5856
+ hotkey = true,
5857
+ loading = false,
5858
+ initialQuery,
5859
+ placeholder = "Search sessions and commands\u2026",
5860
+ emptyMessage,
5861
+ label = "Command palette"
5862
+ }) {
5863
+ const [internalOpen, setInternalOpen] = useState16(false);
5864
+ const open = controlledOpen ?? internalOpen;
5865
+ const setOpen = useCallback11(
5866
+ (next) => {
5867
+ if (controlledOpen === void 0) setInternalOpen(next);
5868
+ onOpenChange?.(next);
5869
+ },
5870
+ [controlledOpen, onOpenChange]
5871
+ );
5872
+ const [query, setQuery] = useState16(initialQuery ?? "");
5873
+ const [active, setActive] = useState16(0);
5874
+ const inputRef = useRef12(null);
5875
+ const surfaceId = useId5();
5876
+ const listId = `${surfaceId}-list`;
5877
+ const flat = useMemo8(() => filterCommandPaletteItems(items, query), [items, query]);
5878
+ const sections = useMemo8(() => groupCommandPaletteItems(flat), [flat]);
5879
+ const activeIndex = flat.length === 0 ? 0 : Math.min(active, flat.length - 1);
5880
+ const activeId = flat.length > 0 ? `${listId}-${activeIndex}` : void 0;
5881
+ useEffect12(() => {
5882
+ if (!hotkey) return;
5883
+ function onKeyDown(e) {
5884
+ if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === "k") {
5885
+ e.preventDefault();
5886
+ setOpen(!open);
5887
+ }
5888
+ }
5889
+ document.addEventListener("keydown", onKeyDown);
5890
+ return () => document.removeEventListener("keydown", onKeyDown);
5891
+ }, [hotkey, open, setOpen]);
5892
+ const restoreFocusRef = useRef12(null);
5893
+ useEffect12(() => {
5894
+ if (open) {
5895
+ restoreFocusRef.current = document.activeElement;
5896
+ inputRef.current?.focus();
5897
+ return;
5898
+ }
5899
+ setQuery(initialQuery ?? "");
5900
+ setActive(0);
5901
+ const restore = restoreFocusRef.current;
5902
+ restoreFocusRef.current = null;
5903
+ if (restore instanceof HTMLElement) restore.focus();
5904
+ }, [open]);
5905
+ useEffect12(() => {
5906
+ if (!open || !activeId) return;
5907
+ document.getElementById(activeId)?.scrollIntoView?.({ block: "nearest" });
5908
+ }, [open, activeId]);
5909
+ const choose = useCallback11(
5910
+ (item) => {
5911
+ onSelect(item);
5912
+ setOpen(false);
5913
+ },
5914
+ [onSelect, setOpen]
5915
+ );
5916
+ const handleKeyDown = (e) => {
5917
+ if (e.key === "ArrowDown") {
5918
+ e.preventDefault();
5919
+ if (flat.length > 0) setActive((activeIndex + 1) % flat.length);
5920
+ } else if (e.key === "ArrowUp") {
5921
+ e.preventDefault();
5922
+ if (flat.length > 0) setActive((activeIndex - 1 + flat.length) % flat.length);
5923
+ } else if (e.key === "Enter") {
5924
+ e.preventDefault();
5925
+ const item = flat[activeIndex];
5926
+ if (item) choose(item);
5927
+ } else if (e.key === "Escape") {
5928
+ e.preventDefault();
5929
+ setOpen(false);
5930
+ }
5931
+ };
5932
+ if (!open || typeof document === "undefined") return null;
5933
+ let rowIndex = -1;
5934
+ return createPortal(
5935
+ /* @__PURE__ */ jsxs11(Fragment7, { children: [
5936
+ /* @__PURE__ */ jsx13(
5937
+ "div",
5938
+ {
5939
+ "aria-hidden": true,
5940
+ "data-testid": "command-palette-backdrop",
5941
+ onMouseDown: () => setOpen(false),
5942
+ className: "fixed inset-0 z-[999] bg-background/80"
5943
+ }
5944
+ ),
5945
+ /* @__PURE__ */ jsx13("div", { className: "pointer-events-none fixed inset-x-0 top-[15%] z-[1000] flex justify-center px-4", children: /* @__PURE__ */ jsxs11(
5946
+ "div",
5947
+ {
5948
+ role: "dialog",
5949
+ "aria-modal": "true",
5950
+ "aria-label": label,
5951
+ ...{ [POPOVER_SURFACE_ATTR]: surfaceId },
5952
+ 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}`,
5953
+ children: [
5954
+ /* @__PURE__ */ jsxs11("div", { className: "flex shrink-0 items-center gap-2 border-b border-border px-3 py-2.5", children: [
5955
+ /* @__PURE__ */ jsx13(SearchGlyph, { className: "h-4 w-4 shrink-0 text-muted-foreground" }),
5956
+ /* @__PURE__ */ jsx13(
5957
+ "input",
5958
+ {
5959
+ ref: inputRef,
5960
+ type: "text",
5961
+ role: "combobox",
5962
+ "aria-expanded": true,
5963
+ "aria-controls": listId,
5964
+ "aria-activedescendant": activeId,
5965
+ "aria-label": label,
5966
+ value: query,
5967
+ onChange: (e) => {
5968
+ setQuery(e.target.value);
5969
+ setActive(0);
5970
+ },
5971
+ onKeyDown: handleKeyDown,
5972
+ placeholder,
5973
+ className: "flex-1 bg-transparent text-sm text-foreground placeholder:text-muted-foreground"
5974
+ }
5975
+ )
5976
+ ] }),
5977
+ /* @__PURE__ */ jsxs11("div", { role: "listbox", id: listId, className: "min-h-0 flex-1 overflow-y-auto p-1 pb-2", children: [
5978
+ loading && /* @__PURE__ */ jsx13("div", { className: "px-3 py-4 text-center text-sm text-muted-foreground", children: "Loading\u2026" }),
5979
+ !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") }),
5980
+ !loading && sections.map((section) => /* @__PURE__ */ jsxs11("div", { children: [
5981
+ /* @__PURE__ */ jsx13("div", { className: "px-3 pb-1 pt-3 text-xs font-semibold uppercase tracking-wide text-muted-foreground", children: section.group }),
5982
+ section.items.map((item) => {
5983
+ rowIndex += 1;
5984
+ const index = rowIndex;
5985
+ return /* @__PURE__ */ jsxs11(
5986
+ "div",
5987
+ {
5988
+ id: `${listId}-${index}`,
5989
+ role: "option",
5990
+ "aria-selected": index === activeIndex,
5991
+ onMouseMove: () => setActive(index),
5992
+ onClick: () => choose(item),
5993
+ 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" : ""}`,
5994
+ children: [
5995
+ /* @__PURE__ */ jsx13("span", { className: "truncate text-foreground", children: item.label }),
5996
+ item.description && /* @__PURE__ */ jsx13("span", { className: "truncate text-xs text-muted-foreground", children: item.description }),
5997
+ item.hint && /* @__PURE__ */ jsx13("span", { className: "ml-auto shrink-0 text-xs tabular-nums text-muted-foreground", children: item.hint })
5998
+ ]
5999
+ },
6000
+ item.id
6001
+ );
6002
+ })
6003
+ ] }, section.group))
6004
+ ] }),
6005
+ /* @__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: [
6006
+ /* @__PURE__ */ jsx13("span", { className: "tabular-nums", children: query.trim() ? `${flat.length} of ${items.length}` : `${items.length} items` }),
6007
+ /* @__PURE__ */ jsxs11("span", { className: "flex items-center gap-1.5", children: [
6008
+ /* @__PURE__ */ jsx13("kbd", { className: "rounded border border-border bg-background px-1 py-0.5", children: "\u2191\u2193" }),
6009
+ /* @__PURE__ */ jsx13("span", { children: "navigate" }),
6010
+ /* @__PURE__ */ jsx13("kbd", { className: "ml-1.5 rounded border border-border bg-background px-1 py-0.5", children: "\u21B5" }),
6011
+ /* @__PURE__ */ jsx13("span", { children: "select" }),
6012
+ /* @__PURE__ */ jsx13("kbd", { className: "ml-1.5 rounded border border-border bg-background px-1 py-0.5", children: "esc" }),
6013
+ /* @__PURE__ */ jsx13("span", { children: "close" })
6014
+ ] })
6015
+ ] })
6016
+ ]
6017
+ }
6018
+ ) })
6019
+ ] }),
6020
+ document.body
6021
+ );
6022
+ }
6023
+
5314
6024
  // src/web-react/class-names.ts
5315
6025
  function joinClasses(...parts) {
5316
6026
  const kept = [];
@@ -5323,7 +6033,7 @@ function joinClasses(...parts) {
5323
6033
  }
5324
6034
 
5325
6035
  // src/web-react/sparkline.tsx
5326
- import { jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
6036
+ import { jsx as jsx14, jsxs as jsxs12 } from "react/jsx-runtime";
5327
6037
  var DEFAULT_SPARKLINE_WIDTH = 96;
5328
6038
  var DEFAULT_SPARKLINE_HEIGHT = 24;
5329
6039
  var DEFAULT_INSET = 2.5;
@@ -5429,21 +6139,21 @@ function Sparkline({
5429
6139
  const geometry = sparklineGeometry(values, { width, height });
5430
6140
  const accessibleName = sparklineLabel(values, { label, format });
5431
6141
  if (geometry.points.length === 0) {
5432
- return /* @__PURE__ */ jsxs11(
6142
+ return /* @__PURE__ */ jsxs12(
5433
6143
  "span",
5434
6144
  {
5435
6145
  "data-sparkline": geometry.gaps > 0 ? "unavailable" : "empty",
5436
6146
  className: joinClasses("text-[11px] text-muted-foreground", className),
5437
6147
  children: [
5438
- /* @__PURE__ */ jsx13("span", { className: "sr-only", children: accessibleName }),
5439
- /* @__PURE__ */ jsx13("span", { "aria-hidden": "true", children: geometry.gaps > 0 ? unavailableLabel : emptyLabel })
6148
+ /* @__PURE__ */ jsx14("span", { className: "sr-only", children: accessibleName }),
6149
+ /* @__PURE__ */ jsx14("span", { "aria-hidden": "true", children: geometry.gaps > 0 ? unavailableLabel : emptyLabel })
5440
6150
  ]
5441
6151
  }
5442
6152
  );
5443
6153
  }
5444
6154
  const drawsLine = geometry.segments.some((segment) => segment.length > 1);
5445
6155
  const end = geometry.points[geometry.points.length - 1];
5446
- return /* @__PURE__ */ jsxs11(
6156
+ return /* @__PURE__ */ jsxs12(
5447
6157
  "svg",
5448
6158
  {
5449
6159
  role: "img",
@@ -5460,7 +6170,7 @@ function Sparkline({
5460
6170
  geometry.segments.map((segment, index) => {
5461
6171
  const key = `segment-${index}`;
5462
6172
  if (segment.length > 1) {
5463
- return /* @__PURE__ */ jsx13(
6173
+ return /* @__PURE__ */ jsx14(
5464
6174
  "polyline",
5465
6175
  {
5466
6176
  points: sparklinePointsAttribute(segment),
@@ -5476,9 +6186,9 @@ function Sparkline({
5476
6186
  }
5477
6187
  const only = segment[0];
5478
6188
  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);
6189
+ return /* @__PURE__ */ jsx14("circle", { cx: only.x, cy: only.y, r: DOT_RADIUS, fill: "currentColor" }, key);
5480
6190
  }),
5481
- /* @__PURE__ */ jsx13("circle", { cx: end.x, cy: end.y, r: DOT_RADIUS, fill: "currentColor" })
6191
+ /* @__PURE__ */ jsx14("circle", { cx: end.x, cy: end.y, r: DOT_RADIUS, fill: "currentColor" })
5482
6192
  ]
5483
6193
  }
5484
6194
  );
@@ -5487,12 +6197,12 @@ function Sparkline({
5487
6197
  // src/web-react/insight-card.tsx
5488
6198
  import {
5489
6199
  isValidElement as isValidElement2,
5490
- useCallback as useCallback10,
5491
- useEffect as useEffect11,
5492
- useRef as useRef11,
5493
- useState as useState15
6200
+ useCallback as useCallback12,
6201
+ useEffect as useEffect13,
6202
+ useRef as useRef13,
6203
+ useState as useState17
5494
6204
  } from "react";
5495
- import { Fragment as Fragment7, jsx as jsx14, jsxs as jsxs12 } from "react/jsx-runtime";
6205
+ import { Fragment as Fragment8, jsx as jsx15, jsxs as jsxs13 } from "react/jsx-runtime";
5496
6206
  function insightDelta(value, previous) {
5497
6207
  if (typeof value !== "number" || !Number.isFinite(value)) return null;
5498
6208
  if (typeof previous !== "number" || !Number.isFinite(previous)) return null;
@@ -5525,6 +6235,7 @@ var DIRECTION_GLYPH = { up: "\u2191", down: "\u2193", flat: "\u2192" };
5525
6235
  var INSIGHT_UNAVAILABLE_GLYPH = "\u2014";
5526
6236
  var INSIGHT_UNAVAILABLE_LABEL = "Not available";
5527
6237
  function InsightCard({
6238
+ eyebrow,
5528
6239
  title,
5529
6240
  value,
5530
6241
  unit,
@@ -5544,7 +6255,7 @@ function InsightCard({
5544
6255
  const tone = delta ? insightDeltaTone(delta.direction, polarity) : "neutral";
5545
6256
  const unavailable = typeof value === "number" && !Number.isFinite(value);
5546
6257
  const shown = typeof value === "number" ? format(value) : value;
5547
- return /* @__PURE__ */ jsxs12(
6258
+ return /* @__PURE__ */ jsxs13(
5548
6259
  "article",
5549
6260
  {
5550
6261
  "data-insight-card": "",
@@ -5552,39 +6263,40 @@ function InsightCard({
5552
6263
  className: joinClasses("agent-arrive flex h-full flex-col rounded-xl border border-card-edge bg-card p-4", className),
5553
6264
  style,
5554
6265
  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 }),
6266
+ 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,
6267
+ /* @__PURE__ */ jsxs13("div", { className: "flex items-baseline justify-between gap-2", children: [
6268
+ /* @__PURE__ */ jsx15("h3", { className: "text-[13px] font-medium text-muted-foreground", children: title }),
5557
6269
  live ? (
5558
6270
  // No `data-motion` opt-out: the word is the signal and the sweep is
5559
6271
  // emphasis, so the reduced-motion floor reaches this like everything
5560
6272
  // 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 })
6273
+ /* @__PURE__ */ jsx15("span", { className: "agent-shimmer shrink-0 text-[11px] font-medium", "data-insight-live": "", children: liveLabel })
5562
6274
  ) : null
5563
6275
  ] }),
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
6276
+ /* @__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: [
6277
+ /* @__PURE__ */ jsx15("span", { "aria-hidden": "true", children: INSIGHT_UNAVAILABLE_GLYPH }),
6278
+ /* @__PURE__ */ jsx15("span", { className: "sr-only", children: INSIGHT_UNAVAILABLE_LABEL })
6279
+ ] }) : /* @__PURE__ */ jsxs13(Fragment8, { children: [
6280
+ /* @__PURE__ */ jsx15("span", { className: "text-xl font-semibold tabular-nums text-foreground", children: shown }),
6281
+ unit ? /* @__PURE__ */ jsx15("span", { className: "text-[11px] text-muted-foreground", children: unit }) : null
5570
6282
  ] }) }),
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: [
6283
+ delta ? /* @__PURE__ */ jsxs13("p", { "data-insight-delta": delta.direction, className: `mt-0.5 text-[11px] font-medium ${TONE_CLASS[tone]}`, children: [
6284
+ /* @__PURE__ */ jsxs13("span", { "aria-hidden": "true", children: [
5573
6285
  DIRECTION_GLYPH[delta.direction],
5574
6286
  " "
5575
6287
  ] }),
5576
6288
  formatInsightDelta(delta, format)
5577
6289
  ] }) : 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
6290
+ description ? /* @__PURE__ */ jsx15("p", { className: "mt-1 text-[11px] text-muted-foreground", children: description }) : null,
6291
+ series ? /* @__PURE__ */ jsx15("div", { className: "mt-2 text-muted-foreground", children: /* @__PURE__ */ jsx15(Sparkline, { values: series, label: seriesLabel ?? title, format }) }) : null,
6292
+ action ? /* @__PURE__ */ jsx15("div", { className: "mt-3", children: renderInsightAction(action) }) : null
5581
6293
  ]
5582
6294
  }
5583
6295
  );
5584
6296
  }
5585
6297
  function renderInsightAction(action) {
5586
6298
  if (isValidElement2(action)) return action;
5587
- return /* @__PURE__ */ jsx14(
6299
+ return /* @__PURE__ */ jsx15(
5588
6300
  "button",
5589
6301
  {
5590
6302
  type: "button",
@@ -5653,15 +6365,15 @@ function InsightDeck({
5653
6365
  className,
5654
6366
  onPageChange
5655
6367
  }) {
5656
- const [page, setPage] = useState15(0);
5657
- const [held, setHeld] = useState15(null);
6368
+ const [page, setPage] = useState17(0);
6369
+ const [held, setHeld] = useState17(null);
5658
6370
  const answered = state.status === "error" || state.status === "empty";
5659
6371
  const carried = state.status === "ready" ? state.value : answered ? null : held;
5660
6372
  if (carried !== held) setHeld(carried);
5661
6373
  const shown = carried !== null && state.status !== "ready" ? { status: "ready", value: carried, retry: state.retry } : state;
5662
6374
  const refreshing = shown !== state;
5663
- const reported = useRef11(0);
5664
- const settlePage = useCallback10(
6375
+ const reported = useRef13(0);
6376
+ const settlePage = useCallback12(
5665
6377
  (next) => {
5666
6378
  if (reported.current === next) return;
5667
6379
  reported.current = next;
@@ -5669,7 +6381,7 @@ function InsightDeck({
5669
6381
  },
5670
6382
  [onPageChange]
5671
6383
  );
5672
- return /* @__PURE__ */ jsx14(
6384
+ return /* @__PURE__ */ jsx15(
5673
6385
  AsyncView,
5674
6386
  {
5675
6387
  state: shown,
@@ -5677,7 +6389,7 @@ function InsightDeck({
5677
6389
  loadingLabel,
5678
6390
  retryLabel,
5679
6391
  className,
5680
- children: (insights) => /* @__PURE__ */ jsx14(
6392
+ children: (insights) => /* @__PURE__ */ jsx15(
5681
6393
  InsightPages,
5682
6394
  {
5683
6395
  insights,
@@ -5744,18 +6456,18 @@ function InsightPages({
5744
6456
  const pageCount = insightPageCount(insights.length, size);
5745
6457
  const current = Math.min(Math.max(page, 0), pageCount - 1);
5746
6458
  const visible = insightPageSlice(insights, current, size);
5747
- const sectionRef = useRef11(null);
5748
- const listRef = useRef11(null);
5749
- const recoverFocus = useRef11(false);
5750
- useEffect11(() => {
6459
+ const sectionRef = useRef13(null);
6460
+ const listRef = useRef13(null);
6461
+ const recoverFocus = useRef13(false);
6462
+ useEffect13(() => {
5751
6463
  onPageSettled(current);
5752
6464
  }, [current, onPageSettled]);
5753
- useEffect11(() => {
6465
+ useEffect13(() => {
5754
6466
  if (!recoverFocus.current) return;
5755
6467
  recoverFocus.current = false;
5756
6468
  sectionRef.current?.focus();
5757
6469
  }, [current]);
5758
- const goTo = useCallback10(
6470
+ const goTo = useCallback12(
5759
6471
  (next) => {
5760
6472
  const clamped = Math.min(Math.max(next, 0), pageCount - 1);
5761
6473
  if (clamped === current) return false;
@@ -5790,7 +6502,7 @@ function InsightPages({
5790
6502
  }
5791
6503
  if (moved) event.preventDefault();
5792
6504
  };
5793
- return /* @__PURE__ */ jsxs12(
6505
+ return /* @__PURE__ */ jsxs13(
5794
6506
  "section",
5795
6507
  {
5796
6508
  ref: sectionRef,
@@ -5802,7 +6514,7 @@ function InsightPages({
5802
6514
  tabIndex: pageCount > 1 ? 0 : void 0,
5803
6515
  "aria-keyshortcuts": pageCount > 1 ? "ArrowLeft ArrowRight PageUp PageDown Home End" : void 0,
5804
6516
  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) => (
6517
+ /* @__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
6518
  // The page index is in the key on purpose: a page turn is an arrival,
5807
6519
  // and reusing the node would swap the text under a card that never
5808
6520
  // moved. Remounting replays `.agent-arrive` with the new stagger.
@@ -5813,10 +6525,10 @@ function InsightPages({
5813
6525
  // does not arrive a second time. The key does BOTH jobs — but only
5814
6526
  // because the deck now keeps this subtree mounted across a reload
5815
6527
  // (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}`)
6528
+ /* @__PURE__ */ jsx15("li", { children: /* @__PURE__ */ jsx15(InsightCard, { ...card, style: staggerStyle(index, style) }) }, `${current}:${id}`)
5817
6529
  )) }),
5818
- /* @__PURE__ */ jsxs12("div", { className: pageCount > 1 ? "flex items-center justify-between gap-2" : void 0, children: [
5819
- /* @__PURE__ */ jsxs12(
6530
+ /* @__PURE__ */ jsxs13("div", { className: pageCount > 1 ? "flex items-center justify-between gap-2" : void 0, children: [
6531
+ /* @__PURE__ */ jsxs13(
5820
6532
  "p",
5821
6533
  {
5822
6534
  role: "status",
@@ -5830,8 +6542,8 @@ function InsightPages({
5830
6542
  ]
5831
6543
  }
5832
6544
  ),
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) }),
6545
+ pageCount > 1 ? /* @__PURE__ */ jsxs13("div", { className: "flex items-center gap-1", children: [
6546
+ /* @__PURE__ */ jsx15(PagerButton, { label: "Previous insights", glyph: "\u2039", atEnd: current === 0, onClick: () => goTo(current - 1) }),
5835
6547
  pageCount <= MAX_PAGE_DOTS ? Array.from({ length: pageCount }, (_, index) => (
5836
6548
  // WCAG 2.2 SC 2.5.8 wants a 24x24 CSS px target. The dot stays
5837
6549
  // 8px because a 24px dot is a different control; the BUTTON
@@ -5839,7 +6551,7 @@ function InsightPages({
5839
6551
  // and the span is the graphic. The Spacing exception cannot
5840
6552
  // rescue the bare dot — at a 12px pitch the 24px circle around
5841
6553
  // each centre overlaps its neighbour's.
5842
- /* @__PURE__ */ jsx14(
6554
+ /* @__PURE__ */ jsx15(
5843
6555
  "button",
5844
6556
  {
5845
6557
  type: "button",
@@ -5847,7 +6559,7 @@ function InsightPages({
5847
6559
  "aria-current": index === current ? "page" : void 0,
5848
6560
  onClick: () => goTo(index),
5849
6561
  className: "group flex h-6 w-6 shrink-0 items-center justify-center rounded-full",
5850
- children: /* @__PURE__ */ jsx14(
6562
+ children: /* @__PURE__ */ jsx15(
5851
6563
  "span",
5852
6564
  {
5853
6565
  "aria-hidden": "true",
@@ -5861,7 +6573,7 @@ function InsightPages({
5861
6573
  index
5862
6574
  )
5863
6575
  )) : null,
5864
- /* @__PURE__ */ jsx14(
6576
+ /* @__PURE__ */ jsx15(
5865
6577
  PagerButton,
5866
6578
  {
5867
6579
  label: "Next insights",
@@ -5882,7 +6594,7 @@ function PagerButton({
5882
6594
  atEnd,
5883
6595
  onClick
5884
6596
  }) {
5885
- return /* @__PURE__ */ jsx14(
6597
+ return /* @__PURE__ */ jsx15(
5886
6598
  "button",
5887
6599
  {
5888
6600
  type: "button",
@@ -5892,13 +6604,13 @@ function PagerButton({
5892
6604
  if (!atEnd) onClick();
5893
6605
  },
5894
6606
  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 })
6607
+ children: /* @__PURE__ */ jsx15("span", { "aria-hidden": "true", children: glyph })
5896
6608
  }
5897
6609
  );
5898
6610
  }
5899
6611
 
5900
6612
  // src/web-react/index.tsx
5901
- import { Fragment as Fragment8, jsx as jsx15, jsxs as jsxs13 } from "react/jsx-runtime";
6613
+ import { Fragment as Fragment9, jsx as jsx16, jsxs as jsxs14 } from "react/jsx-runtime";
5902
6614
  function formatModelCost(msg, models) {
5903
6615
  if (msg.promptTokens == null && msg.completionTokens == null) return null;
5904
6616
  const pricing = models.find((m) => m.id === msg.modelUsed)?.pricing;
@@ -5917,41 +6629,41 @@ function formatTokensPerSecond(msg) {
5917
6629
  return `${Math.round(msg.completionTokens / (msg.durationMs / 1e3))} tok/s`;
5918
6630
  }
5919
6631
  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(
6632
+ 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: [
6633
+ /* @__PURE__ */ jsxs14("div", { className: "flex items-center gap-2 border-b border-border px-4 py-3", children: [
6634
+ /* @__PURE__ */ jsx16(
5923
6635
  "span",
5924
6636
  {
5925
6637
  className: `h-2 w-2 shrink-0 rounded-full ${run.status === "running" ? "bg-warning" : run.status === "error" ? "bg-destructive" : "bg-success"}`
5926
6638
  }
5927
6639
  ),
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 })
6640
+ /* @__PURE__ */ jsxs14("div", { className: "min-w-0 flex-1", children: [
6641
+ /* @__PURE__ */ jsx16("p", { className: "truncate text-[15px] font-semibold", children: run.title }),
6642
+ /* @__PURE__ */ jsx16("p", { className: "truncate font-mono text-xs text-muted-foreground", children: run.toolName })
5931
6643
  ] }),
5932
- /* @__PURE__ */ jsx15(
6644
+ /* @__PURE__ */ jsx16(
5933
6645
  "button",
5934
6646
  {
5935
6647
  type: "button",
5936
6648
  onClick: onClose,
5937
6649
  "aria-label": "Close",
5938
6650
  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" }) })
6651
+ 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
6652
  }
5941
6653
  )
5942
6654
  ] }),
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" }) })
6655
+ /* @__PURE__ */ jsxs14("div", { className: "flex-1 space-y-3 overflow-y-auto p-4", children: [
6656
+ run.steps.length === 0 && /* @__PURE__ */ jsx16("p", { className: "text-sm text-muted-foreground", children: "No steps recorded yet." }),
6657
+ run.steps.map((step, i) => /* @__PURE__ */ jsxs14("div", { className: "rounded-lg border border-card-edge bg-card", children: [
6658
+ /* @__PURE__ */ jsxs14("div", { className: "flex items-baseline gap-2 border-b border-border px-3 py-1.5", children: [
6659
+ /* @__PURE__ */ jsx16("span", { className: `font-mono text-xs ${step.status === "error" ? "text-destructive" : "text-muted-foreground"}`, children: step.status === "error" ? "\u2717" : "$" }),
6660
+ /* @__PURE__ */ jsx16("code", { className: "min-w-0 flex-1 truncate font-mono text-xs", children: step.label }),
6661
+ /* @__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
6662
  ] }),
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 })
6663
+ 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
6664
  ] }, i))
5953
6665
  ] }),
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." })
6666
+ /* @__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
6667
  ] });
5956
6668
  }
5957
6669
  function pendingApprovalOf(call) {
@@ -5967,23 +6679,23 @@ function ChatEmptyState({
5967
6679
  }) {
5968
6680
  const doorCount = Math.min(doors?.length ?? 0, 3);
5969
6681
  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(
6682
+ 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: [
6683
+ /* @__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" }) }),
6684
+ /* @__PURE__ */ jsx16("p", { className: "text-xs font-semibold uppercase tracking-[0.05em] text-muted-foreground", children: productName }),
6685
+ /* @__PURE__ */ jsx16("h2", { className: "mt-1.5 text-balance text-2xl font-semibold leading-tight text-foreground", children: headline }),
6686
+ subline && /* @__PURE__ */ jsx16("p", { className: "mt-3 max-w-md text-[15px] leading-relaxed text-muted-foreground", children: subline }),
6687
+ 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
6688
  "button",
5977
6689
  {
5978
6690
  type: "button",
5979
6691
  onClick: door.onSelect,
5980
6692
  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
6693
  children: [
5982
- /* @__PURE__ */ jsxs13("span", { className: "flex items-center gap-2 text-sm font-semibold text-foreground", children: [
6694
+ /* @__PURE__ */ jsxs14("span", { className: "flex items-center gap-2 text-sm font-semibold text-foreground", children: [
5983
6695
  door.icon,
5984
6696
  door.label
5985
6697
  ] }),
5986
- door.description && /* @__PURE__ */ jsx15("span", { className: "mt-0.5 text-[12px] leading-snug text-muted-foreground", children: door.description })
6698
+ door.description && /* @__PURE__ */ jsx16("span", { className: "mt-0.5 text-[12px] leading-snug text-muted-foreground", children: door.description })
5987
6699
  ]
5988
6700
  },
5989
6701
  i
@@ -5992,26 +6704,26 @@ function ChatEmptyState({
5992
6704
  }
5993
6705
  function ToolGlyph({ name, className }) {
5994
6706
  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" })
6707
+ return /* @__PURE__ */ jsxs14("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: [
6708
+ /* @__PURE__ */ jsx16("polyline", { points: "4 17 10 11 4 5" }),
6709
+ /* @__PURE__ */ jsx16("line", { x1: "12", y1: "19", x2: "20", y2: "19" })
5998
6710
  ] });
5999
6711
  }
6000
6712
  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" })
6713
+ return /* @__PURE__ */ jsxs14("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: [
6714
+ /* @__PURE__ */ jsx16("path", { d: "M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" }),
6715
+ /* @__PURE__ */ jsx16("path", { d: "M14 2v6h6M9 15l2 2 4-4" })
6004
6716
  ] });
6005
6717
  }
6006
6718
  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" })
6719
+ return /* @__PURE__ */ jsxs14("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", "aria-hidden": true, children: [
6720
+ /* @__PURE__ */ jsx16("circle", { cx: "12", cy: "12", r: "9" }),
6721
+ /* @__PURE__ */ jsx16("path", { d: "M12 7v5l3 3" })
6010
6722
  ] });
6011
6723
  }
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" })
6724
+ return /* @__PURE__ */ jsxs14("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: [
6725
+ /* @__PURE__ */ jsx16("path", { d: "M12 3v3m0 12v3M3 12h3m12 0h3" }),
6726
+ /* @__PURE__ */ jsx16("circle", { cx: "12", cy: "12", r: "4" })
6015
6727
  ] });
6016
6728
  }
6017
6729
  function toolOutcomeOf(call) {
@@ -6097,40 +6809,40 @@ function truncate(v, max = 240) {
6097
6809
  function KvRows({ data }) {
6098
6810
  const entries = Object.entries(data).filter(([, v]) => v !== void 0 && v !== null && v !== "");
6099
6811
  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) })
6812
+ 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: [
6813
+ /* @__PURE__ */ jsx16("dt", { className: "font-mono text-xs text-muted-foreground", children: k }),
6814
+ /* @__PURE__ */ jsx16("dd", { className: "min-w-0 whitespace-pre-wrap break-words font-mono text-xs text-muted-foreground", children: truncate(v) })
6103
6815
  ] }, k)) });
6104
6816
  }
6105
6817
  function ShellDetail({ call }) {
6106
6818
  const outcome = toolOutcomeOf(call);
6107
6819
  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: [
6820
+ return /* @__PURE__ */ jsxs14("div", { className: "overflow-hidden rounded-md bg-zinc-900 font-mono text-xs leading-relaxed", children: [
6821
+ /* @__PURE__ */ jsxs14("div", { className: "flex items-center gap-2 px-3 pt-2 text-zinc-400", children: [
6822
+ /* @__PURE__ */ jsx16("span", { className: "select-none text-zinc-500", children: "$" }),
6823
+ /* @__PURE__ */ jsx16("span", { className: "min-w-0 flex-1 truncate text-zinc-200", children: String(call.args?.command ?? "") }),
6824
+ r.exitCode != null && /* @__PURE__ */ jsxs14("span", { className: r.exitCode === 0 ? "text-success" : "text-destructive", children: [
6113
6825
  "exit ",
6114
6826
  r.exitCode
6115
6827
  ] })
6116
6828
  ] }),
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)" })
6829
+ /* @__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
6830
  ] });
6119
6831
  }
6120
6832
  function DefaultToolDetail({ call }) {
6121
6833
  const result = call.result;
6122
6834
  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 })
6835
+ return /* @__PURE__ */ jsxs14("div", { className: "space-y-2", children: [
6836
+ call.args && Object.keys(call.args).length > 0 && /* @__PURE__ */ jsxs14("div", { children: [
6837
+ /* @__PURE__ */ jsx16("p", { className: "mb-1 text-xs font-semibold uppercase tracking-wide text-muted-foreground", children: "Called with" }),
6838
+ /* @__PURE__ */ jsx16(KvRows, { data: call.args })
6127
6839
  ] }),
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) })
6840
+ envelope ? /* @__PURE__ */ jsxs14("div", { children: [
6841
+ /* @__PURE__ */ jsx16("p", { className: "mb-1 text-xs font-semibold uppercase tracking-wide text-muted-foreground", children: envelope.ok === false ? "Failed" : "Result" }),
6842
+ 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
6843
+ ] }) : result != null ? /* @__PURE__ */ jsxs14("div", { children: [
6844
+ /* @__PURE__ */ jsx16("p", { className: "mb-1 text-xs font-semibold uppercase tracking-wide text-muted-foreground", children: "Result" }),
6845
+ /* @__PURE__ */ jsx16("p", { className: "font-mono text-xs text-muted-foreground", children: truncate(result) })
6134
6846
  ] }) : null
6135
6847
  ] });
6136
6848
  }
@@ -6141,24 +6853,24 @@ function ProposalCard({
6141
6853
  approval,
6142
6854
  renderers
6143
6855
  }) {
6144
- const [expanded, setExpanded] = useState16(false);
6856
+ const [expanded, setExpanded] = useState18(false);
6145
6857
  const { summary, meta, typeSlug } = proposalPreview(call);
6146
6858
  const custom = renderers?.[call.name]?.(call, message);
6147
6859
  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)) })
6860
+ 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: [
6861
+ /* @__PURE__ */ jsxs14("div", { className: "flex items-start gap-2.5 px-4 pt-3.5", children: [
6862
+ /* @__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" }) }),
6863
+ /* @__PURE__ */ jsxs14("div", { className: "min-w-0 flex-1", children: [
6864
+ /* @__PURE__ */ jsx16("p", { className: "text-xs font-semibold uppercase tracking-[0.05em] text-warning-strong", children: approval ? "Needs your approval" : "Awaiting approval" }),
6865
+ /* @__PURE__ */ jsx16("p", { className: "mt-0.5 text-[15px] font-semibold leading-snug text-foreground", children: friendlyToolTitle(call) }),
6866
+ summary && /* @__PURE__ */ jsx16("p", { className: "mt-1 text-xs leading-relaxed text-muted-foreground", children: summary }),
6867
+ typeSlug && /* @__PURE__ */ jsx16("p", { className: "mt-1 font-mono text-xs text-muted-foreground", children: typeSlug }),
6868
+ 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
6869
  ] })
6158
6870
  ] }),
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(
6871
+ /* @__PURE__ */ jsxs14("div", { className: "flex flex-wrap items-center gap-2 px-4 pb-3.5 pt-3", children: [
6872
+ approval && /* @__PURE__ */ jsxs14(Fragment9, { children: [
6873
+ /* @__PURE__ */ jsx16(
6162
6874
  "button",
6163
6875
  {
6164
6876
  type: "button",
@@ -6168,7 +6880,7 @@ function ProposalCard({
6168
6880
  children: "Approve & run"
6169
6881
  }
6170
6882
  ),
6171
- /* @__PURE__ */ jsx15(
6883
+ /* @__PURE__ */ jsx16(
6172
6884
  "button",
6173
6885
  {
6174
6886
  type: "button",
@@ -6179,7 +6891,7 @@ function ProposalCard({
6179
6891
  }
6180
6892
  )
6181
6893
  ] }),
6182
- /* @__PURE__ */ jsxs13(
6894
+ /* @__PURE__ */ jsxs14(
6183
6895
  "button",
6184
6896
  {
6185
6897
  type: "button",
@@ -6188,12 +6900,12 @@ function ProposalCard({
6188
6900
  className: "ml-auto inline-flex items-center gap-1 rounded text-[12px] font-medium text-muted-foreground transition hover:text-foreground",
6189
6901
  children: [
6190
6902
  expanded ? "Hide details" : "View details",
6191
- /* @__PURE__ */ jsx15(ChevronDown, { className: `h-3 w-3 transition-transform ${expanded ? "rotate-180" : ""}` })
6903
+ /* @__PURE__ */ jsx16(ChevronDown, { className: `h-3 w-3 transition-transform ${expanded ? "rotate-180" : ""}` })
6192
6904
  ]
6193
6905
  }
6194
6906
  )
6195
6907
  ] }),
6196
- expanded && /* @__PURE__ */ jsx15("div", { className: "border-t border-warning/20 px-4 py-3 text-xs", children: custom ?? /* @__PURE__ */ jsx15(DefaultToolDetail, { call }) })
6908
+ expanded && /* @__PURE__ */ jsx16("div", { className: "border-t border-warning/20 px-4 py-3 text-xs", children: custom ?? /* @__PURE__ */ jsx16(DefaultToolDetail, { call }) })
6197
6909
  ] });
6198
6910
  }
6199
6911
  function formatFollowupWhen(when) {
@@ -6208,14 +6920,14 @@ function FollowupCard({ call }) {
6208
6920
  const when = typeof a.when === "string" ? a.when : typeof a.at === "string" ? a.at : typeof a.schedule === "string" ? a.schedule : null;
6209
6921
  const failed = toolCallFailed(call);
6210
6922
  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)]"}` }) })
6923
+ 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: [
6924
+ /* @__PURE__ */ jsxs14("div", { className: "flex w-full items-center gap-2.5 px-3 py-2", children: [
6925
+ /* @__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" }) }),
6926
+ /* @__PURE__ */ jsx16("span", { className: "shrink-0 whitespace-nowrap text-xs font-medium text-foreground", children: friendlyToolTitle(call) }),
6927
+ 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) }),
6928
+ /* @__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
6929
  ] }),
6218
- errorText && /* @__PURE__ */ jsx15("div", { className: "border-t border-border px-3 py-2 text-xs text-[var(--surface-danger-text)]", children: errorText })
6930
+ errorText && /* @__PURE__ */ jsx16("div", { className: "border-t border-border px-3 py-2 text-xs text-[var(--surface-danger-text)]", children: errorText })
6219
6931
  ] }) });
6220
6932
  }
6221
6933
  function toolRowTitle(call) {
@@ -6238,10 +6950,10 @@ function ToolCallCard({
6238
6950
  const arrival = useArrivalStyle(staggerIndex ?? 0);
6239
6951
  const pending = call.status === "done" ? pendingApprovalOf(call) : null;
6240
6952
  const kind = blockKindOf(call);
6241
- const arrive = (row) => /* @__PURE__ */ jsx15("div", { className: "agent-arrive", style: arrival, children: row });
6953
+ const arrive = (row) => /* @__PURE__ */ jsx16("div", { className: "agent-arrive", style: arrival, children: row });
6242
6954
  if (pending) {
6243
6955
  return arrive(
6244
- /* @__PURE__ */ jsx15(
6956
+ /* @__PURE__ */ jsx16(
6245
6957
  ProposalCard,
6246
6958
  {
6247
6959
  call,
@@ -6254,18 +6966,18 @@ function ToolCallCard({
6254
6966
  );
6255
6967
  }
6256
6968
  if (kind === "followup") {
6257
- return arrive(/* @__PURE__ */ jsx15(FollowupCard, { call }));
6969
+ return arrive(/* @__PURE__ */ jsx16(FollowupCard, { call }));
6258
6970
  }
6259
6971
  const custom = renderers?.[call.name]?.(call, message);
6260
6972
  return arrive(
6261
- /* @__PURE__ */ jsx15(
6973
+ /* @__PURE__ */ jsx16(
6262
6974
  InlineToolItem,
6263
6975
  {
6264
6976
  part: chatToolCallPart(call),
6265
6977
  title: toolRowTitle(call),
6266
6978
  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(
6979
+ renderToolDetail: () => custom ?? (call.name === "sandbox_run_command" ? /* @__PURE__ */ jsx16(ShellDetail, { call }) : /* @__PURE__ */ jsx16(DefaultToolDetail, { call })),
6980
+ actions: onOpenRun && call.name.startsWith("sandbox_") ? /* @__PURE__ */ jsx16(
6269
6981
  "button",
6270
6982
  {
6271
6983
  type: "button",
@@ -6273,9 +6985,9 @@ function ToolCallCard({
6273
6985
  "aria-label": "Open full transcript",
6274
6986
  title: "Open full transcript",
6275
6987
  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" })
6988
+ 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: [
6989
+ /* @__PURE__ */ jsx16("path", { d: "M7 17 17 7" }),
6990
+ /* @__PURE__ */ jsx16("path", { d: "M7 7h10v10" })
6279
6991
  ] })
6280
6992
  }
6281
6993
  ) : void 0
@@ -6284,7 +6996,7 @@ function ToolCallCard({
6284
6996
  );
6285
6997
  }
6286
6998
  function StreamingCaret() {
6287
- return /* @__PURE__ */ jsx15(
6999
+ return /* @__PURE__ */ jsx16(
6288
7000
  "span",
6289
7001
  {
6290
7002
  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 +7013,7 @@ function SegmentText({
6301
7013
  messageClassName
6302
7014
  }) {
6303
7015
  const text = useSmoothText(content, streaming);
6304
- const body = useMemo7(() => renderBody(text), [renderBody, text]);
7016
+ const body = useMemo9(() => renderBody(text), [renderBody, text]);
6305
7017
  if (!content.trim() && !showCaret) return null;
6306
7018
  return (
6307
7019
  // A settled run arrives from a short blur; the LIVE run does not, because
@@ -6309,9 +7021,9 @@ function SegmentText({
6309
7021
  // the container on top of that makes the paragraph shimmer while it types.
6310
7022
  // The distinction is what separates "the answer materialised" from "the
6311
7023
  // log was appended to".
6312
- /* @__PURE__ */ jsxs13("div", { className: `${messageClassName}${streaming ? "" : " agent-stream-in"}`, children: [
7024
+ /* @__PURE__ */ jsxs14("div", { className: `${messageClassName}${streaming ? "" : " agent-stream-in"}`, children: [
6313
7025
  body,
6314
- showCaret && /* @__PURE__ */ jsx15(StreamingCaret, {})
7026
+ showCaret && /* @__PURE__ */ jsx16(StreamingCaret, {})
6315
7027
  ] })
6316
7028
  );
6317
7029
  }
@@ -6336,7 +7048,7 @@ function SegmentedBody({
6336
7048
  const leftoverToolCalls = (msg.toolCalls ?? []).filter(
6337
7049
  (tc) => !segmentToolIds.has(tc.id)
6338
7050
  );
6339
- const renderToolCard = (call, index) => /* @__PURE__ */ jsx15(
7051
+ const renderToolCard = (call, index) => /* @__PURE__ */ jsx16(
6340
7052
  ToolCallCard,
6341
7053
  {
6342
7054
  call,
@@ -6364,7 +7076,7 @@ function SegmentedBody({
6364
7076
  for (const g of groups) {
6365
7077
  if (g.kind === "text") {
6366
7078
  children.push(
6367
- /* @__PURE__ */ jsx15(
7079
+ /* @__PURE__ */ jsx16(
6368
7080
  SegmentText,
6369
7081
  {
6370
7082
  content: g.content,
@@ -6380,13 +7092,13 @@ function SegmentedBody({
6380
7092
  }
6381
7093
  if (!streaming && g.calls.length >= COLLAPSE_TOOL_RUN_AT && !g.calls.some(isImportantTool)) {
6382
7094
  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: [
7095
+ /* @__PURE__ */ jsxs14("details", { children: [
7096
+ /* @__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
7097
  "Worked through ",
6386
7098
  g.calls.length,
6387
7099
  " steps"
6388
7100
  ] }),
6389
- /* @__PURE__ */ jsx15("div", { className: "mt-1.5 flex flex-col gap-1.5", children: g.calls.map(renderToolCard) })
7101
+ /* @__PURE__ */ jsx16("div", { className: "mt-1.5 flex flex-col gap-1.5", children: g.calls.map(renderToolCard) })
6390
7102
  ] }, `tools-fold-${g.index}`)
6391
7103
  );
6392
7104
  continue;
@@ -6395,9 +7107,9 @@ function SegmentedBody({
6395
7107
  }
6396
7108
  leftoverToolCalls.forEach((call, index) => children.push(renderToolCard(call, index)));
6397
7109
  if (streaming && segments[lastIndex]?.kind === "tool") {
6398
- children.push(/* @__PURE__ */ jsx15(StreamingCaret, {}, "streaming-caret"));
7110
+ children.push(/* @__PURE__ */ jsx16(StreamingCaret, {}, "streaming-caret"));
6399
7111
  }
6400
- return /* @__PURE__ */ jsx15("div", { className: "flex flex-col gap-2", children });
7112
+ return /* @__PURE__ */ jsx16("div", { className: "flex flex-col gap-2", children });
6401
7113
  }
6402
7114
  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
7115
  function copyTextOf(msg) {
@@ -6406,9 +7118,9 @@ function copyTextOf(msg) {
6406
7118
  return msg.content;
6407
7119
  }
6408
7120
  function CopyMessageButton({ text }) {
6409
- const [copied, setCopied] = useState16(false);
6410
- const timerRef = useRef12(null);
6411
- useEffect12(
7121
+ const [copied, setCopied] = useState18(false);
7122
+ const timerRef = useRef14(null);
7123
+ useEffect14(
6412
7124
  () => () => {
6413
7125
  if (timerRef.current !== null) clearTimeout(timerRef.current);
6414
7126
  },
@@ -6427,7 +7139,7 @@ function CopyMessageButton({ text }) {
6427
7139
  }
6428
7140
  );
6429
7141
  };
6430
- return /* @__PURE__ */ jsx15(
7142
+ return /* @__PURE__ */ jsx16(
6431
7143
  "button",
6432
7144
  {
6433
7145
  type: "button",
@@ -6435,9 +7147,9 @@ function CopyMessageButton({ text }) {
6435
7147
  "aria-label": "Copy message",
6436
7148
  title: "Copy message",
6437
7149
  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" })
7150
+ 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: [
7151
+ /* @__PURE__ */ jsx16("rect", { x: "9", y: "9", width: "13", height: "13", rx: "2" }),
7152
+ /* @__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
7153
  ] })
6442
7154
  }
6443
7155
  );
@@ -6460,34 +7172,34 @@ function AssistantMessageImpl({
6460
7172
  }) {
6461
7173
  const content = useSmoothText(msg.content, streaming);
6462
7174
  const reasoning = useSmoothText(msg.reasoning ?? "", streaming);
6463
- const body = useMemo7(() => renderBody(content), [renderBody, content]);
7175
+ const body = useMemo9(() => renderBody(content), [renderBody, content]);
6464
7176
  const segments = msg.segments;
6465
7177
  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);
7178
+ const reasoningScrollRef = useRef14(null);
7179
+ const thinkStartRef = useRef14(null);
7180
+ const thinkMsRef = useRef14(null);
6469
7181
  if (streaming && reasoning && !hasAnswerText && thinkStartRef.current === null) {
6470
7182
  thinkStartRef.current = performance.now();
6471
7183
  }
6472
7184
  if (hasAnswerText && thinkStartRef.current !== null && thinkMsRef.current === null) {
6473
7185
  thinkMsRef.current = performance.now() - thinkStartRef.current;
6474
7186
  }
6475
- useEffect12(() => {
7187
+ useEffect14(() => {
6476
7188
  const el = reasoningScrollRef.current;
6477
7189
  if (el && streaming && !hasAnswerText) el.scrollTop = el.scrollHeight;
6478
7190
  }, [reasoning, streaming, hasAnswerText]);
6479
7191
  const thinkingSeconds = useThinkingSeconds(
6480
7192
  streaming && !!reasoning && !hasAnswerText
6481
7193
  );
6482
- const [reasoningToggled, setReasoningToggled] = useState16(null);
7194
+ const [reasoningToggled, setReasoningToggled] = useState18(null);
6483
7195
  const reasoningOpen = reasoningToggled ?? !hasAnswerText;
6484
7196
  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) })
7197
+ return /* @__PURE__ */ jsxs14("div", { className: `mx-auto w-full max-w-3xl px-6 ${quiet ? "group pb-1 pt-3" : "py-3"}`, children: [
7198
+ !quiet && /* @__PURE__ */ jsxs14("div", { className: "mb-1 flex items-baseline gap-2 text-xs tabular-nums text-muted-foreground", children: [
7199
+ /* @__PURE__ */ jsx16("span", { className: "font-semibold uppercase tracking-[0.05em]", children: agentLabel }),
7200
+ msg.modelUsed && /* @__PURE__ */ jsx16("span", { className: "font-mono normal-case", children: msg.modelUsed }),
7201
+ formatTokensPerSecond(msg) && /* @__PURE__ */ jsx16("span", { children: formatTokensPerSecond(msg) }),
7202
+ formatModelCost(msg, models) && /* @__PURE__ */ jsx16("span", { children: formatModelCost(msg, models) })
6491
7203
  ] }),
6492
7204
  reasoning && // The canonical run-row grammar (RunRowShell — the same shell the tool
6493
7205
  // rows compose): one family of rows instead of a bespoke disclosure per
@@ -6497,12 +7209,12 @@ function AssistantMessageImpl({
6497
7209
  // and a click outranks the default from then on — the contract the old
6498
7210
  // hand-rolled disclosure had, now enforced through the shell's
6499
7211
  // controlled `open`.
6500
- /* @__PURE__ */ jsx15(
7212
+ /* @__PURE__ */ jsx16(
6501
7213
  RunRowShell,
6502
7214
  {
6503
7215
  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: [
7216
+ icon: /* @__PURE__ */ jsx16(BrainGlyph, { className: "h-3.5 w-3.5" }),
7217
+ title: !hasAnswerText ? /* @__PURE__ */ jsxs14("span", { className: "agent-shimmer", "data-motion": "essential", children: [
6506
7218
  "Thinking",
6507
7219
  thinkingSeconds >= 1 ? ` \xB7 ${thinkingSeconds}s` : "\u2026"
6508
7220
  ] }) : thinkMsRef.current != null ? (
@@ -6517,7 +7229,7 @@ function AssistantMessageImpl({
6517
7229
  status: hasAnswerText ? "idle" : "running",
6518
7230
  open: reasoningOpen,
6519
7231
  onOpenChange: (next) => setReasoningToggled(next),
6520
- children: /* @__PURE__ */ jsx15(
7232
+ children: /* @__PURE__ */ jsx16(
6521
7233
  "div",
6522
7234
  {
6523
7235
  ref: reasoningScrollRef,
@@ -6527,7 +7239,7 @@ function AssistantMessageImpl({
6527
7239
  )
6528
7240
  }
6529
7241
  ),
6530
- segments && segments.length > 0 ? /* @__PURE__ */ jsx15(
7242
+ segments && segments.length > 0 ? /* @__PURE__ */ jsx16(
6531
7243
  SegmentedBody,
6532
7244
  {
6533
7245
  segments,
@@ -6539,12 +7251,12 @@ function AssistantMessageImpl({
6539
7251
  toolRenderers,
6540
7252
  messageClassName
6541
7253
  }
6542
- ) : /* @__PURE__ */ jsxs13(Fragment8, { children: [
6543
- /* @__PURE__ */ jsxs13("div", { className: messageClassName, children: [
7254
+ ) : /* @__PURE__ */ jsxs14(Fragment9, { children: [
7255
+ /* @__PURE__ */ jsxs14("div", { className: messageClassName, children: [
6544
7256
  body,
6545
- streaming && content && !msg.toolCalls?.length && /* @__PURE__ */ jsx15(StreamingCaret, {})
7257
+ streaming && content && !msg.toolCalls?.length && /* @__PURE__ */ jsx16(StreamingCaret, {})
6546
7258
  ] }),
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(
7259
+ 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
7260
  ToolCallCard,
6549
7261
  {
6550
7262
  call: tc,
@@ -6557,7 +7269,7 @@ function AssistantMessageImpl({
6557
7269
  tc.id
6558
7270
  )) })
6559
7271
  ] }),
6560
- durableCards && msg.parts && /* @__PURE__ */ jsx15(
7272
+ durableCards && msg.parts && /* @__PURE__ */ jsx16(
6561
7273
  DurableChatCards,
6562
7274
  {
6563
7275
  ...durableCards,
@@ -6566,7 +7278,7 @@ function AssistantMessageImpl({
6566
7278
  className: "mt-3"
6567
7279
  }
6568
7280
  ),
6569
- workProductCards && workProductPartsFromMessageParts(msg.parts).map((part) => /* @__PURE__ */ jsx15(
7281
+ workProductCards && workProductPartsFromMessageParts(msg.parts).map((part) => /* @__PURE__ */ jsx16(
6570
7282
  WorkProductCard,
6571
7283
  {
6572
7284
  part,
@@ -6576,7 +7288,7 @@ function AssistantMessageImpl({
6576
7288
  `${part.ref.id}:${part.ref.version}`
6577
7289
  )),
6578
7290
  renderExtras?.(msg),
6579
- resolveAttachmentUrl && attachmentPartsFromMessageParts(msg.parts).length > 0 && /* @__PURE__ */ jsx15("div", { className: "mt-2", children: /* @__PURE__ */ jsx15(
7291
+ resolveAttachmentUrl && attachmentPartsFromMessageParts(msg.parts).length > 0 && /* @__PURE__ */ jsx16("div", { className: "mt-2", children: /* @__PURE__ */ jsx16(
6580
7292
  MessageAttachments,
6581
7293
  {
6582
7294
  parts: attachmentPartsFromMessageParts(msg.parts),
@@ -6584,18 +7296,18 @@ function AssistantMessageImpl({
6584
7296
  justify: "start"
6585
7297
  }
6586
7298
  ) }),
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) })
7299
+ quiet && /* @__PURE__ */ jsxs14("div", { "data-testid": "message-meta-lane", className: QUIET_META_LANE_CLASS, children: [
7300
+ /* @__PURE__ */ jsx16(CopyMessageButton, { text: copyTextOf(msg) }),
7301
+ msg.modelUsed && /* @__PURE__ */ jsx16("span", { className: "font-mono", children: msg.modelUsed }),
7302
+ formatTokensPerSecond(msg) && /* @__PURE__ */ jsx16("span", { children: formatTokensPerSecond(msg) }),
7303
+ formatModelCost(msg, models) && /* @__PURE__ */ jsx16("span", { children: formatModelCost(msg, models) })
6592
7304
  ] })
6593
7305
  ] });
6594
7306
  }
6595
7307
  var AssistantMessage = memo(AssistantMessageImpl);
6596
7308
  function useThinkingSeconds(active) {
6597
- const [seconds, setSeconds] = useState16(0);
6598
- useEffect12(() => {
7309
+ const [seconds, setSeconds] = useState18(0);
7310
+ useEffect14(() => {
6599
7311
  if (!active) return;
6600
7312
  setSeconds(0);
6601
7313
  const id = setInterval(() => setSeconds((s) => s + 1), 1e3);
@@ -6605,23 +7317,23 @@ function useThinkingSeconds(active) {
6605
7317
  }
6606
7318
  function ThinkingRow({ agentLabel, chrome = "labeled" }) {
6607
7319
  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" }) }),
7320
+ return /* @__PURE__ */ jsxs14("div", { className: "mx-auto w-full max-w-3xl px-6 py-3", children: [
7321
+ chrome !== "quiet" && /* @__PURE__ */ jsx16("p", { className: "mb-1 text-xs font-semibold uppercase tracking-[0.05em] text-muted-foreground", children: agentLabel }),
7322
+ /* @__PURE__ */ jsxs14("div", { className: "flex items-center gap-2 text-[15px] text-muted-foreground", children: [
7323
+ /* @__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
7324
  "Thinking",
6613
7325
  seconds >= 3 ? ` \xB7 ${seconds}s` : "..."
6614
7326
  ] })
6615
7327
  ] });
6616
7328
  }
6617
7329
  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" })
7330
+ 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: [
7331
+ /* @__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: [
7332
+ /* @__PURE__ */ jsx16("circle", { cx: "12", cy: "12", r: "9" }),
7333
+ /* @__PURE__ */ jsx16("path", { d: "M12 8v4m0 4h.01" })
6622
7334
  ] }),
6623
- /* @__PURE__ */ jsx15("span", { className: "min-w-0 flex-1 break-words", children: message }),
6624
- onRetry && /* @__PURE__ */ jsx15(
7335
+ /* @__PURE__ */ jsx16("span", { className: "min-w-0 flex-1 break-words", children: message }),
7336
+ onRetry && /* @__PURE__ */ jsx16(
6625
7337
  "button",
6626
7338
  {
6627
7339
  type: "button",
@@ -6655,33 +7367,33 @@ function ChatMessages({
6655
7367
  workProductCards
6656
7368
  }) {
6657
7369
  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 })),
7370
+ const renderBody = useMemo9(
7371
+ () => renderMarkdown ?? ((content) => /* @__PURE__ */ jsx16("p", { className: "whitespace-pre-wrap", children: content })),
6660
7372
  [renderMarkdown]
6661
7373
  );
6662
7374
  const lastIsUser = messages[messages.length - 1]?.role === "user";
6663
7375
  const quiet = chrome === "quiet";
6664
7376
  if (messages.length === 0 && !loading && !error) {
6665
- const empty = renderEmpty ? renderEmpty() : /* @__PURE__ */ jsx15(ChatEmptyState, { ...emptyState });
6666
- return /* @__PURE__ */ jsxs13(Fragment8, { children: [
7377
+ const empty = renderEmpty ? renderEmpty() : /* @__PURE__ */ jsx16(ChatEmptyState, { ...emptyState });
7378
+ return /* @__PURE__ */ jsxs14(Fragment9, { children: [
6667
7379
  header,
6668
7380
  empty
6669
7381
  ] });
6670
7382
  }
6671
- return /* @__PURE__ */ jsxs13(Fragment8, { children: [
7383
+ return /* @__PURE__ */ jsxs14(Fragment9, { children: [
6672
7384
  header,
6673
7385
  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(
7386
+ (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: [
7387
+ /* @__PURE__ */ jsxs14("div", { className: `ml-auto w-fit ${quiet ? "max-w-[72%]" : "max-w-[85%]"}`, children: [
7388
+ !quiet && /* @__PURE__ */ jsx16("p", { className: "mb-1 text-right text-xs font-semibold uppercase tracking-[0.05em] text-muted-foreground", children: userLabel }),
7389
+ /* @__PURE__ */ jsx16(
6678
7390
  "div",
6679
7391
  {
6680
7392
  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 })
7393
+ children: /* @__PURE__ */ jsx16("p", { className: "whitespace-pre-wrap", children: msg.content })
6682
7394
  }
6683
7395
  ),
6684
- resolveAttachmentUrl && attachmentPartsFromMessageParts(msg.parts).length > 0 && /* @__PURE__ */ jsx15("div", { className: "mt-1.5", children: /* @__PURE__ */ jsx15(
7396
+ resolveAttachmentUrl && attachmentPartsFromMessageParts(msg.parts).length > 0 && /* @__PURE__ */ jsx16("div", { className: "mt-1.5", children: /* @__PURE__ */ jsx16(
6685
7397
  MessageAttachments,
6686
7398
  {
6687
7399
  parts: attachmentPartsFromMessageParts(msg.parts),
@@ -6690,8 +7402,8 @@ function ChatMessages({
6690
7402
  }
6691
7403
  ) })
6692
7404
  ] }),
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(
7405
+ quiet && /* @__PURE__ */ jsx16("div", { "data-testid": "message-meta-lane", className: `${QUIET_META_LANE_CLASS} justify-end`, children: /* @__PURE__ */ jsx16(CopyMessageButton, { text: msg.content }) })
7406
+ ] }, msg.id) : /* @__PURE__ */ jsx16(
6695
7407
  AssistantMessage,
6696
7408
  {
6697
7409
  msg,
@@ -6712,8 +7424,8 @@ function ChatMessages({
6712
7424
  msg.id
6713
7425
  )
6714
7426
  ),
6715
- loading && lastIsUser && /* @__PURE__ */ jsx15(ThinkingRow, { agentLabel, chrome }),
6716
- error && !loading && /* @__PURE__ */ jsx15(StreamErrorRow, { message: error, onRetry })
7427
+ loading && lastIsUser && /* @__PURE__ */ jsx16(ThinkingRow, { agentLabel, chrome }),
7428
+ error && !loading && /* @__PURE__ */ jsx16(StreamErrorRow, { message: error, onRetry })
6717
7429
  ] });
6718
7430
  }
6719
7431
 
@@ -6748,6 +7460,10 @@ export {
6748
7460
  dispatchChatStreamLine,
6749
7461
  consumeChatStream,
6750
7462
  streamChatTurn,
7463
+ pickDictationMimeType,
7464
+ dictationErrorMessage,
7465
+ formatDictationElapsed,
7466
+ useDictation,
6751
7467
  ChatComposer,
6752
7468
  DurablePlanClientError,
6753
7469
  createDurablePlanDecisionClient,
@@ -6803,6 +7519,7 @@ export {
6803
7519
  recordGridFail,
6804
7520
  isRecordGridCellApplicable,
6805
7521
  sameRecordGridValue,
7522
+ diffRecordGridProposal,
6806
7523
  parseRecordGridInput,
6807
7524
  validateRecordGridCell,
6808
7525
  readRecordGridCell,
@@ -6822,6 +7539,7 @@ export {
6822
7539
  withoutRecordGridRemoved,
6823
7540
  pruneRecordGridOverlay,
6824
7541
  RecordGrid,
7542
+ CommandPalette,
6825
7543
  DEFAULT_SPARKLINE_WIDTH,
6826
7544
  DEFAULT_SPARKLINE_HEIGHT,
6827
7545
  DEFAULT_SPARKLINE_LABEL,
@@ -6851,4 +7569,4 @@ export {
6851
7569
  useThinkingSeconds,
6852
7570
  ChatMessages
6853
7571
  };
6854
- //# sourceMappingURL=chunk-FOXGPGXF.js.map
7572
+ //# sourceMappingURL=chunk-OMGV3CX2.js.map