Fix video element not reloading on regeneration by adding key prop tied
to video history ID. Fix cancel showing timeout message by passing
"user-cancelled" reason through AbortController and checking it in all
three executor catch blocks.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The loopCount (1x/2x/3x) feature was only applied in executeWorkflow()
but not in regenerateNode(), which is the code path triggered by
clicking the "Stitch" button. This adds the blob duplication logic to
both code paths and includes the UI controls and type definitions.
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>
- Stabilize thumbnail extraction deps with a string key derived from clip
edge IDs, preventing excessive re-runs on unrelated node changes
- Use ref-based cache for thumbnails and add cleanup abort for pending
extractions
- Fix clipOrder sync triggering extra render by tracking last written order
- Add WaveSpeed case to ProviderBadge in GenerateImageNode and
GenerateVideoNode
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- AudioInputNode: use named event handlers with removeEventListener in cleanup
- deduplicatedFetch: track setTimeout IDs and clear them in clearFetchCache()
- waitForPendingImageSyncs: clear timeout in finally block after Promise.race
- decodeWithMediaBunny: wrap in try/finally to close sink, input, blobSource
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The presets overlay was being clipped by BaseNode's overflow-hidden container.
Render it through createPortal to document.body with position tracking via
requestAnimationFrame, matching the existing pattern used for comment tooltips.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Use fixed-width editor, absolute-positioned video to fill node, and
uniform gap-based spacing for controls below the curve editor.
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>
- Node height adjusts between 480px (editor) and 320px (video) on tab switch
- Default to video tab when settings are inherited from parent
- Rename "Ease In"/"Ease Out" handle labels to "Settings"
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
HTML5 drag-and-drop doesn't work inside React Flow nodes because
React Flow captures pointer events at a higher level. Switch to
pointer-event-based reorder with visual feedback (dragged item dims,
hover target highlights with blue ring).
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add nodrag class to thumbnail items so React Flow doesn't capture
drag events, allowing the HTML drag-and-drop reorder to work.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Extract thumbnails from easeCurve and videoStitch source nodes (not
just generateVideo)
- Add content-start to grid container so rows pack to top instead of
stretching to fill available space
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Replace 2 video handles with 4 labeled handles (Video In/Out at 35%, Ease In/Out at 75%)
- Add lime-300 color for easeCurve handles matching node accent
- Compute inheritance state from edges (useMemo)
- Add inheritance overlay on Editor tab: semi-transparent dark bg with informational text
- Add "Control manually" button that removes easeCurve edge and clears inheritedFrom
- Disable CubicBezierEditor, Presets button, and Apply button when inherited
- Video tab unaffected by inheritance state
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Replace horizontal scroll filmstrip with 4-column CSS grid layout
- Use aspect-video for thumbnails that scale with grid cells
- Add blue "Video N" labels to each video input handle
- Add blue "Output" label to video output handle
- Audio handle label unchanged (purple)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
All Easing Functions presets were not updating the editor because
handleSelectEasing fell back to the same DEFAULT_CUSTOM_BEZIER for every
non-preset easing. Added EASING_BEZIER_MAP with correct cubic-bezier
values from easings.net and a getEasingBezier() lookup. Also corrected
three PRESET_BEZIERS entries (easeInOutExpo, easeInOutCubic, easeInOutSine)
that diverged from the easings.net source.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Two tabs: Editor (curve editor) and Video (processed result)
- CubicBezierEditor integration with nodrag isolation
- Preset popover with visual SVG thumbnails of all 24 easing functions
- 5 Bezier presets with exact handles, plus 19 sampled polyline thumbnails
- Duration control (0.1-30 sec) with nodrag input
- Video preview with auto-play, loop, click-to-clear
- Auto-switch to Video tab when processing completes
- Encoder detection on mount with unsupported browser fallback
- Processing overlay with spinner and progress percentage
- Error display below tabs
- Run button via BaseNode onRun
- Exported from src/components/nodes/index.ts
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>
Ensure handles render in all node states (disabled/checking/normal) by
extracting a shared renderHandles function. Move clipOrder syncing from
useMemo side-effect into a proper useEffect. Add dynamic video handle
resolution in WorkflowCanvas for indexed handles. Add VideoStitch
execution case in regenerateNode.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add level-based topological sort (groupNodesByLevel) to identify
nodes that can execute concurrently
- Change state from currentNodeId to currentNodeIds[] for tracking
multiple executing nodes
- Execute nodes in batches using Promise.allSettled with configurable
concurrency limit (default 3, adjustable 1-10)
- Integrate AbortController for fail-fast cancellation on errors
- Pass abort signals to API fetch calls for proper request cancellation
- Update UI: multiple nodes show execution border, FloatingActionBar
shows "N nodes" during parallel execution
- Add concurrency slider in Project Settings > Node Defaults tab
- Store concurrency preference in localStorage
Workflows with independent generate nodes (e.g., 3 parallel image
generations) now complete in ~20-25s instead of ~60-90s sequential.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Component with encoder detection on mount (checkEncoderSupport)
- Dynamic video input handles (video-0, video-1, etc.) based on connections
- Audio input handle at bottom (violet color, matching AudioInput)
- Video output handle
- Filmstrip UI with thumbnail extraction (seek to 25% duration)
- Clip ordering via clipOrder array with drag-to-reorder support
- X button per clip to disconnect
- Processing trigger button (disabled until 2+ clips connected)
- Progress overlay during stitching (shows percentage)
- Output video preview on completion
- Disabled state when encoder not supported
- Export added to nodes/index.ts
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Added AudioInputNode export to nodes/index.ts
- Added audioInput to defaultNodeDimensions (300x200) and createDefaultNodeData
- Registered audioInput in WorkflowCanvas nodeTypes map
- Updated getHandleType to recognize audio handles
- Added audioInput to getNodeHandles (no inputs, audio output)
- Updated isValidConnection for audio-to-audio validation
- Added audio to ConnectionDropState handleType union
- Updated findCompatibleHandle to accept audio type
- Added AUDIO_SOURCE_OPTIONS and AUDIO_TARGET_OPTIONS to ConnectionDropMenu
- Added audio array to getConnectedInputs return type
- Updated getSourceOutput to handle audioInput nodes
- Added audioInput execution case (no-op, data source)
- Added AudioInputNodeData import to workflowStore
- Updated DEFAULT_DIMENSIONS in WorkflowCanvas and quickstart/validation.ts
- Updated createDefaultNodeData in quickstart/validation.ts
- Added violet minimap color (#a78bfa) for audioInput nodes
- File upload via drag-and-drop or click
- Waveform visualization using useAudioVisualization hook
- Play/pause controls with scrub bar
- Canvas-based rendering with ResizeObserver for responsive sizing
- Audio handle (purple/violet) on right side
- Min dimensions: 250px width, 150px height
- Remove button to clear audio file
- Fix Kie.ai GPT Image 1.5 failing to retrieve output URL by parsing
resultJson as JSON string (was treated as object incorrectly)
- Add 1x5 grid option to SplitGridNode for vertical contact sheets
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add onKeyDown handler to PromptNode variable name input so Enter saves.
Add outputGallery, imageCompare, and promptConstructor to ConnectionDropMenu
handle mappings for auto-connect on drag-drop. For imageCompare, redirect
connections to the second handle (image-1) when the first is occupied.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Extract autocomplete logic to usePromptAutocomplete hook
- Add PromptConstructorEditorModal with portal rendering
- Sync PromptConstructor outputText reactively for downstream nodes
- Add headerButtons prop to BaseNode for custom header actions
- Rename Blank Canvas to New Project with ProjectSetupModal integration
- Auto-push generated images to connected OutputGallery nodes
- Deduplicate images in OutputGallery during workflow execution
- Move PromptNode variable button to header with hover animation
- Extract AvailableVariable interface to types/nodes.ts
- Update tests for onNewProject rename
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Two labeled image input handles: 'A' (top 35%) and 'B' (bottom 65%)
- Real-time image collection from connected nodes (same pattern as OutputGalleryNode)
- ReactCompareSlider with horizontal-only slider (portrait=false)
- Corner labels A and B on comparison view
- Placeholder states for 0 or 1 images with connection status feedback
- nodrag nopan nowheel classes for interaction isolation
- BaseNode wrapper with customizable title and comments
- Build verified: zero TypeScript errors
- Install react-compare-slider dependency
- Add imageCompare to NodeType union
- Add ImageCompareNodeData interface (imageA, imageB fields)
- Add to WorkflowNodeData union type
- Register in nodeDefaults with 400x360 dimensions
- Add to WorkflowCanvas nodeTypes and minimap (teal color)
- Add to ConnectionDropMenu IMAGE_TARGET_OPTIONS
- Add executeWorkflow case to assign images[0] to imageA, images[1] to imageB
- Export ImageCompareNode from components/nodes/index
- Add to validation.ts DEFAULT_DIMENSIONS and createDefaultNodeData
- Template textarea with @ autocomplete dropdown
- Detects @ typing and shows matching connected variable names
- Autocomplete dropdown shows @name and prompt value preview
- Arrow keys navigate, Enter/Tab select, Escape closes
- Client-side computation of unresolvedVars from @(\w+) patterns
- Warning badge displays unresolved @tags in amber
- Live preview tooltip on hover showing resolved template
- availableVariables computed from connected prompt nodes with variableName
- Text input handle (target, left) and text output handle (source, right)
- BaseNode wrapper with title 'Prompt Constructor'
- Sync template to store on blur (same pattern as PromptNode)
- Local state prevents cursor jumping during typing
- Filtered autocomplete based on typed characters after @
- Position autocomplete relative to textarea with line height calculation
- Add variableName field to PromptNodeData (optional, backward compatible)
- Add promptConstructor to NodeType union
- Add PromptConstructorNodeData interface (template, outputText, unresolvedVars)
- Add to WorkflowNodeData union type
- Add promptConstructor to defaultNodeDimensions (340x280)
- Add createDefaultNodeData case for promptConstructor
- Modified PromptNode with @ variable naming dialog using createPortal
- Variable name validation: alphanumeric + underscore, max 30 chars
- Blue @ icon when variableName set, dimmed when not set
- Dialog shows variable name input, preview, Save/Clear buttons
- Register PromptConstructorNode in WorkflowCanvas nodeTypes
- Add promptConstructor minimap color (#f472b6 pink)
- Add to TEXT_TARGET_OPTIONS and TEXT_SOURCE_OPTIONS in ConnectionDropMenu
- Add getConnectedInputs support for promptConstructor outputText
- Add executeWorkflow case for promptConstructor:
- Finds connected prompt nodes via text edges
- Builds variable map from variableName + prompt values
- Resolves template by replacing @variableName with values
- Tracks unresolved variables in unresolvedVars array
- Stores outputText and unresolvedVars on node data
- Export PromptConstructorNode from components/nodes/index.ts
- Update duplicate defaultDimensions in WorkflowCanvas and quickstart validation
- Create OutputGalleryNode.tsx with BaseNode wrapper
- Single image input handle (type target, id image)
- Real-time image collection from connected nodes (imageInput, annotation, nanoBanana)
- 3-column scrollable thumbnail grid with nowheel for scroll isolation
- Full-screen lightbox via createPortal with:
- Left/right arrow navigation
- Download button (saves as gallery-image-N.png)
- Close button (X icon, top-right)
- Click backdrop to close
- Keyboard support (Escape, ArrowLeft, ArrowRight)
- Image counter display (N / Total)
- Empty state placeholder when no images connected
- Fix validation.ts: add outputGallery dimensions and createDefaultNodeData case
- Build passes with zero errors
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add outputGallery to NodeType union in types/nodes.ts
- Add OutputGalleryNodeData interface with images array field
- Add outputGallery to WorkflowNodeData union
- Register in nodeDefaults: dimensions 320x360, default data with empty images array
- Export OutputGalleryNode from nodes/index.ts
- Register outputGallery in WorkflowCanvas nodeTypes with pink minimap color (#ec4899)
- Add outputGallery to ConnectionDropMenu IMAGE_TARGET_OPTIONS with grid icon
- Add outputGallery case to executeWorkflow in workflowStore
- Add outputGallery to getNodeHandles (image input, no outputs)
- Add outputGallery dimensions to keyboard shortcut default dimensions
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add Kie.ai as a new provider for image and video generation:
Image models (8):
- z-image, seedream/4.5-text-to-image, seedream/4.5-edit
- nano-banana-pro, gpt-image/1.5-text-to-image, gpt-image/1.5-image-to-image
- google/nano-banana, google/nano-banana-edit
Video models (11):
- sora-2-text-to-video, sora-2-image-to-video
- sora-2-pro-text-to-video, sora-2-pro-image-to-video
- veo3_fast, veo3, bytedance/seedance-1.5-pro
- kling-2.6/text-to-video, kling-2.6/image-to-video
- wan/2-6-text-to-video, wan/2-6-image-to-video
Key changes:
- Add "kie" to ProviderType union
- Add generateWithKie() with image upload and task polling
- Add hardcoded model list and schemas (no discovery API)
- Update workflow store for API key handling
- Update UI components with Kie provider badge
Co-Authored-By: Claude <noreply@anthropic.com>
LLMGenerateNode improvements:
- Add collapsible parameters section with temperature and max tokens sliders
- Add copy to clipboard button for output text
- Clean up UI organization
PromptNode improvements:
- Add text input handle to receive incoming text connections
- Auto-populate prompt when connected to a text source (e.g., another LLM)
ConnectionDropMenu:
- Add Prompt as a target option for text connections
ProjectSetupModal:
- Add max tokens slider for LLM default settings
Test updates:
- Update mocks to include edges and getConnectedInputs
- Fix tests for collapsible parameters section
- Update wrap-around test for new menu options
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Update getHandleType() to recognize indexed handles (text-0, image-0)
used by external providers like Replicate and fal.ai
- Fix validateWorkflow() to accept indexed text handles for nanoBanana nodes
- Add validation for generateVideo nodes requiring text input
- Use local state pattern in PromptNode to prevent cursor jumping during typing
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
## Template Explorer Enhancements
- Add rich metadata and thumbnails for community workflows
- Create communityWorkflows.ts config for workflow metadata
- Reuse TemplateCard for community workflows with tag filtering
- Search now includes workflow descriptions
## Comment Navigation System
- Add comment navigation icon in header with unviewed count badge
- Navigate between nodes with comments using prev/next controls
- Tooltips show comment preview on hover
## Output Node Improvements
- Add custom filename and createDirectory options for output saves
- Remove default React Flow styling from output nodes
- Fix video output node connections
- Preserve node height on resize
## Image Handling Fixes
- Resolve image history race condition and duplicate images on save
- Preserve image history IDs when saving to new directory
- Verify outputImageRef matches selected history index on save
- Persist useExternalImageStorage setting in workflow config
- Paste images directly into selected imageInput nodes
## Model & Generation Fixes
- Use selectedModel.modelId for Gemini generation
- Return 200 with notFound flag instead of 404 for missing files
## Other
- Add git-lfs tracking for large workflow files
- Remove deprecated example workflows
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The aspectRatio, resolution, and useGoogleSearch controls were only
shown when isGeminiOnly was true, but that was always false since
fal.ai is always available. Now these controls show based on whether
the currently selected model is a Gemini model.
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>
- Always render image/text handles with placeholders for unused inputs
- Placeholder handles (dimmed 30%) preserve connections for models that
don't use that input type (e.g., text-to-image models)
- Normalize dynamic handle IDs to standard values (image, text, image-0)
- Update findCompatibleHandle to return normalized IDs
- Map normalized handles to schema names in getConnectedInputs
- Exclude image_size from image input detection
- Switch save-generation to hash suffix format for better readability
Co-Authored-By: Claude <noreply@anthropic.com>
- Fix isImageInput() to use word-boundary checks instead of substring
matching, preventing num_images from being misclassified as image input
- Fix stale node data in workflow execution by getting fresh data from
store instead of using captured array references
- Add missing parameters to regenerateNode request bodies for both
nanoBanana and generateVideo nodes
- Update fal.ai schema fetch to use Model Search API endpoint
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add Gemini sparkle logo to ProviderBadge component
- Move provider badge to left of title as prefix
- Remove "Generate Image/Video" text, show only model name
- Add titlePrefix prop to BaseNode for prepending icons
- Increase badge size from w-3 to w-3.5 for better visibility
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add ProviderBadge component showing Replicate/fal.ai icons
- Display badge in node header next to Browse button
- Gemini nodes show no badge (default provider)
- Reuse official SVG paths from FloatingActionBar
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add isLoadingCarouselVideo state for loading indicator
- Add generationsPath from store for video loading
- Add loadVideoById callback to fetch videos from API
- Add carousel navigation handlers (handleCarouselPrevious/Next)
- Add hasCarouselVideos check for conditional display
- Add carousel UI with prev/next buttons and position indicator
- Add loading overlay during carousel navigation
- Update validation.ts with videoHistory fields
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Import useToast and useRef for error handling
- Add useEffect to trigger persistent toast when error occurs
- Add red error overlay visible over previous output video
- Overlay shows error icon and directs user to toast for details
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Import useToast and useRef for error handling
- Add useEffect to trigger persistent toast when error occurs
- Add red error overlay visible over previous output image
- Overlay shows error icon and directs user to toast for details
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>