From 48cbb24c4cac17d35bbee003c021bd71e02e9ff1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Padez?= Date: Sun, 23 Aug 2026 22:36:51 +0000 Subject: [PATCH] cap the scale at 2x MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The quarter steps did not actually shrink it. In a panel around 1500x950 device pixels the fit is 2.56, and floor(2.56 x 4) / 4 is still 2.5 — the finer steps changed where the next size would land, not this one. So a ceiling instead of another nudge: 2x, whatever room the panel has. That is a taste decision rather than a limit of the method — past 2x this stops reading as a window on a desktop — and the panel header has a zoom control, so it is a default, not a verdict. Raise MAX_SCALE and the quarter steps take over again. Co-Authored-By: Claude Opus 5 Officer-Session: de3ed4e6-f88a-429c-94e6-a760336ba83b --- README.md | 4 ++++ web/MinesweeperPanel.tsx | 14 +++++++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index ca1465a..d4575cf 100644 --- a/README.md +++ b/README.md @@ -96,6 +96,10 @@ exactly 36 screen pixels, as even as at 2x, and only sub-cell pixels alternate. than halves because coarse steps overshoot — with halves this jumped 2x to 2.5x, too small to too large with nothing in between. +The scale is then CAPPED at 2x. Fitting the panel is not the same as looking right, and past 2x this stops +reading as a window on a desktop. That is a default, not a verdict — the panel header has a zoom control, +and `MAX_SCALE` is one constant. + Two things the scale has to know about that are easy to miss. `devicePixelRatio` is not the whole story: the panel header has its own CSS `zoom`, so the effective ratio is `devicePixelRatio × zoom`, read as `getBoundingClientRect().width / offsetWidth`. And the stage is fitted into the box MINUS a margin, because diff --git a/web/MinesweeperPanel.tsx b/web/MinesweeperPanel.tsx index fac1962..f64c77a 100644 --- a/web/MinesweeperPanel.tsx +++ b/web/MinesweeperPanel.tsx @@ -27,6 +27,17 @@ const BUTTON_MASK: Record = { 0: 1, 1: 2, 2: 4 }; /** Breathing room around the stage, in device pixels. Small — it is a frame, not a margin. */ const MARGIN_DEVICE_PX = 24; +/** + * The ceiling on how large the game is drawn, whatever room the panel has. + * + * A taste decision, not a limit of the method: fitting the panel is not the same as looking right, and + * past 2x this stops reading as a window on a desktop and starts reading as a wall. Raise it and the + * quarter steps below take over again. + * + * The panel header has a zoom control, so this is a default rather than a verdict. + */ +const MAX_SCALE = 2; + // Plugin panels have no localisation mechanism yet — `officerdev`'s `useTranslation`/`panelCatalogs` // (merged by `I18nBridge`) is platform-internal and not exported from the barrel, and // `scripts/i18n/scan-hardcoded.ts` explicitly does not scan `plugins/`. So these stay plain English @@ -124,7 +135,8 @@ export const MinesweeperPanel = () => { const fit = Math.min(usableW / frame.width, usableH / frame.height); // Below 1 there is nothing to floor to, so take a unit fraction: the rows that survive are then // evenly spaced instead of dropped in clumps. - const scale = fit < 1 ? 1 / Math.ceil(1 / fit) : fit < 2 ? 1 : Math.floor(fit * 4) / 4; + const snapped = fit < 1 ? 1 / Math.ceil(1 / fit) : fit < 2 ? 1 : Math.floor(fit * 4) / 4; + const scale = Math.min(snapped, MAX_SCALE); return { width: (frame.width * scale) / box.dpr, height: (frame.height * scale) / box.dpr }; }, [frame, box]);