afterbefore 0.2.13 → 0.2.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
 
3
3
  // src/overlay/index.tsx
4
- import { useState as useState6, useCallback as useCallback5, useRef as useRef5, useEffect as useEffect5 } from "react";
4
+ import { useState as useState5, useCallback as useCallback5, useRef as useRef4, useEffect as useEffect4 } from "react";
5
5
 
6
6
  // src/overlay/state.ts
7
7
  import { useState, useCallback } from "react";
@@ -181,11 +181,13 @@ function loadImage(src) {
181
181
  import { useRef, useCallback as useCallback2, useEffect, useState as useState2 } from "react";
182
182
  import { Camera, Check, LoaderCircle } from "lucide-react";
183
183
  import { jsx, jsxs } from "react/jsx-runtime";
184
- var ICON_SIZE = 32;
184
+ var CONTAINER_SIZE = 38;
185
+ var ICON_SIZE = CONTAINER_SIZE;
185
186
  var EDGE_MARGIN = 24;
186
187
  function Icon({ phase, onClick, loading, onPositionChange }) {
187
188
  const ref = useRef(null);
188
189
  const [pos, setPos] = useState2({ x: -1, y: -1 });
190
+ const [dragging, setDragging] = useState2(false);
189
191
  const dragState = useRef(null);
190
192
  useEffect(() => {
191
193
  setPos((prev) => {
@@ -206,6 +208,7 @@ function Icon({ phase, onClick, loading, onPositionChange }) {
206
208
  const handleMouseDown = useCallback2(
207
209
  (e) => {
208
210
  e.preventDefault();
211
+ setDragging(true);
209
212
  dragState.current = {
210
213
  dragging: true,
211
214
  startX: e.clientX,
@@ -240,6 +243,7 @@ function Icon({ phase, onClick, loading, onPositionChange }) {
240
243
  if (ds.distance < 5) {
241
244
  onClick();
242
245
  }
246
+ setDragging(false);
243
247
  dragState.current = null;
244
248
  };
245
249
  window.addEventListener("mousemove", handleMouseMove);
@@ -260,17 +264,17 @@ function Icon({ phase, onClick, loading, onPositionChange }) {
260
264
  position: "fixed",
261
265
  left: pos.x,
262
266
  top: pos.y,
263
- width: ICON_SIZE,
264
- height: ICON_SIZE,
267
+ width: CONTAINER_SIZE,
268
+ height: CONTAINER_SIZE,
265
269
  borderRadius: "50%",
266
270
  background: "rgba(32, 32, 36, 0.92)",
267
271
  backdropFilter: "blur(20px)",
268
272
  WebkitBackdropFilter: "blur(20px)",
269
- border: "1px solid rgba(255, 255, 255, 0.1)",
273
+ border: "none",
270
274
  display: "flex",
271
275
  alignItems: "center",
272
276
  justifyContent: "center",
273
- cursor: "grab",
277
+ cursor: dragging ? "grabbing" : "pointer",
274
278
  zIndex: 2147483647,
275
279
  boxShadow: "0 8px 32px rgba(0, 0, 0, 0.4)",
276
280
  transition: "background 0.15s",
@@ -310,7 +314,7 @@ function Icon({ phase, onClick, loading, onPositionChange }) {
310
314
 
311
315
  // src/overlay/ui/preview.tsx
312
316
  import { jsx as jsx2 } from "react/jsx-runtime";
313
- 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`;
317
+ var CAMERA_CURSOR = `url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='32' height='32' viewBox='0 0 32 32'%3E%3Cpath d='M6 12h4l2-3h8l2 3h4a3 3 0 0 1 3 3v9a3 3 0 0 1-3 3H6a3 3 0 0 1-3-3v-9a3 3 0 0 1 3-3z' fill='black' fill-opacity='0.25' transform='translate(0,1)'/%3E%3Cpath d='M6 12h4l2-3h8l2 3h4a3 3 0 0 1 3 3v9a3 3 0 0 1-3 3H6a3 3 0 0 1-3-3v-9a3 3 0 0 1 3-3z' fill='white'/%3E%3Ccircle cx='16' cy='19' r='4.5' fill='none' stroke='%23555' stroke-width='1.5'/%3E%3Ccircle cx='16' cy='19' r='1.5' fill='%23999'/%3E%3C/svg%3E") 16 16, pointer`;
314
318
  function CapturePreview({ mode, onClick }) {
315
319
  if (mode === "viewport" || mode === "fullpage") {
316
320
  return /* @__PURE__ */ jsx2(
@@ -323,8 +327,7 @@ function CapturePreview({ mode, onClick }) {
323
327
  inset: 0,
324
328
  zIndex: 2147483645,
325
329
  cursor: CAMERA_CURSOR,
326
- background: "rgba(59, 130, 246, 0.15)",
327
- boxShadow: "inset 0 0 0 2px rgba(59, 130, 246, 0.7)"
330
+ background: "rgba(59, 130, 246, 0.15)"
328
331
  }
329
332
  }
330
333
  );
@@ -333,9 +336,12 @@ function CapturePreview({ mode, onClick }) {
333
336
  }
334
337
 
335
338
  // src/overlay/ui/toolbar.tsx
336
- import { useEffect as useEffect2, useRef as useRef2, useState as useState3 } from "react";
339
+ import { useCallback as useCallback3, useEffect as useEffect2, useRef as useRef2, useState as useState3 } from "react";
337
340
  import {
341
+ ArrowUp,
338
342
  ChevronDown,
343
+ Clock,
344
+ Copy,
339
345
  Maximize,
340
346
  FolderOpen,
341
347
  Frame,
@@ -363,6 +369,7 @@ function Toolbar({
363
369
  onFrameSettingsChange
364
370
  }) {
365
371
  const [settingsOpen, setSettingsOpen] = useState3(false);
372
+ const [historyOpen, setHistoryOpen] = useState3(false);
366
373
  useEffect2(() => {
367
374
  const onKey = (e) => {
368
375
  if (e.target?.tagName === "INPUT") {
@@ -403,7 +410,7 @@ function Toolbar({
403
410
  background: "rgba(32, 32, 36, 0.92)",
404
411
  backdropFilter: "blur(20px)",
405
412
  WebkitBackdropFilter: "blur(20px)",
406
- border: "1px solid rgba(255, 255, 255, 0.1)",
413
+ border: "none",
407
414
  borderRadius: 999,
408
415
  padding: 6,
409
416
  boxShadow: "0 8px 32px rgba(0, 0, 0, 0.4)",
@@ -418,6 +425,7 @@ function Toolbar({
418
425
  selected: selectedMode === mode,
419
426
  onClick: () => {
420
427
  setSettingsOpen(false);
428
+ setHistoryOpen(false);
421
429
  onModeChange(mode);
422
430
  },
423
431
  children: /* @__PURE__ */ jsx3(Icon2, { size: 16, strokeWidth: 1.7 })
@@ -429,11 +437,24 @@ function Toolbar({
429
437
  SettingsButton,
430
438
  {
431
439
  open: settingsOpen,
432
- onClick: () => setSettingsOpen((prev) => !prev),
440
+ onClick: () => {
441
+ setSettingsOpen((prev) => !prev);
442
+ setHistoryOpen(false);
443
+ },
433
444
  selectedMode,
434
445
  frameSettings,
435
446
  onFrameSettingsChange
436
447
  }
448
+ ),
449
+ /* @__PURE__ */ jsx3(
450
+ HistoryButton,
451
+ {
452
+ open: historyOpen,
453
+ onClick: () => {
454
+ setHistoryOpen((prev) => !prev);
455
+ setSettingsOpen(false);
456
+ }
457
+ }
437
458
  )
438
459
  ] })
439
460
  }
@@ -450,7 +471,7 @@ function CloseButton({ onClick }) {
450
471
  style: {
451
472
  width: 32,
452
473
  height: 32,
453
- borderRadius: 10,
474
+ borderRadius: "50%",
454
475
  border: "none",
455
476
  background: hovered ? "rgba(255, 255, 255, 0.12)" : "transparent",
456
477
  display: "flex",
@@ -505,7 +526,7 @@ function ModeButton({
505
526
  style: {
506
527
  width: 32,
507
528
  height: 32,
508
- borderRadius: 10,
529
+ borderRadius: "50%",
509
530
  border: "none",
510
531
  background: selected || hovered ? "rgba(255, 255, 255, 0.12)" : "transparent",
511
532
  display: "flex",
@@ -663,7 +684,30 @@ function FrameSizeControl({
663
684
  const isCustom = !currentPreset;
664
685
  return /* @__PURE__ */ jsxs2("div", { children: [
665
686
  /* @__PURE__ */ jsx3(SettingsLabel, { children: "Size" }),
666
- /* @__PURE__ */ jsx3("div", { style: { display: "flex", alignItems: "center", gap: 6, marginTop: 4 }, children: /* @__PURE__ */ jsxs2("div", { style: { position: "relative", flex: 1 }, children: [
687
+ /* @__PURE__ */ jsxs2("div", { style: { display: "flex", alignItems: "center", gap: 4, marginTop: 4 }, children: [
688
+ /* @__PURE__ */ jsx3(
689
+ NumInput,
690
+ {
691
+ value: size.w,
692
+ onChange: (v) => {
693
+ const n = parseInt(v, 10);
694
+ if (!Number.isNaN(n) && n > 0) onChange({ ...size, w: n });
695
+ }
696
+ }
697
+ ),
698
+ /* @__PURE__ */ jsx3(StaticText, { children: "x" }),
699
+ /* @__PURE__ */ jsx3(
700
+ NumInput,
701
+ {
702
+ value: size.h,
703
+ onChange: (v) => {
704
+ const n = parseInt(v, 10);
705
+ if (!Number.isNaN(n) && n > 0) onChange({ ...size, h: n });
706
+ }
707
+ }
708
+ )
709
+ ] }),
710
+ /* @__PURE__ */ jsxs2("div", { style: { position: "relative", marginTop: 6 }, children: [
667
711
  /* @__PURE__ */ jsxs2(
668
712
  "button",
669
713
  {
@@ -690,7 +734,7 @@ function FrameSizeControl({
690
734
  ]
691
735
  }
692
736
  ),
693
- sizeOpen && /* @__PURE__ */ jsxs2(
737
+ sizeOpen && /* @__PURE__ */ jsx3(
694
738
  "div",
695
739
  {
696
740
  style: {
@@ -707,44 +751,18 @@ function FrameSizeControl({
707
751
  WebkitBackdropFilter: "blur(20px)",
708
752
  zIndex: 1
709
753
  },
710
- children: [
711
- FRAME_SIZE_PRESETS.map((preset) => /* @__PURE__ */ jsx3(
712
- DropItem,
713
- {
714
- active: !isCustom && preset.w === size.w && preset.h === size.h,
715
- onClick: () => {
716
- onChange({ w: preset.w, h: preset.h });
717
- setSizeOpen(false);
718
- },
719
- children: preset.label
754
+ children: FRAME_SIZE_PRESETS.map((preset) => /* @__PURE__ */ jsx3(
755
+ DropItem,
756
+ {
757
+ active: !isCustom && preset.w === size.w && preset.h === size.h,
758
+ onClick: () => {
759
+ onChange({ w: preset.w, h: preset.h });
760
+ setSizeOpen(false);
720
761
  },
721
- preset.label
722
- )),
723
- /* @__PURE__ */ jsx3(DropItem, { active: isCustom, onClick: () => setSizeOpen(false), children: "Custom" })
724
- ]
725
- }
726
- )
727
- ] }) }),
728
- isCustom && /* @__PURE__ */ jsxs2("div", { style: { display: "flex", alignItems: "center", gap: 4, marginTop: 6 }, children: [
729
- /* @__PURE__ */ jsx3(
730
- NumInput,
731
- {
732
- value: size.w,
733
- onChange: (v) => {
734
- const n = parseInt(v, 10);
735
- if (!Number.isNaN(n) && n > 0) onChange({ ...size, w: n });
736
- }
737
- }
738
- ),
739
- /* @__PURE__ */ jsx3(StaticText, { children: "x" }),
740
- /* @__PURE__ */ jsx3(
741
- NumInput,
742
- {
743
- value: size.h,
744
- onChange: (v) => {
745
- const n = parseInt(v, 10);
746
- if (!Number.isNaN(n) && n > 0) onChange({ ...size, h: n });
747
- }
762
+ children: preset.label
763
+ },
764
+ preset.label
765
+ ))
748
766
  }
749
767
  )
750
768
  ] })
@@ -805,7 +823,7 @@ function FrameBackgroundControl({
805
823
  ] }),
806
824
  bgType === "color" && /* @__PURE__ */ jsxs2("div", { style: { display: "flex", alignItems: "center", gap: 8, marginTop: 6 }, children: [
807
825
  /* @__PURE__ */ jsx3(ColorSwatch, { color: bgColor, onChange: (c) => onChange({ bgColor: c }) }),
808
- /* @__PURE__ */ jsx3("span", { style: { fontSize: 12, color: "rgba(255,255,255,0.6)" }, children: bgColor })
826
+ /* @__PURE__ */ jsx3(HexInput, { value: bgColor, onChange: (c) => onChange({ bgColor: c }) })
809
827
  ] }),
810
828
  bgType === "image" && /* @__PURE__ */ jsxs2("div", { style: { marginTop: 6 }, children: [
811
829
  /* @__PURE__ */ jsx3(
@@ -1148,6 +1166,57 @@ function NumInput({
1148
1166
  }
1149
1167
  );
1150
1168
  }
1169
+ function HexInput({
1170
+ value,
1171
+ onChange
1172
+ }) {
1173
+ const [editing, setEditing] = useState3(false);
1174
+ const [text, setText] = useState3(value);
1175
+ useEffect2(() => {
1176
+ if (!editing) {
1177
+ setText(value);
1178
+ }
1179
+ }, [editing, value]);
1180
+ const commit = (raw) => {
1181
+ const hex = raw.startsWith("#") ? raw : `#${raw}`;
1182
+ if (/^#[0-9a-fA-F]{6}$/.test(hex)) {
1183
+ onChange(hex);
1184
+ }
1185
+ };
1186
+ return /* @__PURE__ */ jsx3(
1187
+ "input",
1188
+ {
1189
+ type: "text",
1190
+ value: editing ? text : value,
1191
+ onFocus: () => {
1192
+ setEditing(true);
1193
+ setText(value);
1194
+ },
1195
+ onBlur: () => {
1196
+ setEditing(false);
1197
+ commit(text);
1198
+ },
1199
+ onChange: (e) => setText(e.target.value),
1200
+ onKeyDown: (e) => {
1201
+ if (e.key === "Enter") {
1202
+ e.target.blur();
1203
+ }
1204
+ },
1205
+ style: {
1206
+ width: 72,
1207
+ padding: "4px 6px",
1208
+ background: "rgba(255, 255, 255, 0.07)",
1209
+ border: "1px solid rgba(255, 255, 255, 0.1)",
1210
+ borderRadius: 7,
1211
+ color: "rgba(255, 255, 255, 0.9)",
1212
+ fontSize: 12,
1213
+ fontFamily: "system-ui, -apple-system, sans-serif",
1214
+ textAlign: "left",
1215
+ outline: "none"
1216
+ }
1217
+ }
1218
+ );
1219
+ }
1151
1220
  function StaticText({ children }) {
1152
1221
  return /* @__PURE__ */ jsx3(
1153
1222
  "span",
@@ -1202,150 +1271,44 @@ function Separator({ vertical = true }) {
1202
1271
  }
1203
1272
  );
1204
1273
  }
1205
-
1206
- // src/overlay/ui/inspector.tsx
1207
- import { useEffect as useEffect3, useRef as useRef3, useCallback as useCallback3, useState as useState4 } from "react";
1208
- import { Fragment as Fragment2, jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
1209
- function Inspector({ onSelect, onCancel }) {
1210
- const [highlight, setHighlight] = useState4(null);
1211
- const hoveredEl = useRef3(null);
1212
- const styleEl = useRef3(null);
1213
- useEffect3(() => {
1214
- const style = document.createElement("style");
1215
- style.setAttribute("data-afterbefore", "true");
1216
- style.textContent = "*, *::before, *::after { cursor: crosshair !important; }";
1217
- document.head.appendChild(style);
1218
- styleEl.current = style;
1219
- return () => {
1220
- style.remove();
1221
- };
1222
- }, []);
1223
- const isOverlayElement = useCallback3((el) => {
1224
- let node = el;
1225
- while (node) {
1226
- if (node instanceof HTMLElement && node.dataset.afterbefore) return true;
1227
- node = node.parentElement;
1274
+ function HistoryButton({
1275
+ open,
1276
+ onClick
1277
+ }) {
1278
+ const [hovered, setHovered] = useState3(false);
1279
+ const [toast, setToast] = useState3(null);
1280
+ const [pushing, setPushing] = useState3(false);
1281
+ const [repos, setRepos] = useState3([]);
1282
+ const [branches, setBranches] = useState3([]);
1283
+ const [screenshots, setScreenshots] = useState3([]);
1284
+ const [selectedRepo, setSelectedRepo] = useState3(null);
1285
+ const [selectedBranch, setSelectedBranch] = useState3(null);
1286
+ const [loading, setLoading] = useState3(false);
1287
+ const [repoDropOpen, setRepoDropOpen] = useState3(false);
1288
+ const [branchDropOpen, setBranchDropOpen] = useState3(false);
1289
+ useEffect2(() => {
1290
+ if (!open) {
1291
+ setRepoDropOpen(false);
1292
+ setBranchDropOpen(false);
1293
+ return;
1228
1294
  }
1229
- return false;
1230
- }, []);
1231
- const handleMouseMove = useCallback3(
1232
- (e) => {
1233
- const el = document.elementFromPoint(e.clientX, e.clientY);
1234
- if (!el || !(el instanceof HTMLElement) || isOverlayElement(el)) {
1235
- setHighlight(null);
1236
- hoveredEl.current = null;
1237
- return;
1238
- }
1239
- hoveredEl.current = el;
1240
- const rect = el.getBoundingClientRect();
1241
- setHighlight({
1242
- x: rect.x,
1243
- y: rect.y,
1244
- width: rect.width,
1245
- height: rect.height,
1246
- tag: el.tagName.toLowerCase() + (el.className && typeof el.className === "string" ? `.${el.className.split(" ")[0]}` : "")
1247
- });
1248
- },
1249
- [isOverlayElement]
1250
- );
1251
- const handleClick = useCallback3(
1252
- (e) => {
1253
- if (isOverlayElement(e.target)) return;
1254
- e.preventDefault();
1255
- e.stopPropagation();
1256
- e.stopImmediatePropagation();
1257
- if (hoveredEl.current) {
1258
- onSelect(hoveredEl.current);
1259
- }
1260
- },
1261
- [onSelect, isOverlayElement]
1262
- );
1263
- const handleKeyDown = useCallback3(
1264
- (e) => {
1265
- if (e.key === "Escape") {
1266
- onCancel();
1267
- }
1268
- },
1269
- [onCancel]
1270
- );
1271
- useEffect3(() => {
1272
- document.addEventListener("mousemove", handleMouseMove, true);
1273
- document.addEventListener("click", handleClick, true);
1274
- document.addEventListener("keydown", handleKeyDown);
1275
- return () => {
1276
- document.removeEventListener("mousemove", handleMouseMove, true);
1277
- document.removeEventListener("click", handleClick, true);
1278
- document.removeEventListener("keydown", handleKeyDown);
1279
- };
1280
- }, [handleMouseMove, handleClick, handleKeyDown]);
1281
- return /* @__PURE__ */ jsx4("div", { "data-afterbefore": "true", style: { position: "fixed", inset: 0, zIndex: 2147483646, pointerEvents: "none" }, children: highlight && /* @__PURE__ */ jsxs3(Fragment2, { children: [
1282
- /* @__PURE__ */ jsx4(
1283
- "div",
1284
- {
1285
- style: {
1286
- position: "fixed",
1287
- left: highlight.x,
1288
- top: highlight.y,
1289
- width: highlight.width,
1290
- height: highlight.height,
1291
- background: "rgba(59, 130, 246, 0.15)",
1292
- border: "2px solid rgba(59, 130, 246, 0.7)",
1293
- borderRadius: 2,
1294
- pointerEvents: "none"
1295
- }
1296
- }
1297
- ),
1298
- /* @__PURE__ */ jsx4(
1299
- "div",
1300
- {
1301
- style: {
1302
- position: "fixed",
1303
- left: highlight.x,
1304
- top: Math.max(0, highlight.y - 24),
1305
- background: "rgba(59, 130, 246, 0.9)",
1306
- color: "#fff",
1307
- fontSize: 11,
1308
- fontFamily: "system-ui, -apple-system, monospace",
1309
- padding: "2px 6px",
1310
- borderRadius: 3,
1311
- pointerEvents: "none",
1312
- whiteSpace: "nowrap",
1313
- lineHeight: "18px"
1314
- },
1315
- children: highlight.tag
1316
- }
1317
- )
1318
- ] }) });
1319
- }
1320
-
1321
- // src/overlay/ui/status.tsx
1322
- import { useState as useState5, useRef as useRef4, useEffect as useEffect4, useCallback as useCallback4 } from "react";
1323
- import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
1324
- var PANEL_WIDTH = 220;
1325
- function Status({ onReset, position, onClose }) {
1326
- const panelRef = useRef4(null);
1327
- const [toast, setToast] = useState5(null);
1328
- const [pushing, setPushing] = useState5(false);
1329
- const showToast = useCallback4((message, type) => {
1295
+ setLoading(true);
1296
+ const params = new URLSearchParams();
1297
+ if (selectedRepo) params.set("repo", selectedRepo);
1298
+ if (selectedBranch) params.set("branch", selectedBranch);
1299
+ fetch(`/__afterbefore/history?${params}`).then((r) => r.json()).then((data) => {
1300
+ setRepos(data.repos || []);
1301
+ setBranches(data.branches || []);
1302
+ setScreenshots(data.screenshots || []);
1303
+ if (!selectedRepo && data.currentRepo) setSelectedRepo(data.currentRepo);
1304
+ if (!selectedBranch && data.currentBranch) setSelectedBranch(data.currentBranch);
1305
+ }).catch(() => {
1306
+ }).finally(() => setLoading(false));
1307
+ }, [open, selectedRepo, selectedBranch]);
1308
+ const showToast = useCallback3((message, type) => {
1330
1309
  setToast({ message, type });
1331
1310
  setTimeout(() => setToast(null), 3e3);
1332
1311
  }, []);
1333
- useEffect4(() => {
1334
- const handler = (e) => {
1335
- if (panelRef.current && !panelRef.current.contains(e.target)) {
1336
- onClose();
1337
- }
1338
- };
1339
- document.addEventListener("mousedown", handler);
1340
- return () => document.removeEventListener("mousedown", handler);
1341
- }, [onClose]);
1342
- useEffect4(() => {
1343
- const handler = (e) => {
1344
- if (e.key === "Escape") onClose();
1345
- };
1346
- document.addEventListener("keydown", handler);
1347
- return () => document.removeEventListener("keydown", handler);
1348
- }, [onClose]);
1349
1312
  const handleOpenFolder = async () => {
1350
1313
  try {
1351
1314
  const res = await fetch("/__afterbefore/open", { method: "POST" });
@@ -1384,273 +1347,502 @@ function Status({ onReset, position, onClose }) {
1384
1347
  setPushing(false);
1385
1348
  }
1386
1349
  };
1387
- const handleReset = () => {
1388
- onReset();
1389
- onClose();
1390
- };
1391
- const panelLeft = Math.max(
1392
- 8,
1393
- Math.min(position.x - PANEL_WIDTH / 2 + 20, window.innerWidth - PANEL_WIDTH - 8)
1394
- );
1395
- const panelBottom = window.innerHeight - position.y + 8;
1396
- const buttonStyle = {
1397
- display: "flex",
1398
- alignItems: "center",
1399
- gap: 6,
1400
- width: "100%",
1401
- padding: "7px 10px",
1402
- border: "none",
1403
- background: "transparent",
1404
- color: "rgba(255,255,255,0.9)",
1405
- fontSize: 13,
1406
- borderRadius: 6,
1407
- cursor: "pointer",
1408
- textAlign: "left",
1409
- fontFamily: "system-ui, -apple-system, sans-serif",
1410
- transition: "background 0.1s"
1411
- };
1412
- const onEnter = (e) => {
1413
- e.currentTarget.style.background = "rgba(255,255,255,0.1)";
1414
- };
1415
- const onLeave = (e) => {
1416
- e.currentTarget.style.background = "transparent";
1417
- };
1418
- return /* @__PURE__ */ jsxs4(
1419
- "div",
1420
- {
1421
- ref: panelRef,
1422
- "data-afterbefore": "true",
1423
- style: {
1424
- position: "fixed",
1425
- left: panelLeft,
1426
- bottom: panelBottom,
1427
- width: PANEL_WIDTH,
1428
- background: "rgba(24, 24, 27, 0.95)",
1429
- borderRadius: 10,
1430
- boxShadow: "0 4px 20px rgba(0,0,0,0.4), 0 0 0 1px rgba(255,255,255,0.08)",
1431
- zIndex: 2147483647,
1432
- fontFamily: "system-ui, -apple-system, sans-serif",
1433
- backdropFilter: "blur(12px)",
1434
- overflow: "hidden"
1435
- },
1436
- children: [
1437
- /* @__PURE__ */ jsx5(
1438
- "div",
1439
- {
1440
- style: {
1441
- padding: "10px 12px 6px",
1442
- fontSize: 12,
1443
- fontWeight: 600,
1444
- color: "rgba(255,255,255,0.5)",
1445
- letterSpacing: "0.02em"
1446
- },
1447
- children: "Screenshot captured"
1448
- }
1449
- ),
1450
- /* @__PURE__ */ jsxs4("div", { style: { padding: "0 4px 4px" }, children: [
1451
- /* @__PURE__ */ jsxs4(
1452
- "button",
1453
- {
1454
- style: buttonStyle,
1455
- onClick: handleOpenFolder,
1456
- onMouseEnter: onEnter,
1457
- onMouseLeave: onLeave,
1458
- children: [
1459
- /* @__PURE__ */ jsx5(FolderIcon, {}),
1460
- "Open Folder"
1461
- ]
1462
- }
1463
- ),
1464
- /* @__PURE__ */ jsxs4(
1465
- "button",
1466
- {
1467
- style: buttonStyle,
1468
- onClick: handleCopyMarkdown,
1469
- onMouseEnter: onEnter,
1470
- onMouseLeave: onLeave,
1471
- children: [
1472
- /* @__PURE__ */ jsx5(CopyIcon, {}),
1473
- "Copy Markdown"
1474
- ]
1475
- }
1476
- ),
1477
- /* @__PURE__ */ jsxs4(
1478
- "button",
1479
- {
1480
- style: buttonStyle,
1481
- onClick: handlePush,
1482
- disabled: pushing,
1483
- onMouseEnter: onEnter,
1484
- onMouseLeave: onLeave,
1485
- children: [
1486
- /* @__PURE__ */ jsx5(PushIcon, {}),
1487
- pushing ? "Pushing..." : "Push to PR"
1488
- ]
1489
- }
1490
- ),
1491
- /* @__PURE__ */ jsx5(
1350
+ return /* @__PURE__ */ jsxs2("div", { style: { position: "relative" }, children: [
1351
+ hovered && !open && /* @__PURE__ */ jsx3(
1352
+ "div",
1353
+ {
1354
+ style: {
1355
+ position: "absolute",
1356
+ left: "50%",
1357
+ bottom: "calc(100% + 10px)",
1358
+ transform: "translateX(-50%)",
1359
+ background: "rgba(32, 32, 36, 0.96)",
1360
+ backdropFilter: "blur(20px)",
1361
+ WebkitBackdropFilter: "blur(20px)",
1362
+ border: "1px solid rgba(255, 255, 255, 0.1)",
1363
+ borderRadius: 8,
1364
+ padding: "5px 10px",
1365
+ color: "rgba(255, 255, 255, 0.88)",
1366
+ fontSize: 12,
1367
+ whiteSpace: "nowrap",
1368
+ boxShadow: "0 8px 28px rgba(0, 0, 0, 0.28)",
1369
+ pointerEvents: "none"
1370
+ },
1371
+ children: "Screenshots"
1372
+ }
1373
+ ),
1374
+ /* @__PURE__ */ jsx3(
1375
+ "button",
1376
+ {
1377
+ onClick,
1378
+ onMouseEnter: () => setHovered(true),
1379
+ onMouseLeave: () => setHovered(false),
1380
+ style: {
1381
+ width: 32,
1382
+ height: 32,
1383
+ borderRadius: "50%",
1384
+ border: "none",
1385
+ background: open || hovered ? "rgba(255, 255, 255, 0.12)" : "transparent",
1386
+ display: "flex",
1387
+ alignItems: "center",
1388
+ justifyContent: "center",
1389
+ cursor: "pointer",
1390
+ padding: 0,
1391
+ color: open || hovered ? "rgba(255, 255, 255, 0.96)" : "rgba(255, 255, 255, 0.52)",
1392
+ transition: "background 0.12s ease, color 0.12s ease"
1393
+ },
1394
+ children: /* @__PURE__ */ jsx3(Clock, { size: 16, strokeWidth: 1.7 })
1395
+ }
1396
+ ),
1397
+ open && /* @__PURE__ */ jsxs2(
1398
+ "div",
1399
+ {
1400
+ style: {
1401
+ position: "absolute",
1402
+ left: "50%",
1403
+ bottom: "calc(100% + 12px)",
1404
+ transform: "translateX(-50%)",
1405
+ minWidth: 300,
1406
+ maxWidth: 360,
1407
+ padding: "10px 12px",
1408
+ borderRadius: 12,
1409
+ background: "rgba(32, 32, 36, 0.96)",
1410
+ border: "1px solid rgba(255, 255, 255, 0.1)",
1411
+ boxShadow: "0 14px 36px rgba(0, 0, 0, 0.32)",
1412
+ backdropFilter: "blur(20px)",
1413
+ WebkitBackdropFilter: "blur(20px)"
1414
+ },
1415
+ children: [
1416
+ /* @__PURE__ */ jsx3(
1492
1417
  "div",
1493
1418
  {
1494
1419
  style: {
1495
- height: 1,
1496
- background: "rgba(255,255,255,0.08)",
1497
- margin: "4px 6px"
1498
- }
1420
+ fontSize: 11,
1421
+ color: "rgba(255, 255, 255, 0.46)",
1422
+ letterSpacing: "0.03em",
1423
+ textTransform: "uppercase",
1424
+ marginBottom: 10
1425
+ },
1426
+ children: "Screenshots"
1499
1427
  }
1500
1428
  ),
1501
- /* @__PURE__ */ jsxs4(
1502
- "button",
1429
+ /* @__PURE__ */ jsxs2("div", { style: { display: "flex", flexDirection: "column", gap: 6, marginBottom: 10 }, children: [
1430
+ /* @__PURE__ */ jsx3(
1431
+ FilterDropdown,
1432
+ {
1433
+ label: "Project",
1434
+ value: selectedRepo,
1435
+ options: repos,
1436
+ isOpen: repoDropOpen,
1437
+ onToggle: () => {
1438
+ setRepoDropOpen((p) => !p);
1439
+ setBranchDropOpen(false);
1440
+ },
1441
+ onSelect: (repo) => {
1442
+ setSelectedRepo(repo);
1443
+ setSelectedBranch(null);
1444
+ setRepoDropOpen(false);
1445
+ }
1446
+ }
1447
+ ),
1448
+ /* @__PURE__ */ jsx3(
1449
+ FilterDropdown,
1450
+ {
1451
+ label: "Branch",
1452
+ value: selectedBranch,
1453
+ options: branches,
1454
+ isOpen: branchDropOpen,
1455
+ onToggle: () => {
1456
+ setBranchDropOpen((p) => !p);
1457
+ setRepoDropOpen(false);
1458
+ },
1459
+ onSelect: (branch) => {
1460
+ setSelectedBranch(branch);
1461
+ setBranchDropOpen(false);
1462
+ }
1463
+ }
1464
+ )
1465
+ ] }),
1466
+ loading ? /* @__PURE__ */ jsx3(
1467
+ "div",
1468
+ {
1469
+ style: {
1470
+ padding: "12px 0",
1471
+ textAlign: "center",
1472
+ fontSize: 12,
1473
+ color: "rgba(255, 255, 255, 0.35)"
1474
+ },
1475
+ children: "Loading..."
1476
+ }
1477
+ ) : screenshots.length === 0 ? /* @__PURE__ */ jsx3(
1478
+ "div",
1503
1479
  {
1504
- style: { ...buttonStyle, color: "rgba(255,255,255,0.5)" },
1505
- onClick: handleReset,
1506
- onMouseEnter: onEnter,
1507
- onMouseLeave: onLeave,
1508
- children: [
1509
- /* @__PURE__ */ jsx5(ResetIcon, {}),
1510
- "Reset"
1511
- ]
1480
+ style: {
1481
+ padding: "12px 0",
1482
+ textAlign: "center",
1483
+ fontSize: 12,
1484
+ color: "rgba(255, 255, 255, 0.35)"
1485
+ },
1486
+ children: "No screenshots yet"
1487
+ }
1488
+ ) : /* @__PURE__ */ jsxs2(Fragment, { children: [
1489
+ /* @__PURE__ */ jsx3(
1490
+ "div",
1491
+ {
1492
+ style: {
1493
+ maxHeight: 240,
1494
+ overflowY: "auto",
1495
+ display: "flex",
1496
+ flexDirection: "column",
1497
+ gap: 8
1498
+ },
1499
+ children: screenshots.map((shot) => /* @__PURE__ */ jsxs2(
1500
+ "div",
1501
+ {
1502
+ style: {
1503
+ display: "flex",
1504
+ gap: 10,
1505
+ alignItems: "center"
1506
+ },
1507
+ children: [
1508
+ /* @__PURE__ */ jsx3(
1509
+ "img",
1510
+ {
1511
+ src: `/__afterbefore/history/image?repo=${encodeURIComponent(selectedRepo || "")}&branch=${encodeURIComponent(selectedBranch || "")}&file=${encodeURIComponent(shot.filename)}`,
1512
+ alt: "",
1513
+ style: {
1514
+ width: 56,
1515
+ height: 36,
1516
+ borderRadius: 4,
1517
+ objectFit: "cover",
1518
+ border: "1px solid rgba(255, 255, 255, 0.1)",
1519
+ flexShrink: 0,
1520
+ background: "rgba(255, 255, 255, 0.05)"
1521
+ }
1522
+ }
1523
+ ),
1524
+ /* @__PURE__ */ jsx3("div", { style: { flex: 1, minWidth: 0 }, children: /* @__PURE__ */ jsx3(
1525
+ "div",
1526
+ {
1527
+ style: {
1528
+ fontSize: 12,
1529
+ color: "rgba(255, 255, 255, 0.88)"
1530
+ },
1531
+ children: formatTimestamp(shot.filename)
1532
+ }
1533
+ ) })
1534
+ ]
1535
+ },
1536
+ shot.filename
1537
+ ))
1538
+ }
1539
+ ),
1540
+ /* @__PURE__ */ jsx3(
1541
+ "div",
1542
+ {
1543
+ style: {
1544
+ height: 1,
1545
+ background: "rgba(255, 255, 255, 0.08)",
1546
+ margin: "8px 0"
1547
+ }
1548
+ }
1549
+ ),
1550
+ /* @__PURE__ */ jsxs2("div", { style: { display: "flex", flexDirection: "column", gap: 2 }, children: [
1551
+ /* @__PURE__ */ jsxs2(ActionButton, { onClick: handleOpenFolder, children: [
1552
+ /* @__PURE__ */ jsx3(FolderOpen, { size: 13, strokeWidth: 1.8 }),
1553
+ "Open Folder"
1554
+ ] }),
1555
+ /* @__PURE__ */ jsxs2(ActionButton, { onClick: handleCopyMarkdown, children: [
1556
+ /* @__PURE__ */ jsx3(Copy, { size: 13, strokeWidth: 1.8 }),
1557
+ "Copy Markdown"
1558
+ ] }),
1559
+ /* @__PURE__ */ jsxs2(ActionButton, { onClick: handlePush, disabled: pushing, children: [
1560
+ /* @__PURE__ */ jsx3(ArrowUp, { size: 13, strokeWidth: 1.8 }),
1561
+ pushing ? "Pushing..." : "Push to PR"
1562
+ ] })
1563
+ ] })
1564
+ ] }),
1565
+ toast && /* @__PURE__ */ jsx3(
1566
+ "div",
1567
+ {
1568
+ style: {
1569
+ position: "absolute",
1570
+ bottom: "100%",
1571
+ left: "50%",
1572
+ transform: "translateX(-50%)",
1573
+ marginBottom: 8,
1574
+ padding: "6px 12px",
1575
+ borderRadius: 6,
1576
+ fontSize: 12,
1577
+ fontWeight: 500,
1578
+ whiteSpace: "nowrap",
1579
+ color: "white",
1580
+ background: toast.type === "success" ? "rgba(34, 197, 94, 0.9)" : "rgba(239, 68, 68, 0.9)",
1581
+ boxShadow: "0 2px 8px rgba(0,0,0,0.3)"
1582
+ },
1583
+ children: toast.message
1512
1584
  }
1513
1585
  )
1514
- ] }),
1515
- toast && /* @__PURE__ */ jsx5(
1516
- "div",
1517
- {
1518
- style: {
1519
- position: "absolute",
1520
- bottom: "100%",
1521
- left: "50%",
1522
- transform: "translateX(-50%)",
1523
- marginBottom: 8,
1524
- padding: "6px 12px",
1525
- borderRadius: 6,
1526
- fontSize: 12,
1527
- fontWeight: 500,
1528
- whiteSpace: "nowrap",
1529
- color: "white",
1530
- background: toast.type === "success" ? "rgba(34, 197, 94, 0.9)" : "rgba(239, 68, 68, 0.9)",
1531
- boxShadow: "0 2px 8px rgba(0,0,0,0.3)"
1532
- },
1533
- children: toast.message
1534
- }
1535
- )
1536
- ]
1537
- }
1538
- );
1586
+ ]
1587
+ }
1588
+ )
1589
+ ] });
1539
1590
  }
1540
- function FolderIcon() {
1541
- return /* @__PURE__ */ jsx5(
1542
- "svg",
1543
- {
1544
- width: "14",
1545
- height: "14",
1546
- viewBox: "0 0 14 14",
1547
- style: { color: "rgba(255,255,255,0.5)" },
1548
- children: /* @__PURE__ */ jsx5(
1549
- "path",
1591
+ function FilterDropdown({
1592
+ label,
1593
+ value,
1594
+ options,
1595
+ isOpen,
1596
+ onToggle,
1597
+ onSelect
1598
+ }) {
1599
+ return /* @__PURE__ */ jsxs2("div", { children: [
1600
+ /* @__PURE__ */ jsx3(
1601
+ "div",
1602
+ {
1603
+ style: {
1604
+ fontSize: 11,
1605
+ color: "rgba(255, 255, 255, 0.42)",
1606
+ letterSpacing: "0.02em",
1607
+ marginBottom: 3
1608
+ },
1609
+ children: label
1610
+ }
1611
+ ),
1612
+ /* @__PURE__ */ jsxs2("div", { style: { position: "relative" }, children: [
1613
+ /* @__PURE__ */ jsxs2(
1614
+ "button",
1550
1615
  {
1551
- 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",
1552
- fill: "none",
1553
- stroke: "currentColor",
1554
- strokeWidth: "1.3"
1616
+ onClick: onToggle,
1617
+ style: {
1618
+ display: "flex",
1619
+ alignItems: "center",
1620
+ justifyContent: "space-between",
1621
+ gap: 6,
1622
+ width: "100%",
1623
+ height: 30,
1624
+ padding: "0 8px",
1625
+ borderRadius: 7,
1626
+ border: "1px solid rgba(255,255,255,0.1)",
1627
+ background: "rgba(255,255,255,0.07)",
1628
+ color: "rgba(255,255,255,0.88)",
1629
+ cursor: "pointer",
1630
+ fontSize: 12,
1631
+ fontFamily: "inherit"
1632
+ },
1633
+ children: [
1634
+ /* @__PURE__ */ jsx3(
1635
+ "span",
1636
+ {
1637
+ style: {
1638
+ overflow: "hidden",
1639
+ textOverflow: "ellipsis",
1640
+ whiteSpace: "nowrap"
1641
+ },
1642
+ children: value || "\u2014"
1643
+ }
1644
+ ),
1645
+ /* @__PURE__ */ jsx3(ChevronDown, { size: 12, strokeWidth: 2 })
1646
+ ]
1647
+ }
1648
+ ),
1649
+ isOpen && options.length > 0 && /* @__PURE__ */ jsx3(
1650
+ "div",
1651
+ {
1652
+ style: {
1653
+ position: "absolute",
1654
+ bottom: "calc(100% + 4px)",
1655
+ left: 0,
1656
+ right: 0,
1657
+ maxHeight: 160,
1658
+ overflowY: "auto",
1659
+ background: "rgba(32, 32, 36, 0.96)",
1660
+ border: "1px solid rgba(255, 255, 255, 0.1)",
1661
+ borderRadius: 8,
1662
+ padding: "4px 0",
1663
+ boxShadow: "0 10px 30px rgba(0, 0, 0, 0.3)",
1664
+ backdropFilter: "blur(20px)",
1665
+ WebkitBackdropFilter: "blur(20px)",
1666
+ zIndex: 1
1667
+ },
1668
+ children: options.map((opt) => /* @__PURE__ */ jsx3(
1669
+ DropItem,
1670
+ {
1671
+ active: opt === value,
1672
+ onClick: () => onSelect(opt),
1673
+ children: opt
1674
+ },
1675
+ opt
1676
+ ))
1555
1677
  }
1556
1678
  )
1557
- }
1558
- );
1679
+ ] })
1680
+ ] });
1559
1681
  }
1560
- function CopyIcon() {
1561
- return /* @__PURE__ */ jsxs4(
1562
- "svg",
1682
+ function ActionButton({
1683
+ children,
1684
+ onClick,
1685
+ disabled
1686
+ }) {
1687
+ const [hovered, setHovered] = useState3(false);
1688
+ return /* @__PURE__ */ jsx3(
1689
+ "button",
1563
1690
  {
1564
- width: "14",
1565
- height: "14",
1566
- viewBox: "0 0 14 14",
1567
- style: { color: "rgba(255,255,255,0.5)" },
1568
- children: [
1569
- /* @__PURE__ */ jsx5(
1570
- "rect",
1571
- {
1572
- x: "4",
1573
- y: "4",
1574
- width: "8.5",
1575
- height: "8.5",
1576
- rx: "1.5",
1577
- fill: "none",
1578
- stroke: "currentColor",
1579
- strokeWidth: "1.3"
1580
- }
1581
- ),
1582
- /* @__PURE__ */ jsx5(
1583
- "path",
1584
- {
1585
- 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",
1586
- fill: "none",
1587
- stroke: "currentColor",
1588
- strokeWidth: "1.3"
1589
- }
1590
- )
1591
- ]
1691
+ onClick,
1692
+ disabled,
1693
+ onMouseEnter: () => setHovered(true),
1694
+ onMouseLeave: () => setHovered(false),
1695
+ style: {
1696
+ display: "flex",
1697
+ alignItems: "center",
1698
+ gap: 6,
1699
+ width: "100%",
1700
+ padding: "6px 8px",
1701
+ border: "none",
1702
+ background: hovered ? "rgba(255, 255, 255, 0.08)" : "transparent",
1703
+ color: "rgba(255, 255, 255, 0.78)",
1704
+ fontSize: 12,
1705
+ borderRadius: 6,
1706
+ cursor: disabled ? "wait" : "pointer",
1707
+ textAlign: "left",
1708
+ fontFamily: "inherit",
1709
+ transition: "background 0.1s ease"
1710
+ },
1711
+ children
1592
1712
  }
1593
1713
  );
1594
1714
  }
1595
- function PushIcon() {
1596
- return /* @__PURE__ */ jsx5(
1597
- "svg",
1598
- {
1599
- width: "14",
1600
- height: "14",
1601
- viewBox: "0 0 14 14",
1602
- style: { color: "rgba(255,255,255,0.5)" },
1603
- children: /* @__PURE__ */ jsx5(
1604
- "path",
1605
- {
1606
- d: "M7 11V3m0 0L4 6m3-3l3 3",
1607
- fill: "none",
1608
- stroke: "currentColor",
1609
- strokeWidth: "1.3",
1610
- strokeLinecap: "round",
1611
- strokeLinejoin: "round"
1612
- }
1613
- )
1614
- }
1615
- );
1715
+ function formatTimestamp(filename) {
1716
+ const iso = filename.replace(/\.png$/, "").replace(/T(\d{2})-(\d{2})-(\d{2})-(\d+)Z$/, "T$1:$2:$3.$4Z");
1717
+ const date = new Date(iso);
1718
+ if (Number.isNaN(date.getTime())) return filename.replace(/\.png$/, "");
1719
+ const now = /* @__PURE__ */ new Date();
1720
+ const diffMs = now.getTime() - date.getTime();
1721
+ const diffMin = Math.floor(diffMs / 6e4);
1722
+ if (diffMin < 1) return "Just now";
1723
+ if (diffMin < 60) return `${diffMin}m ago`;
1724
+ const diffHr = Math.floor(diffMin / 60);
1725
+ if (diffHr < 24) return `${diffHr}h ago`;
1726
+ return date.toLocaleDateString(void 0, { month: "short", day: "numeric", hour: "2-digit", minute: "2-digit" });
1616
1727
  }
1617
- function ResetIcon() {
1618
- return /* @__PURE__ */ jsxs4(
1619
- "svg",
1620
- {
1621
- width: "14",
1622
- height: "14",
1623
- viewBox: "0 0 14 14",
1624
- style: { color: "rgba(255,255,255,0.4)" },
1625
- children: [
1626
- /* @__PURE__ */ jsx5(
1627
- "path",
1628
- {
1629
- d: "M2.5 7a4.5 4.5 0 118 2.5",
1630
- fill: "none",
1631
- stroke: "currentColor",
1632
- strokeWidth: "1.3",
1633
- strokeLinecap: "round"
1634
- }
1635
- ),
1636
- /* @__PURE__ */ jsx5(
1637
- "path",
1638
- {
1639
- d: "M2.5 3v4h4",
1640
- fill: "none",
1641
- stroke: "currentColor",
1642
- strokeWidth: "1.3",
1643
- strokeLinecap: "round",
1644
- strokeLinejoin: "round"
1645
- }
1646
- )
1647
- ]
1728
+
1729
+ // src/overlay/ui/inspector.tsx
1730
+ import { useEffect as useEffect3, useRef as useRef3, useCallback as useCallback4, useState as useState4 } from "react";
1731
+ import { Fragment as Fragment2, jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
1732
+ function Inspector({ onSelect, onCancel }) {
1733
+ const [highlight, setHighlight] = useState4(null);
1734
+ const hoveredEl = useRef3(null);
1735
+ const styleEl = useRef3(null);
1736
+ useEffect3(() => {
1737
+ const style = document.createElement("style");
1738
+ style.setAttribute("data-afterbefore", "true");
1739
+ style.textContent = ":not([data-afterbefore]):not([data-afterbefore] *) { cursor: crosshair !important; }";
1740
+ document.head.appendChild(style);
1741
+ styleEl.current = style;
1742
+ return () => {
1743
+ style.remove();
1744
+ };
1745
+ }, []);
1746
+ const isOverlayElement = useCallback4((el) => {
1747
+ let node = el;
1748
+ while (node) {
1749
+ if (node instanceof HTMLElement && node.dataset.afterbefore) return true;
1750
+ node = node.parentElement;
1648
1751
  }
1752
+ return false;
1753
+ }, []);
1754
+ const handleMouseMove = useCallback4(
1755
+ (e) => {
1756
+ const el = document.elementFromPoint(e.clientX, e.clientY);
1757
+ if (!el || !(el instanceof HTMLElement) || isOverlayElement(el)) {
1758
+ setHighlight(null);
1759
+ hoveredEl.current = null;
1760
+ return;
1761
+ }
1762
+ hoveredEl.current = el;
1763
+ const rect = el.getBoundingClientRect();
1764
+ setHighlight({
1765
+ x: rect.x,
1766
+ y: rect.y,
1767
+ width: rect.width,
1768
+ height: rect.height,
1769
+ tag: el.tagName.toLowerCase() + (el.className && typeof el.className === "string" ? `.${el.className.split(" ")[0]}` : "")
1770
+ });
1771
+ },
1772
+ [isOverlayElement]
1773
+ );
1774
+ const handleClick = useCallback4(
1775
+ (e) => {
1776
+ if (isOverlayElement(e.target)) return;
1777
+ e.preventDefault();
1778
+ e.stopPropagation();
1779
+ e.stopImmediatePropagation();
1780
+ if (hoveredEl.current) {
1781
+ onSelect(hoveredEl.current);
1782
+ }
1783
+ },
1784
+ [onSelect, isOverlayElement]
1649
1785
  );
1786
+ const handleKeyDown = useCallback4(
1787
+ (e) => {
1788
+ if (e.key === "Escape") {
1789
+ onCancel();
1790
+ }
1791
+ },
1792
+ [onCancel]
1793
+ );
1794
+ useEffect3(() => {
1795
+ document.addEventListener("mousemove", handleMouseMove, true);
1796
+ document.addEventListener("click", handleClick, true);
1797
+ document.addEventListener("keydown", handleKeyDown);
1798
+ return () => {
1799
+ document.removeEventListener("mousemove", handleMouseMove, true);
1800
+ document.removeEventListener("click", handleClick, true);
1801
+ document.removeEventListener("keydown", handleKeyDown);
1802
+ };
1803
+ }, [handleMouseMove, handleClick, handleKeyDown]);
1804
+ return /* @__PURE__ */ jsx4("div", { "data-afterbefore": "true", style: { position: "fixed", inset: 0, zIndex: 2147483646, pointerEvents: "none" }, children: highlight && /* @__PURE__ */ jsxs3(Fragment2, { children: [
1805
+ /* @__PURE__ */ jsx4(
1806
+ "div",
1807
+ {
1808
+ style: {
1809
+ position: "fixed",
1810
+ left: highlight.x,
1811
+ top: highlight.y,
1812
+ width: highlight.width,
1813
+ height: highlight.height,
1814
+ background: "rgba(59, 130, 246, 0.15)",
1815
+ border: "2px solid rgba(59, 130, 246, 0.7)",
1816
+ borderRadius: 2,
1817
+ pointerEvents: "none"
1818
+ }
1819
+ }
1820
+ ),
1821
+ /* @__PURE__ */ jsx4(
1822
+ "div",
1823
+ {
1824
+ style: {
1825
+ position: "fixed",
1826
+ left: highlight.x,
1827
+ top: Math.max(0, highlight.y - 24),
1828
+ background: "rgba(59, 130, 246, 0.9)",
1829
+ color: "#fff",
1830
+ fontSize: 11,
1831
+ fontFamily: "system-ui, -apple-system, monospace",
1832
+ padding: "2px 6px",
1833
+ borderRadius: 3,
1834
+ pointerEvents: "none",
1835
+ whiteSpace: "nowrap",
1836
+ lineHeight: "18px"
1837
+ },
1838
+ children: highlight.tag
1839
+ }
1840
+ )
1841
+ ] }) });
1650
1842
  }
1651
1843
 
1652
1844
  // src/overlay/index.tsx
1653
- import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
1845
+ import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
1654
1846
  async function saveCapture(mode, dataUrl) {
1655
1847
  try {
1656
1848
  const res = await fetch("/__afterbefore/save", {
@@ -1668,14 +1860,13 @@ async function saveCapture(mode, dataUrl) {
1668
1860
  }
1669
1861
  function AfterBefore() {
1670
1862
  const { state, captureComplete, reset } = useOverlayState();
1671
- const [statusOpen, setStatusOpen] = useState6(false);
1672
- const [toolbarActive, setToolbarActive] = useState6(false);
1673
- const [inspectorActive, setInspectorActive] = useState6(false);
1674
- const [loading, setLoading] = useState6(false);
1675
- const [selectedMode, setSelectedMode] = useState6("component");
1676
- const [frameSettings, setFrameSettings] = useState6(DEFAULT_FRAME_SETTINGS);
1677
- const iconPos = useRef5({ x: 24, y: 0 });
1678
- useEffect5(() => {
1863
+ const [toolbarActive, setToolbarActive] = useState5(false);
1864
+ const [inspectorActive, setInspectorActive] = useState5(false);
1865
+ const [loading, setLoading] = useState5(false);
1866
+ const [selectedMode, setSelectedMode] = useState5("component");
1867
+ const [frameSettings, setFrameSettings] = useState5(DEFAULT_FRAME_SETTINGS);
1868
+ const iconPos = useRef4({ x: 24, y: 0 });
1869
+ useEffect4(() => {
1679
1870
  try {
1680
1871
  const stored = localStorage.getItem("ab-frame-settings");
1681
1872
  if (stored) {
@@ -1690,11 +1881,10 @@ function AfterBefore() {
1690
1881
  setFrameSettings(DEFAULT_FRAME_SETTINGS);
1691
1882
  }
1692
1883
  }, []);
1693
- useEffect5(() => {
1884
+ useEffect4(() => {
1694
1885
  if (state.phase === "ready") {
1695
1886
  const timer = setTimeout(() => {
1696
1887
  reset();
1697
- setStatusOpen(false);
1698
1888
  }, 1500);
1699
1889
  return () => clearTimeout(timer);
1700
1890
  }
@@ -1708,13 +1898,12 @@ function AfterBefore() {
1708
1898
  const handleIconClick = useCallback5(() => {
1709
1899
  if (loading) return;
1710
1900
  if (state.phase === "ready") {
1711
- setStatusOpen((prev) => !prev);
1712
- } else if (toolbarActive || inspectorActive) {
1901
+ reset();
1902
+ }
1903
+ if (toolbarActive || inspectorActive) {
1713
1904
  setToolbarActive(false);
1714
1905
  setInspectorActive(false);
1715
- setStatusOpen(false);
1716
1906
  } else {
1717
- setStatusOpen(false);
1718
1907
  if (selectedMode === "component") {
1719
1908
  setToolbarActive(true);
1720
1909
  setInspectorActive(true);
@@ -1723,7 +1912,7 @@ function AfterBefore() {
1723
1912
  setInspectorActive(false);
1724
1913
  }
1725
1914
  }
1726
- }, [state.phase, loading, toolbarActive, inspectorActive, selectedMode]);
1915
+ }, [state.phase, loading, toolbarActive, inspectorActive, selectedMode, reset]);
1727
1916
  const performCapture = useCallback5(
1728
1917
  async (mode, element) => {
1729
1918
  setLoading(true);
@@ -1781,19 +1970,10 @@ function AfterBefore() {
1781
1970
  }, []);
1782
1971
  const handleModeChange = useCallback5((mode) => {
1783
1972
  setSelectedMode(mode);
1784
- if (mode === "component") {
1785
- setInspectorActive(true);
1786
- }
1787
- }, []);
1788
- const handleReset = useCallback5(() => {
1789
- reset();
1790
- setStatusOpen(false);
1791
- }, [reset]);
1792
- const handleStatusClose = useCallback5(() => {
1793
- setStatusOpen(false);
1973
+ setInspectorActive(mode === "component");
1794
1974
  }, []);
1795
- return /* @__PURE__ */ jsxs5("div", { "data-afterbefore": "true", children: [
1796
- /* @__PURE__ */ jsx6(
1975
+ return /* @__PURE__ */ jsxs4("div", { "data-afterbefore": "true", children: [
1976
+ /* @__PURE__ */ jsx5(
1797
1977
  Icon,
1798
1978
  {
1799
1979
  phase: state.phase,
@@ -1802,14 +1982,14 @@ function AfterBefore() {
1802
1982
  onPositionChange: handlePositionChange
1803
1983
  }
1804
1984
  ),
1805
- toolbarActive && !inspectorActive && !loading && /* @__PURE__ */ jsx6(
1985
+ toolbarActive && !inspectorActive && !loading && /* @__PURE__ */ jsx5(
1806
1986
  CapturePreview,
1807
1987
  {
1808
1988
  mode: selectedMode,
1809
1989
  onClick: () => handleToolbarCapture(selectedMode)
1810
1990
  }
1811
1991
  ),
1812
- toolbarActive && /* @__PURE__ */ jsx6(
1992
+ toolbarActive && /* @__PURE__ */ jsx5(
1813
1993
  Toolbar,
1814
1994
  {
1815
1995
  selectedMode,
@@ -1820,15 +2000,7 @@ function AfterBefore() {
1820
2000
  onFrameSettingsChange: handleFrameSettingsChange
1821
2001
  }
1822
2002
  ),
1823
- inspectorActive && /* @__PURE__ */ jsx6(Inspector, { onSelect: handleComponentSelect, onCancel: handleComponentCancel }),
1824
- statusOpen && state.phase === "ready" && /* @__PURE__ */ jsx6(
1825
- Status,
1826
- {
1827
- onReset: handleReset,
1828
- position: iconPos.current,
1829
- onClose: handleStatusClose
1830
- }
1831
- )
2003
+ inspectorActive && /* @__PURE__ */ jsx5(Inspector, { onSelect: handleComponentSelect, onCancel: handleComponentCancel })
1832
2004
  ] });
1833
2005
  }
1834
2006
  export {