Use overflow-visible instead of overflow-clip for fullBleed nodes so
handle circles and labels extend beyond the node boundary. Add
overflow-hidden rounded-lg to image/video content containers to
preserve rounded corners on media.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace dashed-border selector assertions with text-based assertions
since full-bleed nodes use a background fill instead of a dashed
border for their empty state.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Video fills the entire node with no padding or chrome. Carousel
controls overlay at the bottom with backdrop blur (matching
GenerateImageNode pattern). Removed inline parameters since they
are displayed in the floating panel. Hidden ModelParameters kept
for schema-loading side effect (dynamic handles). All handles use
z-index to stay visible over content.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Textarea fills the entire node with no visible border. Available
variables display as a fixed semi-transparent footer pinned at the
bottom. Unresolved variables warning overlays at the top. Autocomplete
dropdown remains functional. Handles use z-index to stay visible.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Textarea fills the entire node with no visible border or chrome.
Variable name badge overlays at the bottom-left. Handles use
z-index to stay visible over the content surface.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Handles were painted behind the full-bleed image content since they
share the same stacking context. Adding zIndex: 10 to all handles
and their labels ensures they render on top of the image.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The header width calculation was preferring node.style.width (set at
creation time, often stale) over node.measured.width (actual DOM size).
This caused headers to be narrower than nodes after image generation
or manual resizing. Now uses measured → style → default fallback chain,
matching the existing pattern used elsewhere in the file.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace gap-2 with justify-between so header controls (comment,
expand, run) stay flush-right against the node boundary on all widths.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Use node.style.width (number or string), node.measured.width, or
default dimensions — in that priority order. Headers now match the
actual node width after user resize.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- FloatingNodeHeader: Controls (comment, expand, run) now sit flush-right
against the node edge with tighter spacing.
- ControlPanel: Remove "Model" label. Model name sits directly under the
settings title, separated by a horizontal divider.
- Add new-tab link icon next to provider name that opens model details page.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- ControlPanel: Replace provider/model dropdowns with model name text +
Browse button. Fixed width, no horizontal scroll.
- GenerateImageNode: Carousel controls now overlay on image bottom with
semi-transparent dark background for legibility.
- BaseNode: Add fullBleed prop for borderless mode. Image IS the node -
no visible container, border, or padding.
- GenerateImageNode uses fullBleed mode. Empty state uses subtle fill
instead of dashed border.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Remove header props (title, customTitle, comment, etc.) from BaseNode calls
- Apply full-bleed content pattern with contentClassName
- Replace dashed border empty states with subtle fills (bg-neutral-900/40)
- Move floating UI elements to absolute positioning
Part of batch update for all 21 remaining node files.
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
- Removed aspect ratio, resolution, and search toggles from node body
- Removed ModelParameters component from node body
- Parameters will be shown in ControlPanel (Plan 02 integration)
- Image preview now has room to fill node in full-bleed pattern
Task 1 partial progress: GenerateImageNode parameters removed, 24 more nodes need header props removed and full-bleed content applied
- ImageInputNode: remove header props, use contentClassName with overflow-clip, full-bleed image with object-cover
- OutputNode: same full-bleed pattern, floating download button overlays content
- nodeDimensions: add calculateNodeSizeForFullBleed for nodes without header chrome
- Empty states use subtle bg-neutral-900/40 fill with centered icons (no dashed borders)
Task 1 in progress - 23 more nodes need same pattern applied
- Created ControlPanel component rendering on right side of viewport
- Fixed position with pointer-events-none wrapper, auto scrolling
- Filters to single selected configurable node (7 types)
- Placeholder controls for each node type (Task 2 will implement)
- Integrated into WorkflowCanvas after ChatPanel
- Panel appears/disappears based on node selection
- Import ViewportPortal from @xyflow/react
- Add NODE_TITLES mapping for all node types
- Create getNodeTitle helper (checks selectedModel.displayName for generate nodes)
- Create getNodeTitlePrefix helper (returns ProviderBadge for nodes with provider)
- Wire up handleCustomTitleChange and handleCommentChange callbacks
- Render FloatingNodeHeader for each node inside ViewportPortal
- Headers positioned above nodes, width matches node width
- onExpand/onRun callbacks not wired yet (will be added in Plan 03)
- Comment editing and title editing fully functional
The server-reported availableProviders were only set on fresh API calls.
When models were served from localStorage cache, the provider filter
icons reverted to only showing Gemini/fal.ai. Now availableProviders
is stored in and restored from the model cache.
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>
- Create shared getModelPageUrl utility in src/utils/providerUrls.ts
- Add external-link icon to GenerateImageNode header (before Browse button)
- Add external-link icon to GenerateVideoNode header (before Browse button)
- Add external-link icon to Generate3DNode header (before Browse button)
- Icon only appears when model is selected and provider has a page URL
- Gemini models show no link (returns null); Replicate/fal/Kie/WaveSpeed show links
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Gemini and fal.ai always shown (always-available providers)
- Replicate, Kie.ai, WaveSpeed icons only shown when API key is configured
- Reset provider filter to "all" if selected provider becomes unavailable
- Fix WaveSpeed active color from orange to purple (matching its badge color)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Remove Annotate button assertion from basic rendering test
- Remove Annotate click test case
- Add All Nodes Menu test suite (4 tests: render, open, select, close)
- Update Browse Models tests for "All models" text label
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Remove Annotate shortcut button from action bar
- Add AllNodesMenu dropdown with all 22 node types in 6 categories
- Replace magnifying glass Browse models icon with "All models" text button
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
ConnectionDropMenu tests expected stale last items and item counts after
Router, Switch, and Conditional Switch were added. WorkflowCanvas tests
were missing dimmedNodeIds from the mock store state.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
After disconnecting an image edge, the target node's inputImages data
was not cleared. On regeneration, useStoredFallback would pick up the
stale images and send them to the API, causing results to still resemble
the old reference. Now removeEdge and onEdgesChange call
clearStaleInputImages to reset inputImages when no image-source edges
remain on the target node.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The else-if chain in blockedTypes detection meant that when a switch/
conditionalSwitch node was itself dimmed but had an enabled output,
the finalDimmed.has(source) check was skipped entirely. Changed the
dimmed-source check from else-if to a standalone if so it runs for
all source types, including switch and conditionalSwitch.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Instead of setting inputSchema via useEffect (which runs after render),
set it directly in handleModelChange and handleBrowseModelSelect. This
ensures dynamic handles (including image-0 for I2V models) are rendered
in the same update cycle as model selection, preventing connection issues
where the image handle wouldn't accept connections.
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>
Veo models (veo-3.1, veo-3.1-fast) showed no user-configurable
parameters in the UI. Follows the established pattern from
GenerateImageNode where Gemini model parameters are hardcoded in the
component rather than fetched via the schema API. Adds aspect ratio,
duration, resolution, negative prompt, and seed controls.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The dimming algorithm processed potentially-dimmed nodes in arbitrary
order, causing nodes downstream of a rescued convergence point to stay
dimmed. Fix by topologically sorting the candidate set (upstream-first)
and checking finalDimmed instead of potentiallyDimmed, so rescued nodes
properly propagate their active status downstream.
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>
Adds an evaluationPaused flag that lets users reset evaluation state
and un-dim all downstream paths. The flag auto-resets on workflow
execution or manual rule edits.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The useMemo for incomingText depended on edges (from useEdges), which
doesn't change when upstream node data changes. On re-runs, the memo
returned stale text and the useEffect overwrote correct execution
results. Replace with a Zustand store selector using
getConnectedInputsPure that recomputes when upstream node data changes.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The customTitle and comment fields are typed as string | undefined in
ConditionalSwitchNodeData. The null values were masked by an 'as' cast
but caused strict type errors. These fields are optional and don't need
explicit defaults.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The customTitle and comment fields are typed as string | undefined, not
string | null. Remove them from the quickstart default data to fix the
TypeScript build error.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The inline defaultDimensions record in the keyboard shortcut handler was
missing the conditionalSwitch entry, causing a TypeScript build error.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The createDefaultNodeData function in validation.ts had cases for router
and switch but was missing conditionalSwitch, despite it being listed in
VALID_NODE_TYPES and DEFAULT_DIMENSIONS.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add guard in handleDelete and hide the delete button when only one
switch remains, matching ConditionalSwitchNode's existing behavior.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The same 20-line block for evaluating rules and updating node data before
executing a ConditionalSwitch appeared in both executeWorkflow and
executeSelectedNodes. Extract to evaluateAndExecuteConditionalSwitch helper.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Identical rule evaluation logic existed in ConditionalSwitchNode.tsx and
workflowStore.ts. Extract to src/store/utils/ruleEvaluation.ts and
import from both locations.
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>
ConditionalSwitch should act as a gate/trigger, not a data router.
Active outputs now trigger downstream execution without forwarding
the upstream text through the connection.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>