"use client"; import { useMemo } from "react"; import { useStore, type Node } from "@xyflow/react"; import { usePipelinePreview } from "@/hooks/use-pipeline-preview"; import { collectPipeline, getSourceImage, type PipelineStep } from "@/lib/image-pipeline/contracts"; const PREVIEW_PIPELINE_TYPES = new Set([ "curves", "color-adjust", "light-adjust", "detail-adjust", ]); function resolveNodeImageUrl(node: Node): string | null { const data = (node.data ?? {}) as Record; const directUrl = typeof data.url === "string" ? data.url : null; if (directUrl && directUrl.length > 0) { return directUrl; } const previewUrl = typeof data.previewUrl === "string" ? data.previewUrl : null; if (previewUrl && previewUrl.length > 0) { return previewUrl; } return null; } function compactHistogram(values: readonly number[], points = 64): number[] { if (points <= 0) { return []; } if (values.length === 0) { return Array.from({ length: points }, () => 0); } const bucket = values.length / points; const compacted: number[] = []; for (let pointIndex = 0; pointIndex < points; pointIndex += 1) { let sum = 0; const start = Math.floor(pointIndex * bucket); const end = Math.min(values.length, Math.floor((pointIndex + 1) * bucket) || start + 1); for (let index = start; index < end; index += 1) { sum += values[index] ?? 0; } compacted.push(sum); } return compacted; } function histogramPolyline(values: readonly number[], maxValue: number, width: number, height: number): string { if (values.length === 0) { return ""; } const divisor = Math.max(1, values.length - 1); return values .map((value, index) => { const x = (index / divisor) * width; const normalized = maxValue > 0 ? value / maxValue : 0; const y = height - normalized * height; return `${x.toFixed(2)},${y.toFixed(2)}`; }) .join(" "); } export default function AdjustmentPreview({ nodeId, nodeWidth, currentType, currentParams, }: { nodeId: string; nodeWidth: number; currentType: string; currentParams: unknown; }) { const nodes = useStore((state) => state.nodes); const edges = useStore((state) => state.edges); const pipelineNodes = useMemo( () => nodes.map((node) => ({ id: node.id, type: node.type ?? "", data: node.data })), [nodes], ); const pipelineEdges = useMemo( () => edges.map((edge) => ({ source: edge.source, target: edge.target })), [edges], ); const sourceUrl = useMemo( () => getSourceImage({ nodeId, nodes: pipelineNodes, edges: pipelineEdges, isSourceNode: (node) => node.type === "image" || node.type === "ai-image" || node.type === "asset", getSourceImageFromNode: (node) => { const sourceNode = nodes.find((candidate) => candidate.id === node.id); return sourceNode ? resolveNodeImageUrl(sourceNode) : null; }, }), [nodeId, nodes, pipelineEdges, pipelineNodes], ); const steps = useMemo(() => { const collected = collectPipeline({ nodeId, nodes: pipelineNodes, edges: pipelineEdges, 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, nodeId, pipelineEdges, pipelineNodes]); const { canvasRef, histogram, isRendering, hasSource, previewAspectRatio, error } = usePipelinePreview({ sourceUrl, steps, nodeWidth, }); const histogramSeries = useMemo(() => { const red = compactHistogram(histogram.red, 64); const green = compactHistogram(histogram.green, 64); const blue = compactHistogram(histogram.blue, 64); const rgb = compactHistogram(histogram.rgb, 64); const max = Math.max(1, ...red, ...green, ...blue, ...rgb); return { red, green, blue, rgb, max }; }, [histogram.blue, histogram.green, histogram.red, histogram.rgb]); const histogramPolylines = useMemo(() => { const width = 96; const height = 44; return { red: histogramPolyline(histogramSeries.red, histogramSeries.max, width, height), green: histogramPolyline(histogramSeries.green, histogramSeries.max, width, height), blue: histogramPolyline(histogramSeries.blue, histogramSeries.max, width, height), rgb: histogramPolyline(histogramSeries.rgb, histogramSeries.max, width, height), }; }, [histogramSeries.blue, histogramSeries.green, histogramSeries.max, histogramSeries.red, histogramSeries.rgb]); return (
{!hasSource ? (
Verbinde eine Bild-, Asset- oder KI-Bild-Node fuer Live-Preview.
) : null} {hasSource ? ( ) : null} {isRendering ? (
Rendering...
) : null}
{error ?

{error}

: null}
); }