- Add bg-[#2a2a2a] rounded-lg to outer wrapper when settings expanded to
fill gap behind rounded corners at node/panel junction
- Reduce grid min column width from 240px to 180px so fields fit in narrow nodes
- Add overflow-hidden to InlineParameterPanel content div as safety net
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Convert trackedSettingsHeight from useState to useRef to eliminate a
race condition between two useLayoutEffect hooks. On expand, both the
rAF-based measure (Effect 1) and the ResizeObserver (Effect 2) were
adding the panel height — a double-add. On collapse only one subtracted,
leaving residual height each cycle.
The fix removes the expand branch from Effect 1 (now collapse-only) and
lets the ResizeObserver solely handle initial and dynamic measurement.
Using a ref also prevents the ResizeObserver from being torn down and
recreated on every height change.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
calculateAspectFitSize was receiving the total node height (content +
settings panel), producing incorrect aspect-fit dimensions when settings
were expanded. Now subtracts trackedSettingsHeight before calculating
and re-adds it when applying the final height.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Switch BaseNode wrapper from `display: contents` to flex layout when
settings are expanded so NodeResizer handles span the full visual height.
Height management is now centralized in BaseNode via useLayoutEffect +
ResizeObserver instead of per-node useEffects that compounded height.
- BaseNode: track settings panel height, adjust node dimensions on
expand/collapse, observe dynamic content changes
- InlineParameterPanel: remove animation (instant show/hide), remove
selection ring logic (now handled by BaseNode outer container)
- GenerateImageNode/GenerateVideoNode: remove buggy height management
useEffects and parameterPanelRef
- All 5 node types: remove `selected` prop from InlineParameterPanel
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move InlineParameterPanel from BaseNode children to a settingsPanel prop
rendered as a sibling of the bordered div. This fixes:
- Notch between preview and settings (width mismatch from border inset)
- Selection ring overlap/gap at the junction (clip-path on separate overlay)
- Bottom border and rounding when settings expanded
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add settingsExpanded prop to BaseNode. When true + selected, the node
div uses rounded-t-lg instead of rounded-lg so its ring has no bottom
rounding. Combined with InlineParameterPanel's overlapping ring with
rounded-b-lg, this creates a seamless continuous selection outline.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Revert min-h-full back to h-full (which broke preview layout). Instead,
InlineParameterPanel uses -mt-[3px] negative margin when expanded+selected
to overlap with BaseNode's ring bottom edge, creating a seamless continuous
blue outline around both preview and settings areas.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Change BaseNode inner div from h-full to min-h-full so it grows to
encompass the settings panel. The ring now wraps the entire area as
one continuous outline instead of two separate rings with a gap.
Removed the now-unnecessary selected prop from InlineParameterPanel.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
InlineParameterPanel now accepts a selected prop and draws a matching
ring-2 ring-blue-500/40 around the settings area when the node is
selected and parameters are expanded. Passed from all five node types.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Settings area uses lighter grey (#2a2a2a), field inputs use darker
grey (#1a1a1a) with no borders, matching the reference design.
Applied across InlineParameterPanel, GenerateImageNode, LLMGenerateNode,
and ModelParameters.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Chevron toggle now shows "Settings" text and has no background when
collapsed so it floats below the node edge. Solid bg only appears on
the expanded content area. Preview container switches from rounded-lg
to rounded-t-lg when parameters are expanded so the settings area
connects seamlessly to the image above.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Solid dark background attached to image preview, labels on same line
as fields, slightly larger text (11px), darker rounded inputs matching
the reference design. Applied consistently across Gemini controls,
LLM controls, and ModelParameters (external provider fields).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Center max-width container, clip rounded bottom corners, improve
chevron visibility with subtle background, remove duplicate model
selector from image/video nodes (already in FloatingNodeHeader),
and unify control sizing across Gemini and LLM selects.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Apply max-w-[280px] to parameter fields container
- Prevents fields from stretching too wide on large nodes
- Maintains compact, readable parameter inputs
- Replace useState with useSyncExternalStore for shared reactive state
- Add subscriber system to notify all hook consumers when value changes
- Fix bug where toggling setting required page refresh to see updates
- All generation nodes now update immediately when setting changes
- Import useInlineParameters hook and InlineParameterPanel component in all 3 nodes
- Add LLM_PROVIDERS and LLM_MODELS constants to LLMGenerateNode
- Add collapse state computation and toggle handlers for all 3 nodes
- LLMGenerateNode: render inline controls for provider, model, temperature, max tokens with compact styling
- GenerateAudioNode: render model selector and ModelParameters inline
- Generate3DNode: render model selector and ModelParameters inline
- Hide top ModelParameters when inline enabled (prevents duplication)
- All nodes use unified compact styling matching GenerateImageNode Gemini controls
- Import useInlineParameters hook and InlineParameterPanel component
- Add collapse state computation and toggle handler for both nodes
- Render InlineParameterPanel after content area when inline enabled
- GenerateImageNode includes Gemini-specific controls (model, aspect ratio, resolution, search toggles) with unified styling
- GenerateImageNode includes ModelParameters for external providers
- GenerateVideoNode includes model selector and ModelParameters
- Add node height management effects that measure parameter panel and resize nodes
- Conditional rendering: inline params only show when global setting enabled
- Import useInlineParameters hook in ControlPanel and ProjectSetupModal
- Define GENERATION_NODE_TYPES constant for conditional hiding logic
- Hide ControlPanel for generation nodes when inline parameters enabled
- ControlPanel still shows for easeCurve and conditionalSwitch regardless of setting
- Add inline parameters toggle to ProjectSetupModal Project tab
- Toggle labeled 'Inline Parameters' with descriptive help text
- Setting persists to localStorage and applies immediately
- Add parametersExpanded field to 5 generation node data types (NanoBanana, GenerateVideo, Generate3D, GenerateAudio, LLMGenerate)
- Create useInlineParameters hook for localStorage-backed global setting (default: false)
- Create InlineParameterPanel component with collapsible UI and chevron toggle
- All TypeScript compiles without new errors
- Remove duplicate idle-state tests in GenerateImageNode and GenerateVideoNode
- Fix misleading "Text target labels" comment to "Text source labels"
- Use object-contain on AnnotationNode so full image is visible for annotation
- Add abort signal checks to Router/Switch/ConditionalSwitch executors
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add nodeClickDistance={5} to match nodeDragThreshold, eliminating the
0-5px dead zone where d3-drag suppressed clicks but drags hadn't started.
Stop header pointerdown propagation to prevent pane deselection race.
Fix header drag threshold to use screen pixels for zoom-independent behavior.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
React Flow prioritizes node.width over node.style.width for rendering.
NodeResizer writes to node.width but the aspect-fit handler was reading
from node.style and only writing back to node.style — so resized
dimensions were silently overridden. Extract getNodeDimension() and
applyNodeDimensions() helpers to read/write both sources consistently.
Also removes an unused Zustand store subscription that caused unnecessary
re-renders.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The previous implementation called getNodes() in handlePointerUp which
could return stale pre-drag positions in React Flow's controlled mode,
causing nodes to remain in groups after being dragged out. Also only
checked the header node, not all selected nodes moved together.
Now calculates final positions from drag delta (startPos + clientDelta/zoom)
and iterates all entries in startPositions to check group membership for
every moved node.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The aspect-fit double-click feature broke after manual resize because imperative
DOM listeners (useEffect + addEventListener) fall out of sync when React Flow
re-renders resize controls. Switches to a React onDoubleClick handler on a
`display: contents` wrapper, which stays in sync across all re-renders.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Attach dblclick listener to stable .react-flow__node wrapper instead of
individual .react-flow__resize-control elements. React Flow recreates
resize controls on re-render after manual resize, leaving old listeners
on stale DOM nodes. Event delegation ensures the handler survives across
re-renders.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When a node has media (image/video) and resize handles are visible,
double-clicking any resize handle resizes the node to match the media's
aspect ratio, preferring to grow rather than shrink. Applies to all
selected nodes for multi-selection consistency.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Import defaultNodeDimensions for node sizing
- Capture wasDragging state before resetting ref
- Calculate node center using measured/default dimensions
- Check if node center is inside any group bounds
- Update groupId via setNodeGroupId if it changed
- Matches WorkflowCanvas handleNodeDragStop logic
Fixes bug where nodes dragged outside groups via floating header
were not removed from the group, causing them to still move when
the group was dragged.
Move pointer-events-auto from the header's outer container to the inner
content bar so invisible header area doesn't block events reaching nodes.
Switch custom drag handler from mouse events to pointer events for
consistency with React Flow. Fix pointer capture leak in VideoStitchNode
that could break subsequent drags.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When a child EaseCurve node inherits settings from a parent via an
easeCurve handle connection, the ControlPanel now shows a semi-transparent
overlay indicating inheritance with a "Control manually" button to break
the connection and restore manual editing.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Wrap each button in a flex justify-end container so they sit in the
bottom-right of the settings panel.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Match the existing panel button style (bg-neutral-700, border, compact
sizing) instead of full-width blue. Add play triangle icon for Run
buttons and checkmark icon for the Apply button.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add execution buttons at the bottom of each configurable node's settings
panel so users can trigger generation directly from where they configure
settings, instead of needing keyboard shortcuts or external run buttons.
- GenerateImage, GenerateVideo, Generate3D, GenerateAudio, LLM: "Run" button
- EaseCurve: "Apply" button
- ConditionalSwitch: no button (passive routing node)
- Buttons disable and show running state while workflow executes
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The easingCurve prop (showing the actual easing function curve over the
bezier editor) was present in the old node-body editor but wasn't carried
over to the ControlPanel's EaseCurveControls.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Strip editor tab, tab bar, and all editor-related state/handlers from EaseCurveNode
since controls now live in the ControlPanel. Node body shows only the video preview
full-bleed. Fix ControlPanel presets popup to open leftward (right-anchored) so it
stays on-screen when panel is at the right viewport edge.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove nodrag/nopan from the text output area so the node can be
dragged from the body (text is read-only, not editable). Move
nodrag/nopan to the individual action buttons instead. Hide
copy/regenerate/clear buttons by default, showing them only on
hover of the text area to prevent overlapping content.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Switch to fullBleed BaseNode so the image preview fills the entire
node area. Move grid config, Settings, and Split controls into an
absolute-positioned overlay at the bottom with backdrop blur,
matching the pattern used by PromptConstructorNode and other
full-bleed nodes.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add createPortal import from react-dom
- Wrap modal JSX in createPortal targeting document.body
- Modal now renders outside React Flow's DOM tree entirely
- Fixes z-index bleed-through from React Flow node elements
- Change fullBleed nodes from transparent to subtle bg-neutral-800/50 with border-neutral-700/40
- Makes empty GenerateImage, GenerateVideo, and LLMGenerate nodes visible on canvas
- Content still covers background when present (images/videos fill the area)
Remove provider and model dropdowns, replacing with model name +
provider display block and Browse button — same as image and video.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Always display parameters inline instead of behind a collapsible
"Parameters" header. The toggle added unnecessary interaction cost.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove provider and model dropdowns from the video control panel,
replacing them with the cleaner model name + provider display block
with Browse button — matching the image node's updated layout.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add hoveredNodeId to store, set from BaseNode mouse events, read in
FloatingNodeHeader. Controls now appear when hovering any part of
the node, not just the floating header strip.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Bump ControlPanel from z-[50] to z-[90] so the layered box-shadow
paints in front of the React Flow canvas stacking context, not
behind it. Still below z-[100] used by modals and toolbars.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Apply Tobias Ahlin / Josh Comeau layered shadow technique: each
layer doubles offset and blur (1/2/4/8/16/32px) with decreasing
opacity. Each layer covers a narrow distance band so no single
layer interpolates over a large range, eliminating banding.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
CSS linear-gradient bands on large areas due to 8-bit color depth.
box-shadow is compositor-rendered and produces smooth falloff without
visible stepping artifacts.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace the narrow hard-edged gradient strip with a wide, smooth
multi-stop gradient that extends behind the panel for a natural
shadow falloff instead of a stepped/hard edge.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- LLMGenerateNode: Remove inline provider/model/parameter controls
- LLMGenerateNode: Convert to full-bleed layout with fullBleed prop
- LLMGenerateNode: Show only text output area in node body
- LLMGenerateNode: Keep floating action buttons (copy, regenerate, clear)
- ControlPanel: Add Max Tokens slider to LLMControls (256-16384 range)
- ControlPanel: Add shadow/gradient overlay on left edge of panel
- WorkflowCanvas: Fix type errors with node.data union types (Rule 1 bug fix)