diff --git a/READER_IMPLEMENTATION_PLAN.md b/READER_IMPLEMENTATION_PLAN.md
index ec54eed..f22ab00 100644
--- a/READER_IMPLEMENTATION_PLAN.md
+++ b/READER_IMPLEMENTATION_PLAN.md
@@ -4606,95 +4606,6 @@ function getElementChildren(element: Element): Element[] {
---
-### 5.8 Typography Engine (Procedural)
-
-**File:** `web/src/reader/ebook/typography-engine.ts`
-
-```typescript
-// Typography engine for ebook rendering
-// Procedural style: Functions, not classes
-
-interface TypographyConfig {
- fontSize: number;
- lineHeight: number;
- textAlign: 'left' | 'justify';
- hyphenate: boolean;
- ligatures: boolean;
- fontSmoothing: 'auto' | 'grayscale';
-}
-
-export function applyTypographyConfig(
- element: HTMLElement,
- config: TypographyConfig
-): void {
- // Enable/disable ligatures
- setLigatures(element, config.ligatures);
-
- // Enable/disable hyphenation
- if (config.hyphenate) {
- enableHyphenation(element);
- }
-
- // Apply justification settings
- if (config.textAlign === 'justify') {
- enableJustification(element);
- }
-
- // Apply font smoothing
- element.style.fontSmooth = config.fontSmoothing;
-}
-
-function setLigatures(element: HTMLElement, enabled: boolean): void {
- if (enabled) {
- element.style.fontVariantLigatures = 'common-ligatures';
- element.style.fontFeatureSettings = '"liga", "dlig"';
- } else {
- element.style.fontVariantLigatures = 'no-common-ligatures';
- element.style.fontFeatureSettings = 'normal';
- }
-}
-
-function enableHyphenation(element: HTMLElement): void {
- element.style.hyphens = 'auto';
- element.style.hyphenateLimitChars = '6 3 3';
-
- // Add language attribute from EPUB metadata
- const lang = element.closest('[data-language]')?.getAttribute('data-language') || 'en';
- element.setAttribute('lang', lang);
-}
-
-function enableJustification(element: HTMLElement): void {
- element.style.wordBreak = 'normal';
- element.style.overflowWrap = 'break-word';
- element.style.wordWrap = 'break-word';
- element.style.letterSpacing = '0.01em';
-}
-
-export function measureReadingTime(
- container: HTMLElement,
- wordsPerMinute: number = 250
-): number {
- const content = container.querySelector('.ebook-content');
- if (!content) return 0;
-
- const text = content.textContent || '';
- const words = text.split(/\s+/).length;
- const minutes = words / wordsPerMinute;
-
- return Math.ceil(minutes);
-}
-
-export function getWordCount(container: HTMLElement): number {
- const content = container.querySelector('.ebook-content');
- if (!content) return 0;
-
- const text = content.textContent || '';
- return text.split(/\s+/).length;
-}
-```
-
----
-
### 5.9 Ebook Search (Procedural)
**File:** `web/src/reader/ebook/search.ts`
@@ -5103,194 +5014,7 @@ All fonts use SIL Open Font License 1.1
WOFF2 format, ~1.2MB total"
```
-#### 5.10.4 Font Loading in Templates
-
-**File:** `templates/reader.templ` (updated)
-
-Add to `
` section:
-
-```go
-templ Reader(user User, metadata ReaderMetadata) {
-
-
-
-
-
- { metadata.title } - Bookhoard Reader
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Table of Contents
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Reader Settings
-
-
-
-
-
-
Font
-
-
-
-
-
-
Font Size: 16px
-
-
-
-
-
-
Reading Theme
-
-
-
-
-
-
-
-
-
-
-
Line Spacing
-
-
-
-
-
-
Page Margins
-
-
-
-
-
-
-
-
-
-}
-```
-
-#### 5.10.5 Alternative: Use Google Fonts CDN (Not Recommended)
+#### 5.10.4 Alternative: Use Google Fonts CDN (Not Recommended)
If you don't want to bundle fonts (slower initial load, privacy concerns):
@@ -5820,166 +5544,6 @@ function getWordCount(container: HTMLElement): number {
}
```
-### 5.12 Search Within Ebook
-
-**File:** `web/src/reader/ebook/search.ts`
-
-```typescript
-// Search within ebook content
-
-interface SearchResult {
- cfi: string;
- snippet: string;
- chapterTitle: string;
-}
-
-// Search within ebook content
-// Procedural implementation (no OOP)
-
-interface SearchResult {
- cfi: string;
- snippet: string;
- chapterTitle: string;
-}
-
-async function searchEbook(
- epubPackage: EPUBPackage,
- query: string
-): Promise {
- const results: SearchResult[] = [];
- const lowerQuery = query.toLowerCase();
-
- for (const [index, spineItem] of epubPackage.spine.entries()) {
- const doc = await getSpineItemDocument(epubPackage, spineItem);
-
- if (!doc) continue;
-
- const chapterTitle = getChapterTitle(epubPackage, spineItem);
- const textNodes = findTextNodes(doc.body);
-
- for (const node of textNodes) {
- const text = node.textContent || '';
- const lowerText = text.toLowerCase();
-
- let foundAt = 0;
- while ((foundAt = lowerText.indexOf(lowerQuery, foundAt)) !== -1) {
- const cfi = generateSearchCFI(node, foundAt);
- const snippet = extractSearchSnippet(text, foundAt, query.length);
-
- results.push({
- cfi,
- snippet,
- chapterTitle
- });
-
- foundAt += lowerQuery.length;
- }
- }
- }
-
- return results;
-}
-
-async function getSpineItemDocument(
- epubPackage: EPUBPackage,
- spineItem: EPUBSpineItem
-): Promise {
- try {
- const content = await epubPackage.resources.get(spineItem.href)?.text();
- if (!content) return null;
-
- const parser = new DOMParser();
- return parser.parseFromString(content, 'text/html');
- } catch (error) {
- console.error('Failed to load spine item:', spineItem.href, error);
- return null;
- }
-}
-
-function findTextNodes(root: Node): Text[] {
- const textNodes: Text[] = [];
-
- const walker = document.createTreeWalker(
- root,
- NodeFilter.SHOW_TEXT,
- {
- acceptNode: (node) => {
- const parent = node.parentElement;
- if (parent && ['SCRIPT', 'STYLE', 'NOSCRIPT'].includes(parent.tagName)) {
- return NodeFilter.FILTER_REJECT;
- }
-
- if (!node.textContent?.trim()) {
- return NodeFilter.FILTER_REJECT;
- }
-
- return NodeFilter.FILTER_ACCEPT;
- }
- }
- );
-
- let node: Node | null;
- while ((node = walker.nextNode())) {
- textNodes.push(node as Text);
- }
-
- return textNodes;
-}
-
-function generateSearchCFI(node: Text, offset: number): string {
- const path: number[] = [];
- let current: Node | null = node;
-
- while (current && current.parentNode) {
- const parent = current.parentNode;
- const siblings = Array.from(parent.childNodes)
- .filter(n => n.nodeType === Node.ELEMENT_NODE);
- const index = siblings.indexOf(current as Node);
-
- path.unshift(index);
- current = parent;
- }
-
- const spineIndex = 0;
-
- return generateCFI(spineIndex, path, offset);
-}
-
-function extractSearchSnippet(text: string, offset: number, length: number): string {
- const contextBefore = 30;
- const contextAfter = 50;
-
- const start = Math.max(0, offset - contextBefore);
- const end = Math.min(text.length, offset + length + contextAfter);
-
- let snippet = text.substring(start, end);
-
- if (start > 0) snippet = '...' + snippet;
- if (end < text.length) snippet = snippet + '...';
-
- return snippet;
-}
-
-function getChapterTitle(
- epubPackage: EPUBPackage,
- spineItem: EPUBSpineItem
-): string {
- for (const toc of epubPackage.toc) {
- if (toc.href === spineItem.href) {
- return toc.label;
- }
-
- for (const child of toc.children) {
- if (child.href === spineItem.href) {
- return child.label;
- }
- }
- }
-
- return 'Chapter ' + (epubPackage.spine.indexOf(spineItem) + 1);
-}
-```
-
### 5.13 Copy Text Handler
**File:** `web/src/reader/ebook/copy-handler.ts`