afterbefore 0.2.2 → 0.2.4
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/{chunk-DAWAW3ZL.js → chunk-A73KCX5G.js} +2 -2
- package/dist/chunk-A73KCX5G.js.map +1 -0
- package/dist/overlay/index.js +1131 -217
- package/dist/overlay/index.js.map +1 -1
- package/dist/server/middleware.js +1 -1
- package/dist/server/route.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-DAWAW3ZL.js.map +0 -1
package/dist/overlay/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
// src/overlay/index.tsx
|
|
4
|
-
import { useState as
|
|
4
|
+
import { useState as useState6, useCallback as useCallback7, useRef as useRef6, useEffect as useEffect6 } from "react";
|
|
5
5
|
|
|
6
6
|
// src/overlay/state.ts
|
|
7
7
|
import { useState, useCallback } from "react";
|
|
@@ -33,32 +33,99 @@ function useOverlayState() {
|
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
// src/overlay/capture.ts
|
|
36
|
-
|
|
37
|
-
async function
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
36
|
+
var cachedStream = null;
|
|
37
|
+
async function getTabStream() {
|
|
38
|
+
if (cachedStream && cachedStream.active) {
|
|
39
|
+
return cachedStream;
|
|
40
|
+
}
|
|
41
|
+
const stream = await navigator.mediaDevices.getDisplayMedia({
|
|
42
|
+
video: { displaySurface: "browser" },
|
|
43
|
+
preferCurrentTab: true
|
|
44
|
+
});
|
|
45
|
+
cachedStream = stream;
|
|
46
|
+
stream.getVideoTracks()[0].addEventListener("ended", () => {
|
|
47
|
+
cachedStream = null;
|
|
48
|
+
});
|
|
49
|
+
return stream;
|
|
50
|
+
}
|
|
51
|
+
async function hideOverlay() {
|
|
52
|
+
const elements = document.querySelectorAll(
|
|
53
|
+
"[data-afterbefore]"
|
|
54
|
+
);
|
|
55
|
+
const saved = /* @__PURE__ */ new Map();
|
|
56
|
+
elements.forEach((el) => {
|
|
57
|
+
saved.set(el, el.style.visibility);
|
|
58
|
+
el.style.visibility = "hidden";
|
|
59
|
+
});
|
|
60
|
+
await new Promise((resolve) => {
|
|
61
|
+
requestAnimationFrame(() => {
|
|
62
|
+
requestAnimationFrame(() => {
|
|
63
|
+
setTimeout(resolve, 50);
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
return () => {
|
|
68
|
+
saved.forEach((v, el) => {
|
|
69
|
+
el.style.visibility = v;
|
|
70
|
+
});
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
async function grabFrame() {
|
|
74
|
+
const stream = await getTabStream();
|
|
75
|
+
const track = stream.getVideoTracks()[0];
|
|
76
|
+
const imageCapture = new window.ImageCapture(track);
|
|
77
|
+
return imageCapture.grabFrame();
|
|
78
|
+
}
|
|
79
|
+
function bitmapToDataUrl(bitmap, crop) {
|
|
80
|
+
const canvas = document.createElement("canvas");
|
|
81
|
+
const ctx = canvas.getContext("2d");
|
|
82
|
+
if (crop) {
|
|
83
|
+
const scaleX = bitmap.width / window.innerWidth;
|
|
84
|
+
const scaleY = bitmap.height / window.innerHeight;
|
|
85
|
+
const sx = crop.x * scaleX;
|
|
86
|
+
const sy = crop.y * scaleY;
|
|
87
|
+
const sw = crop.width * scaleX;
|
|
88
|
+
const sh = crop.height * scaleY;
|
|
89
|
+
canvas.width = sw;
|
|
90
|
+
canvas.height = sh;
|
|
91
|
+
ctx.drawImage(bitmap, sx, sy, sw, sh, 0, 0, sw, sh);
|
|
92
|
+
} else {
|
|
93
|
+
canvas.width = bitmap.width;
|
|
94
|
+
canvas.height = bitmap.height;
|
|
95
|
+
ctx.drawImage(bitmap, 0, 0);
|
|
41
96
|
}
|
|
97
|
+
return canvas.toDataURL("image/png");
|
|
98
|
+
}
|
|
99
|
+
async function capture(options) {
|
|
100
|
+
const { mode, area, element } = options;
|
|
42
101
|
if (mode === "fullpage") {
|
|
43
102
|
return captureFullPage();
|
|
44
103
|
}
|
|
45
|
-
|
|
46
|
-
|
|
104
|
+
const restoreOverlay = await hideOverlay();
|
|
105
|
+
try {
|
|
106
|
+
const bitmap = await grabFrame();
|
|
107
|
+
if (mode === "viewport") {
|
|
108
|
+
return bitmapToDataUrl(bitmap);
|
|
109
|
+
}
|
|
110
|
+
if (mode === "area" && area) {
|
|
111
|
+
return bitmapToDataUrl(bitmap, area);
|
|
112
|
+
}
|
|
113
|
+
if (mode === "component" && element) {
|
|
114
|
+
const rect = element.getBoundingClientRect();
|
|
115
|
+
return bitmapToDataUrl(bitmap, {
|
|
116
|
+
x: rect.x,
|
|
117
|
+
y: rect.y,
|
|
118
|
+
width: rect.width,
|
|
119
|
+
height: rect.height
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
throw new Error(`Invalid capture mode: ${mode}`);
|
|
123
|
+
} finally {
|
|
124
|
+
restoreOverlay();
|
|
47
125
|
}
|
|
48
|
-
throw new Error(`Invalid capture mode: ${mode}`);
|
|
49
|
-
}
|
|
50
|
-
async function captureViewport() {
|
|
51
|
-
const dataUrl = await toPng(document.documentElement, {
|
|
52
|
-
width: window.innerWidth,
|
|
53
|
-
height: window.innerHeight,
|
|
54
|
-
style: {
|
|
55
|
-
overflow: "hidden"
|
|
56
|
-
},
|
|
57
|
-
filter: filterOverlay
|
|
58
|
-
});
|
|
59
|
-
return dataUrl;
|
|
60
126
|
}
|
|
61
127
|
async function captureFullPage() {
|
|
128
|
+
const { toPng } = await import("html-to-image");
|
|
62
129
|
const scrollY = window.scrollY;
|
|
63
130
|
const body = document.body;
|
|
64
131
|
const html = document.documentElement;
|
|
@@ -76,43 +143,11 @@ async function captureFullPage() {
|
|
|
76
143
|
overflow: "visible",
|
|
77
144
|
height: `${fullHeight}px`
|
|
78
145
|
},
|
|
79
|
-
filter:
|
|
146
|
+
filter: (node) => !node.dataset?.afterbefore
|
|
80
147
|
});
|
|
81
148
|
window.scrollTo(0, scrollY);
|
|
82
149
|
return dataUrl;
|
|
83
150
|
}
|
|
84
|
-
async function captureArea(area) {
|
|
85
|
-
const fullDataUrl = await captureViewport();
|
|
86
|
-
const img = await loadImage(fullDataUrl);
|
|
87
|
-
const dpr = window.devicePixelRatio || 1;
|
|
88
|
-
const canvas = document.createElement("canvas");
|
|
89
|
-
canvas.width = area.width * dpr;
|
|
90
|
-
canvas.height = area.height * dpr;
|
|
91
|
-
const ctx = canvas.getContext("2d");
|
|
92
|
-
ctx.drawImage(
|
|
93
|
-
img,
|
|
94
|
-
area.x * dpr,
|
|
95
|
-
area.y * dpr,
|
|
96
|
-
area.width * dpr,
|
|
97
|
-
area.height * dpr,
|
|
98
|
-
0,
|
|
99
|
-
0,
|
|
100
|
-
area.width * dpr,
|
|
101
|
-
area.height * dpr
|
|
102
|
-
);
|
|
103
|
-
return canvas.toDataURL("image/png");
|
|
104
|
-
}
|
|
105
|
-
function loadImage(src) {
|
|
106
|
-
return new Promise((resolve, reject) => {
|
|
107
|
-
const img = new Image();
|
|
108
|
-
img.onload = () => resolve(img);
|
|
109
|
-
img.onerror = reject;
|
|
110
|
-
img.src = src;
|
|
111
|
-
});
|
|
112
|
-
}
|
|
113
|
-
function filterOverlay(node) {
|
|
114
|
-
return !node.dataset?.afterbefore;
|
|
115
|
-
}
|
|
116
151
|
|
|
117
152
|
// src/overlay/ui/icon.tsx
|
|
118
153
|
import { useRef, useCallback as useCallback2, useEffect, useState as useState2 } from "react";
|
|
@@ -338,7 +373,7 @@ var modes = [
|
|
|
338
373
|
{
|
|
339
374
|
mode: "viewport",
|
|
340
375
|
label: "Viewport",
|
|
341
|
-
icon: /* @__PURE__ */ jsxs2("svg", { width: "
|
|
376
|
+
icon: /* @__PURE__ */ jsxs2("svg", { width: "18", height: "18", viewBox: "0 0 16 16", children: [
|
|
342
377
|
/* @__PURE__ */ jsx2(
|
|
343
378
|
"rect",
|
|
344
379
|
{
|
|
@@ -369,7 +404,7 @@ var modes = [
|
|
|
369
404
|
{
|
|
370
405
|
mode: "fullpage",
|
|
371
406
|
label: "Full Page",
|
|
372
|
-
icon: /* @__PURE__ */ jsxs2("svg", { width: "
|
|
407
|
+
icon: /* @__PURE__ */ jsxs2("svg", { width: "18", height: "18", viewBox: "0 0 16 16", children: [
|
|
373
408
|
/* @__PURE__ */ jsx2(
|
|
374
409
|
"rect",
|
|
375
410
|
{
|
|
@@ -424,7 +459,7 @@ var modes = [
|
|
|
424
459
|
{
|
|
425
460
|
mode: "area",
|
|
426
461
|
label: "Select Area",
|
|
427
|
-
icon: /* @__PURE__ */ jsxs2("svg", { width: "
|
|
462
|
+
icon: /* @__PURE__ */ jsxs2("svg", { width: "18", height: "18", viewBox: "0 0 16 16", children: [
|
|
428
463
|
/* @__PURE__ */ jsx2(
|
|
429
464
|
"path",
|
|
430
465
|
{
|
|
@@ -466,9 +501,41 @@ var modes = [
|
|
|
466
501
|
}
|
|
467
502
|
)
|
|
468
503
|
] })
|
|
504
|
+
},
|
|
505
|
+
{
|
|
506
|
+
mode: "component",
|
|
507
|
+
label: "Component",
|
|
508
|
+
icon: /* @__PURE__ */ jsxs2("svg", { width: "18", height: "18", viewBox: "0 0 16 16", children: [
|
|
509
|
+
/* @__PURE__ */ jsx2(
|
|
510
|
+
"path",
|
|
511
|
+
{
|
|
512
|
+
d: "M3 2l2.5 10 2-3.5L11 11 3 2z",
|
|
513
|
+
fill: "none",
|
|
514
|
+
stroke: "currentColor",
|
|
515
|
+
strokeWidth: "1.3",
|
|
516
|
+
strokeLinejoin: "round",
|
|
517
|
+
strokeLinecap: "round"
|
|
518
|
+
}
|
|
519
|
+
),
|
|
520
|
+
/* @__PURE__ */ jsx2(
|
|
521
|
+
"rect",
|
|
522
|
+
{
|
|
523
|
+
x: "8",
|
|
524
|
+
y: "5",
|
|
525
|
+
width: "6.5",
|
|
526
|
+
height: "6.5",
|
|
527
|
+
rx: "1",
|
|
528
|
+
fill: "none",
|
|
529
|
+
stroke: "currentColor",
|
|
530
|
+
strokeWidth: "1.2",
|
|
531
|
+
strokeDasharray: "2 1.5"
|
|
532
|
+
}
|
|
533
|
+
)
|
|
534
|
+
] })
|
|
469
535
|
}
|
|
470
536
|
];
|
|
471
|
-
var
|
|
537
|
+
var PILL_WIDTH = 270;
|
|
538
|
+
var BTN_SIZE = 40;
|
|
472
539
|
function Menu({ onSelect, onClose, position }) {
|
|
473
540
|
const menuRef = useRef2(null);
|
|
474
541
|
const handleClickOutside = useCallback3(
|
|
@@ -497,10 +564,10 @@ function Menu({ onSelect, onClose, position }) {
|
|
|
497
564
|
}, [handleClickOutside, handleKeyDown]);
|
|
498
565
|
const menuLeft = Math.max(
|
|
499
566
|
8,
|
|
500
|
-
Math.min(position.x -
|
|
567
|
+
Math.min(position.x - PILL_WIDTH / 2 + 20, window.innerWidth - PILL_WIDTH - 8)
|
|
501
568
|
);
|
|
502
569
|
const menuBottom = window.innerHeight - position.y + 8;
|
|
503
|
-
return /* @__PURE__ */
|
|
570
|
+
return /* @__PURE__ */ jsxs2(
|
|
504
571
|
"div",
|
|
505
572
|
{
|
|
506
573
|
ref: menuRef,
|
|
@@ -509,128 +576,354 @@ function Menu({ onSelect, onClose, position }) {
|
|
|
509
576
|
position: "fixed",
|
|
510
577
|
left: menuLeft,
|
|
511
578
|
bottom: menuBottom,
|
|
512
|
-
|
|
579
|
+
display: "flex",
|
|
580
|
+
alignItems: "center",
|
|
581
|
+
gap: 4,
|
|
513
582
|
background: "rgba(24, 24, 27, 0.95)",
|
|
514
|
-
borderRadius:
|
|
515
|
-
padding:
|
|
583
|
+
borderRadius: 22,
|
|
584
|
+
padding: "5px 6px",
|
|
516
585
|
boxShadow: "0 4px 20px rgba(0,0,0,0.4), 0 0 0 1px rgba(255,255,255,0.08)",
|
|
517
586
|
zIndex: 2147483647,
|
|
518
587
|
fontFamily: "system-ui, -apple-system, sans-serif",
|
|
519
588
|
backdropFilter: "blur(12px)"
|
|
520
589
|
},
|
|
521
|
-
children:
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
590
|
+
children: [
|
|
591
|
+
modes.map(({ mode, label, icon }) => /* @__PURE__ */ jsx2(
|
|
592
|
+
"button",
|
|
593
|
+
{
|
|
594
|
+
title: label,
|
|
595
|
+
onClick: () => onSelect(mode),
|
|
596
|
+
style: {
|
|
597
|
+
display: "flex",
|
|
598
|
+
alignItems: "center",
|
|
599
|
+
justifyContent: "center",
|
|
600
|
+
width: BTN_SIZE,
|
|
601
|
+
height: BTN_SIZE,
|
|
602
|
+
border: "none",
|
|
603
|
+
background: "transparent",
|
|
604
|
+
color: "rgba(255,255,255,0.7)",
|
|
605
|
+
borderRadius: "50%",
|
|
606
|
+
cursor: "pointer",
|
|
607
|
+
padding: 0,
|
|
608
|
+
transition: "background 0.1s, color 0.1s",
|
|
609
|
+
flexShrink: 0
|
|
610
|
+
},
|
|
611
|
+
onMouseEnter: (e) => {
|
|
612
|
+
const btn = e.currentTarget;
|
|
613
|
+
btn.style.background = "rgba(255,255,255,0.12)";
|
|
614
|
+
btn.style.color = "rgba(255,255,255,1)";
|
|
615
|
+
},
|
|
616
|
+
onMouseLeave: (e) => {
|
|
617
|
+
const btn = e.currentTarget;
|
|
618
|
+
btn.style.background = "transparent";
|
|
619
|
+
btn.style.color = "rgba(255,255,255,0.7)";
|
|
620
|
+
},
|
|
621
|
+
children: icon
|
|
545
622
|
},
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
623
|
+
mode
|
|
624
|
+
)),
|
|
625
|
+
/* @__PURE__ */ jsx2(
|
|
626
|
+
"div",
|
|
627
|
+
{
|
|
628
|
+
style: {
|
|
629
|
+
width: 1,
|
|
630
|
+
height: 24,
|
|
631
|
+
background: "rgba(255,255,255,0.12)",
|
|
632
|
+
flexShrink: 0,
|
|
633
|
+
margin: "0 2px"
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
),
|
|
637
|
+
/* @__PURE__ */ jsx2(
|
|
638
|
+
"button",
|
|
639
|
+
{
|
|
640
|
+
title: "Close",
|
|
641
|
+
onClick: onClose,
|
|
642
|
+
style: {
|
|
643
|
+
display: "flex",
|
|
644
|
+
alignItems: "center",
|
|
645
|
+
justifyContent: "center",
|
|
646
|
+
width: BTN_SIZE,
|
|
647
|
+
height: BTN_SIZE,
|
|
648
|
+
border: "none",
|
|
649
|
+
background: "transparent",
|
|
650
|
+
color: "rgba(255,255,255,0.45)",
|
|
651
|
+
borderRadius: "50%",
|
|
652
|
+
cursor: "pointer",
|
|
653
|
+
padding: 0,
|
|
654
|
+
transition: "background 0.1s, color 0.1s",
|
|
655
|
+
flexShrink: 0
|
|
656
|
+
},
|
|
657
|
+
onMouseEnter: (e) => {
|
|
658
|
+
const btn = e.currentTarget;
|
|
659
|
+
btn.style.background = "rgba(255,255,255,0.12)";
|
|
660
|
+
btn.style.color = "rgba(255,255,255,0.9)";
|
|
661
|
+
},
|
|
662
|
+
onMouseLeave: (e) => {
|
|
663
|
+
const btn = e.currentTarget;
|
|
664
|
+
btn.style.background = "transparent";
|
|
665
|
+
btn.style.color = "rgba(255,255,255,0.45)";
|
|
666
|
+
},
|
|
667
|
+
children: /* @__PURE__ */ jsx2("svg", { width: "16", height: "16", viewBox: "0 0 14 14", children: /* @__PURE__ */ jsx2(
|
|
668
|
+
"path",
|
|
549
669
|
{
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
},
|
|
555
|
-
children: icon
|
|
670
|
+
d: "M3 3l8 8M11 3l-8 8",
|
|
671
|
+
stroke: "currentColor",
|
|
672
|
+
strokeWidth: "1.5",
|
|
673
|
+
strokeLinecap: "round"
|
|
556
674
|
}
|
|
557
|
-
)
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
mode
|
|
562
|
-
))
|
|
675
|
+
) })
|
|
676
|
+
}
|
|
677
|
+
)
|
|
678
|
+
]
|
|
563
679
|
}
|
|
564
680
|
);
|
|
565
681
|
}
|
|
566
682
|
|
|
567
683
|
// src/overlay/ui/selector.tsx
|
|
568
|
-
import { useState as useState3, useRef as useRef3, useCallback as useCallback4, useEffect as useEffect3 } from "react";
|
|
684
|
+
import React3, { useState as useState3, useRef as useRef3, useCallback as useCallback4, useEffect as useEffect3 } from "react";
|
|
569
685
|
import { Fragment, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
570
|
-
var
|
|
686
|
+
var HANDLE_R = 6;
|
|
687
|
+
var HANDLE_HIT = 16;
|
|
688
|
+
var MIN_SIZE = 20;
|
|
689
|
+
var PANEL_HEIGHT_EST = 140;
|
|
690
|
+
var ASPECT_RATIOS = [
|
|
691
|
+
{ label: "Free", value: 0 },
|
|
692
|
+
{ label: "16:9", value: 16 / 9 },
|
|
693
|
+
{ label: "4:3", value: 4 / 3 },
|
|
694
|
+
{ label: "1:1", value: 1 },
|
|
695
|
+
{ label: "3:2", value: 3 / 2 },
|
|
696
|
+
{ label: "21:9", value: 21 / 9 }
|
|
697
|
+
];
|
|
571
698
|
function Selector({ onSelect, onCancel }) {
|
|
572
|
-
const [
|
|
573
|
-
const
|
|
574
|
-
const
|
|
575
|
-
|
|
699
|
+
const [rect, setRect] = useState3(null);
|
|
700
|
+
const [placed, setPlaced] = useState3(false);
|
|
701
|
+
const [aspect, setAspect] = useState3("Free");
|
|
702
|
+
const [aspectOpen, setAspectOpen] = useState3(false);
|
|
703
|
+
const [savedOpen, setSavedOpen] = useState3(false);
|
|
704
|
+
const [presets, setPresets] = useState3(
|
|
705
|
+
() => {
|
|
706
|
+
try {
|
|
707
|
+
const s = localStorage.getItem("ab-area-presets");
|
|
708
|
+
return s ? JSON.parse(s) : [];
|
|
709
|
+
} catch {
|
|
710
|
+
return [];
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
);
|
|
714
|
+
const [cursor, setCursor] = useState3("crosshair");
|
|
715
|
+
const mode = useRef3("none");
|
|
716
|
+
const start = useRef3({ x: 0, y: 0 });
|
|
717
|
+
const snap = useRef3({ x: 0, y: 0, w: 0, h: 0 });
|
|
718
|
+
const corner = useRef3("br");
|
|
719
|
+
const panelRef = useRef3(null);
|
|
720
|
+
const ratio = ASPECT_RATIOS.find((a) => a.label === aspect)?.value ?? 0;
|
|
721
|
+
useEffect3(() => {
|
|
722
|
+
const onKey = (e) => {
|
|
723
|
+
if (e.target?.tagName === "INPUT") {
|
|
724
|
+
if (e.key === "Escape") e.target.blur();
|
|
725
|
+
return;
|
|
726
|
+
}
|
|
576
727
|
if (e.key === "Escape") {
|
|
577
|
-
|
|
728
|
+
if (placed) {
|
|
729
|
+
setPlaced(false);
|
|
730
|
+
setRect(null);
|
|
731
|
+
} else {
|
|
732
|
+
onCancel();
|
|
733
|
+
}
|
|
734
|
+
} else if (e.key === "Enter" && placed && rect) {
|
|
735
|
+
onSelect({
|
|
736
|
+
x: Math.round(rect.x),
|
|
737
|
+
y: Math.round(rect.y),
|
|
738
|
+
width: Math.round(rect.w),
|
|
739
|
+
height: Math.round(rect.h)
|
|
740
|
+
});
|
|
578
741
|
}
|
|
742
|
+
};
|
|
743
|
+
document.addEventListener("keydown", onKey);
|
|
744
|
+
return () => document.removeEventListener("keydown", onKey);
|
|
745
|
+
}, [placed, rect, onSelect, onCancel]);
|
|
746
|
+
const hitCorner = useCallback4(
|
|
747
|
+
(mx, my, r) => {
|
|
748
|
+
const cs = [
|
|
749
|
+
["tl", r.x, r.y],
|
|
750
|
+
["tr", r.x + r.w, r.y],
|
|
751
|
+
["bl", r.x, r.y + r.h],
|
|
752
|
+
["br", r.x + r.w, r.y + r.h]
|
|
753
|
+
];
|
|
754
|
+
for (const [c, cx, cy] of cs) {
|
|
755
|
+
if (Math.abs(mx - cx) <= HANDLE_HIT && Math.abs(my - cy) <= HANDLE_HIT)
|
|
756
|
+
return c;
|
|
757
|
+
}
|
|
758
|
+
return null;
|
|
579
759
|
},
|
|
580
|
-
[
|
|
760
|
+
[]
|
|
581
761
|
);
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
const
|
|
587
|
-
e
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
762
|
+
const hitInside = useCallback4(
|
|
763
|
+
(mx, my, r) => mx >= r.x && mx <= r.x + r.w && my >= r.y && my <= r.y + r.h,
|
|
764
|
+
[]
|
|
765
|
+
);
|
|
766
|
+
const onDown = useCallback4(
|
|
767
|
+
(e) => {
|
|
768
|
+
if (panelRef.current?.contains(e.target)) return;
|
|
769
|
+
e.preventDefault();
|
|
770
|
+
setAspectOpen(false);
|
|
771
|
+
setSavedOpen(false);
|
|
772
|
+
const mx = e.clientX, my = e.clientY;
|
|
773
|
+
if (placed && rect) {
|
|
774
|
+
const c = hitCorner(mx, my, rect);
|
|
775
|
+
if (c) {
|
|
776
|
+
mode.current = "resizing";
|
|
777
|
+
corner.current = c;
|
|
778
|
+
start.current = { x: mx, y: my };
|
|
779
|
+
snap.current = { ...rect };
|
|
780
|
+
return;
|
|
781
|
+
}
|
|
782
|
+
if (hitInside(mx, my, rect)) {
|
|
783
|
+
mode.current = "moving";
|
|
784
|
+
start.current = { x: mx, y: my };
|
|
785
|
+
snap.current = { ...rect };
|
|
786
|
+
return;
|
|
787
|
+
}
|
|
788
|
+
setPlaced(false);
|
|
789
|
+
}
|
|
790
|
+
mode.current = "drawing";
|
|
791
|
+
start.current = { x: mx, y: my };
|
|
792
|
+
setRect({ x: mx, y: my, w: 0, h: 0 });
|
|
793
|
+
},
|
|
794
|
+
[placed, rect, hitCorner, hitInside]
|
|
795
|
+
);
|
|
796
|
+
const onMove = useCallback4(
|
|
797
|
+
(e) => {
|
|
798
|
+
const mx = e.clientX, my = e.clientY;
|
|
799
|
+
if (mode.current === "drawing") {
|
|
800
|
+
const sx = start.current.x, sy = start.current.y;
|
|
801
|
+
let x = Math.min(sx, mx), y = Math.min(sy, my);
|
|
802
|
+
let w = Math.abs(mx - sx), h = Math.abs(my - sy);
|
|
803
|
+
if (ratio > 0) {
|
|
804
|
+
h = w / ratio;
|
|
805
|
+
if (my < sy) y = sy - h;
|
|
806
|
+
}
|
|
807
|
+
setRect({ x, y, w, h });
|
|
808
|
+
} else if (mode.current === "moving") {
|
|
809
|
+
const dx = mx - start.current.x, dy = my - start.current.y;
|
|
810
|
+
setRect({
|
|
811
|
+
...snap.current,
|
|
812
|
+
x: snap.current.x + dx,
|
|
813
|
+
y: snap.current.y + dy
|
|
814
|
+
});
|
|
815
|
+
} else if (mode.current === "resizing") {
|
|
816
|
+
const o = snap.current;
|
|
817
|
+
const c = corner.current;
|
|
818
|
+
const nr = { ...o };
|
|
819
|
+
if (c === "br") {
|
|
820
|
+
nr.w = Math.max(MIN_SIZE, mx - o.x);
|
|
821
|
+
nr.h = ratio > 0 ? nr.w / ratio : Math.max(MIN_SIZE, my - o.y);
|
|
822
|
+
} else if (c === "bl") {
|
|
823
|
+
nr.w = Math.max(MIN_SIZE, o.x + o.w - mx);
|
|
824
|
+
nr.x = o.x + o.w - nr.w;
|
|
825
|
+
nr.h = ratio > 0 ? nr.w / ratio : Math.max(MIN_SIZE, my - o.y);
|
|
826
|
+
} else if (c === "tr") {
|
|
827
|
+
nr.w = Math.max(MIN_SIZE, mx - o.x);
|
|
828
|
+
nr.h = ratio > 0 ? nr.w / ratio : Math.max(MIN_SIZE, o.y + o.h - my);
|
|
829
|
+
nr.y = o.y + o.h - nr.h;
|
|
830
|
+
} else {
|
|
831
|
+
nr.w = Math.max(MIN_SIZE, o.x + o.w - mx);
|
|
832
|
+
nr.h = ratio > 0 ? nr.w / ratio : Math.max(MIN_SIZE, o.y + o.h - my);
|
|
833
|
+
nr.x = o.x + o.w - nr.w;
|
|
834
|
+
nr.y = o.y + o.h - nr.h;
|
|
835
|
+
}
|
|
836
|
+
setRect(nr);
|
|
837
|
+
} else if (placed && rect) {
|
|
838
|
+
const c = hitCorner(mx, my, rect);
|
|
839
|
+
if (c === "tl" || c === "br") setCursor("nwse-resize");
|
|
840
|
+
else if (c === "tr" || c === "bl") setCursor("nesw-resize");
|
|
841
|
+
else if (hitInside(mx, my, rect)) setCursor("move");
|
|
842
|
+
else setCursor("crosshair");
|
|
843
|
+
}
|
|
844
|
+
},
|
|
845
|
+
[ratio, placed, rect, hitCorner, hitInside]
|
|
846
|
+
);
|
|
847
|
+
const onUp = useCallback4(
|
|
848
|
+
(e) => {
|
|
849
|
+
const prevMode = mode.current;
|
|
850
|
+
if (prevMode === "drawing" && rect) {
|
|
851
|
+
if (rect.w >= MIN_SIZE && rect.h >= MIN_SIZE) {
|
|
852
|
+
setPlaced(true);
|
|
853
|
+
} else {
|
|
854
|
+
setRect(null);
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
if (prevMode === "moving" && placed && rect) {
|
|
858
|
+
const dx = Math.abs(e.clientX - start.current.x);
|
|
859
|
+
const dy = Math.abs(e.clientY - start.current.y);
|
|
860
|
+
if (dx < 3 && dy < 3) {
|
|
861
|
+
onSelect({
|
|
862
|
+
x: Math.round(rect.x),
|
|
863
|
+
y: Math.round(rect.y),
|
|
864
|
+
width: Math.round(rect.w),
|
|
865
|
+
height: Math.round(rect.h)
|
|
866
|
+
});
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
mode.current = "none";
|
|
870
|
+
},
|
|
871
|
+
[rect, placed, onSelect]
|
|
872
|
+
);
|
|
873
|
+
const setSize = useCallback4(
|
|
874
|
+
(field, v) => {
|
|
875
|
+
const n = parseInt(v, 10);
|
|
876
|
+
if (isNaN(n) || n < MIN_SIZE || !rect) return;
|
|
877
|
+
if (field === "w") {
|
|
878
|
+
setRect({ ...rect, w: n, h: ratio > 0 ? n / ratio : rect.h });
|
|
879
|
+
} else {
|
|
880
|
+
setRect({ ...rect, h: n, w: ratio > 0 ? n * ratio : rect.w });
|
|
881
|
+
}
|
|
882
|
+
},
|
|
883
|
+
[rect, ratio]
|
|
884
|
+
);
|
|
885
|
+
const setPos = useCallback4(
|
|
886
|
+
(field, v) => {
|
|
887
|
+
const n = parseInt(v, 10);
|
|
888
|
+
if (isNaN(n) || !rect) return;
|
|
889
|
+
setRect({ ...rect, [field]: n });
|
|
890
|
+
},
|
|
891
|
+
[rect]
|
|
892
|
+
);
|
|
893
|
+
const savePreset = useCallback4(() => {
|
|
894
|
+
if (!rect) return;
|
|
895
|
+
const label = `${Math.round(rect.w)}\xD7${Math.round(rect.h)}`;
|
|
896
|
+
const next = [
|
|
897
|
+
...presets.filter((p) => p.label !== label),
|
|
898
|
+
{ label, rect: { ...rect } }
|
|
899
|
+
];
|
|
900
|
+
setPresets(next);
|
|
901
|
+
try {
|
|
902
|
+
localStorage.setItem("ab-area-presets", JSON.stringify(next));
|
|
903
|
+
} catch {
|
|
614
904
|
}
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
}
|
|
905
|
+
setSavedOpen(false);
|
|
906
|
+
}, [rect, presets]);
|
|
907
|
+
const loadPreset = useCallback4((p) => {
|
|
908
|
+
setRect({ ...p.rect });
|
|
909
|
+
setPlaced(true);
|
|
910
|
+
setSavedOpen(false);
|
|
911
|
+
}, []);
|
|
912
|
+
const activeCursor = mode.current === "moving" ? "move" : mode.current === "resizing" ? "nwse-resize" : mode.current === "drawing" ? "crosshair" : cursor;
|
|
913
|
+
const panelAbove = rect && rect.y + rect.h + PANEL_HEIGHT_EST > window.innerHeight;
|
|
914
|
+
const hasRect = rect && rect.w > 0 && rect.h > 0;
|
|
622
915
|
return /* @__PURE__ */ jsxs3(
|
|
623
916
|
"div",
|
|
624
917
|
{
|
|
625
918
|
"data-afterbefore": "true",
|
|
626
|
-
onMouseDown:
|
|
627
|
-
onMouseMove:
|
|
628
|
-
onMouseUp:
|
|
919
|
+
onMouseDown: onDown,
|
|
920
|
+
onMouseMove: onMove,
|
|
921
|
+
onMouseUp: onUp,
|
|
629
922
|
style: {
|
|
630
923
|
position: "fixed",
|
|
631
924
|
inset: 0,
|
|
632
925
|
zIndex: 2147483647,
|
|
633
|
-
cursor:
|
|
926
|
+
cursor: activeCursor
|
|
634
927
|
},
|
|
635
928
|
children: [
|
|
636
929
|
/* @__PURE__ */ jsx3(
|
|
@@ -639,9 +932,9 @@ function Selector({ onSelect, onCancel }) {
|
|
|
639
932
|
style: {
|
|
640
933
|
position: "absolute",
|
|
641
934
|
inset: 0,
|
|
642
|
-
background: "rgba(0, 0, 0, 0.
|
|
935
|
+
background: "rgba(0, 0, 0, 0.5)",
|
|
643
936
|
pointerEvents: "none",
|
|
644
|
-
...
|
|
937
|
+
...hasRect ? {
|
|
645
938
|
clipPath: `polygon(
|
|
646
939
|
0% 0%, 0% 100%, 100% 100%, 100% 0%, 0% 0%,
|
|
647
940
|
${rect.x}px ${rect.y}px,
|
|
@@ -654,7 +947,7 @@ function Selector({ onSelect, onCancel }) {
|
|
|
654
947
|
}
|
|
655
948
|
}
|
|
656
949
|
),
|
|
657
|
-
|
|
950
|
+
hasRect && /* @__PURE__ */ jsxs3(Fragment, { children: [
|
|
658
951
|
/* @__PURE__ */ jsx3(
|
|
659
952
|
"div",
|
|
660
953
|
{
|
|
@@ -664,14 +957,245 @@ function Selector({ onSelect, onCancel }) {
|
|
|
664
957
|
top: rect.y,
|
|
665
958
|
width: rect.w,
|
|
666
959
|
height: rect.h,
|
|
667
|
-
border: "
|
|
668
|
-
|
|
669
|
-
pointerEvents: "none",
|
|
670
|
-
boxShadow: "0 0 0 1px rgba(0,0,0,0.3)"
|
|
960
|
+
border: "1.5px dashed rgba(255, 255, 255, 0.45)",
|
|
961
|
+
pointerEvents: "none"
|
|
671
962
|
}
|
|
672
963
|
}
|
|
673
964
|
),
|
|
674
|
-
/* @__PURE__ */ jsxs3(
|
|
965
|
+
placed && [1, 2].map((i) => /* @__PURE__ */ jsxs3(React3.Fragment, { children: [
|
|
966
|
+
/* @__PURE__ */ jsx3(
|
|
967
|
+
"div",
|
|
968
|
+
{
|
|
969
|
+
style: {
|
|
970
|
+
position: "absolute",
|
|
971
|
+
left: rect.x + rect.w * i / 3,
|
|
972
|
+
top: rect.y,
|
|
973
|
+
width: 0,
|
|
974
|
+
height: rect.h,
|
|
975
|
+
borderLeft: "1px dashed rgba(255, 255, 255, 0.18)",
|
|
976
|
+
pointerEvents: "none"
|
|
977
|
+
}
|
|
978
|
+
}
|
|
979
|
+
),
|
|
980
|
+
/* @__PURE__ */ jsx3(
|
|
981
|
+
"div",
|
|
982
|
+
{
|
|
983
|
+
style: {
|
|
984
|
+
position: "absolute",
|
|
985
|
+
left: rect.x,
|
|
986
|
+
top: rect.y + rect.h * i / 3,
|
|
987
|
+
width: rect.w,
|
|
988
|
+
height: 0,
|
|
989
|
+
borderTop: "1px dashed rgba(255, 255, 255, 0.18)",
|
|
990
|
+
pointerEvents: "none"
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
)
|
|
994
|
+
] }, i)),
|
|
995
|
+
placed && [
|
|
996
|
+
[rect.x, rect.y],
|
|
997
|
+
[rect.x + rect.w, rect.y],
|
|
998
|
+
[rect.x, rect.y + rect.h],
|
|
999
|
+
[rect.x + rect.w, rect.y + rect.h]
|
|
1000
|
+
].map(([cx, cy], i) => /* @__PURE__ */ jsx3(
|
|
1001
|
+
"div",
|
|
1002
|
+
{
|
|
1003
|
+
style: {
|
|
1004
|
+
position: "absolute",
|
|
1005
|
+
left: cx - HANDLE_R,
|
|
1006
|
+
top: cy - HANDLE_R,
|
|
1007
|
+
width: HANDLE_R * 2,
|
|
1008
|
+
height: HANDLE_R * 2,
|
|
1009
|
+
borderRadius: "50%",
|
|
1010
|
+
border: "2px solid rgba(255, 255, 255, 0.8)",
|
|
1011
|
+
background: "rgba(0, 0, 0, 0.25)",
|
|
1012
|
+
pointerEvents: "none"
|
|
1013
|
+
}
|
|
1014
|
+
},
|
|
1015
|
+
i
|
|
1016
|
+
)),
|
|
1017
|
+
placed && /* @__PURE__ */ jsxs3(
|
|
1018
|
+
"div",
|
|
1019
|
+
{
|
|
1020
|
+
ref: panelRef,
|
|
1021
|
+
onMouseDown: (e) => e.stopPropagation(),
|
|
1022
|
+
style: {
|
|
1023
|
+
position: "absolute",
|
|
1024
|
+
left: rect.x + rect.w / 2,
|
|
1025
|
+
...panelAbove ? { bottom: window.innerHeight - rect.y + 16 } : { top: rect.y + rect.h + 16 },
|
|
1026
|
+
transform: "translateX(-50%)",
|
|
1027
|
+
background: "rgba(32, 32, 36, 0.92)",
|
|
1028
|
+
backdropFilter: "blur(20px)",
|
|
1029
|
+
WebkitBackdropFilter: "blur(20px)",
|
|
1030
|
+
border: "1px solid rgba(255, 255, 255, 0.1)",
|
|
1031
|
+
borderRadius: 10,
|
|
1032
|
+
padding: "10px 14px",
|
|
1033
|
+
display: "flex",
|
|
1034
|
+
flexDirection: "column",
|
|
1035
|
+
gap: 6,
|
|
1036
|
+
minWidth: 0,
|
|
1037
|
+
fontFamily: "system-ui, -apple-system, sans-serif",
|
|
1038
|
+
color: "rgba(255, 255, 255, 0.9)",
|
|
1039
|
+
boxShadow: "0 8px 32px rgba(0, 0, 0, 0.4)",
|
|
1040
|
+
zIndex: 1
|
|
1041
|
+
},
|
|
1042
|
+
children: [
|
|
1043
|
+
/* @__PURE__ */ jsxs3(Row, { label: "Size", children: [
|
|
1044
|
+
/* @__PURE__ */ jsx3(
|
|
1045
|
+
NumInput,
|
|
1046
|
+
{
|
|
1047
|
+
value: Math.round(rect.w),
|
|
1048
|
+
onChange: (v) => setSize("w", v)
|
|
1049
|
+
}
|
|
1050
|
+
),
|
|
1051
|
+
/* @__PURE__ */ jsx3(Sep, { children: "\xD7" }),
|
|
1052
|
+
/* @__PURE__ */ jsx3(
|
|
1053
|
+
NumInput,
|
|
1054
|
+
{
|
|
1055
|
+
value: Math.round(rect.h),
|
|
1056
|
+
onChange: (v) => setSize("h", v)
|
|
1057
|
+
}
|
|
1058
|
+
),
|
|
1059
|
+
/* @__PURE__ */ jsx3(Unit, { children: "px" })
|
|
1060
|
+
] }),
|
|
1061
|
+
/* @__PURE__ */ jsxs3(Row, { label: "Position", children: [
|
|
1062
|
+
/* @__PURE__ */ jsx3(
|
|
1063
|
+
NumInput,
|
|
1064
|
+
{
|
|
1065
|
+
value: Math.round(rect.x),
|
|
1066
|
+
onChange: (v) => setPos("x", v)
|
|
1067
|
+
}
|
|
1068
|
+
),
|
|
1069
|
+
/* @__PURE__ */ jsx3(Sep, {}),
|
|
1070
|
+
/* @__PURE__ */ jsx3(
|
|
1071
|
+
NumInput,
|
|
1072
|
+
{
|
|
1073
|
+
value: Math.round(rect.y),
|
|
1074
|
+
onChange: (v) => setPos("y", v)
|
|
1075
|
+
}
|
|
1076
|
+
),
|
|
1077
|
+
/* @__PURE__ */ jsx3(Unit, { children: "px" })
|
|
1078
|
+
] }),
|
|
1079
|
+
/* @__PURE__ */ jsx3(
|
|
1080
|
+
"div",
|
|
1081
|
+
{
|
|
1082
|
+
style: {
|
|
1083
|
+
height: 1,
|
|
1084
|
+
background: "rgba(255, 255, 255, 0.08)",
|
|
1085
|
+
margin: "1px 0"
|
|
1086
|
+
}
|
|
1087
|
+
}
|
|
1088
|
+
),
|
|
1089
|
+
/* @__PURE__ */ jsxs3(
|
|
1090
|
+
"div",
|
|
1091
|
+
{
|
|
1092
|
+
style: { display: "flex", alignItems: "center", gap: 12 },
|
|
1093
|
+
children: [
|
|
1094
|
+
/* @__PURE__ */ jsxs3("div", { style: { position: "relative" }, children: [
|
|
1095
|
+
/* @__PURE__ */ jsxs3(
|
|
1096
|
+
DropBtn,
|
|
1097
|
+
{
|
|
1098
|
+
active: aspect !== "Free",
|
|
1099
|
+
onClick: () => {
|
|
1100
|
+
setAspectOpen(!aspectOpen);
|
|
1101
|
+
setSavedOpen(false);
|
|
1102
|
+
},
|
|
1103
|
+
children: [
|
|
1104
|
+
/* @__PURE__ */ jsx3(AspectIcon, {}),
|
|
1105
|
+
aspect === "Free" ? "Free" : aspect,
|
|
1106
|
+
/* @__PURE__ */ jsx3(Chevron, {})
|
|
1107
|
+
]
|
|
1108
|
+
}
|
|
1109
|
+
),
|
|
1110
|
+
aspectOpen && /* @__PURE__ */ jsx3(DropMenu, { children: ASPECT_RATIOS.map((ar) => /* @__PURE__ */ jsx3(
|
|
1111
|
+
DropItem,
|
|
1112
|
+
{
|
|
1113
|
+
active: ar.label === aspect,
|
|
1114
|
+
onClick: () => {
|
|
1115
|
+
setAspect(ar.label);
|
|
1116
|
+
setAspectOpen(false);
|
|
1117
|
+
if (ar.value > 0 && rect) {
|
|
1118
|
+
setRect({ ...rect, h: rect.w / ar.value });
|
|
1119
|
+
}
|
|
1120
|
+
},
|
|
1121
|
+
children: ar.label
|
|
1122
|
+
},
|
|
1123
|
+
ar.label
|
|
1124
|
+
)) })
|
|
1125
|
+
] }),
|
|
1126
|
+
/* @__PURE__ */ jsxs3("div", { style: { position: "relative" }, children: [
|
|
1127
|
+
/* @__PURE__ */ jsxs3(
|
|
1128
|
+
DropBtn,
|
|
1129
|
+
{
|
|
1130
|
+
onClick: () => {
|
|
1131
|
+
setSavedOpen(!savedOpen);
|
|
1132
|
+
setAspectOpen(false);
|
|
1133
|
+
},
|
|
1134
|
+
children: [
|
|
1135
|
+
/* @__PURE__ */ jsx3(SavedIcon, {}),
|
|
1136
|
+
"Saved",
|
|
1137
|
+
/* @__PURE__ */ jsx3(Chevron, {})
|
|
1138
|
+
]
|
|
1139
|
+
}
|
|
1140
|
+
),
|
|
1141
|
+
savedOpen && /* @__PURE__ */ jsxs3(DropMenu, { children: [
|
|
1142
|
+
/* @__PURE__ */ jsx3(
|
|
1143
|
+
DropItem,
|
|
1144
|
+
{
|
|
1145
|
+
accent: true,
|
|
1146
|
+
onClick: savePreset,
|
|
1147
|
+
children: "Save current"
|
|
1148
|
+
}
|
|
1149
|
+
),
|
|
1150
|
+
presets.length > 0 && /* @__PURE__ */ jsx3(
|
|
1151
|
+
"div",
|
|
1152
|
+
{
|
|
1153
|
+
style: {
|
|
1154
|
+
height: 1,
|
|
1155
|
+
background: "rgba(255,255,255,0.08)",
|
|
1156
|
+
margin: "4px 0"
|
|
1157
|
+
}
|
|
1158
|
+
}
|
|
1159
|
+
),
|
|
1160
|
+
presets.map((p) => /* @__PURE__ */ jsx3(
|
|
1161
|
+
DropItem,
|
|
1162
|
+
{
|
|
1163
|
+
onClick: () => loadPreset(p),
|
|
1164
|
+
children: p.label
|
|
1165
|
+
},
|
|
1166
|
+
p.label
|
|
1167
|
+
)),
|
|
1168
|
+
presets.length === 0 && /* @__PURE__ */ jsx3(
|
|
1169
|
+
"div",
|
|
1170
|
+
{
|
|
1171
|
+
style: {
|
|
1172
|
+
padding: "6px 12px",
|
|
1173
|
+
color: "rgba(255,255,255,0.3)",
|
|
1174
|
+
fontSize: 12
|
|
1175
|
+
},
|
|
1176
|
+
children: "No saved areas"
|
|
1177
|
+
}
|
|
1178
|
+
)
|
|
1179
|
+
] })
|
|
1180
|
+
] })
|
|
1181
|
+
]
|
|
1182
|
+
}
|
|
1183
|
+
),
|
|
1184
|
+
/* @__PURE__ */ jsx3(
|
|
1185
|
+
"div",
|
|
1186
|
+
{
|
|
1187
|
+
style: {
|
|
1188
|
+
fontSize: 10,
|
|
1189
|
+
color: "rgba(255, 255, 255, 0.25)",
|
|
1190
|
+
textAlign: "center"
|
|
1191
|
+
},
|
|
1192
|
+
children: "Click area or Enter to capture \xB7 Esc to cancel"
|
|
1193
|
+
}
|
|
1194
|
+
)
|
|
1195
|
+
]
|
|
1196
|
+
}
|
|
1197
|
+
),
|
|
1198
|
+
!placed && /* @__PURE__ */ jsxs3(
|
|
675
1199
|
"div",
|
|
676
1200
|
{
|
|
677
1201
|
style: {
|
|
@@ -680,7 +1204,7 @@ function Selector({ onSelect, onCancel }) {
|
|
|
680
1204
|
top: rect.y + rect.h + 8,
|
|
681
1205
|
transform: "translateX(-50%)",
|
|
682
1206
|
background: "rgba(24, 24, 27, 0.9)",
|
|
683
|
-
color: "rgba(255,255,255,0.9)",
|
|
1207
|
+
color: "rgba(255, 255, 255, 0.9)",
|
|
684
1208
|
fontSize: 11,
|
|
685
1209
|
fontFamily: "system-ui, -apple-system, monospace",
|
|
686
1210
|
padding: "2px 8px",
|
|
@@ -696,7 +1220,7 @@ function Selector({ onSelect, onCancel }) {
|
|
|
696
1220
|
}
|
|
697
1221
|
)
|
|
698
1222
|
] }),
|
|
699
|
-
!
|
|
1223
|
+
!rect && /* @__PURE__ */ jsx3(
|
|
700
1224
|
"div",
|
|
701
1225
|
{
|
|
702
1226
|
style: {
|
|
@@ -704,11 +1228,11 @@ function Selector({ onSelect, onCancel }) {
|
|
|
704
1228
|
top: "50%",
|
|
705
1229
|
left: "50%",
|
|
706
1230
|
transform: "translate(-50%, -50%)",
|
|
707
|
-
color: "rgba(255,255,255,0.7)",
|
|
1231
|
+
color: "rgba(255, 255, 255, 0.7)",
|
|
708
1232
|
fontSize: 14,
|
|
709
1233
|
fontFamily: "system-ui, -apple-system, sans-serif",
|
|
710
1234
|
pointerEvents: "none",
|
|
711
|
-
textShadow: "0 1px 4px rgba(0,0,0,0.5)"
|
|
1235
|
+
textShadow: "0 1px 4px rgba(0, 0, 0, 0.5)"
|
|
712
1236
|
},
|
|
713
1237
|
children: "Drag to select an area \xB7 Esc to cancel"
|
|
714
1238
|
}
|
|
@@ -717,20 +1241,386 @@ function Selector({ onSelect, onCancel }) {
|
|
|
717
1241
|
}
|
|
718
1242
|
);
|
|
719
1243
|
}
|
|
1244
|
+
function Row({
|
|
1245
|
+
label,
|
|
1246
|
+
children
|
|
1247
|
+
}) {
|
|
1248
|
+
return /* @__PURE__ */ jsxs3("div", { style: { display: "flex", alignItems: "center", gap: 6 }, children: [
|
|
1249
|
+
/* @__PURE__ */ jsx3(
|
|
1250
|
+
"span",
|
|
1251
|
+
{
|
|
1252
|
+
style: {
|
|
1253
|
+
width: 36,
|
|
1254
|
+
fontSize: 11,
|
|
1255
|
+
color: "rgba(255, 255, 255, 0.4)",
|
|
1256
|
+
fontWeight: 400,
|
|
1257
|
+
flexShrink: 0
|
|
1258
|
+
},
|
|
1259
|
+
children: label
|
|
1260
|
+
}
|
|
1261
|
+
),
|
|
1262
|
+
children
|
|
1263
|
+
] });
|
|
1264
|
+
}
|
|
1265
|
+
function Sep({ children }) {
|
|
1266
|
+
return /* @__PURE__ */ jsx3(
|
|
1267
|
+
"span",
|
|
1268
|
+
{
|
|
1269
|
+
style: {
|
|
1270
|
+
fontSize: 11,
|
|
1271
|
+
color: "rgba(255, 255, 255, 0.35)",
|
|
1272
|
+
width: 12,
|
|
1273
|
+
textAlign: "center",
|
|
1274
|
+
flexShrink: 0,
|
|
1275
|
+
visibility: children ? "visible" : "hidden"
|
|
1276
|
+
},
|
|
1277
|
+
children: children || "\xD7"
|
|
1278
|
+
}
|
|
1279
|
+
);
|
|
1280
|
+
}
|
|
1281
|
+
function Unit({ children }) {
|
|
1282
|
+
return /* @__PURE__ */ jsx3(
|
|
1283
|
+
"span",
|
|
1284
|
+
{
|
|
1285
|
+
style: {
|
|
1286
|
+
fontSize: 10,
|
|
1287
|
+
color: "rgba(255, 255, 255, 0.3)",
|
|
1288
|
+
flexShrink: 0
|
|
1289
|
+
},
|
|
1290
|
+
children
|
|
1291
|
+
}
|
|
1292
|
+
);
|
|
1293
|
+
}
|
|
1294
|
+
function NumInput({
|
|
1295
|
+
value,
|
|
1296
|
+
onChange
|
|
1297
|
+
}) {
|
|
1298
|
+
const [editing, setEditing] = useState3(false);
|
|
1299
|
+
const [text, setText] = useState3(String(value));
|
|
1300
|
+
useEffect3(() => {
|
|
1301
|
+
if (!editing) setText(String(value));
|
|
1302
|
+
}, [value, editing]);
|
|
1303
|
+
return /* @__PURE__ */ jsx3(
|
|
1304
|
+
"input",
|
|
1305
|
+
{
|
|
1306
|
+
type: "text",
|
|
1307
|
+
value: editing ? text : String(value),
|
|
1308
|
+
onFocus: () => {
|
|
1309
|
+
setEditing(true);
|
|
1310
|
+
setText(String(value));
|
|
1311
|
+
},
|
|
1312
|
+
onBlur: () => {
|
|
1313
|
+
setEditing(false);
|
|
1314
|
+
onChange(text);
|
|
1315
|
+
},
|
|
1316
|
+
onChange: (e) => setText(e.target.value),
|
|
1317
|
+
onKeyDown: (e) => {
|
|
1318
|
+
if (e.key === "Enter") e.target.blur();
|
|
1319
|
+
},
|
|
1320
|
+
style: {
|
|
1321
|
+
width: 52,
|
|
1322
|
+
padding: "3px 6px",
|
|
1323
|
+
background: "rgba(255, 255, 255, 0.07)",
|
|
1324
|
+
border: "1px solid rgba(255, 255, 255, 0.1)",
|
|
1325
|
+
borderRadius: 4,
|
|
1326
|
+
color: "rgba(255, 255, 255, 0.9)",
|
|
1327
|
+
fontSize: 12,
|
|
1328
|
+
fontFamily: "system-ui, -apple-system, sans-serif",
|
|
1329
|
+
textAlign: "center",
|
|
1330
|
+
outline: "none"
|
|
1331
|
+
}
|
|
1332
|
+
}
|
|
1333
|
+
);
|
|
1334
|
+
}
|
|
1335
|
+
function DropBtn({
|
|
1336
|
+
children,
|
|
1337
|
+
onClick,
|
|
1338
|
+
active
|
|
1339
|
+
}) {
|
|
1340
|
+
return /* @__PURE__ */ jsx3(
|
|
1341
|
+
"button",
|
|
1342
|
+
{
|
|
1343
|
+
onClick,
|
|
1344
|
+
style: {
|
|
1345
|
+
display: "flex",
|
|
1346
|
+
alignItems: "center",
|
|
1347
|
+
gap: 4,
|
|
1348
|
+
background: "none",
|
|
1349
|
+
border: "none",
|
|
1350
|
+
color: active ? "rgba(147, 130, 220, 0.9)" : "rgba(255, 255, 255, 0.5)",
|
|
1351
|
+
cursor: "pointer",
|
|
1352
|
+
fontSize: 11,
|
|
1353
|
+
fontFamily: "inherit",
|
|
1354
|
+
padding: "2px 0"
|
|
1355
|
+
},
|
|
1356
|
+
children
|
|
1357
|
+
}
|
|
1358
|
+
);
|
|
1359
|
+
}
|
|
1360
|
+
function DropMenu({ children }) {
|
|
1361
|
+
return /* @__PURE__ */ jsx3(
|
|
1362
|
+
"div",
|
|
1363
|
+
{
|
|
1364
|
+
style: {
|
|
1365
|
+
position: "absolute",
|
|
1366
|
+
bottom: "100%",
|
|
1367
|
+
left: 0,
|
|
1368
|
+
marginBottom: 4,
|
|
1369
|
+
background: "rgba(32, 32, 36, 0.95)",
|
|
1370
|
+
border: "1px solid rgba(255, 255, 255, 0.1)",
|
|
1371
|
+
borderRadius: 8,
|
|
1372
|
+
padding: "4px 0",
|
|
1373
|
+
minWidth: 110,
|
|
1374
|
+
boxShadow: "0 4px 16px rgba(0, 0, 0, 0.3)",
|
|
1375
|
+
backdropFilter: "blur(20px)",
|
|
1376
|
+
WebkitBackdropFilter: "blur(20px)"
|
|
1377
|
+
},
|
|
1378
|
+
children
|
|
1379
|
+
}
|
|
1380
|
+
);
|
|
1381
|
+
}
|
|
1382
|
+
function DropItem({
|
|
1383
|
+
children,
|
|
1384
|
+
onClick,
|
|
1385
|
+
active,
|
|
1386
|
+
accent
|
|
1387
|
+
}) {
|
|
1388
|
+
return /* @__PURE__ */ jsx3(
|
|
1389
|
+
"button",
|
|
1390
|
+
{
|
|
1391
|
+
onClick,
|
|
1392
|
+
style: {
|
|
1393
|
+
display: "block",
|
|
1394
|
+
width: "100%",
|
|
1395
|
+
padding: "6px 12px",
|
|
1396
|
+
background: active ? "rgba(255, 255, 255, 0.08)" : "none",
|
|
1397
|
+
border: "none",
|
|
1398
|
+
color: accent ? "rgba(147, 130, 220, 0.9)" : "rgba(255, 255, 255, 0.8)",
|
|
1399
|
+
textAlign: "left",
|
|
1400
|
+
cursor: "pointer",
|
|
1401
|
+
fontSize: 13,
|
|
1402
|
+
fontFamily: "inherit"
|
|
1403
|
+
},
|
|
1404
|
+
children
|
|
1405
|
+
}
|
|
1406
|
+
);
|
|
1407
|
+
}
|
|
1408
|
+
function Chevron() {
|
|
1409
|
+
return /* @__PURE__ */ jsx3("svg", { width: "10", height: "10", viewBox: "0 0 10 10", fill: "none", children: /* @__PURE__ */ jsx3(
|
|
1410
|
+
"path",
|
|
1411
|
+
{
|
|
1412
|
+
d: "M3 4l2 2.5L7 4",
|
|
1413
|
+
stroke: "currentColor",
|
|
1414
|
+
strokeWidth: "1.2"
|
|
1415
|
+
}
|
|
1416
|
+
) });
|
|
1417
|
+
}
|
|
1418
|
+
function AspectIcon() {
|
|
1419
|
+
return /* @__PURE__ */ jsxs3("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", children: [
|
|
1420
|
+
/* @__PURE__ */ jsx3(
|
|
1421
|
+
"rect",
|
|
1422
|
+
{
|
|
1423
|
+
x: "2",
|
|
1424
|
+
y: "4",
|
|
1425
|
+
width: "12",
|
|
1426
|
+
height: "8",
|
|
1427
|
+
rx: "1.5",
|
|
1428
|
+
stroke: "currentColor",
|
|
1429
|
+
strokeWidth: "1.2",
|
|
1430
|
+
strokeDasharray: "2 1.5"
|
|
1431
|
+
}
|
|
1432
|
+
),
|
|
1433
|
+
/* @__PURE__ */ jsx3(
|
|
1434
|
+
"line",
|
|
1435
|
+
{
|
|
1436
|
+
x1: "6.33",
|
|
1437
|
+
y1: "4",
|
|
1438
|
+
x2: "6.33",
|
|
1439
|
+
y2: "12",
|
|
1440
|
+
stroke: "currentColor",
|
|
1441
|
+
strokeWidth: "0.8",
|
|
1442
|
+
strokeDasharray: "1.5 1"
|
|
1443
|
+
}
|
|
1444
|
+
),
|
|
1445
|
+
/* @__PURE__ */ jsx3(
|
|
1446
|
+
"line",
|
|
1447
|
+
{
|
|
1448
|
+
x1: "9.67",
|
|
1449
|
+
y1: "4",
|
|
1450
|
+
x2: "9.67",
|
|
1451
|
+
y2: "12",
|
|
1452
|
+
stroke: "currentColor",
|
|
1453
|
+
strokeWidth: "0.8",
|
|
1454
|
+
strokeDasharray: "1.5 1"
|
|
1455
|
+
}
|
|
1456
|
+
)
|
|
1457
|
+
] });
|
|
1458
|
+
}
|
|
1459
|
+
function SavedIcon() {
|
|
1460
|
+
return /* @__PURE__ */ jsx3("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", children: /* @__PURE__ */ jsx3(
|
|
1461
|
+
"rect",
|
|
1462
|
+
{
|
|
1463
|
+
x: "2",
|
|
1464
|
+
y: "3",
|
|
1465
|
+
width: "12",
|
|
1466
|
+
height: "10",
|
|
1467
|
+
rx: "1.5",
|
|
1468
|
+
stroke: "currentColor",
|
|
1469
|
+
strokeWidth: "1.2",
|
|
1470
|
+
strokeDasharray: "2 1.5"
|
|
1471
|
+
}
|
|
1472
|
+
) });
|
|
1473
|
+
}
|
|
1474
|
+
|
|
1475
|
+
// src/overlay/ui/inspector.tsx
|
|
1476
|
+
import { useEffect as useEffect4, useRef as useRef4, useCallback as useCallback5, useState as useState4 } from "react";
|
|
1477
|
+
import { Fragment as Fragment2, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
1478
|
+
function Inspector({ onSelect, onCancel }) {
|
|
1479
|
+
const [highlight, setHighlight] = useState4(null);
|
|
1480
|
+
const hoveredEl = useRef4(null);
|
|
1481
|
+
const styleEl = useRef4(null);
|
|
1482
|
+
useEffect4(() => {
|
|
1483
|
+
const style = document.createElement("style");
|
|
1484
|
+
style.setAttribute("data-afterbefore", "true");
|
|
1485
|
+
style.textContent = "*, *::before, *::after { cursor: crosshair !important; }";
|
|
1486
|
+
document.head.appendChild(style);
|
|
1487
|
+
styleEl.current = style;
|
|
1488
|
+
return () => {
|
|
1489
|
+
style.remove();
|
|
1490
|
+
};
|
|
1491
|
+
}, []);
|
|
1492
|
+
const isOverlayElement = useCallback5((el) => {
|
|
1493
|
+
let node = el;
|
|
1494
|
+
while (node) {
|
|
1495
|
+
if (node instanceof HTMLElement && node.dataset.afterbefore) return true;
|
|
1496
|
+
node = node.parentElement;
|
|
1497
|
+
}
|
|
1498
|
+
return false;
|
|
1499
|
+
}, []);
|
|
1500
|
+
const handleMouseMove = useCallback5(
|
|
1501
|
+
(e) => {
|
|
1502
|
+
const el = document.elementFromPoint(e.clientX, e.clientY);
|
|
1503
|
+
if (!el || !(el instanceof HTMLElement) || isOverlayElement(el)) {
|
|
1504
|
+
setHighlight(null);
|
|
1505
|
+
hoveredEl.current = null;
|
|
1506
|
+
return;
|
|
1507
|
+
}
|
|
1508
|
+
hoveredEl.current = el;
|
|
1509
|
+
const rect = el.getBoundingClientRect();
|
|
1510
|
+
setHighlight({
|
|
1511
|
+
x: rect.x,
|
|
1512
|
+
y: rect.y,
|
|
1513
|
+
width: rect.width,
|
|
1514
|
+
height: rect.height,
|
|
1515
|
+
tag: el.tagName.toLowerCase() + (el.className && typeof el.className === "string" ? `.${el.className.split(" ")[0]}` : "")
|
|
1516
|
+
});
|
|
1517
|
+
},
|
|
1518
|
+
[isOverlayElement]
|
|
1519
|
+
);
|
|
1520
|
+
const handleClick = useCallback5(
|
|
1521
|
+
(e) => {
|
|
1522
|
+
e.preventDefault();
|
|
1523
|
+
e.stopPropagation();
|
|
1524
|
+
e.stopImmediatePropagation();
|
|
1525
|
+
if (hoveredEl.current) {
|
|
1526
|
+
onSelect(hoveredEl.current);
|
|
1527
|
+
}
|
|
1528
|
+
},
|
|
1529
|
+
[onSelect]
|
|
1530
|
+
);
|
|
1531
|
+
const handleKeyDown = useCallback5(
|
|
1532
|
+
(e) => {
|
|
1533
|
+
if (e.key === "Escape") {
|
|
1534
|
+
onCancel();
|
|
1535
|
+
}
|
|
1536
|
+
},
|
|
1537
|
+
[onCancel]
|
|
1538
|
+
);
|
|
1539
|
+
useEffect4(() => {
|
|
1540
|
+
document.addEventListener("mousemove", handleMouseMove, true);
|
|
1541
|
+
document.addEventListener("click", handleClick, true);
|
|
1542
|
+
document.addEventListener("keydown", handleKeyDown);
|
|
1543
|
+
return () => {
|
|
1544
|
+
document.removeEventListener("mousemove", handleMouseMove, true);
|
|
1545
|
+
document.removeEventListener("click", handleClick, true);
|
|
1546
|
+
document.removeEventListener("keydown", handleKeyDown);
|
|
1547
|
+
};
|
|
1548
|
+
}, [handleMouseMove, handleClick, handleKeyDown]);
|
|
1549
|
+
return /* @__PURE__ */ jsxs4("div", { "data-afterbefore": "true", style: { position: "fixed", inset: 0, zIndex: 2147483646, pointerEvents: "none" }, children: [
|
|
1550
|
+
highlight && /* @__PURE__ */ jsxs4(Fragment2, { children: [
|
|
1551
|
+
/* @__PURE__ */ jsx4(
|
|
1552
|
+
"div",
|
|
1553
|
+
{
|
|
1554
|
+
style: {
|
|
1555
|
+
position: "fixed",
|
|
1556
|
+
left: highlight.x,
|
|
1557
|
+
top: highlight.y,
|
|
1558
|
+
width: highlight.width,
|
|
1559
|
+
height: highlight.height,
|
|
1560
|
+
background: "rgba(59, 130, 246, 0.15)",
|
|
1561
|
+
border: "2px solid rgba(59, 130, 246, 0.7)",
|
|
1562
|
+
borderRadius: 2,
|
|
1563
|
+
pointerEvents: "none"
|
|
1564
|
+
}
|
|
1565
|
+
}
|
|
1566
|
+
),
|
|
1567
|
+
/* @__PURE__ */ jsx4(
|
|
1568
|
+
"div",
|
|
1569
|
+
{
|
|
1570
|
+
style: {
|
|
1571
|
+
position: "fixed",
|
|
1572
|
+
left: highlight.x,
|
|
1573
|
+
top: Math.max(0, highlight.y - 24),
|
|
1574
|
+
background: "rgba(59, 130, 246, 0.9)",
|
|
1575
|
+
color: "#fff",
|
|
1576
|
+
fontSize: 11,
|
|
1577
|
+
fontFamily: "system-ui, -apple-system, monospace",
|
|
1578
|
+
padding: "2px 6px",
|
|
1579
|
+
borderRadius: 3,
|
|
1580
|
+
pointerEvents: "none",
|
|
1581
|
+
whiteSpace: "nowrap",
|
|
1582
|
+
lineHeight: "18px"
|
|
1583
|
+
},
|
|
1584
|
+
children: highlight.tag
|
|
1585
|
+
}
|
|
1586
|
+
)
|
|
1587
|
+
] }),
|
|
1588
|
+
!highlight && /* @__PURE__ */ jsx4(
|
|
1589
|
+
"div",
|
|
1590
|
+
{
|
|
1591
|
+
style: {
|
|
1592
|
+
position: "fixed",
|
|
1593
|
+
top: "50%",
|
|
1594
|
+
left: "50%",
|
|
1595
|
+
transform: "translate(-50%, -50%)",
|
|
1596
|
+
color: "rgba(255, 255, 255, 0.7)",
|
|
1597
|
+
fontSize: 14,
|
|
1598
|
+
fontFamily: "system-ui, -apple-system, sans-serif",
|
|
1599
|
+
pointerEvents: "none",
|
|
1600
|
+
textShadow: "0 1px 4px rgba(0, 0, 0, 0.5)",
|
|
1601
|
+
background: "rgba(0, 0, 0, 0.5)",
|
|
1602
|
+
padding: "8px 16px",
|
|
1603
|
+
borderRadius: 8
|
|
1604
|
+
},
|
|
1605
|
+
children: "Hover to inspect \xB7 Click to capture \xB7 Esc to cancel"
|
|
1606
|
+
}
|
|
1607
|
+
)
|
|
1608
|
+
] });
|
|
1609
|
+
}
|
|
720
1610
|
|
|
721
1611
|
// src/overlay/ui/status.tsx
|
|
722
|
-
import { useState as
|
|
723
|
-
import { jsx as
|
|
1612
|
+
import { useState as useState5, useRef as useRef5, useEffect as useEffect5, useCallback as useCallback6 } from "react";
|
|
1613
|
+
import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
724
1614
|
var PANEL_WIDTH = 220;
|
|
725
1615
|
function Status({ onReset, position, onClose }) {
|
|
726
|
-
const panelRef =
|
|
727
|
-
const [toast, setToast] =
|
|
728
|
-
const [pushing, setPushing] =
|
|
729
|
-
const showToast =
|
|
1616
|
+
const panelRef = useRef5(null);
|
|
1617
|
+
const [toast, setToast] = useState5(null);
|
|
1618
|
+
const [pushing, setPushing] = useState5(false);
|
|
1619
|
+
const showToast = useCallback6((message, type) => {
|
|
730
1620
|
setToast({ message, type });
|
|
731
1621
|
setTimeout(() => setToast(null), 3e3);
|
|
732
1622
|
}, []);
|
|
733
|
-
|
|
1623
|
+
useEffect5(() => {
|
|
734
1624
|
const handler = (e) => {
|
|
735
1625
|
if (panelRef.current && !panelRef.current.contains(e.target)) {
|
|
736
1626
|
onClose();
|
|
@@ -739,7 +1629,7 @@ function Status({ onReset, position, onClose }) {
|
|
|
739
1629
|
document.addEventListener("mousedown", handler);
|
|
740
1630
|
return () => document.removeEventListener("mousedown", handler);
|
|
741
1631
|
}, [onClose]);
|
|
742
|
-
|
|
1632
|
+
useEffect5(() => {
|
|
743
1633
|
const handler = (e) => {
|
|
744
1634
|
if (e.key === "Escape") onClose();
|
|
745
1635
|
};
|
|
@@ -815,7 +1705,7 @@ function Status({ onReset, position, onClose }) {
|
|
|
815
1705
|
const onLeave = (e) => {
|
|
816
1706
|
e.currentTarget.style.background = "transparent";
|
|
817
1707
|
};
|
|
818
|
-
return /* @__PURE__ */
|
|
1708
|
+
return /* @__PURE__ */ jsxs5(
|
|
819
1709
|
"div",
|
|
820
1710
|
{
|
|
821
1711
|
ref: panelRef,
|
|
@@ -834,7 +1724,7 @@ function Status({ onReset, position, onClose }) {
|
|
|
834
1724
|
overflow: "hidden"
|
|
835
1725
|
},
|
|
836
1726
|
children: [
|
|
837
|
-
/* @__PURE__ */
|
|
1727
|
+
/* @__PURE__ */ jsx5(
|
|
838
1728
|
"div",
|
|
839
1729
|
{
|
|
840
1730
|
style: {
|
|
@@ -847,8 +1737,8 @@ function Status({ onReset, position, onClose }) {
|
|
|
847
1737
|
children: "Before & After captured"
|
|
848
1738
|
}
|
|
849
1739
|
),
|
|
850
|
-
/* @__PURE__ */
|
|
851
|
-
/* @__PURE__ */
|
|
1740
|
+
/* @__PURE__ */ jsxs5("div", { style: { padding: "0 4px 4px" }, children: [
|
|
1741
|
+
/* @__PURE__ */ jsxs5(
|
|
852
1742
|
"button",
|
|
853
1743
|
{
|
|
854
1744
|
style: buttonStyle,
|
|
@@ -856,12 +1746,12 @@ function Status({ onReset, position, onClose }) {
|
|
|
856
1746
|
onMouseEnter: onEnter,
|
|
857
1747
|
onMouseLeave: onLeave,
|
|
858
1748
|
children: [
|
|
859
|
-
/* @__PURE__ */
|
|
1749
|
+
/* @__PURE__ */ jsx5(FolderIcon, {}),
|
|
860
1750
|
"Open Folder"
|
|
861
1751
|
]
|
|
862
1752
|
}
|
|
863
1753
|
),
|
|
864
|
-
/* @__PURE__ */
|
|
1754
|
+
/* @__PURE__ */ jsxs5(
|
|
865
1755
|
"button",
|
|
866
1756
|
{
|
|
867
1757
|
style: buttonStyle,
|
|
@@ -869,12 +1759,12 @@ function Status({ onReset, position, onClose }) {
|
|
|
869
1759
|
onMouseEnter: onEnter,
|
|
870
1760
|
onMouseLeave: onLeave,
|
|
871
1761
|
children: [
|
|
872
|
-
/* @__PURE__ */
|
|
1762
|
+
/* @__PURE__ */ jsx5(CopyIcon, {}),
|
|
873
1763
|
"Copy Markdown"
|
|
874
1764
|
]
|
|
875
1765
|
}
|
|
876
1766
|
),
|
|
877
|
-
/* @__PURE__ */
|
|
1767
|
+
/* @__PURE__ */ jsxs5(
|
|
878
1768
|
"button",
|
|
879
1769
|
{
|
|
880
1770
|
style: buttonStyle,
|
|
@@ -883,12 +1773,12 @@ function Status({ onReset, position, onClose }) {
|
|
|
883
1773
|
onMouseEnter: onEnter,
|
|
884
1774
|
onMouseLeave: onLeave,
|
|
885
1775
|
children: [
|
|
886
|
-
/* @__PURE__ */
|
|
1776
|
+
/* @__PURE__ */ jsx5(PushIcon, {}),
|
|
887
1777
|
pushing ? "Pushing..." : "Push to PR"
|
|
888
1778
|
]
|
|
889
1779
|
}
|
|
890
1780
|
),
|
|
891
|
-
/* @__PURE__ */
|
|
1781
|
+
/* @__PURE__ */ jsx5(
|
|
892
1782
|
"div",
|
|
893
1783
|
{
|
|
894
1784
|
style: {
|
|
@@ -898,7 +1788,7 @@ function Status({ onReset, position, onClose }) {
|
|
|
898
1788
|
}
|
|
899
1789
|
}
|
|
900
1790
|
),
|
|
901
|
-
/* @__PURE__ */
|
|
1791
|
+
/* @__PURE__ */ jsxs5(
|
|
902
1792
|
"button",
|
|
903
1793
|
{
|
|
904
1794
|
style: { ...buttonStyle, color: "rgba(255,255,255,0.5)" },
|
|
@@ -906,13 +1796,13 @@ function Status({ onReset, position, onClose }) {
|
|
|
906
1796
|
onMouseEnter: onEnter,
|
|
907
1797
|
onMouseLeave: onLeave,
|
|
908
1798
|
children: [
|
|
909
|
-
/* @__PURE__ */
|
|
1799
|
+
/* @__PURE__ */ jsx5(ResetIcon, {}),
|
|
910
1800
|
"Reset"
|
|
911
1801
|
]
|
|
912
1802
|
}
|
|
913
1803
|
)
|
|
914
1804
|
] }),
|
|
915
|
-
toast && /* @__PURE__ */
|
|
1805
|
+
toast && /* @__PURE__ */ jsx5(
|
|
916
1806
|
"div",
|
|
917
1807
|
{
|
|
918
1808
|
style: {
|
|
@@ -938,14 +1828,14 @@ function Status({ onReset, position, onClose }) {
|
|
|
938
1828
|
);
|
|
939
1829
|
}
|
|
940
1830
|
function FolderIcon() {
|
|
941
|
-
return /* @__PURE__ */
|
|
1831
|
+
return /* @__PURE__ */ jsx5(
|
|
942
1832
|
"svg",
|
|
943
1833
|
{
|
|
944
1834
|
width: "14",
|
|
945
1835
|
height: "14",
|
|
946
1836
|
viewBox: "0 0 14 14",
|
|
947
1837
|
style: { color: "rgba(255,255,255,0.5)" },
|
|
948
|
-
children: /* @__PURE__ */
|
|
1838
|
+
children: /* @__PURE__ */ jsx5(
|
|
949
1839
|
"path",
|
|
950
1840
|
{
|
|
951
1841
|
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",
|
|
@@ -958,7 +1848,7 @@ function FolderIcon() {
|
|
|
958
1848
|
);
|
|
959
1849
|
}
|
|
960
1850
|
function CopyIcon() {
|
|
961
|
-
return /* @__PURE__ */
|
|
1851
|
+
return /* @__PURE__ */ jsxs5(
|
|
962
1852
|
"svg",
|
|
963
1853
|
{
|
|
964
1854
|
width: "14",
|
|
@@ -966,7 +1856,7 @@ function CopyIcon() {
|
|
|
966
1856
|
viewBox: "0 0 14 14",
|
|
967
1857
|
style: { color: "rgba(255,255,255,0.5)" },
|
|
968
1858
|
children: [
|
|
969
|
-
/* @__PURE__ */
|
|
1859
|
+
/* @__PURE__ */ jsx5(
|
|
970
1860
|
"rect",
|
|
971
1861
|
{
|
|
972
1862
|
x: "4",
|
|
@@ -979,7 +1869,7 @@ function CopyIcon() {
|
|
|
979
1869
|
strokeWidth: "1.3"
|
|
980
1870
|
}
|
|
981
1871
|
),
|
|
982
|
-
/* @__PURE__ */
|
|
1872
|
+
/* @__PURE__ */ jsx5(
|
|
983
1873
|
"path",
|
|
984
1874
|
{
|
|
985
1875
|
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",
|
|
@@ -993,14 +1883,14 @@ function CopyIcon() {
|
|
|
993
1883
|
);
|
|
994
1884
|
}
|
|
995
1885
|
function PushIcon() {
|
|
996
|
-
return /* @__PURE__ */
|
|
1886
|
+
return /* @__PURE__ */ jsx5(
|
|
997
1887
|
"svg",
|
|
998
1888
|
{
|
|
999
1889
|
width: "14",
|
|
1000
1890
|
height: "14",
|
|
1001
1891
|
viewBox: "0 0 14 14",
|
|
1002
1892
|
style: { color: "rgba(255,255,255,0.5)" },
|
|
1003
|
-
children: /* @__PURE__ */
|
|
1893
|
+
children: /* @__PURE__ */ jsx5(
|
|
1004
1894
|
"path",
|
|
1005
1895
|
{
|
|
1006
1896
|
d: "M7 11V3m0 0L4 6m3-3l3 3",
|
|
@@ -1015,7 +1905,7 @@ function PushIcon() {
|
|
|
1015
1905
|
);
|
|
1016
1906
|
}
|
|
1017
1907
|
function ResetIcon() {
|
|
1018
|
-
return /* @__PURE__ */
|
|
1908
|
+
return /* @__PURE__ */ jsxs5(
|
|
1019
1909
|
"svg",
|
|
1020
1910
|
{
|
|
1021
1911
|
width: "14",
|
|
@@ -1023,7 +1913,7 @@ function ResetIcon() {
|
|
|
1023
1913
|
viewBox: "0 0 14 14",
|
|
1024
1914
|
style: { color: "rgba(255,255,255,0.4)" },
|
|
1025
1915
|
children: [
|
|
1026
|
-
/* @__PURE__ */
|
|
1916
|
+
/* @__PURE__ */ jsx5(
|
|
1027
1917
|
"path",
|
|
1028
1918
|
{
|
|
1029
1919
|
d: "M2.5 7a4.5 4.5 0 118 2.5",
|
|
@@ -1033,7 +1923,7 @@ function ResetIcon() {
|
|
|
1033
1923
|
strokeLinecap: "round"
|
|
1034
1924
|
}
|
|
1035
1925
|
),
|
|
1036
|
-
/* @__PURE__ */
|
|
1926
|
+
/* @__PURE__ */ jsx5(
|
|
1037
1927
|
"path",
|
|
1038
1928
|
{
|
|
1039
1929
|
d: "M2.5 3v4h4",
|
|
@@ -1050,7 +1940,7 @@ function ResetIcon() {
|
|
|
1050
1940
|
}
|
|
1051
1941
|
|
|
1052
1942
|
// src/overlay/index.tsx
|
|
1053
|
-
import { jsx as
|
|
1943
|
+
import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1054
1944
|
async function saveCapture(type, mode, dataUrl) {
|
|
1055
1945
|
try {
|
|
1056
1946
|
const res = await fetch("/__afterbefore/save", {
|
|
@@ -1068,18 +1958,29 @@ async function saveCapture(type, mode, dataUrl) {
|
|
|
1068
1958
|
}
|
|
1069
1959
|
function AfterBefore() {
|
|
1070
1960
|
const { state, captureComplete, reset } = useOverlayState();
|
|
1071
|
-
const [menuOpen, setMenuOpen] =
|
|
1072
|
-
const [statusOpen, setStatusOpen] =
|
|
1073
|
-
const [selectorActive, setSelectorActive] =
|
|
1074
|
-
const [
|
|
1075
|
-
const
|
|
1076
|
-
const
|
|
1961
|
+
const [menuOpen, setMenuOpen] = useState6(false);
|
|
1962
|
+
const [statusOpen, setStatusOpen] = useState6(false);
|
|
1963
|
+
const [selectorActive, setSelectorActive] = useState6(false);
|
|
1964
|
+
const [inspectorActive, setInspectorActive] = useState6(false);
|
|
1965
|
+
const [loading, setLoading] = useState6(false);
|
|
1966
|
+
const iconPos = useRef6({ x: 24, y: 0 });
|
|
1967
|
+
useEffect6(() => {
|
|
1968
|
+
if (state.phase === "ready") {
|
|
1969
|
+
const timer = setTimeout(() => {
|
|
1970
|
+
reset();
|
|
1971
|
+
setStatusOpen(false);
|
|
1972
|
+
setMenuOpen(false);
|
|
1973
|
+
}, 1500);
|
|
1974
|
+
return () => clearTimeout(timer);
|
|
1975
|
+
}
|
|
1976
|
+
}, [state.phase, reset]);
|
|
1977
|
+
const handlePositionChange = useCallback7(
|
|
1077
1978
|
(pos) => {
|
|
1078
1979
|
iconPos.current = pos;
|
|
1079
1980
|
},
|
|
1080
1981
|
[]
|
|
1081
1982
|
);
|
|
1082
|
-
const handleIconClick =
|
|
1983
|
+
const handleIconClick = useCallback7(() => {
|
|
1083
1984
|
if (loading) return;
|
|
1084
1985
|
if (state.phase === "ready") {
|
|
1085
1986
|
setStatusOpen((prev) => !prev);
|
|
@@ -1089,11 +1990,11 @@ function AfterBefore() {
|
|
|
1089
1990
|
setStatusOpen(false);
|
|
1090
1991
|
}
|
|
1091
1992
|
}, [state.phase, loading]);
|
|
1092
|
-
const performCapture =
|
|
1093
|
-
async (mode, area) => {
|
|
1993
|
+
const performCapture = useCallback7(
|
|
1994
|
+
async (mode, area, element) => {
|
|
1094
1995
|
setLoading(true);
|
|
1095
1996
|
try {
|
|
1096
|
-
const dataUrl = await capture({ mode, area });
|
|
1997
|
+
const dataUrl = await capture({ mode, area, element });
|
|
1097
1998
|
const type = state.phase === "idle" ? "before" : "after";
|
|
1098
1999
|
await saveCapture(type, mode, dataUrl);
|
|
1099
2000
|
captureComplete({
|
|
@@ -1109,40 +2010,52 @@ function AfterBefore() {
|
|
|
1109
2010
|
},
|
|
1110
2011
|
[state.phase, captureComplete]
|
|
1111
2012
|
);
|
|
1112
|
-
const handleModeSelect =
|
|
2013
|
+
const handleModeSelect = useCallback7(
|
|
1113
2014
|
(mode) => {
|
|
1114
2015
|
setMenuOpen(false);
|
|
1115
2016
|
if (mode === "area") {
|
|
1116
2017
|
setSelectorActive(true);
|
|
2018
|
+
} else if (mode === "component") {
|
|
2019
|
+
setInspectorActive(true);
|
|
1117
2020
|
} else {
|
|
1118
2021
|
performCapture(mode);
|
|
1119
2022
|
}
|
|
1120
2023
|
},
|
|
1121
2024
|
[performCapture]
|
|
1122
2025
|
);
|
|
1123
|
-
const
|
|
2026
|
+
const handleComponentSelect = useCallback7(
|
|
2027
|
+
(element) => {
|
|
2028
|
+
setInspectorActive(false);
|
|
2029
|
+
performCapture("component", void 0, element);
|
|
2030
|
+
},
|
|
2031
|
+
[performCapture]
|
|
2032
|
+
);
|
|
2033
|
+
const handleComponentCancel = useCallback7(() => {
|
|
2034
|
+
setInspectorActive(false);
|
|
2035
|
+
}, []);
|
|
2036
|
+
const handleAreaSelect = useCallback7(
|
|
1124
2037
|
(area) => {
|
|
1125
2038
|
setSelectorActive(false);
|
|
1126
2039
|
performCapture("area", area);
|
|
1127
2040
|
},
|
|
1128
2041
|
[performCapture]
|
|
1129
2042
|
);
|
|
1130
|
-
const handleAreaCancel =
|
|
2043
|
+
const handleAreaCancel = useCallback7(() => {
|
|
1131
2044
|
setSelectorActive(false);
|
|
1132
2045
|
}, []);
|
|
1133
|
-
const handleReset =
|
|
2046
|
+
const handleReset = useCallback7(() => {
|
|
1134
2047
|
reset();
|
|
1135
2048
|
setStatusOpen(false);
|
|
1136
2049
|
setMenuOpen(false);
|
|
1137
2050
|
}, [reset]);
|
|
1138
|
-
const handleMenuClose =
|
|
2051
|
+
const handleMenuClose = useCallback7(() => {
|
|
1139
2052
|
setMenuOpen(false);
|
|
1140
2053
|
}, []);
|
|
1141
|
-
const handleStatusClose =
|
|
2054
|
+
const handleStatusClose = useCallback7(() => {
|
|
1142
2055
|
setStatusOpen(false);
|
|
1143
2056
|
}, []);
|
|
1144
|
-
return /* @__PURE__ */
|
|
1145
|
-
/* @__PURE__ */
|
|
2057
|
+
return /* @__PURE__ */ jsxs6("div", { "data-afterbefore": "true", children: [
|
|
2058
|
+
/* @__PURE__ */ jsx6(
|
|
1146
2059
|
Icon,
|
|
1147
2060
|
{
|
|
1148
2061
|
phase: state.phase,
|
|
@@ -1151,7 +2064,7 @@ function AfterBefore() {
|
|
|
1151
2064
|
onPositionChange: handlePositionChange
|
|
1152
2065
|
}
|
|
1153
2066
|
),
|
|
1154
|
-
menuOpen && (state.phase === "idle" || state.phase === "captured-before") && /* @__PURE__ */
|
|
2067
|
+
menuOpen && (state.phase === "idle" || state.phase === "captured-before") && /* @__PURE__ */ jsx6(
|
|
1155
2068
|
Menu,
|
|
1156
2069
|
{
|
|
1157
2070
|
onSelect: handleModeSelect,
|
|
@@ -1159,8 +2072,9 @@ function AfterBefore() {
|
|
|
1159
2072
|
position: iconPos.current
|
|
1160
2073
|
}
|
|
1161
2074
|
),
|
|
1162
|
-
selectorActive && /* @__PURE__ */
|
|
1163
|
-
|
|
2075
|
+
selectorActive && /* @__PURE__ */ jsx6(Selector, { onSelect: handleAreaSelect, onCancel: handleAreaCancel }),
|
|
2076
|
+
inspectorActive && /* @__PURE__ */ jsx6(Inspector, { onSelect: handleComponentSelect, onCancel: handleComponentCancel }),
|
|
2077
|
+
statusOpen && state.phase === "ready" && /* @__PURE__ */ jsx6(
|
|
1164
2078
|
Status,
|
|
1165
2079
|
{
|
|
1166
2080
|
onReset: handleReset,
|