Gemini image models (nano-banana, nano-banana-pro, nano-banana-2) were
not showing parameters when set as fallback models. Three root causes:
1. Schema API returned empty for Gemini image models — added
getGeminiImageSchema() with aspect ratio, resolution, and search
grounding parameters per model
2. ModelParameters had an early-return guard skipping all non-Veo Gemini
models — removed to make the component provider-agnostic
3. nanoBananaExecutor ignored parametersOverride for Gemini-specific
fields (aspectRatio, resolution, useGoogleSearch, useImageSearch) —
now reads from override when available
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
The render guard at line 222 blocked all Gemini-provider models from
rendering parameters, including Veo models. Hoisted the isVeoModel
check to component scope and added the same exception used in the
fetch guard so Veo parameters (aspect ratio, duration, resolution)
display correctly.
Fixes#106
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
ModelParameters had an early return for all Gemini provider models,
skipping the API schema fetch. This was correct for Gemini image
models (which have built-in parameters) but prevented Veo video
models from loading their aspect ratio, duration, and resolution
parameters from the /api/models/[modelId] endpoint.
Closes#106
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
On 144Hz+ displays, requestAnimationFrame fires ~144 times/sec. This
adds a throttledRAF utility that limits callback execution to ~60fps
regardless of display refresh rate, normalizing performance across
60Hz, 120Hz, and 144Hz monitors.
Applied to:
- workflowStore hover debounce (event-driven throttle)
- FloatingNodeHeader position tracking (continuous loop throttle)
- ModelParameters ResizeObserver (event-driven throttle)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The displaySchema useMemo was placed after conditional early returns,
violating React's Rules of Hooks and causing hook order mismatch errors
when the component toggled between rendering null and its content.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Wrap ModelParameters and ParameterInput in React.memo
- Add useMemo on sortedSchema and displaySchema computations
- RAF-debounce ResizeObserver with redundancy guard
- Stabilize onChange callback (pass name as prop, not closure)
- Eliminates re-renders during canvas pan/zoom/drag
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Change outer wrapper bg from #2a2a2a to bg-neutral-800 to match node body,
eliminating visible color mismatch at rounded corners
- Remove overflow-hidden from InlineParameterPanel (was clipping fields)
- Use min(180px, 100%) in grid minmax so columns shrink to fit narrow nodes
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- 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>
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>
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>
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>
Use local state during editing and sync to store on blur, preventing
React Flow re-renders from resetting cursor position on every keystroke.
Also add click-to-seek on GenerateAudioNode waveform.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds 48-hour client-side caching to persist model data across dev server
restarts and page refreshes, eliminating repeated API calls during hot
reloading in development mode.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Split AudioInputNode waveform effect to avoid ResizeObserver churn on every animation tick
- Add missing kieApiKey/wavespeedApiKey deps to ModelParameters useEffect
- Clean up VideoSampleSource/Output on error in useApplySpeedCurve
- Fix off-by-one in useStitchVideos duplicate-frame skip (<= to <) and add try/finally for resource cleanup
- Replace leftover currentNodeId: null with currentNodeIds: [] in workflowStore
- Store full response metadata (status, headers, body) in deduplicatedFetch cache
- Add in-flight guard to imageStorage to prevent duplicate concurrent saves
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Multiple node instances (GenerateImageNode, GenerateVideoNode, etc.) were
each making their own /api/models requests when mounted, causing N duplicate
network requests when N nodes existed on the canvas.
This fix:
- Adds deduplicatedFetch utility that shares in-flight requests to the same URL
- Adds useProviderApiKeys() hook with shallow equality for stable API key refs
- Updates all components to use primitive dependencies instead of object refs
- Uses deduplicatedFetch for all model-fetching operations
Expected result: 1 request instead of N when multiple nodes need the same data.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Fixes issues with external provider models (e.g., seedream-4.5) where:
- Boolean/integer params with "image" in name were misclassified as image inputs
- Array params without items.type specification were incorrectly rejected
- Handle ID mismatch between node components (image-0) and store (image)
- dynamicInputs not passed to API in node regeneration code path
Changes:
- Update isImageInput() to check schema types, not just names
- Map both indexed (image-0) and legacy (image) handle IDs to schema names
- Add dynamicInputs to nanoBanana regeneration request body
- Add hidden backward-compat handles for edge routing
- Add comprehensive tests for schema classification and handle mapping
- Fix Toast test to use specific selector for close button
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Introduced a model search dialog for selecting external models in GenerateImageNode and GenerateVideoNode.
- Updated node titles to reflect selected models dynamically.
- Improved parameter handling by expanding the ModelParameters component to notify parent nodes on schema load.
- Adjusted handle positions and styles for better alignment and visual consistency.
- Enhanced input validation and processing for dynamic inputs across the workflow.
- Add ModelInputDef type to node data interfaces
- Update ModelParameters to pass inputs via onInputsLoaded callback
- GenerateImageNode and GenerateVideoNode store input schema
This sets up the foundation for dynamic handle rendering.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add onExpandChange callback to ModelParameters component
- Remove max-height constraint from expanded parameters
- GenerateImageNode/GenerateVideoNode calculate new height based on parameter count
- Node height grows when expanded, shrinks when collapsed
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add inline validation for number/integer parameters with min/max bounds
- Show red border and error text for out-of-range values
- Clear parameters when switching provider or model (schemas differ)
- Validation shows specific constraints (Min/Max values, integer requirement)
- Create ModelParameters component with collapsible UI
- Fetches schema from /api/models/{modelId} endpoint
- Renders appropriate inputs based on parameter type (string, number, boolean, enum)
- Add parameters field to NanoBananaNodeData and GenerateVideoNodeData
- Integrate into GenerateImageNode and GenerateVideoNode