@cntyclub/agent-react 0.16.0 → 0.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/agent-panel.d.ts.map +1 -1
- package/dist/components/message-item.d.ts +5 -1
- package/dist/components/message-item.d.ts.map +1 -1
- package/dist/components/thinking-indicator.d.ts.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +174 -120
- package/dist/index.js.map +1 -1
- package/dist/use-smooth-text.d.ts +17 -0
- package/dist/use-smooth-text.d.ts.map +1 -0
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { cn, Button, ThinkingOrb, Tooltip, TooltipTrigger, TooltipContent, ChatMessage, ChatMessageBubble, Markdown, AuroraText, Spinner, Checkbox, Collapsible, CollapsibleTrigger, ChatToolChip, CollapsiblePanel, ChatToolCard, ChatToolCardHeader, ChatToolCardActions, Group, Menu, MenuTrigger, MenuPopup, MenuItem, ChatHeader, ChatHeaderAvatar, ChatHeaderTitle, ChatHeaderActions, Chat, ChatEmptyState, ChatEmptyStateIcon, ChatEmptyStateTitle, ChatEmptyStateDescription, ChatSuggestions, ChatSuggestion, useMediaQuery, TooltipProvider, DataTablePaged } from '@cntyclub/ui-react';
|
|
2
2
|
import { XIcon, Trash2Icon, SquareIcon, PauseIcon, PlayIcon, CheckIcon, FileTextIcon, PaperclipIcon, MicIcon, ArrowUpIcon, CopyIcon, Maximize2Icon, ListChecksIcon, ChevronDownIcon, ShieldAlertIcon, CheckCheckIcon, UploadCloudIcon, HistoryIcon, SquarePenIcon, Minimize2Icon, ChevronsRightIcon, SparklesIcon, DownloadIcon, CodeIcon } from 'lucide-react';
|
|
3
|
-
import * as
|
|
3
|
+
import * as React12 from 'react';
|
|
4
4
|
import { AnimatePresence, motion } from 'motion/react';
|
|
5
5
|
import { createPortal } from 'react-dom';
|
|
6
6
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
@@ -259,7 +259,7 @@ function formatBytes(bytes) {
|
|
|
259
259
|
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
260
260
|
}
|
|
261
261
|
function AgentImageLightbox({ src, alt, onClose }) {
|
|
262
|
-
|
|
262
|
+
React12.useEffect(() => {
|
|
263
263
|
if (!src) return;
|
|
264
264
|
const onKey = (event) => {
|
|
265
265
|
if (event.key === "Escape") onClose();
|
|
@@ -329,24 +329,24 @@ function pickMimeType() {
|
|
|
329
329
|
return "";
|
|
330
330
|
}
|
|
331
331
|
function AgentVoiceRecorder({ maxSeconds, transcribe, onDone, onCancel, onError }) {
|
|
332
|
-
const [phase, setPhase] =
|
|
333
|
-
const [elapsed, setElapsed] =
|
|
334
|
-
const [playing, setPlaying] =
|
|
335
|
-
const recorderRef =
|
|
336
|
-
const streamRef =
|
|
337
|
-
const chunksRef =
|
|
338
|
-
const blobRef =
|
|
339
|
-
const audioUrlRef =
|
|
340
|
-
const audioRef =
|
|
341
|
-
const timerRef =
|
|
342
|
-
const canvasRef =
|
|
343
|
-
const audioContextRef =
|
|
344
|
-
const analyserRef =
|
|
345
|
-
const rafRef =
|
|
346
|
-
const levelsRef =
|
|
347
|
-
const onErrorRef =
|
|
332
|
+
const [phase, setPhase] = React12.useState("recording");
|
|
333
|
+
const [elapsed, setElapsed] = React12.useState(0);
|
|
334
|
+
const [playing, setPlaying] = React12.useState(false);
|
|
335
|
+
const recorderRef = React12.useRef(null);
|
|
336
|
+
const streamRef = React12.useRef(null);
|
|
337
|
+
const chunksRef = React12.useRef([]);
|
|
338
|
+
const blobRef = React12.useRef(null);
|
|
339
|
+
const audioUrlRef = React12.useRef(null);
|
|
340
|
+
const audioRef = React12.useRef(null);
|
|
341
|
+
const timerRef = React12.useRef(null);
|
|
342
|
+
const canvasRef = React12.useRef(null);
|
|
343
|
+
const audioContextRef = React12.useRef(null);
|
|
344
|
+
const analyserRef = React12.useRef(null);
|
|
345
|
+
const rafRef = React12.useRef(null);
|
|
346
|
+
const levelsRef = React12.useRef(new Array(MAX_BARS).fill(0));
|
|
347
|
+
const onErrorRef = React12.useRef(onError);
|
|
348
348
|
onErrorRef.current = onError;
|
|
349
|
-
const stopMeter =
|
|
349
|
+
const stopMeter = React12.useCallback(() => {
|
|
350
350
|
if (rafRef.current != null) {
|
|
351
351
|
cancelAnimationFrame(rafRef.current);
|
|
352
352
|
rafRef.current = null;
|
|
@@ -357,7 +357,7 @@ function AgentVoiceRecorder({ maxSeconds, transcribe, onDone, onCancel, onError
|
|
|
357
357
|
if (ctx && ctx.state !== "closed") void ctx.close().catch(() => {
|
|
358
358
|
});
|
|
359
359
|
}, []);
|
|
360
|
-
const stopTracks =
|
|
360
|
+
const stopTracks = React12.useCallback(() => {
|
|
361
361
|
streamRef.current?.getTracks().forEach((track) => track.stop());
|
|
362
362
|
streamRef.current = null;
|
|
363
363
|
if (timerRef.current) {
|
|
@@ -366,7 +366,7 @@ function AgentVoiceRecorder({ maxSeconds, transcribe, onDone, onCancel, onError
|
|
|
366
366
|
}
|
|
367
367
|
stopMeter();
|
|
368
368
|
}, [stopMeter]);
|
|
369
|
-
const startMeter =
|
|
369
|
+
const startMeter = React12.useCallback((stream) => {
|
|
370
370
|
const Ctor = typeof window === "undefined" ? void 0 : window.AudioContext ?? window.webkitAudioContext;
|
|
371
371
|
if (!Ctor) return;
|
|
372
372
|
let context;
|
|
@@ -436,7 +436,7 @@ function AgentVoiceRecorder({ maxSeconds, transcribe, onDone, onCancel, onError
|
|
|
436
436
|
};
|
|
437
437
|
rafRef.current = requestAnimationFrame(draw);
|
|
438
438
|
}, []);
|
|
439
|
-
|
|
439
|
+
React12.useEffect(() => {
|
|
440
440
|
let cancelled = false;
|
|
441
441
|
void (async () => {
|
|
442
442
|
if (typeof navigator === "undefined" || !navigator.mediaDevices?.getUserMedia) {
|
|
@@ -589,40 +589,40 @@ function AgentComposer({
|
|
|
589
589
|
voiceEnabled,
|
|
590
590
|
voiceMaxSeconds = 600
|
|
591
591
|
}) {
|
|
592
|
-
const textareaRef =
|
|
593
|
-
const inputRef =
|
|
594
|
-
const [recording, setRecording] =
|
|
595
|
-
const [lightboxSrc, setLightboxSrc] =
|
|
592
|
+
const textareaRef = React12.useRef(null);
|
|
593
|
+
const inputRef = React12.useRef(null);
|
|
594
|
+
const [recording, setRecording] = React12.useState(false);
|
|
595
|
+
const [lightboxSrc, setLightboxSrc] = React12.useState(null);
|
|
596
596
|
const canSubmit = !disabled && !busy && (value.trim().length > 0 || files.length > 0);
|
|
597
597
|
const voiceAvailable = Boolean(voiceEnabled && onTranscribe);
|
|
598
|
-
const submit =
|
|
598
|
+
const submit = React12.useCallback(() => {
|
|
599
599
|
if (!canSubmit) return;
|
|
600
600
|
onSubmit(value.trim());
|
|
601
601
|
textareaRef.current?.focus();
|
|
602
602
|
}, [canSubmit, onSubmit, value]);
|
|
603
|
-
|
|
603
|
+
React12.useEffect(() => {
|
|
604
604
|
if (!disabled) textareaRef.current?.focus();
|
|
605
605
|
}, [disabled]);
|
|
606
|
-
|
|
606
|
+
React12.useEffect(() => {
|
|
607
607
|
const textarea = textareaRef.current;
|
|
608
608
|
if (!textarea) return;
|
|
609
609
|
textarea.style.height = "auto";
|
|
610
610
|
textarea.style.height = `${Math.min(textarea.scrollHeight, 144)}px`;
|
|
611
611
|
}, [value]);
|
|
612
|
-
const previews =
|
|
612
|
+
const previews = React12.useMemo(
|
|
613
613
|
() => files.map((file, originalIndex) => {
|
|
614
614
|
const image = isImageFile(file);
|
|
615
615
|
return { file, image, originalIndex, url: image ? URL.createObjectURL(file) : null };
|
|
616
616
|
}),
|
|
617
617
|
[files]
|
|
618
618
|
);
|
|
619
|
-
|
|
619
|
+
React12.useEffect(
|
|
620
620
|
() => () => {
|
|
621
621
|
previews.forEach((preview) => preview.url && URL.revokeObjectURL(preview.url));
|
|
622
622
|
},
|
|
623
623
|
[previews]
|
|
624
624
|
);
|
|
625
|
-
const ordered =
|
|
625
|
+
const ordered = React12.useMemo(
|
|
626
626
|
() => [...previews].sort((a, b) => Number(b.image) - Number(a.image)),
|
|
627
627
|
[previews]
|
|
628
628
|
);
|
|
@@ -867,15 +867,15 @@ function groupMessages(messages) {
|
|
|
867
867
|
return items;
|
|
868
868
|
}
|
|
869
869
|
function ChatScroller({ children, className, ...props }) {
|
|
870
|
-
const viewportRef =
|
|
871
|
-
const pinnedRef =
|
|
872
|
-
const handleScroll =
|
|
870
|
+
const viewportRef = React12.useRef(null);
|
|
871
|
+
const pinnedRef = React12.useRef(true);
|
|
872
|
+
const handleScroll = React12.useCallback(() => {
|
|
873
873
|
const viewport = viewportRef.current;
|
|
874
874
|
if (!viewport) return;
|
|
875
875
|
const distanceFromBottom = viewport.scrollHeight - viewport.scrollTop - viewport.clientHeight;
|
|
876
876
|
pinnedRef.current = distanceFromBottom < 64;
|
|
877
877
|
}, []);
|
|
878
|
-
|
|
878
|
+
React12.useEffect(() => {
|
|
879
879
|
const viewport = viewportRef.current;
|
|
880
880
|
if (!viewport || !pinnedRef.current) return;
|
|
881
881
|
viewport.scrollTop = viewport.scrollHeight;
|
|
@@ -892,15 +892,66 @@ function ChatScroller({ children, className, ...props }) {
|
|
|
892
892
|
}
|
|
893
893
|
);
|
|
894
894
|
}
|
|
895
|
+
var MIN_CHARS_PER_FRAME = 2;
|
|
896
|
+
var CATCH_UP = 0.22;
|
|
897
|
+
function usePrefersReducedMotion() {
|
|
898
|
+
const [reduced, setReduced] = React12.useState(
|
|
899
|
+
() => typeof matchMedia !== "undefined" && matchMedia("(prefers-reduced-motion: reduce)").matches
|
|
900
|
+
);
|
|
901
|
+
React12.useEffect(() => {
|
|
902
|
+
if (typeof matchMedia === "undefined") return;
|
|
903
|
+
const mq = matchMedia("(prefers-reduced-motion: reduce)");
|
|
904
|
+
const onChange = (event) => setReduced(event.matches);
|
|
905
|
+
mq.addEventListener("change", onChange);
|
|
906
|
+
return () => mq.removeEventListener("change", onChange);
|
|
907
|
+
}, []);
|
|
908
|
+
return reduced;
|
|
909
|
+
}
|
|
910
|
+
function useSmoothText(target, animate) {
|
|
911
|
+
const prefersReduced = usePrefersReducedMotion();
|
|
912
|
+
const enabled = animate && !prefersReduced;
|
|
913
|
+
const [count, setCount] = React12.useState(() => enabled ? 0 : target.length);
|
|
914
|
+
const countRef = React12.useRef(count);
|
|
915
|
+
countRef.current = count;
|
|
916
|
+
const everEnabled = React12.useRef(enabled);
|
|
917
|
+
if (enabled) everEnabled.current = true;
|
|
918
|
+
React12.useEffect(() => {
|
|
919
|
+
if (!everEnabled.current) {
|
|
920
|
+
if (countRef.current !== target.length) setCount(target.length);
|
|
921
|
+
return;
|
|
922
|
+
}
|
|
923
|
+
if (countRef.current > target.length) {
|
|
924
|
+
setCount(target.length);
|
|
925
|
+
return;
|
|
926
|
+
}
|
|
927
|
+
if (countRef.current >= target.length) return;
|
|
928
|
+
let raf = 0;
|
|
929
|
+
let last = performance.now();
|
|
930
|
+
const tick = (now) => {
|
|
931
|
+
const dt = Math.min(64, now - last);
|
|
932
|
+
last = now;
|
|
933
|
+
const frames = dt / (1e3 / 60);
|
|
934
|
+
const gap = target.length - countRef.current;
|
|
935
|
+
const step = Math.max(MIN_CHARS_PER_FRAME * frames, gap * CATCH_UP * frames);
|
|
936
|
+
const next = Math.min(target.length, countRef.current + step);
|
|
937
|
+
countRef.current = next;
|
|
938
|
+
setCount(next);
|
|
939
|
+
if (next < target.length) raf = requestAnimationFrame(tick);
|
|
940
|
+
};
|
|
941
|
+
raf = requestAnimationFrame(tick);
|
|
942
|
+
return () => cancelAnimationFrame(raf);
|
|
943
|
+
}, [target, enabled]);
|
|
944
|
+
return everEnabled.current ? target.slice(0, Math.floor(count)) : target;
|
|
945
|
+
}
|
|
895
946
|
function CopyButton({ className, text }) {
|
|
896
|
-
const [copied, setCopied] =
|
|
897
|
-
const timerRef =
|
|
898
|
-
|
|
947
|
+
const [copied, setCopied] = React12.useState(false);
|
|
948
|
+
const timerRef = React12.useRef(null);
|
|
949
|
+
React12.useEffect(() => {
|
|
899
950
|
return () => {
|
|
900
951
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
901
952
|
};
|
|
902
953
|
}, []);
|
|
903
|
-
const copy =
|
|
954
|
+
const copy = React12.useCallback(async () => {
|
|
904
955
|
try {
|
|
905
956
|
await navigator.clipboard.writeText(text);
|
|
906
957
|
} catch {
|
|
@@ -932,8 +983,9 @@ function CopyButton({ className, text }) {
|
|
|
932
983
|
] });
|
|
933
984
|
}
|
|
934
985
|
var isImageAttachment = (format) => IMAGE_ACCEPT.includes(`.${format.toLowerCase()}`);
|
|
935
|
-
function MessageItem({ message }) {
|
|
936
|
-
const [lightboxSrc, setLightboxSrc] =
|
|
986
|
+
function MessageItem({ animate = false, message }) {
|
|
987
|
+
const [lightboxSrc, setLightboxSrc] = React12.useState(null);
|
|
988
|
+
const displayed = useSmoothText(message.content ?? "", animate && message.role === "assistant");
|
|
937
989
|
if (message.role === "user") {
|
|
938
990
|
const attachments = message.attachments ?? [];
|
|
939
991
|
const images = attachments.filter((a) => isImageAttachment(a.format) && a.url);
|
|
@@ -989,7 +1041,7 @@ function MessageItem({ message }) {
|
|
|
989
1041
|
if (src) setLightboxSrc(src);
|
|
990
1042
|
},
|
|
991
1043
|
role: "presentation",
|
|
992
|
-
children: /* @__PURE__ */ jsx(Markdown, { children:
|
|
1044
|
+
children: /* @__PURE__ */ jsx(Markdown, { children: displayed })
|
|
993
1045
|
}
|
|
994
1046
|
) }),
|
|
995
1047
|
/* @__PURE__ */ jsx(
|
|
@@ -1017,8 +1069,8 @@ var DEFAULT_PHRASES = [
|
|
|
1017
1069
|
];
|
|
1018
1070
|
function ThinkingIndicator({ className, intervalMs = 1800, phrases }) {
|
|
1019
1071
|
const words = phrases && phrases.length ? phrases : DEFAULT_PHRASES;
|
|
1020
|
-
const [index, setIndex] =
|
|
1021
|
-
|
|
1072
|
+
const [index, setIndex] = React12.useState(0);
|
|
1073
|
+
React12.useEffect(() => {
|
|
1022
1074
|
setIndex(0);
|
|
1023
1075
|
const timer = setInterval(() => {
|
|
1024
1076
|
setIndex((current) => (current + 1) % words.length);
|
|
@@ -1037,14 +1089,7 @@ function ThinkingIndicator({ className, intervalMs = 1800, phrases }) {
|
|
|
1037
1089
|
role: "status",
|
|
1038
1090
|
children: [
|
|
1039
1091
|
/* @__PURE__ */ jsx(ThinkingOrb, { className: "shrink-0", size: 20, speed: 0.7, state: "solving" }),
|
|
1040
|
-
/* @__PURE__ */ jsx(
|
|
1041
|
-
"span",
|
|
1042
|
-
{
|
|
1043
|
-
className: "fade-in-0 animate-in text-xs font-medium duration-300",
|
|
1044
|
-
children: /* @__PURE__ */ jsx(AuroraText, { children: words[index] })
|
|
1045
|
-
},
|
|
1046
|
-
index
|
|
1047
|
-
)
|
|
1092
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs font-medium", children: /* @__PURE__ */ jsx(AuroraText, { speed: 2.2, children: /* @__PURE__ */ jsx("span", { className: "fade-in-0 inline-block animate-in duration-300", children: words[index] }, index) }) })
|
|
1048
1093
|
]
|
|
1049
1094
|
}
|
|
1050
1095
|
);
|
|
@@ -1273,7 +1318,7 @@ function DownloadButton({ block, size = "sm" }) {
|
|
|
1273
1318
|
);
|
|
1274
1319
|
}
|
|
1275
1320
|
function DocumentViewer({ block, onClose }) {
|
|
1276
|
-
|
|
1321
|
+
React12.useEffect(() => {
|
|
1277
1322
|
const onKey = (event) => event.key === "Escape" && onClose();
|
|
1278
1323
|
window.addEventListener("keydown", onKey);
|
|
1279
1324
|
return () => window.removeEventListener("keydown", onKey);
|
|
@@ -1298,7 +1343,7 @@ function DocumentViewer({ block, onClose }) {
|
|
|
1298
1343
|
] });
|
|
1299
1344
|
}
|
|
1300
1345
|
function DocumentCard({ block }) {
|
|
1301
|
-
const [open, setOpen] =
|
|
1346
|
+
const [open, setOpen] = React12.useState(false);
|
|
1302
1347
|
const previewLines = (block.preview || "").split("\n").slice(0, 6).join("\n");
|
|
1303
1348
|
return /* @__PURE__ */ jsxs("div", { className: "w-full max-w-full overflow-hidden rounded-xl border border-border bg-card shadow-xs", children: [
|
|
1304
1349
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 border-border/72 border-b px-3 py-2", children: [
|
|
@@ -1420,7 +1465,7 @@ function StepDetail({
|
|
|
1420
1465
|
] });
|
|
1421
1466
|
}
|
|
1422
1467
|
function ToolActivityGroup({ describeToolCall, steps }) {
|
|
1423
|
-
const [open, setOpen] =
|
|
1468
|
+
const [open, setOpen] = React12.useState(false);
|
|
1424
1469
|
if (steps.length === 0) return null;
|
|
1425
1470
|
const last = steps[steps.length - 1];
|
|
1426
1471
|
const anyRunning = steps.some((step) => step.status === "running");
|
|
@@ -1525,12 +1570,12 @@ function AgentPanel({
|
|
|
1525
1570
|
onClose,
|
|
1526
1571
|
onToggleAgentMode
|
|
1527
1572
|
}) {
|
|
1528
|
-
const [draft, setDraft] =
|
|
1529
|
-
const [view, setView] =
|
|
1530
|
-
const [files, setFiles] =
|
|
1531
|
-
const [uploadError, setUploadError] =
|
|
1532
|
-
const [dragging, setDragging] =
|
|
1533
|
-
const dragDepth =
|
|
1573
|
+
const [draft, setDraft] = React12.useState("");
|
|
1574
|
+
const [view, setView] = React12.useState("chat");
|
|
1575
|
+
const [files, setFiles] = React12.useState([]);
|
|
1576
|
+
const [uploadError, setUploadError] = React12.useState(null);
|
|
1577
|
+
const [dragging, setDragging] = React12.useState(false);
|
|
1578
|
+
const dragDepth = React12.useRef(0);
|
|
1534
1579
|
const agentName = config.agentName ?? chat.agentInfo?.name ?? "Assistant";
|
|
1535
1580
|
const caps = {
|
|
1536
1581
|
visionEnabled: chat.agentInfo?.vision_enabled,
|
|
@@ -1541,7 +1586,7 @@ function AgentPanel({
|
|
|
1541
1586
|
"What can you help me with?",
|
|
1542
1587
|
"Give me a quick summary of my data."
|
|
1543
1588
|
];
|
|
1544
|
-
const addFiles =
|
|
1589
|
+
const addFiles = React12.useCallback(
|
|
1545
1590
|
(incoming) => {
|
|
1546
1591
|
const accepted = [];
|
|
1547
1592
|
let rejection = null;
|
|
@@ -1558,10 +1603,10 @@ function AgentPanel({
|
|
|
1558
1603
|
},
|
|
1559
1604
|
[config, caps.visionEnabled, caps.visionMaxMb]
|
|
1560
1605
|
);
|
|
1561
|
-
const removeFile =
|
|
1606
|
+
const removeFile = React12.useCallback((index) => {
|
|
1562
1607
|
setFiles((current) => current.filter((_, i) => i !== index));
|
|
1563
1608
|
}, []);
|
|
1564
|
-
const handleSubmit =
|
|
1609
|
+
const handleSubmit = React12.useCallback(
|
|
1565
1610
|
(text) => {
|
|
1566
1611
|
const staged = files;
|
|
1567
1612
|
setDraft("");
|
|
@@ -1571,7 +1616,7 @@ function AgentPanel({
|
|
|
1571
1616
|
},
|
|
1572
1617
|
[chat, files]
|
|
1573
1618
|
);
|
|
1574
|
-
const onDragEnter =
|
|
1619
|
+
const onDragEnter = React12.useCallback(
|
|
1575
1620
|
(event) => {
|
|
1576
1621
|
if (!uploadEnabled || !event.dataTransfer?.types?.includes("Files")) return;
|
|
1577
1622
|
dragDepth.current += 1;
|
|
@@ -1579,12 +1624,12 @@ function AgentPanel({
|
|
|
1579
1624
|
},
|
|
1580
1625
|
[uploadEnabled]
|
|
1581
1626
|
);
|
|
1582
|
-
const onDragLeave =
|
|
1627
|
+
const onDragLeave = React12.useCallback((event) => {
|
|
1583
1628
|
if (!event.dataTransfer?.types?.includes("Files")) return;
|
|
1584
1629
|
dragDepth.current = Math.max(0, dragDepth.current - 1);
|
|
1585
1630
|
if (dragDepth.current === 0) setDragging(false);
|
|
1586
1631
|
}, []);
|
|
1587
|
-
const onDrop =
|
|
1632
|
+
const onDrop = React12.useCallback(
|
|
1588
1633
|
(event) => {
|
|
1589
1634
|
if (!uploadEnabled) return;
|
|
1590
1635
|
event.preventDefault();
|
|
@@ -1595,7 +1640,7 @@ function AgentPanel({
|
|
|
1595
1640
|
},
|
|
1596
1641
|
[addFiles, uploadEnabled]
|
|
1597
1642
|
);
|
|
1598
|
-
const openHistory =
|
|
1643
|
+
const openHistory = React12.useCallback(() => {
|
|
1599
1644
|
setView("history");
|
|
1600
1645
|
void chat.refreshConversations();
|
|
1601
1646
|
}, [chat]);
|
|
@@ -1759,7 +1804,16 @@ function AgentPanel({
|
|
|
1759
1804
|
const anchorKey = newMessageAnchorId ? grouped.find(
|
|
1760
1805
|
(it) => it.kind !== "tool-activity" && it.message.id === newMessageAnchorId
|
|
1761
1806
|
)?.key : void 0;
|
|
1762
|
-
|
|
1807
|
+
let liveAssistantKey = null;
|
|
1808
|
+
if (chat.sending) {
|
|
1809
|
+
for (let i = grouped.length - 1; i >= 0; i -= 1) {
|
|
1810
|
+
if (grouped[i].kind === "assistant-text") {
|
|
1811
|
+
liveAssistantKey = grouped[i].key;
|
|
1812
|
+
break;
|
|
1813
|
+
}
|
|
1814
|
+
}
|
|
1815
|
+
}
|
|
1816
|
+
return grouped.map((item) => /* @__PURE__ */ jsxs(React12.Fragment, { children: [
|
|
1763
1817
|
item.key === anchorKey ? /* @__PURE__ */ jsx(NewMessagesDivider, {}) : null,
|
|
1764
1818
|
/* @__PURE__ */ jsx(
|
|
1765
1819
|
motion.div,
|
|
@@ -1773,7 +1827,7 @@ function AgentPanel({
|
|
|
1773
1827
|
describeToolCall: config.describeToolCall,
|
|
1774
1828
|
steps: item.steps
|
|
1775
1829
|
}
|
|
1776
|
-
) }) : /* @__PURE__ */ jsx(MessageItem, { message: item.message })
|
|
1830
|
+
) }) : /* @__PURE__ */ jsx(MessageItem, { animate: item.key === liveAssistantKey, message: item.message })
|
|
1777
1831
|
}
|
|
1778
1832
|
)
|
|
1779
1833
|
] }, item.key));
|
|
@@ -1859,17 +1913,17 @@ function buildOptimisticUserMessage(text, files) {
|
|
|
1859
1913
|
};
|
|
1860
1914
|
}
|
|
1861
1915
|
function useAgentChat(config) {
|
|
1862
|
-
const client =
|
|
1863
|
-
const [agentInfo, setAgentInfo] =
|
|
1864
|
-
const [conversationId, setConversationId] =
|
|
1865
|
-
const [conversationEpoch, setConversationEpoch] =
|
|
1866
|
-
const [conversations, setConversations] =
|
|
1867
|
-
const [messages, setMessages] =
|
|
1868
|
-
const [pendingApprovals, setPendingApprovals] =
|
|
1869
|
-
const [sending, setSending] =
|
|
1870
|
-
const [resolvingApprovalId, setResolvingApprovalId] =
|
|
1871
|
-
const [error, setError] =
|
|
1872
|
-
|
|
1916
|
+
const client = React12.useMemo(() => new AgentApiClient(config), [config]);
|
|
1917
|
+
const [agentInfo, setAgentInfo] = React12.useState(null);
|
|
1918
|
+
const [conversationId, setConversationId] = React12.useState(null);
|
|
1919
|
+
const [conversationEpoch, setConversationEpoch] = React12.useState(0);
|
|
1920
|
+
const [conversations, setConversations] = React12.useState([]);
|
|
1921
|
+
const [messages, setMessages] = React12.useState([]);
|
|
1922
|
+
const [pendingApprovals, setPendingApprovals] = React12.useState([]);
|
|
1923
|
+
const [sending, setSending] = React12.useState(false);
|
|
1924
|
+
const [resolvingApprovalId, setResolvingApprovalId] = React12.useState(null);
|
|
1925
|
+
const [error, setError] = React12.useState(null);
|
|
1926
|
+
React12.useEffect(() => {
|
|
1873
1927
|
let cancelled = false;
|
|
1874
1928
|
client.getConfig().then((info) => {
|
|
1875
1929
|
if (!cancelled) setAgentInfo(info);
|
|
@@ -1879,11 +1933,11 @@ function useAgentChat(config) {
|
|
|
1879
1933
|
cancelled = true;
|
|
1880
1934
|
};
|
|
1881
1935
|
}, [client]);
|
|
1882
|
-
const handleFailure =
|
|
1936
|
+
const handleFailure = React12.useCallback((err) => {
|
|
1883
1937
|
const message = err instanceof AgentApiError ? err.message : "Something went wrong. Please try again.";
|
|
1884
1938
|
setError(message);
|
|
1885
1939
|
}, []);
|
|
1886
|
-
const applyResponse =
|
|
1940
|
+
const applyResponse = React12.useCallback(
|
|
1887
1941
|
(response) => {
|
|
1888
1942
|
if (response.status === "error" && !response.messages?.length) {
|
|
1889
1943
|
setError(response.message ?? "The assistant could not answer.");
|
|
@@ -1913,7 +1967,7 @@ function useAgentChat(config) {
|
|
|
1913
1967
|
},
|
|
1914
1968
|
[]
|
|
1915
1969
|
);
|
|
1916
|
-
const handleStreamEvent =
|
|
1970
|
+
const handleStreamEvent = React12.useCallback(
|
|
1917
1971
|
(event, ctx) => {
|
|
1918
1972
|
switch (event.type) {
|
|
1919
1973
|
case "conversation":
|
|
@@ -1992,7 +2046,7 @@ function useAgentChat(config) {
|
|
|
1992
2046
|
[]
|
|
1993
2047
|
);
|
|
1994
2048
|
const streamingEnabled = config.streaming !== false && typeof ReadableStream !== "undefined";
|
|
1995
|
-
const send =
|
|
2049
|
+
const send = React12.useCallback(
|
|
1996
2050
|
async (text, files) => {
|
|
1997
2051
|
if (sending) return;
|
|
1998
2052
|
if (!text.trim() && !(files && files.length)) return;
|
|
@@ -2023,14 +2077,14 @@ function useAgentChat(config) {
|
|
|
2023
2077
|
},
|
|
2024
2078
|
[applyResponse, client, conversationId, handleFailure, handleStreamEvent, sending, streamingEnabled]
|
|
2025
2079
|
);
|
|
2026
|
-
const transcribe =
|
|
2080
|
+
const transcribe = React12.useCallback(
|
|
2027
2081
|
async (audio) => {
|
|
2028
2082
|
const { text } = await client.transcribe(audio);
|
|
2029
2083
|
return text;
|
|
2030
2084
|
},
|
|
2031
2085
|
[client]
|
|
2032
2086
|
);
|
|
2033
|
-
const resolveApproval =
|
|
2087
|
+
const resolveApproval = React12.useCallback(
|
|
2034
2088
|
async (callId, approve) => {
|
|
2035
2089
|
if (resolvingApprovalId) return;
|
|
2036
2090
|
setResolvingApprovalId(callId);
|
|
@@ -2059,14 +2113,14 @@ function useAgentChat(config) {
|
|
|
2059
2113
|
},
|
|
2060
2114
|
[applyResponse, client, handleFailure, handleStreamEvent, resolvingApprovalId, streamingEnabled]
|
|
2061
2115
|
);
|
|
2062
|
-
const newChat =
|
|
2116
|
+
const newChat = React12.useCallback(() => {
|
|
2063
2117
|
setConversationId(null);
|
|
2064
2118
|
setMessages([]);
|
|
2065
2119
|
setPendingApprovals([]);
|
|
2066
2120
|
setError(null);
|
|
2067
2121
|
setConversationEpoch((e) => e + 1);
|
|
2068
2122
|
}, []);
|
|
2069
|
-
const refreshConversations =
|
|
2123
|
+
const refreshConversations = React12.useCallback(async () => {
|
|
2070
2124
|
try {
|
|
2071
2125
|
const { conversations: list } = await client.listConversations();
|
|
2072
2126
|
setConversations(list);
|
|
@@ -2076,7 +2130,7 @@ function useAgentChat(config) {
|
|
|
2076
2130
|
return [];
|
|
2077
2131
|
}
|
|
2078
2132
|
}, [client, handleFailure]);
|
|
2079
|
-
const openConversation =
|
|
2133
|
+
const openConversation = React12.useCallback(
|
|
2080
2134
|
async (id) => {
|
|
2081
2135
|
setError(null);
|
|
2082
2136
|
try {
|
|
@@ -2091,7 +2145,7 @@ function useAgentChat(config) {
|
|
|
2091
2145
|
},
|
|
2092
2146
|
[client, handleFailure]
|
|
2093
2147
|
);
|
|
2094
|
-
const deleteConversation =
|
|
2148
|
+
const deleteConversation = React12.useCallback(
|
|
2095
2149
|
async (id) => {
|
|
2096
2150
|
try {
|
|
2097
2151
|
await client.deleteConversation(id);
|
|
@@ -2103,7 +2157,7 @@ function useAgentChat(config) {
|
|
|
2103
2157
|
},
|
|
2104
2158
|
[client, conversationId, handleFailure, newChat]
|
|
2105
2159
|
);
|
|
2106
|
-
const clearError =
|
|
2160
|
+
const clearError = React12.useCallback(() => setError(null), []);
|
|
2107
2161
|
return {
|
|
2108
2162
|
agentInfo,
|
|
2109
2163
|
clearError,
|
|
@@ -2140,11 +2194,11 @@ function readStored(clientId) {
|
|
|
2140
2194
|
}
|
|
2141
2195
|
}
|
|
2142
2196
|
function useAlwaysApprove(clientId) {
|
|
2143
|
-
const [approved, setApproved] =
|
|
2144
|
-
|
|
2197
|
+
const [approved, setApproved] = React12.useState(() => /* @__PURE__ */ new Set());
|
|
2198
|
+
React12.useEffect(() => {
|
|
2145
2199
|
setApproved(readStored(clientId));
|
|
2146
2200
|
}, [clientId]);
|
|
2147
|
-
const persist =
|
|
2201
|
+
const persist = React12.useCallback(
|
|
2148
2202
|
(next) => {
|
|
2149
2203
|
setApproved(next);
|
|
2150
2204
|
if (typeof window === "undefined") return;
|
|
@@ -2155,8 +2209,8 @@ function useAlwaysApprove(clientId) {
|
|
|
2155
2209
|
},
|
|
2156
2210
|
[clientId]
|
|
2157
2211
|
);
|
|
2158
|
-
const isAlwaysApproved =
|
|
2159
|
-
const setAlwaysApprove =
|
|
2212
|
+
const isAlwaysApproved = React12.useCallback((toolName) => approved.has(toolName), [approved]);
|
|
2213
|
+
const setAlwaysApprove = React12.useCallback(
|
|
2160
2214
|
(toolName) => {
|
|
2161
2215
|
if (approved.has(toolName)) return;
|
|
2162
2216
|
const next = new Set(approved);
|
|
@@ -2165,7 +2219,7 @@ function useAlwaysApprove(clientId) {
|
|
|
2165
2219
|
},
|
|
2166
2220
|
[approved, persist]
|
|
2167
2221
|
);
|
|
2168
|
-
const clearAlwaysApprove =
|
|
2222
|
+
const clearAlwaysApprove = React12.useCallback(
|
|
2169
2223
|
(toolName) => {
|
|
2170
2224
|
if (!approved.has(toolName)) return;
|
|
2171
2225
|
const next = new Set(approved);
|
|
@@ -2191,10 +2245,10 @@ function AgentWidget({
|
|
|
2191
2245
|
const isControlled = controlledOpen !== void 0;
|
|
2192
2246
|
const isDocked = presentation === "docked";
|
|
2193
2247
|
const storageKey2 = `cc-agent-open:${config.clientId}`;
|
|
2194
|
-
const [uncontrolledOpen, setUncontrolledOpen] =
|
|
2248
|
+
const [uncontrolledOpen, setUncontrolledOpen] = React12.useState(false);
|
|
2195
2249
|
const open = controlledOpen ?? uncontrolledOpen;
|
|
2196
|
-
const [bootstrapDone, setBootstrapDone] =
|
|
2197
|
-
const persist =
|
|
2250
|
+
const [bootstrapDone, setBootstrapDone] = React12.useState(isControlled);
|
|
2251
|
+
const persist = React12.useCallback(
|
|
2198
2252
|
(next) => {
|
|
2199
2253
|
if (isControlled || !behavior.persistOpenState) return;
|
|
2200
2254
|
try {
|
|
@@ -2204,7 +2258,7 @@ function AgentWidget({
|
|
|
2204
2258
|
},
|
|
2205
2259
|
[behavior.persistOpenState, isControlled, storageKey2]
|
|
2206
2260
|
);
|
|
2207
|
-
const setOpen =
|
|
2261
|
+
const setOpen = React12.useCallback(
|
|
2208
2262
|
(next) => {
|
|
2209
2263
|
setUncontrolledOpen(next);
|
|
2210
2264
|
persist(next);
|
|
@@ -2212,14 +2266,14 @@ function AgentWidget({
|
|
|
2212
2266
|
},
|
|
2213
2267
|
[onOpenChange, persist]
|
|
2214
2268
|
);
|
|
2215
|
-
const [agentMode, setAgentMode] =
|
|
2269
|
+
const [agentMode, setAgentMode] = React12.useState(false);
|
|
2216
2270
|
const isDesktop = useMediaQuery("(min-width: 640px)", { defaultSSRValue: true, ssr: true });
|
|
2217
2271
|
const fullscreen = !isDesktop || agentMode;
|
|
2218
2272
|
const chat = useAgentChat(config);
|
|
2219
2273
|
const alwaysApprove = useAlwaysApprove(config.clientId);
|
|
2220
2274
|
const { messages, pendingApprovals, resolveApproval, resolvingApprovalId, sending } = chat;
|
|
2221
|
-
const bootRef =
|
|
2222
|
-
|
|
2275
|
+
const bootRef = React12.useRef(false);
|
|
2276
|
+
React12.useEffect(() => {
|
|
2223
2277
|
if (bootRef.current) return;
|
|
2224
2278
|
if (isControlled && !isDocked) return;
|
|
2225
2279
|
bootRef.current = true;
|
|
@@ -2251,15 +2305,15 @@ function AgentWidget({
|
|
|
2251
2305
|
cancelled = true;
|
|
2252
2306
|
};
|
|
2253
2307
|
}, []);
|
|
2254
|
-
const seenCountRef =
|
|
2255
|
-
const [baselined, setBaselined] =
|
|
2256
|
-
const [anchorId, setAnchorId] =
|
|
2257
|
-
|
|
2308
|
+
const seenCountRef = React12.useRef(0);
|
|
2309
|
+
const [baselined, setBaselined] = React12.useState(false);
|
|
2310
|
+
const [anchorId, setAnchorId] = React12.useState(null);
|
|
2311
|
+
React12.useEffect(() => {
|
|
2258
2312
|
if (baselined || !bootstrapDone) return;
|
|
2259
2313
|
seenCountRef.current = messages.length;
|
|
2260
2314
|
setBaselined(true);
|
|
2261
2315
|
}, [baselined, bootstrapDone, messages.length]);
|
|
2262
|
-
|
|
2316
|
+
React12.useEffect(() => {
|
|
2263
2317
|
if (open) {
|
|
2264
2318
|
if (messages.length > seenCountRef.current) {
|
|
2265
2319
|
const firstUnread = messages[seenCountRef.current];
|
|
@@ -2269,7 +2323,7 @@ function AgentWidget({
|
|
|
2269
2323
|
}
|
|
2270
2324
|
}, [open, messages]);
|
|
2271
2325
|
const unreadCount = open || !baselined ? 0 : messages.slice(seenCountRef.current).filter(isUnreadable).length;
|
|
2272
|
-
const workLabel =
|
|
2326
|
+
const workLabel = React12.useMemo(() => {
|
|
2273
2327
|
if (!sending) return null;
|
|
2274
2328
|
const last = messages[messages.length - 1];
|
|
2275
2329
|
if (last?.role === "tool") return "Calling tools\u2026";
|
|
@@ -2278,14 +2332,14 @@ function AgentWidget({
|
|
|
2278
2332
|
}, [sending, messages]);
|
|
2279
2333
|
const approvalWaiting = pendingApprovals.length > 0;
|
|
2280
2334
|
const { isAlwaysApproved } = alwaysApprove;
|
|
2281
|
-
|
|
2335
|
+
React12.useEffect(() => {
|
|
2282
2336
|
if (resolvingApprovalId) return;
|
|
2283
2337
|
const target = pendingApprovals.find((approval) => isAlwaysApproved(approval.tool_name));
|
|
2284
2338
|
if (target) void resolveApproval(target.id, true);
|
|
2285
2339
|
}, [pendingApprovals, resolvingApprovalId, isAlwaysApproved, resolveApproval]);
|
|
2286
|
-
const firedWriteIds =
|
|
2340
|
+
const firedWriteIds = React12.useRef(/* @__PURE__ */ new Set());
|
|
2287
2341
|
const { onWriteSuccess } = config;
|
|
2288
|
-
|
|
2342
|
+
React12.useEffect(() => {
|
|
2289
2343
|
if (!onWriteSuccess) return;
|
|
2290
2344
|
for (const message of messages) {
|
|
2291
2345
|
if (message.role !== "tool" || !message.is_write || message.tool_status !== "success") continue;
|
|
@@ -2295,9 +2349,9 @@ function AgentWidget({
|
|
|
2295
2349
|
onWriteSuccess({ arguments: args, toolName: message.tool_name ?? "" });
|
|
2296
2350
|
}
|
|
2297
2351
|
}, [messages, onWriteSuccess]);
|
|
2298
|
-
const lastNavigatedMessageId =
|
|
2299
|
-
const navEpochRef =
|
|
2300
|
-
|
|
2352
|
+
const lastNavigatedMessageId = React12.useRef(null);
|
|
2353
|
+
const navEpochRef = React12.useRef(-1);
|
|
2354
|
+
React12.useEffect(() => {
|
|
2301
2355
|
if (!open || fullscreen || !config.navigate || !config.pages?.length) return;
|
|
2302
2356
|
const toolMessages = messages.filter((message) => message.role === "tool");
|
|
2303
2357
|
const latest = toolMessages[toolMessages.length - 1];
|
|
@@ -2316,7 +2370,7 @@ function AgentWidget({
|
|
|
2316
2370
|
const scrollTo = typeof mapping.scrollTo === "function" ? mapping.scrollTo(args) : mapping.scrollTo;
|
|
2317
2371
|
config.navigate(path, { scrollTo: scrollTo ?? null });
|
|
2318
2372
|
}, [chat.conversationEpoch, config, fullscreen, messages, open]);
|
|
2319
|
-
const closePanel =
|
|
2373
|
+
const closePanel = React12.useCallback(() => {
|
|
2320
2374
|
setOpen(false);
|
|
2321
2375
|
setAgentMode(false);
|
|
2322
2376
|
}, [setOpen]);
|
|
@@ -2489,6 +2543,6 @@ function defineAgentConfig(config) {
|
|
|
2489
2543
|
return config;
|
|
2490
2544
|
}
|
|
2491
2545
|
|
|
2492
|
-
export { AgentApiClient, AgentApiError, AgentComposer, AgentImageLightbox, AgentPanel, AgentVoiceRecorder, AgentWidget, ChatScroller, CopyButton, DEFAULT_ACCEPT, DocumentCard, IMAGE_ACCEPT, MessageItem, TaskChecklist, ThinkingIndicator, ToolActivityGroup, ToolApprovalCard, acceptAttribute, acceptedExtensions, attachmentsEnabled, defineAgentConfig, deriveDetails, fileUploadEnabled, formatBytes, groupMessages, humanizeToolName, isImageFile, resolveToolPresentation, textExtensions, useAgentChat, useAlwaysApprove, validateFile };
|
|
2546
|
+
export { AgentApiClient, AgentApiError, AgentComposer, AgentImageLightbox, AgentPanel, AgentVoiceRecorder, AgentWidget, ChatScroller, CopyButton, DEFAULT_ACCEPT, DocumentCard, IMAGE_ACCEPT, MessageItem, TaskChecklist, ThinkingIndicator, ToolActivityGroup, ToolApprovalCard, acceptAttribute, acceptedExtensions, attachmentsEnabled, defineAgentConfig, deriveDetails, fileUploadEnabled, formatBytes, groupMessages, humanizeToolName, isImageFile, resolveToolPresentation, textExtensions, useAgentChat, useAlwaysApprove, useSmoothText, validateFile };
|
|
2493
2547
|
//# sourceMappingURL=index.js.map
|
|
2494
2548
|
//# sourceMappingURL=index.js.map
|