From 5d45d90f847a05d722949a2e34cada30edd94a27 Mon Sep 17 00:00:00 2001 From: shrimbly Date: Tue, 3 Mar 2026 06:42:03 +1300 Subject: [PATCH] fix(46-04): ControlPanel layout, carousel overlay, and full-bleed nodes - ControlPanel: Replace provider/model dropdowns with model name text + Browse button. Fixed width, no horizontal scroll. - GenerateImageNode: Carousel controls now overlay on image bottom with semi-transparent dark background for legibility. - BaseNode: Add fullBleed prop for borderless mode. Image IS the node - no visible container, border, or padding. - GenerateImageNode uses fullBleed mode. Empty state uses subtle fill instead of dashed border. Co-Authored-By: Claude Opus 4.6 --- src/components/nodes/BaseNode.tsx | 15 +- src/components/nodes/ControlPanel.tsx | 69 +++------ src/components/nodes/GenerateImageNode.tsx | 170 ++++++++++----------- 3 files changed, 112 insertions(+), 142 deletions(-) diff --git a/src/components/nodes/BaseNode.tsx b/src/components/nodes/BaseNode.tsx index 8f395ff5..6958818c 100644 --- a/src/components/nodes/BaseNode.tsx +++ b/src/components/nodes/BaseNode.tsx @@ -14,6 +14,8 @@ interface BaseNodeProps { contentClassName?: string; minWidth?: number; minHeight?: number; + /** When true, node has no background/border — content fills the entire node area */ + fullBleed?: boolean; } export function BaseNode({ @@ -26,6 +28,7 @@ export function BaseNode({ contentClassName, minWidth = 180, minHeight = 100, + fullBleed = false, }: BaseNodeProps) { const currentNodeIds = useWorkflowStore((state) => state.currentNodeIds); const nodes = useWorkflowStore((state) => state.nodes); @@ -71,14 +74,16 @@ export function BaseNode({ />
-
{children}
+
{children}
); diff --git a/src/components/nodes/ControlPanel.tsx b/src/components/nodes/ControlPanel.tsx index bb432512..4a8afc08 100644 --- a/src/components/nodes/ControlPanel.tsx +++ b/src/components/nodes/ControlPanel.tsx @@ -353,61 +353,26 @@ function GenerateImageControls({ node }: { node: Node }) { return ( <>
- {/* Provider Selector */} + {/* Model display + Browse */}
- - -
- - {/* Model Selector */} - {isGeminiProvider ? ( -
- - -
- ) : ( -
- -
- - + +
+
+
+ {nodeData.selectedModel?.displayName || GEMINI_IMAGE_MODELS.find(m => m.value === nodeData.model)?.label || "Select model..."} +
+
+ {enabledProviders.find(p => p.id === currentProvider)?.name || currentProvider} +
- {modelsFetchError && ( -

{modelsFetchError}

- )} +
- )} +
{/* Gemini-specific controls */} {isGeminiProvider && ( diff --git a/src/components/nodes/GenerateImageNode.tsx b/src/components/nodes/GenerateImageNode.tsx index 878a6d92..0e432bd4 100644 --- a/src/components/nodes/GenerateImageNode.tsx +++ b/src/components/nodes/GenerateImageNode.tsx @@ -486,6 +486,7 @@ export function GenerateImageNode({ id, data, selected }: NodeProps {/* Input handles - ALWAYS use same IDs and positions for connection stability */} {/* Image input at 35%, Text input at 65% - never changes regardless of model */} @@ -547,113 +548,112 @@ export function GenerateImageNode({ id, data, selected }: NodeProps -
+
{/* Preview area */} {nodeData.outputImage ? ( <> -
- Generated - {/* Loading overlay for generation */} - {nodeData.status === "loading" && ( -
- - - - -
- )} - {/* Error overlay when generation failed */} - {nodeData.status === "error" && ( -
- + {/* Loading overlay for generation */} + {nodeData.status === "loading" && ( +
+ + - - - Generation failed - See toast for details -
- )} - {/* Loading overlay for carousel navigation */} - {isLoadingCarouselImage && ( -
- - - - -
- )} -
-
+ )} + {/* Error overlay when generation failed */} + {nodeData.status === "error" && ( +
+ - - - - + + + Generation failed + See toast for details +
+ )} + {/* Loading overlay for carousel navigation */} + {isLoadingCarouselImage && ( +
+ + + +
+ )} + {/* Clear button */} +
+
- {/* Carousel controls - only show if there are multiple images */} + {/* Carousel controls - overlaid on image bottom */} {hasCarouselImages && ( -
+
- + {(nodeData.selectedHistoryIndex || 0) + 1} / {(nodeData.imageHistory || []).length}