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,62 @@
|
||||
// Import types and existing parsers
|
||||
import type { ReflowableBook, SpineItem, TOCItem } from "./types";
|
||||
import { parseEPUB } from "../../parsers/epub-parsers";
|
||||
import { parseFB2 } from "../../parsers/fb2-parser";
|
||||
import { parseTXT } from "../../parsers/txt-parser";
|
||||
import { parseHTML } from "../../parsers/html-parser";
|
||||
|
||||
// Parse any reflowable format
|
||||
export async function parseReflowable(
|
||||
file: File,
|
||||
format: "epub" | "fb2" | "txt" | "html",
|
||||
): Promise<ReflowableBook> {
|
||||
switch (format) {
|
||||
case "epub":
|
||||
return await parseEPUB(file);
|
||||
case "fb2":
|
||||
return await parseFB2(file);
|
||||
case "txt":
|
||||
return await parseTXT(file);
|
||||
case "html":
|
||||
return await parseHTML(file);
|
||||
default:
|
||||
throw new Error(`Unsupported reflowable format: ${format}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Validate parsed book data
|
||||
export function validateBook(book: ReflowableBook): boolean {
|
||||
return book.spine.length > 0 && book.metadata.title !== "";
|
||||
}
|
||||
|
||||
// Get book title
|
||||
export function getBookTitle(book: ReflowableBook): string {
|
||||
return book.metadata.title || "Untitled";
|
||||
}
|
||||
|
||||
// Get book author
|
||||
export function getBookAuthor(book: ReflowableBook): string {
|
||||
return book.metadata.author || "Unknown";
|
||||
}
|
||||
|
||||
// Get total spine count
|
||||
export function getSpineCount(book: ReflowableBook): number {
|
||||
return book.spine.length;
|
||||
}
|
||||
|
||||
// Get TOC as flat list
|
||||
export function getFlatTOC(book: ReflowableBook): TOCItem[] {
|
||||
const flat: TOCItem[] = [];
|
||||
|
||||
function traverse(items: TOCItem[]) {
|
||||
for (const item of items) {
|
||||
flat.push(item);
|
||||
if (item.children.length > 0) {
|
||||
traverse(item.children);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
traverse(book.toc);
|
||||
return flat;
|
||||
}
|
||||
Reference in New Issue
Block a user