import { setButtonLabel } from "./renderers/dom.ts"; import { convertUrl, converterFormats } from "./convert/client.ts"; import { extensionOf } from "./kinds.ts"; import { readBounded } from "./source.ts"; import type { ViewerSource } from "./types.ts"; /** Bounded plain text for search/indexing consumers; the server rechecks source access. */ export async function extractText( source: ViewerSource, options: { baseURI?: string; signal?: AbortSignal; fetch?: typeof fetch } = {}, ): Promise { const url = convertUrl(source, "text", options.baseURI); if (!url) throw new Error("Text extraction needs a URL"); const response = await (options.fetch ?? fetch)(url, { signal: options.signal }); if (!response.headers.get("content-type")?.startsWith("text/plain")) { await response.body?.cancel(); throw new Error("Text extraction unavailable"); } return new TextDecoder().decode(await readBounded(response, 2 * 1024 * 1024)); } export function mountExtractedText( parent: HTMLElement, source: ViewerSource, signal: AbortSignal, ): () => void { const doc = parent.ownerDocument; const button = doc.createElement("button"); button.type = "button"; button.className = "wrn-viewer-action"; setButtonLabel(button, "Extract text"); const details = doc.createElement("section"); const summary = doc.createElement("h3"); summary.textContent = "Extracted text"; const text = doc.createElement("pre"); details.append(summary, text); let disposed = false; void converterFormats({ baseURI: doc.baseURI }) .then((formats) => { if (!disposed && source.src && formats[extensionOf(source.name)]) parent.append(button); }) .catch(() => {}); button.onclick = async () => { button.disabled = true; try { const result = await extractText(source, { baseURI: doc.baseURI, signal }); if (disposed) return; text.textContent = result || "No text was found."; parent.append(details); } catch { if (!disposed) { text.textContent = "Text extraction is unavailable."; parent.append(details); } } finally { if (!disposed) button.disabled = false; } }; return () => { disposed = true; button.remove(); details.remove(); }; }