AppSettings.textSize (compact|default|large) becomes uiScale: number, 0.5-4
in 0.1 steps, default 1. One shared clampUiScale (next to the
effectiveConcurrency precedent) defines "valid" for the store, the service
and the stepper: clamp to range, snap to the step grid, strip float noise,
and map NaN/Infinity to 1.
- SettingsStore.load migrates legacy files (compact/default/large ->
0.9/1/1.2, never over an explicit uiScale) and clamps ALWAYS, not just on
migration — buildPreload writes this value into every window's root
font-size, so a hand-edited settings.json (uiScale: 100) must not be able
to render the UI unusable. Verified against 100, 0.001, -3, NaN, Infinity
and float noise; migration verified against a real legacy file.
- SettingsService: applyUiScale sets the root font-size (16 * scale, removed
entirely at 1 per the delete-when-default rule). Rides the existing
settingsChanged broadcast, so open editor/manager windows follow live.
- buildPreload bakes the same font-size so the first paint is already scaled
— without it every window opens at 100% and snaps.
- Both tab-bars: the 3-button Interface row becomes the stepper. Clicking the
percentage resets to 100% (tooltip says so). i18n: compact/default/large
dropped, smaller/larger/reset added, all 7 locales.
Verified live, win7 + modern: scale 1 is pixel-identical to the pre-work
baseline (opaque interior diff exactly 0); 0.5x is a real dense mode (~2x
rows); 2x scales title bar, tabs, rows, icons and controls together with text
contained in its boxes — which is what stage 1c's 7.css bridge bought; 4x is
enormous but coherent, and the win7 title-bar controls stay on-screen at the
default window size. The known caveat stands: no minimum window size exists,
so 4x in a small window is legal but cramped.
Co-Authored-By: Claude <noreply@anthropic.com>