The old single-POST endpoint (fal.ai/api/storage/upload) returns 404.
Switched to the current fal.ai upload API: initiate via
rest.alpha.fal.ai/storage/upload/initiate, then PUT binary data to
the returned signed URL.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Delete unused generateWithFal function (replaced by generateWithFalQueue)
- Add 20MB size guard in uploadImageToFal to prevent memory spikes
- Add refresh button to ModelSearchDialog that clears model and schema
localStorage caches plus in-memory fetch cache
- Wire "Try Again" error button to also clear stale caches
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace blocking fal.run endpoint with async queue.fal.run + 1s polling
to match Replicate/WaveSpeed behavior. Upload base64 images to fal CDN
before queue submission to avoid large inline payloads. Cache schema
mappings in-memory (30min TTL) to eliminate redundant API calls per
generation. Also fix localStorage cache leaking between tests.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Derive provider capabilities from mediaType instead of hardcoding
text-to-image, so isVideoModel is correct for video generations.
Treat application/octet-stream as unhelpful content-type and fall back
to video/mp4 or image/png based on model type. Add safety net in
save-generation to override .bin extension when media type is known.
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>
Add Veo 3 and Veo 3 Fast models (text-to-video + image-to-video) with
custom API integration using Kie's /api/v1/veo/ endpoints and numeric
successFlag polling. Also document Kie model integration SOP in CLAUDE.md.
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>
- 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>
- Replace RegExp constructor with replaceAll() for @variable substitution
in promptConstructor to prevent regex injection via variable names
- Wrap annotation, prompt, and promptConstructor execution cases in
try/catch to gracefully handle errors (log + set node error, continue)
- Fix MediaBunny resource cleanup type safety with closeable type guard
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>
- 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>
Update stale test mocks from currentNodeId to currentNodeIds in
AudioInputNode, EaseCurveNode, and VideoStitchNode tests. Replace
hard 401 return for missing FAL API key with console.warn, allowing
requests to proceed without auth (rate-limited) since
generateWithFal() already handles null keys gracefully.
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>
- Add nano-banana-pro model to Kie provider with schema and image input key
- Reorder Kie generation to process dynamicInputs before fallback image
uploads, preventing double-uploads when schema-mapped connections exist
- Update test mocks for deduplicatedFetch, useProviderApiKeys, and static
handle changes across GenerateImageNode, GenerateVideoNode,
ModelParameters, and ModelSearchDialog tests
- Add test commands to CLAUDE.md
- Fix SplitGridSettingsModal grid preview count (5 → 6 options)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
handleNodeDragStop used a hardcoded 300x280 fallback for node dimensions,
causing miscalculated centers for non-standard nodes (easeCurve 340x480,
videoStitch 400x280, audioInput 300x200). Now uses the same 3-level
fallback (measured → style → defaultNodeDimensions) as createGroup.
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>
Offset segmentBaseTime by frameInterval so the first frame starts at
timestamp 0 instead of -frameInterval, which the encoder rejects.
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>
Remove 36 unsupported Kie.ai models (14 image, 22 video) and add 2 new
ones (kling-2.6/motion-control, topaz/video-upscale), bringing the total
to 22 models. Fix parameter schemas to match official Kie.ai docs:
seedream quality param, flux-2 input_urls, grok-imagine video mode/duration,
kling sound param, wan resolution param, and singular vs array image keys.
Remove dead Veo/Kontext/Runway helper functions and endpoint branching —
all remaining models use the standard createTask endpoint.
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>
- Added validation to ensure fal.ai API key is provided in requests, returning a 401 status with an appropriate error message if not configured.
- Updated related tests to reflect the new API key requirement and adjusted assertions for expected responses.
- Modified documentation to clarify that fal.ai API key is now mandatory for model access.
Merge feature/wavespeed branch, adding WaveSpeed as a new provider alongside
Kie. Fix shared integration bugs in both providers: standardize API key headers
to X-Kie-Key/X-WaveSpeed-Key, add both to handleSaveProviders, ModelParameters
headers, CostDialog filter, and ModelSearchDialog badges. Replace per-provider
icon buttons in FloatingActionBar with a single "Browse models" button.
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>
- Add videoStitch handle resolution in handleConnect batch loop (both original source and other selected nodes)
- Add batchUsed parameter to findCompatibleHandle for batch-aware handle lookup
- Add videoStitch resolution in ConnectionDropMenu batch handler
- Prevents all bulk connections from routing to the same video-0 handle
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>
- Import and register EaseCurveNode in nodeTypes
- Add easeCurve case to getNodeHandles (video in, video out)
- Add easeCurve to isValidConnection video target list
- Add easeCurve video handle mapping in handleMenuSelect
- Add amber minimap color (#f59e0b) for easeCurve nodes
- Add Ease Curve to VIDEO_TARGET_OPTIONS and VIDEO_SOURCE_OPTIONS
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>
- 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>
- 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>
- 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>
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>