feat: chapter chevrons, sticky app frame, and focus mode (0.21.0)
Reader gets chapter-aware chevrons + a persistent app frame + distraction-free focus mode. - Single-mode chevrons (and ArrowLeft/Right + j/k) advance pages within the chapter and fall through to the adjacent chapter at the boundaries. Last page of last chapter / first page of first chapter disables the chevron and silent-no-ops on the keypress. - Continuous-mode gets a fixed bottom bar with prev/next chapter buttons; arrows + j/k jump chapters directly. - `?page=N` and `?page=last` URL query lets the prev-chapter jump land on the previous chapter's last page. - Layout header is fixed at the top; reader nav is sticky just below it; both stay visible while scrolling so reading settings are always reachable. - New "Focus" toggle in the reader nav hides the layout header, reader nav, and bottom chapter bar with smooth 220ms slide animations. Exit via Esc or a small floating Minimize2 button at the top-right (low resting opacity, full on hover). Reset on reader unmount so it doesn't leak to other pages. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
33
frontend/src/lib/reader-fullscreen.svelte.ts
Normal file
33
frontend/src/lib/reader-fullscreen.svelte.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* Cross-component flag for the reader's "hide all chrome" view.
|
||||
*
|
||||
* The reader page toggles this; the root layout reads it to hide the
|
||||
* top app navbar; the reader itself reads it to hide its own nav bar
|
||||
* and bottom chapter bar. CSS handles the slide animations via a
|
||||
* `data-reader-fullscreen` attribute on `<html>` so the entire frame
|
||||
* (layout chrome included) stays synchronised.
|
||||
*
|
||||
* Always reset on reader unmount — letting the flag leak across
|
||||
* navigation would orphan a hidden app navbar on other pages.
|
||||
*/
|
||||
let active = $state(false);
|
||||
|
||||
export const readerFullscreen = {
|
||||
get value() {
|
||||
return active;
|
||||
},
|
||||
set value(v: boolean) {
|
||||
active = v;
|
||||
if (typeof document !== 'undefined') {
|
||||
if (v) document.documentElement.dataset.readerFullscreen = 'true';
|
||||
else delete document.documentElement.dataset.readerFullscreen;
|
||||
}
|
||||
},
|
||||
toggle() {
|
||||
this.value = !active;
|
||||
},
|
||||
/** Force off — call from the reader's onDestroy. */
|
||||
reset() {
|
||||
this.value = false;
|
||||
}
|
||||
};
|
||||
@@ -60,6 +60,13 @@
|
||||
--icon-md: 18px;
|
||||
--icon-lg: 22px;
|
||||
|
||||
/* App-frame heights (fixed-position bars at the top and bottom of
|
||||
the viewport). Used by the layout + reader to reserve content
|
||||
space and animate fullscreen mode. Recomputed once if the
|
||||
header padding/font-size ever changes — keep in sync. */
|
||||
--app-header-h: 60px;
|
||||
--reader-bar-h: 56px;
|
||||
|
||||
--z-dropdown: 10;
|
||||
--z-sticky: 50;
|
||||
--z-modal: 100;
|
||||
|
||||
Reference in New Issue
Block a user