react-grab 0.0.12 → 0.0.13

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.d.ts CHANGED
@@ -14,7 +14,7 @@ interface Options {
14
14
  /**
15
15
  * hotkey to trigger the overlay
16
16
  *
17
- * default: "Meta"
17
+ * default: ["Meta", "C"]
18
18
  */
19
19
  hotkey?: Hotkey | Hotkey[];
20
20
  /**
@@ -147,7 +147,9 @@ var ReactGrab = (function (exports) {
147
147
  };
148
148
  const checkAllKeysPressed = (pressedKeys) => {
149
149
  if (Array.isArray(key)) {
150
- return key.every((keyFromCombo) => checkSingleKeyPressed(keyFromCombo, pressedKeys));
150
+ return key.every(
151
+ (keyFromCombo) => checkSingleKeyPressed(keyFromCombo, pressedKeys)
152
+ );
151
153
  }
152
154
  return checkSingleKeyPressed(key, pressedKeys);
153
155
  };
@@ -214,236 +216,6 @@ var ReactGrab = (function (exports) {
214
216
  return cleanup;
215
217
  };
216
218
 
217
- // src/overlay.ts
218
- var VIEWPORT_MARGIN_PX = 8;
219
- var LABEL_OFFSET_PX = 6;
220
- var lerp = (start, end, factor) => {
221
- return start + (end - start) * factor;
222
- };
223
- var SELECTION_LERP_FACTOR = 0.95;
224
- var createSelectionElement = ({
225
- borderRadius,
226
- height,
227
- transform,
228
- width,
229
- x,
230
- y
231
- }) => {
232
- const overlay = document.createElement("div");
233
- overlay.style.position = "fixed";
234
- overlay.style.top = `${y}px`;
235
- overlay.style.left = `${x}px`;
236
- overlay.style.width = `${width}px`;
237
- overlay.style.height = `${height}px`;
238
- overlay.style.borderRadius = borderRadius;
239
- overlay.style.transform = transform;
240
- overlay.style.pointerEvents = "none";
241
- overlay.style.border = "1px solid rgb(210, 57, 192)";
242
- overlay.style.backgroundColor = "rgba(210, 57, 192, 0.2)";
243
- overlay.style.zIndex = "2147483646";
244
- overlay.style.boxSizing = "border-box";
245
- overlay.style.display = "none";
246
- return overlay;
247
- };
248
- var updateSelectionElement = (element, { borderRadius, height, transform, width, x, y }) => {
249
- const currentTop = parseFloat(element.style.top) || 0;
250
- const currentLeft = parseFloat(element.style.left) || 0;
251
- const currentWidth = parseFloat(element.style.width) || 0;
252
- const currentHeight = parseFloat(element.style.height) || 0;
253
- const topValue = `${lerp(currentTop, y, SELECTION_LERP_FACTOR)}px`;
254
- const leftValue = `${lerp(currentLeft, x, SELECTION_LERP_FACTOR)}px`;
255
- const widthValue = `${lerp(currentWidth, width, SELECTION_LERP_FACTOR)}px`;
256
- const heightValue = `${lerp(currentHeight, height, SELECTION_LERP_FACTOR)}px`;
257
- if (element.style.top !== topValue) {
258
- element.style.top = topValue;
259
- }
260
- if (element.style.left !== leftValue) {
261
- element.style.left = leftValue;
262
- }
263
- if (element.style.width !== widthValue) {
264
- element.style.width = widthValue;
265
- }
266
- if (element.style.height !== heightValue) {
267
- element.style.height = heightValue;
268
- }
269
- if (element.style.borderRadius !== borderRadius) {
270
- element.style.borderRadius = borderRadius;
271
- }
272
- if (element.style.transform !== transform) {
273
- element.style.transform = transform;
274
- }
275
- };
276
- var createSelectionOverlay = (root) => {
277
- const element = createSelectionElement({
278
- borderRadius: "0px",
279
- height: 0,
280
- transform: "none",
281
- width: 0,
282
- x: -1e3,
283
- y: -1e3
284
- });
285
- root.appendChild(element);
286
- let visible = false;
287
- return {
288
- hide: () => {
289
- visible = false;
290
- element.style.display = "none";
291
- element.style.pointerEvents = "none";
292
- },
293
- isVisible: () => visible,
294
- show: () => {
295
- visible = true;
296
- element.style.display = "block";
297
- element.style.pointerEvents = "auto";
298
- },
299
- update: (selection) => {
300
- updateSelectionElement(element, selection);
301
- }
302
- };
303
- };
304
- var createSpinner = () => {
305
- const spinner = document.createElement("span");
306
- spinner.style.display = "inline-block";
307
- spinner.style.width = "8px";
308
- spinner.style.height = "8px";
309
- spinner.style.border = "1.5px solid rgb(210, 57, 192)";
310
- spinner.style.borderTopColor = "transparent";
311
- spinner.style.borderRadius = "50%";
312
- spinner.style.marginRight = "4px";
313
- spinner.style.verticalAlign = "middle";
314
- spinner.animate(
315
- [{ transform: "rotate(0deg)" }, { transform: "rotate(360deg)" }],
316
- {
317
- duration: 600,
318
- easing: "linear",
319
- iterations: Infinity
320
- }
321
- );
322
- return spinner;
323
- };
324
- var activeIndicator = null;
325
- var createIndicator = () => {
326
- const indicator = document.createElement("div");
327
- indicator.style.position = "fixed";
328
- indicator.style.top = "calc(8px + env(safe-area-inset-top))";
329
- indicator.style.padding = "2px 6px";
330
- indicator.style.backgroundColor = "#fde7f7";
331
- indicator.style.color = "#b21c8e";
332
- indicator.style.border = "1px solid #f7c5ec";
333
- indicator.style.borderRadius = "4px";
334
- indicator.style.fontSize = "11px";
335
- indicator.style.fontWeight = "500";
336
- indicator.style.fontFamily = "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif";
337
- indicator.style.zIndex = "2147483647";
338
- indicator.style.pointerEvents = "none";
339
- indicator.style.opacity = "0";
340
- indicator.style.transition = "opacity 0.2s ease-in-out";
341
- indicator.style.display = "flex";
342
- indicator.style.alignItems = "center";
343
- indicator.style.maxWidth = "calc(100vw - (16px + env(safe-area-inset-left) + env(safe-area-inset-right)))";
344
- indicator.style.overflow = "hidden";
345
- indicator.style.textOverflow = "ellipsis";
346
- indicator.style.whiteSpace = "nowrap";
347
- return indicator;
348
- };
349
- var showCopyIndicator = (selectionLeftPx, selectionTopPx) => {
350
- if (activeIndicator) {
351
- activeIndicator.remove();
352
- activeIndicator = null;
353
- }
354
- const indicator = createIndicator();
355
- const loadingSpinner = createSpinner();
356
- const labelText = document.createElement("span");
357
- labelText.textContent = "Grabbing\u2026";
358
- indicator.appendChild(loadingSpinner);
359
- indicator.appendChild(labelText);
360
- document.body.appendChild(indicator);
361
- activeIndicator = indicator;
362
- const indicatorRect = indicator.getBoundingClientRect();
363
- const viewportWidthPx = window.innerWidth;
364
- const viewportHeightPx = window.innerHeight;
365
- let indicatorLeftPx = Math.round(selectionLeftPx);
366
- let indicatorTopPx = Math.round(selectionTopPx) - indicatorRect.height - LABEL_OFFSET_PX;
367
- indicatorLeftPx = Math.max(
368
- VIEWPORT_MARGIN_PX,
369
- Math.min(
370
- indicatorLeftPx,
371
- viewportWidthPx - indicatorRect.width - VIEWPORT_MARGIN_PX
372
- )
373
- );
374
- indicatorTopPx = Math.max(
375
- VIEWPORT_MARGIN_PX,
376
- Math.min(
377
- indicatorTopPx,
378
- viewportHeightPx - indicatorRect.height - VIEWPORT_MARGIN_PX
379
- )
380
- );
381
- indicator.style.left = `${indicatorLeftPx}px`;
382
- indicator.style.top = `${indicatorTopPx}px`;
383
- indicator.style.right = "auto";
384
- requestAnimationFrame(() => {
385
- indicator.style.opacity = "1";
386
- });
387
- return (tagName) => {
388
- loadingSpinner.remove();
389
- const checkmarkIcon = document.createElement("span");
390
- checkmarkIcon.textContent = "\u2713";
391
- checkmarkIcon.style.display = "inline-block";
392
- checkmarkIcon.style.marginRight = "4px";
393
- checkmarkIcon.style.fontWeight = "600";
394
- indicator.insertBefore(checkmarkIcon, labelText);
395
- const tagNameMonospace = document.createElement("span");
396
- tagNameMonospace.textContent = tagName ? `<${tagName}>` : "<element>";
397
- tagNameMonospace.style.fontFamily = "ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace";
398
- tagNameMonospace.style.fontVariantNumeric = "tabular-nums";
399
- labelText.replaceChildren(
400
- document.createTextNode("Grabbed "),
401
- tagNameMonospace
402
- );
403
- setTimeout(() => {
404
- indicator.style.opacity = "0";
405
- setTimeout(() => {
406
- indicator.remove();
407
- if (activeIndicator === indicator) {
408
- activeIndicator = null;
409
- }
410
- }, 200);
411
- }, 1500);
412
- };
413
- };
414
-
415
- // src/utils/copy-text.ts
416
- var IS_NAVIGATOR_CLIPBOARD_AVAILABLE = typeof window !== "undefined" && window.navigator.clipboard && window.isSecureContext;
417
- var copyTextToClipboard = async (text) => {
418
- if (IS_NAVIGATOR_CLIPBOARD_AVAILABLE) {
419
- try {
420
- await navigator.clipboard.writeText(text);
421
- return true;
422
- } catch {
423
- }
424
- }
425
- const textareaElement = document.createElement("textarea");
426
- textareaElement.value = text;
427
- textareaElement.setAttribute("readonly", "");
428
- textareaElement.style.position = "fixed";
429
- textareaElement.style.top = "-9999px";
430
- textareaElement.style.opacity = "0";
431
- textareaElement.style.pointerEvents = "none";
432
- const doc = document.body || document.documentElement;
433
- doc.appendChild(textareaElement);
434
- textareaElement.select();
435
- textareaElement.setSelectionRange(0, textareaElement.value.length);
436
- let didCopyToClipboard = false;
437
- try {
438
- didCopyToClipboard = document.execCommand("copy");
439
- } catch {
440
- didCopyToClipboard = false;
441
- } finally {
442
- doc.removeChild(textareaElement);
443
- }
444
- return didCopyToClipboard;
445
- };
446
-
447
219
  // node_modules/.pnpm/bippy@0.3.31_@types+react@19.2.2_react@19.2.0/node_modules/bippy/dist/src-R2iEnVC1.js
448
220
  var version = "0.3.31";
449
221
  var BIPPY_INSTRUMENTATION_STRING = `bippy-${version}`;
@@ -542,6 +314,7 @@ var ReactGrab = (function (exports) {
542
314
  return rdtHook;
543
315
  };
544
316
  var patchRDTHook = (onActive) => {
317
+ if (onActive) onActiveListeners.add(onActive);
545
318
  try {
546
319
  const rdtHook = globalThis.__REACT_DEVTOOLS_GLOBAL_HOOK__;
547
320
  if (!rdtHook) return;
@@ -622,6 +395,28 @@ var ReactGrab = (function (exports) {
622
395
  if (!unwrappedType) return null;
623
396
  return unwrappedType.displayName || unwrappedType.name || null;
624
397
  };
398
+ var instrument = (options) => {
399
+ return getRDTHook(() => {
400
+ const rdtHook = getRDTHook();
401
+ options.onActive?.();
402
+ rdtHook._instrumentationSource = options.name ?? BIPPY_INSTRUMENTATION_STRING;
403
+ const prevOnCommitFiberRoot = rdtHook.onCommitFiberRoot;
404
+ if (options.onCommitFiberRoot) rdtHook.onCommitFiberRoot = (rendererID, root, priority) => {
405
+ if (prevOnCommitFiberRoot) prevOnCommitFiberRoot(rendererID, root, priority);
406
+ options.onCommitFiberRoot?.(rendererID, root, priority);
407
+ };
408
+ const prevOnCommitFiberUnmount = rdtHook.onCommitFiberUnmount;
409
+ if (options.onCommitFiberUnmount) rdtHook.onCommitFiberUnmount = (rendererID, root) => {
410
+ if (prevOnCommitFiberUnmount) prevOnCommitFiberUnmount(rendererID, root);
411
+ options.onCommitFiberUnmount?.(rendererID, root);
412
+ };
413
+ const prevOnPostCommitFiberRoot = rdtHook.onPostCommitFiberRoot;
414
+ if (options.onPostCommitFiberRoot) rdtHook.onPostCommitFiberRoot = (rendererID, root) => {
415
+ if (prevOnPostCommitFiberRoot) prevOnPostCommitFiberRoot(rendererID, root);
416
+ options.onPostCommitFiberRoot?.(rendererID, root);
417
+ };
418
+ });
419
+ };
625
420
  var getFiberFromHostInstance = (hostInstance) => {
626
421
  const rdtHook = getRDTHook();
627
422
  for (const renderer of rdtHook.renderers.values()) try {
@@ -635,6 +430,7 @@ var ReactGrab = (function (exports) {
635
430
  }
636
431
  return null;
637
432
  };
433
+ var _fiberRoots = /* @__PURE__ */ new Set();
638
434
  safelyInstallRDTHook();
639
435
 
640
436
  // node_modules/.pnpm/bippy@0.3.31_@types+react@19.2.2_react@19.2.0/node_modules/bippy/dist/source-DWOhEbf2.js
@@ -2417,14 +2213,18 @@ ${error.stack}`;
2417
2213
  return matches;
2418
2214
  };
2419
2215
 
2420
- // src/utils/data.ts
2216
+ // src/instrumentation.ts
2217
+ var fiberRoots = _fiberRoots;
2218
+ instrument({
2219
+ onCommitFiberRoot(_, fiberRoot) {
2220
+ fiberRoots.add(fiberRoot);
2221
+ }
2222
+ });
2421
2223
  var getStack = async (element) => {
2422
2224
  const fiber = getFiberFromHostInstance(element);
2423
2225
  if (!fiber) return null;
2424
2226
  const stackTrace = getFiberStackTrace(fiber);
2425
- console.log(stackTrace);
2426
2227
  const rawOwnerStack = await getOwnerStack(stackTrace);
2427
- console.log(rawOwnerStack);
2428
2228
  const stack = rawOwnerStack.map((item) => ({
2429
2229
  componentName: item.name,
2430
2230
  fileName: item.source?.fileName
@@ -2612,7 +2412,9 @@ ${error.stack}`;
2612
2412
  lines.push(`${indent2} </${prevSibling.tagName.toLowerCase()}>`);
2613
2413
  } else if (targetIndex > 0) {
2614
2414
  const indent2 = " ".repeat(ancestors.length);
2615
- lines.push(`${indent2} ... (${targetIndex} element${targetIndex === 1 ? "" : "s"})`);
2415
+ lines.push(
2416
+ `${indent2} ... (${targetIndex} element${targetIndex === 1 ? "" : "s"})`
2417
+ );
2616
2418
  }
2617
2419
  }
2618
2420
  }
@@ -2622,7 +2424,9 @@ ${error.stack}`;
2622
2424
  const childrenCount = element.children.length;
2623
2425
  if (textContent && childrenCount === 0 && textContent.length < 40) {
2624
2426
  lines.push(
2625
- `${indent} ${getElementTag(element)}${textContent}${getClosingTag(element)}`
2427
+ `${indent} ${getElementTag(element)}${textContent}${getClosingTag(
2428
+ element
2429
+ )}`
2626
2430
  );
2627
2431
  } else {
2628
2432
  lines.push(indent + " " + getElementTag(element));
@@ -2659,6 +2463,284 @@ ${error.stack}`;
2659
2463
  return lines.join("\n");
2660
2464
  };
2661
2465
 
2466
+ // src/overlay.ts
2467
+ var VIEWPORT_MARGIN_PX = 8;
2468
+ var LABEL_OFFSET_PX = 6;
2469
+ var INDICATOR_CLAMP_PADDING_PX = 4;
2470
+ var INDICATOR_SUCCESS_VISIBLE_MS = 1500;
2471
+ var INDICATOR_FADE_MS = 200;
2472
+ var INDICATOR_TOTAL_HIDE_DELAY_MS = INDICATOR_SUCCESS_VISIBLE_MS + INDICATOR_FADE_MS;
2473
+ var lerp = (start, end, factor) => {
2474
+ return start + (end - start) * factor;
2475
+ };
2476
+ var SELECTION_LERP_FACTOR = 0.95;
2477
+ var createSelectionElement = ({
2478
+ borderRadius,
2479
+ height,
2480
+ transform,
2481
+ width,
2482
+ x,
2483
+ y
2484
+ }) => {
2485
+ const overlay = document.createElement("div");
2486
+ overlay.style.position = "fixed";
2487
+ overlay.style.top = `${y}px`;
2488
+ overlay.style.left = `${x}px`;
2489
+ overlay.style.width = `${width}px`;
2490
+ overlay.style.height = `${height}px`;
2491
+ overlay.style.borderRadius = borderRadius;
2492
+ overlay.style.transform = transform;
2493
+ overlay.style.pointerEvents = "none";
2494
+ overlay.style.border = "1px solid rgb(210, 57, 192)";
2495
+ overlay.style.backgroundColor = "rgba(210, 57, 192, 0.2)";
2496
+ overlay.style.zIndex = "2147483646";
2497
+ overlay.style.boxSizing = "border-box";
2498
+ overlay.style.display = "none";
2499
+ return overlay;
2500
+ };
2501
+ var updateSelectionElement = (element, { borderRadius, height, transform, width, x, y }) => {
2502
+ const currentTop = parseFloat(element.style.top) || 0;
2503
+ const currentLeft = parseFloat(element.style.left) || 0;
2504
+ const currentWidth = parseFloat(element.style.width) || 0;
2505
+ const currentHeight = parseFloat(element.style.height) || 0;
2506
+ const topValue = `${lerp(currentTop, y, SELECTION_LERP_FACTOR)}px`;
2507
+ const leftValue = `${lerp(currentLeft, x, SELECTION_LERP_FACTOR)}px`;
2508
+ const widthValue = `${lerp(currentWidth, width, SELECTION_LERP_FACTOR)}px`;
2509
+ const heightValue = `${lerp(currentHeight, height, SELECTION_LERP_FACTOR)}px`;
2510
+ if (element.style.top !== topValue) {
2511
+ element.style.top = topValue;
2512
+ }
2513
+ if (element.style.left !== leftValue) {
2514
+ element.style.left = leftValue;
2515
+ }
2516
+ if (element.style.width !== widthValue) {
2517
+ element.style.width = widthValue;
2518
+ }
2519
+ if (element.style.height !== heightValue) {
2520
+ element.style.height = heightValue;
2521
+ }
2522
+ if (element.style.borderRadius !== borderRadius) {
2523
+ element.style.borderRadius = borderRadius;
2524
+ }
2525
+ if (element.style.transform !== transform) {
2526
+ element.style.transform = transform;
2527
+ }
2528
+ };
2529
+ var createSelectionOverlay = (root) => {
2530
+ const element = createSelectionElement({
2531
+ borderRadius: "0px",
2532
+ height: 0,
2533
+ transform: "none",
2534
+ width: 0,
2535
+ x: -1e3,
2536
+ y: -1e3
2537
+ });
2538
+ root.appendChild(element);
2539
+ let visible = false;
2540
+ return {
2541
+ hide: () => {
2542
+ visible = false;
2543
+ element.style.display = "none";
2544
+ element.style.pointerEvents = "none";
2545
+ },
2546
+ isVisible: () => visible,
2547
+ show: () => {
2548
+ visible = true;
2549
+ element.style.display = "block";
2550
+ element.style.pointerEvents = "auto";
2551
+ },
2552
+ update: (selection) => {
2553
+ updateSelectionElement(element, selection);
2554
+ }
2555
+ };
2556
+ };
2557
+ var createSpinner = () => {
2558
+ const spinner = document.createElement("span");
2559
+ spinner.style.display = "inline-block";
2560
+ spinner.style.width = "8px";
2561
+ spinner.style.height = "8px";
2562
+ spinner.style.border = "1.5px solid rgb(210, 57, 192)";
2563
+ spinner.style.borderTopColor = "transparent";
2564
+ spinner.style.borderRadius = "50%";
2565
+ spinner.style.marginRight = "4px";
2566
+ spinner.style.verticalAlign = "middle";
2567
+ spinner.animate(
2568
+ [{ transform: "rotate(0deg)" }, { transform: "rotate(360deg)" }],
2569
+ {
2570
+ duration: 600,
2571
+ easing: "linear",
2572
+ iterations: Infinity
2573
+ }
2574
+ );
2575
+ return spinner;
2576
+ };
2577
+ var activeIndicator = null;
2578
+ var createIndicator = () => {
2579
+ const indicator = document.createElement("div");
2580
+ indicator.style.position = "fixed";
2581
+ indicator.style.top = "calc(8px + env(safe-area-inset-top))";
2582
+ indicator.style.padding = "2px 6px";
2583
+ indicator.style.backgroundColor = "#fde7f7";
2584
+ indicator.style.color = "#b21c8e";
2585
+ indicator.style.border = "1px solid #f7c5ec";
2586
+ indicator.style.borderRadius = "4px";
2587
+ indicator.style.fontSize = "11px";
2588
+ indicator.style.fontWeight = "500";
2589
+ indicator.style.fontFamily = "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif";
2590
+ indicator.style.zIndex = "2147483647";
2591
+ indicator.style.pointerEvents = "none";
2592
+ indicator.style.opacity = "0";
2593
+ indicator.style.transition = "opacity 0.2s ease-in-out";
2594
+ indicator.style.display = "flex";
2595
+ indicator.style.alignItems = "center";
2596
+ indicator.style.maxWidth = "calc(100vw - (16px + env(safe-area-inset-left) + env(safe-area-inset-right)))";
2597
+ indicator.style.overflow = "hidden";
2598
+ indicator.style.textOverflow = "ellipsis";
2599
+ indicator.style.whiteSpace = "nowrap";
2600
+ return indicator;
2601
+ };
2602
+ var showLabel = (selectionLeftPx, selectionTopPx, tagName) => {
2603
+ let indicator = activeIndicator;
2604
+ let isNewIndicator = false;
2605
+ if (!indicator) {
2606
+ indicator = createIndicator();
2607
+ document.body.appendChild(indicator);
2608
+ activeIndicator = indicator;
2609
+ isNewIndicator = true;
2610
+ isProcessing = false;
2611
+ }
2612
+ if (!isProcessing) {
2613
+ const labelText = indicator.querySelector("span");
2614
+ if (labelText) {
2615
+ const tagNameMonospace = document.createElement("span");
2616
+ tagNameMonospace.textContent = tagName ? `<${tagName}>` : "<element>";
2617
+ tagNameMonospace.style.fontFamily = "ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace";
2618
+ tagNameMonospace.style.fontVariantNumeric = "tabular-nums";
2619
+ labelText.replaceChildren(tagNameMonospace);
2620
+ } else {
2621
+ const newLabelText = document.createElement("span");
2622
+ const tagNameMonospace = document.createElement("span");
2623
+ tagNameMonospace.textContent = tagName ? `<${tagName}>` : "<element>";
2624
+ tagNameMonospace.style.fontFamily = "ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace";
2625
+ tagNameMonospace.style.fontVariantNumeric = "tabular-nums";
2626
+ newLabelText.appendChild(tagNameMonospace);
2627
+ indicator.appendChild(newLabelText);
2628
+ }
2629
+ }
2630
+ const indicatorRect = indicator.getBoundingClientRect();
2631
+ const viewportWidthPx = window.innerWidth;
2632
+ const viewportHeightPx = window.innerHeight;
2633
+ let indicatorLeftPx = Math.round(selectionLeftPx);
2634
+ let indicatorTopPx = Math.round(selectionTopPx) - indicatorRect.height - LABEL_OFFSET_PX;
2635
+ const CLAMPED_PADDING = INDICATOR_CLAMP_PADDING_PX;
2636
+ const minLeft = VIEWPORT_MARGIN_PX;
2637
+ const minTop = VIEWPORT_MARGIN_PX;
2638
+ const maxLeft = viewportWidthPx - indicatorRect.width - VIEWPORT_MARGIN_PX;
2639
+ const maxTop = viewportHeightPx - indicatorRect.height - VIEWPORT_MARGIN_PX;
2640
+ const willClampLeft = indicatorLeftPx < minLeft;
2641
+ const willClampTop = indicatorTopPx < minTop;
2642
+ const isClamped = willClampLeft || willClampTop;
2643
+ indicatorLeftPx = Math.max(minLeft, Math.min(indicatorLeftPx, maxLeft));
2644
+ indicatorTopPx = Math.max(minTop, Math.min(indicatorTopPx, maxTop));
2645
+ if (isClamped) {
2646
+ indicatorLeftPx += CLAMPED_PADDING;
2647
+ indicatorTopPx += CLAMPED_PADDING;
2648
+ }
2649
+ indicator.style.left = `${indicatorLeftPx}px`;
2650
+ indicator.style.top = `${indicatorTopPx}px`;
2651
+ indicator.style.right = "auto";
2652
+ if (isNewIndicator) {
2653
+ requestAnimationFrame(() => {
2654
+ indicator.style.opacity = "1";
2655
+ });
2656
+ } else if (indicator.style.opacity !== "1") {
2657
+ indicator.style.opacity = "1";
2658
+ }
2659
+ };
2660
+ var isProcessing = false;
2661
+ var updateLabelToProcessing = () => {
2662
+ if (!activeIndicator || isProcessing) return () => {
2663
+ };
2664
+ isProcessing = true;
2665
+ const indicator = activeIndicator;
2666
+ indicator.innerHTML = "";
2667
+ const loadingSpinner = createSpinner();
2668
+ const labelText = document.createElement("span");
2669
+ labelText.textContent = "Grabbing\u2026";
2670
+ indicator.appendChild(loadingSpinner);
2671
+ indicator.appendChild(labelText);
2672
+ return (tagName) => {
2673
+ if (!activeIndicator) {
2674
+ isProcessing = false;
2675
+ return;
2676
+ }
2677
+ indicator.textContent = "";
2678
+ const checkmarkIcon = document.createElement("span");
2679
+ checkmarkIcon.textContent = "\u2713";
2680
+ checkmarkIcon.style.display = "inline-block";
2681
+ checkmarkIcon.style.marginRight = "4px";
2682
+ checkmarkIcon.style.fontWeight = "600";
2683
+ const newLabelText = document.createElement("span");
2684
+ const tagNameMonospace = document.createElement("span");
2685
+ tagNameMonospace.textContent = tagName ? `<${tagName}>` : "<element>";
2686
+ tagNameMonospace.style.fontFamily = "ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace";
2687
+ tagNameMonospace.style.fontVariantNumeric = "tabular-nums";
2688
+ newLabelText.appendChild(document.createTextNode("Grabbed "));
2689
+ newLabelText.appendChild(tagNameMonospace);
2690
+ indicator.appendChild(checkmarkIcon);
2691
+ indicator.appendChild(newLabelText);
2692
+ setTimeout(() => {
2693
+ indicator.style.opacity = "0";
2694
+ setTimeout(() => {
2695
+ indicator.remove();
2696
+ if (activeIndicator === indicator) {
2697
+ activeIndicator = null;
2698
+ }
2699
+ isProcessing = false;
2700
+ }, INDICATOR_FADE_MS);
2701
+ }, INDICATOR_SUCCESS_VISIBLE_MS);
2702
+ };
2703
+ };
2704
+ var hideLabel = () => {
2705
+ if (activeIndicator) {
2706
+ activeIndicator.remove();
2707
+ activeIndicator = null;
2708
+ }
2709
+ isProcessing = false;
2710
+ };
2711
+
2712
+ // src/utils/copy-text.ts
2713
+ var IS_NAVIGATOR_CLIPBOARD_AVAILABLE = typeof window !== "undefined" && window.navigator.clipboard && window.isSecureContext;
2714
+ var copyTextToClipboard = async (text) => {
2715
+ if (IS_NAVIGATOR_CLIPBOARD_AVAILABLE) {
2716
+ try {
2717
+ await navigator.clipboard.writeText(text);
2718
+ return true;
2719
+ } catch {
2720
+ }
2721
+ }
2722
+ const textareaElement = document.createElement("textarea");
2723
+ textareaElement.value = text;
2724
+ textareaElement.setAttribute("readonly", "");
2725
+ textareaElement.style.position = "fixed";
2726
+ textareaElement.style.top = "-9999px";
2727
+ textareaElement.style.opacity = "0";
2728
+ textareaElement.style.pointerEvents = "none";
2729
+ const doc = document.body || document.documentElement;
2730
+ doc.appendChild(textareaElement);
2731
+ textareaElement.select();
2732
+ textareaElement.setSelectionRange(0, textareaElement.value.length);
2733
+ let didCopyToClipboard = false;
2734
+ try {
2735
+ didCopyToClipboard = document.execCommand("copy");
2736
+ } catch {
2737
+ didCopyToClipboard = false;
2738
+ } finally {
2739
+ doc.removeChild(textareaElement);
2740
+ }
2741
+ return didCopyToClipboard;
2742
+ };
2743
+
2662
2744
  // src/utils/is-element-visible.ts
2663
2745
  var isElementVisible = (element, computedStyle = window.getComputedStyle(element)) => {
2664
2746
  return computedStyle.display !== "none" && computedStyle.visibility !== "hidden" && computedStyle.opacity !== "0";
@@ -2811,7 +2893,7 @@ ${error.stack}`;
2811
2893
  }
2812
2894
  const resolvedOptions = {
2813
2895
  enabled: true,
2814
- hotkey: "Meta",
2896
+ hotkey: ["Meta", "C"],
2815
2897
  keyHoldDuration: 500,
2816
2898
  ...options
2817
2899
  };
@@ -2830,9 +2912,6 @@ ${error.stack}`;
2830
2912
  }
2831
2913
  return isKeyPressed(resolvedOptions.hotkey);
2832
2914
  };
2833
- const isCopyHotkeyPressed = () => {
2834
- return isKeyPressed("Meta") && isKeyPressed("C");
2835
- };
2836
2915
  let cleanupActivationHotkeyWatcher = null;
2837
2916
  const handleKeyStateChange = (pressedKeys) => {
2838
2917
  const { overlayMode } = libStore.getState();
@@ -2869,13 +2948,6 @@ ${error.stack}`;
2869
2948
  }
2870
2949
  return;
2871
2950
  }
2872
- if (isCopyHotkeyPressed() && overlayMode === "visible") {
2873
- libStore.setState((state) => ({
2874
- ...state,
2875
- overlayMode: "copying"
2876
- }));
2877
- return;
2878
- }
2879
2951
  const isActivationHotkeyPressed = checkIsActivationHotkeyPressed();
2880
2952
  if (!isActivationHotkeyPressed) {
2881
2953
  if (cleanupActivationHotkeyWatcher) {
@@ -2949,8 +3021,7 @@ ${error.stack}`;
2949
3021
  return null;
2950
3022
  };
2951
3023
  const handleCopy = async (element) => {
2952
- const rect = element.getBoundingClientRect();
2953
- const cleanupCopyIndicator = showCopyIndicator(rect.left, rect.top);
3024
+ const cleanupIndicator = updateLabelToProcessing();
2954
3025
  try {
2955
3026
  const stack = await getStack(element);
2956
3027
  const htmlSnippet = getHTMLSnippet(element);
@@ -2958,16 +3029,22 @@ ${error.stack}`;
2958
3029
  if (stack) {
2959
3030
  const filteredStack = filterStack(stack);
2960
3031
  const serializedStack = serializeStack(filteredStack);
2961
- text = `${serializedStack}
3032
+ text = `${htmlSnippet}
2962
3033
 
2963
- ${htmlSnippet}`;
3034
+ Component owner stack:
3035
+ ${serializedStack}`;
2964
3036
  }
2965
- await copyTextToClipboard(`
2966
- ${text}`);
3037
+ await copyTextToClipboard(
3038
+ `
3039
+
3040
+ <referenced_element>
3041
+ ${text}
3042
+ </referenced_element>`
3043
+ );
2967
3044
  const tagName = (element.tagName || "").toLowerCase();
2968
- cleanupCopyIndicator(tagName);
3045
+ cleanupIndicator(tagName);
2969
3046
  } catch {
2970
- cleanupCopyIndicator();
3047
+ cleanupIndicator();
2971
3048
  }
2972
3049
  };
2973
3050
  const handleRender = throttle((state) => {
@@ -2975,6 +3052,9 @@ ${text}`);
2975
3052
  if (overlayMode === "hidden") {
2976
3053
  if (selectionOverlay.isVisible()) {
2977
3054
  selectionOverlay.hide();
3055
+ if (!isCopying) {
3056
+ hideLabel();
3057
+ }
2978
3058
  hoveredElement = null;
2979
3059
  }
2980
3060
  return;
@@ -3000,7 +3080,10 @@ ${text}`);
3000
3080
  ...state2,
3001
3081
  overlayMode: "hidden"
3002
3082
  }));
3003
- isCopying = false;
3083
+ selectionOverlay.hide();
3084
+ window.setTimeout(() => {
3085
+ isCopying = false;
3086
+ }, INDICATOR_TOTAL_HIDE_DELAY_MS);
3004
3087
  });
3005
3088
  }
3006
3089
  return;
@@ -3009,10 +3092,14 @@ ${text}`);
3009
3092
  if (!element) {
3010
3093
  if (selectionOverlay.isVisible()) {
3011
3094
  selectionOverlay.hide();
3095
+ if (!isCopying) {
3096
+ hideLabel();
3097
+ }
3012
3098
  }
3013
3099
  hoveredElement = null;
3014
3100
  return;
3015
3101
  }
3102
+ const tagName = (element.tagName || "").toLowerCase();
3016
3103
  hoveredElement = element;
3017
3104
  const rect = element.getBoundingClientRect();
3018
3105
  const computedStyle = window.getComputedStyle(element);
@@ -3029,6 +3116,7 @@ ${text}`);
3029
3116
  if (!selectionOverlay.isVisible()) {
3030
3117
  selectionOverlay.show();
3031
3118
  }
3119
+ showLabel(rect.left, rect.top, tagName);
3032
3120
  }, 10);
3033
3121
  const cleanupRenderSubscription = libStore.subscribe((state) => {
3034
3122
  scheduleRunWhenIdle(() => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-grab",
3
- "version": "0.0.12",
3
+ "version": "0.0.13",
4
4
  "description": "",
5
5
  "keywords": [],
6
6
  "homepage": "https://github.com/aidenybai/react-grab#readme",