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