Output nodes were collapsing multiple connected media inputs into one visible result, which made image/video/audio/3D connections look ignored. The node now preserves each connected media kind and presents explicit result switching with current/all download actions.
Constraint: Output node remains a result endpoint and does not transform media or create hidden downstream behavior
Rejected: Auto-merge media into one combined preview | would hide workflow structure and violate the endpoint-only product model
Confidence: high
Scope-risk: moderate
Directive: Keep output node behavior as explicit result display; route further processing through visible downstream nodes
Tested: npm test -- --run src/components/__tests__/OutputNode.test.tsx src/store/execution/__tests__/simpleNodeExecutors.test.ts
Tested: npm run build
Tested: npm run test:run
Tested: Playwright localhost smoke with no page or console errors
The accumulated fixes move the test build toward the current product shape: Popi models are the default provider surface, remote community workflow APIs are removed from the local template flow, media nodes expose accurate preview/preview-modal behavior, and Generate Video owns first-class duration, resolution, and sound defaults instead of burying those values in provider schemas.
The test suite was brought back in line with the product mode split: Popi provider mode is the default runtime, while multi-provider model search remains covered explicitly under multi mode.
Constraint: Product direction is canvas-owned workflow structure with Popi Models as the tested default provider path.
Rejected: Keep legacy community workflow API surface | user requested removing remote data paths and retaining local templates.
Rejected: Treat video duration/resolution/sound as hidden provider params | these are user-facing Generate Video controls.
Confidence: high
Scope-risk: broad
Directive: Do not reintroduce remote community template loading without a product decision on hosted templates and i18n ownership.
Tested: npm test -- --run src/store/utils/__tests__/nodeDefaults.test.ts src/store/execution/__tests__/generateVideoExecutor.test.ts src/components/__tests__/GenerateVideoNode.test.tsx src/components/__tests__/GenerationComposer.test.tsx
Tested: npm run build
Tested: npm test -- --run src/components/__tests__/ModelSearchDialog.test.tsx src/store/utils/__tests__/localStorage.test.ts
Tested: npm run test:run
Not-tested: Live provider generation calls against paid external APIs.
P0 regressions showed several workflow paths needed to stay visible and deterministic: language switching had a hardcoded composer label, Video Trim hid controls before media loaded, and media handoffs to LLM needed real canvas edges rather than implicit conversion. P1 fixes keep canvas-owned structure by adding explicit image/video LLM handles, audio composer validation, 3D output/file routing, and Chat proposal cards that require user apply before mutating nodes.
Constraint: User asked to stop new feature work and only repair P0/P1 product-fit gaps.
Rejected: Auto-convert image or video directly into text for LLM | hides workflow structure and violates the visible-node product model.
Rejected: Let Chat tool calls silently add or edit nodes | canvas structure must be previewed and explicitly applied.
Confidence: high
Scope-risk: moderate
Directive: Keep media transformations represented by visible handles, nodes, and edges; do not add silent Chat or media mutation paths without product review.
Tested: Browser automation for language switch, video input to Generate Video, prompt to Generate Video, LLM default model, Video Trim controls, and video media to LLM input.
Tested: npm run test:run -- src/components/__tests__/ConnectionDropMenu.test.tsx src/components/__tests__/GenerationComposer.test.tsx src/components/__tests__/VideoTrimNode.test.tsx src/components/__tests__/LLMGenerateNode.test.tsx src/store/execution/__tests__/llmGenerateExecutor.test.ts src/app/api/llm/__tests__/route.test.ts src/store/execution/__tests__/generateAudioExecutor.test.ts src/store/execution/__tests__/generate3dExecutor.test.ts src/store/execution/__tests__/simpleNodeExecutors.test.ts src/components/__tests__/OutputNode.test.tsx src/components/__tests__/Generate3DNode.test.tsx src/components/__tests__/ChatPanel.test.tsx src/lib/chat/tools.test.ts src/lib/chat/editOperations.test.ts src/lib/__tests__/llmModels.test.ts src/store/utils/__tests__/nodeDefaults.test.ts
Tested: npm run build
Tested: git diff --cached --check
Not-tested: npm run lint, because the existing script calls removed Next.js lint command and exits with invalid project directory /lint.
Not-tested: Live paid provider calls for LLM, audio, and 3D generation.
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>
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>
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>
- 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>
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>
Renders low-resolution JPEG thumbnails (256px, q=0.6) in nodes when
zoomed out (effective width < 300px), swapping to full resolution when
zoomed in. Thumbnails are generated eagerly and cached in memory.
- New utility: imageThumbnail.ts (offscreen canvas downscaling)
- New cache: thumbnailCache.ts (ephemeral Map-based cache)
- New hook: useAdaptiveImageSrc (zoom-aware src selector)
- Integrated into all image-bearing nodes (GenerateImage, ImageInput,
Output, Annotation, SplitGrid, VideoFrameGrab, OutputGallery, GLBViewer)
- Lightbox/modal views always use full resolution
https://claude.ai/code/session_01MvD1n4QeXutgwUpKJuDGHa
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>
Handles were inside the content div that had overflow-clip, causing them
to be clipped. Move clipping to a wrapper around just the media content
so handles remain unclipped — same pattern as GenerateImage/VideoNode.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The content div's `relative` positioning creates a stacking context that
paints over the handles. Adding zIndex: 10 matches the pattern used by
GenerateImageNode and GenerateVideoNode.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Use overflow-visible unconditionally on BaseNode outer wrapper so
handles are never clipped. Content clipping is already handled by each
node's content div (overflow-hidden/overflow-clip). Add rounded-lg to
OutputNode's content container for rounded corners on edge-to-edge media.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- 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
- Replace broad `state.edges` subscription with count-only selector scoped to this
node's target edges, preventing unnecessary re-renders from unrelated edge changes
- Merge two useEffect hooks into one with a null sentinel ref to prevent false
regeneration on mount when loading saved workflows with existing connections
- Pass isExecuting={isRunning} to BaseNode so the Run button grays out during execution
- Update test mock to include edges, regenerateNode, and isRunning
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Output Node now automatically triggers execution when a new connection is made
- Added Run button to Output Node header for manual triggering
- Updated regenerateNode to support output node type
- Prevents re-triggering on initial mount or disconnections using edge count tracking
Resolves issue where Output Node would not display data from already-generated upstream nodes unless connected before generation.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
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>
Add audio support to the Output node so GenerateAudio can pipe audio
through for playback and download. Adds audio input handle, <audio>
player, audio→output connection validation, and executeOutput audio
handling.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
## Template Explorer Enhancements
- Add rich metadata and thumbnails for community workflows
- Create communityWorkflows.ts config for workflow metadata
- Reuse TemplateCard for community workflows with tag filtering
- Search now includes workflow descriptions
## Comment Navigation System
- Add comment navigation icon in header with unviewed count badge
- Navigate between nodes with comments using prev/next controls
- Tooltips show comment preview on hover
## Output Node Improvements
- Add custom filename and createDirectory options for output saves
- Remove default React Flow styling from output nodes
- Fix video output node connections
- Preserve node height on resize
## Image Handling Fixes
- Resolve image history race condition and duplicate images on save
- Preserve image history IDs when saving to new directory
- Verify outputImageRef matches selected history index on save
- Persist useExternalImageStorage setting in workflow config
- Paste images directly into selected imageInput nodes
## Model & Generation Fixes
- Use selectedModel.modelId for Gemini generation
- Return 200 with notFound flag instead of 404 for missing files
## Other
- Add git-lfs tracking for large workflow files
- Remove deprecated example workflows
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add video and contentType fields to OutputNodeData
- Detect video content from data URL prefix or contentType hint
- Render video element with controls, loop, muted autoplay
- Support both base64 data URLs and HTTP URLs for video
- Update lightbox to show video player in fullscreen
- Update placeholder text to 'Waiting for image or video'