diff --git a/src/components/ui/BrandIcon.tsx b/src/components/ui/BrandIcon.tsx
new file mode 100644
index 0000000..ba71386
--- /dev/null
+++ b/src/components/ui/BrandIcon.tsx
@@ -0,0 +1,39 @@
+'use client';
+
+import { cn } from '@/lib/utils';
+
+interface BrandIconProps {
+ size?: 'sm' | 'md' | 'lg' | 'xl';
+ className?: string;
+}
+
+const sizeMap = {
+ sm: 'w-6 h-6',
+ md: 'w-8 h-8',
+ lg: 'w-12 h-12',
+ xl: 'w-16 h-16',
+};
+
+export function BrandIcon({ size = 'md', className }: BrandIconProps) {
+ return (
+
+ );
+}
diff --git a/src/components/ui/ThemeToggle.tsx b/src/components/ui/ThemeToggle.tsx
new file mode 100644
index 0000000..273d146
--- /dev/null
+++ b/src/components/ui/ThemeToggle.tsx
@@ -0,0 +1,57 @@
+'use client';
+
+import { useState, useEffect } from 'react';
+import { Moon, Sun } from 'lucide-react';
+import { cn } from '@/lib/utils';
+
+interface ThemeToggleProps {
+ className?: string;
+}
+
+export function ThemeToggle({ className }: ThemeToggleProps) {
+ const [isDark, setIsDark] = useState(false);
+ const [mounted, setMounted] = useState(false);
+
+ useEffect(() => {
+ setMounted(true);
+ // 从 localStorage 或 document 获取当前主题
+ const currentTheme = document.documentElement.getAttribute('data-theme');
+ const savedTheme = localStorage.getItem('theme');
+ const theme = currentTheme || savedTheme || 'light';
+ setIsDark(theme === 'dark');
+ }, []);
+
+ const toggleTheme = () => {
+ const newTheme = isDark ? 'light' : 'dark';
+ setIsDark(!isDark);
+ document.documentElement.setAttribute('data-theme', newTheme);
+ localStorage.setItem('theme', newTheme);
+ };
+
+ // 避免服务端渲染不匹配
+ if (!mounted) {
+ return (
+
+ );
+ }
+
+ return (
+
+ );
+}
diff --git a/src/components/ui/Toast.tsx b/src/components/ui/Toast.tsx
new file mode 100644
index 0000000..e80c54c
--- /dev/null
+++ b/src/components/ui/Toast.tsx
@@ -0,0 +1,30 @@
+'use client';
+
+import { Toaster as SonnerToaster } from 'sonner';
+
+export function Toaster() {
+ return (
+
+ );
+}
+
+// 导出 toast 函数以便在组件中使用
+export { toast } from 'sonner';