afterbefore 0.2.19 → 0.2.21

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.
@@ -10,7 +10,7 @@ var initialState = {
10
10
  lastCapture: null
11
11
  };
12
12
  function useOverlayState() {
13
- const [state, setState] = useState(initialState);
13
+ const [state2, setState] = useState(initialState);
14
14
  const captureComplete = useCallback(
15
15
  (result) => {
16
16
  setState({ phase: "ready", lastCapture: result });
@@ -20,7 +20,7 @@ function useOverlayState() {
20
20
  const reset = useCallback(() => {
21
21
  setState(initialState);
22
22
  }, []);
23
- return { state, captureComplete, reset };
23
+ return { state: state2, captureComplete, reset };
24
24
  }
25
25
 
26
26
  // src/overlay/capture.ts
@@ -31,7 +31,9 @@ var DEFAULT_FRAME_SETTINGS = {
31
31
  bgType: "color",
32
32
  bgColor: "#000000",
33
33
  bgImage: null,
34
- padding: 40
34
+ padding: 40,
35
+ browserChrome: false,
36
+ browserTheme: "dark"
35
37
  };
36
38
  var FRAME_SIZE_PRESETS = [
37
39
  { label: "1920 x 1080", hint: "Desktop / HD", w: 1920, h: 1080 },
@@ -62,7 +64,7 @@ async function toPngDataUrl(el, opts) {
62
64
  async function capture(options) {
63
65
  const { mode, element } = options;
64
66
  if (mode === "viewport") {
65
- return captureViewport();
67
+ return captureViewport(options.frameSettings);
66
68
  }
67
69
  if (mode === "fullpage") {
68
70
  return captureFullPage();
@@ -72,19 +74,19 @@ async function capture(options) {
72
74
  }
73
75
  throw new Error(`Invalid capture mode: ${mode}`);
74
76
  }
75
- async function captureViewport() {
77
+ async function captureViewport(frameSettings) {
76
78
  const dpr = window.devicePixelRatio || 1;
77
79
  const vw = window.innerWidth;
78
80
  const vh = window.innerHeight;
79
81
  const scrollY = window.scrollY;
80
82
  const fullDataUrl = await captureFullPage();
81
- const img = await loadImage(fullDataUrl);
83
+ const fullImg = await loadImage(fullDataUrl);
82
84
  const canvas = document.createElement("canvas");
83
85
  canvas.width = vw * dpr;
84
86
  canvas.height = vh * dpr;
85
87
  const ctx = canvas.getContext("2d");
86
88
  ctx.drawImage(
87
- img,
89
+ fullImg,
88
90
  0,
89
91
  scrollY * dpr,
90
92
  vw * dpr,
@@ -94,8 +96,77 @@ async function captureViewport() {
94
96
  vw * dpr,
95
97
  vh * dpr
96
98
  );
99
+ const viewportDataUrl = canvas.toDataURL("image/png");
100
+ if (!frameSettings?.browserChrome) {
101
+ return viewportDataUrl;
102
+ }
103
+ const viewportImg = await loadImage(viewportDataUrl);
104
+ return drawBrowserChrome(viewportImg, frameSettings.browserTheme, dpr);
105
+ }
106
+ function drawBrowserChrome(img, theme, dpr) {
107
+ const TITLE_BAR_H = 40;
108
+ const URL_BAR_H = 28;
109
+ const URL_BAR_MARGIN_TOP = 6;
110
+ const URL_BAR_MARGIN_BOTTOM = 6;
111
+ const CHROME_H = TITLE_BAR_H + URL_BAR_H + URL_BAR_MARGIN_TOP + URL_BAR_MARGIN_BOTTOM;
112
+ const imgW = img.width;
113
+ const imgH = img.height;
114
+ const canvas = document.createElement("canvas");
115
+ canvas.width = imgW;
116
+ canvas.height = imgH + CHROME_H * dpr;
117
+ const ctx = canvas.getContext("2d");
118
+ const colors = theme === "dark" ? { titleBar: "#1C1C1C", urlBar: "#262626", text: "#7B7B7B", border: "#333333" } : { titleBar: "#F5F5F5", urlBar: "#FFFFFF", text: "#999999", border: "#EBEBEB" };
119
+ ctx.fillStyle = colors.titleBar;
120
+ ctx.fillRect(0, 0, canvas.width, CHROME_H * dpr);
121
+ ctx.fillStyle = colors.border;
122
+ ctx.fillRect(0, (CHROME_H - 1) * dpr, canvas.width, dpr);
123
+ const dotY = TITLE_BAR_H / 2 * dpr;
124
+ const dotR = 6 * dpr;
125
+ const dotStartX = 18 * dpr;
126
+ const dotGap = 20 * dpr;
127
+ const dotColors = ["#FF5F57", "#FEBC2E", "#28C840"];
128
+ for (let i = 0; i < 3; i++) {
129
+ ctx.beginPath();
130
+ ctx.arc(dotStartX + i * dotGap, dotY, dotR, 0, Math.PI * 2);
131
+ ctx.fillStyle = dotColors[i];
132
+ ctx.fill();
133
+ }
134
+ const urlBarY = (TITLE_BAR_H + URL_BAR_MARGIN_TOP) * dpr;
135
+ const urlBarPadX = 80 * dpr;
136
+ const urlBarX = urlBarPadX;
137
+ const urlBarW = canvas.width - urlBarPadX * 2;
138
+ const urlBarH = URL_BAR_H * dpr;
139
+ const urlBarR = 6 * dpr;
140
+ ctx.fillStyle = colors.urlBar;
141
+ roundRect(ctx, urlBarX, urlBarY, urlBarW, urlBarH, urlBarR);
142
+ ctx.fill();
143
+ if (theme === "light") {
144
+ ctx.strokeStyle = colors.border;
145
+ ctx.lineWidth = dpr;
146
+ roundRect(ctx, urlBarX, urlBarY, urlBarW, urlBarH, urlBarR);
147
+ ctx.stroke();
148
+ }
149
+ ctx.fillStyle = colors.text;
150
+ ctx.font = `${11 * dpr}px -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif`;
151
+ ctx.textAlign = "center";
152
+ ctx.textBaseline = "middle";
153
+ ctx.fillText("localhost", canvas.width / 2, urlBarY + urlBarH / 2);
154
+ ctx.drawImage(img, 0, CHROME_H * dpr);
97
155
  return canvas.toDataURL("image/png");
98
156
  }
157
+ function roundRect(ctx, x, y, w, h, r) {
158
+ ctx.beginPath();
159
+ ctx.moveTo(x + r, y);
160
+ ctx.lineTo(x + w - r, y);
161
+ ctx.quadraticCurveTo(x + w, y, x + w, y + r);
162
+ ctx.lineTo(x + w, y + h - r);
163
+ ctx.quadraticCurveTo(x + w, y + h, x + w - r, y + h);
164
+ ctx.lineTo(x + r, y + h);
165
+ ctx.quadraticCurveTo(x, y + h, x, y + h - r);
166
+ ctx.lineTo(x, y + r);
167
+ ctx.quadraticCurveTo(x, y, x + r, y);
168
+ ctx.closePath();
169
+ }
99
170
  async function captureFullPage() {
100
171
  const scrollY = window.scrollY;
101
172
  const body = document.body;
@@ -177,6 +248,64 @@ function loadImage(src) {
177
248
  });
178
249
  }
179
250
 
251
+ // src/overlay/font.ts
252
+ var FONT_FAMILY = "'Inter var', 'Inter', system-ui, -apple-system, sans-serif";
253
+ var FONT_FEATURE_SETTINGS = "'ss11' 1, 'calt' 1";
254
+ var fontBase = {
255
+ fontFamily: FONT_FAMILY,
256
+ fontFeatureSettings: FONT_FEATURE_SETTINGS
257
+ };
258
+ function style(fontSize, lineHeight, fontWeight, letterSpacing, extra) {
259
+ return { fontSize, lineHeight: `${lineHeight}px`, fontWeight, letterSpacing, ...extra };
260
+ }
261
+ var text = {
262
+ // --- Title (Medium 500) ---
263
+ title: {
264
+ h1: style(56, 64, 500, "-0.01em"),
265
+ h2: style(48, 56, 500, "-0.01em"),
266
+ h3: style(40, 48, 500, "-0.01em"),
267
+ h4: style(32, 40, 500, "-0.005em"),
268
+ h5: style(24, 32, 500, "0em"),
269
+ h6: style(20, 28, 500, "0em")
270
+ },
271
+ // --- Label (Medium 500) ---
272
+ label: {
273
+ xl: style(24, 32, 500, "-0.015em"),
274
+ lg: style(18, 24, 500, "-0.015em"),
275
+ md: style(16, 24, 500, "-0.011em"),
276
+ sm: style(14, 20, 500, "-0.006em"),
277
+ xs: style(12, 16, 500, "0em")
278
+ },
279
+ // --- Paragraph (Regular 400) ---
280
+ paragraph: {
281
+ xl: style(24, 32, 400, "-0.015em"),
282
+ lg: style(18, 24, 400, "-0.015em"),
283
+ md: style(16, 24, 400, "-0.011em"),
284
+ sm: style(14, 20, 400, "-0.006em"),
285
+ xs: style(12, 16, 400, "0em")
286
+ },
287
+ // --- Subheading (Medium 500, uppercase) ---
288
+ subheading: {
289
+ md: style(16, 24, 500, "0.06em", { textTransform: "uppercase" }),
290
+ sm: style(14, 20, 500, "0.06em", { textTransform: "uppercase" }),
291
+ xs: style(12, 16, 500, "0.04em", { textTransform: "uppercase" }),
292
+ xxs: style(11, 12, 500, "0.02em", { textTransform: "uppercase" })
293
+ }
294
+ };
295
+ var INTER_CSS_ID = "afterbefore-inter-font";
296
+ function injectInterFont() {
297
+ if (document.getElementById(INTER_CSS_ID)) return;
298
+ const preconnect = document.createElement("link");
299
+ preconnect.rel = "preconnect";
300
+ preconnect.href = "https://rsms.me/";
301
+ document.head.appendChild(preconnect);
302
+ const stylesheet = document.createElement("link");
303
+ stylesheet.id = INTER_CSS_ID;
304
+ stylesheet.rel = "stylesheet";
305
+ stylesheet.href = "https://rsms.me/inter/inter.css";
306
+ document.head.appendChild(stylesheet);
307
+ }
308
+
180
309
  // src/overlay/ui/toolbar.tsx
181
310
  import { useCallback as useCallback2, useEffect as useEffect2, useRef as useRef2, useState as useState3 } from "react";
182
311
  import {
@@ -191,11 +320,73 @@ import {
191
320
  FileText,
192
321
  Monitor,
193
322
  MousePointer2,
194
- Settings,
195
323
  Trash2 as Trash22,
196
324
  X as X2
197
325
  } from "lucide-react";
198
326
 
327
+ // src/overlay/color.ts
328
+ var gray = {
329
+ 950: "#171717",
330
+ 900: "#1C1C1C",
331
+ 800: "#262626",
332
+ 700: "#333333",
333
+ 600: "#5C5C5C",
334
+ 500: "#7B7B7B",
335
+ 400: "#A3A3A3",
336
+ 300: "#D1D1D1",
337
+ 200: "#EBEBEB",
338
+ 100: "#F5F5F5",
339
+ 50: "#F7F7F7",
340
+ 0: "#FFFFFF"
341
+ };
342
+ var bg = {
343
+ base: gray[900],
344
+ elevated: gray[950],
345
+ dropdown: gray[900]
346
+ };
347
+ var fg = {
348
+ strong: gray[200],
349
+ default: gray[300],
350
+ sub: gray[500],
351
+ muted: gray[600],
352
+ faint: gray[600]
353
+ };
354
+ var stroke = {
355
+ soft: "rgba(255, 255, 255, 0.08)",
356
+ default: "rgba(255, 255, 255, 0.1)",
357
+ strong: "rgba(255, 255, 255, 0.12)",
358
+ interactive: "rgba(255, 255, 255, 0.18)"
359
+ };
360
+ var state = {
361
+ subtle: "rgba(255, 255, 255, 0.04)",
362
+ button: "rgba(255, 255, 255, 0.06)",
363
+ input: "rgba(255, 255, 255, 0.07)",
364
+ active: "rgba(255, 255, 255, 0.08)",
365
+ hover: "rgba(255, 255, 255, 0.1)",
366
+ hoverStrong: "rgba(255, 255, 255, 0.12)",
367
+ pressed: "rgba(255, 255, 255, 0.14)"
368
+ };
369
+ var accent = {
370
+ primary: "#335CFF",
371
+ toggle: "#38bdf8",
372
+ check: "#4ade80",
373
+ highlight: "rgba(125, 211, 252, 0.96)"
374
+ };
375
+ var feedback = {
376
+ success: "rgba(34, 197, 94, 0.9)",
377
+ error: "rgba(239, 68, 68, 0.9)",
378
+ errorText: "rgba(239, 68, 68, 0.9)",
379
+ errorBg: "rgba(239, 68, 68, 0.1)"
380
+ };
381
+ var shadow = {
382
+ toolbar: "0 8px 32px rgba(0, 0, 0, 0.4)",
383
+ panel: "0 14px 36px rgba(0, 0, 0, 0.32)",
384
+ dropdown: "0 10px 30px rgba(0, 0, 0, 0.3)",
385
+ tooltip: "0 8px 28px rgba(0, 0, 0, 0.28)",
386
+ toast: "0 2px 8px rgba(0, 0, 0, 0.3)",
387
+ status: "0 4px 20px rgba(0, 0, 0, 0.4), 0 0 0 1px rgba(255, 255, 255, 0.08)"
388
+ };
389
+
199
390
  // src/overlay/ui/settings-panel.tsx
200
391
  import { useEffect, useRef, useState as useState2 } from "react";
201
392
  import {
@@ -207,140 +398,110 @@ import {
207
398
  X
208
399
  } from "lucide-react";
209
400
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
210
- function SettingsPanel({
211
- style,
212
- onClose,
213
- selectedMode,
401
+ function SettingsContent({
214
402
  frameSettings,
215
- onFrameSettingsChange
403
+ onFrameSettingsChange,
404
+ saveDir,
405
+ picking,
406
+ onPickFolder
216
407
  }) {
217
- const [saveDir, setSaveDir] = useState2(null);
218
- const [picking, setPicking] = useState2(false);
219
- useEffect(() => {
220
- fetch("/__afterbefore/config").then((r) => r.json()).then((data) => setSaveDir(data.saveDir)).catch(() => {
221
- });
222
- }, []);
223
- const handlePickFolder = async () => {
224
- setPicking(true);
225
- try {
226
- const res = await fetch("/__afterbefore/pick-folder", { method: "POST" });
227
- const data = await res.json();
228
- if (data.folder) {
229
- await fetch("/__afterbefore/config", {
230
- method: "POST",
231
- headers: { "Content-Type": "application/json" },
232
- body: JSON.stringify({ saveDir: data.folder })
233
- });
234
- setSaveDir(data.folder);
408
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
409
+ /* @__PURE__ */ jsx(
410
+ SettingsRow,
411
+ {
412
+ title: "Frame",
413
+ control: /* @__PURE__ */ jsx(
414
+ ToggleSwitch,
415
+ {
416
+ enabled: frameSettings.enabled,
417
+ onChange: () => onFrameSettingsChange({ ...frameSettings, enabled: !frameSettings.enabled })
418
+ }
419
+ )
235
420
  }
236
- } catch {
237
- } finally {
238
- setPicking(false);
239
- }
240
- };
241
- const shortDir = saveDir ? saveDir.replace(/^\/Users\/[^/]+/, "~") : "~/Desktop";
242
- return /* @__PURE__ */ jsxs(
243
- "div",
244
- {
245
- style: {
246
- minWidth: 300,
247
- padding: "12px 16px",
248
- borderRadius: 12,
249
- background: "rgb(32, 32, 36)",
250
- border: "1px solid rgba(255, 255, 255, 0.1)",
251
- boxShadow: "0 14px 36px rgba(0, 0, 0, 0.32)",
252
- ...style
253
- },
254
- children: [
255
- /* @__PURE__ */ jsx(SettingsHeader, { onClose }),
256
- /* @__PURE__ */ jsx(SettingsDivider, {}),
257
- selectedMode === "component" && /* @__PURE__ */ jsxs(Fragment, { children: [
258
- /* @__PURE__ */ jsx(
259
- SettingsRow,
260
- {
261
- title: "Frame",
262
- description: "Wrap in a sized canvas with background",
263
- control: /* @__PURE__ */ jsx(
264
- ToggleSwitch,
265
- {
266
- enabled: frameSettings.enabled,
267
- onChange: () => onFrameSettingsChange({ ...frameSettings, enabled: !frameSettings.enabled })
268
- }
269
- )
270
- }
271
- ),
272
- frameSettings.enabled && /* @__PURE__ */ jsxs(Fragment, { children: [
273
- /* @__PURE__ */ jsx(SettingsDivider, {}),
421
+ ),
422
+ frameSettings.enabled && /* @__PURE__ */ jsxs(Fragment, { children: [
423
+ /* @__PURE__ */ jsx(SettingsDivider, {}),
424
+ /* @__PURE__ */ jsx(
425
+ FrameSizeControl,
426
+ {
427
+ size: frameSettings.size,
428
+ onChange: (size) => onFrameSettingsChange({ ...frameSettings, size })
429
+ }
430
+ ),
431
+ /* @__PURE__ */ jsx(SettingsDivider, {}),
432
+ /* @__PURE__ */ jsx(
433
+ FrameBackgroundControl,
434
+ {
435
+ bgType: frameSettings.bgType,
436
+ bgColor: frameSettings.bgColor,
437
+ bgImage: frameSettings.bgImage,
438
+ frameSize: frameSettings.size,
439
+ onChange: (updates) => onFrameSettingsChange({ ...frameSettings, ...updates })
440
+ }
441
+ )
442
+ ] }),
443
+ /* @__PURE__ */ jsx(SettingsDivider, {}),
444
+ /* @__PURE__ */ jsx(
445
+ SettingsRow,
446
+ {
447
+ title: "Browser Chrome",
448
+ control: /* @__PURE__ */ jsx(
449
+ ToggleSwitch,
450
+ {
451
+ enabled: frameSettings.browserChrome,
452
+ onChange: () => onFrameSettingsChange({ ...frameSettings, browserChrome: !frameSettings.browserChrome })
453
+ }
454
+ )
455
+ }
456
+ ),
457
+ frameSettings.browserChrome && /* @__PURE__ */ jsxs(Fragment, { children: [
458
+ /* @__PURE__ */ jsx(SettingsDivider, {}),
459
+ /* @__PURE__ */ jsx(
460
+ SettingsRow,
461
+ {
462
+ title: "Theme",
463
+ control: /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: 2, flexShrink: 0 }, children: [
274
464
  /* @__PURE__ */ jsx(
275
- FrameSizeControl,
465
+ SegmentButton,
276
466
  {
277
- size: frameSettings.size,
278
- onChange: (size) => onFrameSettingsChange({ ...frameSettings, size })
467
+ active: frameSettings.browserTheme === "light",
468
+ onClick: () => onFrameSettingsChange({ ...frameSettings, browserTheme: "light" }),
469
+ style: { borderRadius: "6px 0 0 6px" },
470
+ children: "Light"
279
471
  }
280
472
  ),
281
- /* @__PURE__ */ jsx(SettingsDivider, {}),
282
473
  /* @__PURE__ */ jsx(
283
- FrameBackgroundControl,
474
+ SegmentButton,
284
475
  {
285
- bgType: frameSettings.bgType,
286
- bgColor: frameSettings.bgColor,
287
- bgImage: frameSettings.bgImage,
288
- frameSize: frameSettings.size,
289
- onChange: (updates) => onFrameSettingsChange({ ...frameSettings, ...updates })
476
+ active: frameSettings.browserTheme === "dark",
477
+ onClick: () => onFrameSettingsChange({ ...frameSettings, browserTheme: "dark" }),
478
+ style: { borderRadius: "0 6px 6px 0" },
479
+ children: "Dark"
290
480
  }
291
481
  )
292
- ] }),
293
- /* @__PURE__ */ jsx(SettingsDivider, {})
294
- ] }),
295
- /* @__PURE__ */ jsx(
296
- SettingsRow,
482
+ ] })
483
+ }
484
+ )
485
+ ] }),
486
+ /* @__PURE__ */ jsx(SettingsDivider, {}),
487
+ saveDir !== void 0 && onPickFolder && /* @__PURE__ */ jsx(
488
+ SettingsRow,
489
+ {
490
+ title: "Save Location",
491
+ description: /* @__PURE__ */ jsx(
492
+ "span",
297
493
  {
298
- title: "Save Location",
299
- description: /* @__PURE__ */ jsx(
300
- "span",
301
- {
302
- style: {
303
- overflow: "hidden",
304
- textOverflow: "ellipsis",
305
- whiteSpace: "nowrap",
306
- display: "block"
307
- },
308
- title: shortDir,
309
- children: shortDir
310
- }
311
- ),
312
- control: /* @__PURE__ */ jsx(SmallButton, { onClick: handlePickFolder, children: picking ? "..." : "Change" })
494
+ style: {
495
+ overflow: "hidden",
496
+ textOverflow: "ellipsis",
497
+ whiteSpace: "nowrap",
498
+ display: "block"
499
+ },
500
+ title: saveDir,
501
+ children: saveDir
313
502
  }
314
- )
315
- ]
316
- }
317
- );
318
- }
319
- function SettingsHeader({ onClose }) {
320
- const [hovered, setHovered] = useState2(false);
321
- return /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", justifyContent: "space-between", paddingBottom: 8 }, children: [
322
- /* @__PURE__ */ jsx("span", { style: { fontSize: 14, fontWeight: 600, color: "rgba(255, 255, 255, 0.92)" }, children: "Settings" }),
323
- /* @__PURE__ */ jsx(
324
- "button",
325
- {
326
- onClick: onClose,
327
- onMouseEnter: () => setHovered(true),
328
- onMouseLeave: () => setHovered(false),
329
- style: {
330
- width: 24,
331
- height: 24,
332
- borderRadius: 6,
333
- border: "none",
334
- background: hovered ? "rgba(255, 255, 255, 0.1)" : "transparent",
335
- display: "flex",
336
- alignItems: "center",
337
- justifyContent: "center",
338
- cursor: "pointer",
339
- color: hovered ? "rgba(255, 255, 255, 0.8)" : "rgba(255, 255, 255, 0.46)",
340
- padding: 0,
341
- transition: "background 0.12s ease, color 0.12s ease"
342
- },
343
- children: /* @__PURE__ */ jsx(X, { size: 14, strokeWidth: 2 })
503
+ ),
504
+ control: /* @__PURE__ */ jsx(SmallButton, { onClick: onPickFolder, children: picking ? "..." : "Change" })
344
505
  }
345
506
  )
346
507
  ] });
@@ -350,16 +511,23 @@ function SettingsRow({
350
511
  description,
351
512
  control
352
513
  }) {
353
- return /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", justifyContent: "space-between", gap: 16, padding: "14px 0" }, children: [
354
- /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", gap: 3, flex: 1, minWidth: 0 }, children: [
355
- /* @__PURE__ */ jsx("span", { style: { fontSize: 13, fontWeight: 600, color: "rgba(255, 255, 255, 0.92)" }, children: title }),
356
- description && /* @__PURE__ */ jsx("span", { style: { fontSize: 11, color: "rgba(255, 255, 255, 0.42)", lineHeight: 1.3 }, children: description })
514
+ return /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", justifyContent: "space-between", gap: 16, padding: "10px 0" }, children: [
515
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", gap: 2, flex: 1, minWidth: 0 }, children: [
516
+ /* @__PURE__ */ jsx("span", { style: { ...text.label.sm, color: fg.strong }, children: title }),
517
+ description && /* @__PURE__ */ jsx("span", { style: { ...text.paragraph.xs, color: fg.muted }, children: description })
357
518
  ] }),
358
519
  /* @__PURE__ */ jsx("div", { style: { flexShrink: 0 }, children: control })
359
520
  ] });
360
521
  }
361
522
  function SettingsDivider() {
362
- return /* @__PURE__ */ jsx("div", { style: { height: 1, background: "rgba(255, 255, 255, 0.08)" } });
523
+ return /* @__PURE__ */ jsx(
524
+ "div",
525
+ {
526
+ style: {
527
+ borderBottom: `1px solid ${stroke.soft}`
528
+ }
529
+ }
530
+ );
363
531
  }
364
532
  function ToggleSwitch({
365
533
  enabled,
@@ -375,7 +543,7 @@ function ToggleSwitch({
375
543
  height: 22,
376
544
  borderRadius: 999,
377
545
  border: "none",
378
- background: enabled ? "#38bdf8" : "rgba(255, 255, 255, 0.18)",
546
+ background: enabled ? accent.toggle : stroke.interactive,
379
547
  position: "relative",
380
548
  cursor: "pointer",
381
549
  padding: 0,
@@ -407,12 +575,9 @@ function FrameSizeControl({
407
575
  const [sizeOpen, setSizeOpen] = useState2(false);
408
576
  const currentPreset = FRAME_SIZE_PRESETS.find((p) => p.w === size.w && p.h === size.h);
409
577
  const isCustom = !currentPreset;
410
- return /* @__PURE__ */ jsxs("div", { style: { padding: "14px 0" }, children: [
578
+ return /* @__PURE__ */ jsxs("div", { style: { padding: "10px 0" }, children: [
411
579
  /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", justifyContent: "space-between", gap: 16 }, children: [
412
- /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", gap: 3 }, children: [
413
- /* @__PURE__ */ jsx("span", { style: { fontSize: 13, fontWeight: 600, color: "rgba(255, 255, 255, 0.92)" }, children: "Size" }),
414
- /* @__PURE__ */ jsx("span", { style: { fontSize: 11, color: "rgba(255, 255, 255, 0.42)", lineHeight: 1.3 }, children: "Set the frame dimensions" })
415
- ] }),
580
+ /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx("span", { style: { ...text.label.sm, color: fg.strong }, children: "Size" }) }),
416
581
  /* @__PURE__ */ jsxs("div", { style: { position: "relative", flexShrink: 0 }, children: [
417
582
  /* @__PURE__ */ jsxs(
418
583
  "button",
@@ -425,11 +590,11 @@ function FrameSizeControl({
425
590
  height: 30,
426
591
  padding: "0 10px",
427
592
  borderRadius: 7,
428
- border: "1px solid rgba(255,255,255,0.1)",
429
- background: "rgba(255,255,255,0.07)",
430
- color: "rgba(255,255,255,0.88)",
593
+ border: `1px solid ${stroke.default}`,
594
+ background: state.input,
595
+ color: fg.strong,
431
596
  cursor: "pointer",
432
- fontSize: 12,
597
+ ...text.label.xs,
433
598
  fontFamily: "inherit",
434
599
  whiteSpace: "nowrap"
435
600
  },
@@ -447,11 +612,11 @@ function FrameSizeControl({
447
612
  bottom: "calc(100% + 4px)",
448
613
  right: 0,
449
614
  minWidth: 180,
450
- background: "rgb(32, 32, 36)",
451
- border: "1px solid rgba(255, 255, 255, 0.1)",
615
+ background: bg.base,
616
+ border: `1px solid ${stroke.default}`,
452
617
  borderRadius: 8,
453
618
  padding: "4px 0",
454
- boxShadow: "0 10px 30px rgba(0, 0, 0, 0.3)",
619
+ boxShadow: shadow.dropdown,
455
620
  zIndex: 1
456
621
  },
457
622
  children: FRAME_SIZE_PRESETS.map((preset) => /* @__PURE__ */ jsxs(
@@ -464,7 +629,7 @@ function FrameSizeControl({
464
629
  },
465
630
  children: [
466
631
  /* @__PURE__ */ jsx("span", { children: preset.label }),
467
- /* @__PURE__ */ jsx("span", { style: { marginLeft: 6, fontSize: 10, color: "rgba(255,255,255,0.34)" }, children: preset.hint })
632
+ /* @__PURE__ */ jsx("span", { style: { marginLeft: 6, fontSize: 10, color: fg.faint }, children: preset.hint })
468
633
  ]
469
634
  },
470
635
  preset.label
@@ -523,12 +688,9 @@ function FrameBackgroundControl({
523
688
  reader.readAsDataURL(file);
524
689
  e.target.value = "";
525
690
  };
526
- return /* @__PURE__ */ jsxs("div", { style: { padding: "14px 0" }, children: [
691
+ return /* @__PURE__ */ jsxs("div", { style: { padding: "10px 0" }, children: [
527
692
  /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", justifyContent: "space-between", gap: 16 }, children: [
528
- /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", gap: 3 }, children: [
529
- /* @__PURE__ */ jsx("span", { style: { fontSize: 13, fontWeight: 600, color: "rgba(255, 255, 255, 0.92)" }, children: "Background" }),
530
- /* @__PURE__ */ jsx("span", { style: { fontSize: 11, color: "rgba(255, 255, 255, 0.42)", lineHeight: 1.3 }, children: "Frame background color or image" })
531
- ] }),
693
+ /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx("span", { style: { ...text.label.sm, color: fg.strong }, children: "Background" }) }),
532
694
  /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: 2, flexShrink: 0 }, children: [
533
695
  /* @__PURE__ */ jsxs(
534
696
  SegmentButton,
@@ -582,7 +744,7 @@ function FrameBackgroundControl({
582
744
  height: 28,
583
745
  borderRadius: 4,
584
746
  objectFit: "cover",
585
- border: "1px solid rgba(255,255,255,0.12)"
747
+ border: `1px solid ${stroke.strong}`
586
748
  }
587
749
  }
588
750
  ),
@@ -602,7 +764,7 @@ function SegmentButton({
602
764
  children,
603
765
  active,
604
766
  onClick,
605
- style
767
+ style: style2
606
768
  }) {
607
769
  return /* @__PURE__ */ jsx(
608
770
  "button",
@@ -612,16 +774,16 @@ function SegmentButton({
612
774
  display: "flex",
613
775
  alignItems: "center",
614
776
  gap: 4,
615
- height: 26,
777
+ height: 28,
616
778
  padding: "0 10px",
617
- border: "1px solid rgba(255,255,255,0.1)",
618
- background: active ? "rgba(255,255,255,0.14)" : "rgba(255,255,255,0.04)",
619
- color: active ? "rgba(255,255,255,0.92)" : "rgba(255,255,255,0.5)",
779
+ border: `1px solid ${stroke.default}`,
780
+ background: active ? state.pressed : state.subtle,
781
+ color: active ? fg.strong : fg.sub,
620
782
  cursor: "pointer",
621
- fontSize: 11,
783
+ ...text.label.xs,
622
784
  fontFamily: "inherit",
623
785
  transition: "background 0.12s ease, color 0.12s ease",
624
- ...style
786
+ ...style2
625
787
  },
626
788
  children
627
789
  }
@@ -641,7 +803,7 @@ function ColorSwatch({
641
803
  width: 24,
642
804
  height: 24,
643
805
  borderRadius: 6,
644
- border: "1px solid rgba(255,255,255,0.18)",
806
+ border: `1px solid ${stroke.interactive}`,
645
807
  background: color,
646
808
  cursor: "pointer",
647
809
  padding: 0
@@ -683,12 +845,12 @@ function SmallButton({
683
845
  display: "flex",
684
846
  alignItems: "center",
685
847
  gap: 4,
686
- padding: "3px 8px",
848
+ padding: "4px 10px",
687
849
  borderRadius: 6,
688
- border: "1px solid rgba(255,255,255,0.12)",
689
- background: hovered ? "rgba(255,255,255,0.12)" : "rgba(255,255,255,0.06)",
690
- color: "rgba(255,255,255,0.78)",
691
- fontSize: 11,
850
+ border: `1px solid ${stroke.strong}`,
851
+ background: hovered ? state.hoverStrong : state.button,
852
+ color: fg.default,
853
+ ...text.label.xs,
692
854
  cursor: "pointer",
693
855
  fontFamily: "inherit",
694
856
  transition: "background 0.12s ease"
@@ -702,7 +864,7 @@ function NumInput({
702
864
  onChange
703
865
  }) {
704
866
  const [editing, setEditing] = useState2(false);
705
- const [text, setText] = useState2(String(value));
867
+ const [text2, setText] = useState2(String(value));
706
868
  useEffect(() => {
707
869
  if (!editing) {
708
870
  setText(String(value));
@@ -712,14 +874,14 @@ function NumInput({
712
874
  "input",
713
875
  {
714
876
  type: "text",
715
- value: editing ? text : String(value),
877
+ value: editing ? text2 : String(value),
716
878
  onFocus: () => {
717
879
  setEditing(true);
718
880
  setText(String(value));
719
881
  },
720
882
  onBlur: () => {
721
883
  setEditing(false);
722
- onChange(text);
884
+ onChange(text2);
723
885
  },
724
886
  onChange: (e) => setText(e.target.value),
725
887
  onKeyDown: (e) => {
@@ -730,12 +892,12 @@ function NumInput({
730
892
  style: {
731
893
  width: 54,
732
894
  padding: "4px 6px",
733
- background: "rgba(255, 255, 255, 0.07)",
734
- border: "1px solid rgba(255, 255, 255, 0.1)",
895
+ background: state.input,
896
+ border: `1px solid ${stroke.default}`,
735
897
  borderRadius: 7,
736
- color: "rgba(255, 255, 255, 0.9)",
737
- fontSize: 12,
738
- fontFamily: "system-ui, -apple-system, sans-serif",
898
+ color: fg.strong,
899
+ ...text.label.xs,
900
+ fontFamily: "inherit",
739
901
  textAlign: "center",
740
902
  outline: "none"
741
903
  }
@@ -747,7 +909,7 @@ function HexInput({
747
909
  onChange
748
910
  }) {
749
911
  const [editing, setEditing] = useState2(false);
750
- const [text, setText] = useState2(value);
912
+ const [text2, setText] = useState2(value);
751
913
  useEffect(() => {
752
914
  if (!editing) {
753
915
  setText(value);
@@ -763,14 +925,14 @@ function HexInput({
763
925
  "input",
764
926
  {
765
927
  type: "text",
766
- value: editing ? text : value,
928
+ value: editing ? text2 : value,
767
929
  onFocus: () => {
768
930
  setEditing(true);
769
931
  setText(value);
770
932
  },
771
933
  onBlur: () => {
772
934
  setEditing(false);
773
- commit(text);
935
+ commit(text2);
774
936
  },
775
937
  onChange: (e) => setText(e.target.value),
776
938
  onKeyDown: (e) => {
@@ -781,12 +943,12 @@ function HexInput({
781
943
  style: {
782
944
  width: 72,
783
945
  padding: "4px 6px",
784
- background: "rgba(255, 255, 255, 0.07)",
785
- border: "1px solid rgba(255, 255, 255, 0.1)",
946
+ background: state.input,
947
+ border: `1px solid ${stroke.default}`,
786
948
  borderRadius: 7,
787
- color: "rgba(255, 255, 255, 0.9)",
788
- fontSize: 12,
789
- fontFamily: "system-ui, -apple-system, sans-serif",
949
+ color: fg.strong,
950
+ ...text.label.xs,
951
+ fontFamily: "inherit",
790
952
  textAlign: "left",
791
953
  outline: "none"
792
954
  }
@@ -799,7 +961,7 @@ function StaticText({ children }) {
799
961
  {
800
962
  style: {
801
963
  fontSize: 11,
802
- color: "rgba(255,255,255,0.35)",
964
+ color: fg.faint,
803
965
  minWidth: 8,
804
966
  textAlign: "center"
805
967
  },
@@ -820,12 +982,12 @@ function DropItem({
820
982
  display: "block",
821
983
  width: "100%",
822
984
  padding: "7px 12px",
823
- background: active ? "rgba(255, 255, 255, 0.08)" : "transparent",
985
+ background: active ? state.active : "transparent",
824
986
  border: "none",
825
- color: "rgba(255, 255, 255, 0.86)",
987
+ color: fg.strong,
826
988
  textAlign: "left",
827
989
  cursor: "pointer",
828
- fontSize: 13,
990
+ ...text.paragraph.sm,
829
991
  fontFamily: "inherit"
830
992
  },
831
993
  children
@@ -879,6 +1041,20 @@ function snapToCorner(x, y) {
879
1041
  }
880
1042
  return y < cy ? "top-right" : "bottom-right";
881
1043
  }
1044
+ function getCornerPosition(corner, w, h) {
1045
+ const vw = window.innerWidth;
1046
+ const vh = window.innerHeight;
1047
+ switch (corner) {
1048
+ case "bottom-right":
1049
+ return { x: vw - EDGE_MARGIN - w, y: vh - EDGE_MARGIN - h };
1050
+ case "bottom-left":
1051
+ return { x: EDGE_MARGIN, y: vh - EDGE_MARGIN - h };
1052
+ case "top-right":
1053
+ return { x: vw - EDGE_MARGIN - w, y: EDGE_MARGIN };
1054
+ case "top-left":
1055
+ return { x: EDGE_MARGIN, y: EDGE_MARGIN };
1056
+ }
1057
+ }
882
1058
  var MODES = [
883
1059
  { mode: "component", label: "Component", icon: MousePointer2 },
884
1060
  { mode: "viewport", label: "Viewport", icon: Monitor },
@@ -896,8 +1072,29 @@ function Toolbar({
896
1072
  frameSettings,
897
1073
  onFrameSettingsChange
898
1074
  }) {
899
- const [settingsOpen, setSettingsOpen] = useState3(false);
900
1075
  const [historyOpen, setHistoryOpen] = useState3(false);
1076
+ const [modesExpanded, setModesExpanded] = useState3(true);
1077
+ const [buttonsVisible, setButtonsVisible] = useState3(expanded);
1078
+ const [animIn, setAnimIn] = useState3(expanded);
1079
+ const [animDone, setAnimDone] = useState3(expanded);
1080
+ useEffect2(() => {
1081
+ if (expanded) {
1082
+ setAnimDone(false);
1083
+ setButtonsVisible(true);
1084
+ setAnimIn(false);
1085
+ requestAnimationFrame(() => {
1086
+ requestAnimationFrame(() => setAnimIn(true));
1087
+ });
1088
+ const timer = setTimeout(() => setAnimDone(true), 250);
1089
+ return () => clearTimeout(timer);
1090
+ } else if (buttonsVisible) {
1091
+ setHistoryOpen(false);
1092
+ setAnimDone(false);
1093
+ setAnimIn(false);
1094
+ const timer = setTimeout(() => setButtonsVisible(false), 150);
1095
+ return () => clearTimeout(timer);
1096
+ }
1097
+ }, [expanded]);
901
1098
  const [corner, setCorner] = useState3(() => {
902
1099
  try {
903
1100
  const stored = localStorage.getItem("ab-toolbar-corner");
@@ -910,6 +1107,7 @@ function Toolbar({
910
1107
  });
911
1108
  const [dragging, setDragging] = useState3(false);
912
1109
  const [dragPos, setDragPos] = useState3(null);
1110
+ const [snapAnim, setSnapAnim] = useState3(null);
913
1111
  const dragState = useRef2(null);
914
1112
  const toolbarRef = useRef2(null);
915
1113
  const [cameraHovered, setCameraHovered] = useState3(false);
@@ -923,8 +1121,8 @@ function Toolbar({
923
1121
  return;
924
1122
  }
925
1123
  if (e.key === "Escape") {
926
- if (settingsOpen) {
927
- setSettingsOpen(false);
1124
+ if (historyOpen) {
1125
+ setHistoryOpen(false);
928
1126
  return;
929
1127
  }
930
1128
  onCancel();
@@ -934,7 +1132,7 @@ function Toolbar({
934
1132
  };
935
1133
  document.addEventListener("keydown", onKey);
936
1134
  return () => document.removeEventListener("keydown", onKey);
937
- }, [expanded, onCancel, onCapture, selectedMode, settingsOpen]);
1135
+ }, [expanded, onCancel, onCapture, selectedMode, historyOpen]);
938
1136
  const handleMouseDown = useCallback2(
939
1137
  (e) => {
940
1138
  e.preventDefault();
@@ -971,22 +1169,35 @@ function Toolbar({
971
1169
  if (!ds) return;
972
1170
  if (ds.distance < 5) {
973
1171
  onToggle();
1172
+ setDragging(false);
1173
+ setDragPos(null);
1174
+ dragState.current = null;
974
1175
  } else {
975
1176
  const el = toolbarRef.current;
976
1177
  const w = el?.offsetWidth ?? CONTAINER_SIZE;
977
1178
  const h = el?.offsetHeight ?? CONTAINER_SIZE;
978
- const centerX = ds.origX + (e.clientX - ds.startX) + w / 2;
979
- const centerY = ds.origY + (e.clientY - ds.startY) + h / 2;
1179
+ const currentX = ds.origX + (e.clientX - ds.startX);
1180
+ const currentY = ds.origY + (e.clientY - ds.startY);
1181
+ const centerX = currentX + w / 2;
1182
+ const centerY = currentY + h / 2;
980
1183
  const newCorner = snapToCorner(centerX, centerY);
981
1184
  setCorner(newCorner);
982
1185
  try {
983
1186
  localStorage.setItem("ab-toolbar-corner", newCorner);
984
1187
  } catch {
985
1188
  }
1189
+ const targetPos = getCornerPosition(newCorner, w, h);
1190
+ setDragging(false);
1191
+ setDragPos(null);
1192
+ setSnapAnim({ x: currentX, y: currentY, animate: false });
1193
+ dragState.current = null;
1194
+ requestAnimationFrame(() => {
1195
+ requestAnimationFrame(() => {
1196
+ setSnapAnim({ ...targetPos, animate: true });
1197
+ setTimeout(() => setSnapAnim(null), 300);
1198
+ });
1199
+ });
986
1200
  }
987
- setDragging(false);
988
- setDragPos(null);
989
- dragState.current = null;
990
1201
  };
991
1202
  window.addEventListener("mousemove", handleMouseMove);
992
1203
  window.addEventListener("mouseup", handleMouseUp);
@@ -998,7 +1209,13 @@ function Toolbar({
998
1209
  const panelSide = isRightCorner(corner) ? "left" : "right";
999
1210
  const tooltipSide = panelSide;
1000
1211
  const bottom = isBottomCorner(corner);
1001
- const positionStyle = dragging && dragPos ? { left: dragPos.x, top: dragPos.y } : getCornerStyle(corner);
1212
+ const positionStyle = dragging && dragPos ? { left: dragPos.x, top: dragPos.y } : snapAnim ? {
1213
+ left: snapAnim.x,
1214
+ top: snapAnim.y,
1215
+ ...snapAnim.animate && {
1216
+ transition: "left 0.3s cubic-bezier(0.23, 1, 0.32, 1), top 0.3s cubic-bezier(0.23, 1, 0.32, 1)"
1217
+ }
1218
+ } : getCornerStyle(corner);
1002
1219
  const cameraTooltipLabel = expanded ? "Close" : void 0;
1003
1220
  const cameraTooltipStyle = cameraTooltipLabel ? tooltipSide === "left" ? { right: "calc(100% + 10px)", top: "50%", transform: "translateY(-50%)" } : { left: "calc(100% + 10px)", top: "50%", transform: "translateY(-50%)" } : void 0;
1004
1221
  const cameraButton = /* @__PURE__ */ jsxs2("div", { style: { position: "relative" }, children: [
@@ -1008,14 +1225,17 @@ function Toolbar({
1008
1225
  style: {
1009
1226
  position: "absolute",
1010
1227
  ...cameraTooltipStyle,
1011
- background: "rgb(32, 32, 36)",
1012
- border: "1px solid rgba(255, 255, 255, 0.1)",
1228
+ background: bg.base,
1229
+ border: `1px solid ${stroke.default}`,
1013
1230
  borderRadius: 6,
1014
- padding: "3px 8px",
1015
- color: "rgba(255, 255, 255, 0.88)",
1016
- fontSize: 11,
1231
+ padding: "0 8px",
1232
+ height: 24,
1233
+ display: "flex",
1234
+ alignItems: "center",
1235
+ color: fg.strong,
1236
+ ...text.label.xs,
1017
1237
  whiteSpace: "nowrap",
1018
- boxShadow: "0 8px 28px rgba(0, 0, 0, 0.28)",
1238
+ boxShadow: shadow.tooltip,
1019
1239
  pointerEvents: "none"
1020
1240
  },
1021
1241
  children: cameraTooltipLabel
@@ -1036,7 +1256,7 @@ function Toolbar({
1036
1256
  alignItems: "center",
1037
1257
  justifyContent: "center",
1038
1258
  cursor: dragging ? "grabbing" : "pointer",
1039
- background: expanded && cameraHovered ? "rgba(255, 255, 255, 0.12)" : "transparent",
1259
+ background: expanded && cameraHovered ? state.hoverStrong : "transparent",
1040
1260
  transition: "background 0.12s ease"
1041
1261
  },
1042
1262
  children: [
@@ -1048,7 +1268,12 @@ function Toolbar({
1048
1268
  @keyframes ab-spin {
1049
1269
  0% { transform: rotate(0deg); }
1050
1270
  100% { transform: rotate(360deg); }
1051
- }`
1271
+ }
1272
+ @keyframes ab-panel-in {
1273
+ from { opacity: 0; transform: translateY(4px); }
1274
+ to { opacity: 1; transform: translateY(0); }
1275
+ }
1276
+ `
1052
1277
  }
1053
1278
  }
1054
1279
  ),
@@ -1059,78 +1284,111 @@ function Toolbar({
1059
1284
  strokeWidth: 2,
1060
1285
  style: { animation: "ab-spin 0.8s linear infinite", color: "white" }
1061
1286
  }
1062
- ) : phase === "ready" ? /* @__PURE__ */ jsx2(Check, { size: 16, strokeWidth: 2.6, color: "#4ade80" }) : expanded ? /* @__PURE__ */ jsx2(
1287
+ ) : phase === "ready" ? /* @__PURE__ */ jsx2(Check, { size: 16, strokeWidth: 2.6, color: accent.check }) : expanded ? /* @__PURE__ */ jsx2(
1063
1288
  X2,
1064
1289
  {
1065
1290
  size: 16,
1066
1291
  strokeWidth: 1.7,
1067
- color: cameraHovered ? "rgba(255, 255, 255, 0.96)" : "rgba(255, 255, 255, 0.52)"
1292
+ color: cameraHovered ? fg.strong : fg.sub
1068
1293
  }
1069
1294
  ) : /* @__PURE__ */ jsx2(
1070
1295
  Camera,
1071
1296
  {
1072
1297
  size: 16,
1073
1298
  strokeWidth: 1.9,
1074
- color: "rgba(255, 255, 255, 0.52)"
1299
+ color: fg.sub
1075
1300
  }
1076
1301
  )
1077
1302
  ]
1078
1303
  }
1079
1304
  )
1080
1305
  ] });
1081
- const toolbarButtons = expanded ? /* @__PURE__ */ jsxs2(Fragment2, { children: [
1082
- /* @__PURE__ */ jsx2("div", { style: { display: "flex", flexDirection: "column", alignItems: "center", gap: 2, padding: "4px 0" }, children: MODES.map(({ mode, label, icon: ModeIcon }) => /* @__PURE__ */ jsx2(
1083
- IconButton,
1084
- {
1085
- active: selectedMode === mode,
1086
- tooltipSide,
1087
- tooltip: label,
1088
- onClick: () => {
1089
- setSettingsOpen(false);
1090
- setHistoryOpen(false);
1091
- if (mode === "viewport" || mode === "fullpage") {
1092
- onModeChange(mode);
1093
- onCapture(mode);
1094
- } else {
1095
- onModeChange(mode);
1096
- }
1097
- },
1098
- children: /* @__PURE__ */ jsx2(ModeIcon, { size: 16, strokeWidth: 1.7 })
1306
+ const toolbarButtons = buttonsVisible ? /* @__PURE__ */ jsx2(
1307
+ "div",
1308
+ {
1309
+ style: {
1310
+ overflow: animDone ? "visible" : "hidden",
1311
+ maxHeight: animIn ? 195 : 0,
1312
+ transition: animIn ? "max-height 250ms cubic-bezier(0.23, 1, 0.32, 1)" : "max-height 150ms cubic-bezier(0.23, 1, 0.32, 1)"
1099
1313
  },
1100
- mode
1101
- )) }),
1102
- /* @__PURE__ */ jsx2(Separator, { vertical: false }),
1103
- /* @__PURE__ */ jsx2(
1104
- SettingsButton,
1105
- {
1106
- open: settingsOpen,
1107
- onClick: () => {
1108
- setSettingsOpen((prev) => !prev);
1109
- setHistoryOpen(false);
1110
- },
1111
- selectedMode,
1112
- frameSettings,
1113
- onFrameSettingsChange,
1114
- panelSide,
1115
- tooltipSide,
1116
- bottom
1117
- }
1118
- ),
1119
- /* @__PURE__ */ jsx2(
1120
- HistoryButton,
1121
- {
1122
- open: historyOpen,
1123
- onClick: () => {
1124
- setHistoryOpen((prev) => !prev);
1125
- setSettingsOpen(false);
1126
- },
1127
- panelSide,
1128
- tooltipSide,
1129
- bottom
1130
- }
1131
- ),
1132
- /* @__PURE__ */ jsx2(Separator, { vertical: false })
1133
- ] }) : null;
1314
+ children: /* @__PURE__ */ jsxs2(
1315
+ "div",
1316
+ {
1317
+ style: {
1318
+ display: "flex",
1319
+ flexDirection: "column",
1320
+ alignItems: "center",
1321
+ opacity: animIn ? 1 : 0,
1322
+ transform: animIn ? "translateY(0)" : `translateY(${bottom ? 6 : -6}px)`,
1323
+ transition: animIn ? "opacity 200ms cubic-bezier(0.23, 1, 0.32, 1), transform 200ms cubic-bezier(0.23, 1, 0.32, 1)" : "opacity 150ms cubic-bezier(0.23, 1, 0.32, 1), transform 150ms cubic-bezier(0.23, 1, 0.32, 1)",
1324
+ willChange: "transform, opacity"
1325
+ },
1326
+ children: [
1327
+ /* @__PURE__ */ jsx2("div", { style: { paddingBottom: 2 }, children: /* @__PURE__ */ jsx2(
1328
+ IconButton,
1329
+ {
1330
+ active: selectedMode === "component" && !historyOpen,
1331
+ tooltipSide,
1332
+ tooltip: "Component",
1333
+ onClick: () => {
1334
+ setHistoryOpen(false);
1335
+ onModeChange("component");
1336
+ },
1337
+ children: /* @__PURE__ */ jsx2(MousePointer2, { size: 16, strokeWidth: 1.7 })
1338
+ }
1339
+ ) }),
1340
+ MODES.filter((m) => m.mode !== "component").map(({ mode, label, icon: ModeIcon }) => /* @__PURE__ */ jsx2(
1341
+ "div",
1342
+ {
1343
+ style: {
1344
+ maxHeight: modesExpanded ? 34 : 0,
1345
+ opacity: modesExpanded ? 1 : 0,
1346
+ transition: "max-height 200ms cubic-bezier(0.23, 1, 0.32, 1), opacity 150ms cubic-bezier(0.23, 1, 0.32, 1)"
1347
+ },
1348
+ children: /* @__PURE__ */ jsx2(
1349
+ IconButton,
1350
+ {
1351
+ active: selectedMode === mode && !historyOpen,
1352
+ tooltipSide,
1353
+ tooltip: label,
1354
+ onClick: () => {
1355
+ setHistoryOpen(false);
1356
+ onModeChange(mode);
1357
+ onCapture(mode);
1358
+ },
1359
+ children: /* @__PURE__ */ jsx2(ModeIcon, { size: 16, strokeWidth: 1.7 })
1360
+ }
1361
+ )
1362
+ },
1363
+ mode
1364
+ )),
1365
+ /* @__PURE__ */ jsx2(
1366
+ Separator,
1367
+ {
1368
+ vertical: false,
1369
+ onClick: () => setModesExpanded((p) => !p)
1370
+ }
1371
+ ),
1372
+ /* @__PURE__ */ jsx2(
1373
+ HistoryButton,
1374
+ {
1375
+ open: historyOpen,
1376
+ onClick: () => {
1377
+ setHistoryOpen((prev) => !prev);
1378
+ },
1379
+ selectedMode,
1380
+ frameSettings,
1381
+ onFrameSettingsChange,
1382
+ panelSide,
1383
+ tooltipSide,
1384
+ bottom
1385
+ }
1386
+ )
1387
+ ]
1388
+ }
1389
+ )
1390
+ }
1391
+ ) : null;
1134
1392
  return /* @__PURE__ */ jsx2(
1135
1393
  "div",
1136
1394
  {
@@ -1143,11 +1401,11 @@ function Toolbar({
1143
1401
  display: "flex",
1144
1402
  flexDirection: "column",
1145
1403
  alignItems: "center",
1146
- background: "rgb(32, 32, 36)",
1404
+ background: bg.base,
1147
1405
  borderRadius: 999,
1148
1406
  padding: 6,
1149
- boxShadow: "0 8px 32px rgba(0, 0, 0, 0.4)",
1150
- fontFamily: "system-ui, -apple-system, sans-serif",
1407
+ boxShadow: shadow.toolbar,
1408
+ ...fontBase,
1151
1409
  userSelect: "none"
1152
1410
  },
1153
1411
  children: bottom ? /* @__PURE__ */ jsxs2(Fragment2, { children: [
@@ -1184,14 +1442,17 @@ function IconButton({
1184
1442
  style: {
1185
1443
  position: "absolute",
1186
1444
  ...tooltipStyle,
1187
- background: "rgb(32, 32, 36)",
1188
- border: "1px solid rgba(255, 255, 255, 0.1)",
1445
+ background: bg.base,
1446
+ border: `1px solid ${stroke.default}`,
1189
1447
  borderRadius: 6,
1190
- padding: "3px 8px",
1191
- color: "rgba(255, 255, 255, 0.88)",
1192
- fontSize: 11,
1448
+ padding: "0 8px",
1449
+ height: 24,
1450
+ display: "flex",
1451
+ alignItems: "center",
1452
+ color: fg.strong,
1453
+ ...text.label.xs,
1193
1454
  whiteSpace: "nowrap",
1194
- boxShadow: "0 8px 28px rgba(0, 0, 0, 0.28)",
1455
+ boxShadow: shadow.tooltip,
1195
1456
  pointerEvents: "none"
1196
1457
  },
1197
1458
  children: tooltip
@@ -1208,13 +1469,13 @@ function IconButton({
1208
1469
  height: 32,
1209
1470
  borderRadius: "50%",
1210
1471
  border: "none",
1211
- background: active || hovered ? "rgba(255, 255, 255, 0.12)" : "transparent",
1472
+ background: active || hovered ? state.hoverStrong : "transparent",
1212
1473
  display: "flex",
1213
1474
  alignItems: "center",
1214
1475
  justifyContent: "center",
1215
1476
  cursor: "pointer",
1216
1477
  padding: 0,
1217
- color: active || hovered ? "rgba(255, 255, 255, 0.96)" : "rgba(255, 255, 255, 0.52)",
1478
+ color: active || hovered ? fg.strong : fg.sub,
1218
1479
  transition: "background 0.12s ease, color 0.12s ease"
1219
1480
  },
1220
1481
  children
@@ -1222,37 +1483,11 @@ function IconButton({
1222
1483
  )
1223
1484
  ] });
1224
1485
  }
1225
- function SettingsButton({
1226
- open,
1227
- onClick,
1228
- selectedMode,
1229
- frameSettings,
1230
- onFrameSettingsChange,
1231
- panelSide,
1232
- tooltipSide,
1233
- bottom
1234
- }) {
1235
- const verticalAlign = bottom ? { bottom: 0 } : { top: 0 };
1236
- const panelStyle = panelSide === "left" ? { right: "calc(100% + 10px)", ...verticalAlign } : { left: "calc(100% + 10px)", ...verticalAlign };
1237
- return /* @__PURE__ */ jsxs2("div", { style: { position: "relative" }, children: [
1238
- /* @__PURE__ */ jsx2(IconButton, { active: open, tooltipSide, tooltip: !open ? "Settings" : void 0, onClick, children: /* @__PURE__ */ jsx2(Settings, { size: 16, strokeWidth: 1.7 }) }),
1239
- open && /* @__PURE__ */ jsx2(
1240
- SettingsPanel,
1241
- {
1242
- style: { position: "absolute", ...panelStyle },
1243
- onClose: onClick,
1244
- selectedMode,
1245
- frameSettings,
1246
- onFrameSettingsChange
1247
- }
1248
- )
1249
- ] });
1250
- }
1251
1486
  function DropItem2({
1252
1487
  children,
1253
1488
  onClick,
1254
1489
  active,
1255
- accent
1490
+ accent: accent2
1256
1491
  }) {
1257
1492
  return /* @__PURE__ */ jsx2(
1258
1493
  "button",
@@ -1262,41 +1497,79 @@ function DropItem2({
1262
1497
  display: "block",
1263
1498
  width: "100%",
1264
1499
  padding: "7px 12px",
1265
- background: active ? "rgba(255, 255, 255, 0.08)" : "transparent",
1500
+ background: active ? state.active : "transparent",
1266
1501
  border: "none",
1267
- color: accent ? "rgba(125, 211, 252, 0.96)" : "rgba(255, 255, 255, 0.86)",
1502
+ color: accent2 ? accent.highlight : fg.strong,
1268
1503
  textAlign: "left",
1269
1504
  cursor: "pointer",
1270
- fontSize: 13,
1505
+ ...text.paragraph.sm,
1271
1506
  fontFamily: "inherit"
1272
1507
  },
1273
1508
  children
1274
1509
  }
1275
1510
  );
1276
1511
  }
1277
- function Separator({ vertical = true }) {
1512
+ function Separator({
1513
+ vertical = true,
1514
+ onClick
1515
+ }) {
1278
1516
  return /* @__PURE__ */ jsx2(
1279
1517
  "div",
1280
1518
  {
1519
+ onClick,
1281
1520
  style: {
1521
+ position: "relative",
1282
1522
  width: vertical ? 1 : 24,
1283
1523
  height: vertical ? 18 : 1,
1284
- background: "rgba(255,255,255,0.12)",
1524
+ background: stroke.strong,
1285
1525
  flexShrink: 0,
1286
- margin: vertical ? "0 6px" : "6px 0"
1287
- }
1526
+ margin: vertical ? "0 6px" : "6px 0",
1527
+ cursor: onClick ? "pointer" : void 0
1528
+ },
1529
+ children: onClick && /* @__PURE__ */ jsx2("div", { style: { position: "absolute", inset: vertical ? "0 -8px" : "-8px 0" } })
1530
+ }
1531
+ );
1532
+ }
1533
+ function PanelTab({
1534
+ children,
1535
+ active,
1536
+ onClick
1537
+ }) {
1538
+ return /* @__PURE__ */ jsx2(
1539
+ "button",
1540
+ {
1541
+ onClick,
1542
+ style: {
1543
+ flex: 1,
1544
+ padding: "6px 0",
1545
+ border: "none",
1546
+ borderBottom: `2px solid ${active ? fg.strong : "transparent"}`,
1547
+ background: "transparent",
1548
+ color: active ? fg.strong : fg.muted,
1549
+ ...text.label.xs,
1550
+ fontFamily: "inherit",
1551
+ cursor: "pointer",
1552
+ transition: "color 0.12s ease, border-color 0.12s ease"
1553
+ },
1554
+ children
1288
1555
  }
1289
1556
  );
1290
1557
  }
1291
1558
  function HistoryButton({
1292
1559
  open,
1293
1560
  onClick,
1561
+ selectedMode,
1562
+ frameSettings,
1563
+ onFrameSettingsChange,
1294
1564
  panelSide,
1295
1565
  tooltipSide,
1296
1566
  bottom
1297
1567
  }) {
1568
+ const [activeTab, setActiveTab] = useState3("screenshots");
1298
1569
  const [toast, setToast] = useState3(null);
1299
1570
  const [pushing, setPushing] = useState3(false);
1571
+ const [saveDir, setSaveDir] = useState3(null);
1572
+ const [picking, setPicking] = useState3(false);
1300
1573
  const [repos, setRepos] = useState3([]);
1301
1574
  const [branches, setBranches] = useState3([]);
1302
1575
  const [screenshots, setScreenshots] = useState3([]);
@@ -1309,6 +1582,30 @@ function HistoryButton({
1309
1582
  const [editingFile, setEditingFile] = useState3(null);
1310
1583
  const [editValue, setEditValue] = useState3("");
1311
1584
  const [hoveredThumb, setHoveredThumb] = useState3(null);
1585
+ useEffect2(() => {
1586
+ if (!open) return;
1587
+ fetch("/__afterbefore/config").then((r) => r.json()).then((data) => setSaveDir(data.saveDir)).catch(() => {
1588
+ });
1589
+ }, [open]);
1590
+ const handlePickFolder = async () => {
1591
+ setPicking(true);
1592
+ try {
1593
+ const res = await fetch("/__afterbefore/pick-folder", { method: "POST" });
1594
+ const data = await res.json();
1595
+ if (data.folder) {
1596
+ await fetch("/__afterbefore/config", {
1597
+ method: "POST",
1598
+ headers: { "Content-Type": "application/json" },
1599
+ body: JSON.stringify({ saveDir: data.folder })
1600
+ });
1601
+ setSaveDir(data.folder);
1602
+ }
1603
+ } catch {
1604
+ } finally {
1605
+ setPicking(false);
1606
+ }
1607
+ };
1608
+ const shortDir = saveDir ? saveDir.replace(/^\/Users\/[^/]+/, "~") : "~/Desktop";
1312
1609
  useEffect2(() => {
1313
1610
  if (!open) {
1314
1611
  setRepoDropOpen(false);
@@ -1425,255 +1722,259 @@ function HistoryButton({
1425
1722
  maxWidth: 360,
1426
1723
  padding: "10px 12px",
1427
1724
  borderRadius: 12,
1428
- background: "rgb(32, 32, 36)",
1429
- border: "1px solid rgba(255, 255, 255, 0.1)",
1430
- boxShadow: "0 14px 36px rgba(0, 0, 0, 0.32)"
1725
+ background: bg.base,
1726
+ border: `1px solid ${stroke.default}`,
1727
+ boxShadow: shadow.panel,
1728
+ animation: "ab-panel-in 150ms cubic-bezier(0.23, 1, 0.32, 1)"
1431
1729
  },
1432
1730
  children: [
1433
- /* @__PURE__ */ jsx2(
1434
- "div",
1731
+ /* @__PURE__ */ jsxs2("div", { style: { display: "flex", gap: 2, marginBottom: 10 }, children: [
1732
+ /* @__PURE__ */ jsx2(PanelTab, { active: activeTab === "screenshots", onClick: () => setActiveTab("screenshots"), children: "Screenshots" }),
1733
+ /* @__PURE__ */ jsx2(PanelTab, { active: activeTab === "settings", onClick: () => setActiveTab("settings"), children: "Settings" })
1734
+ ] }),
1735
+ activeTab === "settings" && /* @__PURE__ */ jsx2(
1736
+ SettingsContent,
1435
1737
  {
1436
- style: {
1437
- fontSize: 11,
1438
- color: "rgba(255, 255, 255, 0.46)",
1439
- letterSpacing: "0.03em",
1440
- textTransform: "uppercase",
1441
- marginBottom: 10
1442
- },
1443
- children: "Screenshots"
1738
+ frameSettings,
1739
+ onFrameSettingsChange,
1740
+ saveDir: shortDir,
1741
+ picking,
1742
+ onPickFolder: handlePickFolder
1444
1743
  }
1445
1744
  ),
1446
- /* @__PURE__ */ jsxs2("div", { style: { display: "flex", flexDirection: "column", gap: 6, marginBottom: 10 }, children: [
1447
- /* @__PURE__ */ jsx2(
1448
- FilterDropdown,
1449
- {
1450
- label: "Project",
1451
- value: selectedRepo,
1452
- options: repos,
1453
- isOpen: repoDropOpen,
1454
- onToggle: () => {
1455
- setRepoDropOpen((p) => !p);
1456
- setBranchDropOpen(false);
1457
- },
1458
- onSelect: (repo) => {
1459
- setSelectedRepo(repo);
1460
- setSelectedBranch(null);
1461
- setRepoDropOpen(false);
1745
+ activeTab === "screenshots" && /* @__PURE__ */ jsxs2(Fragment2, { children: [
1746
+ /* @__PURE__ */ jsxs2("div", { style: { display: "flex", flexDirection: "column", gap: 6, marginBottom: 10 }, children: [
1747
+ /* @__PURE__ */ jsx2(
1748
+ FilterDropdown,
1749
+ {
1750
+ label: "Project",
1751
+ value: selectedRepo,
1752
+ options: repos,
1753
+ isOpen: repoDropOpen,
1754
+ onToggle: () => {
1755
+ setRepoDropOpen((p) => !p);
1756
+ setBranchDropOpen(false);
1757
+ },
1758
+ onSelect: (repo) => {
1759
+ setSelectedRepo(repo);
1760
+ setSelectedBranch(null);
1761
+ setRepoDropOpen(false);
1762
+ }
1462
1763
  }
1463
- }
1464
- ),
1465
- /* @__PURE__ */ jsx2(
1466
- FilterDropdown,
1467
- {
1468
- label: "Branch",
1469
- value: selectedBranch,
1470
- options: branches,
1471
- isOpen: branchDropOpen,
1472
- onToggle: () => {
1473
- setBranchDropOpen((p) => !p);
1474
- setRepoDropOpen(false);
1475
- },
1476
- onSelect: (branch) => {
1477
- setSelectedBranch(branch);
1478
- setBranchDropOpen(false);
1764
+ ),
1765
+ /* @__PURE__ */ jsx2(
1766
+ FilterDropdown,
1767
+ {
1768
+ label: "Branch",
1769
+ value: selectedBranch,
1770
+ options: branches,
1771
+ isOpen: branchDropOpen,
1772
+ onToggle: () => {
1773
+ setBranchDropOpen((p) => !p);
1774
+ setRepoDropOpen(false);
1775
+ },
1776
+ onSelect: (branch) => {
1777
+ setSelectedBranch(branch);
1778
+ setBranchDropOpen(false);
1779
+ }
1479
1780
  }
1480
- }
1481
- )
1482
- ] }),
1483
- loading ? /* @__PURE__ */ jsx2(
1484
- "div",
1485
- {
1486
- style: {
1487
- padding: "12px 0",
1488
- textAlign: "center",
1489
- fontSize: 12,
1490
- color: "rgba(255, 255, 255, 0.35)"
1491
- },
1492
- children: "Loading..."
1493
- }
1494
- ) : screenshots.length === 0 ? /* @__PURE__ */ jsx2(
1495
- "div",
1496
- {
1497
- style: {
1498
- padding: "12px 0",
1499
- textAlign: "center",
1500
- fontSize: 12,
1501
- color: "rgba(255, 255, 255, 0.35)"
1502
- },
1503
- children: "No screenshots yet"
1504
- }
1505
- ) : /* @__PURE__ */ jsxs2(Fragment2, { children: [
1506
- /* @__PURE__ */ jsx2(
1781
+ )
1782
+ ] }),
1783
+ loading ? /* @__PURE__ */ jsx2(
1507
1784
  "div",
1508
1785
  {
1509
1786
  style: {
1510
- maxHeight: 240,
1511
- overflowY: "auto",
1512
- display: "flex",
1513
- flexDirection: "column",
1514
- gap: 8
1787
+ padding: "12px 0",
1788
+ textAlign: "center",
1789
+ ...text.paragraph.xs,
1790
+ color: fg.faint
1515
1791
  },
1516
- children: screenshots.map((shot) => {
1517
- const imgUrl = `/__afterbefore/history/image?repo=${encodeURIComponent(selectedRepo || "")}&branch=${encodeURIComponent(selectedBranch || "")}&file=${encodeURIComponent(shot.filename)}`;
1518
- const isEditing = editingFile === shot.filename;
1519
- return /* @__PURE__ */ jsxs2(
1520
- "div",
1521
- {
1522
- style: {
1523
- display: "flex",
1524
- gap: 10,
1525
- alignItems: "center"
1526
- },
1527
- children: [
1528
- /* @__PURE__ */ jsxs2(
1529
- "div",
1530
- {
1531
- style: {
1532
- position: "relative",
1533
- width: 56,
1534
- height: 36,
1535
- flexShrink: 0,
1536
- borderRadius: 4,
1537
- overflow: "hidden",
1538
- cursor: "pointer"
1539
- },
1540
- onMouseEnter: () => setHoveredThumb(shot.filename),
1541
- onMouseLeave: () => setHoveredThumb(null),
1542
- onClick: () => setLightboxSrc(imgUrl),
1543
- children: [
1544
- /* @__PURE__ */ jsx2(
1545
- "img",
1546
- {
1547
- src: imgUrl,
1548
- alt: "",
1549
- style: {
1550
- width: 56,
1551
- height: 36,
1552
- objectFit: "cover",
1553
- border: "1px solid rgba(255, 255, 255, 0.1)",
1554
- borderRadius: 4,
1555
- background: "rgba(255, 255, 255, 0.05)",
1556
- display: "block"
1557
- }
1558
- }
1559
- ),
1560
- /* @__PURE__ */ jsx2(
1561
- "div",
1562
- {
1563
- style: {
1564
- position: "absolute",
1565
- inset: 0,
1566
- background: "rgba(0, 0, 0, 0.55)",
1567
- display: "flex",
1568
- alignItems: "center",
1569
- justifyContent: "center",
1570
- borderRadius: 4,
1571
- opacity: hoveredThumb === shot.filename ? 1 : 0,
1572
- transition: "opacity 0.15s ease"
1573
- },
1574
- children: /* @__PURE__ */ jsx2(Eye, { size: 16, strokeWidth: 1.8, color: "rgba(255, 255, 255, 0.9)" })
1575
- }
1576
- )
1577
- ]
1578
- }
1579
- ),
1580
- /* @__PURE__ */ jsx2("div", { style: { flex: 1, minWidth: 0 }, children: isEditing ? /* @__PURE__ */ jsx2(
1581
- "input",
1582
- {
1583
- autoFocus: true,
1584
- value: editValue,
1585
- onChange: (e) => setEditValue(e.target.value),
1586
- onKeyDown: (e) => {
1587
- if (e.key === "Enter") handleRename(shot.filename, editValue);
1588
- if (e.key === "Escape") setEditingFile(null);
1589
- },
1590
- onBlur: () => handleRename(shot.filename, editValue),
1591
- style: {
1592
- width: "100%",
1593
- fontSize: 12,
1594
- color: "rgba(255, 255, 255, 0.88)",
1595
- background: "rgba(255, 255, 255, 0.1)",
1596
- border: "1px solid rgba(255, 255, 255, 0.2)",
1597
- borderRadius: 4,
1598
- padding: "2px 6px",
1599
- outline: "none",
1600
- fontFamily: "inherit"
1601
- }
1602
- }
1603
- ) : /* @__PURE__ */ jsx2(
1604
- "div",
1605
- {
1606
- onClick: () => {
1607
- setEditingFile(shot.filename);
1608
- setEditValue(shot.filename.replace(/\.png$/, ""));
1609
- },
1610
- style: {
1611
- fontSize: 12,
1612
- color: "rgba(255, 255, 255, 0.88)",
1613
- cursor: "pointer",
1614
- overflow: "hidden",
1615
- textOverflow: "ellipsis",
1616
- whiteSpace: "nowrap"
1617
- },
1618
- title: "Click to rename",
1619
- children: formatTimestamp(shot.filename)
1620
- }
1621
- ) }),
1622
- /* @__PURE__ */ jsx2(
1623
- "button",
1624
- {
1625
- onClick: () => handleDelete(shot.filename),
1626
- title: "Delete screenshot",
1627
- style: {
1628
- flexShrink: 0,
1629
- width: 24,
1630
- height: 24,
1631
- borderRadius: 4,
1632
- border: "none",
1633
- background: "transparent",
1634
- color: "rgba(255, 255, 255, 0.35)",
1635
- cursor: "pointer",
1636
- display: "flex",
1637
- alignItems: "center",
1638
- justifyContent: "center",
1639
- padding: 0
1640
- },
1641
- onMouseEnter: (e) => {
1642
- e.currentTarget.style.color = "rgba(239, 68, 68, 0.9)";
1643
- e.currentTarget.style.background = "rgba(239, 68, 68, 0.1)";
1644
- },
1645
- onMouseLeave: (e) => {
1646
- e.currentTarget.style.color = "rgba(255, 255, 255, 0.35)";
1647
- e.currentTarget.style.background = "transparent";
1648
- },
1649
- children: /* @__PURE__ */ jsx2(Trash22, { size: 13, strokeWidth: 1.8 })
1650
- }
1651
- )
1652
- ]
1653
- },
1654
- shot.filename
1655
- );
1656
- })
1792
+ children: "Loading..."
1657
1793
  }
1658
- ),
1659
- /* @__PURE__ */ jsx2(
1794
+ ) : screenshots.length === 0 ? /* @__PURE__ */ jsx2(
1660
1795
  "div",
1661
1796
  {
1662
1797
  style: {
1663
- height: 1,
1664
- background: "rgba(255, 255, 255, 0.08)",
1665
- margin: "8px 0"
1666
- }
1798
+ padding: "12px 0",
1799
+ textAlign: "center",
1800
+ ...text.paragraph.xs,
1801
+ color: fg.faint
1802
+ },
1803
+ children: "No screenshots yet"
1667
1804
  }
1668
- ),
1669
- /* @__PURE__ */ jsxs2("div", { style: { display: "flex", flexDirection: "column", gap: 2 }, children: [
1670
- /* @__PURE__ */ jsxs2(ActionButton, { onClick: handleOpenFolder, children: [
1671
- /* @__PURE__ */ jsx2(FolderOpen, { size: 13, strokeWidth: 1.8 }),
1672
- "Open Folder"
1673
- ] }),
1674
- /* @__PURE__ */ jsxs2(ActionButton, { onClick: handlePush, disabled: pushing, children: [
1675
- /* @__PURE__ */ jsx2(ArrowUp, { size: 13, strokeWidth: 1.8 }),
1676
- pushing ? "Pushing..." : "Push to PR"
1805
+ ) : /* @__PURE__ */ jsxs2(Fragment2, { children: [
1806
+ /* @__PURE__ */ jsx2(
1807
+ "div",
1808
+ {
1809
+ style: {
1810
+ maxHeight: 240,
1811
+ overflowY: "auto",
1812
+ display: "flex",
1813
+ flexDirection: "column",
1814
+ gap: 8
1815
+ },
1816
+ children: screenshots.map((shot) => {
1817
+ const imgUrl = `/__afterbefore/history/image?repo=${encodeURIComponent(selectedRepo || "")}&branch=${encodeURIComponent(selectedBranch || "")}&file=${encodeURIComponent(shot.filename)}`;
1818
+ const isEditing = editingFile === shot.filename;
1819
+ return /* @__PURE__ */ jsxs2(
1820
+ "div",
1821
+ {
1822
+ style: {
1823
+ display: "flex",
1824
+ gap: 10,
1825
+ alignItems: "center"
1826
+ },
1827
+ children: [
1828
+ /* @__PURE__ */ jsxs2(
1829
+ "div",
1830
+ {
1831
+ style: {
1832
+ position: "relative",
1833
+ width: 56,
1834
+ height: 36,
1835
+ flexShrink: 0,
1836
+ borderRadius: 4,
1837
+ overflow: "hidden",
1838
+ cursor: "pointer"
1839
+ },
1840
+ onMouseEnter: () => setHoveredThumb(shot.filename),
1841
+ onMouseLeave: () => setHoveredThumb(null),
1842
+ onClick: () => setLightboxSrc(imgUrl),
1843
+ children: [
1844
+ /* @__PURE__ */ jsx2(
1845
+ "img",
1846
+ {
1847
+ src: imgUrl,
1848
+ alt: "",
1849
+ style: {
1850
+ width: 56,
1851
+ height: 36,
1852
+ objectFit: "cover",
1853
+ border: `1px solid ${stroke.default}`,
1854
+ borderRadius: 4,
1855
+ background: state.subtle,
1856
+ display: "block"
1857
+ }
1858
+ }
1859
+ ),
1860
+ /* @__PURE__ */ jsx2(
1861
+ "div",
1862
+ {
1863
+ style: {
1864
+ position: "absolute",
1865
+ inset: 0,
1866
+ background: "rgba(0, 0, 0, 0.55)",
1867
+ display: "flex",
1868
+ alignItems: "center",
1869
+ justifyContent: "center",
1870
+ borderRadius: 4,
1871
+ opacity: hoveredThumb === shot.filename ? 1 : 0,
1872
+ transition: "opacity 0.15s ease"
1873
+ },
1874
+ children: /* @__PURE__ */ jsx2(Eye, { size: 16, strokeWidth: 1.8, color: fg.strong })
1875
+ }
1876
+ )
1877
+ ]
1878
+ }
1879
+ ),
1880
+ /* @__PURE__ */ jsx2("div", { style: { flex: 1, minWidth: 0 }, children: isEditing ? /* @__PURE__ */ jsx2(
1881
+ "input",
1882
+ {
1883
+ autoFocus: true,
1884
+ value: editValue,
1885
+ onChange: (e) => setEditValue(e.target.value),
1886
+ onKeyDown: (e) => {
1887
+ if (e.key === "Enter") handleRename(shot.filename, editValue);
1888
+ if (e.key === "Escape") setEditingFile(null);
1889
+ },
1890
+ onBlur: () => handleRename(shot.filename, editValue),
1891
+ style: {
1892
+ width: "100%",
1893
+ ...text.label.xs,
1894
+ color: fg.strong,
1895
+ background: state.hover,
1896
+ border: `1px solid ${stroke.interactive}`,
1897
+ borderRadius: 4,
1898
+ padding: "2px 6px",
1899
+ outline: "none",
1900
+ fontFamily: "inherit"
1901
+ }
1902
+ }
1903
+ ) : /* @__PURE__ */ jsx2(
1904
+ "div",
1905
+ {
1906
+ onClick: () => {
1907
+ setEditingFile(shot.filename);
1908
+ setEditValue(shot.filename.replace(/\.png$/, ""));
1909
+ },
1910
+ style: {
1911
+ ...text.label.xs,
1912
+ color: fg.strong,
1913
+ cursor: "pointer",
1914
+ overflow: "hidden",
1915
+ textOverflow: "ellipsis",
1916
+ whiteSpace: "nowrap"
1917
+ },
1918
+ title: "Click to rename",
1919
+ children: formatTimestamp(shot.filename)
1920
+ }
1921
+ ) }),
1922
+ /* @__PURE__ */ jsx2(
1923
+ "button",
1924
+ {
1925
+ onClick: () => handleDelete(shot.filename),
1926
+ title: "Delete screenshot",
1927
+ style: {
1928
+ flexShrink: 0,
1929
+ width: 24,
1930
+ height: 24,
1931
+ borderRadius: 4,
1932
+ border: "none",
1933
+ background: "transparent",
1934
+ color: fg.faint,
1935
+ cursor: "pointer",
1936
+ display: "flex",
1937
+ alignItems: "center",
1938
+ justifyContent: "center",
1939
+ padding: 0
1940
+ },
1941
+ onMouseEnter: (e) => {
1942
+ e.currentTarget.style.color = feedback.error;
1943
+ e.currentTarget.style.background = feedback.errorBg;
1944
+ },
1945
+ onMouseLeave: (e) => {
1946
+ e.currentTarget.style.color = fg.faint;
1947
+ e.currentTarget.style.background = "transparent";
1948
+ },
1949
+ children: /* @__PURE__ */ jsx2(Trash22, { size: 13, strokeWidth: 1.8 })
1950
+ }
1951
+ )
1952
+ ]
1953
+ },
1954
+ shot.filename
1955
+ );
1956
+ })
1957
+ }
1958
+ ),
1959
+ /* @__PURE__ */ jsx2(
1960
+ "div",
1961
+ {
1962
+ style: {
1963
+ height: 1,
1964
+ background: state.active,
1965
+ margin: "8px 0"
1966
+ }
1967
+ }
1968
+ ),
1969
+ /* @__PURE__ */ jsxs2("div", { style: { display: "flex", flexDirection: "column", gap: 2 }, children: [
1970
+ /* @__PURE__ */ jsxs2(ActionButton, { onClick: handleOpenFolder, children: [
1971
+ /* @__PURE__ */ jsx2(FolderOpen, { size: 13, strokeWidth: 1.8 }),
1972
+ "Open Folder"
1973
+ ] }),
1974
+ /* @__PURE__ */ jsxs2(ActionButton, { onClick: handlePush, disabled: pushing, children: [
1975
+ /* @__PURE__ */ jsx2(ArrowUp, { size: 13, strokeWidth: 1.8 }),
1976
+ pushing ? "Pushing..." : "Push to PR"
1977
+ ] })
1677
1978
  ] })
1678
1979
  ] })
1679
1980
  ] }),
@@ -1688,12 +1989,11 @@ function HistoryButton({
1688
1989
  marginBottom: 8,
1689
1990
  padding: "6px 12px",
1690
1991
  borderRadius: 6,
1691
- fontSize: 12,
1692
- fontWeight: 500,
1992
+ ...text.label.xs,
1693
1993
  whiteSpace: "nowrap",
1694
1994
  color: "white",
1695
- background: toast.type === "success" ? "rgba(34, 197, 94, 0.9)" : "rgba(239, 68, 68, 0.9)",
1696
- boxShadow: "0 2px 8px rgba(0,0,0,0.3)"
1995
+ background: toast.type === "success" ? feedback.success : feedback.error,
1996
+ boxShadow: shadow.toast
1697
1997
  },
1698
1998
  children: toast.message
1699
1999
  }
@@ -1747,7 +2047,7 @@ function HistoryButton({
1747
2047
  height: 32,
1748
2048
  borderRadius: "50%",
1749
2049
  border: "none",
1750
- background: "rgba(255, 255, 255, 0.15)",
2050
+ background: state.pressed,
1751
2051
  color: "white",
1752
2052
  cursor: "pointer",
1753
2053
  display: "flex",
@@ -1776,9 +2076,8 @@ function FilterDropdown({
1776
2076
  "div",
1777
2077
  {
1778
2078
  style: {
1779
- fontSize: 11,
1780
- color: "rgba(255, 255, 255, 0.42)",
1781
- letterSpacing: "0.02em",
2079
+ ...text.subheading.xxs,
2080
+ color: fg.muted,
1782
2081
  marginBottom: 3
1783
2082
  },
1784
2083
  children: label
@@ -1798,11 +2097,11 @@ function FilterDropdown({
1798
2097
  height: 30,
1799
2098
  padding: "0 8px",
1800
2099
  borderRadius: 7,
1801
- border: "1px solid rgba(255,255,255,0.1)",
1802
- background: "rgba(255,255,255,0.07)",
1803
- color: "rgba(255,255,255,0.88)",
2100
+ border: `1px solid ${stroke.default}`,
2101
+ background: state.input,
2102
+ color: fg.strong,
1804
2103
  cursor: "pointer",
1805
- fontSize: 12,
2104
+ ...text.label.xs,
1806
2105
  fontFamily: "inherit"
1807
2106
  },
1808
2107
  children: [
@@ -1831,11 +2130,11 @@ function FilterDropdown({
1831
2130
  right: 0,
1832
2131
  maxHeight: 160,
1833
2132
  overflowY: "auto",
1834
- background: "rgb(32, 32, 36)",
1835
- border: "1px solid rgba(255, 255, 255, 0.1)",
2133
+ background: bg.base,
2134
+ border: `1px solid ${stroke.default}`,
1836
2135
  borderRadius: 8,
1837
2136
  padding: "4px 0",
1838
- boxShadow: "0 10px 30px rgba(0, 0, 0, 0.3)",
2137
+ boxShadow: shadow.dropdown,
1839
2138
  zIndex: 1
1840
2139
  },
1841
2140
  children: options.map((opt) => /* @__PURE__ */ jsx2(
@@ -1872,9 +2171,9 @@ function ActionButton({
1872
2171
  width: "100%",
1873
2172
  padding: "6px 8px",
1874
2173
  border: "none",
1875
- background: hovered ? "rgba(255, 255, 255, 0.08)" : "transparent",
1876
- color: "rgba(255, 255, 255, 0.78)",
1877
- fontSize: 12,
2174
+ background: hovered ? state.active : "transparent",
2175
+ color: fg.default,
2176
+ ...text.label.xs,
1878
2177
  borderRadius: 6,
1879
2178
  cursor: disabled ? "wait" : "pointer",
1880
2179
  textAlign: "left",
@@ -1907,18 +2206,18 @@ function Inspector({ onSelect, onCancel }) {
1907
2206
  const hoveredEl = useRef3(null);
1908
2207
  const styleEl = useRef3(null);
1909
2208
  useEffect3(() => {
1910
- const style = document.createElement("style");
1911
- style.setAttribute("data-afterbefore", "true");
1912
- style.textContent = [
2209
+ const style2 = document.createElement("style");
2210
+ style2.setAttribute("data-afterbefore", "true");
2211
+ style2.textContent = [
1913
2212
  "* { cursor: crosshair !important; }",
1914
2213
  "[data-afterbefore], [data-afterbefore] * { cursor: auto !important; }",
1915
2214
  "[data-afterbefore] button, [data-afterbefore] label, [data-afterbefore] a { cursor: pointer !important; }",
1916
2215
  "[data-afterbefore] input, [data-afterbefore] textarea { cursor: text !important; }"
1917
2216
  ].join("\n");
1918
- document.head.appendChild(style);
1919
- styleEl.current = style;
2217
+ document.head.appendChild(style2);
2218
+ styleEl.current = style2;
1920
2219
  return () => {
1921
- style.remove();
2220
+ style2.remove();
1922
2221
  };
1923
2222
  }, []);
1924
2223
  const isOverlayElement = useCallback3((el) => {
@@ -2015,12 +2314,15 @@ async function saveCapture(mode, dataUrl) {
2015
2314
  }
2016
2315
  }
2017
2316
  function AfterBefore() {
2018
- const { state, captureComplete, reset } = useOverlayState();
2317
+ const { state: state2, captureComplete, reset } = useOverlayState();
2019
2318
  const [toolbarActive, setToolbarActive] = useState5(false);
2020
2319
  const [inspectorActive, setInspectorActive] = useState5(false);
2021
2320
  const [loading, setLoading] = useState5(false);
2022
2321
  const [selectedMode, setSelectedMode] = useState5("component");
2023
2322
  const [frameSettings, setFrameSettings] = useState5(DEFAULT_FRAME_SETTINGS);
2323
+ useEffect4(() => {
2324
+ injectInterFont();
2325
+ }, []);
2024
2326
  useEffect4(() => {
2025
2327
  try {
2026
2328
  const stored = localStorage.getItem("ab-frame-settings");
@@ -2037,16 +2339,16 @@ function AfterBefore() {
2037
2339
  }
2038
2340
  }, []);
2039
2341
  useEffect4(() => {
2040
- if (state.phase === "ready") {
2342
+ if (state2.phase === "ready") {
2041
2343
  const timer = setTimeout(() => {
2042
2344
  reset();
2043
2345
  }, 1500);
2044
2346
  return () => clearTimeout(timer);
2045
2347
  }
2046
- }, [state.phase, reset]);
2348
+ }, [state2.phase, reset]);
2047
2349
  const handleToggle = useCallback4(() => {
2048
2350
  if (loading) return;
2049
- if (state.phase === "ready") {
2351
+ if (state2.phase === "ready") {
2050
2352
  reset();
2051
2353
  }
2052
2354
  if (toolbarActive || inspectorActive) {
@@ -2061,7 +2363,7 @@ function AfterBefore() {
2061
2363
  setInspectorActive(false);
2062
2364
  }
2063
2365
  }
2064
- }, [state.phase, loading, toolbarActive, inspectorActive, selectedMode, reset]);
2366
+ }, [state2.phase, loading, toolbarActive, inspectorActive, selectedMode, reset]);
2065
2367
  const performCapture = useCallback4(
2066
2368
  async (mode, element) => {
2067
2369
  setLoading(true);
@@ -2128,7 +2430,7 @@ function AfterBefore() {
2128
2430
  {
2129
2431
  expanded: toolbarActive,
2130
2432
  onToggle: handleToggle,
2131
- phase: state.phase,
2433
+ phase: state2.phase,
2132
2434
  loading,
2133
2435
  selectedMode,
2134
2436
  onModeChange: handleModeChange,