afterbefore 0.2.3 → 0.2.5

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.
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
 
3
3
  // src/overlay/index.tsx
4
- import { useState as useState6, useCallback as useCallback7, useRef as useRef6 } from "react";
4
+ import { useState as useState6, useCallback as useCallback6, useRef as useRef5, useEffect as useEffect5 } from "react";
5
5
 
6
6
  // src/overlay/state.ts
7
7
  import { useState, useCallback } from "react";
@@ -33,33 +33,65 @@ function useOverlayState() {
33
33
  }
34
34
 
35
35
  // src/overlay/capture.ts
36
- import { toPng } from "html-to-image";
36
+ import { domToPng } from "modern-screenshot";
37
+ var DEV_UI_SELECTORS = [
38
+ // Afterbefore overlay
39
+ "[data-afterbefore]",
40
+ // Next.js dev indicators
41
+ "[data-nextjs-toast]",
42
+ "[data-nextjs-dev-overlay]",
43
+ "[data-nextjs-dialog]",
44
+ "[data-nextjs-dialog-backdrop]",
45
+ "[data-next-badge]",
46
+ "[data-next-mark]"
47
+ ].join(",");
48
+ function filterDevUI(node) {
49
+ if (node instanceof HTMLElement) {
50
+ return !node.matches(DEV_UI_SELECTORS);
51
+ }
52
+ return true;
53
+ }
54
+ function hideDevUI() {
55
+ const elements = document.querySelectorAll(DEV_UI_SELECTORS);
56
+ const saved = /* @__PURE__ */ new Map();
57
+ elements.forEach((el) => {
58
+ saved.set(el, el.style.display);
59
+ el.style.display = "none";
60
+ });
61
+ return () => {
62
+ saved.forEach((v, el) => {
63
+ el.style.display = v;
64
+ });
65
+ };
66
+ }
37
67
  async function capture(options) {
38
68
  const { mode, area, element } = options;
39
- if (mode === "viewport") {
40
- return captureViewport();
41
- }
42
- if (mode === "fullpage") {
43
- return captureFullPage();
44
- }
45
- if (mode === "area" && area) {
46
- return captureArea(area);
47
- }
48
- if (mode === "component" && element) {
49
- return captureComponent(element);
69
+ const restore = hideDevUI();
70
+ try {
71
+ if (mode === "viewport") {
72
+ return await captureViewport();
73
+ }
74
+ if (mode === "fullpage") {
75
+ return await captureFullPage();
76
+ }
77
+ if (mode === "area" && area) {
78
+ return await captureArea(area);
79
+ }
80
+ if (mode === "component" && element) {
81
+ return await captureComponent(element);
82
+ }
83
+ throw new Error(`Invalid capture mode: ${mode}`);
84
+ } finally {
85
+ restore();
50
86
  }
51
- throw new Error(`Invalid capture mode: ${mode}`);
52
87
  }
53
88
  async function captureViewport() {
54
- const dataUrl = await toPng(document.documentElement, {
89
+ return domToPng(document.documentElement, {
55
90
  width: window.innerWidth,
56
91
  height: window.innerHeight,
57
- style: {
58
- overflow: "hidden"
59
- },
60
- filter: filterOverlay
92
+ style: { overflow: "hidden" },
93
+ filter: filterDevUI
61
94
  });
62
- return dataUrl;
63
95
  }
64
96
  async function captureFullPage() {
65
97
  const scrollY = window.scrollY;
@@ -72,14 +104,14 @@ async function captureFullPage() {
72
104
  html.scrollHeight,
73
105
  html.offsetHeight
74
106
  );
75
- const dataUrl = await toPng(document.documentElement, {
107
+ const dataUrl = await domToPng(document.documentElement, {
76
108
  width: window.innerWidth,
77
109
  height: fullHeight,
78
110
  style: {
79
111
  overflow: "visible",
80
112
  height: `${fullHeight}px`
81
113
  },
82
- filter: filterOverlay
114
+ filter: filterDevUI
83
115
  });
84
116
  window.scrollTo(0, scrollY);
85
117
  return dataUrl;
@@ -106,10 +138,9 @@ async function captureArea(area) {
106
138
  return canvas.toDataURL("image/png");
107
139
  }
108
140
  async function captureComponent(element) {
109
- const dataUrl = await toPng(element, {
110
- filter: filterOverlay
141
+ return domToPng(element, {
142
+ filter: filterDevUI
111
143
  });
112
- return dataUrl;
113
144
  }
114
145
  function loadImage(src) {
115
146
  return new Promise((resolve, reject) => {
@@ -119,9 +150,6 @@ function loadImage(src) {
119
150
  img.src = src;
120
151
  });
121
152
  }
122
- function filterOverlay(node) {
123
- return !node.dataset?.afterbefore;
124
- }
125
153
 
126
154
  // src/overlay/ui/icon.tsx
127
155
  import { useRef, useCallback as useCallback2, useEffect, useState as useState2 } from "react";
@@ -340,327 +368,14 @@ function Icon({ phase, onClick, loading, onPositionChange }) {
340
368
  );
341
369
  }
342
370
 
343
- // src/overlay/ui/menu.tsx
344
- import { useEffect as useEffect2, useRef as useRef2, useCallback as useCallback3 } from "react";
345
- import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
346
- var modes = [
347
- {
348
- mode: "viewport",
349
- label: "Viewport",
350
- icon: /* @__PURE__ */ jsxs2("svg", { width: "16", height: "16", viewBox: "0 0 16 16", children: [
351
- /* @__PURE__ */ jsx2(
352
- "rect",
353
- {
354
- x: "1",
355
- y: "2",
356
- width: "14",
357
- height: "11",
358
- rx: "1.5",
359
- fill: "none",
360
- stroke: "currentColor",
361
- strokeWidth: "1.5"
362
- }
363
- ),
364
- /* @__PURE__ */ jsx2(
365
- "line",
366
- {
367
- x1: "1",
368
- y1: "14",
369
- x2: "15",
370
- y2: "14",
371
- stroke: "currentColor",
372
- strokeWidth: "1.5",
373
- strokeLinecap: "round"
374
- }
375
- )
376
- ] })
377
- },
378
- {
379
- mode: "fullpage",
380
- label: "Full Page",
381
- icon: /* @__PURE__ */ jsxs2("svg", { width: "16", height: "16", viewBox: "0 0 16 16", children: [
382
- /* @__PURE__ */ jsx2(
383
- "rect",
384
- {
385
- x: "3",
386
- y: "1",
387
- width: "10",
388
- height: "14",
389
- rx: "1.5",
390
- fill: "none",
391
- stroke: "currentColor",
392
- strokeWidth: "1.5"
393
- }
394
- ),
395
- /* @__PURE__ */ jsx2(
396
- "line",
397
- {
398
- x1: "5.5",
399
- y1: "4",
400
- x2: "10.5",
401
- y2: "4",
402
- stroke: "currentColor",
403
- strokeWidth: "1",
404
- strokeLinecap: "round"
405
- }
406
- ),
407
- /* @__PURE__ */ jsx2(
408
- "line",
409
- {
410
- x1: "5.5",
411
- y1: "6.5",
412
- x2: "10.5",
413
- y2: "6.5",
414
- stroke: "currentColor",
415
- strokeWidth: "1",
416
- strokeLinecap: "round"
417
- }
418
- ),
419
- /* @__PURE__ */ jsx2(
420
- "line",
421
- {
422
- x1: "5.5",
423
- y1: "9",
424
- x2: "10.5",
425
- y2: "9",
426
- stroke: "currentColor",
427
- strokeWidth: "1",
428
- strokeLinecap: "round"
429
- }
430
- )
431
- ] })
432
- },
433
- {
434
- mode: "area",
435
- label: "Select Area",
436
- icon: /* @__PURE__ */ jsxs2("svg", { width: "16", height: "16", viewBox: "0 0 16 16", children: [
437
- /* @__PURE__ */ jsx2(
438
- "path",
439
- {
440
- d: "M1 5V2.5A1.5 1.5 0 012.5 1H5",
441
- fill: "none",
442
- stroke: "currentColor",
443
- strokeWidth: "1.5",
444
- strokeLinecap: "round"
445
- }
446
- ),
447
- /* @__PURE__ */ jsx2(
448
- "path",
449
- {
450
- d: "M11 1h2.5A1.5 1.5 0 0115 2.5V5",
451
- fill: "none",
452
- stroke: "currentColor",
453
- strokeWidth: "1.5",
454
- strokeLinecap: "round"
455
- }
456
- ),
457
- /* @__PURE__ */ jsx2(
458
- "path",
459
- {
460
- d: "M15 11v2.5a1.5 1.5 0 01-1.5 1.5H11",
461
- fill: "none",
462
- stroke: "currentColor",
463
- strokeWidth: "1.5",
464
- strokeLinecap: "round"
465
- }
466
- ),
467
- /* @__PURE__ */ jsx2(
468
- "path",
469
- {
470
- d: "M5 15H2.5A1.5 1.5 0 011 13.5V11",
471
- fill: "none",
472
- stroke: "currentColor",
473
- strokeWidth: "1.5",
474
- strokeLinecap: "round"
475
- }
476
- )
477
- ] })
478
- },
479
- {
480
- mode: "component",
481
- label: "Component",
482
- icon: /* @__PURE__ */ jsxs2("svg", { width: "16", height: "16", viewBox: "0 0 16 16", children: [
483
- /* @__PURE__ */ jsx2(
484
- "path",
485
- {
486
- d: "M3 2l2.5 10 2-3.5L11 11 3 2z",
487
- fill: "none",
488
- stroke: "currentColor",
489
- strokeWidth: "1.3",
490
- strokeLinejoin: "round",
491
- strokeLinecap: "round"
492
- }
493
- ),
494
- /* @__PURE__ */ jsx2(
495
- "rect",
496
- {
497
- x: "8",
498
- y: "5",
499
- width: "6.5",
500
- height: "6.5",
501
- rx: "1",
502
- fill: "none",
503
- stroke: "currentColor",
504
- strokeWidth: "1.2",
505
- strokeDasharray: "2 1.5"
506
- }
507
- )
508
- ] })
509
- }
510
- ];
511
- var PILL_WIDTH = 220;
512
- var BTN_SIZE = 36;
513
- function Menu({ onSelect, onClose, position }) {
514
- const menuRef = useRef2(null);
515
- const handleClickOutside = useCallback3(
516
- (e) => {
517
- if (menuRef.current && !menuRef.current.contains(e.target)) {
518
- onClose();
519
- }
520
- },
521
- [onClose]
522
- );
523
- const handleKeyDown = useCallback3(
524
- (e) => {
525
- if (e.key === "Escape") {
526
- onClose();
527
- }
528
- },
529
- [onClose]
530
- );
531
- useEffect2(() => {
532
- document.addEventListener("mousedown", handleClickOutside);
533
- document.addEventListener("keydown", handleKeyDown);
534
- return () => {
535
- document.removeEventListener("mousedown", handleClickOutside);
536
- document.removeEventListener("keydown", handleKeyDown);
537
- };
538
- }, [handleClickOutside, handleKeyDown]);
539
- const menuLeft = Math.max(
540
- 8,
541
- Math.min(position.x - PILL_WIDTH / 2 + 20, window.innerWidth - PILL_WIDTH - 8)
542
- );
543
- const menuBottom = window.innerHeight - position.y + 8;
544
- return /* @__PURE__ */ jsxs2(
545
- "div",
546
- {
547
- ref: menuRef,
548
- "data-afterbefore": "true",
549
- style: {
550
- position: "fixed",
551
- left: menuLeft,
552
- bottom: menuBottom,
553
- display: "flex",
554
- alignItems: "center",
555
- gap: 2,
556
- background: "rgba(24, 24, 27, 0.95)",
557
- borderRadius: 22,
558
- padding: "4px 4px",
559
- boxShadow: "0 4px 20px rgba(0,0,0,0.4), 0 0 0 1px rgba(255,255,255,0.08)",
560
- zIndex: 2147483647,
561
- fontFamily: "system-ui, -apple-system, sans-serif",
562
- backdropFilter: "blur(12px)"
563
- },
564
- children: [
565
- modes.map(({ mode, label, icon }) => /* @__PURE__ */ jsx2(
566
- "button",
567
- {
568
- title: label,
569
- onClick: () => onSelect(mode),
570
- style: {
571
- display: "flex",
572
- alignItems: "center",
573
- justifyContent: "center",
574
- width: BTN_SIZE,
575
- height: BTN_SIZE,
576
- border: "none",
577
- background: "transparent",
578
- color: "rgba(255,255,255,0.7)",
579
- borderRadius: "50%",
580
- cursor: "pointer",
581
- padding: 0,
582
- transition: "background 0.1s, color 0.1s",
583
- flexShrink: 0
584
- },
585
- onMouseEnter: (e) => {
586
- const btn = e.currentTarget;
587
- btn.style.background = "rgba(255,255,255,0.12)";
588
- btn.style.color = "rgba(255,255,255,1)";
589
- },
590
- onMouseLeave: (e) => {
591
- const btn = e.currentTarget;
592
- btn.style.background = "transparent";
593
- btn.style.color = "rgba(255,255,255,0.7)";
594
- },
595
- children: icon
596
- },
597
- mode
598
- )),
599
- /* @__PURE__ */ jsx2(
600
- "div",
601
- {
602
- style: {
603
- width: 1,
604
- height: 20,
605
- background: "rgba(255,255,255,0.12)",
606
- flexShrink: 0,
607
- margin: "0 2px"
608
- }
609
- }
610
- ),
611
- /* @__PURE__ */ jsx2(
612
- "button",
613
- {
614
- title: "Close",
615
- onClick: onClose,
616
- style: {
617
- display: "flex",
618
- alignItems: "center",
619
- justifyContent: "center",
620
- width: BTN_SIZE,
621
- height: BTN_SIZE,
622
- border: "none",
623
- background: "transparent",
624
- color: "rgba(255,255,255,0.45)",
625
- borderRadius: "50%",
626
- cursor: "pointer",
627
- padding: 0,
628
- transition: "background 0.1s, color 0.1s",
629
- flexShrink: 0
630
- },
631
- onMouseEnter: (e) => {
632
- const btn = e.currentTarget;
633
- btn.style.background = "rgba(255,255,255,0.12)";
634
- btn.style.color = "rgba(255,255,255,0.9)";
635
- },
636
- onMouseLeave: (e) => {
637
- const btn = e.currentTarget;
638
- btn.style.background = "transparent";
639
- btn.style.color = "rgba(255,255,255,0.45)";
640
- },
641
- children: /* @__PURE__ */ jsx2("svg", { width: "14", height: "14", viewBox: "0 0 14 14", children: /* @__PURE__ */ jsx2(
642
- "path",
643
- {
644
- d: "M3 3l8 8M11 3l-8 8",
645
- stroke: "currentColor",
646
- strokeWidth: "1.5",
647
- strokeLinecap: "round"
648
- }
649
- ) })
650
- }
651
- )
652
- ]
653
- }
654
- );
655
- }
656
-
657
371
  // src/overlay/ui/selector.tsx
658
- import React3, { useState as useState3, useRef as useRef3, useCallback as useCallback4, useEffect as useEffect3 } from "react";
659
- import { Fragment, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
372
+ import React2, { useState as useState3, useRef as useRef2, useCallback as useCallback3, useEffect as useEffect2 } from "react";
373
+ import { Fragment, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
660
374
  var HANDLE_R = 6;
661
375
  var HANDLE_HIT = 16;
662
376
  var MIN_SIZE = 20;
663
- var PANEL_HEIGHT_EST = 200;
377
+ var PANEL_HEIGHT_EST = 140;
378
+ var SNAP_THRESHOLD = 8;
664
379
  var ASPECT_RATIOS = [
665
380
  { label: "Free", value: 0 },
666
381
  { label: "16:9", value: 16 / 9 },
@@ -669,7 +384,7 @@ var ASPECT_RATIOS = [
669
384
  { label: "3:2", value: 3 / 2 },
670
385
  { label: "21:9", value: 21 / 9 }
671
386
  ];
672
- function Selector({ onSelect, onCancel }) {
387
+ function Selector({ onSelect, onCancel, onViewport, onFullPage, onComponent }) {
673
388
  const [rect, setRect] = useState3(null);
674
389
  const [placed, setPlaced] = useState3(false);
675
390
  const [aspect, setAspect] = useState3("Free");
@@ -686,13 +401,39 @@ function Selector({ onSelect, onCancel }) {
686
401
  }
687
402
  );
688
403
  const [cursor, setCursor] = useState3("crosshair");
689
- const mode = useRef3("none");
690
- const start = useRef3({ x: 0, y: 0 });
691
- const snap = useRef3({ x: 0, y: 0, w: 0, h: 0 });
692
- const corner = useRef3("br");
693
- const panelRef = useRef3(null);
404
+ const [magnet, setMagnet] = useState3(() => {
405
+ try {
406
+ return localStorage.getItem("ab-magnet") !== "false";
407
+ } catch {
408
+ return true;
409
+ }
410
+ });
411
+ const [snappedX, setSnappedX] = useState3(false);
412
+ const mode = useRef2("none");
413
+ const start = useRef2({ x: 0, y: 0 });
414
+ const snapRef = useRef2({ x: 0, y: 0, w: 0, h: 0 });
415
+ const corner = useRef2("br");
416
+ const panelRef = useRef2(null);
694
417
  const ratio = ASPECT_RATIOS.find((a) => a.label === aspect)?.value ?? 0;
695
- useEffect3(() => {
418
+ const applySnap = useCallback3(
419
+ (r) => {
420
+ if (!magnet) {
421
+ setSnappedX(false);
422
+ return r;
423
+ }
424
+ const vw = window.innerWidth;
425
+ const centerX = r.x + r.w / 2;
426
+ const viewCenterX = vw / 2;
427
+ if (Math.abs(centerX - viewCenterX) < SNAP_THRESHOLD) {
428
+ setSnappedX(true);
429
+ return { ...r, x: viewCenterX - r.w / 2 };
430
+ }
431
+ setSnappedX(false);
432
+ return r;
433
+ },
434
+ [magnet]
435
+ );
436
+ useEffect2(() => {
696
437
  const onKey = (e) => {
697
438
  if (e.target?.tagName === "INPUT") {
698
439
  if (e.key === "Escape") e.target.blur();
@@ -717,7 +458,7 @@ function Selector({ onSelect, onCancel }) {
717
458
  document.addEventListener("keydown", onKey);
718
459
  return () => document.removeEventListener("keydown", onKey);
719
460
  }, [placed, rect, onSelect, onCancel]);
720
- const hitCorner = useCallback4(
461
+ const hitCorner = useCallback3(
721
462
  (mx, my, r) => {
722
463
  const cs = [
723
464
  ["tl", r.x, r.y],
@@ -733,11 +474,11 @@ function Selector({ onSelect, onCancel }) {
733
474
  },
734
475
  []
735
476
  );
736
- const hitInside = useCallback4(
477
+ const hitInside = useCallback3(
737
478
  (mx, my, r) => mx >= r.x && mx <= r.x + r.w && my >= r.y && my <= r.y + r.h,
738
479
  []
739
480
  );
740
- const onDown = useCallback4(
481
+ const onDown = useCallback3(
741
482
  (e) => {
742
483
  if (panelRef.current?.contains(e.target)) return;
743
484
  e.preventDefault();
@@ -750,13 +491,13 @@ function Selector({ onSelect, onCancel }) {
750
491
  mode.current = "resizing";
751
492
  corner.current = c;
752
493
  start.current = { x: mx, y: my };
753
- snap.current = { ...rect };
494
+ snapRef.current = { ...rect };
754
495
  return;
755
496
  }
756
497
  if (hitInside(mx, my, rect)) {
757
498
  mode.current = "moving";
758
499
  start.current = { x: mx, y: my };
759
- snap.current = { ...rect };
500
+ snapRef.current = { ...rect };
760
501
  return;
761
502
  }
762
503
  setPlaced(false);
@@ -767,7 +508,7 @@ function Selector({ onSelect, onCancel }) {
767
508
  },
768
509
  [placed, rect, hitCorner, hitInside]
769
510
  );
770
- const onMove = useCallback4(
511
+ const onMove = useCallback3(
771
512
  (e) => {
772
513
  const mx = e.clientX, my = e.clientY;
773
514
  if (mode.current === "drawing") {
@@ -778,16 +519,18 @@ function Selector({ onSelect, onCancel }) {
778
519
  h = w / ratio;
779
520
  if (my < sy) y = sy - h;
780
521
  }
781
- setRect({ x, y, w, h });
522
+ setRect(applySnap({ x, y, w, h }));
782
523
  } else if (mode.current === "moving") {
783
524
  const dx = mx - start.current.x, dy = my - start.current.y;
784
- setRect({
785
- ...snap.current,
786
- x: snap.current.x + dx,
787
- y: snap.current.y + dy
788
- });
525
+ setRect(
526
+ applySnap({
527
+ ...snapRef.current,
528
+ x: snapRef.current.x + dx,
529
+ y: snapRef.current.y + dy
530
+ })
531
+ );
789
532
  } else if (mode.current === "resizing") {
790
- const o = snap.current;
533
+ const o = snapRef.current;
791
534
  const c = corner.current;
792
535
  const nr = { ...o };
793
536
  if (c === "br") {
@@ -807,7 +550,7 @@ function Selector({ onSelect, onCancel }) {
807
550
  nr.x = o.x + o.w - nr.w;
808
551
  nr.y = o.y + o.h - nr.h;
809
552
  }
810
- setRect(nr);
553
+ setRect(applySnap(nr));
811
554
  } else if (placed && rect) {
812
555
  const c = hitCorner(mx, my, rect);
813
556
  if (c === "tl" || c === "br") setCursor("nwse-resize");
@@ -816,19 +559,35 @@ function Selector({ onSelect, onCancel }) {
816
559
  else setCursor("crosshair");
817
560
  }
818
561
  },
819
- [ratio, placed, rect, hitCorner, hitInside]
562
+ [ratio, placed, rect, hitCorner, hitInside, applySnap]
820
563
  );
821
- const onUp = useCallback4(() => {
822
- if (mode.current === "drawing" && rect) {
823
- if (rect.w >= MIN_SIZE && rect.h >= MIN_SIZE) {
824
- setPlaced(true);
825
- } else {
826
- setRect(null);
564
+ const onUp = useCallback3(
565
+ (e) => {
566
+ const prevMode = mode.current;
567
+ if (prevMode === "drawing" && rect) {
568
+ if (rect.w >= MIN_SIZE && rect.h >= MIN_SIZE) {
569
+ setPlaced(true);
570
+ } else {
571
+ setRect(null);
572
+ }
827
573
  }
828
- }
829
- mode.current = "none";
830
- }, [rect]);
831
- const setSize = useCallback4(
574
+ if (prevMode === "moving" && placed && rect) {
575
+ const dx = Math.abs(e.clientX - start.current.x);
576
+ const dy = Math.abs(e.clientY - start.current.y);
577
+ if (dx < 3 && dy < 3) {
578
+ onSelect({
579
+ x: Math.round(rect.x),
580
+ y: Math.round(rect.y),
581
+ width: Math.round(rect.w),
582
+ height: Math.round(rect.h)
583
+ });
584
+ }
585
+ }
586
+ mode.current = "none";
587
+ },
588
+ [rect, placed, onSelect]
589
+ );
590
+ const setSize = useCallback3(
832
591
  (field, v) => {
833
592
  const n = parseInt(v, 10);
834
593
  if (isNaN(n) || n < MIN_SIZE || !rect) return;
@@ -840,7 +599,7 @@ function Selector({ onSelect, onCancel }) {
840
599
  },
841
600
  [rect, ratio]
842
601
  );
843
- const setPos = useCallback4(
602
+ const setPos = useCallback3(
844
603
  (field, v) => {
845
604
  const n = parseInt(v, 10);
846
605
  if (isNaN(n) || !rect) return;
@@ -848,7 +607,7 @@ function Selector({ onSelect, onCancel }) {
848
607
  },
849
608
  [rect]
850
609
  );
851
- const savePreset = useCallback4(() => {
610
+ const savePreset = useCallback3(() => {
852
611
  if (!rect) return;
853
612
  const label = `${Math.round(rect.w)}\xD7${Math.round(rect.h)}`;
854
613
  const next = [
@@ -862,7 +621,7 @@ function Selector({ onSelect, onCancel }) {
862
621
  }
863
622
  setSavedOpen(false);
864
623
  }, [rect, presets]);
865
- const loadPreset = useCallback4((p) => {
624
+ const loadPreset = useCallback3((p) => {
866
625
  setRect({ ...p.rect });
867
626
  setPlaced(true);
868
627
  setSavedOpen(false);
@@ -870,7 +629,7 @@ function Selector({ onSelect, onCancel }) {
870
629
  const activeCursor = mode.current === "moving" ? "move" : mode.current === "resizing" ? "nwse-resize" : mode.current === "drawing" ? "crosshair" : cursor;
871
630
  const panelAbove = rect && rect.y + rect.h + PANEL_HEIGHT_EST > window.innerHeight;
872
631
  const hasRect = rect && rect.w > 0 && rect.h > 0;
873
- return /* @__PURE__ */ jsxs3(
632
+ return /* @__PURE__ */ jsxs2(
874
633
  "div",
875
634
  {
876
635
  "data-afterbefore": "true",
@@ -884,7 +643,22 @@ function Selector({ onSelect, onCancel }) {
884
643
  cursor: activeCursor
885
644
  },
886
645
  children: [
887
- /* @__PURE__ */ jsx3(
646
+ snappedX && /* @__PURE__ */ jsx2(
647
+ "div",
648
+ {
649
+ style: {
650
+ position: "absolute",
651
+ left: "50%",
652
+ top: 0,
653
+ bottom: 0,
654
+ width: 0,
655
+ borderLeft: "1px solid rgba(147, 130, 220, 0.5)",
656
+ pointerEvents: "none",
657
+ zIndex: 2
658
+ }
659
+ }
660
+ ),
661
+ /* @__PURE__ */ jsx2(
888
662
  "div",
889
663
  {
890
664
  style: {
@@ -905,8 +679,8 @@ function Selector({ onSelect, onCancel }) {
905
679
  }
906
680
  }
907
681
  ),
908
- hasRect && /* @__PURE__ */ jsxs3(Fragment, { children: [
909
- /* @__PURE__ */ jsx3(
682
+ hasRect && /* @__PURE__ */ jsxs2(Fragment, { children: [
683
+ /* @__PURE__ */ jsx2(
910
684
  "div",
911
685
  {
912
686
  style: {
@@ -920,8 +694,8 @@ function Selector({ onSelect, onCancel }) {
920
694
  }
921
695
  }
922
696
  ),
923
- placed && [1, 2].map((i) => /* @__PURE__ */ jsxs3(React3.Fragment, { children: [
924
- /* @__PURE__ */ jsx3(
697
+ placed && [1, 2].map((i) => /* @__PURE__ */ jsxs2(React2.Fragment, { children: [
698
+ /* @__PURE__ */ jsx2(
925
699
  "div",
926
700
  {
927
701
  style: {
@@ -935,7 +709,7 @@ function Selector({ onSelect, onCancel }) {
935
709
  }
936
710
  }
937
711
  ),
938
- /* @__PURE__ */ jsx3(
712
+ /* @__PURE__ */ jsx2(
939
713
  "div",
940
714
  {
941
715
  style: {
@@ -955,7 +729,7 @@ function Selector({ onSelect, onCancel }) {
955
729
  [rect.x + rect.w, rect.y],
956
730
  [rect.x, rect.y + rect.h],
957
731
  [rect.x + rect.w, rect.y + rect.h]
958
- ].map(([cx, cy], i) => /* @__PURE__ */ jsx3(
732
+ ].map(([cx, cy], i) => /* @__PURE__ */ jsx2(
959
733
  "div",
960
734
  {
961
735
  style: {
@@ -972,7 +746,7 @@ function Selector({ onSelect, onCancel }) {
972
746
  },
973
747
  i
974
748
  )),
975
- placed && /* @__PURE__ */ jsxs3(
749
+ placed && /* @__PURE__ */ jsxs2(
976
750
  "div",
977
751
  {
978
752
  ref: panelRef,
@@ -986,71 +760,71 @@ function Selector({ onSelect, onCancel }) {
986
760
  backdropFilter: "blur(20px)",
987
761
  WebkitBackdropFilter: "blur(20px)",
988
762
  border: "1px solid rgba(255, 255, 255, 0.1)",
989
- borderRadius: 14,
990
- padding: "16px 24px",
763
+ borderRadius: 10,
764
+ padding: "10px 14px",
991
765
  display: "flex",
992
766
  flexDirection: "column",
993
- gap: 12,
994
- minWidth: 340,
767
+ gap: 6,
768
+ minWidth: 0,
995
769
  fontFamily: "system-ui, -apple-system, sans-serif",
996
770
  color: "rgba(255, 255, 255, 0.9)",
997
771
  boxShadow: "0 8px 32px rgba(0, 0, 0, 0.4)",
998
772
  zIndex: 1
999
773
  },
1000
774
  children: [
1001
- /* @__PURE__ */ jsxs3(Row, { label: "Size", children: [
1002
- /* @__PURE__ */ jsx3(
775
+ /* @__PURE__ */ jsxs2(Row, { label: "Size", children: [
776
+ /* @__PURE__ */ jsx2(
1003
777
  NumInput,
1004
778
  {
1005
779
  value: Math.round(rect.w),
1006
780
  onChange: (v) => setSize("w", v)
1007
781
  }
1008
782
  ),
1009
- /* @__PURE__ */ jsx3(Sep, { children: "\\u00d7" }),
1010
- /* @__PURE__ */ jsx3(
783
+ /* @__PURE__ */ jsx2(Sep, { children: "\xD7" }),
784
+ /* @__PURE__ */ jsx2(
1011
785
  NumInput,
1012
786
  {
1013
787
  value: Math.round(rect.h),
1014
788
  onChange: (v) => setSize("h", v)
1015
789
  }
1016
790
  ),
1017
- /* @__PURE__ */ jsx3(Unit, { children: "px" })
791
+ /* @__PURE__ */ jsx2(Unit, { children: "px" })
1018
792
  ] }),
1019
- /* @__PURE__ */ jsxs3(Row, { label: "Position", children: [
1020
- /* @__PURE__ */ jsx3(
793
+ /* @__PURE__ */ jsxs2(Row, { label: "Position", children: [
794
+ /* @__PURE__ */ jsx2(
1021
795
  NumInput,
1022
796
  {
1023
797
  value: Math.round(rect.x),
1024
798
  onChange: (v) => setPos("x", v)
1025
799
  }
1026
800
  ),
1027
- /* @__PURE__ */ jsx3(Sep, {}),
1028
- /* @__PURE__ */ jsx3(
801
+ /* @__PURE__ */ jsx2(Sep, {}),
802
+ /* @__PURE__ */ jsx2(
1029
803
  NumInput,
1030
804
  {
1031
805
  value: Math.round(rect.y),
1032
806
  onChange: (v) => setPos("y", v)
1033
807
  }
1034
808
  ),
1035
- /* @__PURE__ */ jsx3(Unit, { children: "px" })
809
+ /* @__PURE__ */ jsx2(Unit, { children: "px" })
1036
810
  ] }),
1037
- /* @__PURE__ */ jsx3(
811
+ /* @__PURE__ */ jsx2(
1038
812
  "div",
1039
813
  {
1040
814
  style: {
1041
815
  height: 1,
1042
816
  background: "rgba(255, 255, 255, 0.08)",
1043
- margin: "2px 0"
817
+ margin: "1px 0"
1044
818
  }
1045
819
  }
1046
820
  ),
1047
- /* @__PURE__ */ jsxs3(
821
+ /* @__PURE__ */ jsxs2(
1048
822
  "div",
1049
823
  {
1050
- style: { display: "flex", alignItems: "center", gap: 20 },
824
+ style: { display: "flex", alignItems: "center", gap: 12 },
1051
825
  children: [
1052
- /* @__PURE__ */ jsxs3("div", { style: { position: "relative" }, children: [
1053
- /* @__PURE__ */ jsxs3(
826
+ /* @__PURE__ */ jsxs2("div", { style: { position: "relative" }, children: [
827
+ /* @__PURE__ */ jsxs2(
1054
828
  DropBtn,
1055
829
  {
1056
830
  active: aspect !== "Free",
@@ -1059,13 +833,13 @@ function Selector({ onSelect, onCancel }) {
1059
833
  setSavedOpen(false);
1060
834
  },
1061
835
  children: [
1062
- /* @__PURE__ */ jsx3(AspectIcon, {}),
836
+ /* @__PURE__ */ jsx2(AspectIcon, {}),
1063
837
  aspect === "Free" ? "Free" : aspect,
1064
- /* @__PURE__ */ jsx3(Chevron, {})
838
+ /* @__PURE__ */ jsx2(Chevron, {})
1065
839
  ]
1066
840
  }
1067
841
  ),
1068
- aspectOpen && /* @__PURE__ */ jsx3(DropMenu, { children: ASPECT_RATIOS.map((ar) => /* @__PURE__ */ jsx3(
842
+ aspectOpen && /* @__PURE__ */ jsx2(DropMenu, { children: ASPECT_RATIOS.map((ar) => /* @__PURE__ */ jsx2(
1069
843
  DropItem,
1070
844
  {
1071
845
  active: ar.label === aspect,
@@ -1081,8 +855,8 @@ function Selector({ onSelect, onCancel }) {
1081
855
  ar.label
1082
856
  )) })
1083
857
  ] }),
1084
- /* @__PURE__ */ jsxs3("div", { style: { position: "relative" }, children: [
1085
- /* @__PURE__ */ jsxs3(
858
+ /* @__PURE__ */ jsxs2("div", { style: { position: "relative" }, children: [
859
+ /* @__PURE__ */ jsxs2(
1086
860
  DropBtn,
1087
861
  {
1088
862
  onClick: () => {
@@ -1090,14 +864,14 @@ function Selector({ onSelect, onCancel }) {
1090
864
  setAspectOpen(false);
1091
865
  },
1092
866
  children: [
1093
- /* @__PURE__ */ jsx3(SavedIcon, {}),
867
+ /* @__PURE__ */ jsx2(SavedIcon, {}),
1094
868
  "Saved",
1095
- /* @__PURE__ */ jsx3(Chevron, {})
869
+ /* @__PURE__ */ jsx2(Chevron, {})
1096
870
  ]
1097
871
  }
1098
872
  ),
1099
- savedOpen && /* @__PURE__ */ jsxs3(DropMenu, { children: [
1100
- /* @__PURE__ */ jsx3(
873
+ savedOpen && /* @__PURE__ */ jsxs2(DropMenu, { children: [
874
+ /* @__PURE__ */ jsx2(
1101
875
  DropItem,
1102
876
  {
1103
877
  accent: true,
@@ -1105,7 +879,7 @@ function Selector({ onSelect, onCancel }) {
1105
879
  children: "Save current"
1106
880
  }
1107
881
  ),
1108
- presets.length > 0 && /* @__PURE__ */ jsx3(
882
+ presets.length > 0 && /* @__PURE__ */ jsx2(
1109
883
  "div",
1110
884
  {
1111
885
  style: {
@@ -1115,7 +889,7 @@ function Selector({ onSelect, onCancel }) {
1115
889
  }
1116
890
  }
1117
891
  ),
1118
- presets.map((p) => /* @__PURE__ */ jsx3(
892
+ presets.map((p) => /* @__PURE__ */ jsx2(
1119
893
  DropItem,
1120
894
  {
1121
895
  onClick: () => loadPreset(p),
@@ -1123,7 +897,7 @@ function Selector({ onSelect, onCancel }) {
1123
897
  },
1124
898
  p.label
1125
899
  )),
1126
- presets.length === 0 && /* @__PURE__ */ jsx3(
900
+ presets.length === 0 && /* @__PURE__ */ jsx2(
1127
901
  "div",
1128
902
  {
1129
903
  style: {
@@ -1139,21 +913,77 @@ function Selector({ onSelect, onCancel }) {
1139
913
  ]
1140
914
  }
1141
915
  ),
1142
- /* @__PURE__ */ jsx3(
916
+ (onViewport || onFullPage || onComponent) && /* @__PURE__ */ jsxs2(Fragment, { children: [
917
+ /* @__PURE__ */ jsx2(
918
+ "div",
919
+ {
920
+ style: {
921
+ height: 1,
922
+ background: "rgba(255, 255, 255, 0.08)",
923
+ margin: "1px 0"
924
+ }
925
+ }
926
+ ),
927
+ /* @__PURE__ */ jsxs2(
928
+ "div",
929
+ {
930
+ style: { display: "flex", alignItems: "center", gap: 4 },
931
+ children: [
932
+ onViewport && /* @__PURE__ */ jsxs2(ModeBtn, { onClick: onViewport, children: [
933
+ /* @__PURE__ */ jsx2(ViewportIcon, {}),
934
+ "Viewport"
935
+ ] }),
936
+ onFullPage && /* @__PURE__ */ jsxs2(ModeBtn, { onClick: onFullPage, children: [
937
+ /* @__PURE__ */ jsx2(FullPageIcon, {}),
938
+ "Full Page"
939
+ ] }),
940
+ onComponent && /* @__PURE__ */ jsxs2(ModeBtn, { onClick: onComponent, children: [
941
+ /* @__PURE__ */ jsx2(ComponentIcon, {}),
942
+ "Component"
943
+ ] })
944
+ ]
945
+ }
946
+ )
947
+ ] }),
948
+ /* @__PURE__ */ jsx2(
949
+ "div",
950
+ {
951
+ style: {
952
+ display: "flex",
953
+ alignItems: "center",
954
+ gap: 6
955
+ },
956
+ children: /* @__PURE__ */ jsx2(
957
+ MagnetToggle,
958
+ {
959
+ enabled: magnet,
960
+ onToggle: () => {
961
+ const next = !magnet;
962
+ setMagnet(next);
963
+ try {
964
+ localStorage.setItem("ab-magnet", String(next));
965
+ } catch {
966
+ }
967
+ }
968
+ }
969
+ )
970
+ }
971
+ ),
972
+ /* @__PURE__ */ jsx2(
1143
973
  "div",
1144
974
  {
1145
975
  style: {
1146
- fontSize: 11,
976
+ fontSize: 10,
1147
977
  color: "rgba(255, 255, 255, 0.25)",
1148
978
  textAlign: "center"
1149
979
  },
1150
- children: "Enter to capture \xB7 Esc to cancel"
980
+ children: "Click area or Enter to capture \xB7 Esc to cancel"
1151
981
  }
1152
982
  )
1153
983
  ]
1154
984
  }
1155
985
  ),
1156
- !placed && /* @__PURE__ */ jsxs3(
986
+ !placed && /* @__PURE__ */ jsxs2(
1157
987
  "div",
1158
988
  {
1159
989
  style: {
@@ -1178,7 +1008,7 @@ function Selector({ onSelect, onCancel }) {
1178
1008
  }
1179
1009
  )
1180
1010
  ] }),
1181
- !rect && /* @__PURE__ */ jsx3(
1011
+ !rect && /* @__PURE__ */ jsxs2(
1182
1012
  "div",
1183
1013
  {
1184
1014
  style: {
@@ -1186,13 +1016,57 @@ function Selector({ onSelect, onCancel }) {
1186
1016
  top: "50%",
1187
1017
  left: "50%",
1188
1018
  transform: "translate(-50%, -50%)",
1189
- color: "rgba(255, 255, 255, 0.7)",
1190
- fontSize: 14,
1191
- fontFamily: "system-ui, -apple-system, sans-serif",
1192
- pointerEvents: "none",
1193
- textShadow: "0 1px 4px rgba(0, 0, 0, 0.5)"
1019
+ display: "flex",
1020
+ flexDirection: "column",
1021
+ alignItems: "center",
1022
+ gap: 16,
1023
+ fontFamily: "system-ui, -apple-system, sans-serif"
1194
1024
  },
1195
- children: "Drag to select an area \xB7 Esc to cancel"
1025
+ children: [
1026
+ /* @__PURE__ */ jsx2(
1027
+ "div",
1028
+ {
1029
+ style: {
1030
+ color: "rgba(255, 255, 255, 0.7)",
1031
+ fontSize: 14,
1032
+ pointerEvents: "none",
1033
+ textShadow: "0 1px 4px rgba(0, 0, 0, 0.5)"
1034
+ },
1035
+ children: "Drag to select an area \xB7 Esc to cancel"
1036
+ }
1037
+ ),
1038
+ (onViewport || onFullPage || onComponent) && /* @__PURE__ */ jsxs2(
1039
+ "div",
1040
+ {
1041
+ onMouseDown: (e) => e.stopPropagation(),
1042
+ style: {
1043
+ display: "flex",
1044
+ alignItems: "center",
1045
+ gap: 4,
1046
+ background: "rgba(32, 32, 36, 0.92)",
1047
+ backdropFilter: "blur(20px)",
1048
+ WebkitBackdropFilter: "blur(20px)",
1049
+ border: "1px solid rgba(255, 255, 255, 0.1)",
1050
+ borderRadius: 8,
1051
+ padding: "6px 8px"
1052
+ },
1053
+ children: [
1054
+ onViewport && /* @__PURE__ */ jsxs2(ModeBtn, { onClick: onViewport, children: [
1055
+ /* @__PURE__ */ jsx2(ViewportIcon, {}),
1056
+ "Viewport"
1057
+ ] }),
1058
+ onFullPage && /* @__PURE__ */ jsxs2(ModeBtn, { onClick: onFullPage, children: [
1059
+ /* @__PURE__ */ jsx2(FullPageIcon, {}),
1060
+ "Full Page"
1061
+ ] }),
1062
+ onComponent && /* @__PURE__ */ jsxs2(ModeBtn, { onClick: onComponent, children: [
1063
+ /* @__PURE__ */ jsx2(ComponentIcon, {}),
1064
+ "Component"
1065
+ ] })
1066
+ ]
1067
+ }
1068
+ )
1069
+ ]
1196
1070
  }
1197
1071
  )
1198
1072
  ]
@@ -1203,14 +1077,14 @@ function Row({
1203
1077
  label,
1204
1078
  children
1205
1079
  }) {
1206
- return /* @__PURE__ */ jsxs3("div", { style: { display: "flex", alignItems: "center", gap: 12 }, children: [
1207
- /* @__PURE__ */ jsx3(
1080
+ return /* @__PURE__ */ jsxs2("div", { style: { display: "flex", alignItems: "center", gap: 6 }, children: [
1081
+ /* @__PURE__ */ jsx2(
1208
1082
  "span",
1209
1083
  {
1210
1084
  style: {
1211
- width: 64,
1212
- fontSize: 13,
1213
- color: "rgba(255, 255, 255, 0.45)",
1085
+ width: 36,
1086
+ fontSize: 11,
1087
+ color: "rgba(255, 255, 255, 0.4)",
1214
1088
  fontWeight: 400,
1215
1089
  flexShrink: 0
1216
1090
  },
@@ -1221,13 +1095,13 @@ function Row({
1221
1095
  ] });
1222
1096
  }
1223
1097
  function Sep({ children }) {
1224
- return /* @__PURE__ */ jsx3(
1098
+ return /* @__PURE__ */ jsx2(
1225
1099
  "span",
1226
1100
  {
1227
1101
  style: {
1228
- fontSize: 13,
1102
+ fontSize: 11,
1229
1103
  color: "rgba(255, 255, 255, 0.35)",
1230
- width: 14,
1104
+ width: 12,
1231
1105
  textAlign: "center",
1232
1106
  flexShrink: 0,
1233
1107
  visibility: children ? "visible" : "hidden"
@@ -1237,11 +1111,11 @@ function Sep({ children }) {
1237
1111
  );
1238
1112
  }
1239
1113
  function Unit({ children }) {
1240
- return /* @__PURE__ */ jsx3(
1114
+ return /* @__PURE__ */ jsx2(
1241
1115
  "span",
1242
1116
  {
1243
1117
  style: {
1244
- fontSize: 12,
1118
+ fontSize: 10,
1245
1119
  color: "rgba(255, 255, 255, 0.3)",
1246
1120
  flexShrink: 0
1247
1121
  },
@@ -1255,10 +1129,10 @@ function NumInput({
1255
1129
  }) {
1256
1130
  const [editing, setEditing] = useState3(false);
1257
1131
  const [text, setText] = useState3(String(value));
1258
- useEffect3(() => {
1132
+ useEffect2(() => {
1259
1133
  if (!editing) setText(String(value));
1260
1134
  }, [value, editing]);
1261
- return /* @__PURE__ */ jsx3(
1135
+ return /* @__PURE__ */ jsx2(
1262
1136
  "input",
1263
1137
  {
1264
1138
  type: "text",
@@ -1276,13 +1150,13 @@ function NumInput({
1276
1150
  if (e.key === "Enter") e.target.blur();
1277
1151
  },
1278
1152
  style: {
1279
- width: 72,
1280
- padding: "6px 10px",
1153
+ width: 52,
1154
+ padding: "3px 6px",
1281
1155
  background: "rgba(255, 255, 255, 0.07)",
1282
1156
  border: "1px solid rgba(255, 255, 255, 0.1)",
1283
- borderRadius: 6,
1157
+ borderRadius: 4,
1284
1158
  color: "rgba(255, 255, 255, 0.9)",
1285
- fontSize: 14,
1159
+ fontSize: 12,
1286
1160
  fontFamily: "system-ui, -apple-system, sans-serif",
1287
1161
  textAlign: "center",
1288
1162
  outline: "none"
@@ -1295,28 +1169,28 @@ function DropBtn({
1295
1169
  onClick,
1296
1170
  active
1297
1171
  }) {
1298
- return /* @__PURE__ */ jsx3(
1172
+ return /* @__PURE__ */ jsx2(
1299
1173
  "button",
1300
1174
  {
1301
1175
  onClick,
1302
1176
  style: {
1303
1177
  display: "flex",
1304
1178
  alignItems: "center",
1305
- gap: 6,
1179
+ gap: 4,
1306
1180
  background: "none",
1307
1181
  border: "none",
1308
1182
  color: active ? "rgba(147, 130, 220, 0.9)" : "rgba(255, 255, 255, 0.5)",
1309
1183
  cursor: "pointer",
1310
- fontSize: 13,
1184
+ fontSize: 11,
1311
1185
  fontFamily: "inherit",
1312
- padding: "4px 2px"
1186
+ padding: "2px 0"
1313
1187
  },
1314
1188
  children
1315
1189
  }
1316
1190
  );
1317
1191
  }
1318
1192
  function DropMenu({ children }) {
1319
- return /* @__PURE__ */ jsx3(
1193
+ return /* @__PURE__ */ jsx2(
1320
1194
  "div",
1321
1195
  {
1322
1196
  style: {
@@ -1343,7 +1217,7 @@ function DropItem({
1343
1217
  active,
1344
1218
  accent
1345
1219
  }) {
1346
- return /* @__PURE__ */ jsx3(
1220
+ return /* @__PURE__ */ jsx2(
1347
1221
  "button",
1348
1222
  {
1349
1223
  onClick,
@@ -1364,7 +1238,7 @@ function DropItem({
1364
1238
  );
1365
1239
  }
1366
1240
  function Chevron() {
1367
- return /* @__PURE__ */ jsx3("svg", { width: "10", height: "10", viewBox: "0 0 10 10", fill: "none", children: /* @__PURE__ */ jsx3(
1241
+ return /* @__PURE__ */ jsx2("svg", { width: "10", height: "10", viewBox: "0 0 10 10", fill: "none", children: /* @__PURE__ */ jsx2(
1368
1242
  "path",
1369
1243
  {
1370
1244
  d: "M3 4l2 2.5L7 4",
@@ -1374,8 +1248,8 @@ function Chevron() {
1374
1248
  ) });
1375
1249
  }
1376
1250
  function AspectIcon() {
1377
- return /* @__PURE__ */ jsxs3("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", children: [
1378
- /* @__PURE__ */ jsx3(
1251
+ return /* @__PURE__ */ jsxs2("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", children: [
1252
+ /* @__PURE__ */ jsx2(
1379
1253
  "rect",
1380
1254
  {
1381
1255
  x: "2",
@@ -1388,7 +1262,7 @@ function AspectIcon() {
1388
1262
  strokeDasharray: "2 1.5"
1389
1263
  }
1390
1264
  ),
1391
- /* @__PURE__ */ jsx3(
1265
+ /* @__PURE__ */ jsx2(
1392
1266
  "line",
1393
1267
  {
1394
1268
  x1: "6.33",
@@ -1400,7 +1274,7 @@ function AspectIcon() {
1400
1274
  strokeDasharray: "1.5 1"
1401
1275
  }
1402
1276
  ),
1403
- /* @__PURE__ */ jsx3(
1277
+ /* @__PURE__ */ jsx2(
1404
1278
  "line",
1405
1279
  {
1406
1280
  x1: "9.67",
@@ -1415,7 +1289,7 @@ function AspectIcon() {
1415
1289
  ] });
1416
1290
  }
1417
1291
  function SavedIcon() {
1418
- return /* @__PURE__ */ jsx3("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", children: /* @__PURE__ */ jsx3(
1292
+ return /* @__PURE__ */ jsx2("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", children: /* @__PURE__ */ jsx2(
1419
1293
  "rect",
1420
1294
  {
1421
1295
  x: "2",
@@ -1429,15 +1303,200 @@ function SavedIcon() {
1429
1303
  }
1430
1304
  ) });
1431
1305
  }
1306
+ function ModeBtn({
1307
+ children,
1308
+ onClick
1309
+ }) {
1310
+ return /* @__PURE__ */ jsx2(
1311
+ "button",
1312
+ {
1313
+ onClick,
1314
+ style: {
1315
+ display: "flex",
1316
+ alignItems: "center",
1317
+ gap: 5,
1318
+ background: "rgba(255, 255, 255, 0.06)",
1319
+ border: "1px solid rgba(255, 255, 255, 0.08)",
1320
+ borderRadius: 6,
1321
+ color: "rgba(255, 255, 255, 0.55)",
1322
+ cursor: "pointer",
1323
+ fontSize: 11,
1324
+ fontFamily: "inherit",
1325
+ padding: "4px 8px",
1326
+ transition: "background 0.1s, color 0.1s"
1327
+ },
1328
+ onMouseEnter: (e) => {
1329
+ const btn = e.currentTarget;
1330
+ btn.style.background = "rgba(255, 255, 255, 0.12)";
1331
+ btn.style.color = "rgba(255, 255, 255, 0.85)";
1332
+ },
1333
+ onMouseLeave: (e) => {
1334
+ const btn = e.currentTarget;
1335
+ btn.style.background = "rgba(255, 255, 255, 0.06)";
1336
+ btn.style.color = "rgba(255, 255, 255, 0.55)";
1337
+ },
1338
+ children
1339
+ }
1340
+ );
1341
+ }
1342
+ function MagnetToggle({
1343
+ enabled,
1344
+ onToggle
1345
+ }) {
1346
+ return /* @__PURE__ */ jsxs2(
1347
+ "button",
1348
+ {
1349
+ onClick: onToggle,
1350
+ style: {
1351
+ display: "flex",
1352
+ alignItems: "center",
1353
+ gap: 5,
1354
+ background: "none",
1355
+ border: "none",
1356
+ color: enabled ? "rgba(147, 130, 220, 0.9)" : "rgba(255, 255, 255, 0.35)",
1357
+ cursor: "pointer",
1358
+ fontSize: 11,
1359
+ fontFamily: "inherit",
1360
+ padding: "2px 0"
1361
+ },
1362
+ children: [
1363
+ /* @__PURE__ */ jsx2(MagnetIcon, {}),
1364
+ "Magnet"
1365
+ ]
1366
+ }
1367
+ );
1368
+ }
1369
+ function MagnetIcon() {
1370
+ return /* @__PURE__ */ jsxs2("svg", { width: "14", height: "14", viewBox: "0 0 16 16", fill: "none", children: [
1371
+ /* @__PURE__ */ jsx2(
1372
+ "path",
1373
+ {
1374
+ d: "M4 2v4a4 4 0 008 0V2",
1375
+ stroke: "currentColor",
1376
+ strokeWidth: "1.3",
1377
+ strokeLinecap: "round"
1378
+ }
1379
+ ),
1380
+ /* @__PURE__ */ jsx2("line", { x1: "4", y1: "2", x2: "4", y2: "5", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round" }),
1381
+ /* @__PURE__ */ jsx2("line", { x1: "12", y1: "2", x2: "12", y2: "5", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round" })
1382
+ ] });
1383
+ }
1384
+ function ViewportIcon() {
1385
+ return /* @__PURE__ */ jsxs2("svg", { width: "14", height: "14", viewBox: "0 0 16 16", fill: "none", children: [
1386
+ /* @__PURE__ */ jsx2(
1387
+ "rect",
1388
+ {
1389
+ x: "1",
1390
+ y: "2",
1391
+ width: "14",
1392
+ height: "11",
1393
+ rx: "1.5",
1394
+ stroke: "currentColor",
1395
+ strokeWidth: "1.3"
1396
+ }
1397
+ ),
1398
+ /* @__PURE__ */ jsx2(
1399
+ "line",
1400
+ {
1401
+ x1: "1",
1402
+ y1: "14",
1403
+ x2: "15",
1404
+ y2: "14",
1405
+ stroke: "currentColor",
1406
+ strokeWidth: "1.3",
1407
+ strokeLinecap: "round"
1408
+ }
1409
+ )
1410
+ ] });
1411
+ }
1412
+ function FullPageIcon() {
1413
+ return /* @__PURE__ */ jsxs2("svg", { width: "14", height: "14", viewBox: "0 0 16 16", fill: "none", children: [
1414
+ /* @__PURE__ */ jsx2(
1415
+ "rect",
1416
+ {
1417
+ x: "3",
1418
+ y: "1",
1419
+ width: "10",
1420
+ height: "14",
1421
+ rx: "1.5",
1422
+ stroke: "currentColor",
1423
+ strokeWidth: "1.3"
1424
+ }
1425
+ ),
1426
+ /* @__PURE__ */ jsx2(
1427
+ "line",
1428
+ {
1429
+ x1: "5.5",
1430
+ y1: "4",
1431
+ x2: "10.5",
1432
+ y2: "4",
1433
+ stroke: "currentColor",
1434
+ strokeWidth: "1",
1435
+ strokeLinecap: "round"
1436
+ }
1437
+ ),
1438
+ /* @__PURE__ */ jsx2(
1439
+ "line",
1440
+ {
1441
+ x1: "5.5",
1442
+ y1: "6.5",
1443
+ x2: "10.5",
1444
+ y2: "6.5",
1445
+ stroke: "currentColor",
1446
+ strokeWidth: "1",
1447
+ strokeLinecap: "round"
1448
+ }
1449
+ ),
1450
+ /* @__PURE__ */ jsx2(
1451
+ "line",
1452
+ {
1453
+ x1: "5.5",
1454
+ y1: "9",
1455
+ x2: "10.5",
1456
+ y2: "9",
1457
+ stroke: "currentColor",
1458
+ strokeWidth: "1",
1459
+ strokeLinecap: "round"
1460
+ }
1461
+ )
1462
+ ] });
1463
+ }
1464
+ function ComponentIcon() {
1465
+ return /* @__PURE__ */ jsxs2("svg", { width: "14", height: "14", viewBox: "0 0 16 16", fill: "none", children: [
1466
+ /* @__PURE__ */ jsx2(
1467
+ "path",
1468
+ {
1469
+ d: "M3 2l2.5 10 2-3.5L11 11 3 2z",
1470
+ stroke: "currentColor",
1471
+ strokeWidth: "1.2",
1472
+ strokeLinejoin: "round",
1473
+ strokeLinecap: "round"
1474
+ }
1475
+ ),
1476
+ /* @__PURE__ */ jsx2(
1477
+ "rect",
1478
+ {
1479
+ x: "8",
1480
+ y: "5",
1481
+ width: "6.5",
1482
+ height: "6.5",
1483
+ rx: "1",
1484
+ stroke: "currentColor",
1485
+ strokeWidth: "1.1",
1486
+ strokeDasharray: "2 1.5"
1487
+ }
1488
+ )
1489
+ ] });
1490
+ }
1432
1491
 
1433
1492
  // src/overlay/ui/inspector.tsx
1434
- import { useEffect as useEffect4, useRef as useRef4, useCallback as useCallback5, useState as useState4 } from "react";
1435
- import { Fragment as Fragment2, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
1493
+ import { useEffect as useEffect3, useRef as useRef3, useCallback as useCallback4, useState as useState4 } from "react";
1494
+ import { Fragment as Fragment2, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
1436
1495
  function Inspector({ onSelect, onCancel }) {
1437
1496
  const [highlight, setHighlight] = useState4(null);
1438
- const hoveredEl = useRef4(null);
1439
- const styleEl = useRef4(null);
1440
- useEffect4(() => {
1497
+ const hoveredEl = useRef3(null);
1498
+ const styleEl = useRef3(null);
1499
+ useEffect3(() => {
1441
1500
  const style = document.createElement("style");
1442
1501
  style.setAttribute("data-afterbefore", "true");
1443
1502
  style.textContent = "*, *::before, *::after { cursor: crosshair !important; }";
@@ -1447,7 +1506,7 @@ function Inspector({ onSelect, onCancel }) {
1447
1506
  style.remove();
1448
1507
  };
1449
1508
  }, []);
1450
- const isOverlayElement = useCallback5((el) => {
1509
+ const isOverlayElement = useCallback4((el) => {
1451
1510
  let node = el;
1452
1511
  while (node) {
1453
1512
  if (node instanceof HTMLElement && node.dataset.afterbefore) return true;
@@ -1455,7 +1514,7 @@ function Inspector({ onSelect, onCancel }) {
1455
1514
  }
1456
1515
  return false;
1457
1516
  }, []);
1458
- const handleMouseMove = useCallback5(
1517
+ const handleMouseMove = useCallback4(
1459
1518
  (e) => {
1460
1519
  const el = document.elementFromPoint(e.clientX, e.clientY);
1461
1520
  if (!el || !(el instanceof HTMLElement) || isOverlayElement(el)) {
@@ -1475,7 +1534,7 @@ function Inspector({ onSelect, onCancel }) {
1475
1534
  },
1476
1535
  [isOverlayElement]
1477
1536
  );
1478
- const handleClick = useCallback5(
1537
+ const handleClick = useCallback4(
1479
1538
  (e) => {
1480
1539
  e.preventDefault();
1481
1540
  e.stopPropagation();
@@ -1486,7 +1545,7 @@ function Inspector({ onSelect, onCancel }) {
1486
1545
  },
1487
1546
  [onSelect]
1488
1547
  );
1489
- const handleKeyDown = useCallback5(
1548
+ const handleKeyDown = useCallback4(
1490
1549
  (e) => {
1491
1550
  if (e.key === "Escape") {
1492
1551
  onCancel();
@@ -1494,7 +1553,7 @@ function Inspector({ onSelect, onCancel }) {
1494
1553
  },
1495
1554
  [onCancel]
1496
1555
  );
1497
- useEffect4(() => {
1556
+ useEffect3(() => {
1498
1557
  document.addEventListener("mousemove", handleMouseMove, true);
1499
1558
  document.addEventListener("click", handleClick, true);
1500
1559
  document.addEventListener("keydown", handleKeyDown);
@@ -1504,9 +1563,9 @@ function Inspector({ onSelect, onCancel }) {
1504
1563
  document.removeEventListener("keydown", handleKeyDown);
1505
1564
  };
1506
1565
  }, [handleMouseMove, handleClick, handleKeyDown]);
1507
- return /* @__PURE__ */ jsxs4("div", { "data-afterbefore": "true", style: { position: "fixed", inset: 0, zIndex: 2147483646, pointerEvents: "none" }, children: [
1508
- highlight && /* @__PURE__ */ jsxs4(Fragment2, { children: [
1509
- /* @__PURE__ */ jsx4(
1566
+ return /* @__PURE__ */ jsxs3("div", { "data-afterbefore": "true", style: { position: "fixed", inset: 0, zIndex: 2147483646, pointerEvents: "none" }, children: [
1567
+ highlight && /* @__PURE__ */ jsxs3(Fragment2, { children: [
1568
+ /* @__PURE__ */ jsx3(
1510
1569
  "div",
1511
1570
  {
1512
1571
  style: {
@@ -1522,7 +1581,7 @@ function Inspector({ onSelect, onCancel }) {
1522
1581
  }
1523
1582
  }
1524
1583
  ),
1525
- /* @__PURE__ */ jsx4(
1584
+ /* @__PURE__ */ jsx3(
1526
1585
  "div",
1527
1586
  {
1528
1587
  style: {
@@ -1543,7 +1602,7 @@ function Inspector({ onSelect, onCancel }) {
1543
1602
  }
1544
1603
  )
1545
1604
  ] }),
1546
- !highlight && /* @__PURE__ */ jsx4(
1605
+ !highlight && /* @__PURE__ */ jsx3(
1547
1606
  "div",
1548
1607
  {
1549
1608
  style: {
@@ -1567,18 +1626,18 @@ function Inspector({ onSelect, onCancel }) {
1567
1626
  }
1568
1627
 
1569
1628
  // src/overlay/ui/status.tsx
1570
- import { useState as useState5, useRef as useRef5, useEffect as useEffect5, useCallback as useCallback6 } from "react";
1571
- import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
1629
+ import { useState as useState5, useRef as useRef4, useEffect as useEffect4, useCallback as useCallback5 } from "react";
1630
+ import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
1572
1631
  var PANEL_WIDTH = 220;
1573
1632
  function Status({ onReset, position, onClose }) {
1574
- const panelRef = useRef5(null);
1633
+ const panelRef = useRef4(null);
1575
1634
  const [toast, setToast] = useState5(null);
1576
1635
  const [pushing, setPushing] = useState5(false);
1577
- const showToast = useCallback6((message, type) => {
1636
+ const showToast = useCallback5((message, type) => {
1578
1637
  setToast({ message, type });
1579
1638
  setTimeout(() => setToast(null), 3e3);
1580
1639
  }, []);
1581
- useEffect5(() => {
1640
+ useEffect4(() => {
1582
1641
  const handler = (e) => {
1583
1642
  if (panelRef.current && !panelRef.current.contains(e.target)) {
1584
1643
  onClose();
@@ -1587,7 +1646,7 @@ function Status({ onReset, position, onClose }) {
1587
1646
  document.addEventListener("mousedown", handler);
1588
1647
  return () => document.removeEventListener("mousedown", handler);
1589
1648
  }, [onClose]);
1590
- useEffect5(() => {
1649
+ useEffect4(() => {
1591
1650
  const handler = (e) => {
1592
1651
  if (e.key === "Escape") onClose();
1593
1652
  };
@@ -1663,7 +1722,7 @@ function Status({ onReset, position, onClose }) {
1663
1722
  const onLeave = (e) => {
1664
1723
  e.currentTarget.style.background = "transparent";
1665
1724
  };
1666
- return /* @__PURE__ */ jsxs5(
1725
+ return /* @__PURE__ */ jsxs4(
1667
1726
  "div",
1668
1727
  {
1669
1728
  ref: panelRef,
@@ -1682,7 +1741,7 @@ function Status({ onReset, position, onClose }) {
1682
1741
  overflow: "hidden"
1683
1742
  },
1684
1743
  children: [
1685
- /* @__PURE__ */ jsx5(
1744
+ /* @__PURE__ */ jsx4(
1686
1745
  "div",
1687
1746
  {
1688
1747
  style: {
@@ -1695,8 +1754,8 @@ function Status({ onReset, position, onClose }) {
1695
1754
  children: "Before & After captured"
1696
1755
  }
1697
1756
  ),
1698
- /* @__PURE__ */ jsxs5("div", { style: { padding: "0 4px 4px" }, children: [
1699
- /* @__PURE__ */ jsxs5(
1757
+ /* @__PURE__ */ jsxs4("div", { style: { padding: "0 4px 4px" }, children: [
1758
+ /* @__PURE__ */ jsxs4(
1700
1759
  "button",
1701
1760
  {
1702
1761
  style: buttonStyle,
@@ -1704,12 +1763,12 @@ function Status({ onReset, position, onClose }) {
1704
1763
  onMouseEnter: onEnter,
1705
1764
  onMouseLeave: onLeave,
1706
1765
  children: [
1707
- /* @__PURE__ */ jsx5(FolderIcon, {}),
1766
+ /* @__PURE__ */ jsx4(FolderIcon, {}),
1708
1767
  "Open Folder"
1709
1768
  ]
1710
1769
  }
1711
1770
  ),
1712
- /* @__PURE__ */ jsxs5(
1771
+ /* @__PURE__ */ jsxs4(
1713
1772
  "button",
1714
1773
  {
1715
1774
  style: buttonStyle,
@@ -1717,12 +1776,12 @@ function Status({ onReset, position, onClose }) {
1717
1776
  onMouseEnter: onEnter,
1718
1777
  onMouseLeave: onLeave,
1719
1778
  children: [
1720
- /* @__PURE__ */ jsx5(CopyIcon, {}),
1779
+ /* @__PURE__ */ jsx4(CopyIcon, {}),
1721
1780
  "Copy Markdown"
1722
1781
  ]
1723
1782
  }
1724
1783
  ),
1725
- /* @__PURE__ */ jsxs5(
1784
+ /* @__PURE__ */ jsxs4(
1726
1785
  "button",
1727
1786
  {
1728
1787
  style: buttonStyle,
@@ -1731,12 +1790,12 @@ function Status({ onReset, position, onClose }) {
1731
1790
  onMouseEnter: onEnter,
1732
1791
  onMouseLeave: onLeave,
1733
1792
  children: [
1734
- /* @__PURE__ */ jsx5(PushIcon, {}),
1793
+ /* @__PURE__ */ jsx4(PushIcon, {}),
1735
1794
  pushing ? "Pushing..." : "Push to PR"
1736
1795
  ]
1737
1796
  }
1738
1797
  ),
1739
- /* @__PURE__ */ jsx5(
1798
+ /* @__PURE__ */ jsx4(
1740
1799
  "div",
1741
1800
  {
1742
1801
  style: {
@@ -1746,7 +1805,7 @@ function Status({ onReset, position, onClose }) {
1746
1805
  }
1747
1806
  }
1748
1807
  ),
1749
- /* @__PURE__ */ jsxs5(
1808
+ /* @__PURE__ */ jsxs4(
1750
1809
  "button",
1751
1810
  {
1752
1811
  style: { ...buttonStyle, color: "rgba(255,255,255,0.5)" },
@@ -1754,13 +1813,13 @@ function Status({ onReset, position, onClose }) {
1754
1813
  onMouseEnter: onEnter,
1755
1814
  onMouseLeave: onLeave,
1756
1815
  children: [
1757
- /* @__PURE__ */ jsx5(ResetIcon, {}),
1816
+ /* @__PURE__ */ jsx4(ResetIcon, {}),
1758
1817
  "Reset"
1759
1818
  ]
1760
1819
  }
1761
1820
  )
1762
1821
  ] }),
1763
- toast && /* @__PURE__ */ jsx5(
1822
+ toast && /* @__PURE__ */ jsx4(
1764
1823
  "div",
1765
1824
  {
1766
1825
  style: {
@@ -1786,14 +1845,14 @@ function Status({ onReset, position, onClose }) {
1786
1845
  );
1787
1846
  }
1788
1847
  function FolderIcon() {
1789
- return /* @__PURE__ */ jsx5(
1848
+ return /* @__PURE__ */ jsx4(
1790
1849
  "svg",
1791
1850
  {
1792
1851
  width: "14",
1793
1852
  height: "14",
1794
1853
  viewBox: "0 0 14 14",
1795
1854
  style: { color: "rgba(255,255,255,0.5)" },
1796
- children: /* @__PURE__ */ jsx5(
1855
+ children: /* @__PURE__ */ jsx4(
1797
1856
  "path",
1798
1857
  {
1799
1858
  d: "M1.5 3A1.5 1.5 0 013 1.5h2.38a1 1 0 01.72.3L7 2.72a1 1 0 00.72.3H11A1.5 1.5 0 0112.5 4.5v6A1.5 1.5 0 0111 12H3A1.5 1.5 0 011.5 10.5V3z",
@@ -1806,7 +1865,7 @@ function FolderIcon() {
1806
1865
  );
1807
1866
  }
1808
1867
  function CopyIcon() {
1809
- return /* @__PURE__ */ jsxs5(
1868
+ return /* @__PURE__ */ jsxs4(
1810
1869
  "svg",
1811
1870
  {
1812
1871
  width: "14",
@@ -1814,7 +1873,7 @@ function CopyIcon() {
1814
1873
  viewBox: "0 0 14 14",
1815
1874
  style: { color: "rgba(255,255,255,0.5)" },
1816
1875
  children: [
1817
- /* @__PURE__ */ jsx5(
1876
+ /* @__PURE__ */ jsx4(
1818
1877
  "rect",
1819
1878
  {
1820
1879
  x: "4",
@@ -1827,7 +1886,7 @@ function CopyIcon() {
1827
1886
  strokeWidth: "1.3"
1828
1887
  }
1829
1888
  ),
1830
- /* @__PURE__ */ jsx5(
1889
+ /* @__PURE__ */ jsx4(
1831
1890
  "path",
1832
1891
  {
1833
1892
  d: "M10 4V2.5A1.5 1.5 0 008.5 1h-6A1.5 1.5 0 001 2.5v6A1.5 1.5 0 002.5 10H4",
@@ -1841,14 +1900,14 @@ function CopyIcon() {
1841
1900
  );
1842
1901
  }
1843
1902
  function PushIcon() {
1844
- return /* @__PURE__ */ jsx5(
1903
+ return /* @__PURE__ */ jsx4(
1845
1904
  "svg",
1846
1905
  {
1847
1906
  width: "14",
1848
1907
  height: "14",
1849
1908
  viewBox: "0 0 14 14",
1850
1909
  style: { color: "rgba(255,255,255,0.5)" },
1851
- children: /* @__PURE__ */ jsx5(
1910
+ children: /* @__PURE__ */ jsx4(
1852
1911
  "path",
1853
1912
  {
1854
1913
  d: "M7 11V3m0 0L4 6m3-3l3 3",
@@ -1863,7 +1922,7 @@ function PushIcon() {
1863
1922
  );
1864
1923
  }
1865
1924
  function ResetIcon() {
1866
- return /* @__PURE__ */ jsxs5(
1925
+ return /* @__PURE__ */ jsxs4(
1867
1926
  "svg",
1868
1927
  {
1869
1928
  width: "14",
@@ -1871,7 +1930,7 @@ function ResetIcon() {
1871
1930
  viewBox: "0 0 14 14",
1872
1931
  style: { color: "rgba(255,255,255,0.4)" },
1873
1932
  children: [
1874
- /* @__PURE__ */ jsx5(
1933
+ /* @__PURE__ */ jsx4(
1875
1934
  "path",
1876
1935
  {
1877
1936
  d: "M2.5 7a4.5 4.5 0 118 2.5",
@@ -1881,7 +1940,7 @@ function ResetIcon() {
1881
1940
  strokeLinecap: "round"
1882
1941
  }
1883
1942
  ),
1884
- /* @__PURE__ */ jsx5(
1943
+ /* @__PURE__ */ jsx4(
1885
1944
  "path",
1886
1945
  {
1887
1946
  d: "M2.5 3v4h4",
@@ -1898,7 +1957,7 @@ function ResetIcon() {
1898
1957
  }
1899
1958
 
1900
1959
  // src/overlay/index.tsx
1901
- import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
1960
+ import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
1902
1961
  async function saveCapture(type, mode, dataUrl) {
1903
1962
  try {
1904
1963
  const res = await fetch("/__afterbefore/save", {
@@ -1916,29 +1975,36 @@ async function saveCapture(type, mode, dataUrl) {
1916
1975
  }
1917
1976
  function AfterBefore() {
1918
1977
  const { state, captureComplete, reset } = useOverlayState();
1919
- const [menuOpen, setMenuOpen] = useState6(false);
1920
1978
  const [statusOpen, setStatusOpen] = useState6(false);
1921
1979
  const [selectorActive, setSelectorActive] = useState6(false);
1922
1980
  const [inspectorActive, setInspectorActive] = useState6(false);
1923
1981
  const [loading, setLoading] = useState6(false);
1924
- const iconPos = useRef6({ x: 24, y: 0 });
1925
- const handlePositionChange = useCallback7(
1982
+ const iconPos = useRef5({ x: 24, y: 0 });
1983
+ useEffect5(() => {
1984
+ if (state.phase === "ready") {
1985
+ const timer = setTimeout(() => {
1986
+ reset();
1987
+ setStatusOpen(false);
1988
+ }, 1500);
1989
+ return () => clearTimeout(timer);
1990
+ }
1991
+ }, [state.phase, reset]);
1992
+ const handlePositionChange = useCallback6(
1926
1993
  (pos) => {
1927
1994
  iconPos.current = pos;
1928
1995
  },
1929
1996
  []
1930
1997
  );
1931
- const handleIconClick = useCallback7(() => {
1998
+ const handleIconClick = useCallback6(() => {
1932
1999
  if (loading) return;
1933
2000
  if (state.phase === "ready") {
1934
2001
  setStatusOpen((prev) => !prev);
1935
- setMenuOpen(false);
1936
2002
  } else {
1937
- setMenuOpen((prev) => !prev);
2003
+ setSelectorActive((prev) => !prev);
1938
2004
  setStatusOpen(false);
1939
2005
  }
1940
2006
  }, [state.phase, loading]);
1941
- const performCapture = useCallback7(
2007
+ const performCapture = useCallback6(
1942
2008
  async (mode, area, element) => {
1943
2009
  setLoading(true);
1944
2010
  try {
@@ -1958,52 +2024,47 @@ function AfterBefore() {
1958
2024
  },
1959
2025
  [state.phase, captureComplete]
1960
2026
  );
1961
- const handleModeSelect = useCallback7(
1962
- (mode) => {
1963
- setMenuOpen(false);
1964
- if (mode === "area") {
1965
- setSelectorActive(true);
1966
- } else if (mode === "component") {
1967
- setInspectorActive(true);
1968
- } else {
1969
- performCapture(mode);
1970
- }
1971
- },
1972
- [performCapture]
1973
- );
1974
- const handleComponentSelect = useCallback7(
2027
+ const handleViewportCapture = useCallback6(() => {
2028
+ setSelectorActive(false);
2029
+ performCapture("viewport");
2030
+ }, [performCapture]);
2031
+ const handleFullPageCapture = useCallback6(() => {
2032
+ setSelectorActive(false);
2033
+ performCapture("fullpage");
2034
+ }, [performCapture]);
2035
+ const handleComponentMode = useCallback6(() => {
2036
+ setSelectorActive(false);
2037
+ setInspectorActive(true);
2038
+ }, []);
2039
+ const handleComponentSelect = useCallback6(
1975
2040
  (element) => {
1976
2041
  setInspectorActive(false);
1977
2042
  performCapture("component", void 0, element);
1978
2043
  },
1979
2044
  [performCapture]
1980
2045
  );
1981
- const handleComponentCancel = useCallback7(() => {
2046
+ const handleComponentCancel = useCallback6(() => {
1982
2047
  setInspectorActive(false);
1983
2048
  }, []);
1984
- const handleAreaSelect = useCallback7(
2049
+ const handleAreaSelect = useCallback6(
1985
2050
  (area) => {
1986
2051
  setSelectorActive(false);
1987
2052
  performCapture("area", area);
1988
2053
  },
1989
2054
  [performCapture]
1990
2055
  );
1991
- const handleAreaCancel = useCallback7(() => {
2056
+ const handleAreaCancel = useCallback6(() => {
1992
2057
  setSelectorActive(false);
1993
2058
  }, []);
1994
- const handleReset = useCallback7(() => {
2059
+ const handleReset = useCallback6(() => {
1995
2060
  reset();
1996
2061
  setStatusOpen(false);
1997
- setMenuOpen(false);
1998
2062
  }, [reset]);
1999
- const handleMenuClose = useCallback7(() => {
2000
- setMenuOpen(false);
2001
- }, []);
2002
- const handleStatusClose = useCallback7(() => {
2063
+ const handleStatusClose = useCallback6(() => {
2003
2064
  setStatusOpen(false);
2004
2065
  }, []);
2005
- return /* @__PURE__ */ jsxs6("div", { "data-afterbefore": "true", children: [
2006
- /* @__PURE__ */ jsx6(
2066
+ return /* @__PURE__ */ jsxs5("div", { "data-afterbefore": "true", children: [
2067
+ /* @__PURE__ */ jsx5(
2007
2068
  Icon,
2008
2069
  {
2009
2070
  phase: state.phase,
@@ -2012,17 +2073,18 @@ function AfterBefore() {
2012
2073
  onPositionChange: handlePositionChange
2013
2074
  }
2014
2075
  ),
2015
- menuOpen && (state.phase === "idle" || state.phase === "captured-before") && /* @__PURE__ */ jsx6(
2016
- Menu,
2076
+ selectorActive && /* @__PURE__ */ jsx5(
2077
+ Selector,
2017
2078
  {
2018
- onSelect: handleModeSelect,
2019
- onClose: handleMenuClose,
2020
- position: iconPos.current
2079
+ onSelect: handleAreaSelect,
2080
+ onCancel: handleAreaCancel,
2081
+ onViewport: handleViewportCapture,
2082
+ onFullPage: handleFullPageCapture,
2083
+ onComponent: handleComponentMode
2021
2084
  }
2022
2085
  ),
2023
- selectorActive && /* @__PURE__ */ jsx6(Selector, { onSelect: handleAreaSelect, onCancel: handleAreaCancel }),
2024
- inspectorActive && /* @__PURE__ */ jsx6(Inspector, { onSelect: handleComponentSelect, onCancel: handleComponentCancel }),
2025
- statusOpen && state.phase === "ready" && /* @__PURE__ */ jsx6(
2086
+ inspectorActive && /* @__PURE__ */ jsx5(Inspector, { onSelect: handleComponentSelect, onCancel: handleComponentCancel }),
2087
+ statusOpen && state.phase === "ready" && /* @__PURE__ */ jsx5(
2026
2088
  Status,
2027
2089
  {
2028
2090
  onReset: handleReset,