| 1 | /** |
| 2 | * DPI zoom scale service. |
| 3 | * |
| 4 | * Uses the WebView2 ZoomFactor (Go side) for reliable, layout-safe zooming. |
| 5 | * The frontend only saves the user's preference and lets the Go binding persist |
| 6 | * it to desktop-zoom.json; on next startup main.go reads the file and applies |
| 7 | * ZoomFactor to the WebView2 window options. |
| 8 | * |
| 9 | * Range: 0.50 – 2.00 (50% – 200%), step 0.05. |
| 10 | */ |
| 11 | |
| 12 | export const MIN_ZOOM = 0.5; |
| 13 | export const MAX_ZOOM = 2.0; |
| 14 | export const ZOOM_STEP = 0.05; |
| 15 | |
| 16 | export type ZoomLevel = number; // 0.5 – 2.0 |
| 17 | |
| 18 | export const DEFAULT_ZOOM: ZoomLevel = 1.0; |
| 19 | |
| 20 | const ZOOM_KEY = "reasonix-zoom-restart"; |
| 21 | |
| 22 | // ─── helpers ──────────────────────────────────────────────────────── |
| 23 | |
| 24 | /** Snap a number to the nearest valid step within [MIN, MAX]. */ |
| 25 | export function snapZoom(value: number): ZoomLevel { |
| 26 | const clamped = Math.min(MAX_ZOOM, Math.max(MIN_ZOOM, value)); |
| 27 | const steps = Math.round((clamped - MIN_ZOOM) / ZOOM_STEP); |
| 28 | return parseFloat((MIN_ZOOM + steps * ZOOM_STEP).toFixed(2)); |
| 29 | } |
| 30 | |
| 31 | /** Convert a zoom value (0.5-2.0) to a percentage integer (50-200). */ |
| 32 | export function zoomToPercent(zoom: ZoomLevel): number { |
| 33 | return Math.round(zoom * 100); |
| 34 | } |
| 35 | |
| 36 | /** Convert a percentage integer (50-200) back to a zoom value (0.5-2.0). */ |
| 37 | export function percentToZoom(pct: number): ZoomLevel { |
| 38 | return snapZoom(pct / 100); |
| 39 | } |
| 40 | |
| 41 | function readZoom(fallback: ZoomLevel): ZoomLevel { |
| 42 | const stored = typeof localStorage !== "undefined" ? localStorage.getItem(ZOOM_KEY) : null; |
| 43 | if (stored === null) return fallback; |
| 44 | const parsed = parseFloat(stored); |
| 45 | if (isNaN(parsed)) return fallback; |
| 46 | return snapZoom(parsed); |
| 47 | } |
| 48 | |
| 49 | function writeZoom(value: ZoomLevel): void { |
| 50 | try { |
| 51 | localStorage.setItem(ZOOM_KEY, String(value)); |
| 52 | } catch { |
| 53 | /* private mode / no storage */ |
| 54 | } |
| 55 | } |
| 56 | |
| 57 | // ─── public API ───────────────────────────────────────────────────── |
| 58 | |
| 59 | /** Read the saved zoom level that will be applied on next restart. */ |
| 60 | export function getRestartZoom(): ZoomLevel { |
| 61 | return readZoom(DEFAULT_ZOOM); |
| 62 | } |
| 63 | |
| 64 | /** |
| 65 | * Save a zoom factor that will be picked up by Go on next startup and |
| 66 | * applied as the WebView2 `ZoomFactor`. Takes effect after the app is |
| 67 | * restarted (no layout issues — ZoomFactor is engine-level). |
| 68 | */ |
| 69 | export function saveRestartZoom(userZoom: ZoomLevel): void { |
| 70 | writeZoom(snapZoom(userZoom)); |
| 71 | } |
| 72 | |
| 73 | /** |
| 74 | * Init: no-op for zoom (the Go-side ZoomFactor is applied at WebView2 |
| 75 | * creation time). |
| 76 | */ |
| 77 | export function initDpiScale(): void { |
| 78 | /* zoom is handled entirely by the Go side (ZoomFactor) */ |
| 79 | } |
| 80 |