|
|
|
@ -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<MediaEditTool>(MEDIA_EDIT_TOOL.crop); |
|
|
|
const [cropAspectRatio, setCropAspectRatio] = useState<MediaCropAspectRatio>("original"); |
|
|
|
const [cropPayload, setCropPayload] = useState<MediaCropConfirmPayload | null>(null); |
|
|
|
const [isCropUploading, setIsCropUploading] = useState(false); |
|
|
|
const [isSplitGridRunning, setIsSplitGridRunning] = useState(false); |
|
|
|
const [gridSelection, setGridSelection] = useState<SplitGridSelection | null>(null); |
|
|
|
const [selectedGridCellKeys, setSelectedGridCellKeys] = useState<Set<string>>(() => 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 handleConfirmCrop = useCallback((payload: MediaCropConfirmPayload) => { |
|
|
|
const currentNode = getNodeById(id); |
|
|
|
const currentPosition = currentNode?.position ?? { x: 0, y: 0 }; |
|
|
|
const currentWidth = typeof currentNode?.width === "number" |
|
|
|
if (!currentNode) return; |
|
|
|
|
|
|
|
const currentPosition = currentNode.position ?? { x: 0, y: 0 }; |
|
|
|
const currentWidth = typeof currentNode.width === "number" |
|
|
|
? currentNode.width |
|
|
|
: typeof currentNode?.style?.width === "number" |
|
|
|
: typeof currentNode.style?.width === "number" |
|
|
|
? currentNode.style.width |
|
|
|
: 300; |
|
|
|
createDerivedImageNode({ |
|
|
|
const filename = `crop-${Date.now()}.png`; |
|
|
|
const nodeId = createPendingDerivedImageNode({ |
|
|
|
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", |
|
|
|
task: { |
|
|
|
cropRect: payload.cropRect, |
|
|
|
outputWidth: payload.outputWidth, |
|
|
|
outputHeight: payload.outputHeight, |
|
|
|
aspectRatio: payload.aspectRatio, |
|
|
|
}, |
|
|
|
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]); |
|
|
|
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 |
|
|
|
<MediaEditToolbar |
|
|
|
activeTool={activeMediaTool} |
|
|
|
tools={IMAGE_MEDIA_EDIT_TOOLS} |
|
|
|
busy={isCropUploading} |
|
|
|
onCancel={() => 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} |
|
|
|
/> |
|
|
|
)} |
|
|
|
|