diff --git a/src/browser/byte-store.ts b/src/browser/byte-store.ts index 8eeee82..72d516a 100644 --- a/src/browser/byte-store.ts +++ b/src/browser/byte-store.ts @@ -25,7 +25,11 @@ export interface PdfByteRecord { } export interface PdfByteStore { - put(documentId: DocumentId, bytes: Uint8Array): PdfByteRecord; + put( + documentId: DocumentId, + bytes: Uint8Array, + contentType?: string, + ): PdfByteRecord; get(documentId: DocumentId): PdfByteRecord | null; has(documentId: DocumentId): boolean; delete(documentId: DocumentId): boolean; @@ -70,7 +74,7 @@ export function createPdfByteStore( const records = new Map(); return { - put(documentId, bytes) { + put(documentId, bytes, contentType = "application/pdf") { // Replace previous record (revoking the prior URL) if any. const prior = records.get(documentId); if (prior) revokeUrl(prior.blobUrl); @@ -79,7 +83,7 @@ export function createPdfByteStore( // refuses without help. The bytes here always come from a fresh // arrayBuffer() call, so a regular ArrayBuffer is guaranteed. const blob = new Blob([bytes as unknown as ArrayBuffer], { - type: "application/pdf", + type: contentType, }); const blobUrl = createUrl(blob); const record: PdfByteRecord = { bytes, blobUrl }; diff --git a/src/browser/index.ts b/src/browser/index.ts index 9271b61..7a0d816 100644 --- a/src/browser/index.ts +++ b/src/browser/index.ts @@ -15,6 +15,12 @@ export { type PdfByteStore, } from "./byte-store"; export { + ingestHtmlFromFile, + ingestMarkdownFromFile, ingestPdfFromFile, type IngestPdfFromFileOptions, } from "./upload"; +export { + markdownSourceToViewerHtml, + resolveDomViewerHtml, +} from "./view-html"; diff --git a/src/browser/upload.ts b/src/browser/upload.ts index 8804f5c..99d8e4f 100644 --- a/src/browser/upload.ts +++ b/src/browser/upload.ts @@ -18,6 +18,8 @@ * place that revokes them. */ +import { ingestHtml, type IngestHtmlResult } from "../html/ingest"; +import { ingestMarkdown, type IngestMarkdownResult } from "../markdown/ingest"; import { ingestPdf, type IngestPdfResult } from "../pdf/ingest"; import type { PdfByteStore } from "./byte-store"; @@ -37,7 +39,39 @@ export async function ingestPdfFromFile( ...(filename !== undefined ? { filename } : {}), ...(options.title !== undefined ? { title: options.title } : {}), }); - const record = store.put(ingested.document.id, bytes); + const record = store.put(ingested.document.id, bytes, "application/pdf"); + const document = { ...ingested.document, uri: record.blobUrl }; + return { document, representation: ingested.representation }; +} + +export async function ingestHtmlFromFile( + file: File | Blob, + store: PdfByteStore, + options: IngestPdfFromFileOptions = {}, +): Promise { + const bytes = new Uint8Array(await file.arrayBuffer()); + const filename = "name" in file && typeof file.name === "string" ? file.name : undefined; + const ingested = await ingestHtml(bytes, { + ...(filename !== undefined ? { filename } : {}), + ...(options.title !== undefined ? { title: options.title } : {}), + }); + const record = store.put(ingested.document.id, bytes, "text/html"); + const document = { ...ingested.document, uri: record.blobUrl }; + return { document, representation: ingested.representation }; +} + +export async function ingestMarkdownFromFile( + file: File | Blob, + store: PdfByteStore, + options: IngestPdfFromFileOptions = {}, +): Promise { + const bytes = new Uint8Array(await file.arrayBuffer()); + const filename = "name" in file && typeof file.name === "string" ? file.name : undefined; + const ingested = await ingestMarkdown(bytes, { + ...(filename !== undefined ? { filename } : {}), + ...(options.title !== undefined ? { title: options.title } : {}), + }); + const record = store.put(ingested.document.id, bytes, "text/markdown"); const document = { ...ingested.document, uri: record.blobUrl }; return { document, representation: ingested.representation }; } diff --git a/src/browser/view-html.test.ts b/src/browser/view-html.test.ts new file mode 100644 index 0000000..c39a2f5 --- /dev/null +++ b/src/browser/view-html.test.ts @@ -0,0 +1,23 @@ +import { describe, expect, it } from "vitest"; +import { createPdfByteStore } from "./byte-store"; +import { markdownSourceToViewerHtml, resolveDomViewerHtml } from "./view-html"; +import { ingestHtmlFromFile } from "./upload"; + +const HTML = `

Hello HTML viewer

`; + +describe("resolveDomViewerHtml", () => { + it("returns sanitized HTML for html-dom representations", async () => { + const store = createPdfByteStore(); + const file = new File([HTML], "sample.html", { type: "text/html" }); + const { document, representation } = await ingestHtmlFromFile(file, store); + const html = resolveDomViewerHtml(document, representation, store); + expect(html).toContain("Hello HTML viewer"); + expect(html).not.toMatch(/