Phase 2 of the reader redesign:
- Fixed-layout touch engine (foliate-js e9e61d8): pinch-zoom around
the midpoint, two-finger pan, single-finger pan while zoomed,
horizontal swipe page-turn at fit (RTL-aware via next()/prev()),
and double-tap to zoom 2.5x / reset. Touch events forwarded from
page iframes with converted coordinates; preventDefault only when
the engine consumes the gesture, so PDF text selection and native
taps stay intact. touch-action: none on the host and in comic/pdf
page documents keeps the browser from fighting the engine.
- Tap zones (Kindle-style) for touch devices: tap the outer margins
to page, center to toggle chrome. Size configurable (10-50%) via
the revived tap_zone_size setting; toggle via new tap_zones_enabled
(Behavior section of the settings drawer). Pointer-based + passive
so drags/swipes/selection never trigger; attached both to the
viewport and inside every page document (iframe events don't
bubble); debounced 280ms so double-tap zoom doesn't also page; no
zone actions while a fixed-layout page is zoomed.
- Drawers become full-width sheets on screens <= 640px.
Phase 1 of the reader redesign:
- Reading surface is edge-to-edge; top/bottom bars overlay
translucently (backdrop-blur) instead of reserving insets, killing
the inset-coordination bug class entirely. Chrome auto-hides after
2.5s of pointer inactivity (chrome_behavior setting finally wired:
auto-hide / always-visible; legacy values map to auto-hide). Pointer
activity inside page iframes keeps it awake; Esc toggles.
- TOC / Settings / Bookmarks become slide-over drawers with a scrim
(z-50, full-height, safe-area aware), replacing the dockable-panel
system and its window-shade headers. Only one drawer opens at a
time; Esc or scrim click closes.
- Bottom bar is contextual: reflowable keeps nav/slider/progress/TOC;
fixed-layout row adds Fit Page/Width select, zoom cluster,
magnifier (now shows active state), Double Page Spread toggle, and
a Smart | Pan | Text segmented control replacing the cryptic
two-state icon. Smart = text-aware drag; Text = selection-only
(manual smart-detect off); Pan = force pan. Choice persists via
pdf_interaction_mode (new setting + foliate 29bc958 'text' mode).
- Settings drawer: Behavior (chrome, progress mode), Appearance with
18 Kindle-style theme swatches (single source of truth from
THEME_COLORS), Typography, Layout — each scoped by format.
- Keyboard: t/s/b open TOC/settings/bookmark, Esc closes drawers
before toggling chrome, shortcuts skip form inputs; both slider
rows tracked correctly (no duplicate-ID lookups).
- Topbar: Back, title, add-bookmark, bookmarks drawer, Aa settings;
chrome follows user theme.
The double_page_spread checkbox in the reader settings panel was inert:
it had no Alpine binding, no apply logic, and no persistence. Default
was also inconsistent (false in settings-manager, absent from server
defaults).
- Add doublePageSpread state to the reader Alpine component, loaded
from saved settings (default true)
- Add applyDoublePageSpread() which sets the renderer's 'spread'
attribute to auto/none and persists the setting via saveSettings
- Apply the spread attribute during fixed-layout renderer init
- Bind the settings checkbox with x-model and @change
- Add double_page_spread: true to ReaderService server defaults so
new users get the same starting value the client expects
- Also improve the PDF pan/select toolbar button: distinct smart-
select vs pan icons, highlighted state while pan mode is active,
and dynamic tooltips/aria-labels explaining each mode
- Add Comic Sans MS as a test font option to verify font switching works
- Add !important to background-color and color in getCSS() to prevent
book CSS from overriding user's reading theme colors
- Expand font size range from 12-24px to 10-40px, bump default to 18px
- Expand line height range from 1.0-2.5 to 0.8-3.0
- Add restoreDefaults() method that resets reading theme, font, size,
line height, and justify/hyphenate to sensible defaults
- Add ↩️ restore defaults button in top bar underneath the settings gear
Add explicit light/dark mode toggle switch to the reading theme settings.
The reading mode defaults based on the chrome theme (dark chrome themes
like tokyo-night default to dark reading mode).
- Add readingMode property to readerShell Alpine component
- Add toggleReadingMode() method that toggles dark class on body
- Add detectChromeDarkMode() to infer default from chrome theme
- Update applyTheme() to add/remove dark class and persist reading_mode
- Add toggle switch UI in settings panel (blue pill style, next to
Reading Theme heading)
- Add reading_mode to default settings in settings-manager
Books were failing to load because foliate-js fetches the file URL
without auth headers, getting rejected by JWT middleware. Also,
reading themes were not being applied because getCSS() didn't
inject background/text colors into the book iframe.
- Fetch book file with Bearer token, pass as File (not URL) to
view.open() so foliate-js can detect format via filename extension
- Add reading theme class to body so foliate-themes.css activates
the correct --reader-bg/--reader-text CSS variables
- Update getCSS() to read theme colors from outer page and embed
them in the iframe CSS string (background-color, color, link
color, selection color)
- Fix Alpine.start() deadlock: move call outside the alpine:init
listener so Alpine actually initializes
- Remove unused tocItem from relocate handler destructuring
- Replace apiGet/apiPut with direct fetch in settings-manager to
fix /api prefix mismatch (reader routes are at /readers/*, not
/api/readers/*)
Major rewrite of the web reader to properly interface with
@bookhoard/foliate-js, replacing the abandoned panel-detection
architecture with direct pan and zoom support built into the
foliate-js FixedLayout renderer.
Template (reader.templ):
- Fix critical bug: x-init config was using literal strings
'{ readerData.X }' inside a quoted attribute, which templ
treated as raw text and never interpolated. Values were never
actually passed to JavaScript. Now uses fmt.Sprintf() with
templ's expression attribute syntax ={ }.
- Pass fileUrl from server so foliate-js can open books directly.
- Redesign bottom bar with foliate-js parity: left/right navigation
buttons, progress slider with tick marks, and zoom controls
(zoom out, percentage display, zoom in, magnifier, pan/select
mode toggle for PDFs).
- Remove panel editor button and enablePanelDetection config.
- Add SVG icon styles for consistent reader controls.
Go types (templates/types.go):
- Expand ReaderMetadata with FormatGroup, MangaType,
ReadingDirection, FileURL, and LibraryID fields needed by
the reader frontend.
Router (internal/router/reader.go):
- Populate new ReaderMetadata fields from database values.
- Construct FileURL from library ID and file path for the
/uploads/library-{id}/* file serving route.
Reader JS (reader.ts):
- Full rewrite modeled on foliate-js Reader class, adapted for
Alpine.js. Opens books via view.open(fileUrl), accesses
view.renderer for zoom/pan/navigation, and wires up keyboard
shortcuts (+/-/0 for zoom, arrows for nav, Escape for magnifier).
- Uses view.isFixedLayout instead of importing FixedLayout class,
avoiding a TypeScript module resolution issue with the Vite alias.
Settings manager (settings-manager.ts):
- Remove dependency on deleted ReaderContext event bus.
- Export loadSettings/saveSettings/syncSettings directly as
standalone async functions.
Cleanup:
- Delete reader-context.ts and reader-events.ts (over-engineered
event system replaced by direct function calls).
- Remove panel_zoom_enabled from ReaderSettings type.
Supporting updates for the new page-based pagination architecture:
settings-manager.ts:
- Export loadSettings function for use in reader initialization
- Allow external modules to access user reading preferences
gestures.ts:
- Update import paths for panel detection modules
- Reflect new format-specific directory structure
types/reader.d.ts:
- Add saved_progress field to ReaderMetadata interface
- Include current_page, cfi, and progress fields
- Enable type-safe access to restored reading position
These changes enable the pagination system to access user
settings and properly type restored progress data.
- Export getDefaultSettings function for use in reader-navigation
- Minor formatting improvements in settings-manager.ts
- Add tabindex and ebook-content class to reader-content in template
Complete the Feature Registration Pattern refactoring across all reader
modules. Each feature now exports an init(context) function and uses the
event-based architecture for loose coupling.
## Comic Features (6 files)
- background-color.ts: Background color picker with toggle
- chapter-markers.ts: Visual chapter indicators
- page-cache.ts: 5-page ahead prefetch with cleanup
- page-order.ts: Auto-detect Japanese vs Western order
- page-scrubber.ts: Quick navigation slider
- panel-gap.ts: Adjustable panel gap controls
## Ebook Features (6 files)
- copy-handler.ts: Text copying with citation
- dictionary-popup.ts: Word lookup integration
- font-loader.ts: 8 bundled libre fonts
- search.ts: Full-text search across spine
- typography-engine.ts: Font rendering and hyphenation
## Manga Features (4 files)
- reading-direction.ts: RTL/LTR/vertical detection
- rtl-navigator.ts: Reversed page turn direction
- settings.ts: Webtoon mode and transitions
- vertical-scroll-mode.ts: Infinite scroll with lazy loading
## PDF Features (3 files)
- pdf-navigation.ts: Page turning, zoom, fit modes
- pdf-text-selection.ts: Highlight creation via backend
- annotation-layer.ts: Render highlights and notes
## Root-Level Features (3 files)
- offline-manager.ts: PWA service worker and sync
- reading-speed-tracker.ts: Pages/words per minute tracking
- settings-manager.ts: Per-user settings with localStorage fallback
## Core Infrastructure (1 file)
- parser-manager.ts: Fixed import paths for all parsers
## Key Changes
- All features use init(context) pattern
- Event-based communication via context.events.on/emit
- No direct DOM manipulation in feature exports
- State managed within feature closures
- Clean initialization and teardown
- Zero functionality lost - all features preserved
Total: 23 files converted to unified architecture
- reader-shell.ts: Main initialization, Alpine.js integration, media type detection
- progress-indicator.ts: Reading progress tracking and display components
- settings-manager.ts: User settings persistence and retrieval
- panel-dock-system.ts: Dockable panel management with drag/drop and collapse
- parser-manager.ts: Parser selection and format detection system
These core modules provide the foundation for all reader types with
shared functionality for progress tracking, settings management, and
the flexible panel docking system.