Files
Erik W. a62da8926d release: v2.3.0 refactor and UI/audio fixes
- bump project/runtime version markers to v2.3.0 (pyproject + TetriNode UI version)

- complete behavior-preserving refactor from monoliths into modular Python and JS architecture (tetrinode/ + js/live/)

- add parity + standard regression harnesses under qa/

- fix queue panel layout so Queue bottom stays aligned to matrix bottom regardless of Controls visibility

- fix music playback fallback when selected track is none, including modal Play behavior and runtime playback path
2026-02-08 17:14:25 -05:00

102 lines
3.3 KiB
JavaScript

import {
BLOCK,
CONTROL_GAP,
CONTROL_MIN,
CONTROL_WIDGET_PADDING,
EXTRA_VISIBLE_ROWS,
GRID_H_VISIBLE,
GRID_W,
HEADER_H,
PADDING,
PREVIEW_GRID,
TOOLBAR_H,
} from "../constants.js";
import { getHoldEnabled, getNextPieceEnabled } from "../config/store.js";
export function getLayout(node) {
const pauseBottom = null;
const minTop = HEADER_H + PADDING + TOOLBAR_H + CONTROL_GAP;
const widgetsBottom = getWidgetsBottom(node);
const widgetTop =
widgetsBottom != null ? widgetsBottom + CONTROL_WIDGET_PADDING : minTop + CONTROL_MIN;
const topY = Math.max(minTop, widgetTop);
const bottomY = Math.max(topY, node.size[1] - PADDING);
const innerH = Math.max(0, bottomY - topY);
const innerW = Math.max(0, node.size[0] - PADDING * 2);
const showHold = getHoldEnabled(node);
const showNext = getNextPieceEnabled(node);
const sideSlots = 2;
let sideW = 120;
let blockSize = BLOCK;
const effectiveRows = GRID_H_VISIBLE + EXTRA_VISIBLE_ROWS;
for (let i = 0; i < 2; i += 1) {
const boardW = Math.max(0, innerW - sideW * sideSlots - PADDING * sideSlots);
blockSize = Math.floor(Math.min(boardW / GRID_W, innerH / effectiveRows));
blockSize = Math.max(6, blockSize);
sideW = Math.max(PREVIEW_GRID * blockSize + PADDING * 2, 120);
}
const boardW = blockSize * GRID_W;
const extraPx = Math.round(blockSize * EXTRA_VISIBLE_ROWS);
const boardH = blockSize * GRID_H_VISIBLE + extraPx;
const boardX = Math.max(PADDING, Math.round((node.size[0] - boardW) / 2));
const boardY = Math.round(topY);
const sideY = boardY;
node.__tetrisLastLayout = {
boardY,
pauseBottom,
};
return {
boardX,
boardY,
boardW,
boardH,
sideY,
blockSize,
extraPx,
showHold,
showNext,
};
}
export function getWidgetsBottom(node) {
if (!node?.widgets?.length) return null;
const controlWidget = node.widgets.find((widget) => widget?.name === "control_after_generate");
if (controlWidget && node.__tetrisWidgetBottom == null) {
const index = node.widgets.indexOf(controlWidget);
const startY =
(Number.isFinite(node.widgets_start_y) && node.widgets_start_y) ||
(Number.isFinite(LiteGraph?.NODE_TITLE_HEIGHT) ? LiteGraph.NODE_TITLE_HEIGHT : HEADER_H);
const paddingTop = Number.isFinite(LiteGraph?.NODE_WIDGET_PADDING)
? LiteGraph.NODE_WIDGET_PADDING
: 4;
const rowHeight = Number.isFinite(LiteGraph?.NODE_WIDGET_HEIGHT)
? LiteGraph.NODE_WIDGET_HEIGHT
: 20;
const margin = Number.isFinite(LiteGraph?.NODE_WIDGET_MARGIN)
? LiteGraph.NODE_WIDGET_MARGIN
: 4;
return startY + paddingTop + (index + 1) * (rowHeight + margin);
}
if (Number.isFinite(node.__tetrisWidgetBottom)) {
return node.__tetrisWidgetBottom;
}
const startY =
(Number.isFinite(node.widgets_start_y) && node.widgets_start_y) ||
(Number.isFinite(LiteGraph?.NODE_TITLE_HEIGHT) ? LiteGraph.NODE_TITLE_HEIGHT : HEADER_H);
const paddingTop = Number.isFinite(LiteGraph?.NODE_WIDGET_PADDING)
? LiteGraph.NODE_WIDGET_PADDING
: 4;
const rowHeight = Number.isFinite(LiteGraph?.NODE_WIDGET_HEIGHT)
? LiteGraph.NODE_WIDGET_HEIGHT
: 20;
const margin = Number.isFinite(LiteGraph?.NODE_WIDGET_MARGIN)
? LiteGraph.NODE_WIDGET_MARGIN
: 4;
return startY + paddingTop + node.widgets.length * (rowHeight + margin);
}