TypeScript 61.9%
HTML 37.2%
SQL 0.7%
1'use client';23import { useSyncExternalStore } from 'react';4import { Moon, Sun } from 'lucide-react';56type Theme = 'light' | 'dark';78const listeners = new Set<() => void>();9function subscribe(cb: () => void) {10 listeners.add(cb);11 const mq = window.matchMedia('(prefers-color-scheme: dark)');12 mq.addEventListener('change', cb);13 return () => {14 listeners.delete(cb);15 mq.removeEventListener('change', cb);16 };17}18function getSnapshot(): Theme {19 const attr = document.documentElement.getAttribute('data-theme');20 if (attr === 'dark' || attr === 'light') return attr;21 return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';22}23function getServerSnapshot(): Theme {24 return 'light';25}26function applyTheme(t: Theme) {27 document.documentElement.setAttribute('data-theme', t);28 try {29 localStorage.setItem('ri-theme', t);30 } catch {31 /* ignore */32 }33 for (const l of listeners) l();34}3536export function ThemeToggle({ className }: { className?: string }) {37 const theme = useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);38 return (39 <button40 type="button"41 aria-label={theme === 'dark' ? 'Switch to light mode' : 'Switch to dark mode'}42 className={className ?? 'inline-flex h-8 w-8 items-center justify-center rounded-md text-muted hover:bg-inset hover:text-fg'}43 onClick={() => applyTheme(theme === 'dark' ? 'light' : 'dark')}44 >45 {theme === 'dark' ? <Sun className="h-4 w-4" /> : <Moon className="h-4 w-4" />}46 </button>47 );48}4950/** Inline script: apply the stored theme before paint to avoid flashes. */51export const THEME_SCRIPT = `(function(){try{var t=localStorage.getItem('ri-theme');if(t==='dark'||t==='light'){document.documentElement.setAttribute('data-theme',t);}}catch(e){}})();`;52