fix(ebook-reader): Replace ReflowableBook with UniversalReader type system
BREAKING CHANGE: Unify ebook reader type system to match actual data structures Problem: - ReflowableBook type had direct properties (spine, resources, toc, metadata) - UniversalReader wraps EbookCIF in cif property with runtime state - Type mismatch caused unsafe 'as any' casts and runtime errors - Two conflicting UniversalReader definitions existed (reader-shell vs reader-context) Root Cause: - Parsers return EbookCIF (nested structure) - ReflowableBook expected flat structure - Code mixed both approaches causing confusion Changes: Type System Updates: - Replace all ReflowableBook references with UniversalReader - Remove duplicate UniversalReader definition in reader-context.ts - Import UniversalReader from canonical source (reader-shell.ts) - Update all function signatures across reflowable module Property Access Patterns: - book.cif.spine instead of book.spine - book.cif.resources instead of book.resources - book.cif.toc instead of book.toc - book.cif.metadata instead of book.metadata Fixed Modules: - reader-shell.ts: UniversalReader object construction - reader-context.ts: Remove duplicate interface, import from reader-shell - reader-navigation.ts: Remove unsafe type casts, fix property access - reader-services.ts: Align with UniversalReader structure - reflowable/navigation.ts: Update all navigation function signatures - reflowable/progress-tracker.ts: Update tracker function signatures - reflowable/parser.ts: Return UniversalReader with proper structure - reflowable/page-calculator.ts: Update calculation function signatures - reflowable/ebook/search.ts: Fix property access patterns - types/reader.d.ts: Remove duplicate type definitions Impact: - ✅ Type-safe throughout ebook reader - ✅ Matches actual data structures from parsers - ✅ No more unsafe type casts - ✅ Single source of truth for UniversalReader - ✅ Aligns with EbookCIF format from API/parsers Files changed: 10 Lines changed: +320, -180
This commit is contained in:
@@ -1,51 +1,113 @@
|
||||
// Import types and existing parsers
|
||||
import type { ReflowableBook, SpineItem, TOCItem } from "./types";
|
||||
import type { 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";
|
||||
import { UniversalReader } from "../../reader-shell";
|
||||
|
||||
// Parse any reflowable format
|
||||
export async function parseReflowable(
|
||||
file: File,
|
||||
format: "epub" | "fb2" | "txt" | "html",
|
||||
): Promise<ReflowableBook> {
|
||||
): Promise<UniversalReader> {
|
||||
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);
|
||||
case "epub": {
|
||||
const cif = await parseEPUB(file); // Get EbookCIF
|
||||
return {
|
||||
// Wrap in UniversalReader structure
|
||||
type: "ebook",
|
||||
cif, // ← The EbookCIF goes here
|
||||
currentSpineIndex: 0,
|
||||
currentPage: 1,
|
||||
pagination: null,
|
||||
position: {
|
||||
currentPage: 1,
|
||||
spineIndex: 0,
|
||||
localPageIndex: 0,
|
||||
cfi: "",
|
||||
progress: 0,
|
||||
},
|
||||
};
|
||||
}
|
||||
case "fb2": {
|
||||
const cif = await parseFB2(file);
|
||||
return {
|
||||
type: "ebook",
|
||||
cif,
|
||||
currentSpineIndex: 0,
|
||||
currentPage: 1,
|
||||
pagination: null,
|
||||
position: {
|
||||
currentPage: 1,
|
||||
spineIndex: 0,
|
||||
localPageIndex: 0,
|
||||
cfi: "",
|
||||
progress: 0,
|
||||
},
|
||||
};
|
||||
}
|
||||
case "txt": {
|
||||
const cif = await parseTXT(file);
|
||||
return {
|
||||
type: "ebook",
|
||||
cif,
|
||||
currentSpineIndex: 0,
|
||||
currentPage: 1,
|
||||
pagination: null,
|
||||
position: {
|
||||
currentPage: 1,
|
||||
spineIndex: 0,
|
||||
localPageIndex: 0,
|
||||
cfi: "",
|
||||
progress: 0,
|
||||
},
|
||||
};
|
||||
}
|
||||
case "html": {
|
||||
const cif = await parseHTML(file);
|
||||
return {
|
||||
type: "ebook",
|
||||
cif,
|
||||
currentSpineIndex: 0,
|
||||
currentPage: 1,
|
||||
pagination: null,
|
||||
position: {
|
||||
currentPage: 1,
|
||||
spineIndex: 0,
|
||||
localPageIndex: 0,
|
||||
cfi: "",
|
||||
progress: 0,
|
||||
},
|
||||
};
|
||||
}
|
||||
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 !== "";
|
||||
export function validateBook(book: UniversalReader): boolean {
|
||||
return book.cif.spine.length > 0 && book.cif.metadata.title !== "";
|
||||
}
|
||||
|
||||
// Get book title
|
||||
export function getBookTitle(book: ReflowableBook): string {
|
||||
return book.metadata.title || "Untitled";
|
||||
export function getBookTitle(book: UniversalReader): string {
|
||||
return book.cif.metadata.title || "Untitled";
|
||||
}
|
||||
|
||||
// Get book author
|
||||
export function getBookAuthor(book: ReflowableBook): string {
|
||||
return book.metadata.author || "Unknown";
|
||||
export function getBookAuthor(book: UniversalReader): string {
|
||||
return book.cif.metadata.author || "Unknown";
|
||||
}
|
||||
|
||||
// Get total spine count
|
||||
export function getSpineCount(book: ReflowableBook): number {
|
||||
return book.spine.length;
|
||||
export function getSpineCount(book: UniversalReader): number {
|
||||
return book.cif.spine.length;
|
||||
}
|
||||
|
||||
// Get TOC as flat list
|
||||
export function getFlatTOC(book: ReflowableBook): TOCItem[] {
|
||||
export function getFlatTOC(book: UniversalReader): TOCItem[] {
|
||||
const flat: TOCItem[] = [];
|
||||
|
||||
function traverse(items: TOCItem[]) {
|
||||
@@ -57,6 +119,6 @@ export function getFlatTOC(book: ReflowableBook): TOCItem[] {
|
||||
}
|
||||
}
|
||||
|
||||
traverse(book.toc);
|
||||
traverse(book.cif.toc);
|
||||
return flat;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user