"use client"; import { useParams, useRouter, useSearchParams } from "next/navigation"; import { useCallback } from "react"; import { toast } from "sonner"; import { RealtimeCanvasDialog } from "@/components/canvas/RealtimeCanvasDialog"; import { canvasService } from "@/services/canvas"; import { canvasSelectors, useCanvasStore } from "@/store/canvas"; export default function CanvasRealtimePage() { const router = useRouter(); const routeParams = useParams(); const searchParams = useSearchParams(); const id = String(routeParams?.id || ""); const initialMode = searchParams?.get("mode") === "enhance-pro" ? "enhance-pro" : "realtime"; const viewport = useCanvasStore(canvasSelectors.viewport); const selectedNodeIds = useCanvasStore(canvasSelectors.selectedNodeIds); const nodes = useCanvasStore(canvasSelectors.nodes); const addNode = useCanvasStore((state) => state.addNode); const getCanvasState = useCanvasStore((state) => state.getCanvasState); const selectedImageNode = selectedNodeIds.length === 1 ? nodes.find( (node) => node.id === selectedNodeIds[0] && node.type === "image" ) : null; const handleOpenChange = useCallback( (nextOpen: boolean) => { if (!nextOpen) { router.push(`/canvas/${id}`); } }, [id, router] ); const handleApplyResult = useCallback( async ({ imageUrl, sourceImageUrl, prompt, title, model, width, height, }: { imageUrl: string; sourceImageUrl?: string; prompt: string; title?: string; model?: string; width?: number; height?: number; }) => { const viewportX = viewport.x || 0; const viewportY = viewport.y || 0; const viewportZoom = viewport.zoom || 1; const stageWidth = typeof window !== "undefined" ? window.innerWidth : 1280; const stageHeight = typeof window !== "undefined" ? window.innerHeight : 720; const safeWidth = Number(width); const safeHeight = Number(height); const hasNaturalSize = safeWidth > 0 && safeHeight > 0; const maxPreviewEdge = 420; const fallbackSize = 360; const scale = hasNaturalSize ? Math.min(1, maxPreviewEdge / Math.max(safeWidth, safeHeight)) : 1; const targetWidth = hasNaturalSize ? Math.max(140, Math.round(safeWidth * scale)) : fallbackSize; const targetHeight = hasNaturalSize ? Math.max(140, Math.round(safeHeight * scale)) : fallbackSize; const compareSourceUrl = String(sourceImageUrl || "").trim(); const x = (stageWidth * 0.45 - viewportX) / viewportZoom - targetWidth / 2; const y = (stageHeight * 0.45 - viewportY) / viewportZoom - targetHeight / 2; addNode({ type: "image", position: { x, y }, size: { width: targetWidth, height: targetHeight }, data: { src: imageUrl, originalSrc: compareSourceUrl || undefined, status: "completed", title: title || "实时画布结果", prompt, model: model || "bfl/flux-2", metadata: compareSourceUrl ? { compareSourceUrl, } : undefined, }, connections: [], }); try { await canvasService.saveCanvasState(id, getCanvasState()); } catch (error) { console.error("[RealtimeCanvasPage] 保存结果失败:", error); } toast.success(`${title || "结果"}已添加到画布`); }, [addNode, getCanvasState, id, viewport.x, viewport.y, viewport.zoom] ); return ( ); }