@tangle-network/sandbox-ui 0.116.7 → 0.116.9

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.
@@ -1085,7 +1085,7 @@ function ProfileAvatar({ user, isLoading = false, onLogout, onSettingsClick, set
1085
1085
  className: "mr-2 h-4 w-4",
1086
1086
  "aria-hidden": "true"
1087
1087
  }), "Settings"]
1088
- }) : /* @__PURE__ */ jsx(DropdownMenuItem, {
1088
+ }) : settingsHref !== null ? /* @__PURE__ */ jsx(DropdownMenuItem, {
1089
1089
  asChild: true,
1090
1090
  children: /* @__PURE__ */ jsxs(Link, {
1091
1091
  href: settingsHref,
@@ -1096,8 +1096,8 @@ function ProfileAvatar({ user, isLoading = false, onLogout, onSettingsClick, set
1096
1096
  "aria-hidden": "true"
1097
1097
  }), "Settings"]
1098
1098
  })
1099
- }),
1100
- onLogout && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(DropdownMenuSeparator, {}), /* @__PURE__ */ jsxs(DropdownMenuItem, {
1099
+ }) : null,
1100
+ onLogout && /* @__PURE__ */ jsxs(Fragment, { children: [(children || appearance || onSettingsClick || settingsHref !== null) && /* @__PURE__ */ jsx(DropdownMenuSeparator, {}), /* @__PURE__ */ jsxs(DropdownMenuItem, {
1101
1101
  className: "text-[var(--surface-danger-text)]",
1102
1102
  onClick: onLogout,
1103
1103
  children: [/* @__PURE__ */ jsx(LogOutIcon, {
@@ -1489,6 +1489,12 @@ function Spec({ icon, value, unit }) {
1489
1489
  ]
1490
1490
  });
1491
1491
  }
1492
+ function UnknownMetric({ label }) {
1493
+ return /* @__PURE__ */ jsxs("div", {
1494
+ className: "flex items-center justify-between text-xs font-mono uppercase tracking-wide text-muted-foreground",
1495
+ children: [/* @__PURE__ */ jsx("span", { children: label }), /* @__PURE__ */ jsx("span", { children: "Unknown" })]
1496
+ });
1497
+ }
1492
1498
  function SandboxCard({ sandbox, onOpenIDE, onOpenTerminal, onWake, onRestore, onDelete, onStop, onResume, onFork, onKeepAlive, onUsage, onHealth, className }) {
1493
1499
  const isRunning = sandbox.status === "running";
1494
1500
  const isTransitioning = sandbox.status === "provisioning" || sandbox.status === "creating";
@@ -1502,9 +1508,9 @@ function SandboxCard({ sandbox, onOpenIDE, onOpenTerminal, onWake, onRestore, on
1502
1508
  const resumeHandler = isStopped ? resolveResumeHandler(sandbox.status) : void 0;
1503
1509
  const status = STATUS_META[sandbox.status];
1504
1510
  const isArchived = sandbox.status === "archived";
1505
- const cpuPercent = sandbox.cpuPercent ?? 0;
1506
- const ramUsed = sandbox.ramUsed ?? 0;
1507
- const ramTotal = sandbox.ramTotal ?? 0;
1511
+ const cpuPercent = sandbox.cpuPercent;
1512
+ const ramUsed = sandbox.ramUsed;
1513
+ const ramTotal = sandbox.ramTotal;
1508
1514
  const diskUsed = sandbox.diskUsed;
1509
1515
  const diskTotal = sandbox.diskTotal;
1510
1516
  const specs = [
@@ -1514,7 +1520,7 @@ function SandboxCard({ sandbox, onOpenIDE, onOpenTerminal, onWake, onRestore, on
1514
1520
  value: sandbox.vcpu,
1515
1521
  unit: "vCPU"
1516
1522
  },
1517
- ramTotal > 0 && {
1523
+ ramTotal != null && ramTotal > 0 && {
1518
1524
  key: "ram",
1519
1525
  icon: /* @__PURE__ */ jsx(MemoryStick, { className: "h-3.5 w-3.5" }),
1520
1526
  value: ramTotal,
@@ -1531,27 +1537,35 @@ function SandboxCard({ sandbox, onOpenIDE, onOpenTerminal, onWake, onRestore, on
1531
1537
  className: cn$1("group relative flex flex-col justify-between overflow-hidden rounded-lg border bg-surface-container p-5 transition-colors", isRunning ? "border-[var(--status-running)]/30" : "border-[var(--md3-outline-variant)]", "hover:border-foreground/15", className),
1532
1538
  children: [
1533
1539
  /* @__PURE__ */ jsxs("div", {
1534
- className: "flex items-start justify-between",
1535
- children: [/* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsxs("div", {
1536
- className: "flex items-center gap-2",
1537
- children: [/* @__PURE__ */ jsx("h3", {
1538
- className: "truncate text-sm font-bold text-foreground",
1539
- children: sandbox.name
1540
- }), sandbox.team && /* @__PURE__ */ jsxs("span", {
1541
- className: "inline-flex items-center gap-1 rounded-full bg-[var(--accent-surface-soft)] px-2 py-0.5 text-[10px] font-semibold text-[var(--accent-text)]",
1542
- title: `Shared with ${sandbox.team.name ?? "Team"} · ${sandbox.team.role}`,
1543
- children: [/* @__PURE__ */ jsx(Users, {
1544
- className: "h-3 w-3",
1545
- "aria-hidden": "true"
1546
- }), sandbox.team.name ?? "Team"]
1547
- })]
1548
- }), /* @__PURE__ */ jsxs("p", {
1549
- className: "mt-0.5 font-mono text-[10px] tracking-wider text-muted-foreground uppercase",
1550
- children: [sandbox.nodeId || "Unknown Node", sandbox.team && /* @__PURE__ */ jsxs("span", {
1551
- className: "ml-2 normal-case tracking-normal",
1552
- children: ["· your role: ", sandbox.team.role]
1540
+ className: "flex min-w-0 items-start justify-between gap-3",
1541
+ children: [/* @__PURE__ */ jsxs("div", {
1542
+ className: "min-w-0 flex-1",
1543
+ children: [/* @__PURE__ */ jsxs("div", {
1544
+ className: "flex min-w-0 items-center gap-2",
1545
+ children: [/* @__PURE__ */ jsx("h3", {
1546
+ className: "min-w-0 flex-1 truncate text-sm font-bold text-foreground",
1547
+ title: sandbox.name,
1548
+ "aria-label": sandbox.name,
1549
+ children: sandbox.name
1550
+ }), sandbox.team && /* @__PURE__ */ jsxs("span", {
1551
+ className: "inline-flex min-w-0 max-w-40 items-center gap-1 rounded-full bg-[var(--accent-surface-soft)] px-2 py-0.5 text-[10px] font-semibold text-[var(--accent-text)]",
1552
+ title: `Shared with ${sandbox.team.name ?? "Team"} · ${sandbox.team.role}`,
1553
+ children: [/* @__PURE__ */ jsx(Users, {
1554
+ className: "h-3 w-3 shrink-0",
1555
+ "aria-hidden": "true"
1556
+ }), /* @__PURE__ */ jsx("span", {
1557
+ className: "min-w-0 truncate",
1558
+ children: sandbox.team.name ?? "Team"
1559
+ })]
1560
+ })]
1561
+ }), /* @__PURE__ */ jsxs("p", {
1562
+ className: "mt-0.5 min-w-0 truncate font-mono text-[10px] tracking-wider text-muted-foreground uppercase",
1563
+ children: [sandbox.nodeId || "Unknown Node", sandbox.team && /* @__PURE__ */ jsxs("span", {
1564
+ className: "ml-2 normal-case tracking-normal",
1565
+ children: ["· your role: ", sandbox.team.role]
1566
+ })]
1553
1567
  })]
1554
- })] }), /* @__PURE__ */ jsxs(DropdownMenu, { children: [/* @__PURE__ */ jsx(DropdownMenuTrigger, {
1568
+ }), /* @__PURE__ */ jsxs(DropdownMenu, { children: [/* @__PURE__ */ jsx(DropdownMenuTrigger, {
1555
1569
  asChild: true,
1556
1570
  children: /* @__PURE__ */ jsx("button", {
1557
1571
  type: "button",
@@ -1636,19 +1650,22 @@ function SandboxCard({ sandbox, onOpenIDE, onOpenTerminal, onWake, onRestore, on
1636
1650
  isRunning ? /* @__PURE__ */ jsxs("div", {
1637
1651
  className: "space-y-2 rounded-md border border-[var(--md3-outline-variant)] bg-surface-container-high p-3",
1638
1652
  children: [
1639
- /* @__PURE__ */ jsx(ResourceMeter, {
1653
+ cpuPercent != null ? /* @__PURE__ */ jsx(ResourceMeter, {
1640
1654
  label: "CPU",
1641
1655
  value: cpuPercent,
1642
1656
  max: 100,
1643
1657
  icon: /* @__PURE__ */ jsx(Cpu, { className: "h-3 w-3" })
1644
- }),
1645
- ramTotal > 0 && /* @__PURE__ */ jsx(ResourceMeter, {
1646
- label: "MEM",
1658
+ }) : /* @__PURE__ */ jsx(UnknownMetric, { label: "CPU" }),
1659
+ ramUsed != null && ramTotal != null && ramTotal > 0 ? /* @__PURE__ */ jsx(ResourceMeter, {
1660
+ label: "RAM",
1647
1661
  value: ramUsed,
1648
1662
  max: ramTotal,
1649
- valueLabel: `${ramUsed} / ${ramTotal} GB`,
1663
+ valueLabel: String(ramUsed) + " / " + String(ramTotal) + " GB",
1650
1664
  icon: /* @__PURE__ */ jsx(MemoryStick, { className: "h-3 w-3" })
1651
- }),
1665
+ }) : ramUsed != null ? /* @__PURE__ */ jsxs("p", {
1666
+ className: "flex items-center justify-between text-xs font-mono uppercase tracking-wide text-muted-foreground",
1667
+ children: [/* @__PURE__ */ jsx("span", { children: "RAM" }), /* @__PURE__ */ jsxs("span", { children: [String(ramUsed), " GB used; capacity unknown"] })]
1668
+ }) : /* @__PURE__ */ jsx(UnknownMetric, { label: "RAM" }),
1652
1669
  diskUsed != null && diskTotal != null && diskTotal > 0 && /* @__PURE__ */ jsx(ResourceMeter, {
1653
1670
  label: "DISK",
1654
1671
  value: diskUsed,
@@ -1702,13 +1719,13 @@ function SandboxCard({ sandbox, onOpenIDE, onOpenTerminal, onWake, onRestore, on
1702
1719
  children: isRunning ? /* @__PURE__ */ jsxs("button", {
1703
1720
  type: "button",
1704
1721
  onClick: () => onOpenIDE?.(sandbox.id),
1705
- className: "flex w-full items-center justify-center gap-2 rounded-md bg-[var(--btn-primary-bg)] px-4 py-2 text-sm font-semibold text-[var(--btn-primary-text)] transition-colors hover:bg-[var(--btn-primary-hover)] active:scale-[0.97]",
1722
+ className: cn$1("flex w-full items-center justify-center gap-2 min-h-11 rounded-md bg-[var(--btn-primary-bg)] px-4 py-2 text-sm font-semibold text-[var(--btn-primary-text)] transition-colors hover:bg-[var(--btn-primary-hover)] active:scale-[0.97]", focusRing),
1706
1723
  children: [/* @__PURE__ */ jsx(Network, { className: "h-4 w-4" }), "Connect Session"]
1707
1724
  }) : /* @__PURE__ */ jsxs("button", {
1708
1725
  type: "button",
1709
1726
  onClick: () => resumeHandler?.(sandbox.id),
1710
1727
  disabled: isTransitioning || !resumeHandler,
1711
- className: cn$1("flex w-full items-center justify-center gap-2 rounded-md px-4 py-2 text-sm font-semibold transition-colors border", isTransitioning || !resumeHandler ? "bg-muted text-muted-foreground cursor-not-allowed border-[var(--md3-outline-variant)]" : "bg-surface-container-high text-foreground hover:bg-surface-container-highest border-[var(--md3-outline-variant)] active:scale-[0.97]"),
1728
+ className: cn$1("flex min-h-11 w-full items-center justify-center gap-2 rounded-md px-4 py-2 text-sm font-semibold transition-colors border", focusRing, isTransitioning || !resumeHandler ? "bg-muted text-muted-foreground cursor-not-allowed border-[var(--md3-outline-variant)]" : "bg-surface-container-high text-foreground hover:bg-surface-container-highest border-[var(--md3-outline-variant)] active:scale-[0.97]"),
1712
1729
  children: [/* @__PURE__ */ jsx(Play, { className: "h-4 w-4" }), isTransitioning ? "Starting..." : resumeLabel]
1713
1730
  })
1714
1731
  })
@@ -1779,6 +1796,7 @@ function isResumable(status) {
1779
1796
  return status !== "running" && status !== "provisioning" && status !== "creating";
1780
1797
  }
1781
1798
  function MiniMeter({ label, percent, className }) {
1799
+ const hasValue = percent != null && Number.isFinite(percent);
1782
1800
  return /* @__PURE__ */ jsxs("div", {
1783
1801
  className: cn$1("space-y-1", className),
1784
1802
  children: [/* @__PURE__ */ jsxs("div", {
@@ -1786,44 +1804,91 @@ function MiniMeter({ label, percent, className }) {
1786
1804
  children: [/* @__PURE__ */ jsx("span", {
1787
1805
  className: "font-bold",
1788
1806
  children: label
1789
- }), /* @__PURE__ */ jsxs("span", {
1790
- className: "text-primary",
1791
- children: [percent, "%"]
1807
+ }), /* @__PURE__ */ jsx("span", {
1808
+ className: hasValue ? "text-primary" : "text-muted-foreground",
1809
+ children: hasValue ? String(percent) + "%" : "Unknown"
1792
1810
  })]
1793
1811
  }), /* @__PURE__ */ jsx("div", {
1794
- className: "h-1.5 w-full bg-muted rounded-full overflow-hidden",
1795
- children: /* @__PURE__ */ jsx("div", {
1796
- className: "h-full bg-primary rounded-full",
1797
- style: { width: `${percent}%` }
1812
+ className: "h-1.5 w-full overflow-hidden rounded-full bg-muted",
1813
+ "aria-hidden": "true",
1814
+ children: hasValue && /* @__PURE__ */ jsx("div", {
1815
+ className: "h-full rounded-full bg-primary",
1816
+ style: { width: String(percent) + "%" }
1798
1817
  })
1799
1818
  })]
1800
1819
  });
1801
1820
  }
1821
+ function StatusIndicator({ status }) {
1822
+ const sc = statusColors[status] ?? statusColors.stopped;
1823
+ return /* @__PURE__ */ jsxs("div", {
1824
+ className: "flex items-center gap-2 whitespace-nowrap",
1825
+ children: [/* @__PURE__ */ jsx("span", {
1826
+ className: cn$1("flex h-2.5 w-2.5 shrink-0 rounded-full", sc.dot),
1827
+ "aria-hidden": "true",
1828
+ ...sc.dot.includes("animate-") ? { "data-motion": "essential" } : {}
1829
+ }), /* @__PURE__ */ jsx("span", {
1830
+ className: cn$1("text-xs font-bold uppercase tracking-wide", sc.text),
1831
+ children: status.charAt(0).toUpperCase() + status.slice(1)
1832
+ })]
1833
+ });
1834
+ }
1835
+ function SandboxResources({ sandbox, className }) {
1836
+ if (sandbox.status === "running") {
1837
+ const ramPercent = sandbox.ramUsed != null && sandbox.ramTotal != null && sandbox.ramTotal > 0 ? Math.round(sandbox.ramUsed / sandbox.ramTotal * 100) : void 0;
1838
+ return /* @__PURE__ */ jsxs("div", {
1839
+ className: cn$1("min-w-0 space-y-3", className),
1840
+ children: [/* @__PURE__ */ jsx(MiniMeter, {
1841
+ label: "CPU",
1842
+ percent: sandbox.cpuPercent
1843
+ }), /* @__PURE__ */ jsx(MiniMeter, {
1844
+ label: "RAM",
1845
+ percent: ramPercent
1846
+ })]
1847
+ });
1848
+ }
1849
+ if (sandbox.status === "provisioning" || sandbox.status === "creating") return /* @__PURE__ */ jsxs("div", {
1850
+ className: cn$1("flex min-w-0 items-center gap-2 text-primary italic text-[10px] font-bold", className),
1851
+ children: [/* @__PURE__ */ jsx(RefreshCw, {
1852
+ className: "h-3.5 w-3.5 shrink-0 animate-spin",
1853
+ "data-motion": "essential"
1854
+ }), /* @__PURE__ */ jsx("span", {
1855
+ className: "min-w-0 truncate",
1856
+ title: sandbox.provisioningMessage ?? "Allocating nodes...",
1857
+ children: sandbox.provisioningMessage ?? "Allocating nodes..."
1858
+ })]
1859
+ });
1860
+ if (sandbox.status === "hibernating") return /* @__PURE__ */ jsx("p", {
1861
+ className: cn$1("text-xs text-muted-foreground", className),
1862
+ children: "Not running"
1863
+ });
1864
+ return null;
1865
+ }
1866
+ function getPageItems(page, totalPages) {
1867
+ if (totalPages <= 7) return Array.from({ length: totalPages }, (_, index) => index + 1);
1868
+ let start = Math.max(2, page - 1);
1869
+ let end = Math.min(totalPages - 1, page + 1);
1870
+ if (page <= 3) {
1871
+ start = 2;
1872
+ end = 5;
1873
+ } else if (page >= totalPages - 2) {
1874
+ start = totalPages - 4;
1875
+ end = totalPages - 1;
1876
+ }
1877
+ const items = [1];
1878
+ if (start > 2) items.push("start-ellipsis");
1879
+ for (let current = start; current <= end; current += 1) items.push(current);
1880
+ if (end < totalPages - 1) items.push("end-ellipsis");
1881
+ items.push(totalPages);
1882
+ return items;
1883
+ }
1802
1884
  function SandboxTable({ sandboxes, page = 1, pageSize = 10, total, onPageChange, onOpenIDE, onOpenTerminal, onSSH, onResume, onWake, onMore, onDelete, onStop, onKeepAlive, onUsage, onHealth, onFork, className }) {
1803
- const totalCount = total ?? sandboxes.length;
1804
- const totalPages = Math.ceil(totalCount / pageSize);
1885
+ const totalCount = Math.max(0, total ?? sandboxes.length);
1886
+ const safePageSize = Number.isFinite(pageSize) ? Math.max(1, Math.floor(pageSize)) : 10;
1887
+ const totalPages = Math.ceil(totalCount / safePageSize);
1888
+ const currentPage = Math.min(Number.isFinite(page) ? Math.max(1, Math.floor(page)) : 1, Math.max(1, totalPages));
1889
+ const rangeStart = totalCount === 0 || sandboxes.length === 0 ? 0 : (currentPage - 1) * safePageSize + 1;
1890
+ const rangeEnd = rangeStart === 0 ? 0 : Math.min(rangeStart + sandboxes.length - 1, totalCount);
1805
1891
  const hasTeamSandboxes = sandboxes.some((sb) => sb.team !== void 0);
1806
- const tableScrollRef = React$1.useRef(null);
1807
- const [hasHiddenColumns, setHasHiddenColumns] = React$1.useState(false);
1808
- React$1.useEffect(() => {
1809
- const scroller = tableScrollRef.current;
1810
- const table = scroller?.querySelector("table");
1811
- if (!scroller || !table) return;
1812
- const measure = () => setHasHiddenColumns(scroller.scrollWidth > scroller.clientWidth + 1);
1813
- measure();
1814
- if (typeof ResizeObserver === "undefined") {
1815
- window.addEventListener("resize", measure);
1816
- return () => window.removeEventListener("resize", measure);
1817
- }
1818
- const observer = new ResizeObserver(measure);
1819
- observer.observe(scroller);
1820
- observer.observe(table);
1821
- return () => observer.disconnect();
1822
- }, [sandboxes, hasTeamSandboxes]);
1823
- const scrollColumns = (direction) => {
1824
- const scroller = tableScrollRef.current;
1825
- scroller?.scrollBy({ left: direction * scroller.clientWidth * .75 });
1826
- };
1827
1892
  const resolveResumeHandler = (status) => {
1828
1893
  if (onResume) return onResume;
1829
1894
  if (status === "hibernating") return onWake;
@@ -1836,348 +1901,359 @@ function SandboxTable({ sandboxes, page = 1, pageSize = 10, total, onPageChange,
1836
1901
  };
1837
1902
  return /* @__PURE__ */ jsxs("div", {
1838
1903
  className: cn$1("w-full", className),
1839
- children: [
1840
- sandboxes.length > 0 && hasHiddenColumns && /* @__PURE__ */ jsxs("div", {
1841
- className: "mb-2 flex items-center justify-end gap-2 text-xs text-muted-foreground",
1842
- children: [
1843
- /* @__PURE__ */ jsx("span", { children: "Columns" }),
1844
- /* @__PURE__ */ jsx("button", {
1845
- type: "button",
1846
- onClick: () => scrollColumns(-1),
1847
- className: `rounded-md border border-[var(--md3-outline-variant)] p-1.5 ${focusRing}`,
1848
- "aria-label": "Scroll sandbox table left",
1849
- title: "Scroll table left",
1850
- children: /* @__PURE__ */ jsx(ChevronLeft, { className: "h-4 w-4" })
1851
- }),
1852
- /* @__PURE__ */ jsx("button", {
1853
- type: "button",
1854
- onClick: () => scrollColumns(1),
1855
- className: `rounded-md border border-[var(--md3-outline-variant)] p-1.5 ${focusRing}`,
1856
- "aria-label": "Scroll sandbox table right",
1857
- title: "Scroll table right",
1858
- children: /* @__PURE__ */ jsx(ChevronRight, { className: "h-4 w-4" })
1859
- })
1860
- ]
1861
- }),
1862
- /* @__PURE__ */ jsx("div", {
1863
- className: "w-full bg-surface-container rounded-2xl overflow-hidden border border-[var(--md3-outline-variant)]",
1864
- children: /* @__PURE__ */ jsx("div", {
1865
- ref: tableScrollRef,
1866
- className: `max-h-[min(60vh,35rem)] overflow-auto ${focusRingInset}`,
1867
- role: "region",
1868
- "aria-label": "Sandbox table; scroll horizontally for more columns",
1869
- tabIndex: 0,
1870
- children: /* @__PURE__ */ jsxs("table", {
1871
- className: cn$1("w-full text-left border-collapse", hasTeamSandboxes && "[&_th]:px-4 [&_td]:px-4"),
1872
- children: [/* @__PURE__ */ jsx("thead", {
1873
- className: "sticky top-0 z-10",
1874
- children: /* @__PURE__ */ jsxs("tr", {
1875
- className: "bg-surface-container-high border-b border-[var(--md3-outline-variant)]",
1904
+ children: [/* @__PURE__ */ jsx("div", {
1905
+ className: "w-full bg-surface-container rounded-2xl overflow-hidden border border-[var(--md3-outline-variant)]",
1906
+ children: /* @__PURE__ */ jsx("div", {
1907
+ className: cn$1("max-h-[min(60vh,35rem)] overflow-x-hidden overflow-y-auto", focusRingInset),
1908
+ role: "region",
1909
+ "aria-label": "Sandbox list",
1910
+ tabIndex: 0,
1911
+ children: /* @__PURE__ */ jsxs("table", {
1912
+ className: "w-full table-fixed border-collapse text-left",
1913
+ children: [/* @__PURE__ */ jsx("thead", {
1914
+ className: "sticky top-0 z-10",
1915
+ children: /* @__PURE__ */ jsxs("tr", {
1916
+ className: "bg-surface-container-high border-b border-[var(--md3-outline-variant)]",
1917
+ children: [
1918
+ /* @__PURE__ */ jsx("th", {
1919
+ scope: "col",
1920
+ className: "hidden w-28 px-3 py-4 text-xs font-semibold uppercase tracking-wider text-muted-foreground xl:table-cell",
1921
+ children: "Status"
1922
+ }),
1923
+ /* @__PURE__ */ jsx("th", {
1924
+ scope: "col",
1925
+ className: "px-3 py-4 text-xs font-semibold uppercase tracking-wider text-muted-foreground xl:px-6",
1926
+ children: "Sandbox"
1927
+ }),
1928
+ hasTeamSandboxes && /* @__PURE__ */ jsx("th", {
1929
+ scope: "col",
1930
+ className: "hidden w-32 px-3 py-4 text-xs font-semibold uppercase tracking-wider text-muted-foreground xl:table-cell",
1931
+ children: "Scope"
1932
+ }),
1933
+ /* @__PURE__ */ jsx("th", {
1934
+ scope: "col",
1935
+ className: "hidden w-28 px-3 py-4 text-xs font-semibold uppercase tracking-wider text-muted-foreground xl:table-cell",
1936
+ children: "Environment"
1937
+ }),
1938
+ /* @__PURE__ */ jsx("th", {
1939
+ scope: "col",
1940
+ className: "hidden w-48 px-3 py-4 text-xs font-semibold uppercase tracking-wider text-muted-foreground xl:table-cell",
1941
+ children: "Resources"
1942
+ }),
1943
+ /* @__PURE__ */ jsx("th", {
1944
+ scope: "col",
1945
+ className: "w-28 px-2 py-4 text-right text-xs font-semibold uppercase tracking-wider text-muted-foreground xl:w-52 xl:px-4",
1946
+ children: "Actions"
1947
+ })
1948
+ ]
1949
+ })
1950
+ }), /* @__PURE__ */ jsx("tbody", {
1951
+ className: "divide-y divide-border",
1952
+ children: sandboxes.map((sb) => {
1953
+ const isActive = sb.status === "running";
1954
+ const isHibernating = sb.status === "hibernating";
1955
+ const isProvisioning = sb.status === "provisioning" || sb.status === "creating";
1956
+ const resumeHandler = isResumable(sb.status) ? resolveResumeHandler(sb.status) : void 0;
1957
+ const onRowClick = resolveRowClick(sb);
1958
+ const resumeLabel = isHibernating ? "Wake Up" : "Resume";
1959
+ const stopRowClick = (e) => e.stopPropagation();
1960
+ return /* @__PURE__ */ jsxs("tr", {
1961
+ className: cn$1("group relative transition-colors", onRowClick ? "cursor-pointer hover:bg-surface-container-high" : "hover:bg-surface-container-high"),
1962
+ onClick: onRowClick,
1876
1963
  children: [
1877
- /* @__PURE__ */ jsx("th", {
1878
- className: "px-6 py-4 text-xs font-semibold text-muted-foreground uppercase tracking-wider",
1879
- children: "Status"
1964
+ /* @__PURE__ */ jsx("td", {
1965
+ className: "hidden px-3 py-4 align-top xl:table-cell xl:px-4 xl:py-5",
1966
+ children: /* @__PURE__ */ jsx(StatusIndicator, { status: sb.status })
1880
1967
  }),
1881
- /* @__PURE__ */ jsx("th", {
1882
- className: "px-6 py-4 text-xs font-semibold text-muted-foreground uppercase tracking-wider",
1883
- children: "Sandbox Name"
1884
- }),
1885
- hasTeamSandboxes && /* @__PURE__ */ jsx("th", {
1886
- className: "px-6 py-4 text-xs font-semibold text-muted-foreground uppercase tracking-wider",
1887
- children: "Scope"
1888
- }),
1889
- /* @__PURE__ */ jsx("th", {
1890
- className: "px-6 py-4 text-xs font-semibold text-muted-foreground uppercase tracking-wider",
1891
- children: "Environment"
1892
- }),
1893
- /* @__PURE__ */ jsx("th", {
1894
- className: "px-6 py-4 text-xs font-semibold text-muted-foreground uppercase tracking-wider",
1895
- children: "Resources"
1896
- }),
1897
- /* @__PURE__ */ jsx("th", {
1898
- className: "px-6 py-4 text-xs font-semibold text-muted-foreground uppercase tracking-wider text-right",
1899
- children: "Actions"
1900
- })
1901
- ]
1902
- })
1903
- }), /* @__PURE__ */ jsx("tbody", {
1904
- className: "divide-y divide-border",
1905
- children: sandboxes.map((sb) => {
1906
- const sc = statusColors[sb.status] ?? statusColors.stopped;
1907
- const isActive = sb.status === "running";
1908
- const isHibernating = sb.status === "hibernating";
1909
- const isProvisioning = sb.status === "provisioning";
1910
- const resumeHandler = isResumable(sb.status) ? resolveResumeHandler(sb.status) : void 0;
1911
- const onRowClick = resolveRowClick(sb);
1912
- const resumeLabel = isHibernating ? "Wake Up" : "Resume";
1913
- const stopRowClick = (e) => e.stopPropagation();
1914
- return /* @__PURE__ */ jsxs("tr", {
1915
- className: cn$1("group relative transition-colors", onRowClick ? "cursor-pointer hover:bg-surface-container-high" : "hover:bg-surface-container-high"),
1916
- onClick: onRowClick,
1917
- children: [
1918
- /* @__PURE__ */ jsx("td", {
1919
- className: "px-6 py-5 whitespace-nowrap",
1920
- children: /* @__PURE__ */ jsxs("div", {
1921
- className: "flex items-center gap-2",
1922
- children: [/* @__PURE__ */ jsx("span", {
1923
- className: cn$1("flex h-2.5 w-2.5 rounded-full", sc.dot),
1924
- ...sc.dot.includes("animate-") ? { "data-motion": "essential" } : {}
1925
- }), /* @__PURE__ */ jsx("span", {
1926
- className: cn$1("text-xs font-bold uppercase tracking-wide", sc.text),
1927
- children: sb.status.charAt(0).toUpperCase() + sb.status.slice(1)
1968
+ /* @__PURE__ */ jsx("td", {
1969
+ className: "min-w-0 px-3 py-4 align-top xl:px-6 xl:py-5",
1970
+ children: /* @__PURE__ */ jsxs("div", {
1971
+ className: "flex min-w-0 flex-col gap-2",
1972
+ children: [/* @__PURE__ */ jsxs("div", {
1973
+ className: "flex min-w-0 items-start gap-2",
1974
+ children: [/* @__PURE__ */ jsx("div", {
1975
+ className: "shrink-0 xl:hidden",
1976
+ children: /* @__PURE__ */ jsx(StatusIndicator, { status: sb.status })
1977
+ }), /* @__PURE__ */ jsxs("div", {
1978
+ className: "min-w-0 flex-1",
1979
+ children: [/* @__PURE__ */ jsx("span", {
1980
+ className: "block min-w-0 truncate text-sm font-bold text-foreground transition-colors group-hover:text-primary",
1981
+ title: sb.name,
1982
+ "aria-label": sb.name,
1983
+ children: sb.name
1984
+ }), sb.nodeId && /* @__PURE__ */ jsx("span", {
1985
+ className: "block min-w-0 truncate text-[10px] font-mono text-muted-foreground",
1986
+ title: sb.nodeId,
1987
+ children: sb.nodeId
1988
+ })]
1928
1989
  })]
1929
- })
1930
- }),
1931
- /* @__PURE__ */ jsx("td", {
1932
- className: "px-6 py-5",
1933
- children: /* @__PURE__ */ jsxs("div", {
1934
- className: cn$1("flex min-w-0 flex-col", hasTeamSandboxes ? "max-w-36" : "max-w-64"),
1935
- children: [/* @__PURE__ */ jsx("span", {
1936
- className: "truncate text-sm font-bold text-foreground group-hover:text-primary transition-colors",
1937
- title: sb.name,
1938
- children: sb.name
1939
- }), sb.nodeId && /* @__PURE__ */ jsx("span", {
1940
- className: "truncate text-[10px] font-mono text-muted-foreground",
1941
- title: sb.nodeId,
1942
- children: sb.nodeId
1943
- })]
1944
- })
1945
- }),
1946
- hasTeamSandboxes && /* @__PURE__ */ jsx("td", {
1947
- className: "px-6 py-5",
1948
- children: sb.team ? /* @__PURE__ */ jsxs("div", {
1949
- className: "inline-flex max-w-40 items-center gap-1.5 rounded-full bg-[var(--accent-surface-soft)] px-2.5 py-1 text-[11px] font-semibold text-[var(--accent-text)]",
1950
- title: `Shared with ${sb.team.name ?? "Team"} · ${sb.team.role}`,
1990
+ }), /* @__PURE__ */ jsxs("div", {
1991
+ className: "grid min-w-0 gap-1 text-[11px] xl:hidden",
1951
1992
  children: [
1952
- /* @__PURE__ */ jsx(Users, {
1953
- className: "h-3 w-3 shrink-0",
1954
- "aria-hidden": "true"
1993
+ hasTeamSandboxes && /* @__PURE__ */ jsxs("div", {
1994
+ className: "flex min-w-0 gap-2",
1995
+ children: [/* @__PURE__ */ jsx("span", {
1996
+ className: "shrink-0 text-muted-foreground",
1997
+ children: "Scope"
1998
+ }), sb.team ? /* @__PURE__ */ jsxs("span", {
1999
+ className: "min-w-0 truncate text-foreground",
2000
+ title: "Shared with " + (sb.team.name ?? "Team") + " · " + sb.team.role,
2001
+ children: [
2002
+ sb.team.name ?? "Team",
2003
+ " - ",
2004
+ sb.team.role
2005
+ ]
2006
+ }) : /* @__PURE__ */ jsx("span", {
2007
+ className: "text-foreground",
2008
+ children: "Personal"
2009
+ })]
1955
2010
  }),
1956
- /* @__PURE__ */ jsx("span", {
1957
- className: "min-w-0 truncate",
1958
- children: sb.team.name ?? "Team"
2011
+ (sb.customImage ?? sb.image) && /* @__PURE__ */ jsxs("div", {
2012
+ className: "flex min-w-0 gap-2",
2013
+ children: [/* @__PURE__ */ jsx("span", {
2014
+ className: "shrink-0 text-muted-foreground",
2015
+ children: "Environment"
2016
+ }), /* @__PURE__ */ jsx("span", {
2017
+ className: "min-w-0 truncate text-foreground",
2018
+ title: sb.customImage ?? sb.image,
2019
+ children: sb.customImage ?? sb.image
2020
+ })]
1959
2021
  }),
1960
- /* @__PURE__ */ jsxs("span", {
1961
- className: "shrink-0 font-normal text-muted-foreground",
1962
- children: ["· ", sb.team.role]
2022
+ (isActive || isProvisioning || isHibernating) && /* @__PURE__ */ jsxs("div", {
2023
+ className: "flex min-w-0 flex-col gap-1",
2024
+ children: [/* @__PURE__ */ jsx("span", {
2025
+ className: "text-muted-foreground",
2026
+ children: "Resources"
2027
+ }), /* @__PURE__ */ jsx(SandboxResources, { sandbox: sb })]
1963
2028
  })
1964
2029
  ]
1965
- }) : /* @__PURE__ */ jsxs("div", {
1966
- className: "inline-flex items-center gap-1.5 rounded-full bg-surface-container-high px-2.5 py-1 text-[11px] font-medium text-muted-foreground",
1967
- title: "Personal sandbox",
1968
- children: [/* @__PURE__ */ jsx(User, {
1969
- className: "h-3 w-3",
2030
+ })]
2031
+ })
2032
+ }),
2033
+ hasTeamSandboxes && /* @__PURE__ */ jsx("td", {
2034
+ className: "hidden px-3 py-4 align-top xl:table-cell xl:px-4 xl:py-5",
2035
+ children: sb.team ? /* @__PURE__ */ jsxs("div", {
2036
+ className: "inline-flex max-w-40 items-center gap-1.5 rounded-full bg-[var(--accent-surface-soft)] px-2.5 py-1 text-[11px] font-semibold text-[var(--accent-text)]",
2037
+ title: "Shared with " + (sb.team.name ?? "Team") + " · " + sb.team.role,
2038
+ children: [
2039
+ /* @__PURE__ */ jsx(Users, {
2040
+ className: "h-3 w-3 shrink-0",
1970
2041
  "aria-hidden": "true"
1971
- }), "Personal"]
1972
- })
1973
- }),
1974
- /* @__PURE__ */ jsx("td", {
1975
- className: "px-6 py-5",
1976
- children: /* @__PURE__ */ jsxs("div", {
1977
- className: cn$1("flex min-w-0 items-center gap-3", hasTeamSandboxes ? "max-w-26" : "max-w-28"),
1978
- children: [sb.imageIcon && /* @__PURE__ */ jsx("div", {
1979
- className: "w-8 h-8 shrink-0 rounded-lg bg-surface-container-high flex items-center justify-center",
1980
- children: sb.imageIcon
1981
- }), sb.image && /* @__PURE__ */ jsx("span", {
1982
- className: "min-w-0 truncate text-xs font-bold text-foreground",
1983
- title: sb.image,
1984
- children: sb.image
1985
- })]
1986
- })
1987
- }),
1988
- /* @__PURE__ */ jsx("td", {
1989
- className: "px-6 py-5",
1990
- children: isActive ? /* @__PURE__ */ jsxs("div", {
1991
- className: cn$1("space-y-3", hasTeamSandboxes ? "w-44" : "w-48"),
1992
- children: [/* @__PURE__ */ jsx(MiniMeter, {
1993
- label: "CPU",
1994
- percent: sb.cpuPercent ?? 0
1995
- }), /* @__PURE__ */ jsx(MiniMeter, {
1996
- label: "RAM",
1997
- percent: sb.ramTotal ? Math.round((sb.ramUsed ?? 0) / sb.ramTotal * 100) : 0
1998
- })]
1999
- }) : isProvisioning ? /* @__PURE__ */ jsxs("div", {
2000
- className: cn$1("flex items-center gap-2 text-primary italic text-[10px] font-bold", hasTeamSandboxes ? "max-w-44" : "max-w-48"),
2001
- children: [/* @__PURE__ */ jsx(RefreshCw, {
2002
- className: "h-3.5 w-3.5 shrink-0 animate-spin",
2003
- "data-motion": "essential"
2004
- }), /* @__PURE__ */ jsx("span", {
2042
+ }),
2043
+ /* @__PURE__ */ jsx("span", {
2005
2044
  className: "min-w-0 truncate",
2006
- title: sb.provisioningMessage ?? "Allocating nodes...",
2007
- children: sb.provisioningMessage ?? "Allocating nodes..."
2008
- })]
2009
- }) : isHibernating ? /* @__PURE__ */ jsxs("div", {
2010
- className: cn$1("space-y-3 opacity-30", hasTeamSandboxes ? "w-44" : "w-48"),
2011
- children: [/* @__PURE__ */ jsx(MiniMeter, {
2012
- label: "CPU",
2013
- percent: 0
2014
- }), /* @__PURE__ */ jsx(MiniMeter, {
2015
- label: "RAM",
2016
- percent: 0
2017
- })]
2018
- }) : null
2019
- }),
2020
- /* @__PURE__ */ jsx("td", {
2021
- className: "px-6 py-5 text-right",
2022
- children: /* @__PURE__ */ jsxs("div", {
2023
- className: "flex items-center justify-end gap-1",
2024
- children: [
2025
- isActive && /* @__PURE__ */ jsxs(Fragment, { children: [
2026
- /* @__PURE__ */ jsx("button", {
2027
- type: "button",
2028
- onClick: (e) => {
2029
- stopRowClick(e);
2030
- onOpenIDE?.(sb.id);
2031
- },
2032
- className: "p-2 rounded-lg hover:bg-surface-container-high text-muted-foreground hover:text-foreground transition-all active:scale-90",
2033
- title: "Open IDE",
2034
- children: /* @__PURE__ */ jsx(Code2, { className: "h-4 w-4" })
2035
- }),
2036
- /* @__PURE__ */ jsx("button", {
2037
- type: "button",
2038
- onClick: (e) => {
2039
- stopRowClick(e);
2040
- onOpenTerminal?.(sb.id);
2041
- },
2042
- className: "p-2 rounded-lg hover:bg-surface-container-high text-muted-foreground hover:text-foreground transition-all active:scale-90",
2043
- title: "Terminal",
2044
- children: /* @__PURE__ */ jsx(Terminal, { className: "h-4 w-4" })
2045
- }),
2046
- /* @__PURE__ */ jsx("button", {
2047
- type: "button",
2048
- onClick: (e) => {
2049
- stopRowClick(e);
2050
- onSSH?.(sb.id);
2051
- },
2052
- className: "p-2 rounded-lg hover:bg-surface-container-high text-muted-foreground hover:text-foreground transition-all active:scale-90",
2053
- title: "SSH",
2054
- children: /* @__PURE__ */ jsx(Key, { className: "h-4 w-4" })
2055
- })
2056
- ] }),
2057
- resumeHandler && /* @__PURE__ */ jsxs("button", {
2045
+ children: sb.team.name ?? "Team"
2046
+ }),
2047
+ /* @__PURE__ */ jsxs("span", {
2048
+ className: "shrink-0 font-normal text-muted-foreground",
2049
+ children: ["- ", sb.team.role]
2050
+ })
2051
+ ]
2052
+ }) : /* @__PURE__ */ jsxs("div", {
2053
+ className: "inline-flex items-center gap-1.5 rounded-full bg-surface-container-high px-2.5 py-1 text-[11px] font-medium text-muted-foreground",
2054
+ title: "Personal sandbox",
2055
+ children: [/* @__PURE__ */ jsx(User, {
2056
+ className: "h-3 w-3",
2057
+ "aria-hidden": "true"
2058
+ }), "Personal"]
2059
+ })
2060
+ }),
2061
+ /* @__PURE__ */ jsx("td", {
2062
+ className: "hidden px-3 py-4 align-top xl:table-cell xl:px-4 xl:py-5",
2063
+ children: /* @__PURE__ */ jsxs("div", {
2064
+ className: "flex min-w-0 items-center gap-2",
2065
+ children: [sb.imageIcon && /* @__PURE__ */ jsx("div", {
2066
+ className: "flex h-8 w-8 shrink-0 items-center justify-center rounded-lg bg-surface-container-high",
2067
+ children: sb.imageIcon
2068
+ }), (sb.customImage ?? sb.image) && /* @__PURE__ */ jsx("span", {
2069
+ className: "min-w-0 truncate text-xs font-bold text-foreground",
2070
+ title: sb.customImage ?? sb.image,
2071
+ children: sb.customImage ?? sb.image
2072
+ })]
2073
+ })
2074
+ }),
2075
+ /* @__PURE__ */ jsx("td", {
2076
+ className: "hidden px-3 py-4 align-top xl:table-cell xl:px-4 xl:py-5",
2077
+ children: /* @__PURE__ */ jsx(SandboxResources, { sandbox: sb })
2078
+ }),
2079
+ /* @__PURE__ */ jsx("td", {
2080
+ className: "w-28 px-2 py-3 text-right align-top xl:w-52 xl:px-4 xl:py-5",
2081
+ children: /* @__PURE__ */ jsxs("div", {
2082
+ className: "flex min-w-0 flex-wrap items-center justify-end gap-1",
2083
+ children: [
2084
+ isActive && /* @__PURE__ */ jsxs(Fragment, { children: [
2085
+ /* @__PURE__ */ jsx("button", {
2058
2086
  type: "button",
2059
2087
  onClick: (e) => {
2060
2088
  stopRowClick(e);
2061
- resumeHandler(sb.id);
2089
+ onOpenIDE?.(sb.id);
2062
2090
  },
2063
- className: "inline-flex items-center gap-1.5 px-3 py-1.5 rounded-lg border border-[var(--md3-outline-variant)] text-primary text-[10px] font-bold uppercase tracking-wider hover:bg-[var(--accent-surface-soft)] active:scale-95 transition-all",
2064
- title: resumeLabel,
2065
- children: [/* @__PURE__ */ jsx(Play, { className: "h-3 w-3" }), resumeLabel]
2091
+ className: cn$1("min-h-11 min-w-11 shrink-0 p-2 rounded-lg hover:bg-surface-container-high text-muted-foreground hover:text-foreground transition-all active:scale-90", focusRing),
2092
+ "aria-label": "Open IDE",
2093
+ title: "Open IDE",
2094
+ children: /* @__PURE__ */ jsx(Code2, { className: "h-4 w-4" })
2066
2095
  }),
2067
- (() => {
2068
- const runItem = (handler) => (e) => {
2069
- e.stopPropagation();
2070
- handler(sb.id);
2071
- };
2072
- const overflowSections = [];
2073
- if (isActive) {
2074
- const lifecycle = [];
2075
- if (onStop) lifecycle.push(/* @__PURE__ */ jsxs(DropdownMenuItem, {
2076
- onClick: runItem(onStop),
2077
- children: [/* @__PURE__ */ jsx(PowerOff, { className: "mr-2 h-4 w-4" }), " Stop Sandbox"]
2078
- }, "stop"));
2079
- if (onKeepAlive) lifecycle.push(/* @__PURE__ */ jsxs(DropdownMenuItem, {
2080
- onClick: runItem(onKeepAlive),
2081
- children: [/* @__PURE__ */ jsx(Clock, { className: "mr-2 h-4 w-4" }), " Keep Alive"]
2082
- }, "keep-alive"));
2083
- if (lifecycle.length) overflowSections.push(lifecycle);
2084
- const observability = [];
2085
- if (onUsage) observability.push(/* @__PURE__ */ jsxs(DropdownMenuItem, {
2086
- onClick: runItem(onUsage),
2087
- children: [/* @__PURE__ */ jsx(BarChart2, { className: "mr-2 h-4 w-4" }), " View Usage"]
2088
- }, "usage"));
2089
- if (onHealth) observability.push(/* @__PURE__ */ jsxs(DropdownMenuItem, {
2090
- onClick: runItem(onHealth),
2091
- children: [/* @__PURE__ */ jsx(Activity, { className: "mr-2 h-4 w-4" }), " Health Check"]
2092
- }, "health"));
2093
- if (observability.length) overflowSections.push(observability);
2094
- if (onFork) overflowSections.push([/* @__PURE__ */ jsxs(DropdownMenuItem, {
2095
- onClick: runItem(onFork),
2096
- children: [/* @__PURE__ */ jsx(Copy, { className: "mr-2 h-4 w-4" }), " Fork Sandbox"]
2097
- }, "fork")]);
2098
- } else if (isResumable(sb.status)) {
2099
- if (onFork) overflowSections.push([/* @__PURE__ */ jsxs(DropdownMenuItem, {
2100
- onClick: runItem(onFork),
2101
- children: [/* @__PURE__ */ jsx(Copy, { className: "mr-2 h-4 w-4" }), " Fork Sandbox"]
2102
- }, "fork")]);
2103
- }
2104
- if (onMore) overflowSections.push([/* @__PURE__ */ jsxs(DropdownMenuItem, {
2105
- onClick: runItem(onMore),
2106
- children: [/* @__PURE__ */ jsx(ExternalLink, { className: "mr-2 h-4 w-4" }), " View Details"]
2107
- }, "view-details")]);
2108
- if (overflowSections.length === 0) return null;
2109
- return /* @__PURE__ */ jsxs(DropdownMenu, { children: [/* @__PURE__ */ jsx(DropdownMenuTrigger, {
2110
- asChild: true,
2111
- children: /* @__PURE__ */ jsx("button", {
2112
- type: "button",
2113
- onClick: stopRowClick,
2114
- className: `p-2 rounded-lg hover:bg-surface-container-high text-muted-foreground hover:text-foreground transition-all active:scale-90 ${focusRing}`,
2115
- "aria-label": `More actions for ${sb.name}`,
2116
- title: "More actions",
2117
- children: /* @__PURE__ */ jsx(MoreVertical, { className: "h-4 w-4" })
2118
- })
2119
- }), /* @__PURE__ */ jsx(DropdownMenuContent, {
2120
- align: "end",
2121
- className: "min-w-[180px]",
2122
- children: overflowSections.map((section, sectionIdx) => /* @__PURE__ */ jsxs(React$1.Fragment, { children: [sectionIdx > 0 && /* @__PURE__ */ jsx(DropdownMenuSeparator, {}), section] }, sectionIdx))
2123
- })] });
2124
- })(),
2125
- onDelete && canAdminSandbox(sb) && /* @__PURE__ */ jsx("button", {
2096
+ /* @__PURE__ */ jsx("button", {
2126
2097
  type: "button",
2127
- "aria-label": `Delete ${sb.name}`,
2128
2098
  onClick: (e) => {
2129
2099
  stopRowClick(e);
2130
- onDelete(sb.id);
2100
+ onOpenTerminal?.(sb.id);
2131
2101
  },
2132
- className: `p-2 rounded-lg hover:bg-[var(--surface-danger-bg)] text-muted-foreground hover:text-[var(--surface-danger-text)] transition-all active:scale-90 ${focusRing}`,
2133
- title: "Delete",
2134
- children: /* @__PURE__ */ jsx(Trash2, { className: "h-4 w-4" })
2102
+ className: cn$1("min-h-11 min-w-11 shrink-0 p-2 rounded-lg hover:bg-surface-container-high text-muted-foreground hover:text-foreground transition-all active:scale-90", focusRing),
2103
+ "aria-label": "Open terminal",
2104
+ title: "Terminal",
2105
+ children: /* @__PURE__ */ jsx(Terminal, { className: "h-4 w-4" })
2106
+ }),
2107
+ /* @__PURE__ */ jsx("button", {
2108
+ type: "button",
2109
+ onClick: (e) => {
2110
+ stopRowClick(e);
2111
+ onSSH?.(sb.id);
2112
+ },
2113
+ className: cn$1("min-h-11 min-w-11 shrink-0 p-2 rounded-lg hover:bg-surface-container-high text-muted-foreground hover:text-foreground transition-all active:scale-90", focusRing),
2114
+ "aria-label": "Open SSH details",
2115
+ title: "SSH",
2116
+ children: /* @__PURE__ */ jsx(Key, { className: "h-4 w-4" })
2135
2117
  })
2136
- ]
2137
- })
2118
+ ] }),
2119
+ resumeHandler && /* @__PURE__ */ jsxs("button", {
2120
+ type: "button",
2121
+ onClick: (e) => {
2122
+ stopRowClick(e);
2123
+ resumeHandler(sb.id);
2124
+ },
2125
+ className: cn$1("inline-flex min-h-11 items-center gap-1.5 px-3 py-1.5 rounded-lg border border-[var(--md3-outline-variant)] text-primary text-[10px] font-bold uppercase tracking-wider hover:bg-[var(--accent-surface-soft)] active:scale-95 transition-all", focusRing),
2126
+ title: resumeLabel,
2127
+ children: [/* @__PURE__ */ jsx(Play, { className: "h-3 w-3" }), resumeLabel]
2128
+ }),
2129
+ (() => {
2130
+ const runItem = (handler) => (e) => {
2131
+ e.stopPropagation();
2132
+ handler(sb.id);
2133
+ };
2134
+ const overflowSections = [];
2135
+ if (isActive) {
2136
+ const lifecycle = [];
2137
+ if (onStop) lifecycle.push(/* @__PURE__ */ jsxs(DropdownMenuItem, {
2138
+ onClick: runItem(onStop),
2139
+ children: [/* @__PURE__ */ jsx(PowerOff, { className: "mr-2 h-4 w-4" }), " Stop Sandbox"]
2140
+ }, "stop"));
2141
+ if (onKeepAlive) lifecycle.push(/* @__PURE__ */ jsxs(DropdownMenuItem, {
2142
+ onClick: runItem(onKeepAlive),
2143
+ children: [/* @__PURE__ */ jsx(Clock, { className: "mr-2 h-4 w-4" }), " Keep Alive"]
2144
+ }, "keep-alive"));
2145
+ if (lifecycle.length) overflowSections.push(lifecycle);
2146
+ const observability = [];
2147
+ if (onUsage) observability.push(/* @__PURE__ */ jsxs(DropdownMenuItem, {
2148
+ onClick: runItem(onUsage),
2149
+ children: [/* @__PURE__ */ jsx(BarChart2, { className: "mr-2 h-4 w-4" }), " View Usage"]
2150
+ }, "usage"));
2151
+ if (onHealth) observability.push(/* @__PURE__ */ jsxs(DropdownMenuItem, {
2152
+ onClick: runItem(onHealth),
2153
+ children: [/* @__PURE__ */ jsx(Activity, { className: "mr-2 h-4 w-4" }), " Health Check"]
2154
+ }, "health"));
2155
+ if (observability.length) overflowSections.push(observability);
2156
+ if (onFork) overflowSections.push([/* @__PURE__ */ jsxs(DropdownMenuItem, {
2157
+ onClick: runItem(onFork),
2158
+ children: [/* @__PURE__ */ jsx(Copy, { className: "mr-2 h-4 w-4" }), " Fork Sandbox"]
2159
+ }, "fork")]);
2160
+ } else if (isResumable(sb.status)) {
2161
+ if (onFork) overflowSections.push([/* @__PURE__ */ jsxs(DropdownMenuItem, {
2162
+ onClick: runItem(onFork),
2163
+ children: [/* @__PURE__ */ jsx(Copy, { className: "mr-2 h-4 w-4" }), " Fork Sandbox"]
2164
+ }, "fork")]);
2165
+ }
2166
+ if (onMore) overflowSections.push([/* @__PURE__ */ jsxs(DropdownMenuItem, {
2167
+ onClick: runItem(onMore),
2168
+ children: [/* @__PURE__ */ jsx(ExternalLink, { className: "mr-2 h-4 w-4" }), " View Details"]
2169
+ }, "view-details")]);
2170
+ if (overflowSections.length === 0) return null;
2171
+ return /* @__PURE__ */ jsxs(DropdownMenu, { children: [/* @__PURE__ */ jsx(DropdownMenuTrigger, {
2172
+ asChild: true,
2173
+ children: /* @__PURE__ */ jsx("button", {
2174
+ type: "button",
2175
+ onClick: stopRowClick,
2176
+ className: `min-h-11 min-w-11 shrink-0 p-2 rounded-lg hover:bg-surface-container-high text-muted-foreground hover:text-foreground transition-all active:scale-90 ${focusRing}`,
2177
+ "aria-label": `More actions for ${sb.name}`,
2178
+ title: "More actions",
2179
+ children: /* @__PURE__ */ jsx(MoreVertical, { className: "h-4 w-4" })
2180
+ })
2181
+ }), /* @__PURE__ */ jsx(DropdownMenuContent, {
2182
+ align: "end",
2183
+ className: "min-w-[180px]",
2184
+ children: overflowSections.map((section, sectionIdx) => /* @__PURE__ */ jsxs(React$1.Fragment, { children: [sectionIdx > 0 && /* @__PURE__ */ jsx(DropdownMenuSeparator, {}), section] }, sectionIdx))
2185
+ })] });
2186
+ })(),
2187
+ onDelete && canAdminSandbox(sb) && /* @__PURE__ */ jsx("button", {
2188
+ type: "button",
2189
+ "aria-label": `Delete ${sb.name}`,
2190
+ onClick: (e) => {
2191
+ stopRowClick(e);
2192
+ onDelete(sb.id);
2193
+ },
2194
+ className: `min-h-11 min-w-11 shrink-0 p-2 rounded-lg hover:bg-[var(--surface-danger-bg)] text-muted-foreground hover:text-[var(--surface-danger-text)] transition-all active:scale-90 ${focusRing}`,
2195
+ title: "Delete",
2196
+ children: /* @__PURE__ */ jsx(Trash2, { className: "h-4 w-4" })
2197
+ })
2198
+ ]
2138
2199
  })
2139
- ]
2140
- }, sb.id);
2141
- })
2142
- })]
2143
- })
2144
- })
2145
- }),
2146
- totalPages > 1 && /* @__PURE__ */ jsxs("div", {
2147
- className: "mt-6 flex flex-col md:flex-row justify-between items-center text-muted-foreground text-xs font-medium gap-4",
2148
- children: [/* @__PURE__ */ jsxs("p", { children: [
2149
- "Showing ",
2150
- sandboxes.length,
2151
- " of ",
2152
- totalCount,
2153
- " active sandboxes"
2154
- ] }), /* @__PURE__ */ jsxs("div", {
2155
- className: "flex items-center gap-2",
2156
- children: [
2157
- /* @__PURE__ */ jsx("button", {
2158
- type: "button",
2159
- onClick: () => onPageChange?.(page - 1),
2160
- disabled: page <= 1,
2161
- className: "p-2 rounded-lg border border-[var(--md3-outline-variant)] hover:bg-surface-container-high transition-colors disabled:opacity-30",
2162
- children: /* @__PURE__ */ jsx(ChevronLeft, { className: "h-4 w-4" })
2163
- }),
2164
- Array.from({ length: Math.min(totalPages, 5) }, (_, i) => i + 1).map((p) => /* @__PURE__ */ jsx("button", {
2165
- type: "button",
2166
- onClick: () => onPageChange?.(p),
2167
- className: cn$1("px-3 py-1 rounded-lg transition-colors", p === page ? "bg-[var(--accent-surface-soft)] text-primary border border-[var(--md3-outline)]" : "hover:bg-surface-container-high"),
2168
- children: p
2169
- }, p)),
2170
- /* @__PURE__ */ jsx("button", {
2171
- type: "button",
2172
- onClick: () => onPageChange?.(page + 1),
2173
- disabled: page >= totalPages,
2174
- className: "p-2 rounded-lg border border-[var(--md3-outline-variant)] hover:bg-surface-container-high transition-colors disabled:opacity-30",
2175
- children: /* @__PURE__ */ jsx(ChevronRight, { className: "h-4 w-4" })
2200
+ })
2201
+ ]
2202
+ }, sb.id);
2176
2203
  })
2177
- ]
2178
- })]
2204
+ })]
2205
+ })
2179
2206
  })
2180
- ]
2207
+ }), totalPages > 1 && /* @__PURE__ */ jsxs("div", {
2208
+ className: "mt-6 flex flex-col items-center justify-between gap-4 text-xs font-medium text-muted-foreground sm:flex-row",
2209
+ children: [/* @__PURE__ */ jsx("p", { children: rangeStart === 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
2210
+ "Showing 0 of ",
2211
+ totalCount,
2212
+ " sandboxes"
2213
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
2214
+ "Showing ",
2215
+ rangeStart,
2216
+ "-",
2217
+ rangeEnd,
2218
+ " of ",
2219
+ totalCount,
2220
+ " sandboxes"
2221
+ ] }) }), /* @__PURE__ */ jsxs("nav", {
2222
+ className: "flex flex-wrap items-center justify-center gap-1",
2223
+ "aria-label": "Sandbox pages",
2224
+ children: [
2225
+ /* @__PURE__ */ jsx("button", {
2226
+ type: "button",
2227
+ onClick: () => onPageChange?.(currentPage - 1),
2228
+ disabled: !onPageChange || currentPage <= 1,
2229
+ "aria-label": "Previous page",
2230
+ className: cn$1("min-h-11 min-w-11 rounded-lg border border-[var(--md3-outline-variant)] p-2 transition-colors hover:bg-surface-container-high disabled:opacity-30", focusRing),
2231
+ children: /* @__PURE__ */ jsx(ChevronLeft, { className: "h-4 w-4" })
2232
+ }),
2233
+ getPageItems(currentPage, totalPages).map((item) => typeof item === "number" ? /* @__PURE__ */ jsx("button", {
2234
+ type: "button",
2235
+ onClick: () => onPageChange?.(item),
2236
+ disabled: !onPageChange,
2237
+ "aria-label": "Go to page " + item,
2238
+ "aria-current": item === currentPage ? "page" : void 0,
2239
+ className: cn$1("min-h-11 min-w-11 rounded-lg px-2 py-1 transition-colors", item === currentPage ? "border border-[var(--md3-outline)] bg-[var(--accent-surface-soft)] text-primary" : "hover:bg-surface-container-high", !onPageChange && "cursor-not-allowed opacity-30"),
2240
+ children: item
2241
+ }, item) : /* @__PURE__ */ jsx("span", {
2242
+ className: "px-1",
2243
+ "aria-hidden": "true",
2244
+ children: "..."
2245
+ }, item)),
2246
+ /* @__PURE__ */ jsx("button", {
2247
+ type: "button",
2248
+ onClick: () => onPageChange?.(currentPage + 1),
2249
+ disabled: !onPageChange || currentPage >= totalPages,
2250
+ "aria-label": "Next page",
2251
+ className: cn$1("min-h-11 min-w-11 rounded-lg border border-[var(--md3-outline-variant)] p-2 transition-colors hover:bg-surface-container-high disabled:opacity-30", focusRing),
2252
+ children: /* @__PURE__ */ jsx(ChevronRight, { className: "h-4 w-4" })
2253
+ })
2254
+ ]
2255
+ })]
2256
+ })]
2181
2257
  });
2182
2258
  }
2183
2259
  //#endregion