mirror of
https://github.com/sortedcord/omnia.git
synced 2026-07-22 03:52:48 +05:30
feat(gui): Added home background
This commit is contained in:
BIN
apps/gui/public/background.png
Normal file
BIN
apps/gui/public/background.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 59 KiB |
BIN
apps/gui/public/logo-shadow.png
Normal file
BIN
apps/gui/public/logo-shadow.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.4 KiB |
BIN
apps/gui/public/output.webm
Normal file
BIN
apps/gui/public/output.webm
Normal file
Binary file not shown.
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import type { ReactNode } from "react";
|
||||
import { useEffect, type ReactNode } from "react";
|
||||
import { Jersey_25, JetBrains_Mono, Space_Mono } from "next/font/google";
|
||||
import Link from "next/link";
|
||||
import { usePathname } from "next/navigation";
|
||||
@@ -40,22 +40,43 @@ const links = [
|
||||
export default function RootLayout({ children }: { children: ReactNode }) {
|
||||
const pathname = usePathname();
|
||||
|
||||
useEffect(() => {
|
||||
document.title = "Omnia";
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<html lang="en">
|
||||
<body className={`${jersey25.variable} ${jetbrainsMono.variable} ${spaceMono.variable} flex flex-col h-dvh overflow-hidden bg-background text-foreground font-sans`}>
|
||||
<body
|
||||
className={`${jersey25.variable} ${jetbrainsMono.variable} ${spaceMono.variable} flex flex-col h-dvh overflow-hidden bg-background text-foreground font-sans`}
|
||||
>
|
||||
{!pathname?.startsWith("/play") && (
|
||||
<video
|
||||
className="fixed inset-0 w-full h-full object-cover pointer-events-none"
|
||||
src="/output.webm"
|
||||
poster="/background.png"
|
||||
autoPlay
|
||||
loop
|
||||
muted
|
||||
playsInline
|
||||
/>
|
||||
)}
|
||||
<nav className="border-b border-dotted border-border/20 bg-secondary/30 shrink-0">
|
||||
<div className="mx-auto max-w-[800px] px-10 py-3 flex items-center justify-center">
|
||||
<NavigationMenu viewport={false}>
|
||||
<NavigationMenuList>
|
||||
{links.map((link) => {
|
||||
const isActive = pathname === link.href || (link.href !== "/" && pathname?.startsWith(link.href));
|
||||
const isActive =
|
||||
pathname === link.href ||
|
||||
(link.href !== "/" && pathname?.startsWith(link.href));
|
||||
return (
|
||||
<NavigationMenuItem key={link.href}>
|
||||
<NavigationMenuLink asChild active={isActive}>
|
||||
<Link
|
||||
href={link.href}
|
||||
className={`text-foreground no-underline font-medium text-sm p-2 transition-all outline-none ${
|
||||
isActive ? "bg-primary/15 text-primary" : "hover:bg-secondary hover:text-foreground"
|
||||
isActive
|
||||
? "bg-primary/15 text-primary"
|
||||
: "hover:bg-secondary hover:text-foreground"
|
||||
}`}
|
||||
>
|
||||
{link.label}
|
||||
@@ -68,9 +89,7 @@ export default function RootLayout({ children }: { children: ReactNode }) {
|
||||
</NavigationMenu>
|
||||
</div>
|
||||
</nav>
|
||||
<main className="flex-1 flex flex-col min-h-0">
|
||||
{children}
|
||||
</main>
|
||||
<main className="flex-1 flex flex-col min-h-0">{children}</main>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
|
||||
@@ -102,8 +102,8 @@ export function ConfigView() {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex-1 overflow-y-auto w-full">
|
||||
<div className="mx-auto max-w-[800px] px-10 py-12">
|
||||
<div className="flex-1 overflow-y-auto w-full relative">
|
||||
<div className="relative z-10 mx-auto max-w-[800px] px-10 py-12">
|
||||
<h1 className="mb-6 text-headline-lg text-primary animate-fade-in">
|
||||
Configuration
|
||||
</h1>
|
||||
|
||||
@@ -41,14 +41,22 @@ export function DashboardView() {
|
||||
const [error, setError] = useState("");
|
||||
|
||||
const [savedSessions, setSavedSessions] = useState<SimSnapshot[]>([]);
|
||||
const [scenarios, setScenarios] = useState<{ path: string; name: string; description: string }[]>([]);
|
||||
const [scenarios, setScenarios] = useState<
|
||||
{ path: string; name: string; description: string }[]
|
||||
>([]);
|
||||
const [, setProviderInstances] = useState<ModelProviderInstance[]>([]);
|
||||
|
||||
// Modal State
|
||||
const [scenarioForModal, setScenarioForModal] = useState<{ path: string; name: string } | null>(null);
|
||||
const [modalEntities, setModalEntities] = useState<{ id: string; name: string }[]>([]);
|
||||
const [scenarioForModal, setScenarioForModal] = useState<{
|
||||
path: string;
|
||||
name: string;
|
||||
} | null>(null);
|
||||
const [modalEntities, setModalEntities] = useState<
|
||||
{ id: string; name: string }[]
|
||||
>([]);
|
||||
const [loadingEntities, setLoadingEntities] = useState(false);
|
||||
const [selectedEntityForModal, setSelectedEntityForModal] = useState<string>("");
|
||||
const [selectedEntityForModal, setSelectedEntityForModal] =
|
||||
useState<string>("");
|
||||
|
||||
const loadSavedSessions = useCallback(async () => {
|
||||
try {
|
||||
@@ -108,7 +116,8 @@ export function DashboardView() {
|
||||
|
||||
const handleDelete = async (id: string, e: React.MouseEvent) => {
|
||||
e.stopPropagation();
|
||||
if (!confirm("Are you sure you want to delete this simulation session?")) return;
|
||||
if (!confirm("Are you sure you want to delete this simulation session?"))
|
||||
return;
|
||||
setLoading(true);
|
||||
try {
|
||||
const res = await deleteSimulation(id);
|
||||
@@ -118,13 +127,18 @@ export function DashboardView() {
|
||||
await loadSavedSessions();
|
||||
}
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : "Failed to delete session.");
|
||||
setError(
|
||||
err instanceof Error ? err.message : "Failed to delete session.",
|
||||
);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleScenarioClick = async (scenario: { path: string; name: string }) => {
|
||||
const handleScenarioClick = async (scenario: {
|
||||
path: string;
|
||||
name: string;
|
||||
}) => {
|
||||
setScenarioForModal(scenario);
|
||||
setLoadingEntities(true);
|
||||
setSelectedEntityForModal(""); // Reset selection to Spectator
|
||||
@@ -164,204 +178,242 @@ export function DashboardView() {
|
||||
router.push(`/play?simId=${result.snapshot.id}`);
|
||||
} catch (err) {
|
||||
setError(
|
||||
err instanceof Error
|
||||
? err.message
|
||||
: "Failed to start simulation.",
|
||||
err instanceof Error ? err.message : "Failed to start simulation.",
|
||||
);
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex-1 overflow-y-auto w-full">
|
||||
<div className="mx-auto max-w-[800px] px-10 py-12">
|
||||
<div className="animate-fade-in">
|
||||
{/* Centered Big Logo */}
|
||||
<div className="flex flex-col items-center justify-center mb-10 pt-4">
|
||||
<img src="/logo.png" alt="Omnia Logo" className="h-28 object-contain mb-3" />
|
||||
<p className="text-body-md text-muted-foreground font-mono uppercase tracking-widest">Simulation Engine</p>
|
||||
</div>
|
||||
|
||||
{error && (
|
||||
<div className="mb-6 border border-destructive bg-destructive/10 px-3 py-2 text-sm text-destructive">
|
||||
{error}
|
||||
<div className="flex-1 overflow-y-auto w-full relative">
|
||||
<div className="relative z-10 mx-auto max-w-[800px] px-10 py-12">
|
||||
<div className="animate-fade-in">
|
||||
{/* Centered Big Logo */}
|
||||
<div className="flex flex-col items-center justify-center mb-10 pt-4">
|
||||
<img
|
||||
src="/logo-shadow.png"
|
||||
alt="Omnia Logo"
|
||||
className="h-28 object-contain mb-3"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Simulations Section */}
|
||||
<section className="mb-10">
|
||||
<h2 className="text-headline-lg text-primary mb-6 animate-fade-in">Simulations</h2>
|
||||
{loadingData ? (
|
||||
<div className="flex overflow-x-auto gap-6 pb-4 scrollbar-thin scrollbar-thumb-border/20">
|
||||
{Array.from({ length: 3 }).map((_, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="flex-shrink-0 w-72 border border-border/30 bg-card p-5 shadow-sm transition-all flex flex-col justify-between h-[148px]"
|
||||
>
|
||||
<div className="space-y-3">
|
||||
<Skeleton className="h-5 w-2/3" />
|
||||
<Skeleton className="h-4 w-1/2" />
|
||||
<Skeleton className="h-3 w-1/3" />
|
||||
</div>
|
||||
<div className="flex justify-between items-center mt-4">
|
||||
<Skeleton className="h-4 w-16" />
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
) : savedSessions.length === 0 ? (
|
||||
<p className="text-sm italic text-muted-foreground">No saved simulations found. Start a new one below!</p>
|
||||
) : (
|
||||
<div className="flex overflow-x-auto gap-6 pb-4 scrollbar-thin scrollbar-thumb-border/20">
|
||||
{savedSessions.map((s) => (
|
||||
<div
|
||||
key={s.id}
|
||||
onClick={() => handleResume(s.id)}
|
||||
className="flex-shrink-0 w-72 border border-border/30 bg-card p-5 cursor-pointer shadow-sm hover:-translate-y-0.5 hover:shadow-md active:translate-y-0 active:shadow-sm transition-all relative group"
|
||||
>
|
||||
<div className="flex flex-col gap-1 text-sm mb-4">
|
||||
<strong className="text-body-md text-foreground block">{s.scenarioName}</strong>
|
||||
<span className="text-xs text-muted-foreground">
|
||||
Turn {s.turn} · {s.entities.length} entities · {s.status}
|
||||
</span>
|
||||
<span className="text-xs text-muted-foreground/60 font-mono">
|
||||
ID: {s.id.substring(0, 8)}...
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex justify-between items-center mt-4">
|
||||
<span className="text-xs text-primary font-mono uppercase tracking-wider">Resume</span>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="destructive"
|
||||
onClick={(e) => handleDelete(s.id, e)}
|
||||
disabled={loading}
|
||||
title="Delete Session"
|
||||
className="opacity-0 group-hover:opacity-100 transition-opacity"
|
||||
>
|
||||
Delete
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
{error && (
|
||||
<div className="mb-6 border border-destructive bg-destructive/10 px-3 py-2 text-sm text-destructive">
|
||||
{error}
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
|
||||
{/* Scenarios Section */}
|
||||
<section className="mb-10">
|
||||
<h2 className="text-headline-lg text-primary mb-6 animate-fade-in">Scenarios</h2>
|
||||
{loadingData ? (
|
||||
<div className="flex overflow-x-auto gap-6 pb-4 scrollbar-thin scrollbar-thumb-border/20">
|
||||
<Link href="/builder" className="no-underline flex-shrink-0">
|
||||
<div className="w-64 border border-border/30 bg-card p-5 cursor-pointer shadow-sm hover:-translate-y-0.5 hover:shadow-md active:translate-y-0 active:shadow-sm transition-all flex flex-col justify-between h-full min-h-[148px]">
|
||||
<div>
|
||||
<strong className="text-body-md text-foreground block mb-1">Build a scenario</strong>
|
||||
<span className="text-xs text-muted-foreground">Create a custom simulation starting point</span>
|
||||
{/* Simulations Section */}
|
||||
<section className="mb-10">
|
||||
<h2 className="text-headline-lg text-primary mb-6 animate-fade-in">
|
||||
Simulations
|
||||
</h2>
|
||||
{loadingData ? (
|
||||
<div className="flex overflow-x-auto gap-6 pb-4 scrollbar-thin scrollbar-thumb-border/20">
|
||||
{Array.from({ length: 3 }).map((_, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="flex-shrink-0 w-72 border border-border/30 bg-card p-5 shadow-sm transition-all flex flex-col justify-between h-[148px]"
|
||||
>
|
||||
<div className="space-y-3">
|
||||
<Skeleton className="h-5 w-2/3" />
|
||||
<Skeleton className="h-4 w-1/2" />
|
||||
<Skeleton className="h-3 w-1/3" />
|
||||
</div>
|
||||
<div className="flex justify-between items-center mt-4">
|
||||
<Skeleton className="h-4 w-16" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 mt-4 text-xs font-mono uppercase tracking-wider text-primary">
|
||||
<span className="flex items-center justify-center size-5 border border-primary text-primary font-bold text-sm bg-primary/10">+</span>
|
||||
<span>Create New</span>
|
||||
))}
|
||||
</div>
|
||||
) : savedSessions.length === 0 ? (
|
||||
<p className="text-sm italic text-muted-foreground">
|
||||
No saved simulations found. Start a new one below!
|
||||
</p>
|
||||
) : (
|
||||
<div className="flex overflow-x-auto gap-6 pb-4 scrollbar-thin scrollbar-thumb-border/20">
|
||||
{savedSessions.map((s) => (
|
||||
<div
|
||||
key={s.id}
|
||||
onClick={() => handleResume(s.id)}
|
||||
className="flex-shrink-0 w-72 border border-border/30 bg-card p-5 cursor-pointer shadow-sm hover:-translate-y-0.5 hover:shadow-md active:translate-y-0 active:shadow-sm transition-all relative group"
|
||||
>
|
||||
<div className="flex flex-col gap-1 text-sm mb-4">
|
||||
<strong className="text-body-md text-foreground block">
|
||||
{s.scenarioName}
|
||||
</strong>
|
||||
<span className="text-xs text-muted-foreground">
|
||||
Turn {s.turn} · {s.entities.length} entities
|
||||
· {s.status}
|
||||
</span>
|
||||
<span className="text-xs text-muted-foreground/60 font-mono">
|
||||
ID: {s.id.substring(0, 8)}...
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex justify-between items-center mt-4">
|
||||
<span className="text-xs text-primary font-mono uppercase tracking-wider">
|
||||
Resume
|
||||
</span>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="destructive"
|
||||
onClick={(e) => handleDelete(s.id, e)}
|
||||
disabled={loading}
|
||||
title="Delete Session"
|
||||
className="opacity-0 group-hover:opacity-100 transition-opacity"
|
||||
>
|
||||
Delete
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
{Array.from({ length: 3 }).map((_, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="flex-shrink-0 w-64 border border-border/30 bg-card p-5 shadow-sm flex flex-col justify-between h-[148px]"
|
||||
>
|
||||
<div className="space-y-3">
|
||||
<Skeleton className="h-5 w-3/4" />
|
||||
<Skeleton className="h-3 w-1/2" />
|
||||
</div>
|
||||
<div className="mt-4">
|
||||
<Skeleton className="h-4 w-20" />
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex overflow-x-auto gap-6 pb-4 scrollbar-thin scrollbar-thumb-border/20">
|
||||
<Link href="/builder" className="no-underline flex-shrink-0">
|
||||
<div className="w-64 border border-primary bg-primary p-5 cursor-pointer shadow-sm hover:-translate-y-0.5 hover:shadow-md active:translate-y-0 active:shadow-sm transition-all flex flex-col justify-between h-full min-h-[148px]">
|
||||
<div>
|
||||
<strong className="text-body-md text-surface block mb-1">Build a scenario</strong>
|
||||
<span className="text-xs text-surface/80">Create a custom simulation starting point</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 mt-4 text-xs font-mono uppercase tracking-wider text-surface">
|
||||
<span className="flex items-center justify-center size-5 border border-surface text-surface font-bold text-sm bg-surface/20">+</span>
|
||||
<span>Create New</span>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
{scenarios.map((s) => (
|
||||
<ScenarioCard
|
||||
key={s.path}
|
||||
name={s.name}
|
||||
description={s.description}
|
||||
onClick={() => handleScenarioClick(s)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
|
||||
{/* Start Scenario Selection Modal */}
|
||||
{scenarioForModal && (
|
||||
<Dialog open={!!scenarioForModal} onOpenChange={(open) => !open && setScenarioForModal(null)}>
|
||||
<DialogContent className="max-w-[400px]">
|
||||
<DialogHeader className="border-b border-dotted border-border/20 pb-4 mb-2">
|
||||
<DialogTitle>Start Scenario</DialogTitle>
|
||||
<DialogDescription>
|
||||
Choose how you want to interact with <strong>{scenarioForModal.name}</strong>.
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
{/* Scenarios Section */}
|
||||
<section className="mb-10">
|
||||
<h2 className="text-headline-lg text-primary mb-6 animate-fade-in">
|
||||
Scenarios
|
||||
</h2>
|
||||
{loadingData ? (
|
||||
<div className="flex overflow-x-auto gap-6 pb-4 scrollbar-thin scrollbar-thumb-border/20">
|
||||
<Link href="/builder" className="no-underline flex-shrink-0">
|
||||
<div className="w-64 border border-border/30 bg-card p-5 cursor-pointer shadow-sm hover:-translate-y-0.5 hover:shadow-md active:translate-y-0 active:shadow-sm transition-all flex flex-col justify-between h-full min-h-[148px]">
|
||||
<div>
|
||||
<strong className="text-body-md text-foreground block mb-1">
|
||||
Build a scenario
|
||||
</strong>
|
||||
<span className="text-xs text-muted-foreground">
|
||||
Create a custom simulation starting point
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 mt-4 text-xs font-mono uppercase tracking-wider text-primary">
|
||||
<span className="flex items-center justify-center size-5 border border-primary text-primary font-bold text-sm bg-primary/10">
|
||||
+
|
||||
</span>
|
||||
<span>Create New</span>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
{Array.from({ length: 3 }).map((_, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="flex-shrink-0 w-64 border border-border/30 bg-card p-5 shadow-sm flex flex-col justify-between h-[148px]"
|
||||
>
|
||||
<div className="space-y-3">
|
||||
<Skeleton className="h-5 w-3/4" />
|
||||
<Skeleton className="h-3 w-1/2" />
|
||||
</div>
|
||||
<div className="mt-4">
|
||||
<Skeleton className="h-4 w-20" />
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex overflow-x-auto gap-6 pb-4 scrollbar-thin scrollbar-thumb-border/20">
|
||||
<Link href="/builder" className="no-underline flex-shrink-0">
|
||||
<div className="w-64 border border-primary bg-primary p-5 cursor-pointer shadow-sm hover:-translate-y-0.5 hover:shadow-md active:translate-y-0 active:shadow-sm transition-all flex flex-col justify-between h-full min-h-[148px]">
|
||||
<div>
|
||||
<strong className="text-body-md text-surface block mb-1">
|
||||
Build a scenario
|
||||
</strong>
|
||||
<span className="text-xs text-surface/80">
|
||||
Create a custom simulation starting point
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 mt-4 text-xs font-mono uppercase tracking-wider text-surface">
|
||||
<span className="flex items-center justify-center size-5 border border-surface text-surface font-bold text-sm bg-surface/20">
|
||||
+
|
||||
</span>
|
||||
<span>Create New</span>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
{scenarios.map((s) => (
|
||||
<ScenarioCard
|
||||
key={s.path}
|
||||
name={s.name}
|
||||
description={s.description}
|
||||
onClick={() => handleScenarioClick(s)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
|
||||
{loadingEntities ? (
|
||||
<div className="flex items-center gap-2 text-sm text-muted-foreground py-4 justify-center">
|
||||
<Spinner />
|
||||
<span>Loading scenario entities...</span>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex flex-col gap-4 py-4">
|
||||
<div className="flex flex-col gap-2">
|
||||
<label className="text-xs font-semibold uppercase tracking-wider text-muted-foreground font-mono">
|
||||
Simulation Mode / Play as
|
||||
</label>
|
||||
<Select
|
||||
value={selectedEntityForModal}
|
||||
onValueChange={(val) => setSelectedEntityForModal(val || "")}
|
||||
>
|
||||
<SelectTrigger className="w-full">
|
||||
<SelectValue placeholder="-- Run Fully Autonomously --" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectGroup>
|
||||
<SelectItem value="">-- Run Fully Autonomously --</SelectItem>
|
||||
{modalEntities.map((ent) => (
|
||||
<SelectItem key={ent.id} value={ent.id}>
|
||||
Play as {ent.name}
|
||||
{/* Start Scenario Selection Modal */}
|
||||
{scenarioForModal && (
|
||||
<Dialog
|
||||
open={!!scenarioForModal}
|
||||
onOpenChange={(open) => !open && setScenarioForModal(null)}
|
||||
>
|
||||
<DialogContent className="max-w-[400px]">
|
||||
<DialogHeader className="border-b border-dotted border-border/20 pb-4 mb-2">
|
||||
<DialogTitle>Start Scenario</DialogTitle>
|
||||
<DialogDescription>
|
||||
Choose how you want to interact with{" "}
|
||||
<strong>{scenarioForModal.name}</strong>.
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
{loadingEntities ? (
|
||||
<div className="flex items-center gap-2 text-sm text-muted-foreground py-4 justify-center">
|
||||
<Spinner />
|
||||
<span>Loading scenario entities...</span>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex flex-col gap-4 py-4">
|
||||
<div className="flex flex-col gap-2">
|
||||
<label className="text-xs font-semibold uppercase tracking-wider text-muted-foreground font-mono">
|
||||
Simulation Mode / Play as
|
||||
</label>
|
||||
<Select
|
||||
value={selectedEntityForModal}
|
||||
onValueChange={(val) =>
|
||||
setSelectedEntityForModal(val || "")
|
||||
}
|
||||
>
|
||||
<SelectTrigger className="w-full">
|
||||
<SelectValue placeholder="-- Run Fully Autonomously --" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectGroup>
|
||||
<SelectItem value="">
|
||||
-- Run Fully Autonomously --
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectGroup>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
{modalEntities.map((ent) => (
|
||||
<SelectItem key={ent.id} value={ent.id}>
|
||||
Play as {ent.name}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectGroup>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
)}
|
||||
|
||||
<DialogFooter className="gap-2">
|
||||
<Button variant="outline" onClick={() => setScenarioForModal(null)}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button onClick={handleStartFromModal} disabled={loading || loadingEntities}>
|
||||
{loading ? "Starting..." : "Launch"}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
)}
|
||||
<DialogFooter className="gap-2">
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => setScenarioForModal(null)}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
onClick={handleStartFromModal}
|
||||
disabled={loading || loadingEntities}
|
||||
>
|
||||
{loading ? "Starting..." : "Launch"}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user