52 lines
2.7 KiB
TypeScript
52 lines
2.7 KiB
TypeScript
import { StrictMode, useCallback, useEffect, useState } from 'react';
|
|
import { createRoot } from 'react-dom/client';
|
|
import { App } from './App';
|
|
import { devSwitch } from './dev/switches';
|
|
import { setLang } from './i18n';
|
|
import { applyPrefs, loadPrefs, type Prefs, resolveLang, savePrefs } from './prefs';
|
|
import { PrefsContext } from './prefs-ctx';
|
|
import { installGlobalHandlers, report } from './lib/crash';
|
|
import { DataProvider } from './store';
|
|
import { TrayCardBridge } from './card/bridge';
|
|
import { ErrorBoundary } from './ui/ErrorBoundary';
|
|
import { ContextMenuProvider } from './ui/ContextMenu';
|
|
import { ExportProvider } from './ui/Exports';
|
|
import './styles.css';
|
|
import './design.css';
|
|
|
|
const systemLang = (): string => (typeof navigator !== 'undefined' ? navigator.language : 'en');
|
|
|
|
|
|
function Root() {
|
|
const [prefs, setPrefs] = useState<Prefs>(() => {
|
|
const p = loadPrefs();
|
|
// Dev builds only: theme=light|dark and lang=en|tr force them (screenshots without clicking).
|
|
const th = devSwitch('theme'), lg = devSwitch('lang');
|
|
if (th === 'light' || th === 'dark') p.theme = th;
|
|
if (lg === 'en' || lg === 'tr') p.lang = lg;
|
|
if (devSwitch('start')) p.onboarded = true;
|
|
return p;
|
|
});
|
|
const lang = resolveLang(prefs.lang, systemLang());
|
|
setLang(lang);
|
|
useEffect(() => { applyPrefs(prefs, document.documentElement, systemLang()); }, [prefs]);
|
|
// React has the window now: public/boot.js stops watching for a blank start
|
|
useEffect(() => { document.documentElement.dataset.booted = '1'; }, []);
|
|
const update = useCallback((p: Prefs) => { setPrefs(p); savePrefs(p); }, []);
|
|
const onTheme = useCallback(() => {
|
|
const dark = prefs.theme === 'dark' || (prefs.theme === 'system' && matchMedia('(prefers-color-scheme: dark)').matches);
|
|
update({ ...prefs, theme: dark ? 'light' : 'dark' });
|
|
}, [prefs, update]);
|
|
// a language change re-renders the whole tree in the new language
|
|
return <PrefsContext.Provider value={{ prefs, update }}><DataProvider><TrayCardBridge /><ExportProvider><ContextMenuProvider><App key={lang} onTheme={onTheme} /></ContextMenuProvider></ExportProvider></DataProvider></PrefsContext.Provider>;
|
|
}
|
|
|
|
installGlobalHandlers();
|
|
createRoot(document.getElementById('root')!, {
|
|
// errors no boundary caught (a boundary's own fallback failing): logged; public/boot.js shows its plain panel
|
|
onUncaughtError: (e, info) => {
|
|
const r = report('react (uncaught)', e, info.componentStack);
|
|
(window as { __storeboundBlank?: (m: string, s?: string) => void }).__storeboundBlank?.(r.message, r.detail);
|
|
},
|
|
onRecoverableError: (e, info) => { report('react (recovered)', e, info.componentStack, 'warn'); },
|
|
}).render(<StrictMode><ErrorBoundary where="app" full><Root /></ErrorBoundary></StrictMode>);
|