Name both halves of the opt-out: the data-capture-wheel attribute and the
contains(document.activeElement) gate that hover-focus exists to satisfy.
Removing either one silently breaks scroll-to-zoom.
The doc-gaps scratch file is working notes, not something end users need;
keep it local via .gitignore. Moved the reverse-engineered-behavior callout
up next to Video Outpaint since that's the node it actually concerns.
Replace the ordering-dependent onConfigure value-latching dance with a
convergent refreshFromWidgets() reconcile that is safe to call any time:
crop_state is parsed to a source-space st.pendingCrop, consumed exactly
once by the first successful initLayout, and colors + AR-lock button are
reconciled from the hidden widgets. Removes _latched* state and the
per-site replay logic; collapses the double-rAF init to a single frame
(ResizeObserver uniformly owns geometry-readiness).
Sizing contract is hardened belt-and-suspenders: add getHeight and the
--comfy-widget-min-height CSS var alongside getMinHeight, and add a
one-shot warnIfSizingIgnored() that flags (never fights) a frontend that
stops honoring the undocumented DOM-widget sizing options.
Fix two blob-URL leaks via setFrameImg/revokeIfBlob: the scrub blob
overwritten by applyFrameData's data: URL, and the final blob on removal.
Also document in V3_DOC_GAPS #2 that only the onResize width-clamp was
actually shipped-and-reverted. Full plan in VACE_OUTPAINT_HARDENING.md.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Restore the "🪐 Video Outpaint" display name and move the node to the
Wan VACE Prep/Video category
- Disconnect the canvas ResizeObserver on node removal to avoid a leak
- Update README: drop the stale "unregistered/legacy V1" notes now that
all nodes are V3, and add a brief note on the reverse-engineered,
potentially fragile V3 behaviors
Migrate the last remaining V1 node (VACEOutpaint) to the V3 io.ComfyNode
API and register it in get_node_list(). The Python change is mechanical;
the stdlib io import is aliased to _io to avoid clashing with comfy's io.
Rewrite the JS node-sizing for Nodes 2.0. The old onResize/computeSize
hack inverted the V3 sizing model and caused runaway vertical growth on
horizontal drag. Drive height via the getMinHeight option with a flex
column that lets the canvas absorb extra height. There is no working
min-width channel for DOM widgets in Nodes 2.0, so contain the controls
with CSS (overflow:hidden, wrap/scroll rows) instead of forcing a floor.
Add V3_DOC_GAPS.md documenting the V3/Nodes 2.0 gaps hit during migration.
Handle reference images via WanVaceToVideo.reference_image instead,
which properly VAE-encodes, channels-doubles, and prepends the frame
alongside the control video latent. Avoids double-prepending when
both nodes tried to handle it.
V2 checks widget.type to decide whether to render; setting only
widget.hidden = true was insufficient. Adding widget.type = "hidden"
matches the V2-compatible pattern used across the ComfyUI ecosystem.
In ComfyUI's V2 (Vue) renderer, node height is derived from the DOM
widget's rendered size, so setting dom.wrap.style.height inside onResize
creates a feedback loop: each DOM change causes V2 to fire onResize again
with a larger size[1], growing the node without bound.
The fix measures the actual node-height overhead (everything above
dom.wrap) from V2's own first onResize call, where size[1] is DOM-derived
and therefore encodes the exact overhead needed for a zero-change
equilibrium. The handler returns early without touching the DOM until this
measurement is taken, so the loop never starts. A secondary recalibration
step corrects the overhead if V2's internal chrome value shifts post-init
(observed in diagnostic data). The dom.wrap.offsetHeight === h guard is
the terminal condition for both cases.
V1 behavior is unchanged: overhead is pre-seeded with the hardcoded
CTRL_H + NODE_CHROME and no measurement or recalibration runs.
Thank you to @fullthrottler for identifying the root cause and suggesting a temporary fix.
If a workflow is loaded with the outpaint node outside the visible
viewport, ComfyUI gives the DOM widget clientWidth=0. The double-rAF
initLayout call returned false, and the old code did an early return
that skipped wireInteractions, setupResizeObserver, and the
execution_success listener entirely. The node had no event handlers
and no way to recover.
- Double-rAF no longer returns early on layout failure; interactions
and the ResizeObserver are always wired up
- ResizeObserver handles !st.initialized by running the full deferred
init sequence the first time the wrap gets a valid size
- initLayout returns false early when maxW or maxH <= 0, preventing
degenerate scale values from a partially-visible wrap
- Control video pad color is now customizable, allowing direct use with the LTX-2 outpainting lora and any other applications that require a specific color.
- Node renamed to Video Outpaint to reflect its utility beyond VACE.