Bücher: "Zuletzt gelesen" wieder als normale Listenansicht statt Karten-Leiste (SW v40)
All checks were successful
Build and push Docker image / build (push) Successful in 16s
Test / test (push) Successful in 24s

Bleibt an der gleichen Stelle (fest oben, unabhängig von Ordner-Navigation und
Lesefilter), nutzt jetzt aber dieselben Listeneinträge wie der Rest der Ansicht
(inkl. Drei-Punkte-Menü) statt der horizontal scrollbaren Karten.

Da ein Buch dadurch gleichzeitig oben bei "Zuletzt gelesen" und unten in der
Ordner-/Gesamtliste gerendert werden kann, darf das Drei-Punkte-Menü nicht mehr
über eine (dann doppelt vergebene) id gesucht werden — toggleBookMenu bekommt
jetzt den geklickten Button und findet sein Menü relativ dazu im DOM.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011j4LcoeddwFt6Rw8Wyaknj
This commit is contained in:
marwin 2026-08-17 15:37:09 +02:00
parent 635fd7ef1c
commit 0de6c186fb
3 changed files with 22 additions and 51 deletions

View file

@ -1658,50 +1658,21 @@ body.dnd-mode .timer-display {
cursor: pointer; cursor: pointer;
} }
/* --- Recently-read shelf (always pinned atop the books view) --- */ /* --- Recently-read section (always pinned atop the books view, plain list style) --- */
.book-recent-shelf { .book-recent-section {
display: flex;
flex-direction: column;
gap: 6px;
margin-bottom: 14px; margin-bottom: 14px;
padding-bottom: 14px;
border-bottom: 1px solid var(--border);
} }
.book-recent-title { .book-recent-title {
font-size: 13px; font-size: 13px;
font-weight: 600; font-weight: 600;
margin: 0 0 6px; margin: 0;
color: var(--muted, #888); color: var(--muted, #888);
} }
.book-recent-row {
display: flex;
gap: 8px;
overflow-x: auto;
padding-bottom: 2px;
}
.book-recent-item {
flex: 0 0 auto;
display: flex;
flex-direction: column;
gap: 2px;
max-width: 160px;
padding: 8px 10px;
border: 1px solid var(--border);
border-radius: var(--radius);
background: none;
color: var(--fg);
text-align: left;
cursor: pointer;
font: inherit;
}
.book-recent-item-title {
font-size: 13px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.book-recent-item-author {
font-size: 11px;
color: var(--muted, #888);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
/* --- Book folders (single level) --- */ /* --- Book folders (single level) --- */
.book-folder-grid { .book-folder-grid {

View file

@ -3461,8 +3461,8 @@ function _renderBookItemHtml(b) {
<div class="book-item-actions"> <div class="book-item-actions">
<button class="btn btn-sm" onclick="openBook(${b.id})"${b.keyOk === false ? ' disabled title="Import the correct encryption key first"' : ''}>Open</button> <button class="btn btn-sm" onclick="openBook(${b.id})"${b.keyOk === false ? ' disabled title="Import the correct encryption key first"' : ''}>Open</button>
<div class="book-item-menu"> <div class="book-item-menu">
<button class="btn btn-sm book-menu-toggle" title="Weitere Optionen" onclick="toggleBookMenu(${b.id})"></button> <button class="btn btn-sm book-menu-toggle" title="Weitere Optionen" onclick="toggleBookMenu(this)"></button>
<div class="book-item-menu-list" id="book-menu-${b.id}"> <div class="book-item-menu-list">
${broken ? `<button class="book-menu-item book-menu-item--danger book-broken-btn" onclick="repairBook(${b.id})">🔧 Reparieren (Datei erneut hochladen)</button>` : ''} ${broken ? `<button class="book-menu-item book-menu-item--danger book-broken-btn" onclick="repairBook(${b.id})">🔧 Reparieren (Datei erneut hochladen)</button>` : ''}
${b.has_highlights ? `<button class="book-menu-item" onclick="downloadBookAnnotationsFromList(${b.id}, this)">⭳ Markierungen &amp; Notizen herunterladen</button>` : ''} ${b.has_highlights ? `<button class="book-menu-item" onclick="downloadBookAnnotationsFromList(${b.id}, this)">⭳ Markierungen &amp; Notizen herunterladen</button>` : ''}
<button class="book-menu-item" onclick="assignBookFolder(${b.id})">📁 Ordner zuweisen</button> <button class="book-menu-item" onclick="assignBookFolder(${b.id})">📁 Ordner zuweisen</button>
@ -3475,8 +3475,11 @@ function _renderBookItemHtml(b) {
</div>`; </div>`;
} }
function toggleBookMenu(bookId) { function toggleBookMenu(toggleBtn) {
const menu = document.getElementById(`book-menu-${bookId}`); // Looked up relative to the clicked button (not by id) because the same book — and
// thus the same rendered menu — can appear twice at once: once in the "Zuletzt
// gelesen" section and again further down in the folder/unfiled list.
const menu = toggleBtn.closest('.book-item-menu')?.querySelector('.book-item-menu-list');
if (!menu) return; if (!menu) return;
const willOpen = !menu.classList.contains('open'); const willOpen = !menu.classList.contains('open');
document.querySelectorAll('.book-item-menu-list.open').forEach(m => m.classList.remove('open')); document.querySelectorAll('.book-item-menu-list.open').forEach(m => m.classList.remove('open'));
@ -3507,18 +3510,15 @@ function renderBookList(books) {
let html = ''; let html = '';
// Always-visible "recently read" shelf, independent of folder navigation and the // Always-visible "recently read" section, independent of folder navigation and the
// read/unread filter below — it's a shortcut back into whatever was open last. // read/unread filter below — it's a shortcut back into whatever was open last. Same
// vertical list-item look as the rest of the list (not a card shelf).
const recent = _recentlyReadBooks(books); const recent = _recentlyReadBooks(books);
if (recent.length) { if (recent.length) {
html += '<div class="book-recent-shelf">' html += '<div class="book-recent-section">'
+ '<h3 class="book-recent-title">Zuletzt gelesen</h3>' + '<h3 class="book-recent-title">Zuletzt gelesen</h3>'
+ '<div class="book-recent-row">' + recent.map(_renderBookItemHtml).join('')
+ recent.map(b => `<button class="book-recent-item" onclick="openBook(${b.id})" title="${escapeHtml(b.title)}"> + '</div>';
<span class="book-recent-item-title">${escapeHtml(b.title)}</span>
${b.author ? `<span class="book-recent-item-author">${escapeHtml(b.author)}</span>` : ''}
</button>`).join('')
+ '</div></div>';
} }
const visible = _bookShowRead ? books : books.filter(b => !b.is_read); const visible = _bookShowRead ? books : books.filter(b => !b.is_read);

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-v39'; const CACHE = 'diora-v40';
const PODCAST_CACHE = 'diora-podcast-v1'; const PODCAST_CACHE = 'diora-podcast-v1';
const SHELL = [ const SHELL = [
'/static/css/app.css', '/static/css/app.css',