"use client"; import { useEffect, useMemo, useRef } from "react"; import { useCanvasGraph } from "@/components/canvas/canvas-graph-context"; import { usePipelinePreview } from "@/hooks/use-pipeline-preview"; import { collectPipelineFromGraph, getSourceImageFromGraph, } from "@/lib/canvas-render-preview"; import type { PipelineStep } from "@/lib/image-pipeline/contracts"; import { buildHistogramPlot } from "@/lib/image-pipeline/histogram-plot"; const PREVIEW_PIPELINE_TYPES = new Set([ "curves", "color-adjust", "light-adjust", "detail-adjust", ]); type PreviewLatencyTrace = { sequence: number; changedAtMs: number; nodeType: string; origin: string; }; function readPreviewLatencyTrace(): PreviewLatencyTrace | null { if (process.env.NODE_ENV === "production") { return null; } const debugGlobals = globalThis as typeof globalThis & { __LEMONSPACE_DEBUG_PREVIEW_LATENCY__?: boolean; __LEMONSPACE_LAST_PREVIEW_TRACE__?: PreviewLatencyTrace; }; if (debugGlobals.__LEMONSPACE_DEBUG_PREVIEW_LATENCY__ !== true) { return null; } return debugGlobals.__LEMONSPACE_LAST_PREVIEW_TRACE__ ?? null; } export default function AdjustmentPreview({ nodeId, nodeWidth, currentType, currentParams, }: { nodeId: string; nodeWidth: number; currentType: string; currentParams: unknown; }) { const graph = useCanvasGraph(); const lastLoggedTraceSequenceRef = useRef(null); const sourceUrl = useMemo( () => getSourceImageFromGraph(graph, { nodeId, isSourceNode: (node) => node.type === "image" || node.type === "ai-image" || node.type === "asset", getSourceImageFromNode: (node) => { const sourceData = (node.data ?? {}) as Record; const directUrl = typeof sourceData.url === "string" ? sourceData.url : null; if (directUrl && directUrl.length > 0) { return directUrl; } const previewUrl = typeof sourceData.previewUrl === "string" ? sourceData.previewUrl : null; return previewUrl && previewUrl.length > 0 ? previewUrl : null; }, }), [graph, nodeId], ); const steps = useMemo(() => { const collected = collectPipelineFromGraph(graph, { nodeId, isPipelineNode: (node) => PREVIEW_PIPELINE_TYPES.has(node.type ?? ""), }); return collected.map((step) => { if (step.nodeId === nodeId && step.type === currentType) { return { ...step, params: currentParams, } as PipelineStep; } return step as PipelineStep; }); }, [currentParams, currentType, graph, nodeId]); useEffect(() => { const trace = readPreviewLatencyTrace(); if (!trace) { return; } if (lastLoggedTraceSequenceRef.current === trace.sequence) { return; } lastLoggedTraceSequenceRef.current = trace.sequence; console.info("[Preview latency] downstream-graph-visible", { nodeId, nodeType: currentType, sourceNodeType: trace.nodeType, sourceOrigin: trace.origin, sinceChangeMs: performance.now() - trace.changedAtMs, pipelineDepth: steps.length, stepTypes: steps.map((step) => step.type), hasSource: Boolean(sourceUrl), }); }, [currentType, nodeId, sourceUrl, steps]); const { canvasRef, histogram, isRendering, hasSource, previewAspectRatio, error } = usePipelinePreview({ sourceUrl, steps, nodeWidth, includeHistogram: true, // Die Vorschau muss in-Node gut lesbar bleiben, aber nicht in voller // Display-Auflösung rechnen. previewScale: 0.5, maxPreviewWidth: 720, maxDevicePixelRatio: 1.25, }); const histogramPlot = useMemo(() => { return buildHistogramPlot(histogram, { points: 64, width: 96, height: 44, }); }, [histogram]); return (
{!hasSource ? (
Verbinde eine Bild-, Asset- oder KI-Bild-Node fuer Live-Preview.
) : null} {hasSource ? ( ) : null} {isRendering ? (
Rendering...
) : null}
{error ?

{error}

: null}
); }