'use strict'; // Two navigation levels; scraper tabs stay within their section, not another menu. const SETTINGS_SECTIONS = [ { id: 'user/reading', group: 'User', title: 'Reading', hint: 'Reader modes, page spacing and zoom.' }, { id: 'user/reading-hours', group: 'User', title: 'Reading hours', hint: 'When this server allows chapter reading.' }, { id: 'user/reading-time', group: 'User', title: 'Reading time & privacy', hint: 'Your clock, Reading Story and personal data.' }, { id: 'user/notifications', group: 'User', title: 'Notifications', hint: 'Chapter alerts and your optional webhook.' }, { id: 'user/account', group: 'User', title: 'Account', hint: 'Your account and password.' }, { id: 'admin/scraper', group: 'Admin', title: 'Scraper', hint: 'Live activity, downloads, update checks and sources.' }, { id: 'admin/peering', group: 'Admin', title: 'Peering', hint: 'Pair servers and manage their permissions.', href: 'peers.html#admin' }, { id: 'admin/peer-catalog', group: 'Admin', title: 'Peer catalogs', hint: 'Approve titles to share and browse peer libraries.', href: 'peer-catalog.html' }, { id: 'admin/library', group: 'Admin', title: 'Server & library', hint: 'Library location, scan status and maintenance.' }, { id: 'admin/users', group: 'Admin', title: 'Users & access', hint: 'Manage accounts, passwords and administrators.' }, { id: 'admin/collections', group: 'Admin', title: 'Collections & metadata', hint: 'Group manga and review their relationships.', href: 'collections.html' }, { id: 'admin/health', group: 'Admin', title: 'Service health', hint: 'Service status, incidents and recovery.', href: 'health.html' }, ]; const SCRAPER_TABS = ['activity', 'queue', 'updates', 'sources']; const READING_DAYS = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']; function readingDayText(schedule, index) { const previous = (index + 6) % 7; const carried = schedule.days[previous].filter(([start, end]) => end < start) .map(([, end]) => `00:00–${end} (from ${READING_DAYS[previous]})`); const starting = schedule.days[index].map(([start, end]) => `${start}–${end}${end < start ? ' (next day)' : ''}`); return [...carried, ...starting].join(' · ') || 'Closed all day'; } function settingsRoute(hash, admin) { const parts = String(hash || '').replace(/^#/, '').split('/'); const candidate = parts.slice(0, 2).join('/'), tab = parts[2]; const section = SETTINGS_SECTIONS.find(item => item.id === candidate && (admin || item.group === 'User')); return { id: section?.id || 'user/reading', tab: SCRAPER_TABS.includes(tab) ? tab : tab === 'hosters' ? 'sources' : 'activity' }; } function settingsShell(me, active = 'user/reading') { const main = $('main'); main.textContent = ''; const nav = h('nav', { class: 'settings-menu', 'aria-label': 'Settings sections' }); const links = new Map(); for (const group of me.admin ? ['User', 'Admin'] : ['User']) { nav.append(h('div', { class: 'settings-menu-group' }, h('h2', {}, group), SETTINGS_SECTIONS.filter(section => section.group === group).map(section => { const link = h('a', { href: section.href || 'settings.html#' + section.id }, section.title); links.set(section.id, link); return link; }))); } const crumb = h('p', { class: 'settings-breadcrumb muted small' }); const title = h('h1', { tabindex: '-1' }); const titleRow = h('div', { class: 'settings-title-row' }, title); const hint = h('p', { class: 'muted settings-hint' }); const content = h('div', { class: 'settings-content' }); main.append(h('div', { class: 'settings-layout' }, nav, h('div', { class: 'settings-workspace' }, crumb, titleRow, hint, content))); function select(id, focus = false) { const section = SETTINGS_SECTIONS.find(item => item.id === id); if (!section || (section.group === 'Admin' && !me.admin)) return; for (const [key, link] of links) { if (key === id) link.setAttribute('aria-current', 'page'); else link.removeAttribute('aria-current'); } crumb.textContent = 'Settings / ' + section.group; title.textContent = section.title; hint.textContent = section.hint; document.title = section.title + ' · Settings · MangaCircus'; if (focus) title.focus({ preventScroll: true }); } select(active); return { content, titleRow, select }; } if (typeof module !== 'undefined') module.exports = { SETTINGS_SECTIONS, SCRAPER_TABS, settingsRoute, readingDayText };