Navigation: diora Reader zeigt die Lesen-Abschnitte in seiner Leiste; Podcast-UI aus diora-android 26f5019
All checks were successful
Build and push Docker image / build (push) Successful in 15s
Test / test (push) Successful in 8m59s
Test / e2e (push) Successful in 2m0s

Auf dem tolino stehen Bücher, Zeitschriften, Nachrichten, Blogs direkt in der
Leiste unten (html.nav-flat, keine Abschnittszeile: auf E-Ink nicht wischen).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
mrwnslz 2026-10-16 14:53:38 +02:00
parent b21f59f4eb
commit a48dca07ea
5 changed files with 32 additions and 15 deletions

View file

@ -1047,22 +1047,33 @@
if (e.key === 'Escape' && readerOpen()) closeReader(); if (e.key === 'Escape' && readerOpen()) closeReader();
}); });
// diora Reader has no tab bar of its own: a small one for books and feeds. // diora Reader has no tab bar of its own: a small one. With the navigation (nav.js) it
// holds the sections of Lesen itself — a row of them to swipe is no good on e-ink.
if (READER_APP && !document.getElementById('nui-tabbar')) { if (READER_APP && !document.getElementById('nui-tabbar')) {
const NAV = window.DioraNav;
const dock = document.createElement('div'); const dock = document.createElement('div');
dock.id = 'nui-dock'; dock.id = 'nui-dock';
dock.innerHTML = '<nav id="nui-tabbar">' + dock.innerHTML = '<nav id="nui-tabbar">' + (NAV
'<button type="button" data-tab="books"><span class="nui-tab-icon">▤</span>Bücher</button>' + ? NAV.sections().map(s => '<button type="button" data-nav="' + s.id + '"><span class="nui-tab-icon">' +
'<button type="button" data-tab="feeds"><span class="nui-tab-icon">☰</span>Feeds</button></nav>'; esc(s.icon) + '</span>' + esc(s.label) + '</button>').join('')
: '<button type="button" data-tab="books"><span class="nui-tab-icon">▤</span>Bücher</button>' +
'<button type="button" data-tab="feeds"><span class="nui-tab-icon">☰</span>Feeds</button>') + '</nav>';
document.body.appendChild(dock); document.body.appendChild(dock);
const mark = () => dock.querySelectorAll('button').forEach(b => const mark = () => dock.querySelectorAll('button').forEach(b => b.classList.toggle('nui-active', NAV
b.classList.toggle('nui-active', typeof _currentTopTab !== 'undefined' && b.dataset.tab === _currentTopTab)); ? b.dataset.nav === NAV.current()
: typeof _currentTopTab !== 'undefined' && b.dataset.tab === _currentTopTab));
dock.addEventListener('click', e => { dock.addEventListener('click', e => {
const b = e.target.closest('button'); const b = e.target.closest('button');
if (!b) return; if (!b) return;
showTab(b.dataset.tab); if (NAV) NAV.go(b.dataset.nav);
else showTab(b.dataset.tab);
window.scrollTo(0, 0); window.scrollTo(0, 0);
}); });
if (NAV) {
document.documentElement.classList.add('nav-flat'); // every section in the bar: no row on top
NAV.onChange(mark);
NAV.barsReady();
}
const orig = window.showTab; const orig = window.showTab;
window.showTab = function (name) { window.showTab = function (name) {
orig.apply(this, arguments); orig.apply(this, arguments);

View file

@ -200,7 +200,9 @@
} }
if (segs) { if (segs) {
const g = cur ? gs.filter(x => x.id === cur.group)[0] : null; const g = cur ? gs.filter(x => x.id === cur.group)[0] : null;
const show = !!g && g.sections.length > 1 && html.classList.contains('nav-bars'); // Not where the bar has every section already (the sidebar, diora Reader's bar).
const show = !!g && g.sections.length > 1 && html.classList.contains('nav-bars') &&
!html.classList.contains('nav-flat') && !html.classList.contains('nui-sidebar');
segs.hidden = !show; segs.hidden = !show;
segs.innerHTML = show ? g.sections.map(id => button(id, byId[id].label, 'nav-seg', current === id)).join('') : ''; segs.innerHTML = show ? g.sections.map(id => button(id, byId[id].label, 'nav-seg', current === id)).join('') : '';
} }
@ -217,7 +219,7 @@
first.parentNode.insertBefore(segs, first); first.parentNode.insertBefore(segs, first);
segs.addEventListener('click', onClick); segs.addEventListener('click', onClick);
} }
if (tabs && !nativeUi()) { if (tabs && !nativeUi() && !readerApp()) { // diora Reader brings its own (feeds.js)
html.classList.add('nav-bars'); // the page's own bar knows the sections html.classList.add('nav-bars'); // the page's own bar knows the sections
tabs.addEventListener('click', onClick); tabs.addEventListener('click', onClick);
} }
@ -265,7 +267,7 @@
// Wide web page: every section is in the bar, the row would repeat it. // Wide web page: every section is in the bar, the row would repeat it.
'@media (min-width: 721px) { html:not(.nui-radio) #nav-segments { display: none; } }', '@media (min-width: 721px) { html:not(.nui-radio) #nav-segments { display: none; } }',
// The sidebar (apps, wide): the sections are in it. // The sidebar (apps, wide): the sections are in it.
'html.nui-sidebar #nav-segments { display: none; }', 'html.nui-sidebar #nav-segments, html.nav-flat #nav-segments { display: none; }',
].join('\n'); ].join('\n');
document.head.appendChild(style); document.head.appendChild(style);
})(); })();

View file

@ -2,7 +2,7 @@
* diora service worker — caches the app shell for offline use. * diora service worker — caches the app shell for offline use.
*/ */
const CACHE = 'diora-v115'; const CACHE = 'diora-v116';
const SHELL = [ const SHELL = [
'/static/css/app.css', '/static/css/app.css',
'/static/js/app/01-core.js', '/static/js/app/01-core.js',

View file

@ -1,4 +1,4 @@
// Copied from diora-android 7b1d500 2026-10-16: web-native/static/native-podcast.js // Copied from diora-android 26f5019 2026-10-16: web-native/static/native-podcast.js
// by tools/sync_podcast_ui.py. Do not edit here: change it in diora-android // by tools/sync_podcast_ui.py. Do not edit here: change it in diora-android
// and copy again. The browser host is static/js/podcasts-web.js. // and copy again. The browser host is static/js/podcasts-web.js.
@ -42,6 +42,9 @@
{id: 'queue', label: 'Podcasts: Warteschlange'}, {id: 'queue', label: 'Podcasts: Warteschlange'},
{id: 'inbox', label: 'Podcasts: Neu'}, {id: 'inbox', label: 'Podcasts: Neu'},
{id: 'radio', label: 'Radio'}, {id: 'radio', label: 'Radio'},
// Sections of the page's navigation (nav.js), where the page has them.
window.DioraNav && DioraNav.sections().some(s => s.id === 'audiobooks') && {id: 'audiobooks', label: 'Hörbücher'},
window.DioraNav && DioraNav.sections().some(s => s.id === 'news') && {id: 'news', label: 'Nachrichten'},
].filter(Boolean); ].filter(Boolean);
function startPage() { function startPage() {
let v = null; let v = null;
@ -2877,9 +2880,10 @@
// One player for podcasts and radio: the radio's own bar steps aside. // One player for podcasts and radio: the radio's own bar steps aside.
document.documentElement.classList.add('nui-one-player'); document.documentElement.classList.add('nui-one-player');
// A "Podcasts" button in the bottom bar, between books and radio. // A "Podcasts" button in the bottom bar, between books and radio (with the page's
// navigation, nav.js, the bar has the podcasts as a section already).
const tabbar = $id('nui-tabbar'); const tabbar = $id('nui-tabbar');
if (tabbar && !tabbar.querySelector('[data-tab="podcasts"]')) { if (tabbar && !window.DioraNav && !tabbar.querySelector('[data-tab="podcasts"]')) {
const b = el('button'); const b = el('button');
b.type = 'button'; b.type = 'button';
b.dataset.tab = 'podcasts'; b.dataset.tab = 'podcasts';

View file

@ -1,4 +1,4 @@
/* Copied from diora-android 7b1d500 2026-10-16: the podcast parts of web-native/static/native.css /* Copied from diora-android 26f5019 2026-10-16: the podcast parts of web-native/static/native.css
(bottom sheet, podcasts) by tools/sync_podcast_ui.py. Do not edit here. (bottom sheet, podcasts) by tools/sync_podcast_ui.py. Do not edit here.
The web's own additions are in podcasts-web.css. */ The web's own additions are in podcasts-web.css. */