Scale stage 2: continuous uiScale setting + the (-) 100% (+) stepper
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>
This commit is contained in:
+25
-3
@@ -100,8 +100,13 @@ export interface AppSettings {
|
||||
/** Visible optional file-list columns (both panels; Name always shows). */
|
||||
columns: ColumnKey[];
|
||||
speedUnit: "MBps" | "Mbps";
|
||||
/** Size of the panel interface: file list, path bar, header controls. */
|
||||
textSize: "compact" | "default" | "large";
|
||||
/**
|
||||
* Interface scale factor, 0.5–4 in 0.1 steps (shown as 50%–400%). Works by
|
||||
* setting the root font-size — every size in the UI is rem, so one lever
|
||||
* scales BOTH skins whole. Replaced the 3-tier `textSize`
|
||||
* (compact|default|large); SettingsStore.load migrates old files.
|
||||
*/
|
||||
uiScale: number;
|
||||
/** Run transfer jobs one at a time, or several in parallel. */
|
||||
transferMode: "single" | "multi";
|
||||
/** How many jobs run at once when transferMode is "multi". */
|
||||
@@ -127,7 +132,7 @@ export const DEFAULT_SETTINGS: AppSettings = {
|
||||
iconColors: "multi",
|
||||
columns: ["size", "mtime"],
|
||||
speedUnit: "MBps",
|
||||
textSize: "default",
|
||||
uiScale: 1,
|
||||
transferMode: "single",
|
||||
transferConcurrency: 3,
|
||||
autoClearCompleted: true,
|
||||
@@ -136,6 +141,23 @@ export const DEFAULT_SETTINGS: AppSettings = {
|
||||
// autoRefresh: true,
|
||||
};
|
||||
|
||||
export const UI_SCALE_MIN = 0.5;
|
||||
export const UI_SCALE_MAX = 4;
|
||||
export const UI_SCALE_STEP = 0.1;
|
||||
|
||||
/**
|
||||
* Clamp + snap a scale factor to the legal range and step grid. Shared by the
|
||||
* store (a hand-edited settings.json must not be able to render the UI
|
||||
* unusable), the settings service and the stepper — one definition of "valid",
|
||||
* same as effectiveConcurrency below.
|
||||
*/
|
||||
export function clampUiScale(value: number): number {
|
||||
if (!Number.isFinite(value)) return 1;
|
||||
const clamped = Math.min(UI_SCALE_MAX, Math.max(UI_SCALE_MIN, value));
|
||||
// snap to the step grid; toFixed strips float noise (0.7000000000000001 -> 0.7)
|
||||
return Number((Math.round(clamped / UI_SCALE_STEP) * UI_SCALE_STEP).toFixed(2));
|
||||
}
|
||||
|
||||
export function effectiveConcurrency(settings: AppSettings): number {
|
||||
if (settings.transferMode === "single") return 1;
|
||||
return Math.max(1, Math.min(8, Math.round(settings.transferConcurrency) || 1));
|
||||
|
||||
Reference in New Issue
Block a user