refactor(reader): create modular format-specific architecture
Implement complete modularization of reader code by separating format-specific functionality into dedicated modules. This replaces the monolithic structure with a clean, maintainable architecture that separates concerns by format type. ## New Architecture ### Format-Specific Modules - **formats/reflowable/**: EPUB, FB2, TXT, HTML (page-based pagination) - types.ts: Shared type definitions for reflowable formats - page-calculator.ts: Word-count based pagination with HTML slicing - navigation.ts: Page-based navigation logic - progress-tracker.ts: CFI-based progress tracking - content-renderer.ts: DOM rendering for page content - parser.ts: Unified parser interface for all reflowable formats - ebook/**: Migrated ebook-specific features - **formats/pdf/**: PDF format support - Core PDF functionality (navigation, text selection, annotations) - Advanced features (bookmarks, search, outlines, dual-page) - Page cache and rendering optimizations - **formats/comic/**: Comic format support - Background color, chapter markers, page caching - Page ordering, gap adjustments - **formats/manga/**: Manga format support - RTL navigation, vertical scrolling, reading direction ## Key Improvements 1. **Separation of Concerns**: Each format has its own dedicated module 2. **No Circular Dependencies**: Clean import structure 3. **Type Safety**: Comprehensive TypeScript types throughout 4. **Functional Programming**: Pure functions, no OOP complexity 5. **Scalability**: Easy to add new formats without touching core code ## Migration Path - Old format-specific code in reader/, ebook/, pdf/, comic/, manga/ - New code in formats/[format]/ structure - Maintains backward compatibility during transition - Core reader logic remains format-agnostic This change enables the implementation of page-based pagination for reflowable formats while keeping PDF, comic, and manga functionality unchanged.
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
// Import types
|
||||
import type { ReflowableBook, ReadingPosition } from "./types";
|
||||
import { findPageByCFI, createPositionFromPage } from "./page-calculator";
|
||||
|
||||
// Update current position
|
||||
export function updateCurrentPosition(
|
||||
book: ReflowableBook,
|
||||
position: ReadingPosition,
|
||||
): ReflowableBook {
|
||||
return {
|
||||
...book,
|
||||
position,
|
||||
};
|
||||
}
|
||||
|
||||
// Extract CFI from position
|
||||
export function getCurrentCFI(book: ReflowableBook): string {
|
||||
return book.position.cfi;
|
||||
}
|
||||
|
||||
// Calculate progress for display
|
||||
export function calculateProgress(book: ReflowableBook): {
|
||||
currentPage: number;
|
||||
totalPages: number;
|
||||
percentage: number;
|
||||
} {
|
||||
const totalPages = book.pagination?.totalPages || 1;
|
||||
const currentPage = book.position.currentPage;
|
||||
const percentage =
|
||||
totalPages > 0 ? Math.round((currentPage / totalPages) * 100) : 0;
|
||||
|
||||
return { currentPage, totalPages, percentage };
|
||||
}
|
||||
|
||||
// Get position for saving to database
|
||||
export function getPositionForSave(book: ReflowableBook): {
|
||||
cfi: string;
|
||||
progress: number;
|
||||
page: number;
|
||||
} {
|
||||
return {
|
||||
cfi: book.position.cfi,
|
||||
progress: book.position.progress,
|
||||
page: book.position.currentPage,
|
||||
};
|
||||
}
|
||||
|
||||
// Restore position from database
|
||||
export function restorePosition(
|
||||
book: ReflowableBook,
|
||||
savedCFI: string,
|
||||
savedPage?: number,
|
||||
): ReadingPosition {
|
||||
if (!book.pagination) {
|
||||
return book.position;
|
||||
}
|
||||
|
||||
// If we have saved CFI, try to find exact position
|
||||
if (savedCFI) {
|
||||
const pageNum = findPageByCFI(book.pagination, savedCFI);
|
||||
return createPositionFromPage(book, pageNum);
|
||||
}
|
||||
|
||||
// Otherwise use saved page number
|
||||
if (savedPage && savedPage > 0) {
|
||||
return createPositionFromPage(book, savedPage);
|
||||
}
|
||||
|
||||
return book.position;
|
||||
}
|
||||
|
||||
// Check if position changed significantly
|
||||
export function didPositionChange(
|
||||
oldPos: ReadingPosition,
|
||||
newPos: ReadingPosition,
|
||||
): boolean {
|
||||
return (
|
||||
oldPos.currentPage !== newPos.currentPage ||
|
||||
oldPos.cfi !== newPos.cfi ||
|
||||
Math.abs(oldPos.progress - newPos.progress) > 0.01
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user