"use client"; import { useParams, useRouter } from "next/navigation"; import { useCallback, useEffect } from "react"; import { toast } from "sonner"; import { StyleProWorkspace } from "@/components/style-pro/StyleProWorkspace"; import { canvasService } from "@/services/canvas"; import { canvasSelectors, useCanvasStore } from "@/store/canvas"; import type { StyleProHistoryItem, StyleProPersistedState, } from "@/lib/style-pro/config"; import { normalizeStyleProPersistedState } from "@/lib/style-pro/config"; export default function CanvasStyleProPage() { const router = useRouter(); const routeParams = useParams(); const id = String(routeParams?.id || ""); const viewport = useCanvasStore(canvasSelectors.viewport); const selectedNodeIds = useCanvasStore(canvasSelectors.selectedNodeIds); const nodes = useCanvasStore(canvasSelectors.nodes); const styleProState = useCanvasStore((state) => state.styleProState); const addNode = useCanvasStore((state) => state.addNode); const loadCanvas = useCanvasStore((state) => state.loadCanvas); const selectNodes = useCanvasStore((state) => state.selectNodes); const setProject = useCanvasStore((state) => state.setProject); const setStyleProState = useCanvasStore((state) => state.setStyleProState); const getCanvasState = useCanvasStore((state) => state.getCanvasState); useEffect(() => { let cancelled = false; const loadPersistedStyleProState = async () => { if (!id) return; const currentState = useCanvasStore.getState(); if ( currentState.projectId === id && (currentState.nodes.length > 0 || currentState.styleProState.history.length > 0) ) { return; } const result = await canvasService.getProject(id); if (cancelled || !result.success || !result.data?.canvasData) return; const canvasData = result.data.canvasData; const latestState = useCanvasStore.getState(); setProject(result.data.id, result.data.title); if (latestState.nodes.length === 0 && latestState.edges.length === 0) { loadCanvas(canvasData); return; } const nextStyleProState = normalizeStyleProPersistedState( canvasData.styleProState ); if (nextStyleProState.history.length > 0) { setStyleProState(nextStyleProState); } }; void loadPersistedStyleProState(); return () => { cancelled = true; }; }, [id, loadCanvas, setProject, setStyleProState]); const selectedImageNode = selectedNodeIds.length === 1 ? nodes.find( (node) => node.id === selectedNodeIds[0] && node.type === "image" ) : null; const selectedImageUrl = selectedImageNode ? String((selectedImageNode.data as any)?.src || "").trim() : ""; const handleBack = useCallback(() => { router.push(`/canvas/${id}`); }, [id, router]); const handlePersistState = useCallback( async (nextState: StyleProPersistedState) => { useCanvasStore.getState().setStyleProState(nextState); const result = await canvasService.saveCanvasState( id, useCanvasStore.getState().getCanvasState() ); if (!result.success) { console.error( "[CanvasStyleProPage] 保存风格化 PRO 状态失败:", result.error ); } }, [id] ); const handleAddToCanvas = useCallback( async (item: StyleProHistoryItem) => { if (!item.resultImageUrl) { toast.error("没有可添加到画布的结果图"); return; } 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 naturalWidth = Number(item.width || 0); const naturalHeight = Number(item.height || 0); const hasNaturalSize = naturalWidth > 0 && naturalHeight > 0; const maxPreviewEdge = 460; const fallbackSize = 360; const scale = hasNaturalSize ? Math.min(1, maxPreviewEdge / Math.max(naturalWidth, naturalHeight)) : 1; const targetWidth = hasNaturalSize ? Math.max(160, Math.round(naturalWidth * scale)) : fallbackSize; const targetHeight = hasNaturalSize ? Math.max(160, Math.round(naturalHeight * scale)) : fallbackSize; const x = (stageWidth * 0.46 - viewportX) / viewportZoom - targetWidth / 2; const y = (stageHeight * 0.46 - viewportY) / viewportZoom - targetHeight / 2; const nodeId = addNode({ type: "image", position: { x, y }, size: { width: targetWidth, height: targetHeight }, data: { src: item.resultImageUrl, originalSrc: item.sourceImageUrl, status: "completed", title: "风格化 PRO 结果", prompt: item.optionLabels.join(" / ") || "风格化 PRO", model: item.model || "Style Pro", metadata: { compareSourceUrl: item.sourceImageUrl, styleProHistoryId: item.id, styleProSelections: item.selections, }, }, connections: [], }); selectNodes([nodeId]); const result = await canvasService.saveCanvasState(id, getCanvasState()); if (!result.success) { toast.error(result.error || "保存画布失败"); return; } toast.success("风格化 PRO 结果已添加到画布"); }, [ addNode, getCanvasState, id, selectNodes, viewport.x, viewport.y, viewport.zoom, ] ); return ( ); }