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.cjs CHANGED
@@ -20,14 +20,18 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // index.ts
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
+ MESURER_STORAGE_VERSION: () => MESURER_STORAGE_VERSION,
23
24
  Measurer: () => Measurer,
24
25
  TextInspector: () => TextInspector,
25
- createTextInspector: () => createTextInspector
26
+ createLocalStoragePersistence: () => createLocalStoragePersistence,
27
+ createTextInspector: () => createTextInspector,
28
+ normalizeStoredSettings: () => normalizeStoredSettings,
29
+ normalizeStoredWorkspace: () => normalizeStoredWorkspace
26
30
  });
27
31
  module.exports = __toCommonJS(index_exports);
28
32
 
29
33
  // measurer.tsx
30
- var import_react20 = require("react");
34
+ var import_react23 = require("react");
31
35
  var import_react_dom = require("react-dom");
32
36
 
33
37
  // core/constants.ts
@@ -74,19 +78,760 @@ function ensureMeasurerStyles(cssText, target) {
74
78
  }
75
79
 
76
80
  // styles.generated.ts
77
- var MESURER_STYLES = '/*! tailwindcss v4.2.2 | MIT License | https://tailwindcss.com */\n@layer properties;\n@layer theme, base, components, utilities;\n@layer theme {\n :root, :host {\n --msr-font-sans: ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji",\n "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";\n --msr-font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono",\n "Courier New", monospace;\n --msr-color-black: #000;\n --msr-color-white: #fff;\n --msr-spacing: 0.25rem;\n --msr-radius-md: 0.375rem;\n --msr-radius-lg: 0.5rem;\n --msr-default-transition-duration: 150ms;\n --msr-default-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n --msr-default-font-family: var(--msr-font-sans);\n --msr-default-mono-font-family: var(--msr-font-mono);\n --msr-color-ink-50: var(--color-ink-50);\n --msr-color-ink-200: var(--color-ink-200);\n --msr-color-ink-700: var(--color-ink-700);\n --msr-color-ink-900: var(--color-ink-900);\n }\n}\n@layer base {\n *, ::after, ::before, ::backdrop, ::file-selector-button {\n box-sizing: border-box;\n margin: 0;\n padding: 0;\n border: 0 solid;\n }\n html, :host {\n line-height: 1.5;\n -webkit-text-size-adjust: 100%;\n tab-size: 4;\n font-family: var(--msr-default-font-family, ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");\n font-feature-settings: var(--msr-default-font-feature-settings, normal);\n font-variation-settings: var(--msr-default-font-variation-settings, normal);\n -webkit-tap-highlight-color: transparent;\n }\n hr {\n height: 0;\n color: inherit;\n border-top-width: 1px;\n }\n abbr:where([title]) {\n -webkit-text-decoration: underline dotted;\n text-decoration: underline dotted;\n }\n h1, h2, h3, h4, h5, h6 {\n font-size: inherit;\n font-weight: inherit;\n }\n a {\n color: inherit;\n -webkit-text-decoration: inherit;\n text-decoration: inherit;\n }\n b, strong {\n font-weight: bolder;\n }\n code, kbd, samp, pre {\n font-family: var(--msr-default-mono-font-family, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);\n font-feature-settings: var(--msr-default-mono-font-feature-settings, normal);\n font-variation-settings: var(--msr-default-mono-font-variation-settings, normal);\n font-size: 1em;\n }\n small {\n font-size: 80%;\n }\n sub, sup {\n font-size: 75%;\n line-height: 0;\n position: relative;\n vertical-align: baseline;\n }\n sub {\n bottom: -0.25em;\n }\n sup {\n top: -0.5em;\n }\n table {\n text-indent: 0;\n border-color: inherit;\n border-collapse: collapse;\n }\n :-moz-focusring {\n outline: auto;\n }\n progress {\n vertical-align: baseline;\n }\n summary {\n display: list-item;\n }\n ol, ul, menu {\n list-style: none;\n }\n img, svg, video, canvas, audio, iframe, embed, object {\n display: block;\n vertical-align: middle;\n }\n img, video {\n max-width: 100%;\n height: auto;\n }\n button, input, select, optgroup, textarea, ::file-selector-button {\n font: inherit;\n font-feature-settings: inherit;\n font-variation-settings: inherit;\n letter-spacing: inherit;\n color: inherit;\n border-radius: 0;\n background-color: transparent;\n opacity: 1;\n }\n :where(select:is([multiple], [size])) optgroup {\n font-weight: bolder;\n }\n :where(select:is([multiple], [size])) optgroup option {\n padding-inline-start: 20px;\n }\n ::file-selector-button {\n margin-inline-end: 4px;\n }\n ::placeholder {\n opacity: 1;\n }\n @supports (not (-webkit-appearance: -apple-pay-button)) or (contain-intrinsic-size: 1px) {\n ::placeholder {\n color: currentcolor;\n @supports (color: color-mix(in lab, red, red)) {\n color: color-mix(in oklab, currentcolor 50%, transparent);\n }\n }\n }\n textarea {\n resize: vertical;\n }\n ::-webkit-search-decoration {\n -webkit-appearance: none;\n }\n ::-webkit-date-and-time-value {\n min-height: 1lh;\n text-align: inherit;\n }\n ::-webkit-datetime-edit {\n display: inline-flex;\n }\n ::-webkit-datetime-edit-fields-wrapper {\n padding: 0;\n }\n ::-webkit-datetime-edit, ::-webkit-datetime-edit-year-field, ::-webkit-datetime-edit-month-field, ::-webkit-datetime-edit-day-field, ::-webkit-datetime-edit-hour-field, ::-webkit-datetime-edit-minute-field, ::-webkit-datetime-edit-second-field, ::-webkit-datetime-edit-millisecond-field, ::-webkit-datetime-edit-meridiem-field {\n padding-block: 0;\n }\n ::-webkit-calendar-picker-indicator {\n line-height: 1;\n }\n :-moz-ui-invalid {\n box-shadow: none;\n }\n button, input:where([type="button"], [type="reset"], [type="submit"]), ::file-selector-button {\n appearance: button;\n }\n ::-webkit-inner-spin-button, ::-webkit-outer-spin-button {\n height: auto;\n }\n [hidden]:where(:not([hidden="until-found"])) {\n display: none !important;\n }\n}\n@layer utilities {\n .msr\\:pointer-events-auto {\n pointer-events: auto;\n }\n .msr\\:pointer-events-none {\n pointer-events: none;\n }\n .msr\\:absolute {\n position: absolute;\n }\n .msr\\:fixed {\n position: fixed;\n }\n .msr\\:relative {\n position: relative;\n }\n .msr\\:inset-0 {\n inset: calc(var(--msr-spacing) * 0);\n }\n .msr\\:top-0 {\n top: calc(var(--msr-spacing) * 0);\n }\n .msr\\:top-\\[18px\\] {\n top: 18px;\n }\n .msr\\:top-full {\n top: 100%;\n }\n .msr\\:right-0 {\n right: calc(var(--msr-spacing) * 0);\n }\n .msr\\:bottom-0 {\n bottom: calc(var(--msr-spacing) * 0);\n }\n .msr\\:bottom-full {\n bottom: 100%;\n }\n .msr\\:left-0 {\n left: calc(var(--msr-spacing) * 0);\n }\n .msr\\:left-1\\/2 {\n left: calc(1 / 2 * 100%);\n }\n .msr\\:left-\\[18px\\] {\n left: 18px;\n }\n .msr\\:z-50 {\n z-index: 50;\n }\n .msr\\:z-\\[70\\] {\n z-index: 70;\n }\n .msr\\:z-\\[90\\] {\n z-index: 90;\n }\n .msr\\:mt-2 {\n margin-top: calc(var(--msr-spacing) * 2);\n }\n .msr\\:mb-2 {\n margin-bottom: calc(var(--msr-spacing) * 2);\n }\n .msr\\:-ml-1 {\n margin-left: calc(var(--msr-spacing) * -1);\n }\n .msr\\:block {\n display: block;\n }\n .msr\\:flex {\n display: flex;\n }\n .msr\\:size-8 {\n width: calc(var(--msr-spacing) * 8);\n height: calc(var(--msr-spacing) * 8);\n }\n .msr\\:size-\\[18px\\] {\n width: 18px;\n height: 18px;\n }\n .msr\\:h-6 {\n height: calc(var(--msr-spacing) * 6);\n }\n .msr\\:h-8 {\n height: calc(var(--msr-spacing) * 8);\n }\n .msr\\:h-full {\n height: 100%;\n }\n .msr\\:h-px {\n height: 1px;\n }\n .msr\\:w-4 {\n width: calc(var(--msr-spacing) * 4);\n }\n .msr\\:w-6 {\n width: calc(var(--msr-spacing) * 6);\n }\n .msr\\:w-44 {\n width: calc(var(--msr-spacing) * 44);\n }\n .msr\\:w-\\[18px\\] {\n width: 18px;\n }\n .msr\\:w-full {\n width: 100%;\n }\n .msr\\:w-px {\n width: 1px;\n }\n .msr\\:flex-1 {\n flex: 1;\n }\n .msr\\:-translate-x-1\\/2 {\n --tw-translate-x: calc(calc(1 / 2 * 100%) * -1);\n translate: var(--tw-translate-x) var(--tw-translate-y);\n }\n .msr\\:-translate-y-1\\/2 {\n --tw-translate-y: calc(calc(1 / 2 * 100%) * -1);\n translate: var(--tw-translate-x) var(--tw-translate-y);\n }\n .msr\\:rotate-90 {\n rotate: 90deg;\n }\n .msr\\:rotate-\\[45deg\\] {\n rotate: 45deg;\n }\n .msr\\:rotate-\\[135deg\\] {\n rotate: 135deg;\n }\n .msr\\:cursor-crosshair {\n cursor: crosshair;\n }\n .msr\\:cursor-default {\n cursor: default;\n }\n .msr\\:flex-col {\n flex-direction: column;\n }\n .msr\\:items-center {\n align-items: center;\n }\n .msr\\:items-stretch {\n align-items: stretch;\n }\n .msr\\:justify-center {\n justify-content: center;\n }\n .msr\\:gap-1 {\n gap: calc(var(--msr-spacing) * 1);\n }\n .msr\\:gap-2 {\n gap: calc(var(--msr-spacing) * 2);\n }\n .msr\\:gap-px {\n gap: 1px;\n }\n .msr\\:overflow-hidden {\n overflow: hidden;\n }\n .msr\\:rounded {\n border-radius: 0.25rem;\n }\n .msr\\:rounded-\\[6px\\] {\n border-radius: 6px;\n }\n .msr\\:rounded-\\[8px\\] {\n border-radius: 8px;\n }\n .msr\\:rounded-\\[12px\\] {\n border-radius: 12px;\n }\n .msr\\:rounded-lg {\n border-radius: var(--msr-radius-lg);\n }\n .msr\\:rounded-md {\n border-radius: var(--msr-radius-md);\n }\n .msr\\:border {\n border-style: var(--tw-border-style);\n border-width: 1px;\n }\n .msr\\:border-t {\n border-top-style: var(--tw-border-style);\n border-top-width: 1px;\n }\n .msr\\:border-l {\n border-left-style: var(--tw-border-style);\n border-left-width: 1px;\n }\n .msr\\:border-dashed {\n --tw-border-style: dashed;\n border-style: dashed;\n }\n .msr\\:border-\\[\\#2563eb\\]\\/70 {\n border-color: color-mix(in oklab, #2563eb 70%, transparent);\n }\n .msr\\:border-ink-200 {\n border-color: var(--msr-color-ink-200);\n }\n .msr\\:bg-\\[\\#0d99ff\\] {\n background-color: #0d99ff;\n }\n .msr\\:bg-\\[\\#2563eb\\] {\n background-color: #2563eb;\n }\n .msr\\:bg-\\[\\#fff\\] {\n background-color: #fff;\n }\n .msr\\:bg-black {\n background-color: var(--msr-color-black);\n }\n .msr\\:bg-black\\/10 {\n background-color: var(--msr-color-black);\n @supports (color: color-mix(in lab, red, red)) {\n background-color: color-mix(in oklab, var(--msr-color-black) 10%, transparent);\n }\n }\n .msr\\:bg-ink-900\\/90 {\n background-color: var(--msr-color-ink-900);\n @supports (color: color-mix(in lab, red, red)) {\n background-color: color-mix(in oklab, var(--msr-color-ink-900) 90%, transparent);\n }\n }\n .msr\\:bg-transparent {\n background-color: transparent;\n }\n .msr\\:bg-white {\n background-color: var(--msr-color-white);\n }\n .msr\\:p-1 {\n padding: calc(var(--msr-spacing) * 1);\n }\n .msr\\:px-1 {\n padding-inline: calc(var(--msr-spacing) * 1);\n }\n .msr\\:px-2 {\n padding-inline: calc(var(--msr-spacing) * 2);\n }\n .msr\\:py-0\\.5 {\n padding-block: calc(var(--msr-spacing) * 0.5);\n }\n .msr\\:py-1 {\n padding-block: calc(var(--msr-spacing) * 1);\n }\n .msr\\:py-1\\.5 {\n padding-block: calc(var(--msr-spacing) * 1.5);\n }\n .msr\\:text-left {\n text-align: left;\n }\n .msr\\:text-\\[9px\\] {\n font-size: 9px;\n }\n .msr\\:text-\\[10px\\] {\n font-size: 10px;\n }\n .msr\\:text-\\[11px\\] {\n font-size: 11px;\n }\n .msr\\:text-\\[12px\\] {\n font-size: 12px;\n }\n .msr\\:whitespace-nowrap {\n white-space: nowrap;\n }\n .msr\\:text-\\[\\#64748b\\] {\n color: #64748b;\n }\n .msr\\:text-black {\n color: var(--msr-color-black);\n }\n .msr\\:text-ink-50 {\n color: var(--msr-color-ink-50);\n }\n .msr\\:text-ink-700 {\n color: var(--msr-color-ink-700);\n }\n .msr\\:text-white {\n color: var(--msr-color-white);\n }\n .msr\\:text-white\\/60 {\n color: var(--msr-color-white);\n @supports (color: color-mix(in lab, red, red)) {\n color: color-mix(in oklab, var(--msr-color-white) 60%, transparent);\n }\n }\n .msr\\:tabular-nums {\n --tw-numeric-spacing: tabular-nums;\n font-variant-numeric: var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,);\n }\n .msr\\:opacity-0 {\n opacity: 0%;\n }\n .msr\\:opacity-100 {\n opacity: 100%;\n }\n .msr\\:outline {\n outline-style: var(--tw-outline-style);\n outline-width: 1px;\n }\n .msr\\:outline-transparent {\n outline-color: transparent;\n }\n .msr\\:transition-opacity {\n transition-property: opacity;\n transition-timing-function: var(--tw-ease, var(--msr-default-transition-timing-function));\n transition-duration: var(--tw-duration, var(--msr-default-transition-duration));\n }\n .msr\\:transition-none {\n transition-property: none;\n }\n .msr\\:duration-150 {\n --tw-duration: 150ms;\n transition-duration: 150ms;\n }\n .msr\\:outline-none {\n --tw-outline-style: none;\n outline-style: none;\n }\n .msr\\:select-none {\n -webkit-user-select: none;\n user-select: none;\n }\n .msr\\:hover\\:bg-\\[\\#0d99ff\\] {\n &:hover {\n @media (hover: hover) {\n background-color: #0d99ff;\n }\n }\n }\n .msr\\:hover\\:bg-black\\/4 {\n &:hover {\n @media (hover: hover) {\n background-color: var(--msr-color-black);\n @supports (color: color-mix(in lab, red, red)) {\n background-color: color-mix(in oklab, var(--msr-color-black) 4%, transparent);\n }\n }\n }\n }\n .msr\\:hover\\:bg-black\\/10 {\n &:hover {\n @media (hover: hover) {\n background-color: var(--msr-color-black);\n @supports (color: color-mix(in lab, red, red)) {\n background-color: color-mix(in oklab, var(--msr-color-black) 10%, transparent);\n }\n }\n }\n }\n .msr\\:hover\\:text-white {\n &:hover {\n @media (hover: hover) {\n color: var(--msr-color-white);\n }\n }\n }\n .msr\\:focus\\:outline-none {\n &:focus {\n --tw-outline-style: none;\n outline-style: none;\n }\n }\n}\n:root, :host {\n --color-ink-50: #f8fafc;\n --color-ink-200: #e2e8f0;\n --color-ink-500: #64748b;\n --color-ink-700: #334155;\n --color-ink-900: #0f172a;\n}\n.mesurer-toolbar-surface {\n box-shadow: 0 0 0.5px rgba(0, 0, 0, 0.18), 0 3px 8px rgba(0, 0, 0, 0.1), 0 1px 3px rgba(0, 0, 0, 0.1);\n}\n.mesurer-menu-surface {\n box-shadow: 0 10px 30px rgba(0, 0, 0, 0.08);\n}\n.mesurer-root {\n --msr-spacing: 4px;\n}\n@property --tw-translate-x {\n syntax: "*";\n inherits: false;\n initial-value: 0;\n}\n@property --tw-translate-y {\n syntax: "*";\n inherits: false;\n initial-value: 0;\n}\n@property --tw-translate-z {\n syntax: "*";\n inherits: false;\n initial-value: 0;\n}\n@property --tw-border-style {\n syntax: "*";\n inherits: false;\n initial-value: solid;\n}\n@property --tw-ordinal {\n syntax: "*";\n inherits: false;\n}\n@property --tw-slashed-zero {\n syntax: "*";\n inherits: false;\n}\n@property --tw-numeric-figure {\n syntax: "*";\n inherits: false;\n}\n@property --tw-numeric-spacing {\n syntax: "*";\n inherits: false;\n}\n@property --tw-numeric-fraction {\n syntax: "*";\n inherits: false;\n}\n@property --tw-outline-style {\n syntax: "*";\n inherits: false;\n initial-value: solid;\n}\n@property --tw-duration {\n syntax: "*";\n inherits: false;\n}\n@layer properties {\n @supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))) {\n *, ::before, ::after, ::backdrop {\n --tw-translate-x: 0;\n --tw-translate-y: 0;\n --tw-translate-z: 0;\n --tw-border-style: solid;\n --tw-ordinal: initial;\n --tw-slashed-zero: initial;\n --tw-numeric-figure: initial;\n --tw-numeric-spacing: initial;\n --tw-numeric-fraction: initial;\n --tw-outline-style: solid;\n --tw-duration: initial;\n }\n }\n}\n';
81
+ 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';
78
82
 
79
83
  // components/toolbar.tsx
80
- var import_react = require("react");
84
+ var import_react3 = require("react");
81
85
 
82
86
  // core/utils.ts
83
87
  var formatValue = (value) => Math.round(value);
84
88
  var cn = (...inputs) => inputs.filter(Boolean).join(" ");
85
89
  var createId = () => typeof crypto !== "undefined" && "randomUUID" in crypto ? crypto.randomUUID() : `${Date.now()}-${Math.random()}`;
86
90
 
87
- // components/icons.tsx
91
+ // components/settings-panel.tsx
92
+ var import_react2 = require("react");
93
+
94
+ // package.json
95
+ var package_default = {
96
+ name: "mesurer",
97
+ version: "0.0.10",
98
+ description: "Measure and align elements on any website.",
99
+ license: "MIT",
100
+ author: "Julien Thibeaut",
101
+ type: "module",
102
+ main: "./dist/index.cjs",
103
+ module: "./dist/index.js",
104
+ types: "./dist/index.d.ts",
105
+ exports: {
106
+ ".": {
107
+ types: "./dist/index.d.ts",
108
+ import: "./dist/index.js",
109
+ require: "./dist/index.cjs"
110
+ },
111
+ "./styles.css": "./dist/styles.css",
112
+ "./package.json": "./package.json"
113
+ },
114
+ files: [
115
+ "dist",
116
+ "dist/styles.css",
117
+ "CHANGELOG.md",
118
+ "README.md",
119
+ "LICENSE"
120
+ ],
121
+ sideEffects: [
122
+ "*.css"
123
+ ],
124
+ publishConfig: {
125
+ access: "public"
126
+ },
127
+ homepage: "https://mesurer.dev",
128
+ repository: {
129
+ type: "git",
130
+ url: "https://github.com/ibelick/mesurer.git"
131
+ },
132
+ bugs: {
133
+ url: "https://github.com/ibelick/mesurer/issues"
134
+ },
135
+ scripts: {
136
+ 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",
137
+ dev: "node scripts/dev.mjs",
138
+ typecheck: "tsc --noEmit",
139
+ prepublishOnly: "npm run typecheck && npm run build"
140
+ },
141
+ peerDependencies: {
142
+ react: ">=18.0.0",
143
+ "react-dom": ">=18.0.0"
144
+ },
145
+ devDependencies: {
146
+ "@tailwindcss/cli": "^4.2.2",
147
+ "@types/react": "^19.0.0",
148
+ "@types/react-dom": "^19.0.0",
149
+ react: "^19.0.0",
150
+ "react-dom": "^19.0.0",
151
+ tailwindcss: "^4.2.2",
152
+ tsup: "^8.5.0",
153
+ typescript: "^5.9.2"
154
+ },
155
+ keywords: [
156
+ "measurement",
157
+ "alignment"
158
+ ]
159
+ };
160
+
161
+ // core/colors.ts
162
+ var clamp = (value, min = 0, max = 1) => Math.min(max, Math.max(min, value));
163
+ var round = (value, digits = 2) => {
164
+ const factor = 10 ** digits;
165
+ return Math.round(value * factor) / factor;
166
+ };
167
+ var parseChannel = (value) => {
168
+ const trimmed = value.trim();
169
+ const numeric = trimmed.endsWith("%") ? trimmed.slice(0, -1).trim() : trimmed;
170
+ if (!/^[+-]?(?:\d+\.?\d*|\.\d+)(?:e[+-]?\d+)?$/i.test(numeric)) return null;
171
+ const parsed = Number.parseFloat(numeric);
172
+ if (!Number.isFinite(parsed)) return null;
173
+ if (trimmed.endsWith("%")) return clamp(parsed / 100) * 255;
174
+ return clamp(parsed / 255) * 255;
175
+ };
176
+ var parseAlpha = (value) => {
177
+ if (!value) return 1;
178
+ const trimmed = value.trim();
179
+ const numeric = trimmed.endsWith("%") ? trimmed.slice(0, -1).trim() : trimmed;
180
+ if (!/^[+-]?(?:\d+\.?\d*|\.\d+)(?:e[+-]?\d+)?$/i.test(numeric)) return null;
181
+ const parsed = Number.parseFloat(numeric);
182
+ if (!Number.isFinite(parsed)) return null;
183
+ return clamp(trimmed.endsWith("%") ? parsed / 100 : parsed);
184
+ };
185
+ var parseCssColor = (value) => {
186
+ const input = value.trim().toLowerCase();
187
+ if (!input || input === "transparent") return null;
188
+ if (input.startsWith("#")) {
189
+ const hex = input.slice(1);
190
+ const expanded = hex.length === 3 || hex.length === 4 ? hex.split("").map((digit) => digit + digit).join("") : hex;
191
+ if (![6, 8].includes(expanded.length) || !/^[\da-f]+$/.test(expanded)) return null;
192
+ return {
193
+ red: Number.parseInt(expanded.slice(0, 2), 16),
194
+ green: Number.parseInt(expanded.slice(2, 4), 16),
195
+ blue: Number.parseInt(expanded.slice(4, 6), 16),
196
+ alpha: expanded.length === 8 ? Number.parseInt(expanded.slice(6, 8), 16) / 255 : 1
197
+ };
198
+ }
199
+ const match = input.match(/^rgba?\((.*)\)$/);
200
+ if (!match) return null;
201
+ const channels = match[1].replace(/\//g, " ").split(/[ ,]+/).filter(Boolean);
202
+ if (channels.length < 3 || channels.length > 4) return null;
203
+ const red = parseChannel(channels[0]);
204
+ const green = parseChannel(channels[1]);
205
+ const blue = parseChannel(channels[2]);
206
+ const alpha = parseAlpha(channels[3]);
207
+ if (red === null || green === null || blue === null || alpha === null) return null;
208
+ return { red, green, blue, alpha };
209
+ };
210
+ var toByte = (value) => Math.round(clamp(value / 255) * 255);
211
+ var colorToHex = (color) => {
212
+ const hex = [color.red, color.green, color.blue].map((channel) => toByte(channel).toString(16).padStart(2, "0")).join("");
213
+ if (color.alpha >= 0.999) return `#${hex}`;
214
+ return `#${hex}${Math.round(color.alpha * 255).toString(16).padStart(2, "0")}`;
215
+ };
216
+ var colorToRgb = (color) => {
217
+ const channels = `${toByte(color.red)}, ${toByte(color.green)}, ${toByte(color.blue)}`;
218
+ return color.alpha >= 0.999 ? `rgb(${channels})` : `rgba(${channels}, ${round(color.alpha, 3)})`;
219
+ };
220
+ var colorToHsl = (color) => {
221
+ const red = color.red / 255;
222
+ const green = color.green / 255;
223
+ const blue = color.blue / 255;
224
+ const max = Math.max(red, green, blue);
225
+ const min = Math.min(red, green, blue);
226
+ const lightness = (max + min) / 2;
227
+ const delta = max - min;
228
+ let hue = 0;
229
+ let saturation = 0;
230
+ if (delta !== 0) {
231
+ saturation = delta / (1 - Math.abs(2 * lightness - 1));
232
+ if (max === red) hue = (green - blue) / delta % 6;
233
+ else if (max === green) hue = (blue - red) / delta + 2;
234
+ else hue = (red - green) / delta + 4;
235
+ hue *= 60;
236
+ if (hue < 0) hue += 360;
237
+ }
238
+ const result = `hsl(${round(hue, 1)} ${round(saturation * 100, 1)}% ${round(lightness * 100, 1)}%`;
239
+ return color.alpha >= 0.999 ? `${result})` : `${result} / ${round(color.alpha, 3)})`;
240
+ };
241
+ var linearize = (value) => {
242
+ const normalized = value / 255;
243
+ return normalized <= 0.04045 ? normalized / 12.92 : ((normalized + 0.055) / 1.055) ** 2.4;
244
+ };
245
+ var colorToOklch = (color) => {
246
+ const red = linearize(color.red);
247
+ const green = linearize(color.green);
248
+ const blue = linearize(color.blue);
249
+ const l = 0.4122214708 * red + 0.5363325363 * green + 0.0514459929 * blue;
250
+ const m = 0.2119034982 * red + 0.6806995451 * green + 0.1073969566 * blue;
251
+ const s = 0.0883024619 * red + 0.2817188376 * green + 0.6299787005 * blue;
252
+ const lRoot = Math.cbrt(l);
253
+ const mRoot = Math.cbrt(m);
254
+ const sRoot = Math.cbrt(s);
255
+ const lightness = 0.2104542553 * lRoot + 0.793617785 * mRoot - 0.0040720468 * sRoot;
256
+ const a = 1.9779984951 * lRoot - 2.428592205 * mRoot + 0.4505937099 * sRoot;
257
+ const b = 0.0259040371 * lRoot + 0.7827717662 * mRoot - 0.808675766 * sRoot;
258
+ const chroma = Math.sqrt(a * a + b * b);
259
+ let hue = Math.atan2(b, a) * 180 / Math.PI;
260
+ if (hue < 0) hue += 360;
261
+ const result = `oklch(${round(lightness, 3)} ${round(chroma, 3)} ${round(hue, 1)}`;
262
+ return color.alpha >= 0.999 ? `${result})` : `${result} / ${round(color.alpha, 3)})`;
263
+ };
264
+ var formatColor = (color, format) => {
265
+ if (format === "hex") return colorToHex(color);
266
+ if (format === "rgb") return colorToRgb(color);
267
+ if (format === "hsl") return colorToHsl(color);
268
+ return colorToOklch(color);
269
+ };
270
+
271
+ // components/tooltip.tsx
272
+ var import_react = require("react");
88
273
  var import_jsx_runtime = require("react/jsx-runtime");
89
- var IconBase = ({ size = 24, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
274
+ var TOOLTIP_DELAY_MS = 800;
275
+ function Tooltip({
276
+ label,
277
+ shortcut,
278
+ visible,
279
+ instant = false,
280
+ side = "top",
281
+ className
282
+ }) {
283
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
284
+ "span",
285
+ {
286
+ role: "tooltip",
287
+ className: cn(
288
+ "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",
289
+ instant && "msr:transition-none",
290
+ side === "top" ? "msr:bottom-full msr:mb-2" : "msr:top-full msr:mt-2",
291
+ visible === void 0 ? null : visible ? "msr:opacity-100" : "msr:opacity-0",
292
+ className
293
+ ),
294
+ children: [
295
+ label,
296
+ shortcut ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
297
+ " ",
298
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("kbd", { className: "msr:text-white/60", children: shortcut })
299
+ ] }) : null
300
+ ]
301
+ }
302
+ );
303
+ }
304
+ function useTooltip() {
305
+ const [visibleTooltipId, setVisibleTooltipId] = (0, import_react.useState)(null);
306
+ const timerRef = (0, import_react.useRef)(null);
307
+ const instantRef = (0, import_react.useRef)(false);
308
+ const [tooltipInstant, setTooltipInstant] = (0, import_react.useState)(false);
309
+ const clearTimer = (0, import_react.useCallback)(() => {
310
+ if (timerRef.current === null) return;
311
+ window.clearTimeout(timerRef.current);
312
+ timerRef.current = null;
313
+ }, []);
314
+ const onTooltipEnter = (0, import_react.useCallback)((id) => {
315
+ clearTimer();
316
+ if (instantRef.current) {
317
+ setTooltipInstant(true);
318
+ setVisibleTooltipId(id);
319
+ return;
320
+ }
321
+ setTooltipInstant(false);
322
+ timerRef.current = window.setTimeout(() => {
323
+ setVisibleTooltipId(id);
324
+ instantRef.current = true;
325
+ timerRef.current = null;
326
+ }, TOOLTIP_DELAY_MS);
327
+ }, [clearTimer]);
328
+ const onTooltipLeave = (0, import_react.useCallback)(() => {
329
+ clearTimer();
330
+ setVisibleTooltipId(null);
331
+ }, [clearTimer]);
332
+ const onTooltipContainerLeave = (0, import_react.useCallback)(() => {
333
+ clearTimer();
334
+ setVisibleTooltipId(null);
335
+ instantRef.current = false;
336
+ setTooltipInstant(false);
337
+ }, [clearTimer]);
338
+ return {
339
+ visibleTooltipId,
340
+ tooltipInstant,
341
+ onTooltipEnter,
342
+ onTooltipLeave,
343
+ onTooltipContainerLeave
344
+ };
345
+ }
346
+
347
+ // components/settings-panel.tsx
348
+ var import_jsx_runtime2 = require("react/jsx-runtime");
349
+ var COLOR_FORMATS = ["hex", "rgb", "hsl", "oklch"];
350
+ var GUIDE_PATTERNS = [
351
+ { value: "solid", label: "Solid" },
352
+ { value: "dashed", label: "Dashed" },
353
+ { value: "dotted", label: "Dotted" }
354
+ ];
355
+ function ControlShell({ left, right }) {
356
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
357
+ "div",
358
+ {
359
+ 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",
360
+ children: [
361
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("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 }),
362
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("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 })
363
+ ]
364
+ }
365
+ );
366
+ }
367
+ function SettingsSwitch({ label, checked, onChange }) {
368
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
369
+ "button",
370
+ {
371
+ type: "button",
372
+ role: "switch",
373
+ "aria-checked": checked,
374
+ 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",
375
+ onClick: () => onChange(!checked),
376
+ children: [
377
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: label }),
378
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
379
+ "span",
380
+ {
381
+ "aria-hidden": "true",
382
+ style: { justifySelf: "end" },
383
+ className: cn(
384
+ "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",
385
+ checked ? "msr:border-[#0d99ff] msr:bg-[#0d99ff]" : "msr:border-ink-200 msr:bg-ink-50"
386
+ ),
387
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
388
+ "span",
389
+ {
390
+ className: "msr:block msr:size-[10px] msr:shrink-0 msr:rounded-full msr:bg-white msr:shadow-sm msr:transition-transform",
391
+ style: { transform: `translateX(${checked ? 12 : 0}px)` }
392
+ }
393
+ )
394
+ }
395
+ )
396
+ ]
397
+ }
398
+ );
399
+ }
400
+ function SliderControl({
401
+ label,
402
+ min,
403
+ max,
404
+ step,
405
+ value,
406
+ onChange,
407
+ formatValue: formatValue4 = (currentValue) => String(currentValue),
408
+ parseInput = (input) => Number(input)
409
+ }) {
410
+ const thumbSize = 12;
411
+ const thumbInset = 8;
412
+ const percentage = (value - min) / (max - min) * 100;
413
+ const [draftValue, setDraftValue] = (0, import_react2.useState)(formatValue4(value));
414
+ const [editing, setEditing] = (0, import_react2.useState)(false);
415
+ const commitDraft = () => {
416
+ const parsed = parseInput(draftValue);
417
+ if (Number.isFinite(parsed)) {
418
+ onChange(Math.min(max, Math.max(min, parsed)));
419
+ }
420
+ const next = Number.isFinite(parsed) ? Math.min(max, Math.max(min, parsed)) : value;
421
+ setDraftValue(formatValue4(next));
422
+ setEditing(false);
423
+ };
424
+ const updateFromPointer = (event) => {
425
+ event.stopPropagation();
426
+ const rect = event.currentTarget.getBoundingClientRect();
427
+ const usableWidth = Math.max(1, rect.width - thumbInset * 2);
428
+ const ratio = Math.min(1, Math.max(0, (event.clientX - rect.left - thumbInset) / usableWidth));
429
+ const rawValue = min + ratio * (max - min);
430
+ const steppedValue = Math.round((rawValue - min) / step) * step + min;
431
+ onChange(Number(steppedValue.toFixed(4)));
432
+ };
433
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "msr:col-span-2 msr:grid msr:w-full msr:grid-cols-[78px_156px] msr:items-center msr:gap-3", children: [
434
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "msr:text-[11px] msr:font-medium msr:text-ink-700", children: label }),
435
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
436
+ ControlShell,
437
+ {
438
+ left: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
439
+ "div",
440
+ {
441
+ className: "msr:relative msr:min-w-0 msr:flex-1 msr:touch-none msr:select-none msr:px-2",
442
+ style: { height: 20 },
443
+ "data-slider-container": "true",
444
+ onPointerDown: (event) => {
445
+ event.stopPropagation();
446
+ event.currentTarget.setPointerCapture(event.pointerId);
447
+ updateFromPointer(event);
448
+ },
449
+ onPointerMove: (event) => {
450
+ if (event.currentTarget.hasPointerCapture(event.pointerId)) updateFromPointer(event);
451
+ },
452
+ onPointerUp: (event) => {
453
+ event.stopPropagation();
454
+ if (event.currentTarget.hasPointerCapture(event.pointerId)) event.currentTarget.releasePointerCapture(event.pointerId);
455
+ },
456
+ onPointerCancel: (event) => {
457
+ event.stopPropagation();
458
+ if (event.currentTarget.hasPointerCapture(event.pointerId)) event.currentTarget.releasePointerCapture(event.pointerId);
459
+ },
460
+ onClick: (event) => event.stopPropagation(),
461
+ children: [
462
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
463
+ "div",
464
+ {
465
+ className: "msr:absolute msr:left-[8px] msr:right-[8px] msr:rounded-full",
466
+ style: { top: 8, height: 4, backgroundColor: "rgba(15, 23, 42, 0.16)" },
467
+ "aria-hidden": "true"
468
+ }
469
+ ),
470
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
471
+ "div",
472
+ {
473
+ className: "msr:absolute msr:left-[8px] msr:rounded-full",
474
+ style: { top: 8, width: `calc(${percentage}% - ${percentage * thumbInset * 2 / 100}px)`, height: 4, backgroundColor: "#0d99ff" },
475
+ "aria-hidden": "true"
476
+ }
477
+ ),
478
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
479
+ "div",
480
+ {
481
+ 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",
482
+ style: {
483
+ left: `calc(8px + (100% - 16px) * ${percentage / 100})`,
484
+ top: 4,
485
+ width: thumbSize,
486
+ height: thumbSize,
487
+ border: "0",
488
+ transform: "translateX(-50%)"
489
+ },
490
+ role: "slider",
491
+ tabIndex: 0,
492
+ "aria-label": label,
493
+ "aria-valuemin": min,
494
+ "aria-valuemax": max,
495
+ "aria-valuenow": value,
496
+ "aria-orientation": "horizontal",
497
+ onKeyDown: (event) => {
498
+ event.stopPropagation();
499
+ const direction = event.key === "ArrowRight" || event.key === "ArrowUp" ? 1 : event.key === "ArrowLeft" || event.key === "ArrowDown" ? -1 : 0;
500
+ if (event.key === "Home") onChange(min);
501
+ else if (event.key === "End") onChange(max);
502
+ else if (direction) {
503
+ event.preventDefault();
504
+ onChange(Number(Math.min(max, Math.max(min, value + direction * step)).toFixed(4)));
505
+ } else return;
506
+ event.preventDefault();
507
+ }
508
+ }
509
+ )
510
+ ]
511
+ }
512
+ ),
513
+ right: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
514
+ "input",
515
+ {
516
+ type: "text",
517
+ "aria-label": `${label} value`,
518
+ 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",
519
+ style: { boxSizing: "border-box", borderRadius: "0 5px 5px 0", lineHeight: "1rem" },
520
+ value: editing ? draftValue : formatValue4(value),
521
+ onFocus: () => {
522
+ setDraftValue(formatValue4(value));
523
+ setEditing(true);
524
+ },
525
+ onChange: (event) => {
526
+ const nextDraft = event.target.value;
527
+ setDraftValue(nextDraft);
528
+ const next = parseInput(nextDraft);
529
+ if (Number.isFinite(next)) onChange(Math.min(max, Math.max(min, next)));
530
+ },
531
+ onPointerDown: (event) => event.stopPropagation(),
532
+ onClick: (event) => event.stopPropagation(),
533
+ onBlur: () => {
534
+ commitDraft();
535
+ },
536
+ onKeyDown: (event) => {
537
+ event.stopPropagation();
538
+ if (event.key === "ArrowUp" || event.key === "ArrowDown") {
539
+ event.preventDefault();
540
+ const current = parseInput(event.currentTarget.value);
541
+ const direction = event.key === "ArrowUp" ? 1 : -1;
542
+ const next = Number(
543
+ Math.min(max, Math.max(min, (Number.isFinite(current) ? current : value) + direction * step)).toFixed(4)
544
+ );
545
+ setDraftValue(formatValue4(next));
546
+ onChange(next);
547
+ return;
548
+ }
549
+ if (event.key === "Enter") {
550
+ event.preventDefault();
551
+ event.currentTarget.blur();
552
+ }
553
+ }
554
+ }
555
+ )
556
+ }
557
+ )
558
+ ] });
559
+ }
560
+ function ColorField({ label, value, fallback, ownerWindow, onChange }) {
561
+ const parsed = parseCssColor(value);
562
+ const canvas = ownerWindow.document.createElement("canvas");
563
+ const context = canvas.getContext("2d");
564
+ if (context) context.fillStyle = value;
565
+ const serialized = typeof context?.fillStyle === "string" ? context.fillStyle : "";
566
+ const converted = serialized ? parseCssColor(serialized) : null;
567
+ const inputValue = parsed ?? converted ? colorToHex(parsed ?? converted).slice(0, 7) : fallback;
568
+ const sample = parsed ?? converted ?? parseCssColor(fallback);
569
+ const hexValue = sample ? colorToHex({ ...sample, alpha: 1 }).slice(1).toUpperCase() : "000000";
570
+ const alphaValue = sample ? Math.round(sample.alpha * 100) : 100;
571
+ const [hexDraft, setHexDraft] = (0, import_react2.useState)(hexValue);
572
+ const [alphaDraft, setAlphaDraft] = (0, import_react2.useState)(String(alphaValue));
573
+ const [hexFocused, setHexFocused] = (0, import_react2.useState)(false);
574
+ const [alphaFocused, setAlphaFocused] = (0, import_react2.useState)(false);
575
+ const updateColor = (nextHex, nextAlpha) => {
576
+ if (!/^[\da-f]{6}$/i.test(nextHex)) return;
577
+ const nextSample = parseCssColor(`#${nextHex}`);
578
+ if (!nextSample) return;
579
+ onChange(colorToHex({ ...nextSample, alpha: Math.min(100, Math.max(0, nextAlpha)) / 100 }));
580
+ };
581
+ const swatchColor = ownerWindow.CSS?.supports("color", value) ? value : fallback;
582
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("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: [
583
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: label }),
584
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
585
+ ControlShell,
586
+ {
587
+ left: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
588
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
589
+ "span",
590
+ {
591
+ 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",
592
+ style: { backgroundColor: swatchColor },
593
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
594
+ "input",
595
+ {
596
+ type: "color",
597
+ "aria-label": `${label} color picker`,
598
+ value: inputValue,
599
+ className: "msr:absolute msr:inset-0 msr:size-full msr:cursor-pointer msr:opacity-0",
600
+ onChange: (event) => onChange(event.target.value)
601
+ }
602
+ )
603
+ }
604
+ ),
605
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
606
+ "input",
607
+ {
608
+ "aria-label": `${label} hex value`,
609
+ type: "text",
610
+ value: hexFocused ? hexDraft : hexValue,
611
+ maxLength: 6,
612
+ 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",
613
+ onFocus: () => {
614
+ setHexDraft(hexValue);
615
+ setHexFocused(true);
616
+ },
617
+ onBlur: () => setHexFocused(false),
618
+ onChange: (event) => {
619
+ const next = event.target.value.replace(/[^\da-f]/gi, "").slice(0, 6).toUpperCase();
620
+ setHexDraft(next);
621
+ updateColor(next, alphaFocused ? Number(alphaDraft) : alphaValue);
622
+ },
623
+ onPointerDown: (event) => event.stopPropagation(),
624
+ onClick: (event) => event.stopPropagation()
625
+ }
626
+ )
627
+ ] }),
628
+ right: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
629
+ "input",
630
+ {
631
+ "aria-label": `${label} opacity value`,
632
+ type: "text",
633
+ inputMode: "numeric",
634
+ value: alphaFocused ? alphaDraft ? `${alphaDraft}%` : "" : `${alphaValue}%`,
635
+ maxLength: 4,
636
+ 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",
637
+ onFocus: () => {
638
+ setAlphaDraft(String(alphaValue));
639
+ setAlphaFocused(true);
640
+ },
641
+ onBlur: () => setAlphaFocused(false),
642
+ onChange: (event) => {
643
+ const next = event.target.value.replace(/\D/g, "").slice(0, 3);
644
+ setAlphaDraft(next);
645
+ updateColor(hexFocused ? hexDraft : hexValue, Number(next));
646
+ },
647
+ onKeyDown: (event) => {
648
+ if (event.key !== "ArrowUp" && event.key !== "ArrowDown") return;
649
+ event.preventDefault();
650
+ event.stopPropagation();
651
+ const current = Number.parseInt(alphaFocused ? alphaDraft : String(alphaValue), 10);
652
+ const direction = event.key === "ArrowUp" ? 1 : -1;
653
+ const next = Math.min(100, Math.max(0, (Number.isFinite(current) ? current : 0) + direction));
654
+ setAlphaDraft(String(next));
655
+ updateColor(hexFocused ? hexDraft : hexValue, next);
656
+ },
657
+ onPointerDown: (event) => event.stopPropagation(),
658
+ onClick: (event) => event.stopPropagation()
659
+ }
660
+ )
661
+ }
662
+ )
663
+ ] });
664
+ }
665
+ function SettingsPanel({
666
+ ownerWindow,
667
+ highlightColor,
668
+ setHighlightColor,
669
+ guideColor,
670
+ setGuideColor,
671
+ hoverHighlight,
672
+ setHoverHighlight,
673
+ persistOnReload,
674
+ setPersistOnReload,
675
+ colorFormats,
676
+ setColorFormats,
677
+ colorClickFormat,
678
+ setColorClickFormat,
679
+ snapEnabled,
680
+ setSnapEnabled,
681
+ snapGuidesEnabled,
682
+ setSnapGuidesEnabled,
683
+ selectNewGuideEnabled,
684
+ setSelectNewGuideEnabled,
685
+ multiMeasureEnabled,
686
+ setMultiMeasureEnabled,
687
+ guideStyle,
688
+ setGuideStyle,
689
+ rulerSettings,
690
+ setRulerSettings,
691
+ initialTab,
692
+ onResetSettings,
693
+ onClearWorkspace
694
+ }) {
695
+ const toggleFormat = (format) => {
696
+ setColorFormats((previous) => {
697
+ if (previous.includes(format)) {
698
+ if (previous.length === 1) return previous;
699
+ return previous.filter((item) => item !== format);
700
+ }
701
+ return [...previous, format];
702
+ });
703
+ };
704
+ const [activeTab, setActiveTab] = (0, import_react2.useState)(initialTab);
705
+ const patternTooltip = useTooltip();
706
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("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: [
707
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("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: [
708
+ ["guides", "Guides"],
709
+ ["select", "Select"],
710
+ ["color-picker", "Color"],
711
+ ["rulers", "Rulers"],
712
+ ["general", "General"]
713
+ ].map(([value, label]) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
714
+ "button",
715
+ {
716
+ type: "button",
717
+ role: "tab",
718
+ "aria-selected": activeTab === value,
719
+ className: cn(
720
+ "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]",
721
+ 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"
722
+ ),
723
+ onClick: () => setActiveTab(value),
724
+ children: label
725
+ },
726
+ value
727
+ )) }),
728
+ activeTab === "guides" ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("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: [
729
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ColorField, { label: "Color", value: guideColor, fallback: "#f97316", ownerWindow, onChange: setGuideColor }),
730
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(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 })) }),
731
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "msr:col-span-2 msr:grid msr:grid-cols-[78px_156px] msr:items-center msr:gap-3", children: [
732
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "msr:text-[12px] msr:text-ink-700", children: "Pattern" }),
733
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "msr:flex msr:gap-1", role: "radiogroup", "aria-label": "Guide pattern", onMouseLeave: patternTooltip.onTooltipContainerLeave, children: GUIDE_PATTERNS.map(({ value, label }) => {
734
+ const selected = guideStyle.pattern === value;
735
+ const tooltipId = `guide-pattern-${value}`;
736
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
737
+ "button",
738
+ {
739
+ type: "button",
740
+ role: "radio",
741
+ "aria-label": `${label} guide pattern`,
742
+ "aria-checked": selected,
743
+ className: cn(
744
+ "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]",
745
+ selected ? "msr:border-[#0d99ff] msr:bg-[#0d99ff]/10" : "msr:border-ink-200 msr:bg-ink-50 msr:hover:bg-ink-100"
746
+ ),
747
+ onClick: () => setGuideStyle((style) => ({ ...style, pattern: value })),
748
+ onMouseEnter: () => patternTooltip.onTooltipEnter(tooltipId),
749
+ onFocus: () => patternTooltip.onTooltipEnter(tooltipId),
750
+ onBlur: patternTooltip.onTooltipLeave,
751
+ children: [
752
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("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") }),
753
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Tooltip, { label, visible: patternTooltip.visibleTooltipId === tooltipId, instant: patternTooltip.tooltipInstant, className: "msr:z-10" })
754
+ ]
755
+ },
756
+ value
757
+ );
758
+ }) })
759
+ ] }),
760
+ guideStyle.pattern !== "solid" ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
761
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(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 })) }),
762
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(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 })) })
763
+ ] }) : null,
764
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "msr:col-span-2", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(SettingsSwitch, { label: "Snap", checked: snapGuidesEnabled, onChange: setSnapGuidesEnabled }) }),
765
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "msr:col-span-2", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(SettingsSwitch, { label: "Highlight", checked: selectNewGuideEnabled, onChange: setSelectNewGuideEnabled }) })
766
+ ] }) : null,
767
+ activeTab === "select" ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("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: [
768
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ColorField, { label: "Color", value: highlightColor, fallback: "#0d99ff", ownerWindow, onChange: setHighlightColor }),
769
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "msr:col-span-2", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(SettingsSwitch, { label: "Hover", checked: hoverHighlight, onChange: setHoverHighlight }) }),
770
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "msr:col-span-2", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(SettingsSwitch, { label: "Element snap", checked: snapEnabled, onChange: setSnapEnabled }) }),
771
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "msr:col-span-2", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(SettingsSwitch, { label: "Stack", checked: multiMeasureEnabled, onChange: setMultiMeasureEnabled }) })
772
+ ] }) : null,
773
+ activeTab === "color-picker" ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("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: [
774
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "msr:col-span-2 msr:grid msr:grid-cols-[78px_156px] msr:items-center msr:gap-3", children: [
775
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "msr:text-[12px] msr:text-ink-700", children: "Format" }),
776
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "msr:flex msr:min-w-0 msr:gap-1", children: COLOR_FORMATS.map((format) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
777
+ "button",
778
+ {
779
+ type: "button",
780
+ "aria-pressed": colorFormats.includes(format),
781
+ className: cn(
782
+ "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]",
783
+ 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"
784
+ ),
785
+ onClick: () => toggleFormat(format),
786
+ children: format
787
+ },
788
+ format
789
+ )) })
790
+ ] }),
791
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("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: [
792
+ "Copy",
793
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("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__ */ (0, import_jsx_runtime2.jsx)("option", { value: format, children: format }, format)) })
794
+ ] })
795
+ ] }) : null,
796
+ activeTab === "rulers" ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("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: [
797
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(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 })) }),
798
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "msr:col-span-2", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(SettingsSwitch, { label: "Edge reveal", checked: rulerSettings.edgeReveal, onChange: (edgeReveal) => setRulerSettings((settings) => ({ ...settings, edgeReveal })) }) })
799
+ ] }) : null,
800
+ activeTab === "general" ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("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: [
801
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "msr:col-span-2", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(SettingsSwitch, { label: "Persist", checked: persistOnReload, onChange: setPersistOnReload }) }),
802
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("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: [
803
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: "Version" }),
804
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "msr:justify-self-end msr:font-mono msr:text-[11px] msr:tabular-nums msr:text-ink-700", children: package_default.version })
805
+ ] }),
806
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "msr:col-span-2 msr:flex msr:justify-end msr:gap-1", children: [
807
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
808
+ "button",
809
+ {
810
+ type: "button",
811
+ "aria-label": "Reset settings to defaults",
812
+ 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]",
813
+ onClick: onResetSettings,
814
+ children: "Use defaults"
815
+ }
816
+ ),
817
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
818
+ "button",
819
+ {
820
+ type: "button",
821
+ "aria-label": "Clear workspace",
822
+ 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]",
823
+ onClick: onClearWorkspace,
824
+ children: "Clear workspace"
825
+ }
826
+ )
827
+ ] })
828
+ ] }) : null
829
+ ] });
830
+ }
831
+
832
+ // components/icons.tsx
833
+ var import_jsx_runtime3 = require("react/jsx-runtime");
834
+ var IconBase = ({ size = 24, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
90
835
  "svg",
91
836
  {
92
837
  xmlns: "http://www.w3.org/2000/svg",
@@ -94,12 +839,14 @@ var IconBase = ({ size = 24, ...props }) => /* @__PURE__ */ (0, import_jsx_runti
94
839
  height: size,
95
840
  viewBox: "0 0 256 256",
96
841
  fill: "currentColor",
842
+ "aria-hidden": "true",
843
+ onDragStart: (event) => event.preventDefault(),
97
844
  ...props
98
845
  }
99
846
  );
100
- var CursorIcon = ({ size = 20, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(IconBase, { size, ...props, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M166.59,134.1a1.91,1.91,0,0,1-.55-1.79,2,2,0,0,1,1.08-1.42l46.25-17.76.24-.1A14,14,0,0,0,212.38,87L52.29,34.7A13.95,13.95,0,0,0,34.7,52.29L87,212.38a13.82,13.82,0,0,0,12.6,9.6c.23,0,.46,0,.69,0A13.84,13.84,0,0,0,113,213.61a2.44,2.44,0,0,0,.1-.24l17.76-46.25a2,2,0,0,1,3.21-.53l51.31,51.31a14,14,0,0,0,19.8,0l12.69-12.69a14,14,0,0,0,0-19.8Zm42.82,62.63-12.68,12.68a2,2,0,0,1-2.83,0L142.59,158.1a14,14,0,0,0-22.74,4.32,2.44,2.44,0,0,0-.1.24L102,208.91a2,2,0,0,1-3.61-.26L46.11,48.57a1.87,1.87,0,0,1,.47-2A1.92,1.92,0,0,1,47.93,46a2.22,2.22,0,0,1,.64.1L208.65,98.38a2,2,0,0,1,.26,3.61l-46.25,17.76-.24.1a14,14,0,0,0-4.32,22.74h0l51.31,51.31A2,2,0,0,1,209.41,196.73Z" }) });
101
- var RulerIcon = ({ size = 20, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(IconBase, { size, ...props, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M233.91,74.79,181.22,22.1a14,14,0,0,0-19.8,0L22.09,161.41a14,14,0,0,0,0,19.8L74.78,233.9a14,14,0,0,0,19.8,0L233.91,94.59A14,14,0,0,0,233.91,74.79ZM225.42,86.1,86.1,225.41h0a2,2,0,0,1-2.83,0L30.58,172.73a2,2,0,0,1,0-2.83L64,136.48l27.76,27.76a6,6,0,1,0,8.48-8.48L72.48,128,96,104.48l27.76,27.76a6,6,0,0,0,8.48-8.48L104.48,96,128,72.49l27.76,27.75a6,6,0,0,0,8.48-8.48L136.49,64,169.9,30.59a2,2,0,0,1,2.83,0l52.69,52.68A2,2,0,0,1,225.42,86.1Z" }) });
102
- var XrayIcon = ({ size = 20, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(IconBase, { size, ...props, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
847
+ var CursorIcon = ({ size = 20, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(IconBase, { size, ...props, children: /* @__PURE__ */ (0, import_jsx_runtime3.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" }) });
848
+ var RulerIcon = ({ size = 20, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(IconBase, { size, ...props, children: /* @__PURE__ */ (0, import_jsx_runtime3.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" }) });
849
+ var XrayIcon = ({ size = 20, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(IconBase, { size, ...props, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
103
850
  "path",
104
851
  {
105
852
  fill: "none",
@@ -111,7 +858,7 @@ var XrayIcon = ({ size = 20, ...props }) => /* @__PURE__ */ (0, import_jsx_runti
111
858
  d: "M76 48h96a16 16 0 0 1 16 16v96a16 16 0 0 1-16 16H76a16 16 0 0 1-16-16V64a16 16 0 0 1 16-16Zm32 32h96a16 16 0 0 1 16 16v96a16 16 0 0 1-16 16h-96a16 16 0 0 1-16-16V96a16 16 0 0 1 16-16Z"
112
859
  }
113
860
  ) });
114
- var RulersIcon = ({ size = 20, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(IconBase, { size, ...props, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
861
+ var RulersIcon = ({ size = 20, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(IconBase, { size, ...props, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
115
862
  "path",
116
863
  {
117
864
  fill: "none",
@@ -122,10 +869,10 @@ var RulersIcon = ({ size = 20, ...props }) => /* @__PURE__ */ (0, import_jsx_run
122
869
  d: "M48 208V48h160M80 48v24m32-24v24m32-24v24m32-24v24M48 80h24M48 112h24m-24 32h24m-24 32h24"
123
870
  }
124
871
  ) });
125
- var CaretDownIcon = ({ size = 8, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(IconBase, { size, ...props, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M213.66,101.66l-80,80a8,8,0,0,1-11.32,0l-80-80A8,8,0,0,1,53.66,90.34L128,164.69l74.34-74.35a8,8,0,0,1,11.32,11.32Z" }) });
126
- var CheckIcon = ({ size = 12, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(IconBase, { size, ...props, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M232.49,80.49l-128,128a12,12,0,0,1-17,0l-56-56a12,12,0,1,1,17-17L96,183,215.51,63.51a12,12,0,0,1,17,17Z" }) });
127
- var MinusIcon = ({ size = 12, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(IconBase, { size, ...props, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M228,128a12,12,0,0,1-12,12H40a12,12,0,0,1,0-24H216A12,12,0,0,1,228,128Z" }) });
128
- var TextInspectorIcon = ({ size = 20, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
872
+ var CaretDownIcon = ({ size = 8, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(IconBase, { size, ...props, children: /* @__PURE__ */ (0, import_jsx_runtime3.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" }) });
873
+ var CheckIcon = ({ size = 12, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(IconBase, { size, ...props, children: /* @__PURE__ */ (0, import_jsx_runtime3.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" }) });
874
+ var MinusIcon = ({ size = 12, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(IconBase, { size, ...props, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { d: "M228,128a12,12,0,0,1-12,12H40a12,12,0,0,1,0-24H216A12,12,0,0,1,228,128Z" }) });
875
+ var TextInspectorIcon = ({ size = 20, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
129
876
  "svg",
130
877
  {
131
878
  xmlns: "http://www.w3.org/2000/svg",
@@ -133,8 +880,10 @@ var TextInspectorIcon = ({ size = 20, ...props }) => /* @__PURE__ */ (0, import_
133
880
  height: size,
134
881
  viewBox: "0 0 256 256",
135
882
  fill: "currentColor",
883
+ "aria-hidden": "true",
884
+ onDragStart: (event) => event.preventDefault(),
136
885
  ...props,
137
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
886
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
138
887
  "text",
139
888
  {
140
889
  x: "50%",
@@ -150,9 +899,54 @@ var TextInspectorIcon = ({ size = 20, ...props }) => /* @__PURE__ */ (0, import_
150
899
  )
151
900
  }
152
901
  );
902
+ var ColorPickerIcon = ({ size = 20, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
903
+ "svg",
904
+ {
905
+ xmlns: "http://www.w3.org/2000/svg",
906
+ width: size,
907
+ height: size,
908
+ viewBox: "0 0 24 24",
909
+ fill: "none",
910
+ ...props,
911
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
912
+ "path",
913
+ {
914
+ fill: "currentColor",
915
+ fillRule: "evenodd",
916
+ transform: "translate(-1.44 -1.44) scale(1.12)",
917
+ 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",
918
+ clipRule: "evenodd"
919
+ }
920
+ )
921
+ }
922
+ );
923
+ var GearIcon = ({ size = 20, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
924
+ "svg",
925
+ {
926
+ xmlns: "http://www.w3.org/2000/svg",
927
+ width: size,
928
+ height: size,
929
+ viewBox: "0 0 24 24",
930
+ fill: "none",
931
+ ...props,
932
+ children: [
933
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
934
+ "path",
935
+ {
936
+ 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",
937
+ stroke: "currentColor",
938
+ strokeWidth: "1",
939
+ strokeLinecap: "round",
940
+ strokeLinejoin: "round"
941
+ }
942
+ ),
943
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("circle", { cx: "12", cy: "12", r: "2.5", stroke: "currentColor", strokeWidth: "1" })
944
+ ]
945
+ }
946
+ );
153
947
 
154
948
  // components/toolbar.tsx
155
- var import_jsx_runtime2 = require("react/jsx-runtime");
949
+ var import_jsx_runtime4 = require("react/jsx-runtime");
156
950
  var TOOLBAR_TOOLTIP_DELAY_MS = 800;
157
951
  var TOOLBAR_DRAG_SLOP = 6;
158
952
  var GUIDE_MENU_WIDTH = 176;
@@ -170,61 +964,43 @@ function ToolbarButton({
170
964
  onTooltipLeave,
171
965
  children
172
966
  }) {
173
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
967
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
174
968
  "div",
175
969
  {
176
970
  className: "msr:relative",
177
971
  onMouseEnter: () => onTooltipEnter(id),
178
972
  onMouseLeave: () => onTooltipLeave(id),
179
973
  children: [
180
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
974
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
181
975
  "button",
182
976
  {
183
977
  type: "button",
184
- "aria-label": `${label} (${shortcut})`,
185
978
  "aria-pressed": active,
186
- title: `${label} (${shortcut})`,
979
+ "aria-label": `${label} (${shortcut})`,
187
980
  className: cn(
188
- "msr:flex msr:size-8 msr:items-center msr:justify-center msr:rounded-[8px] msr:outline-none",
981
+ "msr:flex msr:size-8 msr:select-none msr:items-center msr:justify-center msr:rounded-[8px] msr:outline-none",
189
982
  active ? "msr:bg-[#0d99ff] msr:text-white" : "msr:bg-transparent msr:text-black msr:hover:bg-black/4"
190
983
  ),
191
984
  onClick,
192
985
  children
193
986
  }
194
987
  ),
195
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
196
- "span",
197
- {
198
- className: cn(
199
- cn(
200
- "msr:pointer-events-none msr:absolute msr:left-1/2 msr:-translate-x-1/2 msr:whitespace-nowrap msr:rounded msr:bg-black msr:px-2 msr:py-1 msr:text-[11px] msr:text-white msr:transition-opacity msr:duration-150 msr:select-none",
201
- tooltipInstant && "msr:transition-none"
202
- ),
203
- tooltipSide === "top" ? "msr:bottom-full msr:mb-2" : "msr:top-full msr:mt-2",
204
- tooltipVisible ? "msr:opacity-100" : "msr:opacity-0"
205
- ),
206
- children: [
207
- label,
208
- " ",
209
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("kbd", { className: "msr:text-white/60", children: shortcut })
210
- ]
211
- }
212
- )
988
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Tooltip, { label, shortcut, visible: tooltipVisible, instant: tooltipInstant, side: tooltipSide })
213
989
  ]
214
990
  }
215
991
  );
216
992
  }
217
993
  function useToolbarTooltip() {
218
- const [visibleTooltipId, setVisibleTooltipId] = (0, import_react.useState)(null);
219
- const timerRef = (0, import_react.useRef)(null);
220
- const instantRef = (0, import_react.useRef)(false);
221
- const [tooltipInstant, setTooltipInstant] = (0, import_react.useState)(false);
222
- const clearTimer = (0, import_react.useCallback)(() => {
994
+ const [visibleTooltipId, setVisibleTooltipId] = (0, import_react3.useState)(null);
995
+ const timerRef = (0, import_react3.useRef)(null);
996
+ const instantRef = (0, import_react3.useRef)(false);
997
+ const [tooltipInstant, setTooltipInstant] = (0, import_react3.useState)(false);
998
+ const clearTimer = (0, import_react3.useCallback)(() => {
223
999
  if (timerRef.current === null) return;
224
1000
  window.clearTimeout(timerRef.current);
225
1001
  timerRef.current = null;
226
1002
  }, []);
227
- const onTooltipEnter = (0, import_react.useCallback)(
1003
+ const onTooltipEnter = (0, import_react3.useCallback)(
228
1004
  (id) => {
229
1005
  clearTimer();
230
1006
  if (instantRef.current) {
@@ -241,14 +1017,14 @@ function useToolbarTooltip() {
241
1017
  },
242
1018
  [clearTimer]
243
1019
  );
244
- const onTooltipLeave = (0, import_react.useCallback)(
1020
+ const onTooltipLeave = (0, import_react3.useCallback)(
245
1021
  (id) => {
246
1022
  clearTimer();
247
1023
  setVisibleTooltipId((prev) => prev === id ? null : prev);
248
1024
  },
249
1025
  [clearTimer]
250
1026
  );
251
- const onToolbarLeave = (0, import_react.useCallback)(() => {
1027
+ const onToolbarLeave = (0, import_react3.useCallback)(() => {
252
1028
  clearTimer();
253
1029
  setVisibleTooltipId(null);
254
1030
  instantRef.current = false;
@@ -263,10 +1039,11 @@ function useToolbarTooltip() {
263
1039
  };
264
1040
  }
265
1041
  function useToolbarDrag(initialPosition, eventTarget) {
266
- const [position, setPosition] = (0, import_react.useState)(initialPosition);
267
- const suppressClickRef = (0, import_react.useRef)(false);
268
- const detachListenersRef = (0, import_react.useRef)(null);
269
- const dragRef = (0, import_react.useRef)({
1042
+ const [position, setPosition] = (0, import_react3.useState)(initialPosition);
1043
+ const suppressClickRef = (0, import_react3.useRef)(false);
1044
+ const previousUserSelectRef = (0, import_react3.useRef)(null);
1045
+ const detachListenersRef = (0, import_react3.useRef)(null);
1046
+ const dragRef = (0, import_react3.useRef)({
270
1047
  active: false,
271
1048
  didDrag: false,
272
1049
  pointerId: -1,
@@ -277,12 +1054,28 @@ function useToolbarDrag(initialPosition, eventTarget) {
277
1054
  width: 0,
278
1055
  height: 0
279
1056
  });
280
- const onPointerDown = (0, import_react.useCallback)(
1057
+ const disableTextSelection = (0, import_react3.useCallback)(() => {
1058
+ if (previousUserSelectRef.current !== null) return;
1059
+ const root = eventTarget.document.documentElement;
1060
+ previousUserSelectRef.current = root.style.userSelect;
1061
+ root.style.setProperty("user-select", "none", "important");
1062
+ }, [eventTarget]);
1063
+ const restoreTextSelection = (0, import_react3.useCallback)(() => {
1064
+ const previous = previousUserSelectRef.current;
1065
+ if (previous === null) return;
1066
+ const root = eventTarget.document.documentElement;
1067
+ root.style.userSelect = previous;
1068
+ previousUserSelectRef.current = null;
1069
+ }, [eventTarget]);
1070
+ (0, import_react3.useEffect)(() => restoreTextSelection, [restoreTextSelection]);
1071
+ const onPointerDown = (0, import_react3.useCallback)(
281
1072
  (event) => {
282
1073
  if (event.button !== 0) return;
1074
+ disableTextSelection();
283
1075
  if (detachListenersRef.current) {
284
1076
  detachListenersRef.current();
285
1077
  detachListenersRef.current = null;
1078
+ restoreTextSelection();
286
1079
  }
287
1080
  const state = dragRef.current;
288
1081
  state.active = false;
@@ -317,6 +1110,7 @@ function useToolbarDrag(initialPosition, eventTarget) {
317
1110
  if (current.pointerId !== endEvent.pointerId && current.pointerId !== -1)
318
1111
  return;
319
1112
  suppressClickRef.current = current.didDrag;
1113
+ restoreTextSelection();
320
1114
  current.active = false;
321
1115
  current.didDrag = false;
322
1116
  current.pointerId = -1;
@@ -334,9 +1128,9 @@ function useToolbarDrag(initialPosition, eventTarget) {
334
1128
  eventTarget.removeEventListener("pointercancel", handlePointerEnd);
335
1129
  };
336
1130
  },
337
- [eventTarget, position.x, position.y]
1131
+ [disableTextSelection, eventTarget, position.x, position.y, restoreTextSelection]
338
1132
  );
339
- const onClickCapture = (0, import_react.useCallback)(
1133
+ const onClickCapture = (0, import_react3.useCallback)(
340
1134
  (event) => {
341
1135
  if (!suppressClickRef.current) return;
342
1136
  event.preventDefault();
@@ -351,12 +1145,46 @@ function ToolbarComponent({
351
1145
  toolMode,
352
1146
  setEnabled,
353
1147
  setToolMode,
1148
+ xrayVisible,
1149
+ setXrayVisible,
354
1150
  rulersVisible,
355
1151
  setRulersVisible,
356
1152
  guideOrientation,
357
1153
  setGuideOrientation,
358
1154
  onInteract,
359
- eventTarget
1155
+ eventTarget,
1156
+ colorPickerActive,
1157
+ setColorPickerActive,
1158
+ onColorPickerClick,
1159
+ settingsOpen,
1160
+ setSettingsOpen,
1161
+ highlightColor,
1162
+ setHighlightColor,
1163
+ guideColor,
1164
+ setGuideColor,
1165
+ hoverHighlight,
1166
+ setHoverHighlight,
1167
+ persistOnReload,
1168
+ setPersistOnReload,
1169
+ colorPickerFormats,
1170
+ setColorPickerFormats,
1171
+ colorPickerClickFormat,
1172
+ setColorPickerClickFormat,
1173
+ snapEnabled,
1174
+ setSnapEnabled,
1175
+ snapGuidesEnabled,
1176
+ setSnapGuidesEnabled,
1177
+ selectNewGuideEnabled,
1178
+ setSelectNewGuideEnabled,
1179
+ multiMeasureEnabled,
1180
+ setMultiMeasureEnabled,
1181
+ guideStyle,
1182
+ setGuideStyle,
1183
+ rulerSettings,
1184
+ setRulerSettings,
1185
+ initialSettingsTab,
1186
+ onResetSettings,
1187
+ onClearWorkspace
360
1188
  }, ref) {
361
1189
  const { position, onPointerDown, onClickCapture } = useToolbarDrag({
362
1190
  x: 16,
@@ -369,11 +1197,13 @@ function ToolbarComponent({
369
1197
  onTooltipLeave,
370
1198
  onToolbarLeave
371
1199
  } = useToolbarTooltip();
372
- const [guideMenuOpen, setGuideMenuOpen] = (0, import_react.useState)(false);
373
- const guideMenuRef = (0, import_react.useRef)(null);
374
- const [activeMenuIndex, setActiveMenuIndex] = (0, import_react.useState)(0);
375
- const [menuAlign, setMenuAlign] = (0, import_react.useState)("right");
376
- const updateMenuAlign = (0, import_react.useCallback)(() => {
1200
+ const [guideMenuOpen, setGuideMenuOpen] = (0, import_react3.useState)(false);
1201
+ const settingsRef = (0, import_react3.useRef)(null);
1202
+ const guideMenuRef = (0, import_react3.useRef)(null);
1203
+ const [activeMenuIndex, setActiveMenuIndex] = (0, import_react3.useState)(0);
1204
+ const [menuAlign, setMenuAlign] = (0, import_react3.useState)("right");
1205
+ const tooltipsEnabled = !guideMenuOpen && !settingsOpen;
1206
+ const updateMenuAlign = (0, import_react3.useCallback)(() => {
377
1207
  const anchorRect = guideMenuRef.current?.getBoundingClientRect();
378
1208
  if (!anchorRect) return;
379
1209
  const rightAlignedLeft = anchorRect.right - GUIDE_MENU_WIDTH;
@@ -393,34 +1223,50 @@ function ToolbarComponent({
393
1223
  const nearBottom = viewportHeight > 0 && position.y > viewportHeight - 56;
394
1224
  const tooltipSide = nearTop && !nearBottom ? "bottom" : "top";
395
1225
  const menuSide = nearBottom ? "top" : "bottom";
396
- const selectMode = (0, import_react.useCallback)(() => {
1226
+ const selectMode = (0, import_react3.useCallback)(() => {
397
1227
  setEnabled(true);
1228
+ setColorPickerActive(false);
398
1229
  setToolMode((prev) => prev === "select" ? "none" : "select");
399
1230
  onInteract();
400
- }, [onInteract, setEnabled, setToolMode]);
401
- const guidesMode = (0, import_react.useCallback)(() => {
1231
+ }, [onInteract, setColorPickerActive, setEnabled, setToolMode]);
1232
+ const guidesMode = (0, import_react3.useCallback)(() => {
402
1233
  setEnabled(true);
1234
+ setColorPickerActive(false);
403
1235
  setToolMode((prev) => prev === "guides" ? "none" : "guides");
404
1236
  onInteract();
405
- }, [onInteract, setEnabled, setToolMode]);
406
- const textInspectorMode = (0, import_react.useCallback)(() => {
1237
+ }, [onInteract, setColorPickerActive, setEnabled, setToolMode]);
1238
+ const textInspectorMode = (0, import_react3.useCallback)(() => {
407
1239
  setEnabled(true);
1240
+ setColorPickerActive(false);
408
1241
  setToolMode(
409
1242
  (prev) => prev === "text-inspector" ? "none" : "text-inspector"
410
1243
  );
411
1244
  onInteract();
412
- }, [onInteract, setEnabled, setToolMode]);
413
- const xrayMode = (0, import_react.useCallback)(() => {
1245
+ }, [onInteract, setColorPickerActive, setEnabled, setToolMode]);
1246
+ const xrayMode = (0, import_react3.useCallback)(() => {
414
1247
  setEnabled(true);
415
- setToolMode((prev) => prev === "xray" ? "none" : "xray");
1248
+ setColorPickerActive(false);
1249
+ setXrayVisible((prev) => !prev);
416
1250
  onInteract();
417
- }, [onInteract, setEnabled, setToolMode]);
418
- const rulersMode = (0, import_react.useCallback)(() => {
1251
+ }, [onInteract, setColorPickerActive, setEnabled, setXrayVisible]);
1252
+ const colorPickerMode = (0, import_react3.useCallback)(() => {
419
1253
  setEnabled(true);
1254
+ setToolMode("none");
1255
+ if (colorPickerActive) {
1256
+ setColorPickerActive(false);
1257
+ } else {
1258
+ setColorPickerActive(true);
1259
+ onColorPickerClick();
1260
+ }
1261
+ onInteract();
1262
+ }, [colorPickerActive, onColorPickerClick, onInteract, setColorPickerActive, setEnabled, setToolMode]);
1263
+ const rulersMode = (0, import_react3.useCallback)(() => {
1264
+ setEnabled(true);
1265
+ setColorPickerActive(false);
420
1266
  setRulersVisible((prev) => !prev);
421
1267
  onInteract();
422
- }, [onInteract, setEnabled, setRulersVisible]);
423
- const selectGuideOrientation = (0, import_react.useCallback)(
1268
+ }, [onInteract, setColorPickerActive, setEnabled, setRulersVisible]);
1269
+ const selectGuideOrientation = (0, import_react3.useCallback)(
424
1270
  (orientation) => {
425
1271
  setEnabled(true);
426
1272
  setToolMode("guides");
@@ -430,30 +1276,34 @@ function ToolbarComponent({
430
1276
  },
431
1277
  [onInteract, setEnabled, setGuideOrientation, setToolMode]
432
1278
  );
433
- (0, import_react.useLayoutEffect)(() => {
434
- if (!guideMenuOpen) return;
435
- const frame = eventTarget.requestAnimationFrame(() => {
1279
+ (0, import_react3.useLayoutEffect)(() => {
1280
+ if (!guideMenuOpen && !settingsOpen) return;
1281
+ const frame = guideMenuOpen ? eventTarget.requestAnimationFrame(() => {
436
1282
  guideMenuRef.current?.querySelector("[role='menu']")?.focus();
437
- });
1283
+ }) : 0;
438
1284
  const handlePointerDown = (event) => {
439
- const menu = guideMenuRef.current;
440
- if (!menu) return;
441
1285
  const path = event.composedPath();
442
- if (path.includes(menu)) return;
443
- setGuideMenuOpen(false);
1286
+ if (guideMenuOpen) {
1287
+ const menu = guideMenuRef.current;
1288
+ if (menu && !path.includes(menu)) setGuideMenuOpen(false);
1289
+ }
1290
+ if (settingsOpen) {
1291
+ const settings = settingsRef.current;
1292
+ if (settings && !path.includes(settings)) setSettingsOpen(false);
1293
+ }
444
1294
  };
445
1295
  const handleResize = () => {
446
- updateMenuAlign();
1296
+ if (guideMenuOpen) updateMenuAlign();
447
1297
  };
448
1298
  eventTarget.addEventListener("pointerdown", handlePointerDown);
449
- eventTarget.addEventListener("resize", handleResize);
1299
+ if (guideMenuOpen) eventTarget.addEventListener("resize", handleResize);
450
1300
  return () => {
451
- eventTarget.cancelAnimationFrame(frame);
1301
+ if (frame) eventTarget.cancelAnimationFrame(frame);
452
1302
  eventTarget.removeEventListener("pointerdown", handlePointerDown);
453
1303
  eventTarget.removeEventListener("resize", handleResize);
454
1304
  };
455
- }, [eventTarget, guideMenuOpen, guideOrientation, updateMenuAlign]);
456
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
1305
+ }, [eventTarget, guideMenuOpen, guideOrientation, settingsOpen, updateMenuAlign]);
1306
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
457
1307
  "div",
458
1308
  {
459
1309
  ref,
@@ -466,7 +1316,7 @@ function ToolbarComponent({
466
1316
  onClickCapture,
467
1317
  onMouseLeave: onToolbarLeave,
468
1318
  children: [
469
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1319
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
470
1320
  ToolbarButton,
471
1321
  {
472
1322
  id: "select",
@@ -474,31 +1324,47 @@ function ToolbarComponent({
474
1324
  label: "Select",
475
1325
  shortcut: "S",
476
1326
  onClick: selectMode,
477
- tooltipVisible: visibleTooltipId === "select",
1327
+ tooltipVisible: tooltipsEnabled && visibleTooltipId === "select",
478
1328
  tooltipInstant,
479
1329
  tooltipSide,
480
1330
  onTooltipEnter,
481
1331
  onTooltipLeave,
482
- children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(CursorIcon, { size: 20 })
1332
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(CursorIcon, { size: 20 })
483
1333
  }
484
1334
  ),
485
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1335
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
486
1336
  ToolbarButton,
487
1337
  {
488
1338
  id: "xray",
489
- active: toolMode === "xray",
1339
+ active: xrayVisible,
490
1340
  label: "X-ray",
491
1341
  shortcut: "X",
492
1342
  onClick: xrayMode,
493
- tooltipVisible: visibleTooltipId === "xray",
1343
+ tooltipVisible: tooltipsEnabled && visibleTooltipId === "xray",
494
1344
  tooltipInstant,
495
1345
  tooltipSide,
496
1346
  onTooltipEnter,
497
1347
  onTooltipLeave,
498
- children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(XrayIcon, { size: 20 })
1348
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(XrayIcon, { size: 20 })
499
1349
  }
500
1350
  ),
501
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1351
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1352
+ ToolbarButton,
1353
+ {
1354
+ id: "color-picker",
1355
+ active: colorPickerActive,
1356
+ label: "Color picker",
1357
+ shortcut: "P",
1358
+ onClick: colorPickerMode,
1359
+ tooltipVisible: tooltipsEnabled && visibleTooltipId === "color-picker",
1360
+ tooltipInstant,
1361
+ tooltipSide,
1362
+ onTooltipEnter,
1363
+ onTooltipLeave,
1364
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ColorPickerIcon, { size: 20, "aria-hidden": "true" })
1365
+ }
1366
+ ),
1367
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
502
1368
  ToolbarButton,
503
1369
  {
504
1370
  id: "rulers",
@@ -506,15 +1372,15 @@ function ToolbarComponent({
506
1372
  label: "Rulers",
507
1373
  shortcut: "R",
508
1374
  onClick: rulersMode,
509
- tooltipVisible: visibleTooltipId === "rulers",
1375
+ tooltipVisible: tooltipsEnabled && visibleTooltipId === "rulers",
510
1376
  tooltipInstant,
511
1377
  tooltipSide,
512
1378
  onTooltipEnter,
513
1379
  onTooltipLeave,
514
- children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(RulersIcon, { size: 20 })
1380
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(RulersIcon, { size: 20 })
515
1381
  }
516
1382
  ),
517
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1383
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
518
1384
  ToolbarButton,
519
1385
  {
520
1386
  id: "text-inspector",
@@ -522,15 +1388,15 @@ function ToolbarComponent({
522
1388
  label: "Text inspector",
523
1389
  shortcut: "A",
524
1390
  onClick: textInspectorMode,
525
- tooltipVisible: visibleTooltipId === "text-inspector",
1391
+ tooltipVisible: tooltipsEnabled && visibleTooltipId === "text-inspector",
526
1392
  tooltipInstant,
527
1393
  tooltipSide,
528
1394
  onTooltipEnter,
529
1395
  onTooltipLeave,
530
- children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(TextInspectorIcon, { size: 20, "aria-hidden": "true" })
1396
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(TextInspectorIcon, { size: 20, "aria-hidden": "true" })
531
1397
  }
532
1398
  ),
533
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1399
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
534
1400
  ToolbarButton,
535
1401
  {
536
1402
  id: "guides",
@@ -538,12 +1404,12 @@ function ToolbarComponent({
538
1404
  label: "Guides",
539
1405
  shortcut: "G",
540
1406
  onClick: guidesMode,
541
- tooltipVisible: visibleTooltipId === "guides",
1407
+ tooltipVisible: tooltipsEnabled && visibleTooltipId === "guides",
542
1408
  tooltipInstant,
543
1409
  tooltipSide,
544
1410
  onTooltipEnter,
545
1411
  onTooltipLeave,
546
- children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1412
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
547
1413
  RulerIcon,
548
1414
  {
549
1415
  size: 20,
@@ -554,7 +1420,7 @@ function ToolbarComponent({
554
1420
  )
555
1421
  }
556
1422
  ),
557
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
1423
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
558
1424
  "div",
559
1425
  {
560
1426
  className: "msr:group msr:relative msr:-ml-1 msr:flex msr:items-stretch",
@@ -562,12 +1428,11 @@ function ToolbarComponent({
562
1428
  onMouseEnter: () => onTooltipEnter("guide-menu"),
563
1429
  onMouseLeave: () => onTooltipLeave("guide-menu"),
564
1430
  children: [
565
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1431
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
566
1432
  "button",
567
1433
  {
568
1434
  type: "button",
569
1435
  "aria-label": "Guide orientation menu",
570
- title: "Guide orientation",
571
1436
  className: cn(
572
1437
  "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",
573
1438
  guideMenuOpen ? "msr:bg-black/10 msr:text-black" : "msr:text-black"
@@ -582,21 +1447,21 @@ function ToolbarComponent({
582
1447
  return !prev;
583
1448
  });
584
1449
  },
585
- children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(CaretDownIcon, { size: 8 })
1450
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(CaretDownIcon, { size: 8 })
586
1451
  }
587
1452
  ),
588
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1453
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
589
1454
  "span",
590
1455
  {
591
1456
  className: cn(
592
1457
  "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",
593
1458
  tooltipSide === "top" ? "msr:bottom-full msr:mb-2" : "msr:top-full msr:mt-2",
594
- visibleTooltipId === "guide-menu" && !guideMenuOpen ? "msr:opacity-100" : "msr:opacity-0"
1459
+ visibleTooltipId === "guide-menu" && tooltipsEnabled ? "msr:opacity-100" : "msr:opacity-0"
595
1460
  ),
596
1461
  children: "Orientation Guide"
597
1462
  }
598
1463
  ),
599
- guideMenuOpen ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
1464
+ guideMenuOpen ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
600
1465
  "div",
601
1466
  {
602
1467
  className: cn(
@@ -637,7 +1502,7 @@ function ToolbarComponent({
637
1502
  }
638
1503
  },
639
1504
  children: [
640
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
1505
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
641
1506
  "button",
642
1507
  {
643
1508
  type: "button",
@@ -647,7 +1512,7 @@ function ToolbarComponent({
647
1512
  ),
648
1513
  onClick: () => selectGuideOrientation("horizontal"),
649
1514
  children: [
650
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1515
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
651
1516
  CheckIcon,
652
1517
  {
653
1518
  size: 12,
@@ -656,13 +1521,13 @@ function ToolbarComponent({
656
1521
  )
657
1522
  }
658
1523
  ),
659
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(MinusIcon, { size: 12 }),
660
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "msr:flex-1", children: "Horizontal" }),
661
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: "H" })
1524
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(MinusIcon, { size: 12 }),
1525
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "msr:flex-1", children: "Horizontal" }),
1526
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: "H" })
662
1527
  ]
663
1528
  }
664
1529
  ),
665
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
1530
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
666
1531
  "button",
667
1532
  {
668
1533
  type: "button",
@@ -672,7 +1537,7 @@ function ToolbarComponent({
672
1537
  ),
673
1538
  onClick: () => selectGuideOrientation("vertical"),
674
1539
  children: [
675
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1540
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
676
1541
  CheckIcon,
677
1542
  {
678
1543
  size: 12,
@@ -681,35 +1546,310 @@ function ToolbarComponent({
681
1546
  )
682
1547
  }
683
1548
  ),
684
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(MinusIcon, { size: 12, className: "msr:rotate-90" }),
685
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "msr:flex-1", children: "Vertical" }),
686
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: "V" })
1549
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(MinusIcon, { size: 12, className: "msr:rotate-90" }),
1550
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "msr:flex-1", children: "Vertical" }),
1551
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: "V" })
687
1552
  ]
688
1553
  }
689
1554
  )
690
1555
  ]
691
1556
  }
692
- ) : null
1557
+ ) : null
1558
+ ]
1559
+ }
1560
+ ),
1561
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { ref: settingsRef, className: "msr:relative msr:flex", children: [
1562
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1563
+ ToolbarButton,
1564
+ {
1565
+ id: "settings",
1566
+ active: settingsOpen,
1567
+ label: "Settings",
1568
+ shortcut: "\u2318/Ctrl+,",
1569
+ onClick: () => {
1570
+ onInteract();
1571
+ setSettingsOpen((previous) => !previous);
1572
+ },
1573
+ tooltipVisible: tooltipsEnabled && visibleTooltipId === "settings",
1574
+ tooltipInstant,
1575
+ tooltipSide,
1576
+ onTooltipEnter,
1577
+ onTooltipLeave,
1578
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(GearIcon, { size: 20, "aria-hidden": "true" })
1579
+ }
1580
+ ),
1581
+ settingsOpen ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1582
+ "div",
1583
+ {
1584
+ className: cn(
1585
+ "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",
1586
+ menuSide === "bottom" ? "msr:top-full msr:mt-2" : "msr:bottom-full msr:mb-2"
1587
+ ),
1588
+ "data-mesurer-inspector-ui": "true",
1589
+ role: "dialog",
1590
+ "aria-label": "Settings",
1591
+ onPointerDown: (event) => event.stopPropagation(),
1592
+ onPointerMove: (event) => event.stopPropagation(),
1593
+ onPointerUp: (event) => event.stopPropagation(),
1594
+ onClick: (event) => event.stopPropagation(),
1595
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1596
+ SettingsPanel,
1597
+ {
1598
+ ownerWindow: eventTarget,
1599
+ highlightColor,
1600
+ setHighlightColor,
1601
+ guideColor,
1602
+ setGuideColor,
1603
+ hoverHighlight,
1604
+ setHoverHighlight,
1605
+ persistOnReload,
1606
+ setPersistOnReload,
1607
+ colorFormats: colorPickerFormats,
1608
+ setColorFormats: setColorPickerFormats,
1609
+ colorClickFormat: colorPickerClickFormat,
1610
+ setColorClickFormat: setColorPickerClickFormat,
1611
+ snapEnabled,
1612
+ setSnapEnabled,
1613
+ snapGuidesEnabled,
1614
+ setSnapGuidesEnabled,
1615
+ selectNewGuideEnabled,
1616
+ setSelectNewGuideEnabled,
1617
+ multiMeasureEnabled,
1618
+ setMultiMeasureEnabled,
1619
+ guideStyle,
1620
+ setGuideStyle,
1621
+ rulerSettings,
1622
+ setRulerSettings,
1623
+ initialTab: initialSettingsTab,
1624
+ onResetSettings,
1625
+ onClearWorkspace
1626
+ }
1627
+ )
1628
+ }
1629
+ ) : null
1630
+ ] })
1631
+ ]
1632
+ }
1633
+ );
1634
+ }
1635
+ var Toolbar = (0, import_react3.memo)((0, import_react3.forwardRef)(ToolbarComponent));
1636
+
1637
+ // components/color-picker.tsx
1638
+ var import_react4 = require("react");
1639
+ var import_jsx_runtime5 = require("react/jsx-runtime");
1640
+ function CopyableColorValue({
1641
+ id,
1642
+ value,
1643
+ copiedId,
1644
+ onCopy,
1645
+ onTooltipEnter,
1646
+ tooltip,
1647
+ className
1648
+ }) {
1649
+ const copied = copiedId === id;
1650
+ const showTooltip = tooltip.visibleTooltipId === id || copied && tooltip.visibleTooltipId === null;
1651
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
1652
+ "span",
1653
+ {
1654
+ className: "msr:relative msr:inline-flex",
1655
+ onMouseLeave: tooltip.onTooltipLeave,
1656
+ children: [
1657
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1658
+ "button",
1659
+ {
1660
+ type: "button",
1661
+ className,
1662
+ onMouseEnter: () => onTooltipEnter(id),
1663
+ onFocus: () => onTooltipEnter(id),
1664
+ onBlur: tooltip.onTooltipLeave,
1665
+ onClick: onCopy,
1666
+ children: value
1667
+ }
1668
+ ),
1669
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1670
+ Tooltip,
1671
+ {
1672
+ label: copied ? "Copied!" : "Click to copy",
1673
+ visible: showTooltip,
1674
+ instant: copied || tooltip.tooltipInstant,
1675
+ side: "bottom",
1676
+ className: "msr:z-10"
1677
+ }
1678
+ )
1679
+ ]
1680
+ }
1681
+ );
1682
+ }
1683
+ function ColorPicker({
1684
+ active,
1685
+ sample,
1686
+ unsupported,
1687
+ formats,
1688
+ favoriteFormat,
1689
+ ownerWindow,
1690
+ toolbarRef,
1691
+ onClose
1692
+ }) {
1693
+ const panelRef = (0, import_react4.useRef)(null);
1694
+ const copyTimeoutRef = (0, import_react4.useRef)(null);
1695
+ const [copiedId, setCopiedId] = (0, import_react4.useState)(null);
1696
+ const tooltip = useTooltip();
1697
+ const copyValue = (0, import_react4.useCallback)(
1698
+ (id, value) => {
1699
+ const clipboardWrite = ownerWindow.navigator.clipboard?.writeText(value);
1700
+ void clipboardWrite?.catch(() => void 0);
1701
+ tooltip.onTooltipLeave();
1702
+ setCopiedId(id);
1703
+ if (copyTimeoutRef.current !== null) {
1704
+ ownerWindow.clearTimeout(copyTimeoutRef.current);
1705
+ }
1706
+ copyTimeoutRef.current = ownerWindow.setTimeout(() => {
1707
+ copyTimeoutRef.current = null;
1708
+ setCopiedId(null);
1709
+ }, 1500);
1710
+ },
1711
+ [ownerWindow, tooltip]
1712
+ );
1713
+ const handleTooltipEnter = (0, import_react4.useCallback)(
1714
+ (id) => {
1715
+ if (copiedId !== null && copiedId !== id) {
1716
+ if (copyTimeoutRef.current !== null) {
1717
+ ownerWindow.clearTimeout(copyTimeoutRef.current);
1718
+ copyTimeoutRef.current = null;
1719
+ }
1720
+ setCopiedId(null);
1721
+ }
1722
+ tooltip.onTooltipEnter(id);
1723
+ },
1724
+ [copiedId, ownerWindow, tooltip]
1725
+ );
1726
+ (0, import_react4.useLayoutEffect)(() => {
1727
+ if (!active) return;
1728
+ const panel = panelRef.current;
1729
+ const toolbar = toolbarRef.current;
1730
+ if (!panel || !toolbar) return;
1731
+ let frame = 0;
1732
+ const updatePosition = () => {
1733
+ const toolbarRect = toolbar.getBoundingClientRect();
1734
+ const panelRect = panel.getBoundingClientRect();
1735
+ const left = Math.min(
1736
+ Math.max(8, toolbarRect.left),
1737
+ ownerWindow.innerWidth - panelRect.width - 8
1738
+ );
1739
+ const belowTop = toolbarRect.bottom + 8;
1740
+ const aboveTop = toolbarRect.top - panelRect.height - 8;
1741
+ const top = belowTop + panelRect.height <= ownerWindow.innerHeight ? belowTop : Math.max(8, aboveTop);
1742
+ panel.style.left = `${left}px`;
1743
+ panel.style.top = `${top}px`;
1744
+ frame = ownerWindow.requestAnimationFrame(updatePosition);
1745
+ };
1746
+ frame = ownerWindow.requestAnimationFrame(updatePosition);
1747
+ return () => ownerWindow.cancelAnimationFrame(frame);
1748
+ }, [active, ownerWindow, toolbarRef, formats, sample, unsupported, favoriteFormat]);
1749
+ (0, import_react4.useLayoutEffect)(() => {
1750
+ return () => {
1751
+ if (copyTimeoutRef.current !== null) {
1752
+ ownerWindow.clearTimeout(copyTimeoutRef.current);
1753
+ }
1754
+ };
1755
+ }, [ownerWindow]);
1756
+ if (!active || !sample && !unsupported) return null;
1757
+ const headerFormat = formats.includes(favoriteFormat) ? favoriteFormat : formats[0];
1758
+ const secondaryFormats = headerFormat ? formats.filter((format) => format !== headerFormat) : [];
1759
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1760
+ "div",
1761
+ {
1762
+ ref: panelRef,
1763
+ 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",
1764
+ role: "dialog",
1765
+ "aria-label": "Selected color values",
1766
+ onMouseLeave: tooltip.onTooltipContainerLeave,
1767
+ children: unsupported ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "msr:flex msr:items-start msr:gap-2", children: [
1768
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "msr:text-black/60", children: "Color picker is not supported in this browser." }),
1769
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1770
+ "button",
1771
+ {
1772
+ type: "button",
1773
+ className: "msr:text-black/45 msr:hover:text-black",
1774
+ "aria-label": "Close color picker message",
1775
+ onClick: onClose,
1776
+ children: "x"
1777
+ }
1778
+ )
1779
+ ] }) : sample ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
1780
+ headerFormat ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
1781
+ "div",
1782
+ {
1783
+ 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",
1784
+ children: [
1785
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1786
+ "span",
1787
+ {
1788
+ className: "msr:size-3 msr:shrink-0 msr:rounded-full msr:border msr:border-black/15",
1789
+ style: { backgroundColor: colorToHex(sample) },
1790
+ "aria-hidden": "true"
1791
+ }
1792
+ ),
1793
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1794
+ CopyableColorValue,
1795
+ {
1796
+ id: headerFormat,
1797
+ value: formatColor(sample, headerFormat),
1798
+ copiedId,
1799
+ onCopy: () => copyValue(headerFormat, formatColor(sample, headerFormat)),
1800
+ onTooltipEnter: handleTooltipEnter,
1801
+ tooltip,
1802
+ className: "msr:font-medium msr:tabular-nums msr:text-black msr:hover:underline"
1803
+ }
1804
+ )
693
1805
  ]
694
1806
  }
695
- )
696
- ]
1807
+ ) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "msr:flex msr:items-center", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1808
+ "span",
1809
+ {
1810
+ className: "msr:size-3 msr:shrink-0 msr:rounded-full msr:border msr:border-black/15",
1811
+ style: { backgroundColor: colorToHex(sample) },
1812
+ "aria-hidden": "true"
1813
+ }
1814
+ ) }),
1815
+ secondaryFormats.map((format) => {
1816
+ const value = formatColor(sample, format);
1817
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "msr:flex msr:items-center msr:gap-2", children: [
1818
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "msr:w-9 msr:text-black/45", children: format }),
1819
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1820
+ CopyableColorValue,
1821
+ {
1822
+ id: format,
1823
+ value,
1824
+ copiedId,
1825
+ onCopy: () => copyValue(format, value),
1826
+ onTooltipEnter: handleTooltipEnter,
1827
+ tooltip,
1828
+ className: "msr:tabular-nums msr:text-black msr:hover:underline"
1829
+ }
1830
+ )
1831
+ ] }, format);
1832
+ })
1833
+ ] }) : null
697
1834
  }
698
1835
  );
699
1836
  }
700
- var Toolbar = (0, import_react.memo)((0, import_react.forwardRef)(ToolbarComponent));
701
1837
 
702
1838
  // components/rulers-overlay.tsx
703
- var import_react2 = require("react");
704
- var import_jsx_runtime3 = require("react/jsx-runtime");
1839
+ var import_react5 = require("react");
1840
+ var import_jsx_runtime6 = require("react/jsx-runtime");
705
1841
  var RULER_SIZE = 18;
706
1842
  var RULER_LENGTH = 4e3;
707
1843
  var TICK_STEP = 5;
1844
+ var RULER_EDGE_REVEAL_DISTANCE = 32;
1845
+ var RULER_FADE_MS = 100;
708
1846
  var ticks = Array.from(
709
1847
  { length: RULER_LENGTH / TICK_STEP + 1 },
710
1848
  (_, index) => index * TICK_STEP
711
1849
  );
712
- var RulersOverlay = (0, import_react2.memo)(function RulersOverlay2({
1850
+ var RulersOverlay = (0, import_react5.memo)(function RulersOverlay2({
1851
+ ownerWindow,
1852
+ settings,
713
1853
  onStartGuide,
714
1854
  onMoveGuide,
715
1855
  onFinishGuide,
@@ -717,9 +1857,26 @@ var RulersOverlay = (0, import_react2.memo)(function RulersOverlay2({
717
1857
  guides,
718
1858
  selectedGuideIds
719
1859
  }) {
720
- const [dragPosition, setDragPosition] = (0, import_react2.useState)(null);
721
- const [dragOrientation, setDragOrientation] = (0, import_react2.useState)(null);
722
- const dragRef = (0, import_react2.useRef)(null);
1860
+ const [dragPosition, setDragPosition] = (0, import_react5.useState)(null);
1861
+ const [dragOrientation, setDragOrientation] = (0, import_react5.useState)(null);
1862
+ const [nearEdge, setNearEdge] = (0, import_react5.useState)(false);
1863
+ const dragRef = (0, import_react5.useRef)(null);
1864
+ (0, import_react5.useEffect)(() => {
1865
+ if (!settings.edgeReveal) return;
1866
+ const handlePointerMove = (event) => {
1867
+ const pointer = event;
1868
+ const nearHorizontalEdge = pointer.clientX <= RULER_EDGE_REVEAL_DISTANCE || pointer.clientX >= ownerWindow.innerWidth - RULER_EDGE_REVEAL_DISTANCE;
1869
+ const nearVerticalEdge = pointer.clientY <= RULER_EDGE_REVEAL_DISTANCE || pointer.clientY >= ownerWindow.innerHeight - RULER_EDGE_REVEAL_DISTANCE;
1870
+ setNearEdge(nearHorizontalEdge || nearVerticalEdge);
1871
+ };
1872
+ ownerWindow.document.addEventListener("pointermove", handlePointerMove, true);
1873
+ ownerWindow.document.addEventListener("mousemove", handlePointerMove, true);
1874
+ return () => {
1875
+ ownerWindow.document.removeEventListener("pointermove", handlePointerMove, true);
1876
+ ownerWindow.document.removeEventListener("mousemove", handlePointerMove, true);
1877
+ };
1878
+ }, [ownerWindow, settings.edgeReveal]);
1879
+ const showRulers = !settings.edgeReveal || nearEdge;
723
1880
  const beginGuideDrag = (orientation, event) => {
724
1881
  if (event.button !== 0) return;
725
1882
  event.stopPropagation();
@@ -769,13 +1926,18 @@ var RulersOverlay = (0, import_react2.memo)(function RulersOverlay2({
769
1926
  const selectedHorizontalGuides = guides.filter(
770
1927
  (guide) => guide.orientation === "horizontal" && selectedGuideIds.includes(guide.id)
771
1928
  );
772
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
1929
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
773
1930
  "div",
774
1931
  {
775
1932
  "aria-hidden": "true",
1933
+ "data-mesurer-rulers": "true",
776
1934
  className: "msr:pointer-events-none msr:absolute msr:inset-0 msr:select-none msr:text-[9px] msr:text-[#64748b]",
1935
+ style: {
1936
+ opacity: showRulers ? settings.opacity : 0,
1937
+ transition: `opacity ${RULER_FADE_MS}ms ease`
1938
+ },
777
1939
  children: [
778
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1940
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
779
1941
  "div",
780
1942
  {
781
1943
  className: "msr:absolute msr:left-[18px] msr:right-0 msr:top-0 msr:overflow-hidden msr:bg-white",
@@ -783,13 +1945,13 @@ var RulersOverlay = (0, import_react2.memo)(function RulersOverlay2({
783
1945
  boxShadow: "0 1px 3px rgba(0, 0, 0, 0.12)",
784
1946
  height: RULER_SIZE,
785
1947
  cursor: "ns-resize",
786
- pointerEvents: "auto"
1948
+ pointerEvents: showRulers ? "auto" : "none"
787
1949
  },
788
1950
  onPointerDown: (event) => beginGuideDrag("horizontal", event),
789
1951
  onPointerMove: moveGuide,
790
1952
  onPointerUp: finishGuideDrag,
791
1953
  onPointerCancel: cancelGuideDrag,
792
- children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
1954
+ children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
793
1955
  "svg",
794
1956
  {
795
1957
  className: "msr:block msr:h-6",
@@ -797,7 +1959,7 @@ var RulersOverlay = (0, import_react2.memo)(function RulersOverlay2({
797
1959
  height: RULER_SIZE,
798
1960
  viewBox: `0 0 ${RULER_LENGTH} ${RULER_SIZE}`,
799
1961
  children: [
800
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("defs", { children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
1962
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("defs", { children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
801
1963
  "linearGradient",
802
1964
  {
803
1965
  id: "ruler-label-fade-x",
@@ -805,18 +1967,18 @@ var RulersOverlay = (0, import_react2.memo)(function RulersOverlay2({
805
1967
  x2: "100%",
806
1968
  gradientUnits: "objectBoundingBox",
807
1969
  children: [
808
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("stop", { offset: "0", stopColor: "white", stopOpacity: "0" }),
809
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("stop", { offset: "0.2", stopColor: "white" }),
810
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("stop", { offset: "0.8", stopColor: "white" }),
811
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("stop", { offset: "1", stopColor: "white", stopOpacity: "0" })
1970
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("stop", { offset: "0", stopColor: "white", stopOpacity: "0" }),
1971
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("stop", { offset: "0.2", stopColor: "white" }),
1972
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("stop", { offset: "0.8", stopColor: "white" }),
1973
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("stop", { offset: "1", stopColor: "white", stopOpacity: "0" })
812
1974
  ]
813
1975
  }
814
1976
  ) }),
815
1977
  ticks.map((x) => {
816
1978
  const major = x % 100 === 0;
817
1979
  const medium = x % 50 === 0;
818
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("g", { children: [
819
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1980
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("g", { children: [
1981
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
820
1982
  "line",
821
1983
  {
822
1984
  x1: x,
@@ -827,11 +1989,11 @@ var RulersOverlay = (0, import_react2.memo)(function RulersOverlay2({
827
1989
  strokeWidth: "1"
828
1990
  }
829
1991
  ),
830
- major ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("text", { x, y: "8", textAnchor: "middle", fill: "currentColor", children: x }) : null
1992
+ major ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("text", { x, y: "8", textAnchor: "middle", fill: "currentColor", children: x }) : null
831
1993
  ] }, x);
832
1994
  }),
833
- selectedVerticalGuides.map((guide) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("g", { children: [
834
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1995
+ selectedVerticalGuides.map((guide) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("g", { children: [
1996
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
835
1997
  "rect",
836
1998
  {
837
1999
  x: guide.position - RULER_SIZE - 8,
@@ -841,7 +2003,7 @@ var RulersOverlay = (0, import_react2.memo)(function RulersOverlay2({
841
2003
  fill: "url(#ruler-label-fade-x)"
842
2004
  }
843
2005
  ),
844
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
2006
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
845
2007
  "text",
846
2008
  {
847
2009
  x: guide.position - RULER_SIZE + 16,
@@ -853,8 +2015,8 @@ var RulersOverlay = (0, import_react2.memo)(function RulersOverlay2({
853
2015
  }
854
2016
  )
855
2017
  ] }, guide.id)),
856
- dragOrientation === "vertical" && dragPosition !== null ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("g", { children: [
857
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
2018
+ dragOrientation === "vertical" && dragPosition !== null ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("g", { children: [
2019
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
858
2020
  "rect",
859
2021
  {
860
2022
  x: dragPosition - RULER_SIZE - 8,
@@ -864,7 +2026,7 @@ var RulersOverlay = (0, import_react2.memo)(function RulersOverlay2({
864
2026
  fill: "url(#ruler-label-fade-x)"
865
2027
  }
866
2028
  ),
867
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
2029
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
868
2030
  "text",
869
2031
  {
870
2032
  x: dragPosition - RULER_SIZE + 16,
@@ -881,7 +2043,7 @@ var RulersOverlay = (0, import_react2.memo)(function RulersOverlay2({
881
2043
  )
882
2044
  }
883
2045
  ),
884
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
2046
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
885
2047
  "div",
886
2048
  {
887
2049
  className: "msr:absolute msr:bottom-0 msr:left-0 msr:top-[18px] msr:w-[18px] msr:overflow-hidden msr:bg-white",
@@ -889,13 +2051,13 @@ var RulersOverlay = (0, import_react2.memo)(function RulersOverlay2({
889
2051
  boxShadow: "1px 0 3px rgba(0, 0, 0, 0.12)",
890
2052
  width: RULER_SIZE,
891
2053
  cursor: "ew-resize",
892
- pointerEvents: "auto"
2054
+ pointerEvents: showRulers ? "auto" : "none"
893
2055
  },
894
2056
  onPointerDown: (event) => beginGuideDrag("vertical", event),
895
2057
  onPointerMove: moveGuide,
896
2058
  onPointerUp: finishGuideDrag,
897
2059
  onPointerCancel: cancelGuideDrag,
898
- children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
2060
+ children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
899
2061
  "svg",
900
2062
  {
901
2063
  className: "msr:block msr:w-6",
@@ -903,7 +2065,7 @@ var RulersOverlay = (0, import_react2.memo)(function RulersOverlay2({
903
2065
  height: RULER_LENGTH,
904
2066
  viewBox: `0 0 ${RULER_SIZE} ${RULER_LENGTH}`,
905
2067
  children: [
906
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("defs", { children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
2068
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("defs", { children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
907
2069
  "linearGradient",
908
2070
  {
909
2071
  id: "ruler-label-fade-y",
@@ -911,18 +2073,18 @@ var RulersOverlay = (0, import_react2.memo)(function RulersOverlay2({
911
2073
  y2: "100%",
912
2074
  gradientUnits: "objectBoundingBox",
913
2075
  children: [
914
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("stop", { offset: "0", stopColor: "white", stopOpacity: "0" }),
915
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("stop", { offset: "0.2", stopColor: "white" }),
916
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("stop", { offset: "0.8", stopColor: "white" }),
917
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("stop", { offset: "1", stopColor: "white", stopOpacity: "0" })
2076
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("stop", { offset: "0", stopColor: "white", stopOpacity: "0" }),
2077
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("stop", { offset: "0.2", stopColor: "white" }),
2078
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("stop", { offset: "0.8", stopColor: "white" }),
2079
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("stop", { offset: "1", stopColor: "white", stopOpacity: "0" })
918
2080
  ]
919
2081
  }
920
2082
  ) }),
921
2083
  ticks.map((y) => {
922
2084
  const major = y % 100 === 0;
923
2085
  const medium = y % 50 === 0;
924
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("g", { children: [
925
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
2086
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("g", { children: [
2087
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
926
2088
  "line",
927
2089
  {
928
2090
  x1: RULER_SIZE,
@@ -933,7 +2095,7 @@ var RulersOverlay = (0, import_react2.memo)(function RulersOverlay2({
933
2095
  strokeWidth: "1"
934
2096
  }
935
2097
  ),
936
- major ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
2098
+ major ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
937
2099
  "text",
938
2100
  {
939
2101
  x: "8",
@@ -945,8 +2107,8 @@ var RulersOverlay = (0, import_react2.memo)(function RulersOverlay2({
945
2107
  ) : null
946
2108
  ] }, y);
947
2109
  }),
948
- selectedHorizontalGuides.map((guide) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("g", { children: [
949
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
2110
+ selectedHorizontalGuides.map((guide) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("g", { children: [
2111
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
950
2112
  "rect",
951
2113
  {
952
2114
  x: "-3",
@@ -956,7 +2118,7 @@ var RulersOverlay = (0, import_react2.memo)(function RulersOverlay2({
956
2118
  fill: "url(#ruler-label-fade-y)"
957
2119
  }
958
2120
  ),
959
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
2121
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
960
2122
  "text",
961
2123
  {
962
2124
  x: "9",
@@ -969,8 +2131,8 @@ var RulersOverlay = (0, import_react2.memo)(function RulersOverlay2({
969
2131
  }
970
2132
  )
971
2133
  ] }, guide.id)),
972
- dragOrientation === "horizontal" && dragPosition !== null ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("g", { children: [
973
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
2134
+ dragOrientation === "horizontal" && dragPosition !== null ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("g", { children: [
2135
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
974
2136
  "rect",
975
2137
  {
976
2138
  x: "-3",
@@ -980,7 +2142,7 @@ var RulersOverlay = (0, import_react2.memo)(function RulersOverlay2({
980
2142
  fill: "url(#ruler-label-fade-y)"
981
2143
  }
982
2144
  ),
983
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
2145
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
984
2146
  "text",
985
2147
  {
986
2148
  x: "9",
@@ -998,34 +2160,34 @@ var RulersOverlay = (0, import_react2.memo)(function RulersOverlay2({
998
2160
  )
999
2161
  }
1000
2162
  ),
1001
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "msr:absolute msr:left-0 msr:top-0 msr:size-[18px] msr:bg-white" })
2163
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "msr:absolute msr:left-0 msr:top-0 msr:size-[18px] msr:bg-white" })
1002
2164
  ]
1003
2165
  }
1004
2166
  );
1005
2167
  });
1006
2168
 
1007
2169
  // hooks/use-drag-state.ts
1008
- var import_react3 = require("react");
2170
+ var import_react6 = require("react");
1009
2171
  var useDragState = () => {
1010
- const [start, setStart] = (0, import_react3.useState)(null);
1011
- const [end, setEnd] = (0, import_react3.useState)(null);
1012
- const [isDragging, setIsDragging] = (0, import_react3.useState)(false);
2172
+ const [start, setStart] = (0, import_react6.useState)(null);
2173
+ const [end, setEnd] = (0, import_react6.useState)(null);
2174
+ const [isDragging, setIsDragging] = (0, import_react6.useState)(false);
1013
2175
  return { start, setStart, end, setEnd, isDragging, setIsDragging };
1014
2176
  };
1015
2177
 
1016
2178
  // hooks/use-guide-drag-hold.ts
1017
- var import_react4 = require("react");
2179
+ var import_react7 = require("react");
1018
2180
  var useGuideDragHold = (eventTarget) => {
1019
- const guideDragHoldTimerRef = (0, import_react4.useRef)(null);
1020
- const guideDragHoldGuideIdRef = (0, import_react4.useRef)(null);
1021
- const clearGuideDragHold = (0, import_react4.useCallback)(() => {
2181
+ const guideDragHoldTimerRef = (0, import_react7.useRef)(null);
2182
+ const guideDragHoldGuideIdRef = (0, import_react7.useRef)(null);
2183
+ const clearGuideDragHold = (0, import_react7.useCallback)(() => {
1022
2184
  if (guideDragHoldTimerRef.current !== null) {
1023
2185
  eventTarget.clearTimeout(guideDragHoldTimerRef.current);
1024
2186
  guideDragHoldTimerRef.current = null;
1025
2187
  }
1026
2188
  guideDragHoldGuideIdRef.current = null;
1027
2189
  }, []);
1028
- const scheduleGuideDragHold = (0, import_react4.useCallback)(
2190
+ const scheduleGuideDragHold = (0, import_react7.useCallback)(
1029
2191
  (id, onHold) => {
1030
2192
  clearGuideDragHold();
1031
2193
  guideDragHoldGuideIdRef.current = id;
@@ -1044,11 +2206,11 @@ var useGuideDragHold = (eventTarget) => {
1044
2206
  };
1045
2207
 
1046
2208
  // hooks/use-guide-state.ts
1047
- var import_react5 = require("react");
2209
+ var import_react8 = require("react");
1048
2210
  var useGuideState = (options = {}) => {
1049
- const [guides, setGuides] = (0, import_react5.useState)(options.initialGuides ?? []);
1050
- const [draggingGuideId, setDraggingGuideId] = (0, import_react5.useState)(null);
1051
- const [selectedGuideIds, setSelectedGuideIds] = (0, import_react5.useState)(
2211
+ const [guides, setGuides] = (0, import_react8.useState)(options.initialGuides ?? []);
2212
+ const [draggingGuideId, setDraggingGuideId] = (0, import_react8.useState)(null);
2213
+ const [selectedGuideIds, setSelectedGuideIds] = (0, import_react8.useState)(
1052
2214
  options.initialSelectedGuideIds ?? []
1053
2215
  );
1054
2216
  return {
@@ -1062,67 +2224,91 @@ var useGuideState = (options = {}) => {
1062
2224
  };
1063
2225
 
1064
2226
  // hooks/use-hotkeys.ts
1065
- var import_react6 = require("react");
2227
+ var import_react9 = require("react");
1066
2228
  var useHotkeys = (options) => {
1067
- (0, import_react6.useEffect)(() => {
2229
+ const optionsRef = (0, import_react9.useRef)(options);
2230
+ optionsRef.current = options;
2231
+ (0, import_react9.useEffect)(() => {
1068
2232
  const target = options.eventTarget;
1069
2233
  const handleKeyDown = (event) => {
2234
+ const current = optionsRef.current;
1070
2235
  if (event.key === "Escape") {
1071
- options.clearAll();
2236
+ if (current.isSettingsOpen()) {
2237
+ current.onToggleSettings();
2238
+ return;
2239
+ }
2240
+ if (current.isColorPickerActive()) {
2241
+ current.onCloseColorPicker();
2242
+ return;
2243
+ }
2244
+ current.clearAll();
1072
2245
  return;
1073
2246
  }
1074
2247
  if (event.metaKey || event.ctrlKey) {
2248
+ if (event.key === ",") {
2249
+ event.preventDefault();
2250
+ current.onToggleSettings();
2251
+ current.onInteract();
2252
+ return;
2253
+ }
1075
2254
  if (event.key.toLowerCase() !== "z") return;
1076
2255
  if (event.shiftKey) {
1077
2256
  event.preventDefault();
1078
- options.redo();
2257
+ current.redo();
1079
2258
  return;
1080
2259
  }
1081
2260
  event.preventDefault();
1082
- options.undo();
2261
+ current.undo();
1083
2262
  return;
1084
2263
  }
1085
2264
  if (event.key.toLowerCase() === "m") {
1086
- options.setEnabled((prev) => !prev);
2265
+ current.setEnabled((prev) => !prev);
1087
2266
  }
1088
2267
  const key = event.key.toLowerCase();
1089
- if (options.isOverlayActive()) {
2268
+ if (key === "p") {
2269
+ event.preventDefault();
2270
+ current.onColorPicker();
2271
+ current.onInteract();
2272
+ return;
2273
+ }
2274
+ if (key === "x") {
2275
+ current.onToggleXray();
2276
+ current.onInteract();
2277
+ return;
2278
+ }
2279
+ if (current.isOverlayActive()) {
1090
2280
  if (key === "a") {
1091
- options.setToolMode(
2281
+ current.setToolMode(
1092
2282
  (prev) => prev === "text-inspector" ? "none" : "text-inspector"
1093
2283
  );
1094
- options.onInteract();
2284
+ current.onInteract();
1095
2285
  }
1096
2286
  if (key === "s") {
1097
- options.setToolMode((prev) => prev === "select" ? "none" : "select");
1098
- options.onInteract();
2287
+ current.setToolMode((prev) => prev === "select" ? "none" : "select");
2288
+ current.onInteract();
1099
2289
  }
1100
2290
  if (key === "g") {
1101
- options.setToolMode((prev) => prev === "guides" ? "none" : "guides");
1102
- options.onInteract();
1103
- }
1104
- if (key === "x") {
1105
- options.setToolMode((prev) => prev === "xray" ? "none" : "xray");
1106
- options.onInteract();
2291
+ current.setToolMode((prev) => prev === "guides" ? "none" : "guides");
2292
+ current.onInteract();
1107
2293
  }
1108
2294
  if (key === "r") {
1109
- options.setRulersVisible((prev) => !prev);
1110
- options.onInteract();
2295
+ current.setRulersVisible((prev) => !prev);
2296
+ current.onInteract();
1111
2297
  }
1112
2298
  if (key === "h") {
1113
- options.setGuideOrientation("horizontal");
1114
- options.onInteract();
2299
+ current.setGuideOrientation("horizontal");
2300
+ current.onInteract();
1115
2301
  }
1116
2302
  if (key === "v") {
1117
- options.setGuideOrientation("vertical");
1118
- options.onInteract();
2303
+ current.setGuideOrientation("vertical");
2304
+ current.onInteract();
1119
2305
  }
1120
2306
  }
1121
2307
  if (event.key === "Alt") {
1122
- options.setAltPressed(true);
2308
+ current.setAltPressed(true);
1123
2309
  }
1124
2310
  if (event.key === "Backspace" || event.key === "Delete") {
1125
- const removed = options.removeSelectedGuides();
2311
+ const removed = current.removeSelectedGuides();
1126
2312
  if (removed) {
1127
2313
  event.preventDefault();
1128
2314
  }
@@ -1130,7 +2316,7 @@ var useHotkeys = (options) => {
1130
2316
  };
1131
2317
  const handleKeyUp = (event) => {
1132
2318
  if (event.key === "Alt") {
1133
- options.setAltPressed(false);
2319
+ optionsRef.current.setAltPressed(false);
1134
2320
  }
1135
2321
  };
1136
2322
  target.addEventListener("keydown", handleKeyDown);
@@ -1139,11 +2325,11 @@ var useHotkeys = (options) => {
1139
2325
  target.removeEventListener("keydown", handleKeyDown);
1140
2326
  target.removeEventListener("keyup", handleKeyUp);
1141
2327
  };
1142
- }, [options]);
2328
+ }, [options.eventTarget]);
1143
2329
  };
1144
2330
 
1145
2331
  // hooks/use-live-element-tracking.ts
1146
- var import_react7 = require("react");
2332
+ var import_react10 = require("react");
1147
2333
 
1148
2334
  // core/geometry.ts
1149
2335
  var getViewportSize = (ownerWindow) => ({
@@ -1169,7 +2355,7 @@ var getRectFromPoints = (start, end) => {
1169
2355
  const height = Math.abs(start.y - end.y);
1170
2356
  return { left, top, width, height };
1171
2357
  };
1172
- var clamp = (value, min, max) => Math.max(min, Math.min(max, value));
2358
+ var clamp2 = (value, min, max) => Math.max(min, Math.min(max, value));
1173
2359
  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;
1174
2360
  var rectArea = (rect) => rect.width * rect.height;
1175
2361
  var intersectionArea = (rectA, rectB) => {
@@ -1233,10 +2419,10 @@ var getDistanceOverlay = (rectA, rectB, elementRefA, elementRefB, ownerWindow =
1233
2419
  }
1234
2420
  }
1235
2421
  const normalizedConnectors = connectors.map((segment) => ({
1236
- x1: clamp(segment.x1, 0, ownerWindow.innerWidth),
1237
- y1: clamp(segment.y1, 0, ownerWindow.innerHeight),
1238
- x2: clamp(segment.x2, 0, ownerWindow.innerWidth),
1239
- y2: clamp(segment.y2, 0, ownerWindow.innerHeight)
2422
+ x1: clamp2(segment.x1, 0, ownerWindow.innerWidth),
2423
+ y1: clamp2(segment.y1, 0, ownerWindow.innerHeight),
2424
+ x2: clamp2(segment.x2, 0, ownerWindow.innerWidth),
2425
+ y2: clamp2(segment.y2, 0, ownerWindow.innerHeight)
1240
2426
  })).filter(
1241
2427
  (segment) => Math.abs(segment.x1 - segment.x2) > 0.5 || Math.abs(segment.y1 - segment.y2) > 0.5
1242
2428
  );
@@ -1403,19 +2589,23 @@ var updateMeasurementForResize = (measurement, viewport = getViewportSize(), own
1403
2589
 
1404
2590
  // hooks/use-live-element-tracking.ts
1405
2591
  var useLiveElementTracking = (params) => {
1406
- const frameRef = (0, import_react7.useRef)(null);
1407
- (0, import_react7.useEffect)(() => {
2592
+ const paramsRef = (0, import_react10.useRef)(params);
2593
+ paramsRef.current = params;
2594
+ const frameRef = (0, import_react10.useRef)(null);
2595
+ (0, import_react10.useEffect)(() => {
2596
+ const ownerWindow = params.window;
1408
2597
  if (!params.enabled) {
1409
2598
  if (frameRef.current) {
1410
- params.window.cancelAnimationFrame(frameRef.current);
2599
+ ownerWindow.cancelAnimationFrame(frameRef.current);
1411
2600
  }
1412
2601
  frameRef.current = null;
1413
2602
  return;
1414
2603
  }
1415
2604
  const tick = () => {
1416
- params.setMeasurements(
2605
+ const current = paramsRef.current;
2606
+ current.setMeasurements(
1417
2607
  (prev) => prev.map((measurement) => {
1418
- if (!measurement.elementRef || !params.document.contains(measurement.elementRef)) {
2608
+ if (!measurement.elementRef || !current.document.contains(measurement.elementRef)) {
1419
2609
  return measurement;
1420
2610
  }
1421
2611
  const rect = getRectFromDom(measurement.elementRef);
@@ -1428,8 +2618,8 @@ var useLiveElementTracking = (params) => {
1428
2618
  };
1429
2619
  })
1430
2620
  );
1431
- params.setActiveMeasurement((prev) => {
1432
- if (!prev?.elementRef || !params.document.contains(prev.elementRef))
2621
+ current.setActiveMeasurement((prev) => {
2622
+ if (!prev?.elementRef || !current.document.contains(prev.elementRef))
1433
2623
  return prev;
1434
2624
  const rect = getRectFromDom(prev.elementRef);
1435
2625
  if (rectAlmostEqual(rect, prev.rect)) return prev;
@@ -1440,10 +2630,10 @@ var useLiveElementTracking = (params) => {
1440
2630
  originRect: void 0
1441
2631
  };
1442
2632
  });
1443
- params.setHeldDistances(
2633
+ current.setHeldDistances(
1444
2634
  (prev) => prev.map((distance) => {
1445
- const canTrackA = distance.elementRefA && params.document.contains(distance.elementRefA);
1446
- const canTrackB = distance.elementRefB && params.document.contains(distance.elementRefB);
2635
+ const canTrackA = distance.elementRefA && current.document.contains(distance.elementRefA);
2636
+ const canTrackB = distance.elementRefB && current.document.contains(distance.elementRefB);
1447
2637
  if (!canTrackA && !canTrackB) return distance;
1448
2638
  const rectA = canTrackA ? getRectFromDom(distance.elementRefA) : distance.rectA;
1449
2639
  const rectB = canTrackB ? getRectFromDom(distance.elementRefB) : distance.rectB;
@@ -1455,7 +2645,7 @@ var useLiveElementTracking = (params) => {
1455
2645
  rectB,
1456
2646
  distance.elementRefA,
1457
2647
  distance.elementRefB,
1458
- params.window
2648
+ ownerWindow
1459
2649
  );
1460
2650
  return {
1461
2651
  ...updated,
@@ -1463,62 +2653,71 @@ var useLiveElementTracking = (params) => {
1463
2653
  };
1464
2654
  })
1465
2655
  );
1466
- const selected = params.selectedElementRef.current;
1467
- if (selected && params.document.contains(selected)) {
1468
- params.setSelectedMeasurement((prev) => {
1469
- const next = getInspectMeasurement(selected, params.window);
2656
+ const selected = current.selectedElementRef.current;
2657
+ if (current.selectionEnabled && selected && current.document.contains(selected)) {
2658
+ current.setSelectedMeasurement((prev) => {
2659
+ const next = getInspectMeasurement(selected, ownerWindow);
1470
2660
  if (prev && rectAlmostEqual(prev.rect, next.rect)) return prev;
1471
2661
  return next;
1472
2662
  });
1473
2663
  }
1474
- params.setSelectedMeasurements(
1475
- (prev) => prev.map((measurement) => {
1476
- if (!measurement.elementRef || !params.document.contains(measurement.elementRef)) {
1477
- return measurement;
1478
- }
1479
- const next = getInspectMeasurement(measurement.elementRef, params.window);
1480
- if (rectAlmostEqual(next.rect, measurement.rect)) return measurement;
1481
- return {
1482
- ...next,
1483
- id: measurement.id
1484
- };
1485
- })
1486
- );
1487
- const hover = params.hoverElementRef.current;
1488
- if (hover && params.document.contains(hover)) {
2664
+ if (current.selectionEnabled) {
2665
+ current.setSelectedMeasurements(
2666
+ (prev) => prev.map((measurement) => {
2667
+ if (!measurement.elementRef || !current.document.contains(measurement.elementRef)) {
2668
+ return measurement;
2669
+ }
2670
+ const next = getInspectMeasurement(measurement.elementRef, ownerWindow);
2671
+ if (rectAlmostEqual(next.rect, measurement.rect)) return measurement;
2672
+ return {
2673
+ ...next,
2674
+ id: measurement.id
2675
+ };
2676
+ })
2677
+ );
2678
+ }
2679
+ const hover = current.hoverElementRef.current;
2680
+ if (current.selectionEnabled && hover && current.document.contains(hover)) {
1489
2681
  const rect = getRectFromDom(hover);
1490
- params.setHoverRect(
2682
+ current.setHoverRect(
1491
2683
  (prev) => prev && rectAlmostEqual(prev, rect) ? prev : rect
1492
2684
  );
1493
2685
  }
1494
- frameRef.current = params.window.requestAnimationFrame(tick);
2686
+ frameRef.current = ownerWindow.requestAnimationFrame(tick);
1495
2687
  };
1496
- frameRef.current = params.window.requestAnimationFrame(tick);
2688
+ frameRef.current = ownerWindow.requestAnimationFrame(tick);
1497
2689
  return () => {
1498
2690
  if (frameRef.current) {
1499
- params.window.cancelAnimationFrame(frameRef.current);
2691
+ ownerWindow.cancelAnimationFrame(frameRef.current);
1500
2692
  }
1501
2693
  frameRef.current = null;
1502
2694
  };
1503
- }, [params]);
2695
+ }, [params.enabled, params.window]);
1504
2696
  };
1505
2697
 
1506
2698
  // hooks/use-measure-toggles.ts
1507
- var import_react8 = require("react");
2699
+ var import_react11 = require("react");
1508
2700
  var useMeasureToggles = (options = {}) => {
1509
- const [enabled, setEnabled] = (0, import_react8.useState)(options.initialEnabled ?? true);
1510
- const [altPressed, setAltPressed] = (0, import_react8.useState)(false);
1511
- const [toolMode, setToolMode] = (0, import_react8.useState)(
2701
+ const [enabled, setEnabled] = (0, import_react11.useState)(options.initialEnabled ?? true);
2702
+ const [altPressed, setAltPressed] = (0, import_react11.useState)(false);
2703
+ const [toolMode, setToolMode] = (0, import_react11.useState)(
1512
2704
  options.initialToolMode ?? "none"
1513
2705
  );
1514
- const [rulersVisible, setRulersVisible] = (0, import_react8.useState)(
2706
+ const [rulersVisible, setRulersVisible] = (0, import_react11.useState)(
1515
2707
  options.initialRulersVisible ?? false
1516
2708
  );
1517
2709
  const holdEnabled = false;
1518
- const multiMeasureEnabled = false;
1519
- const snapGuidesEnabled = true;
2710
+ const [multiMeasureEnabled, setMultiMeasureEnabled] = (0, import_react11.useState)(
2711
+ options.initialMultiMeasureEnabled ?? false
2712
+ );
2713
+ const [snapGuidesEnabled, setSnapGuidesEnabled] = (0, import_react11.useState)(
2714
+ options.initialSnapGuidesEnabled ?? true
2715
+ );
2716
+ const [selectNewGuideEnabled, setSelectNewGuideEnabled] = (0, import_react11.useState)(
2717
+ options.initialSelectNewGuideEnabled ?? true
2718
+ );
1520
2719
  const guidesEnabled = toolMode === "guides";
1521
- const snapEnabled = true;
2720
+ const [snapEnabled, setSnapEnabled] = (0, import_react11.useState)(options.initialSnapEnabled ?? true);
1522
2721
  return {
1523
2722
  enabled,
1524
2723
  setEnabled,
@@ -1532,21 +2731,26 @@ var useMeasureToggles = (options = {}) => {
1532
2731
  guidesEnabled,
1533
2732
  multiMeasureEnabled,
1534
2733
  snapGuidesEnabled,
1535
- snapEnabled
2734
+ setSnapGuidesEnabled,
2735
+ selectNewGuideEnabled,
2736
+ setSelectNewGuideEnabled,
2737
+ snapEnabled,
2738
+ setSnapEnabled,
2739
+ setMultiMeasureEnabled
1536
2740
  };
1537
2741
  };
1538
2742
 
1539
2743
  // hooks/use-measurement-state.ts
1540
- var import_react9 = require("react");
2744
+ var import_react12 = require("react");
1541
2745
  var useMeasurementState = (options = {}) => {
1542
- const [activeMeasurement, setActiveMeasurement] = (0, import_react9.useState)(options.initialActiveMeasurement ?? null);
1543
- const [measurements, setMeasurements] = (0, import_react9.useState)(
2746
+ const [activeMeasurement, setActiveMeasurement] = (0, import_react12.useState)(options.initialActiveMeasurement ?? null);
2747
+ const [measurements, setMeasurements] = (0, import_react12.useState)(
1544
2748
  options.initialMeasurements ?? []
1545
2749
  );
1546
- const [selectedMeasurement, setSelectedMeasurement] = (0, import_react9.useState)(null);
1547
- const [selectedMeasurements, setSelectedMeasurements] = (0, import_react9.useState)([]);
1548
- const [hoverRect, setHoverRect] = (0, import_react9.useState)(null);
1549
- const [heldDistances, setHeldDistances] = (0, import_react9.useState)(
2750
+ const [selectedMeasurement, setSelectedMeasurement] = (0, import_react12.useState)(null);
2751
+ const [selectedMeasurements, setSelectedMeasurements] = (0, import_react12.useState)([]);
2752
+ const [hoverRect, setHoverRect] = (0, import_react12.useState)(null);
2753
+ const [heldDistances, setHeldDistances] = (0, import_react12.useState)(
1550
2754
  options.initialHeldDistances ?? []
1551
2755
  );
1552
2756
  return {
@@ -1566,7 +2770,7 @@ var useMeasurementState = (options = {}) => {
1566
2770
  };
1567
2771
 
1568
2772
  // hooks/use-measurer-derived.ts
1569
- var import_react10 = require("react");
2773
+ var import_react13 = require("react");
1570
2774
 
1571
2775
  // core/edge-visibility.ts
1572
2776
  var rangesOverlap = (a1, a2, b1, b2) => Math.min(a2, b2) - Math.max(a1, b1) > 0;
@@ -1835,7 +3039,7 @@ var useMeasurerDerived = ({
1835
3039
  highlightColor,
1836
3040
  guideColor
1837
3041
  }) => {
1838
- const activeRect = (0, import_react10.useMemo)(() => {
3042
+ const activeRect = (0, import_react13.useMemo)(() => {
1839
3043
  if (!start || !end) return null;
1840
3044
  return {
1841
3045
  left: Math.min(start.x, end.x),
@@ -1846,7 +3050,7 @@ var useMeasurerDerived = ({
1846
3050
  }, [end, start]);
1847
3051
  const activeWidth = activeRect ? formatValue(activeRect.width) : 0;
1848
3052
  const activeHeight = activeRect ? formatValue(activeRect.height) : 0;
1849
- const groupedSelection = (0, import_react10.useMemo)(() => {
3053
+ const groupedSelection = (0, import_react13.useMemo)(() => {
1850
3054
  if (selectedMeasurements.length <= 1) return null;
1851
3055
  const selectionKey = selectedMeasurements.map((measurement) => measurement.id).join("|");
1852
3056
  const rects = selectedMeasurements.map((measurement) => measurement.rect);
@@ -1876,24 +3080,24 @@ var useMeasurerDerived = ({
1876
3080
  originRect: selectionOriginRect ?? void 0
1877
3081
  };
1878
3082
  }, [selectedMeasurements, selectionOriginRect]);
1879
- const displayedSelectedMeasurements = (0, import_react10.useMemo)(
3083
+ const displayedSelectedMeasurements = (0, import_react13.useMemo)(
1880
3084
  () => groupedSelection ? [groupedSelection] : selectedMeasurements.length > 0 ? selectedMeasurements : selectedMeasurement ? [selectedMeasurement] : [],
1881
3085
  [groupedSelection, selectedMeasurement, selectedMeasurements]
1882
3086
  );
1883
- const selectedGuide = (0, import_react10.useMemo)(
3087
+ const selectedGuide = (0, import_react13.useMemo)(
1884
3088
  () => getSelectedGuide(guides, selectedGuideIds),
1885
3089
  [guides, selectedGuideIds]
1886
3090
  );
1887
- const hoverGuide = (0, import_react10.useMemo)(
3091
+ const hoverGuide = (0, import_react13.useMemo)(
1888
3092
  () => getHoveredGuide(hoverPointer, guides),
1889
3093
  [guides, hoverPointer]
1890
3094
  );
1891
- const primarySelectedMeasurement = (0, import_react10.useMemo)(
3095
+ const primarySelectedMeasurement = (0, import_react13.useMemo)(
1892
3096
  () => getPrimarySelectedMeasurement(selectedMeasurements, selectedMeasurement),
1893
3097
  [selectedMeasurement, selectedMeasurements]
1894
3098
  );
1895
3099
  const effectivePrimarySelected = groupedSelection ?? primarySelectedMeasurement;
1896
- const optionPairOverlay = (0, import_react10.useMemo)(() => {
3100
+ const optionPairOverlay = (0, import_react13.useMemo)(() => {
1897
3101
  return getOptionPairOverlay({
1898
3102
  altPressed,
1899
3103
  primarySelectedMeasurement: effectivePrimarySelected,
@@ -1912,7 +3116,7 @@ var useMeasurerDerived = ({
1912
3116
  selectedElement,
1913
3117
  selectedGuide
1914
3118
  ]);
1915
- const optionContainerLines = (0, import_react10.useMemo)(() => {
3119
+ const optionContainerLines = (0, import_react13.useMemo)(() => {
1916
3120
  return getOptionContainerLines({
1917
3121
  altPressed,
1918
3122
  primarySelectedMeasurement: effectivePrimarySelected,
@@ -1931,7 +3135,7 @@ var useMeasurerDerived = ({
1931
3135
  selectedElement,
1932
3136
  selectedGuideIds
1933
3137
  ]);
1934
- const guideDistanceOverlay = (0, import_react10.useMemo)(() => {
3138
+ const guideDistanceOverlay = (0, import_react13.useMemo)(() => {
1935
3139
  if (!altPressed || !guidesEnabled) return null;
1936
3140
  if (selectedGuide && hoverGuide && selectedGuide.id !== hoverGuide.id) {
1937
3141
  return getDistanceOverlay(
@@ -1983,7 +3187,7 @@ var useMeasurerDerived = ({
1983
3187
  guideColorHover,
1984
3188
  guideColorDefault,
1985
3189
  guideColorPreview
1986
- } = (0, import_react10.useMemo)(
3190
+ } = (0, import_react13.useMemo)(
1987
3191
  () => ({
1988
3192
  outlineColor: `color-mix(in oklch, ${highlightColor} 80%, transparent)`,
1989
3193
  fillColor: `color-mix(in oklch, ${highlightColor} 8%, transparent)`,
@@ -1994,12 +3198,12 @@ var useMeasurerDerived = ({
1994
3198
  }),
1995
3199
  [guideColor, highlightColor]
1996
3200
  );
1997
- const selectedRects = (0, import_react10.useMemo)(
3201
+ const selectedRects = (0, import_react13.useMemo)(
1998
3202
  () => displayedSelectedMeasurements.map((measurement) => measurement.rect),
1999
3203
  [displayedSelectedMeasurements]
2000
3204
  );
2001
3205
  const hoverRectToShow = hoverHighlightEnabled && hoverRect && selectedMeasurements.length <= 1 ? hoverRect : null;
2002
- const selectionEdgeVisibility = (0, import_react10.useMemo)(
3206
+ const selectionEdgeVisibility = (0, import_react13.useMemo)(
2003
3207
  () => getEdgeVisibilityForRects(selectedRects),
2004
3208
  [selectedRects]
2005
3209
  );
@@ -2008,7 +3212,7 @@ var useMeasurerDerived = ({
2008
3212
  selectedRects.length
2009
3213
  );
2010
3214
  const hoverEdgeVisibility = hoverRectToShow ? { top: true, right: true, bottom: true, left: true } : null;
2011
- const measurementEdgeVisibility = (0, import_react10.useMemo)(
3215
+ const measurementEdgeVisibility = (0, import_react13.useMemo)(
2012
3216
  () => getEdgeVisibilityForRects(displayedMeasurements.map((m) => m.rect)),
2013
3217
  [displayedMeasurements]
2014
3218
  );
@@ -2036,7 +3240,7 @@ var useMeasurerDerived = ({
2036
3240
  };
2037
3241
 
2038
3242
  // hooks/use-measurer-history.ts
2039
- var import_react11 = require("react");
3243
+ var import_react14 = require("react");
2040
3244
  var HISTORY_LIMIT = 50;
2041
3245
  var useMeasurerHistory = ({
2042
3246
  toggles,
@@ -2082,10 +3286,10 @@ var useMeasurerHistory = ({
2082
3286
  setSelectedElement,
2083
3287
  clearSelectionRect
2084
3288
  } = transient;
2085
- const historyRef = (0, import_react11.useRef)([]);
2086
- const futureRef = (0, import_react11.useRef)([]);
2087
- const historySignatureRef = (0, import_react11.useRef)(null);
2088
- const captureSnapshot = (0, import_react11.useCallback)(() => {
3289
+ const historyRef = (0, import_react14.useRef)([]);
3290
+ const futureRef = (0, import_react14.useRef)([]);
3291
+ const historySignatureRef = (0, import_react14.useRef)(null);
3292
+ const captureSnapshot = (0, import_react14.useCallback)(() => {
2089
3293
  return {
2090
3294
  enabled,
2091
3295
  toolMode,
@@ -2112,7 +3316,7 @@ var useMeasurerHistory = ({
2112
3316
  selectedMeasurements,
2113
3317
  toolMode
2114
3318
  ]);
2115
- const getSnapshotSignature = (0, import_react11.useCallback)((snapshot) => {
3319
+ const getSnapshotSignature = (0, import_react14.useCallback)((snapshot) => {
2116
3320
  const serializeRect = (rect) => `${Math.round(rect.left)}:${Math.round(rect.top)}:${Math.round(rect.width)}:${Math.round(rect.height)}`;
2117
3321
  return [
2118
3322
  snapshot.enabled ? "1" : "0",
@@ -2128,7 +3332,7 @@ var useMeasurerHistory = ({
2128
3332
  snapshot.draggingGuideId ?? ""
2129
3333
  ].join("|");
2130
3334
  }, []);
2131
- const recordSnapshot = (0, import_react11.useCallback)(() => {
3335
+ const recordSnapshot = (0, import_react14.useCallback)(() => {
2132
3336
  const snapshot = captureSnapshot();
2133
3337
  const signature = getSnapshotSignature(snapshot);
2134
3338
  if (historySignatureRef.current === signature) return;
@@ -2139,7 +3343,7 @@ var useMeasurerHistory = ({
2139
3343
  historyRef.current.shift();
2140
3344
  }
2141
3345
  }, [captureSnapshot, getSnapshotSignature]);
2142
- const restoreSnapshot = (0, import_react11.useCallback)(
3346
+ const restoreSnapshot = (0, import_react14.useCallback)(
2143
3347
  (snapshot) => {
2144
3348
  setEnabled(snapshot.enabled);
2145
3349
  setToolMode(snapshot.toolMode);
@@ -2184,7 +3388,7 @@ var useMeasurerHistory = ({
2184
3388
  setHoverElement
2185
3389
  ]
2186
3390
  );
2187
- const undo = (0, import_react11.useCallback)(() => {
3391
+ const undo = (0, import_react14.useCallback)(() => {
2188
3392
  const previousSnapshot = historyRef.current.pop();
2189
3393
  if (!previousSnapshot) return;
2190
3394
  const currentSnapshot = captureSnapshot();
@@ -2195,7 +3399,7 @@ var useMeasurerHistory = ({
2195
3399
  historySignatureRef.current = null;
2196
3400
  restoreSnapshot(previousSnapshot);
2197
3401
  }, [captureSnapshot, restoreSnapshot]);
2198
- const redo = (0, import_react11.useCallback)(() => {
3402
+ const redo = (0, import_react14.useCallback)(() => {
2199
3403
  const nextSnapshot = futureRef.current.pop();
2200
3404
  if (!nextSnapshot) return;
2201
3405
  historyRef.current.push(captureSnapshot());
@@ -2205,7 +3409,7 @@ var useMeasurerHistory = ({
2205
3409
  historySignatureRef.current = null;
2206
3410
  restoreSnapshot(nextSnapshot);
2207
3411
  }, [captureSnapshot, restoreSnapshot]);
2208
- const setToolModeWithHistory = (0, import_react11.useCallback)(
3412
+ const setToolModeWithHistory = (0, import_react14.useCallback)(
2209
3413
  (value) => {
2210
3414
  setToolMode((prev) => {
2211
3415
  const next = typeof value === "function" ? value(prev) : value;
@@ -2216,7 +3420,7 @@ var useMeasurerHistory = ({
2216
3420
  },
2217
3421
  [recordSnapshot, setToolMode]
2218
3422
  );
2219
- const setGuideOrientationWithHistory = (0, import_react11.useCallback)(
3423
+ const setGuideOrientationWithHistory = (0, import_react14.useCallback)(
2220
3424
  (value) => {
2221
3425
  setGuideOrientation((prev) => {
2222
3426
  const next = typeof value === "function" ? value(prev) : value;
@@ -2227,7 +3431,7 @@ var useMeasurerHistory = ({
2227
3431
  },
2228
3432
  [recordSnapshot, setGuideOrientation]
2229
3433
  );
2230
- const setEnabledWithHistory = (0, import_react11.useCallback)(
3434
+ const setEnabledWithHistory = (0, import_react14.useCallback)(
2231
3435
  (value) => {
2232
3436
  setEnabled((prev) => {
2233
3437
  const next = typeof value === "function" ? value(prev) : value;
@@ -2238,7 +3442,7 @@ var useMeasurerHistory = ({
2238
3442
  },
2239
3443
  [recordSnapshot, setEnabled]
2240
3444
  );
2241
- const createActionCommit = (0, import_react11.useCallback)(() => {
3445
+ const createActionCommit = (0, import_react14.useCallback)(() => {
2242
3446
  let committed = false;
2243
3447
  return () => {
2244
3448
  if (committed) return;
@@ -2258,35 +3462,35 @@ var useMeasurerHistory = ({
2258
3462
  };
2259
3463
 
2260
3464
  // hooks/use-measurer-local-state.ts
2261
- var import_react12 = require("react");
3465
+ var import_react15 = require("react");
2262
3466
  var useMeasurerLocalState = ({
2263
3467
  selectedElementRef,
2264
3468
  hoverElementRef,
2265
3469
  selectionRectRef
2266
3470
  }) => {
2267
- const [selectionOriginRect, setSelectionOriginRect] = (0, import_react12.useState)(
3471
+ const [selectionOriginRect, setSelectionOriginRect] = (0, import_react15.useState)(
2268
3472
  null
2269
3473
  );
2270
- const [hoverPointer, setHoverPointer] = (0, import_react12.useState)(null);
2271
- const [hoverElement, setHoverElementState] = (0, import_react12.useState)(
3474
+ const [hoverPointer, setHoverPointer] = (0, import_react15.useState)(null);
3475
+ const [hoverElement, setHoverElementState] = (0, import_react15.useState)(
2272
3476
  null
2273
3477
  );
2274
- const [selectedElement, setSelectedElementState] = (0, import_react12.useState)(null);
2275
- const setSelectedElement = (0, import_react12.useCallback)(
3478
+ const [selectedElement, setSelectedElementState] = (0, import_react15.useState)(null);
3479
+ const setSelectedElement = (0, import_react15.useCallback)(
2276
3480
  (element) => {
2277
3481
  selectedElementRef.current = element;
2278
3482
  setSelectedElementState(element);
2279
3483
  },
2280
3484
  [selectedElementRef]
2281
3485
  );
2282
- const setHoverElement = (0, import_react12.useCallback)(
3486
+ const setHoverElement = (0, import_react15.useCallback)(
2283
3487
  (element) => {
2284
3488
  hoverElementRef.current = element;
2285
3489
  setHoverElementState(element);
2286
3490
  },
2287
3491
  [hoverElementRef]
2288
3492
  );
2289
- const clearSelectionRect = (0, import_react12.useCallback)(() => {
3493
+ const clearSelectionRect = (0, import_react15.useCallback)(() => {
2290
3494
  selectionRectRef.current = null;
2291
3495
  setSelectionOriginRect(null);
2292
3496
  }, [selectionRectRef]);
@@ -2304,7 +3508,7 @@ var useMeasurerLocalState = ({
2304
3508
  };
2305
3509
 
2306
3510
  // hooks/use-measurer-pointer.ts
2307
- var import_react13 = require("react");
3511
+ var import_react16 = require("react");
2308
3512
 
2309
3513
  // core/targets.ts
2310
3514
  var pickMultiTargets = (selectionRect, items) => {
@@ -2507,10 +3711,12 @@ var useMeasurerPointer = ({
2507
3711
  clearGuideDragHold,
2508
3712
  scheduleGuideDragHold,
2509
3713
  enabled,
3714
+ settingsOpen,
2510
3715
  toolMode,
2511
3716
  guidesEnabled,
2512
3717
  snapEnabled,
2513
3718
  snapGuidesEnabled,
3719
+ selectNewGuideEnabled,
2514
3720
  altPressed,
2515
3721
  guideOrientation,
2516
3722
  hoverHighlightEnabled,
@@ -2543,24 +3749,24 @@ var useMeasurerPointer = ({
2543
3749
  setHoverPointer,
2544
3750
  clearSelectionRect
2545
3751
  }) => {
2546
- const hoverFrameRef = (0, import_react13.useRef)(null);
2547
- const hoverPointRef = (0, import_react13.useRef)(null);
2548
- const selectionCacheRef = (0, import_react13.useRef)({
3752
+ const hoverFrameRef = (0, import_react16.useRef)(null);
3753
+ const hoverPointRef = (0, import_react16.useRef)(null);
3754
+ const selectionCacheRef = (0, import_react16.useRef)({
2549
3755
  key: "",
2550
3756
  entries: [],
2551
3757
  overlayNode: null,
2552
3758
  frame: -1
2553
3759
  });
2554
- const shiftDragRef = (0, import_react13.useRef)(false);
2555
- const shiftToggleElementRef = (0, import_react13.useRef)(null);
2556
- (0, import_react13.useEffect)(() => {
3760
+ const shiftDragRef = (0, import_react16.useRef)(false);
3761
+ const shiftToggleElementRef = (0, import_react16.useRef)(null);
3762
+ (0, import_react16.useEffect)(() => {
2557
3763
  return () => {
2558
3764
  if (hoverFrameRef.current) {
2559
3765
  window2.cancelAnimationFrame(hoverFrameRef.current);
2560
3766
  }
2561
3767
  };
2562
3768
  }, []);
2563
- const updateHoverTarget = (0, import_react13.useCallback)(
3769
+ const updateHoverTarget = (0, import_react16.useCallback)(
2564
3770
  (point) => {
2565
3771
  const target = getTargetElement(point, overlayRef.current, document2);
2566
3772
  if (target) {
@@ -2579,18 +3785,19 @@ var useMeasurerPointer = ({
2579
3785
  },
2580
3786
  [document2, overlayRef, setHoverElement, setHoverRect]
2581
3787
  );
2582
- const updateHoverElement = (0, import_react13.useCallback)(
3788
+ const updateHoverElement = (0, import_react16.useCallback)(
2583
3789
  (point) => {
2584
3790
  const target = getTargetElement(point, overlayRef.current, document2);
2585
3791
  setHoverElement(target);
2586
3792
  },
2587
3793
  [document2, overlayRef, setHoverElement]
2588
3794
  );
2589
- const handlePointerDown = (0, import_react13.useCallback)(
3795
+ const handlePointerDown = (0, import_react16.useCallback)(
2590
3796
  (event) => {
2591
3797
  const commit = createActionCommit();
2592
3798
  const toolbarNode = toolbarRef.current;
2593
3799
  if (toolbarNode && toolbarNode.contains(event.target)) return;
3800
+ if (settingsOpen) return;
2594
3801
  if (!enabled || event.button !== 0) return;
2595
3802
  if (toolMode === "none") return;
2596
3803
  clearSelectionRect();
@@ -2615,7 +3822,11 @@ var useMeasurerPointer = ({
2615
3822
  return;
2616
3823
  }
2617
3824
  if (guidesEnabled) {
3825
+ event.preventDefault();
2618
3826
  commit();
3827
+ setStart(null);
3828
+ setEnd(null);
3829
+ setIsDragging(false);
2619
3830
  const position = getSnapGuidePosition({
2620
3831
  orientation: guideOrientation,
2621
3832
  point,
@@ -2626,7 +3837,7 @@ var useMeasurerPointer = ({
2626
3837
  document: document2
2627
3838
  });
2628
3839
  const id = createId();
2629
- setSelectedGuideIds([id]);
3840
+ setSelectedGuideIds(selectNewGuideEnabled ? [id] : []);
2630
3841
  setGuides((prev) => [
2631
3842
  ...prev,
2632
3843
  { id, orientation: guideOrientation, position }
@@ -2651,12 +3862,14 @@ var useMeasurerPointer = ({
2651
3862
  draggingGuideId,
2652
3863
  document2,
2653
3864
  enabled,
3865
+ settingsOpen,
2654
3866
  guideOrientation,
2655
3867
  guides,
2656
3868
  guidesEnabled,
2657
3869
  optionPairOverlay,
2658
3870
  overlayRef,
2659
3871
  scheduleGuideDragHold,
3872
+ selectNewGuideEnabled,
2660
3873
  selectedGuideIds.length,
2661
3874
  selectedMeasurements,
2662
3875
  setDraggingGuideId,
@@ -2671,10 +3884,11 @@ var useMeasurerPointer = ({
2671
3884
  toolbarRef
2672
3885
  ]
2673
3886
  );
2674
- const handlePointerMove = (0, import_react13.useCallback)(
3887
+ const handlePointerMove = (0, import_react16.useCallback)(
2675
3888
  (event) => {
2676
3889
  const toolbarNode = toolbarRef.current;
2677
3890
  if (toolbarNode && toolbarNode.contains(event.target)) return;
3891
+ if (settingsOpen) return;
2678
3892
  if (!enabled) return;
2679
3893
  if (toolMode === "none") {
2680
3894
  if (hoverHighlightEnabled) {
@@ -2693,7 +3907,7 @@ var useMeasurerPointer = ({
2693
3907
  if (!hoverFrameRef.current) {
2694
3908
  hoverFrameRef.current = window2.requestAnimationFrame(() => {
2695
3909
  const latest = hoverPointRef.current;
2696
- if (latest && !draggingGuideId) {
3910
+ if (latest && !draggingGuideId && !guidesEnabled) {
2697
3911
  if (hoverHighlightEnabled) {
2698
3912
  updateHoverTarget(latest);
2699
3913
  } else {
@@ -2743,6 +3957,7 @@ var useMeasurerPointer = ({
2743
3957
  )
2744
3958
  );
2745
3959
  }
3960
+ if (guidesEnabled) return;
2746
3961
  if (!start) return;
2747
3962
  setEnd(point);
2748
3963
  if (!isDragging) {
@@ -2758,6 +3973,7 @@ var useMeasurerPointer = ({
2758
3973
  altPressed,
2759
3974
  draggingGuideId,
2760
3975
  enabled,
3976
+ settingsOpen,
2761
3977
  hoverHighlightEnabled,
2762
3978
  guides,
2763
3979
  guidesEnabled,
@@ -2780,13 +3996,20 @@ var useMeasurerPointer = ({
2780
3996
  updateHoverTarget
2781
3997
  ]
2782
3998
  );
2783
- const handlePointerUp = (0, import_react13.useCallback)(
3999
+ const handlePointerUp = (0, import_react16.useCallback)(
2784
4000
  (event) => {
2785
4001
  const commit = createActionCommit();
2786
4002
  const toolbarNode = toolbarRef.current;
2787
4003
  if (toolbarNode && toolbarNode.contains(event.target)) return;
4004
+ if (settingsOpen) return;
2788
4005
  if (!enabled) return;
2789
4006
  clearGuideDragHold();
4007
+ if (guidesEnabled) {
4008
+ setStart(null);
4009
+ setEnd(null);
4010
+ setIsDragging(false);
4011
+ return;
4012
+ }
2790
4013
  if (toolMode === "none") {
2791
4014
  setStart(null);
2792
4015
  setEnd(null);
@@ -2957,7 +4180,9 @@ var useMeasurerPointer = ({
2957
4180
  createActionCommit,
2958
4181
  draggingGuideId,
2959
4182
  enabled,
4183
+ settingsOpen,
2960
4184
  end,
4185
+ guidesEnabled,
2961
4186
  hoverHighlightEnabled,
2962
4187
  isDragging,
2963
4188
  overlayRef,
@@ -2980,7 +4205,7 @@ var useMeasurerPointer = ({
2980
4205
  toolbarRef
2981
4206
  ]
2982
4207
  );
2983
- const handlePointerLeave = (0, import_react13.useCallback)(() => {
4208
+ const handlePointerLeave = (0, import_react16.useCallback)(() => {
2984
4209
  clearGuideDragHold();
2985
4210
  setStart(null);
2986
4211
  setEnd(null);
@@ -3004,54 +4229,58 @@ var useMeasurerPointer = ({
3004
4229
  };
3005
4230
 
3006
4231
  // hooks/use-overlay-refs.ts
3007
- var import_react14 = require("react");
4232
+ var import_react17 = require("react");
3008
4233
  var useOverlayRefs = () => {
3009
- const overlayRef = (0, import_react14.useRef)(null);
3010
- const selectedElementRef = (0, import_react14.useRef)(null);
3011
- const hoverElementRef = (0, import_react14.useRef)(null);
4234
+ const overlayRef = (0, import_react17.useRef)(null);
4235
+ const selectedElementRef = (0, import_react17.useRef)(null);
4236
+ const hoverElementRef = (0, import_react17.useRef)(null);
3012
4237
  return { overlayRef, selectedElementRef, hoverElementRef };
3013
4238
  };
3014
4239
 
3015
4240
  // hooks/use-resize-sync.ts
3016
- var import_react15 = require("react");
4241
+ var import_react18 = require("react");
3017
4242
  var useResizeSync = (params) => {
3018
- const resizeFrameRef = (0, import_react15.useRef)(null);
3019
- const viewportRef = (0, import_react15.useRef)(getViewportSize(params.window));
3020
- (0, import_react15.useEffect)(() => {
4243
+ const paramsRef = (0, import_react18.useRef)(params);
4244
+ paramsRef.current = params;
4245
+ const resizeFrameRef = (0, import_react18.useRef)(null);
4246
+ const viewportRef = (0, import_react18.useRef)(getViewportSize(params.window));
4247
+ (0, import_react18.useEffect)(() => {
4248
+ const ownerWindow = params.window;
3021
4249
  const handleResize = () => {
4250
+ const current = paramsRef.current;
3022
4251
  if (resizeFrameRef.current) {
3023
- params.window.cancelAnimationFrame(resizeFrameRef.current);
4252
+ ownerWindow.cancelAnimationFrame(resizeFrameRef.current);
3024
4253
  }
3025
- resizeFrameRef.current = params.window.requestAnimationFrame(() => {
3026
- const viewport = getViewportSize(params.window);
4254
+ resizeFrameRef.current = ownerWindow.requestAnimationFrame(() => {
4255
+ const viewport = getViewportSize(ownerWindow);
3027
4256
  const previousViewport = viewportRef.current;
3028
- params.setMeasurements(
4257
+ current.setMeasurements(
3029
4258
  (prev) => prev.map(
3030
- (measurement) => updateMeasurementForResize(measurement, viewport, params.document)
4259
+ (measurement) => updateMeasurementForResize(measurement, viewport, current.document)
3031
4260
  )
3032
4261
  );
3033
- params.setActiveMeasurement(
3034
- (prev) => prev ? updateMeasurementForResize(prev, viewport, params.document) : prev
4262
+ current.setActiveMeasurement(
4263
+ (prev) => prev ? updateMeasurementForResize(prev, viewport, current.document) : prev
3035
4264
  );
3036
- params.setHeldDistances(
4265
+ current.setHeldDistances(
3037
4266
  (prev) => prev.map(
3038
4267
  (distance) => updateDistanceForResize(
3039
4268
  distance,
3040
4269
  viewport,
3041
- params.document,
3042
- params.window
4270
+ current.document,
4271
+ ownerWindow
3043
4272
  )
3044
4273
  )
3045
4274
  );
3046
- if (params.selectedElementRef.current && params.document.contains(params.selectedElementRef.current)) {
3047
- params.setSelectedMeasurement(
3048
- getInspectMeasurement(params.selectedElementRef.current, params.window)
4275
+ if (current.selectedElementRef.current && current.document.contains(current.selectedElementRef.current)) {
4276
+ current.setSelectedMeasurement(
4277
+ getInspectMeasurement(current.selectedElementRef.current, ownerWindow)
3049
4278
  );
3050
4279
  }
3051
4280
  if (previousViewport.width > 0 && previousViewport.height > 0) {
3052
4281
  const scaleX = viewport.width / previousViewport.width;
3053
4282
  const scaleY = viewport.height / previousViewport.height;
3054
- params.setGuides(
4283
+ current.setGuides(
3055
4284
  (prev) => prev.map(
3056
4285
  (guide) => guide.orientation === "vertical" ? { ...guide, position: guide.position * scaleX } : { ...guide, position: guide.position * scaleY }
3057
4286
  )
@@ -3060,27 +4289,27 @@ var useResizeSync = (params) => {
3060
4289
  viewportRef.current = viewport;
3061
4290
  });
3062
4291
  };
3063
- params.window.addEventListener("resize", handleResize);
4292
+ ownerWindow.addEventListener("resize", handleResize);
3064
4293
  return () => {
3065
4294
  if (resizeFrameRef.current) {
3066
- params.window.cancelAnimationFrame(resizeFrameRef.current);
4295
+ ownerWindow.cancelAnimationFrame(resizeFrameRef.current);
3067
4296
  }
3068
- params.window.removeEventListener("resize", handleResize);
4297
+ ownerWindow.removeEventListener("resize", handleResize);
3069
4298
  };
3070
- }, [params]);
4299
+ }, [params.window]);
3071
4300
  };
3072
4301
 
3073
4302
  // render/measurer-overlay.tsx
3074
- var import_react19 = require("react");
4303
+ var import_react22 = require("react");
3075
4304
 
3076
4305
  // components/measure-tag.tsx
3077
- var import_jsx_runtime4 = require("react/jsx-runtime");
4306
+ var import_jsx_runtime7 = require("react/jsx-runtime");
3078
4307
  function MeasureTag({
3079
4308
  children,
3080
4309
  className = "",
3081
4310
  style
3082
4311
  }) {
3083
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
4312
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3084
4313
  "div",
3085
4314
  {
3086
4315
  className: cn(
@@ -3094,21 +4323,21 @@ function MeasureTag({
3094
4323
  }
3095
4324
 
3096
4325
  // components/measurement-box.tsx
3097
- var import_react16 = require("react");
3098
- var import_jsx_runtime5 = require("react/jsx-runtime");
4326
+ var import_react19 = require("react");
4327
+ var import_jsx_runtime8 = require("react/jsx-runtime");
3099
4328
  var formatValue2 = (value) => Math.round(value);
3100
- var MeasurementBox = (0, import_react16.memo)(function MeasurementBox2({
4329
+ var MeasurementBox = (0, import_react19.memo)(function MeasurementBox2({
3101
4330
  measurement,
3102
4331
  transitionMs,
3103
4332
  labelOffset,
3104
- edgeVisibility
4333
+ edgeVisibility,
4334
+ outlineColor,
4335
+ fillColor
3105
4336
  }) {
3106
4337
  const edges = edgeVisibility ?? { top: true, right: true, bottom: true, left: true };
3107
4338
  const displayRect = measurement.rect;
3108
- const outlineColor = "color-mix(in oklch, oklch(0.62 0.18 255) 80%, transparent)";
3109
- const fillColor = "color-mix(in oklch, oklch(0.62 0.18 255) 8%, transparent)";
3110
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "msr:pointer-events-none", children: [
3111
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
4339
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "msr:pointer-events-none", children: [
4340
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
3112
4341
  "div",
3113
4342
  {
3114
4343
  className: "msr:absolute",
@@ -3121,28 +4350,28 @@ var MeasurementBox = (0, import_react16.memo)(function MeasurementBox2({
3121
4350
  transition: `left ${transitionMs}ms ease, top ${transitionMs}ms ease, width ${transitionMs}ms ease, height ${transitionMs}ms ease`
3122
4351
  },
3123
4352
  children: [
3124
- edges.top ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
4353
+ edges.top ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
3125
4354
  "div",
3126
4355
  {
3127
4356
  className: "msr:absolute msr:left-0 msr:top-0 msr:h-px msr:w-full",
3128
4357
  style: { backgroundColor: outlineColor }
3129
4358
  }
3130
4359
  ) : null,
3131
- edges.right ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
4360
+ edges.right ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
3132
4361
  "div",
3133
4362
  {
3134
4363
  className: "msr:absolute msr:right-0 msr:top-0 msr:h-full msr:w-px",
3135
4364
  style: { backgroundColor: outlineColor }
3136
4365
  }
3137
4366
  ) : null,
3138
- edges.bottom ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
4367
+ edges.bottom ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
3139
4368
  "div",
3140
4369
  {
3141
4370
  className: "msr:absolute msr:bottom-0 msr:left-0 msr:h-px msr:w-full",
3142
4371
  style: { backgroundColor: outlineColor }
3143
4372
  }
3144
4373
  ) : null,
3145
- edges.left ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
4374
+ edges.left ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
3146
4375
  "div",
3147
4376
  {
3148
4377
  className: "msr:absolute msr:left-0 msr:top-0 msr:h-full msr:w-px",
@@ -3152,7 +4381,7 @@ var MeasurementBox = (0, import_react16.memo)(function MeasurementBox2({
3152
4381
  ]
3153
4382
  }
3154
4383
  ),
3155
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
4384
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
3156
4385
  MeasureTag,
3157
4386
  {
3158
4387
  className: "msr:-translate-x-1/2 msr:bg-ink-900/90",
@@ -3172,21 +4401,21 @@ var MeasurementBox = (0, import_react16.memo)(function MeasurementBox2({
3172
4401
  });
3173
4402
 
3174
4403
  // components/selected-measurement-box.tsx
3175
- var import_react17 = require("react");
3176
- var import_jsx_runtime6 = require("react/jsx-runtime");
4404
+ var import_react20 = require("react");
4405
+ var import_jsx_runtime9 = require("react/jsx-runtime");
3177
4406
  var formatValue3 = (value) => Math.round(value);
3178
- var SelectedMeasurementBox = (0, import_react17.memo)(function SelectedMeasurementBox2({
4407
+ var SelectedMeasurementBox = (0, import_react20.memo)(function SelectedMeasurementBox2({
3179
4408
  measurement,
3180
4409
  transitionMs,
3181
4410
  labelOffset,
3182
- edgeVisibility
4411
+ edgeVisibility,
4412
+ outlineColor,
4413
+ fillColor
3183
4414
  }) {
3184
4415
  const edges = edgeVisibility ?? { top: true, right: true, bottom: true, left: true };
3185
4416
  const displayRect = measurement.rect;
3186
- const outlineColor = "color-mix(in oklch, oklch(0.62 0.18 255) 80%, transparent)";
3187
- const fillColor = "color-mix(in oklch, oklch(0.62 0.18 255) 8%, transparent)";
3188
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "msr:pointer-events-none", children: [
3189
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
4417
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "msr:pointer-events-none", "data-mesurer-selected-measurement": "true", children: [
4418
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
3190
4419
  "div",
3191
4420
  {
3192
4421
  className: "msr:absolute",
@@ -3199,28 +4428,28 @@ var SelectedMeasurementBox = (0, import_react17.memo)(function SelectedMeasureme
3199
4428
  transition: `left ${transitionMs}ms ease, top ${transitionMs}ms ease, width ${transitionMs}ms ease, height ${transitionMs}ms ease`
3200
4429
  },
3201
4430
  children: [
3202
- edges.top ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
4431
+ edges.top ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
3203
4432
  "div",
3204
4433
  {
3205
4434
  className: "msr:absolute msr:left-0 msr:top-0 msr:h-px msr:w-full",
3206
4435
  style: { backgroundColor: outlineColor }
3207
4436
  }
3208
4437
  ) : null,
3209
- edges.right ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
4438
+ edges.right ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
3210
4439
  "div",
3211
4440
  {
3212
4441
  className: "msr:absolute msr:right-0 msr:top-0 msr:h-full msr:w-px",
3213
4442
  style: { backgroundColor: outlineColor }
3214
4443
  }
3215
4444
  ) : null,
3216
- edges.bottom ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
4445
+ edges.bottom ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
3217
4446
  "div",
3218
4447
  {
3219
4448
  className: "msr:absolute msr:bottom-0 msr:left-0 msr:h-px msr:w-full",
3220
4449
  style: { backgroundColor: outlineColor }
3221
4450
  }
3222
4451
  ) : null,
3223
- edges.left ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
4452
+ edges.left ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
3224
4453
  "div",
3225
4454
  {
3226
4455
  className: "msr:absolute msr:left-0 msr:top-0 msr:h-full msr:w-px",
@@ -3230,7 +4459,7 @@ var SelectedMeasurementBox = (0, import_react17.memo)(function SelectedMeasureme
3230
4459
  ]
3231
4460
  }
3232
4461
  ),
3233
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
4462
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
3234
4463
  MeasureTag,
3235
4464
  {
3236
4465
  className: "msr:-translate-x-1/2 msr:bg-ink-900/90",
@@ -3250,14 +4479,14 @@ var SelectedMeasurementBox = (0, import_react17.memo)(function SelectedMeasureme
3250
4479
  });
3251
4480
 
3252
4481
  // render/distance-overlay-item.tsx
3253
- var import_react18 = require("react");
3254
- var import_jsx_runtime7 = require("react/jsx-runtime");
3255
- var DistanceOverlayItem = (0, import_react18.memo)(function DistanceOverlayItem2({
4482
+ var import_react21 = require("react");
4483
+ var import_jsx_runtime10 = require("react/jsx-runtime");
4484
+ var DistanceOverlayItem = (0, import_react21.memo)(function DistanceOverlayItem2({
3256
4485
  distance,
3257
4486
  labelOffset,
3258
4487
  onRemove
3259
4488
  }) {
3260
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
4489
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
3261
4490
  "div",
3262
4491
  {
3263
4492
  className: onRemove ? "msr:pointer-events-auto" : "msr:pointer-events-none",
@@ -3266,7 +4495,7 @@ var DistanceOverlayItem = (0, import_react18.memo)(function DistanceOverlayItem2
3266
4495
  onRemove(distance.id);
3267
4496
  } : void 0,
3268
4497
  children: [
3269
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
4498
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
3270
4499
  "div",
3271
4500
  {
3272
4501
  className: "msr:absolute msr:rounded msr:border msr:border-[#2563eb]/70",
@@ -3278,7 +4507,7 @@ var DistanceOverlayItem = (0, import_react18.memo)(function DistanceOverlayItem2
3278
4507
  }
3279
4508
  }
3280
4509
  ),
3281
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
4510
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
3282
4511
  "div",
3283
4512
  {
3284
4513
  className: "msr:absolute msr:rounded msr:border msr:border-[#2563eb]/70",
@@ -3291,7 +4520,7 @@ var DistanceOverlayItem = (0, import_react18.memo)(function DistanceOverlayItem2
3291
4520
  }
3292
4521
  ),
3293
4522
  distance.connectors.map(
3294
- (connector, index) => Math.abs(connector.x1 - connector.x2) < 1 ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
4523
+ (connector, index) => Math.abs(connector.x1 - connector.x2) < 1 ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
3295
4524
  "div",
3296
4525
  {
3297
4526
  className: "msr:absolute msr:border-l msr:border-dashed msr:border-[#2563eb]/70",
@@ -3302,7 +4531,7 @@ var DistanceOverlayItem = (0, import_react18.memo)(function DistanceOverlayItem2
3302
4531
  }
3303
4532
  },
3304
4533
  `${distance.id}-connector-${index}`
3305
- ) : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
4534
+ ) : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
3306
4535
  "div",
3307
4536
  {
3308
4537
  className: "msr:absolute msr:border-t msr:border-dashed msr:border-[#2563eb]/70",
@@ -3315,8 +4544,8 @@ var DistanceOverlayItem = (0, import_react18.memo)(function DistanceOverlayItem2
3315
4544
  `${distance.id}-connector-${index}`
3316
4545
  )
3317
4546
  ),
3318
- distance.horizontal && distance.horizontal.value > 0 ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
3319
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
4547
+ distance.horizontal && distance.horizontal.value > 0 ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
4548
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
3320
4549
  "div",
3321
4550
  {
3322
4551
  className: "msr:absolute msr:h-px msr:bg-[#2563eb]",
@@ -3327,7 +4556,7 @@ var DistanceOverlayItem = (0, import_react18.memo)(function DistanceOverlayItem2
3327
4556
  }
3328
4557
  }
3329
4558
  ),
3330
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
4559
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
3331
4560
  MeasureTag,
3332
4561
  {
3333
4562
  className: "msr:-translate-x-1/2 msr:bg-ink-900/90",
@@ -3339,8 +4568,8 @@ var DistanceOverlayItem = (0, import_react18.memo)(function DistanceOverlayItem2
3339
4568
  }
3340
4569
  )
3341
4570
  ] }) : null,
3342
- distance.vertical && distance.vertical.value > 0 ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
3343
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
4571
+ distance.vertical && distance.vertical.value > 0 ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
4572
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
3344
4573
  "div",
3345
4574
  {
3346
4575
  className: "msr:absolute msr:w-px msr:bg-[#2563eb]",
@@ -3351,7 +4580,7 @@ var DistanceOverlayItem = (0, import_react18.memo)(function DistanceOverlayItem2
3351
4580
  }
3352
4581
  }
3353
4582
  ),
3354
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
4583
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
3355
4584
  MeasureTag,
3356
4585
  {
3357
4586
  className: "msr:-translate-y-1/2 msr:bg-ink-900/90",
@@ -3369,9 +4598,10 @@ var DistanceOverlayItem = (0, import_react18.memo)(function DistanceOverlayItem2
3369
4598
  });
3370
4599
 
3371
4600
  // render/measurer-overlay.tsx
3372
- var import_jsx_runtime8 = require("react/jsx-runtime");
3373
- var MeasurerOverlay = (0, import_react19.memo)(function MeasurerOverlay2({
4601
+ var import_jsx_runtime11 = require("react/jsx-runtime");
4602
+ var MeasurerOverlay = (0, import_react22.memo)(function MeasurerOverlay2({
3374
4603
  enabled,
4604
+ interactive = true,
3375
4605
  toolMode,
3376
4606
  guidePointerEvents,
3377
4607
  guidesEnabled,
@@ -3401,6 +4631,7 @@ var MeasurerOverlay = (0, import_react19.memo)(function MeasurerOverlay2({
3401
4631
  guideColorActive,
3402
4632
  guideColorHover,
3403
4633
  guideColorDefault,
4634
+ guideStyle,
3404
4635
  onPointerDown,
3405
4636
  onPointerMove,
3406
4637
  onPointerUp,
@@ -3411,30 +4642,32 @@ var MeasurerOverlay = (0, import_react19.memo)(function MeasurerOverlay2({
3411
4642
  onGuidePointerCancel
3412
4643
  }) {
3413
4644
  const overlayVisible = enabled;
3414
- const overlayInteractive = overlayVisible && toolMode !== "none" && toolMode !== "text-inspector" && toolMode !== "xray" && toolMode !== "rulers";
3415
- const selectionVisible = toolMode === "select";
3416
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
4645
+ const overlayInteractive = interactive && overlayVisible && toolMode !== "none" && toolMode !== "text-inspector" && toolMode !== "xray" && toolMode !== "rulers";
4646
+ const selectionVisible = interactive && toolMode === "select";
4647
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
3417
4648
  "div",
3418
4649
  {
3419
- className: `msr:absolute msr:inset-0 ${overlayVisible ? `msr:pointer-events-auto ${guidesEnabled ? hoverGuide || draggingGuideId ? "msr:cursor-default" : "msr:cursor-crosshair" : "msr:cursor-default"} msr:opacity-100` : "msr:pointer-events-none msr:opacity-0"}`,
4650
+ 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"}`,
3420
4651
  style: { pointerEvents: overlayInteractive ? "auto" : "none" },
3421
4652
  onPointerDown,
3422
4653
  onPointerMove,
3423
4654
  onPointerUp,
3424
4655
  onPointerLeave,
3425
4656
  children: [
3426
- selectionVisible ? displayedMeasurements.map((measurement, index) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
4657
+ selectionVisible ? displayedMeasurements.map((measurement, index) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
3427
4658
  MeasurementBox,
3428
4659
  {
3429
4660
  measurement,
3430
4661
  transitionMs: MEASURE_TRANSITION_MS,
3431
4662
  labelOffset: MEASURE_LABEL_OFFSET,
3432
- edgeVisibility: measurementEdgeVisibility[index]
4663
+ edgeVisibility: measurementEdgeVisibility[index],
4664
+ outlineColor,
4665
+ fillColor
3433
4666
  },
3434
4667
  measurement.id
3435
4668
  )) : null,
3436
- selectionVisible && activeRect && isDragging ? /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
3437
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
4669
+ selectionVisible && activeRect && isDragging ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
4670
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
3438
4671
  "div",
3439
4672
  {
3440
4673
  className: "msr:pointer-events-none msr:absolute",
@@ -3446,28 +4679,28 @@ var MeasurerOverlay = (0, import_react19.memo)(function MeasurerOverlay2({
3446
4679
  backgroundColor: fillColor
3447
4680
  },
3448
4681
  children: [
3449
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
4682
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
3450
4683
  "div",
3451
4684
  {
3452
4685
  className: "msr:absolute msr:left-0 msr:top-0 msr:h-px msr:w-full",
3453
4686
  style: { backgroundColor: outlineColor }
3454
4687
  }
3455
4688
  ),
3456
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
4689
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
3457
4690
  "div",
3458
4691
  {
3459
4692
  className: "msr:absolute msr:right-0 msr:top-0 msr:h-full msr:w-px",
3460
4693
  style: { backgroundColor: outlineColor }
3461
4694
  }
3462
4695
  ),
3463
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
4696
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
3464
4697
  "div",
3465
4698
  {
3466
4699
  className: "msr:absolute msr:bottom-0 msr:left-0 msr:h-px msr:w-full",
3467
4700
  style: { backgroundColor: outlineColor }
3468
4701
  }
3469
4702
  ),
3470
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
4703
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
3471
4704
  "div",
3472
4705
  {
3473
4706
  className: "msr:absolute msr:left-0 msr:top-0 msr:h-full msr:w-px",
@@ -3477,7 +4710,7 @@ var MeasurerOverlay = (0, import_react19.memo)(function MeasurerOverlay2({
3477
4710
  ]
3478
4711
  }
3479
4712
  ),
3480
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
4713
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
3481
4714
  MeasureTag,
3482
4715
  {
3483
4716
  className: "msr:-translate-x-1/2 msr:bg-ink-900/90",
@@ -3493,7 +4726,7 @@ var MeasurerOverlay = (0, import_react19.memo)(function MeasurerOverlay2({
3493
4726
  }
3494
4727
  )
3495
4728
  ] }) : null,
3496
- selectionVisible && hoverRectToShow ? /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
4729
+ selectionVisible && hoverRectToShow ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
3497
4730
  "div",
3498
4731
  {
3499
4732
  className: "msr:pointer-events-none msr:absolute",
@@ -3505,28 +4738,28 @@ var MeasurerOverlay = (0, import_react19.memo)(function MeasurerOverlay2({
3505
4738
  backgroundColor: fillColor
3506
4739
  },
3507
4740
  children: [
3508
- hoverEdgeVisibility?.top ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
4741
+ hoverEdgeVisibility?.top ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
3509
4742
  "div",
3510
4743
  {
3511
4744
  className: "msr:absolute msr:left-0 msr:top-0 msr:h-px msr:w-full",
3512
4745
  style: { backgroundColor: outlineColor }
3513
4746
  }
3514
4747
  ) : null,
3515
- hoverEdgeVisibility?.right ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
4748
+ hoverEdgeVisibility?.right ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
3516
4749
  "div",
3517
4750
  {
3518
4751
  className: "msr:absolute msr:right-0 msr:top-0 msr:h-full msr:w-px",
3519
4752
  style: { backgroundColor: outlineColor }
3520
4753
  }
3521
4754
  ) : null,
3522
- hoverEdgeVisibility?.bottom ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
4755
+ hoverEdgeVisibility?.bottom ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
3523
4756
  "div",
3524
4757
  {
3525
4758
  className: "msr:absolute msr:bottom-0 msr:left-0 msr:h-px msr:w-full",
3526
4759
  style: { backgroundColor: outlineColor }
3527
4760
  }
3528
4761
  ) : null,
3529
- hoverEdgeVisibility?.left ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
4762
+ hoverEdgeVisibility?.left ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
3530
4763
  "div",
3531
4764
  {
3532
4765
  className: "msr:absolute msr:left-0 msr:top-0 msr:h-full msr:w-px",
@@ -3536,7 +4769,7 @@ var MeasurerOverlay = (0, import_react19.memo)(function MeasurerOverlay2({
3536
4769
  ]
3537
4770
  }
3538
4771
  ) : null,
3539
- guidesEnabled && guidePreview ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
4772
+ interactive && guidesEnabled && guidePreview ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
3540
4773
  "div",
3541
4774
  {
3542
4775
  className: "msr:pointer-events-none msr:absolute",
@@ -3551,7 +4784,7 @@ var MeasurerOverlay = (0, import_react19.memo)(function MeasurerOverlay2({
3551
4784
  height: GUIDE_HITBOX_SIZE,
3552
4785
  width: "100%"
3553
4786
  },
3554
- children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
4787
+ children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
3555
4788
  "div",
3556
4789
  {
3557
4790
  className: "msr:absolute",
@@ -3572,17 +4805,19 @@ var MeasurerOverlay = (0, import_react19.memo)(function MeasurerOverlay2({
3572
4805
  )
3573
4806
  }
3574
4807
  ) : null,
3575
- selectionVisible ? displayedSelectedMeasurements.map((measurement, index) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
4808
+ selectionVisible ? displayedSelectedMeasurements.map((measurement, index) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
3576
4809
  SelectedMeasurementBox,
3577
4810
  {
3578
4811
  measurement,
3579
4812
  transitionMs: MEASURE_TRANSITION_MS,
3580
4813
  labelOffset: MEASURE_LABEL_OFFSET,
3581
- edgeVisibility: selectedEdgeVisibility[index]
4814
+ edgeVisibility: selectedEdgeVisibility[index],
4815
+ outlineColor,
4816
+ fillColor
3582
4817
  },
3583
4818
  measurement.id
3584
4819
  )) : null,
3585
- heldDistances.map((distance) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
4820
+ interactive ? heldDistances.map((distance) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
3586
4821
  DistanceOverlayItem,
3587
4822
  {
3588
4823
  distance,
@@ -3590,8 +4825,8 @@ var MeasurerOverlay = (0, import_react19.memo)(function MeasurerOverlay2({
3590
4825
  onRemove: onRemoveHeldDistance
3591
4826
  },
3592
4827
  `held-${distance.id}`
3593
- )),
3594
- selectionVisible && altPressed && optionPairOverlay ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
4828
+ )) : null,
4829
+ selectionVisible && altPressed && optionPairOverlay ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
3595
4830
  DistanceOverlayItem,
3596
4831
  {
3597
4832
  distance: optionPairOverlay,
@@ -3599,7 +4834,7 @@ var MeasurerOverlay = (0, import_react19.memo)(function MeasurerOverlay2({
3599
4834
  },
3600
4835
  `option-${optionPairOverlay.id}`
3601
4836
  ) : null,
3602
- guidesEnabled && altPressed && guideDistanceOverlay ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
4837
+ interactive && guidesEnabled && altPressed && guideDistanceOverlay ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
3603
4838
  DistanceOverlayItem,
3604
4839
  {
3605
4840
  distance: guideDistanceOverlay,
@@ -3607,9 +4842,9 @@ var MeasurerOverlay = (0, import_react19.memo)(function MeasurerOverlay2({
3607
4842
  },
3608
4843
  `guide-preview-${guideDistanceOverlay.id}`
3609
4844
  ) : null,
3610
- selectionVisible && altPressed && optionContainerLines ? /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
3611
- optionContainerLines.top.value > 0 ? /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
3612
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
4845
+ selectionVisible && altPressed && optionContainerLines ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
4846
+ optionContainerLines.top.value > 0 ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
4847
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
3613
4848
  "div",
3614
4849
  {
3615
4850
  className: "msr:absolute msr:w-px msr:bg-[#2563eb]",
@@ -3620,7 +4855,7 @@ var MeasurerOverlay = (0, import_react19.memo)(function MeasurerOverlay2({
3620
4855
  }
3621
4856
  }
3622
4857
  ),
3623
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
4858
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
3624
4859
  MeasureTag,
3625
4860
  {
3626
4861
  className: "msr:-translate-y-1/2 msr:bg-ink-900/90",
@@ -3632,8 +4867,8 @@ var MeasurerOverlay = (0, import_react19.memo)(function MeasurerOverlay2({
3632
4867
  }
3633
4868
  )
3634
4869
  ] }) : null,
3635
- optionContainerLines.bottom.value > 0 ? /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
3636
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
4870
+ optionContainerLines.bottom.value > 0 ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
4871
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
3637
4872
  "div",
3638
4873
  {
3639
4874
  className: "msr:absolute msr:w-px msr:bg-[#2563eb]",
@@ -3644,7 +4879,7 @@ var MeasurerOverlay = (0, import_react19.memo)(function MeasurerOverlay2({
3644
4879
  }
3645
4880
  }
3646
4881
  ),
3647
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
4882
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
3648
4883
  MeasureTag,
3649
4884
  {
3650
4885
  className: "msr:-translate-y-1/2 msr:bg-ink-900/90",
@@ -3656,8 +4891,8 @@ var MeasurerOverlay = (0, import_react19.memo)(function MeasurerOverlay2({
3656
4891
  }
3657
4892
  )
3658
4893
  ] }) : null,
3659
- optionContainerLines.left.value > 0 ? /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
3660
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
4894
+ optionContainerLines.left.value > 0 ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
4895
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
3661
4896
  "div",
3662
4897
  {
3663
4898
  className: "msr:absolute msr:h-px msr:bg-[#2563eb]",
@@ -3668,7 +4903,7 @@ var MeasurerOverlay = (0, import_react19.memo)(function MeasurerOverlay2({
3668
4903
  }
3669
4904
  }
3670
4905
  ),
3671
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
4906
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
3672
4907
  MeasureTag,
3673
4908
  {
3674
4909
  className: "msr:-translate-x-1/2 msr:bg-ink-900/90",
@@ -3680,8 +4915,8 @@ var MeasurerOverlay = (0, import_react19.memo)(function MeasurerOverlay2({
3680
4915
  }
3681
4916
  )
3682
4917
  ] }) : null,
3683
- optionContainerLines.right.value > 0 ? /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
3684
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
4918
+ optionContainerLines.right.value > 0 ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
4919
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
3685
4920
  "div",
3686
4921
  {
3687
4922
  className: "msr:absolute msr:h-px msr:bg-[#2563eb]",
@@ -3692,7 +4927,7 @@ var MeasurerOverlay = (0, import_react19.memo)(function MeasurerOverlay2({
3692
4927
  }
3693
4928
  }
3694
4929
  ),
3695
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
4930
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
3696
4931
  MeasureTag,
3697
4932
  {
3698
4933
  className: "msr:-translate-x-1/2 msr:bg-ink-900/90",
@@ -3708,9 +4943,12 @@ var MeasurerOverlay = (0, import_react19.memo)(function MeasurerOverlay2({
3708
4943
  guides.map((guide) => {
3709
4944
  const isSelected = selectedGuideIds.includes(guide.id);
3710
4945
  const isHovered = hoverGuide?.id === guide.id;
3711
- const strokeWidth = isSelected || isHovered ? 2 : 1;
3712
4946
  const strokeColor = isSelected ? guideColorActive : isHovered ? guideColorHover : guideColorDefault;
3713
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
4947
+ const strokeWidth = Math.max(guideStyle.width, isSelected || isHovered ? 2 : guideStyle.width);
4948
+ const isSolid = guideStyle.pattern === "solid";
4949
+ 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)`;
4950
+ const backgroundSize = guideStyle.pattern === "dotted" ? guide.orientation === "vertical" ? `${strokeWidth}px ${guideStyle.dashLength + guideStyle.gap}px` : `${guideStyle.dashLength + guideStyle.gap}px ${strokeWidth}px` : void 0;
4951
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
3714
4952
  "div",
3715
4953
  {
3716
4954
  className: "msr:absolute",
@@ -3731,7 +4969,7 @@ var MeasurerOverlay = (0, import_react19.memo)(function MeasurerOverlay2({
3731
4969
  onPointerDown: (event) => onGuidePointerDown(guide, event),
3732
4970
  onPointerUp: (event) => onGuidePointerUp(guide, event),
3733
4971
  onPointerCancel: (event) => onGuidePointerCancel(guide, event),
3734
- children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
4972
+ children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
3735
4973
  "div",
3736
4974
  {
3737
4975
  className: "msr:absolute",
@@ -3740,13 +4978,19 @@ var MeasurerOverlay = (0, import_react19.memo)(function MeasurerOverlay2({
3740
4978
  top: 0,
3741
4979
  width: strokeWidth,
3742
4980
  height: "100%",
3743
- backgroundColor: strokeColor
4981
+ backgroundColor: isSolid ? strokeColor : "transparent",
4982
+ backgroundImage,
4983
+ backgroundSize,
4984
+ opacity: guideStyle.opacity
3744
4985
  } : {
3745
4986
  top: GUIDE_HITBOX_SIZE / 2 - 1,
3746
4987
  left: 0,
3747
4988
  height: strokeWidth,
3748
4989
  width: "100%",
3749
- backgroundColor: strokeColor
4990
+ backgroundColor: isSolid ? strokeColor : "transparent",
4991
+ backgroundImage,
4992
+ backgroundSize,
4993
+ opacity: guideStyle.opacity
3750
4994
  }
3751
4995
  }
3752
4996
  )
@@ -4267,6 +5511,7 @@ var createTextInspector = (options = {}, legacy = false) => {
4267
5511
  if (!element) return false;
4268
5512
  for (let node = element; node; node = node.parentElement) {
4269
5513
  if (node.classList.contains("mesurer-toolbar-surface")) return true;
5514
+ if (node.hasAttribute("data-mesurer-inspector-ui")) return true;
4270
5515
  }
4271
5516
  return false;
4272
5517
  };
@@ -4564,7 +5809,7 @@ var createTextInspector = (options = {}, legacy = false) => {
4564
5809
  const isInspectorUIEvent = (event) => event.composedPath().some(
4565
5810
  (node) => node instanceof HTMLElementConstructor && (() => {
4566
5811
  const element = node;
4567
- return element.id === OVERLAY_ID || element.classList.contains("mesurer-ti-card") || element.classList.contains("mesurer-ti-box") || element.classList.contains("mesurer-ti-close") || element.classList.contains("mesurer-toolbar-surface");
5812
+ 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");
4568
5813
  })()
4569
5814
  );
4570
5815
  const onClick = (event) => {
@@ -4647,8 +5892,223 @@ var createTextInspector = (options = {}, legacy = false) => {
4647
5892
  };
4648
5893
  var TextInspector = createTextInspector({}, true);
4649
5894
 
5895
+ // core/persistence.ts
5896
+ var MESURER_STORAGE_VERSION = 2;
5897
+ var DEFAULT_GUIDE_STYLE = {
5898
+ opacity: 1,
5899
+ width: 1,
5900
+ pattern: "solid",
5901
+ dashLength: 6,
5902
+ gap: 4
5903
+ };
5904
+ var DEFAULT_RULER_SETTINGS = {
5905
+ opacity: 1,
5906
+ edgeReveal: false
5907
+ };
5908
+ var isFormat = (value) => value === "hex" || value === "rgb" || value === "hsl" || value === "oklch";
5909
+ var normalizeGuideStyle = (value) => {
5910
+ if (!value || typeof value !== "object") return void 0;
5911
+ const input = value;
5912
+ return {
5913
+ opacity: typeof input.opacity === "number" ? Math.min(1, Math.max(0, input.opacity)) : DEFAULT_GUIDE_STYLE.opacity,
5914
+ width: typeof input.width === "number" ? Math.min(4, Math.max(1, input.width)) : DEFAULT_GUIDE_STYLE.width,
5915
+ pattern: input.pattern === "dashed" || input.pattern === "dotted" ? input.pattern : DEFAULT_GUIDE_STYLE.pattern,
5916
+ dashLength: typeof input.dashLength === "number" ? Math.min(24, Math.max(2, input.dashLength)) : DEFAULT_GUIDE_STYLE.dashLength,
5917
+ gap: typeof input.gap === "number" ? Math.min(24, Math.max(0, input.gap)) : DEFAULT_GUIDE_STYLE.gap
5918
+ };
5919
+ };
5920
+ var normalizeRulerSettings = (value) => {
5921
+ if (!value || typeof value !== "object") return void 0;
5922
+ const input = value;
5923
+ return {
5924
+ opacity: typeof input.opacity === "number" ? Math.min(1, Math.max(0.2, input.opacity)) : DEFAULT_RULER_SETTINGS.opacity,
5925
+ edgeReveal: typeof input.edgeReveal === "boolean" ? input.edgeReveal : DEFAULT_RULER_SETTINGS.edgeReveal
5926
+ };
5927
+ };
5928
+ var isFiniteNumber = (value) => typeof value === "number" && Number.isFinite(value);
5929
+ var isRect = (value) => {
5930
+ if (!value || typeof value !== "object") return false;
5931
+ const rect = value;
5932
+ return isFiniteNumber(rect.left) && isFiniteNumber(rect.top) && isFiniteNumber(rect.width) && rect.width >= 0 && isFiniteNumber(rect.height) && rect.height >= 0;
5933
+ };
5934
+ var isNormalizedRect = (value) => isRect(value);
5935
+ var isMeasurement = (value) => {
5936
+ if (!value || typeof value !== "object") return false;
5937
+ const measurement = value;
5938
+ 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");
5939
+ };
5940
+ var isGuide = (value) => {
5941
+ if (!value || typeof value !== "object") return false;
5942
+ const guide = value;
5943
+ return typeof guide.id === "string" && (guide.orientation === "vertical" || guide.orientation === "horizontal") && isFiniteNumber(guide.position);
5944
+ };
5945
+ var isDistanceOverlay = (value) => {
5946
+ if (!value || typeof value !== "object") return false;
5947
+ const distance = value;
5948
+ const isDistanceLine = (line) => {
5949
+ if (line === null) return true;
5950
+ if (!line || typeof line !== "object") return false;
5951
+ const item = line;
5952
+ return isFiniteNumber(item.x1) && isFiniteNumber(item.x2) && isFiniteNumber(item.y) && isFiniteNumber(item.value);
5953
+ };
5954
+ const isVerticalLine = (line) => {
5955
+ if (line === null) return true;
5956
+ if (!line || typeof line !== "object") return false;
5957
+ const item = line;
5958
+ return isFiniteNumber(item.y1) && isFiniteNumber(item.y2) && isFiniteNumber(item.x) && isFiniteNumber(item.value);
5959
+ };
5960
+ 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) => {
5961
+ if (!connector || typeof connector !== "object") return false;
5962
+ const item = connector;
5963
+ return isFiniteNumber(item.x1) && isFiniteNumber(item.y1) && isFiniteNumber(item.x2) && isFiniteNumber(item.y2);
5964
+ });
5965
+ };
5966
+ var normalizeStoredSettings = (value) => {
5967
+ if (!value || typeof value !== "object") return {};
5968
+ const input = value;
5969
+ return {
5970
+ ...typeof input.highlightColor === "string" ? { highlightColor: input.highlightColor } : {},
5971
+ ...typeof input.guideColor === "string" ? { guideColor: input.guideColor } : {},
5972
+ ...typeof input.hoverHighlightEnabled === "boolean" ? { hoverHighlightEnabled: input.hoverHighlightEnabled } : {},
5973
+ ...Array.isArray(input.colorPickerFormats) && input.colorPickerFormats.some(isFormat) ? { colorPickerFormats: input.colorPickerFormats.filter(isFormat) } : {},
5974
+ ...isFormat(input.colorPickerClickFormat) ? { colorPickerClickFormat: input.colorPickerClickFormat } : {},
5975
+ ...typeof input.snapEnabled === "boolean" ? { snapEnabled: input.snapEnabled } : {},
5976
+ ...typeof input.snapGuidesEnabled === "boolean" ? { snapGuidesEnabled: input.snapGuidesEnabled } : {},
5977
+ ...typeof input.selectNewGuideEnabled === "boolean" ? { selectNewGuideEnabled: input.selectNewGuideEnabled } : {},
5978
+ ...typeof input.multiMeasureEnabled === "boolean" ? { multiMeasureEnabled: input.multiMeasureEnabled } : {},
5979
+ ...typeof input.persistOnReload === "boolean" ? { persistOnReload: input.persistOnReload } : {},
5980
+ ...normalizeGuideStyle(input.guideStyle) ? { guideStyle: normalizeGuideStyle(input.guideStyle) } : {},
5981
+ ...normalizeRulerSettings(input.rulerSettings) ? { rulerSettings: normalizeRulerSettings(input.rulerSettings) } : {}
5982
+ };
5983
+ };
5984
+ var normalizeStoredWorkspace = (value) => {
5985
+ if (!value || typeof value !== "object") return null;
5986
+ const input = value;
5987
+ 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;
5988
+ return {
5989
+ enabled: input.enabled,
5990
+ xrayVisible: typeof input.xrayVisible === "boolean" ? input.xrayVisible : input.toolMode === "xray",
5991
+ toolMode: input.toolMode,
5992
+ rulersVisible: input.rulersVisible,
5993
+ guideOrientation: input.guideOrientation,
5994
+ guides: input.guides.filter(isGuide),
5995
+ selectedGuideIds: input.selectedGuideIds.filter((id) => typeof id === "string"),
5996
+ measurements: input.measurements.filter(isMeasurement),
5997
+ activeMeasurement: isMeasurement(input.activeMeasurement) ? input.activeMeasurement : null,
5998
+ heldDistances: input.heldDistances.filter(isDistanceOverlay)
5999
+ };
6000
+ };
6001
+ var normalizePersistenceSnapshot = (value) => {
6002
+ if (!value || typeof value !== "object") return null;
6003
+ const record = value;
6004
+ if (record.version !== MESURER_STORAGE_VERSION) return null;
6005
+ return {
6006
+ settings: normalizeStoredSettings(record.settings),
6007
+ workspace: normalizeStoredWorkspace(record.workspace)
6008
+ };
6009
+ };
6010
+ var migrate = (record) => {
6011
+ if (record.version === MESURER_STORAGE_VERSION) {
6012
+ return normalizePersistenceSnapshot(record);
6013
+ }
6014
+ if (record.version !== 1) return null;
6015
+ return {
6016
+ settings: {},
6017
+ workspace: record.enabled === void 0 || !record.toolMode || !record.guideOrientation || !record.guides || !record.selectedGuideIds || !record.measurements || record.activeMeasurement === void 0 || !record.heldDistances ? null : normalizeStoredWorkspace({
6018
+ enabled: record.enabled,
6019
+ xrayVisible: record.toolMode === "xray",
6020
+ toolMode: record.toolMode,
6021
+ rulersVisible: record.rulersVisible ?? record.toolMode === "rulers",
6022
+ guideOrientation: record.guideOrientation,
6023
+ guides: record.guides,
6024
+ selectedGuideIds: record.selectedGuideIds,
6025
+ measurements: record.measurements,
6026
+ activeMeasurement: record.activeMeasurement,
6027
+ heldDistances: record.heldDistances
6028
+ })
6029
+ };
6030
+ };
6031
+ var createLocalStoragePersistence = (ownerWindow, workspaceKey, settingsKey = workspaceKey, legacyKey) => {
6032
+ let errorHandler;
6033
+ const readRecord = (key) => {
6034
+ try {
6035
+ const raw = ownerWindow.localStorage.getItem(key);
6036
+ if (!raw) return null;
6037
+ return migrate(JSON.parse(raw));
6038
+ } catch (error) {
6039
+ errorHandler?.(error);
6040
+ return null;
6041
+ }
6042
+ };
6043
+ const read = () => {
6044
+ const legacy = legacyKey ? readRecord(legacyKey) : null;
6045
+ const settingsRecord = readRecord(settingsKey);
6046
+ const workspaceRecord = readRecord(workspaceKey);
6047
+ if (!settingsRecord && !workspaceRecord && !legacy) return null;
6048
+ return {
6049
+ settings: settingsRecord?.settings ?? legacy?.settings ?? {},
6050
+ workspace: workspaceRecord?.workspace ?? legacy?.workspace ?? null
6051
+ };
6052
+ };
6053
+ const writeRecord = (key, snapshot) => {
6054
+ try {
6055
+ ownerWindow.localStorage.setItem(
6056
+ key,
6057
+ JSON.stringify({ version: MESURER_STORAGE_VERSION, ...snapshot })
6058
+ );
6059
+ } catch (error) {
6060
+ errorHandler?.(error);
6061
+ }
6062
+ };
6063
+ return {
6064
+ load: read,
6065
+ saveSettings: (settings) => {
6066
+ if (settingsKey === workspaceKey) {
6067
+ writeRecord(workspaceKey, { settings, workspace: read()?.workspace ?? null });
6068
+ return;
6069
+ }
6070
+ writeRecord(settingsKey, { settings, workspace: null });
6071
+ },
6072
+ saveWorkspace: (workspace) => {
6073
+ if (settingsKey === workspaceKey) {
6074
+ writeRecord(workspaceKey, { settings: read()?.settings ?? {}, workspace });
6075
+ return;
6076
+ }
6077
+ writeRecord(workspaceKey, { settings: {}, workspace });
6078
+ },
6079
+ clearWorkspace: () => {
6080
+ if (settingsKey === workspaceKey) {
6081
+ const current = read();
6082
+ if (current) writeRecord(workspaceKey, { settings: current.settings, workspace: null });
6083
+ return;
6084
+ }
6085
+ writeRecord(workspaceKey, { settings: {}, workspace: null });
6086
+ },
6087
+ clearSettings: () => {
6088
+ if (settingsKey === workspaceKey) {
6089
+ writeRecord(workspaceKey, { settings: {}, workspace: read()?.workspace ?? null });
6090
+ return;
6091
+ }
6092
+ writeRecord(settingsKey, { settings: {}, workspace: null });
6093
+ },
6094
+ setErrorHandler: (handler) => {
6095
+ errorHandler = handler;
6096
+ },
6097
+ subscribe: (listener) => {
6098
+ const handleStorage = (event) => {
6099
+ if (event.key !== settingsKey && event.key !== workspaceKey && event.key !== legacyKey) return;
6100
+ listener(read());
6101
+ };
6102
+ ownerWindow.addEventListener("storage", handleStorage);
6103
+ return () => {
6104
+ ownerWindow.removeEventListener("storage", handleStorage);
6105
+ };
6106
+ }
6107
+ };
6108
+ };
6109
+
4650
6110
  // measurer.tsx
4651
- var import_jsx_runtime9 = require("react/jsx-runtime");
6111
+ var import_jsx_runtime12 = require("react/jsx-runtime");
4652
6112
  var measurerInstanceCount = 0;
4653
6113
  var XRAY_STYLE_ID = "mesurer-xray-styles";
4654
6114
  var XRAY_STYLES = `
@@ -4660,13 +6120,17 @@ var XRAY_STYLES = `
4660
6120
  .xray-mode .mesurer-root,
4661
6121
  .xray-mode .mesurer-root *,
4662
6122
  .xray-mode .mesurer-toolbar-surface,
4663
- .xray-mode .mesurer-toolbar-surface * {
6123
+ .xray-mode .mesurer-toolbar-surface *,
6124
+ .xray-mode .mesurer-ti-box,
6125
+ .xray-mode .mesurer-ti-card,
6126
+ .xray-mode .mesurer-ti-card *,
6127
+ .xray-mode .mesurer-ti-close {
4664
6128
  outline: none !important;
4665
6129
  }
4666
6130
  `;
4667
6131
  var subscribeHydration = () => () => {
4668
6132
  };
4669
- var useHydrated = () => (0, import_react20.useSyncExternalStore)(
6133
+ var useHydrated = () => (0, import_react23.useSyncExternalStore)(
4670
6134
  subscribeHydration,
4671
6135
  () => true,
4672
6136
  () => false
@@ -4680,70 +6144,112 @@ var stripDistance = (distance) => ({
4680
6144
  elementRefA: void 0,
4681
6145
  elementRefB: void 0
4682
6146
  });
6147
+ var TAB_ID_KEY = "mesurer:tab-id";
6148
+ var SETTINGS_STORAGE_KEY = "mesurer-settings";
6149
+ var LEGACY_STORAGE_KEY = "mesurer-state";
6150
+ var getTabId = (ownerWindow) => {
6151
+ try {
6152
+ const existing = ownerWindow.sessionStorage.getItem(TAB_ID_KEY);
6153
+ if (existing) return existing;
6154
+ const id = ownerWindow.crypto.randomUUID();
6155
+ ownerWindow.sessionStorage.setItem(TAB_ID_KEY, id);
6156
+ return id;
6157
+ } catch {
6158
+ return "session";
6159
+ }
6160
+ };
6161
+ var sanitizeStoredSettings = (ownerWindow, settings) => {
6162
+ const supportsColor = (value) => value !== void 0 && ownerWindow.CSS?.supports("color", value) === true;
6163
+ return {
6164
+ ...settings,
6165
+ ...supportsColor(settings.highlightColor) ? {} : { highlightColor: void 0 },
6166
+ ...supportsColor(settings.guideColor) ? {} : { guideColor: void 0 }
6167
+ };
6168
+ };
4683
6169
  function MeasurerClient({
4684
6170
  highlightColor,
4685
6171
  guideColor,
4686
6172
  hoverHighlightEnabled,
4687
6173
  persistOnReload,
4688
6174
  portalTarget,
4689
- persistKey
6175
+ persistKey,
6176
+ colorPickerFormats,
6177
+ colorPickerClickFormat,
6178
+ snapEnabled: snapEnabledDefault,
6179
+ snapGuidesEnabled: snapGuidesEnabledDefault,
6180
+ selectNewGuideEnabled: selectNewGuideEnabledDefault,
6181
+ multiMeasureEnabled: multiMeasureEnabledDefault,
6182
+ guideStyle: guideStyleDefault,
6183
+ rulerSettings: rulerSettingsDefault,
6184
+ persistence,
6185
+ onPersistenceError
4690
6186
  }) {
4691
- const instanceIdRef = (0, import_react20.useRef)(null);
6187
+ const instanceIdRef = (0, import_react23.useRef)(null);
4692
6188
  if (instanceIdRef.current === null) {
4693
6189
  instanceIdRef.current = ++measurerInstanceCount;
4694
6190
  }
4695
- const storageKey = persistKey ?? (instanceIdRef.current === 1 ? "mesurer-state" : `mesurer-state-${instanceIdRef.current}`);
4696
6191
  const ownerDocument = portalTarget.ownerDocument ?? document;
4697
6192
  const ownerWindow = ownerDocument.defaultView ?? window;
4698
- const guideScrollRef = (0, import_react20.useRef)({
6193
+ const tabIdRef = (0, import_react23.useRef)(null);
6194
+ if (tabIdRef.current === null) tabIdRef.current = getTabId(ownerWindow);
6195
+ const storageKey = persistKey ?? (instanceIdRef.current === 1 ? `mesurer-state:${tabIdRef.current}` : `mesurer-state:${tabIdRef.current}:${instanceIdRef.current}`);
6196
+ const legacyStorageKey = persistKey ? void 0 : LEGACY_STORAGE_KEY;
6197
+ const guideScrollRef = (0, import_react23.useRef)({
4699
6198
  x: ownerWindow.scrollX,
4700
6199
  y: ownerWindow.scrollY
4701
6200
  });
4702
- const textInspectorRef = (0, import_react20.useRef)(null);
6201
+ const textInspectorRef = (0, import_react23.useRef)(null);
4703
6202
  if (!textInspectorRef.current) {
4704
6203
  textInspectorRef.current = createTextInspector({ portalTarget });
4705
6204
  }
4706
6205
  const textInspector = textInspectorRef.current;
4707
- const selectionRectRef = (0, import_react20.useRef)(null);
4708
- const toolbarRef = (0, import_react20.useRef)(null);
4709
- const selectionAnimationCleanupTimeoutRef = (0, import_react20.useRef)(null);
4710
- const guideDragRef = (0, import_react20.useRef)(null);
4711
- const guideUserSelectRef = (0, import_react20.useRef)(null);
4712
- const persistedState = (0, import_react20.useMemo)(() => {
4713
- if (!persistOnReload) return null;
4714
- try {
4715
- const stored = window.localStorage.getItem(storageKey);
4716
- if (!stored) return null;
4717
- const parsed = JSON.parse(stored);
4718
- if (!parsed || parsed.version !== 1) return null;
4719
- return parsed;
4720
- } catch {
4721
- return null;
4722
- }
4723
- }, [persistOnReload, storageKey]);
4724
- const enabledRef = (0, import_react20.useRef)(false);
4725
- const toolModeRef = (0, import_react20.useRef)(
6206
+ const selectionRectRef = (0, import_react23.useRef)(null);
6207
+ const toolbarRef = (0, import_react23.useRef)(null);
6208
+ const selectionAnimationCleanupTimeoutRef = (0, import_react23.useRef)(null);
6209
+ const guideDragRef = (0, import_react23.useRef)(null);
6210
+ const guideUserSelectRef = (0, import_react23.useRef)(null);
6211
+ const persistenceErrorHandlerRef = (0, import_react23.useRef)(onPersistenceError);
6212
+ persistenceErrorHandlerRef.current = onPersistenceError;
6213
+ const activePersistence = (0, import_react23.useMemo)(() => {
6214
+ const next = persistence ?? createLocalStoragePersistence(ownerWindow, storageKey, SETTINGS_STORAGE_KEY, legacyStorageKey);
6215
+ next.setErrorHandler?.((error) => persistenceErrorHandlerRef.current?.(error));
6216
+ return next;
6217
+ }, [legacyStorageKey, ownerWindow, persistence, storageKey]);
6218
+ const storedState = (0, import_react23.useMemo)(
6219
+ () => activePersistence.load(),
6220
+ [activePersistence]
6221
+ );
6222
+ const persistedState = persistOnReload || storedState?.settings.persistOnReload ? storedState?.workspace ?? null : null;
6223
+ const persistedSettings = sanitizeStoredSettings(ownerWindow, storedState?.settings ?? {});
6224
+ (0, import_react23.useEffect)(() => {
6225
+ return () => activePersistence.setErrorHandler?.(void 0);
6226
+ }, [activePersistence]);
6227
+ const enabledRef = (0, import_react23.useRef)(false);
6228
+ const toolModeRef = (0, import_react23.useRef)(
4726
6229
  persistedState?.toolMode === "rulers" ? "none" : persistedState?.toolMode ?? "none"
4727
6230
  );
4728
- const rulersVisibleRef = (0, import_react20.useRef)(
6231
+ const rulersVisibleRef = (0, import_react23.useRef)(
4729
6232
  persistedState?.rulersVisible ?? persistedState?.toolMode === "rulers"
4730
6233
  );
4731
- const guideOrientationRef = (0, import_react20.useRef)(
6234
+ const xrayVisibleRef = (0, import_react23.useRef)(persistedState?.xrayVisible ?? persistedState?.toolMode === "xray");
6235
+ const guideOrientationRef = (0, import_react23.useRef)(
4732
6236
  persistedState?.guideOrientation ?? "vertical"
4733
6237
  );
4734
- const measurementsRef = (0, import_react20.useRef)(
6238
+ const measurementsRef = (0, import_react23.useRef)(
4735
6239
  persistedState?.measurements ?? []
4736
6240
  );
4737
- const activeMeasurementRef = (0, import_react20.useRef)(
6241
+ const activeMeasurementRef = (0, import_react23.useRef)(
4738
6242
  persistedState?.activeMeasurement ?? null
4739
6243
  );
4740
- const heldDistancesRef = (0, import_react20.useRef)(
6244
+ const heldDistancesRef = (0, import_react23.useRef)(
4741
6245
  persistedState?.heldDistances ?? []
4742
6246
  );
4743
- const guidesRef = (0, import_react20.useRef)(persistedState?.guides ?? []);
4744
- const selectedGuideIdsRef = (0, import_react20.useRef)(
6247
+ const guidesRef = (0, import_react23.useRef)(persistedState?.guides ?? []);
6248
+ const selectedGuideIdsRef = (0, import_react23.useRef)(
4745
6249
  persistedState?.selectedGuideIds ?? []
4746
6250
  );
6251
+ const workspacePersistTimeoutRef = (0, import_react23.useRef)(null);
6252
+ const applyingExternalPersistenceRef = (0, import_react23.useRef)(false);
4747
6253
  const { overlayRef, selectedElementRef, hoverElementRef } = useOverlayRefs();
4748
6254
  const {
4749
6255
  selectionOriginRect,
@@ -4773,11 +6279,20 @@ function MeasurerClient({
4773
6279
  setRulersVisible,
4774
6280
  guidesEnabled,
4775
6281
  multiMeasureEnabled,
4776
- snapGuidesEnabled
6282
+ snapGuidesEnabled,
6283
+ setSnapGuidesEnabled,
6284
+ selectNewGuideEnabled,
6285
+ setSelectNewGuideEnabled,
6286
+ setSnapEnabled,
6287
+ setMultiMeasureEnabled
4777
6288
  } = useMeasureToggles({
4778
6289
  initialEnabled: persistedState?.enabled,
4779
6290
  initialToolMode: persistedState?.toolMode === "rulers" ? "none" : persistedState?.toolMode,
4780
- initialRulersVisible: persistedState?.rulersVisible ?? persistedState?.toolMode === "rulers"
6291
+ initialRulersVisible: persistedState?.rulersVisible ?? persistedState?.toolMode === "rulers",
6292
+ initialSnapEnabled: persistedSettings.snapEnabled ?? snapEnabledDefault,
6293
+ initialSnapGuidesEnabled: persistedSettings.snapGuidesEnabled ?? snapGuidesEnabledDefault,
6294
+ initialSelectNewGuideEnabled: persistedSettings.selectNewGuideEnabled ?? selectNewGuideEnabledDefault,
6295
+ initialMultiMeasureEnabled: persistedSettings.multiMeasureEnabled ?? multiMeasureEnabledDefault
4781
6296
  });
4782
6297
  const { start, setStart, end, setEnd, isDragging, setIsDragging } = useDragState();
4783
6298
  const {
@@ -4809,11 +6324,58 @@ function MeasurerClient({
4809
6324
  initialGuides: persistedState?.guides ?? [],
4810
6325
  initialSelectedGuideIds: persistedState?.selectedGuideIds ?? []
4811
6326
  });
4812
- const [toolbarActive, setToolbarActive] = (0, import_react20.useState)(true);
6327
+ const [toolbarActive, setToolbarActive] = (0, import_react23.useState)(true);
6328
+ const [colorPickerActive, setColorPickerActive] = (0, import_react23.useState)(false);
6329
+ const [colorPickerSample, setColorPickerSample] = (0, import_react23.useState)(null);
6330
+ const [colorPickerUnsupported, setColorPickerUnsupported] = (0, import_react23.useState)(false);
6331
+ const [settingsOpen, setSettingsOpen] = (0, import_react23.useState)(false);
6332
+ const [settingsHighlightColor, setSettingsHighlightColor] = (0, import_react23.useState)(
6333
+ persistedSettings.highlightColor ?? highlightColor
6334
+ );
6335
+ const [settingsGuideColor, setSettingsGuideColor] = (0, import_react23.useState)(
6336
+ persistedSettings.guideColor ?? guideColor
6337
+ );
6338
+ const [settingsHoverHighlight, setSettingsHoverHighlight] = (0, import_react23.useState)(
6339
+ persistedSettings.hoverHighlightEnabled ?? hoverHighlightEnabled
6340
+ );
6341
+ const [settingsPersistOnReload, setSettingsPersistOnReload] = (0, import_react23.useState)(
6342
+ persistedSettings.persistOnReload ?? persistOnReload
6343
+ );
6344
+ const [settingsColorFormats, setSettingsColorFormats] = (0, import_react23.useState)(
6345
+ persistedSettings.colorPickerFormats ?? colorPickerFormats
6346
+ );
6347
+ const [settingsColorClickFormat, setSettingsColorClickFormat] = (0, import_react23.useState)(
6348
+ persistedSettings.colorPickerClickFormat ?? colorPickerClickFormat
6349
+ );
6350
+ const [settingsGuideStyle, setSettingsGuideStyle] = (0, import_react23.useState)({
6351
+ ...guideStyleDefault,
6352
+ ...persistedSettings.guideStyle
6353
+ });
6354
+ const [settingsRulerSettings, setSettingsRulerSettings] = (0, import_react23.useState)({
6355
+ ...rulerSettingsDefault,
6356
+ ...persistedSettings.rulerSettings
6357
+ });
6358
+ const [xrayVisible, setXrayVisible] = (0, import_react23.useState)(xrayVisibleRef.current);
4813
6359
  const { clearGuideDragHold, scheduleGuideDragHold } = useGuideDragHold(ownerWindow);
4814
- const [guidePreview, setGuidePreview] = (0, import_react20.useState)(null);
4815
- const [guideOrientation, setGuideOrientation] = (0, import_react20.useState)(persistedState?.guideOrientation ?? "vertical");
6360
+ const [guidePreview, setGuidePreview] = (0, import_react23.useState)(null);
6361
+ const resetSettings = () => {
6362
+ setSettingsHighlightColor(highlightColor);
6363
+ setSettingsGuideColor(guideColor);
6364
+ setSettingsHoverHighlight(hoverHighlightEnabled);
6365
+ setSettingsPersistOnReload(persistOnReload);
6366
+ setSettingsColorFormats([...colorPickerFormats]);
6367
+ setSettingsColorClickFormat(colorPickerClickFormat);
6368
+ setSnapEnabled(snapEnabledDefault);
6369
+ setSnapGuidesEnabled(snapGuidesEnabledDefault);
6370
+ setSelectNewGuideEnabled(selectNewGuideEnabledDefault);
6371
+ setMultiMeasureEnabled(multiMeasureEnabledDefault);
6372
+ setSettingsGuideStyle({ ...guideStyleDefault });
6373
+ setSettingsRulerSettings({ ...rulerSettingsDefault });
6374
+ };
6375
+ const initialSettingsTab = colorPickerActive ? "color-picker" : rulersVisible ? "rulers" : toolMode === "guides" ? "guides" : toolMode === "select" || toolMode === "text-inspector" ? "select" : "general";
6376
+ const [guideOrientation, setGuideOrientation] = (0, import_react23.useState)(persistedState?.guideOrientation ?? "vertical");
4816
6377
  enabledRef.current = enabled;
6378
+ xrayVisibleRef.current = xrayVisible;
4817
6379
  toolModeRef.current = toolMode;
4818
6380
  rulersVisibleRef.current = rulersVisible;
4819
6381
  guideOrientationRef.current = guideOrientation;
@@ -4822,29 +6384,173 @@ function MeasurerClient({
4822
6384
  heldDistancesRef.current = heldDistances;
4823
6385
  guidesRef.current = guides;
4824
6386
  selectedGuideIdsRef.current = selectedGuideIds;
4825
- const persistState = (0, import_react20.useCallback)(() => {
4826
- if (!persistOnReload) return;
4827
- if (typeof window === "undefined") return;
4828
- try {
4829
- window.localStorage.setItem(
4830
- storageKey,
4831
- JSON.stringify({
4832
- version: 1,
4833
- enabled: enabledRef.current,
4834
- toolMode: toolModeRef.current,
4835
- rulersVisible: rulersVisibleRef.current,
4836
- guideOrientation: guideOrientationRef.current,
4837
- guides: guidesRef.current,
4838
- selectedGuideIds: selectedGuideIdsRef.current,
4839
- measurements: measurementsRef.current.map(stripMeasurement),
4840
- activeMeasurement: activeMeasurementRef.current ? stripMeasurement(activeMeasurementRef.current) : null,
4841
- heldDistances: heldDistancesRef.current.map(stripDistance)
4842
- })
4843
- );
4844
- } catch {
6387
+ const saveWorkspace = (0, import_react23.useCallback)(() => {
6388
+ if (!settingsPersistOnReload) return;
6389
+ const workspace = {
6390
+ enabled: enabledRef.current,
6391
+ xrayVisible: xrayVisibleRef.current,
6392
+ toolMode: toolModeRef.current,
6393
+ rulersVisible: rulersVisibleRef.current,
6394
+ guideOrientation: guideOrientationRef.current,
6395
+ guides: guidesRef.current,
6396
+ selectedGuideIds: selectedGuideIdsRef.current,
6397
+ measurements: measurementsRef.current.map(stripMeasurement),
6398
+ activeMeasurement: activeMeasurementRef.current ? stripMeasurement(activeMeasurementRef.current) : null,
6399
+ heldDistances: heldDistancesRef.current.map(stripDistance)
6400
+ };
6401
+ activePersistence.saveWorkspace(workspace);
6402
+ }, [activePersistence, settingsPersistOnReload]);
6403
+ const persistState = (0, import_react23.useCallback)(() => {
6404
+ if (!settingsPersistOnReload) return;
6405
+ if (workspacePersistTimeoutRef.current !== null) {
6406
+ ownerWindow.clearTimeout(workspacePersistTimeoutRef.current);
6407
+ }
6408
+ workspacePersistTimeoutRef.current = ownerWindow.setTimeout(() => {
6409
+ workspacePersistTimeoutRef.current = null;
6410
+ saveWorkspace();
6411
+ }, 250);
6412
+ }, [ownerWindow, saveWorkspace, settingsPersistOnReload]);
6413
+ const persistSettings = (0, import_react23.useCallback)(() => {
6414
+ activePersistence.saveSettings({
6415
+ highlightColor: settingsHighlightColor,
6416
+ guideColor: settingsGuideColor,
6417
+ hoverHighlightEnabled: settingsHoverHighlight,
6418
+ colorPickerFormats: settingsColorFormats,
6419
+ colorPickerClickFormat: settingsColorClickFormat,
6420
+ snapEnabled,
6421
+ snapGuidesEnabled,
6422
+ selectNewGuideEnabled,
6423
+ multiMeasureEnabled,
6424
+ persistOnReload: settingsPersistOnReload,
6425
+ guideStyle: settingsGuideStyle,
6426
+ rulerSettings: settingsRulerSettings
6427
+ });
6428
+ }, [
6429
+ multiMeasureEnabled,
6430
+ selectNewGuideEnabled,
6431
+ settingsColorClickFormat,
6432
+ settingsColorFormats,
6433
+ settingsGuideColor,
6434
+ settingsHighlightColor,
6435
+ settingsHoverHighlight,
6436
+ settingsPersistOnReload,
6437
+ snapEnabled,
6438
+ snapGuidesEnabled,
6439
+ activePersistence,
6440
+ settingsGuideStyle,
6441
+ settingsRulerSettings
6442
+ ]);
6443
+ (0, import_react23.useEffect)(() => {
6444
+ if (applyingExternalPersistenceRef.current) {
6445
+ applyingExternalPersistenceRef.current = false;
6446
+ return;
6447
+ }
6448
+ persistSettings();
6449
+ if (settingsPersistOnReload) persistState();
6450
+ }, [persistSettings, persistState, settingsPersistOnReload]);
6451
+ (0, import_react23.useEffect)(() => {
6452
+ if (!settingsPersistOnReload) return;
6453
+ const handleBeforeUnload = () => saveWorkspace();
6454
+ ownerWindow.addEventListener("beforeunload", handleBeforeUnload);
6455
+ return () => {
6456
+ ownerWindow.removeEventListener("beforeunload", handleBeforeUnload);
6457
+ if (workspacePersistTimeoutRef.current !== null) {
6458
+ ownerWindow.clearTimeout(workspacePersistTimeoutRef.current);
6459
+ workspacePersistTimeoutRef.current = null;
6460
+ saveWorkspace();
6461
+ }
6462
+ };
6463
+ }, [ownerWindow, saveWorkspace, settingsPersistOnReload]);
6464
+ const clearPersistedWorkspace = (0, import_react23.useCallback)(() => {
6465
+ enabledRef.current = false;
6466
+ toolModeRef.current = "none";
6467
+ rulersVisibleRef.current = false;
6468
+ xrayVisibleRef.current = false;
6469
+ guideOrientationRef.current = "vertical";
6470
+ measurementsRef.current = [];
6471
+ activeMeasurementRef.current = null;
6472
+ heldDistancesRef.current = [];
6473
+ guidesRef.current = [];
6474
+ selectedGuideIdsRef.current = [];
6475
+ setEnabled(false);
6476
+ setToolMode("none");
6477
+ setRulersVisible(false);
6478
+ setXrayVisible(false);
6479
+ setGuideOrientation("vertical");
6480
+ setMeasurements([]);
6481
+ setActiveMeasurement(null);
6482
+ setSelectedMeasurement(null);
6483
+ setSelectedMeasurements([]);
6484
+ setHeldDistances([]);
6485
+ setGuides([]);
6486
+ setSelectedGuideIds([]);
6487
+ }, [setActiveMeasurement, setEnabled, setGuideOrientation, setGuides, setHeldDistances, setMeasurements, setRulersVisible, setSelectedGuideIds, setSelectedMeasurement, setSelectedMeasurements, setToolMode]);
6488
+ const clearWorkspace = (0, import_react23.useCallback)(() => {
6489
+ clearPersistedWorkspace();
6490
+ activePersistence.clearWorkspace();
6491
+ }, [activePersistence, clearPersistedWorkspace]);
6492
+ const applyPersistedWorkspace = (0, import_react23.useCallback)((workspace) => {
6493
+ enabledRef.current = workspace.enabled;
6494
+ toolModeRef.current = workspace.toolMode;
6495
+ rulersVisibleRef.current = workspace.rulersVisible;
6496
+ xrayVisibleRef.current = workspace.xrayVisible;
6497
+ guideOrientationRef.current = workspace.guideOrientation;
6498
+ measurementsRef.current = workspace.measurements;
6499
+ activeMeasurementRef.current = workspace.activeMeasurement;
6500
+ heldDistancesRef.current = workspace.heldDistances;
6501
+ guidesRef.current = workspace.guides;
6502
+ selectedGuideIdsRef.current = workspace.selectedGuideIds;
6503
+ setEnabled(workspace.enabled);
6504
+ setToolMode(workspace.toolMode);
6505
+ setRulersVisible(workspace.rulersVisible);
6506
+ setXrayVisible(workspace.xrayVisible);
6507
+ setGuideOrientation(workspace.guideOrientation);
6508
+ setMeasurements(workspace.measurements);
6509
+ setActiveMeasurement(workspace.activeMeasurement);
6510
+ setGuides(workspace.guides);
6511
+ setSelectedGuideIds(workspace.selectedGuideIds);
6512
+ setHeldDistances(workspace.heldDistances);
6513
+ }, [setActiveMeasurement, setEnabled, setGuideOrientation, setGuides, setHeldDistances, setMeasurements, setRulersVisible, setSelectedGuideIds, setToolMode]);
6514
+ const applyPersistenceSnapshot = (0, import_react23.useCallback)((snapshot) => {
6515
+ if (!snapshot) {
6516
+ clearPersistedWorkspace();
6517
+ return;
6518
+ }
6519
+ applyingExternalPersistenceRef.current = true;
6520
+ const settings = sanitizeStoredSettings(ownerWindow, snapshot.settings);
6521
+ if (settings.highlightColor !== void 0) setSettingsHighlightColor(settings.highlightColor);
6522
+ if (settings.guideColor !== void 0) setSettingsGuideColor(settings.guideColor);
6523
+ if (settings.hoverHighlightEnabled !== void 0) setSettingsHoverHighlight(settings.hoverHighlightEnabled);
6524
+ if (settings.colorPickerFormats !== void 0) setSettingsColorFormats(settings.colorPickerFormats);
6525
+ if (settings.colorPickerClickFormat !== void 0) setSettingsColorClickFormat(settings.colorPickerClickFormat);
6526
+ if (settings.persistOnReload !== void 0) setSettingsPersistOnReload(settings.persistOnReload);
6527
+ if (settings.snapEnabled !== void 0) setSnapEnabled(settings.snapEnabled);
6528
+ if (settings.snapGuidesEnabled !== void 0) setSnapGuidesEnabled(settings.snapGuidesEnabled);
6529
+ if (settings.selectNewGuideEnabled !== void 0) setSelectNewGuideEnabled(settings.selectNewGuideEnabled);
6530
+ if (settings.multiMeasureEnabled !== void 0) setMultiMeasureEnabled(settings.multiMeasureEnabled);
6531
+ if (settings.guideStyle !== void 0) setSettingsGuideStyle({ ...guideStyleDefault, ...settings.guideStyle });
6532
+ if (settings.rulerSettings !== void 0) setSettingsRulerSettings({ ...rulerSettingsDefault, ...settings.rulerSettings });
6533
+ const workspace = snapshot.workspace;
6534
+ if (!workspace) {
6535
+ clearPersistedWorkspace();
6536
+ } else if (settings.persistOnReload ?? settingsPersistOnReload) {
6537
+ applyPersistedWorkspace(workspace);
4845
6538
  }
4846
- }, [persistOnReload, storageKey]);
4847
- const setEnabledPersisted = (0, import_react20.useCallback)(
6539
+ ownerWindow.setTimeout(() => {
6540
+ applyingExternalPersistenceRef.current = false;
6541
+ }, 0);
6542
+ }, [applyPersistedWorkspace, clearPersistedWorkspace, guideStyleDefault, ownerWindow, rulerSettingsDefault, setMultiMeasureEnabled, setSelectNewGuideEnabled, setSettingsColorClickFormat, setSettingsColorFormats, setSettingsGuideColor, setSettingsHighlightColor, setSettingsHoverHighlight, setSettingsPersistOnReload, setSnapEnabled, setSnapGuidesEnabled, settingsPersistOnReload]);
6543
+ const previousPersistenceRef = (0, import_react23.useRef)(activePersistence);
6544
+ (0, import_react23.useEffect)(() => {
6545
+ if (previousPersistenceRef.current === activePersistence) return;
6546
+ previousPersistenceRef.current = activePersistence;
6547
+ applyPersistenceSnapshot(storedState);
6548
+ }, [activePersistence, applyPersistenceSnapshot, storedState]);
6549
+ (0, import_react23.useEffect)(() => {
6550
+ const unsubscribe = activePersistence.subscribe?.(applyPersistenceSnapshot);
6551
+ return unsubscribe;
6552
+ }, [activePersistence, applyPersistenceSnapshot]);
6553
+ const setEnabledPersisted = (0, import_react23.useCallback)(
4848
6554
  (value) => {
4849
6555
  const next = typeof value === "function" ? value(enabledRef.current) : value;
4850
6556
  if (Object.is(next, enabledRef.current)) return;
@@ -4854,7 +6560,7 @@ function MeasurerClient({
4854
6560
  },
4855
6561
  [persistState, setEnabled]
4856
6562
  );
4857
- const setRulersVisiblePersisted = (0, import_react20.useCallback)(
6563
+ const setRulersVisiblePersisted = (0, import_react23.useCallback)(
4858
6564
  (value) => {
4859
6565
  const next = typeof value === "function" ? value(rulersVisibleRef.current) : value;
4860
6566
  if (Object.is(next, rulersVisibleRef.current)) return;
@@ -4864,7 +6570,7 @@ function MeasurerClient({
4864
6570
  },
4865
6571
  [persistState, setRulersVisible]
4866
6572
  );
4867
- const setToolModePersisted = (0, import_react20.useCallback)(
6573
+ const setToolModePersisted = (0, import_react23.useCallback)(
4868
6574
  (value) => {
4869
6575
  const next = typeof value === "function" ? value(toolModeRef.current) : value;
4870
6576
  if (Object.is(next, toolModeRef.current)) return;
@@ -4879,7 +6585,7 @@ function MeasurerClient({
4879
6585
  },
4880
6586
  [persistState, setToolMode, textInspector]
4881
6587
  );
4882
- const setGuideOrientationPersisted = (0, import_react20.useCallback)(
6588
+ const setGuideOrientationPersisted = (0, import_react23.useCallback)(
4883
6589
  (value) => {
4884
6590
  const next = typeof value === "function" ? value(guideOrientationRef.current) : value;
4885
6591
  if (Object.is(next, guideOrientationRef.current)) return;
@@ -4889,7 +6595,7 @@ function MeasurerClient({
4889
6595
  },
4890
6596
  [persistState, setGuideOrientation]
4891
6597
  );
4892
- const setMeasurementsPersisted = (0, import_react20.useCallback)(
6598
+ const setMeasurementsPersisted = (0, import_react23.useCallback)(
4893
6599
  (value) => {
4894
6600
  const next = typeof value === "function" ? value(measurementsRef.current) : value;
4895
6601
  if (Object.is(next, measurementsRef.current)) return;
@@ -4899,7 +6605,7 @@ function MeasurerClient({
4899
6605
  },
4900
6606
  [persistState, setMeasurements]
4901
6607
  );
4902
- const setActiveMeasurementPersisted = (0, import_react20.useCallback)(
6608
+ const setActiveMeasurementPersisted = (0, import_react23.useCallback)(
4903
6609
  (value) => {
4904
6610
  const next = typeof value === "function" ? value(activeMeasurementRef.current) : value;
4905
6611
  if (Object.is(next, activeMeasurementRef.current)) return;
@@ -4909,7 +6615,7 @@ function MeasurerClient({
4909
6615
  },
4910
6616
  [persistState, setActiveMeasurement]
4911
6617
  );
4912
- const setHeldDistancesPersisted = (0, import_react20.useCallback)(
6618
+ const setHeldDistancesPersisted = (0, import_react23.useCallback)(
4913
6619
  (value) => {
4914
6620
  const next = typeof value === "function" ? value(heldDistancesRef.current) : value;
4915
6621
  if (Object.is(next, heldDistancesRef.current)) return;
@@ -4919,7 +6625,7 @@ function MeasurerClient({
4919
6625
  },
4920
6626
  [persistState, setHeldDistances]
4921
6627
  );
4922
- const setGuidesPersisted = (0, import_react20.useCallback)(
6628
+ const setGuidesPersisted = (0, import_react23.useCallback)(
4923
6629
  (value) => {
4924
6630
  const next = typeof value === "function" ? value(guidesRef.current) : value;
4925
6631
  if (Object.is(next, guidesRef.current)) return;
@@ -4929,7 +6635,7 @@ function MeasurerClient({
4929
6635
  },
4930
6636
  [persistState, setGuides]
4931
6637
  );
4932
- const setSelectedGuideIdsPersisted = (0, import_react20.useCallback)(
6638
+ const setSelectedGuideIdsPersisted = (0, import_react23.useCallback)(
4933
6639
  (value) => {
4934
6640
  const next = typeof value === "function" ? value(selectedGuideIdsRef.current) : value;
4935
6641
  if (Object.is(next, selectedGuideIdsRef.current)) return;
@@ -4987,15 +6693,15 @@ function MeasurerClient({
4987
6693
  clearSelectionRect
4988
6694
  }
4989
6695
  });
4990
- const undo = (0, import_react20.useCallback)(() => {
6696
+ const undo = (0, import_react23.useCallback)(() => {
4991
6697
  if (toolMode === "text-inspector" && textInspector.undo()) return;
4992
6698
  undoHistory();
4993
6699
  }, [textInspector, toolMode, undoHistory]);
4994
- const redo = (0, import_react20.useCallback)(() => {
6700
+ const redo = (0, import_react23.useCallback)(() => {
4995
6701
  if (toolMode === "text-inspector" && textInspector.redo()) return;
4996
6702
  redoHistory();
4997
6703
  }, [redoHistory, textInspector, toolMode]);
4998
- const clearAll = (0, import_react20.useCallback)(() => {
6704
+ const clearAll = (0, import_react23.useCallback)(() => {
4999
6705
  if (toolMode === "text-inspector") {
5000
6706
  textInspector.clear();
5001
6707
  }
@@ -5035,7 +6741,7 @@ function MeasurerClient({
5035
6741
  textInspector,
5036
6742
  toolMode
5037
6743
  ]);
5038
- const removeSelectedGuides = (0, import_react20.useCallback)(() => {
6744
+ const removeSelectedGuides = (0, import_react23.useCallback)(() => {
5039
6745
  if (selectedGuideIds.length === 0) return false;
5040
6746
  recordSnapshot();
5041
6747
  setGuidesPersisted(
@@ -5049,6 +6755,29 @@ function MeasurerClient({
5049
6755
  setGuidesPersisted,
5050
6756
  setSelectedGuideIdsPersisted
5051
6757
  ]);
6758
+ const openColorPicker = (0, import_react23.useCallback)(async () => {
6759
+ const EyeDropper = ownerWindow.EyeDropper;
6760
+ setEnabledWithHistory(true);
6761
+ setToolModeWithHistory("none");
6762
+ setColorPickerActive(true);
6763
+ setColorPickerSample(null);
6764
+ setColorPickerUnsupported(!EyeDropper);
6765
+ if (!EyeDropper) return;
6766
+ try {
6767
+ const result = await new EyeDropper().open();
6768
+ const nextSample = parseCssColor(result.sRGBHex);
6769
+ if (!nextSample) return;
6770
+ setColorPickerSample(nextSample);
6771
+ const clipboardWrite = ownerWindow.navigator.clipboard?.writeText(
6772
+ formatColor(nextSample, settingsColorClickFormat)
6773
+ );
6774
+ void clipboardWrite?.catch(() => void 0);
6775
+ } catch (error) {
6776
+ if (error instanceof DOMException && error.name === "AbortError") {
6777
+ setColorPickerActive(false);
6778
+ }
6779
+ }
6780
+ }, [ownerWindow, setEnabledWithHistory, setToolModeWithHistory, settingsColorClickFormat]);
5052
6781
  useHotkeys({
5053
6782
  eventTarget: ownerWindow,
5054
6783
  clearAll,
@@ -5061,7 +6790,13 @@ function MeasurerClient({
5061
6790
  setAltPressed,
5062
6791
  isOverlayActive: () => enabled && (toolMode !== "none" || toolbarActive),
5063
6792
  setGuideOrientation: setGuideOrientationWithHistory,
5064
- onInteract: () => setToolbarActive(true)
6793
+ onInteract: () => setToolbarActive(true),
6794
+ onColorPicker: openColorPicker,
6795
+ onToggleXray: () => setXrayVisible((previous) => !previous),
6796
+ onToggleSettings: () => setSettingsOpen((previous) => !previous),
6797
+ isSettingsOpen: () => settingsOpen,
6798
+ onCloseColorPicker: () => setColorPickerActive(false),
6799
+ isColorPickerActive: () => colorPickerActive
5065
6800
  });
5066
6801
  useResizeSync({
5067
6802
  document: ownerDocument,
@@ -5073,7 +6808,7 @@ function MeasurerClient({
5073
6808
  setGuides: setGuidesPersisted,
5074
6809
  selectedElementRef
5075
6810
  });
5076
- (0, import_react20.useEffect)(() => {
6811
+ (0, import_react23.useEffect)(() => {
5077
6812
  const handleScroll = () => {
5078
6813
  const next = {
5079
6814
  x: ownerWindow.scrollX,
@@ -5097,6 +6832,7 @@ function MeasurerClient({
5097
6832
  document: ownerDocument,
5098
6833
  window: ownerWindow,
5099
6834
  enabled,
6835
+ selectionEnabled: toolMode === "select" && !settingsOpen,
5100
6836
  selectedElementRef,
5101
6837
  hoverElementRef,
5102
6838
  setSelectedMeasurement,
@@ -5106,7 +6842,7 @@ function MeasurerClient({
5106
6842
  setActiveMeasurement: setActiveMeasurementPersisted,
5107
6843
  setHeldDistances: setHeldDistancesPersisted
5108
6844
  });
5109
- (0, import_react20.useEffect)(() => {
6845
+ (0, import_react23.useEffect)(() => {
5110
6846
  if (!toolbarActive || toolMode !== "none") return;
5111
6847
  const handlePointerDown2 = (event) => {
5112
6848
  const toolbarNode = toolbarRef.current;
@@ -5118,9 +6854,10 @@ function MeasurerClient({
5118
6854
  ownerWindow.removeEventListener("pointerdown", handlePointerDown2);
5119
6855
  };
5120
6856
  }, [ownerWindow, toolbarActive, toolMode]);
5121
- (0, import_react20.useEffect)(() => {
6857
+ (0, import_react23.useEffect)(() => {
5122
6858
  if (!enabled) return;
5123
6859
  const handleGuidePointerDown2 = (event) => {
6860
+ if (settingsOpen) return;
5124
6861
  if (toolbarRef.current?.contains(event.target)) return;
5125
6862
  const guideTarget = event.composedPath().some(
5126
6863
  (target) => target instanceof ownerWindow.Element && target.hasAttribute("data-mesurer-guide")
@@ -5203,16 +6940,30 @@ function MeasurerClient({
5203
6940
  ownerWindow,
5204
6941
  setGuidesPersisted,
5205
6942
  setSelectedGuideIdsPersisted,
6943
+ settingsOpen,
5206
6944
  toolMode
5207
6945
  ]);
5208
- (0, import_react20.useEffect)(() => {
5209
- if (toolMode === "text-inspector") {
6946
+ (0, import_react23.useEffect)(() => {
6947
+ if (toolMode === "text-inspector" && !settingsOpen) {
5210
6948
  textInspector.enable();
5211
6949
  } else {
5212
6950
  textInspector.disable();
5213
6951
  }
5214
- }, [textInspector, toolMode]);
5215
- (0, import_react20.useEffect)(() => {
6952
+ }, [settingsOpen, textInspector, toolMode]);
6953
+ const selectionContextRef = (0, import_react23.useRef)({ toolMode, settingsOpen });
6954
+ if (selectionContextRef.current.toolMode !== toolMode || selectionContextRef.current.settingsOpen !== settingsOpen) {
6955
+ selectionContextRef.current = { toolMode, settingsOpen };
6956
+ if (!(toolMode === "select" && !settingsOpen)) {
6957
+ setSelectedElement(null);
6958
+ setHoverElement(null);
6959
+ setHoverRect(null);
6960
+ setHoverPointer(null);
6961
+ setSelectedMeasurement(null);
6962
+ setSelectedMeasurements([]);
6963
+ clearSelectionRect();
6964
+ }
6965
+ }
6966
+ (0, import_react23.useEffect)(() => {
5216
6967
  let style = ownerDocument.getElementById(XRAY_STYLE_ID);
5217
6968
  if (!style) {
5218
6969
  style = ownerDocument.createElement("style");
@@ -5220,7 +6971,7 @@ function MeasurerClient({
5220
6971
  style.textContent = XRAY_STYLES;
5221
6972
  ownerDocument.head.appendChild(style);
5222
6973
  }
5223
- if (toolMode === "xray") {
6974
+ if (xrayVisible) {
5224
6975
  ownerDocument.body.classList.add("xray-mode");
5225
6976
  } else {
5226
6977
  ownerDocument.body.classList.remove("xray-mode");
@@ -5228,13 +6979,13 @@ function MeasurerClient({
5228
6979
  return () => {
5229
6980
  ownerDocument.body.classList.remove("xray-mode");
5230
6981
  };
5231
- }, [ownerDocument, toolMode]);
5232
- (0, import_react20.useEffect)(() => {
6982
+ }, [ownerDocument, xrayVisible]);
6983
+ (0, import_react23.useEffect)(() => {
5233
6984
  return () => {
5234
6985
  textInspector.destroy();
5235
6986
  };
5236
6987
  }, [textInspector]);
5237
- (0, import_react20.useEffect)(() => {
6988
+ (0, import_react23.useEffect)(() => {
5238
6989
  const hasSelectionAnimationState = !!selectionOriginRect || !!selectedMeasurement?.originRect || selectedMeasurements.some((measurement) => !!measurement.originRect);
5239
6990
  if (!hasSelectionAnimationState) {
5240
6991
  if (selectionAnimationCleanupTimeoutRef.current !== null) {
@@ -5315,9 +7066,9 @@ function MeasurerClient({
5315
7066
  guidesEnabled,
5316
7067
  guidePreview,
5317
7068
  displayedMeasurements,
5318
- hoverHighlightEnabled,
5319
- highlightColor,
5320
- guideColor
7069
+ hoverHighlightEnabled: settingsHoverHighlight,
7070
+ highlightColor: settingsHighlightColor,
7071
+ guideColor: settingsGuideColor
5321
7072
  });
5322
7073
  const {
5323
7074
  handlePointerDown,
@@ -5334,10 +7085,12 @@ function MeasurerClient({
5334
7085
  clearGuideDragHold,
5335
7086
  scheduleGuideDragHold,
5336
7087
  enabled,
7088
+ settingsOpen,
5337
7089
  toolMode,
5338
7090
  guidesEnabled,
5339
7091
  snapEnabled,
5340
7092
  snapGuidesEnabled,
7093
+ selectNewGuideEnabled,
5341
7094
  altPressed,
5342
7095
  guideOrientation,
5343
7096
  hoverHighlightEnabled,
@@ -5370,7 +7123,7 @@ function MeasurerClient({
5370
7123
  setHoverPointer,
5371
7124
  clearSelectionRect
5372
7125
  });
5373
- const removeHeldDistance = (0, import_react20.useCallback)(
7126
+ const removeHeldDistance = (0, import_react23.useCallback)(
5374
7127
  (id) => {
5375
7128
  recordSnapshot();
5376
7129
  setHeldDistancesPersisted(
@@ -5379,42 +7132,62 @@ function MeasurerClient({
5379
7132
  },
5380
7133
  [recordSnapshot, setHeldDistancesPersisted]
5381
7134
  );
5382
- const startGuideFromRuler = (0, import_react20.useCallback)(
7135
+ const snapGuidePosition = (0, import_react23.useCallback)(
7136
+ (orientation, position, draggingGuideId2 = null) => getSnapGuidePosition({
7137
+ orientation,
7138
+ point: orientation === "vertical" ? { x: position, y: 0 } : { x: 0, y: position },
7139
+ snapGuidesEnabled,
7140
+ overlayNode: overlayRef.current,
7141
+ guides,
7142
+ draggingGuideId: draggingGuideId2,
7143
+ document: ownerDocument
7144
+ }),
7145
+ [guides, ownerDocument, overlayRef, snapGuidesEnabled]
7146
+ );
7147
+ const startGuideFromRuler = (0, import_react23.useCallback)(
5383
7148
  (orientation, position) => {
5384
7149
  const id = createId();
5385
7150
  const commit = createActionCommit();
5386
7151
  commit();
5387
7152
  setSelectedGuideIdsPersisted([]);
5388
- setGuidesPersisted((prev) => [...prev, { id, orientation, position }]);
7153
+ setGuidesPersisted((prev) => [
7154
+ ...prev,
7155
+ { id, orientation, position: snapGuidePosition(orientation, position) }
7156
+ ]);
5389
7157
  return id;
5390
7158
  },
5391
7159
  [
5392
7160
  createActionCommit,
5393
7161
  setGuidesPersisted,
5394
- setSelectedGuideIdsPersisted
7162
+ setSelectedGuideIdsPersisted,
7163
+ snapGuidePosition
5395
7164
  ]
5396
7165
  );
5397
- const moveGuideFromRuler = (0, import_react20.useCallback)(
7166
+ const moveGuideFromRuler = (0, import_react23.useCallback)(
5398
7167
  (id, position) => {
5399
7168
  setGuidesPersisted(
5400
- (prev) => prev.map((guide) => guide.id === id ? { ...guide, position } : guide)
7169
+ (prev) => prev.map(
7170
+ (guide) => guide.id === id ? { ...guide, position: snapGuidePosition(guide.orientation, position, id) } : guide
7171
+ )
5401
7172
  );
5402
7173
  },
5403
- [setGuidesPersisted]
7174
+ [setGuidesPersisted, snapGuidePosition]
5404
7175
  );
5405
- const finishGuideFromRuler = (0, import_react20.useCallback)(
7176
+ const finishGuideFromRuler = (0, import_react23.useCallback)(
5406
7177
  (id) => {
5407
- setSelectedGuideIdsPersisted([id]);
7178
+ if (selectNewGuideEnabled) {
7179
+ setSelectedGuideIdsPersisted([id]);
7180
+ }
5408
7181
  },
5409
- [setSelectedGuideIdsPersisted]
7182
+ [selectNewGuideEnabled, setSelectedGuideIdsPersisted]
5410
7183
  );
5411
- const cancelGuideFromRuler = (0, import_react20.useCallback)(
7184
+ const cancelGuideFromRuler = (0, import_react23.useCallback)(
5412
7185
  (id) => {
5413
7186
  setGuidesPersisted((prev) => prev.filter((guide) => guide.id !== id));
5414
7187
  },
5415
7188
  [setGuidesPersisted]
5416
7189
  );
5417
- const handleGuidePointerDown = (0, import_react20.useCallback)(
7190
+ const handleGuidePointerDown = (0, import_react23.useCallback)(
5418
7191
  (guide, event) => {
5419
7192
  const commit = createActionCommit();
5420
7193
  if (!enabled) return;
@@ -5440,7 +7213,7 @@ function MeasurerClient({
5440
7213
  setSelectedGuideIdsPersisted
5441
7214
  ]
5442
7215
  );
5443
- const handleGuidePointerUp = (0, import_react20.useCallback)(
7216
+ const handleGuidePointerUp = (0, import_react23.useCallback)(
5444
7217
  (guide, event) => {
5445
7218
  event.stopPropagation();
5446
7219
  clearGuideDragHold();
@@ -5451,16 +7224,19 @@ function MeasurerClient({
5451
7224
  },
5452
7225
  [clearGuideDragHold, setDraggingGuideId]
5453
7226
  );
7227
+ const overlayInteractive = enabled && !settingsOpen;
5454
7228
  return (0, import_react_dom.createPortal)(
5455
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
7229
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
5456
7230
  "div",
5457
7231
  {
5458
7232
  ref: overlayRef,
5459
7233
  className: "mesurer-root msr:pointer-events-none msr:fixed msr:inset-0 msr:z-50",
5460
7234
  children: [
5461
- enabled && rulersVisible ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
7235
+ enabled && rulersVisible && !settingsOpen ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
5462
7236
  RulersOverlay,
5463
7237
  {
7238
+ ownerWindow,
7239
+ settings: settingsRulerSettings,
5464
7240
  onStartGuide: startGuideFromRuler,
5465
7241
  onMoveGuide: moveGuideFromRuler,
5466
7242
  onFinishGuide: finishGuideFromRuler,
@@ -5469,12 +7245,13 @@ function MeasurerClient({
5469
7245
  selectedGuideIds
5470
7246
  }
5471
7247
  ) : null,
5472
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
7248
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
5473
7249
  MeasurerOverlay,
5474
7250
  {
5475
- enabled,
7251
+ enabled: enabled && (!settingsOpen || guides.length > 0),
7252
+ interactive: overlayInteractive,
5476
7253
  toolMode,
5477
- guidePointerEvents: enabled && (toolMode !== "none" || rulersVisible),
7254
+ guidePointerEvents: overlayInteractive && (toolMode !== "none" || rulersVisible),
5478
7255
  guidesEnabled,
5479
7256
  altPressed,
5480
7257
  isDragging,
@@ -5502,6 +7279,7 @@ function MeasurerClient({
5502
7279
  guideColorActive,
5503
7280
  guideColorHover,
5504
7281
  guideColorDefault,
7282
+ guideStyle: settingsGuideStyle,
5505
7283
  onPointerDown: handlePointerDown,
5506
7284
  onPointerMove: handlePointerMove,
5507
7285
  onPointerUp: handlePointerUp,
@@ -5512,7 +7290,20 @@ function MeasurerClient({
5512
7290
  onGuidePointerCancel: handleGuidePointerUp
5513
7291
  }
5514
7292
  ),
5515
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
7293
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
7294
+ ColorPicker,
7295
+ {
7296
+ active: colorPickerActive,
7297
+ sample: colorPickerSample,
7298
+ unsupported: colorPickerUnsupported,
7299
+ ownerWindow,
7300
+ toolbarRef,
7301
+ formats: settingsColorFormats,
7302
+ favoriteFormat: settingsColorClickFormat,
7303
+ onClose: () => setColorPickerActive(false)
7304
+ }
7305
+ ),
7306
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
5516
7307
  Toolbar,
5517
7308
  {
5518
7309
  ref: toolbarRef,
@@ -5520,11 +7311,45 @@ function MeasurerClient({
5520
7311
  toolMode,
5521
7312
  setEnabled: setEnabledWithHistory,
5522
7313
  setToolMode: setToolModeWithHistory,
7314
+ xrayVisible,
7315
+ setXrayVisible,
5523
7316
  rulersVisible,
5524
7317
  setRulersVisible: setRulersVisiblePersisted,
5525
7318
  guideOrientation,
5526
7319
  setGuideOrientation: setGuideOrientationWithHistory,
5527
- onInteract: () => setToolbarActive(true)
7320
+ onInteract: () => setToolbarActive(true),
7321
+ colorPickerActive,
7322
+ setColorPickerActive,
7323
+ onColorPickerClick: openColorPicker,
7324
+ settingsOpen,
7325
+ setSettingsOpen,
7326
+ highlightColor: settingsHighlightColor,
7327
+ setHighlightColor: setSettingsHighlightColor,
7328
+ guideColor: settingsGuideColor,
7329
+ setGuideColor: setSettingsGuideColor,
7330
+ hoverHighlight: settingsHoverHighlight,
7331
+ setHoverHighlight: setSettingsHoverHighlight,
7332
+ persistOnReload: settingsPersistOnReload,
7333
+ setPersistOnReload: setSettingsPersistOnReload,
7334
+ colorPickerFormats: settingsColorFormats,
7335
+ setColorPickerFormats: setSettingsColorFormats,
7336
+ colorPickerClickFormat: settingsColorClickFormat,
7337
+ setColorPickerClickFormat: setSettingsColorClickFormat,
7338
+ snapEnabled,
7339
+ setSnapEnabled,
7340
+ snapGuidesEnabled,
7341
+ setSnapGuidesEnabled,
7342
+ selectNewGuideEnabled,
7343
+ setSelectNewGuideEnabled,
7344
+ multiMeasureEnabled,
7345
+ setMultiMeasureEnabled,
7346
+ guideStyle: settingsGuideStyle,
7347
+ setGuideStyle: setSettingsGuideStyle,
7348
+ rulerSettings: settingsRulerSettings,
7349
+ setRulerSettings: setSettingsRulerSettings,
7350
+ initialSettingsTab,
7351
+ onResetSettings: resetSettings,
7352
+ onClearWorkspace: clearWorkspace
5528
7353
  }
5529
7354
  )
5530
7355
  ]
@@ -5539,14 +7364,24 @@ function Measurer({
5539
7364
  hoverHighlightEnabled = true,
5540
7365
  persistOnReload = false,
5541
7366
  portalTarget,
5542
- persistKey
7367
+ persistKey,
7368
+ colorPickerFormats = ["hex", "rgb", "oklch"],
7369
+ colorPickerClickFormat = "hex",
7370
+ snapEnabled = true,
7371
+ snapGuidesEnabled = true,
7372
+ selectNewGuideEnabled = true,
7373
+ multiMeasureEnabled = false,
7374
+ guideStyle,
7375
+ rulerSettings,
7376
+ persistence,
7377
+ onPersistenceError
5543
7378
  }) {
5544
7379
  if (typeof document !== "undefined") {
5545
7380
  ensureMeasurerStyles(MESURER_STYLES, portalTarget);
5546
7381
  }
5547
7382
  const hydrated = useHydrated();
5548
7383
  if (!hydrated) return null;
5549
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
7384
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
5550
7385
  MeasurerClient,
5551
7386
  {
5552
7387
  highlightColor,
@@ -5554,13 +7389,27 @@ function Measurer({
5554
7389
  hoverHighlightEnabled,
5555
7390
  persistOnReload,
5556
7391
  persistKey,
7392
+ colorPickerFormats,
7393
+ colorPickerClickFormat,
7394
+ snapEnabled,
7395
+ snapGuidesEnabled,
7396
+ selectNewGuideEnabled,
7397
+ multiMeasureEnabled,
7398
+ guideStyle: { ...DEFAULT_GUIDE_STYLE, ...guideStyle },
7399
+ rulerSettings: { ...DEFAULT_RULER_SETTINGS, ...rulerSettings },
7400
+ persistence,
7401
+ onPersistenceError,
5557
7402
  portalTarget: portalTarget ?? document.body
5558
7403
  }
5559
7404
  );
5560
7405
  }
5561
7406
  // Annotate the CommonJS export names for ESM import in node:
5562
7407
  0 && (module.exports = {
7408
+ MESURER_STORAGE_VERSION,
5563
7409
  Measurer,
5564
7410
  TextInspector,
5565
- createTextInspector
7411
+ createLocalStoragePersistence,
7412
+ createTextInspector,
7413
+ normalizeStoredSettings,
7414
+ normalizeStoredWorkspace
5566
7415
  });