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 +189 -256
- package/dist/index.global.js +16 -18
- package/dist/index.js +191 -258
- package/package.json +3 -1
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(`<
|
|
262
|
-
var _tmpl$4 = /* @__PURE__ */ web.template(`<div style=
|
|
263
|
-
var _tmpl$5 = /* @__PURE__ */ web.template(`<
|
|
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
|
-
},
|
|
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) +
|
|
352
|
-
top: Math.round(currentY) +
|
|
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 -
|
|
355
|
-
top: Math.round(currentY) +
|
|
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) +
|
|
358
|
-
top: Math.round(currentY) - boundingRect.height -
|
|
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 -
|
|
361
|
-
top: Math.round(currentY) - boundingRect.height -
|
|
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$
|
|
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.
|
|
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
|
-
|
|
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$
|
|
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
|
-
|
|
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);
|
|
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
|
|
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
|
-
});
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
893
|
-
|
|
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
|
|
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
|
-
|
|
916
|
-
lines.push(
|
|
917
|
-
|
|
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
|
-
|
|
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 =
|
|
865
|
+
const prevId = extractSiblingIdentifier(prevSibling);
|
|
930
866
|
if (prevId && targetIndex <= 2) {
|
|
931
867
|
const indent2 = " ".repeat(ancestors.length);
|
|
932
|
-
lines.push(`${indent2} ${
|
|
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
|
-
|
|
944
|
-
const
|
|
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
|
-
`${
|
|
890
|
+
`${elementIndent}${formatElementOpeningTag(element)}${textContent}${formatElementClosingTag(
|
|
949
891
|
element
|
|
950
892
|
)}`
|
|
951
893
|
);
|
|
952
894
|
} else {
|
|
953
|
-
lines.push(
|
|
895
|
+
lines.push(`${elementIndent}${formatElementOpeningTag(element)}`);
|
|
954
896
|
if (textContent) {
|
|
955
|
-
lines.push(`${
|
|
897
|
+
lines.push(`${elementIndent} ${textContent}`);
|
|
956
898
|
}
|
|
957
899
|
if (childrenCount > 0) {
|
|
958
900
|
lines.push(
|
|
959
|
-
`${
|
|
901
|
+
`${elementIndent} ... (${childrenCount} element${childrenCount === 1 ? "" : "s"})`
|
|
960
902
|
);
|
|
961
903
|
}
|
|
962
|
-
lines.push(
|
|
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 =
|
|
914
|
+
const nextId = extractSiblingIdentifier(nextSibling);
|
|
970
915
|
if (nextId && siblingsAfter <= 2) {
|
|
971
|
-
lines.push(`${indent} ${
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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) => {
|
|
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
|
|
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
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
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
|
|
1326
|
-
|
|
1327
|
-
|
|
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
|
|
1332
|
-
const plainTextContent =
|
|
1333
|
-
const htmlContent =
|
|
1270
|
+
const content = await getHTMLSnippet(targetElement2);
|
|
1271
|
+
const plainTextContent = wrapInSelectedElementTags(content);
|
|
1272
|
+
const htmlContent = createStructuredClipboardHtmlBlob([{
|
|
1334
1273
|
tagName,
|
|
1335
|
-
content
|
|
1336
|
-
computedStyles:
|
|
1274
|
+
content,
|
|
1275
|
+
computedStyles: extractRelevantComputedStyles(targetElement2)
|
|
1337
1276
|
}]);
|
|
1338
1277
|
await copyContent([plainTextContent, htmlContent]);
|
|
1339
1278
|
} catch {
|
|
1340
1279
|
}
|
|
1341
|
-
|
|
1280
|
+
showTemporarySuccessLabel(tagName ? `<${tagName}>` : "<element>", copyStartX(), copyStartY());
|
|
1342
1281
|
};
|
|
1343
|
-
const
|
|
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
|
-
|
|
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) =>
|
|
1288
|
+
const elementSnippets = await Promise.all(targetElements.map((element) => getHTMLSnippet(element)));
|
|
1355
1289
|
const combinedContent = elementSnippets.join("\n\n---\n\n");
|
|
1356
|
-
const plainTextContent =
|
|
1357
|
-
const structuredElements =
|
|
1358
|
-
tagName:
|
|
1359
|
-
content
|
|
1360
|
-
computedStyles:
|
|
1361
|
-
}))
|
|
1362
|
-
const htmlContent =
|
|
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
|
-
|
|
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
|
|
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 =
|
|
1327
|
+
const dragDistance = calculateDragDistance(mouseX(), mouseY());
|
|
1394
1328
|
return dragDistance.x > DRAG_THRESHOLD_PX || dragDistance.y > DRAG_THRESHOLD_PX;
|
|
1395
1329
|
});
|
|
1396
|
-
const
|
|
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 =
|
|
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 ? `<${
|
|
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
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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([]);
|