afterbefore 0.2.21 → 0.2.23

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.
@@ -308,13 +308,13 @@ function injectInterFont() {
308
308
 
309
309
  // src/overlay/ui/toolbar.tsx
310
310
  import { useCallback as useCallback2, useEffect as useEffect2, useRef as useRef2, useState as useState3 } from "react";
311
+ import { createPortal } from "react-dom";
311
312
  import {
312
313
  ArrowUp,
313
314
  Camera,
314
315
  Check,
315
316
  ChevronDown as ChevronDown2,
316
317
  Image as Image2,
317
- Eye,
318
318
  FolderOpen,
319
319
  LoaderCircle,
320
320
  FileText,
@@ -1379,9 +1379,7 @@ function Toolbar({
1379
1379
  selectedMode,
1380
1380
  frameSettings,
1381
1381
  onFrameSettingsChange,
1382
- panelSide,
1383
- tooltipSide,
1384
- bottom
1382
+ tooltipSide
1385
1383
  }
1386
1384
  )
1387
1385
  ]
@@ -1530,28 +1528,120 @@ function Separator({
1530
1528
  }
1531
1529
  );
1532
1530
  }
1533
- function PanelTab({
1534
- children,
1535
- active,
1536
- onClick
1531
+ function BrowserChromeBar({ theme }) {
1532
+ const colors = theme === "dark" ? { titleBar: "#1C1C1C", urlBar: "#262626", text: "#7B7B7B", border: "#333333" } : { titleBar: "#F5F5F5", urlBar: "#FFFFFF", text: "#999999", border: "#EBEBEB" };
1533
+ return /* @__PURE__ */ jsxs2("div", { style: { background: colors.titleBar, flexShrink: 0 }, children: [
1534
+ /* @__PURE__ */ jsx2("div", { style: { display: "flex", alignItems: "center", height: 18, padding: "0 8px" }, children: /* @__PURE__ */ jsx2("div", { style: { display: "flex", gap: 5 }, children: ["#FF5F57", "#FEBC2E", "#28C840"].map((c) => /* @__PURE__ */ jsx2("div", { style: { width: 6, height: 6, borderRadius: "50%", background: c } }, c)) }) }),
1535
+ /* @__PURE__ */ jsx2("div", { style: { padding: "2px 40px 3px" }, children: /* @__PURE__ */ jsx2(
1536
+ "div",
1537
+ {
1538
+ style: {
1539
+ background: colors.urlBar,
1540
+ borderRadius: 3,
1541
+ padding: "2px 0",
1542
+ textAlign: "center",
1543
+ ...theme === "light" ? { border: `1px solid ${colors.border}` } : {}
1544
+ },
1545
+ children: /* @__PURE__ */ jsx2(
1546
+ "span",
1547
+ {
1548
+ style: {
1549
+ fontSize: 7,
1550
+ color: colors.text,
1551
+ fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif'
1552
+ },
1553
+ children: "localhost"
1554
+ }
1555
+ )
1556
+ }
1557
+ ) }),
1558
+ /* @__PURE__ */ jsx2("div", { style: { height: 1, background: colors.border } })
1559
+ ] });
1560
+ }
1561
+ function FramePreview({
1562
+ previewUrl,
1563
+ frameSettings,
1564
+ loading,
1565
+ onClickLightbox
1537
1566
  }) {
1567
+ const { enabled: frameEnabled, browserChrome, browserTheme, size, bgType, bgColor, bgImage, padding } = frameSettings;
1568
+ const aspectRatio = frameEnabled ? `${size.w} / ${size.h}` : "16 / 10";
1569
+ const outerBg = frameEnabled ? bgType === "image" && bgImage ? { backgroundImage: `url(${bgImage})`, backgroundSize: "cover", backgroundPosition: "center" } : { background: bgColor } : { background: bg.elevated };
1570
+ const paddingPct = frameEnabled ? `${padding / size.w * 100}%` : void 0;
1571
+ const emptyState = /* @__PURE__ */ jsx2("span", { style: { ...text.paragraph.xs, color: fg.faint }, children: loading ? "Loading..." : "No screenshots yet" });
1572
+ const imageEl = previewUrl ? /* @__PURE__ */ jsx2(
1573
+ "img",
1574
+ {
1575
+ src: previewUrl,
1576
+ alt: "",
1577
+ style: {
1578
+ maxWidth: "100%",
1579
+ maxHeight: "100%",
1580
+ objectFit: "contain",
1581
+ display: "block"
1582
+ }
1583
+ }
1584
+ ) : null;
1538
1585
  return /* @__PURE__ */ jsx2(
1539
- "button",
1586
+ "div",
1540
1587
  {
1541
- onClick,
1588
+ onClick: onClickLightbox,
1542
1589
  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"
1590
+ width: "100%",
1591
+ aspectRatio,
1592
+ borderRadius: 10,
1593
+ overflow: "hidden",
1594
+ border: `1px solid ${stroke.soft}`,
1595
+ cursor: previewUrl ? "zoom-in" : "default",
1596
+ marginBottom: 10,
1597
+ display: "flex",
1598
+ flexDirection: "column",
1599
+ ...outerBg
1553
1600
  },
1554
- children
1601
+ children: frameEnabled ? /* @__PURE__ */ jsxs2(
1602
+ "div",
1603
+ {
1604
+ style: {
1605
+ flex: 1,
1606
+ display: "flex",
1607
+ flexDirection: "column",
1608
+ padding: paddingPct,
1609
+ minHeight: 0
1610
+ },
1611
+ children: [
1612
+ browserChrome && /* @__PURE__ */ jsx2(BrowserChromeBar, { theme: browserTheme }),
1613
+ /* @__PURE__ */ jsx2(
1614
+ "div",
1615
+ {
1616
+ style: {
1617
+ flex: 1,
1618
+ display: "flex",
1619
+ alignItems: "center",
1620
+ justifyContent: "center",
1621
+ minHeight: 0,
1622
+ overflow: "hidden"
1623
+ },
1624
+ children: imageEl || emptyState
1625
+ }
1626
+ )
1627
+ ]
1628
+ }
1629
+ ) : /* @__PURE__ */ jsxs2(Fragment2, { children: [
1630
+ browserChrome && /* @__PURE__ */ jsx2(BrowserChromeBar, { theme: browserTheme }),
1631
+ /* @__PURE__ */ jsx2(
1632
+ "div",
1633
+ {
1634
+ style: {
1635
+ flex: 1,
1636
+ display: "flex",
1637
+ alignItems: "center",
1638
+ justifyContent: "center",
1639
+ minHeight: 0
1640
+ },
1641
+ children: imageEl || emptyState
1642
+ }
1643
+ )
1644
+ ] })
1555
1645
  }
1556
1646
  );
1557
1647
  }
@@ -1561,11 +1651,8 @@ function HistoryButton({
1561
1651
  selectedMode,
1562
1652
  frameSettings,
1563
1653
  onFrameSettingsChange,
1564
- panelSide,
1565
- tooltipSide,
1566
- bottom
1654
+ tooltipSide
1567
1655
  }) {
1568
- const [activeTab, setActiveTab] = useState3("screenshots");
1569
1656
  const [toast, setToast] = useState3(null);
1570
1657
  const [pushing, setPushing] = useState3(false);
1571
1658
  const [saveDir, setSaveDir] = useState3(null);
@@ -1581,7 +1668,7 @@ function HistoryButton({
1581
1668
  const [lightboxSrc, setLightboxSrc] = useState3(null);
1582
1669
  const [editingFile, setEditingFile] = useState3(null);
1583
1670
  const [editValue, setEditValue] = useState3("");
1584
- const [hoveredThumb, setHoveredThumb] = useState3(null);
1671
+ const [selectedFile, setSelectedFile] = useState3(null);
1585
1672
  useEffect2(() => {
1586
1673
  if (!open) return;
1587
1674
  fetch("/__afterbefore/config").then((r) => r.json()).then((data) => setSaveDir(data.saveDir)).catch(() => {
@@ -1619,7 +1706,15 @@ function HistoryButton({
1619
1706
  fetch(`/__afterbefore/history?${params}`).then((r) => r.json()).then((data) => {
1620
1707
  setRepos(data.repos || []);
1621
1708
  setBranches(data.branches || []);
1622
- setScreenshots(data.screenshots || []);
1709
+ const shots = data.screenshots || [];
1710
+ setScreenshots(shots);
1711
+ if (shots.length > 0) {
1712
+ setSelectedFile(
1713
+ (prev) => prev && shots.some((s) => s.filename === prev) ? prev : shots[0].filename
1714
+ );
1715
+ } else {
1716
+ setSelectedFile(null);
1717
+ }
1623
1718
  if (!selectedRepo && data.currentRepo) setSelectedRepo(data.currentRepo);
1624
1719
  if (!selectedBranch && data.currentBranch) setSelectedBranch(data.currentBranch);
1625
1720
  }).catch(() => {
@@ -1666,6 +1761,7 @@ function HistoryButton({
1666
1761
  (s) => s.filename === oldName ? { ...s, filename: data.filename, timestamp: data.filename.replace(/\.png$/, "") } : s
1667
1762
  )
1668
1763
  );
1764
+ if (selectedFile === oldName) setSelectedFile(data.filename);
1669
1765
  showToast("Renamed", "success");
1670
1766
  } catch {
1671
1767
  showToast("Rename failed", "error");
@@ -1685,6 +1781,10 @@ function HistoryButton({
1685
1781
  });
1686
1782
  if (!res.ok) throw new Error();
1687
1783
  setScreenshots((prev) => prev.filter((s) => s.filename !== filename));
1784
+ if (selectedFile === filename) {
1785
+ const remaining = screenshots.filter((s) => s.filename !== filename);
1786
+ setSelectedFile(remaining.length > 0 ? remaining[0].filename : null);
1787
+ }
1688
1788
  showToast("Deleted", "success");
1689
1789
  } catch {
1690
1790
  showToast("Delete failed", "error");
@@ -1708,358 +1808,369 @@ function HistoryButton({
1708
1808
  setPushing(false);
1709
1809
  }
1710
1810
  };
1711
- const verticalAlign = bottom ? { bottom: 0 } : { top: 0 };
1712
- const panelStyle = panelSide === "left" ? { right: "calc(100% + 10px)", ...verticalAlign } : { left: "calc(100% + 10px)", ...verticalAlign };
1713
- return /* @__PURE__ */ jsxs2("div", { style: { position: "relative" }, children: [
1811
+ const previewUrl = selectedFile ? `/__afterbefore/history/image?repo=${encodeURIComponent(selectedRepo || "")}&branch=${encodeURIComponent(selectedBranch || "")}&file=${encodeURIComponent(selectedFile)}` : null;
1812
+ return /* @__PURE__ */ jsxs2(Fragment2, { children: [
1714
1813
  /* @__PURE__ */ jsx2(IconButton, { active: open, tooltipSide, tooltip: !open ? "Screenshots" : void 0, onClick, children: /* @__PURE__ */ jsx2(Image2, { size: 16, strokeWidth: 1.7 }) }),
1715
- open && /* @__PURE__ */ jsxs2(
1716
- "div",
1717
- {
1718
- style: {
1719
- position: "absolute",
1720
- ...panelStyle,
1721
- minWidth: 300,
1722
- maxWidth: 360,
1723
- padding: "10px 12px",
1724
- borderRadius: 12,
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)"
1729
- },
1730
- children: [
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,
1737
- {
1738
- frameSettings,
1739
- onFrameSettingsChange,
1740
- saveDir: shortDir,
1741
- picking,
1742
- onPickFolder: handlePickFolder
1743
- }
1744
- ),
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
- }
1763
- }
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
- }
1780
- }
1781
- )
1782
- ] }),
1783
- loading ? /* @__PURE__ */ jsx2(
1784
- "div",
1785
- {
1786
- style: {
1787
- padding: "12px 0",
1788
- textAlign: "center",
1789
- ...text.paragraph.xs,
1790
- color: fg.faint
1791
- },
1792
- children: "Loading..."
1793
- }
1794
- ) : screenshots.length === 0 ? /* @__PURE__ */ jsx2(
1814
+ open && createPortal(
1815
+ /* @__PURE__ */ jsxs2(
1816
+ "div",
1817
+ {
1818
+ "data-afterbefore": "true",
1819
+ onClick,
1820
+ style: {
1821
+ position: "fixed",
1822
+ inset: 0,
1823
+ zIndex: 2147483647,
1824
+ background: "rgba(0, 0, 0, 0.45)",
1825
+ display: "flex",
1826
+ alignItems: "center",
1827
+ justifyContent: "center",
1828
+ ...fontBase
1829
+ },
1830
+ children: [
1831
+ /* @__PURE__ */ jsxs2(
1795
1832
  "div",
1796
1833
  {
1834
+ onClick: (e) => e.stopPropagation(),
1797
1835
  style: {
1798
- padding: "12px 0",
1799
- textAlign: "center",
1800
- ...text.paragraph.xs,
1801
- color: fg.faint
1836
+ width: 580,
1837
+ maxHeight: "80vh",
1838
+ borderRadius: 14,
1839
+ background: bg.base,
1840
+ border: `1px solid ${stroke.default}`,
1841
+ boxShadow: shadow.panel,
1842
+ display: "flex",
1843
+ flexDirection: "column",
1844
+ overflow: "hidden",
1845
+ animation: "ab-panel-in 150ms cubic-bezier(0.23, 1, 0.32, 1)"
1802
1846
  },
1803
- children: "No screenshots yet"
1804
- }
1805
- ) : /* @__PURE__ */ jsxs2(Fragment2, { children: [
1806
- /* @__PURE__ */ jsx2(
1807
- "div",
1808
- {
1809
- style: {
1810
- maxHeight: 240,
1811
- overflowY: "auto",
1847
+ children: [
1848
+ /* @__PURE__ */ jsxs2("div", { style: {
1812
1849
  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(
1850
+ alignItems: "center",
1851
+ justifyContent: "space-between",
1852
+ padding: "16px 20px 0"
1853
+ }, children: [
1854
+ /* @__PURE__ */ jsxs2("div", { children: [
1855
+ /* @__PURE__ */ jsx2("div", { style: { ...text.label.sm, color: fg.strong }, children: "Screenshots" }),
1856
+ /* @__PURE__ */ jsx2("div", { style: { ...text.paragraph.xs, color: fg.muted, marginTop: 2 }, children: "Capture history & settings" })
1857
+ ] }),
1858
+ /* @__PURE__ */ jsx2(
1859
+ "button",
1860
+ {
1861
+ onClick,
1862
+ style: {
1863
+ width: 28,
1864
+ height: 28,
1865
+ borderRadius: 7,
1866
+ border: "none",
1867
+ background: "transparent",
1868
+ display: "flex",
1869
+ alignItems: "center",
1870
+ justifyContent: "center",
1871
+ cursor: "pointer",
1872
+ color: fg.muted,
1873
+ padding: 0,
1874
+ transition: "background 0.12s ease, color 0.12s ease"
1875
+ },
1876
+ onMouseEnter: (e) => {
1877
+ e.currentTarget.style.background = state.hover;
1878
+ e.currentTarget.style.color = fg.default;
1879
+ },
1880
+ onMouseLeave: (e) => {
1881
+ e.currentTarget.style.background = "transparent";
1882
+ e.currentTarget.style.color = fg.muted;
1883
+ },
1884
+ children: /* @__PURE__ */ jsx2(X2, { size: 14, strokeWidth: 2 })
1885
+ }
1886
+ )
1887
+ ] }),
1888
+ /* @__PURE__ */ jsxs2("div", { style: { flex: 1, overflowY: "auto", padding: "16px 20px 20px" }, children: [
1889
+ /* @__PURE__ */ jsx2(
1890
+ FramePreview,
1891
+ {
1892
+ previewUrl,
1893
+ frameSettings,
1894
+ loading,
1895
+ onClickLightbox: () => {
1896
+ if (previewUrl) setLightboxSrc(previewUrl);
1897
+ }
1898
+ }
1899
+ ),
1900
+ selectedFile && !loading && /* @__PURE__ */ jsxs2("div", { style: {
1901
+ display: "flex",
1902
+ alignItems: "center",
1903
+ gap: 8,
1904
+ marginBottom: 12
1905
+ }, children: [
1906
+ /* @__PURE__ */ jsx2("div", { style: { flex: 1, minWidth: 0 }, children: editingFile === selectedFile ? /* @__PURE__ */ jsx2(
1907
+ "input",
1908
+ {
1909
+ autoFocus: true,
1910
+ value: editValue,
1911
+ onChange: (e) => setEditValue(e.target.value),
1912
+ onKeyDown: (e) => {
1913
+ if (e.key === "Enter") handleRename(selectedFile, editValue);
1914
+ if (e.key === "Escape") setEditingFile(null);
1915
+ },
1916
+ onBlur: () => handleRename(selectedFile, editValue),
1917
+ style: {
1918
+ width: "100%",
1919
+ ...text.label.xs,
1920
+ color: fg.strong,
1921
+ background: state.hover,
1922
+ border: `1px solid ${stroke.interactive}`,
1923
+ borderRadius: 4,
1924
+ padding: "2px 6px",
1925
+ outline: "none",
1926
+ fontFamily: "inherit"
1927
+ }
1928
+ }
1929
+ ) : /* @__PURE__ */ jsx2(
1930
+ "div",
1931
+ {
1932
+ onClick: () => {
1933
+ setEditingFile(selectedFile);
1934
+ setEditValue(selectedFile.replace(/\.png$/, ""));
1935
+ },
1936
+ style: {
1937
+ ...text.label.xs,
1938
+ color: fg.strong,
1939
+ cursor: "pointer",
1940
+ overflow: "hidden",
1941
+ textOverflow: "ellipsis",
1942
+ whiteSpace: "nowrap"
1943
+ },
1944
+ title: "Click to rename",
1945
+ children: formatTimestamp(selectedFile)
1946
+ }
1947
+ ) }),
1948
+ /* @__PURE__ */ jsx2(
1949
+ "button",
1950
+ {
1951
+ onClick: () => handleDelete(selectedFile),
1952
+ title: "Delete screenshot",
1953
+ style: {
1954
+ flexShrink: 0,
1955
+ width: 28,
1956
+ height: 28,
1957
+ borderRadius: 6,
1958
+ border: "none",
1959
+ background: "transparent",
1960
+ color: fg.faint,
1961
+ cursor: "pointer",
1962
+ display: "flex",
1963
+ alignItems: "center",
1964
+ justifyContent: "center",
1965
+ padding: 0
1966
+ },
1967
+ onMouseEnter: (e) => {
1968
+ e.currentTarget.style.color = feedback.error;
1969
+ e.currentTarget.style.background = feedback.errorBg;
1970
+ },
1971
+ onMouseLeave: (e) => {
1972
+ e.currentTarget.style.color = fg.faint;
1973
+ e.currentTarget.style.background = "transparent";
1974
+ },
1975
+ children: /* @__PURE__ */ jsx2(Trash22, { size: 14, strokeWidth: 1.8 })
1976
+ }
1977
+ )
1978
+ ] }),
1979
+ /* @__PURE__ */ jsx2("div", { style: { marginBottom: 12 }, children: /* @__PURE__ */ jsx2(
1980
+ SettingsContent,
1981
+ {
1982
+ frameSettings,
1983
+ onFrameSettingsChange,
1984
+ saveDir: shortDir,
1985
+ picking,
1986
+ onPickFolder: handlePickFolder
1987
+ }
1988
+ ) }),
1989
+ /* @__PURE__ */ jsxs2("div", { style: { display: "flex", gap: 10, marginBottom: 10 }, children: [
1990
+ /* @__PURE__ */ jsx2("div", { style: { flex: 1, position: "relative" }, children: /* @__PURE__ */ jsx2(
1991
+ FilterDropdown,
1992
+ {
1993
+ label: "Project",
1994
+ value: selectedRepo,
1995
+ options: repos,
1996
+ isOpen: repoDropOpen,
1997
+ onToggle: () => {
1998
+ setRepoDropOpen((p) => !p);
1999
+ setBranchDropOpen(false);
2000
+ },
2001
+ onSelect: (repo) => {
2002
+ setSelectedRepo(repo);
2003
+ setSelectedBranch(null);
2004
+ setRepoDropOpen(false);
2005
+ }
2006
+ }
2007
+ ) }),
2008
+ /* @__PURE__ */ jsx2("div", { style: { flex: 1, position: "relative" }, children: /* @__PURE__ */ jsx2(
2009
+ FilterDropdown,
2010
+ {
2011
+ label: "Branch",
2012
+ value: selectedBranch,
2013
+ options: branches,
2014
+ isOpen: branchDropOpen,
2015
+ onToggle: () => {
2016
+ setBranchDropOpen((p) => !p);
2017
+ setRepoDropOpen(false);
2018
+ },
2019
+ onSelect: (branch) => {
2020
+ setSelectedBranch(branch);
2021
+ setBranchDropOpen(false);
2022
+ }
2023
+ }
2024
+ ) })
2025
+ ] }),
2026
+ screenshots.length > 0 && /* @__PURE__ */ jsx2(
1820
2027
  "div",
1821
2028
  {
1822
2029
  style: {
1823
2030
  display: "flex",
1824
- gap: 10,
1825
- alignItems: "center"
2031
+ gap: 6,
2032
+ overflowX: "auto",
2033
+ paddingBottom: 4,
2034
+ marginBottom: 10
1826
2035
  },
1827
- children: [
1828
- /* @__PURE__ */ jsxs2(
2036
+ children: screenshots.map((shot) => {
2037
+ const thumbUrl = `/__afterbefore/history/image?repo=${encodeURIComponent(selectedRepo || "")}&branch=${encodeURIComponent(selectedBranch || "")}&file=${encodeURIComponent(shot.filename)}`;
2038
+ const isSelected = selectedFile === shot.filename;
2039
+ return /* @__PURE__ */ jsx2(
1829
2040
  "div",
1830
2041
  {
2042
+ onClick: () => setSelectedFile(shot.filename),
1831
2043
  style: {
1832
- position: "relative",
1833
- width: 56,
1834
- height: 36,
2044
+ width: 64,
2045
+ height: 44,
1835
2046
  flexShrink: 0,
1836
- borderRadius: 4,
2047
+ borderRadius: 6,
1837
2048
  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
2049
  cursor: "pointer",
1914
- overflow: "hidden",
1915
- textOverflow: "ellipsis",
1916
- whiteSpace: "nowrap"
2050
+ border: isSelected ? `2px solid ${accent.highlight}` : `1px solid ${stroke.soft}`,
2051
+ opacity: isSelected ? 1 : 0.7,
2052
+ transition: "opacity 0.12s ease, border-color 0.12s ease"
1917
2053
  },
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
- ] })
1978
- ] })
1979
- ] })
1980
- ] }),
1981
- toast && /* @__PURE__ */ jsx2(
1982
- "div",
1983
- {
1984
- style: {
1985
- position: "absolute",
1986
- bottom: "100%",
1987
- left: "50%",
1988
- transform: "translateX(-50%)",
1989
- marginBottom: 8,
1990
- padding: "6px 12px",
1991
- borderRadius: 6,
1992
- ...text.label.xs,
1993
- whiteSpace: "nowrap",
1994
- color: "white",
1995
- background: toast.type === "success" ? feedback.success : feedback.error,
1996
- boxShadow: shadow.toast
1997
- },
1998
- children: toast.message
1999
- }
2000
- )
2001
- ]
2002
- }
2054
+ children: /* @__PURE__ */ jsx2(
2055
+ "img",
2056
+ {
2057
+ src: thumbUrl,
2058
+ alt: "",
2059
+ style: {
2060
+ width: "100%",
2061
+ height: "100%",
2062
+ objectFit: "cover",
2063
+ display: "block"
2064
+ }
2065
+ }
2066
+ )
2067
+ },
2068
+ shot.filename
2069
+ );
2070
+ })
2071
+ }
2072
+ ),
2073
+ /* @__PURE__ */ jsxs2("div", { style: { display: "flex", gap: 6 }, children: [
2074
+ /* @__PURE__ */ jsxs2(ActionButton, { onClick: handleOpenFolder, children: [
2075
+ /* @__PURE__ */ jsx2(FolderOpen, { size: 13, strokeWidth: 1.8 }),
2076
+ "Open Folder"
2077
+ ] }),
2078
+ /* @__PURE__ */ jsxs2(ActionButton, { onClick: handlePush, disabled: pushing, children: [
2079
+ /* @__PURE__ */ jsx2(ArrowUp, { size: 13, strokeWidth: 1.8 }),
2080
+ pushing ? "Pushing..." : "Push to PR"
2081
+ ] })
2082
+ ] })
2083
+ ] })
2084
+ ]
2085
+ }
2086
+ ),
2087
+ toast && /* @__PURE__ */ jsx2(
2088
+ "div",
2089
+ {
2090
+ onClick: (e) => e.stopPropagation(),
2091
+ style: {
2092
+ position: "fixed",
2093
+ bottom: 32,
2094
+ left: "50%",
2095
+ transform: "translateX(-50%)",
2096
+ padding: "6px 14px",
2097
+ borderRadius: 8,
2098
+ ...text.label.xs,
2099
+ whiteSpace: "nowrap",
2100
+ color: "white",
2101
+ background: toast.type === "success" ? feedback.success : feedback.error,
2102
+ boxShadow: shadow.toast
2103
+ },
2104
+ children: toast.message
2105
+ }
2106
+ )
2107
+ ]
2108
+ }
2109
+ ),
2110
+ document.body
2003
2111
  ),
2004
- lightboxSrc && /* @__PURE__ */ jsxs2(
2005
- "div",
2006
- {
2007
- onClick: () => setLightboxSrc(null),
2008
- onKeyDown: (e) => {
2009
- if (e.key === "Escape") setLightboxSrc(null);
2010
- },
2011
- style: {
2012
- position: "fixed",
2013
- inset: 0,
2014
- bottom: 100,
2015
- zIndex: 2147483646,
2016
- background: "rgba(0, 0, 0, 0.85)",
2017
- display: "flex",
2018
- alignItems: "center",
2019
- justifyContent: "center",
2020
- cursor: "zoom-out"
2021
- },
2022
- children: [
2023
- /* @__PURE__ */ jsx2(
2024
- "img",
2025
- {
2026
- src: lightboxSrc,
2027
- alt: "",
2028
- onClick: (e) => e.stopPropagation(),
2029
- style: {
2030
- maxWidth: "90vw",
2031
- maxHeight: "calc(100% - 32px)",
2032
- borderRadius: 8,
2033
- boxShadow: "0 20px 60px rgba(0, 0, 0, 0.5)",
2034
- cursor: "default"
2112
+ lightboxSrc && createPortal(
2113
+ /* @__PURE__ */ jsxs2(
2114
+ "div",
2115
+ {
2116
+ "data-afterbefore": "true",
2117
+ onClick: () => setLightboxSrc(null),
2118
+ onKeyDown: (e) => {
2119
+ if (e.key === "Escape") setLightboxSrc(null);
2120
+ },
2121
+ style: {
2122
+ position: "fixed",
2123
+ inset: 0,
2124
+ zIndex: 2147483647,
2125
+ background: "rgba(0, 0, 0, 0.85)",
2126
+ display: "flex",
2127
+ alignItems: "center",
2128
+ justifyContent: "center",
2129
+ cursor: "zoom-out"
2130
+ },
2131
+ children: [
2132
+ /* @__PURE__ */ jsx2(
2133
+ "img",
2134
+ {
2135
+ src: lightboxSrc,
2136
+ alt: "",
2137
+ onClick: (e) => e.stopPropagation(),
2138
+ style: {
2139
+ maxWidth: "90vw",
2140
+ maxHeight: "calc(100vh - 64px)",
2141
+ borderRadius: 8,
2142
+ boxShadow: "0 20px 60px rgba(0, 0, 0, 0.5)",
2143
+ cursor: "default"
2144
+ }
2035
2145
  }
2036
- }
2037
- ),
2038
- /* @__PURE__ */ jsx2(
2039
- "button",
2040
- {
2041
- onClick: () => setLightboxSrc(null),
2042
- style: {
2043
- position: "absolute",
2044
- top: 16,
2045
- right: 16,
2046
- width: 32,
2047
- height: 32,
2048
- borderRadius: "50%",
2049
- border: "none",
2050
- background: state.pressed,
2051
- color: "white",
2052
- cursor: "pointer",
2053
- display: "flex",
2054
- alignItems: "center",
2055
- justifyContent: "center",
2056
- padding: 0
2057
- },
2058
- children: /* @__PURE__ */ jsx2(X2, { size: 18, strokeWidth: 2 })
2059
- }
2060
- )
2061
- ]
2062
- }
2146
+ ),
2147
+ /* @__PURE__ */ jsx2(
2148
+ "button",
2149
+ {
2150
+ onClick: () => setLightboxSrc(null),
2151
+ style: {
2152
+ position: "absolute",
2153
+ top: 16,
2154
+ right: 16,
2155
+ width: 32,
2156
+ height: 32,
2157
+ borderRadius: "50%",
2158
+ border: "none",
2159
+ background: state.pressed,
2160
+ color: "white",
2161
+ cursor: "pointer",
2162
+ display: "flex",
2163
+ alignItems: "center",
2164
+ justifyContent: "center",
2165
+ padding: 0
2166
+ },
2167
+ children: /* @__PURE__ */ jsx2(X2, { size: 18, strokeWidth: 2 })
2168
+ }
2169
+ )
2170
+ ]
2171
+ }
2172
+ ),
2173
+ document.body
2063
2174
  )
2064
2175
  ] });
2065
2176
  }
@@ -2125,7 +2236,7 @@ function FilterDropdown({
2125
2236
  {
2126
2237
  style: {
2127
2238
  position: "absolute",
2128
- bottom: "calc(100% + 4px)",
2239
+ top: "calc(100% + 4px)",
2129
2240
  left: 0,
2130
2241
  right: 0,
2131
2242
  maxHeight: 160,