react-display-scale-engine 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,498 @@
1
+ // src/displayScaleBaseline.ts
2
+ var STORAGE_KEY = "ep-display-scale-baseline";
3
+ function isBrowser() {
4
+ return typeof window !== "undefined" && typeof sessionStorage !== "undefined";
5
+ }
6
+ function readDisplayScaleBaseline() {
7
+ if (!isBrowser()) return null;
8
+ try {
9
+ const raw = sessionStorage.getItem(STORAGE_KEY);
10
+ if (!raw) return null;
11
+ const parsed = JSON.parse(raw);
12
+ if (typeof parsed.devicePixelRatio !== "number" || typeof parsed.browserZoom !== "number" || typeof parsed.capturedAt !== "string") {
13
+ return null;
14
+ }
15
+ return parsed;
16
+ } catch {
17
+ return null;
18
+ }
19
+ }
20
+ function writeDisplayScaleBaseline(baseline) {
21
+ if (!isBrowser()) return;
22
+ try {
23
+ sessionStorage.setItem(STORAGE_KEY, JSON.stringify(baseline));
24
+ } catch {
25
+ }
26
+ }
27
+ function clearDisplayScaleBaseline() {
28
+ if (!isBrowser()) return;
29
+ try {
30
+ sessionStorage.removeItem(STORAGE_KEY);
31
+ } catch {
32
+ }
33
+ }
34
+
35
+ // src/displayScaleWindows.ts
36
+ var WINDOWS_OS_SCALE_TIERS = [1, 1.25, 1.5, 1.75, 2, 2.25, 2.5, 3];
37
+ function snapToWindowsOsScale(scale) {
38
+ if (!Number.isFinite(scale) || scale <= 0) return 1;
39
+ let closest = WINDOWS_OS_SCALE_TIERS[0];
40
+ let minDiff = Number.POSITIVE_INFINITY;
41
+ for (const tier of WINDOWS_OS_SCALE_TIERS) {
42
+ const diff = Math.abs(tier - scale);
43
+ if (diff < minDiff) {
44
+ minDiff = diff;
45
+ closest = tier;
46
+ }
47
+ }
48
+ return minDiff <= 0.1 ? closest : scale;
49
+ }
50
+ function shouldApplyOsScaleCompensation() {
51
+ if (typeof window === "undefined") return false;
52
+ const isWindows = /Windows/i.test(window.navigator.userAgent);
53
+ const isCoarsePointer = window.matchMedia("(pointer: coarse)").matches;
54
+ const isCompactViewport = window.innerWidth < 600;
55
+ return isWindows && !isCoarsePointer && !isCompactViewport;
56
+ }
57
+
58
+ // src/detectDisplayScale.ts
59
+ var DEFAULT_SNAPSHOT = {
60
+ browserZoom: 1,
61
+ browserZoomPercent: 100,
62
+ osDisplayScale: 1,
63
+ osDisplayScalePercent: 100,
64
+ devicePixelRatio: 1,
65
+ effectiveScale: 1,
66
+ viewport: { width: 0, height: 0 },
67
+ screen: { width: 0, height: 0, availWidth: 0, availHeight: 0 },
68
+ method: "unavailable",
69
+ confidence: "low",
70
+ calibration: "unavailable",
71
+ measuredAt: (/* @__PURE__ */ new Date(0)).toISOString()
72
+ };
73
+ function roundScale(value, decimals = 3) {
74
+ const factor = 10 ** decimals;
75
+ return Math.round(value * factor) / factor;
76
+ }
77
+ function toPercent(scale) {
78
+ return Math.round(scale * 100);
79
+ }
80
+ function readViewportMetrics() {
81
+ const devicePixelRatio = window.devicePixelRatio || 1;
82
+ return {
83
+ devicePixelRatio,
84
+ effectiveScale: devicePixelRatio,
85
+ viewport: {
86
+ width: window.innerWidth,
87
+ height: window.innerHeight
88
+ },
89
+ screen: {
90
+ width: window.screen.width,
91
+ height: window.screen.height,
92
+ availWidth: window.screen.availWidth,
93
+ availHeight: window.screen.availHeight
94
+ }
95
+ };
96
+ }
97
+ function estimateBrowserZoom(devicePixelRatio, baseline) {
98
+ const visualViewportScale = window.visualViewport?.scale;
99
+ if (visualViewportScale != null && Number.isFinite(visualViewportScale) && visualViewportScale > 0 && Math.abs(visualViewportScale - 1) > 0.01) {
100
+ return {
101
+ browserZoom: roundScale(visualViewportScale),
102
+ method: "visual-viewport",
103
+ confidence: "high"
104
+ };
105
+ }
106
+ if (baseline && baseline.devicePixelRatio > 0) {
107
+ const fromBaseline = devicePixelRatio / baseline.devicePixelRatio * baseline.browserZoom;
108
+ if (Number.isFinite(fromBaseline) && fromBaseline > 0) {
109
+ return {
110
+ browserZoom: roundScale(fromBaseline),
111
+ method: "baseline-ratio",
112
+ confidence: "medium"
113
+ };
114
+ }
115
+ }
116
+ if (devicePixelRatio > 0) {
117
+ return {
118
+ browserZoom: 1,
119
+ method: "device-pixel-ratio",
120
+ confidence: "low"
121
+ };
122
+ }
123
+ return {
124
+ browserZoom: 1,
125
+ method: "unavailable",
126
+ confidence: "low"
127
+ };
128
+ }
129
+ function ensureBaseline(devicePixelRatio, browserZoom, autoBaseline) {
130
+ const existing = readDisplayScaleBaseline();
131
+ if (existing) return existing;
132
+ if (!autoBaseline) return null;
133
+ const baseline = {
134
+ devicePixelRatio,
135
+ browserZoom,
136
+ capturedAt: (/* @__PURE__ */ new Date()).toISOString(),
137
+ isExplicit: false
138
+ };
139
+ writeDisplayScaleBaseline(baseline);
140
+ return baseline;
141
+ }
142
+ function resolveOsDisplayScale(devicePixelRatio, browserZoom) {
143
+ const viewportScale = window.visualViewport?.scale;
144
+ const browserZoomNeutral = viewportScale == null || !Number.isFinite(viewportScale) || Math.abs(viewportScale - 1) < 0.02;
145
+ if (browserZoomNeutral && shouldApplyOsScaleCompensation()) {
146
+ const snappedDpr = snapToWindowsOsScale(devicePixelRatio);
147
+ if (snappedDpr > 1.05 && Math.abs(devicePixelRatio - snappedDpr) < 0.12) {
148
+ return {
149
+ osDisplayScale: snappedDpr,
150
+ method: "device-pixel-ratio",
151
+ confidence: "high"
152
+ };
153
+ }
154
+ }
155
+ return {
156
+ osDisplayScale: roundScale(devicePixelRatio / browserZoom),
157
+ method: "baseline-ratio",
158
+ confidence: "medium"
159
+ };
160
+ }
161
+ function detectDisplayScale(options) {
162
+ if (typeof window === "undefined") {
163
+ return DEFAULT_SNAPSHOT;
164
+ }
165
+ const autoBaseline = options?.autoBaseline ?? true;
166
+ const metrics = readViewportMetrics();
167
+ const visualViewportScale = window.visualViewport?.scale;
168
+ const seedBrowserZoom = visualViewportScale != null && Number.isFinite(visualViewportScale) && visualViewportScale > 0 ? roundScale(visualViewportScale) : 1;
169
+ const baseline = ensureBaseline(metrics.devicePixelRatio, seedBrowserZoom, autoBaseline);
170
+ const zoomEstimate = estimateBrowserZoom(metrics.devicePixelRatio, baseline);
171
+ const browserZoom = Math.max(zoomEstimate.browserZoom, 0.1);
172
+ const osEstimate = resolveOsDisplayScale(metrics.devicePixelRatio, browserZoom);
173
+ return {
174
+ ...metrics,
175
+ browserZoom,
176
+ browserZoomPercent: toPercent(browserZoom),
177
+ osDisplayScale: osEstimate.osDisplayScale,
178
+ osDisplayScalePercent: toPercent(osEstimate.osDisplayScale),
179
+ method: osEstimate.confidence === "high" ? osEstimate.method : zoomEstimate.method,
180
+ confidence: osEstimate.confidence === "high" ? osEstimate.confidence : zoomEstimate.confidence,
181
+ calibration: baseline?.isExplicit ? "explicit" : baseline ? "automatic" : "unavailable",
182
+ measuredAt: (/* @__PURE__ */ new Date()).toISOString()
183
+ };
184
+ }
185
+ function recalibrateDisplayScale(assumedBrowserZoom = 1) {
186
+ if (typeof window === "undefined") {
187
+ return {
188
+ devicePixelRatio: 1,
189
+ browserZoom: assumedBrowserZoom,
190
+ capturedAt: (/* @__PURE__ */ new Date()).toISOString(),
191
+ isExplicit: true
192
+ };
193
+ }
194
+ const baseline = {
195
+ devicePixelRatio: window.devicePixelRatio || 1,
196
+ browserZoom: assumedBrowserZoom,
197
+ capturedAt: (/* @__PURE__ */ new Date()).toISOString(),
198
+ isExplicit: true
199
+ };
200
+ writeDisplayScaleBaseline(baseline);
201
+ window.dispatchEvent(new Event("ep-display-scale-calibrated"));
202
+ return baseline;
203
+ }
204
+ function resetDisplayScaleEngine() {
205
+ clearDisplayScaleBaseline();
206
+ if (typeof window !== "undefined") {
207
+ window.dispatchEvent(new Event("ep-display-scale-calibrated"));
208
+ }
209
+ }
210
+
211
+ // src/displayScaleCompensation.ts
212
+ var DEFAULT_DISPLAY_SCALE_COMPENSATION_CONFIG = {
213
+ enabled: true,
214
+ windowsOnly: true,
215
+ minOsScaleToCompensate: 1.05,
216
+ targetOsScale: 1,
217
+ snapOsScale: true,
218
+ requireExplicitCalibration: false
219
+ };
220
+ var NO_COMPENSATION = {
221
+ active: false,
222
+ zoom: 1,
223
+ widthPercent: 100,
224
+ heightPercent: 100,
225
+ osDisplayScale: 1,
226
+ targetOsScalePercent: 100
227
+ };
228
+ function supportsCssZoom() {
229
+ if (typeof CSS === "undefined" || typeof CSS.supports !== "function") return false;
230
+ return CSS.supports("zoom", "1");
231
+ }
232
+ function computeDisplayScaleCompensation(snapshot, options) {
233
+ const config = { ...DEFAULT_DISPLAY_SCALE_COMPENSATION_CONFIG, ...options };
234
+ if (!config.enabled) return NO_COMPENSATION;
235
+ if (config.windowsOnly && !shouldApplyOsScaleCompensation()) return NO_COMPENSATION;
236
+ if (config.requireExplicitCalibration && snapshot.calibration !== "explicit") {
237
+ return NO_COMPENSATION;
238
+ }
239
+ const osScale = config.snapOsScale ? snapToWindowsOsScale(snapshot.osDisplayScale) : snapshot.osDisplayScale;
240
+ if (osScale <= config.minOsScaleToCompensate) {
241
+ return {
242
+ ...NO_COMPENSATION,
243
+ osDisplayScale: osScale
244
+ };
245
+ }
246
+ const zoom = config.targetOsScale / osScale;
247
+ const extentPercent = 100 / zoom;
248
+ return {
249
+ active: true,
250
+ zoom,
251
+ widthPercent: extentPercent,
252
+ heightPercent: extentPercent,
253
+ osDisplayScale: osScale,
254
+ targetOsScalePercent: Math.round(config.targetOsScale * 100)
255
+ };
256
+ }
257
+ function primeDisplayScaleCompensation(options) {
258
+ if (typeof document === "undefined") return NO_COMPENSATION;
259
+ const snapshot = detectDisplayScale({ autoBaseline: true });
260
+ const compensation = computeDisplayScaleCompensation(snapshot, options);
261
+ syncDisplayScaleCompensationMetadata(compensation, void 0, snapshot);
262
+ if (!compensation.active) {
263
+ const html = document.documentElement;
264
+ html.dataset.displayScaleEngine = shouldApplyOsScaleCompensation() ? "inactive" : "skipped";
265
+ html.dataset.displayScaleOs = String(snapshot.osDisplayScale);
266
+ html.dataset.displayScaleBrowserZoom = String(snapshot.browserZoom);
267
+ html.dataset.displayScaleMethod = snapshot.method;
268
+ }
269
+ return compensation;
270
+ }
271
+ function readDisplayScaleViewportSize() {
272
+ if (typeof window === "undefined") {
273
+ return { width: 0, height: 0 };
274
+ }
275
+ const viewport = window.visualViewport;
276
+ const width = viewport?.width ?? window.innerWidth;
277
+ const height = viewport?.height ?? window.innerHeight;
278
+ return {
279
+ width: Math.max(0, Math.round(width)),
280
+ height: Math.max(0, Math.round(height))
281
+ };
282
+ }
283
+ function measureDisplayScaleLayoutExtents(zoom) {
284
+ const safeZoom = Number.isFinite(zoom) && zoom > 0 ? zoom : 1;
285
+ const { width, height } = readDisplayScaleViewportSize();
286
+ return {
287
+ layoutWidthPx: Math.round(width / safeZoom),
288
+ layoutHeightPx: Math.round(height / safeZoom)
289
+ };
290
+ }
291
+ function applyDisplayScaleCompensationStyles(element, compensation, extents) {
292
+ if (!compensation.active) {
293
+ element.style.removeProperty("zoom");
294
+ element.style.removeProperty("transform");
295
+ element.style.removeProperty("transform-origin");
296
+ element.style.width = "";
297
+ element.style.height = "";
298
+ element.style.maxWidth = "";
299
+ element.style.maxHeight = "";
300
+ return;
301
+ }
302
+ const layout = extents ?? measureDisplayScaleLayoutExtents(compensation.zoom);
303
+ if (supportsCssZoom()) {
304
+ element.style.zoom = String(compensation.zoom);
305
+ element.style.removeProperty("transform");
306
+ element.style.removeProperty("transform-origin");
307
+ } else {
308
+ element.style.removeProperty("zoom");
309
+ element.style.transform = `scale(${compensation.zoom})`;
310
+ element.style.transformOrigin = "top left";
311
+ }
312
+ element.style.width = `${layout.layoutWidthPx}px`;
313
+ element.style.height = `${layout.layoutHeightPx}px`;
314
+ element.style.maxWidth = `${layout.layoutWidthPx}px`;
315
+ element.style.maxHeight = `${layout.layoutHeightPx}px`;
316
+ }
317
+ function clearDisplayScaleCompensationStyles(element) {
318
+ element.style.removeProperty("zoom");
319
+ element.style.removeProperty("transform");
320
+ element.style.removeProperty("transform-origin");
321
+ element.style.width = "";
322
+ element.style.height = "";
323
+ element.style.maxWidth = "";
324
+ element.style.maxHeight = "";
325
+ delete element.dataset.displayScaleCompensated;
326
+ delete element.dataset.osDisplayScale;
327
+ }
328
+ function syncDisplayScaleCompensationMetadata(compensation, extents, snapshot) {
329
+ if (typeof document === "undefined") return;
330
+ const html = document.documentElement;
331
+ if (!compensation.active) {
332
+ html.style.removeProperty("--display-scale-zoom");
333
+ html.style.removeProperty("--display-scale-layout-width");
334
+ html.style.removeProperty("--display-scale-layout-height");
335
+ delete html.dataset.displayScaleCompensated;
336
+ delete html.dataset.displayScaleEngine;
337
+ delete html.dataset.displayScaleOs;
338
+ delete html.dataset.displayScaleBrowserZoom;
339
+ delete html.dataset.displayScaleMethod;
340
+ delete html.dataset.osDisplayScale;
341
+ return;
342
+ }
343
+ const layout = extents ?? measureDisplayScaleLayoutExtents(compensation.zoom);
344
+ html.style.setProperty("--display-scale-zoom", String(compensation.zoom));
345
+ html.style.setProperty("--display-scale-layout-width", `${layout.layoutWidthPx}px`);
346
+ html.style.setProperty("--display-scale-layout-height", `${layout.layoutHeightPx}px`);
347
+ html.dataset.displayScaleCompensated = "true";
348
+ html.dataset.displayScaleEngine = "active";
349
+ html.dataset.displayScaleOs = String(compensation.osDisplayScale);
350
+ html.dataset.osDisplayScale = String(compensation.osDisplayScale);
351
+ if (snapshot) {
352
+ html.dataset.displayScaleBrowserZoom = String(snapshot.browserZoom);
353
+ html.dataset.displayScaleMethod = snapshot.method;
354
+ }
355
+ }
356
+ function applyDisplayScaleCompensationToDocument(compensation) {
357
+ syncDisplayScaleCompensationMetadata(compensation);
358
+ }
359
+ function clearDisplayScaleCompensationFromDocument() {
360
+ if (typeof document === "undefined") return;
361
+ const html = document.documentElement;
362
+ clearDisplayScaleCompensationStyles(html);
363
+ syncDisplayScaleCompensationMetadata({ ...NO_COMPENSATION, active: false });
364
+ }
365
+
366
+ // src/evaluateDisplayScale.ts
367
+ var DEFAULT_DISPLAY_SCALE_THRESHOLDS = {
368
+ browserZoomMin: 0.9,
369
+ browserZoomMax: 1.1,
370
+ osDisplayScaleMax: 2,
371
+ minViewportWidth: 0
372
+ };
373
+ function evaluateDisplayScale(snapshot, thresholds) {
374
+ const resolved = { ...DEFAULT_DISPLAY_SCALE_THRESHOLDS, ...thresholds };
375
+ const issues = [];
376
+ if (snapshot.browserZoom > resolved.browserZoomMax) {
377
+ issues.push("browser-zoomed-in");
378
+ } else if (snapshot.browserZoom < resolved.browserZoomMin) {
379
+ issues.push("browser-zoomed-out");
380
+ }
381
+ if (snapshot.osDisplayScale > resolved.osDisplayScaleMax) {
382
+ issues.push("high-os-scale");
383
+ }
384
+ if (resolved.minViewportWidth > 0 && snapshot.viewport.width > 0 && snapshot.viewport.width < resolved.minViewportWidth) {
385
+ issues.push("viewport-too-narrow");
386
+ }
387
+ return {
388
+ snapshot,
389
+ issues,
390
+ isAcceptable: issues.length === 0
391
+ };
392
+ }
393
+
394
+ // src/displayScaleStore.ts
395
+ var DEFAULT_SERVER_SNAPSHOT = {
396
+ browserZoom: 1,
397
+ browserZoomPercent: 100,
398
+ osDisplayScale: 1,
399
+ osDisplayScalePercent: 100,
400
+ devicePixelRatio: 1,
401
+ effectiveScale: 1,
402
+ viewport: { width: 0, height: 0 },
403
+ screen: { width: 0, height: 0, availWidth: 0, availHeight: 0 },
404
+ method: "unavailable",
405
+ confidence: "low",
406
+ calibration: "unavailable",
407
+ measuredAt: (/* @__PURE__ */ new Date(0)).toISOString()
408
+ };
409
+ function snapshotSignature(snapshot) {
410
+ const viewportWidth = Math.round(snapshot.viewport.width / 32);
411
+ const viewportHeight = Math.round(snapshot.viewport.height / 32);
412
+ return [
413
+ snapshot.browserZoom,
414
+ snapshot.osDisplayScale,
415
+ snapshot.devicePixelRatio,
416
+ snapshot.calibration,
417
+ viewportWidth,
418
+ viewportHeight
419
+ ].join("|");
420
+ }
421
+ var cachedSnapshot = null;
422
+ var cachedSignature = "";
423
+ var cachedOptionsKey = "";
424
+ function optionsKey(options) {
425
+ return options?.autoBaseline === false ? "no-baseline" : "baseline";
426
+ }
427
+ function getCachedDisplayScaleSnapshot() {
428
+ return cachedSnapshot;
429
+ }
430
+ function getDisplayScaleSnapshot(options) {
431
+ if (typeof window === "undefined") {
432
+ return DEFAULT_SERVER_SNAPSHOT;
433
+ }
434
+ const next = detectDisplayScale(options);
435
+ const signature = snapshotSignature(next);
436
+ const key = optionsKey(options);
437
+ if (cachedSnapshot && cachedSignature === signature && cachedOptionsKey === key) {
438
+ return cachedSnapshot;
439
+ }
440
+ cachedOptionsKey = key;
441
+ cachedSignature = signature;
442
+ cachedSnapshot = next;
443
+ return cachedSnapshot;
444
+ }
445
+ function hasDisplayScaleSnapshotChanged(previous, options) {
446
+ const next = detectDisplayScale(options);
447
+ return snapshotSignature(next) !== snapshotSignature(previous);
448
+ }
449
+ function commitDisplayScaleSnapshot(snapshot, options) {
450
+ cachedOptionsKey = optionsKey(options);
451
+ cachedSignature = snapshotSignature(snapshot);
452
+ cachedSnapshot = snapshot;
453
+ return cachedSnapshot;
454
+ }
455
+ function getServerDisplayScaleSnapshot() {
456
+ return DEFAULT_SERVER_SNAPSHOT;
457
+ }
458
+
459
+ // src/subscribeDisplayScale.ts
460
+ function debounce(fn, waitMs) {
461
+ let timer;
462
+ return ((...args) => {
463
+ if (timer) clearTimeout(timer);
464
+ timer = setTimeout(() => {
465
+ timer = void 0;
466
+ fn(...args);
467
+ }, waitMs);
468
+ });
469
+ }
470
+ function subscribeDisplayScale(listener, options) {
471
+ if (typeof window === "undefined") {
472
+ return () => void 0;
473
+ }
474
+ const debounceMs = options?.debounceMs ?? 100;
475
+ const notify = debounce(() => {
476
+ const previous = getCachedDisplayScaleSnapshot() ?? getDisplayScaleSnapshot(options);
477
+ if (!hasDisplayScaleSnapshotChanged(previous, options)) {
478
+ return;
479
+ }
480
+ commitDisplayScaleSnapshot(detectDisplayScale(options), options);
481
+ listener();
482
+ }, debounceMs);
483
+ const onResize = () => notify();
484
+ window.addEventListener("resize", onResize);
485
+ window.visualViewport?.addEventListener("resize", onResize);
486
+ window.addEventListener("pageshow", onResize);
487
+ window.addEventListener("ep-display-scale-calibrated", onResize);
488
+ return () => {
489
+ window.removeEventListener("resize", onResize);
490
+ window.visualViewport?.removeEventListener("resize", onResize);
491
+ window.removeEventListener("pageshow", onResize);
492
+ window.removeEventListener("ep-display-scale-calibrated", onResize);
493
+ };
494
+ }
495
+
496
+ export { DEFAULT_DISPLAY_SCALE_COMPENSATION_CONFIG, DEFAULT_DISPLAY_SCALE_THRESHOLDS, applyDisplayScaleCompensationStyles, applyDisplayScaleCompensationToDocument, clearDisplayScaleBaseline, clearDisplayScaleCompensationFromDocument, clearDisplayScaleCompensationStyles, computeDisplayScaleCompensation, detectDisplayScale, evaluateDisplayScale, getDisplayScaleSnapshot, getServerDisplayScaleSnapshot, measureDisplayScaleLayoutExtents, primeDisplayScaleCompensation, readDisplayScaleBaseline, readDisplayScaleViewportSize, recalibrateDisplayScale, resetDisplayScaleEngine, shouldApplyOsScaleCompensation, snapToWindowsOsScale, subscribeDisplayScale, supportsCssZoom, syncDisplayScaleCompensationMetadata, writeDisplayScaleBaseline };
497
+ //# sourceMappingURL=chunk-VWJNHWMD.js.map
498
+ //# sourceMappingURL=chunk-VWJNHWMD.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/displayScaleBaseline.ts","../src/displayScaleWindows.ts","../src/detectDisplayScale.ts","../src/displayScaleCompensation.ts","../src/evaluateDisplayScale.ts","../src/displayScaleStore.ts","../src/subscribeDisplayScale.ts"],"names":[],"mappings":"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","file":"chunk-VWJNHWMD.js","sourcesContent":["import type { DisplayScaleBaseline } from \"./types\";\n\nconst STORAGE_KEY = \"ep-display-scale-baseline\";\n\nfunction isBrowser(): boolean {\n return typeof window !== \"undefined\" && typeof sessionStorage !== \"undefined\";\n}\n\nexport function readDisplayScaleBaseline(): DisplayScaleBaseline | null {\n if (!isBrowser()) return null;\n\n try {\n const raw = sessionStorage.getItem(STORAGE_KEY);\n if (!raw) return null;\n\n const parsed = JSON.parse(raw) as DisplayScaleBaseline;\n if (\n typeof parsed.devicePixelRatio !== \"number\" ||\n typeof parsed.browserZoom !== \"number\" ||\n typeof parsed.capturedAt !== \"string\"\n ) {\n return null;\n }\n\n return parsed;\n } catch {\n return null;\n }\n}\n\nexport function writeDisplayScaleBaseline(baseline: DisplayScaleBaseline): void {\n if (!isBrowser()) return;\n\n try {\n sessionStorage.setItem(STORAGE_KEY, JSON.stringify(baseline));\n } catch {\n // Ignore quota / private-mode failures.\n }\n}\n\nexport function clearDisplayScaleBaseline(): void {\n if (!isBrowser()) return;\n\n try {\n sessionStorage.removeItem(STORAGE_KEY);\n } catch {\n // Ignore.\n }\n}\n","/** Windows display tiers (100%, 125%, 150%, …). */\nexport const WINDOWS_OS_SCALE_TIERS = [1, 1.25, 1.5, 1.75, 2, 2.25, 2.5, 3] as const;\n\nexport function snapToWindowsOsScale(scale: number): number {\n if (!Number.isFinite(scale) || scale <= 0) return 1;\n\n let closest: number = WINDOWS_OS_SCALE_TIERS[0];\n let minDiff = Number.POSITIVE_INFINITY;\n\n for (const tier of WINDOWS_OS_SCALE_TIERS) {\n const diff = Math.abs(tier - scale);\n if (diff < minDiff) {\n minDiff = diff;\n closest = tier;\n }\n }\n\n return minDiff <= 0.1 ? closest : scale;\n}\n\n/** True when OS scale compensation should run in this environment. */\nexport function shouldApplyOsScaleCompensation(): boolean {\n if (typeof window === \"undefined\") return false;\n\n const isWindows = /Windows/i.test(window.navigator.userAgent);\n const isCoarsePointer = window.matchMedia(\"(pointer: coarse)\").matches;\n const isCompactViewport = window.innerWidth < 600;\n\n return isWindows && !isCoarsePointer && !isCompactViewport;\n}\n","import {\n clearDisplayScaleBaseline,\n readDisplayScaleBaseline,\n writeDisplayScaleBaseline,\n} from \"./displayScaleBaseline\";\nimport { shouldApplyOsScaleCompensation, snapToWindowsOsScale } from \"./displayScaleWindows\";\nimport type {\n DetectDisplayScaleOptions,\n DisplayScaleBaseline,\n DisplayScaleConfidence,\n DisplayScaleDetectionMethod,\n DisplayScaleSnapshot,\n} from \"./types\";\n\nconst DEFAULT_SNAPSHOT: DisplayScaleSnapshot = {\n browserZoom: 1,\n browserZoomPercent: 100,\n osDisplayScale: 1,\n osDisplayScalePercent: 100,\n devicePixelRatio: 1,\n effectiveScale: 1,\n viewport: { width: 0, height: 0 },\n screen: { width: 0, height: 0, availWidth: 0, availHeight: 0 },\n method: \"unavailable\",\n confidence: \"low\",\n calibration: \"unavailable\",\n measuredAt: new Date(0).toISOString(),\n};\n\nfunction roundScale(value: number, decimals = 3): number {\n const factor = 10 ** decimals;\n return Math.round(value * factor) / factor;\n}\n\nfunction toPercent(scale: number): number {\n return Math.round(scale * 100);\n}\n\nfunction readViewportMetrics(): Pick<\n DisplayScaleSnapshot,\n \"viewport\" | \"screen\" | \"devicePixelRatio\" | \"effectiveScale\"\n> {\n const devicePixelRatio = window.devicePixelRatio || 1;\n\n return {\n devicePixelRatio,\n effectiveScale: devicePixelRatio,\n viewport: {\n width: window.innerWidth,\n height: window.innerHeight,\n },\n screen: {\n width: window.screen.width,\n height: window.screen.height,\n availWidth: window.screen.availWidth,\n availHeight: window.screen.availHeight,\n },\n };\n}\n\ntype BrowserZoomEstimate = {\n browserZoom: number;\n method: DisplayScaleDetectionMethod;\n confidence: DisplayScaleConfidence;\n};\n\nfunction estimateBrowserZoom(\n devicePixelRatio: number,\n baseline: DisplayScaleBaseline | null,\n): BrowserZoomEstimate {\n const visualViewportScale = window.visualViewport?.scale;\n\n if (\n visualViewportScale != null &&\n Number.isFinite(visualViewportScale) &&\n visualViewportScale > 0 &&\n Math.abs(visualViewportScale - 1) > 0.01\n ) {\n return {\n browserZoom: roundScale(visualViewportScale),\n method: \"visual-viewport\",\n confidence: \"high\",\n };\n }\n\n if (baseline && baseline.devicePixelRatio > 0) {\n const fromBaseline = (devicePixelRatio / baseline.devicePixelRatio) * baseline.browserZoom;\n\n if (Number.isFinite(fromBaseline) && fromBaseline > 0) {\n return {\n browserZoom: roundScale(fromBaseline),\n method: \"baseline-ratio\",\n confidence: \"medium\",\n };\n }\n }\n\n if (devicePixelRatio > 0) {\n return {\n browserZoom: 1,\n method: \"device-pixel-ratio\",\n confidence: \"low\",\n };\n }\n\n return {\n browserZoom: 1,\n method: \"unavailable\",\n confidence: \"low\",\n };\n}\n\nfunction ensureBaseline(\n devicePixelRatio: number,\n browserZoom: number,\n autoBaseline: boolean,\n): DisplayScaleBaseline | null {\n const existing = readDisplayScaleBaseline();\n if (existing) return existing;\n if (!autoBaseline) return null;\n\n const baseline: DisplayScaleBaseline = {\n devicePixelRatio,\n browserZoom,\n capturedAt: new Date().toISOString(),\n isExplicit: false,\n };\n writeDisplayScaleBaseline(baseline);\n return baseline;\n}\n\ntype OsScaleEstimate = {\n osDisplayScale: number;\n method: DisplayScaleDetectionMethod;\n confidence: DisplayScaleConfidence;\n};\n\n/**\n * Windows desktop at 100% browser zoom: `devicePixelRatio` tracks the OS display tier.\n * Prefer this over baseline math, which can collapse OS scale to 1 on a fresh deploy origin.\n */\nfunction resolveOsDisplayScale(devicePixelRatio: number, browserZoom: number): OsScaleEstimate {\n const viewportScale = window.visualViewport?.scale;\n const browserZoomNeutral =\n viewportScale == null || !Number.isFinite(viewportScale) || Math.abs(viewportScale - 1) < 0.02;\n\n if (browserZoomNeutral && shouldApplyOsScaleCompensation()) {\n const snappedDpr = snapToWindowsOsScale(devicePixelRatio);\n if (snappedDpr > 1.05 && Math.abs(devicePixelRatio - snappedDpr) < 0.12) {\n return {\n osDisplayScale: snappedDpr,\n method: \"device-pixel-ratio\",\n confidence: \"high\",\n };\n }\n }\n\n return {\n osDisplayScale: roundScale(devicePixelRatio / browserZoom),\n method: \"baseline-ratio\",\n confidence: \"medium\",\n };\n}\n\n/**\n * Measure current browser zoom and OS display scale.\n *\n * Browser zoom is tracked relative to a session baseline (first visit assumes 100%).\n * Call `recalibrateDisplayScale()` when the user resets zoom to 100%.\n */\nexport function detectDisplayScale(options?: DetectDisplayScaleOptions): DisplayScaleSnapshot {\n if (typeof window === \"undefined\") {\n return DEFAULT_SNAPSHOT;\n }\n\n const autoBaseline = options?.autoBaseline ?? true;\n const metrics = readViewportMetrics();\n const visualViewportScale = window.visualViewport?.scale;\n const seedBrowserZoom =\n visualViewportScale != null && Number.isFinite(visualViewportScale) && visualViewportScale > 0\n ? roundScale(visualViewportScale)\n : 1;\n\n const baseline = ensureBaseline(metrics.devicePixelRatio, seedBrowserZoom, autoBaseline);\n const zoomEstimate = estimateBrowserZoom(metrics.devicePixelRatio, baseline);\n const browserZoom = Math.max(zoomEstimate.browserZoom, 0.1);\n const osEstimate = resolveOsDisplayScale(metrics.devicePixelRatio, browserZoom);\n\n return {\n ...metrics,\n browserZoom,\n browserZoomPercent: toPercent(browserZoom),\n osDisplayScale: osEstimate.osDisplayScale,\n osDisplayScalePercent: toPercent(osEstimate.osDisplayScale),\n method: osEstimate.confidence === \"high\" ? osEstimate.method : zoomEstimate.method,\n confidence: osEstimate.confidence === \"high\" ? osEstimate.confidence : zoomEstimate.confidence,\n calibration: baseline?.isExplicit ? \"explicit\" : baseline ? \"automatic\" : \"unavailable\",\n measuredAt: new Date().toISOString(),\n };\n}\n\n/**\n * Reset the session baseline — use when browser zoom is known to be 100%.\n *\n * @param assumedBrowserZoom Browser zoom at calibration time (default 1).\n */\nexport function recalibrateDisplayScale(assumedBrowserZoom = 1): DisplayScaleBaseline {\n if (typeof window === \"undefined\") {\n return {\n devicePixelRatio: 1,\n browserZoom: assumedBrowserZoom,\n capturedAt: new Date().toISOString(),\n isExplicit: true,\n };\n }\n\n const baseline: DisplayScaleBaseline = {\n devicePixelRatio: window.devicePixelRatio || 1,\n browserZoom: assumedBrowserZoom,\n capturedAt: new Date().toISOString(),\n isExplicit: true,\n };\n writeDisplayScaleBaseline(baseline);\n window.dispatchEvent(new Event(\"ep-display-scale-calibrated\"));\n return baseline;\n}\n\n/** Clear stored baseline (e.g. on sign-out). */\nexport function resetDisplayScaleEngine(): void {\n clearDisplayScaleBaseline();\n if (typeof window !== \"undefined\") {\n window.dispatchEvent(new Event(\"ep-display-scale-calibrated\"));\n }\n}\n","import { detectDisplayScale } from \"./detectDisplayScale\";\nimport { shouldApplyOsScaleCompensation, snapToWindowsOsScale } from \"./displayScaleWindows\";\nimport type { DisplayScaleSnapshot } from \"./types\";\n\nexport { shouldApplyOsScaleCompensation, snapToWindowsOsScale } from \"./displayScaleWindows\";\n\nexport type DisplayScaleCompensationConfig = {\n /** Master switch. @default true */\n enabled: boolean;\n /** Only run on Windows desktop/laptop — skips macOS Retina (DPR 2). @default true */\n windowsOnly: boolean;\n /** Compensate when snapped OS scale is above this value. @default 1.05 */\n minOsScaleToCompensate: number;\n /** Emulate this OS scale after compensation (1 = 100%). @default 1 */\n targetOsScale: number;\n /** Snap measured scale to Windows tiers for stable zoom. @default true */\n snapOsScale: boolean;\n /** Require a baseline explicitly calibrated at known browser zoom before compensating. @default false */\n requireExplicitCalibration: boolean;\n};\n\nexport const DEFAULT_DISPLAY_SCALE_COMPENSATION_CONFIG: DisplayScaleCompensationConfig = {\n enabled: true,\n windowsOnly: true,\n minOsScaleToCompensate: 1.05,\n targetOsScale: 1,\n snapOsScale: true,\n requireExplicitCalibration: false,\n};\n\nexport type DisplayScaleCompensation = {\n active: boolean;\n /** CSS `zoom` / transform scale applied to the app root (e.g. 0.667 for 150% OS). */\n zoom: number;\n /** @deprecated Prefer {@link measureDisplayScaleLayoutExtents} pixel layout size. */\n widthPercent: number;\n /** @deprecated Prefer {@link measureDisplayScaleLayoutExtents} pixel layout size. */\n heightPercent: number;\n /** Snapped OS scale used for compensation. */\n osDisplayScale: number;\n /** Target OS scale after compensation (percent). */\n targetOsScalePercent: number;\n};\n\nexport type DisplayScaleLayoutExtents = {\n /** Pre-zoom layout width so `layoutWidth * zoom` matches the visible viewport. */\n layoutWidthPx: number;\n /** Pre-zoom layout height so `layoutHeight * zoom` matches the visible viewport. */\n layoutHeightPx: number;\n};\n\nexport type ComputeDisplayScaleCompensationOptions = Partial<DisplayScaleCompensationConfig>;\n\nconst NO_COMPENSATION: DisplayScaleCompensation = {\n active: false,\n zoom: 1,\n widthPercent: 100,\n heightPercent: 100,\n osDisplayScale: 1,\n targetOsScalePercent: 100,\n};\n\nexport function supportsCssZoom(): boolean {\n if (typeof CSS === \"undefined\" || typeof CSS.supports !== \"function\") return false;\n return CSS.supports(\"zoom\", \"1\");\n}\n\nexport function computeDisplayScaleCompensation(\n snapshot: DisplayScaleSnapshot,\n options?: ComputeDisplayScaleCompensationOptions,\n): DisplayScaleCompensation {\n const config = { ...DEFAULT_DISPLAY_SCALE_COMPENSATION_CONFIG, ...options };\n\n if (!config.enabled) return NO_COMPENSATION;\n if (config.windowsOnly && !shouldApplyOsScaleCompensation()) return NO_COMPENSATION;\n if (config.requireExplicitCalibration && snapshot.calibration !== \"explicit\") {\n return NO_COMPENSATION;\n }\n\n const osScale = config.snapOsScale\n ? snapToWindowsOsScale(snapshot.osDisplayScale)\n : snapshot.osDisplayScale;\n\n if (osScale <= config.minOsScaleToCompensate) {\n return {\n ...NO_COMPENSATION,\n osDisplayScale: osScale,\n };\n }\n\n const zoom = config.targetOsScale / osScale;\n const extentPercent = 100 / zoom;\n\n return {\n active: true,\n zoom,\n widthPercent: extentPercent,\n heightPercent: extentPercent,\n osDisplayScale: osScale,\n targetOsScalePercent: Math.round(config.targetOsScale * 100),\n };\n}\n\n/**\n * Warm up detection before React mounts (stores baseline + sets html metadata).\n * Visual compensation is applied by `DisplayScaleCompensationProvider`.\n */\nexport function primeDisplayScaleCompensation(\n options?: ComputeDisplayScaleCompensationOptions,\n): DisplayScaleCompensation {\n if (typeof document === \"undefined\") return NO_COMPENSATION;\n\n const snapshot = detectDisplayScale({ autoBaseline: true });\n const compensation = computeDisplayScaleCompensation(snapshot, options);\n\n syncDisplayScaleCompensationMetadata(compensation, undefined, snapshot);\n if (!compensation.active) {\n const html = document.documentElement;\n html.dataset.displayScaleEngine = shouldApplyOsScaleCompensation() ? \"inactive\" : \"skipped\";\n html.dataset.displayScaleOs = String(snapshot.osDisplayScale);\n html.dataset.displayScaleBrowserZoom = String(snapshot.browserZoom);\n html.dataset.displayScaleMethod = snapshot.method;\n }\n\n return compensation;\n}\n\n/** Read the visible viewport in CSS pixels (avoids `%` / `vw` inflation on scaled displays). */\nexport function readDisplayScaleViewportSize(): { width: number; height: number } {\n if (typeof window === \"undefined\") {\n return { width: 0, height: 0 };\n }\n\n const viewport = window.visualViewport;\n const width = viewport?.width ?? window.innerWidth;\n const height = viewport?.height ?? window.innerHeight;\n\n return {\n width: Math.max(0, Math.round(width)),\n height: Math.max(0, Math.round(height)),\n };\n}\n\n/** Layout box size for the compensation root before `zoom` is applied. */\nexport function measureDisplayScaleLayoutExtents(zoom: number): DisplayScaleLayoutExtents {\n const safeZoom = Number.isFinite(zoom) && zoom > 0 ? zoom : 1;\n const { width, height } = readDisplayScaleViewportSize();\n\n return {\n layoutWidthPx: Math.round(width / safeZoom),\n layoutHeightPx: Math.round(height / safeZoom),\n };\n}\n\nexport function applyDisplayScaleCompensationStyles(\n element: HTMLElement,\n compensation: DisplayScaleCompensation,\n extents?: DisplayScaleLayoutExtents,\n): void {\n if (!compensation.active) {\n element.style.removeProperty(\"zoom\");\n element.style.removeProperty(\"transform\");\n element.style.removeProperty(\"transform-origin\");\n element.style.width = \"\";\n element.style.height = \"\";\n element.style.maxWidth = \"\";\n element.style.maxHeight = \"\";\n return;\n }\n\n const layout = extents ?? measureDisplayScaleLayoutExtents(compensation.zoom);\n\n if (supportsCssZoom()) {\n element.style.zoom = String(compensation.zoom);\n element.style.removeProperty(\"transform\");\n element.style.removeProperty(\"transform-origin\");\n } else {\n element.style.removeProperty(\"zoom\");\n element.style.transform = `scale(${compensation.zoom})`;\n element.style.transformOrigin = \"top left\";\n }\n\n element.style.width = `${layout.layoutWidthPx}px`;\n element.style.height = `${layout.layoutHeightPx}px`;\n element.style.maxWidth = `${layout.layoutWidthPx}px`;\n element.style.maxHeight = `${layout.layoutHeightPx}px`;\n}\n\nexport function clearDisplayScaleCompensationStyles(element: HTMLElement): void {\n element.style.removeProperty(\"zoom\");\n element.style.removeProperty(\"transform\");\n element.style.removeProperty(\"transform-origin\");\n element.style.width = \"\";\n element.style.height = \"\";\n element.style.maxWidth = \"\";\n element.style.maxHeight = \"\";\n delete element.dataset.displayScaleCompensated;\n delete element.dataset.osDisplayScale;\n}\n\n/** Publish compensation metadata on `html` (visual zoom lives on the app root). */\nexport function syncDisplayScaleCompensationMetadata(\n compensation: DisplayScaleCompensation,\n extents?: DisplayScaleLayoutExtents,\n snapshot?: DisplayScaleSnapshot,\n): void {\n if (typeof document === \"undefined\") return;\n\n const html = document.documentElement;\n\n if (!compensation.active) {\n html.style.removeProperty(\"--display-scale-zoom\");\n html.style.removeProperty(\"--display-scale-layout-width\");\n html.style.removeProperty(\"--display-scale-layout-height\");\n delete html.dataset.displayScaleCompensated;\n delete html.dataset.displayScaleEngine;\n delete html.dataset.displayScaleOs;\n delete html.dataset.displayScaleBrowserZoom;\n delete html.dataset.displayScaleMethod;\n delete html.dataset.osDisplayScale;\n return;\n }\n\n const layout = extents ?? measureDisplayScaleLayoutExtents(compensation.zoom);\n\n html.style.setProperty(\"--display-scale-zoom\", String(compensation.zoom));\n html.style.setProperty(\"--display-scale-layout-width\", `${layout.layoutWidthPx}px`);\n html.style.setProperty(\"--display-scale-layout-height\", `${layout.layoutHeightPx}px`);\n html.dataset.displayScaleCompensated = \"true\";\n html.dataset.displayScaleEngine = \"active\";\n html.dataset.displayScaleOs = String(compensation.osDisplayScale);\n html.dataset.osDisplayScale = String(compensation.osDisplayScale);\n if (snapshot) {\n html.dataset.displayScaleBrowserZoom = String(snapshot.browserZoom);\n html.dataset.displayScaleMethod = snapshot.method;\n }\n}\n\n/** @deprecated Use {@link syncDisplayScaleCompensationMetadata} + root element styles. */\nexport function applyDisplayScaleCompensationToDocument(\n compensation: DisplayScaleCompensation,\n): void {\n syncDisplayScaleCompensationMetadata(compensation);\n}\n\n/** Remove html metadata from legacy document-level compensation. */\nexport function clearDisplayScaleCompensationFromDocument(): void {\n if (typeof document === \"undefined\") return;\n\n const html = document.documentElement;\n\n clearDisplayScaleCompensationStyles(html);\n syncDisplayScaleCompensationMetadata({ ...NO_COMPENSATION, active: false });\n}\n","import type {\n DisplayScaleEvaluation,\n DisplayScaleIssue,\n DisplayScaleSnapshot,\n DisplayScaleThresholds,\n} from \"./types\";\n\nexport const DEFAULT_DISPLAY_SCALE_THRESHOLDS: Required<DisplayScaleThresholds> = {\n browserZoomMin: 0.9,\n browserZoomMax: 1.1,\n osDisplayScaleMax: 2,\n minViewportWidth: 0,\n};\n\nexport function evaluateDisplayScale(\n snapshot: DisplayScaleSnapshot,\n thresholds?: DisplayScaleThresholds,\n): DisplayScaleEvaluation {\n const resolved = { ...DEFAULT_DISPLAY_SCALE_THRESHOLDS, ...thresholds };\n const issues: DisplayScaleIssue[] = [];\n\n if (snapshot.browserZoom > resolved.browserZoomMax) {\n issues.push(\"browser-zoomed-in\");\n } else if (snapshot.browserZoom < resolved.browserZoomMin) {\n issues.push(\"browser-zoomed-out\");\n }\n\n if (snapshot.osDisplayScale > resolved.osDisplayScaleMax) {\n issues.push(\"high-os-scale\");\n }\n\n if (\n resolved.minViewportWidth > 0 &&\n snapshot.viewport.width > 0 &&\n snapshot.viewport.width < resolved.minViewportWidth\n ) {\n issues.push(\"viewport-too-narrow\");\n }\n\n return {\n snapshot,\n issues,\n isAcceptable: issues.length === 0,\n };\n}\n","import { detectDisplayScale } from \"./detectDisplayScale\";\nimport type { DetectDisplayScaleOptions, DisplayScaleSnapshot } from \"./types\";\n\nconst DEFAULT_SERVER_SNAPSHOT: DisplayScaleSnapshot = {\n browserZoom: 1,\n browserZoomPercent: 100,\n osDisplayScale: 1,\n osDisplayScalePercent: 100,\n devicePixelRatio: 1,\n effectiveScale: 1,\n viewport: { width: 0, height: 0 },\n screen: { width: 0, height: 0, availWidth: 0, availHeight: 0 },\n method: \"unavailable\",\n confidence: \"low\",\n calibration: \"unavailable\",\n measuredAt: new Date(0).toISOString(),\n};\n\nfunction snapshotSignature(snapshot: DisplayScaleSnapshot): string {\n const viewportWidth = Math.round(snapshot.viewport.width / 32);\n const viewportHeight = Math.round(snapshot.viewport.height / 32);\n\n return [\n snapshot.browserZoom,\n snapshot.osDisplayScale,\n snapshot.devicePixelRatio,\n snapshot.calibration,\n viewportWidth,\n viewportHeight,\n ].join(\"|\");\n}\n\nlet cachedSnapshot: DisplayScaleSnapshot | null = null;\nlet cachedSignature = \"\";\nlet cachedOptionsKey = \"\";\n\nfunction optionsKey(options?: DetectDisplayScaleOptions): string {\n return options?.autoBaseline === false ? \"no-baseline\" : \"baseline\";\n}\n\n/** Last committed snapshot without performing another browser measurement. */\nexport function getCachedDisplayScaleSnapshot(): DisplayScaleSnapshot | null {\n return cachedSnapshot;\n}\n\n/**\n * Cached snapshot for `useSyncExternalStore` — returns the same object reference\n * until meaningful scale / viewport values change.\n */\nexport function getDisplayScaleSnapshot(options?: DetectDisplayScaleOptions): DisplayScaleSnapshot {\n if (typeof window === \"undefined\") {\n return DEFAULT_SERVER_SNAPSHOT;\n }\n\n const next = detectDisplayScale(options);\n const signature = snapshotSignature(next);\n const key = optionsKey(options);\n\n if (cachedSnapshot && cachedSignature === signature && cachedOptionsKey === key) {\n return cachedSnapshot;\n }\n\n cachedOptionsKey = key;\n cachedSignature = signature;\n cachedSnapshot = next;\n return cachedSnapshot;\n}\n\nexport function invalidateDisplayScaleSnapshot(next?: DisplayScaleSnapshot): void {\n if (next) {\n cachedSnapshot = next;\n cachedSignature = snapshotSignature(next);\n return;\n }\n\n cachedSnapshot = null;\n cachedSignature = \"\";\n}\n\nexport function hasDisplayScaleSnapshotChanged(\n previous: DisplayScaleSnapshot,\n options?: DetectDisplayScaleOptions,\n): boolean {\n const next = detectDisplayScale(options);\n return snapshotSignature(next) !== snapshotSignature(previous);\n}\n\nexport function commitDisplayScaleSnapshot(\n snapshot: DisplayScaleSnapshot,\n options?: DetectDisplayScaleOptions,\n): DisplayScaleSnapshot {\n cachedOptionsKey = optionsKey(options);\n cachedSignature = snapshotSignature(snapshot);\n cachedSnapshot = snapshot;\n return cachedSnapshot;\n}\n\nexport function getServerDisplayScaleSnapshot(): DisplayScaleSnapshot {\n return DEFAULT_SERVER_SNAPSHOT;\n}\n","import { detectDisplayScale } from \"./detectDisplayScale\";\nimport {\n commitDisplayScaleSnapshot,\n getCachedDisplayScaleSnapshot,\n getDisplayScaleSnapshot,\n hasDisplayScaleSnapshotChanged,\n} from \"./displayScaleStore\";\nimport type { SubscribeDisplayScaleOptions } from \"./types\";\n\nfunction debounce<T extends (...args: never[]) => void>(fn: T, waitMs: number): T {\n let timer: ReturnType<typeof setTimeout> | undefined;\n\n return ((...args: never[]) => {\n if (timer) clearTimeout(timer);\n timer = setTimeout(() => {\n timer = undefined;\n fn(...args);\n }, waitMs);\n }) as T;\n}\n\n/**\n * Subscribe to display-scale changes (resize, visualViewport).\n * Returns an unsubscribe function.\n */\nexport function subscribeDisplayScale(\n listener: () => void,\n options?: SubscribeDisplayScaleOptions,\n): () => void {\n if (typeof window === \"undefined\") {\n return () => undefined;\n }\n\n const debounceMs = options?.debounceMs ?? 100;\n\n const notify = debounce(() => {\n const previous = getCachedDisplayScaleSnapshot() ?? getDisplayScaleSnapshot(options);\n if (!hasDisplayScaleSnapshotChanged(previous, options)) {\n return;\n }\n\n commitDisplayScaleSnapshot(detectDisplayScale(options), options);\n listener();\n }, debounceMs);\n\n const onResize = () => notify();\n\n window.addEventListener(\"resize\", onResize);\n window.visualViewport?.addEventListener(\"resize\", onResize);\n window.addEventListener(\"pageshow\", onResize);\n window.addEventListener(\"ep-display-scale-calibrated\", onResize);\n\n return () => {\n window.removeEventListener(\"resize\", onResize);\n window.visualViewport?.removeEventListener(\"resize\", onResize);\n window.removeEventListener(\"pageshow\", onResize);\n window.removeEventListener(\"ep-display-scale-calibrated\", onResize);\n };\n}\n"]}
package/dist/core.d.ts ADDED
@@ -0,0 +1,37 @@
1
+ import { b as DisplayScaleBaseline, a as DetectDisplayScaleOptions, n as DisplayScaleSnapshot, o as DisplayScaleThresholds, h as DisplayScaleEvaluation, S as SubscribeDisplayScaleOptions } from './displayScaleLogicalViewport-Ikf-DtkQ.js';
2
+ export { C as ComputeDisplayScaleCompensationOptions, D as DEFAULT_DISPLAY_SCALE_COMPENSATION_CONFIG, c as DisplayScaleCalibration, d as DisplayScaleCompensation, e as DisplayScaleCompensationConfig, f as DisplayScaleConfidence, g as DisplayScaleDetectionMethod, i as DisplayScaleIssue, j as DisplayScaleLayoutExtents, k as DisplayScaleLogicalViewport, l as DisplayScaleOverlayBoundsOptions, m as DisplayScaleScreen, p as DisplayScaleViewport, q as applyDisplayScaleCompensationStyles, r as applyDisplayScaleCompensationToDocument, s as clearDisplayScaleCompensationFromDocument, t as clearDisplayScaleCompensationStyles, u as computeDisplayScaleCompensation, v as getDisplayScaleLogicalViewport, w as getDisplayScaleOverlayMaxHeight, x as measureDisplayScaleLayoutExtents, y as primeDisplayScaleCompensation, z as readDisplayScaleViewportSize, A as supportsCssZoom, B as syncDisplayScaleCompensationMetadata, E as toDisplayScaleLogicalPixels } from './displayScaleLogicalViewport-Ikf-DtkQ.js';
3
+
4
+ declare function readDisplayScaleBaseline(): DisplayScaleBaseline | null;
5
+ declare function writeDisplayScaleBaseline(baseline: DisplayScaleBaseline): void;
6
+ declare function clearDisplayScaleBaseline(): void;
7
+
8
+ /**
9
+ * Measure current browser zoom and OS display scale.
10
+ *
11
+ * Browser zoom is tracked relative to a session baseline (first visit assumes 100%).
12
+ * Call `recalibrateDisplayScale()` when the user resets zoom to 100%.
13
+ */
14
+ declare function detectDisplayScale(options?: DetectDisplayScaleOptions): DisplayScaleSnapshot;
15
+ /**
16
+ * Reset the session baseline — use when browser zoom is known to be 100%.
17
+ *
18
+ * @param assumedBrowserZoom Browser zoom at calibration time (default 1).
19
+ */
20
+ declare function recalibrateDisplayScale(assumedBrowserZoom?: number): DisplayScaleBaseline;
21
+ /** Clear stored baseline (e.g. on sign-out). */
22
+ declare function resetDisplayScaleEngine(): void;
23
+
24
+ declare function snapToWindowsOsScale(scale: number): number;
25
+ /** True when OS scale compensation should run in this environment. */
26
+ declare function shouldApplyOsScaleCompensation(): boolean;
27
+
28
+ declare const DEFAULT_DISPLAY_SCALE_THRESHOLDS: Required<DisplayScaleThresholds>;
29
+ declare function evaluateDisplayScale(snapshot: DisplayScaleSnapshot, thresholds?: DisplayScaleThresholds): DisplayScaleEvaluation;
30
+
31
+ /**
32
+ * Subscribe to display-scale changes (resize, visualViewport).
33
+ * Returns an unsubscribe function.
34
+ */
35
+ declare function subscribeDisplayScale(listener: () => void, options?: SubscribeDisplayScaleOptions): () => void;
36
+
37
+ export { DEFAULT_DISPLAY_SCALE_THRESHOLDS, DetectDisplayScaleOptions, DisplayScaleBaseline, DisplayScaleEvaluation, DisplayScaleSnapshot, DisplayScaleThresholds, SubscribeDisplayScaleOptions, clearDisplayScaleBaseline, detectDisplayScale, evaluateDisplayScale, readDisplayScaleBaseline, recalibrateDisplayScale, resetDisplayScaleEngine, shouldApplyOsScaleCompensation, snapToWindowsOsScale, subscribeDisplayScale, writeDisplayScaleBaseline };
package/dist/core.js ADDED
@@ -0,0 +1,5 @@
1
+ import './chunk-SLNYXBHU.js';
2
+ export { getDisplayScaleLogicalViewport, getDisplayScaleOverlayMaxHeight, toDisplayScaleLogicalPixels } from './chunk-U3ZWHUJG.js';
3
+ export { DEFAULT_DISPLAY_SCALE_COMPENSATION_CONFIG, DEFAULT_DISPLAY_SCALE_THRESHOLDS, applyDisplayScaleCompensationStyles, applyDisplayScaleCompensationToDocument, clearDisplayScaleBaseline, clearDisplayScaleCompensationFromDocument, clearDisplayScaleCompensationStyles, computeDisplayScaleCompensation, detectDisplayScale, evaluateDisplayScale, measureDisplayScaleLayoutExtents, primeDisplayScaleCompensation, readDisplayScaleBaseline, readDisplayScaleViewportSize, recalibrateDisplayScale, resetDisplayScaleEngine, shouldApplyOsScaleCompensation, snapToWindowsOsScale, subscribeDisplayScale, supportsCssZoom, syncDisplayScaleCompensationMetadata, writeDisplayScaleBaseline } from './chunk-VWJNHWMD.js';
4
+ //# sourceMappingURL=core.js.map
5
+ //# sourceMappingURL=core.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"core.js"}