ui-sniper 3.2.1 → 3.2.2

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/README.md CHANGED
@@ -39,12 +39,21 @@ The toolbar appears in the bottom-right corner. Click to activate, then click an
39
39
  - **Text selection** – Select text to annotate specific content
40
40
  - **Multi-select** – Drag to select multiple elements at once
41
41
  - **Area selection** – Drag to annotate any region, even empty space
42
- - **Animation pause** – Freeze all animations (CSS, JS, videos) to capture specific states
42
+ - **Animation pause (P)** – Freeze all animations (CSS, JS, videos) to capture specific states
43
+ - **X-Ray mode (X)** – Reveal invisible structure, bounding boxes, and alignments
44
+ - **Layout mode (L)** – Drag, drop, edit text, or draw directly on the screen
45
+ - **Detailed Help Panel** – In-app guide detailing how, why, and the value of each mode
43
46
  - **Structured output** – Copy markdown with selectors, positions, and context
44
47
  - **Programmatic access** – Callback prop for direct integration with tools
45
48
  - **Dark/light mode** – Toggle in settings, persists to localStorage
46
49
  - **Zero dependencies** – Pure CSS animations, no runtime libraries
47
50
 
51
+ ## What's New in v3.2
52
+ - **Rich Help Panel**: A new built-in help panel explaining feature usage and value propositions.
53
+ - **Improved Review Panel**: Hover states, distinct color actions (Delete, Copy, Mark Done), and layout fixes preventing overlap.
54
+ - **Enhanced Visual Feedback**: The toolbar now has a distinct active border, and capturing screenshots accurately respects visibility states.
55
+ - **Advanced X-Ray & Layout Tools**: Reposition elements on the fly or inspect padding/margins before writing feedback.
56
+
48
57
  ## Props
49
58
 
50
59
  | Prop | Type | Default | Description |
@@ -135,6 +144,6 @@ Full documentation at [hara-xy.com](https://hara-xy.com)
135
144
 
136
145
  ## License
137
146
 
138
- © 2026 Benji Taylor
147
+ © 2026 Shiva Patil
139
148
 
140
149
  Licensed under PolyForm Shield 1.0.0
package/dist/index.js CHANGED
@@ -3706,7 +3706,7 @@ var styles_module_default4 = classNames5;
3706
3706
 
3707
3707
  // src/components/xray-overlay/index.tsx
3708
3708
  var import_jsx_runtime7 = require("react/jsx-runtime");
3709
- function XRayOverlay({ isActive, isDarkMode }) {
3709
+ function XRayOverlay({ isActive, isDarkMode, isFrozen }) {
3710
3710
  const [hoveredNode, setHoveredNode] = (0, import_react4.useState)(null);
3711
3711
  const [stylesData, setStylesData] = (0, import_react4.useState)({});
3712
3712
  const [cursorPos, setCursorPos] = (0, import_react4.useState)({ x: 0, y: 0 });
@@ -3716,6 +3716,7 @@ function XRayOverlay({ isActive, isDarkMode }) {
3716
3716
  return;
3717
3717
  }
3718
3718
  const onMouseMove = (e) => {
3719
+ if (isFrozen) return;
3719
3720
  setCursorPos({ x: e.clientX, y: e.clientY });
3720
3721
  const target = e.target;
3721
3722
  if (target.closest("[data-ui-sniper-toolbar]") || target.closest("[data-ui-sniper-root]") || target.closest("[data-review-panel]")) {
@@ -3739,7 +3740,7 @@ function XRayOverlay({ isActive, isDarkMode }) {
3739
3740
  };
3740
3741
  window.addEventListener("mousemove", onMouseMove);
3741
3742
  return () => window.removeEventListener("mousemove", onMouseMove);
3742
- }, [isActive]);
3743
+ }, [isActive, isFrozen]);
3743
3744
  if (!isActive || !hoveredNode) return null;
3744
3745
  const rect = hoveredNode.getBoundingClientRect();
3745
3746
  return (0, import_react_dom2.createPortal)(
@@ -7879,20 +7880,6 @@ async function getDomCapture() {
7879
7880
  }
7880
7881
  }
7881
7882
  function findCaptureTarget(captureX, captureY, captureW, captureH) {
7882
- const cx = captureX + captureW / 2;
7883
- const cy = captureY + captureH / 2;
7884
- const elements = document.elementsFromPoint(cx, cy);
7885
- for (const el of elements) {
7886
- if (!(el instanceof HTMLElement)) continue;
7887
- if (el.hasAttribute("data-ui-sniper-root")) continue;
7888
- if (el.closest?.("[data-ui-sniper-root]")) continue;
7889
- if (el.tagName === "CANVAS") continue;
7890
- if (el === document.documentElement || el === document.body) continue;
7891
- const rect = el.getBoundingClientRect();
7892
- if (rect.left <= captureX + captureW * 0.1 && rect.top <= captureY + captureH * 0.1 && rect.right >= captureX + captureW * 0.9 && rect.bottom >= captureY + captureH * 0.9) {
7893
- return el;
7894
- }
7895
- }
7896
7883
  return document.body;
7897
7884
  }
7898
7885
  async function captureDomRegion(regionX, regionY, regionW, regionH, strokes, padding = 32, quality = 0.85, piiConfig) {
@@ -8948,7 +8935,7 @@ function SettingsPanel({
8948
8935
  /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("a", { className: styles_module_default11.settingsBrand, href: "https://hara-xy.com", target: "_blank", rel: "noopener noreferrer", children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("svg", { width: "72", height: "16", viewBox: "0 0 500 151", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("text", { x: "10", y: "110", fontFamily: "'Arial Black', 'Impact', sans-serif", fontSize: "105", fontWeight: "900", fontStyle: "italic", letterSpacing: "-2", fill: "currentColor", children: "UI SNIPER" }) }) }),
8949
8936
  /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("p", { className: styles_module_default11.settingsVersion, children: [
8950
8937
  "v",
8951
- "3.2.1"
8938
+ "3.2.2"
8952
8939
  ] }),
8953
8940
  /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
8954
8941
  "button",
@@ -10981,7 +10968,7 @@ function PageFeedbackToolbarCSS({
10981
10968
  });
10982
10969
  }, [effectiveReactMode]);
10983
10970
  (0, import_react11.useEffect)(() => {
10984
- if (!isActive || isDrawMode || isDesignMode || isXRayMode || showReviewPanel) return;
10971
+ if (!isActive || isDrawMode || isDesignMode || showReviewPanel) return;
10985
10972
  const handleClick = (e) => {
10986
10973
  if (justFinishedDragRef.current) {
10987
10974
  justFinishedDragRef.current = false;
@@ -11134,7 +11121,7 @@ function PageFeedbackToolbarCSS({
11134
11121
  };
11135
11122
  }, [isActive, pendingMultiSelectElements, createMultiSelectPendingAnnotation]);
11136
11123
  (0, import_react11.useEffect)(() => {
11137
- if (!isActive || pendingAnnotation || isDrawMode || isDesignMode || isXRayMode) return;
11124
+ if (!isActive || pendingAnnotation || isDrawMode || isDesignMode) return;
11138
11125
  const handleMouseDown = (e) => {
11139
11126
  const target = e.composedPath()[0] || e.target;
11140
11127
  if (closestCrossingShadow(target, "[data-feedback-toolbar]")) return;
@@ -11485,16 +11472,30 @@ function PageFeedbackToolbarCSS({
11485
11472
  }
11486
11473
  if (elToCapture) {
11487
11474
  try {
11488
- const rect = elToCapture.getBoundingClientRect();
11475
+ let targetRect = elToCapture.getBoundingClientRect();
11476
+ let currentEl = elToCapture;
11477
+ const MAX_W = Math.min(800, window.innerWidth - 40);
11478
+ const MAX_H = Math.min(600, window.innerHeight - 40);
11479
+ for (let i = 0; i < 4; i++) {
11480
+ if (!currentEl || !currentEl.parentElement) break;
11481
+ if (currentEl.parentElement === document.body) break;
11482
+ const parentRect = currentEl.parentElement.getBoundingClientRect();
11483
+ if (parentRect.width <= MAX_W && parentRect.height <= MAX_H) {
11484
+ targetRect = parentRect;
11485
+ currentEl = currentEl.parentElement;
11486
+ } else {
11487
+ break;
11488
+ }
11489
+ }
11489
11490
  const result = await captureDomRegion(
11490
- rect.left,
11491
- rect.top,
11492
- rect.width,
11493
- rect.height,
11491
+ targetRect.left,
11492
+ targetRect.top,
11493
+ targetRect.width,
11494
+ targetRect.height,
11494
11495
  [],
11495
11496
  // No strokes for standard annotation
11496
- 150,
11497
- // 150px padding to show surrounding context
11497
+ 48,
11498
+ // 48px extra padding around the logical parent
11498
11499
  0.8,
11499
11500
  piiConfig.enabled ? piiConfig : void 0
11500
11501
  );
@@ -12202,7 +12203,7 @@ function PageFeedbackToolbarCSS({
12202
12203
  copyOutput();
12203
12204
  }
12204
12205
  }
12205
- if (e.key === "x" || e.key === "X") {
12206
+ if ((e.key === "x" || e.key === "X") && e.shiftKey) {
12206
12207
  if (annotations.length > 0 || designPlacements.length > 0 || rearrangeState) {
12207
12208
  e.preventDefault();
12208
12209
  hideTooltipsUntilMouseLeave();
@@ -12210,6 +12211,24 @@ function PageFeedbackToolbarCSS({
12210
12211
  if (designPlacements.length > 0) setDesignPlacements([]);
12211
12212
  if (rearrangeState) setRearrangeState(null);
12212
12213
  }
12214
+ } else if ((e.key === "x" || e.key === "X") && !e.shiftKey) {
12215
+ e.preventDefault();
12216
+ hideTooltipsUntilMouseLeave();
12217
+ setIsXRayMode(!isXRayMode);
12218
+ }
12219
+ if (e.key === "l" || e.key === "L") {
12220
+ e.preventDefault();
12221
+ hideTooltipsUntilMouseLeave();
12222
+ if (isDesignMode) {
12223
+ closeDesignMode();
12224
+ } else {
12225
+ setIsDesignMode(true);
12226
+ }
12227
+ }
12228
+ if (e.key === "p" || e.key === "P") {
12229
+ e.preventDefault();
12230
+ hideTooltipsUntilMouseLeave();
12231
+ toggleFreeze();
12213
12232
  }
12214
12233
  if (e.key === "s" || e.key === "S") {
12215
12234
  const hasValidWebhook = isValidUrl(settings.webhookUrl) || isValidUrl(webhookUrl || "");
@@ -12238,7 +12257,8 @@ function PageFeedbackToolbarCSS({
12238
12257
  toggleFreeze,
12239
12258
  copyOutput,
12240
12259
  clearAll,
12241
- pendingMultiSelectElements
12260
+ pendingMultiSelectElements,
12261
+ isXRayMode
12242
12262
  ]);
12243
12263
  if (!mounted) return null;
12244
12264
  if (isToolbarHidden) return null;
@@ -12561,7 +12581,7 @@ function PageFeedbackToolbarCSS({
12561
12581
  ),
12562
12582
  /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("span", { className: styles_module_default6.buttonTooltip, children: [
12563
12583
  "Clear your notes",
12564
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: styles_module_default6.shortcut, children: "X" })
12584
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: styles_module_default6.shortcut, children: "Shift+X" })
12565
12585
  ] })
12566
12586
  ] }),
12567
12587
  /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: styles_module_default6.buttonWrapper, children: [
@@ -12810,7 +12830,7 @@ function PageFeedbackToolbarCSS({
12810
12830
  }
12811
12831
  }
12812
12832
  ),
12813
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(XRayOverlay, { isActive: isXRayMode, isDarkMode }),
12833
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(XRayOverlay, { isActive: isXRayMode, isDarkMode, isFrozen: !!(pendingAnnotation || editingAnnotation) }),
12814
12834
  (isDesignMode || designOverlayExiting) && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
12815
12835
  "div",
12816
12836
  {