- Refactor executeSingleNode error paths to throw instead of setting global
isRunning/currentNodeIds state, which corrupted sibling nodes running in
parallel via Promise.allSettled
- Validate WaveSpeed providedPollUrl with SSRF check before use
- Sanitize WaveSpeed taskId in provider with path-traversal validation
- Revoke blob URLs (URL.revokeObjectURL) before replacing outputVideo to
prevent memory leaks from large video files
- Move pause edge cleanup to outer executeWorkflow loop
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add validateMediaUrl() utility to block private IPs, localhost, and
non-HTTP protocols before server-side fetches (with tests)
- URL-encode Kie taskId in poll URL to prevent injection
- Validate WaveSpeed modelId against path traversal in both route.ts
and wavespeed.ts provider
- Add 500MB Content-Length check before downloading media at all 3
fetch sites (Kie, WaveSpeed route, WaveSpeed provider)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add ~38 new image/video models (Imagen 4, FLUX.2, Flux Kontext,
Grok Imagine, Ideogram, Qwen, Runway, Hailuo, Seedance variants,
Kling older, Wan older) with schemas and endpoint routing for
dedicated APIs (Flux Kontext, Runway). Add pageUrl to all Kie
models and update ModelSearchDialog to use it.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Cover 11 new source files with 280 specs across pure function libs,
React components, and async hook logic. Key areas: easing functions,
bezier presets, speed curve warping, video encoding config,
AudioInputNode, EaseCurveNode, CubicBezierEditor, audio mixing,
audio visualization, video stitching, and speed curve application.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- 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>
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>
- Add easeCurve (340x480) to WorkflowCanvas keyboard shortcut dimensions
- Add easeCurve (340x480) to quickstart validation DEFAULT_DIMENSIONS
- Add easeCurve case to createDefaultNodeData switch (fixes exhaustive return)
- Required for NodeType union exhaustiveness after adding easeCurve type
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
- Install mediabunny@^1.28.0 for video processing
- Add audioInput and videoStitch to NodeType union
- Add AudioInputNodeData interface (audioFile, filename, duration, format)
- Add VideoStitchClip interface for filmstrip items
- Add VideoStitchNodeData interface (clips, clipOrder, outputVideo, progress, encoderSupported)
- Update WorkflowNodeData union to include new node types
- Extend HandleType to include audio and video handles
- Create src/lib/video-encoding.ts with AVC encoding config helpers
- Test buildEditSystemPrompt basic structure (all major sections present)
- Test with workflow context including node listing and connection formatting
- Test scoped selection with restSummary (node count, type breakdown, boundary connections)
- Test empty restSummary does not show SELECTED SUBSET section
- Test createChatTools returns 3 properly structured tools
- Test execute behavior for answerQuestion, createWorkflow, and editWorkflow
- 21 test cases covering prompt building and tool creation
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Test all 5 operation types: addNode, removeNode, updateNode, addEdge, removeEdge
- Test skip scenarios for missing nodes/edges with descriptive reasons
- Test batch operations with mixed applied/skipped counts
- Test narrateOperations formatting for all operation types
- 28 test cases covering happy paths and edge cases
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
- 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>
- Import extractSubgraph from subgraphExtractor
- Parse selectedNodeIds from request body
- Call extractSubgraph before building workflow context
- Pass subgraph.selectedNodes and selectedEdges to buildWorkflowContext
- Update buildEditSystemPrompt to accept optional restSummary parameter
- Add WORKFLOW CONTEXT (SELECTED SUBSET) section when scoped
- Include rest-of-workflow summary with type breakdown and boundary connections
- Add metadata placeholder note to system prompt
- Enhance error handling to return 413 for oversized payloads
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add 22 tests covering:
- Binary field stripping across all 8 node types
- Metadata placeholder generation with size/dimensions
- History and ref field removal
- Parameter preservation
- Position inclusion
- Enhanced buildWorkflowContext with full node data
- formatContextForPrompt rendering
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Implement extractSubgraph function with full selection logic
- Early return for no selection (return all nodes/edges, isScoped=false)
- Filter nodes into selected vs unselected with O(1) Set lookup
- Classify edges: fully within selection, boundary, or outside
- Build type breakdown from unselected nodes
- Identify boundary connections with direction (incoming/outgoing)
- All 8 tests passing (GREEN phase)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Create comprehensive test suite for extractSubgraph function
- Test cases cover: no selection, select all, single node, connected pairs,
subset selection, type breakdown, disconnected nodes, multi-handle scenarios
- All 8 tests failing with "Not implemented" error (RED phase)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add execute functions to all three chat tools (pass-through pattern)
so AI SDK produces proper tool-call/tool-result pairs in conversation
history, fixing AI_MissingToolResultsError on subsequent messages
- Fix stale closure in ChatPanel customFetch by using a ref for
workflowState, ensuring current canvas state is sent with each request
- Add EDITABLE NODE PROPERTIES section to system prompt so LLM uses
correct data property names (e.g. "prompt" not "text" for prompt nodes)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Create tools.ts with buildEditSystemPrompt function that constructs enhanced system prompt with workflow context and tool usage rules. Implement createChatTools function that returns three tool definitions using AI SDK v6 tool() pattern:
- answerQuestion: informational queries without workflow modification
- createWorkflow: build new workflows from scratch via quickstart
- editWorkflow: targeted edits with operation list and explanation
All tools use zod schemas for type-safe input validation. Tools follow "generate" pattern (no execute function) - LLM provides structured output that client/route handles.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Create editOperations.ts with EditOperation type union covering all 5 operation types (addNode, removeNode, updateNode, addEdge, removeEdge). Implement applyEditOperations function with batched immutable updates, proper ID generation, and validation with skip tracking. Add narrateOperations function for human-readable summaries.
Create contextBuilder.ts with WorkflowContext interface and buildWorkflowContext function that strips base64 data, history arrays, and internal state. Add formatContextForPrompt function for system prompt injection.
Both modules are pure TypeScript with no UI or API dependencies.
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>
- 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 function that generates a prompt asking the LLM to output a
WorkflowProposal JSON structure instead of a full workflow.
Key differences from buildQuickstartPrompt:
- Outputs reviewable proposal, not internal workflow state
- Focus on explaining purpose of each node
- Human-readable connection descriptions
- Simpler schema without positions or runtime data
- Includes complexity estimate and warnings
The prompt guides the LLM to create proposals that users can
review and modify before building the actual workflow.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Update API routes to fetch from nodebananapro.com
- Remove local workflow JSON files (now hosted on R2)
- Remove LFS tracking (.gitattributes)
- Remove local communityWorkflows.ts metadata (now on server)
- Add COMMUNITY_WORKFLOWS_API_URL env variable option
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>
- Change categories from product/style/composition to simple/advanced
- Add provider filter to sidebar (currently shows Gemini)
- Display provider tags on template cards
- Update tests for new category structure
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Replace model image with new model.png for the Model + Product template,
update the Style Transfer prompt to watercolor children's book style.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add template thumbnails to public/template-thumbnails/
- Add hover images for model-product and style-transfer templates
- Update TemplateCard to support hover image with opacity transition
- Replace street-scene.jpg with new-bg-model-product.png in model-product
- Replace house-lake.jpg with style-transfer-reference.png in style-transfer
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add TemplateMetadata interface with nodeCount, category, tags, previewImage
- Add TemplateCategory type (product, style, composition, community)
- Update PresetTemplate interface with category and tags fields
- Add category and tags to all 6 preset templates
- Add getTemplateMetadata() to extract node count from workflow
- Add getPresetWithMetadata() to get template with full metadata
- Update getAllPresets() to include category and tags in return type
When providers like fal.ai or Replicate expect array parameters (e.g.,
image_urls), single values from dynamicInputs are now automatically
wrapped in arrays based on the OpenAPI schema's type property.
Changes:
- Add isArray field to ModelInput type for schema detection
- Update getFalInputMapping() to return schemaArrayParams
- Update getInputMappingFromSchema() to return schemaArrayParams
- Apply array wrapping in both fal.ai and Replicate handlers
- Add tests for array wrapping behavior
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Changed DEFAULT_TTL from 10 minutes to 60 minutes
- Reduces API calls to provider model listing endpoints
- Models don't change frequently, longer TTL is appropriate
- 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>
- 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>
- Add ModelInput type for connectable inputs (image/text handles)
- Update model schema API to return both parameters and inputs
- Categorize properties: image inputs, text inputs, and parameters
- Sort inputs by required status and type
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Create /api/models/[modelId] endpoint for fetching model parameter schemas
- Add ModelParameter interface to lib/providers/types.ts
- Support both Replicate and fal.ai providers
- Filter out internal params, prioritize user-relevant ones
- 10-minute cache TTL for schema responses
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>
Implement generation via fal.ai's fal.run API:
- POST to https://fal.run/{modelId} with prompt and parameters
- Use "Key {apiKey}" auth header (works without key but rate limited)
- Extract image URL from response (handles images array, image object, or output string)
- Fetch image and convert to base64 data URL
- Return GenerationOutput with type: "image"
Note: Image inputs skipped for now (Phase 5 adds URL server)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Implement generation via Replicate's prediction API:
- POST to /predictions with model version and prompt
- Poll /predictions/{id} until succeeded/failed/canceled
- Fetch output image URL and convert to base64 data URL
- Return GenerationOutput with type: "image"
Note: Image inputs skipped for now (Phase 5 adds URL server)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Export cache utilities from provider index and add listAllModels/
searchAllModels helper functions for multi-provider operations.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add in-memory cache for provider model lists with 10-minute TTL.
Includes get/set/invalidate functions and cache key generation.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add defensive null checks before calling .map() on data.results
in both the API route and provider implementation. Returns empty
array with success:true when results is undefined, preventing
"Cannot read properties of undefined (reading 'map')" crash.
Fixes: UAT-001 from 02-01-ISSUES.md
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
fal.ai API only accepts a single category parameter, but we were
sending multiple categories. Now we fetch all active models and
filter client-side using the existing isRelevantModel() function.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Implement ProviderInterface for fal.ai model marketplace:
- Model discovery via REST API (listModels, searchModels, getModel)
- Filter to relevant categories (text-to-image, image-to-image, text-to-video, image-to-video)
- Optional API key auth (works without key but with rate limits)
- Self-registration in provider registry on import
- Stub generate() for Phase 3 implementation
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>