import { Accordion, AccordionItem } from '@heroui/accordion' import { Card, CardBody, CardHeader } from '@heroui/card' import { useRequest } from 'ahooks' import clsx from 'clsx' import { useEffect, useRef } from 'react' import { Controller, useForm, useWatch } from 'react-hook-form' import toast from 'react-hot-toast' import { FaUserAstronaut } from 'react-icons/fa' import { FaPaintbrush } from 'react-icons/fa6' import { IoIosColorPalette } from 'react-icons/io' import { MdDarkMode, MdLightMode } from 'react-icons/md' import themes from '@/const/themes' import ColorPicker from '@/components/ColorPicker' import SaveButtons from '@/components/button/save_buttons' import PageLoading from '@/components/page_loading' import { colorKeys, generateTheme, loadTheme } from '@/utils/theme' import WebUIManager from '@/controllers/webui_manager' export type PreviewThemeCardProps = { theme: ThemeInfo onPreview: () => void } const values = [ '', '-50', '-100', '-200', '-300', '-400', '-500', '-600', '-700', '-800', '-900' ] const colors = [ 'primary', 'secondary', 'success', 'danger', 'warning', 'default' ] function PreviewThemeCard({ theme, onPreview }: PreviewThemeCardProps) { const style = document.createElement('style') style.innerHTML = generateTheme(theme.theme, theme.name) const cardRef = useRef(null) useEffect(() => { document.head.appendChild(style) return () => { document.head.removeChild(style) } }, []) return (
{theme.name}
{theme.author ?? '未知'}
{theme.description}
{colors.map((color) => (
{color[0].toUpperCase()}
{values.map((value) => (
))}
))}
) } const ThemeConfigCard = () => { const { data, loading, error, refreshAsync } = useRequest( WebUIManager.getThemeConfig ) const { control, handleSubmit: handleOnebotSubmit, formState: { isSubmitting }, setValue: setOnebotValue } = useForm<{ theme: ThemeConfig }>({ defaultValues: { theme: { dark: {}, light: {} } } }) // 使用 useRef 存储 style 标签引用 const styleTagRef = useRef(null) // 在组件挂载时创建 style 标签,并在卸载时清理 useEffect(() => { const styleTag = document.createElement('style') document.head.appendChild(styleTag) styleTagRef.current = styleTag return () => { if (styleTagRef.current) { document.head.removeChild(styleTagRef.current) } } }, []) const theme = useWatch({ control, name: 'theme' }) const reset = () => { if (data) setOnebotValue('theme', data) } const onSubmit = handleOnebotSubmit(async (data) => { try { await WebUIManager.setThemeConfig(data.theme) toast.success('保存成功') loadTheme() } catch (error) { const msg = (error as Error).message toast.error(`保存失败: ${msg}`) } }) const onRefresh = async () => { try { await refreshAsync() toast.success('刷新成功') } catch (error) { const msg = (error as Error).message toast.error(`刷新失败: ${msg}`) } } useEffect(() => { reset() }, [data]) useEffect(() => { if (theme && styleTagRef.current) { const css = generateTheme(theme) styleTagRef.current.innerHTML = css } }, [theme]) if (loading) return if (error) return (
{error.message}
) return ( <> 主题配置 - NapCat WebUI
实时预览,记得保存!
} >
{themes.map((theme) => ( { setOnebotValue('theme', theme.theme) }} /> ))}
} >
{(['dark', 'light'] as const).map((mode) => (

{mode === 'dark' ? ( ) : ( )} {mode === 'dark' ? '夜间模式主题' : '白昼模式主题'}

{colorKeys.map((key) => (
{ const hslArray = value?.split(' ') ?? [0, 0, 0] const color = `hsl(${hslArray[0]}, ${hslArray[1]}, ${hslArray[2]})` return ( { onChange( `${result.hsl.h} ${result.hsl.s * 100}% ${result.hsl.l * 100}%` ) }} /> ) }} />
))}
))}
) } export default ThemeConfigCard