afterbefore 0.2.10 → 0.2.12
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-N33DB2F6.js +271 -0
- package/dist/chunk-N33DB2F6.js.map +1 -0
- package/dist/overlay/index.js +725 -1270
- package/dist/overlay/index.js.map +1 -1
- package/dist/server/middleware.js +14 -2
- package/dist/server/middleware.js.map +1 -1
- package/dist/server/route.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-A73KCX5G.js +0 -226
- package/dist/chunk-A73KCX5G.js.map +0 -1
package/dist/overlay/index.js
CHANGED
|
@@ -1,28 +1,19 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
// src/overlay/index.tsx
|
|
4
|
-
import { useState as
|
|
4
|
+
import { useState as useState6, useCallback as useCallback5, useRef as useRef5, useEffect as useEffect5 } from "react";
|
|
5
5
|
|
|
6
6
|
// src/overlay/state.ts
|
|
7
7
|
import { useState, useCallback } from "react";
|
|
8
8
|
var initialState = {
|
|
9
9
|
phase: "idle",
|
|
10
|
-
|
|
11
|
-
after: null
|
|
10
|
+
lastCapture: null
|
|
12
11
|
};
|
|
13
12
|
function useOverlayState() {
|
|
14
13
|
const [state, setState] = useState(initialState);
|
|
15
14
|
const captureComplete = useCallback(
|
|
16
15
|
(result) => {
|
|
17
|
-
setState(
|
|
18
|
-
if (prev.phase === "idle") {
|
|
19
|
-
return { ...prev, phase: "captured-before", before: result };
|
|
20
|
-
}
|
|
21
|
-
if (prev.phase === "captured-before") {
|
|
22
|
-
return { ...prev, phase: "ready", after: result };
|
|
23
|
-
}
|
|
24
|
-
return prev;
|
|
25
|
-
});
|
|
16
|
+
setState({ phase: "ready", lastCapture: result });
|
|
26
17
|
},
|
|
27
18
|
[]
|
|
28
19
|
);
|
|
@@ -34,6 +25,20 @@ function useOverlayState() {
|
|
|
34
25
|
|
|
35
26
|
// src/overlay/capture.ts
|
|
36
27
|
import { snapdom } from "@zumer/snapdom";
|
|
28
|
+
var DEFAULT_FRAME_SETTINGS = {
|
|
29
|
+
enabled: false,
|
|
30
|
+
size: { w: 1920, h: 1080 },
|
|
31
|
+
bgType: "color",
|
|
32
|
+
bgColor: "#000000",
|
|
33
|
+
bgImage: null,
|
|
34
|
+
padding: 40
|
|
35
|
+
};
|
|
36
|
+
var FRAME_SIZE_PRESETS = [
|
|
37
|
+
{ label: "1920 x 1080", w: 1920, h: 1080 },
|
|
38
|
+
{ label: "1080 x 1080", w: 1080, h: 1080 },
|
|
39
|
+
{ label: "1200 x 630", w: 1200, h: 630 },
|
|
40
|
+
{ label: "1080 x 1920", w: 1080, h: 1920 }
|
|
41
|
+
];
|
|
37
42
|
var DEV_UI_SELECTORS = [
|
|
38
43
|
// Afterbefore overlay
|
|
39
44
|
"[data-afterbefore]",
|
|
@@ -55,18 +60,15 @@ async function toPngDataUrl(el, opts) {
|
|
|
55
60
|
return img.src;
|
|
56
61
|
}
|
|
57
62
|
async function capture(options) {
|
|
58
|
-
const { mode,
|
|
63
|
+
const { mode, element } = options;
|
|
59
64
|
if (mode === "viewport") {
|
|
60
65
|
return captureViewport();
|
|
61
66
|
}
|
|
62
67
|
if (mode === "fullpage") {
|
|
63
68
|
return captureFullPage();
|
|
64
69
|
}
|
|
65
|
-
if (mode === "area" && area) {
|
|
66
|
-
return captureArea(area);
|
|
67
|
-
}
|
|
68
70
|
if (mode === "component" && element) {
|
|
69
|
-
return captureComponent(element, options.
|
|
71
|
+
return captureComponent(element, options.frameSettings);
|
|
70
72
|
}
|
|
71
73
|
throw new Error(`Invalid capture mode: ${mode}`);
|
|
72
74
|
}
|
|
@@ -120,39 +122,18 @@ async function captureFullPage() {
|
|
|
120
122
|
window.scrollTo(0, scrollY);
|
|
121
123
|
}
|
|
122
124
|
}
|
|
123
|
-
async function
|
|
124
|
-
const fullDataUrl = await captureViewport();
|
|
125
|
-
const img = await loadImage(fullDataUrl);
|
|
126
|
-
const dpr = window.devicePixelRatio || 1;
|
|
127
|
-
const canvas = document.createElement("canvas");
|
|
128
|
-
canvas.width = area.width * dpr;
|
|
129
|
-
canvas.height = area.height * dpr;
|
|
130
|
-
const ctx = canvas.getContext("2d");
|
|
131
|
-
ctx.drawImage(
|
|
132
|
-
img,
|
|
133
|
-
area.x * dpr,
|
|
134
|
-
area.y * dpr,
|
|
135
|
-
area.width * dpr,
|
|
136
|
-
area.height * dpr,
|
|
137
|
-
0,
|
|
138
|
-
0,
|
|
139
|
-
area.width * dpr,
|
|
140
|
-
area.height * dpr
|
|
141
|
-
);
|
|
142
|
-
return canvas.toDataURL("image/png");
|
|
143
|
-
}
|
|
144
|
-
async function captureComponent(element, frameOnBlack) {
|
|
125
|
+
async function captureComponent(element, frameSettings) {
|
|
145
126
|
const dataUrl = await toPngDataUrl(element);
|
|
146
|
-
if (!
|
|
127
|
+
if (!frameSettings?.enabled) {
|
|
147
128
|
return dataUrl;
|
|
148
129
|
}
|
|
149
130
|
const img = await loadImage(dataUrl);
|
|
150
131
|
const dpr = window.devicePixelRatio || 1;
|
|
151
|
-
const FRAME_W =
|
|
152
|
-
const FRAME_H =
|
|
132
|
+
const FRAME_W = frameSettings.size.w;
|
|
133
|
+
const FRAME_H = frameSettings.size.h;
|
|
134
|
+
const padding = frameSettings.padding;
|
|
153
135
|
const compW = img.width / dpr;
|
|
154
136
|
const compH = img.height / dpr;
|
|
155
|
-
const padding = 40;
|
|
156
137
|
const maxW = FRAME_W - padding * 2;
|
|
157
138
|
const maxH = FRAME_H - padding * 2;
|
|
158
139
|
const scale = Math.min(1, maxW / compW, maxH / compH);
|
|
@@ -162,13 +143,31 @@ async function captureComponent(element, frameOnBlack) {
|
|
|
162
143
|
canvas.width = FRAME_W * dpr;
|
|
163
144
|
canvas.height = FRAME_H * dpr;
|
|
164
145
|
const ctx = canvas.getContext("2d");
|
|
165
|
-
|
|
166
|
-
|
|
146
|
+
if (frameSettings.bgType === "image" && frameSettings.bgImage) {
|
|
147
|
+
try {
|
|
148
|
+
const bgImg = await loadImage(frameSettings.bgImage);
|
|
149
|
+
drawCover(ctx, bgImg, canvas.width, canvas.height);
|
|
150
|
+
} catch {
|
|
151
|
+
ctx.fillStyle = frameSettings.bgColor;
|
|
152
|
+
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
153
|
+
}
|
|
154
|
+
} else {
|
|
155
|
+
ctx.fillStyle = frameSettings.bgColor;
|
|
156
|
+
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
157
|
+
}
|
|
167
158
|
const dx = (canvas.width - drawW) / 2;
|
|
168
159
|
const dy = (canvas.height - drawH) / 2;
|
|
169
160
|
ctx.drawImage(img, dx, dy, drawW, drawH);
|
|
170
161
|
return canvas.toDataURL("image/png");
|
|
171
162
|
}
|
|
163
|
+
function drawCover(ctx, img, cw, ch) {
|
|
164
|
+
const scale = Math.max(cw / img.width, ch / img.height);
|
|
165
|
+
const sw = cw / scale;
|
|
166
|
+
const sh = ch / scale;
|
|
167
|
+
const sx = (img.width - sw) / 2;
|
|
168
|
+
const sy = (img.height - sh) / 2;
|
|
169
|
+
ctx.drawImage(img, sx, sy, sw, sh, 0, 0, cw, ch);
|
|
170
|
+
}
|
|
172
171
|
function loadImage(src) {
|
|
173
172
|
return new Promise((resolve, reject) => {
|
|
174
173
|
const img = new Image();
|
|
@@ -182,23 +181,25 @@ function loadImage(src) {
|
|
|
182
181
|
import { useRef, useCallback as useCallback2, useEffect, useState as useState2 } from "react";
|
|
183
182
|
import { Camera, Check, LoaderCircle } from "lucide-react";
|
|
184
183
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
185
|
-
var ICON_SIZE =
|
|
184
|
+
var ICON_SIZE = 32;
|
|
186
185
|
var EDGE_MARGIN = 24;
|
|
187
186
|
function Icon({ phase, onClick, loading, onPositionChange }) {
|
|
188
187
|
const ref = useRef(null);
|
|
189
|
-
const [pos, setPos] = useState2({ x:
|
|
188
|
+
const [pos, setPos] = useState2({ x: -1, y: -1 });
|
|
190
189
|
const dragState = useRef(null);
|
|
191
190
|
useEffect(() => {
|
|
192
191
|
setPos((prev) => {
|
|
193
|
-
if (prev.y === -1) {
|
|
194
|
-
|
|
195
|
-
|
|
192
|
+
if (prev.x === -1 || prev.y === -1) {
|
|
193
|
+
return {
|
|
194
|
+
x: window.innerWidth - ICON_SIZE - EDGE_MARGIN,
|
|
195
|
+
y: window.innerHeight - ICON_SIZE - EDGE_MARGIN
|
|
196
|
+
};
|
|
196
197
|
}
|
|
197
198
|
return prev;
|
|
198
199
|
});
|
|
199
200
|
}, []);
|
|
200
201
|
useEffect(() => {
|
|
201
|
-
if (pos.y !== -1) {
|
|
202
|
+
if (pos.x !== -1 && pos.y !== -1) {
|
|
202
203
|
onPositionChange?.({ x: pos.x, y: pos.y });
|
|
203
204
|
}
|
|
204
205
|
}, [pos, onPositionChange]);
|
|
@@ -248,7 +249,7 @@ function Icon({ phase, onClick, loading, onPositionChange }) {
|
|
|
248
249
|
window.removeEventListener("mouseup", handleMouseUp);
|
|
249
250
|
};
|
|
250
251
|
}, [onClick]);
|
|
251
|
-
if (pos.y === -1) return null;
|
|
252
|
+
if (pos.x === -1 || pos.y === -1) return null;
|
|
252
253
|
return /* @__PURE__ */ jsxs(
|
|
253
254
|
"div",
|
|
254
255
|
{
|
|
@@ -284,10 +285,6 @@ function Icon({ phase, onClick, loading, onPositionChange }) {
|
|
|
284
285
|
{
|
|
285
286
|
dangerouslySetInnerHTML: {
|
|
286
287
|
__html: `
|
|
287
|
-
@keyframes ab-pulse {
|
|
288
|
-
0%, 100% { transform: scale(1); opacity: 1; }
|
|
289
|
-
50% { transform: scale(1.08); opacity: 0.85; }
|
|
290
|
-
}
|
|
291
288
|
@keyframes ab-spin {
|
|
292
289
|
0% { transform: rotate(0deg); }
|
|
293
290
|
100% { transform: rotate(360deg); }
|
|
@@ -298,113 +295,20 @@ function Icon({ phase, onClick, loading, onPositionChange }) {
|
|
|
298
295
|
loading ? /* @__PURE__ */ jsx(
|
|
299
296
|
LoaderCircle,
|
|
300
297
|
{
|
|
301
|
-
size:
|
|
298
|
+
size: 16,
|
|
302
299
|
strokeWidth: 2,
|
|
303
300
|
style: { animation: "ab-spin 0.8s linear infinite", color: "white" }
|
|
304
301
|
}
|
|
305
|
-
) : phase === "ready" ? /* @__PURE__ */ jsx(Check, { size:
|
|
306
|
-
"div",
|
|
307
|
-
{
|
|
308
|
-
style: {
|
|
309
|
-
position: "relative",
|
|
310
|
-
display: "flex",
|
|
311
|
-
alignItems: "center",
|
|
312
|
-
justifyContent: "center",
|
|
313
|
-
animation: phase === "captured-before" ? "ab-pulse 2s ease-in-out infinite" : "none"
|
|
314
|
-
},
|
|
315
|
-
children: [
|
|
316
|
-
/* @__PURE__ */ jsx(Camera, { size: 20, strokeWidth: 1.9, color: "white" }),
|
|
317
|
-
phase === "captured-before" && /* @__PURE__ */ jsx(
|
|
318
|
-
"div",
|
|
319
|
-
{
|
|
320
|
-
style: {
|
|
321
|
-
position: "absolute",
|
|
322
|
-
top: -6,
|
|
323
|
-
right: -8,
|
|
324
|
-
width: 14,
|
|
325
|
-
height: 14,
|
|
326
|
-
borderRadius: "50%",
|
|
327
|
-
background: "#3b82f6",
|
|
328
|
-
color: "white",
|
|
329
|
-
fontSize: "9px",
|
|
330
|
-
fontWeight: 700,
|
|
331
|
-
display: "flex",
|
|
332
|
-
alignItems: "center",
|
|
333
|
-
justifyContent: "center",
|
|
334
|
-
lineHeight: 1,
|
|
335
|
-
fontFamily: "system-ui, sans-serif"
|
|
336
|
-
},
|
|
337
|
-
children: "1"
|
|
338
|
-
}
|
|
339
|
-
)
|
|
340
|
-
]
|
|
341
|
-
}
|
|
342
|
-
)
|
|
302
|
+
) : phase === "ready" ? /* @__PURE__ */ jsx(Check, { size: 16, strokeWidth: 2.6, color: "#4ade80" }) : /* @__PURE__ */ jsx(Camera, { size: 16, strokeWidth: 1.9, color: "white" })
|
|
343
303
|
]
|
|
344
304
|
}
|
|
345
305
|
);
|
|
346
306
|
}
|
|
347
307
|
|
|
348
308
|
// src/overlay/ui/preview.tsx
|
|
349
|
-
import { useEffect as useEffect2, useState as useState3 } from "react";
|
|
350
309
|
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
351
|
-
var DEFAULT_ASPECT_RATIO = 16 / 9;
|
|
352
|
-
function getAreaPreviewRect() {
|
|
353
|
-
const safeWidth = Math.max(320, window.innerWidth - 120);
|
|
354
|
-
const safeHeight = Math.max(180, window.innerHeight - 220);
|
|
355
|
-
const width = Math.min(window.innerWidth * 0.72, safeHeight * DEFAULT_ASPECT_RATIO, safeWidth);
|
|
356
|
-
const height = width / DEFAULT_ASPECT_RATIO;
|
|
357
|
-
return {
|
|
358
|
-
x: (window.innerWidth - width) / 2,
|
|
359
|
-
y: Math.max(40, (window.innerHeight - height) / 2 - 20),
|
|
360
|
-
width,
|
|
361
|
-
height
|
|
362
|
-
};
|
|
363
|
-
}
|
|
364
310
|
var CAMERA_CURSOR = `url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='32' height='32' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M14.5 4h-5L7 7H4a2 2 0 0 0-2 2v9a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V9a2 2 0 0 0-2-2h-3l-2.5-3z'/%3E%3Ccircle cx='12' cy='13' r='3'/%3E%3C/svg%3E") 16 16, pointer`;
|
|
365
311
|
function CapturePreview({ mode, onClick }) {
|
|
366
|
-
const [areaRect, setAreaRect] = useState3(null);
|
|
367
|
-
useEffect2(() => {
|
|
368
|
-
if (mode !== "area") {
|
|
369
|
-
return;
|
|
370
|
-
}
|
|
371
|
-
const syncRect = () => {
|
|
372
|
-
setAreaRect(getAreaPreviewRect());
|
|
373
|
-
};
|
|
374
|
-
syncRect();
|
|
375
|
-
window.addEventListener("resize", syncRect);
|
|
376
|
-
return () => window.removeEventListener("resize", syncRect);
|
|
377
|
-
}, [mode]);
|
|
378
|
-
if (mode === "area" && areaRect) {
|
|
379
|
-
return /* @__PURE__ */ jsx2(
|
|
380
|
-
"div",
|
|
381
|
-
{
|
|
382
|
-
"data-afterbefore": "true",
|
|
383
|
-
style: {
|
|
384
|
-
position: "fixed",
|
|
385
|
-
inset: 0,
|
|
386
|
-
zIndex: 2147483645,
|
|
387
|
-
pointerEvents: "none"
|
|
388
|
-
},
|
|
389
|
-
children: /* @__PURE__ */ jsx2(
|
|
390
|
-
"div",
|
|
391
|
-
{
|
|
392
|
-
style: {
|
|
393
|
-
position: "absolute",
|
|
394
|
-
left: areaRect.x,
|
|
395
|
-
top: areaRect.y,
|
|
396
|
-
width: areaRect.width,
|
|
397
|
-
height: areaRect.height,
|
|
398
|
-
background: "rgba(125, 211, 252, 0.16)",
|
|
399
|
-
border: "1.5px solid rgba(125, 211, 252, 0.95)",
|
|
400
|
-
boxShadow: "0 0 0 1px rgba(191, 219, 254, 0.4), 0 0 32px rgba(56, 189, 248, 0.18)",
|
|
401
|
-
borderRadius: 14
|
|
402
|
-
}
|
|
403
|
-
}
|
|
404
|
-
)
|
|
405
|
-
}
|
|
406
|
-
);
|
|
407
|
-
}
|
|
408
312
|
if (mode === "viewport" || mode === "fullpage") {
|
|
409
313
|
return /* @__PURE__ */ jsx2(
|
|
410
314
|
"div",
|
|
@@ -416,518 +320,47 @@ function CapturePreview({ mode, onClick }) {
|
|
|
416
320
|
inset: 0,
|
|
417
321
|
zIndex: 2147483645,
|
|
418
322
|
cursor: CAMERA_CURSOR,
|
|
419
|
-
background: "rgba(
|
|
420
|
-
boxShadow: "inset 0 0 0
|
|
421
|
-
}
|
|
422
|
-
children: /* @__PURE__ */ jsx2(
|
|
423
|
-
"div",
|
|
424
|
-
{
|
|
425
|
-
style: {
|
|
426
|
-
position: "absolute",
|
|
427
|
-
top: 36,
|
|
428
|
-
left: "50%",
|
|
429
|
-
transform: "translateX(-50%)",
|
|
430
|
-
padding: "8px 14px",
|
|
431
|
-
borderRadius: 999,
|
|
432
|
-
background: "rgba(125, 211, 252, 0.16)",
|
|
433
|
-
border: "1px solid rgba(125, 211, 252, 0.42)",
|
|
434
|
-
color: "rgba(224, 242, 254, 0.96)",
|
|
435
|
-
fontSize: 12,
|
|
436
|
-
fontFamily: "system-ui, -apple-system, sans-serif",
|
|
437
|
-
boxShadow: "0 10px 30px rgba(14, 116, 144, 0.18)",
|
|
438
|
-
pointerEvents: "none"
|
|
439
|
-
},
|
|
440
|
-
children: mode === "fullpage" ? "Click to capture full page" : "Click to capture viewport"
|
|
441
|
-
}
|
|
442
|
-
)
|
|
323
|
+
background: "rgba(59, 130, 246, 0.15)",
|
|
324
|
+
boxShadow: "inset 0 0 0 2px rgba(59, 130, 246, 0.7)"
|
|
325
|
+
}
|
|
443
326
|
}
|
|
444
327
|
);
|
|
445
328
|
}
|
|
446
|
-
return
|
|
447
|
-
"div",
|
|
448
|
-
{
|
|
449
|
-
"data-afterbefore": "true",
|
|
450
|
-
style: {
|
|
451
|
-
position: "fixed",
|
|
452
|
-
inset: 0,
|
|
453
|
-
zIndex: 2147483645,
|
|
454
|
-
pointerEvents: "none"
|
|
455
|
-
},
|
|
456
|
-
children: /* @__PURE__ */ jsx2(
|
|
457
|
-
"div",
|
|
458
|
-
{
|
|
459
|
-
style: {
|
|
460
|
-
position: "absolute",
|
|
461
|
-
top: 36,
|
|
462
|
-
left: "50%",
|
|
463
|
-
transform: "translateX(-50%)",
|
|
464
|
-
padding: "8px 14px",
|
|
465
|
-
borderRadius: 999,
|
|
466
|
-
background: "rgba(125, 211, 252, 0.16)",
|
|
467
|
-
border: "1px solid rgba(125, 211, 252, 0.42)",
|
|
468
|
-
color: "rgba(224, 242, 254, 0.96)",
|
|
469
|
-
fontSize: 12,
|
|
470
|
-
fontFamily: "system-ui, -apple-system, sans-serif",
|
|
471
|
-
boxShadow: "0 10px 30px rgba(14, 116, 144, 0.18)"
|
|
472
|
-
},
|
|
473
|
-
children: "Click Capture, then hover an element to preview it"
|
|
474
|
-
}
|
|
475
|
-
)
|
|
476
|
-
}
|
|
477
|
-
);
|
|
329
|
+
return null;
|
|
478
330
|
}
|
|
479
331
|
|
|
480
332
|
// src/overlay/ui/toolbar.tsx
|
|
481
|
-
import { useEffect as
|
|
333
|
+
import { useEffect as useEffect2, useRef as useRef2, useState as useState3 } from "react";
|
|
482
334
|
import {
|
|
483
|
-
Camera as Camera2,
|
|
484
335
|
ChevronDown,
|
|
485
|
-
|
|
486
|
-
|
|
336
|
+
Maximize,
|
|
337
|
+
FolderOpen,
|
|
487
338
|
Frame,
|
|
488
|
-
|
|
339
|
+
ImageIcon,
|
|
489
340
|
Monitor,
|
|
490
341
|
MousePointer2,
|
|
491
|
-
|
|
492
|
-
|
|
342
|
+
Palette,
|
|
343
|
+
Settings,
|
|
344
|
+
Trash2,
|
|
345
|
+
Upload,
|
|
493
346
|
X
|
|
494
347
|
} from "lucide-react";
|
|
495
|
-
|
|
496
|
-
// src/overlay/ui/selector.tsx
|
|
497
|
-
import React3, { useRef as useRef2, useCallback as useCallback3, useEffect as useEffect3, useMemo, useState as useState4 } from "react";
|
|
498
348
|
import { Fragment, jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
499
|
-
var DEFAULT_AREA_ASPECT = "16:9";
|
|
500
|
-
var AREA_ASPECT_RATIOS = [
|
|
501
|
-
{ label: "Free", value: 0 },
|
|
502
|
-
{ label: "16:9", value: 16 / 9 },
|
|
503
|
-
{ label: "4:3", value: 4 / 3 },
|
|
504
|
-
{ label: "1:1", value: 1 },
|
|
505
|
-
{ label: "3:2", value: 3 / 2 },
|
|
506
|
-
{ label: "21:9", value: 21 / 9 }
|
|
507
|
-
];
|
|
508
|
-
var HANDLE_R = 6;
|
|
509
|
-
var HANDLE_HIT = 16;
|
|
510
|
-
var MIN_SIZE = 20;
|
|
511
|
-
var SNAP_THRESHOLD = 8;
|
|
512
|
-
var CAMERA_CURSOR2 = `url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='28' height='28' viewBox='0 0 24 24' fill='none' stroke='%23e0f2fe' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 7h3l1.5-2h7L17 7h3a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V9a2 2 0 0 1 2-2Z'/%3E%3Ccircle cx='12' cy='13' r='4'/%3E%3C/svg%3E") 12 12, crosshair`;
|
|
513
|
-
function createInitialAreaRect() {
|
|
514
|
-
const safeWidth = Math.max(320, window.innerWidth - 96);
|
|
515
|
-
const safeHeight = Math.max(180, window.innerHeight - 220);
|
|
516
|
-
const w = Math.min(window.innerWidth * 0.72, safeHeight * (16 / 9), safeWidth);
|
|
517
|
-
const h = w / (16 / 9);
|
|
518
|
-
return {
|
|
519
|
-
x: (window.innerWidth - w) / 2,
|
|
520
|
-
y: Math.max(40, (window.innerHeight - h) / 2 - 20),
|
|
521
|
-
w,
|
|
522
|
-
h
|
|
523
|
-
};
|
|
524
|
-
}
|
|
525
|
-
function getAspectRatio(label) {
|
|
526
|
-
return AREA_ASPECT_RATIOS.find((item) => item.label === label)?.value ?? 0;
|
|
527
|
-
}
|
|
528
|
-
function Selector({
|
|
529
|
-
rect,
|
|
530
|
-
aspect,
|
|
531
|
-
magnetEnabled,
|
|
532
|
-
onRectChange,
|
|
533
|
-
onSelect,
|
|
534
|
-
onCancel
|
|
535
|
-
}) {
|
|
536
|
-
const [snappedX, setSnappedX] = useState4(false);
|
|
537
|
-
const [cursor, setCursor] = useState4("crosshair");
|
|
538
|
-
const mode = useRef2("none");
|
|
539
|
-
const start = useRef2({ x: 0, y: 0 });
|
|
540
|
-
const snapRef = useRef2({ x: 0, y: 0, w: 0, h: 0 });
|
|
541
|
-
const corner = useRef2("br");
|
|
542
|
-
const ratio = useMemo(() => getAspectRatio(aspect), [aspect]);
|
|
543
|
-
const applySnap = useCallback3(
|
|
544
|
-
(nextRect) => {
|
|
545
|
-
if (!magnetEnabled) {
|
|
546
|
-
setSnappedX(false);
|
|
547
|
-
return nextRect;
|
|
548
|
-
}
|
|
549
|
-
const centerX = nextRect.x + nextRect.w / 2;
|
|
550
|
-
const viewportCenterX = window.innerWidth / 2;
|
|
551
|
-
if (Math.abs(centerX - viewportCenterX) < SNAP_THRESHOLD) {
|
|
552
|
-
setSnappedX(true);
|
|
553
|
-
return { ...nextRect, x: viewportCenterX - nextRect.w / 2 };
|
|
554
|
-
}
|
|
555
|
-
setSnappedX(false);
|
|
556
|
-
return nextRect;
|
|
557
|
-
},
|
|
558
|
-
[magnetEnabled]
|
|
559
|
-
);
|
|
560
|
-
useEffect3(() => {
|
|
561
|
-
const onKey = (e) => {
|
|
562
|
-
if (e.key === "Escape") {
|
|
563
|
-
onCancel();
|
|
564
|
-
}
|
|
565
|
-
};
|
|
566
|
-
document.addEventListener("keydown", onKey);
|
|
567
|
-
return () => document.removeEventListener("keydown", onKey);
|
|
568
|
-
}, [onCancel]);
|
|
569
|
-
const hitCorner = useCallback3((mx, my, currentRect) => {
|
|
570
|
-
const corners = [
|
|
571
|
-
["tl", currentRect.x, currentRect.y],
|
|
572
|
-
["tr", currentRect.x + currentRect.w, currentRect.y],
|
|
573
|
-
["bl", currentRect.x, currentRect.y + currentRect.h],
|
|
574
|
-
["br", currentRect.x + currentRect.w, currentRect.y + currentRect.h]
|
|
575
|
-
];
|
|
576
|
-
for (const [hitCorner2, cx, cy] of corners) {
|
|
577
|
-
if (Math.abs(mx - cx) <= HANDLE_HIT && Math.abs(my - cy) <= HANDLE_HIT) {
|
|
578
|
-
return hitCorner2;
|
|
579
|
-
}
|
|
580
|
-
}
|
|
581
|
-
return null;
|
|
582
|
-
}, []);
|
|
583
|
-
const hitInside = useCallback3(
|
|
584
|
-
(mx, my, currentRect) => mx >= currentRect.x && mx <= currentRect.x + currentRect.w && my >= currentRect.y && my <= currentRect.y + currentRect.h,
|
|
585
|
-
[]
|
|
586
|
-
);
|
|
587
|
-
const onDown = useCallback3(
|
|
588
|
-
(e) => {
|
|
589
|
-
e.preventDefault();
|
|
590
|
-
const mx = e.clientX;
|
|
591
|
-
const my = e.clientY;
|
|
592
|
-
if (rect) {
|
|
593
|
-
const activeCorner = hitCorner(mx, my, rect);
|
|
594
|
-
if (activeCorner) {
|
|
595
|
-
mode.current = "resizing";
|
|
596
|
-
corner.current = activeCorner;
|
|
597
|
-
start.current = { x: mx, y: my };
|
|
598
|
-
snapRef.current = { ...rect };
|
|
599
|
-
return;
|
|
600
|
-
}
|
|
601
|
-
if (hitInside(mx, my, rect)) {
|
|
602
|
-
mode.current = "moving";
|
|
603
|
-
start.current = { x: mx, y: my };
|
|
604
|
-
snapRef.current = { ...rect };
|
|
605
|
-
return;
|
|
606
|
-
}
|
|
607
|
-
}
|
|
608
|
-
mode.current = "drawing";
|
|
609
|
-
start.current = { x: mx, y: my };
|
|
610
|
-
onRectChange({ x: mx, y: my, w: 0, h: 0 });
|
|
611
|
-
},
|
|
612
|
-
[hitCorner, hitInside, onRectChange, rect]
|
|
613
|
-
);
|
|
614
|
-
const onMove = useCallback3(
|
|
615
|
-
(e) => {
|
|
616
|
-
const mx = e.clientX;
|
|
617
|
-
const my = e.clientY;
|
|
618
|
-
if (mode.current === "none") {
|
|
619
|
-
if (rect) {
|
|
620
|
-
const activeCorner = hitCorner(mx, my, rect);
|
|
621
|
-
if (activeCorner) {
|
|
622
|
-
setCursor(
|
|
623
|
-
activeCorner === "tl" || activeCorner === "br" ? "nwse-resize" : "nesw-resize"
|
|
624
|
-
);
|
|
625
|
-
} else if (hitInside(mx, my, rect)) {
|
|
626
|
-
setCursor(CAMERA_CURSOR2);
|
|
627
|
-
} else {
|
|
628
|
-
setCursor("crosshair");
|
|
629
|
-
}
|
|
630
|
-
} else {
|
|
631
|
-
setCursor("crosshair");
|
|
632
|
-
}
|
|
633
|
-
return;
|
|
634
|
-
}
|
|
635
|
-
if (mode.current === "drawing") {
|
|
636
|
-
const sx = start.current.x;
|
|
637
|
-
const sy = start.current.y;
|
|
638
|
-
let x = Math.min(sx, mx);
|
|
639
|
-
let y = Math.min(sy, my);
|
|
640
|
-
let w = Math.abs(mx - sx);
|
|
641
|
-
let h = Math.abs(my - sy);
|
|
642
|
-
if (ratio > 0) {
|
|
643
|
-
h = w / ratio;
|
|
644
|
-
if (my < sy) {
|
|
645
|
-
y = sy - h;
|
|
646
|
-
}
|
|
647
|
-
}
|
|
648
|
-
onRectChange(applySnap({ x, y, w, h }));
|
|
649
|
-
return;
|
|
650
|
-
}
|
|
651
|
-
if (mode.current === "moving") {
|
|
652
|
-
const dx = mx - start.current.x;
|
|
653
|
-
const dy = my - start.current.y;
|
|
654
|
-
onRectChange(
|
|
655
|
-
applySnap({
|
|
656
|
-
...snapRef.current,
|
|
657
|
-
x: snapRef.current.x + dx,
|
|
658
|
-
y: snapRef.current.y + dy
|
|
659
|
-
})
|
|
660
|
-
);
|
|
661
|
-
return;
|
|
662
|
-
}
|
|
663
|
-
if (mode.current === "resizing") {
|
|
664
|
-
const original = snapRef.current;
|
|
665
|
-
const nextRect = { ...original };
|
|
666
|
-
if (corner.current === "br") {
|
|
667
|
-
nextRect.w = Math.max(MIN_SIZE, mx - original.x);
|
|
668
|
-
nextRect.h = ratio > 0 ? nextRect.w / ratio : Math.max(MIN_SIZE, my - original.y);
|
|
669
|
-
} else if (corner.current === "bl") {
|
|
670
|
-
nextRect.w = Math.max(MIN_SIZE, original.x + original.w - mx);
|
|
671
|
-
nextRect.x = original.x + original.w - nextRect.w;
|
|
672
|
-
nextRect.h = ratio > 0 ? nextRect.w / ratio : Math.max(MIN_SIZE, my - original.y);
|
|
673
|
-
} else if (corner.current === "tr") {
|
|
674
|
-
nextRect.w = Math.max(MIN_SIZE, mx - original.x);
|
|
675
|
-
nextRect.h = ratio > 0 ? nextRect.w / ratio : Math.max(MIN_SIZE, original.y + original.h - my);
|
|
676
|
-
nextRect.y = original.y + original.h - nextRect.h;
|
|
677
|
-
} else {
|
|
678
|
-
nextRect.w = Math.max(MIN_SIZE, original.x + original.w - mx);
|
|
679
|
-
nextRect.h = ratio > 0 ? nextRect.w / ratio : Math.max(MIN_SIZE, original.y + original.h - my);
|
|
680
|
-
nextRect.x = original.x + original.w - nextRect.w;
|
|
681
|
-
nextRect.y = original.y + original.h - nextRect.h;
|
|
682
|
-
}
|
|
683
|
-
onRectChange(applySnap(nextRect));
|
|
684
|
-
}
|
|
685
|
-
},
|
|
686
|
-
[applySnap, hitCorner, hitInside, onRectChange, ratio, rect]
|
|
687
|
-
);
|
|
688
|
-
const onUp = useCallback3(
|
|
689
|
-
(e) => {
|
|
690
|
-
const previousMode = mode.current;
|
|
691
|
-
if (previousMode === "drawing" && rect) {
|
|
692
|
-
if (rect.w < MIN_SIZE || rect.h < MIN_SIZE) {
|
|
693
|
-
onRectChange(null);
|
|
694
|
-
}
|
|
695
|
-
}
|
|
696
|
-
if (previousMode === "moving" && rect) {
|
|
697
|
-
const dx = Math.abs(e.clientX - start.current.x);
|
|
698
|
-
const dy = Math.abs(e.clientY - start.current.y);
|
|
699
|
-
if (dx < 3 && dy < 3) {
|
|
700
|
-
onSelect({
|
|
701
|
-
x: Math.round(rect.x),
|
|
702
|
-
y: Math.round(rect.y),
|
|
703
|
-
width: Math.round(rect.w),
|
|
704
|
-
height: Math.round(rect.h)
|
|
705
|
-
});
|
|
706
|
-
}
|
|
707
|
-
}
|
|
708
|
-
mode.current = "none";
|
|
709
|
-
},
|
|
710
|
-
[onRectChange, onSelect, rect]
|
|
711
|
-
);
|
|
712
|
-
const hasRect = rect && rect.w > 0 && rect.h > 0;
|
|
713
|
-
return /* @__PURE__ */ jsxs2(
|
|
714
|
-
"div",
|
|
715
|
-
{
|
|
716
|
-
"data-afterbefore": "true",
|
|
717
|
-
onMouseDown: onDown,
|
|
718
|
-
onMouseMove: onMove,
|
|
719
|
-
onMouseUp: onUp,
|
|
720
|
-
style: {
|
|
721
|
-
position: "fixed",
|
|
722
|
-
inset: 0,
|
|
723
|
-
zIndex: 2147483646,
|
|
724
|
-
cursor
|
|
725
|
-
},
|
|
726
|
-
children: [
|
|
727
|
-
snappedX && /* @__PURE__ */ jsx3(
|
|
728
|
-
"div",
|
|
729
|
-
{
|
|
730
|
-
style: {
|
|
731
|
-
position: "absolute",
|
|
732
|
-
left: "50%",
|
|
733
|
-
top: 0,
|
|
734
|
-
bottom: 0,
|
|
735
|
-
width: 0,
|
|
736
|
-
borderLeft: "1px solid rgba(56, 189, 248, 0.55)",
|
|
737
|
-
pointerEvents: "none",
|
|
738
|
-
zIndex: 2
|
|
739
|
-
}
|
|
740
|
-
}
|
|
741
|
-
),
|
|
742
|
-
hasRect ? /* @__PURE__ */ jsxs2(Fragment, { children: [
|
|
743
|
-
/* @__PURE__ */ jsx3(
|
|
744
|
-
"div",
|
|
745
|
-
{
|
|
746
|
-
style: {
|
|
747
|
-
position: "absolute",
|
|
748
|
-
left: 0,
|
|
749
|
-
top: 0,
|
|
750
|
-
width: "100%",
|
|
751
|
-
height: rect.y,
|
|
752
|
-
background: "rgba(0, 0, 0, 0.5)",
|
|
753
|
-
pointerEvents: "none"
|
|
754
|
-
}
|
|
755
|
-
}
|
|
756
|
-
),
|
|
757
|
-
/* @__PURE__ */ jsx3(
|
|
758
|
-
"div",
|
|
759
|
-
{
|
|
760
|
-
style: {
|
|
761
|
-
position: "absolute",
|
|
762
|
-
left: 0,
|
|
763
|
-
top: rect.y,
|
|
764
|
-
width: rect.x,
|
|
765
|
-
height: rect.h,
|
|
766
|
-
background: "rgba(0, 0, 0, 0.5)",
|
|
767
|
-
pointerEvents: "none"
|
|
768
|
-
}
|
|
769
|
-
}
|
|
770
|
-
),
|
|
771
|
-
/* @__PURE__ */ jsx3(
|
|
772
|
-
"div",
|
|
773
|
-
{
|
|
774
|
-
style: {
|
|
775
|
-
position: "absolute",
|
|
776
|
-
left: rect.x + rect.w,
|
|
777
|
-
top: rect.y,
|
|
778
|
-
width: `calc(100% - ${rect.x + rect.w}px)`,
|
|
779
|
-
height: rect.h,
|
|
780
|
-
background: "rgba(0, 0, 0, 0.5)",
|
|
781
|
-
pointerEvents: "none"
|
|
782
|
-
}
|
|
783
|
-
}
|
|
784
|
-
),
|
|
785
|
-
/* @__PURE__ */ jsx3(
|
|
786
|
-
"div",
|
|
787
|
-
{
|
|
788
|
-
style: {
|
|
789
|
-
position: "absolute",
|
|
790
|
-
left: 0,
|
|
791
|
-
top: rect.y + rect.h,
|
|
792
|
-
width: "100%",
|
|
793
|
-
height: `calc(100% - ${rect.y + rect.h}px)`,
|
|
794
|
-
background: "rgba(0, 0, 0, 0.5)",
|
|
795
|
-
pointerEvents: "none"
|
|
796
|
-
}
|
|
797
|
-
}
|
|
798
|
-
),
|
|
799
|
-
/* @__PURE__ */ jsx3(
|
|
800
|
-
"div",
|
|
801
|
-
{
|
|
802
|
-
style: {
|
|
803
|
-
position: "absolute",
|
|
804
|
-
left: rect.x,
|
|
805
|
-
top: rect.y,
|
|
806
|
-
width: rect.w,
|
|
807
|
-
height: rect.h,
|
|
808
|
-
background: "rgba(125, 211, 252, 0.08)",
|
|
809
|
-
pointerEvents: "none",
|
|
810
|
-
borderRadius: 12
|
|
811
|
-
}
|
|
812
|
-
}
|
|
813
|
-
),
|
|
814
|
-
/* @__PURE__ */ jsx3(
|
|
815
|
-
"div",
|
|
816
|
-
{
|
|
817
|
-
style: {
|
|
818
|
-
position: "absolute",
|
|
819
|
-
left: rect.x,
|
|
820
|
-
top: rect.y,
|
|
821
|
-
width: rect.w,
|
|
822
|
-
height: rect.h,
|
|
823
|
-
border: "1.5px dashed rgba(255, 255, 255, 0.45)",
|
|
824
|
-
borderRadius: 12,
|
|
825
|
-
pointerEvents: "none",
|
|
826
|
-
boxShadow: "0 0 0 1px rgba(125, 211, 252, 0.38)"
|
|
827
|
-
}
|
|
828
|
-
}
|
|
829
|
-
),
|
|
830
|
-
[1, 2].map((line) => /* @__PURE__ */ jsxs2(React3.Fragment, { children: [
|
|
831
|
-
/* @__PURE__ */ jsx3(
|
|
832
|
-
"div",
|
|
833
|
-
{
|
|
834
|
-
style: {
|
|
835
|
-
position: "absolute",
|
|
836
|
-
left: rect.x + rect.w * line / 3,
|
|
837
|
-
top: rect.y,
|
|
838
|
-
width: 0,
|
|
839
|
-
height: rect.h,
|
|
840
|
-
borderLeft: "1px dashed rgba(255, 255, 255, 0.18)",
|
|
841
|
-
pointerEvents: "none"
|
|
842
|
-
}
|
|
843
|
-
}
|
|
844
|
-
),
|
|
845
|
-
/* @__PURE__ */ jsx3(
|
|
846
|
-
"div",
|
|
847
|
-
{
|
|
848
|
-
style: {
|
|
849
|
-
position: "absolute",
|
|
850
|
-
left: rect.x,
|
|
851
|
-
top: rect.y + rect.h * line / 3,
|
|
852
|
-
width: rect.w,
|
|
853
|
-
height: 0,
|
|
854
|
-
borderTop: "1px dashed rgba(255, 255, 255, 0.18)",
|
|
855
|
-
pointerEvents: "none"
|
|
856
|
-
}
|
|
857
|
-
}
|
|
858
|
-
)
|
|
859
|
-
] }, line)),
|
|
860
|
-
[
|
|
861
|
-
[rect.x, rect.y],
|
|
862
|
-
[rect.x + rect.w, rect.y],
|
|
863
|
-
[rect.x, rect.y + rect.h],
|
|
864
|
-
[rect.x + rect.w, rect.y + rect.h]
|
|
865
|
-
].map(([cx, cy], index) => /* @__PURE__ */ jsx3(
|
|
866
|
-
"div",
|
|
867
|
-
{
|
|
868
|
-
style: {
|
|
869
|
-
position: "absolute",
|
|
870
|
-
left: cx - HANDLE_R,
|
|
871
|
-
top: cy - HANDLE_R,
|
|
872
|
-
width: HANDLE_R * 2,
|
|
873
|
-
height: HANDLE_R * 2,
|
|
874
|
-
borderRadius: "50%",
|
|
875
|
-
border: "2px solid rgba(255, 255, 255, 0.8)",
|
|
876
|
-
background: "rgba(0, 0, 0, 0.25)",
|
|
877
|
-
pointerEvents: "none"
|
|
878
|
-
}
|
|
879
|
-
},
|
|
880
|
-
index
|
|
881
|
-
))
|
|
882
|
-
] }) : /* @__PURE__ */ jsx3(
|
|
883
|
-
"div",
|
|
884
|
-
{
|
|
885
|
-
style: {
|
|
886
|
-
position: "absolute",
|
|
887
|
-
inset: 0,
|
|
888
|
-
background: "rgba(0, 0, 0, 0.5)",
|
|
889
|
-
pointerEvents: "none"
|
|
890
|
-
}
|
|
891
|
-
}
|
|
892
|
-
)
|
|
893
|
-
]
|
|
894
|
-
}
|
|
895
|
-
);
|
|
896
|
-
}
|
|
897
|
-
|
|
898
|
-
// src/overlay/ui/toolbar.tsx
|
|
899
|
-
import { Fragment as Fragment2, jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
900
349
|
var MODES = [
|
|
901
|
-
{ mode: "
|
|
350
|
+
{ mode: "component", label: "Capture Component", icon: MousePointer2 },
|
|
902
351
|
{ mode: "viewport", label: "Capture Viewport", icon: Monitor },
|
|
903
|
-
{ mode: "fullpage", label: "Capture Full Page", icon:
|
|
904
|
-
{ mode: "component", label: "Capture Component", icon: MousePointer2 }
|
|
352
|
+
{ mode: "fullpage", label: "Capture Full Page", icon: Maximize }
|
|
905
353
|
];
|
|
906
354
|
function Toolbar({
|
|
907
355
|
selectedMode,
|
|
908
356
|
onModeChange,
|
|
909
357
|
onCapture,
|
|
910
358
|
onCancel,
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
areaSelectionActive,
|
|
914
|
-
areaRect,
|
|
915
|
-
areaAspect,
|
|
916
|
-
areaPresets,
|
|
917
|
-
onAreaSizeChange,
|
|
918
|
-
onAreaPositionChange,
|
|
919
|
-
onAreaAspectChange,
|
|
920
|
-
onAreaSavePreset,
|
|
921
|
-
onAreaLoadPreset,
|
|
922
|
-
frameOnBlackEnabled,
|
|
923
|
-
onFrameOnBlackChange
|
|
359
|
+
frameSettings,
|
|
360
|
+
onFrameSettingsChange
|
|
924
361
|
}) {
|
|
925
|
-
const [settingsOpen, setSettingsOpen] =
|
|
926
|
-
|
|
927
|
-
const [savedOpen, setSavedOpen] = useState5(false);
|
|
928
|
-
const showAreaControls = selectedMode === "area" && areaSelectionActive && areaRect !== null;
|
|
929
|
-
const activeAreaRect = showAreaControls ? areaRect : null;
|
|
930
|
-
useEffect4(() => {
|
|
362
|
+
const [settingsOpen, setSettingsOpen] = useState3(false);
|
|
363
|
+
useEffect2(() => {
|
|
931
364
|
const onKey = (e) => {
|
|
932
365
|
if (e.target?.tagName === "INPUT") {
|
|
933
366
|
if (e.key === "Escape") {
|
|
@@ -940,14 +373,6 @@ function Toolbar({
|
|
|
940
373
|
setSettingsOpen(false);
|
|
941
374
|
return;
|
|
942
375
|
}
|
|
943
|
-
if (aspectOpen) {
|
|
944
|
-
setAspectOpen(false);
|
|
945
|
-
return;
|
|
946
|
-
}
|
|
947
|
-
if (savedOpen) {
|
|
948
|
-
setSavedOpen(false);
|
|
949
|
-
return;
|
|
950
|
-
}
|
|
951
376
|
onCancel();
|
|
952
377
|
} else if (e.key === "Enter") {
|
|
953
378
|
onCapture(selectedMode);
|
|
@@ -955,8 +380,8 @@ function Toolbar({
|
|
|
955
380
|
};
|
|
956
381
|
document.addEventListener("keydown", onKey);
|
|
957
382
|
return () => document.removeEventListener("keydown", onKey);
|
|
958
|
-
}, [
|
|
959
|
-
return /* @__PURE__ */
|
|
383
|
+
}, [onCancel, onCapture, selectedMode, settingsOpen]);
|
|
384
|
+
return /* @__PURE__ */ jsx3(
|
|
960
385
|
"div",
|
|
961
386
|
{
|
|
962
387
|
"data-afterbefore": "true",
|
|
@@ -976,163 +401,64 @@ function Toolbar({
|
|
|
976
401
|
backdropFilter: "blur(20px)",
|
|
977
402
|
WebkitBackdropFilter: "blur(20px)",
|
|
978
403
|
border: "1px solid rgba(255, 255, 255, 0.1)",
|
|
979
|
-
borderRadius:
|
|
980
|
-
padding: "
|
|
404
|
+
borderRadius: 999,
|
|
405
|
+
padding: "5px 8px",
|
|
981
406
|
boxShadow: "0 8px 32px rgba(0, 0, 0, 0.4)",
|
|
982
407
|
fontFamily: "system-ui, -apple-system, sans-serif"
|
|
983
408
|
},
|
|
984
|
-
children: [
|
|
985
|
-
/* @__PURE__ */
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
onModeChange(mode);
|
|
995
|
-
},
|
|
996
|
-
children: /* @__PURE__ */ jsx4(Icon2, { size: 18, strokeWidth: 1.7 })
|
|
409
|
+
children: /* @__PURE__ */ jsxs2("div", { style: { display: "flex", alignItems: "center", gap: 0 }, children: [
|
|
410
|
+
/* @__PURE__ */ jsx3(CloseButton, { onClick: onCancel }),
|
|
411
|
+
/* @__PURE__ */ jsx3("div", { style: { display: "flex", alignItems: "center", gap: 2, padding: "0 4px" }, children: MODES.map(({ mode, label, icon: Icon2 }) => /* @__PURE__ */ jsx3(
|
|
412
|
+
ModeButton,
|
|
413
|
+
{
|
|
414
|
+
label,
|
|
415
|
+
selected: selectedMode === mode,
|
|
416
|
+
onClick: () => {
|
|
417
|
+
setSettingsOpen(false);
|
|
418
|
+
onModeChange(mode);
|
|
997
419
|
},
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
onFrameOnBlackChange
|
|
1015
|
-
}
|
|
1016
|
-
),
|
|
1017
|
-
!showAreaControls && /* @__PURE__ */ jsx4(
|
|
1018
|
-
CaptureButton,
|
|
1019
|
-
{
|
|
1020
|
-
label: "Capture",
|
|
1021
|
-
onClick: () => onCapture(selectedMode)
|
|
1022
|
-
}
|
|
1023
|
-
)
|
|
1024
|
-
] }),
|
|
1025
|
-
activeAreaRect && /* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
1026
|
-
/* @__PURE__ */ jsx4(Separator, { vertical: false }),
|
|
1027
|
-
/* @__PURE__ */ jsxs3(
|
|
1028
|
-
"div",
|
|
1029
|
-
{
|
|
1030
|
-
style: {
|
|
1031
|
-
display: "flex",
|
|
1032
|
-
alignItems: "center",
|
|
1033
|
-
gap: 8,
|
|
1034
|
-
flexWrap: "wrap",
|
|
1035
|
-
justifyContent: "center"
|
|
1036
|
-
},
|
|
1037
|
-
children: [
|
|
1038
|
-
/* @__PURE__ */ jsxs3(ControlGroup, { label: "Size", children: [
|
|
1039
|
-
/* @__PURE__ */ jsx4(NumInput, { value: Math.round(activeAreaRect.w), onChange: (value) => onAreaSizeChange("w", value) }),
|
|
1040
|
-
/* @__PURE__ */ jsx4(StaticText, { children: "x" }),
|
|
1041
|
-
/* @__PURE__ */ jsx4(NumInput, { value: Math.round(activeAreaRect.h), onChange: (value) => onAreaSizeChange("h", value) })
|
|
1042
|
-
] }),
|
|
1043
|
-
/* @__PURE__ */ jsxs3(ControlGroup, { label: "Position", children: [
|
|
1044
|
-
/* @__PURE__ */ jsx4(NumInput, { value: Math.round(activeAreaRect.x), onChange: (value) => onAreaPositionChange("x", value) }),
|
|
1045
|
-
/* @__PURE__ */ jsx4(StaticText, { children: "x" }),
|
|
1046
|
-
/* @__PURE__ */ jsx4(NumInput, { value: Math.round(activeAreaRect.y), onChange: (value) => onAreaPositionChange("y", value) })
|
|
1047
|
-
] }),
|
|
1048
|
-
/* @__PURE__ */ jsxs3("div", { style: { position: "relative" }, children: [
|
|
1049
|
-
/* @__PURE__ */ jsxs3(
|
|
1050
|
-
DropButton,
|
|
1051
|
-
{
|
|
1052
|
-
active: areaAspect !== "Free",
|
|
1053
|
-
onClick: () => {
|
|
1054
|
-
setSavedOpen(false);
|
|
1055
|
-
setAspectOpen((prev) => !prev);
|
|
1056
|
-
},
|
|
1057
|
-
children: [
|
|
1058
|
-
areaAspect,
|
|
1059
|
-
/* @__PURE__ */ jsx4(ChevronDown, { size: 14, strokeWidth: 1.8 })
|
|
1060
|
-
]
|
|
1061
|
-
}
|
|
1062
|
-
),
|
|
1063
|
-
aspectOpen && /* @__PURE__ */ jsx4(DropMenu, { children: AREA_ASPECT_RATIOS.map((item) => /* @__PURE__ */ jsx4(
|
|
1064
|
-
DropItem,
|
|
1065
|
-
{
|
|
1066
|
-
active: item.label === areaAspect,
|
|
1067
|
-
onClick: () => {
|
|
1068
|
-
onAreaAspectChange(item.label);
|
|
1069
|
-
setAspectOpen(false);
|
|
1070
|
-
},
|
|
1071
|
-
children: item.label
|
|
1072
|
-
},
|
|
1073
|
-
item.label
|
|
1074
|
-
)) })
|
|
1075
|
-
] }),
|
|
1076
|
-
/* @__PURE__ */ jsxs3("div", { style: { position: "relative" }, children: [
|
|
1077
|
-
/* @__PURE__ */ jsxs3(
|
|
1078
|
-
DropButton,
|
|
1079
|
-
{
|
|
1080
|
-
onClick: () => {
|
|
1081
|
-
setAspectOpen(false);
|
|
1082
|
-
setSavedOpen((prev) => !prev);
|
|
1083
|
-
},
|
|
1084
|
-
children: [
|
|
1085
|
-
/* @__PURE__ */ jsx4(Save, { size: 14, strokeWidth: 1.8 }),
|
|
1086
|
-
"Saved",
|
|
1087
|
-
/* @__PURE__ */ jsx4(ChevronDown, { size: 14, strokeWidth: 1.8 })
|
|
1088
|
-
]
|
|
1089
|
-
}
|
|
1090
|
-
),
|
|
1091
|
-
savedOpen && /* @__PURE__ */ jsxs3(DropMenu, { children: [
|
|
1092
|
-
/* @__PURE__ */ jsx4(DropItem, { accent: true, onClick: onAreaSavePreset, children: "Save current" }),
|
|
1093
|
-
areaPresets.length > 0 && /* @__PURE__ */ jsx4(MenuDivider, {}),
|
|
1094
|
-
areaPresets.map((preset) => /* @__PURE__ */ jsx4(
|
|
1095
|
-
DropItem,
|
|
1096
|
-
{
|
|
1097
|
-
onClick: () => {
|
|
1098
|
-
onAreaLoadPreset(preset);
|
|
1099
|
-
setSavedOpen(false);
|
|
1100
|
-
},
|
|
1101
|
-
children: preset.label
|
|
1102
|
-
},
|
|
1103
|
-
preset.label
|
|
1104
|
-
)),
|
|
1105
|
-
areaPresets.length === 0 && /* @__PURE__ */ jsx4(
|
|
1106
|
-
"div",
|
|
1107
|
-
{
|
|
1108
|
-
style: {
|
|
1109
|
-
padding: "6px 12px",
|
|
1110
|
-
color: "rgba(255,255,255,0.3)",
|
|
1111
|
-
fontSize: 12
|
|
1112
|
-
},
|
|
1113
|
-
children: "No saved areas"
|
|
1114
|
-
}
|
|
1115
|
-
)
|
|
1116
|
-
] })
|
|
1117
|
-
] })
|
|
1118
|
-
]
|
|
1119
|
-
}
|
|
1120
|
-
)
|
|
1121
|
-
] })
|
|
1122
|
-
]
|
|
420
|
+
children: /* @__PURE__ */ jsx3(Icon2, { size: 16, strokeWidth: 1.7 })
|
|
421
|
+
},
|
|
422
|
+
mode
|
|
423
|
+
)) }),
|
|
424
|
+
/* @__PURE__ */ jsx3(Separator, {}),
|
|
425
|
+
/* @__PURE__ */ jsx3(
|
|
426
|
+
SettingsButton,
|
|
427
|
+
{
|
|
428
|
+
open: settingsOpen,
|
|
429
|
+
onClick: () => setSettingsOpen((prev) => !prev),
|
|
430
|
+
selectedMode,
|
|
431
|
+
frameSettings,
|
|
432
|
+
onFrameSettingsChange
|
|
433
|
+
}
|
|
434
|
+
)
|
|
435
|
+
] })
|
|
1123
436
|
}
|
|
1124
437
|
);
|
|
1125
438
|
}
|
|
1126
439
|
function CloseButton({ onClick }) {
|
|
1127
|
-
const [hovered, setHovered] =
|
|
1128
|
-
return /* @__PURE__ */
|
|
440
|
+
const [hovered, setHovered] = useState3(false);
|
|
441
|
+
return /* @__PURE__ */ jsx3(
|
|
1129
442
|
"button",
|
|
1130
443
|
{
|
|
1131
444
|
onClick,
|
|
1132
445
|
onMouseEnter: () => setHovered(true),
|
|
1133
446
|
onMouseLeave: () => setHovered(false),
|
|
1134
|
-
style:
|
|
1135
|
-
|
|
447
|
+
style: {
|
|
448
|
+
width: 32,
|
|
449
|
+
height: 32,
|
|
450
|
+
borderRadius: 10,
|
|
451
|
+
border: "none",
|
|
452
|
+
background: hovered ? "rgba(255, 255, 255, 0.08)" : "transparent",
|
|
453
|
+
display: "flex",
|
|
454
|
+
alignItems: "center",
|
|
455
|
+
justifyContent: "center",
|
|
456
|
+
cursor: "pointer",
|
|
457
|
+
padding: 0,
|
|
458
|
+
color: hovered ? "rgba(255, 255, 255, 0.96)" : "rgba(255, 255, 255, 0.52)",
|
|
459
|
+
transition: "background 0.12s ease, color 0.12s ease"
|
|
460
|
+
},
|
|
461
|
+
children: /* @__PURE__ */ jsx3(X, { size: 16, strokeWidth: 1.7 })
|
|
1136
462
|
}
|
|
1137
463
|
);
|
|
1138
464
|
}
|
|
@@ -1142,9 +468,9 @@ function ModeButton({
|
|
|
1142
468
|
selected,
|
|
1143
469
|
onClick
|
|
1144
470
|
}) {
|
|
1145
|
-
const [hovered, setHovered] =
|
|
1146
|
-
return /* @__PURE__ */
|
|
1147
|
-
hovered && /* @__PURE__ */
|
|
471
|
+
const [hovered, setHovered] = useState3(false);
|
|
472
|
+
return /* @__PURE__ */ jsxs2("div", { style: { position: "relative" }, children: [
|
|
473
|
+
hovered && /* @__PURE__ */ jsx3(
|
|
1148
474
|
"div",
|
|
1149
475
|
{
|
|
1150
476
|
style: {
|
|
@@ -1167,16 +493,16 @@ function ModeButton({
|
|
|
1167
493
|
children: label
|
|
1168
494
|
}
|
|
1169
495
|
),
|
|
1170
|
-
/* @__PURE__ */
|
|
496
|
+
/* @__PURE__ */ jsx3(
|
|
1171
497
|
"button",
|
|
1172
498
|
{
|
|
1173
499
|
onClick,
|
|
1174
500
|
onMouseEnter: () => setHovered(true),
|
|
1175
501
|
onMouseLeave: () => setHovered(false),
|
|
1176
502
|
style: {
|
|
1177
|
-
width:
|
|
1178
|
-
height:
|
|
1179
|
-
borderRadius:
|
|
503
|
+
width: 32,
|
|
504
|
+
height: 32,
|
|
505
|
+
borderRadius: 10,
|
|
1180
506
|
border: "none",
|
|
1181
507
|
background: selected ? "rgba(255, 255, 255, 0.15)" : hovered ? "rgba(255, 255, 255, 0.08)" : "transparent",
|
|
1182
508
|
display: "flex",
|
|
@@ -1196,28 +522,61 @@ function SettingsButton({
|
|
|
1196
522
|
open,
|
|
1197
523
|
onClick,
|
|
1198
524
|
selectedMode,
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
frameOnBlackEnabled,
|
|
1202
|
-
onFrameOnBlackChange
|
|
525
|
+
frameSettings,
|
|
526
|
+
onFrameSettingsChange
|
|
1203
527
|
}) {
|
|
1204
|
-
const [hovered, setHovered] =
|
|
1205
|
-
const
|
|
1206
|
-
|
|
1207
|
-
|
|
528
|
+
const [hovered, setHovered] = useState3(false);
|
|
529
|
+
const [saveDir, setSaveDir] = useState3(null);
|
|
530
|
+
const [picking, setPicking] = useState3(false);
|
|
531
|
+
useEffect2(() => {
|
|
532
|
+
if (!open) return;
|
|
533
|
+
fetch("/__afterbefore/config").then((r) => r.json()).then((data) => setSaveDir(data.saveDir)).catch(() => {
|
|
534
|
+
});
|
|
535
|
+
}, [open]);
|
|
536
|
+
const handlePickFolder = async () => {
|
|
537
|
+
setPicking(true);
|
|
538
|
+
try {
|
|
539
|
+
const res = await fetch("/__afterbefore/pick-folder", { method: "POST" });
|
|
540
|
+
const data = await res.json();
|
|
541
|
+
if (data.folder) {
|
|
542
|
+
await fetch("/__afterbefore/config", {
|
|
543
|
+
method: "POST",
|
|
544
|
+
headers: { "Content-Type": "application/json" },
|
|
545
|
+
body: JSON.stringify({ saveDir: data.folder })
|
|
546
|
+
});
|
|
547
|
+
setSaveDir(data.folder);
|
|
548
|
+
}
|
|
549
|
+
} catch {
|
|
550
|
+
} finally {
|
|
551
|
+
setPicking(false);
|
|
552
|
+
}
|
|
553
|
+
};
|
|
554
|
+
const shortDir = saveDir ? saveDir.replace(/^\/Users\/[^/]+/, "~") : "~/Desktop";
|
|
555
|
+
return /* @__PURE__ */ jsxs2("div", { style: { position: "relative" }, children: [
|
|
556
|
+
/* @__PURE__ */ jsx3(
|
|
1208
557
|
"button",
|
|
1209
558
|
{
|
|
1210
559
|
onClick,
|
|
1211
560
|
onMouseEnter: () => setHovered(true),
|
|
1212
561
|
onMouseLeave: () => setHovered(false),
|
|
1213
562
|
style: {
|
|
1214
|
-
|
|
1215
|
-
|
|
563
|
+
width: 32,
|
|
564
|
+
height: 32,
|
|
565
|
+
borderRadius: "50%",
|
|
566
|
+
border: "none",
|
|
567
|
+
background: open || hovered ? "rgba(255, 255, 255, 0.12)" : "transparent",
|
|
568
|
+
display: "flex",
|
|
569
|
+
alignItems: "center",
|
|
570
|
+
justifyContent: "center",
|
|
571
|
+
cursor: "pointer",
|
|
572
|
+
padding: 0,
|
|
573
|
+
color: open || hovered ? "rgba(255, 255, 255, 0.96)" : "rgba(255, 255, 255, 0.52)",
|
|
574
|
+
transition: "background 0.12s ease, color 0.12s ease"
|
|
1216
575
|
},
|
|
1217
|
-
children: /* @__PURE__ */
|
|
576
|
+
children: /* @__PURE__ */ jsx3(Settings, { size: 16, strokeWidth: 1.7 })
|
|
1218
577
|
}
|
|
1219
578
|
),
|
|
1220
|
-
open && /* @__PURE__ */
|
|
579
|
+
open && /* @__PURE__ */ jsxs2(
|
|
1221
580
|
"div",
|
|
1222
581
|
{
|
|
1223
582
|
style: {
|
|
@@ -1225,7 +584,7 @@ function SettingsButton({
|
|
|
1225
584
|
left: "50%",
|
|
1226
585
|
bottom: "calc(100% + 12px)",
|
|
1227
586
|
transform: "translateX(-50%)",
|
|
1228
|
-
minWidth:
|
|
587
|
+
minWidth: 260,
|
|
1229
588
|
padding: "10px 12px",
|
|
1230
589
|
borderRadius: 12,
|
|
1231
590
|
background: "rgba(32, 32, 36, 0.96)",
|
|
@@ -1235,7 +594,7 @@ function SettingsButton({
|
|
|
1235
594
|
WebkitBackdropFilter: "blur(20px)"
|
|
1236
595
|
},
|
|
1237
596
|
children: [
|
|
1238
|
-
/* @__PURE__ */
|
|
597
|
+
/* @__PURE__ */ jsx3(
|
|
1239
598
|
"div",
|
|
1240
599
|
{
|
|
1241
600
|
style: {
|
|
@@ -1245,25 +604,46 @@ function SettingsButton({
|
|
|
1245
604
|
textTransform: "uppercase",
|
|
1246
605
|
marginBottom: 10
|
|
1247
606
|
},
|
|
1248
|
-
children:
|
|
1249
|
-
}
|
|
1250
|
-
),
|
|
1251
|
-
selectedMode === "area" && /* @__PURE__ */ jsx4(
|
|
1252
|
-
ToggleRow,
|
|
1253
|
-
{
|
|
1254
|
-
icon: /* @__PURE__ */ jsx4(Magnet, { size: 15, strokeWidth: 1.8 }),
|
|
1255
|
-
label: "Magnet snap",
|
|
1256
|
-
enabled: magnetEnabled,
|
|
1257
|
-
onChange: () => onMagnetChange(!magnetEnabled)
|
|
607
|
+
children: "Settings"
|
|
1258
608
|
}
|
|
1259
609
|
),
|
|
1260
|
-
selectedMode === "component" && /* @__PURE__ */
|
|
1261
|
-
|
|
610
|
+
selectedMode === "component" && /* @__PURE__ */ jsxs2(Fragment, { children: [
|
|
611
|
+
/* @__PURE__ */ jsx3(
|
|
612
|
+
ToggleRow,
|
|
613
|
+
{
|
|
614
|
+
icon: /* @__PURE__ */ jsx3(Frame, { size: 15, strokeWidth: 1.8 }),
|
|
615
|
+
label: "Frame",
|
|
616
|
+
enabled: frameSettings.enabled,
|
|
617
|
+
onChange: () => onFrameSettingsChange({ ...frameSettings, enabled: !frameSettings.enabled })
|
|
618
|
+
}
|
|
619
|
+
),
|
|
620
|
+
frameSettings.enabled && /* @__PURE__ */ jsxs2("div", { style: { marginTop: 8, display: "flex", flexDirection: "column", gap: 10 }, children: [
|
|
621
|
+
/* @__PURE__ */ jsx3(
|
|
622
|
+
FrameSizeControl,
|
|
623
|
+
{
|
|
624
|
+
size: frameSettings.size,
|
|
625
|
+
onChange: (size) => onFrameSettingsChange({ ...frameSettings, size })
|
|
626
|
+
}
|
|
627
|
+
),
|
|
628
|
+
/* @__PURE__ */ jsx3(
|
|
629
|
+
FrameBackgroundControl,
|
|
630
|
+
{
|
|
631
|
+
bgType: frameSettings.bgType,
|
|
632
|
+
bgColor: frameSettings.bgColor,
|
|
633
|
+
bgImage: frameSettings.bgImage,
|
|
634
|
+
frameSize: frameSettings.size,
|
|
635
|
+
onChange: (updates) => onFrameSettingsChange({ ...frameSettings, ...updates })
|
|
636
|
+
}
|
|
637
|
+
)
|
|
638
|
+
] })
|
|
639
|
+
] }),
|
|
640
|
+
selectedMode === "component" && /* @__PURE__ */ jsx3("div", { style: { height: 1, background: "rgba(255,255,255,0.08)", margin: "8px 0" } }),
|
|
641
|
+
/* @__PURE__ */ jsx3(
|
|
642
|
+
SaveLocationRow,
|
|
1262
643
|
{
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
onChange: () => onFrameOnBlackChange(!frameOnBlackEnabled)
|
|
644
|
+
dir: shortDir,
|
|
645
|
+
picking,
|
|
646
|
+
onPick: handlePickFolder
|
|
1267
647
|
}
|
|
1268
648
|
)
|
|
1269
649
|
]
|
|
@@ -1271,13 +651,390 @@ function SettingsButton({
|
|
|
1271
651
|
)
|
|
1272
652
|
] });
|
|
1273
653
|
}
|
|
654
|
+
function FrameSizeControl({
|
|
655
|
+
size,
|
|
656
|
+
onChange
|
|
657
|
+
}) {
|
|
658
|
+
const [sizeOpen, setSizeOpen] = useState3(false);
|
|
659
|
+
const currentPreset = FRAME_SIZE_PRESETS.find((p) => p.w === size.w && p.h === size.h);
|
|
660
|
+
const isCustom = !currentPreset;
|
|
661
|
+
return /* @__PURE__ */ jsxs2("div", { children: [
|
|
662
|
+
/* @__PURE__ */ jsx3(SettingsLabel, { children: "Size" }),
|
|
663
|
+
/* @__PURE__ */ jsx3("div", { style: { display: "flex", alignItems: "center", gap: 6, marginTop: 4 }, children: /* @__PURE__ */ jsxs2("div", { style: { position: "relative", flex: 1 }, children: [
|
|
664
|
+
/* @__PURE__ */ jsxs2(
|
|
665
|
+
"button",
|
|
666
|
+
{
|
|
667
|
+
onClick: () => setSizeOpen((prev) => !prev),
|
|
668
|
+
style: {
|
|
669
|
+
display: "flex",
|
|
670
|
+
alignItems: "center",
|
|
671
|
+
justifyContent: "space-between",
|
|
672
|
+
gap: 6,
|
|
673
|
+
width: "100%",
|
|
674
|
+
height: 30,
|
|
675
|
+
padding: "0 8px",
|
|
676
|
+
borderRadius: 7,
|
|
677
|
+
border: "1px solid rgba(255,255,255,0.1)",
|
|
678
|
+
background: "rgba(255,255,255,0.07)",
|
|
679
|
+
color: "rgba(255,255,255,0.88)",
|
|
680
|
+
cursor: "pointer",
|
|
681
|
+
fontSize: 12,
|
|
682
|
+
fontFamily: "inherit"
|
|
683
|
+
},
|
|
684
|
+
children: [
|
|
685
|
+
currentPreset ? currentPreset.label : "Custom",
|
|
686
|
+
/* @__PURE__ */ jsx3(ChevronDown, { size: 12, strokeWidth: 2 })
|
|
687
|
+
]
|
|
688
|
+
}
|
|
689
|
+
),
|
|
690
|
+
sizeOpen && /* @__PURE__ */ jsxs2(
|
|
691
|
+
"div",
|
|
692
|
+
{
|
|
693
|
+
style: {
|
|
694
|
+
position: "absolute",
|
|
695
|
+
bottom: "calc(100% + 4px)",
|
|
696
|
+
left: 0,
|
|
697
|
+
right: 0,
|
|
698
|
+
background: "rgba(32, 32, 36, 0.96)",
|
|
699
|
+
border: "1px solid rgba(255, 255, 255, 0.1)",
|
|
700
|
+
borderRadius: 8,
|
|
701
|
+
padding: "4px 0",
|
|
702
|
+
boxShadow: "0 10px 30px rgba(0, 0, 0, 0.3)",
|
|
703
|
+
backdropFilter: "blur(20px)",
|
|
704
|
+
WebkitBackdropFilter: "blur(20px)",
|
|
705
|
+
zIndex: 1
|
|
706
|
+
},
|
|
707
|
+
children: [
|
|
708
|
+
FRAME_SIZE_PRESETS.map((preset) => /* @__PURE__ */ jsx3(
|
|
709
|
+
DropItem,
|
|
710
|
+
{
|
|
711
|
+
active: !isCustom && preset.w === size.w && preset.h === size.h,
|
|
712
|
+
onClick: () => {
|
|
713
|
+
onChange({ w: preset.w, h: preset.h });
|
|
714
|
+
setSizeOpen(false);
|
|
715
|
+
},
|
|
716
|
+
children: preset.label
|
|
717
|
+
},
|
|
718
|
+
preset.label
|
|
719
|
+
)),
|
|
720
|
+
/* @__PURE__ */ jsx3(DropItem, { active: isCustom, onClick: () => setSizeOpen(false), children: "Custom" })
|
|
721
|
+
]
|
|
722
|
+
}
|
|
723
|
+
)
|
|
724
|
+
] }) }),
|
|
725
|
+
isCustom && /* @__PURE__ */ jsxs2("div", { style: { display: "flex", alignItems: "center", gap: 4, marginTop: 6 }, children: [
|
|
726
|
+
/* @__PURE__ */ jsx3(
|
|
727
|
+
NumInput,
|
|
728
|
+
{
|
|
729
|
+
value: size.w,
|
|
730
|
+
onChange: (v) => {
|
|
731
|
+
const n = parseInt(v, 10);
|
|
732
|
+
if (!Number.isNaN(n) && n > 0) onChange({ ...size, w: n });
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
),
|
|
736
|
+
/* @__PURE__ */ jsx3(StaticText, { children: "x" }),
|
|
737
|
+
/* @__PURE__ */ jsx3(
|
|
738
|
+
NumInput,
|
|
739
|
+
{
|
|
740
|
+
value: size.h,
|
|
741
|
+
onChange: (v) => {
|
|
742
|
+
const n = parseInt(v, 10);
|
|
743
|
+
if (!Number.isNaN(n) && n > 0) onChange({ ...size, h: n });
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
)
|
|
747
|
+
] })
|
|
748
|
+
] });
|
|
749
|
+
}
|
|
750
|
+
function FrameBackgroundControl({
|
|
751
|
+
bgType,
|
|
752
|
+
bgColor,
|
|
753
|
+
bgImage,
|
|
754
|
+
frameSize,
|
|
755
|
+
onChange
|
|
756
|
+
}) {
|
|
757
|
+
const fileInputRef = useRef2(null);
|
|
758
|
+
const handleFileSelect = (e) => {
|
|
759
|
+
const file = e.target.files?.[0];
|
|
760
|
+
if (!file) return;
|
|
761
|
+
const reader = new FileReader();
|
|
762
|
+
reader.onload = () => {
|
|
763
|
+
const dataUrl = reader.result;
|
|
764
|
+
if (dataUrl.length > 2 * 1024 * 1024) {
|
|
765
|
+
downscaleImage(dataUrl, frameSize.w, frameSize.h).then((scaled) => {
|
|
766
|
+
onChange({ bgType: "image", bgImage: scaled });
|
|
767
|
+
});
|
|
768
|
+
} else {
|
|
769
|
+
onChange({ bgType: "image", bgImage: dataUrl });
|
|
770
|
+
}
|
|
771
|
+
};
|
|
772
|
+
reader.readAsDataURL(file);
|
|
773
|
+
e.target.value = "";
|
|
774
|
+
};
|
|
775
|
+
return /* @__PURE__ */ jsxs2("div", { children: [
|
|
776
|
+
/* @__PURE__ */ jsx3(SettingsLabel, { children: "Background" }),
|
|
777
|
+
/* @__PURE__ */ jsxs2("div", { style: { display: "flex", gap: 2, marginTop: 4 }, children: [
|
|
778
|
+
/* @__PURE__ */ jsxs2(
|
|
779
|
+
SegmentButton,
|
|
780
|
+
{
|
|
781
|
+
active: bgType === "color",
|
|
782
|
+
onClick: () => onChange({ bgType: "color" }),
|
|
783
|
+
style: { borderRadius: "6px 0 0 6px" },
|
|
784
|
+
children: [
|
|
785
|
+
/* @__PURE__ */ jsx3(Palette, { size: 12, strokeWidth: 2 }),
|
|
786
|
+
"Color"
|
|
787
|
+
]
|
|
788
|
+
}
|
|
789
|
+
),
|
|
790
|
+
/* @__PURE__ */ jsxs2(
|
|
791
|
+
SegmentButton,
|
|
792
|
+
{
|
|
793
|
+
active: bgType === "image",
|
|
794
|
+
onClick: () => onChange({ bgType: "image" }),
|
|
795
|
+
style: { borderRadius: "0 6px 6px 0" },
|
|
796
|
+
children: [
|
|
797
|
+
/* @__PURE__ */ jsx3(ImageIcon, { size: 12, strokeWidth: 2 }),
|
|
798
|
+
"Image"
|
|
799
|
+
]
|
|
800
|
+
}
|
|
801
|
+
)
|
|
802
|
+
] }),
|
|
803
|
+
bgType === "color" && /* @__PURE__ */ jsxs2("div", { style: { display: "flex", alignItems: "center", gap: 8, marginTop: 6 }, children: [
|
|
804
|
+
/* @__PURE__ */ jsx3(ColorSwatch, { color: bgColor, onChange: (c) => onChange({ bgColor: c }) }),
|
|
805
|
+
/* @__PURE__ */ jsx3("span", { style: { fontSize: 12, color: "rgba(255,255,255,0.6)" }, children: bgColor })
|
|
806
|
+
] }),
|
|
807
|
+
bgType === "image" && /* @__PURE__ */ jsxs2("div", { style: { marginTop: 6 }, children: [
|
|
808
|
+
/* @__PURE__ */ jsx3(
|
|
809
|
+
"input",
|
|
810
|
+
{
|
|
811
|
+
ref: fileInputRef,
|
|
812
|
+
type: "file",
|
|
813
|
+
accept: "image/*",
|
|
814
|
+
onChange: handleFileSelect,
|
|
815
|
+
style: { display: "none" }
|
|
816
|
+
}
|
|
817
|
+
),
|
|
818
|
+
bgImage ? /* @__PURE__ */ jsxs2("div", { style: { display: "flex", alignItems: "center", gap: 8 }, children: [
|
|
819
|
+
/* @__PURE__ */ jsx3(
|
|
820
|
+
"img",
|
|
821
|
+
{
|
|
822
|
+
src: bgImage,
|
|
823
|
+
alt: "",
|
|
824
|
+
style: {
|
|
825
|
+
width: 48,
|
|
826
|
+
height: 28,
|
|
827
|
+
borderRadius: 4,
|
|
828
|
+
objectFit: "cover",
|
|
829
|
+
border: "1px solid rgba(255,255,255,0.12)"
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
),
|
|
833
|
+
/* @__PURE__ */ jsxs2(SmallButton, { onClick: () => fileInputRef.current?.click(), children: [
|
|
834
|
+
/* @__PURE__ */ jsx3(Upload, { size: 11, strokeWidth: 2 }),
|
|
835
|
+
"Replace"
|
|
836
|
+
] }),
|
|
837
|
+
/* @__PURE__ */ jsx3(SmallButton, { onClick: () => onChange({ bgImage: null }), children: /* @__PURE__ */ jsx3(Trash2, { size: 11, strokeWidth: 2 }) })
|
|
838
|
+
] }) : /* @__PURE__ */ jsxs2(SmallButton, { onClick: () => fileInputRef.current?.click(), children: [
|
|
839
|
+
/* @__PURE__ */ jsx3(Upload, { size: 11, strokeWidth: 2 }),
|
|
840
|
+
"Upload image"
|
|
841
|
+
] })
|
|
842
|
+
] })
|
|
843
|
+
] });
|
|
844
|
+
}
|
|
845
|
+
function SettingsLabel({ children }) {
|
|
846
|
+
return /* @__PURE__ */ jsx3(
|
|
847
|
+
"div",
|
|
848
|
+
{
|
|
849
|
+
style: {
|
|
850
|
+
fontSize: 11,
|
|
851
|
+
color: "rgba(255, 255, 255, 0.42)",
|
|
852
|
+
letterSpacing: "0.02em"
|
|
853
|
+
},
|
|
854
|
+
children
|
|
855
|
+
}
|
|
856
|
+
);
|
|
857
|
+
}
|
|
858
|
+
function SegmentButton({
|
|
859
|
+
children,
|
|
860
|
+
active,
|
|
861
|
+
onClick,
|
|
862
|
+
style
|
|
863
|
+
}) {
|
|
864
|
+
return /* @__PURE__ */ jsx3(
|
|
865
|
+
"button",
|
|
866
|
+
{
|
|
867
|
+
onClick,
|
|
868
|
+
style: {
|
|
869
|
+
display: "flex",
|
|
870
|
+
alignItems: "center",
|
|
871
|
+
gap: 4,
|
|
872
|
+
height: 26,
|
|
873
|
+
padding: "0 10px",
|
|
874
|
+
border: "1px solid rgba(255,255,255,0.1)",
|
|
875
|
+
background: active ? "rgba(255,255,255,0.14)" : "rgba(255,255,255,0.04)",
|
|
876
|
+
color: active ? "rgba(255,255,255,0.92)" : "rgba(255,255,255,0.5)",
|
|
877
|
+
cursor: "pointer",
|
|
878
|
+
fontSize: 11,
|
|
879
|
+
fontFamily: "inherit",
|
|
880
|
+
transition: "background 0.12s ease, color 0.12s ease",
|
|
881
|
+
...style
|
|
882
|
+
},
|
|
883
|
+
children
|
|
884
|
+
}
|
|
885
|
+
);
|
|
886
|
+
}
|
|
887
|
+
function ColorSwatch({
|
|
888
|
+
color,
|
|
889
|
+
onChange
|
|
890
|
+
}) {
|
|
891
|
+
const inputRef = useRef2(null);
|
|
892
|
+
return /* @__PURE__ */ jsxs2("div", { style: { position: "relative" }, children: [
|
|
893
|
+
/* @__PURE__ */ jsx3(
|
|
894
|
+
"button",
|
|
895
|
+
{
|
|
896
|
+
onClick: () => inputRef.current?.click(),
|
|
897
|
+
style: {
|
|
898
|
+
width: 24,
|
|
899
|
+
height: 24,
|
|
900
|
+
borderRadius: 6,
|
|
901
|
+
border: "1px solid rgba(255,255,255,0.18)",
|
|
902
|
+
background: color,
|
|
903
|
+
cursor: "pointer",
|
|
904
|
+
padding: 0
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
),
|
|
908
|
+
/* @__PURE__ */ jsx3(
|
|
909
|
+
"input",
|
|
910
|
+
{
|
|
911
|
+
ref: inputRef,
|
|
912
|
+
type: "color",
|
|
913
|
+
value: color,
|
|
914
|
+
onChange: (e) => onChange(e.target.value),
|
|
915
|
+
style: {
|
|
916
|
+
position: "absolute",
|
|
917
|
+
top: 0,
|
|
918
|
+
left: 0,
|
|
919
|
+
width: 0,
|
|
920
|
+
height: 0,
|
|
921
|
+
opacity: 0,
|
|
922
|
+
pointerEvents: "none"
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
)
|
|
926
|
+
] });
|
|
927
|
+
}
|
|
928
|
+
function SmallButton({
|
|
929
|
+
children,
|
|
930
|
+
onClick
|
|
931
|
+
}) {
|
|
932
|
+
const [hovered, setHovered] = useState3(false);
|
|
933
|
+
return /* @__PURE__ */ jsx3(
|
|
934
|
+
"button",
|
|
935
|
+
{
|
|
936
|
+
onClick,
|
|
937
|
+
onMouseEnter: () => setHovered(true),
|
|
938
|
+
onMouseLeave: () => setHovered(false),
|
|
939
|
+
style: {
|
|
940
|
+
display: "flex",
|
|
941
|
+
alignItems: "center",
|
|
942
|
+
gap: 4,
|
|
943
|
+
padding: "3px 8px",
|
|
944
|
+
borderRadius: 6,
|
|
945
|
+
border: "1px solid rgba(255,255,255,0.12)",
|
|
946
|
+
background: hovered ? "rgba(255,255,255,0.12)" : "rgba(255,255,255,0.06)",
|
|
947
|
+
color: "rgba(255,255,255,0.78)",
|
|
948
|
+
fontSize: 11,
|
|
949
|
+
cursor: "pointer",
|
|
950
|
+
fontFamily: "inherit",
|
|
951
|
+
transition: "background 0.12s ease"
|
|
952
|
+
},
|
|
953
|
+
children
|
|
954
|
+
}
|
|
955
|
+
);
|
|
956
|
+
}
|
|
957
|
+
async function downscaleImage(dataUrl, maxW, maxH) {
|
|
958
|
+
return new Promise((resolve) => {
|
|
959
|
+
const img = new Image();
|
|
960
|
+
img.onload = () => {
|
|
961
|
+
const canvas = document.createElement("canvas");
|
|
962
|
+
const scale = Math.min(maxW / img.width, maxH / img.height, 1);
|
|
963
|
+
canvas.width = img.width * scale;
|
|
964
|
+
canvas.height = img.height * scale;
|
|
965
|
+
const ctx = canvas.getContext("2d");
|
|
966
|
+
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
|
|
967
|
+
resolve(canvas.toDataURL("image/jpeg", 0.85));
|
|
968
|
+
};
|
|
969
|
+
img.onerror = () => resolve(dataUrl);
|
|
970
|
+
img.src = dataUrl;
|
|
971
|
+
});
|
|
972
|
+
}
|
|
973
|
+
function SaveLocationRow({
|
|
974
|
+
dir,
|
|
975
|
+
picking,
|
|
976
|
+
onPick
|
|
977
|
+
}) {
|
|
978
|
+
const [btnHovered, setBtnHovered] = useState3(false);
|
|
979
|
+
return /* @__PURE__ */ jsx3("div", { style: { display: "flex", flexDirection: "column", gap: 6 }, children: /* @__PURE__ */ jsxs2(
|
|
980
|
+
"div",
|
|
981
|
+
{
|
|
982
|
+
style: {
|
|
983
|
+
display: "flex",
|
|
984
|
+
alignItems: "center",
|
|
985
|
+
gap: 8,
|
|
986
|
+
color: "rgba(255, 255, 255, 0.88)",
|
|
987
|
+
fontSize: 13
|
|
988
|
+
},
|
|
989
|
+
children: [
|
|
990
|
+
/* @__PURE__ */ jsx3(FolderOpen, { size: 15, strokeWidth: 1.8, style: { flexShrink: 0 } }),
|
|
991
|
+
/* @__PURE__ */ jsx3(
|
|
992
|
+
"span",
|
|
993
|
+
{
|
|
994
|
+
style: {
|
|
995
|
+
overflow: "hidden",
|
|
996
|
+
textOverflow: "ellipsis",
|
|
997
|
+
whiteSpace: "nowrap",
|
|
998
|
+
flex: 1,
|
|
999
|
+
minWidth: 0
|
|
1000
|
+
},
|
|
1001
|
+
title: dir,
|
|
1002
|
+
children: dir
|
|
1003
|
+
}
|
|
1004
|
+
),
|
|
1005
|
+
/* @__PURE__ */ jsx3(
|
|
1006
|
+
"button",
|
|
1007
|
+
{
|
|
1008
|
+
onClick: onPick,
|
|
1009
|
+
disabled: picking,
|
|
1010
|
+
onMouseEnter: () => setBtnHovered(true),
|
|
1011
|
+
onMouseLeave: () => setBtnHovered(false),
|
|
1012
|
+
style: {
|
|
1013
|
+
padding: "3px 8px",
|
|
1014
|
+
borderRadius: 6,
|
|
1015
|
+
border: "1px solid rgba(255,255,255,0.12)",
|
|
1016
|
+
background: btnHovered ? "rgba(255,255,255,0.12)" : "rgba(255,255,255,0.06)",
|
|
1017
|
+
color: "rgba(255,255,255,0.78)",
|
|
1018
|
+
fontSize: 11,
|
|
1019
|
+
cursor: picking ? "wait" : "pointer",
|
|
1020
|
+
flexShrink: 0,
|
|
1021
|
+
fontFamily: "inherit",
|
|
1022
|
+
transition: "background 0.12s ease"
|
|
1023
|
+
},
|
|
1024
|
+
children: picking ? "..." : "Change"
|
|
1025
|
+
}
|
|
1026
|
+
)
|
|
1027
|
+
]
|
|
1028
|
+
}
|
|
1029
|
+
) });
|
|
1030
|
+
}
|
|
1274
1031
|
function ToggleRow({
|
|
1275
1032
|
icon,
|
|
1276
1033
|
label,
|
|
1277
1034
|
enabled,
|
|
1278
1035
|
onChange
|
|
1279
1036
|
}) {
|
|
1280
|
-
return /* @__PURE__ */
|
|
1037
|
+
return /* @__PURE__ */ jsxs2(
|
|
1281
1038
|
"label",
|
|
1282
1039
|
{
|
|
1283
1040
|
style: {
|
|
@@ -1288,7 +1045,7 @@ function ToggleRow({
|
|
|
1288
1045
|
cursor: "pointer"
|
|
1289
1046
|
},
|
|
1290
1047
|
children: [
|
|
1291
|
-
/* @__PURE__ */
|
|
1048
|
+
/* @__PURE__ */ jsxs2(
|
|
1292
1049
|
"span",
|
|
1293
1050
|
{
|
|
1294
1051
|
style: {
|
|
@@ -1305,7 +1062,7 @@ function ToggleRow({
|
|
|
1305
1062
|
]
|
|
1306
1063
|
}
|
|
1307
1064
|
),
|
|
1308
|
-
/* @__PURE__ */
|
|
1065
|
+
/* @__PURE__ */ jsx3(
|
|
1309
1066
|
"button",
|
|
1310
1067
|
{
|
|
1311
1068
|
type: "button",
|
|
@@ -1322,7 +1079,7 @@ function ToggleRow({
|
|
|
1322
1079
|
flexShrink: 0,
|
|
1323
1080
|
transition: "background 0.12s ease"
|
|
1324
1081
|
},
|
|
1325
|
-
children: /* @__PURE__ */
|
|
1082
|
+
children: /* @__PURE__ */ jsx3(
|
|
1326
1083
|
"span",
|
|
1327
1084
|
{
|
|
1328
1085
|
style: {
|
|
@@ -1343,85 +1100,18 @@ function ToggleRow({
|
|
|
1343
1100
|
}
|
|
1344
1101
|
);
|
|
1345
1102
|
}
|
|
1346
|
-
function CaptureButton({
|
|
1347
|
-
label,
|
|
1348
|
-
onClick
|
|
1349
|
-
}) {
|
|
1350
|
-
const [hovered, setHovered] = useState5(false);
|
|
1351
|
-
return /* @__PURE__ */ jsxs3(
|
|
1352
|
-
"button",
|
|
1353
|
-
{
|
|
1354
|
-
onClick,
|
|
1355
|
-
onMouseEnter: () => setHovered(true),
|
|
1356
|
-
onMouseLeave: () => setHovered(false),
|
|
1357
|
-
style: {
|
|
1358
|
-
padding: "0 18px",
|
|
1359
|
-
height: 40,
|
|
1360
|
-
borderRadius: 12,
|
|
1361
|
-
border: "none",
|
|
1362
|
-
background: hovered ? "#2f7bf8" : "#3b82f6",
|
|
1363
|
-
color: "white",
|
|
1364
|
-
fontSize: 13,
|
|
1365
|
-
fontWeight: 600,
|
|
1366
|
-
fontFamily: "inherit",
|
|
1367
|
-
cursor: "pointer",
|
|
1368
|
-
whiteSpace: "nowrap",
|
|
1369
|
-
transition: "background 0.12s ease",
|
|
1370
|
-
flexShrink: 0,
|
|
1371
|
-
display: "flex",
|
|
1372
|
-
alignItems: "center",
|
|
1373
|
-
gap: 8
|
|
1374
|
-
},
|
|
1375
|
-
children: [
|
|
1376
|
-
/* @__PURE__ */ jsx4(Camera2, { size: 16, strokeWidth: 1.9 }),
|
|
1377
|
-
label
|
|
1378
|
-
]
|
|
1379
|
-
}
|
|
1380
|
-
);
|
|
1381
|
-
}
|
|
1382
|
-
function ControlGroup({
|
|
1383
|
-
label,
|
|
1384
|
-
children
|
|
1385
|
-
}) {
|
|
1386
|
-
return /* @__PURE__ */ jsxs3(
|
|
1387
|
-
"div",
|
|
1388
|
-
{
|
|
1389
|
-
style: {
|
|
1390
|
-
display: "flex",
|
|
1391
|
-
alignItems: "center",
|
|
1392
|
-
gap: 6,
|
|
1393
|
-
padding: "0 4px"
|
|
1394
|
-
},
|
|
1395
|
-
children: [
|
|
1396
|
-
/* @__PURE__ */ jsx4(
|
|
1397
|
-
"span",
|
|
1398
|
-
{
|
|
1399
|
-
style: {
|
|
1400
|
-
fontSize: 11,
|
|
1401
|
-
color: "rgba(255, 255, 255, 0.42)",
|
|
1402
|
-
textTransform: "uppercase",
|
|
1403
|
-
letterSpacing: "0.03em"
|
|
1404
|
-
},
|
|
1405
|
-
children: label
|
|
1406
|
-
}
|
|
1407
|
-
),
|
|
1408
|
-
children
|
|
1409
|
-
]
|
|
1410
|
-
}
|
|
1411
|
-
);
|
|
1412
|
-
}
|
|
1413
1103
|
function NumInput({
|
|
1414
1104
|
value,
|
|
1415
1105
|
onChange
|
|
1416
1106
|
}) {
|
|
1417
|
-
const [editing, setEditing] =
|
|
1418
|
-
const [text, setText] =
|
|
1419
|
-
|
|
1107
|
+
const [editing, setEditing] = useState3(false);
|
|
1108
|
+
const [text, setText] = useState3(String(value));
|
|
1109
|
+
useEffect2(() => {
|
|
1420
1110
|
if (!editing) {
|
|
1421
1111
|
setText(String(value));
|
|
1422
1112
|
}
|
|
1423
1113
|
}, [editing, value]);
|
|
1424
|
-
return /* @__PURE__ */
|
|
1114
|
+
return /* @__PURE__ */ jsx3(
|
|
1425
1115
|
"input",
|
|
1426
1116
|
{
|
|
1427
1117
|
type: "text",
|
|
@@ -1456,7 +1146,7 @@ function NumInput({
|
|
|
1456
1146
|
);
|
|
1457
1147
|
}
|
|
1458
1148
|
function StaticText({ children }) {
|
|
1459
|
-
return /* @__PURE__ */
|
|
1149
|
+
return /* @__PURE__ */ jsx3(
|
|
1460
1150
|
"span",
|
|
1461
1151
|
{
|
|
1462
1152
|
style: {
|
|
@@ -1469,62 +1159,13 @@ function StaticText({ children }) {
|
|
|
1469
1159
|
}
|
|
1470
1160
|
);
|
|
1471
1161
|
}
|
|
1472
|
-
function DropButton({
|
|
1473
|
-
children,
|
|
1474
|
-
onClick,
|
|
1475
|
-
active
|
|
1476
|
-
}) {
|
|
1477
|
-
return /* @__PURE__ */ jsx4(
|
|
1478
|
-
"button",
|
|
1479
|
-
{
|
|
1480
|
-
onClick,
|
|
1481
|
-
style: {
|
|
1482
|
-
display: "flex",
|
|
1483
|
-
alignItems: "center",
|
|
1484
|
-
gap: 6,
|
|
1485
|
-
height: 34,
|
|
1486
|
-
padding: "0 10px",
|
|
1487
|
-
borderRadius: 10,
|
|
1488
|
-
border: "1px solid rgba(255,255,255,0.08)",
|
|
1489
|
-
background: "rgba(255,255,255,0.04)",
|
|
1490
|
-
color: active ? "rgba(125, 211, 252, 0.96)" : "rgba(255,255,255,0.78)",
|
|
1491
|
-
cursor: "pointer",
|
|
1492
|
-
fontSize: 12,
|
|
1493
|
-
fontFamily: "inherit"
|
|
1494
|
-
},
|
|
1495
|
-
children
|
|
1496
|
-
}
|
|
1497
|
-
);
|
|
1498
|
-
}
|
|
1499
|
-
function DropMenu({ children }) {
|
|
1500
|
-
return /* @__PURE__ */ jsx4(
|
|
1501
|
-
"div",
|
|
1502
|
-
{
|
|
1503
|
-
style: {
|
|
1504
|
-
position: "absolute",
|
|
1505
|
-
bottom: "calc(100% + 8px)",
|
|
1506
|
-
left: "50%",
|
|
1507
|
-
transform: "translateX(-50%)",
|
|
1508
|
-
minWidth: 120,
|
|
1509
|
-
background: "rgba(32, 32, 36, 0.96)",
|
|
1510
|
-
border: "1px solid rgba(255, 255, 255, 0.1)",
|
|
1511
|
-
borderRadius: 10,
|
|
1512
|
-
padding: "4px 0",
|
|
1513
|
-
boxShadow: "0 10px 30px rgba(0, 0, 0, 0.3)",
|
|
1514
|
-
backdropFilter: "blur(20px)",
|
|
1515
|
-
WebkitBackdropFilter: "blur(20px)"
|
|
1516
|
-
},
|
|
1517
|
-
children
|
|
1518
|
-
}
|
|
1519
|
-
);
|
|
1520
|
-
}
|
|
1521
1162
|
function DropItem({
|
|
1522
1163
|
children,
|
|
1523
1164
|
onClick,
|
|
1524
1165
|
active,
|
|
1525
1166
|
accent
|
|
1526
1167
|
}) {
|
|
1527
|
-
return /* @__PURE__ */
|
|
1168
|
+
return /* @__PURE__ */ jsx3(
|
|
1528
1169
|
"button",
|
|
1529
1170
|
{
|
|
1530
1171
|
onClick,
|
|
@@ -1544,56 +1185,29 @@ function DropItem({
|
|
|
1544
1185
|
}
|
|
1545
1186
|
);
|
|
1546
1187
|
}
|
|
1547
|
-
function MenuDivider() {
|
|
1548
|
-
return /* @__PURE__ */ jsx4(
|
|
1549
|
-
"div",
|
|
1550
|
-
{
|
|
1551
|
-
style: {
|
|
1552
|
-
height: 1,
|
|
1553
|
-
background: "rgba(255,255,255,0.08)",
|
|
1554
|
-
margin: "4px 0"
|
|
1555
|
-
}
|
|
1556
|
-
}
|
|
1557
|
-
);
|
|
1558
|
-
}
|
|
1559
1188
|
function Separator({ vertical = true }) {
|
|
1560
|
-
return /* @__PURE__ */
|
|
1189
|
+
return /* @__PURE__ */ jsx3(
|
|
1561
1190
|
"div",
|
|
1562
1191
|
{
|
|
1563
1192
|
style: {
|
|
1564
1193
|
width: vertical ? 1 : 24,
|
|
1565
|
-
height: vertical ?
|
|
1194
|
+
height: vertical ? 18 : 1,
|
|
1566
1195
|
background: "rgba(255,255,255,0.12)",
|
|
1567
|
-
flexShrink: 0
|
|
1196
|
+
flexShrink: 0,
|
|
1197
|
+
margin: vertical ? "0 6px" : "6px 0"
|
|
1568
1198
|
}
|
|
1569
1199
|
}
|
|
1570
1200
|
);
|
|
1571
1201
|
}
|
|
1572
|
-
function circleButtonStyle(active, round) {
|
|
1573
|
-
return {
|
|
1574
|
-
width: 40,
|
|
1575
|
-
height: 40,
|
|
1576
|
-
borderRadius: round ? "50%" : 12,
|
|
1577
|
-
border: "none",
|
|
1578
|
-
background: active ? "rgba(255, 255, 255, 0.12)" : "transparent",
|
|
1579
|
-
display: "flex",
|
|
1580
|
-
alignItems: "center",
|
|
1581
|
-
justifyContent: "center",
|
|
1582
|
-
cursor: "pointer",
|
|
1583
|
-
padding: 0,
|
|
1584
|
-
transition: "background 0.12s ease, color 0.12s ease",
|
|
1585
|
-
marginRight: round ? 6 : 0
|
|
1586
|
-
};
|
|
1587
|
-
}
|
|
1588
1202
|
|
|
1589
1203
|
// src/overlay/ui/inspector.tsx
|
|
1590
|
-
import { useEffect as
|
|
1591
|
-
import { Fragment as
|
|
1204
|
+
import { useEffect as useEffect3, useRef as useRef3, useCallback as useCallback3, useState as useState4 } from "react";
|
|
1205
|
+
import { Fragment as Fragment2, jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
1592
1206
|
function Inspector({ onSelect, onCancel }) {
|
|
1593
|
-
const [highlight, setHighlight] =
|
|
1207
|
+
const [highlight, setHighlight] = useState4(null);
|
|
1594
1208
|
const hoveredEl = useRef3(null);
|
|
1595
1209
|
const styleEl = useRef3(null);
|
|
1596
|
-
|
|
1210
|
+
useEffect3(() => {
|
|
1597
1211
|
const style = document.createElement("style");
|
|
1598
1212
|
style.setAttribute("data-afterbefore", "true");
|
|
1599
1213
|
style.textContent = "*, *::before, *::after { cursor: crosshair !important; }";
|
|
@@ -1603,7 +1217,7 @@ function Inspector({ onSelect, onCancel }) {
|
|
|
1603
1217
|
style.remove();
|
|
1604
1218
|
};
|
|
1605
1219
|
}, []);
|
|
1606
|
-
const isOverlayElement =
|
|
1220
|
+
const isOverlayElement = useCallback3((el) => {
|
|
1607
1221
|
let node = el;
|
|
1608
1222
|
while (node) {
|
|
1609
1223
|
if (node instanceof HTMLElement && node.dataset.afterbefore) return true;
|
|
@@ -1611,7 +1225,7 @@ function Inspector({ onSelect, onCancel }) {
|
|
|
1611
1225
|
}
|
|
1612
1226
|
return false;
|
|
1613
1227
|
}, []);
|
|
1614
|
-
const handleMouseMove =
|
|
1228
|
+
const handleMouseMove = useCallback3(
|
|
1615
1229
|
(e) => {
|
|
1616
1230
|
const el = document.elementFromPoint(e.clientX, e.clientY);
|
|
1617
1231
|
if (!el || !(el instanceof HTMLElement) || isOverlayElement(el)) {
|
|
@@ -1631,7 +1245,7 @@ function Inspector({ onSelect, onCancel }) {
|
|
|
1631
1245
|
},
|
|
1632
1246
|
[isOverlayElement]
|
|
1633
1247
|
);
|
|
1634
|
-
const handleClick =
|
|
1248
|
+
const handleClick = useCallback3(
|
|
1635
1249
|
(e) => {
|
|
1636
1250
|
e.preventDefault();
|
|
1637
1251
|
e.stopPropagation();
|
|
@@ -1642,7 +1256,7 @@ function Inspector({ onSelect, onCancel }) {
|
|
|
1642
1256
|
},
|
|
1643
1257
|
[onSelect]
|
|
1644
1258
|
);
|
|
1645
|
-
const handleKeyDown =
|
|
1259
|
+
const handleKeyDown = useCallback3(
|
|
1646
1260
|
(e) => {
|
|
1647
1261
|
if (e.key === "Escape") {
|
|
1648
1262
|
onCancel();
|
|
@@ -1650,7 +1264,7 @@ function Inspector({ onSelect, onCancel }) {
|
|
|
1650
1264
|
},
|
|
1651
1265
|
[onCancel]
|
|
1652
1266
|
);
|
|
1653
|
-
|
|
1267
|
+
useEffect3(() => {
|
|
1654
1268
|
document.addEventListener("mousemove", handleMouseMove, true);
|
|
1655
1269
|
document.addEventListener("click", handleClick, true);
|
|
1656
1270
|
document.addEventListener("keydown", handleKeyDown);
|
|
@@ -1660,81 +1274,59 @@ function Inspector({ onSelect, onCancel }) {
|
|
|
1660
1274
|
document.removeEventListener("keydown", handleKeyDown);
|
|
1661
1275
|
};
|
|
1662
1276
|
}, [handleMouseMove, handleClick, handleKeyDown]);
|
|
1663
|
-
return /* @__PURE__ */
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
{
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
pointerEvents: "none"
|
|
1678
|
-
}
|
|
1679
|
-
}
|
|
1680
|
-
),
|
|
1681
|
-
/* @__PURE__ */ jsx5(
|
|
1682
|
-
"div",
|
|
1683
|
-
{
|
|
1684
|
-
style: {
|
|
1685
|
-
position: "fixed",
|
|
1686
|
-
left: highlight.x,
|
|
1687
|
-
top: Math.max(0, highlight.y - 24),
|
|
1688
|
-
background: "rgba(59, 130, 246, 0.9)",
|
|
1689
|
-
color: "#fff",
|
|
1690
|
-
fontSize: 11,
|
|
1691
|
-
fontFamily: "system-ui, -apple-system, monospace",
|
|
1692
|
-
padding: "2px 6px",
|
|
1693
|
-
borderRadius: 3,
|
|
1694
|
-
pointerEvents: "none",
|
|
1695
|
-
whiteSpace: "nowrap",
|
|
1696
|
-
lineHeight: "18px"
|
|
1697
|
-
},
|
|
1698
|
-
children: highlight.tag
|
|
1277
|
+
return /* @__PURE__ */ jsx4("div", { "data-afterbefore": "true", style: { position: "fixed", inset: 0, zIndex: 2147483646, pointerEvents: "none" }, children: highlight && /* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
1278
|
+
/* @__PURE__ */ jsx4(
|
|
1279
|
+
"div",
|
|
1280
|
+
{
|
|
1281
|
+
style: {
|
|
1282
|
+
position: "fixed",
|
|
1283
|
+
left: highlight.x,
|
|
1284
|
+
top: highlight.y,
|
|
1285
|
+
width: highlight.width,
|
|
1286
|
+
height: highlight.height,
|
|
1287
|
+
background: "rgba(59, 130, 246, 0.15)",
|
|
1288
|
+
border: "2px solid rgba(59, 130, 246, 0.7)",
|
|
1289
|
+
borderRadius: 2,
|
|
1290
|
+
pointerEvents: "none"
|
|
1699
1291
|
}
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1292
|
+
}
|
|
1293
|
+
),
|
|
1294
|
+
/* @__PURE__ */ jsx4(
|
|
1703
1295
|
"div",
|
|
1704
1296
|
{
|
|
1705
1297
|
style: {
|
|
1706
1298
|
position: "fixed",
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
color: "
|
|
1711
|
-
fontSize:
|
|
1712
|
-
fontFamily: "system-ui, -apple-system,
|
|
1299
|
+
left: highlight.x,
|
|
1300
|
+
top: Math.max(0, highlight.y - 24),
|
|
1301
|
+
background: "rgba(59, 130, 246, 0.9)",
|
|
1302
|
+
color: "#fff",
|
|
1303
|
+
fontSize: 11,
|
|
1304
|
+
fontFamily: "system-ui, -apple-system, monospace",
|
|
1305
|
+
padding: "2px 6px",
|
|
1306
|
+
borderRadius: 3,
|
|
1713
1307
|
pointerEvents: "none",
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
padding: "8px 16px",
|
|
1717
|
-
borderRadius: 8
|
|
1308
|
+
whiteSpace: "nowrap",
|
|
1309
|
+
lineHeight: "18px"
|
|
1718
1310
|
},
|
|
1719
|
-
children:
|
|
1311
|
+
children: highlight.tag
|
|
1720
1312
|
}
|
|
1721
1313
|
)
|
|
1722
|
-
] });
|
|
1314
|
+
] }) });
|
|
1723
1315
|
}
|
|
1724
1316
|
|
|
1725
1317
|
// src/overlay/ui/status.tsx
|
|
1726
|
-
import { useState as
|
|
1727
|
-
import { jsx as
|
|
1318
|
+
import { useState as useState5, useRef as useRef4, useEffect as useEffect4, useCallback as useCallback4 } from "react";
|
|
1319
|
+
import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
1728
1320
|
var PANEL_WIDTH = 220;
|
|
1729
1321
|
function Status({ onReset, position, onClose }) {
|
|
1730
1322
|
const panelRef = useRef4(null);
|
|
1731
|
-
const [toast, setToast] =
|
|
1732
|
-
const [pushing, setPushing] =
|
|
1733
|
-
const showToast =
|
|
1323
|
+
const [toast, setToast] = useState5(null);
|
|
1324
|
+
const [pushing, setPushing] = useState5(false);
|
|
1325
|
+
const showToast = useCallback4((message, type) => {
|
|
1734
1326
|
setToast({ message, type });
|
|
1735
1327
|
setTimeout(() => setToast(null), 3e3);
|
|
1736
1328
|
}, []);
|
|
1737
|
-
|
|
1329
|
+
useEffect4(() => {
|
|
1738
1330
|
const handler = (e) => {
|
|
1739
1331
|
if (panelRef.current && !panelRef.current.contains(e.target)) {
|
|
1740
1332
|
onClose();
|
|
@@ -1743,7 +1335,7 @@ function Status({ onReset, position, onClose }) {
|
|
|
1743
1335
|
document.addEventListener("mousedown", handler);
|
|
1744
1336
|
return () => document.removeEventListener("mousedown", handler);
|
|
1745
1337
|
}, [onClose]);
|
|
1746
|
-
|
|
1338
|
+
useEffect4(() => {
|
|
1747
1339
|
const handler = (e) => {
|
|
1748
1340
|
if (e.key === "Escape") onClose();
|
|
1749
1341
|
};
|
|
@@ -1819,7 +1411,7 @@ function Status({ onReset, position, onClose }) {
|
|
|
1819
1411
|
const onLeave = (e) => {
|
|
1820
1412
|
e.currentTarget.style.background = "transparent";
|
|
1821
1413
|
};
|
|
1822
|
-
return /* @__PURE__ */
|
|
1414
|
+
return /* @__PURE__ */ jsxs4(
|
|
1823
1415
|
"div",
|
|
1824
1416
|
{
|
|
1825
1417
|
ref: panelRef,
|
|
@@ -1838,7 +1430,7 @@ function Status({ onReset, position, onClose }) {
|
|
|
1838
1430
|
overflow: "hidden"
|
|
1839
1431
|
},
|
|
1840
1432
|
children: [
|
|
1841
|
-
/* @__PURE__ */
|
|
1433
|
+
/* @__PURE__ */ jsx5(
|
|
1842
1434
|
"div",
|
|
1843
1435
|
{
|
|
1844
1436
|
style: {
|
|
@@ -1848,11 +1440,11 @@ function Status({ onReset, position, onClose }) {
|
|
|
1848
1440
|
color: "rgba(255,255,255,0.5)",
|
|
1849
1441
|
letterSpacing: "0.02em"
|
|
1850
1442
|
},
|
|
1851
|
-
children: "
|
|
1443
|
+
children: "Screenshot captured"
|
|
1852
1444
|
}
|
|
1853
1445
|
),
|
|
1854
|
-
/* @__PURE__ */
|
|
1855
|
-
/* @__PURE__ */
|
|
1446
|
+
/* @__PURE__ */ jsxs4("div", { style: { padding: "0 4px 4px" }, children: [
|
|
1447
|
+
/* @__PURE__ */ jsxs4(
|
|
1856
1448
|
"button",
|
|
1857
1449
|
{
|
|
1858
1450
|
style: buttonStyle,
|
|
@@ -1860,12 +1452,12 @@ function Status({ onReset, position, onClose }) {
|
|
|
1860
1452
|
onMouseEnter: onEnter,
|
|
1861
1453
|
onMouseLeave: onLeave,
|
|
1862
1454
|
children: [
|
|
1863
|
-
/* @__PURE__ */
|
|
1455
|
+
/* @__PURE__ */ jsx5(FolderIcon, {}),
|
|
1864
1456
|
"Open Folder"
|
|
1865
1457
|
]
|
|
1866
1458
|
}
|
|
1867
1459
|
),
|
|
1868
|
-
/* @__PURE__ */
|
|
1460
|
+
/* @__PURE__ */ jsxs4(
|
|
1869
1461
|
"button",
|
|
1870
1462
|
{
|
|
1871
1463
|
style: buttonStyle,
|
|
@@ -1873,12 +1465,12 @@ function Status({ onReset, position, onClose }) {
|
|
|
1873
1465
|
onMouseEnter: onEnter,
|
|
1874
1466
|
onMouseLeave: onLeave,
|
|
1875
1467
|
children: [
|
|
1876
|
-
/* @__PURE__ */
|
|
1468
|
+
/* @__PURE__ */ jsx5(CopyIcon, {}),
|
|
1877
1469
|
"Copy Markdown"
|
|
1878
1470
|
]
|
|
1879
1471
|
}
|
|
1880
1472
|
),
|
|
1881
|
-
/* @__PURE__ */
|
|
1473
|
+
/* @__PURE__ */ jsxs4(
|
|
1882
1474
|
"button",
|
|
1883
1475
|
{
|
|
1884
1476
|
style: buttonStyle,
|
|
@@ -1887,12 +1479,12 @@ function Status({ onReset, position, onClose }) {
|
|
|
1887
1479
|
onMouseEnter: onEnter,
|
|
1888
1480
|
onMouseLeave: onLeave,
|
|
1889
1481
|
children: [
|
|
1890
|
-
/* @__PURE__ */
|
|
1482
|
+
/* @__PURE__ */ jsx5(PushIcon, {}),
|
|
1891
1483
|
pushing ? "Pushing..." : "Push to PR"
|
|
1892
1484
|
]
|
|
1893
1485
|
}
|
|
1894
1486
|
),
|
|
1895
|
-
/* @__PURE__ */
|
|
1487
|
+
/* @__PURE__ */ jsx5(
|
|
1896
1488
|
"div",
|
|
1897
1489
|
{
|
|
1898
1490
|
style: {
|
|
@@ -1902,7 +1494,7 @@ function Status({ onReset, position, onClose }) {
|
|
|
1902
1494
|
}
|
|
1903
1495
|
}
|
|
1904
1496
|
),
|
|
1905
|
-
/* @__PURE__ */
|
|
1497
|
+
/* @__PURE__ */ jsxs4(
|
|
1906
1498
|
"button",
|
|
1907
1499
|
{
|
|
1908
1500
|
style: { ...buttonStyle, color: "rgba(255,255,255,0.5)" },
|
|
@@ -1910,13 +1502,13 @@ function Status({ onReset, position, onClose }) {
|
|
|
1910
1502
|
onMouseEnter: onEnter,
|
|
1911
1503
|
onMouseLeave: onLeave,
|
|
1912
1504
|
children: [
|
|
1913
|
-
/* @__PURE__ */
|
|
1505
|
+
/* @__PURE__ */ jsx5(ResetIcon, {}),
|
|
1914
1506
|
"Reset"
|
|
1915
1507
|
]
|
|
1916
1508
|
}
|
|
1917
1509
|
)
|
|
1918
1510
|
] }),
|
|
1919
|
-
toast && /* @__PURE__ */
|
|
1511
|
+
toast && /* @__PURE__ */ jsx5(
|
|
1920
1512
|
"div",
|
|
1921
1513
|
{
|
|
1922
1514
|
style: {
|
|
@@ -1942,14 +1534,14 @@ function Status({ onReset, position, onClose }) {
|
|
|
1942
1534
|
);
|
|
1943
1535
|
}
|
|
1944
1536
|
function FolderIcon() {
|
|
1945
|
-
return /* @__PURE__ */
|
|
1537
|
+
return /* @__PURE__ */ jsx5(
|
|
1946
1538
|
"svg",
|
|
1947
1539
|
{
|
|
1948
1540
|
width: "14",
|
|
1949
1541
|
height: "14",
|
|
1950
1542
|
viewBox: "0 0 14 14",
|
|
1951
1543
|
style: { color: "rgba(255,255,255,0.5)" },
|
|
1952
|
-
children: /* @__PURE__ */
|
|
1544
|
+
children: /* @__PURE__ */ jsx5(
|
|
1953
1545
|
"path",
|
|
1954
1546
|
{
|
|
1955
1547
|
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",
|
|
@@ -1962,7 +1554,7 @@ function FolderIcon() {
|
|
|
1962
1554
|
);
|
|
1963
1555
|
}
|
|
1964
1556
|
function CopyIcon() {
|
|
1965
|
-
return /* @__PURE__ */
|
|
1557
|
+
return /* @__PURE__ */ jsxs4(
|
|
1966
1558
|
"svg",
|
|
1967
1559
|
{
|
|
1968
1560
|
width: "14",
|
|
@@ -1970,7 +1562,7 @@ function CopyIcon() {
|
|
|
1970
1562
|
viewBox: "0 0 14 14",
|
|
1971
1563
|
style: { color: "rgba(255,255,255,0.5)" },
|
|
1972
1564
|
children: [
|
|
1973
|
-
/* @__PURE__ */
|
|
1565
|
+
/* @__PURE__ */ jsx5(
|
|
1974
1566
|
"rect",
|
|
1975
1567
|
{
|
|
1976
1568
|
x: "4",
|
|
@@ -1983,7 +1575,7 @@ function CopyIcon() {
|
|
|
1983
1575
|
strokeWidth: "1.3"
|
|
1984
1576
|
}
|
|
1985
1577
|
),
|
|
1986
|
-
/* @__PURE__ */
|
|
1578
|
+
/* @__PURE__ */ jsx5(
|
|
1987
1579
|
"path",
|
|
1988
1580
|
{
|
|
1989
1581
|
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",
|
|
@@ -1997,14 +1589,14 @@ function CopyIcon() {
|
|
|
1997
1589
|
);
|
|
1998
1590
|
}
|
|
1999
1591
|
function PushIcon() {
|
|
2000
|
-
return /* @__PURE__ */
|
|
1592
|
+
return /* @__PURE__ */ jsx5(
|
|
2001
1593
|
"svg",
|
|
2002
1594
|
{
|
|
2003
1595
|
width: "14",
|
|
2004
1596
|
height: "14",
|
|
2005
1597
|
viewBox: "0 0 14 14",
|
|
2006
1598
|
style: { color: "rgba(255,255,255,0.5)" },
|
|
2007
|
-
children: /* @__PURE__ */
|
|
1599
|
+
children: /* @__PURE__ */ jsx5(
|
|
2008
1600
|
"path",
|
|
2009
1601
|
{
|
|
2010
1602
|
d: "M7 11V3m0 0L4 6m3-3l3 3",
|
|
@@ -2019,7 +1611,7 @@ function PushIcon() {
|
|
|
2019
1611
|
);
|
|
2020
1612
|
}
|
|
2021
1613
|
function ResetIcon() {
|
|
2022
|
-
return /* @__PURE__ */
|
|
1614
|
+
return /* @__PURE__ */ jsxs4(
|
|
2023
1615
|
"svg",
|
|
2024
1616
|
{
|
|
2025
1617
|
width: "14",
|
|
@@ -2027,7 +1619,7 @@ function ResetIcon() {
|
|
|
2027
1619
|
viewBox: "0 0 14 14",
|
|
2028
1620
|
style: { color: "rgba(255,255,255,0.4)" },
|
|
2029
1621
|
children: [
|
|
2030
|
-
/* @__PURE__ */
|
|
1622
|
+
/* @__PURE__ */ jsx5(
|
|
2031
1623
|
"path",
|
|
2032
1624
|
{
|
|
2033
1625
|
d: "M2.5 7a4.5 4.5 0 118 2.5",
|
|
@@ -2037,7 +1629,7 @@ function ResetIcon() {
|
|
|
2037
1629
|
strokeLinecap: "round"
|
|
2038
1630
|
}
|
|
2039
1631
|
),
|
|
2040
|
-
/* @__PURE__ */
|
|
1632
|
+
/* @__PURE__ */ jsx5(
|
|
2041
1633
|
"path",
|
|
2042
1634
|
{
|
|
2043
1635
|
d: "M2.5 3v4h4",
|
|
@@ -2054,55 +1646,47 @@ function ResetIcon() {
|
|
|
2054
1646
|
}
|
|
2055
1647
|
|
|
2056
1648
|
// src/overlay/index.tsx
|
|
2057
|
-
import { jsx as
|
|
2058
|
-
async function saveCapture(
|
|
1649
|
+
import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1650
|
+
async function saveCapture(mode, dataUrl) {
|
|
2059
1651
|
try {
|
|
2060
1652
|
const res = await fetch("/__afterbefore/save", {
|
|
2061
1653
|
method: "POST",
|
|
2062
1654
|
headers: { "Content-Type": "application/json" },
|
|
2063
|
-
body: JSON.stringify({
|
|
1655
|
+
body: JSON.stringify({ mode, image: dataUrl })
|
|
2064
1656
|
});
|
|
2065
1657
|
if (!res.ok) throw new Error("Save failed");
|
|
2066
1658
|
} catch {
|
|
2067
1659
|
const link = document.createElement("a");
|
|
2068
|
-
link.download =
|
|
1660
|
+
link.download = "screenshot.png";
|
|
2069
1661
|
link.href = dataUrl;
|
|
2070
1662
|
link.click();
|
|
2071
1663
|
}
|
|
2072
1664
|
}
|
|
2073
1665
|
function AfterBefore() {
|
|
2074
1666
|
const { state, captureComplete, reset } = useOverlayState();
|
|
2075
|
-
const [statusOpen, setStatusOpen] =
|
|
2076
|
-
const [toolbarActive, setToolbarActive] =
|
|
2077
|
-
const [
|
|
2078
|
-
const [
|
|
2079
|
-
const [
|
|
2080
|
-
const [
|
|
2081
|
-
const [magnetEnabled, setMagnetEnabled] = useState8(true);
|
|
2082
|
-
const [areaRect, setAreaRect] = useState8(null);
|
|
2083
|
-
const [areaAspect, setAreaAspect] = useState8(DEFAULT_AREA_ASPECT);
|
|
2084
|
-
const [areaPresets, setAreaPresets] = useState8([]);
|
|
2085
|
-
const [frameOnBlackEnabled, setFrameOnBlackEnabled] = useState8(false);
|
|
1667
|
+
const [statusOpen, setStatusOpen] = useState6(false);
|
|
1668
|
+
const [toolbarActive, setToolbarActive] = useState6(false);
|
|
1669
|
+
const [inspectorActive, setInspectorActive] = useState6(false);
|
|
1670
|
+
const [loading, setLoading] = useState6(false);
|
|
1671
|
+
const [selectedMode, setSelectedMode] = useState6("component");
|
|
1672
|
+
const [frameSettings, setFrameSettings] = useState6(DEFAULT_FRAME_SETTINGS);
|
|
2086
1673
|
const iconPos = useRef5({ x: 24, y: 0 });
|
|
2087
|
-
|
|
2088
|
-
try {
|
|
2089
|
-
setMagnetEnabled(localStorage.getItem("ab-magnet") !== "false");
|
|
2090
|
-
} catch {
|
|
2091
|
-
setMagnetEnabled(true);
|
|
2092
|
-
}
|
|
2093
|
-
try {
|
|
2094
|
-
const savedPresets = localStorage.getItem("ab-area-presets");
|
|
2095
|
-
setAreaPresets(savedPresets ? JSON.parse(savedPresets) : []);
|
|
2096
|
-
} catch {
|
|
2097
|
-
setAreaPresets([]);
|
|
2098
|
-
}
|
|
1674
|
+
useEffect5(() => {
|
|
2099
1675
|
try {
|
|
2100
|
-
|
|
1676
|
+
const stored = localStorage.getItem("ab-frame-settings");
|
|
1677
|
+
if (stored) {
|
|
1678
|
+
setFrameSettings({ ...DEFAULT_FRAME_SETTINGS, ...JSON.parse(stored) });
|
|
1679
|
+
} else if (localStorage.getItem("ab-frame-black") === "true") {
|
|
1680
|
+
const migrated = { ...DEFAULT_FRAME_SETTINGS, enabled: true };
|
|
1681
|
+
setFrameSettings(migrated);
|
|
1682
|
+
localStorage.setItem("ab-frame-settings", JSON.stringify(migrated));
|
|
1683
|
+
localStorage.removeItem("ab-frame-black");
|
|
1684
|
+
}
|
|
2101
1685
|
} catch {
|
|
2102
|
-
|
|
1686
|
+
setFrameSettings(DEFAULT_FRAME_SETTINGS);
|
|
2103
1687
|
}
|
|
2104
1688
|
}, []);
|
|
2105
|
-
|
|
1689
|
+
useEffect5(() => {
|
|
2106
1690
|
if (state.phase === "ready") {
|
|
2107
1691
|
const timer = setTimeout(() => {
|
|
2108
1692
|
reset();
|
|
@@ -2111,43 +1695,37 @@ function AfterBefore() {
|
|
|
2111
1695
|
return () => clearTimeout(timer);
|
|
2112
1696
|
}
|
|
2113
1697
|
}, [state.phase, reset]);
|
|
2114
|
-
const handlePositionChange =
|
|
1698
|
+
const handlePositionChange = useCallback5(
|
|
2115
1699
|
(pos) => {
|
|
2116
1700
|
iconPos.current = pos;
|
|
2117
1701
|
},
|
|
2118
1702
|
[]
|
|
2119
1703
|
);
|
|
2120
|
-
const handleIconClick =
|
|
1704
|
+
const handleIconClick = useCallback5(() => {
|
|
2121
1705
|
if (loading) return;
|
|
2122
1706
|
if (state.phase === "ready") {
|
|
2123
1707
|
setStatusOpen((prev) => !prev);
|
|
2124
1708
|
} else if (toolbarActive || inspectorActive) {
|
|
2125
1709
|
setToolbarActive(false);
|
|
2126
|
-
setSelectorActive(false);
|
|
2127
1710
|
setInspectorActive(false);
|
|
2128
1711
|
setStatusOpen(false);
|
|
2129
1712
|
} else {
|
|
2130
1713
|
setStatusOpen(false);
|
|
2131
1714
|
if (selectedMode === "component") {
|
|
1715
|
+
setToolbarActive(true);
|
|
2132
1716
|
setInspectorActive(true);
|
|
2133
1717
|
} else {
|
|
2134
1718
|
setToolbarActive(true);
|
|
2135
1719
|
setInspectorActive(false);
|
|
2136
|
-
if (selectedMode === "area") {
|
|
2137
|
-
setSelectorActive(true);
|
|
2138
|
-
setAreaRect(createInitialAreaRect());
|
|
2139
|
-
setAreaAspect(DEFAULT_AREA_ASPECT);
|
|
2140
|
-
}
|
|
2141
1720
|
}
|
|
2142
1721
|
}
|
|
2143
1722
|
}, [state.phase, loading, toolbarActive, inspectorActive, selectedMode]);
|
|
2144
|
-
const performCapture =
|
|
2145
|
-
async (mode,
|
|
1723
|
+
const performCapture = useCallback5(
|
|
1724
|
+
async (mode, element) => {
|
|
2146
1725
|
setLoading(true);
|
|
2147
1726
|
try {
|
|
2148
|
-
const dataUrl = await capture({ mode,
|
|
2149
|
-
|
|
2150
|
-
await saveCapture(type, mode, dataUrl);
|
|
1727
|
+
const dataUrl = await capture({ mode, element, frameSettings });
|
|
1728
|
+
await saveCapture(mode, dataUrl);
|
|
2151
1729
|
captureComplete({
|
|
2152
1730
|
dataUrl,
|
|
2153
1731
|
mode,
|
|
@@ -2159,160 +1737,59 @@ function AfterBefore() {
|
|
|
2159
1737
|
setLoading(false);
|
|
2160
1738
|
}
|
|
2161
1739
|
},
|
|
2162
|
-
[
|
|
1740
|
+
[captureComplete, frameSettings]
|
|
2163
1741
|
);
|
|
2164
|
-
const handleToolbarCapture =
|
|
1742
|
+
const handleToolbarCapture = useCallback5(
|
|
2165
1743
|
(mode) => {
|
|
2166
|
-
if (mode === "
|
|
2167
|
-
if (areaRect) {
|
|
2168
|
-
setSelectorActive(false);
|
|
2169
|
-
setToolbarActive(false);
|
|
2170
|
-
performCapture("area", {
|
|
2171
|
-
x: Math.round(areaRect.x),
|
|
2172
|
-
y: Math.round(areaRect.y),
|
|
2173
|
-
width: Math.round(areaRect.w),
|
|
2174
|
-
height: Math.round(areaRect.h)
|
|
2175
|
-
});
|
|
2176
|
-
}
|
|
2177
|
-
return;
|
|
2178
|
-
} else if (mode === "viewport") {
|
|
1744
|
+
if (mode === "viewport") {
|
|
2179
1745
|
setToolbarActive(false);
|
|
2180
1746
|
performCapture("viewport");
|
|
2181
1747
|
} else if (mode === "fullpage") {
|
|
2182
1748
|
setToolbarActive(false);
|
|
2183
1749
|
performCapture("fullpage");
|
|
2184
1750
|
} else if (mode === "component") {
|
|
2185
|
-
setToolbarActive(false);
|
|
2186
1751
|
setInspectorActive(true);
|
|
2187
1752
|
}
|
|
2188
1753
|
},
|
|
2189
|
-
[
|
|
1754
|
+
[performCapture]
|
|
2190
1755
|
);
|
|
2191
|
-
const handleToolbarCancel =
|
|
1756
|
+
const handleToolbarCancel = useCallback5(() => {
|
|
2192
1757
|
setToolbarActive(false);
|
|
2193
|
-
setSelectorActive(false);
|
|
2194
1758
|
}, []);
|
|
2195
|
-
const handleComponentSelect =
|
|
1759
|
+
const handleComponentSelect = useCallback5(
|
|
2196
1760
|
(element) => {
|
|
2197
1761
|
setInspectorActive(false);
|
|
2198
|
-
performCapture("component", void 0, element);
|
|
2199
|
-
},
|
|
2200
|
-
[performCapture]
|
|
2201
|
-
);
|
|
2202
|
-
const handleComponentCancel = useCallback6(() => {
|
|
2203
|
-
setInspectorActive(false);
|
|
2204
|
-
setToolbarActive(true);
|
|
2205
|
-
}, []);
|
|
2206
|
-
const handleAreaSelect = useCallback6(
|
|
2207
|
-
(area) => {
|
|
2208
|
-
setSelectorActive(false);
|
|
2209
1762
|
setToolbarActive(false);
|
|
2210
|
-
performCapture("
|
|
1763
|
+
performCapture("component", element);
|
|
2211
1764
|
},
|
|
2212
1765
|
[performCapture]
|
|
2213
1766
|
);
|
|
2214
|
-
const
|
|
2215
|
-
|
|
1767
|
+
const handleComponentCancel = useCallback5(() => {
|
|
1768
|
+
setInspectorActive(false);
|
|
2216
1769
|
setToolbarActive(true);
|
|
2217
1770
|
}, []);
|
|
2218
|
-
const
|
|
2219
|
-
|
|
2220
|
-
try {
|
|
2221
|
-
localStorage.setItem("ab-magnet", String(enabled));
|
|
2222
|
-
} catch {
|
|
2223
|
-
}
|
|
2224
|
-
}, []);
|
|
2225
|
-
const handleFrameOnBlackChange = useCallback6((enabled) => {
|
|
2226
|
-
setFrameOnBlackEnabled(enabled);
|
|
1771
|
+
const handleFrameSettingsChange = useCallback5((next) => {
|
|
1772
|
+
setFrameSettings(next);
|
|
2227
1773
|
try {
|
|
2228
|
-
localStorage.setItem("ab-frame-
|
|
1774
|
+
localStorage.setItem("ab-frame-settings", JSON.stringify(next));
|
|
2229
1775
|
} catch {
|
|
2230
1776
|
}
|
|
2231
1777
|
}, []);
|
|
2232
|
-
const handleModeChange =
|
|
1778
|
+
const handleModeChange = useCallback5((mode) => {
|
|
2233
1779
|
setSelectedMode(mode);
|
|
2234
|
-
if (mode === "
|
|
2235
|
-
|
|
2236
|
-
setAreaRect(createInitialAreaRect());
|
|
2237
|
-
setAreaAspect(DEFAULT_AREA_ASPECT);
|
|
2238
|
-
} else {
|
|
2239
|
-
setSelectorActive(false);
|
|
2240
|
-
}
|
|
2241
|
-
}, []);
|
|
2242
|
-
const handleAreaRectChange = useCallback6((nextRect) => {
|
|
2243
|
-
setAreaRect(nextRect);
|
|
2244
|
-
}, []);
|
|
2245
|
-
const handleAreaSizeChange = useCallback6(
|
|
2246
|
-
(field, value) => {
|
|
2247
|
-
const nextValue = parseInt(value, 10);
|
|
2248
|
-
if (Number.isNaN(nextValue) || nextValue < 20 || !areaRect) {
|
|
2249
|
-
return;
|
|
2250
|
-
}
|
|
2251
|
-
const aspectRatio = areaAspect === "Free" ? 0 : { "16:9": 16 / 9, "4:3": 4 / 3, "1:1": 1, "3:2": 3 / 2, "21:9": 21 / 9 }[areaAspect];
|
|
2252
|
-
if (field === "w") {
|
|
2253
|
-
setAreaRect({
|
|
2254
|
-
...areaRect,
|
|
2255
|
-
w: nextValue,
|
|
2256
|
-
h: aspectRatio > 0 ? nextValue / aspectRatio : areaRect.h
|
|
2257
|
-
});
|
|
2258
|
-
} else {
|
|
2259
|
-
setAreaRect({
|
|
2260
|
-
...areaRect,
|
|
2261
|
-
h: nextValue,
|
|
2262
|
-
w: aspectRatio > 0 ? nextValue * aspectRatio : areaRect.w
|
|
2263
|
-
});
|
|
2264
|
-
}
|
|
2265
|
-
},
|
|
2266
|
-
[areaAspect, areaRect]
|
|
2267
|
-
);
|
|
2268
|
-
const handleAreaPositionChange = useCallback6(
|
|
2269
|
-
(field, value) => {
|
|
2270
|
-
const nextValue = parseInt(value, 10);
|
|
2271
|
-
if (Number.isNaN(nextValue) || !areaRect) {
|
|
2272
|
-
return;
|
|
2273
|
-
}
|
|
2274
|
-
setAreaRect({ ...areaRect, [field]: nextValue });
|
|
2275
|
-
},
|
|
2276
|
-
[areaRect]
|
|
2277
|
-
);
|
|
2278
|
-
const handleAreaAspectChange = useCallback6(
|
|
2279
|
-
(nextAspect) => {
|
|
2280
|
-
setAreaAspect(nextAspect);
|
|
2281
|
-
if (!areaRect || nextAspect === "Free") {
|
|
2282
|
-
return;
|
|
2283
|
-
}
|
|
2284
|
-
const aspectRatio = { "16:9": 16 / 9, "4:3": 4 / 3, "1:1": 1, "3:2": 3 / 2, "21:9": 21 / 9 }[nextAspect];
|
|
2285
|
-
setAreaRect({ ...areaRect, h: areaRect.w / aspectRatio });
|
|
2286
|
-
},
|
|
2287
|
-
[areaRect]
|
|
2288
|
-
);
|
|
2289
|
-
const handleAreaSavePreset = useCallback6(() => {
|
|
2290
|
-
if (!areaRect) {
|
|
2291
|
-
return;
|
|
2292
|
-
}
|
|
2293
|
-
const label = `${Math.round(areaRect.w)}x${Math.round(areaRect.h)}`;
|
|
2294
|
-
const nextPresets = [
|
|
2295
|
-
...areaPresets.filter((preset) => preset.label !== label),
|
|
2296
|
-
{ label, rect: { ...areaRect } }
|
|
2297
|
-
];
|
|
2298
|
-
setAreaPresets(nextPresets);
|
|
2299
|
-
try {
|
|
2300
|
-
localStorage.setItem("ab-area-presets", JSON.stringify(nextPresets));
|
|
2301
|
-
} catch {
|
|
1780
|
+
if (mode === "component") {
|
|
1781
|
+
setInspectorActive(true);
|
|
2302
1782
|
}
|
|
2303
|
-
}, [areaPresets, areaRect]);
|
|
2304
|
-
const handleAreaLoadPreset = useCallback6((preset) => {
|
|
2305
|
-
setAreaRect({ ...preset.rect });
|
|
2306
1783
|
}, []);
|
|
2307
|
-
const handleReset =
|
|
1784
|
+
const handleReset = useCallback5(() => {
|
|
2308
1785
|
reset();
|
|
2309
1786
|
setStatusOpen(false);
|
|
2310
1787
|
}, [reset]);
|
|
2311
|
-
const handleStatusClose =
|
|
1788
|
+
const handleStatusClose = useCallback5(() => {
|
|
2312
1789
|
setStatusOpen(false);
|
|
2313
1790
|
}, []);
|
|
2314
|
-
return /* @__PURE__ */
|
|
2315
|
-
/* @__PURE__ */
|
|
1791
|
+
return /* @__PURE__ */ jsxs5("div", { "data-afterbefore": "true", children: [
|
|
1792
|
+
/* @__PURE__ */ jsx6(
|
|
2316
1793
|
Icon,
|
|
2317
1794
|
{
|
|
2318
1795
|
phase: state.phase,
|
|
@@ -2321,48 +1798,26 @@ function AfterBefore() {
|
|
|
2321
1798
|
onPositionChange: handlePositionChange
|
|
2322
1799
|
}
|
|
2323
1800
|
),
|
|
2324
|
-
toolbarActive && !
|
|
1801
|
+
toolbarActive && !inspectorActive && !loading && /* @__PURE__ */ jsx6(
|
|
2325
1802
|
CapturePreview,
|
|
2326
1803
|
{
|
|
2327
1804
|
mode: selectedMode,
|
|
2328
1805
|
onClick: () => handleToolbarCapture(selectedMode)
|
|
2329
1806
|
}
|
|
2330
1807
|
),
|
|
2331
|
-
toolbarActive &&
|
|
1808
|
+
toolbarActive && /* @__PURE__ */ jsx6(
|
|
2332
1809
|
Toolbar,
|
|
2333
1810
|
{
|
|
2334
1811
|
selectedMode,
|
|
2335
1812
|
onModeChange: handleModeChange,
|
|
2336
1813
|
onCapture: handleToolbarCapture,
|
|
2337
1814
|
onCancel: handleToolbarCancel,
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
areaSelectionActive: selectorActive,
|
|
2341
|
-
areaRect,
|
|
2342
|
-
areaAspect,
|
|
2343
|
-
areaPresets,
|
|
2344
|
-
onAreaSizeChange: handleAreaSizeChange,
|
|
2345
|
-
onAreaPositionChange: handleAreaPositionChange,
|
|
2346
|
-
onAreaAspectChange: handleAreaAspectChange,
|
|
2347
|
-
onAreaSavePreset: handleAreaSavePreset,
|
|
2348
|
-
onAreaLoadPreset: handleAreaLoadPreset,
|
|
2349
|
-
frameOnBlackEnabled,
|
|
2350
|
-
onFrameOnBlackChange: handleFrameOnBlackChange
|
|
2351
|
-
}
|
|
2352
|
-
),
|
|
2353
|
-
selectorActive && /* @__PURE__ */ jsx7(
|
|
2354
|
-
Selector,
|
|
2355
|
-
{
|
|
2356
|
-
rect: areaRect,
|
|
2357
|
-
aspect: areaAspect,
|
|
2358
|
-
onRectChange: handleAreaRectChange,
|
|
2359
|
-
onSelect: handleAreaSelect,
|
|
2360
|
-
onCancel: handleAreaCancel,
|
|
2361
|
-
magnetEnabled
|
|
1815
|
+
frameSettings,
|
|
1816
|
+
onFrameSettingsChange: handleFrameSettingsChange
|
|
2362
1817
|
}
|
|
2363
1818
|
),
|
|
2364
|
-
inspectorActive && /* @__PURE__ */
|
|
2365
|
-
statusOpen && state.phase === "ready" && /* @__PURE__ */
|
|
1819
|
+
inspectorActive && /* @__PURE__ */ jsx6(Inspector, { onSelect: handleComponentSelect, onCancel: handleComponentCancel }),
|
|
1820
|
+
statusOpen && state.phase === "ready" && /* @__PURE__ */ jsx6(
|
|
2366
1821
|
Status,
|
|
2367
1822
|
{
|
|
2368
1823
|
onReset: handleReset,
|