cap the scale at 2x

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 <noreply@anthropic.com>
Officer-Session: de3ed4e6-f88a-429c-94e6-a760336ba83b
This commit is contained in:
2026-08-23 22:36:51 +00:00
co-authored by Claude Opus 5
parent ef74f0c2dd
commit 48cbb24c4c
2 changed files with 17 additions and 1 deletions
+4
View File
@@ -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
+13 -1
View File
@@ -27,6 +27,17 @@ const BUTTON_MASK: Record<number, number> = { 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]);