mesurer 0.0.9 → 0.0.10

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";
@@ -53,16 +53,17 @@ function ensureMeasurerStyles(cssText, target) {
53
53
  }
54
54
 
55
55
  // 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';
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-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: var(--color-ink-50);\n --msr-color-ink-200: var(--color-ink-200);\n --msr-color-ink-500: var(--color-ink-500);\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-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.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-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
57
 
58
58
  // components/toolbar.tsx
59
59
  import {
60
60
  forwardRef,
61
61
  memo,
62
- useCallback,
62
+ useCallback as useCallback2,
63
+ useEffect,
63
64
  useLayoutEffect,
64
- useRef,
65
- useState
65
+ useRef as useRef2,
66
+ useState as useState3
66
67
  } from "react";
67
68
 
68
69
  // core/utils.ts
@@ -70,9 +71,750 @@ var formatValue = (value) => Math.round(value);
70
71
  var cn = (...inputs) => inputs.filter(Boolean).join(" ");
71
72
  var createId = () => typeof crypto !== "undefined" && "randomUUID" in crypto ? crypto.randomUUID() : `${Date.now()}-${Math.random()}`;
72
73
 
74
+ // components/settings-panel.tsx
75
+ import { useState as useState2 } from "react";
76
+
77
+ // package.json
78
+ var package_default = {
79
+ name: "mesurer",
80
+ version: "0.0.10",
81
+ description: "Measure and align elements on any website.",
82
+ license: "MIT",
83
+ author: "Julien Thibeaut",
84
+ type: "module",
85
+ main: "./dist/index.cjs",
86
+ module: "./dist/index.js",
87
+ types: "./dist/index.d.ts",
88
+ exports: {
89
+ ".": {
90
+ types: "./dist/index.d.ts",
91
+ import: "./dist/index.js",
92
+ require: "./dist/index.cjs"
93
+ },
94
+ "./styles.css": "./dist/styles.css",
95
+ "./package.json": "./package.json"
96
+ },
97
+ files: [
98
+ "dist",
99
+ "dist/styles.css",
100
+ "CHANGELOG.md",
101
+ "README.md",
102
+ "LICENSE"
103
+ ],
104
+ sideEffects: [
105
+ "*.css"
106
+ ],
107
+ publishConfig: {
108
+ access: "public"
109
+ },
110
+ homepage: "https://mesurer.dev",
111
+ repository: {
112
+ type: "git",
113
+ url: "https://github.com/ibelick/mesurer.git"
114
+ },
115
+ bugs: {
116
+ url: "https://github.com/ibelick/mesurer/issues"
117
+ },
118
+ scripts: {
119
+ 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",
120
+ dev: "node scripts/dev.mjs",
121
+ typecheck: "tsc --noEmit",
122
+ prepublishOnly: "npm run typecheck && npm run build"
123
+ },
124
+ peerDependencies: {
125
+ react: ">=18.0.0",
126
+ "react-dom": ">=18.0.0"
127
+ },
128
+ devDependencies: {
129
+ "@tailwindcss/cli": "^4.2.2",
130
+ "@types/react": "^19.0.0",
131
+ "@types/react-dom": "^19.0.0",
132
+ react: "^19.0.0",
133
+ "react-dom": "^19.0.0",
134
+ tailwindcss: "^4.2.2",
135
+ tsup: "^8.5.0",
136
+ typescript: "^5.9.2"
137
+ },
138
+ keywords: [
139
+ "measurement",
140
+ "alignment"
141
+ ]
142
+ };
143
+
144
+ // core/colors.ts
145
+ var clamp = (value, min = 0, max = 1) => Math.min(max, Math.max(min, value));
146
+ var round = (value, digits = 2) => {
147
+ const factor = 10 ** digits;
148
+ return Math.round(value * factor) / factor;
149
+ };
150
+ var parseChannel = (value) => {
151
+ const trimmed = value.trim();
152
+ const numeric = trimmed.endsWith("%") ? trimmed.slice(0, -1).trim() : trimmed;
153
+ if (!/^[+-]?(?:\d+\.?\d*|\.\d+)(?:e[+-]?\d+)?$/i.test(numeric)) return null;
154
+ const parsed = Number.parseFloat(numeric);
155
+ if (!Number.isFinite(parsed)) return null;
156
+ if (trimmed.endsWith("%")) return clamp(parsed / 100) * 255;
157
+ return clamp(parsed / 255) * 255;
158
+ };
159
+ var parseAlpha = (value) => {
160
+ if (!value) return 1;
161
+ const trimmed = value.trim();
162
+ const numeric = trimmed.endsWith("%") ? trimmed.slice(0, -1).trim() : trimmed;
163
+ if (!/^[+-]?(?:\d+\.?\d*|\.\d+)(?:e[+-]?\d+)?$/i.test(numeric)) return null;
164
+ const parsed = Number.parseFloat(numeric);
165
+ if (!Number.isFinite(parsed)) return null;
166
+ return clamp(trimmed.endsWith("%") ? parsed / 100 : parsed);
167
+ };
168
+ var parseCssColor = (value) => {
169
+ const input = value.trim().toLowerCase();
170
+ if (!input || input === "transparent") return null;
171
+ if (input.startsWith("#")) {
172
+ const hex = input.slice(1);
173
+ const expanded = hex.length === 3 || hex.length === 4 ? hex.split("").map((digit) => digit + digit).join("") : hex;
174
+ if (![6, 8].includes(expanded.length) || !/^[\da-f]+$/.test(expanded)) return null;
175
+ return {
176
+ red: Number.parseInt(expanded.slice(0, 2), 16),
177
+ green: Number.parseInt(expanded.slice(2, 4), 16),
178
+ blue: Number.parseInt(expanded.slice(4, 6), 16),
179
+ alpha: expanded.length === 8 ? Number.parseInt(expanded.slice(6, 8), 16) / 255 : 1
180
+ };
181
+ }
182
+ const match = input.match(/^rgba?\((.*)\)$/);
183
+ if (!match) return null;
184
+ const channels = match[1].replace(/\//g, " ").split(/[ ,]+/).filter(Boolean);
185
+ if (channels.length < 3 || channels.length > 4) return null;
186
+ const red = parseChannel(channels[0]);
187
+ const green = parseChannel(channels[1]);
188
+ const blue = parseChannel(channels[2]);
189
+ const alpha = parseAlpha(channels[3]);
190
+ if (red === null || green === null || blue === null || alpha === null) return null;
191
+ return { red, green, blue, alpha };
192
+ };
193
+ var toByte = (value) => Math.round(clamp(value / 255) * 255);
194
+ var colorToHex = (color) => {
195
+ const hex = [color.red, color.green, color.blue].map((channel) => toByte(channel).toString(16).padStart(2, "0")).join("");
196
+ if (color.alpha >= 0.999) return `#${hex}`;
197
+ return `#${hex}${Math.round(color.alpha * 255).toString(16).padStart(2, "0")}`;
198
+ };
199
+ var colorToRgb = (color) => {
200
+ const channels = `${toByte(color.red)}, ${toByte(color.green)}, ${toByte(color.blue)}`;
201
+ return color.alpha >= 0.999 ? `rgb(${channels})` : `rgba(${channels}, ${round(color.alpha, 3)})`;
202
+ };
203
+ var colorToHsl = (color) => {
204
+ const red = color.red / 255;
205
+ const green = color.green / 255;
206
+ const blue = color.blue / 255;
207
+ const max = Math.max(red, green, blue);
208
+ const min = Math.min(red, green, blue);
209
+ const lightness = (max + min) / 2;
210
+ const delta = max - min;
211
+ let hue = 0;
212
+ let saturation = 0;
213
+ if (delta !== 0) {
214
+ saturation = delta / (1 - Math.abs(2 * lightness - 1));
215
+ if (max === red) hue = (green - blue) / delta % 6;
216
+ else if (max === green) hue = (blue - red) / delta + 2;
217
+ else hue = (red - green) / delta + 4;
218
+ hue *= 60;
219
+ if (hue < 0) hue += 360;
220
+ }
221
+ const result = `hsl(${round(hue, 1)} ${round(saturation * 100, 1)}% ${round(lightness * 100, 1)}%`;
222
+ return color.alpha >= 0.999 ? `${result})` : `${result} / ${round(color.alpha, 3)})`;
223
+ };
224
+ var linearize = (value) => {
225
+ const normalized = value / 255;
226
+ return normalized <= 0.04045 ? normalized / 12.92 : ((normalized + 0.055) / 1.055) ** 2.4;
227
+ };
228
+ var colorToOklch = (color) => {
229
+ const red = linearize(color.red);
230
+ const green = linearize(color.green);
231
+ const blue = linearize(color.blue);
232
+ const l = 0.4122214708 * red + 0.5363325363 * green + 0.0514459929 * blue;
233
+ const m = 0.2119034982 * red + 0.6806995451 * green + 0.1073969566 * blue;
234
+ const s = 0.0883024619 * red + 0.2817188376 * green + 0.6299787005 * blue;
235
+ const lRoot = Math.cbrt(l);
236
+ const mRoot = Math.cbrt(m);
237
+ const sRoot = Math.cbrt(s);
238
+ const lightness = 0.2104542553 * lRoot + 0.793617785 * mRoot - 0.0040720468 * sRoot;
239
+ const a = 1.9779984951 * lRoot - 2.428592205 * mRoot + 0.4505937099 * sRoot;
240
+ const b = 0.0259040371 * lRoot + 0.7827717662 * mRoot - 0.808675766 * sRoot;
241
+ const chroma = Math.sqrt(a * a + b * b);
242
+ let hue = Math.atan2(b, a) * 180 / Math.PI;
243
+ if (hue < 0) hue += 360;
244
+ const result = `oklch(${round(lightness, 3)} ${round(chroma, 3)} ${round(hue, 1)}`;
245
+ return color.alpha >= 0.999 ? `${result})` : `${result} / ${round(color.alpha, 3)})`;
246
+ };
247
+ var formatColor = (color, format) => {
248
+ if (format === "hex") return colorToHex(color);
249
+ if (format === "rgb") return colorToRgb(color);
250
+ if (format === "hsl") return colorToHsl(color);
251
+ return colorToOklch(color);
252
+ };
253
+
254
+ // components/tooltip.tsx
255
+ import { useCallback, useRef, useState } from "react";
256
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
257
+ var TOOLTIP_DELAY_MS = 800;
258
+ function Tooltip({
259
+ label,
260
+ shortcut,
261
+ visible,
262
+ instant = false,
263
+ side = "top",
264
+ className
265
+ }) {
266
+ return /* @__PURE__ */ jsxs(
267
+ "span",
268
+ {
269
+ role: "tooltip",
270
+ className: cn(
271
+ "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",
272
+ instant && "msr:transition-none",
273
+ side === "top" ? "msr:bottom-full msr:mb-2" : "msr:top-full msr:mt-2",
274
+ visible === void 0 ? null : visible ? "msr:opacity-100" : "msr:opacity-0",
275
+ className
276
+ ),
277
+ children: [
278
+ label,
279
+ shortcut ? /* @__PURE__ */ jsxs(Fragment, { children: [
280
+ " ",
281
+ /* @__PURE__ */ jsx("kbd", { className: "msr:text-white/60", children: shortcut })
282
+ ] }) : null
283
+ ]
284
+ }
285
+ );
286
+ }
287
+ function useTooltip() {
288
+ const [visibleTooltipId, setVisibleTooltipId] = useState(null);
289
+ const timerRef = useRef(null);
290
+ const instantRef = useRef(false);
291
+ const [tooltipInstant, setTooltipInstant] = useState(false);
292
+ const clearTimer = useCallback(() => {
293
+ if (timerRef.current === null) return;
294
+ window.clearTimeout(timerRef.current);
295
+ timerRef.current = null;
296
+ }, []);
297
+ const onTooltipEnter = useCallback((id) => {
298
+ clearTimer();
299
+ if (instantRef.current) {
300
+ setTooltipInstant(true);
301
+ setVisibleTooltipId(id);
302
+ return;
303
+ }
304
+ setTooltipInstant(false);
305
+ timerRef.current = window.setTimeout(() => {
306
+ setVisibleTooltipId(id);
307
+ instantRef.current = true;
308
+ timerRef.current = null;
309
+ }, TOOLTIP_DELAY_MS);
310
+ }, [clearTimer]);
311
+ const onTooltipLeave = useCallback(() => {
312
+ clearTimer();
313
+ setVisibleTooltipId(null);
314
+ }, [clearTimer]);
315
+ const onTooltipContainerLeave = useCallback(() => {
316
+ clearTimer();
317
+ setVisibleTooltipId(null);
318
+ instantRef.current = false;
319
+ setTooltipInstant(false);
320
+ }, [clearTimer]);
321
+ return {
322
+ visibleTooltipId,
323
+ tooltipInstant,
324
+ onTooltipEnter,
325
+ onTooltipLeave,
326
+ onTooltipContainerLeave
327
+ };
328
+ }
329
+
330
+ // components/settings-panel.tsx
331
+ import { Fragment as Fragment2, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
332
+ var COLOR_FORMATS = ["hex", "rgb", "hsl", "oklch"];
333
+ var GUIDE_PATTERNS = [
334
+ { value: "solid", label: "Solid" },
335
+ { value: "dashed", label: "Dashed" },
336
+ { value: "dotted", label: "Dotted" }
337
+ ];
338
+ function ControlShell({ left, right }) {
339
+ return /* @__PURE__ */ jsxs2(
340
+ "div",
341
+ {
342
+ className: "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",
343
+ children: [
344
+ /* @__PURE__ */ jsx2("div", { className: "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 }),
345
+ /* @__PURE__ */ jsx2("div", { className: "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 })
346
+ ]
347
+ }
348
+ );
349
+ }
350
+ function SettingsSwitch({ label, checked, onChange }) {
351
+ return /* @__PURE__ */ jsxs2(
352
+ "button",
353
+ {
354
+ type: "button",
355
+ role: "switch",
356
+ "aria-checked": checked,
357
+ className: "msr:col-span-2 msr:grid msr:h-6 msr:w-full msr:grid-cols-[78px_156px] msr:items-center msr:gap-3 msr:text-left msr:text-[12px] msr:leading-none msr:text-ink-700",
358
+ onClick: () => onChange(!checked),
359
+ children: [
360
+ /* @__PURE__ */ jsx2("span", { children: label }),
361
+ /* @__PURE__ */ jsx2(
362
+ "span",
363
+ {
364
+ "aria-hidden": "true",
365
+ style: { justifySelf: "end" },
366
+ className: cn(
367
+ "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",
368
+ checked ? "msr:border-[#0d99ff] msr:bg-[#0d99ff]" : "msr:border-ink-200 msr:bg-ink-50"
369
+ ),
370
+ children: /* @__PURE__ */ jsx2(
371
+ "span",
372
+ {
373
+ className: "msr:block msr:size-[10px] msr:shrink-0 msr:rounded-full msr:bg-white msr:shadow-sm msr:transition-transform",
374
+ style: { transform: `translateX(${checked ? 12 : 0}px)` }
375
+ }
376
+ )
377
+ }
378
+ )
379
+ ]
380
+ }
381
+ );
382
+ }
383
+ function SliderControl({
384
+ label,
385
+ min,
386
+ max,
387
+ step,
388
+ value,
389
+ onChange,
390
+ formatValue: formatValue4 = (currentValue) => String(currentValue),
391
+ parseInput = (input) => Number(input)
392
+ }) {
393
+ const thumbSize = 12;
394
+ const thumbInset = 8;
395
+ const percentage = (value - min) / (max - min) * 100;
396
+ const [draftValue, setDraftValue] = useState2(formatValue4(value));
397
+ const [editing, setEditing] = useState2(false);
398
+ const commitDraft = () => {
399
+ const parsed = parseInput(draftValue);
400
+ if (Number.isFinite(parsed)) {
401
+ onChange(Math.min(max, Math.max(min, parsed)));
402
+ }
403
+ const next = Number.isFinite(parsed) ? Math.min(max, Math.max(min, parsed)) : value;
404
+ setDraftValue(formatValue4(next));
405
+ setEditing(false);
406
+ };
407
+ const updateFromPointer = (event) => {
408
+ event.stopPropagation();
409
+ const rect = event.currentTarget.getBoundingClientRect();
410
+ const usableWidth = Math.max(1, rect.width - thumbInset * 2);
411
+ const ratio = Math.min(1, Math.max(0, (event.clientX - rect.left - thumbInset) / usableWidth));
412
+ const rawValue = min + ratio * (max - min);
413
+ const steppedValue = Math.round((rawValue - min) / step) * step + min;
414
+ onChange(Number(steppedValue.toFixed(4)));
415
+ };
416
+ 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: [
417
+ /* @__PURE__ */ jsx2("span", { className: "msr:text-[11px] msr:font-medium msr:text-ink-700", children: label }),
418
+ /* @__PURE__ */ jsx2(
419
+ ControlShell,
420
+ {
421
+ left: /* @__PURE__ */ jsxs2(
422
+ "div",
423
+ {
424
+ className: "msr:relative msr:min-w-0 msr:flex-1 msr:touch-none msr:select-none msr:px-2",
425
+ style: { height: 20 },
426
+ "data-slider-container": "true",
427
+ onPointerDown: (event) => {
428
+ event.stopPropagation();
429
+ event.currentTarget.setPointerCapture(event.pointerId);
430
+ updateFromPointer(event);
431
+ },
432
+ onPointerMove: (event) => {
433
+ if (event.currentTarget.hasPointerCapture(event.pointerId)) updateFromPointer(event);
434
+ },
435
+ onPointerUp: (event) => {
436
+ event.stopPropagation();
437
+ if (event.currentTarget.hasPointerCapture(event.pointerId)) event.currentTarget.releasePointerCapture(event.pointerId);
438
+ },
439
+ onPointerCancel: (event) => {
440
+ event.stopPropagation();
441
+ if (event.currentTarget.hasPointerCapture(event.pointerId)) event.currentTarget.releasePointerCapture(event.pointerId);
442
+ },
443
+ onClick: (event) => event.stopPropagation(),
444
+ children: [
445
+ /* @__PURE__ */ jsx2(
446
+ "div",
447
+ {
448
+ className: "msr:absolute msr:left-[8px] msr:right-[8px] msr:rounded-full",
449
+ style: { top: 8, height: 4, backgroundColor: "rgba(15, 23, 42, 0.16)" },
450
+ "aria-hidden": "true"
451
+ }
452
+ ),
453
+ /* @__PURE__ */ jsx2(
454
+ "div",
455
+ {
456
+ className: "msr:absolute msr:left-[8px] msr:rounded-full",
457
+ style: { top: 8, width: `calc(${percentage}% - ${percentage * thumbInset * 2 / 100}px)`, height: 4, backgroundColor: "#0d99ff" },
458
+ "aria-hidden": "true"
459
+ }
460
+ ),
461
+ /* @__PURE__ */ jsx2(
462
+ "div",
463
+ {
464
+ 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",
465
+ style: {
466
+ left: `calc(8px + (100% - 16px) * ${percentage / 100})`,
467
+ top: 4,
468
+ width: thumbSize,
469
+ height: thumbSize,
470
+ border: "0",
471
+ transform: "translateX(-50%)"
472
+ },
473
+ role: "slider",
474
+ tabIndex: 0,
475
+ "aria-label": label,
476
+ "aria-valuemin": min,
477
+ "aria-valuemax": max,
478
+ "aria-valuenow": value,
479
+ "aria-orientation": "horizontal",
480
+ onKeyDown: (event) => {
481
+ event.stopPropagation();
482
+ const direction = event.key === "ArrowRight" || event.key === "ArrowUp" ? 1 : event.key === "ArrowLeft" || event.key === "ArrowDown" ? -1 : 0;
483
+ if (event.key === "Home") onChange(min);
484
+ else if (event.key === "End") onChange(max);
485
+ else if (direction) {
486
+ event.preventDefault();
487
+ onChange(Number(Math.min(max, Math.max(min, value + direction * step)).toFixed(4)));
488
+ } else return;
489
+ event.preventDefault();
490
+ }
491
+ }
492
+ )
493
+ ]
494
+ }
495
+ ),
496
+ right: /* @__PURE__ */ jsx2(
497
+ "input",
498
+ {
499
+ type: "text",
500
+ "aria-label": `${label} value`,
501
+ 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",
502
+ style: { boxSizing: "border-box", borderRadius: "0 5px 5px 0", lineHeight: "1rem" },
503
+ value: editing ? draftValue : formatValue4(value),
504
+ onFocus: () => {
505
+ setDraftValue(formatValue4(value));
506
+ setEditing(true);
507
+ },
508
+ onChange: (event) => {
509
+ const nextDraft = event.target.value;
510
+ setDraftValue(nextDraft);
511
+ const next = parseInput(nextDraft);
512
+ if (Number.isFinite(next)) onChange(Math.min(max, Math.max(min, next)));
513
+ },
514
+ onPointerDown: (event) => event.stopPropagation(),
515
+ onClick: (event) => event.stopPropagation(),
516
+ onBlur: () => {
517
+ commitDraft();
518
+ },
519
+ onKeyDown: (event) => {
520
+ event.stopPropagation();
521
+ if (event.key === "ArrowUp" || event.key === "ArrowDown") {
522
+ event.preventDefault();
523
+ const current = parseInput(event.currentTarget.value);
524
+ const direction = event.key === "ArrowUp" ? 1 : -1;
525
+ const next = Number(
526
+ Math.min(max, Math.max(min, (Number.isFinite(current) ? current : value) + direction * step)).toFixed(4)
527
+ );
528
+ setDraftValue(formatValue4(next));
529
+ onChange(next);
530
+ return;
531
+ }
532
+ if (event.key === "Enter") {
533
+ event.preventDefault();
534
+ event.currentTarget.blur();
535
+ }
536
+ }
537
+ }
538
+ )
539
+ }
540
+ )
541
+ ] });
542
+ }
543
+ function ColorField({ label, value, fallback, ownerWindow, onChange }) {
544
+ const parsed = parseCssColor(value);
545
+ const canvas = ownerWindow.document.createElement("canvas");
546
+ const context = canvas.getContext("2d");
547
+ if (context) context.fillStyle = value;
548
+ const serialized = typeof context?.fillStyle === "string" ? context.fillStyle : "";
549
+ const converted = serialized ? parseCssColor(serialized) : null;
550
+ const inputValue = parsed ?? converted ? colorToHex(parsed ?? converted).slice(0, 7) : fallback;
551
+ const sample = parsed ?? converted ?? parseCssColor(fallback);
552
+ const hexValue = sample ? colorToHex({ ...sample, alpha: 1 }).slice(1).toUpperCase() : "000000";
553
+ const alphaValue = sample ? Math.round(sample.alpha * 100) : 100;
554
+ const [hexDraft, setHexDraft] = useState2(hexValue);
555
+ const [alphaDraft, setAlphaDraft] = useState2(String(alphaValue));
556
+ const [hexFocused, setHexFocused] = useState2(false);
557
+ const [alphaFocused, setAlphaFocused] = useState2(false);
558
+ const updateColor = (nextHex, nextAlpha) => {
559
+ if (!/^[\da-f]{6}$/i.test(nextHex)) return;
560
+ const nextSample = parseCssColor(`#${nextHex}`);
561
+ if (!nextSample) return;
562
+ onChange(colorToHex({ ...nextSample, alpha: Math.min(100, Math.max(0, nextAlpha)) / 100 }));
563
+ };
564
+ const swatchColor = ownerWindow.CSS?.supports("color", value) ? value : fallback;
565
+ 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: [
566
+ /* @__PURE__ */ jsx2("span", { children: label }),
567
+ /* @__PURE__ */ jsx2(
568
+ ControlShell,
569
+ {
570
+ left: /* @__PURE__ */ jsxs2(Fragment2, { children: [
571
+ /* @__PURE__ */ jsx2(
572
+ "span",
573
+ {
574
+ 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",
575
+ style: { backgroundColor: swatchColor },
576
+ children: /* @__PURE__ */ jsx2(
577
+ "input",
578
+ {
579
+ type: "color",
580
+ "aria-label": `${label} color picker`,
581
+ value: inputValue,
582
+ className: "msr:absolute msr:inset-0 msr:size-full msr:cursor-pointer msr:opacity-0",
583
+ onChange: (event) => onChange(event.target.value)
584
+ }
585
+ )
586
+ }
587
+ ),
588
+ /* @__PURE__ */ jsx2(
589
+ "input",
590
+ {
591
+ "aria-label": `${label} hex value`,
592
+ type: "text",
593
+ value: hexFocused ? hexDraft : hexValue,
594
+ maxLength: 6,
595
+ 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",
596
+ onFocus: () => {
597
+ setHexDraft(hexValue);
598
+ setHexFocused(true);
599
+ },
600
+ onBlur: () => setHexFocused(false),
601
+ onChange: (event) => {
602
+ const next = event.target.value.replace(/[^\da-f]/gi, "").slice(0, 6).toUpperCase();
603
+ setHexDraft(next);
604
+ updateColor(next, alphaFocused ? Number(alphaDraft) : alphaValue);
605
+ },
606
+ onPointerDown: (event) => event.stopPropagation(),
607
+ onClick: (event) => event.stopPropagation()
608
+ }
609
+ )
610
+ ] }),
611
+ right: /* @__PURE__ */ jsx2(
612
+ "input",
613
+ {
614
+ "aria-label": `${label} opacity value`,
615
+ type: "text",
616
+ inputMode: "numeric",
617
+ value: alphaFocused ? alphaDraft ? `${alphaDraft}%` : "" : `${alphaValue}%`,
618
+ maxLength: 4,
619
+ 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",
620
+ onFocus: () => {
621
+ setAlphaDraft(String(alphaValue));
622
+ setAlphaFocused(true);
623
+ },
624
+ onBlur: () => setAlphaFocused(false),
625
+ onChange: (event) => {
626
+ const next = event.target.value.replace(/\D/g, "").slice(0, 3);
627
+ setAlphaDraft(next);
628
+ updateColor(hexFocused ? hexDraft : hexValue, Number(next));
629
+ },
630
+ onKeyDown: (event) => {
631
+ if (event.key !== "ArrowUp" && event.key !== "ArrowDown") return;
632
+ event.preventDefault();
633
+ event.stopPropagation();
634
+ const current = Number.parseInt(alphaFocused ? alphaDraft : String(alphaValue), 10);
635
+ const direction = event.key === "ArrowUp" ? 1 : -1;
636
+ const next = Math.min(100, Math.max(0, (Number.isFinite(current) ? current : 0) + direction));
637
+ setAlphaDraft(String(next));
638
+ updateColor(hexFocused ? hexDraft : hexValue, next);
639
+ },
640
+ onPointerDown: (event) => event.stopPropagation(),
641
+ onClick: (event) => event.stopPropagation()
642
+ }
643
+ )
644
+ }
645
+ )
646
+ ] });
647
+ }
648
+ function SettingsPanel({
649
+ ownerWindow,
650
+ highlightColor,
651
+ setHighlightColor,
652
+ guideColor,
653
+ setGuideColor,
654
+ hoverHighlight,
655
+ setHoverHighlight,
656
+ persistOnReload,
657
+ setPersistOnReload,
658
+ colorFormats,
659
+ setColorFormats,
660
+ colorClickFormat,
661
+ setColorClickFormat,
662
+ snapEnabled,
663
+ setSnapEnabled,
664
+ snapGuidesEnabled,
665
+ setSnapGuidesEnabled,
666
+ selectNewGuideEnabled,
667
+ setSelectNewGuideEnabled,
668
+ multiMeasureEnabled,
669
+ setMultiMeasureEnabled,
670
+ guideStyle,
671
+ setGuideStyle,
672
+ rulerSettings,
673
+ setRulerSettings,
674
+ initialTab,
675
+ onResetSettings,
676
+ onClearWorkspace
677
+ }) {
678
+ const toggleFormat = (format) => {
679
+ setColorFormats((previous) => {
680
+ if (previous.includes(format)) {
681
+ if (previous.length === 1) return previous;
682
+ return previous.filter((item) => item !== format);
683
+ }
684
+ return [...previous, format];
685
+ });
686
+ };
687
+ const [activeTab, setActiveTab] = useState2(initialTab);
688
+ const patternTooltip = useTooltip();
689
+ 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: [
690
+ /* @__PURE__ */ jsx2("div", { className: "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: [
691
+ ["guides", "Guides"],
692
+ ["select", "Select"],
693
+ ["color-picker", "Color"],
694
+ ["rulers", "Rulers"],
695
+ ["general", "General"]
696
+ ].map(([value, label]) => /* @__PURE__ */ jsx2(
697
+ "button",
698
+ {
699
+ type: "button",
700
+ role: "tab",
701
+ "aria-selected": activeTab === value,
702
+ className: cn(
703
+ "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]",
704
+ 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"
705
+ ),
706
+ onClick: () => setActiveTab(value),
707
+ children: label
708
+ },
709
+ value
710
+ )) }),
711
+ 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: [
712
+ /* @__PURE__ */ jsx2(ColorField, { label: "Color", value: guideColor, fallback: "#f97316", ownerWindow, onChange: setGuideColor }),
713
+ /* @__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 })) }),
714
+ /* @__PURE__ */ jsxs2("div", { className: "msr:col-span-2 msr:grid msr:grid-cols-[78px_156px] msr:items-center msr:gap-3", children: [
715
+ /* @__PURE__ */ jsx2("span", { className: "msr:text-[12px] msr:text-ink-700", children: "Pattern" }),
716
+ /* @__PURE__ */ jsx2("div", { className: "msr:flex msr:gap-1", role: "radiogroup", "aria-label": "Guide pattern", onMouseLeave: patternTooltip.onTooltipContainerLeave, children: GUIDE_PATTERNS.map(({ value, label }) => {
717
+ const selected = guideStyle.pattern === value;
718
+ const tooltipId = `guide-pattern-${value}`;
719
+ return /* @__PURE__ */ jsxs2(
720
+ "button",
721
+ {
722
+ type: "button",
723
+ role: "radio",
724
+ "aria-label": `${label} guide pattern`,
725
+ "aria-checked": selected,
726
+ className: cn(
727
+ "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]",
728
+ selected ? "msr:border-[#0d99ff] msr:bg-[#0d99ff]/10" : "msr:border-ink-200 msr:bg-ink-50 msr:hover:bg-ink-100"
729
+ ),
730
+ onClick: () => setGuideStyle((style) => ({ ...style, pattern: value })),
731
+ onMouseEnter: () => patternTooltip.onTooltipEnter(tooltipId),
732
+ onFocus: () => patternTooltip.onTooltipEnter(tooltipId),
733
+ onBlur: patternTooltip.onTooltipLeave,
734
+ children: [
735
+ /* @__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") }),
736
+ /* @__PURE__ */ jsx2(Tooltip, { label, visible: patternTooltip.visibleTooltipId === tooltipId, instant: patternTooltip.tooltipInstant, className: "msr:z-10" })
737
+ ]
738
+ },
739
+ value
740
+ );
741
+ }) })
742
+ ] }),
743
+ guideStyle.pattern !== "solid" ? /* @__PURE__ */ jsxs2(Fragment2, { children: [
744
+ /* @__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 })) }),
745
+ /* @__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 })) })
746
+ ] }) : null,
747
+ /* @__PURE__ */ jsx2("div", { className: "msr:col-span-2", children: /* @__PURE__ */ jsx2(SettingsSwitch, { label: "Snap", checked: snapGuidesEnabled, onChange: setSnapGuidesEnabled }) }),
748
+ /* @__PURE__ */ jsx2("div", { className: "msr:col-span-2", children: /* @__PURE__ */ jsx2(SettingsSwitch, { label: "Highlight", checked: selectNewGuideEnabled, onChange: setSelectNewGuideEnabled }) })
749
+ ] }) : null,
750
+ 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: [
751
+ /* @__PURE__ */ jsx2(ColorField, { label: "Color", value: highlightColor, fallback: "#0d99ff", ownerWindow, onChange: setHighlightColor }),
752
+ /* @__PURE__ */ jsx2("div", { className: "msr:col-span-2", children: /* @__PURE__ */ jsx2(SettingsSwitch, { label: "Hover", checked: hoverHighlight, onChange: setHoverHighlight }) }),
753
+ /* @__PURE__ */ jsx2("div", { className: "msr:col-span-2", children: /* @__PURE__ */ jsx2(SettingsSwitch, { label: "Element snap", checked: snapEnabled, onChange: setSnapEnabled }) }),
754
+ /* @__PURE__ */ jsx2("div", { className: "msr:col-span-2", children: /* @__PURE__ */ jsx2(SettingsSwitch, { label: "Stack", checked: multiMeasureEnabled, onChange: setMultiMeasureEnabled }) })
755
+ ] }) : null,
756
+ 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: [
757
+ /* @__PURE__ */ jsxs2("div", { className: "msr:col-span-2 msr:grid msr:grid-cols-[78px_156px] msr:items-center msr:gap-3", children: [
758
+ /* @__PURE__ */ jsx2("span", { className: "msr:text-[12px] msr:text-ink-700", children: "Format" }),
759
+ /* @__PURE__ */ jsx2("div", { className: "msr:flex msr:min-w-0 msr:gap-1", children: COLOR_FORMATS.map((format) => /* @__PURE__ */ jsx2(
760
+ "button",
761
+ {
762
+ type: "button",
763
+ "aria-pressed": colorFormats.includes(format),
764
+ className: cn(
765
+ "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]",
766
+ 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"
767
+ ),
768
+ onClick: () => toggleFormat(format),
769
+ children: format
770
+ },
771
+ format
772
+ )) })
773
+ ] }),
774
+ /* @__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: [
775
+ "Copy",
776
+ /* @__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)) })
777
+ ] })
778
+ ] }) : null,
779
+ 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: [
780
+ /* @__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 })) }),
781
+ /* @__PURE__ */ jsx2("div", { className: "msr:col-span-2", children: /* @__PURE__ */ jsx2(SettingsSwitch, { label: "Edge reveal", checked: rulerSettings.edgeReveal, onChange: (edgeReveal) => setRulerSettings((settings) => ({ ...settings, edgeReveal })) }) })
782
+ ] }) : null,
783
+ 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: [
784
+ /* @__PURE__ */ jsx2("div", { className: "msr:col-span-2", children: /* @__PURE__ */ jsx2(SettingsSwitch, { label: "Persist", checked: persistOnReload, onChange: setPersistOnReload }) }),
785
+ /* @__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: [
786
+ /* @__PURE__ */ jsx2("span", { children: "Version" }),
787
+ /* @__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 })
788
+ ] }),
789
+ /* @__PURE__ */ jsxs2("div", { className: "msr:col-span-2 msr:flex msr:justify-end msr:gap-1", children: [
790
+ /* @__PURE__ */ jsx2(
791
+ "button",
792
+ {
793
+ type: "button",
794
+ "aria-label": "Reset settings to defaults",
795
+ 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]",
796
+ onClick: onResetSettings,
797
+ children: "Use defaults"
798
+ }
799
+ ),
800
+ /* @__PURE__ */ jsx2(
801
+ "button",
802
+ {
803
+ type: "button",
804
+ "aria-label": "Clear workspace",
805
+ 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]",
806
+ onClick: onClearWorkspace,
807
+ children: "Clear workspace"
808
+ }
809
+ )
810
+ ] })
811
+ ] }) : null
812
+ ] });
813
+ }
814
+
73
815
  // components/icons.tsx
74
- import { jsx } from "react/jsx-runtime";
75
- var IconBase = ({ size = 24, ...props }) => /* @__PURE__ */ jsx(
816
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
817
+ var IconBase = ({ size = 24, ...props }) => /* @__PURE__ */ jsx3(
76
818
  "svg",
77
819
  {
78
820
  xmlns: "http://www.w3.org/2000/svg",
@@ -80,12 +822,14 @@ var IconBase = ({ size = 24, ...props }) => /* @__PURE__ */ jsx(
80
822
  height: size,
81
823
  viewBox: "0 0 256 256",
82
824
  fill: "currentColor",
825
+ "aria-hidden": "true",
826
+ onDragStart: (event) => event.preventDefault(),
83
827
  ...props
84
828
  }
85
829
  );
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(
830
+ 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" }) });
831
+ 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" }) });
832
+ var XrayIcon = ({ size = 20, ...props }) => /* @__PURE__ */ jsx3(IconBase, { size, ...props, children: /* @__PURE__ */ jsx3(
89
833
  "path",
90
834
  {
91
835
  fill: "none",
@@ -97,7 +841,7 @@ var XrayIcon = ({ size = 20, ...props }) => /* @__PURE__ */ jsx(IconBase, { size
97
841
  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
842
  }
99
843
  ) });
100
- var RulersIcon = ({ size = 20, ...props }) => /* @__PURE__ */ jsx(IconBase, { size, ...props, children: /* @__PURE__ */ jsx(
844
+ var RulersIcon = ({ size = 20, ...props }) => /* @__PURE__ */ jsx3(IconBase, { size, ...props, children: /* @__PURE__ */ jsx3(
101
845
  "path",
102
846
  {
103
847
  fill: "none",
@@ -108,10 +852,10 @@ var RulersIcon = ({ size = 20, ...props }) => /* @__PURE__ */ jsx(IconBase, { si
108
852
  d: "M48 208V48h160M80 48v24m32-24v24m32-24v24m32-24v24M48 80h24M48 112h24m-24 32h24m-24 32h24"
109
853
  }
110
854
  ) });
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(
855
+ 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" }) });
856
+ 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" }) });
857
+ 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" }) });
858
+ var TextInspectorIcon = ({ size = 20, ...props }) => /* @__PURE__ */ jsx3(
115
859
  "svg",
116
860
  {
117
861
  xmlns: "http://www.w3.org/2000/svg",
@@ -119,8 +863,10 @@ var TextInspectorIcon = ({ size = 20, ...props }) => /* @__PURE__ */ jsx(
119
863
  height: size,
120
864
  viewBox: "0 0 256 256",
121
865
  fill: "currentColor",
866
+ "aria-hidden": "true",
867
+ onDragStart: (event) => event.preventDefault(),
122
868
  ...props,
123
- children: /* @__PURE__ */ jsx(
869
+ children: /* @__PURE__ */ jsx3(
124
870
  "text",
125
871
  {
126
872
  x: "50%",
@@ -136,9 +882,54 @@ var TextInspectorIcon = ({ size = 20, ...props }) => /* @__PURE__ */ jsx(
136
882
  )
137
883
  }
138
884
  );
885
+ var ColorPickerIcon = ({ size = 20, ...props }) => /* @__PURE__ */ jsx3(
886
+ "svg",
887
+ {
888
+ xmlns: "http://www.w3.org/2000/svg",
889
+ width: size,
890
+ height: size,
891
+ viewBox: "0 0 24 24",
892
+ fill: "none",
893
+ ...props,
894
+ children: /* @__PURE__ */ jsx3(
895
+ "path",
896
+ {
897
+ fill: "currentColor",
898
+ fillRule: "evenodd",
899
+ transform: "translate(-1.44 -1.44) scale(1.12)",
900
+ 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",
901
+ clipRule: "evenodd"
902
+ }
903
+ )
904
+ }
905
+ );
906
+ var GearIcon = ({ size = 20, ...props }) => /* @__PURE__ */ jsxs3(
907
+ "svg",
908
+ {
909
+ xmlns: "http://www.w3.org/2000/svg",
910
+ width: size,
911
+ height: size,
912
+ viewBox: "0 0 24 24",
913
+ fill: "none",
914
+ ...props,
915
+ children: [
916
+ /* @__PURE__ */ jsx3(
917
+ "path",
918
+ {
919
+ 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",
920
+ stroke: "currentColor",
921
+ strokeWidth: "1",
922
+ strokeLinecap: "round",
923
+ strokeLinejoin: "round"
924
+ }
925
+ ),
926
+ /* @__PURE__ */ jsx3("circle", { cx: "12", cy: "12", r: "2.5", stroke: "currentColor", strokeWidth: "1" })
927
+ ]
928
+ }
929
+ );
139
930
 
140
931
  // components/toolbar.tsx
141
- import { jsx as jsx2, jsxs } from "react/jsx-runtime";
932
+ import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
142
933
  var TOOLBAR_TOOLTIP_DELAY_MS = 800;
143
934
  var TOOLBAR_DRAG_SLOP = 6;
144
935
  var GUIDE_MENU_WIDTH = 176;
@@ -156,61 +947,43 @@ function ToolbarButton({
156
947
  onTooltipLeave,
157
948
  children
158
949
  }) {
159
- return /* @__PURE__ */ jsxs(
950
+ return /* @__PURE__ */ jsxs4(
160
951
  "div",
161
952
  {
162
953
  className: "msr:relative",
163
954
  onMouseEnter: () => onTooltipEnter(id),
164
955
  onMouseLeave: () => onTooltipLeave(id),
165
956
  children: [
166
- /* @__PURE__ */ jsx2(
957
+ /* @__PURE__ */ jsx4(
167
958
  "button",
168
959
  {
169
960
  type: "button",
170
- "aria-label": `${label} (${shortcut})`,
171
961
  "aria-pressed": active,
172
- title: `${label} (${shortcut})`,
962
+ "aria-label": `${label} (${shortcut})`,
173
963
  className: cn(
174
- "msr:flex msr:size-8 msr:items-center msr:justify-center msr:rounded-[8px] msr:outline-none",
964
+ "msr:flex msr:size-8 msr:select-none msr:items-center msr:justify-center msr:rounded-[8px] msr:outline-none",
175
965
  active ? "msr:bg-[#0d99ff] msr:text-white" : "msr:bg-transparent msr:text-black msr:hover:bg-black/4"
176
966
  ),
177
967
  onClick,
178
968
  children
179
969
  }
180
970
  ),
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
- )
971
+ /* @__PURE__ */ jsx4(Tooltip, { label, shortcut, visible: tooltipVisible, instant: tooltipInstant, side: tooltipSide })
199
972
  ]
200
973
  }
201
974
  );
202
975
  }
203
976
  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(() => {
977
+ const [visibleTooltipId, setVisibleTooltipId] = useState3(null);
978
+ const timerRef = useRef2(null);
979
+ const instantRef = useRef2(false);
980
+ const [tooltipInstant, setTooltipInstant] = useState3(false);
981
+ const clearTimer = useCallback2(() => {
209
982
  if (timerRef.current === null) return;
210
983
  window.clearTimeout(timerRef.current);
211
984
  timerRef.current = null;
212
985
  }, []);
213
- const onTooltipEnter = useCallback(
986
+ const onTooltipEnter = useCallback2(
214
987
  (id) => {
215
988
  clearTimer();
216
989
  if (instantRef.current) {
@@ -227,14 +1000,14 @@ function useToolbarTooltip() {
227
1000
  },
228
1001
  [clearTimer]
229
1002
  );
230
- const onTooltipLeave = useCallback(
1003
+ const onTooltipLeave = useCallback2(
231
1004
  (id) => {
232
1005
  clearTimer();
233
1006
  setVisibleTooltipId((prev) => prev === id ? null : prev);
234
1007
  },
235
1008
  [clearTimer]
236
1009
  );
237
- const onToolbarLeave = useCallback(() => {
1010
+ const onToolbarLeave = useCallback2(() => {
238
1011
  clearTimer();
239
1012
  setVisibleTooltipId(null);
240
1013
  instantRef.current = false;
@@ -249,10 +1022,11 @@ function useToolbarTooltip() {
249
1022
  };
250
1023
  }
251
1024
  function useToolbarDrag(initialPosition, eventTarget) {
252
- const [position, setPosition] = useState(initialPosition);
253
- const suppressClickRef = useRef(false);
254
- const detachListenersRef = useRef(null);
255
- const dragRef = useRef({
1025
+ const [position, setPosition] = useState3(initialPosition);
1026
+ const suppressClickRef = useRef2(false);
1027
+ const previousUserSelectRef = useRef2(null);
1028
+ const detachListenersRef = useRef2(null);
1029
+ const dragRef = useRef2({
256
1030
  active: false,
257
1031
  didDrag: false,
258
1032
  pointerId: -1,
@@ -263,12 +1037,28 @@ function useToolbarDrag(initialPosition, eventTarget) {
263
1037
  width: 0,
264
1038
  height: 0
265
1039
  });
266
- const onPointerDown = useCallback(
1040
+ const disableTextSelection = useCallback2(() => {
1041
+ if (previousUserSelectRef.current !== null) return;
1042
+ const root = eventTarget.document.documentElement;
1043
+ previousUserSelectRef.current = root.style.userSelect;
1044
+ root.style.setProperty("user-select", "none", "important");
1045
+ }, [eventTarget]);
1046
+ const restoreTextSelection = useCallback2(() => {
1047
+ const previous = previousUserSelectRef.current;
1048
+ if (previous === null) return;
1049
+ const root = eventTarget.document.documentElement;
1050
+ root.style.userSelect = previous;
1051
+ previousUserSelectRef.current = null;
1052
+ }, [eventTarget]);
1053
+ useEffect(() => restoreTextSelection, [restoreTextSelection]);
1054
+ const onPointerDown = useCallback2(
267
1055
  (event) => {
268
1056
  if (event.button !== 0) return;
1057
+ disableTextSelection();
269
1058
  if (detachListenersRef.current) {
270
1059
  detachListenersRef.current();
271
1060
  detachListenersRef.current = null;
1061
+ restoreTextSelection();
272
1062
  }
273
1063
  const state = dragRef.current;
274
1064
  state.active = false;
@@ -303,6 +1093,7 @@ function useToolbarDrag(initialPosition, eventTarget) {
303
1093
  if (current.pointerId !== endEvent.pointerId && current.pointerId !== -1)
304
1094
  return;
305
1095
  suppressClickRef.current = current.didDrag;
1096
+ restoreTextSelection();
306
1097
  current.active = false;
307
1098
  current.didDrag = false;
308
1099
  current.pointerId = -1;
@@ -320,9 +1111,9 @@ function useToolbarDrag(initialPosition, eventTarget) {
320
1111
  eventTarget.removeEventListener("pointercancel", handlePointerEnd);
321
1112
  };
322
1113
  },
323
- [eventTarget, position.x, position.y]
1114
+ [disableTextSelection, eventTarget, position.x, position.y, restoreTextSelection]
324
1115
  );
325
- const onClickCapture = useCallback(
1116
+ const onClickCapture = useCallback2(
326
1117
  (event) => {
327
1118
  if (!suppressClickRef.current) return;
328
1119
  event.preventDefault();
@@ -337,12 +1128,46 @@ function ToolbarComponent({
337
1128
  toolMode,
338
1129
  setEnabled,
339
1130
  setToolMode,
1131
+ xrayVisible,
1132
+ setXrayVisible,
340
1133
  rulersVisible,
341
1134
  setRulersVisible,
342
1135
  guideOrientation,
343
1136
  setGuideOrientation,
344
1137
  onInteract,
345
- eventTarget
1138
+ eventTarget,
1139
+ colorPickerActive,
1140
+ setColorPickerActive,
1141
+ onColorPickerClick,
1142
+ settingsOpen,
1143
+ setSettingsOpen,
1144
+ highlightColor,
1145
+ setHighlightColor,
1146
+ guideColor,
1147
+ setGuideColor,
1148
+ hoverHighlight,
1149
+ setHoverHighlight,
1150
+ persistOnReload,
1151
+ setPersistOnReload,
1152
+ colorPickerFormats,
1153
+ setColorPickerFormats,
1154
+ colorPickerClickFormat,
1155
+ setColorPickerClickFormat,
1156
+ snapEnabled,
1157
+ setSnapEnabled,
1158
+ snapGuidesEnabled,
1159
+ setSnapGuidesEnabled,
1160
+ selectNewGuideEnabled,
1161
+ setSelectNewGuideEnabled,
1162
+ multiMeasureEnabled,
1163
+ setMultiMeasureEnabled,
1164
+ guideStyle,
1165
+ setGuideStyle,
1166
+ rulerSettings,
1167
+ setRulerSettings,
1168
+ initialSettingsTab,
1169
+ onResetSettings,
1170
+ onClearWorkspace
346
1171
  }, ref) {
347
1172
  const { position, onPointerDown, onClickCapture } = useToolbarDrag({
348
1173
  x: 16,
@@ -355,11 +1180,13 @@ function ToolbarComponent({
355
1180
  onTooltipLeave,
356
1181
  onToolbarLeave
357
1182
  } = 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(() => {
1183
+ const [guideMenuOpen, setGuideMenuOpen] = useState3(false);
1184
+ const settingsRef = useRef2(null);
1185
+ const guideMenuRef = useRef2(null);
1186
+ const [activeMenuIndex, setActiveMenuIndex] = useState3(0);
1187
+ const [menuAlign, setMenuAlign] = useState3("right");
1188
+ const tooltipsEnabled = !guideMenuOpen && !settingsOpen;
1189
+ const updateMenuAlign = useCallback2(() => {
363
1190
  const anchorRect = guideMenuRef.current?.getBoundingClientRect();
364
1191
  if (!anchorRect) return;
365
1192
  const rightAlignedLeft = anchorRect.right - GUIDE_MENU_WIDTH;
@@ -379,34 +1206,50 @@ function ToolbarComponent({
379
1206
  const nearBottom = viewportHeight > 0 && position.y > viewportHeight - 56;
380
1207
  const tooltipSide = nearTop && !nearBottom ? "bottom" : "top";
381
1208
  const menuSide = nearBottom ? "top" : "bottom";
382
- const selectMode = useCallback(() => {
1209
+ const selectMode = useCallback2(() => {
383
1210
  setEnabled(true);
1211
+ setColorPickerActive(false);
384
1212
  setToolMode((prev) => prev === "select" ? "none" : "select");
385
1213
  onInteract();
386
- }, [onInteract, setEnabled, setToolMode]);
387
- const guidesMode = useCallback(() => {
1214
+ }, [onInteract, setColorPickerActive, setEnabled, setToolMode]);
1215
+ const guidesMode = useCallback2(() => {
388
1216
  setEnabled(true);
1217
+ setColorPickerActive(false);
389
1218
  setToolMode((prev) => prev === "guides" ? "none" : "guides");
390
1219
  onInteract();
391
- }, [onInteract, setEnabled, setToolMode]);
392
- const textInspectorMode = useCallback(() => {
1220
+ }, [onInteract, setColorPickerActive, setEnabled, setToolMode]);
1221
+ const textInspectorMode = useCallback2(() => {
393
1222
  setEnabled(true);
1223
+ setColorPickerActive(false);
394
1224
  setToolMode(
395
1225
  (prev) => prev === "text-inspector" ? "none" : "text-inspector"
396
1226
  );
397
1227
  onInteract();
398
- }, [onInteract, setEnabled, setToolMode]);
399
- const xrayMode = useCallback(() => {
1228
+ }, [onInteract, setColorPickerActive, setEnabled, setToolMode]);
1229
+ const xrayMode = useCallback2(() => {
400
1230
  setEnabled(true);
401
- setToolMode((prev) => prev === "xray" ? "none" : "xray");
1231
+ setColorPickerActive(false);
1232
+ setXrayVisible((prev) => !prev);
402
1233
  onInteract();
403
- }, [onInteract, setEnabled, setToolMode]);
404
- const rulersMode = useCallback(() => {
1234
+ }, [onInteract, setColorPickerActive, setEnabled, setXrayVisible]);
1235
+ const colorPickerMode = useCallback2(() => {
405
1236
  setEnabled(true);
1237
+ setToolMode("none");
1238
+ if (colorPickerActive) {
1239
+ setColorPickerActive(false);
1240
+ } else {
1241
+ setColorPickerActive(true);
1242
+ onColorPickerClick();
1243
+ }
1244
+ onInteract();
1245
+ }, [colorPickerActive, onColorPickerClick, onInteract, setColorPickerActive, setEnabled, setToolMode]);
1246
+ const rulersMode = useCallback2(() => {
1247
+ setEnabled(true);
1248
+ setColorPickerActive(false);
406
1249
  setRulersVisible((prev) => !prev);
407
1250
  onInteract();
408
- }, [onInteract, setEnabled, setRulersVisible]);
409
- const selectGuideOrientation = useCallback(
1251
+ }, [onInteract, setColorPickerActive, setEnabled, setRulersVisible]);
1252
+ const selectGuideOrientation = useCallback2(
410
1253
  (orientation) => {
411
1254
  setEnabled(true);
412
1255
  setToolMode("guides");
@@ -417,29 +1260,33 @@ function ToolbarComponent({
417
1260
  [onInteract, setEnabled, setGuideOrientation, setToolMode]
418
1261
  );
419
1262
  useLayoutEffect(() => {
420
- if (!guideMenuOpen) return;
421
- const frame = eventTarget.requestAnimationFrame(() => {
1263
+ if (!guideMenuOpen && !settingsOpen) return;
1264
+ const frame = guideMenuOpen ? eventTarget.requestAnimationFrame(() => {
422
1265
  guideMenuRef.current?.querySelector("[role='menu']")?.focus();
423
- });
1266
+ }) : 0;
424
1267
  const handlePointerDown = (event) => {
425
- const menu = guideMenuRef.current;
426
- if (!menu) return;
427
1268
  const path = event.composedPath();
428
- if (path.includes(menu)) return;
429
- setGuideMenuOpen(false);
1269
+ if (guideMenuOpen) {
1270
+ const menu = guideMenuRef.current;
1271
+ if (menu && !path.includes(menu)) setGuideMenuOpen(false);
1272
+ }
1273
+ if (settingsOpen) {
1274
+ const settings = settingsRef.current;
1275
+ if (settings && !path.includes(settings)) setSettingsOpen(false);
1276
+ }
430
1277
  };
431
1278
  const handleResize = () => {
432
- updateMenuAlign();
1279
+ if (guideMenuOpen) updateMenuAlign();
433
1280
  };
434
1281
  eventTarget.addEventListener("pointerdown", handlePointerDown);
435
- eventTarget.addEventListener("resize", handleResize);
1282
+ if (guideMenuOpen) eventTarget.addEventListener("resize", handleResize);
436
1283
  return () => {
437
- eventTarget.cancelAnimationFrame(frame);
1284
+ if (frame) eventTarget.cancelAnimationFrame(frame);
438
1285
  eventTarget.removeEventListener("pointerdown", handlePointerDown);
439
1286
  eventTarget.removeEventListener("resize", handleResize);
440
1287
  };
441
- }, [eventTarget, guideMenuOpen, guideOrientation, updateMenuAlign]);
442
- return /* @__PURE__ */ jsxs(
1288
+ }, [eventTarget, guideMenuOpen, guideOrientation, settingsOpen, updateMenuAlign]);
1289
+ return /* @__PURE__ */ jsxs4(
443
1290
  "div",
444
1291
  {
445
1292
  ref,
@@ -452,7 +1299,7 @@ function ToolbarComponent({
452
1299
  onClickCapture,
453
1300
  onMouseLeave: onToolbarLeave,
454
1301
  children: [
455
- /* @__PURE__ */ jsx2(
1302
+ /* @__PURE__ */ jsx4(
456
1303
  ToolbarButton,
457
1304
  {
458
1305
  id: "select",
@@ -460,31 +1307,47 @@ function ToolbarComponent({
460
1307
  label: "Select",
461
1308
  shortcut: "S",
462
1309
  onClick: selectMode,
463
- tooltipVisible: visibleTooltipId === "select",
1310
+ tooltipVisible: tooltipsEnabled && visibleTooltipId === "select",
464
1311
  tooltipInstant,
465
1312
  tooltipSide,
466
1313
  onTooltipEnter,
467
1314
  onTooltipLeave,
468
- children: /* @__PURE__ */ jsx2(CursorIcon, { size: 20 })
1315
+ children: /* @__PURE__ */ jsx4(CursorIcon, { size: 20 })
469
1316
  }
470
1317
  ),
471
- /* @__PURE__ */ jsx2(
1318
+ /* @__PURE__ */ jsx4(
472
1319
  ToolbarButton,
473
1320
  {
474
1321
  id: "xray",
475
- active: toolMode === "xray",
1322
+ active: xrayVisible,
476
1323
  label: "X-ray",
477
1324
  shortcut: "X",
478
1325
  onClick: xrayMode,
479
- tooltipVisible: visibleTooltipId === "xray",
1326
+ tooltipVisible: tooltipsEnabled && visibleTooltipId === "xray",
480
1327
  tooltipInstant,
481
1328
  tooltipSide,
482
1329
  onTooltipEnter,
483
1330
  onTooltipLeave,
484
- children: /* @__PURE__ */ jsx2(XrayIcon, { size: 20 })
1331
+ children: /* @__PURE__ */ jsx4(XrayIcon, { size: 20 })
485
1332
  }
486
1333
  ),
487
- /* @__PURE__ */ jsx2(
1334
+ /* @__PURE__ */ jsx4(
1335
+ ToolbarButton,
1336
+ {
1337
+ id: "color-picker",
1338
+ active: colorPickerActive,
1339
+ label: "Color picker",
1340
+ shortcut: "P",
1341
+ onClick: colorPickerMode,
1342
+ tooltipVisible: tooltipsEnabled && visibleTooltipId === "color-picker",
1343
+ tooltipInstant,
1344
+ tooltipSide,
1345
+ onTooltipEnter,
1346
+ onTooltipLeave,
1347
+ children: /* @__PURE__ */ jsx4(ColorPickerIcon, { size: 20, "aria-hidden": "true" })
1348
+ }
1349
+ ),
1350
+ /* @__PURE__ */ jsx4(
488
1351
  ToolbarButton,
489
1352
  {
490
1353
  id: "rulers",
@@ -492,15 +1355,15 @@ function ToolbarComponent({
492
1355
  label: "Rulers",
493
1356
  shortcut: "R",
494
1357
  onClick: rulersMode,
495
- tooltipVisible: visibleTooltipId === "rulers",
1358
+ tooltipVisible: tooltipsEnabled && visibleTooltipId === "rulers",
496
1359
  tooltipInstant,
497
1360
  tooltipSide,
498
1361
  onTooltipEnter,
499
1362
  onTooltipLeave,
500
- children: /* @__PURE__ */ jsx2(RulersIcon, { size: 20 })
1363
+ children: /* @__PURE__ */ jsx4(RulersIcon, { size: 20 })
501
1364
  }
502
1365
  ),
503
- /* @__PURE__ */ jsx2(
1366
+ /* @__PURE__ */ jsx4(
504
1367
  ToolbarButton,
505
1368
  {
506
1369
  id: "text-inspector",
@@ -508,15 +1371,15 @@ function ToolbarComponent({
508
1371
  label: "Text inspector",
509
1372
  shortcut: "A",
510
1373
  onClick: textInspectorMode,
511
- tooltipVisible: visibleTooltipId === "text-inspector",
1374
+ tooltipVisible: tooltipsEnabled && visibleTooltipId === "text-inspector",
512
1375
  tooltipInstant,
513
1376
  tooltipSide,
514
1377
  onTooltipEnter,
515
1378
  onTooltipLeave,
516
- children: /* @__PURE__ */ jsx2(TextInspectorIcon, { size: 20, "aria-hidden": "true" })
1379
+ children: /* @__PURE__ */ jsx4(TextInspectorIcon, { size: 20, "aria-hidden": "true" })
517
1380
  }
518
1381
  ),
519
- /* @__PURE__ */ jsx2(
1382
+ /* @__PURE__ */ jsx4(
520
1383
  ToolbarButton,
521
1384
  {
522
1385
  id: "guides",
@@ -524,12 +1387,12 @@ function ToolbarComponent({
524
1387
  label: "Guides",
525
1388
  shortcut: "G",
526
1389
  onClick: guidesMode,
527
- tooltipVisible: visibleTooltipId === "guides",
1390
+ tooltipVisible: tooltipsEnabled && visibleTooltipId === "guides",
528
1391
  tooltipInstant,
529
1392
  tooltipSide,
530
1393
  onTooltipEnter,
531
1394
  onTooltipLeave,
532
- children: /* @__PURE__ */ jsx2(
1395
+ children: /* @__PURE__ */ jsx4(
533
1396
  RulerIcon,
534
1397
  {
535
1398
  size: 20,
@@ -540,7 +1403,7 @@ function ToolbarComponent({
540
1403
  )
541
1404
  }
542
1405
  ),
543
- /* @__PURE__ */ jsxs(
1406
+ /* @__PURE__ */ jsxs4(
544
1407
  "div",
545
1408
  {
546
1409
  className: "msr:group msr:relative msr:-ml-1 msr:flex msr:items-stretch",
@@ -548,12 +1411,11 @@ function ToolbarComponent({
548
1411
  onMouseEnter: () => onTooltipEnter("guide-menu"),
549
1412
  onMouseLeave: () => onTooltipLeave("guide-menu"),
550
1413
  children: [
551
- /* @__PURE__ */ jsx2(
1414
+ /* @__PURE__ */ jsx4(
552
1415
  "button",
553
1416
  {
554
1417
  type: "button",
555
1418
  "aria-label": "Guide orientation menu",
556
- title: "Guide orientation",
557
1419
  className: cn(
558
1420
  "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
1421
  guideMenuOpen ? "msr:bg-black/10 msr:text-black" : "msr:text-black"
@@ -568,21 +1430,21 @@ function ToolbarComponent({
568
1430
  return !prev;
569
1431
  });
570
1432
  },
571
- children: /* @__PURE__ */ jsx2(CaretDownIcon, { size: 8 })
1433
+ children: /* @__PURE__ */ jsx4(CaretDownIcon, { size: 8 })
572
1434
  }
573
1435
  ),
574
- /* @__PURE__ */ jsx2(
1436
+ /* @__PURE__ */ jsx4(
575
1437
  "span",
576
1438
  {
577
1439
  className: cn(
578
1440
  "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
1441
  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"
1442
+ visibleTooltipId === "guide-menu" && tooltipsEnabled ? "msr:opacity-100" : "msr:opacity-0"
581
1443
  ),
582
1444
  children: "Orientation Guide"
583
1445
  }
584
1446
  ),
585
- guideMenuOpen ? /* @__PURE__ */ jsxs(
1447
+ guideMenuOpen ? /* @__PURE__ */ jsxs4(
586
1448
  "div",
587
1449
  {
588
1450
  className: cn(
@@ -623,7 +1485,7 @@ function ToolbarComponent({
623
1485
  }
624
1486
  },
625
1487
  children: [
626
- /* @__PURE__ */ jsxs(
1488
+ /* @__PURE__ */ jsxs4(
627
1489
  "button",
628
1490
  {
629
1491
  type: "button",
@@ -633,7 +1495,7 @@ function ToolbarComponent({
633
1495
  ),
634
1496
  onClick: () => selectGuideOrientation("horizontal"),
635
1497
  children: [
636
- /* @__PURE__ */ jsx2(
1498
+ /* @__PURE__ */ jsx4(
637
1499
  CheckIcon,
638
1500
  {
639
1501
  size: 12,
@@ -642,13 +1504,13 @@ function ToolbarComponent({
642
1504
  )
643
1505
  }
644
1506
  ),
645
- /* @__PURE__ */ jsx2(MinusIcon, { size: 12 }),
646
- /* @__PURE__ */ jsx2("span", { className: "msr:flex-1", children: "Horizontal" }),
647
- /* @__PURE__ */ jsx2("span", { children: "H" })
1507
+ /* @__PURE__ */ jsx4(MinusIcon, { size: 12 }),
1508
+ /* @__PURE__ */ jsx4("span", { className: "msr:flex-1", children: "Horizontal" }),
1509
+ /* @__PURE__ */ jsx4("span", { children: "H" })
648
1510
  ]
649
1511
  }
650
1512
  ),
651
- /* @__PURE__ */ jsxs(
1513
+ /* @__PURE__ */ jsxs4(
652
1514
  "button",
653
1515
  {
654
1516
  type: "button",
@@ -658,7 +1520,7 @@ function ToolbarComponent({
658
1520
  ),
659
1521
  onClick: () => selectGuideOrientation("vertical"),
660
1522
  children: [
661
- /* @__PURE__ */ jsx2(
1523
+ /* @__PURE__ */ jsx4(
662
1524
  CheckIcon,
663
1525
  {
664
1526
  size: 12,
@@ -667,9 +1529,9 @@ function ToolbarComponent({
667
1529
  )
668
1530
  }
669
1531
  ),
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" })
1532
+ /* @__PURE__ */ jsx4(MinusIcon, { size: 12, className: "msr:rotate-90" }),
1533
+ /* @__PURE__ */ jsx4("span", { className: "msr:flex-1", children: "Vertical" }),
1534
+ /* @__PURE__ */ jsx4("span", { children: "V" })
673
1535
  ]
674
1536
  }
675
1537
  )
@@ -678,24 +1540,299 @@ function ToolbarComponent({
678
1540
  ) : null
679
1541
  ]
680
1542
  }
1543
+ ),
1544
+ /* @__PURE__ */ jsxs4("div", { ref: settingsRef, className: "msr:relative msr:flex", children: [
1545
+ /* @__PURE__ */ jsx4(
1546
+ ToolbarButton,
1547
+ {
1548
+ id: "settings",
1549
+ active: settingsOpen,
1550
+ label: "Settings",
1551
+ shortcut: "\u2318/Ctrl+,",
1552
+ onClick: () => {
1553
+ onInteract();
1554
+ setSettingsOpen((previous) => !previous);
1555
+ },
1556
+ tooltipVisible: tooltipsEnabled && visibleTooltipId === "settings",
1557
+ tooltipInstant,
1558
+ tooltipSide,
1559
+ onTooltipEnter,
1560
+ onTooltipLeave,
1561
+ children: /* @__PURE__ */ jsx4(GearIcon, { size: 20, "aria-hidden": "true" })
1562
+ }
1563
+ ),
1564
+ settingsOpen ? /* @__PURE__ */ jsx4(
1565
+ "div",
1566
+ {
1567
+ className: cn(
1568
+ "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",
1569
+ menuSide === "bottom" ? "msr:top-full msr:mt-2" : "msr:bottom-full msr:mb-2"
1570
+ ),
1571
+ "data-mesurer-inspector-ui": "true",
1572
+ role: "dialog",
1573
+ "aria-label": "Settings",
1574
+ onPointerDown: (event) => event.stopPropagation(),
1575
+ onPointerMove: (event) => event.stopPropagation(),
1576
+ onPointerUp: (event) => event.stopPropagation(),
1577
+ onClick: (event) => event.stopPropagation(),
1578
+ children: /* @__PURE__ */ jsx4(
1579
+ SettingsPanel,
1580
+ {
1581
+ ownerWindow: eventTarget,
1582
+ highlightColor,
1583
+ setHighlightColor,
1584
+ guideColor,
1585
+ setGuideColor,
1586
+ hoverHighlight,
1587
+ setHoverHighlight,
1588
+ persistOnReload,
1589
+ setPersistOnReload,
1590
+ colorFormats: colorPickerFormats,
1591
+ setColorFormats: setColorPickerFormats,
1592
+ colorClickFormat: colorPickerClickFormat,
1593
+ setColorClickFormat: setColorPickerClickFormat,
1594
+ snapEnabled,
1595
+ setSnapEnabled,
1596
+ snapGuidesEnabled,
1597
+ setSnapGuidesEnabled,
1598
+ selectNewGuideEnabled,
1599
+ setSelectNewGuideEnabled,
1600
+ multiMeasureEnabled,
1601
+ setMultiMeasureEnabled,
1602
+ guideStyle,
1603
+ setGuideStyle,
1604
+ rulerSettings,
1605
+ setRulerSettings,
1606
+ initialTab: initialSettingsTab,
1607
+ onResetSettings,
1608
+ onClearWorkspace
1609
+ }
1610
+ )
1611
+ }
1612
+ ) : null
1613
+ ] })
1614
+ ]
1615
+ }
1616
+ );
1617
+ }
1618
+ var Toolbar = memo(forwardRef(ToolbarComponent));
1619
+
1620
+ // components/color-picker.tsx
1621
+ import { useCallback as useCallback3, useLayoutEffect as useLayoutEffect2, useRef as useRef3, useState as useState4 } from "react";
1622
+ import { Fragment as Fragment3, jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
1623
+ function CopyableColorValue({
1624
+ id,
1625
+ value,
1626
+ copiedId,
1627
+ onCopy,
1628
+ onTooltipEnter,
1629
+ tooltip,
1630
+ className
1631
+ }) {
1632
+ const copied = copiedId === id;
1633
+ const showTooltip = tooltip.visibleTooltipId === id || copied && tooltip.visibleTooltipId === null;
1634
+ return /* @__PURE__ */ jsxs5(
1635
+ "span",
1636
+ {
1637
+ className: "msr:relative msr:inline-flex",
1638
+ onMouseLeave: tooltip.onTooltipLeave,
1639
+ children: [
1640
+ /* @__PURE__ */ jsx5(
1641
+ "button",
1642
+ {
1643
+ type: "button",
1644
+ className,
1645
+ onMouseEnter: () => onTooltipEnter(id),
1646
+ onFocus: () => onTooltipEnter(id),
1647
+ onBlur: tooltip.onTooltipLeave,
1648
+ onClick: onCopy,
1649
+ children: value
1650
+ }
1651
+ ),
1652
+ /* @__PURE__ */ jsx5(
1653
+ Tooltip,
1654
+ {
1655
+ label: copied ? "Copied!" : "Click to copy",
1656
+ visible: showTooltip,
1657
+ instant: copied || tooltip.tooltipInstant,
1658
+ side: "bottom",
1659
+ className: "msr:z-10"
1660
+ }
681
1661
  )
682
1662
  ]
683
1663
  }
684
1664
  );
685
1665
  }
686
- var Toolbar = memo(forwardRef(ToolbarComponent));
1666
+ function ColorPicker({
1667
+ active,
1668
+ sample,
1669
+ unsupported,
1670
+ formats,
1671
+ favoriteFormat,
1672
+ ownerWindow,
1673
+ toolbarRef,
1674
+ onClose
1675
+ }) {
1676
+ const panelRef = useRef3(null);
1677
+ const copyTimeoutRef = useRef3(null);
1678
+ const [copiedId, setCopiedId] = useState4(null);
1679
+ const tooltip = useTooltip();
1680
+ const copyValue = useCallback3(
1681
+ (id, value) => {
1682
+ const clipboardWrite = ownerWindow.navigator.clipboard?.writeText(value);
1683
+ void clipboardWrite?.catch(() => void 0);
1684
+ tooltip.onTooltipLeave();
1685
+ setCopiedId(id);
1686
+ if (copyTimeoutRef.current !== null) {
1687
+ ownerWindow.clearTimeout(copyTimeoutRef.current);
1688
+ }
1689
+ copyTimeoutRef.current = ownerWindow.setTimeout(() => {
1690
+ copyTimeoutRef.current = null;
1691
+ setCopiedId(null);
1692
+ }, 1500);
1693
+ },
1694
+ [ownerWindow, tooltip]
1695
+ );
1696
+ const handleTooltipEnter = useCallback3(
1697
+ (id) => {
1698
+ if (copiedId !== null && copiedId !== id) {
1699
+ if (copyTimeoutRef.current !== null) {
1700
+ ownerWindow.clearTimeout(copyTimeoutRef.current);
1701
+ copyTimeoutRef.current = null;
1702
+ }
1703
+ setCopiedId(null);
1704
+ }
1705
+ tooltip.onTooltipEnter(id);
1706
+ },
1707
+ [copiedId, ownerWindow, tooltip]
1708
+ );
1709
+ useLayoutEffect2(() => {
1710
+ if (!active) return;
1711
+ const panel = panelRef.current;
1712
+ const toolbar = toolbarRef.current;
1713
+ if (!panel || !toolbar) return;
1714
+ let frame = 0;
1715
+ const updatePosition = () => {
1716
+ const toolbarRect = toolbar.getBoundingClientRect();
1717
+ const panelRect = panel.getBoundingClientRect();
1718
+ const left = Math.min(
1719
+ Math.max(8, toolbarRect.left),
1720
+ ownerWindow.innerWidth - panelRect.width - 8
1721
+ );
1722
+ const belowTop = toolbarRect.bottom + 8;
1723
+ const aboveTop = toolbarRect.top - panelRect.height - 8;
1724
+ const top = belowTop + panelRect.height <= ownerWindow.innerHeight ? belowTop : Math.max(8, aboveTop);
1725
+ panel.style.left = `${left}px`;
1726
+ panel.style.top = `${top}px`;
1727
+ frame = ownerWindow.requestAnimationFrame(updatePosition);
1728
+ };
1729
+ frame = ownerWindow.requestAnimationFrame(updatePosition);
1730
+ return () => ownerWindow.cancelAnimationFrame(frame);
1731
+ }, [active, ownerWindow, toolbarRef, formats, sample, unsupported, favoriteFormat]);
1732
+ useLayoutEffect2(() => {
1733
+ return () => {
1734
+ if (copyTimeoutRef.current !== null) {
1735
+ ownerWindow.clearTimeout(copyTimeoutRef.current);
1736
+ }
1737
+ };
1738
+ }, [ownerWindow]);
1739
+ if (!active || !sample && !unsupported) return null;
1740
+ const headerFormat = formats.includes(favoriteFormat) ? favoriteFormat : formats[0];
1741
+ const secondaryFormats = headerFormat ? formats.filter((format) => format !== headerFormat) : [];
1742
+ return /* @__PURE__ */ jsx5(
1743
+ "div",
1744
+ {
1745
+ ref: panelRef,
1746
+ 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",
1747
+ role: "dialog",
1748
+ "aria-label": "Selected color values",
1749
+ onMouseLeave: tooltip.onTooltipContainerLeave,
1750
+ children: unsupported ? /* @__PURE__ */ jsxs5("div", { className: "msr:flex msr:items-start msr:gap-2", children: [
1751
+ /* @__PURE__ */ jsx5("span", { className: "msr:text-black/60", children: "Color picker is not supported in this browser." }),
1752
+ /* @__PURE__ */ jsx5(
1753
+ "button",
1754
+ {
1755
+ type: "button",
1756
+ className: "msr:text-black/45 msr:hover:text-black",
1757
+ "aria-label": "Close color picker message",
1758
+ onClick: onClose,
1759
+ children: "x"
1760
+ }
1761
+ )
1762
+ ] }) : sample ? /* @__PURE__ */ jsxs5(Fragment3, { children: [
1763
+ headerFormat ? /* @__PURE__ */ jsxs5(
1764
+ "div",
1765
+ {
1766
+ 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",
1767
+ children: [
1768
+ /* @__PURE__ */ jsx5(
1769
+ "span",
1770
+ {
1771
+ className: "msr:size-3 msr:shrink-0 msr:rounded-full msr:border msr:border-black/15",
1772
+ style: { backgroundColor: colorToHex(sample) },
1773
+ "aria-hidden": "true"
1774
+ }
1775
+ ),
1776
+ /* @__PURE__ */ jsx5(
1777
+ CopyableColorValue,
1778
+ {
1779
+ id: headerFormat,
1780
+ value: formatColor(sample, headerFormat),
1781
+ copiedId,
1782
+ onCopy: () => copyValue(headerFormat, formatColor(sample, headerFormat)),
1783
+ onTooltipEnter: handleTooltipEnter,
1784
+ tooltip,
1785
+ className: "msr:font-medium msr:tabular-nums msr:text-black msr:hover:underline"
1786
+ }
1787
+ )
1788
+ ]
1789
+ }
1790
+ ) : /* @__PURE__ */ jsx5("div", { className: "msr:flex msr:items-center", children: /* @__PURE__ */ jsx5(
1791
+ "span",
1792
+ {
1793
+ className: "msr:size-3 msr:shrink-0 msr:rounded-full msr:border msr:border-black/15",
1794
+ style: { backgroundColor: colorToHex(sample) },
1795
+ "aria-hidden": "true"
1796
+ }
1797
+ ) }),
1798
+ secondaryFormats.map((format) => {
1799
+ const value = formatColor(sample, format);
1800
+ return /* @__PURE__ */ jsxs5("div", { className: "msr:flex msr:items-center msr:gap-2", children: [
1801
+ /* @__PURE__ */ jsx5("span", { className: "msr:w-9 msr:text-black/45", children: format }),
1802
+ /* @__PURE__ */ jsx5(
1803
+ CopyableColorValue,
1804
+ {
1805
+ id: format,
1806
+ value,
1807
+ copiedId,
1808
+ onCopy: () => copyValue(format, value),
1809
+ onTooltipEnter: handleTooltipEnter,
1810
+ tooltip,
1811
+ className: "msr:tabular-nums msr:text-black msr:hover:underline"
1812
+ }
1813
+ )
1814
+ ] }, format);
1815
+ })
1816
+ ] }) : null
1817
+ }
1818
+ );
1819
+ }
687
1820
 
688
1821
  // 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";
1822
+ import { memo as memo2, useEffect as useEffect2, useRef as useRef4, useState as useState5 } from "react";
1823
+ import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
691
1824
  var RULER_SIZE = 18;
692
1825
  var RULER_LENGTH = 4e3;
693
1826
  var TICK_STEP = 5;
1827
+ var RULER_EDGE_REVEAL_DISTANCE = 32;
1828
+ var RULER_FADE_MS = 100;
694
1829
  var ticks = Array.from(
695
1830
  { length: RULER_LENGTH / TICK_STEP + 1 },
696
1831
  (_, index) => index * TICK_STEP
697
1832
  );
698
1833
  var RulersOverlay = memo2(function RulersOverlay2({
1834
+ ownerWindow,
1835
+ settings,
699
1836
  onStartGuide,
700
1837
  onMoveGuide,
701
1838
  onFinishGuide,
@@ -703,9 +1840,26 @@ var RulersOverlay = memo2(function RulersOverlay2({
703
1840
  guides,
704
1841
  selectedGuideIds
705
1842
  }) {
706
- const [dragPosition, setDragPosition] = useState2(null);
707
- const [dragOrientation, setDragOrientation] = useState2(null);
708
- const dragRef = useRef2(null);
1843
+ const [dragPosition, setDragPosition] = useState5(null);
1844
+ const [dragOrientation, setDragOrientation] = useState5(null);
1845
+ const [nearEdge, setNearEdge] = useState5(false);
1846
+ const dragRef = useRef4(null);
1847
+ useEffect2(() => {
1848
+ if (!settings.edgeReveal) return;
1849
+ const handlePointerMove = (event) => {
1850
+ const pointer = event;
1851
+ const nearHorizontalEdge = pointer.clientX <= RULER_EDGE_REVEAL_DISTANCE || pointer.clientX >= ownerWindow.innerWidth - RULER_EDGE_REVEAL_DISTANCE;
1852
+ const nearVerticalEdge = pointer.clientY <= RULER_EDGE_REVEAL_DISTANCE || pointer.clientY >= ownerWindow.innerHeight - RULER_EDGE_REVEAL_DISTANCE;
1853
+ setNearEdge(nearHorizontalEdge || nearVerticalEdge);
1854
+ };
1855
+ ownerWindow.document.addEventListener("pointermove", handlePointerMove, true);
1856
+ ownerWindow.document.addEventListener("mousemove", handlePointerMove, true);
1857
+ return () => {
1858
+ ownerWindow.document.removeEventListener("pointermove", handlePointerMove, true);
1859
+ ownerWindow.document.removeEventListener("mousemove", handlePointerMove, true);
1860
+ };
1861
+ }, [ownerWindow, settings.edgeReveal]);
1862
+ const showRulers = !settings.edgeReveal || nearEdge;
709
1863
  const beginGuideDrag = (orientation, event) => {
710
1864
  if (event.button !== 0) return;
711
1865
  event.stopPropagation();
@@ -755,13 +1909,18 @@ var RulersOverlay = memo2(function RulersOverlay2({
755
1909
  const selectedHorizontalGuides = guides.filter(
756
1910
  (guide) => guide.orientation === "horizontal" && selectedGuideIds.includes(guide.id)
757
1911
  );
758
- return /* @__PURE__ */ jsxs2(
1912
+ return /* @__PURE__ */ jsxs6(
759
1913
  "div",
760
1914
  {
761
1915
  "aria-hidden": "true",
1916
+ "data-mesurer-rulers": "true",
762
1917
  className: "msr:pointer-events-none msr:absolute msr:inset-0 msr:select-none msr:text-[9px] msr:text-[#64748b]",
1918
+ style: {
1919
+ opacity: showRulers ? settings.opacity : 0,
1920
+ transition: `opacity ${RULER_FADE_MS}ms ease`
1921
+ },
763
1922
  children: [
764
- /* @__PURE__ */ jsx3(
1923
+ /* @__PURE__ */ jsx6(
765
1924
  "div",
766
1925
  {
767
1926
  className: "msr:absolute msr:left-[18px] msr:right-0 msr:top-0 msr:overflow-hidden msr:bg-white",
@@ -769,13 +1928,13 @@ var RulersOverlay = memo2(function RulersOverlay2({
769
1928
  boxShadow: "0 1px 3px rgba(0, 0, 0, 0.12)",
770
1929
  height: RULER_SIZE,
771
1930
  cursor: "ns-resize",
772
- pointerEvents: "auto"
1931
+ pointerEvents: showRulers ? "auto" : "none"
773
1932
  },
774
1933
  onPointerDown: (event) => beginGuideDrag("horizontal", event),
775
1934
  onPointerMove: moveGuide,
776
1935
  onPointerUp: finishGuideDrag,
777
1936
  onPointerCancel: cancelGuideDrag,
778
- children: /* @__PURE__ */ jsxs2(
1937
+ children: /* @__PURE__ */ jsxs6(
779
1938
  "svg",
780
1939
  {
781
1940
  className: "msr:block msr:h-6",
@@ -783,7 +1942,7 @@ var RulersOverlay = memo2(function RulersOverlay2({
783
1942
  height: RULER_SIZE,
784
1943
  viewBox: `0 0 ${RULER_LENGTH} ${RULER_SIZE}`,
785
1944
  children: [
786
- /* @__PURE__ */ jsx3("defs", { children: /* @__PURE__ */ jsxs2(
1945
+ /* @__PURE__ */ jsx6("defs", { children: /* @__PURE__ */ jsxs6(
787
1946
  "linearGradient",
788
1947
  {
789
1948
  id: "ruler-label-fade-x",
@@ -791,18 +1950,18 @@ var RulersOverlay = memo2(function RulersOverlay2({
791
1950
  x2: "100%",
792
1951
  gradientUnits: "objectBoundingBox",
793
1952
  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" })
1953
+ /* @__PURE__ */ jsx6("stop", { offset: "0", stopColor: "white", stopOpacity: "0" }),
1954
+ /* @__PURE__ */ jsx6("stop", { offset: "0.2", stopColor: "white" }),
1955
+ /* @__PURE__ */ jsx6("stop", { offset: "0.8", stopColor: "white" }),
1956
+ /* @__PURE__ */ jsx6("stop", { offset: "1", stopColor: "white", stopOpacity: "0" })
798
1957
  ]
799
1958
  }
800
1959
  ) }),
801
1960
  ticks.map((x) => {
802
1961
  const major = x % 100 === 0;
803
1962
  const medium = x % 50 === 0;
804
- return /* @__PURE__ */ jsxs2("g", { children: [
805
- /* @__PURE__ */ jsx3(
1963
+ return /* @__PURE__ */ jsxs6("g", { children: [
1964
+ /* @__PURE__ */ jsx6(
806
1965
  "line",
807
1966
  {
808
1967
  x1: x,
@@ -813,11 +1972,11 @@ var RulersOverlay = memo2(function RulersOverlay2({
813
1972
  strokeWidth: "1"
814
1973
  }
815
1974
  ),
816
- major ? /* @__PURE__ */ jsx3("text", { x, y: "8", textAnchor: "middle", fill: "currentColor", children: x }) : null
1975
+ major ? /* @__PURE__ */ jsx6("text", { x, y: "8", textAnchor: "middle", fill: "currentColor", children: x }) : null
817
1976
  ] }, x);
818
1977
  }),
819
- selectedVerticalGuides.map((guide) => /* @__PURE__ */ jsxs2("g", { children: [
820
- /* @__PURE__ */ jsx3(
1978
+ selectedVerticalGuides.map((guide) => /* @__PURE__ */ jsxs6("g", { children: [
1979
+ /* @__PURE__ */ jsx6(
821
1980
  "rect",
822
1981
  {
823
1982
  x: guide.position - RULER_SIZE - 8,
@@ -827,7 +1986,7 @@ var RulersOverlay = memo2(function RulersOverlay2({
827
1986
  fill: "url(#ruler-label-fade-x)"
828
1987
  }
829
1988
  ),
830
- /* @__PURE__ */ jsx3(
1989
+ /* @__PURE__ */ jsx6(
831
1990
  "text",
832
1991
  {
833
1992
  x: guide.position - RULER_SIZE + 16,
@@ -839,8 +1998,8 @@ var RulersOverlay = memo2(function RulersOverlay2({
839
1998
  }
840
1999
  )
841
2000
  ] }, guide.id)),
842
- dragOrientation === "vertical" && dragPosition !== null ? /* @__PURE__ */ jsxs2("g", { children: [
843
- /* @__PURE__ */ jsx3(
2001
+ dragOrientation === "vertical" && dragPosition !== null ? /* @__PURE__ */ jsxs6("g", { children: [
2002
+ /* @__PURE__ */ jsx6(
844
2003
  "rect",
845
2004
  {
846
2005
  x: dragPosition - RULER_SIZE - 8,
@@ -850,7 +2009,7 @@ var RulersOverlay = memo2(function RulersOverlay2({
850
2009
  fill: "url(#ruler-label-fade-x)"
851
2010
  }
852
2011
  ),
853
- /* @__PURE__ */ jsx3(
2012
+ /* @__PURE__ */ jsx6(
854
2013
  "text",
855
2014
  {
856
2015
  x: dragPosition - RULER_SIZE + 16,
@@ -867,7 +2026,7 @@ var RulersOverlay = memo2(function RulersOverlay2({
867
2026
  )
868
2027
  }
869
2028
  ),
870
- /* @__PURE__ */ jsx3(
2029
+ /* @__PURE__ */ jsx6(
871
2030
  "div",
872
2031
  {
873
2032
  className: "msr:absolute msr:bottom-0 msr:left-0 msr:top-[18px] msr:w-[18px] msr:overflow-hidden msr:bg-white",
@@ -875,13 +2034,13 @@ var RulersOverlay = memo2(function RulersOverlay2({
875
2034
  boxShadow: "1px 0 3px rgba(0, 0, 0, 0.12)",
876
2035
  width: RULER_SIZE,
877
2036
  cursor: "ew-resize",
878
- pointerEvents: "auto"
2037
+ pointerEvents: showRulers ? "auto" : "none"
879
2038
  },
880
2039
  onPointerDown: (event) => beginGuideDrag("vertical", event),
881
2040
  onPointerMove: moveGuide,
882
2041
  onPointerUp: finishGuideDrag,
883
2042
  onPointerCancel: cancelGuideDrag,
884
- children: /* @__PURE__ */ jsxs2(
2043
+ children: /* @__PURE__ */ jsxs6(
885
2044
  "svg",
886
2045
  {
887
2046
  className: "msr:block msr:w-6",
@@ -889,7 +2048,7 @@ var RulersOverlay = memo2(function RulersOverlay2({
889
2048
  height: RULER_LENGTH,
890
2049
  viewBox: `0 0 ${RULER_SIZE} ${RULER_LENGTH}`,
891
2050
  children: [
892
- /* @__PURE__ */ jsx3("defs", { children: /* @__PURE__ */ jsxs2(
2051
+ /* @__PURE__ */ jsx6("defs", { children: /* @__PURE__ */ jsxs6(
893
2052
  "linearGradient",
894
2053
  {
895
2054
  id: "ruler-label-fade-y",
@@ -897,18 +2056,18 @@ var RulersOverlay = memo2(function RulersOverlay2({
897
2056
  y2: "100%",
898
2057
  gradientUnits: "objectBoundingBox",
899
2058
  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" })
2059
+ /* @__PURE__ */ jsx6("stop", { offset: "0", stopColor: "white", stopOpacity: "0" }),
2060
+ /* @__PURE__ */ jsx6("stop", { offset: "0.2", stopColor: "white" }),
2061
+ /* @__PURE__ */ jsx6("stop", { offset: "0.8", stopColor: "white" }),
2062
+ /* @__PURE__ */ jsx6("stop", { offset: "1", stopColor: "white", stopOpacity: "0" })
904
2063
  ]
905
2064
  }
906
2065
  ) }),
907
2066
  ticks.map((y) => {
908
2067
  const major = y % 100 === 0;
909
2068
  const medium = y % 50 === 0;
910
- return /* @__PURE__ */ jsxs2("g", { children: [
911
- /* @__PURE__ */ jsx3(
2069
+ return /* @__PURE__ */ jsxs6("g", { children: [
2070
+ /* @__PURE__ */ jsx6(
912
2071
  "line",
913
2072
  {
914
2073
  x1: RULER_SIZE,
@@ -919,7 +2078,7 @@ var RulersOverlay = memo2(function RulersOverlay2({
919
2078
  strokeWidth: "1"
920
2079
  }
921
2080
  ),
922
- major ? /* @__PURE__ */ jsx3(
2081
+ major ? /* @__PURE__ */ jsx6(
923
2082
  "text",
924
2083
  {
925
2084
  x: "8",
@@ -931,8 +2090,8 @@ var RulersOverlay = memo2(function RulersOverlay2({
931
2090
  ) : null
932
2091
  ] }, y);
933
2092
  }),
934
- selectedHorizontalGuides.map((guide) => /* @__PURE__ */ jsxs2("g", { children: [
935
- /* @__PURE__ */ jsx3(
2093
+ selectedHorizontalGuides.map((guide) => /* @__PURE__ */ jsxs6("g", { children: [
2094
+ /* @__PURE__ */ jsx6(
936
2095
  "rect",
937
2096
  {
938
2097
  x: "-3",
@@ -942,7 +2101,7 @@ var RulersOverlay = memo2(function RulersOverlay2({
942
2101
  fill: "url(#ruler-label-fade-y)"
943
2102
  }
944
2103
  ),
945
- /* @__PURE__ */ jsx3(
2104
+ /* @__PURE__ */ jsx6(
946
2105
  "text",
947
2106
  {
948
2107
  x: "9",
@@ -955,8 +2114,8 @@ var RulersOverlay = memo2(function RulersOverlay2({
955
2114
  }
956
2115
  )
957
2116
  ] }, guide.id)),
958
- dragOrientation === "horizontal" && dragPosition !== null ? /* @__PURE__ */ jsxs2("g", { children: [
959
- /* @__PURE__ */ jsx3(
2117
+ dragOrientation === "horizontal" && dragPosition !== null ? /* @__PURE__ */ jsxs6("g", { children: [
2118
+ /* @__PURE__ */ jsx6(
960
2119
  "rect",
961
2120
  {
962
2121
  x: "-3",
@@ -966,7 +2125,7 @@ var RulersOverlay = memo2(function RulersOverlay2({
966
2125
  fill: "url(#ruler-label-fade-y)"
967
2126
  }
968
2127
  ),
969
- /* @__PURE__ */ jsx3(
2128
+ /* @__PURE__ */ jsx6(
970
2129
  "text",
971
2130
  {
972
2131
  x: "9",
@@ -984,34 +2143,34 @@ var RulersOverlay = memo2(function RulersOverlay2({
984
2143
  )
985
2144
  }
986
2145
  ),
987
- /* @__PURE__ */ jsx3("div", { className: "msr:absolute msr:left-0 msr:top-0 msr:size-[18px] msr:bg-white" })
2146
+ /* @__PURE__ */ jsx6("div", { className: "msr:absolute msr:left-0 msr:top-0 msr:size-[18px] msr:bg-white" })
988
2147
  ]
989
2148
  }
990
2149
  );
991
2150
  });
992
2151
 
993
2152
  // hooks/use-drag-state.ts
994
- import { useState as useState3 } from "react";
2153
+ import { useState as useState6 } from "react";
995
2154
  var useDragState = () => {
996
- const [start, setStart] = useState3(null);
997
- const [end, setEnd] = useState3(null);
998
- const [isDragging, setIsDragging] = useState3(false);
2155
+ const [start, setStart] = useState6(null);
2156
+ const [end, setEnd] = useState6(null);
2157
+ const [isDragging, setIsDragging] = useState6(false);
999
2158
  return { start, setStart, end, setEnd, isDragging, setIsDragging };
1000
2159
  };
1001
2160
 
1002
2161
  // hooks/use-guide-drag-hold.ts
1003
- import { useCallback as useCallback2, useRef as useRef3 } from "react";
2162
+ import { useCallback as useCallback4, useRef as useRef5 } from "react";
1004
2163
  var useGuideDragHold = (eventTarget) => {
1005
- const guideDragHoldTimerRef = useRef3(null);
1006
- const guideDragHoldGuideIdRef = useRef3(null);
1007
- const clearGuideDragHold = useCallback2(() => {
2164
+ const guideDragHoldTimerRef = useRef5(null);
2165
+ const guideDragHoldGuideIdRef = useRef5(null);
2166
+ const clearGuideDragHold = useCallback4(() => {
1008
2167
  if (guideDragHoldTimerRef.current !== null) {
1009
2168
  eventTarget.clearTimeout(guideDragHoldTimerRef.current);
1010
2169
  guideDragHoldTimerRef.current = null;
1011
2170
  }
1012
2171
  guideDragHoldGuideIdRef.current = null;
1013
2172
  }, []);
1014
- const scheduleGuideDragHold = useCallback2(
2173
+ const scheduleGuideDragHold = useCallback4(
1015
2174
  (id, onHold) => {
1016
2175
  clearGuideDragHold();
1017
2176
  guideDragHoldGuideIdRef.current = id;
@@ -1030,11 +2189,11 @@ var useGuideDragHold = (eventTarget) => {
1030
2189
  };
1031
2190
 
1032
2191
  // hooks/use-guide-state.ts
1033
- import { useState as useState4 } from "react";
2192
+ import { useState as useState7 } from "react";
1034
2193
  var useGuideState = (options = {}) => {
1035
- const [guides, setGuides] = useState4(options.initialGuides ?? []);
1036
- const [draggingGuideId, setDraggingGuideId] = useState4(null);
1037
- const [selectedGuideIds, setSelectedGuideIds] = useState4(
2194
+ const [guides, setGuides] = useState7(options.initialGuides ?? []);
2195
+ const [draggingGuideId, setDraggingGuideId] = useState7(null);
2196
+ const [selectedGuideIds, setSelectedGuideIds] = useState7(
1038
2197
  options.initialSelectedGuideIds ?? []
1039
2198
  );
1040
2199
  return {
@@ -1048,67 +2207,91 @@ var useGuideState = (options = {}) => {
1048
2207
  };
1049
2208
 
1050
2209
  // hooks/use-hotkeys.ts
1051
- import { useEffect } from "react";
2210
+ import { useEffect as useEffect3, useRef as useRef6 } from "react";
1052
2211
  var useHotkeys = (options) => {
1053
- useEffect(() => {
2212
+ const optionsRef = useRef6(options);
2213
+ optionsRef.current = options;
2214
+ useEffect3(() => {
1054
2215
  const target = options.eventTarget;
1055
2216
  const handleKeyDown = (event) => {
2217
+ const current = optionsRef.current;
1056
2218
  if (event.key === "Escape") {
1057
- options.clearAll();
2219
+ if (current.isSettingsOpen()) {
2220
+ current.onToggleSettings();
2221
+ return;
2222
+ }
2223
+ if (current.isColorPickerActive()) {
2224
+ current.onCloseColorPicker();
2225
+ return;
2226
+ }
2227
+ current.clearAll();
1058
2228
  return;
1059
2229
  }
1060
2230
  if (event.metaKey || event.ctrlKey) {
2231
+ if (event.key === ",") {
2232
+ event.preventDefault();
2233
+ current.onToggleSettings();
2234
+ current.onInteract();
2235
+ return;
2236
+ }
1061
2237
  if (event.key.toLowerCase() !== "z") return;
1062
2238
  if (event.shiftKey) {
1063
2239
  event.preventDefault();
1064
- options.redo();
2240
+ current.redo();
1065
2241
  return;
1066
2242
  }
1067
2243
  event.preventDefault();
1068
- options.undo();
2244
+ current.undo();
1069
2245
  return;
1070
2246
  }
1071
2247
  if (event.key.toLowerCase() === "m") {
1072
- options.setEnabled((prev) => !prev);
2248
+ current.setEnabled((prev) => !prev);
1073
2249
  }
1074
2250
  const key = event.key.toLowerCase();
1075
- if (options.isOverlayActive()) {
2251
+ if (key === "p") {
2252
+ event.preventDefault();
2253
+ current.onColorPicker();
2254
+ current.onInteract();
2255
+ return;
2256
+ }
2257
+ if (key === "x") {
2258
+ current.onToggleXray();
2259
+ current.onInteract();
2260
+ return;
2261
+ }
2262
+ if (current.isOverlayActive()) {
1076
2263
  if (key === "a") {
1077
- options.setToolMode(
2264
+ current.setToolMode(
1078
2265
  (prev) => prev === "text-inspector" ? "none" : "text-inspector"
1079
2266
  );
1080
- options.onInteract();
2267
+ current.onInteract();
1081
2268
  }
1082
2269
  if (key === "s") {
1083
- options.setToolMode((prev) => prev === "select" ? "none" : "select");
1084
- options.onInteract();
2270
+ current.setToolMode((prev) => prev === "select" ? "none" : "select");
2271
+ current.onInteract();
1085
2272
  }
1086
2273
  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();
2274
+ current.setToolMode((prev) => prev === "guides" ? "none" : "guides");
2275
+ current.onInteract();
1093
2276
  }
1094
2277
  if (key === "r") {
1095
- options.setRulersVisible((prev) => !prev);
1096
- options.onInteract();
2278
+ current.setRulersVisible((prev) => !prev);
2279
+ current.onInteract();
1097
2280
  }
1098
2281
  if (key === "h") {
1099
- options.setGuideOrientation("horizontal");
1100
- options.onInteract();
2282
+ current.setGuideOrientation("horizontal");
2283
+ current.onInteract();
1101
2284
  }
1102
2285
  if (key === "v") {
1103
- options.setGuideOrientation("vertical");
1104
- options.onInteract();
2286
+ current.setGuideOrientation("vertical");
2287
+ current.onInteract();
1105
2288
  }
1106
2289
  }
1107
2290
  if (event.key === "Alt") {
1108
- options.setAltPressed(true);
2291
+ current.setAltPressed(true);
1109
2292
  }
1110
2293
  if (event.key === "Backspace" || event.key === "Delete") {
1111
- const removed = options.removeSelectedGuides();
2294
+ const removed = current.removeSelectedGuides();
1112
2295
  if (removed) {
1113
2296
  event.preventDefault();
1114
2297
  }
@@ -1116,7 +2299,7 @@ var useHotkeys = (options) => {
1116
2299
  };
1117
2300
  const handleKeyUp = (event) => {
1118
2301
  if (event.key === "Alt") {
1119
- options.setAltPressed(false);
2302
+ optionsRef.current.setAltPressed(false);
1120
2303
  }
1121
2304
  };
1122
2305
  target.addEventListener("keydown", handleKeyDown);
@@ -1125,11 +2308,11 @@ var useHotkeys = (options) => {
1125
2308
  target.removeEventListener("keydown", handleKeyDown);
1126
2309
  target.removeEventListener("keyup", handleKeyUp);
1127
2310
  };
1128
- }, [options]);
2311
+ }, [options.eventTarget]);
1129
2312
  };
1130
2313
 
1131
2314
  // hooks/use-live-element-tracking.ts
1132
- import { useEffect as useEffect2, useRef as useRef4 } from "react";
2315
+ import { useEffect as useEffect4, useRef as useRef7 } from "react";
1133
2316
 
1134
2317
  // core/geometry.ts
1135
2318
  var getViewportSize = (ownerWindow) => ({
@@ -1155,7 +2338,7 @@ var getRectFromPoints = (start, end) => {
1155
2338
  const height = Math.abs(start.y - end.y);
1156
2339
  return { left, top, width, height };
1157
2340
  };
1158
- var clamp = (value, min, max) => Math.max(min, Math.min(max, value));
2341
+ var clamp2 = (value, min, max) => Math.max(min, Math.min(max, value));
1159
2342
  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
2343
  var rectArea = (rect) => rect.width * rect.height;
1161
2344
  var intersectionArea = (rectA, rectB) => {
@@ -1219,10 +2402,10 @@ var getDistanceOverlay = (rectA, rectB, elementRefA, elementRefB, ownerWindow =
1219
2402
  }
1220
2403
  }
1221
2404
  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)
2405
+ x1: clamp2(segment.x1, 0, ownerWindow.innerWidth),
2406
+ y1: clamp2(segment.y1, 0, ownerWindow.innerHeight),
2407
+ x2: clamp2(segment.x2, 0, ownerWindow.innerWidth),
2408
+ y2: clamp2(segment.y2, 0, ownerWindow.innerHeight)
1226
2409
  })).filter(
1227
2410
  (segment) => Math.abs(segment.x1 - segment.x2) > 0.5 || Math.abs(segment.y1 - segment.y2) > 0.5
1228
2411
  );
@@ -1389,19 +2572,23 @@ var updateMeasurementForResize = (measurement, viewport = getViewportSize(), own
1389
2572
 
1390
2573
  // hooks/use-live-element-tracking.ts
1391
2574
  var useLiveElementTracking = (params) => {
1392
- const frameRef = useRef4(null);
1393
- useEffect2(() => {
2575
+ const paramsRef = useRef7(params);
2576
+ paramsRef.current = params;
2577
+ const frameRef = useRef7(null);
2578
+ useEffect4(() => {
2579
+ const ownerWindow = params.window;
1394
2580
  if (!params.enabled) {
1395
2581
  if (frameRef.current) {
1396
- params.window.cancelAnimationFrame(frameRef.current);
2582
+ ownerWindow.cancelAnimationFrame(frameRef.current);
1397
2583
  }
1398
2584
  frameRef.current = null;
1399
2585
  return;
1400
2586
  }
1401
2587
  const tick = () => {
1402
- params.setMeasurements(
2588
+ const current = paramsRef.current;
2589
+ current.setMeasurements(
1403
2590
  (prev) => prev.map((measurement) => {
1404
- if (!measurement.elementRef || !params.document.contains(measurement.elementRef)) {
2591
+ if (!measurement.elementRef || !current.document.contains(measurement.elementRef)) {
1405
2592
  return measurement;
1406
2593
  }
1407
2594
  const rect = getRectFromDom(measurement.elementRef);
@@ -1414,8 +2601,8 @@ var useLiveElementTracking = (params) => {
1414
2601
  };
1415
2602
  })
1416
2603
  );
1417
- params.setActiveMeasurement((prev) => {
1418
- if (!prev?.elementRef || !params.document.contains(prev.elementRef))
2604
+ current.setActiveMeasurement((prev) => {
2605
+ if (!prev?.elementRef || !current.document.contains(prev.elementRef))
1419
2606
  return prev;
1420
2607
  const rect = getRectFromDom(prev.elementRef);
1421
2608
  if (rectAlmostEqual(rect, prev.rect)) return prev;
@@ -1426,10 +2613,10 @@ var useLiveElementTracking = (params) => {
1426
2613
  originRect: void 0
1427
2614
  };
1428
2615
  });
1429
- params.setHeldDistances(
2616
+ current.setHeldDistances(
1430
2617
  (prev) => prev.map((distance) => {
1431
- const canTrackA = distance.elementRefA && params.document.contains(distance.elementRefA);
1432
- const canTrackB = distance.elementRefB && params.document.contains(distance.elementRefB);
2618
+ const canTrackA = distance.elementRefA && current.document.contains(distance.elementRefA);
2619
+ const canTrackB = distance.elementRefB && current.document.contains(distance.elementRefB);
1433
2620
  if (!canTrackA && !canTrackB) return distance;
1434
2621
  const rectA = canTrackA ? getRectFromDom(distance.elementRefA) : distance.rectA;
1435
2622
  const rectB = canTrackB ? getRectFromDom(distance.elementRefB) : distance.rectB;
@@ -1441,7 +2628,7 @@ var useLiveElementTracking = (params) => {
1441
2628
  rectB,
1442
2629
  distance.elementRefA,
1443
2630
  distance.elementRefB,
1444
- params.window
2631
+ ownerWindow
1445
2632
  );
1446
2633
  return {
1447
2634
  ...updated,
@@ -1449,62 +2636,71 @@ var useLiveElementTracking = (params) => {
1449
2636
  };
1450
2637
  })
1451
2638
  );
1452
- const selected = params.selectedElementRef.current;
1453
- if (selected && params.document.contains(selected)) {
1454
- params.setSelectedMeasurement((prev) => {
1455
- const next = getInspectMeasurement(selected, params.window);
2639
+ const selected = current.selectedElementRef.current;
2640
+ if (current.selectionEnabled && selected && current.document.contains(selected)) {
2641
+ current.setSelectedMeasurement((prev) => {
2642
+ const next = getInspectMeasurement(selected, ownerWindow);
1456
2643
  if (prev && rectAlmostEqual(prev.rect, next.rect)) return prev;
1457
2644
  return next;
1458
2645
  });
1459
2646
  }
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)) {
2647
+ if (current.selectionEnabled) {
2648
+ current.setSelectedMeasurements(
2649
+ (prev) => prev.map((measurement) => {
2650
+ if (!measurement.elementRef || !current.document.contains(measurement.elementRef)) {
2651
+ return measurement;
2652
+ }
2653
+ const next = getInspectMeasurement(measurement.elementRef, ownerWindow);
2654
+ if (rectAlmostEqual(next.rect, measurement.rect)) return measurement;
2655
+ return {
2656
+ ...next,
2657
+ id: measurement.id
2658
+ };
2659
+ })
2660
+ );
2661
+ }
2662
+ const hover = current.hoverElementRef.current;
2663
+ if (current.selectionEnabled && hover && current.document.contains(hover)) {
1475
2664
  const rect = getRectFromDom(hover);
1476
- params.setHoverRect(
2665
+ current.setHoverRect(
1477
2666
  (prev) => prev && rectAlmostEqual(prev, rect) ? prev : rect
1478
2667
  );
1479
2668
  }
1480
- frameRef.current = params.window.requestAnimationFrame(tick);
2669
+ frameRef.current = ownerWindow.requestAnimationFrame(tick);
1481
2670
  };
1482
- frameRef.current = params.window.requestAnimationFrame(tick);
2671
+ frameRef.current = ownerWindow.requestAnimationFrame(tick);
1483
2672
  return () => {
1484
2673
  if (frameRef.current) {
1485
- params.window.cancelAnimationFrame(frameRef.current);
2674
+ ownerWindow.cancelAnimationFrame(frameRef.current);
1486
2675
  }
1487
2676
  frameRef.current = null;
1488
2677
  };
1489
- }, [params]);
2678
+ }, [params.enabled, params.window]);
1490
2679
  };
1491
2680
 
1492
2681
  // hooks/use-measure-toggles.ts
1493
- import { useState as useState5 } from "react";
2682
+ import { useState as useState8 } from "react";
1494
2683
  var useMeasureToggles = (options = {}) => {
1495
- const [enabled, setEnabled] = useState5(options.initialEnabled ?? true);
1496
- const [altPressed, setAltPressed] = useState5(false);
1497
- const [toolMode, setToolMode] = useState5(
2684
+ const [enabled, setEnabled] = useState8(options.initialEnabled ?? true);
2685
+ const [altPressed, setAltPressed] = useState8(false);
2686
+ const [toolMode, setToolMode] = useState8(
1498
2687
  options.initialToolMode ?? "none"
1499
2688
  );
1500
- const [rulersVisible, setRulersVisible] = useState5(
2689
+ const [rulersVisible, setRulersVisible] = useState8(
1501
2690
  options.initialRulersVisible ?? false
1502
2691
  );
1503
2692
  const holdEnabled = false;
1504
- const multiMeasureEnabled = false;
1505
- const snapGuidesEnabled = true;
2693
+ const [multiMeasureEnabled, setMultiMeasureEnabled] = useState8(
2694
+ options.initialMultiMeasureEnabled ?? false
2695
+ );
2696
+ const [snapGuidesEnabled, setSnapGuidesEnabled] = useState8(
2697
+ options.initialSnapGuidesEnabled ?? true
2698
+ );
2699
+ const [selectNewGuideEnabled, setSelectNewGuideEnabled] = useState8(
2700
+ options.initialSelectNewGuideEnabled ?? true
2701
+ );
1506
2702
  const guidesEnabled = toolMode === "guides";
1507
- const snapEnabled = true;
2703
+ const [snapEnabled, setSnapEnabled] = useState8(options.initialSnapEnabled ?? true);
1508
2704
  return {
1509
2705
  enabled,
1510
2706
  setEnabled,
@@ -1518,21 +2714,26 @@ var useMeasureToggles = (options = {}) => {
1518
2714
  guidesEnabled,
1519
2715
  multiMeasureEnabled,
1520
2716
  snapGuidesEnabled,
1521
- snapEnabled
2717
+ setSnapGuidesEnabled,
2718
+ selectNewGuideEnabled,
2719
+ setSelectNewGuideEnabled,
2720
+ snapEnabled,
2721
+ setSnapEnabled,
2722
+ setMultiMeasureEnabled
1522
2723
  };
1523
2724
  };
1524
2725
 
1525
2726
  // hooks/use-measurement-state.ts
1526
- import { useState as useState6 } from "react";
2727
+ import { useState as useState9 } from "react";
1527
2728
  var useMeasurementState = (options = {}) => {
1528
- const [activeMeasurement, setActiveMeasurement] = useState6(options.initialActiveMeasurement ?? null);
1529
- const [measurements, setMeasurements] = useState6(
2729
+ const [activeMeasurement, setActiveMeasurement] = useState9(options.initialActiveMeasurement ?? null);
2730
+ const [measurements, setMeasurements] = useState9(
1530
2731
  options.initialMeasurements ?? []
1531
2732
  );
1532
- const [selectedMeasurement, setSelectedMeasurement] = useState6(null);
1533
- const [selectedMeasurements, setSelectedMeasurements] = useState6([]);
1534
- const [hoverRect, setHoverRect] = useState6(null);
1535
- const [heldDistances, setHeldDistances] = useState6(
2733
+ const [selectedMeasurement, setSelectedMeasurement] = useState9(null);
2734
+ const [selectedMeasurements, setSelectedMeasurements] = useState9([]);
2735
+ const [hoverRect, setHoverRect] = useState9(null);
2736
+ const [heldDistances, setHeldDistances] = useState9(
1536
2737
  options.initialHeldDistances ?? []
1537
2738
  );
1538
2739
  return {
@@ -2022,7 +3223,7 @@ var useMeasurerDerived = ({
2022
3223
  };
2023
3224
 
2024
3225
  // hooks/use-measurer-history.ts
2025
- import { useCallback as useCallback3, useRef as useRef5 } from "react";
3226
+ import { useCallback as useCallback5, useRef as useRef8 } from "react";
2026
3227
  var HISTORY_LIMIT = 50;
2027
3228
  var useMeasurerHistory = ({
2028
3229
  toggles,
@@ -2068,10 +3269,10 @@ var useMeasurerHistory = ({
2068
3269
  setSelectedElement,
2069
3270
  clearSelectionRect
2070
3271
  } = transient;
2071
- const historyRef = useRef5([]);
2072
- const futureRef = useRef5([]);
2073
- const historySignatureRef = useRef5(null);
2074
- const captureSnapshot = useCallback3(() => {
3272
+ const historyRef = useRef8([]);
3273
+ const futureRef = useRef8([]);
3274
+ const historySignatureRef = useRef8(null);
3275
+ const captureSnapshot = useCallback5(() => {
2075
3276
  return {
2076
3277
  enabled,
2077
3278
  toolMode,
@@ -2098,7 +3299,7 @@ var useMeasurerHistory = ({
2098
3299
  selectedMeasurements,
2099
3300
  toolMode
2100
3301
  ]);
2101
- const getSnapshotSignature = useCallback3((snapshot) => {
3302
+ const getSnapshotSignature = useCallback5((snapshot) => {
2102
3303
  const serializeRect = (rect) => `${Math.round(rect.left)}:${Math.round(rect.top)}:${Math.round(rect.width)}:${Math.round(rect.height)}`;
2103
3304
  return [
2104
3305
  snapshot.enabled ? "1" : "0",
@@ -2114,7 +3315,7 @@ var useMeasurerHistory = ({
2114
3315
  snapshot.draggingGuideId ?? ""
2115
3316
  ].join("|");
2116
3317
  }, []);
2117
- const recordSnapshot = useCallback3(() => {
3318
+ const recordSnapshot = useCallback5(() => {
2118
3319
  const snapshot = captureSnapshot();
2119
3320
  const signature = getSnapshotSignature(snapshot);
2120
3321
  if (historySignatureRef.current === signature) return;
@@ -2125,7 +3326,7 @@ var useMeasurerHistory = ({
2125
3326
  historyRef.current.shift();
2126
3327
  }
2127
3328
  }, [captureSnapshot, getSnapshotSignature]);
2128
- const restoreSnapshot = useCallback3(
3329
+ const restoreSnapshot = useCallback5(
2129
3330
  (snapshot) => {
2130
3331
  setEnabled(snapshot.enabled);
2131
3332
  setToolMode(snapshot.toolMode);
@@ -2170,7 +3371,7 @@ var useMeasurerHistory = ({
2170
3371
  setHoverElement
2171
3372
  ]
2172
3373
  );
2173
- const undo = useCallback3(() => {
3374
+ const undo = useCallback5(() => {
2174
3375
  const previousSnapshot = historyRef.current.pop();
2175
3376
  if (!previousSnapshot) return;
2176
3377
  const currentSnapshot = captureSnapshot();
@@ -2181,7 +3382,7 @@ var useMeasurerHistory = ({
2181
3382
  historySignatureRef.current = null;
2182
3383
  restoreSnapshot(previousSnapshot);
2183
3384
  }, [captureSnapshot, restoreSnapshot]);
2184
- const redo = useCallback3(() => {
3385
+ const redo = useCallback5(() => {
2185
3386
  const nextSnapshot = futureRef.current.pop();
2186
3387
  if (!nextSnapshot) return;
2187
3388
  historyRef.current.push(captureSnapshot());
@@ -2191,7 +3392,7 @@ var useMeasurerHistory = ({
2191
3392
  historySignatureRef.current = null;
2192
3393
  restoreSnapshot(nextSnapshot);
2193
3394
  }, [captureSnapshot, restoreSnapshot]);
2194
- const setToolModeWithHistory = useCallback3(
3395
+ const setToolModeWithHistory = useCallback5(
2195
3396
  (value) => {
2196
3397
  setToolMode((prev) => {
2197
3398
  const next = typeof value === "function" ? value(prev) : value;
@@ -2202,7 +3403,7 @@ var useMeasurerHistory = ({
2202
3403
  },
2203
3404
  [recordSnapshot, setToolMode]
2204
3405
  );
2205
- const setGuideOrientationWithHistory = useCallback3(
3406
+ const setGuideOrientationWithHistory = useCallback5(
2206
3407
  (value) => {
2207
3408
  setGuideOrientation((prev) => {
2208
3409
  const next = typeof value === "function" ? value(prev) : value;
@@ -2213,7 +3414,7 @@ var useMeasurerHistory = ({
2213
3414
  },
2214
3415
  [recordSnapshot, setGuideOrientation]
2215
3416
  );
2216
- const setEnabledWithHistory = useCallback3(
3417
+ const setEnabledWithHistory = useCallback5(
2217
3418
  (value) => {
2218
3419
  setEnabled((prev) => {
2219
3420
  const next = typeof value === "function" ? value(prev) : value;
@@ -2224,7 +3425,7 @@ var useMeasurerHistory = ({
2224
3425
  },
2225
3426
  [recordSnapshot, setEnabled]
2226
3427
  );
2227
- const createActionCommit = useCallback3(() => {
3428
+ const createActionCommit = useCallback5(() => {
2228
3429
  let committed = false;
2229
3430
  return () => {
2230
3431
  if (committed) return;
@@ -2244,35 +3445,35 @@ var useMeasurerHistory = ({
2244
3445
  };
2245
3446
 
2246
3447
  // hooks/use-measurer-local-state.ts
2247
- import { useCallback as useCallback4, useState as useState7 } from "react";
3448
+ import { useCallback as useCallback6, useState as useState10 } from "react";
2248
3449
  var useMeasurerLocalState = ({
2249
3450
  selectedElementRef,
2250
3451
  hoverElementRef,
2251
3452
  selectionRectRef
2252
3453
  }) => {
2253
- const [selectionOriginRect, setSelectionOriginRect] = useState7(
3454
+ const [selectionOriginRect, setSelectionOriginRect] = useState10(
2254
3455
  null
2255
3456
  );
2256
- const [hoverPointer, setHoverPointer] = useState7(null);
2257
- const [hoverElement, setHoverElementState] = useState7(
3457
+ const [hoverPointer, setHoverPointer] = useState10(null);
3458
+ const [hoverElement, setHoverElementState] = useState10(
2258
3459
  null
2259
3460
  );
2260
- const [selectedElement, setSelectedElementState] = useState7(null);
2261
- const setSelectedElement = useCallback4(
3461
+ const [selectedElement, setSelectedElementState] = useState10(null);
3462
+ const setSelectedElement = useCallback6(
2262
3463
  (element) => {
2263
3464
  selectedElementRef.current = element;
2264
3465
  setSelectedElementState(element);
2265
3466
  },
2266
3467
  [selectedElementRef]
2267
3468
  );
2268
- const setHoverElement = useCallback4(
3469
+ const setHoverElement = useCallback6(
2269
3470
  (element) => {
2270
3471
  hoverElementRef.current = element;
2271
3472
  setHoverElementState(element);
2272
3473
  },
2273
3474
  [hoverElementRef]
2274
3475
  );
2275
- const clearSelectionRect = useCallback4(() => {
3476
+ const clearSelectionRect = useCallback6(() => {
2276
3477
  selectionRectRef.current = null;
2277
3478
  setSelectionOriginRect(null);
2278
3479
  }, [selectionRectRef]);
@@ -2290,7 +3491,7 @@ var useMeasurerLocalState = ({
2290
3491
  };
2291
3492
 
2292
3493
  // hooks/use-measurer-pointer.ts
2293
- import { useCallback as useCallback5, useEffect as useEffect3, useRef as useRef6 } from "react";
3494
+ import { useCallback as useCallback7, useEffect as useEffect5, useRef as useRef9 } from "react";
2294
3495
 
2295
3496
  // core/targets.ts
2296
3497
  var pickMultiTargets = (selectionRect, items) => {
@@ -2493,10 +3694,12 @@ var useMeasurerPointer = ({
2493
3694
  clearGuideDragHold,
2494
3695
  scheduleGuideDragHold,
2495
3696
  enabled,
3697
+ settingsOpen,
2496
3698
  toolMode,
2497
3699
  guidesEnabled,
2498
3700
  snapEnabled,
2499
3701
  snapGuidesEnabled,
3702
+ selectNewGuideEnabled,
2500
3703
  altPressed,
2501
3704
  guideOrientation,
2502
3705
  hoverHighlightEnabled,
@@ -2529,24 +3732,24 @@ var useMeasurerPointer = ({
2529
3732
  setHoverPointer,
2530
3733
  clearSelectionRect
2531
3734
  }) => {
2532
- const hoverFrameRef = useRef6(null);
2533
- const hoverPointRef = useRef6(null);
2534
- const selectionCacheRef = useRef6({
3735
+ const hoverFrameRef = useRef9(null);
3736
+ const hoverPointRef = useRef9(null);
3737
+ const selectionCacheRef = useRef9({
2535
3738
  key: "",
2536
3739
  entries: [],
2537
3740
  overlayNode: null,
2538
3741
  frame: -1
2539
3742
  });
2540
- const shiftDragRef = useRef6(false);
2541
- const shiftToggleElementRef = useRef6(null);
2542
- useEffect3(() => {
3743
+ const shiftDragRef = useRef9(false);
3744
+ const shiftToggleElementRef = useRef9(null);
3745
+ useEffect5(() => {
2543
3746
  return () => {
2544
3747
  if (hoverFrameRef.current) {
2545
3748
  window2.cancelAnimationFrame(hoverFrameRef.current);
2546
3749
  }
2547
3750
  };
2548
3751
  }, []);
2549
- const updateHoverTarget = useCallback5(
3752
+ const updateHoverTarget = useCallback7(
2550
3753
  (point) => {
2551
3754
  const target = getTargetElement(point, overlayRef.current, document2);
2552
3755
  if (target) {
@@ -2565,18 +3768,19 @@ var useMeasurerPointer = ({
2565
3768
  },
2566
3769
  [document2, overlayRef, setHoverElement, setHoverRect]
2567
3770
  );
2568
- const updateHoverElement = useCallback5(
3771
+ const updateHoverElement = useCallback7(
2569
3772
  (point) => {
2570
3773
  const target = getTargetElement(point, overlayRef.current, document2);
2571
3774
  setHoverElement(target);
2572
3775
  },
2573
3776
  [document2, overlayRef, setHoverElement]
2574
3777
  );
2575
- const handlePointerDown = useCallback5(
3778
+ const handlePointerDown = useCallback7(
2576
3779
  (event) => {
2577
3780
  const commit = createActionCommit();
2578
3781
  const toolbarNode = toolbarRef.current;
2579
3782
  if (toolbarNode && toolbarNode.contains(event.target)) return;
3783
+ if (settingsOpen) return;
2580
3784
  if (!enabled || event.button !== 0) return;
2581
3785
  if (toolMode === "none") return;
2582
3786
  clearSelectionRect();
@@ -2601,7 +3805,11 @@ var useMeasurerPointer = ({
2601
3805
  return;
2602
3806
  }
2603
3807
  if (guidesEnabled) {
3808
+ event.preventDefault();
2604
3809
  commit();
3810
+ setStart(null);
3811
+ setEnd(null);
3812
+ setIsDragging(false);
2605
3813
  const position = getSnapGuidePosition({
2606
3814
  orientation: guideOrientation,
2607
3815
  point,
@@ -2612,7 +3820,7 @@ var useMeasurerPointer = ({
2612
3820
  document: document2
2613
3821
  });
2614
3822
  const id = createId();
2615
- setSelectedGuideIds([id]);
3823
+ setSelectedGuideIds(selectNewGuideEnabled ? [id] : []);
2616
3824
  setGuides((prev) => [
2617
3825
  ...prev,
2618
3826
  { id, orientation: guideOrientation, position }
@@ -2637,12 +3845,14 @@ var useMeasurerPointer = ({
2637
3845
  draggingGuideId,
2638
3846
  document2,
2639
3847
  enabled,
3848
+ settingsOpen,
2640
3849
  guideOrientation,
2641
3850
  guides,
2642
3851
  guidesEnabled,
2643
3852
  optionPairOverlay,
2644
3853
  overlayRef,
2645
3854
  scheduleGuideDragHold,
3855
+ selectNewGuideEnabled,
2646
3856
  selectedGuideIds.length,
2647
3857
  selectedMeasurements,
2648
3858
  setDraggingGuideId,
@@ -2657,10 +3867,11 @@ var useMeasurerPointer = ({
2657
3867
  toolbarRef
2658
3868
  ]
2659
3869
  );
2660
- const handlePointerMove = useCallback5(
3870
+ const handlePointerMove = useCallback7(
2661
3871
  (event) => {
2662
3872
  const toolbarNode = toolbarRef.current;
2663
3873
  if (toolbarNode && toolbarNode.contains(event.target)) return;
3874
+ if (settingsOpen) return;
2664
3875
  if (!enabled) return;
2665
3876
  if (toolMode === "none") {
2666
3877
  if (hoverHighlightEnabled) {
@@ -2679,7 +3890,7 @@ var useMeasurerPointer = ({
2679
3890
  if (!hoverFrameRef.current) {
2680
3891
  hoverFrameRef.current = window2.requestAnimationFrame(() => {
2681
3892
  const latest = hoverPointRef.current;
2682
- if (latest && !draggingGuideId) {
3893
+ if (latest && !draggingGuideId && !guidesEnabled) {
2683
3894
  if (hoverHighlightEnabled) {
2684
3895
  updateHoverTarget(latest);
2685
3896
  } else {
@@ -2729,6 +3940,7 @@ var useMeasurerPointer = ({
2729
3940
  )
2730
3941
  );
2731
3942
  }
3943
+ if (guidesEnabled) return;
2732
3944
  if (!start) return;
2733
3945
  setEnd(point);
2734
3946
  if (!isDragging) {
@@ -2744,6 +3956,7 @@ var useMeasurerPointer = ({
2744
3956
  altPressed,
2745
3957
  draggingGuideId,
2746
3958
  enabled,
3959
+ settingsOpen,
2747
3960
  hoverHighlightEnabled,
2748
3961
  guides,
2749
3962
  guidesEnabled,
@@ -2766,13 +3979,20 @@ var useMeasurerPointer = ({
2766
3979
  updateHoverTarget
2767
3980
  ]
2768
3981
  );
2769
- const handlePointerUp = useCallback5(
3982
+ const handlePointerUp = useCallback7(
2770
3983
  (event) => {
2771
3984
  const commit = createActionCommit();
2772
3985
  const toolbarNode = toolbarRef.current;
2773
3986
  if (toolbarNode && toolbarNode.contains(event.target)) return;
3987
+ if (settingsOpen) return;
2774
3988
  if (!enabled) return;
2775
3989
  clearGuideDragHold();
3990
+ if (guidesEnabled) {
3991
+ setStart(null);
3992
+ setEnd(null);
3993
+ setIsDragging(false);
3994
+ return;
3995
+ }
2776
3996
  if (toolMode === "none") {
2777
3997
  setStart(null);
2778
3998
  setEnd(null);
@@ -2943,7 +4163,9 @@ var useMeasurerPointer = ({
2943
4163
  createActionCommit,
2944
4164
  draggingGuideId,
2945
4165
  enabled,
4166
+ settingsOpen,
2946
4167
  end,
4168
+ guidesEnabled,
2947
4169
  hoverHighlightEnabled,
2948
4170
  isDragging,
2949
4171
  overlayRef,
@@ -2966,7 +4188,7 @@ var useMeasurerPointer = ({
2966
4188
  toolbarRef
2967
4189
  ]
2968
4190
  );
2969
- const handlePointerLeave = useCallback5(() => {
4191
+ const handlePointerLeave = useCallback7(() => {
2970
4192
  clearGuideDragHold();
2971
4193
  setStart(null);
2972
4194
  setEnd(null);
@@ -2990,54 +4212,58 @@ var useMeasurerPointer = ({
2990
4212
  };
2991
4213
 
2992
4214
  // hooks/use-overlay-refs.ts
2993
- import { useRef as useRef7 } from "react";
4215
+ import { useRef as useRef10 } from "react";
2994
4216
  var useOverlayRefs = () => {
2995
- const overlayRef = useRef7(null);
2996
- const selectedElementRef = useRef7(null);
2997
- const hoverElementRef = useRef7(null);
4217
+ const overlayRef = useRef10(null);
4218
+ const selectedElementRef = useRef10(null);
4219
+ const hoverElementRef = useRef10(null);
2998
4220
  return { overlayRef, selectedElementRef, hoverElementRef };
2999
4221
  };
3000
4222
 
3001
4223
  // hooks/use-resize-sync.ts
3002
- import { useEffect as useEffect4, useRef as useRef8 } from "react";
4224
+ import { useEffect as useEffect6, useRef as useRef11 } from "react";
3003
4225
  var useResizeSync = (params) => {
3004
- const resizeFrameRef = useRef8(null);
3005
- const viewportRef = useRef8(getViewportSize(params.window));
3006
- useEffect4(() => {
4226
+ const paramsRef = useRef11(params);
4227
+ paramsRef.current = params;
4228
+ const resizeFrameRef = useRef11(null);
4229
+ const viewportRef = useRef11(getViewportSize(params.window));
4230
+ useEffect6(() => {
4231
+ const ownerWindow = params.window;
3007
4232
  const handleResize = () => {
4233
+ const current = paramsRef.current;
3008
4234
  if (resizeFrameRef.current) {
3009
- params.window.cancelAnimationFrame(resizeFrameRef.current);
4235
+ ownerWindow.cancelAnimationFrame(resizeFrameRef.current);
3010
4236
  }
3011
- resizeFrameRef.current = params.window.requestAnimationFrame(() => {
3012
- const viewport = getViewportSize(params.window);
4237
+ resizeFrameRef.current = ownerWindow.requestAnimationFrame(() => {
4238
+ const viewport = getViewportSize(ownerWindow);
3013
4239
  const previousViewport = viewportRef.current;
3014
- params.setMeasurements(
4240
+ current.setMeasurements(
3015
4241
  (prev) => prev.map(
3016
- (measurement) => updateMeasurementForResize(measurement, viewport, params.document)
4242
+ (measurement) => updateMeasurementForResize(measurement, viewport, current.document)
3017
4243
  )
3018
4244
  );
3019
- params.setActiveMeasurement(
3020
- (prev) => prev ? updateMeasurementForResize(prev, viewport, params.document) : prev
4245
+ current.setActiveMeasurement(
4246
+ (prev) => prev ? updateMeasurementForResize(prev, viewport, current.document) : prev
3021
4247
  );
3022
- params.setHeldDistances(
4248
+ current.setHeldDistances(
3023
4249
  (prev) => prev.map(
3024
4250
  (distance) => updateDistanceForResize(
3025
4251
  distance,
3026
4252
  viewport,
3027
- params.document,
3028
- params.window
4253
+ current.document,
4254
+ ownerWindow
3029
4255
  )
3030
4256
  )
3031
4257
  );
3032
- if (params.selectedElementRef.current && params.document.contains(params.selectedElementRef.current)) {
3033
- params.setSelectedMeasurement(
3034
- getInspectMeasurement(params.selectedElementRef.current, params.window)
4258
+ if (current.selectedElementRef.current && current.document.contains(current.selectedElementRef.current)) {
4259
+ current.setSelectedMeasurement(
4260
+ getInspectMeasurement(current.selectedElementRef.current, ownerWindow)
3035
4261
  );
3036
4262
  }
3037
4263
  if (previousViewport.width > 0 && previousViewport.height > 0) {
3038
4264
  const scaleX = viewport.width / previousViewport.width;
3039
4265
  const scaleY = viewport.height / previousViewport.height;
3040
- params.setGuides(
4266
+ current.setGuides(
3041
4267
  (prev) => prev.map(
3042
4268
  (guide) => guide.orientation === "vertical" ? { ...guide, position: guide.position * scaleX } : { ...guide, position: guide.position * scaleY }
3043
4269
  )
@@ -3046,27 +4272,27 @@ var useResizeSync = (params) => {
3046
4272
  viewportRef.current = viewport;
3047
4273
  });
3048
4274
  };
3049
- params.window.addEventListener("resize", handleResize);
4275
+ ownerWindow.addEventListener("resize", handleResize);
3050
4276
  return () => {
3051
4277
  if (resizeFrameRef.current) {
3052
- params.window.cancelAnimationFrame(resizeFrameRef.current);
4278
+ ownerWindow.cancelAnimationFrame(resizeFrameRef.current);
3053
4279
  }
3054
- params.window.removeEventListener("resize", handleResize);
4280
+ ownerWindow.removeEventListener("resize", handleResize);
3055
4281
  };
3056
- }, [params]);
4282
+ }, [params.window]);
3057
4283
  };
3058
4284
 
3059
4285
  // render/measurer-overlay.tsx
3060
4286
  import { memo as memo6 } from "react";
3061
4287
 
3062
4288
  // components/measure-tag.tsx
3063
- import { jsx as jsx4 } from "react/jsx-runtime";
4289
+ import { jsx as jsx7 } from "react/jsx-runtime";
3064
4290
  function MeasureTag({
3065
4291
  children,
3066
4292
  className = "",
3067
4293
  style
3068
4294
  }) {
3069
- return /* @__PURE__ */ jsx4(
4295
+ return /* @__PURE__ */ jsx7(
3070
4296
  "div",
3071
4297
  {
3072
4298
  className: cn(
@@ -3081,20 +4307,20 @@ function MeasureTag({
3081
4307
 
3082
4308
  // components/measurement-box.tsx
3083
4309
  import { memo as memo3 } from "react";
3084
- import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
4310
+ import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
3085
4311
  var formatValue2 = (value) => Math.round(value);
3086
4312
  var MeasurementBox = memo3(function MeasurementBox2({
3087
4313
  measurement,
3088
4314
  transitionMs,
3089
4315
  labelOffset,
3090
- edgeVisibility
4316
+ edgeVisibility,
4317
+ outlineColor,
4318
+ fillColor
3091
4319
  }) {
3092
4320
  const edges = edgeVisibility ?? { top: true, right: true, bottom: true, left: true };
3093
4321
  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(
4322
+ return /* @__PURE__ */ jsxs7("div", { className: "msr:pointer-events-none", children: [
4323
+ /* @__PURE__ */ jsxs7(
3098
4324
  "div",
3099
4325
  {
3100
4326
  className: "msr:absolute",
@@ -3107,28 +4333,28 @@ var MeasurementBox = memo3(function MeasurementBox2({
3107
4333
  transition: `left ${transitionMs}ms ease, top ${transitionMs}ms ease, width ${transitionMs}ms ease, height ${transitionMs}ms ease`
3108
4334
  },
3109
4335
  children: [
3110
- edges.top ? /* @__PURE__ */ jsx5(
4336
+ edges.top ? /* @__PURE__ */ jsx8(
3111
4337
  "div",
3112
4338
  {
3113
4339
  className: "msr:absolute msr:left-0 msr:top-0 msr:h-px msr:w-full",
3114
4340
  style: { backgroundColor: outlineColor }
3115
4341
  }
3116
4342
  ) : null,
3117
- edges.right ? /* @__PURE__ */ jsx5(
4343
+ edges.right ? /* @__PURE__ */ jsx8(
3118
4344
  "div",
3119
4345
  {
3120
4346
  className: "msr:absolute msr:right-0 msr:top-0 msr:h-full msr:w-px",
3121
4347
  style: { backgroundColor: outlineColor }
3122
4348
  }
3123
4349
  ) : null,
3124
- edges.bottom ? /* @__PURE__ */ jsx5(
4350
+ edges.bottom ? /* @__PURE__ */ jsx8(
3125
4351
  "div",
3126
4352
  {
3127
4353
  className: "msr:absolute msr:bottom-0 msr:left-0 msr:h-px msr:w-full",
3128
4354
  style: { backgroundColor: outlineColor }
3129
4355
  }
3130
4356
  ) : null,
3131
- edges.left ? /* @__PURE__ */ jsx5(
4357
+ edges.left ? /* @__PURE__ */ jsx8(
3132
4358
  "div",
3133
4359
  {
3134
4360
  className: "msr:absolute msr:left-0 msr:top-0 msr:h-full msr:w-px",
@@ -3138,7 +4364,7 @@ var MeasurementBox = memo3(function MeasurementBox2({
3138
4364
  ]
3139
4365
  }
3140
4366
  ),
3141
- /* @__PURE__ */ jsxs3(
4367
+ /* @__PURE__ */ jsxs7(
3142
4368
  MeasureTag,
3143
4369
  {
3144
4370
  className: "msr:-translate-x-1/2 msr:bg-ink-900/90",
@@ -3159,20 +4385,20 @@ var MeasurementBox = memo3(function MeasurementBox2({
3159
4385
 
3160
4386
  // components/selected-measurement-box.tsx
3161
4387
  import { memo as memo4 } from "react";
3162
- import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
4388
+ import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
3163
4389
  var formatValue3 = (value) => Math.round(value);
3164
4390
  var SelectedMeasurementBox = memo4(function SelectedMeasurementBox2({
3165
4391
  measurement,
3166
4392
  transitionMs,
3167
4393
  labelOffset,
3168
- edgeVisibility
4394
+ edgeVisibility,
4395
+ outlineColor,
4396
+ fillColor
3169
4397
  }) {
3170
4398
  const edges = edgeVisibility ?? { top: true, right: true, bottom: true, left: true };
3171
4399
  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(
4400
+ return /* @__PURE__ */ jsxs8("div", { className: "msr:pointer-events-none", "data-mesurer-selected-measurement": "true", children: [
4401
+ /* @__PURE__ */ jsxs8(
3176
4402
  "div",
3177
4403
  {
3178
4404
  className: "msr:absolute",
@@ -3185,28 +4411,28 @@ var SelectedMeasurementBox = memo4(function SelectedMeasurementBox2({
3185
4411
  transition: `left ${transitionMs}ms ease, top ${transitionMs}ms ease, width ${transitionMs}ms ease, height ${transitionMs}ms ease`
3186
4412
  },
3187
4413
  children: [
3188
- edges.top ? /* @__PURE__ */ jsx6(
4414
+ edges.top ? /* @__PURE__ */ jsx9(
3189
4415
  "div",
3190
4416
  {
3191
4417
  className: "msr:absolute msr:left-0 msr:top-0 msr:h-px msr:w-full",
3192
4418
  style: { backgroundColor: outlineColor }
3193
4419
  }
3194
4420
  ) : null,
3195
- edges.right ? /* @__PURE__ */ jsx6(
4421
+ edges.right ? /* @__PURE__ */ jsx9(
3196
4422
  "div",
3197
4423
  {
3198
4424
  className: "msr:absolute msr:right-0 msr:top-0 msr:h-full msr:w-px",
3199
4425
  style: { backgroundColor: outlineColor }
3200
4426
  }
3201
4427
  ) : null,
3202
- edges.bottom ? /* @__PURE__ */ jsx6(
4428
+ edges.bottom ? /* @__PURE__ */ jsx9(
3203
4429
  "div",
3204
4430
  {
3205
4431
  className: "msr:absolute msr:bottom-0 msr:left-0 msr:h-px msr:w-full",
3206
4432
  style: { backgroundColor: outlineColor }
3207
4433
  }
3208
4434
  ) : null,
3209
- edges.left ? /* @__PURE__ */ jsx6(
4435
+ edges.left ? /* @__PURE__ */ jsx9(
3210
4436
  "div",
3211
4437
  {
3212
4438
  className: "msr:absolute msr:left-0 msr:top-0 msr:h-full msr:w-px",
@@ -3216,7 +4442,7 @@ var SelectedMeasurementBox = memo4(function SelectedMeasurementBox2({
3216
4442
  ]
3217
4443
  }
3218
4444
  ),
3219
- /* @__PURE__ */ jsxs4(
4445
+ /* @__PURE__ */ jsxs8(
3220
4446
  MeasureTag,
3221
4447
  {
3222
4448
  className: "msr:-translate-x-1/2 msr:bg-ink-900/90",
@@ -3237,13 +4463,13 @@ var SelectedMeasurementBox = memo4(function SelectedMeasurementBox2({
3237
4463
 
3238
4464
  // render/distance-overlay-item.tsx
3239
4465
  import { memo as memo5 } from "react";
3240
- import { Fragment, jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
4466
+ import { Fragment as Fragment4, jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
3241
4467
  var DistanceOverlayItem = memo5(function DistanceOverlayItem2({
3242
4468
  distance,
3243
4469
  labelOffset,
3244
4470
  onRemove
3245
4471
  }) {
3246
- return /* @__PURE__ */ jsxs5(
4472
+ return /* @__PURE__ */ jsxs9(
3247
4473
  "div",
3248
4474
  {
3249
4475
  className: onRemove ? "msr:pointer-events-auto" : "msr:pointer-events-none",
@@ -3252,7 +4478,7 @@ var DistanceOverlayItem = memo5(function DistanceOverlayItem2({
3252
4478
  onRemove(distance.id);
3253
4479
  } : void 0,
3254
4480
  children: [
3255
- /* @__PURE__ */ jsx7(
4481
+ /* @__PURE__ */ jsx10(
3256
4482
  "div",
3257
4483
  {
3258
4484
  className: "msr:absolute msr:rounded msr:border msr:border-[#2563eb]/70",
@@ -3264,7 +4490,7 @@ var DistanceOverlayItem = memo5(function DistanceOverlayItem2({
3264
4490
  }
3265
4491
  }
3266
4492
  ),
3267
- /* @__PURE__ */ jsx7(
4493
+ /* @__PURE__ */ jsx10(
3268
4494
  "div",
3269
4495
  {
3270
4496
  className: "msr:absolute msr:rounded msr:border msr:border-[#2563eb]/70",
@@ -3277,7 +4503,7 @@ var DistanceOverlayItem = memo5(function DistanceOverlayItem2({
3277
4503
  }
3278
4504
  ),
3279
4505
  distance.connectors.map(
3280
- (connector, index) => Math.abs(connector.x1 - connector.x2) < 1 ? /* @__PURE__ */ jsx7(
4506
+ (connector, index) => Math.abs(connector.x1 - connector.x2) < 1 ? /* @__PURE__ */ jsx10(
3281
4507
  "div",
3282
4508
  {
3283
4509
  className: "msr:absolute msr:border-l msr:border-dashed msr:border-[#2563eb]/70",
@@ -3288,7 +4514,7 @@ var DistanceOverlayItem = memo5(function DistanceOverlayItem2({
3288
4514
  }
3289
4515
  },
3290
4516
  `${distance.id}-connector-${index}`
3291
- ) : /* @__PURE__ */ jsx7(
4517
+ ) : /* @__PURE__ */ jsx10(
3292
4518
  "div",
3293
4519
  {
3294
4520
  className: "msr:absolute msr:border-t msr:border-dashed msr:border-[#2563eb]/70",
@@ -3301,8 +4527,8 @@ var DistanceOverlayItem = memo5(function DistanceOverlayItem2({
3301
4527
  `${distance.id}-connector-${index}`
3302
4528
  )
3303
4529
  ),
3304
- distance.horizontal && distance.horizontal.value > 0 ? /* @__PURE__ */ jsxs5(Fragment, { children: [
3305
- /* @__PURE__ */ jsx7(
4530
+ distance.horizontal && distance.horizontal.value > 0 ? /* @__PURE__ */ jsxs9(Fragment4, { children: [
4531
+ /* @__PURE__ */ jsx10(
3306
4532
  "div",
3307
4533
  {
3308
4534
  className: "msr:absolute msr:h-px msr:bg-[#2563eb]",
@@ -3313,7 +4539,7 @@ var DistanceOverlayItem = memo5(function DistanceOverlayItem2({
3313
4539
  }
3314
4540
  }
3315
4541
  ),
3316
- /* @__PURE__ */ jsx7(
4542
+ /* @__PURE__ */ jsx10(
3317
4543
  MeasureTag,
3318
4544
  {
3319
4545
  className: "msr:-translate-x-1/2 msr:bg-ink-900/90",
@@ -3325,8 +4551,8 @@ var DistanceOverlayItem = memo5(function DistanceOverlayItem2({
3325
4551
  }
3326
4552
  )
3327
4553
  ] }) : null,
3328
- distance.vertical && distance.vertical.value > 0 ? /* @__PURE__ */ jsxs5(Fragment, { children: [
3329
- /* @__PURE__ */ jsx7(
4554
+ distance.vertical && distance.vertical.value > 0 ? /* @__PURE__ */ jsxs9(Fragment4, { children: [
4555
+ /* @__PURE__ */ jsx10(
3330
4556
  "div",
3331
4557
  {
3332
4558
  className: "msr:absolute msr:w-px msr:bg-[#2563eb]",
@@ -3337,7 +4563,7 @@ var DistanceOverlayItem = memo5(function DistanceOverlayItem2({
3337
4563
  }
3338
4564
  }
3339
4565
  ),
3340
- /* @__PURE__ */ jsx7(
4566
+ /* @__PURE__ */ jsx10(
3341
4567
  MeasureTag,
3342
4568
  {
3343
4569
  className: "msr:-translate-y-1/2 msr:bg-ink-900/90",
@@ -3355,9 +4581,10 @@ var DistanceOverlayItem = memo5(function DistanceOverlayItem2({
3355
4581
  });
3356
4582
 
3357
4583
  // render/measurer-overlay.tsx
3358
- import { Fragment as Fragment2, jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
4584
+ import { Fragment as Fragment5, jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
3359
4585
  var MeasurerOverlay = memo6(function MeasurerOverlay2({
3360
4586
  enabled,
4587
+ interactive = true,
3361
4588
  toolMode,
3362
4589
  guidePointerEvents,
3363
4590
  guidesEnabled,
@@ -3387,6 +4614,7 @@ var MeasurerOverlay = memo6(function MeasurerOverlay2({
3387
4614
  guideColorActive,
3388
4615
  guideColorHover,
3389
4616
  guideColorDefault,
4617
+ guideStyle,
3390
4618
  onPointerDown,
3391
4619
  onPointerMove,
3392
4620
  onPointerUp,
@@ -3397,30 +4625,32 @@ var MeasurerOverlay = memo6(function MeasurerOverlay2({
3397
4625
  onGuidePointerCancel
3398
4626
  }) {
3399
4627
  const overlayVisible = enabled;
3400
- const overlayInteractive = overlayVisible && toolMode !== "none" && toolMode !== "text-inspector" && toolMode !== "xray" && toolMode !== "rulers";
3401
- const selectionVisible = toolMode === "select";
3402
- return /* @__PURE__ */ jsxs6(
4628
+ const overlayInteractive = interactive && overlayVisible && toolMode !== "none" && toolMode !== "text-inspector" && toolMode !== "xray" && toolMode !== "rulers";
4629
+ const selectionVisible = interactive && toolMode === "select";
4630
+ return /* @__PURE__ */ jsxs10(
3403
4631
  "div",
3404
4632
  {
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"}`,
4633
+ 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
4634
  style: { pointerEvents: overlayInteractive ? "auto" : "none" },
3407
4635
  onPointerDown,
3408
4636
  onPointerMove,
3409
4637
  onPointerUp,
3410
4638
  onPointerLeave,
3411
4639
  children: [
3412
- selectionVisible ? displayedMeasurements.map((measurement, index) => /* @__PURE__ */ jsx8(
4640
+ selectionVisible ? displayedMeasurements.map((measurement, index) => /* @__PURE__ */ jsx11(
3413
4641
  MeasurementBox,
3414
4642
  {
3415
4643
  measurement,
3416
4644
  transitionMs: MEASURE_TRANSITION_MS,
3417
4645
  labelOffset: MEASURE_LABEL_OFFSET,
3418
- edgeVisibility: measurementEdgeVisibility[index]
4646
+ edgeVisibility: measurementEdgeVisibility[index],
4647
+ outlineColor,
4648
+ fillColor
3419
4649
  },
3420
4650
  measurement.id
3421
4651
  )) : null,
3422
- selectionVisible && activeRect && isDragging ? /* @__PURE__ */ jsxs6(Fragment2, { children: [
3423
- /* @__PURE__ */ jsxs6(
4652
+ selectionVisible && activeRect && isDragging ? /* @__PURE__ */ jsxs10(Fragment5, { children: [
4653
+ /* @__PURE__ */ jsxs10(
3424
4654
  "div",
3425
4655
  {
3426
4656
  className: "msr:pointer-events-none msr:absolute",
@@ -3432,28 +4662,28 @@ var MeasurerOverlay = memo6(function MeasurerOverlay2({
3432
4662
  backgroundColor: fillColor
3433
4663
  },
3434
4664
  children: [
3435
- /* @__PURE__ */ jsx8(
4665
+ /* @__PURE__ */ jsx11(
3436
4666
  "div",
3437
4667
  {
3438
4668
  className: "msr:absolute msr:left-0 msr:top-0 msr:h-px msr:w-full",
3439
4669
  style: { backgroundColor: outlineColor }
3440
4670
  }
3441
4671
  ),
3442
- /* @__PURE__ */ jsx8(
4672
+ /* @__PURE__ */ jsx11(
3443
4673
  "div",
3444
4674
  {
3445
4675
  className: "msr:absolute msr:right-0 msr:top-0 msr:h-full msr:w-px",
3446
4676
  style: { backgroundColor: outlineColor }
3447
4677
  }
3448
4678
  ),
3449
- /* @__PURE__ */ jsx8(
4679
+ /* @__PURE__ */ jsx11(
3450
4680
  "div",
3451
4681
  {
3452
4682
  className: "msr:absolute msr:bottom-0 msr:left-0 msr:h-px msr:w-full",
3453
4683
  style: { backgroundColor: outlineColor }
3454
4684
  }
3455
4685
  ),
3456
- /* @__PURE__ */ jsx8(
4686
+ /* @__PURE__ */ jsx11(
3457
4687
  "div",
3458
4688
  {
3459
4689
  className: "msr:absolute msr:left-0 msr:top-0 msr:h-full msr:w-px",
@@ -3463,7 +4693,7 @@ var MeasurerOverlay = memo6(function MeasurerOverlay2({
3463
4693
  ]
3464
4694
  }
3465
4695
  ),
3466
- /* @__PURE__ */ jsxs6(
4696
+ /* @__PURE__ */ jsxs10(
3467
4697
  MeasureTag,
3468
4698
  {
3469
4699
  className: "msr:-translate-x-1/2 msr:bg-ink-900/90",
@@ -3479,7 +4709,7 @@ var MeasurerOverlay = memo6(function MeasurerOverlay2({
3479
4709
  }
3480
4710
  )
3481
4711
  ] }) : null,
3482
- selectionVisible && hoverRectToShow ? /* @__PURE__ */ jsxs6(
4712
+ selectionVisible && hoverRectToShow ? /* @__PURE__ */ jsxs10(
3483
4713
  "div",
3484
4714
  {
3485
4715
  className: "msr:pointer-events-none msr:absolute",
@@ -3491,28 +4721,28 @@ var MeasurerOverlay = memo6(function MeasurerOverlay2({
3491
4721
  backgroundColor: fillColor
3492
4722
  },
3493
4723
  children: [
3494
- hoverEdgeVisibility?.top ? /* @__PURE__ */ jsx8(
4724
+ hoverEdgeVisibility?.top ? /* @__PURE__ */ jsx11(
3495
4725
  "div",
3496
4726
  {
3497
4727
  className: "msr:absolute msr:left-0 msr:top-0 msr:h-px msr:w-full",
3498
4728
  style: { backgroundColor: outlineColor }
3499
4729
  }
3500
4730
  ) : null,
3501
- hoverEdgeVisibility?.right ? /* @__PURE__ */ jsx8(
4731
+ hoverEdgeVisibility?.right ? /* @__PURE__ */ jsx11(
3502
4732
  "div",
3503
4733
  {
3504
4734
  className: "msr:absolute msr:right-0 msr:top-0 msr:h-full msr:w-px",
3505
4735
  style: { backgroundColor: outlineColor }
3506
4736
  }
3507
4737
  ) : null,
3508
- hoverEdgeVisibility?.bottom ? /* @__PURE__ */ jsx8(
4738
+ hoverEdgeVisibility?.bottom ? /* @__PURE__ */ jsx11(
3509
4739
  "div",
3510
4740
  {
3511
4741
  className: "msr:absolute msr:bottom-0 msr:left-0 msr:h-px msr:w-full",
3512
4742
  style: { backgroundColor: outlineColor }
3513
4743
  }
3514
4744
  ) : null,
3515
- hoverEdgeVisibility?.left ? /* @__PURE__ */ jsx8(
4745
+ hoverEdgeVisibility?.left ? /* @__PURE__ */ jsx11(
3516
4746
  "div",
3517
4747
  {
3518
4748
  className: "msr:absolute msr:left-0 msr:top-0 msr:h-full msr:w-px",
@@ -3522,7 +4752,7 @@ var MeasurerOverlay = memo6(function MeasurerOverlay2({
3522
4752
  ]
3523
4753
  }
3524
4754
  ) : null,
3525
- guidesEnabled && guidePreview ? /* @__PURE__ */ jsx8(
4755
+ interactive && guidesEnabled && guidePreview ? /* @__PURE__ */ jsx11(
3526
4756
  "div",
3527
4757
  {
3528
4758
  className: "msr:pointer-events-none msr:absolute",
@@ -3537,7 +4767,7 @@ var MeasurerOverlay = memo6(function MeasurerOverlay2({
3537
4767
  height: GUIDE_HITBOX_SIZE,
3538
4768
  width: "100%"
3539
4769
  },
3540
- children: /* @__PURE__ */ jsx8(
4770
+ children: /* @__PURE__ */ jsx11(
3541
4771
  "div",
3542
4772
  {
3543
4773
  className: "msr:absolute",
@@ -3558,17 +4788,19 @@ var MeasurerOverlay = memo6(function MeasurerOverlay2({
3558
4788
  )
3559
4789
  }
3560
4790
  ) : null,
3561
- selectionVisible ? displayedSelectedMeasurements.map((measurement, index) => /* @__PURE__ */ jsx8(
4791
+ selectionVisible ? displayedSelectedMeasurements.map((measurement, index) => /* @__PURE__ */ jsx11(
3562
4792
  SelectedMeasurementBox,
3563
4793
  {
3564
4794
  measurement,
3565
4795
  transitionMs: MEASURE_TRANSITION_MS,
3566
4796
  labelOffset: MEASURE_LABEL_OFFSET,
3567
- edgeVisibility: selectedEdgeVisibility[index]
4797
+ edgeVisibility: selectedEdgeVisibility[index],
4798
+ outlineColor,
4799
+ fillColor
3568
4800
  },
3569
4801
  measurement.id
3570
4802
  )) : null,
3571
- heldDistances.map((distance) => /* @__PURE__ */ jsx8(
4803
+ interactive ? heldDistances.map((distance) => /* @__PURE__ */ jsx11(
3572
4804
  DistanceOverlayItem,
3573
4805
  {
3574
4806
  distance,
@@ -3576,8 +4808,8 @@ var MeasurerOverlay = memo6(function MeasurerOverlay2({
3576
4808
  onRemove: onRemoveHeldDistance
3577
4809
  },
3578
4810
  `held-${distance.id}`
3579
- )),
3580
- selectionVisible && altPressed && optionPairOverlay ? /* @__PURE__ */ jsx8(
4811
+ )) : null,
4812
+ selectionVisible && altPressed && optionPairOverlay ? /* @__PURE__ */ jsx11(
3581
4813
  DistanceOverlayItem,
3582
4814
  {
3583
4815
  distance: optionPairOverlay,
@@ -3585,7 +4817,7 @@ var MeasurerOverlay = memo6(function MeasurerOverlay2({
3585
4817
  },
3586
4818
  `option-${optionPairOverlay.id}`
3587
4819
  ) : null,
3588
- guidesEnabled && altPressed && guideDistanceOverlay ? /* @__PURE__ */ jsx8(
4820
+ interactive && guidesEnabled && altPressed && guideDistanceOverlay ? /* @__PURE__ */ jsx11(
3589
4821
  DistanceOverlayItem,
3590
4822
  {
3591
4823
  distance: guideDistanceOverlay,
@@ -3593,9 +4825,9 @@ var MeasurerOverlay = memo6(function MeasurerOverlay2({
3593
4825
  },
3594
4826
  `guide-preview-${guideDistanceOverlay.id}`
3595
4827
  ) : null,
3596
- selectionVisible && altPressed && optionContainerLines ? /* @__PURE__ */ jsxs6(Fragment2, { children: [
3597
- optionContainerLines.top.value > 0 ? /* @__PURE__ */ jsxs6(Fragment2, { children: [
3598
- /* @__PURE__ */ jsx8(
4828
+ selectionVisible && altPressed && optionContainerLines ? /* @__PURE__ */ jsxs10(Fragment5, { children: [
4829
+ optionContainerLines.top.value > 0 ? /* @__PURE__ */ jsxs10(Fragment5, { children: [
4830
+ /* @__PURE__ */ jsx11(
3599
4831
  "div",
3600
4832
  {
3601
4833
  className: "msr:absolute msr:w-px msr:bg-[#2563eb]",
@@ -3606,7 +4838,7 @@ var MeasurerOverlay = memo6(function MeasurerOverlay2({
3606
4838
  }
3607
4839
  }
3608
4840
  ),
3609
- /* @__PURE__ */ jsx8(
4841
+ /* @__PURE__ */ jsx11(
3610
4842
  MeasureTag,
3611
4843
  {
3612
4844
  className: "msr:-translate-y-1/2 msr:bg-ink-900/90",
@@ -3618,8 +4850,8 @@ var MeasurerOverlay = memo6(function MeasurerOverlay2({
3618
4850
  }
3619
4851
  )
3620
4852
  ] }) : null,
3621
- optionContainerLines.bottom.value > 0 ? /* @__PURE__ */ jsxs6(Fragment2, { children: [
3622
- /* @__PURE__ */ jsx8(
4853
+ optionContainerLines.bottom.value > 0 ? /* @__PURE__ */ jsxs10(Fragment5, { children: [
4854
+ /* @__PURE__ */ jsx11(
3623
4855
  "div",
3624
4856
  {
3625
4857
  className: "msr:absolute msr:w-px msr:bg-[#2563eb]",
@@ -3630,7 +4862,7 @@ var MeasurerOverlay = memo6(function MeasurerOverlay2({
3630
4862
  }
3631
4863
  }
3632
4864
  ),
3633
- /* @__PURE__ */ jsx8(
4865
+ /* @__PURE__ */ jsx11(
3634
4866
  MeasureTag,
3635
4867
  {
3636
4868
  className: "msr:-translate-y-1/2 msr:bg-ink-900/90",
@@ -3642,8 +4874,8 @@ var MeasurerOverlay = memo6(function MeasurerOverlay2({
3642
4874
  }
3643
4875
  )
3644
4876
  ] }) : null,
3645
- optionContainerLines.left.value > 0 ? /* @__PURE__ */ jsxs6(Fragment2, { children: [
3646
- /* @__PURE__ */ jsx8(
4877
+ optionContainerLines.left.value > 0 ? /* @__PURE__ */ jsxs10(Fragment5, { children: [
4878
+ /* @__PURE__ */ jsx11(
3647
4879
  "div",
3648
4880
  {
3649
4881
  className: "msr:absolute msr:h-px msr:bg-[#2563eb]",
@@ -3654,7 +4886,7 @@ var MeasurerOverlay = memo6(function MeasurerOverlay2({
3654
4886
  }
3655
4887
  }
3656
4888
  ),
3657
- /* @__PURE__ */ jsx8(
4889
+ /* @__PURE__ */ jsx11(
3658
4890
  MeasureTag,
3659
4891
  {
3660
4892
  className: "msr:-translate-x-1/2 msr:bg-ink-900/90",
@@ -3666,8 +4898,8 @@ var MeasurerOverlay = memo6(function MeasurerOverlay2({
3666
4898
  }
3667
4899
  )
3668
4900
  ] }) : null,
3669
- optionContainerLines.right.value > 0 ? /* @__PURE__ */ jsxs6(Fragment2, { children: [
3670
- /* @__PURE__ */ jsx8(
4901
+ optionContainerLines.right.value > 0 ? /* @__PURE__ */ jsxs10(Fragment5, { children: [
4902
+ /* @__PURE__ */ jsx11(
3671
4903
  "div",
3672
4904
  {
3673
4905
  className: "msr:absolute msr:h-px msr:bg-[#2563eb]",
@@ -3678,7 +4910,7 @@ var MeasurerOverlay = memo6(function MeasurerOverlay2({
3678
4910
  }
3679
4911
  }
3680
4912
  ),
3681
- /* @__PURE__ */ jsx8(
4913
+ /* @__PURE__ */ jsx11(
3682
4914
  MeasureTag,
3683
4915
  {
3684
4916
  className: "msr:-translate-x-1/2 msr:bg-ink-900/90",
@@ -3694,9 +4926,12 @@ var MeasurerOverlay = memo6(function MeasurerOverlay2({
3694
4926
  guides.map((guide) => {
3695
4927
  const isSelected = selectedGuideIds.includes(guide.id);
3696
4928
  const isHovered = hoverGuide?.id === guide.id;
3697
- const strokeWidth = isSelected || isHovered ? 2 : 1;
3698
4929
  const strokeColor = isSelected ? guideColorActive : isHovered ? guideColorHover : guideColorDefault;
3699
- return /* @__PURE__ */ jsx8(
4930
+ const strokeWidth = Math.max(guideStyle.width, isSelected || isHovered ? 2 : guideStyle.width);
4931
+ const isSolid = guideStyle.pattern === "solid";
4932
+ 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)`;
4933
+ const backgroundSize = guideStyle.pattern === "dotted" ? guide.orientation === "vertical" ? `${strokeWidth}px ${guideStyle.dashLength + guideStyle.gap}px` : `${guideStyle.dashLength + guideStyle.gap}px ${strokeWidth}px` : void 0;
4934
+ return /* @__PURE__ */ jsx11(
3700
4935
  "div",
3701
4936
  {
3702
4937
  className: "msr:absolute",
@@ -3717,7 +4952,7 @@ var MeasurerOverlay = memo6(function MeasurerOverlay2({
3717
4952
  onPointerDown: (event) => onGuidePointerDown(guide, event),
3718
4953
  onPointerUp: (event) => onGuidePointerUp(guide, event),
3719
4954
  onPointerCancel: (event) => onGuidePointerCancel(guide, event),
3720
- children: /* @__PURE__ */ jsx8(
4955
+ children: /* @__PURE__ */ jsx11(
3721
4956
  "div",
3722
4957
  {
3723
4958
  className: "msr:absolute",
@@ -3726,13 +4961,19 @@ var MeasurerOverlay = memo6(function MeasurerOverlay2({
3726
4961
  top: 0,
3727
4962
  width: strokeWidth,
3728
4963
  height: "100%",
3729
- backgroundColor: strokeColor
4964
+ backgroundColor: isSolid ? strokeColor : "transparent",
4965
+ backgroundImage,
4966
+ backgroundSize,
4967
+ opacity: guideStyle.opacity
3730
4968
  } : {
3731
4969
  top: GUIDE_HITBOX_SIZE / 2 - 1,
3732
4970
  left: 0,
3733
4971
  height: strokeWidth,
3734
4972
  width: "100%",
3735
- backgroundColor: strokeColor
4973
+ backgroundColor: isSolid ? strokeColor : "transparent",
4974
+ backgroundImage,
4975
+ backgroundSize,
4976
+ opacity: guideStyle.opacity
3736
4977
  }
3737
4978
  }
3738
4979
  )
@@ -4253,6 +5494,7 @@ var createTextInspector = (options = {}, legacy = false) => {
4253
5494
  if (!element) return false;
4254
5495
  for (let node = element; node; node = node.parentElement) {
4255
5496
  if (node.classList.contains("mesurer-toolbar-surface")) return true;
5497
+ if (node.hasAttribute("data-mesurer-inspector-ui")) return true;
4256
5498
  }
4257
5499
  return false;
4258
5500
  };
@@ -4550,7 +5792,7 @@ var createTextInspector = (options = {}, legacy = false) => {
4550
5792
  const isInspectorUIEvent = (event) => event.composedPath().some(
4551
5793
  (node) => node instanceof HTMLElementConstructor && (() => {
4552
5794
  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");
5795
+ 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
5796
  })()
4555
5797
  );
4556
5798
  const onClick = (event) => {
@@ -4633,8 +5875,223 @@ var createTextInspector = (options = {}, legacy = false) => {
4633
5875
  };
4634
5876
  var TextInspector = createTextInspector({}, true);
4635
5877
 
5878
+ // core/persistence.ts
5879
+ var MESURER_STORAGE_VERSION = 2;
5880
+ var DEFAULT_GUIDE_STYLE = {
5881
+ opacity: 1,
5882
+ width: 1,
5883
+ pattern: "solid",
5884
+ dashLength: 6,
5885
+ gap: 4
5886
+ };
5887
+ var DEFAULT_RULER_SETTINGS = {
5888
+ opacity: 1,
5889
+ edgeReveal: false
5890
+ };
5891
+ var isFormat = (value) => value === "hex" || value === "rgb" || value === "hsl" || value === "oklch";
5892
+ var normalizeGuideStyle = (value) => {
5893
+ if (!value || typeof value !== "object") return void 0;
5894
+ const input = value;
5895
+ return {
5896
+ opacity: typeof input.opacity === "number" ? Math.min(1, Math.max(0, input.opacity)) : DEFAULT_GUIDE_STYLE.opacity,
5897
+ width: typeof input.width === "number" ? Math.min(4, Math.max(1, input.width)) : DEFAULT_GUIDE_STYLE.width,
5898
+ pattern: input.pattern === "dashed" || input.pattern === "dotted" ? input.pattern : DEFAULT_GUIDE_STYLE.pattern,
5899
+ dashLength: typeof input.dashLength === "number" ? Math.min(24, Math.max(2, input.dashLength)) : DEFAULT_GUIDE_STYLE.dashLength,
5900
+ gap: typeof input.gap === "number" ? Math.min(24, Math.max(0, input.gap)) : DEFAULT_GUIDE_STYLE.gap
5901
+ };
5902
+ };
5903
+ var normalizeRulerSettings = (value) => {
5904
+ if (!value || typeof value !== "object") return void 0;
5905
+ const input = value;
5906
+ return {
5907
+ opacity: typeof input.opacity === "number" ? Math.min(1, Math.max(0.2, input.opacity)) : DEFAULT_RULER_SETTINGS.opacity,
5908
+ edgeReveal: typeof input.edgeReveal === "boolean" ? input.edgeReveal : DEFAULT_RULER_SETTINGS.edgeReveal
5909
+ };
5910
+ };
5911
+ var isFiniteNumber = (value) => typeof value === "number" && Number.isFinite(value);
5912
+ var isRect = (value) => {
5913
+ if (!value || typeof value !== "object") return false;
5914
+ const rect = value;
5915
+ return isFiniteNumber(rect.left) && isFiniteNumber(rect.top) && isFiniteNumber(rect.width) && rect.width >= 0 && isFiniteNumber(rect.height) && rect.height >= 0;
5916
+ };
5917
+ var isNormalizedRect = (value) => isRect(value);
5918
+ var isMeasurement = (value) => {
5919
+ if (!value || typeof value !== "object") return false;
5920
+ const measurement = value;
5921
+ 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");
5922
+ };
5923
+ var isGuide = (value) => {
5924
+ if (!value || typeof value !== "object") return false;
5925
+ const guide = value;
5926
+ return typeof guide.id === "string" && (guide.orientation === "vertical" || guide.orientation === "horizontal") && isFiniteNumber(guide.position);
5927
+ };
5928
+ var isDistanceOverlay = (value) => {
5929
+ if (!value || typeof value !== "object") return false;
5930
+ const distance = value;
5931
+ const isDistanceLine = (line) => {
5932
+ if (line === null) return true;
5933
+ if (!line || typeof line !== "object") return false;
5934
+ const item = line;
5935
+ return isFiniteNumber(item.x1) && isFiniteNumber(item.x2) && isFiniteNumber(item.y) && isFiniteNumber(item.value);
5936
+ };
5937
+ const isVerticalLine = (line) => {
5938
+ if (line === null) return true;
5939
+ if (!line || typeof line !== "object") return false;
5940
+ const item = line;
5941
+ return isFiniteNumber(item.y1) && isFiniteNumber(item.y2) && isFiniteNumber(item.x) && isFiniteNumber(item.value);
5942
+ };
5943
+ 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) => {
5944
+ if (!connector || typeof connector !== "object") return false;
5945
+ const item = connector;
5946
+ return isFiniteNumber(item.x1) && isFiniteNumber(item.y1) && isFiniteNumber(item.x2) && isFiniteNumber(item.y2);
5947
+ });
5948
+ };
5949
+ var normalizeStoredSettings = (value) => {
5950
+ if (!value || typeof value !== "object") return {};
5951
+ const input = value;
5952
+ return {
5953
+ ...typeof input.highlightColor === "string" ? { highlightColor: input.highlightColor } : {},
5954
+ ...typeof input.guideColor === "string" ? { guideColor: input.guideColor } : {},
5955
+ ...typeof input.hoverHighlightEnabled === "boolean" ? { hoverHighlightEnabled: input.hoverHighlightEnabled } : {},
5956
+ ...Array.isArray(input.colorPickerFormats) && input.colorPickerFormats.some(isFormat) ? { colorPickerFormats: input.colorPickerFormats.filter(isFormat) } : {},
5957
+ ...isFormat(input.colorPickerClickFormat) ? { colorPickerClickFormat: input.colorPickerClickFormat } : {},
5958
+ ...typeof input.snapEnabled === "boolean" ? { snapEnabled: input.snapEnabled } : {},
5959
+ ...typeof input.snapGuidesEnabled === "boolean" ? { snapGuidesEnabled: input.snapGuidesEnabled } : {},
5960
+ ...typeof input.selectNewGuideEnabled === "boolean" ? { selectNewGuideEnabled: input.selectNewGuideEnabled } : {},
5961
+ ...typeof input.multiMeasureEnabled === "boolean" ? { multiMeasureEnabled: input.multiMeasureEnabled } : {},
5962
+ ...typeof input.persistOnReload === "boolean" ? { persistOnReload: input.persistOnReload } : {},
5963
+ ...normalizeGuideStyle(input.guideStyle) ? { guideStyle: normalizeGuideStyle(input.guideStyle) } : {},
5964
+ ...normalizeRulerSettings(input.rulerSettings) ? { rulerSettings: normalizeRulerSettings(input.rulerSettings) } : {}
5965
+ };
5966
+ };
5967
+ var normalizeStoredWorkspace = (value) => {
5968
+ if (!value || typeof value !== "object") return null;
5969
+ const input = value;
5970
+ 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;
5971
+ return {
5972
+ enabled: input.enabled,
5973
+ xrayVisible: typeof input.xrayVisible === "boolean" ? input.xrayVisible : input.toolMode === "xray",
5974
+ toolMode: input.toolMode,
5975
+ rulersVisible: input.rulersVisible,
5976
+ guideOrientation: input.guideOrientation,
5977
+ guides: input.guides.filter(isGuide),
5978
+ selectedGuideIds: input.selectedGuideIds.filter((id) => typeof id === "string"),
5979
+ measurements: input.measurements.filter(isMeasurement),
5980
+ activeMeasurement: isMeasurement(input.activeMeasurement) ? input.activeMeasurement : null,
5981
+ heldDistances: input.heldDistances.filter(isDistanceOverlay)
5982
+ };
5983
+ };
5984
+ var normalizePersistenceSnapshot = (value) => {
5985
+ if (!value || typeof value !== "object") return null;
5986
+ const record = value;
5987
+ if (record.version !== MESURER_STORAGE_VERSION) return null;
5988
+ return {
5989
+ settings: normalizeStoredSettings(record.settings),
5990
+ workspace: normalizeStoredWorkspace(record.workspace)
5991
+ };
5992
+ };
5993
+ var migrate = (record) => {
5994
+ if (record.version === MESURER_STORAGE_VERSION) {
5995
+ return normalizePersistenceSnapshot(record);
5996
+ }
5997
+ if (record.version !== 1) return null;
5998
+ return {
5999
+ settings: {},
6000
+ workspace: record.enabled === void 0 || !record.toolMode || !record.guideOrientation || !record.guides || !record.selectedGuideIds || !record.measurements || record.activeMeasurement === void 0 || !record.heldDistances ? null : normalizeStoredWorkspace({
6001
+ enabled: record.enabled,
6002
+ xrayVisible: record.toolMode === "xray",
6003
+ toolMode: record.toolMode,
6004
+ rulersVisible: record.rulersVisible ?? record.toolMode === "rulers",
6005
+ guideOrientation: record.guideOrientation,
6006
+ guides: record.guides,
6007
+ selectedGuideIds: record.selectedGuideIds,
6008
+ measurements: record.measurements,
6009
+ activeMeasurement: record.activeMeasurement,
6010
+ heldDistances: record.heldDistances
6011
+ })
6012
+ };
6013
+ };
6014
+ var createLocalStoragePersistence = (ownerWindow, workspaceKey, settingsKey = workspaceKey, legacyKey) => {
6015
+ let errorHandler;
6016
+ const readRecord = (key) => {
6017
+ try {
6018
+ const raw = ownerWindow.localStorage.getItem(key);
6019
+ if (!raw) return null;
6020
+ return migrate(JSON.parse(raw));
6021
+ } catch (error) {
6022
+ errorHandler?.(error);
6023
+ return null;
6024
+ }
6025
+ };
6026
+ const read = () => {
6027
+ const legacy = legacyKey ? readRecord(legacyKey) : null;
6028
+ const settingsRecord = readRecord(settingsKey);
6029
+ const workspaceRecord = readRecord(workspaceKey);
6030
+ if (!settingsRecord && !workspaceRecord && !legacy) return null;
6031
+ return {
6032
+ settings: settingsRecord?.settings ?? legacy?.settings ?? {},
6033
+ workspace: workspaceRecord?.workspace ?? legacy?.workspace ?? null
6034
+ };
6035
+ };
6036
+ const writeRecord = (key, snapshot) => {
6037
+ try {
6038
+ ownerWindow.localStorage.setItem(
6039
+ key,
6040
+ JSON.stringify({ version: MESURER_STORAGE_VERSION, ...snapshot })
6041
+ );
6042
+ } catch (error) {
6043
+ errorHandler?.(error);
6044
+ }
6045
+ };
6046
+ return {
6047
+ load: read,
6048
+ saveSettings: (settings) => {
6049
+ if (settingsKey === workspaceKey) {
6050
+ writeRecord(workspaceKey, { settings, workspace: read()?.workspace ?? null });
6051
+ return;
6052
+ }
6053
+ writeRecord(settingsKey, { settings, workspace: null });
6054
+ },
6055
+ saveWorkspace: (workspace) => {
6056
+ if (settingsKey === workspaceKey) {
6057
+ writeRecord(workspaceKey, { settings: read()?.settings ?? {}, workspace });
6058
+ return;
6059
+ }
6060
+ writeRecord(workspaceKey, { settings: {}, workspace });
6061
+ },
6062
+ clearWorkspace: () => {
6063
+ if (settingsKey === workspaceKey) {
6064
+ const current = read();
6065
+ if (current) writeRecord(workspaceKey, { settings: current.settings, workspace: null });
6066
+ return;
6067
+ }
6068
+ writeRecord(workspaceKey, { settings: {}, workspace: null });
6069
+ },
6070
+ clearSettings: () => {
6071
+ if (settingsKey === workspaceKey) {
6072
+ writeRecord(workspaceKey, { settings: {}, workspace: read()?.workspace ?? null });
6073
+ return;
6074
+ }
6075
+ writeRecord(settingsKey, { settings: {}, workspace: null });
6076
+ },
6077
+ setErrorHandler: (handler) => {
6078
+ errorHandler = handler;
6079
+ },
6080
+ subscribe: (listener) => {
6081
+ const handleStorage = (event) => {
6082
+ if (event.key !== settingsKey && event.key !== workspaceKey && event.key !== legacyKey) return;
6083
+ listener(read());
6084
+ };
6085
+ ownerWindow.addEventListener("storage", handleStorage);
6086
+ return () => {
6087
+ ownerWindow.removeEventListener("storage", handleStorage);
6088
+ };
6089
+ }
6090
+ };
6091
+ };
6092
+
4636
6093
  // measurer.tsx
4637
- import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
6094
+ import { jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
4638
6095
  var measurerInstanceCount = 0;
4639
6096
  var XRAY_STYLE_ID = "mesurer-xray-styles";
4640
6097
  var XRAY_STYLES = `
@@ -4646,7 +6103,11 @@ var XRAY_STYLES = `
4646
6103
  .xray-mode .mesurer-root,
4647
6104
  .xray-mode .mesurer-root *,
4648
6105
  .xray-mode .mesurer-toolbar-surface,
4649
- .xray-mode .mesurer-toolbar-surface * {
6106
+ .xray-mode .mesurer-toolbar-surface *,
6107
+ .xray-mode .mesurer-ti-box,
6108
+ .xray-mode .mesurer-ti-card,
6109
+ .xray-mode .mesurer-ti-card *,
6110
+ .xray-mode .mesurer-ti-close {
4650
6111
  outline: none !important;
4651
6112
  }
4652
6113
  `;
@@ -4666,70 +6127,112 @@ var stripDistance = (distance) => ({
4666
6127
  elementRefA: void 0,
4667
6128
  elementRefB: void 0
4668
6129
  });
6130
+ var TAB_ID_KEY = "mesurer:tab-id";
6131
+ var SETTINGS_STORAGE_KEY = "mesurer-settings";
6132
+ var LEGACY_STORAGE_KEY = "mesurer-state";
6133
+ var getTabId = (ownerWindow) => {
6134
+ try {
6135
+ const existing = ownerWindow.sessionStorage.getItem(TAB_ID_KEY);
6136
+ if (existing) return existing;
6137
+ const id = ownerWindow.crypto.randomUUID();
6138
+ ownerWindow.sessionStorage.setItem(TAB_ID_KEY, id);
6139
+ return id;
6140
+ } catch {
6141
+ return "session";
6142
+ }
6143
+ };
6144
+ var sanitizeStoredSettings = (ownerWindow, settings) => {
6145
+ const supportsColor = (value) => value !== void 0 && ownerWindow.CSS?.supports("color", value) === true;
6146
+ return {
6147
+ ...settings,
6148
+ ...supportsColor(settings.highlightColor) ? {} : { highlightColor: void 0 },
6149
+ ...supportsColor(settings.guideColor) ? {} : { guideColor: void 0 }
6150
+ };
6151
+ };
4669
6152
  function MeasurerClient({
4670
6153
  highlightColor,
4671
6154
  guideColor,
4672
6155
  hoverHighlightEnabled,
4673
6156
  persistOnReload,
4674
6157
  portalTarget,
4675
- persistKey
6158
+ persistKey,
6159
+ colorPickerFormats,
6160
+ colorPickerClickFormat,
6161
+ snapEnabled: snapEnabledDefault,
6162
+ snapGuidesEnabled: snapGuidesEnabledDefault,
6163
+ selectNewGuideEnabled: selectNewGuideEnabledDefault,
6164
+ multiMeasureEnabled: multiMeasureEnabledDefault,
6165
+ guideStyle: guideStyleDefault,
6166
+ rulerSettings: rulerSettingsDefault,
6167
+ persistence,
6168
+ onPersistenceError
4676
6169
  }) {
4677
- const instanceIdRef = useRef9(null);
6170
+ const instanceIdRef = useRef12(null);
4678
6171
  if (instanceIdRef.current === null) {
4679
6172
  instanceIdRef.current = ++measurerInstanceCount;
4680
6173
  }
4681
- const storageKey = persistKey ?? (instanceIdRef.current === 1 ? "mesurer-state" : `mesurer-state-${instanceIdRef.current}`);
4682
6174
  const ownerDocument = portalTarget.ownerDocument ?? document;
4683
6175
  const ownerWindow = ownerDocument.defaultView ?? window;
4684
- const guideScrollRef = useRef9({
6176
+ const tabIdRef = useRef12(null);
6177
+ if (tabIdRef.current === null) tabIdRef.current = getTabId(ownerWindow);
6178
+ const storageKey = persistKey ?? (instanceIdRef.current === 1 ? `mesurer-state:${tabIdRef.current}` : `mesurer-state:${tabIdRef.current}:${instanceIdRef.current}`);
6179
+ const legacyStorageKey = persistKey ? void 0 : LEGACY_STORAGE_KEY;
6180
+ const guideScrollRef = useRef12({
4685
6181
  x: ownerWindow.scrollX,
4686
6182
  y: ownerWindow.scrollY
4687
6183
  });
4688
- const textInspectorRef = useRef9(null);
6184
+ const textInspectorRef = useRef12(null);
4689
6185
  if (!textInspectorRef.current) {
4690
6186
  textInspectorRef.current = createTextInspector({ portalTarget });
4691
6187
  }
4692
6188
  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(
6189
+ const selectionRectRef = useRef12(null);
6190
+ const toolbarRef = useRef12(null);
6191
+ const selectionAnimationCleanupTimeoutRef = useRef12(null);
6192
+ const guideDragRef = useRef12(null);
6193
+ const guideUserSelectRef = useRef12(null);
6194
+ const persistenceErrorHandlerRef = useRef12(onPersistenceError);
6195
+ persistenceErrorHandlerRef.current = onPersistenceError;
6196
+ const activePersistence = useMemo2(() => {
6197
+ const next = persistence ?? createLocalStoragePersistence(ownerWindow, storageKey, SETTINGS_STORAGE_KEY, legacyStorageKey);
6198
+ next.setErrorHandler?.((error) => persistenceErrorHandlerRef.current?.(error));
6199
+ return next;
6200
+ }, [legacyStorageKey, ownerWindow, persistence, storageKey]);
6201
+ const storedState = useMemo2(
6202
+ () => activePersistence.load(),
6203
+ [activePersistence]
6204
+ );
6205
+ const persistedState = persistOnReload || storedState?.settings.persistOnReload ? storedState?.workspace ?? null : null;
6206
+ const persistedSettings = sanitizeStoredSettings(ownerWindow, storedState?.settings ?? {});
6207
+ useEffect7(() => {
6208
+ return () => activePersistence.setErrorHandler?.(void 0);
6209
+ }, [activePersistence]);
6210
+ const enabledRef = useRef12(false);
6211
+ const toolModeRef = useRef12(
4712
6212
  persistedState?.toolMode === "rulers" ? "none" : persistedState?.toolMode ?? "none"
4713
6213
  );
4714
- const rulersVisibleRef = useRef9(
6214
+ const rulersVisibleRef = useRef12(
4715
6215
  persistedState?.rulersVisible ?? persistedState?.toolMode === "rulers"
4716
6216
  );
4717
- const guideOrientationRef = useRef9(
6217
+ const xrayVisibleRef = useRef12(persistedState?.xrayVisible ?? persistedState?.toolMode === "xray");
6218
+ const guideOrientationRef = useRef12(
4718
6219
  persistedState?.guideOrientation ?? "vertical"
4719
6220
  );
4720
- const measurementsRef = useRef9(
6221
+ const measurementsRef = useRef12(
4721
6222
  persistedState?.measurements ?? []
4722
6223
  );
4723
- const activeMeasurementRef = useRef9(
6224
+ const activeMeasurementRef = useRef12(
4724
6225
  persistedState?.activeMeasurement ?? null
4725
6226
  );
4726
- const heldDistancesRef = useRef9(
6227
+ const heldDistancesRef = useRef12(
4727
6228
  persistedState?.heldDistances ?? []
4728
6229
  );
4729
- const guidesRef = useRef9(persistedState?.guides ?? []);
4730
- const selectedGuideIdsRef = useRef9(
6230
+ const guidesRef = useRef12(persistedState?.guides ?? []);
6231
+ const selectedGuideIdsRef = useRef12(
4731
6232
  persistedState?.selectedGuideIds ?? []
4732
6233
  );
6234
+ const workspacePersistTimeoutRef = useRef12(null);
6235
+ const applyingExternalPersistenceRef = useRef12(false);
4733
6236
  const { overlayRef, selectedElementRef, hoverElementRef } = useOverlayRefs();
4734
6237
  const {
4735
6238
  selectionOriginRect,
@@ -4759,11 +6262,20 @@ function MeasurerClient({
4759
6262
  setRulersVisible,
4760
6263
  guidesEnabled,
4761
6264
  multiMeasureEnabled,
4762
- snapGuidesEnabled
6265
+ snapGuidesEnabled,
6266
+ setSnapGuidesEnabled,
6267
+ selectNewGuideEnabled,
6268
+ setSelectNewGuideEnabled,
6269
+ setSnapEnabled,
6270
+ setMultiMeasureEnabled
4763
6271
  } = useMeasureToggles({
4764
6272
  initialEnabled: persistedState?.enabled,
4765
6273
  initialToolMode: persistedState?.toolMode === "rulers" ? "none" : persistedState?.toolMode,
4766
- initialRulersVisible: persistedState?.rulersVisible ?? persistedState?.toolMode === "rulers"
6274
+ initialRulersVisible: persistedState?.rulersVisible ?? persistedState?.toolMode === "rulers",
6275
+ initialSnapEnabled: persistedSettings.snapEnabled ?? snapEnabledDefault,
6276
+ initialSnapGuidesEnabled: persistedSettings.snapGuidesEnabled ?? snapGuidesEnabledDefault,
6277
+ initialSelectNewGuideEnabled: persistedSettings.selectNewGuideEnabled ?? selectNewGuideEnabledDefault,
6278
+ initialMultiMeasureEnabled: persistedSettings.multiMeasureEnabled ?? multiMeasureEnabledDefault
4767
6279
  });
4768
6280
  const { start, setStart, end, setEnd, isDragging, setIsDragging } = useDragState();
4769
6281
  const {
@@ -4795,11 +6307,58 @@ function MeasurerClient({
4795
6307
  initialGuides: persistedState?.guides ?? [],
4796
6308
  initialSelectedGuideIds: persistedState?.selectedGuideIds ?? []
4797
6309
  });
4798
- const [toolbarActive, setToolbarActive] = useState8(true);
6310
+ const [toolbarActive, setToolbarActive] = useState11(true);
6311
+ const [colorPickerActive, setColorPickerActive] = useState11(false);
6312
+ const [colorPickerSample, setColorPickerSample] = useState11(null);
6313
+ const [colorPickerUnsupported, setColorPickerUnsupported] = useState11(false);
6314
+ const [settingsOpen, setSettingsOpen] = useState11(false);
6315
+ const [settingsHighlightColor, setSettingsHighlightColor] = useState11(
6316
+ persistedSettings.highlightColor ?? highlightColor
6317
+ );
6318
+ const [settingsGuideColor, setSettingsGuideColor] = useState11(
6319
+ persistedSettings.guideColor ?? guideColor
6320
+ );
6321
+ const [settingsHoverHighlight, setSettingsHoverHighlight] = useState11(
6322
+ persistedSettings.hoverHighlightEnabled ?? hoverHighlightEnabled
6323
+ );
6324
+ const [settingsPersistOnReload, setSettingsPersistOnReload] = useState11(
6325
+ persistedSettings.persistOnReload ?? persistOnReload
6326
+ );
6327
+ const [settingsColorFormats, setSettingsColorFormats] = useState11(
6328
+ persistedSettings.colorPickerFormats ?? colorPickerFormats
6329
+ );
6330
+ const [settingsColorClickFormat, setSettingsColorClickFormat] = useState11(
6331
+ persistedSettings.colorPickerClickFormat ?? colorPickerClickFormat
6332
+ );
6333
+ const [settingsGuideStyle, setSettingsGuideStyle] = useState11({
6334
+ ...guideStyleDefault,
6335
+ ...persistedSettings.guideStyle
6336
+ });
6337
+ const [settingsRulerSettings, setSettingsRulerSettings] = useState11({
6338
+ ...rulerSettingsDefault,
6339
+ ...persistedSettings.rulerSettings
6340
+ });
6341
+ const [xrayVisible, setXrayVisible] = useState11(xrayVisibleRef.current);
4799
6342
  const { clearGuideDragHold, scheduleGuideDragHold } = useGuideDragHold(ownerWindow);
4800
- const [guidePreview, setGuidePreview] = useState8(null);
4801
- const [guideOrientation, setGuideOrientation] = useState8(persistedState?.guideOrientation ?? "vertical");
6343
+ const [guidePreview, setGuidePreview] = useState11(null);
6344
+ const resetSettings = () => {
6345
+ setSettingsHighlightColor(highlightColor);
6346
+ setSettingsGuideColor(guideColor);
6347
+ setSettingsHoverHighlight(hoverHighlightEnabled);
6348
+ setSettingsPersistOnReload(persistOnReload);
6349
+ setSettingsColorFormats([...colorPickerFormats]);
6350
+ setSettingsColorClickFormat(colorPickerClickFormat);
6351
+ setSnapEnabled(snapEnabledDefault);
6352
+ setSnapGuidesEnabled(snapGuidesEnabledDefault);
6353
+ setSelectNewGuideEnabled(selectNewGuideEnabledDefault);
6354
+ setMultiMeasureEnabled(multiMeasureEnabledDefault);
6355
+ setSettingsGuideStyle({ ...guideStyleDefault });
6356
+ setSettingsRulerSettings({ ...rulerSettingsDefault });
6357
+ };
6358
+ const initialSettingsTab = colorPickerActive ? "color-picker" : rulersVisible ? "rulers" : toolMode === "guides" ? "guides" : toolMode === "select" || toolMode === "text-inspector" ? "select" : "general";
6359
+ const [guideOrientation, setGuideOrientation] = useState11(persistedState?.guideOrientation ?? "vertical");
4802
6360
  enabledRef.current = enabled;
6361
+ xrayVisibleRef.current = xrayVisible;
4803
6362
  toolModeRef.current = toolMode;
4804
6363
  rulersVisibleRef.current = rulersVisible;
4805
6364
  guideOrientationRef.current = guideOrientation;
@@ -4808,29 +6367,173 @@ function MeasurerClient({
4808
6367
  heldDistancesRef.current = heldDistances;
4809
6368
  guidesRef.current = guides;
4810
6369
  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 {
6370
+ const saveWorkspace = useCallback8(() => {
6371
+ if (!settingsPersistOnReload) return;
6372
+ const workspace = {
6373
+ enabled: enabledRef.current,
6374
+ xrayVisible: xrayVisibleRef.current,
6375
+ toolMode: toolModeRef.current,
6376
+ rulersVisible: rulersVisibleRef.current,
6377
+ guideOrientation: guideOrientationRef.current,
6378
+ guides: guidesRef.current,
6379
+ selectedGuideIds: selectedGuideIdsRef.current,
6380
+ measurements: measurementsRef.current.map(stripMeasurement),
6381
+ activeMeasurement: activeMeasurementRef.current ? stripMeasurement(activeMeasurementRef.current) : null,
6382
+ heldDistances: heldDistancesRef.current.map(stripDistance)
6383
+ };
6384
+ activePersistence.saveWorkspace(workspace);
6385
+ }, [activePersistence, settingsPersistOnReload]);
6386
+ const persistState = useCallback8(() => {
6387
+ if (!settingsPersistOnReload) return;
6388
+ if (workspacePersistTimeoutRef.current !== null) {
6389
+ ownerWindow.clearTimeout(workspacePersistTimeoutRef.current);
6390
+ }
6391
+ workspacePersistTimeoutRef.current = ownerWindow.setTimeout(() => {
6392
+ workspacePersistTimeoutRef.current = null;
6393
+ saveWorkspace();
6394
+ }, 250);
6395
+ }, [ownerWindow, saveWorkspace, settingsPersistOnReload]);
6396
+ const persistSettings = useCallback8(() => {
6397
+ activePersistence.saveSettings({
6398
+ highlightColor: settingsHighlightColor,
6399
+ guideColor: settingsGuideColor,
6400
+ hoverHighlightEnabled: settingsHoverHighlight,
6401
+ colorPickerFormats: settingsColorFormats,
6402
+ colorPickerClickFormat: settingsColorClickFormat,
6403
+ snapEnabled,
6404
+ snapGuidesEnabled,
6405
+ selectNewGuideEnabled,
6406
+ multiMeasureEnabled,
6407
+ persistOnReload: settingsPersistOnReload,
6408
+ guideStyle: settingsGuideStyle,
6409
+ rulerSettings: settingsRulerSettings
6410
+ });
6411
+ }, [
6412
+ multiMeasureEnabled,
6413
+ selectNewGuideEnabled,
6414
+ settingsColorClickFormat,
6415
+ settingsColorFormats,
6416
+ settingsGuideColor,
6417
+ settingsHighlightColor,
6418
+ settingsHoverHighlight,
6419
+ settingsPersistOnReload,
6420
+ snapEnabled,
6421
+ snapGuidesEnabled,
6422
+ activePersistence,
6423
+ settingsGuideStyle,
6424
+ settingsRulerSettings
6425
+ ]);
6426
+ useEffect7(() => {
6427
+ if (applyingExternalPersistenceRef.current) {
6428
+ applyingExternalPersistenceRef.current = false;
6429
+ return;
6430
+ }
6431
+ persistSettings();
6432
+ if (settingsPersistOnReload) persistState();
6433
+ }, [persistSettings, persistState, settingsPersistOnReload]);
6434
+ useEffect7(() => {
6435
+ if (!settingsPersistOnReload) return;
6436
+ const handleBeforeUnload = () => saveWorkspace();
6437
+ ownerWindow.addEventListener("beforeunload", handleBeforeUnload);
6438
+ return () => {
6439
+ ownerWindow.removeEventListener("beforeunload", handleBeforeUnload);
6440
+ if (workspacePersistTimeoutRef.current !== null) {
6441
+ ownerWindow.clearTimeout(workspacePersistTimeoutRef.current);
6442
+ workspacePersistTimeoutRef.current = null;
6443
+ saveWorkspace();
6444
+ }
6445
+ };
6446
+ }, [ownerWindow, saveWorkspace, settingsPersistOnReload]);
6447
+ const clearPersistedWorkspace = useCallback8(() => {
6448
+ enabledRef.current = false;
6449
+ toolModeRef.current = "none";
6450
+ rulersVisibleRef.current = false;
6451
+ xrayVisibleRef.current = false;
6452
+ guideOrientationRef.current = "vertical";
6453
+ measurementsRef.current = [];
6454
+ activeMeasurementRef.current = null;
6455
+ heldDistancesRef.current = [];
6456
+ guidesRef.current = [];
6457
+ selectedGuideIdsRef.current = [];
6458
+ setEnabled(false);
6459
+ setToolMode("none");
6460
+ setRulersVisible(false);
6461
+ setXrayVisible(false);
6462
+ setGuideOrientation("vertical");
6463
+ setMeasurements([]);
6464
+ setActiveMeasurement(null);
6465
+ setSelectedMeasurement(null);
6466
+ setSelectedMeasurements([]);
6467
+ setHeldDistances([]);
6468
+ setGuides([]);
6469
+ setSelectedGuideIds([]);
6470
+ }, [setActiveMeasurement, setEnabled, setGuideOrientation, setGuides, setHeldDistances, setMeasurements, setRulersVisible, setSelectedGuideIds, setSelectedMeasurement, setSelectedMeasurements, setToolMode]);
6471
+ const clearWorkspace = useCallback8(() => {
6472
+ clearPersistedWorkspace();
6473
+ activePersistence.clearWorkspace();
6474
+ }, [activePersistence, clearPersistedWorkspace]);
6475
+ const applyPersistedWorkspace = useCallback8((workspace) => {
6476
+ enabledRef.current = workspace.enabled;
6477
+ toolModeRef.current = workspace.toolMode;
6478
+ rulersVisibleRef.current = workspace.rulersVisible;
6479
+ xrayVisibleRef.current = workspace.xrayVisible;
6480
+ guideOrientationRef.current = workspace.guideOrientation;
6481
+ measurementsRef.current = workspace.measurements;
6482
+ activeMeasurementRef.current = workspace.activeMeasurement;
6483
+ heldDistancesRef.current = workspace.heldDistances;
6484
+ guidesRef.current = workspace.guides;
6485
+ selectedGuideIdsRef.current = workspace.selectedGuideIds;
6486
+ setEnabled(workspace.enabled);
6487
+ setToolMode(workspace.toolMode);
6488
+ setRulersVisible(workspace.rulersVisible);
6489
+ setXrayVisible(workspace.xrayVisible);
6490
+ setGuideOrientation(workspace.guideOrientation);
6491
+ setMeasurements(workspace.measurements);
6492
+ setActiveMeasurement(workspace.activeMeasurement);
6493
+ setGuides(workspace.guides);
6494
+ setSelectedGuideIds(workspace.selectedGuideIds);
6495
+ setHeldDistances(workspace.heldDistances);
6496
+ }, [setActiveMeasurement, setEnabled, setGuideOrientation, setGuides, setHeldDistances, setMeasurements, setRulersVisible, setSelectedGuideIds, setToolMode]);
6497
+ const applyPersistenceSnapshot = useCallback8((snapshot) => {
6498
+ if (!snapshot) {
6499
+ clearPersistedWorkspace();
6500
+ return;
4831
6501
  }
4832
- }, [persistOnReload, storageKey]);
4833
- const setEnabledPersisted = useCallback6(
6502
+ applyingExternalPersistenceRef.current = true;
6503
+ const settings = sanitizeStoredSettings(ownerWindow, snapshot.settings);
6504
+ if (settings.highlightColor !== void 0) setSettingsHighlightColor(settings.highlightColor);
6505
+ if (settings.guideColor !== void 0) setSettingsGuideColor(settings.guideColor);
6506
+ if (settings.hoverHighlightEnabled !== void 0) setSettingsHoverHighlight(settings.hoverHighlightEnabled);
6507
+ if (settings.colorPickerFormats !== void 0) setSettingsColorFormats(settings.colorPickerFormats);
6508
+ if (settings.colorPickerClickFormat !== void 0) setSettingsColorClickFormat(settings.colorPickerClickFormat);
6509
+ if (settings.persistOnReload !== void 0) setSettingsPersistOnReload(settings.persistOnReload);
6510
+ if (settings.snapEnabled !== void 0) setSnapEnabled(settings.snapEnabled);
6511
+ if (settings.snapGuidesEnabled !== void 0) setSnapGuidesEnabled(settings.snapGuidesEnabled);
6512
+ if (settings.selectNewGuideEnabled !== void 0) setSelectNewGuideEnabled(settings.selectNewGuideEnabled);
6513
+ if (settings.multiMeasureEnabled !== void 0) setMultiMeasureEnabled(settings.multiMeasureEnabled);
6514
+ if (settings.guideStyle !== void 0) setSettingsGuideStyle({ ...guideStyleDefault, ...settings.guideStyle });
6515
+ if (settings.rulerSettings !== void 0) setSettingsRulerSettings({ ...rulerSettingsDefault, ...settings.rulerSettings });
6516
+ const workspace = snapshot.workspace;
6517
+ if (!workspace) {
6518
+ clearPersistedWorkspace();
6519
+ } else if (settings.persistOnReload ?? settingsPersistOnReload) {
6520
+ applyPersistedWorkspace(workspace);
6521
+ }
6522
+ ownerWindow.setTimeout(() => {
6523
+ applyingExternalPersistenceRef.current = false;
6524
+ }, 0);
6525
+ }, [applyPersistedWorkspace, clearPersistedWorkspace, guideStyleDefault, ownerWindow, rulerSettingsDefault, setMultiMeasureEnabled, setSelectNewGuideEnabled, setSettingsColorClickFormat, setSettingsColorFormats, setSettingsGuideColor, setSettingsHighlightColor, setSettingsHoverHighlight, setSettingsPersistOnReload, setSnapEnabled, setSnapGuidesEnabled, settingsPersistOnReload]);
6526
+ const previousPersistenceRef = useRef12(activePersistence);
6527
+ useEffect7(() => {
6528
+ if (previousPersistenceRef.current === activePersistence) return;
6529
+ previousPersistenceRef.current = activePersistence;
6530
+ applyPersistenceSnapshot(storedState);
6531
+ }, [activePersistence, applyPersistenceSnapshot, storedState]);
6532
+ useEffect7(() => {
6533
+ const unsubscribe = activePersistence.subscribe?.(applyPersistenceSnapshot);
6534
+ return unsubscribe;
6535
+ }, [activePersistence, applyPersistenceSnapshot]);
6536
+ const setEnabledPersisted = useCallback8(
4834
6537
  (value) => {
4835
6538
  const next = typeof value === "function" ? value(enabledRef.current) : value;
4836
6539
  if (Object.is(next, enabledRef.current)) return;
@@ -4840,7 +6543,7 @@ function MeasurerClient({
4840
6543
  },
4841
6544
  [persistState, setEnabled]
4842
6545
  );
4843
- const setRulersVisiblePersisted = useCallback6(
6546
+ const setRulersVisiblePersisted = useCallback8(
4844
6547
  (value) => {
4845
6548
  const next = typeof value === "function" ? value(rulersVisibleRef.current) : value;
4846
6549
  if (Object.is(next, rulersVisibleRef.current)) return;
@@ -4850,7 +6553,7 @@ function MeasurerClient({
4850
6553
  },
4851
6554
  [persistState, setRulersVisible]
4852
6555
  );
4853
- const setToolModePersisted = useCallback6(
6556
+ const setToolModePersisted = useCallback8(
4854
6557
  (value) => {
4855
6558
  const next = typeof value === "function" ? value(toolModeRef.current) : value;
4856
6559
  if (Object.is(next, toolModeRef.current)) return;
@@ -4865,7 +6568,7 @@ function MeasurerClient({
4865
6568
  },
4866
6569
  [persistState, setToolMode, textInspector]
4867
6570
  );
4868
- const setGuideOrientationPersisted = useCallback6(
6571
+ const setGuideOrientationPersisted = useCallback8(
4869
6572
  (value) => {
4870
6573
  const next = typeof value === "function" ? value(guideOrientationRef.current) : value;
4871
6574
  if (Object.is(next, guideOrientationRef.current)) return;
@@ -4875,7 +6578,7 @@ function MeasurerClient({
4875
6578
  },
4876
6579
  [persistState, setGuideOrientation]
4877
6580
  );
4878
- const setMeasurementsPersisted = useCallback6(
6581
+ const setMeasurementsPersisted = useCallback8(
4879
6582
  (value) => {
4880
6583
  const next = typeof value === "function" ? value(measurementsRef.current) : value;
4881
6584
  if (Object.is(next, measurementsRef.current)) return;
@@ -4885,7 +6588,7 @@ function MeasurerClient({
4885
6588
  },
4886
6589
  [persistState, setMeasurements]
4887
6590
  );
4888
- const setActiveMeasurementPersisted = useCallback6(
6591
+ const setActiveMeasurementPersisted = useCallback8(
4889
6592
  (value) => {
4890
6593
  const next = typeof value === "function" ? value(activeMeasurementRef.current) : value;
4891
6594
  if (Object.is(next, activeMeasurementRef.current)) return;
@@ -4895,7 +6598,7 @@ function MeasurerClient({
4895
6598
  },
4896
6599
  [persistState, setActiveMeasurement]
4897
6600
  );
4898
- const setHeldDistancesPersisted = useCallback6(
6601
+ const setHeldDistancesPersisted = useCallback8(
4899
6602
  (value) => {
4900
6603
  const next = typeof value === "function" ? value(heldDistancesRef.current) : value;
4901
6604
  if (Object.is(next, heldDistancesRef.current)) return;
@@ -4905,7 +6608,7 @@ function MeasurerClient({
4905
6608
  },
4906
6609
  [persistState, setHeldDistances]
4907
6610
  );
4908
- const setGuidesPersisted = useCallback6(
6611
+ const setGuidesPersisted = useCallback8(
4909
6612
  (value) => {
4910
6613
  const next = typeof value === "function" ? value(guidesRef.current) : value;
4911
6614
  if (Object.is(next, guidesRef.current)) return;
@@ -4915,7 +6618,7 @@ function MeasurerClient({
4915
6618
  },
4916
6619
  [persistState, setGuides]
4917
6620
  );
4918
- const setSelectedGuideIdsPersisted = useCallback6(
6621
+ const setSelectedGuideIdsPersisted = useCallback8(
4919
6622
  (value) => {
4920
6623
  const next = typeof value === "function" ? value(selectedGuideIdsRef.current) : value;
4921
6624
  if (Object.is(next, selectedGuideIdsRef.current)) return;
@@ -4973,15 +6676,15 @@ function MeasurerClient({
4973
6676
  clearSelectionRect
4974
6677
  }
4975
6678
  });
4976
- const undo = useCallback6(() => {
6679
+ const undo = useCallback8(() => {
4977
6680
  if (toolMode === "text-inspector" && textInspector.undo()) return;
4978
6681
  undoHistory();
4979
6682
  }, [textInspector, toolMode, undoHistory]);
4980
- const redo = useCallback6(() => {
6683
+ const redo = useCallback8(() => {
4981
6684
  if (toolMode === "text-inspector" && textInspector.redo()) return;
4982
6685
  redoHistory();
4983
6686
  }, [redoHistory, textInspector, toolMode]);
4984
- const clearAll = useCallback6(() => {
6687
+ const clearAll = useCallback8(() => {
4985
6688
  if (toolMode === "text-inspector") {
4986
6689
  textInspector.clear();
4987
6690
  }
@@ -5021,7 +6724,7 @@ function MeasurerClient({
5021
6724
  textInspector,
5022
6725
  toolMode
5023
6726
  ]);
5024
- const removeSelectedGuides = useCallback6(() => {
6727
+ const removeSelectedGuides = useCallback8(() => {
5025
6728
  if (selectedGuideIds.length === 0) return false;
5026
6729
  recordSnapshot();
5027
6730
  setGuidesPersisted(
@@ -5035,6 +6738,29 @@ function MeasurerClient({
5035
6738
  setGuidesPersisted,
5036
6739
  setSelectedGuideIdsPersisted
5037
6740
  ]);
6741
+ const openColorPicker = useCallback8(async () => {
6742
+ const EyeDropper = ownerWindow.EyeDropper;
6743
+ setEnabledWithHistory(true);
6744
+ setToolModeWithHistory("none");
6745
+ setColorPickerActive(true);
6746
+ setColorPickerSample(null);
6747
+ setColorPickerUnsupported(!EyeDropper);
6748
+ if (!EyeDropper) return;
6749
+ try {
6750
+ const result = await new EyeDropper().open();
6751
+ const nextSample = parseCssColor(result.sRGBHex);
6752
+ if (!nextSample) return;
6753
+ setColorPickerSample(nextSample);
6754
+ const clipboardWrite = ownerWindow.navigator.clipboard?.writeText(
6755
+ formatColor(nextSample, settingsColorClickFormat)
6756
+ );
6757
+ void clipboardWrite?.catch(() => void 0);
6758
+ } catch (error) {
6759
+ if (error instanceof DOMException && error.name === "AbortError") {
6760
+ setColorPickerActive(false);
6761
+ }
6762
+ }
6763
+ }, [ownerWindow, setEnabledWithHistory, setToolModeWithHistory, settingsColorClickFormat]);
5038
6764
  useHotkeys({
5039
6765
  eventTarget: ownerWindow,
5040
6766
  clearAll,
@@ -5047,7 +6773,13 @@ function MeasurerClient({
5047
6773
  setAltPressed,
5048
6774
  isOverlayActive: () => enabled && (toolMode !== "none" || toolbarActive),
5049
6775
  setGuideOrientation: setGuideOrientationWithHistory,
5050
- onInteract: () => setToolbarActive(true)
6776
+ onInteract: () => setToolbarActive(true),
6777
+ onColorPicker: openColorPicker,
6778
+ onToggleXray: () => setXrayVisible((previous) => !previous),
6779
+ onToggleSettings: () => setSettingsOpen((previous) => !previous),
6780
+ isSettingsOpen: () => settingsOpen,
6781
+ onCloseColorPicker: () => setColorPickerActive(false),
6782
+ isColorPickerActive: () => colorPickerActive
5051
6783
  });
5052
6784
  useResizeSync({
5053
6785
  document: ownerDocument,
@@ -5059,7 +6791,7 @@ function MeasurerClient({
5059
6791
  setGuides: setGuidesPersisted,
5060
6792
  selectedElementRef
5061
6793
  });
5062
- useEffect5(() => {
6794
+ useEffect7(() => {
5063
6795
  const handleScroll = () => {
5064
6796
  const next = {
5065
6797
  x: ownerWindow.scrollX,
@@ -5083,6 +6815,7 @@ function MeasurerClient({
5083
6815
  document: ownerDocument,
5084
6816
  window: ownerWindow,
5085
6817
  enabled,
6818
+ selectionEnabled: toolMode === "select" && !settingsOpen,
5086
6819
  selectedElementRef,
5087
6820
  hoverElementRef,
5088
6821
  setSelectedMeasurement,
@@ -5092,7 +6825,7 @@ function MeasurerClient({
5092
6825
  setActiveMeasurement: setActiveMeasurementPersisted,
5093
6826
  setHeldDistances: setHeldDistancesPersisted
5094
6827
  });
5095
- useEffect5(() => {
6828
+ useEffect7(() => {
5096
6829
  if (!toolbarActive || toolMode !== "none") return;
5097
6830
  const handlePointerDown2 = (event) => {
5098
6831
  const toolbarNode = toolbarRef.current;
@@ -5104,9 +6837,10 @@ function MeasurerClient({
5104
6837
  ownerWindow.removeEventListener("pointerdown", handlePointerDown2);
5105
6838
  };
5106
6839
  }, [ownerWindow, toolbarActive, toolMode]);
5107
- useEffect5(() => {
6840
+ useEffect7(() => {
5108
6841
  if (!enabled) return;
5109
6842
  const handleGuidePointerDown2 = (event) => {
6843
+ if (settingsOpen) return;
5110
6844
  if (toolbarRef.current?.contains(event.target)) return;
5111
6845
  const guideTarget = event.composedPath().some(
5112
6846
  (target) => target instanceof ownerWindow.Element && target.hasAttribute("data-mesurer-guide")
@@ -5189,16 +6923,30 @@ function MeasurerClient({
5189
6923
  ownerWindow,
5190
6924
  setGuidesPersisted,
5191
6925
  setSelectedGuideIdsPersisted,
6926
+ settingsOpen,
5192
6927
  toolMode
5193
6928
  ]);
5194
- useEffect5(() => {
5195
- if (toolMode === "text-inspector") {
6929
+ useEffect7(() => {
6930
+ if (toolMode === "text-inspector" && !settingsOpen) {
5196
6931
  textInspector.enable();
5197
6932
  } else {
5198
6933
  textInspector.disable();
5199
6934
  }
5200
- }, [textInspector, toolMode]);
5201
- useEffect5(() => {
6935
+ }, [settingsOpen, textInspector, toolMode]);
6936
+ const selectionContextRef = useRef12({ toolMode, settingsOpen });
6937
+ if (selectionContextRef.current.toolMode !== toolMode || selectionContextRef.current.settingsOpen !== settingsOpen) {
6938
+ selectionContextRef.current = { toolMode, settingsOpen };
6939
+ if (!(toolMode === "select" && !settingsOpen)) {
6940
+ setSelectedElement(null);
6941
+ setHoverElement(null);
6942
+ setHoverRect(null);
6943
+ setHoverPointer(null);
6944
+ setSelectedMeasurement(null);
6945
+ setSelectedMeasurements([]);
6946
+ clearSelectionRect();
6947
+ }
6948
+ }
6949
+ useEffect7(() => {
5202
6950
  let style = ownerDocument.getElementById(XRAY_STYLE_ID);
5203
6951
  if (!style) {
5204
6952
  style = ownerDocument.createElement("style");
@@ -5206,7 +6954,7 @@ function MeasurerClient({
5206
6954
  style.textContent = XRAY_STYLES;
5207
6955
  ownerDocument.head.appendChild(style);
5208
6956
  }
5209
- if (toolMode === "xray") {
6957
+ if (xrayVisible) {
5210
6958
  ownerDocument.body.classList.add("xray-mode");
5211
6959
  } else {
5212
6960
  ownerDocument.body.classList.remove("xray-mode");
@@ -5214,13 +6962,13 @@ function MeasurerClient({
5214
6962
  return () => {
5215
6963
  ownerDocument.body.classList.remove("xray-mode");
5216
6964
  };
5217
- }, [ownerDocument, toolMode]);
5218
- useEffect5(() => {
6965
+ }, [ownerDocument, xrayVisible]);
6966
+ useEffect7(() => {
5219
6967
  return () => {
5220
6968
  textInspector.destroy();
5221
6969
  };
5222
6970
  }, [textInspector]);
5223
- useEffect5(() => {
6971
+ useEffect7(() => {
5224
6972
  const hasSelectionAnimationState = !!selectionOriginRect || !!selectedMeasurement?.originRect || selectedMeasurements.some((measurement) => !!measurement.originRect);
5225
6973
  if (!hasSelectionAnimationState) {
5226
6974
  if (selectionAnimationCleanupTimeoutRef.current !== null) {
@@ -5301,9 +7049,9 @@ function MeasurerClient({
5301
7049
  guidesEnabled,
5302
7050
  guidePreview,
5303
7051
  displayedMeasurements,
5304
- hoverHighlightEnabled,
5305
- highlightColor,
5306
- guideColor
7052
+ hoverHighlightEnabled: settingsHoverHighlight,
7053
+ highlightColor: settingsHighlightColor,
7054
+ guideColor: settingsGuideColor
5307
7055
  });
5308
7056
  const {
5309
7057
  handlePointerDown,
@@ -5320,10 +7068,12 @@ function MeasurerClient({
5320
7068
  clearGuideDragHold,
5321
7069
  scheduleGuideDragHold,
5322
7070
  enabled,
7071
+ settingsOpen,
5323
7072
  toolMode,
5324
7073
  guidesEnabled,
5325
7074
  snapEnabled,
5326
7075
  snapGuidesEnabled,
7076
+ selectNewGuideEnabled,
5327
7077
  altPressed,
5328
7078
  guideOrientation,
5329
7079
  hoverHighlightEnabled,
@@ -5356,7 +7106,7 @@ function MeasurerClient({
5356
7106
  setHoverPointer,
5357
7107
  clearSelectionRect
5358
7108
  });
5359
- const removeHeldDistance = useCallback6(
7109
+ const removeHeldDistance = useCallback8(
5360
7110
  (id) => {
5361
7111
  recordSnapshot();
5362
7112
  setHeldDistancesPersisted(
@@ -5365,42 +7115,62 @@ function MeasurerClient({
5365
7115
  },
5366
7116
  [recordSnapshot, setHeldDistancesPersisted]
5367
7117
  );
5368
- const startGuideFromRuler = useCallback6(
7118
+ const snapGuidePosition = useCallback8(
7119
+ (orientation, position, draggingGuideId2 = null) => getSnapGuidePosition({
7120
+ orientation,
7121
+ point: orientation === "vertical" ? { x: position, y: 0 } : { x: 0, y: position },
7122
+ snapGuidesEnabled,
7123
+ overlayNode: overlayRef.current,
7124
+ guides,
7125
+ draggingGuideId: draggingGuideId2,
7126
+ document: ownerDocument
7127
+ }),
7128
+ [guides, ownerDocument, overlayRef, snapGuidesEnabled]
7129
+ );
7130
+ const startGuideFromRuler = useCallback8(
5369
7131
  (orientation, position) => {
5370
7132
  const id = createId();
5371
7133
  const commit = createActionCommit();
5372
7134
  commit();
5373
7135
  setSelectedGuideIdsPersisted([]);
5374
- setGuidesPersisted((prev) => [...prev, { id, orientation, position }]);
7136
+ setGuidesPersisted((prev) => [
7137
+ ...prev,
7138
+ { id, orientation, position: snapGuidePosition(orientation, position) }
7139
+ ]);
5375
7140
  return id;
5376
7141
  },
5377
7142
  [
5378
7143
  createActionCommit,
5379
7144
  setGuidesPersisted,
5380
- setSelectedGuideIdsPersisted
7145
+ setSelectedGuideIdsPersisted,
7146
+ snapGuidePosition
5381
7147
  ]
5382
7148
  );
5383
- const moveGuideFromRuler = useCallback6(
7149
+ const moveGuideFromRuler = useCallback8(
5384
7150
  (id, position) => {
5385
7151
  setGuidesPersisted(
5386
- (prev) => prev.map((guide) => guide.id === id ? { ...guide, position } : guide)
7152
+ (prev) => prev.map(
7153
+ (guide) => guide.id === id ? { ...guide, position: snapGuidePosition(guide.orientation, position, id) } : guide
7154
+ )
5387
7155
  );
5388
7156
  },
5389
- [setGuidesPersisted]
7157
+ [setGuidesPersisted, snapGuidePosition]
5390
7158
  );
5391
- const finishGuideFromRuler = useCallback6(
7159
+ const finishGuideFromRuler = useCallback8(
5392
7160
  (id) => {
5393
- setSelectedGuideIdsPersisted([id]);
7161
+ if (selectNewGuideEnabled) {
7162
+ setSelectedGuideIdsPersisted([id]);
7163
+ }
5394
7164
  },
5395
- [setSelectedGuideIdsPersisted]
7165
+ [selectNewGuideEnabled, setSelectedGuideIdsPersisted]
5396
7166
  );
5397
- const cancelGuideFromRuler = useCallback6(
7167
+ const cancelGuideFromRuler = useCallback8(
5398
7168
  (id) => {
5399
7169
  setGuidesPersisted((prev) => prev.filter((guide) => guide.id !== id));
5400
7170
  },
5401
7171
  [setGuidesPersisted]
5402
7172
  );
5403
- const handleGuidePointerDown = useCallback6(
7173
+ const handleGuidePointerDown = useCallback8(
5404
7174
  (guide, event) => {
5405
7175
  const commit = createActionCommit();
5406
7176
  if (!enabled) return;
@@ -5426,7 +7196,7 @@ function MeasurerClient({
5426
7196
  setSelectedGuideIdsPersisted
5427
7197
  ]
5428
7198
  );
5429
- const handleGuidePointerUp = useCallback6(
7199
+ const handleGuidePointerUp = useCallback8(
5430
7200
  (guide, event) => {
5431
7201
  event.stopPropagation();
5432
7202
  clearGuideDragHold();
@@ -5437,16 +7207,19 @@ function MeasurerClient({
5437
7207
  },
5438
7208
  [clearGuideDragHold, setDraggingGuideId]
5439
7209
  );
7210
+ const overlayInteractive = enabled && !settingsOpen;
5440
7211
  return createPortal(
5441
- /* @__PURE__ */ jsxs7(
7212
+ /* @__PURE__ */ jsxs11(
5442
7213
  "div",
5443
7214
  {
5444
7215
  ref: overlayRef,
5445
7216
  className: "mesurer-root msr:pointer-events-none msr:fixed msr:inset-0 msr:z-50",
5446
7217
  children: [
5447
- enabled && rulersVisible ? /* @__PURE__ */ jsx9(
7218
+ enabled && rulersVisible && !settingsOpen ? /* @__PURE__ */ jsx12(
5448
7219
  RulersOverlay,
5449
7220
  {
7221
+ ownerWindow,
7222
+ settings: settingsRulerSettings,
5450
7223
  onStartGuide: startGuideFromRuler,
5451
7224
  onMoveGuide: moveGuideFromRuler,
5452
7225
  onFinishGuide: finishGuideFromRuler,
@@ -5455,12 +7228,13 @@ function MeasurerClient({
5455
7228
  selectedGuideIds
5456
7229
  }
5457
7230
  ) : null,
5458
- /* @__PURE__ */ jsx9(
7231
+ /* @__PURE__ */ jsx12(
5459
7232
  MeasurerOverlay,
5460
7233
  {
5461
- enabled,
7234
+ enabled: enabled && (!settingsOpen || guides.length > 0),
7235
+ interactive: overlayInteractive,
5462
7236
  toolMode,
5463
- guidePointerEvents: enabled && (toolMode !== "none" || rulersVisible),
7237
+ guidePointerEvents: overlayInteractive && (toolMode !== "none" || rulersVisible),
5464
7238
  guidesEnabled,
5465
7239
  altPressed,
5466
7240
  isDragging,
@@ -5488,6 +7262,7 @@ function MeasurerClient({
5488
7262
  guideColorActive,
5489
7263
  guideColorHover,
5490
7264
  guideColorDefault,
7265
+ guideStyle: settingsGuideStyle,
5491
7266
  onPointerDown: handlePointerDown,
5492
7267
  onPointerMove: handlePointerMove,
5493
7268
  onPointerUp: handlePointerUp,
@@ -5498,7 +7273,20 @@ function MeasurerClient({
5498
7273
  onGuidePointerCancel: handleGuidePointerUp
5499
7274
  }
5500
7275
  ),
5501
- /* @__PURE__ */ jsx9(
7276
+ /* @__PURE__ */ jsx12(
7277
+ ColorPicker,
7278
+ {
7279
+ active: colorPickerActive,
7280
+ sample: colorPickerSample,
7281
+ unsupported: colorPickerUnsupported,
7282
+ ownerWindow,
7283
+ toolbarRef,
7284
+ formats: settingsColorFormats,
7285
+ favoriteFormat: settingsColorClickFormat,
7286
+ onClose: () => setColorPickerActive(false)
7287
+ }
7288
+ ),
7289
+ /* @__PURE__ */ jsx12(
5502
7290
  Toolbar,
5503
7291
  {
5504
7292
  ref: toolbarRef,
@@ -5506,11 +7294,45 @@ function MeasurerClient({
5506
7294
  toolMode,
5507
7295
  setEnabled: setEnabledWithHistory,
5508
7296
  setToolMode: setToolModeWithHistory,
7297
+ xrayVisible,
7298
+ setXrayVisible,
5509
7299
  rulersVisible,
5510
7300
  setRulersVisible: setRulersVisiblePersisted,
5511
7301
  guideOrientation,
5512
7302
  setGuideOrientation: setGuideOrientationWithHistory,
5513
- onInteract: () => setToolbarActive(true)
7303
+ onInteract: () => setToolbarActive(true),
7304
+ colorPickerActive,
7305
+ setColorPickerActive,
7306
+ onColorPickerClick: openColorPicker,
7307
+ settingsOpen,
7308
+ setSettingsOpen,
7309
+ highlightColor: settingsHighlightColor,
7310
+ setHighlightColor: setSettingsHighlightColor,
7311
+ guideColor: settingsGuideColor,
7312
+ setGuideColor: setSettingsGuideColor,
7313
+ hoverHighlight: settingsHoverHighlight,
7314
+ setHoverHighlight: setSettingsHoverHighlight,
7315
+ persistOnReload: settingsPersistOnReload,
7316
+ setPersistOnReload: setSettingsPersistOnReload,
7317
+ colorPickerFormats: settingsColorFormats,
7318
+ setColorPickerFormats: setSettingsColorFormats,
7319
+ colorPickerClickFormat: settingsColorClickFormat,
7320
+ setColorPickerClickFormat: setSettingsColorClickFormat,
7321
+ snapEnabled,
7322
+ setSnapEnabled,
7323
+ snapGuidesEnabled,
7324
+ setSnapGuidesEnabled,
7325
+ selectNewGuideEnabled,
7326
+ setSelectNewGuideEnabled,
7327
+ multiMeasureEnabled,
7328
+ setMultiMeasureEnabled,
7329
+ guideStyle: settingsGuideStyle,
7330
+ setGuideStyle: setSettingsGuideStyle,
7331
+ rulerSettings: settingsRulerSettings,
7332
+ setRulerSettings: setSettingsRulerSettings,
7333
+ initialSettingsTab,
7334
+ onResetSettings: resetSettings,
7335
+ onClearWorkspace: clearWorkspace
5514
7336
  }
5515
7337
  )
5516
7338
  ]
@@ -5525,14 +7347,24 @@ function Measurer({
5525
7347
  hoverHighlightEnabled = true,
5526
7348
  persistOnReload = false,
5527
7349
  portalTarget,
5528
- persistKey
7350
+ persistKey,
7351
+ colorPickerFormats = ["hex", "rgb", "oklch"],
7352
+ colorPickerClickFormat = "hex",
7353
+ snapEnabled = true,
7354
+ snapGuidesEnabled = true,
7355
+ selectNewGuideEnabled = true,
7356
+ multiMeasureEnabled = false,
7357
+ guideStyle,
7358
+ rulerSettings,
7359
+ persistence,
7360
+ onPersistenceError
5529
7361
  }) {
5530
7362
  if (typeof document !== "undefined") {
5531
7363
  ensureMeasurerStyles(MESURER_STYLES, portalTarget);
5532
7364
  }
5533
7365
  const hydrated = useHydrated();
5534
7366
  if (!hydrated) return null;
5535
- return /* @__PURE__ */ jsx9(
7367
+ return /* @__PURE__ */ jsx12(
5536
7368
  MeasurerClient,
5537
7369
  {
5538
7370
  highlightColor,
@@ -5540,12 +7372,26 @@ function Measurer({
5540
7372
  hoverHighlightEnabled,
5541
7373
  persistOnReload,
5542
7374
  persistKey,
7375
+ colorPickerFormats,
7376
+ colorPickerClickFormat,
7377
+ snapEnabled,
7378
+ snapGuidesEnabled,
7379
+ selectNewGuideEnabled,
7380
+ multiMeasureEnabled,
7381
+ guideStyle: { ...DEFAULT_GUIDE_STYLE, ...guideStyle },
7382
+ rulerSettings: { ...DEFAULT_RULER_SETTINGS, ...rulerSettings },
7383
+ persistence,
7384
+ onPersistenceError,
5543
7385
  portalTarget: portalTarget ?? document.body
5544
7386
  }
5545
7387
  );
5546
7388
  }
5547
7389
  export {
7390
+ MESURER_STORAGE_VERSION,
5548
7391
  Measurer,
5549
7392
  TextInspector,
5550
- createTextInspector
7393
+ createLocalStoragePersistence,
7394
+ createTextInspector,
7395
+ normalizeStoredSettings,
7396
+ normalizeStoredWorkspace
5551
7397
  };