@tangle-network/sandbox-ui 0.16.0 → 0.16.2
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.
- package/dist/{chunk-HLZTKSGT.js → chunk-5I363RL7.js} +303 -126
- package/dist/dashboard.d.ts +23 -1
- package/dist/dashboard.js +1 -1
- package/dist/globals.css +3 -0
- package/dist/index.js +1 -1
- package/dist/styles.css +3 -0
- package/package.json +1 -1
|
@@ -666,8 +666,34 @@ function NewSandboxCard({ onClick, className }) {
|
|
|
666
666
|
}
|
|
667
667
|
|
|
668
668
|
// src/dashboard/sandbox-table.tsx
|
|
669
|
-
import
|
|
670
|
-
import {
|
|
669
|
+
import * as React3 from "react";
|
|
670
|
+
import {
|
|
671
|
+
Activity as Activity2,
|
|
672
|
+
BarChart2 as BarChart22,
|
|
673
|
+
ChevronLeft,
|
|
674
|
+
ChevronRight,
|
|
675
|
+
Clock as Clock2,
|
|
676
|
+
Code2 as Code22,
|
|
677
|
+
Copy as Copy2,
|
|
678
|
+
ExternalLink,
|
|
679
|
+
Key,
|
|
680
|
+
MoreVertical as MoreVertical2,
|
|
681
|
+
Play as Play2,
|
|
682
|
+
PowerOff as PowerOff2,
|
|
683
|
+
RefreshCw,
|
|
684
|
+
Terminal as Terminal2,
|
|
685
|
+
Trash2 as Trash22,
|
|
686
|
+
User,
|
|
687
|
+
Users as Users2
|
|
688
|
+
} from "lucide-react";
|
|
689
|
+
import {
|
|
690
|
+
DropdownMenu as DropdownMenu3,
|
|
691
|
+
DropdownMenuContent as DropdownMenuContent3,
|
|
692
|
+
DropdownMenuItem as DropdownMenuItem3,
|
|
693
|
+
DropdownMenuSeparator as DropdownMenuSeparator3,
|
|
694
|
+
DropdownMenuTrigger as DropdownMenuTrigger3
|
|
695
|
+
} from "@tangle-network/ui/primitives";
|
|
696
|
+
import { Fragment as Fragment5, jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
671
697
|
var statusColors = {
|
|
672
698
|
running: { dot: "bg-[var(--code-success)] animate-pulse", text: "text-[var(--code-success)]", bar: "bg-[var(--code-success)]" },
|
|
673
699
|
hibernating: { dot: "bg-muted-foreground", text: "text-muted-foreground", bar: "bg-muted-foreground" },
|
|
@@ -677,6 +703,9 @@ var statusColors = {
|
|
|
677
703
|
failed: { dot: "bg-[var(--code-error)]", text: "text-[var(--code-error)]", bar: "bg-[var(--code-error)]" },
|
|
678
704
|
archived: { dot: "bg-border", text: "text-muted-foreground", bar: "bg-border" }
|
|
679
705
|
};
|
|
706
|
+
function isResumable(status) {
|
|
707
|
+
return status !== "running" && status !== "provisioning" && status !== "creating";
|
|
708
|
+
}
|
|
680
709
|
function MiniMeter({ label, percent, className }) {
|
|
681
710
|
return /* @__PURE__ */ jsxs6("div", { className: cn("space-y-1", className), children: [
|
|
682
711
|
/* @__PURE__ */ jsxs6("div", { className: "flex justify-between text-[10px] font-mono text-muted-foreground", children: [
|
|
@@ -698,14 +727,33 @@ function SandboxTable({
|
|
|
698
727
|
onOpenIDE,
|
|
699
728
|
onOpenTerminal,
|
|
700
729
|
onSSH,
|
|
730
|
+
onResume,
|
|
701
731
|
onWake,
|
|
702
732
|
onMore,
|
|
703
733
|
onDelete,
|
|
734
|
+
onStop,
|
|
735
|
+
onKeepAlive,
|
|
736
|
+
onUsage,
|
|
737
|
+
onHealth,
|
|
738
|
+
onFork,
|
|
704
739
|
className
|
|
705
740
|
}) {
|
|
706
741
|
const totalCount = total ?? sandboxes.length;
|
|
707
742
|
const totalPages = Math.ceil(totalCount / pageSize);
|
|
708
743
|
const hasTeamSandboxes = sandboxes.some((sb) => sb.team !== void 0);
|
|
744
|
+
const resolveResumeHandler = (status) => {
|
|
745
|
+
if (onResume) return onResume;
|
|
746
|
+
if (status === "hibernating") return onWake;
|
|
747
|
+
return void 0;
|
|
748
|
+
};
|
|
749
|
+
const resolveRowClick = (sb) => {
|
|
750
|
+
if (sb.status === "running") {
|
|
751
|
+
return onOpenIDE ? () => onOpenIDE(sb.id) : void 0;
|
|
752
|
+
}
|
|
753
|
+
if (!isResumable(sb.status)) return void 0;
|
|
754
|
+
const handler = resolveResumeHandler(sb.status);
|
|
755
|
+
return handler ? () => handler(sb.id) : void 0;
|
|
756
|
+
};
|
|
709
757
|
return /* @__PURE__ */ jsxs6("div", { className: cn("w-full", className), children: [
|
|
710
758
|
/* @__PURE__ */ jsx7("div", { className: "w-full bg-card rounded-2xl overflow-hidden border border-border", children: /* @__PURE__ */ jsx7("div", { className: "overflow-x-auto", children: /* @__PURE__ */ jsxs6("table", { className: "w-full text-left border-collapse", children: [
|
|
711
759
|
/* @__PURE__ */ jsx7("thead", { children: /* @__PURE__ */ jsxs6("tr", { className: "bg-background border-b border-border", children: [
|
|
@@ -721,65 +769,194 @@ function SandboxTable({
|
|
|
721
769
|
const isActive = sb.status === "running";
|
|
722
770
|
const isHibernating = sb.status === "hibernating";
|
|
723
771
|
const isProvisioning = sb.status === "provisioning";
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
sb.team.role
|
|
744
|
-
] })
|
|
745
|
-
]
|
|
746
|
-
}
|
|
747
|
-
) : /* @__PURE__ */ jsxs6(
|
|
748
|
-
"div",
|
|
772
|
+
const resumeHandler = isResumable(sb.status) ? resolveResumeHandler(sb.status) : void 0;
|
|
773
|
+
const onRowClick = resolveRowClick(sb);
|
|
774
|
+
const resumeLabel = isHibernating ? "Wake Up" : "Resume";
|
|
775
|
+
const stopRowClick = (e) => e.stopPropagation();
|
|
776
|
+
return (
|
|
777
|
+
// onClick is a sighted-user convenience only. We
|
|
778
|
+
// deliberately do NOT add role="button" / tabIndex /
|
|
779
|
+
// an onKeyDown handler to the row — overriding a
|
|
780
|
+
// <tr>'s implicit row role with "button" collapses
|
|
781
|
+
// the per-cell announcements (Status, Environment,
|
|
782
|
+
// Resources…) that screen-reader users navigate
|
|
783
|
+
// through. Keyboard and assistive-tech users reach
|
|
784
|
+
// the same actions through the real <button>
|
|
785
|
+
// elements inside the actions cell (Resume, Open
|
|
786
|
+
// IDE, Delete, …), which keep their native
|
|
787
|
+
// semantics. Mouse users get the click-anywhere
|
|
788
|
+
// affordance; nobody loses access.
|
|
789
|
+
/* @__PURE__ */ jsxs6(
|
|
790
|
+
"tr",
|
|
749
791
|
{
|
|
750
|
-
className:
|
|
751
|
-
|
|
792
|
+
className: cn(
|
|
793
|
+
"group relative transition-colors",
|
|
794
|
+
onRowClick ? "cursor-pointer hover:bg-muted/50" : "hover:bg-muted/30"
|
|
795
|
+
),
|
|
796
|
+
onClick: onRowClick,
|
|
752
797
|
children: [
|
|
753
|
-
/* @__PURE__ */ jsx7(
|
|
754
|
-
|
|
798
|
+
/* @__PURE__ */ jsx7("td", { className: "px-6 py-5 whitespace-nowrap", children: /* @__PURE__ */ jsxs6("div", { className: "flex items-center gap-2", children: [
|
|
799
|
+
/* @__PURE__ */ jsx7("span", { className: cn("flex h-2.5 w-2.5 rounded-full", sc.dot) }),
|
|
800
|
+
/* @__PURE__ */ jsx7("span", { className: cn("text-xs font-bold uppercase tracking-wide", sc.text), children: sb.status.charAt(0).toUpperCase() + sb.status.slice(1) })
|
|
801
|
+
] }) }),
|
|
802
|
+
/* @__PURE__ */ jsx7("td", { className: "px-6 py-5", children: /* @__PURE__ */ jsxs6("div", { className: "flex flex-col", children: [
|
|
803
|
+
/* @__PURE__ */ jsx7("span", { className: "text-sm font-bold text-foreground group-hover:text-primary transition-colors", children: sb.name }),
|
|
804
|
+
sb.nodeId && /* @__PURE__ */ jsx7("span", { className: "text-[10px] font-mono text-muted-foreground", children: sb.nodeId })
|
|
805
|
+
] }) }),
|
|
806
|
+
hasTeamSandboxes && /* @__PURE__ */ jsx7("td", { className: "px-6 py-5", children: sb.team ? /* @__PURE__ */ jsxs6(
|
|
807
|
+
"div",
|
|
808
|
+
{
|
|
809
|
+
className: "inline-flex 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)]",
|
|
810
|
+
title: `Shared with ${sb.team.name ?? "Team"} \xB7 ${sb.team.role}`,
|
|
811
|
+
children: [
|
|
812
|
+
/* @__PURE__ */ jsx7(Users2, { className: "h-3 w-3", "aria-hidden": "true" }),
|
|
813
|
+
/* @__PURE__ */ jsx7("span", { children: sb.team.name ?? "Team" }),
|
|
814
|
+
/* @__PURE__ */ jsxs6("span", { className: "font-normal text-muted-foreground", children: [
|
|
815
|
+
"\xB7 ",
|
|
816
|
+
sb.team.role
|
|
817
|
+
] })
|
|
818
|
+
]
|
|
819
|
+
}
|
|
820
|
+
) : /* @__PURE__ */ jsxs6(
|
|
821
|
+
"div",
|
|
822
|
+
{
|
|
823
|
+
className: "inline-flex items-center gap-1.5 rounded-full bg-muted px-2.5 py-1 text-[11px] font-medium text-muted-foreground",
|
|
824
|
+
title: "Personal sandbox",
|
|
825
|
+
children: [
|
|
826
|
+
/* @__PURE__ */ jsx7(User, { className: "h-3 w-3", "aria-hidden": "true" }),
|
|
827
|
+
"Personal"
|
|
828
|
+
]
|
|
829
|
+
}
|
|
830
|
+
) }),
|
|
831
|
+
/* @__PURE__ */ jsx7("td", { className: "px-6 py-5", children: /* @__PURE__ */ jsxs6("div", { className: "flex items-center gap-3", children: [
|
|
832
|
+
sb.imageIcon && /* @__PURE__ */ jsx7("div", { className: "w-8 h-8 rounded-lg bg-muted/50 flex items-center justify-center", children: sb.imageIcon }),
|
|
833
|
+
sb.image && /* @__PURE__ */ jsx7("span", { className: "text-xs font-bold text-foreground", children: sb.image })
|
|
834
|
+
] }) }),
|
|
835
|
+
/* @__PURE__ */ jsx7("td", { className: "px-6 py-5", children: isActive ? /* @__PURE__ */ jsxs6("div", { className: "space-y-3 w-48", children: [
|
|
836
|
+
/* @__PURE__ */ jsx7(MiniMeter, { label: "CPU", percent: sb.cpuPercent ?? 0 }),
|
|
837
|
+
/* @__PURE__ */ jsx7(MiniMeter, { label: "RAM", percent: sb.ramTotal ? Math.round((sb.ramUsed ?? 0) / sb.ramTotal * 100) : 0 })
|
|
838
|
+
] }) : isProvisioning ? /* @__PURE__ */ jsxs6("div", { className: "flex items-center gap-2 text-primary italic text-[10px] font-bold", children: [
|
|
839
|
+
/* @__PURE__ */ jsx7(RefreshCw, { className: "h-3.5 w-3.5 animate-spin" }),
|
|
840
|
+
sb.provisioningMessage ?? "Allocating nodes..."
|
|
841
|
+
] }) : isHibernating ? /* @__PURE__ */ jsxs6("div", { className: "space-y-3 w-48 opacity-30", children: [
|
|
842
|
+
/* @__PURE__ */ jsx7(MiniMeter, { label: "CPU", percent: 0 }),
|
|
843
|
+
/* @__PURE__ */ jsx7(MiniMeter, { label: "RAM", percent: 0 })
|
|
844
|
+
] }) : null }),
|
|
845
|
+
/* @__PURE__ */ jsx7("td", { className: "px-6 py-5 text-right", children: /* @__PURE__ */ jsxs6("div", { className: "flex items-center justify-end gap-1", children: [
|
|
846
|
+
isActive && /* @__PURE__ */ jsxs6(Fragment5, { children: [
|
|
847
|
+
/* @__PURE__ */ jsx7("button", { type: "button", onClick: (e) => {
|
|
848
|
+
stopRowClick(e);
|
|
849
|
+
onOpenIDE?.(sb.id);
|
|
850
|
+
}, className: "p-2 rounded-lg hover:bg-muted text-muted-foreground hover:text-foreground transition-all active:scale-90", title: "Open IDE", children: /* @__PURE__ */ jsx7(Code22, { className: "h-4 w-4" }) }),
|
|
851
|
+
/* @__PURE__ */ jsx7("button", { type: "button", onClick: (e) => {
|
|
852
|
+
stopRowClick(e);
|
|
853
|
+
onOpenTerminal?.(sb.id);
|
|
854
|
+
}, className: "p-2 rounded-lg hover:bg-muted text-muted-foreground hover:text-foreground transition-all active:scale-90", title: "Terminal", children: /* @__PURE__ */ jsx7(Terminal2, { className: "h-4 w-4" }) }),
|
|
855
|
+
/* @__PURE__ */ jsx7("button", { type: "button", onClick: (e) => {
|
|
856
|
+
stopRowClick(e);
|
|
857
|
+
onSSH?.(sb.id);
|
|
858
|
+
}, className: "p-2 rounded-lg hover:bg-muted text-muted-foreground hover:text-foreground transition-all active:scale-90", title: "SSH", children: /* @__PURE__ */ jsx7(Key, { className: "h-4 w-4" }) })
|
|
859
|
+
] }),
|
|
860
|
+
resumeHandler && /* @__PURE__ */ jsxs6(
|
|
861
|
+
"button",
|
|
862
|
+
{
|
|
863
|
+
type: "button",
|
|
864
|
+
onClick: (e) => {
|
|
865
|
+
stopRowClick(e);
|
|
866
|
+
resumeHandler(sb.id);
|
|
867
|
+
},
|
|
868
|
+
className: "inline-flex items-center gap-1.5 px-3 py-1.5 rounded-lg border border-border text-primary text-[10px] font-bold uppercase tracking-wider hover:bg-[var(--accent-surface-soft)] active:scale-95 transition-all",
|
|
869
|
+
title: resumeLabel,
|
|
870
|
+
children: [
|
|
871
|
+
/* @__PURE__ */ jsx7(Play2, { className: "h-3 w-3" }),
|
|
872
|
+
resumeLabel
|
|
873
|
+
]
|
|
874
|
+
}
|
|
875
|
+
),
|
|
876
|
+
(() => {
|
|
877
|
+
const runItem = (handler) => (e) => {
|
|
878
|
+
e.stopPropagation();
|
|
879
|
+
handler(sb.id);
|
|
880
|
+
};
|
|
881
|
+
const overflowSections = [];
|
|
882
|
+
if (isActive) {
|
|
883
|
+
const lifecycle = [];
|
|
884
|
+
if (onStop) lifecycle.push(
|
|
885
|
+
/* @__PURE__ */ jsxs6(DropdownMenuItem3, { onClick: runItem(onStop), children: [
|
|
886
|
+
/* @__PURE__ */ jsx7(PowerOff2, { className: "mr-2 h-4 w-4" }),
|
|
887
|
+
" Stop Sandbox"
|
|
888
|
+
] }, "stop")
|
|
889
|
+
);
|
|
890
|
+
if (onKeepAlive) lifecycle.push(
|
|
891
|
+
/* @__PURE__ */ jsxs6(DropdownMenuItem3, { onClick: runItem(onKeepAlive), children: [
|
|
892
|
+
/* @__PURE__ */ jsx7(Clock2, { className: "mr-2 h-4 w-4" }),
|
|
893
|
+
" Keep Alive"
|
|
894
|
+
] }, "keep-alive")
|
|
895
|
+
);
|
|
896
|
+
if (lifecycle.length) overflowSections.push(lifecycle);
|
|
897
|
+
const observability = [];
|
|
898
|
+
if (onUsage) observability.push(
|
|
899
|
+
/* @__PURE__ */ jsxs6(DropdownMenuItem3, { onClick: runItem(onUsage), children: [
|
|
900
|
+
/* @__PURE__ */ jsx7(BarChart22, { className: "mr-2 h-4 w-4" }),
|
|
901
|
+
" View Usage"
|
|
902
|
+
] }, "usage")
|
|
903
|
+
);
|
|
904
|
+
if (onHealth) observability.push(
|
|
905
|
+
/* @__PURE__ */ jsxs6(DropdownMenuItem3, { onClick: runItem(onHealth), children: [
|
|
906
|
+
/* @__PURE__ */ jsx7(Activity2, { className: "mr-2 h-4 w-4" }),
|
|
907
|
+
" Health Check"
|
|
908
|
+
] }, "health")
|
|
909
|
+
);
|
|
910
|
+
if (observability.length) overflowSections.push(observability);
|
|
911
|
+
if (onFork) overflowSections.push([
|
|
912
|
+
/* @__PURE__ */ jsxs6(DropdownMenuItem3, { onClick: runItem(onFork), children: [
|
|
913
|
+
/* @__PURE__ */ jsx7(Copy2, { className: "mr-2 h-4 w-4" }),
|
|
914
|
+
" Fork Sandbox"
|
|
915
|
+
] }, "fork")
|
|
916
|
+
]);
|
|
917
|
+
} else if (isResumable(sb.status)) {
|
|
918
|
+
if (onFork) overflowSections.push([
|
|
919
|
+
/* @__PURE__ */ jsxs6(DropdownMenuItem3, { onClick: runItem(onFork), children: [
|
|
920
|
+
/* @__PURE__ */ jsx7(Copy2, { className: "mr-2 h-4 w-4" }),
|
|
921
|
+
" Fork Sandbox"
|
|
922
|
+
] }, "fork")
|
|
923
|
+
]);
|
|
924
|
+
}
|
|
925
|
+
if (onMore) overflowSections.push([
|
|
926
|
+
/* @__PURE__ */ jsxs6(DropdownMenuItem3, { onClick: runItem(onMore), children: [
|
|
927
|
+
/* @__PURE__ */ jsx7(ExternalLink, { className: "mr-2 h-4 w-4" }),
|
|
928
|
+
" View Details"
|
|
929
|
+
] }, "view-details")
|
|
930
|
+
]);
|
|
931
|
+
if (overflowSections.length === 0) return null;
|
|
932
|
+
return /* @__PURE__ */ jsxs6(DropdownMenu3, { children: [
|
|
933
|
+
/* @__PURE__ */ jsx7(DropdownMenuTrigger3, { asChild: true, children: /* @__PURE__ */ jsx7(
|
|
934
|
+
"button",
|
|
935
|
+
{
|
|
936
|
+
type: "button",
|
|
937
|
+
onClick: stopRowClick,
|
|
938
|
+
className: "p-2 rounded-lg hover:bg-muted text-muted-foreground hover:text-foreground transition-all active:scale-90 outline-none",
|
|
939
|
+
"aria-label": "More actions",
|
|
940
|
+
title: "More actions",
|
|
941
|
+
children: /* @__PURE__ */ jsx7(MoreVertical2, { className: "h-4 w-4" })
|
|
942
|
+
}
|
|
943
|
+
) }),
|
|
944
|
+
/* @__PURE__ */ jsx7(DropdownMenuContent3, { align: "end", className: "min-w-[180px]", children: overflowSections.map((section, sectionIdx) => /* @__PURE__ */ jsxs6(React3.Fragment, { children: [
|
|
945
|
+
sectionIdx > 0 && /* @__PURE__ */ jsx7(DropdownMenuSeparator3, {}),
|
|
946
|
+
section
|
|
947
|
+
] }, sectionIdx)) })
|
|
948
|
+
] });
|
|
949
|
+
})(),
|
|
950
|
+
onDelete && canAdminSandbox(sb) && /* @__PURE__ */ jsx7("button", { type: "button", onClick: (e) => {
|
|
951
|
+
stopRowClick(e);
|
|
952
|
+
onDelete(sb.id);
|
|
953
|
+
}, 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", title: "Delete", children: /* @__PURE__ */ jsx7(Trash22, { className: "h-4 w-4" }) })
|
|
954
|
+
] }) })
|
|
755
955
|
]
|
|
756
|
-
}
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
sb.image && /* @__PURE__ */ jsx7("span", { className: "text-xs font-bold text-foreground", children: sb.image })
|
|
761
|
-
] }) }),
|
|
762
|
-
/* @__PURE__ */ jsx7("td", { className: "px-6 py-5", children: isActive ? /* @__PURE__ */ jsxs6("div", { className: "space-y-3 w-48", children: [
|
|
763
|
-
/* @__PURE__ */ jsx7(MiniMeter, { label: "CPU", percent: sb.cpuPercent ?? 0 }),
|
|
764
|
-
/* @__PURE__ */ jsx7(MiniMeter, { label: "RAM", percent: sb.ramTotal ? Math.round((sb.ramUsed ?? 0) / sb.ramTotal * 100) : 0 })
|
|
765
|
-
] }) : isProvisioning ? /* @__PURE__ */ jsxs6("div", { className: "flex items-center gap-2 text-primary italic text-[10px] font-bold", children: [
|
|
766
|
-
/* @__PURE__ */ jsx7(RefreshCw, { className: "h-3.5 w-3.5 animate-spin" }),
|
|
767
|
-
sb.provisioningMessage ?? "Allocating nodes..."
|
|
768
|
-
] }) : isHibernating ? /* @__PURE__ */ jsxs6("div", { className: "space-y-3 w-48 opacity-30", children: [
|
|
769
|
-
/* @__PURE__ */ jsx7(MiniMeter, { label: "CPU", percent: 0 }),
|
|
770
|
-
/* @__PURE__ */ jsx7(MiniMeter, { label: "RAM", percent: 0 })
|
|
771
|
-
] }) : null }),
|
|
772
|
-
/* @__PURE__ */ jsx7("td", { className: "px-6 py-5 text-right", children: /* @__PURE__ */ jsxs6("div", { className: "flex items-center justify-end gap-1", children: [
|
|
773
|
-
isActive && /* @__PURE__ */ jsxs6(Fragment4, { children: [
|
|
774
|
-
/* @__PURE__ */ jsx7("button", { type: "button", onClick: () => onOpenIDE?.(sb.id), className: "p-2 rounded-lg hover:bg-muted text-muted-foreground hover:text-foreground transition-all active:scale-90", title: "Open IDE", children: /* @__PURE__ */ jsx7(Code22, { className: "h-4 w-4" }) }),
|
|
775
|
-
/* @__PURE__ */ jsx7("button", { type: "button", onClick: () => onOpenTerminal?.(sb.id), className: "p-2 rounded-lg hover:bg-muted text-muted-foreground hover:text-foreground transition-all active:scale-90", title: "Terminal", children: /* @__PURE__ */ jsx7(Terminal2, { className: "h-4 w-4" }) }),
|
|
776
|
-
/* @__PURE__ */ jsx7("button", { type: "button", onClick: () => onSSH?.(sb.id), className: "p-2 rounded-lg hover:bg-muted text-muted-foreground hover:text-foreground transition-all active:scale-90", title: "SSH", children: /* @__PURE__ */ jsx7(Key, { className: "h-4 w-4" }) })
|
|
777
|
-
] }),
|
|
778
|
-
isHibernating && /* @__PURE__ */ jsx7("button", { type: "button", onClick: () => onWake?.(sb.id), className: "px-3 py-1.5 rounded-lg border border-border text-primary text-[10px] font-bold uppercase tracking-wider hover:bg-[var(--accent-surface-soft)] active:scale-95 transition-all", children: "Wake Up" }),
|
|
779
|
-
onMore && /* @__PURE__ */ jsx7("button", { type: "button", onClick: () => onMore(sb.id), className: "p-2 rounded-lg hover:bg-muted text-muted-foreground hover:text-foreground transition-all active:scale-90", children: /* @__PURE__ */ jsx7(Code22, { className: "h-4 w-4" }) }),
|
|
780
|
-
onDelete && canAdminSandbox(sb) && /* @__PURE__ */ jsx7("button", { type: "button", onClick: () => onDelete(sb.id), 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", title: "Delete", children: /* @__PURE__ */ jsx7(Trash22, { className: "h-4 w-4" }) })
|
|
781
|
-
] }) })
|
|
782
|
-
] }, sb.id);
|
|
956
|
+
},
|
|
957
|
+
sb.id
|
|
958
|
+
)
|
|
959
|
+
);
|
|
783
960
|
}) })
|
|
784
961
|
] }) }) }),
|
|
785
962
|
totalPages > 1 && /* @__PURE__ */ jsxs6("div", { className: "mt-6 flex flex-col md:flex-row justify-between items-center text-muted-foreground text-xs font-medium gap-4", children: [
|
|
@@ -1039,9 +1216,9 @@ function HarnessPicker({
|
|
|
1039
1216
|
}
|
|
1040
1217
|
|
|
1041
1218
|
// src/dashboard/dashboard-layout.tsx
|
|
1042
|
-
import * as
|
|
1219
|
+
import * as React4 from "react";
|
|
1043
1220
|
import { Plus as Plus2, Bell } from "lucide-react";
|
|
1044
|
-
import { Fragment as
|
|
1221
|
+
import { Fragment as Fragment7, jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
1045
1222
|
function SettingsIconSmall({ className }) {
|
|
1046
1223
|
return /* @__PURE__ */ jsxs10("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, children: [
|
|
1047
1224
|
/* @__PURE__ */ jsx12("title", { children: "Settings" }),
|
|
@@ -1102,10 +1279,10 @@ function DashboardLayoutInner({
|
|
|
1102
1279
|
notifications: notifData
|
|
1103
1280
|
}) {
|
|
1104
1281
|
const Link = LinkComponent;
|
|
1105
|
-
const [mobileMenuOpen, setMobileMenuOpen] =
|
|
1106
|
-
const [notificationsOpen, setNotificationsOpen] =
|
|
1107
|
-
const notifRef =
|
|
1108
|
-
|
|
1282
|
+
const [mobileMenuOpen, setMobileMenuOpen] = React4.useState(false);
|
|
1283
|
+
const [notificationsOpen, setNotificationsOpen] = React4.useState(false);
|
|
1284
|
+
const notifRef = React4.useRef(null);
|
|
1285
|
+
React4.useEffect(() => {
|
|
1109
1286
|
if (!notificationsOpen) return;
|
|
1110
1287
|
const handler = (e) => {
|
|
1111
1288
|
if (notifRef.current && !notifRef.current.contains(e.target)) {
|
|
@@ -1123,21 +1300,21 @@ function DashboardLayoutInner({
|
|
|
1123
1300
|
};
|
|
1124
1301
|
}, [notificationsOpen]);
|
|
1125
1302
|
const { contentMargin, hidden, mode, hasPanels, panelOpen } = useSidebar();
|
|
1126
|
-
const modeSet =
|
|
1127
|
-
const sidebarUser =
|
|
1303
|
+
const modeSet = React4.useMemo(() => new Set(modeItems), [modeItems]);
|
|
1304
|
+
const sidebarUser = React4.useMemo(
|
|
1128
1305
|
() => user ? { email: user.email, name: user.name, tier: user.tier, avatarUrl: user.avatarUrl } : void 0,
|
|
1129
1306
|
[user?.email, user?.name, user?.tier, user?.avatarUrl]
|
|
1130
1307
|
);
|
|
1131
1308
|
const activePanel = panels.find((p) => p.mode === mode);
|
|
1132
|
-
const buildSidebarContent =
|
|
1133
|
-
(showLabels) => /* @__PURE__ */ jsxs10(
|
|
1309
|
+
const buildSidebarContent = React4.useCallback(
|
|
1310
|
+
(showLabels) => /* @__PURE__ */ jsxs10(Fragment7, { children: [
|
|
1134
1311
|
/* @__PURE__ */ jsxs10(SidebarRail, { wide: showLabels, children: [
|
|
1135
1312
|
/* @__PURE__ */ jsx12(SidebarRailHeader, { children: /* @__PURE__ */ jsx12(Link, { href: "/", to: "/", className: "p-1 rounded-md transition-colors hover:bg-muted/50", children: /* @__PURE__ */ jsx12(Logo, { variant, size: "sm", iconOnly: true }) }) }),
|
|
1136
1313
|
/* @__PURE__ */ jsx12(SidebarRailNav, { children: navItems.map((item, i) => {
|
|
1137
1314
|
const isMode = modeSet.has(item.id);
|
|
1138
1315
|
const prevIsMode = i > 0 && modeSet.has(navItems[i - 1].id);
|
|
1139
1316
|
const showSep = i > 0 && isMode && !prevIsMode;
|
|
1140
|
-
return /* @__PURE__ */ jsxs10(
|
|
1317
|
+
return /* @__PURE__ */ jsxs10(React4.Fragment, { children: [
|
|
1141
1318
|
showSep && /* @__PURE__ */ jsx12(RailSeparator, {}),
|
|
1142
1319
|
isMode ? /* @__PURE__ */ jsx12(
|
|
1143
1320
|
RailModeButton,
|
|
@@ -1202,8 +1379,8 @@ function DashboardLayoutInner({
|
|
|
1202
1379
|
mode
|
|
1203
1380
|
]
|
|
1204
1381
|
);
|
|
1205
|
-
const sidebarContent =
|
|
1206
|
-
const mobileSidebarContent =
|
|
1382
|
+
const sidebarContent = React4.useMemo(() => buildSidebarContent(false), [buildSidebarContent]);
|
|
1383
|
+
const mobileSidebarContent = React4.useMemo(() => buildSidebarContent(true), [buildSidebarContent]);
|
|
1207
1384
|
return /* @__PURE__ */ jsxs10("div", { className: cn("min-h-screen bg-background text-foreground", className), children: [
|
|
1208
1385
|
/* @__PURE__ */ jsxs10(
|
|
1209
1386
|
"nav",
|
|
@@ -1341,14 +1518,14 @@ import { Check as Check2, ChevronDown as ChevronDown2, Plus as Plus3, Settings }
|
|
|
1341
1518
|
import { Button } from "@tangle-network/ui/primitives";
|
|
1342
1519
|
import { Badge } from "@tangle-network/ui/primitives";
|
|
1343
1520
|
import {
|
|
1344
|
-
DropdownMenu as
|
|
1345
|
-
DropdownMenuContent as
|
|
1346
|
-
DropdownMenuItem as
|
|
1521
|
+
DropdownMenu as DropdownMenu4,
|
|
1522
|
+
DropdownMenuContent as DropdownMenuContent4,
|
|
1523
|
+
DropdownMenuItem as DropdownMenuItem4,
|
|
1347
1524
|
DropdownMenuLabel as DropdownMenuLabel2,
|
|
1348
|
-
DropdownMenuSeparator as
|
|
1349
|
-
DropdownMenuTrigger as
|
|
1525
|
+
DropdownMenuSeparator as DropdownMenuSeparator4,
|
|
1526
|
+
DropdownMenuTrigger as DropdownMenuTrigger4
|
|
1350
1527
|
} from "@tangle-network/ui/primitives";
|
|
1351
|
-
import { Fragment as
|
|
1528
|
+
import { Fragment as Fragment8, jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
1352
1529
|
function ProfileSelector({
|
|
1353
1530
|
profiles,
|
|
1354
1531
|
selectedId,
|
|
@@ -1365,14 +1542,14 @@ function ProfileSelector({
|
|
|
1365
1542
|
const customProfiles = profiles.filter((p) => !p.is_builtin);
|
|
1366
1543
|
return /* @__PURE__ */ jsxs11("div", { className, children: [
|
|
1367
1544
|
label && /* @__PURE__ */ jsx13("label", { className: "mb-2 block font-medium text-sm", children: label }),
|
|
1368
|
-
/* @__PURE__ */ jsxs11(
|
|
1369
|
-
/* @__PURE__ */ jsx13(
|
|
1545
|
+
/* @__PURE__ */ jsxs11(DropdownMenu4, { children: [
|
|
1546
|
+
/* @__PURE__ */ jsx13(DropdownMenuTrigger4, { asChild: true, children: /* @__PURE__ */ jsxs11(Button, { variant: "outline", className: "w-full justify-between", children: [
|
|
1370
1547
|
/* @__PURE__ */ jsx13("span", { className: "truncate", children: selected ? selected.name : placeholder }),
|
|
1371
1548
|
/* @__PURE__ */ jsx13(ChevronDown2, { className: "ml-2 h-4 w-4 shrink-0 opacity-50" })
|
|
1372
1549
|
] }) }),
|
|
1373
|
-
/* @__PURE__ */ jsxs11(
|
|
1550
|
+
/* @__PURE__ */ jsxs11(DropdownMenuContent4, { className: "w-[300px]", align: "start", children: [
|
|
1374
1551
|
/* @__PURE__ */ jsxs11(
|
|
1375
|
-
|
|
1552
|
+
DropdownMenuItem4,
|
|
1376
1553
|
{
|
|
1377
1554
|
onClick: () => onSelect(null),
|
|
1378
1555
|
className: "flex items-center justify-between",
|
|
@@ -1382,11 +1559,11 @@ function ProfileSelector({
|
|
|
1382
1559
|
]
|
|
1383
1560
|
}
|
|
1384
1561
|
),
|
|
1385
|
-
builtinProfiles.length > 0 && /* @__PURE__ */ jsxs11(
|
|
1386
|
-
/* @__PURE__ */ jsx13(
|
|
1562
|
+
builtinProfiles.length > 0 && /* @__PURE__ */ jsxs11(Fragment8, { children: [
|
|
1563
|
+
/* @__PURE__ */ jsx13(DropdownMenuSeparator4, {}),
|
|
1387
1564
|
/* @__PURE__ */ jsx13(DropdownMenuLabel2, { children: "Built-in Profiles" }),
|
|
1388
1565
|
builtinProfiles.map((profile) => /* @__PURE__ */ jsxs11(
|
|
1389
|
-
|
|
1566
|
+
DropdownMenuItem4,
|
|
1390
1567
|
{
|
|
1391
1568
|
onClick: () => onSelect(profile),
|
|
1392
1569
|
className: "flex flex-col items-start gap-1",
|
|
@@ -1407,11 +1584,11 @@ function ProfileSelector({
|
|
|
1407
1584
|
profile.id
|
|
1408
1585
|
))
|
|
1409
1586
|
] }),
|
|
1410
|
-
customProfiles.length > 0 && /* @__PURE__ */ jsxs11(
|
|
1411
|
-
/* @__PURE__ */ jsx13(
|
|
1587
|
+
customProfiles.length > 0 && /* @__PURE__ */ jsxs11(Fragment8, { children: [
|
|
1588
|
+
/* @__PURE__ */ jsx13(DropdownMenuSeparator4, {}),
|
|
1412
1589
|
/* @__PURE__ */ jsx13(DropdownMenuLabel2, { children: "Custom Profiles" }),
|
|
1413
1590
|
customProfiles.map((profile) => /* @__PURE__ */ jsxs11(
|
|
1414
|
-
|
|
1591
|
+
DropdownMenuItem4,
|
|
1415
1592
|
{
|
|
1416
1593
|
onClick: () => onSelect(profile),
|
|
1417
1594
|
className: "flex flex-col items-start gap-1",
|
|
@@ -1444,10 +1621,10 @@ function ProfileSelector({
|
|
|
1444
1621
|
profile.id
|
|
1445
1622
|
))
|
|
1446
1623
|
] }),
|
|
1447
|
-
(onCreateClick || onManageClick) && /* @__PURE__ */ jsxs11(
|
|
1448
|
-
/* @__PURE__ */ jsx13(
|
|
1624
|
+
(onCreateClick || onManageClick) && /* @__PURE__ */ jsxs11(Fragment8, { children: [
|
|
1625
|
+
/* @__PURE__ */ jsx13(DropdownMenuSeparator4, {}),
|
|
1449
1626
|
onCreateClick && /* @__PURE__ */ jsxs11(
|
|
1450
|
-
|
|
1627
|
+
DropdownMenuItem4,
|
|
1451
1628
|
{
|
|
1452
1629
|
onClick: onCreateClick,
|
|
1453
1630
|
className: "text-[var(--surface-info-text)]",
|
|
@@ -1458,7 +1635,7 @@ function ProfileSelector({
|
|
|
1458
1635
|
}
|
|
1459
1636
|
),
|
|
1460
1637
|
onManageClick && /* @__PURE__ */ jsxs11(
|
|
1461
|
-
|
|
1638
|
+
DropdownMenuItem4,
|
|
1462
1639
|
{
|
|
1463
1640
|
onClick: onManageClick,
|
|
1464
1641
|
className: "text-muted-foreground",
|
|
@@ -1542,8 +1719,8 @@ function ProfileComparison({
|
|
|
1542
1719
|
import {
|
|
1543
1720
|
Check as Check3,
|
|
1544
1721
|
CheckCircle2,
|
|
1545
|
-
Clock as
|
|
1546
|
-
ExternalLink,
|
|
1722
|
+
Clock as Clock3,
|
|
1723
|
+
ExternalLink as ExternalLink2,
|
|
1547
1724
|
Loader2,
|
|
1548
1725
|
Timer,
|
|
1549
1726
|
X,
|
|
@@ -1551,10 +1728,10 @@ import {
|
|
|
1551
1728
|
} from "lucide-react";
|
|
1552
1729
|
import { Button as Button2 } from "@tangle-network/ui/primitives";
|
|
1553
1730
|
import { Badge as Badge2 } from "@tangle-network/ui/primitives";
|
|
1554
|
-
import { Fragment as
|
|
1731
|
+
import { Fragment as Fragment9, jsx as jsx14, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
1555
1732
|
var statusConfig = {
|
|
1556
1733
|
pending: {
|
|
1557
|
-
icon:
|
|
1734
|
+
icon: Clock3,
|
|
1558
1735
|
color: "text-[var(--surface-warning-text)]",
|
|
1559
1736
|
bg: "bg-[var(--surface-warning-bg)]",
|
|
1560
1737
|
border: "border-[var(--surface-warning-border)]",
|
|
@@ -1674,7 +1851,7 @@ function VariantList({
|
|
|
1674
1851
|
children: outcomeConfig[variant.outcome].label
|
|
1675
1852
|
}
|
|
1676
1853
|
),
|
|
1677
|
-
variant.status === "completed" && variant.outcome === "pending_review" && onAccept && onReject && /* @__PURE__ */ jsxs12(
|
|
1854
|
+
variant.status === "completed" && variant.outcome === "pending_review" && onAccept && onReject && /* @__PURE__ */ jsxs12(Fragment9, { children: [
|
|
1678
1855
|
/* @__PURE__ */ jsxs12(
|
|
1679
1856
|
Button2,
|
|
1680
1857
|
{
|
|
@@ -1720,7 +1897,7 @@ function VariantList({
|
|
|
1720
1897
|
e.stopPropagation();
|
|
1721
1898
|
window.open(variant.detailsUrl, "_blank");
|
|
1722
1899
|
},
|
|
1723
|
-
children: /* @__PURE__ */ jsx14(
|
|
1900
|
+
children: /* @__PURE__ */ jsx14(ExternalLink2, { className: "h-3.5 w-3.5" })
|
|
1724
1901
|
}
|
|
1725
1902
|
)
|
|
1726
1903
|
] })
|
|
@@ -1846,7 +2023,7 @@ function SystemLogsViewer({ apiUrl, token, className }) {
|
|
|
1846
2023
|
}
|
|
1847
2024
|
|
|
1848
2025
|
// src/dashboard/usage-summary.tsx
|
|
1849
|
-
import { Clock as
|
|
2026
|
+
import { Clock as Clock4, Layers, MessageSquare, DollarSign } from "lucide-react";
|
|
1850
2027
|
import { StatCard } from "@tangle-network/ui/primitives";
|
|
1851
2028
|
import { Skeleton as Skeleton2 } from "@tangle-network/ui/primitives";
|
|
1852
2029
|
import { jsx as jsx16, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
@@ -1862,7 +2039,7 @@ function UsageSummary({ data, loading = false, className }) {
|
|
|
1862
2039
|
title: "Compute Hours",
|
|
1863
2040
|
value: data.computeHours.toFixed(1),
|
|
1864
2041
|
subtitle: "This billing period",
|
|
1865
|
-
icon: /* @__PURE__ */ jsx16(
|
|
2042
|
+
icon: /* @__PURE__ */ jsx16(Clock4, { className: "h-5 w-5" })
|
|
1866
2043
|
}
|
|
1867
2044
|
),
|
|
1868
2045
|
/* @__PURE__ */ jsx16(
|
|
@@ -1969,14 +2146,14 @@ function GitPanel({ status, log, loading = false, onRefresh, className }) {
|
|
|
1969
2146
|
}
|
|
1970
2147
|
|
|
1971
2148
|
// src/dashboard/ports-list.tsx
|
|
1972
|
-
import * as
|
|
1973
|
-
import { Copy as
|
|
2149
|
+
import * as React6 from "react";
|
|
2150
|
+
import { Copy as Copy3, Check as Check4, Globe, Plus as Plus4, Trash2 as Trash23 } from "lucide-react";
|
|
1974
2151
|
import { jsx as jsx18, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
1975
2152
|
function PortsList({ ports, onExposePort, onRemovePort, isExposing = false, className }) {
|
|
1976
|
-
const [newPort, setNewPort] =
|
|
1977
|
-
const [copiedPort, setCopiedPort] =
|
|
1978
|
-
const copyTimerRef =
|
|
1979
|
-
|
|
2153
|
+
const [newPort, setNewPort] = React6.useState("");
|
|
2154
|
+
const [copiedPort, setCopiedPort] = React6.useState(null);
|
|
2155
|
+
const copyTimerRef = React6.useRef(null);
|
|
2156
|
+
React6.useEffect(() => {
|
|
1980
2157
|
return () => {
|
|
1981
2158
|
if (copyTimerRef.current) clearTimeout(copyTimerRef.current);
|
|
1982
2159
|
};
|
|
@@ -2016,7 +2193,7 @@ function PortsList({ ports, onExposePort, onRemovePort, isExposing = false, clas
|
|
|
2016
2193
|
className: "flex items-center gap-2 font-mono text-xs text-primary hover:underline cursor-pointer group",
|
|
2017
2194
|
children: [
|
|
2018
2195
|
/* @__PURE__ */ jsx18("span", { className: "truncate max-w-[300px]", children: p.url }),
|
|
2019
|
-
copiedPort === p.port ? /* @__PURE__ */ jsx18(Check4, { className: "h-3 w-3 text-[var(--surface-success-text)] shrink-0" }) : /* @__PURE__ */ jsx18(
|
|
2196
|
+
copiedPort === p.port ? /* @__PURE__ */ jsx18(Check4, { className: "h-3 w-3 text-[var(--surface-success-text)] shrink-0" }) : /* @__PURE__ */ jsx18(Copy3, { className: "h-3 w-3 opacity-0 group-hover:opacity-100 transition-opacity shrink-0" })
|
|
2020
2197
|
]
|
|
2021
2198
|
}
|
|
2022
2199
|
) }),
|
|
@@ -2070,8 +2247,8 @@ function PortsList({ ports, onExposePort, onRemovePort, isExposing = false, clas
|
|
|
2070
2247
|
}
|
|
2071
2248
|
|
|
2072
2249
|
// src/dashboard/process-list.tsx
|
|
2073
|
-
import * as
|
|
2074
|
-
import { Activity as
|
|
2250
|
+
import * as React7 from "react";
|
|
2251
|
+
import { Activity as Activity3, Plus as Plus5, Skull, Terminal as Terminal4 } from "lucide-react";
|
|
2075
2252
|
import { jsx as jsx19, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
2076
2253
|
function formatUptime(startedAt) {
|
|
2077
2254
|
if (!startedAt) return "-";
|
|
@@ -2082,7 +2259,7 @@ function formatUptime(startedAt) {
|
|
|
2082
2259
|
return `${Math.floor(ms / 36e5)}h ${Math.floor(ms % 36e5 / 6e4)}m`;
|
|
2083
2260
|
}
|
|
2084
2261
|
function ProcessList({ processes, onSpawn, onKill, loading = false, className }) {
|
|
2085
|
-
const [newCommand, setNewCommand] =
|
|
2262
|
+
const [newCommand, setNewCommand] = React7.useState("");
|
|
2086
2263
|
const handleSpawn = () => {
|
|
2087
2264
|
const cmd = newCommand.trim();
|
|
2088
2265
|
if (cmd) {
|
|
@@ -2092,7 +2269,7 @@ function ProcessList({ processes, onSpawn, onKill, loading = false, className })
|
|
|
2092
2269
|
};
|
|
2093
2270
|
return /* @__PURE__ */ jsxs17("div", { className: cn("space-y-4", className), children: [
|
|
2094
2271
|
loading ? /* @__PURE__ */ jsxs17("div", { className: "rounded-lg border border-border bg-muted/20 p-6 text-center", children: [
|
|
2095
|
-
/* @__PURE__ */ jsx19(
|
|
2272
|
+
/* @__PURE__ */ jsx19(Activity3, { className: "mx-auto h-6 w-6 text-muted-foreground animate-spin mb-2" }),
|
|
2096
2273
|
/* @__PURE__ */ jsx19("p", { className: "text-sm text-muted-foreground", children: "Loading processes..." })
|
|
2097
2274
|
] }) : processes.length > 0 ? /* @__PURE__ */ jsx19("div", { className: "rounded-lg border border-border overflow-hidden", children: /* @__PURE__ */ jsxs17("table", { className: "w-full text-sm", children: [
|
|
2098
2275
|
/* @__PURE__ */ jsx19("thead", { className: "bg-muted/30 border-b border-border", children: /* @__PURE__ */ jsxs17("tr", { children: [
|
|
@@ -2155,11 +2332,11 @@ function ProcessList({ processes, onSpawn, onKill, loading = false, className })
|
|
|
2155
2332
|
}
|
|
2156
2333
|
|
|
2157
2334
|
// src/dashboard/network-config.tsx
|
|
2158
|
-
import * as
|
|
2335
|
+
import * as React8 from "react";
|
|
2159
2336
|
import { Network as Network2, Plus as Plus6, Trash2 as Trash24, ShieldAlert } from "lucide-react";
|
|
2160
2337
|
import { jsx as jsx20, jsxs as jsxs18 } from "react/jsx-runtime";
|
|
2161
2338
|
function NetworkConfig({ config, onUpdate, loading = false, className }) {
|
|
2162
|
-
const [newCidr, setNewCidr] =
|
|
2339
|
+
const [newCidr, setNewCidr] = React8.useState("");
|
|
2163
2340
|
const isValidCidr = (value) => {
|
|
2164
2341
|
const match = value.match(/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})\/(\d{1,2})$/);
|
|
2165
2342
|
if (!match) return false;
|
|
@@ -2263,9 +2440,9 @@ function NetworkConfig({ config, onUpdate, loading = false, className }) {
|
|
|
2263
2440
|
}
|
|
2264
2441
|
|
|
2265
2442
|
// src/dashboard/backend-config.tsx
|
|
2266
|
-
import * as
|
|
2443
|
+
import * as React9 from "react";
|
|
2267
2444
|
import { Bot, Plus as Plus7, RefreshCw as RefreshCw2, Trash2 as Trash25, Server, Wrench } from "lucide-react";
|
|
2268
|
-
import { Fragment as
|
|
2445
|
+
import { Fragment as Fragment10, jsx as jsx21, jsxs as jsxs19 } from "react/jsx-runtime";
|
|
2269
2446
|
function BackendConfig({
|
|
2270
2447
|
status,
|
|
2271
2448
|
mcpServers,
|
|
@@ -2275,10 +2452,10 @@ function BackendConfig({
|
|
|
2275
2452
|
loading = false,
|
|
2276
2453
|
className
|
|
2277
2454
|
}) {
|
|
2278
|
-
const [showAddMcp, setShowAddMcp] =
|
|
2279
|
-
const [mcpName, setMcpName] =
|
|
2280
|
-
const [mcpCommand, setMcpCommand] =
|
|
2281
|
-
const [mcpArgs, setMcpArgs] =
|
|
2455
|
+
const [showAddMcp, setShowAddMcp] = React9.useState(false);
|
|
2456
|
+
const [mcpName, setMcpName] = React9.useState("");
|
|
2457
|
+
const [mcpCommand, setMcpCommand] = React9.useState("");
|
|
2458
|
+
const [mcpArgs, setMcpArgs] = React9.useState("");
|
|
2282
2459
|
const handleAddMcp = () => {
|
|
2283
2460
|
const name = mcpName.trim();
|
|
2284
2461
|
const command = mcpCommand.trim();
|
|
@@ -2325,7 +2502,7 @@ function BackendConfig({
|
|
|
2325
2502
|
), children: status.running ? "Running" : "Stopped" }) }),
|
|
2326
2503
|
/* @__PURE__ */ jsx21("dt", { className: "text-muted-foreground", children: "Model" }),
|
|
2327
2504
|
/* @__PURE__ */ jsx21("dd", { className: "font-mono text-xs", children: status.model ?? "Default" }),
|
|
2328
|
-
status.provider && /* @__PURE__ */ jsxs19(
|
|
2505
|
+
status.provider && /* @__PURE__ */ jsxs19(Fragment10, { children: [
|
|
2329
2506
|
/* @__PURE__ */ jsx21("dt", { className: "text-muted-foreground", children: "Provider" }),
|
|
2330
2507
|
/* @__PURE__ */ jsx21("dd", { className: "font-mono text-xs", children: status.provider })
|
|
2331
2508
|
] })
|
|
@@ -2436,8 +2613,8 @@ function BackendConfig({
|
|
|
2436
2613
|
}
|
|
2437
2614
|
|
|
2438
2615
|
// src/dashboard/snapshot-list.tsx
|
|
2439
|
-
import * as
|
|
2440
|
-
import { Camera, Clock as
|
|
2616
|
+
import * as React10 from "react";
|
|
2617
|
+
import { Camera, Clock as Clock5, HardDrive, Plus as Plus8, RotateCcw } from "lucide-react";
|
|
2441
2618
|
import { jsx as jsx22, jsxs as jsxs20 } from "react/jsx-runtime";
|
|
2442
2619
|
function formatBytes(bytes) {
|
|
2443
2620
|
if (bytes == null || bytes < 0) return "-";
|
|
@@ -2453,8 +2630,8 @@ function formatDate(dateStr) {
|
|
|
2453
2630
|
return d.toLocaleDateString(void 0, { month: "short", day: "numeric", hour: "2-digit", minute: "2-digit" });
|
|
2454
2631
|
}
|
|
2455
2632
|
function SnapshotList({ snapshots, onCreate, onRestore, onSaveAsTemplate, loading = false, className }) {
|
|
2456
|
-
const [showCreate, setShowCreate] =
|
|
2457
|
-
const [tags, setTags] =
|
|
2633
|
+
const [showCreate, setShowCreate] = React10.useState(false);
|
|
2634
|
+
const [tags, setTags] = React10.useState("");
|
|
2458
2635
|
const handleCreate = () => {
|
|
2459
2636
|
const tagList = tags.trim() ? tags.trim().split(",").map((t) => t.trim()).filter(Boolean) : void 0;
|
|
2460
2637
|
onCreate(tagList);
|
|
@@ -2528,7 +2705,7 @@ function SnapshotList({ snapshots, onCreate, onRestore, onSaveAsTemplate, loadin
|
|
|
2528
2705
|
/* @__PURE__ */ jsx22("tbody", { className: "divide-y divide-border", children: snapshots.map((s) => /* @__PURE__ */ jsxs20("tr", { children: [
|
|
2529
2706
|
/* @__PURE__ */ jsx22("td", { className: "px-4 py-3 font-mono text-xs text-foreground", children: s.id.slice(0, 12) }),
|
|
2530
2707
|
/* @__PURE__ */ jsx22("td", { className: "px-4 py-3 text-xs text-muted-foreground", children: /* @__PURE__ */ jsxs20("span", { className: "inline-flex items-center gap-1.5", children: [
|
|
2531
|
-
/* @__PURE__ */ jsx22(
|
|
2708
|
+
/* @__PURE__ */ jsx22(Clock5, { className: "h-3 w-3" }),
|
|
2532
2709
|
formatDate(s.createdAt)
|
|
2533
2710
|
] }) }),
|
|
2534
2711
|
/* @__PURE__ */ jsx22("td", { className: "px-4 py-3 text-xs text-muted-foreground", children: /* @__PURE__ */ jsxs20("span", { className: "inline-flex items-center gap-1.5", children: [
|
|
@@ -2571,7 +2748,7 @@ function SnapshotList({ snapshots, onCreate, onRestore, onSaveAsTemplate, loadin
|
|
|
2571
2748
|
}
|
|
2572
2749
|
|
|
2573
2750
|
// src/dashboard/promo-banner.tsx
|
|
2574
|
-
import { Fragment as
|
|
2751
|
+
import { Fragment as Fragment11, jsx as jsx23, jsxs as jsxs21 } from "react/jsx-runtime";
|
|
2575
2752
|
function PromoBanner({
|
|
2576
2753
|
title,
|
|
2577
2754
|
description,
|
|
@@ -2586,7 +2763,7 @@ function PromoBanner({
|
|
|
2586
2763
|
"mt-6 inline-flex items-center gap-2 rounded-md border border-white/20 bg-[var(--btn-primary-bg)] px-4 py-2 text-sm font-medium text-[var(--btn-primary-text)] transition-colors",
|
|
2587
2764
|
disabled ? "opacity-50 cursor-not-allowed" : "hover:bg-[var(--btn-primary-hover)]"
|
|
2588
2765
|
);
|
|
2589
|
-
const buttonContent = /* @__PURE__ */ jsxs21(
|
|
2766
|
+
const buttonContent = /* @__PURE__ */ jsxs21(Fragment11, { children: [
|
|
2590
2767
|
buttonLabel,
|
|
2591
2768
|
/* @__PURE__ */ jsxs21("svg", { "aria-hidden": "true", xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: "h-4 w-4", children: [
|
|
2592
2769
|
/* @__PURE__ */ jsx23("path", { d: "M5 12h14" }),
|
package/dist/dashboard.d.ts
CHANGED
|
@@ -231,12 +231,34 @@ interface SandboxTableProps {
|
|
|
231
231
|
onOpenIDE?: (id: string) => void;
|
|
232
232
|
onOpenTerminal?: (id: string) => void;
|
|
233
233
|
onSSH?: (id: string) => void;
|
|
234
|
+
/**
|
|
235
|
+
* Resume a stopped / failed / archived sandbox, or wake a hibernating
|
|
236
|
+
* one. Surfaces an explicit "Resume" (or "Wake Up" for `hibernating`)
|
|
237
|
+
* button in the actions cell and makes the row body clickable for any
|
|
238
|
+
* status this prop covers. Preferred over `onWake` for new code — the
|
|
239
|
+
* two are kept as separate props only so existing consumers that
|
|
240
|
+
* wired up `onWake` for hibernating continue to work.
|
|
241
|
+
*/
|
|
242
|
+
onResume?: (id: string) => void;
|
|
243
|
+
/**
|
|
244
|
+
* @deprecated Use `onResume` instead. Retained for back-compat:
|
|
245
|
+
* when `onResume` is not provided but `onWake` is, hibernating rows
|
|
246
|
+
* will still surface a Wake action. New consumers should pass
|
|
247
|
+
* `onResume` so stopped / failed / archived rows also get a start
|
|
248
|
+
* affordance.
|
|
249
|
+
*/
|
|
234
250
|
onWake?: (id: string) => void;
|
|
235
251
|
onMore?: (id: string) => void;
|
|
252
|
+
/** Fired on the user's first click; the caller owns the confirmation step. */
|
|
236
253
|
onDelete?: (id: string) => void;
|
|
254
|
+
onStop?: (id: string) => void;
|
|
255
|
+
onKeepAlive?: (id: string) => void;
|
|
256
|
+
onUsage?: (id: string) => void;
|
|
257
|
+
onHealth?: (id: string) => void;
|
|
258
|
+
onFork?: (id: string) => void;
|
|
237
259
|
className?: string;
|
|
238
260
|
}
|
|
239
|
-
declare function SandboxTable({ sandboxes, page, pageSize, total, onPageChange, onOpenIDE, onOpenTerminal, onSSH, onWake, onMore, onDelete, className, }: SandboxTableProps): react_jsx_runtime.JSX.Element;
|
|
261
|
+
declare function SandboxTable({ sandboxes, page, pageSize, total, onPageChange, onOpenIDE, onOpenTerminal, onSSH, onResume, onWake, onMore, onDelete, onStop, onKeepAlive, onUsage, onHealth, onFork, className, }: SandboxTableProps): react_jsx_runtime.JSX.Element;
|
|
240
262
|
|
|
241
263
|
interface Invoice {
|
|
242
264
|
id: string;
|
package/dist/dashboard.js
CHANGED
package/dist/globals.css
CHANGED
package/dist/index.js
CHANGED
package/dist/styles.css
CHANGED
package/package.json
CHANGED