// TXT Parser - Wraps plain text in HTML structure // Procedural style: Functions, not classes // ============================================================ // Main Parse Function // ============================================================ export async function parseTXT(txtBlob: Blob): Promise { const textContent = await txtBlob.text(); const metadata = extractTXTMetadata(txtBlob); const toc = createTXTTOC(textContent); const spine = createTXTSpine(textContent); const resources = new Map(); // No external resources for plain text const totalCharacters = textContent.length; return { metadata, toc, spine, resources, locations: { totalCharacters, estimatedPages: Math.ceil(totalCharacters / 1500), }, }; } // ============================================================ // Helper Functions // ============================================================ function extractTXTMetadata(txtBlob: Blob): EbookCIF["metadata"] { const filename = txtBlob.name || "Unknown"; return { title: filename.replace(/\.(txt|text)$/i, ""), author: "Unknown", language: "en", }; } function createTXTTOC(textContent: string): EbookCIF["toc"] { // Try to detect chapters (simple heuristic) const toc: EbookCIF["toc"] = []; const lines = textContent.split("\n"); let chapterIndex = 0; lines.forEach((line, index) => { // Common chapter patterns const chapterPattern = /^(chapter|part|section)\s+\d+/i; if (chapterPattern.test(line.trim())) { toc.push({ id: `chapter-${chapterIndex}`, title: line.trim(), href: `#chapter-${chapterIndex}`, children: [], }); chapterIndex++; } }); // If no chapters found, create single entry if (toc.length === 0) { toc.push({ id: "full-text", title: "Full Text", href: "#full-text", children: [], }); } return toc; } function createTXTSpine(textContent: string): EbookCIF["spine"] { // Convert plain text to HTML paragraphs const lines = textContent.split("\n"); let htmlContent = '
'; lines.forEach((line) => { const trimmed = line.trim(); if (trimmed) { htmlContent += `

${escapeHTML(trimmed)}

`; } else { htmlContent += "
"; } }); htmlContent += "
"; return [ { id: "full-text", type: "html", content: htmlContent, index: 0, }, ]; } function escapeHTML(text: string): string { const div = document.createElement("div"); div.textContent = text; return div.innerHTML; } // Removed - backend handles detailed position tracking // ============================================================ // Metadata Quick Extract // ============================================================ export async function getTXTMetadata( txtBlob: Blob, ): Promise> { return extractTXTMetadata(txtBlob); }