@ai-matrx/office 0.8.65 → 0.8.66

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 CHANGED
@@ -1,5 +1,10 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.8.66
4
+
5
+ UNVERIFIED: this release has no Unreleased entry and the source comparison could not be made (the caller supplied no source comparison). Whether source changed is unknown; consumer action is unknown.
6
+ Check the package's gitHead on npm against this version's gitHead: `git diff <previous gitHead> <this gitHead> -- apps/shared/office/src`.
7
+
3
8
  ## 0.8.65
4
9
 
5
10
  - Correct `DocumentRow.organization_id` to the non-null UUID required by `workbench.udt_documents`. Consumer action: pass the document's organization ID when constructing a row; database reads already carry it.
package/dist/docs-io.cjs CHANGED
@@ -310,13 +310,39 @@ function docxNumbering(xml) {
310
310
  }
311
311
  return (numId, level) => _nullishCoalesce(_optionalChain([abstract, 'access', _35 => _35.get, 'call', _36 => _36(_nullishCoalesce(nums.get(numId), () => ( ""))), 'optionalAccess', _37 => _37.get, 'call', _38 => _38(level)]), () => ( null));
312
312
  }
313
+ function docxStyleNumbering(xml) {
314
+ const none = { numId: null, ilvl: null };
315
+ if (!xml) return () => none;
316
+ const doc = new DOMParser().parseFromString(xml, "application/xml");
317
+ const val = (el, name) => _nullishCoalesce(_nullishCoalesce(_optionalChain([el, 'optionalAccess', _39 => _39.getAttributeNS, 'call', _40 => _40(W, name)]), () => ( _optionalChain([el, 'optionalAccess', _41 => _41.getAttribute, 'call', _42 => _42(`w:${name}`)]))), () => ( null));
318
+ const styles = /* @__PURE__ */ new Map();
319
+ for (const st of Array.from(doc.getElementsByTagNameNS(W, "style"))) styles.set(_nullishCoalesce(val(st, "styleId"), () => ( "")), st);
320
+ return (styleId) => {
321
+ let numId = null;
322
+ let ilvl = null;
323
+ const seen = /* @__PURE__ */ new Set();
324
+ for (let id = styleId; id && !seen.has(id); ) {
325
+ seen.add(id);
326
+ const st = styles.get(id);
327
+ if (!st) break;
328
+ const numPr = st.getElementsByTagNameNS(W, "numPr")[0];
329
+ if (numPr) {
330
+ numId ??= val(numPr.getElementsByTagNameNS(W, "numId")[0], "val");
331
+ ilvl ??= val(numPr.getElementsByTagNameNS(W, "ilvl")[0], "val");
332
+ }
333
+ id = val(st.getElementsByTagNameNS(W, "basedOn")[0], "val");
334
+ }
335
+ return { numId, ilvl };
336
+ };
337
+ }
313
338
  function docxXmlToMarkdown(xml, parts = {}) {
314
339
  const doc = new DOMParser().parseFromString(xml, "application/xml");
315
340
  const body = doc.getElementsByTagNameNS(W, "body")[0];
316
341
  if (!body) throw new Error("This Word file has no document body.");
317
342
  const rels = docxRelationships(parts.rels);
318
343
  const listFormat = docxNumbering(parts.numbering);
319
- const attr = (el, name) => _nullishCoalesce(_nullishCoalesce(_optionalChain([el, 'optionalAccess', _39 => _39.getAttributeNS, 'call', _40 => _40(W, name)]), () => ( _optionalChain([el, 'optionalAccess', _41 => _41.getAttribute, 'call', _42 => _42(`w:${name}`)]))), () => ( null));
344
+ const styleNum = docxStyleNumbering(parts.styles);
345
+ const attr = (el, name) => _nullishCoalesce(_nullishCoalesce(_optionalChain([el, 'optionalAccess', _43 => _43.getAttributeNS, 'call', _44 => _44(W, name)]), () => ( _optionalChain([el, 'optionalAccess', _45 => _45.getAttribute, 'call', _46 => _46(`w:${name}`)]))), () => ( null));
320
346
  const first = (el, name) => el.getElementsByTagNameNS(W, name)[0];
321
347
  const on = (props, name) => {
322
348
  const el = props ? first(props, name) : void 0;
@@ -325,11 +351,11 @@ function docxXmlToMarkdown(xml, parts = {}) {
325
351
  const picture = (r) => {
326
352
  const all = Array.from(r.getElementsByTagName("*"));
327
353
  const blip = all.find((e) => e.localName === "blip");
328
- const id = _optionalChain([blip, 'optionalAccess', _43 => _43.getAttributeNS, 'call', _44 => _44(R, "embed")]) || _optionalChain([blip, 'optionalAccess', _45 => _45.getAttribute, 'call', _46 => _46("r:embed")]) || _optionalChain([blip, 'optionalAccess', _47 => _47.getAttributeNS, 'call', _48 => _48(R, "link")]) || _optionalChain([blip, 'optionalAccess', _49 => _49.getAttribute, 'call', _50 => _50("r:link")]);
354
+ const id = _optionalChain([blip, 'optionalAccess', _47 => _47.getAttributeNS, 'call', _48 => _48(R, "embed")]) || _optionalChain([blip, 'optionalAccess', _49 => _49.getAttribute, 'call', _50 => _50("r:embed")]) || _optionalChain([blip, 'optionalAccess', _51 => _51.getAttributeNS, 'call', _52 => _52(R, "link")]) || _optionalChain([blip, 'optionalAccess', _53 => _53.getAttribute, 'call', _54 => _54("r:link")]);
329
355
  const target = id ? rels.get(id) : void 0;
330
356
  if (!target) return "";
331
357
  const named = all.find((e) => e.localName === "docPr");
332
- const alt = (_optionalChain([named, 'optionalAccess', _51 => _51.getAttribute, 'call', _52 => _52("descr")]) || _optionalChain([named, 'optionalAccess', _53 => _53.getAttribute, 'call', _54 => _54("title")]) || "").replace(/[[\]]/g, "");
358
+ const alt = (_optionalChain([named, 'optionalAccess', _55 => _55.getAttribute, 'call', _56 => _56("descr")]) || _optionalChain([named, 'optionalAccess', _57 => _57.getAttribute, 'call', _58 => _58("title")]) || "").replace(/[[\]]/g, "");
333
359
  return `![${alt}](${target.replace(/ /g, "%20")})`;
334
360
  };
335
361
  const run = (r) => {
@@ -346,8 +372,8 @@ function docxXmlToMarkdown(xml, parts = {}) {
346
372
  const bold = on(rPr, "b");
347
373
  const italic = on(rPr, "i");
348
374
  const strike = on(rPr, "strike");
349
- const lead = _nullishCoalesce(_optionalChain([t, 'access', _55 => _55.match, 'call', _56 => _56(/^\s*/), 'optionalAccess', _57 => _57[0]]), () => ( ""));
350
- const trail = _nullishCoalesce(_optionalChain([t, 'access', _58 => _58.match, 'call', _59 => _59(/\s*$/), 'optionalAccess', _60 => _60[0]]), () => ( ""));
375
+ const lead = _nullishCoalesce(_optionalChain([t, 'access', _59 => _59.match, 'call', _60 => _60(/^\s*/), 'optionalAccess', _61 => _61[0]]), () => ( ""));
376
+ const trail = _nullishCoalesce(_optionalChain([t, 'access', _62 => _62.match, 'call', _63 => _63(/\s*$/), 'optionalAccess', _64 => _64[0]]), () => ( ""));
351
377
  const core = t.trim();
352
378
  if (!core) return t + pic;
353
379
  let marked = core;
@@ -376,12 +402,15 @@ function docxXmlToMarkdown(xml, parts = {}) {
376
402
  const text = inline(p);
377
403
  const pPr = first(p, "pPr");
378
404
  const style = _nullishCoalesce(attr(pPr ? first(pPr, "pStyle") : void 0, "val"), () => ( ""));
379
- const heading = _nullishCoalesce(_optionalChain([style, 'access', _61 => _61.match, 'call', _62 => _62(/^(?:Heading|heading)\s?(\d)$/), 'optionalAccess', _63 => _63[1]]), () => ( (style === "Title" ? "1" : null)));
405
+ const heading = _nullishCoalesce(_optionalChain([style, 'access', _65 => _65.match, 'call', _66 => _66(/^(?:Heading|heading)\s?(\d)$/), 'optionalAccess', _67 => _67[1]]), () => ( (style === "Title" ? "1" : null)));
380
406
  if (heading && text.trim()) return `${"#".repeat(Math.min(6, Number(heading)))} ${text.replace(/\*+/g, "").trim()}`;
381
407
  const numPr = pPr ? first(pPr, "numPr") : void 0;
382
- if (numPr && text.trim()) {
383
- const level = Number(_nullishCoalesce(attr(first(numPr, "ilvl"), "val"), () => ( 0)));
384
- const fmt = listFormat(_nullishCoalesce(attr(first(numPr, "numId"), "val"), () => ( "")), level);
408
+ const fromStyle = styleNum(style);
409
+ const numId = _nullishCoalesce(attr(numPr ? first(numPr, "numId") : void 0, "val"), () => ( fromStyle.numId));
410
+ const ilvl = _nullishCoalesce(attr(numPr ? first(numPr, "ilvl") : void 0, "val"), () => ( fromStyle.ilvl));
411
+ if ((numPr || numId) && numId !== "0" && text.trim()) {
412
+ const level = Number(_nullishCoalesce(ilvl, () => ( 0)));
413
+ const fmt = listFormat(_nullishCoalesce(numId, () => ( "")), level);
385
414
  const marker = fmt && fmt !== "bullet" && fmt !== "none" ? "1." : "-";
386
415
  return `${" ".repeat(level)}${marker} ${text.trim()}`;
387
416
  }
@@ -494,9 +523,10 @@ async function documentFileToMarkdown(file) {
494
523
  const entry = zip.get("word/document.xml");
495
524
  if (!entry) throw new Error("This is not a Word document (.docx).");
496
525
  const xml = await entry.text();
497
- const rels = await _asyncNullishCoalesce(await _optionalChain([zip, 'access', _64 => _64.get, 'call', _65 => _65("word/_rels/document.xml.rels"), 'optionalAccess', _66 => _66.text, 'call', _67 => _67()]), async () => ( null));
498
- const numbering = await _asyncNullishCoalesce(await _optionalChain([zip, 'access', _68 => _68.get, 'call', _69 => _69("word/numbering.xml"), 'optionalAccess', _70 => _70.text, 'call', _71 => _71()]), async () => ( null));
499
- const markdown = docxXmlToMarkdown(xml, { rels, numbering }).replace(/!\[[^\]]*\]\(word\/[^)]*\)/g, "");
526
+ const rels = await _asyncNullishCoalesce(await _optionalChain([zip, 'access', _68 => _68.get, 'call', _69 => _69("word/_rels/document.xml.rels"), 'optionalAccess', _70 => _70.text, 'call', _71 => _71()]), async () => ( null));
527
+ const numbering = await _asyncNullishCoalesce(await _optionalChain([zip, 'access', _72 => _72.get, 'call', _73 => _73("word/numbering.xml"), 'optionalAccess', _74 => _74.text, 'call', _75 => _75()]), async () => ( null));
528
+ const styles = await _asyncNullishCoalesce(await _optionalChain([zip, 'access', _76 => _76.get, 'call', _77 => _77("word/styles.xml"), 'optionalAccess', _78 => _78.text, 'call', _79 => _79()]), async () => ( null));
529
+ const markdown = docxXmlToMarkdown(xml, { rels, numbering, styles }).replace(/!\[[^\]]*\]\(word\/[^)]*\)/g, "");
500
530
  return { markdown, source: "imported_docx" };
501
531
  }
502
532
  const text = await file.text();
@@ -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\nimport { isPipeLedRow } from \"@ai-matrx/content-ir/source\";\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 * Backslash-escape typed text that a markdown reader would take for HTML (`<b>`, `<A22-klm>`) or an entity\n * (`&amp;`), so every renderer and exporter (PDF, Word, EPUB, HTML, print, the shared view) shows the typed text.\n * Default true. Pass false only for a consumer that treats the text as literal characters, not markdown (the\n * Google push writes the string into a Google Doc as is; an agent reading the passage; a source view).\n */\n escapeMarkup?: boolean;\n}\n\n/**\n * One typed character as markdown that renders back as the same character. Markdown readers (the print engine behind\n * PDF / Word / EPUB / HTML among them) treat `<tag>` as raw HTML and drop it, and read `&amp;` as an entity, so typed\n * text that looks like either is backslash-escaped (`\\<`, `\\&`); a plain `<` or `&` (`A < B`, `a&b`) is left alone.\n * A backslash right before one of those is doubled so it cannot swallow the escape.\n */\nfunction markdownSafeChar(stream: string, at: number): string {\n const c = stream[at]!;\n const next = stream[at + 1] ?? \"\";\n if (c === \"<\" && /[A-Za-z/!?]/.test(next)) return \"\\\\<\";\n if (c === \"&\" && /^#?\\w+;/.test(stream.slice(at + 1, at + 34))) return \"\\\\&\";\n if (c === \"\\\\\" && (next === \"<\" || next === \"&\")) return \"\\\\\\\\\";\n return c;\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 const escapeMarkup = options.escapeMarkup !== false;\n\n // Pictures: each CUSTOM_BLOCK token is read back as `![alt](source)`.\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 += escapeMarkup ? markdownSafeChar(dataStream, k) : 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 = isPipeLedRow;\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\";\nconst R = \"http://schemas.openxmlformats.org/officeDocument/2006/relationships\";\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/**\n * The parts of a .docx beside `word/document.xml` that its body points into. Each is optional: without\n * `rels` a link keeps only its words and a picture is skipped; without `numbering` every list is bulleted.\n */\nexport interface DocxParts {\n /** `word/_rels/document.xml.rels` — link addresses and picture files by relationship id. */\n rels?: string | null;\n /** `word/numbering.xml` — which lists are numbered. */\n numbering?: string | null;\n}\n\n/** Relationship id -> target. An internal target is written as its path inside the archive (`word/media/image1.png`). */\nexport function docxRelationships(rels: string | null | undefined): Map<string, string> {\n const out = new Map<string, string>();\n if (!rels) return out;\n const doc = new DOMParser().parseFromString(rels, \"application/xml\");\n for (const el of Array.from(doc.getElementsByTagName(\"*\"))) {\n if (el.localName !== \"Relationship\") continue;\n const id = el.getAttribute(\"Id\");\n const target = el.getAttribute(\"Target\");\n if (!id || !target) continue;\n const external = el.getAttribute(\"TargetMode\") === \"External\" || /^[a-z][a-z0-9+.-]*:/i.test(target);\n out.set(id, external ? target : target.startsWith(\"/\") ? target.slice(1) : `word/${target}`.replace(/\\/\\.\\//g, \"/\"));\n }\n return out;\n}\n\n/** numId -> (level -> numFmt), from numbering.xml. */\nfunction docxNumbering(xml: string | null | undefined): (numId: string, level: number) => string | null {\n if (!xml) return () => null;\n const doc = new DOMParser().parseFromString(xml, \"application/xml\");\n const val = (el: Element | undefined, name: string) => el?.getAttributeNS(W, name) ?? el?.getAttribute(`w:${name}`) ?? null;\n const abstract = new Map<string, Map<number, string>>();\n for (const a of Array.from(doc.getElementsByTagNameNS(W, \"abstractNum\"))) {\n const levels = new Map<number, string>();\n for (const lvl of Array.from(a.getElementsByTagNameNS(W, \"lvl\"))) {\n const fmt = val(lvl.getElementsByTagNameNS(W, \"numFmt\")[0], \"val\");\n if (fmt) levels.set(Number(val(lvl, \"ilvl\") ?? 0), fmt);\n }\n abstract.set(val(a, \"abstractNumId\") ?? \"\", levels);\n }\n const nums = new Map<string, string>();\n for (const n of Array.from(doc.getElementsByTagNameNS(W, \"num\"))) {\n const ref = val(n.getElementsByTagNameNS(W, \"abstractNumId\")[0], \"val\");\n if (ref != null) nums.set(val(n, \"numId\") ?? \"\", ref);\n }\n return (numId, level) => abstract.get(nums.get(numId) ?? \"\")?.get(level) ?? null;\n}\n\n/**\n * Word's document.xml -> markdown: headings, bold/italic/strike runs, links (with `parts.rels`), pictures as\n * `![alt](word/media/…)` (with `parts.rels`; the caller resolves the path to a stored file), bulleted and\n * numbered list paragraphs (numbered with `parts.numbering`), tables as rows.\n */\nexport function docxXmlToMarkdown(xml: string, parts: DocxParts = {}): 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 rels = docxRelationships(parts.rels);\n const listFormat = docxNumbering(parts.numbering);\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 picture = (r: Element): string => {\n const all = Array.from(r.getElementsByTagName(\"*\"));\n const blip = all.find((e) => e.localName === \"blip\");\n const id = blip?.getAttributeNS(R, \"embed\") || blip?.getAttribute(\"r:embed\") || blip?.getAttributeNS(R, \"link\") || blip?.getAttribute(\"r:link\");\n const target = id ? rels.get(id) : undefined;\n if (!target) return \"\";\n const named = all.find((e) => e.localName === \"docPr\");\n const alt = (named?.getAttribute(\"descr\") || named?.getAttribute(\"title\") || \"\").replace(/[[\\]]/g, \"\");\n return `![${alt}](${target.replace(/ /g, \"%20\")})`;\n };\n const run = (r: Element): string => {\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 const pic = Array.from(r.childNodes).some((n) => (n as Element).localName === \"drawing\" || (n as Element).localName === \"pict\") ? picture(r) : \"\";\n if (!t) return pic;\n const rPr = first(r, \"rPr\");\n const bold = on(rPr, \"b\");\n const italic = on(rPr, \"i\");\n const strike = on(rPr, \"strike\");\n const lead = t.match(/^\\s*/)?.[0] ?? \"\";\n const trail = t.match(/\\s*$/)?.[0] ?? \"\";\n const core = t.trim();\n if (!core) return t + pic;\n let marked = core;\n if (bold) marked = `**${marked}**`;\n if (italic) marked = `*${marked}*`;\n if (strike) marked = `~~${marked}~~`;\n return `${lead}${marked}${trail}${pic}`;\n };\n const inline = (parent: Element): string => {\n let text = \"\";\n for (const node of Array.from(parent.childNodes)) {\n const el = node as Element;\n if (el.nodeType !== 1) continue;\n if (el.localName === \"r\") text += run(el);\n else if (el.localName === \"hyperlink\") {\n const words = inline(el);\n const id = el.getAttributeNS(R, \"id\") || el.getAttribute(\"r:id\");\n const href = id ? rels.get(id) : undefined;\n const anchor = el.getAttributeNS(W, \"anchor\") || el.getAttribute(\"w:anchor\");\n text += words.trim() && (href || anchor) ? `[${words.trim()}](${href ?? `#${anchor}`})` : words;\n } else if (el.localName === \"ins\" || el.localName === \"smartTag\" || el.localName === \"sdt\" || el.localName === \"sdtContent\" || el.localName === \"fldSimple\") text += inline(el);\n }\n return text;\n };\n const paragraph = (p: Element): string => {\n const text = inline(p);\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 const numPr = pPr ? first(pPr, \"numPr\") : undefined;\n if (numPr && text.trim()) {\n const level = Number(attr(first(numPr, \"ilvl\"), \"val\") ?? 0);\n const fmt = listFormat(attr(first(numPr, \"numId\"), \"val\") ?? \"\", level);\n const marker = fmt && fmt !== \"bullet\" && fmt !== \"none\" ? \"1.\" : \"-\";\n return `${\" \".repeat(level)}${marker} ${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\") {\n // A figure caption under a picture (\"Figure 1: Team photo\") repeats the picture's description.\n const pPr = first(child, \"pPr\");\n const style = attr(pPr ? first(pPr, \"pStyle\") : undefined, \"val\") ?? \"\";\n if (/^caption$/i.test(style) && /^!\\[[^\\]]*\\]\\([^)]*\\)$/.test(out[out.length - 1] ?? \"\")) continue;\n 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 // A header row drawn bold is a header row, not bold words.\n const head = rows[0];\n if (head && head.every((c) => !c || /^\\*\\*[^*]+\\*\\*$/.test(c))) rows[0] = head.map((c) => c.replace(/^\\*\\*|\\*\\*$/g, \"\"));\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 isList = (l: string | undefined) => /^\\s*(-|\\d+\\.) /.test(l ?? \"\");\n if (i > 0 && !(isList(out[i - 1]) && isList(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 const rels = (await zip.get(\"word/_rels/document.xml.rels\")?.text()) ?? null;\n const numbering = (await zip.get(\"word/numbering.xml\")?.text()) ?? null;\n // A picture stored inside the file has no address a Docs document can draw, so it stays out here (as\n // before); a Spaces import reads `docxXmlToMarkdown` itself and stores each picture as a file.\n const markdown = docxXmlToMarkdown(xml, { rels, numbering }).replace(/!\\[[^\\]]*\\]\\(word\\/[^)]*\\)/g, \"\");\n return { markdown, 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 { downloadFile } from \"@ai-matrx/kit/download\";\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) => downloadFile(fileName, bytes as BlobPart, mime),\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\nimport { isPipeLedRow } from \"@ai-matrx/content-ir/source\";\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 * Backslash-escape typed text that a markdown reader would take for HTML (`<b>`, `<A22-klm>`) or an entity\n * (`&amp;`), so every renderer and exporter (PDF, Word, EPUB, HTML, print, the shared view) shows the typed text.\n * Default true. Pass false only for a consumer that treats the text as literal characters, not markdown (the\n * Google push writes the string into a Google Doc as is; an agent reading the passage; a source view).\n */\n escapeMarkup?: boolean;\n}\n\n/**\n * One typed character as markdown that renders back as the same character. Markdown readers (the print engine behind\n * PDF / Word / EPUB / HTML among them) treat `<tag>` as raw HTML and drop it, and read `&amp;` as an entity, so typed\n * text that looks like either is backslash-escaped (`\\<`, `\\&`); a plain `<` or `&` (`A < B`, `a&b`) is left alone.\n * A backslash right before one of those is doubled so it cannot swallow the escape.\n */\nfunction markdownSafeChar(stream: string, at: number): string {\n const c = stream[at]!;\n const next = stream[at + 1] ?? \"\";\n if (c === \"<\" && /[A-Za-z/!?]/.test(next)) return \"\\\\<\";\n if (c === \"&\" && /^#?\\w+;/.test(stream.slice(at + 1, at + 34))) return \"\\\\&\";\n if (c === \"\\\\\" && (next === \"<\" || next === \"&\")) return \"\\\\\\\\\";\n return c;\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 const escapeMarkup = options.escapeMarkup !== false;\n\n // Pictures: each CUSTOM_BLOCK token is read back as `![alt](source)`.\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 += escapeMarkup ? markdownSafeChar(dataStream, k) : 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 = isPipeLedRow;\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\";\nconst R = \"http://schemas.openxmlformats.org/officeDocument/2006/relationships\";\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/**\n * The parts of a .docx beside `word/document.xml` that its body points into. Each is optional: without\n * `rels` a link keeps only its words and a picture is skipped; without `numbering` every list is bulleted.\n */\nexport interface DocxParts {\n /** `word/_rels/document.xml.rels` — link addresses and picture files by relationship id. */\n rels?: string | null;\n /** `word/numbering.xml` — which lists are numbered. */\n numbering?: string | null;\n /** `word/styles.xml` — list numbering a paragraph STYLE carries (\"List Bullet\", \"List Number\"). */\n styles?: string | null;\n}\n\n/** Relationship id -> target. An internal target is written as its path inside the archive (`word/media/image1.png`). */\nexport function docxRelationships(rels: string | null | undefined): Map<string, string> {\n const out = new Map<string, string>();\n if (!rels) return out;\n const doc = new DOMParser().parseFromString(rels, \"application/xml\");\n for (const el of Array.from(doc.getElementsByTagName(\"*\"))) {\n if (el.localName !== \"Relationship\") continue;\n const id = el.getAttribute(\"Id\");\n const target = el.getAttribute(\"Target\");\n if (!id || !target) continue;\n const external = el.getAttribute(\"TargetMode\") === \"External\" || /^[a-z][a-z0-9+.-]*:/i.test(target);\n out.set(id, external ? target : target.startsWith(\"/\") ? target.slice(1) : `word/${target}`.replace(/\\/\\.\\//g, \"/\"));\n }\n return out;\n}\n\n/** numId -> (level -> numFmt), from numbering.xml. */\nfunction docxNumbering(xml: string | null | undefined): (numId: string, level: number) => string | null {\n if (!xml) return () => null;\n const doc = new DOMParser().parseFromString(xml, \"application/xml\");\n const val = (el: Element | undefined, name: string) => el?.getAttributeNS(W, name) ?? el?.getAttribute(`w:${name}`) ?? null;\n const abstract = new Map<string, Map<number, string>>();\n for (const a of Array.from(doc.getElementsByTagNameNS(W, \"abstractNum\"))) {\n const levels = new Map<number, string>();\n for (const lvl of Array.from(a.getElementsByTagNameNS(W, \"lvl\"))) {\n const fmt = val(lvl.getElementsByTagNameNS(W, \"numFmt\")[0], \"val\");\n if (fmt) levels.set(Number(val(lvl, \"ilvl\") ?? 0), fmt);\n }\n abstract.set(val(a, \"abstractNumId\") ?? \"\", levels);\n }\n const nums = new Map<string, string>();\n for (const n of Array.from(doc.getElementsByTagNameNS(W, \"num\"))) {\n const ref = val(n.getElementsByTagNameNS(W, \"abstractNumId\")[0], \"val\");\n if (ref != null) nums.set(val(n, \"numId\") ?? \"\", ref);\n }\n return (numId, level) => abstract.get(nums.get(numId) ?? \"\")?.get(level) ?? null;\n}\n\n/** style id -> the numId/ilvl its w:numPr declares, following w:basedOn (nearest style wins per field). */\nfunction docxStyleNumbering(xml: string | null | undefined): (styleId: string) => { numId: string | null; ilvl: string | null } {\n const none = { numId: null, ilvl: null };\n if (!xml) return () => none;\n const doc = new DOMParser().parseFromString(xml, \"application/xml\");\n const val = (el: Element | undefined, name: string) => el?.getAttributeNS(W, name) ?? el?.getAttribute(`w:${name}`) ?? null;\n const styles = new Map<string, Element>();\n for (const st of Array.from(doc.getElementsByTagNameNS(W, \"style\"))) styles.set(val(st, \"styleId\") ?? \"\", st);\n return (styleId) => {\n let numId: string | null = null;\n let ilvl: string | null = null;\n const seen = new Set<string>();\n for (let id: string | null = styleId; id && !seen.has(id); ) {\n seen.add(id);\n const st = styles.get(id);\n if (!st) break;\n const numPr = st.getElementsByTagNameNS(W, \"numPr\")[0];\n if (numPr) {\n numId ??= val(numPr.getElementsByTagNameNS(W, \"numId\")[0], \"val\");\n ilvl ??= val(numPr.getElementsByTagNameNS(W, \"ilvl\")[0], \"val\");\n }\n id = val(st.getElementsByTagNameNS(W, \"basedOn\")[0], \"val\");\n }\n return { numId, ilvl };\n };\n}\n\n/**\n * Word's document.xml -> markdown: headings, bold/italic/strike runs, links (with `parts.rels`), pictures as\n * `![alt](word/media/…)` (with `parts.rels`; the caller resolves the path to a stored file), bulleted and\n * numbered list paragraphs (numbered with `parts.numbering`), tables as rows.\n */\nexport function docxXmlToMarkdown(xml: string, parts: DocxParts = {}): 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 rels = docxRelationships(parts.rels);\n const listFormat = docxNumbering(parts.numbering);\n const styleNum = docxStyleNumbering(parts.styles);\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 picture = (r: Element): string => {\n const all = Array.from(r.getElementsByTagName(\"*\"));\n const blip = all.find((e) => e.localName === \"blip\");\n const id = blip?.getAttributeNS(R, \"embed\") || blip?.getAttribute(\"r:embed\") || blip?.getAttributeNS(R, \"link\") || blip?.getAttribute(\"r:link\");\n const target = id ? rels.get(id) : undefined;\n if (!target) return \"\";\n const named = all.find((e) => e.localName === \"docPr\");\n const alt = (named?.getAttribute(\"descr\") || named?.getAttribute(\"title\") || \"\").replace(/[[\\]]/g, \"\");\n return `![${alt}](${target.replace(/ /g, \"%20\")})`;\n };\n const run = (r: Element): string => {\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 const pic = Array.from(r.childNodes).some((n) => (n as Element).localName === \"drawing\" || (n as Element).localName === \"pict\") ? picture(r) : \"\";\n if (!t) return pic;\n const rPr = first(r, \"rPr\");\n const bold = on(rPr, \"b\");\n const italic = on(rPr, \"i\");\n const strike = on(rPr, \"strike\");\n const lead = t.match(/^\\s*/)?.[0] ?? \"\";\n const trail = t.match(/\\s*$/)?.[0] ?? \"\";\n const core = t.trim();\n if (!core) return t + pic;\n let marked = core;\n if (bold) marked = `**${marked}**`;\n if (italic) marked = `*${marked}*`;\n if (strike) marked = `~~${marked}~~`;\n return `${lead}${marked}${trail}${pic}`;\n };\n const inline = (parent: Element): string => {\n let text = \"\";\n for (const node of Array.from(parent.childNodes)) {\n const el = node as Element;\n if (el.nodeType !== 1) continue;\n if (el.localName === \"r\") text += run(el);\n else if (el.localName === \"hyperlink\") {\n const words = inline(el);\n const id = el.getAttributeNS(R, \"id\") || el.getAttribute(\"r:id\");\n const href = id ? rels.get(id) : undefined;\n const anchor = el.getAttributeNS(W, \"anchor\") || el.getAttribute(\"w:anchor\");\n text += words.trim() && (href || anchor) ? `[${words.trim()}](${href ?? `#${anchor}`})` : words;\n } else if (el.localName === \"ins\" || el.localName === \"smartTag\" || el.localName === \"sdt\" || el.localName === \"sdtContent\" || el.localName === \"fldSimple\") text += inline(el);\n }\n return text;\n };\n const paragraph = (p: Element): string => {\n const text = inline(p);\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 const numPr = pPr ? first(pPr, \"numPr\") : undefined;\n // The paragraph's own numPr wins field by field; what it leaves out comes from its style chain.\n const fromStyle = styleNum(style);\n const numId = attr(numPr ? first(numPr, \"numId\") : undefined, \"val\") ?? fromStyle.numId;\n const ilvl = attr(numPr ? first(numPr, \"ilvl\") : undefined, \"val\") ?? fromStyle.ilvl;\n if ((numPr || numId) && numId !== \"0\" && text.trim()) {\n const level = Number(ilvl ?? 0);\n const fmt = listFormat(numId ?? \"\", level);\n const marker = fmt && fmt !== \"bullet\" && fmt !== \"none\" ? \"1.\" : \"-\";\n return `${\" \".repeat(level)}${marker} ${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\") {\n // A figure caption under a picture (\"Figure 1: Team photo\") repeats the picture's description.\n const pPr = first(child, \"pPr\");\n const style = attr(pPr ? first(pPr, \"pStyle\") : undefined, \"val\") ?? \"\";\n if (/^caption$/i.test(style) && /^!\\[[^\\]]*\\]\\([^)]*\\)$/.test(out[out.length - 1] ?? \"\")) continue;\n 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 // A header row drawn bold is a header row, not bold words.\n const head = rows[0];\n if (head && head.every((c) => !c || /^\\*\\*[^*]+\\*\\*$/.test(c))) rows[0] = head.map((c) => c.replace(/^\\*\\*|\\*\\*$/g, \"\"));\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 isList = (l: string | undefined) => /^\\s*(-|\\d+\\.) /.test(l ?? \"\");\n if (i > 0 && !(isList(out[i - 1]) && isList(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 const rels = (await zip.get(\"word/_rels/document.xml.rels\")?.text()) ?? null;\n const numbering = (await zip.get(\"word/numbering.xml\")?.text()) ?? null;\n const styles = (await zip.get(\"word/styles.xml\")?.text()) ?? null;\n // A picture stored inside the file has no address a Docs document can draw, so it stays out here (as\n // before); a Spaces import reads `docxXmlToMarkdown` itself and stores each picture as a file.\n const markdown = docxXmlToMarkdown(xml, { rels, numbering, styles }).replace(/!\\[[^\\]]*\\]\\(word\\/[^)]*\\)/g, \"\");\n return { markdown, 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 { downloadFile } from \"@ai-matrx/kit/download\";\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) => downloadFile(fileName, bytes as BlobPart, mime),\n });\n}\n"]}
@@ -122,6 +122,8 @@ interface DocxParts {
122
122
  rels?: string | null;
123
123
  /** `word/numbering.xml` — which lists are numbered. */
124
124
  numbering?: string | null;
125
+ /** `word/styles.xml` — list numbering a paragraph STYLE carries ("List Bullet", "List Number"). */
126
+ styles?: string | null;
125
127
  }
126
128
  /** Relationship id -> target. An internal target is written as its path inside the archive (`word/media/image1.png`). */
127
129
  declare function docxRelationships(rels: string | null | undefined): Map<string, string>;
package/dist/docs-io.d.ts CHANGED
@@ -122,6 +122,8 @@ interface DocxParts {
122
122
  rels?: string | null;
123
123
  /** `word/numbering.xml` — which lists are numbered. */
124
124
  numbering?: string | null;
125
+ /** `word/styles.xml` — list numbering a paragraph STYLE carries ("List Bullet", "List Number"). */
126
+ styles?: string | null;
125
127
  }
126
128
  /** Relationship id -> target. An internal target is written as its path inside the archive (`word/media/image1.png`). */
127
129
  declare function docxRelationships(rels: string | null | undefined): Map<string, string>;
package/dist/docs-io.js CHANGED
@@ -310,12 +310,38 @@ function docxNumbering(xml) {
310
310
  }
311
311
  return (numId, level) => abstract.get(nums.get(numId) ?? "")?.get(level) ?? null;
312
312
  }
313
+ function docxStyleNumbering(xml) {
314
+ const none = { numId: null, ilvl: null };
315
+ if (!xml) return () => none;
316
+ const doc = new DOMParser().parseFromString(xml, "application/xml");
317
+ const val = (el, name) => el?.getAttributeNS(W, name) ?? el?.getAttribute(`w:${name}`) ?? null;
318
+ const styles = /* @__PURE__ */ new Map();
319
+ for (const st of Array.from(doc.getElementsByTagNameNS(W, "style"))) styles.set(val(st, "styleId") ?? "", st);
320
+ return (styleId) => {
321
+ let numId = null;
322
+ let ilvl = null;
323
+ const seen = /* @__PURE__ */ new Set();
324
+ for (let id = styleId; id && !seen.has(id); ) {
325
+ seen.add(id);
326
+ const st = styles.get(id);
327
+ if (!st) break;
328
+ const numPr = st.getElementsByTagNameNS(W, "numPr")[0];
329
+ if (numPr) {
330
+ numId ??= val(numPr.getElementsByTagNameNS(W, "numId")[0], "val");
331
+ ilvl ??= val(numPr.getElementsByTagNameNS(W, "ilvl")[0], "val");
332
+ }
333
+ id = val(st.getElementsByTagNameNS(W, "basedOn")[0], "val");
334
+ }
335
+ return { numId, ilvl };
336
+ };
337
+ }
313
338
  function docxXmlToMarkdown(xml, parts = {}) {
314
339
  const doc = new DOMParser().parseFromString(xml, "application/xml");
315
340
  const body = doc.getElementsByTagNameNS(W, "body")[0];
316
341
  if (!body) throw new Error("This Word file has no document body.");
317
342
  const rels = docxRelationships(parts.rels);
318
343
  const listFormat = docxNumbering(parts.numbering);
344
+ const styleNum = docxStyleNumbering(parts.styles);
319
345
  const attr = (el, name) => el?.getAttributeNS(W, name) ?? el?.getAttribute(`w:${name}`) ?? null;
320
346
  const first = (el, name) => el.getElementsByTagNameNS(W, name)[0];
321
347
  const on = (props, name) => {
@@ -379,9 +405,12 @@ function docxXmlToMarkdown(xml, parts = {}) {
379
405
  const heading = style.match(/^(?:Heading|heading)\s?(\d)$/)?.[1] ?? (style === "Title" ? "1" : null);
380
406
  if (heading && text.trim()) return `${"#".repeat(Math.min(6, Number(heading)))} ${text.replace(/\*+/g, "").trim()}`;
381
407
  const numPr = pPr ? first(pPr, "numPr") : void 0;
382
- if (numPr && text.trim()) {
383
- const level = Number(attr(first(numPr, "ilvl"), "val") ?? 0);
384
- const fmt = listFormat(attr(first(numPr, "numId"), "val") ?? "", level);
408
+ const fromStyle = styleNum(style);
409
+ const numId = attr(numPr ? first(numPr, "numId") : void 0, "val") ?? fromStyle.numId;
410
+ const ilvl = attr(numPr ? first(numPr, "ilvl") : void 0, "val") ?? fromStyle.ilvl;
411
+ if ((numPr || numId) && numId !== "0" && text.trim()) {
412
+ const level = Number(ilvl ?? 0);
413
+ const fmt = listFormat(numId ?? "", level);
385
414
  const marker = fmt && fmt !== "bullet" && fmt !== "none" ? "1." : "-";
386
415
  return `${" ".repeat(level)}${marker} ${text.trim()}`;
387
416
  }
@@ -496,7 +525,8 @@ async function documentFileToMarkdown(file) {
496
525
  const xml = await entry.text();
497
526
  const rels = await zip.get("word/_rels/document.xml.rels")?.text() ?? null;
498
527
  const numbering = await zip.get("word/numbering.xml")?.text() ?? null;
499
- const markdown = docxXmlToMarkdown(xml, { rels, numbering }).replace(/!\[[^\]]*\]\(word\/[^)]*\)/g, "");
528
+ const styles = await zip.get("word/styles.xml")?.text() ?? null;
529
+ const markdown = docxXmlToMarkdown(xml, { rels, numbering, styles }).replace(/!\[[^\]]*\]\(word\/[^)]*\)/g, "");
500
530
  return { markdown, source: "imported_docx" };
501
531
  }
502
532
  const text = await file.text();
@@ -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\nimport { isPipeLedRow } from \"@ai-matrx/content-ir/source\";\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 * Backslash-escape typed text that a markdown reader would take for HTML (`<b>`, `<A22-klm>`) or an entity\n * (`&amp;`), so every renderer and exporter (PDF, Word, EPUB, HTML, print, the shared view) shows the typed text.\n * Default true. Pass false only for a consumer that treats the text as literal characters, not markdown (the\n * Google push writes the string into a Google Doc as is; an agent reading the passage; a source view).\n */\n escapeMarkup?: boolean;\n}\n\n/**\n * One typed character as markdown that renders back as the same character. Markdown readers (the print engine behind\n * PDF / Word / EPUB / HTML among them) treat `<tag>` as raw HTML and drop it, and read `&amp;` as an entity, so typed\n * text that looks like either is backslash-escaped (`\\<`, `\\&`); a plain `<` or `&` (`A < B`, `a&b`) is left alone.\n * A backslash right before one of those is doubled so it cannot swallow the escape.\n */\nfunction markdownSafeChar(stream: string, at: number): string {\n const c = stream[at]!;\n const next = stream[at + 1] ?? \"\";\n if (c === \"<\" && /[A-Za-z/!?]/.test(next)) return \"\\\\<\";\n if (c === \"&\" && /^#?\\w+;/.test(stream.slice(at + 1, at + 34))) return \"\\\\&\";\n if (c === \"\\\\\" && (next === \"<\" || next === \"&\")) return \"\\\\\\\\\";\n return c;\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 const escapeMarkup = options.escapeMarkup !== false;\n\n // Pictures: each CUSTOM_BLOCK token is read back as `![alt](source)`.\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 += escapeMarkup ? markdownSafeChar(dataStream, k) : 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 = isPipeLedRow;\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\";\nconst R = \"http://schemas.openxmlformats.org/officeDocument/2006/relationships\";\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/**\n * The parts of a .docx beside `word/document.xml` that its body points into. Each is optional: without\n * `rels` a link keeps only its words and a picture is skipped; without `numbering` every list is bulleted.\n */\nexport interface DocxParts {\n /** `word/_rels/document.xml.rels` — link addresses and picture files by relationship id. */\n rels?: string | null;\n /** `word/numbering.xml` — which lists are numbered. */\n numbering?: string | null;\n}\n\n/** Relationship id -> target. An internal target is written as its path inside the archive (`word/media/image1.png`). */\nexport function docxRelationships(rels: string | null | undefined): Map<string, string> {\n const out = new Map<string, string>();\n if (!rels) return out;\n const doc = new DOMParser().parseFromString(rels, \"application/xml\");\n for (const el of Array.from(doc.getElementsByTagName(\"*\"))) {\n if (el.localName !== \"Relationship\") continue;\n const id = el.getAttribute(\"Id\");\n const target = el.getAttribute(\"Target\");\n if (!id || !target) continue;\n const external = el.getAttribute(\"TargetMode\") === \"External\" || /^[a-z][a-z0-9+.-]*:/i.test(target);\n out.set(id, external ? target : target.startsWith(\"/\") ? target.slice(1) : `word/${target}`.replace(/\\/\\.\\//g, \"/\"));\n }\n return out;\n}\n\n/** numId -> (level -> numFmt), from numbering.xml. */\nfunction docxNumbering(xml: string | null | undefined): (numId: string, level: number) => string | null {\n if (!xml) return () => null;\n const doc = new DOMParser().parseFromString(xml, \"application/xml\");\n const val = (el: Element | undefined, name: string) => el?.getAttributeNS(W, name) ?? el?.getAttribute(`w:${name}`) ?? null;\n const abstract = new Map<string, Map<number, string>>();\n for (const a of Array.from(doc.getElementsByTagNameNS(W, \"abstractNum\"))) {\n const levels = new Map<number, string>();\n for (const lvl of Array.from(a.getElementsByTagNameNS(W, \"lvl\"))) {\n const fmt = val(lvl.getElementsByTagNameNS(W, \"numFmt\")[0], \"val\");\n if (fmt) levels.set(Number(val(lvl, \"ilvl\") ?? 0), fmt);\n }\n abstract.set(val(a, \"abstractNumId\") ?? \"\", levels);\n }\n const nums = new Map<string, string>();\n for (const n of Array.from(doc.getElementsByTagNameNS(W, \"num\"))) {\n const ref = val(n.getElementsByTagNameNS(W, \"abstractNumId\")[0], \"val\");\n if (ref != null) nums.set(val(n, \"numId\") ?? \"\", ref);\n }\n return (numId, level) => abstract.get(nums.get(numId) ?? \"\")?.get(level) ?? null;\n}\n\n/**\n * Word's document.xml -> markdown: headings, bold/italic/strike runs, links (with `parts.rels`), pictures as\n * `![alt](word/media/…)` (with `parts.rels`; the caller resolves the path to a stored file), bulleted and\n * numbered list paragraphs (numbered with `parts.numbering`), tables as rows.\n */\nexport function docxXmlToMarkdown(xml: string, parts: DocxParts = {}): 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 rels = docxRelationships(parts.rels);\n const listFormat = docxNumbering(parts.numbering);\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 picture = (r: Element): string => {\n const all = Array.from(r.getElementsByTagName(\"*\"));\n const blip = all.find((e) => e.localName === \"blip\");\n const id = blip?.getAttributeNS(R, \"embed\") || blip?.getAttribute(\"r:embed\") || blip?.getAttributeNS(R, \"link\") || blip?.getAttribute(\"r:link\");\n const target = id ? rels.get(id) : undefined;\n if (!target) return \"\";\n const named = all.find((e) => e.localName === \"docPr\");\n const alt = (named?.getAttribute(\"descr\") || named?.getAttribute(\"title\") || \"\").replace(/[[\\]]/g, \"\");\n return `![${alt}](${target.replace(/ /g, \"%20\")})`;\n };\n const run = (r: Element): string => {\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 const pic = Array.from(r.childNodes).some((n) => (n as Element).localName === \"drawing\" || (n as Element).localName === \"pict\") ? picture(r) : \"\";\n if (!t) return pic;\n const rPr = first(r, \"rPr\");\n const bold = on(rPr, \"b\");\n const italic = on(rPr, \"i\");\n const strike = on(rPr, \"strike\");\n const lead = t.match(/^\\s*/)?.[0] ?? \"\";\n const trail = t.match(/\\s*$/)?.[0] ?? \"\";\n const core = t.trim();\n if (!core) return t + pic;\n let marked = core;\n if (bold) marked = `**${marked}**`;\n if (italic) marked = `*${marked}*`;\n if (strike) marked = `~~${marked}~~`;\n return `${lead}${marked}${trail}${pic}`;\n };\n const inline = (parent: Element): string => {\n let text = \"\";\n for (const node of Array.from(parent.childNodes)) {\n const el = node as Element;\n if (el.nodeType !== 1) continue;\n if (el.localName === \"r\") text += run(el);\n else if (el.localName === \"hyperlink\") {\n const words = inline(el);\n const id = el.getAttributeNS(R, \"id\") || el.getAttribute(\"r:id\");\n const href = id ? rels.get(id) : undefined;\n const anchor = el.getAttributeNS(W, \"anchor\") || el.getAttribute(\"w:anchor\");\n text += words.trim() && (href || anchor) ? `[${words.trim()}](${href ?? `#${anchor}`})` : words;\n } else if (el.localName === \"ins\" || el.localName === \"smartTag\" || el.localName === \"sdt\" || el.localName === \"sdtContent\" || el.localName === \"fldSimple\") text += inline(el);\n }\n return text;\n };\n const paragraph = (p: Element): string => {\n const text = inline(p);\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 const numPr = pPr ? first(pPr, \"numPr\") : undefined;\n if (numPr && text.trim()) {\n const level = Number(attr(first(numPr, \"ilvl\"), \"val\") ?? 0);\n const fmt = listFormat(attr(first(numPr, \"numId\"), \"val\") ?? \"\", level);\n const marker = fmt && fmt !== \"bullet\" && fmt !== \"none\" ? \"1.\" : \"-\";\n return `${\" \".repeat(level)}${marker} ${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\") {\n // A figure caption under a picture (\"Figure 1: Team photo\") repeats the picture's description.\n const pPr = first(child, \"pPr\");\n const style = attr(pPr ? first(pPr, \"pStyle\") : undefined, \"val\") ?? \"\";\n if (/^caption$/i.test(style) && /^!\\[[^\\]]*\\]\\([^)]*\\)$/.test(out[out.length - 1] ?? \"\")) continue;\n 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 // A header row drawn bold is a header row, not bold words.\n const head = rows[0];\n if (head && head.every((c) => !c || /^\\*\\*[^*]+\\*\\*$/.test(c))) rows[0] = head.map((c) => c.replace(/^\\*\\*|\\*\\*$/g, \"\"));\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 isList = (l: string | undefined) => /^\\s*(-|\\d+\\.) /.test(l ?? \"\");\n if (i > 0 && !(isList(out[i - 1]) && isList(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 const rels = (await zip.get(\"word/_rels/document.xml.rels\")?.text()) ?? null;\n const numbering = (await zip.get(\"word/numbering.xml\")?.text()) ?? null;\n // A picture stored inside the file has no address a Docs document can draw, so it stays out here (as\n // before); a Spaces import reads `docxXmlToMarkdown` itself and stores each picture as a file.\n const markdown = docxXmlToMarkdown(xml, { rels, numbering }).replace(/!\\[[^\\]]*\\]\\(word\\/[^)]*\\)/g, \"\");\n return { markdown, 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 { downloadFile } from \"@ai-matrx/kit/download\";\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) => downloadFile(fileName, bytes as BlobPart, mime),\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\nimport { isPipeLedRow } from \"@ai-matrx/content-ir/source\";\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 * Backslash-escape typed text that a markdown reader would take for HTML (`<b>`, `<A22-klm>`) or an entity\n * (`&amp;`), so every renderer and exporter (PDF, Word, EPUB, HTML, print, the shared view) shows the typed text.\n * Default true. Pass false only for a consumer that treats the text as literal characters, not markdown (the\n * Google push writes the string into a Google Doc as is; an agent reading the passage; a source view).\n */\n escapeMarkup?: boolean;\n}\n\n/**\n * One typed character as markdown that renders back as the same character. Markdown readers (the print engine behind\n * PDF / Word / EPUB / HTML among them) treat `<tag>` as raw HTML and drop it, and read `&amp;` as an entity, so typed\n * text that looks like either is backslash-escaped (`\\<`, `\\&`); a plain `<` or `&` (`A < B`, `a&b`) is left alone.\n * A backslash right before one of those is doubled so it cannot swallow the escape.\n */\nfunction markdownSafeChar(stream: string, at: number): string {\n const c = stream[at]!;\n const next = stream[at + 1] ?? \"\";\n if (c === \"<\" && /[A-Za-z/!?]/.test(next)) return \"\\\\<\";\n if (c === \"&\" && /^#?\\w+;/.test(stream.slice(at + 1, at + 34))) return \"\\\\&\";\n if (c === \"\\\\\" && (next === \"<\" || next === \"&\")) return \"\\\\\\\\\";\n return c;\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 const escapeMarkup = options.escapeMarkup !== false;\n\n // Pictures: each CUSTOM_BLOCK token is read back as `![alt](source)`.\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 += escapeMarkup ? markdownSafeChar(dataStream, k) : 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 = isPipeLedRow;\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\";\nconst R = \"http://schemas.openxmlformats.org/officeDocument/2006/relationships\";\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/**\n * The parts of a .docx beside `word/document.xml` that its body points into. Each is optional: without\n * `rels` a link keeps only its words and a picture is skipped; without `numbering` every list is bulleted.\n */\nexport interface DocxParts {\n /** `word/_rels/document.xml.rels` — link addresses and picture files by relationship id. */\n rels?: string | null;\n /** `word/numbering.xml` — which lists are numbered. */\n numbering?: string | null;\n /** `word/styles.xml` — list numbering a paragraph STYLE carries (\"List Bullet\", \"List Number\"). */\n styles?: string | null;\n}\n\n/** Relationship id -> target. An internal target is written as its path inside the archive (`word/media/image1.png`). */\nexport function docxRelationships(rels: string | null | undefined): Map<string, string> {\n const out = new Map<string, string>();\n if (!rels) return out;\n const doc = new DOMParser().parseFromString(rels, \"application/xml\");\n for (const el of Array.from(doc.getElementsByTagName(\"*\"))) {\n if (el.localName !== \"Relationship\") continue;\n const id = el.getAttribute(\"Id\");\n const target = el.getAttribute(\"Target\");\n if (!id || !target) continue;\n const external = el.getAttribute(\"TargetMode\") === \"External\" || /^[a-z][a-z0-9+.-]*:/i.test(target);\n out.set(id, external ? target : target.startsWith(\"/\") ? target.slice(1) : `word/${target}`.replace(/\\/\\.\\//g, \"/\"));\n }\n return out;\n}\n\n/** numId -> (level -> numFmt), from numbering.xml. */\nfunction docxNumbering(xml: string | null | undefined): (numId: string, level: number) => string | null {\n if (!xml) return () => null;\n const doc = new DOMParser().parseFromString(xml, \"application/xml\");\n const val = (el: Element | undefined, name: string) => el?.getAttributeNS(W, name) ?? el?.getAttribute(`w:${name}`) ?? null;\n const abstract = new Map<string, Map<number, string>>();\n for (const a of Array.from(doc.getElementsByTagNameNS(W, \"abstractNum\"))) {\n const levels = new Map<number, string>();\n for (const lvl of Array.from(a.getElementsByTagNameNS(W, \"lvl\"))) {\n const fmt = val(lvl.getElementsByTagNameNS(W, \"numFmt\")[0], \"val\");\n if (fmt) levels.set(Number(val(lvl, \"ilvl\") ?? 0), fmt);\n }\n abstract.set(val(a, \"abstractNumId\") ?? \"\", levels);\n }\n const nums = new Map<string, string>();\n for (const n of Array.from(doc.getElementsByTagNameNS(W, \"num\"))) {\n const ref = val(n.getElementsByTagNameNS(W, \"abstractNumId\")[0], \"val\");\n if (ref != null) nums.set(val(n, \"numId\") ?? \"\", ref);\n }\n return (numId, level) => abstract.get(nums.get(numId) ?? \"\")?.get(level) ?? null;\n}\n\n/** style id -> the numId/ilvl its w:numPr declares, following w:basedOn (nearest style wins per field). */\nfunction docxStyleNumbering(xml: string | null | undefined): (styleId: string) => { numId: string | null; ilvl: string | null } {\n const none = { numId: null, ilvl: null };\n if (!xml) return () => none;\n const doc = new DOMParser().parseFromString(xml, \"application/xml\");\n const val = (el: Element | undefined, name: string) => el?.getAttributeNS(W, name) ?? el?.getAttribute(`w:${name}`) ?? null;\n const styles = new Map<string, Element>();\n for (const st of Array.from(doc.getElementsByTagNameNS(W, \"style\"))) styles.set(val(st, \"styleId\") ?? \"\", st);\n return (styleId) => {\n let numId: string | null = null;\n let ilvl: string | null = null;\n const seen = new Set<string>();\n for (let id: string | null = styleId; id && !seen.has(id); ) {\n seen.add(id);\n const st = styles.get(id);\n if (!st) break;\n const numPr = st.getElementsByTagNameNS(W, \"numPr\")[0];\n if (numPr) {\n numId ??= val(numPr.getElementsByTagNameNS(W, \"numId\")[0], \"val\");\n ilvl ??= val(numPr.getElementsByTagNameNS(W, \"ilvl\")[0], \"val\");\n }\n id = val(st.getElementsByTagNameNS(W, \"basedOn\")[0], \"val\");\n }\n return { numId, ilvl };\n };\n}\n\n/**\n * Word's document.xml -> markdown: headings, bold/italic/strike runs, links (with `parts.rels`), pictures as\n * `![alt](word/media/…)` (with `parts.rels`; the caller resolves the path to a stored file), bulleted and\n * numbered list paragraphs (numbered with `parts.numbering`), tables as rows.\n */\nexport function docxXmlToMarkdown(xml: string, parts: DocxParts = {}): 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 rels = docxRelationships(parts.rels);\n const listFormat = docxNumbering(parts.numbering);\n const styleNum = docxStyleNumbering(parts.styles);\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 picture = (r: Element): string => {\n const all = Array.from(r.getElementsByTagName(\"*\"));\n const blip = all.find((e) => e.localName === \"blip\");\n const id = blip?.getAttributeNS(R, \"embed\") || blip?.getAttribute(\"r:embed\") || blip?.getAttributeNS(R, \"link\") || blip?.getAttribute(\"r:link\");\n const target = id ? rels.get(id) : undefined;\n if (!target) return \"\";\n const named = all.find((e) => e.localName === \"docPr\");\n const alt = (named?.getAttribute(\"descr\") || named?.getAttribute(\"title\") || \"\").replace(/[[\\]]/g, \"\");\n return `![${alt}](${target.replace(/ /g, \"%20\")})`;\n };\n const run = (r: Element): string => {\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 const pic = Array.from(r.childNodes).some((n) => (n as Element).localName === \"drawing\" || (n as Element).localName === \"pict\") ? picture(r) : \"\";\n if (!t) return pic;\n const rPr = first(r, \"rPr\");\n const bold = on(rPr, \"b\");\n const italic = on(rPr, \"i\");\n const strike = on(rPr, \"strike\");\n const lead = t.match(/^\\s*/)?.[0] ?? \"\";\n const trail = t.match(/\\s*$/)?.[0] ?? \"\";\n const core = t.trim();\n if (!core) return t + pic;\n let marked = core;\n if (bold) marked = `**${marked}**`;\n if (italic) marked = `*${marked}*`;\n if (strike) marked = `~~${marked}~~`;\n return `${lead}${marked}${trail}${pic}`;\n };\n const inline = (parent: Element): string => {\n let text = \"\";\n for (const node of Array.from(parent.childNodes)) {\n const el = node as Element;\n if (el.nodeType !== 1) continue;\n if (el.localName === \"r\") text += run(el);\n else if (el.localName === \"hyperlink\") {\n const words = inline(el);\n const id = el.getAttributeNS(R, \"id\") || el.getAttribute(\"r:id\");\n const href = id ? rels.get(id) : undefined;\n const anchor = el.getAttributeNS(W, \"anchor\") || el.getAttribute(\"w:anchor\");\n text += words.trim() && (href || anchor) ? `[${words.trim()}](${href ?? `#${anchor}`})` : words;\n } else if (el.localName === \"ins\" || el.localName === \"smartTag\" || el.localName === \"sdt\" || el.localName === \"sdtContent\" || el.localName === \"fldSimple\") text += inline(el);\n }\n return text;\n };\n const paragraph = (p: Element): string => {\n const text = inline(p);\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 const numPr = pPr ? first(pPr, \"numPr\") : undefined;\n // The paragraph's own numPr wins field by field; what it leaves out comes from its style chain.\n const fromStyle = styleNum(style);\n const numId = attr(numPr ? first(numPr, \"numId\") : undefined, \"val\") ?? fromStyle.numId;\n const ilvl = attr(numPr ? first(numPr, \"ilvl\") : undefined, \"val\") ?? fromStyle.ilvl;\n if ((numPr || numId) && numId !== \"0\" && text.trim()) {\n const level = Number(ilvl ?? 0);\n const fmt = listFormat(numId ?? \"\", level);\n const marker = fmt && fmt !== \"bullet\" && fmt !== \"none\" ? \"1.\" : \"-\";\n return `${\" \".repeat(level)}${marker} ${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\") {\n // A figure caption under a picture (\"Figure 1: Team photo\") repeats the picture's description.\n const pPr = first(child, \"pPr\");\n const style = attr(pPr ? first(pPr, \"pStyle\") : undefined, \"val\") ?? \"\";\n if (/^caption$/i.test(style) && /^!\\[[^\\]]*\\]\\([^)]*\\)$/.test(out[out.length - 1] ?? \"\")) continue;\n 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 // A header row drawn bold is a header row, not bold words.\n const head = rows[0];\n if (head && head.every((c) => !c || /^\\*\\*[^*]+\\*\\*$/.test(c))) rows[0] = head.map((c) => c.replace(/^\\*\\*|\\*\\*$/g, \"\"));\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 isList = (l: string | undefined) => /^\\s*(-|\\d+\\.) /.test(l ?? \"\");\n if (i > 0 && !(isList(out[i - 1]) && isList(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 const rels = (await zip.get(\"word/_rels/document.xml.rels\")?.text()) ?? null;\n const numbering = (await zip.get(\"word/numbering.xml\")?.text()) ?? null;\n const styles = (await zip.get(\"word/styles.xml\")?.text()) ?? null;\n // A picture stored inside the file has no address a Docs document can draw, so it stays out here (as\n // before); a Spaces import reads `docxXmlToMarkdown` itself and stores each picture as a file.\n const markdown = docxXmlToMarkdown(xml, { rels, numbering, styles }).replace(/!\\[[^\\]]*\\]\\(word\\/[^)]*\\)/g, \"\");\n return { markdown, 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 { downloadFile } from \"@ai-matrx/kit/download\";\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) => downloadFile(fileName, bytes as BlobPart, mime),\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.65",
3
+ "version": "0.8.66",
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",
@@ -194,6 +194,16 @@
194
194
  }
195
195
  },
196
196
  "dependencies": {
197
+ "@ai-matrx/alchemy": "latest",
198
+ "@ai-matrx/api": "latest",
199
+ "@ai-matrx/auth": "latest",
200
+ "@ai-matrx/content-ir": "latest",
201
+ "@ai-matrx/data": "latest",
202
+ "@ai-matrx/design-system": "latest",
203
+ "@ai-matrx/kit": "latest",
204
+ "@ai-matrx/print": "latest",
205
+ "@ai-matrx/records": "latest",
206
+ "@ai-matrx/rich-content": "latest",
197
207
  "@univerjs/core": "1.2.0",
198
208
  "@univerjs/docs": "1.2.0",
199
209
  "@univerjs/engine-formula": "1.2.0",
@@ -203,23 +213,15 @@
203
213
  "@univerjs/ui": "1.2.0",
204
214
  "ot-json1": "^1.0.2",
205
215
  "y-protocols": "^1.0.7",
206
- "yjs": "^13.6.33",
207
- "@ai-matrx/alchemy": "latest",
208
- "@ai-matrx/api": "latest",
209
- "@ai-matrx/auth": "latest",
210
- "@ai-matrx/content-ir": "latest",
211
- "@ai-matrx/data": "latest",
212
- "@ai-matrx/design-system": "latest",
213
- "@ai-matrx/print": "latest",
214
- "@ai-matrx/kit": "latest",
215
- "@ai-matrx/records": "latest",
216
- "@ai-matrx/rich-content": "latest"
216
+ "yjs": "^13.6.33"
217
217
  },
218
218
  "peerDependencies": {
219
219
  "@supabase/supabase-js": ">=2.0.0",
220
220
  "react": ">=18.0.0"
221
221
  },
222
222
  "devDependencies": {
223
+ "@ai-matrx/media": "0.15.1",
224
+ "@ai-matrx/records-ui": "0.116.0",
223
225
  "@arethetypeswrong/cli": "^0.18.5",
224
226
  "@supabase/supabase-js": "^2.117.0",
225
227
  "@types/node": "^26.6.2",
@@ -242,9 +244,7 @@
242
244
  "react-dom": "^19.3.0",
243
245
  "tsup": "^8.5.1",
244
246
  "typescript": "^6.0.3",
245
- "vitest": "^5.0.1",
246
- "@ai-matrx/media": "0.15.1",
247
- "@ai-matrx/records-ui": "0.116.0"
247
+ "vitest": "^5.0.1"
248
248
  },
249
249
  "publishConfig": {
250
250
  "access": "public",
@@ -284,5 +284,5 @@
284
284
  "fuzz:sheets": "SHEET_FUZZ_SEEDS=${SHEET_FUZZ_SEEDS:-4000} CHAIN_FUZZ_SEEDS=${CHAIN_FUZZ_SEEDS:-4000} SHEET_FUZZ_HEAP_MB=${SHEET_FUZZ_HEAP_MB:-12288} NODE_OPTIONS=--max-old-space-size=16384 vitest run src/collab/sheets/transform/sheet-transform-fuzz.test.ts src/collab/sheets/transform/sheet-transform-chain-fuzz.test.ts",
285
285
  "verify:tarball": "node ./scripts/verify-tarball.mjs"
286
286
  },
287
- "gitHead": "ba2eaed14f8188f606751129c882c0fb12fa96bf"
287
+ "gitHead": "00cdf507e063aaedba00e776712cb3a339e4f95c"
288
288
  }