diff --git a/src/components/nodes/GenerateImageNode.tsx b/src/components/nodes/GenerateImageNode.tsx index 7e69da86..26080b3a 100644 --- a/src/components/nodes/GenerateImageNode.tsx +++ b/src/components/nodes/GenerateImageNode.tsx @@ -31,11 +31,9 @@ import { NodeActionCapsule, type NodeActionCapsuleAction } from "./NodeActionCap import { SaveToAssetLibraryModal } from "@/components/SaveToAssetLibraryModal"; import { MediaCropOverlay, type MediaCropAspectRatio, type MediaCropConfirmPayload } from "@/components/media/MediaCropOverlay"; import { CropRatioSelect, MEDIA_EDIT_TOOL, MediaEditToolbar, type MediaEditTool, type MediaEditToolOption } from "@/components/media/MediaEditToolbar"; -import { createCroppedImageBlob } from "@/components/media/imageCropUtils"; -import { uploadBlobToGatewayMedia } from "@/utils/gatewayMediaUpload"; import { SplitGridCapsuleMenu, type SplitGridSelection } from "@/components/media/SplitGridCapsuleMenu"; import { createSplitGridImageNodes } from "@/utils/splitGridNodes"; -import { createDerivedImageNode } from "@/utils/derivedImageNodes"; +import { createPendingDerivedImageNode } from "@/utils/derivedImageNodes"; import { SplitGridSelectionOverlay } from "@/components/media/SplitGridSelectionOverlay"; import { SplitGridSelectionToolbar } from "@/components/media/SplitGridSelectionToolbar"; import { getAutoMediaElementClassName, getAutoMediaFrameClassName } from "./mediaAutoSize"; @@ -91,7 +89,6 @@ export function GenerateImageNodeView({ id, data, selected }: GenerateImageNodeV const [activeMediaTool, setActiveMediaTool] = useState(MEDIA_EDIT_TOOL.crop); const [cropAspectRatio, setCropAspectRatio] = useState("original"); const [cropPayload, setCropPayload] = useState(null); - const [isCropUploading, setIsCropUploading] = useState(false); const [isSplitGridRunning, setIsSplitGridRunning] = useState(false); const [gridSelection, setGridSelection] = useState(null); const [selectedGridCellKeys, setSelectedGridCellKeys] = useState>(() => new Set()); @@ -205,57 +202,40 @@ export function GenerateImageNodeView({ id, data, selected }: GenerateImageNodeV }, nextItem.dimensions ?? null); }, [id, nodeData.imageHistory, nodeData.selectedHistoryIndex, updateMediaNodeData, updateNodeData]); - const handleConfirmCrop = useCallback(async (payload: MediaCropConfirmPayload) => { - const sourceImage = nodeData.outputImage || displayImage; - if (!sourceImage) return; - - let blob: Blob | null = null; - try { - blob = await createCroppedImageBlob(sourceImage, payload); - } catch { - alert(t("imageInput.cropFailed")); - return; - } - - if (!blob) { - alert(t("imageInput.cropFailed")); - return; - } - - setIsCropUploading(true); - try { - const filename = `crop-${Date.now()}.png`; - const uploaded = await uploadBlobToGatewayMedia(blob, filename, "image/png", "image"); - const currentNode = getNodeById(id); - const currentPosition = currentNode?.position ?? { x: 0, y: 0 }; - const currentWidth = typeof currentNode?.width === "number" - ? currentNode.width - : typeof currentNode?.style?.width === "number" - ? currentNode.style.width - : 300; - createDerivedImageNode({ - sourceNodeId: id, - position: { - x: currentPosition.x + currentWidth + 220, - y: currentPosition.y, - }, - image: uploaded.url, - previewImage: uploaded.previewUrl, - filename, - dimensions: { width: payload.outputWidth, height: payload.outputHeight }, - customTitle: t("imageInput.crop"), - operation: "crop", - addNode, - onConnect, - selectSingleNode, - }); - setIsCropping(false); - } catch (error) { - alert(error instanceof Error ? error.message : t("imageInput.cropFailed")); - } finally { - setIsCropUploading(false); - } - }, [addNode, displayImage, getNodeById, id, nodeData.outputImage, onConnect, selectSingleNode, t]); + const handleConfirmCrop = useCallback((payload: MediaCropConfirmPayload) => { + const currentNode = getNodeById(id); + if (!currentNode) return; + + const currentPosition = currentNode.position ?? { x: 0, y: 0 }; + const currentWidth = typeof currentNode.width === "number" + ? currentNode.width + : typeof currentNode.style?.width === "number" + ? currentNode.style.width + : 300; + const filename = `crop-${Date.now()}.png`; + const nodeId = createPendingDerivedImageNode({ + sourceNodeId: id, + position: { + x: currentPosition.x + currentWidth + 220, + y: currentPosition.y, + }, + filename, + customTitle: t("imageInput.crop"), + operation: "crop", + task: { + cropRect: payload.cropRect, + outputWidth: payload.outputWidth, + outputHeight: payload.outputHeight, + aspectRatio: payload.aspectRatio, + }, + addNode, + onConnect, + selectSingleNode, + }); + setIsCropping(false); + setCropPayload(null); + void regenerateNode(nodeId); + }, [addNode, getNodeById, id, onConnect, regenerateNode, selectSingleNode, t]); const handleStartGridSelection = useCallback((selection: SplitGridSelection) => { setIsCropping(false); @@ -564,7 +544,6 @@ export function GenerateImageNodeView({ id, data, selected }: GenerateImageNodeV setIsCropping(false)} context={{ value: cropAspectRatio, @@ -629,7 +608,6 @@ export function GenerateImageNodeView({ id, data, selected }: GenerateImageNodeV naturalWidth={overlayImageDimensions?.width} naturalHeight={overlayImageDimensions?.height} aspectRatio={cropAspectRatio} - busy={isCropUploading} onCropChange={setCropPayload} /> )}