- Add inheritedFrom field to EaseCurveNodeData interface
- Add easeCurve to HandleType union
- Update getHandleType, getNodeHandles, isValidConnection for easeCurve
- Add easeCurve extraction in getConnectedInputs
- Propagate parent bezierHandles/easingPreset in both execution paths
- Fix validation.ts default data for easeCurve node
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add EaseCurveNodeData to type imports
- Add easeCurve as video source in getConnectedInputs
- Add easeCurve case to executeWorkflow with speed curve processing
- Add easeCurve case to regenerateNode for individual node execution
- Dynamic imports for applySpeedCurveAsync and createBezierEasing
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- useApplySpeedCurve hook with MediaBunny encoding pipeline (no frame blending, no preview mode)
- Standalone applySpeedCurveAsync export for store/API usage
- SpeedCurveProgress type for progress tracking
- EaseCurveNodeData interface with bezierHandles, easingPreset, outputDuration, status fields
- "easeCurve" added to NodeType union and WorkflowNodeData
- Default dimensions (340x480) and node data (easeInOutSine preset) registered
- Shared applySpeedCurveCore extracts common logic between hook and standalone function
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Extract MIME type from data URL header when fetch() Blob loses it
- Create new Blob with correct type for MediaBunny format detection
- Fix both executeWorkflow and regenerateNode videoStitch paths
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>
WorkflowCanvas.tsx:
- Import VideoStitchNode
- Add videoStitch to nodeTypes
- Add getNodeHandles case: inputs=["video", "audio"], outputs=["video"]
- Update isValidConnection to allow video→videoStitch connections
- Add minimap color: orange-500 (#f97316)
ConnectionDropMenu.tsx:
- Add videoStitch to VIDEO_TARGET_OPTIONS (first position)
- Add videoStitch to VIDEO_SOURCE_OPTIONS (produces video output)
- Add videoStitch to AUDIO_TARGET_OPTIONS (accepts audio input)
workflowStore.ts:
- Import VideoStitchNodeData type
- Add videoStitch to getSourceOutput (returns video type)
- Add executeWorkflow case for videoStitch with full stitching logic:
* Check encoder support
* Get connected inputs (videos, audio)
* Convert video data URLs to Blobs
* Prepare audio if connected (using prepareAudioAsync)
* Call stitchVideosAsync with progress callback
* Store output (blob URL for >20MB, data URL otherwise)
* Handle errors
Note: nodeDefaults.ts already had videoStitch dimensions and
createDefaultNodeData from Plan 02, so no changes needed there.
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
- 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>
- 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
- 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
- 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 createdAt timestamp to WorkflowEdgeData interface
- Store createdAt on edge creation in onConnect and addEdgeWithType
- Add getImageSequenceNumber helper to EdgeToolbar
- Display "Image N" label for image connections with 2+ edges to same target
- Sequence numbers based on createdAt for stable ordering
- Single connections and text connections show no numbering
Add applyEditOperations to workflowStore for AI-driven workflow modifications.
Wire ChatPanel to process editWorkflow tool calls and apply operations via store.
Pass lightweight workflow state to chat API for context-aware assistance.
Changes:
- workflowStore: add applyEditOperations method and EditOperation import
- ChatPanel: add onApplyEdits prop, workflowState prop, custom fetch wrapper
- ChatPanel: process tool-invocation parts for editWorkflow and createWorkflow
- ChatPanel: render tool explanations in chat UI
- WorkflowCanvas: extract chatWorkflowState, create handleApplyEdits callback
- WorkflowCanvas: pass onApplyEdits and workflowState to ChatPanel
AI edits trigger captureSnapshot before applying (revert button support).
AI edits do NOT increment manualChangeCount (only manual edits clear snapshot).
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
loadWorkflow was unconditionally calling clearSnapshot(), wiping the
snapshot that captureSnapshot() just saved. Add preserveSnapshot
option to loadWorkflow and pass it from the AI build workflow path
so the Revert AI Changes button actually appears.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Test: multiple images to same schema-mapped handle aggregate into array
- Test: single image to schema-mapped handle stays as plain string
- Test: multiple images with distinct schema handles remain separate strings
- Test: three images to same handle produce array of length 3
- Fix react-dom version mismatch (19.2.0 -> 19.2.3) to match react
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Update dynamicInputs type from Record<string, string> to Record<string, string | string[]> in 4 locations
- Fix getConnectedInputs to aggregate same-key values into arrays instead of overwriting
- Fix dynamicInputs.prompt access to handle array case
- Fix hasPrompt check in POST handler to handle array-typed prompt
- Update processedDynamicInputs type in both Replicate and fal blocks
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Test: multiple images to same schema-mapped handle aggregate into array
- Test: single image to schema-mapped handle stays as plain string
- Test: multiple images with distinct schema handles remain separate strings
- Test: three images to same handle produce array of length 3
- Fix react-dom version mismatch (19.2.0 -> 19.2.3) to match react
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Update dynamicInputs type from Record<string, string> to Record<string, string | string[]> in 4 locations
- Fix getConnectedInputs to aggregate same-key values into arrays instead of overwriting
- Fix dynamicInputs.prompt access to handle array case
- Fix hasPrompt check in POST handler to handle array-typed prompt
- Update processedDynamicInputs type in both Replicate and fal blocks
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add defensive check before Object.keys(groups) in saveToFile and
exportWorkflow to prevent "Cannot read properties of undefined
(reading 'length')" when groups is undefined after AI workflow
generation.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add snapshot state and actions to capture, revert, and clear workflow
state before AI-initiated changes. Includes manual change tracking
that auto-clears snapshot after 3 user edits.
- Add previousWorkflowSnapshot state (nodes, edges, groups, edgeStyle)
- Add manualChangeCount state
- Implement captureSnapshot() to save current workflow state
- Implement revertToSnapshot() to restore snapshot
- Implement clearSnapshot() to remove snapshot
- Implement incrementManualChangeCount() with auto-clear at 3 changes
- Wire manual change tracking into addNode, removeNode, onConnect,
removeEdge, onNodesChange (remove only), onEdgesChange (remove only)
- Call clearSnapshot() in clearWorkflow and loadWorkflow
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
React Flow's internal bounds for nodes were undefined, causing certain nodes
to be incorrectly included in drag selections regardless of their position.
Added statistical outlier detection using IQR to identify and automatically
deselect nodes that are clearly outside the actual selection area.
Also includes:
- Clear selected state and validate position coordinates on workflow load
- Strip selected property when saving workflows (transient UI state)
- Enable prompt nodes to receive text input connections
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>
Two related issues fixed:
1. Image history IDs not syncing before workflow save (PR#27 follow-up):
- Added pending sync tracking for save-generation API calls
- saveToFile() now awaits all pending syncs before saving
- Ensures imageHistory/videoHistory IDs match actual saved filenames
2. Duplicate images created on every save:
- After externalization, store nodes now receive the image refs
- Subsequent saves recognize existing refs and skip creating new files
- Prevents inputs/ and generations/ folders from accumulating duplicates
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Enable storing default model and settings per node type in localStorage.
New nodes created via shortcuts will use these stored preferences.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The save-generation API changed to use prompt+hash based filenames for
deduplication, but the workflow store was still using timestamp-based IDs
in imageHistory/videoHistory. This caused the carousel to fail loading
images since the IDs didn't match the actual saved filenames.
Now we capture the API response and update the history entry with the
actual imageId returned by the save-generation endpoint. This is backward
compatible - old workflows with timestamp-based filenames continue to work.
- Add SelectedModelPricing interface to providers.ts
- Update SelectedModel type with optional pricing field
- Track fal.ai costs in nanoBanana execution and regeneration
- Track fal.ai costs in generateVideo execution and regeneration
- Skip cost tracking for Replicate (no pricing API available)
- Gemini continues to use hardcoded pricing
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>
- Added support for long-running video generation using a queue system with a 10-minute timeout.
- Updated maxDuration constant to reflect the new timeout for video generation.
- Enhanced API request handling to accommodate video generation, including error handling and polling for completion.
- Updated model retrieval tests to reflect the inclusion of additional models and improved provider filtering.
- Modified component tests to track model usage and reflect changes in the UI for provider selection.
- Image data flow through node chains (imageInput, annotation, nanoBanana)
- Text data flow through node chains (prompt, llmGenerate)
- Dynamic inputs from schema-mapped connections
- State updates during execution (status, outputImage)
- Mixed image and text data flow
- Add RecentModel interface to types/index.ts
- Add localStorage helpers: getRecentModels, saveRecentModels, MAX_RECENT_MODELS
- Add recentModels state to workflowStore (initialized from localStorage)
- Add trackModelUsage action to track model usage with timestamps
- Create src/store/utils/nodeDefaults.ts with node creation utilities
- Move createDefaultNodeData function (90 lines)
- Move GROUP_COLORS and GROUP_COLOR_ORDER constants
- Consolidate defaultNodeDimensions (was duplicated in addNode and createGroup)
- Fix: createGroup's dimensions now includes generateVideo node type
- Re-export GROUP_COLORS from workflowStore for backward compatibility
- 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 CarouselVideoItem type to types/index.ts
- Update GenerateVideoNodeData with videoHistory and selectedVideoHistoryIndex
- Add default values in createDefaultNodeData for generateVideo
- Add video history tracking in executeWorkflow for generateVideo
- Add video history tracking in regenerateNode for generateVideo
- Save videos with videoId for carousel support
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Updated .env.example to include new API keys for OpenAI, Replicate, and fal.ai.
- Modified API routes to prioritize user-provided API keys over environment variables for Gemini and OpenAI.
- Improved error messages for missing API keys to guide users on configuration.
- Enhanced ProjectSetupModal to support OpenAI provider and manage API key overrides.
- Updated workflow store to handle new provider settings and API key integration for OpenAI.
- Added functionality to auto-save generated images and videos to the specified directory.
- Render dynamic input handles from model schema (GenerateImage/VideoNode)
- Update connection validation with getHandleType() helper
- Add dynamicInputs to getConnectedInputs() return value
- Pass dynamicInputs through generation pipeline (workflowStore -> API)
- Update generateWithFal/Replicate to use dynamic inputs
- Process large images in dynamicInputs to URLs for external providers
- Support prompt from dynamicInputs when top-level prompt is empty
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Mark NanoBananaNode alias as deprecated with migration guidance
- Mark saveNanoBananaDefaults as deprecated with alternative
- ModelSearchDialog already has video capability badges and filtering
- Include parameters in requestPayload for nanoBanana and generateVideo nodes
- Add logging for custom parameters in Replicate and fal.ai provider functions
- Parameters already spread via ...input.parameters in existing provider code
- Add generateVideo case to getConnectedInputs for downstream nodes
- Update output node execution to detect video content type
- Set video and contentType fields when receiving video content
- Enable Prompt -> GenerateVideo -> Output workflow pipeline
Extend /api/generate to support video output from Replicate and fal.ai:
- Detect video content type in Replicate response
- Handle fal.ai video.url field for video model responses
- Return video/videoUrl fields with contentType for videos
- Add generateVideo case to executeWorkflow and regenerateNode
- Handle large videos (>20MB) by returning URL directly
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add GenerateVideoNode for video generation using Replicate and fal.ai:
- Add GenerateVideoNodeData interface with video output field
- Add generateVideo to NodeType union
- Create GenerateVideoNode component with provider/model selection
- Register in WorkflowCanvas with purple minimap color (#9333ea)
- Add default dimensions and node data creation in store
- Update validation.ts for new node type
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add ModelSearchDialog with search, provider filter, and capability filter
- Integrate with /api/models endpoint using API key headers
- Display model grid with cover images, provider badges, and descriptions
- Update addNode to accept optional initialData for pre-configured nodes
- Replace placeholder in FloatingActionBar with actual dialog
- Add modelSearchOpen/modelSearchProvider state to workflowStore
- Add setModelSearchOpen action to toggle dialog with provider filter
- Add ProviderIconButton component with Replicate (R) and fal.ai (lightning) icons
- Replicate icon only visible when API key is configured
- fal.ai icon always visible (works without key but rate limited)