Stabilize accumulator preview layout

This commit is contained in:
2026-06-26 09:24:28 +02:00
parent 4f18fcda86
commit 49c6ee77a6
+17 -34
View File
@@ -6,13 +6,14 @@ const NODE_NAME = "SxCPAccumulatorPreview";
const STYLE_ID = "sxcp-accumulator-preview-styles";
const DEBUG_STORAGE_KEY = "sxcpAccumulatorPreviewDebug";
const MIN_CELL_W = 180;
const MIN_CELL_W = 150;
const MAX_CELL_W = 190;
const DEFAULT_GRID_H = 360;
const GAP = 6;
const PAD = 4;
const TOOLBAR_H = 28;
const CAPTION_H = 20;
const EMPTY_GRID_H = 84;
const MAX_GRID_H = 640;
const MIN_W = 560;
const MARGIN = 10;
@@ -225,13 +226,14 @@ function injectStyles() {
flex:1 1 auto; min-height:0; box-sizing:border-box; }
.sxap-grid.sxap-dragover { outline:2px dashed #6cf; outline-offset:-2px; }
.sxap-empty { width:100%; padding:12px; text-align:center; font-size:12px; opacity:0.65; box-sizing:border-box; }
.sxap-cell { position:relative; width:var(--sxap-cell-w, 240px); border:2px solid transparent;
.sxap-cell { position:relative; width:var(--sxap-cell-w, 170px); border:2px solid transparent;
border-radius:4px; overflow:hidden; background:#222; box-sizing:border-box;
transition:border-color .1s, background .1s; }
.sxap-cell:hover { border-color:#555; }
.sxap-cell.sxap-selected { border-color:#6cf; }
.sxap-cell.sxap-drop { border-color:#fc6; border-style:dashed; }
.sxap-thumb { width:100%; object-fit:contain; display:block; cursor:grab; background:#111; }
.sxap-thumb { width:100%; height:var(--sxap-thumb-h, 170px); object-fit:contain; display:block;
cursor:grab; background:#111; }
.sxap-thumb:active { cursor:grabbing; }
.sxap-badge { position:absolute; top:2px; left:2px; font-size:10px; background:rgba(0,0,0,0.65);
color:#fff; padding:0 4px; border-radius:3px; pointer-events:none; }
@@ -255,38 +257,21 @@ function injectStyles() {
document.head.appendChild(style);
}
function entryAspect(entry) {
const shape = Array.isArray(entry?.shape) ? entry.shape : [];
const height = Number(shape[0] || 0);
const width = Number(shape[1] || 0);
if (height > 0 && width > 0) return height / width;
return 1;
}
function layoutMetrics(node) {
const width = Math.max(node.size?.[0] || MIN_W, MIN_W);
const inner = Math.max(MIN_CELL_W, width - 2 * MARGIN - 2 * PAD);
const entries = imageEntries(node);
const count = entries.length;
const perRow = count <= 1 ? 1 : Math.max(1, Math.floor((inner + GAP) / (MIN_CELL_W + GAP)));
const cellW = Math.max(MIN_CELL_W, Math.floor((inner - GAP * (perRow - 1)) / perRow));
let gridH = EMPTY_GRID_H;
if (count > 0) {
gridH = 2 * PAD;
for (let start = 0; start < count; start += perRow) {
const row = entries.slice(start, start + perRow);
const rowH = Math.max(...row.map((entry) => Math.round(cellW * entryAspect(entry)) + CAPTION_H));
gridH += rowH;
if (start + perRow < count) gridH += GAP;
}
}
return {cellW, gridH};
const perRow = Math.max(1, Math.floor((inner + GAP) / (MIN_CELL_W + GAP)));
const rawCellW = Math.floor((inner - GAP * (perRow - 1)) / perRow);
const cellW = Math.min(MAX_CELL_W, Math.max(MIN_CELL_W, rawCellW));
const thumbH = Math.round(cellW * 1.1);
return {cellW, thumbH, gridH: DEFAULT_GRID_H};
}
function recomputeSize(node) {
const {cellW, gridH} = layoutMetrics(node);
const {cellW, thumbH, gridH} = layoutMetrics(node);
node._sxapCellW = cellW;
node._sxapWidgetH = 2 * MARGIN + TOOLBAR_H + 6 + Math.min(gridH, MAX_GRID_H);
node._sxapThumbH = thumbH;
node._sxapWidgetH = 2 * MARGIN + TOOLBAR_H + 6 + Math.max(gridH, EMPTY_GRID_H);
}
function syncWidgetWidth(node) {
@@ -298,11 +283,9 @@ function resizeToContent(node) {
const targetH = node.computeSize?.()[1] || node._sxapWidgetH || 140;
const width = node.size?.[0] || MIN_W;
const currentH = node.size?.[1] || 0;
const previousAutoH = node._sxapAutoHeight || 0;
const userSizedTaller = previousAutoH > 0 && currentH > previousAutoH + 6 && currentH > targetH;
const nextH = userSizedTaller ? currentH : targetH;
const nextH = currentH > 0 ? Math.max(currentH, targetH) : targetH;
node._sxapAutoHeight = nextH;
node.setSize?.([width, nextH]);
if (Math.abs(currentH - nextH) > 1) node.setSize?.([width, nextH]);
syncWidgetWidth(node);
node.setDirtyCanvas?.(true, true);
}
@@ -351,6 +334,7 @@ function renderCell(node, entry, imageParams, displayIndex) {
cell.title = entryTitle(entry);
const cellW = node._sxapCellW || MIN_CELL_W;
cell.style.setProperty("--sxap-cell-w", `${cellW}px`);
cell.style.setProperty("--sxap-thumb-h", `${node._sxapThumbH || Math.round(cellW * 1.1)}px`);
cell.ondragover = (event) => {
if (!node._sxapDragEntry) return;
@@ -379,7 +363,6 @@ function renderCell(node, entry, imageParams, displayIndex) {
const thumb = document.createElement("img");
thumb.className = "sxap-thumb";
thumb.style.height = `${Math.max(48, Math.round(cellW * entryAspect(entry)))}px`;
if (imageParams) {
const url = imageUrl(imageParams);
thumb.src = url;