From f4079a6f545cbb2b33cf02d2abf56765203e8617 Mon Sep 17 00:00:00 2001 From: PJ Date: Mon, 22 Jun 2026 13:36:50 -0400 Subject: [PATCH] fix(import): keep inline svg and unresolved image alt text Inline svg elements were dropped entirely and an img whose source could not be resolved was discarded. Serialize a standalone svg to a data:image/svg+xml figure, and fall back to an unresolved image's alt text as a paragraph instead of losing it. Claude-Session: https://claude.ai/code/session_01RzTWrLiy4zGuw2hZSQGamk --- src/import/epub.ts | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/src/import/epub.ts b/src/import/epub.ts index 934a95b..ff2e29b 100644 --- a/src/import/epub.ts +++ b/src/import/epub.ts @@ -146,13 +146,23 @@ function meaningful(inline: JSONContent[]): boolean { function imgFigure(img: Element, caption = ""): JSONContent[] { const src = img.getAttribute("src"); - if (!src || !src.startsWith("data:")) return []; - return [ - { - type: "figure", - attrs: { src, alt: img.getAttribute("alt") ?? "", caption, placement: "full-width" }, - }, - ]; + if (src && src.startsWith("data:")) { + return [ + { + type: "figure", + attrs: { src, alt: img.getAttribute("alt") ?? "", caption, placement: "full-width" }, + }, + ]; + } + const alt = (img.getAttribute("alt") ?? "").trim(); + return alt ? [{ type: "paragraph", content: [{ type: "text", text: alt }] }] : []; +} + +function svgFigure(el: Element): JSONContent[] { + const markup = el.outerHTML; + if (!markup) return []; + const src = `data:image/svg+xml;base64,${btoa(unescape(encodeURIComponent(markup)))}`; + return [{ type: "figure", attrs: { src, alt: "", caption: "", placement: "full-width" } }]; } function figureFrom(el: Element): JSONContent[] { @@ -218,6 +228,8 @@ function blockFromElement(el: Element): JSONContent[] { return figureFrom(el); case "img": return imgFigure(el); + case "svg": + return svgFigure(el); case "script": case "style": return [];