Files
bookhoard/web/src/reader/formats/manga/settings.ts
T
john-okeefe e4c18e51f9 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.
2026-04-09 14:53:12 -04:00

99 lines
2.9 KiB
TypeScript

// Manga-specific settings integration
// Feature Registration Pattern implementation
import type { ReaderContext } from "../core/reader-context";
export function init(context: ReaderContext): void {
let currentSettings: MangaSettings | null = null;
context.events.on("reader:loaded", async () => {
currentSettings = await getMangaSettings();
applyMangaSettings(currentSettings);
context.events.emit("manga-settings:loaded", currentSettings);
});
context.events.on("manga-settings:update", async (detail: { settings: Partial<MangaSettings> }) => {
if (currentSettings) {
currentSettings = { ...currentSettings, ...detail.settings };
await updateMangaSettings(detail.settings);
applyMangaSettings(currentSettings);
context.events.emit("manga-settings:changed", currentSettings);
}
});
context.events.on("manga-settings:get", () => {
if (currentSettings) {
context.events.emit("manga-settings:current", currentSettings);
}
});
}
interface MangaSettings {
readingDirection: "auto" | "ltr" | "rtl" | "vertical";
verticalScrollSpeed: "slow" | "normal" | "fast";
rtlPageTransition: "slide" | "fade" | "none";
webtoonMode: boolean;
}
async function getMangaSettings(): Promise<MangaSettings> {
const defaultSettings: MangaSettings = {
readingDirection: "auto",
verticalScrollSpeed: "normal",
rtlPageTransition: "slide",
webtoonMode: false,
};
try {
const userId = localStorage.getItem("userId");
const response = await fetch(`/api/users/${userId}/settings`);
if (response.ok) {
const settings = await response.json();
return { ...defaultSettings, ...settings };
}
} catch (error) {
console.error("Failed to load manga settings:", error);
}
return defaultSettings;
}
async function updateMangaSettings(
settings: Partial<MangaSettings>,
): Promise<void> {
const userId = localStorage.getItem("userId");
try {
const response = await fetch(`/api/users/${userId}/settings`, {
method: "PUT",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${localStorage.getItem("token")}`,
},
body: JSON.stringify(settings),
});
if (!response.ok) {
throw new Error("Failed to update manga settings");
}
} catch (error) {
console.error("Failed to save manga settings:", error);
}
}
function applyMangaSettings(settings: MangaSettings): void {
document.documentElement.dataset.readingDirection = settings.readingDirection;
if (settings.verticalScrollSpeed === "slow") {
document.documentElement.style.scrollBehavior = "smooth";
} else if (settings.verticalScrollSpeed === "fast") {
document.documentElement.style.scrollBehavior = "auto";
}
if (settings.rtlPageTransition !== "none") {
document.documentElement.dataset.pageTransition =
settings.rtlPageTransition;
}
document.documentElement.dataset.webtoonMode = String(settings.webtoonMode);
}