storebound/apps/desktop/src/main.tsx

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>);