refactor: 将 WebUI 源码纳入仓库,移除打包产物

- 新增 webui/ 前端源码目录(React+Vite+TypeScript+Tailwind)
- 删除 api/webui/ 中旧的打包静态资源
- 更新 .gitignore 忽略 api/webui/ 构建输出和 webui/node_modules
- 更新 README 增加前端构建说明
This commit is contained in:
程序员阿江(Relakkes)
2026-07-01 23:05:03 +08:00
parent 2ac0716c41
commit 65ddaa8828
68 changed files with 9064 additions and 356 deletions

View File

@@ -0,0 +1,69 @@
import { create } from 'zustand'
type Theme = 'light' | 'dark' | 'system'
interface ThemeState {
theme: Theme
resolvedTheme: 'light' | 'dark'
setTheme: (theme: Theme) => void
}
const THEME_KEY = 'mediacrawler_theme'
function getSystemTheme(): 'light' | 'dark' {
if (typeof window === 'undefined') return 'light'
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
}
function getStoredTheme(): Theme {
if (typeof window === 'undefined') return 'light'
const stored = localStorage.getItem(THEME_KEY) as Theme | null
if (stored && ['light', 'dark', 'system'].includes(stored)) {
return stored
}
return 'light' // Default to light
}
function applyTheme(resolved: 'light' | 'dark') {
const root = document.documentElement
if (resolved === 'dark') {
root.classList.add('dark')
} else {
root.classList.remove('dark')
}
}
function resolveTheme(theme: Theme): 'light' | 'dark' {
return theme === 'system' ? getSystemTheme() : theme
}
// Initialize theme immediately to prevent flash
const initialTheme = getStoredTheme()
const initialResolved = resolveTheme(initialTheme)
if (typeof window !== 'undefined') {
applyTheme(initialResolved)
}
export const useThemeStore = create<ThemeState>((set) => ({
theme: initialTheme,
resolvedTheme: initialResolved,
setTheme: (theme) => {
const resolved = resolveTheme(theme)
localStorage.setItem(THEME_KEY, theme)
applyTheme(resolved)
set({ theme, resolvedTheme: resolved })
},
}))
// Listen for system theme changes
if (typeof window !== 'undefined') {
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', (e) => {
const state = useThemeStore.getState()
if (state.theme === 'system') {
const resolved = e.matches ? 'dark' : 'light'
applyTheme(resolved)
useThemeStore.setState({ resolvedTheme: resolved })
}
})
}