/* ===== Main app: shell, routing, state, tweaks ===== */
const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
"accent": "#6243EB",
"theme": "light",
"density": "regular",
"fontPair": "modern",
"rail": false
}/*EDITMODE-END*/;
const FONT_PAIRS = {
modern: { display: "'Inter'", ui: "'Inter'" },
geometric: { display: "'Inter'", ui: "'Inter'" },
playful: { display: "'Inter'", ui: "'Inter'" }
};
const NAV_MAIN = [
{ id: 'dashboard', label: 'Dashboard', icon: 'dashboard' },
{ id: 'browse', label: 'Library', icon: 'library' },
{ id: 'collections', label: 'Collections', icon: 'collections' },
{ id: 'favorites', label: 'Favorites', icon: 'heart' },
{ id: 'manage', label: 'My Sections', icon: 'folder' }
];
function App() {
const [t, setTweak] = useTweaks(TWEAK_DEFAULTS);
const [route, setRoute] = useState({ view: 'dashboard' });
const [search, setSearch] = useState('');
const [favs, setFavs] = useState(() => {
var v = window.Store.get('favs'); return Array.isArray(v) ? v : [];
});
const [catsOpen, setCatsOpen] = useState(() => {
var v = window.Store.get('catsOpen'); return v === undefined ? true : !!v;
});
useEffect(() => { window.Store.set('catsOpen', catsOpen); }, [catsOpen]);
const [profile, setProfileState] = useState(() => {
var def = { name: 'Ashraf Hossain', email: 'ashraf@sectionly.app', role: 'Frontend Developer', company: 'Sectionly', bio: 'Building beautiful frontends, one section at a time.', avatar: 'app/assets/ashraf.png',
notif: { product: true, weekly: true, mentions: true, marketing: false }, twoFactor: false };
var saved = window.Store.get('profile'); return Object.assign(def, saved && typeof saved === 'object' ? saved : {});
});
const setProfile = useCallback((patch) => {
setProfileState(prev => { var next = typeof patch === 'function' ? patch(prev) : Object.assign({}, prev, patch); window.Store.set('profile', next); return next; });
}, []);
const searchRef = useRef(null);
const [notifOpen, setNotifOpen] = useState(false);
const notifRef = useRef(null);
useEffect(() => {
if (!notifOpen) return;
const onDoc = (e) => { if (notifRef.current && !notifRef.current.contains(e.target)) setNotifOpen(false); };
const onKey = (e) => { if (e.key === 'Escape') setNotifOpen(false); };
document.addEventListener('mousedown', onDoc);
document.addEventListener('keydown', onKey);
return () => { document.removeEventListener('mousedown', onDoc); document.removeEventListener('keydown', onKey); };
}, [notifOpen]);
const [version, setVersion] = useState(0);
const refresh = useCallback(() => setVersion(v => v + 1), []);
const [toast, setToast] = useState(null);
const toastT = useRef(null);
const notify = useCallback((msg, error) => {
setToast({ msg: msg, error: !!error });
if (toastT.current) clearTimeout(toastT.current);
toastT.current = setTimeout(() => setToast(null), 2400);
}, []);
useEffect(() => { window.Store.set('favs', favs); }, [favs]);
const toggleFav = useCallback((id) => {
setFavs(prev => prev.includes(id) ? prev.filter(x => x !== id) : [...prev, id]);
}, []);
const nav = useCallback((r) => {
if (r.view !== 'browse') setSearch('');
setRoute(r);
const el = document.querySelector('.content'); if (el) el.scrollTop = 0;
}, []);
/* cmd+k focuses search */
useEffect(() => {
const onKey = (e) => {
if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === 'k') {
e.preventDefault(); searchRef.current && searchRef.current.focus();
}
};
window.addEventListener('keydown', onKey);
return () => window.removeEventListener('keydown', onKey);
}, []);
const onSearch = (v) => {
setSearch(v);
if (v && route.view !== 'browse') setRoute({ view: 'browse' });
};
const fp = FONT_PAIRS[t.fontPair] || FONT_PAIRS.modern;
const rootStyle = {
'--accent': t.accent,
'--accent-press': 'color-mix(in srgb, ' + t.accent + ' 78%, #000)',
'--font-display': fp.display + ", 'Inter', sans-serif",
'--font-ui': fp.ui + ", -apple-system, system-ui, sans-serif"
};
const cls = 'app theme-' + t.theme + ' density-' + t.density + (t.rail ? ' rail-collapsed' : '');
const activeNav = route.view === 'category' ? 'browse' : route.view;
const activeCat = route.view === 'category' ? route.catId : null;
let body;
if (route.view === 'dashboard') body =
No notifications yet
You’re all caught up. New activity will show up here.