fix(ebook-reader): load SVG images and fix progress update ID

- Add SVG image support: process <image xlink:href=...> elements
  in addition to HTML <img> tags for cover pages and embedded images
- Fix progress update: use 'id' from API response instead of
  'media_item_id' which the backend never returns for this endpoint
- Update reader context interfaces to include currentPage field
- Add debug logging for all resource keys to help troubleshoot
  image loading issues in future epubs
This commit is contained in:
2026-04-06 17:01:05 -04:00
parent b12040c63f
commit 6805d0b66d
4 changed files with 29 additions and 15 deletions
+5 -1
View File
@@ -28,7 +28,11 @@ interface MangaReader {
readingDirection: "rtl" | "vertical";
}
type CurrentReader = UniversalReader | PDFReader | ComicReader | MangaReader;
export type CurrentReader =
| UniversalReader
| PDFReader
| ComicReader
| MangaReader;
export interface ReaderContext {
getState: () => {
+22 -12
View File
@@ -279,6 +279,7 @@ export async function renderSpineItem() {
if (!container || !spineItem) return;
// Get the actual content from resources using the href
const resources = state.currentReader.cif.resources;
console.log("ALL resource keys:", Array.from(resources.keys()));
const contentBlob = resources?.get(spineItem.content);
if (!contentBlob) {
// Fallback: try to fetch directly if not in resources
@@ -326,16 +327,11 @@ export async function renderSpineItem() {
// Rewrite image src paths to use blob URLs from resources
const modifiedContent = rewriteImageUrls(contentText, resources);
const images = modifiedContent.match(/<img[^>]+>/g);
console.log("Found images in content:", images);
// Test resource lookup
console.log("Trying to find image:", {
direct: resources.get("image/1.png"),
withoutFolder: resources.get("1.png"),
allImageKeys: Array.from(resources.keys()).filter(
(k: string) => k.includes("image") || k.includes("png"),
),
});
const htmlImages = modifiedContent.match(/<img[^>]+>/g);
const svgImages =
modifiedContent.match(/<image[^>]+xlink:href="([^"]+)"[^>]*>/gi) || [];
const allImages = [...htmlImages, ...svgImages];
console.log("Found images in content:", allImages);
// Parse HTML and process images BEFORE setting innerHTML
const parser = new DOMParser();
@@ -354,6 +350,20 @@ export async function renderSpineItem() {
img.setAttribute("src", blobUrl);
}
}
// Process SVG <image> elements with xlink:href
const svgImgElements = Array.from(doc.querySelectorAll("image"));
for (const img of svgImgElements) {
const src = img.getAttribute("xlink:href");
if (!src) continue;
let blob = findImageInResources(resources, src);
if (blob) {
const blobUrl = URL.createObjectURL(blob);
img.setAttribute("xlink:href", blobUrl);
}
}
container.innerHTML = doc.body.innerHTML;
// Apply reader styling
applyReaderTheme();
@@ -498,7 +508,7 @@ function sendProgressUpdate() {
if (!state.currentReader || !state.readerMetadata) return;
const mediaItemId =
state.readerMetadata.media_item_id ||
state.readerMetadata.id ||
document.body.dataset.mediaItemId ||
window.location.pathname.split("/").pop();
@@ -541,7 +551,7 @@ function sendProgressUpdate() {
const percentage = totalPages > 0 ? (currentPage / totalPages) * 100 : 0;
updateReadingProgress(
state.readerMetadata.media_item_id,
state.readerMetadata.id,
{
current_page: currentPage,
total_pages: totalPages,
+1 -1
View File
@@ -1,4 +1,4 @@
import type { ReaderMetadata, ReaderState } from "./reader-context";
import type { ReaderState } from "./reader-context";
type CurrentReader = import("./reader-context").CurrentReader;
+1 -1
View File
@@ -72,7 +72,7 @@ interface ParserCapabilities {
// ============================================================
interface ReaderMetadata {
media_item_id: string;
id: string;
title: string;
author: string;
cover_image_path: string;