Nodes with `position: relative` on inner wrappers (e.g. image wrapper
needing it for its remove button) caused left-side handles to render
behind the wrapper due to CSS painting order. Adding z-index: 5 to
all handles ensures they always paint above node content.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Browser hit-testing against large base64 <img> elements is expensive
even when no React state updates occur. Two CSS-level fixes:
1. pointer-events: none on all .react-flow__node img — images never
need direct mouse events (parent containers handle clicks)
2. .canvas-interacting class on <html> during pan/drag disables
pointer-events on all node content via CSS descendant selector,
toggled via direct DOM access (zero React overhead)
https://claude.ai/code/session_01MvD1n4QeXutgwUpKJuDGHa
- Add video, audio, text, 3d, easeCurve entries to EDGE_COLORS in SharedEdgeGradients
- Normalize handle type suffixes (e.g. image-0 -> image) in EditableEdge color lookup
- Gate Kie provider filter behind API key check (return 400 if missing)
- Fix getMediaDimensions to check URL pathname for image extensions before defaulting to video
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
fal was unconditionally listed as available, which could surface
fal models to users without a fal API key configured.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The capabilities prop doesn't exist on ModelSearchDialogProps. Use the
correct initialCapabilityFilter="image" prop instead.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The `.react-flow__node:not(.switch-dimmed)` rule applied a transition to
every node DOM element unnecessarily. The `.switch-dimmed` rule already
handles the transition for dimming. Removing this makes undimming instant
(snappier) and eliminates a transition declaration from every node.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add beforeunload handler that prompts the user when they try to close
the tab or navigate away while there are unsaved changes.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Increase resize handle hit area from 8px to 16px (edge handles) and
from 3px to 5px (corner handles) so they're easier to grab.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The ModelSearchDialog only checked client-side localStorage keys to
determine available providers, missing providers configured via .env
server-side vars. Now the /api/models endpoint returns an
availableProviders array and the dialog merges both sources.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Instead of an inline text field, negative prompt is now a proper text
handle on the node that can receive connections from prompt/LLM nodes.
Updates the API route to merge dynamicInputs.negative_prompt into the
Veo parameters, and aligns the model schema definition accordingly.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add early validation for image-to-video models when no image is provided
- Wrap Veo generation/polling in try/catch with specific error messages
- Add 60s AbortController timeout to video download fetch
- Fix SwitchNode toggle knob not sliding (peer-checked on non-sibling)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The MAX_BASE64_SIZE check was returning the video URL with the API key
appended as a query parameter to the browser client for videos over 20MB.
Always encode to base64 instead since the buffer is already downloaded.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Import generateWithGeminiVideo and route Veo model requests (modelId
starting with 'veo-') to the video provider before falling through
to the existing image generation path.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add Veo video generation using @google/genai SDK with:
- Model ID mapping (veo-3.1/veo-3.1-fast to API model IDs)
- Async generation with polling (10s intervals, 5min timeout)
- Image-to-video support with base64 image input
- Video download and base64 encoding (URL fallback for >20MB)
- Configurable aspect ratio, duration, resolution, negative prompt, seed
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add getGeminiVideoSchema() function returning parameter schemas (aspect
ratio, duration, resolution, negative prompt, seed) and input definitions
for all 4 Veo model variants. Wire into GET handler with gemini provider
support.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add GEMINI_VIDEO_MODELS array with 4 native Veo entries (3.1 and 3.1 Fast,
text-to-video and image-to-video variants) and include them in the GET
handler alongside existing Gemini image models.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add Anthropic as a third LLM provider with Claude Opus 4.6, Sonnet 4.5,
and Haiku 4.5 models. Extend Google provider with Gemini 3.1 Pro.
Includes full API route support with multimodal content blocks,
provider settings UI, temperature clamping (Anthropic max 1.0),
header builder plumbing, and comprehensive test coverage.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Nano Banana 2 (gemini-3.1-flash-image) supports a new searchTypes API
that allows enabling web search and image search independently. This adds
a useImageSearch toggle visible only for Nano Banana 2, while keeping the
existing Google Search toggle using the legacy format for Nano Banana Pro.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Update expected model counts and sort order to account for the new
Nano Banana 2 Gemini model (3 Gemini models instead of 2).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add Nano Banana 2 entry to GEMINI_IMAGE_MODELS in model registry
- Add model to GenerateImageNode dropdown with 512px resolution option
- Extend resolution/Google Search gates to support nano-banana-2
- Replace hardcoded model name ternaries with MODEL_DISPLAY_NAMES lookup
in workflowStore, nodeDefaults, and GenerateImageNode migration code
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Map nano-banana-2 to gemini-3.1-flash-image-preview API model.
Enable resolution control and Google Search grounding for nano-banana-2.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Subscribe to dimmedNodeIds in WorkflowCanvas
- Apply switch-dimmed className in allNodes useMemo
- Exclude Switch nodes from dimming (they stay fully visible)
- Added CSS: 40% opacity with 250ms transition
- Transition applies in both directions (dimming and un-dimming)
- Dimming updates reactively when toggles or edges change
Add resolvePropertyType() helper to extract effective type/format from
OpenAPI wrapper patterns (anyOf, oneOf, allOf, $ref) used by Pydantic
and other code generators. Update isImageInput() and convertSchemaProperty()
to use resolved types instead of reading prop.type directly.
This ensures nullable fields like `{anyOf: [{type: "string"}, {type: "null"}]}`
are correctly identified as image inputs when their name matches image patterns.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Create validateWorkflowPath() utility to prevent path traversal attacks
- Reject non-absolute paths, traversal sequences, and dangerous system directories
- Apply validation to workflow and workflow-images POST/GET handlers
- Fix workflow-images ENOENT-specific catch block to match workflow route pattern
- Fix workflow mkdir failure status code from 400 to 500 (server error)
- Add comprehensive path traversal tests for both routes
- All malicious path attempts now return 400 Bad Request
- 3 tests per provider (Replicate, fal.ai): prompt included with dynamicInputs,
no duplication when dynamicInputs already has prompt, existing non-dynamicInputs path works
- Mock fetch intercepts provider API calls and captures request bodies
- Verifies the fix from the previous commit prevents "prompt is required" errors
- Include input.prompt in API request when dynamicInputs are present but don't contain a prompt key
- Use schema paramMap to resolve model-specific prompt parameter name
- Skip if dynamicInputs already provides a prompt value (no duplication)
- Remove Save As button and handler from Header (feature not ready)
- Remove Array node button from FloatingActionBar (not ready for release)
- Add error logging for failed Kie.ai tasks to aid debugging
- Add .superset/ to .gitignore
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The old preview model ID (gemini-2.5-flash-preview-image-generation) was
deprecated. Updated to the current stable ID across provider, cost
calculator, tests, and docs.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add path traversal guard to workflow-images GET handler (matching POST handler)
- Preserve stored prompt in generateAudioExecutor when connected text is null
- Reset conflicting isVideo/isAudio flags in kie.ts content-type detection
- Fix Infinity aspect ratio causing zero-height canvas in VideoStitchNode
- Check response content-type before trusting isAudioModel in fal.ts
- Move audio feeding inside inner try block in useStitchVideos for proper cleanup
- Add 30s seek timeout to frame extraction in videoProcessingExecutors
- Surface file-open errors via toast in Generate3DNode instead of failing silently
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Extract shared video-probing module (constants, helpers, probeVideoMetadata)
from useTrimVideo and useStitchVideos into src/lib/video-probing.ts
- Fix audio interleaving in useTrimVideo to feed audio before video frames,
matching useStitchVideos fix for Discord playback compatibility
- Fix blob URL leaks in VideoTrimNode and videoProcessingExecutors
- Fix nanoBananaExecutor: null coercion for non-string prompts, redact PII
in console.warn, remove duplicate dynamicInputs.prompt
- Fix generateAudioExecutor double updateNodeData on error paths
- Fix fal.ts media fetch ordering for 3D model detection
- Fix kie.ts and replicate.ts content-type detection for audio models
- Fix WorkflowCanvas connection drop menu wiring for generateAudio nodes
- Fix open-file route Windows path quoting
- Add path traversal protection to workflow-images route
- Add AudioInputNode onerror handler for metadata loading failures
- Fix useAudioPlayback uncaught play() promise rejection
- Add generateAudio case to regenerateNode in workflowStore
- Update placeholder Discord links to real invite URL
- Extract ProviderBadge into shared component in Generate3DNode
- Fix VideoTrimNode duration detection with proper cleanup and cancellation
- Fix GenerateAudioNode test fetch mock cleanup
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add generateAudio and videoFrameGrab cases to executeNode dispatcher
- Add selectedModel null guards in route.ts for all non-Gemini providers
- Fix ConnectionDropMenu wrap-around test (5->6 items after generateAudio)
- Fix audio duration edge cases (NaN guard in formatTime, truthiness check)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Resolve prompt from dynamicInputs.prompt in Gemini code path as fallback
- Return clear 400 error when prompt is provided but is not a string (corrupted data)
- Improve parallel batch error logging to include nodeId and nodeType for diagnosis
- Replace find() with for-loop to preserve index mapping between results and batch nodes
The helper returned string[] which didn't satisfy the ModelCapability[]
type in GenerationInput. Import and use the proper type.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
text-to-speech was already in RELEVANT_CATEGORIES but the test still
expected it to be filtered out. Update count and add assertion that
TTS models are mapped to text-to-audio capability.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Deduplicate the media response block (3d/video/audio/image) that was
copy-pasted across all 4 provider branches, and the mediaType-to-
capabilities ternary chain repeated 4 times.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace 3 if-statements mapping fal audio categories to a single
record lookup, making it easier to add new audio categories.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Audio models (music, TTS, speech) were misclassified as text-to-image
because capability inference only checked for 3D and video keywords.
This caused audio models to not appear in GenerateAudio's model browser
and to create nanoBanana nodes instead of generateAudio nodes.
- Add audio keyword detection to inferReplicateCapabilities()
- Add audio keyword detection to inferWaveSpeedCapabilities()
- Add audio output handling to replicate.ts provider
- Add audio output handling to wavespeed.ts provider
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add text-to-music and text-to-sound-effects to fal.ai RELEVANT_CATEGORIES
so music/SFX models (MiniMax Music, Lyria 2, Stable Audio, etc.) appear
- Map new fal categories to text-to-audio capability in mapFalCategory
- Add ElevenLabs Sound Effects v2 model to KIE_MODELS registry
- Add parameter schemas for all 4 ElevenLabs models (turbo-v2.5,
multilingual-v2, text-to-dialogue-v3, sound-effect-v2) in getKieSchema
- Add model defaults for ElevenLabs TTS and SFX models
- Remap prompt->text field for ElevenLabs models (Kie API requirement)
- Add audio response detection in generateWithKie (capability, URL
extension, and content-type checks) so audio output is correctly
returned as type "audio" instead of being misclassified as "image"
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Fal returns audio in result.audio.url for TTS models, but the media URL
extraction chain didn't check for this field. Add audio URL extraction,
isAudioModel detection, and audio-specific output handling that returns
base64-encoded audio with type "audio".
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add text-to-audio capability routing in all 4 provider blocks of
/api/generate, handle audio output type responses, and add audio
MIME type mappings and extraction logic to /api/save-generation.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add comprehensive text-to-speech (TTS) support with a new generateAudio node
that integrates with multiple AI providers (Kie.ai, Replicate, fal.ai, WaveSpeed).
Changes:
- Add 'text-to-audio' capability to ModelCapability type
- Add GenerateAudioNodeData type and CarouselAudioItem for audio history
- Create GenerateAudioNode component with audio playback and waveform visualization
- Add TTS models from Kie.ai (ElevenLabs Turbo v2.5, Multilingual v2, Eleven V3)
- Update fal.ai category mapping to support 'text-to-speech' -> 'text-to-audio'
- Add audio filter option to ModelSearchDialog
- Register generateAudio node in WorkflowCanvas with fuchsia minimap color
- Add generateAudio to ConnectionDropMenu for both audio source and text target
- Update connectedInputs to handle audio output from generateAudio nodes
- Add default node data and dimensions for generateAudio type
- Update quickstart validation to include generateAudio node type
The node supports:
- Text-to-audio generation with multiple TTS models
- Real-time audio playback with waveform visualization
- Audio history carousel for navigating generated audio
- Dynamic model parameters based on selected provider
- Integration with existing audio handle system
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- 14 tests for /api/open-file: localhost guard, input validation, path
restriction, file validation, platform commands
- 13 tests for getExtensionFromUrl: 3D extensions, media extensions,
query strings, fragments, edge cases, zip exclusion
- 3 tests for fal.ai 3D response formats: model_glb.url, model_urls.glb.url,
enhanced error log with Result keys
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add localhost-only guard to /api/open-file (rejects non-localhost with 403)
- Restrict open-file paths to user's home directory
- Change savedFilename/savedFilePath from optional to required-but-nullable
- Remove zip from KNOWN_3D_EXTENSIONS (not a valid 3D format)
- Export getExtensionFromUrl for testability
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Extract file extension from fal.ai CDN URLs instead of always
defaulting to .glb. Adds getExtensionFromUrl() helper that recognizes
glb, gltf, obj, fbx, usdz, stl, ply, and zip extensions. Also expands
MIME type mappings for additional 3D formats.
Priority chain: URL extension → content-type header → MIME fallback → .glb
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Supports Windows (explorer /select), macOS (open -R), and Linux
(xdg-open on parent dir). Modeled on existing /api/open-directory.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>