feat(canvas): add mixer drag-resize and mixer->render bake

This commit is contained in:
2026-04-11 10:03:41 +02:00
parent ae2fa1d269
commit f499aea691
28 changed files with 1731 additions and 152 deletions

View File

@@ -463,11 +463,13 @@ export default function RenderNode({ id, data, selected, width, height }: NodePr
);
const sourceUrl = renderPreviewInput.sourceUrl;
const sourceComposition = renderPreviewInput.sourceComposition;
useEffect(() => {
logRenderDebug("node-data-updated", {
nodeId: id,
hasSourceUrl: typeof sourceUrl === "string" && sourceUrl.length > 0,
hasSourceComposition: Boolean(sourceComposition),
storageId: data.storageId ?? null,
lastUploadStorageId: data.lastUploadStorageId ?? null,
hasResolvedUrl: typeof data.url === "string" && data.url.length > 0,
@@ -484,6 +486,7 @@ export default function RenderNode({ id, data, selected, width, height }: NodePr
data.url,
id,
sourceUrl,
sourceComposition,
]);
const sourceNode = useMemo<SourceNodeDescriptor | null>(
@@ -525,9 +528,12 @@ export default function RenderNode({ id, data, selected, width, height }: NodePr
);
const currentPipelineHash = useMemo(() => {
if (!sourceUrl) return null;
return hashPipeline({ sourceUrl, render: renderFingerprint }, steps);
}, [renderFingerprint, sourceUrl, steps]);
if (!sourceUrl && !sourceComposition) return null;
return hashPipeline(
{ source: sourceComposition ?? sourceUrl, render: renderFingerprint },
steps,
);
}, [renderFingerprint, sourceComposition, sourceUrl, steps]);
const isRenderCurrent =
Boolean(currentPipelineHash) && localData.lastRenderedHash === currentPipelineHash;
@@ -557,7 +563,8 @@ export default function RenderNode({ id, data, selected, width, height }: NodePr
error: "Error",
};
const hasSource = typeof sourceUrl === "string" && sourceUrl.length > 0;
const hasSource =
(typeof sourceUrl === "string" && sourceUrl.length > 0) || Boolean(sourceComposition);
const previewNodeWidth = Math.max(260, Math.round(width ?? 320));
const {
@@ -568,6 +575,7 @@ export default function RenderNode({ id, data, selected, width, height }: NodePr
error: previewError,
} = usePipelinePreview({
sourceUrl,
sourceComposition,
steps,
nodeWidth: previewNodeWidth,
debounceMs: previewDebounceMs,
@@ -585,6 +593,7 @@ export default function RenderNode({ id, data, selected, width, height }: NodePr
error: fullscreenPreviewError,
} = usePipelinePreview({
sourceUrl: isFullscreenOpen && sourceUrl ? sourceUrl : null,
sourceComposition: isFullscreenOpen ? sourceComposition : undefined,
steps,
nodeWidth: fullscreenPreviewWidth,
includeHistogram: false,
@@ -719,11 +728,12 @@ export default function RenderNode({ id, data, selected, width, height }: NodePr
};
const handleRender = async (mode: "download" | "upload") => {
if (!sourceUrl || !currentPipelineHash) {
if ((!sourceUrl && !sourceComposition) || !currentPipelineHash) {
logRenderDebug("render-aborted-prerequisites", {
nodeId: id,
mode,
hasSourceUrl: Boolean(sourceUrl),
hasSourceComposition: Boolean(sourceComposition),
hasPipelineHash: Boolean(currentPipelineHash),
isOffline: status.isOffline,
});
@@ -768,7 +778,8 @@ export default function RenderNode({ id, data, selected, width, height }: NodePr
});
const renderResult = await renderFullWithWorkerFallback({
sourceUrl,
sourceUrl: sourceUrl ?? undefined,
sourceComposition,
steps,
render: {
resolution: activeData.outputResolution,