mesurer 0.0.9 → 0.0.11

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.js CHANGED
@@ -1,10 +1,10 @@
1
1
  // measurer.tsx
2
2
  import {
3
- useCallback as useCallback6,
4
- useEffect as useEffect5,
3
+ useCallback as useCallback8,
4
+ useEffect as useEffect7,
5
5
  useMemo as useMemo2,
6
- useRef as useRef9,
7
- useState as useState8,
6
+ useRef as useRef12,
7
+ useState as useState11,
8
8
  useSyncExternalStore
9
9
  } from "react";
10
10
  import { createPortal } from "react-dom";
@@ -24,6 +24,7 @@ var MIN_SINGLE_ELEMENT_COVERAGE = 0.15;
24
24
 
25
25
  // runtime/style-inject.ts
26
26
  var MESURER_STYLE_ID = "mesurer-styles";
27
+ var injectedTargets = /* @__PURE__ */ new WeakSet();
27
28
  var isDocument = (target) => target.nodeType === 9;
28
29
  var isShadowRoot = (target) => target.nodeType === 11;
29
30
  var getStyleTarget = (target) => {
@@ -34,12 +35,29 @@ var getStyleTarget = (target) => {
34
35
  if (rootNode.nodeType === 11) return rootNode;
35
36
  return target.ownerDocument ?? document;
36
37
  };
38
+ var adoptSheet = (styleTarget, cssText) => {
39
+ const sheet = new CSSStyleSheet();
40
+ sheet.replaceSync(cssText);
41
+ styleTarget.adoptedStyleSheets = [...styleTarget.adoptedStyleSheets, sheet];
42
+ };
37
43
  function ensureMeasurerStyles(cssText, target) {
38
44
  if (typeof document === "undefined") return;
39
45
  if (!cssText) return;
40
46
  const styleTarget = getStyleTarget(target);
41
47
  if (!styleTarget) return;
42
- if (styleTarget.querySelector(`#${MESURER_STYLE_ID}`)) return;
48
+ if (injectedTargets.has(styleTarget)) return;
49
+ if (styleTarget.querySelector(`#${MESURER_STYLE_ID}`)) {
50
+ injectedTargets.add(styleTarget);
51
+ return;
52
+ }
53
+ if (isShadowRoot(styleTarget)) {
54
+ try {
55
+ adoptSheet(styleTarget, cssText);
56
+ injectedTargets.add(styleTarget);
57
+ return;
58
+ } catch {
59
+ }
60
+ }
43
61
  const ownerDocument = isDocument(styleTarget) ? styleTarget : styleTarget.ownerDocument;
44
62
  if (!ownerDocument) return;
45
63
  const style = ownerDocument.createElement("style");
@@ -47,22 +65,24 @@ function ensureMeasurerStyles(cssText, target) {
47
65
  style.textContent = cssText;
48
66
  if (isShadowRoot(styleTarget)) {
49
67
  styleTarget.appendChild(style);
50
- return;
68
+ } else {
69
+ ownerDocument.head.appendChild(style);
51
70
  }
52
- ownerDocument.head.appendChild(style);
71
+ injectedTargets.add(styleTarget);
53
72
  }
54
73
 
55
74
  // styles.generated.ts
56
- 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';
75
+ 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-red-50: oklch(97.1% 0.013 17.38);\n --msr-color-red-200: oklch(88.5% 0.062 18.334);\n --msr-color-red-600: oklch(57.7% 0.245 27.325);\n --msr-color-black: #000;\n --msr-color-white: #fff;\n --msr-spacing: 0.25rem;\n --msr-font-weight-normal: 400;\n --msr-font-weight-medium: 500;\n --msr-font-weight-semibold: 600;\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: #f8fafc;\n --msr-color-ink-200: #e2e8f0;\n --msr-color-ink-500: #64748b;\n --msr-color-ink-700: #334155;\n --msr-color-ink-900: #0f172a;\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-1 {\n right: calc(var(--msr-spacing) * -1);\n }\n .msr\\:right-0 {\n right: calc(var(--msr-spacing) * 0);\n }\n .msr\\:right-\\[8px\\] {\n right: 8px;\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-\\[8px\\] {\n left: 8px;\n }\n .msr\\:left-\\[18px\\] {\n left: 18px;\n }\n .msr\\:z-10 {\n z-index: 10;\n }\n .msr\\:z-50 {\n z-index: 50;\n }\n .msr\\:z-\\[70\\] {\n z-index: 70;\n }\n .msr\\:z-\\[80\\] {\n z-index: 80;\n }\n .msr\\:z-\\[90\\] {\n z-index: 90;\n }\n .msr\\:col-span-2 {\n grid-column: span 2 / span 2;\n }\n .msr\\:mt-2 {\n margin-top: calc(var(--msr-spacing) * 2);\n }\n .msr\\:mb-1 {\n margin-bottom: calc(var(--msr-spacing) * 1);\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\\:ml-1 {\n margin-left: calc(var(--msr-spacing) * 1);\n }\n .msr\\:box-border {\n box-sizing: border-box;\n }\n .msr\\:block {\n display: block;\n }\n .msr\\:flex {\n display: flex;\n }\n .msr\\:grid {\n display: grid;\n }\n .msr\\:inline-flex {\n display: inline-flex;\n }\n .msr\\:size-3 {\n width: calc(var(--msr-spacing) * 3);\n height: calc(var(--msr-spacing) * 3);\n }\n .msr\\:size-4 {\n width: calc(var(--msr-spacing) * 4);\n height: calc(var(--msr-spacing) * 4);\n }\n .msr\\:size-8 {\n width: calc(var(--msr-spacing) * 8);\n height: calc(var(--msr-spacing) * 8);\n }\n .msr\\:size-\\[10px\\] {\n width: 10px;\n height: 10px;\n }\n .msr\\:size-\\[18px\\] {\n width: 18px;\n height: 18px;\n }\n .msr\\:size-full {\n width: 100%;\n height: 100%;\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-\\[14px\\] {\n height: 14px;\n }\n .msr\\:h-full {\n height: 100%;\n }\n .msr\\:h-px {\n height: 1px;\n }\n .msr\\:max-h-\\[min\\(70vh\\,34rem\\)\\] {\n max-height: min(70vh, 34rem);\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-9 {\n width: calc(var(--msr-spacing) * 9);\n }\n .msr\\:w-12 {\n width: calc(var(--msr-spacing) * 12);\n }\n .msr\\:w-44 {\n width: calc(var(--msr-spacing) * 44);\n }\n .msr\\:w-\\[18px\\] {\n width: 18px;\n }\n .msr\\:w-\\[26px\\] {\n width: 26px;\n }\n .msr\\:w-\\[272px\\] {\n width: 272px;\n }\n .msr\\:w-full {\n width: 100%;\n }\n .msr\\:w-px {\n width: 1px;\n }\n .msr\\:max-w-\\[calc\\(100vw-16px\\)\\] {\n max-width: calc(100vw - 16px);\n }\n .msr\\:min-w-0 {\n min-width: calc(var(--msr-spacing) * 0);\n }\n .msr\\:min-w-36 {\n min-width: calc(var(--msr-spacing) * 36);\n }\n .msr\\:flex-1 {\n flex: 1;\n }\n .msr\\:shrink-0 {\n flex-shrink: 0;\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\\:cursor-pointer {\n cursor: pointer;\n }\n .msr\\:touch-none {\n touch-action: none;\n }\n .msr\\:appearance-none {\n appearance: none;\n }\n .msr\\:grid-cols-\\[78px_156px\\] {\n grid-template-columns: 78px 156px;\n }\n .msr\\:flex-col {\n flex-direction: column;\n }\n .msr\\:items-center {\n align-items: center;\n }\n .msr\\:items-start {\n align-items: flex-start;\n }\n .msr\\:items-stretch {\n align-items: stretch;\n }\n .msr\\:justify-between {\n justify-content: space-between;\n }\n .msr\\:justify-center {\n justify-content: center;\n }\n .msr\\:justify-end {\n justify-content: flex-end;\n }\n .msr\\:gap-0 {\n gap: calc(var(--msr-spacing) * 0);\n }\n .msr\\:gap-1 {\n gap: calc(var(--msr-spacing) * 1);\n }\n .msr\\:gap-1\\.5 {\n gap: calc(var(--msr-spacing) * 1.5);\n }\n .msr\\:gap-2 {\n gap: calc(var(--msr-spacing) * 2);\n }\n .msr\\:gap-3 {\n gap: calc(var(--msr-spacing) * 3);\n }\n .msr\\:gap-px {\n gap: 1px;\n }\n .msr\\:gap-x-3 {\n column-gap: calc(var(--msr-spacing) * 3);\n }\n .msr\\:gap-y-1 {\n row-gap: calc(var(--msr-spacing) * 1);\n }\n .msr\\:justify-self-end {\n justify-self: flex-end;\n }\n .msr\\:overflow-hidden {\n overflow: hidden;\n }\n .msr\\:overflow-y-auto {\n overflow-y: auto;\n }\n .msr\\:rounded {\n border-radius: 0.25rem;\n }\n .msr\\:rounded-\\[3px\\] {\n border-radius: 3px;\n }\n .msr\\:rounded-\\[5px\\] {\n border-radius: 5px;\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-full {\n border-radius: calc(infinity * 1px);\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-0 {\n border-style: var(--tw-border-style);\n border-width: 0px;\n }\n .msr\\:border-t {\n border-top-style: var(--tw-border-style);\n border-top-width: 1px;\n }\n .msr\\:border-t-2 {\n border-top-style: var(--tw-border-style);\n border-top-width: 2px;\n }\n .msr\\:border-b {\n border-bottom-style: var(--tw-border-style);\n border-bottom-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-dotted {\n --tw-border-style: dotted;\n border-style: dotted;\n }\n .msr\\:border-solid {\n --tw-border-style: solid;\n border-style: solid;\n }\n .msr\\:border-\\[\\#0d99ff\\] {\n border-color: #0d99ff;\n }\n .msr\\:border-\\[\\#2563eb\\]\\/70 {\n border-color: color-mix(in oklab, #2563eb 70%, transparent);\n }\n .msr\\:border-black\\/8 {\n border-color: var(--msr-color-black);\n @supports (color: color-mix(in lab, red, red)) {\n border-color: color-mix(in oklab, var(--msr-color-black) 8%, transparent);\n }\n }\n .msr\\:border-black\\/10 {\n border-color: var(--msr-color-black);\n @supports (color: color-mix(in lab, red, red)) {\n border-color: color-mix(in oklab, var(--msr-color-black) 10%, transparent);\n }\n }\n .msr\\:border-black\\/15 {\n border-color: var(--msr-color-black);\n @supports (color: color-mix(in lab, red, red)) {\n border-color: color-mix(in oklab, var(--msr-color-black) 15%, transparent);\n }\n }\n .msr\\:border-ink-200 {\n border-color: var(--msr-color-ink-200);\n }\n .msr\\:border-ink-700 {\n border-color: var(--msr-color-ink-700);\n }\n .msr\\:border-red-200 {\n border-color: var(--msr-color-red-200);\n }\n .msr\\:border-transparent {\n border-color: transparent;\n }\n .msr\\:bg-\\[\\#0d99ff\\] {\n background-color: #0d99ff;\n }\n .msr\\:bg-\\[\\#0d99ff\\]\\/10 {\n background-color: color-mix(in oklab, #0d99ff 10%, transparent);\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-50 {\n background-color: var(--msr-color-ink-50);\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\\:p-3 {\n padding: calc(var(--msr-spacing) * 3);\n }\n .msr\\:p-px {\n padding: 1px;\n }\n .msr\\:px-1 {\n padding-inline: calc(var(--msr-spacing) * 1);\n }\n .msr\\:px-1\\.5 {\n padding-inline: calc(var(--msr-spacing) * 1.5);\n }\n .msr\\:px-2 {\n padding-inline: calc(var(--msr-spacing) * 2);\n }\n .msr\\:py-0 {\n padding-block: calc(var(--msr-spacing) * 0);\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\\:py-2 {\n padding-block: calc(var(--msr-spacing) * 2);\n }\n .msr\\:pb-1 {\n padding-bottom: calc(var(--msr-spacing) * 1);\n }\n .msr\\:text-center {\n text-align: center;\n }\n .msr\\:text-left {\n text-align: left;\n }\n .msr\\:font-mono {\n font-family: var(--msr-font-mono);\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\\:leading-4 {\n --tw-leading: calc(var(--msr-spacing) * 4);\n line-height: calc(var(--msr-spacing) * 4);\n }\n .msr\\:leading-none {\n --tw-leading: 1;\n line-height: 1;\n }\n .msr\\:font-medium {\n --tw-font-weight: var(--msr-font-weight-medium);\n font-weight: var(--msr-font-weight-medium);\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-black\\/45 {\n color: var(--msr-color-black);\n @supports (color: color-mix(in lab, red, red)) {\n color: color-mix(in oklab, var(--msr-color-black) 45%, transparent);\n }\n }\n .msr\\:text-black\\/60 {\n color: var(--msr-color-black);\n @supports (color: color-mix(in lab, red, red)) {\n color: color-mix(in oklab, var(--msr-color-black) 60%, transparent);\n }\n }\n .msr\\:text-ink-50 {\n color: var(--msr-color-ink-50);\n }\n .msr\\:text-ink-500 {\n color: var(--msr-color-ink-500);\n }\n .msr\\:text-ink-700 {\n color: var(--msr-color-ink-700);\n }\n .msr\\:text-ink-900 {\n color: var(--msr-color-ink-900);\n }\n .msr\\:text-red-600 {\n color: var(--msr-color-red-600);\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\\:shadow-\\[0_0_0_1px_rgba\\(15\\,23\\,42\\,0\\.12\\)\\] {\n --tw-shadow: 0 0 0 1px var(--tw-shadow-color, rgba(15,23,42,0.12));\n box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);\n }\n .msr\\:shadow-lg {\n --tw-shadow: 0 10px 15px -3px var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 4px 6px -4px var(--tw-shadow-color, rgb(0 0 0 / 0.1));\n box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);\n }\n .msr\\:shadow-sm {\n --tw-shadow: 0 1px 3px 0 var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 1px 2px -1px var(--tw-shadow-color, rgb(0 0 0 / 0.1));\n box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);\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-colors {\n transition-property: color, background-color, border-color, outline-color, text-decoration-color, fill, stroke, --tw-gradient-from, --tw-gradient-via, --tw-gradient-to;\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-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-shadow {\n transition-property: box-shadow;\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-transform {\n transition-property: transform, translate, scale, rotate;\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\\:group-hover\\:border-ink-200 {\n &:is(:where(.msr\\:group):hover *) {\n @media (hover: hover) {\n border-color: var(--msr-color-ink-200);\n }\n }\n }\n .msr\\:focus-within\\:rounded-l-\\[5px\\] {\n &:focus-within {\n border-top-left-radius: 5px;\n border-bottom-left-radius: 5px;\n }\n }\n .msr\\:focus-within\\:rounded-r-\\[5px\\] {\n &:focus-within {\n border-top-right-radius: 5px;\n border-bottom-right-radius: 5px;\n }\n }\n .msr\\:focus-within\\:outline {\n &:focus-within {\n outline-style: var(--tw-outline-style);\n outline-width: 1px;\n }\n }\n .msr\\:focus-within\\:outline-1 {\n &:focus-within {\n outline-style: var(--tw-outline-style);\n outline-width: 1px;\n }\n }\n .msr\\:focus-within\\:outline-offset-\\[-1px\\] {\n &:focus-within {\n outline-offset: -1px;\n }\n }\n .msr\\:focus-within\\:outline-\\[\\#0d99ff\\] {\n &:focus-within {\n outline-color: #0d99ff;\n }\n }\n .msr\\:hover\\:border-ink-200 {\n &:hover {\n @media (hover: hover) {\n border-color: var(--msr-color-ink-200);\n }\n }\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\\:bg-ink-50 {\n &:hover {\n @media (hover: hover) {\n background-color: var(--msr-color-ink-50);\n }\n }\n }\n .msr\\:hover\\:bg-red-50 {\n &:hover {\n @media (hover: hover) {\n background-color: var(--msr-color-red-50);\n }\n }\n }\n .msr\\:hover\\:text-black {\n &:hover {\n @media (hover: hover) {\n color: var(--msr-color-black);\n }\n }\n }\n .msr\\:hover\\:text-ink-700 {\n &:hover {\n @media (hover: hover) {\n color: var(--msr-color-ink-700);\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\\:hover\\:underline {\n &:hover {\n @media (hover: hover) {\n text-decoration-line: underline;\n }\n }\n }\n .msr\\:focus\\:shadow-\\[inset_0_0_0_1px_\\#0d99ff\\] {\n &:focus {\n --tw-shadow: inset 0 0 0 1px var(--tw-shadow-color, #0d99ff);\n box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);\n }\n }\n .msr\\:focus\\:outline-none {\n &:focus {\n --tw-outline-style: none;\n outline-style: none;\n }\n }\n .msr\\:focus-visible\\:shadow-\\[inset_0_0_0_1px_\\#0d99ff\\] {\n &:focus-visible {\n --tw-shadow: inset 0 0 0 1px var(--tw-shadow-color, #0d99ff);\n box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);\n }\n }\n .msr\\:focus-visible\\:shadow-\\[inset_0_0_0_1px_\\#ef4444\\] {\n &:focus-visible {\n --tw-shadow: inset 0 0 0 1px var(--tw-shadow-color, #ef4444);\n box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);\n }\n }\n .msr\\:focus-visible\\:ring-1 {\n &:focus-visible {\n --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor);\n box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);\n }\n }\n .msr\\:focus-visible\\:ring-\\[\\#0d99ff\\]\\/25 {\n &:focus-visible {\n --tw-ring-color: color-mix(in oklab, #0d99ff 25%, transparent);\n }\n }\n .msr\\:focus-visible\\:outline-none {\n &:focus-visible {\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:host {\n all: initial;\n display: block;\n color-scheme: light;\n color: #0f172a;\n font-family: ui-sans-serif, system-ui, sans-serif;\n font-size: 16px;\n font-weight: 400;\n line-height: 1.5;\n letter-spacing: normal;\n direction: ltr;\n --msr-spacing: 4px;\n}\n*, *::before, *::after {\n box-sizing: border-box;\n}\n@layer base {\n *, ::before, ::after, ::backdrop {\n --tw-border-style: solid;\n --tw-outline-style: solid;\n --tw-shadow: 0 0 #0000;\n --tw-inset-shadow: 0 0 #0000;\n --tw-ring-shadow: 0 0 #0000;\n --tw-inset-ring-shadow: 0 0 #0000;\n --tw-ring-offset-shadow: 0 0 #0000;\n --tw-translate-x: 0;\n --tw-translate-y: 0;\n --tw-translate-z: 0;\n }\n button, input, select, textarea {\n appearance: none;\n background-color: transparent;\n padding: 0;\n font: inherit;\n color: inherit;\n letter-spacing: inherit;\n }\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-color-picker {\n border: 1px solid rgba(0, 0, 0, 0.1);\n box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);\n}\n.mesurer-settings-tabs {\n box-shadow: inset 0 0 0 1px rgba(15, 23, 42, 0.08);\n}\n.mesurer-settings-tab[aria-selected="true"] {\n box-shadow: 0 0 0 1px rgba(15, 23, 42, 0.12);\n}\n.mesurer-switch-track {\n box-sizing: border-box;\n border: 1px solid #e2e8f0;\n}\n.mesurer-switch-track[data-checked="true"] {\n border-color: #0d99ff;\n}\n.mesurer-control-shell {\n border: 1px solid transparent;\n}\n.mesurer-control-shell:hover {\n border-color: #e2e8f0;\n}\n.mesurer-control-focus:focus-within {\n outline: 1px solid #0d99ff;\n outline-offset: -1px;\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-leading {\n syntax: "*";\n inherits: false;\n}\n@property --tw-font-weight {\n syntax: "*";\n inherits: false;\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-shadow {\n syntax: "*";\n inherits: false;\n initial-value: 0 0 #0000;\n}\n@property --tw-shadow-color {\n syntax: "*";\n inherits: false;\n}\n@property --tw-shadow-alpha {\n syntax: "<percentage>";\n inherits: false;\n initial-value: 100%;\n}\n@property --tw-inset-shadow {\n syntax: "*";\n inherits: false;\n initial-value: 0 0 #0000;\n}\n@property --tw-inset-shadow-color {\n syntax: "*";\n inherits: false;\n}\n@property --tw-inset-shadow-alpha {\n syntax: "<percentage>";\n inherits: false;\n initial-value: 100%;\n}\n@property --tw-ring-color {\n syntax: "*";\n inherits: false;\n}\n@property --tw-ring-shadow {\n syntax: "*";\n inherits: false;\n initial-value: 0 0 #0000;\n}\n@property --tw-inset-ring-color {\n syntax: "*";\n inherits: false;\n}\n@property --tw-inset-ring-shadow {\n syntax: "*";\n inherits: false;\n initial-value: 0 0 #0000;\n}\n@property --tw-ring-inset {\n syntax: "*";\n inherits: false;\n}\n@property --tw-ring-offset-width {\n syntax: "<length>";\n inherits: false;\n initial-value: 0px;\n}\n@property --tw-ring-offset-color {\n syntax: "*";\n inherits: false;\n initial-value: #fff;\n}\n@property --tw-ring-offset-shadow {\n syntax: "*";\n inherits: false;\n initial-value: 0 0 #0000;\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-leading: initial;\n --tw-font-weight: initial;\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-shadow: 0 0 #0000;\n --tw-shadow-color: initial;\n --tw-shadow-alpha: 100%;\n --tw-inset-shadow: 0 0 #0000;\n --tw-inset-shadow-color: initial;\n --tw-inset-shadow-alpha: 100%;\n --tw-ring-color: initial;\n --tw-ring-shadow: 0 0 #0000;\n --tw-inset-ring-color: initial;\n --tw-inset-ring-shadow: 0 0 #0000;\n --tw-ring-inset: initial;\n --tw-ring-offset-width: 0px;\n --tw-ring-offset-color: #fff;\n --tw-ring-offset-shadow: 0 0 #0000;\n --tw-outline-style: solid;\n --tw-duration: initial;\n }\n }\n}\n';
57
76
 
58
77
  // components/toolbar.tsx
59
78
  import {
60
79
  forwardRef,
61
80
  memo,
62
- useCallback,
81
+ useCallback as useCallback2,
82
+ useEffect,
63
83
  useLayoutEffect,
64
- useRef,
65
- useState
84
+ useRef as useRef2,
85
+ useState as useState3
66
86
  } from "react";
67
87
 
68
88
  // core/utils.ts
@@ -70,9 +90,751 @@ var formatValue = (value) => Math.round(value);
70
90
  var cn = (...inputs) => inputs.filter(Boolean).join(" ");
71
91
  var createId = () => typeof crypto !== "undefined" && "randomUUID" in crypto ? crypto.randomUUID() : `${Date.now()}-${Math.random()}`;
72
92
 
93
+ // components/settings-panel.tsx
94
+ import { useState as useState2 } from "react";
95
+
96
+ // package.json
97
+ var package_default = {
98
+ name: "mesurer",
99
+ version: "0.0.11",
100
+ description: "Measure and align elements on any website.",
101
+ license: "MIT",
102
+ author: "Julien Thibeaut",
103
+ type: "module",
104
+ main: "./dist/index.cjs",
105
+ module: "./dist/index.js",
106
+ types: "./dist/index.d.ts",
107
+ exports: {
108
+ ".": {
109
+ types: "./dist/index.d.ts",
110
+ import: "./dist/index.js",
111
+ require: "./dist/index.cjs"
112
+ },
113
+ "./styles.css": "./dist/styles.css",
114
+ "./package.json": "./package.json"
115
+ },
116
+ files: [
117
+ "dist",
118
+ "dist/styles.css",
119
+ "CHANGELOG.md",
120
+ "README.md",
121
+ "LICENSE"
122
+ ],
123
+ sideEffects: [
124
+ "*.css"
125
+ ],
126
+ publishConfig: {
127
+ access: "public"
128
+ },
129
+ homepage: "https://mesurer.dev",
130
+ repository: {
131
+ type: "git",
132
+ url: "https://github.com/ibelick/mesurer.git"
133
+ },
134
+ bugs: {
135
+ url: "https://github.com/ibelick/mesurer/issues"
136
+ },
137
+ scripts: {
138
+ build: "pnpm exec tailwindcss -i styles.css -o styles.generated.css && node scripts/generate-styles.mjs && tsup && pnpm exec tailwindcss -i styles.css -o dist/styles.css",
139
+ dev: "node scripts/dev.mjs",
140
+ typecheck: "tsc --noEmit",
141
+ prepublishOnly: "npm run typecheck && npm run build"
142
+ },
143
+ peerDependencies: {
144
+ react: ">=18.0.0",
145
+ "react-dom": ">=18.0.0"
146
+ },
147
+ devDependencies: {
148
+ "@tailwindcss/cli": "^4.2.2",
149
+ "@types/react": "^19.0.0",
150
+ "@types/react-dom": "^19.0.0",
151
+ react: "^19.0.0",
152
+ "react-dom": "^19.0.0",
153
+ tailwindcss: "^4.2.2",
154
+ tsup: "^8.5.0",
155
+ typescript: "^5.9.2"
156
+ },
157
+ keywords: [
158
+ "measurement",
159
+ "alignment"
160
+ ]
161
+ };
162
+
163
+ // core/colors.ts
164
+ var clamp = (value, min = 0, max = 1) => Math.min(max, Math.max(min, value));
165
+ var round = (value, digits = 2) => {
166
+ const factor = 10 ** digits;
167
+ return Math.round(value * factor) / factor;
168
+ };
169
+ var parseChannel = (value) => {
170
+ const trimmed = value.trim();
171
+ const numeric = trimmed.endsWith("%") ? trimmed.slice(0, -1).trim() : trimmed;
172
+ if (!/^[+-]?(?:\d+\.?\d*|\.\d+)(?:e[+-]?\d+)?$/i.test(numeric)) return null;
173
+ const parsed = Number.parseFloat(numeric);
174
+ if (!Number.isFinite(parsed)) return null;
175
+ if (trimmed.endsWith("%")) return clamp(parsed / 100) * 255;
176
+ return clamp(parsed / 255) * 255;
177
+ };
178
+ var parseAlpha = (value) => {
179
+ if (!value) return 1;
180
+ const trimmed = value.trim();
181
+ const numeric = trimmed.endsWith("%") ? trimmed.slice(0, -1).trim() : trimmed;
182
+ if (!/^[+-]?(?:\d+\.?\d*|\.\d+)(?:e[+-]?\d+)?$/i.test(numeric)) return null;
183
+ const parsed = Number.parseFloat(numeric);
184
+ if (!Number.isFinite(parsed)) return null;
185
+ return clamp(trimmed.endsWith("%") ? parsed / 100 : parsed);
186
+ };
187
+ var parseCssColor = (value) => {
188
+ const input = value.trim().toLowerCase();
189
+ if (!input || input === "transparent") return null;
190
+ if (input.startsWith("#")) {
191
+ const hex = input.slice(1);
192
+ const expanded = hex.length === 3 || hex.length === 4 ? hex.split("").map((digit) => digit + digit).join("") : hex;
193
+ if (![6, 8].includes(expanded.length) || !/^[\da-f]+$/.test(expanded)) return null;
194
+ return {
195
+ red: Number.parseInt(expanded.slice(0, 2), 16),
196
+ green: Number.parseInt(expanded.slice(2, 4), 16),
197
+ blue: Number.parseInt(expanded.slice(4, 6), 16),
198
+ alpha: expanded.length === 8 ? Number.parseInt(expanded.slice(6, 8), 16) / 255 : 1
199
+ };
200
+ }
201
+ const match = input.match(/^rgba?\((.*)\)$/);
202
+ if (!match) return null;
203
+ const channels = match[1].replace(/\//g, " ").split(/[ ,]+/).filter(Boolean);
204
+ if (channels.length < 3 || channels.length > 4) return null;
205
+ const red = parseChannel(channels[0]);
206
+ const green = parseChannel(channels[1]);
207
+ const blue = parseChannel(channels[2]);
208
+ const alpha = parseAlpha(channels[3]);
209
+ if (red === null || green === null || blue === null || alpha === null) return null;
210
+ return { red, green, blue, alpha };
211
+ };
212
+ var toByte = (value) => Math.round(clamp(value / 255) * 255);
213
+ var colorToHex = (color) => {
214
+ const hex = [color.red, color.green, color.blue].map((channel) => toByte(channel).toString(16).padStart(2, "0")).join("");
215
+ if (color.alpha >= 0.999) return `#${hex}`;
216
+ return `#${hex}${Math.round(color.alpha * 255).toString(16).padStart(2, "0")}`;
217
+ };
218
+ var colorToRgb = (color) => {
219
+ const channels = `${toByte(color.red)}, ${toByte(color.green)}, ${toByte(color.blue)}`;
220
+ return color.alpha >= 0.999 ? `rgb(${channels})` : `rgba(${channels}, ${round(color.alpha, 3)})`;
221
+ };
222
+ var colorToHsl = (color) => {
223
+ const red = color.red / 255;
224
+ const green = color.green / 255;
225
+ const blue = color.blue / 255;
226
+ const max = Math.max(red, green, blue);
227
+ const min = Math.min(red, green, blue);
228
+ const lightness = (max + min) / 2;
229
+ const delta = max - min;
230
+ let hue = 0;
231
+ let saturation = 0;
232
+ if (delta !== 0) {
233
+ saturation = delta / (1 - Math.abs(2 * lightness - 1));
234
+ if (max === red) hue = (green - blue) / delta % 6;
235
+ else if (max === green) hue = (blue - red) / delta + 2;
236
+ else hue = (red - green) / delta + 4;
237
+ hue *= 60;
238
+ if (hue < 0) hue += 360;
239
+ }
240
+ const result = `hsl(${round(hue, 1)} ${round(saturation * 100, 1)}% ${round(lightness * 100, 1)}%`;
241
+ return color.alpha >= 0.999 ? `${result})` : `${result} / ${round(color.alpha, 3)})`;
242
+ };
243
+ var linearize = (value) => {
244
+ const normalized = value / 255;
245
+ return normalized <= 0.04045 ? normalized / 12.92 : ((normalized + 0.055) / 1.055) ** 2.4;
246
+ };
247
+ var colorToOklch = (color) => {
248
+ const red = linearize(color.red);
249
+ const green = linearize(color.green);
250
+ const blue = linearize(color.blue);
251
+ const l = 0.4122214708 * red + 0.5363325363 * green + 0.0514459929 * blue;
252
+ const m = 0.2119034982 * red + 0.6806995451 * green + 0.1073969566 * blue;
253
+ const s = 0.0883024619 * red + 0.2817188376 * green + 0.6299787005 * blue;
254
+ const lRoot = Math.cbrt(l);
255
+ const mRoot = Math.cbrt(m);
256
+ const sRoot = Math.cbrt(s);
257
+ const lightness = 0.2104542553 * lRoot + 0.793617785 * mRoot - 0.0040720468 * sRoot;
258
+ const a = 1.9779984951 * lRoot - 2.428592205 * mRoot + 0.4505937099 * sRoot;
259
+ const b = 0.0259040371 * lRoot + 0.7827717662 * mRoot - 0.808675766 * sRoot;
260
+ const chroma = Math.sqrt(a * a + b * b);
261
+ let hue = Math.atan2(b, a) * 180 / Math.PI;
262
+ if (hue < 0) hue += 360;
263
+ const result = `oklch(${round(lightness, 3)} ${round(chroma, 3)} ${round(hue, 1)}`;
264
+ return color.alpha >= 0.999 ? `${result})` : `${result} / ${round(color.alpha, 3)})`;
265
+ };
266
+ var formatColor = (color, format) => {
267
+ if (format === "hex") return colorToHex(color);
268
+ if (format === "rgb") return colorToRgb(color);
269
+ if (format === "hsl") return colorToHsl(color);
270
+ return colorToOklch(color);
271
+ };
272
+
273
+ // components/tooltip.tsx
274
+ import { useCallback, useRef, useState } from "react";
275
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
276
+ var TOOLTIP_DELAY_MS = 800;
277
+ function Tooltip({
278
+ label,
279
+ shortcut,
280
+ visible,
281
+ instant = false,
282
+ side = "top",
283
+ className
284
+ }) {
285
+ return /* @__PURE__ */ jsxs(
286
+ "span",
287
+ {
288
+ role: "tooltip",
289
+ className: cn(
290
+ "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",
291
+ instant && "msr:transition-none",
292
+ side === "top" ? "msr:bottom-full msr:mb-2" : "msr:top-full msr:mt-2",
293
+ visible === void 0 ? null : visible ? "msr:opacity-100" : "msr:opacity-0",
294
+ className
295
+ ),
296
+ children: [
297
+ label,
298
+ shortcut ? /* @__PURE__ */ jsxs(Fragment, { children: [
299
+ " ",
300
+ /* @__PURE__ */ jsx("kbd", { className: "msr:text-white/60", children: shortcut })
301
+ ] }) : null
302
+ ]
303
+ }
304
+ );
305
+ }
306
+ function useTooltip() {
307
+ const [visibleTooltipId, setVisibleTooltipId] = useState(null);
308
+ const timerRef = useRef(null);
309
+ const instantRef = useRef(false);
310
+ const [tooltipInstant, setTooltipInstant] = useState(false);
311
+ const clearTimer = useCallback(() => {
312
+ if (timerRef.current === null) return;
313
+ window.clearTimeout(timerRef.current);
314
+ timerRef.current = null;
315
+ }, []);
316
+ const onTooltipEnter = useCallback((id) => {
317
+ clearTimer();
318
+ if (instantRef.current) {
319
+ setTooltipInstant(true);
320
+ setVisibleTooltipId(id);
321
+ return;
322
+ }
323
+ setTooltipInstant(false);
324
+ timerRef.current = window.setTimeout(() => {
325
+ setVisibleTooltipId(id);
326
+ instantRef.current = true;
327
+ timerRef.current = null;
328
+ }, TOOLTIP_DELAY_MS);
329
+ }, [clearTimer]);
330
+ const onTooltipLeave = useCallback(() => {
331
+ clearTimer();
332
+ setVisibleTooltipId(null);
333
+ }, [clearTimer]);
334
+ const onTooltipContainerLeave = useCallback(() => {
335
+ clearTimer();
336
+ setVisibleTooltipId(null);
337
+ instantRef.current = false;
338
+ setTooltipInstant(false);
339
+ }, [clearTimer]);
340
+ return {
341
+ visibleTooltipId,
342
+ tooltipInstant,
343
+ onTooltipEnter,
344
+ onTooltipLeave,
345
+ onTooltipContainerLeave
346
+ };
347
+ }
348
+
349
+ // components/settings-panel.tsx
350
+ import { Fragment as Fragment2, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
351
+ var COLOR_FORMATS = ["hex", "rgb", "hsl", "oklch"];
352
+ var GUIDE_PATTERNS = [
353
+ { value: "solid", label: "Solid" },
354
+ { value: "dashed", label: "Dashed" },
355
+ { value: "dotted", label: "Dotted" }
356
+ ];
357
+ function ControlShell({ left, right }) {
358
+ return /* @__PURE__ */ jsxs2(
359
+ "div",
360
+ {
361
+ className: "mesurer-control-shell msr:group msr:flex msr:h-6 msr:w-full msr:min-w-0 msr:items-center msr:overflow-hidden msr:rounded-[5px] msr:border msr:border-transparent msr:bg-ink-50 msr:hover:border-ink-200",
362
+ children: [
363
+ /* @__PURE__ */ jsx2("div", { className: "mesurer-control-focus msr:flex msr:h-full msr:min-w-0 msr:flex-1 msr:items-center msr:focus-within:rounded-l-[5px] msr:focus-within:outline msr:focus-within:outline-1 msr:focus-within:outline-[#0d99ff] msr:focus-within:outline-offset-[-1px]", children: left }),
364
+ /* @__PURE__ */ jsx2("div", { className: "mesurer-control-focus msr:box-border msr:flex msr:h-full msr:w-12 msr:shrink-0 msr:items-center msr:border-l msr:border-transparent msr:group-hover:border-ink-200 msr:focus-within:rounded-r-[5px] msr:focus-within:outline msr:focus-within:outline-1 msr:focus-within:outline-[#0d99ff] msr:focus-within:outline-offset-[-1px]", children: right })
365
+ ]
366
+ }
367
+ );
368
+ }
369
+ function SettingsSwitch({ label, checked, onChange }) {
370
+ return /* @__PURE__ */ jsxs2(
371
+ "button",
372
+ {
373
+ type: "button",
374
+ role: "switch",
375
+ "aria-checked": checked,
376
+ className: "msr:col-span-2 msr:grid msr:h-6 msr:w-full msr:appearance-none msr:grid-cols-[78px_156px] msr:items-center msr:gap-3 msr:text-left msr:text-[12px] msr:leading-none msr:text-ink-700",
377
+ onClick: () => onChange(!checked),
378
+ children: [
379
+ /* @__PURE__ */ jsx2("span", { children: label }),
380
+ /* @__PURE__ */ jsx2(
381
+ "span",
382
+ {
383
+ "aria-hidden": "true",
384
+ style: { justifySelf: "end" },
385
+ "data-checked": checked ? "true" : void 0,
386
+ className: cn(
387
+ "mesurer-switch-track msr:flex msr:h-[14px] msr:w-[26px] msr:shrink-0 msr:items-center msr:rounded-full msr:border msr:p-px msr:transition-colors",
388
+ checked ? "msr:border-[#0d99ff] msr:bg-[#0d99ff]" : "msr:border-ink-200 msr:bg-ink-50"
389
+ ),
390
+ children: /* @__PURE__ */ jsx2(
391
+ "span",
392
+ {
393
+ className: "msr:block msr:size-[10px] msr:shrink-0 msr:rounded-full msr:bg-white msr:shadow-sm msr:transition-transform",
394
+ style: { transform: `translateX(${checked ? 12 : 0}px)` }
395
+ }
396
+ )
397
+ }
398
+ )
399
+ ]
400
+ }
401
+ );
402
+ }
403
+ function SliderControl({
404
+ label,
405
+ min,
406
+ max,
407
+ step,
408
+ value,
409
+ onChange,
410
+ formatValue: formatValue4 = (currentValue) => String(currentValue),
411
+ parseInput = (input) => Number(input)
412
+ }) {
413
+ const thumbSize = 12;
414
+ const thumbInset = 8;
415
+ const percentage = (value - min) / (max - min) * 100;
416
+ const [draftValue, setDraftValue] = useState2(formatValue4(value));
417
+ const [editing, setEditing] = useState2(false);
418
+ const commitDraft = () => {
419
+ const parsed = parseInput(draftValue);
420
+ if (Number.isFinite(parsed)) {
421
+ onChange(Math.min(max, Math.max(min, parsed)));
422
+ }
423
+ const next = Number.isFinite(parsed) ? Math.min(max, Math.max(min, parsed)) : value;
424
+ setDraftValue(formatValue4(next));
425
+ setEditing(false);
426
+ };
427
+ const updateFromPointer = (event) => {
428
+ event.stopPropagation();
429
+ const rect = event.currentTarget.getBoundingClientRect();
430
+ const usableWidth = Math.max(1, rect.width - thumbInset * 2);
431
+ const ratio = Math.min(1, Math.max(0, (event.clientX - rect.left - thumbInset) / usableWidth));
432
+ const rawValue = min + ratio * (max - min);
433
+ const steppedValue = Math.round((rawValue - min) / step) * step + min;
434
+ onChange(Number(steppedValue.toFixed(4)));
435
+ };
436
+ return /* @__PURE__ */ jsxs2("div", { className: "msr:col-span-2 msr:grid msr:w-full msr:grid-cols-[78px_156px] msr:items-center msr:gap-3", children: [
437
+ /* @__PURE__ */ jsx2("span", { className: "msr:text-[11px] msr:font-medium msr:text-ink-700", children: label }),
438
+ /* @__PURE__ */ jsx2(
439
+ ControlShell,
440
+ {
441
+ left: /* @__PURE__ */ jsxs2(
442
+ "div",
443
+ {
444
+ className: "msr:relative msr:min-w-0 msr:flex-1 msr:touch-none msr:select-none msr:px-2",
445
+ style: { height: 20 },
446
+ "data-slider-container": "true",
447
+ onPointerDown: (event) => {
448
+ event.stopPropagation();
449
+ event.currentTarget.setPointerCapture(event.pointerId);
450
+ updateFromPointer(event);
451
+ },
452
+ onPointerMove: (event) => {
453
+ if (event.currentTarget.hasPointerCapture(event.pointerId)) updateFromPointer(event);
454
+ },
455
+ onPointerUp: (event) => {
456
+ event.stopPropagation();
457
+ if (event.currentTarget.hasPointerCapture(event.pointerId)) event.currentTarget.releasePointerCapture(event.pointerId);
458
+ },
459
+ onPointerCancel: (event) => {
460
+ event.stopPropagation();
461
+ if (event.currentTarget.hasPointerCapture(event.pointerId)) event.currentTarget.releasePointerCapture(event.pointerId);
462
+ },
463
+ onClick: (event) => event.stopPropagation(),
464
+ children: [
465
+ /* @__PURE__ */ jsx2(
466
+ "div",
467
+ {
468
+ className: "msr:absolute msr:left-[8px] msr:right-[8px] msr:rounded-full",
469
+ style: { top: 8, height: 4, backgroundColor: "rgba(15, 23, 42, 0.16)" },
470
+ "aria-hidden": "true"
471
+ }
472
+ ),
473
+ /* @__PURE__ */ jsx2(
474
+ "div",
475
+ {
476
+ className: "msr:absolute msr:left-[8px] msr:rounded-full",
477
+ style: { top: 8, width: `calc(${percentage}% - ${percentage * thumbInset * 2 / 100}px)`, height: 4, backgroundColor: "#0d99ff" },
478
+ "aria-hidden": "true"
479
+ }
480
+ ),
481
+ /* @__PURE__ */ jsx2(
482
+ "div",
483
+ {
484
+ className: "msr:absolute msr:rounded-[5px] msr:bg-white msr:shadow-sm msr:transition-shadow msr:outline-none msr:focus-visible:ring-1 msr:focus-visible:ring-[#0d99ff]/25",
485
+ style: {
486
+ left: `calc(8px + (100% - 16px) * ${percentage / 100})`,
487
+ top: 4,
488
+ width: thumbSize,
489
+ height: thumbSize,
490
+ border: "0",
491
+ transform: "translateX(-50%)"
492
+ },
493
+ role: "slider",
494
+ tabIndex: 0,
495
+ "aria-label": label,
496
+ "aria-valuemin": min,
497
+ "aria-valuemax": max,
498
+ "aria-valuenow": value,
499
+ "aria-orientation": "horizontal",
500
+ onKeyDown: (event) => {
501
+ event.stopPropagation();
502
+ const direction = event.key === "ArrowRight" || event.key === "ArrowUp" ? 1 : event.key === "ArrowLeft" || event.key === "ArrowDown" ? -1 : 0;
503
+ if (event.key === "Home") onChange(min);
504
+ else if (event.key === "End") onChange(max);
505
+ else if (direction) {
506
+ event.preventDefault();
507
+ onChange(Number(Math.min(max, Math.max(min, value + direction * step)).toFixed(4)));
508
+ } else return;
509
+ event.preventDefault();
510
+ }
511
+ }
512
+ )
513
+ ]
514
+ }
515
+ ),
516
+ right: /* @__PURE__ */ jsx2(
517
+ "input",
518
+ {
519
+ type: "text",
520
+ "aria-label": `${label} value`,
521
+ className: "msr:h-full msr:w-full msr:shrink-0 msr:border-0 msr:bg-transparent msr:px-2 msr:text-center msr:font-mono msr:text-[12px] msr:font-medium msr:tabular-nums msr:text-ink-700 msr:outline-none",
522
+ style: { boxSizing: "border-box", borderRadius: "0 5px 5px 0", lineHeight: "1rem" },
523
+ value: editing ? draftValue : formatValue4(value),
524
+ onFocus: () => {
525
+ setDraftValue(formatValue4(value));
526
+ setEditing(true);
527
+ },
528
+ onChange: (event) => {
529
+ const nextDraft = event.target.value;
530
+ setDraftValue(nextDraft);
531
+ const next = parseInput(nextDraft);
532
+ if (Number.isFinite(next)) onChange(Math.min(max, Math.max(min, next)));
533
+ },
534
+ onPointerDown: (event) => event.stopPropagation(),
535
+ onClick: (event) => event.stopPropagation(),
536
+ onBlur: () => {
537
+ commitDraft();
538
+ },
539
+ onKeyDown: (event) => {
540
+ event.stopPropagation();
541
+ if (event.key === "ArrowUp" || event.key === "ArrowDown") {
542
+ event.preventDefault();
543
+ const current = parseInput(event.currentTarget.value);
544
+ const direction = event.key === "ArrowUp" ? 1 : -1;
545
+ const next = Number(
546
+ Math.min(max, Math.max(min, (Number.isFinite(current) ? current : value) + direction * step)).toFixed(4)
547
+ );
548
+ setDraftValue(formatValue4(next));
549
+ onChange(next);
550
+ return;
551
+ }
552
+ if (event.key === "Enter") {
553
+ event.preventDefault();
554
+ event.currentTarget.blur();
555
+ }
556
+ }
557
+ }
558
+ )
559
+ }
560
+ )
561
+ ] });
562
+ }
563
+ function ColorField({ label, value, fallback, ownerWindow, onChange }) {
564
+ const parsed = parseCssColor(value);
565
+ const canvas = ownerWindow.document.createElement("canvas");
566
+ const context = canvas.getContext("2d");
567
+ if (context) context.fillStyle = value;
568
+ const serialized = typeof context?.fillStyle === "string" ? context.fillStyle : "";
569
+ const converted = serialized ? parseCssColor(serialized) : null;
570
+ const inputValue = parsed ?? converted ? colorToHex(parsed ?? converted).slice(0, 7) : fallback;
571
+ const sample = parsed ?? converted ?? parseCssColor(fallback);
572
+ const hexValue = sample ? colorToHex({ ...sample, alpha: 1 }).slice(1).toUpperCase() : "000000";
573
+ const alphaValue = sample ? Math.round(sample.alpha * 100) : 100;
574
+ const [hexDraft, setHexDraft] = useState2(hexValue);
575
+ const [alphaDraft, setAlphaDraft] = useState2(String(alphaValue));
576
+ const [hexFocused, setHexFocused] = useState2(false);
577
+ const [alphaFocused, setAlphaFocused] = useState2(false);
578
+ const updateColor = (nextHex, nextAlpha) => {
579
+ if (!/^[\da-f]{6}$/i.test(nextHex)) return;
580
+ const nextSample = parseCssColor(`#${nextHex}`);
581
+ if (!nextSample) return;
582
+ onChange(colorToHex({ ...nextSample, alpha: Math.min(100, Math.max(0, nextAlpha)) / 100 }));
583
+ };
584
+ const swatchColor = ownerWindow.CSS?.supports("color", value) ? value : fallback;
585
+ return /* @__PURE__ */ jsxs2("div", { className: "msr:col-span-2 msr:grid msr:w-full msr:grid-cols-[78px_156px] msr:items-center msr:gap-3 msr:text-[12px] msr:text-ink-700", children: [
586
+ /* @__PURE__ */ jsx2("span", { children: label }),
587
+ /* @__PURE__ */ jsx2(
588
+ ControlShell,
589
+ {
590
+ left: /* @__PURE__ */ jsxs2(Fragment2, { children: [
591
+ /* @__PURE__ */ jsx2(
592
+ "span",
593
+ {
594
+ className: "msr:relative msr:ml-1 msr:block msr:size-4 msr:shrink-0 msr:overflow-hidden msr:rounded-[3px] msr:border msr:border-black/10",
595
+ style: { backgroundColor: swatchColor },
596
+ children: /* @__PURE__ */ jsx2(
597
+ "input",
598
+ {
599
+ type: "color",
600
+ "aria-label": `${label} color picker`,
601
+ value: inputValue,
602
+ className: "msr:absolute msr:inset-0 msr:size-full msr:cursor-pointer msr:opacity-0",
603
+ onChange: (event) => onChange(event.target.value)
604
+ }
605
+ )
606
+ }
607
+ ),
608
+ /* @__PURE__ */ jsx2(
609
+ "input",
610
+ {
611
+ "aria-label": `${label} hex value`,
612
+ type: "text",
613
+ value: hexFocused ? hexDraft : hexValue,
614
+ maxLength: 6,
615
+ className: "msr:min-w-0 msr:flex-1 msr:bg-transparent msr:px-2 msr:font-mono msr:text-[12px] msr:tabular-nums msr:text-ink-700 msr:outline-none",
616
+ onFocus: () => {
617
+ setHexDraft(hexValue);
618
+ setHexFocused(true);
619
+ },
620
+ onBlur: () => setHexFocused(false),
621
+ onChange: (event) => {
622
+ const next = event.target.value.replace(/[^\da-f]/gi, "").slice(0, 6).toUpperCase();
623
+ setHexDraft(next);
624
+ updateColor(next, alphaFocused ? Number(alphaDraft) : alphaValue);
625
+ },
626
+ onPointerDown: (event) => event.stopPropagation(),
627
+ onClick: (event) => event.stopPropagation()
628
+ }
629
+ )
630
+ ] }),
631
+ right: /* @__PURE__ */ jsx2(
632
+ "input",
633
+ {
634
+ "aria-label": `${label} opacity value`,
635
+ type: "text",
636
+ inputMode: "numeric",
637
+ value: alphaFocused ? alphaDraft ? `${alphaDraft}%` : "" : `${alphaValue}%`,
638
+ maxLength: 4,
639
+ className: "msr:h-full msr:w-full msr:bg-transparent msr:px-1 msr:text-center msr:font-mono msr:text-[12px] msr:tabular-nums msr:text-ink-700 msr:outline-none",
640
+ onFocus: () => {
641
+ setAlphaDraft(String(alphaValue));
642
+ setAlphaFocused(true);
643
+ },
644
+ onBlur: () => setAlphaFocused(false),
645
+ onChange: (event) => {
646
+ const next = event.target.value.replace(/\D/g, "").slice(0, 3);
647
+ setAlphaDraft(next);
648
+ updateColor(hexFocused ? hexDraft : hexValue, Number(next));
649
+ },
650
+ onKeyDown: (event) => {
651
+ if (event.key !== "ArrowUp" && event.key !== "ArrowDown") return;
652
+ event.preventDefault();
653
+ event.stopPropagation();
654
+ const current = Number.parseInt(alphaFocused ? alphaDraft : String(alphaValue), 10);
655
+ const direction = event.key === "ArrowUp" ? 1 : -1;
656
+ const next = Math.min(100, Math.max(0, (Number.isFinite(current) ? current : 0) + direction));
657
+ setAlphaDraft(String(next));
658
+ updateColor(hexFocused ? hexDraft : hexValue, next);
659
+ },
660
+ onPointerDown: (event) => event.stopPropagation(),
661
+ onClick: (event) => event.stopPropagation()
662
+ }
663
+ )
664
+ }
665
+ )
666
+ ] });
667
+ }
668
+ function SettingsPanel({
669
+ ownerWindow,
670
+ highlightColor,
671
+ setHighlightColor,
672
+ guideColor,
673
+ setGuideColor,
674
+ hoverHighlight,
675
+ setHoverHighlight,
676
+ persistOnReload,
677
+ setPersistOnReload,
678
+ colorFormats,
679
+ setColorFormats,
680
+ colorClickFormat,
681
+ setColorClickFormat,
682
+ snapEnabled,
683
+ setSnapEnabled,
684
+ snapGuidesEnabled,
685
+ setSnapGuidesEnabled,
686
+ selectNewGuideEnabled,
687
+ setSelectNewGuideEnabled,
688
+ multiMeasureEnabled,
689
+ setMultiMeasureEnabled,
690
+ guideStyle,
691
+ setGuideStyle,
692
+ rulerSettings,
693
+ setRulerSettings,
694
+ activeTab,
695
+ onTabChange,
696
+ onResetSettings,
697
+ onClearWorkspace
698
+ }) {
699
+ const toggleFormat = (format) => {
700
+ setColorFormats((previous) => {
701
+ if (previous.includes(format)) {
702
+ if (previous.length === 1) return previous;
703
+ return previous.filter((item) => item !== format);
704
+ }
705
+ return [...previous, format];
706
+ });
707
+ };
708
+ const patternTooltip = useTooltip();
709
+ return /* @__PURE__ */ jsxs2("div", { className: "msr:flex msr:max-h-[min(70vh,34rem)] msr:flex-col msr:gap-2 msr:overflow-y-auto", onPointerDown: (event) => event.stopPropagation(), children: [
710
+ /* @__PURE__ */ jsx2("div", { className: "mesurer-settings-tabs msr:flex msr:h-6 msr:shrink-0 msr:select-none msr:items-stretch msr:gap-0 msr:rounded-[5px] msr:bg-ink-50 msr:p-px", role: "tablist", "aria-label": "Settings sections", children: [
711
+ ["guides", "Guides"],
712
+ ["select", "Select"],
713
+ ["color-picker", "Color"],
714
+ ["rulers", "Rulers"],
715
+ ["general", "General"]
716
+ ].map(([value, label]) => /* @__PURE__ */ jsx2(
717
+ "button",
718
+ {
719
+ type: "button",
720
+ role: "tab",
721
+ "aria-selected": activeTab === value,
722
+ className: cn(
723
+ "mesurer-settings-tab msr:relative msr:flex msr:min-w-0 msr:flex-1 msr:appearance-none msr:items-center msr:justify-center msr:whitespace-nowrap msr:px-1.5 msr:py-0 msr:text-[10px] msr:font-medium msr:transition-colors msr:focus-visible:outline-none msr:focus-visible:shadow-[inset_0_0_0_1px_#0d99ff]",
724
+ activeTab === value ? "msr:rounded-[5px] msr:bg-white msr:text-ink-900 msr:shadow-[0_0_0_1px_rgba(15,23,42,0.12)]" : "msr:rounded-[5px] msr:text-ink-500 msr:hover:text-ink-700"
725
+ ),
726
+ onClick: () => onTabChange(value),
727
+ children: label
728
+ },
729
+ value
730
+ )) }),
731
+ activeTab === "guides" ? /* @__PURE__ */ jsxs2("section", { className: "msr:grid msr:grid-cols-[78px_156px] msr:items-center msr:gap-x-3 msr:gap-y-1", "aria-label": "Guide settings", children: [
732
+ /* @__PURE__ */ jsx2(ColorField, { label: "Color", value: guideColor, fallback: "#f97316", ownerWindow, onChange: setGuideColor }),
733
+ /* @__PURE__ */ jsx2(SliderControl, { label: "Weight", min: 1, max: 4, step: 1, value: guideStyle.width, formatValue: (value) => `${value}px`, parseInput: (input) => Number.parseFloat(input), onChange: (value) => setGuideStyle((style) => ({ ...style, width: value })) }),
734
+ /* @__PURE__ */ jsxs2("div", { className: "msr:col-span-2 msr:grid msr:grid-cols-[78px_156px] msr:items-center msr:gap-3", children: [
735
+ /* @__PURE__ */ jsx2("span", { className: "msr:text-[12px] msr:text-ink-700", children: "Pattern" }),
736
+ /* @__PURE__ */ jsx2("div", { className: "msr:flex msr:gap-1", role: "radiogroup", "aria-label": "Guide pattern", onMouseLeave: patternTooltip.onTooltipContainerLeave, children: GUIDE_PATTERNS.map(({ value, label }) => {
737
+ const selected = guideStyle.pattern === value;
738
+ const tooltipId = `guide-pattern-${value}`;
739
+ return /* @__PURE__ */ jsxs2(
740
+ "button",
741
+ {
742
+ type: "button",
743
+ role: "radio",
744
+ "aria-label": `${label} guide pattern`,
745
+ "aria-checked": selected,
746
+ className: cn(
747
+ "msr:relative msr:flex msr:h-6 msr:min-w-0 msr:flex-1 msr:items-center msr:justify-center msr:rounded-[5px] msr:border msr:px-1 msr:focus-visible:outline-none msr:focus-visible:shadow-[inset_0_0_0_1px_#0d99ff]",
748
+ selected ? "msr:border-[#0d99ff] msr:bg-[#0d99ff]/10" : "msr:border-ink-200 msr:bg-ink-50 msr:hover:bg-ink-100"
749
+ ),
750
+ onClick: () => setGuideStyle((style) => ({ ...style, pattern: value })),
751
+ onMouseEnter: () => patternTooltip.onTooltipEnter(tooltipId),
752
+ onFocus: () => patternTooltip.onTooltipEnter(tooltipId),
753
+ onBlur: patternTooltip.onTooltipLeave,
754
+ children: [
755
+ /* @__PURE__ */ jsx2("span", { "aria-hidden": "true", className: cn("msr:block msr:w-full msr:border-t-2 msr:border-ink-700", value === "dashed" ? "msr:border-dashed" : value === "dotted" ? "msr:border-dotted" : "msr:border-solid") }),
756
+ /* @__PURE__ */ jsx2(Tooltip, { label, visible: patternTooltip.visibleTooltipId === tooltipId, instant: patternTooltip.tooltipInstant, className: "msr:z-10" })
757
+ ]
758
+ },
759
+ value
760
+ );
761
+ }) })
762
+ ] }),
763
+ guideStyle.pattern !== "solid" ? /* @__PURE__ */ jsxs2(Fragment2, { children: [
764
+ /* @__PURE__ */ jsx2(SliderControl, { label: "Length", min: 2, max: 24, step: 1, value: guideStyle.dashLength, formatValue: (value) => `${value}px`, parseInput: (input) => Number.parseFloat(input), onChange: (value) => setGuideStyle((style) => ({ ...style, dashLength: value })) }),
765
+ /* @__PURE__ */ jsx2(SliderControl, { label: "Gap", min: 0, max: 24, step: 1, value: guideStyle.gap, formatValue: (value) => `${value}px`, parseInput: (input) => Number.parseFloat(input), onChange: (value) => setGuideStyle((style) => ({ ...style, gap: value })) })
766
+ ] }) : null,
767
+ /* @__PURE__ */ jsx2("div", { className: "msr:col-span-2", children: /* @__PURE__ */ jsx2(SettingsSwitch, { label: "Snap", checked: snapGuidesEnabled, onChange: setSnapGuidesEnabled }) }),
768
+ /* @__PURE__ */ jsx2("div", { className: "msr:col-span-2", children: /* @__PURE__ */ jsx2(SettingsSwitch, { label: "Highlight", checked: selectNewGuideEnabled, onChange: setSelectNewGuideEnabled }) })
769
+ ] }) : null,
770
+ activeTab === "select" ? /* @__PURE__ */ jsxs2("section", { className: "msr:grid msr:grid-cols-[78px_156px] msr:items-center msr:gap-x-3 msr:gap-y-1", "aria-label": "Selection settings", children: [
771
+ /* @__PURE__ */ jsx2(ColorField, { label: "Color", value: highlightColor, fallback: "#0d99ff", ownerWindow, onChange: setHighlightColor }),
772
+ /* @__PURE__ */ jsx2("div", { className: "msr:col-span-2", children: /* @__PURE__ */ jsx2(SettingsSwitch, { label: "Hover", checked: hoverHighlight, onChange: setHoverHighlight }) }),
773
+ /* @__PURE__ */ jsx2("div", { className: "msr:col-span-2", children: /* @__PURE__ */ jsx2(SettingsSwitch, { label: "Element snap", checked: snapEnabled, onChange: setSnapEnabled }) }),
774
+ /* @__PURE__ */ jsx2("div", { className: "msr:col-span-2", children: /* @__PURE__ */ jsx2(SettingsSwitch, { label: "Stack", checked: multiMeasureEnabled, onChange: setMultiMeasureEnabled }) })
775
+ ] }) : null,
776
+ activeTab === "color-picker" ? /* @__PURE__ */ jsxs2("section", { className: "msr:grid msr:grid-cols-[78px_156px] msr:items-center msr:gap-x-3 msr:gap-y-1", "aria-label": "Color settings", children: [
777
+ /* @__PURE__ */ jsxs2("div", { className: "msr:col-span-2 msr:grid msr:grid-cols-[78px_156px] msr:items-center msr:gap-3", children: [
778
+ /* @__PURE__ */ jsx2("span", { className: "msr:text-[12px] msr:text-ink-700", children: "Format" }),
779
+ /* @__PURE__ */ jsx2("div", { className: "msr:flex msr:min-w-0 msr:gap-1", children: COLOR_FORMATS.map((format) => /* @__PURE__ */ jsx2(
780
+ "button",
781
+ {
782
+ type: "button",
783
+ "aria-pressed": colorFormats.includes(format),
784
+ className: cn(
785
+ "msr:h-6 msr:min-w-0 msr:flex-1 msr:rounded-[5px] msr:border msr:px-1 msr:text-[11px] msr:focus-visible:outline-none msr:focus-visible:shadow-[inset_0_0_0_1px_#0d99ff]",
786
+ colorFormats.includes(format) ? "msr:border-[#0d99ff] msr:bg-[#0d99ff] msr:text-white" : "msr:border-ink-200 msr:text-ink-500 msr:hover:bg-ink-50"
787
+ ),
788
+ onClick: () => toggleFormat(format),
789
+ children: format
790
+ },
791
+ format
792
+ )) })
793
+ ] }),
794
+ /* @__PURE__ */ jsxs2("label", { className: "msr:col-span-2 msr:flex msr:items-center msr:justify-between msr:gap-3 msr:text-[12px] msr:text-ink-700", children: [
795
+ "Copy",
796
+ /* @__PURE__ */ jsx2("select", { value: colorClickFormat, className: "msr:rounded-[5px] msr:border msr:border-ink-200 msr:bg-white msr:px-1.5 msr:py-1 msr:text-[11px] msr:outline-none msr:focus:shadow-[inset_0_0_0_1px_#0d99ff]", onChange: (event) => setColorClickFormat(event.target.value), children: COLOR_FORMATS.map((format) => /* @__PURE__ */ jsx2("option", { value: format, children: format }, format)) })
797
+ ] })
798
+ ] }) : null,
799
+ activeTab === "rulers" ? /* @__PURE__ */ jsxs2("section", { className: "msr:grid msr:grid-cols-[78px_156px] msr:items-center msr:gap-x-3 msr:gap-y-1", "aria-label": "Ruler settings", children: [
800
+ /* @__PURE__ */ jsx2(SliderControl, { label: "Opacity", min: 0.2, max: 1, step: 0.05, value: rulerSettings.opacity, formatValue: (value) => `${Math.round(value * 100)}%`, parseInput: (input) => Number.parseFloat(input) / 100, onChange: (value) => setRulerSettings((settings) => ({ ...settings, opacity: value })) }),
801
+ /* @__PURE__ */ jsx2("div", { className: "msr:col-span-2", children: /* @__PURE__ */ jsx2(SettingsSwitch, { label: "Edge reveal", checked: rulerSettings.edgeReveal, onChange: (edgeReveal) => setRulerSettings((settings) => ({ ...settings, edgeReveal })) }) })
802
+ ] }) : null,
803
+ activeTab === "general" ? /* @__PURE__ */ jsxs2("section", { className: "msr:grid msr:grid-cols-[78px_156px] msr:items-center msr:gap-x-3 msr:gap-y-1", "aria-label": "General settings", children: [
804
+ /* @__PURE__ */ jsx2("div", { className: "msr:col-span-2", children: /* @__PURE__ */ jsx2(SettingsSwitch, { label: "Persist", checked: persistOnReload, onChange: setPersistOnReload }) }),
805
+ /* @__PURE__ */ jsxs2("div", { className: "msr:col-span-2 msr:grid msr:h-6 msr:grid-cols-[78px_156px] msr:items-center msr:gap-3 msr:text-[12px] msr:text-ink-700", children: [
806
+ /* @__PURE__ */ jsx2("span", { children: "Version" }),
807
+ /* @__PURE__ */ jsx2("span", { className: "msr:justify-self-end msr:font-mono msr:text-[11px] msr:tabular-nums msr:text-ink-700", children: package_default.version })
808
+ ] }),
809
+ /* @__PURE__ */ jsxs2("div", { className: "msr:col-span-2 msr:flex msr:justify-end msr:gap-1", children: [
810
+ /* @__PURE__ */ jsx2(
811
+ "button",
812
+ {
813
+ type: "button",
814
+ "aria-label": "Reset settings to defaults",
815
+ className: "msr:rounded-[5px] msr:border msr:border-ink-200 msr:px-2 msr:py-1 msr:text-[11px] msr:text-ink-700 msr:hover:bg-ink-50 msr:focus-visible:outline-none msr:focus-visible:shadow-[inset_0_0_0_1px_#0d99ff]",
816
+ onClick: onResetSettings,
817
+ children: "Use defaults"
818
+ }
819
+ ),
820
+ /* @__PURE__ */ jsx2(
821
+ "button",
822
+ {
823
+ type: "button",
824
+ "aria-label": "Clear workspace",
825
+ className: "msr:rounded-[5px] msr:border msr:border-red-200 msr:px-2 msr:py-1 msr:text-[11px] msr:text-red-600 msr:hover:bg-red-50 msr:focus-visible:outline-none msr:focus-visible:shadow-[inset_0_0_0_1px_#ef4444]",
826
+ onClick: onClearWorkspace,
827
+ children: "Clear workspace"
828
+ }
829
+ )
830
+ ] })
831
+ ] }) : null
832
+ ] });
833
+ }
834
+
73
835
  // components/icons.tsx
74
- import { jsx } from "react/jsx-runtime";
75
- var IconBase = ({ size = 24, ...props }) => /* @__PURE__ */ jsx(
836
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
837
+ var IconBase = ({ size = 24, ...props }) => /* @__PURE__ */ jsx3(
76
838
  "svg",
77
839
  {
78
840
  xmlns: "http://www.w3.org/2000/svg",
@@ -80,12 +842,14 @@ var IconBase = ({ size = 24, ...props }) => /* @__PURE__ */ jsx(
80
842
  height: size,
81
843
  viewBox: "0 0 256 256",
82
844
  fill: "currentColor",
845
+ "aria-hidden": "true",
846
+ onDragStart: (event) => event.preventDefault(),
83
847
  ...props
84
848
  }
85
849
  );
86
- var CursorIcon = ({ size = 20, ...props }) => /* @__PURE__ */ jsx(IconBase, { size, ...props, children: /* @__PURE__ */ 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" }) });
87
- var RulerIcon = ({ size = 20, ...props }) => /* @__PURE__ */ jsx(IconBase, { size, ...props, children: /* @__PURE__ */ 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" }) });
88
- var XrayIcon = ({ size = 20, ...props }) => /* @__PURE__ */ jsx(IconBase, { size, ...props, children: /* @__PURE__ */ jsx(
850
+ var CursorIcon = ({ size = 20, ...props }) => /* @__PURE__ */ jsx3(IconBase, { size, ...props, children: /* @__PURE__ */ jsx3("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" }) });
851
+ var RulerIcon = ({ size = 20, ...props }) => /* @__PURE__ */ jsx3(IconBase, { size, ...props, children: /* @__PURE__ */ jsx3("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" }) });
852
+ var XrayIcon = ({ size = 20, ...props }) => /* @__PURE__ */ jsx3(IconBase, { size, ...props, children: /* @__PURE__ */ jsx3(
89
853
  "path",
90
854
  {
91
855
  fill: "none",
@@ -97,7 +861,7 @@ var XrayIcon = ({ size = 20, ...props }) => /* @__PURE__ */ jsx(IconBase, { size
97
861
  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"
98
862
  }
99
863
  ) });
100
- var RulersIcon = ({ size = 20, ...props }) => /* @__PURE__ */ jsx(IconBase, { size, ...props, children: /* @__PURE__ */ jsx(
864
+ var RulersIcon = ({ size = 20, ...props }) => /* @__PURE__ */ jsx3(IconBase, { size, ...props, children: /* @__PURE__ */ jsx3(
101
865
  "path",
102
866
  {
103
867
  fill: "none",
@@ -108,10 +872,10 @@ var RulersIcon = ({ size = 20, ...props }) => /* @__PURE__ */ jsx(IconBase, { si
108
872
  d: "M48 208V48h160M80 48v24m32-24v24m32-24v24m32-24v24M48 80h24M48 112h24m-24 32h24m-24 32h24"
109
873
  }
110
874
  ) });
111
- var CaretDownIcon = ({ size = 8, ...props }) => /* @__PURE__ */ jsx(IconBase, { size, ...props, children: /* @__PURE__ */ 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" }) });
112
- var CheckIcon = ({ size = 12, ...props }) => /* @__PURE__ */ jsx(IconBase, { size, ...props, children: /* @__PURE__ */ 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" }) });
113
- var MinusIcon = ({ size = 12, ...props }) => /* @__PURE__ */ jsx(IconBase, { size, ...props, children: /* @__PURE__ */ jsx("path", { d: "M228,128a12,12,0,0,1-12,12H40a12,12,0,0,1,0-24H216A12,12,0,0,1,228,128Z" }) });
114
- var TextInspectorIcon = ({ size = 20, ...props }) => /* @__PURE__ */ jsx(
875
+ var CaretDownIcon = ({ size = 8, ...props }) => /* @__PURE__ */ jsx3(IconBase, { size, ...props, children: /* @__PURE__ */ jsx3("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" }) });
876
+ var CheckIcon = ({ size = 12, ...props }) => /* @__PURE__ */ jsx3(IconBase, { size, ...props, children: /* @__PURE__ */ jsx3("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" }) });
877
+ var MinusIcon = ({ size = 12, ...props }) => /* @__PURE__ */ jsx3(IconBase, { size, ...props, children: /* @__PURE__ */ jsx3("path", { d: "M228,128a12,12,0,0,1-12,12H40a12,12,0,0,1,0-24H216A12,12,0,0,1,228,128Z" }) });
878
+ var TextInspectorIcon = ({ size = 20, ...props }) => /* @__PURE__ */ jsx3(
115
879
  "svg",
116
880
  {
117
881
  xmlns: "http://www.w3.org/2000/svg",
@@ -119,8 +883,10 @@ var TextInspectorIcon = ({ size = 20, ...props }) => /* @__PURE__ */ jsx(
119
883
  height: size,
120
884
  viewBox: "0 0 256 256",
121
885
  fill: "currentColor",
886
+ "aria-hidden": "true",
887
+ onDragStart: (event) => event.preventDefault(),
122
888
  ...props,
123
- children: /* @__PURE__ */ jsx(
889
+ children: /* @__PURE__ */ jsx3(
124
890
  "text",
125
891
  {
126
892
  x: "50%",
@@ -136,9 +902,54 @@ var TextInspectorIcon = ({ size = 20, ...props }) => /* @__PURE__ */ jsx(
136
902
  )
137
903
  }
138
904
  );
905
+ var ColorPickerIcon = ({ size = 20, ...props }) => /* @__PURE__ */ jsx3(
906
+ "svg",
907
+ {
908
+ xmlns: "http://www.w3.org/2000/svg",
909
+ width: size,
910
+ height: size,
911
+ viewBox: "0 0 24 24",
912
+ fill: "none",
913
+ ...props,
914
+ children: /* @__PURE__ */ jsx3(
915
+ "path",
916
+ {
917
+ fill: "currentColor",
918
+ fillRule: "evenodd",
919
+ transform: "translate(-1.44 -1.44) scale(1.12)",
920
+ d: "M15.16 5.658a2.25 2.25 0 0 1 3.18.001l.155.17a2.25 2.25 0 0 1 0 2.84l-.154.172-1.696 1.692a1.5 1.5 0 0 1 .02 1.913l-.104.114a1.5 1.5 0 0 1-2.007.103l-.02-.018-4.443 4.447a2.24 2.24 0 0 1-1.716.65l-.814.815a1.5 1.5 0 0 1-2.121-2.121l.816-.818a2.25 2.25 0 0 1 .653-1.708l4.443-4.446a1.5 1.5 0 0 1 .088-2.025l.114-.103a1.5 1.5 0 0 1 1.91.015zm-7.544 8.959a1.25 1.25 0 0 0-.358 1.021c.021.197-.014.406-.154.546l-.958.96a.5.5 0 0 0 .708.706l.955-.956c.14-.14.352-.176.55-.153.364.042.745-.077 1.025-.356l4.438-4.442-1.767-1.767zm10.018-8.251a1.25 1.25 0 0 0-1.768 0l-1.782 1.78-.065.06a.87.87 0 0 1-1.165-.06.5.5 0 0 0-.707.707l3 3a.5.5 0 0 0 .628.064l.079-.064a.5.5 0 0 0 0-.707l-.004-.004a.873.873 0 0 1 .004-1.23l1.78-1.778a1.25 1.25 0 0 0 0-1.768",
921
+ clipRule: "evenodd"
922
+ }
923
+ )
924
+ }
925
+ );
926
+ var GearIcon = ({ size = 20, ...props }) => /* @__PURE__ */ jsxs3(
927
+ "svg",
928
+ {
929
+ xmlns: "http://www.w3.org/2000/svg",
930
+ width: size,
931
+ height: size,
932
+ viewBox: "0 0 24 24",
933
+ fill: "none",
934
+ ...props,
935
+ children: [
936
+ /* @__PURE__ */ jsx3(
937
+ "path",
938
+ {
939
+ d: "M10.6504 5.81117C10.9939 4.39628 13.0061 4.39628 13.3496 5.81117C13.5715 6.72517 14.6187 7.15891 15.4219 6.66952C16.6652 5.91193 18.0881 7.33479 17.3305 8.57815C16.8411 9.38134 17.2748 10.4285 18.1888 10.6504C19.6037 10.9939 19.6037 13.0061 18.1888 13.3496C17.2748 13.5715 16.8411 14.6187 17.3305 15.4219C18.0881 16.6652 16.6652 18.0881 15.4219 17.3305C14.6187 16.8411 13.5715 17.2748 13.3496 18.1888C13.0061 19.6037 10.9939 19.6037 10.6504 18.1888C10.4285 17.2748 9.38135 16.8411 8.57815 17.3305C7.33479 18.0881 5.91193 16.6652 6.66952 15.4219C7.15891 14.6187 6.72517 13.5715 5.81117 13.3496C4.39628 13.0061 4.39628 10.9939 5.81117 10.6504C6.72517 10.4285 7.15891 9.38134 6.66952 8.57815C5.91193 7.33479 7.33479 5.91192 8.57815 6.66952C9.38135 7.15891 10.4285 6.72517 10.6504 5.81117Z",
940
+ stroke: "currentColor",
941
+ strokeWidth: "1",
942
+ strokeLinecap: "round",
943
+ strokeLinejoin: "round"
944
+ }
945
+ ),
946
+ /* @__PURE__ */ jsx3("circle", { cx: "12", cy: "12", r: "2.5", stroke: "currentColor", strokeWidth: "1" })
947
+ ]
948
+ }
949
+ );
139
950
 
140
951
  // components/toolbar.tsx
141
- import { jsx as jsx2, jsxs } from "react/jsx-runtime";
952
+ import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
142
953
  var TOOLBAR_TOOLTIP_DELAY_MS = 800;
143
954
  var TOOLBAR_DRAG_SLOP = 6;
144
955
  var GUIDE_MENU_WIDTH = 176;
@@ -156,61 +967,43 @@ function ToolbarButton({
156
967
  onTooltipLeave,
157
968
  children
158
969
  }) {
159
- return /* @__PURE__ */ jsxs(
970
+ return /* @__PURE__ */ jsxs4(
160
971
  "div",
161
972
  {
162
973
  className: "msr:relative",
163
974
  onMouseEnter: () => onTooltipEnter(id),
164
975
  onMouseLeave: () => onTooltipLeave(id),
165
976
  children: [
166
- /* @__PURE__ */ jsx2(
977
+ /* @__PURE__ */ jsx4(
167
978
  "button",
168
979
  {
169
980
  type: "button",
170
- "aria-label": `${label} (${shortcut})`,
171
981
  "aria-pressed": active,
172
- title: `${label} (${shortcut})`,
982
+ "aria-label": `${label} (${shortcut})`,
173
983
  className: cn(
174
- "msr:flex msr:size-8 msr:items-center msr:justify-center msr:rounded-[8px] msr:outline-none",
984
+ "msr:flex msr:size-8 msr:select-none msr:items-center msr:justify-center msr:rounded-[8px] msr:outline-none",
175
985
  active ? "msr:bg-[#0d99ff] msr:text-white" : "msr:bg-transparent msr:text-black msr:hover:bg-black/4"
176
986
  ),
177
987
  onClick,
178
988
  children
179
989
  }
180
990
  ),
181
- /* @__PURE__ */ jsxs(
182
- "span",
183
- {
184
- className: cn(
185
- cn(
186
- "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",
187
- tooltipInstant && "msr:transition-none"
188
- ),
189
- tooltipSide === "top" ? "msr:bottom-full msr:mb-2" : "msr:top-full msr:mt-2",
190
- tooltipVisible ? "msr:opacity-100" : "msr:opacity-0"
191
- ),
192
- children: [
193
- label,
194
- " ",
195
- /* @__PURE__ */ jsx2("kbd", { className: "msr:text-white/60", children: shortcut })
196
- ]
197
- }
198
- )
991
+ /* @__PURE__ */ jsx4(Tooltip, { label, shortcut, visible: tooltipVisible, instant: tooltipInstant, side: tooltipSide })
199
992
  ]
200
993
  }
201
994
  );
202
995
  }
203
996
  function useToolbarTooltip() {
204
- const [visibleTooltipId, setVisibleTooltipId] = useState(null);
205
- const timerRef = useRef(null);
206
- const instantRef = useRef(false);
207
- const [tooltipInstant, setTooltipInstant] = useState(false);
208
- const clearTimer = useCallback(() => {
997
+ const [visibleTooltipId, setVisibleTooltipId] = useState3(null);
998
+ const timerRef = useRef2(null);
999
+ const instantRef = useRef2(false);
1000
+ const [tooltipInstant, setTooltipInstant] = useState3(false);
1001
+ const clearTimer = useCallback2(() => {
209
1002
  if (timerRef.current === null) return;
210
1003
  window.clearTimeout(timerRef.current);
211
1004
  timerRef.current = null;
212
1005
  }, []);
213
- const onTooltipEnter = useCallback(
1006
+ const onTooltipEnter = useCallback2(
214
1007
  (id) => {
215
1008
  clearTimer();
216
1009
  if (instantRef.current) {
@@ -227,14 +1020,14 @@ function useToolbarTooltip() {
227
1020
  },
228
1021
  [clearTimer]
229
1022
  );
230
- const onTooltipLeave = useCallback(
1023
+ const onTooltipLeave = useCallback2(
231
1024
  (id) => {
232
1025
  clearTimer();
233
1026
  setVisibleTooltipId((prev) => prev === id ? null : prev);
234
1027
  },
235
1028
  [clearTimer]
236
1029
  );
237
- const onToolbarLeave = useCallback(() => {
1030
+ const onToolbarLeave = useCallback2(() => {
238
1031
  clearTimer();
239
1032
  setVisibleTooltipId(null);
240
1033
  instantRef.current = false;
@@ -249,10 +1042,11 @@ function useToolbarTooltip() {
249
1042
  };
250
1043
  }
251
1044
  function useToolbarDrag(initialPosition, eventTarget) {
252
- const [position, setPosition] = useState(initialPosition);
253
- const suppressClickRef = useRef(false);
254
- const detachListenersRef = useRef(null);
255
- const dragRef = useRef({
1045
+ const [position, setPosition] = useState3(initialPosition);
1046
+ const suppressClickRef = useRef2(false);
1047
+ const previousUserSelectRef = useRef2(null);
1048
+ const detachListenersRef = useRef2(null);
1049
+ const dragRef = useRef2({
256
1050
  active: false,
257
1051
  didDrag: false,
258
1052
  pointerId: -1,
@@ -263,12 +1057,28 @@ function useToolbarDrag(initialPosition, eventTarget) {
263
1057
  width: 0,
264
1058
  height: 0
265
1059
  });
266
- const onPointerDown = useCallback(
1060
+ const disableTextSelection = useCallback2(() => {
1061
+ if (previousUserSelectRef.current !== null) return;
1062
+ const root = eventTarget.document.documentElement;
1063
+ previousUserSelectRef.current = root.style.userSelect;
1064
+ root.style.setProperty("user-select", "none", "important");
1065
+ }, [eventTarget]);
1066
+ const restoreTextSelection = useCallback2(() => {
1067
+ const previous = previousUserSelectRef.current;
1068
+ if (previous === null) return;
1069
+ const root = eventTarget.document.documentElement;
1070
+ root.style.userSelect = previous;
1071
+ previousUserSelectRef.current = null;
1072
+ }, [eventTarget]);
1073
+ useEffect(() => restoreTextSelection, [restoreTextSelection]);
1074
+ const onPointerDown = useCallback2(
267
1075
  (event) => {
268
1076
  if (event.button !== 0) return;
1077
+ disableTextSelection();
269
1078
  if (detachListenersRef.current) {
270
1079
  detachListenersRef.current();
271
1080
  detachListenersRef.current = null;
1081
+ restoreTextSelection();
272
1082
  }
273
1083
  const state = dragRef.current;
274
1084
  state.active = false;
@@ -303,6 +1113,7 @@ function useToolbarDrag(initialPosition, eventTarget) {
303
1113
  if (current.pointerId !== endEvent.pointerId && current.pointerId !== -1)
304
1114
  return;
305
1115
  suppressClickRef.current = current.didDrag;
1116
+ restoreTextSelection();
306
1117
  current.active = false;
307
1118
  current.didDrag = false;
308
1119
  current.pointerId = -1;
@@ -320,9 +1131,9 @@ function useToolbarDrag(initialPosition, eventTarget) {
320
1131
  eventTarget.removeEventListener("pointercancel", handlePointerEnd);
321
1132
  };
322
1133
  },
323
- [eventTarget, position.x, position.y]
1134
+ [disableTextSelection, eventTarget, position.x, position.y, restoreTextSelection]
324
1135
  );
325
- const onClickCapture = useCallback(
1136
+ const onClickCapture = useCallback2(
326
1137
  (event) => {
327
1138
  if (!suppressClickRef.current) return;
328
1139
  event.preventDefault();
@@ -337,12 +1148,48 @@ function ToolbarComponent({
337
1148
  toolMode,
338
1149
  setEnabled,
339
1150
  setToolMode,
1151
+ xrayVisible,
1152
+ setXrayVisible,
340
1153
  rulersVisible,
341
1154
  setRulersVisible,
342
1155
  guideOrientation,
343
1156
  setGuideOrientation,
344
1157
  onInteract,
345
- eventTarget
1158
+ eventTarget,
1159
+ colorPickerActive,
1160
+ setColorPickerActive,
1161
+ onColorPickerClick,
1162
+ settingsOpen,
1163
+ setSettingsOpen,
1164
+ highlightColor,
1165
+ setHighlightColor,
1166
+ guideColor,
1167
+ setGuideColor,
1168
+ hoverHighlight,
1169
+ setHoverHighlight,
1170
+ persistOnReload,
1171
+ setPersistOnReload,
1172
+ colorPickerFormats,
1173
+ setColorPickerFormats,
1174
+ colorPickerClickFormat,
1175
+ setColorPickerClickFormat,
1176
+ snapEnabled,
1177
+ setSnapEnabled,
1178
+ snapGuidesEnabled,
1179
+ setSnapGuidesEnabled,
1180
+ selectNewGuideEnabled,
1181
+ setSelectNewGuideEnabled,
1182
+ multiMeasureEnabled,
1183
+ setMultiMeasureEnabled,
1184
+ guideStyle,
1185
+ setGuideStyle,
1186
+ rulerSettings,
1187
+ setRulerSettings,
1188
+ settingsTab,
1189
+ setSettingsTab,
1190
+ onToggleSettings,
1191
+ onResetSettings,
1192
+ onClearWorkspace
346
1193
  }, ref) {
347
1194
  const { position, onPointerDown, onClickCapture } = useToolbarDrag({
348
1195
  x: 16,
@@ -355,11 +1202,13 @@ function ToolbarComponent({
355
1202
  onTooltipLeave,
356
1203
  onToolbarLeave
357
1204
  } = useToolbarTooltip();
358
- const [guideMenuOpen, setGuideMenuOpen] = useState(false);
359
- const guideMenuRef = useRef(null);
360
- const [activeMenuIndex, setActiveMenuIndex] = useState(0);
361
- const [menuAlign, setMenuAlign] = useState("right");
362
- const updateMenuAlign = useCallback(() => {
1205
+ const [guideMenuOpen, setGuideMenuOpen] = useState3(false);
1206
+ const settingsRef = useRef2(null);
1207
+ const guideMenuRef = useRef2(null);
1208
+ const [activeMenuIndex, setActiveMenuIndex] = useState3(0);
1209
+ const [menuAlign, setMenuAlign] = useState3("right");
1210
+ const tooltipsEnabled = !guideMenuOpen && !settingsOpen;
1211
+ const updateMenuAlign = useCallback2(() => {
363
1212
  const anchorRect = guideMenuRef.current?.getBoundingClientRect();
364
1213
  if (!anchorRect) return;
365
1214
  const rightAlignedLeft = anchorRect.right - GUIDE_MENU_WIDTH;
@@ -379,34 +1228,50 @@ function ToolbarComponent({
379
1228
  const nearBottom = viewportHeight > 0 && position.y > viewportHeight - 56;
380
1229
  const tooltipSide = nearTop && !nearBottom ? "bottom" : "top";
381
1230
  const menuSide = nearBottom ? "top" : "bottom";
382
- const selectMode = useCallback(() => {
1231
+ const selectMode = useCallback2(() => {
383
1232
  setEnabled(true);
1233
+ setColorPickerActive(false);
384
1234
  setToolMode((prev) => prev === "select" ? "none" : "select");
385
1235
  onInteract();
386
- }, [onInteract, setEnabled, setToolMode]);
387
- const guidesMode = useCallback(() => {
1236
+ }, [onInteract, setColorPickerActive, setEnabled, setToolMode]);
1237
+ const guidesMode = useCallback2(() => {
388
1238
  setEnabled(true);
1239
+ setColorPickerActive(false);
389
1240
  setToolMode((prev) => prev === "guides" ? "none" : "guides");
390
1241
  onInteract();
391
- }, [onInteract, setEnabled, setToolMode]);
392
- const textInspectorMode = useCallback(() => {
1242
+ }, [onInteract, setColorPickerActive, setEnabled, setToolMode]);
1243
+ const textInspectorMode = useCallback2(() => {
393
1244
  setEnabled(true);
1245
+ setColorPickerActive(false);
394
1246
  setToolMode(
395
1247
  (prev) => prev === "text-inspector" ? "none" : "text-inspector"
396
1248
  );
397
1249
  onInteract();
398
- }, [onInteract, setEnabled, setToolMode]);
399
- const xrayMode = useCallback(() => {
1250
+ }, [onInteract, setColorPickerActive, setEnabled, setToolMode]);
1251
+ const xrayMode = useCallback2(() => {
400
1252
  setEnabled(true);
401
- setToolMode((prev) => prev === "xray" ? "none" : "xray");
1253
+ setColorPickerActive(false);
1254
+ setXrayVisible((prev) => !prev);
402
1255
  onInteract();
403
- }, [onInteract, setEnabled, setToolMode]);
404
- const rulersMode = useCallback(() => {
1256
+ }, [onInteract, setColorPickerActive, setEnabled, setXrayVisible]);
1257
+ const colorPickerMode = useCallback2(() => {
405
1258
  setEnabled(true);
1259
+ setToolMode("none");
1260
+ if (colorPickerActive) {
1261
+ setColorPickerActive(false);
1262
+ } else {
1263
+ setColorPickerActive(true);
1264
+ onColorPickerClick();
1265
+ }
1266
+ onInteract();
1267
+ }, [colorPickerActive, onColorPickerClick, onInteract, setColorPickerActive, setEnabled, setToolMode]);
1268
+ const rulersMode = useCallback2(() => {
1269
+ setEnabled(true);
1270
+ setColorPickerActive(false);
406
1271
  setRulersVisible((prev) => !prev);
407
1272
  onInteract();
408
- }, [onInteract, setEnabled, setRulersVisible]);
409
- const selectGuideOrientation = useCallback(
1273
+ }, [onInteract, setColorPickerActive, setEnabled, setRulersVisible]);
1274
+ const selectGuideOrientation = useCallback2(
410
1275
  (orientation) => {
411
1276
  setEnabled(true);
412
1277
  setToolMode("guides");
@@ -417,29 +1282,33 @@ function ToolbarComponent({
417
1282
  [onInteract, setEnabled, setGuideOrientation, setToolMode]
418
1283
  );
419
1284
  useLayoutEffect(() => {
420
- if (!guideMenuOpen) return;
421
- const frame = eventTarget.requestAnimationFrame(() => {
1285
+ if (!guideMenuOpen && !settingsOpen) return;
1286
+ const frame = guideMenuOpen ? eventTarget.requestAnimationFrame(() => {
422
1287
  guideMenuRef.current?.querySelector("[role='menu']")?.focus();
423
- });
1288
+ }) : 0;
424
1289
  const handlePointerDown = (event) => {
425
- const menu = guideMenuRef.current;
426
- if (!menu) return;
427
1290
  const path = event.composedPath();
428
- if (path.includes(menu)) return;
429
- setGuideMenuOpen(false);
1291
+ if (guideMenuOpen) {
1292
+ const menu = guideMenuRef.current;
1293
+ if (menu && !path.includes(menu)) setGuideMenuOpen(false);
1294
+ }
1295
+ if (settingsOpen) {
1296
+ const settings = settingsRef.current;
1297
+ if (settings && !path.includes(settings)) setSettingsOpen(false);
1298
+ }
430
1299
  };
431
1300
  const handleResize = () => {
432
- updateMenuAlign();
1301
+ if (guideMenuOpen) updateMenuAlign();
433
1302
  };
434
1303
  eventTarget.addEventListener("pointerdown", handlePointerDown);
435
- eventTarget.addEventListener("resize", handleResize);
1304
+ if (guideMenuOpen) eventTarget.addEventListener("resize", handleResize);
436
1305
  return () => {
437
- eventTarget.cancelAnimationFrame(frame);
1306
+ if (frame) eventTarget.cancelAnimationFrame(frame);
438
1307
  eventTarget.removeEventListener("pointerdown", handlePointerDown);
439
1308
  eventTarget.removeEventListener("resize", handleResize);
440
1309
  };
441
- }, [eventTarget, guideMenuOpen, guideOrientation, updateMenuAlign]);
442
- return /* @__PURE__ */ jsxs(
1310
+ }, [eventTarget, guideMenuOpen, guideOrientation, settingsOpen, updateMenuAlign]);
1311
+ return /* @__PURE__ */ jsxs4(
443
1312
  "div",
444
1313
  {
445
1314
  ref,
@@ -452,7 +1321,7 @@ function ToolbarComponent({
452
1321
  onClickCapture,
453
1322
  onMouseLeave: onToolbarLeave,
454
1323
  children: [
455
- /* @__PURE__ */ jsx2(
1324
+ /* @__PURE__ */ jsx4(
456
1325
  ToolbarButton,
457
1326
  {
458
1327
  id: "select",
@@ -460,31 +1329,47 @@ function ToolbarComponent({
460
1329
  label: "Select",
461
1330
  shortcut: "S",
462
1331
  onClick: selectMode,
463
- tooltipVisible: visibleTooltipId === "select",
1332
+ tooltipVisible: tooltipsEnabled && visibleTooltipId === "select",
464
1333
  tooltipInstant,
465
1334
  tooltipSide,
466
1335
  onTooltipEnter,
467
1336
  onTooltipLeave,
468
- children: /* @__PURE__ */ jsx2(CursorIcon, { size: 20 })
1337
+ children: /* @__PURE__ */ jsx4(CursorIcon, { size: 20 })
469
1338
  }
470
1339
  ),
471
- /* @__PURE__ */ jsx2(
1340
+ /* @__PURE__ */ jsx4(
472
1341
  ToolbarButton,
473
1342
  {
474
1343
  id: "xray",
475
- active: toolMode === "xray",
1344
+ active: xrayVisible,
476
1345
  label: "X-ray",
477
1346
  shortcut: "X",
478
1347
  onClick: xrayMode,
479
- tooltipVisible: visibleTooltipId === "xray",
1348
+ tooltipVisible: tooltipsEnabled && visibleTooltipId === "xray",
480
1349
  tooltipInstant,
481
1350
  tooltipSide,
482
1351
  onTooltipEnter,
483
1352
  onTooltipLeave,
484
- children: /* @__PURE__ */ jsx2(XrayIcon, { size: 20 })
1353
+ children: /* @__PURE__ */ jsx4(XrayIcon, { size: 20 })
485
1354
  }
486
1355
  ),
487
- /* @__PURE__ */ jsx2(
1356
+ /* @__PURE__ */ jsx4(
1357
+ ToolbarButton,
1358
+ {
1359
+ id: "color-picker",
1360
+ active: colorPickerActive,
1361
+ label: "Color picker",
1362
+ shortcut: "P",
1363
+ onClick: colorPickerMode,
1364
+ tooltipVisible: tooltipsEnabled && visibleTooltipId === "color-picker",
1365
+ tooltipInstant,
1366
+ tooltipSide,
1367
+ onTooltipEnter,
1368
+ onTooltipLeave,
1369
+ children: /* @__PURE__ */ jsx4(ColorPickerIcon, { size: 20, "aria-hidden": "true" })
1370
+ }
1371
+ ),
1372
+ /* @__PURE__ */ jsx4(
488
1373
  ToolbarButton,
489
1374
  {
490
1375
  id: "rulers",
@@ -492,15 +1377,15 @@ function ToolbarComponent({
492
1377
  label: "Rulers",
493
1378
  shortcut: "R",
494
1379
  onClick: rulersMode,
495
- tooltipVisible: visibleTooltipId === "rulers",
1380
+ tooltipVisible: tooltipsEnabled && visibleTooltipId === "rulers",
496
1381
  tooltipInstant,
497
1382
  tooltipSide,
498
1383
  onTooltipEnter,
499
1384
  onTooltipLeave,
500
- children: /* @__PURE__ */ jsx2(RulersIcon, { size: 20 })
1385
+ children: /* @__PURE__ */ jsx4(RulersIcon, { size: 20 })
501
1386
  }
502
1387
  ),
503
- /* @__PURE__ */ jsx2(
1388
+ /* @__PURE__ */ jsx4(
504
1389
  ToolbarButton,
505
1390
  {
506
1391
  id: "text-inspector",
@@ -508,15 +1393,15 @@ function ToolbarComponent({
508
1393
  label: "Text inspector",
509
1394
  shortcut: "A",
510
1395
  onClick: textInspectorMode,
511
- tooltipVisible: visibleTooltipId === "text-inspector",
1396
+ tooltipVisible: tooltipsEnabled && visibleTooltipId === "text-inspector",
512
1397
  tooltipInstant,
513
1398
  tooltipSide,
514
1399
  onTooltipEnter,
515
1400
  onTooltipLeave,
516
- children: /* @__PURE__ */ jsx2(TextInspectorIcon, { size: 20, "aria-hidden": "true" })
1401
+ children: /* @__PURE__ */ jsx4(TextInspectorIcon, { size: 20, "aria-hidden": "true" })
517
1402
  }
518
1403
  ),
519
- /* @__PURE__ */ jsx2(
1404
+ /* @__PURE__ */ jsx4(
520
1405
  ToolbarButton,
521
1406
  {
522
1407
  id: "guides",
@@ -524,12 +1409,12 @@ function ToolbarComponent({
524
1409
  label: "Guides",
525
1410
  shortcut: "G",
526
1411
  onClick: guidesMode,
527
- tooltipVisible: visibleTooltipId === "guides",
1412
+ tooltipVisible: tooltipsEnabled && visibleTooltipId === "guides",
528
1413
  tooltipInstant,
529
1414
  tooltipSide,
530
1415
  onTooltipEnter,
531
1416
  onTooltipLeave,
532
- children: /* @__PURE__ */ jsx2(
1417
+ children: /* @__PURE__ */ jsx4(
533
1418
  RulerIcon,
534
1419
  {
535
1420
  size: 20,
@@ -540,7 +1425,7 @@ function ToolbarComponent({
540
1425
  )
541
1426
  }
542
1427
  ),
543
- /* @__PURE__ */ jsxs(
1428
+ /* @__PURE__ */ jsxs4(
544
1429
  "div",
545
1430
  {
546
1431
  className: "msr:group msr:relative msr:-ml-1 msr:flex msr:items-stretch",
@@ -548,12 +1433,11 @@ function ToolbarComponent({
548
1433
  onMouseEnter: () => onTooltipEnter("guide-menu"),
549
1434
  onMouseLeave: () => onTooltipLeave("guide-menu"),
550
1435
  children: [
551
- /* @__PURE__ */ jsx2(
1436
+ /* @__PURE__ */ jsx4(
552
1437
  "button",
553
1438
  {
554
1439
  type: "button",
555
1440
  "aria-label": "Guide orientation menu",
556
- title: "Guide orientation",
557
1441
  className: cn(
558
1442
  "msr:flex msr:h-8 msr:w-4 msr:items-center msr:justify-center msr:rounded-[6px] msr:outline-none msr:hover:bg-black/10",
559
1443
  guideMenuOpen ? "msr:bg-black/10 msr:text-black" : "msr:text-black"
@@ -568,21 +1452,21 @@ function ToolbarComponent({
568
1452
  return !prev;
569
1453
  });
570
1454
  },
571
- children: /* @__PURE__ */ jsx2(CaretDownIcon, { size: 8 })
1455
+ children: /* @__PURE__ */ jsx4(CaretDownIcon, { size: 8 })
572
1456
  }
573
1457
  ),
574
- /* @__PURE__ */ jsx2(
1458
+ /* @__PURE__ */ jsx4(
575
1459
  "span",
576
1460
  {
577
1461
  className: cn(
578
1462
  "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",
579
1463
  tooltipSide === "top" ? "msr:bottom-full msr:mb-2" : "msr:top-full msr:mt-2",
580
- visibleTooltipId === "guide-menu" && !guideMenuOpen ? "msr:opacity-100" : "msr:opacity-0"
1464
+ visibleTooltipId === "guide-menu" && tooltipsEnabled ? "msr:opacity-100" : "msr:opacity-0"
581
1465
  ),
582
1466
  children: "Orientation Guide"
583
1467
  }
584
1468
  ),
585
- guideMenuOpen ? /* @__PURE__ */ jsxs(
1469
+ guideMenuOpen ? /* @__PURE__ */ jsxs4(
586
1470
  "div",
587
1471
  {
588
1472
  className: cn(
@@ -623,7 +1507,7 @@ function ToolbarComponent({
623
1507
  }
624
1508
  },
625
1509
  children: [
626
- /* @__PURE__ */ jsxs(
1510
+ /* @__PURE__ */ jsxs4(
627
1511
  "button",
628
1512
  {
629
1513
  type: "button",
@@ -633,7 +1517,7 @@ function ToolbarComponent({
633
1517
  ),
634
1518
  onClick: () => selectGuideOrientation("horizontal"),
635
1519
  children: [
636
- /* @__PURE__ */ jsx2(
1520
+ /* @__PURE__ */ jsx4(
637
1521
  CheckIcon,
638
1522
  {
639
1523
  size: 12,
@@ -642,13 +1526,13 @@ function ToolbarComponent({
642
1526
  )
643
1527
  }
644
1528
  ),
645
- /* @__PURE__ */ jsx2(MinusIcon, { size: 12 }),
646
- /* @__PURE__ */ jsx2("span", { className: "msr:flex-1", children: "Horizontal" }),
647
- /* @__PURE__ */ jsx2("span", { children: "H" })
1529
+ /* @__PURE__ */ jsx4(MinusIcon, { size: 12 }),
1530
+ /* @__PURE__ */ jsx4("span", { className: "msr:flex-1", children: "Horizontal" }),
1531
+ /* @__PURE__ */ jsx4("span", { children: "H" })
648
1532
  ]
649
1533
  }
650
1534
  ),
651
- /* @__PURE__ */ jsxs(
1535
+ /* @__PURE__ */ jsxs4(
652
1536
  "button",
653
1537
  {
654
1538
  type: "button",
@@ -658,7 +1542,7 @@ function ToolbarComponent({
658
1542
  ),
659
1543
  onClick: () => selectGuideOrientation("vertical"),
660
1544
  children: [
661
- /* @__PURE__ */ jsx2(
1545
+ /* @__PURE__ */ jsx4(
662
1546
  CheckIcon,
663
1547
  {
664
1548
  size: 12,
@@ -667,9 +1551,9 @@ function ToolbarComponent({
667
1551
  )
668
1552
  }
669
1553
  ),
670
- /* @__PURE__ */ jsx2(MinusIcon, { size: 12, className: "msr:rotate-90" }),
671
- /* @__PURE__ */ jsx2("span", { className: "msr:flex-1", children: "Vertical" }),
672
- /* @__PURE__ */ jsx2("span", { children: "V" })
1554
+ /* @__PURE__ */ jsx4(MinusIcon, { size: 12, className: "msr:rotate-90" }),
1555
+ /* @__PURE__ */ jsx4("span", { className: "msr:flex-1", children: "Vertical" }),
1556
+ /* @__PURE__ */ jsx4("span", { children: "V" })
673
1557
  ]
674
1558
  }
675
1559
  )
@@ -678,34 +1562,329 @@ function ToolbarComponent({
678
1562
  ) : null
679
1563
  ]
680
1564
  }
1565
+ ),
1566
+ /* @__PURE__ */ jsxs4("div", { ref: settingsRef, className: "msr:relative msr:flex", children: [
1567
+ /* @__PURE__ */ jsx4(
1568
+ ToolbarButton,
1569
+ {
1570
+ id: "settings",
1571
+ active: settingsOpen,
1572
+ label: "Settings",
1573
+ shortcut: "\u2318/Ctrl+,",
1574
+ onClick: () => {
1575
+ onInteract();
1576
+ onToggleSettings();
1577
+ },
1578
+ tooltipVisible: tooltipsEnabled && visibleTooltipId === "settings",
1579
+ tooltipInstant,
1580
+ tooltipSide,
1581
+ onTooltipEnter,
1582
+ onTooltipLeave,
1583
+ children: /* @__PURE__ */ jsx4(GearIcon, { size: 20, "aria-hidden": "true" })
1584
+ }
1585
+ ),
1586
+ settingsOpen ? /* @__PURE__ */ jsx4(
1587
+ "div",
1588
+ {
1589
+ className: cn(
1590
+ "mesurer-menu-surface msr:absolute msr:-right-1 msr:z-[70] msr:box-border msr:w-[272px] msr:max-w-[calc(100vw-16px)] msr:rounded-lg msr:border msr:border-ink-200 msr:bg-white msr:p-3",
1591
+ menuSide === "bottom" ? "msr:top-full msr:mt-2" : "msr:bottom-full msr:mb-2"
1592
+ ),
1593
+ "data-mesurer-inspector-ui": "true",
1594
+ role: "dialog",
1595
+ "aria-label": "Settings",
1596
+ onPointerDown: (event) => event.stopPropagation(),
1597
+ onPointerMove: (event) => event.stopPropagation(),
1598
+ onPointerUp: (event) => event.stopPropagation(),
1599
+ onClick: (event) => event.stopPropagation(),
1600
+ children: /* @__PURE__ */ jsx4(
1601
+ SettingsPanel,
1602
+ {
1603
+ ownerWindow: eventTarget,
1604
+ highlightColor,
1605
+ setHighlightColor,
1606
+ guideColor,
1607
+ setGuideColor,
1608
+ hoverHighlight,
1609
+ setHoverHighlight,
1610
+ persistOnReload,
1611
+ setPersistOnReload,
1612
+ colorFormats: colorPickerFormats,
1613
+ setColorFormats: setColorPickerFormats,
1614
+ colorClickFormat: colorPickerClickFormat,
1615
+ setColorClickFormat: setColorPickerClickFormat,
1616
+ snapEnabled,
1617
+ setSnapEnabled,
1618
+ snapGuidesEnabled,
1619
+ setSnapGuidesEnabled,
1620
+ selectNewGuideEnabled,
1621
+ setSelectNewGuideEnabled,
1622
+ multiMeasureEnabled,
1623
+ setMultiMeasureEnabled,
1624
+ guideStyle,
1625
+ setGuideStyle,
1626
+ rulerSettings,
1627
+ setRulerSettings,
1628
+ activeTab: settingsTab,
1629
+ onTabChange: setSettingsTab,
1630
+ onResetSettings,
1631
+ onClearWorkspace
1632
+ }
1633
+ )
1634
+ }
1635
+ ) : null
1636
+ ] })
1637
+ ]
1638
+ }
1639
+ );
1640
+ }
1641
+ var Toolbar = memo(forwardRef(ToolbarComponent));
1642
+
1643
+ // components/color-picker.tsx
1644
+ import { useCallback as useCallback3, useLayoutEffect as useLayoutEffect2, useRef as useRef3, useState as useState4 } from "react";
1645
+ import { Fragment as Fragment3, jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
1646
+ function CopyableColorValue({
1647
+ id,
1648
+ value,
1649
+ copiedId,
1650
+ onCopy,
1651
+ onTooltipEnter,
1652
+ tooltip,
1653
+ className
1654
+ }) {
1655
+ const copied = copiedId === id;
1656
+ const showTooltip = tooltip.visibleTooltipId === id || copied && tooltip.visibleTooltipId === null;
1657
+ return /* @__PURE__ */ jsxs5(
1658
+ "span",
1659
+ {
1660
+ className: "msr:relative msr:inline-flex",
1661
+ onMouseLeave: tooltip.onTooltipLeave,
1662
+ children: [
1663
+ /* @__PURE__ */ jsx5(
1664
+ "button",
1665
+ {
1666
+ type: "button",
1667
+ className,
1668
+ onMouseEnter: () => onTooltipEnter(id),
1669
+ onFocus: () => onTooltipEnter(id),
1670
+ onBlur: tooltip.onTooltipLeave,
1671
+ onClick: onCopy,
1672
+ children: value
1673
+ }
1674
+ ),
1675
+ /* @__PURE__ */ jsx5(
1676
+ Tooltip,
1677
+ {
1678
+ label: copied ? "Copied!" : "Click to copy",
1679
+ visible: showTooltip,
1680
+ instant: copied || tooltip.tooltipInstant,
1681
+ side: "bottom",
1682
+ className: "msr:z-10"
1683
+ }
681
1684
  )
682
1685
  ]
683
1686
  }
684
1687
  );
685
1688
  }
686
- var Toolbar = memo(forwardRef(ToolbarComponent));
1689
+ function ColorPicker({
1690
+ active,
1691
+ sample,
1692
+ unsupported,
1693
+ formats,
1694
+ favoriteFormat,
1695
+ ownerWindow,
1696
+ toolbarRef,
1697
+ onClose
1698
+ }) {
1699
+ const panelRef = useRef3(null);
1700
+ const copyTimeoutRef = useRef3(null);
1701
+ const [copiedId, setCopiedId] = useState4(null);
1702
+ const tooltip = useTooltip();
1703
+ const copyValue = useCallback3(
1704
+ (id, value) => {
1705
+ const clipboardWrite = ownerWindow.navigator.clipboard?.writeText(value);
1706
+ void clipboardWrite?.catch(() => void 0);
1707
+ tooltip.onTooltipLeave();
1708
+ setCopiedId(id);
1709
+ if (copyTimeoutRef.current !== null) {
1710
+ ownerWindow.clearTimeout(copyTimeoutRef.current);
1711
+ }
1712
+ copyTimeoutRef.current = ownerWindow.setTimeout(() => {
1713
+ copyTimeoutRef.current = null;
1714
+ setCopiedId(null);
1715
+ }, 1500);
1716
+ },
1717
+ [ownerWindow, tooltip]
1718
+ );
1719
+ const handleTooltipEnter = useCallback3(
1720
+ (id) => {
1721
+ if (copiedId !== null && copiedId !== id) {
1722
+ if (copyTimeoutRef.current !== null) {
1723
+ ownerWindow.clearTimeout(copyTimeoutRef.current);
1724
+ copyTimeoutRef.current = null;
1725
+ }
1726
+ setCopiedId(null);
1727
+ }
1728
+ tooltip.onTooltipEnter(id);
1729
+ },
1730
+ [copiedId, ownerWindow, tooltip]
1731
+ );
1732
+ useLayoutEffect2(() => {
1733
+ if (!active) return;
1734
+ const panel = panelRef.current;
1735
+ const toolbar = toolbarRef.current;
1736
+ if (!panel || !toolbar) return;
1737
+ let frame = 0;
1738
+ const updatePosition = () => {
1739
+ const toolbarRect = toolbar.getBoundingClientRect();
1740
+ const panelRect = panel.getBoundingClientRect();
1741
+ const left = Math.min(
1742
+ Math.max(8, toolbarRect.left),
1743
+ ownerWindow.innerWidth - panelRect.width - 8
1744
+ );
1745
+ const belowTop = toolbarRect.bottom + 8;
1746
+ const aboveTop = toolbarRect.top - panelRect.height - 8;
1747
+ const top = belowTop + panelRect.height <= ownerWindow.innerHeight ? belowTop : Math.max(8, aboveTop);
1748
+ panel.style.left = `${left}px`;
1749
+ panel.style.top = `${top}px`;
1750
+ frame = ownerWindow.requestAnimationFrame(updatePosition);
1751
+ };
1752
+ frame = ownerWindow.requestAnimationFrame(updatePosition);
1753
+ return () => ownerWindow.cancelAnimationFrame(frame);
1754
+ }, [active, ownerWindow, toolbarRef, formats, sample, unsupported, favoriteFormat]);
1755
+ useLayoutEffect2(() => {
1756
+ return () => {
1757
+ if (copyTimeoutRef.current !== null) {
1758
+ ownerWindow.clearTimeout(copyTimeoutRef.current);
1759
+ }
1760
+ };
1761
+ }, [ownerWindow]);
1762
+ if (!active || !sample && !unsupported) return null;
1763
+ const headerFormat = formats.includes(favoriteFormat) ? favoriteFormat : formats[0];
1764
+ const secondaryFormats = headerFormat ? formats.filter((format) => format !== headerFormat) : [];
1765
+ return /* @__PURE__ */ jsx5(
1766
+ "div",
1767
+ {
1768
+ ref: panelRef,
1769
+ className: "mesurer-color-picker msr:pointer-events-auto msr:fixed msr:z-[80] msr:min-w-36 msr:rounded-lg msr:border msr:border-black/10 msr:bg-white msr:px-2 msr:py-2 msr:font-mono msr:text-[10px] msr:leading-4 msr:shadow-lg",
1770
+ role: "dialog",
1771
+ "aria-label": "Selected color values",
1772
+ onMouseLeave: tooltip.onTooltipContainerLeave,
1773
+ children: unsupported ? /* @__PURE__ */ jsxs5("div", { className: "msr:flex msr:items-start msr:gap-2", children: [
1774
+ /* @__PURE__ */ jsx5("span", { className: "msr:text-black/60", children: "Color picker is not supported in this browser." }),
1775
+ /* @__PURE__ */ jsx5(
1776
+ "button",
1777
+ {
1778
+ type: "button",
1779
+ className: "msr:text-black/45 msr:hover:text-black",
1780
+ "aria-label": "Close color picker message",
1781
+ onClick: onClose,
1782
+ children: "x"
1783
+ }
1784
+ )
1785
+ ] }) : sample ? /* @__PURE__ */ jsxs5(Fragment3, { children: [
1786
+ headerFormat ? /* @__PURE__ */ jsxs5(
1787
+ "div",
1788
+ {
1789
+ className: secondaryFormats.length > 0 ? "msr:mb-1 msr:flex msr:items-center msr:gap-1.5 msr:border-b msr:border-black/8 msr:pb-1" : "msr:flex msr:items-center msr:gap-1.5",
1790
+ children: [
1791
+ /* @__PURE__ */ jsx5(
1792
+ "span",
1793
+ {
1794
+ className: "msr:size-3 msr:shrink-0 msr:rounded-full msr:border msr:border-black/15",
1795
+ style: { backgroundColor: colorToHex(sample) },
1796
+ "aria-hidden": "true"
1797
+ }
1798
+ ),
1799
+ /* @__PURE__ */ jsx5(
1800
+ CopyableColorValue,
1801
+ {
1802
+ id: headerFormat,
1803
+ value: formatColor(sample, headerFormat),
1804
+ copiedId,
1805
+ onCopy: () => copyValue(headerFormat, formatColor(sample, headerFormat)),
1806
+ onTooltipEnter: handleTooltipEnter,
1807
+ tooltip,
1808
+ className: "msr:font-medium msr:tabular-nums msr:text-black msr:hover:underline"
1809
+ }
1810
+ )
1811
+ ]
1812
+ }
1813
+ ) : /* @__PURE__ */ jsx5("div", { className: "msr:flex msr:items-center", children: /* @__PURE__ */ jsx5(
1814
+ "span",
1815
+ {
1816
+ className: "msr:size-3 msr:shrink-0 msr:rounded-full msr:border msr:border-black/15",
1817
+ style: { backgroundColor: colorToHex(sample) },
1818
+ "aria-hidden": "true"
1819
+ }
1820
+ ) }),
1821
+ secondaryFormats.map((format) => {
1822
+ const value = formatColor(sample, format);
1823
+ return /* @__PURE__ */ jsxs5("div", { className: "msr:flex msr:items-center msr:gap-2", children: [
1824
+ /* @__PURE__ */ jsx5("span", { className: "msr:w-9 msr:text-black/45", children: format }),
1825
+ /* @__PURE__ */ jsx5(
1826
+ CopyableColorValue,
1827
+ {
1828
+ id: format,
1829
+ value,
1830
+ copiedId,
1831
+ onCopy: () => copyValue(format, value),
1832
+ onTooltipEnter: handleTooltipEnter,
1833
+ tooltip,
1834
+ className: "msr:tabular-nums msr:text-black msr:hover:underline"
1835
+ }
1836
+ )
1837
+ ] }, format);
1838
+ })
1839
+ ] }) : null
1840
+ }
1841
+ );
1842
+ }
687
1843
 
688
1844
  // components/rulers-overlay.tsx
689
- import { memo as memo2, useRef as useRef2, useState as useState2 } from "react";
690
- import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
1845
+ import { memo as memo2, useEffect as useEffect2, useRef as useRef4, useState as useState5 } from "react";
1846
+ import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
691
1847
  var RULER_SIZE = 18;
692
1848
  var RULER_LENGTH = 4e3;
693
1849
  var TICK_STEP = 5;
1850
+ var RULER_EDGE_REVEAL_DISTANCE = 32;
1851
+ var RULER_FADE_MS = 100;
694
1852
  var ticks = Array.from(
695
1853
  { length: RULER_LENGTH / TICK_STEP + 1 },
696
1854
  (_, index) => index * TICK_STEP
697
1855
  );
698
1856
  var RulersOverlay = memo2(function RulersOverlay2({
1857
+ ownerWindow,
1858
+ settings,
699
1859
  onStartGuide,
700
1860
  onMoveGuide,
701
1861
  onFinishGuide,
702
1862
  onCancelGuide,
703
1863
  guides,
704
- selectedGuideIds
1864
+ selectedGuideIds,
1865
+ interactive = true,
1866
+ forceVisible = false
705
1867
  }) {
706
- const [dragPosition, setDragPosition] = useState2(null);
707
- const [dragOrientation, setDragOrientation] = useState2(null);
708
- const dragRef = useRef2(null);
1868
+ const [dragPosition, setDragPosition] = useState5(null);
1869
+ const [dragOrientation, setDragOrientation] = useState5(null);
1870
+ const [nearEdge, setNearEdge] = useState5(false);
1871
+ const dragRef = useRef4(null);
1872
+ useEffect2(() => {
1873
+ if (!settings.edgeReveal) return;
1874
+ const handlePointerMove = (event) => {
1875
+ const pointer = event;
1876
+ const nearHorizontalEdge = pointer.clientX <= RULER_EDGE_REVEAL_DISTANCE || pointer.clientX >= ownerWindow.innerWidth - RULER_EDGE_REVEAL_DISTANCE;
1877
+ const nearVerticalEdge = pointer.clientY <= RULER_EDGE_REVEAL_DISTANCE || pointer.clientY >= ownerWindow.innerHeight - RULER_EDGE_REVEAL_DISTANCE;
1878
+ setNearEdge(nearHorizontalEdge || nearVerticalEdge);
1879
+ };
1880
+ ownerWindow.document.addEventListener("pointermove", handlePointerMove, true);
1881
+ ownerWindow.document.addEventListener("mousemove", handlePointerMove, true);
1882
+ return () => {
1883
+ ownerWindow.document.removeEventListener("pointermove", handlePointerMove, true);
1884
+ ownerWindow.document.removeEventListener("mousemove", handlePointerMove, true);
1885
+ };
1886
+ }, [ownerWindow, settings.edgeReveal]);
1887
+ const showRulers = forceVisible || !settings.edgeReveal || nearEdge;
709
1888
  const beginGuideDrag = (orientation, event) => {
710
1889
  if (event.button !== 0) return;
711
1890
  event.stopPropagation();
@@ -755,13 +1934,18 @@ var RulersOverlay = memo2(function RulersOverlay2({
755
1934
  const selectedHorizontalGuides = guides.filter(
756
1935
  (guide) => guide.orientation === "horizontal" && selectedGuideIds.includes(guide.id)
757
1936
  );
758
- return /* @__PURE__ */ jsxs2(
1937
+ return /* @__PURE__ */ jsxs6(
759
1938
  "div",
760
1939
  {
761
1940
  "aria-hidden": "true",
1941
+ "data-mesurer-rulers": "true",
762
1942
  className: "msr:pointer-events-none msr:absolute msr:inset-0 msr:select-none msr:text-[9px] msr:text-[#64748b]",
1943
+ style: {
1944
+ opacity: showRulers ? settings.opacity : 0,
1945
+ transition: `opacity ${RULER_FADE_MS}ms ease`
1946
+ },
763
1947
  children: [
764
- /* @__PURE__ */ jsx3(
1948
+ /* @__PURE__ */ jsx6(
765
1949
  "div",
766
1950
  {
767
1951
  className: "msr:absolute msr:left-[18px] msr:right-0 msr:top-0 msr:overflow-hidden msr:bg-white",
@@ -769,13 +1953,13 @@ var RulersOverlay = memo2(function RulersOverlay2({
769
1953
  boxShadow: "0 1px 3px rgba(0, 0, 0, 0.12)",
770
1954
  height: RULER_SIZE,
771
1955
  cursor: "ns-resize",
772
- pointerEvents: "auto"
1956
+ pointerEvents: showRulers && interactive ? "auto" : "none"
773
1957
  },
774
1958
  onPointerDown: (event) => beginGuideDrag("horizontal", event),
775
1959
  onPointerMove: moveGuide,
776
1960
  onPointerUp: finishGuideDrag,
777
1961
  onPointerCancel: cancelGuideDrag,
778
- children: /* @__PURE__ */ jsxs2(
1962
+ children: /* @__PURE__ */ jsxs6(
779
1963
  "svg",
780
1964
  {
781
1965
  className: "msr:block msr:h-6",
@@ -783,7 +1967,7 @@ var RulersOverlay = memo2(function RulersOverlay2({
783
1967
  height: RULER_SIZE,
784
1968
  viewBox: `0 0 ${RULER_LENGTH} ${RULER_SIZE}`,
785
1969
  children: [
786
- /* @__PURE__ */ jsx3("defs", { children: /* @__PURE__ */ jsxs2(
1970
+ /* @__PURE__ */ jsx6("defs", { children: /* @__PURE__ */ jsxs6(
787
1971
  "linearGradient",
788
1972
  {
789
1973
  id: "ruler-label-fade-x",
@@ -791,18 +1975,18 @@ var RulersOverlay = memo2(function RulersOverlay2({
791
1975
  x2: "100%",
792
1976
  gradientUnits: "objectBoundingBox",
793
1977
  children: [
794
- /* @__PURE__ */ jsx3("stop", { offset: "0", stopColor: "white", stopOpacity: "0" }),
795
- /* @__PURE__ */ jsx3("stop", { offset: "0.2", stopColor: "white" }),
796
- /* @__PURE__ */ jsx3("stop", { offset: "0.8", stopColor: "white" }),
797
- /* @__PURE__ */ jsx3("stop", { offset: "1", stopColor: "white", stopOpacity: "0" })
1978
+ /* @__PURE__ */ jsx6("stop", { offset: "0", stopColor: "white", stopOpacity: "0" }),
1979
+ /* @__PURE__ */ jsx6("stop", { offset: "0.2", stopColor: "white" }),
1980
+ /* @__PURE__ */ jsx6("stop", { offset: "0.8", stopColor: "white" }),
1981
+ /* @__PURE__ */ jsx6("stop", { offset: "1", stopColor: "white", stopOpacity: "0" })
798
1982
  ]
799
1983
  }
800
1984
  ) }),
801
1985
  ticks.map((x) => {
802
1986
  const major = x % 100 === 0;
803
1987
  const medium = x % 50 === 0;
804
- return /* @__PURE__ */ jsxs2("g", { children: [
805
- /* @__PURE__ */ jsx3(
1988
+ return /* @__PURE__ */ jsxs6("g", { children: [
1989
+ /* @__PURE__ */ jsx6(
806
1990
  "line",
807
1991
  {
808
1992
  x1: x,
@@ -813,11 +1997,11 @@ var RulersOverlay = memo2(function RulersOverlay2({
813
1997
  strokeWidth: "1"
814
1998
  }
815
1999
  ),
816
- major ? /* @__PURE__ */ jsx3("text", { x, y: "8", textAnchor: "middle", fill: "currentColor", children: x }) : null
2000
+ major ? /* @__PURE__ */ jsx6("text", { x, y: "8", textAnchor: "middle", fill: "currentColor", children: x }) : null
817
2001
  ] }, x);
818
2002
  }),
819
- selectedVerticalGuides.map((guide) => /* @__PURE__ */ jsxs2("g", { children: [
820
- /* @__PURE__ */ jsx3(
2003
+ selectedVerticalGuides.map((guide) => /* @__PURE__ */ jsxs6("g", { children: [
2004
+ /* @__PURE__ */ jsx6(
821
2005
  "rect",
822
2006
  {
823
2007
  x: guide.position - RULER_SIZE - 8,
@@ -827,7 +2011,7 @@ var RulersOverlay = memo2(function RulersOverlay2({
827
2011
  fill: "url(#ruler-label-fade-x)"
828
2012
  }
829
2013
  ),
830
- /* @__PURE__ */ jsx3(
2014
+ /* @__PURE__ */ jsx6(
831
2015
  "text",
832
2016
  {
833
2017
  x: guide.position - RULER_SIZE + 16,
@@ -839,8 +2023,8 @@ var RulersOverlay = memo2(function RulersOverlay2({
839
2023
  }
840
2024
  )
841
2025
  ] }, guide.id)),
842
- dragOrientation === "vertical" && dragPosition !== null ? /* @__PURE__ */ jsxs2("g", { children: [
843
- /* @__PURE__ */ jsx3(
2026
+ dragOrientation === "vertical" && dragPosition !== null ? /* @__PURE__ */ jsxs6("g", { children: [
2027
+ /* @__PURE__ */ jsx6(
844
2028
  "rect",
845
2029
  {
846
2030
  x: dragPosition - RULER_SIZE - 8,
@@ -850,7 +2034,7 @@ var RulersOverlay = memo2(function RulersOverlay2({
850
2034
  fill: "url(#ruler-label-fade-x)"
851
2035
  }
852
2036
  ),
853
- /* @__PURE__ */ jsx3(
2037
+ /* @__PURE__ */ jsx6(
854
2038
  "text",
855
2039
  {
856
2040
  x: dragPosition - RULER_SIZE + 16,
@@ -867,7 +2051,7 @@ var RulersOverlay = memo2(function RulersOverlay2({
867
2051
  )
868
2052
  }
869
2053
  ),
870
- /* @__PURE__ */ jsx3(
2054
+ /* @__PURE__ */ jsx6(
871
2055
  "div",
872
2056
  {
873
2057
  className: "msr:absolute msr:bottom-0 msr:left-0 msr:top-[18px] msr:w-[18px] msr:overflow-hidden msr:bg-white",
@@ -875,13 +2059,13 @@ var RulersOverlay = memo2(function RulersOverlay2({
875
2059
  boxShadow: "1px 0 3px rgba(0, 0, 0, 0.12)",
876
2060
  width: RULER_SIZE,
877
2061
  cursor: "ew-resize",
878
- pointerEvents: "auto"
2062
+ pointerEvents: showRulers && interactive ? "auto" : "none"
879
2063
  },
880
2064
  onPointerDown: (event) => beginGuideDrag("vertical", event),
881
2065
  onPointerMove: moveGuide,
882
2066
  onPointerUp: finishGuideDrag,
883
2067
  onPointerCancel: cancelGuideDrag,
884
- children: /* @__PURE__ */ jsxs2(
2068
+ children: /* @__PURE__ */ jsxs6(
885
2069
  "svg",
886
2070
  {
887
2071
  className: "msr:block msr:w-6",
@@ -889,7 +2073,7 @@ var RulersOverlay = memo2(function RulersOverlay2({
889
2073
  height: RULER_LENGTH,
890
2074
  viewBox: `0 0 ${RULER_SIZE} ${RULER_LENGTH}`,
891
2075
  children: [
892
- /* @__PURE__ */ jsx3("defs", { children: /* @__PURE__ */ jsxs2(
2076
+ /* @__PURE__ */ jsx6("defs", { children: /* @__PURE__ */ jsxs6(
893
2077
  "linearGradient",
894
2078
  {
895
2079
  id: "ruler-label-fade-y",
@@ -897,18 +2081,18 @@ var RulersOverlay = memo2(function RulersOverlay2({
897
2081
  y2: "100%",
898
2082
  gradientUnits: "objectBoundingBox",
899
2083
  children: [
900
- /* @__PURE__ */ jsx3("stop", { offset: "0", stopColor: "white", stopOpacity: "0" }),
901
- /* @__PURE__ */ jsx3("stop", { offset: "0.2", stopColor: "white" }),
902
- /* @__PURE__ */ jsx3("stop", { offset: "0.8", stopColor: "white" }),
903
- /* @__PURE__ */ jsx3("stop", { offset: "1", stopColor: "white", stopOpacity: "0" })
2084
+ /* @__PURE__ */ jsx6("stop", { offset: "0", stopColor: "white", stopOpacity: "0" }),
2085
+ /* @__PURE__ */ jsx6("stop", { offset: "0.2", stopColor: "white" }),
2086
+ /* @__PURE__ */ jsx6("stop", { offset: "0.8", stopColor: "white" }),
2087
+ /* @__PURE__ */ jsx6("stop", { offset: "1", stopColor: "white", stopOpacity: "0" })
904
2088
  ]
905
2089
  }
906
2090
  ) }),
907
2091
  ticks.map((y) => {
908
2092
  const major = y % 100 === 0;
909
2093
  const medium = y % 50 === 0;
910
- return /* @__PURE__ */ jsxs2("g", { children: [
911
- /* @__PURE__ */ jsx3(
2094
+ return /* @__PURE__ */ jsxs6("g", { children: [
2095
+ /* @__PURE__ */ jsx6(
912
2096
  "line",
913
2097
  {
914
2098
  x1: RULER_SIZE,
@@ -919,7 +2103,7 @@ var RulersOverlay = memo2(function RulersOverlay2({
919
2103
  strokeWidth: "1"
920
2104
  }
921
2105
  ),
922
- major ? /* @__PURE__ */ jsx3(
2106
+ major ? /* @__PURE__ */ jsx6(
923
2107
  "text",
924
2108
  {
925
2109
  x: "8",
@@ -931,8 +2115,8 @@ var RulersOverlay = memo2(function RulersOverlay2({
931
2115
  ) : null
932
2116
  ] }, y);
933
2117
  }),
934
- selectedHorizontalGuides.map((guide) => /* @__PURE__ */ jsxs2("g", { children: [
935
- /* @__PURE__ */ jsx3(
2118
+ selectedHorizontalGuides.map((guide) => /* @__PURE__ */ jsxs6("g", { children: [
2119
+ /* @__PURE__ */ jsx6(
936
2120
  "rect",
937
2121
  {
938
2122
  x: "-3",
@@ -942,7 +2126,7 @@ var RulersOverlay = memo2(function RulersOverlay2({
942
2126
  fill: "url(#ruler-label-fade-y)"
943
2127
  }
944
2128
  ),
945
- /* @__PURE__ */ jsx3(
2129
+ /* @__PURE__ */ jsx6(
946
2130
  "text",
947
2131
  {
948
2132
  x: "9",
@@ -955,8 +2139,8 @@ var RulersOverlay = memo2(function RulersOverlay2({
955
2139
  }
956
2140
  )
957
2141
  ] }, guide.id)),
958
- dragOrientation === "horizontal" && dragPosition !== null ? /* @__PURE__ */ jsxs2("g", { children: [
959
- /* @__PURE__ */ jsx3(
2142
+ dragOrientation === "horizontal" && dragPosition !== null ? /* @__PURE__ */ jsxs6("g", { children: [
2143
+ /* @__PURE__ */ jsx6(
960
2144
  "rect",
961
2145
  {
962
2146
  x: "-3",
@@ -966,7 +2150,7 @@ var RulersOverlay = memo2(function RulersOverlay2({
966
2150
  fill: "url(#ruler-label-fade-y)"
967
2151
  }
968
2152
  ),
969
- /* @__PURE__ */ jsx3(
2153
+ /* @__PURE__ */ jsx6(
970
2154
  "text",
971
2155
  {
972
2156
  x: "9",
@@ -984,34 +2168,34 @@ var RulersOverlay = memo2(function RulersOverlay2({
984
2168
  )
985
2169
  }
986
2170
  ),
987
- /* @__PURE__ */ jsx3("div", { className: "msr:absolute msr:left-0 msr:top-0 msr:size-[18px] msr:bg-white" })
2171
+ /* @__PURE__ */ jsx6("div", { className: "msr:absolute msr:left-0 msr:top-0 msr:size-[18px] msr:bg-white" })
988
2172
  ]
989
2173
  }
990
2174
  );
991
2175
  });
992
2176
 
993
2177
  // hooks/use-drag-state.ts
994
- import { useState as useState3 } from "react";
2178
+ import { useState as useState6 } from "react";
995
2179
  var useDragState = () => {
996
- const [start, setStart] = useState3(null);
997
- const [end, setEnd] = useState3(null);
998
- const [isDragging, setIsDragging] = useState3(false);
2180
+ const [start, setStart] = useState6(null);
2181
+ const [end, setEnd] = useState6(null);
2182
+ const [isDragging, setIsDragging] = useState6(false);
999
2183
  return { start, setStart, end, setEnd, isDragging, setIsDragging };
1000
2184
  };
1001
2185
 
1002
2186
  // hooks/use-guide-drag-hold.ts
1003
- import { useCallback as useCallback2, useRef as useRef3 } from "react";
2187
+ import { useCallback as useCallback4, useRef as useRef5 } from "react";
1004
2188
  var useGuideDragHold = (eventTarget) => {
1005
- const guideDragHoldTimerRef = useRef3(null);
1006
- const guideDragHoldGuideIdRef = useRef3(null);
1007
- const clearGuideDragHold = useCallback2(() => {
2189
+ const guideDragHoldTimerRef = useRef5(null);
2190
+ const guideDragHoldGuideIdRef = useRef5(null);
2191
+ const clearGuideDragHold = useCallback4(() => {
1008
2192
  if (guideDragHoldTimerRef.current !== null) {
1009
2193
  eventTarget.clearTimeout(guideDragHoldTimerRef.current);
1010
2194
  guideDragHoldTimerRef.current = null;
1011
2195
  }
1012
2196
  guideDragHoldGuideIdRef.current = null;
1013
2197
  }, []);
1014
- const scheduleGuideDragHold = useCallback2(
2198
+ const scheduleGuideDragHold = useCallback4(
1015
2199
  (id, onHold) => {
1016
2200
  clearGuideDragHold();
1017
2201
  guideDragHoldGuideIdRef.current = id;
@@ -1030,11 +2214,11 @@ var useGuideDragHold = (eventTarget) => {
1030
2214
  };
1031
2215
 
1032
2216
  // hooks/use-guide-state.ts
1033
- import { useState as useState4 } from "react";
2217
+ import { useState as useState7 } from "react";
1034
2218
  var useGuideState = (options = {}) => {
1035
- const [guides, setGuides] = useState4(options.initialGuides ?? []);
1036
- const [draggingGuideId, setDraggingGuideId] = useState4(null);
1037
- const [selectedGuideIds, setSelectedGuideIds] = useState4(
2219
+ const [guides, setGuides] = useState7(options.initialGuides ?? []);
2220
+ const [draggingGuideId, setDraggingGuideId] = useState7(null);
2221
+ const [selectedGuideIds, setSelectedGuideIds] = useState7(
1038
2222
  options.initialSelectedGuideIds ?? []
1039
2223
  );
1040
2224
  return {
@@ -1048,67 +2232,91 @@ var useGuideState = (options = {}) => {
1048
2232
  };
1049
2233
 
1050
2234
  // hooks/use-hotkeys.ts
1051
- import { useEffect } from "react";
2235
+ import { useEffect as useEffect3, useRef as useRef6 } from "react";
1052
2236
  var useHotkeys = (options) => {
1053
- useEffect(() => {
2237
+ const optionsRef = useRef6(options);
2238
+ optionsRef.current = options;
2239
+ useEffect3(() => {
1054
2240
  const target = options.eventTarget;
1055
2241
  const handleKeyDown = (event) => {
2242
+ const current = optionsRef.current;
1056
2243
  if (event.key === "Escape") {
1057
- options.clearAll();
2244
+ if (current.isSettingsOpen()) {
2245
+ current.onToggleSettings();
2246
+ return;
2247
+ }
2248
+ if (current.isColorPickerActive()) {
2249
+ current.onCloseColorPicker();
2250
+ return;
2251
+ }
2252
+ current.clearAll();
1058
2253
  return;
1059
2254
  }
1060
2255
  if (event.metaKey || event.ctrlKey) {
2256
+ if (event.key === ",") {
2257
+ event.preventDefault();
2258
+ current.onToggleSettings();
2259
+ current.onInteract();
2260
+ return;
2261
+ }
1061
2262
  if (event.key.toLowerCase() !== "z") return;
1062
2263
  if (event.shiftKey) {
1063
2264
  event.preventDefault();
1064
- options.redo();
2265
+ current.redo();
1065
2266
  return;
1066
2267
  }
1067
2268
  event.preventDefault();
1068
- options.undo();
2269
+ current.undo();
1069
2270
  return;
1070
2271
  }
1071
2272
  if (event.key.toLowerCase() === "m") {
1072
- options.setEnabled((prev) => !prev);
2273
+ current.setEnabled((prev) => !prev);
1073
2274
  }
1074
2275
  const key = event.key.toLowerCase();
1075
- if (options.isOverlayActive()) {
2276
+ if (key === "p") {
2277
+ event.preventDefault();
2278
+ current.onColorPicker();
2279
+ current.onInteract();
2280
+ return;
2281
+ }
2282
+ if (key === "x") {
2283
+ current.onToggleXray();
2284
+ current.onInteract();
2285
+ return;
2286
+ }
2287
+ if (current.isOverlayActive()) {
1076
2288
  if (key === "a") {
1077
- options.setToolMode(
2289
+ current.setToolMode(
1078
2290
  (prev) => prev === "text-inspector" ? "none" : "text-inspector"
1079
2291
  );
1080
- options.onInteract();
2292
+ current.onInteract();
1081
2293
  }
1082
2294
  if (key === "s") {
1083
- options.setToolMode((prev) => prev === "select" ? "none" : "select");
1084
- options.onInteract();
2295
+ current.setToolMode((prev) => prev === "select" ? "none" : "select");
2296
+ current.onInteract();
1085
2297
  }
1086
2298
  if (key === "g") {
1087
- options.setToolMode((prev) => prev === "guides" ? "none" : "guides");
1088
- options.onInteract();
1089
- }
1090
- if (key === "x") {
1091
- options.setToolMode((prev) => prev === "xray" ? "none" : "xray");
1092
- options.onInteract();
2299
+ current.setToolMode((prev) => prev === "guides" ? "none" : "guides");
2300
+ current.onInteract();
1093
2301
  }
1094
2302
  if (key === "r") {
1095
- options.setRulersVisible((prev) => !prev);
1096
- options.onInteract();
2303
+ current.setRulersVisible((prev) => !prev);
2304
+ current.onInteract();
1097
2305
  }
1098
2306
  if (key === "h") {
1099
- options.setGuideOrientation("horizontal");
1100
- options.onInteract();
2307
+ current.setGuideOrientation("horizontal");
2308
+ current.onInteract();
1101
2309
  }
1102
2310
  if (key === "v") {
1103
- options.setGuideOrientation("vertical");
1104
- options.onInteract();
2311
+ current.setGuideOrientation("vertical");
2312
+ current.onInteract();
1105
2313
  }
1106
2314
  }
1107
2315
  if (event.key === "Alt") {
1108
- options.setAltPressed(true);
2316
+ current.setAltPressed(true);
1109
2317
  }
1110
2318
  if (event.key === "Backspace" || event.key === "Delete") {
1111
- const removed = options.removeSelectedGuides();
2319
+ const removed = current.removeSelectedGuides();
1112
2320
  if (removed) {
1113
2321
  event.preventDefault();
1114
2322
  }
@@ -1116,7 +2324,7 @@ var useHotkeys = (options) => {
1116
2324
  };
1117
2325
  const handleKeyUp = (event) => {
1118
2326
  if (event.key === "Alt") {
1119
- options.setAltPressed(false);
2327
+ optionsRef.current.setAltPressed(false);
1120
2328
  }
1121
2329
  };
1122
2330
  target.addEventListener("keydown", handleKeyDown);
@@ -1125,11 +2333,11 @@ var useHotkeys = (options) => {
1125
2333
  target.removeEventListener("keydown", handleKeyDown);
1126
2334
  target.removeEventListener("keyup", handleKeyUp);
1127
2335
  };
1128
- }, [options]);
2336
+ }, [options.eventTarget]);
1129
2337
  };
1130
2338
 
1131
2339
  // hooks/use-live-element-tracking.ts
1132
- import { useEffect as useEffect2, useRef as useRef4 } from "react";
2340
+ import { useEffect as useEffect4, useRef as useRef7 } from "react";
1133
2341
 
1134
2342
  // core/geometry.ts
1135
2343
  var getViewportSize = (ownerWindow) => ({
@@ -1155,7 +2363,7 @@ var getRectFromPoints = (start, end) => {
1155
2363
  const height = Math.abs(start.y - end.y);
1156
2364
  return { left, top, width, height };
1157
2365
  };
1158
- var clamp = (value, min, max) => Math.max(min, Math.min(max, value));
2366
+ var clamp2 = (value, min, max) => Math.max(min, Math.min(max, value));
1159
2367
  var rectsOverlap = (rectA, rectB) => rectA.left < rectB.left + rectB.width && rectA.left + rectA.width > rectB.left && rectA.top < rectB.top + rectB.height && rectA.top + rectA.height > rectB.top;
1160
2368
  var rectArea = (rect) => rect.width * rect.height;
1161
2369
  var intersectionArea = (rectA, rectB) => {
@@ -1219,10 +2427,10 @@ var getDistanceOverlay = (rectA, rectB, elementRefA, elementRefB, ownerWindow =
1219
2427
  }
1220
2428
  }
1221
2429
  const normalizedConnectors = connectors.map((segment) => ({
1222
- x1: clamp(segment.x1, 0, ownerWindow.innerWidth),
1223
- y1: clamp(segment.y1, 0, ownerWindow.innerHeight),
1224
- x2: clamp(segment.x2, 0, ownerWindow.innerWidth),
1225
- y2: clamp(segment.y2, 0, ownerWindow.innerHeight)
2430
+ x1: clamp2(segment.x1, 0, ownerWindow.innerWidth),
2431
+ y1: clamp2(segment.y1, 0, ownerWindow.innerHeight),
2432
+ x2: clamp2(segment.x2, 0, ownerWindow.innerWidth),
2433
+ y2: clamp2(segment.y2, 0, ownerWindow.innerHeight)
1226
2434
  })).filter(
1227
2435
  (segment) => Math.abs(segment.x1 - segment.x2) > 0.5 || Math.abs(segment.y1 - segment.y2) > 0.5
1228
2436
  );
@@ -1389,19 +2597,23 @@ var updateMeasurementForResize = (measurement, viewport = getViewportSize(), own
1389
2597
 
1390
2598
  // hooks/use-live-element-tracking.ts
1391
2599
  var useLiveElementTracking = (params) => {
1392
- const frameRef = useRef4(null);
1393
- useEffect2(() => {
2600
+ const paramsRef = useRef7(params);
2601
+ paramsRef.current = params;
2602
+ const frameRef = useRef7(null);
2603
+ useEffect4(() => {
2604
+ const ownerWindow = params.window;
1394
2605
  if (!params.enabled) {
1395
2606
  if (frameRef.current) {
1396
- params.window.cancelAnimationFrame(frameRef.current);
2607
+ ownerWindow.cancelAnimationFrame(frameRef.current);
1397
2608
  }
1398
2609
  frameRef.current = null;
1399
2610
  return;
1400
2611
  }
1401
2612
  const tick = () => {
1402
- params.setMeasurements(
2613
+ const current = paramsRef.current;
2614
+ current.setMeasurements(
1403
2615
  (prev) => prev.map((measurement) => {
1404
- if (!measurement.elementRef || !params.document.contains(measurement.elementRef)) {
2616
+ if (!measurement.elementRef || !current.document.contains(measurement.elementRef)) {
1405
2617
  return measurement;
1406
2618
  }
1407
2619
  const rect = getRectFromDom(measurement.elementRef);
@@ -1414,8 +2626,8 @@ var useLiveElementTracking = (params) => {
1414
2626
  };
1415
2627
  })
1416
2628
  );
1417
- params.setActiveMeasurement((prev) => {
1418
- if (!prev?.elementRef || !params.document.contains(prev.elementRef))
2629
+ current.setActiveMeasurement((prev) => {
2630
+ if (!prev?.elementRef || !current.document.contains(prev.elementRef))
1419
2631
  return prev;
1420
2632
  const rect = getRectFromDom(prev.elementRef);
1421
2633
  if (rectAlmostEqual(rect, prev.rect)) return prev;
@@ -1426,10 +2638,10 @@ var useLiveElementTracking = (params) => {
1426
2638
  originRect: void 0
1427
2639
  };
1428
2640
  });
1429
- params.setHeldDistances(
2641
+ current.setHeldDistances(
1430
2642
  (prev) => prev.map((distance) => {
1431
- const canTrackA = distance.elementRefA && params.document.contains(distance.elementRefA);
1432
- const canTrackB = distance.elementRefB && params.document.contains(distance.elementRefB);
2643
+ const canTrackA = distance.elementRefA && current.document.contains(distance.elementRefA);
2644
+ const canTrackB = distance.elementRefB && current.document.contains(distance.elementRefB);
1433
2645
  if (!canTrackA && !canTrackB) return distance;
1434
2646
  const rectA = canTrackA ? getRectFromDom(distance.elementRefA) : distance.rectA;
1435
2647
  const rectB = canTrackB ? getRectFromDom(distance.elementRefB) : distance.rectB;
@@ -1441,7 +2653,7 @@ var useLiveElementTracking = (params) => {
1441
2653
  rectB,
1442
2654
  distance.elementRefA,
1443
2655
  distance.elementRefB,
1444
- params.window
2656
+ ownerWindow
1445
2657
  );
1446
2658
  return {
1447
2659
  ...updated,
@@ -1449,62 +2661,71 @@ var useLiveElementTracking = (params) => {
1449
2661
  };
1450
2662
  })
1451
2663
  );
1452
- const selected = params.selectedElementRef.current;
1453
- if (selected && params.document.contains(selected)) {
1454
- params.setSelectedMeasurement((prev) => {
1455
- const next = getInspectMeasurement(selected, params.window);
2664
+ const selected = current.selectedElementRef.current;
2665
+ if (current.selectionEnabled && selected && current.document.contains(selected)) {
2666
+ current.setSelectedMeasurement((prev) => {
2667
+ const next = getInspectMeasurement(selected, ownerWindow);
1456
2668
  if (prev && rectAlmostEqual(prev.rect, next.rect)) return prev;
1457
2669
  return next;
1458
2670
  });
1459
2671
  }
1460
- params.setSelectedMeasurements(
1461
- (prev) => prev.map((measurement) => {
1462
- if (!measurement.elementRef || !params.document.contains(measurement.elementRef)) {
1463
- return measurement;
1464
- }
1465
- const next = getInspectMeasurement(measurement.elementRef, params.window);
1466
- if (rectAlmostEqual(next.rect, measurement.rect)) return measurement;
1467
- return {
1468
- ...next,
1469
- id: measurement.id
1470
- };
1471
- })
1472
- );
1473
- const hover = params.hoverElementRef.current;
1474
- if (hover && params.document.contains(hover)) {
2672
+ if (current.selectionEnabled) {
2673
+ current.setSelectedMeasurements(
2674
+ (prev) => prev.map((measurement) => {
2675
+ if (!measurement.elementRef || !current.document.contains(measurement.elementRef)) {
2676
+ return measurement;
2677
+ }
2678
+ const next = getInspectMeasurement(measurement.elementRef, ownerWindow);
2679
+ if (rectAlmostEqual(next.rect, measurement.rect)) return measurement;
2680
+ return {
2681
+ ...next,
2682
+ id: measurement.id
2683
+ };
2684
+ })
2685
+ );
2686
+ }
2687
+ const hover = current.hoverElementRef.current;
2688
+ if (current.selectionEnabled && hover && current.document.contains(hover)) {
1475
2689
  const rect = getRectFromDom(hover);
1476
- params.setHoverRect(
2690
+ current.setHoverRect(
1477
2691
  (prev) => prev && rectAlmostEqual(prev, rect) ? prev : rect
1478
2692
  );
1479
2693
  }
1480
- frameRef.current = params.window.requestAnimationFrame(tick);
2694
+ frameRef.current = ownerWindow.requestAnimationFrame(tick);
1481
2695
  };
1482
- frameRef.current = params.window.requestAnimationFrame(tick);
2696
+ frameRef.current = ownerWindow.requestAnimationFrame(tick);
1483
2697
  return () => {
1484
2698
  if (frameRef.current) {
1485
- params.window.cancelAnimationFrame(frameRef.current);
2699
+ ownerWindow.cancelAnimationFrame(frameRef.current);
1486
2700
  }
1487
2701
  frameRef.current = null;
1488
2702
  };
1489
- }, [params]);
2703
+ }, [params.enabled, params.window]);
1490
2704
  };
1491
2705
 
1492
2706
  // hooks/use-measure-toggles.ts
1493
- import { useState as useState5 } from "react";
2707
+ import { useState as useState8 } from "react";
1494
2708
  var useMeasureToggles = (options = {}) => {
1495
- const [enabled, setEnabled] = useState5(options.initialEnabled ?? true);
1496
- const [altPressed, setAltPressed] = useState5(false);
1497
- const [toolMode, setToolMode] = useState5(
2709
+ const [enabled, setEnabled] = useState8(options.initialEnabled ?? true);
2710
+ const [altPressed, setAltPressed] = useState8(false);
2711
+ const [toolMode, setToolMode] = useState8(
1498
2712
  options.initialToolMode ?? "none"
1499
2713
  );
1500
- const [rulersVisible, setRulersVisible] = useState5(
2714
+ const [rulersVisible, setRulersVisible] = useState8(
1501
2715
  options.initialRulersVisible ?? false
1502
2716
  );
1503
2717
  const holdEnabled = false;
1504
- const multiMeasureEnabled = false;
1505
- const snapGuidesEnabled = true;
2718
+ const [multiMeasureEnabled, setMultiMeasureEnabled] = useState8(
2719
+ options.initialMultiMeasureEnabled ?? false
2720
+ );
2721
+ const [snapGuidesEnabled, setSnapGuidesEnabled] = useState8(
2722
+ options.initialSnapGuidesEnabled ?? true
2723
+ );
2724
+ const [selectNewGuideEnabled, setSelectNewGuideEnabled] = useState8(
2725
+ options.initialSelectNewGuideEnabled ?? true
2726
+ );
1506
2727
  const guidesEnabled = toolMode === "guides";
1507
- const snapEnabled = true;
2728
+ const [snapEnabled, setSnapEnabled] = useState8(options.initialSnapEnabled ?? true);
1508
2729
  return {
1509
2730
  enabled,
1510
2731
  setEnabled,
@@ -1518,21 +2739,26 @@ var useMeasureToggles = (options = {}) => {
1518
2739
  guidesEnabled,
1519
2740
  multiMeasureEnabled,
1520
2741
  snapGuidesEnabled,
1521
- snapEnabled
2742
+ setSnapGuidesEnabled,
2743
+ selectNewGuideEnabled,
2744
+ setSelectNewGuideEnabled,
2745
+ snapEnabled,
2746
+ setSnapEnabled,
2747
+ setMultiMeasureEnabled
1522
2748
  };
1523
2749
  };
1524
2750
 
1525
2751
  // hooks/use-measurement-state.ts
1526
- import { useState as useState6 } from "react";
2752
+ import { useState as useState9 } from "react";
1527
2753
  var useMeasurementState = (options = {}) => {
1528
- const [activeMeasurement, setActiveMeasurement] = useState6(options.initialActiveMeasurement ?? null);
1529
- const [measurements, setMeasurements] = useState6(
2754
+ const [activeMeasurement, setActiveMeasurement] = useState9(options.initialActiveMeasurement ?? null);
2755
+ const [measurements, setMeasurements] = useState9(
1530
2756
  options.initialMeasurements ?? []
1531
2757
  );
1532
- const [selectedMeasurement, setSelectedMeasurement] = useState6(null);
1533
- const [selectedMeasurements, setSelectedMeasurements] = useState6([]);
1534
- const [hoverRect, setHoverRect] = useState6(null);
1535
- const [heldDistances, setHeldDistances] = useState6(
2758
+ const [selectedMeasurement, setSelectedMeasurement] = useState9(null);
2759
+ const [selectedMeasurements, setSelectedMeasurements] = useState9([]);
2760
+ const [hoverRect, setHoverRect] = useState9(null);
2761
+ const [heldDistances, setHeldDistances] = useState9(
1536
2762
  options.initialHeldDistances ?? []
1537
2763
  );
1538
2764
  return {
@@ -2022,7 +3248,7 @@ var useMeasurerDerived = ({
2022
3248
  };
2023
3249
 
2024
3250
  // hooks/use-measurer-history.ts
2025
- import { useCallback as useCallback3, useRef as useRef5 } from "react";
3251
+ import { useCallback as useCallback5, useRef as useRef8 } from "react";
2026
3252
  var HISTORY_LIMIT = 50;
2027
3253
  var useMeasurerHistory = ({
2028
3254
  toggles,
@@ -2068,10 +3294,10 @@ var useMeasurerHistory = ({
2068
3294
  setSelectedElement,
2069
3295
  clearSelectionRect
2070
3296
  } = transient;
2071
- const historyRef = useRef5([]);
2072
- const futureRef = useRef5([]);
2073
- const historySignatureRef = useRef5(null);
2074
- const captureSnapshot = useCallback3(() => {
3297
+ const historyRef = useRef8([]);
3298
+ const futureRef = useRef8([]);
3299
+ const historySignatureRef = useRef8(null);
3300
+ const captureSnapshot = useCallback5(() => {
2075
3301
  return {
2076
3302
  enabled,
2077
3303
  toolMode,
@@ -2098,7 +3324,7 @@ var useMeasurerHistory = ({
2098
3324
  selectedMeasurements,
2099
3325
  toolMode
2100
3326
  ]);
2101
- const getSnapshotSignature = useCallback3((snapshot) => {
3327
+ const getSnapshotSignature = useCallback5((snapshot) => {
2102
3328
  const serializeRect = (rect) => `${Math.round(rect.left)}:${Math.round(rect.top)}:${Math.round(rect.width)}:${Math.round(rect.height)}`;
2103
3329
  return [
2104
3330
  snapshot.enabled ? "1" : "0",
@@ -2114,7 +3340,7 @@ var useMeasurerHistory = ({
2114
3340
  snapshot.draggingGuideId ?? ""
2115
3341
  ].join("|");
2116
3342
  }, []);
2117
- const recordSnapshot = useCallback3(() => {
3343
+ const recordSnapshot = useCallback5(() => {
2118
3344
  const snapshot = captureSnapshot();
2119
3345
  const signature = getSnapshotSignature(snapshot);
2120
3346
  if (historySignatureRef.current === signature) return;
@@ -2125,7 +3351,7 @@ var useMeasurerHistory = ({
2125
3351
  historyRef.current.shift();
2126
3352
  }
2127
3353
  }, [captureSnapshot, getSnapshotSignature]);
2128
- const restoreSnapshot = useCallback3(
3354
+ const restoreSnapshot = useCallback5(
2129
3355
  (snapshot) => {
2130
3356
  setEnabled(snapshot.enabled);
2131
3357
  setToolMode(snapshot.toolMode);
@@ -2170,7 +3396,7 @@ var useMeasurerHistory = ({
2170
3396
  setHoverElement
2171
3397
  ]
2172
3398
  );
2173
- const undo = useCallback3(() => {
3399
+ const undo = useCallback5(() => {
2174
3400
  const previousSnapshot = historyRef.current.pop();
2175
3401
  if (!previousSnapshot) return;
2176
3402
  const currentSnapshot = captureSnapshot();
@@ -2181,7 +3407,7 @@ var useMeasurerHistory = ({
2181
3407
  historySignatureRef.current = null;
2182
3408
  restoreSnapshot(previousSnapshot);
2183
3409
  }, [captureSnapshot, restoreSnapshot]);
2184
- const redo = useCallback3(() => {
3410
+ const redo = useCallback5(() => {
2185
3411
  const nextSnapshot = futureRef.current.pop();
2186
3412
  if (!nextSnapshot) return;
2187
3413
  historyRef.current.push(captureSnapshot());
@@ -2191,7 +3417,7 @@ var useMeasurerHistory = ({
2191
3417
  historySignatureRef.current = null;
2192
3418
  restoreSnapshot(nextSnapshot);
2193
3419
  }, [captureSnapshot, restoreSnapshot]);
2194
- const setToolModeWithHistory = useCallback3(
3420
+ const setToolModeWithHistory = useCallback5(
2195
3421
  (value) => {
2196
3422
  setToolMode((prev) => {
2197
3423
  const next = typeof value === "function" ? value(prev) : value;
@@ -2202,7 +3428,7 @@ var useMeasurerHistory = ({
2202
3428
  },
2203
3429
  [recordSnapshot, setToolMode]
2204
3430
  );
2205
- const setGuideOrientationWithHistory = useCallback3(
3431
+ const setGuideOrientationWithHistory = useCallback5(
2206
3432
  (value) => {
2207
3433
  setGuideOrientation((prev) => {
2208
3434
  const next = typeof value === "function" ? value(prev) : value;
@@ -2213,7 +3439,7 @@ var useMeasurerHistory = ({
2213
3439
  },
2214
3440
  [recordSnapshot, setGuideOrientation]
2215
3441
  );
2216
- const setEnabledWithHistory = useCallback3(
3442
+ const setEnabledWithHistory = useCallback5(
2217
3443
  (value) => {
2218
3444
  setEnabled((prev) => {
2219
3445
  const next = typeof value === "function" ? value(prev) : value;
@@ -2224,7 +3450,7 @@ var useMeasurerHistory = ({
2224
3450
  },
2225
3451
  [recordSnapshot, setEnabled]
2226
3452
  );
2227
- const createActionCommit = useCallback3(() => {
3453
+ const createActionCommit = useCallback5(() => {
2228
3454
  let committed = false;
2229
3455
  return () => {
2230
3456
  if (committed) return;
@@ -2244,35 +3470,35 @@ var useMeasurerHistory = ({
2244
3470
  };
2245
3471
 
2246
3472
  // hooks/use-measurer-local-state.ts
2247
- import { useCallback as useCallback4, useState as useState7 } from "react";
3473
+ import { useCallback as useCallback6, useState as useState10 } from "react";
2248
3474
  var useMeasurerLocalState = ({
2249
3475
  selectedElementRef,
2250
3476
  hoverElementRef,
2251
3477
  selectionRectRef
2252
3478
  }) => {
2253
- const [selectionOriginRect, setSelectionOriginRect] = useState7(
3479
+ const [selectionOriginRect, setSelectionOriginRect] = useState10(
2254
3480
  null
2255
3481
  );
2256
- const [hoverPointer, setHoverPointer] = useState7(null);
2257
- const [hoverElement, setHoverElementState] = useState7(
3482
+ const [hoverPointer, setHoverPointer] = useState10(null);
3483
+ const [hoverElement, setHoverElementState] = useState10(
2258
3484
  null
2259
3485
  );
2260
- const [selectedElement, setSelectedElementState] = useState7(null);
2261
- const setSelectedElement = useCallback4(
3486
+ const [selectedElement, setSelectedElementState] = useState10(null);
3487
+ const setSelectedElement = useCallback6(
2262
3488
  (element) => {
2263
3489
  selectedElementRef.current = element;
2264
3490
  setSelectedElementState(element);
2265
3491
  },
2266
3492
  [selectedElementRef]
2267
3493
  );
2268
- const setHoverElement = useCallback4(
3494
+ const setHoverElement = useCallback6(
2269
3495
  (element) => {
2270
3496
  hoverElementRef.current = element;
2271
3497
  setHoverElementState(element);
2272
3498
  },
2273
3499
  [hoverElementRef]
2274
3500
  );
2275
- const clearSelectionRect = useCallback4(() => {
3501
+ const clearSelectionRect = useCallback6(() => {
2276
3502
  selectionRectRef.current = null;
2277
3503
  setSelectionOriginRect(null);
2278
3504
  }, [selectionRectRef]);
@@ -2290,7 +3516,7 @@ var useMeasurerLocalState = ({
2290
3516
  };
2291
3517
 
2292
3518
  // hooks/use-measurer-pointer.ts
2293
- import { useCallback as useCallback5, useEffect as useEffect3, useRef as useRef6 } from "react";
3519
+ import { useCallback as useCallback7, useEffect as useEffect5, useRef as useRef9 } from "react";
2294
3520
 
2295
3521
  // core/targets.ts
2296
3522
  var pickMultiTargets = (selectionRect, items) => {
@@ -2493,10 +3719,12 @@ var useMeasurerPointer = ({
2493
3719
  clearGuideDragHold,
2494
3720
  scheduleGuideDragHold,
2495
3721
  enabled,
3722
+ settingsOpen,
2496
3723
  toolMode,
2497
3724
  guidesEnabled,
2498
3725
  snapEnabled,
2499
3726
  snapGuidesEnabled,
3727
+ selectNewGuideEnabled,
2500
3728
  altPressed,
2501
3729
  guideOrientation,
2502
3730
  hoverHighlightEnabled,
@@ -2529,24 +3757,24 @@ var useMeasurerPointer = ({
2529
3757
  setHoverPointer,
2530
3758
  clearSelectionRect
2531
3759
  }) => {
2532
- const hoverFrameRef = useRef6(null);
2533
- const hoverPointRef = useRef6(null);
2534
- const selectionCacheRef = useRef6({
3760
+ const hoverFrameRef = useRef9(null);
3761
+ const hoverPointRef = useRef9(null);
3762
+ const selectionCacheRef = useRef9({
2535
3763
  key: "",
2536
3764
  entries: [],
2537
3765
  overlayNode: null,
2538
3766
  frame: -1
2539
3767
  });
2540
- const shiftDragRef = useRef6(false);
2541
- const shiftToggleElementRef = useRef6(null);
2542
- useEffect3(() => {
3768
+ const shiftDragRef = useRef9(false);
3769
+ const shiftToggleElementRef = useRef9(null);
3770
+ useEffect5(() => {
2543
3771
  return () => {
2544
3772
  if (hoverFrameRef.current) {
2545
3773
  window2.cancelAnimationFrame(hoverFrameRef.current);
2546
3774
  }
2547
3775
  };
2548
3776
  }, []);
2549
- const updateHoverTarget = useCallback5(
3777
+ const updateHoverTarget = useCallback7(
2550
3778
  (point) => {
2551
3779
  const target = getTargetElement(point, overlayRef.current, document2);
2552
3780
  if (target) {
@@ -2565,18 +3793,19 @@ var useMeasurerPointer = ({
2565
3793
  },
2566
3794
  [document2, overlayRef, setHoverElement, setHoverRect]
2567
3795
  );
2568
- const updateHoverElement = useCallback5(
3796
+ const updateHoverElement = useCallback7(
2569
3797
  (point) => {
2570
3798
  const target = getTargetElement(point, overlayRef.current, document2);
2571
3799
  setHoverElement(target);
2572
3800
  },
2573
3801
  [document2, overlayRef, setHoverElement]
2574
3802
  );
2575
- const handlePointerDown = useCallback5(
3803
+ const handlePointerDown = useCallback7(
2576
3804
  (event) => {
2577
3805
  const commit = createActionCommit();
2578
3806
  const toolbarNode = toolbarRef.current;
2579
3807
  if (toolbarNode && toolbarNode.contains(event.target)) return;
3808
+ if (settingsOpen) return;
2580
3809
  if (!enabled || event.button !== 0) return;
2581
3810
  if (toolMode === "none") return;
2582
3811
  clearSelectionRect();
@@ -2601,7 +3830,11 @@ var useMeasurerPointer = ({
2601
3830
  return;
2602
3831
  }
2603
3832
  if (guidesEnabled) {
3833
+ event.preventDefault();
2604
3834
  commit();
3835
+ setStart(null);
3836
+ setEnd(null);
3837
+ setIsDragging(false);
2605
3838
  const position = getSnapGuidePosition({
2606
3839
  orientation: guideOrientation,
2607
3840
  point,
@@ -2612,7 +3845,7 @@ var useMeasurerPointer = ({
2612
3845
  document: document2
2613
3846
  });
2614
3847
  const id = createId();
2615
- setSelectedGuideIds([id]);
3848
+ setSelectedGuideIds(selectNewGuideEnabled ? [id] : []);
2616
3849
  setGuides((prev) => [
2617
3850
  ...prev,
2618
3851
  { id, orientation: guideOrientation, position }
@@ -2637,12 +3870,14 @@ var useMeasurerPointer = ({
2637
3870
  draggingGuideId,
2638
3871
  document2,
2639
3872
  enabled,
3873
+ settingsOpen,
2640
3874
  guideOrientation,
2641
3875
  guides,
2642
3876
  guidesEnabled,
2643
3877
  optionPairOverlay,
2644
3878
  overlayRef,
2645
3879
  scheduleGuideDragHold,
3880
+ selectNewGuideEnabled,
2646
3881
  selectedGuideIds.length,
2647
3882
  selectedMeasurements,
2648
3883
  setDraggingGuideId,
@@ -2657,10 +3892,11 @@ var useMeasurerPointer = ({
2657
3892
  toolbarRef
2658
3893
  ]
2659
3894
  );
2660
- const handlePointerMove = useCallback5(
3895
+ const handlePointerMove = useCallback7(
2661
3896
  (event) => {
2662
3897
  const toolbarNode = toolbarRef.current;
2663
3898
  if (toolbarNode && toolbarNode.contains(event.target)) return;
3899
+ if (settingsOpen) return;
2664
3900
  if (!enabled) return;
2665
3901
  if (toolMode === "none") {
2666
3902
  if (hoverHighlightEnabled) {
@@ -2679,7 +3915,7 @@ var useMeasurerPointer = ({
2679
3915
  if (!hoverFrameRef.current) {
2680
3916
  hoverFrameRef.current = window2.requestAnimationFrame(() => {
2681
3917
  const latest = hoverPointRef.current;
2682
- if (latest && !draggingGuideId) {
3918
+ if (latest && !draggingGuideId && !guidesEnabled) {
2683
3919
  if (hoverHighlightEnabled) {
2684
3920
  updateHoverTarget(latest);
2685
3921
  } else {
@@ -2729,6 +3965,7 @@ var useMeasurerPointer = ({
2729
3965
  )
2730
3966
  );
2731
3967
  }
3968
+ if (guidesEnabled) return;
2732
3969
  if (!start) return;
2733
3970
  setEnd(point);
2734
3971
  if (!isDragging) {
@@ -2744,6 +3981,7 @@ var useMeasurerPointer = ({
2744
3981
  altPressed,
2745
3982
  draggingGuideId,
2746
3983
  enabled,
3984
+ settingsOpen,
2747
3985
  hoverHighlightEnabled,
2748
3986
  guides,
2749
3987
  guidesEnabled,
@@ -2766,13 +4004,20 @@ var useMeasurerPointer = ({
2766
4004
  updateHoverTarget
2767
4005
  ]
2768
4006
  );
2769
- const handlePointerUp = useCallback5(
4007
+ const handlePointerUp = useCallback7(
2770
4008
  (event) => {
2771
4009
  const commit = createActionCommit();
2772
4010
  const toolbarNode = toolbarRef.current;
2773
4011
  if (toolbarNode && toolbarNode.contains(event.target)) return;
4012
+ if (settingsOpen) return;
2774
4013
  if (!enabled) return;
2775
4014
  clearGuideDragHold();
4015
+ if (guidesEnabled) {
4016
+ setStart(null);
4017
+ setEnd(null);
4018
+ setIsDragging(false);
4019
+ return;
4020
+ }
2776
4021
  if (toolMode === "none") {
2777
4022
  setStart(null);
2778
4023
  setEnd(null);
@@ -2943,7 +4188,9 @@ var useMeasurerPointer = ({
2943
4188
  createActionCommit,
2944
4189
  draggingGuideId,
2945
4190
  enabled,
4191
+ settingsOpen,
2946
4192
  end,
4193
+ guidesEnabled,
2947
4194
  hoverHighlightEnabled,
2948
4195
  isDragging,
2949
4196
  overlayRef,
@@ -2966,7 +4213,7 @@ var useMeasurerPointer = ({
2966
4213
  toolbarRef
2967
4214
  ]
2968
4215
  );
2969
- const handlePointerLeave = useCallback5(() => {
4216
+ const handlePointerLeave = useCallback7(() => {
2970
4217
  clearGuideDragHold();
2971
4218
  setStart(null);
2972
4219
  setEnd(null);
@@ -2990,54 +4237,58 @@ var useMeasurerPointer = ({
2990
4237
  };
2991
4238
 
2992
4239
  // hooks/use-overlay-refs.ts
2993
- import { useRef as useRef7 } from "react";
4240
+ import { useRef as useRef10 } from "react";
2994
4241
  var useOverlayRefs = () => {
2995
- const overlayRef = useRef7(null);
2996
- const selectedElementRef = useRef7(null);
2997
- const hoverElementRef = useRef7(null);
4242
+ const overlayRef = useRef10(null);
4243
+ const selectedElementRef = useRef10(null);
4244
+ const hoverElementRef = useRef10(null);
2998
4245
  return { overlayRef, selectedElementRef, hoverElementRef };
2999
4246
  };
3000
4247
 
3001
4248
  // hooks/use-resize-sync.ts
3002
- import { useEffect as useEffect4, useRef as useRef8 } from "react";
4249
+ import { useEffect as useEffect6, useRef as useRef11 } from "react";
3003
4250
  var useResizeSync = (params) => {
3004
- const resizeFrameRef = useRef8(null);
3005
- const viewportRef = useRef8(getViewportSize(params.window));
3006
- useEffect4(() => {
4251
+ const paramsRef = useRef11(params);
4252
+ paramsRef.current = params;
4253
+ const resizeFrameRef = useRef11(null);
4254
+ const viewportRef = useRef11(getViewportSize(params.window));
4255
+ useEffect6(() => {
4256
+ const ownerWindow = params.window;
3007
4257
  const handleResize = () => {
4258
+ const current = paramsRef.current;
3008
4259
  if (resizeFrameRef.current) {
3009
- params.window.cancelAnimationFrame(resizeFrameRef.current);
4260
+ ownerWindow.cancelAnimationFrame(resizeFrameRef.current);
3010
4261
  }
3011
- resizeFrameRef.current = params.window.requestAnimationFrame(() => {
3012
- const viewport = getViewportSize(params.window);
4262
+ resizeFrameRef.current = ownerWindow.requestAnimationFrame(() => {
4263
+ const viewport = getViewportSize(ownerWindow);
3013
4264
  const previousViewport = viewportRef.current;
3014
- params.setMeasurements(
4265
+ current.setMeasurements(
3015
4266
  (prev) => prev.map(
3016
- (measurement) => updateMeasurementForResize(measurement, viewport, params.document)
4267
+ (measurement) => updateMeasurementForResize(measurement, viewport, current.document)
3017
4268
  )
3018
4269
  );
3019
- params.setActiveMeasurement(
3020
- (prev) => prev ? updateMeasurementForResize(prev, viewport, params.document) : prev
4270
+ current.setActiveMeasurement(
4271
+ (prev) => prev ? updateMeasurementForResize(prev, viewport, current.document) : prev
3021
4272
  );
3022
- params.setHeldDistances(
4273
+ current.setHeldDistances(
3023
4274
  (prev) => prev.map(
3024
4275
  (distance) => updateDistanceForResize(
3025
4276
  distance,
3026
4277
  viewport,
3027
- params.document,
3028
- params.window
4278
+ current.document,
4279
+ ownerWindow
3029
4280
  )
3030
4281
  )
3031
4282
  );
3032
- if (params.selectedElementRef.current && params.document.contains(params.selectedElementRef.current)) {
3033
- params.setSelectedMeasurement(
3034
- getInspectMeasurement(params.selectedElementRef.current, params.window)
4283
+ if (current.selectedElementRef.current && current.document.contains(current.selectedElementRef.current)) {
4284
+ current.setSelectedMeasurement(
4285
+ getInspectMeasurement(current.selectedElementRef.current, ownerWindow)
3035
4286
  );
3036
4287
  }
3037
4288
  if (previousViewport.width > 0 && previousViewport.height > 0) {
3038
4289
  const scaleX = viewport.width / previousViewport.width;
3039
4290
  const scaleY = viewport.height / previousViewport.height;
3040
- params.setGuides(
4291
+ current.setGuides(
3041
4292
  (prev) => prev.map(
3042
4293
  (guide) => guide.orientation === "vertical" ? { ...guide, position: guide.position * scaleX } : { ...guide, position: guide.position * scaleY }
3043
4294
  )
@@ -3046,27 +4297,27 @@ var useResizeSync = (params) => {
3046
4297
  viewportRef.current = viewport;
3047
4298
  });
3048
4299
  };
3049
- params.window.addEventListener("resize", handleResize);
4300
+ ownerWindow.addEventListener("resize", handleResize);
3050
4301
  return () => {
3051
4302
  if (resizeFrameRef.current) {
3052
- params.window.cancelAnimationFrame(resizeFrameRef.current);
4303
+ ownerWindow.cancelAnimationFrame(resizeFrameRef.current);
3053
4304
  }
3054
- params.window.removeEventListener("resize", handleResize);
4305
+ ownerWindow.removeEventListener("resize", handleResize);
3055
4306
  };
3056
- }, [params]);
4307
+ }, [params.window]);
3057
4308
  };
3058
4309
 
3059
4310
  // render/measurer-overlay.tsx
3060
4311
  import { memo as memo6 } from "react";
3061
4312
 
3062
4313
  // components/measure-tag.tsx
3063
- import { jsx as jsx4 } from "react/jsx-runtime";
4314
+ import { jsx as jsx7 } from "react/jsx-runtime";
3064
4315
  function MeasureTag({
3065
4316
  children,
3066
4317
  className = "",
3067
4318
  style
3068
4319
  }) {
3069
- return /* @__PURE__ */ jsx4(
4320
+ return /* @__PURE__ */ jsx7(
3070
4321
  "div",
3071
4322
  {
3072
4323
  className: cn(
@@ -3081,20 +4332,20 @@ function MeasureTag({
3081
4332
 
3082
4333
  // components/measurement-box.tsx
3083
4334
  import { memo as memo3 } from "react";
3084
- import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
4335
+ import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
3085
4336
  var formatValue2 = (value) => Math.round(value);
3086
4337
  var MeasurementBox = memo3(function MeasurementBox2({
3087
4338
  measurement,
3088
4339
  transitionMs,
3089
4340
  labelOffset,
3090
- edgeVisibility
4341
+ edgeVisibility,
4342
+ outlineColor,
4343
+ fillColor
3091
4344
  }) {
3092
4345
  const edges = edgeVisibility ?? { top: true, right: true, bottom: true, left: true };
3093
4346
  const displayRect = measurement.rect;
3094
- const outlineColor = "color-mix(in oklch, oklch(0.62 0.18 255) 80%, transparent)";
3095
- const fillColor = "color-mix(in oklch, oklch(0.62 0.18 255) 8%, transparent)";
3096
- return /* @__PURE__ */ jsxs3("div", { className: "msr:pointer-events-none", children: [
3097
- /* @__PURE__ */ jsxs3(
4347
+ return /* @__PURE__ */ jsxs7("div", { className: "msr:pointer-events-none", children: [
4348
+ /* @__PURE__ */ jsxs7(
3098
4349
  "div",
3099
4350
  {
3100
4351
  className: "msr:absolute",
@@ -3107,28 +4358,28 @@ var MeasurementBox = memo3(function MeasurementBox2({
3107
4358
  transition: `left ${transitionMs}ms ease, top ${transitionMs}ms ease, width ${transitionMs}ms ease, height ${transitionMs}ms ease`
3108
4359
  },
3109
4360
  children: [
3110
- edges.top ? /* @__PURE__ */ jsx5(
4361
+ edges.top ? /* @__PURE__ */ jsx8(
3111
4362
  "div",
3112
4363
  {
3113
4364
  className: "msr:absolute msr:left-0 msr:top-0 msr:h-px msr:w-full",
3114
4365
  style: { backgroundColor: outlineColor }
3115
4366
  }
3116
4367
  ) : null,
3117
- edges.right ? /* @__PURE__ */ jsx5(
4368
+ edges.right ? /* @__PURE__ */ jsx8(
3118
4369
  "div",
3119
4370
  {
3120
4371
  className: "msr:absolute msr:right-0 msr:top-0 msr:h-full msr:w-px",
3121
4372
  style: { backgroundColor: outlineColor }
3122
4373
  }
3123
4374
  ) : null,
3124
- edges.bottom ? /* @__PURE__ */ jsx5(
4375
+ edges.bottom ? /* @__PURE__ */ jsx8(
3125
4376
  "div",
3126
4377
  {
3127
4378
  className: "msr:absolute msr:bottom-0 msr:left-0 msr:h-px msr:w-full",
3128
4379
  style: { backgroundColor: outlineColor }
3129
4380
  }
3130
4381
  ) : null,
3131
- edges.left ? /* @__PURE__ */ jsx5(
4382
+ edges.left ? /* @__PURE__ */ jsx8(
3132
4383
  "div",
3133
4384
  {
3134
4385
  className: "msr:absolute msr:left-0 msr:top-0 msr:h-full msr:w-px",
@@ -3138,7 +4389,7 @@ var MeasurementBox = memo3(function MeasurementBox2({
3138
4389
  ]
3139
4390
  }
3140
4391
  ),
3141
- /* @__PURE__ */ jsxs3(
4392
+ /* @__PURE__ */ jsxs7(
3142
4393
  MeasureTag,
3143
4394
  {
3144
4395
  className: "msr:-translate-x-1/2 msr:bg-ink-900/90",
@@ -3159,20 +4410,20 @@ var MeasurementBox = memo3(function MeasurementBox2({
3159
4410
 
3160
4411
  // components/selected-measurement-box.tsx
3161
4412
  import { memo as memo4 } from "react";
3162
- import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
4413
+ import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
3163
4414
  var formatValue3 = (value) => Math.round(value);
3164
4415
  var SelectedMeasurementBox = memo4(function SelectedMeasurementBox2({
3165
4416
  measurement,
3166
4417
  transitionMs,
3167
4418
  labelOffset,
3168
- edgeVisibility
4419
+ edgeVisibility,
4420
+ outlineColor,
4421
+ fillColor
3169
4422
  }) {
3170
4423
  const edges = edgeVisibility ?? { top: true, right: true, bottom: true, left: true };
3171
4424
  const displayRect = measurement.rect;
3172
- const outlineColor = "color-mix(in oklch, oklch(0.62 0.18 255) 80%, transparent)";
3173
- const fillColor = "color-mix(in oklch, oklch(0.62 0.18 255) 8%, transparent)";
3174
- return /* @__PURE__ */ jsxs4("div", { className: "msr:pointer-events-none", children: [
3175
- /* @__PURE__ */ jsxs4(
4425
+ return /* @__PURE__ */ jsxs8("div", { className: "msr:pointer-events-none", "data-mesurer-selected-measurement": "true", children: [
4426
+ /* @__PURE__ */ jsxs8(
3176
4427
  "div",
3177
4428
  {
3178
4429
  className: "msr:absolute",
@@ -3185,28 +4436,28 @@ var SelectedMeasurementBox = memo4(function SelectedMeasurementBox2({
3185
4436
  transition: `left ${transitionMs}ms ease, top ${transitionMs}ms ease, width ${transitionMs}ms ease, height ${transitionMs}ms ease`
3186
4437
  },
3187
4438
  children: [
3188
- edges.top ? /* @__PURE__ */ jsx6(
4439
+ edges.top ? /* @__PURE__ */ jsx9(
3189
4440
  "div",
3190
4441
  {
3191
4442
  className: "msr:absolute msr:left-0 msr:top-0 msr:h-px msr:w-full",
3192
4443
  style: { backgroundColor: outlineColor }
3193
4444
  }
3194
4445
  ) : null,
3195
- edges.right ? /* @__PURE__ */ jsx6(
4446
+ edges.right ? /* @__PURE__ */ jsx9(
3196
4447
  "div",
3197
4448
  {
3198
4449
  className: "msr:absolute msr:right-0 msr:top-0 msr:h-full msr:w-px",
3199
4450
  style: { backgroundColor: outlineColor }
3200
4451
  }
3201
4452
  ) : null,
3202
- edges.bottom ? /* @__PURE__ */ jsx6(
4453
+ edges.bottom ? /* @__PURE__ */ jsx9(
3203
4454
  "div",
3204
4455
  {
3205
4456
  className: "msr:absolute msr:bottom-0 msr:left-0 msr:h-px msr:w-full",
3206
4457
  style: { backgroundColor: outlineColor }
3207
4458
  }
3208
4459
  ) : null,
3209
- edges.left ? /* @__PURE__ */ jsx6(
4460
+ edges.left ? /* @__PURE__ */ jsx9(
3210
4461
  "div",
3211
4462
  {
3212
4463
  className: "msr:absolute msr:left-0 msr:top-0 msr:h-full msr:w-px",
@@ -3216,7 +4467,7 @@ var SelectedMeasurementBox = memo4(function SelectedMeasurementBox2({
3216
4467
  ]
3217
4468
  }
3218
4469
  ),
3219
- /* @__PURE__ */ jsxs4(
4470
+ /* @__PURE__ */ jsxs8(
3220
4471
  MeasureTag,
3221
4472
  {
3222
4473
  className: "msr:-translate-x-1/2 msr:bg-ink-900/90",
@@ -3237,13 +4488,13 @@ var SelectedMeasurementBox = memo4(function SelectedMeasurementBox2({
3237
4488
 
3238
4489
  // render/distance-overlay-item.tsx
3239
4490
  import { memo as memo5 } from "react";
3240
- import { Fragment, jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
4491
+ import { Fragment as Fragment4, jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
3241
4492
  var DistanceOverlayItem = memo5(function DistanceOverlayItem2({
3242
4493
  distance,
3243
4494
  labelOffset,
3244
4495
  onRemove
3245
4496
  }) {
3246
- return /* @__PURE__ */ jsxs5(
4497
+ return /* @__PURE__ */ jsxs9(
3247
4498
  "div",
3248
4499
  {
3249
4500
  className: onRemove ? "msr:pointer-events-auto" : "msr:pointer-events-none",
@@ -3252,7 +4503,7 @@ var DistanceOverlayItem = memo5(function DistanceOverlayItem2({
3252
4503
  onRemove(distance.id);
3253
4504
  } : void 0,
3254
4505
  children: [
3255
- /* @__PURE__ */ jsx7(
4506
+ /* @__PURE__ */ jsx10(
3256
4507
  "div",
3257
4508
  {
3258
4509
  className: "msr:absolute msr:rounded msr:border msr:border-[#2563eb]/70",
@@ -3264,7 +4515,7 @@ var DistanceOverlayItem = memo5(function DistanceOverlayItem2({
3264
4515
  }
3265
4516
  }
3266
4517
  ),
3267
- /* @__PURE__ */ jsx7(
4518
+ /* @__PURE__ */ jsx10(
3268
4519
  "div",
3269
4520
  {
3270
4521
  className: "msr:absolute msr:rounded msr:border msr:border-[#2563eb]/70",
@@ -3277,7 +4528,7 @@ var DistanceOverlayItem = memo5(function DistanceOverlayItem2({
3277
4528
  }
3278
4529
  ),
3279
4530
  distance.connectors.map(
3280
- (connector, index) => Math.abs(connector.x1 - connector.x2) < 1 ? /* @__PURE__ */ jsx7(
4531
+ (connector, index) => Math.abs(connector.x1 - connector.x2) < 1 ? /* @__PURE__ */ jsx10(
3281
4532
  "div",
3282
4533
  {
3283
4534
  className: "msr:absolute msr:border-l msr:border-dashed msr:border-[#2563eb]/70",
@@ -3288,7 +4539,7 @@ var DistanceOverlayItem = memo5(function DistanceOverlayItem2({
3288
4539
  }
3289
4540
  },
3290
4541
  `${distance.id}-connector-${index}`
3291
- ) : /* @__PURE__ */ jsx7(
4542
+ ) : /* @__PURE__ */ jsx10(
3292
4543
  "div",
3293
4544
  {
3294
4545
  className: "msr:absolute msr:border-t msr:border-dashed msr:border-[#2563eb]/70",
@@ -3301,8 +4552,8 @@ var DistanceOverlayItem = memo5(function DistanceOverlayItem2({
3301
4552
  `${distance.id}-connector-${index}`
3302
4553
  )
3303
4554
  ),
3304
- distance.horizontal && distance.horizontal.value > 0 ? /* @__PURE__ */ jsxs5(Fragment, { children: [
3305
- /* @__PURE__ */ jsx7(
4555
+ distance.horizontal && distance.horizontal.value > 0 ? /* @__PURE__ */ jsxs9(Fragment4, { children: [
4556
+ /* @__PURE__ */ jsx10(
3306
4557
  "div",
3307
4558
  {
3308
4559
  className: "msr:absolute msr:h-px msr:bg-[#2563eb]",
@@ -3313,7 +4564,7 @@ var DistanceOverlayItem = memo5(function DistanceOverlayItem2({
3313
4564
  }
3314
4565
  }
3315
4566
  ),
3316
- /* @__PURE__ */ jsx7(
4567
+ /* @__PURE__ */ jsx10(
3317
4568
  MeasureTag,
3318
4569
  {
3319
4570
  className: "msr:-translate-x-1/2 msr:bg-ink-900/90",
@@ -3325,8 +4576,8 @@ var DistanceOverlayItem = memo5(function DistanceOverlayItem2({
3325
4576
  }
3326
4577
  )
3327
4578
  ] }) : null,
3328
- distance.vertical && distance.vertical.value > 0 ? /* @__PURE__ */ jsxs5(Fragment, { children: [
3329
- /* @__PURE__ */ jsx7(
4579
+ distance.vertical && distance.vertical.value > 0 ? /* @__PURE__ */ jsxs9(Fragment4, { children: [
4580
+ /* @__PURE__ */ jsx10(
3330
4581
  "div",
3331
4582
  {
3332
4583
  className: "msr:absolute msr:w-px msr:bg-[#2563eb]",
@@ -3337,7 +4588,7 @@ var DistanceOverlayItem = memo5(function DistanceOverlayItem2({
3337
4588
  }
3338
4589
  }
3339
4590
  ),
3340
- /* @__PURE__ */ jsx7(
4591
+ /* @__PURE__ */ jsx10(
3341
4592
  MeasureTag,
3342
4593
  {
3343
4594
  className: "msr:-translate-y-1/2 msr:bg-ink-900/90",
@@ -3355,9 +4606,10 @@ var DistanceOverlayItem = memo5(function DistanceOverlayItem2({
3355
4606
  });
3356
4607
 
3357
4608
  // render/measurer-overlay.tsx
3358
- import { Fragment as Fragment2, jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
4609
+ import { Fragment as Fragment5, jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
3359
4610
  var MeasurerOverlay = memo6(function MeasurerOverlay2({
3360
4611
  enabled,
4612
+ interactive = true,
3361
4613
  toolMode,
3362
4614
  guidePointerEvents,
3363
4615
  guidesEnabled,
@@ -3387,6 +4639,7 @@ var MeasurerOverlay = memo6(function MeasurerOverlay2({
3387
4639
  guideColorActive,
3388
4640
  guideColorHover,
3389
4641
  guideColorDefault,
4642
+ guideStyle,
3390
4643
  onPointerDown,
3391
4644
  onPointerMove,
3392
4645
  onPointerUp,
@@ -3397,30 +4650,32 @@ var MeasurerOverlay = memo6(function MeasurerOverlay2({
3397
4650
  onGuidePointerCancel
3398
4651
  }) {
3399
4652
  const overlayVisible = enabled;
3400
- const overlayInteractive = overlayVisible && toolMode !== "none" && toolMode !== "text-inspector" && toolMode !== "xray" && toolMode !== "rulers";
4653
+ const overlayInteractive = interactive && overlayVisible && toolMode !== "none" && toolMode !== "text-inspector" && toolMode !== "xray" && toolMode !== "rulers";
3401
4654
  const selectionVisible = toolMode === "select";
3402
- return /* @__PURE__ */ jsxs6(
4655
+ return /* @__PURE__ */ jsxs10(
3403
4656
  "div",
3404
4657
  {
3405
- 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"}`,
4658
+ className: `msr:absolute msr:inset-0 msr:select-none ${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"}`,
3406
4659
  style: { pointerEvents: overlayInteractive ? "auto" : "none" },
3407
4660
  onPointerDown,
3408
4661
  onPointerMove,
3409
4662
  onPointerUp,
3410
4663
  onPointerLeave,
3411
4664
  children: [
3412
- selectionVisible ? displayedMeasurements.map((measurement, index) => /* @__PURE__ */ jsx8(
4665
+ selectionVisible ? displayedMeasurements.map((measurement, index) => /* @__PURE__ */ jsx11(
3413
4666
  MeasurementBox,
3414
4667
  {
3415
4668
  measurement,
3416
4669
  transitionMs: MEASURE_TRANSITION_MS,
3417
4670
  labelOffset: MEASURE_LABEL_OFFSET,
3418
- edgeVisibility: measurementEdgeVisibility[index]
4671
+ edgeVisibility: measurementEdgeVisibility[index],
4672
+ outlineColor,
4673
+ fillColor
3419
4674
  },
3420
4675
  measurement.id
3421
4676
  )) : null,
3422
- selectionVisible && activeRect && isDragging ? /* @__PURE__ */ jsxs6(Fragment2, { children: [
3423
- /* @__PURE__ */ jsxs6(
4677
+ selectionVisible && activeRect && isDragging ? /* @__PURE__ */ jsxs10(Fragment5, { children: [
4678
+ /* @__PURE__ */ jsxs10(
3424
4679
  "div",
3425
4680
  {
3426
4681
  className: "msr:pointer-events-none msr:absolute",
@@ -3432,28 +4687,28 @@ var MeasurerOverlay = memo6(function MeasurerOverlay2({
3432
4687
  backgroundColor: fillColor
3433
4688
  },
3434
4689
  children: [
3435
- /* @__PURE__ */ jsx8(
4690
+ /* @__PURE__ */ jsx11(
3436
4691
  "div",
3437
4692
  {
3438
4693
  className: "msr:absolute msr:left-0 msr:top-0 msr:h-px msr:w-full",
3439
4694
  style: { backgroundColor: outlineColor }
3440
4695
  }
3441
4696
  ),
3442
- /* @__PURE__ */ jsx8(
4697
+ /* @__PURE__ */ jsx11(
3443
4698
  "div",
3444
4699
  {
3445
4700
  className: "msr:absolute msr:right-0 msr:top-0 msr:h-full msr:w-px",
3446
4701
  style: { backgroundColor: outlineColor }
3447
4702
  }
3448
4703
  ),
3449
- /* @__PURE__ */ jsx8(
4704
+ /* @__PURE__ */ jsx11(
3450
4705
  "div",
3451
4706
  {
3452
4707
  className: "msr:absolute msr:bottom-0 msr:left-0 msr:h-px msr:w-full",
3453
4708
  style: { backgroundColor: outlineColor }
3454
4709
  }
3455
4710
  ),
3456
- /* @__PURE__ */ jsx8(
4711
+ /* @__PURE__ */ jsx11(
3457
4712
  "div",
3458
4713
  {
3459
4714
  className: "msr:absolute msr:left-0 msr:top-0 msr:h-full msr:w-px",
@@ -3463,7 +4718,7 @@ var MeasurerOverlay = memo6(function MeasurerOverlay2({
3463
4718
  ]
3464
4719
  }
3465
4720
  ),
3466
- /* @__PURE__ */ jsxs6(
4721
+ /* @__PURE__ */ jsxs10(
3467
4722
  MeasureTag,
3468
4723
  {
3469
4724
  className: "msr:-translate-x-1/2 msr:bg-ink-900/90",
@@ -3479,7 +4734,7 @@ var MeasurerOverlay = memo6(function MeasurerOverlay2({
3479
4734
  }
3480
4735
  )
3481
4736
  ] }) : null,
3482
- selectionVisible && hoverRectToShow ? /* @__PURE__ */ jsxs6(
4737
+ selectionVisible && hoverRectToShow ? /* @__PURE__ */ jsxs10(
3483
4738
  "div",
3484
4739
  {
3485
4740
  className: "msr:pointer-events-none msr:absolute",
@@ -3491,28 +4746,28 @@ var MeasurerOverlay = memo6(function MeasurerOverlay2({
3491
4746
  backgroundColor: fillColor
3492
4747
  },
3493
4748
  children: [
3494
- hoverEdgeVisibility?.top ? /* @__PURE__ */ jsx8(
4749
+ hoverEdgeVisibility?.top ? /* @__PURE__ */ jsx11(
3495
4750
  "div",
3496
4751
  {
3497
4752
  className: "msr:absolute msr:left-0 msr:top-0 msr:h-px msr:w-full",
3498
4753
  style: { backgroundColor: outlineColor }
3499
4754
  }
3500
4755
  ) : null,
3501
- hoverEdgeVisibility?.right ? /* @__PURE__ */ jsx8(
4756
+ hoverEdgeVisibility?.right ? /* @__PURE__ */ jsx11(
3502
4757
  "div",
3503
4758
  {
3504
4759
  className: "msr:absolute msr:right-0 msr:top-0 msr:h-full msr:w-px",
3505
4760
  style: { backgroundColor: outlineColor }
3506
4761
  }
3507
4762
  ) : null,
3508
- hoverEdgeVisibility?.bottom ? /* @__PURE__ */ jsx8(
4763
+ hoverEdgeVisibility?.bottom ? /* @__PURE__ */ jsx11(
3509
4764
  "div",
3510
4765
  {
3511
4766
  className: "msr:absolute msr:bottom-0 msr:left-0 msr:h-px msr:w-full",
3512
4767
  style: { backgroundColor: outlineColor }
3513
4768
  }
3514
4769
  ) : null,
3515
- hoverEdgeVisibility?.left ? /* @__PURE__ */ jsx8(
4770
+ hoverEdgeVisibility?.left ? /* @__PURE__ */ jsx11(
3516
4771
  "div",
3517
4772
  {
3518
4773
  className: "msr:absolute msr:left-0 msr:top-0 msr:h-full msr:w-px",
@@ -3522,7 +4777,7 @@ var MeasurerOverlay = memo6(function MeasurerOverlay2({
3522
4777
  ]
3523
4778
  }
3524
4779
  ) : null,
3525
- guidesEnabled && guidePreview ? /* @__PURE__ */ jsx8(
4780
+ interactive && guidesEnabled && guidePreview ? /* @__PURE__ */ jsx11(
3526
4781
  "div",
3527
4782
  {
3528
4783
  className: "msr:pointer-events-none msr:absolute",
@@ -3537,7 +4792,7 @@ var MeasurerOverlay = memo6(function MeasurerOverlay2({
3537
4792
  height: GUIDE_HITBOX_SIZE,
3538
4793
  width: "100%"
3539
4794
  },
3540
- children: /* @__PURE__ */ jsx8(
4795
+ children: /* @__PURE__ */ jsx11(
3541
4796
  "div",
3542
4797
  {
3543
4798
  className: "msr:absolute",
@@ -3558,17 +4813,19 @@ var MeasurerOverlay = memo6(function MeasurerOverlay2({
3558
4813
  )
3559
4814
  }
3560
4815
  ) : null,
3561
- selectionVisible ? displayedSelectedMeasurements.map((measurement, index) => /* @__PURE__ */ jsx8(
4816
+ selectionVisible ? displayedSelectedMeasurements.map((measurement, index) => /* @__PURE__ */ jsx11(
3562
4817
  SelectedMeasurementBox,
3563
4818
  {
3564
4819
  measurement,
3565
4820
  transitionMs: MEASURE_TRANSITION_MS,
3566
4821
  labelOffset: MEASURE_LABEL_OFFSET,
3567
- edgeVisibility: selectedEdgeVisibility[index]
4822
+ edgeVisibility: selectedEdgeVisibility[index],
4823
+ outlineColor,
4824
+ fillColor
3568
4825
  },
3569
4826
  measurement.id
3570
4827
  )) : null,
3571
- heldDistances.map((distance) => /* @__PURE__ */ jsx8(
4828
+ heldDistances.map((distance) => /* @__PURE__ */ jsx11(
3572
4829
  DistanceOverlayItem,
3573
4830
  {
3574
4831
  distance,
@@ -3577,7 +4834,7 @@ var MeasurerOverlay = memo6(function MeasurerOverlay2({
3577
4834
  },
3578
4835
  `held-${distance.id}`
3579
4836
  )),
3580
- selectionVisible && altPressed && optionPairOverlay ? /* @__PURE__ */ jsx8(
4837
+ selectionVisible && altPressed && optionPairOverlay ? /* @__PURE__ */ jsx11(
3581
4838
  DistanceOverlayItem,
3582
4839
  {
3583
4840
  distance: optionPairOverlay,
@@ -3585,7 +4842,7 @@ var MeasurerOverlay = memo6(function MeasurerOverlay2({
3585
4842
  },
3586
4843
  `option-${optionPairOverlay.id}`
3587
4844
  ) : null,
3588
- guidesEnabled && altPressed && guideDistanceOverlay ? /* @__PURE__ */ jsx8(
4845
+ interactive && guidesEnabled && altPressed && guideDistanceOverlay ? /* @__PURE__ */ jsx11(
3589
4846
  DistanceOverlayItem,
3590
4847
  {
3591
4848
  distance: guideDistanceOverlay,
@@ -3593,9 +4850,9 @@ var MeasurerOverlay = memo6(function MeasurerOverlay2({
3593
4850
  },
3594
4851
  `guide-preview-${guideDistanceOverlay.id}`
3595
4852
  ) : null,
3596
- selectionVisible && altPressed && optionContainerLines ? /* @__PURE__ */ jsxs6(Fragment2, { children: [
3597
- optionContainerLines.top.value > 0 ? /* @__PURE__ */ jsxs6(Fragment2, { children: [
3598
- /* @__PURE__ */ jsx8(
4853
+ selectionVisible && altPressed && optionContainerLines ? /* @__PURE__ */ jsxs10(Fragment5, { children: [
4854
+ optionContainerLines.top.value > 0 ? /* @__PURE__ */ jsxs10(Fragment5, { children: [
4855
+ /* @__PURE__ */ jsx11(
3599
4856
  "div",
3600
4857
  {
3601
4858
  className: "msr:absolute msr:w-px msr:bg-[#2563eb]",
@@ -3606,7 +4863,7 @@ var MeasurerOverlay = memo6(function MeasurerOverlay2({
3606
4863
  }
3607
4864
  }
3608
4865
  ),
3609
- /* @__PURE__ */ jsx8(
4866
+ /* @__PURE__ */ jsx11(
3610
4867
  MeasureTag,
3611
4868
  {
3612
4869
  className: "msr:-translate-y-1/2 msr:bg-ink-900/90",
@@ -3618,8 +4875,8 @@ var MeasurerOverlay = memo6(function MeasurerOverlay2({
3618
4875
  }
3619
4876
  )
3620
4877
  ] }) : null,
3621
- optionContainerLines.bottom.value > 0 ? /* @__PURE__ */ jsxs6(Fragment2, { children: [
3622
- /* @__PURE__ */ jsx8(
4878
+ optionContainerLines.bottom.value > 0 ? /* @__PURE__ */ jsxs10(Fragment5, { children: [
4879
+ /* @__PURE__ */ jsx11(
3623
4880
  "div",
3624
4881
  {
3625
4882
  className: "msr:absolute msr:w-px msr:bg-[#2563eb]",
@@ -3630,7 +4887,7 @@ var MeasurerOverlay = memo6(function MeasurerOverlay2({
3630
4887
  }
3631
4888
  }
3632
4889
  ),
3633
- /* @__PURE__ */ jsx8(
4890
+ /* @__PURE__ */ jsx11(
3634
4891
  MeasureTag,
3635
4892
  {
3636
4893
  className: "msr:-translate-y-1/2 msr:bg-ink-900/90",
@@ -3642,8 +4899,8 @@ var MeasurerOverlay = memo6(function MeasurerOverlay2({
3642
4899
  }
3643
4900
  )
3644
4901
  ] }) : null,
3645
- optionContainerLines.left.value > 0 ? /* @__PURE__ */ jsxs6(Fragment2, { children: [
3646
- /* @__PURE__ */ jsx8(
4902
+ optionContainerLines.left.value > 0 ? /* @__PURE__ */ jsxs10(Fragment5, { children: [
4903
+ /* @__PURE__ */ jsx11(
3647
4904
  "div",
3648
4905
  {
3649
4906
  className: "msr:absolute msr:h-px msr:bg-[#2563eb]",
@@ -3654,7 +4911,7 @@ var MeasurerOverlay = memo6(function MeasurerOverlay2({
3654
4911
  }
3655
4912
  }
3656
4913
  ),
3657
- /* @__PURE__ */ jsx8(
4914
+ /* @__PURE__ */ jsx11(
3658
4915
  MeasureTag,
3659
4916
  {
3660
4917
  className: "msr:-translate-x-1/2 msr:bg-ink-900/90",
@@ -3666,8 +4923,8 @@ var MeasurerOverlay = memo6(function MeasurerOverlay2({
3666
4923
  }
3667
4924
  )
3668
4925
  ] }) : null,
3669
- optionContainerLines.right.value > 0 ? /* @__PURE__ */ jsxs6(Fragment2, { children: [
3670
- /* @__PURE__ */ jsx8(
4926
+ optionContainerLines.right.value > 0 ? /* @__PURE__ */ jsxs10(Fragment5, { children: [
4927
+ /* @__PURE__ */ jsx11(
3671
4928
  "div",
3672
4929
  {
3673
4930
  className: "msr:absolute msr:h-px msr:bg-[#2563eb]",
@@ -3678,7 +4935,7 @@ var MeasurerOverlay = memo6(function MeasurerOverlay2({
3678
4935
  }
3679
4936
  }
3680
4937
  ),
3681
- /* @__PURE__ */ jsx8(
4938
+ /* @__PURE__ */ jsx11(
3682
4939
  MeasureTag,
3683
4940
  {
3684
4941
  className: "msr:-translate-x-1/2 msr:bg-ink-900/90",
@@ -3694,9 +4951,12 @@ var MeasurerOverlay = memo6(function MeasurerOverlay2({
3694
4951
  guides.map((guide) => {
3695
4952
  const isSelected = selectedGuideIds.includes(guide.id);
3696
4953
  const isHovered = hoverGuide?.id === guide.id;
3697
- const strokeWidth = isSelected || isHovered ? 2 : 1;
3698
4954
  const strokeColor = isSelected ? guideColorActive : isHovered ? guideColorHover : guideColorDefault;
3699
- return /* @__PURE__ */ jsx8(
4955
+ const strokeWidth = Math.max(guideStyle.width, isSelected || isHovered ? 2 : guideStyle.width);
4956
+ const isSolid = guideStyle.pattern === "solid";
4957
+ const backgroundImage = guideStyle.pattern === "solid" ? void 0 : guideStyle.pattern === "dotted" ? `radial-gradient(circle, ${strokeColor} 0 ${strokeWidth / 2}px, transparent ${strokeWidth / 2 + 0.5}px)` : `repeating-linear-gradient(${guide.orientation === "vertical" ? "to bottom" : "to right"}, ${strokeColor} 0 ${guideStyle.dashLength}px, transparent ${guideStyle.dashLength}px ${guideStyle.dashLength + guideStyle.gap}px)`;
4958
+ const backgroundSize = guideStyle.pattern === "dotted" ? guide.orientation === "vertical" ? `${strokeWidth}px ${guideStyle.dashLength + guideStyle.gap}px` : `${guideStyle.dashLength + guideStyle.gap}px ${strokeWidth}px` : void 0;
4959
+ return /* @__PURE__ */ jsx11(
3700
4960
  "div",
3701
4961
  {
3702
4962
  className: "msr:absolute",
@@ -3717,7 +4977,7 @@ var MeasurerOverlay = memo6(function MeasurerOverlay2({
3717
4977
  onPointerDown: (event) => onGuidePointerDown(guide, event),
3718
4978
  onPointerUp: (event) => onGuidePointerUp(guide, event),
3719
4979
  onPointerCancel: (event) => onGuidePointerCancel(guide, event),
3720
- children: /* @__PURE__ */ jsx8(
4980
+ children: /* @__PURE__ */ jsx11(
3721
4981
  "div",
3722
4982
  {
3723
4983
  className: "msr:absolute",
@@ -3726,13 +4986,19 @@ var MeasurerOverlay = memo6(function MeasurerOverlay2({
3726
4986
  top: 0,
3727
4987
  width: strokeWidth,
3728
4988
  height: "100%",
3729
- backgroundColor: strokeColor
4989
+ backgroundColor: isSolid ? strokeColor : "transparent",
4990
+ backgroundImage,
4991
+ backgroundSize,
4992
+ opacity: guideStyle.opacity
3730
4993
  } : {
3731
4994
  top: GUIDE_HITBOX_SIZE / 2 - 1,
3732
4995
  left: 0,
3733
4996
  height: strokeWidth,
3734
4997
  width: "100%",
3735
- backgroundColor: strokeColor
4998
+ backgroundColor: isSolid ? strokeColor : "transparent",
4999
+ backgroundImage,
5000
+ backgroundSize,
5001
+ opacity: guideStyle.opacity
3736
5002
  }
3737
5003
  }
3738
5004
  )
@@ -4253,6 +5519,7 @@ var createTextInspector = (options = {}, legacy = false) => {
4253
5519
  if (!element) return false;
4254
5520
  for (let node = element; node; node = node.parentElement) {
4255
5521
  if (node.classList.contains("mesurer-toolbar-surface")) return true;
5522
+ if (node.hasAttribute("data-mesurer-inspector-ui")) return true;
4256
5523
  }
4257
5524
  return false;
4258
5525
  };
@@ -4550,7 +5817,7 @@ var createTextInspector = (options = {}, legacy = false) => {
4550
5817
  const isInspectorUIEvent = (event) => event.composedPath().some(
4551
5818
  (node) => node instanceof HTMLElementConstructor && (() => {
4552
5819
  const element = node;
4553
- 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");
5820
+ 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") || element.hasAttribute("data-mesurer-inspector-ui");
4554
5821
  })()
4555
5822
  );
4556
5823
  const onClick = (event) => {
@@ -4633,8 +5900,226 @@ var createTextInspector = (options = {}, legacy = false) => {
4633
5900
  };
4634
5901
  var TextInspector = createTextInspector({}, true);
4635
5902
 
5903
+ // core/persistence.ts
5904
+ var MESURER_STORAGE_VERSION = 2;
5905
+ var DEFAULT_GUIDE_STYLE = {
5906
+ opacity: 1,
5907
+ width: 1,
5908
+ pattern: "solid",
5909
+ dashLength: 6,
5910
+ gap: 4
5911
+ };
5912
+ var DEFAULT_RULER_SETTINGS = {
5913
+ opacity: 1,
5914
+ edgeReveal: false
5915
+ };
5916
+ var isFormat = (value) => value === "hex" || value === "rgb" || value === "hsl" || value === "oklch";
5917
+ var normalizeGuideStyle = (value) => {
5918
+ if (!value || typeof value !== "object") return void 0;
5919
+ const input = value;
5920
+ return {
5921
+ opacity: typeof input.opacity === "number" ? Math.min(1, Math.max(0, input.opacity)) : DEFAULT_GUIDE_STYLE.opacity,
5922
+ width: typeof input.width === "number" ? Math.min(4, Math.max(1, input.width)) : DEFAULT_GUIDE_STYLE.width,
5923
+ pattern: input.pattern === "dashed" || input.pattern === "dotted" ? input.pattern : DEFAULT_GUIDE_STYLE.pattern,
5924
+ dashLength: typeof input.dashLength === "number" ? Math.min(24, Math.max(2, input.dashLength)) : DEFAULT_GUIDE_STYLE.dashLength,
5925
+ gap: typeof input.gap === "number" ? Math.min(24, Math.max(0, input.gap)) : DEFAULT_GUIDE_STYLE.gap
5926
+ };
5927
+ };
5928
+ var normalizeRulerSettings = (value) => {
5929
+ if (!value || typeof value !== "object") return void 0;
5930
+ const input = value;
5931
+ return {
5932
+ opacity: typeof input.opacity === "number" ? Math.min(1, Math.max(0.2, input.opacity)) : DEFAULT_RULER_SETTINGS.opacity,
5933
+ edgeReveal: typeof input.edgeReveal === "boolean" ? input.edgeReveal : DEFAULT_RULER_SETTINGS.edgeReveal
5934
+ };
5935
+ };
5936
+ var isFiniteNumber = (value) => typeof value === "number" && Number.isFinite(value);
5937
+ var isRect = (value) => {
5938
+ if (!value || typeof value !== "object") return false;
5939
+ const rect = value;
5940
+ return isFiniteNumber(rect.left) && isFiniteNumber(rect.top) && isFiniteNumber(rect.width) && rect.width >= 0 && isFiniteNumber(rect.height) && rect.height >= 0;
5941
+ };
5942
+ var isNormalizedRect = (value) => isRect(value);
5943
+ var isMeasurement = (value) => {
5944
+ if (!value || typeof value !== "object") return false;
5945
+ const measurement = value;
5946
+ return typeof measurement.id === "string" && isRect(measurement.rect) && isNormalizedRect(measurement.normalizedRect) && isFiniteNumber(measurement.deltaX) && isFiniteNumber(measurement.deltaY) && (measurement.snapped === void 0 || typeof measurement.snapped === "boolean");
5947
+ };
5948
+ var isGuide = (value) => {
5949
+ if (!value || typeof value !== "object") return false;
5950
+ const guide = value;
5951
+ return typeof guide.id === "string" && (guide.orientation === "vertical" || guide.orientation === "horizontal") && isFiniteNumber(guide.position);
5952
+ };
5953
+ var isDistanceOverlay = (value) => {
5954
+ if (!value || typeof value !== "object") return false;
5955
+ const distance = value;
5956
+ const isDistanceLine = (line) => {
5957
+ if (line === null) return true;
5958
+ if (!line || typeof line !== "object") return false;
5959
+ const item = line;
5960
+ return isFiniteNumber(item.x1) && isFiniteNumber(item.x2) && isFiniteNumber(item.y) && isFiniteNumber(item.value);
5961
+ };
5962
+ const isVerticalLine = (line) => {
5963
+ if (line === null) return true;
5964
+ if (!line || typeof line !== "object") return false;
5965
+ const item = line;
5966
+ return isFiniteNumber(item.y1) && isFiniteNumber(item.y2) && isFiniteNumber(item.x) && isFiniteNumber(item.value);
5967
+ };
5968
+ return typeof distance.id === "string" && isRect(distance.rectA) && isRect(distance.rectB) && isNormalizedRect(distance.normalizedRectA) && isNormalizedRect(distance.normalizedRectB) && isDistanceLine(distance.horizontal) && isVerticalLine(distance.vertical) && Array.isArray(distance.connectors) && distance.connectors.every((connector) => {
5969
+ if (!connector || typeof connector !== "object") return false;
5970
+ const item = connector;
5971
+ return isFiniteNumber(item.x1) && isFiniteNumber(item.y1) && isFiniteNumber(item.x2) && isFiniteNumber(item.y2);
5972
+ });
5973
+ };
5974
+ var normalizeStoredSettings = (value) => {
5975
+ if (!value || typeof value !== "object") return {};
5976
+ const input = value;
5977
+ return {
5978
+ ...typeof input.highlightColor === "string" ? { highlightColor: input.highlightColor } : {},
5979
+ ...typeof input.guideColor === "string" ? { guideColor: input.guideColor } : {},
5980
+ ...typeof input.hoverHighlightEnabled === "boolean" ? { hoverHighlightEnabled: input.hoverHighlightEnabled } : {},
5981
+ ...Array.isArray(input.colorPickerFormats) && input.colorPickerFormats.some(isFormat) ? { colorPickerFormats: input.colorPickerFormats.filter(isFormat) } : {},
5982
+ ...isFormat(input.colorPickerClickFormat) ? { colorPickerClickFormat: input.colorPickerClickFormat } : {},
5983
+ ...typeof input.snapEnabled === "boolean" ? { snapEnabled: input.snapEnabled } : {},
5984
+ ...typeof input.snapGuidesEnabled === "boolean" ? { snapGuidesEnabled: input.snapGuidesEnabled } : {},
5985
+ ...typeof input.selectNewGuideEnabled === "boolean" ? { selectNewGuideEnabled: input.selectNewGuideEnabled } : {},
5986
+ ...typeof input.multiMeasureEnabled === "boolean" ? { multiMeasureEnabled: input.multiMeasureEnabled } : {},
5987
+ ...typeof input.persistOnReload === "boolean" ? { persistOnReload: input.persistOnReload } : {},
5988
+ ...normalizeGuideStyle(input.guideStyle) ? { guideStyle: normalizeGuideStyle(input.guideStyle) } : {},
5989
+ ...normalizeRulerSettings(input.rulerSettings) ? { rulerSettings: normalizeRulerSettings(input.rulerSettings) } : {}
5990
+ };
5991
+ };
5992
+ var normalizeStoredWorkspace = (value) => {
5993
+ if (!value || typeof value !== "object") return null;
5994
+ const input = value;
5995
+ if (typeof input.enabled !== "boolean" || input.toolMode !== "none" && input.toolMode !== "select" && input.toolMode !== "guides" && input.toolMode !== "text-inspector" && input.toolMode !== "xray" && input.toolMode !== "rulers" || typeof input.rulersVisible !== "boolean" || input.guideOrientation !== "vertical" && input.guideOrientation !== "horizontal" || !Array.isArray(input.guides) || !Array.isArray(input.selectedGuideIds) || !Array.isArray(input.measurements) || !Array.isArray(input.heldDistances)) return null;
5996
+ return {
5997
+ enabled: input.enabled,
5998
+ xrayVisible: typeof input.xrayVisible === "boolean" ? input.xrayVisible : input.toolMode === "xray",
5999
+ toolMode: input.toolMode,
6000
+ rulersVisible: input.rulersVisible,
6001
+ guideOrientation: input.guideOrientation,
6002
+ guides: input.guides.filter(isGuide),
6003
+ selectedGuideIds: input.selectedGuideIds.filter((id) => typeof id === "string"),
6004
+ measurements: input.measurements.filter(isMeasurement),
6005
+ activeMeasurement: isMeasurement(input.activeMeasurement) ? input.activeMeasurement : null,
6006
+ heldDistances: input.heldDistances.filter(isDistanceOverlay)
6007
+ };
6008
+ };
6009
+ var normalizePersistenceSnapshot = (value) => {
6010
+ if (!value || typeof value !== "object") return null;
6011
+ const record = value;
6012
+ if (record.version !== MESURER_STORAGE_VERSION) return null;
6013
+ return {
6014
+ settings: normalizeStoredSettings(record.settings),
6015
+ workspace: normalizeStoredWorkspace(record.workspace)
6016
+ };
6017
+ };
6018
+ var migrate = (record) => {
6019
+ if (record.version === MESURER_STORAGE_VERSION) {
6020
+ return normalizePersistenceSnapshot(record);
6021
+ }
6022
+ if (record.version !== 1) return null;
6023
+ return {
6024
+ settings: {},
6025
+ workspace: record.enabled === void 0 || !record.toolMode || !record.guideOrientation || !record.guides || !record.selectedGuideIds || !record.measurements || record.activeMeasurement === void 0 || !record.heldDistances ? null : normalizeStoredWorkspace({
6026
+ enabled: record.enabled,
6027
+ xrayVisible: record.toolMode === "xray",
6028
+ toolMode: record.toolMode,
6029
+ rulersVisible: record.rulersVisible ?? record.toolMode === "rulers",
6030
+ guideOrientation: record.guideOrientation,
6031
+ guides: record.guides,
6032
+ selectedGuideIds: record.selectedGuideIds,
6033
+ measurements: record.measurements,
6034
+ activeMeasurement: record.activeMeasurement,
6035
+ heldDistances: record.heldDistances
6036
+ })
6037
+ };
6038
+ };
6039
+ var createLocalStoragePersistence = (ownerWindow, workspaceKey, settingsKey = workspaceKey, legacyKey) => {
6040
+ let errorHandler;
6041
+ const readRecord = (key) => {
6042
+ try {
6043
+ const raw = ownerWindow.localStorage.getItem(key);
6044
+ if (!raw) return null;
6045
+ return migrate(JSON.parse(raw));
6046
+ } catch (error) {
6047
+ errorHandler?.(error);
6048
+ return null;
6049
+ }
6050
+ };
6051
+ const read = () => {
6052
+ const legacy = legacyKey ? readRecord(legacyKey) : null;
6053
+ const settingsRecord = readRecord(settingsKey);
6054
+ const workspaceRecord = readRecord(workspaceKey);
6055
+ if (!settingsRecord && !workspaceRecord && !legacy) return null;
6056
+ return {
6057
+ settings: settingsRecord?.settings ?? legacy?.settings ?? {},
6058
+ workspace: workspaceRecord?.workspace ?? legacy?.workspace ?? null
6059
+ };
6060
+ };
6061
+ const writeRecord = (key, snapshot) => {
6062
+ try {
6063
+ ownerWindow.localStorage.setItem(
6064
+ key,
6065
+ JSON.stringify({ version: MESURER_STORAGE_VERSION, ...snapshot })
6066
+ );
6067
+ } catch (error) {
6068
+ errorHandler?.(error);
6069
+ }
6070
+ };
6071
+ return {
6072
+ load: read,
6073
+ saveSettings: (settings) => {
6074
+ if (settingsKey === workspaceKey) {
6075
+ writeRecord(workspaceKey, { settings, workspace: read()?.workspace ?? null });
6076
+ return;
6077
+ }
6078
+ writeRecord(settingsKey, { settings, workspace: null });
6079
+ },
6080
+ saveWorkspace: (workspace) => {
6081
+ if (settingsKey === workspaceKey) {
6082
+ writeRecord(workspaceKey, { settings: read()?.settings ?? {}, workspace });
6083
+ return;
6084
+ }
6085
+ writeRecord(workspaceKey, { settings: {}, workspace });
6086
+ },
6087
+ clearWorkspace: () => {
6088
+ if (settingsKey === workspaceKey) {
6089
+ const current = read();
6090
+ if (current) writeRecord(workspaceKey, { settings: current.settings, workspace: null });
6091
+ return;
6092
+ }
6093
+ writeRecord(workspaceKey, { settings: {}, workspace: null });
6094
+ },
6095
+ clearSettings: () => {
6096
+ if (settingsKey === workspaceKey) {
6097
+ writeRecord(workspaceKey, { settings: {}, workspace: read()?.workspace ?? null });
6098
+ return;
6099
+ }
6100
+ writeRecord(settingsKey, { settings: {}, workspace: null });
6101
+ },
6102
+ setErrorHandler: (handler) => {
6103
+ errorHandler = handler;
6104
+ },
6105
+ subscribe: (listener) => {
6106
+ const handleStorage = (event) => {
6107
+ if (event.key !== settingsKey && event.key !== workspaceKey && event.key !== legacyKey) return;
6108
+ listener(read(), {
6109
+ settings: event.key === settingsKey || event.key === legacyKey || settingsKey === workspaceKey,
6110
+ workspace: event.key === workspaceKey || event.key === legacyKey || settingsKey === workspaceKey
6111
+ });
6112
+ };
6113
+ ownerWindow.addEventListener("storage", handleStorage);
6114
+ return () => {
6115
+ ownerWindow.removeEventListener("storage", handleStorage);
6116
+ };
6117
+ }
6118
+ };
6119
+ };
6120
+
4636
6121
  // measurer.tsx
4637
- import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
6122
+ import { jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
4638
6123
  var measurerInstanceCount = 0;
4639
6124
  var XRAY_STYLE_ID = "mesurer-xray-styles";
4640
6125
  var XRAY_STYLES = `
@@ -4646,7 +6131,11 @@ var XRAY_STYLES = `
4646
6131
  .xray-mode .mesurer-root,
4647
6132
  .xray-mode .mesurer-root *,
4648
6133
  .xray-mode .mesurer-toolbar-surface,
4649
- .xray-mode .mesurer-toolbar-surface * {
6134
+ .xray-mode .mesurer-toolbar-surface *,
6135
+ .xray-mode .mesurer-ti-box,
6136
+ .xray-mode .mesurer-ti-card,
6137
+ .xray-mode .mesurer-ti-card *,
6138
+ .xray-mode .mesurer-ti-close {
4650
6139
  outline: none !important;
4651
6140
  }
4652
6141
  `;
@@ -4666,70 +6155,112 @@ var stripDistance = (distance) => ({
4666
6155
  elementRefA: void 0,
4667
6156
  elementRefB: void 0
4668
6157
  });
6158
+ var TAB_ID_KEY = "mesurer:tab-id";
6159
+ var SETTINGS_STORAGE_KEY = "mesurer-settings";
6160
+ var LEGACY_STORAGE_KEY = "mesurer-state";
6161
+ var getTabId = (ownerWindow) => {
6162
+ try {
6163
+ const existing = ownerWindow.sessionStorage.getItem(TAB_ID_KEY);
6164
+ if (existing) return existing;
6165
+ const id = ownerWindow.crypto.randomUUID();
6166
+ ownerWindow.sessionStorage.setItem(TAB_ID_KEY, id);
6167
+ return id;
6168
+ } catch {
6169
+ return "session";
6170
+ }
6171
+ };
6172
+ var sanitizeStoredSettings = (ownerWindow, settings) => {
6173
+ const supportsColor = (value) => value !== void 0 && ownerWindow.CSS?.supports("color", value) === true;
6174
+ return {
6175
+ ...settings,
6176
+ ...supportsColor(settings.highlightColor) ? {} : { highlightColor: void 0 },
6177
+ ...supportsColor(settings.guideColor) ? {} : { guideColor: void 0 }
6178
+ };
6179
+ };
4669
6180
  function MeasurerClient({
4670
6181
  highlightColor,
4671
6182
  guideColor,
4672
6183
  hoverHighlightEnabled,
4673
6184
  persistOnReload,
4674
6185
  portalTarget,
4675
- persistKey
6186
+ persistKey,
6187
+ colorPickerFormats,
6188
+ colorPickerClickFormat,
6189
+ snapEnabled: snapEnabledDefault,
6190
+ snapGuidesEnabled: snapGuidesEnabledDefault,
6191
+ selectNewGuideEnabled: selectNewGuideEnabledDefault,
6192
+ multiMeasureEnabled: multiMeasureEnabledDefault,
6193
+ guideStyle: guideStyleDefault,
6194
+ rulerSettings: rulerSettingsDefault,
6195
+ persistence,
6196
+ onPersistenceError
4676
6197
  }) {
4677
- const instanceIdRef = useRef9(null);
6198
+ const instanceIdRef = useRef12(null);
4678
6199
  if (instanceIdRef.current === null) {
4679
6200
  instanceIdRef.current = ++measurerInstanceCount;
4680
6201
  }
4681
- const storageKey = persistKey ?? (instanceIdRef.current === 1 ? "mesurer-state" : `mesurer-state-${instanceIdRef.current}`);
4682
6202
  const ownerDocument = portalTarget.ownerDocument ?? document;
4683
6203
  const ownerWindow = ownerDocument.defaultView ?? window;
4684
- const guideScrollRef = useRef9({
6204
+ const tabIdRef = useRef12(null);
6205
+ if (tabIdRef.current === null) tabIdRef.current = getTabId(ownerWindow);
6206
+ const storageKey = persistKey ?? (instanceIdRef.current === 1 ? `mesurer-state:${tabIdRef.current}` : `mesurer-state:${tabIdRef.current}:${instanceIdRef.current}`);
6207
+ const legacyStorageKey = persistKey ? void 0 : LEGACY_STORAGE_KEY;
6208
+ const guideScrollRef = useRef12({
4685
6209
  x: ownerWindow.scrollX,
4686
6210
  y: ownerWindow.scrollY
4687
6211
  });
4688
- const textInspectorRef = useRef9(null);
6212
+ const textInspectorRef = useRef12(null);
4689
6213
  if (!textInspectorRef.current) {
4690
6214
  textInspectorRef.current = createTextInspector({ portalTarget });
4691
6215
  }
4692
6216
  const textInspector = textInspectorRef.current;
4693
- const selectionRectRef = useRef9(null);
4694
- const toolbarRef = useRef9(null);
4695
- const selectionAnimationCleanupTimeoutRef = useRef9(null);
4696
- const guideDragRef = useRef9(null);
4697
- const guideUserSelectRef = useRef9(null);
4698
- const persistedState = useMemo2(() => {
4699
- if (!persistOnReload) return null;
4700
- try {
4701
- const stored = window.localStorage.getItem(storageKey);
4702
- if (!stored) return null;
4703
- const parsed = JSON.parse(stored);
4704
- if (!parsed || parsed.version !== 1) return null;
4705
- return parsed;
4706
- } catch {
4707
- return null;
4708
- }
4709
- }, [persistOnReload, storageKey]);
4710
- const enabledRef = useRef9(false);
4711
- const toolModeRef = useRef9(
6217
+ const selectionRectRef = useRef12(null);
6218
+ const toolbarRef = useRef12(null);
6219
+ const selectionAnimationCleanupTimeoutRef = useRef12(null);
6220
+ const guideDragRef = useRef12(null);
6221
+ const guideUserSelectRef = useRef12(null);
6222
+ const persistenceErrorHandlerRef = useRef12(onPersistenceError);
6223
+ persistenceErrorHandlerRef.current = onPersistenceError;
6224
+ const activePersistence = useMemo2(() => {
6225
+ const next = persistence ?? createLocalStoragePersistence(ownerWindow, storageKey, SETTINGS_STORAGE_KEY, legacyStorageKey);
6226
+ next.setErrorHandler?.((error) => persistenceErrorHandlerRef.current?.(error));
6227
+ return next;
6228
+ }, [legacyStorageKey, ownerWindow, persistence, storageKey]);
6229
+ const storedState = useMemo2(
6230
+ () => activePersistence.load(),
6231
+ [activePersistence]
6232
+ );
6233
+ const persistedState = persistOnReload || storedState?.settings.persistOnReload ? storedState?.workspace ?? null : null;
6234
+ const persistedSettings = sanitizeStoredSettings(ownerWindow, storedState?.settings ?? {});
6235
+ useEffect7(() => {
6236
+ return () => activePersistence.setErrorHandler?.(void 0);
6237
+ }, [activePersistence]);
6238
+ const enabledRef = useRef12(false);
6239
+ const toolModeRef = useRef12(
4712
6240
  persistedState?.toolMode === "rulers" ? "none" : persistedState?.toolMode ?? "none"
4713
6241
  );
4714
- const rulersVisibleRef = useRef9(
6242
+ const rulersVisibleRef = useRef12(
4715
6243
  persistedState?.rulersVisible ?? persistedState?.toolMode === "rulers"
4716
6244
  );
4717
- const guideOrientationRef = useRef9(
6245
+ const xrayVisibleRef = useRef12(persistedState?.xrayVisible ?? persistedState?.toolMode === "xray");
6246
+ const guideOrientationRef = useRef12(
4718
6247
  persistedState?.guideOrientation ?? "vertical"
4719
6248
  );
4720
- const measurementsRef = useRef9(
6249
+ const measurementsRef = useRef12(
4721
6250
  persistedState?.measurements ?? []
4722
6251
  );
4723
- const activeMeasurementRef = useRef9(
6252
+ const activeMeasurementRef = useRef12(
4724
6253
  persistedState?.activeMeasurement ?? null
4725
6254
  );
4726
- const heldDistancesRef = useRef9(
6255
+ const heldDistancesRef = useRef12(
4727
6256
  persistedState?.heldDistances ?? []
4728
6257
  );
4729
- const guidesRef = useRef9(persistedState?.guides ?? []);
4730
- const selectedGuideIdsRef = useRef9(
6258
+ const guidesRef = useRef12(persistedState?.guides ?? []);
6259
+ const selectedGuideIdsRef = useRef12(
4731
6260
  persistedState?.selectedGuideIds ?? []
4732
6261
  );
6262
+ const workspacePersistTimeoutRef = useRef12(null);
6263
+ const applyingExternalPersistenceRef = useRef12(false);
4733
6264
  const { overlayRef, selectedElementRef, hoverElementRef } = useOverlayRefs();
4734
6265
  const {
4735
6266
  selectionOriginRect,
@@ -4759,11 +6290,20 @@ function MeasurerClient({
4759
6290
  setRulersVisible,
4760
6291
  guidesEnabled,
4761
6292
  multiMeasureEnabled,
4762
- snapGuidesEnabled
6293
+ snapGuidesEnabled,
6294
+ setSnapGuidesEnabled,
6295
+ selectNewGuideEnabled,
6296
+ setSelectNewGuideEnabled,
6297
+ setSnapEnabled,
6298
+ setMultiMeasureEnabled
4763
6299
  } = useMeasureToggles({
4764
6300
  initialEnabled: persistedState?.enabled,
4765
6301
  initialToolMode: persistedState?.toolMode === "rulers" ? "none" : persistedState?.toolMode,
4766
- initialRulersVisible: persistedState?.rulersVisible ?? persistedState?.toolMode === "rulers"
6302
+ initialRulersVisible: persistedState?.rulersVisible ?? persistedState?.toolMode === "rulers",
6303
+ initialSnapEnabled: persistedSettings.snapEnabled ?? snapEnabledDefault,
6304
+ initialSnapGuidesEnabled: persistedSettings.snapGuidesEnabled ?? snapGuidesEnabledDefault,
6305
+ initialSelectNewGuideEnabled: persistedSettings.selectNewGuideEnabled ?? selectNewGuideEnabledDefault,
6306
+ initialMultiMeasureEnabled: persistedSettings.multiMeasureEnabled ?? multiMeasureEnabledDefault
4767
6307
  });
4768
6308
  const { start, setStart, end, setEnd, isDragging, setIsDragging } = useDragState();
4769
6309
  const {
@@ -4795,11 +6335,59 @@ function MeasurerClient({
4795
6335
  initialGuides: persistedState?.guides ?? [],
4796
6336
  initialSelectedGuideIds: persistedState?.selectedGuideIds ?? []
4797
6337
  });
4798
- const [toolbarActive, setToolbarActive] = useState8(true);
6338
+ const [toolbarActive, setToolbarActive] = useState11(true);
6339
+ const [colorPickerActive, setColorPickerActive] = useState11(false);
6340
+ const [colorPickerSample, setColorPickerSample] = useState11(null);
6341
+ const [colorPickerUnsupported, setColorPickerUnsupported] = useState11(false);
6342
+ const [settingsOpen, setSettingsOpen] = useState11(false);
6343
+ const [settingsTab, setSettingsTab] = useState11("general");
6344
+ const [settingsHighlightColor, setSettingsHighlightColor] = useState11(
6345
+ persistedSettings.highlightColor ?? highlightColor
6346
+ );
6347
+ const [settingsGuideColor, setSettingsGuideColor] = useState11(
6348
+ persistedSettings.guideColor ?? guideColor
6349
+ );
6350
+ const [settingsHoverHighlight, setSettingsHoverHighlight] = useState11(
6351
+ persistedSettings.hoverHighlightEnabled ?? hoverHighlightEnabled
6352
+ );
6353
+ const [settingsPersistOnReload, setSettingsPersistOnReload] = useState11(
6354
+ persistedSettings.persistOnReload ?? persistOnReload
6355
+ );
6356
+ const [settingsColorFormats, setSettingsColorFormats] = useState11(
6357
+ persistedSettings.colorPickerFormats ?? colorPickerFormats
6358
+ );
6359
+ const [settingsColorClickFormat, setSettingsColorClickFormat] = useState11(
6360
+ persistedSettings.colorPickerClickFormat ?? colorPickerClickFormat
6361
+ );
6362
+ const [settingsGuideStyle, setSettingsGuideStyle] = useState11({
6363
+ ...guideStyleDefault,
6364
+ ...persistedSettings.guideStyle
6365
+ });
6366
+ const [settingsRulerSettings, setSettingsRulerSettings] = useState11({
6367
+ ...rulerSettingsDefault,
6368
+ ...persistedSettings.rulerSettings
6369
+ });
6370
+ const [xrayVisible, setXrayVisible] = useState11(xrayVisibleRef.current);
4799
6371
  const { clearGuideDragHold, scheduleGuideDragHold } = useGuideDragHold(ownerWindow);
4800
- const [guidePreview, setGuidePreview] = useState8(null);
4801
- const [guideOrientation, setGuideOrientation] = useState8(persistedState?.guideOrientation ?? "vertical");
6372
+ const [guidePreview, setGuidePreview] = useState11(null);
6373
+ const resetSettings = () => {
6374
+ setSettingsHighlightColor(highlightColor);
6375
+ setSettingsGuideColor(guideColor);
6376
+ setSettingsHoverHighlight(hoverHighlightEnabled);
6377
+ setSettingsPersistOnReload(persistOnReload);
6378
+ setSettingsColorFormats([...colorPickerFormats]);
6379
+ setSettingsColorClickFormat(colorPickerClickFormat);
6380
+ setSnapEnabled(snapEnabledDefault);
6381
+ setSnapGuidesEnabled(snapGuidesEnabledDefault);
6382
+ setSelectNewGuideEnabled(selectNewGuideEnabledDefault);
6383
+ setMultiMeasureEnabled(multiMeasureEnabledDefault);
6384
+ setSettingsGuideStyle({ ...guideStyleDefault });
6385
+ setSettingsRulerSettings({ ...rulerSettingsDefault });
6386
+ };
6387
+ const initialSettingsTab = colorPickerActive ? "color-picker" : rulersVisible ? "rulers" : toolMode === "guides" ? "guides" : toolMode === "select" || toolMode === "text-inspector" ? "select" : "general";
6388
+ const [guideOrientation, setGuideOrientation] = useState11(persistedState?.guideOrientation ?? "vertical");
4802
6389
  enabledRef.current = enabled;
6390
+ xrayVisibleRef.current = xrayVisible;
4803
6391
  toolModeRef.current = toolMode;
4804
6392
  rulersVisibleRef.current = rulersVisible;
4805
6393
  guideOrientationRef.current = guideOrientation;
@@ -4808,29 +6396,168 @@ function MeasurerClient({
4808
6396
  heldDistancesRef.current = heldDistances;
4809
6397
  guidesRef.current = guides;
4810
6398
  selectedGuideIdsRef.current = selectedGuideIds;
4811
- const persistState = useCallback6(() => {
4812
- if (!persistOnReload) return;
4813
- if (typeof window === "undefined") return;
4814
- try {
4815
- window.localStorage.setItem(
4816
- storageKey,
4817
- JSON.stringify({
4818
- version: 1,
4819
- enabled: enabledRef.current,
4820
- toolMode: toolModeRef.current,
4821
- rulersVisible: rulersVisibleRef.current,
4822
- guideOrientation: guideOrientationRef.current,
4823
- guides: guidesRef.current,
4824
- selectedGuideIds: selectedGuideIdsRef.current,
4825
- measurements: measurementsRef.current.map(stripMeasurement),
4826
- activeMeasurement: activeMeasurementRef.current ? stripMeasurement(activeMeasurementRef.current) : null,
4827
- heldDistances: heldDistancesRef.current.map(stripDistance)
4828
- })
4829
- );
4830
- } catch {
6399
+ const saveWorkspace = useCallback8(() => {
6400
+ if (!settingsPersistOnReload) return;
6401
+ const workspace = {
6402
+ enabled: enabledRef.current,
6403
+ xrayVisible: xrayVisibleRef.current,
6404
+ toolMode: toolModeRef.current,
6405
+ rulersVisible: rulersVisibleRef.current,
6406
+ guideOrientation: guideOrientationRef.current,
6407
+ guides: guidesRef.current,
6408
+ selectedGuideIds: selectedGuideIdsRef.current,
6409
+ measurements: measurementsRef.current.map(stripMeasurement),
6410
+ activeMeasurement: activeMeasurementRef.current ? stripMeasurement(activeMeasurementRef.current) : null,
6411
+ heldDistances: heldDistancesRef.current.map(stripDistance)
6412
+ };
6413
+ activePersistence.saveWorkspace(workspace);
6414
+ }, [activePersistence, settingsPersistOnReload]);
6415
+ const persistState = useCallback8(() => {
6416
+ if (!settingsPersistOnReload) return;
6417
+ if (workspacePersistTimeoutRef.current !== null) {
6418
+ ownerWindow.clearTimeout(workspacePersistTimeoutRef.current);
6419
+ }
6420
+ workspacePersistTimeoutRef.current = ownerWindow.setTimeout(() => {
6421
+ workspacePersistTimeoutRef.current = null;
6422
+ saveWorkspace();
6423
+ }, 250);
6424
+ }, [ownerWindow, saveWorkspace, settingsPersistOnReload]);
6425
+ const persistSettings = useCallback8(() => {
6426
+ activePersistence.saveSettings({
6427
+ highlightColor: settingsHighlightColor,
6428
+ guideColor: settingsGuideColor,
6429
+ hoverHighlightEnabled: settingsHoverHighlight,
6430
+ colorPickerFormats: settingsColorFormats,
6431
+ colorPickerClickFormat: settingsColorClickFormat,
6432
+ snapEnabled,
6433
+ snapGuidesEnabled,
6434
+ selectNewGuideEnabled,
6435
+ multiMeasureEnabled,
6436
+ persistOnReload: settingsPersistOnReload,
6437
+ guideStyle: settingsGuideStyle,
6438
+ rulerSettings: settingsRulerSettings
6439
+ });
6440
+ }, [
6441
+ multiMeasureEnabled,
6442
+ selectNewGuideEnabled,
6443
+ settingsColorClickFormat,
6444
+ settingsColorFormats,
6445
+ settingsGuideColor,
6446
+ settingsHighlightColor,
6447
+ settingsHoverHighlight,
6448
+ settingsPersistOnReload,
6449
+ snapEnabled,
6450
+ snapGuidesEnabled,
6451
+ activePersistence,
6452
+ settingsGuideStyle,
6453
+ settingsRulerSettings
6454
+ ]);
6455
+ useEffect7(() => {
6456
+ if (applyingExternalPersistenceRef.current) {
6457
+ applyingExternalPersistenceRef.current = false;
6458
+ return;
6459
+ }
6460
+ persistSettings();
6461
+ if (settingsPersistOnReload) persistState();
6462
+ }, [persistSettings, persistState, settingsPersistOnReload]);
6463
+ useEffect7(() => {
6464
+ if (!settingsPersistOnReload) return;
6465
+ const handleBeforeUnload = () => saveWorkspace();
6466
+ ownerWindow.addEventListener("beforeunload", handleBeforeUnload);
6467
+ return () => {
6468
+ ownerWindow.removeEventListener("beforeunload", handleBeforeUnload);
6469
+ if (workspacePersistTimeoutRef.current !== null) {
6470
+ ownerWindow.clearTimeout(workspacePersistTimeoutRef.current);
6471
+ workspacePersistTimeoutRef.current = null;
6472
+ saveWorkspace();
6473
+ }
6474
+ };
6475
+ }, [ownerWindow, saveWorkspace, settingsPersistOnReload]);
6476
+ const clearPersistedWorkspace = useCallback8(() => {
6477
+ enabledRef.current = false;
6478
+ toolModeRef.current = "none";
6479
+ rulersVisibleRef.current = false;
6480
+ xrayVisibleRef.current = false;
6481
+ guideOrientationRef.current = "vertical";
6482
+ measurementsRef.current = [];
6483
+ activeMeasurementRef.current = null;
6484
+ heldDistancesRef.current = [];
6485
+ guidesRef.current = [];
6486
+ selectedGuideIdsRef.current = [];
6487
+ setEnabled(false);
6488
+ setToolMode("none");
6489
+ setRulersVisible(false);
6490
+ setXrayVisible(false);
6491
+ setGuideOrientation("vertical");
6492
+ setMeasurements([]);
6493
+ setActiveMeasurement(null);
6494
+ setSelectedMeasurement(null);
6495
+ setSelectedMeasurements([]);
6496
+ setHeldDistances([]);
6497
+ setGuides([]);
6498
+ setSelectedGuideIds([]);
6499
+ }, [setActiveMeasurement, setEnabled, setGuideOrientation, setGuides, setHeldDistances, setMeasurements, setRulersVisible, setSelectedGuideIds, setSelectedMeasurement, setSelectedMeasurements, setToolMode]);
6500
+ const clearWorkspace = useCallback8(() => {
6501
+ clearPersistedWorkspace();
6502
+ activePersistence.clearWorkspace();
6503
+ }, [activePersistence, clearPersistedWorkspace]);
6504
+ const applyPersistedWorkspace = useCallback8((workspace) => {
6505
+ enabledRef.current = workspace.enabled;
6506
+ toolModeRef.current = workspace.toolMode;
6507
+ rulersVisibleRef.current = workspace.rulersVisible;
6508
+ xrayVisibleRef.current = workspace.xrayVisible;
6509
+ guideOrientationRef.current = workspace.guideOrientation;
6510
+ measurementsRef.current = workspace.measurements;
6511
+ activeMeasurementRef.current = workspace.activeMeasurement;
6512
+ heldDistancesRef.current = workspace.heldDistances;
6513
+ guidesRef.current = workspace.guides;
6514
+ selectedGuideIdsRef.current = workspace.selectedGuideIds;
6515
+ setEnabled(workspace.enabled);
6516
+ setToolMode(workspace.toolMode);
6517
+ setRulersVisible(workspace.rulersVisible);
6518
+ setXrayVisible(workspace.xrayVisible);
6519
+ setGuideOrientation(workspace.guideOrientation);
6520
+ setMeasurements(workspace.measurements);
6521
+ setActiveMeasurement(workspace.activeMeasurement);
6522
+ setGuides(workspace.guides);
6523
+ setSelectedGuideIds(workspace.selectedGuideIds);
6524
+ setHeldDistances(workspace.heldDistances);
6525
+ }, [setActiveMeasurement, setEnabled, setGuideOrientation, setGuides, setHeldDistances, setMeasurements, setRulersVisible, setSelectedGuideIds, setToolMode]);
6526
+ const applyPersistenceSnapshot = useCallback8((snapshot, source) => {
6527
+ if (!snapshot) return;
6528
+ applyingExternalPersistenceRef.current = true;
6529
+ const settings = sanitizeStoredSettings(ownerWindow, snapshot.settings);
6530
+ if (settings.highlightColor !== void 0) setSettingsHighlightColor(settings.highlightColor);
6531
+ if (settings.guideColor !== void 0) setSettingsGuideColor(settings.guideColor);
6532
+ if (settings.hoverHighlightEnabled !== void 0) setSettingsHoverHighlight(settings.hoverHighlightEnabled);
6533
+ if (settings.colorPickerFormats !== void 0) setSettingsColorFormats(settings.colorPickerFormats);
6534
+ if (settings.colorPickerClickFormat !== void 0) setSettingsColorClickFormat(settings.colorPickerClickFormat);
6535
+ if (settings.persistOnReload !== void 0) setSettingsPersistOnReload(settings.persistOnReload);
6536
+ if (settings.snapEnabled !== void 0) setSnapEnabled(settings.snapEnabled);
6537
+ if (settings.snapGuidesEnabled !== void 0) setSnapGuidesEnabled(settings.snapGuidesEnabled);
6538
+ if (settings.selectNewGuideEnabled !== void 0) setSelectNewGuideEnabled(settings.selectNewGuideEnabled);
6539
+ if (settings.multiMeasureEnabled !== void 0) setMultiMeasureEnabled(settings.multiMeasureEnabled);
6540
+ if (settings.guideStyle !== void 0) setSettingsGuideStyle({ ...guideStyleDefault, ...settings.guideStyle });
6541
+ if (settings.rulerSettings !== void 0) setSettingsRulerSettings({ ...rulerSettingsDefault, ...settings.rulerSettings });
6542
+ const workspace = snapshot.workspace;
6543
+ if (source?.workspace !== false && workspace && (settings.persistOnReload ?? settingsPersistOnReload)) {
6544
+ applyPersistedWorkspace(workspace);
4831
6545
  }
4832
- }, [persistOnReload, storageKey]);
4833
- const setEnabledPersisted = useCallback6(
6546
+ ownerWindow.setTimeout(() => {
6547
+ applyingExternalPersistenceRef.current = false;
6548
+ }, 0);
6549
+ }, [applyPersistedWorkspace, guideStyleDefault, ownerWindow, rulerSettingsDefault, setMultiMeasureEnabled, setSelectNewGuideEnabled, setSettingsColorClickFormat, setSettingsColorFormats, setSettingsGuideColor, setSettingsHighlightColor, setSettingsHoverHighlight, setSettingsPersistOnReload, setSnapEnabled, setSnapGuidesEnabled, settingsPersistOnReload]);
6550
+ const previousPersistenceRef = useRef12(activePersistence);
6551
+ useEffect7(() => {
6552
+ if (previousPersistenceRef.current === activePersistence) return;
6553
+ previousPersistenceRef.current = activePersistence;
6554
+ applyPersistenceSnapshot(storedState);
6555
+ }, [activePersistence, applyPersistenceSnapshot, storedState]);
6556
+ useEffect7(() => {
6557
+ const unsubscribe = activePersistence.subscribe?.(applyPersistenceSnapshot);
6558
+ return unsubscribe;
6559
+ }, [activePersistence, applyPersistenceSnapshot]);
6560
+ const setEnabledPersisted = useCallback8(
4834
6561
  (value) => {
4835
6562
  const next = typeof value === "function" ? value(enabledRef.current) : value;
4836
6563
  if (Object.is(next, enabledRef.current)) return;
@@ -4840,7 +6567,7 @@ function MeasurerClient({
4840
6567
  },
4841
6568
  [persistState, setEnabled]
4842
6569
  );
4843
- const setRulersVisiblePersisted = useCallback6(
6570
+ const setRulersVisiblePersisted = useCallback8(
4844
6571
  (value) => {
4845
6572
  const next = typeof value === "function" ? value(rulersVisibleRef.current) : value;
4846
6573
  if (Object.is(next, rulersVisibleRef.current)) return;
@@ -4850,7 +6577,7 @@ function MeasurerClient({
4850
6577
  },
4851
6578
  [persistState, setRulersVisible]
4852
6579
  );
4853
- const setToolModePersisted = useCallback6(
6580
+ const setToolModePersisted = useCallback8(
4854
6581
  (value) => {
4855
6582
  const next = typeof value === "function" ? value(toolModeRef.current) : value;
4856
6583
  if (Object.is(next, toolModeRef.current)) return;
@@ -4865,7 +6592,7 @@ function MeasurerClient({
4865
6592
  },
4866
6593
  [persistState, setToolMode, textInspector]
4867
6594
  );
4868
- const setGuideOrientationPersisted = useCallback6(
6595
+ const setGuideOrientationPersisted = useCallback8(
4869
6596
  (value) => {
4870
6597
  const next = typeof value === "function" ? value(guideOrientationRef.current) : value;
4871
6598
  if (Object.is(next, guideOrientationRef.current)) return;
@@ -4875,7 +6602,7 @@ function MeasurerClient({
4875
6602
  },
4876
6603
  [persistState, setGuideOrientation]
4877
6604
  );
4878
- const setMeasurementsPersisted = useCallback6(
6605
+ const setMeasurementsPersisted = useCallback8(
4879
6606
  (value) => {
4880
6607
  const next = typeof value === "function" ? value(measurementsRef.current) : value;
4881
6608
  if (Object.is(next, measurementsRef.current)) return;
@@ -4885,7 +6612,7 @@ function MeasurerClient({
4885
6612
  },
4886
6613
  [persistState, setMeasurements]
4887
6614
  );
4888
- const setActiveMeasurementPersisted = useCallback6(
6615
+ const setActiveMeasurementPersisted = useCallback8(
4889
6616
  (value) => {
4890
6617
  const next = typeof value === "function" ? value(activeMeasurementRef.current) : value;
4891
6618
  if (Object.is(next, activeMeasurementRef.current)) return;
@@ -4895,7 +6622,7 @@ function MeasurerClient({
4895
6622
  },
4896
6623
  [persistState, setActiveMeasurement]
4897
6624
  );
4898
- const setHeldDistancesPersisted = useCallback6(
6625
+ const setHeldDistancesPersisted = useCallback8(
4899
6626
  (value) => {
4900
6627
  const next = typeof value === "function" ? value(heldDistancesRef.current) : value;
4901
6628
  if (Object.is(next, heldDistancesRef.current)) return;
@@ -4905,7 +6632,7 @@ function MeasurerClient({
4905
6632
  },
4906
6633
  [persistState, setHeldDistances]
4907
6634
  );
4908
- const setGuidesPersisted = useCallback6(
6635
+ const setGuidesPersisted = useCallback8(
4909
6636
  (value) => {
4910
6637
  const next = typeof value === "function" ? value(guidesRef.current) : value;
4911
6638
  if (Object.is(next, guidesRef.current)) return;
@@ -4915,7 +6642,7 @@ function MeasurerClient({
4915
6642
  },
4916
6643
  [persistState, setGuides]
4917
6644
  );
4918
- const setSelectedGuideIdsPersisted = useCallback6(
6645
+ const setSelectedGuideIdsPersisted = useCallback8(
4919
6646
  (value) => {
4920
6647
  const next = typeof value === "function" ? value(selectedGuideIdsRef.current) : value;
4921
6648
  if (Object.is(next, selectedGuideIdsRef.current)) return;
@@ -4973,15 +6700,15 @@ function MeasurerClient({
4973
6700
  clearSelectionRect
4974
6701
  }
4975
6702
  });
4976
- const undo = useCallback6(() => {
6703
+ const undo = useCallback8(() => {
4977
6704
  if (toolMode === "text-inspector" && textInspector.undo()) return;
4978
6705
  undoHistory();
4979
6706
  }, [textInspector, toolMode, undoHistory]);
4980
- const redo = useCallback6(() => {
6707
+ const redo = useCallback8(() => {
4981
6708
  if (toolMode === "text-inspector" && textInspector.redo()) return;
4982
6709
  redoHistory();
4983
6710
  }, [redoHistory, textInspector, toolMode]);
4984
- const clearAll = useCallback6(() => {
6711
+ const clearAll = useCallback8(() => {
4985
6712
  if (toolMode === "text-inspector") {
4986
6713
  textInspector.clear();
4987
6714
  }
@@ -5021,7 +6748,7 @@ function MeasurerClient({
5021
6748
  textInspector,
5022
6749
  toolMode
5023
6750
  ]);
5024
- const removeSelectedGuides = useCallback6(() => {
6751
+ const removeSelectedGuides = useCallback8(() => {
5025
6752
  if (selectedGuideIds.length === 0) return false;
5026
6753
  recordSnapshot();
5027
6754
  setGuidesPersisted(
@@ -5035,6 +6762,37 @@ function MeasurerClient({
5035
6762
  setGuidesPersisted,
5036
6763
  setSelectedGuideIdsPersisted
5037
6764
  ]);
6765
+ const openColorPicker = useCallback8(async () => {
6766
+ const EyeDropper = ownerWindow.EyeDropper;
6767
+ setEnabledWithHistory(true);
6768
+ setToolModeWithHistory("none");
6769
+ setColorPickerActive(true);
6770
+ setColorPickerSample(null);
6771
+ setColorPickerUnsupported(!EyeDropper);
6772
+ if (!EyeDropper) return;
6773
+ try {
6774
+ const result = await new EyeDropper().open();
6775
+ const nextSample = parseCssColor(result.sRGBHex);
6776
+ if (!nextSample) return;
6777
+ setColorPickerSample(nextSample);
6778
+ const clipboardWrite = ownerWindow.navigator.clipboard?.writeText(
6779
+ formatColor(nextSample, settingsColorClickFormat)
6780
+ );
6781
+ void clipboardWrite?.catch(() => void 0);
6782
+ } catch (error) {
6783
+ if (error instanceof DOMException && error.name === "AbortError") {
6784
+ setColorPickerActive(false);
6785
+ }
6786
+ }
6787
+ }, [ownerWindow, setEnabledWithHistory, setToolModeWithHistory, settingsColorClickFormat]);
6788
+ const toggleSettings = useCallback8(() => {
6789
+ if (settingsOpen) {
6790
+ setSettingsOpen(false);
6791
+ return;
6792
+ }
6793
+ setSettingsTab(initialSettingsTab);
6794
+ setSettingsOpen(true);
6795
+ }, [initialSettingsTab, settingsOpen]);
5038
6796
  useHotkeys({
5039
6797
  eventTarget: ownerWindow,
5040
6798
  clearAll,
@@ -5047,7 +6805,13 @@ function MeasurerClient({
5047
6805
  setAltPressed,
5048
6806
  isOverlayActive: () => enabled && (toolMode !== "none" || toolbarActive),
5049
6807
  setGuideOrientation: setGuideOrientationWithHistory,
5050
- onInteract: () => setToolbarActive(true)
6808
+ onInteract: () => setToolbarActive(true),
6809
+ onColorPicker: openColorPicker,
6810
+ onToggleXray: () => setXrayVisible((previous) => !previous),
6811
+ onToggleSettings: toggleSettings,
6812
+ isSettingsOpen: () => settingsOpen,
6813
+ onCloseColorPicker: () => setColorPickerActive(false),
6814
+ isColorPickerActive: () => colorPickerActive
5051
6815
  });
5052
6816
  useResizeSync({
5053
6817
  document: ownerDocument,
@@ -5059,7 +6823,7 @@ function MeasurerClient({
5059
6823
  setGuides: setGuidesPersisted,
5060
6824
  selectedElementRef
5061
6825
  });
5062
- useEffect5(() => {
6826
+ useEffect7(() => {
5063
6827
  const handleScroll = () => {
5064
6828
  const next = {
5065
6829
  x: ownerWindow.scrollX,
@@ -5083,6 +6847,7 @@ function MeasurerClient({
5083
6847
  document: ownerDocument,
5084
6848
  window: ownerWindow,
5085
6849
  enabled,
6850
+ selectionEnabled: toolMode === "select",
5086
6851
  selectedElementRef,
5087
6852
  hoverElementRef,
5088
6853
  setSelectedMeasurement,
@@ -5092,7 +6857,7 @@ function MeasurerClient({
5092
6857
  setActiveMeasurement: setActiveMeasurementPersisted,
5093
6858
  setHeldDistances: setHeldDistancesPersisted
5094
6859
  });
5095
- useEffect5(() => {
6860
+ useEffect7(() => {
5096
6861
  if (!toolbarActive || toolMode !== "none") return;
5097
6862
  const handlePointerDown2 = (event) => {
5098
6863
  const toolbarNode = toolbarRef.current;
@@ -5104,9 +6869,10 @@ function MeasurerClient({
5104
6869
  ownerWindow.removeEventListener("pointerdown", handlePointerDown2);
5105
6870
  };
5106
6871
  }, [ownerWindow, toolbarActive, toolMode]);
5107
- useEffect5(() => {
6872
+ useEffect7(() => {
5108
6873
  if (!enabled) return;
5109
6874
  const handleGuidePointerDown2 = (event) => {
6875
+ if (settingsOpen) return;
5110
6876
  if (toolbarRef.current?.contains(event.target)) return;
5111
6877
  const guideTarget = event.composedPath().some(
5112
6878
  (target) => target instanceof ownerWindow.Element && target.hasAttribute("data-mesurer-guide")
@@ -5189,16 +6955,30 @@ function MeasurerClient({
5189
6955
  ownerWindow,
5190
6956
  setGuidesPersisted,
5191
6957
  setSelectedGuideIdsPersisted,
6958
+ settingsOpen,
5192
6959
  toolMode
5193
6960
  ]);
5194
- useEffect5(() => {
6961
+ useEffect7(() => {
5195
6962
  if (toolMode === "text-inspector") {
5196
6963
  textInspector.enable();
5197
6964
  } else {
5198
6965
  textInspector.disable();
5199
6966
  }
5200
6967
  }, [textInspector, toolMode]);
5201
- useEffect5(() => {
6968
+ const selectionToolRef = useRef12(toolMode);
6969
+ if (selectionToolRef.current !== toolMode) {
6970
+ selectionToolRef.current = toolMode;
6971
+ if (toolMode !== "select") {
6972
+ setSelectedElement(null);
6973
+ setHoverElement(null);
6974
+ setHoverRect(null);
6975
+ setHoverPointer(null);
6976
+ setSelectedMeasurement(null);
6977
+ setSelectedMeasurements([]);
6978
+ clearSelectionRect();
6979
+ }
6980
+ }
6981
+ useEffect7(() => {
5202
6982
  let style = ownerDocument.getElementById(XRAY_STYLE_ID);
5203
6983
  if (!style) {
5204
6984
  style = ownerDocument.createElement("style");
@@ -5206,7 +6986,7 @@ function MeasurerClient({
5206
6986
  style.textContent = XRAY_STYLES;
5207
6987
  ownerDocument.head.appendChild(style);
5208
6988
  }
5209
- if (toolMode === "xray") {
6989
+ if (xrayVisible) {
5210
6990
  ownerDocument.body.classList.add("xray-mode");
5211
6991
  } else {
5212
6992
  ownerDocument.body.classList.remove("xray-mode");
@@ -5214,13 +6994,13 @@ function MeasurerClient({
5214
6994
  return () => {
5215
6995
  ownerDocument.body.classList.remove("xray-mode");
5216
6996
  };
5217
- }, [ownerDocument, toolMode]);
5218
- useEffect5(() => {
6997
+ }, [ownerDocument, xrayVisible]);
6998
+ useEffect7(() => {
5219
6999
  return () => {
5220
7000
  textInspector.destroy();
5221
7001
  };
5222
7002
  }, [textInspector]);
5223
- useEffect5(() => {
7003
+ useEffect7(() => {
5224
7004
  const hasSelectionAnimationState = !!selectionOriginRect || !!selectedMeasurement?.originRect || selectedMeasurements.some((measurement) => !!measurement.originRect);
5225
7005
  if (!hasSelectionAnimationState) {
5226
7006
  if (selectionAnimationCleanupTimeoutRef.current !== null) {
@@ -5301,9 +7081,9 @@ function MeasurerClient({
5301
7081
  guidesEnabled,
5302
7082
  guidePreview,
5303
7083
  displayedMeasurements,
5304
- hoverHighlightEnabled,
5305
- highlightColor,
5306
- guideColor
7084
+ hoverHighlightEnabled: settingsHoverHighlight,
7085
+ highlightColor: settingsHighlightColor,
7086
+ guideColor: settingsGuideColor
5307
7087
  });
5308
7088
  const {
5309
7089
  handlePointerDown,
@@ -5320,10 +7100,12 @@ function MeasurerClient({
5320
7100
  clearGuideDragHold,
5321
7101
  scheduleGuideDragHold,
5322
7102
  enabled,
7103
+ settingsOpen,
5323
7104
  toolMode,
5324
7105
  guidesEnabled,
5325
7106
  snapEnabled,
5326
7107
  snapGuidesEnabled,
7108
+ selectNewGuideEnabled,
5327
7109
  altPressed,
5328
7110
  guideOrientation,
5329
7111
  hoverHighlightEnabled,
@@ -5356,7 +7138,7 @@ function MeasurerClient({
5356
7138
  setHoverPointer,
5357
7139
  clearSelectionRect
5358
7140
  });
5359
- const removeHeldDistance = useCallback6(
7141
+ const removeHeldDistance = useCallback8(
5360
7142
  (id) => {
5361
7143
  recordSnapshot();
5362
7144
  setHeldDistancesPersisted(
@@ -5365,42 +7147,62 @@ function MeasurerClient({
5365
7147
  },
5366
7148
  [recordSnapshot, setHeldDistancesPersisted]
5367
7149
  );
5368
- const startGuideFromRuler = useCallback6(
7150
+ const snapGuidePosition = useCallback8(
7151
+ (orientation, position, draggingGuideId2 = null) => getSnapGuidePosition({
7152
+ orientation,
7153
+ point: orientation === "vertical" ? { x: position, y: 0 } : { x: 0, y: position },
7154
+ snapGuidesEnabled,
7155
+ overlayNode: overlayRef.current,
7156
+ guides,
7157
+ draggingGuideId: draggingGuideId2,
7158
+ document: ownerDocument
7159
+ }),
7160
+ [guides, ownerDocument, overlayRef, snapGuidesEnabled]
7161
+ );
7162
+ const startGuideFromRuler = useCallback8(
5369
7163
  (orientation, position) => {
5370
7164
  const id = createId();
5371
7165
  const commit = createActionCommit();
5372
7166
  commit();
5373
7167
  setSelectedGuideIdsPersisted([]);
5374
- setGuidesPersisted((prev) => [...prev, { id, orientation, position }]);
7168
+ setGuidesPersisted((prev) => [
7169
+ ...prev,
7170
+ { id, orientation, position: snapGuidePosition(orientation, position) }
7171
+ ]);
5375
7172
  return id;
5376
7173
  },
5377
7174
  [
5378
7175
  createActionCommit,
5379
7176
  setGuidesPersisted,
5380
- setSelectedGuideIdsPersisted
7177
+ setSelectedGuideIdsPersisted,
7178
+ snapGuidePosition
5381
7179
  ]
5382
7180
  );
5383
- const moveGuideFromRuler = useCallback6(
7181
+ const moveGuideFromRuler = useCallback8(
5384
7182
  (id, position) => {
5385
7183
  setGuidesPersisted(
5386
- (prev) => prev.map((guide) => guide.id === id ? { ...guide, position } : guide)
7184
+ (prev) => prev.map(
7185
+ (guide) => guide.id === id ? { ...guide, position: snapGuidePosition(guide.orientation, position, id) } : guide
7186
+ )
5387
7187
  );
5388
7188
  },
5389
- [setGuidesPersisted]
7189
+ [setGuidesPersisted, snapGuidePosition]
5390
7190
  );
5391
- const finishGuideFromRuler = useCallback6(
7191
+ const finishGuideFromRuler = useCallback8(
5392
7192
  (id) => {
5393
- setSelectedGuideIdsPersisted([id]);
7193
+ if (selectNewGuideEnabled) {
7194
+ setSelectedGuideIdsPersisted([id]);
7195
+ }
5394
7196
  },
5395
- [setSelectedGuideIdsPersisted]
7197
+ [selectNewGuideEnabled, setSelectedGuideIdsPersisted]
5396
7198
  );
5397
- const cancelGuideFromRuler = useCallback6(
7199
+ const cancelGuideFromRuler = useCallback8(
5398
7200
  (id) => {
5399
7201
  setGuidesPersisted((prev) => prev.filter((guide) => guide.id !== id));
5400
7202
  },
5401
7203
  [setGuidesPersisted]
5402
7204
  );
5403
- const handleGuidePointerDown = useCallback6(
7205
+ const handleGuidePointerDown = useCallback8(
5404
7206
  (guide, event) => {
5405
7207
  const commit = createActionCommit();
5406
7208
  if (!enabled) return;
@@ -5426,7 +7228,7 @@ function MeasurerClient({
5426
7228
  setSelectedGuideIdsPersisted
5427
7229
  ]
5428
7230
  );
5429
- const handleGuidePointerUp = useCallback6(
7231
+ const handleGuidePointerUp = useCallback8(
5430
7232
  (guide, event) => {
5431
7233
  event.stopPropagation();
5432
7234
  clearGuideDragHold();
@@ -5437,16 +7239,37 @@ function MeasurerClient({
5437
7239
  },
5438
7240
  [clearGuideDragHold, setDraggingGuideId]
5439
7241
  );
7242
+ const overlayInteractive = enabled && !settingsOpen;
7243
+ const overlayGuides = useMemo2(() => {
7244
+ if (guides.length > 0) return guides;
7245
+ if (!settingsOpen || settingsTab !== "guides") return guides;
7246
+ return [
7247
+ {
7248
+ id: "__mesurer-preview-vertical",
7249
+ orientation: "vertical",
7250
+ position: ownerWindow.innerWidth / 2
7251
+ },
7252
+ {
7253
+ id: "__mesurer-preview-horizontal",
7254
+ orientation: "horizontal",
7255
+ position: ownerWindow.innerHeight / 2
7256
+ }
7257
+ ];
7258
+ }, [guides, ownerWindow, settingsOpen, settingsTab]);
5440
7259
  return createPortal(
5441
- /* @__PURE__ */ jsxs7(
7260
+ /* @__PURE__ */ jsxs11(
5442
7261
  "div",
5443
7262
  {
5444
7263
  ref: overlayRef,
5445
7264
  className: "mesurer-root msr:pointer-events-none msr:fixed msr:inset-0 msr:z-50",
5446
7265
  children: [
5447
- enabled && rulersVisible ? /* @__PURE__ */ jsx9(
7266
+ enabled && rulersVisible ? /* @__PURE__ */ jsx12(
5448
7267
  RulersOverlay,
5449
7268
  {
7269
+ ownerWindow,
7270
+ settings: settingsRulerSettings,
7271
+ interactive: !settingsOpen,
7272
+ forceVisible: settingsOpen,
5450
7273
  onStartGuide: startGuideFromRuler,
5451
7274
  onMoveGuide: moveGuideFromRuler,
5452
7275
  onFinishGuide: finishGuideFromRuler,
@@ -5455,12 +7278,13 @@ function MeasurerClient({
5455
7278
  selectedGuideIds
5456
7279
  }
5457
7280
  ) : null,
5458
- /* @__PURE__ */ jsx9(
7281
+ /* @__PURE__ */ jsx12(
5459
7282
  MeasurerOverlay,
5460
7283
  {
5461
7284
  enabled,
7285
+ interactive: overlayInteractive,
5462
7286
  toolMode,
5463
- guidePointerEvents: enabled && (toolMode !== "none" || rulersVisible),
7287
+ guidePointerEvents: overlayInteractive && (toolMode !== "none" || rulersVisible),
5464
7288
  guidesEnabled,
5465
7289
  altPressed,
5466
7290
  isDragging,
@@ -5481,13 +7305,14 @@ function MeasurerClient({
5481
7305
  optionPairOverlay,
5482
7306
  guideDistanceOverlay,
5483
7307
  optionContainerLines,
5484
- guides,
7308
+ guides: overlayGuides,
5485
7309
  hoverGuide,
5486
7310
  draggingGuideId,
5487
7311
  selectedGuideIds,
5488
7312
  guideColorActive,
5489
7313
  guideColorHover,
5490
7314
  guideColorDefault,
7315
+ guideStyle: settingsGuideStyle,
5491
7316
  onPointerDown: handlePointerDown,
5492
7317
  onPointerMove: handlePointerMove,
5493
7318
  onPointerUp: handlePointerUp,
@@ -5498,7 +7323,20 @@ function MeasurerClient({
5498
7323
  onGuidePointerCancel: handleGuidePointerUp
5499
7324
  }
5500
7325
  ),
5501
- /* @__PURE__ */ jsx9(
7326
+ /* @__PURE__ */ jsx12(
7327
+ ColorPicker,
7328
+ {
7329
+ active: colorPickerActive,
7330
+ sample: colorPickerSample,
7331
+ unsupported: colorPickerUnsupported,
7332
+ ownerWindow,
7333
+ toolbarRef,
7334
+ formats: settingsColorFormats,
7335
+ favoriteFormat: settingsColorClickFormat,
7336
+ onClose: () => setColorPickerActive(false)
7337
+ }
7338
+ ),
7339
+ /* @__PURE__ */ jsx12(
5502
7340
  Toolbar,
5503
7341
  {
5504
7342
  ref: toolbarRef,
@@ -5506,11 +7344,47 @@ function MeasurerClient({
5506
7344
  toolMode,
5507
7345
  setEnabled: setEnabledWithHistory,
5508
7346
  setToolMode: setToolModeWithHistory,
7347
+ xrayVisible,
7348
+ setXrayVisible,
5509
7349
  rulersVisible,
5510
7350
  setRulersVisible: setRulersVisiblePersisted,
5511
7351
  guideOrientation,
5512
7352
  setGuideOrientation: setGuideOrientationWithHistory,
5513
- onInteract: () => setToolbarActive(true)
7353
+ onInteract: () => setToolbarActive(true),
7354
+ colorPickerActive,
7355
+ setColorPickerActive,
7356
+ onColorPickerClick: openColorPicker,
7357
+ settingsOpen,
7358
+ setSettingsOpen,
7359
+ highlightColor: settingsHighlightColor,
7360
+ setHighlightColor: setSettingsHighlightColor,
7361
+ guideColor: settingsGuideColor,
7362
+ setGuideColor: setSettingsGuideColor,
7363
+ hoverHighlight: settingsHoverHighlight,
7364
+ setHoverHighlight: setSettingsHoverHighlight,
7365
+ persistOnReload: settingsPersistOnReload,
7366
+ setPersistOnReload: setSettingsPersistOnReload,
7367
+ colorPickerFormats: settingsColorFormats,
7368
+ setColorPickerFormats: setSettingsColorFormats,
7369
+ colorPickerClickFormat: settingsColorClickFormat,
7370
+ setColorPickerClickFormat: setSettingsColorClickFormat,
7371
+ snapEnabled,
7372
+ setSnapEnabled,
7373
+ snapGuidesEnabled,
7374
+ setSnapGuidesEnabled,
7375
+ selectNewGuideEnabled,
7376
+ setSelectNewGuideEnabled,
7377
+ multiMeasureEnabled,
7378
+ setMultiMeasureEnabled,
7379
+ guideStyle: settingsGuideStyle,
7380
+ setGuideStyle: setSettingsGuideStyle,
7381
+ rulerSettings: settingsRulerSettings,
7382
+ setRulerSettings: setSettingsRulerSettings,
7383
+ settingsTab,
7384
+ setSettingsTab,
7385
+ onToggleSettings: toggleSettings,
7386
+ onResetSettings: resetSettings,
7387
+ onClearWorkspace: clearWorkspace
5514
7388
  }
5515
7389
  )
5516
7390
  ]
@@ -5525,14 +7399,24 @@ function Measurer({
5525
7399
  hoverHighlightEnabled = true,
5526
7400
  persistOnReload = false,
5527
7401
  portalTarget,
5528
- persistKey
7402
+ persistKey,
7403
+ colorPickerFormats = ["hex", "rgb", "oklch"],
7404
+ colorPickerClickFormat = "hex",
7405
+ snapEnabled = true,
7406
+ snapGuidesEnabled = true,
7407
+ selectNewGuideEnabled = true,
7408
+ multiMeasureEnabled = false,
7409
+ guideStyle,
7410
+ rulerSettings,
7411
+ persistence,
7412
+ onPersistenceError
5529
7413
  }) {
5530
7414
  if (typeof document !== "undefined") {
5531
7415
  ensureMeasurerStyles(MESURER_STYLES, portalTarget);
5532
7416
  }
5533
7417
  const hydrated = useHydrated();
5534
7418
  if (!hydrated) return null;
5535
- return /* @__PURE__ */ jsx9(
7419
+ return /* @__PURE__ */ jsx12(
5536
7420
  MeasurerClient,
5537
7421
  {
5538
7422
  highlightColor,
@@ -5540,12 +7424,26 @@ function Measurer({
5540
7424
  hoverHighlightEnabled,
5541
7425
  persistOnReload,
5542
7426
  persistKey,
7427
+ colorPickerFormats,
7428
+ colorPickerClickFormat,
7429
+ snapEnabled,
7430
+ snapGuidesEnabled,
7431
+ selectNewGuideEnabled,
7432
+ multiMeasureEnabled,
7433
+ guideStyle: { ...DEFAULT_GUIDE_STYLE, ...guideStyle },
7434
+ rulerSettings: { ...DEFAULT_RULER_SETTINGS, ...rulerSettings },
7435
+ persistence,
7436
+ onPersistenceError,
5543
7437
  portalTarget: portalTarget ?? document.body
5544
7438
  }
5545
7439
  );
5546
7440
  }
5547
7441
  export {
7442
+ MESURER_STORAGE_VERSION,
5548
7443
  Measurer,
5549
7444
  TextInspector,
5550
- createTextInspector
7445
+ createLocalStoragePersistence,
7446
+ createTextInspector,
7447
+ normalizeStoredSettings,
7448
+ normalizeStoredWorkspace
5551
7449
  };