diff --git a/src/components/mode-toggle.tsx b/src/components/mode-toggle.tsx
new file mode 100644
index 0000000..6490f27
--- /dev/null
+++ b/src/components/mode-toggle.tsx
@@ -0,0 +1,37 @@
+import { Moon, Sun } from "lucide-react";
+
+import { Button } from "@/components/ui/button";
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuItem,
+ DropdownMenuTrigger,
+} from "@/components/ui/dropdown-menu";
+import { useTheme } from "@/components/theme-provider";
+
+export function ModeToggle() {
+ const { setTheme } = useTheme();
+
+ return (
+
+
+
+
+
+ setTheme("light")}>
+ Light
+
+ setTheme("dark")}>
+ Dark
+
+ setTheme("system")}>
+ System
+
+
+
+ );
+}
diff --git a/src/components/theme-provider.tsx b/src/components/theme-provider.tsx
new file mode 100644
index 0000000..75eeffc
--- /dev/null
+++ b/src/components/theme-provider.tsx
@@ -0,0 +1,74 @@
+import { createContext } from "preact";
+import { useContext, useEffect, useState } from "preact/hooks";
+
+type Theme = "dark" | "light" | "system";
+
+type ThemeProviderProps = {
+ children: preact.VNode;
+ defaultTheme?: Theme;
+ storageKey?: string;
+};
+
+type ThemeProviderState = {
+ theme: Theme;
+ setTheme: (theme: Theme) => void;
+};
+
+const initialState: ThemeProviderState = {
+ theme: "system",
+ setTheme: () => null,
+};
+
+const ThemeProviderContext = createContext(initialState);
+
+export function ThemeProvider({
+ children,
+ defaultTheme = "system",
+ storageKey = "vite-ui-theme",
+ ...props
+}: ThemeProviderProps) {
+ const [theme, setTheme] = useState(
+ () => (localStorage.getItem(storageKey) as Theme) || defaultTheme
+ );
+
+ useEffect(() => {
+ const root = window.document.documentElement;
+
+ root.classList.remove("light", "dark");
+
+ if (theme === "system") {
+ const systemTheme = window.matchMedia("(prefers-color-scheme: dark)")
+ .matches
+ ? "dark"
+ : "light";
+
+ root.classList.add(systemTheme);
+ return;
+ }
+
+ root.classList.add(theme);
+ }, [theme]);
+
+ const value = {
+ theme,
+ setTheme: (theme: Theme) => {
+ localStorage.setItem(storageKey, theme);
+ setTheme(theme);
+ },
+ };
+
+ return (
+
+ {children}
+
+ );
+}
+
+export const useTheme = () => {
+ const context = useContext(ThemeProviderContext);
+
+ if (context === undefined)
+ throw new Error("useTheme must be used within a ThemeProvider");
+
+ return context;
+};
diff --git a/src/main.tsx b/src/main.tsx
index 1cd9d59..ba96249 100644
--- a/src/main.tsx
+++ b/src/main.tsx
@@ -5,6 +5,7 @@ import { App } from "@/pages/App";
import { Tr, langCodeContext, LANG_OPTIONS } from "@/translate";
import { SidebarProvider } from "@/components/ui/sidebar";
import { Toaster } from "@/components/ui/toaster";
+import { ThemeProvider } from "@/components/theme-provider";
function Base() {
const [langCode, _setLangCode] = useState("en-US");
@@ -47,11 +48,14 @@ function Base() {
return (
/* @ts-ignore */
+
-
-
-
-
+
+
+
+
+
+
);
}
diff --git a/src/pages/App.tsx b/src/pages/App.tsx
index 00fb6da..9f9f061 100644
--- a/src/pages/App.tsx
+++ b/src/pages/App.tsx
@@ -71,6 +71,7 @@ import {
CogIcon,
} from "lucide-react";
import { Badge } from "@/components/ui/badge";
+import { ModeToggle } from "@/components/mode-toggle";
export function App() {
// init selected index
@@ -396,7 +397,7 @@ export function App() {
-
+