react-grab 0.0.7 → 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/{index.cjs → core.cjs} +6 -201
- package/dist/core.d.cts +10 -0
- package/dist/core.d.ts +10 -0
- package/dist/{index.js → core.js} +3 -201
- package/dist/index.global.js +2672 -61
- package/package.json +4 -7
- package/dist/index.d.cts +0 -3
package/README.md
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# react-grab
|
|
2
|
+
|
|
3
|
+
Inspect React components and copy their source file paths to clipboard.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install react-grab
|
|
9
|
+
# or
|
|
10
|
+
pnpm add react-grab
|
|
11
|
+
# or
|
|
12
|
+
yarn add react-grab
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
### Next.js (App Router)
|
|
18
|
+
|
|
19
|
+
Add to your `app/layout.tsx`:
|
|
20
|
+
|
|
21
|
+
```jsx
|
|
22
|
+
import Script from "next/script";
|
|
23
|
+
|
|
24
|
+
export default function RootLayout({ children }) {
|
|
25
|
+
return (
|
|
26
|
+
<html>
|
|
27
|
+
<head>
|
|
28
|
+
{process.env.NODE_ENV === "development" && (
|
|
29
|
+
<Script
|
|
30
|
+
src="//unpkg.com/react-grab@0.0.7/dist/index.global.js"
|
|
31
|
+
crossOrigin="anonymous"
|
|
32
|
+
strategy="beforeInteractive"
|
|
33
|
+
/>
|
|
34
|
+
)}
|
|
35
|
+
</head>
|
|
36
|
+
<body>{children}</body>
|
|
37
|
+
</html>
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### How it works
|
|
43
|
+
|
|
44
|
+
1. Hold **Cmd** (Mac) for ~1 second to activate
|
|
45
|
+
2. Hover over any element on the page
|
|
46
|
+
3. Click to copy component stack trace and HTML to clipboard
|
|
@@ -16,7 +16,9 @@ var getStack = async (element) => {
|
|
|
16
16
|
const fiber = bippy.getFiberFromHostInstance(element);
|
|
17
17
|
if (!fiber) return null;
|
|
18
18
|
const stackTrace = source.getFiberStackTrace(fiber);
|
|
19
|
+
console.log(stackTrace);
|
|
19
20
|
const rawOwnerStack = await source.getOwnerStack(stackTrace);
|
|
21
|
+
console.log(rawOwnerStack);
|
|
20
22
|
const stack = rawOwnerStack.map((item) => ({
|
|
21
23
|
componentName: item.name,
|
|
22
24
|
fileName: item.source?.fileName
|
|
@@ -251,204 +253,7 @@ var getHTMLSnippet = (element) => {
|
|
|
251
253
|
return lines.join("\n");
|
|
252
254
|
};
|
|
253
255
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
let isActive = false;
|
|
259
|
-
let isLocked = false;
|
|
260
|
-
let currentElement = null;
|
|
261
|
-
let animationFrame = null;
|
|
262
|
-
let pendingCopyText = null;
|
|
263
|
-
const copyToClipboard = async (text) => {
|
|
264
|
-
if (!document.hasFocus()) {
|
|
265
|
-
pendingCopyText = text;
|
|
266
|
-
return;
|
|
267
|
-
}
|
|
268
|
-
try {
|
|
269
|
-
await navigator.clipboard.writeText(text);
|
|
270
|
-
pendingCopyText = null;
|
|
271
|
-
hideOverlay();
|
|
272
|
-
} catch {
|
|
273
|
-
const textarea = document.createElement("textarea");
|
|
274
|
-
textarea.value = text;
|
|
275
|
-
textarea.style.position = "fixed";
|
|
276
|
-
textarea.style.left = "-999999px";
|
|
277
|
-
textarea.style.top = "-999999px";
|
|
278
|
-
document.body.appendChild(textarea);
|
|
279
|
-
textarea.focus();
|
|
280
|
-
textarea.select();
|
|
281
|
-
try {
|
|
282
|
-
document.execCommand("copy");
|
|
283
|
-
pendingCopyText = null;
|
|
284
|
-
hideOverlay();
|
|
285
|
-
} catch (execErr) {
|
|
286
|
-
console.error("Failed to copy to clipboard:", execErr);
|
|
287
|
-
hideOverlay();
|
|
288
|
-
}
|
|
289
|
-
document.body.removeChild(textarea);
|
|
290
|
-
}
|
|
291
|
-
};
|
|
292
|
-
const handleWindowFocus = () => {
|
|
293
|
-
if (pendingCopyText) {
|
|
294
|
-
void copyToClipboard(pendingCopyText);
|
|
295
|
-
}
|
|
296
|
-
};
|
|
297
|
-
let currentX = 0;
|
|
298
|
-
let currentY = 0;
|
|
299
|
-
let currentWidth = 0;
|
|
300
|
-
let currentHeight = 0;
|
|
301
|
-
let targetX = 0;
|
|
302
|
-
let targetY = 0;
|
|
303
|
-
let targetWidth = 0;
|
|
304
|
-
let targetHeight = 0;
|
|
305
|
-
let targetBorderRadius = "";
|
|
306
|
-
const isInsideInputOrTextarea = () => {
|
|
307
|
-
const activeElement = document.activeElement;
|
|
308
|
-
return activeElement instanceof HTMLInputElement || activeElement instanceof HTMLTextAreaElement || activeElement?.tagName === "INPUT" || activeElement?.tagName === "TEXTAREA";
|
|
309
|
-
};
|
|
310
|
-
const createOverlay = () => {
|
|
311
|
-
const div = document.createElement("div");
|
|
312
|
-
div.style.position = "fixed";
|
|
313
|
-
div.style.border = "2px solid #3b82f6";
|
|
314
|
-
div.style.backgroundColor = "rgba(59, 130, 246, 0.1)";
|
|
315
|
-
div.style.pointerEvents = "none";
|
|
316
|
-
div.style.zIndex = "999999";
|
|
317
|
-
div.style.transition = "none";
|
|
318
|
-
document.body.appendChild(div);
|
|
319
|
-
return div;
|
|
320
|
-
};
|
|
321
|
-
const lerp = (start, end, factor) => {
|
|
322
|
-
return start + (end - start) * factor;
|
|
323
|
-
};
|
|
324
|
-
const updateOverlayPosition = () => {
|
|
325
|
-
if (!overlay || !isActive) return;
|
|
326
|
-
const factor = 0.5;
|
|
327
|
-
currentX = lerp(currentX, targetX, factor);
|
|
328
|
-
currentY = lerp(currentY, targetY, factor);
|
|
329
|
-
currentWidth = lerp(currentWidth, targetWidth, factor);
|
|
330
|
-
currentHeight = lerp(currentHeight, targetHeight, factor);
|
|
331
|
-
overlay.style.left = `${currentX}px`;
|
|
332
|
-
overlay.style.top = `${currentY}px`;
|
|
333
|
-
overlay.style.width = `${currentWidth}px`;
|
|
334
|
-
overlay.style.height = `${currentHeight}px`;
|
|
335
|
-
overlay.style.borderRadius = targetBorderRadius;
|
|
336
|
-
animationFrame = requestAnimationFrame(updateOverlayPosition);
|
|
337
|
-
};
|
|
338
|
-
const handleMouseMove = (e) => {
|
|
339
|
-
if (isLocked) return;
|
|
340
|
-
const element = document.elementFromPoint(e.clientX, e.clientY);
|
|
341
|
-
if (!element || element === overlay) return;
|
|
342
|
-
currentElement = element;
|
|
343
|
-
const rect = element.getBoundingClientRect();
|
|
344
|
-
const computedStyle = window.getComputedStyle(element);
|
|
345
|
-
targetX = rect.left;
|
|
346
|
-
targetY = rect.top;
|
|
347
|
-
targetWidth = rect.width;
|
|
348
|
-
targetHeight = rect.height;
|
|
349
|
-
targetBorderRadius = computedStyle.borderRadius;
|
|
350
|
-
};
|
|
351
|
-
const handleClick = (e) => {
|
|
352
|
-
if (!isActive) return;
|
|
353
|
-
e.preventDefault();
|
|
354
|
-
e.stopPropagation();
|
|
355
|
-
e.stopImmediatePropagation();
|
|
356
|
-
isLocked = true;
|
|
357
|
-
const elementToInspect = currentElement;
|
|
358
|
-
if (elementToInspect) {
|
|
359
|
-
void getStack(elementToInspect).then((stack) => {
|
|
360
|
-
if (!stack) {
|
|
361
|
-
hideOverlay();
|
|
362
|
-
return;
|
|
363
|
-
}
|
|
364
|
-
const serializedStack = serializeStack(filterStack(stack));
|
|
365
|
-
const htmlSnippet = getHTMLSnippet(elementToInspect);
|
|
366
|
-
const payload = `## Referenced element
|
|
367
|
-
${htmlSnippet}
|
|
368
|
-
|
|
369
|
-
Import traces:
|
|
370
|
-
${serializedStack}
|
|
371
|
-
|
|
372
|
-
Page: ${window.location.href}`;
|
|
373
|
-
void copyToClipboard(payload);
|
|
374
|
-
});
|
|
375
|
-
}
|
|
376
|
-
};
|
|
377
|
-
const handleMouseDown = (e) => {
|
|
378
|
-
if (!isActive) return;
|
|
379
|
-
e.preventDefault();
|
|
380
|
-
e.stopPropagation();
|
|
381
|
-
e.stopImmediatePropagation();
|
|
382
|
-
};
|
|
383
|
-
const showOverlay = () => {
|
|
384
|
-
if (!overlay) {
|
|
385
|
-
overlay = createOverlay();
|
|
386
|
-
}
|
|
387
|
-
isActive = true;
|
|
388
|
-
overlay.style.display = "block";
|
|
389
|
-
currentX = targetX;
|
|
390
|
-
currentY = targetY;
|
|
391
|
-
currentWidth = targetWidth;
|
|
392
|
-
currentHeight = targetHeight;
|
|
393
|
-
updateOverlayPosition();
|
|
394
|
-
};
|
|
395
|
-
const hideOverlay = () => {
|
|
396
|
-
isActive = false;
|
|
397
|
-
isLocked = false;
|
|
398
|
-
if (overlay) {
|
|
399
|
-
overlay.style.display = "none";
|
|
400
|
-
}
|
|
401
|
-
if (animationFrame) {
|
|
402
|
-
cancelAnimationFrame(animationFrame);
|
|
403
|
-
animationFrame = null;
|
|
404
|
-
}
|
|
405
|
-
currentElement = null;
|
|
406
|
-
};
|
|
407
|
-
const handleKeyDown = (e) => {
|
|
408
|
-
if (e.metaKey && !metaKeyTimer && !isActive) {
|
|
409
|
-
metaKeyTimer = setTimeout(() => {
|
|
410
|
-
if (!isInsideInputOrTextarea()) {
|
|
411
|
-
showOverlay();
|
|
412
|
-
}
|
|
413
|
-
metaKeyTimer = null;
|
|
414
|
-
}, 750);
|
|
415
|
-
}
|
|
416
|
-
};
|
|
417
|
-
const handleKeyUp = (e) => {
|
|
418
|
-
if (!e.metaKey) {
|
|
419
|
-
if (metaKeyTimer) {
|
|
420
|
-
clearTimeout(metaKeyTimer);
|
|
421
|
-
metaKeyTimer = null;
|
|
422
|
-
}
|
|
423
|
-
if (isActive && !isLocked) {
|
|
424
|
-
hideOverlay();
|
|
425
|
-
}
|
|
426
|
-
}
|
|
427
|
-
};
|
|
428
|
-
document.addEventListener("keydown", handleKeyDown);
|
|
429
|
-
document.addEventListener("keyup", handleKeyUp);
|
|
430
|
-
document.addEventListener("mousemove", handleMouseMove);
|
|
431
|
-
document.addEventListener("mousedown", handleMouseDown, true);
|
|
432
|
-
document.addEventListener("click", handleClick, true);
|
|
433
|
-
window.addEventListener("focus", handleWindowFocus);
|
|
434
|
-
return () => {
|
|
435
|
-
if (metaKeyTimer) {
|
|
436
|
-
clearTimeout(metaKeyTimer);
|
|
437
|
-
}
|
|
438
|
-
if (animationFrame) {
|
|
439
|
-
cancelAnimationFrame(animationFrame);
|
|
440
|
-
}
|
|
441
|
-
if (overlay && overlay.parentNode) {
|
|
442
|
-
overlay.parentNode.removeChild(overlay);
|
|
443
|
-
}
|
|
444
|
-
document.removeEventListener("keydown", handleKeyDown);
|
|
445
|
-
document.removeEventListener("keyup", handleKeyUp);
|
|
446
|
-
document.removeEventListener("mousemove", handleMouseMove);
|
|
447
|
-
document.removeEventListener("mousedown", handleMouseDown, true);
|
|
448
|
-
document.removeEventListener("click", handleClick, true);
|
|
449
|
-
window.removeEventListener("focus", handleWindowFocus);
|
|
450
|
-
};
|
|
451
|
-
};
|
|
452
|
-
init();
|
|
453
|
-
|
|
454
|
-
exports.init = init;
|
|
256
|
+
exports.filterStack = filterStack;
|
|
257
|
+
exports.getHTMLSnippet = getHTMLSnippet;
|
|
258
|
+
exports.getStack = getStack;
|
|
259
|
+
exports.serializeStack = serializeStack;
|
package/dist/core.d.cts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
interface StackItem {
|
|
2
|
+
componentName: string;
|
|
3
|
+
fileName: string | undefined;
|
|
4
|
+
}
|
|
5
|
+
declare const getStack: (element: Element) => Promise<StackItem[] | null>;
|
|
6
|
+
declare const filterStack: (stack: StackItem[]) => StackItem[];
|
|
7
|
+
declare const serializeStack: (stack: StackItem[]) => string;
|
|
8
|
+
declare const getHTMLSnippet: (element: Element) => string;
|
|
9
|
+
|
|
10
|
+
export { type StackItem, filterStack, getHTMLSnippet, getStack, serializeStack };
|
package/dist/core.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
interface StackItem {
|
|
2
|
+
componentName: string;
|
|
3
|
+
fileName: string | undefined;
|
|
4
|
+
}
|
|
5
|
+
declare const getStack: (element: Element) => Promise<StackItem[] | null>;
|
|
6
|
+
declare const filterStack: (stack: StackItem[]) => StackItem[];
|
|
7
|
+
declare const serializeStack: (stack: StackItem[]) => string;
|
|
8
|
+
declare const getHTMLSnippet: (element: Element) => string;
|
|
9
|
+
|
|
10
|
+
export { type StackItem, filterStack, getHTMLSnippet, getStack, serializeStack };
|
|
@@ -14,7 +14,9 @@ var getStack = async (element) => {
|
|
|
14
14
|
const fiber = getFiberFromHostInstance(element);
|
|
15
15
|
if (!fiber) return null;
|
|
16
16
|
const stackTrace = getFiberStackTrace(fiber);
|
|
17
|
+
console.log(stackTrace);
|
|
17
18
|
const rawOwnerStack = await getOwnerStack(stackTrace);
|
|
19
|
+
console.log(rawOwnerStack);
|
|
18
20
|
const stack = rawOwnerStack.map((item) => ({
|
|
19
21
|
componentName: item.name,
|
|
20
22
|
fileName: item.source?.fileName
|
|
@@ -249,204 +251,4 @@ var getHTMLSnippet = (element) => {
|
|
|
249
251
|
return lines.join("\n");
|
|
250
252
|
};
|
|
251
253
|
|
|
252
|
-
|
|
253
|
-
var init = () => {
|
|
254
|
-
let metaKeyTimer = null;
|
|
255
|
-
let overlay = null;
|
|
256
|
-
let isActive = false;
|
|
257
|
-
let isLocked = false;
|
|
258
|
-
let currentElement = null;
|
|
259
|
-
let animationFrame = null;
|
|
260
|
-
let pendingCopyText = null;
|
|
261
|
-
const copyToClipboard = async (text) => {
|
|
262
|
-
if (!document.hasFocus()) {
|
|
263
|
-
pendingCopyText = text;
|
|
264
|
-
return;
|
|
265
|
-
}
|
|
266
|
-
try {
|
|
267
|
-
await navigator.clipboard.writeText(text);
|
|
268
|
-
pendingCopyText = null;
|
|
269
|
-
hideOverlay();
|
|
270
|
-
} catch {
|
|
271
|
-
const textarea = document.createElement("textarea");
|
|
272
|
-
textarea.value = text;
|
|
273
|
-
textarea.style.position = "fixed";
|
|
274
|
-
textarea.style.left = "-999999px";
|
|
275
|
-
textarea.style.top = "-999999px";
|
|
276
|
-
document.body.appendChild(textarea);
|
|
277
|
-
textarea.focus();
|
|
278
|
-
textarea.select();
|
|
279
|
-
try {
|
|
280
|
-
document.execCommand("copy");
|
|
281
|
-
pendingCopyText = null;
|
|
282
|
-
hideOverlay();
|
|
283
|
-
} catch (execErr) {
|
|
284
|
-
console.error("Failed to copy to clipboard:", execErr);
|
|
285
|
-
hideOverlay();
|
|
286
|
-
}
|
|
287
|
-
document.body.removeChild(textarea);
|
|
288
|
-
}
|
|
289
|
-
};
|
|
290
|
-
const handleWindowFocus = () => {
|
|
291
|
-
if (pendingCopyText) {
|
|
292
|
-
void copyToClipboard(pendingCopyText);
|
|
293
|
-
}
|
|
294
|
-
};
|
|
295
|
-
let currentX = 0;
|
|
296
|
-
let currentY = 0;
|
|
297
|
-
let currentWidth = 0;
|
|
298
|
-
let currentHeight = 0;
|
|
299
|
-
let targetX = 0;
|
|
300
|
-
let targetY = 0;
|
|
301
|
-
let targetWidth = 0;
|
|
302
|
-
let targetHeight = 0;
|
|
303
|
-
let targetBorderRadius = "";
|
|
304
|
-
const isInsideInputOrTextarea = () => {
|
|
305
|
-
const activeElement = document.activeElement;
|
|
306
|
-
return activeElement instanceof HTMLInputElement || activeElement instanceof HTMLTextAreaElement || activeElement?.tagName === "INPUT" || activeElement?.tagName === "TEXTAREA";
|
|
307
|
-
};
|
|
308
|
-
const createOverlay = () => {
|
|
309
|
-
const div = document.createElement("div");
|
|
310
|
-
div.style.position = "fixed";
|
|
311
|
-
div.style.border = "2px solid #3b82f6";
|
|
312
|
-
div.style.backgroundColor = "rgba(59, 130, 246, 0.1)";
|
|
313
|
-
div.style.pointerEvents = "none";
|
|
314
|
-
div.style.zIndex = "999999";
|
|
315
|
-
div.style.transition = "none";
|
|
316
|
-
document.body.appendChild(div);
|
|
317
|
-
return div;
|
|
318
|
-
};
|
|
319
|
-
const lerp = (start, end, factor) => {
|
|
320
|
-
return start + (end - start) * factor;
|
|
321
|
-
};
|
|
322
|
-
const updateOverlayPosition = () => {
|
|
323
|
-
if (!overlay || !isActive) return;
|
|
324
|
-
const factor = 0.5;
|
|
325
|
-
currentX = lerp(currentX, targetX, factor);
|
|
326
|
-
currentY = lerp(currentY, targetY, factor);
|
|
327
|
-
currentWidth = lerp(currentWidth, targetWidth, factor);
|
|
328
|
-
currentHeight = lerp(currentHeight, targetHeight, factor);
|
|
329
|
-
overlay.style.left = `${currentX}px`;
|
|
330
|
-
overlay.style.top = `${currentY}px`;
|
|
331
|
-
overlay.style.width = `${currentWidth}px`;
|
|
332
|
-
overlay.style.height = `${currentHeight}px`;
|
|
333
|
-
overlay.style.borderRadius = targetBorderRadius;
|
|
334
|
-
animationFrame = requestAnimationFrame(updateOverlayPosition);
|
|
335
|
-
};
|
|
336
|
-
const handleMouseMove = (e) => {
|
|
337
|
-
if (isLocked) return;
|
|
338
|
-
const element = document.elementFromPoint(e.clientX, e.clientY);
|
|
339
|
-
if (!element || element === overlay) return;
|
|
340
|
-
currentElement = element;
|
|
341
|
-
const rect = element.getBoundingClientRect();
|
|
342
|
-
const computedStyle = window.getComputedStyle(element);
|
|
343
|
-
targetX = rect.left;
|
|
344
|
-
targetY = rect.top;
|
|
345
|
-
targetWidth = rect.width;
|
|
346
|
-
targetHeight = rect.height;
|
|
347
|
-
targetBorderRadius = computedStyle.borderRadius;
|
|
348
|
-
};
|
|
349
|
-
const handleClick = (e) => {
|
|
350
|
-
if (!isActive) return;
|
|
351
|
-
e.preventDefault();
|
|
352
|
-
e.stopPropagation();
|
|
353
|
-
e.stopImmediatePropagation();
|
|
354
|
-
isLocked = true;
|
|
355
|
-
const elementToInspect = currentElement;
|
|
356
|
-
if (elementToInspect) {
|
|
357
|
-
void getStack(elementToInspect).then((stack) => {
|
|
358
|
-
if (!stack) {
|
|
359
|
-
hideOverlay();
|
|
360
|
-
return;
|
|
361
|
-
}
|
|
362
|
-
const serializedStack = serializeStack(filterStack(stack));
|
|
363
|
-
const htmlSnippet = getHTMLSnippet(elementToInspect);
|
|
364
|
-
const payload = `## Referenced element
|
|
365
|
-
${htmlSnippet}
|
|
366
|
-
|
|
367
|
-
Import traces:
|
|
368
|
-
${serializedStack}
|
|
369
|
-
|
|
370
|
-
Page: ${window.location.href}`;
|
|
371
|
-
void copyToClipboard(payload);
|
|
372
|
-
});
|
|
373
|
-
}
|
|
374
|
-
};
|
|
375
|
-
const handleMouseDown = (e) => {
|
|
376
|
-
if (!isActive) return;
|
|
377
|
-
e.preventDefault();
|
|
378
|
-
e.stopPropagation();
|
|
379
|
-
e.stopImmediatePropagation();
|
|
380
|
-
};
|
|
381
|
-
const showOverlay = () => {
|
|
382
|
-
if (!overlay) {
|
|
383
|
-
overlay = createOverlay();
|
|
384
|
-
}
|
|
385
|
-
isActive = true;
|
|
386
|
-
overlay.style.display = "block";
|
|
387
|
-
currentX = targetX;
|
|
388
|
-
currentY = targetY;
|
|
389
|
-
currentWidth = targetWidth;
|
|
390
|
-
currentHeight = targetHeight;
|
|
391
|
-
updateOverlayPosition();
|
|
392
|
-
};
|
|
393
|
-
const hideOverlay = () => {
|
|
394
|
-
isActive = false;
|
|
395
|
-
isLocked = false;
|
|
396
|
-
if (overlay) {
|
|
397
|
-
overlay.style.display = "none";
|
|
398
|
-
}
|
|
399
|
-
if (animationFrame) {
|
|
400
|
-
cancelAnimationFrame(animationFrame);
|
|
401
|
-
animationFrame = null;
|
|
402
|
-
}
|
|
403
|
-
currentElement = null;
|
|
404
|
-
};
|
|
405
|
-
const handleKeyDown = (e) => {
|
|
406
|
-
if (e.metaKey && !metaKeyTimer && !isActive) {
|
|
407
|
-
metaKeyTimer = setTimeout(() => {
|
|
408
|
-
if (!isInsideInputOrTextarea()) {
|
|
409
|
-
showOverlay();
|
|
410
|
-
}
|
|
411
|
-
metaKeyTimer = null;
|
|
412
|
-
}, 750);
|
|
413
|
-
}
|
|
414
|
-
};
|
|
415
|
-
const handleKeyUp = (e) => {
|
|
416
|
-
if (!e.metaKey) {
|
|
417
|
-
if (metaKeyTimer) {
|
|
418
|
-
clearTimeout(metaKeyTimer);
|
|
419
|
-
metaKeyTimer = null;
|
|
420
|
-
}
|
|
421
|
-
if (isActive && !isLocked) {
|
|
422
|
-
hideOverlay();
|
|
423
|
-
}
|
|
424
|
-
}
|
|
425
|
-
};
|
|
426
|
-
document.addEventListener("keydown", handleKeyDown);
|
|
427
|
-
document.addEventListener("keyup", handleKeyUp);
|
|
428
|
-
document.addEventListener("mousemove", handleMouseMove);
|
|
429
|
-
document.addEventListener("mousedown", handleMouseDown, true);
|
|
430
|
-
document.addEventListener("click", handleClick, true);
|
|
431
|
-
window.addEventListener("focus", handleWindowFocus);
|
|
432
|
-
return () => {
|
|
433
|
-
if (metaKeyTimer) {
|
|
434
|
-
clearTimeout(metaKeyTimer);
|
|
435
|
-
}
|
|
436
|
-
if (animationFrame) {
|
|
437
|
-
cancelAnimationFrame(animationFrame);
|
|
438
|
-
}
|
|
439
|
-
if (overlay && overlay.parentNode) {
|
|
440
|
-
overlay.parentNode.removeChild(overlay);
|
|
441
|
-
}
|
|
442
|
-
document.removeEventListener("keydown", handleKeyDown);
|
|
443
|
-
document.removeEventListener("keyup", handleKeyUp);
|
|
444
|
-
document.removeEventListener("mousemove", handleMouseMove);
|
|
445
|
-
document.removeEventListener("mousedown", handleMouseDown, true);
|
|
446
|
-
document.removeEventListener("click", handleClick, true);
|
|
447
|
-
window.removeEventListener("focus", handleWindowFocus);
|
|
448
|
-
};
|
|
449
|
-
};
|
|
450
|
-
init();
|
|
451
|
-
|
|
452
|
-
export { init };
|
|
254
|
+
export { filterStack, getHTMLSnippet, getStack, serializeStack };
|