Files
agent-radar 2ed3415fb7
Publish Claude Code Binaries / Stage and publish binaries (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-arm64 (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build linux-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build windows-amd64 (push) Canceled after 0s
Sync Tutti Agent Skills / sync (push) Canceled after 0s
Windows Agent Adapters / Test Windows agent adapters (push) Canceled after 0s
Windows Daemon Adapters / Test Windows daemon adapters (push) Canceled after 0s
Publish Tutti App Runtime / Publish runtime catalog (push) Canceled after 0s
源码快照 @ 2026-09-01 (source-only, 剔除非源码素材)
2026-09-01 20:17:27 +08:00

100 lines
2.6 KiB
TypeScript

import { useEffect, useState, type JSX } from "react";
import { resolveWorkspaceImageMimeType } from "@tutti-os/workspace-file-preview";
import { useOptionalAgentHostApi } from "../../../agentActivityHost";
import { ZoomableImage } from "../../../app/renderer/components/ZoomableImage";
import {
isLocalImagePath,
resolveImageGenerationPreviewSrc
} from "../../imageGenerationTool";
interface AgentGeneratedImagePreviewProps {
uri: string;
mimeType: string | null;
alt: string;
className: string;
}
export function AgentGeneratedImagePreview({
uri,
mimeType,
alt,
className
}: AgentGeneratedImagePreviewProps): JSX.Element | null {
"use memo";
const agentHostApi = useOptionalAgentHostApi();
const localPath = isLocalImagePath(uri) ? uri.trim() : null;
const readWorkspaceImage = localPath
? agentHostApi?.workspace?.readFile
: undefined;
const [src, setSrc] = useState<string | null>(() =>
!localPath ? resolveImageGenerationPreviewSrc(uri) : null
);
useEffect(() => {
if (!localPath || !readWorkspaceImage) {
setSrc(resolveImageGenerationPreviewSrc(uri));
return;
}
const resolvedLocalPath = localPath;
const resolvedReadWorkspaceImage = readWorkspaceImage;
let canceled = false;
let objectUrl: string | null = null;
const resolvedMimeType =
mimeType?.trim() ||
resolveWorkspaceImageMimeType(resolvedLocalPath) ||
"image/png";
async function loadWorkspaceImage(): Promise<void> {
try {
const result = await resolvedReadWorkspaceImage({
path: resolvedLocalPath
});
if (canceled) {
return;
}
const bytes =
result.bytes instanceof Uint8Array
? result.bytes
: new Uint8Array(result.bytes);
const arrayBuffer = bytes.buffer.slice(
bytes.byteOffset,
bytes.byteOffset + bytes.byteLength
) as ArrayBuffer;
objectUrl = URL.createObjectURL(
new Blob([arrayBuffer], { type: resolvedMimeType })
);
setSrc(objectUrl);
} catch {
if (!canceled) {
setSrc(null);
}
}
}
setSrc(null);
void loadWorkspaceImage();
return () => {
canceled = true;
if (objectUrl) {
URL.revokeObjectURL(objectUrl);
}
};
}, [localPath, mimeType, readWorkspaceImage, uri]);
if (!src) {
return null;
}
return (
<ZoomableImage
alt={alt}
className={className}
downloadName={localPath ? localPath.split(/[\\/]/).pop() : "image.png"}
src={src}
wrapElement="span"
/>
);
}