"use client"; import { useEffect, useState } from "react"; import { Moon, Sun } from "lucide-react"; import { Button } from "@/components/ui/Button"; import { DEFAULT_THEME_MODE, THEME_STORAGE_KEY, nextThemeMode, normalizeThemeMode, themeModeLabel, themeToggleLabel, type ThemeMode, } from "@/lib/ui/theme"; function applyThemeMode(mode: ThemeMode) { document.documentElement.dataset.theme = mode; } function readStoredThemeMode(): ThemeMode { try { return normalizeThemeMode(window.localStorage.getItem(THEME_STORAGE_KEY)); } catch { return DEFAULT_THEME_MODE; } } function writeStoredThemeMode(mode: ThemeMode) { try { window.localStorage.setItem(THEME_STORAGE_KEY, mode); } catch { // 存储不可用时仍允许本次页面会话切换主题。 } } export function ThemeToggle() { // 首帧用默认模式(与服务端一致)避免水合不一致;挂载后以 localStorage 校正。 // 可见的图标由 CSS 按 决定(ThemeScript 已在水合前写入),故无首帧闪烁。 const [mode, setMode] = useState(DEFAULT_THEME_MODE); useEffect(() => { // 水合后以 localStorage 为权威源校正(dataset 已由 ThemeScript 同样依据写入,通常一致)。 const initial = readStoredThemeMode(); setMode(initial); applyThemeMode(initial); }, []); const toggle = () => { setMode((current) => { const next = nextThemeMode(current); writeStoredThemeMode(next); applyThemeMode(next); return next; }); }; // 图标显隐交给 CSS(按 data-theme),两个图标始终渲染 → 首帧结构与服务端一致、无闪烁。 // 首帧 label 用默认模式(与服务端一致),挂载后随 mode 校正为正确文案。 return ( ); }