mesurer 0.0.7 → 0.0.9

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/dist/index.cjs CHANGED
@@ -20,15 +20,17 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // index.ts
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
- Measurer: () => Measurer
23
+ Measurer: () => Measurer,
24
+ TextInspector: () => TextInspector,
25
+ createTextInspector: () => createTextInspector
24
26
  });
25
27
  module.exports = __toCommonJS(index_exports);
26
28
 
27
29
  // measurer.tsx
28
- var import_react19 = require("react");
30
+ var import_react20 = require("react");
29
31
  var import_react_dom = require("react-dom");
30
32
 
31
- // constants.ts
33
+ // core/constants.ts
32
34
  var MEASURE_LABEL_OFFSET = 2;
33
35
  var GUIDE_SNAP_DISTANCE = 10;
34
36
  var GUIDE_HITBOX_SIZE = 14;
@@ -41,15 +43,17 @@ var ANCESTOR_KEEP_COVERAGE = 0.92;
41
43
  var MIN_SINGLE_TARGET_SIZE = 8;
42
44
  var MIN_SINGLE_ELEMENT_COVERAGE = 0.15;
43
45
 
44
- // style-inject.ts
46
+ // runtime/style-inject.ts
45
47
  var MESURER_STYLE_ID = "mesurer-styles";
48
+ var isDocument = (target) => target.nodeType === 9;
49
+ var isShadowRoot = (target) => target.nodeType === 11;
46
50
  var getStyleTarget = (target) => {
47
51
  if (typeof document === "undefined") return null;
48
52
  if (!target) return document;
49
- if (target instanceof ShadowRoot) return target;
53
+ if (isShadowRoot(target)) return target;
50
54
  const rootNode = target.getRootNode();
51
- if (rootNode instanceof ShadowRoot) return rootNode;
52
- return document;
55
+ if (rootNode.nodeType === 11) return rootNode;
56
+ return target.ownerDocument ?? document;
53
57
  };
54
58
  function ensureMeasurerStyles(cssText, target) {
55
59
  if (typeof document === "undefined") return;
@@ -57,23 +61,25 @@ function ensureMeasurerStyles(cssText, target) {
57
61
  const styleTarget = getStyleTarget(target);
58
62
  if (!styleTarget) return;
59
63
  if (styleTarget.querySelector(`#${MESURER_STYLE_ID}`)) return;
60
- const style = document.createElement("style");
64
+ const ownerDocument = isDocument(styleTarget) ? styleTarget : styleTarget.ownerDocument;
65
+ if (!ownerDocument) return;
66
+ const style = ownerDocument.createElement("style");
61
67
  style.id = MESURER_STYLE_ID;
62
68
  style.textContent = cssText;
63
- if (styleTarget instanceof ShadowRoot) {
69
+ if (isShadowRoot(styleTarget)) {
64
70
  styleTarget.appendChild(style);
65
71
  return;
66
72
  }
67
- styleTarget.head.appendChild(style);
73
+ ownerDocument.head.appendChild(style);
68
74
  }
69
75
 
70
76
  // styles.generated.ts
71
- var MESURER_STYLES = '/*! tailwindcss v4.2.2 | MIT License | https://tailwindcss.com */\n@layer properties;\n@layer theme, base, components, utilities;\n@layer theme {\n :root, :host {\n --msr-font-sans: ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji",\n "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";\n --msr-font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono",\n "Courier New", monospace;\n --msr-color-black: #000;\n --msr-color-white: #fff;\n --msr-spacing: 0.25rem;\n --msr-radius-md: 0.375rem;\n --msr-radius-lg: 0.5rem;\n --msr-default-transition-duration: 150ms;\n --msr-default-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n --msr-default-font-family: var(--msr-font-sans);\n --msr-default-mono-font-family: var(--msr-font-mono);\n --msr-color-ink-50: var(--color-ink-50);\n --msr-color-ink-200: var(--color-ink-200);\n --msr-color-ink-700: var(--color-ink-700);\n --msr-color-ink-900: var(--color-ink-900);\n }\n}\n@layer base {\n *, ::after, ::before, ::backdrop, ::file-selector-button {\n box-sizing: border-box;\n margin: 0;\n padding: 0;\n border: 0 solid;\n }\n html, :host {\n line-height: 1.5;\n -webkit-text-size-adjust: 100%;\n tab-size: 4;\n font-family: var(--msr-default-font-family, ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");\n font-feature-settings: var(--msr-default-font-feature-settings, normal);\n font-variation-settings: var(--msr-default-font-variation-settings, normal);\n -webkit-tap-highlight-color: transparent;\n }\n hr {\n height: 0;\n color: inherit;\n border-top-width: 1px;\n }\n abbr:where([title]) {\n -webkit-text-decoration: underline dotted;\n text-decoration: underline dotted;\n }\n h1, h2, h3, h4, h5, h6 {\n font-size: inherit;\n font-weight: inherit;\n }\n a {\n color: inherit;\n -webkit-text-decoration: inherit;\n text-decoration: inherit;\n }\n b, strong {\n font-weight: bolder;\n }\n code, kbd, samp, pre {\n font-family: var(--msr-default-mono-font-family, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);\n font-feature-settings: var(--msr-default-mono-font-feature-settings, normal);\n font-variation-settings: var(--msr-default-mono-font-variation-settings, normal);\n font-size: 1em;\n }\n small {\n font-size: 80%;\n }\n sub, sup {\n font-size: 75%;\n line-height: 0;\n position: relative;\n vertical-align: baseline;\n }\n sub {\n bottom: -0.25em;\n }\n sup {\n top: -0.5em;\n }\n table {\n text-indent: 0;\n border-color: inherit;\n border-collapse: collapse;\n }\n :-moz-focusring {\n outline: auto;\n }\n progress {\n vertical-align: baseline;\n }\n summary {\n display: list-item;\n }\n ol, ul, menu {\n list-style: none;\n }\n img, svg, video, canvas, audio, iframe, embed, object {\n display: block;\n vertical-align: middle;\n }\n img, video {\n max-width: 100%;\n height: auto;\n }\n button, input, select, optgroup, textarea, ::file-selector-button {\n font: inherit;\n font-feature-settings: inherit;\n font-variation-settings: inherit;\n letter-spacing: inherit;\n color: inherit;\n border-radius: 0;\n background-color: transparent;\n opacity: 1;\n }\n :where(select:is([multiple], [size])) optgroup {\n font-weight: bolder;\n }\n :where(select:is([multiple], [size])) optgroup option {\n padding-inline-start: 20px;\n }\n ::file-selector-button {\n margin-inline-end: 4px;\n }\n ::placeholder {\n opacity: 1;\n }\n @supports (not (-webkit-appearance: -apple-pay-button)) or (contain-intrinsic-size: 1px) {\n ::placeholder {\n color: currentcolor;\n @supports (color: color-mix(in lab, red, red)) {\n color: color-mix(in oklab, currentcolor 50%, transparent);\n }\n }\n }\n textarea {\n resize: vertical;\n }\n ::-webkit-search-decoration {\n -webkit-appearance: none;\n }\n ::-webkit-date-and-time-value {\n min-height: 1lh;\n text-align: inherit;\n }\n ::-webkit-datetime-edit {\n display: inline-flex;\n }\n ::-webkit-datetime-edit-fields-wrapper {\n padding: 0;\n }\n ::-webkit-datetime-edit, ::-webkit-datetime-edit-year-field, ::-webkit-datetime-edit-month-field, ::-webkit-datetime-edit-day-field, ::-webkit-datetime-edit-hour-field, ::-webkit-datetime-edit-minute-field, ::-webkit-datetime-edit-second-field, ::-webkit-datetime-edit-millisecond-field, ::-webkit-datetime-edit-meridiem-field {\n padding-block: 0;\n }\n ::-webkit-calendar-picker-indicator {\n line-height: 1;\n }\n :-moz-ui-invalid {\n box-shadow: none;\n }\n button, input:where([type="button"], [type="reset"], [type="submit"]), ::file-selector-button {\n appearance: button;\n }\n ::-webkit-inner-spin-button, ::-webkit-outer-spin-button {\n height: auto;\n }\n [hidden]:where(:not([hidden="until-found"])) {\n display: none !important;\n }\n}\n@layer utilities {\n .msr\\:pointer-events-auto {\n pointer-events: auto;\n }\n .msr\\:pointer-events-none {\n pointer-events: none;\n }\n .msr\\:absolute {\n position: absolute;\n }\n .msr\\:fixed {\n position: fixed;\n }\n .msr\\:relative {\n position: relative;\n }\n .msr\\:inset-0 {\n inset: calc(var(--msr-spacing) * 0);\n }\n .msr\\:top-0 {\n top: calc(var(--msr-spacing) * 0);\n }\n .msr\\:top-full {\n top: 100%;\n }\n .msr\\:right-0 {\n right: calc(var(--msr-spacing) * 0);\n }\n .msr\\:bottom-0 {\n bottom: calc(var(--msr-spacing) * 0);\n }\n .msr\\:bottom-full {\n bottom: 100%;\n }\n .msr\\:left-0 {\n left: calc(var(--msr-spacing) * 0);\n }\n .msr\\:left-1\\/2 {\n left: calc(1 / 2 * 100%);\n }\n .msr\\:z-50 {\n z-index: 50;\n }\n .msr\\:z-\\[70\\] {\n z-index: 70;\n }\n .msr\\:z-\\[90\\] {\n z-index: 90;\n }\n .msr\\:mt-2 {\n margin-top: calc(var(--msr-spacing) * 2);\n }\n .msr\\:mb-2 {\n margin-bottom: calc(var(--msr-spacing) * 2);\n }\n .msr\\:-ml-1 {\n margin-left: calc(var(--msr-spacing) * -1);\n }\n .msr\\:flex {\n display: flex;\n }\n .msr\\:size-8 {\n width: calc(var(--msr-spacing) * 8);\n height: calc(var(--msr-spacing) * 8);\n }\n .msr\\:h-8 {\n height: calc(var(--msr-spacing) * 8);\n }\n .msr\\:h-full {\n height: 100%;\n }\n .msr\\:h-px {\n height: 1px;\n }\n .msr\\:w-4 {\n width: calc(var(--msr-spacing) * 4);\n }\n .msr\\:w-44 {\n width: calc(var(--msr-spacing) * 44);\n }\n .msr\\:w-full {\n width: 100%;\n }\n .msr\\:w-px {\n width: 1px;\n }\n .msr\\:flex-1 {\n flex: 1;\n }\n .msr\\:-translate-x-1\\/2 {\n --tw-translate-x: calc(calc(1 / 2 * 100%) * -1);\n translate: var(--tw-translate-x) var(--tw-translate-y);\n }\n .msr\\:-translate-y-1\\/2 {\n --tw-translate-y: calc(calc(1 / 2 * 100%) * -1);\n translate: var(--tw-translate-x) var(--tw-translate-y);\n }\n .msr\\:rotate-90 {\n rotate: 90deg;\n }\n .msr\\:rotate-\\[45deg\\] {\n rotate: 45deg;\n }\n .msr\\:rotate-\\[135deg\\] {\n rotate: 135deg;\n }\n .msr\\:cursor-crosshair {\n cursor: crosshair;\n }\n .msr\\:cursor-default {\n cursor: default;\n }\n .msr\\:flex-col {\n flex-direction: column;\n }\n .msr\\:items-center {\n align-items: center;\n }\n .msr\\:items-stretch {\n align-items: stretch;\n }\n .msr\\:justify-center {\n justify-content: center;\n }\n .msr\\:gap-1 {\n gap: calc(var(--msr-spacing) * 1);\n }\n .msr\\:gap-2 {\n gap: calc(var(--msr-spacing) * 2);\n }\n .msr\\:gap-px {\n gap: 1px;\n }\n .msr\\:rounded {\n border-radius: 0.25rem;\n }\n .msr\\:rounded-\\[6px\\] {\n border-radius: 6px;\n }\n .msr\\:rounded-\\[8px\\] {\n border-radius: 8px;\n }\n .msr\\:rounded-\\[12px\\] {\n border-radius: 12px;\n }\n .msr\\:rounded-lg {\n border-radius: var(--msr-radius-lg);\n }\n .msr\\:rounded-md {\n border-radius: var(--msr-radius-md);\n }\n .msr\\:border {\n border-style: var(--tw-border-style);\n border-width: 1px;\n }\n .msr\\:border-t {\n border-top-style: var(--tw-border-style);\n border-top-width: 1px;\n }\n .msr\\:border-l {\n border-left-style: var(--tw-border-style);\n border-left-width: 1px;\n }\n .msr\\:border-dashed {\n --tw-border-style: dashed;\n border-style: dashed;\n }\n .msr\\:border-\\[\\#2563eb\\]\\/70 {\n border-color: color-mix(in oklab, #2563eb 70%, transparent);\n }\n .msr\\:border-ink-200 {\n border-color: var(--msr-color-ink-200);\n }\n .msr\\:bg-\\[\\#0d99ff\\] {\n background-color: #0d99ff;\n }\n .msr\\:bg-\\[\\#2563eb\\] {\n background-color: #2563eb;\n }\n .msr\\:bg-\\[\\#fff\\] {\n background-color: #fff;\n }\n .msr\\:bg-black {\n background-color: var(--msr-color-black);\n }\n .msr\\:bg-black\\/10 {\n background-color: var(--msr-color-black);\n @supports (color: color-mix(in lab, red, red)) {\n background-color: color-mix(in oklab, var(--msr-color-black) 10%, transparent);\n }\n }\n .msr\\:bg-ink-900\\/90 {\n background-color: var(--msr-color-ink-900);\n @supports (color: color-mix(in lab, red, red)) {\n background-color: color-mix(in oklab, var(--msr-color-ink-900) 90%, transparent);\n }\n }\n .msr\\:bg-transparent {\n background-color: transparent;\n }\n .msr\\:bg-white {\n background-color: var(--msr-color-white);\n }\n .msr\\:p-1 {\n padding: calc(var(--msr-spacing) * 1);\n }\n .msr\\:px-1 {\n padding-inline: calc(var(--msr-spacing) * 1);\n }\n .msr\\:px-2 {\n padding-inline: calc(var(--msr-spacing) * 2);\n }\n .msr\\:py-0\\.5 {\n padding-block: calc(var(--msr-spacing) * 0.5);\n }\n .msr\\:py-1 {\n padding-block: calc(var(--msr-spacing) * 1);\n }\n .msr\\:py-1\\.5 {\n padding-block: calc(var(--msr-spacing) * 1.5);\n }\n .msr\\:text-left {\n text-align: left;\n }\n .msr\\:text-\\[10px\\] {\n font-size: 10px;\n }\n .msr\\:text-\\[11px\\] {\n font-size: 11px;\n }\n .msr\\:text-\\[12px\\] {\n font-size: 12px;\n }\n .msr\\:whitespace-nowrap {\n white-space: nowrap;\n }\n .msr\\:text-black {\n color: var(--msr-color-black);\n }\n .msr\\:text-ink-50 {\n color: var(--msr-color-ink-50);\n }\n .msr\\:text-ink-700 {\n color: var(--msr-color-ink-700);\n }\n .msr\\:text-white {\n color: var(--msr-color-white);\n }\n .msr\\:text-white\\/60 {\n color: var(--msr-color-white);\n @supports (color: color-mix(in lab, red, red)) {\n color: color-mix(in oklab, var(--msr-color-white) 60%, transparent);\n }\n }\n .msr\\:tabular-nums {\n --tw-numeric-spacing: tabular-nums;\n font-variant-numeric: var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,);\n }\n .msr\\:opacity-0 {\n opacity: 0%;\n }\n .msr\\:opacity-100 {\n opacity: 100%;\n }\n .msr\\:outline {\n outline-style: var(--tw-outline-style);\n outline-width: 1px;\n }\n .msr\\:outline-transparent {\n outline-color: transparent;\n }\n .msr\\:transition-opacity {\n transition-property: opacity;\n transition-timing-function: var(--tw-ease, var(--msr-default-transition-timing-function));\n transition-duration: var(--tw-duration, var(--msr-default-transition-duration));\n }\n .msr\\:duration-150 {\n --tw-duration: 150ms;\n transition-duration: 150ms;\n }\n .msr\\:outline-none {\n --tw-outline-style: none;\n outline-style: none;\n }\n .msr\\:select-none {\n -webkit-user-select: none;\n user-select: none;\n }\n .msr\\:hover\\:bg-\\[\\#0d99ff\\] {\n &:hover {\n @media (hover: hover) {\n background-color: #0d99ff;\n }\n }\n }\n .msr\\:hover\\:bg-black\\/4 {\n &:hover {\n @media (hover: hover) {\n background-color: var(--msr-color-black);\n @supports (color: color-mix(in lab, red, red)) {\n background-color: color-mix(in oklab, var(--msr-color-black) 4%, transparent);\n }\n }\n }\n }\n .msr\\:hover\\:bg-black\\/10 {\n &:hover {\n @media (hover: hover) {\n background-color: var(--msr-color-black);\n @supports (color: color-mix(in lab, red, red)) {\n background-color: color-mix(in oklab, var(--msr-color-black) 10%, transparent);\n }\n }\n }\n }\n .msr\\:hover\\:text-white {\n &:hover {\n @media (hover: hover) {\n color: var(--msr-color-white);\n }\n }\n }\n .msr\\:focus\\:outline-none {\n &:focus {\n --tw-outline-style: none;\n outline-style: none;\n }\n }\n}\n:root, :host {\n --color-ink-50: #f8fafc;\n --color-ink-200: #e2e8f0;\n --color-ink-500: #64748b;\n --color-ink-700: #334155;\n --color-ink-900: #0f172a;\n}\n.mesurer-toolbar-surface {\n box-shadow: 0 0 0.5px rgba(0, 0, 0, 0.18), 0 3px 8px rgba(0, 0, 0, 0.1), 0 1px 3px rgba(0, 0, 0, 0.1);\n}\n.mesurer-menu-surface {\n box-shadow: 0 10px 30px rgba(0, 0, 0, 0.08);\n}\n@property --tw-translate-x {\n syntax: "*";\n inherits: false;\n initial-value: 0;\n}\n@property --tw-translate-y {\n syntax: "*";\n inherits: false;\n initial-value: 0;\n}\n@property --tw-translate-z {\n syntax: "*";\n inherits: false;\n initial-value: 0;\n}\n@property --tw-border-style {\n syntax: "*";\n inherits: false;\n initial-value: solid;\n}\n@property --tw-ordinal {\n syntax: "*";\n inherits: false;\n}\n@property --tw-slashed-zero {\n syntax: "*";\n inherits: false;\n}\n@property --tw-numeric-figure {\n syntax: "*";\n inherits: false;\n}\n@property --tw-numeric-spacing {\n syntax: "*";\n inherits: false;\n}\n@property --tw-numeric-fraction {\n syntax: "*";\n inherits: false;\n}\n@property --tw-outline-style {\n syntax: "*";\n inherits: false;\n initial-value: solid;\n}\n@property --tw-duration {\n syntax: "*";\n inherits: false;\n}\n@layer properties {\n @supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))) {\n *, ::before, ::after, ::backdrop {\n --tw-translate-x: 0;\n --tw-translate-y: 0;\n --tw-translate-z: 0;\n --tw-border-style: solid;\n --tw-ordinal: initial;\n --tw-slashed-zero: initial;\n --tw-numeric-figure: initial;\n --tw-numeric-spacing: initial;\n --tw-numeric-fraction: initial;\n --tw-outline-style: solid;\n --tw-duration: initial;\n }\n }\n}\n';
77
+ var MESURER_STYLES = '/*! tailwindcss v4.2.2 | MIT License | https://tailwindcss.com */\n@layer properties;\n@layer theme, base, components, utilities;\n@layer theme {\n :root, :host {\n --msr-font-sans: ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji",\n "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";\n --msr-font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono",\n "Courier New", monospace;\n --msr-color-black: #000;\n --msr-color-white: #fff;\n --msr-spacing: 0.25rem;\n --msr-radius-md: 0.375rem;\n --msr-radius-lg: 0.5rem;\n --msr-default-transition-duration: 150ms;\n --msr-default-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n --msr-default-font-family: var(--msr-font-sans);\n --msr-default-mono-font-family: var(--msr-font-mono);\n --msr-color-ink-50: var(--color-ink-50);\n --msr-color-ink-200: var(--color-ink-200);\n --msr-color-ink-700: var(--color-ink-700);\n --msr-color-ink-900: var(--color-ink-900);\n }\n}\n@layer base {\n *, ::after, ::before, ::backdrop, ::file-selector-button {\n box-sizing: border-box;\n margin: 0;\n padding: 0;\n border: 0 solid;\n }\n html, :host {\n line-height: 1.5;\n -webkit-text-size-adjust: 100%;\n tab-size: 4;\n font-family: var(--msr-default-font-family, ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");\n font-feature-settings: var(--msr-default-font-feature-settings, normal);\n font-variation-settings: var(--msr-default-font-variation-settings, normal);\n -webkit-tap-highlight-color: transparent;\n }\n hr {\n height: 0;\n color: inherit;\n border-top-width: 1px;\n }\n abbr:where([title]) {\n -webkit-text-decoration: underline dotted;\n text-decoration: underline dotted;\n }\n h1, h2, h3, h4, h5, h6 {\n font-size: inherit;\n font-weight: inherit;\n }\n a {\n color: inherit;\n -webkit-text-decoration: inherit;\n text-decoration: inherit;\n }\n b, strong {\n font-weight: bolder;\n }\n code, kbd, samp, pre {\n font-family: var(--msr-default-mono-font-family, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);\n font-feature-settings: var(--msr-default-mono-font-feature-settings, normal);\n font-variation-settings: var(--msr-default-mono-font-variation-settings, normal);\n font-size: 1em;\n }\n small {\n font-size: 80%;\n }\n sub, sup {\n font-size: 75%;\n line-height: 0;\n position: relative;\n vertical-align: baseline;\n }\n sub {\n bottom: -0.25em;\n }\n sup {\n top: -0.5em;\n }\n table {\n text-indent: 0;\n border-color: inherit;\n border-collapse: collapse;\n }\n :-moz-focusring {\n outline: auto;\n }\n progress {\n vertical-align: baseline;\n }\n summary {\n display: list-item;\n }\n ol, ul, menu {\n list-style: none;\n }\n img, svg, video, canvas, audio, iframe, embed, object {\n display: block;\n vertical-align: middle;\n }\n img, video {\n max-width: 100%;\n height: auto;\n }\n button, input, select, optgroup, textarea, ::file-selector-button {\n font: inherit;\n font-feature-settings: inherit;\n font-variation-settings: inherit;\n letter-spacing: inherit;\n color: inherit;\n border-radius: 0;\n background-color: transparent;\n opacity: 1;\n }\n :where(select:is([multiple], [size])) optgroup {\n font-weight: bolder;\n }\n :where(select:is([multiple], [size])) optgroup option {\n padding-inline-start: 20px;\n }\n ::file-selector-button {\n margin-inline-end: 4px;\n }\n ::placeholder {\n opacity: 1;\n }\n @supports (not (-webkit-appearance: -apple-pay-button)) or (contain-intrinsic-size: 1px) {\n ::placeholder {\n color: currentcolor;\n @supports (color: color-mix(in lab, red, red)) {\n color: color-mix(in oklab, currentcolor 50%, transparent);\n }\n }\n }\n textarea {\n resize: vertical;\n }\n ::-webkit-search-decoration {\n -webkit-appearance: none;\n }\n ::-webkit-date-and-time-value {\n min-height: 1lh;\n text-align: inherit;\n }\n ::-webkit-datetime-edit {\n display: inline-flex;\n }\n ::-webkit-datetime-edit-fields-wrapper {\n padding: 0;\n }\n ::-webkit-datetime-edit, ::-webkit-datetime-edit-year-field, ::-webkit-datetime-edit-month-field, ::-webkit-datetime-edit-day-field, ::-webkit-datetime-edit-hour-field, ::-webkit-datetime-edit-minute-field, ::-webkit-datetime-edit-second-field, ::-webkit-datetime-edit-millisecond-field, ::-webkit-datetime-edit-meridiem-field {\n padding-block: 0;\n }\n ::-webkit-calendar-picker-indicator {\n line-height: 1;\n }\n :-moz-ui-invalid {\n box-shadow: none;\n }\n button, input:where([type="button"], [type="reset"], [type="submit"]), ::file-selector-button {\n appearance: button;\n }\n ::-webkit-inner-spin-button, ::-webkit-outer-spin-button {\n height: auto;\n }\n [hidden]:where(:not([hidden="until-found"])) {\n display: none !important;\n }\n}\n@layer utilities {\n .msr\\:pointer-events-auto {\n pointer-events: auto;\n }\n .msr\\:pointer-events-none {\n pointer-events: none;\n }\n .msr\\:absolute {\n position: absolute;\n }\n .msr\\:fixed {\n position: fixed;\n }\n .msr\\:relative {\n position: relative;\n }\n .msr\\:inset-0 {\n inset: calc(var(--msr-spacing) * 0);\n }\n .msr\\:top-0 {\n top: calc(var(--msr-spacing) * 0);\n }\n .msr\\:top-\\[18px\\] {\n top: 18px;\n }\n .msr\\:top-full {\n top: 100%;\n }\n .msr\\:right-0 {\n right: calc(var(--msr-spacing) * 0);\n }\n .msr\\:bottom-0 {\n bottom: calc(var(--msr-spacing) * 0);\n }\n .msr\\:bottom-full {\n bottom: 100%;\n }\n .msr\\:left-0 {\n left: calc(var(--msr-spacing) * 0);\n }\n .msr\\:left-1\\/2 {\n left: calc(1 / 2 * 100%);\n }\n .msr\\:left-\\[18px\\] {\n left: 18px;\n }\n .msr\\:z-50 {\n z-index: 50;\n }\n .msr\\:z-\\[70\\] {\n z-index: 70;\n }\n .msr\\:z-\\[90\\] {\n z-index: 90;\n }\n .msr\\:mt-2 {\n margin-top: calc(var(--msr-spacing) * 2);\n }\n .msr\\:mb-2 {\n margin-bottom: calc(var(--msr-spacing) * 2);\n }\n .msr\\:-ml-1 {\n margin-left: calc(var(--msr-spacing) * -1);\n }\n .msr\\:block {\n display: block;\n }\n .msr\\:flex {\n display: flex;\n }\n .msr\\:size-8 {\n width: calc(var(--msr-spacing) * 8);\n height: calc(var(--msr-spacing) * 8);\n }\n .msr\\:size-\\[18px\\] {\n width: 18px;\n height: 18px;\n }\n .msr\\:h-6 {\n height: calc(var(--msr-spacing) * 6);\n }\n .msr\\:h-8 {\n height: calc(var(--msr-spacing) * 8);\n }\n .msr\\:h-full {\n height: 100%;\n }\n .msr\\:h-px {\n height: 1px;\n }\n .msr\\:w-4 {\n width: calc(var(--msr-spacing) * 4);\n }\n .msr\\:w-6 {\n width: calc(var(--msr-spacing) * 6);\n }\n .msr\\:w-44 {\n width: calc(var(--msr-spacing) * 44);\n }\n .msr\\:w-\\[18px\\] {\n width: 18px;\n }\n .msr\\:w-full {\n width: 100%;\n }\n .msr\\:w-px {\n width: 1px;\n }\n .msr\\:flex-1 {\n flex: 1;\n }\n .msr\\:-translate-x-1\\/2 {\n --tw-translate-x: calc(calc(1 / 2 * 100%) * -1);\n translate: var(--tw-translate-x) var(--tw-translate-y);\n }\n .msr\\:-translate-y-1\\/2 {\n --tw-translate-y: calc(calc(1 / 2 * 100%) * -1);\n translate: var(--tw-translate-x) var(--tw-translate-y);\n }\n .msr\\:rotate-90 {\n rotate: 90deg;\n }\n .msr\\:rotate-\\[45deg\\] {\n rotate: 45deg;\n }\n .msr\\:rotate-\\[135deg\\] {\n rotate: 135deg;\n }\n .msr\\:cursor-crosshair {\n cursor: crosshair;\n }\n .msr\\:cursor-default {\n cursor: default;\n }\n .msr\\:flex-col {\n flex-direction: column;\n }\n .msr\\:items-center {\n align-items: center;\n }\n .msr\\:items-stretch {\n align-items: stretch;\n }\n .msr\\:justify-center {\n justify-content: center;\n }\n .msr\\:gap-1 {\n gap: calc(var(--msr-spacing) * 1);\n }\n .msr\\:gap-2 {\n gap: calc(var(--msr-spacing) * 2);\n }\n .msr\\:gap-px {\n gap: 1px;\n }\n .msr\\:overflow-hidden {\n overflow: hidden;\n }\n .msr\\:rounded {\n border-radius: 0.25rem;\n }\n .msr\\:rounded-\\[6px\\] {\n border-radius: 6px;\n }\n .msr\\:rounded-\\[8px\\] {\n border-radius: 8px;\n }\n .msr\\:rounded-\\[12px\\] {\n border-radius: 12px;\n }\n .msr\\:rounded-lg {\n border-radius: var(--msr-radius-lg);\n }\n .msr\\:rounded-md {\n border-radius: var(--msr-radius-md);\n }\n .msr\\:border {\n border-style: var(--tw-border-style);\n border-width: 1px;\n }\n .msr\\:border-t {\n border-top-style: var(--tw-border-style);\n border-top-width: 1px;\n }\n .msr\\:border-l {\n border-left-style: var(--tw-border-style);\n border-left-width: 1px;\n }\n .msr\\:border-dashed {\n --tw-border-style: dashed;\n border-style: dashed;\n }\n .msr\\:border-\\[\\#2563eb\\]\\/70 {\n border-color: color-mix(in oklab, #2563eb 70%, transparent);\n }\n .msr\\:border-ink-200 {\n border-color: var(--msr-color-ink-200);\n }\n .msr\\:bg-\\[\\#0d99ff\\] {\n background-color: #0d99ff;\n }\n .msr\\:bg-\\[\\#2563eb\\] {\n background-color: #2563eb;\n }\n .msr\\:bg-\\[\\#fff\\] {\n background-color: #fff;\n }\n .msr\\:bg-black {\n background-color: var(--msr-color-black);\n }\n .msr\\:bg-black\\/10 {\n background-color: var(--msr-color-black);\n @supports (color: color-mix(in lab, red, red)) {\n background-color: color-mix(in oklab, var(--msr-color-black) 10%, transparent);\n }\n }\n .msr\\:bg-ink-900\\/90 {\n background-color: var(--msr-color-ink-900);\n @supports (color: color-mix(in lab, red, red)) {\n background-color: color-mix(in oklab, var(--msr-color-ink-900) 90%, transparent);\n }\n }\n .msr\\:bg-transparent {\n background-color: transparent;\n }\n .msr\\:bg-white {\n background-color: var(--msr-color-white);\n }\n .msr\\:p-1 {\n padding: calc(var(--msr-spacing) * 1);\n }\n .msr\\:px-1 {\n padding-inline: calc(var(--msr-spacing) * 1);\n }\n .msr\\:px-2 {\n padding-inline: calc(var(--msr-spacing) * 2);\n }\n .msr\\:py-0\\.5 {\n padding-block: calc(var(--msr-spacing) * 0.5);\n }\n .msr\\:py-1 {\n padding-block: calc(var(--msr-spacing) * 1);\n }\n .msr\\:py-1\\.5 {\n padding-block: calc(var(--msr-spacing) * 1.5);\n }\n .msr\\:text-left {\n text-align: left;\n }\n .msr\\:text-\\[9px\\] {\n font-size: 9px;\n }\n .msr\\:text-\\[10px\\] {\n font-size: 10px;\n }\n .msr\\:text-\\[11px\\] {\n font-size: 11px;\n }\n .msr\\:text-\\[12px\\] {\n font-size: 12px;\n }\n .msr\\:whitespace-nowrap {\n white-space: nowrap;\n }\n .msr\\:text-\\[\\#64748b\\] {\n color: #64748b;\n }\n .msr\\:text-black {\n color: var(--msr-color-black);\n }\n .msr\\:text-ink-50 {\n color: var(--msr-color-ink-50);\n }\n .msr\\:text-ink-700 {\n color: var(--msr-color-ink-700);\n }\n .msr\\:text-white {\n color: var(--msr-color-white);\n }\n .msr\\:text-white\\/60 {\n color: var(--msr-color-white);\n @supports (color: color-mix(in lab, red, red)) {\n color: color-mix(in oklab, var(--msr-color-white) 60%, transparent);\n }\n }\n .msr\\:tabular-nums {\n --tw-numeric-spacing: tabular-nums;\n font-variant-numeric: var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,);\n }\n .msr\\:opacity-0 {\n opacity: 0%;\n }\n .msr\\:opacity-100 {\n opacity: 100%;\n }\n .msr\\:outline {\n outline-style: var(--tw-outline-style);\n outline-width: 1px;\n }\n .msr\\:outline-transparent {\n outline-color: transparent;\n }\n .msr\\:transition-opacity {\n transition-property: opacity;\n transition-timing-function: var(--tw-ease, var(--msr-default-transition-timing-function));\n transition-duration: var(--tw-duration, var(--msr-default-transition-duration));\n }\n .msr\\:transition-none {\n transition-property: none;\n }\n .msr\\:duration-150 {\n --tw-duration: 150ms;\n transition-duration: 150ms;\n }\n .msr\\:outline-none {\n --tw-outline-style: none;\n outline-style: none;\n }\n .msr\\:select-none {\n -webkit-user-select: none;\n user-select: none;\n }\n .msr\\:hover\\:bg-\\[\\#0d99ff\\] {\n &:hover {\n @media (hover: hover) {\n background-color: #0d99ff;\n }\n }\n }\n .msr\\:hover\\:bg-black\\/4 {\n &:hover {\n @media (hover: hover) {\n background-color: var(--msr-color-black);\n @supports (color: color-mix(in lab, red, red)) {\n background-color: color-mix(in oklab, var(--msr-color-black) 4%, transparent);\n }\n }\n }\n }\n .msr\\:hover\\:bg-black\\/10 {\n &:hover {\n @media (hover: hover) {\n background-color: var(--msr-color-black);\n @supports (color: color-mix(in lab, red, red)) {\n background-color: color-mix(in oklab, var(--msr-color-black) 10%, transparent);\n }\n }\n }\n }\n .msr\\:hover\\:text-white {\n &:hover {\n @media (hover: hover) {\n color: var(--msr-color-white);\n }\n }\n }\n .msr\\:focus\\:outline-none {\n &:focus {\n --tw-outline-style: none;\n outline-style: none;\n }\n }\n}\n:root, :host {\n --color-ink-50: #f8fafc;\n --color-ink-200: #e2e8f0;\n --color-ink-500: #64748b;\n --color-ink-700: #334155;\n --color-ink-900: #0f172a;\n}\n.mesurer-toolbar-surface {\n box-shadow: 0 0 0.5px rgba(0, 0, 0, 0.18), 0 3px 8px rgba(0, 0, 0, 0.1), 0 1px 3px rgba(0, 0, 0, 0.1);\n}\n.mesurer-menu-surface {\n box-shadow: 0 10px 30px rgba(0, 0, 0, 0.08);\n}\n.mesurer-root {\n --msr-spacing: 4px;\n}\n@property --tw-translate-x {\n syntax: "*";\n inherits: false;\n initial-value: 0;\n}\n@property --tw-translate-y {\n syntax: "*";\n inherits: false;\n initial-value: 0;\n}\n@property --tw-translate-z {\n syntax: "*";\n inherits: false;\n initial-value: 0;\n}\n@property --tw-border-style {\n syntax: "*";\n inherits: false;\n initial-value: solid;\n}\n@property --tw-ordinal {\n syntax: "*";\n inherits: false;\n}\n@property --tw-slashed-zero {\n syntax: "*";\n inherits: false;\n}\n@property --tw-numeric-figure {\n syntax: "*";\n inherits: false;\n}\n@property --tw-numeric-spacing {\n syntax: "*";\n inherits: false;\n}\n@property --tw-numeric-fraction {\n syntax: "*";\n inherits: false;\n}\n@property --tw-outline-style {\n syntax: "*";\n inherits: false;\n initial-value: solid;\n}\n@property --tw-duration {\n syntax: "*";\n inherits: false;\n}\n@layer properties {\n @supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))) {\n *, ::before, ::after, ::backdrop {\n --tw-translate-x: 0;\n --tw-translate-y: 0;\n --tw-translate-z: 0;\n --tw-border-style: solid;\n --tw-ordinal: initial;\n --tw-slashed-zero: initial;\n --tw-numeric-figure: initial;\n --tw-numeric-spacing: initial;\n --tw-numeric-fraction: initial;\n --tw-outline-style: solid;\n --tw-duration: initial;\n }\n }\n}\n';
72
78
 
73
79
  // components/toolbar.tsx
74
80
  var import_react = require("react");
75
81
 
76
- // utils.ts
82
+ // core/utils.ts
77
83
  var formatValue = (value) => Math.round(value);
78
84
  var cn = (...inputs) => inputs.filter(Boolean).join(" ");
79
85
  var createId = () => typeof crypto !== "undefined" && "randomUUID" in crypto ? crypto.randomUUID() : `${Date.now()}-${Math.random()}`;
@@ -93,9 +99,57 @@ var IconBase = ({ size = 24, ...props }) => /* @__PURE__ */ (0, import_jsx_runti
93
99
  );
94
100
  var CursorIcon = ({ size = 20, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(IconBase, { size, ...props, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M166.59,134.1a1.91,1.91,0,0,1-.55-1.79,2,2,0,0,1,1.08-1.42l46.25-17.76.24-.1A14,14,0,0,0,212.38,87L52.29,34.7A13.95,13.95,0,0,0,34.7,52.29L87,212.38a13.82,13.82,0,0,0,12.6,9.6c.23,0,.46,0,.69,0A13.84,13.84,0,0,0,113,213.61a2.44,2.44,0,0,0,.1-.24l17.76-46.25a2,2,0,0,1,3.21-.53l51.31,51.31a14,14,0,0,0,19.8,0l12.69-12.69a14,14,0,0,0,0-19.8Zm42.82,62.63-12.68,12.68a2,2,0,0,1-2.83,0L142.59,158.1a14,14,0,0,0-22.74,4.32,2.44,2.44,0,0,0-.1.24L102,208.91a2,2,0,0,1-3.61-.26L46.11,48.57a1.87,1.87,0,0,1,.47-2A1.92,1.92,0,0,1,47.93,46a2.22,2.22,0,0,1,.64.1L208.65,98.38a2,2,0,0,1,.26,3.61l-46.25,17.76-.24.1a14,14,0,0,0-4.32,22.74h0l51.31,51.31A2,2,0,0,1,209.41,196.73Z" }) });
95
101
  var RulerIcon = ({ size = 20, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(IconBase, { size, ...props, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M233.91,74.79,181.22,22.1a14,14,0,0,0-19.8,0L22.09,161.41a14,14,0,0,0,0,19.8L74.78,233.9a14,14,0,0,0,19.8,0L233.91,94.59A14,14,0,0,0,233.91,74.79ZM225.42,86.1,86.1,225.41h0a2,2,0,0,1-2.83,0L30.58,172.73a2,2,0,0,1,0-2.83L64,136.48l27.76,27.76a6,6,0,1,0,8.48-8.48L72.48,128,96,104.48l27.76,27.76a6,6,0,0,0,8.48-8.48L104.48,96,128,72.49l27.76,27.75a6,6,0,0,0,8.48-8.48L136.49,64,169.9,30.59a2,2,0,0,1,2.83,0l52.69,52.68A2,2,0,0,1,225.42,86.1Z" }) });
102
+ var XrayIcon = ({ size = 20, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(IconBase, { size, ...props, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
103
+ "path",
104
+ {
105
+ fill: "none",
106
+ stroke: "currentColor",
107
+ strokeLinecap: "round",
108
+ strokeLinejoin: "round",
109
+ strokeWidth: "12",
110
+ strokeOpacity: "0.78",
111
+ d: "M76 48h96a16 16 0 0 1 16 16v96a16 16 0 0 1-16 16H76a16 16 0 0 1-16-16V64a16 16 0 0 1 16-16Zm32 32h96a16 16 0 0 1 16 16v96a16 16 0 0 1-16 16h-96a16 16 0 0 1-16-16V96a16 16 0 0 1 16-16Z"
112
+ }
113
+ ) });
114
+ var RulersIcon = ({ size = 20, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(IconBase, { size, ...props, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
115
+ "path",
116
+ {
117
+ fill: "none",
118
+ stroke: "currentColor",
119
+ strokeLinecap: "round",
120
+ strokeLinejoin: "round",
121
+ strokeWidth: "12",
122
+ d: "M48 208V48h160M80 48v24m32-24v24m32-24v24m32-24v24M48 80h24M48 112h24m-24 32h24m-24 32h24"
123
+ }
124
+ ) });
96
125
  var CaretDownIcon = ({ size = 8, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(IconBase, { size, ...props, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M213.66,101.66l-80,80a8,8,0,0,1-11.32,0l-80-80A8,8,0,0,1,53.66,90.34L128,164.69l74.34-74.35a8,8,0,0,1,11.32,11.32Z" }) });
97
126
  var CheckIcon = ({ size = 12, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(IconBase, { size, ...props, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M232.49,80.49l-128,128a12,12,0,0,1-17,0l-56-56a12,12,0,1,1,17-17L96,183,215.51,63.51a12,12,0,0,1,17,17Z" }) });
98
127
  var MinusIcon = ({ size = 12, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(IconBase, { size, ...props, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M228,128a12,12,0,0,1-12,12H40a12,12,0,0,1,0-24H216A12,12,0,0,1,228,128Z" }) });
128
+ var TextInspectorIcon = ({ size = 20, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
129
+ "svg",
130
+ {
131
+ xmlns: "http://www.w3.org/2000/svg",
132
+ width: size,
133
+ height: size,
134
+ viewBox: "0 0 256 256",
135
+ fill: "currentColor",
136
+ ...props,
137
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
138
+ "text",
139
+ {
140
+ x: "50%",
141
+ y: "60%",
142
+ textAnchor: "middle",
143
+ dominantBaseline: "middle",
144
+ fill: "currentColor",
145
+ fontFamily: "ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, sans-serif",
146
+ fontWeight: 300,
147
+ fontSize: 220,
148
+ children: "Aa"
149
+ }
150
+ )
151
+ }
152
+ );
99
153
 
100
154
  // components/toolbar.tsx
101
155
  var import_jsx_runtime2 = require("react/jsx-runtime");
@@ -110,6 +164,7 @@ function ToolbarButton({
110
164
  shortcut,
111
165
  onClick,
112
166
  tooltipVisible,
167
+ tooltipInstant,
113
168
  tooltipSide,
114
169
  onTooltipEnter,
115
170
  onTooltipLeave,
@@ -127,6 +182,7 @@ function ToolbarButton({
127
182
  {
128
183
  type: "button",
129
184
  "aria-label": `${label} (${shortcut})`,
185
+ "aria-pressed": active,
130
186
  title: `${label} (${shortcut})`,
131
187
  className: cn(
132
188
  "msr:flex msr:size-8 msr:items-center msr:justify-center msr:rounded-[8px] msr:outline-none",
@@ -140,7 +196,10 @@ function ToolbarButton({
140
196
  "span",
141
197
  {
142
198
  className: cn(
143
- "msr:pointer-events-none msr:absolute msr:left-1/2 msr:-translate-x-1/2 msr:whitespace-nowrap msr:rounded msr:bg-black msr:px-2 msr:py-1 msr:text-[11px] msr:text-white msr:transition-opacity msr:duration-150 msr:select-none",
199
+ cn(
200
+ "msr:pointer-events-none msr:absolute msr:left-1/2 msr:-translate-x-1/2 msr:whitespace-nowrap msr:rounded msr:bg-black msr:px-2 msr:py-1 msr:text-[11px] msr:text-white msr:transition-opacity msr:duration-150 msr:select-none",
201
+ tooltipInstant && "msr:transition-none"
202
+ ),
144
203
  tooltipSide === "top" ? "msr:bottom-full msr:mb-2" : "msr:top-full msr:mt-2",
145
204
  tooltipVisible ? "msr:opacity-100" : "msr:opacity-0"
146
205
  ),
@@ -159,6 +218,7 @@ function useToolbarTooltip() {
159
218
  const [visibleTooltipId, setVisibleTooltipId] = (0, import_react.useState)(null);
160
219
  const timerRef = (0, import_react.useRef)(null);
161
220
  const instantRef = (0, import_react.useRef)(false);
221
+ const [tooltipInstant, setTooltipInstant] = (0, import_react.useState)(false);
162
222
  const clearTimer = (0, import_react.useCallback)(() => {
163
223
  if (timerRef.current === null) return;
164
224
  window.clearTimeout(timerRef.current);
@@ -168,9 +228,11 @@ function useToolbarTooltip() {
168
228
  (id) => {
169
229
  clearTimer();
170
230
  if (instantRef.current) {
231
+ setTooltipInstant(true);
171
232
  setVisibleTooltipId(id);
172
233
  return;
173
234
  }
235
+ setTooltipInstant(false);
174
236
  timerRef.current = window.setTimeout(() => {
175
237
  setVisibleTooltipId(id);
176
238
  instantRef.current = true;
@@ -190,15 +252,17 @@ function useToolbarTooltip() {
190
252
  clearTimer();
191
253
  setVisibleTooltipId(null);
192
254
  instantRef.current = false;
255
+ setTooltipInstant(false);
193
256
  }, [clearTimer]);
194
- (0, import_react.useEffect)(() => {
195
- return () => {
196
- clearTimer();
197
- };
198
- }, [clearTimer]);
199
- return { visibleTooltipId, onTooltipEnter, onTooltipLeave, onToolbarLeave };
257
+ return {
258
+ visibleTooltipId,
259
+ tooltipInstant,
260
+ onTooltipEnter,
261
+ onTooltipLeave,
262
+ onToolbarLeave
263
+ };
200
264
  }
201
- function useToolbarDrag(initialPosition) {
265
+ function useToolbarDrag(initialPosition, eventTarget) {
202
266
  const [position, setPosition] = (0, import_react.useState)(initialPosition);
203
267
  const suppressClickRef = (0, import_react.useRef)(false);
204
268
  const detachListenersRef = (0, import_react.useRef)(null);
@@ -241,8 +305,8 @@ function useToolbarDrag(initialPosition) {
241
305
  }
242
306
  if (!current.active) return;
243
307
  current.didDrag = true;
244
- const maxX = Math.max(8, window.innerWidth - current.width - 8);
245
- const maxY = Math.max(8, window.innerHeight - current.height - 8);
308
+ const maxX = Math.max(8, eventTarget.innerWidth - current.width - 8);
309
+ const maxY = Math.max(8, eventTarget.innerHeight - current.height - 8);
246
310
  setPosition({
247
311
  x: Math.min(maxX, Math.max(8, current.originX + dx)),
248
312
  y: Math.min(maxY, Math.max(8, current.originY + dy))
@@ -256,21 +320,21 @@ function useToolbarDrag(initialPosition) {
256
320
  current.active = false;
257
321
  current.didDrag = false;
258
322
  current.pointerId = -1;
259
- window.removeEventListener("pointermove", handlePointerMove);
260
- window.removeEventListener("pointerup", handlePointerEnd);
261
- window.removeEventListener("pointercancel", handlePointerEnd);
323
+ eventTarget.removeEventListener("pointermove", handlePointerMove);
324
+ eventTarget.removeEventListener("pointerup", handlePointerEnd);
325
+ eventTarget.removeEventListener("pointercancel", handlePointerEnd);
262
326
  detachListenersRef.current = null;
263
327
  };
264
- window.addEventListener("pointermove", handlePointerMove);
265
- window.addEventListener("pointerup", handlePointerEnd);
266
- window.addEventListener("pointercancel", handlePointerEnd);
328
+ eventTarget.addEventListener("pointermove", handlePointerMove);
329
+ eventTarget.addEventListener("pointerup", handlePointerEnd);
330
+ eventTarget.addEventListener("pointercancel", handlePointerEnd);
267
331
  detachListenersRef.current = () => {
268
- window.removeEventListener("pointermove", handlePointerMove);
269
- window.removeEventListener("pointerup", handlePointerEnd);
270
- window.removeEventListener("pointercancel", handlePointerEnd);
332
+ eventTarget.removeEventListener("pointermove", handlePointerMove);
333
+ eventTarget.removeEventListener("pointerup", handlePointerEnd);
334
+ eventTarget.removeEventListener("pointercancel", handlePointerEnd);
271
335
  };
272
336
  },
273
- [position.x, position.y]
337
+ [eventTarget, position.x, position.y]
274
338
  );
275
339
  const onClickCapture = (0, import_react.useCallback)(
276
340
  (event) => {
@@ -287,15 +351,24 @@ function ToolbarComponent({
287
351
  toolMode,
288
352
  setEnabled,
289
353
  setToolMode,
354
+ rulersVisible,
355
+ setRulersVisible,
290
356
  guideOrientation,
291
357
  setGuideOrientation,
292
- onInteract
358
+ onInteract,
359
+ eventTarget
293
360
  }, ref) {
294
361
  const { position, onPointerDown, onClickCapture } = useToolbarDrag({
295
362
  x: 16,
296
363
  y: 16
297
- });
298
- const { visibleTooltipId, onTooltipEnter, onTooltipLeave, onToolbarLeave } = useToolbarTooltip();
364
+ }, eventTarget);
365
+ const {
366
+ visibleTooltipId,
367
+ tooltipInstant,
368
+ onTooltipEnter,
369
+ onTooltipLeave,
370
+ onToolbarLeave
371
+ } = useToolbarTooltip();
299
372
  const [guideMenuOpen, setGuideMenuOpen] = (0, import_react.useState)(false);
300
373
  const guideMenuRef = (0, import_react.useRef)(null);
301
374
  const [activeMenuIndex, setActiveMenuIndex] = (0, import_react.useState)(0);
@@ -309,13 +382,13 @@ function ToolbarComponent({
309
382
  setMenuAlign("left");
310
383
  return;
311
384
  }
312
- if (leftAlignedRight > window.innerWidth - VIEWPORT_PADDING) {
385
+ if (leftAlignedRight > eventTarget.innerWidth - VIEWPORT_PADDING) {
313
386
  setMenuAlign("right");
314
387
  return;
315
388
  }
316
389
  setMenuAlign("right");
317
- }, []);
318
- const viewportHeight = typeof window === "undefined" ? 0 : window.innerHeight || 0;
390
+ }, [eventTarget]);
391
+ const viewportHeight = eventTarget.innerHeight || 0;
319
392
  const nearTop = position.y < 56;
320
393
  const nearBottom = viewportHeight > 0 && position.y > viewportHeight - 56;
321
394
  const tooltipSide = nearTop && !nearBottom ? "bottom" : "top";
@@ -330,6 +403,23 @@ function ToolbarComponent({
330
403
  setToolMode((prev) => prev === "guides" ? "none" : "guides");
331
404
  onInteract();
332
405
  }, [onInteract, setEnabled, setToolMode]);
406
+ const textInspectorMode = (0, import_react.useCallback)(() => {
407
+ setEnabled(true);
408
+ setToolMode(
409
+ (prev) => prev === "text-inspector" ? "none" : "text-inspector"
410
+ );
411
+ onInteract();
412
+ }, [onInteract, setEnabled, setToolMode]);
413
+ const xrayMode = (0, import_react.useCallback)(() => {
414
+ setEnabled(true);
415
+ setToolMode((prev) => prev === "xray" ? "none" : "xray");
416
+ onInteract();
417
+ }, [onInteract, setEnabled, setToolMode]);
418
+ const rulersMode = (0, import_react.useCallback)(() => {
419
+ setEnabled(true);
420
+ setRulersVisible((prev) => !prev);
421
+ onInteract();
422
+ }, [onInteract, setEnabled, setRulersVisible]);
333
423
  const selectGuideOrientation = (0, import_react.useCallback)(
334
424
  (orientation) => {
335
425
  setEnabled(true);
@@ -342,7 +432,7 @@ function ToolbarComponent({
342
432
  );
343
433
  (0, import_react.useLayoutEffect)(() => {
344
434
  if (!guideMenuOpen) return;
345
- const frame = requestAnimationFrame(() => {
435
+ const frame = eventTarget.requestAnimationFrame(() => {
346
436
  guideMenuRef.current?.querySelector("[role='menu']")?.focus();
347
437
  });
348
438
  const handlePointerDown = (event) => {
@@ -355,14 +445,14 @@ function ToolbarComponent({
355
445
  const handleResize = () => {
356
446
  updateMenuAlign();
357
447
  };
358
- window.addEventListener("pointerdown", handlePointerDown);
359
- window.addEventListener("resize", handleResize);
448
+ eventTarget.addEventListener("pointerdown", handlePointerDown);
449
+ eventTarget.addEventListener("resize", handleResize);
360
450
  return () => {
361
- cancelAnimationFrame(frame);
362
- window.removeEventListener("pointerdown", handlePointerDown);
363
- window.removeEventListener("resize", handleResize);
451
+ eventTarget.cancelAnimationFrame(frame);
452
+ eventTarget.removeEventListener("pointerdown", handlePointerDown);
453
+ eventTarget.removeEventListener("resize", handleResize);
364
454
  };
365
- }, [guideMenuOpen, guideOrientation, updateMenuAlign]);
455
+ }, [eventTarget, guideMenuOpen, guideOrientation, updateMenuAlign]);
366
456
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
367
457
  "div",
368
458
  {
@@ -385,12 +475,61 @@ function ToolbarComponent({
385
475
  shortcut: "S",
386
476
  onClick: selectMode,
387
477
  tooltipVisible: visibleTooltipId === "select",
478
+ tooltipInstant,
388
479
  tooltipSide,
389
480
  onTooltipEnter,
390
481
  onTooltipLeave,
391
482
  children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(CursorIcon, { size: 20 })
392
483
  }
393
484
  ),
485
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
486
+ ToolbarButton,
487
+ {
488
+ id: "xray",
489
+ active: toolMode === "xray",
490
+ label: "X-ray",
491
+ shortcut: "X",
492
+ onClick: xrayMode,
493
+ tooltipVisible: visibleTooltipId === "xray",
494
+ tooltipInstant,
495
+ tooltipSide,
496
+ onTooltipEnter,
497
+ onTooltipLeave,
498
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(XrayIcon, { size: 20 })
499
+ }
500
+ ),
501
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
502
+ ToolbarButton,
503
+ {
504
+ id: "rulers",
505
+ active: rulersVisible,
506
+ label: "Rulers",
507
+ shortcut: "R",
508
+ onClick: rulersMode,
509
+ tooltipVisible: visibleTooltipId === "rulers",
510
+ tooltipInstant,
511
+ tooltipSide,
512
+ onTooltipEnter,
513
+ onTooltipLeave,
514
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(RulersIcon, { size: 20 })
515
+ }
516
+ ),
517
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
518
+ ToolbarButton,
519
+ {
520
+ id: "text-inspector",
521
+ active: toolMode === "text-inspector",
522
+ label: "Text inspector",
523
+ shortcut: "A",
524
+ onClick: textInspectorMode,
525
+ tooltipVisible: visibleTooltipId === "text-inspector",
526
+ tooltipInstant,
527
+ tooltipSide,
528
+ onTooltipEnter,
529
+ onTooltipLeave,
530
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(TextInspectorIcon, { size: 20, "aria-hidden": "true" })
531
+ }
532
+ ),
394
533
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
395
534
  ToolbarButton,
396
535
  {
@@ -400,6 +539,7 @@ function ToolbarComponent({
400
539
  shortcut: "G",
401
540
  onClick: guidesMode,
402
541
  tooltipVisible: visibleTooltipId === "guides",
542
+ tooltipInstant,
403
543
  tooltipSide,
404
544
  onTooltipEnter,
405
545
  onTooltipLeave,
@@ -559,32 +699,337 @@ function ToolbarComponent({
559
699
  }
560
700
  var Toolbar = (0, import_react.memo)((0, import_react.forwardRef)(ToolbarComponent));
561
701
 
562
- // hooks/use-drag-state.ts
702
+ // components/rulers-overlay.tsx
563
703
  var import_react2 = require("react");
704
+ var import_jsx_runtime3 = require("react/jsx-runtime");
705
+ var RULER_SIZE = 18;
706
+ var RULER_LENGTH = 4e3;
707
+ var TICK_STEP = 5;
708
+ var ticks = Array.from(
709
+ { length: RULER_LENGTH / TICK_STEP + 1 },
710
+ (_, index) => index * TICK_STEP
711
+ );
712
+ var RulersOverlay = (0, import_react2.memo)(function RulersOverlay2({
713
+ onStartGuide,
714
+ onMoveGuide,
715
+ onFinishGuide,
716
+ onCancelGuide,
717
+ guides,
718
+ selectedGuideIds
719
+ }) {
720
+ const [dragPosition, setDragPosition] = (0, import_react2.useState)(null);
721
+ const [dragOrientation, setDragOrientation] = (0, import_react2.useState)(null);
722
+ const dragRef = (0, import_react2.useRef)(null);
723
+ const beginGuideDrag = (orientation, event) => {
724
+ if (event.button !== 0) return;
725
+ event.stopPropagation();
726
+ const position = orientation === "horizontal" ? event.clientY : event.clientX;
727
+ setDragPosition(position);
728
+ setDragOrientation(orientation);
729
+ dragRef.current = {
730
+ orientation,
731
+ pointerId: event.pointerId,
732
+ id: onStartGuide(orientation, position)
733
+ };
734
+ event.currentTarget.setPointerCapture(event.pointerId);
735
+ };
736
+ const moveGuide = (event) => {
737
+ const drag = dragRef.current;
738
+ if (!drag || drag.pointerId !== event.pointerId) return;
739
+ event.stopPropagation();
740
+ const position = drag.orientation === "horizontal" ? event.clientY : event.clientX;
741
+ setDragPosition(position);
742
+ onMoveGuide(drag.id, position);
743
+ };
744
+ const finishGuideDrag = (event) => {
745
+ const drag = dragRef.current;
746
+ if (!drag || drag.pointerId !== event.pointerId) return;
747
+ event.stopPropagation();
748
+ const position = drag.orientation === "horizontal" ? event.clientY : event.clientX;
749
+ if (event.currentTarget.hasPointerCapture(event.pointerId)) {
750
+ event.currentTarget.releasePointerCapture(event.pointerId);
751
+ }
752
+ dragRef.current = null;
753
+ setDragPosition(null);
754
+ setDragOrientation(null);
755
+ onMoveGuide(drag.id, position);
756
+ onFinishGuide(drag.id);
757
+ };
758
+ const cancelGuideDrag = (event) => {
759
+ const drag = dragRef.current;
760
+ if (!drag || drag.pointerId !== event.pointerId) return;
761
+ dragRef.current = null;
762
+ setDragPosition(null);
763
+ setDragOrientation(null);
764
+ onCancelGuide(drag.id);
765
+ };
766
+ const selectedVerticalGuides = guides.filter(
767
+ (guide) => guide.orientation === "vertical" && selectedGuideIds.includes(guide.id)
768
+ );
769
+ const selectedHorizontalGuides = guides.filter(
770
+ (guide) => guide.orientation === "horizontal" && selectedGuideIds.includes(guide.id)
771
+ );
772
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
773
+ "div",
774
+ {
775
+ "aria-hidden": "true",
776
+ className: "msr:pointer-events-none msr:absolute msr:inset-0 msr:select-none msr:text-[9px] msr:text-[#64748b]",
777
+ children: [
778
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
779
+ "div",
780
+ {
781
+ className: "msr:absolute msr:left-[18px] msr:right-0 msr:top-0 msr:overflow-hidden msr:bg-white",
782
+ style: {
783
+ boxShadow: "0 1px 3px rgba(0, 0, 0, 0.12)",
784
+ height: RULER_SIZE,
785
+ cursor: "ns-resize",
786
+ pointerEvents: "auto"
787
+ },
788
+ onPointerDown: (event) => beginGuideDrag("horizontal", event),
789
+ onPointerMove: moveGuide,
790
+ onPointerUp: finishGuideDrag,
791
+ onPointerCancel: cancelGuideDrag,
792
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
793
+ "svg",
794
+ {
795
+ className: "msr:block msr:h-6",
796
+ width: RULER_LENGTH,
797
+ height: RULER_SIZE,
798
+ viewBox: `0 0 ${RULER_LENGTH} ${RULER_SIZE}`,
799
+ children: [
800
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("defs", { children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
801
+ "linearGradient",
802
+ {
803
+ id: "ruler-label-fade-x",
804
+ x1: "0%",
805
+ x2: "100%",
806
+ gradientUnits: "objectBoundingBox",
807
+ children: [
808
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("stop", { offset: "0", stopColor: "white", stopOpacity: "0" }),
809
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("stop", { offset: "0.2", stopColor: "white" }),
810
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("stop", { offset: "0.8", stopColor: "white" }),
811
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("stop", { offset: "1", stopColor: "white", stopOpacity: "0" })
812
+ ]
813
+ }
814
+ ) }),
815
+ ticks.map((x) => {
816
+ const major = x % 100 === 0;
817
+ const medium = x % 50 === 0;
818
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("g", { children: [
819
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
820
+ "line",
821
+ {
822
+ x1: x,
823
+ y1: RULER_SIZE,
824
+ x2: x,
825
+ y2: major ? 8 : medium ? 13 : 17,
826
+ stroke: "currentColor",
827
+ strokeWidth: "1"
828
+ }
829
+ ),
830
+ major ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("text", { x, y: "8", textAnchor: "middle", fill: "currentColor", children: x }) : null
831
+ ] }, x);
832
+ }),
833
+ selectedVerticalGuides.map((guide) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("g", { children: [
834
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
835
+ "rect",
836
+ {
837
+ x: guide.position - RULER_SIZE - 8,
838
+ y: "-1",
839
+ width: "48",
840
+ height: "12",
841
+ fill: "url(#ruler-label-fade-x)"
842
+ }
843
+ ),
844
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
845
+ "text",
846
+ {
847
+ x: guide.position - RULER_SIZE + 16,
848
+ y: "8",
849
+ textAnchor: "middle",
850
+ fill: "#ef4444",
851
+ fontWeight: "600",
852
+ children: Math.round(guide.position - RULER_SIZE)
853
+ }
854
+ )
855
+ ] }, guide.id)),
856
+ dragOrientation === "vertical" && dragPosition !== null ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("g", { children: [
857
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
858
+ "rect",
859
+ {
860
+ x: dragPosition - RULER_SIZE - 8,
861
+ y: "-1",
862
+ width: "48",
863
+ height: "12",
864
+ fill: "url(#ruler-label-fade-x)"
865
+ }
866
+ ),
867
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
868
+ "text",
869
+ {
870
+ x: dragPosition - RULER_SIZE + 16,
871
+ y: "8",
872
+ textAnchor: "middle",
873
+ fill: "#ef4444",
874
+ fontWeight: "600",
875
+ children: Math.round(dragPosition - RULER_SIZE)
876
+ }
877
+ )
878
+ ] }) : null
879
+ ]
880
+ }
881
+ )
882
+ }
883
+ ),
884
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
885
+ "div",
886
+ {
887
+ className: "msr:absolute msr:bottom-0 msr:left-0 msr:top-[18px] msr:w-[18px] msr:overflow-hidden msr:bg-white",
888
+ style: {
889
+ boxShadow: "1px 0 3px rgba(0, 0, 0, 0.12)",
890
+ width: RULER_SIZE,
891
+ cursor: "ew-resize",
892
+ pointerEvents: "auto"
893
+ },
894
+ onPointerDown: (event) => beginGuideDrag("vertical", event),
895
+ onPointerMove: moveGuide,
896
+ onPointerUp: finishGuideDrag,
897
+ onPointerCancel: cancelGuideDrag,
898
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
899
+ "svg",
900
+ {
901
+ className: "msr:block msr:w-6",
902
+ width: RULER_SIZE,
903
+ height: RULER_LENGTH,
904
+ viewBox: `0 0 ${RULER_SIZE} ${RULER_LENGTH}`,
905
+ children: [
906
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("defs", { children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
907
+ "linearGradient",
908
+ {
909
+ id: "ruler-label-fade-y",
910
+ y1: "0%",
911
+ y2: "100%",
912
+ gradientUnits: "objectBoundingBox",
913
+ children: [
914
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("stop", { offset: "0", stopColor: "white", stopOpacity: "0" }),
915
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("stop", { offset: "0.2", stopColor: "white" }),
916
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("stop", { offset: "0.8", stopColor: "white" }),
917
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("stop", { offset: "1", stopColor: "white", stopOpacity: "0" })
918
+ ]
919
+ }
920
+ ) }),
921
+ ticks.map((y) => {
922
+ const major = y % 100 === 0;
923
+ const medium = y % 50 === 0;
924
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("g", { children: [
925
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
926
+ "line",
927
+ {
928
+ x1: RULER_SIZE,
929
+ y1: y,
930
+ x2: major ? 8 : medium ? 13 : 17,
931
+ y2: y,
932
+ stroke: "currentColor",
933
+ strokeWidth: "1"
934
+ }
935
+ ),
936
+ major ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
937
+ "text",
938
+ {
939
+ x: "8",
940
+ y: y + 10,
941
+ fill: "currentColor",
942
+ transform: `rotate(-90 8 ${y + 10})`,
943
+ children: y
944
+ }
945
+ ) : null
946
+ ] }, y);
947
+ }),
948
+ selectedHorizontalGuides.map((guide) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("g", { children: [
949
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
950
+ "rect",
951
+ {
952
+ x: "-3",
953
+ y: guide.position - RULER_SIZE - 38,
954
+ width: "24",
955
+ height: "48",
956
+ fill: "url(#ruler-label-fade-y)"
957
+ }
958
+ ),
959
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
960
+ "text",
961
+ {
962
+ x: "9",
963
+ y: guide.position - RULER_SIZE - 16,
964
+ textAnchor: "middle",
965
+ fill: "#ef4444",
966
+ fontWeight: "600",
967
+ transform: `rotate(-90 9 ${guide.position - RULER_SIZE - 16})`,
968
+ children: Math.round(guide.position - RULER_SIZE)
969
+ }
970
+ )
971
+ ] }, guide.id)),
972
+ dragOrientation === "horizontal" && dragPosition !== null ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("g", { children: [
973
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
974
+ "rect",
975
+ {
976
+ x: "-3",
977
+ y: dragPosition - RULER_SIZE - 38,
978
+ width: "24",
979
+ height: "48",
980
+ fill: "url(#ruler-label-fade-y)"
981
+ }
982
+ ),
983
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
984
+ "text",
985
+ {
986
+ x: "9",
987
+ y: dragPosition - RULER_SIZE - 16,
988
+ textAnchor: "middle",
989
+ fill: "#ef4444",
990
+ fontWeight: "600",
991
+ transform: `rotate(-90 9 ${dragPosition - RULER_SIZE - 16})`,
992
+ children: Math.round(dragPosition - RULER_SIZE)
993
+ }
994
+ )
995
+ ] }) : null
996
+ ]
997
+ }
998
+ )
999
+ }
1000
+ ),
1001
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "msr:absolute msr:left-0 msr:top-0 msr:size-[18px] msr:bg-white" })
1002
+ ]
1003
+ }
1004
+ );
1005
+ });
1006
+
1007
+ // hooks/use-drag-state.ts
1008
+ var import_react3 = require("react");
564
1009
  var useDragState = () => {
565
- const [start, setStart] = (0, import_react2.useState)(null);
566
- const [end, setEnd] = (0, import_react2.useState)(null);
567
- const [isDragging, setIsDragging] = (0, import_react2.useState)(false);
1010
+ const [start, setStart] = (0, import_react3.useState)(null);
1011
+ const [end, setEnd] = (0, import_react3.useState)(null);
1012
+ const [isDragging, setIsDragging] = (0, import_react3.useState)(false);
568
1013
  return { start, setStart, end, setEnd, isDragging, setIsDragging };
569
1014
  };
570
1015
 
571
1016
  // hooks/use-guide-drag-hold.ts
572
- var import_react3 = require("react");
573
- var useGuideDragHold = () => {
574
- const guideDragHoldTimerRef = (0, import_react3.useRef)(null);
575
- const guideDragHoldGuideIdRef = (0, import_react3.useRef)(null);
576
- const clearGuideDragHold = (0, import_react3.useCallback)(() => {
1017
+ var import_react4 = require("react");
1018
+ var useGuideDragHold = (eventTarget) => {
1019
+ const guideDragHoldTimerRef = (0, import_react4.useRef)(null);
1020
+ const guideDragHoldGuideIdRef = (0, import_react4.useRef)(null);
1021
+ const clearGuideDragHold = (0, import_react4.useCallback)(() => {
577
1022
  if (guideDragHoldTimerRef.current !== null) {
578
- window.clearTimeout(guideDragHoldTimerRef.current);
1023
+ eventTarget.clearTimeout(guideDragHoldTimerRef.current);
579
1024
  guideDragHoldTimerRef.current = null;
580
1025
  }
581
1026
  guideDragHoldGuideIdRef.current = null;
582
1027
  }, []);
583
- const scheduleGuideDragHold = (0, import_react3.useCallback)(
1028
+ const scheduleGuideDragHold = (0, import_react4.useCallback)(
584
1029
  (id, onHold) => {
585
1030
  clearGuideDragHold();
586
1031
  guideDragHoldGuideIdRef.current = id;
587
- guideDragHoldTimerRef.current = window.setTimeout(() => {
1032
+ guideDragHoldTimerRef.current = eventTarget.setTimeout(() => {
588
1033
  if (guideDragHoldGuideIdRef.current === id) {
589
1034
  onHold(id);
590
1035
  }
@@ -599,11 +1044,11 @@ var useGuideDragHold = () => {
599
1044
  };
600
1045
 
601
1046
  // hooks/use-guide-state.ts
602
- var import_react4 = require("react");
1047
+ var import_react5 = require("react");
603
1048
  var useGuideState = (options = {}) => {
604
- const [guides, setGuides] = (0, import_react4.useState)(options.initialGuides ?? []);
605
- const [draggingGuideId, setDraggingGuideId] = (0, import_react4.useState)(null);
606
- const [selectedGuideIds, setSelectedGuideIds] = (0, import_react4.useState)(
1049
+ const [guides, setGuides] = (0, import_react5.useState)(options.initialGuides ?? []);
1050
+ const [draggingGuideId, setDraggingGuideId] = (0, import_react5.useState)(null);
1051
+ const [selectedGuideIds, setSelectedGuideIds] = (0, import_react5.useState)(
607
1052
  options.initialSelectedGuideIds ?? []
608
1053
  );
609
1054
  return {
@@ -617,19 +1062,37 @@ var useGuideState = (options = {}) => {
617
1062
  };
618
1063
 
619
1064
  // hooks/use-hotkeys.ts
620
- var import_react5 = require("react");
1065
+ var import_react6 = require("react");
621
1066
  var useHotkeys = (options) => {
622
- (0, import_react5.useEffect)(() => {
1067
+ (0, import_react6.useEffect)(() => {
1068
+ const target = options.eventTarget;
623
1069
  const handleKeyDown = (event) => {
624
1070
  if (event.key === "Escape") {
625
1071
  options.clearAll();
626
1072
  return;
627
1073
  }
1074
+ if (event.metaKey || event.ctrlKey) {
1075
+ if (event.key.toLowerCase() !== "z") return;
1076
+ if (event.shiftKey) {
1077
+ event.preventDefault();
1078
+ options.redo();
1079
+ return;
1080
+ }
1081
+ event.preventDefault();
1082
+ options.undo();
1083
+ return;
1084
+ }
628
1085
  if (event.key.toLowerCase() === "m") {
629
1086
  options.setEnabled((prev) => !prev);
630
1087
  }
631
1088
  const key = event.key.toLowerCase();
632
1089
  if (options.isOverlayActive()) {
1090
+ if (key === "a") {
1091
+ options.setToolMode(
1092
+ (prev) => prev === "text-inspector" ? "none" : "text-inspector"
1093
+ );
1094
+ options.onInteract();
1095
+ }
633
1096
  if (key === "s") {
634
1097
  options.setToolMode((prev) => prev === "select" ? "none" : "select");
635
1098
  options.onInteract();
@@ -638,6 +1101,14 @@ var useHotkeys = (options) => {
638
1101
  options.setToolMode((prev) => prev === "guides" ? "none" : "guides");
639
1102
  options.onInteract();
640
1103
  }
1104
+ if (key === "x") {
1105
+ options.setToolMode((prev) => prev === "xray" ? "none" : "xray");
1106
+ options.onInteract();
1107
+ }
1108
+ if (key === "r") {
1109
+ options.setRulersVisible((prev) => !prev);
1110
+ options.onInteract();
1111
+ }
641
1112
  if (key === "h") {
642
1113
  options.setGuideOrientation("horizontal");
643
1114
  options.onInteract();
@@ -662,22 +1133,22 @@ var useHotkeys = (options) => {
662
1133
  options.setAltPressed(false);
663
1134
  }
664
1135
  };
665
- window.addEventListener("keydown", handleKeyDown);
666
- window.addEventListener("keyup", handleKeyUp);
1136
+ target.addEventListener("keydown", handleKeyDown);
1137
+ target.addEventListener("keyup", handleKeyUp);
667
1138
  return () => {
668
- window.removeEventListener("keydown", handleKeyDown);
669
- window.removeEventListener("keyup", handleKeyUp);
1139
+ target.removeEventListener("keydown", handleKeyDown);
1140
+ target.removeEventListener("keyup", handleKeyUp);
670
1141
  };
671
1142
  }, [options]);
672
1143
  };
673
1144
 
674
1145
  // hooks/use-live-element-tracking.ts
675
- var import_react6 = require("react");
1146
+ var import_react7 = require("react");
676
1147
 
677
- // geometry.ts
678
- var getViewportSize = () => ({
679
- width: typeof window === "undefined" ? 1 : window.innerWidth || 1,
680
- height: typeof window === "undefined" ? 1 : window.innerHeight || 1
1148
+ // core/geometry.ts
1149
+ var getViewportSize = (ownerWindow) => ({
1150
+ width: ownerWindow?.innerWidth || (typeof window === "undefined" ? 1 : window.innerWidth) || 1,
1151
+ height: ownerWindow?.innerHeight || (typeof window === "undefined" ? 1 : window.innerHeight) || 1
681
1152
  });
682
1153
  var normalizeRect = (rect, viewport = getViewportSize()) => ({
683
1154
  left: rect.left / viewport.width,
@@ -719,9 +1190,9 @@ var getDistanceToRect = (point, rect) => {
719
1190
  return Math.hypot(dx, dy);
720
1191
  };
721
1192
 
722
- // distances.ts
723
- var getDistanceOverlay = (rectA, rectB, elementRefA, elementRefB) => {
724
- const viewport = getViewportSize();
1193
+ // core/distances.ts
1194
+ var getDistanceOverlay = (rectA, rectB, elementRefA, elementRefB, ownerWindow = window) => {
1195
+ const viewport = getViewportSize(ownerWindow);
725
1196
  const normalizedRectA = normalizeRect(rectA, viewport);
726
1197
  const normalizedRectB = normalizeRect(rectB, viewport);
727
1198
  const rightA = rectA.left + rectA.width;
@@ -762,10 +1233,10 @@ var getDistanceOverlay = (rectA, rectB, elementRefA, elementRefB) => {
762
1233
  }
763
1234
  }
764
1235
  const normalizedConnectors = connectors.map((segment) => ({
765
- x1: clamp(segment.x1, 0, window.innerWidth),
766
- y1: clamp(segment.y1, 0, window.innerHeight),
767
- x2: clamp(segment.x2, 0, window.innerWidth),
768
- y2: clamp(segment.y2, 0, window.innerHeight)
1236
+ x1: clamp(segment.x1, 0, ownerWindow.innerWidth),
1237
+ y1: clamp(segment.y1, 0, ownerWindow.innerHeight),
1238
+ x2: clamp(segment.x2, 0, ownerWindow.innerWidth),
1239
+ y2: clamp(segment.y2, 0, ownerWindow.innerHeight)
769
1240
  })).filter(
770
1241
  (segment) => Math.abs(segment.x1 - segment.x2) > 0.5 || Math.abs(segment.y1 - segment.y2) > 0.5
771
1242
  );
@@ -782,17 +1253,17 @@ var getDistanceOverlay = (rectA, rectB, elementRefA, elementRefB) => {
782
1253
  connectors: normalizedConnectors
783
1254
  };
784
1255
  };
785
- var updateDistanceForResize = (distance, viewport = getViewportSize()) => {
786
- const normalizedRectA = distance.normalizedRectA ?? normalizeRect(distance.rectA);
787
- const normalizedRectB = distance.normalizedRectB ?? normalizeRect(distance.rectB);
1256
+ var updateDistanceForResize = (distance, viewport = getViewportSize(), ownerDocument = document, ownerWindow = window) => {
1257
+ const normalizedRectA = distance.normalizedRectA ?? normalizeRect(distance.rectA, viewport);
1258
+ const normalizedRectB = distance.normalizedRectB ?? normalizeRect(distance.rectB, viewport);
788
1259
  let rectA = distance.rectA;
789
1260
  let rectB = distance.rectB;
790
- if (distance.elementRefA && document.contains(distance.elementRefA)) {
1261
+ if (distance.elementRefA && ownerDocument.contains(distance.elementRefA)) {
791
1262
  rectA = distance.elementRefA.getBoundingClientRect();
792
1263
  } else {
793
1264
  rectA = denormalizeRect(normalizedRectA, viewport);
794
1265
  }
795
- if (distance.elementRefB && document.contains(distance.elementRefB)) {
1266
+ if (distance.elementRefB && ownerDocument.contains(distance.elementRefB)) {
796
1267
  rectB = distance.elementRefB.getBoundingClientRect();
797
1268
  } else {
798
1269
  rectB = denormalizeRect(normalizedRectB, viewport);
@@ -801,7 +1272,8 @@ var updateDistanceForResize = (distance, viewport = getViewportSize()) => {
801
1272
  rectA,
802
1273
  rectB,
803
1274
  distance.elementRefA,
804
- distance.elementRefB
1275
+ distance.elementRefB,
1276
+ ownerWindow
805
1277
  );
806
1278
  return {
807
1279
  ...updated,
@@ -809,7 +1281,7 @@ var updateDistanceForResize = (distance, viewport = getViewportSize()) => {
809
1281
  };
810
1282
  };
811
1283
 
812
- // dom.ts
1284
+ // core/dom.ts
813
1285
  var getElementLabel = (element) => {
814
1286
  const tag = element.tagName.toLowerCase();
815
1287
  const id = element.id ? `#${element.id}` : "";
@@ -842,24 +1314,38 @@ var getRectFromDomCached = (element) => {
842
1314
  };
843
1315
  var cachedElements = [];
844
1316
  var cachedFrame = -1;
1317
+ var cachedDocument = null;
845
1318
  var getFrameToken = () => {
846
1319
  if (typeof performance === "undefined") return 0;
847
1320
  return Math.floor(performance.now() / 16);
848
1321
  };
849
- var getBodyElementsCached = () => {
1322
+ var getBodyElementsCached = (ownerDocument = document) => {
850
1323
  const frame = getFrameToken();
851
- if (frame === cachedFrame && cachedElements.length > 0) {
1324
+ if (frame === cachedFrame && cachedDocument === ownerDocument && cachedElements.length > 0) {
852
1325
  return cachedElements;
853
1326
  }
854
1327
  cachedFrame = frame;
855
- cachedElements = Array.from(document.querySelectorAll("body *")).filter(
856
- (element) => element instanceof HTMLElement
857
- );
1328
+ cachedDocument = ownerDocument;
1329
+ const elements = [];
1330
+ const HTMLElementConstructor = ownerDocument.defaultView?.HTMLElement ?? HTMLElement;
1331
+ const visit = (root) => {
1332
+ const walker = ownerDocument.createTreeWalker(root, 1);
1333
+ let node = walker.nextNode();
1334
+ while (node) {
1335
+ if (node instanceof HTMLElementConstructor) {
1336
+ elements.push(node);
1337
+ if (node.shadowRoot) visit(node.shadowRoot);
1338
+ }
1339
+ node = walker.nextNode();
1340
+ }
1341
+ };
1342
+ if (ownerDocument.body) visit(ownerDocument.body);
1343
+ cachedElements = elements;
858
1344
  return cachedElements;
859
1345
  };
860
- var getInspectMeasurement = (element) => {
1346
+ var getInspectMeasurement = (element, ownerWindow = window) => {
861
1347
  const rect = element.getBoundingClientRect();
862
- const style = window.getComputedStyle(element);
1348
+ const style = ownerWindow.getComputedStyle(element);
863
1349
  const padding = {
864
1350
  top: parseEdge(style.paddingTop),
865
1351
  right: parseEdge(style.paddingRight),
@@ -900,9 +1386,9 @@ var getInspectMeasurement = (element) => {
900
1386
  elementRef: element
901
1387
  };
902
1388
  };
903
- var updateMeasurementForResize = (measurement, viewport = getViewportSize()) => {
1389
+ var updateMeasurementForResize = (measurement, viewport = getViewportSize(), ownerDocument = document) => {
904
1390
  let rect = measurement.rect;
905
- if (measurement.elementRef && document.contains(measurement.elementRef)) {
1391
+ if (measurement.elementRef && ownerDocument.contains(measurement.elementRef)) {
906
1392
  rect = getRectFromDom(measurement.elementRef);
907
1393
  } else if (measurement.normalizedRect) {
908
1394
  rect = denormalizeRect(measurement.normalizedRect, viewport);
@@ -917,11 +1403,11 @@ var updateMeasurementForResize = (measurement, viewport = getViewportSize()) =>
917
1403
 
918
1404
  // hooks/use-live-element-tracking.ts
919
1405
  var useLiveElementTracking = (params) => {
920
- const frameRef = (0, import_react6.useRef)(null);
921
- (0, import_react6.useEffect)(() => {
1406
+ const frameRef = (0, import_react7.useRef)(null);
1407
+ (0, import_react7.useEffect)(() => {
922
1408
  if (!params.enabled) {
923
1409
  if (frameRef.current) {
924
- cancelAnimationFrame(frameRef.current);
1410
+ params.window.cancelAnimationFrame(frameRef.current);
925
1411
  }
926
1412
  frameRef.current = null;
927
1413
  return;
@@ -929,7 +1415,7 @@ var useLiveElementTracking = (params) => {
929
1415
  const tick = () => {
930
1416
  params.setMeasurements(
931
1417
  (prev) => prev.map((measurement) => {
932
- if (!measurement.elementRef || !document.contains(measurement.elementRef)) {
1418
+ if (!measurement.elementRef || !params.document.contains(measurement.elementRef)) {
933
1419
  return measurement;
934
1420
  }
935
1421
  const rect = getRectFromDom(measurement.elementRef);
@@ -943,7 +1429,7 @@ var useLiveElementTracking = (params) => {
943
1429
  })
944
1430
  );
945
1431
  params.setActiveMeasurement((prev) => {
946
- if (!prev?.elementRef || !document.contains(prev.elementRef))
1432
+ if (!prev?.elementRef || !params.document.contains(prev.elementRef))
947
1433
  return prev;
948
1434
  const rect = getRectFromDom(prev.elementRef);
949
1435
  if (rectAlmostEqual(rect, prev.rect)) return prev;
@@ -956,8 +1442,8 @@ var useLiveElementTracking = (params) => {
956
1442
  });
957
1443
  params.setHeldDistances(
958
1444
  (prev) => prev.map((distance) => {
959
- const canTrackA = distance.elementRefA && document.contains(distance.elementRefA);
960
- const canTrackB = distance.elementRefB && document.contains(distance.elementRefB);
1445
+ const canTrackA = distance.elementRefA && params.document.contains(distance.elementRefA);
1446
+ const canTrackB = distance.elementRefB && params.document.contains(distance.elementRefB);
961
1447
  if (!canTrackA && !canTrackB) return distance;
962
1448
  const rectA = canTrackA ? getRectFromDom(distance.elementRefA) : distance.rectA;
963
1449
  const rectB = canTrackB ? getRectFromDom(distance.elementRefB) : distance.rectB;
@@ -968,7 +1454,8 @@ var useLiveElementTracking = (params) => {
968
1454
  rectA,
969
1455
  rectB,
970
1456
  distance.elementRefA,
971
- distance.elementRefB
1457
+ distance.elementRefB,
1458
+ params.window
972
1459
  );
973
1460
  return {
974
1461
  ...updated,
@@ -977,19 +1464,19 @@ var useLiveElementTracking = (params) => {
977
1464
  })
978
1465
  );
979
1466
  const selected = params.selectedElementRef.current;
980
- if (selected && document.contains(selected)) {
1467
+ if (selected && params.document.contains(selected)) {
981
1468
  params.setSelectedMeasurement((prev) => {
982
- const next = getInspectMeasurement(selected);
1469
+ const next = getInspectMeasurement(selected, params.window);
983
1470
  if (prev && rectAlmostEqual(prev.rect, next.rect)) return prev;
984
1471
  return next;
985
1472
  });
986
1473
  }
987
1474
  params.setSelectedMeasurements(
988
1475
  (prev) => prev.map((measurement) => {
989
- if (!measurement.elementRef || !document.contains(measurement.elementRef)) {
1476
+ if (!measurement.elementRef || !params.document.contains(measurement.elementRef)) {
990
1477
  return measurement;
991
1478
  }
992
- const next = getInspectMeasurement(measurement.elementRef);
1479
+ const next = getInspectMeasurement(measurement.elementRef, params.window);
993
1480
  if (rectAlmostEqual(next.rect, measurement.rect)) return measurement;
994
1481
  return {
995
1482
  ...next,
@@ -998,18 +1485,18 @@ var useLiveElementTracking = (params) => {
998
1485
  })
999
1486
  );
1000
1487
  const hover = params.hoverElementRef.current;
1001
- if (hover && document.contains(hover)) {
1488
+ if (hover && params.document.contains(hover)) {
1002
1489
  const rect = getRectFromDom(hover);
1003
1490
  params.setHoverRect(
1004
1491
  (prev) => prev && rectAlmostEqual(prev, rect) ? prev : rect
1005
1492
  );
1006
1493
  }
1007
- frameRef.current = requestAnimationFrame(tick);
1494
+ frameRef.current = params.window.requestAnimationFrame(tick);
1008
1495
  };
1009
- frameRef.current = requestAnimationFrame(tick);
1496
+ frameRef.current = params.window.requestAnimationFrame(tick);
1010
1497
  return () => {
1011
1498
  if (frameRef.current) {
1012
- cancelAnimationFrame(frameRef.current);
1499
+ params.window.cancelAnimationFrame(frameRef.current);
1013
1500
  }
1014
1501
  frameRef.current = null;
1015
1502
  };
@@ -1017,13 +1504,16 @@ var useLiveElementTracking = (params) => {
1017
1504
  };
1018
1505
 
1019
1506
  // hooks/use-measure-toggles.ts
1020
- var import_react7 = require("react");
1507
+ var import_react8 = require("react");
1021
1508
  var useMeasureToggles = (options = {}) => {
1022
- const [enabled, setEnabled] = (0, import_react7.useState)(options.initialEnabled ?? true);
1023
- const [altPressed, setAltPressed] = (0, import_react7.useState)(false);
1024
- const [toolMode, setToolMode] = (0, import_react7.useState)(
1509
+ const [enabled, setEnabled] = (0, import_react8.useState)(options.initialEnabled ?? true);
1510
+ const [altPressed, setAltPressed] = (0, import_react8.useState)(false);
1511
+ const [toolMode, setToolMode] = (0, import_react8.useState)(
1025
1512
  options.initialToolMode ?? "none"
1026
1513
  );
1514
+ const [rulersVisible, setRulersVisible] = (0, import_react8.useState)(
1515
+ options.initialRulersVisible ?? false
1516
+ );
1027
1517
  const holdEnabled = false;
1028
1518
  const multiMeasureEnabled = false;
1029
1519
  const snapGuidesEnabled = true;
@@ -1037,6 +1527,8 @@ var useMeasureToggles = (options = {}) => {
1037
1527
  setAltPressed,
1038
1528
  toolMode,
1039
1529
  setToolMode,
1530
+ rulersVisible,
1531
+ setRulersVisible,
1040
1532
  guidesEnabled,
1041
1533
  multiMeasureEnabled,
1042
1534
  snapGuidesEnabled,
@@ -1045,16 +1537,16 @@ var useMeasureToggles = (options = {}) => {
1045
1537
  };
1046
1538
 
1047
1539
  // hooks/use-measurement-state.ts
1048
- var import_react8 = require("react");
1540
+ var import_react9 = require("react");
1049
1541
  var useMeasurementState = (options = {}) => {
1050
- const [activeMeasurement, setActiveMeasurement] = (0, import_react8.useState)(options.initialActiveMeasurement ?? null);
1051
- const [measurements, setMeasurements] = (0, import_react8.useState)(
1542
+ const [activeMeasurement, setActiveMeasurement] = (0, import_react9.useState)(options.initialActiveMeasurement ?? null);
1543
+ const [measurements, setMeasurements] = (0, import_react9.useState)(
1052
1544
  options.initialMeasurements ?? []
1053
1545
  );
1054
- const [selectedMeasurement, setSelectedMeasurement] = (0, import_react8.useState)(null);
1055
- const [selectedMeasurements, setSelectedMeasurements] = (0, import_react8.useState)([]);
1056
- const [hoverRect, setHoverRect] = (0, import_react8.useState)(null);
1057
- const [heldDistances, setHeldDistances] = (0, import_react8.useState)(
1546
+ const [selectedMeasurement, setSelectedMeasurement] = (0, import_react9.useState)(null);
1547
+ const [selectedMeasurements, setSelectedMeasurements] = (0, import_react9.useState)([]);
1548
+ const [hoverRect, setHoverRect] = (0, import_react9.useState)(null);
1549
+ const [heldDistances, setHeldDistances] = (0, import_react9.useState)(
1058
1550
  options.initialHeldDistances ?? []
1059
1551
  );
1060
1552
  return {
@@ -1074,9 +1566,9 @@ var useMeasurementState = (options = {}) => {
1074
1566
  };
1075
1567
 
1076
1568
  // hooks/use-measurer-derived.ts
1077
- var import_react9 = require("react");
1569
+ var import_react10 = require("react");
1078
1570
 
1079
- // edge-visibility.ts
1571
+ // core/edge-visibility.ts
1080
1572
  var rangesOverlap = (a1, a2, b1, b2) => Math.min(a2, b2) - Math.max(a1, b1) > 0;
1081
1573
  var getEdgeVisibilityForRects = (rects, epsilon = 1) => {
1082
1574
  const visibility = rects.map(() => ({
@@ -1116,24 +1608,25 @@ var getEdgeVisibilityForRects = (rects, epsilon = 1) => {
1116
1608
  return visibility;
1117
1609
  };
1118
1610
 
1119
- // guides.ts
1120
- var getGuideRect = (guide) => {
1121
- const viewport = getViewportSize();
1611
+ // core/guides.ts
1612
+ var getGuideRect = (guide, ownerWindow = window) => {
1613
+ const viewport = getViewportSize(ownerWindow);
1122
1614
  return guide.orientation === "vertical" ? { left: guide.position, top: 0, width: 1, height: viewport.height } : { left: 0, top: guide.position, width: viewport.width, height: 1 };
1123
1615
  };
1124
1616
  var getGuideDistance = (guide, point) => guide.orientation === "vertical" ? Math.abs(guide.position - point.x) : Math.abs(guide.position - point.y);
1125
1617
  var getSnapGuidePosition = (params) => {
1618
+ const ownerDocument = params.document ?? document;
1126
1619
  const { orientation, point, snapGuidesEnabled, overlayNode, guides } = params;
1127
1620
  if (!snapGuidesEnabled) {
1128
1621
  return orientation === "vertical" ? point.x : point.y;
1129
1622
  }
1130
- const elements = getBodyElementsCached();
1623
+ const elements = getBodyElementsCached(ownerDocument);
1131
1624
  let bestValue = orientation === "vertical" ? point.x : point.y;
1132
1625
  let bestDistance = GUIDE_SNAP_DISTANCE + 1;
1133
1626
  for (const element of elements) {
1134
- if (!(element instanceof HTMLElement)) continue;
1627
+ if (!(element instanceof (ownerDocument.defaultView?.HTMLElement ?? HTMLElement))) continue;
1135
1628
  if (overlayNode && overlayNode.contains(element)) continue;
1136
- if (element === document.body || element === document.documentElement)
1629
+ if (element === ownerDocument.body || element === ownerDocument.documentElement)
1137
1630
  continue;
1138
1631
  const rect = getRectFromDomCached(element);
1139
1632
  if (rect.width <= 2 || rect.height <= 2) continue;
@@ -1169,7 +1662,7 @@ var getSnapGuidePosition = (params) => {
1169
1662
  return bestDistance <= GUIDE_SNAP_DISTANCE ? bestValue : orientation === "vertical" ? point.x : point.y;
1170
1663
  };
1171
1664
 
1172
- // option-measurements.ts
1665
+ // core/option-measurements.ts
1173
1666
  var getHoveredGuide = (point, guides) => {
1174
1667
  if (!point || guides.length === 0) return null;
1175
1668
  let closest = null;
@@ -1190,6 +1683,8 @@ var getSelectedGuide = (guides, selectedGuideIds) => {
1190
1683
  return guides.find((guide) => guide.id === lastId) ?? null;
1191
1684
  };
1192
1685
  var getOptionPairOverlay = (params) => {
1686
+ const ownerDocument = params.document ?? document;
1687
+ const ownerWindow = params.window ?? window;
1193
1688
  if (!params.altPressed) return null;
1194
1689
  if (!params.selectedGuide && !params.primarySelectedMeasurement) return null;
1195
1690
  const primary = params.primarySelectedMeasurement;
@@ -1218,10 +1713,13 @@ var getOptionPairOverlay = (params) => {
1218
1713
  selectedTarget.rect,
1219
1714
  hoverTarget.rect,
1220
1715
  selectedTarget.element ?? null,
1221
- hoverTarget.element ?? null
1716
+ hoverTarget.element ?? null,
1717
+ ownerWindow
1222
1718
  );
1223
1719
  };
1224
1720
  var getOptionContainerLines = (params) => {
1721
+ const ownerDocument = params.document ?? document;
1722
+ const ownerWindow = params.window ?? window;
1225
1723
  if (!params.altPressed || !params.primarySelectedMeasurement || params.optionPairOverlay)
1226
1724
  return null;
1227
1725
  if (params.selectedGuideIds.length > 0) return null;
@@ -1229,11 +1727,11 @@ var getOptionContainerLines = (params) => {
1229
1727
  if (params.selectedElement && params.hoverElement && params.hoverElement !== params.selectedElement && params.hoverElement.contains(params.selectedElement)) {
1230
1728
  containerElement = params.hoverElement;
1231
1729
  }
1232
- const containerRect = containerElement && containerElement !== document.body && containerElement !== document.documentElement ? getRectFromDom(containerElement) : {
1730
+ const containerRect = containerElement && containerElement !== ownerDocument.body && containerElement !== ownerDocument.documentElement ? getRectFromDom(containerElement) : {
1233
1731
  left: 0,
1234
1732
  top: 0,
1235
- width: window.innerWidth,
1236
- height: window.innerHeight
1733
+ width: ownerWindow.innerWidth,
1734
+ height: ownerWindow.innerHeight
1237
1735
  };
1238
1736
  const rect = params.primarySelectedMeasurement.rect;
1239
1737
  const right = rect.left + rect.width;
@@ -1272,12 +1770,20 @@ var getOptionContainerLines = (params) => {
1272
1770
  };
1273
1771
  };
1274
1772
 
1275
- // selection-helpers.ts
1773
+ // core/selection-helpers.ts
1774
+ var getOverlayHost = (overlayNode) => {
1775
+ if (!overlayNode) return null;
1776
+ const rootNode = overlayNode.getRootNode();
1777
+ return rootNode.nodeType === 11 ? rootNode.host : null;
1778
+ };
1276
1779
  var getPrimarySelectedMeasurement = (selectedMeasurements, selectedMeasurement) => selectedMeasurements.length > 0 ? selectedMeasurements[selectedMeasurements.length - 1] : selectedMeasurement;
1277
1780
  var getSelectedMeasurementHit = (params) => {
1781
+ const ownerDocument = params.document ?? document;
1782
+ const HTMLElementConstructor = ownerDocument.defaultView?.HTMLElement ?? HTMLElement;
1783
+ const overlayHost = getOverlayHost(params.overlayNode);
1278
1784
  const candidates = params.selectedMeasurements.map((measurement) => {
1279
1785
  const element = measurement.elementRef;
1280
- if (!element || !document.contains(element)) return null;
1786
+ if (!element || !ownerDocument.contains(element)) return null;
1281
1787
  const rect = getRectFromDom(element);
1282
1788
  return {
1283
1789
  measurement,
@@ -1287,10 +1793,11 @@ var getSelectedMeasurementHit = (params) => {
1287
1793
  };
1288
1794
  }).filter((item) => item !== null).sort((a, b) => a.area - b.area);
1289
1795
  if (candidates.length === 0) return null;
1290
- const stack = document.elementsFromPoint(params.point.x, params.point.y);
1796
+ const stack = ownerDocument.elementsFromPoint(params.point.x, params.point.y);
1291
1797
  for (const element of stack) {
1292
- if (!(element instanceof HTMLElement)) continue;
1798
+ if (!(element instanceof HTMLElementConstructor)) continue;
1293
1799
  if (params.overlayNode && params.overlayNode.contains(element)) continue;
1800
+ if (overlayHost && element === overlayHost) continue;
1294
1801
  for (const candidate of candidates) {
1295
1802
  if (candidate.element === element || candidate.element.contains(element)) {
1296
1803
  return candidate.measurement;
@@ -1307,6 +1814,8 @@ var getSelectedMeasurementHit = (params) => {
1307
1814
 
1308
1815
  // hooks/use-measurer-derived.ts
1309
1816
  var useMeasurerDerived = ({
1817
+ document: document2,
1818
+ window: window2,
1310
1819
  start,
1311
1820
  end,
1312
1821
  selectedMeasurements,
@@ -1326,7 +1835,7 @@ var useMeasurerDerived = ({
1326
1835
  highlightColor,
1327
1836
  guideColor
1328
1837
  }) => {
1329
- const activeRect = (0, import_react9.useMemo)(() => {
1838
+ const activeRect = (0, import_react10.useMemo)(() => {
1330
1839
  if (!start || !end) return null;
1331
1840
  return {
1332
1841
  left: Math.min(start.x, end.x),
@@ -1337,7 +1846,7 @@ var useMeasurerDerived = ({
1337
1846
  }, [end, start]);
1338
1847
  const activeWidth = activeRect ? formatValue(activeRect.width) : 0;
1339
1848
  const activeHeight = activeRect ? formatValue(activeRect.height) : 0;
1340
- const groupedSelection = (0, import_react9.useMemo)(() => {
1849
+ const groupedSelection = (0, import_react10.useMemo)(() => {
1341
1850
  if (selectedMeasurements.length <= 1) return null;
1342
1851
  const selectionKey = selectedMeasurements.map((measurement) => measurement.id).join("|");
1343
1852
  const rects = selectedMeasurements.map((measurement) => measurement.rect);
@@ -1367,31 +1876,33 @@ var useMeasurerDerived = ({
1367
1876
  originRect: selectionOriginRect ?? void 0
1368
1877
  };
1369
1878
  }, [selectedMeasurements, selectionOriginRect]);
1370
- const displayedSelectedMeasurements = (0, import_react9.useMemo)(
1879
+ const displayedSelectedMeasurements = (0, import_react10.useMemo)(
1371
1880
  () => groupedSelection ? [groupedSelection] : selectedMeasurements.length > 0 ? selectedMeasurements : selectedMeasurement ? [selectedMeasurement] : [],
1372
1881
  [groupedSelection, selectedMeasurement, selectedMeasurements]
1373
1882
  );
1374
- const selectedGuide = (0, import_react9.useMemo)(
1883
+ const selectedGuide = (0, import_react10.useMemo)(
1375
1884
  () => getSelectedGuide(guides, selectedGuideIds),
1376
1885
  [guides, selectedGuideIds]
1377
1886
  );
1378
- const hoverGuide = (0, import_react9.useMemo)(
1887
+ const hoverGuide = (0, import_react10.useMemo)(
1379
1888
  () => getHoveredGuide(hoverPointer, guides),
1380
1889
  [guides, hoverPointer]
1381
1890
  );
1382
- const primarySelectedMeasurement = (0, import_react9.useMemo)(
1891
+ const primarySelectedMeasurement = (0, import_react10.useMemo)(
1383
1892
  () => getPrimarySelectedMeasurement(selectedMeasurements, selectedMeasurement),
1384
1893
  [selectedMeasurement, selectedMeasurements]
1385
1894
  );
1386
1895
  const effectivePrimarySelected = groupedSelection ?? primarySelectedMeasurement;
1387
- const optionPairOverlay = (0, import_react9.useMemo)(() => {
1896
+ const optionPairOverlay = (0, import_react10.useMemo)(() => {
1388
1897
  return getOptionPairOverlay({
1389
1898
  altPressed,
1390
1899
  primarySelectedMeasurement: effectivePrimarySelected,
1391
1900
  selectedGuide,
1392
1901
  hoverGuide,
1393
1902
  hoverElement,
1394
- selectedElementRef: selectedElement
1903
+ selectedElementRef: selectedElement,
1904
+ document: document2,
1905
+ window: window2
1395
1906
  });
1396
1907
  }, [
1397
1908
  altPressed,
@@ -1401,14 +1912,16 @@ var useMeasurerDerived = ({
1401
1912
  selectedElement,
1402
1913
  selectedGuide
1403
1914
  ]);
1404
- const optionContainerLines = (0, import_react9.useMemo)(() => {
1915
+ const optionContainerLines = (0, import_react10.useMemo)(() => {
1405
1916
  return getOptionContainerLines({
1406
1917
  altPressed,
1407
1918
  primarySelectedMeasurement: effectivePrimarySelected,
1408
1919
  optionPairOverlay,
1409
1920
  selectedGuideIds,
1410
1921
  selectedElement,
1411
- hoverElement
1922
+ hoverElement,
1923
+ document: document2,
1924
+ window: window2
1412
1925
  });
1413
1926
  }, [
1414
1927
  altPressed,
@@ -1418,12 +1931,15 @@ var useMeasurerDerived = ({
1418
1931
  selectedElement,
1419
1932
  selectedGuideIds
1420
1933
  ]);
1421
- const guideDistanceOverlay = (0, import_react9.useMemo)(() => {
1934
+ const guideDistanceOverlay = (0, import_react10.useMemo)(() => {
1422
1935
  if (!altPressed || !guidesEnabled) return null;
1423
1936
  if (selectedGuide && hoverGuide && selectedGuide.id !== hoverGuide.id) {
1424
1937
  return getDistanceOverlay(
1425
- getGuideRect(selectedGuide),
1426
- getGuideRect(hoverGuide)
1938
+ getGuideRect(selectedGuide, window2),
1939
+ getGuideRect(hoverGuide, window2),
1940
+ null,
1941
+ null,
1942
+ window2
1427
1943
  );
1428
1944
  }
1429
1945
  if (!guidePreview) return null;
@@ -1446,8 +1962,11 @@ var useMeasurerDerived = ({
1446
1962
  );
1447
1963
  if (!nearest) return null;
1448
1964
  return getDistanceOverlay(
1449
- getGuideRect(previewGuide),
1450
- getGuideRect(nearest.guide)
1965
+ getGuideRect(previewGuide, window2),
1966
+ getGuideRect(nearest.guide, window2),
1967
+ null,
1968
+ null,
1969
+ window2
1451
1970
  );
1452
1971
  }, [
1453
1972
  altPressed,
@@ -1464,7 +1983,7 @@ var useMeasurerDerived = ({
1464
1983
  guideColorHover,
1465
1984
  guideColorDefault,
1466
1985
  guideColorPreview
1467
- } = (0, import_react9.useMemo)(
1986
+ } = (0, import_react10.useMemo)(
1468
1987
  () => ({
1469
1988
  outlineColor: `color-mix(in oklch, ${highlightColor} 80%, transparent)`,
1470
1989
  fillColor: `color-mix(in oklch, ${highlightColor} 8%, transparent)`,
@@ -1475,12 +1994,12 @@ var useMeasurerDerived = ({
1475
1994
  }),
1476
1995
  [guideColor, highlightColor]
1477
1996
  );
1478
- const selectedRects = (0, import_react9.useMemo)(
1997
+ const selectedRects = (0, import_react10.useMemo)(
1479
1998
  () => displayedSelectedMeasurements.map((measurement) => measurement.rect),
1480
1999
  [displayedSelectedMeasurements]
1481
2000
  );
1482
2001
  const hoverRectToShow = hoverHighlightEnabled && hoverRect && selectedMeasurements.length <= 1 ? hoverRect : null;
1483
- const selectionEdgeVisibility = (0, import_react9.useMemo)(
2002
+ const selectionEdgeVisibility = (0, import_react10.useMemo)(
1484
2003
  () => getEdgeVisibilityForRects(selectedRects),
1485
2004
  [selectedRects]
1486
2005
  );
@@ -1489,7 +2008,7 @@ var useMeasurerDerived = ({
1489
2008
  selectedRects.length
1490
2009
  );
1491
2010
  const hoverEdgeVisibility = hoverRectToShow ? { top: true, right: true, bottom: true, left: true } : null;
1492
- const measurementEdgeVisibility = (0, import_react9.useMemo)(
2011
+ const measurementEdgeVisibility = (0, import_react10.useMemo)(
1493
2012
  () => getEdgeVisibilityForRects(displayedMeasurements.map((m) => m.rect)),
1494
2013
  [displayedMeasurements]
1495
2014
  );
@@ -1517,7 +2036,7 @@ var useMeasurerDerived = ({
1517
2036
  };
1518
2037
 
1519
2038
  // hooks/use-measurer-history.ts
1520
- var import_react10 = require("react");
2039
+ var import_react11 = require("react");
1521
2040
  var HISTORY_LIMIT = 50;
1522
2041
  var useMeasurerHistory = ({
1523
2042
  toggles,
@@ -1563,10 +2082,10 @@ var useMeasurerHistory = ({
1563
2082
  setSelectedElement,
1564
2083
  clearSelectionRect
1565
2084
  } = transient;
1566
- const historyRef = (0, import_react10.useRef)([]);
1567
- const futureRef = (0, import_react10.useRef)([]);
1568
- const historySignatureRef = (0, import_react10.useRef)(null);
1569
- const captureSnapshot = (0, import_react10.useCallback)(() => {
2085
+ const historyRef = (0, import_react11.useRef)([]);
2086
+ const futureRef = (0, import_react11.useRef)([]);
2087
+ const historySignatureRef = (0, import_react11.useRef)(null);
2088
+ const captureSnapshot = (0, import_react11.useCallback)(() => {
1570
2089
  return {
1571
2090
  enabled,
1572
2091
  toolMode,
@@ -1593,7 +2112,7 @@ var useMeasurerHistory = ({
1593
2112
  selectedMeasurements,
1594
2113
  toolMode
1595
2114
  ]);
1596
- const getSnapshotSignature = (0, import_react10.useCallback)((snapshot) => {
2115
+ const getSnapshotSignature = (0, import_react11.useCallback)((snapshot) => {
1597
2116
  const serializeRect = (rect) => `${Math.round(rect.left)}:${Math.round(rect.top)}:${Math.round(rect.width)}:${Math.round(rect.height)}`;
1598
2117
  return [
1599
2118
  snapshot.enabled ? "1" : "0",
@@ -1609,7 +2128,7 @@ var useMeasurerHistory = ({
1609
2128
  snapshot.draggingGuideId ?? ""
1610
2129
  ].join("|");
1611
2130
  }, []);
1612
- const recordSnapshot = (0, import_react10.useCallback)(() => {
2131
+ const recordSnapshot = (0, import_react11.useCallback)(() => {
1613
2132
  const snapshot = captureSnapshot();
1614
2133
  const signature = getSnapshotSignature(snapshot);
1615
2134
  if (historySignatureRef.current === signature) return;
@@ -1620,7 +2139,7 @@ var useMeasurerHistory = ({
1620
2139
  historyRef.current.shift();
1621
2140
  }
1622
2141
  }, [captureSnapshot, getSnapshotSignature]);
1623
- const restoreSnapshot = (0, import_react10.useCallback)(
2142
+ const restoreSnapshot = (0, import_react11.useCallback)(
1624
2143
  (snapshot) => {
1625
2144
  setEnabled(snapshot.enabled);
1626
2145
  setToolMode(snapshot.toolMode);
@@ -1665,7 +2184,7 @@ var useMeasurerHistory = ({
1665
2184
  setHoverElement
1666
2185
  ]
1667
2186
  );
1668
- const undo = (0, import_react10.useCallback)(() => {
2187
+ const undo = (0, import_react11.useCallback)(() => {
1669
2188
  const previousSnapshot = historyRef.current.pop();
1670
2189
  if (!previousSnapshot) return;
1671
2190
  const currentSnapshot = captureSnapshot();
@@ -1676,7 +2195,7 @@ var useMeasurerHistory = ({
1676
2195
  historySignatureRef.current = null;
1677
2196
  restoreSnapshot(previousSnapshot);
1678
2197
  }, [captureSnapshot, restoreSnapshot]);
1679
- const redo = (0, import_react10.useCallback)(() => {
2198
+ const redo = (0, import_react11.useCallback)(() => {
1680
2199
  const nextSnapshot = futureRef.current.pop();
1681
2200
  if (!nextSnapshot) return;
1682
2201
  historyRef.current.push(captureSnapshot());
@@ -1686,7 +2205,7 @@ var useMeasurerHistory = ({
1686
2205
  historySignatureRef.current = null;
1687
2206
  restoreSnapshot(nextSnapshot);
1688
2207
  }, [captureSnapshot, restoreSnapshot]);
1689
- const setToolModeWithHistory = (0, import_react10.useCallback)(
2208
+ const setToolModeWithHistory = (0, import_react11.useCallback)(
1690
2209
  (value) => {
1691
2210
  setToolMode((prev) => {
1692
2211
  const next = typeof value === "function" ? value(prev) : value;
@@ -1697,7 +2216,7 @@ var useMeasurerHistory = ({
1697
2216
  },
1698
2217
  [recordSnapshot, setToolMode]
1699
2218
  );
1700
- const setGuideOrientationWithHistory = (0, import_react10.useCallback)(
2219
+ const setGuideOrientationWithHistory = (0, import_react11.useCallback)(
1701
2220
  (value) => {
1702
2221
  setGuideOrientation((prev) => {
1703
2222
  const next = typeof value === "function" ? value(prev) : value;
@@ -1708,7 +2227,7 @@ var useMeasurerHistory = ({
1708
2227
  },
1709
2228
  [recordSnapshot, setGuideOrientation]
1710
2229
  );
1711
- const setEnabledWithHistory = (0, import_react10.useCallback)(
2230
+ const setEnabledWithHistory = (0, import_react11.useCallback)(
1712
2231
  (value) => {
1713
2232
  setEnabled((prev) => {
1714
2233
  const next = typeof value === "function" ? value(prev) : value;
@@ -1719,7 +2238,7 @@ var useMeasurerHistory = ({
1719
2238
  },
1720
2239
  [recordSnapshot, setEnabled]
1721
2240
  );
1722
- const createActionCommit = (0, import_react10.useCallback)(() => {
2241
+ const createActionCommit = (0, import_react11.useCallback)(() => {
1723
2242
  let committed = false;
1724
2243
  return () => {
1725
2244
  if (committed) return;
@@ -1727,64 +2246,47 @@ var useMeasurerHistory = ({
1727
2246
  committed = true;
1728
2247
  };
1729
2248
  }, [recordSnapshot]);
1730
- (0, import_react10.useEffect)(() => {
1731
- const handleKeyDown = (event) => {
1732
- if (!(event.metaKey || event.ctrlKey)) return;
1733
- if (event.key.toLowerCase() !== "z") return;
1734
- if (event.shiftKey) {
1735
- if (futureRef.current.length === 0) return;
1736
- event.preventDefault();
1737
- redo();
1738
- return;
1739
- }
1740
- if (historyRef.current.length === 0) return;
1741
- event.preventDefault();
1742
- undo();
1743
- };
1744
- window.addEventListener("keydown", handleKeyDown);
1745
- return () => {
1746
- window.removeEventListener("keydown", handleKeyDown);
1747
- };
1748
- }, [redo, undo]);
1749
2249
  return {
1750
2250
  recordSnapshot,
1751
2251
  createActionCommit,
1752
2252
  setToolModeWithHistory,
1753
2253
  setGuideOrientationWithHistory,
1754
- setEnabledWithHistory
2254
+ setEnabledWithHistory,
2255
+ undo,
2256
+ redo
1755
2257
  };
1756
2258
  };
1757
2259
 
1758
2260
  // hooks/use-measurer-local-state.ts
1759
- var import_react11 = require("react");
2261
+ var import_react12 = require("react");
1760
2262
  var useMeasurerLocalState = ({
1761
2263
  selectedElementRef,
1762
2264
  hoverElementRef,
1763
2265
  selectionRectRef
1764
2266
  }) => {
1765
- const [selectionOriginRect, setSelectionOriginRect] = (0, import_react11.useState)(
2267
+ const [selectionOriginRect, setSelectionOriginRect] = (0, import_react12.useState)(
1766
2268
  null
1767
2269
  );
1768
- const [hoverPointer, setHoverPointer] = (0, import_react11.useState)(null);
1769
- const [hoverElement, setHoverElementState] = (0, import_react11.useState)(
2270
+ const [hoverPointer, setHoverPointer] = (0, import_react12.useState)(null);
2271
+ const [hoverElement, setHoverElementState] = (0, import_react12.useState)(
1770
2272
  null
1771
2273
  );
1772
- const [selectedElement, setSelectedElementState] = (0, import_react11.useState)(null);
1773
- const setSelectedElement = (0, import_react11.useCallback)(
2274
+ const [selectedElement, setSelectedElementState] = (0, import_react12.useState)(null);
2275
+ const setSelectedElement = (0, import_react12.useCallback)(
1774
2276
  (element) => {
1775
2277
  selectedElementRef.current = element;
1776
2278
  setSelectedElementState(element);
1777
2279
  },
1778
2280
  [selectedElementRef]
1779
2281
  );
1780
- const setHoverElement = (0, import_react11.useCallback)(
2282
+ const setHoverElement = (0, import_react12.useCallback)(
1781
2283
  (element) => {
1782
2284
  hoverElementRef.current = element;
1783
2285
  setHoverElementState(element);
1784
2286
  },
1785
2287
  [hoverElementRef]
1786
2288
  );
1787
- const clearSelectionRect = (0, import_react11.useCallback)(() => {
2289
+ const clearSelectionRect = (0, import_react12.useCallback)(() => {
1788
2290
  selectionRectRef.current = null;
1789
2291
  setSelectionOriginRect(null);
1790
2292
  }, [selectionRectRef]);
@@ -1802,9 +2304,9 @@ var useMeasurerLocalState = ({
1802
2304
  };
1803
2305
 
1804
2306
  // hooks/use-measurer-pointer.ts
1805
- var import_react12 = require("react");
2307
+ var import_react13 = require("react");
1806
2308
 
1807
- // targets.ts
2309
+ // core/targets.ts
1808
2310
  var pickMultiTargets = (selectionRect, items) => {
1809
2311
  const candidates = items.map(({ element, rect }) => {
1810
2312
  const overlapArea = intersectionArea(selectionRect, rect);
@@ -1869,17 +2371,39 @@ var pickPointTarget = (point, items) => {
1869
2371
  return scored[0].element;
1870
2372
  };
1871
2373
 
1872
- // selection.ts
1873
- var getTargetElement = (point, overlayNode) => {
2374
+ // core/selection.ts
2375
+ var getOverlayHost2 = (overlayNode) => {
2376
+ if (!overlayNode) return null;
2377
+ const rootNode = overlayNode.getRootNode();
2378
+ return rootNode.nodeType === 11 ? rootNode.host : null;
2379
+ };
2380
+ var isOverlayElement = (element, overlayNode, overlayHost) => {
2381
+ if (overlayNode && overlayNode.contains(element)) return true;
2382
+ if (overlayHost && element === overlayHost) return true;
2383
+ return false;
2384
+ };
2385
+ var getDeepestElementAt = (element, point) => {
2386
+ let current = element;
2387
+ const HTMLElementConstructor = element.ownerDocument.defaultView?.HTMLElement ?? HTMLElement;
2388
+ while (current instanceof HTMLElementConstructor && current.shadowRoot) {
2389
+ const nested = current.shadowRoot.elementFromPoint(point.x, point.y);
2390
+ if (!nested || nested === current) break;
2391
+ current = nested;
2392
+ }
2393
+ return current;
2394
+ };
2395
+ var getTargetElement = (point, overlayNode, ownerDocument = document) => {
2396
+ const overlayHost = getOverlayHost2(overlayNode);
1874
2397
  if (overlayNode) {
1875
2398
  const previous = overlayNode.style.pointerEvents;
1876
2399
  overlayNode.style.pointerEvents = "none";
1877
- const elements2 = document.elementsFromPoint(point.x, point.y);
2400
+ const elements2 = ownerDocument.elementsFromPoint(point.x, point.y);
1878
2401
  overlayNode.style.pointerEvents = previous;
1879
- for (const element of elements2) {
1880
- if (!(element instanceof HTMLElement)) continue;
1881
- if (overlayNode.contains(element)) continue;
1882
- if (element === document.body || element === document.documentElement)
2402
+ for (const rawElement of elements2) {
2403
+ const element = getDeepestElementAt(rawElement, point);
2404
+ if (!(element instanceof (ownerDocument.defaultView?.HTMLElement ?? HTMLElement))) continue;
2405
+ if (isOverlayElement(element, overlayNode, overlayHost)) continue;
2406
+ if (element === ownerDocument.body || element === ownerDocument.documentElement)
1883
2407
  continue;
1884
2408
  const rect = element.getBoundingClientRect();
1885
2409
  if (rect.width <= 2 || rect.height <= 2) continue;
@@ -1887,10 +2411,12 @@ var getTargetElement = (point, overlayNode) => {
1887
2411
  }
1888
2412
  return null;
1889
2413
  }
1890
- const elements = document.elementsFromPoint(point.x, point.y);
1891
- for (const element of elements) {
1892
- if (!(element instanceof HTMLElement)) continue;
1893
- if (element === document.body || element === document.documentElement)
2414
+ const elements = ownerDocument.elementsFromPoint(point.x, point.y);
2415
+ for (const rawElement of elements) {
2416
+ const element = getDeepestElementAt(rawElement, point);
2417
+ if (!(element instanceof (ownerDocument.defaultView?.HTMLElement ?? HTMLElement))) continue;
2418
+ if (isOverlayElement(element, overlayNode, overlayHost)) continue;
2419
+ if (element === ownerDocument.body || element === ownerDocument.documentElement)
1894
2420
  continue;
1895
2421
  const rect = element.getBoundingClientRect();
1896
2422
  if (rect.width <= 2 || rect.height <= 2) continue;
@@ -1898,33 +2424,34 @@ var getTargetElement = (point, overlayNode) => {
1898
2424
  }
1899
2425
  return null;
1900
2426
  };
1901
- var getSnappedClickTarget = (point, overlayNode, snapEnabled) => {
1902
- if (!snapEnabled) return getTargetElement(point, overlayNode);
2427
+ var getSnappedClickTarget = (point, overlayNode, snapEnabled, ownerDocument = document) => {
2428
+ if (!snapEnabled) return getTargetElement(point, overlayNode, ownerDocument);
1903
2429
  const probeRect = {
1904
2430
  left: point.x - 20,
1905
2431
  top: point.y - 20,
1906
2432
  width: 40,
1907
2433
  height: 40
1908
2434
  };
1909
- const entries = getSelectionEntries(probeRect, overlayNode);
1910
- return pickPointTarget(point, entries) ?? pickSingleTarget(probeRect, point, entries) ?? getTargetElement(point, overlayNode);
2435
+ const entries = getSelectionEntries(probeRect, overlayNode, ownerDocument);
2436
+ return pickPointTarget(point, entries) ?? pickSingleTarget(probeRect, point, entries) ?? getTargetElement(point, overlayNode, ownerDocument);
1911
2437
  };
1912
- var getSelectionEntries = (rect, overlayNode) => {
2438
+ var getSelectionEntries = (rect, overlayNode, ownerDocument = document) => {
2439
+ const overlayHost = getOverlayHost2(overlayNode);
1913
2440
  const frame = getFrameToken();
1914
2441
  const key = `${Math.round(rect.left)}:${Math.round(rect.top)}:${Math.round(
1915
2442
  rect.width
1916
2443
  )}:${Math.round(rect.height)}`;
1917
- if (frame === cachedSelectionFrame && cachedSelectionKey === key && cachedOverlayNode === overlayNode) {
2444
+ if (frame === cachedSelectionFrame && cachedSelectionKey === key && cachedOverlayNode === overlayNode && cachedSelectionDocument === ownerDocument) {
1918
2445
  return cachedSelectionEntries;
1919
2446
  }
1920
2447
  const minLeft = rect.left - 1;
1921
2448
  const minTop = rect.top - 1;
1922
2449
  const maxRight = rect.left + rect.width + 1;
1923
2450
  const maxBottom = rect.top + rect.height + 1;
1924
- const elements = getBodyElementsCached();
2451
+ const elements = getBodyElementsCached(ownerDocument);
1925
2452
  const entries = elements.map((element) => ({ element, rect: getRectFromDomCached(element) })).filter(({ element, rect: elementRect }) => {
1926
- if (overlayNode && overlayNode.contains(element)) return false;
1927
- if (element === document.body || element === document.documentElement)
2453
+ if (isOverlayElement(element, overlayNode, overlayHost)) return false;
2454
+ if (element === ownerDocument.body || element === ownerDocument.documentElement)
1928
2455
  return false;
1929
2456
  if (elementRect.width < MIN_MULTI_TARGET_SIZE || elementRect.height < MIN_MULTI_TARGET_SIZE) {
1930
2457
  return false;
@@ -1938,6 +2465,7 @@ var getSelectionEntries = (rect, overlayNode) => {
1938
2465
  cachedSelectionFrame = frame;
1939
2466
  cachedSelectionKey = key;
1940
2467
  cachedOverlayNode = overlayNode;
2468
+ cachedSelectionDocument = ownerDocument;
1941
2469
  cachedSelectionEntries = entries;
1942
2470
  return entries;
1943
2471
  };
@@ -1945,30 +2473,33 @@ var cachedSelectionFrame = -1;
1945
2473
  var cachedSelectionKey = "";
1946
2474
  var cachedSelectionEntries = [];
1947
2475
  var cachedOverlayNode = null;
2476
+ var cachedSelectionDocument = null;
1948
2477
  var getSelectionCacheKey = (rect) => `${Math.round(rect.left)}:${Math.round(rect.top)}:${Math.round(
1949
2478
  rect.width
1950
2479
  )}:${Math.round(rect.height)}`;
1951
- var getSelectionEntriesCached = (rect, overlayNode, cache) => {
2480
+ var getSelectionEntriesCached = (rect, overlayNode, cache, ownerDocument = document) => {
1952
2481
  const frame = getFrameToken();
1953
2482
  const key = getSelectionCacheKey(rect);
1954
2483
  if (cache.key === key && cache.overlayNode === overlayNode && cache.frame === frame) {
1955
2484
  return cache.entries;
1956
2485
  }
1957
- const entries = getSelectionEntries(rect, overlayNode);
2486
+ const entries = getSelectionEntries(rect, overlayNode, ownerDocument);
1958
2487
  cache.key = key;
1959
2488
  cache.overlayNode = overlayNode;
1960
2489
  cache.frame = frame;
1961
2490
  cache.entries = entries;
1962
2491
  return entries;
1963
2492
  };
1964
- var getElementsInRectCached = (rect, overlayNode, cache) => {
1965
- const entries = getSelectionEntriesCached(rect, overlayNode, cache);
2493
+ var getElementsInRectCached = (rect, overlayNode, cache, ownerDocument = document) => {
2494
+ const entries = getSelectionEntriesCached(rect, overlayNode, cache, ownerDocument);
1966
2495
  if (entries.length === 0) return [];
1967
2496
  return pickMultiTargets(rect, entries);
1968
2497
  };
1969
2498
 
1970
2499
  // hooks/use-measurer-pointer.ts
1971
2500
  var useMeasurerPointer = ({
2501
+ document: document2,
2502
+ window: window2,
1972
2503
  toolbarRef,
1973
2504
  overlayRef,
1974
2505
  selectionRectRef,
@@ -2012,46 +2543,26 @@ var useMeasurerPointer = ({
2012
2543
  setHoverPointer,
2013
2544
  clearSelectionRect
2014
2545
  }) => {
2015
- const hoverFrameRef = (0, import_react12.useRef)(null);
2016
- const hoverPointRef = (0, import_react12.useRef)(null);
2017
- const selectionCacheRef = (0, import_react12.useRef)({
2546
+ const hoverFrameRef = (0, import_react13.useRef)(null);
2547
+ const hoverPointRef = (0, import_react13.useRef)(null);
2548
+ const selectionCacheRef = (0, import_react13.useRef)({
2018
2549
  key: "",
2019
2550
  entries: [],
2020
2551
  overlayNode: null,
2021
2552
  frame: -1
2022
2553
  });
2023
- const shiftDragRef = (0, import_react12.useRef)(false);
2024
- const shiftToggleElementRef = (0, import_react12.useRef)(null);
2025
- const guidesEnabledRef = (0, import_react12.useRef)(guidesEnabled);
2026
- const guidesRef = (0, import_react12.useRef)(guides);
2027
- const snapGuidesEnabledRef = (0, import_react12.useRef)(snapGuidesEnabled);
2028
- const draggingGuideIdRef = (0, import_react12.useRef)(draggingGuideId);
2029
- const guideOrientationRef = (0, import_react12.useRef)(guideOrientation);
2030
- (0, import_react12.useEffect)(() => {
2554
+ const shiftDragRef = (0, import_react13.useRef)(false);
2555
+ const shiftToggleElementRef = (0, import_react13.useRef)(null);
2556
+ (0, import_react13.useEffect)(() => {
2031
2557
  return () => {
2032
2558
  if (hoverFrameRef.current) {
2033
- cancelAnimationFrame(hoverFrameRef.current);
2559
+ window2.cancelAnimationFrame(hoverFrameRef.current);
2034
2560
  }
2035
2561
  };
2036
2562
  }, []);
2037
- (0, import_react12.useEffect)(() => {
2038
- guidesEnabledRef.current = guidesEnabled;
2039
- }, [guidesEnabled]);
2040
- (0, import_react12.useEffect)(() => {
2041
- guidesRef.current = guides;
2042
- }, [guides]);
2043
- (0, import_react12.useEffect)(() => {
2044
- snapGuidesEnabledRef.current = snapGuidesEnabled;
2045
- }, [snapGuidesEnabled]);
2046
- (0, import_react12.useEffect)(() => {
2047
- guideOrientationRef.current = guideOrientation;
2048
- }, [guideOrientation]);
2049
- (0, import_react12.useEffect)(() => {
2050
- draggingGuideIdRef.current = draggingGuideId;
2051
- }, [draggingGuideId]);
2052
- const updateHoverTarget = (0, import_react12.useCallback)(
2563
+ const updateHoverTarget = (0, import_react13.useCallback)(
2053
2564
  (point) => {
2054
- const target = getTargetElement(point, overlayRef.current);
2565
+ const target = getTargetElement(point, overlayRef.current, document2);
2055
2566
  if (target) {
2056
2567
  const rect = target.getBoundingClientRect();
2057
2568
  setHoverRect({
@@ -2066,16 +2577,16 @@ var useMeasurerPointer = ({
2066
2577
  setHoverElement(null);
2067
2578
  }
2068
2579
  },
2069
- [overlayRef, setHoverElement, setHoverRect]
2580
+ [document2, overlayRef, setHoverElement, setHoverRect]
2070
2581
  );
2071
- const updateHoverElement = (0, import_react12.useCallback)(
2582
+ const updateHoverElement = (0, import_react13.useCallback)(
2072
2583
  (point) => {
2073
- const target = getTargetElement(point, overlayRef.current);
2584
+ const target = getTargetElement(point, overlayRef.current, document2);
2074
2585
  setHoverElement(target);
2075
2586
  },
2076
- [overlayRef, setHoverElement]
2587
+ [document2, overlayRef, setHoverElement]
2077
2588
  );
2078
- const handlePointerDown = (0, import_react12.useCallback)(
2589
+ const handlePointerDown = (0, import_react13.useCallback)(
2079
2590
  (event) => {
2080
2591
  const commit = createActionCommit();
2081
2592
  const toolbarNode = toolbarRef.current;
@@ -2088,7 +2599,8 @@ var useMeasurerPointer = ({
2088
2599
  shiftToggleElementRef.current = event.shiftKey ? getSelectedMeasurementHit({
2089
2600
  point,
2090
2601
  selectedMeasurements,
2091
- overlayNode: overlayRef.current
2602
+ overlayNode: overlayRef.current,
2603
+ document: document2
2092
2604
  })?.elementRef ?? null : null;
2093
2605
  selectionCacheRef.current.key = "";
2094
2606
  if (altPressed && optionPairOverlay) {
@@ -2110,10 +2622,11 @@ var useMeasurerPointer = ({
2110
2622
  snapGuidesEnabled,
2111
2623
  overlayNode: overlayRef.current,
2112
2624
  guides,
2113
- draggingGuideId
2625
+ draggingGuideId,
2626
+ document: document2
2114
2627
  });
2115
2628
  const id = createId();
2116
- setSelectedGuideIds([]);
2629
+ setSelectedGuideIds([id]);
2117
2630
  setGuides((prev) => [
2118
2631
  ...prev,
2119
2632
  { id, orientation: guideOrientation, position }
@@ -2136,6 +2649,7 @@ var useMeasurerPointer = ({
2136
2649
  clearSelectionRect,
2137
2650
  createActionCommit,
2138
2651
  draggingGuideId,
2652
+ document2,
2139
2653
  enabled,
2140
2654
  guideOrientation,
2141
2655
  guides,
@@ -2157,7 +2671,7 @@ var useMeasurerPointer = ({
2157
2671
  toolbarRef
2158
2672
  ]
2159
2673
  );
2160
- const handlePointerMove = (0, import_react12.useCallback)(
2674
+ const handlePointerMove = (0, import_react13.useCallback)(
2161
2675
  (event) => {
2162
2676
  const toolbarNode = toolbarRef.current;
2163
2677
  if (toolbarNode && toolbarNode.contains(event.target)) return;
@@ -2177,31 +2691,32 @@ var useMeasurerPointer = ({
2177
2691
  }
2178
2692
  hoverPointRef.current = point;
2179
2693
  if (!hoverFrameRef.current) {
2180
- hoverFrameRef.current = requestAnimationFrame(() => {
2694
+ hoverFrameRef.current = window2.requestAnimationFrame(() => {
2181
2695
  const latest = hoverPointRef.current;
2182
- if (latest && !draggingGuideIdRef.current) {
2696
+ if (latest && !draggingGuideId) {
2183
2697
  if (hoverHighlightEnabled) {
2184
2698
  updateHoverTarget(latest);
2185
2699
  } else {
2186
2700
  updateHoverElement(latest);
2187
2701
  }
2188
2702
  }
2189
- if (latest && guidesRef.current.length > 0) {
2703
+ if (latest && guides.length > 0) {
2190
2704
  setHoverPointer(latest);
2191
2705
  } else {
2192
2706
  setHoverPointer(null);
2193
2707
  }
2194
- if (guidesEnabledRef.current && latest && !draggingGuideIdRef.current) {
2708
+ if (guidesEnabled && latest && !draggingGuideId) {
2195
2709
  const position = getSnapGuidePosition({
2196
- orientation: guideOrientationRef.current,
2710
+ orientation: guideOrientation,
2197
2711
  point: latest,
2198
- snapGuidesEnabled: snapGuidesEnabledRef.current,
2712
+ snapGuidesEnabled,
2199
2713
  overlayNode: overlayRef.current,
2200
- guides: guidesRef.current,
2201
- draggingGuideId: draggingGuideIdRef.current
2714
+ guides,
2715
+ draggingGuideId,
2716
+ document: document2
2202
2717
  });
2203
2718
  setGuidePreview({
2204
- orientation: guideOrientationRef.current,
2719
+ orientation: guideOrientation,
2205
2720
  position
2206
2721
  });
2207
2722
  } else {
@@ -2218,10 +2733,11 @@ var useMeasurerPointer = ({
2218
2733
  position: getSnapGuidePosition({
2219
2734
  orientation: guide.orientation,
2220
2735
  point,
2221
- snapGuidesEnabled: snapGuidesEnabledRef.current,
2736
+ snapGuidesEnabled,
2222
2737
  overlayNode: overlayRef.current,
2223
- guides: guidesRef.current,
2224
- draggingGuideId: draggingGuideIdRef.current
2738
+ guides,
2739
+ draggingGuideId,
2740
+ document: document2
2225
2741
  })
2226
2742
  } : guide
2227
2743
  )
@@ -2243,8 +2759,11 @@ var useMeasurerPointer = ({
2243
2759
  draggingGuideId,
2244
2760
  enabled,
2245
2761
  hoverHighlightEnabled,
2762
+ guides,
2763
+ guidesEnabled,
2246
2764
  isDragging,
2247
2765
  overlayRef,
2766
+ guideOrientation,
2248
2767
  setAltPressed,
2249
2768
  setEnd,
2250
2769
  setGuidePreview,
@@ -2253,6 +2772,7 @@ var useMeasurerPointer = ({
2253
2772
  setHoverPointer,
2254
2773
  setHoverRect,
2255
2774
  setIsDragging,
2775
+ snapGuidesEnabled,
2256
2776
  start,
2257
2777
  toolMode,
2258
2778
  toolbarRef,
@@ -2260,7 +2780,7 @@ var useMeasurerPointer = ({
2260
2780
  updateHoverTarget
2261
2781
  ]
2262
2782
  );
2263
- const handlePointerUp = (0, import_react12.useCallback)(
2783
+ const handlePointerUp = (0, import_react13.useCallback)(
2264
2784
  (event) => {
2265
2785
  const commit = createActionCommit();
2266
2786
  const toolbarNode = toolbarRef.current;
@@ -2306,7 +2826,8 @@ var useMeasurerPointer = ({
2306
2826
  const elements = getElementsInRectCached(
2307
2827
  selectionRect,
2308
2828
  overlayRef.current,
2309
- selectionCacheRef.current
2829
+ selectionCacheRef.current,
2830
+ document2
2310
2831
  );
2311
2832
  const hasSameSelection = elements.length === selectedMeasurements.length && elements.every(
2312
2833
  (element, index) => selectedMeasurements[index]?.elementRef === element
@@ -2317,7 +2838,7 @@ var useMeasurerPointer = ({
2317
2838
  if (!hasSameSelection) {
2318
2839
  commit();
2319
2840
  const nextMeasurements = elements.map((element) => ({
2320
- ...getInspectMeasurement(element),
2841
+ ...getInspectMeasurement(element, window2),
2321
2842
  originRect: selectionRect
2322
2843
  }));
2323
2844
  setSelectedMeasurements(nextMeasurements);
@@ -2351,7 +2872,8 @@ var useMeasurerPointer = ({
2351
2872
  ) ?? null : getSelectedMeasurementHit({
2352
2873
  point,
2353
2874
  selectedMeasurements,
2354
- overlayNode: overlayRef.current
2875
+ overlayNode: overlayRef.current,
2876
+ document: document2
2355
2877
  });
2356
2878
  if (event.shiftKey && selectedHit) {
2357
2879
  commit();
@@ -2381,9 +2903,9 @@ var useMeasurerPointer = ({
2381
2903
  resetDragState();
2382
2904
  return;
2383
2905
  }
2384
- const target = event.shiftKey ? getTargetElement(point, overlayRef.current) ?? getSnappedClickTarget(point, overlayRef.current, snapEnabled) : getSnappedClickTarget(point, overlayRef.current, snapEnabled);
2906
+ const target = event.shiftKey ? getTargetElement(point, overlayRef.current, document2) ?? getSnappedClickTarget(point, overlayRef.current, snapEnabled, document2) : getSnappedClickTarget(point, overlayRef.current, snapEnabled, document2);
2385
2907
  if (target) {
2386
- const inspectMeasurement = getInspectMeasurement(target);
2908
+ const inspectMeasurement = getInspectMeasurement(target, window2);
2387
2909
  clearTransientMeasurements();
2388
2910
  if (event.shiftKey) {
2389
2911
  const alreadySelected = selectedMeasurements.some(
@@ -2458,7 +2980,7 @@ var useMeasurerPointer = ({
2458
2980
  toolbarRef
2459
2981
  ]
2460
2982
  );
2461
- const handlePointerLeave = (0, import_react12.useCallback)(() => {
2983
+ const handlePointerLeave = (0, import_react13.useCallback)(() => {
2462
2984
  clearGuideDragHold();
2463
2985
  setStart(null);
2464
2986
  setEnd(null);
@@ -2482,41 +3004,48 @@ var useMeasurerPointer = ({
2482
3004
  };
2483
3005
 
2484
3006
  // hooks/use-overlay-refs.ts
2485
- var import_react13 = require("react");
3007
+ var import_react14 = require("react");
2486
3008
  var useOverlayRefs = () => {
2487
- const overlayRef = (0, import_react13.useRef)(null);
2488
- const selectedElementRef = (0, import_react13.useRef)(null);
2489
- const hoverElementRef = (0, import_react13.useRef)(null);
3009
+ const overlayRef = (0, import_react14.useRef)(null);
3010
+ const selectedElementRef = (0, import_react14.useRef)(null);
3011
+ const hoverElementRef = (0, import_react14.useRef)(null);
2490
3012
  return { overlayRef, selectedElementRef, hoverElementRef };
2491
3013
  };
2492
3014
 
2493
3015
  // hooks/use-resize-sync.ts
2494
- var import_react14 = require("react");
3016
+ var import_react15 = require("react");
2495
3017
  var useResizeSync = (params) => {
2496
- const resizeFrameRef = (0, import_react14.useRef)(null);
2497
- const viewportRef = (0, import_react14.useRef)(getViewportSize());
2498
- (0, import_react14.useEffect)(() => {
3018
+ const resizeFrameRef = (0, import_react15.useRef)(null);
3019
+ const viewportRef = (0, import_react15.useRef)(getViewportSize(params.window));
3020
+ (0, import_react15.useEffect)(() => {
2499
3021
  const handleResize = () => {
2500
3022
  if (resizeFrameRef.current) {
2501
- cancelAnimationFrame(resizeFrameRef.current);
3023
+ params.window.cancelAnimationFrame(resizeFrameRef.current);
2502
3024
  }
2503
- resizeFrameRef.current = requestAnimationFrame(() => {
2504
- const viewport = getViewportSize();
3025
+ resizeFrameRef.current = params.window.requestAnimationFrame(() => {
3026
+ const viewport = getViewportSize(params.window);
2505
3027
  const previousViewport = viewportRef.current;
2506
3028
  params.setMeasurements(
2507
3029
  (prev) => prev.map(
2508
- (measurement) => updateMeasurementForResize(measurement, viewport)
3030
+ (measurement) => updateMeasurementForResize(measurement, viewport, params.document)
2509
3031
  )
2510
3032
  );
2511
3033
  params.setActiveMeasurement(
2512
- (prev) => prev ? updateMeasurementForResize(prev, viewport) : prev
3034
+ (prev) => prev ? updateMeasurementForResize(prev, viewport, params.document) : prev
2513
3035
  );
2514
3036
  params.setHeldDistances(
2515
- (prev) => prev.map((distance) => updateDistanceForResize(distance, viewport))
3037
+ (prev) => prev.map(
3038
+ (distance) => updateDistanceForResize(
3039
+ distance,
3040
+ viewport,
3041
+ params.document,
3042
+ params.window
3043
+ )
3044
+ )
2516
3045
  );
2517
- if (params.selectedElementRef.current) {
3046
+ if (params.selectedElementRef.current && params.document.contains(params.selectedElementRef.current)) {
2518
3047
  params.setSelectedMeasurement(
2519
- getInspectMeasurement(params.selectedElementRef.current)
3048
+ getInspectMeasurement(params.selectedElementRef.current, params.window)
2520
3049
  );
2521
3050
  }
2522
3051
  if (previousViewport.width > 0 && previousViewport.height > 0) {
@@ -2531,27 +3060,27 @@ var useResizeSync = (params) => {
2531
3060
  viewportRef.current = viewport;
2532
3061
  });
2533
3062
  };
2534
- window.addEventListener("resize", handleResize);
3063
+ params.window.addEventListener("resize", handleResize);
2535
3064
  return () => {
2536
3065
  if (resizeFrameRef.current) {
2537
- cancelAnimationFrame(resizeFrameRef.current);
3066
+ params.window.cancelAnimationFrame(resizeFrameRef.current);
2538
3067
  }
2539
- window.removeEventListener("resize", handleResize);
3068
+ params.window.removeEventListener("resize", handleResize);
2540
3069
  };
2541
3070
  }, [params]);
2542
3071
  };
2543
3072
 
2544
3073
  // render/measurer-overlay.tsx
2545
- var import_react18 = require("react");
3074
+ var import_react19 = require("react");
2546
3075
 
2547
3076
  // components/measure-tag.tsx
2548
- var import_jsx_runtime3 = require("react/jsx-runtime");
3077
+ var import_jsx_runtime4 = require("react/jsx-runtime");
2549
3078
  function MeasureTag({
2550
3079
  children,
2551
3080
  className = "",
2552
3081
  style
2553
3082
  }) {
2554
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
3083
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2555
3084
  "div",
2556
3085
  {
2557
3086
  className: cn(
@@ -2565,31 +3094,21 @@ function MeasureTag({
2565
3094
  }
2566
3095
 
2567
3096
  // components/measurement-box.tsx
2568
- var import_react15 = require("react");
2569
- var import_jsx_runtime4 = require("react/jsx-runtime");
3097
+ var import_react16 = require("react");
3098
+ var import_jsx_runtime5 = require("react/jsx-runtime");
2570
3099
  var formatValue2 = (value) => Math.round(value);
2571
- var MeasurementBox = (0, import_react15.memo)(function MeasurementBox2({
3100
+ var MeasurementBox = (0, import_react16.memo)(function MeasurementBox2({
2572
3101
  measurement,
2573
3102
  transitionMs,
2574
3103
  labelOffset,
2575
3104
  edgeVisibility
2576
3105
  }) {
2577
- const [rect, setRect] = (0, import_react15.useState)(
2578
- measurement.originRect ?? measurement.rect
2579
- );
2580
- (0, import_react15.useEffect)(() => {
2581
- if (!measurement.originRect) return;
2582
- const frame = requestAnimationFrame(() => {
2583
- setRect(measurement.rect);
2584
- });
2585
- return () => cancelAnimationFrame(frame);
2586
- }, [measurement]);
2587
3106
  const edges = edgeVisibility ?? { top: true, right: true, bottom: true, left: true };
2588
- const displayRect = measurement.originRect ? rect : measurement.rect;
3107
+ const displayRect = measurement.rect;
2589
3108
  const outlineColor = "color-mix(in oklch, oklch(0.62 0.18 255) 80%, transparent)";
2590
3109
  const fillColor = "color-mix(in oklch, oklch(0.62 0.18 255) 8%, transparent)";
2591
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "msr:pointer-events-none", children: [
2592
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
3110
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "msr:pointer-events-none", children: [
3111
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
2593
3112
  "div",
2594
3113
  {
2595
3114
  className: "msr:absolute",
@@ -2599,31 +3118,31 @@ var MeasurementBox = (0, import_react15.memo)(function MeasurementBox2({
2599
3118
  width: displayRect.width,
2600
3119
  height: displayRect.height,
2601
3120
  backgroundColor: fillColor,
2602
- transition: measurement.originRect ? `left ${transitionMs}ms ease, top ${transitionMs}ms ease, width ${transitionMs}ms ease, height ${transitionMs}ms ease` : void 0
3121
+ transition: `left ${transitionMs}ms ease, top ${transitionMs}ms ease, width ${transitionMs}ms ease, height ${transitionMs}ms ease`
2603
3122
  },
2604
3123
  children: [
2605
- edges.top ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
3124
+ edges.top ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2606
3125
  "div",
2607
3126
  {
2608
3127
  className: "msr:absolute msr:left-0 msr:top-0 msr:h-px msr:w-full",
2609
3128
  style: { backgroundColor: outlineColor }
2610
3129
  }
2611
3130
  ) : null,
2612
- edges.right ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
3131
+ edges.right ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2613
3132
  "div",
2614
3133
  {
2615
3134
  className: "msr:absolute msr:right-0 msr:top-0 msr:h-full msr:w-px",
2616
3135
  style: { backgroundColor: outlineColor }
2617
3136
  }
2618
3137
  ) : null,
2619
- edges.bottom ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
3138
+ edges.bottom ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2620
3139
  "div",
2621
3140
  {
2622
3141
  className: "msr:absolute msr:bottom-0 msr:left-0 msr:h-px msr:w-full",
2623
3142
  style: { backgroundColor: outlineColor }
2624
3143
  }
2625
3144
  ) : null,
2626
- edges.left ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
3145
+ edges.left ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2627
3146
  "div",
2628
3147
  {
2629
3148
  className: "msr:absolute msr:left-0 msr:top-0 msr:h-full msr:w-px",
@@ -2633,14 +3152,14 @@ var MeasurementBox = (0, import_react15.memo)(function MeasurementBox2({
2633
3152
  ]
2634
3153
  }
2635
3154
  ),
2636
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
3155
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
2637
3156
  MeasureTag,
2638
3157
  {
2639
3158
  className: "msr:-translate-x-1/2 msr:bg-ink-900/90",
2640
3159
  style: {
2641
3160
  left: displayRect.left + displayRect.width / 2,
2642
3161
  top: displayRect.top + displayRect.height + labelOffset,
2643
- transition: measurement.originRect ? `left ${transitionMs}ms ease, top ${transitionMs}ms ease` : void 0
3162
+ transition: `left ${transitionMs}ms ease, top ${transitionMs}ms ease`
2644
3163
  },
2645
3164
  children: [
2646
3165
  formatValue2(displayRect.width),
@@ -2653,31 +3172,21 @@ var MeasurementBox = (0, import_react15.memo)(function MeasurementBox2({
2653
3172
  });
2654
3173
 
2655
3174
  // components/selected-measurement-box.tsx
2656
- var import_react16 = require("react");
2657
- var import_jsx_runtime5 = require("react/jsx-runtime");
3175
+ var import_react17 = require("react");
3176
+ var import_jsx_runtime6 = require("react/jsx-runtime");
2658
3177
  var formatValue3 = (value) => Math.round(value);
2659
- var SelectedMeasurementBox = (0, import_react16.memo)(function SelectedMeasurementBox2({
3178
+ var SelectedMeasurementBox = (0, import_react17.memo)(function SelectedMeasurementBox2({
2660
3179
  measurement,
2661
3180
  transitionMs,
2662
3181
  labelOffset,
2663
3182
  edgeVisibility
2664
3183
  }) {
2665
- const [rect, setRect] = (0, import_react16.useState)(
2666
- measurement.originRect ?? measurement.rect
2667
- );
2668
- (0, import_react16.useEffect)(() => {
2669
- if (!measurement.originRect) return;
2670
- const frame = requestAnimationFrame(() => {
2671
- setRect(measurement.rect);
2672
- });
2673
- return () => cancelAnimationFrame(frame);
2674
- }, [measurement]);
2675
3184
  const edges = edgeVisibility ?? { top: true, right: true, bottom: true, left: true };
2676
- const displayRect = measurement.originRect ? rect : measurement.rect;
3185
+ const displayRect = measurement.rect;
2677
3186
  const outlineColor = "color-mix(in oklch, oklch(0.62 0.18 255) 80%, transparent)";
2678
3187
  const fillColor = "color-mix(in oklch, oklch(0.62 0.18 255) 8%, transparent)";
2679
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "msr:pointer-events-none", children: [
2680
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
3188
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "msr:pointer-events-none", children: [
3189
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
2681
3190
  "div",
2682
3191
  {
2683
3192
  className: "msr:absolute",
@@ -2687,31 +3196,31 @@ var SelectedMeasurementBox = (0, import_react16.memo)(function SelectedMeasureme
2687
3196
  width: displayRect.width,
2688
3197
  height: displayRect.height,
2689
3198
  backgroundColor: fillColor,
2690
- transition: measurement.originRect ? `left ${transitionMs}ms ease, top ${transitionMs}ms ease, width ${transitionMs}ms ease, height ${transitionMs}ms ease` : void 0
3199
+ transition: `left ${transitionMs}ms ease, top ${transitionMs}ms ease, width ${transitionMs}ms ease, height ${transitionMs}ms ease`
2691
3200
  },
2692
3201
  children: [
2693
- edges.top ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3202
+ edges.top ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2694
3203
  "div",
2695
3204
  {
2696
3205
  className: "msr:absolute msr:left-0 msr:top-0 msr:h-px msr:w-full",
2697
3206
  style: { backgroundColor: outlineColor }
2698
3207
  }
2699
3208
  ) : null,
2700
- edges.right ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3209
+ edges.right ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2701
3210
  "div",
2702
3211
  {
2703
3212
  className: "msr:absolute msr:right-0 msr:top-0 msr:h-full msr:w-px",
2704
3213
  style: { backgroundColor: outlineColor }
2705
3214
  }
2706
3215
  ) : null,
2707
- edges.bottom ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3216
+ edges.bottom ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2708
3217
  "div",
2709
3218
  {
2710
3219
  className: "msr:absolute msr:bottom-0 msr:left-0 msr:h-px msr:w-full",
2711
3220
  style: { backgroundColor: outlineColor }
2712
3221
  }
2713
3222
  ) : null,
2714
- edges.left ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3223
+ edges.left ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2715
3224
  "div",
2716
3225
  {
2717
3226
  className: "msr:absolute msr:left-0 msr:top-0 msr:h-full msr:w-px",
@@ -2721,14 +3230,14 @@ var SelectedMeasurementBox = (0, import_react16.memo)(function SelectedMeasureme
2721
3230
  ]
2722
3231
  }
2723
3232
  ),
2724
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
3233
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
2725
3234
  MeasureTag,
2726
3235
  {
2727
3236
  className: "msr:-translate-x-1/2 msr:bg-ink-900/90",
2728
3237
  style: {
2729
3238
  left: displayRect.left + displayRect.width / 2,
2730
3239
  top: displayRect.top + displayRect.height + labelOffset,
2731
- transition: measurement.originRect ? `left ${transitionMs}ms ease, top ${transitionMs}ms ease` : void 0
3240
+ transition: `left ${transitionMs}ms ease, top ${transitionMs}ms ease`
2732
3241
  },
2733
3242
  children: [
2734
3243
  formatValue3(displayRect.width),
@@ -2741,14 +3250,14 @@ var SelectedMeasurementBox = (0, import_react16.memo)(function SelectedMeasureme
2741
3250
  });
2742
3251
 
2743
3252
  // render/distance-overlay-item.tsx
2744
- var import_react17 = require("react");
2745
- var import_jsx_runtime6 = require("react/jsx-runtime");
2746
- var DistanceOverlayItem = (0, import_react17.memo)(function DistanceOverlayItem2({
3253
+ var import_react18 = require("react");
3254
+ var import_jsx_runtime7 = require("react/jsx-runtime");
3255
+ var DistanceOverlayItem = (0, import_react18.memo)(function DistanceOverlayItem2({
2747
3256
  distance,
2748
3257
  labelOffset,
2749
3258
  onRemove
2750
3259
  }) {
2751
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
3260
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
2752
3261
  "div",
2753
3262
  {
2754
3263
  className: onRemove ? "msr:pointer-events-auto" : "msr:pointer-events-none",
@@ -2757,7 +3266,7 @@ var DistanceOverlayItem = (0, import_react17.memo)(function DistanceOverlayItem2
2757
3266
  onRemove(distance.id);
2758
3267
  } : void 0,
2759
3268
  children: [
2760
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
3269
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2761
3270
  "div",
2762
3271
  {
2763
3272
  className: "msr:absolute msr:rounded msr:border msr:border-[#2563eb]/70",
@@ -2769,7 +3278,7 @@ var DistanceOverlayItem = (0, import_react17.memo)(function DistanceOverlayItem2
2769
3278
  }
2770
3279
  }
2771
3280
  ),
2772
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
3281
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2773
3282
  "div",
2774
3283
  {
2775
3284
  className: "msr:absolute msr:rounded msr:border msr:border-[#2563eb]/70",
@@ -2782,7 +3291,7 @@ var DistanceOverlayItem = (0, import_react17.memo)(function DistanceOverlayItem2
2782
3291
  }
2783
3292
  ),
2784
3293
  distance.connectors.map(
2785
- (connector, index) => Math.abs(connector.x1 - connector.x2) < 1 ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
3294
+ (connector, index) => Math.abs(connector.x1 - connector.x2) < 1 ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2786
3295
  "div",
2787
3296
  {
2788
3297
  className: "msr:absolute msr:border-l msr:border-dashed msr:border-[#2563eb]/70",
@@ -2793,7 +3302,7 @@ var DistanceOverlayItem = (0, import_react17.memo)(function DistanceOverlayItem2
2793
3302
  }
2794
3303
  },
2795
3304
  `${distance.id}-connector-${index}`
2796
- ) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
3305
+ ) : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2797
3306
  "div",
2798
3307
  {
2799
3308
  className: "msr:absolute msr:border-t msr:border-dashed msr:border-[#2563eb]/70",
@@ -2806,8 +3315,8 @@ var DistanceOverlayItem = (0, import_react17.memo)(function DistanceOverlayItem2
2806
3315
  `${distance.id}-connector-${index}`
2807
3316
  )
2808
3317
  ),
2809
- distance.horizontal && distance.horizontal.value > 0 ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
2810
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
3318
+ distance.horizontal && distance.horizontal.value > 0 ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
3319
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2811
3320
  "div",
2812
3321
  {
2813
3322
  className: "msr:absolute msr:h-px msr:bg-[#2563eb]",
@@ -2818,7 +3327,7 @@ var DistanceOverlayItem = (0, import_react17.memo)(function DistanceOverlayItem2
2818
3327
  }
2819
3328
  }
2820
3329
  ),
2821
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
3330
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2822
3331
  MeasureTag,
2823
3332
  {
2824
3333
  className: "msr:-translate-x-1/2 msr:bg-ink-900/90",
@@ -2830,8 +3339,8 @@ var DistanceOverlayItem = (0, import_react17.memo)(function DistanceOverlayItem2
2830
3339
  }
2831
3340
  )
2832
3341
  ] }) : null,
2833
- distance.vertical && distance.vertical.value > 0 ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
2834
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
3342
+ distance.vertical && distance.vertical.value > 0 ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
3343
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2835
3344
  "div",
2836
3345
  {
2837
3346
  className: "msr:absolute msr:w-px msr:bg-[#2563eb]",
@@ -2842,7 +3351,7 @@ var DistanceOverlayItem = (0, import_react17.memo)(function DistanceOverlayItem2
2842
3351
  }
2843
3352
  }
2844
3353
  ),
2845
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
3354
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2846
3355
  MeasureTag,
2847
3356
  {
2848
3357
  className: "msr:-translate-y-1/2 msr:bg-ink-900/90",
@@ -2860,10 +3369,11 @@ var DistanceOverlayItem = (0, import_react17.memo)(function DistanceOverlayItem2
2860
3369
  });
2861
3370
 
2862
3371
  // render/measurer-overlay.tsx
2863
- var import_jsx_runtime7 = require("react/jsx-runtime");
2864
- var MeasurerOverlay = (0, import_react18.memo)(function MeasurerOverlay2({
3372
+ var import_jsx_runtime8 = require("react/jsx-runtime");
3373
+ var MeasurerOverlay = (0, import_react19.memo)(function MeasurerOverlay2({
2865
3374
  enabled,
2866
3375
  toolMode,
3376
+ guidePointerEvents,
2867
3377
  guidesEnabled,
2868
3378
  altPressed,
2869
3379
  isDragging,
@@ -2900,16 +3410,20 @@ var MeasurerOverlay = (0, import_react18.memo)(function MeasurerOverlay2({
2900
3410
  onGuidePointerUp,
2901
3411
  onGuidePointerCancel
2902
3412
  }) {
2903
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
3413
+ const overlayVisible = enabled;
3414
+ const overlayInteractive = overlayVisible && toolMode !== "none" && toolMode !== "text-inspector" && toolMode !== "xray" && toolMode !== "rulers";
3415
+ const selectionVisible = toolMode === "select";
3416
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
2904
3417
  "div",
2905
3418
  {
2906
- className: `msr:absolute msr:inset-0 ${enabled && toolMode !== "none" ? `msr:pointer-events-auto ${guidesEnabled ? hoverGuide || draggingGuideId ? "msr:cursor-default" : "msr:cursor-crosshair" : "msr:cursor-default"} msr:opacity-100` : "msr:pointer-events-none msr:opacity-0"}`,
3419
+ className: `msr:absolute msr:inset-0 ${overlayVisible ? `msr:pointer-events-auto ${guidesEnabled ? hoverGuide || draggingGuideId ? "msr:cursor-default" : "msr:cursor-crosshair" : "msr:cursor-default"} msr:opacity-100` : "msr:pointer-events-none msr:opacity-0"}`,
3420
+ style: { pointerEvents: overlayInteractive ? "auto" : "none" },
2907
3421
  onPointerDown,
2908
3422
  onPointerMove,
2909
3423
  onPointerUp,
2910
3424
  onPointerLeave,
2911
3425
  children: [
2912
- !guidesEnabled ? displayedMeasurements.map((measurement, index) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3426
+ selectionVisible ? displayedMeasurements.map((measurement, index) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2913
3427
  MeasurementBox,
2914
3428
  {
2915
3429
  measurement,
@@ -2919,8 +3433,8 @@ var MeasurerOverlay = (0, import_react18.memo)(function MeasurerOverlay2({
2919
3433
  },
2920
3434
  measurement.id
2921
3435
  )) : null,
2922
- !guidesEnabled && activeRect && isDragging ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
2923
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
3436
+ selectionVisible && activeRect && isDragging ? /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
3437
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
2924
3438
  "div",
2925
3439
  {
2926
3440
  className: "msr:pointer-events-none msr:absolute",
@@ -2932,28 +3446,28 @@ var MeasurerOverlay = (0, import_react18.memo)(function MeasurerOverlay2({
2932
3446
  backgroundColor: fillColor
2933
3447
  },
2934
3448
  children: [
2935
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3449
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2936
3450
  "div",
2937
3451
  {
2938
3452
  className: "msr:absolute msr:left-0 msr:top-0 msr:h-px msr:w-full",
2939
3453
  style: { backgroundColor: outlineColor }
2940
3454
  }
2941
3455
  ),
2942
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3456
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2943
3457
  "div",
2944
3458
  {
2945
3459
  className: "msr:absolute msr:right-0 msr:top-0 msr:h-full msr:w-px",
2946
3460
  style: { backgroundColor: outlineColor }
2947
3461
  }
2948
3462
  ),
2949
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3463
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2950
3464
  "div",
2951
3465
  {
2952
3466
  className: "msr:absolute msr:bottom-0 msr:left-0 msr:h-px msr:w-full",
2953
3467
  style: { backgroundColor: outlineColor }
2954
3468
  }
2955
3469
  ),
2956
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3470
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2957
3471
  "div",
2958
3472
  {
2959
3473
  className: "msr:absolute msr:left-0 msr:top-0 msr:h-full msr:w-px",
@@ -2963,7 +3477,7 @@ var MeasurerOverlay = (0, import_react18.memo)(function MeasurerOverlay2({
2963
3477
  ]
2964
3478
  }
2965
3479
  ),
2966
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
3480
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
2967
3481
  MeasureTag,
2968
3482
  {
2969
3483
  className: "msr:-translate-x-1/2 msr:bg-ink-900/90",
@@ -2979,7 +3493,7 @@ var MeasurerOverlay = (0, import_react18.memo)(function MeasurerOverlay2({
2979
3493
  }
2980
3494
  )
2981
3495
  ] }) : null,
2982
- !guidesEnabled && hoverRectToShow ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
3496
+ selectionVisible && hoverRectToShow ? /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
2983
3497
  "div",
2984
3498
  {
2985
3499
  className: "msr:pointer-events-none msr:absolute",
@@ -2991,28 +3505,28 @@ var MeasurerOverlay = (0, import_react18.memo)(function MeasurerOverlay2({
2991
3505
  backgroundColor: fillColor
2992
3506
  },
2993
3507
  children: [
2994
- hoverEdgeVisibility?.top ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3508
+ hoverEdgeVisibility?.top ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2995
3509
  "div",
2996
3510
  {
2997
3511
  className: "msr:absolute msr:left-0 msr:top-0 msr:h-px msr:w-full",
2998
3512
  style: { backgroundColor: outlineColor }
2999
3513
  }
3000
3514
  ) : null,
3001
- hoverEdgeVisibility?.right ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3515
+ hoverEdgeVisibility?.right ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
3002
3516
  "div",
3003
3517
  {
3004
3518
  className: "msr:absolute msr:right-0 msr:top-0 msr:h-full msr:w-px",
3005
3519
  style: { backgroundColor: outlineColor }
3006
3520
  }
3007
3521
  ) : null,
3008
- hoverEdgeVisibility?.bottom ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3522
+ hoverEdgeVisibility?.bottom ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
3009
3523
  "div",
3010
3524
  {
3011
3525
  className: "msr:absolute msr:bottom-0 msr:left-0 msr:h-px msr:w-full",
3012
3526
  style: { backgroundColor: outlineColor }
3013
3527
  }
3014
3528
  ) : null,
3015
- hoverEdgeVisibility?.left ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3529
+ hoverEdgeVisibility?.left ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
3016
3530
  "div",
3017
3531
  {
3018
3532
  className: "msr:absolute msr:left-0 msr:top-0 msr:h-full msr:w-px",
@@ -3022,7 +3536,7 @@ var MeasurerOverlay = (0, import_react18.memo)(function MeasurerOverlay2({
3022
3536
  ]
3023
3537
  }
3024
3538
  ) : null,
3025
- guidesEnabled && guidePreview ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3539
+ guidesEnabled && guidePreview ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
3026
3540
  "div",
3027
3541
  {
3028
3542
  className: "msr:pointer-events-none msr:absolute",
@@ -3037,7 +3551,7 @@ var MeasurerOverlay = (0, import_react18.memo)(function MeasurerOverlay2({
3037
3551
  height: GUIDE_HITBOX_SIZE,
3038
3552
  width: "100%"
3039
3553
  },
3040
- children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3554
+ children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
3041
3555
  "div",
3042
3556
  {
3043
3557
  className: "msr:absolute",
@@ -3058,7 +3572,7 @@ var MeasurerOverlay = (0, import_react18.memo)(function MeasurerOverlay2({
3058
3572
  )
3059
3573
  }
3060
3574
  ) : null,
3061
- !guidesEnabled ? displayedSelectedMeasurements.map((measurement, index) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3575
+ selectionVisible ? displayedSelectedMeasurements.map((measurement, index) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
3062
3576
  SelectedMeasurementBox,
3063
3577
  {
3064
3578
  measurement,
@@ -3068,7 +3582,7 @@ var MeasurerOverlay = (0, import_react18.memo)(function MeasurerOverlay2({
3068
3582
  },
3069
3583
  measurement.id
3070
3584
  )) : null,
3071
- heldDistances.map((distance) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3585
+ heldDistances.map((distance) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
3072
3586
  DistanceOverlayItem,
3073
3587
  {
3074
3588
  distance,
@@ -3077,7 +3591,7 @@ var MeasurerOverlay = (0, import_react18.memo)(function MeasurerOverlay2({
3077
3591
  },
3078
3592
  `held-${distance.id}`
3079
3593
  )),
3080
- !guidesEnabled && altPressed && optionPairOverlay ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3594
+ selectionVisible && altPressed && optionPairOverlay ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
3081
3595
  DistanceOverlayItem,
3082
3596
  {
3083
3597
  distance: optionPairOverlay,
@@ -3085,7 +3599,7 @@ var MeasurerOverlay = (0, import_react18.memo)(function MeasurerOverlay2({
3085
3599
  },
3086
3600
  `option-${optionPairOverlay.id}`
3087
3601
  ) : null,
3088
- guidesEnabled && altPressed && guideDistanceOverlay ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3602
+ guidesEnabled && altPressed && guideDistanceOverlay ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
3089
3603
  DistanceOverlayItem,
3090
3604
  {
3091
3605
  distance: guideDistanceOverlay,
@@ -3093,9 +3607,9 @@ var MeasurerOverlay = (0, import_react18.memo)(function MeasurerOverlay2({
3093
3607
  },
3094
3608
  `guide-preview-${guideDistanceOverlay.id}`
3095
3609
  ) : null,
3096
- !guidesEnabled && altPressed && optionContainerLines ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
3097
- optionContainerLines.top.value > 0 ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
3098
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3610
+ selectionVisible && altPressed && optionContainerLines ? /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
3611
+ optionContainerLines.top.value > 0 ? /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
3612
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
3099
3613
  "div",
3100
3614
  {
3101
3615
  className: "msr:absolute msr:w-px msr:bg-[#2563eb]",
@@ -3106,7 +3620,7 @@ var MeasurerOverlay = (0, import_react18.memo)(function MeasurerOverlay2({
3106
3620
  }
3107
3621
  }
3108
3622
  ),
3109
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3623
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
3110
3624
  MeasureTag,
3111
3625
  {
3112
3626
  className: "msr:-translate-y-1/2 msr:bg-ink-900/90",
@@ -3118,8 +3632,8 @@ var MeasurerOverlay = (0, import_react18.memo)(function MeasurerOverlay2({
3118
3632
  }
3119
3633
  )
3120
3634
  ] }) : null,
3121
- optionContainerLines.bottom.value > 0 ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
3122
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3635
+ optionContainerLines.bottom.value > 0 ? /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
3636
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
3123
3637
  "div",
3124
3638
  {
3125
3639
  className: "msr:absolute msr:w-px msr:bg-[#2563eb]",
@@ -3130,7 +3644,7 @@ var MeasurerOverlay = (0, import_react18.memo)(function MeasurerOverlay2({
3130
3644
  }
3131
3645
  }
3132
3646
  ),
3133
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3647
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
3134
3648
  MeasureTag,
3135
3649
  {
3136
3650
  className: "msr:-translate-y-1/2 msr:bg-ink-900/90",
@@ -3142,8 +3656,8 @@ var MeasurerOverlay = (0, import_react18.memo)(function MeasurerOverlay2({
3142
3656
  }
3143
3657
  )
3144
3658
  ] }) : null,
3145
- optionContainerLines.left.value > 0 ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
3146
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3659
+ optionContainerLines.left.value > 0 ? /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
3660
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
3147
3661
  "div",
3148
3662
  {
3149
3663
  className: "msr:absolute msr:h-px msr:bg-[#2563eb]",
@@ -3154,7 +3668,7 @@ var MeasurerOverlay = (0, import_react18.memo)(function MeasurerOverlay2({
3154
3668
  }
3155
3669
  }
3156
3670
  ),
3157
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3671
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
3158
3672
  MeasureTag,
3159
3673
  {
3160
3674
  className: "msr:-translate-x-1/2 msr:bg-ink-900/90",
@@ -3166,8 +3680,8 @@ var MeasurerOverlay = (0, import_react18.memo)(function MeasurerOverlay2({
3166
3680
  }
3167
3681
  )
3168
3682
  ] }) : null,
3169
- optionContainerLines.right.value > 0 ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
3170
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3683
+ optionContainerLines.right.value > 0 ? /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
3684
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
3171
3685
  "div",
3172
3686
  {
3173
3687
  className: "msr:absolute msr:h-px msr:bg-[#2563eb]",
@@ -3178,7 +3692,7 @@ var MeasurerOverlay = (0, import_react18.memo)(function MeasurerOverlay2({
3178
3692
  }
3179
3693
  }
3180
3694
  ),
3181
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3695
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
3182
3696
  MeasureTag,
3183
3697
  {
3184
3698
  className: "msr:-translate-x-1/2 msr:bg-ink-900/90",
@@ -3196,25 +3710,28 @@ var MeasurerOverlay = (0, import_react18.memo)(function MeasurerOverlay2({
3196
3710
  const isHovered = hoverGuide?.id === guide.id;
3197
3711
  const strokeWidth = isSelected || isHovered ? 2 : 1;
3198
3712
  const strokeColor = isSelected ? guideColorActive : isHovered ? guideColorHover : guideColorDefault;
3199
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3713
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
3200
3714
  "div",
3201
3715
  {
3202
3716
  className: "msr:absolute",
3717
+ "data-mesurer-guide": "true",
3203
3718
  style: guide.orientation === "vertical" ? {
3204
3719
  left: guide.position - GUIDE_HITBOX_SIZE / 2,
3205
3720
  top: 0,
3206
3721
  width: GUIDE_HITBOX_SIZE,
3207
- height: "100%"
3722
+ height: "100%",
3723
+ pointerEvents: guidePointerEvents ? "auto" : "none"
3208
3724
  } : {
3209
3725
  top: guide.position - GUIDE_HITBOX_SIZE / 2,
3210
3726
  left: 0,
3211
3727
  height: GUIDE_HITBOX_SIZE,
3212
- width: "100%"
3728
+ width: "100%",
3729
+ pointerEvents: guidePointerEvents ? "auto" : "none"
3213
3730
  },
3214
3731
  onPointerDown: (event) => onGuidePointerDown(guide, event),
3215
3732
  onPointerUp: (event) => onGuidePointerUp(guide, event),
3216
3733
  onPointerCancel: (event) => onGuidePointerCancel(guide, event),
3217
- children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3734
+ children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
3218
3735
  "div",
3219
3736
  {
3220
3737
  className: "msr:absolute",
@@ -3242,46 +3759,991 @@ var MeasurerOverlay = (0, import_react18.memo)(function MeasurerOverlay2({
3242
3759
  );
3243
3760
  });
3244
3761
 
3245
- // measurer.tsx
3246
- var import_jsx_runtime8 = require("react/jsx-runtime");
3247
- var subscribeHydration = () => () => {
3762
+ // runtime/text-inspector-dom.ts
3763
+ var INK_50 = "#f8fafc";
3764
+ var INK_200 = "#e2e8f0";
3765
+ var INK_500 = "#64748b";
3766
+ var INK_900 = "#0f172a";
3767
+ var makeEdge = (document2, side, color) => {
3768
+ const edge = document2.createElement("div");
3769
+ Object.assign(edge.style, {
3770
+ position: "absolute",
3771
+ backgroundColor: color,
3772
+ ...side === "top" || side === "bottom" ? { left: "0", [side]: "0", width: "100%", height: "1px" } : { top: "0", [side]: "0", width: "1px", height: "100%" }
3773
+ });
3774
+ return edge;
3248
3775
  };
3249
- var useHydrated = () => (0, import_react19.useSyncExternalStore)(
3250
- subscribeHydration,
3251
- () => true,
3252
- () => false
3253
- );
3254
- var stripMeasurement = (measurement) => ({
3255
- ...measurement,
3256
- elementRef: void 0
3257
- });
3258
- var stripDistance = (distance) => ({
3259
- ...distance,
3260
- elementRefA: void 0,
3261
- elementRefB: void 0
3262
- });
3263
- function MeasurerClient({
3776
+ var makeBox = (document2, fill, outline) => {
3777
+ const box = document2.createElement("div");
3778
+ Object.assign(box.style, {
3779
+ position: "fixed",
3780
+ zIndex: "0",
3781
+ pointerEvents: "none",
3782
+ backgroundColor: fill,
3783
+ boxSizing: "border-box"
3784
+ });
3785
+ box.className = "mesurer-ti-box";
3786
+ box.dataset.state = "hidden";
3787
+ for (const side of ["top", "right", "bottom", "left"]) {
3788
+ box.appendChild(makeEdge(document2, side, outline));
3789
+ }
3790
+ return box;
3791
+ };
3792
+ var positionBox = (box, rect) => {
3793
+ Object.assign(box.style, {
3794
+ left: `${rect.left}px`,
3795
+ top: `${rect.top}px`,
3796
+ width: `${rect.width}px`,
3797
+ height: `${rect.height}px`
3798
+ });
3799
+ };
3800
+ var makeCard = (document2, pinned) => {
3801
+ const card = document2.createElement("div");
3802
+ Object.assign(card.style, {
3803
+ position: "fixed",
3804
+ zIndex: "1",
3805
+ pointerEvents: pinned ? "auto" : "none",
3806
+ background: "#ffffff",
3807
+ color: INK_900,
3808
+ borderRadius: "13px",
3809
+ padding: "10px 12px",
3810
+ fontSize: "11px",
3811
+ lineHeight: "1.5",
3812
+ fontFamily: "ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, sans-serif",
3813
+ fontVariantNumeric: "tabular-nums",
3814
+ userSelect: "none",
3815
+ whiteSpace: "nowrap",
3816
+ width: "320px",
3817
+ minWidth: "320px",
3818
+ maxWidth: "320px",
3819
+ boxSizing: "border-box",
3820
+ boxShadow: "0px 0px 0.5px rgba(0, 0, 0, 0.18), 0px 3px 8px rgba(0, 0, 0, 0.1), 0px 1px 3px rgba(0, 0, 0, 0.1)"
3821
+ });
3822
+ card.className = pinned ? "mesurer-ti-card mesurer-ti-card--pinned" : "mesurer-ti-card";
3823
+ card.dataset.state = "hidden";
3824
+ return card;
3825
+ };
3826
+ var populateCard = (document2, card, info, pinned) => {
3827
+ card.replaceChildren();
3828
+ const header = document2.createElement("div");
3829
+ Object.assign(header.style, {
3830
+ display: "flex",
3831
+ alignItems: "center",
3832
+ gap: "8px",
3833
+ marginBottom: "8px",
3834
+ paddingBottom: "6px"
3835
+ });
3836
+ const tag = document2.createElement("span");
3837
+ Object.assign(tag.style, {
3838
+ color: INK_50,
3839
+ background: INK_900,
3840
+ borderRadius: "4px",
3841
+ padding: "1px 5px",
3842
+ fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace",
3843
+ fontSize: "10px",
3844
+ fontWeight: "500"
3845
+ });
3846
+ tag.textContent = info.tagName;
3847
+ header.appendChild(tag);
3848
+ if (info.textSnippet) {
3849
+ const snippet = document2.createElement("span");
3850
+ Object.assign(snippet.style, {
3851
+ color: INK_500,
3852
+ overflow: "hidden",
3853
+ textOverflow: "ellipsis",
3854
+ whiteSpace: "nowrap",
3855
+ flex: "1",
3856
+ minWidth: "0",
3857
+ fontSize: "10px"
3858
+ });
3859
+ snippet.textContent = info.textSnippet;
3860
+ header.appendChild(snippet);
3861
+ }
3862
+ if (pinned) {
3863
+ const close = document2.createElement("button");
3864
+ close.type = "button";
3865
+ close.className = "mesurer-ti-close";
3866
+ close.setAttribute("aria-label", "Close");
3867
+ close.textContent = "\xD7";
3868
+ Object.assign(close.style, {
3869
+ all: "unset",
3870
+ marginLeft: info.textSnippet ? "0" : "auto",
3871
+ flex: "0 0 auto",
3872
+ width: "16px",
3873
+ height: "16px",
3874
+ lineHeight: "14px",
3875
+ textAlign: "center",
3876
+ borderRadius: "4px",
3877
+ color: INK_500,
3878
+ fontSize: "14px"
3879
+ });
3880
+ header.appendChild(close);
3881
+ }
3882
+ card.appendChild(header);
3883
+ const grid = document2.createElement("div");
3884
+ Object.assign(grid.style, {
3885
+ display: "grid",
3886
+ gridTemplateColumns: "auto 1fr",
3887
+ columnGap: "12px",
3888
+ rowGap: "3px",
3889
+ alignItems: "baseline"
3890
+ });
3891
+ for (const row of info.rows) {
3892
+ const label = document2.createElement("span");
3893
+ label.style.color = INK_500;
3894
+ label.style.fontSize = "11px";
3895
+ label.textContent = row.label;
3896
+ const value = document2.createElement("span");
3897
+ Object.assign(value.style, {
3898
+ fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace",
3899
+ fontSize: "11px",
3900
+ color: INK_900,
3901
+ overflow: "hidden",
3902
+ textOverflow: "ellipsis",
3903
+ whiteSpace: "nowrap"
3904
+ });
3905
+ if (!row.varName) {
3906
+ value.textContent = row.value;
3907
+ } else {
3908
+ const variable = document2.createElement("span");
3909
+ variable.textContent = row.value;
3910
+ const separator = document2.createElement("span");
3911
+ separator.style.color = INK_200;
3912
+ separator.style.margin = "0 6px";
3913
+ separator.textContent = "\xB7";
3914
+ const name = document2.createElement("span");
3915
+ name.style.color = "#0369a1";
3916
+ name.textContent = row.varName;
3917
+ value.append(variable, separator, name);
3918
+ }
3919
+ grid.append(label, value);
3920
+ }
3921
+ card.appendChild(grid);
3922
+ };
3923
+ var positionCard = (window2, card, rect, offsetX = 0, offsetY = 0) => {
3924
+ const center = rect.left + rect.width / 2 + offsetX;
3925
+ card.style.left = `${center}px`;
3926
+ const size = card.getBoundingClientRect();
3927
+ let top = rect.bottom + 4 + offsetY;
3928
+ if (top + size.height > window2.innerHeight - 8) {
3929
+ top = rect.top - size.height - 4 + offsetY;
3930
+ }
3931
+ card.style.top = `${Math.max(8, top)}px`;
3932
+ const half = size.width / 2;
3933
+ card.style.left = `${Math.min(
3934
+ Math.max(center, 8 + half),
3935
+ window2.innerWidth - 8 - half
3936
+ )}px`;
3937
+ };
3938
+
3939
+ // runtime/text-inspector-typography.ts
3940
+ var FONT_WEIGHT_KEYWORD = {
3941
+ "100": "thin",
3942
+ "200": "extralight",
3943
+ "300": "light",
3944
+ "400": "normal",
3945
+ "500": "medium",
3946
+ "600": "semibold",
3947
+ "700": "bold",
3948
+ "800": "extrabold",
3949
+ "900": "black"
3950
+ };
3951
+ var TYPO_PROPS = [
3952
+ "font-family",
3953
+ "font-size",
3954
+ "font-weight",
3955
+ "line-height",
3956
+ "letter-spacing"
3957
+ ];
3958
+ var TYPO_LABELS = {
3959
+ Family: "font-family",
3960
+ Size: "font-size",
3961
+ Weight: "font-weight",
3962
+ Line: "line-height",
3963
+ Tracking: "letter-spacing"
3964
+ };
3965
+ var formatPx = (raw) => {
3966
+ if (!raw || raw === "normal") return raw || "normal";
3967
+ const match = /^(-?[\d.]+)px$/.exec(raw);
3968
+ if (!match) return raw;
3969
+ return `${Math.round(Number(match[1]) * 10) / 10}px`;
3970
+ };
3971
+ var firstFontFamily = (families) => (families.split(",")[0] ?? "").trim().replace(/^['"]|['"]$/g, "");
3972
+ var weightWithKeyword = (weight) => {
3973
+ const keyword = FONT_WEIGHT_KEYWORD[weight];
3974
+ return keyword ? `${weight} / ${keyword}` : weight;
3975
+ };
3976
+ var extractVarName = (value) => {
3977
+ if (!value) return null;
3978
+ return /var\(\s*(--[a-zA-Z0-9_-]+)/.exec(value)?.[1] ?? null;
3979
+ };
3980
+ var selectorSpecificity = (selector) => {
3981
+ const ids = (selector.match(/#[\w-]+/g) ?? []).length;
3982
+ const classes = (selector.match(/\.[\w-]+|\[[^\]]+\]|:[\w-]+/g) ?? []).length;
3983
+ const elements = (selector.match(/(?:^|[ >+~])([a-zA-Z][\w-]*)/g) ?? []).length;
3984
+ return ids * 1e4 + classes * 100 + elements;
3985
+ };
3986
+ var wins = (next, previous) => {
3987
+ if (!previous) return true;
3988
+ if (next.important !== previous.important) return next.important;
3989
+ if (next.specificity !== previous.specificity) {
3990
+ return next.specificity > previous.specificity;
3991
+ }
3992
+ return next.order > previous.order;
3993
+ };
3994
+ var TypographyInspector = class {
3995
+ constructor(document2, window2) {
3996
+ this.document = document2;
3997
+ this.window = window2;
3998
+ }
3999
+ collectRules(rules, output, mediaOk, order) {
4000
+ for (let i = 0; i < rules.length; i++) {
4001
+ const rule = rules[i];
4002
+ const currentOrder = order.value++;
4003
+ if (rule.type === 4) {
4004
+ const mediaRule = rule;
4005
+ let matches = mediaOk;
4006
+ try {
4007
+ matches = mediaOk && this.window.matchMedia(mediaRule.media.mediaText).matches;
4008
+ } catch {
4009
+ matches = false;
4010
+ }
4011
+ this.collectRules(mediaRule.cssRules, output, matches, order);
4012
+ } else if (rule.type === 12) {
4013
+ this.collectRules(rule.cssRules, output, mediaOk, order);
4014
+ } else if (rule.type === 1) {
4015
+ output.push({
4016
+ rule,
4017
+ mediaOk,
4018
+ order: currentOrder
4019
+ });
4020
+ }
4021
+ }
4022
+ }
4023
+ getRules() {
4024
+ const rules = [];
4025
+ const order = { value: 0 };
4026
+ for (const sheet of Array.from(this.document.styleSheets)) {
4027
+ try {
4028
+ this.collectRules(sheet.cssRules, rules, true, order);
4029
+ } catch {
4030
+ continue;
4031
+ }
4032
+ }
4033
+ return rules;
4034
+ }
4035
+ findVarReferences(el) {
4036
+ const result = Object.fromEntries(
4037
+ TYPO_PROPS.map((prop) => [prop, null])
4038
+ );
4039
+ const rules = this.getRules();
4040
+ for (const prop of TYPO_PROPS) {
4041
+ let node = el;
4042
+ while (node && result[prop] === null) {
4043
+ let winner;
4044
+ const inlineValue = node.style.getPropertyValue(prop);
4045
+ const inlineName = extractVarName(inlineValue);
4046
+ if (inlineName) {
4047
+ winner = {
4048
+ name: inlineName,
4049
+ specificity: Number.MAX_SAFE_INTEGER,
4050
+ order: Number.MAX_SAFE_INTEGER,
4051
+ important: node.style.getPropertyPriority(prop) === "important"
4052
+ };
4053
+ }
4054
+ for (const { rule, mediaOk, order } of rules) {
4055
+ if (!mediaOk) continue;
4056
+ let matches = false;
4057
+ try {
4058
+ matches = node.matches(rule.selectorText);
4059
+ } catch {
4060
+ continue;
4061
+ }
4062
+ if (!matches) continue;
4063
+ const name = extractVarName(rule.style.getPropertyValue(prop));
4064
+ if (!name) continue;
4065
+ const candidate = {
4066
+ name,
4067
+ specificity: selectorSpecificity(rule.selectorText),
4068
+ order,
4069
+ important: rule.style.getPropertyPriority(prop) === "important"
4070
+ };
4071
+ if (wins(candidate, winner)) winner = candidate;
4072
+ }
4073
+ if (winner) result[prop] = winner.name;
4074
+ node = node.parentElement;
4075
+ }
4076
+ }
4077
+ return result;
4078
+ }
4079
+ getFast(el) {
4080
+ const styles = this.window.getComputedStyle(el);
4081
+ const rows = [
4082
+ { label: "Family", value: firstFontFamily(styles.fontFamily), varName: null },
4083
+ { label: "Size", value: formatPx(styles.fontSize), varName: null },
4084
+ {
4085
+ label: "Weight",
4086
+ value: weightWithKeyword(styles.fontWeight),
4087
+ varName: null
4088
+ },
4089
+ {
4090
+ label: "Line",
4091
+ value: styles.lineHeight === "normal" ? "normal" : formatPx(styles.lineHeight),
4092
+ varName: null
4093
+ },
4094
+ {
4095
+ label: "Tracking",
4096
+ value: styles.letterSpacing === "normal" ? "normal" : formatPx(styles.letterSpacing),
4097
+ varName: null
4098
+ }
4099
+ ];
4100
+ const directText = Array.from(el.childNodes).filter((node) => node.nodeType === Node.TEXT_NODE).map((node) => node.nodeValue?.trim() ?? "").filter(Boolean).join(" ").replace(/\s+/g, " ");
4101
+ return {
4102
+ rows,
4103
+ tagName: el.tagName.toLowerCase(),
4104
+ textSnippet: directText.length > 40 ? `${directText.slice(0, 40)}\u2026` : directText
4105
+ };
4106
+ }
4107
+ getFull(el, base = this.getFast(el)) {
4108
+ const vars = this.findVarReferences(el);
4109
+ return {
4110
+ ...base,
4111
+ rows: base.rows.map((row) => ({
4112
+ ...row,
4113
+ varName: vars[TYPO_LABELS[row.label]]
4114
+ }))
4115
+ };
4116
+ }
4117
+ };
4118
+
4119
+ // runtime/text-inspector.ts
4120
+ var DEFAULT_EXTENSION_HOST_ID = "mesurer-extension-host";
4121
+ var DEFAULT_OVERLAY_ID = "mesurer-text-inspector-overlay";
4122
+ var DEFAULT_STYLE_ID = "mesurer-text-inspector-styles";
4123
+ var DEFAULT_BODY_MODE_CLASS = "mesurer-text-inspect-mode";
4124
+ var inspectorInstanceCount = 0;
4125
+ var FILL_HOVER = "color-mix(in oklch, oklch(0.62 0.18 255) 8%, transparent)";
4126
+ var OUTLINE_HOVER = "color-mix(in oklch, oklch(0.62 0.18 255) 80%, transparent)";
4127
+ var FILL_PINNED = "color-mix(in oklch, oklch(0.62 0.18 255) 4%, transparent)";
4128
+ var OUTLINE_PINNED = "color-mix(in oklch, oklch(0.62 0.18 255) 35%, transparent)";
4129
+ var DEFAULT_SKIP_TAGS = [
4130
+ "HTML",
4131
+ "BODY",
4132
+ "SCRIPT",
4133
+ "STYLE",
4134
+ "META",
4135
+ "LINK",
4136
+ "NOSCRIPT",
4137
+ "IMG",
4138
+ "VIDEO",
4139
+ "AUDIO",
4140
+ "IFRAME"
4141
+ ];
4142
+ var createInspectorStyles = (extensionHostId, overlayId, bodyModeClass) => `
4143
+ .${bodyModeClass},
4144
+ .${bodyModeClass} * {
4145
+ cursor: help !important;
4146
+ }
4147
+ .${bodyModeClass} #${extensionHostId},
4148
+ .${bodyModeClass} #${extensionHostId} * {
4149
+ cursor: auto !important;
4150
+ }
4151
+ #${overlayId} .mesurer-ti-card--pinned {
4152
+ cursor: grab;
4153
+ }
4154
+ #${overlayId} .mesurer-ti-card--pinned:active {
4155
+ cursor: grabbing;
4156
+ }
4157
+ #${overlayId} .mesurer-ti-close {
4158
+ cursor: pointer;
4159
+ }
4160
+ #${overlayId} .mesurer-ti-close:hover {
4161
+ background: rgba(15, 23, 42, 0.06);
4162
+ color: #0f172a;
4163
+ }
4164
+ #${overlayId} .mesurer-ti-box,
4165
+ #${overlayId} .mesurer-ti-card {
4166
+ opacity: 1;
4167
+ }
4168
+ #${overlayId} .mesurer-ti-box[data-state="hidden"],
4169
+ #${overlayId} .mesurer-ti-card[data-state="hidden"] {
4170
+ opacity: 0;
4171
+ }
4172
+ #${overlayId} .mesurer-ti-card {
4173
+ transform: translateX(-50%);
4174
+ }
4175
+ `;
4176
+ var createIdleScheduler = (window2) => {
4177
+ const browser = window2;
4178
+ if (browser.requestIdleCallback) {
4179
+ return {
4180
+ request: (callback) => browser.requestIdleCallback(
4181
+ () => callback(),
4182
+ { timeout: 250 }
4183
+ ),
4184
+ cancel: (id) => browser.cancelIdleCallback?.(id)
4185
+ };
4186
+ }
4187
+ return {
4188
+ request: (callback) => window2.setTimeout(callback, 32),
4189
+ cancel: (id) => window2.clearTimeout(id)
4190
+ };
4191
+ };
4192
+ var createTextInspector = (options = {}, legacy = false) => {
4193
+ const portalDocument = options.portalTarget?.ownerDocument;
4194
+ const availableDocument = portalDocument ?? (typeof globalThis.document === "undefined" ? null : globalThis.document);
4195
+ const availableWindow = availableDocument?.defaultView ?? (typeof globalThis.window === "undefined" ? null : globalThis.window);
4196
+ const document2 = availableDocument;
4197
+ const window2 = availableWindow;
4198
+ const realm = availableWindow;
4199
+ const HTMLElementConstructor = realm?.HTMLElement ?? globalThis.HTMLElement;
4200
+ const SVGElementConstructor = realm?.SVGElement ?? globalThis.SVGElement;
4201
+ const extensionHostId = DEFAULT_EXTENSION_HOST_ID;
4202
+ const instanceId = (options.id ?? (legacy ? DEFAULT_OVERLAY_ID : `mesurer-text-inspector-${++inspectorInstanceCount}`)).replace(
4203
+ /[^a-zA-Z0-9_-]/g,
4204
+ "-"
4205
+ );
4206
+ const overlayId = legacy ? DEFAULT_OVERLAY_ID : `${instanceId}-overlay`;
4207
+ const styleId = legacy ? DEFAULT_STYLE_ID : `${instanceId}-styles`;
4208
+ const bodyModeClass = legacy ? DEFAULT_BODY_MODE_CLASS : `${instanceId}-mode`;
4209
+ const skipTags = new Set(
4210
+ (options.ignoredTags ?? DEFAULT_SKIP_TAGS).map((tag) => tag.toUpperCase())
4211
+ );
4212
+ const maxPinned = Number.isFinite(options.maxPinned) ? Math.max(1, Math.floor(options.maxPinned)) : Infinity;
4213
+ const inspectorStyles = createInspectorStyles(
4214
+ extensionHostId,
4215
+ overlayId,
4216
+ bodyModeClass
4217
+ );
4218
+ const EXTENSION_HOST_ID = extensionHostId;
4219
+ const OVERLAY_ID = overlayId;
4220
+ const STYLE_ID = styleId;
4221
+ const BODY_MODE_CLASS = bodyModeClass;
4222
+ const SKIP_TAGS = skipTags;
4223
+ const INSPECTOR_STYLES = inspectorStyles;
4224
+ const portalIsShadowRoot = options.portalTarget?.nodeType === 11;
4225
+ const typography = new TypographyInspector(document2, window2);
4226
+ const pinned = [];
4227
+ const history = [];
4228
+ const future = [];
4229
+ let enabled = false;
4230
+ let overlay = null;
4231
+ let hoverBox = null;
4232
+ let hoverCard = null;
4233
+ let hoveredEl = null;
4234
+ let pointerX = 0;
4235
+ let pointerY = 0;
4236
+ let frameScheduled = false;
4237
+ let pendingElement = null;
4238
+ let pendingIdleId = -1;
4239
+ let idleScheduler = null;
4240
+ const getIdleScheduler = () => {
4241
+ idleScheduler ?? (idleScheduler = createIdleScheduler(window2));
4242
+ return idleScheduler;
4243
+ };
4244
+ const hasDirectText = (el) => Array.from(el.childNodes).some(
4245
+ (node) => node.nodeType === Node.TEXT_NODE && !!node.nodeValue?.trim()
4246
+ );
4247
+ const isInspectable = (el) => !!el && el instanceof HTMLElementConstructor && !SKIP_TAGS.has(el.tagName) && !(el instanceof SVGElementConstructor) && hasDirectText(el);
4248
+ const isOverlayNode = (node) => {
4249
+ for (let current = node; current; current = current.parentNode) {
4250
+ if (current instanceof HTMLElementConstructor) {
4251
+ const element = current;
4252
+ if (element.id === OVERLAY_ID || element.id === EXTENSION_HOST_ID) {
4253
+ return true;
4254
+ }
4255
+ }
4256
+ }
4257
+ return false;
4258
+ };
4259
+ const pickElementAt = (x, y) => {
4260
+ return document2.elementsFromPoint(x, y).find(
4261
+ (el) => !isOverlayNode(el) && isInspectable(el)
4262
+ ) ?? null;
4263
+ };
4264
+ const pointerOverToolbar = (x, y) => {
4265
+ const shadow = document2.getElementById(EXTENSION_HOST_ID)?.shadowRoot;
4266
+ const element = shadow?.elementFromPoint(x, y);
4267
+ if (!element) return false;
4268
+ for (let node = element; node; node = node.parentElement) {
4269
+ if (node.classList.contains("mesurer-toolbar-surface")) return true;
4270
+ }
4271
+ return false;
4272
+ };
4273
+ const ensureStyles = () => {
4274
+ const append = (root) => {
4275
+ if (root.querySelector(`#${STYLE_ID}`)) return;
4276
+ const style = document2.createElement("style");
4277
+ style.id = STYLE_ID;
4278
+ style.textContent = INSPECTOR_STYLES;
4279
+ root.nodeType === 9 ? root.head.appendChild(style) : root.appendChild(style);
4280
+ };
4281
+ append(document2);
4282
+ if (portalIsShadowRoot) append(options.portalTarget);
4283
+ };
4284
+ const ensureOverlay = () => {
4285
+ if (overlay?.isConnected) return overlay;
4286
+ overlay = document2.createElement("div");
4287
+ overlay.id = OVERLAY_ID;
4288
+ Object.assign(overlay.style, {
4289
+ position: "fixed",
4290
+ inset: "0",
4291
+ pointerEvents: "none",
4292
+ zIndex: "2147483646"
4293
+ });
4294
+ (options.portalTarget ?? document2.body).appendChild(overlay);
4295
+ return overlay;
4296
+ };
4297
+ const setState = (element, visible) => {
4298
+ element.dataset.state = visible ? "visible" : "hidden";
4299
+ };
4300
+ const ensureHover = () => {
4301
+ const root = ensureOverlay();
4302
+ hoverBox ?? (hoverBox = makeBox(document2, FILL_HOVER, OUTLINE_HOVER));
4303
+ hoverCard ?? (hoverCard = makeCard(document2, false));
4304
+ if (!hoverBox.parentNode) root.appendChild(hoverBox);
4305
+ if (!hoverCard.parentNode) root.appendChild(hoverCard);
4306
+ };
4307
+ const scheduleEnrichment = (element, fast) => {
4308
+ if (pendingElement === element) return;
4309
+ if (pendingIdleId !== -1) getIdleScheduler().cancel(pendingIdleId);
4310
+ pendingElement = element;
4311
+ pendingIdleId = getIdleScheduler().request(() => {
4312
+ pendingIdleId = -1;
4313
+ pendingElement = null;
4314
+ if (!enabled) return;
4315
+ const full = typography.getFull(element, fast);
4316
+ if (hoveredEl !== element || !hoverCard) return;
4317
+ populateCard(document2, hoverCard, full, false);
4318
+ positionCard(window2, hoverCard, element.getBoundingClientRect());
4319
+ options.onInspect?.(element, full);
4320
+ });
4321
+ };
4322
+ const inspect = (element) => {
4323
+ if (!enabled || !isInspectable(element)) return false;
4324
+ ensureHover();
4325
+ const rect = element.getBoundingClientRect();
4326
+ hoveredEl = element;
4327
+ const fast = typography.getFast(element);
4328
+ populateCard(document2, hoverCard, fast, false);
4329
+ scheduleEnrichment(element, fast);
4330
+ positionBox(hoverBox, rect);
4331
+ positionCard(window2, hoverCard, rect);
4332
+ setState(hoverBox, true);
4333
+ setState(hoverCard, true);
4334
+ return true;
4335
+ };
4336
+ const updateHover = () => {
4337
+ if (!enabled || pointerOverToolbar(pointerX, pointerY)) {
4338
+ hideHover();
4339
+ return;
4340
+ }
4341
+ const element = pickElementAt(pointerX, pointerY);
4342
+ if (!element) {
4343
+ hideHover();
4344
+ return;
4345
+ }
4346
+ if (element !== hoveredEl) inspect(element);
4347
+ else {
4348
+ positionBox(hoverBox, element.getBoundingClientRect());
4349
+ positionCard(window2, hoverCard, element.getBoundingClientRect());
4350
+ }
4351
+ };
4352
+ const hideHover = () => {
4353
+ hoveredEl = null;
4354
+ if (hoverBox) setState(hoverBox, false);
4355
+ if (hoverCard) setState(hoverCard, false);
4356
+ };
4357
+ const snapshotPins = () => pinned.map((pin) => {
4358
+ const rect = pin.card.getBoundingClientRect();
4359
+ return {
4360
+ sourceEl: pin.sourceEl,
4361
+ left: pin.userPlaced ? rect.left + rect.width / 2 : 0,
4362
+ top: pin.userPlaced ? rect.top : 0,
4363
+ userPlaced: pin.userPlaced
4364
+ };
4365
+ });
4366
+ const recordPinState = () => {
4367
+ history.push(snapshotPins());
4368
+ future.length = 0;
4369
+ };
4370
+ const removePin = (pin, record = true, notify = record) => {
4371
+ const index = pinned.indexOf(pin);
4372
+ if (index === -1) return;
4373
+ if (record) recordPinState();
4374
+ pinned.splice(index, 1);
4375
+ pin.detach();
4376
+ pin.box.remove();
4377
+ pin.card.remove();
4378
+ if (notify) options.onUnpin?.(pin.sourceEl);
4379
+ };
4380
+ const clearPins = (notify = false) => {
4381
+ while (pinned.length) {
4382
+ removePin(pinned[pinned.length - 1], false, notify);
4383
+ }
4384
+ };
4385
+ const attachDrag = (pin) => {
4386
+ let pointerId = -1;
4387
+ let startX = 0;
4388
+ let startY = 0;
4389
+ let originLeft = 0;
4390
+ let originTop = 0;
4391
+ let active = false;
4392
+ let dragged = false;
4393
+ let historyRecorded = false;
4394
+ const slop = 6;
4395
+ const onMove = (event) => {
4396
+ if (event.pointerId !== pointerId) return;
4397
+ const dx = event.clientX - startX;
4398
+ const dy = event.clientY - startY;
4399
+ if (!active) active = Math.abs(dx) > slop || Math.abs(dy) > slop;
4400
+ if (!active) return;
4401
+ if (!historyRecorded) {
4402
+ recordPinState();
4403
+ historyRecorded = true;
4404
+ }
4405
+ dragged = true;
4406
+ pin.card.style.left = `${Math.min(
4407
+ window2.innerWidth - 8,
4408
+ Math.max(8, originLeft + dx)
4409
+ )}px`;
4410
+ pin.card.style.top = `${Math.min(
4411
+ window2.innerHeight - 8,
4412
+ Math.max(8, originTop + dy)
4413
+ )}px`;
4414
+ pin.userPlaced = true;
4415
+ };
4416
+ const onEnd = (event) => {
4417
+ if (event.pointerId !== pointerId && pointerId !== -1) return;
4418
+ window2.removeEventListener("pointermove", onMove);
4419
+ window2.removeEventListener("pointerup", onEnd);
4420
+ window2.removeEventListener("pointercancel", onEnd);
4421
+ pointerId = -1;
4422
+ active = false;
4423
+ historyRecorded = false;
4424
+ if (dragged) {
4425
+ const swallow = (click) => {
4426
+ click.preventDefault();
4427
+ click.stopPropagation();
4428
+ window2.removeEventListener("click", swallow, true);
4429
+ };
4430
+ window2.addEventListener("click", swallow, true);
4431
+ }
4432
+ dragged = false;
4433
+ };
4434
+ const onDown = (event) => {
4435
+ if (event.button !== 0) return;
4436
+ if (event.target?.classList.contains("mesurer-ti-close")) {
4437
+ return;
4438
+ }
4439
+ const rect = pin.card.getBoundingClientRect();
4440
+ originLeft = rect.left + rect.width / 2;
4441
+ originTop = rect.top;
4442
+ startX = event.clientX;
4443
+ startY = event.clientY;
4444
+ pointerId = event.pointerId;
4445
+ active = false;
4446
+ dragged = false;
4447
+ window2.addEventListener("pointermove", onMove);
4448
+ window2.addEventListener("pointerup", onEnd);
4449
+ window2.addEventListener("pointercancel", onEnd);
4450
+ };
4451
+ pin.card.addEventListener("pointerdown", onDown);
4452
+ return () => {
4453
+ pin.card.removeEventListener("pointerdown", onDown);
4454
+ window2.removeEventListener("pointermove", onMove);
4455
+ window2.removeEventListener("pointerup", onEnd);
4456
+ window2.removeEventListener("pointercancel", onEnd);
4457
+ };
4458
+ };
4459
+ const createPin = (sourceEl, state, record = true, notify = record) => {
4460
+ const existing = pinned.find((pin2) => pin2.sourceEl === sourceEl);
4461
+ if (existing) {
4462
+ const root2 = ensureOverlay();
4463
+ root2.append(existing.box, existing.card);
4464
+ return;
4465
+ }
4466
+ if (record) {
4467
+ recordPinState();
4468
+ if (pinned.length >= maxPinned) removePin(pinned[0], false, true);
4469
+ }
4470
+ const root = ensureOverlay();
4471
+ const box = makeBox(document2, FILL_PINNED, OUTLINE_PINNED);
4472
+ const card = makeCard(document2, true);
4473
+ const info = typography.getFull(sourceEl);
4474
+ populateCard(document2, card, info, true);
4475
+ root.append(box, card);
4476
+ const pin = {
4477
+ sourceEl,
4478
+ box,
4479
+ card,
4480
+ userPlaced: state?.userPlaced ?? false,
4481
+ detach: () => {
4482
+ }
4483
+ };
4484
+ const rect = sourceEl.getBoundingClientRect();
4485
+ positionBox(box, rect);
4486
+ positionCard(window2, card, rect);
4487
+ if (state?.userPlaced) {
4488
+ card.style.left = `${state.left}px`;
4489
+ card.style.top = `${state.top}px`;
4490
+ }
4491
+ setState(box, true);
4492
+ setState(card, true);
4493
+ pinned.push(pin);
4494
+ card.querySelector(".mesurer-ti-close")?.addEventListener(
4495
+ "click",
4496
+ (event) => {
4497
+ event.preventDefault();
4498
+ event.stopPropagation();
4499
+ removePin(pin);
4500
+ }
4501
+ );
4502
+ pin.detach = attachDrag(pin);
4503
+ if (notify) options.onPin?.(sourceEl, info);
4504
+ };
4505
+ const restorePins = (states) => {
4506
+ clearPins(false);
4507
+ for (const state of states) {
4508
+ if (state.sourceEl.isConnected) createPin(state.sourceEl, state, false, false);
4509
+ }
4510
+ };
4511
+ const undo = () => {
4512
+ const previous = history.pop();
4513
+ if (!previous) return false;
4514
+ future.push(snapshotPins());
4515
+ restorePins(previous);
4516
+ return true;
4517
+ };
4518
+ const redo = () => {
4519
+ const next = future.pop();
4520
+ if (!next) return false;
4521
+ history.push(snapshotPins());
4522
+ restorePins(next);
4523
+ return true;
4524
+ };
4525
+ const clear = () => {
4526
+ if (!pinned.length) return;
4527
+ recordPinState();
4528
+ clearPins(true);
4529
+ };
4530
+ const syncPins = () => {
4531
+ for (const pin of [...pinned]) {
4532
+ if (!pin.sourceEl.isConnected) {
4533
+ removePin(pin, false);
4534
+ continue;
4535
+ }
4536
+ const rect = pin.sourceEl.getBoundingClientRect();
4537
+ positionBox(pin.box, rect);
4538
+ if (!pin.userPlaced) positionCard(window2, pin.card, rect);
4539
+ const visible = rect.bottom >= 0 && rect.right >= 0 && rect.left <= window2.innerWidth && rect.top <= window2.innerHeight;
4540
+ setState(pin.box, visible);
4541
+ setState(pin.card, visible);
4542
+ pin.card.style.pointerEvents = visible ? "auto" : "none";
4543
+ }
4544
+ };
4545
+ const scheduleFrame = () => {
4546
+ if (frameScheduled) return;
4547
+ frameScheduled = true;
4548
+ requestAnimationFrame(() => {
4549
+ frameScheduled = false;
4550
+ if (!enabled) return;
4551
+ updateHover();
4552
+ syncPins();
4553
+ });
4554
+ };
4555
+ const onMouseMove = (event) => {
4556
+ pointerX = event.clientX;
4557
+ pointerY = event.clientY;
4558
+ scheduleFrame();
4559
+ };
4560
+ const onMouseOut = (event) => {
4561
+ if (!event.relatedTarget) hideHover();
4562
+ };
4563
+ const onScrollOrResize = () => scheduleFrame();
4564
+ const isInspectorUIEvent = (event) => event.composedPath().some(
4565
+ (node) => node instanceof HTMLElementConstructor && (() => {
4566
+ const element = node;
4567
+ return element.id === OVERLAY_ID || element.classList.contains("mesurer-ti-card") || element.classList.contains("mesurer-ti-box") || element.classList.contains("mesurer-ti-close") || element.classList.contains("mesurer-toolbar-surface");
4568
+ })()
4569
+ );
4570
+ const onClick = (event) => {
4571
+ if (isInspectorUIEvent(event) || pointerOverToolbar(event.clientX, event.clientY)) {
4572
+ return;
4573
+ }
4574
+ event.preventDefault();
4575
+ event.stopImmediatePropagation();
4576
+ if (event.button === 0) {
4577
+ const source = pickElementAt(event.clientX, event.clientY);
4578
+ if (source) createPin(source);
4579
+ }
4580
+ };
4581
+ const onAuxClick = (event) => {
4582
+ if (isInspectorUIEvent(event) || pointerOverToolbar(event.clientX, event.clientY)) {
4583
+ return;
4584
+ }
4585
+ event.preventDefault();
4586
+ event.stopImmediatePropagation();
4587
+ };
4588
+ const enable = () => {
4589
+ if (enabled || !availableDocument || !availableWindow) return;
4590
+ enabled = true;
4591
+ ensureStyles();
4592
+ ensureOverlay();
4593
+ document2.body.classList.add(BODY_MODE_CLASS);
4594
+ window2.addEventListener("mousemove", onMouseMove, true);
4595
+ window2.addEventListener("mouseout", onMouseOut, true);
4596
+ window2.addEventListener("click", onClick, true);
4597
+ window2.addEventListener("auxclick", onAuxClick, true);
4598
+ window2.addEventListener("scroll", onScrollOrResize, true);
4599
+ window2.addEventListener("resize", onScrollOrResize, true);
4600
+ };
4601
+ const disable = () => {
4602
+ if (!enabled) return;
4603
+ enabled = false;
4604
+ if (pendingIdleId !== -1) getIdleScheduler().cancel(pendingIdleId);
4605
+ pendingIdleId = -1;
4606
+ pendingElement = null;
4607
+ history.length = 0;
4608
+ future.length = 0;
4609
+ window2.removeEventListener("mousemove", onMouseMove, true);
4610
+ window2.removeEventListener("mouseout", onMouseOut, true);
4611
+ window2.removeEventListener("click", onClick, true);
4612
+ window2.removeEventListener("auxclick", onAuxClick, true);
4613
+ window2.removeEventListener("scroll", onScrollOrResize, true);
4614
+ window2.removeEventListener("resize", onScrollOrResize, true);
4615
+ hideHover();
4616
+ hoverBox?.remove();
4617
+ hoverCard?.remove();
4618
+ hoverBox = null;
4619
+ hoverCard = null;
4620
+ clearPins();
4621
+ document2.body.classList.remove(BODY_MODE_CLASS);
4622
+ };
4623
+ const cleanup = () => {
4624
+ disable();
4625
+ overlay?.remove();
4626
+ overlay = null;
4627
+ if (typeof document2 !== "undefined") {
4628
+ document2.getElementById(STYLE_ID)?.remove();
4629
+ if (portalIsShadowRoot) {
4630
+ options.portalTarget.querySelector(`#${STYLE_ID}`)?.remove();
4631
+ }
4632
+ }
4633
+ };
4634
+ return {
4635
+ enable,
4636
+ disable,
4637
+ undo,
4638
+ redo,
4639
+ canUndo: () => history.length > 0,
4640
+ canRedo: () => future.length > 0,
4641
+ clear,
4642
+ inspect,
4643
+ isEnabled: () => enabled,
4644
+ cleanup,
4645
+ destroy: cleanup
4646
+ };
4647
+ };
4648
+ var TextInspector = createTextInspector({}, true);
4649
+
4650
+ // measurer.tsx
4651
+ var import_jsx_runtime9 = require("react/jsx-runtime");
4652
+ var measurerInstanceCount = 0;
4653
+ var XRAY_STYLE_ID = "mesurer-xray-styles";
4654
+ var XRAY_STYLES = `
4655
+ .xray-mode * {
4656
+ outline: solid 1px blue !important;
4657
+ }
4658
+ .xray-mode #mesurer-extension-host,
4659
+ .xray-mode #mesurer-extension-host *,
4660
+ .xray-mode .mesurer-root,
4661
+ .xray-mode .mesurer-root *,
4662
+ .xray-mode .mesurer-toolbar-surface,
4663
+ .xray-mode .mesurer-toolbar-surface * {
4664
+ outline: none !important;
4665
+ }
4666
+ `;
4667
+ var subscribeHydration = () => () => {
4668
+ };
4669
+ var useHydrated = () => (0, import_react20.useSyncExternalStore)(
4670
+ subscribeHydration,
4671
+ () => true,
4672
+ () => false
4673
+ );
4674
+ var stripMeasurement = (measurement) => ({
4675
+ ...measurement,
4676
+ elementRef: void 0
4677
+ });
4678
+ var stripDistance = (distance) => ({
4679
+ ...distance,
4680
+ elementRefA: void 0,
4681
+ elementRefB: void 0
4682
+ });
4683
+ function MeasurerClient({
3264
4684
  highlightColor,
3265
4685
  guideColor,
3266
4686
  hoverHighlightEnabled,
3267
4687
  persistOnReload,
3268
- portalTarget
4688
+ portalTarget,
4689
+ persistKey
3269
4690
  }) {
3270
- const selectionRectRef = (0, import_react19.useRef)(null);
3271
- const toolbarRef = (0, import_react19.useRef)(null);
3272
- const selectionAnimationCleanupTimeoutRef = (0, import_react19.useRef)(null);
3273
- const persistedState = (0, import_react19.useMemo)(() => {
4691
+ const instanceIdRef = (0, import_react20.useRef)(null);
4692
+ if (instanceIdRef.current === null) {
4693
+ instanceIdRef.current = ++measurerInstanceCount;
4694
+ }
4695
+ const storageKey = persistKey ?? (instanceIdRef.current === 1 ? "mesurer-state" : `mesurer-state-${instanceIdRef.current}`);
4696
+ const ownerDocument = portalTarget.ownerDocument ?? document;
4697
+ const ownerWindow = ownerDocument.defaultView ?? window;
4698
+ const guideScrollRef = (0, import_react20.useRef)({
4699
+ x: ownerWindow.scrollX,
4700
+ y: ownerWindow.scrollY
4701
+ });
4702
+ const textInspectorRef = (0, import_react20.useRef)(null);
4703
+ if (!textInspectorRef.current) {
4704
+ textInspectorRef.current = createTextInspector({ portalTarget });
4705
+ }
4706
+ const textInspector = textInspectorRef.current;
4707
+ const selectionRectRef = (0, import_react20.useRef)(null);
4708
+ const toolbarRef = (0, import_react20.useRef)(null);
4709
+ const selectionAnimationCleanupTimeoutRef = (0, import_react20.useRef)(null);
4710
+ const guideDragRef = (0, import_react20.useRef)(null);
4711
+ const guideUserSelectRef = (0, import_react20.useRef)(null);
4712
+ const persistedState = (0, import_react20.useMemo)(() => {
3274
4713
  if (!persistOnReload) return null;
3275
- const stored = window.localStorage.getItem("mesurer-state");
3276
- if (!stored) return null;
3277
4714
  try {
4715
+ const stored = window.localStorage.getItem(storageKey);
4716
+ if (!stored) return null;
3278
4717
  const parsed = JSON.parse(stored);
3279
4718
  if (!parsed || parsed.version !== 1) return null;
3280
4719
  return parsed;
3281
4720
  } catch {
3282
4721
  return null;
3283
4722
  }
3284
- }, [persistOnReload]);
4723
+ }, [persistOnReload, storageKey]);
4724
+ const enabledRef = (0, import_react20.useRef)(false);
4725
+ const toolModeRef = (0, import_react20.useRef)(
4726
+ persistedState?.toolMode === "rulers" ? "none" : persistedState?.toolMode ?? "none"
4727
+ );
4728
+ const rulersVisibleRef = (0, import_react20.useRef)(
4729
+ persistedState?.rulersVisible ?? persistedState?.toolMode === "rulers"
4730
+ );
4731
+ const guideOrientationRef = (0, import_react20.useRef)(
4732
+ persistedState?.guideOrientation ?? "vertical"
4733
+ );
4734
+ const measurementsRef = (0, import_react20.useRef)(
4735
+ persistedState?.measurements ?? []
4736
+ );
4737
+ const activeMeasurementRef = (0, import_react20.useRef)(
4738
+ persistedState?.activeMeasurement ?? null
4739
+ );
4740
+ const heldDistancesRef = (0, import_react20.useRef)(
4741
+ persistedState?.heldDistances ?? []
4742
+ );
4743
+ const guidesRef = (0, import_react20.useRef)(persistedState?.guides ?? []);
4744
+ const selectedGuideIdsRef = (0, import_react20.useRef)(
4745
+ persistedState?.selectedGuideIds ?? []
4746
+ );
3285
4747
  const { overlayRef, selectedElementRef, hoverElementRef } = useOverlayRefs();
3286
4748
  const {
3287
4749
  selectionOriginRect,
@@ -3307,12 +4769,15 @@ function MeasurerClient({
3307
4769
  setAltPressed,
3308
4770
  toolMode,
3309
4771
  setToolMode,
4772
+ rulersVisible,
4773
+ setRulersVisible,
3310
4774
  guidesEnabled,
3311
4775
  multiMeasureEnabled,
3312
4776
  snapGuidesEnabled
3313
4777
  } = useMeasureToggles({
3314
4778
  initialEnabled: persistedState?.enabled,
3315
- initialToolMode: persistedState?.toolMode
4779
+ initialToolMode: persistedState?.toolMode === "rulers" ? "none" : persistedState?.toolMode,
4780
+ initialRulersVisible: persistedState?.rulersVisible ?? persistedState?.toolMode === "rulers"
3316
4781
  });
3317
4782
  const { start, setStart, end, setEnd, isDragging, setIsDragging } = useDragState();
3318
4783
  const {
@@ -3344,85 +4809,170 @@ function MeasurerClient({
3344
4809
  initialGuides: persistedState?.guides ?? [],
3345
4810
  initialSelectedGuideIds: persistedState?.selectedGuideIds ?? []
3346
4811
  });
3347
- const [toolbarActive, setToolbarActive] = (0, import_react19.useState)(true);
3348
- const { clearGuideDragHold, scheduleGuideDragHold } = useGuideDragHold();
3349
- const [guidePreview, setGuidePreview] = (0, import_react19.useState)(null);
3350
- const [guideOrientation, setGuideOrientation] = (0, import_react19.useState)(persistedState?.guideOrientation ?? "vertical");
3351
- const persistPayload = (0, import_react19.useMemo)(() => {
3352
- if (!persistOnReload) return null;
3353
- return JSON.stringify({
3354
- version: 1,
3355
- enabled,
3356
- toolMode,
3357
- guideOrientation,
3358
- guides,
3359
- selectedGuideIds,
3360
- measurements: measurements.map(stripMeasurement),
3361
- activeMeasurement: activeMeasurement ? stripMeasurement(activeMeasurement) : null,
3362
- heldDistances: heldDistances.map(stripDistance)
3363
- });
3364
- }, [
3365
- activeMeasurement,
3366
- enabled,
3367
- guideOrientation,
3368
- guides,
3369
- heldDistances,
3370
- measurements,
3371
- persistOnReload,
3372
- selectedGuideIds,
3373
- toolMode
3374
- ]);
3375
- (0, import_react19.useEffect)(() => {
4812
+ const [toolbarActive, setToolbarActive] = (0, import_react20.useState)(true);
4813
+ const { clearGuideDragHold, scheduleGuideDragHold } = useGuideDragHold(ownerWindow);
4814
+ const [guidePreview, setGuidePreview] = (0, import_react20.useState)(null);
4815
+ const [guideOrientation, setGuideOrientation] = (0, import_react20.useState)(persistedState?.guideOrientation ?? "vertical");
4816
+ enabledRef.current = enabled;
4817
+ toolModeRef.current = toolMode;
4818
+ rulersVisibleRef.current = rulersVisible;
4819
+ guideOrientationRef.current = guideOrientation;
4820
+ measurementsRef.current = measurements;
4821
+ activeMeasurementRef.current = activeMeasurement;
4822
+ heldDistancesRef.current = heldDistances;
4823
+ guidesRef.current = guides;
4824
+ selectedGuideIdsRef.current = selectedGuideIds;
4825
+ const persistState = (0, import_react20.useCallback)(() => {
3376
4826
  if (!persistOnReload) return;
3377
4827
  if (typeof window === "undefined") return;
3378
- if (!persistPayload) return;
3379
4828
  try {
3380
- window.localStorage.setItem("mesurer-state", persistPayload);
4829
+ window.localStorage.setItem(
4830
+ storageKey,
4831
+ JSON.stringify({
4832
+ version: 1,
4833
+ enabled: enabledRef.current,
4834
+ toolMode: toolModeRef.current,
4835
+ rulersVisible: rulersVisibleRef.current,
4836
+ guideOrientation: guideOrientationRef.current,
4837
+ guides: guidesRef.current,
4838
+ selectedGuideIds: selectedGuideIdsRef.current,
4839
+ measurements: measurementsRef.current.map(stripMeasurement),
4840
+ activeMeasurement: activeMeasurementRef.current ? stripMeasurement(activeMeasurementRef.current) : null,
4841
+ heldDistances: heldDistancesRef.current.map(stripDistance)
4842
+ })
4843
+ );
3381
4844
  } catch {
3382
4845
  }
3383
- const handlePageHide = () => {
3384
- try {
3385
- window.localStorage.setItem("mesurer-state", persistPayload);
3386
- } catch {
4846
+ }, [persistOnReload, storageKey]);
4847
+ const setEnabledPersisted = (0, import_react20.useCallback)(
4848
+ (value) => {
4849
+ const next = typeof value === "function" ? value(enabledRef.current) : value;
4850
+ if (Object.is(next, enabledRef.current)) return;
4851
+ enabledRef.current = next;
4852
+ setEnabled(next);
4853
+ persistState();
4854
+ },
4855
+ [persistState, setEnabled]
4856
+ );
4857
+ const setRulersVisiblePersisted = (0, import_react20.useCallback)(
4858
+ (value) => {
4859
+ const next = typeof value === "function" ? value(rulersVisibleRef.current) : value;
4860
+ if (Object.is(next, rulersVisibleRef.current)) return;
4861
+ rulersVisibleRef.current = next;
4862
+ setRulersVisible(next);
4863
+ persistState();
4864
+ },
4865
+ [persistState, setRulersVisible]
4866
+ );
4867
+ const setToolModePersisted = (0, import_react20.useCallback)(
4868
+ (value) => {
4869
+ const next = typeof value === "function" ? value(toolModeRef.current) : value;
4870
+ if (Object.is(next, toolModeRef.current)) return;
4871
+ toolModeRef.current = next;
4872
+ setToolMode(next);
4873
+ if (next === "text-inspector") {
4874
+ textInspector.enable();
4875
+ } else {
4876
+ textInspector.disable();
3387
4877
  }
3388
- };
3389
- window.addEventListener("pagehide", handlePageHide);
3390
- return () => {
3391
- window.removeEventListener("pagehide", handlePageHide);
3392
- };
3393
- }, [persistOnReload, persistPayload]);
4878
+ persistState();
4879
+ },
4880
+ [persistState, setToolMode, textInspector]
4881
+ );
4882
+ const setGuideOrientationPersisted = (0, import_react20.useCallback)(
4883
+ (value) => {
4884
+ const next = typeof value === "function" ? value(guideOrientationRef.current) : value;
4885
+ if (Object.is(next, guideOrientationRef.current)) return;
4886
+ guideOrientationRef.current = next;
4887
+ setGuideOrientation(next);
4888
+ persistState();
4889
+ },
4890
+ [persistState, setGuideOrientation]
4891
+ );
4892
+ const setMeasurementsPersisted = (0, import_react20.useCallback)(
4893
+ (value) => {
4894
+ const next = typeof value === "function" ? value(measurementsRef.current) : value;
4895
+ if (Object.is(next, measurementsRef.current)) return;
4896
+ measurementsRef.current = next;
4897
+ setMeasurements(next);
4898
+ persistState();
4899
+ },
4900
+ [persistState, setMeasurements]
4901
+ );
4902
+ const setActiveMeasurementPersisted = (0, import_react20.useCallback)(
4903
+ (value) => {
4904
+ const next = typeof value === "function" ? value(activeMeasurementRef.current) : value;
4905
+ if (Object.is(next, activeMeasurementRef.current)) return;
4906
+ activeMeasurementRef.current = next;
4907
+ setActiveMeasurement(next);
4908
+ persistState();
4909
+ },
4910
+ [persistState, setActiveMeasurement]
4911
+ );
4912
+ const setHeldDistancesPersisted = (0, import_react20.useCallback)(
4913
+ (value) => {
4914
+ const next = typeof value === "function" ? value(heldDistancesRef.current) : value;
4915
+ if (Object.is(next, heldDistancesRef.current)) return;
4916
+ heldDistancesRef.current = next;
4917
+ setHeldDistances(next);
4918
+ persistState();
4919
+ },
4920
+ [persistState, setHeldDistances]
4921
+ );
4922
+ const setGuidesPersisted = (0, import_react20.useCallback)(
4923
+ (value) => {
4924
+ const next = typeof value === "function" ? value(guidesRef.current) : value;
4925
+ if (Object.is(next, guidesRef.current)) return;
4926
+ guidesRef.current = next;
4927
+ setGuides(next);
4928
+ persistState();
4929
+ },
4930
+ [persistState, setGuides]
4931
+ );
4932
+ const setSelectedGuideIdsPersisted = (0, import_react20.useCallback)(
4933
+ (value) => {
4934
+ const next = typeof value === "function" ? value(selectedGuideIdsRef.current) : value;
4935
+ if (Object.is(next, selectedGuideIdsRef.current)) return;
4936
+ selectedGuideIdsRef.current = next;
4937
+ setSelectedGuideIds(next);
4938
+ persistState();
4939
+ },
4940
+ [persistState, setSelectedGuideIds]
4941
+ );
3394
4942
  const {
3395
4943
  recordSnapshot,
3396
4944
  createActionCommit,
3397
4945
  setToolModeWithHistory,
3398
4946
  setGuideOrientationWithHistory,
3399
- setEnabledWithHistory
4947
+ setEnabledWithHistory,
4948
+ undo: undoHistory,
4949
+ redo: redoHistory
3400
4950
  } = useMeasurerHistory({
3401
4951
  toggles: {
3402
4952
  enabled,
3403
- setEnabled,
4953
+ setEnabled: setEnabledPersisted,
3404
4954
  toolMode,
3405
- setToolMode,
4955
+ setToolMode: setToolModePersisted,
3406
4956
  guideOrientation,
3407
- setGuideOrientation
4957
+ setGuideOrientation: setGuideOrientationPersisted
3408
4958
  },
3409
4959
  measurements: {
3410
4960
  measurements,
3411
- setMeasurements,
4961
+ setMeasurements: setMeasurementsPersisted,
3412
4962
  activeMeasurement,
3413
- setActiveMeasurement,
4963
+ setActiveMeasurement: setActiveMeasurementPersisted,
3414
4964
  selectedMeasurements,
3415
4965
  setSelectedMeasurements,
3416
4966
  selectedMeasurement,
3417
4967
  setSelectedMeasurement,
3418
4968
  heldDistances,
3419
- setHeldDistances
4969
+ setHeldDistances: setHeldDistancesPersisted
3420
4970
  },
3421
4971
  guides: {
3422
4972
  guides,
3423
- setGuides,
4973
+ setGuides: setGuidesPersisted,
3424
4974
  selectedGuideIds,
3425
- setSelectedGuideIds,
4975
+ setSelectedGuideIds: setSelectedGuideIdsPersisted,
3426
4976
  draggingGuideId,
3427
4977
  setDraggingGuideId
3428
4978
  },
@@ -3437,96 +4987,254 @@ function MeasurerClient({
3437
4987
  clearSelectionRect
3438
4988
  }
3439
4989
  });
3440
- const clearAll = (0, import_react19.useCallback)(() => {
4990
+ const undo = (0, import_react20.useCallback)(() => {
4991
+ if (toolMode === "text-inspector" && textInspector.undo()) return;
4992
+ undoHistory();
4993
+ }, [textInspector, toolMode, undoHistory]);
4994
+ const redo = (0, import_react20.useCallback)(() => {
4995
+ if (toolMode === "text-inspector" && textInspector.redo()) return;
4996
+ redoHistory();
4997
+ }, [redoHistory, textInspector, toolMode]);
4998
+ const clearAll = (0, import_react20.useCallback)(() => {
4999
+ if (toolMode === "text-inspector") {
5000
+ textInspector.clear();
5001
+ }
3441
5002
  recordSnapshot();
3442
5003
  clearGuideDragHold();
3443
5004
  setStart(null);
3444
5005
  setEnd(null);
3445
5006
  setIsDragging(false);
3446
- setActiveMeasurement(null);
3447
- setMeasurements([]);
5007
+ setActiveMeasurementPersisted(null);
5008
+ setMeasurementsPersisted([]);
3448
5009
  setSelectedMeasurement(null);
3449
5010
  setSelectedMeasurements([]);
3450
5011
  clearSelectionRect();
3451
5012
  setSelectedElement(null);
3452
5013
  setHoverRect(null);
3453
5014
  setHoverElement(null);
3454
- setGuides([]);
3455
- setSelectedGuideIds([]);
3456
- setHeldDistances([]);
5015
+ setGuidesPersisted([]);
5016
+ setSelectedGuideIdsPersisted([]);
5017
+ setHeldDistancesPersisted([]);
3457
5018
  }, [
3458
5019
  clearGuideDragHold,
3459
5020
  clearSelectionRect,
3460
5021
  recordSnapshot,
3461
- setActiveMeasurement,
5022
+ setActiveMeasurementPersisted,
3462
5023
  setEnd,
3463
- setGuides,
3464
- setHeldDistances,
5024
+ setGuidesPersisted,
5025
+ setHeldDistancesPersisted,
3465
5026
  setHoverElement,
3466
5027
  setHoverRect,
3467
5028
  setIsDragging,
3468
- setMeasurements,
5029
+ setMeasurementsPersisted,
3469
5030
  setSelectedElement,
3470
- setSelectedGuideIds,
5031
+ setSelectedGuideIdsPersisted,
3471
5032
  setSelectedMeasurement,
3472
5033
  setSelectedMeasurements,
3473
- setStart
5034
+ setStart,
5035
+ textInspector,
5036
+ toolMode
3474
5037
  ]);
3475
- const removeSelectedGuides = (0, import_react19.useCallback)(() => {
5038
+ const removeSelectedGuides = (0, import_react20.useCallback)(() => {
3476
5039
  if (selectedGuideIds.length === 0) return false;
3477
5040
  recordSnapshot();
3478
- setGuides(
5041
+ setGuidesPersisted(
3479
5042
  (prev) => prev.filter((guide) => !selectedGuideIds.includes(guide.id))
3480
5043
  );
3481
- setSelectedGuideIds([]);
5044
+ setSelectedGuideIdsPersisted([]);
3482
5045
  return true;
3483
- }, [recordSnapshot, selectedGuideIds, setGuides, setSelectedGuideIds]);
5046
+ }, [
5047
+ recordSnapshot,
5048
+ selectedGuideIds,
5049
+ setGuidesPersisted,
5050
+ setSelectedGuideIdsPersisted
5051
+ ]);
3484
5052
  useHotkeys({
5053
+ eventTarget: ownerWindow,
3485
5054
  clearAll,
5055
+ undo,
5056
+ redo,
3486
5057
  removeSelectedGuides,
3487
5058
  setEnabled: setEnabledWithHistory,
3488
5059
  setToolMode: setToolModeWithHistory,
5060
+ setRulersVisible: setRulersVisiblePersisted,
3489
5061
  setAltPressed,
3490
5062
  isOverlayActive: () => enabled && (toolMode !== "none" || toolbarActive),
3491
5063
  setGuideOrientation: setGuideOrientationWithHistory,
3492
5064
  onInteract: () => setToolbarActive(true)
3493
5065
  });
3494
5066
  useResizeSync({
3495
- setMeasurements,
3496
- setActiveMeasurement,
3497
- setHeldDistances,
5067
+ document: ownerDocument,
5068
+ window: ownerWindow,
5069
+ setMeasurements: setMeasurementsPersisted,
5070
+ setActiveMeasurement: setActiveMeasurementPersisted,
5071
+ setHeldDistances: setHeldDistancesPersisted,
3498
5072
  setSelectedMeasurement,
3499
- setGuides,
5073
+ setGuides: setGuidesPersisted,
3500
5074
  selectedElementRef
3501
5075
  });
5076
+ (0, import_react20.useEffect)(() => {
5077
+ const handleScroll = () => {
5078
+ const next = {
5079
+ x: ownerWindow.scrollX,
5080
+ y: ownerWindow.scrollY
5081
+ };
5082
+ const deltaX = next.x - guideScrollRef.current.x;
5083
+ const deltaY = next.y - guideScrollRef.current.y;
5084
+ guideScrollRef.current = next;
5085
+ if (deltaX === 0 && deltaY === 0) return;
5086
+ setGuidesPersisted(
5087
+ (prev) => prev.map((guide) => ({
5088
+ ...guide,
5089
+ position: guide.position - (guide.orientation === "vertical" ? deltaX : deltaY)
5090
+ }))
5091
+ );
5092
+ };
5093
+ ownerWindow.addEventListener("scroll", handleScroll, true);
5094
+ return () => ownerWindow.removeEventListener("scroll", handleScroll, true);
5095
+ }, [ownerWindow, setGuidesPersisted]);
3502
5096
  useLiveElementTracking({
5097
+ document: ownerDocument,
5098
+ window: ownerWindow,
3503
5099
  enabled,
3504
5100
  selectedElementRef,
3505
5101
  hoverElementRef,
3506
5102
  setSelectedMeasurement,
3507
5103
  setSelectedMeasurements,
3508
5104
  setHoverRect,
3509
- setMeasurements,
3510
- setActiveMeasurement,
3511
- setHeldDistances
5105
+ setMeasurements: setMeasurementsPersisted,
5106
+ setActiveMeasurement: setActiveMeasurementPersisted,
5107
+ setHeldDistances: setHeldDistancesPersisted
3512
5108
  });
3513
- (0, import_react19.useEffect)(() => {
5109
+ (0, import_react20.useEffect)(() => {
3514
5110
  if (!toolbarActive || toolMode !== "none") return;
3515
5111
  const handlePointerDown2 = (event) => {
3516
5112
  const toolbarNode = toolbarRef.current;
3517
5113
  if (toolbarNode && toolbarNode.contains(event.target)) return;
3518
5114
  setToolbarActive(false);
3519
5115
  };
3520
- window.addEventListener("pointerdown", handlePointerDown2);
5116
+ ownerWindow.addEventListener("pointerdown", handlePointerDown2);
3521
5117
  return () => {
3522
- window.removeEventListener("pointerdown", handlePointerDown2);
5118
+ ownerWindow.removeEventListener("pointerdown", handlePointerDown2);
3523
5119
  };
3524
- }, [toolbarActive, toolMode]);
3525
- (0, import_react19.useEffect)(() => {
3526
- if (hoverHighlightEnabled) return;
3527
- setHoverRect(null);
3528
- }, [hoverHighlightEnabled, setHoverRect]);
3529
- (0, import_react19.useEffect)(() => {
5120
+ }, [ownerWindow, toolbarActive, toolMode]);
5121
+ (0, import_react20.useEffect)(() => {
5122
+ if (!enabled) return;
5123
+ const handleGuidePointerDown2 = (event) => {
5124
+ if (toolbarRef.current?.contains(event.target)) return;
5125
+ const guideTarget = event.composedPath().some(
5126
+ (target) => target instanceof ownerWindow.Element && target.hasAttribute("data-mesurer-guide")
5127
+ );
5128
+ if (guideTarget && toolMode !== "none") return;
5129
+ const point = { x: event.clientX, y: event.clientY };
5130
+ const guide = guides.find((candidate) => {
5131
+ const distance = candidate.orientation === "vertical" ? Math.abs(candidate.position - point.x) : Math.abs(candidate.position - point.y);
5132
+ return distance <= GUIDE_HITBOX_SIZE / 2;
5133
+ });
5134
+ if (!guide) return;
5135
+ if (event.button === 0 && !event.shiftKey && toolMode === "none") {
5136
+ guideDragRef.current = {
5137
+ id: guide.id,
5138
+ orientation: guide.orientation,
5139
+ pointerId: event.pointerId,
5140
+ commit: createActionCommit(),
5141
+ committed: false
5142
+ };
5143
+ }
5144
+ setSelectedGuideIdsPersisted(
5145
+ (prev) => event.shiftKey ? prev.includes(guide.id) ? prev.filter((id) => id !== guide.id) : [...prev, guide.id] : [guide.id]
5146
+ );
5147
+ };
5148
+ const handleGuidePointerMove = (event) => {
5149
+ const drag = guideDragRef.current;
5150
+ if (!drag || drag.pointerId !== event.pointerId) return;
5151
+ const position = drag.orientation === "vertical" ? event.clientX : event.clientY;
5152
+ if (!drag.committed) {
5153
+ event.preventDefault();
5154
+ if (guideUserSelectRef.current === null) {
5155
+ guideUserSelectRef.current = ownerDocument.documentElement.style.userSelect;
5156
+ ownerDocument.documentElement.style.userSelect = "none";
5157
+ }
5158
+ ownerWindow.getSelection()?.removeAllRanges();
5159
+ drag.commit();
5160
+ drag.committed = true;
5161
+ }
5162
+ setGuidesPersisted(
5163
+ (prev) => prev.map(
5164
+ (guide) => guide.id === drag.id ? { ...guide, position } : guide
5165
+ )
5166
+ );
5167
+ };
5168
+ const handleGuidePointerEnd = (event) => {
5169
+ if (guideDragRef.current?.pointerId === event.pointerId) {
5170
+ guideDragRef.current = null;
5171
+ if (guideUserSelectRef.current !== null) {
5172
+ ownerDocument.documentElement.style.userSelect = guideUserSelectRef.current;
5173
+ guideUserSelectRef.current = null;
5174
+ }
5175
+ }
5176
+ };
5177
+ ownerWindow.addEventListener("pointerdown", handleGuidePointerDown2, true);
5178
+ ownerWindow.addEventListener("pointermove", handleGuidePointerMove, true);
5179
+ ownerWindow.addEventListener("pointerup", handleGuidePointerEnd, true);
5180
+ ownerWindow.addEventListener("pointercancel", handleGuidePointerEnd, true);
5181
+ return () => {
5182
+ ownerWindow.removeEventListener(
5183
+ "pointerdown",
5184
+ handleGuidePointerDown2,
5185
+ true
5186
+ );
5187
+ ownerWindow.removeEventListener("pointermove", handleGuidePointerMove, true);
5188
+ ownerWindow.removeEventListener("pointerup", handleGuidePointerEnd, true);
5189
+ ownerWindow.removeEventListener(
5190
+ "pointercancel",
5191
+ handleGuidePointerEnd,
5192
+ true
5193
+ );
5194
+ if (guideUserSelectRef.current !== null) {
5195
+ ownerDocument.documentElement.style.userSelect = guideUserSelectRef.current;
5196
+ guideUserSelectRef.current = null;
5197
+ }
5198
+ };
5199
+ }, [
5200
+ createActionCommit,
5201
+ enabled,
5202
+ guides,
5203
+ ownerWindow,
5204
+ setGuidesPersisted,
5205
+ setSelectedGuideIdsPersisted,
5206
+ toolMode
5207
+ ]);
5208
+ (0, import_react20.useEffect)(() => {
5209
+ if (toolMode === "text-inspector") {
5210
+ textInspector.enable();
5211
+ } else {
5212
+ textInspector.disable();
5213
+ }
5214
+ }, [textInspector, toolMode]);
5215
+ (0, import_react20.useEffect)(() => {
5216
+ let style = ownerDocument.getElementById(XRAY_STYLE_ID);
5217
+ if (!style) {
5218
+ style = ownerDocument.createElement("style");
5219
+ style.id = XRAY_STYLE_ID;
5220
+ style.textContent = XRAY_STYLES;
5221
+ ownerDocument.head.appendChild(style);
5222
+ }
5223
+ if (toolMode === "xray") {
5224
+ ownerDocument.body.classList.add("xray-mode");
5225
+ } else {
5226
+ ownerDocument.body.classList.remove("xray-mode");
5227
+ }
5228
+ return () => {
5229
+ ownerDocument.body.classList.remove("xray-mode");
5230
+ };
5231
+ }, [ownerDocument, toolMode]);
5232
+ (0, import_react20.useEffect)(() => {
5233
+ return () => {
5234
+ textInspector.destroy();
5235
+ };
5236
+ }, [textInspector]);
5237
+ (0, import_react20.useEffect)(() => {
3530
5238
  const hasSelectionAnimationState = !!selectionOriginRect || !!selectedMeasurement?.originRect || selectedMeasurements.some((measurement) => !!measurement.originRect);
3531
5239
  if (!hasSelectionAnimationState) {
3532
5240
  if (selectionAnimationCleanupTimeoutRef.current !== null) {
@@ -3555,6 +5263,12 @@ function MeasurerClient({
3555
5263
  return changed ? next : prev;
3556
5264
  });
3557
5265
  }, MEASURE_TRANSITION_MS);
5266
+ return () => {
5267
+ if (selectionAnimationCleanupTimeoutRef.current !== null) {
5268
+ window.clearTimeout(selectionAnimationCleanupTimeoutRef.current);
5269
+ selectionAnimationCleanupTimeoutRef.current = null;
5270
+ }
5271
+ };
3558
5272
  }, [
3559
5273
  selectionOriginRect,
3560
5274
  selectedMeasurement,
@@ -3563,13 +5277,6 @@ function MeasurerClient({
3563
5277
  setSelectedMeasurements,
3564
5278
  setSelectionOriginRect
3565
5279
  ]);
3566
- (0, import_react19.useEffect)(() => {
3567
- return () => {
3568
- if (selectionAnimationCleanupTimeoutRef.current !== null) {
3569
- window.clearTimeout(selectionAnimationCleanupTimeoutRef.current);
3570
- }
3571
- };
3572
- }, []);
3573
5280
  const displayedMeasurements = holdEnabled ? measurements : multiMeasureEnabled && measurements.length > 0 ? measurements : activeMeasurement ? [activeMeasurement] : [];
3574
5281
  const {
3575
5282
  activeRect,
@@ -3591,6 +5298,8 @@ function MeasurerClient({
3591
5298
  hoverEdgeVisibility,
3592
5299
  measurementEdgeVisibility
3593
5300
  } = useMeasurerDerived({
5301
+ document: ownerDocument,
5302
+ window: ownerWindow,
3594
5303
  start,
3595
5304
  end,
3596
5305
  selectedMeasurements,
@@ -3616,6 +5325,8 @@ function MeasurerClient({
3616
5325
  handlePointerUp,
3617
5326
  handlePointerLeave
3618
5327
  } = useMeasurerPointer({
5328
+ document: ownerDocument,
5329
+ window: ownerWindow,
3619
5330
  toolbarRef,
3620
5331
  overlayRef,
3621
5332
  selectionRectRef,
@@ -3641,15 +5352,15 @@ function MeasurerClient({
3641
5352
  optionPairOverlay,
3642
5353
  setAltPressed,
3643
5354
  setGuidePreview,
3644
- setSelectedGuideIds,
3645
- setGuides,
5355
+ setSelectedGuideIds: setSelectedGuideIdsPersisted,
5356
+ setGuides: setGuidesPersisted,
3646
5357
  setStart,
3647
5358
  setEnd,
3648
5359
  setIsDragging,
3649
- setHeldDistances,
5360
+ setHeldDistances: setHeldDistancesPersisted,
3650
5361
  setDraggingGuideId,
3651
- setActiveMeasurement,
3652
- setMeasurements,
5362
+ setActiveMeasurement: setActiveMeasurementPersisted,
5363
+ setMeasurements: setMeasurementsPersisted,
3653
5364
  setSelectedMeasurements,
3654
5365
  setSelectedMeasurement,
3655
5366
  setSelectionOriginRect,
@@ -3659,27 +5370,65 @@ function MeasurerClient({
3659
5370
  setHoverPointer,
3660
5371
  clearSelectionRect
3661
5372
  });
3662
- const removeHeldDistance = (0, import_react19.useCallback)(
5373
+ const removeHeldDistance = (0, import_react20.useCallback)(
3663
5374
  (id) => {
3664
5375
  recordSnapshot();
3665
- setHeldDistances((prev) => prev.filter((distance) => distance.id !== id));
5376
+ setHeldDistancesPersisted(
5377
+ (prev) => prev.filter((distance) => distance.id !== id)
5378
+ );
5379
+ },
5380
+ [recordSnapshot, setHeldDistancesPersisted]
5381
+ );
5382
+ const startGuideFromRuler = (0, import_react20.useCallback)(
5383
+ (orientation, position) => {
5384
+ const id = createId();
5385
+ const commit = createActionCommit();
5386
+ commit();
5387
+ setSelectedGuideIdsPersisted([]);
5388
+ setGuidesPersisted((prev) => [...prev, { id, orientation, position }]);
5389
+ return id;
5390
+ },
5391
+ [
5392
+ createActionCommit,
5393
+ setGuidesPersisted,
5394
+ setSelectedGuideIdsPersisted
5395
+ ]
5396
+ );
5397
+ const moveGuideFromRuler = (0, import_react20.useCallback)(
5398
+ (id, position) => {
5399
+ setGuidesPersisted(
5400
+ (prev) => prev.map((guide) => guide.id === id ? { ...guide, position } : guide)
5401
+ );
3666
5402
  },
3667
- [recordSnapshot, setHeldDistances]
5403
+ [setGuidesPersisted]
3668
5404
  );
3669
- const handleGuidePointerDown = (0, import_react19.useCallback)(
5405
+ const finishGuideFromRuler = (0, import_react20.useCallback)(
5406
+ (id) => {
5407
+ setSelectedGuideIdsPersisted([id]);
5408
+ },
5409
+ [setSelectedGuideIdsPersisted]
5410
+ );
5411
+ const cancelGuideFromRuler = (0, import_react20.useCallback)(
5412
+ (id) => {
5413
+ setGuidesPersisted((prev) => prev.filter((guide) => guide.id !== id));
5414
+ },
5415
+ [setGuidesPersisted]
5416
+ );
5417
+ const handleGuidePointerDown = (0, import_react20.useCallback)(
3670
5418
  (guide, event) => {
3671
5419
  const commit = createActionCommit();
3672
5420
  if (!enabled) return;
3673
5421
  event.stopPropagation();
5422
+ event.preventDefault();
3674
5423
  if (event.shiftKey) {
3675
5424
  commit();
3676
- setSelectedGuideIds(
5425
+ setSelectedGuideIdsPersisted(
3677
5426
  (prev) => prev.includes(guide.id) ? prev.filter((id) => id !== guide.id) : [...prev, guide.id]
3678
5427
  );
3679
5428
  return;
3680
5429
  }
3681
5430
  commit();
3682
- setSelectedGuideIds([guide.id]);
5431
+ setSelectedGuideIdsPersisted([guide.id]);
3683
5432
  scheduleGuideDragHold(guide.id, setDraggingGuideId);
3684
5433
  event.currentTarget.setPointerCapture(event.pointerId);
3685
5434
  },
@@ -3688,10 +5437,10 @@ function MeasurerClient({
3688
5437
  enabled,
3689
5438
  scheduleGuideDragHold,
3690
5439
  setDraggingGuideId,
3691
- setSelectedGuideIds
5440
+ setSelectedGuideIdsPersisted
3692
5441
  ]
3693
5442
  );
3694
- const handleGuidePointerUp = (0, import_react19.useCallback)(
5443
+ const handleGuidePointerUp = (0, import_react20.useCallback)(
3695
5444
  (guide, event) => {
3696
5445
  event.stopPropagation();
3697
5446
  clearGuideDragHold();
@@ -3703,17 +5452,29 @@ function MeasurerClient({
3703
5452
  [clearGuideDragHold, setDraggingGuideId]
3704
5453
  );
3705
5454
  return (0, import_react_dom.createPortal)(
3706
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
5455
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
3707
5456
  "div",
3708
5457
  {
3709
5458
  ref: overlayRef,
3710
5459
  className: "mesurer-root msr:pointer-events-none msr:fixed msr:inset-0 msr:z-50",
3711
5460
  children: [
3712
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
5461
+ enabled && rulersVisible ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
5462
+ RulersOverlay,
5463
+ {
5464
+ onStartGuide: startGuideFromRuler,
5465
+ onMoveGuide: moveGuideFromRuler,
5466
+ onFinishGuide: finishGuideFromRuler,
5467
+ onCancelGuide: cancelGuideFromRuler,
5468
+ guides,
5469
+ selectedGuideIds
5470
+ }
5471
+ ) : null,
5472
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
3713
5473
  MeasurerOverlay,
3714
5474
  {
3715
5475
  enabled,
3716
5476
  toolMode,
5477
+ guidePointerEvents: enabled && (toolMode !== "none" || rulersVisible),
3717
5478
  guidesEnabled,
3718
5479
  altPressed,
3719
5480
  isDragging,
@@ -3751,13 +5512,16 @@ function MeasurerClient({
3751
5512
  onGuidePointerCancel: handleGuidePointerUp
3752
5513
  }
3753
5514
  ),
3754
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
5515
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
3755
5516
  Toolbar,
3756
5517
  {
3757
5518
  ref: toolbarRef,
5519
+ eventTarget: ownerWindow,
3758
5520
  toolMode,
3759
5521
  setEnabled: setEnabledWithHistory,
3760
5522
  setToolMode: setToolModeWithHistory,
5523
+ rulersVisible,
5524
+ setRulersVisible: setRulersVisiblePersisted,
3761
5525
  guideOrientation,
3762
5526
  setGuideOrientation: setGuideOrientationWithHistory,
3763
5527
  onInteract: () => setToolbarActive(true)
@@ -3774,25 +5538,29 @@ function Measurer({
3774
5538
  guideColor = "oklch(0.63 0.26 29.23)",
3775
5539
  hoverHighlightEnabled = true,
3776
5540
  persistOnReload = false,
3777
- portalTarget
5541
+ portalTarget,
5542
+ persistKey
3778
5543
  }) {
3779
5544
  if (typeof document !== "undefined") {
3780
5545
  ensureMeasurerStyles(MESURER_STYLES, portalTarget);
3781
5546
  }
3782
5547
  const hydrated = useHydrated();
3783
5548
  if (!hydrated) return null;
3784
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
5549
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
3785
5550
  MeasurerClient,
3786
5551
  {
3787
5552
  highlightColor,
3788
5553
  guideColor,
3789
5554
  hoverHighlightEnabled,
3790
5555
  persistOnReload,
5556
+ persistKey,
3791
5557
  portalTarget: portalTarget ?? document.body
3792
5558
  }
3793
5559
  );
3794
5560
  }
3795
5561
  // Annotate the CommonJS export names for ESM import in node:
3796
5562
  0 && (module.exports = {
3797
- Measurer
5563
+ Measurer,
5564
+ TextInspector,
5565
+ createTextInspector
3798
5566
  });