Run code formatting (prettier/eslint) on existing reader format files that were not migrated to the new structure. This ensures consistent code style across the codebase. ## Changes ### Formatting Applied - Added newlines at end of files - Fixed line length and indentation - Updated import statements for consistency - Applied code style rules uniformly ### Files Affected - **Comic**: background-color, chapter-markers, page-order, panel-gap - **Ebook**: font-loader, search, view-modes - **Manga**: vertical-scroll-mode - **PDF**: annotation-layer, pdf-navigation, pdf-text-selection ## Purpose These files will be removed in subsequent commits as they are replaced by the new modular structure. The formatting ensures consistency during the transition period and maintains code quality standards. Note: These are non-functional formatting changes only. No logic or behavior was modified in this commit.
153 lines
3.5 KiB
TypeScript
153 lines
3.5 KiB
TypeScript
// Page order presets for manga/comics
|
|
// Auto-detect Japanese vs Western reading order
|
|
// Feature Registration Pattern implementation
|
|
|
|
import { ReaderContext } from "../core/reader-context";
|
|
|
|
export function init(context: ReaderContext): void {
|
|
let state: PageOrderState | null = null;
|
|
|
|
context.events.on(
|
|
"reader:loaded",
|
|
(detail: { totalPages: number; pageNames: string[] }) => {
|
|
state = createPageOrderState(detail.totalPages, detail.pageNames);
|
|
},
|
|
);
|
|
|
|
context.events.on("page-order:set", (detail: { mode: PageOrderMode }) => {
|
|
if (state) {
|
|
setPageOrderMode(state, detail.mode, context);
|
|
}
|
|
});
|
|
|
|
context.events.on("page-order:get", () => {
|
|
if (state) {
|
|
const order = getPageOrder(state);
|
|
context.events.emit("page-order:current", { order });
|
|
}
|
|
});
|
|
|
|
context.events.on(
|
|
"page-order:reorder",
|
|
(detail: { pageNumbers: number[] }) => {
|
|
if (state) {
|
|
const reordered = reorderPages(state, detail.pageNumbers);
|
|
context.events.emit("page-order:reordered", { pages: reordered });
|
|
}
|
|
},
|
|
);
|
|
|
|
context.events.on(
|
|
"page-order:display-number",
|
|
(detail: { actualPage: number }) => {
|
|
if (state) {
|
|
const displayPage = getDisplayPageNumber(state, detail.actualPage);
|
|
context.events.emit("page-order:display-page", { displayPage });
|
|
}
|
|
},
|
|
);
|
|
}
|
|
|
|
type PageOrderMode = "auto" | "japanese" | "western";
|
|
|
|
interface PageOrderConfig {
|
|
mode: PageOrderMode;
|
|
detectedOrder: PageOrderMode;
|
|
userOverride: boolean;
|
|
}
|
|
|
|
interface PageOrderState {
|
|
config: PageOrderConfig;
|
|
totalPages: number;
|
|
}
|
|
|
|
function detectPageOrder(pageNames: string[]): PageOrderMode {
|
|
if (pageNames.length < 2) return "western";
|
|
|
|
const firstPage = pageNames[0].toLowerCase();
|
|
const lastPage = pageNames[pageNames.length - 1].toLowerCase();
|
|
|
|
const hasFrontCover = /cover|front|001/.test(firstPage);
|
|
const hasBackCover = /back|end|最后的/.test(lastPage);
|
|
|
|
if (hasFrontCover && !hasBackCover) {
|
|
return "western";
|
|
}
|
|
if (hasBackCover && !hasFrontCover) {
|
|
return "japanese";
|
|
}
|
|
|
|
const chapterMatches = pageNames.filter((n) => /ch-\d+|chapter/i.test(n));
|
|
if (chapterMatches.length > 0) {
|
|
const firstChapter = chapterMatches[0];
|
|
const pageNum = parseInt(firstChapter.match(/\d+/)?.[0] || "0");
|
|
return pageNum > 0 ? "western" : "japanese";
|
|
}
|
|
|
|
return "western";
|
|
}
|
|
|
|
function createPageOrderState(
|
|
totalPages: number,
|
|
pageNames: string[],
|
|
): PageOrderState {
|
|
const detectedOrder = detectPageOrder(pageNames);
|
|
|
|
return {
|
|
config: {
|
|
mode: "auto",
|
|
detectedOrder,
|
|
userOverride: false,
|
|
},
|
|
totalPages,
|
|
};
|
|
}
|
|
|
|
function setPageOrderMode(
|
|
state: PageOrderState,
|
|
mode: PageOrderMode,
|
|
context: ReaderContext,
|
|
): PageOrderState {
|
|
state.config = {
|
|
...state.config,
|
|
mode,
|
|
userOverride: mode !== "auto",
|
|
};
|
|
|
|
const order = getPageOrder(state);
|
|
context.events.emit("page-order:changed", { mode, order });
|
|
|
|
return state;
|
|
}
|
|
|
|
function getPageOrder(state: PageOrderState): PageOrderMode {
|
|
if (state.config.mode === "auto") {
|
|
return state.config.detectedOrder;
|
|
}
|
|
return state.config.mode;
|
|
}
|
|
|
|
function reorderPages(state: PageOrderState, pageNumbers: number[]): number[] {
|
|
const order = getPageOrder(state);
|
|
|
|
if (order === "japanese") {
|
|
return [...pageNumbers].reverse();
|
|
}
|
|
|
|
return pageNumbers;
|
|
}
|
|
|
|
function getDisplayPageNumber(
|
|
state: PageOrderState,
|
|
actualPage: number,
|
|
): number {
|
|
const order = getPageOrder(state);
|
|
|
|
if (order === "japanese") {
|
|
return state.totalPages - actualPage + 1;
|
|
}
|
|
|
|
return actualPage;
|
|
}
|
|
|