react-grab 0.0.29 → 0.0.31

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
@@ -4,6 +4,7 @@ var web = require('solid-js/web');
4
4
  var solidJs = require('solid-js');
5
5
  var bippy = require('bippy');
6
6
  var source = require('bippy/dist/source');
7
+ var finder = require('@medv/finder');
7
8
 
8
9
  /**
9
10
  * @license MIT
@@ -96,6 +97,7 @@ var VIEWPORT_MARGIN_PX = 8;
96
97
  var INDICATOR_CLAMP_PADDING_PX = 4;
97
98
  var CURSOR_OFFSET_PX = 14;
98
99
  var SELECTION_LERP_FACTOR = 0.95;
100
+ var SUCCESS_LABEL_DURATION_MS = 1700;
99
101
 
100
102
  // src/utils/lerp.ts
101
103
  var lerp = (start, end, factor) => {
@@ -258,9 +260,9 @@ var getClampedElementPosition = (positionLeft, positionTop, elementWidth, elemen
258
260
  // src/components/label.tsx
259
261
  var _tmpl$3 = /* @__PURE__ */ web.template(`<span style=display:inline-block;margin-right:4px;font-weight:600>\u2713`);
260
262
  var _tmpl$22 = /* @__PURE__ */ web.template(`<div style=margin-right:4px>Copied`);
261
- var _tmpl$32 = /* @__PURE__ */ web.template(`<div style=margin-left:4px>to clipboard`);
262
- var _tmpl$4 = /* @__PURE__ */ web.template(`<div style="position:fixed;padding:2px 6px;background-color:#fde7f7;color:#b21c8e;border:1px solid #f7c5ec;border-radius:4px;font-size:11px;font-weight:500;font-family:-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;pointer-events:none;transition:opacity 0.2s ease-in-out;display:flex;align-items:center;max-width:calc(100vw - (16px + env(safe-area-inset-left) + env(safe-area-inset-right)));overflow:hidden;text-overflow:ellipsis;white-space:nowrap">`);
263
- var _tmpl$5 = /* @__PURE__ */ web.template(`<span style="font-family:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace;font-variant-numeric:tabular-nums">`);
263
+ var _tmpl$32 = /* @__PURE__ */ web.template(`<span style="font-family:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace;font-variant-numeric:tabular-nums">`);
264
+ var _tmpl$4 = /* @__PURE__ */ web.template(`<div style=margin-left:4px>to clipboard`);
265
+ var _tmpl$5 = /* @__PURE__ */ web.template(`<div style="position:fixed;padding:2px 6px;background-color:#fde7f7;color:#b21c8e;border:1px solid #f7c5ec;border-radius:4px;font-size:11px;font-weight:500;font-family:-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;pointer-events:none;transition:opacity 0.2s ease-in-out;display:flex;align-items:center;max-width:calc(100vw - (16px + env(safe-area-inset-left) + env(safe-area-inset-right)));overflow:hidden;text-overflow:ellipsis;white-space:nowrap">`);
264
266
  var Label = (props) => {
265
267
  const [opacity, setOpacity] = solidJs.createSignal(0);
266
268
  const [positionTick, setPositionTick] = solidJs.createSignal(0);
@@ -310,7 +312,7 @@ var Label = (props) => {
310
312
  if (props.variant === "success") {
311
313
  const fadeOutTimer = setTimeout(() => {
312
314
  setOpacity(0);
313
- }, 1500);
315
+ }, SUCCESS_LABEL_DURATION_MS);
314
316
  solidJs.onCleanup(() => clearTimeout(fadeOutTimer));
315
317
  }
316
318
  }));
@@ -331,34 +333,20 @@ var Label = (props) => {
331
333
  left: currentX,
332
334
  top: currentY
333
335
  };
334
- if (props.variant === "success") {
335
- const indicatorLeft = Math.round(currentX);
336
- const indicatorTop = Math.round(currentY) - boundingRect.height - 6;
337
- const willClampLeft = indicatorLeft < VIEWPORT_MARGIN_PX;
338
- const willClampTop = indicatorTop < VIEWPORT_MARGIN_PX;
339
- const isClamped = willClampLeft || willClampTop;
340
- const clamped = getClampedElementPosition(indicatorLeft, indicatorTop, boundingRect.width, boundingRect.height);
341
- if (isClamped) {
342
- clamped.left += INDICATOR_CLAMP_PADDING_PX;
343
- clamped.top += INDICATOR_CLAMP_PADDING_PX;
344
- }
345
- return clamped;
346
- }
347
- const CROSSHAIR_OFFSET = 12;
348
336
  const viewportWidth = window.innerWidth;
349
337
  const viewportHeight = window.innerHeight;
350
338
  const quadrants = [{
351
- left: Math.round(currentX) + CROSSHAIR_OFFSET,
352
- top: Math.round(currentY) + CROSSHAIR_OFFSET
339
+ left: Math.round(currentX) + CURSOR_OFFSET_PX,
340
+ top: Math.round(currentY) + CURSOR_OFFSET_PX
353
341
  }, {
354
- left: Math.round(currentX) - boundingRect.width - CROSSHAIR_OFFSET,
355
- top: Math.round(currentY) + CROSSHAIR_OFFSET
342
+ left: Math.round(currentX) - boundingRect.width - CURSOR_OFFSET_PX,
343
+ top: Math.round(currentY) + CURSOR_OFFSET_PX
356
344
  }, {
357
- left: Math.round(currentX) + CROSSHAIR_OFFSET,
358
- top: Math.round(currentY) - boundingRect.height - CROSSHAIR_OFFSET
345
+ left: Math.round(currentX) + CURSOR_OFFSET_PX,
346
+ top: Math.round(currentY) - boundingRect.height - CURSOR_OFFSET_PX
359
347
  }, {
360
- left: Math.round(currentX) - boundingRect.width - CROSSHAIR_OFFSET,
361
- top: Math.round(currentY) - boundingRect.height - CROSSHAIR_OFFSET
348
+ left: Math.round(currentX) - boundingRect.width - CURSOR_OFFSET_PX,
349
+ top: Math.round(currentY) - boundingRect.height - CURSOR_OFFSET_PX
362
350
  }];
363
351
  for (const position of quadrants) {
364
352
  const fitsHorizontally = position.left >= VIEWPORT_MARGIN_PX && position.left + boundingRect.width <= viewportWidth - VIEWPORT_MARGIN_PX;
@@ -377,7 +365,7 @@ var Label = (props) => {
377
365
  return props.visible !== false;
378
366
  },
379
367
  get children() {
380
- var _el$ = _tmpl$4();
368
+ var _el$ = _tmpl$5();
381
369
  var _ref$ = labelRef;
382
370
  typeof _ref$ === "function" ? web.use(_ref$, _el$) : labelRef = _el$;
383
371
  web.insert(_el$, web.createComponent(solidJs.Show, {
@@ -412,17 +400,12 @@ var Label = (props) => {
412
400
  }), null);
413
401
  web.insert(_el$, web.createComponent(solidJs.Show, {
414
402
  get when() {
415
- return props.text.startsWith("(");
416
- },
417
- get fallback() {
418
- return (() => {
419
- var _el$5 = _tmpl$5();
420
- web.insert(_el$5, () => props.text);
421
- return _el$5;
422
- })();
403
+ return props.variant !== "processing";
423
404
  },
424
405
  get children() {
425
- return props.text;
406
+ var _el$4 = _tmpl$32();
407
+ web.insert(_el$4, () => props.text);
408
+ return _el$4;
426
409
  }
427
410
  }), null);
428
411
  web.insert(_el$, web.createComponent(solidJs.Show, {
@@ -430,7 +413,7 @@ var Label = (props) => {
430
413
  return props.variant === "success";
431
414
  },
432
415
  get children() {
433
- return _tmpl$32();
416
+ return _tmpl$4();
434
417
  }
435
418
  }), null);
436
419
  web.effect((_p$) => {
@@ -530,53 +513,16 @@ var Crosshair = (props) => {
530
513
  context.scale(dpr, dpr);
531
514
  }
532
515
  };
533
- const getSmallestElementBounds = (x, y) => {
534
- const element = document.elementFromPoint(x, y);
535
- if (!element || element === canvasRef || element === document.body || element === document.documentElement) {
536
- return null;
537
- }
538
- const rect = element.getBoundingClientRect();
539
- if (rect.width === 0 || rect.height === 0) {
540
- return null;
541
- }
542
- return {
543
- top: rect.top,
544
- bottom: rect.bottom,
545
- left: rect.left,
546
- right: rect.right
547
- };
548
- };
549
- const scanBoundary = (startX, startY, deltaX, deltaY, maxDistance) => {
550
- const baseBounds = getSmallestElementBounds(startX, startY);
551
- if (!baseBounds) return maxDistance;
552
- const stepSize = 5;
553
- for (let distance = stepSize; distance < maxDistance; distance += stepSize) {
554
- const checkX = startX + deltaX * distance;
555
- const checkY = startY + deltaY * distance;
556
- if (checkX < 0 || checkX >= width || checkY < 0 || checkY >= height) {
557
- return distance;
558
- }
559
- const currentBounds = getSmallestElementBounds(checkX, checkY);
560
- if (!currentBounds || currentBounds.top !== baseBounds.top || currentBounds.bottom !== baseBounds.bottom || currentBounds.left !== baseBounds.left || currentBounds.right !== baseBounds.right) {
561
- return distance;
562
- }
563
- }
564
- return maxDistance;
565
- };
566
516
  const render2 = () => {
567
517
  if (!context) return;
568
518
  context.clearRect(0, 0, width, height);
569
519
  context.strokeStyle = "rgba(210, 57, 192)";
570
520
  context.lineWidth = 1;
571
521
  context.beginPath();
572
- const topBoundary = scanBoundary(currentX, currentY, 0, -1, currentY);
573
- const bottomBoundary = scanBoundary(currentX, currentY, 0, 1, height - currentY);
574
- const leftBoundary = scanBoundary(currentX, currentY, -1, 0, currentX);
575
- const rightBoundary = scanBoundary(currentX, currentY, 1, 0, width - currentX);
576
- context.moveTo(currentX, currentY - topBoundary);
577
- context.lineTo(currentX, currentY + bottomBoundary);
578
- context.moveTo(currentX - leftBoundary, currentY);
579
- context.lineTo(currentX + rightBoundary, currentY);
522
+ context.moveTo(currentX, 0);
523
+ context.lineTo(currentX, height);
524
+ context.moveTo(0, currentY);
525
+ context.lineTo(width, currentY);
580
526
  context.stroke();
581
527
  };
582
528
  const animate = () => {
@@ -767,26 +713,43 @@ bippy.instrument({
767
713
  bippy._fiberRoots.add(fiberRoot);
768
714
  }
769
715
  });
770
- var getSourceTrace = async (element) => {
771
- const fiber = bippy.getFiberFromHostInstance(element);
772
- if (!fiber) return null;
773
- const ownerStack = source.getOwnerStack(fiber);
774
- const sources = await source.getSourcesFromStack(
775
- ownerStack,
776
- Number.MAX_SAFE_INTEGER
777
- );
778
- if (!sources) return null;
779
- console.log(sources);
780
- return sources.map((source$1) => {
781
- return {
782
- ...source$1,
783
- fileName: source.normalizeFileName(source$1.fileName)
784
- };
785
- }).filter((source$1) => {
786
- return source.isSourceFile(source$1.fileName);
787
- });
716
+ var generateCSSSelector = (element) => {
717
+ return finder.finder(element);
788
718
  };
789
- var getHTMLSnippet = (element) => {
719
+ var getHTMLSnippet = async (element) => {
720
+ const truncateString = (string, maxLength) => string.length > maxLength ? `${string.substring(0, maxLength)}...` : string;
721
+ const isInternalComponent = (name) => {
722
+ if (name.startsWith("_")) return true;
723
+ if (name.includes("Provider") && name.includes("Context")) return true;
724
+ return false;
725
+ };
726
+ const extractReactComponentName = (el) => {
727
+ const fiber = bippy.getFiberFromHostInstance(el);
728
+ if (!fiber) return null;
729
+ let componentName = null;
730
+ bippy.traverseFiber(
731
+ fiber,
732
+ (currentFiber) => {
733
+ if (bippy.isCompositeFiber(currentFiber)) {
734
+ const displayName = bippy.getDisplayName(currentFiber);
735
+ if (displayName && !isInternalComponent(displayName)) {
736
+ componentName = displayName;
737
+ return true;
738
+ }
739
+ }
740
+ return false;
741
+ },
742
+ true
743
+ );
744
+ return componentName;
745
+ };
746
+ const formatComponentSourceLocation = async (el) => {
747
+ const source$1 = await source.getSourceFromHostInstance(el);
748
+ if (!source$1) return null;
749
+ const fileName = source.normalizeFileName(source$1.fileName);
750
+ if (!source.isSourceFile(fileName)) return null;
751
+ return `${fileName}:${source$1.lineNumber}:${source$1.columnNumber}`;
752
+ };
790
753
  const semanticTags = /* @__PURE__ */ new Set([
791
754
  "article",
792
755
  "aside",
@@ -809,7 +772,7 @@ var getHTMLSnippet = (element) => {
809
772
  (attr) => attr.name.startsWith("data-")
810
773
  );
811
774
  };
812
- const getAncestorChain = (el, maxDepth = 10) => {
775
+ const collectDistinguishingAncestors = (el, maxDepth = 10) => {
813
776
  const ancestors2 = [];
814
777
  let current = el.parentElement;
815
778
  let depth = 0;
@@ -823,35 +786,7 @@ var getHTMLSnippet = (element) => {
823
786
  }
824
787
  return ancestors2.reverse();
825
788
  };
826
- const getCSSPath = (el) => {
827
- const parts = [];
828
- let current = el;
829
- let depth = 0;
830
- const maxDepth = 5;
831
- while (current && depth < maxDepth && current.tagName !== "BODY") {
832
- let selector = current.tagName.toLowerCase();
833
- if (current.id) {
834
- selector += `#${current.id}`;
835
- parts.unshift(selector);
836
- break;
837
- } else if (current.className && typeof current.className === "string" && current.className.trim()) {
838
- const classes = current.className.trim().split(/\s+/).slice(0, 2);
839
- selector += `.${classes.join(".")}`;
840
- }
841
- if (!current.id && (!current.className || !current.className.trim()) && current.parentElement) {
842
- const siblings = Array.from(current.parentElement.children);
843
- const index = siblings.indexOf(current);
844
- if (index >= 0 && siblings.length > 1) {
845
- selector += `:nth-child(${index + 1})`;
846
- }
847
- }
848
- parts.unshift(selector);
849
- current = current.parentElement;
850
- depth++;
851
- }
852
- return parts.join(" > ");
853
- };
854
- const getElementTag = (el, compact = false) => {
789
+ const formatElementOpeningTag = (el, compact = false) => {
855
790
  const tagName = el.tagName.toLowerCase();
856
791
  const attrs = [];
857
792
  if (el.id) {
@@ -861,10 +796,7 @@ var getHTMLSnippet = (element) => {
861
796
  const classes = el.className.trim().split(/\s+/);
862
797
  if (classes.length > 0 && classes[0]) {
863
798
  const displayClasses = compact ? classes.slice(0, 3) : classes;
864
- let classStr = displayClasses.join(" ");
865
- if (classStr.length > 30) {
866
- classStr = classStr.substring(0, 30) + "...";
867
- }
799
+ const classStr = truncateString(displayClasses.join(" "), 30);
868
800
  attrs.push(`class="${classStr}"`);
869
801
  }
870
802
  }
@@ -873,35 +805,20 @@ var getHTMLSnippet = (element) => {
873
805
  );
874
806
  const displayDataAttrs = compact ? dataAttrs.slice(0, 1) : dataAttrs;
875
807
  for (const attr of displayDataAttrs) {
876
- let value = attr.value;
877
- if (value.length > 20) {
878
- value = value.substring(0, 20) + "...";
879
- }
880
- attrs.push(`${attr.name}="${value}"`);
808
+ attrs.push(`${attr.name}="${truncateString(attr.value, 20)}"`);
881
809
  }
882
810
  const ariaLabel = el.getAttribute("aria-label");
883
811
  if (ariaLabel && !compact) {
884
- let value = ariaLabel;
885
- if (value.length > 20) {
886
- value = value.substring(0, 20) + "...";
887
- }
888
- attrs.push(`aria-label="${value}"`);
812
+ attrs.push(`aria-label="${truncateString(ariaLabel, 20)}"`);
889
813
  }
890
814
  return attrs.length > 0 ? `<${tagName} ${attrs.join(" ")}>` : `<${tagName}>`;
891
815
  };
892
- const getClosingTag = (el) => {
893
- return `</${el.tagName.toLowerCase()}>`;
816
+ const formatElementClosingTag = (el) => `</${el.tagName.toLowerCase()}>`;
817
+ const extractTruncatedTextContent = (el) => {
818
+ const text = (el.textContent || "").trim().replace(/\s+/g, " ");
819
+ return truncateString(text, 60);
894
820
  };
895
- const getTextContent = (el) => {
896
- let text = el.textContent || "";
897
- text = text.trim().replace(/\s+/g, " ");
898
- const maxLength = 60;
899
- if (text.length > maxLength) {
900
- text = text.substring(0, maxLength) + "...";
901
- }
902
- return text;
903
- };
904
- const getSiblingIdentifier = (el) => {
821
+ const extractSiblingIdentifier = (el) => {
905
822
  if (el.id) return `#${el.id}`;
906
823
  if (el.className && typeof el.className === "string") {
907
824
  const classes = el.className.trim().split(/\s+/);
@@ -912,12 +829,31 @@ var getHTMLSnippet = (element) => {
912
829
  return null;
913
830
  };
914
831
  const lines = [];
915
- lines.push(`Path: ${getCSSPath(element)}`);
916
- lines.push("");
917
- const ancestors = getAncestorChain(element);
832
+ const selector = generateCSSSelector(element);
833
+ lines.push(`Locate this element in the codebase:`);
834
+ lines.push(`- selector: ${selector}`);
835
+ const rect = element.getBoundingClientRect();
836
+ lines.push(`- width: ${Math.round(rect.width)}`);
837
+ lines.push(`- height: ${Math.round(rect.height)}`);
838
+ lines.push("HTML snippet:");
839
+ lines.push("```html");
840
+ const ancestors = collectDistinguishingAncestors(element);
841
+ const ancestorComponents = ancestors.map(
842
+ (ancestor) => extractReactComponentName(ancestor)
843
+ );
844
+ const elementComponent = extractReactComponentName(element);
845
+ const ancestorSources = await Promise.all(
846
+ ancestors.map((ancestor) => formatComponentSourceLocation(ancestor))
847
+ );
848
+ const elementSource = await formatComponentSourceLocation(element);
918
849
  for (let i = 0; i < ancestors.length; i++) {
919
850
  const indent2 = " ".repeat(i);
920
- lines.push(indent2 + getElementTag(ancestors[i], true));
851
+ const componentName = ancestorComponents[i];
852
+ const source = ancestorSources[i];
853
+ if (componentName && source && (i === 0 || ancestorComponents[i - 1] !== componentName)) {
854
+ lines.push(`${indent2}<${componentName} source="${source}">`);
855
+ }
856
+ lines.push(`${indent2}${formatElementOpeningTag(ancestors[i], true)}`);
921
857
  }
922
858
  const parent = element.parentElement;
923
859
  let targetIndex = -1;
@@ -926,10 +862,10 @@ var getHTMLSnippet = (element) => {
926
862
  targetIndex = siblings.indexOf(element);
927
863
  if (targetIndex > 0) {
928
864
  const prevSibling = siblings[targetIndex - 1];
929
- const prevId = getSiblingIdentifier(prevSibling);
865
+ const prevId = extractSiblingIdentifier(prevSibling);
930
866
  if (prevId && targetIndex <= 2) {
931
867
  const indent2 = " ".repeat(ancestors.length);
932
- lines.push(`${indent2} ${getElementTag(prevSibling, true)}`);
868
+ lines.push(`${indent2} ${formatElementOpeningTag(prevSibling, true)}`);
933
869
  lines.push(`${indent2} </${prevSibling.tagName.toLowerCase()}>`);
934
870
  } else if (targetIndex > 0) {
935
871
  const indent2 = " ".repeat(ancestors.length);
@@ -940,35 +876,44 @@ var getHTMLSnippet = (element) => {
940
876
  }
941
877
  }
942
878
  const indent = " ".repeat(ancestors.length);
943
- lines.push(indent + " <!-- SELECTED -->");
944
- const textContent = getTextContent(element);
879
+ const lastAncestorComponent = ancestors.length > 0 ? ancestorComponents[ancestorComponents.length - 1] : null;
880
+ const showElementComponent = elementComponent && elementSource && elementComponent !== lastAncestorComponent;
881
+ if (showElementComponent) {
882
+ lines.push(`${indent} <${elementComponent} used-at="${elementSource}">`);
883
+ }
884
+ lines.push(`${indent} <!-- IMPORTANT: selected element -->`);
885
+ const textContent = extractTruncatedTextContent(element);
945
886
  const childrenCount = element.children.length;
887
+ const elementIndent = `${indent}${showElementComponent ? " " : " "}`;
946
888
  if (textContent && childrenCount === 0 && textContent.length < 40) {
947
889
  lines.push(
948
- `${indent} ${getElementTag(element)}${textContent}${getClosingTag(
890
+ `${elementIndent}${formatElementOpeningTag(element)}${textContent}${formatElementClosingTag(
949
891
  element
950
892
  )}`
951
893
  );
952
894
  } else {
953
- lines.push(indent + " " + getElementTag(element));
895
+ lines.push(`${elementIndent}${formatElementOpeningTag(element)}`);
954
896
  if (textContent) {
955
- lines.push(`${indent} ${textContent}`);
897
+ lines.push(`${elementIndent} ${textContent}`);
956
898
  }
957
899
  if (childrenCount > 0) {
958
900
  lines.push(
959
- `${indent} ... (${childrenCount} element${childrenCount === 1 ? "" : "s"})`
901
+ `${elementIndent} ... (${childrenCount} element${childrenCount === 1 ? "" : "s"})`
960
902
  );
961
903
  }
962
- lines.push(indent + " " + getClosingTag(element));
904
+ lines.push(`${elementIndent}${formatElementClosingTag(element)}`);
905
+ }
906
+ if (showElementComponent) {
907
+ lines.push(`${indent} </${elementComponent}>`);
963
908
  }
964
909
  if (parent && targetIndex >= 0) {
965
910
  const siblings = Array.from(parent.children);
966
911
  const siblingsAfter = siblings.length - targetIndex - 1;
967
912
  if (siblingsAfter > 0) {
968
913
  const nextSibling = siblings[targetIndex + 1];
969
- const nextId = getSiblingIdentifier(nextSibling);
914
+ const nextId = extractSiblingIdentifier(nextSibling);
970
915
  if (nextId && siblingsAfter <= 2) {
971
- lines.push(`${indent} ${getElementTag(nextSibling, true)}`);
916
+ lines.push(`${indent} ${formatElementOpeningTag(nextSibling, true)}`);
972
917
  lines.push(`${indent} </${nextSibling.tagName.toLowerCase()}>`);
973
918
  } else {
974
919
  lines.push(
@@ -979,8 +924,14 @@ var getHTMLSnippet = (element) => {
979
924
  }
980
925
  for (let i = ancestors.length - 1; i >= 0; i--) {
981
926
  const indent2 = " ".repeat(i);
982
- lines.push(indent2 + getClosingTag(ancestors[i]));
927
+ lines.push(`${indent2}${formatElementClosingTag(ancestors[i])}`);
928
+ const componentName = ancestorComponents[i];
929
+ const source = ancestorSources[i];
930
+ if (componentName && source && (i === ancestors.length - 1 || ancestorComponents[i + 1] !== componentName)) {
931
+ lines.push(`${indent2}</${componentName}>`);
932
+ }
983
933
  }
934
+ lines.push("```");
984
935
  return lines.join("\n");
985
936
  };
986
937
 
@@ -1210,7 +1161,6 @@ var createElementBounds = (element) => {
1210
1161
  };
1211
1162
 
1212
1163
  // src/core.tsx
1213
- var SUCCESS_LABEL_DURATION_MS = 1700;
1214
1164
  var PROGRESS_INDICATOR_DELAY_MS = 150;
1215
1165
  var init = (rawOptions) => {
1216
1166
  const options = {
@@ -1237,6 +1187,9 @@ var init = (rawOptions) => {
1237
1187
  const [successLabels, setSuccessLabels] = solidJs.createSignal([]);
1238
1188
  const [isActivated, setIsActivated] = solidJs.createSignal(false);
1239
1189
  const [showProgressIndicator, setShowProgressIndicator] = solidJs.createSignal(false);
1190
+ const [didJustDrag, setDidJustDrag] = solidJs.createSignal(false);
1191
+ const [copyStartX, setCopyStartX] = solidJs.createSignal(OFFSCREEN_POSITION);
1192
+ const [copyStartY, setCopyStartY] = solidJs.createSignal(OFFSCREEN_POSITION);
1240
1193
  let holdTimerId = null;
1241
1194
  let progressAnimationId = null;
1242
1195
  let progressDelayTimerId = null;
@@ -1244,7 +1197,7 @@ var init = (rawOptions) => {
1244
1197
  const isRendererActive = solidJs.createMemo(() => isActivated() && !isCopying());
1245
1198
  const hasValidMousePosition = solidJs.createMemo(() => mouseX() > OFFSCREEN_POSITION && mouseY() > OFFSCREEN_POSITION);
1246
1199
  const isTargetKeyCombination = (event) => (event.metaKey || event.ctrlKey) && event.key.toLowerCase() === "c";
1247
- const addGrabbedBox = (bounds) => {
1200
+ const showTemporaryGrabbedBox = (bounds) => {
1248
1201
  const boxId = `grabbed-${Date.now()}-${Math.random()}`;
1249
1202
  const createdAt = Date.now();
1250
1203
  const newBox = {
@@ -1258,7 +1211,7 @@ var init = (rawOptions) => {
1258
1211
  setGrabbedBoxes((previousBoxes) => previousBoxes.filter((box) => box.id !== boxId));
1259
1212
  }, SUCCESS_LABEL_DURATION_MS);
1260
1213
  };
1261
- const addSuccessLabel = (text, positionX, positionY) => {
1214
+ const showTemporarySuccessLabel = (text, positionX, positionY) => {
1262
1215
  const labelId = `success-${Date.now()}-${Math.random()}`;
1263
1216
  setSuccessLabels((previousLabels) => [...previousLabels, {
1264
1217
  id: labelId,
@@ -1270,24 +1223,15 @@ var init = (rawOptions) => {
1270
1223
  setSuccessLabels((previousLabels) => previousLabels.filter((label) => label.id !== labelId));
1271
1224
  }, SUCCESS_LABEL_DURATION_MS);
1272
1225
  };
1273
- const formatStackTrace = (stackTrace) => {
1274
- return stackTrace.map((source) => {
1275
- const functionName = source.functionName ?? "anonymous";
1276
- const fileName = source.fileName ?? "unknown";
1277
- const lineNumber = source.lineNumber ?? 0;
1278
- const columnNumber = source.columnNumber ?? 0;
1279
- return ` at ${functionName} (${fileName}:${lineNumber}:${columnNumber})`;
1280
- }).join("\n");
1281
- };
1282
- const wrapContextInXmlTags = (context) => {
1283
- return `<selected_element>${context}</selected_element>`;
1284
- };
1285
- const getComputedStyles = (element) => {
1226
+ const wrapInSelectedElementTags = (context) => `<selected_element>
1227
+ ${context}
1228
+ </selected_element>`;
1229
+ const extractRelevantComputedStyles = (element) => {
1286
1230
  const computed = window.getComputedStyle(element);
1287
1231
  const rect = element.getBoundingClientRect();
1288
1232
  return {
1289
- width: `${rect.width}px`,
1290
- height: `${rect.height}px`,
1233
+ width: `${Math.round(rect.width)}px`,
1234
+ height: `${Math.round(rect.height)}px`,
1291
1235
  paddingTop: computed.paddingTop,
1292
1236
  paddingRight: computed.paddingRight,
1293
1237
  paddingBottom: computed.paddingBottom,
@@ -1296,7 +1240,7 @@ var init = (rawOptions) => {
1296
1240
  opacity: computed.opacity
1297
1241
  };
1298
1242
  };
1299
- const createStructuredClipboardHtml = (elements) => {
1243
+ const createStructuredClipboardHtmlBlob = (elements) => {
1300
1244
  const structuredData = {
1301
1245
  elements: elements.map((element) => ({
1302
1246
  tagName: element.tagName,
@@ -1310,60 +1254,50 @@ var init = (rawOptions) => {
1310
1254
  type: "text/html"
1311
1255
  });
1312
1256
  };
1313
- const getElementContentWithTrace = async (element) => {
1314
- const elementHtml = getHTMLSnippet(element);
1315
- const componentStackTrace = await getSourceTrace(element);
1316
- if (componentStackTrace?.length) {
1317
- const formattedStackTrace = formatStackTrace(componentStackTrace);
1318
- return `${elementHtml}
1319
-
1320
- Component owner stack:
1321
- ${formattedStackTrace}`;
1322
- }
1323
- return elementHtml;
1257
+ const extractElementTagName = (element) => (element.tagName || "").toLowerCase();
1258
+ const executeCopyOperation = async (positionX, positionY, operation) => {
1259
+ setCopyStartX(positionX);
1260
+ setCopyStartY(positionY);
1261
+ setIsCopying(true);
1262
+ await operation().finally(() => {
1263
+ setIsCopying(false);
1264
+ });
1324
1265
  };
1325
- const getElementTagName = (element) => (element.tagName || "").toLowerCase();
1326
- const handleCopy = async (targetElement2) => {
1327
- const elementBounds = targetElement2.getBoundingClientRect();
1328
- const tagName = getElementTagName(targetElement2);
1329
- addGrabbedBox(createElementBounds(targetElement2));
1266
+ const copySingleElementToClipboard = async (targetElement2) => {
1267
+ const tagName = extractElementTagName(targetElement2);
1268
+ showTemporaryGrabbedBox(createElementBounds(targetElement2));
1330
1269
  try {
1331
- const content = await getElementContentWithTrace(targetElement2);
1332
- const plainTextContent = wrapContextInXmlTags(content);
1333
- const htmlContent = createStructuredClipboardHtml([{
1270
+ const content = await getHTMLSnippet(targetElement2);
1271
+ const plainTextContent = wrapInSelectedElementTags(content);
1272
+ const htmlContent = createStructuredClipboardHtmlBlob([{
1334
1273
  tagName,
1335
- content: await getElementContentWithTrace(targetElement2),
1336
- computedStyles: getComputedStyles(targetElement2)
1274
+ content,
1275
+ computedStyles: extractRelevantComputedStyles(targetElement2)
1337
1276
  }]);
1338
1277
  await copyContent([plainTextContent, htmlContent]);
1339
1278
  } catch {
1340
1279
  }
1341
- addSuccessLabel(tagName ? `<${tagName}>` : "<element>", elementBounds.left, elementBounds.top);
1280
+ showTemporarySuccessLabel(tagName ? `<${tagName}>` : "<element>", copyStartX(), copyStartY());
1342
1281
  };
1343
- const handleMultipleCopy = async (targetElements) => {
1282
+ const copyMultipleElementsToClipboard = async (targetElements) => {
1344
1283
  if (targetElements.length === 0) return;
1345
- let minPositionX = Infinity;
1346
- let minPositionY = Infinity;
1347
1284
  for (const element of targetElements) {
1348
- const elementBounds = element.getBoundingClientRect();
1349
- minPositionX = Math.min(minPositionX, elementBounds.left);
1350
- minPositionY = Math.min(minPositionY, elementBounds.top);
1351
- addGrabbedBox(createElementBounds(element));
1285
+ showTemporaryGrabbedBox(createElementBounds(element));
1352
1286
  }
1353
1287
  try {
1354
- const elementSnippets = await Promise.all(targetElements.map((element) => getElementContentWithTrace(element)));
1288
+ const elementSnippets = await Promise.all(targetElements.map((element) => getHTMLSnippet(element)));
1355
1289
  const combinedContent = elementSnippets.join("\n\n---\n\n");
1356
- const plainTextContent = wrapContextInXmlTags(combinedContent);
1357
- const structuredElements = await Promise.all(targetElements.map(async (element) => ({
1358
- tagName: getElementTagName(element),
1359
- content: await getElementContentWithTrace(element),
1360
- computedStyles: getComputedStyles(element)
1361
- })));
1362
- const htmlContent = createStructuredClipboardHtml(structuredElements);
1290
+ const plainTextContent = wrapInSelectedElementTags(combinedContent);
1291
+ const structuredElements = elementSnippets.map((content, index) => ({
1292
+ tagName: extractElementTagName(targetElements[index]),
1293
+ content,
1294
+ computedStyles: extractRelevantComputedStyles(targetElements[index])
1295
+ }));
1296
+ const htmlContent = createStructuredClipboardHtmlBlob(structuredElements);
1363
1297
  await copyContent([plainTextContent, htmlContent]);
1364
1298
  } catch {
1365
1299
  }
1366
- addSuccessLabel(`${targetElements.length} elements`, minPositionX, minPositionY);
1300
+ showTemporarySuccessLabel(`${targetElements.length} elements`, copyStartX(), copyStartY());
1367
1301
  };
1368
1302
  const targetElement = solidJs.createMemo(() => {
1369
1303
  if (!isRendererActive() || isDragging()) return null;
@@ -1384,16 +1318,16 @@ ${formattedStackTrace}`;
1384
1318
  };
1385
1319
  });
1386
1320
  const DRAG_THRESHOLD_PX = 2;
1387
- const getDragDistance = (endX, endY) => ({
1321
+ const calculateDragDistance = (endX, endY) => ({
1388
1322
  x: Math.abs(endX - dragStartX()),
1389
1323
  y: Math.abs(endY - dragStartY())
1390
1324
  });
1391
1325
  const isDraggingBeyondThreshold = solidJs.createMemo(() => {
1392
1326
  if (!isDragging()) return false;
1393
- const dragDistance = getDragDistance(mouseX(), mouseY());
1327
+ const dragDistance = calculateDragDistance(mouseX(), mouseY());
1394
1328
  return dragDistance.x > DRAG_THRESHOLD_PX || dragDistance.y > DRAG_THRESHOLD_PX;
1395
1329
  });
1396
- const getDragRect = (endX, endY) => {
1330
+ const calculateDragRectangle = (endX, endY) => {
1397
1331
  const dragX = Math.min(dragStartX(), endX);
1398
1332
  const dragY = Math.min(dragStartY(), endY);
1399
1333
  const dragWidth = Math.abs(endX - dragStartX());
@@ -1407,7 +1341,7 @@ ${formattedStackTrace}`;
1407
1341
  };
1408
1342
  const dragBounds = solidJs.createMemo(() => {
1409
1343
  if (!isDraggingBeyondThreshold()) return void 0;
1410
- const drag = getDragRect(mouseX(), mouseY());
1344
+ const drag = calculateDragRectangle(mouseX(), mouseY());
1411
1345
  return {
1412
1346
  borderRadius: "0px",
1413
1347
  height: drag.height,
@@ -1419,19 +1353,16 @@ ${formattedStackTrace}`;
1419
1353
  });
1420
1354
  const labelText = solidJs.createMemo(() => {
1421
1355
  const element = targetElement();
1422
- return element ? `<${getElementTagName(element)}>` : "<element>";
1423
- });
1424
- const labelPosition = solidJs.createMemo(() => {
1425
- return {
1426
- x: mouseX(),
1427
- y: mouseY()
1428
- };
1356
+ return element ? `<${extractElementTagName(element)}>` : "<element>";
1429
1357
  });
1430
- const isSameAsLast = solidJs.createMemo(() => {
1431
- const currentElement = targetElement();
1432
- const lastElement = lastGrabbedElement();
1433
- return Boolean(currentElement) && currentElement === lastElement;
1358
+ const labelPosition = solidJs.createMemo(() => isCopying() ? {
1359
+ x: copyStartX(),
1360
+ y: copyStartY()
1361
+ } : {
1362
+ x: mouseX(),
1363
+ y: mouseY()
1434
1364
  });
1365
+ const isSameAsLast = solidJs.createMemo(() => Boolean(targetElement() && targetElement() === lastGrabbedElement()));
1435
1366
  solidJs.createEffect(solidJs.on(() => [targetElement(), lastGrabbedElement()], ([currentElement, lastElement]) => {
1436
1367
  if (lastElement && currentElement && lastElement !== currentElement) {
1437
1368
  setLastGrabbedElement(null);
@@ -1519,8 +1450,8 @@ ${formattedStackTrace}`;
1519
1450
  });
1520
1451
  window.addEventListener("keyup", (event) => {
1521
1452
  if (!isHoldingKeys() && !isActivated()) return;
1522
- const isReleasingC = event.key.toLowerCase() === "c";
1523
1453
  const isReleasingModifier = !event.metaKey && !event.ctrlKey;
1454
+ const isReleasingC = event.key.toLowerCase() === "c";
1524
1455
  if (isReleasingC || isReleasingModifier) {
1525
1456
  deactivateRenderer();
1526
1457
  }
@@ -1546,39 +1477,41 @@ ${formattedStackTrace}`;
1546
1477
  });
1547
1478
  window.addEventListener("mouseup", (event) => {
1548
1479
  if (!isDragging()) return;
1549
- const dragDistance = getDragDistance(event.clientX, event.clientY);
1480
+ const dragDistance = calculateDragDistance(event.clientX, event.clientY);
1550
1481
  const wasDragGesture = dragDistance.x > DRAG_THRESHOLD_PX || dragDistance.y > DRAG_THRESHOLD_PX;
1551
1482
  setIsDragging(false);
1552
1483
  document.body.style.userSelect = "";
1553
1484
  if (wasDragGesture) {
1554
- const dragRect = getDragRect(event.clientX, event.clientY);
1485
+ setDidJustDrag(true);
1486
+ const dragRect = calculateDragRectangle(event.clientX, event.clientY);
1555
1487
  const elements = getElementsInDrag(dragRect, isValidGrabbableElement);
1556
1488
  if (elements.length > 0) {
1557
- setIsCopying(true);
1558
- void handleMultipleCopy(elements).finally(() => {
1559
- setIsCopying(false);
1560
- });
1489
+ void executeCopyOperation(event.clientX, event.clientY, () => copyMultipleElementsToClipboard(elements));
1561
1490
  } else {
1562
1491
  const fallbackElements = getElementsInDragLoose(dragRect, isValidGrabbableElement);
1563
1492
  if (fallbackElements.length > 0) {
1564
- setIsCopying(true);
1565
- void handleMultipleCopy(fallbackElements).finally(() => {
1566
- setIsCopying(false);
1567
- });
1493
+ void executeCopyOperation(event.clientX, event.clientY, () => copyMultipleElementsToClipboard(fallbackElements));
1568
1494
  }
1569
1495
  }
1570
1496
  } else {
1571
1497
  const element = getElementAtPosition(event.clientX, event.clientY);
1572
1498
  if (!element) return;
1573
- setIsCopying(true);
1574
1499
  setLastGrabbedElement(element);
1575
- void handleCopy(element).finally(() => {
1576
- setIsCopying(false);
1577
- });
1500
+ void executeCopyOperation(event.clientX, event.clientY, () => copySingleElementToClipboard(element));
1578
1501
  }
1579
1502
  }, {
1580
1503
  signal: eventListenerSignal
1581
1504
  });
1505
+ window.addEventListener("click", (event) => {
1506
+ if (didJustDrag()) {
1507
+ event.preventDefault();
1508
+ event.stopPropagation();
1509
+ setDidJustDrag(false);
1510
+ }
1511
+ }, {
1512
+ signal: eventListenerSignal,
1513
+ capture: true
1514
+ });
1582
1515
  document.addEventListener("visibilitychange", () => {
1583
1516
  if (document.hidden) {
1584
1517
  setGrabbedBoxes([]);