feat(reader): fullscreen mode; revert stacked volume; fix manga badges + ghost sidebar

Reader fullscreen: the topbar gains a fullscreen toggle (icon state + f
shortcut + help row; f is guarded so Ctrl/Cmd+F stays find-in-page).
Enter requests the Fullscreen API with fallbacks (desktop/Android get a
true full display; iOS Safari has no page-fullscreen API and falls back
to the soft visual mode only). Margins: the foliate paginator's closed
shadow root only yields through its observed 'margin' attribute, so
fullscreen fits a comfortable ratio (6% of the short side, floored at
48px) and floors it by the measured env insets — Android reports true
cutout insets in fullscreen, so portrait phones keep text clear of the
camera band; iOS letterboxes are already env-padded chrome. Fixed-layout
renderers skip it (fxl/webtoon pack pages edge-to-edge by design).
Exits the user makes outside the toggle (Esc, browser UI) sync via
fullscreenchange(+webkit) and restore the ordinary 48px margin; a
position-keeping re-render follows the ResizeObserver re-entry path.
Verified on-device: enter -> true fullscreen + icon swap + camera band
clear, exit -> chrome back, margins restored, position kept.

Volume: revert the stacked panel — the popup returns to the liked shape
(mute button left, horizontal dragger right, dialogue above the strip);
one-line flex restore of the pre-89665fb layout.

Manga badges: MediaBadgeKind makes the library type authoritative for
the comic/manga split — manga volumes ship as fixed_layout scans as
often as comic_archive (and manga_type is unreliable across sources:
unknown/NULL on real rows), so the old comic_archive+manga mapping left
the scan volumes unbadged. Anything in a manga library reads manga;
anything in a comics library reads comic. Verified: /dashboard lands 24
title="Manga" cover badges for the Manga library and the sections API
reports badge=manga for a fixed_layout item.

Ghost sidebar: the >=1024px media block applied body
padding-left:var(--sidebar-w) unconditionally, so pages that never
render the sidebar (logged-out pages, the audio player, the reader
itself) carried a phantom 16rem left margin that pushed content off
center on desktop widths. The body:has(.app-sidebar) rule below was
already the correct conditional source — it is now the only one.
Verified in the built stylesheet: exactly one such rule remains.

Infra: main.js ?v=3 stamp bump (reader.ts changed; hashed chunks churn
their URLs themselves).
This commit is contained in:
John O'Keefe committed 2026-10-15 20:27:37 -04:00
1 parent 89665fb2bc
commit 73ae1789c5
44 files changed
+707 -577

No files matched your search

+9
View File
@@ -279,6 +279,7 @@ templ Reader(user User, metadata ReaderMetadata) {
<div class="help-row"><span>Contents / thumbnails</span><span class="help-keys"><kbd class="kbd">t</kbd></span></div>
<div class="help-row"><span>Annotations drawer</span><span class="help-keys"><kbd class="kbd">a</kbd></span></div>
<div class="help-row"><span>Settings</span><span class="help-keys"><kbd class="kbd">s</kbd></span></div>
<div class="help-row"><span>Fullscreen</span><span class="help-keys"><kbd class="kbd">f</kbd></span></div>
<div class="help-row"><span>Add bookmark</span><span class="help-keys"><kbd class="kbd">b</kbd></span></div>
<div class="help-row"><span>Close menus / toggle chrome</span><span class="help-keys"><kbd class="kbd">Esc</kbd></span></div>
<h4 class="reader-help-section" x-show="isFixedLayout">Zoom &amp; pan <span class="help-note">(comics, PDFs)</span></h4>
@@ -314,6 +315,14 @@ templ ReaderChrome(metadata ReaderMetadata) {
<button @click="toggleBookmarks()" class="p-1.5 sm:p-2 rounded-lg hover:bg-gray-700" title="Annotations (a)">📝</button>
<button @click="toggleSettings()" class="p-1.5 sm:p-2 rounded-lg hover:bg-gray-700" title="Settings (s)">Aa</button>
<button @click="toggleHelp()" class="p-1.5 sm:p-2 rounded-lg hover:bg-gray-700 text-sm font-bold" title="Shortcuts & gestures (?)" aria-label="Shortcuts and gestures">?</button>
<button @click="toggleFullscreen()" class="p-1.5 sm:p-2 rounded-lg hover:bg-gray-700" title="Fullscreen (f)" aria-label="Toggle fullscreen">
<svg x-show="!isFullscreen" class="reader-icon" width="18" height="18" aria-hidden="true">
<path d="M8 3H5a2 2 0 0 0-2 2v3m18 0V5a2 2 0 0 0-2-2h-3m0 18h3a2 2 0 0 0 2-2v-3M3 16v3a2 2 0 0 0 2 2h3"></path>
</svg>
<svg x-show="isFullscreen" x-cloak class="reader-icon" width="18" height="18" aria-hidden="true">
<path d="M8 3v3a2 2 0 0 1-2 2H3m18 0h-3a2 2 0 0 1-2-2V3m0 18v-3a2 2 0 0 1 2-2h3M3 16h3a2 2 0 0 1 2 2v3"></path>
</svg>
</button>
</div>
</div>
</div>