forked from akai/readest
24370ca511
Open standalone .md files in the reader without converting to EPUB. A new makeMarkdownBook (src/utils/md.ts) parses Markdown to sanitized HTML with marked + DOMPurify, splits the document into sections at H1 boundaries, and builds an in-memory foliate book (modeled on fb2.js) with a nested heading TOC. DocumentLoader routes .md/.markdown before the TXT path so a Markdown file served as text/plain is not converted to EPUB. Layout, font and theme settings apply the same as for any other format. Relative-image resolution and Markdown bundle/folder packages are left as follow-ups (a standalone file has no sibling-asset access on the web). Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
82 lines
2.2 KiB
TypeScript
82 lines
2.2 KiB
TypeScript
import DOMPurify from 'dompurify';
|
|
|
|
export const sanitizeString = (str?: string) => {
|
|
if (!str) return str;
|
|
return str.replace(/\u0000/g, '');
|
|
};
|
|
|
|
/**
|
|
* Strip untrusted HTML (Send-to-Readest email/web/DOCX conversion, OPDS feed
|
|
* descriptions, etc.) down to safe, EPUB-appropriate structural markup. Removes
|
|
* scripts, event handlers, styles, iframes, and form controls; keeps headings,
|
|
* text, lists, tables, links and images.
|
|
*
|
|
* Runs against the real DOM, so callers must be on the client (browser or Tauri
|
|
* webview), both of which provide `window`/`DOMParser`.
|
|
*/
|
|
export function sanitizeHtml(html: string): string {
|
|
return DOMPurify.sanitize(html, {
|
|
ALLOWED_TAGS: [
|
|
'h1',
|
|
'h2',
|
|
'h3',
|
|
'h4',
|
|
'h5',
|
|
'h6',
|
|
'p',
|
|
'br',
|
|
'hr',
|
|
'blockquote',
|
|
'pre',
|
|
'code',
|
|
'strong',
|
|
'em',
|
|
'b',
|
|
'i',
|
|
'u',
|
|
's',
|
|
'del',
|
|
'ins',
|
|
'sup',
|
|
'sub',
|
|
'span',
|
|
'ul',
|
|
'ol',
|
|
'li',
|
|
'dl',
|
|
'dt',
|
|
'dd',
|
|
'table',
|
|
'thead',
|
|
'tbody',
|
|
'tr',
|
|
'th',
|
|
'td',
|
|
'a',
|
|
'img',
|
|
'figure',
|
|
'figcaption',
|
|
],
|
|
// `id` is allowed so heading anchors survive — the EPUB's nested
|
|
// navMap uses `chapter1.xhtml#heading-id` to link the TOC sidebar to
|
|
// each section. Without it readers see one entry per chapter only.
|
|
// `class` is allowed so Markdown code fences keep their `language-*`
|
|
// class for theming (see utils/md.ts).
|
|
ALLOWED_ATTR: ['href', 'src', 'alt', 'title', 'colspan', 'rowspan', 'id', 'class'],
|
|
// Drop anything that would load or run remote code.
|
|
FORBID_TAGS: ['script', 'style', 'iframe', 'object', 'embed', 'form', 'input'],
|
|
FORBID_ATTR: ['srcset'],
|
|
ALLOW_DATA_ATTR: false,
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Strip scripts and event handlers from an untrusted *document* before it is
|
|
* handed to `DOMParser`. Unlike `sanitizeHtml`, this keeps the document
|
|
* structure (`<head>`, `<title>`, sectioning elements) so title extraction and
|
|
* Readability still work — it only removes anything executable.
|
|
*/
|
|
export function sanitizeForParsing(html: string): string {
|
|
return DOMPurify.sanitize(html, { WHOLE_DOCUMENT: true });
|
|
}
|