团子 58494b8fca feat: React webui with flags support, build-ui pipeline
- packages/webui: Vite + React + Tailwind v4, single-file bundle
- Dark industrial theme, Space Grotesk + JetBrains Mono
- Config table with scope/flag badges, masked values, search/filter
- Admin panel for agent management
- scripts/build-ui.sh generates worker/src/ui.ts from webui build
- Worker serves UI at GET / before auth check
2026-04-21 04:01:35 +00:00

32 lines
1.1 KiB
TypeScript

import { useEffect, useState } from "react";
import type { Toast as ToastType } from "../types";
export default function Toast({ toasts, remove }: { toasts: ToastType[]; remove: (id: number) => void }) {
return (
<div className="fixed top-4 right-4 z-50 flex flex-col gap-2">
{toasts.map((t) => (
<ToastItem key={t.id} toast={t} remove={remove} />
))}
</div>
);
}
function ToastItem({ toast, remove }: { toast: ToastType; remove: (id: number) => void }) {
const [show, setShow] = useState(false);
useEffect(() => {
requestAnimationFrame(() => setShow(true));
const timer = setTimeout(() => { setShow(false); setTimeout(() => remove(toast.id), 300); }, 3000);
return () => clearTimeout(timer);
}, [toast.id, remove]);
return (
<div
className={`px-4 py-3 rounded-lg border text-sm font-medium transition-all duration-300 ${
show ? "opacity-100 translate-x-0" : "opacity-0 translate-x-4"
} ${toast.type === "error" ? "bg-red-500/10 border-red-500/30 text-red-400" : "bg-green-500/10 border-green-500/30 text-green-400"}`}
>
{toast.message}
</div>
);
}