- Add --handle-color-video CSS variable
- Fix tutorial overlay race conditions by reading nodes from store
directly and add keyboard accessibility to click-to-continue overlay
- Fix audio extraction in video stitching: defer silent buffer creation
until reference format is known, and only trust probed duration when
all blobs were successfully probed
- Clear saved filename/path when clearing 3D output
- Retry 429/408 transient errors during task polling
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Update all 18 node files to use the centralized HandleLabel component.
Labels now fade in/out with a 150ms opacity transition instead of
mounting/unmounting (pop-in). ~430 lines removed across 18 files.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Extracts the repeated ~10-line handle label pattern into a reusable
component with opacity-based fade transition (150ms ease-in-out)
instead of mount/unmount pop-in.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add useShowHandleLabels hook that combines node selection state with
React Flow's useConnection to show handle labels only when relevant,
reducing visual clutter on the canvas.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- OutputNode download tests: add missing `ok: true` to fetch mock
responses so downloadMedia doesn't bail at the `!response.ok` check
- workflowStore integration test: update easeCurve bounding box
expectations from 480 to 280 to match actual default dimensions
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- TutorialOverlay: move completeCurrentStep() inside setTimeout to prevent
cleanup from clearing the advance timeout, fixing stuck tutorial steps
- useStitchVideos: push silent AudioBuffer for clips with no/incompatible
audio instead of skipping, preventing audio-video timeline desync
- generate3dExecutor: clear savedFilename/savedFilePath on new generation
to prevent stale save metadata from previous runs
- pollTaskCompletion: restructure abort listener/timer cleanup to prevent
listener accumulation across polling iterations
- connectedInputs: exclude loop edges in validateWorkflowPure checks for
nanoBanana/generateVideo text inputs, matching runtime behavior
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Integrates client-side polling for long-running Kie tasks into all
generation executors alongside the existing runWithFallback support.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Resolve conflicts in 4 executor files by combining develop's
runWithFallback structure with feature branch's client-side polling.
Both capabilities are now integrated: executors use runWithFallback
for model redundancy, and pollGenerateTask for long-running Kie tasks.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
All executors that call /api/generate now check for the polling flag
and enter the pollGenerateTask loop for long-running Kie tasks.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
After the initial /api/generate response, check for polling flag and
enter pollGenerateTask loop instead of waiting on a single long request.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Shared polling utility that calls /api/generate/poll with short-lived
requests (3-8s interval with linear backoff). Handles abort signals,
network error retries, and 10-minute client-side timeout.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
New endpoint accepts POST with taskId/provider/modelId and returns
either a polling-continue response or the final media result. Each
request is short-lived (~1s) instead of holding a connection open.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The /api/generate route now submits Kie tasks and returns immediately
with polling metadata (taskId, pollProvider, etc.) instead of holding
the HTTP connection open for 2-10 minutes. Export buildMediaResponse
for reuse by the poll endpoint.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Split the monolithic generateWithKie into three reusable lifecycle functions
for client-side polling support. generateWithKie now composes them internally
for backward compatibility. Added polling fields to GenerateResponse type.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Wrap fetch and JSON parsing in try-catch to handle network errors,
add 429/5xx to retryable status codes, and implement exponential
backoff (2s → 10s) to reduce API load during long-running generations.
Caps consecutive errors at 5 before failing permanently.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The Kie API can return 404/422 or HTTP 200 with non-200 code when
polling starts before the task is fully registered. Previously these
were treated as fatal errors, causing the client to see a network error
even though the generation was still processing on Kie's side. Now
these transient responses are retried instead of aborting.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
maxDuration was 5 minutes which caused Next.js to kill the route handler
before Seedance 2.0 video generation polling could complete, resulting
in a network error on the client despite successful generation.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The Kie API expects "bytedance/seedance-2" as the model value, but we
were sending our internal ID "bytedance/seedance-2/text-to-video" which
includes the capability suffix. Strip the suffix for Seedance models
before sending to the API.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- OutputGalleryNode: add aria-label to gallery tile buttons so screen
readers can identify video tiles (images already had alt via
AdaptiveGalleryThumbnail)
- OutputGalleryNode: add nodeData.imageRefs and nodeData.videoRefs to
removeMedia dependency array so splice operates on current refs
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- OutputGalleryNode: splice imageRefs/videoRefs alongside images/videos
in removeMedia to keep arrays in lockstep
- TutorialOverlay: track and clean up actionCompleted advance timeout
via advanceTimeoutRef to prevent stale nextTutorialStep after skip
- TutorialOverlay: track and clean up populate-content nested timeouts
via populateTimeoutIds ref to prevent post-teardown state mutations
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- TutorialOverlay: track all nested setTimeout IDs and clear on cleanup
to prevent ghost animations when tutorial is skipped mid-demonstration
- FTUXReadyStep: use strict local interface requiring both callbacks
instead of optional FTUXStepProps
- FTUXModelDefaultsStep: replace hardcoded "Gemini nano-banana-pro" with
"None set" for the no-model-selected state
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- copyLoopOutput: add console.warn for unrecognized target node types
- connectedInputs: exclude loop edges from easeCurve edge lookup
- mediaStorage: trim gallery image/video refs to match actual array
lengths after item deletion
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- OutputGalleryNode: replace hardcoded .png/.mp4 download with shared
downloadMedia utility for correct MIME-based extensions
- OutputNode: wrap downloadMedia call in try/catch to prevent unhandled
rejection
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- ModelSearchDialog: show "Remove fallback" button even when no models match
- Generate3DNode: reset settingsTab to "primary" when fallback is removed
- GenerateAudioNode: same settingsTab reset fix
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- runWithFallback: use "loading" (valid NodeStatus) instead of "running"
- WorkflowCanvas: clear fallbackParameters when selecting a new fallback model
- workflowStore: add response.ok check before reading tutorial image blob
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Extract shared downloadMedia utility from OutputNode and add download
buttons to ImageInput, VideoInput, Annotation, AudioInput, GenerateImage,
GenerateVideo, GenerateAudio, and GLBViewer nodes. Refactor OutputNode to
use the shared utility.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1. Normalize and clamp loopCount at execution time (1-100 range)
- Validates imported/malformed values are finite numbers
- Falls back to default of 3 if invalid
- Prevents NaN, Infinity, or extreme values from bypassing setLoopCount validation
2. Fix resume logic for nodes inside loop bodies
- Compute loopStartLevel when startFromNodeId is in loopLevels
- On first iteration, start from that level instead of level 0
- On subsequent iterations, start from level 0 as normal
- Preserves existing copyLoopOutput skip on first iteration
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Reset fallbackParameters when saving a new fallback model provider to
prevent stale parameter values (e.g. temperature=1.5) being sent to a
provider with different valid ranges (e.g. Anthropic max=1).
Wire AbortController signal through mockTutorialExecution so clicking
Stop actually cancels the 5s delay, fetch request, and FileReader,
returning the node to idle state instead of completing silently.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Gemini image models (nano-banana, nano-banana-pro, nano-banana-2) were
not showing parameters when set as fallback models. Three root causes:
1. Schema API returned empty for Gemini image models — added
getGeminiImageSchema() with aspect ratio, resolution, and search
grounding parameters per model
2. ModelParameters had an early-return guard skipping all non-Veo Gemini
models — removed to make the component provider-agnostic
3. nanoBananaExecutor ignored parametersOverride for Gemini-specific
fields (aspectRatio, resolution, useGoogleSearch, useImageSearch) —
now reads from override when available
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Restore FTUX production logic (remove testing override)
- Fix generate3dExecutor clearOutput field and add modelId validation
- Remap startLevel index after loop partitioning in executeWorkflow
- Add missing mockTutorialExecution dep in FloatingActionBar
- Reset settingsTab to "primary" when fallbackModel is removed
- Clear orphaned fallbackParameters when removing fallback model
- Fix OutputGalleryNode removeMedia to splice single item, not all duplicates
- Add data-tutorial="save-button" to untitled save button in Header
- Move data-tutorial to layout-producing element in BaseNode
- Hydrate FTUXApiKeysStep localKeys from providerSettings
- Remove broken docs URL from ftuxStore tutorial steps
- Add error handling in loadTutorialSampleImage
- Fix mediaStorage ref array misalignment in outputGallery hydration
- Make OutputGalleryNodeData.videos optional to match usage
- Remove unused fitView destructuring from WorkflowCanvas
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Update fallback button icon and color scheme (emerald -> blue)
- Move fallback button into headerAction alongside browse button
- Add custom title prop to ModelSearchDialog for fallback context
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Adds SettingsTabBar to LLMGenerateNode with temperature and max tokens
controls for the fallback model. Updates llmGenerateExecutor to use
fallbackParameters override when running the fallback model.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Updates runOnce in all four generation executors to accept an optional
parametersOverride argument. When running the fallback model, the
executor uses fallbackParameters from node data instead of the primary
model's parameters.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Adds SettingsTabBar to all four generation nodes (image, video, audio, 3D).
When a fallback model is set, a tab bar appears to switch between primary
and fallback model parameter controls. Fallback tab renders ModelParameters
bound to nodeData.fallbackParameters.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Minimal pill-style tab bar for switching between primary and fallback
model parameter views inside the InlineParameterPanel.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Updates the runOnce signature to accept an optional parametersOverride.
Primary call always passes undefined; fallback call passes the
fallbackParameters from options. Includes tests for all three cases.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Adds fallbackParameters?: Record<string, unknown> to NanoBananaNodeData,
GenerateVideoNodeData, Generate3DNodeData, and GenerateAudioNodeData so
fallback models can have their own parameter configurations.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Introduces a shared runWithFallback helper that wraps primary model
attempts with an optional fallback. On primary failure (non-abort),
the fallback runs once and metadata is stamped on the node so the UI
can render a "Fallback used" badge. Fallback is skipped when primary
and fallback resolve to the same provider/model to avoid double-billing.
Wired through all generation executors (nanoBanana, generateVideo,
generateAudio, generate3d, llmGenerate) with accompanying tests.
JSON-compatible with Node Banana Pro: fallbackModel and __-prefixed
metadata fields match NBP's shape for clean config round-trips.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
The Kie docs have contradictory info — the parameter description says
"std"/"pro" but the example request body uses "720p"/"1080p". The API
actually accepts "720p"/"1080p" (same as Kling 2.6 Motion Control),
rejecting "std"/"pro" with "mode is not within the range of allowed
options."
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Per Kie docs, background_source accepts "input_video"/"input_image",
not "image"/"video". The previous wrong values were being sent to the
API after schema defaults pre-population, causing request rejection.
Also added background_source to getKieModelDefaults() for consistency.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
When a model schema loads, enum dropdowns showed "Default" but never
wrote the actual default value into node.data.parameters. This caused
the Kie API to receive missing parameter values, resulting in errors
like "mode is not within the range of allowed options."
Now a useEffect watches for schema changes and writes any schema-defined
defaults into parameters when the corresponding key is undefined.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
The dynamic input upload logic only matched data:image URLs, so video
data (data:video/mp4) was passed as raw base64 instead of being
uploaded to get a hosted URL. This caused "File type not supported"
errors for models like Kling 3.0 Motion Control that accept video.
- Rename uploadImageToKie → uploadMediaToKie (keep alias for compat)
- Rename detectImageType → detectMediaType with video magic bytes and
fallback to declared MIME type for non-image content
- Broaden data URL checks from data:image to data: in generateWithKie
- Set uploadPath to videos/audio/images based on detected MIME type
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Add Seedance 2.0 and 2.0 Fast with text-to-video and image-to-video
variants. Both support audio generation, web search, and 4-15 second
duration. Image input uses first_frame_url (singular string).
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>