mesurer 0.1.2 → 0.1.3

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/CHANGELOG.md CHANGED
@@ -1,5 +1,10 @@
1
1
  # mesurer
2
2
 
3
+ ## 0.1.3
4
+
5
+ - fix color settings input, copy, and paste updates in embedded apps and the Chrome extension
6
+ - scope extension keyboard isolation so it does not interfere with embedded Mesurer instances
7
+
3
8
  ## 0.1.2
4
9
 
5
10
  - add configurable keyboard shortcuts and toolbar minimization
package/dist/index.cjs CHANGED
@@ -91,7 +91,7 @@ var import_react2 = require("react");
91
91
  // package.json
92
92
  var package_default = {
93
93
  name: "mesurer",
94
- version: "0.1.2",
94
+ version: "0.1.3",
95
95
  description: "Inspect, annotate, and give feedback on any live interface",
96
96
  license: "MIT",
97
97
  author: "Julien Thibeaut",
@@ -1043,12 +1043,44 @@ function ColorField({ label, value, fallback, ownerWindow, onChange }) {
1043
1043
  const [alphaDraft, setAlphaDraft] = (0, import_react2.useState)(String(alphaValue));
1044
1044
  const [hexFocused, setHexFocused] = (0, import_react2.useState)(false);
1045
1045
  const [alphaFocused, setAlphaFocused] = (0, import_react2.useState)(false);
1046
+ const nativeColorRef = (0, import_react2.useRef)(null);
1047
+ const hexInputRef = (0, import_react2.useRef)(null);
1048
+ const alphaInputRef = (0, import_react2.useRef)(null);
1046
1049
  const updateColor = (nextHex, nextAlpha) => {
1047
1050
  if (!/^[\da-f]{6}$/i.test(nextHex)) return;
1048
1051
  const nextSample = parseCssColor(`#${nextHex}`);
1049
1052
  if (!nextSample) return;
1050
1053
  onChange(colorToHex({ ...nextSample, alpha: Math.min(100, Math.max(0, nextAlpha)) / 100 }));
1051
1054
  };
1055
+ const handleNativeColorInput = (input) => {
1056
+ updateColor(input.value.slice(1), alphaValue);
1057
+ };
1058
+ const handleHexInput = (input) => {
1059
+ const next = input.value.replace(/[^\da-f]/gi, "").slice(0, 6).toUpperCase();
1060
+ setHexDraft(next);
1061
+ updateColor(next, alphaFocused ? Number(alphaDraft) : alphaValue);
1062
+ };
1063
+ const handleAlphaInput = (input) => {
1064
+ const next = input.value.replace(/\D/g, "").slice(0, 3);
1065
+ setAlphaDraft(next);
1066
+ updateColor(hexFocused ? hexDraft : hexValue, Number(next));
1067
+ };
1068
+ (0, import_react2.useLayoutEffect)(() => {
1069
+ const nativeColor = nativeColorRef.current;
1070
+ const hexInput = hexInputRef.current;
1071
+ const alphaInput = alphaInputRef.current;
1072
+ if (!nativeColor || !hexInput || !alphaInput) return;
1073
+ nativeColor.oninput = () => handleNativeColorInput(nativeColor);
1074
+ nativeColor.onchange = () => handleNativeColorInput(nativeColor);
1075
+ hexInput.oninput = () => handleHexInput(hexInput);
1076
+ alphaInput.oninput = () => handleAlphaInput(alphaInput);
1077
+ return () => {
1078
+ nativeColor.oninput = null;
1079
+ nativeColor.onchange = null;
1080
+ hexInput.oninput = null;
1081
+ alphaInput.oninput = null;
1082
+ };
1083
+ });
1052
1084
  const swatchColor = ownerWindow.CSS?.supports("color", value) ? value : fallback;
1053
1085
  return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: `msr:col-span-2 msr:grid msr:h-8 msr:w-full ${SETTINGS_COLUMNS} msr:items-center msr:gap-0 msr:text-[12px] msr:text-ink-700`, children: [
1054
1086
  /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { children: label }),
@@ -1064,11 +1096,11 @@ function ColorField({ label, value, fallback, ownerWindow, onChange }) {
1064
1096
  children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
1065
1097
  "input",
1066
1098
  {
1099
+ ref: nativeColorRef,
1067
1100
  type: "color",
1068
1101
  "aria-label": `${label} color picker`,
1069
1102
  value: inputValue,
1070
- className: "msr:absolute msr:inset-0 msr:size-full msr:cursor-pointer msr:opacity-0",
1071
- onChange: (event) => onChange(event.target.value)
1103
+ className: "msr:absolute msr:inset-0 msr:size-full msr:cursor-pointer msr:opacity-0"
1072
1104
  }
1073
1105
  )
1074
1106
  }
@@ -1076,21 +1108,18 @@ function ColorField({ label, value, fallback, ownerWindow, onChange }) {
1076
1108
  /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
1077
1109
  "input",
1078
1110
  {
1111
+ ref: hexInputRef,
1079
1112
  "aria-label": `${label} hex value`,
1080
1113
  type: "text",
1081
1114
  value: hexFocused ? hexDraft : hexValue,
1082
- maxLength: 6,
1115
+ maxLength: 7,
1083
1116
  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",
1084
1117
  onFocus: () => {
1085
1118
  setHexDraft(hexValue);
1086
1119
  setHexFocused(true);
1087
1120
  },
1088
1121
  onBlur: () => setHexFocused(false),
1089
- onChange: (event) => {
1090
- const next = event.target.value.replace(/[^\da-f]/gi, "").slice(0, 6).toUpperCase();
1091
- setHexDraft(next);
1092
- updateColor(next, alphaFocused ? Number(alphaDraft) : alphaValue);
1093
- },
1122
+ onChange: (event) => handleHexInput(event.currentTarget),
1094
1123
  onPointerDown: (event) => event.stopPropagation(),
1095
1124
  onClick: (event) => event.stopPropagation()
1096
1125
  }
@@ -1099,6 +1128,7 @@ function ColorField({ label, value, fallback, ownerWindow, onChange }) {
1099
1128
  right: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
1100
1129
  "input",
1101
1130
  {
1131
+ ref: alphaInputRef,
1102
1132
  "aria-label": `${label} opacity value`,
1103
1133
  type: "text",
1104
1134
  inputMode: "numeric",
@@ -1110,11 +1140,7 @@ function ColorField({ label, value, fallback, ownerWindow, onChange }) {
1110
1140
  setAlphaFocused(true);
1111
1141
  },
1112
1142
  onBlur: () => setAlphaFocused(false),
1113
- onChange: (event) => {
1114
- const next = event.target.value.replace(/\D/g, "").slice(0, 3);
1115
- setAlphaDraft(next);
1116
- updateColor(hexFocused ? hexDraft : hexValue, Number(next));
1117
- },
1143
+ onChange: (event) => handleAlphaInput(event.currentTarget),
1118
1144
  onKeyDown: (event) => {
1119
1145
  if (event.key !== "ArrowUp" && event.key !== "ArrowDown") return;
1120
1146
  event.preventDefault();
@@ -7497,16 +7523,23 @@ var MESURER_EVENT_TYPES = /* @__PURE__ */ new Set([
7497
7523
  ]);
7498
7524
  var ownsKeyboard = (view) => view.document.documentElement.hasAttribute(MESURER_KEYBOARD_ATTR);
7499
7525
  var isMesurerEventPath = (event) => event.composedPath().some((node) => isMesurerUiNode(node));
7500
- var installKeyboardGate = (view = window, { isolateMesurerEvents = false } = {}) => {
7526
+ var installKeyboardGate = (view = window, {
7527
+ isolateMesurerEvents = false,
7528
+ isolationHostId
7529
+ } = {}) => {
7501
7530
  const gated = view;
7502
7531
  if (gated[INSTALLED]) return;
7503
7532
  gated[INSTALLED] = true;
7533
+ const isIsolatedMesurerEvent = (event) => !isolationHostId || event.composedPath().some(
7534
+ (node) => node instanceof Element && node.id === isolationHostId
7535
+ );
7504
7536
  const blockPageKey = (event) => {
7505
7537
  if (!ownsKeyboard(view)) {
7506
7538
  return;
7507
7539
  }
7508
7540
  const mesurerEvent = isMesurerKeyboardEvent(event, view);
7509
7541
  if (mesurerEvent) {
7542
+ if (isolateMesurerEvents && !isIsolatedMesurerEvent(event)) return;
7510
7543
  if (!isolateMesurerEvents || isTypingInMesurer(event, view)) {
7511
7544
  return;
7512
7545
  }
@@ -7615,7 +7648,7 @@ var installKeyboardGate = (view = window, { isolateMesurerEvents = false } = {})
7615
7648
  const once = typeof options !== "boolean" && Boolean(options?.once);
7616
7649
  const signal = typeof options !== "boolean" ? options?.signal : void 0;
7617
7650
  wrapped = function(event) {
7618
- if (ownsKeyboard(view) && isMesurerEventPath(event)) {
7651
+ if (ownsKeyboard(view) && isMesurerEventPath(event) && isIsolatedMesurerEvent(event)) {
7619
7652
  if (once && !signal?.aborted) {
7620
7653
  nativeAddEventListener.call(this, type, wrapped, options);
7621
7654
  }
package/dist/index.js CHANGED
@@ -66,7 +66,7 @@ import { useId, useLayoutEffect as useLayoutEffect2, useRef as useRef2, useState
66
66
  // package.json
67
67
  var package_default = {
68
68
  name: "mesurer",
69
- version: "0.1.2",
69
+ version: "0.1.3",
70
70
  description: "Inspect, annotate, and give feedback on any live interface",
71
71
  license: "MIT",
72
72
  author: "Julien Thibeaut",
@@ -1024,12 +1024,44 @@ function ColorField({ label, value, fallback, ownerWindow, onChange }) {
1024
1024
  const [alphaDraft, setAlphaDraft] = useState2(String(alphaValue));
1025
1025
  const [hexFocused, setHexFocused] = useState2(false);
1026
1026
  const [alphaFocused, setAlphaFocused] = useState2(false);
1027
+ const nativeColorRef = useRef2(null);
1028
+ const hexInputRef = useRef2(null);
1029
+ const alphaInputRef = useRef2(null);
1027
1030
  const updateColor = (nextHex, nextAlpha) => {
1028
1031
  if (!/^[\da-f]{6}$/i.test(nextHex)) return;
1029
1032
  const nextSample = parseCssColor(`#${nextHex}`);
1030
1033
  if (!nextSample) return;
1031
1034
  onChange(colorToHex({ ...nextSample, alpha: Math.min(100, Math.max(0, nextAlpha)) / 100 }));
1032
1035
  };
1036
+ const handleNativeColorInput = (input) => {
1037
+ updateColor(input.value.slice(1), alphaValue);
1038
+ };
1039
+ const handleHexInput = (input) => {
1040
+ const next = input.value.replace(/[^\da-f]/gi, "").slice(0, 6).toUpperCase();
1041
+ setHexDraft(next);
1042
+ updateColor(next, alphaFocused ? Number(alphaDraft) : alphaValue);
1043
+ };
1044
+ const handleAlphaInput = (input) => {
1045
+ const next = input.value.replace(/\D/g, "").slice(0, 3);
1046
+ setAlphaDraft(next);
1047
+ updateColor(hexFocused ? hexDraft : hexValue, Number(next));
1048
+ };
1049
+ useLayoutEffect2(() => {
1050
+ const nativeColor = nativeColorRef.current;
1051
+ const hexInput = hexInputRef.current;
1052
+ const alphaInput = alphaInputRef.current;
1053
+ if (!nativeColor || !hexInput || !alphaInput) return;
1054
+ nativeColor.oninput = () => handleNativeColorInput(nativeColor);
1055
+ nativeColor.onchange = () => handleNativeColorInput(nativeColor);
1056
+ hexInput.oninput = () => handleHexInput(hexInput);
1057
+ alphaInput.oninput = () => handleAlphaInput(alphaInput);
1058
+ return () => {
1059
+ nativeColor.oninput = null;
1060
+ nativeColor.onchange = null;
1061
+ hexInput.oninput = null;
1062
+ alphaInput.oninput = null;
1063
+ };
1064
+ });
1033
1065
  const swatchColor = ownerWindow.CSS?.supports("color", value) ? value : fallback;
1034
1066
  return /* @__PURE__ */ jsxs10("div", { className: `msr:col-span-2 msr:grid msr:h-8 msr:w-full ${SETTINGS_COLUMNS} msr:items-center msr:gap-0 msr:text-[12px] msr:text-ink-700`, children: [
1035
1067
  /* @__PURE__ */ jsx18("span", { children: label }),
@@ -1045,11 +1077,11 @@ function ColorField({ label, value, fallback, ownerWindow, onChange }) {
1045
1077
  children: /* @__PURE__ */ jsx18(
1046
1078
  "input",
1047
1079
  {
1080
+ ref: nativeColorRef,
1048
1081
  type: "color",
1049
1082
  "aria-label": `${label} color picker`,
1050
1083
  value: inputValue,
1051
- className: "msr:absolute msr:inset-0 msr:size-full msr:cursor-pointer msr:opacity-0",
1052
- onChange: (event) => onChange(event.target.value)
1084
+ className: "msr:absolute msr:inset-0 msr:size-full msr:cursor-pointer msr:opacity-0"
1053
1085
  }
1054
1086
  )
1055
1087
  }
@@ -1057,21 +1089,18 @@ function ColorField({ label, value, fallback, ownerWindow, onChange }) {
1057
1089
  /* @__PURE__ */ jsx18(
1058
1090
  "input",
1059
1091
  {
1092
+ ref: hexInputRef,
1060
1093
  "aria-label": `${label} hex value`,
1061
1094
  type: "text",
1062
1095
  value: hexFocused ? hexDraft : hexValue,
1063
- maxLength: 6,
1096
+ maxLength: 7,
1064
1097
  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",
1065
1098
  onFocus: () => {
1066
1099
  setHexDraft(hexValue);
1067
1100
  setHexFocused(true);
1068
1101
  },
1069
1102
  onBlur: () => setHexFocused(false),
1070
- onChange: (event) => {
1071
- const next = event.target.value.replace(/[^\da-f]/gi, "").slice(0, 6).toUpperCase();
1072
- setHexDraft(next);
1073
- updateColor(next, alphaFocused ? Number(alphaDraft) : alphaValue);
1074
- },
1103
+ onChange: (event) => handleHexInput(event.currentTarget),
1075
1104
  onPointerDown: (event) => event.stopPropagation(),
1076
1105
  onClick: (event) => event.stopPropagation()
1077
1106
  }
@@ -1080,6 +1109,7 @@ function ColorField({ label, value, fallback, ownerWindow, onChange }) {
1080
1109
  right: /* @__PURE__ */ jsx18(
1081
1110
  "input",
1082
1111
  {
1112
+ ref: alphaInputRef,
1083
1113
  "aria-label": `${label} opacity value`,
1084
1114
  type: "text",
1085
1115
  inputMode: "numeric",
@@ -1091,11 +1121,7 @@ function ColorField({ label, value, fallback, ownerWindow, onChange }) {
1091
1121
  setAlphaFocused(true);
1092
1122
  },
1093
1123
  onBlur: () => setAlphaFocused(false),
1094
- onChange: (event) => {
1095
- const next = event.target.value.replace(/\D/g, "").slice(0, 3);
1096
- setAlphaDraft(next);
1097
- updateColor(hexFocused ? hexDraft : hexValue, Number(next));
1098
- },
1124
+ onChange: (event) => handleAlphaInput(event.currentTarget),
1099
1125
  onKeyDown: (event) => {
1100
1126
  if (event.key !== "ArrowUp" && event.key !== "ArrowDown") return;
1101
1127
  event.preventDefault();
@@ -7489,16 +7515,23 @@ var MESURER_EVENT_TYPES = /* @__PURE__ */ new Set([
7489
7515
  ]);
7490
7516
  var ownsKeyboard = (view) => view.document.documentElement.hasAttribute(MESURER_KEYBOARD_ATTR);
7491
7517
  var isMesurerEventPath = (event) => event.composedPath().some((node) => isMesurerUiNode(node));
7492
- var installKeyboardGate = (view = window, { isolateMesurerEvents = false } = {}) => {
7518
+ var installKeyboardGate = (view = window, {
7519
+ isolateMesurerEvents = false,
7520
+ isolationHostId
7521
+ } = {}) => {
7493
7522
  const gated = view;
7494
7523
  if (gated[INSTALLED]) return;
7495
7524
  gated[INSTALLED] = true;
7525
+ const isIsolatedMesurerEvent = (event) => !isolationHostId || event.composedPath().some(
7526
+ (node) => node instanceof Element && node.id === isolationHostId
7527
+ );
7496
7528
  const blockPageKey = (event) => {
7497
7529
  if (!ownsKeyboard(view)) {
7498
7530
  return;
7499
7531
  }
7500
7532
  const mesurerEvent = isMesurerKeyboardEvent(event, view);
7501
7533
  if (mesurerEvent) {
7534
+ if (isolateMesurerEvents && !isIsolatedMesurerEvent(event)) return;
7502
7535
  if (!isolateMesurerEvents || isTypingInMesurer(event, view)) {
7503
7536
  return;
7504
7537
  }
@@ -7607,7 +7640,7 @@ var installKeyboardGate = (view = window, { isolateMesurerEvents = false } = {})
7607
7640
  const once = typeof options !== "boolean" && Boolean(options?.once);
7608
7641
  const signal = typeof options !== "boolean" ? options?.signal : void 0;
7609
7642
  wrapped = function(event) {
7610
- if (ownsKeyboard(view) && isMesurerEventPath(event)) {
7643
+ if (ownsKeyboard(view) && isMesurerEventPath(event) && isIsolatedMesurerEvent(event)) {
7611
7644
  if (once && !signal?.aborted) {
7612
7645
  nativeAddEventListener.call(this, type, wrapped, options);
7613
7646
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mesurer",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "Inspect, annotate, and give feedback on any live interface",
5
5
  "license": "MIT",
6
6
  "author": "Julien Thibeaut",
@@ -20,10 +20,10 @@
20
20
  "files": [
21
21
  "dist",
22
22
  "dist/styles.css",
23
- "CHANGELOG.md",
24
- "README.md",
25
- "PRIVACY.md",
26
- "LICENSE"
23
+ "CHANGELOG.md",
24
+ "README.md",
25
+ "PRIVACY.md",
26
+ "LICENSE"
27
27
  ],
28
28
  "sideEffects": [
29
29
  "*.css"
@@ -39,14 +39,6 @@
39
39
  "bugs": {
40
40
  "url": "https://github.com/ibelick/mesurer/issues"
41
41
  },
42
- "scripts": {
43
- "generate:styles": "pnpm exec tailwindcss -i styles.css -o styles.generated.css -m && node scripts/generate-styles.mjs",
44
- "build": "pnpm run generate:styles && tsup && pnpm exec tailwindcss -i styles.css -o dist/styles.css -m",
45
- "dev": "node scripts/dev.mjs",
46
- "pretypecheck": "pnpm run generate:styles",
47
- "typecheck": "tsc --noEmit",
48
- "prepublishOnly": "npm run typecheck && npm run build"
49
- },
50
42
  "peerDependencies": {
51
43
  "react": ">=18.0.0",
52
44
  "react-dom": ">=18.0.0"
@@ -64,5 +56,12 @@
64
56
  "keywords": [
65
57
  "measurement",
66
58
  "alignment"
67
- ]
68
- }
59
+ ],
60
+ "scripts": {
61
+ "generate:styles": "pnpm exec tailwindcss -i styles.css -o styles.generated.css -m && node scripts/generate-styles.mjs",
62
+ "build": "pnpm run generate:styles && tsup && pnpm exec tailwindcss -i styles.css -o dist/styles.css -m",
63
+ "dev": "node scripts/dev.mjs",
64
+ "pretypecheck": "pnpm run generate:styles",
65
+ "typecheck": "tsc --noEmit"
66
+ }
67
+ }