react-grab 0.0.40 → 0.0.42

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.js CHANGED
@@ -1,9 +1,7 @@
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, traverseFiber, isCompositeFiber, getDisplayName } from 'bippy';
4
- import { getSourceFromHostInstance, normalizeFileName, isSourceFile } from 'bippy/dist/source';
5
- import { finder } from '@medv/finder';
6
- import TurndownService from 'turndown';
3
+ import { getFiberFromHostInstance, traverseFiber, isCompositeFiber, getDisplayName, isFiber, getLatestFiber, isHostFiber } from 'bippy';
4
+ import { isSourceFile, normalizeFileName, getSource } from 'bippy/source';
7
5
 
8
6
  /**
9
7
  * @license MIT
@@ -95,8 +93,12 @@ var mountRoot = () => {
95
93
  var VIEWPORT_MARGIN_PX = 8;
96
94
  var INDICATOR_CLAMP_PADDING_PX = 4;
97
95
  var CURSOR_OFFSET_PX = 14;
96
+ var OFFSCREEN_POSITION = -1e3;
98
97
  var SELECTION_LERP_FACTOR = 0.95;
99
98
  var SUCCESS_LABEL_DURATION_MS = 1700;
99
+ var PROGRESS_INDICATOR_DELAY_MS = 150;
100
+ var DRAG_THRESHOLD_PX = 2;
101
+ var Z_INDEX_LABEL = 2147483647;
100
102
 
101
103
  // src/utils/lerp.ts
102
104
  var lerp = (start, end, factor) => {
@@ -261,30 +263,22 @@ var getClampedElementPosition = (positionLeft, positionTop, elementWidth, elemen
261
263
  const clampedTop = Math.max(minTop, Math.min(positionTop, maxTop));
262
264
  return { left: clampedLeft, top: clampedTop };
263
265
  };
264
-
265
- // src/components/label.tsx
266
- var _tmpl$3 = /* @__PURE__ */ template(`<span style=display:inline-block;margin-right:4px;font-weight:600>\u2713`);
267
- var _tmpl$22 = /* @__PURE__ */ template(`<div style=margin-right:4px>Copied`);
268
- 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;vertical-align:middle">`);
269
- var _tmpl$4 = /* @__PURE__ */ template(`<span style=font-variant-numeric:tabular-nums;font-size:10px;margin-left:4px;vertical-align:middle>`);
270
- var _tmpl$5 = /* @__PURE__ */ template(`<div style=margin-left:4px>to clipboard`);
271
- var _tmpl$6 = /* @__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">`);
272
- var Label = (props) => {
273
- const [opacity, setOpacity] = createSignal(0);
274
- const [positionTick, setPositionTick] = createSignal(0);
275
- let labelRef;
276
- let currentX = props.x;
277
- let currentY = props.y;
278
- let targetX = props.x;
279
- let targetY = props.y;
266
+ var useAnimatedPosition = (options) => {
267
+ const lerpFactor = options.lerpFactor ?? 0.3;
268
+ const convergenceThreshold = options.convergenceThreshold ?? 0.5;
269
+ const [x, setX] = createSignal(options.x());
270
+ const [y, setY] = createSignal(options.y());
271
+ let targetX = options.x();
272
+ let targetY = options.y();
280
273
  let animationFrameId = null;
281
274
  let hasBeenRenderedOnce = false;
282
275
  const animate = () => {
283
- currentX = lerp(currentX, targetX, 0.3);
284
- currentY = lerp(currentY, targetY, 0.3);
285
- setPositionTick((tick) => tick + 1);
286
- const hasConvergedToTarget = Math.abs(currentX - targetX) < 0.5 && Math.abs(currentY - targetY) < 0.5;
287
- if (!hasConvergedToTarget) {
276
+ const currentX = lerp(x(), targetX, lerpFactor);
277
+ const currentY = lerp(y(), targetY, lerpFactor);
278
+ setX(currentX);
279
+ setY(currentY);
280
+ const hasConverged = Math.abs(currentX - targetX) < convergenceThreshold && Math.abs(currentY - targetY) < convergenceThreshold;
281
+ if (!hasConverged) {
288
282
  animationFrameId = requestAnimationFrame(animate);
289
283
  } else {
290
284
  animationFrameId = null;
@@ -294,36 +288,16 @@ var Label = (props) => {
294
288
  if (animationFrameId !== null) return;
295
289
  animationFrameId = requestAnimationFrame(animate);
296
290
  };
297
- const updateTarget = () => {
298
- targetX = props.x;
299
- targetY = props.y;
291
+ createEffect(() => {
292
+ targetX = options.x();
293
+ targetY = options.y();
300
294
  if (!hasBeenRenderedOnce) {
301
- currentX = targetX;
302
- currentY = targetY;
295
+ setX(targetX);
296
+ setY(targetY);
303
297
  hasBeenRenderedOnce = true;
304
- setPositionTick((tick) => tick + 1);
305
298
  return;
306
299
  }
307
300
  startAnimation();
308
- };
309
- createEffect(on(() => props.visible, (visible) => {
310
- if (visible !== false) {
311
- requestAnimationFrame(() => {
312
- setOpacity(1);
313
- });
314
- } else {
315
- setOpacity(0);
316
- return;
317
- }
318
- if (props.variant === "success") {
319
- const fadeOutTimer = setTimeout(() => {
320
- setOpacity(0);
321
- }, SUCCESS_LABEL_DURATION_MS);
322
- onCleanup(() => clearTimeout(fadeOutTimer));
323
- }
324
- }));
325
- createEffect(() => {
326
- updateTarget();
327
301
  });
328
302
  onCleanup(() => {
329
303
  if (animationFrameId !== null) {
@@ -331,34 +305,91 @@ var Label = (props) => {
331
305
  animationFrameId = null;
332
306
  }
333
307
  });
308
+ return { x, y };
309
+ };
310
+ var useFadeInOut = (options) => {
311
+ const [opacity, setOpacity] = createSignal(0);
312
+ createEffect(
313
+ on(
314
+ () => options.visible,
315
+ (isVisible) => {
316
+ if (isVisible !== false) {
317
+ requestAnimationFrame(() => {
318
+ setOpacity(1);
319
+ });
320
+ } else {
321
+ setOpacity(0);
322
+ return;
323
+ }
324
+ if (options.autoFadeOutAfter !== void 0) {
325
+ const fadeOutTimer = setTimeout(() => {
326
+ setOpacity(0);
327
+ }, options.autoFadeOutAfter);
328
+ onCleanup(() => clearTimeout(fadeOutTimer));
329
+ }
330
+ }
331
+ )
332
+ );
333
+ return opacity;
334
+ };
335
+
336
+ // src/utils/get-cursor-quadrants.ts
337
+ var getCursorQuadrants = (cursorX, cursorY, elementWidth, elementHeight, offset) => {
338
+ return [
339
+ {
340
+ left: Math.round(cursorX) + offset,
341
+ top: Math.round(cursorY) + offset
342
+ },
343
+ {
344
+ left: Math.round(cursorX) - elementWidth - offset,
345
+ top: Math.round(cursorY) + offset
346
+ },
347
+ {
348
+ left: Math.round(cursorX) + offset,
349
+ top: Math.round(cursorY) - elementHeight - offset
350
+ },
351
+ {
352
+ left: Math.round(cursorX) - elementWidth - offset,
353
+ top: Math.round(cursorY) - elementHeight - offset
354
+ }
355
+ ];
356
+ };
357
+
358
+ // src/components/label.tsx
359
+ var _tmpl$3 = /* @__PURE__ */ template(`<div style="position:absolute;top:0;left:0;bottom:0;background-color:rgba(178, 28, 142, 0.2);border-radius:3px;transition:width 0.1s ease-out;pointer-events:none">`);
360
+ var _tmpl$22 = /* @__PURE__ */ template(`<span style=display:inline-block;margin-right:4px;font-weight:600>\u2713`);
361
+ var _tmpl$32 = /* @__PURE__ */ template(`<div style=margin-right:4px>Copied`);
362
+ var _tmpl$4 = /* @__PURE__ */ template(`<span style="font-family:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace;font-variant-numeric:tabular-nums;vertical-align:middle">`);
363
+ var _tmpl$5 = /* @__PURE__ */ template(`<span style=font-variant-numeric:tabular-nums;font-size:10px;margin-left:4px;vertical-align:middle>`);
364
+ var _tmpl$6 = /* @__PURE__ */ template(`<div style=margin-left:4px>to clipboard`);
365
+ var _tmpl$7 = /* @__PURE__ */ template(`<div style=font-size:9px;opacity:0.6;text-align:center;margin-top:2px>Click or drag to select`);
366
+ var _tmpl$8 = /* @__PURE__ */ template(`<div style="position:fixed;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;max-width:calc(100vw - (16px + env(safe-area-inset-left) + env(safe-area-inset-right)));overflow:hidden"><div style="position:relative;padding:2px 6px;display:flex;flex-direction:column"><div style=display:flex;align-items:center;text-overflow:ellipsis;white-space:nowrap>`);
367
+ var Label = (props) => {
368
+ let labelRef;
369
+ const position = useAnimatedPosition({
370
+ x: () => props.x,
371
+ y: () => props.y,
372
+ lerpFactor: 0.3
373
+ });
374
+ const opacity = useFadeInOut({
375
+ visible: props.visible,
376
+ autoFadeOutAfter: props.variant === "success" ? SUCCESS_LABEL_DURATION_MS : void 0
377
+ });
334
378
  const labelBoundingRect = () => labelRef?.getBoundingClientRect();
335
379
  const computedPosition = () => {
336
- positionTick();
337
380
  const boundingRect = labelBoundingRect();
338
381
  if (!boundingRect) return {
339
- left: currentX,
340
- top: currentY
382
+ left: position.x(),
383
+ top: position.y()
341
384
  };
342
385
  const viewportWidth = window.innerWidth;
343
386
  const viewportHeight = window.innerHeight;
344
- const quadrants = [{
345
- left: Math.round(currentX) + CURSOR_OFFSET_PX,
346
- top: Math.round(currentY) + CURSOR_OFFSET_PX
347
- }, {
348
- left: Math.round(currentX) - boundingRect.width - CURSOR_OFFSET_PX,
349
- top: Math.round(currentY) + CURSOR_OFFSET_PX
350
- }, {
351
- left: Math.round(currentX) + CURSOR_OFFSET_PX,
352
- top: Math.round(currentY) - boundingRect.height - CURSOR_OFFSET_PX
353
- }, {
354
- left: Math.round(currentX) - boundingRect.width - CURSOR_OFFSET_PX,
355
- top: Math.round(currentY) - boundingRect.height - CURSOR_OFFSET_PX
356
- }];
357
- for (const position of quadrants) {
358
- const fitsHorizontally = position.left >= VIEWPORT_MARGIN_PX && position.left + boundingRect.width <= viewportWidth - VIEWPORT_MARGIN_PX;
359
- const fitsVertically = position.top >= VIEWPORT_MARGIN_PX && position.top + boundingRect.height <= viewportHeight - VIEWPORT_MARGIN_PX;
387
+ const quadrants = getCursorQuadrants(position.x(), position.y(), boundingRect.width, boundingRect.height, CURSOR_OFFSET_PX);
388
+ for (const position2 of quadrants) {
389
+ const fitsHorizontally = position2.left >= VIEWPORT_MARGIN_PX && position2.left + boundingRect.width <= viewportWidth - VIEWPORT_MARGIN_PX;
390
+ const fitsVertically = position2.top >= VIEWPORT_MARGIN_PX && position2.top + boundingRect.height <= viewportHeight - VIEWPORT_MARGIN_PX;
360
391
  if (fitsHorizontally && fitsVertically) {
361
- return position;
392
+ return position2;
362
393
  }
363
394
  }
364
395
  const fallback = getClampedElementPosition(quadrants[0].left, quadrants[0].top, boundingRect.width, boundingRect.height);
@@ -385,10 +416,20 @@ var Label = (props) => {
385
416
  return props.visible !== false;
386
417
  },
387
418
  get children() {
388
- var _el$ = _tmpl$6();
419
+ var _el$ = _tmpl$8(), _el$3 = _el$.firstChild, _el$4 = _el$3.firstChild;
389
420
  var _ref$ = labelRef;
390
421
  typeof _ref$ === "function" ? use(_ref$, _el$) : labelRef = _el$;
391
422
  insert(_el$, createComponent(Show, {
423
+ get when() {
424
+ return memo(() => props.variant === "processing")() && props.progress !== void 0;
425
+ },
426
+ get children() {
427
+ var _el$2 = _tmpl$3();
428
+ effect((_$p) => setStyleProperty(_el$2, "width", `${Math.min(100, Math.max(0, (props.progress ?? 0) * 100))}%`));
429
+ return _el$2;
430
+ }
431
+ }), _el$3);
432
+ insert(_el$4, createComponent(Show, {
392
433
  get when() {
393
434
  return props.variant === "processing";
394
435
  },
@@ -396,55 +437,63 @@ var Label = (props) => {
396
437
  return createComponent(Spinner, {});
397
438
  }
398
439
  }), null);
399
- insert(_el$, createComponent(Show, {
440
+ insert(_el$4, createComponent(Show, {
400
441
  get when() {
401
442
  return props.variant === "success";
402
443
  },
403
444
  get children() {
404
- return _tmpl$3();
445
+ return _tmpl$22();
405
446
  }
406
447
  }), null);
407
- insert(_el$, createComponent(Show, {
448
+ insert(_el$4, createComponent(Show, {
408
449
  get when() {
409
450
  return props.variant === "success";
410
451
  },
411
452
  get children() {
412
- return _tmpl$22();
453
+ return _tmpl$32();
413
454
  }
414
455
  }), null);
415
- insert(_el$, createComponent(Show, {
456
+ insert(_el$4, createComponent(Show, {
416
457
  get when() {
417
458
  return props.variant === "processing";
418
459
  },
419
- children: "Grabbing\u2026"
460
+ children: "Please wait\u2026"
420
461
  }), null);
421
- insert(_el$, createComponent(Show, {
462
+ insert(_el$4, createComponent(Show, {
422
463
  get when() {
423
464
  return props.variant !== "processing";
424
465
  },
425
466
  get children() {
426
467
  return [(() => {
427
- var _el$4 = _tmpl$32();
428
- insert(_el$4, () => labelSegments().primary);
429
- return _el$4;
468
+ var _el$7 = _tmpl$4();
469
+ insert(_el$7, () => labelSegments().primary);
470
+ return _el$7;
430
471
  })(), createComponent(Show, {
431
472
  get when() {
432
473
  return memo(() => props.variant === "hover")() && labelSegments().secondary !== "";
433
474
  },
434
475
  get children() {
435
- var _el$5 = _tmpl$4();
436
- insert(_el$5, () => labelSegments().secondary);
437
- return _el$5;
476
+ var _el$8 = _tmpl$5();
477
+ insert(_el$8, () => labelSegments().secondary);
478
+ return _el$8;
438
479
  }
439
480
  })];
440
481
  }
441
482
  }), null);
442
- insert(_el$, createComponent(Show, {
483
+ insert(_el$4, createComponent(Show, {
443
484
  get when() {
444
485
  return props.variant === "success";
445
486
  },
446
487
  get children() {
447
- return _tmpl$5();
488
+ return _tmpl$6();
489
+ }
490
+ }), null);
491
+ insert(_el$3, createComponent(Show, {
492
+ get when() {
493
+ return memo(() => props.variant === "hover")() && props.showHint;
494
+ },
495
+ get children() {
496
+ return _tmpl$7();
448
497
  }
449
498
  }), null);
450
499
  effect((_p$) => {
@@ -464,22 +513,11 @@ var Label = (props) => {
464
513
  }
465
514
  });
466
515
  };
467
- var _tmpl$7 = /* @__PURE__ */ template(`<div style="position:fixed;z-index:2147483647;pointer-events:none;transition:opacity 0.1s ease-in-out"><div style="width:32px;height:2px;background-color:rgba(178, 28, 142, 0.2);border-radius:1px;overflow:hidden;position:relative"><div style="height:100%;background-color:#b21c8e;border-radius:1px;transition:width 0.05s cubic-bezier(0.165, 0.84, 0.44, 1)">`);
468
- var useFadeInOut = (visible) => {
469
- const [opacity, setOpacity] = createSignal(0);
470
- createEffect(on(() => visible, (isVisible) => {
471
- if (isVisible !== false) {
472
- requestAnimationFrame(() => {
473
- setOpacity(1);
474
- });
475
- } else {
476
- setOpacity(0);
477
- }
478
- }));
479
- return opacity;
480
- };
516
+ var _tmpl$9 = /* @__PURE__ */ template(`<div style="position:fixed;z-index:2147483647;pointer-events:none;transition:opacity 0.1s ease-in-out"><div style="width:32px;height:2px;background-color:rgba(178, 28, 142, 0.2);border-radius:1px;overflow:hidden;position:relative"><div style="height:100%;background-color:#b21c8e;border-radius:1px;transition:width 0.05s cubic-bezier(0.165, 0.84, 0.44, 1)">`);
481
517
  var ProgressIndicator = (props) => {
482
- const opacity = useFadeInOut(props.visible);
518
+ const opacity = useFadeInOut({
519
+ visible: props.visible
520
+ });
483
521
  let progressIndicatorRef;
484
522
  const computedPosition = () => {
485
523
  const boundingRect = progressIndicatorRef?.getBoundingClientRect();
@@ -497,7 +535,7 @@ var ProgressIndicator = (props) => {
497
535
  return props.visible !== false;
498
536
  },
499
537
  get children() {
500
- var _el$ = _tmpl$7(), _el$2 = _el$.firstChild, _el$3 = _el$2.firstChild;
538
+ var _el$ = _tmpl$9(), _el$2 = _el$.firstChild, _el$3 = _el$2.firstChild;
501
539
  var _ref$ = progressIndicatorRef;
502
540
  typeof _ref$ === "function" ? use(_ref$, _el$) : progressIndicatorRef = _el$;
503
541
  effect((_p$) => {
@@ -517,19 +555,18 @@ var ProgressIndicator = (props) => {
517
555
  }
518
556
  });
519
557
  };
520
- var _tmpl$8 = /* @__PURE__ */ template(`<canvas style=position:fixed;top:0;left:0;pointer-events:none;z-index:2147483645>`);
558
+ var _tmpl$10 = /* @__PURE__ */ template(`<canvas style=position:fixed;top:0;left:0;pointer-events:none;z-index:2147483645>`);
521
559
  var Crosshair = (props) => {
522
560
  let canvasRef;
523
561
  let context = null;
524
562
  let width = 0;
525
563
  let height = 0;
526
564
  let dpr = 1;
527
- let currentX = props.mouseX;
528
- let currentY = props.mouseY;
529
- let targetX = props.mouseX;
530
- let targetY = props.mouseY;
531
- let animationFrameId = null;
532
- let hasBeenRenderedOnce = false;
565
+ const position = useAnimatedPosition({
566
+ x: () => props.mouseX,
567
+ y: () => props.mouseY,
568
+ lerpFactor: 0.3
569
+ });
533
570
  const setupCanvas = () => {
534
571
  if (!canvasRef) return;
535
572
  dpr = Math.max(window.devicePixelRatio || 1, 2);
@@ -550,39 +587,12 @@ var Crosshair = (props) => {
550
587
  context.strokeStyle = "rgba(210, 57, 192)";
551
588
  context.lineWidth = 1;
552
589
  context.beginPath();
553
- context.moveTo(currentX, 0);
554
- context.lineTo(currentX, height);
555
- context.moveTo(0, currentY);
556
- context.lineTo(width, currentY);
590
+ context.moveTo(position.x(), 0);
591
+ context.lineTo(position.x(), height);
592
+ context.moveTo(0, position.y());
593
+ context.lineTo(width, position.y());
557
594
  context.stroke();
558
595
  };
559
- const animate = () => {
560
- currentX = lerp(currentX, targetX, 0.3);
561
- currentY = lerp(currentY, targetY, 0.3);
562
- render2();
563
- const hasConvergedToTarget = Math.abs(currentX - targetX) < 0.5 && Math.abs(currentY - targetY) < 0.5;
564
- if (!hasConvergedToTarget) {
565
- animationFrameId = requestAnimationFrame(animate);
566
- } else {
567
- animationFrameId = null;
568
- }
569
- };
570
- const startAnimation = () => {
571
- if (animationFrameId !== null) return;
572
- animationFrameId = requestAnimationFrame(animate);
573
- };
574
- const updateTarget = () => {
575
- targetX = props.mouseX;
576
- targetY = props.mouseY;
577
- if (!hasBeenRenderedOnce) {
578
- currentX = targetX;
579
- currentY = targetY;
580
- hasBeenRenderedOnce = true;
581
- render2();
582
- return;
583
- }
584
- startAnimation();
585
- };
586
596
  createEffect(() => {
587
597
  setupCanvas();
588
598
  render2();
@@ -593,21 +603,19 @@ var Crosshair = (props) => {
593
603
  window.addEventListener("resize", handleResize);
594
604
  onCleanup(() => {
595
605
  window.removeEventListener("resize", handleResize);
596
- if (animationFrameId !== null) {
597
- cancelAnimationFrame(animationFrameId);
598
- animationFrameId = null;
599
- }
600
606
  });
601
607
  });
602
608
  createEffect(() => {
603
- updateTarget();
609
+ position.x();
610
+ position.y();
611
+ render2();
604
612
  });
605
613
  return createComponent(Show, {
606
614
  get when() {
607
615
  return props.visible !== false;
608
616
  },
609
617
  get children() {
610
- var _el$ = _tmpl$8();
618
+ var _el$ = _tmpl$10();
611
619
  var _ref$ = canvasRef;
612
620
  typeof _ref$ === "function" ? use(_ref$, _el$) : canvasRef = _el$;
613
621
  return _el$;
@@ -675,22 +683,29 @@ var ReactGrabRenderer = (props) => {
675
683
  return box.createdAt;
676
684
  }
677
685
  })
678
- }), createComponent(For, {
679
- get each() {
680
- return props.successLabels ?? [];
686
+ }), createComponent(Show, {
687
+ get when() {
688
+ return props.labelVariant !== "processing";
681
689
  },
682
- children: (label) => createComponent(Label, {
683
- variant: "success",
684
- get text() {
685
- return label.text;
686
- },
687
- get x() {
688
- return props.mouseX ?? 0;
689
- },
690
- get y() {
691
- return props.mouseY ?? 0;
692
- }
693
- })
690
+ get children() {
691
+ return createComponent(For, {
692
+ get each() {
693
+ return props.successLabels ?? [];
694
+ },
695
+ children: (label) => createComponent(Label, {
696
+ variant: "success",
697
+ get text() {
698
+ return label.text;
699
+ },
700
+ get x() {
701
+ return props.mouseX ?? 0;
702
+ },
703
+ get y() {
704
+ return props.mouseY ?? 0;
705
+ }
706
+ })
707
+ });
708
+ }
694
709
  }), createComponent(Show, {
695
710
  get when() {
696
711
  return memo(() => !!(props.labelVisible && props.labelVariant && props.labelText && props.labelX !== void 0))() && props.labelY !== void 0;
@@ -714,6 +729,12 @@ var ReactGrabRenderer = (props) => {
714
729
  },
715
730
  get zIndex() {
716
731
  return props.labelZIndex;
732
+ },
733
+ get progress() {
734
+ return props.progress;
735
+ },
736
+ get showHint() {
737
+ return props.labelShowHint;
717
738
  }
718
739
  });
719
740
  }
@@ -744,27 +765,62 @@ var ReactGrabRenderer = (props) => {
744
765
  var isCapitalized = (value) => value.length > 0 && /^[A-Z]/.test(value);
745
766
 
746
767
  // src/instrumentation.ts
747
- instrument({
748
- onCommitFiberRoot(_, fiberRoot) {
749
- _fiberRoots.add(fiberRoot);
750
- }
751
- });
752
- var generateCSSSelector = (element) => {
753
- return finder(element);
768
+ var NEXT_INTERNAL_COMPONENT_NAMES = [
769
+ "InnerLayoutRouter",
770
+ "RedirectErrorBoundary",
771
+ "RedirectBoundary",
772
+ "HTTPAccessFallbackErrorBoundary",
773
+ "HTTPAccessFallbackBoundary",
774
+ "LoadingBoundary",
775
+ "ErrorBoundary",
776
+ "InnerScrollAndFocusHandler",
777
+ "ScrollAndFocusHandler",
778
+ "RenderFromTemplateContext",
779
+ "OuterLayoutRouter",
780
+ "body",
781
+ "html",
782
+ "RedirectErrorBoundary",
783
+ "RedirectBoundary",
784
+ "HTTPAccessFallbackErrorBoundary",
785
+ "HTTPAccessFallbackBoundary",
786
+ "DevRootHTTPAccessFallbackBoundary",
787
+ "AppDevOverlayErrorBoundary",
788
+ "AppDevOverlay",
789
+ "HotReload",
790
+ "Router",
791
+ "ErrorBoundaryHandler",
792
+ "ErrorBoundary",
793
+ "AppRouter",
794
+ "ServerRoot",
795
+ "SegmentStateProvider",
796
+ "RootErrorBoundary"
797
+ ];
798
+ var checkIsNextProject = () => {
799
+ return Boolean(document.getElementById("__NEXT_DATA__"));
800
+ };
801
+ var checkIsInternalComponentName = (name) => {
802
+ if (name.startsWith("_")) return true;
803
+ if (NEXT_INTERNAL_COMPONENT_NAMES.includes(name)) return true;
804
+ return false;
754
805
  };
755
- var truncateString = (string, maxLength) => string.length > maxLength ? `${string.substring(0, maxLength)}...` : string;
756
- var isInternalComponent = (name) => !isCapitalized(name) || name.startsWith("_") || name.startsWith("Primitive.") || name.includes("Provider") && name.includes("Context");
757
- var getNearestComponentDisplayName = (element) => {
806
+ var checkIsSourceComponentName = (name) => {
807
+ if (checkIsInternalComponentName(name)) return false;
808
+ if (!isCapitalized(name)) return false;
809
+ if (name.startsWith("Primitive.")) return false;
810
+ if (name.includes("Provider") && name.includes("Context")) return false;
811
+ return true;
812
+ };
813
+ var getNearestComponentName = (element) => {
758
814
  const fiber = getFiberFromHostInstance(element);
759
815
  if (!fiber) return null;
760
- let componentName = null;
816
+ let foundComponentName = null;
761
817
  traverseFiber(
762
818
  fiber,
763
819
  (currentFiber) => {
764
820
  if (isCompositeFiber(currentFiber)) {
765
821
  const displayName = getDisplayName(currentFiber);
766
- if (displayName && !isInternalComponent(displayName)) {
767
- componentName = displayName;
822
+ if (displayName && checkIsSourceComponentName(displayName)) {
823
+ foundComponentName = displayName;
768
824
  return true;
769
825
  }
770
826
  }
@@ -772,231 +828,107 @@ var getNearestComponentDisplayName = (element) => {
772
828
  },
773
829
  true
774
830
  );
775
- return componentName;
776
- };
777
- var formatComponentSourceLocation = async (el) => {
778
- const source = await getSourceFromHostInstance(el);
779
- if (!source) return null;
780
- const fileName = normalizeFileName(source.fileName);
781
- if (isSourceFile(fileName)) {
782
- return `${fileName}:${source.lineNumber}:${source.columnNumber}`;
783
- }
784
- if (fileName && (fileName.includes(".tsx") || fileName.includes(".ts") || fileName.includes(".jsx") || fileName.includes(".js"))) {
785
- const cleanedFileName = fileName.replace(/^webpack:\/\/_N_E\//, "").replace(/^webpack:\/\/\//, "").replace(/^webpack:\/\//, "").replace(/^\.\//, "");
786
- if (cleanedFileName && !cleanedFileName.startsWith("node_modules") && !cleanedFileName.includes(".next") && !cleanedFileName.startsWith("webpack")) {
787
- return `${cleanedFileName}:${source.lineNumber}:${source.columnNumber}`;
788
- }
789
- }
790
- return null;
831
+ return foundComponentName;
791
832
  };
792
- var getHTMLSnippet = async (element) => {
793
- const semanticTags = /* @__PURE__ */ new Set([
794
- "article",
795
- "aside",
796
- "footer",
797
- "form",
798
- "header",
799
- "main",
800
- "nav",
801
- "section"
802
- ]);
803
- const hasDistinguishingFeatures = (el) => {
804
- const tagName = el.tagName.toLowerCase();
805
- if (semanticTags.has(tagName)) return true;
806
- if (el.id) return true;
807
- if (el.className && typeof el.className === "string") {
808
- const classes = el.className.trim();
809
- if (classes && classes.length > 0) return true;
810
- }
811
- return Array.from(el.attributes).some(
812
- (attr) => attr.name.startsWith("data-")
813
- );
814
- };
815
- const collectDistinguishingAncestors = (el, maxDepth = 10) => {
816
- const ancestors2 = [];
817
- let current = el.parentElement;
818
- let depth = 0;
819
- while (current && depth < maxDepth && current.tagName !== "BODY") {
820
- if (hasDistinguishingFeatures(current)) {
821
- ancestors2.push(current);
822
- if (ancestors2.length >= 3) break;
823
- }
824
- current = current.parentElement;
825
- depth++;
826
- }
827
- return ancestors2.reverse();
828
- };
829
- const formatElementOpeningTag = (el, compact = false) => {
830
- const tagName = el.tagName.toLowerCase();
831
- const attrs = [];
832
- if (el.id) {
833
- attrs.push(`id="${el.id}"`);
834
- }
835
- if (el.className && typeof el.className === "string") {
836
- const classes = el.className.trim().split(/\s+/);
837
- if (classes.length > 0 && classes[0]) {
838
- const displayClasses = compact ? classes.slice(0, 3) : classes;
839
- const classStr = truncateString(displayClasses.join(" "), 30);
840
- attrs.push(`class="${classStr}"`);
841
- }
842
- }
843
- const dataAttrs = Array.from(el.attributes).filter(
844
- (attr) => attr.name.startsWith("data-")
845
- );
846
- const displayDataAttrs = compact ? dataAttrs.slice(0, 1) : dataAttrs;
847
- for (const attr of displayDataAttrs) {
848
- attrs.push(`${attr.name}="${truncateString(attr.value, 20)}"`);
849
- }
850
- const ariaLabel = el.getAttribute("aria-label");
851
- if (ariaLabel && !compact) {
852
- attrs.push(`aria-label="${truncateString(ariaLabel, 20)}"`);
853
- }
854
- return attrs.length > 0 ? `<${tagName} ${attrs.join(" ")}>` : `<${tagName}>`;
855
- };
856
- const formatElementClosingTag = (el) => `</${el.tagName.toLowerCase()}>`;
857
- const extractTruncatedTextContent = (el) => {
858
- const text = (el.textContent || "").trim().replace(/\s+/g, " ");
859
- return truncateString(text, 60);
860
- };
861
- const extractSiblingIdentifier = (el) => {
862
- if (el.id) return `#${el.id}`;
863
- if (el.className && typeof el.className === "string") {
864
- const classes = el.className.trim().split(/\s+/);
865
- if (classes.length > 0 && classes[0]) {
866
- return `.${classes[0]}`;
833
+ var getStack = async (element) => {
834
+ const maybeFiber = getFiberFromHostInstance(element);
835
+ if (!maybeFiber || !isFiber(maybeFiber)) return [];
836
+ const fiber = getLatestFiber(maybeFiber);
837
+ const unresolvedStack = [];
838
+ traverseFiber(
839
+ fiber,
840
+ (currentFiber) => {
841
+ const displayName = isHostFiber(currentFiber) ? typeof currentFiber.type === "string" ? currentFiber.type : null : getDisplayName(currentFiber);
842
+ if (displayName && !checkIsInternalComponentName(displayName)) {
843
+ unresolvedStack.push({
844
+ name: displayName,
845
+ sourcePromise: getSource(currentFiber)
846
+ });
867
847
  }
868
- }
869
- return null;
870
- };
871
- const lines = [];
872
- const selector = generateCSSSelector(element);
873
- lines.push(`- selector: ${selector}`);
874
- const rect = element.getBoundingClientRect();
875
- lines.push(`- width: ${Math.round(rect.width)}`);
876
- lines.push(`- height: ${Math.round(rect.height)}`);
877
- lines.push("HTML snippet:");
878
- lines.push("```html");
879
- const ancestors = collectDistinguishingAncestors(element);
880
- const ancestorComponents = ancestors.map(
881
- (ancestor) => getNearestComponentDisplayName(ancestor)
848
+ },
849
+ true
882
850
  );
883
- const elementComponent = getNearestComponentDisplayName(element);
884
- const ancestorSources = await Promise.all(
885
- ancestors.map((ancestor) => formatComponentSourceLocation(ancestor))
851
+ const resolvedStack = await Promise.all(
852
+ unresolvedStack.map(async (frame) => ({
853
+ name: frame.name,
854
+ source: await frame.sourcePromise
855
+ }))
886
856
  );
887
- const elementSource = await formatComponentSourceLocation(element);
888
- const ancestorElementIndents = [];
889
- const ancestorComponentIndents = [];
890
- let currentIndentLevel = 0;
891
- const getIndent = (level) => " ".repeat(level);
892
- for (let i = 0; i < ancestors.length; i++) {
893
- const componentName = ancestorComponents[i];
894
- const source = ancestorSources[i];
895
- const isNewComponent = componentName && source && (i === 0 || ancestorComponents[i - 1] !== componentName);
896
- if (isNewComponent) {
897
- ancestorComponentIndents[i] = currentIndentLevel;
898
- lines.push(
899
- `${getIndent(currentIndentLevel)}<${componentName} used-at="${source}">`
900
- );
901
- currentIndentLevel++;
857
+ return resolvedStack.filter((frame) => frame.source !== null);
858
+ };
859
+ var formatStack = (stack) => {
860
+ const isNextProject = checkIsNextProject();
861
+ return stack.map(({ name, source }) => {
862
+ if (!source) return ` at ${name}`;
863
+ if (source.fileName.startsWith("about://React/Server")) {
864
+ return ` at ${name} (Server)`;
902
865
  }
903
- ancestorElementIndents[i] = currentIndentLevel;
904
- lines.push(
905
- `${getIndent(currentIndentLevel)}${formatElementOpeningTag(ancestors[i], true)}`
906
- );
907
- currentIndentLevel++;
908
- }
909
- const parent = element.parentElement;
910
- let targetIndex = -1;
911
- if (parent) {
912
- const siblings = Array.from(parent.children);
913
- targetIndex = siblings.indexOf(element);
914
- if (targetIndex > 0) {
915
- const indent = getIndent(currentIndentLevel);
916
- if (targetIndex <= 2) {
917
- for (let i = 0; i < targetIndex; i++) {
918
- const sibling = siblings[i];
919
- const siblingId = extractSiblingIdentifier(sibling);
920
- if (siblingId) {
921
- lines.push(`${indent}${formatElementOpeningTag(sibling, true)}`);
922
- lines.push(`${indent}</${sibling.tagName.toLowerCase()}>`);
923
- }
924
- }
925
- } else {
926
- lines.push(
927
- `${indent}... (${targetIndex} element${targetIndex === 1 ? "" : "s"})`
928
- );
929
- }
866
+ if (!isSourceFile(source.fileName)) return ` at ${name}`;
867
+ const framePart = ` at ${name} in ${normalizeFileName(source.fileName)}`;
868
+ if (isNextProject) {
869
+ return `${framePart}:${source.lineNumber}:${source.columnNumber}`;
930
870
  }
871
+ return framePart;
872
+ }).join("\n");
873
+ };
874
+ var getHTMLPreview = (element) => {
875
+ const tagName = element.tagName.toLowerCase();
876
+ if (!(element instanceof HTMLElement)) {
877
+ return `<${tagName} />`;
931
878
  }
932
- const lastAncestorComponent = ancestors.length > 0 ? ancestorComponents[ancestorComponents.length - 1] : null;
933
- const showElementComponent = elementComponent && elementSource && elementComponent !== lastAncestorComponent;
934
- let elementIndentLevel = currentIndentLevel;
935
- const elementComponentIndentLevel = currentIndentLevel;
936
- if (showElementComponent) {
937
- lines.push(
938
- `${getIndent(elementIndentLevel)}<${elementComponent} used-at="${elementSource}">`
939
- );
940
- elementIndentLevel++;
941
- }
942
- const elementIndent = getIndent(elementIndentLevel);
943
- lines.push(`${elementIndent}<!-- IMPORTANT: selected element -->`);
944
- const textContent = extractTruncatedTextContent(element);
945
- const childrenCount = element.children.length;
946
- if (textContent && childrenCount === 0 && textContent.length < 40) {
947
- lines.push(
948
- `${elementIndent}${formatElementOpeningTag(element)}${textContent}${formatElementClosingTag(
949
- element
950
- )}`
951
- );
952
- } else {
953
- lines.push(`${elementIndent}${formatElementOpeningTag(element)}`);
954
- if (textContent) {
955
- lines.push(`${elementIndent} ${textContent}`);
956
- }
957
- if (childrenCount > 0) {
958
- lines.push(
959
- `${elementIndent} ... (${childrenCount} element${childrenCount === 1 ? "" : "s"})`
960
- );
879
+ const text = element.innerText?.trim() ?? element.textContent?.trim() ?? "";
880
+ let attrsText = "";
881
+ const attributes = Array.from(element.attributes);
882
+ for (const attribute of attributes) {
883
+ const name = attribute.name;
884
+ let value = attribute.value;
885
+ if (value.length > 20) {
886
+ value = `${value.slice(0, 20)}...`;
961
887
  }
962
- lines.push(`${elementIndent}${formatElementClosingTag(element)}`);
888
+ attrsText += ` ${name}="${value}"`;
963
889
  }
964
- if (showElementComponent) {
965
- lines.push(
966
- `${getIndent(elementComponentIndentLevel)}</${elementComponent}>`
967
- );
968
- }
969
- if (parent && targetIndex >= 0) {
970
- const siblings = Array.from(parent.children);
971
- const siblingsAfter = siblings.length - targetIndex - 1;
972
- if (siblingsAfter > 0) {
973
- const indent = getIndent(currentIndentLevel);
974
- if (siblingsAfter <= 2) {
975
- for (let i = targetIndex + 1; i < siblings.length; i++) {
976
- const sibling = siblings[i];
977
- const siblingId = extractSiblingIdentifier(sibling);
978
- if (siblingId) {
979
- lines.push(`${indent}${formatElementOpeningTag(sibling, true)}`);
980
- lines.push(`${indent}</${sibling.tagName.toLowerCase()}>`);
981
- }
982
- }
890
+ const topElements = [];
891
+ const bottomElements = [];
892
+ let foundFirstText = false;
893
+ const childNodes = Array.from(element.childNodes);
894
+ for (const node of childNodes) {
895
+ if (node.nodeType === Node.COMMENT_NODE) continue;
896
+ if (node.nodeType === Node.TEXT_NODE) {
897
+ if (node.textContent && node.textContent.trim().length > 0) {
898
+ foundFirstText = true;
899
+ }
900
+ } else if (node.nodeType === Node.ELEMENT_NODE) {
901
+ if (!foundFirstText) {
902
+ topElements.push(node);
983
903
  } else {
984
- lines.push(
985
- `${indent}... (${siblingsAfter} element${siblingsAfter === 1 ? "" : "s"})`
986
- );
904
+ bottomElements.push(node);
987
905
  }
988
906
  }
989
907
  }
990
- for (let i = ancestors.length - 1; i >= 0; i--) {
991
- const elementIndent2 = getIndent(ancestorElementIndents[i]);
992
- lines.push(`${elementIndent2}${formatElementClosingTag(ancestors[i])}`);
993
- if (ancestorComponentIndents[i] !== void 0) {
994
- const compIndent = getIndent(ancestorComponentIndents[i]);
995
- lines.push(`${compIndent}</${ancestorComponents[i]}>`);
908
+ const formatElements = (elements) => {
909
+ if (elements.length === 0) return "";
910
+ if (elements.length <= 2) {
911
+ return elements.map((el) => `<${el.tagName.toLowerCase()} ...>`).join("\n ");
996
912
  }
913
+ return `(${elements.length} elements)`;
914
+ };
915
+ let content = "";
916
+ const topElementsStr = formatElements(topElements);
917
+ if (topElementsStr) content += `
918
+ ${topElementsStr}`;
919
+ if (text.length > 0) {
920
+ const truncatedText = text.length > 100 ? `${text.slice(0, 100)}...` : text;
921
+ content += `
922
+ ${truncatedText}`;
997
923
  }
998
- lines.push("```");
999
- return lines.join("\n");
924
+ const bottomElementsStr = formatElements(bottomElements);
925
+ if (bottomElementsStr) content += `
926
+ ${bottomElementsStr}`;
927
+ if (content.length > 0) {
928
+ return `<${tagName}${attrsText}>${content}
929
+ </${tagName}>`;
930
+ }
931
+ return `<${tagName}${attrsText} />`;
1000
932
  };
1001
933
 
1002
934
  // src/utils/copy-content.ts
@@ -1016,73 +948,13 @@ var waitForFocus = () => {
1016
948
  var copyContent = async (content, onSuccess) => {
1017
949
  await waitForFocus();
1018
950
  try {
1019
- if (Array.isArray(content)) {
1020
- if (!navigator?.clipboard?.write) {
1021
- for (const contentPart of content) {
1022
- if (typeof contentPart === "string") {
1023
- const result = copyContentFallback(contentPart, onSuccess);
1024
- if (!result) return result;
1025
- }
1026
- }
1027
- onSuccess?.();
1028
- return true;
1029
- }
1030
- const mimeTypeMap = /* @__PURE__ */ new Map();
1031
- for (const contentPart of content) {
1032
- if (contentPart instanceof Blob) {
1033
- const mimeType = contentPart.type || "text/plain";
1034
- if (!mimeTypeMap.has(mimeType)) {
1035
- mimeTypeMap.set(mimeType, contentPart);
1036
- }
1037
- } else {
1038
- if (!mimeTypeMap.has("text/plain")) {
1039
- mimeTypeMap.set(
1040
- "text/plain",
1041
- new Blob([contentPart], { type: "text/plain" })
1042
- );
1043
- }
1044
- }
1045
- }
1046
- if (mimeTypeMap.size === 0) {
1047
- const plainTextFallback = content.find(
1048
- (contentPart) => typeof contentPart === "string"
1049
- );
1050
- if (typeof plainTextFallback === "string") {
1051
- return copyContentFallback(plainTextFallback, onSuccess);
1052
- }
1053
- return false;
1054
- }
1055
- try {
1056
- await navigator.clipboard.write([
1057
- new ClipboardItem(Object.fromEntries(mimeTypeMap))
1058
- ]);
1059
- onSuccess?.();
1060
- return true;
1061
- } catch {
1062
- const plainTextParts = content.filter(
1063
- (contentPart) => typeof contentPart === "string"
1064
- );
1065
- if (plainTextParts.length > 0) {
1066
- const combinedText = plainTextParts.join("\n\n");
1067
- return copyContentFallback(combinedText, onSuccess);
1068
- }
1069
- return false;
1070
- }
1071
- } else if (content instanceof Blob) {
1072
- await navigator.clipboard.write([
1073
- new ClipboardItem({ [content.type]: content })
1074
- ]);
951
+ try {
952
+ await navigator.clipboard.writeText(content);
1075
953
  onSuccess?.();
1076
954
  return true;
1077
- } else {
1078
- try {
1079
- await navigator.clipboard.writeText(String(content));
1080
- onSuccess?.();
1081
- return true;
1082
- } catch {
1083
- const result = copyContentFallback(content, onSuccess);
1084
- return result;
1085
- }
955
+ } catch {
956
+ const result = copyContentFallback(content, onSuccess);
957
+ return result;
1086
958
  }
1087
959
  } catch {
1088
960
  return false;
@@ -1171,13 +1043,27 @@ var getElementAtPosition = (clientX, clientY) => {
1171
1043
 
1172
1044
  // src/utils/get-elements-in-drag.ts
1173
1045
  var DRAG_COVERAGE_THRESHOLD = 0.75;
1046
+ var calculateIntersectionArea = (rect1, rect2) => {
1047
+ const intersectionLeft = Math.max(rect1.left, rect2.left);
1048
+ const intersectionTop = Math.max(rect1.top, rect2.top);
1049
+ const intersectionRight = Math.min(rect1.right, rect2.right);
1050
+ const intersectionBottom = Math.min(rect1.bottom, rect2.bottom);
1051
+ const intersectionWidth = Math.max(0, intersectionRight - intersectionLeft);
1052
+ const intersectionHeight = Math.max(0, intersectionBottom - intersectionTop);
1053
+ return intersectionWidth * intersectionHeight;
1054
+ };
1055
+ var hasIntersection = (rect1, rect2) => {
1056
+ return rect1.left < rect2.right && rect1.right > rect2.left && rect1.top < rect2.bottom && rect1.bottom > rect2.top;
1057
+ };
1174
1058
  var filterElementsInDrag = (dragRect, isValidGrabbableElement2, shouldCheckCoverage) => {
1175
1059
  const elements = [];
1176
1060
  const allElements = Array.from(document.querySelectorAll("*"));
1177
- const dragLeft = dragRect.x;
1178
- const dragTop = dragRect.y;
1179
- const dragRight = dragRect.x + dragRect.width;
1180
- const dragBottom = dragRect.y + dragRect.height;
1061
+ const dragBounds = {
1062
+ left: dragRect.x,
1063
+ top: dragRect.y,
1064
+ right: dragRect.x + dragRect.width,
1065
+ bottom: dragRect.y + dragRect.height
1066
+ };
1181
1067
  for (const candidateElement of allElements) {
1182
1068
  if (!shouldCheckCoverage) {
1183
1069
  const tagName = (candidateElement.tagName || "").toUpperCase();
@@ -1187,26 +1073,21 @@ var filterElementsInDrag = (dragRect, isValidGrabbableElement2, shouldCheckCover
1187
1073
  continue;
1188
1074
  }
1189
1075
  const elementRect = candidateElement.getBoundingClientRect();
1190
- const elementLeft = elementRect.left;
1191
- const elementTop = elementRect.top;
1192
- const elementRight = elementRect.left + elementRect.width;
1193
- const elementBottom = elementRect.top + elementRect.height;
1076
+ const elementBounds = {
1077
+ left: elementRect.left,
1078
+ top: elementRect.top,
1079
+ right: elementRect.left + elementRect.width,
1080
+ bottom: elementRect.top + elementRect.height
1081
+ };
1194
1082
  if (shouldCheckCoverage) {
1195
- const intersectionLeft = Math.max(dragLeft, elementLeft);
1196
- const intersectionTop = Math.max(dragTop, elementTop);
1197
- const intersectionRight = Math.min(dragRight, elementRight);
1198
- const intersectionBottom = Math.min(dragBottom, elementBottom);
1199
- const intersectionWidth = Math.max(0, intersectionRight - intersectionLeft);
1200
- const intersectionHeight = Math.max(0, intersectionBottom - intersectionTop);
1201
- const intersectionArea = intersectionWidth * intersectionHeight;
1083
+ const intersectionArea = calculateIntersectionArea(dragBounds, elementBounds);
1202
1084
  const elementArea = Math.max(0, elementRect.width * elementRect.height);
1203
1085
  const hasMajorityCoverage = elementArea > 0 && intersectionArea / elementArea >= DRAG_COVERAGE_THRESHOLD;
1204
1086
  if (hasMajorityCoverage) {
1205
1087
  elements.push(candidateElement);
1206
1088
  }
1207
1089
  } else {
1208
- const hasIntersection = elementLeft < dragRight && elementRight > dragLeft && elementTop < dragBottom && elementBottom > dragTop;
1209
- if (hasIntersection) {
1090
+ if (hasIntersection(elementBounds, dragBounds)) {
1210
1091
  elements.push(candidateElement);
1211
1092
  }
1212
1093
  }
@@ -1245,70 +1126,7 @@ var createElementBounds = (element) => {
1245
1126
  };
1246
1127
  };
1247
1128
 
1248
- // src/utils/is-localhost.ts
1249
- var isLocalhost = () => {
1250
- const hostname = window.location.hostname;
1251
- return hostname === "localhost" || hostname === "127.0.0.1" || hostname === "[::1]";
1252
- };
1253
- var turndownService = null;
1254
- var getTurndownService = () => {
1255
- if (!turndownService) {
1256
- turndownService = new TurndownService({
1257
- headingStyle: "atx",
1258
- codeBlockStyle: "fenced",
1259
- emDelimiter: "_",
1260
- bulletListMarker: "-",
1261
- linkStyle: "inlined",
1262
- linkReferenceStyle: "full"
1263
- });
1264
- turndownService.addRule("strikethrough", {
1265
- filter: ["del", "s"],
1266
- replacement: (content) => `~~${content}~~`
1267
- });
1268
- turndownService.addRule("removeHidden", {
1269
- filter: (node) => {
1270
- if (node instanceof HTMLElement) {
1271
- const style = window.getComputedStyle(node);
1272
- return style.display === "none" || style.visibility === "hidden" || style.opacity === "0";
1273
- }
1274
- return false;
1275
- },
1276
- replacement: () => ""
1277
- });
1278
- turndownService.addRule("preserveButtons", {
1279
- filter: ["button"],
1280
- replacement: (content) => content ? `[${content}]` : ""
1281
- });
1282
- turndownService.addRule("preserveInputs", {
1283
- filter: (node) => {
1284
- if (node.nodeName === "INPUT" && node instanceof HTMLInputElement) {
1285
- return true;
1286
- }
1287
- return false;
1288
- },
1289
- replacement: (_, node) => {
1290
- if (node instanceof HTMLInputElement) {
1291
- const value = node.value || node.placeholder;
1292
- return value ? `[${value}]` : "";
1293
- }
1294
- return "";
1295
- }
1296
- });
1297
- }
1298
- return turndownService;
1299
- };
1300
- var htmlToMarkdown = (html) => {
1301
- const service = getTurndownService();
1302
- return service.turndown(html).trim();
1303
- };
1304
- var elementToMarkdown = (element) => {
1305
- const clonedElement = element.cloneNode(true);
1306
- const service = getTurndownService();
1307
- return service.turndown(clonedElement).trim();
1308
- };
1309
-
1310
1129
  // src/core.tsx
1311
- var PROGRESS_INDICATOR_DELAY_MS = 150;
1312
1130
  var init = (rawOptions) => {
1313
1131
  const options = {
1314
1132
  enabled: true,
@@ -1331,7 +1149,6 @@ var init = (rawOptions) => {
1331
1149
  };
1332
1150
  }
1333
1151
  return createRoot((dispose) => {
1334
- const OFFSCREEN_POSITION = -1e3;
1335
1152
  const [isHoldingKeys, setIsHoldingKeys] = createSignal(false);
1336
1153
  const [mouseX, setMouseX] = createSignal(OFFSCREEN_POSITION);
1337
1154
  const [mouseY, setMouseY] = createSignal(OFFSCREEN_POSITION);
@@ -1341,7 +1158,7 @@ var init = (rawOptions) => {
1341
1158
  const [isCopying, setIsCopying] = createSignal(false);
1342
1159
  const [lastGrabbedElement, setLastGrabbedElement] = createSignal(null);
1343
1160
  const [progressStartTime, setProgressStartTime] = createSignal(null);
1344
- const [progressTick, setProgressTick] = createSignal(0);
1161
+ const [progress, setProgress] = createSignal(0);
1345
1162
  const [grabbedBoxes, setGrabbedBoxes] = createSignal([]);
1346
1163
  const [successLabels, setSuccessLabels] = createSignal([]);
1347
1164
  const [isActivated, setIsActivated] = createSignal(false);
@@ -1356,8 +1173,6 @@ var init = (rawOptions) => {
1356
1173
  let progressDelayTimerId = null;
1357
1174
  let keydownSpamTimerId = null;
1358
1175
  let mouseSettleTimerId = null;
1359
- let referenceMouseX = OFFSCREEN_POSITION;
1360
- let referenceMouseY = OFFSCREEN_POSITION;
1361
1176
  const isRendererActive = createMemo(() => isActivated() && !isCopying());
1362
1177
  const hasValidMousePosition = createMemo(() => mouseX() > OFFSCREEN_POSITION && mouseY() > OFFSCREEN_POSITION);
1363
1178
  const isTargetKeyCombination = (event) => (event.metaKey || event.ctrlKey) && event.key.toLowerCase() === "c";
@@ -1388,55 +1203,10 @@ var init = (rawOptions) => {
1388
1203
  const wrapInSelectedElementTags = (context) => `<selected_element>
1389
1204
  ${context}
1390
1205
  </selected_element>`;
1391
- const extractRelevantComputedStyles = (element) => {
1392
- const computed = window.getComputedStyle(element);
1393
- const rect = element.getBoundingClientRect();
1394
- return {
1395
- width: `${Math.round(rect.width)}px`,
1396
- height: `${Math.round(rect.height)}px`,
1397
- paddingTop: computed.paddingTop,
1398
- paddingRight: computed.paddingRight,
1399
- paddingBottom: computed.paddingBottom,
1400
- paddingLeft: computed.paddingLeft,
1401
- background: computed.background,
1402
- opacity: computed.opacity
1403
- };
1404
- };
1405
- const createStructuredClipboardHtmlBlob = (elements) => {
1406
- const structuredData = {
1407
- elements: elements.map((element) => ({
1408
- tagName: element.tagName,
1409
- content: element.content,
1410
- computedStyles: element.computedStyles
1411
- }))
1412
- };
1413
- const jsonString = JSON.stringify(structuredData);
1414
- const base64Data = btoa(encodeURIComponent(jsonString).replace(/%([0-9A-F]{2})/g, (_match, p1) => String.fromCharCode(parseInt(p1, 16))));
1415
- const htmlContent = `<div data-react-grab="${base64Data}"></div>`;
1416
- return new Blob([htmlContent], {
1417
- type: "text/html"
1418
- });
1419
- };
1420
1206
  const extractElementTagName = (element) => (element.tagName || "").toLowerCase();
1421
- const extractNearestComponentName = (element) => {
1422
- const fiber = getFiberFromHostInstance(element);
1423
- if (!fiber) return null;
1424
- let componentName = null;
1425
- traverseFiber(fiber, (currentFiber) => {
1426
- if (isCompositeFiber(currentFiber)) {
1427
- const displayName = getDisplayName(currentFiber);
1428
- if (displayName && isCapitalized(displayName) && !displayName.startsWith("_") && !displayName.startsWith("Primitive.")) {
1429
- componentName = displayName;
1430
- return true;
1431
- }
1432
- }
1433
- return false;
1434
- }, true);
1435
- return componentName;
1436
- };
1437
1207
  const extractElementLabelText = (element) => {
1438
1208
  const tagName = extractElementTagName(element);
1439
- const componentName = extractNearestComponentName(element);
1209
+ const componentName = getNearestComponentName(element);
1440
1210
  if (tagName && componentName) {
1441
1211
  return `<${tagName}> in ${componentName}`;
1442
1212
  }
@@ -1458,8 +1228,6 @@ ${context}
1458
1228
  } catch {
1459
1229
  }
1460
1230
  };
1461
- const isExtensionEnvironment = () => window.__REACT_GRAB_EXTENSION_ACTIVE__ === true || options.isExtension === true;
1462
- const isExtensionTextOnlyMode = () => isExtensionEnvironment() && !isLocalhost();
1463
1231
  const executeCopyOperation = async (positionX, positionY, operation) => {
1464
1232
  setCopyStartX(positionX);
1465
1233
  setCopyStartY(positionY);
@@ -1485,39 +1253,37 @@ ${context}
1485
1253
  return element.textContent ?? "";
1486
1254
  };
1487
1255
  const createCombinedTextContent = (elements) => elements.map((element) => extractElementTextContent(element).trim()).filter((textContent) => textContent.length > 0).join("\n\n");
1488
- const createCombinedMarkdownContent = (elements) => elements.map((element) => elementToMarkdown(element).trim()).filter((markdownContent) => markdownContent.length > 0).join("\n\n");
1489
- const copySingleElementToClipboard = async (targetElement2) => {
1490
- showTemporaryGrabbedBox(createElementBounds(targetElement2));
1491
- await new Promise((resolve) => requestAnimationFrame(resolve));
1256
+ const tryCopyWithFallback = async (elements) => {
1492
1257
  let didCopy = false;
1493
1258
  try {
1494
- if (isExtensionTextOnlyMode()) {
1495
- const markdownContent = createCombinedMarkdownContent([targetElement2]);
1496
- if (markdownContent.length > 0) {
1497
- didCopy = await copyContent(markdownContent, options.playCopySound ? playCopySound : void 0);
1498
- }
1499
- } else {
1500
- const content = await getHTMLSnippet(targetElement2);
1501
- const plainTextContent = wrapInSelectedElementTags(content);
1502
- const htmlContent = createStructuredClipboardHtmlBlob([{
1503
- tagName: extractElementTagName(targetElement2),
1504
- content,
1505
- computedStyles: extractRelevantComputedStyles(targetElement2)
1506
- }]);
1507
- didCopy = await copyContent([plainTextContent, htmlContent], options.playCopySound ? playCopySound : void 0);
1508
- if (!didCopy) {
1509
- const plainTextContentOnly = createCombinedTextContent([targetElement2]);
1510
- if (plainTextContentOnly.length > 0) {
1511
- didCopy = await copyContent(plainTextContentOnly, options.playCopySound ? playCopySound : void 0);
1512
- }
1259
+ const elementSnippetResults = await Promise.allSettled(elements.map(async (element) => `## HTML Frame:
1260
+ ${getHTMLPreview(element)}
1261
+
1262
+ ## Code Location:
1263
+ ${formatStack(await getStack(element))}`));
1264
+ const elementSnippets = elementSnippetResults.map((result) => result.status === "fulfilled" ? result.value : "").filter((snippet) => snippet.trim());
1265
+ if (elementSnippets.length > 0) {
1266
+ const plainTextContent = elementSnippets.map((snippet) => wrapInSelectedElementTags(snippet)).join("\n\n");
1267
+ didCopy = await copyContent(plainTextContent, options.playCopySound ? playCopySound : void 0);
1268
+ }
1269
+ if (!didCopy) {
1270
+ const plainTextContentOnly = createCombinedTextContent(elements);
1271
+ if (plainTextContentOnly.length > 0) {
1272
+ didCopy = await copyContent(plainTextContentOnly, options.playCopySound ? playCopySound : void 0);
1513
1273
  }
1514
1274
  }
1515
1275
  } catch {
1516
- const plainTextContentOnly = createCombinedTextContent([targetElement2]);
1276
+ const plainTextContentOnly = createCombinedTextContent(elements);
1517
1277
  if (plainTextContentOnly.length > 0) {
1518
1278
  didCopy = await copyContent(plainTextContentOnly, options.playCopySound ? playCopySound : void 0);
1519
1279
  }
1520
1280
  }
1281
+ return didCopy;
1282
+ };
1283
+ const copySingleElementToClipboard = async (targetElement2) => {
1284
+ showTemporaryGrabbedBox(createElementBounds(targetElement2));
1285
+ await new Promise((resolve) => requestAnimationFrame(resolve));
1286
+ const didCopy = await tryCopyWithFallback([targetElement2]);
1521
1287
  if (didCopy) {
1522
1288
  showTemporarySuccessLabel(extractElementLabelText(targetElement2));
1523
1289
  }
@@ -1529,40 +1295,7 @@ ${context}
1529
1295
  showTemporaryGrabbedBox(createElementBounds(element));
1530
1296
  }
1531
1297
  await new Promise((resolve) => requestAnimationFrame(resolve));
1532
- let didCopy = false;
1533
- try {
1534
- if (isExtensionTextOnlyMode()) {
1535
- const markdownContent = createCombinedMarkdownContent(targetElements);
1536
- if (markdownContent.length > 0) {
1537
- didCopy = await copyContent(markdownContent, options.playCopySound ? playCopySound : void 0);
1538
- }
1539
- } else {
1540
- const elementSnippetResults = await Promise.allSettled(targetElements.map((element) => getHTMLSnippet(element)));
1541
- const elementSnippets = elementSnippetResults.map((result) => result.status === "fulfilled" ? result.value : "").filter((snippet) => snippet.trim());
1542
- if (elementSnippets.length > 0) {
1543
- const plainTextContent = elementSnippets.map((snippet) => wrapInSelectedElementTags(snippet)).join("\n\n");
1544
- const structuredElements = elementSnippets.map((content, elementIndex) => ({
1545
- tagName: extractElementTagName(targetElements[elementIndex]),
1546
- content,
1547
- computedStyles: extractRelevantComputedStyles(targetElements[elementIndex])
1548
- }));
1549
- const htmlContent = createStructuredClipboardHtmlBlob(structuredElements);
1550
- didCopy = await copyContent([plainTextContent, htmlContent], options.playCopySound ? playCopySound : void 0);
1551
- if (!didCopy) {
1552
- const plainTextContentOnly = createCombinedTextContent(targetElements);
1553
- if (plainTextContentOnly.length > 0) {
1554
- didCopy = await copyContent(plainTextContentOnly, options.playCopySound ? playCopySound : void 0);
1555
- }
1556
- }
1557
- } else {
1558
- const plainTextContentOnly = createCombinedTextContent(targetElements);
1559
- if (plainTextContentOnly.length > 0) {
1560
- didCopy = await copyContent(plainTextContentOnly, options.playCopySound ? playCopySound : void 0);
1561
- }
1562
- }
1563
- }
1564
- } catch {
1565
- }
1298
+ const didCopy = await tryCopyWithFallback(targetElements);
1566
1299
  if (didCopy) {
1567
1300
  showTemporarySuccessLabel(`${targetElements.length} elements`);
1568
1301
  }
@@ -1586,7 +1319,6 @@ ${context}
1586
1319
  y: elementBounds.top
1587
1320
  };
1588
1321
  });
1589
- const DRAG_THRESHOLD_PX = 2;
1590
1322
  const calculateDragDistance = (endX, endY) => ({
1591
1323
  x: Math.abs(endX - dragStartX()),
1592
1324
  y: Math.abs(endY - dragStartY())
@@ -1638,36 +1370,28 @@ ${context}
1638
1370
  x: mouseX(),
1639
1371
  y: mouseY()
1640
1372
  });
1641
- const isSameAsLast = createMemo(() => Boolean(targetElement() && targetElement() === lastGrabbedElement()));
1642
1373
  createEffect(on(() => [targetElement(), lastGrabbedElement()], ([currentElement, lastElement]) => {
1643
1374
  if (lastElement && currentElement && lastElement !== currentElement) {
1644
1375
  setLastGrabbedElement(null);
1645
1376
  }
1646
1377
  }));
1647
- const progress = createMemo(() => {
1648
- const startTime = progressStartTime();
1649
- progressTick();
1650
- if (startTime === null) return 0;
1651
- const elapsedTime = Date.now() - startTime;
1652
- const normalizedTime = elapsedTime / options.keyHoldDuration;
1653
- const easedProgress = 1 - Math.exp(-normalizedTime);
1654
- const maxProgressBeforeCompletion = 0.95;
1655
- if (isCopying()) {
1656
- return Math.min(easedProgress, maxProgressBeforeCompletion);
1657
- }
1658
- return 1;
1659
- });
1660
1378
  const startProgressAnimation = () => {
1661
- setProgressStartTime(Date.now());
1379
+ const startTime = Date.now();
1380
+ setProgressStartTime(startTime);
1662
1381
  setShowProgressIndicator(false);
1663
1382
  progressDelayTimerId = window.setTimeout(() => {
1664
1383
  setShowProgressIndicator(true);
1665
1384
  progressDelayTimerId = null;
1666
1385
  }, PROGRESS_INDICATOR_DELAY_MS);
1667
1386
  const animateProgress = () => {
1668
- if (progressStartTime() === null) return;
1669
- setProgressTick((tick) => tick + 1);
1670
- const currentProgress = progress();
1387
+ const currentStartTime = progressStartTime();
1388
+ if (currentStartTime === null) return;
1389
+ const elapsedTime = Date.now() - currentStartTime;
1390
+ const normalizedTime = elapsedTime / options.keyHoldDuration;
1391
+ const easedProgress = 1 - Math.exp(-normalizedTime);
1392
+ const maxProgressBeforeCompletion = 0.95;
1393
+ const currentProgress = isCopying() ? Math.min(easedProgress, maxProgressBeforeCompletion) : 1;
1394
+ setProgress(currentProgress);
1671
1395
  if (currentProgress < 1) {
1672
1396
  progressAnimationId = requestAnimationFrame(animateProgress);
1673
1397
  }
@@ -1684,6 +1408,7 @@ ${context}
1684
1408
  progressDelayTimerId = null;
1685
1409
  }
1686
1410
  setProgressStartTime(null);
1411
+ setProgress(1);
1687
1412
  setShowProgressIndicator(false);
1688
1413
  };
1689
1414
  const activateRenderer = () => {
@@ -1707,8 +1432,6 @@ ${context}
1707
1432
  mouseSettleTimerId = null;
1708
1433
  }
1709
1434
  setMouseHasSettled(false);
1710
- referenceMouseX = OFFSCREEN_POSITION;
1711
- referenceMouseY = OFFSCREEN_POSITION;
1712
1435
  stopProgressAnimation();
1713
1436
  };
1714
1437
  const abortController = new AbortController();
@@ -1775,34 +1498,14 @@ ${context}
1775
1498
  window.addEventListener("mousemove", (event) => {
1776
1499
  setMouseX(event.clientX);
1777
1500
  setMouseY(event.clientY);
1778
- if (referenceMouseX === OFFSCREEN_POSITION) {
1779
- referenceMouseX = event.clientX;
1780
- referenceMouseY = event.clientY;
1781
- }
1782
- const deltaX = event.clientX - referenceMouseX;
1783
- const deltaY = event.clientY - referenceMouseY;
1784
- const distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
1785
- if (distance >= 200) {
1786
- if (mouseSettleTimerId !== null) {
1787
- window.clearTimeout(mouseSettleTimerId);
1788
- }
1789
- setMouseHasSettled(false);
1790
- referenceMouseX = event.clientX;
1791
- referenceMouseY = event.clientY;
1792
- mouseSettleTimerId = window.setTimeout(() => {
1793
- setMouseHasSettled(true);
1794
- referenceMouseX = mouseX();
1795
- referenceMouseY = mouseY();
1796
- mouseSettleTimerId = null;
1797
- }, 100);
1798
- } else if (mouseSettleTimerId === null && !mouseHasSettled()) {
1799
- mouseSettleTimerId = window.setTimeout(() => {
1800
- setMouseHasSettled(true);
1801
- referenceMouseX = mouseX();
1802
- referenceMouseY = mouseY();
1803
- mouseSettleTimerId = null;
1804
- }, 100);
1501
+ if (mouseSettleTimerId !== null) {
1502
+ window.clearTimeout(mouseSettleTimerId);
1805
1503
  }
1504
+ setMouseHasSettled(false);
1505
+ mouseSettleTimerId = window.setTimeout(() => {
1506
+ setMouseHasSettled(true);
1507
+ mouseSettleTimerId = null;
1508
+ }, 300);
1806
1509
  }, {
1807
1510
  signal: eventListenerSignal
1808
1511
  });
@@ -1886,7 +1589,7 @@ ${context}
1886
1589
  const labelVisible = createMemo(() => {
1887
1590
  if (isCopying()) return true;
1888
1591
  if (successLabels().length > 0) return false;
1889
- return isRendererActive() && !isDragging() && mouseHasSettled() && (Boolean(targetElement()) && !isSameAsLast() || !targetElement());
1592
+ return isRendererActive() && !isDragging() && Boolean(targetElement());
1890
1593
  });
1891
1594
  const progressVisible = createMemo(() => isCopying() && showProgressIndicator() && hasValidMousePosition());
1892
1595
  const crosshairVisible = createMemo(() => isRendererActive() && !isDragging());
@@ -1924,7 +1627,10 @@ ${context}
1924
1627
  get labelVisible() {
1925
1628
  return labelVisible();
1926
1629
  },
1927
- labelZIndex: 2147483646,
1630
+ labelZIndex: Z_INDEX_LABEL,
1631
+ get labelShowHint() {
1632
+ return mouseHasSettled();
1633
+ },
1928
1634
  get progressVisible() {
1929
1635
  return progressVisible();
1930
1636
  },
@@ -1970,9 +1676,6 @@ ${context}
1970
1676
  // src/index.ts
1971
1677
  var globalApi = null;
1972
1678
  var getGlobalApi = () => globalApi;
1973
- var EXTENSION_MARKER = "__REACT_GRAB_EXTENSION_ACTIVE__";
1974
- if (!window[EXTENSION_MARKER]) {
1975
- globalApi = init();
1976
- }
1679
+ globalApi = init();
1977
1680
 
1978
- export { elementToMarkdown, getGlobalApi, htmlToMarkdown, init, playCopySound };
1681
+ export { getGlobalApi, init, playCopySound };