fix(frontend): complete settings MVP structure
Independent Staging Quality Gate / validate (push) Failing after 10m21s
Independent Staging Quality Gate / publish (push) Has been skipped

This commit is contained in:
Jesse_Chen
2026-08-30 16:32:58 +08:00
parent f5e6d51646
commit a671986a1d
7 changed files with 209 additions and 31 deletions
@@ -23,15 +23,44 @@ function OptionIcon({ value }: { readonly value: ThemePreference }) {
return <Monitor aria-hidden="true" />;
}
export function ThemePreferenceMenu() {
// The stored choice is browser state, not React state: read it through the
// external store so the server snapshot stays "system" (no hydration
// mismatch) and a change in another tab lands here too.
const preference = useSyncExternalStore(
function useThemePreference() {
return useSyncExternalStore(
subscribeThemePreference,
readThemePreference,
() => "system" as ThemePreference,
);
}
export function ThemePreferencePanel() {
const preference = useThemePreference();
return (
<section className="theme-preference-panel" aria-labelledby="theme-preference-title">
<div className="section-heading">
<b id="theme-preference-title">外观主题</b>
<small>选择 Jyotisha 的显示方式</small>
</div>
<div className="theme-preference-options" role="group" aria-label="外观主题">
{options.map((option) => (
<button
className="theme-preference-option"
key={option.value}
type="button"
aria-pressed={preference === option.value}
onClick={() => applyThemePreference(option.value)}
>
<OptionIcon value={option.value} />
<span>{option.label}</span>
{preference === option.value ? <Check aria-hidden="true" /> : null}
</button>
))}
</div>
</section>
);
}
export function ThemePreferenceMenu() {
const preference = useThemePreference();
return (
<Menu.Group className="theme-menu">