Files
bookhoard/web/src/reader/core/reader-context.ts
T
john-okeefe 11039cfb89 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
2026-04-10 23:21:10 -04:00

83 lines
2.0 KiB
TypeScript

import { readerEvents, ReaderEventType } from "./reader-events";
import { UniversalReader } from "../reader-shell";
import { PDFDocumentProxy } from "pdfjs-dist";
interface PDFReader {
type: "pdf";
doc: PDFDocumentProxy;
currentPage: number;
}
interface ComicReader {
type: "comic";
images: Blob[];
currentPage: number;
}
interface MangaReader {
type: "manga";
images: Blob[];
currentPage: number;
readingDirection: "rtl" | "vertical";
}
export type CurrentReader =
| UniversalReader
| PDFReader
| ComicReader
| MangaReader;
export interface ReaderContext {
getState: () => {
currentReader: CurrentReader | null;
readerMetadata: ReaderMetadata | null;
};
setState: (updates: Partial<ReaderState>) => void;
navigation: {
nextPage: () => void;
previousPage: () => void;
goToPage: (page: number) => void;
goToChapter: (chapterIndex: number) => void;
};
render: () => void;
elements: {
readerContent: HTMLElement;
chrome: HTMLElement | null;
progressDisplay: HTMLElement | null;
};
events: {
on: (event: string, handler: Function) => void;
emit: (event: string, data?: any) => void;
};
}
export interface ReaderState {
currentReader: CurrentReader | null;
readerMetadata: ReaderMetadata | null;
}
export function createReaderContext(
getState: () => ReaderState,
setState: (updates: Partial<ReaderState>) => void,
navigation: ReaderContext["navigation"],
render: () => void,
): ReaderContext {
return {
getState,
setState,
navigation,
render,
elements: {
readerContent: document.getElementById("reader-content")!,
chrome: document.getElementById("reader-chrome"),
progressDisplay: document.getElementById("progress-display"),
},
events: {
on: (event: ReaderEventType, handler: Function) =>
readerEvents.on(event, handler),
emit: (event: ReaderEventType, data?: any) =>
readerEvents.emit(event, data),
},
};
}