From a13d2cc3bb7af4d12d7fa73372efbc97cbcc93ff Mon Sep 17 00:00:00 2001 From: John O'Keefe Date: Sun, 12 Apr 2026 20:44:02 -0400 Subject: [PATCH] docs: Update manga EPUB implementation guide - Add markdownlint disable directives for linting - Update SQL examples for consistency - Update templ examples for panel detection integration - Update TypeScript examples for reader shell configuration --- MANGA_EPUB_IMPLEMENTATION.md | 131 ++++++++++++++++++----------------- 1 file changed, 67 insertions(+), 64 deletions(-) diff --git a/MANGA_EPUB_IMPLEMENTATION.md b/MANGA_EPUB_IMPLEMENTATION.md index f9cd20c..b44040e 100644 --- a/MANGA_EPUB_IMPLEMENTATION.md +++ b/MANGA_EPUB_IMPLEMENTATION.md @@ -1,3 +1,6 @@ + + + # Manga EPUB and Panel Detection Implementation **Status:** Ready to implement @@ -35,7 +38,7 @@ -- ============================================================================ -- Allow EPUB in manga library -UPDATE library_types +UPDATE library_types SET allowed_extensions = array_append(allowed_extensions, '.epub') WHERE name = 'manga'; @@ -47,22 +50,22 @@ BEGIN IF opf_content LIKE '%rendition:layout">pre-paginated<%' THEN RETURN TRUE; END IF; - + IF opf_content LIKE '%rendition:layout="pre-paginated"%' THEN RETURN TRUE; END IF; - + -- Check for RTL page progression (manga indicator) IF opf_content LIKE '%page-progression-direction="rtl"%' THEN RETURN TRUE; END IF; - + -- Check for image-heavy content (count tags) -- Threshold of 50 images suggests manga/comic vs novel IF (SELECT COUNT(*) FROM regexp_matches(opf_content, ']+>', 'g')) > 50 THEN RETURN TRUE; END IF; - + RETURN FALSE; END; $$ LANGUAGE plpgsql; @@ -82,9 +85,9 @@ CREATE TABLE IF NOT EXISTS processing_issues ( ); -- Indexes for querying problem items -CREATE INDEX IF NOT EXISTS idx_processing_issues_library +CREATE INDEX IF NOT EXISTS idx_processing_issues_library ON processing_issues(library_id, resolved); -CREATE INDEX IF NOT EXISTS idx_processing_issues_severity +CREATE INDEX IF NOT EXISTS idx_processing_issues_severity ON processing_issues(severity, resolved); -- Add comment for documentation @@ -111,7 +114,7 @@ COMMENT ON TABLE processing_issues IS 'Tracks media items that cannot be properl -- name: CreateProcessingIssue :one INSERT INTO processing_issues (media_item_id, library_id, issue_type, issue_description, severity) VALUES ($1, $2, $3, $4, $5) -ON CONFLICT (media_item_id, issue_type) +ON CONFLICT (media_item_id, issue_type) DO UPDATE SET issue_description = EXCLUDED.issue_description, severity = EXCLUDED.severity, resolved = false, @@ -119,7 +122,7 @@ DO UPDATE SET issue_description = EXCLUDED.issue_description, RETURNING *; -- name: ListProcessingIssuesByLibrary :many -SELECT +SELECT pi.id, pi.media_item_id, pi.issue_type, @@ -138,7 +141,7 @@ JOIN libraries l ON pi.library_id = l.id JOIN library_types lt ON l.library_type_id = lt.id WHERE pi.library_id = $1 AND pi.resolved = false -ORDER BY +ORDER BY CASE pi.severity WHEN 'error' THEN 1 WHEN 'warning' THEN 2 @@ -147,7 +150,7 @@ ORDER BY pi.created_at DESC; -- name: GetProcessingIssueStats :one -SELECT +SELECT COUNT(*) FILTER (WHERE severity = 'error' AND resolved = false) as error_count, COUNT(*) FILTER (WHERE severity = 'warning' AND resolved = false) as warning_count, COUNT(*) FILTER (WHERE severity = 'info' AND resolved = false) as info_count @@ -155,7 +158,7 @@ FROM processing_issues WHERE library_id = $1; -- name: ResolveProcessingIssue :one -UPDATE processing_issues +UPDATE processing_issues SET resolved = true, resolved_at = NOW() WHERE id = $1 @@ -172,7 +175,7 @@ RETURNING *; -- ============================================================================ -- name: GetLibraryWithType :one -SELECT +SELECT l.*, lt.name as type_name, lt.description as type_description, @@ -713,7 +716,7 @@ templ AdminProcessingIssues(user User, libraryID string, issues []ProcessingIssu @Header(user, "/admin/libraries/"+libraryID) - +
@@ -726,7 +729,7 @@ templ AdminProcessingIssues(user User, libraryID string, issues []ProcessingIssu
- + if stats.ErrorCount > 0 || stats.WarningCount > 0 || stats.InfoCount > 0 {
@@ -750,7 +753,7 @@ templ AdminProcessingIssues(user User, libraryID string, issues []ProcessingIssu }
} - + if len(issues) == 0 {

No processing issues found for this library.

@@ -780,7 +783,7 @@ templ AdminProcessingIssues(user User, libraryID string, issues []ProcessingIssu
if issue.Severity == "warning" || issue.Severity == "info" { - @@ -813,7 +816,7 @@ templ AdminProcessingIssues(user User, libraryID string, issues []ProcessingIssu **Replace with:** ```templ - ``` @@ -841,58 +844,58 @@ import "foliate-js/view.js"; import { Alpine } from "../alpine"; document.addEventListener("alpine:init", () => { - Alpine.data("readerShell", () => ({ - enablePanelDetection: false, - libraryType: '', - formatGroup: '', - mangaType: '', - readingDirection: '', - panelDetector: null, + Alpine.data("readerShell", () => ({ + enablePanelDetection: false, + libraryType: "", + formatGroup: "", + mangaType: "", + readingDirection: "", + panelDetector: null, - initReader(config: any) { - this.enablePanelDetection = config.enablePanelDetection; - this.libraryType = config.libraryType; - this.formatGroup = config.formatGroup; - this.mangaType = config.mangaType; - this.readingDirection = config.readingDirection; + initReader(config: any) { + this.enablePanelDetection = config.enablePanelDetection; + this.libraryType = config.libraryType; + this.formatGroup = config.formatGroup; + this.mangaType = config.mangaType; + this.readingDirection = config.readingDirection; - console.log("Reader initialized with:", { - panelDetection: this.enablePanelDetection, - library: this.libraryType, - format: this.formatGroup - }); + console.log("Reader initialized with:", { + panelDetection: this.enablePanelDetection, + library: this.libraryType, + format: this.formatGroup, + }); - // Only load panel detection if enabled - if (this.enablePanelDetection) { - this.loadPanelDetection(); - } - }, + // Only load panel detection if enabled + if (this.enablePanelDetection) { + this.loadPanelDetection(); + } + }, - async loadPanelDetection() { - try { - // Dynamic import to only load when needed - const { PanelDetector } = await import('foliate-js/panel-detection.js'); - this.panelDetector = new PanelDetector(); - console.log("Panel detection loaded successfully"); - } catch (error) { - console.error("Failed to load panel detection:", error); - } - }, + async loadPanelDetection() { + try { + // Dynamic import to only load when needed + const { PanelDetector } = await import("foliate-js/panel-detection.js"); + this.panelDetector = new PanelDetector(); + console.log("Panel detection loaded successfully"); + } catch (error) { + console.error("Failed to load panel detection:", error); + } + }, - nextPage() { - const view = document.querySelector("#reader-view"); - // @ts-ignore - foliate custom element - view?.next?.(); - }, + nextPage() { + const view = document.querySelector("#reader-view"); + // @ts-ignore - foliate custom element + view?.next?.(); + }, - previousPage() { - const view = document.querySelector("#reader-view"); - // @ts-ignore - foliate custom element - view?.prev?.(); - } - })); + previousPage() { + const view = document.querySelector("#reader-view"); + // @ts-ignore - foliate custom element + view?.prev?.(); + }, + })); - Alpine.start(); + Alpine.start(); }); ```