react-grab 0.0.6 → 0.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +46 -0
- package/dist/core.cjs +259 -0
- package/dist/core.d.cts +10 -0
- package/dist/core.d.ts +10 -0
- package/dist/core.js +254 -0
- package/dist/index.global.js +2672 -61
- package/package.json +4 -7
- package/dist/index.cjs +0 -340
- package/dist/index.d.cts +0 -3
- package/dist/index.js +0 -338
package/dist/index.js
DELETED
|
@@ -1,338 +0,0 @@
|
|
|
1
|
-
import { getFiberFromHostInstance } from 'bippy';
|
|
2
|
-
import { getFiberStackTrace, getOwnerStack } from 'bippy/dist/source';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* @license MIT
|
|
6
|
-
*
|
|
7
|
-
* Copyright (c) 2025 Aiden Bai
|
|
8
|
-
*
|
|
9
|
-
* This source code is licensed under the MIT license found in the
|
|
10
|
-
* LICENSE file in the root directory of this source tree.
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
var getStack = async (element) => {
|
|
14
|
-
const fiber = getFiberFromHostInstance(element);
|
|
15
|
-
if (!fiber) return null;
|
|
16
|
-
const stackTrace = getFiberStackTrace(fiber);
|
|
17
|
-
const rawOwnerStack = await getOwnerStack(stackTrace);
|
|
18
|
-
const stack = rawOwnerStack.map((item) => ({
|
|
19
|
-
componentName: item.name,
|
|
20
|
-
fileName: item.source?.fileName
|
|
21
|
-
}));
|
|
22
|
-
return stack;
|
|
23
|
-
};
|
|
24
|
-
var filterStack = (stack) => {
|
|
25
|
-
return stack.filter(
|
|
26
|
-
(item) => item.fileName && !item.fileName.includes("node_modules") && item.componentName.length > 1 && !item.fileName.startsWith("_")
|
|
27
|
-
);
|
|
28
|
-
};
|
|
29
|
-
var findCommonRoot = (paths) => {
|
|
30
|
-
if (paths.length === 0) return "";
|
|
31
|
-
if (paths.length === 1) {
|
|
32
|
-
const lastSlash2 = paths[0].lastIndexOf("/");
|
|
33
|
-
return lastSlash2 > 0 ? paths[0].substring(0, lastSlash2 + 1) : "";
|
|
34
|
-
}
|
|
35
|
-
let commonPrefix = paths[0];
|
|
36
|
-
for (let i = 1; i < paths.length; i++) {
|
|
37
|
-
const path = paths[i];
|
|
38
|
-
let j = 0;
|
|
39
|
-
while (j < commonPrefix.length && j < path.length && commonPrefix[j] === path[j]) {
|
|
40
|
-
j++;
|
|
41
|
-
}
|
|
42
|
-
commonPrefix = commonPrefix.substring(0, j);
|
|
43
|
-
}
|
|
44
|
-
const lastSlash = commonPrefix.lastIndexOf("/");
|
|
45
|
-
return lastSlash > 0 ? commonPrefix.substring(0, lastSlash + 1) : "";
|
|
46
|
-
};
|
|
47
|
-
var serializeStack = (stack) => {
|
|
48
|
-
const filePaths = stack.map((item) => item.fileName).filter((path) => !!path);
|
|
49
|
-
const commonRoot = findCommonRoot(filePaths);
|
|
50
|
-
return stack.map((item) => {
|
|
51
|
-
let fileName = item.fileName;
|
|
52
|
-
if (fileName && commonRoot) {
|
|
53
|
-
fileName = fileName.startsWith(commonRoot) ? fileName.substring(commonRoot.length) : fileName;
|
|
54
|
-
}
|
|
55
|
-
return `${item.componentName}${fileName ? ` (${fileName})` : ""}`;
|
|
56
|
-
}).join("\n");
|
|
57
|
-
};
|
|
58
|
-
var getHTMLSnippet = (element) => {
|
|
59
|
-
const getElementTag = (el) => {
|
|
60
|
-
const tagName = el.tagName.toLowerCase();
|
|
61
|
-
const importantAttrs = [
|
|
62
|
-
"id",
|
|
63
|
-
"class",
|
|
64
|
-
"name",
|
|
65
|
-
"type",
|
|
66
|
-
"role",
|
|
67
|
-
"aria-label"
|
|
68
|
-
];
|
|
69
|
-
const maxValueLength = 50;
|
|
70
|
-
const attrs = Array.from(el.attributes).filter((attr) => {
|
|
71
|
-
return importantAttrs.includes(attr.name) || attr.name.startsWith("data-");
|
|
72
|
-
}).map((attr) => {
|
|
73
|
-
let value = attr.value;
|
|
74
|
-
if (value.length > maxValueLength) {
|
|
75
|
-
value = value.substring(0, maxValueLength) + "...";
|
|
76
|
-
}
|
|
77
|
-
return `${attr.name}="${value}"`;
|
|
78
|
-
}).join(" ");
|
|
79
|
-
return attrs ? `<${tagName} ${attrs}>` : `<${tagName}>`;
|
|
80
|
-
};
|
|
81
|
-
const getClosingTag = (el) => {
|
|
82
|
-
return `</${el.tagName.toLowerCase()}>`;
|
|
83
|
-
};
|
|
84
|
-
const getChildrenCount = (el) => {
|
|
85
|
-
const children = Array.from(el.children);
|
|
86
|
-
return children.length;
|
|
87
|
-
};
|
|
88
|
-
const getTextContent = (el) => {
|
|
89
|
-
let text = el.textContent || "";
|
|
90
|
-
text = text.trim();
|
|
91
|
-
text = text.replace(/\s+/g, " ");
|
|
92
|
-
const maxLength = 100;
|
|
93
|
-
if (text.length > maxLength) {
|
|
94
|
-
text = text.substring(0, maxLength) + "...";
|
|
95
|
-
}
|
|
96
|
-
return text;
|
|
97
|
-
};
|
|
98
|
-
const lines = [];
|
|
99
|
-
const parent = element.parentElement;
|
|
100
|
-
if (parent) {
|
|
101
|
-
lines.push(getElementTag(parent));
|
|
102
|
-
const siblings = Array.from(parent.children);
|
|
103
|
-
const targetIndex = siblings.indexOf(element);
|
|
104
|
-
if (targetIndex > 0) {
|
|
105
|
-
lines.push(
|
|
106
|
-
` ... (${targetIndex} element${targetIndex === 1 ? "" : "s"})`
|
|
107
|
-
);
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
const indent = parent ? " " : "";
|
|
111
|
-
lines.push(indent + "<!-- SELECTED -->");
|
|
112
|
-
lines.push(indent + getElementTag(element));
|
|
113
|
-
const textContent = getTextContent(element);
|
|
114
|
-
const childrenCount = getChildrenCount(element);
|
|
115
|
-
if (textContent) {
|
|
116
|
-
lines.push(`${indent} ${textContent}`);
|
|
117
|
-
}
|
|
118
|
-
if (childrenCount > 0) {
|
|
119
|
-
lines.push(
|
|
120
|
-
`${indent} ... (${childrenCount} element${childrenCount === 1 ? "" : "s"})`
|
|
121
|
-
);
|
|
122
|
-
}
|
|
123
|
-
lines.push(indent + getClosingTag(element));
|
|
124
|
-
if (parent) {
|
|
125
|
-
const siblings = Array.from(parent.children);
|
|
126
|
-
const targetIndex = siblings.indexOf(element);
|
|
127
|
-
const siblingsAfter = siblings.length - targetIndex - 1;
|
|
128
|
-
if (siblingsAfter > 0) {
|
|
129
|
-
lines.push(
|
|
130
|
-
` ... (${siblingsAfter} element${siblingsAfter === 1 ? "" : "s"})`
|
|
131
|
-
);
|
|
132
|
-
}
|
|
133
|
-
lines.push(getClosingTag(parent));
|
|
134
|
-
}
|
|
135
|
-
return lines.join("\n");
|
|
136
|
-
};
|
|
137
|
-
|
|
138
|
-
// src/index.ts
|
|
139
|
-
var init = () => {
|
|
140
|
-
let metaKeyTimer = null;
|
|
141
|
-
let overlay = null;
|
|
142
|
-
let isActive = false;
|
|
143
|
-
let isLocked = false;
|
|
144
|
-
let currentElement = null;
|
|
145
|
-
let animationFrame = null;
|
|
146
|
-
let pendingCopyText = null;
|
|
147
|
-
const copyToClipboard = async (text) => {
|
|
148
|
-
if (!document.hasFocus()) {
|
|
149
|
-
pendingCopyText = text;
|
|
150
|
-
return;
|
|
151
|
-
}
|
|
152
|
-
try {
|
|
153
|
-
await navigator.clipboard.writeText(text);
|
|
154
|
-
pendingCopyText = null;
|
|
155
|
-
hideOverlay();
|
|
156
|
-
} catch {
|
|
157
|
-
const textarea = document.createElement("textarea");
|
|
158
|
-
textarea.value = text;
|
|
159
|
-
textarea.style.position = "fixed";
|
|
160
|
-
textarea.style.left = "-999999px";
|
|
161
|
-
textarea.style.top = "-999999px";
|
|
162
|
-
document.body.appendChild(textarea);
|
|
163
|
-
textarea.focus();
|
|
164
|
-
textarea.select();
|
|
165
|
-
try {
|
|
166
|
-
document.execCommand("copy");
|
|
167
|
-
pendingCopyText = null;
|
|
168
|
-
hideOverlay();
|
|
169
|
-
} catch (execErr) {
|
|
170
|
-
console.error("Failed to copy to clipboard:", execErr);
|
|
171
|
-
hideOverlay();
|
|
172
|
-
}
|
|
173
|
-
document.body.removeChild(textarea);
|
|
174
|
-
}
|
|
175
|
-
};
|
|
176
|
-
const handleWindowFocus = () => {
|
|
177
|
-
if (pendingCopyText) {
|
|
178
|
-
void copyToClipboard(pendingCopyText);
|
|
179
|
-
}
|
|
180
|
-
};
|
|
181
|
-
let currentX = 0;
|
|
182
|
-
let currentY = 0;
|
|
183
|
-
let currentWidth = 0;
|
|
184
|
-
let currentHeight = 0;
|
|
185
|
-
let targetX = 0;
|
|
186
|
-
let targetY = 0;
|
|
187
|
-
let targetWidth = 0;
|
|
188
|
-
let targetHeight = 0;
|
|
189
|
-
let targetBorderRadius = "";
|
|
190
|
-
const isInsideInputOrTextarea = () => {
|
|
191
|
-
const activeElement = document.activeElement;
|
|
192
|
-
return activeElement instanceof HTMLInputElement || activeElement instanceof HTMLTextAreaElement || activeElement?.tagName === "INPUT" || activeElement?.tagName === "TEXTAREA";
|
|
193
|
-
};
|
|
194
|
-
const createOverlay = () => {
|
|
195
|
-
const div = document.createElement("div");
|
|
196
|
-
div.style.position = "fixed";
|
|
197
|
-
div.style.border = "2px solid #3b82f6";
|
|
198
|
-
div.style.backgroundColor = "rgba(59, 130, 246, 0.1)";
|
|
199
|
-
div.style.pointerEvents = "none";
|
|
200
|
-
div.style.zIndex = "999999";
|
|
201
|
-
div.style.transition = "none";
|
|
202
|
-
document.body.appendChild(div);
|
|
203
|
-
return div;
|
|
204
|
-
};
|
|
205
|
-
const lerp = (start, end, factor) => {
|
|
206
|
-
return start + (end - start) * factor;
|
|
207
|
-
};
|
|
208
|
-
const updateOverlayPosition = () => {
|
|
209
|
-
if (!overlay || !isActive) return;
|
|
210
|
-
const factor = 0.5;
|
|
211
|
-
currentX = lerp(currentX, targetX, factor);
|
|
212
|
-
currentY = lerp(currentY, targetY, factor);
|
|
213
|
-
currentWidth = lerp(currentWidth, targetWidth, factor);
|
|
214
|
-
currentHeight = lerp(currentHeight, targetHeight, factor);
|
|
215
|
-
overlay.style.left = `${currentX}px`;
|
|
216
|
-
overlay.style.top = `${currentY}px`;
|
|
217
|
-
overlay.style.width = `${currentWidth}px`;
|
|
218
|
-
overlay.style.height = `${currentHeight}px`;
|
|
219
|
-
overlay.style.borderRadius = targetBorderRadius;
|
|
220
|
-
animationFrame = requestAnimationFrame(updateOverlayPosition);
|
|
221
|
-
};
|
|
222
|
-
const handleMouseMove = (e) => {
|
|
223
|
-
if (isLocked) return;
|
|
224
|
-
const element = document.elementFromPoint(e.clientX, e.clientY);
|
|
225
|
-
if (!element || element === overlay) return;
|
|
226
|
-
currentElement = element;
|
|
227
|
-
const rect = element.getBoundingClientRect();
|
|
228
|
-
const computedStyle = window.getComputedStyle(element);
|
|
229
|
-
targetX = rect.left;
|
|
230
|
-
targetY = rect.top;
|
|
231
|
-
targetWidth = rect.width;
|
|
232
|
-
targetHeight = rect.height;
|
|
233
|
-
targetBorderRadius = computedStyle.borderRadius;
|
|
234
|
-
};
|
|
235
|
-
const handleClick = (e) => {
|
|
236
|
-
if (!isActive) return;
|
|
237
|
-
e.preventDefault();
|
|
238
|
-
e.stopPropagation();
|
|
239
|
-
e.stopImmediatePropagation();
|
|
240
|
-
isLocked = true;
|
|
241
|
-
const elementToInspect = currentElement;
|
|
242
|
-
if (elementToInspect) {
|
|
243
|
-
void getStack(elementToInspect).then((stack) => {
|
|
244
|
-
if (!stack) {
|
|
245
|
-
hideOverlay();
|
|
246
|
-
return;
|
|
247
|
-
}
|
|
248
|
-
const serializedStack = serializeStack(filterStack(stack));
|
|
249
|
-
const htmlSnippet = getHTMLSnippet(elementToInspect);
|
|
250
|
-
const payload = `## Referenced element
|
|
251
|
-
${htmlSnippet}
|
|
252
|
-
|
|
253
|
-
Import traces:
|
|
254
|
-
${serializedStack}
|
|
255
|
-
|
|
256
|
-
Page: ${window.location.href}`;
|
|
257
|
-
void copyToClipboard(payload);
|
|
258
|
-
});
|
|
259
|
-
}
|
|
260
|
-
};
|
|
261
|
-
const handleMouseDown = (e) => {
|
|
262
|
-
if (!isActive) return;
|
|
263
|
-
e.preventDefault();
|
|
264
|
-
e.stopPropagation();
|
|
265
|
-
e.stopImmediatePropagation();
|
|
266
|
-
};
|
|
267
|
-
const showOverlay = () => {
|
|
268
|
-
if (!overlay) {
|
|
269
|
-
overlay = createOverlay();
|
|
270
|
-
}
|
|
271
|
-
isActive = true;
|
|
272
|
-
overlay.style.display = "block";
|
|
273
|
-
currentX = targetX;
|
|
274
|
-
currentY = targetY;
|
|
275
|
-
currentWidth = targetWidth;
|
|
276
|
-
currentHeight = targetHeight;
|
|
277
|
-
updateOverlayPosition();
|
|
278
|
-
};
|
|
279
|
-
const hideOverlay = () => {
|
|
280
|
-
isActive = false;
|
|
281
|
-
isLocked = false;
|
|
282
|
-
if (overlay) {
|
|
283
|
-
overlay.style.display = "none";
|
|
284
|
-
}
|
|
285
|
-
if (animationFrame) {
|
|
286
|
-
cancelAnimationFrame(animationFrame);
|
|
287
|
-
animationFrame = null;
|
|
288
|
-
}
|
|
289
|
-
currentElement = null;
|
|
290
|
-
};
|
|
291
|
-
const handleKeyDown = (e) => {
|
|
292
|
-
if (e.metaKey && !metaKeyTimer && !isActive) {
|
|
293
|
-
metaKeyTimer = setTimeout(() => {
|
|
294
|
-
if (!isInsideInputOrTextarea()) {
|
|
295
|
-
showOverlay();
|
|
296
|
-
}
|
|
297
|
-
metaKeyTimer = null;
|
|
298
|
-
}, 750);
|
|
299
|
-
}
|
|
300
|
-
};
|
|
301
|
-
const handleKeyUp = (e) => {
|
|
302
|
-
if (!e.metaKey) {
|
|
303
|
-
if (metaKeyTimer) {
|
|
304
|
-
clearTimeout(metaKeyTimer);
|
|
305
|
-
metaKeyTimer = null;
|
|
306
|
-
}
|
|
307
|
-
if (isActive && !isLocked) {
|
|
308
|
-
hideOverlay();
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
};
|
|
312
|
-
document.addEventListener("keydown", handleKeyDown);
|
|
313
|
-
document.addEventListener("keyup", handleKeyUp);
|
|
314
|
-
document.addEventListener("mousemove", handleMouseMove);
|
|
315
|
-
document.addEventListener("mousedown", handleMouseDown, true);
|
|
316
|
-
document.addEventListener("click", handleClick, true);
|
|
317
|
-
window.addEventListener("focus", handleWindowFocus);
|
|
318
|
-
return () => {
|
|
319
|
-
if (metaKeyTimer) {
|
|
320
|
-
clearTimeout(metaKeyTimer);
|
|
321
|
-
}
|
|
322
|
-
if (animationFrame) {
|
|
323
|
-
cancelAnimationFrame(animationFrame);
|
|
324
|
-
}
|
|
325
|
-
if (overlay && overlay.parentNode) {
|
|
326
|
-
overlay.parentNode.removeChild(overlay);
|
|
327
|
-
}
|
|
328
|
-
document.removeEventListener("keydown", handleKeyDown);
|
|
329
|
-
document.removeEventListener("keyup", handleKeyUp);
|
|
330
|
-
document.removeEventListener("mousemove", handleMouseMove);
|
|
331
|
-
document.removeEventListener("mousedown", handleMouseDown, true);
|
|
332
|
-
document.removeEventListener("click", handleClick, true);
|
|
333
|
-
window.removeEventListener("focus", handleWindowFocus);
|
|
334
|
-
};
|
|
335
|
-
};
|
|
336
|
-
init();
|
|
337
|
-
|
|
338
|
-
export { init };
|