From 0fd65682cece073f8c83d6e02f5f12ef5fa4ee78 Mon Sep 17 00:00:00 2001 From: shrimbly Date: Sun, 22 Mar 2026 20:40:40 +1300 Subject: [PATCH] fix: prevent node height accumulation with inline parameters on reload Store measured settings panel height in node data (_settingsPanelHeight). On workflow load, subtract it from persisted height so BaseNode's expand effect re-adds the real panel height from a clean baseline instead of double-counting. Co-Authored-By: Claude Opus 4.6 --- src/components/nodes/BaseNode.tsx | 10 ++++-- src/store/workflowStore.ts | 53 ++++++++++++++++++++++++++----- src/types/nodes.ts | 5 +++ 3 files changed, 58 insertions(+), 10 deletions(-) diff --git a/src/components/nodes/BaseNode.tsx b/src/components/nodes/BaseNode.tsx index 4f296622..ab1ed2bb 100644 --- a/src/components/nodes/BaseNode.tsx +++ b/src/components/nodes/BaseNode.tsx @@ -141,7 +141,10 @@ export function BaseNode({ nodes.map((node) => { if (node.id !== id) return node; const currentHeight = getNodeDimension(node, "height"); - return applyNodeDimensions(node, getNodeDimension(node, "width"), currentHeight + finalHeight); + return { + ...applyNodeDimensions(node, getNodeDimension(node, "width"), currentHeight + finalHeight), + data: { ...node.data, _settingsPanelHeight: finalHeight }, + }; }) ); } @@ -188,7 +191,10 @@ export function BaseNode({ if (node.id !== id) return node; const currentHeight = getNodeDimension(node, "height"); const newHeight = Math.max(minHeight, currentHeight + delta); - return applyNodeDimensions(node, getNodeDimension(node, "width"), newHeight); + return { + ...applyNodeDimensions(node, getNodeDimension(node, "width"), newHeight), + data: { ...node.data, _settingsPanelHeight: newPanelHeight }, + }; }) ); diff --git a/src/store/workflowStore.ts b/src/store/workflowStore.ts index 3391afde..5fa80861 100644 --- a/src/store/workflowStore.ts +++ b/src/store/workflowStore.ts @@ -1712,17 +1712,54 @@ const workflowStoreImpl: StateCreator = (set, get) => ({ // Load cost data for this workflow const costData = workflow.id ? loadWorkflowCostData(workflow.id) : null; + // Subtract saved settings panel height so BaseNode's expand effect + // re-adds the real panel height from a clean baseline (prevents + // height accumulation on each save/reload cycle). + const INLINE_PARAM_NODE_TYPES = new Set([ + "nanoBanana", "generateVideo", "generate3d", "generateAudio", "llmGenerate", + ]); + const inlineParamsEnabled = (() => { + try { return localStorage.getItem("node-banana-inline-parameters") === "true"; } + catch { return false; } + })(); + set({ // Clear selected state - selection should not be persisted across sessions // Also validate position to ensure coordinates are finite numbers - nodes: hydratedWorkflow.nodes.map(node => ({ - ...node, - selected: false, - position: { - x: isFinite(node.position?.x) ? node.position.x : 0, - y: isFinite(node.position?.y) ? node.position.y : 0, - }, - })), + nodes: hydratedWorkflow.nodes.map(node => { + let adjustedNode = { + ...node, + selected: false, + position: { + x: isFinite(node.position?.x) ? node.position.x : 0, + y: isFinite(node.position?.y) ? node.position.y : 0, + }, + }; + + // Subtract panel height for inline-param nodes so the expand effect + // can re-add the real measured height without double-counting + const data = node.data as Record; + const panelHeight = typeof data?._settingsPanelHeight === "number" ? data._settingsPanelHeight : 0; + if ( + INLINE_PARAM_NODE_TYPES.has(node.type ?? "") && + inlineParamsEnabled && + (data?.parametersExpanded ?? true) === true && + panelHeight > 0 + ) { + const currentHeight = (node.style?.height as number) ?? (defaultNodeDimensions[node.type as NodeType]?.height ?? 300); + const minHeight = defaultNodeDimensions[node.type as NodeType]?.height ?? 300; + const newHeight = Math.max(minHeight, currentHeight - panelHeight); + adjustedNode = { + ...adjustedNode, + width: undefined, + height: undefined, + measured: undefined, + style: { ...adjustedNode.style, height: newHeight }, + } as typeof adjustedNode; + } + + return adjustedNode; + }), edges: hydratedWorkflow.edges, edgeStyle: hydratedWorkflow.edgeStyle || "angular", groups: hydratedWorkflow.groups || {}, diff --git a/src/types/nodes.ts b/src/types/nodes.ts index 963c41f7..5a7681c9 100644 --- a/src/types/nodes.ts +++ b/src/types/nodes.ts @@ -178,6 +178,7 @@ export interface NanoBananaNodeData extends BaseNodeData { parameters?: Record; // Model-specific parameters for external providers inputSchema?: ModelInputDef[]; // Model's input schema for dynamic handles parametersExpanded?: boolean; // Collapse state for inline parameter display + _settingsPanelHeight?: number; // Measured settings panel height for reload correction status: NodeStatus; error: string | null; imageHistory: CarouselImageItem[]; // Carousel history (IDs only) @@ -197,6 +198,7 @@ export interface GenerateVideoNodeData extends BaseNodeData { parameters?: Record; // Model-specific parameters inputSchema?: ModelInputDef[]; // Model's input schema for dynamic handles parametersExpanded?: boolean; // Collapse state for inline parameter display + _settingsPanelHeight?: number; // Measured settings panel height for reload correction status: NodeStatus; error: string | null; videoHistory: CarouselVideoItem[]; // Carousel history (IDs only) @@ -217,6 +219,7 @@ export interface Generate3DNodeData extends BaseNodeData { parameters?: Record; inputSchema?: ModelInputDef[]; parametersExpanded?: boolean; // Collapse state for inline parameter display + _settingsPanelHeight?: number; // Measured settings panel height for reload correction status: NodeStatus; error: string | null; } @@ -242,6 +245,7 @@ export interface GenerateAudioNodeData extends BaseNodeData { parameters?: Record; // Model-specific parameters (voice, speed, etc.) inputSchema?: ModelInputDef[]; // Model's input schema for dynamic handles parametersExpanded?: boolean; // Collapse state for inline parameter display + _settingsPanelHeight?: number; // Measured settings panel height for reload correction status: NodeStatus; error: string | null; audioHistory: CarouselAudioItem[]; // Carousel history (IDs only) @@ -263,6 +267,7 @@ export interface LLMGenerateNodeData extends BaseNodeData { temperature: number; maxTokens: number; parametersExpanded?: boolean; // Collapse state for inline parameter display + _settingsPanelHeight?: number; // Measured settings panel height for reload correction status: NodeStatus; error: string | null; }