@ai-matrx/office 0.8.2 → 0.8.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +8 -0
- package/dist/docs-io.cjs +13 -5
- package/dist/docs-io.cjs.map +1 -1
- package/dist/docs-io.js +13 -5
- package/dist/docs-io.js.map +1 -1
- package/package.json +5 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.8.3
|
|
4
|
+
|
|
5
|
+
- A table in an uploaded Word file now imports as one markdown table block (its rows were split by blank lines, so no table was drawn), and an HTML `<table>` imports as a table too (its cells were loose paragraphs). Both go through THE one converter into a real Univer table. Test: `a-word-or-html-table-imports-as-a-real-table.test.ts` (red on 0.8.2).
|
|
6
|
+
|
|
7
|
+
### Consumer action
|
|
8
|
+
|
|
9
|
+
None.
|
|
10
|
+
|
|
3
11
|
## 0.8.2
|
|
4
12
|
|
|
5
13
|
- Markdown an agent (or a paste) puts into an open document is drawn as structure, not typed as text. Live bug: a "Monthly Budget Dashboard" showed its table as raw `| Category | ... |` lines because only "create from markdown" converted. New `markdownFragment(markdown)` (body + table sources for `doc.command.inner-paste`, from THE one writer `markdownToUniverDoc`) and `looksLikeMarkdown(text)` in `./docs/tools`; `insertableBody` is removed (it dropped tables), `DocEditOps.insertMarkdown` now pastes the fragment; `DocumentBodyPort` gains optional `insertMarkdown(at, markdown)` and `writeBodyText` uses it when the new span carries markdown structure. Test: `a-markdown-table-an-agent-writes-is-a-real-table.test.ts` (red on 0.8.1).
|
package/dist/docs-io.cjs
CHANGED
|
@@ -260,6 +260,11 @@ function documentFileKind(name) {
|
|
|
260
260
|
return null;
|
|
261
261
|
}
|
|
262
262
|
var W = "http://schemas.openxmlformats.org/wordprocessingml/2006/main";
|
|
263
|
+
function markdownTable(rows) {
|
|
264
|
+
const width = Math.max(...rows.map((r) => r.length));
|
|
265
|
+
const line = (r) => `| ${Array.from({ length: width }, (_, i) => _nullishCoalesce(r[i], () => ( ""))).join(" | ")} |`;
|
|
266
|
+
return [line(rows[0]), `|${" --- |".repeat(width)}`, ...rows.slice(1).map(line)].join("\n");
|
|
267
|
+
}
|
|
263
268
|
function docxXmlToMarkdown(xml) {
|
|
264
269
|
const doc = new DOMParser().parseFromString(xml, "application/xml");
|
|
265
270
|
const body = doc.getElementsByTagNameNS(W, "body")[0];
|
|
@@ -308,11 +313,7 @@ function docxXmlToMarkdown(xml) {
|
|
|
308
313
|
(tc) => Array.from(tc.getElementsByTagNameNS(W, "p")).map(paragraph).join(" ").replace(/\|/g, "\\|").trim()
|
|
309
314
|
)
|
|
310
315
|
);
|
|
311
|
-
if (rows.length)
|
|
312
|
-
out.push("", `| ${rows[0].join(" | ")} |`, `|${" --- |".repeat(rows[0].length)}`);
|
|
313
|
-
for (const row of rows.slice(1)) out.push(`| ${row.join(" | ")} |`);
|
|
314
|
-
out.push("");
|
|
315
|
-
}
|
|
316
|
+
if (rows.length) out.push(markdownTable(rows));
|
|
316
317
|
}
|
|
317
318
|
}
|
|
318
319
|
return out.reduce((acc, line, i) => {
|
|
@@ -360,6 +361,13 @@ function htmlToDocumentMarkdown(html) {
|
|
|
360
361
|
out.push(inline(el).trim());
|
|
361
362
|
return;
|
|
362
363
|
}
|
|
364
|
+
if (tag === "table") {
|
|
365
|
+
const rows = Array.from(el.querySelectorAll("tr")).map(
|
|
366
|
+
(tr) => Array.from(tr.children).filter((c) => /^(td|th)$/i.test(c.tagName)).map((c) => inline(c).replace(/\|/g, "\\|").replace(/\s+/g, " ").trim())
|
|
367
|
+
);
|
|
368
|
+
if (rows.length) out.push(markdownTable(rows));
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
363
371
|
if (tag === "pre") {
|
|
364
372
|
out.push("```", _nullishCoalesce(el.textContent, () => ( "")), "```");
|
|
365
373
|
return;
|
package/dist/docs-io.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/home/runner/work/aidream/aidream/apps/shared/office/dist/docs-io.cjs","../src/docs/univer-doc-to-markdown.ts","../src/docs/import-document-file.ts","../src/docs/export-open-formats.ts"],"names":["out"],"mappings":"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","file":"/home/runner/work/aidream/aidream/apps/shared/office/dist/docs-io.cjs","sourcesContent":[null,"/**\n * univerDocToMarkdown — the reverse of `markdown-to-univer-doc.ts`.\n *\n * Why it exists: the canvas pane for a cloud document (`udt_document`) offers a\n * `Source` tab, and the rule in `features/canvas/core/canvasSource.ts` is that\n * Source shows THE ITEM'S OWN SOURCE — for a document, its markdown — never the\n * redux envelope. A document's content lives as an opaque Univer\n * `IDocumentData` snapshot in `workbench.udt_document_snapshots`, so something\n * has to read it back out. This is that something, and it is the ONLY one:\n * never re-implement a second snapshot reader.\n *\n * What it reads (the half `markdownToUniverDoc` writes):\n * - `body.dataStream` — the text; `\\r` terminates a paragraph, a trailing\n * `\\n` terminates the section.\n * - `body.paragraphs` — one entry per `\\r` (`startIndex` = the `\\r` offset).\n * - `body.textRuns` — `{ st, ed, ts }` style overlays. `ts.bl` + a heading\n * font size is how `markdownToUniverDoc` encodes\n * `#`..`######`, so the level is recovered from `fs`.\n *\n * Fidelity is deliberately MODEST and honest: headings, bold, italic,\n * strikethrough and inline/monospace code round-trip; everything else comes\n * back as the plain text the user actually sees. A document typed by hand in\n * Univer never had markdown syntax in the first place — printing its text is\n * the truthful answer, not a reconstruction.\n *\n * PURE — no React, no Redux, no IO.\n */\n\nconst TABLE_CELL_SEP = \" | \";\n\n// Univer's DataStreamTreeTokenType control characters (literals: no Univer runtime here).\nconst TABLE_START = \"\\x1A\";\nconst TABLE_ROW_START = \"\\x1B\";\nconst TABLE_CELL_START = \"\\x1C\";\nconst TABLE_CELL_END = \"\\x1D\";\nconst TABLE_ROW_END = \"\\x0E\";\nconst TABLE_END = \"\\x0F\";\nconst LINK_START = \"\\x1F\";\nconst LINK_END = \"\\x1E\";\n\ninterface CustomRangeLike {\n startIndex?: number;\n endIndex?: number;\n rangeType?: number;\n properties?: { url?: unknown };\n}\n\n/** The Univer-native shapes (real tables, hyperlink ranges) written back as markdown. */\nfunction tableMarkdown(rows: string[][]): string[] {\n const width = Math.max(1, ...rows.map((r) => r.length));\n const cell = (r: string[], i: number) => (r[i] ?? \"\").replace(/^\\*\\*(.*)\\*\\*$/, \"$1\");\n const line = (r: string[], plain: boolean) =>\n `| ${Array.from({ length: width }, (_, i) => (plain ? cell(r, i) : (r[i] ?? \"\"))).join(\" | \")} |`;\n const out = [line(rows[0]!, true), `|${\" --- |\".repeat(width)}`];\n for (const r of rows.slice(1)) out.push(line(r, false));\n return out;\n}\n\ninterface TextRunLike {\n st?: number;\n ed?: number;\n ts?: {\n bl?: number;\n it?: number;\n ff?: string;\n fs?: number;\n st?: { s?: number } | number;\n };\n}\n\n/** Inverse of markdown-to-univer-doc's HEADING_FONT_SIZE. */\nexport const HEADING_LEVEL_BY_FONT_SIZE: Record<number, number> = {\n 26: 1,\n 22: 2,\n 18: 3,\n 16: 4,\n 14: 5,\n 13: 6,\n};\n\nfunction asArray<T>(value: unknown): T[] {\n return Array.isArray(value) ? (value as T[]) : [];\n}\n\nconst TRUE_ENOUGH = (v: unknown): boolean => v === 1 || v === true;\n\nfunction isStruck(ts: TextRunLike[\"ts\"]): boolean {\n if (!ts) return false;\n const st = ts.st;\n if (typeof st === \"number\") return TRUE_ENOUGH(st);\n return TRUE_ENOUGH(st?.s);\n}\n\n/** The style that applies to the whole paragraph, if the runs agree on one. */\nfunction paragraphHeadingLevel(runs: TextRunLike[]): number | null {\n for (const run of runs) {\n const fs = run.ts?.fs;\n if (typeof fs === \"number\" && TRUE_ENOUGH(run.ts?.bl)) {\n const level = HEADING_LEVEL_BY_FONT_SIZE[fs];\n if (level) return level;\n }\n }\n return null;\n}\n\nfunction emphasize(text: string, ts: TextRunLike[\"ts\"], inHeading: boolean): string {\n if (!text) return text;\n // Never double-mark: a heading's own runs are bold by construction.\n const leading = text.match(/^\\s*/)?.[0] ?? \"\";\n const trailing = text.match(/\\s*$/)?.[0] ?? \"\";\n let core = text.slice(leading.length, text.length - trailing.length);\n if (!core) return text;\n\n const mono = typeof ts?.ff === \"string\" && /mono/i.test(ts.ff);\n if (mono) core = `\\`${core}\\``;\n else {\n if (TRUE_ENOUGH(ts?.it)) core = `*${core}*`;\n if (TRUE_ENOUGH(ts?.bl) && !inHeading) core = `**${core}**`;\n }\n if (isStruck(ts)) core = `~~${core}~~`;\n return `${leading}${core}${trailing}`;\n}\n\n// An inline picture: one CUSTOM_BLOCK token in the stream, its drawing in `snapshot.drawings`.\nconst CUSTOM_BLOCK = \"\\b\";\n\ninterface DrawingLike {\n source?: unknown;\n title?: unknown;\n description?: unknown;\n docTransform?: { size?: { width?: unknown; height?: unknown } };\n}\n\n/**\n * A picture's markdown source. Our own pictures are `matrx-file:<id>`; the\n * drawn size rides as a `#WxH` fragment so a round trip keeps the shape.\n */\nexport function imageMarkdownSource(drawing: DrawingLike): string | null {\n const source = typeof drawing.source === \"string\" ? drawing.source : \"\";\n if (!source || source.startsWith(\"data:\")) return null;\n const w = Number(drawing.docTransform?.size?.width);\n const h = Number(drawing.docTransform?.size?.height);\n return Number.isFinite(w) && Number.isFinite(h) && w > 0 && h > 0 && !source.includes(\"#\")\n ? `${source}#${Math.round(w)}x${Math.round(h)}`\n : source;\n}\n\n/** Every picture in a snapshot, as its markdown source (`matrx-file:<id>#WxH` or a URL). */\nexport function documentImageSources(snapshot: unknown): string[] {\n const drawings = ((snapshot as { drawings?: unknown } | null)?.drawings ?? {}) as Record<string, DrawingLike>;\n const body = (snapshot as { body?: { customBlocks?: unknown } } | null)?.body;\n return asArray<{ blockId?: unknown }>(body?.customBlocks)\n .map((b) => (typeof b.blockId === \"string\" && drawings[b.blockId] ? imageMarkdownSource(drawings[b.blockId]!) : null))\n .filter((s): s is string => !!s);\n}\n\nexport interface DocToMarkdownOptions {\n /**\n * Where a picture's markdown points. Return null to leave it out (the caller\n * names what was left out). Default: the stored source.\n */\n imageUrl?: (source: string) => string | null;\n}\n\n/**\n * Read a Univer document snapshot back as markdown. Returns `\"\"` for an empty\n * or unreadable snapshot so the caller can say \"no source\" honestly rather than\n * printing an envelope.\n */\nexport function univerDocToMarkdown(snapshot: unknown, options: DocToMarkdownOptions = {}): string {\n if (!snapshot || typeof snapshot !== \"object\") return \"\";\n const body = (snapshot as { body?: unknown }).body;\n if (!body || typeof body !== \"object\") return \"\";\n\n const dataStream = (body as { dataStream?: unknown }).dataStream;\n if (typeof dataStream !== \"string\" || !dataStream) return \"\";\n\n // Pictures: each CUSTOM_BLOCK token is read back as ``.\n const drawings = ((snapshot as { drawings?: unknown }).drawings ?? {}) as Record<string, DrawingLike>;\n const blocks = new Map<number, string>();\n for (const b of asArray<{ startIndex?: unknown; blockId?: unknown }>((body as { customBlocks?: unknown }).customBlocks)) {\n if (typeof b.startIndex !== \"number\" || typeof b.blockId !== \"string\") continue;\n const drawing = drawings[b.blockId];\n const source = drawing ? imageMarkdownSource(drawing) : null;\n if (!source || !drawing) continue;\n const url = options.imageUrl ? options.imageUrl(source) : source;\n if (!url) continue;\n const label = typeof drawing.title === \"string\" && drawing.title ? drawing.title\n : typeof drawing.description === \"string\" && drawing.description ? drawing.description : \"image\";\n blocks.set(b.startIndex, `![${label.replace(/[[\\]]/g, \"\")}](${url})`);\n }\n\n const runs = asArray<TextRunLike>((body as { textRuns?: unknown }).textRuns)\n .filter((r) => typeof r.st === \"number\" && typeof r.ed === \"number\")\n .sort((a, b) => (a.st as number) - (b.st as number));\n\n const links = new Map<number, string>();\n for (const r of asArray<CustomRangeLike>((body as { customRanges?: unknown }).customRanges)) {\n if (r.rangeType === 0 && typeof r.endIndex === \"number\" && typeof r.properties?.url === \"string\") {\n links.set(r.endIndex, r.properties.url);\n }\n }\n // A slice of the stream with its hyperlink tokens read back as `[text](url)`.\n const readSlice = (from: number, to: number): string => {\n let out = \"\";\n for (let k = from; k < to; k++) {\n const c = dataStream[k];\n if (c === LINK_START) out += \"[\";\n else if (c === LINK_END) out += `](${links.get(k) ?? \"\"})`;\n else if (c === CUSTOM_BLOCK) out += blocks.get(k) ?? \"\";\n else out += c;\n }\n return out;\n };\n\n const lines: string[] = [];\n let cursor = 0;\n let runIndex = 0;\n let tableRows: string[][] | null = null;\n\n // Paragraph boundaries are the `\\r`s in the stream itself — reading them from\n // the stream (rather than trusting `paragraphs`) keeps a snapshot written by\n // Univer's own editor, which may index differently, readable.\n for (let i = 0; i <= dataStream.length; i++) {\n const ch = dataStream[i];\n const atEnd = i === dataStream.length;\n if (!atEnd) {\n if (ch === TABLE_START) {\n tableRows = [];\n cursor = i + 1;\n continue;\n }\n if (tableRows && ch === TABLE_ROW_START) {\n tableRows.push([]);\n cursor = i + 1;\n continue;\n }\n if (tableRows && ch === TABLE_CELL_START) {\n tableRows[tableRows.length - 1]?.push(\"\");\n cursor = i + 1;\n continue;\n }\n if (tableRows && (ch === TABLE_CELL_END || ch === TABLE_ROW_END)) {\n cursor = i + 1;\n continue;\n }\n if (tableRows && ch === TABLE_END) {\n if (tableRows.length) lines.push(\"\", ...tableMarkdown(tableRows), \"\");\n tableRows = null;\n cursor = i + 1;\n continue;\n }\n }\n if (!atEnd && ch !== \"\\r\" && ch !== \"\\n\") continue;\n\n const start = cursor;\n const end = i;\n cursor = i + 1;\n if (end <= start) {\n if (ch === \"\\r\" && !tableRows) lines.push(\"\");\n if (atEnd) break;\n continue;\n }\n\n // Collect the runs overlapping this paragraph.\n const paragraphRuns: TextRunLike[] = [];\n let scan = runIndex;\n while (scan < runs.length && (runs[scan]!.st as number) < end) {\n if ((runs[scan]!.ed as number) > start) paragraphRuns.push(runs[scan]!);\n scan++;\n }\n while (\n runIndex < runs.length &&\n (runs[runIndex]!.ed as number) <= end\n ) {\n runIndex++;\n }\n\n const heading = paragraphHeadingLevel(paragraphRuns);\n let text = \"\";\n let at = start;\n for (const run of paragraphRuns) {\n const rs = Math.max(run.st as number, start);\n const re = Math.min(run.ed as number, end);\n if (rs > at) text += readSlice(at, rs);\n if (re > rs) {\n text += emphasize(readSlice(rs, re), run.ts, heading != null);\n }\n at = Math.max(at, re);\n }\n if (at < end) text += readSlice(at, end);\n\n if (tableRows) {\n // A table cell: its paragraphs join into the one cell, pipes escaped.\n const row = tableRows[tableRows.length - 1];\n if (row?.length) {\n const part = text.trim().replace(/\\|/g, \"\\\\|\");\n const last = row.length - 1;\n row[last] = row[last] ? `${row[last]} ${part}` : part;\n }\n } else {\n lines.push(heading ? `${\"#\".repeat(heading)} ${text.trim()}` : text);\n }\n if (atEnd) break;\n }\n\n // Univer keeps one paragraph per visual line; markdown wants a blank line\n // between blocks (but not inside a list or a table), and consecutive blank\n // paragraphs collapse. The writer (`markdownToUniverDoc`) flattens a list item\n // to a literal \"• \" / \"1. \" prefix and a table row to cells joined by\n // TABLE_CELL_SEP, so those shapes are read back as the markdown they came from.\n type Kind = \"text\" | \"list\" | \"table\";\n const kindOf = (line: string): Kind =>\n /^\\s*(?:\\u2022|[-*+]|\\d+[.)])\\s+/.test(line) ? \"list\" : line.includes(TABLE_CELL_SEP) ? \"table\" : \"text\";\n // A document typed or pasted as markdown keeps a table as literal `| a | b |` lines, one paragraph each, often\n // with an empty paragraph between rows: those rows are ONE table, so the gaps between them close.\n const rawRow = (l: string) => /^\\s*\\|.*\\|\\s*$/.test(l);\n const rows = lines.filter((l, i) => {\n if (l.trim()) return true;\n let a = i - 1;\n while (a >= 0 && !lines[a]!.trim()) a--;\n let z = i + 1;\n while (z < lines.length && !lines[z]!.trim()) z++;\n return !(a >= 0 && z < lines.length && rawRow(lines[a]!) && rawRow(lines[z]!));\n });\n const out: string[] = [];\n let prev: Kind | null = null;\n let tableRow = 0;\n for (const raw of rows) {\n if (!raw.trim()) {\n if (out.length && out[out.length - 1] !== \"\") out.push(\"\");\n prev = null;\n continue;\n }\n let line = raw.replace(/\\s+$/, \"\");\n if (rawRow(line)) {\n if (prev !== \"table\" && out.length && out[out.length - 1] !== \"\") out.push(\"\");\n out.push(line.trim());\n prev = \"table\";\n continue;\n }\n const kind = kindOf(line);\n if (kind === \"list\") {\n line = line.replace(/^(\\s*)\\u2022\\s+/, \"$1- \").replace(/^(\\s*)\\*\\s+/, \"$1- \");\n // Four spaces per nesting level in the document; two read as nesting in markdown.\n line = line.replace(/^( +)/, (m) => \" \".repeat(Math.floor(m.length / 2)));\n }\n if (kind === \"table\") {\n const cells = line.split(/\\s*\\|\\s*/).map((c) => c.trim());\n const isHeader = prev !== \"table\";\n const shown = cells.map((c) => (isHeader ? c.replace(/^\\*\\*(.*)\\*\\*$/, \"$1\") : c));\n line = `| ${shown.join(\" | \")} |`;\n if (isHeader) tableRow = shown.length;\n out.push(...(prev === \"table\" || prev === null || !out.length || out[out.length - 1] === \"\" ? [] : [\"\"]));\n out.push(line);\n if (isHeader) out.push(`|${\" --- |\".repeat(tableRow)}`);\n prev = \"table\";\n continue;\n }\n const tight = kind === \"list\" && prev === \"list\";\n if (out.length && out[out.length - 1] !== \"\" && !tight) out.push(\"\");\n out.push(line);\n prev = kind;\n }\n return out.join(\"\\n\").trim();\n}\n","/**\n * A file from the person's computer -> a new document (Word .docx, Markdown,\n * plain text, HTML). Everything lands as markdown first and goes through THE\n * one writer (`markdownToUniverDoc`), so headings, bold, italic, links and\n * lists keep their shape. Parse first: a file we cannot read never leaves an\n * empty document behind.\n */\n\"use client\";\n\nimport { readZip } from \"@ai-matrx/alchemy/operate/zip\";\n\nimport { createDocument, saveDocumentSnapshot, deleteDocument } from \"./document-service\";\nimport { deriveDocumentName, markdownToUniverDoc } from \"./markdown-to-univer-doc\";\nimport { sanitizeUniverDocSnapshot } from \"./sanitize-univer-doc-snapshot\";\n\nexport const DOCUMENT_UPLOAD_ACCEPT = \".docx,.md,.markdown,.txt,.html,.htm\";\n\ntype Source = \"imported_docx\" | \"imported_md\" | \"imported_txt\";\n\nexport function documentFileKind(name: string): \"docx\" | \"md\" | \"txt\" | \"html\" | null {\n const ext = name.toLowerCase().split(\".\").pop() ?? \"\";\n if (ext === \"docx\") return \"docx\";\n if (ext === \"md\" || ext === \"markdown\") return \"md\";\n if (ext === \"txt\") return \"txt\";\n if (ext === \"html\" || ext === \"htm\") return \"html\";\n return null;\n}\n\nconst W = \"http://schemas.openxmlformats.org/wordprocessingml/2006/main\";\n\n/** Word's document.xml -> markdown: headings, bold/italic runs, list paragraphs, tables as rows. */\nexport function docxXmlToMarkdown(xml: string): string {\n const doc = new DOMParser().parseFromString(xml, \"application/xml\");\n const body = doc.getElementsByTagNameNS(W, \"body\")[0];\n if (!body) throw new Error(\"This Word file has no document body.\");\n const attr = (el: Element | undefined, name: string) => el?.getAttributeNS(W, name) ?? el?.getAttribute(`w:${name}`) ?? null;\n const first = (el: Element, name: string) => el.getElementsByTagNameNS(W, name)[0] as Element | undefined;\n const on = (props: Element | undefined, name: string) => {\n const el = props ? first(props, name) : undefined;\n return !!el && attr(el, \"val\") !== \"0\" && attr(el, \"val\") !== \"false\";\n };\n const paragraph = (p: Element): string => {\n let text = \"\";\n for (const r of Array.from(p.getElementsByTagNameNS(W, \"r\"))) {\n const t = Array.from(r.childNodes)\n .map((n) => {\n const el = n as Element;\n if (el.localName === \"t\") return el.textContent ?? \"\";\n if (el.localName === \"tab\") return \"\\t\";\n if (el.localName === \"br\") return \" \\n\";\n return \"\";\n })\n .join(\"\");\n if (!t) continue;\n const rPr = first(r, \"rPr\");\n const bold = on(rPr, \"b\");\n const italic = on(rPr, \"i\");\n const lead = t.match(/^\\s*/)?.[0] ?? \"\";\n const trail = t.match(/\\s*$/)?.[0] ?? \"\";\n const core = t.trim();\n text += core ? `${lead}${bold ? \"**\" : \"\"}${italic ? \"*\" : \"\"}${core}${italic ? \"*\" : \"\"}${bold ? \"**\" : \"\"}${trail}` : t;\n }\n const pPr = first(p, \"pPr\");\n const style = attr(pPr ? first(pPr, \"pStyle\") : undefined, \"val\") ?? \"\";\n const heading = style.match(/^(?:Heading|heading)\\s?(\\d)$/)?.[1] ?? (style === \"Title\" ? \"1\" : null);\n if (heading && text.trim()) return `${\"#\".repeat(Math.min(6, Number(heading)))} ${text.replace(/\\*+/g, \"\").trim()}`;\n if (pPr && first(pPr, \"numPr\") && text.trim()) {\n const level = Number(attr(first(first(pPr, \"numPr\")!, \"ilvl\"), \"val\") ?? 0);\n return `${\" \".repeat(level)}- ${text.trim()}`;\n }\n return text;\n };\n const out: string[] = [];\n for (const child of Array.from(body.children)) {\n if (child.localName === \"p\") out.push(paragraph(child));\n else if (child.localName === \"tbl\") {\n const rows = Array.from(child.getElementsByTagNameNS(W, \"tr\")).map((tr) =>\n Array.from(tr.getElementsByTagNameNS(W, \"tc\")).map((tc) =>\n Array.from(tc.getElementsByTagNameNS(W, \"p\")).map(paragraph).join(\" \").replace(/\\|/g, \"\\\\|\").trim(),\n ),\n );\n if (rows.length) {\n out.push(\"\", `| ${rows[0]!.join(\" | \")} |`, `|${\" --- |\".repeat(rows[0]!.length)}`);\n for (const row of rows.slice(1)) out.push(`| ${row.join(\" | \")} |`);\n out.push(\"\");\n }\n }\n }\n // A list run stays tight; everything else is its own block.\n return out\n .reduce<string[]>((acc, line, i) => {\n const prevList = /^\\s*- /.test(out[i - 1] ?? \"\");\n if (i > 0 && !(prevList && /^\\s*- /.test(line))) acc.push(\"\");\n acc.push(line);\n return acc;\n }, [])\n .join(\"\\n\")\n .replace(/\\n{3,}/g, \"\\n\\n\")\n .trim();\n}\n\n/** HTML -> markdown for the blocks a document keeps (headings, paragraphs, lists, emphasis, links). */\nexport function htmlToDocumentMarkdown(html: string): string {\n const doc = new DOMParser().parseFromString(html, \"text/html\");\n const inline = (node: Node): string => {\n if (node.nodeType === 3) return (node.textContent ?? \"\").replace(/\\s+/g, \" \");\n if (node.nodeType !== 1) return \"\";\n const el = node as Element;\n const inner = Array.from(el.childNodes).map(inline).join(\"\");\n switch (el.tagName.toLowerCase()) {\n case \"strong\":\n case \"b\":\n return inner.trim() ? `**${inner.trim()}**` : \"\";\n case \"em\":\n case \"i\":\n return inner.trim() ? `*${inner.trim()}*` : \"\";\n case \"a\": {\n const href = el.getAttribute(\"href\");\n return href && /^https?:|^mailto:/i.test(href) ? `[${inner.trim()}](${href})` : inner;\n }\n case \"br\":\n return \" \\n\";\n case \"code\":\n return `\\`${inner}\\``;\n default:\n return inner;\n }\n };\n const out: string[] = [];\n const block = (el: Element, depth = 0): void => {\n const tag = el.tagName.toLowerCase();\n const h = tag.match(/^h([1-6])$/);\n if (h) {\n out.push(`${\"#\".repeat(Number(h[1]))} ${inline(el).trim()}`);\n return;\n }\n if (tag === \"p\" || tag === \"blockquote\") {\n out.push(inline(el).trim());\n return;\n }\n if (tag === \"pre\") {\n out.push(\"```\", el.textContent ?? \"\", \"```\");\n return;\n }\n if (tag === \"ul\" || tag === \"ol\") {\n Array.from(el.children).forEach((li, n) => {\n if (li.tagName.toLowerCase() !== \"li\") return;\n const own = Array.from(li.childNodes).filter((c) => !(c.nodeType === 1 && /^(ul|ol)$/i.test((c as Element).tagName)));\n out.push(`${\" \".repeat(depth)}${tag === \"ol\" ? `${n + 1}.` : \"-\"} ${own.map(inline).join(\"\").trim()}`);\n Array.from(li.children)\n .filter((c) => /^(ul|ol)$/i.test(c.tagName))\n .forEach((c) => block(c, depth + 1));\n });\n return;\n }\n if (tag === \"script\" || tag === \"style\" || tag === \"head\") return;\n const kids = Array.from(el.children);\n if (kids.length === 0) {\n const t = inline(el).trim();\n if (t) out.push(t);\n return;\n }\n kids.forEach((k) => block(k, depth));\n };\n block(doc.body);\n return out\n .map((l) => l.replace(/\\s+$/, \"\"))\n .reduce<string[]>((acc, line, i) => {\n const list = (s: string | undefined) => /^\\s*(-|\\d+\\.) /.test(s ?? \"\");\n if (i > 0 && !(list(out[i - 1]) && list(line))) acc.push(\"\");\n acc.push(line);\n return acc;\n }, [])\n .join(\"\\n\")\n .replace(/\\n{3,}/g, \"\\n\\n\")\n .trim();\n}\n\nexport async function documentFileToMarkdown(file: File): Promise<{ markdown: string; source: Source }> {\n const kind = documentFileKind(file.name);\n if (kind === \"docx\") {\n const zip = await readZip(file);\n const entry = zip.get(\"word/document.xml\");\n if (!entry) throw new Error(\"This is not a Word document (.docx).\");\n const xml = await entry.text();\n return { markdown: docxXmlToMarkdown(xml), source: \"imported_docx\" };\n }\n const text = await file.text();\n if (kind === \"md\") return { markdown: text, source: \"imported_md\" };\n if (kind === \"html\") return { markdown: htmlToDocumentMarkdown(text), source: \"imported_md\" };\n if (kind === \"txt\") {\n // Plain text: each line is its own paragraph; nothing in it is read as markdown.\n const escaped = text\n .replace(/\\r\\n?/g, \"\\n\")\n .split(\"\\n\")\n .map((l) => l.replace(/([\\\\`*_[\\]#>|~])/g, \"\\\\$1\"))\n .join(\"\\n\\n\");\n return { markdown: escaped, source: \"imported_txt\" };\n }\n throw new Error(\"Upload a Word (.docx), Markdown, text or HTML file.\");\n}\n\n/** Create a document from an uploaded file; returns its id. */\nexport async function importDocumentFile(file: File, organizationId: string): Promise<{ id: string; name: string }> {\n const { markdown, source } = await documentFileToMarkdown(file);\n const name = file.name.replace(/\\.[^.]+$/, \"\").trim() || deriveDocumentName(markdown);\n const created = await createDocument({ name, source, organizationId, description: `Imported from ${file.name}` });\n if (!created.success) throw new Error(`Could not create the document: ${created.error}`);\n const id = created.data.id;\n const snapshot = sanitizeUniverDocSnapshot(markdownToUniverDoc(markdown, name), id);\n const saved = await saveDocumentSnapshot({ documentId: id, snapshot, origin: \"imported\", label: `Imported from ${file.name}` });\n if (!saved.success) {\n await deleteDocument(id);\n throw new Error(`Could not save the imported content: ${saved.error}`);\n }\n return { id, name };\n}\n","/**\n * File → Download → EPUB / OpenDocument (.odt) / Rich Text (.rtf).\n * EPUB the print package's EPUB engine, straight from the live text.\n * ODT/RTF the same engine writes the .docx, then aidream's LibreOffice route\n * (`POST /office/convert-document`) converts it. Nothing is stored.\n * Engine, converter and saver are injected so the forcing test runs the real logic.\n */\n\nimport { getDocsHost } from \"./ports\";\n\nexport type OpenFormat = \"epub\" | \"odt\" | \"rtf\";\n\nexport const OPEN_FORMATS: Record<OpenFormat, { label: string; extension: string; mime: string }> = {\n epub: { label: \"EPUB\", extension: \"epub\", mime: \"application/epub+zip\" },\n odt: { label: \"OpenDocument\", extension: \"odt\", mime: \"application/vnd.oasis.opendocument.text\" },\n rtf: { label: \"Rich Text\", extension: \"rtf\", mime: \"application/rtf\" },\n};\n\nexport interface OpenFormatDeps {\n engine: (markdown: string, format: \"docx\" | \"epub\", fileName: string) => Promise<{ bytes: Uint8Array; fileName: string }>;\n convert: (docx: Uint8Array, fileName: string, target: \"odt\" | \"rtf\") => Promise<Uint8Array>;\n save: (fileName: string, bytes: Uint8Array, mime: string) => void;\n}\n\nfunction baseName(title: string | null | undefined): string {\n const base = (title ?? \"\").replace(/[\\\\/:*?\"<>|\\u0000-\\u001f]+/g, \" \").replace(/\\s+/g, \" \").trim().slice(0, 120);\n return base || \"document\";\n}\n\nexport async function exportOpenFormat(markdown: string, format: OpenFormat, title: string | null | undefined, deps: OpenFormatDeps): Promise<string> {\n const name = baseName(title);\n const spec = OPEN_FORMATS[format];\n const fileName = `${name}.${spec.extension}`;\n if (format === \"epub\") {\n const out = await deps.engine(markdown, \"epub\", name);\n deps.save(fileName, out.bytes, spec.mime);\n return fileName;\n }\n const docx = await deps.engine(markdown, \"docx\", name);\n const bytes = await deps.convert(docx.bytes, docx.fileName, format);\n if (!bytes.byteLength) throw new Error(`The ${spec.label} converter returned an empty file.`);\n deps.save(fileName, bytes, spec.mime);\n return fileName;\n}\n\nexport async function downloadOpenFormat(markdown: string, format: OpenFormat, title: string | null | undefined): Promise<string> {\n return exportOpenFormat(markdown, format, title, {\n engine: async (md, fmt, fileName) => {\n const { exportDocument } = await import(\"@ai-matrx/print/document\");\n const exp = await exportDocument(md, fmt, { fileName });\n return { bytes: exp.bytes, fileName: exp.fileName };\n },\n convert: async (docx, fileName, target) => {\n const form = new FormData();\n form.append(\"file\", new Blob([docx as BlobPart]), fileName.endsWith(\".docx\") ? fileName : `${fileName}.docx`);\n form.append(\"target\", target);\n const res = await getDocsHost().requestRaw(\"/office/convert-document\", { method: \"POST\", body: form });\n return new Uint8Array(await res.arrayBuffer());\n },\n save: (fileName, bytes, mime) => {\n const url = URL.createObjectURL(new Blob([bytes as BlobPart], { type: mime }));\n const link = document.createElement(\"a\");\n link.href = url;\n link.download = fileName;\n link.rel = \"noopener\";\n document.body.appendChild(link);\n link.click();\n link.remove();\n setTimeout(() => URL.revokeObjectURL(url), 30_000);\n },\n });\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["/home/runner/work/aidream/aidream/apps/shared/office/dist/docs-io.cjs","../src/docs/univer-doc-to-markdown.ts","../src/docs/import-document-file.ts","../src/docs/export-open-formats.ts"],"names":["out"],"mappings":"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","file":"/home/runner/work/aidream/aidream/apps/shared/office/dist/docs-io.cjs","sourcesContent":[null,"/**\n * univerDocToMarkdown — the reverse of `markdown-to-univer-doc.ts`.\n *\n * Why it exists: the canvas pane for a cloud document (`udt_document`) offers a\n * `Source` tab, and the rule in `features/canvas/core/canvasSource.ts` is that\n * Source shows THE ITEM'S OWN SOURCE — for a document, its markdown — never the\n * redux envelope. A document's content lives as an opaque Univer\n * `IDocumentData` snapshot in `workbench.udt_document_snapshots`, so something\n * has to read it back out. This is that something, and it is the ONLY one:\n * never re-implement a second snapshot reader.\n *\n * What it reads (the half `markdownToUniverDoc` writes):\n * - `body.dataStream` — the text; `\\r` terminates a paragraph, a trailing\n * `\\n` terminates the section.\n * - `body.paragraphs` — one entry per `\\r` (`startIndex` = the `\\r` offset).\n * - `body.textRuns` — `{ st, ed, ts }` style overlays. `ts.bl` + a heading\n * font size is how `markdownToUniverDoc` encodes\n * `#`..`######`, so the level is recovered from `fs`.\n *\n * Fidelity is deliberately MODEST and honest: headings, bold, italic,\n * strikethrough and inline/monospace code round-trip; everything else comes\n * back as the plain text the user actually sees. A document typed by hand in\n * Univer never had markdown syntax in the first place — printing its text is\n * the truthful answer, not a reconstruction.\n *\n * PURE — no React, no Redux, no IO.\n */\n\nconst TABLE_CELL_SEP = \" | \";\n\n// Univer's DataStreamTreeTokenType control characters (literals: no Univer runtime here).\nconst TABLE_START = \"\\x1A\";\nconst TABLE_ROW_START = \"\\x1B\";\nconst TABLE_CELL_START = \"\\x1C\";\nconst TABLE_CELL_END = \"\\x1D\";\nconst TABLE_ROW_END = \"\\x0E\";\nconst TABLE_END = \"\\x0F\";\nconst LINK_START = \"\\x1F\";\nconst LINK_END = \"\\x1E\";\n\ninterface CustomRangeLike {\n startIndex?: number;\n endIndex?: number;\n rangeType?: number;\n properties?: { url?: unknown };\n}\n\n/** The Univer-native shapes (real tables, hyperlink ranges) written back as markdown. */\nfunction tableMarkdown(rows: string[][]): string[] {\n const width = Math.max(1, ...rows.map((r) => r.length));\n const cell = (r: string[], i: number) => (r[i] ?? \"\").replace(/^\\*\\*(.*)\\*\\*$/, \"$1\");\n const line = (r: string[], plain: boolean) =>\n `| ${Array.from({ length: width }, (_, i) => (plain ? cell(r, i) : (r[i] ?? \"\"))).join(\" | \")} |`;\n const out = [line(rows[0]!, true), `|${\" --- |\".repeat(width)}`];\n for (const r of rows.slice(1)) out.push(line(r, false));\n return out;\n}\n\ninterface TextRunLike {\n st?: number;\n ed?: number;\n ts?: {\n bl?: number;\n it?: number;\n ff?: string;\n fs?: number;\n st?: { s?: number } | number;\n };\n}\n\n/** Inverse of markdown-to-univer-doc's HEADING_FONT_SIZE. */\nexport const HEADING_LEVEL_BY_FONT_SIZE: Record<number, number> = {\n 26: 1,\n 22: 2,\n 18: 3,\n 16: 4,\n 14: 5,\n 13: 6,\n};\n\nfunction asArray<T>(value: unknown): T[] {\n return Array.isArray(value) ? (value as T[]) : [];\n}\n\nconst TRUE_ENOUGH = (v: unknown): boolean => v === 1 || v === true;\n\nfunction isStruck(ts: TextRunLike[\"ts\"]): boolean {\n if (!ts) return false;\n const st = ts.st;\n if (typeof st === \"number\") return TRUE_ENOUGH(st);\n return TRUE_ENOUGH(st?.s);\n}\n\n/** The style that applies to the whole paragraph, if the runs agree on one. */\nfunction paragraphHeadingLevel(runs: TextRunLike[]): number | null {\n for (const run of runs) {\n const fs = run.ts?.fs;\n if (typeof fs === \"number\" && TRUE_ENOUGH(run.ts?.bl)) {\n const level = HEADING_LEVEL_BY_FONT_SIZE[fs];\n if (level) return level;\n }\n }\n return null;\n}\n\nfunction emphasize(text: string, ts: TextRunLike[\"ts\"], inHeading: boolean): string {\n if (!text) return text;\n // Never double-mark: a heading's own runs are bold by construction.\n const leading = text.match(/^\\s*/)?.[0] ?? \"\";\n const trailing = text.match(/\\s*$/)?.[0] ?? \"\";\n let core = text.slice(leading.length, text.length - trailing.length);\n if (!core) return text;\n\n const mono = typeof ts?.ff === \"string\" && /mono/i.test(ts.ff);\n if (mono) core = `\\`${core}\\``;\n else {\n if (TRUE_ENOUGH(ts?.it)) core = `*${core}*`;\n if (TRUE_ENOUGH(ts?.bl) && !inHeading) core = `**${core}**`;\n }\n if (isStruck(ts)) core = `~~${core}~~`;\n return `${leading}${core}${trailing}`;\n}\n\n// An inline picture: one CUSTOM_BLOCK token in the stream, its drawing in `snapshot.drawings`.\nconst CUSTOM_BLOCK = \"\\b\";\n\ninterface DrawingLike {\n source?: unknown;\n title?: unknown;\n description?: unknown;\n docTransform?: { size?: { width?: unknown; height?: unknown } };\n}\n\n/**\n * A picture's markdown source. Our own pictures are `matrx-file:<id>`; the\n * drawn size rides as a `#WxH` fragment so a round trip keeps the shape.\n */\nexport function imageMarkdownSource(drawing: DrawingLike): string | null {\n const source = typeof drawing.source === \"string\" ? drawing.source : \"\";\n if (!source || source.startsWith(\"data:\")) return null;\n const w = Number(drawing.docTransform?.size?.width);\n const h = Number(drawing.docTransform?.size?.height);\n return Number.isFinite(w) && Number.isFinite(h) && w > 0 && h > 0 && !source.includes(\"#\")\n ? `${source}#${Math.round(w)}x${Math.round(h)}`\n : source;\n}\n\n/** Every picture in a snapshot, as its markdown source (`matrx-file:<id>#WxH` or a URL). */\nexport function documentImageSources(snapshot: unknown): string[] {\n const drawings = ((snapshot as { drawings?: unknown } | null)?.drawings ?? {}) as Record<string, DrawingLike>;\n const body = (snapshot as { body?: { customBlocks?: unknown } } | null)?.body;\n return asArray<{ blockId?: unknown }>(body?.customBlocks)\n .map((b) => (typeof b.blockId === \"string\" && drawings[b.blockId] ? imageMarkdownSource(drawings[b.blockId]!) : null))\n .filter((s): s is string => !!s);\n}\n\nexport interface DocToMarkdownOptions {\n /**\n * Where a picture's markdown points. Return null to leave it out (the caller\n * names what was left out). Default: the stored source.\n */\n imageUrl?: (source: string) => string | null;\n}\n\n/**\n * Read a Univer document snapshot back as markdown. Returns `\"\"` for an empty\n * or unreadable snapshot so the caller can say \"no source\" honestly rather than\n * printing an envelope.\n */\nexport function univerDocToMarkdown(snapshot: unknown, options: DocToMarkdownOptions = {}): string {\n if (!snapshot || typeof snapshot !== \"object\") return \"\";\n const body = (snapshot as { body?: unknown }).body;\n if (!body || typeof body !== \"object\") return \"\";\n\n const dataStream = (body as { dataStream?: unknown }).dataStream;\n if (typeof dataStream !== \"string\" || !dataStream) return \"\";\n\n // Pictures: each CUSTOM_BLOCK token is read back as ``.\n const drawings = ((snapshot as { drawings?: unknown }).drawings ?? {}) as Record<string, DrawingLike>;\n const blocks = new Map<number, string>();\n for (const b of asArray<{ startIndex?: unknown; blockId?: unknown }>((body as { customBlocks?: unknown }).customBlocks)) {\n if (typeof b.startIndex !== \"number\" || typeof b.blockId !== \"string\") continue;\n const drawing = drawings[b.blockId];\n const source = drawing ? imageMarkdownSource(drawing) : null;\n if (!source || !drawing) continue;\n const url = options.imageUrl ? options.imageUrl(source) : source;\n if (!url) continue;\n const label = typeof drawing.title === \"string\" && drawing.title ? drawing.title\n : typeof drawing.description === \"string\" && drawing.description ? drawing.description : \"image\";\n blocks.set(b.startIndex, `![${label.replace(/[[\\]]/g, \"\")}](${url})`);\n }\n\n const runs = asArray<TextRunLike>((body as { textRuns?: unknown }).textRuns)\n .filter((r) => typeof r.st === \"number\" && typeof r.ed === \"number\")\n .sort((a, b) => (a.st as number) - (b.st as number));\n\n const links = new Map<number, string>();\n for (const r of asArray<CustomRangeLike>((body as { customRanges?: unknown }).customRanges)) {\n if (r.rangeType === 0 && typeof r.endIndex === \"number\" && typeof r.properties?.url === \"string\") {\n links.set(r.endIndex, r.properties.url);\n }\n }\n // A slice of the stream with its hyperlink tokens read back as `[text](url)`.\n const readSlice = (from: number, to: number): string => {\n let out = \"\";\n for (let k = from; k < to; k++) {\n const c = dataStream[k];\n if (c === LINK_START) out += \"[\";\n else if (c === LINK_END) out += `](${links.get(k) ?? \"\"})`;\n else if (c === CUSTOM_BLOCK) out += blocks.get(k) ?? \"\";\n else out += c;\n }\n return out;\n };\n\n const lines: string[] = [];\n let cursor = 0;\n let runIndex = 0;\n let tableRows: string[][] | null = null;\n\n // Paragraph boundaries are the `\\r`s in the stream itself — reading them from\n // the stream (rather than trusting `paragraphs`) keeps a snapshot written by\n // Univer's own editor, which may index differently, readable.\n for (let i = 0; i <= dataStream.length; i++) {\n const ch = dataStream[i];\n const atEnd = i === dataStream.length;\n if (!atEnd) {\n if (ch === TABLE_START) {\n tableRows = [];\n cursor = i + 1;\n continue;\n }\n if (tableRows && ch === TABLE_ROW_START) {\n tableRows.push([]);\n cursor = i + 1;\n continue;\n }\n if (tableRows && ch === TABLE_CELL_START) {\n tableRows[tableRows.length - 1]?.push(\"\");\n cursor = i + 1;\n continue;\n }\n if (tableRows && (ch === TABLE_CELL_END || ch === TABLE_ROW_END)) {\n cursor = i + 1;\n continue;\n }\n if (tableRows && ch === TABLE_END) {\n if (tableRows.length) lines.push(\"\", ...tableMarkdown(tableRows), \"\");\n tableRows = null;\n cursor = i + 1;\n continue;\n }\n }\n if (!atEnd && ch !== \"\\r\" && ch !== \"\\n\") continue;\n\n const start = cursor;\n const end = i;\n cursor = i + 1;\n if (end <= start) {\n if (ch === \"\\r\" && !tableRows) lines.push(\"\");\n if (atEnd) break;\n continue;\n }\n\n // Collect the runs overlapping this paragraph.\n const paragraphRuns: TextRunLike[] = [];\n let scan = runIndex;\n while (scan < runs.length && (runs[scan]!.st as number) < end) {\n if ((runs[scan]!.ed as number) > start) paragraphRuns.push(runs[scan]!);\n scan++;\n }\n while (\n runIndex < runs.length &&\n (runs[runIndex]!.ed as number) <= end\n ) {\n runIndex++;\n }\n\n const heading = paragraphHeadingLevel(paragraphRuns);\n let text = \"\";\n let at = start;\n for (const run of paragraphRuns) {\n const rs = Math.max(run.st as number, start);\n const re = Math.min(run.ed as number, end);\n if (rs > at) text += readSlice(at, rs);\n if (re > rs) {\n text += emphasize(readSlice(rs, re), run.ts, heading != null);\n }\n at = Math.max(at, re);\n }\n if (at < end) text += readSlice(at, end);\n\n if (tableRows) {\n // A table cell: its paragraphs join into the one cell, pipes escaped.\n const row = tableRows[tableRows.length - 1];\n if (row?.length) {\n const part = text.trim().replace(/\\|/g, \"\\\\|\");\n const last = row.length - 1;\n row[last] = row[last] ? `${row[last]} ${part}` : part;\n }\n } else {\n lines.push(heading ? `${\"#\".repeat(heading)} ${text.trim()}` : text);\n }\n if (atEnd) break;\n }\n\n // Univer keeps one paragraph per visual line; markdown wants a blank line\n // between blocks (but not inside a list or a table), and consecutive blank\n // paragraphs collapse. The writer (`markdownToUniverDoc`) flattens a list item\n // to a literal \"• \" / \"1. \" prefix and a table row to cells joined by\n // TABLE_CELL_SEP, so those shapes are read back as the markdown they came from.\n type Kind = \"text\" | \"list\" | \"table\";\n const kindOf = (line: string): Kind =>\n /^\\s*(?:\\u2022|[-*+]|\\d+[.)])\\s+/.test(line) ? \"list\" : line.includes(TABLE_CELL_SEP) ? \"table\" : \"text\";\n // A document typed or pasted as markdown keeps a table as literal `| a | b |` lines, one paragraph each, often\n // with an empty paragraph between rows: those rows are ONE table, so the gaps between them close.\n const rawRow = (l: string) => /^\\s*\\|.*\\|\\s*$/.test(l);\n const rows = lines.filter((l, i) => {\n if (l.trim()) return true;\n let a = i - 1;\n while (a >= 0 && !lines[a]!.trim()) a--;\n let z = i + 1;\n while (z < lines.length && !lines[z]!.trim()) z++;\n return !(a >= 0 && z < lines.length && rawRow(lines[a]!) && rawRow(lines[z]!));\n });\n const out: string[] = [];\n let prev: Kind | null = null;\n let tableRow = 0;\n for (const raw of rows) {\n if (!raw.trim()) {\n if (out.length && out[out.length - 1] !== \"\") out.push(\"\");\n prev = null;\n continue;\n }\n let line = raw.replace(/\\s+$/, \"\");\n if (rawRow(line)) {\n if (prev !== \"table\" && out.length && out[out.length - 1] !== \"\") out.push(\"\");\n out.push(line.trim());\n prev = \"table\";\n continue;\n }\n const kind = kindOf(line);\n if (kind === \"list\") {\n line = line.replace(/^(\\s*)\\u2022\\s+/, \"$1- \").replace(/^(\\s*)\\*\\s+/, \"$1- \");\n // Four spaces per nesting level in the document; two read as nesting in markdown.\n line = line.replace(/^( +)/, (m) => \" \".repeat(Math.floor(m.length / 2)));\n }\n if (kind === \"table\") {\n const cells = line.split(/\\s*\\|\\s*/).map((c) => c.trim());\n const isHeader = prev !== \"table\";\n const shown = cells.map((c) => (isHeader ? c.replace(/^\\*\\*(.*)\\*\\*$/, \"$1\") : c));\n line = `| ${shown.join(\" | \")} |`;\n if (isHeader) tableRow = shown.length;\n out.push(...(prev === \"table\" || prev === null || !out.length || out[out.length - 1] === \"\" ? [] : [\"\"]));\n out.push(line);\n if (isHeader) out.push(`|${\" --- |\".repeat(tableRow)}`);\n prev = \"table\";\n continue;\n }\n const tight = kind === \"list\" && prev === \"list\";\n if (out.length && out[out.length - 1] !== \"\" && !tight) out.push(\"\");\n out.push(line);\n prev = kind;\n }\n return out.join(\"\\n\").trim();\n}\n","/**\n * A file from the person's computer -> a new document (Word .docx, Markdown,\n * plain text, HTML). Everything lands as markdown first and goes through THE\n * one writer (`markdownToUniverDoc`), so headings, bold, italic, links and\n * lists keep their shape. Parse first: a file we cannot read never leaves an\n * empty document behind.\n */\n\"use client\";\n\nimport { readZip } from \"@ai-matrx/alchemy/operate/zip\";\n\nimport { createDocument, saveDocumentSnapshot, deleteDocument } from \"./document-service\";\nimport { deriveDocumentName, markdownToUniverDoc } from \"./markdown-to-univer-doc\";\nimport { sanitizeUniverDocSnapshot } from \"./sanitize-univer-doc-snapshot\";\n\nexport const DOCUMENT_UPLOAD_ACCEPT = \".docx,.md,.markdown,.txt,.html,.htm\";\n\ntype Source = \"imported_docx\" | \"imported_md\" | \"imported_txt\";\n\nexport function documentFileKind(name: string): \"docx\" | \"md\" | \"txt\" | \"html\" | null {\n const ext = name.toLowerCase().split(\".\").pop() ?? \"\";\n if (ext === \"docx\") return \"docx\";\n if (ext === \"md\" || ext === \"markdown\") return \"md\";\n if (ext === \"txt\") return \"txt\";\n if (ext === \"html\" || ext === \"htm\") return \"html\";\n return null;\n}\n\nconst W = \"http://schemas.openxmlformats.org/wordprocessingml/2006/main\";\n\n/** Rows of cell text -> ONE markdown table block (header, delimiter, body rows together). */\nfunction markdownTable(rows: string[][]): string {\n const width = Math.max(...rows.map((r) => r.length));\n const line = (r: string[]) => `| ${Array.from({ length: width }, (_, i) => r[i] ?? \"\").join(\" | \")} |`;\n return [line(rows[0]!), `|${\" --- |\".repeat(width)}`, ...rows.slice(1).map(line)].join(\"\\n\");\n}\n\n/** Word's document.xml -> markdown: headings, bold/italic runs, list paragraphs, tables as rows. */\nexport function docxXmlToMarkdown(xml: string): string {\n const doc = new DOMParser().parseFromString(xml, \"application/xml\");\n const body = doc.getElementsByTagNameNS(W, \"body\")[0];\n if (!body) throw new Error(\"This Word file has no document body.\");\n const attr = (el: Element | undefined, name: string) => el?.getAttributeNS(W, name) ?? el?.getAttribute(`w:${name}`) ?? null;\n const first = (el: Element, name: string) => el.getElementsByTagNameNS(W, name)[0] as Element | undefined;\n const on = (props: Element | undefined, name: string) => {\n const el = props ? first(props, name) : undefined;\n return !!el && attr(el, \"val\") !== \"0\" && attr(el, \"val\") !== \"false\";\n };\n const paragraph = (p: Element): string => {\n let text = \"\";\n for (const r of Array.from(p.getElementsByTagNameNS(W, \"r\"))) {\n const t = Array.from(r.childNodes)\n .map((n) => {\n const el = n as Element;\n if (el.localName === \"t\") return el.textContent ?? \"\";\n if (el.localName === \"tab\") return \"\\t\";\n if (el.localName === \"br\") return \" \\n\";\n return \"\";\n })\n .join(\"\");\n if (!t) continue;\n const rPr = first(r, \"rPr\");\n const bold = on(rPr, \"b\");\n const italic = on(rPr, \"i\");\n const lead = t.match(/^\\s*/)?.[0] ?? \"\";\n const trail = t.match(/\\s*$/)?.[0] ?? \"\";\n const core = t.trim();\n text += core ? `${lead}${bold ? \"**\" : \"\"}${italic ? \"*\" : \"\"}${core}${italic ? \"*\" : \"\"}${bold ? \"**\" : \"\"}${trail}` : t;\n }\n const pPr = first(p, \"pPr\");\n const style = attr(pPr ? first(pPr, \"pStyle\") : undefined, \"val\") ?? \"\";\n const heading = style.match(/^(?:Heading|heading)\\s?(\\d)$/)?.[1] ?? (style === \"Title\" ? \"1\" : null);\n if (heading && text.trim()) return `${\"#\".repeat(Math.min(6, Number(heading)))} ${text.replace(/\\*+/g, \"\").trim()}`;\n if (pPr && first(pPr, \"numPr\") && text.trim()) {\n const level = Number(attr(first(first(pPr, \"numPr\")!, \"ilvl\"), \"val\") ?? 0);\n return `${\" \".repeat(level)}- ${text.trim()}`;\n }\n return text;\n };\n const out: string[] = [];\n for (const child of Array.from(body.children)) {\n if (child.localName === \"p\") out.push(paragraph(child));\n else if (child.localName === \"tbl\") {\n const rows = Array.from(child.getElementsByTagNameNS(W, \"tr\")).map((tr) =>\n Array.from(tr.getElementsByTagNameNS(W, \"tc\")).map((tc) =>\n Array.from(tc.getElementsByTagNameNS(W, \"p\")).map(paragraph).join(\" \").replace(/\\|/g, \"\\\\|\").trim(),\n ),\n );\n if (rows.length) out.push(markdownTable(rows));\n }\n }\n // A list run stays tight; everything else is its own block.\n return out\n .reduce<string[]>((acc, line, i) => {\n const prevList = /^\\s*- /.test(out[i - 1] ?? \"\");\n if (i > 0 && !(prevList && /^\\s*- /.test(line))) acc.push(\"\");\n acc.push(line);\n return acc;\n }, [])\n .join(\"\\n\")\n .replace(/\\n{3,}/g, \"\\n\\n\")\n .trim();\n}\n\n/** HTML -> markdown for the blocks a document keeps (headings, paragraphs, lists, emphasis, links). */\nexport function htmlToDocumentMarkdown(html: string): string {\n const doc = new DOMParser().parseFromString(html, \"text/html\");\n const inline = (node: Node): string => {\n if (node.nodeType === 3) return (node.textContent ?? \"\").replace(/\\s+/g, \" \");\n if (node.nodeType !== 1) return \"\";\n const el = node as Element;\n const inner = Array.from(el.childNodes).map(inline).join(\"\");\n switch (el.tagName.toLowerCase()) {\n case \"strong\":\n case \"b\":\n return inner.trim() ? `**${inner.trim()}**` : \"\";\n case \"em\":\n case \"i\":\n return inner.trim() ? `*${inner.trim()}*` : \"\";\n case \"a\": {\n const href = el.getAttribute(\"href\");\n return href && /^https?:|^mailto:/i.test(href) ? `[${inner.trim()}](${href})` : inner;\n }\n case \"br\":\n return \" \\n\";\n case \"code\":\n return `\\`${inner}\\``;\n default:\n return inner;\n }\n };\n const out: string[] = [];\n const block = (el: Element, depth = 0): void => {\n const tag = el.tagName.toLowerCase();\n const h = tag.match(/^h([1-6])$/);\n if (h) {\n out.push(`${\"#\".repeat(Number(h[1]))} ${inline(el).trim()}`);\n return;\n }\n if (tag === \"p\" || tag === \"blockquote\") {\n out.push(inline(el).trim());\n return;\n }\n if (tag === \"table\") {\n const rows = Array.from(el.querySelectorAll(\"tr\")).map((tr) =>\n Array.from(tr.children)\n .filter((c) => /^(td|th)$/i.test(c.tagName))\n .map((c) => inline(c).replace(/\\|/g, \"\\\\|\").replace(/\\s+/g, \" \").trim()),\n );\n if (rows.length) out.push(markdownTable(rows));\n return;\n }\n if (tag === \"pre\") {\n out.push(\"```\", el.textContent ?? \"\", \"```\");\n return;\n }\n if (tag === \"ul\" || tag === \"ol\") {\n Array.from(el.children).forEach((li, n) => {\n if (li.tagName.toLowerCase() !== \"li\") return;\n const own = Array.from(li.childNodes).filter((c) => !(c.nodeType === 1 && /^(ul|ol)$/i.test((c as Element).tagName)));\n out.push(`${\" \".repeat(depth)}${tag === \"ol\" ? `${n + 1}.` : \"-\"} ${own.map(inline).join(\"\").trim()}`);\n Array.from(li.children)\n .filter((c) => /^(ul|ol)$/i.test(c.tagName))\n .forEach((c) => block(c, depth + 1));\n });\n return;\n }\n if (tag === \"script\" || tag === \"style\" || tag === \"head\") return;\n const kids = Array.from(el.children);\n if (kids.length === 0) {\n const t = inline(el).trim();\n if (t) out.push(t);\n return;\n }\n kids.forEach((k) => block(k, depth));\n };\n block(doc.body);\n return out\n .map((l) => l.replace(/\\s+$/, \"\"))\n .reduce<string[]>((acc, line, i) => {\n const list = (s: string | undefined) => /^\\s*(-|\\d+\\.) /.test(s ?? \"\");\n if (i > 0 && !(list(out[i - 1]) && list(line))) acc.push(\"\");\n acc.push(line);\n return acc;\n }, [])\n .join(\"\\n\")\n .replace(/\\n{3,}/g, \"\\n\\n\")\n .trim();\n}\n\nexport async function documentFileToMarkdown(file: File): Promise<{ markdown: string; source: Source }> {\n const kind = documentFileKind(file.name);\n if (kind === \"docx\") {\n const zip = await readZip(file);\n const entry = zip.get(\"word/document.xml\");\n if (!entry) throw new Error(\"This is not a Word document (.docx).\");\n const xml = await entry.text();\n return { markdown: docxXmlToMarkdown(xml), source: \"imported_docx\" };\n }\n const text = await file.text();\n if (kind === \"md\") return { markdown: text, source: \"imported_md\" };\n if (kind === \"html\") return { markdown: htmlToDocumentMarkdown(text), source: \"imported_md\" };\n if (kind === \"txt\") {\n // Plain text: each line is its own paragraph; nothing in it is read as markdown.\n const escaped = text\n .replace(/\\r\\n?/g, \"\\n\")\n .split(\"\\n\")\n .map((l) => l.replace(/([\\\\`*_[\\]#>|~])/g, \"\\\\$1\"))\n .join(\"\\n\\n\");\n return { markdown: escaped, source: \"imported_txt\" };\n }\n throw new Error(\"Upload a Word (.docx), Markdown, text or HTML file.\");\n}\n\n/** Create a document from an uploaded file; returns its id. */\nexport async function importDocumentFile(file: File, organizationId: string): Promise<{ id: string; name: string }> {\n const { markdown, source } = await documentFileToMarkdown(file);\n const name = file.name.replace(/\\.[^.]+$/, \"\").trim() || deriveDocumentName(markdown);\n const created = await createDocument({ name, source, organizationId, description: `Imported from ${file.name}` });\n if (!created.success) throw new Error(`Could not create the document: ${created.error}`);\n const id = created.data.id;\n const snapshot = sanitizeUniverDocSnapshot(markdownToUniverDoc(markdown, name), id);\n const saved = await saveDocumentSnapshot({ documentId: id, snapshot, origin: \"imported\", label: `Imported from ${file.name}` });\n if (!saved.success) {\n await deleteDocument(id);\n throw new Error(`Could not save the imported content: ${saved.error}`);\n }\n return { id, name };\n}\n","/**\n * File → Download → EPUB / OpenDocument (.odt) / Rich Text (.rtf).\n * EPUB the print package's EPUB engine, straight from the live text.\n * ODT/RTF the same engine writes the .docx, then aidream's LibreOffice route\n * (`POST /office/convert-document`) converts it. Nothing is stored.\n * Engine, converter and saver are injected so the forcing test runs the real logic.\n */\n\nimport { getDocsHost } from \"./ports\";\n\nexport type OpenFormat = \"epub\" | \"odt\" | \"rtf\";\n\nexport const OPEN_FORMATS: Record<OpenFormat, { label: string; extension: string; mime: string }> = {\n epub: { label: \"EPUB\", extension: \"epub\", mime: \"application/epub+zip\" },\n odt: { label: \"OpenDocument\", extension: \"odt\", mime: \"application/vnd.oasis.opendocument.text\" },\n rtf: { label: \"Rich Text\", extension: \"rtf\", mime: \"application/rtf\" },\n};\n\nexport interface OpenFormatDeps {\n engine: (markdown: string, format: \"docx\" | \"epub\", fileName: string) => Promise<{ bytes: Uint8Array; fileName: string }>;\n convert: (docx: Uint8Array, fileName: string, target: \"odt\" | \"rtf\") => Promise<Uint8Array>;\n save: (fileName: string, bytes: Uint8Array, mime: string) => void;\n}\n\nfunction baseName(title: string | null | undefined): string {\n const base = (title ?? \"\").replace(/[\\\\/:*?\"<>|\\u0000-\\u001f]+/g, \" \").replace(/\\s+/g, \" \").trim().slice(0, 120);\n return base || \"document\";\n}\n\nexport async function exportOpenFormat(markdown: string, format: OpenFormat, title: string | null | undefined, deps: OpenFormatDeps): Promise<string> {\n const name = baseName(title);\n const spec = OPEN_FORMATS[format];\n const fileName = `${name}.${spec.extension}`;\n if (format === \"epub\") {\n const out = await deps.engine(markdown, \"epub\", name);\n deps.save(fileName, out.bytes, spec.mime);\n return fileName;\n }\n const docx = await deps.engine(markdown, \"docx\", name);\n const bytes = await deps.convert(docx.bytes, docx.fileName, format);\n if (!bytes.byteLength) throw new Error(`The ${spec.label} converter returned an empty file.`);\n deps.save(fileName, bytes, spec.mime);\n return fileName;\n}\n\nexport async function downloadOpenFormat(markdown: string, format: OpenFormat, title: string | null | undefined): Promise<string> {\n return exportOpenFormat(markdown, format, title, {\n engine: async (md, fmt, fileName) => {\n const { exportDocument } = await import(\"@ai-matrx/print/document\");\n const exp = await exportDocument(md, fmt, { fileName });\n return { bytes: exp.bytes, fileName: exp.fileName };\n },\n convert: async (docx, fileName, target) => {\n const form = new FormData();\n form.append(\"file\", new Blob([docx as BlobPart]), fileName.endsWith(\".docx\") ? fileName : `${fileName}.docx`);\n form.append(\"target\", target);\n const res = await getDocsHost().requestRaw(\"/office/convert-document\", { method: \"POST\", body: form });\n return new Uint8Array(await res.arrayBuffer());\n },\n save: (fileName, bytes, mime) => {\n const url = URL.createObjectURL(new Blob([bytes as BlobPart], { type: mime }));\n const link = document.createElement(\"a\");\n link.href = url;\n link.download = fileName;\n link.rel = \"noopener\";\n document.body.appendChild(link);\n link.click();\n link.remove();\n setTimeout(() => URL.revokeObjectURL(url), 30_000);\n },\n });\n}\n"]}
|
package/dist/docs-io.js
CHANGED
|
@@ -260,6 +260,11 @@ function documentFileKind(name) {
|
|
|
260
260
|
return null;
|
|
261
261
|
}
|
|
262
262
|
var W = "http://schemas.openxmlformats.org/wordprocessingml/2006/main";
|
|
263
|
+
function markdownTable(rows) {
|
|
264
|
+
const width = Math.max(...rows.map((r) => r.length));
|
|
265
|
+
const line = (r) => `| ${Array.from({ length: width }, (_, i) => r[i] ?? "").join(" | ")} |`;
|
|
266
|
+
return [line(rows[0]), `|${" --- |".repeat(width)}`, ...rows.slice(1).map(line)].join("\n");
|
|
267
|
+
}
|
|
263
268
|
function docxXmlToMarkdown(xml) {
|
|
264
269
|
const doc = new DOMParser().parseFromString(xml, "application/xml");
|
|
265
270
|
const body = doc.getElementsByTagNameNS(W, "body")[0];
|
|
@@ -308,11 +313,7 @@ function docxXmlToMarkdown(xml) {
|
|
|
308
313
|
(tc) => Array.from(tc.getElementsByTagNameNS(W, "p")).map(paragraph).join(" ").replace(/\|/g, "\\|").trim()
|
|
309
314
|
)
|
|
310
315
|
);
|
|
311
|
-
if (rows.length)
|
|
312
|
-
out.push("", `| ${rows[0].join(" | ")} |`, `|${" --- |".repeat(rows[0].length)}`);
|
|
313
|
-
for (const row of rows.slice(1)) out.push(`| ${row.join(" | ")} |`);
|
|
314
|
-
out.push("");
|
|
315
|
-
}
|
|
316
|
+
if (rows.length) out.push(markdownTable(rows));
|
|
316
317
|
}
|
|
317
318
|
}
|
|
318
319
|
return out.reduce((acc, line, i) => {
|
|
@@ -360,6 +361,13 @@ function htmlToDocumentMarkdown(html) {
|
|
|
360
361
|
out.push(inline(el).trim());
|
|
361
362
|
return;
|
|
362
363
|
}
|
|
364
|
+
if (tag === "table") {
|
|
365
|
+
const rows = Array.from(el.querySelectorAll("tr")).map(
|
|
366
|
+
(tr) => Array.from(tr.children).filter((c) => /^(td|th)$/i.test(c.tagName)).map((c) => inline(c).replace(/\|/g, "\\|").replace(/\s+/g, " ").trim())
|
|
367
|
+
);
|
|
368
|
+
if (rows.length) out.push(markdownTable(rows));
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
363
371
|
if (tag === "pre") {
|
|
364
372
|
out.push("```", el.textContent ?? "", "```");
|
|
365
373
|
return;
|
package/dist/docs-io.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/docs/univer-doc-to-markdown.ts","../src/docs/import-document-file.ts","../src/docs/export-open-formats.ts"],"sourcesContent":["/**\n * univerDocToMarkdown — the reverse of `markdown-to-univer-doc.ts`.\n *\n * Why it exists: the canvas pane for a cloud document (`udt_document`) offers a\n * `Source` tab, and the rule in `features/canvas/core/canvasSource.ts` is that\n * Source shows THE ITEM'S OWN SOURCE — for a document, its markdown — never the\n * redux envelope. A document's content lives as an opaque Univer\n * `IDocumentData` snapshot in `workbench.udt_document_snapshots`, so something\n * has to read it back out. This is that something, and it is the ONLY one:\n * never re-implement a second snapshot reader.\n *\n * What it reads (the half `markdownToUniverDoc` writes):\n * - `body.dataStream` — the text; `\\r` terminates a paragraph, a trailing\n * `\\n` terminates the section.\n * - `body.paragraphs` — one entry per `\\r` (`startIndex` = the `\\r` offset).\n * - `body.textRuns` — `{ st, ed, ts }` style overlays. `ts.bl` + a heading\n * font size is how `markdownToUniverDoc` encodes\n * `#`..`######`, so the level is recovered from `fs`.\n *\n * Fidelity is deliberately MODEST and honest: headings, bold, italic,\n * strikethrough and inline/monospace code round-trip; everything else comes\n * back as the plain text the user actually sees. A document typed by hand in\n * Univer never had markdown syntax in the first place — printing its text is\n * the truthful answer, not a reconstruction.\n *\n * PURE — no React, no Redux, no IO.\n */\n\nconst TABLE_CELL_SEP = \" | \";\n\n// Univer's DataStreamTreeTokenType control characters (literals: no Univer runtime here).\nconst TABLE_START = \"\\x1A\";\nconst TABLE_ROW_START = \"\\x1B\";\nconst TABLE_CELL_START = \"\\x1C\";\nconst TABLE_CELL_END = \"\\x1D\";\nconst TABLE_ROW_END = \"\\x0E\";\nconst TABLE_END = \"\\x0F\";\nconst LINK_START = \"\\x1F\";\nconst LINK_END = \"\\x1E\";\n\ninterface CustomRangeLike {\n startIndex?: number;\n endIndex?: number;\n rangeType?: number;\n properties?: { url?: unknown };\n}\n\n/** The Univer-native shapes (real tables, hyperlink ranges) written back as markdown. */\nfunction tableMarkdown(rows: string[][]): string[] {\n const width = Math.max(1, ...rows.map((r) => r.length));\n const cell = (r: string[], i: number) => (r[i] ?? \"\").replace(/^\\*\\*(.*)\\*\\*$/, \"$1\");\n const line = (r: string[], plain: boolean) =>\n `| ${Array.from({ length: width }, (_, i) => (plain ? cell(r, i) : (r[i] ?? \"\"))).join(\" | \")} |`;\n const out = [line(rows[0]!, true), `|${\" --- |\".repeat(width)}`];\n for (const r of rows.slice(1)) out.push(line(r, false));\n return out;\n}\n\ninterface TextRunLike {\n st?: number;\n ed?: number;\n ts?: {\n bl?: number;\n it?: number;\n ff?: string;\n fs?: number;\n st?: { s?: number } | number;\n };\n}\n\n/** Inverse of markdown-to-univer-doc's HEADING_FONT_SIZE. */\nexport const HEADING_LEVEL_BY_FONT_SIZE: Record<number, number> = {\n 26: 1,\n 22: 2,\n 18: 3,\n 16: 4,\n 14: 5,\n 13: 6,\n};\n\nfunction asArray<T>(value: unknown): T[] {\n return Array.isArray(value) ? (value as T[]) : [];\n}\n\nconst TRUE_ENOUGH = (v: unknown): boolean => v === 1 || v === true;\n\nfunction isStruck(ts: TextRunLike[\"ts\"]): boolean {\n if (!ts) return false;\n const st = ts.st;\n if (typeof st === \"number\") return TRUE_ENOUGH(st);\n return TRUE_ENOUGH(st?.s);\n}\n\n/** The style that applies to the whole paragraph, if the runs agree on one. */\nfunction paragraphHeadingLevel(runs: TextRunLike[]): number | null {\n for (const run of runs) {\n const fs = run.ts?.fs;\n if (typeof fs === \"number\" && TRUE_ENOUGH(run.ts?.bl)) {\n const level = HEADING_LEVEL_BY_FONT_SIZE[fs];\n if (level) return level;\n }\n }\n return null;\n}\n\nfunction emphasize(text: string, ts: TextRunLike[\"ts\"], inHeading: boolean): string {\n if (!text) return text;\n // Never double-mark: a heading's own runs are bold by construction.\n const leading = text.match(/^\\s*/)?.[0] ?? \"\";\n const trailing = text.match(/\\s*$/)?.[0] ?? \"\";\n let core = text.slice(leading.length, text.length - trailing.length);\n if (!core) return text;\n\n const mono = typeof ts?.ff === \"string\" && /mono/i.test(ts.ff);\n if (mono) core = `\\`${core}\\``;\n else {\n if (TRUE_ENOUGH(ts?.it)) core = `*${core}*`;\n if (TRUE_ENOUGH(ts?.bl) && !inHeading) core = `**${core}**`;\n }\n if (isStruck(ts)) core = `~~${core}~~`;\n return `${leading}${core}${trailing}`;\n}\n\n// An inline picture: one CUSTOM_BLOCK token in the stream, its drawing in `snapshot.drawings`.\nconst CUSTOM_BLOCK = \"\\b\";\n\ninterface DrawingLike {\n source?: unknown;\n title?: unknown;\n description?: unknown;\n docTransform?: { size?: { width?: unknown; height?: unknown } };\n}\n\n/**\n * A picture's markdown source. Our own pictures are `matrx-file:<id>`; the\n * drawn size rides as a `#WxH` fragment so a round trip keeps the shape.\n */\nexport function imageMarkdownSource(drawing: DrawingLike): string | null {\n const source = typeof drawing.source === \"string\" ? drawing.source : \"\";\n if (!source || source.startsWith(\"data:\")) return null;\n const w = Number(drawing.docTransform?.size?.width);\n const h = Number(drawing.docTransform?.size?.height);\n return Number.isFinite(w) && Number.isFinite(h) && w > 0 && h > 0 && !source.includes(\"#\")\n ? `${source}#${Math.round(w)}x${Math.round(h)}`\n : source;\n}\n\n/** Every picture in a snapshot, as its markdown source (`matrx-file:<id>#WxH` or a URL). */\nexport function documentImageSources(snapshot: unknown): string[] {\n const drawings = ((snapshot as { drawings?: unknown } | null)?.drawings ?? {}) as Record<string, DrawingLike>;\n const body = (snapshot as { body?: { customBlocks?: unknown } } | null)?.body;\n return asArray<{ blockId?: unknown }>(body?.customBlocks)\n .map((b) => (typeof b.blockId === \"string\" && drawings[b.blockId] ? imageMarkdownSource(drawings[b.blockId]!) : null))\n .filter((s): s is string => !!s);\n}\n\nexport interface DocToMarkdownOptions {\n /**\n * Where a picture's markdown points. Return null to leave it out (the caller\n * names what was left out). Default: the stored source.\n */\n imageUrl?: (source: string) => string | null;\n}\n\n/**\n * Read a Univer document snapshot back as markdown. Returns `\"\"` for an empty\n * or unreadable snapshot so the caller can say \"no source\" honestly rather than\n * printing an envelope.\n */\nexport function univerDocToMarkdown(snapshot: unknown, options: DocToMarkdownOptions = {}): string {\n if (!snapshot || typeof snapshot !== \"object\") return \"\";\n const body = (snapshot as { body?: unknown }).body;\n if (!body || typeof body !== \"object\") return \"\";\n\n const dataStream = (body as { dataStream?: unknown }).dataStream;\n if (typeof dataStream !== \"string\" || !dataStream) return \"\";\n\n // Pictures: each CUSTOM_BLOCK token is read back as ``.\n const drawings = ((snapshot as { drawings?: unknown }).drawings ?? {}) as Record<string, DrawingLike>;\n const blocks = new Map<number, string>();\n for (const b of asArray<{ startIndex?: unknown; blockId?: unknown }>((body as { customBlocks?: unknown }).customBlocks)) {\n if (typeof b.startIndex !== \"number\" || typeof b.blockId !== \"string\") continue;\n const drawing = drawings[b.blockId];\n const source = drawing ? imageMarkdownSource(drawing) : null;\n if (!source || !drawing) continue;\n const url = options.imageUrl ? options.imageUrl(source) : source;\n if (!url) continue;\n const label = typeof drawing.title === \"string\" && drawing.title ? drawing.title\n : typeof drawing.description === \"string\" && drawing.description ? drawing.description : \"image\";\n blocks.set(b.startIndex, `![${label.replace(/[[\\]]/g, \"\")}](${url})`);\n }\n\n const runs = asArray<TextRunLike>((body as { textRuns?: unknown }).textRuns)\n .filter((r) => typeof r.st === \"number\" && typeof r.ed === \"number\")\n .sort((a, b) => (a.st as number) - (b.st as number));\n\n const links = new Map<number, string>();\n for (const r of asArray<CustomRangeLike>((body as { customRanges?: unknown }).customRanges)) {\n if (r.rangeType === 0 && typeof r.endIndex === \"number\" && typeof r.properties?.url === \"string\") {\n links.set(r.endIndex, r.properties.url);\n }\n }\n // A slice of the stream with its hyperlink tokens read back as `[text](url)`.\n const readSlice = (from: number, to: number): string => {\n let out = \"\";\n for (let k = from; k < to; k++) {\n const c = dataStream[k];\n if (c === LINK_START) out += \"[\";\n else if (c === LINK_END) out += `](${links.get(k) ?? \"\"})`;\n else if (c === CUSTOM_BLOCK) out += blocks.get(k) ?? \"\";\n else out += c;\n }\n return out;\n };\n\n const lines: string[] = [];\n let cursor = 0;\n let runIndex = 0;\n let tableRows: string[][] | null = null;\n\n // Paragraph boundaries are the `\\r`s in the stream itself — reading them from\n // the stream (rather than trusting `paragraphs`) keeps a snapshot written by\n // Univer's own editor, which may index differently, readable.\n for (let i = 0; i <= dataStream.length; i++) {\n const ch = dataStream[i];\n const atEnd = i === dataStream.length;\n if (!atEnd) {\n if (ch === TABLE_START) {\n tableRows = [];\n cursor = i + 1;\n continue;\n }\n if (tableRows && ch === TABLE_ROW_START) {\n tableRows.push([]);\n cursor = i + 1;\n continue;\n }\n if (tableRows && ch === TABLE_CELL_START) {\n tableRows[tableRows.length - 1]?.push(\"\");\n cursor = i + 1;\n continue;\n }\n if (tableRows && (ch === TABLE_CELL_END || ch === TABLE_ROW_END)) {\n cursor = i + 1;\n continue;\n }\n if (tableRows && ch === TABLE_END) {\n if (tableRows.length) lines.push(\"\", ...tableMarkdown(tableRows), \"\");\n tableRows = null;\n cursor = i + 1;\n continue;\n }\n }\n if (!atEnd && ch !== \"\\r\" && ch !== \"\\n\") continue;\n\n const start = cursor;\n const end = i;\n cursor = i + 1;\n if (end <= start) {\n if (ch === \"\\r\" && !tableRows) lines.push(\"\");\n if (atEnd) break;\n continue;\n }\n\n // Collect the runs overlapping this paragraph.\n const paragraphRuns: TextRunLike[] = [];\n let scan = runIndex;\n while (scan < runs.length && (runs[scan]!.st as number) < end) {\n if ((runs[scan]!.ed as number) > start) paragraphRuns.push(runs[scan]!);\n scan++;\n }\n while (\n runIndex < runs.length &&\n (runs[runIndex]!.ed as number) <= end\n ) {\n runIndex++;\n }\n\n const heading = paragraphHeadingLevel(paragraphRuns);\n let text = \"\";\n let at = start;\n for (const run of paragraphRuns) {\n const rs = Math.max(run.st as number, start);\n const re = Math.min(run.ed as number, end);\n if (rs > at) text += readSlice(at, rs);\n if (re > rs) {\n text += emphasize(readSlice(rs, re), run.ts, heading != null);\n }\n at = Math.max(at, re);\n }\n if (at < end) text += readSlice(at, end);\n\n if (tableRows) {\n // A table cell: its paragraphs join into the one cell, pipes escaped.\n const row = tableRows[tableRows.length - 1];\n if (row?.length) {\n const part = text.trim().replace(/\\|/g, \"\\\\|\");\n const last = row.length - 1;\n row[last] = row[last] ? `${row[last]} ${part}` : part;\n }\n } else {\n lines.push(heading ? `${\"#\".repeat(heading)} ${text.trim()}` : text);\n }\n if (atEnd) break;\n }\n\n // Univer keeps one paragraph per visual line; markdown wants a blank line\n // between blocks (but not inside a list or a table), and consecutive blank\n // paragraphs collapse. The writer (`markdownToUniverDoc`) flattens a list item\n // to a literal \"• \" / \"1. \" prefix and a table row to cells joined by\n // TABLE_CELL_SEP, so those shapes are read back as the markdown they came from.\n type Kind = \"text\" | \"list\" | \"table\";\n const kindOf = (line: string): Kind =>\n /^\\s*(?:\\u2022|[-*+]|\\d+[.)])\\s+/.test(line) ? \"list\" : line.includes(TABLE_CELL_SEP) ? \"table\" : \"text\";\n // A document typed or pasted as markdown keeps a table as literal `| a | b |` lines, one paragraph each, often\n // with an empty paragraph between rows: those rows are ONE table, so the gaps between them close.\n const rawRow = (l: string) => /^\\s*\\|.*\\|\\s*$/.test(l);\n const rows = lines.filter((l, i) => {\n if (l.trim()) return true;\n let a = i - 1;\n while (a >= 0 && !lines[a]!.trim()) a--;\n let z = i + 1;\n while (z < lines.length && !lines[z]!.trim()) z++;\n return !(a >= 0 && z < lines.length && rawRow(lines[a]!) && rawRow(lines[z]!));\n });\n const out: string[] = [];\n let prev: Kind | null = null;\n let tableRow = 0;\n for (const raw of rows) {\n if (!raw.trim()) {\n if (out.length && out[out.length - 1] !== \"\") out.push(\"\");\n prev = null;\n continue;\n }\n let line = raw.replace(/\\s+$/, \"\");\n if (rawRow(line)) {\n if (prev !== \"table\" && out.length && out[out.length - 1] !== \"\") out.push(\"\");\n out.push(line.trim());\n prev = \"table\";\n continue;\n }\n const kind = kindOf(line);\n if (kind === \"list\") {\n line = line.replace(/^(\\s*)\\u2022\\s+/, \"$1- \").replace(/^(\\s*)\\*\\s+/, \"$1- \");\n // Four spaces per nesting level in the document; two read as nesting in markdown.\n line = line.replace(/^( +)/, (m) => \" \".repeat(Math.floor(m.length / 2)));\n }\n if (kind === \"table\") {\n const cells = line.split(/\\s*\\|\\s*/).map((c) => c.trim());\n const isHeader = prev !== \"table\";\n const shown = cells.map((c) => (isHeader ? c.replace(/^\\*\\*(.*)\\*\\*$/, \"$1\") : c));\n line = `| ${shown.join(\" | \")} |`;\n if (isHeader) tableRow = shown.length;\n out.push(...(prev === \"table\" || prev === null || !out.length || out[out.length - 1] === \"\" ? [] : [\"\"]));\n out.push(line);\n if (isHeader) out.push(`|${\" --- |\".repeat(tableRow)}`);\n prev = \"table\";\n continue;\n }\n const tight = kind === \"list\" && prev === \"list\";\n if (out.length && out[out.length - 1] !== \"\" && !tight) out.push(\"\");\n out.push(line);\n prev = kind;\n }\n return out.join(\"\\n\").trim();\n}\n","/**\n * A file from the person's computer -> a new document (Word .docx, Markdown,\n * plain text, HTML). Everything lands as markdown first and goes through THE\n * one writer (`markdownToUniverDoc`), so headings, bold, italic, links and\n * lists keep their shape. Parse first: a file we cannot read never leaves an\n * empty document behind.\n */\n\"use client\";\n\nimport { readZip } from \"@ai-matrx/alchemy/operate/zip\";\n\nimport { createDocument, saveDocumentSnapshot, deleteDocument } from \"./document-service\";\nimport { deriveDocumentName, markdownToUniverDoc } from \"./markdown-to-univer-doc\";\nimport { sanitizeUniverDocSnapshot } from \"./sanitize-univer-doc-snapshot\";\n\nexport const DOCUMENT_UPLOAD_ACCEPT = \".docx,.md,.markdown,.txt,.html,.htm\";\n\ntype Source = \"imported_docx\" | \"imported_md\" | \"imported_txt\";\n\nexport function documentFileKind(name: string): \"docx\" | \"md\" | \"txt\" | \"html\" | null {\n const ext = name.toLowerCase().split(\".\").pop() ?? \"\";\n if (ext === \"docx\") return \"docx\";\n if (ext === \"md\" || ext === \"markdown\") return \"md\";\n if (ext === \"txt\") return \"txt\";\n if (ext === \"html\" || ext === \"htm\") return \"html\";\n return null;\n}\n\nconst W = \"http://schemas.openxmlformats.org/wordprocessingml/2006/main\";\n\n/** Word's document.xml -> markdown: headings, bold/italic runs, list paragraphs, tables as rows. */\nexport function docxXmlToMarkdown(xml: string): string {\n const doc = new DOMParser().parseFromString(xml, \"application/xml\");\n const body = doc.getElementsByTagNameNS(W, \"body\")[0];\n if (!body) throw new Error(\"This Word file has no document body.\");\n const attr = (el: Element | undefined, name: string) => el?.getAttributeNS(W, name) ?? el?.getAttribute(`w:${name}`) ?? null;\n const first = (el: Element, name: string) => el.getElementsByTagNameNS(W, name)[0] as Element | undefined;\n const on = (props: Element | undefined, name: string) => {\n const el = props ? first(props, name) : undefined;\n return !!el && attr(el, \"val\") !== \"0\" && attr(el, \"val\") !== \"false\";\n };\n const paragraph = (p: Element): string => {\n let text = \"\";\n for (const r of Array.from(p.getElementsByTagNameNS(W, \"r\"))) {\n const t = Array.from(r.childNodes)\n .map((n) => {\n const el = n as Element;\n if (el.localName === \"t\") return el.textContent ?? \"\";\n if (el.localName === \"tab\") return \"\\t\";\n if (el.localName === \"br\") return \" \\n\";\n return \"\";\n })\n .join(\"\");\n if (!t) continue;\n const rPr = first(r, \"rPr\");\n const bold = on(rPr, \"b\");\n const italic = on(rPr, \"i\");\n const lead = t.match(/^\\s*/)?.[0] ?? \"\";\n const trail = t.match(/\\s*$/)?.[0] ?? \"\";\n const core = t.trim();\n text += core ? `${lead}${bold ? \"**\" : \"\"}${italic ? \"*\" : \"\"}${core}${italic ? \"*\" : \"\"}${bold ? \"**\" : \"\"}${trail}` : t;\n }\n const pPr = first(p, \"pPr\");\n const style = attr(pPr ? first(pPr, \"pStyle\") : undefined, \"val\") ?? \"\";\n const heading = style.match(/^(?:Heading|heading)\\s?(\\d)$/)?.[1] ?? (style === \"Title\" ? \"1\" : null);\n if (heading && text.trim()) return `${\"#\".repeat(Math.min(6, Number(heading)))} ${text.replace(/\\*+/g, \"\").trim()}`;\n if (pPr && first(pPr, \"numPr\") && text.trim()) {\n const level = Number(attr(first(first(pPr, \"numPr\")!, \"ilvl\"), \"val\") ?? 0);\n return `${\" \".repeat(level)}- ${text.trim()}`;\n }\n return text;\n };\n const out: string[] = [];\n for (const child of Array.from(body.children)) {\n if (child.localName === \"p\") out.push(paragraph(child));\n else if (child.localName === \"tbl\") {\n const rows = Array.from(child.getElementsByTagNameNS(W, \"tr\")).map((tr) =>\n Array.from(tr.getElementsByTagNameNS(W, \"tc\")).map((tc) =>\n Array.from(tc.getElementsByTagNameNS(W, \"p\")).map(paragraph).join(\" \").replace(/\\|/g, \"\\\\|\").trim(),\n ),\n );\n if (rows.length) {\n out.push(\"\", `| ${rows[0]!.join(\" | \")} |`, `|${\" --- |\".repeat(rows[0]!.length)}`);\n for (const row of rows.slice(1)) out.push(`| ${row.join(\" | \")} |`);\n out.push(\"\");\n }\n }\n }\n // A list run stays tight; everything else is its own block.\n return out\n .reduce<string[]>((acc, line, i) => {\n const prevList = /^\\s*- /.test(out[i - 1] ?? \"\");\n if (i > 0 && !(prevList && /^\\s*- /.test(line))) acc.push(\"\");\n acc.push(line);\n return acc;\n }, [])\n .join(\"\\n\")\n .replace(/\\n{3,}/g, \"\\n\\n\")\n .trim();\n}\n\n/** HTML -> markdown for the blocks a document keeps (headings, paragraphs, lists, emphasis, links). */\nexport function htmlToDocumentMarkdown(html: string): string {\n const doc = new DOMParser().parseFromString(html, \"text/html\");\n const inline = (node: Node): string => {\n if (node.nodeType === 3) return (node.textContent ?? \"\").replace(/\\s+/g, \" \");\n if (node.nodeType !== 1) return \"\";\n const el = node as Element;\n const inner = Array.from(el.childNodes).map(inline).join(\"\");\n switch (el.tagName.toLowerCase()) {\n case \"strong\":\n case \"b\":\n return inner.trim() ? `**${inner.trim()}**` : \"\";\n case \"em\":\n case \"i\":\n return inner.trim() ? `*${inner.trim()}*` : \"\";\n case \"a\": {\n const href = el.getAttribute(\"href\");\n return href && /^https?:|^mailto:/i.test(href) ? `[${inner.trim()}](${href})` : inner;\n }\n case \"br\":\n return \" \\n\";\n case \"code\":\n return `\\`${inner}\\``;\n default:\n return inner;\n }\n };\n const out: string[] = [];\n const block = (el: Element, depth = 0): void => {\n const tag = el.tagName.toLowerCase();\n const h = tag.match(/^h([1-6])$/);\n if (h) {\n out.push(`${\"#\".repeat(Number(h[1]))} ${inline(el).trim()}`);\n return;\n }\n if (tag === \"p\" || tag === \"blockquote\") {\n out.push(inline(el).trim());\n return;\n }\n if (tag === \"pre\") {\n out.push(\"```\", el.textContent ?? \"\", \"```\");\n return;\n }\n if (tag === \"ul\" || tag === \"ol\") {\n Array.from(el.children).forEach((li, n) => {\n if (li.tagName.toLowerCase() !== \"li\") return;\n const own = Array.from(li.childNodes).filter((c) => !(c.nodeType === 1 && /^(ul|ol)$/i.test((c as Element).tagName)));\n out.push(`${\" \".repeat(depth)}${tag === \"ol\" ? `${n + 1}.` : \"-\"} ${own.map(inline).join(\"\").trim()}`);\n Array.from(li.children)\n .filter((c) => /^(ul|ol)$/i.test(c.tagName))\n .forEach((c) => block(c, depth + 1));\n });\n return;\n }\n if (tag === \"script\" || tag === \"style\" || tag === \"head\") return;\n const kids = Array.from(el.children);\n if (kids.length === 0) {\n const t = inline(el).trim();\n if (t) out.push(t);\n return;\n }\n kids.forEach((k) => block(k, depth));\n };\n block(doc.body);\n return out\n .map((l) => l.replace(/\\s+$/, \"\"))\n .reduce<string[]>((acc, line, i) => {\n const list = (s: string | undefined) => /^\\s*(-|\\d+\\.) /.test(s ?? \"\");\n if (i > 0 && !(list(out[i - 1]) && list(line))) acc.push(\"\");\n acc.push(line);\n return acc;\n }, [])\n .join(\"\\n\")\n .replace(/\\n{3,}/g, \"\\n\\n\")\n .trim();\n}\n\nexport async function documentFileToMarkdown(file: File): Promise<{ markdown: string; source: Source }> {\n const kind = documentFileKind(file.name);\n if (kind === \"docx\") {\n const zip = await readZip(file);\n const entry = zip.get(\"word/document.xml\");\n if (!entry) throw new Error(\"This is not a Word document (.docx).\");\n const xml = await entry.text();\n return { markdown: docxXmlToMarkdown(xml), source: \"imported_docx\" };\n }\n const text = await file.text();\n if (kind === \"md\") return { markdown: text, source: \"imported_md\" };\n if (kind === \"html\") return { markdown: htmlToDocumentMarkdown(text), source: \"imported_md\" };\n if (kind === \"txt\") {\n // Plain text: each line is its own paragraph; nothing in it is read as markdown.\n const escaped = text\n .replace(/\\r\\n?/g, \"\\n\")\n .split(\"\\n\")\n .map((l) => l.replace(/([\\\\`*_[\\]#>|~])/g, \"\\\\$1\"))\n .join(\"\\n\\n\");\n return { markdown: escaped, source: \"imported_txt\" };\n }\n throw new Error(\"Upload a Word (.docx), Markdown, text or HTML file.\");\n}\n\n/** Create a document from an uploaded file; returns its id. */\nexport async function importDocumentFile(file: File, organizationId: string): Promise<{ id: string; name: string }> {\n const { markdown, source } = await documentFileToMarkdown(file);\n const name = file.name.replace(/\\.[^.]+$/, \"\").trim() || deriveDocumentName(markdown);\n const created = await createDocument({ name, source, organizationId, description: `Imported from ${file.name}` });\n if (!created.success) throw new Error(`Could not create the document: ${created.error}`);\n const id = created.data.id;\n const snapshot = sanitizeUniverDocSnapshot(markdownToUniverDoc(markdown, name), id);\n const saved = await saveDocumentSnapshot({ documentId: id, snapshot, origin: \"imported\", label: `Imported from ${file.name}` });\n if (!saved.success) {\n await deleteDocument(id);\n throw new Error(`Could not save the imported content: ${saved.error}`);\n }\n return { id, name };\n}\n","/**\n * File → Download → EPUB / OpenDocument (.odt) / Rich Text (.rtf).\n * EPUB the print package's EPUB engine, straight from the live text.\n * ODT/RTF the same engine writes the .docx, then aidream's LibreOffice route\n * (`POST /office/convert-document`) converts it. Nothing is stored.\n * Engine, converter and saver are injected so the forcing test runs the real logic.\n */\n\nimport { getDocsHost } from \"./ports\";\n\nexport type OpenFormat = \"epub\" | \"odt\" | \"rtf\";\n\nexport const OPEN_FORMATS: Record<OpenFormat, { label: string; extension: string; mime: string }> = {\n epub: { label: \"EPUB\", extension: \"epub\", mime: \"application/epub+zip\" },\n odt: { label: \"OpenDocument\", extension: \"odt\", mime: \"application/vnd.oasis.opendocument.text\" },\n rtf: { label: \"Rich Text\", extension: \"rtf\", mime: \"application/rtf\" },\n};\n\nexport interface OpenFormatDeps {\n engine: (markdown: string, format: \"docx\" | \"epub\", fileName: string) => Promise<{ bytes: Uint8Array; fileName: string }>;\n convert: (docx: Uint8Array, fileName: string, target: \"odt\" | \"rtf\") => Promise<Uint8Array>;\n save: (fileName: string, bytes: Uint8Array, mime: string) => void;\n}\n\nfunction baseName(title: string | null | undefined): string {\n const base = (title ?? \"\").replace(/[\\\\/:*?\"<>|\\u0000-\\u001f]+/g, \" \").replace(/\\s+/g, \" \").trim().slice(0, 120);\n return base || \"document\";\n}\n\nexport async function exportOpenFormat(markdown: string, format: OpenFormat, title: string | null | undefined, deps: OpenFormatDeps): Promise<string> {\n const name = baseName(title);\n const spec = OPEN_FORMATS[format];\n const fileName = `${name}.${spec.extension}`;\n if (format === \"epub\") {\n const out = await deps.engine(markdown, \"epub\", name);\n deps.save(fileName, out.bytes, spec.mime);\n return fileName;\n }\n const docx = await deps.engine(markdown, \"docx\", name);\n const bytes = await deps.convert(docx.bytes, docx.fileName, format);\n if (!bytes.byteLength) throw new Error(`The ${spec.label} converter returned an empty file.`);\n deps.save(fileName, bytes, spec.mime);\n return fileName;\n}\n\nexport async function downloadOpenFormat(markdown: string, format: OpenFormat, title: string | null | undefined): Promise<string> {\n return exportOpenFormat(markdown, format, title, {\n engine: async (md, fmt, fileName) => {\n const { exportDocument } = await import(\"@ai-matrx/print/document\");\n const exp = await exportDocument(md, fmt, { fileName });\n return { bytes: exp.bytes, fileName: exp.fileName };\n },\n convert: async (docx, fileName, target) => {\n const form = new FormData();\n form.append(\"file\", new Blob([docx as BlobPart]), fileName.endsWith(\".docx\") ? fileName : `${fileName}.docx`);\n form.append(\"target\", target);\n const res = await getDocsHost().requestRaw(\"/office/convert-document\", { method: \"POST\", body: form });\n return new Uint8Array(await res.arrayBuffer());\n },\n save: (fileName, bytes, mime) => {\n const url = URL.createObjectURL(new Blob([bytes as BlobPart], { type: mime }));\n const link = document.createElement(\"a\");\n link.href = url;\n link.download = fileName;\n link.rel = \"noopener\";\n document.body.appendChild(link);\n link.click();\n link.remove();\n setTimeout(() => URL.revokeObjectURL(url), 30_000);\n },\n });\n}\n"],"mappings":"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","names":["out"]}
|
|
1
|
+
{"version":3,"sources":["../src/docs/univer-doc-to-markdown.ts","../src/docs/import-document-file.ts","../src/docs/export-open-formats.ts"],"sourcesContent":["/**\n * univerDocToMarkdown — the reverse of `markdown-to-univer-doc.ts`.\n *\n * Why it exists: the canvas pane for a cloud document (`udt_document`) offers a\n * `Source` tab, and the rule in `features/canvas/core/canvasSource.ts` is that\n * Source shows THE ITEM'S OWN SOURCE — for a document, its markdown — never the\n * redux envelope. A document's content lives as an opaque Univer\n * `IDocumentData` snapshot in `workbench.udt_document_snapshots`, so something\n * has to read it back out. This is that something, and it is the ONLY one:\n * never re-implement a second snapshot reader.\n *\n * What it reads (the half `markdownToUniverDoc` writes):\n * - `body.dataStream` — the text; `\\r` terminates a paragraph, a trailing\n * `\\n` terminates the section.\n * - `body.paragraphs` — one entry per `\\r` (`startIndex` = the `\\r` offset).\n * - `body.textRuns` — `{ st, ed, ts }` style overlays. `ts.bl` + a heading\n * font size is how `markdownToUniverDoc` encodes\n * `#`..`######`, so the level is recovered from `fs`.\n *\n * Fidelity is deliberately MODEST and honest: headings, bold, italic,\n * strikethrough and inline/monospace code round-trip; everything else comes\n * back as the plain text the user actually sees. A document typed by hand in\n * Univer never had markdown syntax in the first place — printing its text is\n * the truthful answer, not a reconstruction.\n *\n * PURE — no React, no Redux, no IO.\n */\n\nconst TABLE_CELL_SEP = \" | \";\n\n// Univer's DataStreamTreeTokenType control characters (literals: no Univer runtime here).\nconst TABLE_START = \"\\x1A\";\nconst TABLE_ROW_START = \"\\x1B\";\nconst TABLE_CELL_START = \"\\x1C\";\nconst TABLE_CELL_END = \"\\x1D\";\nconst TABLE_ROW_END = \"\\x0E\";\nconst TABLE_END = \"\\x0F\";\nconst LINK_START = \"\\x1F\";\nconst LINK_END = \"\\x1E\";\n\ninterface CustomRangeLike {\n startIndex?: number;\n endIndex?: number;\n rangeType?: number;\n properties?: { url?: unknown };\n}\n\n/** The Univer-native shapes (real tables, hyperlink ranges) written back as markdown. */\nfunction tableMarkdown(rows: string[][]): string[] {\n const width = Math.max(1, ...rows.map((r) => r.length));\n const cell = (r: string[], i: number) => (r[i] ?? \"\").replace(/^\\*\\*(.*)\\*\\*$/, \"$1\");\n const line = (r: string[], plain: boolean) =>\n `| ${Array.from({ length: width }, (_, i) => (plain ? cell(r, i) : (r[i] ?? \"\"))).join(\" | \")} |`;\n const out = [line(rows[0]!, true), `|${\" --- |\".repeat(width)}`];\n for (const r of rows.slice(1)) out.push(line(r, false));\n return out;\n}\n\ninterface TextRunLike {\n st?: number;\n ed?: number;\n ts?: {\n bl?: number;\n it?: number;\n ff?: string;\n fs?: number;\n st?: { s?: number } | number;\n };\n}\n\n/** Inverse of markdown-to-univer-doc's HEADING_FONT_SIZE. */\nexport const HEADING_LEVEL_BY_FONT_SIZE: Record<number, number> = {\n 26: 1,\n 22: 2,\n 18: 3,\n 16: 4,\n 14: 5,\n 13: 6,\n};\n\nfunction asArray<T>(value: unknown): T[] {\n return Array.isArray(value) ? (value as T[]) : [];\n}\n\nconst TRUE_ENOUGH = (v: unknown): boolean => v === 1 || v === true;\n\nfunction isStruck(ts: TextRunLike[\"ts\"]): boolean {\n if (!ts) return false;\n const st = ts.st;\n if (typeof st === \"number\") return TRUE_ENOUGH(st);\n return TRUE_ENOUGH(st?.s);\n}\n\n/** The style that applies to the whole paragraph, if the runs agree on one. */\nfunction paragraphHeadingLevel(runs: TextRunLike[]): number | null {\n for (const run of runs) {\n const fs = run.ts?.fs;\n if (typeof fs === \"number\" && TRUE_ENOUGH(run.ts?.bl)) {\n const level = HEADING_LEVEL_BY_FONT_SIZE[fs];\n if (level) return level;\n }\n }\n return null;\n}\n\nfunction emphasize(text: string, ts: TextRunLike[\"ts\"], inHeading: boolean): string {\n if (!text) return text;\n // Never double-mark: a heading's own runs are bold by construction.\n const leading = text.match(/^\\s*/)?.[0] ?? \"\";\n const trailing = text.match(/\\s*$/)?.[0] ?? \"\";\n let core = text.slice(leading.length, text.length - trailing.length);\n if (!core) return text;\n\n const mono = typeof ts?.ff === \"string\" && /mono/i.test(ts.ff);\n if (mono) core = `\\`${core}\\``;\n else {\n if (TRUE_ENOUGH(ts?.it)) core = `*${core}*`;\n if (TRUE_ENOUGH(ts?.bl) && !inHeading) core = `**${core}**`;\n }\n if (isStruck(ts)) core = `~~${core}~~`;\n return `${leading}${core}${trailing}`;\n}\n\n// An inline picture: one CUSTOM_BLOCK token in the stream, its drawing in `snapshot.drawings`.\nconst CUSTOM_BLOCK = \"\\b\";\n\ninterface DrawingLike {\n source?: unknown;\n title?: unknown;\n description?: unknown;\n docTransform?: { size?: { width?: unknown; height?: unknown } };\n}\n\n/**\n * A picture's markdown source. Our own pictures are `matrx-file:<id>`; the\n * drawn size rides as a `#WxH` fragment so a round trip keeps the shape.\n */\nexport function imageMarkdownSource(drawing: DrawingLike): string | null {\n const source = typeof drawing.source === \"string\" ? drawing.source : \"\";\n if (!source || source.startsWith(\"data:\")) return null;\n const w = Number(drawing.docTransform?.size?.width);\n const h = Number(drawing.docTransform?.size?.height);\n return Number.isFinite(w) && Number.isFinite(h) && w > 0 && h > 0 && !source.includes(\"#\")\n ? `${source}#${Math.round(w)}x${Math.round(h)}`\n : source;\n}\n\n/** Every picture in a snapshot, as its markdown source (`matrx-file:<id>#WxH` or a URL). */\nexport function documentImageSources(snapshot: unknown): string[] {\n const drawings = ((snapshot as { drawings?: unknown } | null)?.drawings ?? {}) as Record<string, DrawingLike>;\n const body = (snapshot as { body?: { customBlocks?: unknown } } | null)?.body;\n return asArray<{ blockId?: unknown }>(body?.customBlocks)\n .map((b) => (typeof b.blockId === \"string\" && drawings[b.blockId] ? imageMarkdownSource(drawings[b.blockId]!) : null))\n .filter((s): s is string => !!s);\n}\n\nexport interface DocToMarkdownOptions {\n /**\n * Where a picture's markdown points. Return null to leave it out (the caller\n * names what was left out). Default: the stored source.\n */\n imageUrl?: (source: string) => string | null;\n}\n\n/**\n * Read a Univer document snapshot back as markdown. Returns `\"\"` for an empty\n * or unreadable snapshot so the caller can say \"no source\" honestly rather than\n * printing an envelope.\n */\nexport function univerDocToMarkdown(snapshot: unknown, options: DocToMarkdownOptions = {}): string {\n if (!snapshot || typeof snapshot !== \"object\") return \"\";\n const body = (snapshot as { body?: unknown }).body;\n if (!body || typeof body !== \"object\") return \"\";\n\n const dataStream = (body as { dataStream?: unknown }).dataStream;\n if (typeof dataStream !== \"string\" || !dataStream) return \"\";\n\n // Pictures: each CUSTOM_BLOCK token is read back as ``.\n const drawings = ((snapshot as { drawings?: unknown }).drawings ?? {}) as Record<string, DrawingLike>;\n const blocks = new Map<number, string>();\n for (const b of asArray<{ startIndex?: unknown; blockId?: unknown }>((body as { customBlocks?: unknown }).customBlocks)) {\n if (typeof b.startIndex !== \"number\" || typeof b.blockId !== \"string\") continue;\n const drawing = drawings[b.blockId];\n const source = drawing ? imageMarkdownSource(drawing) : null;\n if (!source || !drawing) continue;\n const url = options.imageUrl ? options.imageUrl(source) : source;\n if (!url) continue;\n const label = typeof drawing.title === \"string\" && drawing.title ? drawing.title\n : typeof drawing.description === \"string\" && drawing.description ? drawing.description : \"image\";\n blocks.set(b.startIndex, `![${label.replace(/[[\\]]/g, \"\")}](${url})`);\n }\n\n const runs = asArray<TextRunLike>((body as { textRuns?: unknown }).textRuns)\n .filter((r) => typeof r.st === \"number\" && typeof r.ed === \"number\")\n .sort((a, b) => (a.st as number) - (b.st as number));\n\n const links = new Map<number, string>();\n for (const r of asArray<CustomRangeLike>((body as { customRanges?: unknown }).customRanges)) {\n if (r.rangeType === 0 && typeof r.endIndex === \"number\" && typeof r.properties?.url === \"string\") {\n links.set(r.endIndex, r.properties.url);\n }\n }\n // A slice of the stream with its hyperlink tokens read back as `[text](url)`.\n const readSlice = (from: number, to: number): string => {\n let out = \"\";\n for (let k = from; k < to; k++) {\n const c = dataStream[k];\n if (c === LINK_START) out += \"[\";\n else if (c === LINK_END) out += `](${links.get(k) ?? \"\"})`;\n else if (c === CUSTOM_BLOCK) out += blocks.get(k) ?? \"\";\n else out += c;\n }\n return out;\n };\n\n const lines: string[] = [];\n let cursor = 0;\n let runIndex = 0;\n let tableRows: string[][] | null = null;\n\n // Paragraph boundaries are the `\\r`s in the stream itself — reading them from\n // the stream (rather than trusting `paragraphs`) keeps a snapshot written by\n // Univer's own editor, which may index differently, readable.\n for (let i = 0; i <= dataStream.length; i++) {\n const ch = dataStream[i];\n const atEnd = i === dataStream.length;\n if (!atEnd) {\n if (ch === TABLE_START) {\n tableRows = [];\n cursor = i + 1;\n continue;\n }\n if (tableRows && ch === TABLE_ROW_START) {\n tableRows.push([]);\n cursor = i + 1;\n continue;\n }\n if (tableRows && ch === TABLE_CELL_START) {\n tableRows[tableRows.length - 1]?.push(\"\");\n cursor = i + 1;\n continue;\n }\n if (tableRows && (ch === TABLE_CELL_END || ch === TABLE_ROW_END)) {\n cursor = i + 1;\n continue;\n }\n if (tableRows && ch === TABLE_END) {\n if (tableRows.length) lines.push(\"\", ...tableMarkdown(tableRows), \"\");\n tableRows = null;\n cursor = i + 1;\n continue;\n }\n }\n if (!atEnd && ch !== \"\\r\" && ch !== \"\\n\") continue;\n\n const start = cursor;\n const end = i;\n cursor = i + 1;\n if (end <= start) {\n if (ch === \"\\r\" && !tableRows) lines.push(\"\");\n if (atEnd) break;\n continue;\n }\n\n // Collect the runs overlapping this paragraph.\n const paragraphRuns: TextRunLike[] = [];\n let scan = runIndex;\n while (scan < runs.length && (runs[scan]!.st as number) < end) {\n if ((runs[scan]!.ed as number) > start) paragraphRuns.push(runs[scan]!);\n scan++;\n }\n while (\n runIndex < runs.length &&\n (runs[runIndex]!.ed as number) <= end\n ) {\n runIndex++;\n }\n\n const heading = paragraphHeadingLevel(paragraphRuns);\n let text = \"\";\n let at = start;\n for (const run of paragraphRuns) {\n const rs = Math.max(run.st as number, start);\n const re = Math.min(run.ed as number, end);\n if (rs > at) text += readSlice(at, rs);\n if (re > rs) {\n text += emphasize(readSlice(rs, re), run.ts, heading != null);\n }\n at = Math.max(at, re);\n }\n if (at < end) text += readSlice(at, end);\n\n if (tableRows) {\n // A table cell: its paragraphs join into the one cell, pipes escaped.\n const row = tableRows[tableRows.length - 1];\n if (row?.length) {\n const part = text.trim().replace(/\\|/g, \"\\\\|\");\n const last = row.length - 1;\n row[last] = row[last] ? `${row[last]} ${part}` : part;\n }\n } else {\n lines.push(heading ? `${\"#\".repeat(heading)} ${text.trim()}` : text);\n }\n if (atEnd) break;\n }\n\n // Univer keeps one paragraph per visual line; markdown wants a blank line\n // between blocks (but not inside a list or a table), and consecutive blank\n // paragraphs collapse. The writer (`markdownToUniverDoc`) flattens a list item\n // to a literal \"• \" / \"1. \" prefix and a table row to cells joined by\n // TABLE_CELL_SEP, so those shapes are read back as the markdown they came from.\n type Kind = \"text\" | \"list\" | \"table\";\n const kindOf = (line: string): Kind =>\n /^\\s*(?:\\u2022|[-*+]|\\d+[.)])\\s+/.test(line) ? \"list\" : line.includes(TABLE_CELL_SEP) ? \"table\" : \"text\";\n // A document typed or pasted as markdown keeps a table as literal `| a | b |` lines, one paragraph each, often\n // with an empty paragraph between rows: those rows are ONE table, so the gaps between them close.\n const rawRow = (l: string) => /^\\s*\\|.*\\|\\s*$/.test(l);\n const rows = lines.filter((l, i) => {\n if (l.trim()) return true;\n let a = i - 1;\n while (a >= 0 && !lines[a]!.trim()) a--;\n let z = i + 1;\n while (z < lines.length && !lines[z]!.trim()) z++;\n return !(a >= 0 && z < lines.length && rawRow(lines[a]!) && rawRow(lines[z]!));\n });\n const out: string[] = [];\n let prev: Kind | null = null;\n let tableRow = 0;\n for (const raw of rows) {\n if (!raw.trim()) {\n if (out.length && out[out.length - 1] !== \"\") out.push(\"\");\n prev = null;\n continue;\n }\n let line = raw.replace(/\\s+$/, \"\");\n if (rawRow(line)) {\n if (prev !== \"table\" && out.length && out[out.length - 1] !== \"\") out.push(\"\");\n out.push(line.trim());\n prev = \"table\";\n continue;\n }\n const kind = kindOf(line);\n if (kind === \"list\") {\n line = line.replace(/^(\\s*)\\u2022\\s+/, \"$1- \").replace(/^(\\s*)\\*\\s+/, \"$1- \");\n // Four spaces per nesting level in the document; two read as nesting in markdown.\n line = line.replace(/^( +)/, (m) => \" \".repeat(Math.floor(m.length / 2)));\n }\n if (kind === \"table\") {\n const cells = line.split(/\\s*\\|\\s*/).map((c) => c.trim());\n const isHeader = prev !== \"table\";\n const shown = cells.map((c) => (isHeader ? c.replace(/^\\*\\*(.*)\\*\\*$/, \"$1\") : c));\n line = `| ${shown.join(\" | \")} |`;\n if (isHeader) tableRow = shown.length;\n out.push(...(prev === \"table\" || prev === null || !out.length || out[out.length - 1] === \"\" ? [] : [\"\"]));\n out.push(line);\n if (isHeader) out.push(`|${\" --- |\".repeat(tableRow)}`);\n prev = \"table\";\n continue;\n }\n const tight = kind === \"list\" && prev === \"list\";\n if (out.length && out[out.length - 1] !== \"\" && !tight) out.push(\"\");\n out.push(line);\n prev = kind;\n }\n return out.join(\"\\n\").trim();\n}\n","/**\n * A file from the person's computer -> a new document (Word .docx, Markdown,\n * plain text, HTML). Everything lands as markdown first and goes through THE\n * one writer (`markdownToUniverDoc`), so headings, bold, italic, links and\n * lists keep their shape. Parse first: a file we cannot read never leaves an\n * empty document behind.\n */\n\"use client\";\n\nimport { readZip } from \"@ai-matrx/alchemy/operate/zip\";\n\nimport { createDocument, saveDocumentSnapshot, deleteDocument } from \"./document-service\";\nimport { deriveDocumentName, markdownToUniverDoc } from \"./markdown-to-univer-doc\";\nimport { sanitizeUniverDocSnapshot } from \"./sanitize-univer-doc-snapshot\";\n\nexport const DOCUMENT_UPLOAD_ACCEPT = \".docx,.md,.markdown,.txt,.html,.htm\";\n\ntype Source = \"imported_docx\" | \"imported_md\" | \"imported_txt\";\n\nexport function documentFileKind(name: string): \"docx\" | \"md\" | \"txt\" | \"html\" | null {\n const ext = name.toLowerCase().split(\".\").pop() ?? \"\";\n if (ext === \"docx\") return \"docx\";\n if (ext === \"md\" || ext === \"markdown\") return \"md\";\n if (ext === \"txt\") return \"txt\";\n if (ext === \"html\" || ext === \"htm\") return \"html\";\n return null;\n}\n\nconst W = \"http://schemas.openxmlformats.org/wordprocessingml/2006/main\";\n\n/** Rows of cell text -> ONE markdown table block (header, delimiter, body rows together). */\nfunction markdownTable(rows: string[][]): string {\n const width = Math.max(...rows.map((r) => r.length));\n const line = (r: string[]) => `| ${Array.from({ length: width }, (_, i) => r[i] ?? \"\").join(\" | \")} |`;\n return [line(rows[0]!), `|${\" --- |\".repeat(width)}`, ...rows.slice(1).map(line)].join(\"\\n\");\n}\n\n/** Word's document.xml -> markdown: headings, bold/italic runs, list paragraphs, tables as rows. */\nexport function docxXmlToMarkdown(xml: string): string {\n const doc = new DOMParser().parseFromString(xml, \"application/xml\");\n const body = doc.getElementsByTagNameNS(W, \"body\")[0];\n if (!body) throw new Error(\"This Word file has no document body.\");\n const attr = (el: Element | undefined, name: string) => el?.getAttributeNS(W, name) ?? el?.getAttribute(`w:${name}`) ?? null;\n const first = (el: Element, name: string) => el.getElementsByTagNameNS(W, name)[0] as Element | undefined;\n const on = (props: Element | undefined, name: string) => {\n const el = props ? first(props, name) : undefined;\n return !!el && attr(el, \"val\") !== \"0\" && attr(el, \"val\") !== \"false\";\n };\n const paragraph = (p: Element): string => {\n let text = \"\";\n for (const r of Array.from(p.getElementsByTagNameNS(W, \"r\"))) {\n const t = Array.from(r.childNodes)\n .map((n) => {\n const el = n as Element;\n if (el.localName === \"t\") return el.textContent ?? \"\";\n if (el.localName === \"tab\") return \"\\t\";\n if (el.localName === \"br\") return \" \\n\";\n return \"\";\n })\n .join(\"\");\n if (!t) continue;\n const rPr = first(r, \"rPr\");\n const bold = on(rPr, \"b\");\n const italic = on(rPr, \"i\");\n const lead = t.match(/^\\s*/)?.[0] ?? \"\";\n const trail = t.match(/\\s*$/)?.[0] ?? \"\";\n const core = t.trim();\n text += core ? `${lead}${bold ? \"**\" : \"\"}${italic ? \"*\" : \"\"}${core}${italic ? \"*\" : \"\"}${bold ? \"**\" : \"\"}${trail}` : t;\n }\n const pPr = first(p, \"pPr\");\n const style = attr(pPr ? first(pPr, \"pStyle\") : undefined, \"val\") ?? \"\";\n const heading = style.match(/^(?:Heading|heading)\\s?(\\d)$/)?.[1] ?? (style === \"Title\" ? \"1\" : null);\n if (heading && text.trim()) return `${\"#\".repeat(Math.min(6, Number(heading)))} ${text.replace(/\\*+/g, \"\").trim()}`;\n if (pPr && first(pPr, \"numPr\") && text.trim()) {\n const level = Number(attr(first(first(pPr, \"numPr\")!, \"ilvl\"), \"val\") ?? 0);\n return `${\" \".repeat(level)}- ${text.trim()}`;\n }\n return text;\n };\n const out: string[] = [];\n for (const child of Array.from(body.children)) {\n if (child.localName === \"p\") out.push(paragraph(child));\n else if (child.localName === \"tbl\") {\n const rows = Array.from(child.getElementsByTagNameNS(W, \"tr\")).map((tr) =>\n Array.from(tr.getElementsByTagNameNS(W, \"tc\")).map((tc) =>\n Array.from(tc.getElementsByTagNameNS(W, \"p\")).map(paragraph).join(\" \").replace(/\\|/g, \"\\\\|\").trim(),\n ),\n );\n if (rows.length) out.push(markdownTable(rows));\n }\n }\n // A list run stays tight; everything else is its own block.\n return out\n .reduce<string[]>((acc, line, i) => {\n const prevList = /^\\s*- /.test(out[i - 1] ?? \"\");\n if (i > 0 && !(prevList && /^\\s*- /.test(line))) acc.push(\"\");\n acc.push(line);\n return acc;\n }, [])\n .join(\"\\n\")\n .replace(/\\n{3,}/g, \"\\n\\n\")\n .trim();\n}\n\n/** HTML -> markdown for the blocks a document keeps (headings, paragraphs, lists, emphasis, links). */\nexport function htmlToDocumentMarkdown(html: string): string {\n const doc = new DOMParser().parseFromString(html, \"text/html\");\n const inline = (node: Node): string => {\n if (node.nodeType === 3) return (node.textContent ?? \"\").replace(/\\s+/g, \" \");\n if (node.nodeType !== 1) return \"\";\n const el = node as Element;\n const inner = Array.from(el.childNodes).map(inline).join(\"\");\n switch (el.tagName.toLowerCase()) {\n case \"strong\":\n case \"b\":\n return inner.trim() ? `**${inner.trim()}**` : \"\";\n case \"em\":\n case \"i\":\n return inner.trim() ? `*${inner.trim()}*` : \"\";\n case \"a\": {\n const href = el.getAttribute(\"href\");\n return href && /^https?:|^mailto:/i.test(href) ? `[${inner.trim()}](${href})` : inner;\n }\n case \"br\":\n return \" \\n\";\n case \"code\":\n return `\\`${inner}\\``;\n default:\n return inner;\n }\n };\n const out: string[] = [];\n const block = (el: Element, depth = 0): void => {\n const tag = el.tagName.toLowerCase();\n const h = tag.match(/^h([1-6])$/);\n if (h) {\n out.push(`${\"#\".repeat(Number(h[1]))} ${inline(el).trim()}`);\n return;\n }\n if (tag === \"p\" || tag === \"blockquote\") {\n out.push(inline(el).trim());\n return;\n }\n if (tag === \"table\") {\n const rows = Array.from(el.querySelectorAll(\"tr\")).map((tr) =>\n Array.from(tr.children)\n .filter((c) => /^(td|th)$/i.test(c.tagName))\n .map((c) => inline(c).replace(/\\|/g, \"\\\\|\").replace(/\\s+/g, \" \").trim()),\n );\n if (rows.length) out.push(markdownTable(rows));\n return;\n }\n if (tag === \"pre\") {\n out.push(\"```\", el.textContent ?? \"\", \"```\");\n return;\n }\n if (tag === \"ul\" || tag === \"ol\") {\n Array.from(el.children).forEach((li, n) => {\n if (li.tagName.toLowerCase() !== \"li\") return;\n const own = Array.from(li.childNodes).filter((c) => !(c.nodeType === 1 && /^(ul|ol)$/i.test((c as Element).tagName)));\n out.push(`${\" \".repeat(depth)}${tag === \"ol\" ? `${n + 1}.` : \"-\"} ${own.map(inline).join(\"\").trim()}`);\n Array.from(li.children)\n .filter((c) => /^(ul|ol)$/i.test(c.tagName))\n .forEach((c) => block(c, depth + 1));\n });\n return;\n }\n if (tag === \"script\" || tag === \"style\" || tag === \"head\") return;\n const kids = Array.from(el.children);\n if (kids.length === 0) {\n const t = inline(el).trim();\n if (t) out.push(t);\n return;\n }\n kids.forEach((k) => block(k, depth));\n };\n block(doc.body);\n return out\n .map((l) => l.replace(/\\s+$/, \"\"))\n .reduce<string[]>((acc, line, i) => {\n const list = (s: string | undefined) => /^\\s*(-|\\d+\\.) /.test(s ?? \"\");\n if (i > 0 && !(list(out[i - 1]) && list(line))) acc.push(\"\");\n acc.push(line);\n return acc;\n }, [])\n .join(\"\\n\")\n .replace(/\\n{3,}/g, \"\\n\\n\")\n .trim();\n}\n\nexport async function documentFileToMarkdown(file: File): Promise<{ markdown: string; source: Source }> {\n const kind = documentFileKind(file.name);\n if (kind === \"docx\") {\n const zip = await readZip(file);\n const entry = zip.get(\"word/document.xml\");\n if (!entry) throw new Error(\"This is not a Word document (.docx).\");\n const xml = await entry.text();\n return { markdown: docxXmlToMarkdown(xml), source: \"imported_docx\" };\n }\n const text = await file.text();\n if (kind === \"md\") return { markdown: text, source: \"imported_md\" };\n if (kind === \"html\") return { markdown: htmlToDocumentMarkdown(text), source: \"imported_md\" };\n if (kind === \"txt\") {\n // Plain text: each line is its own paragraph; nothing in it is read as markdown.\n const escaped = text\n .replace(/\\r\\n?/g, \"\\n\")\n .split(\"\\n\")\n .map((l) => l.replace(/([\\\\`*_[\\]#>|~])/g, \"\\\\$1\"))\n .join(\"\\n\\n\");\n return { markdown: escaped, source: \"imported_txt\" };\n }\n throw new Error(\"Upload a Word (.docx), Markdown, text or HTML file.\");\n}\n\n/** Create a document from an uploaded file; returns its id. */\nexport async function importDocumentFile(file: File, organizationId: string): Promise<{ id: string; name: string }> {\n const { markdown, source } = await documentFileToMarkdown(file);\n const name = file.name.replace(/\\.[^.]+$/, \"\").trim() || deriveDocumentName(markdown);\n const created = await createDocument({ name, source, organizationId, description: `Imported from ${file.name}` });\n if (!created.success) throw new Error(`Could not create the document: ${created.error}`);\n const id = created.data.id;\n const snapshot = sanitizeUniverDocSnapshot(markdownToUniverDoc(markdown, name), id);\n const saved = await saveDocumentSnapshot({ documentId: id, snapshot, origin: \"imported\", label: `Imported from ${file.name}` });\n if (!saved.success) {\n await deleteDocument(id);\n throw new Error(`Could not save the imported content: ${saved.error}`);\n }\n return { id, name };\n}\n","/**\n * File → Download → EPUB / OpenDocument (.odt) / Rich Text (.rtf).\n * EPUB the print package's EPUB engine, straight from the live text.\n * ODT/RTF the same engine writes the .docx, then aidream's LibreOffice route\n * (`POST /office/convert-document`) converts it. Nothing is stored.\n * Engine, converter and saver are injected so the forcing test runs the real logic.\n */\n\nimport { getDocsHost } from \"./ports\";\n\nexport type OpenFormat = \"epub\" | \"odt\" | \"rtf\";\n\nexport const OPEN_FORMATS: Record<OpenFormat, { label: string; extension: string; mime: string }> = {\n epub: { label: \"EPUB\", extension: \"epub\", mime: \"application/epub+zip\" },\n odt: { label: \"OpenDocument\", extension: \"odt\", mime: \"application/vnd.oasis.opendocument.text\" },\n rtf: { label: \"Rich Text\", extension: \"rtf\", mime: \"application/rtf\" },\n};\n\nexport interface OpenFormatDeps {\n engine: (markdown: string, format: \"docx\" | \"epub\", fileName: string) => Promise<{ bytes: Uint8Array; fileName: string }>;\n convert: (docx: Uint8Array, fileName: string, target: \"odt\" | \"rtf\") => Promise<Uint8Array>;\n save: (fileName: string, bytes: Uint8Array, mime: string) => void;\n}\n\nfunction baseName(title: string | null | undefined): string {\n const base = (title ?? \"\").replace(/[\\\\/:*?\"<>|\\u0000-\\u001f]+/g, \" \").replace(/\\s+/g, \" \").trim().slice(0, 120);\n return base || \"document\";\n}\n\nexport async function exportOpenFormat(markdown: string, format: OpenFormat, title: string | null | undefined, deps: OpenFormatDeps): Promise<string> {\n const name = baseName(title);\n const spec = OPEN_FORMATS[format];\n const fileName = `${name}.${spec.extension}`;\n if (format === \"epub\") {\n const out = await deps.engine(markdown, \"epub\", name);\n deps.save(fileName, out.bytes, spec.mime);\n return fileName;\n }\n const docx = await deps.engine(markdown, \"docx\", name);\n const bytes = await deps.convert(docx.bytes, docx.fileName, format);\n if (!bytes.byteLength) throw new Error(`The ${spec.label} converter returned an empty file.`);\n deps.save(fileName, bytes, spec.mime);\n return fileName;\n}\n\nexport async function downloadOpenFormat(markdown: string, format: OpenFormat, title: string | null | undefined): Promise<string> {\n return exportOpenFormat(markdown, format, title, {\n engine: async (md, fmt, fileName) => {\n const { exportDocument } = await import(\"@ai-matrx/print/document\");\n const exp = await exportDocument(md, fmt, { fileName });\n return { bytes: exp.bytes, fileName: exp.fileName };\n },\n convert: async (docx, fileName, target) => {\n const form = new FormData();\n form.append(\"file\", new Blob([docx as BlobPart]), fileName.endsWith(\".docx\") ? fileName : `${fileName}.docx`);\n form.append(\"target\", target);\n const res = await getDocsHost().requestRaw(\"/office/convert-document\", { method: \"POST\", body: form });\n return new Uint8Array(await res.arrayBuffer());\n },\n save: (fileName, bytes, mime) => {\n const url = URL.createObjectURL(new Blob([bytes as BlobPart], { type: mime }));\n const link = document.createElement(\"a\");\n link.href = url;\n link.download = fileName;\n link.rel = \"noopener\";\n document.body.appendChild(link);\n link.click();\n link.remove();\n setTimeout(() => URL.revokeObjectURL(url), 30_000);\n },\n });\n}\n"],"mappings":"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","names":["out"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-matrx/office",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.3",
|
|
4
4
|
"description": "The AI Matrx Office layer: the editors' shared command and menu model, the Google and Microsoft ribbon model, page setup, the Office record types and the configured product hosts. The Univer glue shared by Sheets and Docs. Sheets, Docs and the other products arrive as sections of this one package.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/core.cjs",
|
|
@@ -173,15 +173,15 @@
|
|
|
173
173
|
"ot-json1": "^1.0.2",
|
|
174
174
|
"y-protocols": "^1.0.7",
|
|
175
175
|
"yjs": "^13.6.33",
|
|
176
|
-
"@ai-matrx/auth": "latest",
|
|
177
176
|
"@ai-matrx/api": "latest",
|
|
177
|
+
"@ai-matrx/auth": "latest",
|
|
178
178
|
"@ai-matrx/content-ir": "latest",
|
|
179
179
|
"@ai-matrx/alchemy": "latest",
|
|
180
180
|
"@ai-matrx/data": "latest",
|
|
181
181
|
"@ai-matrx/kit": "latest",
|
|
182
|
+
"@ai-matrx/print": "latest",
|
|
182
183
|
"@ai-matrx/records": "latest",
|
|
183
|
-
"@ai-matrx/rich-content": "latest"
|
|
184
|
-
"@ai-matrx/print": "latest"
|
|
184
|
+
"@ai-matrx/rich-content": "latest"
|
|
185
185
|
},
|
|
186
186
|
"peerDependencies": {
|
|
187
187
|
"@supabase/supabase-js": ">=2.0.0",
|
|
@@ -235,5 +235,5 @@
|
|
|
235
235
|
"test:watch": "vitest",
|
|
236
236
|
"verify:tarball": "node ./scripts/verify-tarball.mjs"
|
|
237
237
|
},
|
|
238
|
-
"gitHead": "
|
|
238
|
+
"gitHead": "3309120909f52ecfd88fd798c9fd330f452460bb"
|
|
239
239
|
}
|