From 29c48a9115f5b507aa8803cdf9d773102d2547e2 Mon Sep 17 00:00:00 2001 From: Ethanfel Date: Sat, 4 Jul 2026 23:15:52 +0200 Subject: [PATCH] Audio Wave JS: fix DOM-widget width-collapse (keepDomWidgetFullWidth) + explicit height addDOMWidget widgets collapse to ~half width on select/re-layout; added the portable keepDomWidgetFullWidth helper (ResizeObserver + node.size[0] reference) called after addDOMWidget, with getMinHeight and cleanup on remove. Neutral widget type (not preview). Co-Authored-By: Claude Opus 4.8 --- web/audio_wave.js | 58 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 57 insertions(+), 1 deletion(-) diff --git a/web/audio_wave.js b/web/audio_wave.js index 55f67a8..4146afa 100644 --- a/web/audio_wave.js +++ b/web/audio_wave.js @@ -21,6 +21,57 @@ function computePeaks(buf, n) { return peaks; } +// Keep a ComfyUI DOM widget at full node width (fixes the collapse-to-half-width bug on +// selection / re-layout). Portable helper; call once after addDOMWidget. +function keepDomWidgetFullWidth(node, container) { + const GRID_SEL = '[data-testid="node-widgets"], .lg-node-widgets'; + const ROW_SEL = '[data-testid="node-widget"], .lg-node-widget'; + const MAX_MARGIN = 40; + let enforcing = false, marginLogical = Infinity, gridObserver = null; + + function refFromDom() { + const grid = container.closest(GRID_SEL); + if (!grid) return 0; + let w = 0; + for (const row of Array.from(grid.querySelectorAll(ROW_SEL))) { + if (row.contains(container)) continue; + const c = row.lastElementChild; + if (c && c.clientWidth > w) w = c.clientWidth; + } + if (!w && grid.clientWidth > 0) { + const dot = grid.querySelector(`${ROW_SEL.split(",")[0]} > :first-child`); + w = grid.clientWidth - (dot?.offsetWidth ?? 0); + } + return w; + } + function refFromNodeSize(cw) { + const nodeW = node.size?.[0] ?? 0; + if (nodeW <= 0) return 0; + if (cw > 0) { const m = nodeW - cw; if (m >= 0 && m < marginLogical) marginLogical = Math.min(m, MAX_MARGIN); } + const margin = Number.isFinite(marginLogical) ? marginLogical : MAX_MARGIN / 2; + return Math.round(nodeW - margin); + } + function reference(cw) { + let w = refFromDom(); + if (!w) w = refFromNodeSize(cw); + if (!w) for (const wd of node.widgets ?? []) { const el = wd.inputEl || wd.element; if (el && el !== container && el.offsetWidth > w) w = el.offsetWidth; } + if (!w && container.parentElement) w = container.parentElement.clientWidth; + return w; + } + function enforce() { + if (enforcing) return; + if (!gridObserver) { const grid = container.closest(GRID_SEL); if (grid) { gridObserver = new ResizeObserver(enforce); gridObserver.observe(grid); } } + const cw = container.clientWidth; + const ref = reference(cw); + if (ref > 0 && Math.abs(cw - ref) > 2) { enforcing = true; container.style.width = ref + "px"; requestAnimationFrame(() => { enforcing = false; }); } + } + const ro = new ResizeObserver(enforce); + ro.observe(container); + const origOnResize = node.onResize; + node.onResize = function (size) { origOnResize?.call(this, size); enforce(); }; + return () => { ro.disconnect(); gridObserver?.disconnect(); }; +} + function setupWave(node) { const st = { duration: 0, peaks: null, audio: new Audio(), playing: false }; node._wave = st; @@ -38,7 +89,12 @@ function setupWave(node) { canvas.width = 640; canvas.height = 160; canvas.style.cssText = "width:100%;height:160px;background:#141418;border-radius:4px;cursor:pointer;"; wrap.append(bar, canvas); - node.addDOMWidget("wave", "wave", wrap, { serialize: false }); + // Neutral type (NOT "preview" — that triggers aspect-ratio sizing that collapses width), + // with an explicit height, then pin the width against the collapse-on-select bug. + node.addDOMWidget("wave", "wave", wrap, { serialize: false, getMinHeight: () => 205 }); + const cleanupWidth = keepDomWidgetFullWidth(node, wrap); + const origRemoved = node.onRemoved; + node.onRemoved = function () { try { cleanupWidth(); } catch (e) { /* ignore */ } return origRemoved?.apply(this, arguments); }; const ctx = canvas.getContext("2d"); const sjw = getW(node, "segments_json"); // machine field — hide it