extend media display

This commit is contained in:
2026-07-10 17:26:44 +08:00
parent 0911c825d4
commit a3f7332dfe
6 changed files with 254 additions and 23 deletions
+33
View File
@@ -0,0 +1,33 @@
import type { AudioHTMLAttributes } from 'react';
import { useResolvedAssetReadUrl } from '../hooks/useResolvedAssetReadUrl';
type ResolvedAssetAudioProps = Omit<
AudioHTMLAttributes<HTMLAudioElement>,
'src'
> & {
src?: string | null;
objectKey?: string | null;
fallbackSrc?: string | null;
refreshKey?: string | number | null;
};
export function ResolvedAssetAudio({
src,
objectKey,
fallbackSrc,
refreshKey,
...rest
}: ResolvedAssetAudioProps) {
const { resolvedUrl } = useResolvedAssetReadUrl(src, {
objectKey,
refreshKey,
});
const finalSrc = resolvedUrl || fallbackSrc?.trim() || '';
if (!finalSrc) {
return null;
}
return <audio {...rest} src={finalSrc} />;
}
@@ -1,4 +1,4 @@
import { Check, Image as ImageIcon, Loader2, X } from 'lucide-react';
import { Check, Image as ImageIcon, Loader2, Volume2, X } from 'lucide-react';
import type {
EditorAgentAttachmentRef,
@@ -8,7 +8,9 @@ import type {
import { attachmentKey } from '@/src/components/image-editor/EditorAgentConversation/common.ts';
import { PendingToolCall } from '@/src/components/image-editor/EditorAgentConversation/PendingToolCall.tsx';
import { editorAgentToolLabel } from '@/src/components/image-editor/EditorAgentConversation/toolCallPresentation.ts';
import { ResolvedAssetAudio } from '@/src/components/ResolvedAssetAudio.tsx';
import { ResolvedAssetImage } from '@/src/components/ResolvedAssetImage.tsx';
import { ResolvedAssetVideo } from '@/src/components/ResolvedAssetVideo.tsx';
function messageRoleLabel(role: EditorAgentMessage['role']) {
if (role === 'user') {
@@ -55,6 +57,8 @@ export function AttachmentChip({
}
function ToolCallView({ toolCall }: { toolCall: EditorAgentToolCall }) {
const videos = toolCall.videos ?? [];
const audios = toolCall.audios ?? [];
const isExecuting = toolCall.status === 'executing';
const statusLabel =
toolCall.status === 'completed'
@@ -102,6 +106,54 @@ function ToolCallView({ toolCall }: { toolCall: EditorAgentToolCall }) {
))}
</div>
) : null}
{videos.length ? (
<div className="mt-2 grid grid-cols-1 gap-2">
{videos.map((video, index) => (
<div
key={`${toolCall.toolName}-${video.resourceId ?? video.objectKey ?? index}`}
className="overflow-hidden rounded-xl border border-slate-200 bg-slate-100"
>
<ResolvedAssetVideo
src={video.videoSrc}
objectKey={video.objectKey}
fallbackSrc={video.thumbnailSrc}
refreshKey={
video.resourceId ?? video.objectKey ?? toolCall.toolName
}
controls
playsInline
preload="metadata"
className="max-h-56 w-full bg-black object-contain"
/>
</div>
))}
</div>
) : null}
{audios.length ? (
<div className="mt-2 space-y-2">
{audios.map((audio, index) => (
<div
key={`${toolCall.toolName}-${audio.resourceId ?? audio.objectKey ?? index}`}
className="flex items-center gap-2 rounded-xl border border-slate-200 bg-slate-50 p-2"
>
<Volume2
className="h-4 w-4 shrink-0 text-slate-500"
aria-hidden="true"
/>
<ResolvedAssetAudio
src={audio.audioSrc}
objectKey={audio.objectKey}
refreshKey={
audio.resourceId ?? audio.objectKey ?? toolCall.toolName
}
controls
preload="metadata"
className="min-w-0 flex-1"
/>
</div>
))}
</div>
) : null}
</div>
);
}
@@ -298,7 +298,9 @@ export function useEditorAgentConversation({
nextMessages.some(
(message) =>
message.toolCall?.status === 'completed' &&
message.toolCall.images.length > 0,
(message.toolCall.images.length > 0 ||
(message.toolCall.videos?.length ?? 0) > 0 ||
(message.toolCall.audios?.length ?? 0) > 0),
)
) {
onCanvasRefreshRequested?.();