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