Nodes that passed overflow-clip via contentClassName to BaseNode were
clipping their React Flow handles since handles are children of the
content div. Moved the clip to inner content wrappers that don't
contain handles.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When a node has media (image/video) and resize handles are visible,
double-clicking any resize handle resizes the node to match the media's
aspect ratio, preferring to grow rather than shrink. Applies to all
selected nodes for multi-selection consistency.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Removed header props from encoder-not-supported BaseNode
- Added contentClassName for full-bleed pattern
- 2 more BaseNode instances in this file still need updating
- Note: Other node edits were reverted by linter/formatter
Data URLs force Chrome to re-parse base64 on each access. With autoPlay
loop, this happens continuously and can freeze the main thread on systems
with weak GPUs that fall back to software video decoding.
Add useVideoBlobUrl hook that converts data URLs to blob URLs via
fetch->blob->createObjectURL. Returns the data URL immediately as
fallback to avoid flicker, then swaps to blob URL once ready (~50ms).
Properly revokes blob URLs on input change and unmount.
Applied to all 6 <video> elements across 5 components:
- GenerateVideoNode (outputVideo)
- VideoStitchNode (outputVideo)
- OutputNode (contentSrc, inline + lightbox)
- EaseCurveNode (outputVideo)
- VideoTrimNode (previewUrl)
Store data is unaffected — getConnectedInputs, executors, and
imageStorage all read outputVideo from the Zustand store, not from
the rendering layer.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The end slider's full-width invisible body (higher z-index) was intercepting
all pointer events, preventing the start slider from receiving input. Fix by
setting pointer-events: none on both range inputs and pointer-events: all on
only the thumb pseudo-elements. Swap z-index so the start handle is on top
when both thumbs overlap at position 0.
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>
- Dual range sliders with visual timeline scrubber for start/end times
- Inline video preview with play/pause controls (shows source or trimmed)
- Source/Trimmed toggle when output is available
- Time labels: start, duration, end (M:SS.s format)
- Trim button (disabled when no source, loading, or invalid range)
- Processing overlay with progress percentage
- Error display for processing failures
- Encoder check states (not supported / checking)
- Auto-detects video duration on connect and sets endTime