vipan-kumar's picture
Initial commit: Audio Deepfake Detector with 8 detectors trained on jay15k
e6a1f55
Raw
History Blame Contribute Delete
3.04 kB
import { useEffect, useState } from "react";
import { Moon, Sun } from "lucide-react";
import { cn } from "@/lib/utils";
type Theme = "dark" | "light";
const STORAGE_KEY = "adfd:theme";
/** Read & apply the persisted theme. Used at app boot AND by the toggle. */
export function useTheme() {
const [theme, setTheme] = useState<Theme>(() => {
if (typeof document === "undefined") return "dark";
const stored = window.localStorage.getItem(STORAGE_KEY);
if (stored === "light" || stored === "dark") return stored;
// Fall back to system preference if no override.
return window.matchMedia?.("(prefers-color-scheme: light)").matches
? "light"
: "dark";
});
useEffect(() => {
const root = document.documentElement;
if (theme === "light") {
root.classList.add("theme-light");
root.classList.remove("dark");
} else {
root.classList.add("dark");
root.classList.remove("theme-light");
}
window.localStorage.setItem(STORAGE_KEY, theme);
}, [theme]);
return { theme, setTheme };
}
interface Props {
className?: string;
size?: "sm" | "md";
showLabel?: boolean;
}
/** Visual two-state switch: dark (default) ⇄ light. */
export default function ThemeToggle({
className,
size = "md",
showLabel = true,
}: Props) {
const { theme, setTheme } = useTheme();
const isLight = theme === "light";
const dim = size === "sm" ? "h-7" : "h-8";
const padX = size === "sm" ? "px-1" : "px-1.5";
const knob =
size === "sm" ? "h-5 w-5" : "h-6 w-6";
return (
<div className={cn("inline-flex items-center gap-2", className)}>
{showLabel && (
<span className="font-mono text-[10px] uppercase tracking-wider text-ink-mute">
theme
</span>
)}
<button
type="button"
role="switch"
aria-checked={isLight}
aria-label={isLight ? "Switch to dark mode" : "Switch to light mode"}
onClick={() => setTheme(isLight ? "dark" : "light")}
className={cn(
"relative inline-flex items-center rounded-full border border-line bg-surface-alt transition-colors",
dim,
padX,
"w-[58px]",
isLight && "border-warn/40 bg-warn/15"
)}
>
{/* Sun icon (left) */}
<Sun
className={cn(
"absolute left-1.5 h-3 w-3 transition-opacity",
isLight ? "opacity-100 text-warn" : "opacity-50 text-ink-dim"
)}
/>
{/* Moon icon (right) */}
<Moon
className={cn(
"absolute right-1.5 h-3 w-3 transition-opacity",
isLight ? "opacity-50 text-ink-dim" : "opacity-100 text-cyber"
)}
/>
{/* Knob */}
<span
className={cn(
"z-10 inline-block rounded-full shadow-ridge transition-all duration-200",
knob,
isLight
? "translate-x-[28px] bg-bg"
: "translate-x-0 bg-bg"
)}
/>
</button>
</div>
);
}