@agents24/chat-react 0.5.30 → 0.5.32
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/README.md +9 -4
- package/dist/generated/delegation-types.d.ts +129 -0
- package/dist/registry/agents24-chat.json +37 -19
- package/dist/runtime/index.cjs +52 -0
- package/dist/runtime/index.cjs.map +1 -1
- package/dist/runtime/index.d.ts +2 -0
- package/dist/runtime/index.js +51 -0
- package/dist/runtime/index.js.map +1 -1
- package/dist/runtime/use-delegation-tasks.d.ts +20 -0
- package/dist/scaffold/app.d.ts +2 -1
- package/dist/scaffold/components/chat/chat-composer.d.ts +4 -1
- package/dist/scaffold/components/chat/subagent-tasks.d.ts +3 -0
- package/dist/scaffold/components/theme-provider.d.ts +4 -3
- package/dist/scaffold/index.cjs +425 -280
- package/dist/scaffold/index.cjs.map +1 -1
- package/dist/scaffold/index.d.ts +3 -2
- package/dist/scaffold/index.js +416 -271
- package/dist/scaffold/index.js.map +1 -1
- package/dist/styles.css +14 -0
- package/dist/ui/adapters.d.ts +5 -2
- package/dist/ui/agent-chat-composer.d.ts +2 -1
- package/dist/ui/agent-chat-queue.d.ts +0 -2
- package/dist/ui/index.cjs +306 -260
- package/dist/ui/index.cjs.map +1 -1
- package/dist/ui/index.js +308 -263
- package/dist/ui/index.js.map +1 -1
- package/dist/ui/tool-sessions.d.ts +10 -2
- package/dist/ui/use-agent-chat-composer.d.ts +25 -26
- package/dist/ui/use-audio-recorder.d.ts +4 -2
- package/package.json +4 -3
- package/scaffold/host-styles.css +3 -0
- package/scaffold/manifest.json +2 -2
- package/scaffold/src/app.tsx +4 -1
- package/scaffold/src/chat.css +11 -1
- package/scaffold/src/components/chat/chat-composer.tsx +36 -19
- package/scaffold/src/components/chat/chat-shell.tsx +5 -4
- package/scaffold/src/components/chat/external-link-dialog.tsx +6 -5
- package/scaffold/src/components/chat/identity-control.tsx +0 -1
- package/scaffold/src/components/chat/subagent-tasks.tsx +109 -0
- package/scaffold/src/components/theme-provider.tsx +30 -27
- package/scaffold/src/components/ui/tooltip.tsx +3 -0
- package/scaffold/src/host.css +11 -0
- package/scaffold/src/index.css +1 -213
- package/scaffold/src/index.tsx +4 -2
- package/scaffold/src/theme.css +215 -0
package/dist/ui/index.cjs
CHANGED
|
@@ -590,6 +590,7 @@ function attachmentRenderKey(data, index, rowKind) {
|
|
|
590
590
|
function ChatImageAttachment({
|
|
591
591
|
className,
|
|
592
592
|
data,
|
|
593
|
+
presentation = "message",
|
|
593
594
|
onRemove,
|
|
594
595
|
resolveContent,
|
|
595
596
|
...props
|
|
@@ -669,7 +670,7 @@ function ChatImageAttachment({
|
|
|
669
670
|
Attachment,
|
|
670
671
|
{
|
|
671
672
|
...props,
|
|
672
|
-
className: cn("a24-chat-image-attachment", className),
|
|
673
|
+
className: cn("a24-chat-image-attachment", presentation === "composer" && "a24-composer-attachment-card", className),
|
|
673
674
|
orientation: "vertical",
|
|
674
675
|
ref: rootRef,
|
|
675
676
|
state: status === "error" ? "error" : status === "done" ? "done" : "processing",
|
|
@@ -692,6 +693,7 @@ function ChatImageAttachment({
|
|
|
692
693
|
status !== "done" && status !== "error" ? React.createElement("span", { className: "sr-only", role: "status" }, `Loading ${filename}`) : null,
|
|
693
694
|
status === "error" ? React.createElement("span", { className: "sr-only" }, `${filename} preview unavailable`) : null
|
|
694
695
|
),
|
|
696
|
+
presentation === "composer" ? React.createElement("span", { className: "a24-composer-image-caption", title: filename }, filename) : null,
|
|
695
697
|
onRemove ? React.createElement(
|
|
696
698
|
"button",
|
|
697
699
|
{
|
|
@@ -710,6 +712,7 @@ function ChatImageAttachment({
|
|
|
710
712
|
function ChatAttachment({
|
|
711
713
|
className,
|
|
712
714
|
data,
|
|
715
|
+
presentation = "message",
|
|
713
716
|
onRemove,
|
|
714
717
|
orientation,
|
|
715
718
|
resolveContent,
|
|
@@ -722,6 +725,7 @@ function ChatAttachment({
|
|
|
722
725
|
...props,
|
|
723
726
|
className,
|
|
724
727
|
data,
|
|
728
|
+
presentation,
|
|
725
729
|
onRemove,
|
|
726
730
|
resolveContent
|
|
727
731
|
});
|
|
@@ -729,11 +733,16 @@ function ChatAttachment({
|
|
|
729
733
|
return React.createElement(
|
|
730
734
|
Attachment,
|
|
731
735
|
{
|
|
732
|
-
className: cn("a24-chat-attachment", className),
|
|
736
|
+
className: cn("a24-chat-attachment", presentation === "composer" && "a24-composer-attachment-card", className),
|
|
733
737
|
orientation: orientation || "horizontal",
|
|
734
738
|
size: "sm",
|
|
735
739
|
...props
|
|
736
740
|
},
|
|
741
|
+
presentation === "composer" ? React.createElement(
|
|
742
|
+
"div",
|
|
743
|
+
{ className: "a24-composer-file-preview", "aria-hidden": true },
|
|
744
|
+
React.createElement(import_file_icons.FileIcon, { size: 64, type: fileIconType, variant: "solid" })
|
|
745
|
+
) : null,
|
|
737
746
|
React.createElement(
|
|
738
747
|
AttachmentMedia,
|
|
739
748
|
Object.assign(
|
|
@@ -791,6 +800,7 @@ function ChatAttachments({
|
|
|
791
800
|
}
|
|
792
801
|
function ChatAttachmentRows({
|
|
793
802
|
attachments,
|
|
803
|
+
layout = "message",
|
|
794
804
|
className,
|
|
795
805
|
dir,
|
|
796
806
|
onRemove,
|
|
@@ -806,6 +816,7 @@ function ChatAttachmentRows({
|
|
|
806
816
|
items.map(
|
|
807
817
|
(attachment, index) => React.createElement(ChatAttachment, {
|
|
808
818
|
data: attachment,
|
|
819
|
+
presentation: layout,
|
|
809
820
|
dir: "ltr",
|
|
810
821
|
key: attachmentRenderKey(attachment, index, kind),
|
|
811
822
|
onRemove: onRemove ? () => onRemove(attachment) : void 0,
|
|
@@ -816,12 +827,12 @@ function ChatAttachmentRows({
|
|
|
816
827
|
return React.createElement(
|
|
817
828
|
"div",
|
|
818
829
|
{
|
|
819
|
-
className: cn("a24-chat-attachment-rows", className),
|
|
830
|
+
className: cn("a24-chat-attachment-rows", layout === "composer" && "a24-composer-attachment-row", className),
|
|
820
831
|
dir,
|
|
821
832
|
...props
|
|
822
833
|
},
|
|
823
|
-
renderRow(imageAttachments, "images"),
|
|
824
|
-
renderRow(fileAttachments, "files")
|
|
834
|
+
layout === "composer" ? renderRow(attachments, "mixed") : renderRow(imageAttachments, "images"),
|
|
835
|
+
layout === "message" ? renderRow(fileAttachments, "files") : null
|
|
825
836
|
);
|
|
826
837
|
}
|
|
827
838
|
|
|
@@ -831,6 +842,7 @@ var React4 = __toESM(require("react"), 1);
|
|
|
831
842
|
// src/runtime/input-contract.ts
|
|
832
843
|
var import_client = require("@agents24/client");
|
|
833
844
|
var import_client2 = require("@agents24/client");
|
|
845
|
+
var canonicalInputMimeType = import_client.normalizeAttachmentMimeType;
|
|
834
846
|
function uploadByteSize(data) {
|
|
835
847
|
if (data instanceof Uint8Array || data instanceof ArrayBuffer) return data.byteLength;
|
|
836
848
|
return data.size;
|
|
@@ -936,32 +948,38 @@ function useChatComposerFocus({ textareaRef, disabled, focusKey, restoredDraft,
|
|
|
936
948
|
// src/ui/use-audio-recorder.ts
|
|
937
949
|
var React3 = __toESM(require("react"), 1);
|
|
938
950
|
var RECORDING_CANDIDATES = ["audio/webm;codecs=opus", "audio/webm", "audio/mp4"];
|
|
939
|
-
function recordingExtension(mimeType) {
|
|
940
|
-
return mimeType.startsWith("audio/mp4") ? "m4a" : "webm";
|
|
941
|
-
}
|
|
942
951
|
function useAudioRecorder(onRecorded, options = {}) {
|
|
943
|
-
const mountedRef = React3.useRef(true);
|
|
944
|
-
const startingRef = React3.useRef(false);
|
|
945
952
|
const recorderRef = React3.useRef(null);
|
|
946
953
|
const streamRef = React3.useRef(null);
|
|
954
|
+
const pendingRef = React3.useRef(false);
|
|
955
|
+
const generationRef = React3.useRef(0);
|
|
947
956
|
const onRecordedRef = React3.useRef(onRecorded);
|
|
948
957
|
const onStreamChangeRef = React3.useRef(options.onStreamChange);
|
|
949
|
-
|
|
950
|
-
|
|
958
|
+
React3.useEffect(() => {
|
|
959
|
+
onRecordedRef.current = onRecorded;
|
|
960
|
+
onStreamChangeRef.current = options.onStreamChange;
|
|
961
|
+
});
|
|
951
962
|
const [isRecording, setIsRecording] = React3.useState(false);
|
|
952
963
|
const [error, setError] = React3.useState(null);
|
|
964
|
+
const release = React3.useCallback(() => {
|
|
965
|
+
streamRef.current?.getTracks().forEach((track) => track.stop());
|
|
966
|
+
streamRef.current = null;
|
|
967
|
+
recorderRef.current = null;
|
|
968
|
+
onStreamChangeRef.current?.(null);
|
|
969
|
+
}, []);
|
|
953
970
|
const stop = React3.useCallback(() => {
|
|
954
|
-
|
|
971
|
+
const recorder = recorderRef.current;
|
|
972
|
+
if (recorder && recorder.state !== "inactive") recorder.stop();
|
|
955
973
|
}, []);
|
|
956
974
|
const start = React3.useCallback(async () => {
|
|
957
|
-
if (
|
|
958
|
-
startingRef.current = true;
|
|
975
|
+
if (pendingRef.current || recorderRef.current) return;
|
|
959
976
|
setError(null);
|
|
960
977
|
if (!navigator.mediaDevices?.getUserMedia || typeof MediaRecorder === "undefined") {
|
|
961
978
|
setError("Audio recording is not supported in this browser.");
|
|
962
|
-
startingRef.current = false;
|
|
963
979
|
return;
|
|
964
980
|
}
|
|
981
|
+
pendingRef.current = true;
|
|
982
|
+
const generation = generationRef.current;
|
|
965
983
|
try {
|
|
966
984
|
const mimeType = RECORDING_CANDIDATES.find((candidate) => MediaRecorder.isTypeSupported(candidate));
|
|
967
985
|
if (!mimeType) {
|
|
@@ -969,158 +987,169 @@ function useAudioRecorder(onRecorded, options = {}) {
|
|
|
969
987
|
return;
|
|
970
988
|
}
|
|
971
989
|
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
|
|
972
|
-
if (
|
|
990
|
+
if (generation !== generationRef.current) {
|
|
973
991
|
stream.getTracks().forEach((track) => track.stop());
|
|
974
992
|
return;
|
|
975
993
|
}
|
|
976
994
|
streamRef.current = stream;
|
|
977
|
-
onStreamChangeRef.current?.(stream);
|
|
978
995
|
const recorder = new MediaRecorder(stream, { mimeType });
|
|
996
|
+
recorderRef.current = recorder;
|
|
979
997
|
const chunks = [];
|
|
980
998
|
recorder.ondataavailable = (event) => {
|
|
981
999
|
if (event.data.size) chunks.push(event.data);
|
|
982
1000
|
};
|
|
983
1001
|
recorder.onstop = () => {
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
1002
|
+
if (generation !== generationRef.current) return;
|
|
1003
|
+
const actualType = recorder.mimeType || mimeType;
|
|
1004
|
+
const extension = actualType.startsWith("audio/mp4") ? "m4a" : "webm";
|
|
1005
|
+
try {
|
|
1006
|
+
onRecordedRef.current(new File([new Blob(chunks, { type: actualType })], `recording-${Date.now()}.${extension}`, { type: actualType }));
|
|
1007
|
+
} finally {
|
|
1008
|
+
release();
|
|
1009
|
+
setIsRecording(false);
|
|
1010
|
+
}
|
|
1011
|
+
};
|
|
1012
|
+
recorder.onerror = () => {
|
|
1013
|
+
recorder.onstop = null;
|
|
1014
|
+
if (recorder.state !== "inactive") recorder.stop();
|
|
1015
|
+
release();
|
|
1016
|
+
setIsRecording(false);
|
|
1017
|
+
setError("Audio recording failed. Try again.");
|
|
991
1018
|
};
|
|
992
|
-
recorderRef.current = recorder;
|
|
993
|
-
streamRef.current = stream;
|
|
994
1019
|
recorder.start();
|
|
995
1020
|
setIsRecording(true);
|
|
1021
|
+
onStreamChangeRef.current?.(stream);
|
|
996
1022
|
} catch {
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1023
|
+
if (generation === generationRef.current) {
|
|
1024
|
+
release();
|
|
1025
|
+
setIsRecording(false);
|
|
1026
|
+
setError("Microphone access is unavailable.");
|
|
1027
|
+
}
|
|
1002
1028
|
} finally {
|
|
1003
|
-
|
|
1004
|
-
}
|
|
1005
|
-
}, []);
|
|
1006
|
-
React3.useEffect(() => {
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1029
|
+
if (generation === generationRef.current) pendingRef.current = false;
|
|
1030
|
+
}
|
|
1031
|
+
}, [release]);
|
|
1032
|
+
React3.useEffect(() => () => {
|
|
1033
|
+
generationRef.current += 1;
|
|
1034
|
+
pendingRef.current = false;
|
|
1035
|
+
const recorder = recorderRef.current;
|
|
1036
|
+
if (recorder) {
|
|
1037
|
+
recorder.onstop = null;
|
|
1038
|
+
recorder.onerror = null;
|
|
1039
|
+
if (recorder.state !== "inactive") recorder.stop();
|
|
1040
|
+
}
|
|
1041
|
+
release();
|
|
1042
|
+
}, [release]);
|
|
1015
1043
|
return { error, isRecording, start, stop };
|
|
1016
1044
|
}
|
|
1017
1045
|
|
|
1018
1046
|
// src/ui/use-agent-chat-composer.ts
|
|
1019
|
-
function useAgentChatComposer({
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
isCancelling = false,
|
|
1023
|
-
inputContract,
|
|
1024
|
-
recordingEnabled,
|
|
1025
|
-
focusKey,
|
|
1026
|
-
restoredDraft,
|
|
1027
|
-
textareaRef,
|
|
1028
|
-
onSubmit
|
|
1029
|
-
}) {
|
|
1030
|
-
const [text, setDraftText] = React4.useState("");
|
|
1031
|
-
const [files, setDraftFiles] = React4.useState([]);
|
|
1032
|
-
const [isSubmitting, setIsSubmitting] = React4.useState(false);
|
|
1047
|
+
function useAgentChatComposer(options) {
|
|
1048
|
+
const [text, setTextState] = React4.useState("");
|
|
1049
|
+
const [files, setFiles] = React4.useState([]);
|
|
1033
1050
|
const [attachmentError, setAttachmentError] = React4.useState(null);
|
|
1034
|
-
const
|
|
1035
|
-
const
|
|
1051
|
+
const [isSubmitting, setIsSubmitting] = React4.useState(false);
|
|
1052
|
+
const localTextareaRef = React4.useRef(null);
|
|
1053
|
+
const textareaRef = options.textareaRef ?? localTextareaRef;
|
|
1054
|
+
const filesRef = React4.useRef(files);
|
|
1055
|
+
const inFlightFilesRef = React4.useRef([]);
|
|
1056
|
+
const revisionRef = React4.useRef(0);
|
|
1036
1057
|
const submittingRef = React4.useRef(false);
|
|
1037
|
-
const mountedRef = React4.useRef(true);
|
|
1038
|
-
const updateDraft = (nextText, nextFiles) => {
|
|
1039
|
-
draftRef.current = { text: nextText, files: nextFiles, revision: draftRef.current.revision + 1 };
|
|
1040
|
-
setDraftText(nextText);
|
|
1041
|
-
setDraftFiles(nextFiles);
|
|
1042
|
-
};
|
|
1043
1058
|
React4.useEffect(() => {
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
};
|
|
1059
|
+
filesRef.current = files;
|
|
1060
|
+
}, [files]);
|
|
1061
|
+
React4.useEffect(() => () => {
|
|
1062
|
+
revokeAgentChatComposerFiles(filesRef.current);
|
|
1063
|
+
revokeAgentChatComposerFiles(inFlightFilesRef.current);
|
|
1050
1064
|
}, []);
|
|
1051
|
-
const
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1065
|
+
const focusComposer = useChatComposerFocus({
|
|
1066
|
+
textareaRef,
|
|
1067
|
+
disabled: Boolean(options.disabled),
|
|
1068
|
+
focusKey: options.focusKey,
|
|
1069
|
+
restoredDraft: options.restoredDraft,
|
|
1070
|
+
onRestore: (draft) => {
|
|
1071
|
+
revisionRef.current += 1;
|
|
1072
|
+
revokeAgentChatComposerFiles(filesRef.current);
|
|
1073
|
+
setTextState(draft.text);
|
|
1074
|
+
setFiles(draft.files);
|
|
1075
|
+
}
|
|
1076
|
+
});
|
|
1077
|
+
const attachmentMimeTypes = inputMimeTypes(options.inputContract);
|
|
1078
|
+
const setText = (value) => {
|
|
1079
|
+
revisionRef.current += 1;
|
|
1080
|
+
setTextState(value);
|
|
1081
|
+
};
|
|
1056
1082
|
const addFiles = (selected) => {
|
|
1057
|
-
const accepted =
|
|
1083
|
+
const accepted = selected.filter((file) => attachmentMimeTypes.includes(canonicalInputMimeType(file.type, file.name)));
|
|
1084
|
+
revisionRef.current += 1;
|
|
1085
|
+
const additions = accepted.map(createAgentChatComposerFile);
|
|
1086
|
+
setFiles((current) => [...current, ...additions]);
|
|
1058
1087
|
setAttachmentError(accepted.length === selected.length ? null : "One or more selected file types are not supported by this Agent.");
|
|
1059
|
-
updateDraft(draftRef.current.text, [...draftRef.current.files, ...accepted.map(createAgentChatComposerFile)]);
|
|
1060
1088
|
};
|
|
1061
|
-
const
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
if (
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
const
|
|
1068
|
-
|
|
1089
|
+
const removeFile = (id) => {
|
|
1090
|
+
revisionRef.current += 1;
|
|
1091
|
+
const removed = filesRef.current.find((file) => file.id === id);
|
|
1092
|
+
if (removed) revokeAgentChatComposerFiles([removed]);
|
|
1093
|
+
setFiles((current) => current.filter((file) => file.id !== id));
|
|
1094
|
+
};
|
|
1095
|
+
const recorder = useAudioRecorder((file) => addFiles([file]), { onStreamChange: options.onRecordingStreamChange });
|
|
1096
|
+
const validationError = options.inputContract ? validateAgentChatSubmission(
|
|
1097
|
+
options.inputContract,
|
|
1069
1098
|
text,
|
|
1070
|
-
files.map((file) => ({ data: file.source || new Uint8Array(), mediaType: file.mediaType, name: file.filename, byteSize: file.
|
|
1099
|
+
files.map((file) => ({ data: file.source || new Uint8Array(), mediaType: file.mediaType, name: file.filename, byteSize: file.byteSize }))
|
|
1071
1100
|
) : null;
|
|
1072
|
-
const canSubmit = !disabled && !isCancelling && !isSubmitting && !recorder.isRecording && !validationError
|
|
1101
|
+
const canSubmit = Boolean(text.trim() || files.length) && !options.disabled && !options.isCancelling && !isSubmitting && !recorder.isRecording && !validationError;
|
|
1073
1102
|
const submit = async () => {
|
|
1074
1103
|
if (!canSubmit || submittingRef.current) return;
|
|
1075
|
-
const
|
|
1104
|
+
const value = text;
|
|
1105
|
+
const selectedFiles = files;
|
|
1106
|
+
const clearedRevision = ++revisionRef.current;
|
|
1076
1107
|
submittingRef.current = true;
|
|
1077
|
-
inFlightRef.current = submitted.files;
|
|
1078
1108
|
setIsSubmitting(true);
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1109
|
+
inFlightFilesRef.current = selectedFiles;
|
|
1110
|
+
setTextState("");
|
|
1111
|
+
setFiles([]);
|
|
1112
|
+
focusComposer();
|
|
1082
1113
|
try {
|
|
1083
|
-
await onSubmit(
|
|
1084
|
-
|
|
1085
|
-
files: submitted.files,
|
|
1086
|
-
attachmentIds: submitted.files.flatMap((file) => file.persistedAttachmentId ? [file.persistedAttachmentId] : [])
|
|
1087
|
-
});
|
|
1088
|
-
revokeAgentChatComposerFiles(submitted.files);
|
|
1114
|
+
await options.onSubmit(value, selectedFiles);
|
|
1115
|
+
revokeAgentChatComposerFiles(selectedFiles);
|
|
1089
1116
|
} catch {
|
|
1090
|
-
if (
|
|
1091
|
-
|
|
1117
|
+
if (revisionRef.current === clearedRevision) {
|
|
1118
|
+
revisionRef.current += 1;
|
|
1119
|
+
setTextState(value);
|
|
1120
|
+
setFiles(selectedFiles);
|
|
1121
|
+
} else revokeAgentChatComposerFiles(selectedFiles);
|
|
1092
1122
|
} finally {
|
|
1093
|
-
|
|
1123
|
+
inFlightFilesRef.current = [];
|
|
1094
1124
|
submittingRef.current = false;
|
|
1095
|
-
|
|
1125
|
+
setIsSubmitting(false);
|
|
1096
1126
|
}
|
|
1097
1127
|
};
|
|
1098
1128
|
return {
|
|
1099
1129
|
text,
|
|
1130
|
+
setText,
|
|
1100
1131
|
files,
|
|
1101
|
-
|
|
1132
|
+
addFiles,
|
|
1133
|
+
removeFile,
|
|
1134
|
+
textareaRef,
|
|
1135
|
+
submit,
|
|
1102
1136
|
canSubmit,
|
|
1103
|
-
|
|
1137
|
+
isSubmitting,
|
|
1104
1138
|
attachmentError,
|
|
1105
|
-
|
|
1139
|
+
attachmentMimeTypes,
|
|
1140
|
+
validationError,
|
|
1106
1141
|
recorder,
|
|
1107
|
-
|
|
1108
|
-
fileAccept:
|
|
1109
|
-
allowAttachments: accept ? accept.length > 0 : true,
|
|
1110
|
-
action: chatComposerAction({ hasDraft: Boolean(text.trim() || files.length), isRunning, isCancelling }),
|
|
1111
|
-
setText: (value) => updateDraft(value, draftRef.current.files),
|
|
1112
|
-
addFiles,
|
|
1113
|
-
removeFile: (id) => {
|
|
1114
|
-
revokeAgentChatComposerFiles(draftRef.current.files.filter((file) => file.id === id));
|
|
1115
|
-
updateDraft(draftRef.current.text, draftRef.current.files.filter((file) => file.id !== id));
|
|
1116
|
-
},
|
|
1117
|
-
submit,
|
|
1142
|
+
recordingAllowed: options.recordingEnabled ?? options.inputContract?.modalities.audio.recording_enabled ?? false,
|
|
1143
|
+
fileAccept: (0, import_client2.attachmentFileAccept)(attachmentMimeTypes),
|
|
1118
1144
|
onKeyDown: (event) => {
|
|
1119
1145
|
if (event.key === "Enter" && !event.shiftKey && !event.nativeEvent.isComposing) {
|
|
1120
1146
|
event.preventDefault();
|
|
1121
1147
|
void submit();
|
|
1122
1148
|
}
|
|
1123
|
-
}
|
|
1149
|
+
},
|
|
1150
|
+
allowAttachments: attachmentMimeTypes.length > 0,
|
|
1151
|
+
isExpanded: Boolean(options.forceExpanded || text.includes("\n") || text.length > 62),
|
|
1152
|
+
action: chatComposerAction({ hasDraft: Boolean(text.trim() || files.length), isRunning: Boolean(options.isRunning), isCancelling: Boolean(options.isCancelling) })
|
|
1124
1153
|
};
|
|
1125
1154
|
}
|
|
1126
1155
|
|
|
@@ -1239,7 +1268,6 @@ function AgentChatComposer({
|
|
|
1239
1268
|
contextWindow,
|
|
1240
1269
|
accept,
|
|
1241
1270
|
allowAttachments = true,
|
|
1242
|
-
attachmentLayout = "flow",
|
|
1243
1271
|
className,
|
|
1244
1272
|
disabled = false,
|
|
1245
1273
|
forceExpanded = false,
|
|
@@ -1265,7 +1293,7 @@ function AgentChatComposer({
|
|
|
1265
1293
|
focusKey,
|
|
1266
1294
|
restoredDraft,
|
|
1267
1295
|
textareaRef: localTextareaRef,
|
|
1268
|
-
onSubmit: (
|
|
1296
|
+
onSubmit: (text2, files2) => onSubmit({ text: text2.trim(), files: files2, attachmentIds: files2.flatMap((file) => file.id ? [file.id] : []) })
|
|
1269
1297
|
});
|
|
1270
1298
|
const { text, files, setText, isSubmitting, canSubmit, action, submit } = composer;
|
|
1271
1299
|
const isExpanded = forceExpanded || text.includes("\n") || text.length > 62;
|
|
@@ -1301,26 +1329,8 @@ function AgentChatComposer({
|
|
|
1301
1329
|
event.currentTarget.value = "";
|
|
1302
1330
|
};
|
|
1303
1331
|
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: cn("a24-composer relative mx-auto w-full max-w-3xl overflow-visible", className), "data-agents24-chat-composer": "", children: [
|
|
1304
|
-
files.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1305
|
-
"div",
|
|
1306
|
-
{
|
|
1307
|
-
"data-testid": "agent-chat-composer-attachments",
|
|
1308
|
-
"data-layout": attachmentLayout,
|
|
1309
|
-
className: cn(
|
|
1310
|
-
"a24-composer__attachments min-w-0 py-1",
|
|
1311
|
-
attachmentLayout === "overlay" ? "absolute bottom-full left-0 right-0 mb-1.5 flex-nowrap overflow-x-auto overflow-y-hidden overscroll-x-contain px-2" : "mb-2 flex-wrap px-1"
|
|
1312
|
-
),
|
|
1313
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1314
|
-
ChatAttachmentRows,
|
|
1315
|
-
{
|
|
1316
|
-
attachments: files,
|
|
1317
|
-
onRemove: (attachment) => attachment.id && composer.removeFile(attachment.id)
|
|
1318
|
-
}
|
|
1319
|
-
)
|
|
1320
|
-
}
|
|
1321
|
-
) : null,
|
|
1322
1332
|
composer.attachmentError || composer.recorder.error ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { role: "alert", children: composer.attachmentError || composer.recorder.error }) : null,
|
|
1323
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.
|
|
1333
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
1324
1334
|
"div",
|
|
1325
1335
|
{
|
|
1326
1336
|
"data-expanded": isExpanded ? "" : void 0,
|
|
@@ -1328,118 +1338,129 @@ function AgentChatComposer({
|
|
|
1328
1338
|
"a24-composer__surface relative w-full border border-input bg-background shadow-sm",
|
|
1329
1339
|
isExpanded ? "rounded-xl p-2 pb-1.5" : "rounded-xl px-2 py-[3px]"
|
|
1330
1340
|
),
|
|
1331
|
-
children:
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_lucide_react2.Plus, { className: "size-4" })
|
|
1367
|
-
}
|
|
1368
|
-
)
|
|
1369
|
-
] }),
|
|
1370
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { gridArea: "textarea" }, className: "a24-composer__textarea-slot min-w-0 w-full", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1371
|
-
"textarea",
|
|
1372
|
-
{
|
|
1373
|
-
ref: setTextareaRef,
|
|
1374
|
-
"aria-label": "Message",
|
|
1375
|
-
autoComplete: "off",
|
|
1376
|
-
className: cn(
|
|
1377
|
-
"a24-composer__textarea w-full resize-none border-0 bg-transparent px-1 text-[15px] leading-relaxed text-foreground shadow-none outline-none placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-0",
|
|
1378
|
-
isExpanded ? "min-h-9 max-h-[224px] py-1" : "min-h-9 h-9 max-h-9 py-2"
|
|
1341
|
+
children: [
|
|
1342
|
+
files.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1343
|
+
ChatAttachmentRows,
|
|
1344
|
+
{
|
|
1345
|
+
"data-testid": "agent-chat-composer-attachments",
|
|
1346
|
+
layout: "composer",
|
|
1347
|
+
attachments: files,
|
|
1348
|
+
onRemove: (attachment) => attachment.id && composer.removeFile(attachment.id)
|
|
1349
|
+
}
|
|
1350
|
+
) : null,
|
|
1351
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
1352
|
+
"div",
|
|
1353
|
+
{
|
|
1354
|
+
className: "a24-composer__grid w-full",
|
|
1355
|
+
style: {
|
|
1356
|
+
alignItems: "center",
|
|
1357
|
+
display: "grid",
|
|
1358
|
+
gridTemplateAreas: isExpanded ? `"textarea textarea textarea" "plus toolbar submit"` : `"plus textarea composer mic submit"`,
|
|
1359
|
+
gridTemplateColumns: isExpanded ? "auto 1fr auto" : "auto 1fr auto auto auto",
|
|
1360
|
+
gap: isExpanded ? "3px 10px" : "8px"
|
|
1361
|
+
},
|
|
1362
|
+
children: [
|
|
1363
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { gridArea: "plus" }, className: "a24-composer__attach-slot flex shrink-0 items-center justify-center", children: [
|
|
1364
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1365
|
+
"input",
|
|
1366
|
+
{
|
|
1367
|
+
ref: fileInputRef,
|
|
1368
|
+
accept: composer.fileAccept ?? accept,
|
|
1369
|
+
"aria-label": "Attach files",
|
|
1370
|
+
className: "hidden",
|
|
1371
|
+
multiple: true,
|
|
1372
|
+
name: "attachments",
|
|
1373
|
+
onChange: handleFilesChange,
|
|
1374
|
+
type: "file"
|
|
1375
|
+
}
|
|
1379
1376
|
),
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1377
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1378
|
+
"button",
|
|
1379
|
+
{
|
|
1380
|
+
"aria-label": "Attach files",
|
|
1381
|
+
className: cn("a24-composer__attach", pillButtonClass),
|
|
1382
|
+
disabled: disabled || isSubmitting || !allowAttachments || !composer.allowAttachments,
|
|
1383
|
+
onClick: () => fileInputRef.current?.click(),
|
|
1384
|
+
title: "Attach files",
|
|
1385
|
+
type: "button",
|
|
1386
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_lucide_react2.Plus, { className: "size-4" })
|
|
1387
|
+
}
|
|
1388
|
+
)
|
|
1389
|
+
] }),
|
|
1390
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { gridArea: "textarea" }, className: "a24-composer__textarea-slot min-w-0 w-full", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1391
|
+
"textarea",
|
|
1394
1392
|
{
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1393
|
+
ref: setTextareaRef,
|
|
1394
|
+
"aria-label": "Message",
|
|
1395
|
+
autoComplete: "off",
|
|
1396
|
+
className: cn(
|
|
1397
|
+
"a24-composer__textarea w-full resize-none border-0 bg-transparent px-1 text-[15px] leading-relaxed text-foreground shadow-none outline-none placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-0",
|
|
1398
|
+
isExpanded ? "min-h-9 max-h-[224px] py-1" : "min-h-9 h-9 max-h-9 py-2"
|
|
1399
|
+
),
|
|
1400
|
+
disabled,
|
|
1401
|
+
name: "message",
|
|
1402
|
+
onChange: (event) => setText(event.target.value),
|
|
1403
|
+
onKeyDown: composer.onKeyDown,
|
|
1404
|
+
placeholder,
|
|
1405
|
+
rows: isExpanded ? 2 : 1,
|
|
1406
|
+
value: text
|
|
1404
1407
|
}
|
|
1405
|
-
)
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1408
|
+
) }),
|
|
1409
|
+
isExpanded ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { gridArea: "toolbar" }, className: "a24-composer__toolbar flex min-w-0 items-center gap-1.5", children: [
|
|
1410
|
+
inputToolbarContent,
|
|
1411
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(AgentChatContextStatus, { contextWindow }),
|
|
1412
|
+
composer.recordingAllowed ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1413
|
+
"button",
|
|
1414
|
+
{
|
|
1415
|
+
type: "button",
|
|
1416
|
+
className: pillButtonClass,
|
|
1417
|
+
"aria-label": composer.recorder.isRecording ? "Stop recording" : "Record audio",
|
|
1418
|
+
disabled: disabled || isRunning || isSubmitting,
|
|
1419
|
+
onClick: () => {
|
|
1420
|
+
if (composer.recorder.isRecording) composer.recorder.stop();
|
|
1421
|
+
else void composer.recorder.start();
|
|
1422
|
+
},
|
|
1423
|
+
children: composer.recorder.isRecording ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_lucide_react2.Square, {}) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_lucide_react2.Mic, {})
|
|
1424
|
+
}
|
|
1425
|
+
) : null
|
|
1426
|
+
] }) : null,
|
|
1427
|
+
!isExpanded ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { gridArea: "composer" }, className: "flex min-w-0 items-center gap-1.5" }) : null,
|
|
1428
|
+
!isExpanded ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { gridArea: "mic" }, className: "flex shrink-0 items-center justify-end", children: [
|
|
1429
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(AgentChatContextStatus, { contextWindow }),
|
|
1430
|
+
composer.recordingAllowed ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1431
|
+
"button",
|
|
1432
|
+
{
|
|
1433
|
+
type: "button",
|
|
1434
|
+
className: pillButtonClass,
|
|
1435
|
+
"aria-label": composer.recorder.isRecording ? "Stop recording" : "Record audio",
|
|
1436
|
+
disabled: disabled || isRunning || isSubmitting,
|
|
1437
|
+
onClick: () => {
|
|
1438
|
+
if (composer.recorder.isRecording) composer.recorder.stop();
|
|
1439
|
+
else void composer.recorder.start();
|
|
1440
|
+
},
|
|
1441
|
+
children: composer.recorder.isRecording ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_lucide_react2.Square, {}) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_lucide_react2.Mic, {})
|
|
1442
|
+
}
|
|
1443
|
+
) : null
|
|
1444
|
+
] }) : null,
|
|
1445
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { gridArea: "submit" }, className: "a24-composer__submit-slot flex shrink-0 items-center justify-end gap-1", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1411
1446
|
"button",
|
|
1412
1447
|
{
|
|
1413
|
-
|
|
1414
|
-
className:
|
|
1415
|
-
|
|
1416
|
-
disabled: disabled || isRunning || isSubmitting,
|
|
1448
|
+
"aria-label": isCancelling ? "Cancelling response" : action === "stop" ? "Stop generating" : files.length > 0 ? `Send ${formatFileCount(files)}` : isRunning ? "Queue message" : "Send message",
|
|
1449
|
+
className: cn("a24-composer__submit", submitButtonClass, text.trim().length > 0 && "hover:scale-105"),
|
|
1450
|
+
disabled: isCancelling || (action === "stop" ? disabled : !canSubmit),
|
|
1417
1451
|
onClick: () => {
|
|
1418
|
-
if (
|
|
1419
|
-
else void
|
|
1452
|
+
if (action === "stop") onStop?.();
|
|
1453
|
+
else void submit();
|
|
1420
1454
|
},
|
|
1421
|
-
|
|
1455
|
+
title: composer.validationError || (action === "stop" ? "Stop" : isRunning ? "Queue message" : "Send"),
|
|
1456
|
+
type: "button",
|
|
1457
|
+
children: isCancelling ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_lucide_react2.Loader2, { className: "size-3.5 animate-spin" }) : action === "stop" ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_lucide_react2.Square, { className: "size-3.5 fill-current" }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_lucide_react2.ArrowUp, { className: "size-4", strokeWidth: 2.5 })
|
|
1422
1458
|
}
|
|
1423
|
-
)
|
|
1424
|
-
]
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
"aria-label": isCancelling ? "Cancelling response" : action === "stop" ? "Stop generating" : files.length > 0 ? `Send ${formatFileCount(files)}` : isRunning ? "Queue message" : "Send message",
|
|
1429
|
-
className: cn("a24-composer__submit", submitButtonClass, text.trim().length > 0 && "hover:scale-105"),
|
|
1430
|
-
disabled: isCancelling || (action === "stop" ? disabled : !canSubmit),
|
|
1431
|
-
onClick: () => {
|
|
1432
|
-
if (action === "stop") onStop?.();
|
|
1433
|
-
else void submit();
|
|
1434
|
-
},
|
|
1435
|
-
title: composer.validationError || (action === "stop" ? "Stop" : isRunning ? "Queue message" : "Send"),
|
|
1436
|
-
type: "button",
|
|
1437
|
-
children: isCancelling ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_lucide_react2.Loader2, { className: "size-3.5 animate-spin" }) : action === "stop" ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_lucide_react2.Square, { className: "size-3.5 fill-current" }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_lucide_react2.ArrowUp, { className: "size-4", strokeWidth: 2.5 })
|
|
1438
|
-
}
|
|
1439
|
-
) })
|
|
1440
|
-
]
|
|
1441
|
-
}
|
|
1442
|
-
)
|
|
1459
|
+
) })
|
|
1460
|
+
]
|
|
1461
|
+
}
|
|
1462
|
+
)
|
|
1463
|
+
]
|
|
1443
1464
|
}
|
|
1444
1465
|
),
|
|
1445
1466
|
!isExpanded && inputToolbarContent ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "a24-composer__toolbar a24-composer__toolbar--external mt-2 flex items-center gap-2 px-1", children: inputToolbarContent }) : null
|
|
@@ -1470,7 +1491,7 @@ function QueueRow({ message, onRemove, onEdit }) {
|
|
|
1470
1491
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react3.CornerDownLeft, { "aria-hidden": true, className: "size-4 shrink-0 text-muted-foreground" }),
|
|
1471
1492
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "a24-queue__text min-w-0 flex-1 truncate text-foreground", title: message.text, children: message.text || `${message.attachment_ids.length} attachment${message.attachment_ids.length === 1 ? "" : "s"}` }),
|
|
1472
1493
|
message.status === "blocked" ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "shrink-0 text-xs text-amber-700", children: "Needs attention" }) : null,
|
|
1473
|
-
["steer_pending", "delivering"].includes(message.status) ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "shrink-0 text-xs text-muted-foreground", children: "
|
|
1494
|
+
["steer_pending", "delivering"].includes(message.status) ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "shrink-0 text-xs text-muted-foreground", children: "Sending\u2026" }) : null,
|
|
1474
1495
|
editable ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
|
|
1475
1496
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1476
1497
|
"button",
|
|
@@ -1584,6 +1605,21 @@ function AgentChatDefaultActions({
|
|
|
1584
1605
|
React8.useEffect(() => () => {
|
|
1585
1606
|
if (copyResetTimer.current !== null) window.clearTimeout(copyResetTimer.current);
|
|
1586
1607
|
}, []);
|
|
1608
|
+
const [feedbackPending, setFeedbackPending] = React8.useState(false);
|
|
1609
|
+
const feedbackInFlight = React8.useRef(false);
|
|
1610
|
+
const writeFeedback = async (input, close = false) => {
|
|
1611
|
+
if (feedbackInFlight.current) return;
|
|
1612
|
+
feedbackInFlight.current = true;
|
|
1613
|
+
setFeedbackPending(true);
|
|
1614
|
+
try {
|
|
1615
|
+
await handlers?.onSetFeedback?.(message, input);
|
|
1616
|
+
if (close) setFeedbackOpen(false);
|
|
1617
|
+
} catch {
|
|
1618
|
+
} finally {
|
|
1619
|
+
feedbackInFlight.current = false;
|
|
1620
|
+
setFeedbackPending(false);
|
|
1621
|
+
}
|
|
1622
|
+
};
|
|
1587
1623
|
const copied = handlers?.copied ?? optimisticCopied;
|
|
1588
1624
|
const copyMessage = () => {
|
|
1589
1625
|
handlers?.onCopy?.(message.content || "", message.id);
|
|
@@ -1627,7 +1663,8 @@ function AgentChatDefaultActions({
|
|
|
1627
1663
|
ChatMessageAction,
|
|
1628
1664
|
{
|
|
1629
1665
|
label: "Like",
|
|
1630
|
-
|
|
1666
|
+
disabled: feedbackPending,
|
|
1667
|
+
onClick: () => void writeFeedback({
|
|
1631
1668
|
rating: handlers.feedback?.rating === "like" ? null : "like"
|
|
1632
1669
|
}),
|
|
1633
1670
|
tooltip: "Like this response",
|
|
@@ -1638,10 +1675,11 @@ function AgentChatDefaultActions({
|
|
|
1638
1675
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_radix_ui.Popover.Trigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1639
1676
|
ChatMessageAction,
|
|
1640
1677
|
{
|
|
1678
|
+
disabled: feedbackPending,
|
|
1641
1679
|
label: "Dislike",
|
|
1642
1680
|
onClick: () => {
|
|
1643
1681
|
if (handlers.feedback?.rating !== "dislike") {
|
|
1644
|
-
void
|
|
1682
|
+
void writeFeedback({ rating: "dislike" });
|
|
1645
1683
|
}
|
|
1646
1684
|
setFeedbackOpen(true);
|
|
1647
1685
|
},
|
|
@@ -1691,9 +1729,10 @@ function AgentChatDefaultActions({
|
|
|
1691
1729
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1692
1730
|
"button",
|
|
1693
1731
|
{
|
|
1732
|
+
disabled: feedbackPending,
|
|
1694
1733
|
className: "rounded-md px-3 py-2 text-sm font-medium hover:bg-muted",
|
|
1695
1734
|
onClick: () => {
|
|
1696
|
-
void
|
|
1735
|
+
void writeFeedback({ rating: null }, true);
|
|
1697
1736
|
},
|
|
1698
1737
|
type: "button",
|
|
1699
1738
|
children: "Remove feedback"
|
|
@@ -1702,9 +1741,10 @@ function AgentChatDefaultActions({
|
|
|
1702
1741
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1703
1742
|
"button",
|
|
1704
1743
|
{
|
|
1744
|
+
disabled: feedbackPending,
|
|
1705
1745
|
className: "rounded-md bg-primary px-3 py-2 text-sm font-medium text-primary-foreground hover:opacity-90",
|
|
1706
1746
|
onClick: () => {
|
|
1707
|
-
void
|
|
1747
|
+
void writeFeedback({ rating: "dislike", reason, comment }, true);
|
|
1708
1748
|
},
|
|
1709
1749
|
type: "button",
|
|
1710
1750
|
children: "Save"
|
|
@@ -2608,16 +2648,17 @@ function ToolActivityReel({
|
|
|
2608
2648
|
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: previous ? "a24-tool-reel__current" : void 0, children: displayed })
|
|
2609
2649
|
] });
|
|
2610
2650
|
}
|
|
2611
|
-
function ToolActivityOrb({ label }) {
|
|
2612
|
-
return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "a24-tool-orb", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_thinking_orbs.ThinkingOrb, { "aria-label": label
|
|
2651
|
+
function ToolActivityOrb({ label = "Working", kind = "tool", part, renderActivityIcon }) {
|
|
2652
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "a24-tool-orb", children: renderActivityIcon ? renderActivityIcon({ label, kind, part }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_thinking_orbs.ThinkingOrb, { "aria-label": label, size: 20, speed: 0.8, state: "searching" }) });
|
|
2613
2653
|
}
|
|
2614
2654
|
function AgentChatActivityRow({
|
|
2615
2655
|
label,
|
|
2616
2656
|
modelTurnId,
|
|
2657
|
+
renderActivityIcon,
|
|
2617
2658
|
onSelect
|
|
2618
2659
|
}) {
|
|
2619
2660
|
const content = /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
|
|
2620
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ToolActivityOrb, { label }),
|
|
2661
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ToolActivityOrb, { label, kind: "planning", renderActivityIcon }),
|
|
2621
2662
|
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "a24-tool-trigger__label a24-tool-shimmer", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ToolActivityReel, { label }) })
|
|
2622
2663
|
] });
|
|
2623
2664
|
return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "a24-tool-trigger-row a24-tool-trigger-row--root", "data-agents24-model-turn": modelTurnId, role: "status", children: onSelect ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("button", { className: "a24-tool-trigger a24-tool-trigger--root a24-tool-trigger--active", onClick: onSelect, type: "button", children: content }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "a24-tool-trigger a24-tool-trigger--root a24-tool-trigger--active", children: content }) });
|
|
@@ -2626,6 +2667,7 @@ function AgentChatModelTurnRow({ part }) {
|
|
|
2626
2667
|
return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(AgentChatActivityRow, { label: "Planning next moves", modelTurnId: part.modelTurnId });
|
|
2627
2668
|
}
|
|
2628
2669
|
function AgentChatNodeActivityRow({
|
|
2670
|
+
renderActivityIcon,
|
|
2629
2671
|
onSelect,
|
|
2630
2672
|
part
|
|
2631
2673
|
}) {
|
|
@@ -2644,10 +2686,10 @@ function AgentChatNodeActivityRow({
|
|
|
2644
2686
|
const label = running ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "a24-tool-shimmer", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ToolActivityReel, { label: part.label }) }) : part.label;
|
|
2645
2687
|
if (!failed) {
|
|
2646
2688
|
return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "a24-tool-trigger-row", role: "status", children: onSelect ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("button", { className: triggerClassName, onClick: () => onSelect(part), type: "button", children: [
|
|
2647
|
-
running ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ToolActivityOrb, { label: part.label }) : null,
|
|
2689
|
+
running ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ToolActivityOrb, { label: part.label, kind: "node", part, renderActivityIcon }) : null,
|
|
2648
2690
|
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "a24-tool-trigger__label", children: label })
|
|
2649
2691
|
] }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: triggerClassName, children: [
|
|
2650
|
-
running ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ToolActivityOrb, { label: part.label }) : null,
|
|
2692
|
+
running ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ToolActivityOrb, { label: part.label, kind: "node", part, renderActivityIcon }) : null,
|
|
2651
2693
|
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "a24-tool-trigger__label", children: label })
|
|
2652
2694
|
] }) });
|
|
2653
2695
|
}
|
|
@@ -2693,6 +2735,7 @@ function ToolDisclosure({
|
|
|
2693
2735
|
root,
|
|
2694
2736
|
showOrb,
|
|
2695
2737
|
statusLabel,
|
|
2738
|
+
renderActivityIcon,
|
|
2696
2739
|
accessory
|
|
2697
2740
|
}) {
|
|
2698
2741
|
const [open, setOpen] = React11.useState(false);
|
|
@@ -2702,10 +2745,10 @@ function ToolDisclosure({
|
|
|
2702
2745
|
const triggerClassName = cn("a24-tool-trigger", root && "a24-tool-trigger--root", root && !showOrb && "a24-tool-trigger--without-orb", error && "a24-tool-trigger--error", loading && "a24-tool-trigger--active");
|
|
2703
2746
|
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: cn("a24-tool-trigger-row", root && "a24-tool-trigger-row--root"), children: [
|
|
2704
2747
|
onSelect && part ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("button", { className: triggerClassName, onClick: () => onSelect(part), type: "button", children: [
|
|
2705
|
-
showOrb ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ToolActivityOrb, { label: statusLabel }) : null,
|
|
2748
|
+
showOrb ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ToolActivityOrb, { label: statusLabel || "Working", kind: grouped ? "group" : "tool", part, renderActivityIcon }) : null,
|
|
2706
2749
|
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "a24-tool-trigger__label", children: label })
|
|
2707
2750
|
] }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: triggerClassName, children: [
|
|
2708
|
-
showOrb ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ToolActivityOrb, { label: statusLabel }) : null,
|
|
2751
|
+
showOrb ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ToolActivityOrb, { label: statusLabel || "Working", kind: grouped ? "group" : "tool", part, renderActivityIcon }) : null,
|
|
2709
2752
|
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "a24-tool-trigger__label", children: label })
|
|
2710
2753
|
] }),
|
|
2711
2754
|
accessory
|
|
@@ -2727,7 +2770,7 @@ function ToolDisclosure({
|
|
|
2727
2770
|
},
|
|
2728
2771
|
type: "button",
|
|
2729
2772
|
children: [
|
|
2730
|
-
showOrb ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ToolActivityOrb, { label: statusLabel }) : null,
|
|
2773
|
+
showOrb ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ToolActivityOrb, { label: statusLabel || "Working", kind: grouped ? "group" : "tool", part, renderActivityIcon }) : null,
|
|
2731
2774
|
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "a24-tool-trigger__label", children: label }),
|
|
2732
2775
|
open ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_lucide_react6.ChevronDown, { "aria-hidden": "true", className: "a24-tool-trigger__chevron" }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_lucide_react6.ChevronRight, { "aria-hidden": "true", className: "a24-tool-trigger__chevron" })
|
|
2733
2776
|
]
|
|
@@ -2788,6 +2831,7 @@ function AgentChatToolRow({
|
|
|
2788
2831
|
return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { "data-agents24-tool-part": part.type, "data-state": part.state, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
|
|
2789
2832
|
ToolDisclosure,
|
|
2790
2833
|
{
|
|
2834
|
+
renderActivityIcon: slots?.renderActivityIcon,
|
|
2791
2835
|
error: failed,
|
|
2792
2836
|
accessory: toolAccessory(context, slots),
|
|
2793
2837
|
label: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ToolLabel, { active, label: context.label, part, slots }),
|
|
@@ -2834,6 +2878,7 @@ function AgentChatToolSession({
|
|
|
2834
2878
|
return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
2835
2879
|
ToolDisclosure,
|
|
2836
2880
|
{
|
|
2881
|
+
renderActivityIcon: slots?.renderActivityIcon,
|
|
2837
2882
|
error: failed,
|
|
2838
2883
|
grouped: true,
|
|
2839
2884
|
label: labelContent,
|
|
@@ -2878,6 +2923,7 @@ function AgentChatSubagentGroup({ isLatestActivity = true, isLive = false, part,
|
|
|
2878
2923
|
return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
2879
2924
|
ToolDisclosure,
|
|
2880
2925
|
{
|
|
2926
|
+
renderActivityIcon: slots?.renderActivityIcon,
|
|
2881
2927
|
accessory: slots?.renderToolAccessory?.(context),
|
|
2882
2928
|
error: failed,
|
|
2883
2929
|
grouped: true,
|
|
@@ -3137,14 +3183,14 @@ function AgentResponseTimeline({
|
|
|
3137
3183
|
if (part.kind === "model-turn") {
|
|
3138
3184
|
const key = activeActivityClaimed ? part.id : `${message.id}-active-activity`;
|
|
3139
3185
|
activeActivityClaimed = true;
|
|
3140
|
-
rendered.push(/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(AgentChatActivityRow, { label: "Planning next moves", modelTurnId: part.modelTurnId }, key));
|
|
3186
|
+
rendered.push(/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(AgentChatActivityRow, { renderActivityIcon: toolSlots?.renderActivityIcon, label: "Planning next moves", modelTurnId: part.modelTurnId }, key));
|
|
3141
3187
|
continue;
|
|
3142
3188
|
}
|
|
3143
3189
|
if (part.kind === "node-activity") {
|
|
3144
3190
|
const key = activeActivityClaimed ? part.id : `${message.id}-active-activity`;
|
|
3145
3191
|
if (part.status === "running") activeActivityClaimed = true;
|
|
3146
3192
|
rendered.push(
|
|
3147
|
-
part.status === "running" ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(AgentChatActivityRow, { label: part.label, onSelect: onNodeSelect ? () => onNodeSelect(part) : void 0 }, key) : /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(AgentChatNodeActivityRow, { onSelect: onNodeSelect, part }, part.id)
|
|
3193
|
+
part.status === "running" ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(AgentChatActivityRow, { renderActivityIcon: toolSlots?.renderActivityIcon, label: part.label, onSelect: onNodeSelect ? () => onNodeSelect(part) : void 0 }, key) : /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(AgentChatNodeActivityRow, { renderActivityIcon: toolSlots?.renderActivityIcon, onSelect: onNodeSelect, part }, part.id)
|
|
3148
3194
|
);
|
|
3149
3195
|
continue;
|
|
3150
3196
|
}
|
|
@@ -3173,7 +3219,7 @@ function AgentResponseTimeline({
|
|
|
3173
3219
|
rendered.push(/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(MessageResponse, { children: message.content }, `${message.id}-fallback`));
|
|
3174
3220
|
}
|
|
3175
3221
|
if (rendered.length === 0 && isLoading) {
|
|
3176
|
-
rendered.push(/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(AgentChatActivityRow, { label: "Thinking" }, `${message.id}-active-activity`));
|
|
3222
|
+
rendered.push(/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(AgentChatActivityRow, { renderActivityIcon: toolSlots?.renderActivityIcon, label: "Thinking" }, `${message.id}-active-activity`));
|
|
3177
3223
|
}
|
|
3178
3224
|
return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "space-y-3", children: rendered });
|
|
3179
3225
|
}
|