From 49c6ee77a6afa266f00e5b898c992dd7b2523289 Mon Sep 17 00:00:00 2001 From: Ethanfel Date: Fri, 26 Jun 2026 09:24:28 +0200 Subject: [PATCH] Stabilize accumulator preview layout --- web/accumulator_preview.js | 51 +++++++++++++------------------------- 1 file changed, 17 insertions(+), 34 deletions(-) diff --git a/web/accumulator_preview.js b/web/accumulator_preview.js index 99cc777..b615a70 100644 --- a/web/accumulator_preview.js +++ b/web/accumulator_preview.js @@ -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;