@agents24/chat-react 0.5.30 → 0.5.31
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.js
CHANGED
|
@@ -4,8 +4,8 @@ import {
|
|
|
4
4
|
} from "../chunk-5PWUQTLK.js";
|
|
5
5
|
import {
|
|
6
6
|
attachmentFileAccept,
|
|
7
|
+
canonicalInputMimeType,
|
|
7
8
|
inputMimeTypes,
|
|
8
|
-
normalizeAttachmentMimeType,
|
|
9
9
|
validateAgentChatSubmission
|
|
10
10
|
} from "../chunk-INO5DSSJ.js";
|
|
11
11
|
|
|
@@ -489,6 +489,7 @@ function attachmentRenderKey(data, index, rowKind) {
|
|
|
489
489
|
function ChatImageAttachment({
|
|
490
490
|
className,
|
|
491
491
|
data,
|
|
492
|
+
presentation = "message",
|
|
492
493
|
onRemove,
|
|
493
494
|
resolveContent,
|
|
494
495
|
...props
|
|
@@ -568,7 +569,7 @@ function ChatImageAttachment({
|
|
|
568
569
|
Attachment,
|
|
569
570
|
{
|
|
570
571
|
...props,
|
|
571
|
-
className: cn("a24-chat-image-attachment", className),
|
|
572
|
+
className: cn("a24-chat-image-attachment", presentation === "composer" && "a24-composer-attachment-card", className),
|
|
572
573
|
orientation: "vertical",
|
|
573
574
|
ref: rootRef,
|
|
574
575
|
state: status === "error" ? "error" : status === "done" ? "done" : "processing",
|
|
@@ -591,6 +592,7 @@ function ChatImageAttachment({
|
|
|
591
592
|
status !== "done" && status !== "error" ? React.createElement("span", { className: "sr-only", role: "status" }, `Loading ${filename}`) : null,
|
|
592
593
|
status === "error" ? React.createElement("span", { className: "sr-only" }, `${filename} preview unavailable`) : null
|
|
593
594
|
),
|
|
595
|
+
presentation === "composer" ? React.createElement("span", { className: "a24-composer-image-caption", title: filename }, filename) : null,
|
|
594
596
|
onRemove ? React.createElement(
|
|
595
597
|
"button",
|
|
596
598
|
{
|
|
@@ -609,6 +611,7 @@ function ChatImageAttachment({
|
|
|
609
611
|
function ChatAttachment({
|
|
610
612
|
className,
|
|
611
613
|
data,
|
|
614
|
+
presentation = "message",
|
|
612
615
|
onRemove,
|
|
613
616
|
orientation,
|
|
614
617
|
resolveContent,
|
|
@@ -621,6 +624,7 @@ function ChatAttachment({
|
|
|
621
624
|
...props,
|
|
622
625
|
className,
|
|
623
626
|
data,
|
|
627
|
+
presentation,
|
|
624
628
|
onRemove,
|
|
625
629
|
resolveContent
|
|
626
630
|
});
|
|
@@ -628,11 +632,16 @@ function ChatAttachment({
|
|
|
628
632
|
return React.createElement(
|
|
629
633
|
Attachment,
|
|
630
634
|
{
|
|
631
|
-
className: cn("a24-chat-attachment", className),
|
|
635
|
+
className: cn("a24-chat-attachment", presentation === "composer" && "a24-composer-attachment-card", className),
|
|
632
636
|
orientation: orientation || "horizontal",
|
|
633
637
|
size: "sm",
|
|
634
638
|
...props
|
|
635
639
|
},
|
|
640
|
+
presentation === "composer" ? React.createElement(
|
|
641
|
+
"div",
|
|
642
|
+
{ className: "a24-composer-file-preview", "aria-hidden": true },
|
|
643
|
+
React.createElement(FileIcon, { size: 64, type: fileIconType, variant: "solid" })
|
|
644
|
+
) : null,
|
|
636
645
|
React.createElement(
|
|
637
646
|
AttachmentMedia,
|
|
638
647
|
Object.assign(
|
|
@@ -690,6 +699,7 @@ function ChatAttachments({
|
|
|
690
699
|
}
|
|
691
700
|
function ChatAttachmentRows({
|
|
692
701
|
attachments,
|
|
702
|
+
layout = "message",
|
|
693
703
|
className,
|
|
694
704
|
dir,
|
|
695
705
|
onRemove,
|
|
@@ -705,6 +715,7 @@ function ChatAttachmentRows({
|
|
|
705
715
|
items.map(
|
|
706
716
|
(attachment, index) => React.createElement(ChatAttachment, {
|
|
707
717
|
data: attachment,
|
|
718
|
+
presentation: layout,
|
|
708
719
|
dir: "ltr",
|
|
709
720
|
key: attachmentRenderKey(attachment, index, kind),
|
|
710
721
|
onRemove: onRemove ? () => onRemove(attachment) : void 0,
|
|
@@ -715,12 +726,12 @@ function ChatAttachmentRows({
|
|
|
715
726
|
return React.createElement(
|
|
716
727
|
"div",
|
|
717
728
|
{
|
|
718
|
-
className: cn("a24-chat-attachment-rows", className),
|
|
729
|
+
className: cn("a24-chat-attachment-rows", layout === "composer" && "a24-composer-attachment-row", className),
|
|
719
730
|
dir,
|
|
720
731
|
...props
|
|
721
732
|
},
|
|
722
|
-
renderRow(imageAttachments, "images"),
|
|
723
|
-
renderRow(fileAttachments, "files")
|
|
733
|
+
layout === "composer" ? renderRow(attachments, "mixed") : renderRow(imageAttachments, "images"),
|
|
734
|
+
layout === "message" ? renderRow(fileAttachments, "files") : null
|
|
724
735
|
);
|
|
725
736
|
}
|
|
726
737
|
|
|
@@ -728,7 +739,7 @@ function ChatAttachmentRows({
|
|
|
728
739
|
import * as React4 from "react";
|
|
729
740
|
|
|
730
741
|
// src/ui/composer-attachments.ts
|
|
731
|
-
import { normalizeAttachmentMimeType
|
|
742
|
+
import { normalizeAttachmentMimeType } from "@agents24/client";
|
|
732
743
|
var createId = () => typeof crypto !== "undefined" && "randomUUID" in crypto ? crypto.randomUUID() : `file-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
733
744
|
function createAgentChatComposerFile(file) {
|
|
734
745
|
return {
|
|
@@ -736,7 +747,7 @@ function createAgentChatComposerFile(file) {
|
|
|
736
747
|
type: "file",
|
|
737
748
|
url: URL.createObjectURL(file),
|
|
738
749
|
filename: file.name || "attachment",
|
|
739
|
-
mediaType:
|
|
750
|
+
mediaType: normalizeAttachmentMimeType(file.type, file.name),
|
|
740
751
|
source: file
|
|
741
752
|
};
|
|
742
753
|
}
|
|
@@ -791,32 +802,38 @@ function useChatComposerFocus({ textareaRef, disabled, focusKey, restoredDraft,
|
|
|
791
802
|
// src/ui/use-audio-recorder.ts
|
|
792
803
|
import * as React3 from "react";
|
|
793
804
|
var RECORDING_CANDIDATES = ["audio/webm;codecs=opus", "audio/webm", "audio/mp4"];
|
|
794
|
-
function recordingExtension(mimeType) {
|
|
795
|
-
return mimeType.startsWith("audio/mp4") ? "m4a" : "webm";
|
|
796
|
-
}
|
|
797
805
|
function useAudioRecorder(onRecorded, options = {}) {
|
|
798
|
-
const mountedRef = React3.useRef(true);
|
|
799
|
-
const startingRef = React3.useRef(false);
|
|
800
806
|
const recorderRef = React3.useRef(null);
|
|
801
807
|
const streamRef = React3.useRef(null);
|
|
808
|
+
const pendingRef = React3.useRef(false);
|
|
809
|
+
const generationRef = React3.useRef(0);
|
|
802
810
|
const onRecordedRef = React3.useRef(onRecorded);
|
|
803
811
|
const onStreamChangeRef = React3.useRef(options.onStreamChange);
|
|
804
|
-
|
|
805
|
-
|
|
812
|
+
React3.useEffect(() => {
|
|
813
|
+
onRecordedRef.current = onRecorded;
|
|
814
|
+
onStreamChangeRef.current = options.onStreamChange;
|
|
815
|
+
});
|
|
806
816
|
const [isRecording, setIsRecording] = React3.useState(false);
|
|
807
817
|
const [error, setError] = React3.useState(null);
|
|
818
|
+
const release = React3.useCallback(() => {
|
|
819
|
+
streamRef.current?.getTracks().forEach((track) => track.stop());
|
|
820
|
+
streamRef.current = null;
|
|
821
|
+
recorderRef.current = null;
|
|
822
|
+
onStreamChangeRef.current?.(null);
|
|
823
|
+
}, []);
|
|
808
824
|
const stop = React3.useCallback(() => {
|
|
809
|
-
|
|
825
|
+
const recorder = recorderRef.current;
|
|
826
|
+
if (recorder && recorder.state !== "inactive") recorder.stop();
|
|
810
827
|
}, []);
|
|
811
828
|
const start = React3.useCallback(async () => {
|
|
812
|
-
if (
|
|
813
|
-
startingRef.current = true;
|
|
829
|
+
if (pendingRef.current || recorderRef.current) return;
|
|
814
830
|
setError(null);
|
|
815
831
|
if (!navigator.mediaDevices?.getUserMedia || typeof MediaRecorder === "undefined") {
|
|
816
832
|
setError("Audio recording is not supported in this browser.");
|
|
817
|
-
startingRef.current = false;
|
|
818
833
|
return;
|
|
819
834
|
}
|
|
835
|
+
pendingRef.current = true;
|
|
836
|
+
const generation = generationRef.current;
|
|
820
837
|
try {
|
|
821
838
|
const mimeType = RECORDING_CANDIDATES.find((candidate) => MediaRecorder.isTypeSupported(candidate));
|
|
822
839
|
if (!mimeType) {
|
|
@@ -824,158 +841,169 @@ function useAudioRecorder(onRecorded, options = {}) {
|
|
|
824
841
|
return;
|
|
825
842
|
}
|
|
826
843
|
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
|
|
827
|
-
if (
|
|
844
|
+
if (generation !== generationRef.current) {
|
|
828
845
|
stream.getTracks().forEach((track) => track.stop());
|
|
829
846
|
return;
|
|
830
847
|
}
|
|
831
848
|
streamRef.current = stream;
|
|
832
|
-
onStreamChangeRef.current?.(stream);
|
|
833
849
|
const recorder = new MediaRecorder(stream, { mimeType });
|
|
850
|
+
recorderRef.current = recorder;
|
|
834
851
|
const chunks = [];
|
|
835
852
|
recorder.ondataavailable = (event) => {
|
|
836
853
|
if (event.data.size) chunks.push(event.data);
|
|
837
854
|
};
|
|
838
855
|
recorder.onstop = () => {
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
856
|
+
if (generation !== generationRef.current) return;
|
|
857
|
+
const actualType = recorder.mimeType || mimeType;
|
|
858
|
+
const extension = actualType.startsWith("audio/mp4") ? "m4a" : "webm";
|
|
859
|
+
try {
|
|
860
|
+
onRecordedRef.current(new File([new Blob(chunks, { type: actualType })], `recording-${Date.now()}.${extension}`, { type: actualType }));
|
|
861
|
+
} finally {
|
|
862
|
+
release();
|
|
863
|
+
setIsRecording(false);
|
|
864
|
+
}
|
|
865
|
+
};
|
|
866
|
+
recorder.onerror = () => {
|
|
867
|
+
recorder.onstop = null;
|
|
868
|
+
if (recorder.state !== "inactive") recorder.stop();
|
|
869
|
+
release();
|
|
870
|
+
setIsRecording(false);
|
|
871
|
+
setError("Audio recording failed. Try again.");
|
|
846
872
|
};
|
|
847
|
-
recorderRef.current = recorder;
|
|
848
|
-
streamRef.current = stream;
|
|
849
873
|
recorder.start();
|
|
850
874
|
setIsRecording(true);
|
|
875
|
+
onStreamChangeRef.current?.(stream);
|
|
851
876
|
} catch {
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
877
|
+
if (generation === generationRef.current) {
|
|
878
|
+
release();
|
|
879
|
+
setIsRecording(false);
|
|
880
|
+
setError("Microphone access is unavailable.");
|
|
881
|
+
}
|
|
857
882
|
} finally {
|
|
858
|
-
|
|
859
|
-
}
|
|
860
|
-
}, []);
|
|
861
|
-
React3.useEffect(() => {
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
883
|
+
if (generation === generationRef.current) pendingRef.current = false;
|
|
884
|
+
}
|
|
885
|
+
}, [release]);
|
|
886
|
+
React3.useEffect(() => () => {
|
|
887
|
+
generationRef.current += 1;
|
|
888
|
+
pendingRef.current = false;
|
|
889
|
+
const recorder = recorderRef.current;
|
|
890
|
+
if (recorder) {
|
|
891
|
+
recorder.onstop = null;
|
|
892
|
+
recorder.onerror = null;
|
|
893
|
+
if (recorder.state !== "inactive") recorder.stop();
|
|
894
|
+
}
|
|
895
|
+
release();
|
|
896
|
+
}, [release]);
|
|
870
897
|
return { error, isRecording, start, stop };
|
|
871
898
|
}
|
|
872
899
|
|
|
873
900
|
// src/ui/use-agent-chat-composer.ts
|
|
874
|
-
function useAgentChatComposer({
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
isCancelling = false,
|
|
878
|
-
inputContract,
|
|
879
|
-
recordingEnabled,
|
|
880
|
-
focusKey,
|
|
881
|
-
restoredDraft,
|
|
882
|
-
textareaRef,
|
|
883
|
-
onSubmit
|
|
884
|
-
}) {
|
|
885
|
-
const [text, setDraftText] = React4.useState("");
|
|
886
|
-
const [files, setDraftFiles] = React4.useState([]);
|
|
887
|
-
const [isSubmitting, setIsSubmitting] = React4.useState(false);
|
|
901
|
+
function useAgentChatComposer(options) {
|
|
902
|
+
const [text, setTextState] = React4.useState("");
|
|
903
|
+
const [files, setFiles] = React4.useState([]);
|
|
888
904
|
const [attachmentError, setAttachmentError] = React4.useState(null);
|
|
889
|
-
const
|
|
890
|
-
const
|
|
905
|
+
const [isSubmitting, setIsSubmitting] = React4.useState(false);
|
|
906
|
+
const localTextareaRef = React4.useRef(null);
|
|
907
|
+
const textareaRef = options.textareaRef ?? localTextareaRef;
|
|
908
|
+
const filesRef = React4.useRef(files);
|
|
909
|
+
const inFlightFilesRef = React4.useRef([]);
|
|
910
|
+
const revisionRef = React4.useRef(0);
|
|
891
911
|
const submittingRef = React4.useRef(false);
|
|
892
|
-
const mountedRef = React4.useRef(true);
|
|
893
|
-
const updateDraft = (nextText, nextFiles) => {
|
|
894
|
-
draftRef.current = { text: nextText, files: nextFiles, revision: draftRef.current.revision + 1 };
|
|
895
|
-
setDraftText(nextText);
|
|
896
|
-
setDraftFiles(nextFiles);
|
|
897
|
-
};
|
|
898
912
|
React4.useEffect(() => {
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
};
|
|
913
|
+
filesRef.current = files;
|
|
914
|
+
}, [files]);
|
|
915
|
+
React4.useEffect(() => () => {
|
|
916
|
+
revokeAgentChatComposerFiles(filesRef.current);
|
|
917
|
+
revokeAgentChatComposerFiles(inFlightFilesRef.current);
|
|
905
918
|
}, []);
|
|
906
|
-
const
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
919
|
+
const focusComposer = useChatComposerFocus({
|
|
920
|
+
textareaRef,
|
|
921
|
+
disabled: Boolean(options.disabled),
|
|
922
|
+
focusKey: options.focusKey,
|
|
923
|
+
restoredDraft: options.restoredDraft,
|
|
924
|
+
onRestore: (draft) => {
|
|
925
|
+
revisionRef.current += 1;
|
|
926
|
+
revokeAgentChatComposerFiles(filesRef.current);
|
|
927
|
+
setTextState(draft.text);
|
|
928
|
+
setFiles(draft.files);
|
|
929
|
+
}
|
|
930
|
+
});
|
|
931
|
+
const attachmentMimeTypes = inputMimeTypes(options.inputContract);
|
|
932
|
+
const setText = (value) => {
|
|
933
|
+
revisionRef.current += 1;
|
|
934
|
+
setTextState(value);
|
|
935
|
+
};
|
|
911
936
|
const addFiles = (selected) => {
|
|
912
|
-
const accepted =
|
|
937
|
+
const accepted = selected.filter((file) => attachmentMimeTypes.includes(canonicalInputMimeType(file.type, file.name)));
|
|
938
|
+
revisionRef.current += 1;
|
|
939
|
+
const additions = accepted.map(createAgentChatComposerFile);
|
|
940
|
+
setFiles((current) => [...current, ...additions]);
|
|
913
941
|
setAttachmentError(accepted.length === selected.length ? null : "One or more selected file types are not supported by this Agent.");
|
|
914
|
-
updateDraft(draftRef.current.text, [...draftRef.current.files, ...accepted.map(createAgentChatComposerFile)]);
|
|
915
942
|
};
|
|
916
|
-
const
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
if (
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
const
|
|
923
|
-
|
|
943
|
+
const removeFile = (id) => {
|
|
944
|
+
revisionRef.current += 1;
|
|
945
|
+
const removed = filesRef.current.find((file) => file.id === id);
|
|
946
|
+
if (removed) revokeAgentChatComposerFiles([removed]);
|
|
947
|
+
setFiles((current) => current.filter((file) => file.id !== id));
|
|
948
|
+
};
|
|
949
|
+
const recorder = useAudioRecorder((file) => addFiles([file]), { onStreamChange: options.onRecordingStreamChange });
|
|
950
|
+
const validationError = options.inputContract ? validateAgentChatSubmission(
|
|
951
|
+
options.inputContract,
|
|
924
952
|
text,
|
|
925
|
-
files.map((file) => ({ data: file.source || new Uint8Array(), mediaType: file.mediaType, name: file.filename
|
|
953
|
+
files.map((file) => ({ data: file.source || new Uint8Array(), mediaType: file.mediaType, name: file.filename }))
|
|
926
954
|
) : null;
|
|
927
|
-
const canSubmit = !disabled && !isCancelling && !isSubmitting && !recorder.isRecording && !validationError
|
|
955
|
+
const canSubmit = Boolean(text.trim() || files.length) && !options.disabled && !options.isCancelling && !isSubmitting && !recorder.isRecording && !validationError;
|
|
928
956
|
const submit = async () => {
|
|
929
957
|
if (!canSubmit || submittingRef.current) return;
|
|
930
|
-
const
|
|
958
|
+
const value = text;
|
|
959
|
+
const selectedFiles = files;
|
|
960
|
+
const clearedRevision = ++revisionRef.current;
|
|
931
961
|
submittingRef.current = true;
|
|
932
|
-
inFlightRef.current = submitted.files;
|
|
933
962
|
setIsSubmitting(true);
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
963
|
+
inFlightFilesRef.current = selectedFiles;
|
|
964
|
+
setTextState("");
|
|
965
|
+
setFiles([]);
|
|
966
|
+
focusComposer();
|
|
937
967
|
try {
|
|
938
|
-
await onSubmit(
|
|
939
|
-
|
|
940
|
-
files: submitted.files,
|
|
941
|
-
attachmentIds: submitted.files.flatMap((file) => file.persistedAttachmentId ? [file.persistedAttachmentId] : [])
|
|
942
|
-
});
|
|
943
|
-
revokeAgentChatComposerFiles(submitted.files);
|
|
968
|
+
await options.onSubmit(value, selectedFiles);
|
|
969
|
+
revokeAgentChatComposerFiles(selectedFiles);
|
|
944
970
|
} catch {
|
|
945
|
-
if (
|
|
946
|
-
|
|
971
|
+
if (revisionRef.current === clearedRevision) {
|
|
972
|
+
revisionRef.current += 1;
|
|
973
|
+
setTextState(value);
|
|
974
|
+
setFiles(selectedFiles);
|
|
975
|
+
} else revokeAgentChatComposerFiles(selectedFiles);
|
|
947
976
|
} finally {
|
|
948
|
-
|
|
977
|
+
inFlightFilesRef.current = [];
|
|
949
978
|
submittingRef.current = false;
|
|
950
|
-
|
|
979
|
+
setIsSubmitting(false);
|
|
951
980
|
}
|
|
952
981
|
};
|
|
953
982
|
return {
|
|
954
983
|
text,
|
|
984
|
+
setText,
|
|
955
985
|
files,
|
|
956
|
-
|
|
986
|
+
addFiles,
|
|
987
|
+
removeFile,
|
|
988
|
+
textareaRef,
|
|
989
|
+
submit,
|
|
957
990
|
canSubmit,
|
|
958
|
-
|
|
991
|
+
isSubmitting,
|
|
959
992
|
attachmentError,
|
|
960
|
-
|
|
993
|
+
attachmentMimeTypes,
|
|
994
|
+
validationError,
|
|
961
995
|
recorder,
|
|
962
|
-
|
|
963
|
-
fileAccept:
|
|
964
|
-
allowAttachments: accept ? accept.length > 0 : true,
|
|
965
|
-
action: chatComposerAction({ hasDraft: Boolean(text.trim() || files.length), isRunning, isCancelling }),
|
|
966
|
-
setText: (value) => updateDraft(value, draftRef.current.files),
|
|
967
|
-
addFiles,
|
|
968
|
-
removeFile: (id) => {
|
|
969
|
-
revokeAgentChatComposerFiles(draftRef.current.files.filter((file) => file.id === id));
|
|
970
|
-
updateDraft(draftRef.current.text, draftRef.current.files.filter((file) => file.id !== id));
|
|
971
|
-
},
|
|
972
|
-
submit,
|
|
996
|
+
recordingAllowed: options.recordingEnabled ?? options.inputContract?.modalities.audio.recording_enabled ?? false,
|
|
997
|
+
fileAccept: attachmentFileAccept(attachmentMimeTypes),
|
|
973
998
|
onKeyDown: (event) => {
|
|
974
999
|
if (event.key === "Enter" && !event.shiftKey && !event.nativeEvent.isComposing) {
|
|
975
1000
|
event.preventDefault();
|
|
976
1001
|
void submit();
|
|
977
1002
|
}
|
|
978
|
-
}
|
|
1003
|
+
},
|
|
1004
|
+
allowAttachments: attachmentMimeTypes.length > 0,
|
|
1005
|
+
isExpanded: Boolean(options.forceExpanded || text.includes("\n") || text.length > 62),
|
|
1006
|
+
action: chatComposerAction({ hasDraft: Boolean(text.trim() || files.length), isRunning: Boolean(options.isRunning), isCancelling: Boolean(options.isCancelling) })
|
|
979
1007
|
};
|
|
980
1008
|
}
|
|
981
1009
|
|
|
@@ -1094,7 +1122,6 @@ function AgentChatComposer({
|
|
|
1094
1122
|
contextWindow,
|
|
1095
1123
|
accept,
|
|
1096
1124
|
allowAttachments = true,
|
|
1097
|
-
attachmentLayout = "flow",
|
|
1098
1125
|
className,
|
|
1099
1126
|
disabled = false,
|
|
1100
1127
|
forceExpanded = false,
|
|
@@ -1120,7 +1147,7 @@ function AgentChatComposer({
|
|
|
1120
1147
|
focusKey,
|
|
1121
1148
|
restoredDraft,
|
|
1122
1149
|
textareaRef: localTextareaRef,
|
|
1123
|
-
onSubmit: (
|
|
1150
|
+
onSubmit: (text2, files2) => onSubmit({ text: text2.trim(), files: files2, attachmentIds: files2.flatMap((file) => file.id ? [file.id] : []) })
|
|
1124
1151
|
});
|
|
1125
1152
|
const { text, files, setText, isSubmitting, canSubmit, action, submit } = composer;
|
|
1126
1153
|
const isExpanded = forceExpanded || text.includes("\n") || text.length > 62;
|
|
@@ -1156,26 +1183,8 @@ function AgentChatComposer({
|
|
|
1156
1183
|
event.currentTarget.value = "";
|
|
1157
1184
|
};
|
|
1158
1185
|
return /* @__PURE__ */ jsxs2("div", { className: cn("a24-composer relative mx-auto w-full max-w-3xl overflow-visible", className), "data-agents24-chat-composer": "", children: [
|
|
1159
|
-
files.length > 0 ? /* @__PURE__ */ jsx4(
|
|
1160
|
-
"div",
|
|
1161
|
-
{
|
|
1162
|
-
"data-testid": "agent-chat-composer-attachments",
|
|
1163
|
-
"data-layout": attachmentLayout,
|
|
1164
|
-
className: cn(
|
|
1165
|
-
"a24-composer__attachments min-w-0 py-1",
|
|
1166
|
-
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"
|
|
1167
|
-
),
|
|
1168
|
-
children: /* @__PURE__ */ jsx4(
|
|
1169
|
-
ChatAttachmentRows,
|
|
1170
|
-
{
|
|
1171
|
-
attachments: files,
|
|
1172
|
-
onRemove: (attachment) => attachment.id && composer.removeFile(attachment.id)
|
|
1173
|
-
}
|
|
1174
|
-
)
|
|
1175
|
-
}
|
|
1176
|
-
) : null,
|
|
1177
1186
|
composer.attachmentError || composer.recorder.error ? /* @__PURE__ */ jsx4("p", { role: "alert", children: composer.attachmentError || composer.recorder.error }) : null,
|
|
1178
|
-
/* @__PURE__ */
|
|
1187
|
+
/* @__PURE__ */ jsxs2(
|
|
1179
1188
|
"div",
|
|
1180
1189
|
{
|
|
1181
1190
|
"data-expanded": isExpanded ? "" : void 0,
|
|
@@ -1183,118 +1192,129 @@ function AgentChatComposer({
|
|
|
1183
1192
|
"a24-composer__surface relative w-full border border-input bg-background shadow-sm",
|
|
1184
1193
|
isExpanded ? "rounded-xl p-2 pb-1.5" : "rounded-xl px-2 py-[3px]"
|
|
1185
1194
|
),
|
|
1186
|
-
children:
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
children: /* @__PURE__ */ jsx4(Plus, { className: "size-4" })
|
|
1222
|
-
}
|
|
1223
|
-
)
|
|
1224
|
-
] }),
|
|
1225
|
-
/* @__PURE__ */ jsx4("div", { style: { gridArea: "textarea" }, className: "a24-composer__textarea-slot min-w-0 w-full", children: /* @__PURE__ */ jsx4(
|
|
1226
|
-
"textarea",
|
|
1227
|
-
{
|
|
1228
|
-
ref: setTextareaRef,
|
|
1229
|
-
"aria-label": "Message",
|
|
1230
|
-
autoComplete: "off",
|
|
1231
|
-
className: cn(
|
|
1232
|
-
"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",
|
|
1233
|
-
isExpanded ? "min-h-9 max-h-[224px] py-1" : "min-h-9 h-9 max-h-9 py-2"
|
|
1195
|
+
children: [
|
|
1196
|
+
files.length > 0 ? /* @__PURE__ */ jsx4(
|
|
1197
|
+
ChatAttachmentRows,
|
|
1198
|
+
{
|
|
1199
|
+
"data-testid": "agent-chat-composer-attachments",
|
|
1200
|
+
layout: "composer",
|
|
1201
|
+
attachments: files,
|
|
1202
|
+
onRemove: (attachment) => attachment.id && composer.removeFile(attachment.id)
|
|
1203
|
+
}
|
|
1204
|
+
) : null,
|
|
1205
|
+
/* @__PURE__ */ jsxs2(
|
|
1206
|
+
"div",
|
|
1207
|
+
{
|
|
1208
|
+
className: "a24-composer__grid w-full",
|
|
1209
|
+
style: {
|
|
1210
|
+
alignItems: "center",
|
|
1211
|
+
display: "grid",
|
|
1212
|
+
gridTemplateAreas: isExpanded ? `"textarea textarea textarea" "plus toolbar submit"` : `"plus textarea composer mic submit"`,
|
|
1213
|
+
gridTemplateColumns: isExpanded ? "auto 1fr auto" : "auto 1fr auto auto auto",
|
|
1214
|
+
gap: isExpanded ? "3px 10px" : "8px"
|
|
1215
|
+
},
|
|
1216
|
+
children: [
|
|
1217
|
+
/* @__PURE__ */ jsxs2("div", { style: { gridArea: "plus" }, className: "a24-composer__attach-slot flex shrink-0 items-center justify-center", children: [
|
|
1218
|
+
/* @__PURE__ */ jsx4(
|
|
1219
|
+
"input",
|
|
1220
|
+
{
|
|
1221
|
+
ref: fileInputRef,
|
|
1222
|
+
accept: composer.fileAccept ?? accept,
|
|
1223
|
+
"aria-label": "Attach files",
|
|
1224
|
+
className: "hidden",
|
|
1225
|
+
multiple: true,
|
|
1226
|
+
name: "attachments",
|
|
1227
|
+
onChange: handleFilesChange,
|
|
1228
|
+
type: "file"
|
|
1229
|
+
}
|
|
1234
1230
|
),
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1231
|
+
/* @__PURE__ */ jsx4(
|
|
1232
|
+
"button",
|
|
1233
|
+
{
|
|
1234
|
+
"aria-label": "Attach files",
|
|
1235
|
+
className: cn("a24-composer__attach", pillButtonClass),
|
|
1236
|
+
disabled: disabled || isSubmitting || !allowAttachments || !composer.allowAttachments,
|
|
1237
|
+
onClick: () => fileInputRef.current?.click(),
|
|
1238
|
+
title: "Attach files",
|
|
1239
|
+
type: "button",
|
|
1240
|
+
children: /* @__PURE__ */ jsx4(Plus, { className: "size-4" })
|
|
1241
|
+
}
|
|
1242
|
+
)
|
|
1243
|
+
] }),
|
|
1244
|
+
/* @__PURE__ */ jsx4("div", { style: { gridArea: "textarea" }, className: "a24-composer__textarea-slot min-w-0 w-full", children: /* @__PURE__ */ jsx4(
|
|
1245
|
+
"textarea",
|
|
1249
1246
|
{
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1247
|
+
ref: setTextareaRef,
|
|
1248
|
+
"aria-label": "Message",
|
|
1249
|
+
autoComplete: "off",
|
|
1250
|
+
className: cn(
|
|
1251
|
+
"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",
|
|
1252
|
+
isExpanded ? "min-h-9 max-h-[224px] py-1" : "min-h-9 h-9 max-h-9 py-2"
|
|
1253
|
+
),
|
|
1254
|
+
disabled,
|
|
1255
|
+
name: "message",
|
|
1256
|
+
onChange: (event) => setText(event.target.value),
|
|
1257
|
+
onKeyDown: composer.onKeyDown,
|
|
1258
|
+
placeholder,
|
|
1259
|
+
rows: isExpanded ? 2 : 1,
|
|
1260
|
+
value: text
|
|
1259
1261
|
}
|
|
1260
|
-
)
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1262
|
+
) }),
|
|
1263
|
+
isExpanded ? /* @__PURE__ */ jsxs2("div", { style: { gridArea: "toolbar" }, className: "a24-composer__toolbar flex min-w-0 items-center gap-1.5", children: [
|
|
1264
|
+
inputToolbarContent,
|
|
1265
|
+
/* @__PURE__ */ jsx4(AgentChatContextStatus, { contextWindow }),
|
|
1266
|
+
composer.recordingAllowed ? /* @__PURE__ */ jsx4(
|
|
1267
|
+
"button",
|
|
1268
|
+
{
|
|
1269
|
+
type: "button",
|
|
1270
|
+
className: pillButtonClass,
|
|
1271
|
+
"aria-label": composer.recorder.isRecording ? "Stop recording" : "Record audio",
|
|
1272
|
+
disabled: disabled || isRunning || isSubmitting,
|
|
1273
|
+
onClick: () => {
|
|
1274
|
+
if (composer.recorder.isRecording) composer.recorder.stop();
|
|
1275
|
+
else void composer.recorder.start();
|
|
1276
|
+
},
|
|
1277
|
+
children: composer.recorder.isRecording ? /* @__PURE__ */ jsx4(Square, {}) : /* @__PURE__ */ jsx4(Mic, {})
|
|
1278
|
+
}
|
|
1279
|
+
) : null
|
|
1280
|
+
] }) : null,
|
|
1281
|
+
!isExpanded ? /* @__PURE__ */ jsx4("div", { style: { gridArea: "composer" }, className: "flex min-w-0 items-center gap-1.5" }) : null,
|
|
1282
|
+
!isExpanded ? /* @__PURE__ */ jsxs2("div", { style: { gridArea: "mic" }, className: "flex shrink-0 items-center justify-end", children: [
|
|
1283
|
+
/* @__PURE__ */ jsx4(AgentChatContextStatus, { contextWindow }),
|
|
1284
|
+
composer.recordingAllowed ? /* @__PURE__ */ jsx4(
|
|
1285
|
+
"button",
|
|
1286
|
+
{
|
|
1287
|
+
type: "button",
|
|
1288
|
+
className: pillButtonClass,
|
|
1289
|
+
"aria-label": composer.recorder.isRecording ? "Stop recording" : "Record audio",
|
|
1290
|
+
disabled: disabled || isRunning || isSubmitting,
|
|
1291
|
+
onClick: () => {
|
|
1292
|
+
if (composer.recorder.isRecording) composer.recorder.stop();
|
|
1293
|
+
else void composer.recorder.start();
|
|
1294
|
+
},
|
|
1295
|
+
children: composer.recorder.isRecording ? /* @__PURE__ */ jsx4(Square, {}) : /* @__PURE__ */ jsx4(Mic, {})
|
|
1296
|
+
}
|
|
1297
|
+
) : null
|
|
1298
|
+
] }) : null,
|
|
1299
|
+
/* @__PURE__ */ jsx4("div", { style: { gridArea: "submit" }, className: "a24-composer__submit-slot flex shrink-0 items-center justify-end gap-1", children: /* @__PURE__ */ jsx4(
|
|
1266
1300
|
"button",
|
|
1267
1301
|
{
|
|
1268
|
-
|
|
1269
|
-
className:
|
|
1270
|
-
|
|
1271
|
-
disabled: disabled || isRunning || isSubmitting,
|
|
1302
|
+
"aria-label": isCancelling ? "Cancelling response" : action === "stop" ? "Stop generating" : files.length > 0 ? `Send ${formatFileCount(files)}` : isRunning ? "Queue message" : "Send message",
|
|
1303
|
+
className: cn("a24-composer__submit", submitButtonClass, text.trim().length > 0 && "hover:scale-105"),
|
|
1304
|
+
disabled: isCancelling || (action === "stop" ? disabled : !canSubmit),
|
|
1272
1305
|
onClick: () => {
|
|
1273
|
-
if (
|
|
1274
|
-
else void
|
|
1306
|
+
if (action === "stop") onStop?.();
|
|
1307
|
+
else void submit();
|
|
1275
1308
|
},
|
|
1276
|
-
|
|
1309
|
+
title: composer.validationError || (action === "stop" ? "Stop" : isRunning ? "Queue message" : "Send"),
|
|
1310
|
+
type: "button",
|
|
1311
|
+
children: isCancelling ? /* @__PURE__ */ jsx4(Loader2, { className: "size-3.5 animate-spin" }) : action === "stop" ? /* @__PURE__ */ jsx4(Square, { className: "size-3.5 fill-current" }) : /* @__PURE__ */ jsx4(ArrowUp, { className: "size-4", strokeWidth: 2.5 })
|
|
1277
1312
|
}
|
|
1278
|
-
)
|
|
1279
|
-
]
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
"aria-label": isCancelling ? "Cancelling response" : action === "stop" ? "Stop generating" : files.length > 0 ? `Send ${formatFileCount(files)}` : isRunning ? "Queue message" : "Send message",
|
|
1284
|
-
className: cn("a24-composer__submit", submitButtonClass, text.trim().length > 0 && "hover:scale-105"),
|
|
1285
|
-
disabled: isCancelling || (action === "stop" ? disabled : !canSubmit),
|
|
1286
|
-
onClick: () => {
|
|
1287
|
-
if (action === "stop") onStop?.();
|
|
1288
|
-
else void submit();
|
|
1289
|
-
},
|
|
1290
|
-
title: composer.validationError || (action === "stop" ? "Stop" : isRunning ? "Queue message" : "Send"),
|
|
1291
|
-
type: "button",
|
|
1292
|
-
children: isCancelling ? /* @__PURE__ */ jsx4(Loader2, { className: "size-3.5 animate-spin" }) : action === "stop" ? /* @__PURE__ */ jsx4(Square, { className: "size-3.5 fill-current" }) : /* @__PURE__ */ jsx4(ArrowUp, { className: "size-4", strokeWidth: 2.5 })
|
|
1293
|
-
}
|
|
1294
|
-
) })
|
|
1295
|
-
]
|
|
1296
|
-
}
|
|
1297
|
-
)
|
|
1313
|
+
) })
|
|
1314
|
+
]
|
|
1315
|
+
}
|
|
1316
|
+
)
|
|
1317
|
+
]
|
|
1298
1318
|
}
|
|
1299
1319
|
),
|
|
1300
1320
|
!isExpanded && inputToolbarContent ? /* @__PURE__ */ jsx4("div", { className: "a24-composer__toolbar a24-composer__toolbar--external mt-2 flex items-center gap-2 px-1", children: inputToolbarContent }) : null
|
|
@@ -1325,7 +1345,7 @@ function QueueRow({ message, onRemove, onEdit }) {
|
|
|
1325
1345
|
/* @__PURE__ */ jsx5(CornerDownLeft, { "aria-hidden": true, className: "size-4 shrink-0 text-muted-foreground" }),
|
|
1326
1346
|
/* @__PURE__ */ jsx5("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"}` }),
|
|
1327
1347
|
message.status === "blocked" ? /* @__PURE__ */ jsx5("span", { className: "shrink-0 text-xs text-amber-700", children: "Needs attention" }) : null,
|
|
1328
|
-
["steer_pending", "delivering"].includes(message.status) ? /* @__PURE__ */ jsx5("span", { className: "shrink-0 text-xs text-muted-foreground", children: "
|
|
1348
|
+
["steer_pending", "delivering"].includes(message.status) ? /* @__PURE__ */ jsx5("span", { className: "shrink-0 text-xs text-muted-foreground", children: "Sending\u2026" }) : null,
|
|
1329
1349
|
editable ? /* @__PURE__ */ jsxs3(Fragment, { children: [
|
|
1330
1350
|
/* @__PURE__ */ jsx5(
|
|
1331
1351
|
"button",
|
|
@@ -1439,6 +1459,21 @@ function AgentChatDefaultActions({
|
|
|
1439
1459
|
React8.useEffect(() => () => {
|
|
1440
1460
|
if (copyResetTimer.current !== null) window.clearTimeout(copyResetTimer.current);
|
|
1441
1461
|
}, []);
|
|
1462
|
+
const [feedbackPending, setFeedbackPending] = React8.useState(false);
|
|
1463
|
+
const feedbackInFlight = React8.useRef(false);
|
|
1464
|
+
const writeFeedback = async (input, close = false) => {
|
|
1465
|
+
if (feedbackInFlight.current) return;
|
|
1466
|
+
feedbackInFlight.current = true;
|
|
1467
|
+
setFeedbackPending(true);
|
|
1468
|
+
try {
|
|
1469
|
+
await handlers?.onSetFeedback?.(message, input);
|
|
1470
|
+
if (close) setFeedbackOpen(false);
|
|
1471
|
+
} catch {
|
|
1472
|
+
} finally {
|
|
1473
|
+
feedbackInFlight.current = false;
|
|
1474
|
+
setFeedbackPending(false);
|
|
1475
|
+
}
|
|
1476
|
+
};
|
|
1442
1477
|
const copied = handlers?.copied ?? optimisticCopied;
|
|
1443
1478
|
const copyMessage = () => {
|
|
1444
1479
|
handlers?.onCopy?.(message.content || "", message.id);
|
|
@@ -1482,7 +1517,8 @@ function AgentChatDefaultActions({
|
|
|
1482
1517
|
ChatMessageAction,
|
|
1483
1518
|
{
|
|
1484
1519
|
label: "Like",
|
|
1485
|
-
|
|
1520
|
+
disabled: feedbackPending,
|
|
1521
|
+
onClick: () => void writeFeedback({
|
|
1486
1522
|
rating: handlers.feedback?.rating === "like" ? null : "like"
|
|
1487
1523
|
}),
|
|
1488
1524
|
tooltip: "Like this response",
|
|
@@ -1493,10 +1529,11 @@ function AgentChatDefaultActions({
|
|
|
1493
1529
|
/* @__PURE__ */ jsx6(Popover.Trigger, { asChild: true, children: /* @__PURE__ */ jsx6(
|
|
1494
1530
|
ChatMessageAction,
|
|
1495
1531
|
{
|
|
1532
|
+
disabled: feedbackPending,
|
|
1496
1533
|
label: "Dislike",
|
|
1497
1534
|
onClick: () => {
|
|
1498
1535
|
if (handlers.feedback?.rating !== "dislike") {
|
|
1499
|
-
void
|
|
1536
|
+
void writeFeedback({ rating: "dislike" });
|
|
1500
1537
|
}
|
|
1501
1538
|
setFeedbackOpen(true);
|
|
1502
1539
|
},
|
|
@@ -1546,9 +1583,10 @@ function AgentChatDefaultActions({
|
|
|
1546
1583
|
/* @__PURE__ */ jsx6(
|
|
1547
1584
|
"button",
|
|
1548
1585
|
{
|
|
1586
|
+
disabled: feedbackPending,
|
|
1549
1587
|
className: "rounded-md px-3 py-2 text-sm font-medium hover:bg-muted",
|
|
1550
1588
|
onClick: () => {
|
|
1551
|
-
void
|
|
1589
|
+
void writeFeedback({ rating: null }, true);
|
|
1552
1590
|
},
|
|
1553
1591
|
type: "button",
|
|
1554
1592
|
children: "Remove feedback"
|
|
@@ -1557,9 +1595,10 @@ function AgentChatDefaultActions({
|
|
|
1557
1595
|
/* @__PURE__ */ jsx6(
|
|
1558
1596
|
"button",
|
|
1559
1597
|
{
|
|
1598
|
+
disabled: feedbackPending,
|
|
1560
1599
|
className: "rounded-md bg-primary px-3 py-2 text-sm font-medium text-primary-foreground hover:opacity-90",
|
|
1561
1600
|
onClick: () => {
|
|
1562
|
-
void
|
|
1601
|
+
void writeFeedback({ rating: "dislike", reason, comment }, true);
|
|
1563
1602
|
},
|
|
1564
1603
|
type: "button",
|
|
1565
1604
|
children: "Save"
|
|
@@ -2286,16 +2325,17 @@ function ToolActivityReel({
|
|
|
2286
2325
|
/* @__PURE__ */ jsx10("span", { className: previous ? "a24-tool-reel__current" : void 0, children: displayed })
|
|
2287
2326
|
] });
|
|
2288
2327
|
}
|
|
2289
|
-
function ToolActivityOrb({ label }) {
|
|
2290
|
-
return /* @__PURE__ */ jsx10("span", { className: "a24-tool-orb", children: /* @__PURE__ */ jsx10(ThinkingOrb, { "aria-label": label
|
|
2328
|
+
function ToolActivityOrb({ label = "Working", kind = "tool", part, renderActivityIcon }) {
|
|
2329
|
+
return /* @__PURE__ */ jsx10("span", { className: "a24-tool-orb", children: renderActivityIcon ? renderActivityIcon({ label, kind, part }) : /* @__PURE__ */ jsx10(ThinkingOrb, { "aria-label": label, size: 20, speed: 0.8, state: "searching" }) });
|
|
2291
2330
|
}
|
|
2292
2331
|
function AgentChatActivityRow({
|
|
2293
2332
|
label,
|
|
2294
2333
|
modelTurnId,
|
|
2334
|
+
renderActivityIcon,
|
|
2295
2335
|
onSelect
|
|
2296
2336
|
}) {
|
|
2297
2337
|
const content = /* @__PURE__ */ jsxs6(Fragment3, { children: [
|
|
2298
|
-
/* @__PURE__ */ jsx10(ToolActivityOrb, { label }),
|
|
2338
|
+
/* @__PURE__ */ jsx10(ToolActivityOrb, { label, kind: "planning", renderActivityIcon }),
|
|
2299
2339
|
/* @__PURE__ */ jsx10("span", { className: "a24-tool-trigger__label a24-tool-shimmer", children: /* @__PURE__ */ jsx10(ToolActivityReel, { label }) })
|
|
2300
2340
|
] });
|
|
2301
2341
|
return /* @__PURE__ */ jsx10("div", { className: "a24-tool-trigger-row a24-tool-trigger-row--root", "data-agents24-model-turn": modelTurnId, role: "status", children: onSelect ? /* @__PURE__ */ jsx10("button", { className: "a24-tool-trigger a24-tool-trigger--root a24-tool-trigger--active", onClick: onSelect, type: "button", children: content }) : /* @__PURE__ */ jsx10("div", { className: "a24-tool-trigger a24-tool-trigger--root a24-tool-trigger--active", children: content }) });
|
|
@@ -2304,6 +2344,7 @@ function AgentChatModelTurnRow({ part }) {
|
|
|
2304
2344
|
return /* @__PURE__ */ jsx10(AgentChatActivityRow, { label: "Planning next moves", modelTurnId: part.modelTurnId });
|
|
2305
2345
|
}
|
|
2306
2346
|
function AgentChatNodeActivityRow({
|
|
2347
|
+
renderActivityIcon,
|
|
2307
2348
|
onSelect,
|
|
2308
2349
|
part
|
|
2309
2350
|
}) {
|
|
@@ -2322,10 +2363,10 @@ function AgentChatNodeActivityRow({
|
|
|
2322
2363
|
const label = running ? /* @__PURE__ */ jsx10("span", { className: "a24-tool-shimmer", children: /* @__PURE__ */ jsx10(ToolActivityReel, { label: part.label }) }) : part.label;
|
|
2323
2364
|
if (!failed) {
|
|
2324
2365
|
return /* @__PURE__ */ jsx10("div", { className: "a24-tool-trigger-row", role: "status", children: onSelect ? /* @__PURE__ */ jsxs6("button", { className: triggerClassName, onClick: () => onSelect(part), type: "button", children: [
|
|
2325
|
-
running ? /* @__PURE__ */ jsx10(ToolActivityOrb, { label: part.label }) : null,
|
|
2366
|
+
running ? /* @__PURE__ */ jsx10(ToolActivityOrb, { label: part.label, kind: "node", part, renderActivityIcon }) : null,
|
|
2326
2367
|
/* @__PURE__ */ jsx10("span", { className: "a24-tool-trigger__label", children: label })
|
|
2327
2368
|
] }) : /* @__PURE__ */ jsxs6("div", { className: triggerClassName, children: [
|
|
2328
|
-
running ? /* @__PURE__ */ jsx10(ToolActivityOrb, { label: part.label }) : null,
|
|
2369
|
+
running ? /* @__PURE__ */ jsx10(ToolActivityOrb, { label: part.label, kind: "node", part, renderActivityIcon }) : null,
|
|
2329
2370
|
/* @__PURE__ */ jsx10("span", { className: "a24-tool-trigger__label", children: label })
|
|
2330
2371
|
] }) });
|
|
2331
2372
|
}
|
|
@@ -2371,6 +2412,7 @@ function ToolDisclosure({
|
|
|
2371
2412
|
root,
|
|
2372
2413
|
showOrb,
|
|
2373
2414
|
statusLabel,
|
|
2415
|
+
renderActivityIcon,
|
|
2374
2416
|
accessory
|
|
2375
2417
|
}) {
|
|
2376
2418
|
const [open, setOpen] = React11.useState(false);
|
|
@@ -2380,10 +2422,10 @@ function ToolDisclosure({
|
|
|
2380
2422
|
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");
|
|
2381
2423
|
return /* @__PURE__ */ jsxs6("div", { className: cn("a24-tool-trigger-row", root && "a24-tool-trigger-row--root"), children: [
|
|
2382
2424
|
onSelect && part ? /* @__PURE__ */ jsxs6("button", { className: triggerClassName, onClick: () => onSelect(part), type: "button", children: [
|
|
2383
|
-
showOrb ? /* @__PURE__ */ jsx10(ToolActivityOrb, { label: statusLabel }) : null,
|
|
2425
|
+
showOrb ? /* @__PURE__ */ jsx10(ToolActivityOrb, { label: statusLabel || "Working", kind: grouped ? "group" : "tool", part, renderActivityIcon }) : null,
|
|
2384
2426
|
/* @__PURE__ */ jsx10("span", { className: "a24-tool-trigger__label", children: label })
|
|
2385
2427
|
] }) : /* @__PURE__ */ jsxs6("div", { className: triggerClassName, children: [
|
|
2386
|
-
showOrb ? /* @__PURE__ */ jsx10(ToolActivityOrb, { label: statusLabel }) : null,
|
|
2428
|
+
showOrb ? /* @__PURE__ */ jsx10(ToolActivityOrb, { label: statusLabel || "Working", kind: grouped ? "group" : "tool", part, renderActivityIcon }) : null,
|
|
2387
2429
|
/* @__PURE__ */ jsx10("span", { className: "a24-tool-trigger__label", children: label })
|
|
2388
2430
|
] }),
|
|
2389
2431
|
accessory
|
|
@@ -2405,7 +2447,7 @@ function ToolDisclosure({
|
|
|
2405
2447
|
},
|
|
2406
2448
|
type: "button",
|
|
2407
2449
|
children: [
|
|
2408
|
-
showOrb ? /* @__PURE__ */ jsx10(ToolActivityOrb, { label: statusLabel }) : null,
|
|
2450
|
+
showOrb ? /* @__PURE__ */ jsx10(ToolActivityOrb, { label: statusLabel || "Working", kind: grouped ? "group" : "tool", part, renderActivityIcon }) : null,
|
|
2409
2451
|
/* @__PURE__ */ jsx10("span", { className: "a24-tool-trigger__label", children: label }),
|
|
2410
2452
|
open ? /* @__PURE__ */ jsx10(ChevronDown2, { "aria-hidden": "true", className: "a24-tool-trigger__chevron" }) : /* @__PURE__ */ jsx10(ChevronRight2, { "aria-hidden": "true", className: "a24-tool-trigger__chevron" })
|
|
2411
2453
|
]
|
|
@@ -2466,6 +2508,7 @@ function AgentChatToolRow({
|
|
|
2466
2508
|
return /* @__PURE__ */ jsx10("div", { "data-agents24-tool-part": part.type, "data-state": part.state, children: /* @__PURE__ */ jsxs6(
|
|
2467
2509
|
ToolDisclosure,
|
|
2468
2510
|
{
|
|
2511
|
+
renderActivityIcon: slots?.renderActivityIcon,
|
|
2469
2512
|
error: failed,
|
|
2470
2513
|
accessory: toolAccessory(context, slots),
|
|
2471
2514
|
label: /* @__PURE__ */ jsx10(ToolLabel, { active, label: context.label, part, slots }),
|
|
@@ -2512,6 +2555,7 @@ function AgentChatToolSession({
|
|
|
2512
2555
|
return /* @__PURE__ */ jsx10(
|
|
2513
2556
|
ToolDisclosure,
|
|
2514
2557
|
{
|
|
2558
|
+
renderActivityIcon: slots?.renderActivityIcon,
|
|
2515
2559
|
error: failed,
|
|
2516
2560
|
grouped: true,
|
|
2517
2561
|
label: labelContent,
|
|
@@ -2556,6 +2600,7 @@ function AgentChatSubagentGroup({ isLatestActivity = true, isLive = false, part,
|
|
|
2556
2600
|
return /* @__PURE__ */ jsx10(
|
|
2557
2601
|
ToolDisclosure,
|
|
2558
2602
|
{
|
|
2603
|
+
renderActivityIcon: slots?.renderActivityIcon,
|
|
2559
2604
|
accessory: slots?.renderToolAccessory?.(context),
|
|
2560
2605
|
error: failed,
|
|
2561
2606
|
grouped: true,
|
|
@@ -2815,14 +2860,14 @@ function AgentResponseTimeline({
|
|
|
2815
2860
|
if (part.kind === "model-turn") {
|
|
2816
2861
|
const key = activeActivityClaimed ? part.id : `${message.id}-active-activity`;
|
|
2817
2862
|
activeActivityClaimed = true;
|
|
2818
|
-
rendered.push(/* @__PURE__ */ jsx11(AgentChatActivityRow, { label: "Planning next moves", modelTurnId: part.modelTurnId }, key));
|
|
2863
|
+
rendered.push(/* @__PURE__ */ jsx11(AgentChatActivityRow, { renderActivityIcon: toolSlots?.renderActivityIcon, label: "Planning next moves", modelTurnId: part.modelTurnId }, key));
|
|
2819
2864
|
continue;
|
|
2820
2865
|
}
|
|
2821
2866
|
if (part.kind === "node-activity") {
|
|
2822
2867
|
const key = activeActivityClaimed ? part.id : `${message.id}-active-activity`;
|
|
2823
2868
|
if (part.status === "running") activeActivityClaimed = true;
|
|
2824
2869
|
rendered.push(
|
|
2825
|
-
part.status === "running" ? /* @__PURE__ */ jsx11(AgentChatActivityRow, { label: part.label, onSelect: onNodeSelect ? () => onNodeSelect(part) : void 0 }, key) : /* @__PURE__ */ jsx11(AgentChatNodeActivityRow, { onSelect: onNodeSelect, part }, part.id)
|
|
2870
|
+
part.status === "running" ? /* @__PURE__ */ jsx11(AgentChatActivityRow, { renderActivityIcon: toolSlots?.renderActivityIcon, label: part.label, onSelect: onNodeSelect ? () => onNodeSelect(part) : void 0 }, key) : /* @__PURE__ */ jsx11(AgentChatNodeActivityRow, { renderActivityIcon: toolSlots?.renderActivityIcon, onSelect: onNodeSelect, part }, part.id)
|
|
2826
2871
|
);
|
|
2827
2872
|
continue;
|
|
2828
2873
|
}
|
|
@@ -2851,7 +2896,7 @@ function AgentResponseTimeline({
|
|
|
2851
2896
|
rendered.push(/* @__PURE__ */ jsx11(MessageResponse, { children: message.content }, `${message.id}-fallback`));
|
|
2852
2897
|
}
|
|
2853
2898
|
if (rendered.length === 0 && isLoading) {
|
|
2854
|
-
rendered.push(/* @__PURE__ */ jsx11(AgentChatActivityRow, { label: "Thinking" }, `${message.id}-active-activity`));
|
|
2899
|
+
rendered.push(/* @__PURE__ */ jsx11(AgentChatActivityRow, { renderActivityIcon: toolSlots?.renderActivityIcon, label: "Thinking" }, `${message.id}-active-activity`));
|
|
2855
2900
|
}
|
|
2856
2901
|
return /* @__PURE__ */ jsx11("div", { className: "space-y-3", children: rendered });
|
|
2857
2902
|
}
|