Reader settings per login session
Each sign-in keeps its own reader mode, zoom, page tone and skipping once it changes one; sessions that never changed follow the latest set, and a new sign-in starts from the session used last. Settings -> Reading shows every session's state, lets a session follow the latest again or become the latest, and turns the feature off for one shared set. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
03767d5704
commit
e3e57b60bd
9 files changed
+326
-27
No files matched your search
@@ -15,11 +15,61 @@
|
||||
const saved = h('span', { class: 'saved', hidden: true }, 'Saved');
|
||||
shell.titleRow.append(saved);
|
||||
let savedTimer = 0;
|
||||
let refreshSessions = () => {};
|
||||
async function save(patch) {
|
||||
Object.assign(S, (await api('api/settings', patch)).settings);
|
||||
saved.hidden = false;
|
||||
clearTimeout(savedTimer);
|
||||
savedTimer = setTimeout(() => { saved.hidden = true; }, 1500);
|
||||
refreshSessions();
|
||||
}
|
||||
|
||||
// Which login session holds which reader settings (reader-sessions.js on the server).
|
||||
function readerSessionsCard() {
|
||||
const TONE = { original: 'Original', inverted: 'Inverted', sepia: 'Sepia', dim: 'Dim', 'extra-dim': 'Extra dim' };
|
||||
const START = { last: 'last mode', flow: 'always Flow', page: 'always Page', book: 'always Book' };
|
||||
const summary = r => [`${LABEL[r.lastMode] || r.lastMode} (opens: ${START[r.startMode] || r.startMode})`, `tone ${TONE[r.pageTone] || r.pageTone}`,
|
||||
'zoom ' + (Object.keys(r.zoom || {}).length ? Object.entries(r.zoom).map(([k, z]) => `${LABEL[k] || k} ${Math.round(z * 100)}%`).join(', ') : 'fit'),
|
||||
r.skipExtras ? 'skips extras' : null, r.skipCovers ? 'skips covers' : null, r.flowPageGap === false ? 'no Flow gap' : null].filter(Boolean).join(' · ');
|
||||
const state = h('div', {}, h('p', { class: 'muted' }, 'Loading sessions…'));
|
||||
const message = h('span', { class: 'small' });
|
||||
async function act(id, action, current) {
|
||||
try {
|
||||
draw(await api('api/reader-sessions', { id, action }));
|
||||
// This page's controls show this session's settings; reload them when they changed.
|
||||
if (current && action === 'follow') location.reload();
|
||||
} catch (error) { message.textContent = error.message; message.className = 'small error'; }
|
||||
}
|
||||
function draw(o) {
|
||||
const current = o.sessions.find(s => s.current), source = o.sessions.find(s => s.id === o.latestFrom);
|
||||
const latestText = !o.latestFrom ? 'one shared set (changed while per-session settings were off, or never changed)'
|
||||
: source ? (source.current ? 'this session' : `${source.device}, active ${ago(source.seen)}`) : 'a session that has since signed out';
|
||||
const intent = !o.enabled ? 'Off: every session uses the same reader settings, and a change applies everywhere.'
|
||||
: current?.locked ? 'This session has its own reader settings. Changes here stay here and become the starting point for new sessions.'
|
||||
: 'This session follows the latest reader settings. Your first change here gives it its own copy, which stays put when other sessions change theirs.';
|
||||
state.replaceChildren(
|
||||
h('p', {}, h('b', {}, 'Now: '), intent),
|
||||
o.enabled ? h('p', { class: 'muted small' }, 'New sessions start from the latest settings: ', h('b', {}, summary(o.latest)), ` — from ${latestText}.`)
|
||||
: h('p', { class: 'muted small' }, 'Shared settings: ', h('b', {}, summary(o.latest))),
|
||||
h('ul', { class: 'reader-sessions' }, o.sessions.map(s => h('li', {},
|
||||
h('div', {}, h('b', {}, s.device), s.current ? ' (this session)' : '', ' · ',
|
||||
h('span', { class: 'muted small' }, `signed in ${new Date(s.created).toLocaleDateString()}, active ${ago(s.seen)}`)),
|
||||
h('div', { class: 'small' }, !o.enabled ? 'Uses the shared settings' + (s.locked ? ' (its own copy is kept for when this is on again)' : '')
|
||||
: s.locked ? 'Own settings: ' + summary(s.reader) : 'Follows the latest settings'),
|
||||
o.enabled ? h('div', { class: 'row' },
|
||||
s.locked ? h('button', { class: 'btn small', onclick: () => act(s.id, 'follow', s.current) }, 'Follow latest again') : null,
|
||||
s.locked && s.id !== o.latestFrom ? h('button', { class: 'btn small', onclick: () => act(s.id, 'latest', s.current) }, 'Use for new sessions') : null) : null))),
|
||||
message);
|
||||
}
|
||||
refreshSessions = () => api('api/reader-sessions').then(draw, error => { message.textContent = error.message; message.className = 'small error'; });
|
||||
refreshSessions();
|
||||
return h('section', { class: 'card' },
|
||||
h('h2', {}, 'Reader settings per session'),
|
||||
// The controls above switch between this session's set and the shared one, so reload them.
|
||||
h('label', { class: 'check' },
|
||||
h('input', { type: 'checkbox', checked: !!S.readerPerSession, onchange: async e => { await save({ readerPerSession: e.target.checked }); location.reload(); } }),
|
||||
h('span', {}, 'Keep reader settings per session', h('div', { class: 'muted small' }, 'Each sign-in (device or browser) keeps its own reader mode, zoom, tone and the other reader preferences above once you change one there. A new sign-in copies the settings of the session you used last. Turn this off for one shared set.'))),
|
||||
state);
|
||||
}
|
||||
const check = (key, text, hint) => h('label', { class: 'check' },
|
||||
h('input', { type: 'checkbox', checked: !!S[key], onchange: e => save({ [key]: e.target.checked }) }),
|
||||
@@ -90,6 +140,8 @@
|
||||
h('button', { class: 'btn small', onclick: async () => { await save({ zoom: {} }); zoomText.textContent = zooms(); } }, 'Reset to fit'),
|
||||
h('div', { class: 'muted small' }, '100% = the page fits the screen. Change it in the reader with Ctrl + mouse wheel, a pinch, or the + / - keys.'))));
|
||||
|
||||
section('user/reading').append(readerSessionsCard());
|
||||
|
||||
section('user/reading').append(h('section', { class: 'card' },
|
||||
h('h2', {}, 'Library page'),
|
||||
check('continueRow', 'Show "Continue where you left off" at the top',
|
||||
|
||||
Reference in new issue
Block a user