@ai-matrx/office 0.8.2 → 0.8.4

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,21 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.8.4
4
+
5
+ - Bold, italic, code and strike of markdown written into a document were dropped (live: `**notes**` pasted into a blank document stayed plain; the same for the agent body write and building blocks). Cause: Univer's text insert returns early when the target body has no `textRuns` array, and a blank document has none. `sanitizeUniverDocSnapshot` now gives every body an empty `textRuns`. Test: `a-document-keeps-the-styles-of-text-written-into-it.test.ts` (red on 0.8.3).
6
+
7
+ ### Consumer action
8
+
9
+ None (the editor already sanitizes every snapshot it opens).
10
+
11
+ ## 0.8.3
12
+
13
+ - A table in an uploaded Word file now imports as one markdown table block (its rows were split by blank lines, so no table was drawn), and an HTML `<table>` imports as a table too (its cells were loose paragraphs). Both go through THE one converter into a real Univer table. Test: `a-word-or-html-table-imports-as-a-real-table.test.ts` (red on 0.8.2).
14
+
15
+ ### Consumer action
16
+
17
+ None.
18
+
3
19
  ## 0.8.2
4
20
 
5
21
  - Markdown an agent (or a paste) puts into an open document is drawn as structure, not typed as text. Live bug: a "Monthly Budget Dashboard" showed its table as raw `| Category | ... |` lines because only "create from markdown" converted. New `markdownFragment(markdown)` (body + table sources for `doc.command.inner-paste`, from THE one writer `markdownToUniverDoc`) and `looksLikeMarkdown(text)` in `./docs/tools`; `insertableBody` is removed (it dropped tables), `DocEditOps.insertMarkdown` now pastes the fragment; `DocumentBodyPort` gains optional `insertMarkdown(at, markdown)` and `writeBodyText` uses it when the new span carries markdown structure. Test: `a-markdown-table-an-agent-writes-is-a-real-table.test.ts` (red on 0.8.1).
@@ -200,6 +200,7 @@ var NAMED_STYLE_STRING_TO_NUMBER = Object.fromEntries(
200
200
  );
201
201
  function sanitizeUniverDocSnapshot(snapshot, docId) {
202
202
  restorePageStyle(snapshot, docId);
203
+ ensureTextRuns(snapshot);
203
204
  const paragraphs = _optionalChain([snapshot, 'optionalAccess', _5 => _5.body, 'optionalAccess', _6 => _6.paragraphs]);
204
205
  if (!Array.isArray(paragraphs)) return snapshot;
205
206
  let fixed = 0;
@@ -249,6 +250,10 @@ function restorePageStyle(snapshot, docId) {
249
250
  `[data-tables] RECOVERY: document snapshot${docId ? ` ${docId}` : ""} had no documentStyle.pageSize \u2014 restored the default Letter page geometry. Without a page box Univer lays the body out unbounded: text does not wrap and the viewport has no scrollable extent, so the document appears frozen. Fix the snapshot writer (agent/markdown converters must stamp DEFAULT_DOCUMENT_PAGE_STYLE).`
250
251
  );
251
252
  }
253
+ function ensureTextRuns(snapshot) {
254
+ const body = _optionalChain([snapshot, 'optionalAccess', _13 => _13.body]);
255
+ if (body && typeof body === "object" && !Array.isArray(body.textRuns)) body.textRuns = [];
256
+ }
252
257
 
253
258
 
254
259
 
@@ -269,4 +274,4 @@ function restorePageStyle(snapshot, docId) {
269
274
 
270
275
 
271
276
  exports.configureDocsHost = configureDocsHost; exports.getDocsHost = getDocsHost; exports.createDocument = createDocument; exports.listAccessibleDocuments = listAccessibleDocuments; exports.getDocument = getDocument; exports.renameDocument = renameDocument; exports.moveDocument = moveDocument; exports.updateDocumentDescription = updateDocumentDescription; exports.restoreDocument = restoreDocument; exports.discardFailedDocument = discardFailedDocument; exports.deleteDocument = deleteDocument; exports.getLatestDocumentSnapshot = getLatestDocumentSnapshot; exports.saveDocumentSnapshot = saveDocumentSnapshot; exports.copyDocument = copyDocument; exports.listTrashedDocuments = listTrashedDocuments; exports.listDocumentSnapshots = listDocumentSnapshots; exports.sanitizeUniverDocSnapshot = sanitizeUniverDocSnapshot;
272
- //# sourceMappingURL=chunk-6Z3Y6DUI.cjs.map
277
+ //# sourceMappingURL=chunk-556EUPET.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/home/runner/work/aidream/aidream/apps/shared/office/dist/chunk-556EUPET.cjs","../src/docs/ports.ts","../src/docs/document-service.ts","../src/docs/sanitize-univer-doc-snapshot.ts"],"names":["error"],"mappings":"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","file":"/home/runner/work/aidream/aidream/apps/shared/office/dist/chunk-556EUPET.cjs","sourcesContent":[null,"/**\n * The host ports of the Docs model and io.\n *\n * The package owns the document service, the open-document model and the converters, but two things\n * belong to the app that mounts them: the database client (a package owns no client) and the server\n * request that converts a .docx to .odt / .rtf (the app owns the server URL, auth and error capture).\n * The host states them once at start-up with `configureDocsHost`. State sits on a `globalThis` slot\n * so a dual-bundled copy of the package still sees one host. An unconfigured port THROWS naming\n * itself (nothing fails silently).\n */\nimport type { SupabaseClient } from \"@supabase/supabase-js\";\n\n/** A Supabase client over any schema: the package cannot import the app's generated database types. */\nexport type DocsSupabase = SupabaseClient<any, any, any>;\n\nexport interface DocsHostPorts {\n /** The app's database client (signed in as the person). */\n getSupabase(): DocsSupabase;\n /** An authenticated request to the Matrx server returning the raw `Response` (the open-format converter). */\n requestRaw(path: string, init: { method: \"POST\"; body: FormData }): Promise<Response>;\n}\n\nconst SLOT = Symbol.for(\"ai-matrx.office.docs-host\");\ntype Slot = { ports?: DocsHostPorts };\nconst slot = (): Slot => {\n const g = globalThis as Record<symbol, Slot | undefined>;\n return (g[SLOT] ??= {});\n};\n\nexport function configureDocsHost(ports: DocsHostPorts): void {\n slot().ports = ports;\n}\n\nexport function getDocsHost(): DocsHostPorts {\n const ports = slot().ports;\n if (!ports) throw new Error(\"@ai-matrx/office/docs: configureDocsHost(ports) was not called before the document service ran.\");\n return ports;\n}\n","/**\n * Document service — typed wrappers for `udt_documents` + `udt_document_snapshots`.\n *\n * Cloud document editor backed by Univer's preset-docs-core. Same shape as\n * `workbook-service.ts` — metadata in `udt_documents`, content state in\n * append-only `udt_document_snapshots`. The editor hydrates from the LATEST\n * snapshot and writes a new snapshot per save (debounced on the client —\n * see DocumentEditor).\n *\n * What lives elsewhere:\n * - Sharing / permissions: features/sharing/ + `has_permission(...)` RLS\n * - The open document (one model per id per tab: load, one save, realtime,\n * collab): features/documents/document-model/\n * - Component (a view of that model): features/documents/components/DocumentEditor\n *\n * Mirrors `workbook-service.ts`. If you're changing the shape of one, change\n * the other at the same time — see `features/data-tables/FEATURE.md`.\n */\nimport { requireOrganizationContext } from \"@ai-matrx/api\";\nimport { getClaimsUser } from \"@ai-matrx/auth\";\nimport { readAllRows, tryWriteOne, writeOneRow } from \"@ai-matrx/data/db\";\nimport type { ServiceResult } from \"@ai-matrx/records/core\";\n\nimport type { DocumentRow, DocumentSnapshot, DocumentSnapshotOrigin } from \"../core\";\nimport { getDocsHost } from \"./ports\";\n\n/** The app's database client, read through the host port at call time. */\nconst db = () => getDocsHost().getSupabase();\n// ─── documents ───────────────────────────────────────────────────────────────\n\nexport type CreateDocumentArgs = {\n name: string;\n description?: string | null;\n /** Origin label for the document itself, mirrors `udt_documents.source`. */\n source?: \"created\" | \"imported_docx\" | \"imported_md\" | \"imported_txt\" | \"linked_gdoc\";\n organizationId: string;\n projectId?: string | null;\n taskId?: string | null;\n isPublic?: boolean;\n /**\n * cld_files.id of the source upload (DOCX / MD / TXT blob). Set on the\n * import flow so the lossless original is recoverable; FK is ON DELETE SET\n * NULL, so deleting the file just nulls the link — the document survives.\n */\n originalFileId?: string | null;\n};\n\nexport async function createDocument(\n args: CreateDocumentArgs,\n): Promise<ServiceResult<DocumentRow>> {\n let organizationId: string;\n try {\n organizationId = requireOrganizationContext(undefined, args.organizationId);\n } catch (error) {\n return {\n success: false,\n error:\n error instanceof Error\n ? error.message\n : \"Select an organization before creating a document.\",\n };\n }\n\n const { data: userData, error: userErr } = await getClaimsUser(db());\n if (userErr || !userData?.user) {\n return {\n success: false,\n error: userErr?.message ?? \"not authenticated\",\n };\n }\n\n const { data, error } = await db()\n .schema(\"workbench\")\n .from(\"udt_documents\")\n .insert({\n document_name: args.name,\n description: args.description ?? null,\n source: args.source ?? \"created\",\n organization_id: organizationId,\n project_id: args.projectId ?? null,\n task_id: args.taskId ?? null,\n original_file_id: args.originalFileId ?? null,\n // CANONICAL columns (workbench_udt_canonical_step1). `visibility` is the\n // source of truth; the legacy `is_public` boolean is derived from it by\n // the workbench._bridge_legacy_owner trigger, so writing both here would\n // be two authorities for one fact. A new document defaults to `internal`\n // — it is org work product, not an individual person's private thing\n // (db-rules §6) — unless the caller explicitly asked for public.\n created_by: userData.user.id,\n visibility: args.isPublic ? \"public\" : \"internal\",\n })\n .select(\"*\")\n .single();\n if (error) return { success: false, error: error.message };\n return { success: true, data: data as DocumentRow };\n}\n\nexport async function listAccessibleDocuments(): Promise<\n ServiceResult<DocumentRow[]>\n> {\n // RLS handles owner / published-to-the-web / shared access. The library and\n // the pickers treat this list as COMPLETE (\"you have no such document\"), so\n // it pages past PostgREST's silent 1000-row cap (D190).\n try {\n const rows = await readAllRows<DocumentRow>(\n ({ from, to }) =>\n db()\n .schema(\"workbench\")\n .from(\"udt_documents\")\n .select(\"*\", { count: \"exact\" })\n .is(\"deleted_at\", null)\n .order(\"updated_at\", { ascending: false })\n .order(\"id\", { ascending: true })\n .range(from, to)\n .returns<DocumentRow[]>(),\n { label: \"workbench.udt_documents accessible documents\" },\n );\n return { success: true, data: rows };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : String(error),\n };\n }\n}\n\nexport async function getDocument(\n documentId: string,\n): Promise<ServiceResult<DocumentRow>> {\n const { data, error } = await db()\n .schema(\"workbench\")\n .from(\"udt_documents\")\n .select(\"*\")\n .eq(\"id\", documentId)\n .is(\"deleted_at\", null)\n .maybeSingle();\n if (error) return { success: false, error: error.message };\n if (!data) {\n // A trashed or unreachable document is a state the person can act on,\n // never PostgREST's \"Cannot coerce the result to a single JSON object\".\n return {\n success: false,\n error:\n \"This document is in the trash or you no longer have access to it. Restore it from Trash, or ask its owner to share it again.\",\n };\n }\n return { success: true, data: data as DocumentRow };\n}\n\nexport async function renameDocument(\n documentId: string,\n name: string,\n): Promise<ServiceResult<DocumentRow>> {\n const { data, error } = await writeOneRow(\n db()\n .schema(\"workbench\")\n .from(\"udt_documents\")\n .update({ document_name: name, updated_at: new Date().toISOString() })\n .eq(\"id\", documentId)\n .select(\"*\"),\n { action: \"update\", noun: \"document\" },\n );\n if (error) return { success: false, error: error.message };\n return { success: true, data: data as DocumentRow };\n}\n\n/**\n * Move the document into a project (or out of one with `null`). Sibling of\n * `renameDocument`: the one named setter for `project_id`.\n */\nexport async function moveDocument(\n documentId: string,\n projectId: string | null,\n): Promise<ServiceResult<DocumentRow>> {\n const { data, error } = await writeOneRow(\n db()\n .schema(\"workbench\")\n .from(\"udt_documents\")\n .update({ project_id: projectId, updated_at: new Date().toISOString() })\n .eq(\"id\", documentId)\n .select(\"*\"),\n { action: \"update\", noun: \"document\" },\n );\n if (error) return { success: false, error: error.message };\n return { success: true, data: data as DocumentRow };\n}\n\n/**\n * Rewrite the document's description. Sibling of `renameDocument` — the two\n * human-authored fields on `udt_documents` each get a named setter so callers\n * never hand-roll a `.from(\"udt_documents\").update(...)`. Pass `null` (or an\n * empty string) to clear it.\n *\n * Added when the documents surface became agent-writable: the\n * `document_description` write target on `/documents/[id]` is its caller, the\n * same way `updateWorkbookDescription` serves `workbook_description`. Keeping\n * the pair symmetric is this file's standing contract with `workbook-service`\n * (see the module header).\n */\nexport async function updateDocumentDescription(\n documentId: string,\n description: string | null,\n): Promise<ServiceResult<DocumentRow>> {\n const { data, error } = await writeOneRow(\n db()\n .schema(\"workbench\")\n .from(\"udt_documents\")\n .update({\n description: description && description.length > 0 ? description : null,\n updated_at: new Date().toISOString(),\n })\n .eq(\"id\", documentId)\n .select(\"*\"),\n { action: \"update\", noun: \"document\" },\n );\n if (error) return { success: false, error: error.message };\n return { success: true, data: data as DocumentRow };\n}\n\n/**\n * Soft delete — the document is tombstoned, not destroyed, and its snapshots stay\n * with it. Every read path filters `deleted_at is null`. Pair with\n * restoreDocument for undo. (aidream migration 0458.)\n */\nexport async function restoreDocument(\n documentId: string,\n): Promise<ServiceResult<true>> {\n const { error } = await tryWriteOne(\n db()\n .schema(\"workbench\")\n .from(\"udt_documents\")\n .update({ deleted_at: null })\n .eq(\"id\", documentId)\n .select(\"id\"),\n { action: \"restore\", noun: \"document\" },\n );\n if (error) return { success: false, error: error.message };\n return { success: true, data: true };\n}\n\n/**\n * Archive (delete means archive: never a hard delete) — only for rolling back a document this very flow just created and\n * failed to populate. Never use for a user-initiated delete: that is\n * deleteDocument, which tombstones and stays recoverable.\n */\nexport async function discardFailedDocument(\n documentId: string,\n): Promise<ServiceResult<true>> {\n const { error } = await tryWriteOne(\n db()\n .schema(\"workbench\")\n .from(\"udt_documents\")\n .update({ deleted_at: new Date().toISOString() })\n .eq(\"id\", documentId)\n .select(\"id\"),\n { action: \"archive\", noun: \"document\" },\n );\n if (error) return { success: false, error: error.message };\n return { success: true, data: true };\n}\n\nexport async function deleteDocument(\n documentId: string,\n): Promise<ServiceResult<true>> {\n const { error } = await tryWriteOne(\n db()\n .schema(\"workbench\")\n .from(\"udt_documents\")\n .update({ deleted_at: new Date().toISOString() })\n .eq(\"id\", documentId)\n .select(\"id\"),\n { action: \"delete\", noun: \"document\" },\n );\n if (error) return { success: false, error: error.message };\n return { success: true, data: true };\n}\n\n// ─── snapshots (document content) ────────────────────────────────────────────\n\n/**\n * Latest-snapshot fetch — what an opened document hydrates from. Returns\n * `data: null` (success path) when the document has no snapshots yet (newly\n * created, never saved). Distinguish \"no snapshot\" from \"load error\" by\n * checking `result.data === null`.\n */\nexport async function getLatestDocumentSnapshot(\n documentId: string,\n): Promise<ServiceResult<DocumentSnapshot | null>> {\n const { data, error } = await db()\n .schema(\"workbench\")\n .from(\"udt_document_snapshots\")\n .select(\"*\")\n .eq(\"document_id\", documentId)\n .order(\"created_at\", { ascending: false })\n .limit(1)\n .maybeSingle();\n if (error) return { success: false, error: error.message };\n return { success: true, data: (data ?? null) as DocumentSnapshot | null };\n}\n\nexport type SaveDocumentSnapshotArgs = {\n documentId: string;\n snapshot: unknown; // opaque to us — Univer IDocumentData decides the shape\n label?: string | null;\n origin?: DocumentSnapshotOrigin;\n};\n\nexport async function saveDocumentSnapshot(\n args: SaveDocumentSnapshotArgs,\n): Promise<ServiceResult<DocumentSnapshot>> {\n const { data: userData } = await getClaimsUser(db());\n const { data, error } = await db()\n .schema(\"workbench\")\n .from(\"udt_document_snapshots\")\n .insert({\n document_id: args.documentId,\n snapshot: args.snapshot as never,\n label: args.label ?? null,\n origin: args.origin ?? \"autosave\",\n created_by: userData?.user?.id ?? null,\n })\n .select(\"*\")\n .single();\n if (error) return { success: false, error: error.message };\n\n // Touch the parent document's updated_at so list views can sort by recency\n // without scanning snapshots. Best-effort — failure here is harmless.\n // write-lands-exempt: best-effort recency stamp on the parent document; the snapshot insert above is the real write\n await db()\n .schema(\"workbench\")\n .from(\"udt_documents\")\n .update({ updated_at: new Date().toISOString() })\n .eq(\"id\", args.documentId);\n\n return { success: true, data: data as DocumentSnapshot };\n}\n\n/**\n * An independent copy of a document: its own row, with the latest snapshot copied over, so editing the copy never\n * touches the original. The copy lives in the original's organization and project. Used by a board-template use.\n */\nexport async function copyDocument(\n documentId: string,\n name?: string,\n): Promise<ServiceResult<DocumentRow>> {\n const original = await getDocument(documentId);\n if (!original.success) return original;\n const row = original.data;\n if (!row.organization_id) {\n return { success: false, error: \"This document has no organization and cannot be copied.\" };\n }\n const created = await createDocument({\n name: name ?? row.document_name,\n description: row.description ?? null,\n organizationId: row.organization_id,\n projectId: row.project_id ?? null,\n isPublic: row.visibility === \"public\",\n });\n if (!created.success) return created;\n const latest = await getLatestDocumentSnapshot(documentId);\n if (!latest.success) return latest;\n if (latest.data) {\n const saved = await saveDocumentSnapshot({\n documentId: created.data.id,\n snapshot: latest.data.snapshot,\n label: latest.data.label,\n origin: \"autosave\",\n });\n if (!saved.success) return saved;\n }\n return created;\n}\n\n/** Trash view: the caller's own archived documents (RLS decides what is readable). */\nexport async function listTrashedDocuments(): Promise<\n ServiceResult<DocumentRow[]>\n> {\n const { data, error } = await db()\n .schema(\"workbench\")\n .from(\"udt_documents\")\n .select(\"*\")\n .not(\"deleted_at\", \"is\", null)\n .order(\"deleted_at\", { ascending: false });\n if (error) return { success: false, error: error.message };\n return { success: true, data: (data ?? []) as DocumentRow[] };\n}\n\nexport async function listDocumentSnapshots(\n documentId: string,\n limit = 50,\n): Promise<ServiceResult<DocumentSnapshot[]>> {\n const { data, error } = await db()\n .schema(\"workbench\")\n .from(\"udt_document_snapshots\")\n // component-created-by-ok: append-only snapshot with NO updated_by column and NO parent-rewrite trigger — created_by is the author the client wrote at insert (the only person field this table has)\n .select(\"id, document_id, label, origin, created_by, created_at\")\n .eq(\"document_id\", documentId)\n .order(\"created_at\", { ascending: false })\n .limit(limit);\n if (error) return { success: false, error: error.message };\n return { success: true, data: (data ?? []) as DocumentSnapshot[] };\n}\n","/**\n * sanitizeUniverDocSnapshot — defensive normalizer for Univer document\n * snapshots loaded from storage.\n *\n * WHY THIS EXISTS (loud recovery layer):\n * Univer's `NamedStyleType` is a NUMERIC enum (`HEADING_1 = 4`, `HEADING_2 = 5`,\n * …). Some snapshots in storage were produced by an external markdown→Univer\n * converter that wrote the *string* name (`\"HEADING_1\"`) into\n * `paragraphStyle.namedStyleType` instead of the numeric value. At render time\n * Univer does `HEADING_ICON_MAP[namedStyleType]` → `undefined` →\n * `icon.key` throws `Cannot read properties of undefined (reading 'key')` inside\n * the `<ParagraphMenu>` component the moment the cursor lands in a heading. That\n * uncaught error tears down Univer's React root, so the document loads and then\n * vanishes. See `features/data-tables/FEATURE.md`.\n *\n * This normalizer converts those string enum values back to numbers BEFORE the\n * snapshot reaches `createUniverDoc`. It is a recovery layer: when it fires it\n * means malformed data slipped past the writer, so it SCREAMS (console.warn)\n * with a count and the document id. The proper long-term fix is to make the\n * writer emit numeric enum values; until then this keeps the editor alive.\n *\n * Returns the SAME object (mutated in place) for ergonomic call sites, plus a\n * `fixed` count via the second return slot is intentionally avoided — callers\n * only need the sanitized snapshot; the warning is the signal.\n */\nimport { NamedStyleType } from \"@univerjs/core\";\nimport type { IDocumentData } from \"@univerjs/core\";\n\nimport {\n defaultDocumentPageStyle,\n isLegacySqueezedPageStyle,\n} from \"../core\";\n\n/**\n * Map of every string name that could appear in a legacy snapshot to its\n * canonical numeric `NamedStyleType`. Built from the enum itself so it stays in\n * lockstep with Univer (numeric enums expose name→value on string keys).\n */\nconst NAMED_STYLE_STRING_TO_NUMBER: Record<string, number> = Object.fromEntries(\n Object.entries(NamedStyleType)\n .filter(([key, val]) => typeof val === \"number\" && isNaN(Number(key)))\n .map(([key, val]) => [key, val as number]),\n);\n\n/**\n * Normalize a Univer document snapshot in place. Currently fixes string-valued\n * `paragraphStyle.namedStyleType` (the confirmed `<ParagraphMenu>` crash). New\n * classes of string-vs-enum corruption should be added here, not patched at the\n * call site.\n *\n * @param snapshot The snapshot about to be passed to `createUniverDoc`.\n * @param docId Document id, for the loud warning only.\n * @returns the same snapshot reference (mutated).\n */\nexport function sanitizeUniverDocSnapshot(\n snapshot: Partial<IDocumentData>,\n docId?: string,\n): Partial<IDocumentData> {\n restorePageStyle(snapshot, docId);\n ensureTextRuns(snapshot);\n\n const paragraphs = snapshot?.body?.paragraphs;\n if (!Array.isArray(paragraphs)) return snapshot;\n\n let fixed = 0;\n for (const paragraph of paragraphs) {\n const style = paragraph?.paragraphStyle as\n { namedStyleType?: unknown } | undefined;\n const value = style?.namedStyleType;\n if (typeof value === \"string\") {\n const numeric = NAMED_STYLE_STRING_TO_NUMBER[value];\n if (numeric !== undefined) {\n (style as { namedStyleType?: number }).namedStyleType = numeric;\n fixed += 1;\n } else {\n // Unknown string we can't map — drop it so Univer falls back to\n // NORMAL_TEXT rather than crashing on the lookup.\n delete (style as { namedStyleType?: unknown }).namedStyleType;\n fixed += 1;\n }\n }\n }\n\n if (fixed > 0) {\n console.warn(\n `[data-tables] RECOVERY: sanitized ${fixed} string namedStyleType value(s) ` +\n `to numeric NamedStyleType in document snapshot${docId ? ` ${docId}` : \"\"}. ` +\n `The snapshot was written with the wrong enum encoding (string instead of ` +\n `number) and would have crashed Univer's <ParagraphMenu>. Fix the snapshot ` +\n `writer to emit numeric NamedStyleType values.`,\n );\n }\n\n return snapshot;\n}\n\n/**\n * Second recovery: a snapshot with no `documentStyle.pageSize` has no page box,\n * so Univer lays the body out unbounded — text never wraps and the docs\n * viewport reports no scrollable extent, which reads to the user as \"the\n * document won't scroll\". Snapshots written by agents (`origin='agent'`) and by\n * older converters reach storage with `documentStyle: {}`. Stamp the canonical\n * page geometry back on and SCREAM: the writer is the real bug.\n */\nfunction restorePageStyle(snapshot: Partial<IDocumentData>, docId?: string) {\n if (!snapshot || typeof snapshot !== \"object\") return;\n\n // THE SQUEEZED PAGE (2026-10-15): documents carried A4-at-72dpi geometry in Univer's pageless\n // flavour — a 415px text column with no paper. Move them to a real Letter page, unchanged text.\n // A document with no flavour at all is a paginated page too (Univer would otherwise pick pageless).\n const style = snapshot.documentStyle;\n if (style?.pageSize?.width && isLegacySqueezedPageStyle(style)) {\n const page = defaultDocumentPageStyle();\n snapshot.documentStyle = { ...style, ...page, defaultParagraphStyle: style.defaultParagraphStyle ?? page.defaultParagraphStyle } as typeof style;\n return;\n }\n if (style?.pageSize?.width) {\n if (!style.documentFlavor) {\n const page = defaultDocumentPageStyle();\n Object.assign(style, { documentFlavor: page.documentFlavor });\n if (page.defaultParagraphStyle) style.defaultParagraphStyle ??= page.defaultParagraphStyle;\n }\n return;\n }\n\n snapshot.documentStyle = {\n ...defaultDocumentPageStyle(),\n ...(snapshot.documentStyle ?? {}),\n pageSize: defaultDocumentPageStyle().pageSize,\n } as NonNullable<typeof snapshot.documentStyle>;\n\n console.warn(\n `[data-tables] RECOVERY: document snapshot${docId ? ` ${docId}` : \"\"} had no ` +\n `documentStyle.pageSize — restored the default Letter page geometry. Without a ` +\n `page box Univer lays the body out unbounded: text does not wrap and the ` +\n `viewport has no scrollable extent, so the document appears frozen. Fix the ` +\n `snapshot writer (agent/markdown converters must stamp ` +\n `DEFAULT_DOCUMENT_PAGE_STYLE).`,\n );\n}\n\n/**\n * A body with no `textRuns` array is a body Univer silently refuses styled text for: its text\n * insert (`insertTextRuns`) returns early when the target has none, so every bold, italic, code\n * and size run of pasted or agent-written markdown vanished (found 2026-10-16: `**notes**` pasted\n * into a blank document stayed plain). An empty array is the same document and accepts the runs.\n */\nfunction ensureTextRuns(snapshot: Partial<IDocumentData>) {\n const body = snapshot?.body;\n if (body && typeof body === \"object\" && !Array.isArray(body.textRuns)) body.textRuns = [];\n}\n"]}
@@ -200,6 +200,7 @@ var NAMED_STYLE_STRING_TO_NUMBER = Object.fromEntries(
200
200
  );
201
201
  function sanitizeUniverDocSnapshot(snapshot, docId) {
202
202
  restorePageStyle(snapshot, docId);
203
+ ensureTextRuns(snapshot);
203
204
  const paragraphs = snapshot?.body?.paragraphs;
204
205
  if (!Array.isArray(paragraphs)) return snapshot;
205
206
  let fixed = 0;
@@ -249,6 +250,10 @@ function restorePageStyle(snapshot, docId) {
249
250
  `[data-tables] RECOVERY: document snapshot${docId ? ` ${docId}` : ""} had no documentStyle.pageSize \u2014 restored the default Letter page geometry. Without a page box Univer lays the body out unbounded: text does not wrap and the viewport has no scrollable extent, so the document appears frozen. Fix the snapshot writer (agent/markdown converters must stamp DEFAULT_DOCUMENT_PAGE_STYLE).`
250
251
  );
251
252
  }
253
+ function ensureTextRuns(snapshot) {
254
+ const body = snapshot?.body;
255
+ if (body && typeof body === "object" && !Array.isArray(body.textRuns)) body.textRuns = [];
256
+ }
252
257
 
253
258
  export {
254
259
  configureDocsHost,
@@ -269,4 +274,4 @@ export {
269
274
  listDocumentSnapshots,
270
275
  sanitizeUniverDocSnapshot
271
276
  };
272
- //# sourceMappingURL=chunk-XMM62UAT.js.map
277
+ //# sourceMappingURL=chunk-5JAZ32SX.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/docs/ports.ts","../src/docs/document-service.ts","../src/docs/sanitize-univer-doc-snapshot.ts"],"sourcesContent":["/**\n * The host ports of the Docs model and io.\n *\n * The package owns the document service, the open-document model and the converters, but two things\n * belong to the app that mounts them: the database client (a package owns no client) and the server\n * request that converts a .docx to .odt / .rtf (the app owns the server URL, auth and error capture).\n * The host states them once at start-up with `configureDocsHost`. State sits on a `globalThis` slot\n * so a dual-bundled copy of the package still sees one host. An unconfigured port THROWS naming\n * itself (nothing fails silently).\n */\nimport type { SupabaseClient } from \"@supabase/supabase-js\";\n\n/** A Supabase client over any schema: the package cannot import the app's generated database types. */\nexport type DocsSupabase = SupabaseClient<any, any, any>;\n\nexport interface DocsHostPorts {\n /** The app's database client (signed in as the person). */\n getSupabase(): DocsSupabase;\n /** An authenticated request to the Matrx server returning the raw `Response` (the open-format converter). */\n requestRaw(path: string, init: { method: \"POST\"; body: FormData }): Promise<Response>;\n}\n\nconst SLOT = Symbol.for(\"ai-matrx.office.docs-host\");\ntype Slot = { ports?: DocsHostPorts };\nconst slot = (): Slot => {\n const g = globalThis as Record<symbol, Slot | undefined>;\n return (g[SLOT] ??= {});\n};\n\nexport function configureDocsHost(ports: DocsHostPorts): void {\n slot().ports = ports;\n}\n\nexport function getDocsHost(): DocsHostPorts {\n const ports = slot().ports;\n if (!ports) throw new Error(\"@ai-matrx/office/docs: configureDocsHost(ports) was not called before the document service ran.\");\n return ports;\n}\n","/**\n * Document service — typed wrappers for `udt_documents` + `udt_document_snapshots`.\n *\n * Cloud document editor backed by Univer's preset-docs-core. Same shape as\n * `workbook-service.ts` — metadata in `udt_documents`, content state in\n * append-only `udt_document_snapshots`. The editor hydrates from the LATEST\n * snapshot and writes a new snapshot per save (debounced on the client —\n * see DocumentEditor).\n *\n * What lives elsewhere:\n * - Sharing / permissions: features/sharing/ + `has_permission(...)` RLS\n * - The open document (one model per id per tab: load, one save, realtime,\n * collab): features/documents/document-model/\n * - Component (a view of that model): features/documents/components/DocumentEditor\n *\n * Mirrors `workbook-service.ts`. If you're changing the shape of one, change\n * the other at the same time — see `features/data-tables/FEATURE.md`.\n */\nimport { requireOrganizationContext } from \"@ai-matrx/api\";\nimport { getClaimsUser } from \"@ai-matrx/auth\";\nimport { readAllRows, tryWriteOne, writeOneRow } from \"@ai-matrx/data/db\";\nimport type { ServiceResult } from \"@ai-matrx/records/core\";\n\nimport type { DocumentRow, DocumentSnapshot, DocumentSnapshotOrigin } from \"../core\";\nimport { getDocsHost } from \"./ports\";\n\n/** The app's database client, read through the host port at call time. */\nconst db = () => getDocsHost().getSupabase();\n// ─── documents ───────────────────────────────────────────────────────────────\n\nexport type CreateDocumentArgs = {\n name: string;\n description?: string | null;\n /** Origin label for the document itself, mirrors `udt_documents.source`. */\n source?: \"created\" | \"imported_docx\" | \"imported_md\" | \"imported_txt\" | \"linked_gdoc\";\n organizationId: string;\n projectId?: string | null;\n taskId?: string | null;\n isPublic?: boolean;\n /**\n * cld_files.id of the source upload (DOCX / MD / TXT blob). Set on the\n * import flow so the lossless original is recoverable; FK is ON DELETE SET\n * NULL, so deleting the file just nulls the link — the document survives.\n */\n originalFileId?: string | null;\n};\n\nexport async function createDocument(\n args: CreateDocumentArgs,\n): Promise<ServiceResult<DocumentRow>> {\n let organizationId: string;\n try {\n organizationId = requireOrganizationContext(undefined, args.organizationId);\n } catch (error) {\n return {\n success: false,\n error:\n error instanceof Error\n ? error.message\n : \"Select an organization before creating a document.\",\n };\n }\n\n const { data: userData, error: userErr } = await getClaimsUser(db());\n if (userErr || !userData?.user) {\n return {\n success: false,\n error: userErr?.message ?? \"not authenticated\",\n };\n }\n\n const { data, error } = await db()\n .schema(\"workbench\")\n .from(\"udt_documents\")\n .insert({\n document_name: args.name,\n description: args.description ?? null,\n source: args.source ?? \"created\",\n organization_id: organizationId,\n project_id: args.projectId ?? null,\n task_id: args.taskId ?? null,\n original_file_id: args.originalFileId ?? null,\n // CANONICAL columns (workbench_udt_canonical_step1). `visibility` is the\n // source of truth; the legacy `is_public` boolean is derived from it by\n // the workbench._bridge_legacy_owner trigger, so writing both here would\n // be two authorities for one fact. A new document defaults to `internal`\n // — it is org work product, not an individual person's private thing\n // (db-rules §6) — unless the caller explicitly asked for public.\n created_by: userData.user.id,\n visibility: args.isPublic ? \"public\" : \"internal\",\n })\n .select(\"*\")\n .single();\n if (error) return { success: false, error: error.message };\n return { success: true, data: data as DocumentRow };\n}\n\nexport async function listAccessibleDocuments(): Promise<\n ServiceResult<DocumentRow[]>\n> {\n // RLS handles owner / published-to-the-web / shared access. The library and\n // the pickers treat this list as COMPLETE (\"you have no such document\"), so\n // it pages past PostgREST's silent 1000-row cap (D190).\n try {\n const rows = await readAllRows<DocumentRow>(\n ({ from, to }) =>\n db()\n .schema(\"workbench\")\n .from(\"udt_documents\")\n .select(\"*\", { count: \"exact\" })\n .is(\"deleted_at\", null)\n .order(\"updated_at\", { ascending: false })\n .order(\"id\", { ascending: true })\n .range(from, to)\n .returns<DocumentRow[]>(),\n { label: \"workbench.udt_documents accessible documents\" },\n );\n return { success: true, data: rows };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : String(error),\n };\n }\n}\n\nexport async function getDocument(\n documentId: string,\n): Promise<ServiceResult<DocumentRow>> {\n const { data, error } = await db()\n .schema(\"workbench\")\n .from(\"udt_documents\")\n .select(\"*\")\n .eq(\"id\", documentId)\n .is(\"deleted_at\", null)\n .maybeSingle();\n if (error) return { success: false, error: error.message };\n if (!data) {\n // A trashed or unreachable document is a state the person can act on,\n // never PostgREST's \"Cannot coerce the result to a single JSON object\".\n return {\n success: false,\n error:\n \"This document is in the trash or you no longer have access to it. Restore it from Trash, or ask its owner to share it again.\",\n };\n }\n return { success: true, data: data as DocumentRow };\n}\n\nexport async function renameDocument(\n documentId: string,\n name: string,\n): Promise<ServiceResult<DocumentRow>> {\n const { data, error } = await writeOneRow(\n db()\n .schema(\"workbench\")\n .from(\"udt_documents\")\n .update({ document_name: name, updated_at: new Date().toISOString() })\n .eq(\"id\", documentId)\n .select(\"*\"),\n { action: \"update\", noun: \"document\" },\n );\n if (error) return { success: false, error: error.message };\n return { success: true, data: data as DocumentRow };\n}\n\n/**\n * Move the document into a project (or out of one with `null`). Sibling of\n * `renameDocument`: the one named setter for `project_id`.\n */\nexport async function moveDocument(\n documentId: string,\n projectId: string | null,\n): Promise<ServiceResult<DocumentRow>> {\n const { data, error } = await writeOneRow(\n db()\n .schema(\"workbench\")\n .from(\"udt_documents\")\n .update({ project_id: projectId, updated_at: new Date().toISOString() })\n .eq(\"id\", documentId)\n .select(\"*\"),\n { action: \"update\", noun: \"document\" },\n );\n if (error) return { success: false, error: error.message };\n return { success: true, data: data as DocumentRow };\n}\n\n/**\n * Rewrite the document's description. Sibling of `renameDocument` — the two\n * human-authored fields on `udt_documents` each get a named setter so callers\n * never hand-roll a `.from(\"udt_documents\").update(...)`. Pass `null` (or an\n * empty string) to clear it.\n *\n * Added when the documents surface became agent-writable: the\n * `document_description` write target on `/documents/[id]` is its caller, the\n * same way `updateWorkbookDescription` serves `workbook_description`. Keeping\n * the pair symmetric is this file's standing contract with `workbook-service`\n * (see the module header).\n */\nexport async function updateDocumentDescription(\n documentId: string,\n description: string | null,\n): Promise<ServiceResult<DocumentRow>> {\n const { data, error } = await writeOneRow(\n db()\n .schema(\"workbench\")\n .from(\"udt_documents\")\n .update({\n description: description && description.length > 0 ? description : null,\n updated_at: new Date().toISOString(),\n })\n .eq(\"id\", documentId)\n .select(\"*\"),\n { action: \"update\", noun: \"document\" },\n );\n if (error) return { success: false, error: error.message };\n return { success: true, data: data as DocumentRow };\n}\n\n/**\n * Soft delete — the document is tombstoned, not destroyed, and its snapshots stay\n * with it. Every read path filters `deleted_at is null`. Pair with\n * restoreDocument for undo. (aidream migration 0458.)\n */\nexport async function restoreDocument(\n documentId: string,\n): Promise<ServiceResult<true>> {\n const { error } = await tryWriteOne(\n db()\n .schema(\"workbench\")\n .from(\"udt_documents\")\n .update({ deleted_at: null })\n .eq(\"id\", documentId)\n .select(\"id\"),\n { action: \"restore\", noun: \"document\" },\n );\n if (error) return { success: false, error: error.message };\n return { success: true, data: true };\n}\n\n/**\n * Archive (delete means archive: never a hard delete) — only for rolling back a document this very flow just created and\n * failed to populate. Never use for a user-initiated delete: that is\n * deleteDocument, which tombstones and stays recoverable.\n */\nexport async function discardFailedDocument(\n documentId: string,\n): Promise<ServiceResult<true>> {\n const { error } = await tryWriteOne(\n db()\n .schema(\"workbench\")\n .from(\"udt_documents\")\n .update({ deleted_at: new Date().toISOString() })\n .eq(\"id\", documentId)\n .select(\"id\"),\n { action: \"archive\", noun: \"document\" },\n );\n if (error) return { success: false, error: error.message };\n return { success: true, data: true };\n}\n\nexport async function deleteDocument(\n documentId: string,\n): Promise<ServiceResult<true>> {\n const { error } = await tryWriteOne(\n db()\n .schema(\"workbench\")\n .from(\"udt_documents\")\n .update({ deleted_at: new Date().toISOString() })\n .eq(\"id\", documentId)\n .select(\"id\"),\n { action: \"delete\", noun: \"document\" },\n );\n if (error) return { success: false, error: error.message };\n return { success: true, data: true };\n}\n\n// ─── snapshots (document content) ────────────────────────────────────────────\n\n/**\n * Latest-snapshot fetch — what an opened document hydrates from. Returns\n * `data: null` (success path) when the document has no snapshots yet (newly\n * created, never saved). Distinguish \"no snapshot\" from \"load error\" by\n * checking `result.data === null`.\n */\nexport async function getLatestDocumentSnapshot(\n documentId: string,\n): Promise<ServiceResult<DocumentSnapshot | null>> {\n const { data, error } = await db()\n .schema(\"workbench\")\n .from(\"udt_document_snapshots\")\n .select(\"*\")\n .eq(\"document_id\", documentId)\n .order(\"created_at\", { ascending: false })\n .limit(1)\n .maybeSingle();\n if (error) return { success: false, error: error.message };\n return { success: true, data: (data ?? null) as DocumentSnapshot | null };\n}\n\nexport type SaveDocumentSnapshotArgs = {\n documentId: string;\n snapshot: unknown; // opaque to us — Univer IDocumentData decides the shape\n label?: string | null;\n origin?: DocumentSnapshotOrigin;\n};\n\nexport async function saveDocumentSnapshot(\n args: SaveDocumentSnapshotArgs,\n): Promise<ServiceResult<DocumentSnapshot>> {\n const { data: userData } = await getClaimsUser(db());\n const { data, error } = await db()\n .schema(\"workbench\")\n .from(\"udt_document_snapshots\")\n .insert({\n document_id: args.documentId,\n snapshot: args.snapshot as never,\n label: args.label ?? null,\n origin: args.origin ?? \"autosave\",\n created_by: userData?.user?.id ?? null,\n })\n .select(\"*\")\n .single();\n if (error) return { success: false, error: error.message };\n\n // Touch the parent document's updated_at so list views can sort by recency\n // without scanning snapshots. Best-effort — failure here is harmless.\n // write-lands-exempt: best-effort recency stamp on the parent document; the snapshot insert above is the real write\n await db()\n .schema(\"workbench\")\n .from(\"udt_documents\")\n .update({ updated_at: new Date().toISOString() })\n .eq(\"id\", args.documentId);\n\n return { success: true, data: data as DocumentSnapshot };\n}\n\n/**\n * An independent copy of a document: its own row, with the latest snapshot copied over, so editing the copy never\n * touches the original. The copy lives in the original's organization and project. Used by a board-template use.\n */\nexport async function copyDocument(\n documentId: string,\n name?: string,\n): Promise<ServiceResult<DocumentRow>> {\n const original = await getDocument(documentId);\n if (!original.success) return original;\n const row = original.data;\n if (!row.organization_id) {\n return { success: false, error: \"This document has no organization and cannot be copied.\" };\n }\n const created = await createDocument({\n name: name ?? row.document_name,\n description: row.description ?? null,\n organizationId: row.organization_id,\n projectId: row.project_id ?? null,\n isPublic: row.visibility === \"public\",\n });\n if (!created.success) return created;\n const latest = await getLatestDocumentSnapshot(documentId);\n if (!latest.success) return latest;\n if (latest.data) {\n const saved = await saveDocumentSnapshot({\n documentId: created.data.id,\n snapshot: latest.data.snapshot,\n label: latest.data.label,\n origin: \"autosave\",\n });\n if (!saved.success) return saved;\n }\n return created;\n}\n\n/** Trash view: the caller's own archived documents (RLS decides what is readable). */\nexport async function listTrashedDocuments(): Promise<\n ServiceResult<DocumentRow[]>\n> {\n const { data, error } = await db()\n .schema(\"workbench\")\n .from(\"udt_documents\")\n .select(\"*\")\n .not(\"deleted_at\", \"is\", null)\n .order(\"deleted_at\", { ascending: false });\n if (error) return { success: false, error: error.message };\n return { success: true, data: (data ?? []) as DocumentRow[] };\n}\n\nexport async function listDocumentSnapshots(\n documentId: string,\n limit = 50,\n): Promise<ServiceResult<DocumentSnapshot[]>> {\n const { data, error } = await db()\n .schema(\"workbench\")\n .from(\"udt_document_snapshots\")\n // component-created-by-ok: append-only snapshot with NO updated_by column and NO parent-rewrite trigger — created_by is the author the client wrote at insert (the only person field this table has)\n .select(\"id, document_id, label, origin, created_by, created_at\")\n .eq(\"document_id\", documentId)\n .order(\"created_at\", { ascending: false })\n .limit(limit);\n if (error) return { success: false, error: error.message };\n return { success: true, data: (data ?? []) as DocumentSnapshot[] };\n}\n","/**\n * sanitizeUniverDocSnapshot — defensive normalizer for Univer document\n * snapshots loaded from storage.\n *\n * WHY THIS EXISTS (loud recovery layer):\n * Univer's `NamedStyleType` is a NUMERIC enum (`HEADING_1 = 4`, `HEADING_2 = 5`,\n * …). Some snapshots in storage were produced by an external markdown→Univer\n * converter that wrote the *string* name (`\"HEADING_1\"`) into\n * `paragraphStyle.namedStyleType` instead of the numeric value. At render time\n * Univer does `HEADING_ICON_MAP[namedStyleType]` → `undefined` →\n * `icon.key` throws `Cannot read properties of undefined (reading 'key')` inside\n * the `<ParagraphMenu>` component the moment the cursor lands in a heading. That\n * uncaught error tears down Univer's React root, so the document loads and then\n * vanishes. See `features/data-tables/FEATURE.md`.\n *\n * This normalizer converts those string enum values back to numbers BEFORE the\n * snapshot reaches `createUniverDoc`. It is a recovery layer: when it fires it\n * means malformed data slipped past the writer, so it SCREAMS (console.warn)\n * with a count and the document id. The proper long-term fix is to make the\n * writer emit numeric enum values; until then this keeps the editor alive.\n *\n * Returns the SAME object (mutated in place) for ergonomic call sites, plus a\n * `fixed` count via the second return slot is intentionally avoided — callers\n * only need the sanitized snapshot; the warning is the signal.\n */\nimport { NamedStyleType } from \"@univerjs/core\";\nimport type { IDocumentData } from \"@univerjs/core\";\n\nimport {\n defaultDocumentPageStyle,\n isLegacySqueezedPageStyle,\n} from \"../core\";\n\n/**\n * Map of every string name that could appear in a legacy snapshot to its\n * canonical numeric `NamedStyleType`. Built from the enum itself so it stays in\n * lockstep with Univer (numeric enums expose name→value on string keys).\n */\nconst NAMED_STYLE_STRING_TO_NUMBER: Record<string, number> = Object.fromEntries(\n Object.entries(NamedStyleType)\n .filter(([key, val]) => typeof val === \"number\" && isNaN(Number(key)))\n .map(([key, val]) => [key, val as number]),\n);\n\n/**\n * Normalize a Univer document snapshot in place. Currently fixes string-valued\n * `paragraphStyle.namedStyleType` (the confirmed `<ParagraphMenu>` crash). New\n * classes of string-vs-enum corruption should be added here, not patched at the\n * call site.\n *\n * @param snapshot The snapshot about to be passed to `createUniverDoc`.\n * @param docId Document id, for the loud warning only.\n * @returns the same snapshot reference (mutated).\n */\nexport function sanitizeUniverDocSnapshot(\n snapshot: Partial<IDocumentData>,\n docId?: string,\n): Partial<IDocumentData> {\n restorePageStyle(snapshot, docId);\n\n const paragraphs = snapshot?.body?.paragraphs;\n if (!Array.isArray(paragraphs)) return snapshot;\n\n let fixed = 0;\n for (const paragraph of paragraphs) {\n const style = paragraph?.paragraphStyle as\n { namedStyleType?: unknown } | undefined;\n const value = style?.namedStyleType;\n if (typeof value === \"string\") {\n const numeric = NAMED_STYLE_STRING_TO_NUMBER[value];\n if (numeric !== undefined) {\n (style as { namedStyleType?: number }).namedStyleType = numeric;\n fixed += 1;\n } else {\n // Unknown string we can't map — drop it so Univer falls back to\n // NORMAL_TEXT rather than crashing on the lookup.\n delete (style as { namedStyleType?: unknown }).namedStyleType;\n fixed += 1;\n }\n }\n }\n\n if (fixed > 0) {\n console.warn(\n `[data-tables] RECOVERY: sanitized ${fixed} string namedStyleType value(s) ` +\n `to numeric NamedStyleType in document snapshot${docId ? ` ${docId}` : \"\"}. ` +\n `The snapshot was written with the wrong enum encoding (string instead of ` +\n `number) and would have crashed Univer's <ParagraphMenu>. Fix the snapshot ` +\n `writer to emit numeric NamedStyleType values.`,\n );\n }\n\n return snapshot;\n}\n\n/**\n * Second recovery: a snapshot with no `documentStyle.pageSize` has no page box,\n * so Univer lays the body out unbounded — text never wraps and the docs\n * viewport reports no scrollable extent, which reads to the user as \"the\n * document won't scroll\". Snapshots written by agents (`origin='agent'`) and by\n * older converters reach storage with `documentStyle: {}`. Stamp the canonical\n * page geometry back on and SCREAM: the writer is the real bug.\n */\nfunction restorePageStyle(snapshot: Partial<IDocumentData>, docId?: string) {\n if (!snapshot || typeof snapshot !== \"object\") return;\n\n // THE SQUEEZED PAGE (2026-10-15): documents carried A4-at-72dpi geometry in Univer's pageless\n // flavour — a 415px text column with no paper. Move them to a real Letter page, unchanged text.\n // A document with no flavour at all is a paginated page too (Univer would otherwise pick pageless).\n const style = snapshot.documentStyle;\n if (style?.pageSize?.width && isLegacySqueezedPageStyle(style)) {\n const page = defaultDocumentPageStyle();\n snapshot.documentStyle = { ...style, ...page, defaultParagraphStyle: style.defaultParagraphStyle ?? page.defaultParagraphStyle } as typeof style;\n return;\n }\n if (style?.pageSize?.width) {\n if (!style.documentFlavor) {\n const page = defaultDocumentPageStyle();\n Object.assign(style, { documentFlavor: page.documentFlavor });\n if (page.defaultParagraphStyle) style.defaultParagraphStyle ??= page.defaultParagraphStyle;\n }\n return;\n }\n\n snapshot.documentStyle = {\n ...defaultDocumentPageStyle(),\n ...(snapshot.documentStyle ?? {}),\n pageSize: defaultDocumentPageStyle().pageSize,\n } as NonNullable<typeof snapshot.documentStyle>;\n\n console.warn(\n `[data-tables] RECOVERY: document snapshot${docId ? ` ${docId}` : \"\"} had no ` +\n `documentStyle.pageSize — restored the default Letter page geometry. Without a ` +\n `page box Univer lays the body out unbounded: text does not wrap and the ` +\n `viewport has no scrollable extent, so the document appears frozen. Fix the ` +\n `snapshot writer (agent/markdown converters must stamp ` +\n `DEFAULT_DOCUMENT_PAGE_STYLE).`,\n );\n}\n"],"mappings":";;;;;;AAsBA,IAAM,OAAO,uBAAO,IAAI,2BAA2B;AAEnD,IAAM,OAAO,MAAY;AACvB,QAAM,IAAI;AACV,SAAQ,EAAE,IAAI,MAAM,CAAC;AACvB;AAEO,SAAS,kBAAkB,OAA4B;AAC5D,OAAK,EAAE,QAAQ;AACjB;AAEO,SAAS,cAA6B;AAC3C,QAAM,QAAQ,KAAK,EAAE;AACrB,MAAI,CAAC,MAAO,OAAM,IAAI,MAAM,iGAAiG;AAC7H,SAAO;AACT;;;ACnBA,SAAS,kCAAkC;AAC3C,SAAS,qBAAqB;AAC9B,SAAS,aAAa,aAAa,mBAAmB;AAOtD,IAAM,KAAK,MAAM,YAAY,EAAE,YAAY;AAoB3C,eAAsB,eACpB,MACqC;AACrC,MAAI;AACJ,MAAI;AACF,qBAAiB,2BAA2B,QAAW,KAAK,cAAc;AAAA,EAC5E,SAASA,QAAO;AACd,WAAO;AAAA,MACL,SAAS;AAAA,MACT,OACEA,kBAAiB,QACbA,OAAM,UACN;AAAA,IACR;AAAA,EACF;AAEA,QAAM,EAAE,MAAM,UAAU,OAAO,QAAQ,IAAI,MAAM,cAAc,GAAG,CAAC;AACnE,MAAI,WAAW,CAAC,UAAU,MAAM;AAC9B,WAAO;AAAA,MACL,SAAS;AAAA,MACT,OAAO,SAAS,WAAW;AAAA,IAC7B;AAAA,EACF;AAEA,QAAM,EAAE,MAAM,MAAM,IAAI,MAAM,GAAG,EAC9B,OAAO,WAAW,EAClB,KAAK,eAAe,EACpB,OAAO;AAAA,IACN,eAAe,KAAK;AAAA,IACpB,aAAa,KAAK,eAAe;AAAA,IACjC,QAAQ,KAAK,UAAU;AAAA,IACvB,iBAAiB;AAAA,IACjB,YAAY,KAAK,aAAa;AAAA,IAC9B,SAAS,KAAK,UAAU;AAAA,IACxB,kBAAkB,KAAK,kBAAkB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAOzC,YAAY,SAAS,KAAK;AAAA,IAC1B,YAAY,KAAK,WAAW,WAAW;AAAA,EACzC,CAAC,EACA,OAAO,GAAG,EACV,OAAO;AACV,MAAI,MAAO,QAAO,EAAE,SAAS,OAAO,OAAO,MAAM,QAAQ;AACzD,SAAO,EAAE,SAAS,MAAM,KAA0B;AACpD;AAEA,eAAsB,0BAEpB;AAIA,MAAI;AACF,UAAM,OAAO,MAAM;AAAA,MACjB,CAAC,EAAE,MAAM,GAAG,MACV,GAAG,EACA,OAAO,WAAW,EAClB,KAAK,eAAe,EACpB,OAAO,KAAK,EAAE,OAAO,QAAQ,CAAC,EAC9B,GAAG,cAAc,IAAI,EACrB,MAAM,cAAc,EAAE,WAAW,MAAM,CAAC,EACxC,MAAM,MAAM,EAAE,WAAW,KAAK,CAAC,EAC/B,MAAM,MAAM,EAAE,EACd,QAAuB;AAAA,MAC5B,EAAE,OAAO,+CAA+C;AAAA,IAC1D;AACA,WAAO,EAAE,SAAS,MAAM,MAAM,KAAK;AAAA,EACrC,SAAS,OAAO;AACd,WAAO;AAAA,MACL,SAAS;AAAA,MACT,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAAA,IAC9D;AAAA,EACF;AACF;AAEA,eAAsB,YACpB,YACqC;AACrC,QAAM,EAAE,MAAM,MAAM,IAAI,MAAM,GAAG,EAC9B,OAAO,WAAW,EAClB,KAAK,eAAe,EACpB,OAAO,GAAG,EACV,GAAG,MAAM,UAAU,EACnB,GAAG,cAAc,IAAI,EACrB,YAAY;AACf,MAAI,MAAO,QAAO,EAAE,SAAS,OAAO,OAAO,MAAM,QAAQ;AACzD,MAAI,CAAC,MAAM;AAGT,WAAO;AAAA,MACL,SAAS;AAAA,MACT,OACE;AAAA,IACJ;AAAA,EACF;AACA,SAAO,EAAE,SAAS,MAAM,KAA0B;AACpD;AAEA,eAAsB,eACpB,YACA,MACqC;AACrC,QAAM,EAAE,MAAM,MAAM,IAAI,MAAM;AAAA,IAC5B,GAAG,EACA,OAAO,WAAW,EAClB,KAAK,eAAe,EACpB,OAAO,EAAE,eAAe,MAAM,aAAY,oBAAI,KAAK,GAAE,YAAY,EAAE,CAAC,EACpE,GAAG,MAAM,UAAU,EACnB,OAAO,GAAG;AAAA,IACb,EAAE,QAAQ,UAAU,MAAM,WAAW;AAAA,EACvC;AACA,MAAI,MAAO,QAAO,EAAE,SAAS,OAAO,OAAO,MAAM,QAAQ;AACzD,SAAO,EAAE,SAAS,MAAM,KAA0B;AACpD;AAMA,eAAsB,aACpB,YACA,WACqC;AACrC,QAAM,EAAE,MAAM,MAAM,IAAI,MAAM;AAAA,IAC5B,GAAG,EACA,OAAO,WAAW,EAClB,KAAK,eAAe,EACpB,OAAO,EAAE,YAAY,WAAW,aAAY,oBAAI,KAAK,GAAE,YAAY,EAAE,CAAC,EACtE,GAAG,MAAM,UAAU,EACnB,OAAO,GAAG;AAAA,IACb,EAAE,QAAQ,UAAU,MAAM,WAAW;AAAA,EACvC;AACA,MAAI,MAAO,QAAO,EAAE,SAAS,OAAO,OAAO,MAAM,QAAQ;AACzD,SAAO,EAAE,SAAS,MAAM,KAA0B;AACpD;AAcA,eAAsB,0BACpB,YACA,aACqC;AACrC,QAAM,EAAE,MAAM,MAAM,IAAI,MAAM;AAAA,IAC5B,GAAG,EACA,OAAO,WAAW,EAClB,KAAK,eAAe,EACpB,OAAO;AAAA,MACN,aAAa,eAAe,YAAY,SAAS,IAAI,cAAc;AAAA,MACnE,aAAY,oBAAI,KAAK,GAAE,YAAY;AAAA,IACrC,CAAC,EACA,GAAG,MAAM,UAAU,EACnB,OAAO,GAAG;AAAA,IACb,EAAE,QAAQ,UAAU,MAAM,WAAW;AAAA,EACvC;AACA,MAAI,MAAO,QAAO,EAAE,SAAS,OAAO,OAAO,MAAM,QAAQ;AACzD,SAAO,EAAE,SAAS,MAAM,KAA0B;AACpD;AAOA,eAAsB,gBACpB,YAC8B;AAC9B,QAAM,EAAE,MAAM,IAAI,MAAM;AAAA,IACtB,GAAG,EACA,OAAO,WAAW,EAClB,KAAK,eAAe,EACpB,OAAO,EAAE,YAAY,KAAK,CAAC,EAC3B,GAAG,MAAM,UAAU,EACnB,OAAO,IAAI;AAAA,IACd,EAAE,QAAQ,WAAW,MAAM,WAAW;AAAA,EACxC;AACA,MAAI,MAAO,QAAO,EAAE,SAAS,OAAO,OAAO,MAAM,QAAQ;AACzD,SAAO,EAAE,SAAS,MAAM,MAAM,KAAK;AACrC;AAOA,eAAsB,sBACpB,YAC8B;AAC9B,QAAM,EAAE,MAAM,IAAI,MAAM;AAAA,IACtB,GAAG,EACA,OAAO,WAAW,EAClB,KAAK,eAAe,EACpB,OAAO,EAAE,aAAY,oBAAI,KAAK,GAAE,YAAY,EAAE,CAAC,EAC/C,GAAG,MAAM,UAAU,EACnB,OAAO,IAAI;AAAA,IACd,EAAE,QAAQ,WAAW,MAAM,WAAW;AAAA,EACxC;AACA,MAAI,MAAO,QAAO,EAAE,SAAS,OAAO,OAAO,MAAM,QAAQ;AACzD,SAAO,EAAE,SAAS,MAAM,MAAM,KAAK;AACrC;AAEA,eAAsB,eACpB,YAC8B;AAC9B,QAAM,EAAE,MAAM,IAAI,MAAM;AAAA,IACtB,GAAG,EACA,OAAO,WAAW,EAClB,KAAK,eAAe,EACpB,OAAO,EAAE,aAAY,oBAAI,KAAK,GAAE,YAAY,EAAE,CAAC,EAC/C,GAAG,MAAM,UAAU,EACnB,OAAO,IAAI;AAAA,IACd,EAAE,QAAQ,UAAU,MAAM,WAAW;AAAA,EACvC;AACA,MAAI,MAAO,QAAO,EAAE,SAAS,OAAO,OAAO,MAAM,QAAQ;AACzD,SAAO,EAAE,SAAS,MAAM,MAAM,KAAK;AACrC;AAUA,eAAsB,0BACpB,YACiD;AACjD,QAAM,EAAE,MAAM,MAAM,IAAI,MAAM,GAAG,EAC9B,OAAO,WAAW,EAClB,KAAK,wBAAwB,EAC7B,OAAO,GAAG,EACV,GAAG,eAAe,UAAU,EAC5B,MAAM,cAAc,EAAE,WAAW,MAAM,CAAC,EACxC,MAAM,CAAC,EACP,YAAY;AACf,MAAI,MAAO,QAAO,EAAE,SAAS,OAAO,OAAO,MAAM,QAAQ;AACzD,SAAO,EAAE,SAAS,MAAM,MAAO,QAAQ,KAAiC;AAC1E;AASA,eAAsB,qBACpB,MAC0C;AAC1C,QAAM,EAAE,MAAM,SAAS,IAAI,MAAM,cAAc,GAAG,CAAC;AACnD,QAAM,EAAE,MAAM,MAAM,IAAI,MAAM,GAAG,EAC9B,OAAO,WAAW,EAClB,KAAK,wBAAwB,EAC7B,OAAO;AAAA,IACN,aAAa,KAAK;AAAA,IAClB,UAAU,KAAK;AAAA,IACf,OAAO,KAAK,SAAS;AAAA,IACrB,QAAQ,KAAK,UAAU;AAAA,IACvB,YAAY,UAAU,MAAM,MAAM;AAAA,EACpC,CAAC,EACA,OAAO,GAAG,EACV,OAAO;AACV,MAAI,MAAO,QAAO,EAAE,SAAS,OAAO,OAAO,MAAM,QAAQ;AAKzD,QAAM,GAAG,EACN,OAAO,WAAW,EAClB,KAAK,eAAe,EACpB,OAAO,EAAE,aAAY,oBAAI,KAAK,GAAE,YAAY,EAAE,CAAC,EAC/C,GAAG,MAAM,KAAK,UAAU;AAE3B,SAAO,EAAE,SAAS,MAAM,KAA+B;AACzD;AAMA,eAAsB,aACpB,YACA,MACqC;AACrC,QAAM,WAAW,MAAM,YAAY,UAAU;AAC7C,MAAI,CAAC,SAAS,QAAS,QAAO;AAC9B,QAAM,MAAM,SAAS;AACrB,MAAI,CAAC,IAAI,iBAAiB;AACxB,WAAO,EAAE,SAAS,OAAO,OAAO,0DAA0D;AAAA,EAC5F;AACA,QAAM,UAAU,MAAM,eAAe;AAAA,IACnC,MAAM,QAAQ,IAAI;AAAA,IAClB,aAAa,IAAI,eAAe;AAAA,IAChC,gBAAgB,IAAI;AAAA,IACpB,WAAW,IAAI,cAAc;AAAA,IAC7B,UAAU,IAAI,eAAe;AAAA,EAC/B,CAAC;AACD,MAAI,CAAC,QAAQ,QAAS,QAAO;AAC7B,QAAM,SAAS,MAAM,0BAA0B,UAAU;AACzD,MAAI,CAAC,OAAO,QAAS,QAAO;AAC5B,MAAI,OAAO,MAAM;AACf,UAAM,QAAQ,MAAM,qBAAqB;AAAA,MACvC,YAAY,QAAQ,KAAK;AAAA,MACzB,UAAU,OAAO,KAAK;AAAA,MACtB,OAAO,OAAO,KAAK;AAAA,MACnB,QAAQ;AAAA,IACV,CAAC;AACD,QAAI,CAAC,MAAM,QAAS,QAAO;AAAA,EAC7B;AACA,SAAO;AACT;AAGA,eAAsB,uBAEpB;AACA,QAAM,EAAE,MAAM,MAAM,IAAI,MAAM,GAAG,EAC9B,OAAO,WAAW,EAClB,KAAK,eAAe,EACpB,OAAO,GAAG,EACV,IAAI,cAAc,MAAM,IAAI,EAC5B,MAAM,cAAc,EAAE,WAAW,MAAM,CAAC;AAC3C,MAAI,MAAO,QAAO,EAAE,SAAS,OAAO,OAAO,MAAM,QAAQ;AACzD,SAAO,EAAE,SAAS,MAAM,MAAO,QAAQ,CAAC,EAAoB;AAC9D;AAEA,eAAsB,sBACpB,YACA,QAAQ,IACoC;AAC5C,QAAM,EAAE,MAAM,MAAM,IAAI,MAAM,GAAG,EAC9B,OAAO,WAAW,EAClB,KAAK,wBAAwB,EAE7B,OAAO,wDAAwD,EAC/D,GAAG,eAAe,UAAU,EAC5B,MAAM,cAAc,EAAE,WAAW,MAAM,CAAC,EACxC,MAAM,KAAK;AACd,MAAI,MAAO,QAAO,EAAE,SAAS,OAAO,OAAO,MAAM,QAAQ;AACzD,SAAO,EAAE,SAAS,MAAM,MAAO,QAAQ,CAAC,EAAyB;AACnE;;;ACxXA,SAAS,sBAAsB;AAa/B,IAAM,+BAAuD,OAAO;AAAA,EAClE,OAAO,QAAQ,cAAc,EAC1B,OAAO,CAAC,CAAC,KAAK,GAAG,MAAM,OAAO,QAAQ,YAAY,MAAM,OAAO,GAAG,CAAC,CAAC,EACpE,IAAI,CAAC,CAAC,KAAK,GAAG,MAAM,CAAC,KAAK,GAAa,CAAC;AAC7C;AAYO,SAAS,0BACd,UACA,OACwB;AACxB,mBAAiB,UAAU,KAAK;AAEhC,QAAM,aAAa,UAAU,MAAM;AACnC,MAAI,CAAC,MAAM,QAAQ,UAAU,EAAG,QAAO;AAEvC,MAAI,QAAQ;AACZ,aAAW,aAAa,YAAY;AAClC,UAAM,QAAQ,WAAW;AAEzB,UAAM,QAAQ,OAAO;AACrB,QAAI,OAAO,UAAU,UAAU;AAC7B,YAAM,UAAU,6BAA6B,KAAK;AAClD,UAAI,YAAY,QAAW;AACzB,QAAC,MAAsC,iBAAiB;AACxD,iBAAS;AAAA,MACX,OAAO;AAGL,eAAQ,MAAuC;AAC/C,iBAAS;AAAA,MACX;AAAA,IACF;AAAA,EACF;AAEA,MAAI,QAAQ,GAAG;AACb,YAAQ;AAAA,MACN,qCAAqC,KAAK,iFACS,QAAQ,IAAI,KAAK,KAAK,EAAE;AAAA,IAI7E;AAAA,EACF;AAEA,SAAO;AACT;AAUA,SAAS,iBAAiB,UAAkC,OAAgB;AAC1E,MAAI,CAAC,YAAY,OAAO,aAAa,SAAU;AAK/C,QAAM,QAAQ,SAAS;AACvB,MAAI,OAAO,UAAU,SAAS,0BAA0B,KAAK,GAAG;AAC9D,UAAM,OAAO,yBAAyB;AACtC,aAAS,gBAAgB,EAAE,GAAG,OAAO,GAAG,MAAM,uBAAuB,MAAM,yBAAyB,KAAK,sBAAsB;AAC/H;AAAA,EACF;AACA,MAAI,OAAO,UAAU,OAAO;AAC1B,QAAI,CAAC,MAAM,gBAAgB;AACzB,YAAM,OAAO,yBAAyB;AACtC,aAAO,OAAO,OAAO,EAAE,gBAAgB,KAAK,eAAe,CAAC;AAC5D,UAAI,KAAK,sBAAuB,OAAM,0BAA0B,KAAK;AAAA,IACvE;AACA;AAAA,EACF;AAEA,WAAS,gBAAgB;AAAA,IACvB,GAAG,yBAAyB;AAAA,IAC5B,GAAI,SAAS,iBAAiB,CAAC;AAAA,IAC/B,UAAU,yBAAyB,EAAE;AAAA,EACvC;AAEA,UAAQ;AAAA,IACN,4CAA4C,QAAQ,IAAI,KAAK,KAAK,EAAE;AAAA,EAMtE;AACF;","names":["error"]}
1
+ {"version":3,"sources":["../src/docs/ports.ts","../src/docs/document-service.ts","../src/docs/sanitize-univer-doc-snapshot.ts"],"sourcesContent":["/**\n * The host ports of the Docs model and io.\n *\n * The package owns the document service, the open-document model and the converters, but two things\n * belong to the app that mounts them: the database client (a package owns no client) and the server\n * request that converts a .docx to .odt / .rtf (the app owns the server URL, auth and error capture).\n * The host states them once at start-up with `configureDocsHost`. State sits on a `globalThis` slot\n * so a dual-bundled copy of the package still sees one host. An unconfigured port THROWS naming\n * itself (nothing fails silently).\n */\nimport type { SupabaseClient } from \"@supabase/supabase-js\";\n\n/** A Supabase client over any schema: the package cannot import the app's generated database types. */\nexport type DocsSupabase = SupabaseClient<any, any, any>;\n\nexport interface DocsHostPorts {\n /** The app's database client (signed in as the person). */\n getSupabase(): DocsSupabase;\n /** An authenticated request to the Matrx server returning the raw `Response` (the open-format converter). */\n requestRaw(path: string, init: { method: \"POST\"; body: FormData }): Promise<Response>;\n}\n\nconst SLOT = Symbol.for(\"ai-matrx.office.docs-host\");\ntype Slot = { ports?: DocsHostPorts };\nconst slot = (): Slot => {\n const g = globalThis as Record<symbol, Slot | undefined>;\n return (g[SLOT] ??= {});\n};\n\nexport function configureDocsHost(ports: DocsHostPorts): void {\n slot().ports = ports;\n}\n\nexport function getDocsHost(): DocsHostPorts {\n const ports = slot().ports;\n if (!ports) throw new Error(\"@ai-matrx/office/docs: configureDocsHost(ports) was not called before the document service ran.\");\n return ports;\n}\n","/**\n * Document service — typed wrappers for `udt_documents` + `udt_document_snapshots`.\n *\n * Cloud document editor backed by Univer's preset-docs-core. Same shape as\n * `workbook-service.ts` — metadata in `udt_documents`, content state in\n * append-only `udt_document_snapshots`. The editor hydrates from the LATEST\n * snapshot and writes a new snapshot per save (debounced on the client —\n * see DocumentEditor).\n *\n * What lives elsewhere:\n * - Sharing / permissions: features/sharing/ + `has_permission(...)` RLS\n * - The open document (one model per id per tab: load, one save, realtime,\n * collab): features/documents/document-model/\n * - Component (a view of that model): features/documents/components/DocumentEditor\n *\n * Mirrors `workbook-service.ts`. If you're changing the shape of one, change\n * the other at the same time — see `features/data-tables/FEATURE.md`.\n */\nimport { requireOrganizationContext } from \"@ai-matrx/api\";\nimport { getClaimsUser } from \"@ai-matrx/auth\";\nimport { readAllRows, tryWriteOne, writeOneRow } from \"@ai-matrx/data/db\";\nimport type { ServiceResult } from \"@ai-matrx/records/core\";\n\nimport type { DocumentRow, DocumentSnapshot, DocumentSnapshotOrigin } from \"../core\";\nimport { getDocsHost } from \"./ports\";\n\n/** The app's database client, read through the host port at call time. */\nconst db = () => getDocsHost().getSupabase();\n// ─── documents ───────────────────────────────────────────────────────────────\n\nexport type CreateDocumentArgs = {\n name: string;\n description?: string | null;\n /** Origin label for the document itself, mirrors `udt_documents.source`. */\n source?: \"created\" | \"imported_docx\" | \"imported_md\" | \"imported_txt\" | \"linked_gdoc\";\n organizationId: string;\n projectId?: string | null;\n taskId?: string | null;\n isPublic?: boolean;\n /**\n * cld_files.id of the source upload (DOCX / MD / TXT blob). Set on the\n * import flow so the lossless original is recoverable; FK is ON DELETE SET\n * NULL, so deleting the file just nulls the link — the document survives.\n */\n originalFileId?: string | null;\n};\n\nexport async function createDocument(\n args: CreateDocumentArgs,\n): Promise<ServiceResult<DocumentRow>> {\n let organizationId: string;\n try {\n organizationId = requireOrganizationContext(undefined, args.organizationId);\n } catch (error) {\n return {\n success: false,\n error:\n error instanceof Error\n ? error.message\n : \"Select an organization before creating a document.\",\n };\n }\n\n const { data: userData, error: userErr } = await getClaimsUser(db());\n if (userErr || !userData?.user) {\n return {\n success: false,\n error: userErr?.message ?? \"not authenticated\",\n };\n }\n\n const { data, error } = await db()\n .schema(\"workbench\")\n .from(\"udt_documents\")\n .insert({\n document_name: args.name,\n description: args.description ?? null,\n source: args.source ?? \"created\",\n organization_id: organizationId,\n project_id: args.projectId ?? null,\n task_id: args.taskId ?? null,\n original_file_id: args.originalFileId ?? null,\n // CANONICAL columns (workbench_udt_canonical_step1). `visibility` is the\n // source of truth; the legacy `is_public` boolean is derived from it by\n // the workbench._bridge_legacy_owner trigger, so writing both here would\n // be two authorities for one fact. A new document defaults to `internal`\n // — it is org work product, not an individual person's private thing\n // (db-rules §6) — unless the caller explicitly asked for public.\n created_by: userData.user.id,\n visibility: args.isPublic ? \"public\" : \"internal\",\n })\n .select(\"*\")\n .single();\n if (error) return { success: false, error: error.message };\n return { success: true, data: data as DocumentRow };\n}\n\nexport async function listAccessibleDocuments(): Promise<\n ServiceResult<DocumentRow[]>\n> {\n // RLS handles owner / published-to-the-web / shared access. The library and\n // the pickers treat this list as COMPLETE (\"you have no such document\"), so\n // it pages past PostgREST's silent 1000-row cap (D190).\n try {\n const rows = await readAllRows<DocumentRow>(\n ({ from, to }) =>\n db()\n .schema(\"workbench\")\n .from(\"udt_documents\")\n .select(\"*\", { count: \"exact\" })\n .is(\"deleted_at\", null)\n .order(\"updated_at\", { ascending: false })\n .order(\"id\", { ascending: true })\n .range(from, to)\n .returns<DocumentRow[]>(),\n { label: \"workbench.udt_documents accessible documents\" },\n );\n return { success: true, data: rows };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : String(error),\n };\n }\n}\n\nexport async function getDocument(\n documentId: string,\n): Promise<ServiceResult<DocumentRow>> {\n const { data, error } = await db()\n .schema(\"workbench\")\n .from(\"udt_documents\")\n .select(\"*\")\n .eq(\"id\", documentId)\n .is(\"deleted_at\", null)\n .maybeSingle();\n if (error) return { success: false, error: error.message };\n if (!data) {\n // A trashed or unreachable document is a state the person can act on,\n // never PostgREST's \"Cannot coerce the result to a single JSON object\".\n return {\n success: false,\n error:\n \"This document is in the trash or you no longer have access to it. Restore it from Trash, or ask its owner to share it again.\",\n };\n }\n return { success: true, data: data as DocumentRow };\n}\n\nexport async function renameDocument(\n documentId: string,\n name: string,\n): Promise<ServiceResult<DocumentRow>> {\n const { data, error } = await writeOneRow(\n db()\n .schema(\"workbench\")\n .from(\"udt_documents\")\n .update({ document_name: name, updated_at: new Date().toISOString() })\n .eq(\"id\", documentId)\n .select(\"*\"),\n { action: \"update\", noun: \"document\" },\n );\n if (error) return { success: false, error: error.message };\n return { success: true, data: data as DocumentRow };\n}\n\n/**\n * Move the document into a project (or out of one with `null`). Sibling of\n * `renameDocument`: the one named setter for `project_id`.\n */\nexport async function moveDocument(\n documentId: string,\n projectId: string | null,\n): Promise<ServiceResult<DocumentRow>> {\n const { data, error } = await writeOneRow(\n db()\n .schema(\"workbench\")\n .from(\"udt_documents\")\n .update({ project_id: projectId, updated_at: new Date().toISOString() })\n .eq(\"id\", documentId)\n .select(\"*\"),\n { action: \"update\", noun: \"document\" },\n );\n if (error) return { success: false, error: error.message };\n return { success: true, data: data as DocumentRow };\n}\n\n/**\n * Rewrite the document's description. Sibling of `renameDocument` — the two\n * human-authored fields on `udt_documents` each get a named setter so callers\n * never hand-roll a `.from(\"udt_documents\").update(...)`. Pass `null` (or an\n * empty string) to clear it.\n *\n * Added when the documents surface became agent-writable: the\n * `document_description` write target on `/documents/[id]` is its caller, the\n * same way `updateWorkbookDescription` serves `workbook_description`. Keeping\n * the pair symmetric is this file's standing contract with `workbook-service`\n * (see the module header).\n */\nexport async function updateDocumentDescription(\n documentId: string,\n description: string | null,\n): Promise<ServiceResult<DocumentRow>> {\n const { data, error } = await writeOneRow(\n db()\n .schema(\"workbench\")\n .from(\"udt_documents\")\n .update({\n description: description && description.length > 0 ? description : null,\n updated_at: new Date().toISOString(),\n })\n .eq(\"id\", documentId)\n .select(\"*\"),\n { action: \"update\", noun: \"document\" },\n );\n if (error) return { success: false, error: error.message };\n return { success: true, data: data as DocumentRow };\n}\n\n/**\n * Soft delete — the document is tombstoned, not destroyed, and its snapshots stay\n * with it. Every read path filters `deleted_at is null`. Pair with\n * restoreDocument for undo. (aidream migration 0458.)\n */\nexport async function restoreDocument(\n documentId: string,\n): Promise<ServiceResult<true>> {\n const { error } = await tryWriteOne(\n db()\n .schema(\"workbench\")\n .from(\"udt_documents\")\n .update({ deleted_at: null })\n .eq(\"id\", documentId)\n .select(\"id\"),\n { action: \"restore\", noun: \"document\" },\n );\n if (error) return { success: false, error: error.message };\n return { success: true, data: true };\n}\n\n/**\n * Archive (delete means archive: never a hard delete) — only for rolling back a document this very flow just created and\n * failed to populate. Never use for a user-initiated delete: that is\n * deleteDocument, which tombstones and stays recoverable.\n */\nexport async function discardFailedDocument(\n documentId: string,\n): Promise<ServiceResult<true>> {\n const { error } = await tryWriteOne(\n db()\n .schema(\"workbench\")\n .from(\"udt_documents\")\n .update({ deleted_at: new Date().toISOString() })\n .eq(\"id\", documentId)\n .select(\"id\"),\n { action: \"archive\", noun: \"document\" },\n );\n if (error) return { success: false, error: error.message };\n return { success: true, data: true };\n}\n\nexport async function deleteDocument(\n documentId: string,\n): Promise<ServiceResult<true>> {\n const { error } = await tryWriteOne(\n db()\n .schema(\"workbench\")\n .from(\"udt_documents\")\n .update({ deleted_at: new Date().toISOString() })\n .eq(\"id\", documentId)\n .select(\"id\"),\n { action: \"delete\", noun: \"document\" },\n );\n if (error) return { success: false, error: error.message };\n return { success: true, data: true };\n}\n\n// ─── snapshots (document content) ────────────────────────────────────────────\n\n/**\n * Latest-snapshot fetch — what an opened document hydrates from. Returns\n * `data: null` (success path) when the document has no snapshots yet (newly\n * created, never saved). Distinguish \"no snapshot\" from \"load error\" by\n * checking `result.data === null`.\n */\nexport async function getLatestDocumentSnapshot(\n documentId: string,\n): Promise<ServiceResult<DocumentSnapshot | null>> {\n const { data, error } = await db()\n .schema(\"workbench\")\n .from(\"udt_document_snapshots\")\n .select(\"*\")\n .eq(\"document_id\", documentId)\n .order(\"created_at\", { ascending: false })\n .limit(1)\n .maybeSingle();\n if (error) return { success: false, error: error.message };\n return { success: true, data: (data ?? null) as DocumentSnapshot | null };\n}\n\nexport type SaveDocumentSnapshotArgs = {\n documentId: string;\n snapshot: unknown; // opaque to us — Univer IDocumentData decides the shape\n label?: string | null;\n origin?: DocumentSnapshotOrigin;\n};\n\nexport async function saveDocumentSnapshot(\n args: SaveDocumentSnapshotArgs,\n): Promise<ServiceResult<DocumentSnapshot>> {\n const { data: userData } = await getClaimsUser(db());\n const { data, error } = await db()\n .schema(\"workbench\")\n .from(\"udt_document_snapshots\")\n .insert({\n document_id: args.documentId,\n snapshot: args.snapshot as never,\n label: args.label ?? null,\n origin: args.origin ?? \"autosave\",\n created_by: userData?.user?.id ?? null,\n })\n .select(\"*\")\n .single();\n if (error) return { success: false, error: error.message };\n\n // Touch the parent document's updated_at so list views can sort by recency\n // without scanning snapshots. Best-effort — failure here is harmless.\n // write-lands-exempt: best-effort recency stamp on the parent document; the snapshot insert above is the real write\n await db()\n .schema(\"workbench\")\n .from(\"udt_documents\")\n .update({ updated_at: new Date().toISOString() })\n .eq(\"id\", args.documentId);\n\n return { success: true, data: data as DocumentSnapshot };\n}\n\n/**\n * An independent copy of a document: its own row, with the latest snapshot copied over, so editing the copy never\n * touches the original. The copy lives in the original's organization and project. Used by a board-template use.\n */\nexport async function copyDocument(\n documentId: string,\n name?: string,\n): Promise<ServiceResult<DocumentRow>> {\n const original = await getDocument(documentId);\n if (!original.success) return original;\n const row = original.data;\n if (!row.organization_id) {\n return { success: false, error: \"This document has no organization and cannot be copied.\" };\n }\n const created = await createDocument({\n name: name ?? row.document_name,\n description: row.description ?? null,\n organizationId: row.organization_id,\n projectId: row.project_id ?? null,\n isPublic: row.visibility === \"public\",\n });\n if (!created.success) return created;\n const latest = await getLatestDocumentSnapshot(documentId);\n if (!latest.success) return latest;\n if (latest.data) {\n const saved = await saveDocumentSnapshot({\n documentId: created.data.id,\n snapshot: latest.data.snapshot,\n label: latest.data.label,\n origin: \"autosave\",\n });\n if (!saved.success) return saved;\n }\n return created;\n}\n\n/** Trash view: the caller's own archived documents (RLS decides what is readable). */\nexport async function listTrashedDocuments(): Promise<\n ServiceResult<DocumentRow[]>\n> {\n const { data, error } = await db()\n .schema(\"workbench\")\n .from(\"udt_documents\")\n .select(\"*\")\n .not(\"deleted_at\", \"is\", null)\n .order(\"deleted_at\", { ascending: false });\n if (error) return { success: false, error: error.message };\n return { success: true, data: (data ?? []) as DocumentRow[] };\n}\n\nexport async function listDocumentSnapshots(\n documentId: string,\n limit = 50,\n): Promise<ServiceResult<DocumentSnapshot[]>> {\n const { data, error } = await db()\n .schema(\"workbench\")\n .from(\"udt_document_snapshots\")\n // component-created-by-ok: append-only snapshot with NO updated_by column and NO parent-rewrite trigger — created_by is the author the client wrote at insert (the only person field this table has)\n .select(\"id, document_id, label, origin, created_by, created_at\")\n .eq(\"document_id\", documentId)\n .order(\"created_at\", { ascending: false })\n .limit(limit);\n if (error) return { success: false, error: error.message };\n return { success: true, data: (data ?? []) as DocumentSnapshot[] };\n}\n","/**\n * sanitizeUniverDocSnapshot — defensive normalizer for Univer document\n * snapshots loaded from storage.\n *\n * WHY THIS EXISTS (loud recovery layer):\n * Univer's `NamedStyleType` is a NUMERIC enum (`HEADING_1 = 4`, `HEADING_2 = 5`,\n * …). Some snapshots in storage were produced by an external markdown→Univer\n * converter that wrote the *string* name (`\"HEADING_1\"`) into\n * `paragraphStyle.namedStyleType` instead of the numeric value. At render time\n * Univer does `HEADING_ICON_MAP[namedStyleType]` → `undefined` →\n * `icon.key` throws `Cannot read properties of undefined (reading 'key')` inside\n * the `<ParagraphMenu>` component the moment the cursor lands in a heading. That\n * uncaught error tears down Univer's React root, so the document loads and then\n * vanishes. See `features/data-tables/FEATURE.md`.\n *\n * This normalizer converts those string enum values back to numbers BEFORE the\n * snapshot reaches `createUniverDoc`. It is a recovery layer: when it fires it\n * means malformed data slipped past the writer, so it SCREAMS (console.warn)\n * with a count and the document id. The proper long-term fix is to make the\n * writer emit numeric enum values; until then this keeps the editor alive.\n *\n * Returns the SAME object (mutated in place) for ergonomic call sites, plus a\n * `fixed` count via the second return slot is intentionally avoided — callers\n * only need the sanitized snapshot; the warning is the signal.\n */\nimport { NamedStyleType } from \"@univerjs/core\";\nimport type { IDocumentData } from \"@univerjs/core\";\n\nimport {\n defaultDocumentPageStyle,\n isLegacySqueezedPageStyle,\n} from \"../core\";\n\n/**\n * Map of every string name that could appear in a legacy snapshot to its\n * canonical numeric `NamedStyleType`. Built from the enum itself so it stays in\n * lockstep with Univer (numeric enums expose name→value on string keys).\n */\nconst NAMED_STYLE_STRING_TO_NUMBER: Record<string, number> = Object.fromEntries(\n Object.entries(NamedStyleType)\n .filter(([key, val]) => typeof val === \"number\" && isNaN(Number(key)))\n .map(([key, val]) => [key, val as number]),\n);\n\n/**\n * Normalize a Univer document snapshot in place. Currently fixes string-valued\n * `paragraphStyle.namedStyleType` (the confirmed `<ParagraphMenu>` crash). New\n * classes of string-vs-enum corruption should be added here, not patched at the\n * call site.\n *\n * @param snapshot The snapshot about to be passed to `createUniverDoc`.\n * @param docId Document id, for the loud warning only.\n * @returns the same snapshot reference (mutated).\n */\nexport function sanitizeUniverDocSnapshot(\n snapshot: Partial<IDocumentData>,\n docId?: string,\n): Partial<IDocumentData> {\n restorePageStyle(snapshot, docId);\n ensureTextRuns(snapshot);\n\n const paragraphs = snapshot?.body?.paragraphs;\n if (!Array.isArray(paragraphs)) return snapshot;\n\n let fixed = 0;\n for (const paragraph of paragraphs) {\n const style = paragraph?.paragraphStyle as\n { namedStyleType?: unknown } | undefined;\n const value = style?.namedStyleType;\n if (typeof value === \"string\") {\n const numeric = NAMED_STYLE_STRING_TO_NUMBER[value];\n if (numeric !== undefined) {\n (style as { namedStyleType?: number }).namedStyleType = numeric;\n fixed += 1;\n } else {\n // Unknown string we can't map — drop it so Univer falls back to\n // NORMAL_TEXT rather than crashing on the lookup.\n delete (style as { namedStyleType?: unknown }).namedStyleType;\n fixed += 1;\n }\n }\n }\n\n if (fixed > 0) {\n console.warn(\n `[data-tables] RECOVERY: sanitized ${fixed} string namedStyleType value(s) ` +\n `to numeric NamedStyleType in document snapshot${docId ? ` ${docId}` : \"\"}. ` +\n `The snapshot was written with the wrong enum encoding (string instead of ` +\n `number) and would have crashed Univer's <ParagraphMenu>. Fix the snapshot ` +\n `writer to emit numeric NamedStyleType values.`,\n );\n }\n\n return snapshot;\n}\n\n/**\n * Second recovery: a snapshot with no `documentStyle.pageSize` has no page box,\n * so Univer lays the body out unbounded — text never wraps and the docs\n * viewport reports no scrollable extent, which reads to the user as \"the\n * document won't scroll\". Snapshots written by agents (`origin='agent'`) and by\n * older converters reach storage with `documentStyle: {}`. Stamp the canonical\n * page geometry back on and SCREAM: the writer is the real bug.\n */\nfunction restorePageStyle(snapshot: Partial<IDocumentData>, docId?: string) {\n if (!snapshot || typeof snapshot !== \"object\") return;\n\n // THE SQUEEZED PAGE (2026-10-15): documents carried A4-at-72dpi geometry in Univer's pageless\n // flavour — a 415px text column with no paper. Move them to a real Letter page, unchanged text.\n // A document with no flavour at all is a paginated page too (Univer would otherwise pick pageless).\n const style = snapshot.documentStyle;\n if (style?.pageSize?.width && isLegacySqueezedPageStyle(style)) {\n const page = defaultDocumentPageStyle();\n snapshot.documentStyle = { ...style, ...page, defaultParagraphStyle: style.defaultParagraphStyle ?? page.defaultParagraphStyle } as typeof style;\n return;\n }\n if (style?.pageSize?.width) {\n if (!style.documentFlavor) {\n const page = defaultDocumentPageStyle();\n Object.assign(style, { documentFlavor: page.documentFlavor });\n if (page.defaultParagraphStyle) style.defaultParagraphStyle ??= page.defaultParagraphStyle;\n }\n return;\n }\n\n snapshot.documentStyle = {\n ...defaultDocumentPageStyle(),\n ...(snapshot.documentStyle ?? {}),\n pageSize: defaultDocumentPageStyle().pageSize,\n } as NonNullable<typeof snapshot.documentStyle>;\n\n console.warn(\n `[data-tables] RECOVERY: document snapshot${docId ? ` ${docId}` : \"\"} had no ` +\n `documentStyle.pageSize — restored the default Letter page geometry. Without a ` +\n `page box Univer lays the body out unbounded: text does not wrap and the ` +\n `viewport has no scrollable extent, so the document appears frozen. Fix the ` +\n `snapshot writer (agent/markdown converters must stamp ` +\n `DEFAULT_DOCUMENT_PAGE_STYLE).`,\n );\n}\n\n/**\n * A body with no `textRuns` array is a body Univer silently refuses styled text for: its text\n * insert (`insertTextRuns`) returns early when the target has none, so every bold, italic, code\n * and size run of pasted or agent-written markdown vanished (found 2026-10-16: `**notes**` pasted\n * into a blank document stayed plain). An empty array is the same document and accepts the runs.\n */\nfunction ensureTextRuns(snapshot: Partial<IDocumentData>) {\n const body = snapshot?.body;\n if (body && typeof body === \"object\" && !Array.isArray(body.textRuns)) body.textRuns = [];\n}\n"],"mappings":"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","names":["error"]}
package/dist/docs-io.cjs CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
 
6
6
 
7
- var _chunk6Z3Y6DUIcjs = require('./chunk-6Z3Y6DUI.cjs');
7
+ var _chunk556EUPETcjs = require('./chunk-556EUPET.cjs');
8
8
 
9
9
 
10
10
 
@@ -260,6 +260,11 @@ function documentFileKind(name) {
260
260
  return null;
261
261
  }
262
262
  var W = "http://schemas.openxmlformats.org/wordprocessingml/2006/main";
263
+ function markdownTable(rows) {
264
+ const width = Math.max(...rows.map((r) => r.length));
265
+ const line = (r) => `| ${Array.from({ length: width }, (_, i) => _nullishCoalesce(r[i], () => ( ""))).join(" | ")} |`;
266
+ return [line(rows[0]), `|${" --- |".repeat(width)}`, ...rows.slice(1).map(line)].join("\n");
267
+ }
263
268
  function docxXmlToMarkdown(xml) {
264
269
  const doc = new DOMParser().parseFromString(xml, "application/xml");
265
270
  const body = doc.getElementsByTagNameNS(W, "body")[0];
@@ -308,11 +313,7 @@ function docxXmlToMarkdown(xml) {
308
313
  (tc) => Array.from(tc.getElementsByTagNameNS(W, "p")).map(paragraph).join(" ").replace(/\|/g, "\\|").trim()
309
314
  )
310
315
  );
311
- if (rows.length) {
312
- out.push("", `| ${rows[0].join(" | ")} |`, `|${" --- |".repeat(rows[0].length)}`);
313
- for (const row of rows.slice(1)) out.push(`| ${row.join(" | ")} |`);
314
- out.push("");
315
- }
316
+ if (rows.length) out.push(markdownTable(rows));
316
317
  }
317
318
  }
318
319
  return out.reduce((acc, line, i) => {
@@ -360,6 +361,13 @@ function htmlToDocumentMarkdown(html) {
360
361
  out.push(inline(el).trim());
361
362
  return;
362
363
  }
364
+ if (tag === "table") {
365
+ const rows = Array.from(el.querySelectorAll("tr")).map(
366
+ (tr) => Array.from(tr.children).filter((c) => /^(td|th)$/i.test(c.tagName)).map((c) => inline(c).replace(/\|/g, "\\|").replace(/\s+/g, " ").trim())
367
+ );
368
+ if (rows.length) out.push(markdownTable(rows));
369
+ return;
370
+ }
363
371
  if (tag === "pre") {
364
372
  out.push("```", _nullishCoalesce(el.textContent, () => ( "")), "```");
365
373
  return;
@@ -411,13 +419,13 @@ async function documentFileToMarkdown(file) {
411
419
  async function importDocumentFile(file, organizationId) {
412
420
  const { markdown, source } = await documentFileToMarkdown(file);
413
421
  const name = file.name.replace(/\.[^.]+$/, "").trim() || _chunkUBLEFLEScjs.deriveDocumentName.call(void 0, markdown);
414
- const created = await _chunk6Z3Y6DUIcjs.createDocument.call(void 0, { name, source, organizationId, description: `Imported from ${file.name}` });
422
+ const created = await _chunk556EUPETcjs.createDocument.call(void 0, { name, source, organizationId, description: `Imported from ${file.name}` });
415
423
  if (!created.success) throw new Error(`Could not create the document: ${created.error}`);
416
424
  const id = created.data.id;
417
- const snapshot = _chunk6Z3Y6DUIcjs.sanitizeUniverDocSnapshot.call(void 0, _chunkUBLEFLEScjs.markdownToUniverDoc.call(void 0, markdown, name), id);
418
- const saved = await _chunk6Z3Y6DUIcjs.saveDocumentSnapshot.call(void 0, { documentId: id, snapshot, origin: "imported", label: `Imported from ${file.name}` });
425
+ const snapshot = _chunk556EUPETcjs.sanitizeUniverDocSnapshot.call(void 0, _chunkUBLEFLEScjs.markdownToUniverDoc.call(void 0, markdown, name), id);
426
+ const saved = await _chunk556EUPETcjs.saveDocumentSnapshot.call(void 0, { documentId: id, snapshot, origin: "imported", label: `Imported from ${file.name}` });
419
427
  if (!saved.success) {
420
- await _chunk6Z3Y6DUIcjs.deleteDocument.call(void 0, id);
428
+ await _chunk556EUPETcjs.deleteDocument.call(void 0, id);
421
429
  throw new Error(`Could not save the imported content: ${saved.error}`);
422
430
  }
423
431
  return { id, name };
@@ -459,7 +467,7 @@ async function downloadOpenFormat(markdown, format, title) {
459
467
  const form = new FormData();
460
468
  form.append("file", new Blob([docx]), fileName.endsWith(".docx") ? fileName : `${fileName}.docx`);
461
469
  form.append("target", target);
462
- const res = await _chunk6Z3Y6DUIcjs.getDocsHost.call(void 0, ).requestRaw("/office/convert-document", { method: "POST", body: form });
470
+ const res = await _chunk556EUPETcjs.getDocsHost.call(void 0, ).requestRaw("/office/convert-document", { method: "POST", body: form });
463
471
  return new Uint8Array(await res.arrayBuffer());
464
472
  },
465
473
  save: (fileName, bytes, mime) => {
@@ -1 +1 @@
1
- {"version":3,"sources":["/home/runner/work/aidream/aidream/apps/shared/office/dist/docs-io.cjs","../src/docs/univer-doc-to-markdown.ts","../src/docs/import-document-file.ts","../src/docs/export-open-formats.ts"],"names":["out"],"mappings":"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","file":"/home/runner/work/aidream/aidream/apps/shared/office/dist/docs-io.cjs","sourcesContent":[null,"/**\n * univerDocToMarkdown — the reverse of `markdown-to-univer-doc.ts`.\n *\n * Why it exists: the canvas pane for a cloud document (`udt_document`) offers a\n * `Source` tab, and the rule in `features/canvas/core/canvasSource.ts` is that\n * Source shows THE ITEM'S OWN SOURCE — for a document, its markdown — never the\n * redux envelope. A document's content lives as an opaque Univer\n * `IDocumentData` snapshot in `workbench.udt_document_snapshots`, so something\n * has to read it back out. This is that something, and it is the ONLY one:\n * never re-implement a second snapshot reader.\n *\n * What it reads (the half `markdownToUniverDoc` writes):\n * - `body.dataStream` — the text; `\\r` terminates a paragraph, a trailing\n * `\\n` terminates the section.\n * - `body.paragraphs` — one entry per `\\r` (`startIndex` = the `\\r` offset).\n * - `body.textRuns` — `{ st, ed, ts }` style overlays. `ts.bl` + a heading\n * font size is how `markdownToUniverDoc` encodes\n * `#`..`######`, so the level is recovered from `fs`.\n *\n * Fidelity is deliberately MODEST and honest: headings, bold, italic,\n * strikethrough and inline/monospace code round-trip; everything else comes\n * back as the plain text the user actually sees. A document typed by hand in\n * Univer never had markdown syntax in the first place — printing its text is\n * the truthful answer, not a reconstruction.\n *\n * PURE — no React, no Redux, no IO.\n */\n\nconst TABLE_CELL_SEP = \" | \";\n\n// Univer's DataStreamTreeTokenType control characters (literals: no Univer runtime here).\nconst TABLE_START = \"\\x1A\";\nconst TABLE_ROW_START = \"\\x1B\";\nconst TABLE_CELL_START = \"\\x1C\";\nconst TABLE_CELL_END = \"\\x1D\";\nconst TABLE_ROW_END = \"\\x0E\";\nconst TABLE_END = \"\\x0F\";\nconst LINK_START = \"\\x1F\";\nconst LINK_END = \"\\x1E\";\n\ninterface CustomRangeLike {\n startIndex?: number;\n endIndex?: number;\n rangeType?: number;\n properties?: { url?: unknown };\n}\n\n/** The Univer-native shapes (real tables, hyperlink ranges) written back as markdown. */\nfunction tableMarkdown(rows: string[][]): string[] {\n const width = Math.max(1, ...rows.map((r) => r.length));\n const cell = (r: string[], i: number) => (r[i] ?? \"\").replace(/^\\*\\*(.*)\\*\\*$/, \"$1\");\n const line = (r: string[], plain: boolean) =>\n `| ${Array.from({ length: width }, (_, i) => (plain ? cell(r, i) : (r[i] ?? \"\"))).join(\" | \")} |`;\n const out = [line(rows[0]!, true), `|${\" --- |\".repeat(width)}`];\n for (const r of rows.slice(1)) out.push(line(r, false));\n return out;\n}\n\ninterface TextRunLike {\n st?: number;\n ed?: number;\n ts?: {\n bl?: number;\n it?: number;\n ff?: string;\n fs?: number;\n st?: { s?: number } | number;\n };\n}\n\n/** Inverse of markdown-to-univer-doc's HEADING_FONT_SIZE. */\nexport const HEADING_LEVEL_BY_FONT_SIZE: Record<number, number> = {\n 26: 1,\n 22: 2,\n 18: 3,\n 16: 4,\n 14: 5,\n 13: 6,\n};\n\nfunction asArray<T>(value: unknown): T[] {\n return Array.isArray(value) ? (value as T[]) : [];\n}\n\nconst TRUE_ENOUGH = (v: unknown): boolean => v === 1 || v === true;\n\nfunction isStruck(ts: TextRunLike[\"ts\"]): boolean {\n if (!ts) return false;\n const st = ts.st;\n if (typeof st === \"number\") return TRUE_ENOUGH(st);\n return TRUE_ENOUGH(st?.s);\n}\n\n/** The style that applies to the whole paragraph, if the runs agree on one. */\nfunction paragraphHeadingLevel(runs: TextRunLike[]): number | null {\n for (const run of runs) {\n const fs = run.ts?.fs;\n if (typeof fs === \"number\" && TRUE_ENOUGH(run.ts?.bl)) {\n const level = HEADING_LEVEL_BY_FONT_SIZE[fs];\n if (level) return level;\n }\n }\n return null;\n}\n\nfunction emphasize(text: string, ts: TextRunLike[\"ts\"], inHeading: boolean): string {\n if (!text) return text;\n // Never double-mark: a heading's own runs are bold by construction.\n const leading = text.match(/^\\s*/)?.[0] ?? \"\";\n const trailing = text.match(/\\s*$/)?.[0] ?? \"\";\n let core = text.slice(leading.length, text.length - trailing.length);\n if (!core) return text;\n\n const mono = typeof ts?.ff === \"string\" && /mono/i.test(ts.ff);\n if (mono) core = `\\`${core}\\``;\n else {\n if (TRUE_ENOUGH(ts?.it)) core = `*${core}*`;\n if (TRUE_ENOUGH(ts?.bl) && !inHeading) core = `**${core}**`;\n }\n if (isStruck(ts)) core = `~~${core}~~`;\n return `${leading}${core}${trailing}`;\n}\n\n// An inline picture: one CUSTOM_BLOCK token in the stream, its drawing in `snapshot.drawings`.\nconst CUSTOM_BLOCK = \"\\b\";\n\ninterface DrawingLike {\n source?: unknown;\n title?: unknown;\n description?: unknown;\n docTransform?: { size?: { width?: unknown; height?: unknown } };\n}\n\n/**\n * A picture's markdown source. Our own pictures are `matrx-file:<id>`; the\n * drawn size rides as a `#WxH` fragment so a round trip keeps the shape.\n */\nexport function imageMarkdownSource(drawing: DrawingLike): string | null {\n const source = typeof drawing.source === \"string\" ? drawing.source : \"\";\n if (!source || source.startsWith(\"data:\")) return null;\n const w = Number(drawing.docTransform?.size?.width);\n const h = Number(drawing.docTransform?.size?.height);\n return Number.isFinite(w) && Number.isFinite(h) && w > 0 && h > 0 && !source.includes(\"#\")\n ? `${source}#${Math.round(w)}x${Math.round(h)}`\n : source;\n}\n\n/** Every picture in a snapshot, as its markdown source (`matrx-file:<id>#WxH` or a URL). */\nexport function documentImageSources(snapshot: unknown): string[] {\n const drawings = ((snapshot as { drawings?: unknown } | null)?.drawings ?? {}) as Record<string, DrawingLike>;\n const body = (snapshot as { body?: { customBlocks?: unknown } } | null)?.body;\n return asArray<{ blockId?: unknown }>(body?.customBlocks)\n .map((b) => (typeof b.blockId === \"string\" && drawings[b.blockId] ? imageMarkdownSource(drawings[b.blockId]!) : null))\n .filter((s): s is string => !!s);\n}\n\nexport interface DocToMarkdownOptions {\n /**\n * Where a picture's markdown points. Return null to leave it out (the caller\n * names what was left out). Default: the stored source.\n */\n imageUrl?: (source: string) => string | null;\n}\n\n/**\n * Read a Univer document snapshot back as markdown. Returns `\"\"` for an empty\n * or unreadable snapshot so the caller can say \"no source\" honestly rather than\n * printing an envelope.\n */\nexport function univerDocToMarkdown(snapshot: unknown, options: DocToMarkdownOptions = {}): string {\n if (!snapshot || typeof snapshot !== \"object\") return \"\";\n const body = (snapshot as { body?: unknown }).body;\n if (!body || typeof body !== \"object\") return \"\";\n\n const dataStream = (body as { dataStream?: unknown }).dataStream;\n if (typeof dataStream !== \"string\" || !dataStream) return \"\";\n\n // Pictures: each CUSTOM_BLOCK token is read back as `![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 += c;\n }\n return out;\n };\n\n const lines: string[] = [];\n let cursor = 0;\n let runIndex = 0;\n let tableRows: string[][] | null = null;\n\n // Paragraph boundaries are the `\\r`s in the stream itself — reading them from\n // the stream (rather than trusting `paragraphs`) keeps a snapshot written by\n // Univer's own editor, which may index differently, readable.\n for (let i = 0; i <= dataStream.length; i++) {\n const ch = dataStream[i];\n const atEnd = i === dataStream.length;\n if (!atEnd) {\n if (ch === TABLE_START) {\n tableRows = [];\n cursor = i + 1;\n continue;\n }\n if (tableRows && ch === TABLE_ROW_START) {\n tableRows.push([]);\n cursor = i + 1;\n continue;\n }\n if (tableRows && ch === TABLE_CELL_START) {\n tableRows[tableRows.length - 1]?.push(\"\");\n cursor = i + 1;\n continue;\n }\n if (tableRows && (ch === TABLE_CELL_END || ch === TABLE_ROW_END)) {\n cursor = i + 1;\n continue;\n }\n if (tableRows && ch === TABLE_END) {\n if (tableRows.length) lines.push(\"\", ...tableMarkdown(tableRows), \"\");\n tableRows = null;\n cursor = i + 1;\n continue;\n }\n }\n if (!atEnd && ch !== \"\\r\" && ch !== \"\\n\") continue;\n\n const start = cursor;\n const end = i;\n cursor = i + 1;\n if (end <= start) {\n if (ch === \"\\r\" && !tableRows) lines.push(\"\");\n if (atEnd) break;\n continue;\n }\n\n // Collect the runs overlapping this paragraph.\n const paragraphRuns: TextRunLike[] = [];\n let scan = runIndex;\n while (scan < runs.length && (runs[scan]!.st as number) < end) {\n if ((runs[scan]!.ed as number) > start) paragraphRuns.push(runs[scan]!);\n scan++;\n }\n while (\n runIndex < runs.length &&\n (runs[runIndex]!.ed as number) <= end\n ) {\n runIndex++;\n }\n\n const heading = paragraphHeadingLevel(paragraphRuns);\n let text = \"\";\n let at = start;\n for (const run of paragraphRuns) {\n const rs = Math.max(run.st as number, start);\n const re = Math.min(run.ed as number, end);\n if (rs > at) text += readSlice(at, rs);\n if (re > rs) {\n text += emphasize(readSlice(rs, re), run.ts, heading != null);\n }\n at = Math.max(at, re);\n }\n if (at < end) text += readSlice(at, end);\n\n if (tableRows) {\n // A table cell: its paragraphs join into the one cell, pipes escaped.\n const row = tableRows[tableRows.length - 1];\n if (row?.length) {\n const part = text.trim().replace(/\\|/g, \"\\\\|\");\n const last = row.length - 1;\n row[last] = row[last] ? `${row[last]} ${part}` : part;\n }\n } else {\n lines.push(heading ? `${\"#\".repeat(heading)} ${text.trim()}` : text);\n }\n if (atEnd) break;\n }\n\n // Univer keeps one paragraph per visual line; markdown wants a blank line\n // between blocks (but not inside a list or a table), and consecutive blank\n // paragraphs collapse. The writer (`markdownToUniverDoc`) flattens a list item\n // to a literal \"• \" / \"1. \" prefix and a table row to cells joined by\n // TABLE_CELL_SEP, so those shapes are read back as the markdown they came from.\n type Kind = \"text\" | \"list\" | \"table\";\n const kindOf = (line: string): Kind =>\n /^\\s*(?:\\u2022|[-*+]|\\d+[.)])\\s+/.test(line) ? \"list\" : line.includes(TABLE_CELL_SEP) ? \"table\" : \"text\";\n // A document typed or pasted as markdown keeps a table as literal `| a | b |` lines, one paragraph each, often\n // with an empty paragraph between rows: those rows are ONE table, so the gaps between them close.\n const rawRow = (l: string) => /^\\s*\\|.*\\|\\s*$/.test(l);\n const rows = lines.filter((l, i) => {\n if (l.trim()) return true;\n let a = i - 1;\n while (a >= 0 && !lines[a]!.trim()) a--;\n let z = i + 1;\n while (z < lines.length && !lines[z]!.trim()) z++;\n return !(a >= 0 && z < lines.length && rawRow(lines[a]!) && rawRow(lines[z]!));\n });\n const out: string[] = [];\n let prev: Kind | null = null;\n let tableRow = 0;\n for (const raw of rows) {\n if (!raw.trim()) {\n if (out.length && out[out.length - 1] !== \"\") out.push(\"\");\n prev = null;\n continue;\n }\n let line = raw.replace(/\\s+$/, \"\");\n if (rawRow(line)) {\n if (prev !== \"table\" && out.length && out[out.length - 1] !== \"\") out.push(\"\");\n out.push(line.trim());\n prev = \"table\";\n continue;\n }\n const kind = kindOf(line);\n if (kind === \"list\") {\n line = line.replace(/^(\\s*)\\u2022\\s+/, \"$1- \").replace(/^(\\s*)\\*\\s+/, \"$1- \");\n // Four spaces per nesting level in the document; two read as nesting in markdown.\n line = line.replace(/^( +)/, (m) => \" \".repeat(Math.floor(m.length / 2)));\n }\n if (kind === \"table\") {\n const cells = line.split(/\\s*\\|\\s*/).map((c) => c.trim());\n const isHeader = prev !== \"table\";\n const shown = cells.map((c) => (isHeader ? c.replace(/^\\*\\*(.*)\\*\\*$/, \"$1\") : c));\n line = `| ${shown.join(\" | \")} |`;\n if (isHeader) tableRow = shown.length;\n out.push(...(prev === \"table\" || prev === null || !out.length || out[out.length - 1] === \"\" ? [] : [\"\"]));\n out.push(line);\n if (isHeader) out.push(`|${\" --- |\".repeat(tableRow)}`);\n prev = \"table\";\n continue;\n }\n const tight = kind === \"list\" && prev === \"list\";\n if (out.length && out[out.length - 1] !== \"\" && !tight) out.push(\"\");\n out.push(line);\n prev = kind;\n }\n return out.join(\"\\n\").trim();\n}\n","/**\n * A file from the person's computer -> a new document (Word .docx, Markdown,\n * plain text, HTML). Everything lands as markdown first and goes through THE\n * one writer (`markdownToUniverDoc`), so headings, bold, italic, links and\n * lists keep their shape. Parse first: a file we cannot read never leaves an\n * empty document behind.\n */\n\"use client\";\n\nimport { readZip } from \"@ai-matrx/alchemy/operate/zip\";\n\nimport { createDocument, saveDocumentSnapshot, deleteDocument } from \"./document-service\";\nimport { deriveDocumentName, markdownToUniverDoc } from \"./markdown-to-univer-doc\";\nimport { sanitizeUniverDocSnapshot } from \"./sanitize-univer-doc-snapshot\";\n\nexport const DOCUMENT_UPLOAD_ACCEPT = \".docx,.md,.markdown,.txt,.html,.htm\";\n\ntype Source = \"imported_docx\" | \"imported_md\" | \"imported_txt\";\n\nexport function documentFileKind(name: string): \"docx\" | \"md\" | \"txt\" | \"html\" | null {\n const ext = name.toLowerCase().split(\".\").pop() ?? \"\";\n if (ext === \"docx\") return \"docx\";\n if (ext === \"md\" || ext === \"markdown\") return \"md\";\n if (ext === \"txt\") return \"txt\";\n if (ext === \"html\" || ext === \"htm\") return \"html\";\n return null;\n}\n\nconst W = \"http://schemas.openxmlformats.org/wordprocessingml/2006/main\";\n\n/** Word's document.xml -> markdown: headings, bold/italic runs, list paragraphs, tables as rows. */\nexport function docxXmlToMarkdown(xml: string): string {\n const doc = new DOMParser().parseFromString(xml, \"application/xml\");\n const body = doc.getElementsByTagNameNS(W, \"body\")[0];\n if (!body) throw new Error(\"This Word file has no document body.\");\n const attr = (el: Element | undefined, name: string) => el?.getAttributeNS(W, name) ?? el?.getAttribute(`w:${name}`) ?? null;\n const first = (el: Element, name: string) => el.getElementsByTagNameNS(W, name)[0] as Element | undefined;\n const on = (props: Element | undefined, name: string) => {\n const el = props ? first(props, name) : undefined;\n return !!el && attr(el, \"val\") !== \"0\" && attr(el, \"val\") !== \"false\";\n };\n const paragraph = (p: Element): string => {\n let text = \"\";\n for (const r of Array.from(p.getElementsByTagNameNS(W, \"r\"))) {\n const t = Array.from(r.childNodes)\n .map((n) => {\n const el = n as Element;\n if (el.localName === \"t\") return el.textContent ?? \"\";\n if (el.localName === \"tab\") return \"\\t\";\n if (el.localName === \"br\") return \" \\n\";\n return \"\";\n })\n .join(\"\");\n if (!t) continue;\n const rPr = first(r, \"rPr\");\n const bold = on(rPr, \"b\");\n const italic = on(rPr, \"i\");\n const lead = t.match(/^\\s*/)?.[0] ?? \"\";\n const trail = t.match(/\\s*$/)?.[0] ?? \"\";\n const core = t.trim();\n text += core ? `${lead}${bold ? \"**\" : \"\"}${italic ? \"*\" : \"\"}${core}${italic ? \"*\" : \"\"}${bold ? \"**\" : \"\"}${trail}` : t;\n }\n const pPr = first(p, \"pPr\");\n const style = attr(pPr ? first(pPr, \"pStyle\") : undefined, \"val\") ?? \"\";\n const heading = style.match(/^(?:Heading|heading)\\s?(\\d)$/)?.[1] ?? (style === \"Title\" ? \"1\" : null);\n if (heading && text.trim()) return `${\"#\".repeat(Math.min(6, Number(heading)))} ${text.replace(/\\*+/g, \"\").trim()}`;\n if (pPr && first(pPr, \"numPr\") && text.trim()) {\n const level = Number(attr(first(first(pPr, \"numPr\")!, \"ilvl\"), \"val\") ?? 0);\n return `${\" \".repeat(level)}- ${text.trim()}`;\n }\n return text;\n };\n const out: string[] = [];\n for (const child of Array.from(body.children)) {\n if (child.localName === \"p\") out.push(paragraph(child));\n else if (child.localName === \"tbl\") {\n const rows = Array.from(child.getElementsByTagNameNS(W, \"tr\")).map((tr) =>\n Array.from(tr.getElementsByTagNameNS(W, \"tc\")).map((tc) =>\n Array.from(tc.getElementsByTagNameNS(W, \"p\")).map(paragraph).join(\" \").replace(/\\|/g, \"\\\\|\").trim(),\n ),\n );\n if (rows.length) {\n out.push(\"\", `| ${rows[0]!.join(\" | \")} |`, `|${\" --- |\".repeat(rows[0]!.length)}`);\n for (const row of rows.slice(1)) out.push(`| ${row.join(\" | \")} |`);\n out.push(\"\");\n }\n }\n }\n // A list run stays tight; everything else is its own block.\n return out\n .reduce<string[]>((acc, line, i) => {\n const prevList = /^\\s*- /.test(out[i - 1] ?? \"\");\n if (i > 0 && !(prevList && /^\\s*- /.test(line))) acc.push(\"\");\n acc.push(line);\n return acc;\n }, [])\n .join(\"\\n\")\n .replace(/\\n{3,}/g, \"\\n\\n\")\n .trim();\n}\n\n/** HTML -> markdown for the blocks a document keeps (headings, paragraphs, lists, emphasis, links). */\nexport function htmlToDocumentMarkdown(html: string): string {\n const doc = new DOMParser().parseFromString(html, \"text/html\");\n const inline = (node: Node): string => {\n if (node.nodeType === 3) return (node.textContent ?? \"\").replace(/\\s+/g, \" \");\n if (node.nodeType !== 1) return \"\";\n const el = node as Element;\n const inner = Array.from(el.childNodes).map(inline).join(\"\");\n switch (el.tagName.toLowerCase()) {\n case \"strong\":\n case \"b\":\n return inner.trim() ? `**${inner.trim()}**` : \"\";\n case \"em\":\n case \"i\":\n return inner.trim() ? `*${inner.trim()}*` : \"\";\n case \"a\": {\n const href = el.getAttribute(\"href\");\n return href && /^https?:|^mailto:/i.test(href) ? `[${inner.trim()}](${href})` : inner;\n }\n case \"br\":\n return \" \\n\";\n case \"code\":\n return `\\`${inner}\\``;\n default:\n return inner;\n }\n };\n const out: string[] = [];\n const block = (el: Element, depth = 0): void => {\n const tag = el.tagName.toLowerCase();\n const h = tag.match(/^h([1-6])$/);\n if (h) {\n out.push(`${\"#\".repeat(Number(h[1]))} ${inline(el).trim()}`);\n return;\n }\n if (tag === \"p\" || tag === \"blockquote\") {\n out.push(inline(el).trim());\n return;\n }\n if (tag === \"pre\") {\n out.push(\"```\", el.textContent ?? \"\", \"```\");\n return;\n }\n if (tag === \"ul\" || tag === \"ol\") {\n Array.from(el.children).forEach((li, n) => {\n if (li.tagName.toLowerCase() !== \"li\") return;\n const own = Array.from(li.childNodes).filter((c) => !(c.nodeType === 1 && /^(ul|ol)$/i.test((c as Element).tagName)));\n out.push(`${\" \".repeat(depth)}${tag === \"ol\" ? `${n + 1}.` : \"-\"} ${own.map(inline).join(\"\").trim()}`);\n Array.from(li.children)\n .filter((c) => /^(ul|ol)$/i.test(c.tagName))\n .forEach((c) => block(c, depth + 1));\n });\n return;\n }\n if (tag === \"script\" || tag === \"style\" || tag === \"head\") return;\n const kids = Array.from(el.children);\n if (kids.length === 0) {\n const t = inline(el).trim();\n if (t) out.push(t);\n return;\n }\n kids.forEach((k) => block(k, depth));\n };\n block(doc.body);\n return out\n .map((l) => l.replace(/\\s+$/, \"\"))\n .reduce<string[]>((acc, line, i) => {\n const list = (s: string | undefined) => /^\\s*(-|\\d+\\.) /.test(s ?? \"\");\n if (i > 0 && !(list(out[i - 1]) && list(line))) acc.push(\"\");\n acc.push(line);\n return acc;\n }, [])\n .join(\"\\n\")\n .replace(/\\n{3,}/g, \"\\n\\n\")\n .trim();\n}\n\nexport async function documentFileToMarkdown(file: File): Promise<{ markdown: string; source: Source }> {\n const kind = documentFileKind(file.name);\n if (kind === \"docx\") {\n const zip = await readZip(file);\n const entry = zip.get(\"word/document.xml\");\n if (!entry) throw new Error(\"This is not a Word document (.docx).\");\n const xml = await entry.text();\n return { markdown: docxXmlToMarkdown(xml), source: \"imported_docx\" };\n }\n const text = await file.text();\n if (kind === \"md\") return { markdown: text, source: \"imported_md\" };\n if (kind === \"html\") return { markdown: htmlToDocumentMarkdown(text), source: \"imported_md\" };\n if (kind === \"txt\") {\n // Plain text: each line is its own paragraph; nothing in it is read as markdown.\n const escaped = text\n .replace(/\\r\\n?/g, \"\\n\")\n .split(\"\\n\")\n .map((l) => l.replace(/([\\\\`*_[\\]#>|~])/g, \"\\\\$1\"))\n .join(\"\\n\\n\");\n return { markdown: escaped, source: \"imported_txt\" };\n }\n throw new Error(\"Upload a Word (.docx), Markdown, text or HTML file.\");\n}\n\n/** Create a document from an uploaded file; returns its id. */\nexport async function importDocumentFile(file: File, organizationId: string): Promise<{ id: string; name: string }> {\n const { markdown, source } = await documentFileToMarkdown(file);\n const name = file.name.replace(/\\.[^.]+$/, \"\").trim() || deriveDocumentName(markdown);\n const created = await createDocument({ name, source, organizationId, description: `Imported from ${file.name}` });\n if (!created.success) throw new Error(`Could not create the document: ${created.error}`);\n const id = created.data.id;\n const snapshot = sanitizeUniverDocSnapshot(markdownToUniverDoc(markdown, name), id);\n const saved = await saveDocumentSnapshot({ documentId: id, snapshot, origin: \"imported\", label: `Imported from ${file.name}` });\n if (!saved.success) {\n await deleteDocument(id);\n throw new Error(`Could not save the imported content: ${saved.error}`);\n }\n return { id, name };\n}\n","/**\n * File → Download → EPUB / OpenDocument (.odt) / Rich Text (.rtf).\n * EPUB the print package's EPUB engine, straight from the live text.\n * ODT/RTF the same engine writes the .docx, then aidream's LibreOffice route\n * (`POST /office/convert-document`) converts it. Nothing is stored.\n * Engine, converter and saver are injected so the forcing test runs the real logic.\n */\n\nimport { getDocsHost } from \"./ports\";\n\nexport type OpenFormat = \"epub\" | \"odt\" | \"rtf\";\n\nexport const OPEN_FORMATS: Record<OpenFormat, { label: string; extension: string; mime: string }> = {\n epub: { label: \"EPUB\", extension: \"epub\", mime: \"application/epub+zip\" },\n odt: { label: \"OpenDocument\", extension: \"odt\", mime: \"application/vnd.oasis.opendocument.text\" },\n rtf: { label: \"Rich Text\", extension: \"rtf\", mime: \"application/rtf\" },\n};\n\nexport interface OpenFormatDeps {\n engine: (markdown: string, format: \"docx\" | \"epub\", fileName: string) => Promise<{ bytes: Uint8Array; fileName: string }>;\n convert: (docx: Uint8Array, fileName: string, target: \"odt\" | \"rtf\") => Promise<Uint8Array>;\n save: (fileName: string, bytes: Uint8Array, mime: string) => void;\n}\n\nfunction baseName(title: string | null | undefined): string {\n const base = (title ?? \"\").replace(/[\\\\/:*?\"<>|\\u0000-\\u001f]+/g, \" \").replace(/\\s+/g, \" \").trim().slice(0, 120);\n return base || \"document\";\n}\n\nexport async function exportOpenFormat(markdown: string, format: OpenFormat, title: string | null | undefined, deps: OpenFormatDeps): Promise<string> {\n const name = baseName(title);\n const spec = OPEN_FORMATS[format];\n const fileName = `${name}.${spec.extension}`;\n if (format === \"epub\") {\n const out = await deps.engine(markdown, \"epub\", name);\n deps.save(fileName, out.bytes, spec.mime);\n return fileName;\n }\n const docx = await deps.engine(markdown, \"docx\", name);\n const bytes = await deps.convert(docx.bytes, docx.fileName, format);\n if (!bytes.byteLength) throw new Error(`The ${spec.label} converter returned an empty file.`);\n deps.save(fileName, bytes, spec.mime);\n return fileName;\n}\n\nexport async function downloadOpenFormat(markdown: string, format: OpenFormat, title: string | null | undefined): Promise<string> {\n return exportOpenFormat(markdown, format, title, {\n engine: async (md, fmt, fileName) => {\n const { exportDocument } = await import(\"@ai-matrx/print/document\");\n const exp = await exportDocument(md, fmt, { fileName });\n return { bytes: exp.bytes, fileName: exp.fileName };\n },\n convert: async (docx, fileName, target) => {\n const form = new FormData();\n form.append(\"file\", new Blob([docx as BlobPart]), fileName.endsWith(\".docx\") ? fileName : `${fileName}.docx`);\n form.append(\"target\", target);\n const res = await getDocsHost().requestRaw(\"/office/convert-document\", { method: \"POST\", body: form });\n return new Uint8Array(await res.arrayBuffer());\n },\n save: (fileName, bytes, mime) => {\n const url = URL.createObjectURL(new Blob([bytes as BlobPart], { type: mime }));\n const link = document.createElement(\"a\");\n link.href = url;\n link.download = fileName;\n link.rel = \"noopener\";\n document.body.appendChild(link);\n link.click();\n link.remove();\n setTimeout(() => URL.revokeObjectURL(url), 30_000);\n },\n });\n}\n"]}
1
+ {"version":3,"sources":["/home/runner/work/aidream/aidream/apps/shared/office/dist/docs-io.cjs","../src/docs/univer-doc-to-markdown.ts","../src/docs/import-document-file.ts","../src/docs/export-open-formats.ts"],"names":["out"],"mappings":"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","file":"/home/runner/work/aidream/aidream/apps/shared/office/dist/docs-io.cjs","sourcesContent":[null,"/**\n * univerDocToMarkdown — the reverse of `markdown-to-univer-doc.ts`.\n *\n * Why it exists: the canvas pane for a cloud document (`udt_document`) offers a\n * `Source` tab, and the rule in `features/canvas/core/canvasSource.ts` is that\n * Source shows THE ITEM'S OWN SOURCE — for a document, its markdown — never the\n * redux envelope. A document's content lives as an opaque Univer\n * `IDocumentData` snapshot in `workbench.udt_document_snapshots`, so something\n * has to read it back out. This is that something, and it is the ONLY one:\n * never re-implement a second snapshot reader.\n *\n * What it reads (the half `markdownToUniverDoc` writes):\n * - `body.dataStream` — the text; `\\r` terminates a paragraph, a trailing\n * `\\n` terminates the section.\n * - `body.paragraphs` — one entry per `\\r` (`startIndex` = the `\\r` offset).\n * - `body.textRuns` — `{ st, ed, ts }` style overlays. `ts.bl` + a heading\n * font size is how `markdownToUniverDoc` encodes\n * `#`..`######`, so the level is recovered from `fs`.\n *\n * Fidelity is deliberately MODEST and honest: headings, bold, italic,\n * strikethrough and inline/monospace code round-trip; everything else comes\n * back as the plain text the user actually sees. A document typed by hand in\n * Univer never had markdown syntax in the first place — printing its text is\n * the truthful answer, not a reconstruction.\n *\n * PURE — no React, no Redux, no IO.\n */\n\nconst TABLE_CELL_SEP = \" | \";\n\n// Univer's DataStreamTreeTokenType control characters (literals: no Univer runtime here).\nconst TABLE_START = \"\\x1A\";\nconst TABLE_ROW_START = \"\\x1B\";\nconst TABLE_CELL_START = \"\\x1C\";\nconst TABLE_CELL_END = \"\\x1D\";\nconst TABLE_ROW_END = \"\\x0E\";\nconst TABLE_END = \"\\x0F\";\nconst LINK_START = \"\\x1F\";\nconst LINK_END = \"\\x1E\";\n\ninterface CustomRangeLike {\n startIndex?: number;\n endIndex?: number;\n rangeType?: number;\n properties?: { url?: unknown };\n}\n\n/** The Univer-native shapes (real tables, hyperlink ranges) written back as markdown. */\nfunction tableMarkdown(rows: string[][]): string[] {\n const width = Math.max(1, ...rows.map((r) => r.length));\n const cell = (r: string[], i: number) => (r[i] ?? \"\").replace(/^\\*\\*(.*)\\*\\*$/, \"$1\");\n const line = (r: string[], plain: boolean) =>\n `| ${Array.from({ length: width }, (_, i) => (plain ? cell(r, i) : (r[i] ?? \"\"))).join(\" | \")} |`;\n const out = [line(rows[0]!, true), `|${\" --- |\".repeat(width)}`];\n for (const r of rows.slice(1)) out.push(line(r, false));\n return out;\n}\n\ninterface TextRunLike {\n st?: number;\n ed?: number;\n ts?: {\n bl?: number;\n it?: number;\n ff?: string;\n fs?: number;\n st?: { s?: number } | number;\n };\n}\n\n/** Inverse of markdown-to-univer-doc's HEADING_FONT_SIZE. */\nexport const HEADING_LEVEL_BY_FONT_SIZE: Record<number, number> = {\n 26: 1,\n 22: 2,\n 18: 3,\n 16: 4,\n 14: 5,\n 13: 6,\n};\n\nfunction asArray<T>(value: unknown): T[] {\n return Array.isArray(value) ? (value as T[]) : [];\n}\n\nconst TRUE_ENOUGH = (v: unknown): boolean => v === 1 || v === true;\n\nfunction isStruck(ts: TextRunLike[\"ts\"]): boolean {\n if (!ts) return false;\n const st = ts.st;\n if (typeof st === \"number\") return TRUE_ENOUGH(st);\n return TRUE_ENOUGH(st?.s);\n}\n\n/** The style that applies to the whole paragraph, if the runs agree on one. */\nfunction paragraphHeadingLevel(runs: TextRunLike[]): number | null {\n for (const run of runs) {\n const fs = run.ts?.fs;\n if (typeof fs === \"number\" && TRUE_ENOUGH(run.ts?.bl)) {\n const level = HEADING_LEVEL_BY_FONT_SIZE[fs];\n if (level) return level;\n }\n }\n return null;\n}\n\nfunction emphasize(text: string, ts: TextRunLike[\"ts\"], inHeading: boolean): string {\n if (!text) return text;\n // Never double-mark: a heading's own runs are bold by construction.\n const leading = text.match(/^\\s*/)?.[0] ?? \"\";\n const trailing = text.match(/\\s*$/)?.[0] ?? \"\";\n let core = text.slice(leading.length, text.length - trailing.length);\n if (!core) return text;\n\n const mono = typeof ts?.ff === \"string\" && /mono/i.test(ts.ff);\n if (mono) core = `\\`${core}\\``;\n else {\n if (TRUE_ENOUGH(ts?.it)) core = `*${core}*`;\n if (TRUE_ENOUGH(ts?.bl) && !inHeading) core = `**${core}**`;\n }\n if (isStruck(ts)) core = `~~${core}~~`;\n return `${leading}${core}${trailing}`;\n}\n\n// An inline picture: one CUSTOM_BLOCK token in the stream, its drawing in `snapshot.drawings`.\nconst CUSTOM_BLOCK = \"\\b\";\n\ninterface DrawingLike {\n source?: unknown;\n title?: unknown;\n description?: unknown;\n docTransform?: { size?: { width?: unknown; height?: unknown } };\n}\n\n/**\n * A picture's markdown source. Our own pictures are `matrx-file:<id>`; the\n * drawn size rides as a `#WxH` fragment so a round trip keeps the shape.\n */\nexport function imageMarkdownSource(drawing: DrawingLike): string | null {\n const source = typeof drawing.source === \"string\" ? drawing.source : \"\";\n if (!source || source.startsWith(\"data:\")) return null;\n const w = Number(drawing.docTransform?.size?.width);\n const h = Number(drawing.docTransform?.size?.height);\n return Number.isFinite(w) && Number.isFinite(h) && w > 0 && h > 0 && !source.includes(\"#\")\n ? `${source}#${Math.round(w)}x${Math.round(h)}`\n : source;\n}\n\n/** Every picture in a snapshot, as its markdown source (`matrx-file:<id>#WxH` or a URL). */\nexport function documentImageSources(snapshot: unknown): string[] {\n const drawings = ((snapshot as { drawings?: unknown } | null)?.drawings ?? {}) as Record<string, DrawingLike>;\n const body = (snapshot as { body?: { customBlocks?: unknown } } | null)?.body;\n return asArray<{ blockId?: unknown }>(body?.customBlocks)\n .map((b) => (typeof b.blockId === \"string\" && drawings[b.blockId] ? imageMarkdownSource(drawings[b.blockId]!) : null))\n .filter((s): s is string => !!s);\n}\n\nexport interface DocToMarkdownOptions {\n /**\n * Where a picture's markdown points. Return null to leave it out (the caller\n * names what was left out). Default: the stored source.\n */\n imageUrl?: (source: string) => string | null;\n}\n\n/**\n * Read a Univer document snapshot back as markdown. Returns `\"\"` for an empty\n * or unreadable snapshot so the caller can say \"no source\" honestly rather than\n * printing an envelope.\n */\nexport function univerDocToMarkdown(snapshot: unknown, options: DocToMarkdownOptions = {}): string {\n if (!snapshot || typeof snapshot !== \"object\") return \"\";\n const body = (snapshot as { body?: unknown }).body;\n if (!body || typeof body !== \"object\") return \"\";\n\n const dataStream = (body as { dataStream?: unknown }).dataStream;\n if (typeof dataStream !== \"string\" || !dataStream) return \"\";\n\n // Pictures: each CUSTOM_BLOCK token is read back as `![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 += c;\n }\n return out;\n };\n\n const lines: string[] = [];\n let cursor = 0;\n let runIndex = 0;\n let tableRows: string[][] | null = null;\n\n // Paragraph boundaries are the `\\r`s in the stream itself — reading them from\n // the stream (rather than trusting `paragraphs`) keeps a snapshot written by\n // Univer's own editor, which may index differently, readable.\n for (let i = 0; i <= dataStream.length; i++) {\n const ch = dataStream[i];\n const atEnd = i === dataStream.length;\n if (!atEnd) {\n if (ch === TABLE_START) {\n tableRows = [];\n cursor = i + 1;\n continue;\n }\n if (tableRows && ch === TABLE_ROW_START) {\n tableRows.push([]);\n cursor = i + 1;\n continue;\n }\n if (tableRows && ch === TABLE_CELL_START) {\n tableRows[tableRows.length - 1]?.push(\"\");\n cursor = i + 1;\n continue;\n }\n if (tableRows && (ch === TABLE_CELL_END || ch === TABLE_ROW_END)) {\n cursor = i + 1;\n continue;\n }\n if (tableRows && ch === TABLE_END) {\n if (tableRows.length) lines.push(\"\", ...tableMarkdown(tableRows), \"\");\n tableRows = null;\n cursor = i + 1;\n continue;\n }\n }\n if (!atEnd && ch !== \"\\r\" && ch !== \"\\n\") continue;\n\n const start = cursor;\n const end = i;\n cursor = i + 1;\n if (end <= start) {\n if (ch === \"\\r\" && !tableRows) lines.push(\"\");\n if (atEnd) break;\n continue;\n }\n\n // Collect the runs overlapping this paragraph.\n const paragraphRuns: TextRunLike[] = [];\n let scan = runIndex;\n while (scan < runs.length && (runs[scan]!.st as number) < end) {\n if ((runs[scan]!.ed as number) > start) paragraphRuns.push(runs[scan]!);\n scan++;\n }\n while (\n runIndex < runs.length &&\n (runs[runIndex]!.ed as number) <= end\n ) {\n runIndex++;\n }\n\n const heading = paragraphHeadingLevel(paragraphRuns);\n let text = \"\";\n let at = start;\n for (const run of paragraphRuns) {\n const rs = Math.max(run.st as number, start);\n const re = Math.min(run.ed as number, end);\n if (rs > at) text += readSlice(at, rs);\n if (re > rs) {\n text += emphasize(readSlice(rs, re), run.ts, heading != null);\n }\n at = Math.max(at, re);\n }\n if (at < end) text += readSlice(at, end);\n\n if (tableRows) {\n // A table cell: its paragraphs join into the one cell, pipes escaped.\n const row = tableRows[tableRows.length - 1];\n if (row?.length) {\n const part = text.trim().replace(/\\|/g, \"\\\\|\");\n const last = row.length - 1;\n row[last] = row[last] ? `${row[last]} ${part}` : part;\n }\n } else {\n lines.push(heading ? `${\"#\".repeat(heading)} ${text.trim()}` : text);\n }\n if (atEnd) break;\n }\n\n // Univer keeps one paragraph per visual line; markdown wants a blank line\n // between blocks (but not inside a list or a table), and consecutive blank\n // paragraphs collapse. The writer (`markdownToUniverDoc`) flattens a list item\n // to a literal \"• \" / \"1. \" prefix and a table row to cells joined by\n // TABLE_CELL_SEP, so those shapes are read back as the markdown they came from.\n type Kind = \"text\" | \"list\" | \"table\";\n const kindOf = (line: string): Kind =>\n /^\\s*(?:\\u2022|[-*+]|\\d+[.)])\\s+/.test(line) ? \"list\" : line.includes(TABLE_CELL_SEP) ? \"table\" : \"text\";\n // A document typed or pasted as markdown keeps a table as literal `| a | b |` lines, one paragraph each, often\n // with an empty paragraph between rows: those rows are ONE table, so the gaps between them close.\n const rawRow = (l: string) => /^\\s*\\|.*\\|\\s*$/.test(l);\n const rows = lines.filter((l, i) => {\n if (l.trim()) return true;\n let a = i - 1;\n while (a >= 0 && !lines[a]!.trim()) a--;\n let z = i + 1;\n while (z < lines.length && !lines[z]!.trim()) z++;\n return !(a >= 0 && z < lines.length && rawRow(lines[a]!) && rawRow(lines[z]!));\n });\n const out: string[] = [];\n let prev: Kind | null = null;\n let tableRow = 0;\n for (const raw of rows) {\n if (!raw.trim()) {\n if (out.length && out[out.length - 1] !== \"\") out.push(\"\");\n prev = null;\n continue;\n }\n let line = raw.replace(/\\s+$/, \"\");\n if (rawRow(line)) {\n if (prev !== \"table\" && out.length && out[out.length - 1] !== \"\") out.push(\"\");\n out.push(line.trim());\n prev = \"table\";\n continue;\n }\n const kind = kindOf(line);\n if (kind === \"list\") {\n line = line.replace(/^(\\s*)\\u2022\\s+/, \"$1- \").replace(/^(\\s*)\\*\\s+/, \"$1- \");\n // Four spaces per nesting level in the document; two read as nesting in markdown.\n line = line.replace(/^( +)/, (m) => \" \".repeat(Math.floor(m.length / 2)));\n }\n if (kind === \"table\") {\n const cells = line.split(/\\s*\\|\\s*/).map((c) => c.trim());\n const isHeader = prev !== \"table\";\n const shown = cells.map((c) => (isHeader ? c.replace(/^\\*\\*(.*)\\*\\*$/, \"$1\") : c));\n line = `| ${shown.join(\" | \")} |`;\n if (isHeader) tableRow = shown.length;\n out.push(...(prev === \"table\" || prev === null || !out.length || out[out.length - 1] === \"\" ? [] : [\"\"]));\n out.push(line);\n if (isHeader) out.push(`|${\" --- |\".repeat(tableRow)}`);\n prev = \"table\";\n continue;\n }\n const tight = kind === \"list\" && prev === \"list\";\n if (out.length && out[out.length - 1] !== \"\" && !tight) out.push(\"\");\n out.push(line);\n prev = kind;\n }\n return out.join(\"\\n\").trim();\n}\n","/**\n * A file from the person's computer -> a new document (Word .docx, Markdown,\n * plain text, HTML). Everything lands as markdown first and goes through THE\n * one writer (`markdownToUniverDoc`), so headings, bold, italic, links and\n * lists keep their shape. Parse first: a file we cannot read never leaves an\n * empty document behind.\n */\n\"use client\";\n\nimport { readZip } from \"@ai-matrx/alchemy/operate/zip\";\n\nimport { createDocument, saveDocumentSnapshot, deleteDocument } from \"./document-service\";\nimport { deriveDocumentName, markdownToUniverDoc } from \"./markdown-to-univer-doc\";\nimport { sanitizeUniverDocSnapshot } from \"./sanitize-univer-doc-snapshot\";\n\nexport const DOCUMENT_UPLOAD_ACCEPT = \".docx,.md,.markdown,.txt,.html,.htm\";\n\ntype Source = \"imported_docx\" | \"imported_md\" | \"imported_txt\";\n\nexport function documentFileKind(name: string): \"docx\" | \"md\" | \"txt\" | \"html\" | null {\n const ext = name.toLowerCase().split(\".\").pop() ?? \"\";\n if (ext === \"docx\") return \"docx\";\n if (ext === \"md\" || ext === \"markdown\") return \"md\";\n if (ext === \"txt\") return \"txt\";\n if (ext === \"html\" || ext === \"htm\") return \"html\";\n return null;\n}\n\nconst W = \"http://schemas.openxmlformats.org/wordprocessingml/2006/main\";\n\n/** Rows of cell text -> ONE markdown table block (header, delimiter, body rows together). */\nfunction markdownTable(rows: string[][]): string {\n const width = Math.max(...rows.map((r) => r.length));\n const line = (r: string[]) => `| ${Array.from({ length: width }, (_, i) => r[i] ?? \"\").join(\" | \")} |`;\n return [line(rows[0]!), `|${\" --- |\".repeat(width)}`, ...rows.slice(1).map(line)].join(\"\\n\");\n}\n\n/** Word's document.xml -> markdown: headings, bold/italic runs, list paragraphs, tables as rows. */\nexport function docxXmlToMarkdown(xml: string): string {\n const doc = new DOMParser().parseFromString(xml, \"application/xml\");\n const body = doc.getElementsByTagNameNS(W, \"body\")[0];\n if (!body) throw new Error(\"This Word file has no document body.\");\n const attr = (el: Element | undefined, name: string) => el?.getAttributeNS(W, name) ?? el?.getAttribute(`w:${name}`) ?? null;\n const first = (el: Element, name: string) => el.getElementsByTagNameNS(W, name)[0] as Element | undefined;\n const on = (props: Element | undefined, name: string) => {\n const el = props ? first(props, name) : undefined;\n return !!el && attr(el, \"val\") !== \"0\" && attr(el, \"val\") !== \"false\";\n };\n const paragraph = (p: Element): string => {\n let text = \"\";\n for (const r of Array.from(p.getElementsByTagNameNS(W, \"r\"))) {\n const t = Array.from(r.childNodes)\n .map((n) => {\n const el = n as Element;\n if (el.localName === \"t\") return el.textContent ?? \"\";\n if (el.localName === \"tab\") return \"\\t\";\n if (el.localName === \"br\") return \" \\n\";\n return \"\";\n })\n .join(\"\");\n if (!t) continue;\n const rPr = first(r, \"rPr\");\n const bold = on(rPr, \"b\");\n const italic = on(rPr, \"i\");\n const lead = t.match(/^\\s*/)?.[0] ?? \"\";\n const trail = t.match(/\\s*$/)?.[0] ?? \"\";\n const core = t.trim();\n text += core ? `${lead}${bold ? \"**\" : \"\"}${italic ? \"*\" : \"\"}${core}${italic ? \"*\" : \"\"}${bold ? \"**\" : \"\"}${trail}` : t;\n }\n const pPr = first(p, \"pPr\");\n const style = attr(pPr ? first(pPr, \"pStyle\") : undefined, \"val\") ?? \"\";\n const heading = style.match(/^(?:Heading|heading)\\s?(\\d)$/)?.[1] ?? (style === \"Title\" ? \"1\" : null);\n if (heading && text.trim()) return `${\"#\".repeat(Math.min(6, Number(heading)))} ${text.replace(/\\*+/g, \"\").trim()}`;\n if (pPr && first(pPr, \"numPr\") && text.trim()) {\n const level = Number(attr(first(first(pPr, \"numPr\")!, \"ilvl\"), \"val\") ?? 0);\n return `${\" \".repeat(level)}- ${text.trim()}`;\n }\n return text;\n };\n const out: string[] = [];\n for (const child of Array.from(body.children)) {\n if (child.localName === \"p\") out.push(paragraph(child));\n else if (child.localName === \"tbl\") {\n const rows = Array.from(child.getElementsByTagNameNS(W, \"tr\")).map((tr) =>\n Array.from(tr.getElementsByTagNameNS(W, \"tc\")).map((tc) =>\n Array.from(tc.getElementsByTagNameNS(W, \"p\")).map(paragraph).join(\" \").replace(/\\|/g, \"\\\\|\").trim(),\n ),\n );\n if (rows.length) out.push(markdownTable(rows));\n }\n }\n // A list run stays tight; everything else is its own block.\n return out\n .reduce<string[]>((acc, line, i) => {\n const prevList = /^\\s*- /.test(out[i - 1] ?? \"\");\n if (i > 0 && !(prevList && /^\\s*- /.test(line))) acc.push(\"\");\n acc.push(line);\n return acc;\n }, [])\n .join(\"\\n\")\n .replace(/\\n{3,}/g, \"\\n\\n\")\n .trim();\n}\n\n/** HTML -> markdown for the blocks a document keeps (headings, paragraphs, lists, emphasis, links). */\nexport function htmlToDocumentMarkdown(html: string): string {\n const doc = new DOMParser().parseFromString(html, \"text/html\");\n const inline = (node: Node): string => {\n if (node.nodeType === 3) return (node.textContent ?? \"\").replace(/\\s+/g, \" \");\n if (node.nodeType !== 1) return \"\";\n const el = node as Element;\n const inner = Array.from(el.childNodes).map(inline).join(\"\");\n switch (el.tagName.toLowerCase()) {\n case \"strong\":\n case \"b\":\n return inner.trim() ? `**${inner.trim()}**` : \"\";\n case \"em\":\n case \"i\":\n return inner.trim() ? `*${inner.trim()}*` : \"\";\n case \"a\": {\n const href = el.getAttribute(\"href\");\n return href && /^https?:|^mailto:/i.test(href) ? `[${inner.trim()}](${href})` : inner;\n }\n case \"br\":\n return \" \\n\";\n case \"code\":\n return `\\`${inner}\\``;\n default:\n return inner;\n }\n };\n const out: string[] = [];\n const block = (el: Element, depth = 0): void => {\n const tag = el.tagName.toLowerCase();\n const h = tag.match(/^h([1-6])$/);\n if (h) {\n out.push(`${\"#\".repeat(Number(h[1]))} ${inline(el).trim()}`);\n return;\n }\n if (tag === \"p\" || tag === \"blockquote\") {\n out.push(inline(el).trim());\n return;\n }\n if (tag === \"table\") {\n const rows = Array.from(el.querySelectorAll(\"tr\")).map((tr) =>\n Array.from(tr.children)\n .filter((c) => /^(td|th)$/i.test(c.tagName))\n .map((c) => inline(c).replace(/\\|/g, \"\\\\|\").replace(/\\s+/g, \" \").trim()),\n );\n if (rows.length) out.push(markdownTable(rows));\n return;\n }\n if (tag === \"pre\") {\n out.push(\"```\", el.textContent ?? \"\", \"```\");\n return;\n }\n if (tag === \"ul\" || tag === \"ol\") {\n Array.from(el.children).forEach((li, n) => {\n if (li.tagName.toLowerCase() !== \"li\") return;\n const own = Array.from(li.childNodes).filter((c) => !(c.nodeType === 1 && /^(ul|ol)$/i.test((c as Element).tagName)));\n out.push(`${\" \".repeat(depth)}${tag === \"ol\" ? `${n + 1}.` : \"-\"} ${own.map(inline).join(\"\").trim()}`);\n Array.from(li.children)\n .filter((c) => /^(ul|ol)$/i.test(c.tagName))\n .forEach((c) => block(c, depth + 1));\n });\n return;\n }\n if (tag === \"script\" || tag === \"style\" || tag === \"head\") return;\n const kids = Array.from(el.children);\n if (kids.length === 0) {\n const t = inline(el).trim();\n if (t) out.push(t);\n return;\n }\n kids.forEach((k) => block(k, depth));\n };\n block(doc.body);\n return out\n .map((l) => l.replace(/\\s+$/, \"\"))\n .reduce<string[]>((acc, line, i) => {\n const list = (s: string | undefined) => /^\\s*(-|\\d+\\.) /.test(s ?? \"\");\n if (i > 0 && !(list(out[i - 1]) && list(line))) acc.push(\"\");\n acc.push(line);\n return acc;\n }, [])\n .join(\"\\n\")\n .replace(/\\n{3,}/g, \"\\n\\n\")\n .trim();\n}\n\nexport async function documentFileToMarkdown(file: File): Promise<{ markdown: string; source: Source }> {\n const kind = documentFileKind(file.name);\n if (kind === \"docx\") {\n const zip = await readZip(file);\n const entry = zip.get(\"word/document.xml\");\n if (!entry) throw new Error(\"This is not a Word document (.docx).\");\n const xml = await entry.text();\n return { markdown: docxXmlToMarkdown(xml), source: \"imported_docx\" };\n }\n const text = await file.text();\n if (kind === \"md\") return { markdown: text, source: \"imported_md\" };\n if (kind === \"html\") return { markdown: htmlToDocumentMarkdown(text), source: \"imported_md\" };\n if (kind === \"txt\") {\n // Plain text: each line is its own paragraph; nothing in it is read as markdown.\n const escaped = text\n .replace(/\\r\\n?/g, \"\\n\")\n .split(\"\\n\")\n .map((l) => l.replace(/([\\\\`*_[\\]#>|~])/g, \"\\\\$1\"))\n .join(\"\\n\\n\");\n return { markdown: escaped, source: \"imported_txt\" };\n }\n throw new Error(\"Upload a Word (.docx), Markdown, text or HTML file.\");\n}\n\n/** Create a document from an uploaded file; returns its id. */\nexport async function importDocumentFile(file: File, organizationId: string): Promise<{ id: string; name: string }> {\n const { markdown, source } = await documentFileToMarkdown(file);\n const name = file.name.replace(/\\.[^.]+$/, \"\").trim() || deriveDocumentName(markdown);\n const created = await createDocument({ name, source, organizationId, description: `Imported from ${file.name}` });\n if (!created.success) throw new Error(`Could not create the document: ${created.error}`);\n const id = created.data.id;\n const snapshot = sanitizeUniverDocSnapshot(markdownToUniverDoc(markdown, name), id);\n const saved = await saveDocumentSnapshot({ documentId: id, snapshot, origin: \"imported\", label: `Imported from ${file.name}` });\n if (!saved.success) {\n await deleteDocument(id);\n throw new Error(`Could not save the imported content: ${saved.error}`);\n }\n return { id, name };\n}\n","/**\n * File → Download → EPUB / OpenDocument (.odt) / Rich Text (.rtf).\n * EPUB the print package's EPUB engine, straight from the live text.\n * ODT/RTF the same engine writes the .docx, then aidream's LibreOffice route\n * (`POST /office/convert-document`) converts it. Nothing is stored.\n * Engine, converter and saver are injected so the forcing test runs the real logic.\n */\n\nimport { getDocsHost } from \"./ports\";\n\nexport type OpenFormat = \"epub\" | \"odt\" | \"rtf\";\n\nexport const OPEN_FORMATS: Record<OpenFormat, { label: string; extension: string; mime: string }> = {\n epub: { label: \"EPUB\", extension: \"epub\", mime: \"application/epub+zip\" },\n odt: { label: \"OpenDocument\", extension: \"odt\", mime: \"application/vnd.oasis.opendocument.text\" },\n rtf: { label: \"Rich Text\", extension: \"rtf\", mime: \"application/rtf\" },\n};\n\nexport interface OpenFormatDeps {\n engine: (markdown: string, format: \"docx\" | \"epub\", fileName: string) => Promise<{ bytes: Uint8Array; fileName: string }>;\n convert: (docx: Uint8Array, fileName: string, target: \"odt\" | \"rtf\") => Promise<Uint8Array>;\n save: (fileName: string, bytes: Uint8Array, mime: string) => void;\n}\n\nfunction baseName(title: string | null | undefined): string {\n const base = (title ?? \"\").replace(/[\\\\/:*?\"<>|\\u0000-\\u001f]+/g, \" \").replace(/\\s+/g, \" \").trim().slice(0, 120);\n return base || \"document\";\n}\n\nexport async function exportOpenFormat(markdown: string, format: OpenFormat, title: string | null | undefined, deps: OpenFormatDeps): Promise<string> {\n const name = baseName(title);\n const spec = OPEN_FORMATS[format];\n const fileName = `${name}.${spec.extension}`;\n if (format === \"epub\") {\n const out = await deps.engine(markdown, \"epub\", name);\n deps.save(fileName, out.bytes, spec.mime);\n return fileName;\n }\n const docx = await deps.engine(markdown, \"docx\", name);\n const bytes = await deps.convert(docx.bytes, docx.fileName, format);\n if (!bytes.byteLength) throw new Error(`The ${spec.label} converter returned an empty file.`);\n deps.save(fileName, bytes, spec.mime);\n return fileName;\n}\n\nexport async function downloadOpenFormat(markdown: string, format: OpenFormat, title: string | null | undefined): Promise<string> {\n return exportOpenFormat(markdown, format, title, {\n engine: async (md, fmt, fileName) => {\n const { exportDocument } = await import(\"@ai-matrx/print/document\");\n const exp = await exportDocument(md, fmt, { fileName });\n return { bytes: exp.bytes, fileName: exp.fileName };\n },\n convert: async (docx, fileName, target) => {\n const form = new FormData();\n form.append(\"file\", new Blob([docx as BlobPart]), fileName.endsWith(\".docx\") ? fileName : `${fileName}.docx`);\n form.append(\"target\", target);\n const res = await getDocsHost().requestRaw(\"/office/convert-document\", { method: \"POST\", body: form });\n return new Uint8Array(await res.arrayBuffer());\n },\n save: (fileName, bytes, mime) => {\n const url = URL.createObjectURL(new Blob([bytes as BlobPart], { type: mime }));\n const link = document.createElement(\"a\");\n link.href = url;\n link.download = fileName;\n link.rel = \"noopener\";\n document.body.appendChild(link);\n link.click();\n link.remove();\n setTimeout(() => URL.revokeObjectURL(url), 30_000);\n },\n });\n}\n"]}
package/dist/docs-io.js CHANGED
@@ -4,7 +4,7 @@ import {
4
4
  getDocsHost,
5
5
  sanitizeUniverDocSnapshot,
6
6
  saveDocumentSnapshot
7
- } from "./chunk-XMM62UAT.js";
7
+ } from "./chunk-5JAZ32SX.js";
8
8
  import {
9
9
  deriveDocumentName,
10
10
  markdownToUniverDoc
@@ -260,6 +260,11 @@ function documentFileKind(name) {
260
260
  return null;
261
261
  }
262
262
  var W = "http://schemas.openxmlformats.org/wordprocessingml/2006/main";
263
+ function markdownTable(rows) {
264
+ const width = Math.max(...rows.map((r) => r.length));
265
+ const line = (r) => `| ${Array.from({ length: width }, (_, i) => r[i] ?? "").join(" | ")} |`;
266
+ return [line(rows[0]), `|${" --- |".repeat(width)}`, ...rows.slice(1).map(line)].join("\n");
267
+ }
263
268
  function docxXmlToMarkdown(xml) {
264
269
  const doc = new DOMParser().parseFromString(xml, "application/xml");
265
270
  const body = doc.getElementsByTagNameNS(W, "body")[0];
@@ -308,11 +313,7 @@ function docxXmlToMarkdown(xml) {
308
313
  (tc) => Array.from(tc.getElementsByTagNameNS(W, "p")).map(paragraph).join(" ").replace(/\|/g, "\\|").trim()
309
314
  )
310
315
  );
311
- if (rows.length) {
312
- out.push("", `| ${rows[0].join(" | ")} |`, `|${" --- |".repeat(rows[0].length)}`);
313
- for (const row of rows.slice(1)) out.push(`| ${row.join(" | ")} |`);
314
- out.push("");
315
- }
316
+ if (rows.length) out.push(markdownTable(rows));
316
317
  }
317
318
  }
318
319
  return out.reduce((acc, line, i) => {
@@ -360,6 +361,13 @@ function htmlToDocumentMarkdown(html) {
360
361
  out.push(inline(el).trim());
361
362
  return;
362
363
  }
364
+ if (tag === "table") {
365
+ const rows = Array.from(el.querySelectorAll("tr")).map(
366
+ (tr) => Array.from(tr.children).filter((c) => /^(td|th)$/i.test(c.tagName)).map((c) => inline(c).replace(/\|/g, "\\|").replace(/\s+/g, " ").trim())
367
+ );
368
+ if (rows.length) out.push(markdownTable(rows));
369
+ return;
370
+ }
363
371
  if (tag === "pre") {
364
372
  out.push("```", el.textContent ?? "", "```");
365
373
  return;
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/docs/univer-doc-to-markdown.ts","../src/docs/import-document-file.ts","../src/docs/export-open-formats.ts"],"sourcesContent":["/**\n * univerDocToMarkdown — the reverse of `markdown-to-univer-doc.ts`.\n *\n * Why it exists: the canvas pane for a cloud document (`udt_document`) offers a\n * `Source` tab, and the rule in `features/canvas/core/canvasSource.ts` is that\n * Source shows THE ITEM'S OWN SOURCE — for a document, its markdown — never the\n * redux envelope. A document's content lives as an opaque Univer\n * `IDocumentData` snapshot in `workbench.udt_document_snapshots`, so something\n * has to read it back out. This is that something, and it is the ONLY one:\n * never re-implement a second snapshot reader.\n *\n * What it reads (the half `markdownToUniverDoc` writes):\n * - `body.dataStream` — the text; `\\r` terminates a paragraph, a trailing\n * `\\n` terminates the section.\n * - `body.paragraphs` — one entry per `\\r` (`startIndex` = the `\\r` offset).\n * - `body.textRuns` — `{ st, ed, ts }` style overlays. `ts.bl` + a heading\n * font size is how `markdownToUniverDoc` encodes\n * `#`..`######`, so the level is recovered from `fs`.\n *\n * Fidelity is deliberately MODEST and honest: headings, bold, italic,\n * strikethrough and inline/monospace code round-trip; everything else comes\n * back as the plain text the user actually sees. A document typed by hand in\n * Univer never had markdown syntax in the first place — printing its text is\n * the truthful answer, not a reconstruction.\n *\n * PURE — no React, no Redux, no IO.\n */\n\nconst TABLE_CELL_SEP = \" | \";\n\n// Univer's DataStreamTreeTokenType control characters (literals: no Univer runtime here).\nconst TABLE_START = \"\\x1A\";\nconst TABLE_ROW_START = \"\\x1B\";\nconst TABLE_CELL_START = \"\\x1C\";\nconst TABLE_CELL_END = \"\\x1D\";\nconst TABLE_ROW_END = \"\\x0E\";\nconst TABLE_END = \"\\x0F\";\nconst LINK_START = \"\\x1F\";\nconst LINK_END = \"\\x1E\";\n\ninterface CustomRangeLike {\n startIndex?: number;\n endIndex?: number;\n rangeType?: number;\n properties?: { url?: unknown };\n}\n\n/** The Univer-native shapes (real tables, hyperlink ranges) written back as markdown. */\nfunction tableMarkdown(rows: string[][]): string[] {\n const width = Math.max(1, ...rows.map((r) => r.length));\n const cell = (r: string[], i: number) => (r[i] ?? \"\").replace(/^\\*\\*(.*)\\*\\*$/, \"$1\");\n const line = (r: string[], plain: boolean) =>\n `| ${Array.from({ length: width }, (_, i) => (plain ? cell(r, i) : (r[i] ?? \"\"))).join(\" | \")} |`;\n const out = [line(rows[0]!, true), `|${\" --- |\".repeat(width)}`];\n for (const r of rows.slice(1)) out.push(line(r, false));\n return out;\n}\n\ninterface TextRunLike {\n st?: number;\n ed?: number;\n ts?: {\n bl?: number;\n it?: number;\n ff?: string;\n fs?: number;\n st?: { s?: number } | number;\n };\n}\n\n/** Inverse of markdown-to-univer-doc's HEADING_FONT_SIZE. */\nexport const HEADING_LEVEL_BY_FONT_SIZE: Record<number, number> = {\n 26: 1,\n 22: 2,\n 18: 3,\n 16: 4,\n 14: 5,\n 13: 6,\n};\n\nfunction asArray<T>(value: unknown): T[] {\n return Array.isArray(value) ? (value as T[]) : [];\n}\n\nconst TRUE_ENOUGH = (v: unknown): boolean => v === 1 || v === true;\n\nfunction isStruck(ts: TextRunLike[\"ts\"]): boolean {\n if (!ts) return false;\n const st = ts.st;\n if (typeof st === \"number\") return TRUE_ENOUGH(st);\n return TRUE_ENOUGH(st?.s);\n}\n\n/** The style that applies to the whole paragraph, if the runs agree on one. */\nfunction paragraphHeadingLevel(runs: TextRunLike[]): number | null {\n for (const run of runs) {\n const fs = run.ts?.fs;\n if (typeof fs === \"number\" && TRUE_ENOUGH(run.ts?.bl)) {\n const level = HEADING_LEVEL_BY_FONT_SIZE[fs];\n if (level) return level;\n }\n }\n return null;\n}\n\nfunction emphasize(text: string, ts: TextRunLike[\"ts\"], inHeading: boolean): string {\n if (!text) return text;\n // Never double-mark: a heading's own runs are bold by construction.\n const leading = text.match(/^\\s*/)?.[0] ?? \"\";\n const trailing = text.match(/\\s*$/)?.[0] ?? \"\";\n let core = text.slice(leading.length, text.length - trailing.length);\n if (!core) return text;\n\n const mono = typeof ts?.ff === \"string\" && /mono/i.test(ts.ff);\n if (mono) core = `\\`${core}\\``;\n else {\n if (TRUE_ENOUGH(ts?.it)) core = `*${core}*`;\n if (TRUE_ENOUGH(ts?.bl) && !inHeading) core = `**${core}**`;\n }\n if (isStruck(ts)) core = `~~${core}~~`;\n return `${leading}${core}${trailing}`;\n}\n\n// An inline picture: one CUSTOM_BLOCK token in the stream, its drawing in `snapshot.drawings`.\nconst CUSTOM_BLOCK = \"\\b\";\n\ninterface DrawingLike {\n source?: unknown;\n title?: unknown;\n description?: unknown;\n docTransform?: { size?: { width?: unknown; height?: unknown } };\n}\n\n/**\n * A picture's markdown source. Our own pictures are `matrx-file:<id>`; the\n * drawn size rides as a `#WxH` fragment so a round trip keeps the shape.\n */\nexport function imageMarkdownSource(drawing: DrawingLike): string | null {\n const source = typeof drawing.source === \"string\" ? drawing.source : \"\";\n if (!source || source.startsWith(\"data:\")) return null;\n const w = Number(drawing.docTransform?.size?.width);\n const h = Number(drawing.docTransform?.size?.height);\n return Number.isFinite(w) && Number.isFinite(h) && w > 0 && h > 0 && !source.includes(\"#\")\n ? `${source}#${Math.round(w)}x${Math.round(h)}`\n : source;\n}\n\n/** Every picture in a snapshot, as its markdown source (`matrx-file:<id>#WxH` or a URL). */\nexport function documentImageSources(snapshot: unknown): string[] {\n const drawings = ((snapshot as { drawings?: unknown } | null)?.drawings ?? {}) as Record<string, DrawingLike>;\n const body = (snapshot as { body?: { customBlocks?: unknown } } | null)?.body;\n return asArray<{ blockId?: unknown }>(body?.customBlocks)\n .map((b) => (typeof b.blockId === \"string\" && drawings[b.blockId] ? imageMarkdownSource(drawings[b.blockId]!) : null))\n .filter((s): s is string => !!s);\n}\n\nexport interface DocToMarkdownOptions {\n /**\n * Where a picture's markdown points. Return null to leave it out (the caller\n * names what was left out). Default: the stored source.\n */\n imageUrl?: (source: string) => string | null;\n}\n\n/**\n * Read a Univer document snapshot back as markdown. Returns `\"\"` for an empty\n * or unreadable snapshot so the caller can say \"no source\" honestly rather than\n * printing an envelope.\n */\nexport function univerDocToMarkdown(snapshot: unknown, options: DocToMarkdownOptions = {}): string {\n if (!snapshot || typeof snapshot !== \"object\") return \"\";\n const body = (snapshot as { body?: unknown }).body;\n if (!body || typeof body !== \"object\") return \"\";\n\n const dataStream = (body as { dataStream?: unknown }).dataStream;\n if (typeof dataStream !== \"string\" || !dataStream) return \"\";\n\n // Pictures: each CUSTOM_BLOCK token is read back as `![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 += c;\n }\n return out;\n };\n\n const lines: string[] = [];\n let cursor = 0;\n let runIndex = 0;\n let tableRows: string[][] | null = null;\n\n // Paragraph boundaries are the `\\r`s in the stream itself — reading them from\n // the stream (rather than trusting `paragraphs`) keeps a snapshot written by\n // Univer's own editor, which may index differently, readable.\n for (let i = 0; i <= dataStream.length; i++) {\n const ch = dataStream[i];\n const atEnd = i === dataStream.length;\n if (!atEnd) {\n if (ch === TABLE_START) {\n tableRows = [];\n cursor = i + 1;\n continue;\n }\n if (tableRows && ch === TABLE_ROW_START) {\n tableRows.push([]);\n cursor = i + 1;\n continue;\n }\n if (tableRows && ch === TABLE_CELL_START) {\n tableRows[tableRows.length - 1]?.push(\"\");\n cursor = i + 1;\n continue;\n }\n if (tableRows && (ch === TABLE_CELL_END || ch === TABLE_ROW_END)) {\n cursor = i + 1;\n continue;\n }\n if (tableRows && ch === TABLE_END) {\n if (tableRows.length) lines.push(\"\", ...tableMarkdown(tableRows), \"\");\n tableRows = null;\n cursor = i + 1;\n continue;\n }\n }\n if (!atEnd && ch !== \"\\r\" && ch !== \"\\n\") continue;\n\n const start = cursor;\n const end = i;\n cursor = i + 1;\n if (end <= start) {\n if (ch === \"\\r\" && !tableRows) lines.push(\"\");\n if (atEnd) break;\n continue;\n }\n\n // Collect the runs overlapping this paragraph.\n const paragraphRuns: TextRunLike[] = [];\n let scan = runIndex;\n while (scan < runs.length && (runs[scan]!.st as number) < end) {\n if ((runs[scan]!.ed as number) > start) paragraphRuns.push(runs[scan]!);\n scan++;\n }\n while (\n runIndex < runs.length &&\n (runs[runIndex]!.ed as number) <= end\n ) {\n runIndex++;\n }\n\n const heading = paragraphHeadingLevel(paragraphRuns);\n let text = \"\";\n let at = start;\n for (const run of paragraphRuns) {\n const rs = Math.max(run.st as number, start);\n const re = Math.min(run.ed as number, end);\n if (rs > at) text += readSlice(at, rs);\n if (re > rs) {\n text += emphasize(readSlice(rs, re), run.ts, heading != null);\n }\n at = Math.max(at, re);\n }\n if (at < end) text += readSlice(at, end);\n\n if (tableRows) {\n // A table cell: its paragraphs join into the one cell, pipes escaped.\n const row = tableRows[tableRows.length - 1];\n if (row?.length) {\n const part = text.trim().replace(/\\|/g, \"\\\\|\");\n const last = row.length - 1;\n row[last] = row[last] ? `${row[last]} ${part}` : part;\n }\n } else {\n lines.push(heading ? `${\"#\".repeat(heading)} ${text.trim()}` : text);\n }\n if (atEnd) break;\n }\n\n // Univer keeps one paragraph per visual line; markdown wants a blank line\n // between blocks (but not inside a list or a table), and consecutive blank\n // paragraphs collapse. The writer (`markdownToUniverDoc`) flattens a list item\n // to a literal \"• \" / \"1. \" prefix and a table row to cells joined by\n // TABLE_CELL_SEP, so those shapes are read back as the markdown they came from.\n type Kind = \"text\" | \"list\" | \"table\";\n const kindOf = (line: string): Kind =>\n /^\\s*(?:\\u2022|[-*+]|\\d+[.)])\\s+/.test(line) ? \"list\" : line.includes(TABLE_CELL_SEP) ? \"table\" : \"text\";\n // A document typed or pasted as markdown keeps a table as literal `| a | b |` lines, one paragraph each, often\n // with an empty paragraph between rows: those rows are ONE table, so the gaps between them close.\n const rawRow = (l: string) => /^\\s*\\|.*\\|\\s*$/.test(l);\n const rows = lines.filter((l, i) => {\n if (l.trim()) return true;\n let a = i - 1;\n while (a >= 0 && !lines[a]!.trim()) a--;\n let z = i + 1;\n while (z < lines.length && !lines[z]!.trim()) z++;\n return !(a >= 0 && z < lines.length && rawRow(lines[a]!) && rawRow(lines[z]!));\n });\n const out: string[] = [];\n let prev: Kind | null = null;\n let tableRow = 0;\n for (const raw of rows) {\n if (!raw.trim()) {\n if (out.length && out[out.length - 1] !== \"\") out.push(\"\");\n prev = null;\n continue;\n }\n let line = raw.replace(/\\s+$/, \"\");\n if (rawRow(line)) {\n if (prev !== \"table\" && out.length && out[out.length - 1] !== \"\") out.push(\"\");\n out.push(line.trim());\n prev = \"table\";\n continue;\n }\n const kind = kindOf(line);\n if (kind === \"list\") {\n line = line.replace(/^(\\s*)\\u2022\\s+/, \"$1- \").replace(/^(\\s*)\\*\\s+/, \"$1- \");\n // Four spaces per nesting level in the document; two read as nesting in markdown.\n line = line.replace(/^( +)/, (m) => \" \".repeat(Math.floor(m.length / 2)));\n }\n if (kind === \"table\") {\n const cells = line.split(/\\s*\\|\\s*/).map((c) => c.trim());\n const isHeader = prev !== \"table\";\n const shown = cells.map((c) => (isHeader ? c.replace(/^\\*\\*(.*)\\*\\*$/, \"$1\") : c));\n line = `| ${shown.join(\" | \")} |`;\n if (isHeader) tableRow = shown.length;\n out.push(...(prev === \"table\" || prev === null || !out.length || out[out.length - 1] === \"\" ? [] : [\"\"]));\n out.push(line);\n if (isHeader) out.push(`|${\" --- |\".repeat(tableRow)}`);\n prev = \"table\";\n continue;\n }\n const tight = kind === \"list\" && prev === \"list\";\n if (out.length && out[out.length - 1] !== \"\" && !tight) out.push(\"\");\n out.push(line);\n prev = kind;\n }\n return out.join(\"\\n\").trim();\n}\n","/**\n * A file from the person's computer -> a new document (Word .docx, Markdown,\n * plain text, HTML). Everything lands as markdown first and goes through THE\n * one writer (`markdownToUniverDoc`), so headings, bold, italic, links and\n * lists keep their shape. Parse first: a file we cannot read never leaves an\n * empty document behind.\n */\n\"use client\";\n\nimport { readZip } from \"@ai-matrx/alchemy/operate/zip\";\n\nimport { createDocument, saveDocumentSnapshot, deleteDocument } from \"./document-service\";\nimport { deriveDocumentName, markdownToUniverDoc } from \"./markdown-to-univer-doc\";\nimport { sanitizeUniverDocSnapshot } from \"./sanitize-univer-doc-snapshot\";\n\nexport const DOCUMENT_UPLOAD_ACCEPT = \".docx,.md,.markdown,.txt,.html,.htm\";\n\ntype Source = \"imported_docx\" | \"imported_md\" | \"imported_txt\";\n\nexport function documentFileKind(name: string): \"docx\" | \"md\" | \"txt\" | \"html\" | null {\n const ext = name.toLowerCase().split(\".\").pop() ?? \"\";\n if (ext === \"docx\") return \"docx\";\n if (ext === \"md\" || ext === \"markdown\") return \"md\";\n if (ext === \"txt\") return \"txt\";\n if (ext === \"html\" || ext === \"htm\") return \"html\";\n return null;\n}\n\nconst W = \"http://schemas.openxmlformats.org/wordprocessingml/2006/main\";\n\n/** Word's document.xml -> markdown: headings, bold/italic runs, list paragraphs, tables as rows. */\nexport function docxXmlToMarkdown(xml: string): string {\n const doc = new DOMParser().parseFromString(xml, \"application/xml\");\n const body = doc.getElementsByTagNameNS(W, \"body\")[0];\n if (!body) throw new Error(\"This Word file has no document body.\");\n const attr = (el: Element | undefined, name: string) => el?.getAttributeNS(W, name) ?? el?.getAttribute(`w:${name}`) ?? null;\n const first = (el: Element, name: string) => el.getElementsByTagNameNS(W, name)[0] as Element | undefined;\n const on = (props: Element | undefined, name: string) => {\n const el = props ? first(props, name) : undefined;\n return !!el && attr(el, \"val\") !== \"0\" && attr(el, \"val\") !== \"false\";\n };\n const paragraph = (p: Element): string => {\n let text = \"\";\n for (const r of Array.from(p.getElementsByTagNameNS(W, \"r\"))) {\n const t = Array.from(r.childNodes)\n .map((n) => {\n const el = n as Element;\n if (el.localName === \"t\") return el.textContent ?? \"\";\n if (el.localName === \"tab\") return \"\\t\";\n if (el.localName === \"br\") return \" \\n\";\n return \"\";\n })\n .join(\"\");\n if (!t) continue;\n const rPr = first(r, \"rPr\");\n const bold = on(rPr, \"b\");\n const italic = on(rPr, \"i\");\n const lead = t.match(/^\\s*/)?.[0] ?? \"\";\n const trail = t.match(/\\s*$/)?.[0] ?? \"\";\n const core = t.trim();\n text += core ? `${lead}${bold ? \"**\" : \"\"}${italic ? \"*\" : \"\"}${core}${italic ? \"*\" : \"\"}${bold ? \"**\" : \"\"}${trail}` : t;\n }\n const pPr = first(p, \"pPr\");\n const style = attr(pPr ? first(pPr, \"pStyle\") : undefined, \"val\") ?? \"\";\n const heading = style.match(/^(?:Heading|heading)\\s?(\\d)$/)?.[1] ?? (style === \"Title\" ? \"1\" : null);\n if (heading && text.trim()) return `${\"#\".repeat(Math.min(6, Number(heading)))} ${text.replace(/\\*+/g, \"\").trim()}`;\n if (pPr && first(pPr, \"numPr\") && text.trim()) {\n const level = Number(attr(first(first(pPr, \"numPr\")!, \"ilvl\"), \"val\") ?? 0);\n return `${\" \".repeat(level)}- ${text.trim()}`;\n }\n return text;\n };\n const out: string[] = [];\n for (const child of Array.from(body.children)) {\n if (child.localName === \"p\") out.push(paragraph(child));\n else if (child.localName === \"tbl\") {\n const rows = Array.from(child.getElementsByTagNameNS(W, \"tr\")).map((tr) =>\n Array.from(tr.getElementsByTagNameNS(W, \"tc\")).map((tc) =>\n Array.from(tc.getElementsByTagNameNS(W, \"p\")).map(paragraph).join(\" \").replace(/\\|/g, \"\\\\|\").trim(),\n ),\n );\n if (rows.length) {\n out.push(\"\", `| ${rows[0]!.join(\" | \")} |`, `|${\" --- |\".repeat(rows[0]!.length)}`);\n for (const row of rows.slice(1)) out.push(`| ${row.join(\" | \")} |`);\n out.push(\"\");\n }\n }\n }\n // A list run stays tight; everything else is its own block.\n return out\n .reduce<string[]>((acc, line, i) => {\n const prevList = /^\\s*- /.test(out[i - 1] ?? \"\");\n if (i > 0 && !(prevList && /^\\s*- /.test(line))) acc.push(\"\");\n acc.push(line);\n return acc;\n }, [])\n .join(\"\\n\")\n .replace(/\\n{3,}/g, \"\\n\\n\")\n .trim();\n}\n\n/** HTML -> markdown for the blocks a document keeps (headings, paragraphs, lists, emphasis, links). */\nexport function htmlToDocumentMarkdown(html: string): string {\n const doc = new DOMParser().parseFromString(html, \"text/html\");\n const inline = (node: Node): string => {\n if (node.nodeType === 3) return (node.textContent ?? \"\").replace(/\\s+/g, \" \");\n if (node.nodeType !== 1) return \"\";\n const el = node as Element;\n const inner = Array.from(el.childNodes).map(inline).join(\"\");\n switch (el.tagName.toLowerCase()) {\n case \"strong\":\n case \"b\":\n return inner.trim() ? `**${inner.trim()}**` : \"\";\n case \"em\":\n case \"i\":\n return inner.trim() ? `*${inner.trim()}*` : \"\";\n case \"a\": {\n const href = el.getAttribute(\"href\");\n return href && /^https?:|^mailto:/i.test(href) ? `[${inner.trim()}](${href})` : inner;\n }\n case \"br\":\n return \" \\n\";\n case \"code\":\n return `\\`${inner}\\``;\n default:\n return inner;\n }\n };\n const out: string[] = [];\n const block = (el: Element, depth = 0): void => {\n const tag = el.tagName.toLowerCase();\n const h = tag.match(/^h([1-6])$/);\n if (h) {\n out.push(`${\"#\".repeat(Number(h[1]))} ${inline(el).trim()}`);\n return;\n }\n if (tag === \"p\" || tag === \"blockquote\") {\n out.push(inline(el).trim());\n return;\n }\n if (tag === \"pre\") {\n out.push(\"```\", el.textContent ?? \"\", \"```\");\n return;\n }\n if (tag === \"ul\" || tag === \"ol\") {\n Array.from(el.children).forEach((li, n) => {\n if (li.tagName.toLowerCase() !== \"li\") return;\n const own = Array.from(li.childNodes).filter((c) => !(c.nodeType === 1 && /^(ul|ol)$/i.test((c as Element).tagName)));\n out.push(`${\" \".repeat(depth)}${tag === \"ol\" ? `${n + 1}.` : \"-\"} ${own.map(inline).join(\"\").trim()}`);\n Array.from(li.children)\n .filter((c) => /^(ul|ol)$/i.test(c.tagName))\n .forEach((c) => block(c, depth + 1));\n });\n return;\n }\n if (tag === \"script\" || tag === \"style\" || tag === \"head\") return;\n const kids = Array.from(el.children);\n if (kids.length === 0) {\n const t = inline(el).trim();\n if (t) out.push(t);\n return;\n }\n kids.forEach((k) => block(k, depth));\n };\n block(doc.body);\n return out\n .map((l) => l.replace(/\\s+$/, \"\"))\n .reduce<string[]>((acc, line, i) => {\n const list = (s: string | undefined) => /^\\s*(-|\\d+\\.) /.test(s ?? \"\");\n if (i > 0 && !(list(out[i - 1]) && list(line))) acc.push(\"\");\n acc.push(line);\n return acc;\n }, [])\n .join(\"\\n\")\n .replace(/\\n{3,}/g, \"\\n\\n\")\n .trim();\n}\n\nexport async function documentFileToMarkdown(file: File): Promise<{ markdown: string; source: Source }> {\n const kind = documentFileKind(file.name);\n if (kind === \"docx\") {\n const zip = await readZip(file);\n const entry = zip.get(\"word/document.xml\");\n if (!entry) throw new Error(\"This is not a Word document (.docx).\");\n const xml = await entry.text();\n return { markdown: docxXmlToMarkdown(xml), source: \"imported_docx\" };\n }\n const text = await file.text();\n if (kind === \"md\") return { markdown: text, source: \"imported_md\" };\n if (kind === \"html\") return { markdown: htmlToDocumentMarkdown(text), source: \"imported_md\" };\n if (kind === \"txt\") {\n // Plain text: each line is its own paragraph; nothing in it is read as markdown.\n const escaped = text\n .replace(/\\r\\n?/g, \"\\n\")\n .split(\"\\n\")\n .map((l) => l.replace(/([\\\\`*_[\\]#>|~])/g, \"\\\\$1\"))\n .join(\"\\n\\n\");\n return { markdown: escaped, source: \"imported_txt\" };\n }\n throw new Error(\"Upload a Word (.docx), Markdown, text or HTML file.\");\n}\n\n/** Create a document from an uploaded file; returns its id. */\nexport async function importDocumentFile(file: File, organizationId: string): Promise<{ id: string; name: string }> {\n const { markdown, source } = await documentFileToMarkdown(file);\n const name = file.name.replace(/\\.[^.]+$/, \"\").trim() || deriveDocumentName(markdown);\n const created = await createDocument({ name, source, organizationId, description: `Imported from ${file.name}` });\n if (!created.success) throw new Error(`Could not create the document: ${created.error}`);\n const id = created.data.id;\n const snapshot = sanitizeUniverDocSnapshot(markdownToUniverDoc(markdown, name), id);\n const saved = await saveDocumentSnapshot({ documentId: id, snapshot, origin: \"imported\", label: `Imported from ${file.name}` });\n if (!saved.success) {\n await deleteDocument(id);\n throw new Error(`Could not save the imported content: ${saved.error}`);\n }\n return { id, name };\n}\n","/**\n * File → Download → EPUB / OpenDocument (.odt) / Rich Text (.rtf).\n * EPUB the print package's EPUB engine, straight from the live text.\n * ODT/RTF the same engine writes the .docx, then aidream's LibreOffice route\n * (`POST /office/convert-document`) converts it. Nothing is stored.\n * Engine, converter and saver are injected so the forcing test runs the real logic.\n */\n\nimport { getDocsHost } from \"./ports\";\n\nexport type OpenFormat = \"epub\" | \"odt\" | \"rtf\";\n\nexport const OPEN_FORMATS: Record<OpenFormat, { label: string; extension: string; mime: string }> = {\n epub: { label: \"EPUB\", extension: \"epub\", mime: \"application/epub+zip\" },\n odt: { label: \"OpenDocument\", extension: \"odt\", mime: \"application/vnd.oasis.opendocument.text\" },\n rtf: { label: \"Rich Text\", extension: \"rtf\", mime: \"application/rtf\" },\n};\n\nexport interface OpenFormatDeps {\n engine: (markdown: string, format: \"docx\" | \"epub\", fileName: string) => Promise<{ bytes: Uint8Array; fileName: string }>;\n convert: (docx: Uint8Array, fileName: string, target: \"odt\" | \"rtf\") => Promise<Uint8Array>;\n save: (fileName: string, bytes: Uint8Array, mime: string) => void;\n}\n\nfunction baseName(title: string | null | undefined): string {\n const base = (title ?? \"\").replace(/[\\\\/:*?\"<>|\\u0000-\\u001f]+/g, \" \").replace(/\\s+/g, \" \").trim().slice(0, 120);\n return base || \"document\";\n}\n\nexport async function exportOpenFormat(markdown: string, format: OpenFormat, title: string | null | undefined, deps: OpenFormatDeps): Promise<string> {\n const name = baseName(title);\n const spec = OPEN_FORMATS[format];\n const fileName = `${name}.${spec.extension}`;\n if (format === \"epub\") {\n const out = await deps.engine(markdown, \"epub\", name);\n deps.save(fileName, out.bytes, spec.mime);\n return fileName;\n }\n const docx = await deps.engine(markdown, \"docx\", name);\n const bytes = await deps.convert(docx.bytes, docx.fileName, format);\n if (!bytes.byteLength) throw new Error(`The ${spec.label} converter returned an empty file.`);\n deps.save(fileName, bytes, spec.mime);\n return fileName;\n}\n\nexport async function downloadOpenFormat(markdown: string, format: OpenFormat, title: string | null | undefined): Promise<string> {\n return exportOpenFormat(markdown, format, title, {\n engine: async (md, fmt, fileName) => {\n const { exportDocument } = await import(\"@ai-matrx/print/document\");\n const exp = await exportDocument(md, fmt, { fileName });\n return { bytes: exp.bytes, fileName: exp.fileName };\n },\n convert: async (docx, fileName, target) => {\n const form = new FormData();\n form.append(\"file\", new Blob([docx as BlobPart]), fileName.endsWith(\".docx\") ? fileName : `${fileName}.docx`);\n form.append(\"target\", target);\n const res = await getDocsHost().requestRaw(\"/office/convert-document\", { method: \"POST\", body: form });\n return new Uint8Array(await res.arrayBuffer());\n },\n save: (fileName, bytes, mime) => {\n const url = URL.createObjectURL(new Blob([bytes as BlobPart], { type: mime }));\n const link = document.createElement(\"a\");\n link.href = url;\n link.download = fileName;\n link.rel = \"noopener\";\n document.body.appendChild(link);\n link.click();\n link.remove();\n setTimeout(() => URL.revokeObjectURL(url), 30_000);\n },\n });\n}\n"],"mappings":"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","names":["out"]}
1
+ {"version":3,"sources":["../src/docs/univer-doc-to-markdown.ts","../src/docs/import-document-file.ts","../src/docs/export-open-formats.ts"],"sourcesContent":["/**\n * univerDocToMarkdown — the reverse of `markdown-to-univer-doc.ts`.\n *\n * Why it exists: the canvas pane for a cloud document (`udt_document`) offers a\n * `Source` tab, and the rule in `features/canvas/core/canvasSource.ts` is that\n * Source shows THE ITEM'S OWN SOURCE — for a document, its markdown — never the\n * redux envelope. A document's content lives as an opaque Univer\n * `IDocumentData` snapshot in `workbench.udt_document_snapshots`, so something\n * has to read it back out. This is that something, and it is the ONLY one:\n * never re-implement a second snapshot reader.\n *\n * What it reads (the half `markdownToUniverDoc` writes):\n * - `body.dataStream` — the text; `\\r` terminates a paragraph, a trailing\n * `\\n` terminates the section.\n * - `body.paragraphs` — one entry per `\\r` (`startIndex` = the `\\r` offset).\n * - `body.textRuns` — `{ st, ed, ts }` style overlays. `ts.bl` + a heading\n * font size is how `markdownToUniverDoc` encodes\n * `#`..`######`, so the level is recovered from `fs`.\n *\n * Fidelity is deliberately MODEST and honest: headings, bold, italic,\n * strikethrough and inline/monospace code round-trip; everything else comes\n * back as the plain text the user actually sees. A document typed by hand in\n * Univer never had markdown syntax in the first place — printing its text is\n * the truthful answer, not a reconstruction.\n *\n * PURE — no React, no Redux, no IO.\n */\n\nconst TABLE_CELL_SEP = \" | \";\n\n// Univer's DataStreamTreeTokenType control characters (literals: no Univer runtime here).\nconst TABLE_START = \"\\x1A\";\nconst TABLE_ROW_START = \"\\x1B\";\nconst TABLE_CELL_START = \"\\x1C\";\nconst TABLE_CELL_END = \"\\x1D\";\nconst TABLE_ROW_END = \"\\x0E\";\nconst TABLE_END = \"\\x0F\";\nconst LINK_START = \"\\x1F\";\nconst LINK_END = \"\\x1E\";\n\ninterface CustomRangeLike {\n startIndex?: number;\n endIndex?: number;\n rangeType?: number;\n properties?: { url?: unknown };\n}\n\n/** The Univer-native shapes (real tables, hyperlink ranges) written back as markdown. */\nfunction tableMarkdown(rows: string[][]): string[] {\n const width = Math.max(1, ...rows.map((r) => r.length));\n const cell = (r: string[], i: number) => (r[i] ?? \"\").replace(/^\\*\\*(.*)\\*\\*$/, \"$1\");\n const line = (r: string[], plain: boolean) =>\n `| ${Array.from({ length: width }, (_, i) => (plain ? cell(r, i) : (r[i] ?? \"\"))).join(\" | \")} |`;\n const out = [line(rows[0]!, true), `|${\" --- |\".repeat(width)}`];\n for (const r of rows.slice(1)) out.push(line(r, false));\n return out;\n}\n\ninterface TextRunLike {\n st?: number;\n ed?: number;\n ts?: {\n bl?: number;\n it?: number;\n ff?: string;\n fs?: number;\n st?: { s?: number } | number;\n };\n}\n\n/** Inverse of markdown-to-univer-doc's HEADING_FONT_SIZE. */\nexport const HEADING_LEVEL_BY_FONT_SIZE: Record<number, number> = {\n 26: 1,\n 22: 2,\n 18: 3,\n 16: 4,\n 14: 5,\n 13: 6,\n};\n\nfunction asArray<T>(value: unknown): T[] {\n return Array.isArray(value) ? (value as T[]) : [];\n}\n\nconst TRUE_ENOUGH = (v: unknown): boolean => v === 1 || v === true;\n\nfunction isStruck(ts: TextRunLike[\"ts\"]): boolean {\n if (!ts) return false;\n const st = ts.st;\n if (typeof st === \"number\") return TRUE_ENOUGH(st);\n return TRUE_ENOUGH(st?.s);\n}\n\n/** The style that applies to the whole paragraph, if the runs agree on one. */\nfunction paragraphHeadingLevel(runs: TextRunLike[]): number | null {\n for (const run of runs) {\n const fs = run.ts?.fs;\n if (typeof fs === \"number\" && TRUE_ENOUGH(run.ts?.bl)) {\n const level = HEADING_LEVEL_BY_FONT_SIZE[fs];\n if (level) return level;\n }\n }\n return null;\n}\n\nfunction emphasize(text: string, ts: TextRunLike[\"ts\"], inHeading: boolean): string {\n if (!text) return text;\n // Never double-mark: a heading's own runs are bold by construction.\n const leading = text.match(/^\\s*/)?.[0] ?? \"\";\n const trailing = text.match(/\\s*$/)?.[0] ?? \"\";\n let core = text.slice(leading.length, text.length - trailing.length);\n if (!core) return text;\n\n const mono = typeof ts?.ff === \"string\" && /mono/i.test(ts.ff);\n if (mono) core = `\\`${core}\\``;\n else {\n if (TRUE_ENOUGH(ts?.it)) core = `*${core}*`;\n if (TRUE_ENOUGH(ts?.bl) && !inHeading) core = `**${core}**`;\n }\n if (isStruck(ts)) core = `~~${core}~~`;\n return `${leading}${core}${trailing}`;\n}\n\n// An inline picture: one CUSTOM_BLOCK token in the stream, its drawing in `snapshot.drawings`.\nconst CUSTOM_BLOCK = \"\\b\";\n\ninterface DrawingLike {\n source?: unknown;\n title?: unknown;\n description?: unknown;\n docTransform?: { size?: { width?: unknown; height?: unknown } };\n}\n\n/**\n * A picture's markdown source. Our own pictures are `matrx-file:<id>`; the\n * drawn size rides as a `#WxH` fragment so a round trip keeps the shape.\n */\nexport function imageMarkdownSource(drawing: DrawingLike): string | null {\n const source = typeof drawing.source === \"string\" ? drawing.source : \"\";\n if (!source || source.startsWith(\"data:\")) return null;\n const w = Number(drawing.docTransform?.size?.width);\n const h = Number(drawing.docTransform?.size?.height);\n return Number.isFinite(w) && Number.isFinite(h) && w > 0 && h > 0 && !source.includes(\"#\")\n ? `${source}#${Math.round(w)}x${Math.round(h)}`\n : source;\n}\n\n/** Every picture in a snapshot, as its markdown source (`matrx-file:<id>#WxH` or a URL). */\nexport function documentImageSources(snapshot: unknown): string[] {\n const drawings = ((snapshot as { drawings?: unknown } | null)?.drawings ?? {}) as Record<string, DrawingLike>;\n const body = (snapshot as { body?: { customBlocks?: unknown } } | null)?.body;\n return asArray<{ blockId?: unknown }>(body?.customBlocks)\n .map((b) => (typeof b.blockId === \"string\" && drawings[b.blockId] ? imageMarkdownSource(drawings[b.blockId]!) : null))\n .filter((s): s is string => !!s);\n}\n\nexport interface DocToMarkdownOptions {\n /**\n * Where a picture's markdown points. Return null to leave it out (the caller\n * names what was left out). Default: the stored source.\n */\n imageUrl?: (source: string) => string | null;\n}\n\n/**\n * Read a Univer document snapshot back as markdown. Returns `\"\"` for an empty\n * or unreadable snapshot so the caller can say \"no source\" honestly rather than\n * printing an envelope.\n */\nexport function univerDocToMarkdown(snapshot: unknown, options: DocToMarkdownOptions = {}): string {\n if (!snapshot || typeof snapshot !== \"object\") return \"\";\n const body = (snapshot as { body?: unknown }).body;\n if (!body || typeof body !== \"object\") return \"\";\n\n const dataStream = (body as { dataStream?: unknown }).dataStream;\n if (typeof dataStream !== \"string\" || !dataStream) return \"\";\n\n // Pictures: each CUSTOM_BLOCK token is read back as `![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 += c;\n }\n return out;\n };\n\n const lines: string[] = [];\n let cursor = 0;\n let runIndex = 0;\n let tableRows: string[][] | null = null;\n\n // Paragraph boundaries are the `\\r`s in the stream itself — reading them from\n // the stream (rather than trusting `paragraphs`) keeps a snapshot written by\n // Univer's own editor, which may index differently, readable.\n for (let i = 0; i <= dataStream.length; i++) {\n const ch = dataStream[i];\n const atEnd = i === dataStream.length;\n if (!atEnd) {\n if (ch === TABLE_START) {\n tableRows = [];\n cursor = i + 1;\n continue;\n }\n if (tableRows && ch === TABLE_ROW_START) {\n tableRows.push([]);\n cursor = i + 1;\n continue;\n }\n if (tableRows && ch === TABLE_CELL_START) {\n tableRows[tableRows.length - 1]?.push(\"\");\n cursor = i + 1;\n continue;\n }\n if (tableRows && (ch === TABLE_CELL_END || ch === TABLE_ROW_END)) {\n cursor = i + 1;\n continue;\n }\n if (tableRows && ch === TABLE_END) {\n if (tableRows.length) lines.push(\"\", ...tableMarkdown(tableRows), \"\");\n tableRows = null;\n cursor = i + 1;\n continue;\n }\n }\n if (!atEnd && ch !== \"\\r\" && ch !== \"\\n\") continue;\n\n const start = cursor;\n const end = i;\n cursor = i + 1;\n if (end <= start) {\n if (ch === \"\\r\" && !tableRows) lines.push(\"\");\n if (atEnd) break;\n continue;\n }\n\n // Collect the runs overlapping this paragraph.\n const paragraphRuns: TextRunLike[] = [];\n let scan = runIndex;\n while (scan < runs.length && (runs[scan]!.st as number) < end) {\n if ((runs[scan]!.ed as number) > start) paragraphRuns.push(runs[scan]!);\n scan++;\n }\n while (\n runIndex < runs.length &&\n (runs[runIndex]!.ed as number) <= end\n ) {\n runIndex++;\n }\n\n const heading = paragraphHeadingLevel(paragraphRuns);\n let text = \"\";\n let at = start;\n for (const run of paragraphRuns) {\n const rs = Math.max(run.st as number, start);\n const re = Math.min(run.ed as number, end);\n if (rs > at) text += readSlice(at, rs);\n if (re > rs) {\n text += emphasize(readSlice(rs, re), run.ts, heading != null);\n }\n at = Math.max(at, re);\n }\n if (at < end) text += readSlice(at, end);\n\n if (tableRows) {\n // A table cell: its paragraphs join into the one cell, pipes escaped.\n const row = tableRows[tableRows.length - 1];\n if (row?.length) {\n const part = text.trim().replace(/\\|/g, \"\\\\|\");\n const last = row.length - 1;\n row[last] = row[last] ? `${row[last]} ${part}` : part;\n }\n } else {\n lines.push(heading ? `${\"#\".repeat(heading)} ${text.trim()}` : text);\n }\n if (atEnd) break;\n }\n\n // Univer keeps one paragraph per visual line; markdown wants a blank line\n // between blocks (but not inside a list or a table), and consecutive blank\n // paragraphs collapse. The writer (`markdownToUniverDoc`) flattens a list item\n // to a literal \"• \" / \"1. \" prefix and a table row to cells joined by\n // TABLE_CELL_SEP, so those shapes are read back as the markdown they came from.\n type Kind = \"text\" | \"list\" | \"table\";\n const kindOf = (line: string): Kind =>\n /^\\s*(?:\\u2022|[-*+]|\\d+[.)])\\s+/.test(line) ? \"list\" : line.includes(TABLE_CELL_SEP) ? \"table\" : \"text\";\n // A document typed or pasted as markdown keeps a table as literal `| a | b |` lines, one paragraph each, often\n // with an empty paragraph between rows: those rows are ONE table, so the gaps between them close.\n const rawRow = (l: string) => /^\\s*\\|.*\\|\\s*$/.test(l);\n const rows = lines.filter((l, i) => {\n if (l.trim()) return true;\n let a = i - 1;\n while (a >= 0 && !lines[a]!.trim()) a--;\n let z = i + 1;\n while (z < lines.length && !lines[z]!.trim()) z++;\n return !(a >= 0 && z < lines.length && rawRow(lines[a]!) && rawRow(lines[z]!));\n });\n const out: string[] = [];\n let prev: Kind | null = null;\n let tableRow = 0;\n for (const raw of rows) {\n if (!raw.trim()) {\n if (out.length && out[out.length - 1] !== \"\") out.push(\"\");\n prev = null;\n continue;\n }\n let line = raw.replace(/\\s+$/, \"\");\n if (rawRow(line)) {\n if (prev !== \"table\" && out.length && out[out.length - 1] !== \"\") out.push(\"\");\n out.push(line.trim());\n prev = \"table\";\n continue;\n }\n const kind = kindOf(line);\n if (kind === \"list\") {\n line = line.replace(/^(\\s*)\\u2022\\s+/, \"$1- \").replace(/^(\\s*)\\*\\s+/, \"$1- \");\n // Four spaces per nesting level in the document; two read as nesting in markdown.\n line = line.replace(/^( +)/, (m) => \" \".repeat(Math.floor(m.length / 2)));\n }\n if (kind === \"table\") {\n const cells = line.split(/\\s*\\|\\s*/).map((c) => c.trim());\n const isHeader = prev !== \"table\";\n const shown = cells.map((c) => (isHeader ? c.replace(/^\\*\\*(.*)\\*\\*$/, \"$1\") : c));\n line = `| ${shown.join(\" | \")} |`;\n if (isHeader) tableRow = shown.length;\n out.push(...(prev === \"table\" || prev === null || !out.length || out[out.length - 1] === \"\" ? [] : [\"\"]));\n out.push(line);\n if (isHeader) out.push(`|${\" --- |\".repeat(tableRow)}`);\n prev = \"table\";\n continue;\n }\n const tight = kind === \"list\" && prev === \"list\";\n if (out.length && out[out.length - 1] !== \"\" && !tight) out.push(\"\");\n out.push(line);\n prev = kind;\n }\n return out.join(\"\\n\").trim();\n}\n","/**\n * A file from the person's computer -> a new document (Word .docx, Markdown,\n * plain text, HTML). Everything lands as markdown first and goes through THE\n * one writer (`markdownToUniverDoc`), so headings, bold, italic, links and\n * lists keep their shape. Parse first: a file we cannot read never leaves an\n * empty document behind.\n */\n\"use client\";\n\nimport { readZip } from \"@ai-matrx/alchemy/operate/zip\";\n\nimport { createDocument, saveDocumentSnapshot, deleteDocument } from \"./document-service\";\nimport { deriveDocumentName, markdownToUniverDoc } from \"./markdown-to-univer-doc\";\nimport { sanitizeUniverDocSnapshot } from \"./sanitize-univer-doc-snapshot\";\n\nexport const DOCUMENT_UPLOAD_ACCEPT = \".docx,.md,.markdown,.txt,.html,.htm\";\n\ntype Source = \"imported_docx\" | \"imported_md\" | \"imported_txt\";\n\nexport function documentFileKind(name: string): \"docx\" | \"md\" | \"txt\" | \"html\" | null {\n const ext = name.toLowerCase().split(\".\").pop() ?? \"\";\n if (ext === \"docx\") return \"docx\";\n if (ext === \"md\" || ext === \"markdown\") return \"md\";\n if (ext === \"txt\") return \"txt\";\n if (ext === \"html\" || ext === \"htm\") return \"html\";\n return null;\n}\n\nconst W = \"http://schemas.openxmlformats.org/wordprocessingml/2006/main\";\n\n/** Rows of cell text -> ONE markdown table block (header, delimiter, body rows together). */\nfunction markdownTable(rows: string[][]): string {\n const width = Math.max(...rows.map((r) => r.length));\n const line = (r: string[]) => `| ${Array.from({ length: width }, (_, i) => r[i] ?? \"\").join(\" | \")} |`;\n return [line(rows[0]!), `|${\" --- |\".repeat(width)}`, ...rows.slice(1).map(line)].join(\"\\n\");\n}\n\n/** Word's document.xml -> markdown: headings, bold/italic runs, list paragraphs, tables as rows. */\nexport function docxXmlToMarkdown(xml: string): string {\n const doc = new DOMParser().parseFromString(xml, \"application/xml\");\n const body = doc.getElementsByTagNameNS(W, \"body\")[0];\n if (!body) throw new Error(\"This Word file has no document body.\");\n const attr = (el: Element | undefined, name: string) => el?.getAttributeNS(W, name) ?? el?.getAttribute(`w:${name}`) ?? null;\n const first = (el: Element, name: string) => el.getElementsByTagNameNS(W, name)[0] as Element | undefined;\n const on = (props: Element | undefined, name: string) => {\n const el = props ? first(props, name) : undefined;\n return !!el && attr(el, \"val\") !== \"0\" && attr(el, \"val\") !== \"false\";\n };\n const paragraph = (p: Element): string => {\n let text = \"\";\n for (const r of Array.from(p.getElementsByTagNameNS(W, \"r\"))) {\n const t = Array.from(r.childNodes)\n .map((n) => {\n const el = n as Element;\n if (el.localName === \"t\") return el.textContent ?? \"\";\n if (el.localName === \"tab\") return \"\\t\";\n if (el.localName === \"br\") return \" \\n\";\n return \"\";\n })\n .join(\"\");\n if (!t) continue;\n const rPr = first(r, \"rPr\");\n const bold = on(rPr, \"b\");\n const italic = on(rPr, \"i\");\n const lead = t.match(/^\\s*/)?.[0] ?? \"\";\n const trail = t.match(/\\s*$/)?.[0] ?? \"\";\n const core = t.trim();\n text += core ? `${lead}${bold ? \"**\" : \"\"}${italic ? \"*\" : \"\"}${core}${italic ? \"*\" : \"\"}${bold ? \"**\" : \"\"}${trail}` : t;\n }\n const pPr = first(p, \"pPr\");\n const style = attr(pPr ? first(pPr, \"pStyle\") : undefined, \"val\") ?? \"\";\n const heading = style.match(/^(?:Heading|heading)\\s?(\\d)$/)?.[1] ?? (style === \"Title\" ? \"1\" : null);\n if (heading && text.trim()) return `${\"#\".repeat(Math.min(6, Number(heading)))} ${text.replace(/\\*+/g, \"\").trim()}`;\n if (pPr && first(pPr, \"numPr\") && text.trim()) {\n const level = Number(attr(first(first(pPr, \"numPr\")!, \"ilvl\"), \"val\") ?? 0);\n return `${\" \".repeat(level)}- ${text.trim()}`;\n }\n return text;\n };\n const out: string[] = [];\n for (const child of Array.from(body.children)) {\n if (child.localName === \"p\") out.push(paragraph(child));\n else if (child.localName === \"tbl\") {\n const rows = Array.from(child.getElementsByTagNameNS(W, \"tr\")).map((tr) =>\n Array.from(tr.getElementsByTagNameNS(W, \"tc\")).map((tc) =>\n Array.from(tc.getElementsByTagNameNS(W, \"p\")).map(paragraph).join(\" \").replace(/\\|/g, \"\\\\|\").trim(),\n ),\n );\n if (rows.length) out.push(markdownTable(rows));\n }\n }\n // A list run stays tight; everything else is its own block.\n return out\n .reduce<string[]>((acc, line, i) => {\n const prevList = /^\\s*- /.test(out[i - 1] ?? \"\");\n if (i > 0 && !(prevList && /^\\s*- /.test(line))) acc.push(\"\");\n acc.push(line);\n return acc;\n }, [])\n .join(\"\\n\")\n .replace(/\\n{3,}/g, \"\\n\\n\")\n .trim();\n}\n\n/** HTML -> markdown for the blocks a document keeps (headings, paragraphs, lists, emphasis, links). */\nexport function htmlToDocumentMarkdown(html: string): string {\n const doc = new DOMParser().parseFromString(html, \"text/html\");\n const inline = (node: Node): string => {\n if (node.nodeType === 3) return (node.textContent ?? \"\").replace(/\\s+/g, \" \");\n if (node.nodeType !== 1) return \"\";\n const el = node as Element;\n const inner = Array.from(el.childNodes).map(inline).join(\"\");\n switch (el.tagName.toLowerCase()) {\n case \"strong\":\n case \"b\":\n return inner.trim() ? `**${inner.trim()}**` : \"\";\n case \"em\":\n case \"i\":\n return inner.trim() ? `*${inner.trim()}*` : \"\";\n case \"a\": {\n const href = el.getAttribute(\"href\");\n return href && /^https?:|^mailto:/i.test(href) ? `[${inner.trim()}](${href})` : inner;\n }\n case \"br\":\n return \" \\n\";\n case \"code\":\n return `\\`${inner}\\``;\n default:\n return inner;\n }\n };\n const out: string[] = [];\n const block = (el: Element, depth = 0): void => {\n const tag = el.tagName.toLowerCase();\n const h = tag.match(/^h([1-6])$/);\n if (h) {\n out.push(`${\"#\".repeat(Number(h[1]))} ${inline(el).trim()}`);\n return;\n }\n if (tag === \"p\" || tag === \"blockquote\") {\n out.push(inline(el).trim());\n return;\n }\n if (tag === \"table\") {\n const rows = Array.from(el.querySelectorAll(\"tr\")).map((tr) =>\n Array.from(tr.children)\n .filter((c) => /^(td|th)$/i.test(c.tagName))\n .map((c) => inline(c).replace(/\\|/g, \"\\\\|\").replace(/\\s+/g, \" \").trim()),\n );\n if (rows.length) out.push(markdownTable(rows));\n return;\n }\n if (tag === \"pre\") {\n out.push(\"```\", el.textContent ?? \"\", \"```\");\n return;\n }\n if (tag === \"ul\" || tag === \"ol\") {\n Array.from(el.children).forEach((li, n) => {\n if (li.tagName.toLowerCase() !== \"li\") return;\n const own = Array.from(li.childNodes).filter((c) => !(c.nodeType === 1 && /^(ul|ol)$/i.test((c as Element).tagName)));\n out.push(`${\" \".repeat(depth)}${tag === \"ol\" ? `${n + 1}.` : \"-\"} ${own.map(inline).join(\"\").trim()}`);\n Array.from(li.children)\n .filter((c) => /^(ul|ol)$/i.test(c.tagName))\n .forEach((c) => block(c, depth + 1));\n });\n return;\n }\n if (tag === \"script\" || tag === \"style\" || tag === \"head\") return;\n const kids = Array.from(el.children);\n if (kids.length === 0) {\n const t = inline(el).trim();\n if (t) out.push(t);\n return;\n }\n kids.forEach((k) => block(k, depth));\n };\n block(doc.body);\n return out\n .map((l) => l.replace(/\\s+$/, \"\"))\n .reduce<string[]>((acc, line, i) => {\n const list = (s: string | undefined) => /^\\s*(-|\\d+\\.) /.test(s ?? \"\");\n if (i > 0 && !(list(out[i - 1]) && list(line))) acc.push(\"\");\n acc.push(line);\n return acc;\n }, [])\n .join(\"\\n\")\n .replace(/\\n{3,}/g, \"\\n\\n\")\n .trim();\n}\n\nexport async function documentFileToMarkdown(file: File): Promise<{ markdown: string; source: Source }> {\n const kind = documentFileKind(file.name);\n if (kind === \"docx\") {\n const zip = await readZip(file);\n const entry = zip.get(\"word/document.xml\");\n if (!entry) throw new Error(\"This is not a Word document (.docx).\");\n const xml = await entry.text();\n return { markdown: docxXmlToMarkdown(xml), source: \"imported_docx\" };\n }\n const text = await file.text();\n if (kind === \"md\") return { markdown: text, source: \"imported_md\" };\n if (kind === \"html\") return { markdown: htmlToDocumentMarkdown(text), source: \"imported_md\" };\n if (kind === \"txt\") {\n // Plain text: each line is its own paragraph; nothing in it is read as markdown.\n const escaped = text\n .replace(/\\r\\n?/g, \"\\n\")\n .split(\"\\n\")\n .map((l) => l.replace(/([\\\\`*_[\\]#>|~])/g, \"\\\\$1\"))\n .join(\"\\n\\n\");\n return { markdown: escaped, source: \"imported_txt\" };\n }\n throw new Error(\"Upload a Word (.docx), Markdown, text or HTML file.\");\n}\n\n/** Create a document from an uploaded file; returns its id. */\nexport async function importDocumentFile(file: File, organizationId: string): Promise<{ id: string; name: string }> {\n const { markdown, source } = await documentFileToMarkdown(file);\n const name = file.name.replace(/\\.[^.]+$/, \"\").trim() || deriveDocumentName(markdown);\n const created = await createDocument({ name, source, organizationId, description: `Imported from ${file.name}` });\n if (!created.success) throw new Error(`Could not create the document: ${created.error}`);\n const id = created.data.id;\n const snapshot = sanitizeUniverDocSnapshot(markdownToUniverDoc(markdown, name), id);\n const saved = await saveDocumentSnapshot({ documentId: id, snapshot, origin: \"imported\", label: `Imported from ${file.name}` });\n if (!saved.success) {\n await deleteDocument(id);\n throw new Error(`Could not save the imported content: ${saved.error}`);\n }\n return { id, name };\n}\n","/**\n * File → Download → EPUB / OpenDocument (.odt) / Rich Text (.rtf).\n * EPUB the print package's EPUB engine, straight from the live text.\n * ODT/RTF the same engine writes the .docx, then aidream's LibreOffice route\n * (`POST /office/convert-document`) converts it. Nothing is stored.\n * Engine, converter and saver are injected so the forcing test runs the real logic.\n */\n\nimport { getDocsHost } from \"./ports\";\n\nexport type OpenFormat = \"epub\" | \"odt\" | \"rtf\";\n\nexport const OPEN_FORMATS: Record<OpenFormat, { label: string; extension: string; mime: string }> = {\n epub: { label: \"EPUB\", extension: \"epub\", mime: \"application/epub+zip\" },\n odt: { label: \"OpenDocument\", extension: \"odt\", mime: \"application/vnd.oasis.opendocument.text\" },\n rtf: { label: \"Rich Text\", extension: \"rtf\", mime: \"application/rtf\" },\n};\n\nexport interface OpenFormatDeps {\n engine: (markdown: string, format: \"docx\" | \"epub\", fileName: string) => Promise<{ bytes: Uint8Array; fileName: string }>;\n convert: (docx: Uint8Array, fileName: string, target: \"odt\" | \"rtf\") => Promise<Uint8Array>;\n save: (fileName: string, bytes: Uint8Array, mime: string) => void;\n}\n\nfunction baseName(title: string | null | undefined): string {\n const base = (title ?? \"\").replace(/[\\\\/:*?\"<>|\\u0000-\\u001f]+/g, \" \").replace(/\\s+/g, \" \").trim().slice(0, 120);\n return base || \"document\";\n}\n\nexport async function exportOpenFormat(markdown: string, format: OpenFormat, title: string | null | undefined, deps: OpenFormatDeps): Promise<string> {\n const name = baseName(title);\n const spec = OPEN_FORMATS[format];\n const fileName = `${name}.${spec.extension}`;\n if (format === \"epub\") {\n const out = await deps.engine(markdown, \"epub\", name);\n deps.save(fileName, out.bytes, spec.mime);\n return fileName;\n }\n const docx = await deps.engine(markdown, \"docx\", name);\n const bytes = await deps.convert(docx.bytes, docx.fileName, format);\n if (!bytes.byteLength) throw new Error(`The ${spec.label} converter returned an empty file.`);\n deps.save(fileName, bytes, spec.mime);\n return fileName;\n}\n\nexport async function downloadOpenFormat(markdown: string, format: OpenFormat, title: string | null | undefined): Promise<string> {\n return exportOpenFormat(markdown, format, title, {\n engine: async (md, fmt, fileName) => {\n const { exportDocument } = await import(\"@ai-matrx/print/document\");\n const exp = await exportDocument(md, fmt, { fileName });\n return { bytes: exp.bytes, fileName: exp.fileName };\n },\n convert: async (docx, fileName, target) => {\n const form = new FormData();\n form.append(\"file\", new Blob([docx as BlobPart]), fileName.endsWith(\".docx\") ? fileName : `${fileName}.docx`);\n form.append(\"target\", target);\n const res = await getDocsHost().requestRaw(\"/office/convert-document\", { method: \"POST\", body: form });\n return new Uint8Array(await res.arrayBuffer());\n },\n save: (fileName, bytes, mime) => {\n const url = URL.createObjectURL(new Blob([bytes as BlobPart], { type: mime }));\n const link = document.createElement(\"a\");\n link.href = url;\n link.download = fileName;\n link.rel = \"noopener\";\n document.body.appendChild(link);\n link.click();\n link.remove();\n setTimeout(() => URL.revokeObjectURL(url), 30_000);\n },\n });\n}\n"],"mappings":"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","names":["out"]}
package/dist/docs.cjs CHANGED
@@ -16,7 +16,7 @@
16
16
 
17
17
 
18
18
 
19
- var _chunk6Z3Y6DUIcjs = require('./chunk-6Z3Y6DUI.cjs');
19
+ var _chunk556EUPETcjs = require('./chunk-556EUPET.cjs');
20
20
 
21
21
 
22
22
  var _chunkMQB2ZK2Fcjs = require('./chunk-MQB2ZK2F.cjs');
@@ -860,5 +860,5 @@ function documentBodyWriteHandler(deps) {
860
860
 
861
861
 
862
862
 
863
- exports.DOCUMENT_BODY_MAX_LENGTH = DOCUMENT_BODY_MAX_LENGTH; exports.DOCUMENT_DESCRIPTION_MAX_LENGTH = DOCUMENT_DESCRIPTION_MAX_LENGTH; exports.DOCUMENT_NAME_MAX_LENGTH = DOCUMENT_NAME_MAX_LENGTH; exports.DOCUMENT_SAVE_DELAY_MS = DOCUMENT_SAVE_DELAY_MS; exports.DocumentModel = DocumentModel; exports.EMOJI_GROUPS = EMOJI_GROUPS; exports.MIN_FIT_ZOOM = MIN_FIT_ZOOM; exports.SPECIAL_GROUPS = SPECIAL_GROUPS; exports.bodyTextOf = bodyTextOf; exports.configureDocsHost = _chunk6Z3Y6DUIcjs.configureDocsHost; exports.copyDocument = _chunk6Z3Y6DUIcjs.copyDocument; exports.createDocument = _chunk6Z3Y6DUIcjs.createDocument; exports.deleteDocument = _chunk6Z3Y6DUIcjs.deleteDocument; exports.discardFailedDocument = _chunk6Z3Y6DUIcjs.discardFailedDocument; exports.documentBodyWriteHandler = documentBodyWriteHandler; exports.documentSourceLabel = documentSourceLabel; exports.fitZoomRatio = fitZoomRatio; exports.getDocsHost = _chunk6Z3Y6DUIcjs.getDocsHost; exports.getDocument = _chunk6Z3Y6DUIcjs.getDocument; exports.getLatestDocumentSnapshot = _chunk6Z3Y6DUIcjs.getLatestDocumentSnapshot; exports.listAccessibleDocuments = _chunk6Z3Y6DUIcjs.listAccessibleDocuments; exports.listDocumentSnapshots = _chunk6Z3Y6DUIcjs.listDocumentSnapshots; exports.listTrashedDocuments = _chunk6Z3Y6DUIcjs.listTrashedDocuments; exports.moveDocument = _chunk6Z3Y6DUIcjs.moveDocument; exports.planBodyEdit = planBodyEdit; exports.renameDocument = _chunk6Z3Y6DUIcjs.renameDocument; exports.restoreDocument = _chunk6Z3Y6DUIcjs.restoreDocument; exports.sanitizeUniverDocSnapshot = _chunk6Z3Y6DUIcjs.sanitizeUniverDocSnapshot; exports.saveDocumentSnapshot = _chunk6Z3Y6DUIcjs.saveDocumentSnapshot; exports.searchSymbols = searchSymbols; exports.updateDocumentDescription = _chunk6Z3Y6DUIcjs.updateDocumentDescription; exports.validateDocumentBody = validateDocumentBody; exports.validateDocumentDescription = validateDocumentDescription; exports.validateDocumentName = validateDocumentName; exports.writeBodyText = writeBodyText;
863
+ exports.DOCUMENT_BODY_MAX_LENGTH = DOCUMENT_BODY_MAX_LENGTH; exports.DOCUMENT_DESCRIPTION_MAX_LENGTH = DOCUMENT_DESCRIPTION_MAX_LENGTH; exports.DOCUMENT_NAME_MAX_LENGTH = DOCUMENT_NAME_MAX_LENGTH; exports.DOCUMENT_SAVE_DELAY_MS = DOCUMENT_SAVE_DELAY_MS; exports.DocumentModel = DocumentModel; exports.EMOJI_GROUPS = EMOJI_GROUPS; exports.MIN_FIT_ZOOM = MIN_FIT_ZOOM; exports.SPECIAL_GROUPS = SPECIAL_GROUPS; exports.bodyTextOf = bodyTextOf; exports.configureDocsHost = _chunk556EUPETcjs.configureDocsHost; exports.copyDocument = _chunk556EUPETcjs.copyDocument; exports.createDocument = _chunk556EUPETcjs.createDocument; exports.deleteDocument = _chunk556EUPETcjs.deleteDocument; exports.discardFailedDocument = _chunk556EUPETcjs.discardFailedDocument; exports.documentBodyWriteHandler = documentBodyWriteHandler; exports.documentSourceLabel = documentSourceLabel; exports.fitZoomRatio = fitZoomRatio; exports.getDocsHost = _chunk556EUPETcjs.getDocsHost; exports.getDocument = _chunk556EUPETcjs.getDocument; exports.getLatestDocumentSnapshot = _chunk556EUPETcjs.getLatestDocumentSnapshot; exports.listAccessibleDocuments = _chunk556EUPETcjs.listAccessibleDocuments; exports.listDocumentSnapshots = _chunk556EUPETcjs.listDocumentSnapshots; exports.listTrashedDocuments = _chunk556EUPETcjs.listTrashedDocuments; exports.moveDocument = _chunk556EUPETcjs.moveDocument; exports.planBodyEdit = planBodyEdit; exports.renameDocument = _chunk556EUPETcjs.renameDocument; exports.restoreDocument = _chunk556EUPETcjs.restoreDocument; exports.sanitizeUniverDocSnapshot = _chunk556EUPETcjs.sanitizeUniverDocSnapshot; exports.saveDocumentSnapshot = _chunk556EUPETcjs.saveDocumentSnapshot; exports.searchSymbols = searchSymbols; exports.updateDocumentDescription = _chunk556EUPETcjs.updateDocumentDescription; exports.validateDocumentBody = validateDocumentBody; exports.validateDocumentDescription = validateDocumentDescription; exports.validateDocumentName = validateDocumentName; exports.writeBodyText = writeBodyText;
864
864
  //# sourceMappingURL=docs.cjs.map
package/dist/docs.js CHANGED
@@ -16,7 +16,7 @@ import {
16
16
  sanitizeUniverDocSnapshot,
17
17
  saveDocumentSnapshot,
18
18
  updateDocumentDescription
19
- } from "./chunk-XMM62UAT.js";
19
+ } from "./chunk-5JAZ32SX.js";
20
20
  import {
21
21
  looksLikeMarkdown
22
22
  } from "./chunk-47MIGBMH.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-matrx/office",
3
- "version": "0.8.2",
3
+ "version": "0.8.4",
4
4
  "description": "The AI Matrx Office layer: the editors' shared command and menu model, the Google and Microsoft ribbon model, page setup, the Office record types and the configured product hosts. The Univer glue shared by Sheets and Docs. Sheets, Docs and the other products arrive as sections of this one package.",
5
5
  "type": "module",
6
6
  "main": "./dist/core.cjs",
@@ -173,15 +173,15 @@
173
173
  "ot-json1": "^1.0.2",
174
174
  "y-protocols": "^1.0.7",
175
175
  "yjs": "^13.6.33",
176
+ "@ai-matrx/alchemy": "latest",
176
177
  "@ai-matrx/auth": "latest",
177
- "@ai-matrx/api": "latest",
178
178
  "@ai-matrx/content-ir": "latest",
179
- "@ai-matrx/alchemy": "latest",
180
- "@ai-matrx/data": "latest",
181
- "@ai-matrx/kit": "latest",
179
+ "@ai-matrx/api": "latest",
180
+ "@ai-matrx/print": "latest",
182
181
  "@ai-matrx/records": "latest",
182
+ "@ai-matrx/kit": "latest",
183
183
  "@ai-matrx/rich-content": "latest",
184
- "@ai-matrx/print": "latest"
184
+ "@ai-matrx/data": "latest"
185
185
  },
186
186
  "peerDependencies": {
187
187
  "@supabase/supabase-js": ">=2.0.0",
@@ -235,5 +235,5 @@
235
235
  "test:watch": "vitest",
236
236
  "verify:tarball": "node ./scripts/verify-tarball.mjs"
237
237
  },
238
- "gitHead": "a0abd289e5f33a7f2068e6c1805c5f33f68add24"
238
+ "gitHead": "04ca2e385c27a968a05a7dca0caf5d38a9b51582"
239
239
  }
@@ -1 +0,0 @@
1
- {"version":3,"sources":["/home/runner/work/aidream/aidream/apps/shared/office/dist/chunk-6Z3Y6DUI.cjs","../src/docs/ports.ts","../src/docs/document-service.ts","../src/docs/sanitize-univer-doc-snapshot.ts"],"names":["error"],"mappings":"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","file":"/home/runner/work/aidream/aidream/apps/shared/office/dist/chunk-6Z3Y6DUI.cjs","sourcesContent":[null,"/**\n * The host ports of the Docs model and io.\n *\n * The package owns the document service, the open-document model and the converters, but two things\n * belong to the app that mounts them: the database client (a package owns no client) and the server\n * request that converts a .docx to .odt / .rtf (the app owns the server URL, auth and error capture).\n * The host states them once at start-up with `configureDocsHost`. State sits on a `globalThis` slot\n * so a dual-bundled copy of the package still sees one host. An unconfigured port THROWS naming\n * itself (nothing fails silently).\n */\nimport type { SupabaseClient } from \"@supabase/supabase-js\";\n\n/** A Supabase client over any schema: the package cannot import the app's generated database types. */\nexport type DocsSupabase = SupabaseClient<any, any, any>;\n\nexport interface DocsHostPorts {\n /** The app's database client (signed in as the person). */\n getSupabase(): DocsSupabase;\n /** An authenticated request to the Matrx server returning the raw `Response` (the open-format converter). */\n requestRaw(path: string, init: { method: \"POST\"; body: FormData }): Promise<Response>;\n}\n\nconst SLOT = Symbol.for(\"ai-matrx.office.docs-host\");\ntype Slot = { ports?: DocsHostPorts };\nconst slot = (): Slot => {\n const g = globalThis as Record<symbol, Slot | undefined>;\n return (g[SLOT] ??= {});\n};\n\nexport function configureDocsHost(ports: DocsHostPorts): void {\n slot().ports = ports;\n}\n\nexport function getDocsHost(): DocsHostPorts {\n const ports = slot().ports;\n if (!ports) throw new Error(\"@ai-matrx/office/docs: configureDocsHost(ports) was not called before the document service ran.\");\n return ports;\n}\n","/**\n * Document service — typed wrappers for `udt_documents` + `udt_document_snapshots`.\n *\n * Cloud document editor backed by Univer's preset-docs-core. Same shape as\n * `workbook-service.ts` — metadata in `udt_documents`, content state in\n * append-only `udt_document_snapshots`. The editor hydrates from the LATEST\n * snapshot and writes a new snapshot per save (debounced on the client —\n * see DocumentEditor).\n *\n * What lives elsewhere:\n * - Sharing / permissions: features/sharing/ + `has_permission(...)` RLS\n * - The open document (one model per id per tab: load, one save, realtime,\n * collab): features/documents/document-model/\n * - Component (a view of that model): features/documents/components/DocumentEditor\n *\n * Mirrors `workbook-service.ts`. If you're changing the shape of one, change\n * the other at the same time — see `features/data-tables/FEATURE.md`.\n */\nimport { requireOrganizationContext } from \"@ai-matrx/api\";\nimport { getClaimsUser } from \"@ai-matrx/auth\";\nimport { readAllRows, tryWriteOne, writeOneRow } from \"@ai-matrx/data/db\";\nimport type { ServiceResult } from \"@ai-matrx/records/core\";\n\nimport type { DocumentRow, DocumentSnapshot, DocumentSnapshotOrigin } from \"../core\";\nimport { getDocsHost } from \"./ports\";\n\n/** The app's database client, read through the host port at call time. */\nconst db = () => getDocsHost().getSupabase();\n// ─── documents ───────────────────────────────────────────────────────────────\n\nexport type CreateDocumentArgs = {\n name: string;\n description?: string | null;\n /** Origin label for the document itself, mirrors `udt_documents.source`. */\n source?: \"created\" | \"imported_docx\" | \"imported_md\" | \"imported_txt\" | \"linked_gdoc\";\n organizationId: string;\n projectId?: string | null;\n taskId?: string | null;\n isPublic?: boolean;\n /**\n * cld_files.id of the source upload (DOCX / MD / TXT blob). Set on the\n * import flow so the lossless original is recoverable; FK is ON DELETE SET\n * NULL, so deleting the file just nulls the link — the document survives.\n */\n originalFileId?: string | null;\n};\n\nexport async function createDocument(\n args: CreateDocumentArgs,\n): Promise<ServiceResult<DocumentRow>> {\n let organizationId: string;\n try {\n organizationId = requireOrganizationContext(undefined, args.organizationId);\n } catch (error) {\n return {\n success: false,\n error:\n error instanceof Error\n ? error.message\n : \"Select an organization before creating a document.\",\n };\n }\n\n const { data: userData, error: userErr } = await getClaimsUser(db());\n if (userErr || !userData?.user) {\n return {\n success: false,\n error: userErr?.message ?? \"not authenticated\",\n };\n }\n\n const { data, error } = await db()\n .schema(\"workbench\")\n .from(\"udt_documents\")\n .insert({\n document_name: args.name,\n description: args.description ?? null,\n source: args.source ?? \"created\",\n organization_id: organizationId,\n project_id: args.projectId ?? null,\n task_id: args.taskId ?? null,\n original_file_id: args.originalFileId ?? null,\n // CANONICAL columns (workbench_udt_canonical_step1). `visibility` is the\n // source of truth; the legacy `is_public` boolean is derived from it by\n // the workbench._bridge_legacy_owner trigger, so writing both here would\n // be two authorities for one fact. A new document defaults to `internal`\n // — it is org work product, not an individual person's private thing\n // (db-rules §6) — unless the caller explicitly asked for public.\n created_by: userData.user.id,\n visibility: args.isPublic ? \"public\" : \"internal\",\n })\n .select(\"*\")\n .single();\n if (error) return { success: false, error: error.message };\n return { success: true, data: data as DocumentRow };\n}\n\nexport async function listAccessibleDocuments(): Promise<\n ServiceResult<DocumentRow[]>\n> {\n // RLS handles owner / published-to-the-web / shared access. The library and\n // the pickers treat this list as COMPLETE (\"you have no such document\"), so\n // it pages past PostgREST's silent 1000-row cap (D190).\n try {\n const rows = await readAllRows<DocumentRow>(\n ({ from, to }) =>\n db()\n .schema(\"workbench\")\n .from(\"udt_documents\")\n .select(\"*\", { count: \"exact\" })\n .is(\"deleted_at\", null)\n .order(\"updated_at\", { ascending: false })\n .order(\"id\", { ascending: true })\n .range(from, to)\n .returns<DocumentRow[]>(),\n { label: \"workbench.udt_documents accessible documents\" },\n );\n return { success: true, data: rows };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : String(error),\n };\n }\n}\n\nexport async function getDocument(\n documentId: string,\n): Promise<ServiceResult<DocumentRow>> {\n const { data, error } = await db()\n .schema(\"workbench\")\n .from(\"udt_documents\")\n .select(\"*\")\n .eq(\"id\", documentId)\n .is(\"deleted_at\", null)\n .maybeSingle();\n if (error) return { success: false, error: error.message };\n if (!data) {\n // A trashed or unreachable document is a state the person can act on,\n // never PostgREST's \"Cannot coerce the result to a single JSON object\".\n return {\n success: false,\n error:\n \"This document is in the trash or you no longer have access to it. Restore it from Trash, or ask its owner to share it again.\",\n };\n }\n return { success: true, data: data as DocumentRow };\n}\n\nexport async function renameDocument(\n documentId: string,\n name: string,\n): Promise<ServiceResult<DocumentRow>> {\n const { data, error } = await writeOneRow(\n db()\n .schema(\"workbench\")\n .from(\"udt_documents\")\n .update({ document_name: name, updated_at: new Date().toISOString() })\n .eq(\"id\", documentId)\n .select(\"*\"),\n { action: \"update\", noun: \"document\" },\n );\n if (error) return { success: false, error: error.message };\n return { success: true, data: data as DocumentRow };\n}\n\n/**\n * Move the document into a project (or out of one with `null`). Sibling of\n * `renameDocument`: the one named setter for `project_id`.\n */\nexport async function moveDocument(\n documentId: string,\n projectId: string | null,\n): Promise<ServiceResult<DocumentRow>> {\n const { data, error } = await writeOneRow(\n db()\n .schema(\"workbench\")\n .from(\"udt_documents\")\n .update({ project_id: projectId, updated_at: new Date().toISOString() })\n .eq(\"id\", documentId)\n .select(\"*\"),\n { action: \"update\", noun: \"document\" },\n );\n if (error) return { success: false, error: error.message };\n return { success: true, data: data as DocumentRow };\n}\n\n/**\n * Rewrite the document's description. Sibling of `renameDocument` — the two\n * human-authored fields on `udt_documents` each get a named setter so callers\n * never hand-roll a `.from(\"udt_documents\").update(...)`. Pass `null` (or an\n * empty string) to clear it.\n *\n * Added when the documents surface became agent-writable: the\n * `document_description` write target on `/documents/[id]` is its caller, the\n * same way `updateWorkbookDescription` serves `workbook_description`. Keeping\n * the pair symmetric is this file's standing contract with `workbook-service`\n * (see the module header).\n */\nexport async function updateDocumentDescription(\n documentId: string,\n description: string | null,\n): Promise<ServiceResult<DocumentRow>> {\n const { data, error } = await writeOneRow(\n db()\n .schema(\"workbench\")\n .from(\"udt_documents\")\n .update({\n description: description && description.length > 0 ? description : null,\n updated_at: new Date().toISOString(),\n })\n .eq(\"id\", documentId)\n .select(\"*\"),\n { action: \"update\", noun: \"document\" },\n );\n if (error) return { success: false, error: error.message };\n return { success: true, data: data as DocumentRow };\n}\n\n/**\n * Soft delete — the document is tombstoned, not destroyed, and its snapshots stay\n * with it. Every read path filters `deleted_at is null`. Pair with\n * restoreDocument for undo. (aidream migration 0458.)\n */\nexport async function restoreDocument(\n documentId: string,\n): Promise<ServiceResult<true>> {\n const { error } = await tryWriteOne(\n db()\n .schema(\"workbench\")\n .from(\"udt_documents\")\n .update({ deleted_at: null })\n .eq(\"id\", documentId)\n .select(\"id\"),\n { action: \"restore\", noun: \"document\" },\n );\n if (error) return { success: false, error: error.message };\n return { success: true, data: true };\n}\n\n/**\n * Archive (delete means archive: never a hard delete) — only for rolling back a document this very flow just created and\n * failed to populate. Never use for a user-initiated delete: that is\n * deleteDocument, which tombstones and stays recoverable.\n */\nexport async function discardFailedDocument(\n documentId: string,\n): Promise<ServiceResult<true>> {\n const { error } = await tryWriteOne(\n db()\n .schema(\"workbench\")\n .from(\"udt_documents\")\n .update({ deleted_at: new Date().toISOString() })\n .eq(\"id\", documentId)\n .select(\"id\"),\n { action: \"archive\", noun: \"document\" },\n );\n if (error) return { success: false, error: error.message };\n return { success: true, data: true };\n}\n\nexport async function deleteDocument(\n documentId: string,\n): Promise<ServiceResult<true>> {\n const { error } = await tryWriteOne(\n db()\n .schema(\"workbench\")\n .from(\"udt_documents\")\n .update({ deleted_at: new Date().toISOString() })\n .eq(\"id\", documentId)\n .select(\"id\"),\n { action: \"delete\", noun: \"document\" },\n );\n if (error) return { success: false, error: error.message };\n return { success: true, data: true };\n}\n\n// ─── snapshots (document content) ────────────────────────────────────────────\n\n/**\n * Latest-snapshot fetch — what an opened document hydrates from. Returns\n * `data: null` (success path) when the document has no snapshots yet (newly\n * created, never saved). Distinguish \"no snapshot\" from \"load error\" by\n * checking `result.data === null`.\n */\nexport async function getLatestDocumentSnapshot(\n documentId: string,\n): Promise<ServiceResult<DocumentSnapshot | null>> {\n const { data, error } = await db()\n .schema(\"workbench\")\n .from(\"udt_document_snapshots\")\n .select(\"*\")\n .eq(\"document_id\", documentId)\n .order(\"created_at\", { ascending: false })\n .limit(1)\n .maybeSingle();\n if (error) return { success: false, error: error.message };\n return { success: true, data: (data ?? null) as DocumentSnapshot | null };\n}\n\nexport type SaveDocumentSnapshotArgs = {\n documentId: string;\n snapshot: unknown; // opaque to us — Univer IDocumentData decides the shape\n label?: string | null;\n origin?: DocumentSnapshotOrigin;\n};\n\nexport async function saveDocumentSnapshot(\n args: SaveDocumentSnapshotArgs,\n): Promise<ServiceResult<DocumentSnapshot>> {\n const { data: userData } = await getClaimsUser(db());\n const { data, error } = await db()\n .schema(\"workbench\")\n .from(\"udt_document_snapshots\")\n .insert({\n document_id: args.documentId,\n snapshot: args.snapshot as never,\n label: args.label ?? null,\n origin: args.origin ?? \"autosave\",\n created_by: userData?.user?.id ?? null,\n })\n .select(\"*\")\n .single();\n if (error) return { success: false, error: error.message };\n\n // Touch the parent document's updated_at so list views can sort by recency\n // without scanning snapshots. Best-effort — failure here is harmless.\n // write-lands-exempt: best-effort recency stamp on the parent document; the snapshot insert above is the real write\n await db()\n .schema(\"workbench\")\n .from(\"udt_documents\")\n .update({ updated_at: new Date().toISOString() })\n .eq(\"id\", args.documentId);\n\n return { success: true, data: data as DocumentSnapshot };\n}\n\n/**\n * An independent copy of a document: its own row, with the latest snapshot copied over, so editing the copy never\n * touches the original. The copy lives in the original's organization and project. Used by a board-template use.\n */\nexport async function copyDocument(\n documentId: string,\n name?: string,\n): Promise<ServiceResult<DocumentRow>> {\n const original = await getDocument(documentId);\n if (!original.success) return original;\n const row = original.data;\n if (!row.organization_id) {\n return { success: false, error: \"This document has no organization and cannot be copied.\" };\n }\n const created = await createDocument({\n name: name ?? row.document_name,\n description: row.description ?? null,\n organizationId: row.organization_id,\n projectId: row.project_id ?? null,\n isPublic: row.visibility === \"public\",\n });\n if (!created.success) return created;\n const latest = await getLatestDocumentSnapshot(documentId);\n if (!latest.success) return latest;\n if (latest.data) {\n const saved = await saveDocumentSnapshot({\n documentId: created.data.id,\n snapshot: latest.data.snapshot,\n label: latest.data.label,\n origin: \"autosave\",\n });\n if (!saved.success) return saved;\n }\n return created;\n}\n\n/** Trash view: the caller's own archived documents (RLS decides what is readable). */\nexport async function listTrashedDocuments(): Promise<\n ServiceResult<DocumentRow[]>\n> {\n const { data, error } = await db()\n .schema(\"workbench\")\n .from(\"udt_documents\")\n .select(\"*\")\n .not(\"deleted_at\", \"is\", null)\n .order(\"deleted_at\", { ascending: false });\n if (error) return { success: false, error: error.message };\n return { success: true, data: (data ?? []) as DocumentRow[] };\n}\n\nexport async function listDocumentSnapshots(\n documentId: string,\n limit = 50,\n): Promise<ServiceResult<DocumentSnapshot[]>> {\n const { data, error } = await db()\n .schema(\"workbench\")\n .from(\"udt_document_snapshots\")\n // component-created-by-ok: append-only snapshot with NO updated_by column and NO parent-rewrite trigger — created_by is the author the client wrote at insert (the only person field this table has)\n .select(\"id, document_id, label, origin, created_by, created_at\")\n .eq(\"document_id\", documentId)\n .order(\"created_at\", { ascending: false })\n .limit(limit);\n if (error) return { success: false, error: error.message };\n return { success: true, data: (data ?? []) as DocumentSnapshot[] };\n}\n","/**\n * sanitizeUniverDocSnapshot — defensive normalizer for Univer document\n * snapshots loaded from storage.\n *\n * WHY THIS EXISTS (loud recovery layer):\n * Univer's `NamedStyleType` is a NUMERIC enum (`HEADING_1 = 4`, `HEADING_2 = 5`,\n * …). Some snapshots in storage were produced by an external markdown→Univer\n * converter that wrote the *string* name (`\"HEADING_1\"`) into\n * `paragraphStyle.namedStyleType` instead of the numeric value. At render time\n * Univer does `HEADING_ICON_MAP[namedStyleType]` → `undefined` →\n * `icon.key` throws `Cannot read properties of undefined (reading 'key')` inside\n * the `<ParagraphMenu>` component the moment the cursor lands in a heading. That\n * uncaught error tears down Univer's React root, so the document loads and then\n * vanishes. See `features/data-tables/FEATURE.md`.\n *\n * This normalizer converts those string enum values back to numbers BEFORE the\n * snapshot reaches `createUniverDoc`. It is a recovery layer: when it fires it\n * means malformed data slipped past the writer, so it SCREAMS (console.warn)\n * with a count and the document id. The proper long-term fix is to make the\n * writer emit numeric enum values; until then this keeps the editor alive.\n *\n * Returns the SAME object (mutated in place) for ergonomic call sites, plus a\n * `fixed` count via the second return slot is intentionally avoided — callers\n * only need the sanitized snapshot; the warning is the signal.\n */\nimport { NamedStyleType } from \"@univerjs/core\";\nimport type { IDocumentData } from \"@univerjs/core\";\n\nimport {\n defaultDocumentPageStyle,\n isLegacySqueezedPageStyle,\n} from \"../core\";\n\n/**\n * Map of every string name that could appear in a legacy snapshot to its\n * canonical numeric `NamedStyleType`. Built from the enum itself so it stays in\n * lockstep with Univer (numeric enums expose name→value on string keys).\n */\nconst NAMED_STYLE_STRING_TO_NUMBER: Record<string, number> = Object.fromEntries(\n Object.entries(NamedStyleType)\n .filter(([key, val]) => typeof val === \"number\" && isNaN(Number(key)))\n .map(([key, val]) => [key, val as number]),\n);\n\n/**\n * Normalize a Univer document snapshot in place. Currently fixes string-valued\n * `paragraphStyle.namedStyleType` (the confirmed `<ParagraphMenu>` crash). New\n * classes of string-vs-enum corruption should be added here, not patched at the\n * call site.\n *\n * @param snapshot The snapshot about to be passed to `createUniverDoc`.\n * @param docId Document id, for the loud warning only.\n * @returns the same snapshot reference (mutated).\n */\nexport function sanitizeUniverDocSnapshot(\n snapshot: Partial<IDocumentData>,\n docId?: string,\n): Partial<IDocumentData> {\n restorePageStyle(snapshot, docId);\n\n const paragraphs = snapshot?.body?.paragraphs;\n if (!Array.isArray(paragraphs)) return snapshot;\n\n let fixed = 0;\n for (const paragraph of paragraphs) {\n const style = paragraph?.paragraphStyle as\n { namedStyleType?: unknown } | undefined;\n const value = style?.namedStyleType;\n if (typeof value === \"string\") {\n const numeric = NAMED_STYLE_STRING_TO_NUMBER[value];\n if (numeric !== undefined) {\n (style as { namedStyleType?: number }).namedStyleType = numeric;\n fixed += 1;\n } else {\n // Unknown string we can't map — drop it so Univer falls back to\n // NORMAL_TEXT rather than crashing on the lookup.\n delete (style as { namedStyleType?: unknown }).namedStyleType;\n fixed += 1;\n }\n }\n }\n\n if (fixed > 0) {\n console.warn(\n `[data-tables] RECOVERY: sanitized ${fixed} string namedStyleType value(s) ` +\n `to numeric NamedStyleType in document snapshot${docId ? ` ${docId}` : \"\"}. ` +\n `The snapshot was written with the wrong enum encoding (string instead of ` +\n `number) and would have crashed Univer's <ParagraphMenu>. Fix the snapshot ` +\n `writer to emit numeric NamedStyleType values.`,\n );\n }\n\n return snapshot;\n}\n\n/**\n * Second recovery: a snapshot with no `documentStyle.pageSize` has no page box,\n * so Univer lays the body out unbounded — text never wraps and the docs\n * viewport reports no scrollable extent, which reads to the user as \"the\n * document won't scroll\". Snapshots written by agents (`origin='agent'`) and by\n * older converters reach storage with `documentStyle: {}`. Stamp the canonical\n * page geometry back on and SCREAM: the writer is the real bug.\n */\nfunction restorePageStyle(snapshot: Partial<IDocumentData>, docId?: string) {\n if (!snapshot || typeof snapshot !== \"object\") return;\n\n // THE SQUEEZED PAGE (2026-10-15): documents carried A4-at-72dpi geometry in Univer's pageless\n // flavour — a 415px text column with no paper. Move them to a real Letter page, unchanged text.\n // A document with no flavour at all is a paginated page too (Univer would otherwise pick pageless).\n const style = snapshot.documentStyle;\n if (style?.pageSize?.width && isLegacySqueezedPageStyle(style)) {\n const page = defaultDocumentPageStyle();\n snapshot.documentStyle = { ...style, ...page, defaultParagraphStyle: style.defaultParagraphStyle ?? page.defaultParagraphStyle } as typeof style;\n return;\n }\n if (style?.pageSize?.width) {\n if (!style.documentFlavor) {\n const page = defaultDocumentPageStyle();\n Object.assign(style, { documentFlavor: page.documentFlavor });\n if (page.defaultParagraphStyle) style.defaultParagraphStyle ??= page.defaultParagraphStyle;\n }\n return;\n }\n\n snapshot.documentStyle = {\n ...defaultDocumentPageStyle(),\n ...(snapshot.documentStyle ?? {}),\n pageSize: defaultDocumentPageStyle().pageSize,\n } as NonNullable<typeof snapshot.documentStyle>;\n\n console.warn(\n `[data-tables] RECOVERY: document snapshot${docId ? ` ${docId}` : \"\"} had no ` +\n `documentStyle.pageSize — restored the default Letter page geometry. Without a ` +\n `page box Univer lays the body out unbounded: text does not wrap and the ` +\n `viewport has no scrollable extent, so the document appears frozen. Fix the ` +\n `snapshot writer (agent/markdown converters must stamp ` +\n `DEFAULT_DOCUMENT_PAGE_STYLE).`,\n );\n}\n"]}