Files
forgetting/awg-ui/src/lib/theme.ts
T
maeneko 5b29993d5d
ci / check (push) Successful in 23m54s
Panel redesign, per-server I1 masking, node minimum version 0.5.0, installer output
- UI: settings tab with sections (server, API keys, Prometheus, audit, migrate,
  about); server settings moved there; master keys table rework (menu, chips,
  version, compact phone cards); three-state node version chip
- I1: random per-server template (DNS / QUIC Initial / STUN) kept in users.db,
  GET/POST /api/masking, change from the master keys banner
- Hub rejects nodes older than 0.5.0 (close 4026), /ui/nodes reports outdated
- install.sh: spinner for long commands (full output in the log), step progress
  bar, cards; installs older than 0.5.0 require a full reinstall with data reset
- Bump to Beta 0.5.0
2026-10-29 22:59:09 +03:00

48 lines
2.1 KiB
TypeScript

// Copyright (c) 2026 Ivan Vasilev
// This source code is licensed under the MIT license found in the
// LICENSE file in the root directory of this source tree.
// Тема панели: светлая, тёмная или как в системе. Одно хранилище на всё приложение —
// переключатель в шапке и вкладка «Настройки» читают и меняют его через useTheme().
import { useSyncExternalStore } from 'react';
import { THEME_KEY } from './shared';
export type ThemeMode = 'light' | 'dark' | 'system';
export type Theme = 'light' | 'dark';
const dark = window.matchMedia('(prefers-color-scheme: dark)');
const listeners = new Set<() => void>();
function readMode(): ThemeMode {
try {
const v = localStorage.getItem(THEME_KEY);
if (v === 'dark' || v === 'system') return v;
} catch { /* не критично */ }
return 'light'; // без сохранённого выбора — светлая, как было до режима «как в системе»
}
let mode = readMode();
const resolved = (): Theme => (mode === 'system' ? (dark.matches ? 'dark' : 'light') : mode);
function apply() {
document.documentElement.setAttribute('data-theme', resolved());
listeners.forEach(l => l());
}
// «Как в системе» следует за сменой темы ОС на лету
dark.addEventListener('change', () => { if (mode === 'system') apply(); });
apply();
export function setThemeMode(next: ThemeMode) {
mode = next;
try { localStorage.setItem(THEME_KEY, next); } catch { /* не критично */ }
apply();
}
const subscribe = (l: () => void) => { listeners.add(l); return () => { listeners.delete(l); }; };
// mode — выбор пользователя, theme — что реально применено.
export function useTheme(): { mode: ThemeMode; theme: Theme } {
const snap = useSyncExternalStore(subscribe, () => `${mode}:${resolved()}`);
const [m, t] = snap.split(':') as [ThemeMode, Theme];
return { mode: m, theme: t };
}