afterbefore 0.2.22 → 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.
@@ -315,7 +315,6 @@ import {
315
315
  Check,
316
316
  ChevronDown as ChevronDown2,
317
317
  Image as Image2,
318
- Eye,
319
318
  FolderOpen,
320
319
  LoaderCircle,
321
320
  FileText,
@@ -1380,9 +1379,7 @@ function Toolbar({
1380
1379
  selectedMode,
1381
1380
  frameSettings,
1382
1381
  onFrameSettingsChange,
1383
- panelSide,
1384
- tooltipSide,
1385
- bottom
1382
+ tooltipSide
1386
1383
  }
1387
1384
  )
1388
1385
  ]
@@ -1531,28 +1528,120 @@ function Separator({
1531
1528
  }
1532
1529
  );
1533
1530
  }
1534
- function PanelTab({
1535
- children,
1536
- active,
1537
- 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
1538
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;
1539
1585
  return /* @__PURE__ */ jsx2(
1540
- "button",
1586
+ "div",
1541
1587
  {
1542
- onClick,
1588
+ onClick: onClickLightbox,
1543
1589
  style: {
1544
- flex: 1,
1545
- padding: "6px 0",
1546
- border: "none",
1547
- borderBottom: `2px solid ${active ? fg.strong : "transparent"}`,
1548
- background: "transparent",
1549
- color: active ? fg.strong : fg.muted,
1550
- ...text.label.xs,
1551
- fontFamily: "inherit",
1552
- cursor: "pointer",
1553
- 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
1554
1600
  },
1555
- 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
+ ] })
1556
1645
  }
1557
1646
  );
1558
1647
  }
@@ -1562,11 +1651,8 @@ function HistoryButton({
1562
1651
  selectedMode,
1563
1652
  frameSettings,
1564
1653
  onFrameSettingsChange,
1565
- panelSide,
1566
- tooltipSide,
1567
- bottom
1654
+ tooltipSide
1568
1655
  }) {
1569
- const [activeTab, setActiveTab] = useState3("screenshots");
1570
1656
  const [toast, setToast] = useState3(null);
1571
1657
  const [pushing, setPushing] = useState3(false);
1572
1658
  const [saveDir, setSaveDir] = useState3(null);
@@ -1582,7 +1668,7 @@ function HistoryButton({
1582
1668
  const [lightboxSrc, setLightboxSrc] = useState3(null);
1583
1669
  const [editingFile, setEditingFile] = useState3(null);
1584
1670
  const [editValue, setEditValue] = useState3("");
1585
- const [hoveredThumb, setHoveredThumb] = useState3(null);
1671
+ const [selectedFile, setSelectedFile] = useState3(null);
1586
1672
  useEffect2(() => {
1587
1673
  if (!open) return;
1588
1674
  fetch("/__afterbefore/config").then((r) => r.json()).then((data) => setSaveDir(data.saveDir)).catch(() => {
@@ -1620,7 +1706,15 @@ function HistoryButton({
1620
1706
  fetch(`/__afterbefore/history?${params}`).then((r) => r.json()).then((data) => {
1621
1707
  setRepos(data.repos || []);
1622
1708
  setBranches(data.branches || []);
1623
- 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
+ }
1624
1718
  if (!selectedRepo && data.currentRepo) setSelectedRepo(data.currentRepo);
1625
1719
  if (!selectedBranch && data.currentBranch) setSelectedBranch(data.currentBranch);
1626
1720
  }).catch(() => {
@@ -1667,6 +1761,7 @@ function HistoryButton({
1667
1761
  (s) => s.filename === oldName ? { ...s, filename: data.filename, timestamp: data.filename.replace(/\.png$/, "") } : s
1668
1762
  )
1669
1763
  );
1764
+ if (selectedFile === oldName) setSelectedFile(data.filename);
1670
1765
  showToast("Renamed", "success");
1671
1766
  } catch {
1672
1767
  showToast("Rename failed", "error");
@@ -1686,6 +1781,10 @@ function HistoryButton({
1686
1781
  });
1687
1782
  if (!res.ok) throw new Error();
1688
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
+ }
1689
1788
  showToast("Deleted", "success");
1690
1789
  } catch {
1691
1790
  showToast("Delete failed", "error");
@@ -1709,298 +1808,306 @@ function HistoryButton({
1709
1808
  setPushing(false);
1710
1809
  }
1711
1810
  };
1712
- const verticalAlign = bottom ? { bottom: 0 } : { top: 0 };
1713
- const panelStyle = panelSide === "left" ? { right: "calc(100% + 10px)", ...verticalAlign } : { left: "calc(100% + 10px)", ...verticalAlign };
1714
- 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: [
1715
1813
  /* @__PURE__ */ jsx2(IconButton, { active: open, tooltipSide, tooltip: !open ? "Screenshots" : void 0, onClick, children: /* @__PURE__ */ jsx2(Image2, { size: 16, strokeWidth: 1.7 }) }),
1716
- open && /* @__PURE__ */ jsxs2(
1717
- "div",
1718
- {
1719
- style: {
1720
- position: "absolute",
1721
- ...panelStyle,
1722
- minWidth: 300,
1723
- maxWidth: 360,
1724
- padding: "10px 12px",
1725
- borderRadius: 12,
1726
- background: bg.base,
1727
- border: `1px solid ${stroke.default}`,
1728
- boxShadow: shadow.panel,
1729
- animation: "ab-panel-in 150ms cubic-bezier(0.23, 1, 0.32, 1)"
1730
- },
1731
- children: [
1732
- /* @__PURE__ */ jsxs2("div", { style: { display: "flex", gap: 2, marginBottom: 10 }, children: [
1733
- /* @__PURE__ */ jsx2(PanelTab, { active: activeTab === "screenshots", onClick: () => setActiveTab("screenshots"), children: "Screenshots" }),
1734
- /* @__PURE__ */ jsx2(PanelTab, { active: activeTab === "settings", onClick: () => setActiveTab("settings"), children: "Settings" })
1735
- ] }),
1736
- activeTab === "settings" && /* @__PURE__ */ jsx2(
1737
- SettingsContent,
1738
- {
1739
- frameSettings,
1740
- onFrameSettingsChange,
1741
- saveDir: shortDir,
1742
- picking,
1743
- onPickFolder: handlePickFolder
1744
- }
1745
- ),
1746
- activeTab === "screenshots" && /* @__PURE__ */ jsxs2(Fragment2, { children: [
1747
- /* @__PURE__ */ jsxs2("div", { style: { display: "flex", flexDirection: "column", gap: 6, marginBottom: 10 }, children: [
1748
- /* @__PURE__ */ jsx2(
1749
- FilterDropdown,
1750
- {
1751
- label: "Project",
1752
- value: selectedRepo,
1753
- options: repos,
1754
- isOpen: repoDropOpen,
1755
- onToggle: () => {
1756
- setRepoDropOpen((p) => !p);
1757
- setBranchDropOpen(false);
1758
- },
1759
- onSelect: (repo) => {
1760
- setSelectedRepo(repo);
1761
- setSelectedBranch(null);
1762
- setRepoDropOpen(false);
1763
- }
1764
- }
1765
- ),
1766
- /* @__PURE__ */ jsx2(
1767
- FilterDropdown,
1768
- {
1769
- label: "Branch",
1770
- value: selectedBranch,
1771
- options: branches,
1772
- isOpen: branchDropOpen,
1773
- onToggle: () => {
1774
- setBranchDropOpen((p) => !p);
1775
- setRepoDropOpen(false);
1776
- },
1777
- onSelect: (branch) => {
1778
- setSelectedBranch(branch);
1779
- setBranchDropOpen(false);
1780
- }
1781
- }
1782
- )
1783
- ] }),
1784
- loading ? /* @__PURE__ */ jsx2(
1785
- "div",
1786
- {
1787
- style: {
1788
- padding: "12px 0",
1789
- textAlign: "center",
1790
- ...text.paragraph.xs,
1791
- color: fg.faint
1792
- },
1793
- children: "Loading..."
1794
- }
1795
- ) : 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(
1796
1832
  "div",
1797
1833
  {
1834
+ onClick: (e) => e.stopPropagation(),
1798
1835
  style: {
1799
- padding: "12px 0",
1800
- textAlign: "center",
1801
- ...text.paragraph.xs,
1802
- 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)"
1803
1846
  },
1804
- children: "No screenshots yet"
1805
- }
1806
- ) : /* @__PURE__ */ jsxs2(Fragment2, { children: [
1807
- /* @__PURE__ */ jsx2(
1808
- "div",
1809
- {
1810
- style: {
1811
- maxHeight: 240,
1812
- overflowY: "auto",
1847
+ children: [
1848
+ /* @__PURE__ */ jsxs2("div", { style: {
1813
1849
  display: "flex",
1814
- flexDirection: "column",
1815
- gap: 8
1816
- },
1817
- children: screenshots.map((shot) => {
1818
- const imgUrl = `/__afterbefore/history/image?repo=${encodeURIComponent(selectedRepo || "")}&branch=${encodeURIComponent(selectedBranch || "")}&file=${encodeURIComponent(shot.filename)}`;
1819
- const isEditing = editingFile === shot.filename;
1820
- 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(
1821
2027
  "div",
1822
2028
  {
1823
2029
  style: {
1824
2030
  display: "flex",
1825
- gap: 10,
1826
- alignItems: "center"
2031
+ gap: 6,
2032
+ overflowX: "auto",
2033
+ paddingBottom: 4,
2034
+ marginBottom: 10
1827
2035
  },
1828
- children: [
1829
- /* @__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(
1830
2040
  "div",
1831
2041
  {
2042
+ onClick: () => setSelectedFile(shot.filename),
1832
2043
  style: {
1833
- position: "relative",
1834
- width: 56,
1835
- height: 36,
2044
+ width: 64,
2045
+ height: 44,
1836
2046
  flexShrink: 0,
1837
- borderRadius: 4,
2047
+ borderRadius: 6,
1838
2048
  overflow: "hidden",
1839
- cursor: "pointer"
1840
- },
1841
- onMouseEnter: () => setHoveredThumb(shot.filename),
1842
- onMouseLeave: () => setHoveredThumb(null),
1843
- onClick: () => setLightboxSrc(imgUrl),
1844
- children: [
1845
- /* @__PURE__ */ jsx2(
1846
- "img",
1847
- {
1848
- src: imgUrl,
1849
- alt: "",
1850
- style: {
1851
- width: 56,
1852
- height: 36,
1853
- objectFit: "cover",
1854
- border: `1px solid ${stroke.default}`,
1855
- borderRadius: 4,
1856
- background: state.subtle,
1857
- display: "block"
1858
- }
1859
- }
1860
- ),
1861
- /* @__PURE__ */ jsx2(
1862
- "div",
1863
- {
1864
- style: {
1865
- position: "absolute",
1866
- inset: 0,
1867
- background: "rgba(0, 0, 0, 0.55)",
1868
- display: "flex",
1869
- alignItems: "center",
1870
- justifyContent: "center",
1871
- borderRadius: 4,
1872
- opacity: hoveredThumb === shot.filename ? 1 : 0,
1873
- transition: "opacity 0.15s ease"
1874
- },
1875
- children: /* @__PURE__ */ jsx2(Eye, { size: 16, strokeWidth: 1.8, color: fg.strong })
1876
- }
1877
- )
1878
- ]
1879
- }
1880
- ),
1881
- /* @__PURE__ */ jsx2("div", { style: { flex: 1, minWidth: 0 }, children: isEditing ? /* @__PURE__ */ jsx2(
1882
- "input",
1883
- {
1884
- autoFocus: true,
1885
- value: editValue,
1886
- onChange: (e) => setEditValue(e.target.value),
1887
- onKeyDown: (e) => {
1888
- if (e.key === "Enter") handleRename(shot.filename, editValue);
1889
- if (e.key === "Escape") setEditingFile(null);
1890
- },
1891
- onBlur: () => handleRename(shot.filename, editValue),
1892
- style: {
1893
- width: "100%",
1894
- ...text.label.xs,
1895
- color: fg.strong,
1896
- background: state.hover,
1897
- border: `1px solid ${stroke.interactive}`,
1898
- borderRadius: 4,
1899
- padding: "2px 6px",
1900
- outline: "none",
1901
- fontFamily: "inherit"
1902
- }
1903
- }
1904
- ) : /* @__PURE__ */ jsx2(
1905
- "div",
1906
- {
1907
- onClick: () => {
1908
- setEditingFile(shot.filename);
1909
- setEditValue(shot.filename.replace(/\.png$/, ""));
1910
- },
1911
- style: {
1912
- ...text.label.xs,
1913
- color: fg.strong,
1914
- cursor: "pointer",
1915
- overflow: "hidden",
1916
- textOverflow: "ellipsis",
1917
- whiteSpace: "nowrap"
1918
- },
1919
- title: "Click to rename",
1920
- children: formatTimestamp(shot.filename)
1921
- }
1922
- ) }),
1923
- /* @__PURE__ */ jsx2(
1924
- "button",
1925
- {
1926
- onClick: () => handleDelete(shot.filename),
1927
- title: "Delete screenshot",
1928
- style: {
1929
- flexShrink: 0,
1930
- width: 24,
1931
- height: 24,
1932
- borderRadius: 4,
1933
- border: "none",
1934
- background: "transparent",
1935
- color: fg.faint,
1936
2049
  cursor: "pointer",
1937
- display: "flex",
1938
- alignItems: "center",
1939
- justifyContent: "center",
1940
- padding: 0
1941
- },
1942
- onMouseEnter: (e) => {
1943
- e.currentTarget.style.color = feedback.error;
1944
- e.currentTarget.style.background = feedback.errorBg;
1945
- },
1946
- onMouseLeave: (e) => {
1947
- e.currentTarget.style.color = fg.faint;
1948
- e.currentTarget.style.background = "transparent";
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"
1949
2053
  },
1950
- children: /* @__PURE__ */ jsx2(Trash22, { size: 13, strokeWidth: 1.8 })
1951
- }
1952
- )
1953
- ]
1954
- },
1955
- shot.filename
1956
- );
1957
- })
1958
- }
1959
- ),
1960
- /* @__PURE__ */ jsx2(
1961
- "div",
1962
- {
1963
- style: {
1964
- height: 1,
1965
- background: state.active,
1966
- margin: "8px 0"
1967
- }
1968
- }
1969
- ),
1970
- /* @__PURE__ */ jsxs2("div", { style: { display: "flex", flexDirection: "column", gap: 2 }, children: [
1971
- /* @__PURE__ */ jsxs2(ActionButton, { onClick: handleOpenFolder, children: [
1972
- /* @__PURE__ */ jsx2(FolderOpen, { size: 13, strokeWidth: 1.8 }),
1973
- "Open Folder"
1974
- ] }),
1975
- /* @__PURE__ */ jsxs2(ActionButton, { onClick: handlePush, disabled: pushing, children: [
1976
- /* @__PURE__ */ jsx2(ArrowUp, { size: 13, strokeWidth: 1.8 }),
1977
- pushing ? "Pushing..." : "Push to PR"
1978
- ] })
1979
- ] })
1980
- ] })
1981
- ] }),
1982
- toast && /* @__PURE__ */ jsx2(
1983
- "div",
1984
- {
1985
- style: {
1986
- position: "absolute",
1987
- bottom: "100%",
1988
- left: "50%",
1989
- transform: "translateX(-50%)",
1990
- marginBottom: 8,
1991
- padding: "6px 12px",
1992
- borderRadius: 6,
1993
- ...text.label.xs,
1994
- whiteSpace: "nowrap",
1995
- color: "white",
1996
- background: toast.type === "success" ? feedback.success : feedback.error,
1997
- boxShadow: shadow.toast
1998
- },
1999
- children: toast.message
2000
- }
2001
- )
2002
- ]
2003
- }
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
2004
2111
  ),
2005
2112
  lightboxSrc && createPortal(
2006
2113
  /* @__PURE__ */ jsxs2(
@@ -2129,7 +2236,7 @@ function FilterDropdown({
2129
2236
  {
2130
2237
  style: {
2131
2238
  position: "absolute",
2132
- bottom: "calc(100% + 4px)",
2239
+ top: "calc(100% + 4px)",
2133
2240
  left: 0,
2134
2241
  right: 0,
2135
2242
  maxHeight: 160,