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.
99 lines
2.9 KiB
TypeScript
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);
|
|
} |