"use client"; import { useCallback, useRef } from "react"; import { Handle, Position, NodeProps, Node } from "@xyflow/react"; import { BaseNode } from "./BaseNode"; import { useCommentNavigation } from "@/hooks/useCommentNavigation"; import { useWorkflowStore } from "@/store/workflowStore"; import { ImageInputNodeData } from "@/types"; import { defaultNodeDimensions } from "@/store/utils/nodeDefaults"; import { useAdaptiveImageSrc } from "@/hooks/useAdaptiveImageSrc"; import { downloadMedia } from "@/utils/downloadMedia"; import { useShowHandleLabels } from "@/hooks/useShowHandleLabels"; import { HandleLabel } from "./HandleLabel"; import { readImageFile } from "@/utils/imageFile"; import type { LoadedImageFile } from "@/utils/imageFile"; type ImageInputNodeType = Node; export function ImageInputNode({ id, data, selected }: NodeProps) { const nodeData = data; const adaptiveImage = useAdaptiveImageSrc(nodeData.image, id); const commentNavigation = useCommentNavigation(id); const updateNodeData = useWorkflowStore((state) => state.updateNodeData); const addNode = useWorkflowStore((state) => state.addNode); const onConnect = useWorkflowStore((state) => state.onConnect); const nodes = useWorkflowStore((state) => state.nodes); const fileInputRef = useRef(null); const imageToImageInputRef = useRef(null); const showLabels = useShowHandleLabels(selected); const writeLoadedImage = useCallback( (loaded: LoadedImageFile) => { updateNodeData(id, { image: loaded.image, imageRef: undefined, filename: loaded.filename, dimensions: loaded.dimensions, }); }, [id, updateNodeData] ); const applyImageFile = useCallback( async (file: File) => { try { const loaded = await readImageFile(file); writeLoadedImage(loaded); } catch (error) { alert(error instanceof Error ? error.message : "Failed to load image file."); } }, [writeLoadedImage] ); const handleImageToImageFile = useCallback( async (file: File) => { try { const loaded = await readImageFile(file); writeLoadedImage(loaded); const currentNode = nodes.find((node) => node.id === id); const currentWidth = currentNode?.measured?.width ?? (typeof currentNode?.style?.width === "number" ? currentNode.style.width : defaultNodeDimensions.imageInput.width); const currentPosition = currentNode?.position ?? { x: 0, y: 0 }; const nextNodeId = addNode("nanoBanana", { x: currentPosition.x + currentWidth + 180, y: currentPosition.y, }); onConnect({ source: id, sourceHandle: "image", target: nextNodeId, targetHandle: "image", }); } catch (error) { alert(error instanceof Error ? error.message : "Failed to load image file."); } }, [addNode, id, nodes, onConnect, writeLoadedImage] ); const handleFileChange = useCallback( (e: React.ChangeEvent) => { const input = e.currentTarget; const file = input.files?.[0]; if (!file) return; void applyImageFile(file); input.value = ""; }, [applyImageFile] ); const handleImageToImageChange = useCallback( (e: React.ChangeEvent) => { const input = e.currentTarget; const file = input.files?.[0]; if (!file) return; void handleImageToImageFile(file); input.value = ""; }, [handleImageToImageFile] ); const handleDrop = useCallback( (e: React.DragEvent) => { e.preventDefault(); e.stopPropagation(); const file = e.dataTransfer.files?.[0]; if (!file) return; void applyImageFile(file); }, [applyImageFile] ); const handleDragOver = useCallback((e: React.DragEvent) => { e.preventDefault(); e.stopPropagation(); }, []); const handleRemove = useCallback(() => { updateNodeData(id, { image: null, imageRef: undefined, filename: null, dimensions: null, }); }, [id, updateNodeData]); return ( {nodeData.image ? (
{nodeData.filename {nodeData.isOptional && ( Optional )}
) : (
{nodeData.isOptional ? ( Optional ) : (
尝试:
)}
)} {/* Handles rendered after visual content so they paint on top */}
); }