/** * Admin dark mode. Site default comes from the `admin_dark` setting (auto|light|dark); * each user can override it from the header toggle (stored in localStorage). * The theme is applied as data-theme on ; tokens are overridden in admin.scss. */ import { __ } from '@wordpress/i18n'; import { useEffect, useState } from '@wordpress/element'; import { Button } from '@wordpress/components'; import { registerHeaderUtility } from '../ui'; type Mode = 'auto' | 'light' | 'dark'; const KEY = 'streamsera:theme'; function stored(): Mode | null { try { const v = window.localStorage.getItem( KEY ); return v === 'light' || v === 'dark' || v === 'auto' ? v : null; } catch { return null; } } function siteDefault(): Mode { const v = ( window as any ).StreamSeraAdmin?.adminDark; return v === 'light' || v === 'dark' ? v : 'auto'; } export function currentMode(): Mode { return stored() || siteDefault(); } function resolve( mode: Mode ): 'light' | 'dark' { if ( mode !== 'auto' ) { return mode; } return window.matchMedia && window.matchMedia( '(prefers-color-scheme: dark)' ).matches ? 'dark' : 'light'; } export function applyTheme( mode: Mode = currentMode() ) { document.body.setAttribute( 'data-theme', resolve( mode ) ); document.body.setAttribute( 'data-theme-mode', mode ); window.dispatchEvent( new CustomEvent( 'streamsera:theme', { detail: { mode, resolved: resolve( mode ) }, } ) ); } export function setMode( mode: Mode ) { try { if ( mode === siteDefault() ) { window.localStorage.removeItem( KEY ); } else { window.localStorage.setItem( KEY, mode ); } } catch { // Private mode: in-memory only. } applyTheme( mode ); } // Apply immediately so the first paint is already themed. applyTheme(); if ( window.matchMedia ) { window .matchMedia( '(prefers-color-scheme: dark)' ) .addEventListener?.( 'change', () => { if ( currentMode() === 'auto' ) { applyTheme( 'auto' ); } } ); } // Settings page can change the site default without a reload. window.addEventListener( 'streamsera:settings-saved', ( e: any ) => { const v = e?.detail?.admin_dark; const data = ( window as any ).StreamSeraAdmin; if ( ! v || ! data ) { return; } const changed = ( data.adminDark || 'auto' ) !== v; data.adminDark = v; if ( changed ) { // Picking a scheme in Settings is an explicit choice: it replaces this browser's header override. try { window.localStorage.removeItem( KEY ); } catch { // Private mode. } applyTheme( v ); } else if ( ! stored() ) { applyTheme( v ); } } ); const SunIcon = () => ( ); const MoonIcon = () => ( ); function ThemeToggle() { const [ mode, set ] = useState< Mode >( currentMode() ); const [ resolved, setResolved ] = useState( resolve( mode ) ); useEffect( () => { const on = ( e: any ) => { set( e.detail.mode ); setResolved( e.detail.resolved ); }; window.addEventListener( 'streamsera:theme', on ); return () => window.removeEventListener( 'streamsera:theme', on ); }, [] ); const next: Mode = resolved === 'dark' ? 'light' : 'dark'; return ( ); } registerHeaderUtility( { id: 'theme', order: 90, Component: ThemeToggle } );