diff --git a/components/canvas/__tests__/compare-node.test.tsx b/components/canvas/__tests__/compare-node.test.tsx
new file mode 100644
index 0000000..93b8df6
--- /dev/null
+++ b/components/canvas/__tests__/compare-node.test.tsx
@@ -0,0 +1,160 @@
+import React from "react";
+import { beforeEach, describe, expect, it, vi } from "vitest";
+import { renderToStaticMarkup } from "react-dom/server";
+
+type StoreState = {
+ nodes: Array<{ id: string; type?: string; data?: unknown }>;
+ edges: Array<{
+ id: string;
+ source: string;
+ target: string;
+ className?: string;
+ targetHandle?: string;
+ }>;
+};
+
+const storeState: StoreState = {
+ nodes: [],
+ edges: [],
+};
+
+const compareSurfaceSpy = vi.fn();
+
+vi.mock("@xyflow/react", () => ({
+ Handle: () => null,
+ Position: { Left: "left", Right: "right" },
+ useStore: (selector: (state: StoreState) => unknown) => selector(storeState),
+}));
+
+vi.mock("../nodes/base-node-wrapper", () => ({
+ default: ({ children }: { children: React.ReactNode }) =>
{children}
,
+}));
+
+vi.mock("../nodes/compare-surface", () => ({
+ default: (props: unknown) => {
+ compareSurfaceSpy(props);
+ return null;
+ },
+}));
+
+import CompareNode from "../nodes/compare-node";
+
+describe("CompareNode render preview inputs", () => {
+ beforeEach(() => {
+ storeState.nodes = [];
+ storeState.edges = [];
+ compareSurfaceSpy.mockReset();
+ });
+
+ it("passes previewInput to CompareSurface for a connected render node without final output", () => {
+ storeState.nodes = [
+ {
+ id: "image-1",
+ type: "image",
+ data: { url: "https://cdn.example.com/source.png" },
+ },
+ {
+ id: "render-1",
+ type: "render",
+ data: {},
+ },
+ ];
+ storeState.edges = [
+ { id: "edge-image-render", source: "image-1", target: "render-1" },
+ {
+ id: "edge-render-compare",
+ source: "render-1",
+ target: "compare-1",
+ targetHandle: "left",
+ },
+ ];
+
+ renderToStaticMarkup(
+ React.createElement(CompareNode, {
+ id: "compare-1",
+ data: { leftUrl: "https://cdn.example.com/render-output.png" },
+ selected: false,
+ dragging: false,
+ zIndex: 0,
+ isConnectable: true,
+ type: "compare",
+ xPos: 0,
+ yPos: 0,
+ width: 500,
+ height: 380,
+ sourcePosition: undefined,
+ targetPosition: undefined,
+ positionAbsoluteX: 0,
+ positionAbsoluteY: 0,
+ } as never),
+ );
+
+ expect(compareSurfaceSpy).toHaveBeenCalled();
+ const previewCall = compareSurfaceSpy.mock.calls.find(
+ ([props]) =>
+ Boolean(
+ (props as { previewInput?: { sourceUrl: string; steps: unknown[] } }).previewInput,
+ ),
+ );
+ expect(previewCall).toBeDefined();
+ expect(
+ (previewCall?.[0] as { previewInput?: { sourceUrl: string; steps: unknown[] } })
+ .previewInput,
+ ).toEqual({
+ sourceUrl: "https://cdn.example.com/source.png",
+ steps: [],
+ });
+ });
+
+ it("defaults render-backed compare inputs to preview mode even when a final render output exists", () => {
+ storeState.nodes = [
+ {
+ id: "image-1",
+ type: "image",
+ data: { url: "https://cdn.example.com/source.png" },
+ },
+ {
+ id: "render-1",
+ type: "render",
+ data: {
+ lastUploadUrl: "https://cdn.example.com/render-output.png",
+ },
+ },
+ ];
+ storeState.edges = [
+ { id: "edge-image-render", source: "image-1", target: "render-1" },
+ {
+ id: "edge-render-compare",
+ source: "render-1",
+ target: "compare-1",
+ targetHandle: "left",
+ },
+ ];
+
+ renderToStaticMarkup(
+ React.createElement(CompareNode, {
+ id: "compare-1",
+ data: { leftUrl: "https://cdn.example.com/render-output.png" },
+ selected: false,
+ dragging: false,
+ zIndex: 0,
+ isConnectable: true,
+ type: "compare",
+ xPos: 0,
+ yPos: 0,
+ width: 500,
+ height: 380,
+ sourcePosition: undefined,
+ targetPosition: undefined,
+ positionAbsoluteX: 0,
+ positionAbsoluteY: 0,
+ } as never),
+ );
+
+ expect(compareSurfaceSpy).toHaveBeenCalledTimes(1);
+ expect(compareSurfaceSpy.mock.calls[0]?.[0]).toMatchObject({
+ finalUrl: "https://cdn.example.com/render-output.png",
+ preferPreview: true,
+ });
+ });
+});
diff --git a/components/canvas/nodes/compare-node.tsx b/components/canvas/nodes/compare-node.tsx
index c533477..c69c918 100644
--- a/components/canvas/nodes/compare-node.tsx
+++ b/components/canvas/nodes/compare-node.tsx
@@ -148,7 +148,9 @@ export default function CompareNode({ id, data, selected, width }: NodeProps) {
[incomingEdges, nodesById],
);
const shouldDefaultToPreview =
- resolvedSides.left.isStaleRenderOutput || resolvedSides.right.isStaleRenderOutput;
+ hasConnectedRenderInput ||
+ resolvedSides.left.isStaleRenderOutput ||
+ resolvedSides.right.isStaleRenderOutput;
const effectiveDisplayMode =
manualDisplayMode ?? (shouldDefaultToPreview ? "preview" : "render");
const previewNodeWidth = Math.max(240, Math.min(640, Math.round(width ?? 500)));
diff --git a/vitest.config.ts b/vitest.config.ts
index 654be67..141a59a 100644
--- a/vitest.config.ts
+++ b/vitest.config.ts
@@ -13,6 +13,7 @@ export default defineConfig({
"tests/**/*.test.ts",
"components/canvas/__tests__/canvas-helpers.test.ts",
"components/canvas/__tests__/canvas-flow-reconciliation-helpers.test.ts",
+ "components/canvas/__tests__/compare-node.test.tsx",
"components/canvas/__tests__/use-canvas-flow-reconciliation.test.ts",
"components/canvas/__tests__/use-canvas-drop.test.tsx",
"components/canvas/__tests__/use-canvas-node-interactions.test.tsx",