import mammoth from "mammoth" export type TipTapJSON = Record function htmlToTipTapDoc(html: string): TipTapJSON { const parser = typeof DOMParser !== "undefined" ? new DOMParser() : null if (!parser) { return { type: "doc", content: [{ type: "paragraph", content: [{ type: "text", text: html.replace(/<[^>]+>/g, " ") }] }], } } const doc = parser.parseFromString(html, "text/html") const blocks: TipTapJSON[] = [] const walk = (node: Node) => { if (node.nodeType === Node.TEXT_NODE) { const text = node.textContent ?? "" if (text.trim()) { blocks.push({ type: "paragraph", content: [{ type: "text", text }] }) } return } if (node.nodeType !== Node.ELEMENT_NODE) return const el = node as HTMLElement const tag = el.tagName.toLowerCase() if (tag === "p" || tag === "div") { const text = el.textContent ?? "" blocks.push({ type: "paragraph", content: text ? [{ type: "text", text }] : [], }) return } if (/^h[1-6]$/.test(tag)) { const level = Number(tag[1]) blocks.push({ type: "heading", attrs: { level }, content: [{ type: "text", text: el.textContent ?? "" }], }) return } if (tag === "ul" || tag === "ol") { const listType = tag === "ul" ? "bulletList" : "orderedList" const items = Array.from(el.querySelectorAll(":scope > li")).map((li) => ({ type: "listItem", content: [{ type: "paragraph", content: [{ type: "text", text: li.textContent ?? "" }] }], })) blocks.push({ type: listType, content: items }) return } Array.from(el.childNodes).forEach(walk) } Array.from(doc.body.childNodes).forEach(walk) if (blocks.length === 0) { blocks.push({ type: "paragraph" }) } return { type: "doc", content: blocks } } export async function importDocxToTipTap(buffer: ArrayBuffer): Promise { try { const { parseDOCX } = await import("@docen/import-docx") const content = await parseDOCX(buffer) if (content && typeof content === "object") { return content as TipTapJSON } } catch { /* fallback mammoth */ } const result = await mammoth.convertToHtml({ arrayBuffer: buffer }) return htmlToTipTapDoc(result.value) } export async function exportTipTapToDocx(content: TipTapJSON): Promise { try { const { generateDOCX } = await import("@docen/export-docx") const buf = await generateDOCX(content, { outputType: "blob" }) if (buf instanceof Blob) return buf } catch { /* fallback unavailable */ } throw new Error("Export DOCX indisponible") } export async function importFileToTipTap( fileName: string, buffer: ArrayBuffer ): Promise { const ext = fileName.split(".").pop()?.toLowerCase() ?? "" if (ext === "docx" || ext === "docm") { return importDocxToTipTap(buffer) } const text = new TextDecoder().decode(buffer) if (ext === "html" || ext === "htm") { return htmlToTipTapDoc(text) } const lines = text.split(/\r?\n/) return { type: "doc", content: lines.map((line) => ({ type: "paragraph", content: line ? [{ type: "text", text: line }] : [], })), } }