@agents24/chat-react 0.5.5 → 0.5.7
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/index.cjs +11 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +11 -6
- package/dist/index.js.map +1 -1
- package/dist/latest-thread-scroller.d.ts +2 -1
- package/dist/runtime/index.cjs +17 -11
- package/dist/runtime/index.cjs.map +1 -1
- package/dist/runtime/index.js +17 -11
- package/dist/runtime/index.js.map +1 -1
- package/dist/runtime/use-agent-chat-runtime.d.ts +7 -2
- package/dist/scaffold/components/chat/chat-composer.d.ts +2 -1
- package/dist/scaffold/components/chat/chat-message.d.ts +3 -1
- package/dist/scaffold/components/chat/turn-outline.d.ts +1 -1
- package/dist/scaffold/index.cjs +195 -154
- package/dist/scaffold/index.cjs.map +1 -1
- package/dist/scaffold/index.js +215 -168
- package/dist/scaffold/index.js.map +1 -1
- package/dist/styles.css +25 -6
- package/dist/ui/adapters.d.ts +14 -2
- package/dist/ui/agent-chat-composer.d.ts +2 -9
- package/dist/ui/agent-chat-message.d.ts +7 -1
- package/dist/ui/composer-attachments.d.ts +11 -0
- package/dist/ui/index.cjs +781 -389
- package/dist/ui/index.cjs.map +1 -1
- package/dist/ui/index.d.ts +1 -0
- package/dist/ui/index.js +766 -375
- package/dist/ui/index.js.map +1 -1
- package/package.json +4 -3
- package/scaffold/src/components/chat/chat-composer.tsx +43 -19
- package/scaffold/src/components/chat/chat-message.tsx +19 -10
- package/scaffold/src/components/chat/chat-shell.tsx +65 -34
- package/scaffold/src/components/chat/turn-outline.tsx +17 -59
- package/scaffold/src/components/ui/message-scroller.tsx +1 -1
- package/scaffold/src/components/ui/message.tsx +1 -1
- package/scaffold/src/components/ui/sidebar.tsx +1 -1
package/dist/scaffold/index.js
CHANGED
|
@@ -10,12 +10,19 @@ import {
|
|
|
10
10
|
import {
|
|
11
11
|
agentChatRuntimeErrorMessage
|
|
12
12
|
} from "@agents24/chat-react/runtime";
|
|
13
|
-
import { CircleAlert, LoaderCircle as LoaderCircle3, SquarePen as SquarePen2, X
|
|
13
|
+
import { CircleAlert, LoaderCircle as LoaderCircle3, SquarePen as SquarePen2, X } from "lucide-react";
|
|
14
|
+
import * as React8 from "react";
|
|
14
15
|
|
|
15
16
|
// scaffold/src/components/chat/chat-composer.tsx
|
|
16
|
-
import { ArrowUp, ChevronDown, LoaderCircle, Mic, Square
|
|
17
|
+
import { ArrowUp, ChevronDown, LoaderCircle, Mic, Square } from "lucide-react";
|
|
17
18
|
import * as React2 from "react";
|
|
18
|
-
import {
|
|
19
|
+
import {
|
|
20
|
+
AgentChatContextStatus,
|
|
21
|
+
ChatAttachmentRows,
|
|
22
|
+
createAgentChatComposerFile,
|
|
23
|
+
revokeAgentChatComposerFiles,
|
|
24
|
+
useAudioRecorder
|
|
25
|
+
} from "@agents24/chat-react/ui";
|
|
19
26
|
import { canonicalInputMimeType, inputMimeTypes, validateAgentChatSubmission } from "@agents24/chat-react/runtime";
|
|
20
27
|
|
|
21
28
|
// scaffold/src/components/ui/button.tsx
|
|
@@ -491,11 +498,20 @@ function ChatComposer({
|
|
|
491
498
|
const [attachmentError, setAttachmentError] = React2.useState(null);
|
|
492
499
|
const inputRef = React2.useRef(null);
|
|
493
500
|
const textareaRef = React2.useRef(null);
|
|
501
|
+
const filesRef = React2.useRef([]);
|
|
502
|
+
const inFlightFilesRef = React2.useRef([]);
|
|
494
503
|
const submittingRef = React2.useRef(false);
|
|
495
504
|
const draftRevisionRef = React2.useRef(0);
|
|
496
505
|
const attachmentMimeTypes = inputMimeTypes(inputContract);
|
|
497
506
|
const allowAttachments = attachmentMimeTypes.length > 0;
|
|
498
507
|
const recordingAllowed = inputContract.modalities.audio.recording_enabled;
|
|
508
|
+
React2.useEffect(() => {
|
|
509
|
+
filesRef.current = files;
|
|
510
|
+
}, [files]);
|
|
511
|
+
React2.useEffect(() => () => {
|
|
512
|
+
revokeAgentChatComposerFiles(filesRef.current);
|
|
513
|
+
revokeAgentChatComposerFiles(inFlightFilesRef.current);
|
|
514
|
+
}, []);
|
|
499
515
|
React2.useEffect(() => {
|
|
500
516
|
if (disabled || isRunning) return;
|
|
501
517
|
const frame = window.requestAnimationFrame(() => textareaRef.current?.focus({ preventScroll: true }));
|
|
@@ -503,14 +519,14 @@ function ChatComposer({
|
|
|
503
519
|
}, [disabled, focusKey, isRunning]);
|
|
504
520
|
const addRecording = React2.useCallback((file) => {
|
|
505
521
|
draftRevisionRef.current += 1;
|
|
506
|
-
setFiles((current) => [...current, file]);
|
|
522
|
+
setFiles((current) => [...current, createAgentChatComposerFile(file)]);
|
|
507
523
|
}, []);
|
|
508
524
|
const recorder = useAudioRecorder(addRecording);
|
|
509
525
|
const isExpanded = forceExpanded || text.includes("\n") || text.length > 62;
|
|
510
526
|
const validationError = validateAgentChatSubmission(
|
|
511
527
|
inputContract,
|
|
512
528
|
text,
|
|
513
|
-
files.map((file) => ({ data: file, mediaType: file.
|
|
529
|
+
files.map((file) => ({ data: file.source, mediaType: file.mediaType }))
|
|
514
530
|
);
|
|
515
531
|
const selectedAgent = agents.find((item) => item.id === agentId);
|
|
516
532
|
const selectedModel = models.find((item) => item.id === modelId);
|
|
@@ -558,16 +574,23 @@ function ChatComposer({
|
|
|
558
574
|
const clearedRevision = draftRevisionRef.current + 1;
|
|
559
575
|
draftRevisionRef.current = clearedRevision;
|
|
560
576
|
submittingRef.current = true;
|
|
577
|
+
inFlightFilesRef.current = selectedFiles;
|
|
561
578
|
setText("");
|
|
562
579
|
setFiles([]);
|
|
563
580
|
textareaRef.current?.focus();
|
|
564
581
|
try {
|
|
565
582
|
await onSubmit(value, selectedFiles);
|
|
583
|
+
revokeAgentChatComposerFiles(selectedFiles);
|
|
584
|
+
inFlightFilesRef.current = [];
|
|
566
585
|
} catch {
|
|
567
586
|
if (draftRevisionRef.current === clearedRevision) {
|
|
568
587
|
draftRevisionRef.current += 1;
|
|
569
588
|
setText(value);
|
|
570
589
|
setFiles(selectedFiles);
|
|
590
|
+
inFlightFilesRef.current = [];
|
|
591
|
+
} else {
|
|
592
|
+
revokeAgentChatComposerFiles(selectedFiles);
|
|
593
|
+
inFlightFilesRef.current = [];
|
|
571
594
|
}
|
|
572
595
|
} finally {
|
|
573
596
|
submittingRef.current = false;
|
|
@@ -579,13 +602,21 @@ function ChatComposer({
|
|
|
579
602
|
className: "mx-auto w-full max-w-3xl px-4 pb-[max(1rem,env(safe-area-inset-bottom))] sm:px-6",
|
|
580
603
|
"data-agents24-chat-composer": "",
|
|
581
604
|
children: [
|
|
582
|
-
allowAttachments && files.length > 0 && /* @__PURE__ */ jsx7("div", { className: "mb-2
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
605
|
+
allowAttachments && files.length > 0 && /* @__PURE__ */ jsx7("div", { className: "mb-2 min-w-0", children: /* @__PURE__ */ jsx7(
|
|
606
|
+
ChatAttachmentRows,
|
|
607
|
+
{
|
|
608
|
+
attachments: files,
|
|
609
|
+
onRemove: (attachment) => {
|
|
610
|
+
if (!attachment.id) return;
|
|
611
|
+
draftRevisionRef.current += 1;
|
|
612
|
+
setFiles((current) => {
|
|
613
|
+
const removed = current.find((file) => file.id === attachment.id);
|
|
614
|
+
if (removed) revokeAgentChatComposerFiles([removed]);
|
|
615
|
+
return current.filter((file) => file.id !== attachment.id);
|
|
616
|
+
});
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
) }),
|
|
589
620
|
attachmentError || recorder.error ? /* @__PURE__ */ jsx7("p", { className: "mb-2 text-xs text-destructive", role: "alert", children: attachmentError || recorder.error }) : null,
|
|
590
621
|
/* @__PURE__ */ jsxs3(
|
|
591
622
|
"div",
|
|
@@ -609,7 +640,7 @@ function ChatComposer({
|
|
|
609
640
|
const selected = Array.from(event.target.files ?? []);
|
|
610
641
|
const accepted = selected.filter((file) => attachmentMimeTypes.includes(canonicalInputMimeType(file.type)));
|
|
611
642
|
draftRevisionRef.current += 1;
|
|
612
|
-
setFiles((current) => [...current, ...accepted]);
|
|
643
|
+
setFiles((current) => [...current, ...accepted.map(createAgentChatComposerFile)]);
|
|
613
644
|
setAttachmentError(
|
|
614
645
|
accepted.length === selected.length ? null : "One or more selected file types are not supported by this Agent."
|
|
615
646
|
);
|
|
@@ -696,7 +727,7 @@ function ChatComposer({
|
|
|
696
727
|
}
|
|
697
728
|
|
|
698
729
|
// scaffold/src/components/chat/chat-message.tsx
|
|
699
|
-
import { AgentChatDefaultActions,
|
|
730
|
+
import { AgentChatDefaultActions, AgentChatUserMessageActionRow, ChatAttachmentRows as ChatAttachmentRows2 } from "@agents24/chat-react/ui";
|
|
700
731
|
|
|
701
732
|
// scaffold/src/components/chat/message-parts.tsx
|
|
702
733
|
import {
|
|
@@ -1086,7 +1117,7 @@ function MessageFooter({ className, ...props }) {
|
|
|
1086
1117
|
{
|
|
1087
1118
|
"data-slot": "message-footer",
|
|
1088
1119
|
className: cn(
|
|
1089
|
-
"flex max-w-full min-w-0 items-center
|
|
1120
|
+
"flex max-w-full min-w-0 items-center pl-3.5 text-xs font-medium text-muted-foreground group-has-data-[variant=ghost]/message:px-0 group-data-[align=end]/message:justify-end",
|
|
1090
1121
|
className
|
|
1091
1122
|
),
|
|
1092
1123
|
...props
|
|
@@ -1104,17 +1135,19 @@ function ChatMessage({
|
|
|
1104
1135
|
onHitlAction,
|
|
1105
1136
|
onRegenerate,
|
|
1106
1137
|
onSetFeedback,
|
|
1138
|
+
resolveAttachmentContent,
|
|
1107
1139
|
Timeline
|
|
1108
1140
|
}) {
|
|
1109
1141
|
const isUser = message.role === "user";
|
|
1110
|
-
return /* @__PURE__ */ jsx14(Message, { align: isUser ? "end" : "start", children: /* @__PURE__ */ jsxs8(MessageContent, { children: [
|
|
1111
|
-
isUser && message.attachments?.length ? /* @__PURE__ */ jsx14(
|
|
1112
|
-
|
|
1142
|
+
return /* @__PURE__ */ jsx14(Message, { align: isUser ? "end" : "start", children: /* @__PURE__ */ jsxs8(MessageContent, { className: isUser ? "gap-0" : void 0, children: [
|
|
1143
|
+
isUser && message.attachments?.length ? /* @__PURE__ */ jsx14(
|
|
1144
|
+
ChatAttachmentRows2,
|
|
1113
1145
|
{
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1146
|
+
attachments: message.attachments,
|
|
1147
|
+
className: "mb-2",
|
|
1148
|
+
resolveContent: resolveAttachmentContent
|
|
1149
|
+
}
|
|
1150
|
+
) : null,
|
|
1118
1151
|
/* @__PURE__ */ jsx14(Bubble, { variant: isUser ? "secondary" : "ghost", align: isUser ? "end" : "start", className: isUser ? "max-w-[min(75%,42rem)]" : "max-w-full overflow-visible", children: /* @__PURE__ */ jsx14(BubbleContent, { className: isUser ? "px-4 py-2.5" : "w-full overflow-visible", children: /* @__PURE__ */ jsx14(
|
|
1119
1152
|
MessageParts,
|
|
1120
1153
|
{
|
|
@@ -1125,6 +1158,17 @@ function ChatMessage({
|
|
|
1125
1158
|
Timeline
|
|
1126
1159
|
}
|
|
1127
1160
|
) }) }),
|
|
1161
|
+
isUser && /* @__PURE__ */ jsx14(AgentChatUserMessageActionRow, { children: /* @__PURE__ */ jsx14(
|
|
1162
|
+
AgentChatDefaultActions,
|
|
1163
|
+
{
|
|
1164
|
+
handlers: {
|
|
1165
|
+
onCopy: (content) => {
|
|
1166
|
+
void navigator.clipboard.writeText(content).catch(() => void 0);
|
|
1167
|
+
}
|
|
1168
|
+
},
|
|
1169
|
+
message
|
|
1170
|
+
}
|
|
1171
|
+
) }),
|
|
1128
1172
|
!isUser && message.isFinal !== false && /* @__PURE__ */ jsx14(MessageFooter, { className: "gap-1 opacity-70 transition-opacity group-hover/message:opacity-100", children: /* @__PURE__ */ jsx14(
|
|
1129
1173
|
AgentChatDefaultActions,
|
|
1130
1174
|
{
|
|
@@ -1527,7 +1571,7 @@ function Sidebar({
|
|
|
1527
1571
|
className: cn(
|
|
1528
1572
|
"fixed inset-y-0 z-10 hidden h-svh w-(--sidebar-width) transition-[left,right,width] duration-200 ease-linear data-[side=left]:left-0 data-[side=left]:group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)] data-[side=right]:right-0 data-[side=right]:group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)] md:flex",
|
|
1529
1573
|
// Adjust the padding for floating and inset variants.
|
|
1530
|
-
variant === "floating" || variant === "inset" ? "p-2 group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4))+2px)]" : "group-data-[collapsible=icon]:w-(--sidebar-width-icon) group-data-[side=left]:border-r group-data-[side=right]:border-l",
|
|
1574
|
+
variant === "floating" || variant === "inset" ? "p-2 group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4))+2px)]" : "border-border/50 group-data-[collapsible=icon]:w-(--sidebar-width-icon) group-data-[side=left]:border-r group-data-[side=right]:border-l",
|
|
1531
1575
|
className
|
|
1532
1576
|
),
|
|
1533
1577
|
...props,
|
|
@@ -1926,7 +1970,47 @@ function ThreadSidebar({ activeThreadId, hasMore, identity, isLoading, isLoading
|
|
|
1926
1970
|
}
|
|
1927
1971
|
|
|
1928
1972
|
// scaffold/src/components/chat/turn-outline.tsx
|
|
1929
|
-
import {
|
|
1973
|
+
import { LatestThreadScroller } from "@agents24/chat-react";
|
|
1974
|
+
import { jsx as jsx23 } from "react/jsx-runtime";
|
|
1975
|
+
function turnsFromMessages(messages) {
|
|
1976
|
+
return messages.reduce((turns, message) => {
|
|
1977
|
+
if (message.role === "user") {
|
|
1978
|
+
turns.push({
|
|
1979
|
+
id: message.id,
|
|
1980
|
+
label: message.content.trim() || `Turn ${turns.length + 1}`,
|
|
1981
|
+
messageIds: [message.id],
|
|
1982
|
+
responsePreview: ""
|
|
1983
|
+
});
|
|
1984
|
+
} else if (turns.length) {
|
|
1985
|
+
const turn = turns[turns.length - 1];
|
|
1986
|
+
turn.messageIds?.push(message.id);
|
|
1987
|
+
if (!turn.responsePreview) turn.responsePreview = message.content.trim();
|
|
1988
|
+
}
|
|
1989
|
+
return turns;
|
|
1990
|
+
}, []);
|
|
1991
|
+
}
|
|
1992
|
+
function TurnOutline({ messages }) {
|
|
1993
|
+
return /* @__PURE__ */ jsx23(
|
|
1994
|
+
LatestThreadScroller.Outline,
|
|
1995
|
+
{
|
|
1996
|
+
items: turnsFromMessages(messages),
|
|
1997
|
+
minSideGutterForRail: 0,
|
|
1998
|
+
side: "left",
|
|
1999
|
+
verticalOffset: "3.5rem"
|
|
2000
|
+
}
|
|
2001
|
+
);
|
|
2002
|
+
}
|
|
2003
|
+
|
|
2004
|
+
// scaffold/src/components/chat/timeline-loading.ts
|
|
2005
|
+
function isScaffoldTimelineLoading({
|
|
2006
|
+
activeRunId,
|
|
2007
|
+
message,
|
|
2008
|
+
runState,
|
|
2009
|
+
streamingMessageId
|
|
2010
|
+
}) {
|
|
2011
|
+
if (runState !== "streaming" && runState !== "reconnecting") return false;
|
|
2012
|
+
return message.id === streamingMessageId || Boolean(message.runId && message.runId === activeRunId);
|
|
2013
|
+
}
|
|
1930
2014
|
|
|
1931
2015
|
// scaffold/src/components/ui/message-scroller.tsx
|
|
1932
2016
|
import {
|
|
@@ -1936,15 +2020,15 @@ import {
|
|
|
1936
2020
|
useMessageScrollerVisibility
|
|
1937
2021
|
} from "@shadcn/react/message-scroller";
|
|
1938
2022
|
import { ArrowDownIcon } from "lucide-react";
|
|
1939
|
-
import { Fragment as Fragment3, jsx as
|
|
2023
|
+
import { Fragment as Fragment3, jsx as jsx24, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
1940
2024
|
function MessageScrollerProvider(props) {
|
|
1941
|
-
return /* @__PURE__ */
|
|
2025
|
+
return /* @__PURE__ */ jsx24(MessageScrollerPrimitive.Provider, { ...props });
|
|
1942
2026
|
}
|
|
1943
2027
|
function MessageScroller({
|
|
1944
2028
|
className,
|
|
1945
2029
|
...props
|
|
1946
2030
|
}) {
|
|
1947
|
-
return /* @__PURE__ */
|
|
2031
|
+
return /* @__PURE__ */ jsx24(
|
|
1948
2032
|
MessageScrollerPrimitive.Root,
|
|
1949
2033
|
{
|
|
1950
2034
|
"data-slot": "message-scroller",
|
|
@@ -1960,7 +2044,7 @@ function MessageScrollerViewport({
|
|
|
1960
2044
|
className,
|
|
1961
2045
|
...props
|
|
1962
2046
|
}) {
|
|
1963
|
-
return /* @__PURE__ */
|
|
2047
|
+
return /* @__PURE__ */ jsx24(
|
|
1964
2048
|
MessageScrollerPrimitive.Viewport,
|
|
1965
2049
|
{
|
|
1966
2050
|
"data-slot": "message-scroller-viewport",
|
|
@@ -1976,11 +2060,11 @@ function MessageScrollerContent({
|
|
|
1976
2060
|
className,
|
|
1977
2061
|
...props
|
|
1978
2062
|
}) {
|
|
1979
|
-
return /* @__PURE__ */
|
|
2063
|
+
return /* @__PURE__ */ jsx24(
|
|
1980
2064
|
MessageScrollerPrimitive.Content,
|
|
1981
2065
|
{
|
|
1982
2066
|
"data-slot": "message-scroller-content",
|
|
1983
|
-
className: cn("flex h-max min-h-full flex-col gap-
|
|
2067
|
+
className: cn("flex h-max min-h-full flex-col gap-3", className),
|
|
1984
2068
|
...props
|
|
1985
2069
|
}
|
|
1986
2070
|
);
|
|
@@ -1990,7 +2074,7 @@ function MessageScrollerItem({
|
|
|
1990
2074
|
scrollAnchor = false,
|
|
1991
2075
|
...props
|
|
1992
2076
|
}) {
|
|
1993
|
-
return /* @__PURE__ */
|
|
2077
|
+
return /* @__PURE__ */ jsx24(
|
|
1994
2078
|
MessageScrollerPrimitive.Item,
|
|
1995
2079
|
{
|
|
1996
2080
|
"data-slot": "message-scroller-item",
|
|
@@ -2012,7 +2096,7 @@ function MessageScrollerButton({
|
|
|
2012
2096
|
size = "icon-sm",
|
|
2013
2097
|
...props
|
|
2014
2098
|
}) {
|
|
2015
|
-
return /* @__PURE__ */
|
|
2099
|
+
return /* @__PURE__ */ jsx24(
|
|
2016
2100
|
MessageScrollerPrimitive.Button,
|
|
2017
2101
|
{
|
|
2018
2102
|
"data-slot": "message-scroller-button",
|
|
@@ -2024,88 +2108,21 @@ function MessageScrollerButton({
|
|
|
2024
2108
|
"absolute inset-s-1/2 -translate-x-1/2 border-border bg-background text-foreground transition-[translate,scale,opacity] duration-200 hover:bg-muted hover:text-foreground data-[active=false]:pointer-events-none data-[active=false]:scale-95 data-[active=false]:opacity-0 data-[active=false]:duration-400 data-[active=false]:ease-[cubic-bezier(0.7,0,0.84,0)] data-[active=true]:translate-y-0 data-[active=true]:scale-100 data-[active=true]:opacity-100 data-[active=true]:ease-[cubic-bezier(0.23,1,0.32,1)] data-[direction=end]:bottom-4 data-[direction=end]:data-[active=false]:translate-y-full data-[direction=start]:top-4 data-[direction=start]:data-[active=false]:-translate-y-full rtl:translate-x-1/2 data-[direction=start]:[&_svg]:rotate-180",
|
|
2025
2109
|
className
|
|
2026
2110
|
),
|
|
2027
|
-
render: render ?? /* @__PURE__ */
|
|
2111
|
+
render: render ?? /* @__PURE__ */ jsx24(Button, { variant, size }),
|
|
2028
2112
|
...props,
|
|
2029
2113
|
children: children ?? /* @__PURE__ */ jsxs14(Fragment3, { children: [
|
|
2030
|
-
/* @__PURE__ */
|
|
2114
|
+
/* @__PURE__ */ jsx24(
|
|
2031
2115
|
ArrowDownIcon,
|
|
2032
2116
|
{}
|
|
2033
2117
|
),
|
|
2034
|
-
/* @__PURE__ */
|
|
2118
|
+
/* @__PURE__ */ jsx24("span", { className: "sr-only", children: direction === "end" ? "Scroll to end" : "Scroll to start" })
|
|
2035
2119
|
] })
|
|
2036
2120
|
}
|
|
2037
2121
|
);
|
|
2038
2122
|
}
|
|
2039
2123
|
|
|
2040
|
-
// scaffold/src/components/chat/turn-outline.tsx
|
|
2041
|
-
import { jsx as jsx24, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
2042
|
-
function turnsFromMessages(messages) {
|
|
2043
|
-
return messages.reduce((turns, message) => {
|
|
2044
|
-
if (message.role === "user") {
|
|
2045
|
-
turns.push({ id: message.id, label: message.content.trim() || `Turn ${turns.length + 1}`, messageIds: [message.id], response: "" });
|
|
2046
|
-
} else if (turns.length) {
|
|
2047
|
-
const turn = turns[turns.length - 1];
|
|
2048
|
-
turn.messageIds.push(message.id);
|
|
2049
|
-
if (!turn.response) turn.response = message.content.trim();
|
|
2050
|
-
}
|
|
2051
|
-
return turns;
|
|
2052
|
-
}, []);
|
|
2053
|
-
}
|
|
2054
|
-
function TurnOutline({ messages }) {
|
|
2055
|
-
const turns = turnsFromMessages(messages);
|
|
2056
|
-
const { scrollToMessage } = useMessageScroller();
|
|
2057
|
-
const { currentAnchorId, visibleMessageIds } = useMessageScrollerVisibility();
|
|
2058
|
-
const visible = new Set(visibleMessageIds);
|
|
2059
|
-
const [hoveredIndex, setHoveredIndex] = useState8(null);
|
|
2060
|
-
const focusIndex = hoveredIndex ?? -1;
|
|
2061
|
-
if (turns.length < 2) return null;
|
|
2062
|
-
return /* @__PURE__ */ jsx24("nav", { "aria-label": "Transcript outline", className: "pointer-events-none absolute end-2 top-1/2 z-20 hidden max-h-72 w-14 -translate-y-1/2 md:block xl:end-5", children: /* @__PURE__ */ jsx24("div", { className: "pointer-events-auto flex flex-col items-end gap-0.5 py-2", children: turns.map((turn, index) => {
|
|
2063
|
-
const active = turn.messageIds.some((id) => visible.has(id) || currentAnchorId === id);
|
|
2064
|
-
const distance = focusIndex >= 0 ? Math.abs(index - focusIndex) : Number.POSITIVE_INFINITY;
|
|
2065
|
-
const barWidth = focusIndex < 0 ? 12 : distance === 0 ? 50 : distance === 1 ? 36 : distance === 2 ? 23 : 10;
|
|
2066
|
-
const barOpacity = focusIndex < 0 ? active ? 1 : 0.4 : distance === 0 ? 1 : distance === 1 ? 0.78 : distance === 2 ? 0.58 : 0.36;
|
|
2067
|
-
return /* @__PURE__ */ jsxs15(Tooltip, { children: [
|
|
2068
|
-
/* @__PURE__ */ jsx24(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx24(
|
|
2069
|
-
"button",
|
|
2070
|
-
{
|
|
2071
|
-
type: "button",
|
|
2072
|
-
"aria-label": `Jump to ${turn.label}`,
|
|
2073
|
-
className: "group relative flex h-2 w-14 items-center justify-end outline-none after:pointer-events-auto after:absolute after:-inset-y-0.5 after:inset-x-0 after:content-['']",
|
|
2074
|
-
onBlur: () => setHoveredIndex(null),
|
|
2075
|
-
onFocus: () => setHoveredIndex(index),
|
|
2076
|
-
onMouseEnter: () => setHoveredIndex(index),
|
|
2077
|
-
onMouseLeave: () => setHoveredIndex(null),
|
|
2078
|
-
onClick: () => scrollToMessage(turn.id, { align: "start", behavior: "smooth" }),
|
|
2079
|
-
children: /* @__PURE__ */ jsx24(
|
|
2080
|
-
"span",
|
|
2081
|
-
{
|
|
2082
|
-
className: cn("h-0.5 rounded-full bg-muted-foreground/40 transition-[width,background-color,opacity] duration-150 ease-out group-hover:bg-foreground group-focus-visible:bg-foreground", active && "bg-foreground"),
|
|
2083
|
-
style: { opacity: barOpacity, width: barWidth }
|
|
2084
|
-
}
|
|
2085
|
-
)
|
|
2086
|
-
}
|
|
2087
|
-
) }),
|
|
2088
|
-
/* @__PURE__ */ jsxs15(TooltipContent, { side: "left", className: "w-64 flex-col items-stretch gap-0 overflow-hidden p-3", children: [
|
|
2089
|
-
/* @__PURE__ */ jsx24("p", { className: "line-clamp-2 max-w-full wrap-break-word text-sm font-medium", children: turn.label }),
|
|
2090
|
-
turn.response && /* @__PURE__ */ jsx24("p", { className: "mt-1 line-clamp-2 max-w-full wrap-break-word text-xs opacity-70", children: turn.response })
|
|
2091
|
-
] })
|
|
2092
|
-
] }, turn.id);
|
|
2093
|
-
}) }) });
|
|
2094
|
-
}
|
|
2095
|
-
|
|
2096
|
-
// scaffold/src/components/chat/timeline-loading.ts
|
|
2097
|
-
function isScaffoldTimelineLoading({
|
|
2098
|
-
activeRunId,
|
|
2099
|
-
message,
|
|
2100
|
-
runState,
|
|
2101
|
-
streamingMessageId
|
|
2102
|
-
}) {
|
|
2103
|
-
if (runState !== "streaming" && runState !== "reconnecting") return false;
|
|
2104
|
-
return message.id === streamingMessageId || Boolean(message.runId && message.runId === activeRunId);
|
|
2105
|
-
}
|
|
2106
|
-
|
|
2107
2124
|
// scaffold/src/components/chat/chat-shell.tsx
|
|
2108
|
-
import { Fragment as Fragment4, jsx as jsx25, jsxs as
|
|
2125
|
+
import { Fragment as Fragment4, jsx as jsx25, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
2109
2126
|
var LOADING_INPUT_CONTRACT = {
|
|
2110
2127
|
version: "agents24.runtime-inputs.v2",
|
|
2111
2128
|
modalities: {
|
|
@@ -2124,7 +2141,7 @@ function CollapsedHeaderActions({ onNew }) {
|
|
|
2124
2141
|
const { isMobile, openMobile, state } = useSidebar();
|
|
2125
2142
|
const sidebarVisible = isMobile ? openMobile : state === "expanded";
|
|
2126
2143
|
if (sidebarVisible) return null;
|
|
2127
|
-
return /* @__PURE__ */
|
|
2144
|
+
return /* @__PURE__ */ jsxs15(Fragment4, { children: [
|
|
2128
2145
|
/* @__PURE__ */ jsx25("div", { className: "h-8 w-17 shrink-0", "aria-hidden": "true" }),
|
|
2129
2146
|
/* @__PURE__ */ jsx25("div", { className: "pointer-events-none fixed start-12 top-0 z-[60] flex h-12 items-center", children: /* @__PURE__ */ jsx25(Button, { className: "pointer-events-auto", variant: "ghost", size: "icon-sm", "aria-label": "New chat", onClick: onNew, children: /* @__PURE__ */ jsx25(SquarePen2, {}) }) })
|
|
2130
2147
|
] });
|
|
@@ -2132,7 +2149,7 @@ function CollapsedHeaderActions({ onNew }) {
|
|
|
2132
2149
|
function OperationErrorNotice({ runtime }) {
|
|
2133
2150
|
const error = runtime.operationError || runtime.backgroundError;
|
|
2134
2151
|
if (!error) return null;
|
|
2135
|
-
return /* @__PURE__ */ jsx25("div", { className: "pointer-events-none absolute inset-x-0 top-3 z-40 flex justify-center px-4", children: /* @__PURE__ */
|
|
2152
|
+
return /* @__PURE__ */ jsx25("div", { className: "pointer-events-none absolute inset-x-0 top-3 z-40 flex justify-center px-4", children: /* @__PURE__ */ jsxs15("div", { className: "pointer-events-auto flex max-w-lg items-start gap-2 rounded-lg border border-destructive/20 bg-background px-3 py-2 text-sm shadow-md", role: "alert", children: [
|
|
2136
2153
|
/* @__PURE__ */ jsx25(CircleAlert, { className: "mt-0.5 size-4 shrink-0 text-destructive" }),
|
|
2137
2154
|
/* @__PURE__ */ jsx25("span", { className: "min-w-0 flex-1 wrap-break-word", children: agentChatRuntimeErrorMessage(error) || "The chat could not complete that action." }),
|
|
2138
2155
|
/* @__PURE__ */ jsx25(
|
|
@@ -2142,7 +2159,7 @@ function OperationErrorNotice({ runtime }) {
|
|
|
2142
2159
|
onClick: runtime.operationError ? runtime.clearOperationError : runtime.controller.clearBackgroundError,
|
|
2143
2160
|
size: "icon-xs",
|
|
2144
2161
|
variant: "ghost",
|
|
2145
|
-
children: /* @__PURE__ */ jsx25(
|
|
2162
|
+
children: /* @__PURE__ */ jsx25(X, {})
|
|
2146
2163
|
}
|
|
2147
2164
|
)
|
|
2148
2165
|
] }) });
|
|
@@ -2154,12 +2171,26 @@ function ChatShell({
|
|
|
2154
2171
|
sidebar
|
|
2155
2172
|
}) {
|
|
2156
2173
|
const { controller } = runtime;
|
|
2157
|
-
const isRunning = controller.runState === "streaming" || controller.runState === "reconnecting" || controller.runState === "paused" || controller.runState === "cancelling"
|
|
2174
|
+
const isRunning = controller.runState === "streaming" || controller.runState === "reconnecting" || controller.runState === "paused" || controller.runState === "cancelling";
|
|
2175
|
+
const isUploading = controller.runState === "uploading" || runtime.isUploading;
|
|
2158
2176
|
const isCancelling = controller.runState === "cancelling";
|
|
2159
2177
|
const activeTitle = controller.threads.find((thread) => thread.id === controller.activeThreadId)?.title;
|
|
2160
2178
|
const hasBlockingError = Boolean(runtime.fatalError && controller.messages.length === 0);
|
|
2161
2179
|
const isEmptyState = controller.activeThreadId === null && controller.messages.length === 0 && !hasBlockingError;
|
|
2162
|
-
|
|
2180
|
+
const composerOverlayRef = React8.useRef(null);
|
|
2181
|
+
const [composerOverlayHeight, setComposerOverlayHeight] = React8.useState(192);
|
|
2182
|
+
React8.useEffect(() => {
|
|
2183
|
+
const overlay = composerOverlayRef.current;
|
|
2184
|
+
if (!overlay || isEmptyState) return;
|
|
2185
|
+
const updateHeight = () => {
|
|
2186
|
+
setComposerOverlayHeight(Math.ceil(overlay.getBoundingClientRect().height));
|
|
2187
|
+
};
|
|
2188
|
+
updateHeight();
|
|
2189
|
+
const resizeObserver = new ResizeObserver(updateHeight);
|
|
2190
|
+
resizeObserver.observe(overlay);
|
|
2191
|
+
return () => resizeObserver.disconnect();
|
|
2192
|
+
}, [isEmptyState]);
|
|
2193
|
+
return /* @__PURE__ */ jsxs15(
|
|
2163
2194
|
SidebarProvider,
|
|
2164
2195
|
{
|
|
2165
2196
|
className: "h-svh min-h-0 overflow-hidden",
|
|
@@ -2182,11 +2213,11 @@ function ChatShell({
|
|
|
2182
2213
|
onOpen: runtime.openThread
|
|
2183
2214
|
}
|
|
2184
2215
|
),
|
|
2185
|
-
/* @__PURE__ */
|
|
2186
|
-
/* @__PURE__ */
|
|
2216
|
+
/* @__PURE__ */ jsxs15(SidebarInset, { className: "h-svh min-h-0 overflow-hidden bg-background", children: [
|
|
2217
|
+
/* @__PURE__ */ jsxs15(
|
|
2187
2218
|
"header",
|
|
2188
2219
|
{
|
|
2189
|
-
className: "flex h-12 shrink-0 items-center gap-2 border-b border-border/
|
|
2220
|
+
className: "flex h-12 shrink-0 items-center gap-2 border-b border-border/50 px-3 sm:px-4",
|
|
2190
2221
|
"data-agents24-chat-header": "",
|
|
2191
2222
|
children: [
|
|
2192
2223
|
/* @__PURE__ */ jsx25(CollapsedHeaderActions, { onNew: runtime.newThread }),
|
|
@@ -2195,7 +2226,7 @@ function ChatShell({
|
|
|
2195
2226
|
]
|
|
2196
2227
|
}
|
|
2197
2228
|
),
|
|
2198
|
-
/* @__PURE__ */
|
|
2229
|
+
/* @__PURE__ */ jsxs15(
|
|
2199
2230
|
"section",
|
|
2200
2231
|
{
|
|
2201
2232
|
"aria-label": "Conversation",
|
|
@@ -2203,14 +2234,14 @@ function ChatShell({
|
|
|
2203
2234
|
"data-agents24-chat-transcript": "",
|
|
2204
2235
|
children: [
|
|
2205
2236
|
/* @__PURE__ */ jsx25(OperationErrorNotice, { runtime }),
|
|
2206
|
-
isEmptyState ? /* @__PURE__ */
|
|
2237
|
+
isEmptyState ? /* @__PURE__ */ jsxs15("div", { className: "absolute inset-0 flex min-h-0 flex-col justify-center pb-20", children: [
|
|
2207
2238
|
/* @__PURE__ */ jsx25("div", { className: "mx-auto mb-8 w-full max-w-3xl px-4 text-center sm:px-6", children: /* @__PURE__ */ jsx25("h2", { className: "text-3xl font-semibold tracking-tight sm:text-4xl", children: "Ready when you are." }) }),
|
|
2208
2239
|
/* @__PURE__ */ jsx25(
|
|
2209
2240
|
ChatComposer,
|
|
2210
2241
|
{
|
|
2211
2242
|
inputContract: runtime.inputContract || LOADING_INPUT_CONTRACT,
|
|
2212
2243
|
contextWindow: runtime.contextWindow,
|
|
2213
|
-
disabled: runtime.isBootstrapping || runtime.isChangingAgent,
|
|
2244
|
+
disabled: runtime.isBootstrapping || runtime.isChangingAgent || isUploading,
|
|
2214
2245
|
focusKey: controller.activeThreadId,
|
|
2215
2246
|
forceExpanded: true,
|
|
2216
2247
|
isCancelling,
|
|
@@ -2229,36 +2260,38 @@ function ChatShell({
|
|
|
2229
2260
|
onSubmit: (text, files) => runtime.submit({
|
|
2230
2261
|
text,
|
|
2231
2262
|
files: files.map((file) => ({
|
|
2232
|
-
data: file,
|
|
2233
|
-
mediaType: file.
|
|
2234
|
-
name: file.
|
|
2235
|
-
}))
|
|
2263
|
+
data: file.source,
|
|
2264
|
+
mediaType: file.mediaType,
|
|
2265
|
+
name: file.filename
|
|
2266
|
+
})),
|
|
2267
|
+
attachments: files
|
|
2236
2268
|
})
|
|
2237
2269
|
}
|
|
2238
2270
|
)
|
|
2239
|
-
] }) : /* @__PURE__ */
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
/* @__PURE__ */ jsx25(MessageScrollerViewport, { preserveScrollOnPrepend: true, children: /* @__PURE__ */
|
|
2271
|
+
] }) : /* @__PURE__ */ jsx25(Fragment4, { children: /* @__PURE__ */ jsxs15(
|
|
2272
|
+
MessageScrollerProvider,
|
|
2273
|
+
{
|
|
2274
|
+
autoScroll: true,
|
|
2275
|
+
defaultScrollPosition: "last-anchor",
|
|
2276
|
+
scrollPreviousItemPeek: 64,
|
|
2277
|
+
children: [
|
|
2278
|
+
/* @__PURE__ */ jsxs15(MessageScroller, { className: "relative min-h-0 flex-1", children: [
|
|
2279
|
+
/* @__PURE__ */ jsx25(MessageScrollerViewport, { preserveScrollOnPrepend: true, children: /* @__PURE__ */ jsxs15(
|
|
2248
2280
|
MessageScrollerContent,
|
|
2249
2281
|
{
|
|
2250
|
-
className: "mx-auto w-full max-w-3xl gap-
|
|
2282
|
+
className: "mx-auto w-full max-w-3xl gap-3 px-4 pt-8 pb-0 sm:px-6 sm:pt-12",
|
|
2283
|
+
style: { paddingBottom: composerOverlayHeight + 72 },
|
|
2251
2284
|
children: [
|
|
2252
|
-
controller.hasOlderTurns && /* @__PURE__ */
|
|
2285
|
+
controller.hasOlderTurns && /* @__PURE__ */ jsxs15(Button, { className: "self-center", variant: "ghost", size: "sm", disabled: controller.isLoadingOlder, onClick: () => {
|
|
2253
2286
|
void controller.loadOlder().catch(() => void 0);
|
|
2254
2287
|
}, children: [
|
|
2255
2288
|
controller.isLoadingOlder && /* @__PURE__ */ jsx25(LoaderCircle3, { className: "animate-spin" }),
|
|
2256
2289
|
" Load earlier messages"
|
|
2257
2290
|
] }),
|
|
2258
2291
|
runtime.isBootstrapping && /* @__PURE__ */ jsx25("div", { "aria-label": "Loading conversation", className: "flex flex-1 items-center justify-center py-24 text-muted-foreground", role: "status", children: /* @__PURE__ */ jsx25(LoaderCircle3, { className: "size-4 animate-spin" }) }),
|
|
2259
|
-
hasBlockingError && /* @__PURE__ */
|
|
2292
|
+
hasBlockingError && /* @__PURE__ */ jsxs15("div", { className: "flex flex-1 flex-col items-center justify-center gap-3 py-24 text-center", children: [
|
|
2260
2293
|
/* @__PURE__ */ jsx25(CircleAlert, { className: "size-5 text-destructive" }),
|
|
2261
|
-
/* @__PURE__ */
|
|
2294
|
+
/* @__PURE__ */ jsxs15("div", { children: [
|
|
2262
2295
|
/* @__PURE__ */ jsx25("h2", { className: "font-medium", children: "Couldn\u2019t connect" }),
|
|
2263
2296
|
/* @__PURE__ */ jsx25("p", { className: "mt-1 max-w-md text-sm text-muted-foreground", children: runtime.errorMessage })
|
|
2264
2297
|
] }),
|
|
@@ -2285,6 +2318,7 @@ function ChatShell({
|
|
|
2285
2318
|
onHitlAction: runtime.onHitlAction,
|
|
2286
2319
|
onRegenerate: () => controller.regenerate(message),
|
|
2287
2320
|
onSetFeedback: (input) => controller.setFeedback(message, input),
|
|
2321
|
+
resolveAttachmentContent: runtime.resolveAttachmentContent,
|
|
2288
2322
|
Timeline: components?.Timeline
|
|
2289
2323
|
}
|
|
2290
2324
|
)
|
|
@@ -2294,43 +2328,56 @@ function ChatShell({
|
|
|
2294
2328
|
]
|
|
2295
2329
|
}
|
|
2296
2330
|
) }),
|
|
2297
|
-
/* @__PURE__ */ jsx25(TurnOutline, { messages: controller.messages })
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2331
|
+
/* @__PURE__ */ jsx25(TurnOutline, { messages: controller.messages })
|
|
2332
|
+
] }),
|
|
2333
|
+
/* @__PURE__ */ jsxs15(
|
|
2334
|
+
"div",
|
|
2335
|
+
{
|
|
2336
|
+
ref: composerOverlayRef,
|
|
2337
|
+
className: "pointer-events-none absolute inset-x-0 bottom-0 z-30 overflow-visible pt-10",
|
|
2338
|
+
style: {
|
|
2339
|
+
background: "linear-gradient(to bottom, transparent 0%, color-mix(in oklch, var(--background) 85%, transparent) 45%, var(--background) 65%, var(--background) 100%)"
|
|
2340
|
+
},
|
|
2341
|
+
children: [
|
|
2342
|
+
/* @__PURE__ */ jsx25("div", { className: "pointer-events-auto absolute inset-x-0 top-0 flex -translate-y-full justify-center pb-3", children: /* @__PURE__ */ jsx25(MessageScrollerButton, { className: "!relative !inset-auto !translate-x-0 rtl:!translate-x-0" }) }),
|
|
2343
|
+
/* @__PURE__ */ jsx25("div", { className: "pointer-events-auto", children: /* @__PURE__ */ jsx25(
|
|
2344
|
+
ChatComposer,
|
|
2345
|
+
{
|
|
2346
|
+
inputContract: runtime.inputContract || LOADING_INPUT_CONTRACT,
|
|
2347
|
+
contextWindow: runtime.contextWindow,
|
|
2348
|
+
disabled: runtime.isBootstrapping || runtime.isChangingAgent || hasBlockingError || isUploading,
|
|
2349
|
+
focusKey: controller.activeThreadId,
|
|
2350
|
+
isCancelling,
|
|
2351
|
+
isRunning,
|
|
2352
|
+
agentId: runtime.agentId,
|
|
2353
|
+
agents: runtime.agents,
|
|
2354
|
+
onAgentChange: (agentId) => {
|
|
2355
|
+
void runtime.changeAgent(agentId);
|
|
2356
|
+
},
|
|
2357
|
+
onStop: () => {
|
|
2358
|
+
void controller.cancelRun().catch(() => void 0);
|
|
2359
|
+
},
|
|
2360
|
+
modelId: runtime.modelId,
|
|
2361
|
+
models: runtime.models,
|
|
2362
|
+
onModelChange: runtime.changeModel,
|
|
2363
|
+
onSubmit: (text, files) => runtime.submit({
|
|
2364
|
+
text,
|
|
2365
|
+
files: files.map((file) => ({
|
|
2366
|
+
data: file.source,
|
|
2367
|
+
mediaType: file.mediaType,
|
|
2368
|
+
name: file.filename
|
|
2369
|
+
})),
|
|
2370
|
+
attachments: files
|
|
2371
|
+
}),
|
|
2372
|
+
utilityRow: "below"
|
|
2373
|
+
}
|
|
2374
|
+
) })
|
|
2375
|
+
]
|
|
2376
|
+
}
|
|
2377
|
+
)
|
|
2378
|
+
]
|
|
2379
|
+
}
|
|
2380
|
+
) })
|
|
2334
2381
|
]
|
|
2335
2382
|
}
|
|
2336
2383
|
)
|