's'
This commit is contained in:
51
lib/content/ebook.ts
Normal file
51
lib/content/ebook.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* 电子书 Markdown 解析 - 共享模块
|
||||
*/
|
||||
import { marked } from "marked";
|
||||
|
||||
export interface EbookChunk {
|
||||
html: string;
|
||||
estimatedHeight: number;
|
||||
}
|
||||
|
||||
export interface EbookData {
|
||||
chunks: EbookChunk[];
|
||||
headers: string[];
|
||||
}
|
||||
|
||||
export function parseEbookMarkdown(source: string): EbookData {
|
||||
const html = marked.parse(source, { breaks: true, gfm: true }) as string;
|
||||
const lazyHtml = html.replace(/<img /g, '<img loading="lazy" decoding="async" ');
|
||||
|
||||
let rawChunks = lazyHtml.split(/(?=<h1[\s>])/i).filter((c) => c.trim());
|
||||
if (rawChunks.length <= 1) {
|
||||
rawChunks = lazyHtml.split(/(?=<h2[\s>])/i).filter((c) => c.trim());
|
||||
}
|
||||
|
||||
const headers: string[] = [];
|
||||
const headerRegex = /<h1[^>]*>(.*?)<\/h1>/gi;
|
||||
let m;
|
||||
while ((m = headerRegex.exec(lazyHtml)) !== null) {
|
||||
headers.push(m[1].replace(/<[^>]+>/g, ""));
|
||||
}
|
||||
if (headers.length === 0) {
|
||||
const h2Regex = /<h2[^>]*>(.*?)<\/h2>/gi;
|
||||
while ((m = h2Regex.exec(lazyHtml)) !== null) {
|
||||
headers.push(m[1].replace(/<[^>]+>/g, ""));
|
||||
}
|
||||
}
|
||||
|
||||
const chunks: EbookChunk[] = rawChunks.map((chunkHtml) => {
|
||||
const imgCount = (chunkHtml.match(/<img\b/g) || []).length;
|
||||
const textLen = chunkHtml.replace(/<[^>]+>/g, "").length;
|
||||
const estimatedHeight = Math.max(300, Math.round(textLen * 0.6 + imgCount * 350));
|
||||
return { html: chunkHtml, estimatedHeight };
|
||||
});
|
||||
|
||||
return { chunks, headers };
|
||||
}
|
||||
|
||||
/** 估算阅读时间(字/分钟,中文约 300-500) */
|
||||
export function estimateReadingMinutes(textLength: number, wpm = 400): number {
|
||||
return Math.max(1, Math.ceil(textLength / wpm));
|
||||
}
|
||||
Reference in New Issue
Block a user