Dragging a node taller only ever widened its stage. The frontend arranges widgets in one pass: if (w.computeSize) -> fixed height, kept OUT of the split else if (w.computeLayoutSize) -> joins distributeSpace(freeSpace, ...) else -> one standard widget row It is an else-if, so a widget declaring both is pinned by computeSize and its computeLayoutSize is never called. Every stage, player and filmstrip here derived that fixed height from the node's WIDTH, so the extra height became dead space underneath. Compare was fixed by hand; the other six had the same defect. Adds js/shared/panel_layout.mjs with one fillNodeHeight() that deletes computeSize and declares a floor with no ceiling - distributeSpace reads a missing maxSize as Infinity, so the panel takes whatever is left. Applied to Compare, Save Video, Load Video, Frame Chooser, Load Image + Pad, Image Crop+Rotate+Pad, and the shared transform panel that Video Crop+Rotate+Pad mounts. Removing computeSize also removed where those nodes got their opening height, so Save Video, Load Video and Load Image + Pad now state a default; a saved size still wins through onConfigure. Measured in the browser: all seven grow 1:1 with the node and clamp at their own floor. The panel audit now enforces it - a DOM panel is either a stage that calls fillNodeHeight and never assigns computeSize, or is listed as fixed by design - so a new panel cannot quietly reintroduce this.
34 lines
1.7 KiB
JavaScript
34 lines
1.7 KiB
JavaScript
// How a DOM panel claims its share of a node's height. No DOM and no
|
|
// ComfyUI imports in here, so it stays testable under node:test.
|
|
//
|
|
// The frontend arranges a node's widgets in one pass
|
|
// (LGraphNode._arrangeWidgets):
|
|
//
|
|
// if (w.computeSize) -> fixed height, kept OUT of the split
|
|
// else if (w.computeLayoutSize) -> joins distributeSpace(freeSpace, ...)
|
|
// else -> one standard widget row
|
|
//
|
|
// It is an else-if, so a widget declaring BOTH is pinned by computeSize and
|
|
// its computeLayoutSize is never called. Every stage, player and filmstrip in
|
|
// this pack derived that fixed height from the node's WIDTH, which is why
|
|
// dragging a node taller only added dead space underneath: the panel had
|
|
// already been given a height and excluded from the leftover-space split.
|
|
//
|
|
// distributeSpace reads a missing maxSize as Infinity, so declaring a floor
|
|
// with no ceiling means "take whatever is left" - which is exactly "fill the
|
|
// node". minWidth/minHeight accept a number or a function, for panels whose
|
|
// floor depends on state (the frame chooser is shorter until it has frames).
|
|
export function fillNodeHeight(widget, { minWidth = 0, minHeight = 0, minNodeSize } = {}) {
|
|
if (!widget) return widget;
|
|
const floor = (value) => {
|
|
const resolved = Number(typeof value === "function" ? value() : value);
|
|
return Number.isFinite(resolved) ? Math.max(0, resolved) : 0;
|
|
};
|
|
// Deleted, not overwritten: any own computeSize would win the else-if above.
|
|
delete widget.computeSize;
|
|
widget.computeLayoutSize = () => ({ minWidth: floor(minWidth), minHeight: floor(minHeight) });
|
|
widget.options ??= {};
|
|
if (minNodeSize) widget.options.minNodeSize = minNodeSize;
|
|
return widget;
|
|
}
|