This commit implements true page-based pagination for the ebook reader,
similar to Kindle's approach where content is split into discrete pages
based on viewport size, font settings, and content layout.
Phase 1 - Bug Fixes:
- Fix goToPage to use getScrollPositionForPage instead of treating
page numbers as spine indices
- Move page calculation to initialize before first render to avoid
race condition with scroll handler
- Add fallback page calculation when pageCalculationResult is null
Phase 2 - Page Splitting:
- Add new page-splitter.ts module with CFI-based splitting for EPUB
and height-based fallback for other formats
- Integrate splitContent into page-calculator to generate discrete
page content for each spine item
- Store pages[] array in ChapterPageInfo for rendering
- Rewrite navigation (nextPage, previousPage, renderSpineItem) to
use page-based approach with currentPage tracking
- Remove old scroll-based pagination from view-modes.ts
- Update paginated mode CSS for true page clipping
Phase 3 - Progress Display:
- Update progress-indicator to use currentPage directly instead of
calculating from scroll position
- Fix getCurrentPage in reader-state to return currentPage for ebooks
Phase 4 - Interface Fixes:
- Add currentPage field to UniversalReader interface
- Update sendProgressUpdate to use currentPage directly
- Initialize currentPage to 1 on reader creation
Key features:
- Dynamic page count based on font size, line height, margins
- CFI-based page splitting preserves reading context
- Falls back to height-based splitting for non-EPUB formats
- Progress display updates immediately on page change
- Settings changes trigger page recalculation and re-render
- Add SVG image support: process <image xlink:href=...> elements
in addition to HTML <img> tags for cover pages and embedded images
- Fix progress update: use 'id' from API response instead of
'media_item_id' which the backend never returns for this endpoint
- Update reader context interfaces to include currentPage field
- Add debug logging for all resource keys to help troubleshoot
image loading issues in future epubs
- Add enhanced image path lookup (findImageInResources) that tries multiple
path variations: full path, relative path, filename only, without extension,
and common extensions (.jpg, .jpeg, .gif, .webp, .svg, .png)
- Fix keyboard navigation by focusing container on reader init
- Implement Kindle-style page display using pageCalculationResult for both
currentPage and totalPages instead of raw spine index
- Store computed currentPage in state during scroll for UI display
- Extend progress API to send character offset, chapter index, and percentage
for accurate cross-device sync (backend already supports these fields)
- reader-navigation.ts: add fallback for mediaItemId from page URL
- reader-navigation.ts: use getCurrentPageFromScroll for accurate page tracking
- reader-navigation.ts: use pageCalculationResult.totalPages for total
- reader-services.ts: validate mediaItemId before API call
- reader-services.ts: avoid double body consumption by checking response.ok
- reader-context.ts: fix event type to use ReaderEventType instead of string
- page-calculator.ts: add pagesInChapter property to ChapterPageInfo interface
- reader-navigation.ts: remove unused getScrollPositionForPage import
- Replace spine-only navigation with scroll-based page navigation
- nextPage: scroll down within chapter, only jump to next spine at chapter end
- previousPage: scroll up within chapter, only jump to previous spine at chapter start
- Use viewportHeight - 120 for accurate page height calculation
- Fallback to spine-only navigation when page calculation not available
- 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
- Add pageCalculationResult and currentScrollPosition to UniversalReader type
- Add settings:changed event type for settings change notifications
- Call initializePageCalculation after reader ready in reader-shell.ts
- Add tabindex to reader-content for keyboard navigation focus
- Import getCurrentPageFromScroll for viewport-based page calculation
- Replace spine index with actual calculated page numbers when available
- Fix bug where currentPage was assigned to itself (no-op)
- Add fallback to estimated pages while calculation is in progress
- Add page calculation state and initialization function
- Import page calculator functions for dynamic page tracking
- Update nextPage/previousPage to use getScrollPositionForPage for chapter navigation
- Add scroll tracking in renderSpineItem for real-time page updates
- Emit progressUpdated with dynamic page count instead of spine index
- Fix applyReaderTheme to use classList.add instead of className overwrite
- Remove unused state variables from applyReaderTheme/applyTypography
- Create page-calculator.ts module with viewport-based pagination
- Implement calculatePagesForEbook to measure rendered content
- Add getCurrentPageFromScroll for scroll position to page mapping
- Add getScrollPositionForPage for page to scroll position mapping
- Include calculateProgressPercentage for progress tracking
- Create documentation with full implementation spec
- Add readerReady event listener to progress-indicator.ts to ensure
progress displays correctly when reader finishes loading
- Update bruno environment variable values for testing
- Move bluemonday from indirect to direct dependency in go.mod
- Clean up unused indirect dependencies in go.sum
- Add Feature Registry system in reader-shell.ts to track features
- Update format detection to use format_group with switch statement
- Add manga detection via library_type_name or manga_type fields
- Import and register features via init() functions on load
- Fix parser imports in parser-manager.ts (add missing function imports)
- Simplify epub-parsers and fb2-parser by removing unused imports
- Add init(context) exported function to each feature module
- Store module-level context reference for event handlers
- Fix gestures.ts to use library_type_name instead of library_type
- Features: gestures, keyboard-shortcuts, navigator-panel, offline-manager,
panel-dock-system, progress-indicator, reading-speed-tracker
- Move ReaderMetadata from api.ts to reader.d.ts as global type
- Add format_group field for format detection (reflowable/fixed_layout/comic_archive)
- Add library_type_name for library type (ebooks/comics/manga/pdf)
- Add manga_type for manga-specific reading direction metadata
- Remove duplicate mime_type field and fix typo (unkown -> unknown)
- Add reader.ts as second entry point alongside main.ts in vite.config.ts
- Set assetsDir to empty string to output chunks to /static/ root
- Remove reader-shell import from main.ts (loaded separately via reader.js)
- Update .gitignore to exclude web/static/assets/*.js chunks
- Update Bruno environment with new test IDs
Delete old versions of features that are now in /reader/features/:
- offline-manager.ts (moved to features/)
- reading-speed-tracker.ts (moved to features/)
- panel-dock-system.ts (replaced by converted version in features/)
- navigator-panel.ts (replaced by converted version in features/)
All imports now use the /reader/features/ directory. This eliminates
duplication and makes it clear which files are the active ones.
Create panel-dock-system.ts and navigator-panel.ts in features/
directory with proper init() implementations. Copy offline-manager
and reading-speed-tracker to features/ as well.
Fix bug where context parameter was unused in init() but referenced
throughout the file. Now properly stored as module-level variable
so all gesture handler functions can access it.
Convert view-modes.ts to init(context) pattern with:
- Paginated mode with pagination controls
- Scrolled mode for continuous reading
- Single and double column layouts
- Event-based navigation and mode switching
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
- Delete gestures.ts: replaced by features/gestures.ts
- Delete keyboard-shortcuts.ts: replaced by features/keyboard-shortcuts.ts
- Delete progress-indicator.ts: replaced by features/progress-indicator.ts
- Old files used callback pattern, new files use init(context) pattern
- Import reader/reader-shell to load reader functionality
- Ensures reader JavaScript is included in main.js bundle
- Reader features and Alpine components are now initialized
- Replace callback-based architecture with Feature Registration Pattern
- Add feature registry for modular feature initialization
- Create createNavigationAPI() for unified page/chapter navigation
- Implement renderCurrentPage() for PDF/comic/manga rendering
- Add updateProgress() to sync reading progress to backend
- Support ebook, PDF, comic, and manga readers in single interface
- Remove 600+ lines of callback wiring code
- reader-shell now acts as lightweight orchestrator
- Delegates specific functionality to feature modules
- Parse ebooks using parser-manager with format routing
- Initialize readers based on library type from metadata
- Update pdfjs-wrapper.ts: add initializePDFReader() function
- Configure PDF.js worker, standard fonts, and cmaps
- Load PDF documents and extract metadata
- Implement page rendering with canvas
- Support PDF page navigation and zoom
- Return PDFReader with current page tracking
- Integrates with feature-based reader architecture
- Update copy-handler.ts: export enableContextMenuCopy for text selection
- Update typography-engine.ts: export applyTypography and getFontStack
- Update view-modes.ts: export setViewMode and getCurrentViewMode
- Enable text copying with citation for ebooks
- Add typography engine with font loading and text formatting
- Add view mode support (paginated, scrolled, single/dual column)
- These features integrate with settings-manager for customization
- Add getReaderMetadata() to fetch media item metadata
- Add updateReadingProgress() to sync reading progress to backend
- Export ReaderMetadata and ReadingProgress types
- Enable reader to communicate with Go backend
- Move gestures.ts to features/ with init(context) pattern
- Move keyboard-shortcuts.ts to features/ with init(context) pattern
- Remove callback-based architecture
- Features now subscribe to events via ReaderContext
- Support touch gestures (swipe, tap, double-tap, pinch-to-zoom)
- Support keyboard shortcuts (navigation, zoom, fullscreen, bookmarks)
- Add panel-aware navigation for comics/manga
- Keyboard shortcuts include chapter navigation
- Add reader-context.ts: defines ReaderContext interface and factory
- Add reader-events.ts: event bus for feature communication
- Add reader-state.ts: centralized state management
- Add reader-navigation.ts: unified navigation and rendering API
- Add reader-services.ts: shared services (progress, chapters)
- Establishes foundation for Feature Registration Pattern
- settings.ts: Manga reading settings and configuration
- reading-direction.ts: Right-to-left reading direction support
- vertical-scroll-mode.ts: Webtoon/vertical scroll reading mode
- rtl-navigator.ts: RTL navigation for manga
- panel-detection.service.ts: Main orchestration with OpenCV → ML → Grid fallback chain
- panel-detection.opencv.ts: Edge detection using OpenCV for 80% of comics
- panel-detection.ml.ts: COCO-SSD object detection for irregular layouts
- panel-detector.ts: Unified detector interface
- panel-editor.ts: Manual panel editor UI for user corrections
- panel-ml-detector.ts: TensorFlow.js integration for ML detection
- page-cache.ts: Efficient page caching for large comics
- background-color.ts: Auto-detect comic background color
- chapter-markers.ts: Chapter detection and navigation
- page-scrubber.ts: Fast page scrubbing/thumbnails
- page-order.ts: RTL/LTR page ordering support
- panel-gap.ts: Panel gap detection
- Rename font files from VariableFont_wght/opsz,wght to Variable format
- Add reader-fonts.css with @font-face definitions for 10 serif fonts
- Fonts: Crimson Pro, EB Garamond, Literata, Noto Serif, Source Serif 4
- Each font includes regular and italic variants
Add 8 libre font families optimized for extended reading:
Variable fonts (continuous weight range):
- Literata: Modern book typeface for Google Books
- Crimson Pro: Screen-optimized serif
- Source Serif 4: Adobe's professional serif with optical size axis
- EB Garamond: Classic elegance with smooth italics
Static fonts (multiple weights):
- Libertinus Serif: Academic/technical with excellent math support
- Noto Serif: Maximum language coverage
- Charis SIL: Multilingual specialist with extensive Latin support
- IBM Plex Serif: IBM's corporate serif family
All fonts provided in WOFF2 format for optimal compression. Variable fonts
offer continuous weight ranges (200-900) while static fonts provide specific
weights for predictable rendering. Total footprint: ~7.5MB.
These fonts provide excellent readability for extended reading sessions
across all supported languages and scripts.
- epub-parser.ts: EPUB2/EPUB3 parsing with container, encryption, and navigation support
- fb2-parser.ts: FictionBook 2.0/XML parser with metadata and TOC extraction
- txt-parser.ts: Plain text parser with encoding detection and chapter detection
- html-parser.ts: HTML document parser with metadata and structure extraction
All parsers convert their respective formats to the Common Intermediate Format (CIF)
for universal handling. Client-side parsing provides instant access without server
processing for common ebook formats.
Phase 1 focuses on these client-side parsers. Server-side parsers for MOBI, AZW3,
DOCX, and RTF will be implemented in Phase 2.5.
- html-renderer.ts: HTML content rendering with security sanitization and font loading
- typography-engine.ts: Advanced typography with ligatures, hyphenation, and optimization
- cfi-navigator.ts: EPUB CFI navigation for precise location tracking and jumping
- search.ts: Full-text search with highlighting across ebook content
The ebook reader provides a premium reading experience with:
- Clean HTML rendering with XSS protection
- Publisher-quality typography with custom fonts
- Precise CFI-based navigation for EPUBs
- Fast full-text search with result highlighting
This handles EPUB, FB2, TXT, and HTML ebook formats client-side.
- 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.
Define comprehensive TypeScript interfaces for reader functionality:
- Common Intermediate Format (CIF) for universal ebook representation
- Parser capabilities and format detection types
- Reader metadata and chapter structure interfaces
- Panel layout and dock system types
- Reading progress and settings interfaces
- Dictionary and search functionality types
These types provide the foundation for type-safe reader implementation
across all media types (ebook, comic, manga, pdf).