Restructure README following a center-aligned hero style with badges,
feature tables, provider list, tech stack badges, and community links.
Replace screenshot with updated workflow canvas image.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add 14 tests covering:
- Optional inputs with no data are skipped
- Skip propagation cascades downstream (ANY merge policy)
- Shared nodes are not skipped
- Backward compatibility without isOptional
- Skipped status cleared after workflow completes
- Multi-level cascade
Also add skippedNodeIds to WorkflowCanvas test mock.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add node-skipped CSS class (opacity 0.35) for skip dimming
- Expose skippedNodeIds in Zustand store for reactive CSS updates
- Apply node-skipped class in WorkflowCanvas alongside switch-dimmed
- Clear skippedNodeIds on workflow stop, reset, and completion
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Shows a toggle in the FloatingNodeHeader for imageInput, audioInput,
and prompt nodes. When toggled on, the button turns amber and shows
"Optional"; when off, it shows "Required" in the default style.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- ImageInputNode: dashed border + "Optional" text when empty, badge when loaded
- AudioInputNode: same pattern as ImageInputNode
- PromptNode: updated placeholder text when isOptional is set
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When an optional input node has no data, it and all downstream nodes
with a skipped source are silently skipped during execution. Uses
ANY merge policy — if any source is skipped, the downstream node
is also skipped. Skipped status is reset to idle after workflow
completes or errors.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add `isOptional?: boolean` to ImageInputNodeData, AudioInputNodeData,
and PromptNodeData. Add "skipped" to NodeStatus union type to support
skip propagation in the execution engine.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Error overlay persisted on nodes when cycling through history to
previous successful generations. Now resets status/error on carousel
navigation in image, video, and audio generation nodes.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Saves the base directory to localStorage when creating a project and
prefills it next time the "New Project" modal opens, reducing repetitive
path entry for users who store projects under the same parent directory.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Allow marking groups as NBP inputs for workflow export. Adds isNbpInput
flag to NodeGroup, a toggle row in the group context menu with checkmark
indicator, and a dashed border on marked group backgrounds.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move fan container from inside the Background row to a child of the
menu div itself, positioned at top-0 left-0. Dots offset by -12px
(half their size) so the fan radiates exactly from the corner.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace inline horizontal controls with a vertical dropdown menu that
appears above the three-dot button. Menu has icon+label rows for
Background (color picker), Lock/Unlock, and Delete. Color picker fan
anchors from top-left of the menu. Three-dot button is always visible.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Bump button hit areas from w-5/h-5 to w-6/h-6, SVG icons from w-3/h-3
to w-3.5/h-3.5, and three-dot circles from 3px to 4px.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Consolidate group controls (color picker, lock/unlock, delete) from the
floating top-right div into a three-dot menu within the zoom-scaled title
container. Controls now scale with zoom like the title label, and are
toggled via a vertical dots button next to the group name.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
BaseNode: the settings expand branch now clamps the computed height to
minHeight, matching the collapse and ResizeObserver branches.
connectedInputs: resolveTextSourcesThroughRouters now recurses through
switch nodes (same as routers) so upstream text sources are not missed.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The left handle was rendered before the image wrapper div in DOM order.
Since the image wrapper has position: relative (needed for its remove
button), it painted over the handle. Moving both handles to render
after visual content ensures they paint last (on top).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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>
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>
Use the Lucide split icon (rotated 90deg) for the auto-route button
to better convey the splitting action. Also improve settings reactivity
by reading from Zustand store directly and reparsing atomically on
setting changes.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The pre-subtraction in loadWorkflow was overridden by React Flow's
dimension reconciliation. Instead, correct at the source: in BaseNode's
expand timeout, read _settingsPanelHeight from node data and only add
the delta (measured - saved) so reloaded nodes don't double-count.
Also clear _settingsPanelHeight on collapse to avoid stale values.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Store measured settings panel height in node data (_settingsPanelHeight).
On workflow load, subtract it from persisted height so BaseNode's expand
effect re-adds the real panel height from a clean baseline instead of
double-counting.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Variables from upstream Prompt nodes weren't discovered when a Router sat
between the Prompt and PromptConstructor. Added resolveTextSourcesThroughRouters()
helper that recursively traverses router nodes to find actual text sources,
used in both the component and executor.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The default content div used overflow-hidden which clipped handles positioned
at left: -7px / right: -7px. Nodes that need clipping (ImageInput, Annotation,
VideoTrim) already set their own contentClassName.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
contain: paint clips rendering at the element boundary like overflow:
hidden, cutting off connection handles. Keep layout and style
containment for performance without the clipping side effect.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move removePending to .finally() so it runs on both resolve and reject,
preventing stale rejected promises from accumulating in the pending map.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The Split button was permanently disabled because sourceImage was only
set during execution, not when an edge was connected. Added a reactive
effect that watches for connected images and updates sourceImage.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When a router node had multiple output edges to the same downstream node
(e.g., passing both text and image), only the first edge type received
data. The shared _visited set prevented the router from being traversed
a second time, causing subsequent edge types to get empty results.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>