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