react-display-scale-engine 0.1.3 → 0.1.4

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.
package/README.md CHANGED
@@ -1,14 +1,8 @@
1
1
  # react-display-scale-engine
2
2
 
3
- React utilities for maintaining a consistent visual density on Windows displays with elevated OS display scaling, with first-class Material UI overlay support.
3
+ Keep a React app visually consistent on Windows displays that use elevated OS display scaling, while preserving normal browser behavior at standard scale. It is especially useful for MUI overlays such as menus, popovers, poppers, and selects.
4
4
 
5
- The library is split into entry points, so an application only takes the layer it needs:
6
-
7
- ```ts
8
- import { detectDisplayScale } from "react-display-scale-engine/core";
9
- import { DisplayScaleProvider } from "react-display-scale-engine/react";
10
- import { DisplayScaleMenu } from "react-display-scale-engine/mui";
11
- ```
5
+ The engine only applies compensation when it detects the supported display-scale condition. At normal scale, it leaves the app layout and native MUI positioning unchanged.
12
6
 
13
7
  ## Install
14
8
 
@@ -16,11 +10,20 @@ import { DisplayScaleMenu } from "react-display-scale-engine/mui";
16
10
  npm install react-display-scale-engine
17
11
  ```
18
12
 
19
- `react`, `react-dom`, `@mui/material`, `@emotion/react`, `@emotion/styled`, and `@popperjs/core` are peer dependencies. The MUI peers are necessary only for the `/mui` entry point.
13
+ ## Compatibility
14
+
15
+ | Dependency | Supported version | Notes |
16
+ | --- | --- | --- |
17
+ | Node.js | 18 or newer | Required by the package. |
18
+ | React / React DOM | 18 or newer | React 19 is supported and tested. |
19
+ | Material UI | 7.x | Required only when using `…/mui`. MUI 8 is not supported yet. |
20
+ | Emotion | 11 or newer | Required only when using `…/mui`. |
21
+ | `@popperjs/core` | 2 or newer | Required only when using `…/mui`. |
22
+ | TypeScript | Optional | Type declarations are included; the package is verified with TypeScript 5.7. |
20
23
 
21
- ## Quick start
24
+ ## Start here
22
25
 
23
- Call the optional pre-mount warm-up, then wrap the application once.
26
+ Call the optional warm-up before rendering, then add one provider around the app.
24
27
 
25
28
  ```tsx
26
29
  import { primeDisplayScaleCompensation } from "react-display-scale-engine/core";
@@ -29,77 +32,54 @@ import { DisplayScaleProvider } from "react-display-scale-engine/react";
29
32
  primeDisplayScaleCompensation();
30
33
 
31
34
  export function AppRoot() {
32
- return <DisplayScaleProvider><App /></DisplayScaleProvider>;
35
+ return (
36
+ <DisplayScaleProvider>
37
+ <App />
38
+ </DisplayScaleProvider>
39
+ );
33
40
  }
34
41
  ```
35
42
 
36
- For MUI overlays whose placement must account for the compensated coordinate space, choose either the wrapper or hook adapter API.
43
+ ## MUI overlays: recommended hook API
37
44
 
38
- ```tsx
39
- import { DisplayScaleMenu } from "react-display-scale-engine/mui";
40
-
41
- <DisplayScaleMenu anchorEl={anchorEl} open={open} onClose={close}>
42
- <MenuItem onClick={close}>Action</MenuItem>
43
- </DisplayScaleMenu>;
44
- ```
45
-
46
- Or retain MUI's native component and spread engine-owned props from a hook:
45
+ Keep using MUI's native components. The hook supplies the compensated props; spread them onto the matching MUI component.
47
46
 
48
47
  ```tsx
49
- import { Menu } from "@mui/material";
48
+ import { Menu, MenuItem } from "@mui/material";
50
49
  import { useMuiDisplayScaleMenu } from "react-display-scale-engine/mui";
51
50
 
52
- const { menuProps } = useMuiDisplayScaleMenu({
53
- anchorEl,
54
- open,
55
- });
56
-
57
- <Menu {...menuProps} onClose={close}>
58
- <MenuItem onClick={close}>Action</MenuItem>
59
- </Menu>;
60
- ```
61
-
62
- The same pattern is available through `useMuiDisplayScalePopover`,
63
- `useMuiDisplayScalePopper`, and `useMuiDisplayScaleSelect`. The wrappers and
64
- hook adapters share the same engine implementation, so their placement and
65
- scale behavior are equivalent.
51
+ function ActionsMenu({ anchorEl, open, close }) {
52
+ const { menuProps } = useMuiDisplayScaleMenu({ anchorEl, open });
66
53
 
67
- See [the engine guide](docs/display-scale-engine.md) for policy, limitations, and migration guidance.
68
-
69
- ## Development
70
-
71
- ```bash
72
- npm install
73
- npm run check
74
- npm run build
75
- npm pack --dry-run
54
+ return (
55
+ <Menu {...menuProps} onClose={close}>
56
+ <MenuItem onClick={close}>Action</MenuItem>
57
+ </Menu>
58
+ );
59
+ }
76
60
  ```
77
61
 
78
- Public entry points:
62
+ Use the corresponding hook for each MUI component:
79
63
 
80
- | Import | Purpose |
64
+ | MUI component | Engine hook |
81
65
  | --- | --- |
82
- | `react-display-scale-engine` | Compatibility entry with all public exports |
83
- | `…/core` | Framework-independent detection and compensation math |
84
- | `…/react` | Provider and React hooks |
85
- | `…/mui` | MUI 7 wrappers, hook adapters, theme adapter and layout tokens |
86
-
87
- ## Publishing
66
+ | `Menu` | `useMuiDisplayScaleMenu` |
67
+ | `Popover` | `useMuiDisplayScalePopover` |
68
+ | `Popper` | `useMuiDisplayScalePopper` |
69
+ | `Select` | `useMuiDisplayScaleSelect` |
88
70
 
89
- Before first publishing, make sure the `name` in `package.json` is available to your npm account, then run `npm login`.
71
+ `useMuiDisplayScaleSelect` also handles its internal MUI menu, including the first time the options list opens.
90
72
 
91
- ```bash
92
- npm run release:check
93
- npm run release:dry-run
94
- npm run release:publish -- --otp 123456
95
- ```
73
+ Wrapper components (`DisplayScaleMenu`, `DisplayScalePopover`, `DisplayScalePopper`, and `DisplayScaleSelect`) remain available, but hooks are the preferred API for both new and existing MUI applications.
96
74
 
97
- The package includes a CLI:
75
+ ## Import paths
98
76
 
99
- ```bash
100
- npx rds-engine check
101
- npx rds-engine publish --dry-run
102
- npx rds-engine publish --otp 123456
77
+ ```ts
78
+ import { detectDisplayScale } from "react-display-scale-engine/core";
79
+ import { DisplayScaleProvider } from "react-display-scale-engine/react";
80
+ import { useMuiDisplayScalePopover } from "react-display-scale-engine/mui";
103
81
  ```
104
82
 
105
- `publish` always builds and runs `npm pack --dry-run` first. It never changes the version; use `npm version patch|minor|major` before publishing. When npm account policy requires 2FA for publishing, supply the current authenticator code with `--otp` or use an npm granular access token that has bypass-2FA permission.
83
+ The package root (`react-display-scale-engine`) also exports all public APIs for compatibility. Prefer the explicit paths above for new code.
84
+
85
+ For engine behavior, limitations, and migration examples, see [the guide](docs/display-scale-engine.md).
@@ -1,7 +1,7 @@
1
1
  import { useDisplayScaleCompensation, useDisplayScaleAnchor } from './chunk-F5AFPMQC.js';
2
2
  import { Menu, Popover, Popper, Select } from '@mui/material';
3
3
  import { useTheme } from '@mui/material/styles';
4
- import { useCallback, useLayoutEffect, useRef, useMemo, useId, useState } from 'react';
4
+ import { useCallback, useLayoutEffect, useRef, useMemo, useState, useId } from 'react';
5
5
  import { jsx } from 'react/jsx-runtime';
6
6
 
7
7
  // src/createMuiDisplayScaleComponents.ts
@@ -88,6 +88,7 @@ function resolveOrigins(anchorOrigin, transformOrigin, anchorWidth, anchorHeight
88
88
  function useDisplayScalePopoverPosition({
89
89
  open,
90
90
  overlayRootRef,
91
+ overlayRoot: mountedOverlayRoot,
91
92
  anchorElement,
92
93
  anchorReference,
93
94
  anchorPosition,
@@ -98,7 +99,7 @@ function useDisplayScalePopoverPosition({
98
99
  const { compensation, layoutExtents, rootRef } = useDisplayScaleCompensation();
99
100
  const correctPosition = useCallback(() => {
100
101
  if (!open || !compensation.active || !layoutExtents) return;
101
- const overlayRoot = overlayRootRef.current;
102
+ const overlayRoot = mountedOverlayRoot ?? overlayRootRef.current;
102
103
  const paper = overlayRoot?.querySelector(".MuiPopover-paper");
103
104
  if (!paper) return;
104
105
  const zoom = compensation.zoom > 0 ? compensation.zoom : 1;
@@ -140,6 +141,7 @@ function useDisplayScalePopoverPosition({
140
141
  compensation.zoom,
141
142
  layoutExtents,
142
143
  marginThreshold,
144
+ mountedOverlayRoot,
143
145
  open,
144
146
  overlayRootRef,
145
147
  rootRef,
@@ -300,10 +302,15 @@ function useMuiDisplayScaleSelect(props) {
300
302
  } = props;
301
303
  const { rootRef } = useDisplayScaleCompensation();
302
304
  const menuRootRef = useRef(null);
305
+ const [menuRootElement, setMenuRootElement] = useState(null);
303
306
  const selectId = useId();
304
307
  const [anchorElement, setAnchorElement] = useState(null);
305
308
  const [uncontrolledOpen, setUncontrolledOpen] = useState(Boolean(defaultOpen));
306
309
  const open = openProp ?? uncontrolledOpen;
310
+ const handleMenuRootRef = useCallback((element) => {
311
+ menuRootRef.current = element;
312
+ setMenuRootElement(element);
313
+ }, []);
307
314
  useLayoutEffect(() => {
308
315
  if (!open) {
309
316
  setAnchorElement(null);
@@ -330,6 +337,7 @@ function useMuiDisplayScaleSelect(props) {
330
337
  useDisplayScalePopoverPosition({
331
338
  open,
332
339
  overlayRootRef: menuRootRef,
340
+ overlayRoot: menuRootElement,
333
341
  anchorElement,
334
342
  anchorOrigin: { vertical: "bottom", horizontal: "center" },
335
343
  transformOrigin: { vertical: "top", horizontal: "center" }
@@ -340,7 +348,10 @@ function useMuiDisplayScaleSelect(props) {
340
348
  return {
341
349
  ...MenuProps,
342
350
  container: rootRef.current ?? document.body,
343
- ref: menuRootRef,
351
+ // Select mounts its internal Menu only after the first open event.
352
+ // Store the mounted root in state so the position hook runs again once
353
+ // MUI has measured and rendered its paper, rather than only on reopen.
354
+ ref: handleMenuRootRef,
344
355
  slotProps: {
345
356
  ...MenuProps?.slotProps,
346
357
  paper: {
@@ -349,7 +360,7 @@ function useMuiDisplayScaleSelect(props) {
349
360
  }
350
361
  }
351
362
  };
352
- }, [MenuProps, rootRef]);
363
+ }, [MenuProps, handleMenuRootRef, rootRef]);
353
364
  return {
354
365
  selectProps: {
355
366
  ...selectProps,
@@ -441,5 +452,5 @@ var signinSplitColumnSx = {
441
452
  };
442
453
 
443
454
  export { DisplayScaleMenu, DisplayScalePopover, DisplayScalePopper, DisplayScaleSelect, createMuiDisplayScaleComponents, displayScalePageFillSx, signinSplitColumnSx, signinViewportShellSx, useDisplayScalePopoverPosition, useDisplayScaleTabsIndicator, useMuiDisplayScaleComponents, useMuiDisplayScaleMenu, useMuiDisplayScalePopover, useMuiDisplayScalePopper, useMuiDisplayScaleSelect };
444
- //# sourceMappingURL=chunk-5MGGPTIJ.js.map
445
- //# sourceMappingURL=chunk-5MGGPTIJ.js.map
455
+ //# sourceMappingURL=chunk-47O3TJUJ.js.map
456
+ //# sourceMappingURL=chunk-47O3TJUJ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/createMuiDisplayScaleComponents.ts","../src/useDisplayScalePopoverPosition.ts","../src/DisplayScaleMuiOverlays.tsx","../src/DisplayScaleSelect.tsx","../src/useMuiDisplayScaleComponents.ts","../src/useDisplayScaleTabsIndicator.ts","../src/displayScaleLayoutTokens.ts"],"names":["useRef","useCallback","useLayoutEffect","useMemo","jsx"],"mappings":"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","file":"chunk-47O3TJUJ.js","sourcesContent":["import type { Components, Theme } from \"@mui/material/styles\";\n\ntype PortalContainerGetter = () => HTMLElement;\n\ntype MuiDisplayScaleComponentsOptions = {\n getPortalContainer: PortalContainerGetter;\n /**\n * Overlay content is portalled outside the zoomed app root, so it needs the\n * same density correction independently.\n */\n overlayZoom?: number;\n};\n\n/**\n * MUI adapter for a compensated app root.\n *\n * Centralizing these defaults gives every MUI overlay the portal strategy and\n * density correction chosen by the display-scale engine.\n */\nexport function createMuiDisplayScaleComponents({\n getPortalContainer,\n overlayZoom = 1,\n}: MuiDisplayScaleComponentsOptions): Components<Theme> {\n const zoom = overlayZoom > 0 ? overlayZoom : 1;\n const resolveContainerZoom = (container: unknown): number => {\n if (typeof document === \"undefined\") return zoom;\n const resolvedContainer =\n typeof container === \"function\" ? (container as PortalContainerGetter)() : container;\n // MUI does not always expose the resolved portal node through ownerState.\n // Missing means the component will use its default (document.body).\n return resolvedContainer == null || resolvedContainer === document.body ? zoom : 1;\n };\n\n return {\n MuiModal: {\n defaultProps: {\n container: getPortalContainer,\n },\n },\n MuiDialog: {\n defaultProps: {\n container: getPortalContainer,\n },\n styleOverrides: {\n paper: ({ ownerState }) => ({ zoom: resolveContainerZoom(ownerState?.container) }),\n },\n },\n MuiPopover: {\n defaultProps: {\n container: getPortalContainer,\n },\n styleOverrides: {\n paper: ({ ownerState }) => ({ zoom: resolveContainerZoom(ownerState?.container) }),\n },\n },\n MuiPopper: {\n defaultProps: {\n container: getPortalContainer,\n },\n styleOverrides: {\n root: ({ ownerState }) => ({ zoom: resolveContainerZoom(ownerState?.container) }),\n },\n },\n MuiDrawer: {\n defaultProps: {\n // Drawer inherits Modal props. Supplying the portal host here (rather\n // than only through MuiModal) also covers Drawer internals directly.\n container: getPortalContainer,\n },\n styleOverrides: {\n paper: ({ ownerState }) => {\n // Permanent/persistent drawers live inside the compensated root and\n // must not be zoomed a second time. Temporary drawers are portalled\n // to document.body, so their paper needs the overlay density zoom.\n if (ownerState.variant !== \"temporary\") return {};\n\n const isVertical = ownerState.anchor === \"left\" || ownerState.anchor === \"right\";\n return isVertical\n ? {\n zoom,\n height: \"var(--ep-display-scale-overlay-viewport-height, 100dvh)\",\n maxHeight: \"var(--ep-display-scale-overlay-viewport-height, 100dvh)\",\n }\n : {\n zoom,\n width: \"var(--ep-display-scale-overlay-viewport-width, 100dvw)\",\n maxWidth: \"var(--ep-display-scale-overlay-viewport-width, 100dvw)\",\n };\n },\n },\n },\n };\n}\n","import type { PopoverOrigin, PopoverProps } from \"@mui/material\";\nimport { type RefObject, useCallback, useLayoutEffect } from \"react\";\nimport { useDisplayScaleCompensation } from \"./DisplayScaleCompensationProvider\";\n\ntype DisplayScalePopoverPositionOptions = {\n open: boolean;\n overlayRootRef: RefObject<HTMLDivElement | null>;\n /**\n * The mounted overlay root. A ref assignment alone does not trigger a\n * render, so callers with lazily-mounted overlays (notably MUI Select)\n * provide this state value to schedule the first correction after mount.\n */\n overlayRoot?: HTMLDivElement | null;\n anchorElement: HTMLElement | null;\n anchorReference?: PopoverProps[\"anchorReference\"];\n anchorPosition?: PopoverProps[\"anchorPosition\"];\n anchorOrigin?: PopoverProps[\"anchorOrigin\"];\n transformOrigin?: PopoverProps[\"transformOrigin\"];\n marginThreshold?: PopoverProps[\"marginThreshold\"];\n};\n\nfunction resolveOffset(\n origin: number | \"top\" | \"center\" | \"bottom\" | \"left\" | \"right\" | undefined,\n size: number,\n): number {\n if (typeof origin === \"number\") return origin;\n if (origin === \"center\") return size / 2;\n if (origin === \"bottom\" || origin === \"right\") return size;\n return 0;\n}\n\nfunction resolveOrigins(\n anchorOrigin: PopoverOrigin | undefined,\n transformOrigin: PopoverOrigin | undefined,\n anchorWidth: number,\n anchorHeight: number,\n paperWidth: number,\n paperHeight: number,\n): { anchorX: number; anchorY: number; transformX: number; transformY: number } {\n const resolvedAnchorOrigin = anchorOrigin ?? { vertical: \"top\", horizontal: \"left\" };\n const resolvedTransformOrigin = transformOrigin ?? { vertical: \"top\", horizontal: \"left\" };\n\n return {\n anchorX: resolveOffset(resolvedAnchorOrigin.horizontal, anchorWidth),\n anchorY: resolveOffset(resolvedAnchorOrigin.vertical, anchorHeight),\n transformX: resolveOffset(resolvedTransformOrigin.horizontal, paperWidth),\n transformY: resolveOffset(resolvedTransformOrigin.vertical, paperHeight),\n };\n}\n\n/**\n * Replaces only MUI Popover's physical-viewport collision pass. MUI still\n * owns focus, transition and anchor semantics; this writes the final logical\n * top/left inside the compensated portal root.\n */\nexport function useDisplayScalePopoverPosition({\n open,\n overlayRootRef,\n overlayRoot: mountedOverlayRoot,\n anchorElement,\n anchorReference,\n anchorPosition,\n anchorOrigin,\n transformOrigin,\n marginThreshold = 16,\n}: DisplayScalePopoverPositionOptions): void {\n const { compensation, layoutExtents, rootRef } = useDisplayScaleCompensation();\n\n const correctPosition = useCallback(() => {\n if (!open || !compensation.active || !layoutExtents) return;\n\n const overlayRoot = mountedOverlayRoot ?? overlayRootRef.current;\n const paper = overlayRoot?.querySelector<HTMLElement>(\".MuiPopover-paper\");\n if (!paper) return;\n\n const zoom = compensation.zoom > 0 ? compensation.zoom : 1;\n const rootRect = rootRef.current?.getBoundingClientRect();\n const rootLeft = rootRect?.left ?? 0;\n const rootTop = rootRect?.top ?? 0;\n const anchorRect = anchorElement?.getBoundingClientRect();\n const anchorX =\n anchorReference === \"anchorPosition\" && anchorPosition\n ? (anchorPosition.left - rootLeft) / zoom\n : anchorRect\n ? (anchorRect.left - rootLeft) / zoom\n : null;\n const anchorY =\n anchorReference === \"anchorPosition\" && anchorPosition\n ? (anchorPosition.top - rootTop) / zoom\n : anchorRect\n ? (anchorRect.top - rootTop) / zoom\n : null;\n if (anchorX == null || anchorY == null) return;\n\n const paperWidth = paper.offsetWidth;\n const paperHeight = paper.offsetHeight;\n const {\n anchorX: originX,\n anchorY: originY,\n transformX,\n transformY,\n } = resolveOrigins(\n anchorOrigin,\n transformOrigin,\n anchorRect ? anchorRect.width / zoom : 0,\n anchorRect ? anchorRect.height / zoom : 0,\n paperWidth,\n paperHeight,\n );\n const margin = typeof marginThreshold === \"number\" ? marginThreshold : 16;\n const maxLeft = Math.max(margin, layoutExtents.layoutWidthPx - margin - paperWidth);\n const maxTop = Math.max(margin, layoutExtents.layoutHeightPx - margin - paperHeight);\n const left = Math.min(maxLeft, Math.max(margin, anchorX + originX - transformX));\n const top = Math.min(maxTop, Math.max(margin, anchorY + originY - transformY));\n\n paper.style.left = `${Math.round(left)}px`;\n paper.style.top = `${Math.round(top)}px`;\n }, [\n anchorElement,\n anchorOrigin,\n anchorPosition,\n anchorReference,\n compensation.active,\n compensation.zoom,\n layoutExtents,\n marginThreshold,\n mountedOverlayRoot,\n open,\n overlayRootRef,\n rootRef,\n transformOrigin,\n ]);\n\n useLayoutEffect(() => {\n if (!open) return;\n\n const firstFrame = window.requestAnimationFrame(() => {\n correctPosition();\n window.requestAnimationFrame(correctPosition);\n });\n window.addEventListener(\"resize\", correctPosition);\n window.addEventListener(\"scroll\", correctPosition, true);\n\n return () => {\n window.cancelAnimationFrame(firstFrame);\n window.removeEventListener(\"resize\", correctPosition);\n window.removeEventListener(\"scroll\", correctPosition, true);\n };\n }, [correctPosition, open]);\n}\n","import type { MenuProps, PopoverProps, PopperProps } from \"@mui/material\";\nimport { Menu, Popover, Popper } from \"@mui/material\";\nimport { useTheme } from \"@mui/material/styles\";\nimport type {\n OffsetsFunction,\n Options as PopperOffsetOptions,\n} from \"@popperjs/core/lib/modifiers/offset\";\nimport { type ReactElement, useMemo, useRef } from \"react\";\nimport { useDisplayScaleCompensation } from \"./DisplayScaleCompensationProvider\";\nimport { useDisplayScaleAnchor } from \"./useDisplayScaleAnchor\";\nimport { useDisplayScalePopoverPosition } from \"./useDisplayScalePopoverPosition\";\n\ntype NativeAnchorProps = {\n anchorEl?: HTMLElement | null;\n};\n\nexport type DisplayScaleMenuProps = Omit<MenuProps, \"anchorEl\"> & NativeAnchorProps;\n\nexport type MuiDisplayScaleMenuAdapter = {\n /** Spread these props onto MUI's native `<Menu />`. */\n menuProps: MenuProps;\n};\n\n/** Non-wrapper display-scale adapter for MUI's native `<Menu />`. */\nexport function useMuiDisplayScaleMenu({\n anchorEl,\n ...props\n}: DisplayScaleMenuProps): MuiDisplayScaleMenuAdapter {\n const displayScaleAnchor = useDisplayScaleAnchor(anchorEl ?? null);\n const { compensation, rootRef } = useDisplayScaleCompensation();\n const theme = useTheme();\n const overlayRootRef = useRef<HTMLDivElement>(null);\n const horizontalOrigin = theme.direction === \"rtl\" ? \"right\" : \"left\";\n const anchorOrigin = props.anchorOrigin ?? {\n vertical: \"bottom\",\n horizontal: horizontalOrigin,\n };\n const transformOrigin = props.transformOrigin ?? {\n vertical: \"top\",\n horizontal: horizontalOrigin,\n };\n const anchorPosition = useMemo(() => {\n if (props.anchorReference !== \"anchorPosition\" || !props.anchorPosition) {\n return props.anchorPosition;\n }\n const zoom = compensation.zoom > 0 ? compensation.zoom : 1;\n return {\n top: props.anchorPosition.top / zoom,\n left: props.anchorPosition.left / zoom,\n };\n }, [compensation.zoom, props.anchorPosition, props.anchorReference]);\n useDisplayScalePopoverPosition({\n open: props.open,\n overlayRootRef,\n anchorElement: anchorEl ?? null,\n anchorReference: props.anchorReference,\n anchorPosition: props.anchorPosition,\n anchorOrigin,\n transformOrigin,\n marginThreshold: props.marginThreshold,\n });\n\n return {\n menuProps: {\n ...props,\n ref: overlayRootRef,\n anchorEl: displayScaleAnchor,\n anchorPosition,\n anchorOrigin,\n container: rootRef.current ?? document.body,\n transformOrigin,\n },\n };\n}\n\n/** MUI Menu wrapper — prefer `useMuiDisplayScaleMenu` when retaining `<Menu />`. */\nexport function DisplayScaleMenu(props: DisplayScaleMenuProps): ReactElement {\n const { menuProps } = useMuiDisplayScaleMenu(props);\n return <Menu {...menuProps} />;\n}\n\nexport type DisplayScalePopoverProps = Omit<PopoverProps, \"anchorEl\"> & NativeAnchorProps;\n\nexport type MuiDisplayScalePopoverAdapter = {\n /** Spread these props onto MUI's native `<Popover />`. */\n popoverProps: PopoverProps;\n};\n\n/** Non-wrapper display-scale adapter for MUI's native `<Popover />`. */\nexport function useMuiDisplayScalePopover({\n anchorEl,\n ...props\n}: DisplayScalePopoverProps): MuiDisplayScalePopoverAdapter {\n const displayScaleAnchor = useDisplayScaleAnchor(anchorEl ?? null);\n const { rootRef } = useDisplayScaleCompensation();\n const overlayRootRef = useRef<HTMLDivElement>(null);\n useDisplayScalePopoverPosition({\n open: props.open,\n overlayRootRef,\n anchorElement: anchorEl ?? null,\n anchorReference: props.anchorReference,\n anchorPosition: props.anchorPosition,\n anchorOrigin: props.anchorOrigin,\n transformOrigin: props.transformOrigin,\n marginThreshold: props.marginThreshold,\n });\n return {\n popoverProps: {\n ...props,\n ref: overlayRootRef,\n anchorEl: displayScaleAnchor,\n container: rootRef.current ?? document.body,\n },\n };\n}\n\n/** MUI Popover wrapper — prefer `useMuiDisplayScalePopover` when retaining `<Popover />`. */\nexport function DisplayScalePopover(props: DisplayScalePopoverProps): ReactElement {\n const { popoverProps } = useMuiDisplayScalePopover(props);\n return <Popover {...popoverProps} />;\n}\n\nexport type DisplayScalePopperProps = Omit<PopperProps, \"anchorEl\"> & NativeAnchorProps;\n\nexport type MuiDisplayScalePopperAdapter = {\n /** Spread these props onto MUI's native `<Popper />`. */\n popperProps: PopperProps;\n};\n\nconst defaultDisplayScalePopperModifiers: PopperProps[\"modifiers\"] = [\n {\n name: \"offset\",\n options: { offset: [0, 8] },\n },\n];\n\nfunction scalePopperOffsets(\n modifiers: PopperProps[\"modifiers\"],\n zoom: number,\n): PopperProps[\"modifiers\"] {\n if (!modifiers || zoom === 1) return modifiers;\n\n return modifiers.map((modifier) => {\n if (modifier.name !== \"offset\") return modifier;\n\n const options = modifier.options as Partial<PopperOffsetOptions> | undefined;\n const rawOffset = options?.offset;\n if (!rawOffset) return modifier;\n\n const scaleValue = (value: number | null | undefined) => (value == null ? value : value / zoom);\n const offset: PopperOffsetOptions[\"offset\"] =\n typeof rawOffset === \"function\"\n ? (args: Parameters<OffsetsFunction>[0]) => {\n const [skidding, distance] = rawOffset(args);\n return [scaleValue(skidding), scaleValue(distance)];\n }\n : [scaleValue(rawOffset[0]), scaleValue(rawOffset[1])];\n\n return { ...modifier, options: { ...options, offset } };\n });\n}\n\n/** Non-wrapper display-scale adapter for MUI's native `<Popper />`. */\nexport function useMuiDisplayScalePopper({\n anchorEl,\n ...props\n}: DisplayScalePopperProps): MuiDisplayScalePopperAdapter {\n const displayScaleAnchor = useDisplayScaleAnchor(anchorEl ?? null);\n const { compensation, rootRef } = useDisplayScaleCompensation();\n const modifiers = useMemo(\n () =>\n scalePopperOffsets(props.modifiers ?? defaultDisplayScalePopperModifiers, compensation.zoom),\n [compensation.zoom, props.modifiers],\n );\n return {\n popperProps: {\n ...props,\n anchorEl: displayScaleAnchor,\n container: rootRef.current ?? document.body,\n modifiers,\n style: { ...props.style, zoom: 1 },\n },\n };\n}\n\n/** MUI Popper wrapper — prefer `useMuiDisplayScalePopper` when retaining `<Popper />`. */\nexport function DisplayScalePopper(props: DisplayScalePopperProps): ReactElement {\n const { popperProps } = useMuiDisplayScalePopper(props);\n return <Popper {...popperProps} />;\n}\n","import type { MenuProps, SelectProps } from \"@mui/material\";\nimport { Select } from \"@mui/material\";\nimport type { ReactElement, SyntheticEvent } from \"react\";\nimport { useCallback, useId, useLayoutEffect, useMemo, useRef, useState } from \"react\";\nimport { useDisplayScaleCompensation } from \"./DisplayScaleCompensationProvider\";\nimport { useDisplayScalePopoverPosition } from \"./useDisplayScalePopoverPosition\";\n\nexport type MuiDisplayScaleSelectAdapter<Value = unknown> = {\n /** Spread these props onto MUI's native `<Select />`. */\n selectProps: SelectProps<Value>;\n};\n\n/**\n * MUI Select adapter for the display-scale engine. Select creates its Menu\n * internally, so it cannot use DisplayScaleMenu directly.\n */\nexport function useMuiDisplayScaleSelect<Value = unknown>(\n props: SelectProps<Value>,\n): MuiDisplayScaleSelectAdapter<Value> {\n const {\n MenuProps,\n SelectDisplayProps,\n defaultOpen,\n onClose,\n onOpen,\n open: openProp,\n ...selectProps\n } = props;\n const { rootRef } = useDisplayScaleCompensation();\n const menuRootRef = useRef<HTMLDivElement>(null);\n const [menuRootElement, setMenuRootElement] = useState<HTMLDivElement | null>(null);\n const selectId = useId();\n const [anchorElement, setAnchorElement] = useState<HTMLElement | null>(null);\n const [uncontrolledOpen, setUncontrolledOpen] = useState(Boolean(defaultOpen));\n const open = openProp ?? uncontrolledOpen;\n\n const handleMenuRootRef = useCallback((element: HTMLDivElement | null) => {\n menuRootRef.current = element;\n setMenuRootElement(element);\n }, []);\n\n useLayoutEffect(() => {\n if (!open) {\n setAnchorElement(null);\n return;\n }\n setAnchorElement(\n document.querySelector<HTMLElement>(`[data-ep-display-scale-select=\"${selectId}\"]`),\n );\n }, [open, selectId]);\n\n const handleOpen = useCallback(\n (event: SyntheticEvent) => {\n if (openProp === undefined) setUncontrolledOpen(true);\n onOpen?.(event);\n },\n [onOpen, openProp],\n );\n const handleClose = useCallback(\n (event: SyntheticEvent) => {\n if (openProp === undefined) setUncontrolledOpen(false);\n onClose?.(event);\n },\n [onClose, openProp],\n );\n\n useDisplayScalePopoverPosition({\n open,\n overlayRootRef: menuRootRef,\n overlayRoot: menuRootElement,\n anchorElement,\n anchorOrigin: { vertical: \"bottom\", horizontal: \"center\" },\n transformOrigin: { vertical: \"top\", horizontal: \"center\" },\n });\n\n const menuProps = useMemo((): Partial<MenuProps> => {\n const paperSlot = MenuProps?.slotProps?.paper;\n const resolvedPaperSlot = typeof paperSlot === \"function\" ? undefined : paperSlot;\n\n return {\n ...MenuProps,\n container: rootRef.current ?? document.body,\n // Select mounts its internal Menu only after the first open event.\n // Store the mounted root in state so the position hook runs again once\n // MUI has measured and rendered its paper, rather than only on reopen.\n ref: handleMenuRootRef,\n slotProps: {\n ...MenuProps?.slotProps,\n paper: {\n ...resolvedPaperSlot,\n style: { ...resolvedPaperSlot?.style, zoom: 1 },\n },\n },\n };\n }, [MenuProps, handleMenuRootRef, rootRef]);\n\n return {\n selectProps: {\n ...selectProps,\n open,\n onOpen: handleOpen,\n onClose: handleClose,\n MenuProps: menuProps,\n SelectDisplayProps: {\n ...SelectDisplayProps,\n \"data-ep-display-scale-select\": selectId,\n } as SelectProps<Value>[\"SelectDisplayProps\"],\n },\n };\n}\n\n/** MUI Select wrapper — prefer `useMuiDisplayScaleSelect` when retaining `<Select />`. */\nexport function DisplayScaleSelect<Value = unknown>(props: SelectProps<Value>): ReactElement {\n const { selectProps } = useMuiDisplayScaleSelect(props);\n return <Select<Value> {...selectProps} />;\n}\n","import type { Components, Theme } from \"@mui/material/styles\";\nimport { useCallback, useMemo } from \"react\";\nimport { useDisplayScaleCompensation } from \"./DisplayScaleCompensationProvider\";\nimport { createMuiDisplayScaleComponents } from \"./createMuiDisplayScaleComponents\";\n\n/**\n * React adapter for global MUI overlay defaults. Use inside an existing\n * `DisplayScaleProvider`, then pass the result to `createTheme`.\n */\nexport function useMuiDisplayScaleComponents(): Components<Theme> {\n const { compensation, overlayStrategy, rootRef } = useDisplayScaleCompensation();\n\n const getPortalContainer = useCallback((): HTMLElement => {\n if (overlayStrategy === \"document-body\") return document.body;\n return rootRef.current ?? document.body;\n }, [overlayStrategy, rootRef]);\n\n return useMemo(\n () =>\n createMuiDisplayScaleComponents({\n getPortalContainer,\n overlayZoom: overlayStrategy === \"document-body\" ? compensation.zoom : 1,\n }),\n [compensation.zoom, getPortalContainer, overlayStrategy],\n );\n}\n","import { type RefObject, useLayoutEffect } from \"react\";\nimport { useDisplayScaleCompensation } from \"./DisplayScaleCompensationProvider\";\n\n/**\n * Keeps MUI Tabs' indicator in the compensated root's logical coordinate\n * space. MUI normally uses getBoundingClientRect(), whose values are already\n * zoomed and therefore too small for a logical layout container.\n */\nexport function useDisplayScaleTabsIndicator(\n tabsRef: RefObject<HTMLDivElement | null>,\n value: unknown,\n): void {\n const { compensation } = useDisplayScaleCompensation();\n\n useLayoutEffect(() => {\n if (!compensation.active) return;\n // Re-run after the selected Tabs value changes.\n void value;\n\n const updateIndicator = (): void => {\n const tabs = tabsRef.current;\n const indicator = tabs?.querySelector<HTMLElement>(\".MuiTabs-indicator\");\n const selectedTab = tabs?.querySelector<HTMLElement>(\".MuiTab-root.Mui-selected\");\n if (!tabs || !indicator || !selectedTab) return;\n\n const isRtl = getComputedStyle(tabs).direction === \"rtl\";\n const left = selectedTab.offsetLeft;\n const width = selectedTab.offsetWidth;\n\n indicator.style.width = `${width}px`;\n if (isRtl) {\n indicator.style.left = \"auto\";\n indicator.style.right = `${tabs.offsetWidth - left - width}px`;\n } else {\n indicator.style.right = \"auto\";\n indicator.style.left = `${left}px`;\n }\n };\n\n const frame = window.requestAnimationFrame(updateIndicator);\n const observer = new ResizeObserver(updateIndicator);\n if (tabsRef.current) observer.observe(tabsRef.current);\n window.addEventListener(\"resize\", updateIndicator);\n\n return () => {\n window.cancelAnimationFrame(frame);\n observer.disconnect();\n window.removeEventListener(\"resize\", updateIndicator);\n };\n }, [compensation.active, tabsRef, value]);\n}\n","import type { SxProps, Theme } from \"@mui/material/styles\";\n\n/** Fill the compensated app tree — prefer over raw `100vh` inside `#root`. */\nexport const displayScalePageFillSx: SxProps<Theme> = {\n flex: 1,\n minHeight: 0,\n minWidth: 0,\n width: \"100%\",\n height: \"100%\",\n};\n\n/** Full-viewport auth split layout — fills the compensated app root (not `position: fixed`). */\nexport const signinViewportShellSx: SxProps<Theme> = {\n flex: 1,\n minHeight: 0,\n minWidth: 0,\n width: \"100%\",\n height: \"100%\",\n overflow: \"hidden\",\n boxSizing: \"border-box\",\n};\n\n/** 50/50 desktop columns — avoids flex min-content overflow at OS display scale. */\nexport const signinSplitColumnSx: SxProps<Theme> = {\n flex: { xs: \"0 0 auto\", md: \"1 1 0\" },\n width: { xs: \"100%\", md: \"50%\" },\n maxWidth: { xs: \"100%\", md: \"50%\" },\n minWidth: 0,\n boxSizing: \"border-box\",\n};\n"]}
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import './chunk-SLNYXBHU.js';
2
2
  export { useDisplayScaleOverlay, useLogicalMaxWidth, useLogicalMinWidth } from './chunk-64EBQUAW.js';
3
3
  export { getDisplayScaleLogicalViewport, getDisplayScaleOverlayMaxHeight, toDisplayScaleLogicalPixels } from './chunk-U3ZWHUJG.js';
4
- export { DisplayScaleMenu, DisplayScalePopover, DisplayScalePopper, DisplayScaleSelect, createMuiDisplayScaleComponents, displayScalePageFillSx, signinSplitColumnSx, signinViewportShellSx, useDisplayScalePopoverPosition, useDisplayScaleTabsIndicator, useMuiDisplayScaleComponents, useMuiDisplayScaleMenu, useMuiDisplayScalePopover, useMuiDisplayScalePopper, useMuiDisplayScaleSelect } from './chunk-5MGGPTIJ.js';
4
+ export { DisplayScaleMenu, DisplayScalePopover, DisplayScalePopper, DisplayScaleSelect, createMuiDisplayScaleComponents, displayScalePageFillSx, signinSplitColumnSx, signinViewportShellSx, useDisplayScalePopoverPosition, useDisplayScaleTabsIndicator, useMuiDisplayScaleComponents, useMuiDisplayScaleMenu, useMuiDisplayScalePopover, useMuiDisplayScalePopper, useMuiDisplayScaleSelect } from './chunk-47O3TJUJ.js';
5
5
  export { DisplayScaleCompensationProvider, DisplayScaleProvider, useDisplayScale, useDisplayScaleAnchor, useDisplayScaleCompensation, useDisplayScalePortalContainer } from './chunk-F5AFPMQC.js';
6
6
  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';
7
7
  //# sourceMappingURL=index.js.map
package/dist/mui.d.ts CHANGED
@@ -71,6 +71,12 @@ declare function useMuiDisplayScaleComponents(): Components<Theme>;
71
71
  type DisplayScalePopoverPositionOptions = {
72
72
  open: boolean;
73
73
  overlayRootRef: RefObject<HTMLDivElement | null>;
74
+ /**
75
+ * The mounted overlay root. A ref assignment alone does not trigger a
76
+ * render, so callers with lazily-mounted overlays (notably MUI Select)
77
+ * provide this state value to schedule the first correction after mount.
78
+ */
79
+ overlayRoot?: HTMLDivElement | null;
74
80
  anchorElement: HTMLElement | null;
75
81
  anchorReference?: PopoverProps["anchorReference"];
76
82
  anchorPosition?: PopoverProps["anchorPosition"];
@@ -83,7 +89,7 @@ type DisplayScalePopoverPositionOptions = {
83
89
  * owns focus, transition and anchor semantics; this writes the final logical
84
90
  * top/left inside the compensated portal root.
85
91
  */
86
- declare function useDisplayScalePopoverPosition({ open, overlayRootRef, anchorElement, anchorReference, anchorPosition, anchorOrigin, transformOrigin, marginThreshold, }: DisplayScalePopoverPositionOptions): void;
92
+ declare function useDisplayScalePopoverPosition({ open, overlayRootRef, overlayRoot: mountedOverlayRoot, anchorElement, anchorReference, anchorPosition, anchorOrigin, transformOrigin, marginThreshold, }: DisplayScalePopoverPositionOptions): void;
87
93
 
88
94
  /**
89
95
  * Keeps MUI Tabs' indicator in the compensated root's logical coordinate
package/dist/mui.js CHANGED
@@ -1,4 +1,4 @@
1
- export { DisplayScaleMenu, DisplayScalePopover, DisplayScalePopper, DisplayScaleSelect, createMuiDisplayScaleComponents, displayScalePageFillSx, signinSplitColumnSx, signinViewportShellSx, useDisplayScalePopoverPosition, useDisplayScaleTabsIndicator, useMuiDisplayScaleComponents, useMuiDisplayScaleMenu, useMuiDisplayScalePopover, useMuiDisplayScalePopper, useMuiDisplayScaleSelect } from './chunk-5MGGPTIJ.js';
1
+ export { DisplayScaleMenu, DisplayScalePopover, DisplayScalePopper, DisplayScaleSelect, createMuiDisplayScaleComponents, displayScalePageFillSx, signinSplitColumnSx, signinViewportShellSx, useDisplayScalePopoverPosition, useDisplayScaleTabsIndicator, useMuiDisplayScaleComponents, useMuiDisplayScaleMenu, useMuiDisplayScalePopover, useMuiDisplayScalePopper, useMuiDisplayScaleSelect } from './chunk-47O3TJUJ.js';
2
2
  import './chunk-F5AFPMQC.js';
3
3
  import './chunk-VWJNHWMD.js';
4
4
  //# sourceMappingURL=mui.js.map
@@ -1,208 +1,238 @@
1
- # Display-scale engine
1
+ # Display Scale Engine guide
2
2
 
3
- ## Purpose
3
+ `react-display-scale-engine` is an optional visual-density policy for React applications.
4
+ It targets Windows desktop users whose operating-system display scale is above 100%—for example
5
+ 125% or 150%—when a product requires roughly the same dashboard density as Windows 100%.
4
6
 
5
- The display-scale engine provides an optional **visual-density lock** for Windows desktop browsers.
6
- When Windows display scale is 125%, 150%, or another supported tier, the app can render toward a
7
- 100% visual-density target while retaining an expanded logical layout area.
7
+ It is not a generic replacement for responsive design and it does not force browser zoom.
8
8
 
9
- This is a product policy, not a browser requirement. A conventional responsive website should not
10
- enable this behavior unless matching 100% visual density is an explicit requirement.
9
+ ## What it solves
11
10
 
12
- ## Scope and limitations
11
+ At Windows 150%, a browser usually reports a higher `devicePixelRatio`. A conventional web app
12
+ then renders typography, spacing, and MUI components visibly larger. That is normally correct and
13
+ accessible browser behavior, but dense desktop dashboards sometimes require a consistent visual
14
+ density across managed workstations.
13
15
 
14
- - The engine targets Windows desktop and is disabled for coarse-pointer and compact viewports.
15
- - Browser page zoom and OS display scale are not perfectly distinguishable in a normal browser.
16
- `window.devicePixelRatio` is an effective value influenced by both.
17
- - The session baseline assumes browser page zoom is 100% when first stored. If this is not true,
18
- call `recalibrateDisplayScale()` after the user resets page zoom to 100%.
19
- - Native shells such as Electron can provide a more authoritative OS scale factor. A pure web app
20
- cannot.
16
+ Applying CSS `zoom` to an app root corrects that density, but creates a second coordinate system.
17
+ Anchored overlays such as MUI `Menu`, `Popover`, `Popper`, `Select`, `Dialog`, and temporary
18
+ `Drawer` can drift, clip, or calculate a wrong viewport height. This engine manages the root
19
+ compensation and the overlay geometry together.
21
20
 
22
- ## Architecture
21
+ ## When it has an effect
22
+
23
+ | Environment | Engine result |
24
+ | --- | --- |
25
+ | Windows desktop at 100% display scale | Inactive: no compensation wrapper and no visual change. |
26
+ | Windows desktop at 125%, 150%, and supported higher tiers | Active: the app root is compensated toward the configured target density. |
27
+ | macOS / Linux | Inactive by default. |
28
+ | Touch/coarse-pointer or compact viewport | Inactive by default. |
29
+ | Browser zoom differs from the recorded baseline | Best-effort detection; use calibration for a strict policy. |
30
+
31
+ When inactive, `DisplayScaleProvider` supplies context to hooks only. It does not insert a layout
32
+ container or apply visual scaling, so a normal display scale preserves the host app's layout.
33
+
34
+ ## Engine model
23
35
 
24
36
  ```text
25
- display-scale core
26
- detection + calibration + compensation policy
27
- |
28
- v
29
- React provider
30
- compensated root (visible portal host) + logical viewport context
31
- |
32
- +-- clipped app-content layer
33
- +-- document-body MUI portal host
34
- +-- overlay adapter
37
+ Browser metrics + session baseline
38
+
39
+
40
+ /core detection
41
+ policy + compensation
42
+
43
+
44
+ /react DisplayScaleProvider
45
+ active: compensated root + logical viewport
46
+ inactive: context only
47
+
48
+
49
+ /mui integration
50
+ corrected anchor + portal + final overlay placement
35
51
  ```
36
52
 
37
- The code is intentionally divided so that the core calculation modules do not depend on React or
38
- MUI. The React-facing API is exported from `@/shared/display-scale` today and can be moved into
39
- an internal package without changing feature call sites.
53
+ A web browser cannot perfectly separate OS display scale from browser page zoom. The engine uses
54
+ `devicePixelRatio`, `visualViewport`, and a session baseline to form a best-effort estimate. For a
55
+ strict product policy, provide an explicit action after users reset browser zoom to 100%.
40
56
 
41
- ## Public API
57
+ ## Best-practice quick start
42
58
 
43
- ```tsx
44
- import {
45
- DisplayScaleProvider,
46
- useDisplayScale,
47
- useDisplayScaleOverlay,
48
- } from "@/shared/display-scale";
49
-
50
- <DisplayScaleProvider
51
- config={{
52
- windowsOnly: true,
53
- targetOsScale: 1,
54
- minOsScaleToCompensate: 1.05,
55
- requireExplicitCalibration: true,
56
- }}
57
- >
58
- <App />
59
- </DisplayScaleProvider>;
59
+ Use the subpath imports below. The root package export exists only for compatibility.
60
+
61
+ ### 1. Install
62
+
63
+ ```bash
64
+ npm install react-display-scale-engine
60
65
  ```
61
66
 
62
- ### Core utilities
63
-
64
- - `detectDisplayScale()`: returns current DPR, estimated OS scale, estimated browser zoom and
65
- confidence.
66
- - `recalibrateDisplayScale()`: replaces the current session baseline when browser zoom is known
67
- to be 100% and marks it as explicit calibration.
68
- - `computeDisplayScaleCompensation()`: resolves whether compensation is active and the target
69
- root zoom.
70
- - `getDisplayScaleLogicalViewport()`: returns browser viewport and compensated-root viewport.
71
- - `toDisplayScaleLogicalPixels()`: converts visible CSS-pixel geometry to root coordinates.
72
-
73
- ### React utilities
74
-
75
- - `DisplayScaleProvider`: package-friendly name for the existing root provider.
76
- - `useDisplayScale()`: subscribes to a scale snapshot.
77
- - `useDisplayScaleCompensation()`: exposes the root and resolved compensation.
78
- - `useDisplayScaleOverlay()`: obtains the portal container, logical viewport and overlay bounds
79
- helpers, including `toOverlayContentPixels()` for a custom density-zoomed dimension.
80
- - `useDisplayScaleAnchor()`: converts a native anchor to a MUI-compatible virtual anchor in the
81
- compensated portal coordinate space.
82
- - `useLogicalMinWidth()` / `useLogicalMaxWidth()`: evaluate app-shell breakpoints against the
83
- compensated root width.
84
- - `createMuiDisplayScaleComponents()`: optional MUI adapter that centralizes portal defaults for
85
- Dialog, Menu/Popover and Popper.
86
-
87
- ## Overlay integration
88
-
89
- MUI overlays must share the same coordinate system as their anchor. The theme adapter keeps legacy
90
- MUI portals on `document.body` with density correction. For anchored `Menu`, `Popover` and
91
- `Popper`, use the provided wrappers: they move only that overlay into the compensated root and
92
- adapt the anchor rectangle relative to the engine root centrally. This avoids the `top`/`left`
93
- drift caused by applying CSS zoom to an absolutely positioned body portal. The adapter also handles temporary MUI Drawers: the
94
- portal host, density zoom, and inverse viewport dimension are set globally. Permanent and
95
- persistent Drawers remain inside the compensated root and are deliberately untouched.
96
-
97
- The compensated root is intentionally a visible portal host. The app content sits in a clipped
98
- child layer, so a portalled overlay is bounded by the real viewport rather than an invisible
99
- `overflow: hidden` root rectangle.
100
-
101
-
102
- Use `overlayStrategy="compensated-root"` only for an app that has verified every portal and
103
- anchor in logical root coordinates. `document-body` is preferred for CSS zoom roots.
104
-
105
- Do not divide a Popper or Menu height by `compensation.zoom` in feature code. Use the overlay
106
- adapter instead; it uses visible pixels for placement and converts only an overlay content
107
- dimension when its density zoom requires it:
67
+ ### 2. Prime once before React renders
68
+
69
+ In the application entry file, before `createRoot`:
108
70
 
109
71
  ```tsx
110
- const { getMaxHeightBelowAnchor } = useDisplayScaleOverlay();
72
+ import { primeDisplayScaleCompensation } from "react-display-scale-engine/core";
111
73
 
112
- const maxHeight = getMaxHeightBelowAnchor(anchorElement, {
113
- offset: 8,
114
- viewportInset: 16,
115
- });
74
+ primeDisplayScaleCompensation();
75
+ ```
76
+
77
+ ### 3. Add exactly one provider at the application root
78
+
79
+ ```tsx
80
+ import { DisplayScaleProvider } from "react-display-scale-engine/react";
81
+
82
+ export function App() {
83
+ return (
84
+ <DisplayScaleProvider>
85
+ <AppContent />
86
+ </DisplayScaleProvider>
87
+ );
88
+ }
116
89
  ```
117
90
 
118
- For MUI `Menu`, `Popover` and `Popper`, adapt the anchor before passing it to the component:
91
+ Do not add a provider around individual features, dialogs, or pages.
92
+
93
+ ### 4. Add the MUI theme adapter once
94
+
95
+ Call this hook inside `DisplayScaleProvider`, then merge its result into the existing MUI theme.
119
96
 
120
97
  ```tsx
121
- const [anchorEl, setAnchorEl] = useState<HTMLElement | null>(null);
122
- const displayScaleAnchor = useDisplayScaleAnchor(anchorEl);
98
+ import { CssBaseline, ThemeProvider, createTheme } from "@mui/material";
99
+ import { useMemo } from "react";
100
+ import { useMuiDisplayScaleComponents } from "react-display-scale-engine/mui";
101
+
102
+ function AppThemeProvider({ children }: { children: React.ReactNode }) {
103
+ const components = useMuiDisplayScaleComponents();
104
+ const theme = useMemo(() => createTheme({ components }), [components]);
105
+
106
+ return (
107
+ <ThemeProvider theme={theme}>
108
+ <CssBaseline />
109
+ {children}
110
+ </ThemeProvider>
111
+ );
112
+ }
113
+ ```
114
+
115
+ For an existing theme, use `createTheme(baseTheme, { components })`; do not create a second theme
116
+ provider only for this engine.
123
117
 
124
- <Menu anchorEl={displayScaleAnchor} open={Boolean(anchorEl)} />;
118
+ ### 5. Use hook adapters for anchored MUI components
119
+
120
+ This is the recommended API for new code. Your app retains native MUI components and supplies
121
+ engine-managed props through the matching hook.
122
+
123
+ ```tsx
124
+ import { Menu, MenuItem } from "@mui/material";
125
+ import { useState } from "react";
126
+ import { useMuiDisplayScaleMenu } from "react-display-scale-engine/mui";
127
+
128
+ function ActionsMenu() {
129
+ const [anchorEl, setAnchorEl] = useState<HTMLElement | null>(null);
130
+ const open = Boolean(anchorEl);
131
+ const { menuProps } = useMuiDisplayScaleMenu({ anchorEl, open });
132
+
133
+ return (
134
+ <Menu {...menuProps} onClose={() => setAnchorEl(null)}>
135
+ <MenuItem onClick={() => setAnchorEl(null)}>Action</MenuItem>
136
+ </Menu>
137
+ );
138
+ }
125
139
  ```
126
140
 
127
- For new code, prefer the MUI wrappers so the conversion cannot be skipped:
141
+ | Native MUI component | Engine hook |
142
+ | --- | --- |
143
+ | `Menu` | `useMuiDisplayScaleMenu` |
144
+ | `Popover` | `useMuiDisplayScalePopover` |
145
+ | `Popper` | `useMuiDisplayScalePopper` |
146
+ | `Select` | `useMuiDisplayScaleSelect` |
147
+
148
+ The hook owns display-scale-specific props only: corrected anchor geometry, portal container,
149
+ placement correction, zoom-aware modifiers, and internal overlay refs. Keep business state,
150
+ children, item rendering, `onClose`, and `onChange` in the application.
151
+
152
+ ## Wrapper API
153
+
154
+ For a fast migration, wrappers remain available:
128
155
 
129
156
  ```tsx
130
- import { DisplayScaleMenu, DisplayScalePopover, DisplayScalePopper } from "@/shared/display-scale";
157
+ import { DisplayScaleMenu } from "react-display-scale-engine/mui";
131
158
 
132
- <DisplayScaleMenu anchorEl={anchorEl} open={Boolean(anchorEl)} />;
159
+ <DisplayScaleMenu anchorEl={anchorEl} open={open} onClose={handleClose}>
160
+ <MenuItem>Action</MenuItem>
161
+ </DisplayScaleMenu>;
133
162
  ```
134
163
 
135
- The wrappers accept a native `HTMLElement` anchor, and also support a Menu using
136
- `anchorReference="anchorPosition"`. Use the lower-level hook only for components that require a
137
- custom virtual anchor.
164
+ `DisplayScaleMenu`, `DisplayScalePopover`, `DisplayScalePopper`, and `DisplayScaleSelect` use the
165
+ same hook adapters internally. Hook adapters and wrappers therefore have equivalent placement and
166
+ scaling behavior. Prefer hooks for new code; use wrappers when migrating a feature quickly.
167
+
168
+ ## Custom overlay content
169
+
170
+ For a custom panel or scrollable list, use the logical overlay helper instead of dividing values
171
+ by `zoom` manually:
138
172
 
139
- For engine wrappers, MUI still performs its initial placement. The engine then replaces only the
140
- final collision clamp with a logical-viewport calculation, preserving MUI focus handling,
141
- transitions and origin semantics. `Popper` is intentionally separate because it uses Popper.js
142
- modifiers rather than MUI Popover's collision implementation.
173
+ ```tsx
174
+ import { useDisplayScaleOverlay } from "react-display-scale-engine/react";
175
+
176
+ const { getMaxHeightBelowAnchor } = useDisplayScaleOverlay();
177
+ const maxHeight = getMaxHeightBelowAnchor(anchorElement, {
178
+ offset: 8,
179
+ viewportInset: 16,
180
+ });
181
+ ```
143
182
 
144
- For custom positioning:
183
+ Apply `maxHeight` to the scrollable surface, usually with `overflowY: "auto"` and `minHeight: 0`.
145
184
 
146
- 1. Read the anchor rectangle with `getBoundingClientRect()`.
147
- 2. Let `useDisplayScaleOverlay()` convert geometry only when the active portal strategy needs it.
148
- 3. Keep the anchor, portal host and overlay in the same strategy.
149
- 4. Give scrollable content `minHeight: 0` and put `overflowY: "auto"` on the list surface.
150
- 5. Prefer Popper modifiers (`offset`, `flip`, `preventOverflow`) over hard-coded
151
- `position: fixed`, `top` or `right`.
185
+ ## Responsive behavior
152
186
 
153
- ## Responsive guidance
187
+ Density and responsive layout are different concerns. Browser media queries still use the raw CSS
188
+ viewport. When an application-shell breakpoint must stay stable between Windows 100% and 150%, use
189
+ the logical breakpoint hooks:
154
190
 
155
- Visual density and responsive behavior are separate:
191
+ ```tsx
192
+ import { useLogicalMinWidth } from "react-display-scale-engine/react";
156
193
 
157
- - **Density** controls perceived font, component and spacing size.
158
- - **Responsive behavior** controls breakpoints, columns and navigation variants.
159
- - **Overlay geometry** controls portals and viewport bounds.
194
+ const isDesktop = useLogicalMinWidth(900);
195
+ ```
160
196
 
161
- Browser-level media queries still observe the browser CSS viewport. Features that must preserve the
162
- same breakpoint behavior at 100% and 150% should use container queries within the compensated root
163
- or `useLogicalMinWidth()` / `useLogicalMaxWidth()` for JavaScript app-shell decisions. Do not
164
- assume root `zoom` changes `window.matchMedia()`.
197
+ Do not use the engine to make a mobile layout behave as desktop.
165
198
 
166
- ## Migration plan for another React app
199
+ ## Calibration and diagnostics
167
200
 
168
- 1. Copy or package only `displayScaleCompensation`, `detectDisplayScale`, baseline storage,
169
- types and logical viewport utilities as the core layer.
170
- 2. Add `DisplayScaleProvider` around the new app shell.
171
- 3. Configure the app's portal system with `createMuiDisplayScaleComponents()`.
172
- 4. Migrate overlays first, then fixed-position pages, then breakpoint-sensitive layouts.
173
- 5. For strict density lock, set `requireExplicitCalibration: true` and call
174
- `recalibrateDisplayScale()` after the app confirms that browser zoom is 100%.
201
+ The default automatic baseline is suitable for most applications. For strict calibration, call this
202
+ only when browser zoom is known to be 100%:
175
203
 
176
- Suggested future package structure:
204
+ ```tsx
205
+ import { recalibrateDisplayScale } from "react-display-scale-engine/core";
177
206
 
178
- ```text
179
- @envidi/display-scale-core
180
- @envidi/display-scale-react
181
- @envidi/display-scale-mui
207
+ recalibrateDisplayScale();
182
208
  ```
183
209
 
184
- The MUI package should remain optional; core must not import MUI.
210
+ `useDisplayScale()` exposes the current estimate, confidence, browser zoom, and OS scale for a
211
+ diagnostics or support screen. Avoid changing feature layout directly from raw scale values.
185
212
 
186
- ## Verification matrix
213
+ ## Rules that prevent overlay bugs
187
214
 
188
- Run the following with browser page zoom at 100%, then repeat at 80%, 125% and 150%:
215
+ 1. Use one provider for one application root.
216
+ 2. Import by responsibility: `/core`, `/react`, and `/mui`.
217
+ 3. Use an engine hook or wrapper for every anchored MUI overlay; do not manually divide coordinates.
218
+ 4. Keep anchor, portal, and overlay under the same engine strategy.
219
+ 5. Avoid hard-coded `position: fixed`, `top`, or `left` as an overlay repair.
220
+ 6. Test Windows 100%, 125%, and 150% with browser zoom at 100%.
189
221
 
190
- | Windows display scale | Required result |
191
- | --- | --- |
192
- | 100% | No compensation; baseline visual layout |
193
- | 125% | Target visual density; no overlay clipping |
194
- | 150% | Target visual density; no overlay drift or viewport overflow |
195
- | Multiple monitors | Re-read scale after the window moves; recalibrate if confidence is insufficient |
196
-
197
- Check at minimum: app shell, sidebar animation, dialogs, menus, Popper lists, fixed controls,
198
- virtualized or scrollable lists, responsive grids and sign-in.
199
-
200
- ## Current known work
201
-
202
- - Factory selector uses `useDisplayScaleOverlay()` for viewport-bounded list height.
203
- - MUI portals are globally routed to `document.body` by default; the theme adapter applies the
204
- matching overlay density there, avoiding coordinate drift from the compensated root.
205
- - The app shell and sidebar profile already use logical breakpoints. Dashboard grids and other
206
- feature-level breakpoint consumers remain a staged migration.
207
- - The engine currently uses best-effort browser-zoom detection; explicit calibration UX remains a
208
- follow-up before enabling strict density lock across other apps.
222
+ ## Compatibility and release checks
223
+
224
+ - `/core` is framework-independent.
225
+ - `/react` requires React 18 or later.
226
+ - `/mui` is tested with MUI 7 (`>=7 <8`) and Emotion peers.
227
+ - The root export is a compatibility entry; prefer subpaths for new application code.
228
+
229
+ Before publishing, run:
230
+
231
+ ```bash
232
+ npm run check
233
+ npm run release:dry-run
234
+ ```
235
+
236
+ `npm run check` type-checks, builds distributable artifacts, and runs a DOM consumer test. It
237
+ renders root and subpath APIs, wrappers, and native MUI components receiving hook adapter props.
238
+ This catches React Context duplication and normal-scale wrapper regressions before publishing.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-display-scale-engine",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "description": "React and Material UI utilities for consistent Windows display-scale rendering.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/createMuiDisplayScaleComponents.ts","../src/useDisplayScalePopoverPosition.ts","../src/DisplayScaleMuiOverlays.tsx","../src/DisplayScaleSelect.tsx","../src/useMuiDisplayScaleComponents.ts","../src/useDisplayScaleTabsIndicator.ts","../src/displayScaleLayoutTokens.ts"],"names":["useRef","useLayoutEffect","useCallback","useMemo","jsx"],"mappings":";;;;;;;AAmBO,SAAS,+BAAA,CAAgC;AAAA,EAC9C,kBAAA;AAAA,EACA,WAAA,GAAc;AAChB,CAAA,EAAwD;AACtD,EAAA,MAAM,IAAA,GAAO,WAAA,GAAc,CAAA,GAAI,WAAA,GAAc,CAAA;AAC7C,EAAA,MAAM,oBAAA,GAAuB,CAAC,SAAA,KAA+B;AAC3D,IAAA,IAAI,OAAO,QAAA,KAAa,WAAA,EAAa,OAAO,IAAA;AAC5C,IAAA,MAAM,iBAAA,GACJ,OAAO,SAAA,KAAc,UAAA,GAAc,WAAoC,GAAI,SAAA;AAG7E,IAAA,OAAO,iBAAA,IAAqB,IAAA,IAAQ,iBAAA,KAAsB,QAAA,CAAS,OAAO,IAAA,GAAO,CAAA;AAAA,EACnF,CAAA;AAEA,EAAA,OAAO;AAAA,IACL,QAAA,EAAU;AAAA,MACR,YAAA,EAAc;AAAA,QACZ,SAAA,EAAW;AAAA;AACb,KACF;AAAA,IACA,SAAA,EAAW;AAAA,MACT,YAAA,EAAc;AAAA,QACZ,SAAA,EAAW;AAAA,OACb;AAAA,MACA,cAAA,EAAgB;AAAA,QACd,KAAA,EAAO,CAAC,EAAE,UAAA,EAAW,MAAO,EAAE,IAAA,EAAM,oBAAA,CAAqB,UAAA,EAAY,SAAS,CAAA,EAAE;AAAA;AAClF,KACF;AAAA,IACA,UAAA,EAAY;AAAA,MACV,YAAA,EAAc;AAAA,QACZ,SAAA,EAAW;AAAA,OACb;AAAA,MACA,cAAA,EAAgB;AAAA,QACd,KAAA,EAAO,CAAC,EAAE,UAAA,EAAW,MAAO,EAAE,IAAA,EAAM,oBAAA,CAAqB,UAAA,EAAY,SAAS,CAAA,EAAE;AAAA;AAClF,KACF;AAAA,IACA,SAAA,EAAW;AAAA,MACT,YAAA,EAAc;AAAA,QACZ,SAAA,EAAW;AAAA,OACb;AAAA,MACA,cAAA,EAAgB;AAAA,QACd,IAAA,EAAM,CAAC,EAAE,UAAA,EAAW,MAAO,EAAE,IAAA,EAAM,oBAAA,CAAqB,UAAA,EAAY,SAAS,CAAA,EAAE;AAAA;AACjF,KACF;AAAA,IACA,SAAA,EAAW;AAAA,MACT,YAAA,EAAc;AAAA;AAAA;AAAA,QAGZ,SAAA,EAAW;AAAA,OACb;AAAA,MACA,cAAA,EAAgB;AAAA,QACd,KAAA,EAAO,CAAC,EAAE,UAAA,EAAW,KAAM;AAIzB,UAAA,IAAI,UAAA,CAAW,OAAA,KAAY,WAAA,EAAa,OAAO,EAAC;AAEhD,UAAA,MAAM,UAAA,GAAa,UAAA,CAAW,MAAA,KAAW,MAAA,IAAU,WAAW,MAAA,KAAW,OAAA;AACzE,UAAA,OAAO,UAAA,GACH;AAAA,YACE,IAAA;AAAA,YACA,MAAA,EAAQ,yDAAA;AAAA,YACR,SAAA,EAAW;AAAA,WACb,GACA;AAAA,YACE,IAAA;AAAA,YACA,KAAA,EAAO,wDAAA;AAAA,YACP,QAAA,EAAU;AAAA,WACZ;AAAA,QACN;AAAA;AACF;AACF,GACF;AACF;AC7EA,SAAS,aAAA,CACP,QACA,IAAA,EACQ;AACR,EAAA,IAAI,OAAO,MAAA,KAAW,QAAA,EAAU,OAAO,MAAA;AACvC,EAAA,IAAI,MAAA,KAAW,QAAA,EAAU,OAAO,IAAA,GAAO,CAAA;AACvC,EAAA,IAAI,MAAA,KAAW,QAAA,IAAY,MAAA,KAAW,OAAA,EAAS,OAAO,IAAA;AACtD,EAAA,OAAO,CAAA;AACT;AAEA,SAAS,eACP,YAAA,EACA,eAAA,EACA,WAAA,EACA,YAAA,EACA,YACA,WAAA,EAC8E;AAC9E,EAAA,MAAM,uBAAuB,YAAA,IAAgB,EAAE,QAAA,EAAU,KAAA,EAAO,YAAY,MAAA,EAAO;AACnF,EAAA,MAAM,0BAA0B,eAAA,IAAmB,EAAE,QAAA,EAAU,KAAA,EAAO,YAAY,MAAA,EAAO;AAEzF,EAAA,OAAO;AAAA,IACL,OAAA,EAAS,aAAA,CAAc,oBAAA,CAAqB,UAAA,EAAY,WAAW,CAAA;AAAA,IACnE,OAAA,EAAS,aAAA,CAAc,oBAAA,CAAqB,QAAA,EAAU,YAAY,CAAA;AAAA,IAClE,UAAA,EAAY,aAAA,CAAc,uBAAA,CAAwB,UAAA,EAAY,UAAU,CAAA;AAAA,IACxE,UAAA,EAAY,aAAA,CAAc,uBAAA,CAAwB,QAAA,EAAU,WAAW;AAAA,GACzE;AACF;AAOO,SAAS,8BAAA,CAA+B;AAAA,EAC7C,IAAA;AAAA,EACA,cAAA;AAAA,EACA,aAAA;AAAA,EACA,eAAA;AAAA,EACA,cAAA;AAAA,EACA,YAAA;AAAA,EACA,eAAA;AAAA,EACA,eAAA,GAAkB;AACpB,CAAA,EAA6C;AAC3C,EAAA,MAAM,EAAE,YAAA,EAAc,aAAA,EAAe,OAAA,KAAY,2BAAA,EAA4B;AAE7E,EAAA,MAAM,eAAA,GAAkB,YAAY,MAAM;AACxC,IAAA,IAAI,CAAC,IAAA,IAAQ,CAAC,YAAA,CAAa,MAAA,IAAU,CAAC,aAAA,EAAe;AAErD,IAAA,MAAM,cAAc,cAAA,CAAe,OAAA;AACnC,IAAA,MAAM,KAAA,GAAQ,WAAA,EAAa,aAAA,CAA2B,mBAAmB,CAAA;AACzE,IAAA,IAAI,CAAC,KAAA,EAAO;AAEZ,IAAA,MAAM,IAAA,GAAO,YAAA,CAAa,IAAA,GAAO,CAAA,GAAI,aAAa,IAAA,GAAO,CAAA;AACzD,IAAA,MAAM,QAAA,GAAW,OAAA,CAAQ,OAAA,EAAS,qBAAA,EAAsB;AACxD,IAAA,MAAM,QAAA,GAAW,UAAU,IAAA,IAAQ,CAAA;AACnC,IAAA,MAAM,OAAA,GAAU,UAAU,GAAA,IAAO,CAAA;AACjC,IAAA,MAAM,UAAA,GAAa,eAAe,qBAAA,EAAsB;AACxD,IAAA,MAAM,OAAA,GACJ,eAAA,KAAoB,gBAAA,IAAoB,cAAA,GAAA,CACnC,cAAA,CAAe,IAAA,GAAO,QAAA,IAAY,IAAA,GACnC,UAAA,GAAA,CACG,UAAA,CAAW,IAAA,GAAO,QAAA,IAAY,IAAA,GAC/B,IAAA;AACR,IAAA,MAAM,OAAA,GACJ,eAAA,KAAoB,gBAAA,IAAoB,cAAA,GAAA,CACnC,cAAA,CAAe,GAAA,GAAM,OAAA,IAAW,IAAA,GACjC,UAAA,GAAA,CACG,UAAA,CAAW,GAAA,GAAM,OAAA,IAAW,IAAA,GAC7B,IAAA;AACR,IAAA,IAAI,OAAA,IAAW,IAAA,IAAQ,OAAA,IAAW,IAAA,EAAM;AAExC,IAAA,MAAM,aAAa,KAAA,CAAM,WAAA;AACzB,IAAA,MAAM,cAAc,KAAA,CAAM,YAAA;AAC1B,IAAA,MAAM;AAAA,MACJ,OAAA,EAAS,OAAA;AAAA,MACT,OAAA,EAAS,OAAA;AAAA,MACT,UAAA;AAAA,MACA;AAAA,KACF,GAAI,cAAA;AAAA,MACF,YAAA;AAAA,MACA,eAAA;AAAA,MACA,UAAA,GAAa,UAAA,CAAW,KAAA,GAAQ,IAAA,GAAO,CAAA;AAAA,MACvC,UAAA,GAAa,UAAA,CAAW,MAAA,GAAS,IAAA,GAAO,CAAA;AAAA,MACxC,UAAA;AAAA,MACA;AAAA,KACF;AACA,IAAA,MAAM,MAAA,GAAS,OAAO,eAAA,KAAoB,QAAA,GAAW,eAAA,GAAkB,EAAA;AACvE,IAAA,MAAM,UAAU,IAAA,CAAK,GAAA,CAAI,QAAQ,aAAA,CAAc,aAAA,GAAgB,SAAS,UAAU,CAAA;AAClF,IAAA,MAAM,SAAS,IAAA,CAAK,GAAA,CAAI,QAAQ,aAAA,CAAc,cAAA,GAAiB,SAAS,WAAW,CAAA;AACnF,IAAA,MAAM,IAAA,GAAO,IAAA,CAAK,GAAA,CAAI,OAAA,EAAS,IAAA,CAAK,IAAI,MAAA,EAAQ,OAAA,GAAU,OAAA,GAAU,UAAU,CAAC,CAAA;AAC/E,IAAA,MAAM,GAAA,GAAM,IAAA,CAAK,GAAA,CAAI,MAAA,EAAQ,IAAA,CAAK,IAAI,MAAA,EAAQ,OAAA,GAAU,OAAA,GAAU,UAAU,CAAC,CAAA;AAE7E,IAAA,KAAA,CAAM,MAAM,IAAA,GAAO,CAAA,EAAG,IAAA,CAAK,KAAA,CAAM,IAAI,CAAC,CAAA,EAAA,CAAA;AACtC,IAAA,KAAA,CAAM,MAAM,GAAA,GAAM,CAAA,EAAG,IAAA,CAAK,KAAA,CAAM,GAAG,CAAC,CAAA,EAAA,CAAA;AAAA,EACtC,CAAA,EAAG;AAAA,IACD,aAAA;AAAA,IACA,YAAA;AAAA,IACA,cAAA;AAAA,IACA,eAAA;AAAA,IACA,YAAA,CAAa,MAAA;AAAA,IACb,YAAA,CAAa,IAAA;AAAA,IACb,aAAA;AAAA,IACA,eAAA;AAAA,IACA,IAAA;AAAA,IACA,cAAA;AAAA,IACA,OAAA;AAAA,IACA;AAAA,GACD,CAAA;AAED,EAAA,eAAA,CAAgB,MAAM;AACpB,IAAA,IAAI,CAAC,IAAA,EAAM;AAEX,IAAA,MAAM,UAAA,GAAa,MAAA,CAAO,qBAAA,CAAsB,MAAM;AACpD,MAAA,eAAA,EAAgB;AAChB,MAAA,MAAA,CAAO,sBAAsB,eAAe,CAAA;AAAA,IAC9C,CAAC,CAAA;AACD,IAAA,MAAA,CAAO,gBAAA,CAAiB,UAAU,eAAe,CAAA;AACjD,IAAA,MAAA,CAAO,gBAAA,CAAiB,QAAA,EAAU,eAAA,EAAiB,IAAI,CAAA;AAEvD,IAAA,OAAO,MAAM;AACX,MAAA,MAAA,CAAO,qBAAqB,UAAU,CAAA;AACtC,MAAA,MAAA,CAAO,mBAAA,CAAoB,UAAU,eAAe,CAAA;AACpD,MAAA,MAAA,CAAO,mBAAA,CAAoB,QAAA,EAAU,eAAA,EAAiB,IAAI,CAAA;AAAA,IAC5D,CAAA;AAAA,EACF,CAAA,EAAG,CAAC,eAAA,EAAiB,IAAI,CAAC,CAAA;AAC5B;ACrHO,SAAS,sBAAA,CAAuB;AAAA,EACrC,QAAA;AAAA,EACA,GAAG;AACL,CAAA,EAAsD;AACpD,EAAA,MAAM,kBAAA,GAAqB,qBAAA,CAAsB,QAAA,IAAY,IAAI,CAAA;AACjE,EAAA,MAAM,EAAE,YAAA,EAAc,OAAA,EAAQ,GAAI,2BAAA,EAA4B;AAC9D,EAAA,MAAM,QAAQ,QAAA,EAAS;AACvB,EAAA,MAAM,cAAA,GAAiB,OAAuB,IAAI,CAAA;AAClD,EAAA,MAAM,gBAAA,GAAmB,KAAA,CAAM,SAAA,KAAc,KAAA,GAAQ,OAAA,GAAU,MAAA;AAC/D,EAAA,MAAM,YAAA,GAAe,MAAM,YAAA,IAAgB;AAAA,IACzC,QAAA,EAAU,QAAA;AAAA,IACV,UAAA,EAAY;AAAA,GACd;AACA,EAAA,MAAM,eAAA,GAAkB,MAAM,eAAA,IAAmB;AAAA,IAC/C,QAAA,EAAU,KAAA;AAAA,IACV,UAAA,EAAY;AAAA,GACd;AACA,EAAA,MAAM,cAAA,GAAiB,QAAQ,MAAM;AACnC,IAAA,IAAI,KAAA,CAAM,eAAA,KAAoB,gBAAA,IAAoB,CAAC,MAAM,cAAA,EAAgB;AACvE,MAAA,OAAO,KAAA,CAAM,cAAA;AAAA,IACf;AACA,IAAA,MAAM,IAAA,GAAO,YAAA,CAAa,IAAA,GAAO,CAAA,GAAI,aAAa,IAAA,GAAO,CAAA;AACzD,IAAA,OAAO;AAAA,MACL,GAAA,EAAK,KAAA,CAAM,cAAA,CAAe,GAAA,GAAM,IAAA;AAAA,MAChC,IAAA,EAAM,KAAA,CAAM,cAAA,CAAe,IAAA,GAAO;AAAA,KACpC;AAAA,EACF,CAAA,EAAG,CAAC,YAAA,CAAa,IAAA,EAAM,MAAM,cAAA,EAAgB,KAAA,CAAM,eAAe,CAAC,CAAA;AACnE,EAAA,8BAAA,CAA+B;AAAA,IAC7B,MAAM,KAAA,CAAM,IAAA;AAAA,IACZ,cAAA;AAAA,IACA,eAAe,QAAA,IAAY,IAAA;AAAA,IAC3B,iBAAiB,KAAA,CAAM,eAAA;AAAA,IACvB,gBAAgB,KAAA,CAAM,cAAA;AAAA,IACtB,YAAA;AAAA,IACA,eAAA;AAAA,IACA,iBAAiB,KAAA,CAAM;AAAA,GACxB,CAAA;AAED,EAAA,OAAO;AAAA,IACL,SAAA,EAAW;AAAA,MACT,GAAG,KAAA;AAAA,MACH,GAAA,EAAK,cAAA;AAAA,MACL,QAAA,EAAU,kBAAA;AAAA,MACV,cAAA;AAAA,MACA,YAAA;AAAA,MACA,SAAA,EAAW,OAAA,CAAQ,OAAA,IAAW,QAAA,CAAS,IAAA;AAAA,MACvC;AAAA;AACF,GACF;AACF;AAGO,SAAS,iBAAiB,KAAA,EAA4C;AAC3E,EAAA,MAAM,EAAE,SAAA,EAAU,GAAI,sBAAA,CAAuB,KAAK,CAAA;AAClD,EAAA,uBAAO,GAAA,CAAC,IAAA,EAAA,EAAM,GAAG,SAAA,EAAW,CAAA;AAC9B;AAUO,SAAS,yBAAA,CAA0B;AAAA,EACxC,QAAA;AAAA,EACA,GAAG;AACL,CAAA,EAA4D;AAC1D,EAAA,MAAM,kBAAA,GAAqB,qBAAA,CAAsB,QAAA,IAAY,IAAI,CAAA;AACjE,EAAA,MAAM,EAAE,OAAA,EAAQ,GAAI,2BAAA,EAA4B;AAChD,EAAA,MAAM,cAAA,GAAiB,OAAuB,IAAI,CAAA;AAClD,EAAA,8BAAA,CAA+B;AAAA,IAC7B,MAAM,KAAA,CAAM,IAAA;AAAA,IACZ,cAAA;AAAA,IACA,eAAe,QAAA,IAAY,IAAA;AAAA,IAC3B,iBAAiB,KAAA,CAAM,eAAA;AAAA,IACvB,gBAAgB,KAAA,CAAM,cAAA;AAAA,IACtB,cAAc,KAAA,CAAM,YAAA;AAAA,IACpB,iBAAiB,KAAA,CAAM,eAAA;AAAA,IACvB,iBAAiB,KAAA,CAAM;AAAA,GACxB,CAAA;AACD,EAAA,OAAO;AAAA,IACL,YAAA,EAAc;AAAA,MACZ,GAAG,KAAA;AAAA,MACH,GAAA,EAAK,cAAA;AAAA,MACL,QAAA,EAAU,kBAAA;AAAA,MACV,SAAA,EAAW,OAAA,CAAQ,OAAA,IAAW,QAAA,CAAS;AAAA;AACzC,GACF;AACF;AAGO,SAAS,oBAAoB,KAAA,EAA+C;AACjF,EAAA,MAAM,EAAE,YAAA,EAAa,GAAI,yBAAA,CAA0B,KAAK,CAAA;AACxD,EAAA,uBAAO,GAAA,CAAC,OAAA,EAAA,EAAS,GAAG,YAAA,EAAc,CAAA;AACpC;AASA,IAAM,kCAAA,GAA+D;AAAA,EACnE;AAAA,IACE,IAAA,EAAM,QAAA;AAAA,IACN,SAAS,EAAE,MAAA,EAAQ,CAAC,CAAA,EAAG,CAAC,CAAA;AAAE;AAE9B,CAAA;AAEA,SAAS,kBAAA,CACP,WACA,IAAA,EAC0B;AAC1B,EAAA,IAAI,CAAC,SAAA,IAAa,IAAA,KAAS,CAAA,EAAG,OAAO,SAAA;AAErC,EAAA,OAAO,SAAA,CAAU,GAAA,CAAI,CAAC,QAAA,KAAa;AACjC,IAAA,IAAI,QAAA,CAAS,IAAA,KAAS,QAAA,EAAU,OAAO,QAAA;AAEvC,IAAA,MAAM,UAAU,QAAA,CAAS,OAAA;AACzB,IAAA,MAAM,YAAY,OAAA,EAAS,MAAA;AAC3B,IAAA,IAAI,CAAC,WAAW,OAAO,QAAA;AAEvB,IAAA,MAAM,aAAa,CAAC,KAAA,KAAsC,KAAA,IAAS,IAAA,GAAO,QAAQ,KAAA,GAAQ,IAAA;AAC1F,IAAA,MAAM,MAAA,GACJ,OAAO,SAAA,KAAc,UAAA,GACjB,CAAC,IAAA,KAAyC;AACxC,MAAA,MAAM,CAAC,QAAA,EAAU,QAAQ,CAAA,GAAI,UAAU,IAAI,CAAA;AAC3C,MAAA,OAAO,CAAC,UAAA,CAAW,QAAQ,CAAA,EAAG,UAAA,CAAW,QAAQ,CAAC,CAAA;AAAA,IACpD,CAAA,GACA,CAAC,UAAA,CAAW,SAAA,CAAU,CAAC,CAAC,CAAA,EAAG,UAAA,CAAW,SAAA,CAAU,CAAC,CAAC,CAAC,CAAA;AAEzD,IAAA,OAAO,EAAE,GAAG,QAAA,EAAU,OAAA,EAAS,EAAE,GAAG,OAAA,EAAS,QAAO,EAAE;AAAA,EACxD,CAAC,CAAA;AACH;AAGO,SAAS,wBAAA,CAAyB;AAAA,EACvC,QAAA;AAAA,EACA,GAAG;AACL,CAAA,EAA0D;AACxD,EAAA,MAAM,kBAAA,GAAqB,qBAAA,CAAsB,QAAA,IAAY,IAAI,CAAA;AACjE,EAAA,MAAM,EAAE,YAAA,EAAc,OAAA,EAAQ,GAAI,2BAAA,EAA4B;AAC9D,EAAA,MAAM,SAAA,GAAY,OAAA;AAAA,IAChB,MACE,kBAAA,CAAmB,KAAA,CAAM,SAAA,IAAa,kCAAA,EAAoC,aAAa,IAAI,CAAA;AAAA,IAC7F,CAAC,YAAA,CAAa,IAAA,EAAM,KAAA,CAAM,SAAS;AAAA,GACrC;AACA,EAAA,OAAO;AAAA,IACL,WAAA,EAAa;AAAA,MACX,GAAG,KAAA;AAAA,MACH,QAAA,EAAU,kBAAA;AAAA,MACV,SAAA,EAAW,OAAA,CAAQ,OAAA,IAAW,QAAA,CAAS,IAAA;AAAA,MACvC,SAAA;AAAA,MACA,OAAO,EAAE,GAAG,KAAA,CAAM,KAAA,EAAO,MAAM,CAAA;AAAE;AACnC,GACF;AACF;AAGO,SAAS,mBAAmB,KAAA,EAA8C;AAC/E,EAAA,MAAM,EAAE,WAAA,EAAY,GAAI,wBAAA,CAAyB,KAAK,CAAA;AACtD,EAAA,uBAAO,GAAA,CAAC,MAAA,EAAA,EAAQ,GAAG,WAAA,EAAa,CAAA;AAClC;AC7KO,SAAS,yBACd,KAAA,EACqC;AACrC,EAAA,MAAM;AAAA,IACJ,SAAA;AAAA,IACA,kBAAA;AAAA,IACA,WAAA;AAAA,IACA,OAAA;AAAA,IACA,MAAA;AAAA,IACA,IAAA,EAAM,QAAA;AAAA,IACN,GAAG;AAAA,GACL,GAAI,KAAA;AACJ,EAAA,MAAM,EAAE,OAAA,EAAQ,GAAI,2BAAA,EAA4B;AAChD,EAAA,MAAM,WAAA,GAAcA,OAAuB,IAAI,CAAA;AAC/C,EAAA,MAAM,WAAW,KAAA,EAAM;AACvB,EAAA,MAAM,CAAC,aAAA,EAAe,gBAAgB,CAAA,GAAI,SAA6B,IAAI,CAAA;AAC3E,EAAA,MAAM,CAAC,gBAAA,EAAkB,mBAAmB,IAAI,QAAA,CAAS,OAAA,CAAQ,WAAW,CAAC,CAAA;AAC7E,EAAA,MAAM,OAAO,QAAA,IAAY,gBAAA;AAEzB,EAAAC,gBAAgB,MAAM;AACpB,IAAA,IAAI,CAAC,IAAA,EAAM;AACT,MAAA,gBAAA,CAAiB,IAAI,CAAA;AACrB,MAAA;AAAA,IACF;AACA,IAAA,gBAAA;AAAA,MACE,QAAA,CAAS,aAAA,CAA2B,CAAA,+BAAA,EAAkC,QAAQ,CAAA,EAAA,CAAI;AAAA,KACpF;AAAA,EACF,CAAA,EAAG,CAAC,IAAA,EAAM,QAAQ,CAAC,CAAA;AAEnB,EAAA,MAAM,UAAA,GAAaC,WAAAA;AAAA,IACjB,CAAC,KAAA,KAA0B;AACzB,MAAA,IAAI,QAAA,KAAa,MAAA,EAAW,mBAAA,CAAoB,IAAI,CAAA;AACpD,MAAA,MAAA,GAAS,KAAK,CAAA;AAAA,IAChB,CAAA;AAAA,IACA,CAAC,QAAQ,QAAQ;AAAA,GACnB;AACA,EAAA,MAAM,WAAA,GAAcA,WAAAA;AAAA,IAClB,CAAC,KAAA,KAA0B;AACzB,MAAA,IAAI,QAAA,KAAa,MAAA,EAAW,mBAAA,CAAoB,KAAK,CAAA;AACrD,MAAA,OAAA,GAAU,KAAK,CAAA;AAAA,IACjB,CAAA;AAAA,IACA,CAAC,SAAS,QAAQ;AAAA,GACpB;AAEA,EAAA,8BAAA,CAA+B;AAAA,IAC7B,IAAA;AAAA,IACA,cAAA,EAAgB,WAAA;AAAA,IAChB,aAAA;AAAA,IACA,YAAA,EAAc,EAAE,QAAA,EAAU,QAAA,EAAU,YAAY,QAAA,EAAS;AAAA,IACzD,eAAA,EAAiB,EAAE,QAAA,EAAU,KAAA,EAAO,YAAY,QAAA;AAAS,GAC1D,CAAA;AAED,EAAA,MAAM,SAAA,GAAYC,QAAQ,MAA0B;AAClD,IAAA,MAAM,SAAA,GAAY,WAAW,SAAA,EAAW,KAAA;AACxC,IAAA,MAAM,iBAAA,GAAoB,OAAO,SAAA,KAAc,UAAA,GAAa,MAAA,GAAY,SAAA;AAExE,IAAA,OAAO;AAAA,MACL,GAAG,SAAA;AAAA,MACH,SAAA,EAAW,OAAA,CAAQ,OAAA,IAAW,QAAA,CAAS,IAAA;AAAA,MACvC,GAAA,EAAK,WAAA;AAAA,MACL,SAAA,EAAW;AAAA,QACT,GAAG,SAAA,EAAW,SAAA;AAAA,QACd,KAAA,EAAO;AAAA,UACL,GAAG,iBAAA;AAAA,UACH,OAAO,EAAE,GAAG,iBAAA,EAAmB,KAAA,EAAO,MAAM,CAAA;AAAE;AAChD;AACF,KACF;AAAA,EACF,CAAA,EAAG,CAAC,SAAA,EAAW,OAAO,CAAC,CAAA;AAEvB,EAAA,OAAO;AAAA,IACL,WAAA,EAAa;AAAA,MACX,GAAG,WAAA;AAAA,MACH,IAAA;AAAA,MACA,MAAA,EAAQ,UAAA;AAAA,MACR,OAAA,EAAS,WAAA;AAAA,MACT,SAAA,EAAW,SAAA;AAAA,MACX,kBAAA,EAAoB;AAAA,QAClB,GAAG,kBAAA;AAAA,QACH,8BAAA,EAAgC;AAAA;AAClC;AACF,GACF;AACF;AAGO,SAAS,mBAAoC,KAAA,EAAyC;AAC3F,EAAA,MAAM,EAAE,WAAA,EAAY,GAAI,wBAAA,CAAyB,KAAK,CAAA;AACtD,EAAA,uBAAOC,GAAAA,CAAC,MAAA,EAAA,EAAe,GAAG,WAAA,EAAa,CAAA;AACzC;AChGO,SAAS,4BAAA,GAAkD;AAChE,EAAA,MAAM,EAAE,YAAA,EAAc,eAAA,EAAiB,OAAA,KAAY,2BAAA,EAA4B;AAE/E,EAAA,MAAM,kBAAA,GAAqBF,YAAY,MAAmB;AACxD,IAAA,IAAI,eAAA,KAAoB,eAAA,EAAiB,OAAO,QAAA,CAAS,IAAA;AACzD,IAAA,OAAO,OAAA,CAAQ,WAAW,QAAA,CAAS,IAAA;AAAA,EACrC,CAAA,EAAG,CAAC,eAAA,EAAiB,OAAO,CAAC,CAAA;AAE7B,EAAA,OAAOC,OAAAA;AAAA,IACL,MACE,+BAAA,CAAgC;AAAA,MAC9B,kBAAA;AAAA,MACA,WAAA,EAAa,eAAA,KAAoB,eAAA,GAAkB,YAAA,CAAa,IAAA,GAAO;AAAA,KACxE,CAAA;AAAA,IACH,CAAC,YAAA,CAAa,IAAA,EAAM,kBAAA,EAAoB,eAAe;AAAA,GACzD;AACF;ACjBO,SAAS,4BAAA,CACd,SACA,KAAA,EACM;AACN,EAAA,MAAM,EAAE,YAAA,EAAa,GAAI,2BAAA,EAA4B;AAErD,EAAAF,gBAAgB,MAAM;AACpB,IAAA,IAAI,CAAC,aAAa,MAAA,EAAQ;AAI1B,IAAA,MAAM,kBAAkB,MAAY;AAClC,MAAA,MAAM,OAAO,OAAA,CAAQ,OAAA;AACrB,MAAA,MAAM,SAAA,GAAY,IAAA,EAAM,aAAA,CAA2B,oBAAoB,CAAA;AACvE,MAAA,MAAM,WAAA,GAAc,IAAA,EAAM,aAAA,CAA2B,2BAA2B,CAAA;AAChF,MAAA,IAAI,CAAC,IAAA,IAAQ,CAAC,SAAA,IAAa,CAAC,WAAA,EAAa;AAEzC,MAAA,MAAM,KAAA,GAAQ,gBAAA,CAAiB,IAAI,CAAA,CAAE,SAAA,KAAc,KAAA;AACnD,MAAA,MAAM,OAAO,WAAA,CAAY,UAAA;AACzB,MAAA,MAAM,QAAQ,WAAA,CAAY,WAAA;AAE1B,MAAA,SAAA,CAAU,KAAA,CAAM,KAAA,GAAQ,CAAA,EAAG,KAAK,CAAA,EAAA,CAAA;AAChC,MAAA,IAAI,KAAA,EAAO;AACT,QAAA,SAAA,CAAU,MAAM,IAAA,GAAO,MAAA;AACvB,QAAA,SAAA,CAAU,MAAM,KAAA,GAAQ,CAAA,EAAG,IAAA,CAAK,WAAA,GAAc,OAAO,KAAK,CAAA,EAAA,CAAA;AAAA,MAC5D,CAAA,MAAO;AACL,QAAA,SAAA,CAAU,MAAM,KAAA,GAAQ,MAAA;AACxB,QAAA,SAAA,CAAU,KAAA,CAAM,IAAA,GAAO,CAAA,EAAG,IAAI,CAAA,EAAA,CAAA;AAAA,MAChC;AAAA,IACF,CAAA;AAEA,IAAA,MAAM,KAAA,GAAQ,MAAA,CAAO,qBAAA,CAAsB,eAAe,CAAA;AAC1D,IAAA,MAAM,QAAA,GAAW,IAAI,cAAA,CAAe,eAAe,CAAA;AACnD,IAAA,IAAI,OAAA,CAAQ,OAAA,EAAS,QAAA,CAAS,OAAA,CAAQ,QAAQ,OAAO,CAAA;AACrD,IAAA,MAAA,CAAO,gBAAA,CAAiB,UAAU,eAAe,CAAA;AAEjD,IAAA,OAAO,MAAM;AACX,MAAA,MAAA,CAAO,qBAAqB,KAAK,CAAA;AACjC,MAAA,QAAA,CAAS,UAAA,EAAW;AACpB,MAAA,MAAA,CAAO,mBAAA,CAAoB,UAAU,eAAe,CAAA;AAAA,IACtD,CAAA;AAAA,EACF,GAAG,CAAC,YAAA,CAAa,MAAA,EAAQ,OAAA,EAAS,KAAK,CAAC,CAAA;AAC1C;;;AC/CO,IAAM,sBAAA,GAAyC;AAAA,EACpD,IAAA,EAAM,CAAA;AAAA,EACN,SAAA,EAAW,CAAA;AAAA,EACX,QAAA,EAAU,CAAA;AAAA,EACV,KAAA,EAAO,MAAA;AAAA,EACP,MAAA,EAAQ;AACV;AAGO,IAAM,qBAAA,GAAwC;AAAA,EACnD,IAAA,EAAM,CAAA;AAAA,EACN,SAAA,EAAW,CAAA;AAAA,EACX,QAAA,EAAU,CAAA;AAAA,EACV,KAAA,EAAO,MAAA;AAAA,EACP,MAAA,EAAQ,MAAA;AAAA,EACR,QAAA,EAAU,QAAA;AAAA,EACV,SAAA,EAAW;AACb;AAGO,IAAM,mBAAA,GAAsC;AAAA,EACjD,IAAA,EAAM,EAAE,EAAA,EAAI,UAAA,EAAY,IAAI,OAAA,EAAQ;AAAA,EACpC,KAAA,EAAO,EAAE,EAAA,EAAI,MAAA,EAAQ,IAAI,KAAA,EAAM;AAAA,EAC/B,QAAA,EAAU,EAAE,EAAA,EAAI,MAAA,EAAQ,IAAI,KAAA,EAAM;AAAA,EAClC,QAAA,EAAU,CAAA;AAAA,EACV,SAAA,EAAW;AACb","file":"chunk-5MGGPTIJ.js","sourcesContent":["import type { Components, Theme } from \"@mui/material/styles\";\n\ntype PortalContainerGetter = () => HTMLElement;\n\ntype MuiDisplayScaleComponentsOptions = {\n getPortalContainer: PortalContainerGetter;\n /**\n * Overlay content is portalled outside the zoomed app root, so it needs the\n * same density correction independently.\n */\n overlayZoom?: number;\n};\n\n/**\n * MUI adapter for a compensated app root.\n *\n * Centralizing these defaults gives every MUI overlay the portal strategy and\n * density correction chosen by the display-scale engine.\n */\nexport function createMuiDisplayScaleComponents({\n getPortalContainer,\n overlayZoom = 1,\n}: MuiDisplayScaleComponentsOptions): Components<Theme> {\n const zoom = overlayZoom > 0 ? overlayZoom : 1;\n const resolveContainerZoom = (container: unknown): number => {\n if (typeof document === \"undefined\") return zoom;\n const resolvedContainer =\n typeof container === \"function\" ? (container as PortalContainerGetter)() : container;\n // MUI does not always expose the resolved portal node through ownerState.\n // Missing means the component will use its default (document.body).\n return resolvedContainer == null || resolvedContainer === document.body ? zoom : 1;\n };\n\n return {\n MuiModal: {\n defaultProps: {\n container: getPortalContainer,\n },\n },\n MuiDialog: {\n defaultProps: {\n container: getPortalContainer,\n },\n styleOverrides: {\n paper: ({ ownerState }) => ({ zoom: resolveContainerZoom(ownerState?.container) }),\n },\n },\n MuiPopover: {\n defaultProps: {\n container: getPortalContainer,\n },\n styleOverrides: {\n paper: ({ ownerState }) => ({ zoom: resolveContainerZoom(ownerState?.container) }),\n },\n },\n MuiPopper: {\n defaultProps: {\n container: getPortalContainer,\n },\n styleOverrides: {\n root: ({ ownerState }) => ({ zoom: resolveContainerZoom(ownerState?.container) }),\n },\n },\n MuiDrawer: {\n defaultProps: {\n // Drawer inherits Modal props. Supplying the portal host here (rather\n // than only through MuiModal) also covers Drawer internals directly.\n container: getPortalContainer,\n },\n styleOverrides: {\n paper: ({ ownerState }) => {\n // Permanent/persistent drawers live inside the compensated root and\n // must not be zoomed a second time. Temporary drawers are portalled\n // to document.body, so their paper needs the overlay density zoom.\n if (ownerState.variant !== \"temporary\") return {};\n\n const isVertical = ownerState.anchor === \"left\" || ownerState.anchor === \"right\";\n return isVertical\n ? {\n zoom,\n height: \"var(--ep-display-scale-overlay-viewport-height, 100dvh)\",\n maxHeight: \"var(--ep-display-scale-overlay-viewport-height, 100dvh)\",\n }\n : {\n zoom,\n width: \"var(--ep-display-scale-overlay-viewport-width, 100dvw)\",\n maxWidth: \"var(--ep-display-scale-overlay-viewport-width, 100dvw)\",\n };\n },\n },\n },\n };\n}\n","import type { PopoverOrigin, PopoverProps } from \"@mui/material\";\nimport { type RefObject, useCallback, useLayoutEffect } from \"react\";\nimport { useDisplayScaleCompensation } from \"./DisplayScaleCompensationProvider\";\n\ntype DisplayScalePopoverPositionOptions = {\n open: boolean;\n overlayRootRef: RefObject<HTMLDivElement | null>;\n anchorElement: HTMLElement | null;\n anchorReference?: PopoverProps[\"anchorReference\"];\n anchorPosition?: PopoverProps[\"anchorPosition\"];\n anchorOrigin?: PopoverProps[\"anchorOrigin\"];\n transformOrigin?: PopoverProps[\"transformOrigin\"];\n marginThreshold?: PopoverProps[\"marginThreshold\"];\n};\n\nfunction resolveOffset(\n origin: number | \"top\" | \"center\" | \"bottom\" | \"left\" | \"right\" | undefined,\n size: number,\n): number {\n if (typeof origin === \"number\") return origin;\n if (origin === \"center\") return size / 2;\n if (origin === \"bottom\" || origin === \"right\") return size;\n return 0;\n}\n\nfunction resolveOrigins(\n anchorOrigin: PopoverOrigin | undefined,\n transformOrigin: PopoverOrigin | undefined,\n anchorWidth: number,\n anchorHeight: number,\n paperWidth: number,\n paperHeight: number,\n): { anchorX: number; anchorY: number; transformX: number; transformY: number } {\n const resolvedAnchorOrigin = anchorOrigin ?? { vertical: \"top\", horizontal: \"left\" };\n const resolvedTransformOrigin = transformOrigin ?? { vertical: \"top\", horizontal: \"left\" };\n\n return {\n anchorX: resolveOffset(resolvedAnchorOrigin.horizontal, anchorWidth),\n anchorY: resolveOffset(resolvedAnchorOrigin.vertical, anchorHeight),\n transformX: resolveOffset(resolvedTransformOrigin.horizontal, paperWidth),\n transformY: resolveOffset(resolvedTransformOrigin.vertical, paperHeight),\n };\n}\n\n/**\n * Replaces only MUI Popover's physical-viewport collision pass. MUI still\n * owns focus, transition and anchor semantics; this writes the final logical\n * top/left inside the compensated portal root.\n */\nexport function useDisplayScalePopoverPosition({\n open,\n overlayRootRef,\n anchorElement,\n anchorReference,\n anchorPosition,\n anchorOrigin,\n transformOrigin,\n marginThreshold = 16,\n}: DisplayScalePopoverPositionOptions): void {\n const { compensation, layoutExtents, rootRef } = useDisplayScaleCompensation();\n\n const correctPosition = useCallback(() => {\n if (!open || !compensation.active || !layoutExtents) return;\n\n const overlayRoot = overlayRootRef.current;\n const paper = overlayRoot?.querySelector<HTMLElement>(\".MuiPopover-paper\");\n if (!paper) return;\n\n const zoom = compensation.zoom > 0 ? compensation.zoom : 1;\n const rootRect = rootRef.current?.getBoundingClientRect();\n const rootLeft = rootRect?.left ?? 0;\n const rootTop = rootRect?.top ?? 0;\n const anchorRect = anchorElement?.getBoundingClientRect();\n const anchorX =\n anchorReference === \"anchorPosition\" && anchorPosition\n ? (anchorPosition.left - rootLeft) / zoom\n : anchorRect\n ? (anchorRect.left - rootLeft) / zoom\n : null;\n const anchorY =\n anchorReference === \"anchorPosition\" && anchorPosition\n ? (anchorPosition.top - rootTop) / zoom\n : anchorRect\n ? (anchorRect.top - rootTop) / zoom\n : null;\n if (anchorX == null || anchorY == null) return;\n\n const paperWidth = paper.offsetWidth;\n const paperHeight = paper.offsetHeight;\n const {\n anchorX: originX,\n anchorY: originY,\n transformX,\n transformY,\n } = resolveOrigins(\n anchorOrigin,\n transformOrigin,\n anchorRect ? anchorRect.width / zoom : 0,\n anchorRect ? anchorRect.height / zoom : 0,\n paperWidth,\n paperHeight,\n );\n const margin = typeof marginThreshold === \"number\" ? marginThreshold : 16;\n const maxLeft = Math.max(margin, layoutExtents.layoutWidthPx - margin - paperWidth);\n const maxTop = Math.max(margin, layoutExtents.layoutHeightPx - margin - paperHeight);\n const left = Math.min(maxLeft, Math.max(margin, anchorX + originX - transformX));\n const top = Math.min(maxTop, Math.max(margin, anchorY + originY - transformY));\n\n paper.style.left = `${Math.round(left)}px`;\n paper.style.top = `${Math.round(top)}px`;\n }, [\n anchorElement,\n anchorOrigin,\n anchorPosition,\n anchorReference,\n compensation.active,\n compensation.zoom,\n layoutExtents,\n marginThreshold,\n open,\n overlayRootRef,\n rootRef,\n transformOrigin,\n ]);\n\n useLayoutEffect(() => {\n if (!open) return;\n\n const firstFrame = window.requestAnimationFrame(() => {\n correctPosition();\n window.requestAnimationFrame(correctPosition);\n });\n window.addEventListener(\"resize\", correctPosition);\n window.addEventListener(\"scroll\", correctPosition, true);\n\n return () => {\n window.cancelAnimationFrame(firstFrame);\n window.removeEventListener(\"resize\", correctPosition);\n window.removeEventListener(\"scroll\", correctPosition, true);\n };\n }, [correctPosition, open]);\n}\n","import type { MenuProps, PopoverProps, PopperProps } from \"@mui/material\";\nimport { Menu, Popover, Popper } from \"@mui/material\";\nimport { useTheme } from \"@mui/material/styles\";\nimport type {\n OffsetsFunction,\n Options as PopperOffsetOptions,\n} from \"@popperjs/core/lib/modifiers/offset\";\nimport { type ReactElement, useMemo, useRef } from \"react\";\nimport { useDisplayScaleCompensation } from \"./DisplayScaleCompensationProvider\";\nimport { useDisplayScaleAnchor } from \"./useDisplayScaleAnchor\";\nimport { useDisplayScalePopoverPosition } from \"./useDisplayScalePopoverPosition\";\n\ntype NativeAnchorProps = {\n anchorEl?: HTMLElement | null;\n};\n\nexport type DisplayScaleMenuProps = Omit<MenuProps, \"anchorEl\"> & NativeAnchorProps;\n\nexport type MuiDisplayScaleMenuAdapter = {\n /** Spread these props onto MUI's native `<Menu />`. */\n menuProps: MenuProps;\n};\n\n/** Non-wrapper display-scale adapter for MUI's native `<Menu />`. */\nexport function useMuiDisplayScaleMenu({\n anchorEl,\n ...props\n}: DisplayScaleMenuProps): MuiDisplayScaleMenuAdapter {\n const displayScaleAnchor = useDisplayScaleAnchor(anchorEl ?? null);\n const { compensation, rootRef } = useDisplayScaleCompensation();\n const theme = useTheme();\n const overlayRootRef = useRef<HTMLDivElement>(null);\n const horizontalOrigin = theme.direction === \"rtl\" ? \"right\" : \"left\";\n const anchorOrigin = props.anchorOrigin ?? {\n vertical: \"bottom\",\n horizontal: horizontalOrigin,\n };\n const transformOrigin = props.transformOrigin ?? {\n vertical: \"top\",\n horizontal: horizontalOrigin,\n };\n const anchorPosition = useMemo(() => {\n if (props.anchorReference !== \"anchorPosition\" || !props.anchorPosition) {\n return props.anchorPosition;\n }\n const zoom = compensation.zoom > 0 ? compensation.zoom : 1;\n return {\n top: props.anchorPosition.top / zoom,\n left: props.anchorPosition.left / zoom,\n };\n }, [compensation.zoom, props.anchorPosition, props.anchorReference]);\n useDisplayScalePopoverPosition({\n open: props.open,\n overlayRootRef,\n anchorElement: anchorEl ?? null,\n anchorReference: props.anchorReference,\n anchorPosition: props.anchorPosition,\n anchorOrigin,\n transformOrigin,\n marginThreshold: props.marginThreshold,\n });\n\n return {\n menuProps: {\n ...props,\n ref: overlayRootRef,\n anchorEl: displayScaleAnchor,\n anchorPosition,\n anchorOrigin,\n container: rootRef.current ?? document.body,\n transformOrigin,\n },\n };\n}\n\n/** MUI Menu wrapper — prefer `useMuiDisplayScaleMenu` when retaining `<Menu />`. */\nexport function DisplayScaleMenu(props: DisplayScaleMenuProps): ReactElement {\n const { menuProps } = useMuiDisplayScaleMenu(props);\n return <Menu {...menuProps} />;\n}\n\nexport type DisplayScalePopoverProps = Omit<PopoverProps, \"anchorEl\"> & NativeAnchorProps;\n\nexport type MuiDisplayScalePopoverAdapter = {\n /** Spread these props onto MUI's native `<Popover />`. */\n popoverProps: PopoverProps;\n};\n\n/** Non-wrapper display-scale adapter for MUI's native `<Popover />`. */\nexport function useMuiDisplayScalePopover({\n anchorEl,\n ...props\n}: DisplayScalePopoverProps): MuiDisplayScalePopoverAdapter {\n const displayScaleAnchor = useDisplayScaleAnchor(anchorEl ?? null);\n const { rootRef } = useDisplayScaleCompensation();\n const overlayRootRef = useRef<HTMLDivElement>(null);\n useDisplayScalePopoverPosition({\n open: props.open,\n overlayRootRef,\n anchorElement: anchorEl ?? null,\n anchorReference: props.anchorReference,\n anchorPosition: props.anchorPosition,\n anchorOrigin: props.anchorOrigin,\n transformOrigin: props.transformOrigin,\n marginThreshold: props.marginThreshold,\n });\n return {\n popoverProps: {\n ...props,\n ref: overlayRootRef,\n anchorEl: displayScaleAnchor,\n container: rootRef.current ?? document.body,\n },\n };\n}\n\n/** MUI Popover wrapper — prefer `useMuiDisplayScalePopover` when retaining `<Popover />`. */\nexport function DisplayScalePopover(props: DisplayScalePopoverProps): ReactElement {\n const { popoverProps } = useMuiDisplayScalePopover(props);\n return <Popover {...popoverProps} />;\n}\n\nexport type DisplayScalePopperProps = Omit<PopperProps, \"anchorEl\"> & NativeAnchorProps;\n\nexport type MuiDisplayScalePopperAdapter = {\n /** Spread these props onto MUI's native `<Popper />`. */\n popperProps: PopperProps;\n};\n\nconst defaultDisplayScalePopperModifiers: PopperProps[\"modifiers\"] = [\n {\n name: \"offset\",\n options: { offset: [0, 8] },\n },\n];\n\nfunction scalePopperOffsets(\n modifiers: PopperProps[\"modifiers\"],\n zoom: number,\n): PopperProps[\"modifiers\"] {\n if (!modifiers || zoom === 1) return modifiers;\n\n return modifiers.map((modifier) => {\n if (modifier.name !== \"offset\") return modifier;\n\n const options = modifier.options as Partial<PopperOffsetOptions> | undefined;\n const rawOffset = options?.offset;\n if (!rawOffset) return modifier;\n\n const scaleValue = (value: number | null | undefined) => (value == null ? value : value / zoom);\n const offset: PopperOffsetOptions[\"offset\"] =\n typeof rawOffset === \"function\"\n ? (args: Parameters<OffsetsFunction>[0]) => {\n const [skidding, distance] = rawOffset(args);\n return [scaleValue(skidding), scaleValue(distance)];\n }\n : [scaleValue(rawOffset[0]), scaleValue(rawOffset[1])];\n\n return { ...modifier, options: { ...options, offset } };\n });\n}\n\n/** Non-wrapper display-scale adapter for MUI's native `<Popper />`. */\nexport function useMuiDisplayScalePopper({\n anchorEl,\n ...props\n}: DisplayScalePopperProps): MuiDisplayScalePopperAdapter {\n const displayScaleAnchor = useDisplayScaleAnchor(anchorEl ?? null);\n const { compensation, rootRef } = useDisplayScaleCompensation();\n const modifiers = useMemo(\n () =>\n scalePopperOffsets(props.modifiers ?? defaultDisplayScalePopperModifiers, compensation.zoom),\n [compensation.zoom, props.modifiers],\n );\n return {\n popperProps: {\n ...props,\n anchorEl: displayScaleAnchor,\n container: rootRef.current ?? document.body,\n modifiers,\n style: { ...props.style, zoom: 1 },\n },\n };\n}\n\n/** MUI Popper wrapper — prefer `useMuiDisplayScalePopper` when retaining `<Popper />`. */\nexport function DisplayScalePopper(props: DisplayScalePopperProps): ReactElement {\n const { popperProps } = useMuiDisplayScalePopper(props);\n return <Popper {...popperProps} />;\n}\n","import type { MenuProps, SelectProps } from \"@mui/material\";\nimport { Select } from \"@mui/material\";\nimport type { ReactElement, SyntheticEvent } from \"react\";\nimport { useCallback, useId, useLayoutEffect, useMemo, useRef, useState } from \"react\";\nimport { useDisplayScaleCompensation } from \"./DisplayScaleCompensationProvider\";\nimport { useDisplayScalePopoverPosition } from \"./useDisplayScalePopoverPosition\";\n\nexport type MuiDisplayScaleSelectAdapter<Value = unknown> = {\n /** Spread these props onto MUI's native `<Select />`. */\n selectProps: SelectProps<Value>;\n};\n\n/**\n * MUI Select adapter for the display-scale engine. Select creates its Menu\n * internally, so it cannot use DisplayScaleMenu directly.\n */\nexport function useMuiDisplayScaleSelect<Value = unknown>(\n props: SelectProps<Value>,\n): MuiDisplayScaleSelectAdapter<Value> {\n const {\n MenuProps,\n SelectDisplayProps,\n defaultOpen,\n onClose,\n onOpen,\n open: openProp,\n ...selectProps\n } = props;\n const { rootRef } = useDisplayScaleCompensation();\n const menuRootRef = useRef<HTMLDivElement>(null);\n const selectId = useId();\n const [anchorElement, setAnchorElement] = useState<HTMLElement | null>(null);\n const [uncontrolledOpen, setUncontrolledOpen] = useState(Boolean(defaultOpen));\n const open = openProp ?? uncontrolledOpen;\n\n useLayoutEffect(() => {\n if (!open) {\n setAnchorElement(null);\n return;\n }\n setAnchorElement(\n document.querySelector<HTMLElement>(`[data-ep-display-scale-select=\"${selectId}\"]`),\n );\n }, [open, selectId]);\n\n const handleOpen = useCallback(\n (event: SyntheticEvent) => {\n if (openProp === undefined) setUncontrolledOpen(true);\n onOpen?.(event);\n },\n [onOpen, openProp],\n );\n const handleClose = useCallback(\n (event: SyntheticEvent) => {\n if (openProp === undefined) setUncontrolledOpen(false);\n onClose?.(event);\n },\n [onClose, openProp],\n );\n\n useDisplayScalePopoverPosition({\n open,\n overlayRootRef: menuRootRef,\n anchorElement,\n anchorOrigin: { vertical: \"bottom\", horizontal: \"center\" },\n transformOrigin: { vertical: \"top\", horizontal: \"center\" },\n });\n\n const menuProps = useMemo((): Partial<MenuProps> => {\n const paperSlot = MenuProps?.slotProps?.paper;\n const resolvedPaperSlot = typeof paperSlot === \"function\" ? undefined : paperSlot;\n\n return {\n ...MenuProps,\n container: rootRef.current ?? document.body,\n ref: menuRootRef,\n slotProps: {\n ...MenuProps?.slotProps,\n paper: {\n ...resolvedPaperSlot,\n style: { ...resolvedPaperSlot?.style, zoom: 1 },\n },\n },\n };\n }, [MenuProps, rootRef]);\n\n return {\n selectProps: {\n ...selectProps,\n open,\n onOpen: handleOpen,\n onClose: handleClose,\n MenuProps: menuProps,\n SelectDisplayProps: {\n ...SelectDisplayProps,\n \"data-ep-display-scale-select\": selectId,\n } as SelectProps<Value>[\"SelectDisplayProps\"],\n },\n };\n}\n\n/** MUI Select wrapper — prefer `useMuiDisplayScaleSelect` when retaining `<Select />`. */\nexport function DisplayScaleSelect<Value = unknown>(props: SelectProps<Value>): ReactElement {\n const { selectProps } = useMuiDisplayScaleSelect(props);\n return <Select<Value> {...selectProps} />;\n}\n","import type { Components, Theme } from \"@mui/material/styles\";\nimport { useCallback, useMemo } from \"react\";\nimport { useDisplayScaleCompensation } from \"./DisplayScaleCompensationProvider\";\nimport { createMuiDisplayScaleComponents } from \"./createMuiDisplayScaleComponents\";\n\n/**\n * React adapter for global MUI overlay defaults. Use inside an existing\n * `DisplayScaleProvider`, then pass the result to `createTheme`.\n */\nexport function useMuiDisplayScaleComponents(): Components<Theme> {\n const { compensation, overlayStrategy, rootRef } = useDisplayScaleCompensation();\n\n const getPortalContainer = useCallback((): HTMLElement => {\n if (overlayStrategy === \"document-body\") return document.body;\n return rootRef.current ?? document.body;\n }, [overlayStrategy, rootRef]);\n\n return useMemo(\n () =>\n createMuiDisplayScaleComponents({\n getPortalContainer,\n overlayZoom: overlayStrategy === \"document-body\" ? compensation.zoom : 1,\n }),\n [compensation.zoom, getPortalContainer, overlayStrategy],\n );\n}\n","import { type RefObject, useLayoutEffect } from \"react\";\nimport { useDisplayScaleCompensation } from \"./DisplayScaleCompensationProvider\";\n\n/**\n * Keeps MUI Tabs' indicator in the compensated root's logical coordinate\n * space. MUI normally uses getBoundingClientRect(), whose values are already\n * zoomed and therefore too small for a logical layout container.\n */\nexport function useDisplayScaleTabsIndicator(\n tabsRef: RefObject<HTMLDivElement | null>,\n value: unknown,\n): void {\n const { compensation } = useDisplayScaleCompensation();\n\n useLayoutEffect(() => {\n if (!compensation.active) return;\n // Re-run after the selected Tabs value changes.\n void value;\n\n const updateIndicator = (): void => {\n const tabs = tabsRef.current;\n const indicator = tabs?.querySelector<HTMLElement>(\".MuiTabs-indicator\");\n const selectedTab = tabs?.querySelector<HTMLElement>(\".MuiTab-root.Mui-selected\");\n if (!tabs || !indicator || !selectedTab) return;\n\n const isRtl = getComputedStyle(tabs).direction === \"rtl\";\n const left = selectedTab.offsetLeft;\n const width = selectedTab.offsetWidth;\n\n indicator.style.width = `${width}px`;\n if (isRtl) {\n indicator.style.left = \"auto\";\n indicator.style.right = `${tabs.offsetWidth - left - width}px`;\n } else {\n indicator.style.right = \"auto\";\n indicator.style.left = `${left}px`;\n }\n };\n\n const frame = window.requestAnimationFrame(updateIndicator);\n const observer = new ResizeObserver(updateIndicator);\n if (tabsRef.current) observer.observe(tabsRef.current);\n window.addEventListener(\"resize\", updateIndicator);\n\n return () => {\n window.cancelAnimationFrame(frame);\n observer.disconnect();\n window.removeEventListener(\"resize\", updateIndicator);\n };\n }, [compensation.active, tabsRef, value]);\n}\n","import type { SxProps, Theme } from \"@mui/material/styles\";\n\n/** Fill the compensated app tree — prefer over raw `100vh` inside `#root`. */\nexport const displayScalePageFillSx: SxProps<Theme> = {\n flex: 1,\n minHeight: 0,\n minWidth: 0,\n width: \"100%\",\n height: \"100%\",\n};\n\n/** Full-viewport auth split layout — fills the compensated app root (not `position: fixed`). */\nexport const signinViewportShellSx: SxProps<Theme> = {\n flex: 1,\n minHeight: 0,\n minWidth: 0,\n width: \"100%\",\n height: \"100%\",\n overflow: \"hidden\",\n boxSizing: \"border-box\",\n};\n\n/** 50/50 desktop columns — avoids flex min-content overflow at OS display scale. */\nexport const signinSplitColumnSx: SxProps<Theme> = {\n flex: { xs: \"0 0 auto\", md: \"1 1 0\" },\n width: { xs: \"100%\", md: \"50%\" },\n maxWidth: { xs: \"100%\", md: \"50%\" },\n minWidth: 0,\n boxSizing: \"border-box\",\n};\n"]}