diff --git a/web/src/app/(user)/canvas/[id]/canvas-client-page.tsx b/web/src/app/(user)/canvas/[id]/canvas-client-page.tsx index 593c094..0e8dd24 100644 --- a/web/src/app/(user)/canvas/[id]/canvas-client-page.tsx +++ b/web/src/app/(user)/canvas/[id]/canvas-client-page.tsx @@ -251,6 +251,7 @@ function InfiniteCanvasPage() { const [infoNodeId, setInfoNodeId] = useState(null); const [cropNodeId, setCropNodeId] = useState(null); const [angleNodeId, setAngleNodeId] = useState(null); + const [previewNodeId, setPreviewNodeId] = useState(null); const [assistantCollapsed, setAssistantCollapsed] = useState(true); const [assistantMounted, setAssistantMounted] = useState(false); const [titleEditing, setTitleEditing] = useState(false); @@ -525,6 +526,7 @@ function InfiniteCanvasPage() { const infoNode = infoNodeId ? nodeById.get(infoNodeId) || null : null; const cropNode = cropNodeId ? nodeById.get(cropNodeId) || null : null; const angleNode = angleNodeId ? nodeById.get(angleNodeId) || null : null; + const previewNode = previewNodeId ? nodeById.get(previewNodeId) || null : null; const hasMultipleSelectedNodes = selectedNodeIds.size > 1; const activeNodeId = hasMultipleSelectedNodes ? null : hoveredNodeId || (selectedNodeIds.size === 1 ? Array.from(selectedNodeIds)[0] : null); const batchChildCountById = useMemo(() => { @@ -631,6 +633,7 @@ function InfiniteCanvasPage() { setInfoNodeId((current) => (current && allIds.has(current) ? null : current)); setCropNodeId((current) => (current && allIds.has(current) ? null : current)); setAngleNodeId((current) => (current && allIds.has(current) ? null : current)); + setPreviewNodeId((current) => (current && allIds.has(current) ? null : current)); setRunningNodeId((current) => (current && allIds.has(current) ? null : current)); setContextMenu((current) => (current && allIds.has(current.nodeId) ? null : current)); cleanupAssetImages({ projectId, nodes: nodesRef.current.filter((node) => !allIds.has(node.id)), chatSessions }); @@ -656,6 +659,7 @@ function InfiniteCanvasPage() { setInfoNodeId(null); setCropNodeId(null); setAngleNodeId(null); + setPreviewNodeId(null); setRunningNodeId(null); deselectCanvas(); setClearConfirmOpen(false); @@ -2084,6 +2088,7 @@ function InfiniteCanvasPage() { onSaveAsset={(node) => void saveNodeAsset(node)} onCrop={(node) => setCropNodeId(node.id)} onAngle={(node) => setAngleNodeId(node.id)} + onViewImage={(node) => setPreviewNodeId(node.id)} onRetry={(node) => void handleRetryNode(node)} onToggleFreeResize={(node) => toggleNodeFreeResize(node.id)} onDelete={(node) => deleteNodes(new Set([node.id]))} @@ -2141,6 +2146,24 @@ function InfiniteCanvasPage() { {angleNode?.metadata?.content ? setAngleNodeId(null)} onConfirm={(params) => void generateAngleNode(angleNode!, params)} /> : null} + setPreviewNodeId(null)} + footer={null} + width="auto" + styles={{ body: { padding: 0, display: "flex", justifyContent: "center", alignItems: "center", maxHeight: "80vh" } }} + > + {previewNode?.metadata?.content ? ( + {previewNode.title + ) : null} + + void; onCrop: (node: CanvasNodeData) => void; onAngle: (node: CanvasNodeData) => void; + onViewImage: (node: CanvasNodeData) => void; onRetry: (node: CanvasNodeData) => void; onToggleFreeResize: (node: CanvasNodeData) => void; onDelete: (node: CanvasNodeData) => void; @@ -46,6 +47,7 @@ export function CanvasNodeHoverToolbar({ onSaveAsset, onCrop, onAngle, + onViewImage, onRetry, onToggleFreeResize, onDelete, @@ -95,6 +97,7 @@ export function CanvasNodeHoverToolbar({ ) : null} {hasImage ? } onClick={() => onCrop(node)} /> : null} {hasImage ? } onClick={() => onAngle(node)} /> : null} + {hasImage ? } onClick={() => onViewImage(node)} /> : null} ); }