react-grab 0.0.29 → 0.0.30
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 +249 -265
- package/dist/index.global.js +29 -18
- package/dist/index.js +251 -267
- package/package.json +3 -1
package/dist/index.cjs
CHANGED
|
@@ -2,8 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
var web = require('solid-js/web');
|
|
4
4
|
var solidJs = require('solid-js');
|
|
5
|
+
var modernScreenshot = require('modern-screenshot');
|
|
5
6
|
var bippy = require('bippy');
|
|
6
7
|
var source = require('bippy/dist/source');
|
|
8
|
+
var finder = require('@medv/finder');
|
|
7
9
|
|
|
8
10
|
/**
|
|
9
11
|
* @license MIT
|
|
@@ -96,6 +98,7 @@ var VIEWPORT_MARGIN_PX = 8;
|
|
|
96
98
|
var INDICATOR_CLAMP_PADDING_PX = 4;
|
|
97
99
|
var CURSOR_OFFSET_PX = 14;
|
|
98
100
|
var SELECTION_LERP_FACTOR = 0.95;
|
|
101
|
+
var SUCCESS_LABEL_DURATION_MS = 1700;
|
|
99
102
|
|
|
100
103
|
// src/utils/lerp.ts
|
|
101
104
|
var lerp = (start, end, factor) => {
|
|
@@ -258,9 +261,9 @@ var getClampedElementPosition = (positionLeft, positionTop, elementWidth, elemen
|
|
|
258
261
|
// src/components/label.tsx
|
|
259
262
|
var _tmpl$3 = /* @__PURE__ */ web.template(`<span style=display:inline-block;margin-right:4px;font-weight:600>\u2713`);
|
|
260
263
|
var _tmpl$22 = /* @__PURE__ */ web.template(`<div style=margin-right:4px>Copied`);
|
|
261
|
-
var _tmpl$32 = /* @__PURE__ */ web.template(`<
|
|
262
|
-
var _tmpl$4 = /* @__PURE__ */ web.template(`<div style=
|
|
263
|
-
var _tmpl$5 = /* @__PURE__ */ web.template(`<
|
|
264
|
+
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">`);
|
|
265
|
+
var _tmpl$4 = /* @__PURE__ */ web.template(`<div style=margin-left:4px>to clipboard`);
|
|
266
|
+
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
267
|
var Label = (props) => {
|
|
265
268
|
const [opacity, setOpacity] = solidJs.createSignal(0);
|
|
266
269
|
const [positionTick, setPositionTick] = solidJs.createSignal(0);
|
|
@@ -310,7 +313,7 @@ var Label = (props) => {
|
|
|
310
313
|
if (props.variant === "success") {
|
|
311
314
|
const fadeOutTimer = setTimeout(() => {
|
|
312
315
|
setOpacity(0);
|
|
313
|
-
},
|
|
316
|
+
}, SUCCESS_LABEL_DURATION_MS);
|
|
314
317
|
solidJs.onCleanup(() => clearTimeout(fadeOutTimer));
|
|
315
318
|
}
|
|
316
319
|
}));
|
|
@@ -331,34 +334,20 @@ var Label = (props) => {
|
|
|
331
334
|
left: currentX,
|
|
332
335
|
top: currentY
|
|
333
336
|
};
|
|
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
337
|
const viewportWidth = window.innerWidth;
|
|
349
338
|
const viewportHeight = window.innerHeight;
|
|
350
339
|
const quadrants = [{
|
|
351
|
-
left: Math.round(currentX) +
|
|
352
|
-
top: Math.round(currentY) +
|
|
340
|
+
left: Math.round(currentX) + CURSOR_OFFSET_PX,
|
|
341
|
+
top: Math.round(currentY) + CURSOR_OFFSET_PX
|
|
353
342
|
}, {
|
|
354
|
-
left: Math.round(currentX) - boundingRect.width -
|
|
355
|
-
top: Math.round(currentY) +
|
|
343
|
+
left: Math.round(currentX) - boundingRect.width - CURSOR_OFFSET_PX,
|
|
344
|
+
top: Math.round(currentY) + CURSOR_OFFSET_PX
|
|
356
345
|
}, {
|
|
357
|
-
left: Math.round(currentX) +
|
|
358
|
-
top: Math.round(currentY) - boundingRect.height -
|
|
346
|
+
left: Math.round(currentX) + CURSOR_OFFSET_PX,
|
|
347
|
+
top: Math.round(currentY) - boundingRect.height - CURSOR_OFFSET_PX
|
|
359
348
|
}, {
|
|
360
|
-
left: Math.round(currentX) - boundingRect.width -
|
|
361
|
-
top: Math.round(currentY) - boundingRect.height -
|
|
349
|
+
left: Math.round(currentX) - boundingRect.width - CURSOR_OFFSET_PX,
|
|
350
|
+
top: Math.round(currentY) - boundingRect.height - CURSOR_OFFSET_PX
|
|
362
351
|
}];
|
|
363
352
|
for (const position of quadrants) {
|
|
364
353
|
const fitsHorizontally = position.left >= VIEWPORT_MARGIN_PX && position.left + boundingRect.width <= viewportWidth - VIEWPORT_MARGIN_PX;
|
|
@@ -377,7 +366,7 @@ var Label = (props) => {
|
|
|
377
366
|
return props.visible !== false;
|
|
378
367
|
},
|
|
379
368
|
get children() {
|
|
380
|
-
var _el$ = _tmpl$
|
|
369
|
+
var _el$ = _tmpl$5();
|
|
381
370
|
var _ref$ = labelRef;
|
|
382
371
|
typeof _ref$ === "function" ? web.use(_ref$, _el$) : labelRef = _el$;
|
|
383
372
|
web.insert(_el$, web.createComponent(solidJs.Show, {
|
|
@@ -412,17 +401,12 @@ var Label = (props) => {
|
|
|
412
401
|
}), null);
|
|
413
402
|
web.insert(_el$, web.createComponent(solidJs.Show, {
|
|
414
403
|
get when() {
|
|
415
|
-
return props.
|
|
416
|
-
},
|
|
417
|
-
get fallback() {
|
|
418
|
-
return (() => {
|
|
419
|
-
var _el$5 = _tmpl$5();
|
|
420
|
-
web.insert(_el$5, () => props.text);
|
|
421
|
-
return _el$5;
|
|
422
|
-
})();
|
|
404
|
+
return props.variant !== "processing";
|
|
423
405
|
},
|
|
424
406
|
get children() {
|
|
425
|
-
|
|
407
|
+
var _el$4 = _tmpl$32();
|
|
408
|
+
web.insert(_el$4, () => props.text);
|
|
409
|
+
return _el$4;
|
|
426
410
|
}
|
|
427
411
|
}), null);
|
|
428
412
|
web.insert(_el$, web.createComponent(solidJs.Show, {
|
|
@@ -430,7 +414,7 @@ var Label = (props) => {
|
|
|
430
414
|
return props.variant === "success";
|
|
431
415
|
},
|
|
432
416
|
get children() {
|
|
433
|
-
return _tmpl$
|
|
417
|
+
return _tmpl$4();
|
|
434
418
|
}
|
|
435
419
|
}), null);
|
|
436
420
|
web.effect((_p$) => {
|
|
@@ -530,53 +514,16 @@ var Crosshair = (props) => {
|
|
|
530
514
|
context.scale(dpr, dpr);
|
|
531
515
|
}
|
|
532
516
|
};
|
|
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
517
|
const render2 = () => {
|
|
567
518
|
if (!context) return;
|
|
568
519
|
context.clearRect(0, 0, width, height);
|
|
569
520
|
context.strokeStyle = "rgba(210, 57, 192)";
|
|
570
521
|
context.lineWidth = 1;
|
|
571
522
|
context.beginPath();
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
context.moveTo(currentX, currentY - topBoundary);
|
|
577
|
-
context.lineTo(currentX, currentY + bottomBoundary);
|
|
578
|
-
context.moveTo(currentX - leftBoundary, currentY);
|
|
579
|
-
context.lineTo(currentX + rightBoundary, currentY);
|
|
523
|
+
context.moveTo(currentX, 0);
|
|
524
|
+
context.lineTo(currentX, height);
|
|
525
|
+
context.moveTo(0, currentY);
|
|
526
|
+
context.lineTo(width, currentY);
|
|
580
527
|
context.stroke();
|
|
581
528
|
};
|
|
582
529
|
const animate = () => {
|
|
@@ -767,26 +714,43 @@ bippy.instrument({
|
|
|
767
714
|
bippy._fiberRoots.add(fiberRoot);
|
|
768
715
|
}
|
|
769
716
|
});
|
|
770
|
-
var
|
|
771
|
-
|
|
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
|
-
});
|
|
717
|
+
var generateCSSSelector = (element) => {
|
|
718
|
+
return finder.finder(element);
|
|
788
719
|
};
|
|
789
|
-
var getHTMLSnippet = (element) => {
|
|
720
|
+
var getHTMLSnippet = async (element) => {
|
|
721
|
+
const truncateString = (string, maxLength) => string.length > maxLength ? `${string.substring(0, maxLength)}...` : string;
|
|
722
|
+
const isInternalComponent = (name) => {
|
|
723
|
+
if (name.startsWith("_")) return true;
|
|
724
|
+
if (name.includes("Provider") && name.includes("Context")) return true;
|
|
725
|
+
return false;
|
|
726
|
+
};
|
|
727
|
+
const extractReactComponentName = (el) => {
|
|
728
|
+
const fiber = bippy.getFiberFromHostInstance(el);
|
|
729
|
+
if (!fiber) return null;
|
|
730
|
+
let componentName = null;
|
|
731
|
+
bippy.traverseFiber(
|
|
732
|
+
fiber,
|
|
733
|
+
(currentFiber) => {
|
|
734
|
+
if (bippy.isCompositeFiber(currentFiber)) {
|
|
735
|
+
const displayName = bippy.getDisplayName(currentFiber);
|
|
736
|
+
if (displayName && !isInternalComponent(displayName)) {
|
|
737
|
+
componentName = displayName;
|
|
738
|
+
return true;
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
return false;
|
|
742
|
+
},
|
|
743
|
+
true
|
|
744
|
+
);
|
|
745
|
+
return componentName;
|
|
746
|
+
};
|
|
747
|
+
const formatComponentSourceLocation = async (el) => {
|
|
748
|
+
const source$1 = await source.getSourceFromHostInstance(el);
|
|
749
|
+
if (!source$1) return null;
|
|
750
|
+
const fileName = source.normalizeFileName(source$1.fileName);
|
|
751
|
+
if (!source.isSourceFile(fileName)) return null;
|
|
752
|
+
return `${fileName}:${source$1.lineNumber}:${source$1.columnNumber}`;
|
|
753
|
+
};
|
|
790
754
|
const semanticTags = /* @__PURE__ */ new Set([
|
|
791
755
|
"article",
|
|
792
756
|
"aside",
|
|
@@ -809,7 +773,7 @@ var getHTMLSnippet = (element) => {
|
|
|
809
773
|
(attr) => attr.name.startsWith("data-")
|
|
810
774
|
);
|
|
811
775
|
};
|
|
812
|
-
const
|
|
776
|
+
const collectDistinguishingAncestors = (el, maxDepth = 10) => {
|
|
813
777
|
const ancestors2 = [];
|
|
814
778
|
let current = el.parentElement;
|
|
815
779
|
let depth = 0;
|
|
@@ -823,35 +787,7 @@ var getHTMLSnippet = (element) => {
|
|
|
823
787
|
}
|
|
824
788
|
return ancestors2.reverse();
|
|
825
789
|
};
|
|
826
|
-
const
|
|
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) => {
|
|
790
|
+
const formatElementOpeningTag = (el, compact = false) => {
|
|
855
791
|
const tagName = el.tagName.toLowerCase();
|
|
856
792
|
const attrs = [];
|
|
857
793
|
if (el.id) {
|
|
@@ -861,10 +797,7 @@ var getHTMLSnippet = (element) => {
|
|
|
861
797
|
const classes = el.className.trim().split(/\s+/);
|
|
862
798
|
if (classes.length > 0 && classes[0]) {
|
|
863
799
|
const displayClasses = compact ? classes.slice(0, 3) : classes;
|
|
864
|
-
|
|
865
|
-
if (classStr.length > 30) {
|
|
866
|
-
classStr = classStr.substring(0, 30) + "...";
|
|
867
|
-
}
|
|
800
|
+
const classStr = truncateString(displayClasses.join(" "), 30);
|
|
868
801
|
attrs.push(`class="${classStr}"`);
|
|
869
802
|
}
|
|
870
803
|
}
|
|
@@ -873,35 +806,20 @@ var getHTMLSnippet = (element) => {
|
|
|
873
806
|
);
|
|
874
807
|
const displayDataAttrs = compact ? dataAttrs.slice(0, 1) : dataAttrs;
|
|
875
808
|
for (const attr of displayDataAttrs) {
|
|
876
|
-
|
|
877
|
-
if (value.length > 20) {
|
|
878
|
-
value = value.substring(0, 20) + "...";
|
|
879
|
-
}
|
|
880
|
-
attrs.push(`${attr.name}="${value}"`);
|
|
809
|
+
attrs.push(`${attr.name}="${truncateString(attr.value, 20)}"`);
|
|
881
810
|
}
|
|
882
811
|
const ariaLabel = el.getAttribute("aria-label");
|
|
883
812
|
if (ariaLabel && !compact) {
|
|
884
|
-
|
|
885
|
-
if (value.length > 20) {
|
|
886
|
-
value = value.substring(0, 20) + "...";
|
|
887
|
-
}
|
|
888
|
-
attrs.push(`aria-label="${value}"`);
|
|
813
|
+
attrs.push(`aria-label="${truncateString(ariaLabel, 20)}"`);
|
|
889
814
|
}
|
|
890
815
|
return attrs.length > 0 ? `<${tagName} ${attrs.join(" ")}>` : `<${tagName}>`;
|
|
891
816
|
};
|
|
892
|
-
const
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
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;
|
|
817
|
+
const formatElementClosingTag = (el) => `</${el.tagName.toLowerCase()}>`;
|
|
818
|
+
const extractTruncatedTextContent = (el) => {
|
|
819
|
+
const text = (el.textContent || "").trim().replace(/\s+/g, " ");
|
|
820
|
+
return truncateString(text, 60);
|
|
903
821
|
};
|
|
904
|
-
const
|
|
822
|
+
const extractSiblingIdentifier = (el) => {
|
|
905
823
|
if (el.id) return `#${el.id}`;
|
|
906
824
|
if (el.className && typeof el.className === "string") {
|
|
907
825
|
const classes = el.className.trim().split(/\s+/);
|
|
@@ -912,12 +830,31 @@ var getHTMLSnippet = (element) => {
|
|
|
912
830
|
return null;
|
|
913
831
|
};
|
|
914
832
|
const lines = [];
|
|
915
|
-
|
|
916
|
-
lines.push(
|
|
917
|
-
|
|
833
|
+
const selector = generateCSSSelector(element);
|
|
834
|
+
lines.push(`Locate this element in the codebase:`);
|
|
835
|
+
lines.push(`- selector: ${selector}`);
|
|
836
|
+
const rect = element.getBoundingClientRect();
|
|
837
|
+
lines.push(`- width: ${Math.round(rect.width)}`);
|
|
838
|
+
lines.push(`- height: ${Math.round(rect.height)}`);
|
|
839
|
+
lines.push("HTML snippet:");
|
|
840
|
+
lines.push("```html");
|
|
841
|
+
const ancestors = collectDistinguishingAncestors(element);
|
|
842
|
+
const ancestorComponents = ancestors.map(
|
|
843
|
+
(ancestor) => extractReactComponentName(ancestor)
|
|
844
|
+
);
|
|
845
|
+
const elementComponent = extractReactComponentName(element);
|
|
846
|
+
const ancestorSources = await Promise.all(
|
|
847
|
+
ancestors.map((ancestor) => formatComponentSourceLocation(ancestor))
|
|
848
|
+
);
|
|
849
|
+
const elementSource = await formatComponentSourceLocation(element);
|
|
918
850
|
for (let i = 0; i < ancestors.length; i++) {
|
|
919
851
|
const indent2 = " ".repeat(i);
|
|
920
|
-
|
|
852
|
+
const componentName = ancestorComponents[i];
|
|
853
|
+
const source = ancestorSources[i];
|
|
854
|
+
if (componentName && source && (i === 0 || ancestorComponents[i - 1] !== componentName)) {
|
|
855
|
+
lines.push(`${indent2}<${componentName} source="${source}">`);
|
|
856
|
+
}
|
|
857
|
+
lines.push(`${indent2}${formatElementOpeningTag(ancestors[i], true)}`);
|
|
921
858
|
}
|
|
922
859
|
const parent = element.parentElement;
|
|
923
860
|
let targetIndex = -1;
|
|
@@ -926,10 +863,10 @@ var getHTMLSnippet = (element) => {
|
|
|
926
863
|
targetIndex = siblings.indexOf(element);
|
|
927
864
|
if (targetIndex > 0) {
|
|
928
865
|
const prevSibling = siblings[targetIndex - 1];
|
|
929
|
-
const prevId =
|
|
866
|
+
const prevId = extractSiblingIdentifier(prevSibling);
|
|
930
867
|
if (prevId && targetIndex <= 2) {
|
|
931
868
|
const indent2 = " ".repeat(ancestors.length);
|
|
932
|
-
lines.push(`${indent2} ${
|
|
869
|
+
lines.push(`${indent2} ${formatElementOpeningTag(prevSibling, true)}`);
|
|
933
870
|
lines.push(`${indent2} </${prevSibling.tagName.toLowerCase()}>`);
|
|
934
871
|
} else if (targetIndex > 0) {
|
|
935
872
|
const indent2 = " ".repeat(ancestors.length);
|
|
@@ -940,35 +877,44 @@ var getHTMLSnippet = (element) => {
|
|
|
940
877
|
}
|
|
941
878
|
}
|
|
942
879
|
const indent = " ".repeat(ancestors.length);
|
|
943
|
-
|
|
944
|
-
const
|
|
880
|
+
const lastAncestorComponent = ancestors.length > 0 ? ancestorComponents[ancestorComponents.length - 1] : null;
|
|
881
|
+
const showElementComponent = elementComponent && elementSource && elementComponent !== lastAncestorComponent;
|
|
882
|
+
if (showElementComponent) {
|
|
883
|
+
lines.push(`${indent} <${elementComponent} used-at="${elementSource}">`);
|
|
884
|
+
}
|
|
885
|
+
lines.push(`${indent} <!-- IMPORTANT: selected element -->`);
|
|
886
|
+
const textContent = extractTruncatedTextContent(element);
|
|
945
887
|
const childrenCount = element.children.length;
|
|
888
|
+
const elementIndent = `${indent}${showElementComponent ? " " : " "}`;
|
|
946
889
|
if (textContent && childrenCount === 0 && textContent.length < 40) {
|
|
947
890
|
lines.push(
|
|
948
|
-
`${
|
|
891
|
+
`${elementIndent}${formatElementOpeningTag(element)}${textContent}${formatElementClosingTag(
|
|
949
892
|
element
|
|
950
893
|
)}`
|
|
951
894
|
);
|
|
952
895
|
} else {
|
|
953
|
-
lines.push(
|
|
896
|
+
lines.push(`${elementIndent}${formatElementOpeningTag(element)}`);
|
|
954
897
|
if (textContent) {
|
|
955
|
-
lines.push(`${
|
|
898
|
+
lines.push(`${elementIndent} ${textContent}`);
|
|
956
899
|
}
|
|
957
900
|
if (childrenCount > 0) {
|
|
958
901
|
lines.push(
|
|
959
|
-
`${
|
|
902
|
+
`${elementIndent} ... (${childrenCount} element${childrenCount === 1 ? "" : "s"})`
|
|
960
903
|
);
|
|
961
904
|
}
|
|
962
|
-
lines.push(
|
|
905
|
+
lines.push(`${elementIndent}${formatElementClosingTag(element)}`);
|
|
906
|
+
}
|
|
907
|
+
if (showElementComponent) {
|
|
908
|
+
lines.push(`${indent} </${elementComponent}>`);
|
|
963
909
|
}
|
|
964
910
|
if (parent && targetIndex >= 0) {
|
|
965
911
|
const siblings = Array.from(parent.children);
|
|
966
912
|
const siblingsAfter = siblings.length - targetIndex - 1;
|
|
967
913
|
if (siblingsAfter > 0) {
|
|
968
914
|
const nextSibling = siblings[targetIndex + 1];
|
|
969
|
-
const nextId =
|
|
915
|
+
const nextId = extractSiblingIdentifier(nextSibling);
|
|
970
916
|
if (nextId && siblingsAfter <= 2) {
|
|
971
|
-
lines.push(`${indent} ${
|
|
917
|
+
lines.push(`${indent} ${formatElementOpeningTag(nextSibling, true)}`);
|
|
972
918
|
lines.push(`${indent} </${nextSibling.tagName.toLowerCase()}>`);
|
|
973
919
|
} else {
|
|
974
920
|
lines.push(
|
|
@@ -979,8 +925,14 @@ var getHTMLSnippet = (element) => {
|
|
|
979
925
|
}
|
|
980
926
|
for (let i = ancestors.length - 1; i >= 0; i--) {
|
|
981
927
|
const indent2 = " ".repeat(i);
|
|
982
|
-
lines.push(indent2
|
|
928
|
+
lines.push(`${indent2}${formatElementClosingTag(ancestors[i])}`);
|
|
929
|
+
const componentName = ancestorComponents[i];
|
|
930
|
+
const source = ancestorSources[i];
|
|
931
|
+
if (componentName && source && (i === ancestors.length - 1 || ancestorComponents[i + 1] !== componentName)) {
|
|
932
|
+
lines.push(`${indent2}</${componentName}>`);
|
|
933
|
+
}
|
|
983
934
|
}
|
|
935
|
+
lines.push("```");
|
|
984
936
|
return lines.join("\n");
|
|
985
937
|
};
|
|
986
938
|
|
|
@@ -1210,8 +1162,8 @@ var createElementBounds = (element) => {
|
|
|
1210
1162
|
};
|
|
1211
1163
|
|
|
1212
1164
|
// src/core.tsx
|
|
1213
|
-
var SUCCESS_LABEL_DURATION_MS = 1700;
|
|
1214
1165
|
var PROGRESS_INDICATOR_DELAY_MS = 150;
|
|
1166
|
+
var QUICK_REPRESS_THRESHOLD_MS = 150;
|
|
1215
1167
|
var init = (rawOptions) => {
|
|
1216
1168
|
const options = {
|
|
1217
1169
|
enabled: true,
|
|
@@ -1237,14 +1189,19 @@ var init = (rawOptions) => {
|
|
|
1237
1189
|
const [successLabels, setSuccessLabels] = solidJs.createSignal([]);
|
|
1238
1190
|
const [isActivated, setIsActivated] = solidJs.createSignal(false);
|
|
1239
1191
|
const [showProgressIndicator, setShowProgressIndicator] = solidJs.createSignal(false);
|
|
1192
|
+
const [didJustDrag, setDidJustDrag] = solidJs.createSignal(false);
|
|
1193
|
+
const [isModifierHeld, setIsModifierHeld] = solidJs.createSignal(false);
|
|
1194
|
+
const [copyStartX, setCopyStartX] = solidJs.createSignal(OFFSCREEN_POSITION);
|
|
1195
|
+
const [copyStartY, setCopyStartY] = solidJs.createSignal(OFFSCREEN_POSITION);
|
|
1240
1196
|
let holdTimerId = null;
|
|
1241
1197
|
let progressAnimationId = null;
|
|
1242
1198
|
let progressDelayTimerId = null;
|
|
1243
1199
|
let keydownSpamTimerId = null;
|
|
1200
|
+
let lastDeactivationTime = null;
|
|
1244
1201
|
const isRendererActive = solidJs.createMemo(() => isActivated() && !isCopying());
|
|
1245
1202
|
const hasValidMousePosition = solidJs.createMemo(() => mouseX() > OFFSCREEN_POSITION && mouseY() > OFFSCREEN_POSITION);
|
|
1246
1203
|
const isTargetKeyCombination = (event) => (event.metaKey || event.ctrlKey) && event.key.toLowerCase() === "c";
|
|
1247
|
-
const
|
|
1204
|
+
const showTemporaryGrabbedBox = (bounds) => {
|
|
1248
1205
|
const boxId = `grabbed-${Date.now()}-${Math.random()}`;
|
|
1249
1206
|
const createdAt = Date.now();
|
|
1250
1207
|
const newBox = {
|
|
@@ -1258,7 +1215,7 @@ var init = (rawOptions) => {
|
|
|
1258
1215
|
setGrabbedBoxes((previousBoxes) => previousBoxes.filter((box) => box.id !== boxId));
|
|
1259
1216
|
}, SUCCESS_LABEL_DURATION_MS);
|
|
1260
1217
|
};
|
|
1261
|
-
const
|
|
1218
|
+
const showTemporarySuccessLabel = (text, positionX, positionY) => {
|
|
1262
1219
|
const labelId = `success-${Date.now()}-${Math.random()}`;
|
|
1263
1220
|
setSuccessLabels((previousLabels) => [...previousLabels, {
|
|
1264
1221
|
id: labelId,
|
|
@@ -1270,24 +1227,15 @@ var init = (rawOptions) => {
|
|
|
1270
1227
|
setSuccessLabels((previousLabels) => previousLabels.filter((label) => label.id !== labelId));
|
|
1271
1228
|
}, SUCCESS_LABEL_DURATION_MS);
|
|
1272
1229
|
};
|
|
1273
|
-
const
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
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) => {
|
|
1230
|
+
const wrapInSelectedElementTags = (context) => `<selected_element>
|
|
1231
|
+
${context}
|
|
1232
|
+
</selected_element>`;
|
|
1233
|
+
const extractRelevantComputedStyles = (element) => {
|
|
1286
1234
|
const computed = window.getComputedStyle(element);
|
|
1287
1235
|
const rect = element.getBoundingClientRect();
|
|
1288
1236
|
return {
|
|
1289
|
-
width: `${rect.width}px`,
|
|
1290
|
-
height: `${rect.height}px`,
|
|
1237
|
+
width: `${Math.round(rect.width)}px`,
|
|
1238
|
+
height: `${Math.round(rect.height)}px`,
|
|
1291
1239
|
paddingTop: computed.paddingTop,
|
|
1292
1240
|
paddingRight: computed.paddingRight,
|
|
1293
1241
|
paddingBottom: computed.paddingBottom,
|
|
@@ -1296,7 +1244,7 @@ var init = (rawOptions) => {
|
|
|
1296
1244
|
opacity: computed.opacity
|
|
1297
1245
|
};
|
|
1298
1246
|
};
|
|
1299
|
-
const
|
|
1247
|
+
const createStructuredClipboardHtmlBlob = (elements) => {
|
|
1300
1248
|
const structuredData = {
|
|
1301
1249
|
elements: elements.map((element) => ({
|
|
1302
1250
|
tagName: element.tagName,
|
|
@@ -1310,60 +1258,74 @@ var init = (rawOptions) => {
|
|
|
1310
1258
|
type: "text/html"
|
|
1311
1259
|
});
|
|
1312
1260
|
};
|
|
1313
|
-
const
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
${formattedStackTrace}`;
|
|
1322
|
-
}
|
|
1323
|
-
return elementHtml;
|
|
1261
|
+
const extractElementTagName = (element) => (element.tagName || "").toLowerCase();
|
|
1262
|
+
const executeCopyOperation = async (positionX, positionY, operation) => {
|
|
1263
|
+
setCopyStartX(positionX);
|
|
1264
|
+
setCopyStartY(positionY);
|
|
1265
|
+
setIsCopying(true);
|
|
1266
|
+
await operation().finally(() => {
|
|
1267
|
+
setIsCopying(false);
|
|
1268
|
+
});
|
|
1324
1269
|
};
|
|
1325
|
-
const
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
const tagName = getElementTagName(targetElement2);
|
|
1329
|
-
addGrabbedBox(createElementBounds(targetElement2));
|
|
1270
|
+
const copySingleElementToClipboard = async (targetElement2) => {
|
|
1271
|
+
const tagName = extractElementTagName(targetElement2);
|
|
1272
|
+
showTemporaryGrabbedBox(createElementBounds(targetElement2));
|
|
1330
1273
|
try {
|
|
1331
|
-
const content = await
|
|
1332
|
-
const plainTextContent =
|
|
1333
|
-
const htmlContent =
|
|
1274
|
+
const content = await getHTMLSnippet(targetElement2);
|
|
1275
|
+
const plainTextContent = wrapInSelectedElementTags(content);
|
|
1276
|
+
const htmlContent = createStructuredClipboardHtmlBlob([{
|
|
1334
1277
|
tagName,
|
|
1335
|
-
content
|
|
1336
|
-
computedStyles:
|
|
1278
|
+
content,
|
|
1279
|
+
computedStyles: extractRelevantComputedStyles(targetElement2)
|
|
1337
1280
|
}]);
|
|
1338
|
-
|
|
1281
|
+
const clipboardData = [plainTextContent, htmlContent];
|
|
1282
|
+
try {
|
|
1283
|
+
const screenshotDataUrl = await modernScreenshot.domToPng(targetElement2);
|
|
1284
|
+
const response = await fetch(screenshotDataUrl);
|
|
1285
|
+
const pngBlob = await response.blob();
|
|
1286
|
+
const imagePngBlob = new Blob([pngBlob], {
|
|
1287
|
+
type: "image/png"
|
|
1288
|
+
});
|
|
1289
|
+
clipboardData.push(imagePngBlob);
|
|
1290
|
+
} catch {
|
|
1291
|
+
}
|
|
1292
|
+
await copyContent(clipboardData);
|
|
1339
1293
|
} catch {
|
|
1340
1294
|
}
|
|
1341
|
-
|
|
1295
|
+
showTemporarySuccessLabel(tagName ? `<${tagName}>` : "<element>", copyStartX(), copyStartY());
|
|
1342
1296
|
};
|
|
1343
|
-
const
|
|
1297
|
+
const copyMultipleElementsToClipboard = async (targetElements) => {
|
|
1344
1298
|
if (targetElements.length === 0) return;
|
|
1345
|
-
let minPositionX = Infinity;
|
|
1346
|
-
let minPositionY = Infinity;
|
|
1347
1299
|
for (const element of targetElements) {
|
|
1348
|
-
|
|
1349
|
-
minPositionX = Math.min(minPositionX, elementBounds.left);
|
|
1350
|
-
minPositionY = Math.min(minPositionY, elementBounds.top);
|
|
1351
|
-
addGrabbedBox(createElementBounds(element));
|
|
1300
|
+
showTemporaryGrabbedBox(createElementBounds(element));
|
|
1352
1301
|
}
|
|
1353
1302
|
try {
|
|
1354
|
-
const elementSnippets = await Promise.all(targetElements.map((element) =>
|
|
1303
|
+
const elementSnippets = await Promise.all(targetElements.map((element) => getHTMLSnippet(element)));
|
|
1355
1304
|
const combinedContent = elementSnippets.join("\n\n---\n\n");
|
|
1356
|
-
const plainTextContent =
|
|
1357
|
-
const structuredElements =
|
|
1358
|
-
tagName:
|
|
1359
|
-
content
|
|
1360
|
-
computedStyles:
|
|
1361
|
-
}))
|
|
1362
|
-
const htmlContent =
|
|
1363
|
-
|
|
1305
|
+
const plainTextContent = wrapInSelectedElementTags(combinedContent);
|
|
1306
|
+
const structuredElements = elementSnippets.map((content, index) => ({
|
|
1307
|
+
tagName: extractElementTagName(targetElements[index]),
|
|
1308
|
+
content,
|
|
1309
|
+
computedStyles: extractRelevantComputedStyles(targetElements[index])
|
|
1310
|
+
}));
|
|
1311
|
+
const htmlContent = createStructuredClipboardHtmlBlob(structuredElements);
|
|
1312
|
+
const clipboardData = [plainTextContent, htmlContent];
|
|
1313
|
+
if (targetElements.length > 0) {
|
|
1314
|
+
try {
|
|
1315
|
+
const screenshotDataUrl = await modernScreenshot.domToPng(targetElements[0]);
|
|
1316
|
+
const response = await fetch(screenshotDataUrl);
|
|
1317
|
+
const pngBlob = await response.blob();
|
|
1318
|
+
const imagePngBlob = new Blob([pngBlob], {
|
|
1319
|
+
type: "image/png"
|
|
1320
|
+
});
|
|
1321
|
+
clipboardData.push(imagePngBlob);
|
|
1322
|
+
} catch {
|
|
1323
|
+
}
|
|
1324
|
+
}
|
|
1325
|
+
await copyContent(clipboardData);
|
|
1364
1326
|
} catch {
|
|
1365
1327
|
}
|
|
1366
|
-
|
|
1328
|
+
showTemporarySuccessLabel(`${targetElements.length} elements`, copyStartX(), copyStartY());
|
|
1367
1329
|
};
|
|
1368
1330
|
const targetElement = solidJs.createMemo(() => {
|
|
1369
1331
|
if (!isRendererActive() || isDragging()) return null;
|
|
@@ -1384,16 +1346,16 @@ ${formattedStackTrace}`;
|
|
|
1384
1346
|
};
|
|
1385
1347
|
});
|
|
1386
1348
|
const DRAG_THRESHOLD_PX = 2;
|
|
1387
|
-
const
|
|
1349
|
+
const calculateDragDistance = (endX, endY) => ({
|
|
1388
1350
|
x: Math.abs(endX - dragStartX()),
|
|
1389
1351
|
y: Math.abs(endY - dragStartY())
|
|
1390
1352
|
});
|
|
1391
1353
|
const isDraggingBeyondThreshold = solidJs.createMemo(() => {
|
|
1392
1354
|
if (!isDragging()) return false;
|
|
1393
|
-
const dragDistance =
|
|
1355
|
+
const dragDistance = calculateDragDistance(mouseX(), mouseY());
|
|
1394
1356
|
return dragDistance.x > DRAG_THRESHOLD_PX || dragDistance.y > DRAG_THRESHOLD_PX;
|
|
1395
1357
|
});
|
|
1396
|
-
const
|
|
1358
|
+
const calculateDragRectangle = (endX, endY) => {
|
|
1397
1359
|
const dragX = Math.min(dragStartX(), endX);
|
|
1398
1360
|
const dragY = Math.min(dragStartY(), endY);
|
|
1399
1361
|
const dragWidth = Math.abs(endX - dragStartX());
|
|
@@ -1407,7 +1369,7 @@ ${formattedStackTrace}`;
|
|
|
1407
1369
|
};
|
|
1408
1370
|
const dragBounds = solidJs.createMemo(() => {
|
|
1409
1371
|
if (!isDraggingBeyondThreshold()) return void 0;
|
|
1410
|
-
const drag =
|
|
1372
|
+
const drag = calculateDragRectangle(mouseX(), mouseY());
|
|
1411
1373
|
return {
|
|
1412
1374
|
borderRadius: "0px",
|
|
1413
1375
|
height: drag.height,
|
|
@@ -1419,19 +1381,16 @@ ${formattedStackTrace}`;
|
|
|
1419
1381
|
});
|
|
1420
1382
|
const labelText = solidJs.createMemo(() => {
|
|
1421
1383
|
const element = targetElement();
|
|
1422
|
-
return element ? `<${
|
|
1384
|
+
return element ? `<${extractElementTagName(element)}>` : "<element>";
|
|
1423
1385
|
});
|
|
1424
|
-
const labelPosition = solidJs.createMemo(() => {
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
const isSameAsLast = solidJs.createMemo(() => {
|
|
1431
|
-
const currentElement = targetElement();
|
|
1432
|
-
const lastElement = lastGrabbedElement();
|
|
1433
|
-
return Boolean(currentElement) && currentElement === lastElement;
|
|
1386
|
+
const labelPosition = solidJs.createMemo(() => isCopying() ? {
|
|
1387
|
+
x: copyStartX(),
|
|
1388
|
+
y: copyStartY()
|
|
1389
|
+
} : {
|
|
1390
|
+
x: mouseX(),
|
|
1391
|
+
y: mouseY()
|
|
1434
1392
|
});
|
|
1393
|
+
const isSameAsLast = solidJs.createMemo(() => Boolean(targetElement() && targetElement() === lastGrabbedElement()));
|
|
1435
1394
|
solidJs.createEffect(solidJs.on(() => [targetElement(), lastGrabbedElement()], ([currentElement, lastElement]) => {
|
|
1436
1395
|
if (lastElement && currentElement && lastElement !== currentElement) {
|
|
1437
1396
|
setLastGrabbedElement(null);
|
|
@@ -1478,9 +1437,12 @@ ${formattedStackTrace}`;
|
|
|
1478
1437
|
setIsActivated(true);
|
|
1479
1438
|
document.body.style.cursor = "crosshair";
|
|
1480
1439
|
};
|
|
1481
|
-
const deactivateRenderer = () => {
|
|
1440
|
+
const deactivateRenderer = (shouldResetModifier = true) => {
|
|
1482
1441
|
setIsHoldingKeys(false);
|
|
1483
1442
|
setIsActivated(false);
|
|
1443
|
+
if (shouldResetModifier) {
|
|
1444
|
+
setIsModifierHeld(false);
|
|
1445
|
+
}
|
|
1484
1446
|
document.body.style.cursor = "";
|
|
1485
1447
|
if (isDragging()) {
|
|
1486
1448
|
setIsDragging(false);
|
|
@@ -1489,23 +1451,38 @@ ${formattedStackTrace}`;
|
|
|
1489
1451
|
if (holdTimerId) window.clearTimeout(holdTimerId);
|
|
1490
1452
|
if (keydownSpamTimerId) window.clearTimeout(keydownSpamTimerId);
|
|
1491
1453
|
stopProgressAnimation();
|
|
1454
|
+
lastDeactivationTime = Date.now();
|
|
1492
1455
|
};
|
|
1493
1456
|
const abortController = new AbortController();
|
|
1494
1457
|
const eventListenerSignal = abortController.signal;
|
|
1495
1458
|
window.addEventListener("keydown", (event) => {
|
|
1459
|
+
if (event.metaKey || event.ctrlKey) {
|
|
1460
|
+
setIsModifierHeld(true);
|
|
1461
|
+
}
|
|
1496
1462
|
if (event.key === "Escape" && isHoldingKeys()) {
|
|
1497
1463
|
deactivateRenderer();
|
|
1498
1464
|
return;
|
|
1499
1465
|
}
|
|
1500
1466
|
if (isKeyboardEventTriggeredByInput(event)) return;
|
|
1501
1467
|
if (isTargetKeyCombination(event)) {
|
|
1468
|
+
const wasRecentlyDeactivated = lastDeactivationTime !== null && Date.now() - lastDeactivationTime < QUICK_REPRESS_THRESHOLD_MS;
|
|
1502
1469
|
if (!isHoldingKeys()) {
|
|
1503
1470
|
setIsHoldingKeys(true);
|
|
1504
|
-
|
|
1505
|
-
holdTimerId = window.setTimeout(() => {
|
|
1471
|
+
if (wasRecentlyDeactivated && isModifierHeld()) {
|
|
1506
1472
|
activateRenderer();
|
|
1507
1473
|
options.onActivate?.();
|
|
1508
|
-
|
|
1474
|
+
const element = getElementAtPosition(mouseX(), mouseY());
|
|
1475
|
+
if (element) {
|
|
1476
|
+
setLastGrabbedElement(element);
|
|
1477
|
+
void executeCopyOperation(mouseX(), mouseY(), () => copySingleElementToClipboard(element));
|
|
1478
|
+
}
|
|
1479
|
+
} else {
|
|
1480
|
+
startProgressAnimation();
|
|
1481
|
+
holdTimerId = window.setTimeout(() => {
|
|
1482
|
+
activateRenderer();
|
|
1483
|
+
options.onActivate?.();
|
|
1484
|
+
}, options.keyHoldDuration);
|
|
1485
|
+
}
|
|
1509
1486
|
}
|
|
1510
1487
|
if (isActivated()) {
|
|
1511
1488
|
if (keydownSpamTimerId) window.clearTimeout(keydownSpamTimerId);
|
|
@@ -1518,11 +1495,16 @@ ${formattedStackTrace}`;
|
|
|
1518
1495
|
signal: eventListenerSignal
|
|
1519
1496
|
});
|
|
1520
1497
|
window.addEventListener("keyup", (event) => {
|
|
1521
|
-
if (!isHoldingKeys() && !isActivated()) return;
|
|
1522
|
-
const isReleasingC = event.key.toLowerCase() === "c";
|
|
1523
1498
|
const isReleasingModifier = !event.metaKey && !event.ctrlKey;
|
|
1524
|
-
|
|
1525
|
-
|
|
1499
|
+
const isReleasingC = event.key.toLowerCase() === "c";
|
|
1500
|
+
if (isReleasingModifier) {
|
|
1501
|
+
setIsModifierHeld(false);
|
|
1502
|
+
}
|
|
1503
|
+
if (!isHoldingKeys() && !isActivated()) return;
|
|
1504
|
+
if (isReleasingC) {
|
|
1505
|
+
deactivateRenderer(false);
|
|
1506
|
+
} else if (isReleasingModifier) {
|
|
1507
|
+
deactivateRenderer(true);
|
|
1526
1508
|
}
|
|
1527
1509
|
}, {
|
|
1528
1510
|
signal: eventListenerSignal,
|
|
@@ -1546,39 +1528,41 @@ ${formattedStackTrace}`;
|
|
|
1546
1528
|
});
|
|
1547
1529
|
window.addEventListener("mouseup", (event) => {
|
|
1548
1530
|
if (!isDragging()) return;
|
|
1549
|
-
const dragDistance =
|
|
1531
|
+
const dragDistance = calculateDragDistance(event.clientX, event.clientY);
|
|
1550
1532
|
const wasDragGesture = dragDistance.x > DRAG_THRESHOLD_PX || dragDistance.y > DRAG_THRESHOLD_PX;
|
|
1551
1533
|
setIsDragging(false);
|
|
1552
1534
|
document.body.style.userSelect = "";
|
|
1553
1535
|
if (wasDragGesture) {
|
|
1554
|
-
|
|
1536
|
+
setDidJustDrag(true);
|
|
1537
|
+
const dragRect = calculateDragRectangle(event.clientX, event.clientY);
|
|
1555
1538
|
const elements = getElementsInDrag(dragRect, isValidGrabbableElement);
|
|
1556
1539
|
if (elements.length > 0) {
|
|
1557
|
-
|
|
1558
|
-
void handleMultipleCopy(elements).finally(() => {
|
|
1559
|
-
setIsCopying(false);
|
|
1560
|
-
});
|
|
1540
|
+
void executeCopyOperation(event.clientX, event.clientY, () => copyMultipleElementsToClipboard(elements));
|
|
1561
1541
|
} else {
|
|
1562
1542
|
const fallbackElements = getElementsInDragLoose(dragRect, isValidGrabbableElement);
|
|
1563
1543
|
if (fallbackElements.length > 0) {
|
|
1564
|
-
|
|
1565
|
-
void handleMultipleCopy(fallbackElements).finally(() => {
|
|
1566
|
-
setIsCopying(false);
|
|
1567
|
-
});
|
|
1544
|
+
void executeCopyOperation(event.clientX, event.clientY, () => copyMultipleElementsToClipboard(fallbackElements));
|
|
1568
1545
|
}
|
|
1569
1546
|
}
|
|
1570
1547
|
} else {
|
|
1571
1548
|
const element = getElementAtPosition(event.clientX, event.clientY);
|
|
1572
1549
|
if (!element) return;
|
|
1573
|
-
setIsCopying(true);
|
|
1574
1550
|
setLastGrabbedElement(element);
|
|
1575
|
-
void
|
|
1576
|
-
setIsCopying(false);
|
|
1577
|
-
});
|
|
1551
|
+
void executeCopyOperation(event.clientX, event.clientY, () => copySingleElementToClipboard(element));
|
|
1578
1552
|
}
|
|
1579
1553
|
}, {
|
|
1580
1554
|
signal: eventListenerSignal
|
|
1581
1555
|
});
|
|
1556
|
+
window.addEventListener("click", (event) => {
|
|
1557
|
+
if (didJustDrag()) {
|
|
1558
|
+
event.preventDefault();
|
|
1559
|
+
event.stopPropagation();
|
|
1560
|
+
setDidJustDrag(false);
|
|
1561
|
+
}
|
|
1562
|
+
}, {
|
|
1563
|
+
signal: eventListenerSignal,
|
|
1564
|
+
capture: true
|
|
1565
|
+
});
|
|
1582
1566
|
document.addEventListener("visibilitychange", () => {
|
|
1583
1567
|
if (document.hidden) {
|
|
1584
1568
|
setGrabbedBoxes([]);
|