diff --git a/src/components/WorkflowCanvas.tsx b/src/components/WorkflowCanvas.tsx index 74b4ef4a..2b5db88e 100644 --- a/src/components/WorkflowCanvas.tsx +++ b/src/components/WorkflowCanvas.tsx @@ -2811,6 +2811,7 @@ export function WorkflowCanvas() { nodeTypes={nodeTypes} edgeTypes={edgeTypes} isValidConnection={isValidConnection} + onlyRenderVisibleElements fitView fitViewOptions={FIT_VIEW_OPTIONS} deleteKeyCode={["Backspace", "Delete"]} diff --git a/src/components/nodes/ImageInputNode.tsx b/src/components/nodes/ImageInputNode.tsx index d281a92f..d2793a63 100644 --- a/src/components/nodes/ImageInputNode.tsx +++ b/src/components/nodes/ImageInputNode.tsx @@ -176,7 +176,7 @@ export function ImageInputNodeView({ id, data, selected, renderInputHandle = fal image: uploaded.url, imageRef: undefined, assetId: undefined, - previewImage: undefined, + previewImage: uploaded.previewUrl, assetDetailLoading: false, assetDetailError: null, uploadStatus: "idle", diff --git a/src/components/nodes/SmartImageNode.tsx b/src/components/nodes/SmartImageNode.tsx index be028b5a..a158d92d 100644 --- a/src/components/nodes/SmartImageNode.tsx +++ b/src/components/nodes/SmartImageNode.tsx @@ -106,14 +106,14 @@ function SmartImageUploadToolbar({ image: uploaded.url, imageRef: undefined, assetId: undefined, - previewImage: undefined, + previewImage: uploaded.previewUrl, assetDetailLoading: false, assetDetailError: null, uploadStatus: "idle", uploadError: null, filename: uploaded.filename, dimensions, - ...buildUploadedImagePatch(uploaded.url, dimensions), + ...buildUploadedImagePatch(uploaded.url, dimensions, uploaded.previewUrl), }); }) .catch((error) => { diff --git a/src/components/nodes/VideoInputNode.tsx b/src/components/nodes/VideoInputNode.tsx index 8a144d6b..e81b75e3 100644 --- a/src/components/nodes/VideoInputNode.tsx +++ b/src/components/nodes/VideoInputNode.tsx @@ -160,7 +160,7 @@ export function VideoInputNodeView({ id, data, selected }: VideoInputNodeViewPro video: uploaded.url, videoRef: undefined, assetId: undefined, - previewVideoPoster: undefined, + previewVideoPoster: uploaded.previewUrl, assetDetailLoading: false, assetDetailError: null, uploadStatus: "idle", diff --git a/src/utils/gatewayMediaUpload.ts b/src/utils/gatewayMediaUpload.ts index 6463f034..e154b051 100644 --- a/src/utils/gatewayMediaUpload.ts +++ b/src/utils/gatewayMediaUpload.ts @@ -4,6 +4,7 @@ export type GatewayMediaKind = "image" | "video" | "audio"; export type GatewayMediaUploadResult = { url: string; + previewUrl?: string; filename: string; contentType: string; }; @@ -12,8 +13,38 @@ type GatewayMediaUploadResponse = { success?: boolean; url?: string; error?: string; + media?: GatewayMedia | GatewayMedia[] | GatewayMediaCollection; }; +type GatewayMedia = { + url?: string; + thumbUrl?: string; + coverThumb?: string; + cover?: string; +}; + +type GatewayMediaCollection = { + list?: GatewayMedia[]; + files?: GatewayMedia[]; +}; + +function isGatewayMedia(value: GatewayMedia | GatewayMediaCollection): value is GatewayMedia { + return "url" in value || "thumbUrl" in value || "coverThumb" in value || "cover" in value; +} + +function firstMedia(media: GatewayMediaUploadResponse["media"]): GatewayMedia | undefined { + if (!media) return undefined; + if (Array.isArray(media)) return media.find(Boolean); + if ("list" in media && Array.isArray(media.list)) return media.list.find(Boolean); + if ("files" in media && Array.isArray(media.files)) return media.files.find(Boolean); + return isGatewayMedia(media) ? media : undefined; +} + +function getPreviewUrl(result: GatewayMediaUploadResponse): string | undefined { + const media = firstMedia(result.media); + return media?.thumbUrl || media?.coverThumb || media?.cover || result.url || undefined; +} + export async function uploadFileToGatewayMedia( file: File, kind: GatewayMediaKind @@ -42,6 +73,7 @@ export async function uploadBlobToGatewayMedia( return { url: result.url, + previewUrl: getPreviewUrl(result), filename: filename || `canvas-${kind}`, contentType: contentType || blob.type || `${kind}/*`, };