@sobree/core 0.1.0

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.
Files changed (127) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +106 -0
  3. package/dist/__vite-browser-external-DYxpcVy9.js +5 -0
  4. package/dist/__vite-browser-external-DYxpcVy9.js.map +1 -0
  5. package/dist/blob/cache.d.ts +69 -0
  6. package/dist/blob/fetch.d.ts +18 -0
  7. package/dist/blob/hash.d.ts +13 -0
  8. package/dist/blob/index.d.ts +33 -0
  9. package/dist/blob/memory.d.ts +2 -0
  10. package/dist/blob/types.d.ts +80 -0
  11. package/dist/createSobree.d.ts +132 -0
  12. package/dist/doc/api.d.ts +132 -0
  13. package/dist/doc/builders.d.ts +42 -0
  14. package/dist/doc/pageSetupBridge.d.ts +26 -0
  15. package/dist/doc/parts.d.ts +18 -0
  16. package/dist/doc/runs.d.ts +47 -0
  17. package/dist/doc/styles.d.ts +19 -0
  18. package/dist/doc/types.d.ts +800 -0
  19. package/dist/doc/walk.d.ts +30 -0
  20. package/dist/docx/export/contentTypes.d.ts +35 -0
  21. package/dist/docx/export/context.d.ts +59 -0
  22. package/dist/docx/export/document.d.ts +19 -0
  23. package/dist/docx/export/drawings.d.ts +10 -0
  24. package/dist/docx/export/headers.d.ts +19 -0
  25. package/dist/docx/export/index.d.ts +14 -0
  26. package/dist/docx/export/runs.d.ts +8 -0
  27. package/dist/docx/export/styles.d.ts +8 -0
  28. package/dist/docx/export/zip.d.ts +13 -0
  29. package/dist/docx/import/anchoredFrames.d.ts +34 -0
  30. package/dist/docx/import/comments.d.ts +3 -0
  31. package/dist/docx/import/document.d.ts +57 -0
  32. package/dist/docx/import/flowFrames.d.ts +11 -0
  33. package/dist/docx/import/footnotes.d.ts +3 -0
  34. package/dist/docx/import/headers.d.ts +50 -0
  35. package/dist/docx/import/index.d.ts +12 -0
  36. package/dist/docx/import/inlineFrames.d.ts +62 -0
  37. package/dist/docx/import/numbering.d.ts +2 -0
  38. package/dist/docx/import/paragraph.d.ts +24 -0
  39. package/dist/docx/import/paragraphs.d.ts +27 -0
  40. package/dist/docx/import/rels.d.ts +5 -0
  41. package/dist/docx/import/runs.d.ts +64 -0
  42. package/dist/docx/import/settings.d.ts +48 -0
  43. package/dist/docx/import/styles.d.ts +3 -0
  44. package/dist/docx/import/tables.d.ts +12 -0
  45. package/dist/docx/import/unzip.d.ts +13 -0
  46. package/dist/docx/shared/namespaces.d.ts +31 -0
  47. package/dist/docx/shared/pageSize.d.ts +27 -0
  48. package/dist/docx/shared/shading.d.ts +2 -0
  49. package/dist/docx/shared/units.d.ts +35 -0
  50. package/dist/docx/shared/xml.d.ts +29 -0
  51. package/dist/docx/types.d.ts +98 -0
  52. package/dist/editor/index.d.ts +1078 -0
  53. package/dist/editor/internal/blockRegistry.d.ts +91 -0
  54. package/dist/editor/internal/mutations.d.ts +63 -0
  55. package/dist/editor/internal/positionMap.d.ts +35 -0
  56. package/dist/editor/table.d.ts +96 -0
  57. package/dist/editor/view/docRenderer/anchorLayer.d.ts +26 -0
  58. package/dist/editor/view/docRenderer/block.d.ts +13 -0
  59. package/dist/editor/view/docRenderer/fontFallback.d.ts +28 -0
  60. package/dist/editor/view/docRenderer/index.d.ts +18 -0
  61. package/dist/editor/view/docRenderer/inline.d.ts +15 -0
  62. package/dist/editor/view/docRenderer/inlineFrame.d.ts +4 -0
  63. package/dist/editor/view/docRenderer/lists.d.ts +28 -0
  64. package/dist/editor/view/docRenderer/paragraph.d.ts +2 -0
  65. package/dist/editor/view/docRenderer/properties.d.ts +2 -0
  66. package/dist/editor/view/docRenderer/table.d.ts +15 -0
  67. package/dist/editor/view/docRenderer/units.d.ts +48 -0
  68. package/dist/editor/view/docSerialize/block.d.ts +14 -0
  69. package/dist/editor/view/docSerialize/index.d.ts +8 -0
  70. package/dist/editor/view/docSerialize/inline.d.ts +11 -0
  71. package/dist/editor/view/docSerialize/table.d.ts +12 -0
  72. package/dist/editor/view/imageResize.d.ts +16 -0
  73. package/dist/embed/floatingCorner.d.ts +44 -0
  74. package/dist/embed/viewport.d.ts +133 -0
  75. package/dist/fonts/embedAPI.d.ts +33 -0
  76. package/dist/fonts/emit.d.ts +24 -0
  77. package/dist/fonts/fontFaceRegistry.d.ts +20 -0
  78. package/dist/fonts/fsType.d.ts +36 -0
  79. package/dist/fonts/index.d.ts +19 -0
  80. package/dist/fonts/liveness.d.ts +2 -0
  81. package/dist/fonts/odttf.d.ts +33 -0
  82. package/dist/fonts/parse.d.ts +29 -0
  83. package/dist/fonts/types.d.ts +52 -0
  84. package/dist/headless.d.ts +168 -0
  85. package/dist/history/history.d.ts +100 -0
  86. package/dist/history/index.d.ts +4 -0
  87. package/dist/history/types.d.ts +54 -0
  88. package/dist/index.css +1 -0
  89. package/dist/index.d.ts +52 -0
  90. package/dist/index.js +10561 -0
  91. package/dist/index.js.map +1 -0
  92. package/dist/markdown/parse.d.ts +6 -0
  93. package/dist/pagination/cost.d.ts +32 -0
  94. package/dist/pagination/index.d.ts +2 -0
  95. package/dist/pagination/paginate.d.ts +10 -0
  96. package/dist/pagination/postConditions.d.ts +10 -0
  97. package/dist/pagination/types.d.ts +94 -0
  98. package/dist/paperStack/pageSetup.d.ts +42 -0
  99. package/dist/paperStack/paginationAdapter/buildItems.d.ts +19 -0
  100. package/dist/paperStack/paginationAdapter/distribute.d.ts +23 -0
  101. package/dist/paperStack/paginationAdapter/index.d.ts +18 -0
  102. package/dist/paperStack/paginationAdapter/paragraphLines.d.ts +23 -0
  103. package/dist/paperStack/paginationAdapter/splitList.d.ts +19 -0
  104. package/dist/paperStack/paginationAdapter/splitParagraph.d.ts +21 -0
  105. package/dist/paperStack/paginationAdapter/types.d.ts +30 -0
  106. package/dist/paperStack/paper.d.ts +107 -0
  107. package/dist/paperStack/paperStack.d.ts +245 -0
  108. package/dist/plugin.d.ts +24 -0
  109. package/dist/plugins/marks.d.ts +49 -0
  110. package/dist/plugins/sections.d.ts +15 -0
  111. package/dist/presence/attach.d.ts +48 -0
  112. package/dist/presence/awareness.d.ts +28 -0
  113. package/dist/presence/index.d.ts +19 -0
  114. package/dist/presence/overlay.d.ts +28 -0
  115. package/dist/presence/state.d.ts +36 -0
  116. package/dist/sobree.d.ts +211 -0
  117. package/dist/tokens.css +144 -0
  118. package/dist/util/selection.d.ts +13 -0
  119. package/dist/ydoc/apply.d.ts +68 -0
  120. package/dist/ydoc/index.d.ts +18 -0
  121. package/dist/ydoc/project.d.ts +41 -0
  122. package/dist/ydoc/runs.d.ts +51 -0
  123. package/dist/ydoc/schema.d.ts +123 -0
  124. package/dist/ydoc/seed.d.ts +45 -0
  125. package/dist/ydoc/textDiff.d.ts +59 -0
  126. package/dist/zoneEdit/index.d.ts +22 -0
  127. package/package.json +61 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":["../src/plugins/sections.ts","../src/paperStack/pageSetup.ts","../src/paperStack/paginationAdapter/paragraphLines.ts","../src/paperStack/paginationAdapter/buildItems.ts","../src/paperStack/paginationAdapter/splitParagraph.ts","../src/paperStack/paginationAdapter/distribute.ts","../src/pagination/cost.ts","../src/pagination/postConditions.ts","../src/pagination/types.ts","../src/pagination/paginate.ts","../src/paperStack/paginationAdapter/index.ts","../src/editor/view/docRenderer/fontFallback.ts","../src/editor/view/docRenderer/inline.ts","../src/editor/view/docRenderer/table.ts","../src/editor/view/docRenderer/units.ts","../src/doc/styles.ts","../src/editor/view/docRenderer/properties.ts","../src/editor/view/docRenderer/lists.ts","../src/editor/view/docRenderer/inlineFrame.ts","../src/doc/walk.ts","../src/editor/view/docRenderer/paragraph.ts","../src/editor/view/docRenderer/block.ts","../src/editor/view/docRenderer/anchorLayer.ts","../src/paperStack/paper.ts","../src/util/selection.ts","../src/paperStack/paperStack.ts","../src/doc/builders.ts","../src/doc/pageSetupBridge.ts","../src/docx/export/context.ts","../src/docx/shared/namespaces.ts","../src/docx/shared/xml.ts","../src/docx/export/contentTypes.ts","../src/docx/export/drawings.ts","../src/docx/shared/units.ts","../src/docx/export/runs.ts","../src/docx/export/document.ts","../src/docx/export/headers.ts","../src/docx/export/styles.ts","../src/docx/export/zip.ts","../src/fonts/liveness.ts","../src/doc/parts.ts","../src/fonts/odttf.ts","../src/fonts/fsType.ts","../src/fonts/parse.ts","../src/fonts/emit.ts","../src/fonts/embedAPI.ts","../src/fonts/fontFaceRegistry.ts","../src/docx/export/index.ts","../src/docx/import/runs.ts","../src/docx/shared/shading.ts","../src/docx/import/paragraphs.ts","../src/docx/import/paragraph.ts","../src/docx/import/tables.ts","../src/docx/import/document.ts","../src/docx/import/rels.ts","../src/docx/import/headers.ts","../src/docx/import/unzip.ts","../src/docx/import/footnotes.ts","../src/docx/import/comments.ts","../src/docx/import/settings.ts","../src/docx/import/styles.ts","../src/docx/import/numbering.ts","../src/docx/import/anchoredFrames.ts","../src/docx/import/flowFrames.ts","../src/docx/import/inlineFrames.ts","../src/docx/import/index.ts","../src/blob/types.ts","../src/blob/hash.ts","../src/blob/memory.ts","../src/blob/fetch.ts","../src/blob/cache.ts","../src/ydoc/schema.ts","../src/ydoc/runs.ts","../src/ydoc/seed.ts","../src/ydoc/project.ts","../src/ydoc/textDiff.ts","../src/ydoc/apply.ts","../src/doc/api.ts","../src/doc/runs.ts","../src/editor/internal/blockRegistry.ts","../src/editor/internal/mutations.ts","../src/editor/internal/positionMap.ts","../src/editor/table.ts","../src/editor/view/docRenderer/index.ts","../src/history/types.ts","../src/history/history.ts","../src/plugins/marks.ts","../src/editor/view/docSerialize/inline.ts","../src/editor/view/docSerialize/table.ts","../src/editor/view/docSerialize/block.ts","../src/editor/view/docSerialize/index.ts","../src/editor/view/imageResize.ts","../src/editor/index.ts","../src/sobree.ts","../src/embed/viewport.ts","../src/markdown/parse.ts","../src/createSobree.ts","../src/embed/floatingCorner.ts","../src/zoneEdit/index.ts","../src/headless.ts","../src/presence/state.ts","../src/presence/attach.ts","../src/presence/overlay.ts"],"sourcesContent":["import type { Editor } from \"../editor\";\nimport type { SectionBreak } from \"../doc/types\";\n\n/**\n * Section commands plugin.\n *\n * Registers `section.insertBreakAfter` — inserts a `SectionBreak` block\n * after the caret's current block. The new section inherits the\n * previous section's properties (cloned so later edits don't bleed\n * back) so visual continuity is preserved until the user edits the\n * new section in Page Setup.\n *\n * The keyboard plugin's Ctrl+Shift+Enter dispatches this command, the\n * change-type popover does too, and a future Insert menu would as well\n * — same dispatch path for every trigger.\n */\nexport function attachSections(editor: Editor): () => void {\n return editor.commands.register({\n name: \"section.insertBreakAfter\",\n title: \"Insert section break\",\n isAvailable: () => editor.selection.currentCaret() !== null,\n run: () => insertSectionBreakAfterCaret(editor),\n });\n}\n\nfunction insertSectionBreakAfterCaret(editor: Editor): void {\n const caret = editor.selection.currentCaret();\n if (!caret) return;\n const block: SectionBreak = {\n kind: \"section_break\",\n // `toSectionIndex` is the position of the new (next) section. Set\n // to current sections length — Sobree's `change` listener picks up\n // the new section index by re-walking the body and rebuilding its\n // local sections view; we don't enforce the exact value here.\n toSectionIndex: editor.getDocument().sections.length,\n };\n const result = editor.insertBlockAfter(caret.block, block);\n if (!result.ok) {\n console.warn(\"[sobree] section.insertBreakAfter failed:\", result.error);\n return;\n }\n // Clone the previous section's properties for the new one so the\n // visual layout stays the same until the user edits Page Setup. The\n // doc model has a sections array; mirror it.\n const doc = editor.getDocument();\n const sections = doc.sections.slice();\n const lastIdx = sections.length - 1;\n const lastSection = sections[lastIdx];\n if (lastSection) {\n sections.push(structuredClone(lastSection));\n editor.setDocument({ ...doc, sections });\n }\n}\n","export type PageSizeKey = \"A3\" | \"A4\" | \"A5\" | \"B5\" | \"Letter\" | \"Legal\" | \"Tabloid\";\n\nexport interface PageSizeMM {\n width: number;\n height: number;\n}\n\nexport const PAGE_SIZES: Record<PageSizeKey, PageSizeMM> = {\n A3: { width: 297, height: 420 },\n A4: { width: 210, height: 297 },\n A5: { width: 148, height: 210 },\n B5: { width: 176, height: 250 },\n Letter: { width: 215.9, height: 279.4 },\n Legal: { width: 215.9, height: 355.6 },\n Tabloid: { width: 279.4, height: 431.8 },\n};\n\nexport type Orientation = \"portrait\" | \"landscape\";\n\nexport interface Margins {\n top: number;\n right: number;\n bottom: number;\n left: number;\n}\n\nexport interface PageZoneText {\n default: string;\n first: string;\n last: string;\n differentFirst: boolean;\n differentLast: boolean;\n}\n\n/** Vertical alignment of body content on every page — OOXML `<w:vAlign>`. */\nexport type VerticalAlign = \"top\" | \"center\" | \"bottom\" | \"both\";\n\nexport interface PageSetup {\n size: PageSizeKey;\n orientation: Orientation;\n margins: Margins;\n header: PageZoneText;\n footer: PageZoneText;\n /** Section-level vertical alignment. Default `\"top\"`. */\n verticalAlign: VerticalAlign;\n}\n\nexport const DEFAULT_PAGE_SETUP: PageSetup = {\n size: \"A4\",\n orientation: \"portrait\",\n margins: { top: 25, right: 20, bottom: 25, left: 20 },\n header: {\n default: \"\",\n first: \"\",\n last: \"\",\n differentFirst: false,\n differentLast: false,\n },\n footer: {\n default: \"Page {page} of {pages}\",\n first: \"\",\n last: \"\",\n differentFirst: false,\n differentLast: false,\n },\n verticalAlign: \"top\",\n};\n\nexport function resolvedDimensions(setup: PageSetup): { widthMM: number; heightMM: number } {\n const { width, height } = PAGE_SIZES[setup.size];\n return setup.orientation === \"landscape\"\n ? { widthMM: height, heightMM: width }\n : { widthMM: width, heightMM: height };\n}\n\nexport function substituteVariables(\n template: string,\n ctx: { page: number; pages: number },\n): string {\n return template.replace(/\\{page\\}/g, String(ctx.page)).replace(/\\{pages\\}/g, String(ctx.pages));\n}\n\n/** Pick the raw zone template for a given page, honouring first/last overrides. */\nexport function zoneTemplateFor(zone: PageZoneText, page: number, pages: number): string {\n if (page === 1 && zone.differentFirst) return zone.first;\n if (page === pages && zone.differentLast && pages > 1) return zone.last;\n return zone.default;\n}\n","/**\n * Measure the individual line boxes of a paragraph-like element using\n * `Range.getClientRects()`. The returned metrics let the paginator treat\n * each line as its own `Box` so widow/orphan rules can act per line.\n *\n * Fast path uses binary search — O(L · log N) per paragraph for L lines\n * and N characters (versus O(N) for a per-character linear walk).\n */\n\nexport interface LineMetric {\n lineIndex: number;\n /** Natural height of the line box in CSS px. */\n height: number;\n /** Character offset within the element where the line starts. */\n startCharOffset: number;\n /** Character offset (exclusive) where the line ends. */\n endCharOffset: number;\n}\n\nexport function measureParagraphLines(el: HTMLElement): LineMetric[] {\n const totalChars = countTextChars(el);\n if (totalChars === 0) {\n return [\n {\n lineIndex: 0,\n height: el.getBoundingClientRect().height,\n startCharOffset: 0,\n endCharOffset: 0,\n },\n ];\n }\n\n const range = document.createRange();\n range.selectNodeContents(el);\n // jsdom's Range stub doesn't implement `getClientRects` — tests that\n // exercise the paginator (collab-providers loopback, oracle) would\n // otherwise throw and surface as listener errors. Fall back to a\n // single line box using `offsetHeight` when the method is missing;\n // the resulting metrics aren't useful for layout (jsdom doesn't run\n // layout anyway) but they're enough to keep the pipeline running.\n const getRects = (range as Range & { getClientRects?: () => DOMRectList }).getClientRects;\n if (typeof getRects !== \"function\") {\n return [\n {\n lineIndex: 0,\n height: el.offsetHeight,\n startCharOffset: 0,\n endCharOffset: totalChars,\n },\n ];\n }\n const lines = clusterLineRects(Array.from(range.getClientRects()));\n // `offsetHeight` is in LOGICAL px (unaffected by CSS transforms on an\n // ancestor viewport). `getBoundingClientRect()` is post-transform — mixing\n // the two with a logical `pageContentHeight` mis-packs pages at any zoom.\n const logicalHeight = el.offsetHeight;\n if (lines.length <= 1) {\n return [\n {\n lineIndex: 0,\n height: logicalHeight,\n startCharOffset: 0,\n endCharOffset: totalChars,\n },\n ];\n }\n\n const starts: number[] = [0];\n for (let i = 1; i < lines.length; i++) {\n starts.push(findLineStartOffset(el, i, totalChars));\n }\n\n // Distribute the paragraph's logical height uniformly across clustered\n // lines. Sum always equals `offsetHeight` exactly, so the paginator can't\n // overfill or underfill due to scale mismatches.\n const lineHeight = logicalHeight / lines.length;\n return lines.map((_l, i) => ({\n lineIndex: i,\n height: lineHeight,\n startCharOffset: starts[i] ?? 0,\n endCharOffset: starts[i + 1] ?? totalChars,\n }));\n}\n\n/** Find the character in `el` at which line `targetLine` (1-based) begins. */\nfunction findLineStartOffset(el: HTMLElement, targetLine: number, totalChars: number): number {\n // `lo` converges to the smallest offset where range [0, offset) covers\n // `targetLine + 1` line boxes — i.e. (first char of line targetLine) + 1.\n // Return `lo - 1` so the offset IS the first char of the target line.\n const range = document.createRange();\n let lo = 1;\n let hi = totalChars;\n while (lo < hi) {\n const mid = (lo + hi) >>> 1;\n if (countLinesUpTo(el, mid, range) > targetLine) hi = mid;\n else lo = mid + 1;\n }\n return Math.max(0, lo - 1);\n}\n\nfunction countLinesUpTo(el: HTMLElement, charOffset: number, range: Range): number {\n const pos = nodeAtCharOffset(el, charOffset);\n if (!pos) return 1;\n range.setStart(el, 0);\n range.setEnd(pos.node, pos.offset);\n return clusterLineRects(Array.from(range.getClientRects())).length;\n}\n\n/** Resolve a character offset (0-based, counting only text chars) to (textNode, nodeOffset). */\nexport function nodeAtCharOffset(\n el: HTMLElement,\n charOffset: number,\n): { node: Text; offset: number } | null {\n const walker = document.createTreeWalker(el, NodeFilter.SHOW_TEXT);\n let total = 0;\n let node = walker.nextNode() as Text | null;\n while (node) {\n if (total + node.length >= charOffset) {\n return { node, offset: charOffset - total };\n }\n total += node.length;\n node = walker.nextNode() as Text | null;\n }\n // charOffset points past the last char — return end of last text node.\n const last = el.lastChild;\n if (last && last.nodeType === Node.TEXT_NODE) {\n const t = last as Text;\n return { node: t, offset: t.length };\n }\n return null;\n}\n\nfunction countTextChars(el: HTMLElement): number {\n const walker = document.createTreeWalker(el, NodeFilter.SHOW_TEXT);\n let total = 0;\n let node = walker.nextNode() as Text | null;\n while (node) {\n total += node.length;\n node = walker.nextNode() as Text | null;\n }\n return total;\n}\n\n/**\n * Cluster a list of client rects into distinct lines by rounded `top`.\n * Rich content (spans, italics, descenders) on the same visual line\n * can produce rects whose tops drift by 1-2px due to glyph-metric\n * differences. The tolerance has to be loose enough to absorb that\n * drift but tighter than the smallest reasonable line-height; 5px\n * covers sub-pixel jitter without ever merging adjacent lines (the\n * tightest CSS line-height the renderer emits is ~14px at 11pt body).\n *\n * Why this matters: when sibling rects on the same line round to\n * consecutive integers (e.g. 2717 and 2718), a strict `< 1` tolerance\n * counts them as separate lines, inflating `lines.length`. The\n * paginator then divides `offsetHeight` by the inflated count and\n * under-measures every line — pages overflow because each per-line\n * box is recorded smaller than its rendered size. Observed on\n * user-contract page 3 (an LI with 10 visual lines clustered as 12\n * → 35pt under-measurement → page overflow).\n */\nfunction clusterLineRects(rects: DOMRect[]): Array<{ top: number; bottom: number }> {\n const lines: Array<{ top: number; bottom: number }> = [];\n for (const r of rects) {\n if (r.width === 0 && r.height === 0) continue;\n const key = Math.round(r.top);\n const existing = lines.find((l) => Math.abs(l.top - key) <= LINE_CLUSTER_TOL_PX);\n if (existing) {\n existing.bottom = Math.max(existing.bottom, r.bottom);\n } else {\n lines.push({ top: key, bottom: r.bottom });\n }\n }\n return lines.sort((a, b) => a.top - b.top);\n}\n\nconst LINE_CLUSTER_TOL_PX = 5;\n","import { measureParagraphLines } from \"./paragraphLines\";\nimport type { DomBox, DomItem } from \"./types\";\n\n/**\n * Convert a flat list of top-level block elements into an Item stream for\n * the pagination engine.\n *\n * Per-block handling:\n * - `.page-break` / `[data-page-break]` → Penalty(-Infinity) + zero-height Box\n * (element still lands at the top of the next page for round-tripping).\n * - `<figure>` / `.keep-together` / `[data-keep-together]` → monolithic Box\n * (whole block moves together; doesn't split).\n * - `<table>`, `<pre>` → monolithic Box.\n * - h1–h6 → Box with `keepWithNext: true`.\n * - `<p>` with ≥2 line boxes → one Box per line (shared paragraphId,\n * first/last flags set) so widow/orphan can act per line.\n * - Otherwise → one Box with the element's measured height.\n *\n * Inter-block glue(0) makes every inter-block position a candidate break.\n */\nexport function buildItems(blocks: HTMLElement[]): DomItem[] {\n const items: DomItem[] = [];\n // Track running vertical position so inter-block glue reflects the REAL\n // gap after CSS margin-collapse (not the naïve sum of margins).\n let prevBottom = 0;\n for (let i = 0; i < blocks.length; i++) {\n const el = blocks[i];\n if (!el) continue;\n\n // Absolute / fixed positioning takes the element out of flow. It\n // shouldn't contribute to gaps OR to `prevBottom`. Without this\n // the next in-flow block's `offsetTop` is below the previous\n // in-flow block, but `prevBottom` reflects the absolute block's\n // (much smaller) offsetTop+0, producing a negative gap that gets\n // clamped to 0 — harmless — OR an unrelated huge gap when the\n // absolute block landed elsewhere. Either way we should ignore.\n const cs = getComputedStyle(el);\n const isOutOfFlow = cs.position === \"absolute\" || cs.position === \"fixed\";\n\n const gapBefore = isOutOfFlow ? 0 : Math.max(0, el.offsetTop - prevBottom);\n if (i > 0 || gapBefore > 0) {\n // Glue between blocks: height is the actual laid-out gap (often 0 if\n // the previous block's margin-bottom collapsed with this one's margin-top).\n items.push({ type: \"glue\", height: gapBefore });\n }\n\n if (isPageBreakMarker(el)) {\n items.push({ type: \"penalty\", cost: Number.NEGATIVE_INFINITY });\n items.push({ type: \"box\", height: 0, el, monolithic: true });\n } else if (isKeepTogetherGroup(el)) {\n if (hasPageBreakBefore(el)) items.push({ type: \"penalty\", cost: Number.NEGATIVE_INFINITY });\n items.push(singleBox(el, { monolithic: true }));\n } else if (el.tagName === \"P\") {\n if (hasPageBreakBefore(el)) items.push({ type: \"penalty\", cost: Number.NEGATIVE_INFINITY });\n items.push(...paragraphLineItems(el, ensureParagraphId(el)));\n } else if (el.tagName === \"OL\" || el.tagName === \"UL\") {\n if (hasPageBreakBefore(el)) items.push({ type: \"penalty\", cost: Number.NEGATIVE_INFINITY });\n items.push(...listItemBoxes(el, ensureListId(el)));\n } else if (el.tagName === \"TABLE\") {\n if (hasPageBreakBefore(el)) items.push({ type: \"penalty\", cost: Number.NEGATIVE_INFINITY });\n items.push(...tableRowBoxes(el, ensureTableId(el)));\n } else {\n if (hasPageBreakBefore(el)) items.push({ type: \"penalty\", cost: Number.NEGATIVE_INFINITY });\n items.push(singleBox(el, extraFlagsFor(el)));\n }\n\n // Out-of-flow elements don't move prevBottom — they don't push\n // siblings down so the next in-flow block measures its real gap\n // against the previous in-flow block.\n if (!isOutOfFlow) {\n prevBottom = el.offsetTop + el.offsetHeight;\n }\n }\n return items;\n}\n\n/**\n * Lazily assign a stable paragraph id as a data-* attribute. `splitElementAt­CharOffset`\n * copies all attributes to the new fragment, so after a split both halves\n * share this id — widow/orphan then works across the logical paragraph even\n * though the DOM holds two sibling `<p>` elements.\n */\nfunction ensureParagraphId(el: HTMLElement): string {\n const existing = el.dataset.pagPid;\n if (existing) return existing;\n const fresh = `p${Math.random().toString(36).slice(2, 10)}`;\n el.dataset.pagPid = fresh;\n return fresh;\n}\n\n/**\n * Lazily assign a stable list id to `<ol>` / `<ul>` parents. Mirrors\n * `ensureParagraphId`: after a list is split across pages, both halves\n * carry this id so `mergeConsecutiveFragments` can rejoin them on the\n * next pagination pass.\n */\nfunction ensureListId(el: HTMLElement): string {\n const existing = el.dataset.pagLid;\n if (existing) return existing;\n const fresh = `l${Math.random().toString(36).slice(2, 10)}`;\n el.dataset.pagLid = fresh;\n return fresh;\n}\n\n/** Lazily assign a stable table id. Mirrors `ensureListId` so split\n * table fragments can be rejoined by `mergeConsecutiveFragments`. */\nfunction ensureTableId(el: HTMLElement): string {\n const existing = el.dataset.pagTid;\n if (existing) return existing;\n const fresh = `t${Math.random().toString(36).slice(2, 10)}`;\n el.dataset.pagTid = fresh;\n return fresh;\n}\n\n/**\n * Emit one box per `<tr>` child of `table`. Each row becomes its own\n * monolithic box pointing at the `<tr>` element (NOT the table) so the\n * paginator can break between rows. After pagination, `distributePages`\n * rebuilds per-page `<table>` clones from whichever rows landed on\n * each page (mirrors the `<ul>` / `<li>` pattern at one level up).\n *\n * Rows are monolithic — we don't yet split a single row across pages\n * even when its cells contain multi-line content. A row taller than\n * the page budget overflows visibly; that's a known T3 gap (Word's\n * `<w:cantSplit/>` semantics are still the implicit default here).\n */\nfunction tableRowBoxes(table: HTMLElement, tid: string): DomItem[] {\n void tid; // ensureTableId is called by the parent for the data-pag-tid stamp\n const trs: HTMLElement[] = [];\n // Walk THEAD rows first (they render at top of each per-page table\n // clone in distributePages), then TBODY rows. Without this, tables\n // with header rows (Word's `<w:tblHeader/>` → renderer's THEAD)\n // lose their header entirely after pagination.\n for (const section of [\"thead\", \"tbody\"] as const) {\n const sec = table.querySelector(`:scope > ${section}`);\n if (!sec) continue;\n for (const child of Array.from(sec.children)) {\n if (child.tagName === \"TR\" && child instanceof HTMLElement) trs.push(child);\n }\n }\n // Fallback: table with no THEAD/TBODY — just walk direct TR children.\n if (trs.length === 0) {\n for (const child of Array.from(table.children)) {\n if (child.tagName === \"TR\" && child instanceof HTMLElement) trs.push(child);\n }\n }\n if (trs.length === 0) {\n // No rows — emit one zero box pointing at the table so it survives\n // distribution (rare; mostly defensive for malformed imports).\n return [singleBox(table, { monolithic: true })];\n }\n // Estimate a page-budget threshold for the \"tall row\" decision. Rows\n // taller than this are split by their dominant cell's paragraphs so\n // a long Experience / Project listing in a 2-column resume table\n // can flow across pages instead of forcing a whole-row break onto\n // the next page (the pre-split behaviour added an extra page on\n // pentest-engineer.docx). The threshold is a conservative ~40% of a\n // typical Letter content area — large enough that small label rows\n // stay monolithic, small enough that the Experience row triggers a\n // split. We don't have the actual page budget in scope here so we\n // hard-code; the paginator's actual fit decisions still drive the\n // final placement.\n const TALL_ROW_THRESHOLD_PX = 360;\n const out: DomItem[] = [];\n for (let i = 0; i < trs.length; i++) {\n const tr = trs[i]!;\n const rowHeight = tr.offsetHeight;\n if (rowHeight > TALL_ROW_THRESHOLD_PX) {\n out.push(...tallRowParagraphBoxes(tr));\n } else {\n out.push(singleBox(tr, { monolithic: true }));\n }\n if (i < trs.length - 1) out.push({ type: \"glue\", height: 0 });\n }\n return out;\n}\n\n/**\n * For a TR that's too tall to fit on a single page, emit one box per\n * paragraph in the row's DOMINANT cell (the cell with the most\n * paragraph-like children). The dominant cell's paragraphs become\n * candidate break points; non-dominant cells (typically label\n * columns: \"Experience\", \"Education\") attach their content to the\n * row's FIRST box via `isFirstParaOfRow` so the label appears at the\n * top of the row's first occurrence.\n *\n * `distributePages` walks these boxes and routes each into the right\n * cell of a per-page TR clone. The row's overall structure preserves\n * across pages: label cell on the first fragment, content cell content\n * split by where the paginator chose to break.\n */\nfunction tallRowParagraphBoxes(tr: HTMLElement): DomItem[] {\n const cells = Array.from(tr.children).filter(\n (c): c is HTMLElement => c.tagName === \"TD\" || c.tagName === \"TH\",\n );\n if (cells.length === 0) return [singleBox(tr, { monolithic: true })];\n // Dominant cell = cell with most paragraph-like children. Ties go to\n // the leftmost cell (consistent ordering, doesn't matter for layout).\n let dominant = cells[0]!;\n let domCount = cellParagraphs(cells[0]!).length;\n for (let i = 1; i < cells.length; i++) {\n const c = cellParagraphs(cells[i]!).length;\n if (c > domCount) {\n dominant = cells[i]!;\n domCount = c;\n }\n }\n const paras = cellParagraphs(dominant);\n if (paras.length === 0) return [singleBox(tr, { monolithic: true })];\n\n const out: DomItem[] = [];\n for (let i = 0; i < paras.length; i++) {\n const p = paras[i]!;\n out.push({\n type: \"box\",\n height: measureBlockHeight(p),\n el: p,\n cellTr: tr,\n isFirstLineOfParagraph: true,\n isLastLineOfParagraph: true,\n ...(i === 0 ? { isFirstParaOfRow: true } : {}),\n });\n if (i < paras.length - 1) out.push({ type: \"glue\", height: 0 });\n }\n return out;\n}\n\n/**\n * Walk a cell's BLOCK-LEVEL children (`<p>`, `<ol>`, `<ul>`). Each\n * direct child becomes one splittable unit — ULs stay intact (so the\n * bullet marker / list grouping survive the row split). Splitting\n * *within* a UL inside a cell is a smaller-grain feature; for now,\n * the whole UL travels with its preceding paragraph as one box.\n *\n * Nested tables / divs not yet handled — would need recursive walks.\n * Resume cells don't typically nest tables.\n */\nfunction cellParagraphs(cell: HTMLElement): HTMLElement[] {\n const out: HTMLElement[] = [];\n for (const child of Array.from(cell.children)) {\n if (!(child instanceof HTMLElement)) continue;\n if (child.tagName === \"P\" || child.tagName === \"OL\" || child.tagName === \"UL\") {\n out.push(child);\n }\n }\n return out;\n}\n\n/**\n * Emit one box per visual line, per `<li>` child.\n *\n * Each `<li>` is treated as if it were a top-level `<p>` — multi-line\n * LIs produce one box per line (via `paragraphLineItems`) so the\n * paginator can break inside an LI when it doesn't fit. After\n * pagination, `distributePages` splits the LI at the character offset\n * of the first line on the later page (same `splitElementAtCharOffset`\n * machinery <p>-splitting uses). The OL/UL is rebuilt per page from\n * the LI fragments that landed on each.\n *\n * The OL element itself has no box — its identity is preserved via\n * the `data-pag-lid` attribute (stamped here) so\n * `mergeConsecutiveFragments` rejoins split fragments before the next\n * pagination pass. Each LI gets its own `data-pag-pid` for the same\n * reason at the LI level.\n */\nfunction listItemBoxes(list: HTMLElement, lid: string): DomItem[] {\n void lid; // ensureListId is called by the parent for the data-pag-lid stamp\n const lis = Array.from(list.children).filter(\n (c): c is HTMLElement => c.tagName === \"LI\",\n );\n if (lis.length === 0) {\n // Empty list — nothing to paginate. Emit a zero-height placeholder\n // pointing at the OL itself so it survives distribution.\n return [singleBox(list, {})];\n }\n const out: DomItem[] = [];\n for (let i = 0; i < lis.length; i++) {\n const li = lis[i]!;\n const pid = ensureParagraphId(li);\n const lineItems = paragraphLineItems(li, pid);\n out.push(...lineItems);\n if (i < lis.length - 1) out.push({ type: \"glue\", height: 0 });\n }\n return out;\n}\n\nfunction paragraphLineItems(p: HTMLElement, pid: string): DomItem[] {\n const lines = measureParagraphLines(p);\n // `data-keep-next` applies to the LAST line of the paragraph so the\n // paginator forbids breaking between the paragraph and what follows\n // it. Mirrors how h1-h6 implicitly get keepWithNext via extraFlagsFor.\n const keepNext = p.hasAttribute(\"data-keep-next\");\n if (lines.length <= 1) {\n return [\n singleBox(p, {\n paragraphId: pid,\n isFirstLineOfParagraph: true,\n isLastLineOfParagraph: true,\n ...(keepNext ? { keepWithNext: true } : {}),\n }),\n ];\n }\n const out: DomItem[] = [];\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i]!;\n const isLast = i === lines.length - 1;\n const box: DomBox = {\n type: \"box\",\n height: line.height,\n el: p,\n paragraphId: pid,\n isFirstLineOfParagraph: i === 0,\n isLastLineOfParagraph: isLast,\n lineIndex: i,\n totalLines: lines.length,\n ...(isLast && keepNext ? { keepWithNext: true } : {}),\n };\n out.push(box);\n if (i < lines.length - 1) out.push({ type: \"glue\", height: 0 });\n }\n return out;\n}\n\nfunction singleBox(el: HTMLElement, extra: Partial<DomBox>): DomBox {\n return {\n type: \"box\",\n height: measureBlockHeight(el),\n el,\n isFirstLineOfParagraph: true,\n isLastLineOfParagraph: true,\n ...extra,\n };\n}\n\nfunction extraFlagsFor(el: HTMLElement): Partial<DomBox> {\n const tag = el.tagName.toLowerCase();\n const flags: Partial<DomBox> = {};\n if (/^h[1-6]$/.test(tag)) flags.keepWithNext = true;\n // Explicit AST-driven keepNext — stamped by the renderer when the\n // paragraph properties (or its resolved style cascade) say so. Matches\n // Word's `<w:keepNext/>` semantics.\n if (el.hasAttribute(\"data-keep-next\")) flags.keepWithNext = true;\n if (tag === \"pre\") flags.monolithic = true;\n return flags;\n}\n\nfunction measureBlockHeight(el: HTMLElement): number {\n // `offsetHeight` is logical — ignoring any CSS transform scale on an\n // ancestor viewport. `getBoundingClientRect` would return post-transform\n // pixels and mismatch the logical `pageContentHeight` budget.\n const cs = getComputedStyle(el);\n // Absolute / fixed positioning takes the element out of flow — it\n // doesn't push siblings down and shouldn't consume page budget.\n // jellap.docx's lifted text-box and shape-placeholder frames are\n // both absolute; without this skip the paginator counts the\n // placeholder's 47mm height twice (once via the absolute element,\n // once via the gap to its in-flow sibling), losing ~178px from the\n // page budget and triggering a spurious extra page.\n if (cs.position === \"absolute\" || cs.position === \"fixed\") return 0;\n const marginTop = Number.parseFloat(cs.marginTop) || 0;\n return el.offsetHeight + marginTop;\n}\n\nfunction isPageBreakMarker(el: HTMLElement): boolean {\n return el.classList.contains(\"page-break\") || el.hasAttribute(\"data-page-break\");\n}\n\n/**\n * Block elements with `data-page-break-before` (set by the renderer when\n * a paragraph has `pageBreakBefore: true`) want a forced break BEFORE\n * them. We emit a Penalty(-Infinity) before the element's normal box.\n */\nfunction hasPageBreakBefore(el: HTMLElement): boolean {\n return el.hasAttribute(\"data-page-break-before\");\n}\n\nfunction isKeepTogetherGroup(el: HTMLElement): boolean {\n if (el.tagName === \"FIGURE\") return true;\n if (el.classList.contains(\"keep-together\")) return true;\n if (el.hasAttribute(\"data-keep-together\")) return true;\n return false;\n}\n","import { nodeAtCharOffset } from \"./paragraphLines\";\n\n/**\n * Split `el` into two sibling elements at the given character offset. The\n * original keeps [0, charOffset); a new element (same tag + attributes) is\n * inserted immediately after and receives [charOffset, end). Returns the\n * new (bottom) element.\n *\n * Uses `Range.extractContents()` to move the tail into a fragment; preserves\n * element boundaries within the `<p>` (e.g. `<strong>`, `<code>` are cloned\n * if the split falls mid-element).\n */\nexport function splitElementAtCharOffset(el: HTMLElement, charOffset: number): HTMLElement {\n const pos = nodeAtCharOffset(el, charOffset);\n if (!pos) return el;\n\n const range = document.createRange();\n range.setStart(pos.node, pos.offset);\n range.setEnd(el, el.childNodes.length);\n\n const fragment = range.extractContents();\n\n const clone = document.createElement(el.tagName.toLowerCase());\n for (const attr of Array.from(el.attributes)) {\n clone.setAttribute(attr.name, attr.value);\n }\n clone.appendChild(fragment);\n\n // Tag the new fragment so distributePages can detect it. Used to\n // suppress the list marker on continuation `<li>` fragments\n // (matches Word's behaviour: a paragraph that splits inside a\n // numbered list shows the number ONLY on the head fragment, the\n // continuation flows on the next page without a marker).\n clone.dataset.pagContinuation = \"1\";\n\n el.parentNode?.insertBefore(clone, el.nextSibling);\n return clone;\n}\n\n/**\n * Move a split offset backward to the nearest word boundary (whitespace) so\n * words are never cut in half. We don't have a proper line-breaking engine,\n * so keeping words intact is the rule.\n *\n * Returns 0 if no whitespace is found before `offset` (rare — long single\n * word); callers should skip splitting in that case and let the paragraph\n * overflow rather than break mid-word.\n */\nexport function snapToWordBoundary(el: HTMLElement, offset: number): number {\n const text = el.textContent ?? \"\";\n for (let i = Math.min(offset, text.length) - 1; i >= 0; i--) {\n if (/\\s/.test(text[i] ?? \"\")) return i + 1;\n }\n return 0;\n}\n","import { measureParagraphLines } from \"./paragraphLines\";\nimport {\n snapToWordBoundary,\n splitElementAtCharOffset,\n} from \"./splitParagraph\";\nimport type { DomBox } from \"./types\";\nimport type { Page } from \"../../pagination/types\";\n\n/**\n * Turn the paginator's `Page[]` into a per-page array of DOM elements.\n *\n * Two flavours of split:\n *\n * - **Multi-line `<p>` straddling a page boundary**: split the `<p>`\n * at the character offset of the first line on the later page.\n * Original keeps the head fragment; a clone (same tag, attributes\n * preserved, data-pag-continuation stamped) holds the tail.\n * - **Multi-line `<li>` straddling a page boundary**: same machinery\n * — `<li>` is treated like `<p>` by `buildItems`, so the per-line\n * boxes route through `splitParagraphAcrossPages` too. Continuation\n * `<li>` fragments are tagged so CSS can hide their list marker.\n *\n * On top of per-element splits, `<li>` elements need re-wrapping into\n * per-page `<ol>` / `<ul>` clones (you can't have a bare `<li>` at\n * paper-content level — its marker won't render). The third pass\n * groups consecutive same-source LIs into per-page list-container\n * clones with the right `start` attribute so numbering continues\n * across page breaks.\n */\nexport function distributePages(pages: Page[]): HTMLElement[][] {\n // 1. Group line boxes by their originating <p> or <li>.\n const elementPages = new Map<HTMLElement, Map<number, number>>();\n for (let pageIdx = 0; pageIdx < pages.length; pageIdx++) {\n for (const it of pages[pageIdx]!.items) {\n if (it.type !== \"box\") continue;\n const b = it as DomBox;\n if (b.lineIndex === undefined) continue;\n let m = elementPages.get(b.el);\n if (!m) {\n m = new Map();\n elementPages.set(b.el, m);\n }\n m.set(b.lineIndex, pageIdx);\n }\n }\n\n // 2. Split each multi-page element at character offsets. Generic\n // over <p> vs <li> — splitElementAtCharOffset clones whatever\n // tag the source element has.\n const fragments = new Map<HTMLElement, Map<number, HTMLElement>>();\n for (const [original, lineMap] of elementPages) {\n fragments.set(original, splitParagraphAcrossPages(original, lineMap));\n }\n\n // 2b. Mark every non-final fragment of a multi-page element with\n // `sobree-fragment-continued`. The class triggers `text-align-last:\n // justify` in CSS so the visually-last line of each non-final\n // fragment is justified rather than left-aligned — matches Word's\n // rendering of a paragraph that splits across pages (in Word the\n // last line of the *logical* paragraph stays left-aligned, but\n // intermediate fragments' last lines justify because they're a\n // middle line of the source). Visual fidelity only; doesn't change\n // the browser's line-break decisions (verified empirically).\n markContinuedFragments(fragments);\n\n // 3. For every source <ol> / <ul> with split LIs, compute the\n // \"logical\" (user-visible) number of every LI fragment. This is\n // the value the per-page OL clone's `start` attribute uses.\n // Continuation fragments share the number of their head sibling.\n // Walk done BEFORE moving any LI out of the source OL.\n const liLogicalNumber = new Map<HTMLElement, number>();\n const seenSourceLists = new Set<HTMLElement>();\n for (const [original] of fragments) {\n if (original.tagName !== \"LI\") continue;\n const sourceList = original.parentElement;\n if (!sourceList || !isListContainer(sourceList)) continue;\n if (seenSourceLists.has(sourceList)) continue;\n seenSourceLists.add(sourceList);\n indexLogicalNumbers(sourceList, liLogicalNumber);\n }\n\n // 4. Walk pages, build per-page element lists, MOVING LIs into\n // freshly-cloned per-page OL/UL containers as we go. Track every\n // source list we touch so cleanup (step 5) can remove the ones\n // we emptied.\n const sourceListsTouched = new Set<HTMLElement>();\n const sourceTrsTouched = new Set<HTMLElement>();\n const result: HTMLElement[][] = [];\n for (let pageIdx = 0; pageIdx < pages.length; pageIdx++) {\n const seen = new Set<HTMLElement>();\n const pageElements: HTMLElement[] = [];\n let openContainerSource: HTMLElement | null = null;\n let openContainerClone: HTMLElement | null = null;\n // Track open per-page TR clone for row-content splitting (tall rows\n // emit one box per dominant-cell paragraph; each lands in this TR\n // clone's matching cell).\n let openTrSource: HTMLElement | null = null;\n let openTrClone: HTMLElement | null = null;\n\n for (const it of pages[pageIdx]!.items) {\n if (it.type !== \"box\") continue;\n const b = it as DomBox;\n let el: HTMLElement;\n if (b.lineIndex !== undefined) {\n el = fragments.get(b.el)?.get(pageIdx) ?? b.el;\n } else {\n el = b.el;\n }\n if (seen.has(el)) continue;\n seen.add(el);\n\n // Row-content split: paragraph-box from a tall TR. Open / reuse\n // a per-page TR clone and route the paragraph into its matching\n // cell. The TR clone gets all source cells empty; on the FIRST\n // paragraph of the row (`isFirstParaOfRow`) we also move every\n // non-dominant cell's content into the matching cell so labels\n // appear at the top of the row's first occurrence.\n if (b.cellTr) {\n const tr = b.cellTr;\n const sourceTable = tr.closest(\"table\") as HTMLElement | null;\n const sourceTbody = sourceTable?.querySelector(\":scope > tbody\") as HTMLElement | null;\n if (!sourceTable || !sourceTbody) {\n pageElements.push(el);\n openContainerSource = null;\n openContainerClone = null;\n openTrSource = null;\n openTrClone = null;\n continue;\n }\n if (sourceTable !== openContainerSource) {\n openContainerClone = cloneTableContainer(sourceTable);\n pageElements.push(openContainerClone);\n openContainerSource = sourceTable;\n sourceListsTouched.add(sourceTable);\n openTrSource = null;\n openTrClone = null;\n }\n if (tr !== openTrSource) {\n // Build a TR clone with the SAME number of empty cells as the\n // source row (preserves column structure / widths via attrs).\n openTrClone = cloneEmptyRow(tr);\n const cloneTbody = openContainerClone!.querySelector(\":scope > tbody\")!;\n cloneTbody.appendChild(openTrClone);\n openTrSource = tr;\n sourceTrsTouched.add(tr);\n }\n // Find which cell owns the paragraph in the source TR.\n const sourceCell = el.closest(\"td, th\") as HTMLElement | null;\n if (!sourceCell) {\n // Defensive: paragraph isn't actually in a cell. Put it on\n // pageElements as a stand-alone block so it doesn't get lost.\n pageElements.push(el);\n continue;\n }\n const cellChildren = Array.from(tr.children) as HTMLElement[];\n const cellIdx = cellChildren.indexOf(sourceCell);\n const targetCell = openTrClone!.children[cellIdx] as HTMLElement | undefined;\n if (!targetCell) {\n pageElements.push(el);\n continue;\n }\n // For the FIRST paragraph of the row, also move every\n // non-dominant cell's content into the row clone. The label\n // cell's content lands on the first fragment only — subsequent\n // fragments have empty label cells, which is the right visual\n // semantics (label appears once at top of row).\n if (b.isFirstParaOfRow) {\n for (let i = 0; i < cellChildren.length; i++) {\n if (i === cellIdx) continue;\n const srcCell = cellChildren[i]!;\n const tgtCell = openTrClone!.children[i] as HTMLElement;\n while (srcCell.firstChild) tgtCell.appendChild(srcCell.firstChild);\n }\n }\n targetCell.appendChild(el);\n continue;\n }\n if (el.tagName === \"LI\") {\n const sourceList = el.parentElement;\n if (!sourceList || !isListContainer(sourceList)) {\n pageElements.push(el);\n openContainerSource = null;\n openContainerClone = null;\n continue;\n }\n\n if (sourceList !== openContainerSource) {\n const startNum =\n liLogicalNumber.get(el) ?? readStartAttr(sourceList);\n openContainerClone = cloneListContainer(sourceList, startNum);\n pageElements.push(openContainerClone);\n openContainerSource = sourceList;\n sourceListsTouched.add(sourceList);\n }\n\n // Continuation LIs (those carrying the data-pag-continuation\n // stamp from splitElementAtCharOffset) get a class so CSS can\n // hide their marker. Heads keep their default marker.\n if (el.dataset.pagContinuation === \"1\") {\n el.classList.add(\"sobree-li-continuation\");\n } else {\n el.classList.remove(\"sobree-li-continuation\");\n }\n\n openContainerClone!.appendChild(el);\n } else if (el.tagName === \"TR\" && !b.cellTr) {\n // Table rows go into per-page `<table><tbody>` clones, mirroring\n // the LI → OL/UL pattern. Without this, a `<table>` that doesn't\n // fit on one page either lands wholly on the next page (when\n // smaller than one page) or overflows visibly (when larger),\n // hiding most of the resume body. Splitting by row lets the\n // table flow naturally across pages.\n const sourceSection = el.parentElement;\n const sourceTable = sourceSection?.closest(\"table\") as HTMLElement | null;\n if (!sourceTable) {\n pageElements.push(el);\n openContainerSource = null;\n openContainerClone = null;\n continue;\n }\n\n if (sourceTable !== openContainerSource) {\n openContainerClone = cloneTableContainer(sourceTable);\n pageElements.push(openContainerClone);\n openContainerSource = sourceTable;\n sourceListsTouched.add(sourceTable);\n }\n\n // Route THEAD-sourced TRs to the clone's THEAD; TBODY-sourced\n // TRs (the common case) to the clone's TBODY. The clone always\n // has a TBODY; THEAD is created on demand when first needed.\n const isHeader = sourceSection?.tagName === \"THEAD\";\n let target: Element;\n if (isHeader) {\n let cloneThead = openContainerClone!.querySelector(\":scope > thead\");\n if (!cloneThead) {\n cloneThead = document.createElement(\"thead\");\n openContainerClone!.insertBefore(cloneThead, openContainerClone!.firstChild);\n }\n target = cloneThead;\n } else {\n target = openContainerClone!.querySelector(\":scope > tbody\") ?? openContainerClone!;\n }\n target.appendChild(el);\n } else {\n pageElements.push(el);\n openContainerSource = null;\n openContainerClone = null;\n }\n }\n\n result.push(pageElements);\n }\n\n // 5. Cleanup: source OL / UL / TABLE elements whose LIs / TRs all\n // got moved into per-page clones are left empty (or with an\n // empty TBODY in the TABLE case). Remove them — they're no\n // longer part of any page's content. `mergeConsecutiveFragments`\n // would absorb them on the next pass anyway, but cleaning up\n // here keeps the post-distribute DOM tidy and keeps\n // measurement-time inspection from picking up ghosts.\n // Empty source TRs first (row-content split moved all cell content\n // to per-page clones). Must precede source-list / source-table\n // cleanup so the now-empty source table is correctly detected.\n for (const tr of sourceTrsTouched) {\n const allEmpty = Array.from(tr.children).every(\n (cell) => cell.tagName !== \"TD\" && cell.tagName !== \"TH\"\n ? true\n : cell.children.length === 0 && (cell.textContent ?? \"\").trim() === \"\",\n );\n if (allEmpty && tr.parentElement) {\n tr.parentElement.removeChild(tr);\n }\n }\n for (const source of sourceListsTouched) {\n if (source.tagName === \"TABLE\") {\n // Table is \"empty\" iff every THEAD and TBODY contains no rows.\n const sections = Array.from(source.children).filter(\n (c) => c.tagName === \"THEAD\" || c.tagName === \"TBODY\",\n );\n const hasAnyRow = sections.some((s) => s.children.length > 0)\n || (sections.length === 0 && source.children.length > 0);\n if (!hasAnyRow && source.parentElement) {\n source.parentElement.removeChild(source);\n }\n } else if (source.children.length === 0 && source.parentElement) {\n source.parentElement.removeChild(source);\n }\n }\n\n return result;\n}\n\n// ---------- helpers ----------\n\n/**\n * Add `sobree-fragment-continued` to every fragment that has a *later*\n * sibling fragment of the same logical element. The class drives\n * `text-align-last: justify` — matches Word, which justifies the\n * last line of intermediate fragments (the actual-last-line-of-source\n * left-aligns as normal).\n */\nfunction markContinuedFragments(\n fragments: Map<HTMLElement, Map<number, HTMLElement>>,\n): void {\n for (const [, pageMap] of fragments) {\n const orderedPages = Array.from(pageMap.keys()).sort((a, b) => a - b);\n // Every fragment except the one on the highest page is \"continued\".\n for (let i = 0; i < orderedPages.length - 1; i++) {\n const frag = pageMap.get(orderedPages[i]!);\n if (frag) frag.classList.add(\"sobree-fragment-continued\");\n }\n }\n}\n\nfunction isListContainer(el: Element): boolean {\n return el.tagName === \"OL\" || el.tagName === \"UL\";\n}\n\n/**\n * Clone a `<table>` (and its single `<tbody>`) for a per-page split.\n * Preserves attributes (data-block-id, data-section-index, class,\n * style, …) and `data-pag-tid` so `mergeConsecutiveFragments` can\n * rejoin the split table on the next pagination pass. The clone\n * starts with an empty TBODY; rows are appended in the distribute\n * walk.\n *\n * THEAD / COLGROUP would need to be cloned wholesale (not stripped)\n * so per-page tables retain column widths + repeating headers. Not yet\n * exercised by the corpus — when a fixture demands it, copy them here\n * in source order before the empty TBODY.\n */\n/**\n * Clone a `<tr>` with empty cells preserving structure. Each TD / TH\n * is cloned shallow (attributes only, no children) — paragraph boxes\n * from `tallRowParagraphBoxes` will append into the matching cell.\n * Used for row-content splitting where a single source TR's content\n * flows across multiple per-page TR clones.\n */\nfunction cloneEmptyRow(source: HTMLElement): HTMLElement {\n const trClone = document.createElement(\"tr\");\n for (const attr of Array.from(source.attributes)) {\n trClone.setAttribute(attr.name, attr.value);\n }\n for (const cell of Array.from(source.children)) {\n if (cell.tagName !== \"TD\" && cell.tagName !== \"TH\") continue;\n const cellClone = document.createElement(cell.tagName.toLowerCase());\n for (const attr of Array.from(cell.attributes)) {\n cellClone.setAttribute(attr.name, attr.value);\n }\n trClone.appendChild(cellClone);\n }\n return trClone;\n}\n\nfunction cloneTableContainer(source: HTMLElement): HTMLElement {\n const clone = document.createElement(\"table\");\n for (const attr of Array.from(source.attributes)) {\n clone.setAttribute(attr.name, attr.value);\n }\n const sourceTbody = source.querySelector(\":scope > tbody\");\n if (sourceTbody) {\n const tbodyClone = document.createElement(\"tbody\");\n for (const attr of Array.from(sourceTbody.attributes)) {\n tbodyClone.setAttribute(attr.name, attr.value);\n }\n clone.appendChild(tbodyClone);\n } else {\n // Source lacked an explicit tbody (rare; browsers usually add one).\n // Still emit one in the clone so row appends land in valid HTML.\n clone.appendChild(document.createElement(\"tbody\"));\n }\n return clone;\n}\n\nfunction readStartAttr(list: Element): number {\n const raw = list.getAttribute(\"start\");\n if (!raw) return 1;\n const n = Number.parseInt(raw, 10);\n return Number.isFinite(n) ? n : 1;\n}\n\n/**\n * Walk the source OL / UL's LI children and assign each a \"logical\n * number\" — the user-visible number that LI would carry in the\n * un-paginated source list. Continuation fragments (sharing\n * `data-pag-pid` with their preceding sibling) get the SAME number as\n * the head; new heads bump the counter.\n */\nfunction indexLogicalNumbers(\n sourceList: HTMLElement,\n out: Map<HTMLElement, number>,\n): void {\n let logical = readStartAttr(sourceList);\n let lastPid: string | undefined;\n let firstSeen = false;\n for (const child of Array.from(sourceList.children)) {\n if (child.tagName !== \"LI\") continue;\n const li = child as HTMLElement;\n const pid = li.dataset.pagPid;\n if (firstSeen) {\n if (!pid || pid !== lastPid) logical += 1;\n } else {\n firstSeen = true;\n }\n out.set(li, logical);\n lastPid = pid;\n }\n}\n\n/**\n * Make a per-page clone of an OL / UL. Copies all attributes except\n * `start` (which we override with `startNum` so numbering continues\n * across page breaks). The clone shares `data-pag-lid` with the\n * source so `mergeConsecutiveFragments` can rejoin them on the next\n * pagination pass.\n */\nfunction cloneListContainer(source: HTMLElement, startNum: number): HTMLElement {\n const clone = document.createElement(source.tagName.toLowerCase());\n for (const attr of Array.from(source.attributes)) {\n if (attr.name === \"start\") continue;\n clone.setAttribute(attr.name, attr.value);\n }\n if (source.tagName === \"OL\") {\n clone.setAttribute(\"start\", String(startNum));\n }\n return clone;\n}\n\n/**\n * If `original` spans multiple pages, split the element at each\n * boundary. Returns a map: pageIdx → fragment element on that page.\n *\n * Generic over `<p>` and `<li>` — both are split via\n * `splitElementAtCharOffset` which clones whichever tag the source\n * has. Continuation fragments carry `data-pag-continuation=\"1\"`\n * (stamped by `splitElementAtCharOffset`) so the LI continuation\n * styling fires for list items but not for paragraphs.\n */\nfunction splitParagraphAcrossPages(\n original: HTMLElement,\n lineMap: Map<number, number>,\n): Map<number, HTMLElement> {\n const orderedLines = Array.from(lineMap.keys()).sort((a, b) => a - b);\n const result = new Map<number, HTMLElement>();\n\n // Find lines at which the page changes; these are split points.\n const splitLines: number[] = [];\n let prevPage = lineMap.get(orderedLines[0]!)!;\n for (let i = 1; i < orderedLines.length; i++) {\n const line = orderedLines[i]!;\n const page = lineMap.get(line)!;\n if (page !== prevPage) splitLines.push(line);\n prevPage = page;\n }\n\n result.set(lineMap.get(orderedLines[0]!)!, original);\n if (splitLines.length === 0) return result;\n\n // Perform splits in order. After each split, subsequent splits\n // operate on the new tail element (which still contains all later\n // lines).\n let currentFragment = original;\n let currentFragmentStartLine = 0;\n for (const splitLine of splitLines) {\n const localLine = splitLine - currentFragmentStartLine;\n const metrics = measureParagraphLines(currentFragment);\n const rawOffset = metrics[localLine]?.startCharOffset;\n if (rawOffset === undefined || rawOffset === 0) continue;\n // Never split a word — snap backward to the nearest whitespace.\n // If there isn't one (very long single token), skip this split\n // and let the page overflow rather than break the word.\n const offset = snapToWordBoundary(currentFragment, rawOffset);\n if (offset === 0) continue;\n const tail = splitElementAtCharOffset(currentFragment, offset);\n result.set(lineMap.get(splitLine)!, tail);\n currentFragment = tail;\n currentFragmentStartLine = splitLine;\n }\n\n return result;\n}\n","import type { Box, Candidate, Item, ResolvedConfig } from \"./types\";\n\n/**\n * Score a candidate break. Lower is better.\n *\n * totalCost = underfullWeight * leftover²\n * + own penalty cost\n * + widow/orphan penalty\n * + keep-with-next penalty\n * + keep-together penalty\n *\n * `start` is the index of the first item on the current page — needed so we\n * only count paragraph lines that live on this page (not on earlier ones).\n */\nexport function scoreBreak(\n items: Item[],\n start: number,\n c: Candidate,\n cfg: ResolvedConfig,\n): number {\n const leftover = cfg.pageHeight - c.heightAt;\n const underfull = cfg.underfullWeight * leftover * leftover;\n return (\n underfull +\n c.ownCost +\n widowOrphanPenalty(items, start, c, cfg) +\n keepWithNextPenalty(items, c) +\n keepTogetherPenalty(items, c, cfg)\n );\n}\n\n/** Penalty for breaking inside a paragraph and leaving a widow or orphan. */\nexport function widowOrphanPenalty(\n items: Item[],\n start: number,\n c: Candidate,\n cfg: ResolvedConfig,\n): number {\n const before = nearestBoxBefore(items, c.pageEnd);\n const after = nearestBoxAfter(items, c.nextStart);\n if (!before || !after) return 0;\n if (!before.paragraphId || before.paragraphId !== after.paragraphId) return 0;\n\n const pid = before.paragraphId;\n const linesAbove = countParagraphLinesOnPageBefore(items, start, c.pageEnd, pid);\n const linesBelow = countParagraphLinesOnNextPage(items, c.nextStart, pid);\n if (linesAbove < cfg.orphans || linesBelow < cfg.widows) return cfg.widowOrphanPenalty;\n return 0;\n}\n\n/**\n * +Infinity if the break is between a keepWithNext paragraph and whatever\n * follows. The only case we *don't* forbid is when the break is between two\n * lines of the same paragraph (widow/orphan's concern, not keep-with-next's).\n */\nexport function keepWithNextPenalty(items: Item[], c: Candidate): number {\n const before = nearestBoxBefore(items, c.pageEnd);\n const after = nearestBoxAfter(items, c.nextStart);\n if (!before || !after) return 0;\n if (!before.keepWithNext) return 0;\n // Same-paragraph break (both sides share a non-empty paragraphId) is an\n // internal line break — keep-with-next doesn't apply.\n if (before.paragraphId && before.paragraphId === after.paragraphId) return 0;\n return Number.POSITIVE_INFINITY;\n}\n\n/** Penalty for breaking inside a keepTogether paragraph. */\nexport function keepTogetherPenalty(items: Item[], c: Candidate, cfg: ResolvedConfig): number {\n const before = nearestBoxBefore(items, c.pageEnd);\n const after = nearestBoxAfter(items, c.nextStart);\n if (!before || !after) return 0;\n if (before.paragraphId && before.paragraphId === after.paragraphId && before.keepTogether) {\n return cfg.keepPenalty;\n }\n return 0;\n}\n\n// ---------- stream queries ----------\n\nexport function nearestBoxBefore(items: Item[], idxExclusive: number): Box | null {\n for (let i = idxExclusive - 1; i >= 0; i--) {\n const it = items[i];\n if (it && it.type === \"box\") return it;\n }\n return null;\n}\n\nexport function nearestBoxAfter(items: Item[], idxInclusive: number): Box | null {\n for (let i = idxInclusive; i < items.length; i++) {\n const it = items[i];\n if (it && it.type === \"box\") return it;\n }\n return null;\n}\n\nexport function countParagraphLinesOnPageBefore(\n items: Item[],\n start: number,\n pageEnd: number,\n pid: string,\n): number {\n let count = 0;\n for (let i = pageEnd - 1; i >= start; i--) {\n const it = items[i];\n if (!it) continue;\n if (it.type !== \"box\") continue;\n if (it.paragraphId === pid) count++;\n else break;\n }\n return count;\n}\n\nexport function countParagraphLinesOnNextPage(\n items: Item[],\n nextStart: number,\n pid: string,\n): number {\n let count = 0;\n for (let i = nextStart; i < items.length; i++) {\n const it = items[i];\n if (!it) continue;\n if (it.type !== \"box\") continue;\n if (it.paragraphId === pid) count++;\n else break;\n }\n return count;\n}\n\n/** Sum of box + inter-line glue heights for the paragraph beginning at startIdx. */\nexport function sumParagraphHeight(items: Item[], startIdx: number): number {\n const first = items[startIdx];\n if (!first || first.type !== \"box\" || !first.paragraphId) {\n return first && \"height\" in first ? first.height : 0;\n }\n const pid = first.paragraphId;\n let h = 0;\n let i = startIdx;\n while (i < items.length) {\n const it = items[i];\n if (!it) break;\n if (it.type === \"box\") {\n if (it.paragraphId === pid) {\n h += it.height;\n i++;\n } else break;\n } else if (it.type === \"glue\") {\n const next = nearestBoxAfter(items, i + 1);\n if (next && next.paragraphId === pid) {\n h += it.height;\n i++;\n } else break;\n } else {\n i++; // penalty, no height\n }\n }\n return h;\n}\n\nexport function isGlueBetweenBoxes(items: Item[], idx: number): boolean {\n const before = nearestBoxBefore(items, idx);\n const after = nearestBoxAfter(items, idx + 1);\n return Boolean(before && after);\n}\n\nexport function isGlueInsideKeepTogether(items: Item[], idx: number): boolean {\n const before = nearestBoxBefore(items, idx);\n const after = nearestBoxAfter(items, idx + 1);\n if (!before || !after) return false;\n if (!before.paragraphId || before.paragraphId !== after.paragraphId) return false;\n return Boolean(before.keepTogether);\n}\n","import { widowOrphanPenalty } from \"./cost\";\nimport type { Candidate, Item, ResolvedConfig } from \"./types\";\n\n/**\n * If the chosen break still violates widow/orphan (despite the penalty in\n * scoring), walk the candidate list backward and pick the nearest earlier\n * candidate that doesn't violate — \"back off the break by one line\" in the\n * spec — effectively one paragraph line moves to the next page.\n *\n * Returns the original `best` if no earlier candidate avoids the violation.\n */\nexport function backOffIfViolates(\n items: Item[],\n start: number,\n best: Candidate,\n candidates: Candidate[],\n cfg: ResolvedConfig,\n): Candidate {\n if (widowOrphanPenalty(items, start, best, cfg) === 0) return best;\n\n const bestPos = candidates.indexOf(best);\n for (let i = bestPos - 1; i >= 0; i--) {\n const c = candidates[i];\n if (!c) continue;\n if (widowOrphanPenalty(items, start, c, cfg) === 0) return c;\n }\n return best;\n}\n","/** A single line or image-like unit. Contributes height. */\nexport interface Box {\n type: \"box\";\n height: number;\n /** Identifier shared by all lines of the same paragraph. */\n paragraphId?: string;\n isFirstLineOfParagraph?: boolean;\n isLastLineOfParagraph?: boolean;\n /** If true, the box is never split across a page break. */\n monolithic?: boolean;\n /** Paragraph-level: this paragraph must stay adjacent to the next. */\n keepWithNext?: boolean;\n /** Paragraph-level: all lines of this paragraph must fit on one page. */\n keepTogether?: boolean;\n}\n\n/** Whitespace. Discarded at page edges when it ends up there. */\nexport interface Glue {\n type: \"glue\";\n height: number;\n}\n\n/**\n * A break point annotation with a finite cost, or a forced break\n * (-Infinity), or a forbidden break (+Infinity).\n */\nexport interface Penalty {\n type: \"penalty\";\n cost: number;\n}\n\nexport type Item = Box | Glue | Penalty;\n\nexport interface Config {\n /** Default page height in px. Used for any page index not covered\n * by `pageHeights`. */\n pageHeight: number;\n /**\n * Per-page height overrides. Entry `i` is the budget for page index\n * `i`; missing/undefined entries fall back to `pageHeight`. Lets\n * callers shrink the body budget on specific pages (e.g. pages with\n * footnote zones eating space at the bottom) without penalising\n * pages that don't need it.\n */\n pageHeights?: readonly number[];\n /** Minimum lines of a paragraph on the new page. Default 2. */\n widows?: number;\n /** Minimum lines of a paragraph on the current page. Default 2. */\n orphans?: number;\n /** Multiplier for underfull penalty (leftover²). Default 1.0. */\n underfullWeight?: number;\n /** Added when a candidate break would leave a widow or orphan. Default 10000. */\n widowOrphanPenalty?: number;\n /** Added to breaks inside keepTogether ranges; used for keep-with-next. Default 10000. */\n keepPenalty?: number;\n}\n\nexport interface ResolvedConfig {\n pageHeight: number;\n pageHeights?: readonly number[];\n widows: number;\n orphans: number;\n underfullWeight: number;\n widowOrphanPenalty: number;\n keepPenalty: number;\n}\n\nexport interface Page {\n items: Item[];\n /** Total box + glue height, excluding trailing glue. */\n usedHeight: number;\n /** Cost of the chosen break ending this page; 0 for forced / end-of-stream. */\n cost: number;\n}\n\n/**\n * Candidate break position.\n *\n * `pageEnd` is the exclusive upper bound of the current page's items — on a\n * glue break the glue is INCLUDED (trailing glue stays on the current page\n * for round-tripping), so pageEnd = glueIdx + 1. On a penalty break the\n * penalty is EXCLUDED (consumed), so pageEnd = penaltyIdx.\n *\n * `nextStart` is the first item of the next page. On a penalty break it is\n * penaltyIdx + 1 (the penalty is consumed). On a glue break it equals\n * pageEnd.\n *\n * `heightAt` is the accumulated page height just *before* the candidate item\n * — glue is not counted against the page's useful height.\n */\nexport interface Candidate {\n pageEnd: number;\n nextStart: number;\n heightAt: number;\n ownCost: number;\n}\n\nexport const DEFAULTS: Omit<ResolvedConfig, \"pageHeight\"> = {\n // Word's default \"Widow/Orphan Control\" allows a single line of a\n // paragraph on either side of a page break. We mirror that — setting\n // widows / orphans to 1 means \"no special protection against\n // 1-line widows or orphans, accept whatever break the underfull\n // cost prefers\". Match Word's behaviour for the typical case where\n // a long paragraph straddles a page boundary mid-sentence.\n widows: 1,\n orphans: 1,\n underfullWeight: 1.0,\n // Penalty still in place for the explicit cases — e.g. a forced break\n // would still leave NO lines on either side, which is forbidden.\n widowOrphanPenalty: 10000,\n keepPenalty: 10000,\n};\n\nexport function resolveConfig(cfg: Config): ResolvedConfig {\n return {\n pageHeight: cfg.pageHeight,\n ...(cfg.pageHeights ? { pageHeights: cfg.pageHeights } : {}),\n widows: cfg.widows ?? DEFAULTS.widows,\n orphans: cfg.orphans ?? DEFAULTS.orphans,\n underfullWeight: cfg.underfullWeight ?? DEFAULTS.underfullWeight,\n widowOrphanPenalty: cfg.widowOrphanPenalty ?? DEFAULTS.widowOrphanPenalty,\n keepPenalty: cfg.keepPenalty ?? DEFAULTS.keepPenalty,\n };\n}\n\n/** Effective page budget for `pageIdx` — per-page override if present,\n * global `pageHeight` otherwise. */\nexport function pageHeightAt(cfg: ResolvedConfig, pageIdx: number): number {\n return cfg.pageHeights?.[pageIdx] ?? cfg.pageHeight;\n}\n","import {\n isGlueBetweenBoxes,\n isGlueInsideKeepTogether,\n scoreBreak,\n sumParagraphHeight,\n} from \"./cost\";\nimport { backOffIfViolates } from \"./postConditions\";\nimport {\n type Candidate,\n type Config,\n type Item,\n type Page,\n type ResolvedConfig,\n resolveConfig,\n pageHeightAt,\n} from \"./types\";\n\n/**\n * Greedy one-pass paginator. See README.md for the full cost model.\n *\n * Walk the item stream, accumulating page height and a list of candidate\n * break positions. When the next item overflows — or a forced penalty\n * appears — pick the candidate that minimises totalCost (see scoreBreak),\n * emit the page, and continue from the chosen break.\n */\nexport function paginate(items: Item[], config: Config): Page[] {\n const cfg = resolveConfig(config);\n const pages: Page[] = [];\n let start = 0;\n while (start < items.length) {\n // Each page sees its own `pageHeight` — either the per-page\n // override from `cfg.pageHeights[pageIdx]` or the global default.\n // We pass a one-shot resolved config to fillPage rather than\n // threading a page-index parameter through every helper.\n const pageCfg: ResolvedConfig = { ...cfg, pageHeight: pageHeightAt(cfg, pages.length) };\n const result = fillPage(items, start, pageCfg);\n pages.push(result.page);\n if (result.next <= start) break; // paranoia — should never happen\n start = result.next;\n }\n return pages;\n}\n\ninterface FillResult {\n page: Page;\n next: number;\n}\n\nfunction fillPage(items: Item[], start: number, cfg: ResolvedConfig): FillResult {\n let h = 0;\n const candidates: Candidate[] = [];\n let idx = start;\n\n while (idx < items.length) {\n const it = items[idx];\n if (!it) break;\n\n if (it.type === \"penalty\") {\n if (it.cost === Number.NEGATIVE_INFINITY) {\n // Forced break: current page ends before this penalty; the penalty\n // itself is consumed and doesn't appear on either page.\n return emit(items, start, idx, 0, idx + 1);\n }\n if (Number.isFinite(it.cost)) {\n candidates.push({ pageEnd: idx, nextStart: idx + 1, heightAt: h, ownCost: it.cost });\n }\n // +Infinity penalties are never candidates and don't contribute height.\n idx++;\n continue;\n }\n\n if (it.type === \"glue\") {\n if (isGlueBetweenBoxes(items, idx) && !isGlueInsideKeepTogether(items, idx)) {\n // Glue break: glue STAYS on current page as trailing — so pageEnd\n // is one past the glue. heightAt excludes the glue's height so\n // leftover reflects usable page space, not trailing whitespace.\n candidates.push({\n pageEnd: idx + 1,\n nextStart: idx + 1,\n heightAt: h,\n ownCost: 0,\n });\n }\n if (h + it.height > cfg.pageHeight) {\n return pickAndEmit(items, start, idx, candidates, cfg);\n }\n h += it.height;\n idx++;\n continue;\n }\n\n // box\n const box = it;\n\n // Oversized monolithic item with room used: force a break before it.\n if (box.monolithic && h + box.height > cfg.pageHeight && idx > start) {\n return pickAndEmit(items, start, idx, candidates, cfg);\n }\n\n // Oversized item on an otherwise empty page: place alone and let it overflow.\n if (box.height > cfg.pageHeight && idx === start) {\n // eslint-disable-next-line no-console\n console.warn(\n `paginate: item (height ${box.height}) exceeds pageHeight (${cfg.pageHeight}). Placing alone; page will overflow.`,\n );\n return {\n page: { items: [box], usedHeight: box.height, cost: 0 },\n next: idx + 1,\n };\n }\n\n // keepTogether: if the whole paragraph can't fit in remaining space,\n // break before it (so it starts fresh on the next page). If it can't\n // fit on a full page at all, warn and fall through.\n if (box.isFirstLineOfParagraph && box.keepTogether) {\n const paraH = sumParagraphHeight(items, idx);\n if (paraH > cfg.pageHeight) {\n // eslint-disable-next-line no-console\n console.warn(\n `paginate: keepTogether paragraph (height ${paraH}) exceeds pageHeight (${cfg.pageHeight}). Falling back to normal breaking.`,\n );\n // fall through\n } else if (h + paraH > cfg.pageHeight && idx > start) {\n return pickAndEmit(items, start, idx, candidates, cfg);\n }\n }\n\n // Generic overflow: adding this box would exceed page height.\n if (h + box.height > cfg.pageHeight && idx > start) {\n return pickAndEmit(items, start, idx, candidates, cfg);\n }\n\n h += box.height;\n idx++;\n }\n\n // End of stream.\n return emit(items, start, items.length, 0, items.length);\n}\n\nfunction pickAndEmit(\n items: Item[],\n start: number,\n overflowIdx: number,\n candidates: Candidate[],\n cfg: ResolvedConfig,\n): FillResult {\n if (candidates.length === 0) {\n // No candidates: force break at overflow position.\n return emit(items, start, overflowIdx, 0, overflowIdx);\n }\n\n // Score and pick best.\n let best: Candidate = candidates[0] as Candidate;\n let bestCost = scoreBreak(items, start, best, cfg);\n for (let i = 1; i < candidates.length; i++) {\n const c = candidates[i];\n if (!c) continue;\n const cost = scoreBreak(items, start, c, cfg);\n if (cost < bestCost) {\n best = c;\n bestCost = cost;\n }\n }\n\n // Post-condition: back off by one line if widow/orphan still violated.\n const adjusted = backOffIfViolates(items, start, best, candidates, cfg);\n const adjustedCost = adjusted === best ? bestCost : scoreBreak(items, start, adjusted, cfg);\n\n return emit(items, start, adjusted.pageEnd, adjustedCost, adjusted.nextStart);\n}\n\nfunction emit(\n items: Item[],\n start: number,\n endExclusive: number,\n cost: number,\n next: number,\n): FillResult {\n const pageItems = items.slice(start, endExclusive);\n return {\n page: {\n items: pageItems,\n usedHeight: computeUsedHeight(pageItems),\n cost,\n },\n next,\n };\n}\n\n/**\n * Total height of a page: sum of box + glue heights, but drop trailing glue\n * (glue items after the last box on the page). Penalties never contribute.\n */\nfunction computeUsedHeight(pageItems: Item[]): number {\n let lastBoxIdx = -1;\n for (let i = pageItems.length - 1; i >= 0; i--) {\n if (pageItems[i]?.type === \"box\") {\n lastBoxIdx = i;\n break;\n }\n }\n if (lastBoxIdx < 0) return 0;\n let h = 0;\n for (let i = 0; i <= lastBoxIdx; i++) {\n const it = pageItems[i];\n if (!it) continue;\n if (it.type === \"box\" || it.type === \"glue\") h += it.height;\n }\n return h;\n}\n","import { buildItems } from \"./buildItems\";\nimport { distributePages } from \"./distribute\";\nimport { paginate } from \"../../pagination\";\n\n/**\n * DOM → pagination-engine adapter for PaperStack.\n *\n * 1. `buildItems` walks the blocks and emits the Item stream — one Box\n * per block (or one per paragraph line), plus penalties for page-break\n * markers and monolithic flags for figures / keep-together groups.\n * 2. `paginate` (the pure engine in `src/pagination`) produces Pages.\n * 3. `distributePages` turns Pages into a per-page list of DOM elements,\n * physically splitting paragraphs that straddle page boundaries.\n *\n * Features honoured:\n * - Widow/orphan at the line level for `<p>` elements.\n * - Keep-with-next for headings (h1–h6).\n * - Keep-together for `<figure>`, `.keep-together`, `[data-keep-together]`.\n * - Forced page breaks for `.page-break` / `[data-page-break]` markers.\n * - Tables and code blocks remain monolithic.\n */\nexport function paginateBlocks(\n blocks: HTMLElement[],\n pageContentHeightPx: number,\n pageHeightsPx?: readonly number[],\n): HTMLElement[][] {\n if (blocks.length === 0) return [];\n const items = buildItems(blocks);\n const pages = paginate(items, {\n pageHeight: pageContentHeightPx,\n ...(pageHeightsPx ? { pageHeights: pageHeightsPx } : {}),\n });\n return distributePages(pages);\n}\n","/**\n * Build a CSS `font-family` value with metric-compatible fallbacks.\n *\n * Why: when a docx specifies `font-family: \"Bookman Old Style\"` and\n * the host machine doesn't have it installed, the browser picks an\n * arbitrary system serif. The fallback may have a much taller natural\n * ascender+descender than the requested font — and CSS's line-box\n * height = `max(line-height, font-strut-height)`, so the line box\n * inflates beyond the declared `line-height`. That cumulative inflation\n * shifts pagination decisions (a single docx ends up taking more pages\n * than Word produces).\n *\n * The fix: emit a fallback chain whose members have metrics close to\n * the primary font. Georgia is metrically near Bookman; Helvetica Neue\n * near Calibri; etc. The chain ends in a generic family so we never\n * fall off the cliff into the browser's last-resort serif/sans pick.\n *\n * Diagnosis trail: per-block height inflation surfaced via\n * `pnpm fixtures:compare --pages` on user-contract.docx (Bookman not\n * installed on macOS by default). See [diagnosis notes].\n */\n\n/** Hand-curated fallback chains. Each entry's first member is the\n * primary font; the chain ends in a generic family. Keys are matched\n * case-insensitively against the primary font's name.\n *\n * Multi-word names are wrapped in *single* quotes (CSS accepts either)\n * so the inline-style string can later be embedded in JSON / snapshot\n * files using double-quotes without needing escapes. */\nconst CHAINS: Array<{ match: RegExp; chain: string }> = [\n // Calibri & relatives — modern sans-serif.\n //\n // Carlito is Google's open-source, metric-compatible clone of\n // Calibri (Google Fonts SIL OFL). Linux distros + LibreOffice\n // bundle it, and it's the only fallback whose U+2026 (…) glyph\n // matches Calibri's narrow width. Without Carlito the chain\n // falls through to Helvetica/Arial, whose ellipsis glyphs are\n // ~2× wider — long dot-leader runs in CV templates (\"……………\")\n // then overflow the line. Carlito sits BEFORE Helvetica Neue so\n // any host with Carlito installed gets Word-fidelity rendering.\n { match: /^calibri light$/i, chain: `'Calibri Light', Carlito, 'Helvetica Neue', 'Helvetica Light', Helvetica, Arial, sans-serif` },\n { match: /^calibri$/i, chain: `Calibri, Carlito, 'Helvetica Neue', Helvetica, Arial, sans-serif` },\n // Serif families. Chain ends at Times for missing-font fallback\n // because Word and LibreOffice both substitute unknown serif fonts\n // with Times by default — putting Georgia or Hoefler ahead would\n // give visually different rendering than the user's Word, even\n // though Georgia is metrically closer to Bookman. Documents look\n // the same in Sobree and Word when the requested font is missing.\n { match: /^bookman old style$/i, chain: `'Bookman Old Style', Bookman, 'URW Bookman L', 'Times New Roman', serif` },\n { match: /^cambria$/i, chain: `Cambria, 'Times New Roman', serif` },\n { match: /^times new roman$/i, chain: `'Times New Roman', Times, serif` },\n { match: /^georgia$/i, chain: `Georgia, 'Times New Roman', serif` },\n // Common sans-serif neighbours.\n { match: /^arial$/i, chain: `Arial, 'Helvetica Neue', Helvetica, sans-serif` },\n { match: /^helvetica$/i, chain: `Helvetica, 'Helvetica Neue', Arial, sans-serif` },\n { match: /^verdana$/i, chain: `Verdana, Geneva, Tahoma, sans-serif` },\n];\n\n/**\n * Wrap `fontFamily` in quotes if it contains spaces or unusual\n * characters, and append a metric-compatible fallback chain. If the\n * font already matches a curated entry, use that chain directly so the\n * primary font isn't double-listed.\n */\nexport function withFallbacks(fontFamily: string): string {\n const trimmed = fontFamily.trim();\n if (trimmed.length === 0) return trimmed;\n for (const { match, chain } of CHAINS) {\n if (match.test(trimmed)) return chain;\n }\n // Unknown font — quote if needed and end in a generic family. We\n // can't know whether the font is serif or sans-serif without\n // metadata; default to `serif` since most uncommon docx fonts are\n // serif (legal/contract templates lean that way).\n const quoted = needsQuoting(trimmed) ? `'${trimmed}'` : trimmed;\n return `${quoted}, serif`;\n}\n\nfunction needsQuoting(name: string): boolean {\n return /[\\s\"',()]/.test(name);\n}\n","import type { HyperlinkRun, InlineRun, RunProperties, TextRun } from \"../../../doc/types\";\nimport { withFallbacks } from \"./fontFallback\";\n\n/**\n * Render a list of InlineRuns into DOM children of `parent`. Empty run\n * lists produce a `<br>` placeholder so contenteditable can place a\n * caret in the paragraph.\n *\n * `rawParts` is threaded through so `DrawingRun` can resolve its\n * `partPath` to an `<img src>` via a blob URL / data URI.\n */\nexport function appendInlineRuns(\n parent: HTMLElement,\n runs: readonly InlineRun[],\n rawParts: Record<string, Uint8Array> = {},\n): void {\n if (runs.length === 0) {\n parent.appendChild(document.createElement(\"br\"));\n return;\n }\n for (const run of runs) {\n const node = renderRun(run, rawParts);\n if (node) parent.appendChild(node);\n }\n}\n\nfunction renderRun(\n run: InlineRun,\n rawParts: Record<string, Uint8Array>,\n): Node | null {\n switch (run.kind) {\n case \"text\":\n return renderTextRun(run);\n case \"break\":\n if (run.type === \"line\") return document.createElement(\"br\");\n if (run.type === \"page\") {\n const div = document.createElement(\"div\");\n div.className = \"page-break\";\n div.setAttribute(\"data-page-break\", \"\");\n div.setAttribute(\"contenteditable\", \"false\");\n return div;\n }\n if (run.type === \"column\") {\n // Column-break runs are hoisted onto the containing paragraph\n // via `break-before: column` in `renderParagraph` (see\n // block.ts → cascadeColumnBreak). The inline run itself\n // collapses to nothing so the paragraph layout stays clean.\n return null;\n }\n return null;\n case \"tab\":\n return document.createTextNode(\"\\t\");\n case \"field\": {\n // Wrap in a span tagged with the field instruction so per-page\n // contexts (header/footer renderers) can substitute the live\n // PAGE / NUMPAGES value. Body rendering leaves the cached text\n // as-is — the span is invisible to layout.\n const span = document.createElement(\"span\");\n span.className = \"sobree-field\";\n span.dataset.field = run.instruction;\n span.textContent = run.cached ?? \"\";\n return span;\n }\n case \"drawing\":\n return renderDrawing(run, rawParts);\n case \"hyperlink\":\n return renderHyperlink(run, rawParts);\n case \"footnoteRef\":\n return renderFootnoteRef(run);\n case \"commentRef\":\n return renderCommentRef(run);\n default:\n return null;\n }\n}\n\n/**\n * Render a footnote reference as a clickable superscript anchor.\n * `href=\"#sobree-footnote-N\"` points at the matching `<li>` in the\n * footnotes container that the renderer appends at the end of the body.\n */\nfunction renderFootnoteRef(\n run: import(\"../../../doc/types\").FootnoteRefRun,\n): HTMLElement {\n const sup = document.createElement(\"sup\");\n sup.className = \"sobree-footnote-ref\";\n const link = document.createElement(\"a\");\n link.setAttribute(\"href\", `#sobree-footnote-${run.id}`);\n link.setAttribute(\"id\", `sobree-footnote-ref-${run.id}`);\n link.textContent = String(run.id);\n sup.appendChild(link);\n return sup;\n}\n\n/**\n * Render a comment reference as a small balloon glyph linking to the\n * comment card in the comments aside / per-page zone. Word draws a\n * speech-bubble icon; we use the U+1F4AC character (\"💬\") as a\n * lightweight stand-in that needs no SVG.\n */\nfunction renderCommentRef(\n run: import(\"../../../doc/types\").CommentRefRun,\n): HTMLElement {\n const span = document.createElement(\"span\");\n span.className = \"sobree-comment-ref\";\n const link = document.createElement(\"a\");\n link.setAttribute(\"href\", `#sobree-comment-${run.id}`);\n link.setAttribute(\"id\", `sobree-comment-ref-${run.id}`);\n link.setAttribute(\"aria-label\", `Comment ${run.id}`);\n link.textContent = \"\\u{1F4AC}\";\n span.appendChild(link);\n return span;\n}\n\n/**\n * Render a TextRun by:\n * 1. Creating the innermost text node.\n * 2. Wrapping it in semantic tags (`<strong>`, `<em>`, `<u>`, `<s>`,\n * `<sub>`/`<sup>`) for every property that has one.\n * 3. Wrapping once in `<span style=\"...\">` for the rest (colour,\n * highlight, font family/size) if any apply.\n *\n * The DOM shape round-trips cleanly through the serializer which walks\n * these wrappers back into a single flat `RunProperties`.\n */\nfunction renderTextRun(run: TextRun): Node {\n let node: Node = document.createTextNode(run.text);\n const p = run.properties;\n\n if (p.verticalAlign === \"superscript\") node = wrap(\"sup\", node);\n else if (p.verticalAlign === \"subscript\") node = wrap(\"sub\", node);\n\n if (p.strike) node = wrap(\"s\", node);\n if (p.underline && p.underline !== \"none\") node = wrap(\"u\", node);\n if (p.italic) node = wrap(\"em\", node);\n if (p.bold) node = wrap(\"strong\", node);\n\n const style = cssFromRunProps(p);\n if (style) {\n const span = document.createElement(\"span\");\n span.setAttribute(\"style\", style);\n span.appendChild(node);\n node = span;\n }\n // Format-change wrapper — `<span class=\"sobree-revision-format\">`\n // for runs whose properties were tracked-changed (`<w:rPrChange>` /\n // `RunProperties.revisionFormat`). Carries the author so the review\n // plugin can colour the visual hint and surface a hover popover that\n // dispatches to `editor.acceptFormatRevision` /\n // `rejectFormatRevision`. Wraps INSIDE ins/del so a run that was\n // both inserted AND format-changed still hovers each independently.\n if (p.revisionFormat) {\n const wrapper = document.createElement(\"span\");\n wrapper.className = \"sobree-revision-format\";\n if (p.revisionFormat.author) {\n wrapper.dataset.revisionFormatAuthor = p.revisionFormat.author;\n }\n if (p.revisionFormat.date) {\n wrapper.dataset.revisionFormatDate = p.revisionFormat.date;\n }\n wrapper.appendChild(node);\n node = wrapper;\n }\n // Tracked-change wrapper — `<ins>` / `<del>` semantically tag the\n // text as an insertion / deletion. Core ships *neutral* styling\n // (underline / strikethrough) so changes are always visible, even\n // with no review plugin. `data-revision-author` / `-date` carry the\n // metadata; the `@sobree/review` plugin reads `data-revision-author`\n // to apply per-author colour.\n if (p.revision) {\n const tag = p.revision.type === \"ins\" ? \"ins\" : \"del\";\n const wrapper = document.createElement(tag);\n wrapper.className = `sobree-revision sobree-revision-${p.revision.type}`;\n if (p.revision.author) wrapper.dataset.revisionAuthor = p.revision.author;\n if (p.revision.date) wrapper.dataset.revisionDate = p.revision.date;\n wrapper.appendChild(node);\n node = wrapper;\n }\n // Comment-range highlight — wrap in a `<span class=\"sobree-comment-range\">`\n // anchored to the comment ids via `data-comment-ids=\"N,M\"`. Core draws\n // a faint neutral highlight so the commented span is visible; the\n // `@sobree/review` plugin reads `data-comment-ids` to place the\n // comment cards.\n if (p.commentIds && p.commentIds.length > 0) {\n const wrapper = document.createElement(\"span\");\n wrapper.className = \"sobree-comment-range\";\n wrapper.dataset.commentIds = p.commentIds.join(\",\");\n wrapper.appendChild(node);\n node = wrapper;\n }\n return node;\n}\n\nfunction renderHyperlink(link: HyperlinkRun, rawParts: Record<string, Uint8Array>): Node {\n const a = document.createElement(\"a\");\n a.setAttribute(\"href\", link.href);\n appendInlineRuns(a, link.children, rawParts);\n return a;\n}\n\nfunction renderDrawing(\n d: import(\"../../../doc/types\").DrawingRun,\n rawParts: Record<string, Uint8Array>,\n): Node {\n const img = document.createElement(\"img\");\n img.setAttribute(\"data-part\", d.partPath);\n if (d.altText) img.setAttribute(\"alt\", d.altText);\n const url = partPathToUrl(d.partPath, rawParts);\n if (url) img.setAttribute(\"src\", url);\n if (d.widthEmu > 0) img.style.width = `${emuToPx(d.widthEmu)}px`;\n if (d.heightEmu > 0) img.style.height = `${emuToPx(d.heightEmu)}px`;\n if (d.verticalAlign === \"middle\") img.style.verticalAlign = \"middle\";\n if (d.placement === \"anchor\" && d.anchor) applyAnchorPositioning(img, d.anchor);\n return img;\n}\n\n/**\n * Apply CSS positioning to a floating (`<wp:anchor>`) image. We use\n * `position: absolute` with offsets in millimetres, sized into whichever\n * ancestor the paper stack provides as the positioning frame.\n *\n * The `relativeFrom` axis values collapse to two cases:\n * - \"page\" / \"margin\" / \"column\" / \"paragraph\" / \"line\"\n * → all become positions inside the **paper-content** box (the\n * text area). Strictly accurate `relativeFrom: page` would\n * measure from the paper edge including margins, but for the\n * common case of margin-anchored figures the difference is one\n * margin and the visual is close enough until a fixture forces\n * finer calibration.\n *\n * `data-anchor-h` / `data-anchor-v` carry the original `relativeFrom`\n * values for downstream diagnosis / future tightening.\n */\nfunction applyAnchorPositioning(\n img: HTMLImageElement,\n anchor: import(\"../../../doc/types\").DrawingAnchor,\n): void {\n img.style.position = \"absolute\";\n img.style.left = `${emuToMm(anchor.offsetXEmu)}mm`;\n img.style.top = `${emuToMm(anchor.offsetYEmu)}mm`;\n if (anchor.behindDoc) img.style.zIndex = \"-1\";\n img.dataset.anchorH = anchor.relativeFromH;\n img.dataset.anchorV = anchor.relativeFromV;\n}\n\nfunction emuToMm(emu: number): number {\n // 914400 EMU per inch, 25.4mm per inch.\n return Math.round((emu / 914400) * 25.4 * 100) / 100;\n}\n\n/** Convert a raw part's bytes (from `doc.rawParts`) into a blob URL\n * the browser can render as `<img src>` / `background-image`. Exported\n * for the section-frame renderer in `block.ts` which paints the\n * `framePictures` background outside the inline-run code path. */\nexport function partPathToUrl(\n partPath: string,\n rawParts: Record<string, Uint8Array>,\n): string | null {\n const bytes = rawParts[partPath];\n if (!bytes) return null;\n // jsdom (test runner) lacks `URL.createObjectURL`; fall back to a\n // data: URI so the renderer produces a stable string the snapshot\n // can compare against without hitting browser-only APIs.\n if (typeof URL === \"undefined\" || typeof URL.createObjectURL !== \"function\") {\n return `data:${mimeFromPath(partPath)};base64,${bytesToBase64(bytes)}`;\n }\n return bytesToObjectUrl(bytes, mimeFromPath(partPath));\n}\n\nfunction bytesToBase64(bytes: Uint8Array): string {\n // Naive batched encoder — fine for the small PNGs (max ~5 KB) used\n // for section-frame decorations in our corpus.\n let str = \"\";\n const chunk = 0x8000;\n for (let i = 0; i < bytes.length; i += chunk) {\n str += String.fromCharCode(...bytes.subarray(i, i + chunk));\n }\n return typeof btoa === \"function\" ? btoa(str) : Buffer.from(str, \"binary\").toString(\"base64\");\n}\n\nfunction mimeFromPath(path: string): string {\n const ext = path.split(\".\").pop()?.toLowerCase() ?? \"\";\n if (ext === \"png\") return \"image/png\";\n if (ext === \"jpg\" || ext === \"jpeg\") return \"image/jpeg\";\n if (ext === \"gif\") return \"image/gif\";\n if (ext === \"webp\") return \"image/webp\";\n if (ext === \"svg\") return \"image/svg+xml\";\n return \"application/octet-stream\";\n}\n\nfunction bytesToObjectUrl(bytes: Uint8Array, mime: string): string {\n // Copy into a fresh Uint8Array so the Blob's typed-array has a\n // plain `ArrayBuffer` (not `SharedArrayBuffer`), satisfying strict\n // `BlobPart` typing.\n const copy = new Uint8Array(bytes.byteLength);\n copy.set(bytes);\n const blob = new Blob([copy], { type: mime });\n return URL.createObjectURL(blob);\n}\n\nfunction emuToPx(emu: number): number {\n // 914400 EMU per inch, 96 px per inch at default DPI.\n return Math.round((emu / 914400) * 96);\n}\n\nfunction wrap(tag: \"strong\" | \"em\" | \"u\" | \"s\" | \"sub\" | \"sup\", child: Node): HTMLElement {\n const el = document.createElement(tag);\n el.appendChild(child);\n return el;\n}\n\n/**\n * Translate the presentation-level bits of RunProperties into a CSS\n * `style` string. Returns `null` when nothing matches so the caller can\n * skip wrapping in `<span>`.\n */\nfunction cssFromRunProps(p: RunProperties): string | null {\n const decls: string[] = [];\n if (p.color) decls.push(`color:${p.color}`);\n if (p.highlight) decls.push(`background:${normaliseHighlight(p.highlight)}`);\n if (p.fontFamily) decls.push(`font-family:${withFallbacks(p.fontFamily)}`);\n if (p.fontSizePt !== undefined) decls.push(`font-size:${p.fontSizePt}pt`);\n // `<w:caps/>` → CSS `text-transform: uppercase`. The source text\n // keeps its mixed-case characters (round-trip), the display is\n // uppercased. healthcare-with-photo's \"Peter Burkimsher\" name run\n // carries `caps`; rendering as-is shows lowercase, which mismatches\n // Word/LO's all-caps banner label.\n if (p.caps) decls.push(\"text-transform:uppercase\");\n return decls.length > 0 ? decls.join(\";\") : null;\n}\n\nfunction normaliseHighlight(v: string): string {\n if (v.startsWith(\"#\")) return v;\n const map: Record<string, string> = {\n yellow: \"#ffff00\",\n green: \"#00ff00\",\n cyan: \"#00ffff\",\n magenta: \"#ff00ff\",\n blue: \"#0000ff\",\n red: \"#ff0000\",\n darkYellow: \"#808000\",\n };\n return map[v] ?? v;\n}\n","import { renderBlocks } from \"./block\";\nimport type {\n NamedStyle,\n NumberingDefinition,\n Table,\n TableCell,\n TableRow,\n} from \"../../../doc/types\";\n\n/**\n * Render a Table to a `<table>` element. `vMerge: \"restart\"` cells\n * emit with `rowspan=N` computed from the following `\"continue\"` cells\n * in the same column. `\"continue\"` cells render nothing — their column\n * is covered by the spanned restart cell above.\n *\n * `styles` + `numbering` thread through so each cell's paragraphs go\n * through the full cascade (line-height, spacing, alignment, font from\n * style). Without this, table-cell paragraphs would render flat\n * (visible bug: BodyText-styled paragraphs inside cells lost their\n * 1.5× line-height because the cell's own renderer ignored per-paragraph\n * properties).\n */\nexport function renderTable(\n table: Table,\n numbering: readonly NumberingDefinition[] = [],\n styles: readonly NamedStyle[] = [],\n rawParts: Record<string, Uint8Array> = {},\n): HTMLElement {\n const t = document.createElement(\"table\");\n applyTableBorders(t, table);\n\n // Pre-compute rowspan per (row, col) for every restart cell.\n const rowSpans = computeRowSpans(table);\n\n const headRows = table.rows\n .map((r, i) => ({ r, i }))\n .filter((x) => x.r.isHeader);\n const bodyRows = table.rows\n .map((r, i) => ({ r, i }))\n .filter((x) => !x.r.isHeader);\n\n if (headRows.length > 0) {\n const thead = document.createElement(\"thead\");\n for (const { r, i } of headRows) {\n thead.appendChild(renderRow(r, i, \"th\", rowSpans, numbering, styles, rawParts));\n }\n t.appendChild(thead);\n }\n if (bodyRows.length > 0) {\n const tbody = document.createElement(\"tbody\");\n for (const { r, i } of bodyRows) {\n tbody.appendChild(renderRow(r, i, \"td\", rowSpans, numbering, styles, rawParts));\n }\n t.appendChild(tbody);\n }\n return t;\n}\n\nfunction renderRow(\n row: TableRow,\n rowIndex: number,\n defaultCell: \"th\" | \"td\",\n rowSpans: Map<string, number>,\n numbering: readonly NumberingDefinition[],\n styles: readonly NamedStyle[],\n rawParts: Record<string, Uint8Array>,\n): HTMLElement {\n const tr = document.createElement(\"tr\");\n let col = 0;\n for (const cell of row.cells) {\n const gridSpan = cell.gridSpan ?? 1;\n if (cell.vMerge === \"continue\") {\n col += gridSpan;\n continue; // occluded by the restart cell above\n }\n const el = renderCell(cell, defaultCell, numbering, styles, rawParts);\n if (gridSpan > 1) el.setAttribute(\"colspan\", String(gridSpan));\n const rs = rowSpans.get(`${rowIndex}:${col}`);\n if (rs && rs > 1) el.setAttribute(\"rowspan\", String(rs));\n tr.appendChild(el);\n col += gridSpan;\n }\n return tr;\n}\n\nfunction renderCell(\n cell: TableCell,\n defaultTag: \"th\" | \"td\",\n numbering: readonly NumberingDefinition[],\n styles: readonly NamedStyle[],\n rawParts: Record<string, Uint8Array>,\n): HTMLElement {\n const el = document.createElement(defaultTag);\n\n // <w:shd w:fill=\"XXXXXX\"/> on the cell — colour the cell background.\n // We render only the `fill` (most common case); patterned shading\n // (`pct10`, etc.) collapses to solid fill.\n if (cell.shading?.fill && cell.shading.fill !== \"#auto\") {\n el.style.backgroundColor = cell.shading.fill;\n }\n // <w:vAlign w:val=\"top|center|bottom\"/> on the cell. CSS table-cell\n // vertical alignment is the `vertical-align` property on the <td>.\n if (cell.verticalAlign) {\n el.style.verticalAlign =\n cell.verticalAlign === \"center\" ? \"middle\" : cell.verticalAlign;\n }\n\n // Delegate paragraph / nested-table rendering to renderBlocks so each\n // cell child goes through the full cascade (line-height, spacing,\n // alignment, font from style). Without this, per-paragraph properties\n // are silently dropped inside table cells.\n if (cell.content.length > 0) {\n renderBlocks(cell.content, el, numbering, styles, rawParts);\n // Match LibreOffice's \"compress paragraph after-spacing inside\n // table cells\" rendering opinion. Word literally applies\n // `<w:spacing w:after>` in cells; LO ignores it for the implicit\n // 240-twip (4mm) default that most styles inherit. Result on\n // resume-style docs (complex-multipage.docx, healthcare-with-photo)\n // is that LO packs significantly more rows per page than Word /\n // Sobree-stock — which then forces Sobree to spend extra pages\n // for content that LO fits inline.\n //\n // The compression only affects paragraphs whose rendered margin\n // is the OOXML default (4mm); explicit non-default after-spacing\n // (e.g. 8mm, 12mm) is preserved as-authored. Paragraphs that the\n // renderer left WITHOUT an inline margin-bottom (because the\n // source said nothing) inherit the browser default via the CSS\n // selector in `paperStack.css` and get tightened there.\n tightenDefaultAfterSpacing(el);\n } else {\n // Word requires every cell to have at least one paragraph; supply\n // a single <br> so the cell renders with a caret target / minimum\n // height when the AST is empty.\n el.appendChild(document.createElement(\"br\"));\n }\n return el;\n}\n\n/**\n * Walk a table cell's rendered paragraphs and zero out the\n * margin-bottom for any paragraph whose source carried the default\n * `<w:spacing w:after=\"240\"/>` (≈ 4 mm). Word renders this literally;\n * LibreOffice ignores it inside table cells. We match LO so resume-\n * style multi-row cells pack to LO's page count rather than running\n * 1–2 pages longer.\n *\n * Custom non-default after-spacing (e.g. 8 mm, 12 mm — usually set\n * intentionally by the author for visual separation) is left alone;\n * removing it would be a more aggressive deviation from spec than\n * the LO heuristic justifies.\n */\nfunction tightenDefaultAfterSpacing(cellEl: HTMLElement): void {\n for (const p of cellEl.querySelectorAll(\"p\")) {\n if (!(p instanceof HTMLElement)) continue;\n const mb = p.style.marginBottom;\n // The renderer formats the value as `${twipsToMm(after)}mm`. The\n // OOXML default is 240 twips → 4mm. Match the exact rendered\n // string to avoid mis-tightening anything else (e.g. paragraphs\n // with explicit larger after-spacing render as \"8mm\" or \"10mm\").\n if (mb === \"4mm\") p.style.marginBottom = \"0px\";\n // Line-height tightening: Word's \"Multiple 1.15\" line-rule renders\n // as `line-height: ~1.20` (Calibri natural leading 1.05 × 1.15\n // multiplier). LO ignores this inside table cells, defaulting to\n // single-spacing. Tightening here unlocks the page-density win\n // needed to absorb the Operating Systems / Tech Proficiency\n // widow into the previous page. Line baselines shift slightly\n // vs LO's reference rendering, so the corpus drift score rises\n // — accepted as the cost of one-fewer-page convergence.\n const lh = p.style.lineHeight;\n if (lh && /^1\\.(0[5-9]|1[0-9]|2[0-9])\\d*$/.test(lh)) {\n p.style.lineHeight = \"1\";\n }\n }\n}\n\n/**\n * Apply `<w:tblBorders>` to the rendered `<table>` and stamp a class\n * that drives per-cell border rules in CSS. We do it in CSS so the\n * pagination-time split of a table (per-page TR clones) inherits the\n * border styles via the `--border-*` custom properties on the table\n * itself — the cell rules read those vars regardless of which clone\n * the TD ended up in.\n *\n * Word's `<w:tblBorders>` covers the table's outer edges + the\n * inside-horizontal / inside-vertical separators. CSS doesn't have\n * \"inside\" border selectors, so we approximate by setting `border` on\n * every cell (giving you the inside separators) AND overriding the\n * outer edges with the explicit top/right/bottom/left specs.\n *\n * Tables with `tblStyle=\"TableGrid\"` but no explicit `<w:tblBorders>`\n * also get default thin gray borders — TableGrid is Word's built-in\n * style for \"every cell has a border\".\n */\nfunction applyTableBorders(t: HTMLElement, table: Table): void {\n const b = table.properties.borders;\n const isGridStyle = table.properties.styleId === \"TableGrid\";\n // `b` truthy means `<w:tblBorders>` was declared — even if every side\n // resolved to \"none\" (importer carries an empty object in that case).\n // Treat declared-but-empty as \"borderless on purpose\"; the TableGrid\n // heuristic only fills in when the doc said nothing at all.\n const explicitlyDeclared = b !== undefined;\n const hasAnyBorderSide = !!(b && (b.top || b.right || b.bottom || b.left || b.insideH || b.insideV));\n if (!hasAnyBorderSide && !isGridStyle) return;\n if (explicitlyDeclared && !hasAnyBorderSide) return; // explicit \"no borders\"\n\n t.style.borderCollapse = \"collapse\";\n t.classList.add(\"sobree-table-bordered\");\n\n // Inside (cell-to-cell) border: prefer insideH / insideV if declared,\n // fall back to a thin gray when only the outer borders are set\n // (matches Word's typical behaviour for fully-bordered tables).\n const inside = b?.insideH ?? b?.insideV;\n const insideCss = inside ? borderSpecToCss(inside) : \"1px solid #888\";\n t.style.setProperty(\"--table-cell-border\", insideCss);\n\n // Outer edges — set on the TABLE so the perimeter draws explicitly\n // even when cells would otherwise collapse a half-pixel into the\n // table edge.\n if (b?.top) t.style.borderTop = borderSpecToCss(b.top);\n if (b?.right) t.style.borderRight = borderSpecToCss(b.right);\n if (b?.bottom) t.style.borderBottom = borderSpecToCss(b.bottom);\n if (b?.left) t.style.borderLeft = borderSpecToCss(b.left);\n}\n\nfunction borderSpecToCss(spec: { style: string; sizeEighthsOfPt: number; color: string }): string {\n // Word's `sz` is eighths of a point. 4 = 0.5pt ≈ 0.67px @ 96dpi.\n // Clamp to at least 1px so the border is visible on screen.\n const px = Math.max(1, Math.round((spec.sizeEighthsOfPt / 8) * (96 / 72)));\n // Map OOXML border styles → CSS `border-style` keywords. \"single\" /\n // \"thick\" / \"wave\" / unknown all collapse to `solid` — CSS doesn't\n // have wavy borders, and `single` isn't a valid CSS keyword.\n const style = spec.style === \"double\" ? \"double\"\n : spec.style === \"dashed\" ? \"dashed\"\n : spec.style === \"dotted\" ? \"dotted\"\n : spec.style === \"none\" ? \"none\"\n : \"solid\";\n const color = spec.color === \"auto\" ? \"#888\" : spec.color;\n return `${px}px ${style} ${color}`;\n}\n\n/**\n * Walk the table: for each `(row, col)` that hosts a restart cell,\n * count how many following rows at that column carry a `\"continue\"`,\n * and store `rowspan = 1 + continue count`.\n */\nfunction computeRowSpans(table: Table): Map<string, number> {\n const out = new Map<string, number>();\n // First pass: build a per-row, per-column view of cells.\n const grid: (TableCell | null)[][] = [];\n const maxCol = table.rows.reduce(\n (n, r) => Math.max(n, r.cells.reduce((s, c) => s + (c.gridSpan ?? 1), 0)),\n 0,\n );\n for (const row of table.rows) {\n const cols: (TableCell | null)[] = new Array(maxCol).fill(null);\n let col = 0;\n for (const cell of row.cells) {\n const span = cell.gridSpan ?? 1;\n for (let k = 0; k < span; k++) cols[col + k] = cell;\n col += span;\n }\n grid.push(cols);\n }\n\n for (let rowIndex = 0; rowIndex < grid.length; rowIndex++) {\n const row = grid[rowIndex];\n if (!row) continue;\n for (let col = 0; col < row.length; col++) {\n const cell = row[col];\n if (!cell || cell.vMerge !== \"restart\") continue;\n // Only record on the first column the cell spans (left edge).\n if (col > 0 && row[col - 1] === cell) continue;\n let span = 1;\n for (let j = rowIndex + 1; j < grid.length; j++) {\n const below = grid[j]?.[col];\n if (below && below.vMerge === \"continue\") span += 1;\n else break;\n }\n out.set(`${rowIndex}:${col}`, span);\n }\n }\n return out;\n}\n\n","/**\n * OOXML measurement units → CSS conversions, in one place.\n *\n * OOXML mixes three length units:\n * - EMU (English Metric Units): 914400 per inch. Used by DrawingML\n * (`<a:off>`, `<a:ext>`, `<wp:extent>`, picture sizes).\n * - twips (twentieths of a point): 1440 per inch. Used by\n * WordprocessingML (`<w:ind>`, `<w:spacing>`, `<w:tblW>`).\n * - half-points / eighths-of-point: font + border sizes (handled\n * at their own call sites; not length conversions).\n *\n * Derived constants (all exact):\n * - 1 inch = 25.4 mm = 96 px (CSS reference pixel) = 914400 EMU = 1440 twips\n * - 914400 / 25.4 = 36000 EMU per mm\n * - 914400 / 96 = 9525 EMU per px\n *\n * Rounding is a SEPARATE concern from conversion. These helpers return\n * exact values; call sites that want integer mm / px (e.g. for crisp\n * image edges, or to match a historical snapshot) round explicitly.\n */\n\nexport const EMU_PER_INCH = 914400;\nexport const TWIPS_PER_INCH = 1440;\nexport const MM_PER_INCH = 25.4;\nexport const PX_PER_INCH = 96;\n\n/** 914400 / 25.4 = 36000, pinned as an integer literal. Computing it\n * as `914400 / 25.4` risks float drift (25.4 isn't exactly\n * representable), which would change emitted CSS strings vs the\n * literal `36000` the renderer used before this module existed. */\nexport const EMU_PER_MM = 36000;\n/** 914400 / 96 = 9525, exact. */\nexport const EMU_PER_PX = 9525;\n\n/** EMU → millimetres, exact. */\nexport function emuToMm(emu: number): number {\n return emu / EMU_PER_MM;\n}\n\n/** EMU → CSS pixels, exact. */\nexport function emuToPx(emu: number): number {\n return emu / EMU_PER_PX;\n}\n\n/**\n * Twips → millimetres, ROUNDED to the nearest whole millimetre.\n *\n * Word's body-geometry values (indents, spacing, column gaps) are\n * authored in whole points / mm and survive a round-trip as twips;\n * rounding to integer mm here keeps the emitted CSS stable (`5mm`,\n * not `4.97mm`) and matches the renderer's long-standing output that\n * the oracle snapshots are blessed against. Callers needing sub-mm\n * precision should use `twipsToMmExact`.\n */\nexport function twipsToMm(twips: number): number {\n return Math.round((twips / TWIPS_PER_INCH) * MM_PER_INCH);\n}\n\n/** Twips → millimetres, exact (no rounding). */\nexport function twipsToMmExact(twips: number): number {\n return (twips / TWIPS_PER_INCH) * MM_PER_INCH;\n}\n","import type {\n NamedStyle,\n ParagraphProperties,\n RunProperties,\n SobreeDocument,\n} from \"./types\";\n\n/**\n * Style-cascade resolver — walks `styleId` up its `basedOn` chain and\n * merges defaults base-first, leaf-last. The result is what the\n * renderer should apply (block element inherits these to its runs)\n * and what the toolbar's font / mark dropdowns should reflect.\n *\n * Falls back to the document's `Normal` style at the end of the chain\n * so a paragraph with no explicit style still picks up the document's\n * baseline run / paragraph defaults (see `defaultStyles()` in\n * `./builders`).\n *\n * Cycles in `basedOn` are tolerated — we de-dupe by id and stop\n * walking. Missing styles are silently skipped.\n */\nexport function resolveStyleCascade(\n styles: readonly NamedStyle[] | SobreeDocument,\n styleId: string | undefined,\n): { runDefaults: RunProperties; paragraphDefaults: ParagraphProperties } {\n const list = Array.isArray(styles)\n ? (styles as readonly NamedStyle[])\n : (styles as SobreeDocument).styles;\n const chain = collectStyleChain(list, styleId);\n // Build base-up: deeper inherited defaults first, the block's own\n // style last so it wins on conflict.\n //\n // Sub-objects (spacing, indent, borders) shallow-merge field-by-field\n // — OOXML's cascade is field-level, not object-level. A child style\n // that sets only `spacing.line` MUST keep its parent's `spacing.after`\n // / `spacing.before` / `lineRule`; without this Word's \"BodyText\n // overrides line=360 but inherits after=160 from Normal\" looks tight\n // and uniform paragraphs in Word land tighter in Sobree.\n let runDefaults: RunProperties = {};\n let paragraphDefaults: ParagraphProperties = {};\n for (let i = chain.length - 1; i >= 0; i--) {\n const s = chain[i]!;\n if (s.runDefaults) runDefaults = { ...runDefaults, ...s.runDefaults };\n if (s.paragraphDefaults) {\n paragraphDefaults = mergeParagraphDefaults(paragraphDefaults, s.paragraphDefaults);\n }\n }\n return { runDefaults, paragraphDefaults };\n}\n\nfunction mergeParagraphDefaults(\n base: ParagraphProperties,\n over: ParagraphProperties,\n): ParagraphProperties {\n return {\n ...base,\n ...over,\n spacing: { ...base.spacing, ...over.spacing },\n indent: { ...base.indent, ...over.indent },\n borders: { ...base.borders, ...over.borders },\n };\n}\n\nfunction collectStyleChain(\n styles: readonly NamedStyle[],\n styleId: string | undefined,\n): NamedStyle[] {\n const out: NamedStyle[] = [];\n const seen = new Set<string>();\n let id = styleId;\n while (id && !seen.has(id)) {\n seen.add(id);\n const s = styles.find((x) => x.id === id);\n if (!s) break;\n out.push(s);\n id = s.basedOn;\n }\n // Always anchor the chain in the document's \"Normal\" style so a\n // paragraph with no explicit styleId still picks up document\n // baseline defaults (DocDefaults' spacing.afterTwips etc.). The\n // style id varies by language: \"Normal\" (English), \"Norml\"\n // (Hungarian, accent-stripped), \"Standard\" (German), \"Estilo Normal\"\n // (Spanish), ... — Word stamps `displayName=\"Normal\"` on whichever\n // style serves the role. Find by that first, then fall back to the\n // literal \"Normal\" id, then to whatever style is basedOn DocDefaults\n // (Word's other convention for marking the anchor).\n const normal = findNormalAnchor(styles);\n // Continue the cascade walk from the Normal anchor through its\n // basedOn chain (typically to DocDefaults). Without continuing the\n // walk, paragraphs with no explicit style would skip DocDefaults\n // entirely and lose every doc-level default — including the\n // post-paragraph spacing that creates Word's characteristic\n // breathing room between every paragraph.\n let anchorId: string | undefined = normal?.id;\n while (anchorId && !seen.has(anchorId)) {\n seen.add(anchorId);\n const s = styles.find((x) => x.id === anchorId);\n if (!s) break;\n out.push(s);\n anchorId = s.basedOn;\n }\n return out;\n}\n\nfunction findNormalAnchor(styles: readonly NamedStyle[]): NamedStyle | undefined {\n // 1. Literal id match (English Word, most common).\n const byId = styles.find((s) => s.id === \"Normal\");\n if (byId) return byId;\n // 2. displayName = \"Normal\" (localized id, Word stamps the canonical\n // English display name).\n const byDisplay = styles.find(\n (s) => s.type === \"paragraph\" && s.displayName === \"Normal\",\n );\n if (byDisplay) return byDisplay;\n // 3. Paragraph style based directly on DocDefaults (Word's other\n // anchor convention — the \"Normal\" style is the first one based\n // on DocDefaults).\n const byBase = styles.find(\n (s) => s.type === \"paragraph\" && s.basedOn === \"DocDefaults\",\n );\n return byBase;\n}\n","/**\n * Paragraph-property → CSS derivation.\n *\n * `applyParagraphProps` is the single place that turns a paragraph's\n * resolved `ParagraphProperties` (after the style cascade) into inline\n * CSS on its rendered element: font, colour, alignment, line-height,\n * spacing, indent, borders, shading, page-break / keep-next hints,\n * and tab geometry.\n *\n * CSS owns layout / interaction; the document owns typography. Every\n * value applied here comes from the AST + the document's style chain —\n * there are no CSS-only typography fallbacks.\n */\n\nimport { withFallbacks } from \"./fontFallback\";\nimport { twipsToMm } from \"./units\";\nimport { resolveStyleCascade } from \"../../../doc/styles\";\nimport type { NamedStyle, ParagraphProperties } from \"../../../doc/types\";\n\nexport function applyParagraphProps(\n el: HTMLElement,\n props: ParagraphProperties,\n styles: readonly NamedStyle[] = [],\n): void {\n // Resolve the style cascade for both run + paragraph defaults, then\n // overlay the paragraph's own properties so explicit settings win on\n // conflict.\n // Bare paragraphs without an explicit `styleId` inherit from\n // \"Normal\" — same behaviour as Word's pStyle-default. The cascade\n // walks `Normal → docDefaults` (when present) under the hood; a\n // missing \"Normal\" style returns empty defaults safely.\n const effectiveStyleId = props.styleId ?? \"Normal\";\n const { runDefaults: cascadeRunDefaults, paragraphDefaults } = styles.length > 0\n ? resolveStyleCascade(styles, effectiveStyleId)\n : { runDefaults: {}, paragraphDefaults: {} };\n const effective: ParagraphProperties = mergeParagraphProperties(paragraphDefaults, props);\n // Overlay the paragraph's OWN `runDefaults` on top of the style\n // cascade. `pPr/rPr` carries the paragraph-mark font (e.g. an\n // 8pt Arial in jellap.docx's header contact lines, or 9pt Times\n // for form-field empties); without overlaying here the cascade\n // wins and we render those paragraphs at the style's default font\n // (often 12pt Calibri from DocDefaults), throwing every line-height\n // calculation off.\n const runDefaults = { ...cascadeRunDefaults, ...(props.runDefaults ?? {}) };\n\n if (runDefaults.fontFamily) el.style.fontFamily = withFallbacks(runDefaults.fontFamily);\n if (runDefaults.fontSizePt !== undefined) {\n el.style.fontSize = `${runDefaults.fontSizePt}pt`;\n }\n // Apply the rest of the run cascade to the block element so per-run\n // children inherit Word's style-defined colour / weight / italic /\n // underline. Without this, e.g. Heading1's `color: \"#2E74B5\"`\n // (declared in styles.xml) is parsed into the AST but never makes\n // it onto the rendered `<h1>` — headings render in the default\n // text colour instead of Word's blue.\n if (runDefaults.color) el.style.color = runDefaults.color;\n if (runDefaults.bold) el.style.fontWeight = \"bold\";\n if (runDefaults.italic) el.style.fontStyle = \"italic\";\n // `underline` is an enum (single / double / dotted / …) or\n // undefined. Map all non-\"none\" values to a plain underline — the\n // exact style is decorative and most aren't representable as a\n // single CSS rule anyway. Per-run runs can still set richer\n // styles via their own inline declarations.\n if (runDefaults.underline && runDefaults.underline !== \"none\") {\n el.style.textDecoration = \"underline\";\n }\n if (runDefaults.strike) {\n el.style.textDecoration = el.style.textDecoration\n ? `${el.style.textDecoration} line-through`\n : \"line-through\";\n }\n // Caps from cascade — descendant text nodes inherit\n // `text-transform: uppercase` so a run that doesn't set its own\n // caps still uppercases when its paragraph style does.\n // healthcare-with-photo's `PersonalName` style extends `Title`,\n // which carries `<w:caps/>`; without this the name renders mixed\n // case despite the cascade resolving caps=true.\n if (runDefaults.caps) el.style.textTransform = \"uppercase\";\n\n if (props.styleId && !/^Heading[1-6]$/.test(props.styleId)) {\n el.classList.add(`style-${props.styleId.toLowerCase()}`);\n }\n if (effective.alignment) {\n el.style.textAlign =\n effective.alignment === \"both\" ? \"justify\" : effective.alignment;\n }\n if (effective.spacing?.line && effective.spacing.lineRule === \"auto\") {\n // OOXML's `auto` lineRule means \"1 = single line spacing as Word\n // defines it, where single ALREADY includes the font's natural\n // leading\". Word's \"1.5 lines\" is therefore 1.5 × (font-size +\n // natural leading), NOT 1.5 × font-size. CSS's unitless\n // `line-height` is just (multiplier × font-size), so to match Word\n // we multiply by the font's natural leading.\n //\n // Each font has a different built-in leading (declared in its\n // OS/2 + hhea tables and respected by the rasteriser). The values\n // below were measured against LibreOffice's PDF output by the\n // `pnpm fixtures:compare` drift tool — same docx, same font, same\n // line-rule; we ratio LibreOffice's Δy to the font size and back\n // out the leading.\n //\n // `line=240` (single) keeps the `normal` shortcut so the browser\n // uses its own native leading for the font; only multi-line rules\n // need the explicit `line-height: N` declaration to scale.\n if (effective.spacing.line === 240) {\n el.style.lineHeight = \"normal\";\n } else {\n const naturalLeading = naturalLeadingFor(runDefaults.fontFamily);\n el.style.lineHeight = String((effective.spacing.line / 240) * naturalLeading);\n }\n }\n // Spacing applies to LI just as it does to a free paragraph —\n // Word's per-paragraph `<w:spacing w:after>` is the gap BETWEEN\n // consecutive bullets, not just a wrapper concern. Dropping it on\n // LIs (the pre-fix behaviour) collapsed every list to zero inter-\n // bullet gap, packing ~3pt per bullet too tight and cascading into\n // a 2-page short-fall on complex-multipage.docx vs LO.\n if (effective.spacing?.beforeTwips !== undefined) {\n el.style.marginTop = `${twipsToMm(effective.spacing.beforeTwips)}mm`;\n }\n if (effective.spacing?.afterTwips !== undefined) {\n el.style.marginBottom = `${twipsToMm(effective.spacing.afterTwips)}mm`;\n }\n const isLi = el.tagName === \"LI\";\n if (effective.indent?.leftTwips !== undefined && !isLi) {\n // OOXML's `<w:ind w:left>` on a numbered paragraph is the SAME\n // value as the numbering definition's `lvl/pPr/ind/@w:left`. The\n // numbering def already drives the UL's `padding-left`; if we\n // also stamped it as `margin-left` on the LI, the indent would\n // double (text starts at 2 × leftTwips). LIs ignore the paragraph\n // indent here — the UL's padding-left wins. Non-LI paragraphs\n // still get their own indent.\n el.style.marginLeft = `${twipsToMm(effective.indent.leftTwips)}mm`;\n }\n if (effective.indent?.rightTwips !== undefined) {\n el.style.marginRight = `${twipsToMm(effective.indent.rightTwips)}mm`;\n }\n // Paragraph borders (`<w:pBdr>`). Word's sz is eighths-of-a-point;\n // convert to CSS px (1pt = 96/72 px). All four sides supported so\n // page-header dividers (top/bottom) and decorative box paragraphs\n // (all four sides) render correctly.\n if (effective.borders) {\n for (const side of [\"top\", \"bottom\", \"left\", \"right\"] as const) {\n const b = effective.borders[side];\n if (!b || b.style === \"none\") continue;\n const px = Math.max(1, Math.round((b.sizeEighthsOfPt / 8) * (96 / 72)));\n el.style[`border${side[0]!.toUpperCase() + side.slice(1)}` as \"borderTop\"] =\n `${px}px ${mapBorderStyle(b.style)} ${mapBorderColor(b.color)}`;\n }\n }\n // <w:shd w:fill=\"…\"/> on the paragraph — paragraph background colour.\n if (effective.shading?.fill && effective.shading.fill !== \"#auto\") {\n el.style.backgroundColor = effective.shading.fill;\n }\n if (effective.pageBreakBefore) {\n el.setAttribute(\"data-page-break-before\", \"\");\n }\n // `keepNext`: the paragraph must travel together with whatever\n // follows on the same page. Stamped here as a data-attribute so the\n // paginator's `buildItems` reads it (mirrors how `pageBreakBefore`\n // becomes `data-page-break-before`).\n if (effective.keepNext) {\n el.setAttribute(\"data-keep-next\", \"\");\n }\n // Custom tab stops (`<w:pPr><w:tabs>`) → CSS `tab-size` so `\\t`\n // characters in run text honour Word's stop geometry. We use the\n // smallest stop's position as the tab width — a strict approximation:\n // only correct when all stops are evenly spaced and tabs always land\n // on the first stop. Fine for the common case (header label/value\n // column, form fields like \"Cím: \\t 1012 Budapest\"); mixed-position\n // layouts will drift. CSS `tab-size: <length>` is honoured by\n // browsers when `white-space` preserves whitespace (we set\n // `pre-wrap` on paragraphs globally).\n if (effective.tabStops && effective.tabStops.length > 0) {\n const minTwips = Math.min(...effective.tabStops.map((s) => s.positionTwips));\n if (minTwips > 0) {\n el.style.setProperty(\"tab-size\", `${twipsToMm(minTwips)}mm`);\n // Browsers also need the prefixed -moz- variant in older versions.\n el.style.setProperty(\"-moz-tab-size\", `${twipsToMm(minTwips)}mm`);\n }\n }\n}\n\n/**\n * Merge `over` into `base` for paragraph properties — `over`'s explicit\n * values win, but its sub-objects (spacing, indent, borders) shallow-\n * merge with `base`'s so partial overrides don't wipe sibling fields.\n *\n * Example: a paragraph that sets only `spacing.afterTwips: 240` should\n * NOT lose the `spacing.line: 276` from its style cascade.\n */\nfunction mergeParagraphProperties(\n base: ParagraphProperties,\n over: ParagraphProperties,\n): ParagraphProperties {\n return {\n ...base,\n ...over,\n spacing: { ...base.spacing, ...over.spacing },\n indent: { ...base.indent, ...over.indent },\n borders: { ...base.borders, ...over.borders },\n };\n}\n\n/**\n * Per-font natural-leading lookup (single-line height ÷ design size).\n *\n * Measured against LibreOffice via `pnpm fixtures:compare`. Each font's\n * OS/2 + hhea tables declare a different built-in leading, and Word's\n * `lineRule=\"auto\"` multiplies that, not the design size. Without this\n * adjustment, `line=360` (1.5×) on Calibri 11pt renders ~10% denser in\n * Sobree than in Word.\n *\n * Default 1.15 is the Latin-serif baseline (Times / Bookman / Georgia).\n * Add more entries as drift reports show divergence on real docs.\n */\nfunction naturalLeadingFor(fontFamily: string | undefined): number {\n if (!fontFamily) return 1.15;\n const key = fontFamily.toLowerCase();\n if (key.startsWith(\"calibri\")) return 1.05;\n return 1.15;\n}\n\nfunction mapBorderStyle(s: string): string {\n if (s === \"single\" || s === \"thick\") return \"solid\";\n if (s === \"double\") return \"double\";\n if (s === \"dashed\") return \"dashed\";\n if (s === \"dotted\") return \"dotted\";\n return \"solid\";\n}\n\nfunction mapBorderColor(c: string): string {\n if (!c || c === \"auto\") return \"currentColor\";\n return c.startsWith(\"#\") ? c : `#${c}`;\n}\n","/**\n * List rendering: turn a numbered/bulleted paragraph's numbering\n * definition into the right `<ol>` / `<ul>` container with correct\n * marker geometry and glyph.\n *\n * `paragraphListInfo` reads the numbering definition for a paragraph\n * (ordered vs bulleted, indent geometry, bullet glyph). `createListContainer`\n * builds the list element from that info. The caller (renderBlocks)\n * owns the grouping logic (consecutive same-`numId` paragraphs share\n * one container) and the per-`<li>` rendering — those need the\n * paragraph-level pipeline (props, runs, revision marks).\n */\n\nimport { twipsToMm } from \"./units\";\nimport type { Block, NumberingDefinition } from \"../../../doc/types\";\n\nexport interface ListInfo {\n numId: number;\n ordered: boolean;\n /** Effective left indent (text wrap position) for this list level,\n * in twips — from the numbering definition's `<w:lvl><w:pPr><w:ind>`.\n * Applied as `padding-left` on the OL / UL so wrapped text lands at\n * the right position and the marker hangs to its left. */\n leftTwips?: number;\n /** Twips the FIRST line hangs to the left of `leftTwips` (= `@w:hanging`).\n * The marker sits at `(leftTwips - hangingTwips)` from the content edge. */\n hangingTwips?: number;\n /** The level's lvlText glyph (post-Wingdings remapping), for bullets. */\n bulletGlyph?: string;\n}\n\n/**\n * Resolve a paragraph's list membership from the numbering table.\n * Returns `null` for non-list paragraphs (no `numbering` property or\n * a `numId` not present in `numbering`).\n */\nexport function paragraphListInfo(\n block: Block,\n numbering: readonly NumberingDefinition[],\n): ListInfo | null {\n if (block.kind !== \"paragraph\") return null;\n const num = block.properties.numbering;\n if (!num) return null;\n const def = numbering.find((n) => n.numId === num.numId);\n const lvl = def?.abstractFormat.levels[num.level];\n const format = lvl?.format;\n const result: ListInfo = {\n numId: num.numId,\n ordered: format !== \"bullet\",\n };\n if (lvl?.paragraphIndent?.leftTwips !== undefined) {\n result.leftTwips = lvl.paragraphIndent.leftTwips;\n }\n if (lvl?.paragraphIndent?.hangingTwips !== undefined) {\n result.hangingTwips = lvl.paragraphIndent.hangingTwips;\n }\n if (format === \"bullet\" && lvl?.text) {\n result.bulletGlyph = lvl.text;\n }\n return result;\n}\n\n/**\n * Build the `<ol>` / `<ul>` container for a run of list items sharing\n * a `numId`. Sets marker geometry (padding-left + hanging custom\n * property) and bullet glyph (native CSS keyword where one exists,\n * else a `::marker`-content custom property).\n */\nexport function createListContainer(\n info: ListInfo,\n sectionIndex: number,\n): HTMLElement {\n const listEl = document.createElement(info.ordered ? \"ol\" : \"ul\");\n listEl.dataset.sectionIndex = String(sectionIndex);\n // OOXML's `<w:ind w:left=\"X\" w:hanging=\"Y\"/>` on the numbering\n // level says:\n // text starts at `left` twips from the content edge\n // the marker hangs `hanging` twips to the LEFT of text\n // ⇒ marker x = (left - hanging) from the content edge.\n //\n // CSS list-style-position: outside puts the marker right at the\n // LI's content-box edge — which equals the UL's padding-left. So\n // setting `padding-left = (left - hanging)` puts the marker at the\n // right spot, and the `--sobree-list-hanging-mm` custom property (a\n // sitewide CSS rule consumes it) shifts the first line of text by\n // `hanging` to land at `left` total.\n if (info.leftTwips !== undefined) {\n const left = info.leftTwips;\n const hanging = info.hangingTwips ?? 0;\n const markerOffset = Math.max(0, left - hanging);\n listEl.style.paddingLeft = `${twipsToMm(markerOffset)}mm`;\n if (hanging > 0) {\n listEl.style.setProperty(\"--sobree-list-hanging-mm\", `${twipsToMm(hanging)}mm`);\n }\n }\n // Bullet glyph from the numbering definition's `lvlText`. For glyphs\n // that map cleanly to a CSS list-style-type keyword (▪ → square,\n // • → disc, ○ → circle) we set the keyword so the browser draws the\n // native marker. For all OTHER glyphs (❖ ◆ ★ — Wingdings/Symbol\n // chars without a CSS equivalent), stamp the glyph as a custom\n // property; a sitewide `::marker` rule reads it as marker content.\n if (!info.ordered && info.bulletGlyph) {\n const cssKeyword = cssListStyleForGlyph(info.bulletGlyph);\n if (cssKeyword !== \"fallback\") {\n listEl.style.listStyleType = cssKeyword;\n } else {\n listEl.style.setProperty(\"--sobree-bullet-glyph\", `\"${info.bulletGlyph}\"`);\n // Suppress the browser-default disc so the ::marker content shows alone.\n listEl.style.listStyleType = \"none\";\n listEl.classList.add(\"sobree-list-custom-bullet\");\n }\n }\n return listEl;\n}\n\n/**\n * Map a bullet glyph to a CSS `list-style-type` keyword, or \"fallback\"\n * when no native keyword matches (caller uses a `::marker`-content\n * custom property instead).\n */\nfunction cssListStyleForGlyph(glyph: string): string {\n if (!glyph) return \"disc\";\n const first = glyph[0]!;\n if (first === \"▪\" || first === \"■\") return \"square\";\n if (first === \"○\" || first === \"◦\") return \"circle\";\n if (first === \"•\" || first === \"●\" || first === \"◉\") return \"disc\";\n return \"fallback\";\n}\n","/**\n * Render an `InlineFrame` block — a `<w:drawing><wp:inline>` group that\n * carries a textbox (section headings like \"Objective\" / \"Project: X\"\n * on complex-multipage.docx) plus decorative pictures/shapes.\n *\n * The frame renders in body flow (it paginates), as a `position:\n * relative` wrapper sized to the group's height. Decorations\n * (pictures, shapes, the textbox region) are absolute-positioned\n * children scaled from the group's local coordinate system into the\n * wrapper's rendered size: horizontal via percentage of the content\n * width, vertical in millimetres from the group EMU.\n *\n * The textbox body renders recursively through the SAME block pipeline\n * (paragraphs, lists, tables, even nested frames). To avoid a\n * `block.ts ↔ inlineFrame.ts` import cycle, the caller injects the\n * renderer as `renderBody`.\n */\n\nimport { emuToMm } from \"./units\";\nimport { partPathToUrl } from \"./inline\";\nimport { applyParagraphProps } from \"./properties\";\nimport type {\n Block,\n InlineFrame,\n NamedStyle,\n NumberingDefinition,\n} from \"../../../doc/types\";\n\n/** The recursive block renderer, injected to break the import cycle. */\nexport type RenderBody = (\n blocks: readonly Block[],\n host: HTMLElement,\n numbering: readonly NumberingDefinition[],\n styles: readonly NamedStyle[],\n rawParts: Record<string, Uint8Array>,\n) => void;\n\nexport function renderInlineFrameBlock(\n frame: InlineFrame,\n numbering: readonly NumberingDefinition[],\n styles: readonly NamedStyle[],\n rawParts: Record<string, Uint8Array>,\n renderBody: RenderBody,\n): HTMLElement {\n const wrapper = document.createElement(\"div\");\n wrapper.className = \"sobree-inline-frame\";\n // The frame's host paragraph contributes its box spacing (commonly\n // Normal's `<w:spacing w:after>`) — the band must reserve the SAME\n // vertical box Word does: drawing height PLUS the paragraph's\n // spacing-after. Apply the host paragraph's resolved properties\n // (spacing / alignment) the same way body paragraphs get them; the\n // wrapper's own structural styles below override any overlap. The\n // wrapper has no in-flow text (all children are absolute-positioned),\n // so font / line-height applied here are inert — only the margins\n // matter, and they're what closes the per-band vertical deficit.\n applyParagraphProps(wrapper, frame.hostProps ?? {}, styles);\n wrapper.style.position = \"relative\";\n wrapper.style.boxSizing = \"border-box\";\n wrapper.style.width = \"100%\";\n // Frame's intrinsic height — the paginator measures this to decide\n // page boundaries. Width is the content width (100%) so the frame\n // fills the body column; pictures scale by sizeEmu / groupExtentEmu.\n wrapper.style.minHeight = `${emuToMm(frame.sizeEmu.hEmu)}mm`;\n if (frame.pageBreakBefore) wrapper.setAttribute(\"data-page-break-before\", \"\");\n if (frame.keepNext) wrapper.setAttribute(\"data-keep-next\", \"\");\n\n // Scale child decorations from the group's local coord system into\n // the wrapper's rendered size. Horizontal axis uses 100% width\n // relative to the content area; vertical uses sizeEmu in mm.\n const scaleX = frame.groupExtentEmu.wEmu > 0 ? 1 / frame.groupExtentEmu.wEmu : 0;\n\n for (const pic of frame.pictures) {\n const url = partPathToUrl(pic.partPath, rawParts);\n if (!url) continue;\n const img = document.createElement(\"img\");\n img.src = url;\n img.alt = pic.altText ?? \"\";\n img.style.position = \"absolute\";\n img.style.left = `${pic.offsetEmu.xEmu * scaleX * 100}%`;\n img.style.top = `${emuToMm(pic.offsetEmu.yEmu)}mm`;\n img.style.width = `${pic.sizeEmu.wEmu * scaleX * 100}%`;\n img.style.height = `${emuToMm(pic.sizeEmu.hEmu)}mm`;\n img.style.objectFit = \"fill\";\n wrapper.appendChild(img);\n }\n\n for (const shape of frame.shapes) {\n const el = document.createElement(\"div\");\n el.style.position = \"absolute\";\n el.style.left = `${shape.offsetEmu.xEmu * scaleX * 100}%`;\n el.style.top = `${emuToMm(shape.offsetEmu.yEmu)}mm`;\n el.style.width = `${shape.sizeEmu.wEmu * scaleX * 100}%`;\n el.style.height = `${emuToMm(shape.sizeEmu.hEmu)}mm`;\n if (shape.fill) el.style.background = shape.fill;\n if (shape.geometry === \"ellipse\") el.style.borderRadius = \"50%\";\n else if (shape.geometry === \"roundedRect\") el.style.borderRadius = \"8px\";\n wrapper.appendChild(el);\n }\n\n if (frame.textbox) {\n const tb = frame.textbox;\n const region = document.createElement(\"div\");\n region.style.position = \"absolute\";\n region.style.left = `${tb.offsetEmu.xEmu * scaleX * 100}%`;\n region.style.top = `${emuToMm(tb.offsetEmu.yEmu)}mm`;\n region.style.width = `${tb.sizeEmu.wEmu * scaleX * 100}%`;\n region.style.height = `${emuToMm(tb.sizeEmu.hEmu)}mm`;\n region.style.overflow = \"hidden\";\n region.style.boxSizing = \"border-box\";\n // Honour the textbox's vertical anchor (`<wps:bodyPr anchor>`): a\n // flex column whose justification places the body at top / centre /\n // bottom of the region. Word relies on this to vertically centre a\n // single heading line inside the section pills.\n region.style.display = \"flex\";\n region.style.flexDirection = \"column\";\n region.style.justifyContent =\n tb.vAlign === \"center\"\n ? \"center\"\n : tb.vAlign === \"bottom\"\n ? \"flex-end\"\n : \"flex-start\";\n if (tb.fill) region.style.background = tb.fill;\n if (tb.padding) {\n const p = tb.padding;\n region.style.padding =\n `${emuToMm(p.topEmu)}mm ` +\n `${emuToMm(p.rightEmu)}mm ` +\n `${emuToMm(p.bottomEmu)}mm ` +\n `${emuToMm(p.leftEmu)}mm`;\n }\n renderBody(tb.body, region, numbering, styles, rawParts);\n wrapper.appendChild(region);\n }\n\n return wrapper;\n}\n","import type { Block, InlineRun, Paragraph, SobreeDocument, Table } from \"./types\";\n\n/**\n * Visitor pattern over the document tree.\n *\n * Every visitor key is optional — implement only the nodes you care about.\n * Return `false` from any handler to skip descending into children of that\n * node; return anything else (or omit the return) to continue.\n *\n * Why not exhaustive? Because the AST will gain shapes over time (comments,\n * tracked changes, equations) and existing visitors shouldn't break when we\n * add a new node kind. Skipped nodes log nothing — silent traversal.\n */\nexport interface DocVisitor {\n document?: (doc: SobreeDocument) => void | false;\n block?: (block: Block) => void | false;\n paragraph?: (p: Paragraph) => void | false;\n table?: (t: Table) => void | false;\n run?: (r: InlineRun) => void | false;\n}\n\nexport function walk(doc: SobreeDocument, v: DocVisitor): void {\n if (v.document?.(doc) === false) return;\n for (const block of doc.body) walkBlock(block, v);\n}\n\nexport function walkBlock(block: Block, v: DocVisitor): void {\n if (v.block?.(block) === false) return;\n if (block.kind === \"paragraph\") {\n if (v.paragraph?.(block) === false) return;\n for (const run of block.runs) walkRun(run, v);\n } else if (block.kind === \"table\") {\n if (v.table?.(block) === false) return;\n for (const row of block.rows) {\n for (const cell of row.cells) {\n for (const inner of cell.content) walkBlock(inner, v);\n }\n }\n }\n // section_break has no children to walk.\n}\n\nexport function walkRun(run: InlineRun, v: DocVisitor): void {\n if (v.run?.(run) === false) return;\n if (run.kind === \"hyperlink\") {\n for (const child of run.children) walkRun(child, v);\n }\n}\n\n/**\n * Collect every text run's text into a single flat string. Useful for\n * search, outline extraction, and \"give me the plain text\" callers.\n */\nexport function plainText(doc: SobreeDocument): string {\n const parts: string[] = [];\n walk(doc, {\n paragraph: (p) => {\n parts.push(runsToText(p.runs));\n },\n });\n return parts.join(\"\\n\");\n}\n\nexport function runsToText(runs: readonly InlineRun[]): string {\n let out = \"\";\n for (const run of runs) {\n if (run.kind === \"text\") out += run.text;\n else if (run.kind === \"tab\") out += \"\\t\";\n else if (run.kind === \"break\") out += \"\\n\";\n else if (run.kind === \"field\" && run.cached) out += run.cached;\n else if (run.kind === \"hyperlink\") out += runsToText(run.children);\n }\n return out;\n}\n\n/** Derive the heading level from a paragraph's styleId, if any. */\nexport function headingLevelOf(p: Paragraph): number | null {\n const id = p.properties.styleId;\n if (!id) return null;\n const m = id.match(/^Heading(\\d)$/);\n if (!m?.[1]) return null;\n const lv = Number(m[1]);\n return lv >= 1 && lv <= 6 ? lv : null;\n}\n","/**\n * Render a `Paragraph` block to a `<p>` / `<h1..6>` element: heading\n * tag selection, paragraph-property CSS (delegated to properties.ts),\n * dominant-run font cascade, leading column-break hoist, inline runs,\n * and single-tab \"label \\t value\" right-spread.\n */\n\nimport { applyParagraphProps } from \"./properties\";\nimport { appendInlineRuns } from \"./inline\";\nimport { withFallbacks } from \"./fontFallback\";\nimport { headingLevelOf } from \"../../../doc/walk\";\nimport type { NamedStyle, Paragraph } from \"../../../doc/types\";\n\nexport function renderParagraph(\n p: Paragraph,\n styles: readonly NamedStyle[],\n rawParts: Record<string, Uint8Array>,\n): HTMLElement {\n const level = headingLevelOf(p);\n // Empty heading paragraphs (no text, no image runs) demote to plain\n // `<p>` so they don't reserve the full Heading-font line-box. Word\n // / LO render empty headings as a thin gap; without the demotion,\n // Sobree's empty H1 takes ~33px (Heading font + margins) and creates\n // a glaring whitespace gap between e.g. an Experience heading and\n // its first bullet on google-modern.docx. The original heading\n // semantics never matter for an empty paragraph — there's no\n // outline entry to lose.\n const isEmpty = p.runs.length === 0\n || p.runs.every((r) => (r.kind === \"text\" ? !r.text : false));\n const tag = level && !isEmpty ? `h${level}` : \"p\";\n const el = document.createElement(tag);\n applyParagraphProps(el, p.properties, styles);\n // Cascade the dominant text run's font onto the paragraph itself when\n // the paragraph has no explicit font from style cascade. CSS unitless\n // line-height computes against the element's own font-size — if the\n // paragraph keeps the browser-default 16px while its runs use 9pt\n // text, every line box reserves 2.3 × 16 = 36.8px and the form-field\n // layout balloons (the jellap.docx case). Setting the paragraph's\n // font to the run's font keeps line-height honest: 2.3 × 9pt ≈ 24px.\n cascadeDominantRunFont(el, p);\n // Hoist a leading column-break run onto the paragraph element so CSS\n // columns honour it — `break-before: column` doesn't apply to\n // inline-rendered `<br>` / `<span>` markers. This is what makes\n // jellap.docx's form fields pair as rows (helye | ideje, etc.)\n // instead of running sequentially down both columns: the column\n // break advances to the next column.\n if (paragraphLeadsWithColumnBreak(p)) {\n el.style.breakBefore = \"column\";\n el.classList.add(\"sobree-column-break-before\");\n }\n // Hoist a contained `<w:br w:type=\"page\"/>` run onto the paragraph\n // element as `data-page-break-before` so the paginator forces a\n // break here. The paginator only inspects block-level elements'\n // attributes, so a page-break run rendered as an inline marker deep\n // inside the paragraph would otherwise be invisible to it. (This is\n // an approximation: the break is treated as \"before this paragraph\"\n // regardless of the run's position within it — fine for the common\n // case of an empty paragraph that exists solely to carry the break.)\n if (paragraphContainsPageBreak(p)) {\n el.setAttribute(\"data-page-break-before\", \"\");\n }\n // Tab-spread: a paragraph with exactly one tab between two text\n // groups renders as a flex row (via the `sobree-tab-spread` CSS\n // class) so the after-tab content right-aligns to the margin —\n // Word's right-tab-stop behaviour for header label/value lines.\n // The tab run itself is dropped; the two sides become labelled\n // spans the CSS pushes apart with `justify-content: space-between`.\n // Paragraphs that don't match fall through to the normal inline render.\n const split = splitForTabSpread(p);\n if (split) {\n el.classList.add(\"sobree-tab-spread\");\n const before = document.createElement(\"span\");\n before.className = \"sobree-tab-spread__before\";\n appendInlineRuns(before, split.before, rawParts);\n const after = document.createElement(\"span\");\n after.className = \"sobree-tab-spread__after\";\n appendInlineRuns(after, split.after, rawParts);\n el.append(before, after);\n } else {\n appendInlineRuns(el, p.runs, rawParts);\n }\n return el;\n}\n\n/**\n * Detect the \"label … <gap> … value\" spread and split the runs into\n * before / after groups, dropping the gap runs.\n *\n * Word emits the gap of a right-tab-stop header line (e.g.\n * \"YOUR NAME GitHub: link\") as a maximal run of pure-SPACE text\n * runs (`\" \"`), NOT as a `<w:tab/>` element — so the separator we look\n * for is the first consecutive group of space-only runs. A literal\n * tab CHARACTER inside a text run (`\"\\t\"`) is treated as content, not\n * a separator (it stays in the before-side span), matching the\n * dotted-leader header lines where the `\\t` precedes the gap space.\n *\n * Returns `null` for paragraphs without such a gap, or where either\n * side lacks real text — those render inline normally.\n */\nfunction splitForTabSpread(\n p: Paragraph,\n): { before: Paragraph[\"runs\"]; after: Paragraph[\"runs\"] } | null {\n // Only header label/value lines built on a right tab stop spread.\n // The signal is a declared custom tab stop (`<w:pPr><w:tabs>`):\n // Word fills the stop's gap with a run of spaces, which we collapse\n // into the flex space-between. Paragraphs WITHOUT a tab stop keep\n // their standalone space runs verbatim (a normal sentence can carry\n // an isolated `\" \"` run — splitting those would wrongly reflow body\n // text, as seen on lease-agreement / mit-template).\n if (!p.properties.tabStops || p.properties.tabStops.length === 0) return null;\n const isSpaceRun = (r: Paragraph[\"runs\"][number]): boolean =>\n r.kind === \"text\" && /^ +$/.test(r.text);\n // Locate the FIRST maximal group of consecutive space-only runs.\n let sepStart = -1;\n let sepEnd = -1;\n for (let i = 0; i < p.runs.length; i++) {\n if (isSpaceRun(p.runs[i]!)) {\n if (sepStart === -1) sepStart = i;\n sepEnd = i;\n } else if (sepStart !== -1) {\n break;\n }\n }\n if (sepStart === -1) return null;\n const before = p.runs.slice(0, sepStart);\n const after = p.runs.slice(sepEnd + 1);\n const hasText = (runs: Paragraph[\"runs\"]) =>\n runs.some((r) => r.kind === \"text\" && r.text.trim().length > 0);\n if (!hasText(before) || !hasText(after)) return null;\n return { before, after };\n}\n\n/** True when any run in the paragraph is an explicit page break. */\nfunction paragraphContainsPageBreak(p: Paragraph): boolean {\n for (const r of p.runs) {\n if (r.kind === \"break\" && r.type === \"page\") return true;\n }\n return false;\n}\n\n/**\n * True when the paragraph's first non-empty run is a column break.\n * Hoisted to `break-before: column` on the paragraph element.\n */\nfunction paragraphLeadsWithColumnBreak(p: Paragraph): boolean {\n for (const r of p.runs) {\n // Skip empty text runs (whitespace-only) before the break.\n if (r.kind === \"text\" && !r.text.trim()) continue;\n if (r.kind === \"break\" && r.type === \"column\") return true;\n // Any other content first → not a leading column break.\n return false;\n }\n return false;\n}\n\nfunction cascadeDominantRunFont(el: HTMLElement, p: Paragraph): void {\n // Pick the font + size of the FIRST text run with an explicit font;\n // that's the paragraph's visual dominant. Used to set the paragraph\n // element's own font so unitless line-height computes correctly.\n let family: string | undefined;\n let sizePt: number | undefined;\n for (const r of p.runs) {\n if (r.kind !== \"text\") continue;\n if (!family && r.properties.fontFamily) family = r.properties.fontFamily;\n if (sizePt === undefined && r.properties.fontSizePt !== undefined) {\n sizePt = r.properties.fontSizePt;\n }\n if (family && sizePt !== undefined) break;\n }\n // Only set when applyParagraphProps didn't already resolve a font\n // from the style cascade — an explicit style font (e.g. Calibri on\n // the paragraph's pStyle) must win over the first run's font. The\n // dominant-run cascade is a FALLBACK for paragraphs whose style\n // chain leaves the font unset, purely to keep unitless line-height\n // honest. (Symmetric with the fontSize guard below.)\n if (family && !el.style.fontFamily) {\n el.style.fontFamily = withFallbacks(family);\n }\n if (sizePt !== undefined && !el.style.fontSize) {\n el.style.fontSize = `${sizePt}pt`;\n }\n}\n","import { appendInlineRuns } from \"./inline\";\nimport { renderTable } from \"./table\";\nimport { applyParagraphProps } from \"./properties\";\nimport { createListContainer, paragraphListInfo } from \"./lists\";\nimport { renderInlineFrameBlock } from \"./inlineFrame\";\nimport { renderParagraph } from \"./paragraph\";\nimport { twipsToMm } from \"./units\";\nimport type {\n Block,\n NamedStyle,\n NumberingDefinition,\n Paragraph,\n ParagraphProperties,\n SectionProperties,\n} from \"../../../doc/types\";\n\n/**\n * Render a `Block[]` stream into `host`, grouping consecutive paragraphs\n * that share a `numId` into a single `<ul>`/`<ol>` so the browser renders\n * proper list markers.\n *\n * `numbering` maps `numId` → definition so we can decide ordered vs\n * bulleted. Unknown numIds fall back to `<ul>`.\n *\n * `rawParts` is threaded to image rendering so `<img src>` can be\n * populated from embedded bytes via a blob URL.\n */\nexport function renderBlocks(\n blocks: readonly Block[],\n host: HTMLElement,\n numbering: readonly NumberingDefinition[],\n styles: readonly NamedStyle[] = [],\n rawParts: Record<string, Uint8Array> = {},\n blockIds?: readonly string[],\n sections: readonly SectionProperties[] = [],\n): void {\n let currentList: { el: HTMLElement; numId: number } | null = null;\n /**\n * Section index for the block currently being rendered. Starts at 0\n * (the first section), bumps every time we step over a `SectionBreak`.\n * Stamped onto each rendered element as `data-section-index` so the\n * paper stack can apply per-section settings (vAlign, etc.) page-by-page\n * without re-walking the AST.\n */\n let sectionIndex = 0;\n /**\n * If the current section has `columns.count > 1` we wrap its blocks\n * in a `<div class=\"sobree-section-cols\">` so CSS `column-count` can\n * flow content across the columns. `appendTarget` becomes that\n * wrapper; on a section change we close it (revert to `host`) and\n * re-evaluate for the new section.\n */\n let appendTarget: HTMLElement = openColumnContainerIfNeeded(host, sections[0]);\n\n // Word's `<w:lastRenderedPageBreak/>` hints almost always land on\n // an EMPTY paragraph that the source author kept as a \"end of\n // section\" marker. Honoring the break literally creates a page\n // boundary BEFORE the empty paragraph — wasting the previous page\n // and putting the empty paragraph alone at the top of the next.\n // We DEFER the break: when a paragraph carrying `pageBreakBefore`\n // is empty, suppress its attribute and stash the break to be\n // applied to the next NON-EMPTY block. This produces the same\n // visual page boundary (break still happens before real content)\n // but the previous page packs whatever could fit, killing the\n // 11-of-26 wasteful-pages problem on complex-multipage.docx.\n let pendingPageBreak = false;\n const isVisuallyEmptyBlock = (b: Block): boolean => {\n if (b.kind === \"section_break\") return false;\n if (b.kind === \"table\") return false;\n if (b.kind !== \"paragraph\") return false;\n for (const r of b.runs) {\n if (r.kind === \"text\" && r.text.trim().length > 0) return false;\n if (r.kind === \"drawing\") return false;\n if (r.kind === \"tab\") return false;\n if (r.kind === \"field\") return false;\n if (r.kind === \"hyperlink\") return false;\n if (r.kind === \"footnoteRef\") return false;\n }\n return true;\n };\n\n const flushList = () => {\n currentList = null;\n };\n\n for (let i = 0; i < blocks.length; i++) {\n const block = blocks[i];\n if (!block) continue;\n const id = blockIds?.[i];\n\n // Page-break deferral: if this block carries pageBreakBefore but\n // is visually empty, suppress its break and remember it. Apply\n // the pending break to the next non-empty block instead. See the\n // pendingPageBreak comment above for why.\n if (block.kind === \"paragraph\" && block.properties.pageBreakBefore) {\n if (isVisuallyEmptyBlock(block)) {\n pendingPageBreak = true;\n // strip the break from this block so the renderer doesn't\n // apply it. Use a shallow clone of properties so we don't\n // mutate the source AST.\n block.properties = { ...block.properties, pageBreakBefore: false };\n }\n }\n if (pendingPageBreak && !isVisuallyEmptyBlock(block)) {\n if (block.kind === \"paragraph\") {\n block.properties = { ...block.properties, pageBreakBefore: true };\n }\n pendingPageBreak = false;\n }\n\n const listInfo = paragraphListInfo(block, numbering);\n if (listInfo) {\n if (!currentList || currentList.numId !== listInfo.numId) {\n const listEl = createListContainer(listInfo, sectionIndex);\n appendTarget.appendChild(listEl);\n currentList = { el: listEl, numId: listInfo.numId };\n }\n const li = document.createElement(\"li\");\n if (id) li.dataset.blockId = id;\n li.dataset.sectionIndex = String(sectionIndex);\n li.dataset.blockIndex = String(i);\n applyParagraphProps(li, (block as Paragraph).properties, styles);\n stampBlockRevision(li, (block as Paragraph).properties);\n appendInlineRuns(li, (block as Paragraph).runs, rawParts);\n currentList.el.appendChild(li);\n continue;\n }\n flushList();\n\n // SectionBreak rendering needs the upcoming section's `type` to\n // decide whether it's a forced page break (\"nextPage\" / default)\n // or a flow-through (\"continuous\"). Pulled here so renderBlock\n // stays section-array-agnostic.\n const nextSectionForBreak =\n block.kind === \"section_break\"\n ? sections[block.toSectionIndex]\n : undefined;\n const rendered = renderBlock(\n block,\n numbering,\n styles,\n rawParts,\n nextSectionForBreak,\n );\n if (rendered) {\n if (id) rendered.dataset.blockId = id;\n rendered.dataset.sectionIndex = String(sectionIndex);\n rendered.dataset.blockIndex = String(i);\n if (block.kind === \"paragraph\") {\n stampBlockRevision(rendered, block.properties);\n }\n // Section breaks always go to `host`, never into the current\n // column container. If a `<w:sectPr>` ends a 2-column section,\n // the break is the boundary AFTER the section — putting it\n // inside the column container makes it a balanced-flow item\n // that throws off Word's intended pairing (e.g. jellap.docx's\n // ANYA section gets 4|4 instead of 3|3 because the section\n // break counts as the 8th item).\n if (block.kind === \"section_break\") {\n host.appendChild(rendered);\n } else {\n appendTarget.appendChild(rendered);\n }\n }\n if (block.kind === \"section_break\") {\n // Before closing the previous column container, evict any trailing\n // empty paragraphs from inside it back to `host`. Word's column\n // balancer doesn't count trailing empties when distributing\n // content — jellap.docx's ANYA section has a single trailing\n // empty paragraph that, if left in the column flow, makes CSS\n // columns split 4|3 instead of 3|3 and mis-pairs the form\n // labels (Foglalkozása ends up on the LEFT row 4 instead of on\n // RIGHT row 1 next to Neve). Moving trailing empties out lets\n // CSS balance the remaining 6 items as 3|3, matching Word.\n evictTrailingEmptyParagraphs(appendTarget, host);\n // Collapse the visual height of the empty paragraph that\n // immediately precedes this section break in `host`. Word stores\n // sectPrs INSIDE a paragraph's pPr; that paragraph is often an\n // empty placeholder whose only job is to carry the sectPr. Word\n // treats it as part of the section boundary and renders ~no\n // visible whitespace — but our render shows it at full\n // double-spaced 9pt = ~28px, creating an unsightly gap right\n // before each section change (jellap.docx's ANYA / APA headers\n // gain ~28px of post-heading whitespace from this).\n collapseSectionTrailerEmpty(host);\n // Section index ticks AFTER rendering the break itself — the break\n // belongs to the section it ends, not to the next one.\n sectionIndex += 1;\n // Close any open column container and re-evaluate for the new section.\n appendTarget = openColumnContainerIfNeeded(host, sections[sectionIndex]);\n }\n }\n // After the walk, evict trailing empties from whatever the final\n // appendTarget was (if it's a column container).\n evictTrailingEmptyParagraphs(appendTarget, host);\n}\n\n/**\n * Visually collapse the empty paragraph immediately preceding the\n * just-appended section_break. The paragraph stays in the DOM (and the\n * AST) so round-trip and caret behaviour stay intact; CSS just zeros\n * its height. Pulls back from the last element (which is the section\n * break itself) by one position and checks if it's a visually-empty\n * paragraph (no text, no images).\n */\nfunction collapseSectionTrailerEmpty(host: HTMLElement): void {\n // The section_break was the LAST child appended. Step back one.\n const sectBreak = host.lastElementChild;\n if (!sectBreak || !sectBreak.classList.contains(\"sobree-section-break\")) return;\n const trailer = sectBreak.previousElementSibling as HTMLElement | null;\n if (!trailer) return;\n if (trailer.tagName !== \"P\" && trailer.tagName !== \"LI\") return;\n if ((trailer.textContent ?? \"\").trim().length > 0) return;\n if (trailer.querySelector(\"img, svg, table\")) return;\n trailer.classList.add(\"sobree-section-trailer-empty\");\n}\n\n/**\n * If `container` is a column-flow container, move any trailing\n * visually-empty paragraphs out of it and append them to `host`. Keeps\n * Word's column balancing semantics: empties between content count,\n * trailing empties do not.\n */\nfunction evictTrailingEmptyParagraphs(\n container: HTMLElement,\n host: HTMLElement,\n): void {\n if (!container.classList.contains(\"sobree-section-cols\")) return;\n while (container.lastElementChild) {\n const last = container.lastElementChild as HTMLElement;\n if (!isVisuallyEmptyParagraph(last)) break;\n container.removeChild(last);\n host.appendChild(last);\n }\n}\n\nfunction isVisuallyEmptyParagraph(el: HTMLElement): boolean {\n if (el.tagName !== \"P\" && el.tagName !== \"LI\") return false;\n if ((el.textContent ?? \"\").trim().length > 0) return false;\n if (el.querySelector(\"img, svg, table\") !== null) return false;\n return true;\n}\n\n/**\n * If `section.columns.count > 1`, append a column container to `host`\n * and return it as the new append target. Otherwise return `host` —\n * single-column sections write directly to it.\n */\nfunction openColumnContainerIfNeeded(\n host: HTMLElement,\n section: SectionProperties | undefined,\n): HTMLElement {\n const cols = section?.columns;\n if (!cols || cols.count <= 1) return host;\n const wrapper = document.createElement(\"div\");\n wrapper.className = \"sobree-section-cols\";\n wrapper.style.columnCount = String(cols.count);\n if (cols.spaceTwips !== undefined) {\n wrapper.style.columnGap = `${twipsToMm(cols.spaceTwips)}mm`;\n }\n host.appendChild(wrapper);\n return wrapper;\n}\n\nfunction renderBlock(\n block: Block,\n numbering: readonly NumberingDefinition[],\n styles: readonly NamedStyle[],\n rawParts: Record<string, Uint8Array>,\n nextSection?: SectionProperties,\n): HTMLElement | null {\n if (block.kind === \"paragraph\") return renderParagraph(block, styles, rawParts);\n if (block.kind === \"table\") return renderTable(block, numbering, styles, rawParts);\n if (block.kind === \"section_break\") return renderSectionBreak(nextSection);\n if (block.kind === \"inline_frame\") {\n return renderInlineFrameBlock(block, numbering, styles, rawParts, renderBlocks);\n }\n return null;\n}\n\n/**\n * Render a `SectionBreak` as a visible rule. Whether it carries\n * `data-page-break` depends on the upcoming section's `type`:\n *\n * - \"continuous\" — render the rule but DO NOT mark it as a page\n * break. The paginator flows past it; the section change still\n * takes effect for vAlign, columns, etc. on the new section's\n * blocks.\n * - \"nextPage\" / \"evenPage\" / \"oddPage\" / undefined — forced page\n * break. (We don't yet distinguish even / odd from nextPage.)\n *\n * The rule is `contentEditable=false` so caret traffic skips it.\n */\nfunction renderSectionBreak(nextSection?: SectionProperties): HTMLElement {\n const el = document.createElement(\"div\");\n el.className = \"sobree-section-break\";\n const isContinuous = nextSection?.type === \"continuous\";\n if (!isContinuous) {\n el.setAttribute(\"data-page-break\", \"\");\n } else {\n el.classList.add(\"sobree-section-break--continuous\");\n }\n // Set both the IDL property AND the HTML attribute — the IDL drives\n // caret behaviour, the attribute is what querying code (and jsdom in\n // tests) reads; jsdom doesn't reflect the IDL property to the attribute.\n el.contentEditable = \"false\";\n el.setAttribute(\"contenteditable\", \"false\");\n el.setAttribute(\"role\", \"separator\");\n el.setAttribute(\"aria-orientation\", \"horizontal\");\n const label = isContinuous ? \"Section break — continuous\" : \"Section break — next page\";\n el.setAttribute(\"aria-label\", label);\n const display = isContinuous ? \"Section break · continuous\" : \"Section break · next page\";\n el.innerHTML = `<span class=\"sobree-section-break__label\" aria-hidden=\"true\">${display}</span>`;\n return el;\n}\n\n/**\n * Stamp `data-block-revision=\"ins\"|\"del\"` and `data-block-revision-author`\n * on a paragraph element whose properties carry a tracked-change marker\n * on the paragraph mark itself. The review plugin uses these to colour\n * the trailing paragraph-mark glyph (the \"¶\" the user types Enter to\n * produce). Core renders no visual itself — neutral by default; the\n * plugin layers the author colour.\n */\nfunction stampBlockRevision(\n el: HTMLElement,\n props: ParagraphProperties,\n): void {\n const rev = props.revision;\n if (!rev) return;\n el.dataset.blockRevision = rev.type;\n if (rev.author !== undefined) {\n el.dataset.blockRevisionAuthor = rev.author;\n }\n if (rev.date !== undefined) {\n el.dataset.blockRevisionDate = rev.date;\n }\n}\n","/**\n * Render an `AnchoredFrame[]` list to a single absolute-positioned DOM\n * subtree — the per-page floating layer.\n *\n * Used by `PaperStack` once per paper: after distributing body blocks\n * into pages, each paper gets a sibling `<div class=\"paper-anchors\">`\n * containing the frames that resolved to that page. The layer covers\n * the paper-content area, child frames are positioned with `left/top`\n * in millimetres converted from EMU at paint time.\n *\n * Why a separate layer (vs inlining into body flow):\n * - The paginator stays oblivious to floating content. Body blocks\n * are the only thing it splits across pages.\n * - Each frame is exactly one DOM element. Selection / drag / resize\n * can latch onto it without needing to chase synthetic siblings.\n * - Overflow inside textboxes clips (Word behaviour) because the\n * frame's element has `overflow: hidden` — no chance of a long\n * textbox spilling into the next page's layout.\n *\n * Pure function. Takes frames + page dimensions + the binary parts\n * map (to resolve picture URLs), returns a fresh element. Repeated\n * calls produce equivalent DOM — safe to swap out wholesale.\n */\n\nimport type {\n AnchoredFrame,\n AnchoredContent,\n Block,\n} from \"../../../doc/types\";\nimport { emuToMm, emuToPx } from \"./units\";\n\nexport interface AnchorLayerContext {\n /** Map ZIP-path → bytes, used to mint blob URLs for picture content. */\n rawParts: Record<string, Uint8Array>;\n /**\n * Reuse picture URLs across calls so the same image isn't re-blobbed\n * every render. The caller (PaperStack) owns the map.\n */\n pictureUrlCache: Map<string, string>;\n /**\n * Render a textbox's `Block[]` body into a host element. Injected by\n * the caller (PaperStack wires in `renderBlocks`) so this module\n * stays decoupled from the heavy block renderer — anchorLayer only\n * knows the AnchoredFrame model + DOM, not the full paragraph/list/\n * table pipeline. When absent, textbox bodies render as plain\n * stacked text (test/headless fallback).\n */\n renderBody?: (blocks: Block[], host: HTMLElement) => void;\n}\n\n/**\n * Build the per-page anchor layer. Returns a single `<div>` whose\n * children are the frames — render order = z-stack order (later\n * siblings paint on top). The wrapper itself is `position: absolute`\n * inset:0 inside the paper-content area.\n */\nexport function renderAnchorLayer(\n frames: readonly AnchoredFrame[],\n ctx: AnchorLayerContext,\n): HTMLElement {\n const layer = document.createElement(\"div\");\n layer.className = \"paper-anchors\";\n layer.style.position = \"absolute\";\n layer.style.inset = \"0\";\n // Without a stacking context the frames' z-index values would escape\n // to the document root and fight with editor chrome (toolbars,\n // selection rectangles). Pin them here.\n layer.style.isolation = \"isolate\";\n layer.style.pointerEvents = \"none\";\n\n for (const frame of frames) {\n layer.appendChild(renderFrame(frame, ctx));\n }\n return layer;\n}\n\nfunction renderFrame(frame: AnchoredFrame, ctx: AnchorLayerContext): HTMLElement {\n const el = document.createElement(\"div\");\n el.className = \"paper-anchor\";\n el.dataset.anchorId = frame.id;\n el.style.position = \"absolute\";\n el.style.left = `${emuToMm(frame.offsetXEmu)}mm`;\n el.style.top = `${emuToMm(frame.offsetYEmu)}mm`;\n el.style.width = `${emuToMm(frame.widthEmu)}mm`;\n el.style.height = `${emuToMm(frame.heightEmu)}mm`;\n el.style.overflow = \"hidden\";\n el.style.boxSizing = \"border-box\";\n if (frame.behindText) el.style.zIndex = \"-1\";\n else if (frame.zIndex !== undefined) el.style.zIndex = String(frame.zIndex);\n // Let pointer events through to the body by default; selection\n // wiring (a follow-up step) will re-enable per-frame as needed.\n el.style.pointerEvents = \"none\";\n\n paintContent(el, frame, ctx);\n return el;\n}\n\nfunction paintContent(\n host: HTMLElement,\n frame: AnchoredFrame,\n ctx: AnchorLayerContext,\n): void {\n const c = frame.content;\n switch (c.kind) {\n case \"picture\":\n return paintPicture(host, c, ctx);\n case \"shape\":\n return paintShape(host, c);\n case \"textbox\":\n return paintTextbox(host, c, ctx);\n case \"group\":\n return paintGroup(host, c, frame.widthEmu, frame.heightEmu, ctx);\n }\n}\n\nfunction paintPicture(\n host: HTMLElement,\n content: Extract<AnchoredContent, { kind: \"picture\" }>,\n ctx: AnchorLayerContext,\n): void {\n const url = resolvePictureUrl(content.partPath, ctx);\n if (!url) return;\n const img = document.createElement(\"img\");\n img.src = url;\n img.alt = content.altText ?? \"\";\n img.style.width = \"100%\";\n img.style.height = \"100%\";\n img.style.display = \"block\";\n img.style.objectFit = \"fill\";\n host.appendChild(img);\n}\n\nfunction paintShape(\n host: HTMLElement,\n content: Extract<AnchoredContent, { kind: \"shape\" }>,\n): void {\n if (content.fill) host.style.background = content.fill;\n if (content.border) applyBorder(host, content.border);\n switch (content.geometry) {\n case \"ellipse\":\n host.style.borderRadius = \"50%\";\n break;\n case \"roundedRect\":\n // Word's preset corner radius is roughly 25% of the shorter\n // side. Without per-shape adjustment values we use a fixed\n // sensible default — matches the visual most \"rounded rect\"\n // decorations show.\n host.style.borderRadius = \"8px\";\n break;\n case \"line\":\n // A line is a 1-D shape. We render the border as a horizontal\n // rule centered vertically; for a vertical line the EMU dims\n // would naturally make it tall+thin so this still reads right.\n break;\n case \"rect\":\n default:\n break;\n }\n}\n\nfunction paintTextbox(\n host: HTMLElement,\n content: Extract<AnchoredContent, { kind: \"textbox\" }>,\n ctx: AnchorLayerContext,\n): void {\n // The textbox FRAME (fill, border, padding) renders so the visual\n // chrome lands at the OOXML-declared coordinates.\n if (content.fill) host.style.background = content.fill;\n if (content.border) applyBorder(host, content.border);\n if (content.padding) {\n const p = content.padding;\n host.style.padding =\n `${emuToMm(p.topEmu)}mm ` +\n `${emuToMm(p.rightEmu)}mm ` +\n `${emuToMm(p.bottomEmu)}mm ` +\n `${emuToMm(p.leftEmu)}mm`;\n }\n // The textbox body is the anchor layer's text source. (The legacy\n // lifter that used to emit these paragraphs into body flow is gone;\n // `parseAnchoredFrames` claims the drawing so there's no double\n // render.) Use the injected `renderBody` (PaperStack wires in the\n // full `renderBlocks` pipeline); fall back to plain stacked text\n // when no renderer is supplied (headless / unit-test paths).\n if (ctx.renderBody) {\n ctx.renderBody(content.body, host);\n } else {\n for (const block of content.body) {\n if (block.kind !== \"paragraph\") continue;\n const p = document.createElement(\"p\");\n p.style.margin = \"0\";\n p.textContent = block.runs\n .map((r) => (r.kind === \"text\" ? r.text : \"\"))\n .join(\"\");\n host.appendChild(p);\n }\n }\n}\n\nfunction paintGroup(\n host: HTMLElement,\n content: Extract<AnchoredContent, { kind: \"group\" }>,\n frameWidthEmu: number,\n frameHeightEmu: number,\n ctx: AnchorLayerContext,\n): void {\n // Children's offsets are in the group's local coordinate system\n // (`childCoordSystemCx/Cy`); the group is rendered at the frame's\n // actual size (`frameWidthEmu/Cy`). Scale ratio = frame / local.\n const scaleX =\n content.childCoordSystemCx > 0 ? frameWidthEmu / content.childCoordSystemCx : 1;\n const scaleY =\n content.childCoordSystemCy > 0 ? frameHeightEmu / content.childCoordSystemCy : 1;\n for (const child of content.children) {\n const childEl = renderFrame(\n {\n ...child,\n offsetXEmu: child.offsetXEmu * scaleX,\n offsetYEmu: child.offsetYEmu * scaleY,\n widthEmu: child.widthEmu * scaleX,\n heightEmu: child.heightEmu * scaleY,\n },\n ctx,\n );\n host.appendChild(childEl);\n }\n}\n\nfunction applyBorder(\n host: HTMLElement,\n border: { color: string; widthEmu: number; style: \"solid\" | \"dashed\" | \"dotted\" | \"double\" },\n): void {\n const widthPx = Math.max(1, Math.round(emuToPx(border.widthEmu)));\n host.style.border = `${widthPx}px ${border.style} ${border.color}`;\n}\n\nfunction resolvePictureUrl(\n partPath: string,\n ctx: AnchorLayerContext,\n): string | null {\n const cached = ctx.pictureUrlCache.get(partPath);\n if (cached) return cached;\n const bytes = ctx.rawParts[partPath];\n if (!bytes) return null;\n const mime = mimeFromPath(partPath);\n const blob = new Blob([new Uint8Array(bytes)], { type: mime });\n const url = URL.createObjectURL(blob);\n ctx.pictureUrlCache.set(partPath, url);\n return url;\n}\n\nfunction mimeFromPath(path: string): string {\n const ext = path.toLowerCase().split(\".\").pop();\n switch (ext) {\n case \"png\":\n return \"image/png\";\n case \"jpg\":\n case \"jpeg\":\n return \"image/jpeg\";\n case \"gif\":\n return \"image/gif\";\n case \"svg\":\n return \"image/svg+xml\";\n case \"webp\":\n return \"image/webp\";\n default:\n return \"application/octet-stream\";\n }\n}\n","import {\n type PageSetup,\n type VerticalAlign,\n resolvedDimensions,\n} from \"./pageSetup\";\nimport { renderBlocks } from \"../editor/view/docRenderer/block\";\nimport {\n type AnchorLayerContext,\n renderAnchorLayer,\n} from \"../editor/view/docRenderer/anchorLayer\";\nimport type {\n AnchoredFrame,\n Block,\n NamedStyle,\n NumberingDefinition,\n SectionProperties,\n} from \"../doc/types\";\n\nexport interface ZoneRenderContext {\n blocks: readonly Block[];\n numbering: readonly NumberingDefinition[];\n styles: readonly NamedStyle[];\n rawParts: Record<string, Uint8Array>;\n pageNumber: number;\n totalPages: number;\n}\n\n/**\n * A single paper — exactly one page. Fixed width and height from the page\n * setup. Has a header zone, a content area, and a footer zone. The content\n * area is where editable blocks live (the PaperStack assigns them here).\n *\n * Header/footer elements are contentEditable=false; the content area inherits\n * editability from the PaperStack root.\n */\nexport class Paper {\n /**\n * Outer row container — holds the paper card + the right-side\n * comments sidebar. `PaperStack` appends this to its root; CSS uses\n * flex layout so comments sit beside (not inside) the paper.\n */\n readonly outer: HTMLElement;\n /** The paper card itself — sized to the page dimensions. */\n readonly root: HTMLElement;\n readonly content: HTMLElement;\n /**\n * Per-page footnote zone. Sits between body content and the footer\n * INSIDE the paper card. Populated by `PaperStack` after pagination\n * distributes footnote bodies to the page where their referencing\n * `<sup>` landed. Empty zones get `is-empty` for CSS hiding.\n */\n readonly footnotes: HTMLElement;\n /**\n * Per-page floating-object overlay. Absolute-positioned children\n * (anchored textboxes, decorative shapes, anchored pictures) live\n * here, painted on top of `content` by the renderer's anchor-layer\n * module. The paginator never sees these — flow content and\n * floating content are fully decoupled.\n */\n readonly anchors: HTMLElement;\n /**\n * Per-page side gutter — sits to the RIGHT of the paper card\n * (sibling of `root`, child of `outer`). Core leaves it empty (and\n * `is-empty`, so it collapses); the `@sobree/review` plugin fills it\n * with comment cards. Kept in core as a stable mount point so the\n * `.paper-row` flex layout — which the viewport's fit-to-width\n * depends on — doesn't shift when a plugin attaches or detaches.\n */\n readonly comments: HTMLElement;\n private readonly header: HTMLElement;\n private readonly footer: HTMLElement;\n\n constructor(container: HTMLElement, setup: PageSetup) {\n this.outer = document.createElement(\"div\");\n this.outer.className = \"paper-row\";\n\n this.root = document.createElement(\"div\");\n this.root.className = \"paper\";\n\n this.header = document.createElement(\"div\");\n this.header.className = \"paper-header\";\n this.header.contentEditable = \"false\";\n\n this.content = document.createElement(\"div\");\n this.content.className = \"paper-content\";\n\n // The anchor layer is a SIBLING of paper-content (not a child).\n // The editor wipes content via `replaceChildren()` on every render,\n // which would destroy any child layer. Sitting alongside content\n // inside the same paper card keeps it pinned to the page geometry\n // and outside the rewrite path. CSS positions it absolutely so it\n // overlays the content rectangle.\n this.anchors = document.createElement(\"div\");\n this.anchors.className = \"paper-anchors is-empty\";\n this.anchors.contentEditable = \"false\";\n\n this.footnotes = document.createElement(\"div\");\n this.footnotes.className = \"paper-footnotes is-empty\";\n this.footnotes.contentEditable = \"false\";\n\n this.footer = document.createElement(\"div\");\n this.footer.className = \"paper-footer\";\n this.footer.contentEditable = \"false\";\n\n this.root.append(this.header, this.content, this.footnotes, this.footer, this.anchors);\n\n this.comments = document.createElement(\"div\");\n this.comments.className = \"paper-comments is-empty\";\n this.comments.contentEditable = \"false\";\n\n this.outer.append(this.root, this.comments);\n container.appendChild(this.outer);\n this.applySetup(setup);\n }\n\n applySetup(setup: PageSetup): void {\n const { widthMM, heightMM } = resolvedDimensions(setup);\n const m = setup.margins;\n const s = this.root.style;\n s.width = `${widthMM}mm`;\n s.height = `${heightMM}mm`;\n s.setProperty(\"--margin-top\", `${m.top}mm`);\n s.setProperty(\"--margin-right\", `${m.right}mm`);\n s.setProperty(\"--margin-bottom\", `${m.bottom}mm`);\n s.setProperty(\"--margin-left\", `${m.left}mm`);\n this.content.style.justifyContent = justifyContentFor(setup.verticalAlign);\n }\n\n /**\n * Apply per-section settings on top of the base PageSetup. Currently\n * just `vAlign` — extends naturally to per-section page size, margins,\n * column counts, etc. when those land. Idempotent; called by\n * PaperStack after pagination distributes content.\n */\n applySectionOverride(section: SectionProperties): void {\n this.content.style.justifyContent = justifyContentFor(section.vAlign);\n // Header / footer offsets from `<w:pgMar w:header w:footer/>`.\n // Word positions the header text headerTwips from the page TOP,\n // and the body starts AT LEAST headerTwips + header content height\n // below the page top (whichever is more — body never sits under\n // the header). Tracked here as a CSS var so `applyZoneOverflowPadding`\n // can read it and ensure the body padding-top accommodates the\n // running header text + its top offset.\n const m = section.pageMargins;\n if (m?.headerTwips !== undefined) {\n const mm = (m.headerTwips / 1440) * 25.4;\n this.root.style.setProperty(\"--header-offset-mm\", `${mm}mm`);\n }\n if (m?.footerTwips !== undefined) {\n const mm = (m.footerTwips / 1440) * 25.4;\n this.root.style.setProperty(\"--footer-offset-mm\", `${mm}mm`);\n }\n }\n\n /**\n * Render the header zone from the rich AST. Uses the same `renderBlocks`\n * walker as body content so drawings, formatting, hyperlinks and\n * tables all carry through. `PAGE` / `NUMPAGES` field nodes are\n * substituted with this paper's page number / the total count.\n */\n setHeaderBlocks(ctx: ZoneRenderContext): void {\n renderZone(this.header, ctx);\n this.applyZoneOverflowPadding();\n }\n\n setFooterBlocks(ctx: ZoneRenderContext): void {\n renderZone(this.footer, ctx);\n this.applyZoneOverflowPadding();\n }\n\n /**\n * When a rich header (or footer) renders taller than the page's\n * reserved margin, push body content out of the overlap.\n *\n * Word's behaviour: if header content exceeds `pgMar.top`, the body\n * yields downward. Same for footer + `pgMar.bottom`. Our paper\n * normally sets `padding-top: var(--margin-top)`; we override here to\n * `max(--margin-top, headerOffsetHeight)` (resp. for footer) so the\n * body's first paragraph never sits underneath the rendered header.\n *\n * The override is purely visual — the paginator still uses\n * `setup.margins.top/bottom` for its budget, so pages with very tall\n * headers may end up packing fewer body lines than expected. That's\n * a follow-up: feed the observed zone heights back into\n * `pageContentHeightPx` so pagination matches Word's effective body\n * area.\n */\n private applyZoneOverflowPadding(): void {\n const headerPx = this.header.offsetHeight;\n const footerPx = this.footer.offsetHeight;\n // Strip any prior override so the inline style doesn't beat the\n // CSS-var default when content shrinks back down to fit.\n this.root.style.removeProperty(\"padding-top\");\n this.root.style.removeProperty(\"padding-bottom\");\n const marginTopPx = parsePxFromMm(this.root.style.getPropertyValue(\"--margin-top\"));\n const marginBottomPx = parsePxFromMm(this.root.style.getPropertyValue(\"--margin-bottom\"));\n // Word's `<w:pgMar w:header>` reserves headerTwips of space ABOVE\n // the header text. Sobree's CSS `.paper-header` applies that via\n // `padding-top: var(--header-offset-mm, 12.7mm)`, which is\n // INCLUDED in `header.offsetHeight` — so the body just needs to\n // clear the header's actual rendered height, no extra add. (An\n // earlier version added headerOffsetPx on top of headerPx and\n // double-counted ~13mm per page, stealing ~25mm of body budget\n // and triggering systemic page-1 overflow on complex-multipage.)\n if (headerPx > marginTopPx + 1) {\n this.root.style.paddingTop = `${headerPx}px`;\n }\n if (footerPx > marginBottomPx + 1) {\n this.root.style.paddingBottom = `${footerPx}px`;\n }\n }\n\n /**\n * Plain-text fallback for callers that don't have rich blocks (legacy\n * page-setup modal templates that render to a single line of text).\n * Mirrors what `setHeaderBlocks` would do for an \"x of y\"-style\n * paragraph; kept as a separate method to keep the call sites obvious.\n */\n setHeaderText(text: string): void {\n setZoneText(this.header, text);\n }\n\n setFooterText(text: string): void {\n setZoneText(this.footer, text);\n }\n\n /**\n * Replace the anchor layer with frames whose anchor resolves to this\n * page. Idempotent — the old layer is wiped before painting the new\n * one. Pass `frames=[]` to clear the layer (sets `is-empty` so the\n * pointer-events-blocking overlay collapses out of the way).\n */\n setAnchoredFrames(\n frames: readonly AnchoredFrame[],\n ctx: AnchorLayerContext,\n ): void {\n const fresh = renderAnchorLayer(frames, ctx);\n // Keep the same DOM node so external observers (selection layer,\n // CSS) can hold stable references — copy children + classes from\n // the freshly built layer.\n this.anchors.replaceChildren(...Array.from(fresh.children));\n this.anchors.classList.toggle(\"is-empty\", frames.length === 0);\n }\n\n destroy(): void {\n this.outer.remove();\n }\n}\n\nconst MM_TO_PX = 96 / 25.4;\n\nfunction parsePxFromMm(value: string): number {\n const trimmed = value.trim();\n if (!trimmed) return 0;\n if (trimmed.endsWith(\"mm\")) {\n const n = Number(trimmed.slice(0, -2));\n return Number.isFinite(n) ? n * MM_TO_PX : 0;\n }\n if (trimmed.endsWith(\"px\")) {\n const n = Number(trimmed.slice(0, -2));\n return Number.isFinite(n) ? n : 0;\n }\n return 0;\n}\n\nfunction renderZone(zone: HTMLElement, ctx: ZoneRenderContext): void {\n zone.replaceChildren();\n if (ctx.blocks.length === 0) {\n zone.classList.add(\"is-empty\");\n return;\n }\n renderBlocks(ctx.blocks, zone, ctx.numbering, ctx.styles, ctx.rawParts);\n substituteFieldNodes(zone, ctx.pageNumber, ctx.totalPages);\n // \"Empty\" here = no rendered text anywhere; an image-only header\n // still counts as non-empty so the `is-empty` CSS doesn't collapse\n // away its space.\n const hasContent =\n (zone.textContent ?? \"\").trim().length > 0 ||\n zone.querySelector(\"img, svg, table, .sobree-field\") !== null;\n zone.classList.toggle(\"is-empty\", !hasContent);\n}\n\nfunction setZoneText(zone: HTMLElement, text: string): void {\n zone.textContent = text;\n zone.classList.toggle(\"is-empty\", text.trim() === \"\");\n}\n\n/**\n * Walk the rendered header/footer subtree, replacing each\n * `<span class=\"sobree-field\" data-field=\"...\">` node's text with the\n * live PAGE / NUMPAGES value for this paper.\n *\n * Done as a post-render pass instead of inside the inline renderer so\n * `renderBlocks` stays page-agnostic (it has no concept of \"which page\n * is this paragraph on\") and the header pipeline keeps the substitution\n * concern in one place.\n */\nfunction substituteFieldNodes(\n zone: HTMLElement,\n pageNumber: number,\n totalPages: number,\n): void {\n const fields = zone.querySelectorAll<HTMLElement>(\"span.sobree-field\");\n for (const field of Array.from(fields)) {\n const instr = (field.dataset.field ?? \"\").trim().toUpperCase();\n if (instr === \"PAGE\") field.textContent = String(pageNumber);\n else if (instr === \"NUMPAGES\") field.textContent = String(totalPages);\n // Unknown instructions keep whatever the AST cached (Word writes a\n // cached value for non-resolvable fields, e.g. SECTION). No-op.\n }\n}\n\n/**\n * Map a section vAlign to the matching `justify-content` value.\n *\n * `both` (OOXML's \"justified\") would need to redistribute paragraph\n * spacing — closer to `space-between` than to a single shift. `space-\n * between` keeps the visual outcome correct for the common case\n * (heading at top, last block at bottom) without rewriting any\n * paragraph margins.\n */\nfunction justifyContentFor(v: VerticalAlign | undefined): string {\n switch (v) {\n case \"center\":\n return \"center\";\n case \"bottom\":\n return \"flex-end\";\n case \"both\":\n return \"space-between\";\n case \"top\":\n default:\n return \"flex-start\";\n }\n}\n","/**\n * Save and restore the current document selection as a pair of (node, offset)\n * pairs. Works across block re-parenting because text nodes are referenced\n * directly — only breaks when the saved node is actually removed from the DOM.\n */\n\nexport interface SavedSelection {\n startContainer: Node;\n startOffset: number;\n endContainer: Node;\n endOffset: number;\n}\n\nexport function saveSelection(): SavedSelection | null {\n const sel = window.getSelection();\n if (!sel || sel.rangeCount === 0) return null;\n const r = sel.getRangeAt(0);\n return {\n startContainer: r.startContainer,\n startOffset: r.startOffset,\n endContainer: r.endContainer,\n endOffset: r.endOffset,\n };\n}\n\nexport function restoreSelection(saved: SavedSelection | null): void {\n if (!saved) return;\n if (!document.contains(saved.startContainer) || !document.contains(saved.endContainer)) return;\n try {\n const range = document.createRange();\n range.setStart(saved.startContainer, saved.startOffset);\n range.setEnd(saved.endContainer, saved.endOffset);\n const sel = window.getSelection();\n sel?.removeAllRanges();\n sel?.addRange(range);\n } catch {\n // Saved offsets may be invalid after layout shift — give up silently.\n }\n}\n","import \"./paperStack.css\";\nimport {\n type PageSetup,\n resolvedDimensions,\n substituteVariables,\n zoneTemplateFor,\n} from \"./pageSetup\";\nimport { paginateBlocks } from \"./paginationAdapter\";\nimport { Paper } from \"./paper\";\nimport { renderBlocks } from \"../editor/view/docRenderer/block\";\nimport { restoreSelection, saveSelection } from \"../util/selection\";\nimport type {\n AnchoredFrame,\n Block,\n NamedStyle,\n NumberingDefinition,\n SectionProperties,\n} from \"../doc/types\";\n\n/**\n * Sourced from `SobreeDocument`: the rich AST + the dependencies\n * `renderBlocks` needs (numbering, named styles, embedded media bytes).\n * When set, every page renders headers/footers from this AST and\n * ignores the `PageSetup.header.default` / `footer.default` string\n * templates. Cleared (set back to `null`) when the doc has no\n * header/footer parts — falls back to the legacy text path.\n */\nexport interface RichZonesSource {\n headerFooterBodies: Record<string, Block[]>;\n numbering: readonly NumberingDefinition[];\n styles: readonly NamedStyle[];\n rawParts: Record<string, Uint8Array>;\n}\n\nconst MM_TO_PX = 96 / 25.4;\n\n/** Cap on iterative repagination retries. Each iteration shrinks the\n * budget by the observed overflow, so convergence is exponential —\n * 3 is plenty for any realistic doc and prevents accidental infinite\n * loops on pathological content. */\nconst MAX_REPAGINATE_RETRIES = 3;\n\n/** \"Is this page actually overflowing enough to warrant a re-pack?\"\n * Set to ~one body line — sub-line overflows are visually\n * imperceptible and re-paginating to fix them tends to shift page\n * breaks by a full line elsewhere, drifting AWAY from Word/LibreOffice\n * break points rather than toward them. We only iterate when the\n * overflow exceeds a typical line height. (~28px ≈ 21pt at 12pt\n * body — covers the common case where split-slippage adds a line.) */\nconst OVERFLOW_TOLERANCE_PX = 28;\n\n/**\n * Stack of Paper elements. The stack's root is the single contentEditable\n * region; each paper's header/footer is contentEditable=false. Blocks of\n * editor content are physically distributed across the papers'\n * `.paper-content` elements. Editing naturally crosses page boundaries\n * because the whole stack is one editable region.\n *\n * Call `repaginate()` after content changes or after page setup changes.\n */\nexport class PaperStack {\n readonly root: HTMLElement;\n private papers: Paper[] = [];\n private setup: PageSetup;\n /**\n * Layout-side zoom tier currently applied to an ancestor (via the Viewport's\n * CSS `zoom`). Measurements from offsetHeight/offsetTop come back in zoomed\n * pixels, so the page budget must scale the same way.\n */\n private renderTier = 1;\n /** Optional observer fired after every successful repagination. */\n private paginateListeners: Set<(pageCount: number) => void> = new Set();\n /**\n * Per-section property overrides. When set, each Paper applies the\n * section's settings (currently just `vAlign`) based on the\n * `data-section-index` stamped on its first block. Sobree provides\n * this from the live document; absent → all pages use `setup`.\n */\n private sections: SectionProperties[] | null = null;\n /**\n * Optional rich-AST source for header/footer rendering. When present,\n * each paper's header/footer is rendered from the matching part body\n * (resolved through `sections[i].headerRefs` / `footerRefs`) instead\n * of from the `PageSetup` string template. Set via `setRichZones`.\n */\n private richZones: RichZonesSource | null = null;\n /**\n * Document's floating-layer frames (the new architecture replacing\n * the lifter + framePictures hacks). When set, each paper is painted\n * with the subset of frames whose anchor resolves to that page;\n * paper.setAnchoredFrames does the per-page filtering and DOM swap.\n * `null` → no floating layer at all (skeleton state during Phase B).\n */\n private anchoredFrames: AnchoredFrame[] | null = null;\n /** Reused blob-URL cache across renders so the same image isn't re-uploaded. */\n private readonly anchorPictureUrlCache = new Map<string, string>();\n\n constructor(container: HTMLElement, setup: PageSetup) {\n this.setup = setup;\n this.root = document.createElement(\"div\");\n this.root.className = \"paper-stack\";\n container.appendChild(this.root);\n this.ensurePaperCount(1);\n this.renderAllZones();\n }\n\n /**\n * Provide the document's sections for per-page property application.\n * Re-applied immediately so a setSections call without a content\n * change still updates papers (e.g. user edits a section's vAlign in\n * the future Page Setup section selector).\n */\n setSections(sections: readonly SectionProperties[]): void {\n this.sections = sections.slice();\n this.applyPerSectionSettings();\n }\n\n /**\n * Install or clear the rich AST source for header/footer rendering.\n * Re-renders zones immediately so the swap is visible without waiting\n * for the next paginate. Pass `null` to revert to the legacy text-\n * template path (no rich body available).\n */\n setRichZones(source: RichZonesSource | null): void {\n this.richZones = source;\n this.renderAllZones();\n }\n\n /**\n * Install or clear the document's floating-layer frames. Each frame's\n * `anchor.paragraphIndex` (when set) decides which page receives it\n * after the next repaginate; frames without a paragraph index land\n * on the first page of their section. Pass `null` to clear.\n *\n * Call after `setRichZones`/`updateBodyBlocks` so the next render\n * picks up the new floating content alongside body flow.\n */\n setAnchoredFrames(frames: readonly AnchoredFrame[] | null): void {\n this.anchoredFrames = frames ? frames.slice() : null;\n this.paintAnchorLayers();\n }\n\n getSetup(): PageSetup {\n return this.setup;\n }\n\n getPageCount(): number {\n return this.papers.length;\n }\n\n onPaginate(cb: (pageCount: number) => void): () => void {\n this.paginateListeners.add(cb);\n return () => this.paginateListeners.delete(cb);\n }\n\n /** First paper's content — initial render target for a new editor. */\n get primaryContent(): HTMLElement {\n const p = this.papers[0];\n if (!p) throw new Error(\"PaperStack has no papers\");\n return p.content;\n }\n\n /** First paper card — for viewport fit-to-page calculations. */\n get firstPaper(): HTMLElement {\n const p = this.papers[0];\n if (!p) throw new Error(\"PaperStack has no papers\");\n return p.root;\n }\n\n /** First paper's row wrapper (paper + comments sidebar) — for fit-to-width. */\n get firstPaperRow(): HTMLElement {\n const p = this.papers[0];\n if (!p) throw new Error(\"PaperStack has no papers\");\n return p.outer;\n }\n\n /** All content hosts in document order — for serialization. */\n get contentHosts(): HTMLElement[] {\n return this.papers.map((p) => p.content);\n }\n\n updateSetup(setup: PageSetup): void {\n this.setup = setup;\n for (const p of this.papers) p.applySetup(setup);\n this.repaginate();\n }\n\n /**\n * Called by the Viewport when the layout-zoom tier changes. In Chromium,\n * CSS `zoom` doesn't change `offsetHeight`/`offsetTop`, so measurements\n * stay logical and the page budget is unaffected. We still re-paginate\n * defensively — tier changes re-render text at a new resolution, which\n * can nudge line wrapping at sub-pixel boundaries.\n */\n setRenderTier(tier: number): void {\n if (tier === this.renderTier) return;\n this.renderTier = tier;\n this.repaginate();\n }\n\n /**\n * Redistribute blocks across papers so no paper overflows. Creates or\n * removes papers as needed.\n *\n * Delegates to the pure paginator in `src/pagination/` via the DOM\n * adapter. Before paginating, blocks are **consolidated** into the first\n * paper's content so their `offsetTop` values share one coordinate system,\n * and consecutive `<p>` fragments sharing a `data-pag-pid` (previous-split\n * siblings of the same logical paragraph) are **merged** back into a\n * single paragraph — so each repagination starts from the clean logical\n * flow instead of accumulating inter-fragment margins.\n */\n repaginate(): void {\n const initialBlocks = this.collectAllBlocks();\n\n if (initialBlocks.length === 0) {\n this.ensurePaperCount(1);\n this.renderAllZones();\n this.emitPaginate();\n return;\n }\n\n const saved = saveSelection();\n const baselineBudgetPx = this.pageContentHeightPx();\n // Iterative paginate with per-page budget. The paginator gets a\n // `pageHeights[]` array — entry `i` is page `i`'s budget after\n // subtracting whatever space its footnote zone occupies. Pages\n // without footnotes use the full baseline budget. Each iteration:\n //\n // 1. paginate body with current `pageHeights`\n // 2. distribute footnotes — populates per-page zones based on\n // where the refs landed\n // 3. rebuild `pageHeights` from observed footnote zone heights\n // 4. if any page's body now overflows its new (smaller) budget,\n // retry with the updated array\n //\n // Bounded by `MAX_REPAGINATE_RETRIES`; each iteration is exponential\n // so 3 is plenty for any realistic doc.\n let pageHeights: number[] = [];\n for (let attempt = 0; attempt <= MAX_REPAGINATE_RETRIES; attempt++) {\n this.runPaginationOnce(baselineBudgetPx, pageHeights);\n this.distributeFootnotes();\n const newHeights = this.computePageHeights(baselineBudgetPx);\n const stable = arraysEqual(newHeights, pageHeights);\n pageHeights = newHeights;\n // Stable + no overflow → done. Stable + overflow shouldn't\n // happen (the shrunken budget already reserved footnote space),\n // but guard with the existing overflow check anyway.\n const overflowPx = this.maxPaperOverflowPx();\n if (stable && overflowPx <= OVERFLOW_TOLERANCE_PX) break;\n }\n\n // Post-pagination: absorb under-filled middle papers. After the\n // distribution loop converges, each paper has its blocks laid\n // out in their final layout, so offsetHeight is reliable —\n // measurements during pagination see absolute-positioned\n // section-frame decorations as 0-height (they're out of flow at\n // measurement time), so the in-paginate `collapseUnderfilledPages`\n // pass under-estimates page contents and only catches truly tiny\n // widows. This second pass uses the true rendered heights.\n // Disabled — this post-distribute absorption was a session hack\n // that fused tiny widow pages into the previous one to match a\n // page-count target. With anchored content now living in its own\n // layer (not consuming body-flow space), the paginator's primary\n // pass produces correctly-sized pages on its own. Leaving the\n // absorber on caused page 2 of complex-multipage to overflow by\n // 696px (182% fill) — pulling section-heading + bullet content\n // out of page 3 onto an already-full page 2.\n void baselineBudgetPx;\n\n restoreSelection(saved);\n this.renderAllZones();\n this.applyPerSectionSettings();\n this.emitPaginate();\n }\n\n /**\n * Per-page footnote pinning. After body pagination completes, scan\n * each paper for footnote references (`<sup class=\"sobree-footnote-ref\">`)\n * and populate that paper's `.paper-footnotes` zone with the cited\n * bodies — matching Word's \"footnote bodies render at the bottom of\n * the page where they're referenced\" behaviour.\n *\n * Footnote bodies are sourced from two places:\n * 1. The doc-end `<aside class=\"sobree-footnotes\">` the renderer\n * initially appends to paper[0].content. We harvest the `<li>`s\n * out of it before the aside itself gets re-paginated as a block.\n * 2. Any footnote zones already populated from a previous repaginate\n * pass — those bodies stay in scope across iterations.\n *\n * Caveat: body budget is NOT reduced for footnote-zone height. On\n * pages with many footnotes, the footnote zone may extend past the\n * page bottom into the footer area. True budget-reservation is its\n * own paginator feature.\n */\n private distributeFootnotes(): void {\n // 1. Harvest existing footnote bodies (from any source) into a\n // Map<id, HTMLElement>. Bodies are cloned on each insertion so\n // a footnote referenced from multiple pages renders multiple\n // times without DOM ownership conflicts.\n const bodies = new Map<number, HTMLElement>();\n for (const p of this.papers) {\n for (const li of Array.from(p.footnotes.querySelectorAll(\"li[id^='sobree-footnote-']\"))) {\n const id = footnoteIdFromAttr(li.id);\n if (id !== null) bodies.set(id, li as HTMLElement);\n }\n p.footnotes.replaceChildren();\n p.footnotes.classList.add(\"is-empty\");\n }\n // Also harvest from any doc-end `<aside>` left over from the\n // renderer. The paginator treats it as a regular block, so it may\n // have landed on any page — scan them all.\n for (const p of this.papers) {\n for (const aside of Array.from(p.content.querySelectorAll(\"aside.sobree-footnotes\"))) {\n for (const li of Array.from(aside.querySelectorAll(\"li[id^='sobree-footnote-']\"))) {\n const id = footnoteIdFromAttr(li.id);\n if (id !== null) bodies.set(id, li as HTMLElement);\n }\n aside.remove();\n }\n }\n if (bodies.size === 0) return;\n\n // 2. For each paper, collect referenced ids in document order then\n // populate the footnote zone.\n for (const paper of this.papers) {\n const refs = paper.content.querySelectorAll(\"[id^='sobree-footnote-ref-']\");\n if (refs.length === 0) continue;\n const seen = new Set<number>();\n const list = document.createElement(\"ol\");\n list.className = \"sobree-footnotes__list\";\n for (const ref of Array.from(refs)) {\n const id = footnoteIdFromAttr(\n (ref as HTMLElement).id.replace(\"sobree-footnote-ref-\", \"sobree-footnote-\"),\n );\n if (id === null || seen.has(id)) continue;\n seen.add(id);\n const source = bodies.get(id);\n if (!source) continue;\n const clone = source.cloneNode(true) as HTMLElement;\n // Per-paper id keeps the anchor link working when the same\n // footnote pins to multiple pages: only the first occurrence\n // owns the canonical id.\n if (paper.footnotes.querySelector(`#${clone.id}`)) clone.removeAttribute(\"id\");\n list.appendChild(clone);\n }\n if (list.children.length > 0) {\n paper.footnotes.appendChild(list);\n paper.footnotes.classList.remove(\"is-empty\");\n }\n }\n }\n\n /**\n * One round of consolidate → merge → paginate → distribute.\n * Re-entrant: each call re-collects blocks from every paper, so the\n * iterative loop in `repaginate` can call this multiple times with\n * shrinking budgets to absorb post-split slippage.\n */\n private runPaginationOnce(budgetPx: number, pageHeights: readonly number[] = []): void {\n const blocks = this.collectAllBlocks();\n const firstContent = this.papers[0]!.content;\n for (const block of blocks) {\n if (block.parentElement !== firstContent) firstContent.appendChild(block);\n }\n mergeConsecutiveFragments(firstContent);\n const consolidatedBlocks = Array.from(firstContent.children).filter(\n (c): c is HTMLElement => c instanceof HTMLElement,\n );\n\n const rawPages = paginateBlocks(\n consolidatedBlocks,\n budgetPx,\n pageHeights.length > 0 ? pageHeights : undefined,\n );\n // Trailing-empty absorption: if the last page contains only\n // visually-empty blocks (empty paragraphs, no images, no tables),\n // sweep them onto the previous page. Matches Word's / LibreOffice's\n // behaviour for trailing whitespace — they keep blank tail\n // paragraphs anchored to the prior page rather than spawning a\n // dedicated page. Without this, jellap.docx ends with a 3rd page\n // containing two empty paragraphs.\n const emptyCollapsed = collapseTrailingEmptyPages(rawPages);\n // `collapseUnderfilledPages` (post-process widow absorption) was\n // removed: at this point in the loop the blocks are still all\n // stacked inside `firstContent` (per the `firstContent.appendChild`\n // dance above) and `measureBlocksHeight` reads in-stack\n // `offsetHeight`, which for tables and inline-frame wrappers is\n // 5-10× smaller than the same content's height once distributed\n // to its own paper. Traced live on complex-multipage: page 3's\n // 268px Databases table measured 36px at collapse time → wrongly\n // classified as a 15%-widow → absorbed back onto page 2 → page 2\n // overflows 268px into the footer band.\n //\n // The paginator already produces correct page assignments\n // (verified: with the absorber bypassed, page 2 lands at 98%\n // fill, 0 overflow). Widow handling is a paginator-internals\n // concern that belongs in the Knuth-Plass penalty function\n // (Phase 2, task #164), not a post-process pass.\n const pages = emptyCollapsed;\n const pageCount = Math.max(1, pages.length);\n this.ensurePaperCount(pageCount);\n\n // `distributePages` may insert NEW per-page list clones at\n // specific positions in `pageBlocks` (between H1 and the existing\n // P/H1 siblings that came from `renderBlocks`). Those existing\n // siblings are already children of `firstContent` from the pre-\n // pagination consolidation pass. If we only re-parent blocks that\n // currently live in a different paper, the new clones get\n // `appendChild`-ed at the END of `target.content` and the existing\n // siblings keep their old position — yielding all H1/P first, then\n // all UL clones at the bottom (observed on google-modern.docx: 4\n // section headings cluster above 4 bullet lists). Always call\n // `appendChild` per block in `pageBlocks` order: same-parent calls\n // MOVE the node to the end of the parent's child list, so iterating\n // pageBlocks lays them down in exactly the order the paginator\n // intended. Out-of-flow (`position: absolute|fixed`) elements skip\n // the move — re-parenting under a transformed paper could disturb\n // their resolved positioning ancestor (jellap.docx's anchored\n // textbox frames depend on staying under their original paragraph).\n for (let i = 0; i < pageCount; i++) {\n const target = this.papers[i];\n const pageBlocks = pages[i];\n if (!target || !pageBlocks) continue;\n for (const block of pageBlocks) {\n const pos = getComputedStyle(block).position;\n if (pos === \"absolute\" || pos === \"fixed\") {\n if (block.parentElement !== target.content) {\n target.content.appendChild(block);\n }\n continue;\n }\n target.content.appendChild(block);\n }\n }\n }\n\n /**\n * Build a `pageHeights[]` array from observed footnote-zone heights.\n * Entry `i` = `baselineBudgetPx - footnoteZoneHeight(i)`. Pages\n * without footnotes get the full baseline. Returned array trims\n * trailing entries equal to the baseline, so consumers can detect\n * \"no per-page overrides needed\" via `length === 0`.\n */\n private computePageHeights(baselineBudgetPx: number): number[] {\n const heights: number[] = [];\n for (let i = 0; i < this.papers.length; i++) {\n const paper = this.papers[i]!;\n // Only footnotes share the page with body content; comments\n // live in a sidebar outside the paper card.\n const fnH = paper.footnotes.classList.contains(\"is-empty\")\n ? 0\n : paper.footnotes.offsetHeight;\n heights.push(baselineBudgetPx - fnH);\n }\n // Trim trailing baseline entries so two arrays that only differ by\n // unrelated tail entries compare equal — keeps `arraysEqual` honest.\n while (heights.length > 0 && heights[heights.length - 1] === baselineBudgetPx) {\n heights.pop();\n }\n return heights;\n }\n\n /**\n * Largest content overflow across all papers, in CSS px. Zero means\n * every paper fits within its content area. Used by the iterative\n * `repaginate` to detect post-split slippage that needs another\n * pagination pass with a tighter budget.\n */\n private maxPaperOverflowPx(): number {\n const budget = this.pageContentHeightPx();\n let max = 0;\n for (const paper of this.papers) {\n const blocks = Array.from(paper.content.children);\n const last = blocks[blocks.length - 1] as HTMLElement | undefined;\n if (!last) continue;\n const used = last.offsetTop + last.offsetHeight;\n // Footnote zone (when non-empty) sits absolutely positioned just\n // above the footer area, stealing visual space from the body\n // budget. Comments now live in a sidebar OUTSIDE the paper, so\n // they don't compete with body — only footnotes need budget\n // reservation here.\n const footnoteH = paper.footnotes.classList.contains(\"is-empty\")\n ? 0\n : paper.footnotes.offsetHeight;\n const effectiveBudget = budget - footnoteH;\n const overflow = used - effectiveBudget;\n if (overflow > max) max = overflow;\n }\n return max;\n }\n\n /**\n * For every Paper, look at its first content block's `data-section-index`\n * and apply that section's overrides. Falls back silently when no\n * sections were provided or a paper is empty.\n */\n private applyPerSectionSettings(): void {\n if (!this.sections) return;\n for (const paper of this.papers) {\n const first = paper.content.firstElementChild as HTMLElement | null;\n const raw = first?.dataset.sectionIndex;\n const idx = raw === undefined ? 0 : Number(raw);\n const section = this.sections[idx] ?? this.sections[0];\n if (section) paper.applySectionOverride(section);\n }\n }\n\n private emitPaginate(): void {\n const count = this.getPageCount();\n for (const cb of this.paginateListeners) {\n try {\n cb(count);\n } catch (err) {\n console.error(\"[sobree] paginate listener threw:\", err);\n }\n }\n }\n\n destroy(): void {\n this.paginateListeners.clear();\n for (const p of this.papers) p.destroy();\n this.papers = [];\n this.root.remove();\n }\n\n private pageContentHeightPx(): number {\n // Prefer the EFFECTIVE content area height — what the first paper's\n // `.paper-content` element actually reports after CSS layout. The\n // `Paper.applyZoneOverflowPadding` may have bumped the paper's\n // padding-top above `setup.margins.top` to make room for a\n // taller-than-margin rich header (jellap.docx pushes body down by\n // ~50mm because its header has logo + lifted contact-info textbox).\n // If we ignored this and used the raw margin-derived budget, the\n // paginator would think the page is ~200px taller than visible,\n // overpack page 1, and the user would silently lose content past\n // the bottom edge (Word's \"Rendelkezik-e\" and \"Igényelnek-e\"\n // questions vanished without trace).\n const firstPaper = this.papers[0];\n if (firstPaper) {\n const contentHeightPx = firstPaper.content.offsetHeight;\n if (contentHeightPx > 0) return contentHeightPx;\n }\n const { heightMM } = resolvedDimensions(this.setup);\n const { top, bottom } = this.setup.margins;\n // CSS `zoom` on an ancestor changes `getBoundingClientRect` but NOT\n // `offsetHeight`/`offsetTop` (in Chromium). Since the adapter measures\n // blocks via `offsetHeight`, measurements stay in logical space at any\n // render tier — so the budget stays logical too.\n return (heightMM - top - bottom) * MM_TO_PX;\n }\n\n private collectAllBlocks(): HTMLElement[] {\n const out: HTMLElement[] = [];\n for (const p of this.papers) {\n for (const child of Array.from(p.content.children)) {\n if (child instanceof HTMLElement) out.push(child);\n }\n }\n return out;\n }\n\n private ensurePaperCount(n: number): void {\n while (this.papers.length < n) {\n this.papers.push(new Paper(this.root, this.setup));\n }\n while (this.papers.length > n) {\n const p = this.papers.pop();\n p?.destroy();\n }\n }\n\n private renderAllZones(): void {\n const pages = this.papers.length;\n this.papers.forEach((paper, i) => {\n const pageNum = i + 1;\n const sectionIdx = this.sectionIndexForPage(i);\n const isFirstOfSection = this.isFirstPaperOfSection(i, sectionIdx);\n const headerBlocks = this.pickRichZone(\"header\", sectionIdx, isFirstOfSection);\n const footerBlocks = this.pickRichZone(\"footer\", sectionIdx, isFirstOfSection);\n\n if (this.richZones && headerBlocks !== null) {\n paper.setHeaderBlocks({\n blocks: headerBlocks,\n numbering: this.richZones.numbering,\n styles: this.richZones.styles,\n rawParts: this.richZones.rawParts,\n pageNumber: pageNum,\n totalPages: pages,\n });\n } else {\n const hTpl = zoneTemplateFor(this.setup.header, pageNum, pages);\n paper.setHeaderText(substituteVariables(hTpl, { page: pageNum, pages }));\n }\n\n if (this.richZones && footerBlocks !== null) {\n paper.setFooterBlocks({\n blocks: footerBlocks,\n numbering: this.richZones.numbering,\n styles: this.richZones.styles,\n rawParts: this.richZones.rawParts,\n pageNumber: pageNum,\n totalPages: pages,\n });\n } else {\n const fTpl = zoneTemplateFor(this.setup.footer, pageNum, pages);\n paper.setFooterText(substituteVariables(fTpl, { page: pageNum, pages }));\n }\n });\n // Repaint floating layer alongside zones so a setRichZones (or\n // setSections) call that triggers renderAllZones also refreshes\n // anchor placement.\n this.paintAnchorLayers();\n }\n\n /**\n * Filter the document's `anchoredFrames` per paper and ask each Paper\n * to swap its anchor-layer DOM accordingly. A frame's destination\n * page is determined by its anchor:\n * - `paragraphIndex` set → page that ended up containing that\n * body block (looked up by `data-block-index` stamped on\n * rendered children).\n * - paragraphIndex absent → first paper of the frame's section.\n *\n * Cheap and idempotent; safe to call after any layout change.\n */\n private paintAnchorLayers(): void {\n const frames = this.anchoredFrames;\n if (!this.richZones || frames === null) {\n // No floating layer wanted — clear every paper's overlay.\n for (const p of this.papers) {\n p.setAnchoredFrames([], {\n rawParts: {},\n pictureUrlCache: this.anchorPictureUrlCache,\n });\n }\n return;\n }\n const richZones = this.richZones;\n const ctx = {\n rawParts: richZones.rawParts,\n pictureUrlCache: this.anchorPictureUrlCache,\n // Inject the full block renderer so anchored textbox bodies\n // render with the same paragraph/list/table pipeline as body\n // content — anchorLayer stays decoupled from block.ts.\n renderBody: (blocks: Block[], host: HTMLElement) => {\n renderBlocks(blocks, host, richZones.numbering, richZones.styles, richZones.rawParts);\n },\n };\n // Build per-page assignment. A paragraph-anchored frame lives on\n // the page whose .paper-content contains an element stamped with\n // `data-block-index=\"N\"` where N === paragraphIndex. Anything\n // else falls onto the first page of its section (currently always\n // section 0 — multi-section frame routing is a follow-up).\n const perPage: AnchoredFrame[][] = this.papers.map(() => []);\n const paragraphPage = this.buildParagraphPageIndex();\n for (const frame of frames) {\n let page = 0;\n if (frame.anchor.paragraphIndex !== undefined) {\n const found = paragraphPage.get(frame.anchor.paragraphIndex);\n if (found !== undefined) page = found;\n }\n const bucket = perPage[page];\n if (bucket) bucket.push(frame);\n }\n for (let i = 0; i < this.papers.length; i++) {\n this.papers[i]!.setAnchoredFrames(perPage[i] ?? [], ctx);\n }\n }\n\n /**\n * Walk every paper's content children once and record, for each\n * `data-block-index=\"N\"` element, the paper index that holds it.\n * The renderer stamps that attribute when emitting body paragraphs;\n * this is the only piece of mutual knowledge between body flow and\n * the floating layer needed for paragraph anchoring.\n */\n private buildParagraphPageIndex(): Map<number, number> {\n const out = new Map<number, number>();\n for (let i = 0; i < this.papers.length; i++) {\n const stamped = this.papers[i]!.content.querySelectorAll<HTMLElement>(\"[data-block-index]\");\n for (const el of Array.from(stamped)) {\n const n = Number(el.dataset.blockIndex);\n if (Number.isFinite(n) && !out.has(n)) out.set(n, i);\n }\n }\n return out;\n }\n\n /**\n * Section index a paper belongs to. Read off the first content\n * block's `data-section-index` (stamped by `renderBlocks`). Empty\n * papers and the no-sections case fall back to 0.\n */\n private sectionIndexForPage(paperIdx: number): number {\n const paper = this.papers[paperIdx];\n if (!paper) return 0;\n const first = paper.content.firstElementChild as HTMLElement | null;\n const raw = first?.dataset.sectionIndex;\n if (raw === undefined) return 0;\n const n = Number(raw);\n return Number.isFinite(n) ? n : 0;\n }\n\n /**\n * True when this paper is the first page of its section. Used to\n * select the `first`-typed header/footer ref when the section has\n * `titlePage = true`.\n */\n private isFirstPaperOfSection(paperIdx: number, sectionIdx: number): boolean {\n if (paperIdx === 0) return true;\n return this.sectionIndexForPage(paperIdx - 1) !== sectionIdx;\n }\n\n /**\n * Resolve which header/footer Block[] applies to a paper. Walks the\n * section's refs by type and looks up the corresponding part body in\n * `richZones.headerFooterBodies`. Returns `null` when no rich body\n * applies (no refs, or `richZones` not set).\n */\n private pickRichZone(\n kind: \"header\" | \"footer\",\n sectionIdx: number,\n isFirstOfSection: boolean,\n ): readonly Block[] | null {\n if (!this.richZones || !this.sections) return null;\n // OOXML §17.10.3: when a section omits headerReference / footerReference,\n // the corresponding zone is inherited from the prior section. Walk back\n // until we find a section that declares refs; bail at section 0. This\n // is how multi-section forms (jellap.docx) keep a single header part\n // across continuous section breaks even though only section 0 names it.\n let lookupIdx = sectionIdx;\n let section: SectionProperties | undefined;\n while (lookupIdx >= 0) {\n const candidate = this.sections[lookupIdx];\n if (candidate) {\n const refs = kind === \"header\" ? candidate.headerRefs : candidate.footerRefs;\n if (refs.length > 0) {\n section = candidate;\n break;\n }\n }\n lookupIdx -= 1;\n }\n if (!section) return null;\n const refs = kind === \"header\" ? section.headerRefs : section.footerRefs;\n if (refs.length === 0) return null;\n // Type preference: titlePage + first-of-section → `first`,\n // otherwise → `default`. Even-page support is a future addition.\n const preferred = isFirstOfSection && section.titlePage === true\n ? refs.find((r) => r.type === \"first\") ?? refs.find((r) => r.type === \"default\")\n : refs.find((r) => r.type === \"default\") ?? refs[0];\n if (!preferred) return null;\n return this.richZones.headerFooterBodies[preferred.partId] ?? null;\n }\n}\n\n/**\n * Walk `container`'s direct children and merge adjacent fragments back\n * into their logical parents:\n *\n * - `<p>` siblings sharing a `data-pag-pid` rejoin into one paragraph.\n * - `<ol>` / `<ul>` siblings sharing a `data-pag-lid` rejoin into one\n * list — the head's `start` attribute is preserved, the tail's is\n * dropped, and the tail's `<li>` children move into the head.\n * - INSIDE every merged list, sibling `<li>` fragments sharing a\n * `data-pag-pid` rejoin too — the post-paragraph-split repair\n * applied to LIs since they're now treated like paragraphs by\n * the paginator.\n *\n * Continuation markers (`data-pag-continuation`, `sobree-li-continuation`\n * class) are stripped on merge so the rejoined LI starts fresh. Without\n * this, every repagination pass would accumulate stale fragments and\n * marker-suppression flags.\n */\nfunction mergeConsecutiveFragments(container: HTMLElement): void {\n // Pass 1: merge top-level <p> and <ol>/<ul>.\n let child = container.firstElementChild as HTMLElement | null;\n while (child) {\n const next = child.nextElementSibling as HTMLElement | null;\n if (next && canMergeFragments(child, next)) {\n mergeInto(child, next);\n next.remove();\n // `child` may merge with further siblings too — stay put.\n continue;\n }\n child = next;\n }\n // Pass 2: walk into each list and merge its LI children. This\n // happens AFTER pass 1 so all LIs are back inside one OL / UL\n // before we try to merge them.\n for (const list of Array.from(container.children)) {\n if (list.tagName !== \"OL\" && list.tagName !== \"UL\") continue;\n mergeListItemFragments(list as HTMLElement);\n }\n // Pass 3: merge TBODY contents of joined tables. After pass 1\n // joined adjacent `<table>` fragments sharing `data-pag-tid`, their\n // TBODY's now-sibling rows are already in correct order — but the\n // head TABLE may have ended up with two TBODY children (head's own\n // + the moved-in tail TBODY). Collapse those into one.\n for (const table of Array.from(container.children)) {\n if (table.tagName !== \"TABLE\") continue;\n mergeTableBodyFragments(table as HTMLElement);\n }\n}\n\nfunction canMergeFragments(a: HTMLElement, b: HTMLElement): boolean {\n if (a.tagName !== b.tagName) return false;\n if (a.tagName === \"P\") {\n return !!a.dataset.pagPid && a.dataset.pagPid === b.dataset.pagPid;\n }\n if (a.tagName === \"OL\" || a.tagName === \"UL\") {\n return !!a.dataset.pagLid && a.dataset.pagLid === b.dataset.pagLid;\n }\n if (a.tagName === \"TABLE\") {\n return !!a.dataset.pagTid && a.dataset.pagTid === b.dataset.pagTid;\n }\n return false;\n}\n\n/**\n * Collapse multiple TBODY children of one `<table>` into a single\n * TBODY (head's own + every following TBODY's TRs append into the\n * head's TBODY). Side-effect of merging two table fragments via\n * `mergeInto` — it dumps the tail's entire child list (one TBODY)\n * into the head, leaving the head with two consecutive TBODY's. The\n * browser tolerates that but it confuses the next pagination pass:\n * `tableRowBoxes` only walks the FIRST TBODY's TRs.\n */\nfunction mergeTableBodyFragments(table: HTMLElement): void {\n const tbodies = Array.from(table.children).filter(\n (c): c is HTMLElement => c.tagName === \"TBODY\",\n );\n if (tbodies.length <= 1) return;\n const head = tbodies[0]!;\n for (let i = 1; i < tbodies.length; i++) {\n const tail = tbodies[i]!;\n while (tail.firstChild) head.appendChild(tail.firstChild);\n tail.remove();\n }\n}\n\n/**\n * Merge sibling `<li>` fragments inside a list. Two adjacent LIs sharing\n * a `data-pag-pid` collapse into one; the tail's children move into the\n * head, the tail is removed.\n */\nfunction mergeListItemFragments(list: HTMLElement): void {\n let child = list.firstElementChild as HTMLElement | null;\n while (child) {\n const next = child.nextElementSibling as HTMLElement | null;\n if (\n next &&\n child.tagName === \"LI\" &&\n next.tagName === \"LI\" &&\n child.dataset.pagPid &&\n child.dataset.pagPid === next.dataset.pagPid\n ) {\n mergeInto(child, next);\n next.remove();\n continue;\n }\n child = next;\n }\n}\n\n/**\n * Move all of `tail`'s children into `head` (preserving order) and\n * strip continuation markers from `head` so the merged element looks\n * like a fresh paragraph / LI to the next pagination pass.\n */\nfunction mergeInto(head: HTMLElement, tail: HTMLElement): void {\n while (tail.firstChild) head.appendChild(tail.firstChild);\n // Continuation flags belong to the tail, not the merged whole.\n delete head.dataset.pagContinuation;\n head.classList.remove(\"sobree-li-continuation\");\n}\n\n/** Parse \"sobree-footnote-7\" → 7; returns null for anything else. */\nfunction footnoteIdFromAttr(attr: string): number | null {\n const m = /^sobree-footnote-(\\d+)$/.exec(attr);\n if (!m) return null;\n const n = Number(m[1]);\n return Number.isFinite(n) ? n : null;\n}\n\n/**\n * Walk the paginator's output back-to-front, collapsing trailing pages\n * whose blocks are all visually empty into the previous page.\n *\n * \"Visually empty\" = an empty paragraph: a `<p>` (or list-item `<li>`)\n * with no text content and no embedded image / table / break. We keep\n * the empty blocks (round-trip fidelity demands every paragraph mark\n * stays in the AST) — we just stop reserving a fresh page for them.\n *\n * Stops collapsing the moment a page contains any non-empty block, so\n * a real \"last page with just a signature line\" still gets its own\n * page. Idempotent; safe to call on a single-page result.\n */\n/**\n * Walk paginator output forward, absorbing pages whose total content\n * height is ≤ 15% of `budgetPx` (the page-content budget) into the\n * previous page. These tiny tail-pages are paginator widows — usually\n * a 1-line overflow or an LRPB-induced sub-section that didn't have\n * room. Both Word and LO tolerate a bit of bottom-margin spill in\n * exchange for not spending an entire fresh page on the runt.\n *\n * Safety: only absorbs pages whose content height is smaller than the\n * previous page's slack PLUS the widow tolerance (~20% of budget).\n * If absorbing would visibly push content far past the page bottom,\n * we leave the runt page alone — better an underfilled page than\n * unreadable overflow.\n *\n * Run AFTER `collapseTrailingEmptyPages` so trailing whitespace is\n * already merged and we're working on the real content layout.\n */\nexport function collapseUnderfilledPages(\n pages: readonly HTMLElement[][],\n budgetPx: number,\n): HTMLElement[][] {\n if (budgetPx <= 0) return pages.map((p) => p.slice());\n\n /** Two thresholds, picked by how underfilled the target page is.\n * TIGHT: typical widow — a 1-3 line tail-page. Absorb only when\n * it fits within a modest overflow.\n * AGGRESSIVE: a substantially-underfilled page (≤ 40% fill) is\n * almost always a paginator/LRPB mistake. Worth absorbing even\n * if the resulting overflow is noticeable, because the alternative\n * is an obviously-half-empty page that breaks the eye more than\n * a bit of bottom-margin spill.\n * Pages above the aggressive threshold are left alone. */\n // Only the \"tight\" widow rule. We considered an \"aggressive\" pass\n // for ≤40%-fill pages, but at the moment this function runs the\n // candidate blocks are all stacked together in `firstContent` and\n // any absolute-positioned decoration (section-frame banner, anchored\n // shape, lifted textbox) contributes 0 to its block's flow\n // offsetHeight — so a 700-px text-page that carries a 400-px\n // backgroundframe looks like 300 px to `measureBlocksHeight` and\n // accidentally qualifies as a widow. Constrain to tight so the\n // misclassification can't cascade.\n const WIDOW_FRACTION = 0.15;\n const OVERFLOW_FRACTION = 0.2;\n const widowThresholdPx = budgetPx * WIDOW_FRACTION;\n const overflowPx = budgetPx * OVERFLOW_FRACTION;\n\n const out: HTMLElement[][] = pages.map((p) => p.slice());\n for (let i = 0; i + 1 < out.length; i++) {\n const cur = out[i]!;\n const next = out[i + 1]!;\n // Don't absorb when the NEXT page's first block carries the\n // OOXML `<w:pageBreakBefore/>` directive — merging would override\n // the author's explicit page-boundary intent and stuff the\n // heading mid-page. Per `data-page-break-before` stamped by the\n // renderer from `Paragraph.properties.pageBreakBefore`.\n const nextStartsWithForcedBreak =\n next.length > 0 && next[0]!.hasAttribute(\"data-page-break-before\");\n if (nextStartsWithForcedBreak) continue;\n const curH = measureBlocksHeight(cur);\n const nextH = measureBlocksHeight(next);\n // Refuse outright if cur is already over budget — absorbing more\n // would just compound the overflow.\n if (curH > budgetPx) continue;\n if (nextH > 0 && nextH <= widowThresholdPx\n && curH + nextH <= budgetPx + overflowPx) {\n cur.push(...next);\n out.splice(i + 1, 1);\n // Don't decrement i — we want to advance past the absorbed-into\n // page so we never cascade-absorb several widows into one host\n // page (that would compound overflow).\n }\n }\n return out;\n}\n\n/** Sum offsetHeight of a page's blocks. Detached blocks (not yet in\n * the DOM) report 0 — that's fine; they don't contribute to layout. */\nfunction measureBlocksHeight(blocks: readonly HTMLElement[]): number {\n let total = 0;\n for (const b of blocks) total += b.offsetHeight;\n return total;\n}\n\nexport function collapseTrailingEmptyPages(\n pages: readonly HTMLElement[][],\n): HTMLElement[][] {\n const out: HTMLElement[][] = pages.map((page) => page.slice());\n // Walk from the last page back. While the last page is fully empty\n // and we have a previous page to absorb into, merge it down.\n while (out.length >= 2) {\n const last = out[out.length - 1];\n if (!last || !last.every(isVisuallyEmptyBlock)) break;\n const prev = out[out.length - 2]!;\n prev.push(...last);\n out.pop();\n }\n // Now collapse MIDDLE pages whose ONLY blocks are visually empty —\n // these are pages created by stale page-break hints landing AFTER\n // empty placeholder paragraphs. LO collapses them; Sobree should\n // too. complex-multipage.docx has 2 such pages (intentional\n // `<w:br type=\"page\"/>` followed by empty paragraphs); without\n // this collapse pass we'd render 18 pages instead of LO's 16.\n for (let i = out.length - 2; i >= 0; i--) {\n const page = out[i]!;\n if (page.length === 0) continue;\n if (!page.every(isVisuallyEmptyBlock)) continue;\n // All blocks visually empty. Push them onto the next page's\n // start so document order is preserved, then drop this page.\n const next = out[i + 1];\n if (!next) continue;\n next.unshift(...page);\n out.splice(i, 1);\n }\n return out;\n}\n\nfunction isVisuallyEmptyBlock(el: HTMLElement): boolean {\n const tag = el.tagName;\n // Only collapse paragraph / list-item blocks. Tables, drawings,\n // section breaks etc. always justify a page even when \"empty\".\n if (tag !== \"P\" && tag !== \"LI\" && tag !== \"H1\" && tag !== \"H2\" &&\n tag !== \"H3\" && tag !== \"H4\" && tag !== \"H5\" && tag !== \"H6\") {\n return false;\n }\n // Any text content (after trim) means non-empty.\n if ((el.textContent ?? \"\").trim().length > 0) return false;\n // Embedded images / tables / SVG / drawings keep it non-empty. We\n // include both raw graphic tags AND Sobree's drawing wrappers\n // (section-frame banners, anchored shapes) so a paragraph that's\n // textually empty but carries a poster-sized background drawing\n // — like the project pages of complex-multipage.docx where each\n // \"Project:\" page is a textbox-only layout — never gets absorbed.\n if (el.querySelector(\n \"img, svg, table, canvas, iframe, video, [class*='sobree-section-frame'], [data-sobree-drawing]\",\n ) !== null) {\n return false;\n }\n return true;\n}\n\nfunction arraysEqual(a: readonly number[], b: readonly number[]): boolean {\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) {\n if (a[i] !== b[i]) return false;\n }\n return true;\n}\n","import type {\n Block,\n HeaderFooterRef,\n InlineRun,\n NamedStyle,\n PageMargins,\n PageSize,\n Paragraph,\n ParagraphProperties,\n RunProperties,\n SectionProperties,\n SobreeDocument,\n Table,\n TextRun,\n} from \"./types\";\n\n/**\n * Constructors for AST nodes. Two reasons these exist as separate helpers\n * instead of object literals at every call site:\n * 1. Defaults — letter-paper, no margins set, etc. — without sprinkling\n * magic numbers into editor code.\n * 2. Future schema migration — when a new required field is added, all\n * construction goes through here and the migration is one diff.\n */\n\nconst A4_WIDTH_TWIPS = 11906; // 210 mm\nconst A4_HEIGHT_TWIPS = 16838; // 297 mm\nconst ONE_INCH_TWIPS = 1440;\n\n/** A new, blank document with an A4 portrait section and the standard styles. */\nexport function emptyDocument(): SobreeDocument {\n return {\n body: [paragraph()],\n sections: [defaultSection()],\n headerFooterBodies: {},\n styles: defaultStyles(),\n numbering: [],\n rawParts: {},\n fonts: [],\n };\n}\n\nexport function defaultSection(): SectionProperties {\n return {\n pageSize: defaultPageSize(),\n pageMargins: defaultMargins(),\n headerRefs: [],\n footerRefs: [],\n };\n}\n\nexport function defaultPageSize(): PageSize {\n return { wTwips: A4_WIDTH_TWIPS, hTwips: A4_HEIGHT_TWIPS, orientation: \"portrait\" };\n}\n\nexport function defaultMargins(): PageMargins {\n return {\n topTwips: ONE_INCH_TWIPS,\n rightTwips: ONE_INCH_TWIPS,\n bottomTwips: ONE_INCH_TWIPS,\n leftTwips: ONE_INCH_TWIPS,\n headerTwips: 720,\n footerTwips: 720,\n gutterTwips: 0,\n };\n}\n\n/** A paragraph with no runs and no properties beyond the defaults. */\nexport function paragraph(\n runs: InlineRun[] = [],\n properties: ParagraphProperties = {},\n): Paragraph {\n return { kind: \"paragraph\", properties, runs };\n}\n\n/** Heading paragraph (`Heading{level}` style, level clamped to 1..6). */\nexport function heading(\n level: number,\n runs: InlineRun[] = [],\n extra: ParagraphProperties = {},\n): Paragraph {\n const lv = Math.max(1, Math.min(6, level));\n return paragraph(runs, { ...extra, styleId: `Heading${lv}` });\n}\n\n/** Plain text run with optional formatting. */\nexport function text(value: string, properties: RunProperties = {}): TextRun {\n return { kind: \"text\", text: value, properties };\n}\n\n/** Convenience: emphasised (bold + italic) text run. */\nexport function emphasis(value: string, properties: RunProperties = {}): TextRun {\n return { kind: \"text\", text: value, properties: { ...properties, italic: true } };\n}\n\nexport function strong(value: string, properties: RunProperties = {}): TextRun {\n return { kind: \"text\", text: value, properties: { ...properties, bold: true } };\n}\n\nexport function softBreak(): InlineRun {\n return { kind: \"break\", type: \"line\" };\n}\n\nexport function pageBreak(): InlineRun {\n return { kind: \"break\", type: \"page\" };\n}\n\n/** Default Word styles every doc declares so headings render correctly.\n *\n * Carries WORD-HARDCODED-DEFAULT typography — i.e. what Word uses\n * when a docx has bare/empty styles.xml entries. That means single\n * line spacing (1.0×) and zero space-before / space-after on Normal.\n * Headings keep their bold + size on `runDefaults` but DON'T add\n * spacing-before/after either — the original docx tells the renderer\n * via per-paragraph `spacing` properties when something needs to\n * breathe.\n *\n * This is the load-bearing constraint: a docx round-trip must not\n * silently add (or remove) vertical rhythm the original didn't ask\n * for. Embedders that want a different baseline (e.g. markdown output\n * with 8pt-after for visible paragraph separation) override the\n * Normal style on the document they construct — see `parseMarkdown`.\n *\n * Heading scale steps down from a prominent H1 at 24pt to body-sized\n * H6 at 11pt. */\nexport function defaultStyles(): NamedStyle[] {\n const headingSizes = [24, 20, 16, 14, 12, 11] as const;\n const out: NamedStyle[] = [\n {\n id: \"Normal\",\n type: \"paragraph\",\n displayName: \"Normal\",\n runDefaults: {\n fontFamily: \"Helvetica\",\n fontSizePt: 11,\n },\n // Word hardcoded default — single line, zero before/after.\n paragraphDefaults: {\n spacing: { line: 240, lineRule: \"auto\" },\n },\n },\n ];\n for (let i = 1; i <= 6; i++) {\n const size = headingSizes[i - 1] ?? 11;\n out.push({\n id: `Heading${i}`,\n type: \"paragraph\",\n displayName: `heading ${i}`,\n basedOn: \"Normal\",\n nextStyleId: \"Normal\",\n runDefaults: {\n bold: true,\n fontFamily: \"Helvetica\",\n fontSizePt: size,\n },\n // No paragraph-level spacing — inherits from Normal (single\n // line). Documents authored in tools that want breathing\n // room around headings ship explicit `spacing.beforeTwips`\n // / `afterTwips` per heading paragraph.\n });\n }\n out.push({\n id: \"Quote\",\n type: \"paragraph\",\n displayName: \"Quote\",\n basedOn: \"Normal\",\n runDefaults: { italic: true },\n // Quote indent only — no spacing change vs Normal.\n paragraphDefaults: {\n indent: { leftTwips: 567, rightTwips: 567 },\n },\n });\n return out;\n}\n\n/** Push a block onto the document body. Mutates `doc` and returns it. */\nexport function appendBlock(doc: SobreeDocument, block: Block): SobreeDocument {\n doc.body.push(block);\n return doc;\n}\n\n/** Allocate a new header/footer reference id. Pure helper. */\nexport function makeHeaderFooterRef(\n type: \"default\" | \"first\" | \"even\",\n partId: string,\n): HeaderFooterRef {\n return { type, partId };\n}\n\nexport function isParagraph(block: Block): block is Paragraph {\n return block.kind === \"paragraph\";\n}\n\nexport function isTable(block: Block): block is Table {\n return block.kind === \"table\";\n}\n","import type { PageSetup, PageZoneText } from \"../paperStack/pageSetup\";\nimport { text as textRun } from \"./builders\";\nimport type {\n Block,\n FieldRun,\n HeaderFooterRef,\n InlineRun,\n Paragraph,\n SectionProperties,\n TextRun,\n} from \"./types\";\nimport { PAGE_SIZES } from \"../paperStack/pageSetup\";\n\n/**\n * Temporary bridge between Sobree's legacy `PageSetup`/`PageZoneText` model\n * and the native OOXML-flavoured `SectionProperties` + `headerFooterBodies`.\n *\n * Lives in `src/doc/` so both the Sobree façade and the docx import/export\n * modules can import it without creating a layer-violation. Deleted when\n * the editor is cut over to the native AST directly (Phase N4).\n */\n\nconst MM_TO_TWIPS = 56.6929133858; // 1440 / 25.4\n\nexport interface SectionMaterials {\n section: SectionProperties;\n headerFooterBodies: Record<string, Block[]>;\n}\n\n/**\n * Translate `PageSetup` → `SectionProperties` plus the header/footer body\n * blocks. Allocates part ids (`header1.xml`, `footer1.xml`, …) following\n * Word's canonical numbering.\n */\nexport function pageSetupToSection(setup: PageSetup): SectionMaterials {\n const section: SectionProperties = {\n pageSize: pageSizeToTwips(setup),\n pageMargins: marginsToTwips(setup.margins),\n headerRefs: [],\n footerRefs: [],\n };\n if (setup.verticalAlign && setup.verticalAlign !== \"top\") {\n section.vAlign = setup.verticalAlign;\n }\n const bodies: Record<string, Block[]> = {};\n let fileIdx = 1;\n\n const attach = (\n kind: \"header\" | \"footer\",\n type: \"default\" | \"first\",\n template: string,\n ) => {\n const partId = `${kind}${fileIdx}.xml`;\n fileIdx += 1;\n const ref: HeaderFooterRef = { type, partId };\n if (kind === \"header\") section.headerRefs.push(ref);\n else section.footerRefs.push(ref);\n bodies[partId] = templateToBlocks(template);\n };\n\n if (zoneHasContent(setup.header)) {\n attach(\"header\", \"default\", setup.header.default);\n if (setup.header.differentFirst && setup.header.first) {\n attach(\"header\", \"first\", setup.header.first);\n }\n }\n if (zoneHasContent(setup.footer)) {\n attach(\"footer\", \"default\", setup.footer.default);\n if (setup.footer.differentFirst && setup.footer.first) {\n attach(\"footer\", \"first\", setup.footer.first);\n }\n }\n if (setup.header.differentFirst || setup.footer.differentFirst) {\n section.titlePage = true;\n }\n\n return { section, headerFooterBodies: bodies };\n}\n\n/**\n * Translate `SectionProperties` + header/footer bodies → a sparse\n * `Partial<PageSetup>`. Keys are only present if the section actually\n * specified them, so callers can merge cleanly with their current setup.\n */\nexport function sectionToPageSetup(\n section: SectionProperties,\n bodies: Record<string, Block[]>,\n): Partial<PageSetup> {\n const out: Partial<PageSetup> = {};\n\n const { size, orientation } = matchPageSize(section.pageSize.wTwips, section.pageSize.hTwips);\n out.size = size;\n out.orientation = orientation;\n\n out.margins = {\n top: roundMm(section.pageMargins.topTwips),\n right: roundMm(section.pageMargins.rightTwips),\n bottom: roundMm(section.pageMargins.bottomTwips),\n left: roundMm(section.pageMargins.leftTwips),\n };\n\n const header = refsToZoneText(section.headerRefs, bodies, section.titlePage === true);\n if (header) out.header = header;\n const footer = refsToZoneText(section.footerRefs, bodies, section.titlePage === true);\n if (footer) out.footer = footer;\n\n if (section.vAlign) out.verticalAlign = section.vAlign;\n\n return out;\n}\n\nfunction refsToZoneText(\n refs: HeaderFooterRef[],\n bodies: Record<string, Block[]>,\n differentFirst: boolean,\n): PageZoneText | null {\n if (refs.length === 0) return null;\n const zone: PageZoneText = {\n default: \"\",\n first: \"\",\n last: \"\",\n differentFirst,\n differentLast: false,\n };\n for (const ref of refs) {\n if (ref.type === \"even\") continue;\n const body = bodies[ref.partId];\n if (!body) continue;\n const text = blocksToTemplate(body);\n if (ref.type === \"first\") zone.first = text;\n else zone.default = text;\n }\n return zone.default || zone.first ? zone : null;\n}\n\nfunction pageSizeToTwips(setup: PageSetup) {\n const size = PAGE_SIZES[setup.size];\n const [widthMm, heightMm] =\n setup.orientation === \"portrait\" ? [size.width, size.height] : [size.height, size.width];\n return {\n wTwips: Math.round(widthMm * MM_TO_TWIPS),\n hTwips: Math.round(heightMm * MM_TO_TWIPS),\n orientation: setup.orientation,\n };\n}\n\nfunction marginsToTwips(m: PageSetup[\"margins\"]) {\n return {\n topTwips: Math.round(m.top * MM_TO_TWIPS),\n rightTwips: Math.round(m.right * MM_TO_TWIPS),\n bottomTwips: Math.round(m.bottom * MM_TO_TWIPS),\n leftTwips: Math.round(m.left * MM_TO_TWIPS),\n headerTwips: 720,\n footerTwips: 720,\n gutterTwips: 0,\n };\n}\n\nfunction matchPageSize(widthTwips: number, heightTwips: number) {\n const widthMm = widthTwips / MM_TO_TWIPS;\n const heightMm = heightTwips / MM_TO_TWIPS;\n const portrait = heightMm >= widthMm;\n const [w, h] = portrait ? [widthMm, heightMm] : [heightMm, widthMm];\n\n let best: keyof typeof PAGE_SIZES = \"A4\";\n let bestDist = Number.POSITIVE_INFINITY;\n for (const [key, mm] of Object.entries(PAGE_SIZES)) {\n const d = Math.abs(mm.width - w) + Math.abs(mm.height - h);\n if (d < bestDist) {\n bestDist = d;\n best = key as keyof typeof PAGE_SIZES;\n }\n }\n return { size: best, orientation: portrait ? (\"portrait\" as const) : (\"landscape\" as const) };\n}\n\nfunction zoneHasContent(z: PageZoneText): boolean {\n return z.default.length > 0 || (z.differentFirst && z.first.length > 0);\n}\n\nfunction roundMm(twips: number): number {\n return Math.round(twips / MM_TO_TWIPS);\n}\n\n/**\n * Parse a header/footer template (plain text with `{page}` / `{pages}`\n * tokens) into Block[] — one paragraph per line. Field tokens become\n * FieldRun nodes so Word's PAGE/NUMPAGES field codes emit naturally.\n */\nexport function templateToBlocks(template: string): Block[] {\n const lines = template.split(/\\r?\\n/);\n return lines.map((line) => lineToParagraph(line));\n}\n\nfunction lineToParagraph(line: string): Paragraph {\n const runs: InlineRun[] = [];\n const regex = /\\{(page|pages)\\}/g;\n let last = 0;\n let m: RegExpExecArray | null = regex.exec(line);\n while (m !== null) {\n if (m.index > last) runs.push(textRun(line.slice(last, m.index)));\n const field: FieldRun = {\n kind: \"field\",\n instruction: m[1] === \"page\" ? \"PAGE\" : \"NUMPAGES\",\n cached: \"\",\n };\n runs.push(field);\n last = m.index + m[0].length;\n m = regex.exec(line);\n }\n if (last < line.length) runs.push(textRun(line.slice(last)));\n return { kind: \"paragraph\", properties: {}, runs };\n}\n\n/** Reverse of `templateToBlocks`. Used on import. */\nexport function blocksToTemplate(blocks: readonly Block[]): string {\n const lines: string[] = [];\n for (const block of blocks) {\n if (block.kind !== \"paragraph\") continue;\n let line = \"\";\n for (const run of block.runs) {\n if (run.kind === \"text\") line += (run as TextRun).text;\n else if (run.kind === \"field\") {\n if (run.instruction.trim().toUpperCase() === \"PAGE\") line += \"{page}\";\n else if (run.instruction.trim().toUpperCase() === \"NUMPAGES\") line += \"{pages}\";\n } else if (run.kind === \"break\") line += \"\\n\";\n }\n lines.push(line);\n }\n // Collapse leading/trailing empty lines but preserve interior blanks.\n while (lines.length > 0 && lines[lines.length - 1] === \"\") lines.pop();\n while (lines.length > 0 && lines[0] === \"\") lines.shift();\n return lines.join(\"\\n\");\n}\n","import type { SobreeDocument } from \"../../doc/types\";\n\n/**\n * Mutable per-export bookkeeping: tracks which image `partPath`s have\n * been allocated rIds, which new parts to add to the ZIP, and which\n * content-type overrides the package manifest needs to declare.\n *\n * Constructed once in `exportDocx`, threaded through document + runs\n * emission, then folded into the final relationships / manifest.\n */\nexport interface ExportContext {\n /** Next rId to hand out via `allocRel`. Mutated as rIds are allocated. */\n nextRid: number;\n /** Rels to append to `word/_rels/document.xml.rels`. */\n relationships: Array<{\n id: string;\n type: \"header\" | \"footer\" | \"image\" | \"hyperlink\" | \"fontTable\";\n target: string;\n /** External targets (URLs) need `TargetMode=\"External\"`. */\n external?: boolean;\n }>;\n /** New ZIP parts to include in the output package (e.g. `word/media/image1.png`). */\n parts: Record<string, Uint8Array | string>;\n /** Content-type overrides to declare in `[Content_Types].xml`. */\n contentTypeOverrides: Array<{ partName: string; contentType: string }>;\n /** Media extensions seen (for content-type Default entries). */\n mediaExtensions: Set<string>;\n\n /** Cached path → rId so repeated DrawingRuns share one relationship. */\n imageRelByPartPath: Map<string, string>;\n /** Cached href → rId so repeated hyperlinks share one relationship. */\n hyperlinkRelByHref: Map<string, string>;\n /** Running docPr id counter — Word wants unique per-drawing ids. */\n nextDocPrId: number;\n /**\n * Running revision id counter — Word requires `w:id=\"N\"` on each\n * `<w:ins>` / `<w:del>` / `<w:rPrChange>` / paragraph-mark revision\n * element, unique within the document. We share one counter across\n * all revision kinds to keep the IDs simple and contiguous.\n */\n nextRevisionId: number;\n}\n\nexport function makeExportContext(startRid: number): ExportContext {\n return {\n nextRid: startRid,\n relationships: [],\n parts: {},\n contentTypeOverrides: [],\n mediaExtensions: new Set(),\n imageRelByPartPath: new Map(),\n hyperlinkRelByHref: new Map(),\n nextDocPrId: 1,\n nextRevisionId: 1,\n };\n}\n\n/** Allocate the next w:id for a tracked-revision element. */\nexport function nextRevisionId(ctx: ExportContext): number {\n const n = ctx.nextRevisionId;\n ctx.nextRevisionId += 1;\n return n;\n}\n\n/**\n * Ensure an image relationship exists for the given `partPath`. Copies the\n * bytes into `ctx.parts` on first encounter and returns the allocated rId.\n */\nexport function allocImageRel(\n ctx: ExportContext,\n partPath: string,\n doc: SobreeDocument,\n): string | null {\n const cached = ctx.imageRelByPartPath.get(partPath);\n if (cached) return cached;\n\n const bytes = doc.rawParts[partPath];\n if (!bytes) return null;\n\n const id = `rId${ctx.nextRid++}`;\n ctx.imageRelByPartPath.set(partPath, id);\n ctx.parts[partPath] = bytes;\n ctx.relationships.push({\n id,\n type: \"image\",\n target: toWordRelativePath(partPath),\n });\n const ext = partPath.split(\".\").pop()?.toLowerCase() ?? \"\";\n if (ext) ctx.mediaExtensions.add(ext);\n return id;\n}\n\nfunction toWordRelativePath(partPath: string): string {\n // Relationships in word/_rels/document.xml.rels are relative to `word/`,\n // so strip that prefix when present.\n if (partPath.startsWith(\"word/\")) return partPath.slice(\"word/\".length);\n return partPath;\n}\n\n/** Next unique `docPr id` for a drawing. */\nexport function nextDocPr(ctx: ExportContext): number {\n return ctx.nextDocPrId++;\n}\n\n/**\n * Ensure a hyperlink relationship exists for the given external `href`.\n * Hyperlinks are external-target rels (TargetMode=\"External\"), so the\n * URL itself is the rel's `Target` and no part is added to the ZIP.\n */\nexport function allocHyperlinkRel(ctx: ExportContext, href: string): string {\n const cached = ctx.hyperlinkRelByHref.get(href);\n if (cached) return cached;\n const id = `rId${ctx.nextRid++}`;\n ctx.hyperlinkRelByHref.set(href, id);\n ctx.relationships.push({ id, type: \"hyperlink\", target: href, external: true });\n return id;\n}\n","/**\n * OOXML namespace URIs. The spec uses a rich set of prefixes\n * (`w:`, `r:`, `wp:` …); we keep them as constants so both the\n * importer and exporter reference the same strings. Typos here\n * are silent parse failures, so: one source of truth.\n */\n\nexport const NS = {\n /** WordprocessingML — the main body namespace. */\n w: \"http://schemas.openxmlformats.org/wordprocessingml/2006/main\",\n /** Relationships (images, headers, hyperlinks). */\n r: \"http://schemas.openxmlformats.org/officeDocument/2006/relationships\",\n /** DrawingML — image anchors and inline drawings. */\n wp: \"http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing\",\n /** DrawingML core. */\n a: \"http://schemas.openxmlformats.org/drawingml/2006/main\",\n /** Picture (DrawingML). */\n pic: \"http://schemas.openxmlformats.org/drawingml/2006/picture\",\n /** Word-processing shapes (textboxes, geometric primitives). */\n wps: \"http://schemas.microsoft.com/office/word/2010/wordprocessingShape\",\n /** Word-processing groups (containers for shapes / pictures). */\n wpg: \"http://schemas.microsoft.com/office/word/2010/wordprocessingGroup\",\n /** VML — the legacy `<v:shape>` / `<w:pict>` path Word still emits\n * for OLE-embedded images and some compatibility shapes. */\n v: \"urn:schemas-microsoft-com:vml\",\n /** Relationships package part. */\n rel: \"http://schemas.openxmlformats.org/package/2006/relationships\",\n /** Content types. */\n ct: \"http://schemas.openxmlformats.org/package/2006/content-types\",\n} as const;\n\n/** Minimal attr mapping we emit on `<w:document>` (only the namespaces we use). */\nexport const ROOT_DOCUMENT_ATTRS: Record<string, string> = {\n \"xmlns:w\": NS.w,\n \"xmlns:r\": NS.r,\n \"xmlns:wp\": NS.wp,\n \"xmlns:a\": NS.a,\n \"xmlns:pic\": NS.pic,\n};\n","import { NS } from \"./namespaces\";\n\n/**\n * Thin wrappers around the browser's `DOMParser` and `XMLSerializer`.\n * Pure functions; keep anything stateful (caches, registries) out of this\n * file. One helper per concern so call sites stay readable.\n */\n\nexport function parseXml(src: string): Document {\n const parser = new DOMParser();\n const doc = parser.parseFromString(src, \"application/xml\");\n const err = doc.getElementsByTagName(\"parsererror\")[0];\n if (err) throw new Error(`XML parse failed: ${err.textContent?.slice(0, 200) ?? \"\"}`);\n return doc;\n}\n\nexport function serializeXml(node: Node): string {\n return new XMLSerializer().serializeToString(node);\n}\n\n/** Get the first descendant element in the WordprocessingML namespace. */\nexport function wFirst(root: Document | Element, localName: string): Element | null {\n return root.getElementsByTagNameNS(NS.w, localName)[0] ?? null;\n}\n\n/** Get all descendants in the WordprocessingML namespace. */\nexport function wAll(root: Document | Element, localName: string): Element[] {\n return Array.from(root.getElementsByTagNameNS(NS.w, localName));\n}\n\n/** Get direct-child elements in the WordprocessingML namespace. */\nexport function wChildren(parent: Element, localName: string): Element[] {\n const out: Element[] = [];\n for (const child of Array.from(parent.children)) {\n if (child.namespaceURI === NS.w && child.localName === localName) out.push(child);\n }\n return out;\n}\n\n/**\n * Read a `w:val` attribute, Word's standard way of carrying a single value.\n * Accepts either namespaced or non-namespaced attribute lookup since\n * serialised documents differ.\n */\nexport function wVal(el: Element | null): string | null {\n if (!el) return null;\n return el.getAttributeNS(NS.w, \"val\") ?? el.getAttribute(\"w:val\");\n}\n\n/** Build an XML declaration header + root element. Used by the exporter. */\nexport function xmlDocument(rootXml: string): string {\n return `<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\\n${rootXml}`;\n}\n\n/**\n * Emit a single element as a string. Attributes are rendered in insertion\n * order; children are pre-serialized strings. Prefers a tiny, composable\n * string builder over a virtual DOM — the OOXML shapes we emit are flat\n * enough that this is clearer than juggling `document.createElementNS`.\n */\nexport function el(\n tag: string,\n attrs: Record<string, string | number | undefined> | null = null,\n children: string[] | string | null = null,\n): string {\n const a = attrs\n ? Object.entries(attrs)\n .filter(([, v]) => v !== undefined)\n .map(([k, v]) => ` ${k}=\"${escapeAttr(String(v))}\"`)\n .join(\"\")\n : \"\";\n if (children === null || (Array.isArray(children) && children.length === 0)) {\n return `<${tag}${a}/>`;\n }\n const body = Array.isArray(children) ? children.join(\"\") : children;\n return `<${tag}${a}>${body}</${tag}>`;\n}\n\nexport function escapeXmlText(s: string): string {\n return s\n .replace(/&/g, \"&amp;\")\n .replace(/</g, \"&lt;\")\n .replace(/>/g, \"&gt;\");\n}\n\nfunction escapeAttr(s: string): string {\n return s.replace(/&/g, \"&amp;\").replace(/</g, \"&lt;\").replace(/\"/g, \"&quot;\");\n}\n","import { NS } from \"../shared/namespaces\";\nimport { el, xmlDocument } from \"../shared/xml\";\n\nconst REL_TYPES = {\n header: \"http://schemas.openxmlformats.org/officeDocument/2006/relationships/header\",\n footer: \"http://schemas.openxmlformats.org/officeDocument/2006/relationships/footer\",\n image: \"http://schemas.openxmlformats.org/officeDocument/2006/relationships/image\",\n hyperlink: \"http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink\",\n fontTable:\n \"http://schemas.openxmlformats.org/officeDocument/2006/relationships/fontTable\",\n font: \"http://schemas.openxmlformats.org/officeDocument/2006/relationships/font\",\n} as const;\n\ntype RelKind = keyof typeof REL_TYPES;\n\n/**\n * `[Content_Types].xml` tells Office which content-type handler to use for\n * each part. `overrides` are appended to the baseline; `imageExtensions`\n * become `<Default>` content-type entries so embedded media round-trips.\n */\nexport function renderContentTypesXml(\n overrides: Array<{ partName: string; contentType: string }> = [],\n imageExtensions: readonly string[] = [],\n): string {\n const base = [\n el(\"Default\", {\n Extension: \"rels\",\n ContentType: \"application/vnd.openxmlformats-package.relationships+xml\",\n }),\n el(\"Default\", { Extension: \"xml\", ContentType: \"application/xml\" }),\n ];\n\n const seen = new Set<string>();\n for (const ext of imageExtensions) {\n if (seen.has(ext)) continue;\n seen.add(ext);\n base.push(\n el(\"Default\", {\n Extension: ext,\n ContentType: imageMimeFromExtension(ext),\n }),\n );\n }\n\n base.push(\n el(\"Override\", {\n PartName: \"/word/document.xml\",\n ContentType:\n \"application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml\",\n }),\n el(\"Override\", {\n PartName: \"/word/styles.xml\",\n ContentType:\n \"application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml\",\n }),\n );\n\n const extra = overrides.map((o) =>\n el(\"Override\", { PartName: o.partName, ContentType: o.contentType }),\n );\n return xmlDocument(el(\"Types\", { xmlns: NS.ct }, [...base, ...extra]));\n}\n\n/**\n * `_rels/.rels` — the package-level relationships, pointing at the main\n * document part.\n */\nexport function renderRootRelsXml(): string {\n const children = [\n el(\"Relationship\", {\n Id: \"rId1\",\n Type: \"http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument\",\n Target: \"word/document.xml\",\n }),\n ];\n return xmlDocument(el(\"Relationships\", { xmlns: NS.rel }, children));\n}\n\n/**\n * `word/_rels/document.xml.rels` — relationships originating from\n * `document.xml`. Always includes the styles relationship (`rId1`);\n * callers pass additional header/footer/image relationships to append.\n */\nexport function renderDocumentRelsXml(\n extras: Array<{ id: string; type: RelKind; target: string; external?: boolean }> = [],\n): string {\n const stylesRel = el(\"Relationship\", {\n Id: \"rId1\",\n Type: \"http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles\",\n Target: \"styles.xml\",\n });\n const extraRels = extras.map((e) => {\n const attrs: Record<string, string | undefined> = {\n Id: e.id,\n Type: REL_TYPES[e.type],\n Target: e.target,\n };\n if (e.external) attrs.TargetMode = \"External\";\n return el(\"Relationship\", attrs);\n });\n return xmlDocument(el(\"Relationships\", { xmlns: NS.rel }, [stylesRel, ...extraRels]));\n}\n\nfunction imageMimeFromExtension(ext: string): string {\n const lower = ext.toLowerCase();\n if (lower === \"png\") return \"image/png\";\n if (lower === \"jpg\" || lower === \"jpeg\") return \"image/jpeg\";\n if (lower === \"gif\") return \"image/gif\";\n if (lower === \"webp\") return \"image/webp\";\n if (lower === \"svg\") return \"image/svg+xml\";\n if (lower === \"bmp\") return \"image/bmp\";\n // OOXML embedded fonts — obfuscated TrueType/OpenType.\n if (lower === \"odttf\")\n return \"application/vnd.openxmlformats-officedocument.obfuscatedFont\";\n // Bare TTF/OTF (rare for embedded — Word always obfuscates).\n if (lower === \"ttf\") return \"application/x-font-ttf\";\n if (lower === \"otf\") return \"application/x-font-otf\";\n return \"application/octet-stream\";\n}\n","import { NS } from \"../shared/namespaces\";\nimport { el, escapeXmlText } from \"../shared/xml\";\nimport type { DrawingRun } from \"../../doc/types\";\n\n/**\n * Emit a `<w:drawing>` XML fragment for an inline image. Consumes an\n * `rId` allocated elsewhere (via `ExportContext.allocImageRel`) and\n * writes the OOXML shape Word expects for a single inline picture.\n *\n * Anchored / floating drawings are out of scope for Phase 5 — all images\n * render inline.\n */\nexport function renderDrawing(run: DrawingRun, rId: string, docPrId: number): string {\n const cx = run.widthEmu > 0 ? run.widthEmu : 914400; // default 1\"\n const cy = run.heightEmu > 0 ? run.heightEmu : 914400;\n const name = `Picture ${docPrId}`;\n const descr = run.altText ?? \"\";\n\n const blip = el(\"a:blip\", { \"r:embed\": rId });\n const blipFill = el(\n \"pic:blipFill\",\n null,\n `${blip}${el(\"a:stretch\", null, el(\"a:fillRect\"))}`,\n );\n const nvPicPr = el(\n \"pic:nvPicPr\",\n null,\n `${el(\"pic:cNvPr\", { id: docPrId, name, descr: escapeXmlText(descr) })}${el(\"pic:cNvPicPr\")}`,\n );\n const spPr = el(\n \"pic:spPr\",\n null,\n `${el(\n \"a:xfrm\",\n null,\n `${el(\"a:off\", { x: 0, y: 0 })}${el(\"a:ext\", { cx, cy })}`,\n )}${el(\n \"a:prstGeom\",\n { prst: \"rect\" },\n el(\"a:avLst\"),\n )}`,\n );\n const pic = el(\n \"pic:pic\",\n { \"xmlns:pic\": NS.pic },\n `${nvPicPr}${blipFill}${spPr}`,\n );\n const graphicData = el(\n \"a:graphicData\",\n { uri: NS.pic },\n pic,\n );\n const graphic = el(\"a:graphic\", { \"xmlns:a\": NS.a }, graphicData);\n const extent = el(\"wp:extent\", { cx, cy });\n const docPr = el(\"wp:docPr\", {\n id: docPrId,\n name,\n descr: escapeXmlText(descr),\n });\n const inline = el(\n \"wp:inline\",\n {\n distT: 0,\n distB: 0,\n distL: 0,\n distR: 0,\n },\n `${extent}${docPr}${graphic}`,\n );\n return el(\n \"w:r\",\n null,\n el(\"w:drawing\", { \"xmlns:wp\": NS.wp }, inline),\n );\n}\n","/**\n * OOXML unit conversions. Word mixes several units throughout the spec:\n * - **Half-points** (`w:sz`, `w:szCs`) — divide by 2 to get pt.\n * - **Twentieths of a point** aka \"twips\" (`w:spacing`, page margins in\n * `w:pgMar`) — divide by 20 to get pt, by 567 to get mm.\n * - **EMU** (English Metric Units, `wp:extent`) — 914400 per inch,\n * 360000 per cm.\n * - **Half-points via `w:line` for line spacing** — when `w:lineRule=\"auto\"`,\n * the value is twentieths-of-a-point multiplied by a factor: 240 twips\n * = single-spacing.\n *\n * One file, one place to get them wrong or right.\n */\n\nconst PT_PER_INCH = 72;\nconst EMU_PER_INCH = 914400;\nconst MM_PER_INCH = 25.4;\nconst TWIPS_PER_INCH = 1440;\n\n/** Half-point integer → floating-point pt. */\nexport const halfPtToPt = (n: number): number => n / 2;\n/** Floating-point pt → rounded half-point integer. */\nexport const ptToHalfPt = (pt: number): number => Math.round(pt * 2);\n\n/** EMU → CSS px (assuming 96 DPI). */\nexport const emuToPx = (emu: number): number => (emu / EMU_PER_INCH) * 96;\n/** CSS px → EMU. */\nexport const pxToEmu = (px: number): number => Math.round((px / 96) * EMU_PER_INCH);\n\n/** Twips → mm. */\nexport const twipsToMm = (t: number): number => (t / TWIPS_PER_INCH) * MM_PER_INCH;\n/** mm → twips. */\nexport const mmToTwips = (mm: number): number => Math.round((mm / MM_PER_INCH) * TWIPS_PER_INCH);\n\n/** Twips → pt. */\nexport const twipsToPt = (t: number): number => (t / TWIPS_PER_INCH) * PT_PER_INCH;\n/** pt → twips. */\nexport const ptToTwips = (pt: number): number => Math.round((pt / PT_PER_INCH) * TWIPS_PER_INCH);\n\n/**\n * `w:line` for line spacing in \"auto\" mode: 240 twips = single-spacing.\n * So `line-height: 1.5` → `240 * 1.5 = 360`.\n */\nexport const lineHeightToOoxml = (lineHeight: number): number => Math.round(240 * lineHeight);\nexport const ooxmlLineHeightToCss = (line: number): number => line / 240;\n","import {\n type ExportContext,\n allocHyperlinkRel,\n allocImageRel,\n nextDocPr,\n nextRevisionId,\n} from \"./context\";\nimport { renderDrawing } from \"./drawings\";\nimport { ptToHalfPt } from \"../shared/units\";\nimport { el, escapeXmlText } from \"../shared/xml\";\nimport type {\n DrawingRun,\n HyperlinkRun,\n InlineRun,\n RevisionMark,\n RunProperties,\n SobreeDocument,\n} from \"../../doc/types\";\n\n/**\n * Render a list of InlineRuns into concatenated `<w:r>` / `<w:fldSimple>`\n * / `<w:drawing>` XML. Drawings use `ctx` to allocate a relationship id\n * and register the underlying media part.\n */\nexport function inlinesToRuns(\n inlines: readonly InlineRun[],\n ctx: ExportContext,\n doc: SobreeDocument,\n): string {\n // Group consecutive runs that share the same tracked-revision marker\n // (same type + same author) so we wrap them in ONE `<w:ins>` /\n // `<w:del>` element — Word renders them as a single revision span,\n // which matches the AST's coalescing semantics in `getRevisions`.\n // Runs with no revision pass through bare.\n const parts: string[] = [];\n let i = 0;\n while (i < inlines.length) {\n const run = inlines[i]!;\n const rev = revisionOf(run);\n if (!rev) {\n parts.push(emitInline(run, ctx, doc));\n i += 1;\n continue;\n }\n // Extend the group while next run carries the *same* revision.\n let j = i + 1;\n while (j < inlines.length) {\n const r2 = revisionOf(inlines[j]!);\n if (!r2 || r2.type !== rev.type || r2.author !== rev.author || r2.date !== rev.date) {\n break;\n }\n j += 1;\n }\n const inner = inlines\n .slice(i, j)\n .map((r) => emitInline(r, ctx, doc, rev.type === \"del\"))\n .join(\"\");\n parts.push(wrapRevision(rev, inner, ctx));\n i = j;\n }\n return parts.join(\"\");\n}\n\n/** Pull the revision marker off a run, if any. Only text runs in v1. */\nfunction revisionOf(run: InlineRun): RevisionMark | undefined {\n if (run.kind !== \"text\") return undefined;\n return run.properties.revision;\n}\n\n/**\n * Wrap `inner` (already-emitted `<w:r>` xml) in a `<w:ins>` or `<w:del>`\n * element carrying the revision id + author + date attributes Word\n * expects. The marker is removed from `<w:rPr>` of the inner run by\n * `propsToRPr` so it doesn't get serialised twice.\n */\nfunction wrapRevision(rev: RevisionMark, inner: string, ctx: ExportContext): string {\n const tag = rev.type === \"ins\" ? \"w:ins\" : \"w:del\";\n const attrs: Record<string, string | number> = {\n \"w:id\": nextRevisionId(ctx),\n };\n if (rev.author !== undefined) attrs[\"w:author\"] = rev.author;\n if (rev.date !== undefined) attrs[\"w:date\"] = rev.date;\n return el(tag, attrs, inner);\n}\n\nfunction emitInline(\n run: InlineRun,\n ctx: ExportContext,\n doc: SobreeDocument,\n insideDel = false,\n): string {\n switch (run.kind) {\n case \"text\":\n return emitTextRun(run.text, run.properties, ctx, insideDel);\n case \"break\":\n return emitBreak(run.type);\n case \"tab\":\n return emitRun([el(\"w:tab\")], run.properties, ctx);\n case \"field\":\n return emitField(run.instruction, run.cached ?? \"\", run.properties, ctx);\n case \"hyperlink\":\n return emitHyperlink(run, ctx, doc);\n case \"drawing\":\n return emitDrawing(run, ctx, doc);\n default:\n return \"\";\n }\n}\n\nfunction emitHyperlink(link: HyperlinkRun, ctx: ExportContext, doc: SobreeDocument): string {\n const innerRuns = inlinesToRuns(link.children, ctx, doc);\n if (!link.href) return innerRuns;\n const rId = allocHyperlinkRel(ctx, link.href);\n return el(\"w:hyperlink\", { \"r:id\": rId, \"w:history\": 1 }, innerRuns);\n}\n\nfunction emitDrawing(run: DrawingRun, ctx: ExportContext, doc: SobreeDocument): string {\n const rId = allocImageRel(ctx, run.partPath, doc);\n if (!rId) {\n // Media bytes missing — fall back to alt text so the doc still opens.\n return run.altText ? emitTextRun(run.altText, {}, ctx) : \"\";\n }\n return renderDrawing(run, rId, nextDocPr(ctx));\n}\n\nfunction emitTextRun(\n text: string,\n props: RunProperties,\n ctx: ExportContext,\n insideDel = false,\n): string {\n // Inside a `<w:del>` wrapper the run's text element is `<w:delText>`,\n // per ECMA-376 §17.4.13: deleted text is serialised differently so\n // viewers that strip revisions can quietly drop it.\n const tag = insideDel ? \"w:delText\" : \"w:t\";\n const body = el(tag, { \"xml:space\": \"preserve\" }, escapeXmlText(text));\n return emitRun([body], props, ctx);\n}\n\nfunction emitBreak(type: \"line\" | \"page\" | \"column\"): string {\n const attrs =\n type === \"line\" ? undefined : { \"w:type\": type === \"page\" ? \"page\" : \"column\" };\n // No props on bare break runs, no ctx needed.\n return el(\"w:r\", null, el(\"w:br\", attrs ?? null));\n}\n\nfunction emitField(\n instr: string,\n cached: string,\n props: RunProperties | undefined,\n ctx: ExportContext,\n): string {\n const padded = ` ${instr.trim()} `;\n return el(\n \"w:fldSimple\",\n { \"w:instr\": padded },\n el(\n \"w:r\",\n null,\n [\n propsToRPr(props ?? {}, ctx),\n el(\"w:t\", { \"xml:space\": \"preserve\" }, escapeXmlText(cached)),\n ].join(\"\"),\n ),\n );\n}\n\nfunction emitRun(\n bodyElements: string[],\n props: RunProperties | undefined,\n ctx: ExportContext,\n): string {\n const rPr = propsToRPr(props ?? {}, ctx);\n return el(\"w:r\", null, `${rPr}${bodyElements.join(\"\")}`);\n}\n\nfunction propsToRPr(props: RunProperties, ctx?: ExportContext): string {\n const parts = rprChildElements(props);\n // `<w:rPrChange>` — snapshot of the run's pre-tracking properties.\n // Lives inside `<w:rPr>` per ECMA-376 §17.13.5.32. The snapshot\n // re-uses the same property-emitter, so a tracked bold-then-italic\n // round-trips exactly.\n if (props.revisionFormat && ctx) {\n const rf = props.revisionFormat;\n const attrs: Record<string, string | number> = {\n \"w:id\": nextRevisionId(ctx),\n };\n if (rf.author !== undefined) attrs[\"w:author\"] = rf.author;\n if (rf.date !== undefined) attrs[\"w:date\"] = rf.date;\n // The snapshot's `before` itself shouldn't carry `revisionFormat`\n // (recursion-free by `RunProperties.revisionFormat` contract), so\n // we render its rPr children directly.\n const beforeChildren = rprChildElements(rf.before);\n const innerRPr =\n beforeChildren.length > 0 ? el(\"w:rPr\", null, beforeChildren) : el(\"w:rPr\");\n parts.push(el(\"w:rPrChange\", attrs, innerRPr));\n }\n return parts.length > 0 ? el(\"w:rPr\", null, parts) : \"\";\n}\n\n/**\n * Render just the child elements of a `<w:rPr>` — the bold/italic/etc.\n * marker elements — without the outer wrapper. Used by `propsToRPr`\n * for the current run AND for the `<w:rPrChange>` snapshot's inner\n * `<w:rPr>`. Keeps the property-emit logic in one place.\n */\nfunction rprChildElements(props: RunProperties): string[] {\n const parts: string[] = [];\n if (props.styleId) parts.push(el(\"w:rStyle\", { \"w:val\": props.styleId }));\n if (props.bold) parts.push(el(\"w:b\"));\n if (props.italic) parts.push(el(\"w:i\"));\n if (props.strike) parts.push(el(\"w:strike\"));\n if (props.doubleStrike) parts.push(el(\"w:dstrike\"));\n if (props.underline && props.underline !== \"none\") {\n parts.push(el(\"w:u\", { \"w:val\": props.underline }));\n }\n if (props.color) {\n parts.push(el(\"w:color\", { \"w:val\": stripHash(props.color) }));\n }\n if (props.highlight) {\n parts.push(el(\"w:highlight\", { \"w:val\": cssHighlightToWord(props.highlight) }));\n }\n if (props.fontFamily) {\n parts.push(\n el(\"w:rFonts\", {\n \"w:ascii\": props.fontFamily,\n \"w:hAnsi\": props.fontFamily,\n \"w:cs\": props.fontFamily,\n }),\n );\n }\n if (props.fontSizePt) {\n const hp = ptToHalfPt(props.fontSizePt);\n parts.push(el(\"w:sz\", { \"w:val\": hp }));\n parts.push(el(\"w:szCs\", { \"w:val\": hp }));\n }\n if (props.verticalAlign) {\n parts.push(el(\"w:vertAlign\", { \"w:val\": props.verticalAlign }));\n }\n if (props.caps) parts.push(el(\"w:caps\"));\n if (props.smallCaps) parts.push(el(\"w:smallCaps\"));\n if (props.hidden) parts.push(el(\"w:vanish\"));\n return parts;\n}\n\nfunction stripHash(s: string): string {\n return s.replace(/^#/, \"\");\n}\n\nfunction cssHighlightToWord(css: string): string {\n const v = css.trim().toLowerCase();\n const hex = v.startsWith(\"#\") ? v : null;\n const knownByHex: Record<string, string> = {\n \"#ffff00\": \"yellow\",\n \"#00ff00\": \"green\",\n \"#00ffff\": \"cyan\",\n \"#ff00ff\": \"magenta\",\n \"#0000ff\": \"blue\",\n \"#ff0000\": \"red\",\n \"#fff3a1\": \"yellow\",\n };\n if (hex && knownByHex[hex]) return knownByHex[hex];\n if ([\"yellow\", \"green\", \"cyan\", \"magenta\", \"blue\", \"red\"].includes(v)) return v;\n return \"yellow\";\n}\n","import { type ExportContext, nextRevisionId } from \"./context\";\nimport { inlinesToRuns } from \"./runs\";\nimport { ROOT_DOCUMENT_ATTRS } from \"../shared/namespaces\";\nimport { el, xmlDocument } from \"../shared/xml\";\nimport type {\n Block,\n Paragraph,\n ParagraphProperties,\n SobreeDocument,\n Table,\n TableCell,\n TableRow,\n} from \"../../doc/types\";\n\n/**\n * Render the SobreeDocument body into `word/document.xml` (string form).\n *\n * `sectPrXmls` is the parallel array from `emitHeadersAndFooters` —\n * one per section. Non-final sections' sectPr is spliced into the\n * `<w:pPr>` of the LAST PARAGRAPH of that section's body range (OOXML\n * convention; ECMA-376 §17.6.18). The final section's sectPr lands at\n * body level after the last block. `SectionBreak` blocks themselves\n * produce no output — they're delimiters whose semantics are carried\n * by the spliced sectPr.\n *\n * `ctx` is mutated as drawings are encountered — each image registers\n * a relationship and a ZIP media part.\n */\nexport function renderDocumentXml(\n doc: SobreeDocument,\n sectPrXmls: readonly string[],\n ctx: ExportContext,\n): string {\n // Compute which body paragraph each non-final section's sectPr\n // attaches to. Section i (i < N-1) ends at the i-th SectionBreak;\n // the last paragraph of section i sits immediately before that break.\n const trailingSectPr = computeTrailingSectPr(doc.body, sectPrXmls);\n const finalSectPrXml = sectPrXmls[sectPrXmls.length - 1] ?? \"\";\n\n const bodyChildren: string[] = [];\n for (let i = 0; i < doc.body.length; i++) {\n const block = doc.body[i];\n if (!block) continue;\n if (block.kind === \"section_break\") {\n // No own output — its sectPr was attached to the previous paragraph.\n continue;\n }\n if (block.kind === \"paragraph\") {\n const trailing = trailingSectPr.get(i);\n bodyChildren.push(renderParagraph(block, ctx, doc, trailing));\n } else {\n bodyChildren.push(...renderBlock(block, ctx, doc));\n }\n }\n bodyChildren.push(finalSectPrXml);\n const body = el(\"w:body\", null, bodyChildren);\n return xmlDocument(el(\"w:document\", ROOT_DOCUMENT_ATTRS, body));\n}\n\n/**\n * Build a `bodyIndex → sectPrXml` map for paragraphs that need a\n * trailing `<w:sectPr>`. For each non-final section i (with sectPrXmls\n * indexed 0..N-2), the i-th `SectionBreak` in the body marks where\n * section i ends; the paragraph immediately before it gets the sectPr.\n *\n * Edge case: if a section's range is empty (the break is at body[0]\n * or two breaks are adjacent), the sectPr would be orphaned. That\n * shape doesn't appear in well-formed Sobree documents — the editor\n * doesn't allow adjacent breaks — but if it does we silently drop the\n * section's sectPr rather than synthesise an empty paragraph.\n */\nfunction computeTrailingSectPr(\n body: readonly Block[],\n sectPrXmls: readonly string[],\n): Map<number, string> {\n const map = new Map<number, string>();\n let sectionIdx = 0;\n for (let i = 0; i < body.length; i++) {\n if (body[i]?.kind !== \"section_break\") continue;\n if (sectionIdx >= sectPrXmls.length - 1) break;\n // Walk backwards from the break to find the nearest paragraph in\n // this section; that's where the sectPr attaches.\n for (let j = i - 1; j >= 0; j--) {\n const candidate = body[j];\n if (!candidate) continue;\n if (candidate.kind === \"section_break\") break; // empty section, skip\n if (candidate.kind === \"paragraph\") {\n const xml = sectPrXmls[sectionIdx];\n if (xml) map.set(j, xml);\n break;\n }\n // Tables can't host sectPr in their pPr. If the only block in the\n // section is a table, the sectPr is dropped — Word will fall back\n // to the document-final sectPr's settings for that range.\n }\n sectionIdx++;\n }\n return map;\n}\n\n/** Also used for header/footer part bodies. */\nexport function renderBlocks(\n blocks: readonly Block[],\n ctx: ExportContext,\n doc: SobreeDocument,\n): string[] {\n const out: string[] = [];\n for (const block of blocks) out.push(...renderBlock(block, ctx, doc));\n return out;\n}\n\nfunction renderBlock(block: Block, ctx: ExportContext, doc: SobreeDocument): string[] {\n switch (block.kind) {\n case \"paragraph\":\n return [renderParagraph(block, ctx, doc)];\n case \"table\":\n return [renderTable(block, ctx, doc)];\n case \"section_break\":\n return [];\n default:\n return [];\n }\n}\n\n/**\n * Render `<w:p>`. If `trailingSectPr` is provided, splice it into the\n * paragraph's `<w:pPr>` — this is OOXML's \"the section ends here\"\n * convention (the sectPr lives inside the LAST paragraph's pPr of\n * each non-final section).\n */\nfunction renderParagraph(\n p: Paragraph,\n ctx: ExportContext,\n doc: SobreeDocument,\n trailingSectPr?: string,\n): string {\n const pPr = renderPPr(p.properties, ctx, trailingSectPr);\n const runs = inlinesToRuns(p.runs, ctx, doc);\n return el(\"w:p\", null, `${pPr}${runs}`);\n}\n\nfunction renderPPr(\n props: ParagraphProperties,\n ctx: ExportContext,\n trailingSectPr?: string,\n): string {\n const parts: string[] = [];\n if (props.styleId) parts.push(el(\"w:pStyle\", { \"w:val\": props.styleId }));\n if (props.numbering) {\n parts.push(\n el(\n \"w:numPr\",\n null,\n `${el(\"w:ilvl\", { \"w:val\": props.numbering.level })}${el(\"w:numId\", { \"w:val\": props.numbering.numId })}`,\n ),\n );\n }\n if (props.alignment && props.alignment !== \"left\") {\n parts.push(el(\"w:jc\", { \"w:val\": props.alignment }));\n }\n if (props.spacing) {\n const attrs: Record<string, string | number> = {};\n if (props.spacing.beforeTwips !== undefined) attrs[\"w:before\"] = props.spacing.beforeTwips;\n if (props.spacing.afterTwips !== undefined) attrs[\"w:after\"] = props.spacing.afterTwips;\n if (props.spacing.line !== undefined) attrs[\"w:line\"] = props.spacing.line;\n if (props.spacing.lineRule) attrs[\"w:lineRule\"] = props.spacing.lineRule;\n if (Object.keys(attrs).length > 0) parts.push(el(\"w:spacing\", attrs));\n }\n if (props.indent) {\n const attrs: Record<string, string | number> = {};\n if (props.indent.leftTwips !== undefined) attrs[\"w:left\"] = props.indent.leftTwips;\n if (props.indent.rightTwips !== undefined) attrs[\"w:right\"] = props.indent.rightTwips;\n if (props.indent.firstLineTwips !== undefined) attrs[\"w:firstLine\"] = props.indent.firstLineTwips;\n if (props.indent.hangingTwips !== undefined) attrs[\"w:hanging\"] = props.indent.hangingTwips;\n if (Object.keys(attrs).length > 0) parts.push(el(\"w:ind\", attrs));\n }\n if (props.borders?.bottom) {\n const b = props.borders.bottom;\n parts.push(\n el(\n \"w:pBdr\",\n null,\n el(\"w:bottom\", {\n \"w:val\": b.style,\n \"w:sz\": b.sizeEighthsOfPt,\n \"w:space\": b.spaceTwips ?? 1,\n \"w:color\": b.color,\n }),\n ),\n );\n }\n if (props.keepNext) parts.push(el(\"w:keepNext\"));\n if (props.keepLines) parts.push(el(\"w:keepLines\"));\n if (props.pageBreakBefore) parts.push(el(\"w:pageBreakBefore\"));\n // Paragraph-mark revision — `<w:pPr><w:rPr><w:ins .../></w:rPr></w:pPr>`\n // (ECMA-376 §17.13.5.20 for ins, §17.13.5.14 for del). The `<w:rPr>`\n // inside pPr targets the paragraph mark itself, not the run text.\n if (props.revision) {\n const rev = props.revision;\n const tag = rev.type === \"ins\" ? \"w:ins\" : \"w:del\";\n const attrs: Record<string, string | number> = {\n \"w:id\": nextRevisionId(ctx),\n };\n if (rev.author !== undefined) attrs[\"w:author\"] = rev.author;\n if (rev.date !== undefined) attrs[\"w:date\"] = rev.date;\n parts.push(el(\"w:rPr\", null, el(tag, attrs)));\n }\n // Trailing sectPr — last in pPr child order per CT_PPr. Means \"this\n // paragraph is the last one of its section; here are the section's\n // properties.\" Section-end semantics in OOXML.\n if (trailingSectPr) parts.push(trailingSectPr);\n return parts.length > 0 ? el(\"w:pPr\", null, parts) : \"\";\n}\n\nfunction renderTable(t: Table, ctx: ExportContext, doc: SobreeDocument): string {\n const rows = t.rows.map((r) => renderTableRow(r, ctx, doc)).join(\"\");\n const grid = el(\n \"w:tblGrid\",\n null,\n t.grid.map((w) => el(\"w:gridCol\", { \"w:w\": w })),\n );\n const props = el(\n \"w:tblPr\",\n null,\n t.properties.widthTwips !== undefined\n ? el(\"w:tblW\", { \"w:w\": t.properties.widthTwips, \"w:type\": \"dxa\" })\n : el(\"w:tblW\", { \"w:w\": 0, \"w:type\": \"auto\" }),\n );\n return el(\"w:tbl\", null, `${props}${grid}${rows}`);\n}\n\nfunction renderTableRow(row: TableRow, ctx: ExportContext, doc: SobreeDocument): string {\n const trPr = row.isHeader ? el(\"w:trPr\", null, el(\"w:tblHeader\")) : \"\";\n const cells = row.cells.map((c) => renderTableCell(c, ctx, doc)).join(\"\");\n return el(\"w:tr\", null, `${trPr}${cells}`);\n}\n\nfunction renderTableCell(cell: TableCell, ctx: ExportContext, doc: SobreeDocument): string {\n const props: string[] = [];\n if (cell.gridSpan && cell.gridSpan > 1) {\n props.push(el(\"w:gridSpan\", { \"w:val\": cell.gridSpan }));\n }\n if (cell.vMerge) props.push(el(\"w:vMerge\", { \"w:val\": cell.vMerge }));\n if (cell.verticalAlign) props.push(el(\"w:vAlign\", { \"w:val\": cell.verticalAlign }));\n const tcPr = props.length > 0 ? el(\"w:tcPr\", null, props) : \"\";\n const body = cell.content.flatMap((b) => renderBlock(b, ctx, doc)).join(\"\");\n // Word requires every table cell to end with a paragraph. If the content\n // list doesn't already, emit a blank one.\n const tail = cell.content[cell.content.length - 1]?.kind === \"paragraph\" ? \"\" : el(\"w:p\");\n return el(\"w:tc\", null, `${tcPr}${body}${tail}`);\n}\n","import type { ExportContext } from \"./context\";\nimport { renderBlocks } from \"./document\";\nimport { NS } from \"../shared/namespaces\";\nimport { el, xmlDocument } from \"../shared/xml\";\nimport type {\n Block,\n HeaderFooterRef,\n SectionProperties,\n SobreeDocument,\n} from \"../../doc/types\";\n\nconst HEADER_CT =\n \"application/vnd.openxmlformats-officedocument.wordprocessingml.header+xml\";\nconst FOOTER_CT =\n \"application/vnd.openxmlformats-officedocument.wordprocessingml.footer+xml\";\n\n/**\n * Build the OOXML scaffolding for every section in `doc`: header/footer\n * XML parts, relationships, content-type overrides — and a parallel\n * array of `<w:sectPr>` XML strings, one per section.\n *\n * Mutates `ctx`. Each referenced header/footer appends to `ctx.parts`,\n * `ctx.relationships`, and `ctx.contentTypeOverrides`. Returns the\n * sectPr XMLs in section order so the body renderer can splice the\n * non-final ones into the last paragraph of each section's range and\n * place the final one at body level.\n *\n * Header/footer parts are deduped across sections by their `partId`:\n * the same `header1.xml` referenced from two sections only emits one\n * part, with one `rId`. Subsequent references reuse the existing rId.\n */\nexport function emitHeadersAndFooters(doc: SobreeDocument, ctx: ExportContext): string[] {\n if (doc.sections.length === 0) return [renderSectPrFallback()];\n\n // Cache: partId → rId so a header shared across sections gets one rId.\n const partIdToRid = new Map<string, string>();\n\n const emitForSection = (section: SectionProperties): string => {\n const headerRefXml: string[] = [];\n const footerRefXml: string[] = [];\n\n const emit = (ref: HeaderFooterRef, kind: \"header\" | \"footer\"): void => {\n if (ref.type === \"even\") return; // Scope cut.\n const body = doc.headerFooterBodies[ref.partId] ?? [];\n if (body.length === 0) return;\n let id = partIdToRid.get(ref.partId);\n if (!id) {\n const path = `word/${ref.partId}`;\n ctx.parts[path] = renderHeaderFooterXml(kind, body, ctx, doc);\n ctx.contentTypeOverrides.push({\n partName: `/${path}`,\n contentType: kind === \"header\" ? HEADER_CT : FOOTER_CT,\n });\n id = `rId${ctx.nextRid++}`;\n ctx.relationships.push({ id, type: kind, target: ref.partId });\n partIdToRid.set(ref.partId, id);\n }\n const refTag = kind === \"header\" ? \"w:headerReference\" : \"w:footerReference\";\n const xml = el(refTag, { \"w:type\": ref.type, \"r:id\": id });\n if (kind === \"header\") headerRefXml.push(xml);\n else footerRefXml.push(xml);\n };\n\n for (const ref of section.headerRefs) emit(ref, \"header\");\n for (const ref of section.footerRefs) emit(ref, \"footer\");\n\n return renderSectPr(section, headerRefXml, footerRefXml);\n };\n\n return doc.sections.map(emitForSection);\n}\n\nfunction renderHeaderFooterXml(\n kind: \"header\" | \"footer\",\n body: readonly Block[],\n ctx: ExportContext,\n doc: SobreeDocument,\n): string {\n const rootTag = kind === \"header\" ? \"w:hdr\" : \"w:ftr\";\n const children = renderBlocks(body, ctx, doc);\n // Word refuses to open hdr/ftr parts without at least one paragraph.\n if (children.length === 0) children.push(el(\"w:p\"));\n return xmlDocument(el(rootTag, { \"xmlns:w\": NS.w, \"xmlns:r\": NS.r }, children));\n}\n\nexport function renderSectPr(\n section: SectionProperties,\n headerRefs: string[],\n footerRefs: string[],\n): string {\n const children: string[] = [];\n children.push(...headerRefs, ...footerRefs);\n // `<w:type>` lives between header/footer refs and pgSz per the\n // CT_SectPr child order. Default is `nextPage` — only emit when the\n // section explicitly carries a different break type.\n if (section.type && section.type !== \"nextPage\") {\n children.push(el(\"w:type\", { \"w:val\": section.type }));\n } else if (section.type === \"nextPage\") {\n // Word writes `<w:type w:val=\"nextPage\"/>` explicitly when the\n // section was created with a section break — emit it so round-trip\n // metadata is preserved for downstream tools.\n children.push(el(\"w:type\", { \"w:val\": \"nextPage\" }));\n }\n children.push(\n el(\"w:pgSz\", {\n \"w:w\": section.pageSize.wTwips,\n \"w:h\": section.pageSize.hTwips,\n ...(section.pageSize.orientation === \"landscape\" ? { \"w:orient\": \"landscape\" } : {}),\n }),\n );\n children.push(\n el(\"w:pgMar\", {\n \"w:top\": section.pageMargins.topTwips,\n \"w:right\": section.pageMargins.rightTwips,\n \"w:bottom\": section.pageMargins.bottomTwips,\n \"w:left\": section.pageMargins.leftTwips,\n \"w:header\": section.pageMargins.headerTwips,\n \"w:footer\": section.pageMargins.footerTwips,\n \"w:gutter\": section.pageMargins.gutterTwips,\n }),\n );\n // OOXML omits `<w:vAlign>` for the default `top` value; emit only the\n // distinguished cases. See ECMA-376 §17.6.21.\n if (section.vAlign && section.vAlign !== \"top\") {\n children.push(el(\"w:vAlign\", { \"w:val\": section.vAlign }));\n }\n if (section.titlePage) children.push(el(\"w:titlePg\"));\n return el(\"w:sectPr\", null, children);\n}\n\nfunction renderSectPrFallback(): string {\n return el(\n \"w:sectPr\",\n null,\n `${el(\"w:pgSz\", { \"w:w\": 12240, \"w:h\": 15840 })}${el(\"w:pgMar\", {\n \"w:top\": 1440,\n \"w:right\": 1440,\n \"w:bottom\": 1440,\n \"w:left\": 1440,\n \"w:header\": 720,\n \"w:footer\": 720,\n \"w:gutter\": 0,\n })}`,\n );\n}\n","import { ptToHalfPt } from \"../shared/units\";\nimport { NS } from \"../shared/namespaces\";\nimport { el, xmlDocument } from \"../shared/xml\";\nimport type { NamedStyle, RunProperties } from \"../../doc/types\";\n\n/**\n * Render the document's named styles into `word/styles.xml`. Word needs a\n * style-definition entry for every `w:pStyle` referenced in the body.\n * Missing entries make Word fall back to Normal, stripping the visual\n * hierarchy.\n */\nexport function renderStylesXml(styles: readonly NamedStyle[]): string {\n const children: string[] = [];\n\n // Document-wide defaults.\n children.push(\n el(\n \"w:docDefaults\",\n null,\n [el(\"w:rPrDefault\", null, el(\"w:rPr\", null, \"\")), el(\"w:pPrDefault\", null, el(\"w:pPr\", null, \"\"))],\n ),\n );\n\n const hasNormal = styles.some((s) => s.id === \"Normal\");\n if (!hasNormal) {\n children.push(styleElement({ id: \"Normal\", type: \"paragraph\", displayName: \"Normal\" }, true));\n }\n\n for (const style of styles) {\n children.push(styleElement(style, style.id === \"Normal\"));\n }\n\n return xmlDocument(el(\"w:styles\", { \"xmlns:w\": NS.w }, children));\n}\n\nfunction styleElement(style: NamedStyle, isDefault: boolean): string {\n const attrs: Record<string, string> = {\n \"w:type\": style.type,\n \"w:styleId\": style.id,\n };\n if (isDefault) attrs[\"w:default\"] = \"1\";\n\n const body: string[] = [el(\"w:name\", { \"w:val\": style.displayName })];\n if (!isDefault && style.basedOn) body.push(el(\"w:basedOn\", { \"w:val\": style.basedOn }));\n if (style.nextStyleId) body.push(el(\"w:next\", { \"w:val\": style.nextStyleId }));\n if (style.runDefaults) {\n const rPr = runPropertiesToXml(style.runDefaults);\n if (rPr) body.push(rPr);\n }\n\n return el(\"w:style\", attrs, body);\n}\n\nfunction runPropertiesToXml(props: RunProperties): string {\n const parts: string[] = [];\n if (props.bold) parts.push(el(\"w:b\"));\n if (props.italic) parts.push(el(\"w:i\"));\n if (props.strike) parts.push(el(\"w:strike\"));\n if (props.underline && props.underline !== \"none\") {\n parts.push(el(\"w:u\", { \"w:val\": props.underline }));\n }\n if (props.color) parts.push(el(\"w:color\", { \"w:val\": props.color.replace(/^#/, \"\") }));\n if (props.fontFamily) {\n parts.push(\n el(\"w:rFonts\", {\n \"w:ascii\": props.fontFamily,\n \"w:hAnsi\": props.fontFamily,\n \"w:cs\": props.fontFamily,\n }),\n );\n }\n if (props.fontSizePt) {\n const hp = ptToHalfPt(props.fontSizePt);\n parts.push(el(\"w:sz\", { \"w:val\": hp }));\n parts.push(el(\"w:szCs\", { \"w:val\": hp }));\n }\n return parts.length > 0 ? el(\"w:rPr\", null, parts) : \"\";\n}\n","import { zipSync } from \"fflate\";\n\n/** Map of part-path → contents (string parts auto-encoded to UTF-8). */\nexport type DocxParts = Record<string, string | Uint8Array>;\n\nconst encoder = new TextEncoder();\nconst DOCX_MIME =\n \"application/vnd.openxmlformats-officedocument.wordprocessingml.document\";\n\nexport interface DocxPackage {\n blob: Blob;\n bytes: Uint8Array;\n}\n\n/**\n * Build a `.docx` package from a parts map. fflate's `zipSync` is plenty\n * fast for the sizes we care about. We return both a Blob (for downloads)\n * and the raw bytes (for node/jsdom environments where Blob.arrayBuffer()\n * isn't implemented).\n */\nexport function packageDocx(parts: DocxParts): DocxPackage {\n const files: Record<string, Uint8Array> = {};\n for (const [path, value] of Object.entries(parts)) {\n files[path] = typeof value === \"string\" ? encoder.encode(value) : value;\n }\n const bytes = zipSync(files);\n const blob = new Blob([new Uint8Array(bytes)], { type: DOCX_MIME });\n return { blob, bytes };\n}\n","/**\n * Collect every `rawParts` ZIP path that's reachable from the\n * document's font declarations. Used by `doc/parts.ts` so the\n * cross-feature liveness walker stays font-agnostic.\n */\n\nimport type { SobreeDocument } from \"../doc/types\";\nimport type { FontEmbedRef } from \"./types\";\n\nexport function fontLivenessPaths(doc: SobreeDocument): Set<string> {\n const out = new Set<string>();\n for (const f of doc.fonts) {\n if (!f.embed) continue;\n for (const ref of Object.values(f.embed) as Array<FontEmbedRef | undefined>) {\n if (ref?.partPath) out.add(ref.partPath);\n }\n }\n return out;\n}\n","/**\n * Reference-counting / garbage-collection for the document's binary\n * `rawParts`. The naive policy \"keep everything that came in on\n * import\" leaves orphan images behind; with embedded fonts adding more\n * parts (Phase 3), an explicit liveness model becomes essential.\n *\n * A part path is **live** if at least one of these references it:\n * - a `DrawingRun.partPath` anywhere in `doc.body` (including nested\n * paragraphs inside table cells).\n * - a `DrawingRun.partPath` inside any header/footer template body.\n * - a font embed declared on a `SobreeDocument.fonts` entry (Phase 3\n * adds this — until then `fonts` is `[]` and the loop is a no-op).\n *\n * The result feeds two callers:\n * - `editor.pruneUnusedParts()` — explicit GC after a sequence of\n * edits (e.g. deleting an image).\n * - `exportDocx` — packaging only stages live parts into the ZIP, so\n * orphans drop on round-trip without an explicit prune.\n */\n\nimport type { Block, InlineRun, SobreeDocument } from \"./types\";\nimport { walkBlock } from \"./walk\";\nimport { fontLivenessPaths } from \"../fonts/liveness\";\n\n/**\n * Walk the document and return the set of `rawParts` keys that\n * something in the AST still points at. Anything else is an orphan.\n */\nexport function collectLivePartPaths(doc: SobreeDocument): Set<string> {\n const live = new Set<string>();\n\n // Body + every table cell.\n for (const block of doc.body) collectFromBlock(block, live);\n\n // Header + footer templates — same shape as body, separate trees.\n for (const blocks of Object.values(doc.headerFooterBodies)) {\n for (const block of blocks) collectFromBlock(block, live);\n }\n\n // Embedded font parts. The fonts module owns its own walker so the\n // liveness logic stays font-agnostic here.\n for (const path of fontLivenessPaths(doc)) live.add(path);\n\n return live;\n}\n\nfunction collectFromBlock(block: Block, live: Set<string>): void {\n walkBlock(block, {\n run: (run: InlineRun) => {\n if (run.kind === \"drawing\" && run.partPath) live.add(run.partPath);\n },\n });\n}\n\n/**\n * Return a new document whose `rawParts` only contains live keys.\n * Reports which keys were dropped. The document is otherwise unchanged\n * — no AST mutation, no version bumps, no header/footer churn.\n *\n * Idempotent: pruning an already-clean doc is a no-op.\n */\nexport function pruneOrphanParts(doc: SobreeDocument): {\n doc: SobreeDocument;\n kept: number;\n pruned: string[];\n} {\n const live = collectLivePartPaths(doc);\n const next: Record<string, Uint8Array> = {};\n const pruned: string[] = [];\n for (const [path, bytes] of Object.entries(doc.rawParts)) {\n if (live.has(path)) next[path] = bytes;\n else pruned.push(path);\n }\n return {\n doc: pruned.length === 0 ? doc : { ...doc, rawParts: next },\n kept: Object.keys(next).length,\n pruned,\n };\n}\n","/**\n * ODTTF (Obfuscated TrueType Font) codec for OOXML font embedding.\n *\n * Per ECMA-376 Part 4 §2.8.1: Word obfuscates embedded font binaries\n * by XORing the first 32 bytes with a 16-byte key derived from a GUID\n * (the `w:fontKey` attribute on `<w:embedRegular/>` / etc.). The key\n * is the GUID's 16 bytes in **reverse** order. The first 16 bytes of\n * the font are XOR'd with the key, then the next 16 bytes are XOR'd\n * with the same key. Bytes 32..end pass through unchanged.\n *\n * Symmetry — the operation is its own inverse (XOR is involutive), so\n * `obfuscate(deobfuscate(x, k), k) === x`.\n *\n * A `fontKey` of all-zeros means \"no obfuscation\" and the bytes are\n * already a raw TTF/OTF.\n */\n\nconst ALL_ZERO_KEY = \"{00000000-0000-0000-0000-000000000000}\";\n\n/**\n * XOR-deobfuscate or -obfuscate the first 32 bytes of `bytes` with the\n * key derived from `fontKey`. Returns a fresh `Uint8Array` — the input\n * is not mutated.\n */\nexport function deobfuscate(bytes: Uint8Array, fontKey: string): Uint8Array {\n return transform(bytes, fontKey);\n}\n\n/** Symmetric inverse — same operation as `deobfuscate`, named for clarity at call sites. */\nexport function obfuscate(bytes: Uint8Array, fontKey: string): Uint8Array {\n return transform(bytes, fontKey);\n}\n\n/**\n * Generate a fresh GUID in the canonical `{XX-...}` form Word uses for\n * `w:fontKey`. Random bytes via `crypto.getRandomValues`; falls back to\n * `Math.random()` if `crypto` isn't present (jsdom often is, Node 19+\n * is too — fallback only matters for ancient runtimes).\n */\nexport function generateFontKey(): string {\n const bytes = new Uint8Array(16);\n if (typeof crypto !== \"undefined\" && crypto.getRandomValues) {\n crypto.getRandomValues(bytes);\n } else {\n for (let i = 0; i < 16; i++) bytes[i] = Math.floor(Math.random() * 256);\n }\n // Conform to UUID v4 layout (per RFC 4122).\n bytes[6] = ((bytes[6] ?? 0) & 0x0f) | 0x40;\n bytes[8] = ((bytes[8] ?? 0) & 0x3f) | 0x80;\n const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, \"0\")).join(\"\");\n return `{${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20, 32)}}`;\n}\n\n/** True when `fontKey` is the sentinel \"no obfuscation\" GUID. */\nexport function isUnobfuscated(fontKey: string | undefined): boolean {\n if (!fontKey) return true;\n return normaliseKey(fontKey) === ALL_ZERO_KEY.replace(/[^0-9a-f]/gi, \"\");\n}\n\n// ---------- internals ----------\n\nfunction transform(bytes: Uint8Array, fontKey: string): Uint8Array {\n const out = new Uint8Array(bytes.length);\n out.set(bytes);\n const key = keyToReversedBytes(fontKey);\n // 32 bytes total (two 16-byte chunks) get XOR'd. Bound by the actual\n // byte length so a sub-32-byte input doesn't overflow.\n const limit = Math.min(out.length, 32);\n for (let i = 0; i < limit; i++) {\n out[i] = (out[i] ?? 0) ^ (key[i % 16] ?? 0);\n }\n return out;\n}\n\n/**\n * Parse `{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}` into the 16 raw bytes,\n * then **reverse** them — Word's byte order is the canonical hex\n * left-to-right read REVERSED, not .NET's `Guid.ToByteArray()` order.\n */\nfunction keyToReversedBytes(fontKey: string): Uint8Array {\n const hex = normaliseKey(fontKey);\n if (hex.length !== 32) {\n throw new Error(`Invalid fontKey \"${fontKey}\" (expected 32 hex chars after stripping)`);\n }\n const fwd = new Uint8Array(16);\n for (let i = 0; i < 16; i++) {\n fwd[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);\n }\n // Reverse in place.\n const rev = new Uint8Array(16);\n for (let i = 0; i < 16; i++) rev[i] = fwd[15 - i] ?? 0;\n return rev;\n}\n\nfunction normaliseKey(fontKey: string): string {\n return fontKey.replace(/[{}\\-]/g, \"\").toLowerCase();\n}\n","/**\n * Read the OS/2 table's `fsType` field from a TrueType / OpenType font.\n * The field encodes the font foundry's embedding licence — applications\n * that embed must check it. See OpenType spec, OS/2 table § \"fsType\":\n * https://learn.microsoft.com/en-us/typography/opentype/spec/os2#fst\n *\n * Bits (mask with 0x000F for the licence triplet — they're mutually\n * exclusive in practice):\n * 0x0000 Installable embedding allowed. ← happy path.\n * 0x0002 Restricted licence — must NOT be embedded.\n * 0x0004 Preview & Print — embed for view/print only, no editing.\n * 0x0008 Editable — embed and allow modifications.\n * 0x0100 No subsetting — must embed the full face.\n * 0x0200 Bitmap embedding only.\n *\n * `readFsType` returns `null` on parse failure (corrupt header, missing\n * OS/2 table, truncated input). Callers treat `null` as \"unknown\n * licence — don't embed unless explicitly forced.\"\n */\n\nexport type EmbedMode = \"installable\" | \"preview\" | \"editable\" | \"restricted\";\n\nexport interface FsTypeReport {\n allowed: boolean;\n mode: EmbedMode;\n noSubset: boolean;\n bitmapOnly: boolean;\n /** Raw fsType field, for callers that want the bits. */\n raw: number;\n}\n\n/** Walk the table directory and return `OS/2.fsType`, or null on failure. */\nexport function readFsType(font: Uint8Array): number | null {\n if (font.length < 12) return null;\n const view = new DataView(\n font.buffer,\n font.byteOffset,\n font.byteLength,\n );\n // Offset 0: sfnt version (uint32). Valid: 0x00010000 (TrueType),\n // 0x4F54544F = \"OTTO\" (CFF/OpenType). Reject anything else.\n const sfnt = view.getUint32(0);\n if (sfnt !== 0x00010000 && sfnt !== 0x4f54544f) return null;\n const numTables = view.getUint16(4);\n // Each table directory entry is 16 bytes. Header is 12 bytes.\n const dirEnd = 12 + numTables * 16;\n if (font.length < dirEnd) return null;\n\n const OS2_TAG = 0x4f532f32; // \"OS/2\"\n let os2Offset = -1;\n for (let i = 0; i < numTables; i++) {\n const entry = 12 + i * 16;\n const tag = view.getUint32(entry);\n if (tag === OS2_TAG) {\n os2Offset = view.getUint32(entry + 8); // offset\n break;\n }\n }\n if (os2Offset < 0) return null;\n // OS/2 layout: version(uint16) at 0, xAvgCharWidth(int16) at 2,\n // usWeightClass(uint16) at 4, usWidthClass(uint16) at 6,\n // fsType(uint16) at 8.\n if (font.length < os2Offset + 10) return null;\n return view.getUint16(os2Offset + 8);\n}\n\n/**\n * Interpret an `fsType` value into a structured embedding decision.\n * Defensive on `null` input (corrupt font) — reports as restricted so\n * callers fail closed.\n */\nexport function canEmbed(fsType: number | null): FsTypeReport {\n if (fsType === null) {\n return {\n allowed: false,\n mode: \"restricted\",\n noSubset: false,\n bitmapOnly: false,\n raw: 0,\n };\n }\n const licence = fsType & 0x000f;\n const noSubset = (fsType & 0x0100) !== 0;\n const bitmapOnly = (fsType & 0x0200) !== 0;\n\n // Bits 0x0002, 0x0004, 0x0008 are mutually exclusive in practice;\n // when more than one is set, prefer the most restrictive.\n let mode: EmbedMode;\n if ((licence & 0x0002) !== 0) mode = \"restricted\";\n else if ((licence & 0x0004) !== 0) mode = \"preview\";\n else if ((licence & 0x0008) !== 0) mode = \"editable\";\n else mode = \"installable\";\n\n return {\n allowed: mode !== \"restricted\",\n mode,\n noSubset,\n bitmapOnly,\n raw: fsType,\n };\n}\n","/**\n * Parse `word/fontTable.xml` into `FontDeclaration[]`. Embedded font\n * binaries are NOT deobfuscated here — they stay in `rawParts` exactly\n * as Word wrote them so an unmodified round-trip preserves the bytes.\n * Renderer-side `@font-face` injection deobfuscates on demand.\n *\n * Two entry points:\n * - `parseFontTable(xml, rels)` — pure parse, useful in tests.\n * - `mountFontTableFromZip(textParts, parseRels)` — orchestrates the\n * XML lookup + companion `.rels` lookup. Called by `docx/import`.\n */\n\nimport { NS } from \"../docx/shared/namespaces\";\nimport { parseXml, wAll, wChildren, wFirst, wVal } from \"../docx/shared/xml\";\nimport type { FontDeclaration, FontEmbedRef } from \"./types\";\n\ninterface Loaded {\n declarations: FontDeclaration[];\n /** ZIP paths of embedded font parts that should be kept in rawParts. */\n embeddedPartPaths: Set<string>;\n}\n\n/**\n * Convenience for the import pipeline: pull `word/fontTable.xml` +\n * `word/_rels/fontTable.xml.rels` out of the unzipped text-parts map\n * and return the parsed declarations. Falls back to an empty array\n * when the document carries no font table — most don't.\n *\n * `parseRels` is the same `parseRels` helper the document uses; passed\n * in to avoid a cycle through `docx/import`.\n */\nexport function mountFontTableFromZip(\n textParts: Record<string, string>,\n parseRels: (xml: string) => Map<string, string>,\n): FontDeclaration[] {\n const fontTableXml = textParts[\"word/fontTable.xml\"];\n if (!fontTableXml) return [];\n const fontTableRelsXml = textParts[\"word/_rels/fontTable.xml.rels\"];\n const rels = fontTableRelsXml\n ? parseRels(fontTableRelsXml)\n : new Map<string, string>();\n return parseFontTable(fontTableXml, rels).declarations;\n}\n\n/**\n * Returns parsed declarations + the set of font-part ZIP paths the\n * unzipped binary map should retain. `fontTableRels` resolves `r:id`\n * references inside `<w:embed*>` elements to their target paths.\n */\nexport function parseFontTable(\n fontTableXml: string,\n fontTableRels: Map<string, string>,\n): Loaded {\n const doc = parseXml(fontTableXml);\n const fonts = wAll(doc, \"font\");\n const declarations: FontDeclaration[] = [];\n const embeddedPartPaths = new Set<string>();\n\n for (const fEl of fonts) {\n const name =\n fEl.getAttributeNS(NS.w, \"name\") ?? fEl.getAttribute(\"w:name\") ?? \"\";\n if (!name) continue;\n const decl: FontDeclaration = { name };\n\n const altName = wFirst(fEl, \"altName\");\n if (altName) {\n const v = wVal(altName);\n if (v) decl.altName = v;\n }\n const panose = wFirst(fEl, \"panose1\");\n if (panose) {\n const v = wVal(panose);\n if (v) decl.panose = v;\n }\n const charset = wFirst(fEl, \"charset\");\n if (charset) {\n const v = wVal(charset);\n if (v) decl.charset = v;\n }\n const family = wFirst(fEl, \"family\");\n if (family) {\n const v = wVal(family) as FontDeclaration[\"family\"];\n if (v) decl.family = v;\n }\n const pitch = wFirst(fEl, \"pitch\");\n if (pitch) {\n const v = wVal(pitch) as FontDeclaration[\"pitch\"];\n if (v) decl.pitch = v;\n }\n const sigEl = wFirst(fEl, \"sig\");\n if (sigEl) {\n const sig = readSig(sigEl);\n if (sig) decl.sig = sig;\n }\n if (wFirst(fEl, \"notTrueType\")) decl.notTrueType = true;\n\n const embed = readEmbed(fEl, fontTableRels);\n if (embed) {\n decl.embed = embed;\n for (const ref of Object.values(embed)) {\n if (ref?.partPath) embeddedPartPaths.add(ref.partPath);\n }\n }\n\n declarations.push(decl);\n }\n\n return { declarations, embeddedPartPaths };\n}\n\nfunction readSig(sigEl: Element): FontDeclaration[\"sig\"] | null {\n const get = (name: string) =>\n sigEl.getAttributeNS(NS.w, name) ?? sigEl.getAttribute(`w:${name}`);\n const usb0 = get(\"usb0\");\n const usb1 = get(\"usb1\");\n const usb2 = get(\"usb2\");\n const usb3 = get(\"usb3\");\n const csb0 = get(\"csb0\");\n const csb1 = get(\"csb1\");\n if (!usb0 && !usb1 && !usb2 && !usb3 && !csb0 && !csb1) return null;\n return {\n usb0: usb0 ?? \"00000000\",\n usb1: usb1 ?? \"00000000\",\n usb2: usb2 ?? \"00000000\",\n usb3: usb3 ?? \"00000000\",\n csb0: csb0 ?? \"00000000\",\n csb1: csb1 ?? \"00000000\",\n };\n}\n\nfunction readEmbed(\n fEl: Element,\n rels: Map<string, string>,\n): FontDeclaration[\"embed\"] | null {\n const slots: Array<[\"regular\" | \"bold\" | \"italic\" | \"boldItalic\", string]> = [\n [\"regular\", \"embedRegular\"],\n [\"bold\", \"embedBold\"],\n [\"italic\", \"embedItalic\"],\n [\"boldItalic\", \"embedBoldItalic\"],\n ];\n const out: NonNullable<FontDeclaration[\"embed\"]> = {};\n let any = false;\n for (const [key, tag] of slots) {\n const el = wFirst(fEl, tag);\n if (!el) continue;\n const ref = readEmbedRef(el, rels);\n if (ref) {\n out[key] = ref;\n any = true;\n }\n }\n return any ? out : null;\n}\n\nfunction readEmbedRef(\n embedEl: Element,\n rels: Map<string, string>,\n): FontEmbedRef | null {\n const rid =\n embedEl.getAttributeNS(NS.r, \"id\") ?? embedEl.getAttribute(\"r:id\");\n if (!rid) return null;\n const target = rels.get(rid);\n if (!target) return null;\n const partPath = target.startsWith(\"word/\") ? target : `word/${target}`;\n const ref: FontEmbedRef = { partPath };\n const fontKey =\n embedEl.getAttributeNS(NS.w, \"fontKey\") ??\n embedEl.getAttribute(\"w:fontKey\");\n if (fontKey) ref.fontKey = fontKey;\n const subsetted =\n embedEl.getAttributeNS(NS.w, \"subsetted\") ??\n embedEl.getAttribute(\"w:subsetted\");\n if (subsetted === \"true\" || subsetted === \"1\") ref.subsetted = true;\n return ref;\n}\n\n/**\n * Convenience: parse direct-child `<w:font>` elements, in case some\n * producers use children rather than descendants. Reserved for future\n * \"strict mode\" callers.\n */\nexport function readChildFonts(parent: Element): Element[] {\n return wChildren(parent, \"font\");\n}\n","/**\n * Emit `word/fontTable.xml` plus `word/_rels/fontTable.xml.rels`. Each\n * `FontDeclaration` becomes a `<w:font>` element; embeds turn into\n * `<w:embedRegular r:id=\"...\" w:fontKey=\"...\"/>` (and bold/italic\n * variants), with the matching font part exposed via the rels file.\n *\n * Two entry points:\n * - `emitFontTable(doc)` — pure, returns the two XML strings; useful in tests.\n * - `mountFontTableArtifacts(doc, ctx)` — orchestrates all the export-side\n * bookkeeping (stage parts, allocate document-level rel, push content-type\n * override, register .odttf extension). Called by `docx/export/index.ts`.\n */\n\nimport { NS } from \"../docx/shared/namespaces\";\nimport { el, xmlDocument } from \"../docx/shared/xml\";\nimport type { ExportContext } from \"../docx/export/context\";\nimport type { SobreeDocument } from \"../doc/types\";\nimport type { FontDeclaration, FontEmbedRef } from \"./types\";\n\ninterface FontTableEmission {\n /** Inner XML of `word/fontTable.xml`. */\n fontTableXml: string;\n /** Inner XML of `word/_rels/fontTable.xml.rels`. */\n fontTableRelsXml: string;\n}\n\nconst FONT_REL_TYPE =\n \"http://schemas.openxmlformats.org/officeDocument/2006/relationships/font\";\n\nconst FONT_TABLE_CT =\n \"application/vnd.openxmlformats-officedocument.wordprocessingml.fontTable+xml\";\n\n/**\n * Stage every font-related artifact into the export context: the\n * fontTable XML + companion rels, the document-level relationship,\n * the content-type override, and the `.odttf` extension flag. Caller\n * (docx/export/index.ts) just calls this once and forgets — no\n * font-aware code needs to live in the export entry point any more.\n *\n * No-op when `doc.fonts` is empty.\n */\nexport function mountFontTableArtifacts(\n doc: SobreeDocument,\n ctx: ExportContext,\n): void {\n const emission = emitFontTable(doc);\n if (!emission) return;\n ctx.parts[\"word/fontTable.xml\"] = emission.fontTableXml;\n ctx.parts[\"word/_rels/fontTable.xml.rels\"] = emission.fontTableRelsXml;\n const fontTableRid = `rId${ctx.nextRid++}`;\n ctx.relationships.push({\n id: fontTableRid,\n type: \"fontTable\",\n target: \"fontTable.xml\",\n });\n ctx.contentTypeOverrides.push({\n partName: \"/word/fontTable.xml\",\n contentType: FONT_TABLE_CT,\n });\n // If any embed lands as an `.odttf`, register the extension so the\n // packager emits a `<Default Extension=\"odttf\">` content-type entry.\n if (doc.fonts.some(hasObfuscatedEmbed)) ctx.mediaExtensions.add(\"odttf\");\n}\n\n/**\n * Render the font table and its companion .rels. Returns null if the\n * document declares no fonts (caller skips emitting the parts).\n */\nexport function emitFontTable(doc: SobreeDocument): FontTableEmission | null {\n if (!doc.fonts || doc.fonts.length === 0) return null;\n\n // Allocate font-table-local rIds (rId1..rIdN). These IDs live in\n // `fontTable.xml.rels`, NOT in `document.xml.rels`, so they can\n // restart from 1 without colliding.\n let nextRid = 1;\n const fontRels: Array<{ id: string; target: string }> = [];\n\n const fontEls = doc.fonts.map((decl) =>\n renderFontEl(decl, () => `rId${nextRid++}`, fontRels),\n );\n\n const fontTableXml = xmlDocument(\n el(\"w:fonts\", { \"xmlns:w\": NS.w, \"xmlns:r\": NS.r }, fontEls),\n );\n\n const relEls = fontRels.map(({ id, target }) =>\n el(\"Relationship\", { Id: id, Type: FONT_REL_TYPE, Target: target }),\n );\n const fontTableRelsXml = xmlDocument(\n el(\"Relationships\", { xmlns: NS.rel }, relEls),\n );\n\n return { fontTableXml, fontTableRelsXml };\n}\n\nfunction hasObfuscatedEmbed(decl: FontDeclaration): boolean {\n if (!decl.embed) return false;\n for (const ref of Object.values(decl.embed) as Array<FontEmbedRef | undefined>) {\n if (ref?.partPath.toLowerCase().endsWith(\".odttf\")) return true;\n }\n return false;\n}\n\nfunction renderFontEl(\n decl: FontDeclaration,\n allocRid: () => string,\n rels: Array<{ id: string; target: string }>,\n): string {\n const children: string[] = [];\n if (decl.altName) children.push(el(\"w:altName\", { \"w:val\": decl.altName }));\n if (decl.panose) children.push(el(\"w:panose1\", { \"w:val\": decl.panose }));\n if (decl.charset) children.push(el(\"w:charset\", { \"w:val\": decl.charset }));\n if (decl.family) children.push(el(\"w:family\", { \"w:val\": decl.family }));\n if (decl.pitch) children.push(el(\"w:pitch\", { \"w:val\": decl.pitch }));\n if (decl.notTrueType) children.push(el(\"w:notTrueType\"));\n if (decl.sig) {\n children.push(\n el(\"w:sig\", {\n \"w:usb0\": decl.sig.usb0,\n \"w:usb1\": decl.sig.usb1,\n \"w:usb2\": decl.sig.usb2,\n \"w:usb3\": decl.sig.usb3,\n \"w:csb0\": decl.sig.csb0,\n \"w:csb1\": decl.sig.csb1,\n }),\n );\n }\n\n if (decl.embed) {\n const slots: Array<[\"regular\" | \"bold\" | \"italic\" | \"boldItalic\", string]> = [\n [\"regular\", \"w:embedRegular\"],\n [\"bold\", \"w:embedBold\"],\n [\"italic\", \"w:embedItalic\"],\n [\"boldItalic\", \"w:embedBoldItalic\"],\n ];\n for (const [key, tag] of slots) {\n const ref = decl.embed[key];\n if (!ref) continue;\n const rid = allocRid();\n const target = ref.partPath.startsWith(\"word/\")\n ? ref.partPath.slice(\"word/\".length)\n : ref.partPath;\n rels.push({ id: rid, target });\n const attrs: Record<string, string | undefined> = { \"r:id\": rid };\n if (ref.fontKey) attrs[\"w:fontKey\"] = ref.fontKey;\n if (ref.subsetted) attrs[\"w:subsetted\"] = \"true\";\n children.push(el(tag, attrs));\n }\n }\n\n return el(\"w:font\", { \"w:name\": decl.name }, children);\n}\n","/**\n * Pure functions that mutate `SobreeDocument` to add or remove\n * embedded fonts. The Editor wraps these with `setDocument()` so the\n * change participates in the normal commit + render pipeline.\n *\n * Splitting these out of `Editor` lets tests exercise embed semantics\n * without mounting a real editor, and keeps the Editor class focused\n * on DOM/state plumbing.\n */\n\nimport type { SobreeDocument } from \"../doc/types\";\nimport { canEmbed, readFsType } from \"./fsType\";\nimport { generateFontKey, obfuscate } from \"./odttf\";\nimport type { FontDeclaration } from \"./types\";\n\nexport interface EmbedFontFaces {\n regular?: Uint8Array;\n bold?: Uint8Array;\n italic?: Uint8Array;\n boldItalic?: Uint8Array;\n}\n\nexport interface EmbedFontOptions {\n /** Embed even when OS/2 fsType marks the face as restricted. Default false. */\n allowRestricted?: boolean;\n}\n\nexport interface EmbedFontResult {\n /** Next document — same reference as input when nothing was embedded. */\n next: SobreeDocument;\n /** Per-face refusal warnings (e.g. restricted licence). */\n warnings: string[];\n}\n\n/**\n * Returns the next document with the given font embedded, plus any\n * warnings (e.g. a face refused for licence reasons). When no face\n * could be embedded, `next` is `===` the input doc — caller can\n * skip a setDocument round.\n */\nexport function embedFontIntoDoc(\n doc: SobreeDocument,\n name: string,\n faces: EmbedFontFaces,\n opts: EmbedFontOptions = {},\n): EmbedFontResult {\n const warnings: string[] = [];\n const slots: Array<[\"regular\" | \"bold\" | \"italic\" | \"boldItalic\", Uint8Array | undefined]> = [\n [\"regular\", faces.regular],\n [\"bold\", faces.bold],\n [\"italic\", faces.italic],\n [\"boldItalic\", faces.boldItalic],\n ];\n const embed: NonNullable<FontDeclaration[\"embed\"]> = {};\n const nextRawParts: Record<string, Uint8Array> = { ...doc.rawParts };\n\n for (const [key, bytes] of slots) {\n if (!bytes) continue;\n const fsType = readFsType(bytes);\n const verdict = canEmbed(fsType);\n if (verdict.mode === \"restricted\" && !opts.allowRestricted) {\n warnings.push(\n `Refused to embed \"${name}\" face \"${key}\": OS/2 fsType marks it as embedding-restricted.`,\n );\n continue;\n }\n const partPath = allocateFontPath(nextRawParts);\n const fontKey = generateFontKey();\n nextRawParts[partPath] = obfuscate(bytes, fontKey);\n embed[key] = { partPath, fontKey };\n }\n\n if (Object.keys(embed).length === 0) {\n return { next: doc, warnings };\n }\n\n // Merge with an existing declaration of the same name (a follow-up\n // embedFont call extends faces rather than duplicating).\n const existingIdx = doc.fonts.findIndex((f) => f.name === name);\n const nextFonts: FontDeclaration[] = doc.fonts.slice();\n if (existingIdx >= 0) {\n const existing = nextFonts[existingIdx]!;\n nextFonts[existingIdx] = {\n ...existing,\n embed: { ...existing.embed, ...embed },\n };\n } else {\n nextFonts.push({ name, embed });\n }\n return {\n next: { ...doc, rawParts: nextRawParts, fonts: nextFonts },\n warnings,\n };\n}\n\n/**\n * Drop a font declaration by name. Returns the same doc reference if\n * the name wasn't present (caller can skip a setDocument round).\n *\n * Font part bytes are NOT removed from `rawParts` — call\n * `pruneOrphanParts(doc)` (or just rely on export-side filtering) to\n * GC them.\n */\nexport function removeFontFromDoc(\n doc: SobreeDocument,\n name: string,\n): SobreeDocument {\n const next = doc.fonts.filter((f) => f.name !== name);\n if (next.length === doc.fonts.length) return doc;\n return { ...doc, fonts: next };\n}\n\n/** Next free `word/fonts/fontN.odttf` slot. */\nfunction allocateFontPath(rawParts: Record<string, Uint8Array>): string {\n let n = 1;\n while (rawParts[`word/fonts/font${n}.odttf`]) n += 1;\n return `word/fonts/font${n}.odttf`;\n}\n","/**\n * Register embedded fonts as `@font-face` rules so the renderer's\n * `font-family: <name>` actually picks them up. Without this, embedded\n * faces silently fall back to the OS-installed font of the same name\n * (or the next item in the CSS font-stack), which defeats the point of\n * embedding.\n *\n * One registry per Editor — owns its `<style>` tag and the blob URLs\n * it created, so destroy() cleans up after itself.\n */\n\nimport type { FontDeclaration } from \"./types\";\nimport { deobfuscate, isUnobfuscated } from \"./odttf\";\n\nexport class FontFaceRegistry {\n private readonly styleEl: HTMLStyleElement;\n /** Mostly for cleanup — every blob URL we minted needs revoking. */\n private blobUrls: string[] = [];\n /** Last-applied snapshot — skip re-registration when nothing changed. */\n private lastSerialised: string | null = null;\n\n constructor() {\n this.styleEl = document.createElement(\"style\");\n this.styleEl.dataset.sobreeFontFaces = \"\";\n document.head.appendChild(this.styleEl);\n }\n\n /**\n * Sync the registry to the current document's fonts. Idempotent —\n * if the font list hasn't changed since the last call, no work\n * happens (no blob revocation, no DOM churn).\n *\n * Bails out (no-op) when the runtime doesn't expose\n * `URL.createObjectURL` — typically jsdom in tests, where the\n * registry isn't observable anyway.\n */\n sync(fonts: readonly FontDeclaration[], rawParts: Record<string, Uint8Array>): void {\n if (typeof URL === \"undefined\" || typeof URL.createObjectURL !== \"function\") {\n return;\n }\n // Defensive: some doc-construction paths (Y.Doc projections, partial\n // setDocument calls from headless agents) hand us a doc without\n // `fonts` populated. The type contract says required, but the\n // runtime cost of normalising here is one boolean check vs. a hard\n // crash inside `serialiseKey` on every such doc.\n const fontList = fonts ?? [];\n const parts = rawParts ?? {};\n const key = serialiseKey(fontList);\n if (key === this.lastSerialised) return;\n this.lastSerialised = key;\n\n // Revoke old blob URLs before minting new ones.\n for (const url of this.blobUrls) URL.revokeObjectURL(url);\n this.blobUrls = [];\n\n const rules: string[] = [];\n for (const decl of fontList) {\n if (!decl.embed) continue;\n const slots: Array<[\"regular\" | \"bold\" | \"italic\" | \"boldItalic\", { weight: 400 | 700; italic: boolean }]> = [\n [\"regular\", { weight: 400, italic: false }],\n [\"bold\", { weight: 700, italic: false }],\n [\"italic\", { weight: 400, italic: true }],\n [\"boldItalic\", { weight: 700, italic: true }],\n ];\n for (const [key, descriptor] of slots) {\n const ref = decl.embed[key];\n if (!ref) continue;\n const obfuscated = parts[ref.partPath];\n if (!obfuscated) continue;\n const bytes = isUnobfuscated(ref.fontKey)\n ? obfuscated\n : deobfuscate(obfuscated, ref.fontKey ?? \"\");\n const blob = new Blob([new Uint8Array(bytes)], {\n type: \"application/font-sfnt\",\n });\n const url = URL.createObjectURL(blob);\n this.blobUrls.push(url);\n rules.push(buildFontFaceRule(decl.name, url, descriptor));\n }\n }\n this.styleEl.textContent = rules.join(\"\\n\");\n }\n\n destroy(): void {\n if (typeof URL !== \"undefined\" && typeof URL.revokeObjectURL === \"function\") {\n for (const url of this.blobUrls) URL.revokeObjectURL(url);\n }\n this.blobUrls = [];\n this.styleEl.remove();\n }\n}\n\nfunction buildFontFaceRule(\n name: string,\n url: string,\n descriptor: { weight: 400 | 700; italic: boolean },\n): string {\n return `@font-face {\n font-family: ${escapeFontFamily(name)};\n font-weight: ${descriptor.weight};\n font-style: ${descriptor.italic ? \"italic\" : \"normal\"};\n src: url(${url});\n}`;\n}\n\n/** Quote font-family names that contain spaces, per CSS spec. */\nfunction escapeFontFamily(name: string): string {\n return /^[A-Za-z][A-Za-z0-9_-]*$/.test(name) ? name : `\"${name.replace(/\"/g, '\\\\\"')}\"`;\n}\n\nfunction serialiseKey(fonts: readonly FontDeclaration[]): string {\n // Compact key — name + embed paths. We don't include the byte\n // contents because if the bytes change without the partPath\n // changing, the consumer either re-allocates a new partPath\n // (embedFont always does) or knows what they're doing.\n return JSON.stringify(\n fonts.map((f) => ({\n n: f.name,\n e: f.embed\n ? {\n r: f.embed.regular?.partPath,\n b: f.embed.bold?.partPath,\n i: f.embed.italic?.partPath,\n bi: f.embed.boldItalic?.partPath,\n }\n : undefined,\n })),\n );\n}\n","import { makeExportContext } from \"./context\";\nimport {\n renderContentTypesXml,\n renderDocumentRelsXml,\n renderRootRelsXml,\n} from \"./contentTypes\";\nimport { renderDocumentXml } from \"./document\";\nimport { emitHeadersAndFooters } from \"./headers\";\nimport { renderStylesXml } from \"./styles\";\nimport { type DocxParts, packageDocx } from \"./zip\";\nimport { collectLivePartPaths } from \"../../doc/parts\";\nimport { mountFontTableArtifacts } from \"../../fonts\";\nimport type { SobreeDocument } from \"../../doc/types\";\nimport type { DocxExportResult } from \"../types\";\n\n/**\n * Export a SobreeDocument as a .docx Blob + raw bytes.\n *\n * Emits the OOXML package:\n * - `[Content_Types].xml`, `_rels/.rels`,\n * `word/_rels/document.xml.rels`\n * - `word/styles.xml`, `word/document.xml`\n * - `word/header*.xml` / `word/footer*.xml` (per header/footer reference)\n * - `word/media/*` (per referenced image) — copied verbatim from\n * `doc.rawParts` and declared as image relationships.\n */\nexport function exportDocx(doc: SobreeDocument): DocxExportResult {\n const warnings: string[] = [];\n\n // rId1 = styles relationship (written by renderDocumentRelsXml).\n // Headers / footers / images allocate from rId2 upwards.\n const ctx = makeExportContext(2);\n\n // Emit header/footer parts first so they populate ctx before the body\n // walker starts (order doesn't affect rId allocation since images in\n // headers/footers are walked inside emitHeadersAndFooters too). The\n // returned array carries one `<w:sectPr>` per section — non-final\n // ones get spliced into paragraph pPrs by `renderDocumentXml`.\n const sectPrXmls = emitHeadersAndFooters(doc, ctx);\n\n // Walk the body, emitting drawings that register image parts in ctx.\n const documentXml = renderDocumentXml(doc, sectPrXmls, ctx);\n\n // Font table — staged before the safety-net loop so embedded font\n // partPaths are still picked up there. The fonts module owns all\n // the part / rel / content-type bookkeeping.\n mountFontTableArtifacts(doc, ctx);\n\n // Safety net: emitters above stage every referenced part into\n // `ctx.parts` as they encounter it. This loop catches any live part\n // whose bytes live in `doc.rawParts` but never got staged through an\n // emitter (defensive — keeps export aligned with the \"AST is the\n // source of truth\" policy, and lets future emitters skip the manual\n // `ctx.parts[path] = doc.rawParts[path]` copy).\n const live = collectLivePartPaths(doc);\n for (const path of live) {\n if (ctx.parts[path]) continue;\n const bytes = doc.rawParts[path];\n if (bytes) ctx.parts[path] = bytes;\n }\n\n const parts: DocxParts = {\n \"[Content_Types].xml\": renderContentTypesXml(\n ctx.contentTypeOverrides,\n Array.from(ctx.mediaExtensions),\n ),\n \"_rels/.rels\": renderRootRelsXml(),\n \"word/_rels/document.xml.rels\": renderDocumentRelsXml(ctx.relationships),\n \"word/document.xml\": documentXml,\n \"word/styles.xml\": renderStylesXml(doc.styles),\n ...ctx.parts,\n };\n\n const pkg = packageDocx(parts);\n return { blob: pkg.blob, bytes: pkg.bytes, warnings };\n}\n","import { NS } from \"../shared/namespaces\";\nimport { halfPtToPt } from \"../shared/units\";\nimport { wFirst, wVal } from \"../shared/xml\";\nimport type { RunFormat } from \"../types\";\n\n/** Frame-of-reference choices the importer carries through; mapped 1:1 to\n * the `DrawingAnchor.relativeFromH` / `relativeFromV` AST values. */\nexport interface ImportedAnchor {\n offsetXEmu: number;\n offsetYEmu: number;\n relativeFromH: \"page\" | \"margin\" | \"column\" | \"character\";\n relativeFromV: \"page\" | \"margin\" | \"paragraph\" | \"line\";\n behindDoc?: boolean;\n}\n\n/** Drawing info extracted from a `<w:drawing>` inside a run. */\nexport interface ImportedDrawing {\n /** Relationship id of the embedded image (`<a:blip r:embed=\"rIdN\"/>`). */\n embedRelId?: string;\n widthEmu?: number;\n heightEmu?: number;\n altText?: string;\n /** Present when the drawing is a `<wp:anchor>` (floating) rather than\n * `<wp:inline>`. The renderer positions the image absolutely via\n * these coordinates. */\n anchor?: ImportedAnchor;\n}\n\n/**\n * Read a `<w:r>` element into a `{ text, format }` pair. The document\n * converter maps the format flags onto the native `RunProperties` shape.\n */\nexport interface ImportedRun {\n text: string;\n format: RunFormat;\n /** True if this run was `<w:br/>`; `text` is empty in that case. */\n isHardBreak: boolean;\n /** Type of break for `isHardBreak` runs — line (Shift-Enter), page\n * (force new page), or column (force next column in a multi-column\n * section). Defaults to \"line\" when omitted. */\n breakType?: \"line\" | \"page\" | \"column\";\n /** Set when the run wraps an inline `<w:drawing>` (image). */\n drawing?: ImportedDrawing;\n /** Set when the run wraps a `<w:footnoteReference w:id=\"N\"/>`. */\n footnoteRefId?: number;\n /** Set when the run wraps a `<w:commentReference w:id=\"N\"/>`. */\n commentRefId?: number;\n /** Set when the run is inside a `<w:ins>` / `<w:del>` wrapper. */\n revision?: { type: \"ins\" | \"del\"; author?: string; date?: string };\n /** Set when the run is between a `<w:commentRangeStart w:id=\"N\"/>`\n * and matching `<w:commentRangeEnd>`. Multiple ids when nested /\n * overlapping comments cover the run. */\n commentIds?: readonly number[];\n /**\n * Set when the source was a `<w:fldSimple w:instr=\"...\">`. The\n * paragraph converter emits a `FieldRun` from this — used for\n * page-number tokens (`PAGE` / `NUMPAGES`) in headers and footers\n * so the round-trip through `blocksToTemplate` preserves `{page}` /\n * `{pages}`.\n */\n field?: { instruction: string; cached?: string };\n}\n\nexport function readRun(r: Element): ImportedRun {\n // Hard break — emitted as its own run with no text. Word distinguishes\n // line / page / column breaks via `<w:br w:type=\"...\">`. Column\n // breaks are critical for multi-column form layouts (jellap.docx\n // uses one to push subsequent fields to the right column so they\n // pair as table rows).\n const brEl = wFirst(r, \"br\");\n if (brEl) {\n const typeAttr =\n brEl.getAttributeNS(NS.w, \"type\") ?? brEl.getAttribute(\"w:type\");\n const breakType: \"line\" | \"page\" | \"column\" =\n typeAttr === \"page\"\n ? \"page\"\n : typeAttr === \"column\"\n ? \"column\"\n : \"line\";\n return { text: \"\", format: {}, isHardBreak: true, breakType };\n }\n\n // Drawing (image). Inline placement renders in the paragraph; anchor\n // placement is now handled by the per-page anchor layer\n // (`parseAnchoredFrames` + `renderAnchorLayer`). When we encounter\n // an `<wp:anchor>` drawing here, return an empty run so the new\n // layer is the SOLE paint path — without this, runs.ts would emit\n // a full-extent inline DrawingRun for every anchored shape, which\n // the inline renderer paints at the natural picture size (huge\n // arrows / decorative bands intruding into body flow).\n const drawing = wFirst(r, \"drawing\");\n if (drawing) {\n const anchorEl = drawing.getElementsByTagNameNS(NS.wp, \"anchor\")[0];\n if (anchorEl) {\n return { text: \"\", format: {}, isHardBreak: false };\n }\n const info = readDrawing(drawing);\n return { text: \"\", format: {}, isHardBreak: false, drawing: info };\n }\n\n // Legacy VML object: `<w:object>` (OLE embeds, often used by older\n // Word versions and templates) and `<w:pict>` (VML-only pictures).\n // Both can wrap a `<v:shape>` containing `<v:imagedata r:id=\"...\">`\n // — the OLE-fallback image path templates use for badge/logo art.\n // Read the VML imagedata as a regular DrawingRun so the renderer can\n // show the image; without this, OLE-only pictures vanish from the\n // import.\n const vmlContainer = wFirst(r, \"object\") ?? wFirst(r, \"pict\");\n if (vmlContainer) {\n const info = readVmlImage(vmlContainer);\n if (info) return { text: \"\", format: {}, isHardBreak: false, drawing: info };\n }\n\n // Footnote reference — `<w:footnoteReference w:id=\"N\"/>`. The body\n // text of footnote N lives in `word/footnotes.xml`, parsed\n // separately into `SobreeDocument.footnotes[N]`.\n const footnoteRef = wFirst(r, \"footnoteReference\");\n if (footnoteRef) {\n const idAttr =\n footnoteRef.getAttributeNS(NS.w, \"id\") ?? footnoteRef.getAttribute(\"w:id\");\n const id = Number(idAttr);\n if (Number.isFinite(id) && id >= 1) {\n return { text: \"\", format: {}, isHardBreak: false, footnoteRefId: id };\n }\n }\n\n // Comment reference — `<w:commentReference w:id=\"N\"/>`. Word draws\n // a balloon glyph at the position; we mirror with a clickable inline\n // span. The actual comment text lives in `SobreeDocument.comments[N]`.\n const commentRef = wFirst(r, \"commentReference\");\n if (commentRef) {\n const idAttr =\n commentRef.getAttributeNS(NS.w, \"id\") ?? commentRef.getAttribute(\"w:id\");\n const id = Number(idAttr);\n if (Number.isFinite(id) && id >= 0) {\n return { text: \"\", format: {}, isHardBreak: false, commentRefId: id };\n }\n }\n\n // Walk the run's children in document order so interleaved\n // `<w:t>` / `<w:tab/>` / `<w:delText>` elements get concatenated in\n // the right sequence. The earlier `join the <w:t> texts` shortcut\n // silently dropped tabs — fatal for Word headers like jellap.docx\n // where \"Cím:\" → `<w:tab/>` → \"1012 Budapest\" relies on the tab to\n // separate label and value. We emit a real tab character `\\t`;\n // combined with `white-space: pre-wrap` on body paragraphs the\n // browser renders it as a tab stop (default tab-size; honouring the\n // paragraph's own `<w:tabs>` stops is a follow-up).\n let text = \"\";\n for (const child of Array.from(r.children)) {\n if (child.namespaceURI !== NS.w) continue;\n if (child.localName === \"t\" || child.localName === \"delText\") {\n text += child.textContent ?? \"\";\n } else if (child.localName === \"tab\") {\n text += \"\\t\";\n }\n // Other children (rPr, br, drawing, footnoteReference, …) are\n // handled by the dedicated branches above before we reach here.\n }\n\n text = normaliseRunText(text);\n\n const rPr = wFirst(r, \"rPr\");\n const format: RunFormat = rPr ? readRunFormat(rPr) : {};\n\n // `<w:rPrChange>` — a snapshot of the run's pre-tracked-edit\n // properties. Lives inside the *current* `<w:rPr>` and wraps its\n // own inner `<w:rPr>` carrying the snapshot. We recurse into that\n // inner rPr through the same parser so the snapshot keeps the same\n // shape as the current properties.\n if (rPr) {\n const rPrChange = wFirst(rPr, \"rPrChange\");\n if (rPrChange) {\n const innerRPr = wFirst(rPrChange, \"rPr\");\n const before = innerRPr ? readRunFormat(innerRPr) : {};\n const author =\n rPrChange.getAttributeNS(NS.w, \"author\") ??\n rPrChange.getAttribute(\"w:author\") ??\n undefined;\n const date =\n rPrChange.getAttributeNS(NS.w, \"date\") ??\n rPrChange.getAttribute(\"w:date\") ??\n undefined;\n format.revisionFormat = {\n before,\n ...(author !== undefined ? { author } : {}),\n ...(date !== undefined ? { date } : {}),\n };\n }\n }\n\n return { text, format, isHardBreak: false };\n}\n\n/**\n * Parse a `<w:rPr>` element into a `RunFormat`. Extracted so it can\n * recurse for the `<w:rPrChange><w:rPr>...</w:rPr></w:rPrChange>`\n * snapshot (without re-parsing the change marker itself — the snapshot\n * is the *pre-tracking* state and never carries its own `rPrChange`).\n */\n/**\n * Pull-in normalisation for `<w:t>` content. Word renders certain\n * source artefacts visually differently from how a browser would:\n *\n * - **Long whitespace runs** — authors use 50-100 literal spaces to\n * push a label to the right column. Word renders these spaces at\n * a narrower glyph width than browsers (Calibri spaces about 2pt\n * vs browser-fallback about 3.5pt). To stop the row from wrapping\n * in Sobree, collapse pure-whitespace runs of 4+ chars to a single\n * space. Tabs are not collapsed - they are preserved verbatim\n * because jellap.docx (and many forms) lean on `<w:tab/>` for\n * alignment.\n *\n * The transform runs at IMPORT time. Ellipsis (U+2026) characters are\n * NOT substituted here - they keep their original codepoint so the\n * renderer can apply CSS letter-spacing tightening that preserves the\n * dot-leader visual without touching the AST.\n */\nfunction normaliseRunText(text: string): string {\n if (!text) return text;\n if (text.includes(\"\\t\")) return text;\n if (text.length >= 4 && /^[ \\u00A0]+$/.test(text)) return \" \";\n return text;\n}\n\nfunction readRunFormat(rPr: Element): RunFormat {\n const format: RunFormat = {};\n if (hasBooleanProperty(rPr, \"b\")) format.bold = true;\n if (hasBooleanProperty(rPr, \"i\")) format.italic = true;\n if (hasBooleanProperty(rPr, \"strike\")) format.strike = true;\n // `<w:caps/>` (or `<w:caps w:val=\"true\"/>`) — render the run with\n // `text-transform: uppercase`. Word toggles the displayed glyph case\n // without mutating the source text; round-trip preserves the\n // mixed-case characters. Without honouring this, resume templates\n // like healthcare-with-photo render \"Peter Burkimsher\" instead of\n // \"PETER BURKIMSHER\" in the banner.\n if (hasBooleanProperty(rPr, \"caps\")) format.caps = true;\n // Word's `<w:u>` has a `w:val` of \"none\" | \"single\" | \"double\" | … —\n // treat anything non-\"none\" as underline for our purposes.\n const u = wFirst(rPr, \"u\");\n if (u && wVal(u) !== \"none\") format.underline = true;\n\n const colorEl = wFirst(rPr, \"color\");\n const color = wVal(colorEl);\n if (color && color !== \"auto\") {\n format.color = color.startsWith(\"#\") ? color : `#${color}`;\n }\n\n const highlight = wVal(wFirst(rPr, \"highlight\"));\n if (highlight && highlight !== \"none\") format.highlight = highlight;\n\n const rFonts = wFirst(rPr, \"rFonts\");\n if (rFonts) {\n // Prefer `w:ascii`, fall back to `w:hAnsi`. We ignore `eastAsia` and\n // `cs` for the first pass — adding them is a follow-up once we have\n // non-Latin test fixtures.\n const font =\n rFonts.getAttributeNS(NS.w, \"ascii\") ??\n rFonts.getAttribute(\"w:ascii\") ??\n rFonts.getAttributeNS(NS.w, \"hAnsi\") ??\n rFonts.getAttribute(\"w:hAnsi\");\n if (font) format.fontFamily = font;\n }\n\n const sz = wVal(wFirst(rPr, \"sz\"));\n if (sz) {\n const pt = halfPtToPt(Number(sz));\n if (Number.isFinite(pt) && pt > 0) format.fontSizePt = pt;\n }\n\n const vAlign = wVal(wFirst(rPr, \"vertAlign\"));\n if (vAlign === \"subscript\" || vAlign === \"superscript\") format.verticalAlign = vAlign;\n return format;\n}\n\n/**\n * Walk into a `<w:drawing>` to pull out the embed id (rId of the image\n * relationship), the extent (in EMU), any docPr description used as\n * alt text, and — for `<wp:anchor>` drawings — the position offsets +\n * frame of reference.\n */\n/**\n * Read a VML image container (`<w:object>` for OLE embeds, `<w:pict>`\n * for VML-only pictures) into the same `ImportedDrawing` shape the\n * DrawingML path produces. The renderer doesn't care about the source\n * format — it just needs the embedded image's rId and the rendered\n * pixel extent.\n *\n * VML expresses size via `<v:shape style=\"width:Xpt;height:Ypt\">`; we\n * parse the inline style to recover the EMU extent (1pt = 12700 EMU).\n * Returns null when the container has no `<v:imagedata>` (pure shape\n * with no image — rare in real-world docs).\n */\nfunction readVmlImage(container: Element): ImportedDrawing | null {\n // `<v:imagedata>` is the OLE/VML equivalent of `<a:blip>`. Search\n // anywhere under the container; older Word versions nest it inside\n // `<v:shape>` which itself may be inside multiple wrapping elements.\n const V_NS = \"urn:schemas-microsoft-com:vml\";\n const imagedata = container.getElementsByTagNameNS(V_NS, \"imagedata\")[0];\n if (!imagedata) return null;\n const rId =\n imagedata.getAttributeNS(NS.r, \"id\") ?? imagedata.getAttribute(\"r:id\");\n if (!rId) return null;\n const out: ImportedDrawing = { embedRelId: rId };\n // VML size lives in the `<v:shape style=\"...\">` attribute. Parse\n // \"width:Xpt;height:Ypt\" or \"width:Xin;height:Yin\" forms.\n const shape = container.getElementsByTagNameNS(V_NS, \"shape\")[0];\n const style = shape?.getAttribute(\"style\") ?? \"\";\n const widthPt = parseVmlDimension(style.match(/width:\\s*([\\d.]+)([a-z%]*)/i));\n const heightPt = parseVmlDimension(style.match(/height:\\s*([\\d.]+)([a-z%]*)/i));\n if (widthPt > 0) out.widthEmu = Math.round(widthPt * 12700);\n if (heightPt > 0) out.heightEmu = Math.round(heightPt * 12700);\n // VML doesn't carry alt text in a portable place — leave undefined.\n return out;\n}\n\n/** Convert a CSS-style numeric+unit match into points. */\nfunction parseVmlDimension(match: RegExpMatchArray | null): number {\n if (!match) return 0;\n const v = Number(match[1]);\n if (!Number.isFinite(v) || v <= 0) return 0;\n const unit = (match[2] ?? \"pt\").toLowerCase();\n switch (unit) {\n case \"pt\": return v;\n case \"in\": return v * 72;\n case \"px\": return v * 0.75;\n case \"mm\": return (v / 25.4) * 72;\n case \"cm\": return (v / 2.54) * 72;\n default: return v; // assume pt for unitless\n }\n}\n\nfunction readDrawing(drawing: Element): ImportedDrawing {\n const out: ImportedDrawing = {};\n const inlineEl = drawing.getElementsByTagNameNS(NS.wp, \"inline\")[0];\n const anchorEl = drawing.getElementsByTagNameNS(NS.wp, \"anchor\")[0];\n const wpRoot = inlineEl ?? anchorEl;\n const blip = drawing.getElementsByTagNameNS(NS.a, \"blip\")[0];\n if (blip) {\n const rId =\n blip.getAttributeNS(NS.r, \"embed\") ?? blip.getAttribute(\"r:embed\");\n if (rId) out.embedRelId = rId;\n }\n const extent = wpRoot?.getElementsByTagNameNS(NS.wp, \"extent\")[0];\n if (extent) {\n const cx = Number(extent.getAttribute(\"cx\"));\n const cy = Number(extent.getAttribute(\"cy\"));\n if (Number.isFinite(cx) && cx > 0) out.widthEmu = cx;\n if (Number.isFinite(cy) && cy > 0) out.heightEmu = cy;\n }\n const docPr = wpRoot?.getElementsByTagNameNS(NS.wp, \"docPr\")[0];\n if (docPr) {\n const descr = docPr.getAttribute(\"descr\") ?? docPr.getAttribute(\"title\");\n if (descr) out.altText = descr;\n }\n if (anchorEl) {\n out.anchor = readAnchor(anchorEl);\n }\n return out;\n}\n\n/**\n * Parse the position metadata from a `<wp:anchor>` element. Each axis\n * carries a `relativeFrom` (the frame of reference — page corner,\n * margin corner, paragraph baseline, etc.) and a `posOffset` in EMU.\n * `<wp:align>` (left/center/right) is *not* yet parsed — when present\n * we fall back to offset 0 with the default frame; calibration can\n * land that when a fixture surfaces it.\n */\nfunction readAnchor(anchor: Element): ImportedAnchor {\n const posH = anchor.getElementsByTagNameNS(NS.wp, \"positionH\")[0];\n const posV = anchor.getElementsByTagNameNS(NS.wp, \"positionV\")[0];\n const behindDoc = anchor.getAttribute(\"behindDoc\") === \"1\";\n const out: ImportedAnchor = {\n offsetXEmu: readPosOffset(posH),\n offsetYEmu: readPosOffset(posV),\n relativeFromH: normaliseRelativeFromH(\n posH?.getAttribute(\"relativeFrom\") ?? \"column\",\n ),\n relativeFromV: normaliseRelativeFromV(\n posV?.getAttribute(\"relativeFrom\") ?? \"paragraph\",\n ),\n };\n if (behindDoc) out.behindDoc = true;\n return out;\n}\n\nfunction readPosOffset(positionEl: Element | undefined): number {\n if (!positionEl) return 0;\n const posOffset = positionEl.getElementsByTagNameNS(NS.wp, \"posOffset\")[0];\n if (!posOffset) return 0;\n const n = Number(posOffset.textContent ?? \"0\");\n return Number.isFinite(n) ? n : 0;\n}\n\nfunction normaliseRelativeFromH(v: string): ImportedAnchor[\"relativeFromH\"] {\n if (v === \"page\" || v === \"margin\" || v === \"character\") return v;\n // \"column\", \"leftMargin\", \"rightMargin\", \"insideMargin\", \"outsideMargin\"\n // all collapse to \"column\" for our coarse renderer — close enough for\n // single-column docs (the typical floating-image case).\n return \"column\";\n}\n\nfunction normaliseRelativeFromV(v: string): ImportedAnchor[\"relativeFromV\"] {\n if (v === \"page\" || v === \"margin\" || v === \"line\") return v;\n // \"paragraph\", \"topMargin\", \"bottomMargin\", \"insideMargin\", \"outsideMargin\"\n // collapse to \"paragraph\".\n return \"paragraph\";\n}\n\n/**\n * `<w:rPr>` treats presence of `<w:b/>` as \"true\" but also allows\n * `<w:b w:val=\"false\"/>` — the explicit-off form. Honour both.\n */\nfunction hasBooleanProperty(rPr: Element, localName: string): boolean {\n const el = wFirst(rPr, localName);\n if (!el) return false;\n const val = wVal(el);\n if (val === null) return true; // bare `<w:b/>`\n return val !== \"false\" && val !== \"0\";\n}\n","/**\n * Read a `<w:shd>` element into a `Shading` AST node.\n *\n * The `<w:shd>` element appears in three contexts in OOXML:\n * - `<w:tcPr><w:shd>` — table-cell background\n * - `<w:pPr><w:shd>` — paragraph background\n * - `<w:rPr><w:shd>` — run background (rare)\n *\n * The attribute shape is identical across all three (`val`, `fill`,\n * `color`), so one reader covers them all. Returns `undefined` when\n * the element is missing or its `fill` is `auto`/missing (no visible\n * background to apply).\n */\n\nimport { wFirst, wVal } from \"./xml\";\nimport type { Shading } from \"../../doc/types\";\n\nexport function readShading(parent: Element): Shading | undefined {\n const shdEl = wFirst(parent, \"shd\");\n if (!shdEl) return undefined;\n const fillRaw =\n shdEl.getAttributeNS(shdEl.namespaceURI, \"fill\") ?? shdEl.getAttribute(\"w:fill\");\n if (!fillRaw || fillRaw === \"auto\") return undefined;\n const pattern = wVal(shdEl) ?? \"clear\";\n const fill = fillRaw.startsWith(\"#\") ? fillRaw : `#${fillRaw}`;\n const out: Shading = { pattern, fill };\n const colorRaw =\n shdEl.getAttributeNS(shdEl.namespaceURI, \"color\") ?? shdEl.getAttribute(\"w:color\");\n if (colorRaw && colorRaw !== \"auto\") {\n out.color = colorRaw.startsWith(\"#\") ? colorRaw : `#${colorRaw}`;\n }\n return out;\n}\n","import { type ImportedRun, readRun } from \"./runs\";\nimport { NS } from \"../shared/namespaces\";\nimport { halfPtToPt, ooxmlLineHeightToCss } from \"../shared/units\";\nimport { wChildren, wFirst, wVal } from \"../shared/xml\";\nimport { readShading } from \"../shared/shading\";\nimport type { ParagraphFormat } from \"../types\";\n\n/** Source-order paragraph item: either a flat run or a hyperlink-wrapped group. */\nexport type ImportedItem =\n | { kind: \"run\"; run: ImportedRun }\n | { kind: \"hyperlink\"; relId?: string; runs: ImportedRun[] };\n\nexport interface ImportedParagraph {\n /** Items in document order. Hyperlinks contain inner runs. */\n items: ImportedItem[];\n format: ParagraphFormat;\n}\n\n/**\n * Read a single `<w:p>` into an `ImportedParagraph`.\n *\n * `activeComments` is an *external* set the caller threads across\n * paragraphs so comment ranges (`<w:commentRangeStart/End>`) that span\n * multiple paragraphs tag the middle paragraphs' runs too. When\n * omitted, a fresh empty set is used — fine for contexts where ranges\n * shouldn't cross the paragraph (footnote bodies, comment bodies,\n * table cells).\n */\nexport function readParagraph(\n p: Element,\n activeComments: Set<number> = new Set(),\n): ImportedParagraph {\n const items: ImportedItem[] = [];\n collectParagraphChildren(p, items, undefined, activeComments);\n const pPr = wFirst(p, \"pPr\");\n const format = pPr ? readParagraphFormat(pPr) : {};\n return { items, format };\n}\n\n/**\n * Walk a `<w:p>`'s direct children, expanding `<w:ins>` / `<w:del>`\n * wrappers in-place and tagging their inner runs with revision /\n * comment markers so the renderer can apply tracked-change styling\n * and comment highlighting.\n *\n * `revision` is the marker inherited from an enclosing `<w:ins>` /\n * `<w:del>` — undefined at the top level. Nested wrappers (rare; e.g.\n * a deletion inside an insertion) take the *inner* marker since that's\n * the more specific revision.\n *\n * `activeComments` is a running set of comment ids whose\n * `<w:commentRangeStart>` we've seen but whose `<w:commentRangeEnd>`\n * we haven't. Every run pushed while the set is non-empty gets tagged\n * with a snapshot of the active ids. NOTE: ranges that cross paragraph\n * boundaries aren't yet supported — the caller resets `activeComments`\n * per paragraph, so a comment opened in para N and closed in para M>N\n * highlights only its portion in para N. Most comments target a short\n * phrase within one paragraph, so the loss is small for now.\n */\nfunction collectParagraphChildren(\n container: Element,\n out: ImportedItem[],\n revision: ImportedRun[\"revision\"],\n activeComments: Set<number>,\n): void {\n // Complex-field state tracking. Word writes PAGE/NUMPAGES fields as:\n //\n // <w:r><w:fldChar w:fldCharType=\"begin\"/></w:r>\n // <w:r><w:instrText> PAGE </w:instrText></w:r>\n // <w:r><w:fldChar w:fldCharType=\"separate\"/></w:r>\n // <w:r><w:t>1</w:t></w:r> ← cached display value\n // <w:r><w:fldChar w:fldCharType=\"end\"/></w:r>\n //\n // We swallow everything between `begin` and `end`, accumulate the\n // instruction text + cached value, then emit ONE FieldRun. Without\n // this, the footer in complex-multipage.docx shows \"Page 1 of 16\"\n // baked into every page (the source's cached value) instead of the\n // live per-paper substitution `<span class=\"sobree-field\">` enables.\n let fieldState: \"before\" | \"code\" | \"result\" = \"before\";\n let fieldInstr = \"\";\n let fieldCached = \"\";\n\n const flushField = () => {\n if (fieldState === \"before\") return;\n const run: ImportedRun = {\n text: \"\",\n format: {},\n isHardBreak: false,\n field: fieldCached !== \"\"\n ? { instruction: fieldInstr.trim(), cached: fieldCached }\n : { instruction: fieldInstr.trim() },\n };\n if (revision) run.revision = revision;\n if (activeComments.size > 0) run.commentIds = Array.from(activeComments);\n out.push({ kind: \"run\", run });\n fieldState = \"before\";\n fieldInstr = \"\";\n fieldCached = \"\";\n };\n\n for (const child of Array.from(container.children)) {\n if (child.namespaceURI !== NS.w) continue;\n if (child.localName === \"r\") {\n // Probe for field-char boundary / instrText inside this run.\n const fldChar = wFirst(child, \"fldChar\");\n const instrText = wFirst(child, \"instrText\");\n if (fldChar) {\n const type =\n fldChar.getAttributeNS(NS.w, \"fldCharType\") ??\n fldChar.getAttribute(\"w:fldCharType\");\n if (type === \"begin\") {\n // Flush any previously-open malformed field first.\n flushField();\n fieldState = \"code\";\n continue;\n }\n if (type === \"separate\") {\n fieldState = \"result\";\n continue;\n }\n if (type === \"end\") {\n flushField();\n continue;\n }\n }\n if (instrText && fieldState === \"code\") {\n fieldInstr += instrText.textContent ?? \"\";\n continue;\n }\n if (fieldState === \"result\") {\n // Swallow cached display text — the renderer substitutes the\n // live value per page from `field.instruction`.\n const t = wFirst(child, \"t\");\n if (t) fieldCached += t.textContent ?? \"\";\n continue;\n }\n if (fieldState === \"code\") {\n // Inside the instruction zone — instructions can split across\n // runs (Word sometimes adds a stray empty run). Skip non-\n // instrText runs to keep the field together.\n continue;\n }\n const run = readRun(child);\n if (revision) run.revision = revision;\n if (activeComments.size > 0) run.commentIds = Array.from(activeComments);\n out.push({ kind: \"run\", run });\n } else if (child.localName === \"hyperlink\") {\n const relId =\n child.getAttributeNS(NS.r, \"id\") ?? child.getAttribute(\"r:id\") ?? undefined;\n const runs = wChildren(child, \"r\").map((r) => {\n const run = readRun(r);\n if (revision) run.revision = revision;\n if (activeComments.size > 0) run.commentIds = Array.from(activeComments);\n return run;\n });\n out.push({ kind: \"hyperlink\", ...(relId ? { relId } : {}), runs });\n } else if (child.localName === \"ins\" || child.localName === \"del\") {\n const nextRevision: ImportedRun[\"revision\"] = {\n type: child.localName === \"ins\" ? \"ins\" : \"del\",\n ...readRevisionAttrs(child),\n };\n collectParagraphChildren(child, out, nextRevision, activeComments);\n } else if (child.localName === \"commentRangeStart\") {\n const id = readCommentId(child);\n if (id !== null) activeComments.add(id);\n } else if (child.localName === \"commentRangeEnd\") {\n const id = readCommentId(child);\n if (id !== null) activeComments.delete(id);\n } else if (child.localName === \"fldSimple\") {\n // Simple field — `<w:fldSimple w:instr=\"PAGE\"><w:r><w:t>1</w:t></w:r></w:fldSimple>`\n // Emit one `ImportedRun` with `field` set so the paragraph\n // converter produces a `FieldRun`. Used by headers/footers to\n // carry page-number tokens through round-trip.\n const instr =\n child.getAttributeNS(NS.w, \"instr\") ??\n child.getAttribute(\"w:instr\") ??\n \"\";\n const innerR = wFirst(child, \"r\");\n const cachedText = innerR ? wFirst(innerR, \"t\")?.textContent ?? \"\" : \"\";\n const run: ImportedRun = {\n text: \"\",\n format: {},\n isHardBreak: false,\n field: cachedText !== \"\"\n ? { instruction: instr, cached: cachedText }\n : { instruction: instr },\n };\n if (revision) run.revision = revision;\n if (activeComments.size > 0) run.commentIds = Array.from(activeComments);\n out.push({ kind: \"run\", run });\n }\n // pPr / commentReference (the balloon-icon run) handled by the\n // caller or silently dropped — the highlighted range carries the\n // visual signal, so the reference glyph is redundant.\n }\n}\n\nfunction readCommentId(el: Element): number | null {\n const raw = el.getAttributeNS(NS.w, \"id\") ?? el.getAttribute(\"w:id\");\n if (raw === null) return null;\n const n = Number(raw);\n return Number.isFinite(n) ? n : null;\n}\n\n/**\n * Read just the font properties (family + size) from a paragraph-mark\n * `<w:rPr>`. These apply to the invisible paragraph mark glyph and\n * are what Word uses to size empty paragraphs — if the paragraph has\n * no inline runs, the mark's font drives the line height.\n */\nfunction readMarkRunFormat(rPr: Element): { fontFamily?: string; fontSizePt?: number } {\n const out: { fontFamily?: string; fontSizePt?: number } = {};\n const rFonts = wFirst(rPr, \"rFonts\");\n if (rFonts) {\n const font =\n rFonts.getAttributeNS(NS.w, \"ascii\") ??\n rFonts.getAttribute(\"w:ascii\") ??\n rFonts.getAttributeNS(NS.w, \"hAnsi\") ??\n rFonts.getAttribute(\"w:hAnsi\");\n if (font) out.fontFamily = font;\n }\n const sz = wVal(wFirst(rPr, \"sz\"));\n if (sz) {\n const pt = halfPtToPt(Number(sz));\n if (Number.isFinite(pt) && pt > 0) out.fontSizePt = pt;\n }\n return out;\n}\n\nfunction readRevisionAttrs(el: Element): { author?: string; date?: string } {\n const out: { author?: string; date?: string } = {};\n const author = el.getAttributeNS(NS.w, \"author\") ?? el.getAttribute(\"w:author\");\n if (author) out.author = author;\n const date = el.getAttributeNS(NS.w, \"date\") ?? el.getAttribute(\"w:date\");\n if (date) out.date = date;\n return out;\n}\n\nfunction readParagraphFormat(pPr: Element): ParagraphFormat {\n const format: ParagraphFormat = {};\n\n const styleVal = wVal(wFirst(pPr, \"pStyle\"));\n if (styleVal) {\n // Word's canonical heading style names are `Heading1` ... `Heading6`.\n // Also tolerate `heading 1` (OpenOffice exports) and `Title` (treat as H1).\n const m = styleVal.match(/^heading\\s*([1-6])$/i);\n if (m) format.headingLevel = Number(m[1]);\n else if (styleVal.toLowerCase() === \"title\") format.headingLevel = 1;\n // Always carry the raw styleId through. The renderer uses it as\n // the cascade anchor — `ListParagraph` / `BodyText` / etc. style\n // pPr / rPr would otherwise be silently dropped.\n format.styleId = styleVal;\n }\n\n const jcVal = wVal(wFirst(pPr, \"jc\"));\n if (jcVal) {\n if (jcVal === \"left\" || jcVal === \"start\") format.alignment = \"left\";\n else if (jcVal === \"right\" || jcVal === \"end\") format.alignment = \"right\";\n else if (jcVal === \"center\") format.alignment = \"center\";\n else if (jcVal === \"both\" || jcVal === \"distribute\") format.alignment = \"justify\";\n }\n\n const spacing = wFirst(pPr, \"spacing\");\n if (spacing) {\n const line = spacing.getAttributeNS(spacing.namespaceURI, \"line\") ?? spacing.getAttribute(\"w:line\");\n const rule =\n spacing.getAttributeNS(spacing.namespaceURI, \"lineRule\") ??\n spacing.getAttribute(\"w:lineRule\");\n if (line && (!rule || rule === \"auto\")) {\n const n = Number(line);\n if (Number.isFinite(n) && n > 0) format.lineHeight = ooxmlLineHeightToCss(n);\n }\n // `<w:spacing w:before w:after>` — twips of inter-paragraph space.\n // Critical to import: if Word sees a paragraph with explicit\n // `after=\"0\"`, that ZERO must override any DocDefaults-derived\n // afterTwips (200 by default in Word). Without this, the style\n // cascade leaks the DocDefaults' after-space onto every paragraph\n // (jellap.docx form rows would each gain ~13px of bottom margin\n // they didn't ask for, blowing up vertical spacing and\n // pagination). We store the values on `pSpacingBefore` /\n // `pSpacingAfter` so the AST keeps them explicit (including 0).\n const after = spacing.getAttributeNS(spacing.namespaceURI, \"after\") ?? spacing.getAttribute(\"w:after\");\n const before = spacing.getAttributeNS(spacing.namespaceURI, \"before\") ?? spacing.getAttribute(\"w:before\");\n if (after !== null) {\n const n = Number(after);\n if (Number.isFinite(n)) format.spacingAfterTwips = n;\n }\n if (before !== null) {\n const n = Number(before);\n if (Number.isFinite(n)) format.spacingBeforeTwips = n;\n }\n }\n\n const numPr = wFirst(pPr, \"numPr\");\n if (numPr) {\n const numId = wVal(wFirst(numPr, \"numId\"));\n const ilvl = wVal(wFirst(numPr, \"ilvl\"));\n if (numId !== null) format.numId = Number(numId);\n if (ilvl !== null) format.numLevel = Number(ilvl);\n }\n\n // <w:tabs><w:tab w:val=\"left\" w:pos=\"N\"/>…</w:tabs>\n // Custom tab stops in twips, defined per paragraph. Their positions\n // determine where each `<w:tab/>` advances the cursor — critical for\n // Word headers like jellap.docx where \"Cím:\" + tab lands at column\n // ~30pt for the value. We store the raw stops on the AST; the\n // renderer translates them into CSS (tab-size on the paragraph for\n // even spacing, or absolute positions via inline-block stops for\n // mixed layouts).\n const tabsEl = wFirst(pPr, \"tabs\");\n if (tabsEl) {\n const stops: { positionTwips: number; alignment: string; leader?: string }[] = [];\n for (const tab of Array.from(tabsEl.children)) {\n if (tab.namespaceURI !== tabsEl.namespaceURI || tab.localName !== \"tab\") continue;\n const posAttr =\n tab.getAttributeNS(tab.namespaceURI, \"pos\") ?? tab.getAttribute(\"w:pos\");\n const valAttr =\n tab.getAttributeNS(tab.namespaceURI, \"val\") ?? tab.getAttribute(\"w:val\");\n const leaderAttr =\n tab.getAttributeNS(tab.namespaceURI, \"leader\") ?? tab.getAttribute(\"w:leader\");\n if (posAttr === null) continue;\n const pos = Number(posAttr);\n if (!Number.isFinite(pos)) continue;\n stops.push({\n positionTwips: pos,\n alignment: valAttr ?? \"left\",\n ...(leaderAttr ? { leader: leaderAttr } : {}),\n });\n }\n if (stops.length > 0) format.tabStops = stops;\n }\n\n // <w:ind w:left=\"...\" w:right=\"...\" w:firstLine=\"...\" w:hanging=\"...\"/>\n // Paragraph indentation in twips (1/1440 inch). The renderer applies\n // this via `margin-left`/`margin-right`/`text-indent`. Critical for\n // headers like jellap.docx where the logo paragraph uses `w:left=5812`\n // (~10cm) to push the inline image to the right column, leaving the\n // left half free for an absolutely-positioned text-box of contact info.\n const indEl = wFirst(pPr, \"ind\");\n if (indEl) {\n const readTwipAttr = (name: string): number | undefined => {\n const raw =\n indEl.getAttributeNS(indEl.namespaceURI, name) ??\n indEl.getAttribute(`w:${name}`);\n if (raw === null) return undefined;\n const n = Number(raw);\n return Number.isFinite(n) ? n : undefined;\n };\n const left = readTwipAttr(\"left\") ?? readTwipAttr(\"start\");\n const right = readTwipAttr(\"right\") ?? readTwipAttr(\"end\");\n const firstLine = readTwipAttr(\"firstLine\");\n const hanging = readTwipAttr(\"hanging\");\n if (\n left !== undefined ||\n right !== undefined ||\n firstLine !== undefined ||\n hanging !== undefined\n ) {\n format.indent = {\n ...(left !== undefined ? { leftTwips: left } : {}),\n ...(right !== undefined ? { rightTwips: right } : {}),\n ...(firstLine !== undefined ? { firstLineTwips: firstLine } : {}),\n ...(hanging !== undefined ? { hangingTwips: hanging } : {}),\n };\n }\n }\n\n // <w:pBdr> — paragraph borders (top / left / bottom / right /\n // between). Each child element specifies style (single / dashed /\n // dotted / double / …), size (in eighths of a point), color, and\n // optional space (in twips). Word uses these for inline rules like\n // a dotted divider under a header line. Without import, the divider\n // never reaches the AST and the renderer's existing\n // `effective.borders.bottom` branch fires only off the style\n // cascade (which doesn't have the divider).\n const pBdrEl = wFirst(pPr, \"pBdr\");\n if (pBdrEl) {\n const borders: ParagraphFormat[\"borders\"] = {};\n for (const side of [\"top\", \"bottom\", \"left\", \"right\", \"between\"] as const) {\n const child = wFirst(pBdrEl, side);\n if (!child) continue;\n const val = child.getAttribute(\"w:val\") ?? \"single\";\n if (val === \"none\" || val === \"nil\") continue;\n const sz = child.getAttribute(\"w:sz\");\n const color = child.getAttribute(\"w:color\");\n const space = child.getAttribute(\"w:space\");\n const styleMap: Record<string, \"single\" | \"double\" | \"dashed\" | \"dotted\" | \"thick\" | \"none\"> = {\n single: \"single\", double: \"double\", dashed: \"dashed\",\n dotted: \"dotted\", thick: \"thick\", dotDash: \"dashed\", dashDot: \"dashed\",\n };\n borders[side] = {\n style: styleMap[val] ?? \"single\",\n sizeEighthsOfPt: sz ? Number(sz) : 4,\n color: color && color !== \"auto\" ? `#${color}` : \"auto\",\n ...(space ? { spaceTwips: Number(space) } : {}),\n };\n }\n if (Object.keys(borders).length > 0) format.borders = borders;\n }\n\n // <w:shd w:val=\"clear\" w:fill=\"…\"/> — paragraph background colour.\n const shading = readShading(pPr);\n if (shading) format.shading = shading;\n\n // Paragraph-mark properties — `<w:pPr><w:rPr>...</w:rPr></w:pPr>`.\n // Carries (a) revision marker for paragraph-break tracking, and\n // (b) font / size / colour defaults that apply to the invisible\n // paragraph-mark AND to empty paragraphs (no inline runs to\n // override). Without (b), empty paragraphs fall back to the browser\n // default 16px font and balloon every form's vertical spacing\n // (jellap.docx has many empty 9pt paragraphs that need to render\n // at 9pt-derived line-height, not 16px-derived).\n const pPr_rPr = wFirst(pPr, \"rPr\");\n if (pPr_rPr) {\n // Cache the paragraph-mark run properties so the renderer can\n // size empty paragraphs / their paragraph mark correctly. We\n // re-use readRunFormat semantics by inlining a minimal read\n // (just font / size — the only properties that affect height).\n const markFormat = readMarkRunFormat(pPr_rPr);\n if (markFormat.fontFamily || markFormat.fontSizePt !== undefined) {\n format.markFormat = markFormat;\n }\n const insEl = wFirst(pPr_rPr, \"ins\");\n const delEl = wFirst(pPr_rPr, \"del\");\n const revEl = insEl ?? delEl;\n if (revEl) {\n const type: \"ins\" | \"del\" = insEl ? \"ins\" : \"del\";\n const author =\n revEl.getAttributeNS(revEl.namespaceURI, \"author\") ??\n revEl.getAttribute(\"w:author\") ??\n undefined;\n const date =\n revEl.getAttributeNS(revEl.namespaceURI, \"date\") ??\n revEl.getAttribute(\"w:date\") ??\n undefined;\n format.revision = {\n type,\n ...(author !== undefined ? { author } : {}),\n ...(date !== undefined ? { date } : {}),\n };\n }\n }\n\n return format;\n}\n","import { type ImportedItem, readParagraph } from \"./paragraphs\";\nimport type { ImportedRun } from \"./runs\";\nimport type {\n DrawingRun,\n HyperlinkRun,\n InlineRun,\n Paragraph,\n ParagraphProperties,\n RunProperties,\n TextRun,\n} from \"../../doc/types\";\nimport type { ParagraphFormat, RunFormat } from \"../types\";\n\n/**\n * Shared context for importing a body — rels + media lookup. Lives here\n * (rather than in `document.ts`) so `tables.ts` can pull it without\n * forming a `document.ts` ↔ `tables.ts` import cycle.\n */\nexport interface ConvertContext {\n /** Rels map (`rId` → target path). Used for image embed resolution. */\n rels: Map<string, string>;\n /** When true, `<w:lastRenderedPageBreak/>` markers are honoured as\n * forced page breaks (i.e. translated to `pageBreakBefore: true`).\n * Caller sets this after counting hints per document: a meaningful\n * number of hints (≥3) indicates Word's layout produced reliable\n * page boundaries; a stray single hint is usually stale and\n * ignored. */\n honorLastRenderedPageBreaks?: boolean;\n}\n\n/**\n * Convert a single `<w:p>` element into a Paragraph block. Handles\n * paragraph formatting (heading style, alignment, spacing, numbering),\n * runs (text/hyperlink/drawing), and image embed resolution via the\n * rels map carried in `ctx`.\n */\nexport function convertParagraph(\n p: Element,\n ctx: ConvertContext,\n activeComments?: Set<number>,\n): Paragraph {\n const { items, format } = readParagraph(p, activeComments);\n const properties = mapParagraphFormat(format);\n const inlineRuns = itemsToInlines(items, ctx);\n const honorLastRenderedPageBreaks = ctx.honorLastRenderedPageBreaks === true;\n // `<w:lastRenderedPageBreak/>` is a hint Word writes during save —\n // \"the layout engine broke a page here last time we rendered\". LO\n // honours it as a soft page boundary so a doc round-tripping\n // through LO retains the Word author's visual pagination.\n //\n // Caveat: an isolated single hint is often stale (the author\n // edited / resized after Word's last layout pass and the hint\n // doesn't match where LO actually breaks now). We only honour\n // hints when the importer's caller signals \"this doc has enough\n // hints to be reliable\" — see `shouldHonorLastRenderedPageBreaks`\n // in `index.ts` for the threshold. Without that gate, jellap.docx's\n // single stale hint added an unwanted 4th page; complex-multipage's\n // 10 hints correctly produce its 16 pages.\n if (honorLastRenderedPageBreaks && hasLastRenderedPageBreak(p)) {\n properties.pageBreakBefore = true;\n }\n return { kind: \"paragraph\", properties, runs: inlineRuns };\n}\n\nfunction hasLastRenderedPageBreak(p: Element): boolean {\n // Walk descendants (NOT just direct children) — the marker is\n // nested inside a `<w:r>`. We don't recurse into `<w:txbxContent>`\n // (text-box content); textbox breaks shouldn't trigger the outer\n // paragraph's break.\n const stack: Element[] = [p];\n while (stack.length > 0) {\n const el = stack.pop()!;\n for (const child of Array.from(el.children)) {\n if (child.localName === \"txbxContent\") continue;\n if (\n child.localName === \"lastRenderedPageBreak\"\n && child.namespaceURI?.includes(\"wordprocessingml\")\n ) {\n return true;\n }\n stack.push(child);\n }\n }\n return false;\n}\n\nfunction mapParagraphFormat(f: ParagraphFormat): ParagraphProperties {\n const out: ParagraphProperties = {};\n // Style anchor:\n // - heading → canonical `HeadingN` id (overrides whatever Word\n // called it — `heading 1`, `Heading1`, `Title`, etc.)\n // - otherwise → verbatim pStyle (e.g. `ListParagraph`, `BodyText`)\n // so the renderer's cascade picks up the style's pPr/rPr.\n if (f.headingLevel && f.headingLevel >= 1 && f.headingLevel <= 6) {\n out.styleId = `Heading${f.headingLevel}`;\n } else if (f.styleId) {\n out.styleId = f.styleId;\n }\n if (f.alignment) {\n out.alignment = f.alignment === \"justify\" ? \"both\" : f.alignment;\n }\n // Build spacing from line / after / before. Even when only one of\n // them is set, we emit the object so the AST distinguishes \"no\n // override\" (undefined `spacing`) from \"explicit value, possibly 0\"\n // (e.g. `spacingAfterTwips: 0` must beat DocDefaults' 200).\n const spacing: NonNullable<ParagraphProperties[\"spacing\"]> = {};\n if (f.lineHeight !== undefined && f.lineHeight > 0) {\n spacing.line = Math.round(240 * f.lineHeight);\n spacing.lineRule = \"auto\";\n }\n if (f.spacingAfterTwips !== undefined) spacing.afterTwips = f.spacingAfterTwips;\n if (f.spacingBeforeTwips !== undefined) spacing.beforeTwips = f.spacingBeforeTwips;\n if (Object.keys(spacing).length > 0) out.spacing = spacing;\n if (f.numId !== undefined) {\n out.numbering = { numId: f.numId, level: f.numLevel ?? 0 };\n }\n if (f.indent) out.indent = f.indent;\n if (f.tabStops) out.tabStops = f.tabStops;\n if (f.shading) out.shading = f.shading;\n if (f.revision) out.revision = f.revision;\n if (f.borders) out.borders = f.borders;\n // Mark-format → ParagraphProperties.runDefaults. Renderer cascades\n // these onto the paragraph element, so empty paragraphs render at\n // the paragraph mark's font (not the browser default).\n if (f.markFormat && (f.markFormat.fontFamily || f.markFormat.fontSizePt !== undefined)) {\n const runDefaults: RunProperties = { ...(out.runDefaults ?? {}) };\n if (f.markFormat.fontFamily) runDefaults.fontFamily = f.markFormat.fontFamily;\n if (f.markFormat.fontSizePt !== undefined) runDefaults.fontSizePt = f.markFormat.fontSizePt;\n out.runDefaults = runDefaults;\n }\n return out;\n}\n\nfunction itemsToInlines(items: ImportedItem[], ctx: ConvertContext): InlineRun[] {\n const out: InlineRun[] = [];\n for (const item of items) {\n if (item.kind === \"run\") {\n pushInline(item.run, ctx, out);\n } else {\n const inner: InlineRun[] = [];\n for (const run of item.runs) pushInline(run, ctx, inner);\n const href = item.relId ? ctx.rels.get(item.relId) : undefined;\n const link: HyperlinkRun = {\n kind: \"hyperlink\",\n href: href ?? \"#\",\n children: inner,\n };\n out.push(link);\n }\n }\n return out;\n}\n\nfunction pushInline(run: ImportedRun, ctx: ConvertContext, out: InlineRun[]): void {\n if (run.drawing) {\n const drawing = drawingRunFrom(run.drawing, ctx);\n if (drawing) out.push(drawing);\n return;\n }\n if (run.footnoteRefId !== undefined) {\n out.push({ kind: \"footnoteRef\", id: run.footnoteRefId });\n return;\n }\n if (run.commentRefId !== undefined) {\n out.push({ kind: \"commentRef\", id: run.commentRefId });\n return;\n }\n if (run.isHardBreak) {\n out.push({ kind: \"break\", type: run.breakType ?? \"line\" });\n return;\n }\n if (run.field) {\n // FieldRun — Sobree's AST shape mirrors `<w:fldSimple>`:\n // instruction (`PAGE` / `NUMPAGES` / …) plus an optional cached\n // value Word writes for viewers that don't re-evaluate.\n const fieldRun: InlineRun =\n run.field.cached !== undefined\n ? { kind: \"field\", instruction: run.field.instruction, cached: run.field.cached }\n : { kind: \"field\", instruction: run.field.instruction };\n out.push(fieldRun);\n return;\n }\n if (run.text === \"\") return;\n const properties = mapRunFormat(run.format);\n if (run.revision) properties.revision = run.revision;\n if (run.commentIds && run.commentIds.length > 0) properties.commentIds = run.commentIds;\n out.push(makeTextRun(run.text, properties));\n}\n\nfunction drawingRunFrom(\n info: NonNullable<ImportedRun[\"drawing\"]>,\n ctx: ConvertContext,\n): DrawingRun | null {\n if (!info.embedRelId) return null;\n const target = ctx.rels.get(info.embedRelId);\n if (!target) return null;\n const partPath = resolveMediaPath(target);\n const run: DrawingRun = {\n kind: \"drawing\",\n partPath,\n widthEmu: info.widthEmu ?? 0,\n heightEmu: info.heightEmu ?? 0,\n placement: info.anchor ? \"anchor\" : \"inline\",\n };\n if (info.altText) run.altText = info.altText;\n if (info.anchor) {\n run.anchor = {\n offsetXEmu: info.anchor.offsetXEmu,\n offsetYEmu: info.anchor.offsetYEmu,\n relativeFromH: info.anchor.relativeFromH,\n relativeFromV: info.anchor.relativeFromV,\n ...(info.anchor.behindDoc ? { behindDoc: true } : {}),\n };\n }\n return run;\n}\n\nfunction resolveMediaPath(target: string): string {\n if (target.startsWith(\"/\")) return target.slice(1);\n return `word/${target}`;\n}\n\nfunction makeTextRun(text: string, properties: RunProperties): TextRun {\n return { kind: \"text\", text, properties };\n}\n\nfunction mapRunFormat(f: RunFormat): RunProperties {\n const out: RunProperties = {};\n if (f.bold) out.bold = true;\n if (f.italic) out.italic = true;\n if (f.strike) out.strike = true;\n // Threads `<w:caps/>` from the RunFormat to the AST so per-run caps\n // (not just style-cascade caps) reaches the renderer's\n // `text-transform: uppercase`.\n if (f.caps) out.caps = true;\n if (f.underline) out.underline = \"single\";\n if (f.color) out.color = f.color.startsWith(\"#\") ? f.color : `#${f.color}`;\n if (f.highlight) out.highlight = f.highlight;\n if (f.fontFamily) out.fontFamily = f.fontFamily;\n if (f.fontSizePt !== undefined) out.fontSizePt = f.fontSizePt;\n if (f.verticalAlign) out.verticalAlign = f.verticalAlign;\n // `<w:rPrChange>` snapshot — recursively map the before-state through\n // the same RunFormat → RunProperties conversion.\n if (f.revisionFormat) {\n const before = mapRunFormat(f.revisionFormat.before);\n const { author, date } = f.revisionFormat;\n out.revisionFormat = {\n before,\n ...(author !== undefined ? { author } : {}),\n ...(date !== undefined ? { date } : {}),\n };\n }\n return out;\n}\n","import { type ConvertContext, convertParagraph } from \"./paragraph\";\nimport { wChildren, wFirst, wVal } from \"../shared/xml\";\nimport { readShading } from \"../shared/shading\";\nimport type { Block, Table, TableCell, TableRow } from \"../../doc/types\";\n\n/**\n * Convert a `<w:tbl>` element into a native Table block. Handles:\n * - Column widths from `<w:tblGrid>` (twips).\n * - Horizontal merges via `<w:gridSpan>`.\n * - Vertical merges via `<w:vMerge>` (restart / continue).\n * - Header rows flagged with `<w:tblHeader/>` in `<w:trPr>`.\n * - Cell vertical alignment via `<w:vAlign>`.\n * - Cell content as a sequence of Blocks (paragraphs, nested tables).\n */\nexport function convertTable(tbl: Element, ctx: ConvertContext): Table {\n const grid = readGrid(tbl);\n const rows = wChildren(tbl, \"tr\").map((tr) => readRow(tr, ctx));\n const properties: Table[\"properties\"] = {};\n const tblPr = wFirst(tbl, \"tblPr\");\n if (tblPr) {\n const styleId = wVal(wFirst(tblPr, \"tblStyle\"));\n if (styleId) properties.styleId = styleId;\n const tblBorders = wFirst(tblPr, \"tblBorders\");\n if (tblBorders) {\n // Always carry a borders object when the source declared\n // `<w:tblBorders>`, even if every side was `w:val=\"none\"`. An\n // empty `borders: {}` signals \"the doc explicitly said no\n // borders\" so the renderer's TableGrid auto-border heuristic\n // doesn't add stale borders back. Without this distinction,\n // user-contract's name-fields table (TableGrid + explicit none-\n // borders) rendered with the TableGrid default border while LO\n // honoured the override.\n properties.borders = readTableBorders(tblBorders) ?? {};\n }\n }\n return {\n kind: \"table\",\n grid,\n rows,\n properties,\n };\n}\n\n/**\n * Read `<w:tblBorders>` into a TableBorders. Each side child element\n * (`top`, `left`, `right`, `bottom`, `insideH`, `insideV`) is a\n * `BorderSpec` — `style` (single/double/dashed/…), `colorHex` (or\n * \"auto\"), `widthEighthsPt` (1/8 of a point).\n */\nfunction readTableBorders(el: Element): NonNullable<Table[\"properties\"][\"borders\"]> | null {\n const out: NonNullable<Table[\"properties\"][\"borders\"]> = {};\n for (const side of [\"top\", \"left\", \"right\", \"bottom\", \"insideH\", \"insideV\"] as const) {\n const child = wFirst(el, side);\n if (!child) continue;\n const val = child.getAttribute(\"w:val\") ?? \"single\";\n if (val === \"none\" || val === \"nil\") continue;\n const sz = child.getAttribute(\"w:sz\");\n const color = child.getAttribute(\"w:color\");\n const style = (val === \"single\" || val === \"double\" || val === \"dashed\"\n || val === \"dotted\" || val === \"thick\" || val === \"none\") ? val : \"single\";\n out[side] = {\n style,\n sizeEighthsOfPt: sz ? Number(sz) : 4,\n color: color && color !== \"auto\" ? `#${color}` : \"auto\",\n };\n }\n return Object.keys(out).length > 0 ? out : null;\n}\n\nfunction readGrid(tbl: Element): number[] {\n const gridEl = wFirst(tbl, \"tblGrid\");\n if (!gridEl) return [];\n const cols = wChildren(gridEl, \"gridCol\");\n return cols.map((c) => {\n const raw = c.getAttribute(\"w:w\") ?? \"2400\";\n const n = Number(raw);\n return Number.isFinite(n) && n > 0 ? n : 2400;\n });\n}\n\nfunction readRow(tr: Element, ctx: ConvertContext): TableRow {\n const trPr = wFirst(tr, \"trPr\");\n const isHeader = trPr ? wFirst(trPr, \"tblHeader\") !== null : false;\n const cells = wChildren(tr, \"tc\").map((tc) => readCell(tc, ctx));\n const row: TableRow = { cells };\n if (isHeader) row.isHeader = true;\n return row;\n}\n\nfunction readCell(tc: Element, ctx: ConvertContext): TableCell {\n const tcPr = wFirst(tc, \"tcPr\");\n const cell: TableCell = { content: [] };\n\n if (tcPr) {\n const gridSpanEl = wFirst(tcPr, \"gridSpan\");\n const gridSpan = wVal(gridSpanEl);\n if (gridSpan) {\n const n = Number(gridSpan);\n if (Number.isFinite(n) && n > 1) cell.gridSpan = n;\n }\n const vMergeEl = wFirst(tcPr, \"vMerge\");\n if (vMergeEl) {\n // `<w:vMerge/>` with no val means \"continue\". `w:val=\"restart\"` begins\n // a new merge region.\n const val = wVal(vMergeEl);\n cell.vMerge = val === \"restart\" ? \"restart\" : \"continue\";\n }\n const vAlignVal = wVal(wFirst(tcPr, \"vAlign\"));\n if (vAlignVal === \"top\" || vAlignVal === \"center\" || vAlignVal === \"bottom\") {\n cell.verticalAlign = vAlignVal;\n }\n // <w:shd w:val=\"clear\" w:fill=\"C6EFCE\"/> — cell background colour.\n const cellShading = readShading(tcPr);\n if (cellShading) cell.shading = cellShading;\n }\n\n // Cell content: every direct child `<w:p>` or `<w:tbl>` becomes a Block.\n for (const child of Array.from(tc.children)) {\n if (child.namespaceURI === null) continue;\n if (child.localName === \"p\") {\n cell.content.push(convertParagraph(child, ctx));\n } else if (child.localName === \"tbl\") {\n cell.content.push(convertTable(child, ctx));\n }\n // pPr/tcPr/etc. are handled above; other elements are dropped.\n }\n\n // Word requires at least one paragraph per cell. If Phase N2 flattened\n // something weird and left us empty, supply a blank paragraph so the\n // cell renders with a caret target.\n if (cell.content.length === 0) {\n const empty: Block = { kind: \"paragraph\", properties: {}, runs: [] };\n cell.content.push(empty);\n }\n\n return cell;\n}\n","import { type ConvertContext, convertParagraph } from \"./paragraph\";\nimport { convertTable } from \"./tables\";\nimport { wFirst } from \"../shared/xml\";\nimport type { Block } from \"../../doc/types\";\n\n// Re-export so existing callers (and the import barrel) keep their\n// `import { ConvertContext, convertParagraph } from \"./document\"` paths.\n// New code should import from `./paragraph` directly.\nexport { type ConvertContext, convertParagraph } from \"./paragraph\";\n\nexport interface DocumentImport {\n body: Block[];\n warnings: string[];\n /**\n * `<w:sectPr>` elements collected in document order, for the import\n * pipeline to convert into `SectionProperties[]`. Includes both\n * inline (paragraph-pPr) and body-level sectPrs.\n *\n * Length equals the number of sections in the resulting document.\n * Inline sectPrs end non-final sections; the body-level one (always\n * last) is the document-final section.\n */\n sectPrEls: Element[];\n}\n\n/**\n * Convert a parsed `word/document.xml` into the SobreeDocument body — a\n * flat list of `Block`s (Paragraphs, Tables, SectionBreaks).\n *\n * Multi-section detection: any paragraph whose `<w:pPr>` carries an\n * inline `<w:sectPr>` is the last paragraph of a non-final section.\n * The walker emits a `SectionBreak` block immediately after such a\n * paragraph, and stashes the sectPr Element for the import pipeline\n * to convert into `SectionProperties`. The body-level `<w:sectPr>` is\n * stashed last as the document-final section's properties.\n */\n/**\n * Optional per-paragraph block replacements. When the body walker\n * encounters a `<w:p>` element that's a key in `replaceParagraphs`,\n * it emits the mapped Block *instead of* calling `convertParagraph`\n * on it (and does NOT consume the paragraph's text content as a\n * Paragraph block).\n *\n * Used by the `InlineFrame` import path to swap out section-heading\n * paragraphs for first-class `InlineFrame` blocks at their original\n * document-order position — without resorting to DOM-attribute\n * markers or a post-walk splice. The contract is a typed map; the\n * caller owns key identity.\n */\nexport interface ConvertOptions {\n replaceParagraphs?: Map<Element, Block>;\n}\n\nexport function convertDocumentXml(\n xmlDoc: Document,\n ctx: ConvertContext,\n opts?: ConvertOptions,\n): DocumentImport {\n const body = wFirst(xmlDoc, \"body\");\n if (!body) return { body: [], warnings: [\"document.xml has no <w:body>\"], sectPrEls: [] };\n // `<w:lastRenderedPageBreak/>` is a HINT Word writes during save\n // — a record of where Word's layout engine broke pages last time.\n // ECMA-376 says consumers SHOULD ignore it for layout. But ignoring\n // it on heavily-decorated docs (complex-multipage's 21 hints)\n // packs Sobree pages 3-into-1, so per-element y positions drift\n // 300-400pt vs LO's reference. Honoring it preserves LO's page\n // layout exactly but fragments pages when our paginator's section-\n // frame heights differ from LO's.\n //\n // Compromise: honor the hint when there are MANY (≥10) — that's\n // a strong signal the source was rendered+saved by Word with\n // meaningful pagination decisions. For low-hint docs (≤3, the\n // jellap.docx case) ignore the hint entirely. The middle range\n // is a tossup; bias toward honoring since over-pagination is\n // visually less wrong than 300pt content drift.\n const hintCount = body.getElementsByTagNameNS(\n \"http://schemas.openxmlformats.org/wordprocessingml/2006/main\",\n \"lastRenderedPageBreak\",\n ).length;\n const enrichedCtx: ConvertContext = {\n ...ctx,\n honorLastRenderedPageBreaks: hintCount >= 10,\n };\n return convertBlocksFromContainer(body, enrichedCtx, opts);\n}\n\n/**\n * Walk a container element (`<w:body>` for `document.xml`, `<w:hdr>` /\n * `<w:ftr>` for header/footer parts) and turn its direct paragraph +\n * table children into `Block[]`. Extracted from `convertDocumentXml`\n * so header/footer parts get the same rich-content import — drawings,\n * comment ranges, revisions, formatted runs — instead of being\n * collapsed to flat text by `flattenZone`.\n *\n * Header / footer parts never carry inline `<w:sectPr>` elements, so\n * for those the returned `sectPrEls` is always empty.\n */\nexport function convertBlocksFromContainer(\n container: Element,\n ctx: ConvertContext,\n opts?: ConvertOptions,\n): DocumentImport {\n const warnings: string[] = [];\n const blocks: Block[] = [];\n const sectPrEls: Element[] = [];\n let pendingSectionIndex = 0;\n // One shared active-comments set across the whole walk. A comment\n // whose `<w:commentRangeStart>` opens in para N and\n // `<w:commentRangeEnd>` closes in para M>N keeps the intermediate\n // paragraphs' runs tagged. Tables build their own fresh sets\n // (inside `convertTable`) so a body-level range doesn't leak into\n // an unrelated cell.\n const activeComments = new Set<number>();\n\n // Flatten any `<w:sdt>` wrappers (Structured Document Tags / content\n // controls) into the host's direct child list. Word uses SDTs to\n // wrap repeating sections, gallery placeholders, dropdowns, and\n // template-named blocks (e.g. \"Resume Name\"). Their visual\n // semantics are identical to the wrapped content for rendering;\n // they only matter when the user interacts with them as a form\n // control. Without flattening, the importer silently dropped\n // everything inside — observed on healthcare-with-photo.docx where\n // the entire \"Peter Burkimsher\" name + email + photo banner table\n // was wrapped in an SDT and lost.\n const directChildren = expandSdtWrappers(Array.from(container.children));\n\n const replaceParagraphs = opts?.replaceParagraphs;\n\n for (const child of directChildren) {\n if (child.namespaceURI === null) continue;\n const name = child.localName;\n if (name === \"p\") {\n const replacement = replaceParagraphs?.get(child);\n if (replacement) {\n // Caller pre-built a Block to take this paragraph's place\n // (e.g. an InlineFrame derived from the drawing the\n // paragraph contained). Emit that block instead; do NOT\n // call convertParagraph on the source element so the\n // original text/runs don't double-render.\n blocks.push(replacement);\n } else {\n blocks.push(convertParagraph(child, ctx, activeComments));\n }\n const inlineSectPr = inlineSectPrOf(child);\n if (inlineSectPr) {\n // The just-pushed paragraph is the last one of its section.\n // Emit a SectionBreak so the AST mirrors OOXML's structural\n // boundary, and stash the sectPr to be parsed alongside.\n sectPrEls.push(inlineSectPr);\n pendingSectionIndex++;\n blocks.push({ kind: \"section_break\", toSectionIndex: pendingSectionIndex });\n }\n } else if (name === \"tbl\") {\n blocks.push(convertTable(child, ctx));\n } else if (name === \"sectPr\") {\n // Body-level sectPr — the document-final section.\n sectPrEls.push(child);\n } else {\n // Unknown / unhandled elements dropped silently.\n }\n }\n return { body: blocks, warnings, sectPrEls };\n}\n\n/**\n * Expand `<w:sdt>` wrappers in-place: replace each SDT with the\n * children of its `<w:sdtContent>`. Non-SDT children pass through\n * unchanged. Nested SDTs (an SDT containing another SDT) recursively\n * unwrap — Quick Parts / placeholder galleries can nest several\n * levels deep.\n *\n * SDT has the shape:\n * <w:sdt>\n * <w:sdtPr>...properties (alias, tag, placeholder docPart)...</w:sdtPr>\n * <w:sdtEndPr>...</w:sdtEndPr>\n * <w:sdtContent>...the actual paragraphs / tables / runs...</w:sdtContent>\n * </w:sdt>\n * Only `<w:sdtContent>` matters for rendering; the metadata children\n * drive form-control behaviour that Sobree doesn't yet surface.\n */\nfunction expandSdtWrappers(children: readonly Element[]): Element[] {\n const out: Element[] = [];\n for (const child of children) {\n if (child.namespaceURI !== null && child.localName === \"sdt\") {\n const content = wFirst(child, \"sdtContent\");\n if (content) {\n // Recurse so nested SDTs unwrap too.\n out.push(...expandSdtWrappers(Array.from(content.children)));\n }\n // SDT with no `<w:sdtContent>` (rare — placeholder-only) drops\n // silently, matching the original \"unknown element\" behaviour.\n } else {\n out.push(child);\n }\n }\n return out;\n}\n\nfunction inlineSectPrOf(p: Element): Element | null {\n const pPr = wFirst(p, \"pPr\");\n return pPr ? wFirst(pPr, \"sectPr\") : null;\n}\n","import { parseXml } from \"../shared/xml\";\n\n/**\n * Parse a `_rels/*.rels` file into a map of `Id` → `Target`. The Target is\n * relative to the .rels file's own directory (Word's convention).\n */\nexport function parseRels(xmlSrc: string): Map<string, string> {\n const out = new Map<string, string>();\n const doc = parseXml(xmlSrc);\n const rels = doc.getElementsByTagName(\"Relationship\");\n for (const r of Array.from(rels)) {\n const id = r.getAttribute(\"Id\");\n const target = r.getAttribute(\"Target\");\n if (id && target) out.set(id, target);\n }\n return out;\n}\n","import { parseRels } from \"./rels\";\nimport { NS } from \"../shared/namespaces\";\nimport { parseXml, wAll, wChildren, wFirst, wVal } from \"../shared/xml\";\nimport type { PageZoneText } from \"../../paperStack/pageSetup\";\nimport type { HeaderFooterRef, SectionProperties } from \"../../doc/types\";\n\n/** Zone text extracted from the docx, with `{page}`/`{pages}` placeholders. */\nexport interface ImportedZones {\n header: PageZoneText;\n footer: PageZoneText;\n}\n\ninterface Reference {\n type: \"default\" | \"first\" | \"even\";\n relId: string;\n}\n\n/**\n * Resolve header/footer references in the body's first `<w:sectPr>`, load\n * each referenced part, and flatten to plain text with `{page}` / `{pages}`\n * substituted. Returns Sobree's `PageZoneText` model.\n *\n * Phase 2 ignores \"even\" references and keeps only \"default\" and \"first\".\n * \"Different last page\" has no native Word equivalent; we leave\n * `differentLast` off.\n */\nexport function readHeadersAndFooters(\n bodyXml: Document,\n relsXml: string | undefined,\n textParts: Record<string, string>,\n): ImportedZones {\n const defaults: PageZoneText = emptyZone();\n const zones: ImportedZones = { header: emptyZone(), footer: emptyZone() };\n\n const sectPr = wFirst(bodyXml, \"sectPr\");\n if (!sectPr) return zones;\n\n const titlePg = wFirst(sectPr, \"titlePg\") !== null;\n\n const headerRefs = collectReferences(sectPr, \"headerReference\");\n const footerRefs = collectReferences(sectPr, \"footerReference\");\n\n // Without the rels file we can't resolve any reference. Return empties.\n const rels = relsXml ? parseRels(relsXml) : new Map<string, string>();\n\n zones.header = resolveZone(headerRefs, rels, textParts, titlePg) ?? defaults;\n zones.footer = resolveZone(footerRefs, rels, textParts, titlePg) ?? defaults;\n return zones;\n}\n\nfunction collectReferences(sectPr: Element, localName: string): Reference[] {\n const out: Reference[] = [];\n for (const ref of wChildren(sectPr, localName)) {\n const type = (ref.getAttributeNS(NS.w, \"type\") ?? ref.getAttribute(\"w:type\") ?? \"default\") as\n | \"default\"\n | \"first\"\n | \"even\";\n const relId =\n ref.getAttributeNS(NS.r, \"id\") ?? ref.getAttribute(\"r:id\") ?? ref.getAttribute(\"id\");\n if (relId) out.push({ type, relId });\n }\n return out;\n}\n\nfunction resolveZone(\n refs: Reference[],\n rels: Map<string, string>,\n parts: Record<string, string>,\n differentFirst: boolean,\n): PageZoneText | null {\n const result: PageZoneText = emptyZone();\n result.differentFirst = differentFirst;\n\n for (const ref of refs) {\n if (ref.type === \"even\") continue; // Phase 2 scope cut.\n const target = rels.get(ref.relId);\n if (!target) continue;\n const path = resolveRelPath(target);\n const xml = parts[path];\n if (!xml) continue;\n const text = flattenZone(xml);\n if (ref.type === \"first\") result.first = text;\n else result.default = text;\n }\n // Only return the zone if we found at least one reference; otherwise let\n // the caller fall back to an empty default.\n return result.default || result.first ? result : null;\n}\n\n/**\n * `header*.xml` / `footer*.xml` → plain text with `{page}` / `{pages}`\n * substituted for Word field codes. Flat text only; paragraph breaks\n * become `\\n`, inline formatting is dropped. Paired with\n * `templateToBlocks` for the AST round-trip — the bridge converts the\n * `{page}` tokens back into native `FieldRun` nodes.\n */\nexport function flattenZone(xml: string): string {\n const doc = parseXml(xml);\n // OOXML uses `mc:AlternateContent` to provide both a modern\n // representation (`mc:Choice Requires=\"...\"`) AND a legacy\n // `mc:Fallback`. Without stripping Fallback, `wAll(doc, \"p\")`\n // returns paragraphs from BOTH branches and the text duplicates\n // in the header/footer body. Per ECMA-376 §23.2 the consumer\n // should pick one branch; we pick Choice (the modern one).\n removeMcFallbacks(doc);\n const lines: string[] = [];\n // Iterate direct `<w:p>` descendants of the root to preserve paragraph\n // breaks.\n const paragraphs = wAll(doc, \"p\");\n for (const p of paragraphs) {\n lines.push(flattenParagraphText(p));\n }\n return lines.filter((line, i) => i === 0 || line.length > 0).join(\"\\n\").trim();\n}\n\n/**\n * Strip every `<mc:Fallback>` element from the document tree. Used by\n * `flattenZone` so paragraphs inside the AlternateContent fallback\n * don't get walked alongside the Choice branch and duplicate text.\n * Idempotent + safe on docs with no AlternateContent.\n */\nfunction removeMcFallbacks(doc: Document): void {\n const MC_NS = \"http://schemas.openxmlformats.org/markup-compatibility/2006\";\n // getElementsByTagNameNS returns a live HTMLCollection — snapshot\n // into an array before removing so the iteration isn't disturbed.\n const fallbacks = Array.from(doc.getElementsByTagNameNS(MC_NS, \"Fallback\"));\n for (const f of fallbacks) f.parentNode?.removeChild(f);\n}\n\nfunction flattenParagraphText(p: Element): string {\n let out = \"\";\n // Walk children in document order. Handle `<w:r>` + `<w:fldSimple>`\n // + `<w:fldChar>` / `<w:instrText>` for PAGE / NUMPAGES.\n //\n // OOXML complex fields look like:\n // <w:fldChar fldCharType=\"begin\"/>\n // <w:instrText>PAGE</w:instrText>\n // <w:fldChar fldCharType=\"separate\"/>\n // <w:t>2</w:t> ← cached display value (last evaluated by Word)\n // <w:fldChar fldCharType=\"end\"/>\n //\n // We need to skip the cached display — otherwise it leaks into the\n // template as literal text adjacent to the field token, producing\n // gibberish like \"21. / 44. oldal\" at render time. The instruction\n // comes from `<w:instrText>`; everything between `separate` and\n // `end` is the cache and gets dropped.\n let inField = false;\n let instrBuf = \"\";\n for (const child of Array.from(p.childNodes)) {\n if (!(child instanceof Element) || child.namespaceURI !== NS.w) continue;\n if (child.localName === \"r\") {\n for (const sub of Array.from(child.childNodes)) {\n if (!(sub instanceof Element) || sub.namespaceURI !== NS.w) continue;\n if (sub.localName === \"t\") {\n if (!inField) out += sub.textContent ?? \"\";\n // else: cached field display — skip.\n } else if (sub.localName === \"br\") {\n if (!inField) out += \"\\n\";\n } else if (sub.localName === \"fldChar\") {\n const type = sub.getAttributeNS(NS.w, \"fldCharType\") ?? sub.getAttribute(\"w:fldCharType\");\n if (type === \"begin\") {\n inField = true;\n instrBuf = \"\";\n } else if (type === \"end\") {\n if (inField) out += fieldToToken(instrBuf);\n inField = false;\n instrBuf = \"\";\n }\n // `separate` is implicit — we already skip <w:t> while inField.\n } else if (sub.localName === \"instrText\" && inField) {\n instrBuf += sub.textContent ?? \"\";\n }\n }\n } else if (child.localName === \"fldSimple\") {\n const instr =\n child.getAttributeNS(NS.w, \"instr\") ?? child.getAttribute(\"w:instr\") ?? \"\";\n out += fieldToToken(instr);\n }\n }\n return out;\n}\n\nfunction fieldToToken(instr: string): string {\n const trimmed = instr.trim().toUpperCase();\n if (trimmed === \"PAGE\") return \"{page}\";\n if (trimmed === \"NUMPAGES\") return \"{pages}\";\n return \"\";\n}\n\nfunction resolveRelPath(target: string): string {\n // Relationships in word/_rels/document.xml.rels use paths relative to\n // word/. Headers and footers live at `word/header1.xml` etc.\n if (target.startsWith(\"/\")) return target.slice(1);\n return `word/${target}`;\n}\n\nfunction emptyZone(): PageZoneText {\n return { default: \"\", first: \"\", last: \"\", differentFirst: false, differentLast: false };\n}\n\n/** Read a twip-valued attribute off `<w:pgSz>` / `<w:pgMar>`. */\nexport function readTwipsAttr(el: Element | null, name: string): number | null {\n if (!el) return null;\n const v = el.getAttributeNS(NS.w, name) ?? el.getAttribute(`w:${name}`);\n if (!v) return null;\n const n = Number(v);\n return Number.isFinite(n) ? n : null;\n}\n\n/**\n * Convert a `<w:sectPr>` Element into a fully-populated `SectionProperties`.\n *\n * Reads pgSz / pgMar / vAlign / titlePg / type plus header and footer\n * references (resolved through `rels` to partIds). Falls back to A4\n * portrait + 1\" margins when geometry is missing — matches Word's\n * behaviour when an imported sectPr is sparse.\n */\nexport function readSection(sectPr: Element, rels: Map<string, string>): SectionProperties {\n const pgSz = wFirst(sectPr, \"pgSz\");\n const pgMar = wFirst(sectPr, \"pgMar\");\n const vAlignVal = wVal(wFirst(sectPr, \"vAlign\"));\n const typeVal = wVal(wFirst(sectPr, \"type\"));\n const orientAttr = pgSz?.getAttributeNS(NS.w, \"orient\") ?? pgSz?.getAttribute(\"w:orient\") ?? null;\n const wTwips = readTwipsAttr(pgSz, \"w\") ?? 11906; // A4 default\n const hTwips = readTwipsAttr(pgSz, \"h\") ?? 16838;\n\n const section: SectionProperties = {\n pageSize: {\n wTwips,\n hTwips,\n orientation: orientAttr === \"landscape\" ? \"landscape\" : \"portrait\",\n },\n pageMargins: {\n topTwips: readTwipsAttr(pgMar, \"top\") ?? 1440,\n rightTwips: readTwipsAttr(pgMar, \"right\") ?? 1440,\n bottomTwips: readTwipsAttr(pgMar, \"bottom\") ?? 1440,\n leftTwips: readTwipsAttr(pgMar, \"left\") ?? 1440,\n headerTwips: readTwipsAttr(pgMar, \"header\") ?? 720,\n footerTwips: readTwipsAttr(pgMar, \"footer\") ?? 720,\n gutterTwips: readTwipsAttr(pgMar, \"gutter\") ?? 0,\n },\n headerRefs: collectHeaderFooterRefs(sectPr, \"headerReference\", rels),\n footerRefs: collectHeaderFooterRefs(sectPr, \"footerReference\", rels),\n };\n\n if (vAlignVal === \"top\" || vAlignVal === \"center\" || vAlignVal === \"bottom\" || vAlignVal === \"both\") {\n section.vAlign = vAlignVal;\n }\n if (wFirst(sectPr, \"titlePg\")) section.titlePage = true;\n if (typeVal === \"continuous\" || typeVal === \"nextPage\" || typeVal === \"evenPage\" || typeVal === \"oddPage\") {\n section.type = typeVal;\n }\n\n // <w:cols w:num=\"2\" w:space=\"708\"/> — multi-column section layout.\n // Only emit when `num > 1`; the default single-column case is\n // represented by `columns` being absent.\n const cols = wFirst(sectPr, \"cols\");\n if (cols) {\n const num =\n readTwipsAttr(cols, \"num\") ?? 1; // `num` reuses the same attribute reader; it's just an integer.\n if (num > 1) {\n const sectionCols: { count: number; spaceTwips?: number } = { count: num };\n const space = readTwipsAttr(cols, \"space\");\n if (space !== null && space > 0) sectionCols.spaceTwips = space;\n section.columns = sectionCols;\n }\n }\n\n return section;\n}\n\nfunction collectHeaderFooterRefs(\n sectPr: Element,\n localName: \"headerReference\" | \"footerReference\",\n rels: Map<string, string>,\n): HeaderFooterRef[] {\n const out: HeaderFooterRef[] = [];\n for (const ref of wChildren(sectPr, localName)) {\n const typeAttr = ref.getAttributeNS(NS.w, \"type\") ?? ref.getAttribute(\"w:type\") ?? \"default\";\n const relId = ref.getAttributeNS(NS.r, \"id\") ?? ref.getAttribute(\"r:id\");\n if (!relId) continue;\n const target = rels.get(relId);\n if (!target) continue;\n if (typeAttr === \"default\" || typeAttr === \"first\" || typeAttr === \"even\") {\n // partId is the relationship target stripped of the `word/` prefix\n // (rels store paths relative to the document part).\n out.push({ type: typeAttr, partId: target.replace(/^word\\//, \"\") });\n }\n }\n return out;\n}\n\n/** Shared helper: parse `<w:sectPr>` for pgSz/pgMar/vAlign. */\nexport function readPageGeometry(xmlDoc: Document): {\n widthTwips: number | null;\n heightTwips: number | null;\n margins: { top: number | null; right: number | null; bottom: number | null; left: number | null };\n vAlign: \"top\" | \"center\" | \"bottom\" | \"both\" | null;\n} | null {\n const sectPr = wFirst(xmlDoc, \"sectPr\");\n if (!sectPr) return null;\n const pgSz = wFirst(sectPr, \"pgSz\");\n const pgMar = wFirst(sectPr, \"pgMar\");\n const vAlignEl = wFirst(sectPr, \"vAlign\");\n const vAlignVal = vAlignEl ? wVal(vAlignEl) : null;\n return {\n widthTwips: readTwipsAttr(pgSz, \"w\"),\n heightTwips: readTwipsAttr(pgSz, \"h\"),\n margins: {\n top: readTwipsAttr(pgMar, \"top\"),\n right: readTwipsAttr(pgMar, \"right\"),\n bottom: readTwipsAttr(pgMar, \"bottom\"),\n left: readTwipsAttr(pgMar, \"left\"),\n },\n vAlign:\n vAlignVal === \"top\" ||\n vAlignVal === \"center\" ||\n vAlignVal === \"bottom\" ||\n vAlignVal === \"both\"\n ? vAlignVal\n : null,\n };\n}\n\n// Re-export `wVal` so callers don't import from both modules.\nexport { wVal };\n","import { unzipSync } from \"fflate\";\n\n/**\n * Read a .docx file into a map of part-path → UTF-8 text for the parts we\n * need as strings (all the `*.xml` parts), and bytes for the parts we need\n * as binary (images, fonts). Kept synchronous: fflate is fast enough that a\n * 100-page document unpacks in milliseconds on the main thread.\n */\nexport interface UnzippedDocx {\n /** `word/document.xml` → text, `word/styles.xml` → text, etc. */\n readonly text: Record<string, string>;\n /** `word/media/image1.png` → bytes. */\n readonly binary: Record<string, Uint8Array>;\n}\n\nconst TEXT_EXT = new Set([\"xml\", \"rels\"]);\nconst decoder = new TextDecoder(\"utf-8\");\n\nexport async function unzipDocx(\n src: File | Blob | ArrayBuffer | Uint8Array,\n): Promise<UnzippedDocx> {\n const buffer =\n src instanceof Uint8Array\n ? src\n : src instanceof ArrayBuffer\n ? new Uint8Array(src)\n : new Uint8Array(await src.arrayBuffer());\n const files = unzipSync(buffer);\n const text: Record<string, string> = {};\n const binary: Record<string, Uint8Array> = {};\n for (const [path, bytes] of Object.entries(files)) {\n const ext = path.split(\".\").pop()?.toLowerCase() ?? \"\";\n if (TEXT_EXT.has(ext)) text[path] = decoder.decode(bytes);\n else binary[path] = bytes;\n }\n return { text, binary };\n}\n","/**\n * Parse `word/footnotes.xml` into a map of `id → Block[]`.\n *\n * OOXML structure:\n *\n * <w:footnotes>\n * <w:footnote w:type=\"separator\" w:id=\"-1\">...</w:footnote>\n * <w:footnote w:type=\"continuationSeparator\" w:id=\"0\">...</w:footnote>\n * <w:footnote w:id=\"1\"><w:p>...body...</w:p></w:footnote>\n * <w:footnote w:id=\"2\">...</w:footnote>\n * </w:footnotes>\n *\n * The `separator` and `continuationSeparator` footnotes are visual\n * artefacts (the horizontal rule between body and footnote area, and\n * its continuation marker). We skip them — they're a paginator\n * concern. Negative ids are also skipped (Word's reserved range).\n */\n\nimport { type ConvertContext, convertParagraph } from \"./paragraph\";\nimport { convertTable } from \"./tables\";\nimport { parseXml, wAll } from \"../shared/xml\";\nimport { NS } from \"../shared/namespaces\";\nimport type { Block } from \"../../doc/types\";\n\nexport function parseFootnotesXml(\n xml: string | undefined,\n ctx: ConvertContext,\n): Record<number, Block[]> {\n if (!xml) return {};\n let doc: Document;\n try {\n doc = parseXml(xml);\n } catch {\n return {};\n }\n const out: Record<number, Block[]> = {};\n for (const footnote of wAll(doc, \"footnote\")) {\n const typeAttr =\n footnote.getAttributeNS(NS.w, \"type\") ?? footnote.getAttribute(\"w:type\");\n if (typeAttr === \"separator\" || typeAttr === \"continuationSeparator\") continue;\n const idAttr =\n footnote.getAttributeNS(NS.w, \"id\") ?? footnote.getAttribute(\"w:id\");\n const id = Number(idAttr);\n if (!Number.isFinite(id) || id < 1) continue;\n\n const blocks: Block[] = [];\n for (const child of Array.from(footnote.children)) {\n if (child.namespaceURI !== NS.w) continue;\n if (child.localName === \"p\") blocks.push(convertParagraph(child, ctx));\n else if (child.localName === \"tbl\") blocks.push(convertTable(child, ctx));\n // Other element types silently dropped — footnote bodies are\n // almost always plain paragraphs.\n }\n out[id] = blocks;\n }\n return out;\n}\n","/**\n * Parse `word/comments.xml` into a map of `id → Comment`.\n *\n * OOXML structure:\n *\n * <w:comments>\n * <w:comment w:id=\"0\" w:author=\"Alice\" w:date=\"...\" w:initials=\"A\">\n * <w:p><w:r><w:t>Body…</w:t></w:r></w:p>\n * </w:comment>\n * <w:comment w:id=\"1\" w:author=\"Bob\">…</w:comment>\n * </w:comments>\n *\n * Each comment's body is converted with the standard paragraph /\n * table walkers — comment bodies are just small block streams.\n */\n\nimport { type ConvertContext, convertParagraph } from \"./paragraph\";\nimport { convertTable } from \"./tables\";\nimport { parseXml, wAll } from \"../shared/xml\";\nimport { NS } from \"../shared/namespaces\";\nimport type { Block, Comment } from \"../../doc/types\";\n\n/** Namespace for Word 2013+ extensions (`commentsExtended.xml`). */\nconst NS_W15 = \"http://schemas.microsoft.com/office/word/2012/wordml\";\n/** Namespace for Word 2010+ paragraph ids (`paraId`). */\nconst NS_W14 = \"http://schemas.microsoft.com/office/word/2010/wordml\";\n\nexport function parseCommentsXml(\n xml: string | undefined,\n ctx: ConvertContext,\n extendedXml?: string | undefined,\n): Record<number, Comment> {\n if (!xml) return {};\n let doc: Document;\n try {\n doc = parseXml(xml);\n } catch {\n return {};\n }\n // Pre-parse the extensions file (Word 2013+ adds `done` / parent on\n // each `commentEx`, keyed by the body paragraph's `w14:paraId`).\n const ext = extendedXml\n ? parseCommentsExtendedXml(extendedXml)\n : new Map<string, CommentExt>();\n\n // First pass: parse each `<w:comment>` body. Capture the first\n // paragraph's `w14:paraId` so the extensions (keyed by that id) can\n // attach `done` / `replyToId` later. Build a `paraId → commentId`\n // map alongside so we can resolve `paraIdParent` references.\n const out: Record<number, Comment> = {};\n const paraIdToCommentId = new Map<string, number>();\n const commentIdToFirstParaId = new Map<number, string>();\n for (const comment of wAll(doc, \"comment\")) {\n const idAttr =\n comment.getAttributeNS(NS.w, \"id\") ?? comment.getAttribute(\"w:id\");\n const id = Number(idAttr);\n if (!Number.isFinite(id) || id < 0) continue;\n\n const body: Block[] = [];\n let firstParaId: string | null = null;\n for (const child of Array.from(comment.children)) {\n if (child.namespaceURI !== NS.w) continue;\n if (child.localName === \"p\") {\n if (firstParaId === null) {\n firstParaId =\n child.getAttributeNS(NS_W14, \"paraId\") ?? child.getAttribute(\"w14:paraId\");\n }\n body.push(convertParagraph(child, ctx));\n } else if (child.localName === \"tbl\") {\n body.push(convertTable(child, ctx));\n }\n }\n if (firstParaId !== null) {\n paraIdToCommentId.set(firstParaId, id);\n commentIdToFirstParaId.set(id, firstParaId);\n }\n\n const author =\n comment.getAttributeNS(NS.w, \"author\") ?? comment.getAttribute(\"w:author\");\n const initials =\n comment.getAttributeNS(NS.w, \"initials\") ?? comment.getAttribute(\"w:initials\");\n const date = comment.getAttributeNS(NS.w, \"date\") ?? comment.getAttribute(\"w:date\");\n out[id] = {\n id,\n ...(author ? { author } : {}),\n ...(initials ? { initials } : {}),\n ...(date ? { date } : {}),\n body,\n };\n }\n\n // Second pass: decorate each comment with `done` + `replyToId` from\n // the extensions file, joining via the paraId we recorded.\n for (const [id, comment] of Object.entries(out)) {\n const paraId = commentIdToFirstParaId.get(Number(id));\n if (!paraId) continue;\n const meta = ext.get(paraId);\n if (!meta) continue;\n if (meta.done) comment.done = true;\n if (meta.paraIdParent) {\n const parentId = paraIdToCommentId.get(meta.paraIdParent);\n if (parentId !== undefined && parentId !== Number(id)) {\n comment.replyToId = parentId;\n }\n }\n }\n return out;\n}\n\ninterface CommentExt {\n done?: true;\n paraIdParent?: string;\n}\n\n/**\n * Parse `word/commentsExtended.xml` into a `paraId → { done, paraIdParent }` map.\n *\n * <w15:commentsEx xmlns:w15=\"...\">\n * <w15:commentEx w15:paraId=\"ABCD1234\" w15:done=\"1\"/>\n * <w15:commentEx w15:paraId=\"EFGH5678\" w15:paraIdParent=\"ABCD1234\"/>\n * </w15:commentsEx>\n *\n * - `done=\"1\"` → comment was marked resolved in Word.\n * - `paraIdParent` → this comment is a reply to the comment whose\n * body's first paragraph has that paraId. The join from paraId to\n * comment id happens in the caller (the comments file owns the\n * paraId→commentId mapping).\n */\nfunction parseCommentsExtendedXml(xml: string): Map<string, CommentExt> {\n const out = new Map<string, CommentExt>();\n let doc: Document;\n try {\n doc = parseXml(xml);\n } catch {\n return out;\n }\n const exs = Array.from(doc.getElementsByTagNameNS(NS_W15, \"commentEx\"));\n for (const ex of exs) {\n const paraId =\n ex.getAttributeNS(NS_W15, \"paraId\") ?? ex.getAttribute(\"w15:paraId\");\n if (!paraId) continue;\n const done =\n ex.getAttributeNS(NS_W15, \"done\") ?? ex.getAttribute(\"w15:done\");\n const paraIdParent =\n ex.getAttributeNS(NS_W15, \"paraIdParent\") ?? ex.getAttribute(\"w15:paraIdParent\");\n const entry: CommentExt = {};\n if (done === \"1\") entry.done = true;\n if (paraIdParent) entry.paraIdParent = paraIdParent;\n if (entry.done || entry.paraIdParent) out.set(paraId, entry);\n }\n return out;\n}\n","/**\n * Parse the subset of `word/settings.xml` that affects rendering.\n *\n * The two flags we care about right now are the ones that decide\n * whether Word applies its implicit \"Word 2010+ Normal style\" baseline\n * (line ≈ 1.08, after = 8pt) at render time even when styles.xml\n * leaves Normal empty:\n *\n * - `<w:compatibilityMode w:val=\"14\"/>` — Word's rendering era.\n * 12+ = Word 2007+ with the modern Normal defaults.\n * <12 = legacy mode, no implicit spacing.\n * - `<w:doNotUseHTMLParagraphAutoSpacing/>` — when present, Word\n * explicitly opts out of the modern auto-spacing and renders\n * tight regardless of compatibilityMode.\n *\n * This is the missing piece that explains why a Word-authored docx\n * with an empty `<w:style w:styleId=\"Normal\">` renders with visible\n * inter-paragraph breathing in Word (compatibilityMode 14, auto-\n * spacing on), but a programmatically-generated docx with no\n * `<w:compatibilityMode>` renders tight in Word too. Without this\n * gate, Sobree's baseline-injection either over- or under-applies\n * depending on the source.\n */\n\nimport { parseXml, wAll, wFirst } from \"../shared/xml\";\nimport { NS } from \"../shared/namespaces\";\n\nexport interface DocSettings {\n /** Numeric compatibility mode from `<w:compatibilityMode>`. Undefined\n * if the docx omits it (treat as legacy = pre-Word-2007). */\n compatibilityMode?: number;\n /** True when `<w:doNotUseHTMLParagraphAutoSpacing/>` is present. */\n doNotUseHTMLParagraphAutoSpacing: boolean;\n /** `<w:defaultTabStop w:val=\"N\"/>` in twips. Used as the interval\n * for tab advances in paragraphs that don't declare their own\n * `<w:tabs>`. Word's factory default is 720 twips (0.5\"). */\n defaultTabStopTwips?: number;\n}\n\nexport function parseSettingsXml(xml: string | undefined): DocSettings {\n const out: DocSettings = { doNotUseHTMLParagraphAutoSpacing: false };\n if (!xml) return out;\n let doc: Document;\n try {\n doc = parseXml(xml);\n } catch {\n return out;\n }\n\n // <w:compat>/<w:compatSetting w:name=\"compatibilityMode\" w:val=\"14\"/>\n // is the modern format. Older docs may have a bare\n // <w:compatibilityMode> element directly under <w:compat>.\n for (const el of wAll(doc, \"compatSetting\")) {\n const name = el.getAttributeNS(NS.w, \"name\") ?? el.getAttribute(\"w:name\");\n if (name !== \"compatibilityMode\") continue;\n const val = el.getAttributeNS(NS.w, \"val\") ?? el.getAttribute(\"w:val\");\n if (val) {\n const n = Number.parseInt(val, 10);\n if (Number.isFinite(n)) out.compatibilityMode = n;\n }\n }\n if (out.compatibilityMode === undefined) {\n const legacy = wFirst(doc, \"compatibilityMode\");\n if (legacy) {\n const val =\n legacy.getAttributeNS(NS.w, \"val\") ?? legacy.getAttribute(\"w:val\");\n if (val) {\n const n = Number.parseInt(val, 10);\n if (Number.isFinite(n)) out.compatibilityMode = n;\n }\n }\n }\n\n // <w:doNotUseHTMLParagraphAutoSpacing/> — boolean flag (presence = on).\n if (wFirst(doc, \"doNotUseHTMLParagraphAutoSpacing\")) {\n out.doNotUseHTMLParagraphAutoSpacing = true;\n }\n\n // <w:defaultTabStop w:val=\"720\"/> — interval for tab advances when a\n // paragraph has no explicit `<w:tabs>` stops. Word's factory default\n // is 720 twips (0.5\"). Without it, browsers fall back to CSS tab-size\n // default of 8 characters which is much narrower than Word's tab\n // (jellap.docx's header tabs come out ~3x too tight).\n const defaultTabStop = wFirst(doc, \"defaultTabStop\");\n if (defaultTabStop) {\n const val =\n defaultTabStop.getAttributeNS(NS.w, \"val\") ??\n defaultTabStop.getAttribute(\"w:val\");\n if (val) {\n const n = Number.parseInt(val, 10);\n if (Number.isFinite(n) && n > 0) out.defaultTabStopTwips = n;\n }\n }\n\n return out;\n}\n\n/**\n * Should we apply Word's implicit \"Normal style\" paragraph baseline\n * (line ≈ 1.08, after = 8pt) for paragraphs whose explicit settings\n * leave those fields undefined?\n *\n * Word's rule, distilled: yes when in Word 2007+ rendering mode\n * (compatibilityMode >= 12) AND auto-spacing isn't explicitly turned\n * off. Without this gate, Sobree either over-applies (on a docx-\n * library-style doc that lacks compatibilityMode → renders tight in\n * Word too) or under-applies (on a Word-authored doc whose Normal\n * style is empty → Word fills in defaults, we don't).\n */\nexport function shouldApplyAutoSpacing(settings: DocSettings): boolean {\n if (settings.doNotUseHTMLParagraphAutoSpacing) return false;\n if (settings.compatibilityMode === undefined) return false;\n return settings.compatibilityMode >= 12;\n}\n","/**\n * Parse `word/styles.xml` into a `NamedStyle[]`.\n *\n * Word's styles.xml is a long catalogue of `<w:style>` definitions plus\n * a single `<w:docDefaults>` block carrying the document-wide rPr / pPr\n * defaults. Without this, every imported docx falls back to Sobree's\n * synthesised `defaultStyles()` (Helvetica 11pt, Word-hardcoded\n * spacing) which mismatches whatever the docx author actually chose.\n *\n * Coverage:\n * - `<w:style>` rPr → RunProperties: `rFonts.ascii`, `sz` (half-pts),\n * `b`, `i`, `u`, `color`, `vertAlign`.\n * - `<w:style>` pPr → ParagraphProperties: `jc`, `spacing.{line,\n * lineRule, before, after}`, `ind.{left, right}`.\n * - `<w:basedOn>`, `<w:next>`, `<w:name>`.\n * - `<w:docDefaults>` is folded into a synthetic Normal-basis\n * style — anything unspecified there falls through to Word's\n * hardcoded baseline.\n *\n * Out of scope (silently dropped): table styles, numbering styles,\n * conditional formatting (`<w:tblStylePr>`), advanced font hints\n * (`hAnsi`, `cs`, `eastAsia`), runs without a recognised property.\n */\n\nimport type {\n NamedStyle,\n ParagraphProperties,\n ParagraphAlignment,\n ParagraphSpacing,\n ParagraphIndent,\n RunProperties,\n} from \"../../doc/types\";\nimport { parseXml, wAll, wFirst, wVal } from \"../shared/xml\";\nimport { readShading } from \"../shared/shading\";\nimport { NS } from \"../shared/namespaces\";\nimport { type DocSettings, shouldApplyAutoSpacing } from \"./settings\";\nimport { resolveStyleCascade } from \"../../doc/styles\";\n\nexport function parseStylesXml(\n xml: string | undefined,\n settings: DocSettings = { doNotUseHTMLParagraphAutoSpacing: false },\n): NamedStyle[] | null {\n if (!xml) return null;\n let doc: Document;\n try {\n doc = parseXml(xml);\n } catch {\n return null;\n }\n const out: NamedStyle[] = [];\n\n // 1. Synthesise a Normal-or-equivalent style from <w:docDefaults> so\n // docs whose styles.xml omits an explicit Normal still pick up\n // document-wide rPr / pPr (the doc default for everything).\n const docDefaults = wFirst(doc, \"docDefaults\");\n if (docDefaults) {\n const rPrDefault = wFirst(docDefaults, \"rPrDefault\");\n const pPrDefault = wFirst(docDefaults, \"pPrDefault\");\n const rDef = rPrDefault ? wFirst(rPrDefault, \"rPr\") : null;\n const pDef = pPrDefault ? wFirst(pPrDefault, \"pPr\") : null;\n const runDefaults = rDef ? readRunProperties(rDef) : undefined;\n const paragraphDefaults = pDef ? readParagraphProperties(pDef) : undefined;\n if (runDefaults || paragraphDefaults) {\n out.push({\n id: \"DocDefaults\",\n type: \"paragraph\",\n displayName: \"Document defaults\",\n ...(runDefaults ? { runDefaults } : {}),\n ...(paragraphDefaults ? { paragraphDefaults } : {}),\n });\n }\n }\n\n // 2. Walk every <w:style> element. Each becomes a NamedStyle.\n for (const styleEl of wAll(doc, \"style\")) {\n const styleId =\n styleEl.getAttributeNS(NS.w, \"styleId\") ??\n styleEl.getAttribute(\"w:styleId\");\n if (!styleId) continue;\n const typeAttr =\n styleEl.getAttributeNS(NS.w, \"type\") ?? styleEl.getAttribute(\"w:type\");\n const type = mapStyleType(typeAttr);\n if (!type) continue; // skip unknown style types silently\n\n const displayName =\n wVal(wFirst(styleEl, \"name\")) ?? styleId;\n const basedOn = wVal(wFirst(styleEl, \"basedOn\")) ?? undefined;\n const nextStyleId = wVal(wFirst(styleEl, \"next\")) ?? undefined;\n\n const rPr = wFirst(styleEl, \"rPr\");\n const pPr = wFirst(styleEl, \"pPr\");\n const runDefaults = rPr ? readRunProperties(rPr) : undefined;\n const paragraphDefaults = pPr ? readParagraphProperties(pPr) : undefined;\n\n out.push({\n id: styleId,\n type,\n displayName,\n ...(basedOn ? { basedOn } : {}),\n ...(nextStyleId ? { nextStyleId } : {}),\n ...(runDefaults ? { runDefaults } : {}),\n ...(paragraphDefaults ? { paragraphDefaults } : {}),\n });\n }\n\n // 3. Wire up: every paragraph style without an explicit basedOn is\n // based on DocDefaults (when present) so the document-wide\n // typography flows in via the cascade. Skip \"Normal\" itself if\n // DocDefaults exists (Normal becomes basedOn=DocDefaults).\n if (docDefaults) {\n for (const style of out) {\n if (style.id === \"DocDefaults\") continue;\n if (style.type !== \"paragraph\") continue;\n if (style.basedOn) continue;\n style.basedOn = \"DocDefaults\";\n }\n }\n\n // 4. Word-hardcoded baseline. The OOXML spec says \"if not specified,\n // behaviour is implementation-defined.\" In practice Word renders\n // a styles-empty Normal with `line=276` (1.15×) and `after=160`\n // twips (8pt) — that's what gives bare paragraphs visible\n // breathing in Word even when the docx doesn't declare it.\n //\n // We pin those values onto the resolved cascade for whichever\n // style is the document default for paragraphs (Normal, or the\n // one tagged `<w:default w:val=\"1\"/>` if styles.xml uses that).\n // Properties already specified by the docx win.\n ensureWordBaseline(out, doc, shouldApplyAutoSpacing(settings));\n\n return out.length > 0 ? out : null;\n}\n\n/**\n * Apply Word's hardcoded baseline to whichever paragraph style is the\n * document default — matches what Word renders when styles.xml leaves\n * properties unspecified. Anything the docx already declares wins.\n *\n * Two pieces of baseline currently:\n *\n * - **Run defaults**: font `Calibri`, size 11pt. Word's bare Normal\n * style renders with these even when its `<w:rPr>` is empty\n * (Word's hardcoded run baseline for the \"minor latin\" font slot,\n * which is what plain Latin runs land in). Without this, bare\n * paragraphs render in the browser default (Times New Roman 16px\n * for most browsers) and diverge from Word on the very first\n * character.\n * - **Paragraph defaults**: `line=276` (1.15×) + `after=160` (8pt).\n * Word's hardcoded paragraph baseline.\n *\n * The default style is the one with `<w:style w:default=\"1\">` (Word's\n * way of marking \"the default style for this type\") OR, failing that,\n * the style id `Normal`.\n */\nfunction ensureWordBaseline(\n styles: NamedStyle[],\n doc: Document,\n applyAutoSpacing: boolean,\n): void {\n // Find the explicit default-paragraph-style id from styles.xml\n // attribute markers.\n let defaultStyleId: string | undefined;\n for (const styleEl of wAll(doc, \"style\")) {\n const type =\n styleEl.getAttributeNS(NS.w, \"type\") ?? styleEl.getAttribute(\"w:type\");\n if (type !== \"paragraph\") continue;\n const isDefault =\n styleEl.getAttributeNS(NS.w, \"default\") ??\n styleEl.getAttribute(\"w:default\");\n if (isDefault === \"1\" || isDefault === \"true\") {\n defaultStyleId =\n styleEl.getAttributeNS(NS.w, \"styleId\") ??\n styleEl.getAttribute(\"w:styleId\") ??\n undefined;\n break;\n }\n }\n defaultStyleId = defaultStyleId ?? \"Normal\";\n\n // If the docx omits the default style entirely (docx-library\n // generates this shape — empty <w:docDefaults/>, no <w:style\n // w:styleId=\"Normal\">), synthesize it. Word's hardcoded behaviour\n // is to fall back to its own Normal-style baseline; we do the same\n // by materialising a Normal-equivalent here so the cascade has\n // something to resolve to. basedOn `DocDefaults` if we read one,\n // otherwise undefined.\n let target = styles.find((s) => s.id === defaultStyleId);\n if (!target) {\n const hasDocDefaults = styles.some((s) => s.id === \"DocDefaults\");\n target = {\n id: defaultStyleId,\n type: \"paragraph\",\n displayName: defaultStyleId,\n ...(hasDocDefaults ? { basedOn: \"DocDefaults\" } : {}),\n };\n styles.push(target);\n // Re-wire other paragraph styles that don't yet have a basedOn\n // (they should chain through our synthesized default now).\n for (const s of styles) {\n if (s === target || s.id === \"DocDefaults\") continue;\n if (s.type !== \"paragraph\") continue;\n if (s.basedOn) continue;\n s.basedOn = defaultStyleId;\n }\n }\n\n // --- Run defaults: font / size ---\n // Only inject Calibri 11pt for fields the *cascade* doesn't already\n // provide. If the docx ships a `<w:docDefaults>` with rFonts set\n // (e.g. user-contract.docx declares Times New Roman for the whole\n // document), Normal inherits via `basedOn: \"DocDefaults\"` and the\n // cascade resolves to that font — injecting Calibri here would\n // override the author's choice, breaking visual fidelity with Word.\n //\n // The check resolves the chain (Normal → basedOn → … → DocDefaults)\n // and only fills in fields no ancestor specifies. Word's hardcoded\n // Calibri-11pt baseline is the last-resort fallback, not an override.\n const resolved = resolveStyleCascade(styles, defaultStyleId);\n const existingRuns = target.runDefaults ?? {};\n const inheritedFontFamily = resolved.runDefaults.fontFamily;\n const inheritedFontSize = resolved.runDefaults.fontSizePt;\n target.runDefaults = {\n ...existingRuns,\n ...(inheritedFontFamily === undefined ? { fontFamily: \"Calibri\" } : {}),\n ...(inheritedFontSize === undefined ? { fontSizePt: 11 } : {}),\n };\n\n // --- Paragraph defaults: NO hardcoded spacing baseline ---\n // Earlier rounds injected `line=276 + after=160` here as Word's\n // supposed implicit Normal baseline, but visual comparison against\n // fixtures shows Word actually renders TIGHT when both Normal and\n // pPrDefault are empty — even with compatibilityMode 15. The visible\n // breathing in real Word docs (user contract) comes instead from\n // the font's natural leading being multiplied into `lineRule=auto`\n // line values — see the renderer (`block.ts`)'s line-height formula\n // for the calibration. `applyAutoSpacing` is read for future use\n // (e.g. autoSpacing toggles on specific styles) but currently no\n // baseline is injected here.\n void applyAutoSpacing;\n\n // --- Heading baselines ---\n // Word's hardcoded heading defaults: each heading style has\n // `before/after` spacing + `keepNext: true` even when styles.xml\n // doesn't declare them. The docx-library-generated docx (and many\n // other authors) leave HeadingN.pPr empty, relying on Word's\n // built-in baseline; we inject it here for the same reason as the\n // Normal baseline above.\n //\n // Sizes calibrated from Word's \"Latent Styles\" defaults:\n // - Heading1: 240 before, 0 after (12pt + 0)\n // - Heading2: 200 before, 0 after\n // - Heading3-6: 160 before, 0 after\n // All keepNext (the heading stays glued to whatever follows).\n for (let level = 1; level <= 6; level++) {\n const styleId = `Heading${level}`;\n const heading = styles.find((s) => s.id === styleId);\n if (!heading) continue;\n if (!heading.paragraphDefaults) {\n const beforeTwips = level === 1 ? 240 : level === 2 ? 200 : 160;\n heading.paragraphDefaults = {\n spacing: { beforeTwips, afterTwips: 0, line: 240, lineRule: \"auto\" },\n keepNext: true,\n };\n }\n // Heading font baseline: Word's modern theme uses \"Calibri Light\"\n // for headings (the \"Major Latin\" font slot in the theme). When the\n // docx doesn't declare a font on the Heading style (typical — the\n // theme provides it), inject Calibri Light so headings render with\n // their distinct typographic voice. Other run-default fields the\n // docx already set (color, fontSizePt) are preserved.\n const existing = heading.runDefaults ?? {};\n heading.runDefaults = {\n ...existing,\n fontFamily: existing.fontFamily ?? \"Calibri Light\",\n };\n }\n}\n\n// ---------- per-element readers ----------\n\nfunction mapStyleType(raw: string | null): NamedStyle[\"type\"] | null {\n switch (raw) {\n case \"paragraph\":\n return \"paragraph\";\n case \"character\":\n return \"character\";\n case \"table\":\n return \"table\";\n case \"numbering\":\n return \"numbering\";\n default:\n return null;\n }\n}\n\nfunction readRunProperties(rPr: Element): RunProperties | undefined {\n const out: RunProperties = {};\n // <w:rFonts w:ascii=\"...\"/> — Word stores up to four font slots\n // (ascii / hAnsi / eastAsia / cs). We only honour ascii for now;\n // it's the slot Word picks for Latin text, which covers > 99% of\n // visible runs.\n const rFonts = wFirst(rPr, \"rFonts\");\n if (rFonts) {\n const ascii =\n rFonts.getAttributeNS(NS.w, \"ascii\") ?? rFonts.getAttribute(\"w:ascii\");\n if (ascii) out.fontFamily = ascii;\n }\n // <w:sz w:val=\"22\"/> — value is in HALF-POINTS, so 22 = 11pt.\n const szVal = wVal(wFirst(rPr, \"sz\"));\n if (szVal) {\n const halfPts = Number.parseInt(szVal, 10);\n if (Number.isFinite(halfPts)) out.fontSizePt = halfPts / 2;\n }\n // Boolean toggles — present element + (no `w:val` OR val=\"1\"/\"true\") = true.\n if (toggleOn(wFirst(rPr, \"b\"))) out.bold = true;\n if (toggleOn(wFirst(rPr, \"i\"))) out.italic = true;\n const u = wFirst(rPr, \"u\");\n if (u) {\n const v = wVal(u);\n if (v && v !== \"none\") {\n // Treat unknown values as \"single\" — Word has many underline\n // styles (wave, dotted, dashLong, …) but we expose only the\n // canonical set; coerce to the closest renderable.\n out.underline =\n v === \"single\" || v === \"double\" || v === \"dotted\" || v === \"dashed\" || v === \"wave\"\n ? v\n : \"single\";\n } else if (v === null) {\n // No w:val attribute = single underline by default in OOXML.\n out.underline = \"single\";\n }\n }\n if (toggleOn(wFirst(rPr, \"strike\"))) out.strike = true;\n // `<w:caps/>` cascading through a named style — same toggle behaviour\n // as `bold` / `italic`. Mirrors the per-run read in `runs.ts`.\n if (toggleOn(wFirst(rPr, \"caps\"))) out.caps = true;\n // <w:color w:val=\"FF0000\"/> or \"auto\"\n const colorVal = wVal(wFirst(rPr, \"color\"));\n if (colorVal && colorVal !== \"auto\") {\n out.color = colorVal.startsWith(\"#\") ? colorVal : `#${colorVal}`;\n }\n // <w:vertAlign w:val=\"superscript\"/>\n const vAlign = wVal(wFirst(rPr, \"vertAlign\"));\n if (vAlign === \"superscript\" || vAlign === \"subscript\") {\n out.verticalAlign = vAlign;\n }\n return Object.keys(out).length > 0 ? out : undefined;\n}\n\nfunction readParagraphProperties(pPr: Element): ParagraphProperties | undefined {\n const out: ParagraphProperties = {};\n\n // <w:jc w:val=\"left|center|right|both\"/>\n const jc = wVal(wFirst(pPr, \"jc\"));\n const alignment = mapAlignment(jc);\n if (alignment) out.alignment = alignment;\n\n // <w:spacing w:line=\"276\" w:lineRule=\"auto\" w:before=\"240\" w:after=\"120\"/>\n const spacing = wFirst(pPr, \"spacing\");\n if (spacing) {\n const sp: ParagraphSpacing = {};\n const line = readNumAttr(spacing, \"line\");\n if (line !== null) sp.line = line;\n const lineRule = spacing.getAttributeNS(NS.w, \"lineRule\") ?? spacing.getAttribute(\"w:lineRule\");\n if (lineRule === \"auto\" || lineRule === \"exact\" || lineRule === \"atLeast\") {\n sp.lineRule = lineRule;\n }\n const before = readNumAttr(spacing, \"before\");\n if (before !== null) sp.beforeTwips = before;\n const after = readNumAttr(spacing, \"after\");\n if (after !== null) sp.afterTwips = after;\n if (Object.keys(sp).length > 0) out.spacing = sp;\n }\n\n // <w:ind w:left=\"720\" w:right=\"720\"/>\n const ind = wFirst(pPr, \"ind\");\n if (ind) {\n const indent: ParagraphIndent = {};\n const left = readNumAttr(ind, \"left\");\n if (left !== null) indent.leftTwips = left;\n const right = readNumAttr(ind, \"right\");\n if (right !== null) indent.rightTwips = right;\n if (Object.keys(indent).length > 0) out.indent = indent;\n }\n\n // <w:pageBreakBefore/>\n if (wFirst(pPr, \"pageBreakBefore\")) out.pageBreakBefore = true;\n\n // <w:shd w:val=\"clear\" w:fill=\"…\"/> — paragraph background colour.\n const shading = readShading(pPr);\n if (shading) out.shading = shading;\n\n return Object.keys(out).length > 0 ? out : undefined;\n}\n\nfunction mapAlignment(raw: string | null): ParagraphAlignment | null {\n switch (raw) {\n case \"left\":\n case \"right\":\n case \"center\":\n return raw;\n case \"both\":\n case \"justify\":\n return \"both\";\n case \"distribute\":\n return \"distribute\";\n default:\n return null;\n }\n}\n\nfunction toggleOn(el: Element | null): boolean {\n if (!el) return false;\n const v = wVal(el);\n return v === null || v === \"1\" || v === \"true\";\n}\n\nfunction readNumAttr(el: Element, name: string): number | null {\n const v =\n el.getAttributeNS(NS.w, name) ?? el.getAttribute(`w:${name}`);\n if (v === null) return null;\n const n = Number.parseInt(v, 10);\n return Number.isFinite(n) ? n : null;\n}\n","/**\n * Parse `word/numbering.xml` into a `NumberingDefinition[]`.\n *\n * The OOXML numbering model has two layers:\n *\n * 1. `<w:abstractNum>` defines a list \"template\": one `<w:lvl>` per\n * indent level with the marker format (`decimal`, `bullet`, …),\n * the marker text template (`%1.`, `•`, …), and the paragraph\n * indent that goes with that level.\n * 2. `<w:num>` is a concrete list instance — it references an\n * `abstractNumId` and is what `<w:numPr>/<w:numId>` on a\n * paragraph points at.\n *\n * Two `<w:num>` instances can share an `abstractNumId` (Word does\n * this all the time — every fresh numbered list gets a new `numId`\n * but reuses an underlying abstract template). We flatten the\n * reference here so each `NumberingDefinition` carries its own\n * fully-resolved abstract format — simpler for consumers.\n *\n * What we read per level: `numFmt` (decimal / bullet / …), `lvlText`\n * (the marker template), `ind` (left + hanging, in twips). Run\n * properties on the marker (`rPr`) are dropped for now — Word uses\n * them mostly for symbol font on bullets, which we don't render\n * faithfully yet anyway.\n */\n\nimport type {\n NumberingDefinition,\n AbstractNumberingFormat,\n NumberingLevel,\n ParagraphIndent,\n} from \"../../doc/types\";\nimport { parseXml, wAll, wFirst, wVal } from \"../shared/xml\";\nimport { NS } from \"../shared/namespaces\";\n\nexport function parseNumberingXml(xml: string | undefined): NumberingDefinition[] {\n if (!xml) return [];\n let doc: Document;\n try {\n doc = parseXml(xml);\n } catch {\n return [];\n }\n\n // 1. Build the abstractNumId → AbstractNumberingFormat lookup.\n const abstracts = new Map<number, AbstractNumberingFormat>();\n for (const absEl of wAll(doc, \"abstractNum\")) {\n const idStr =\n absEl.getAttributeNS(NS.w, \"abstractNumId\") ??\n absEl.getAttribute(\"w:abstractNumId\");\n if (!idStr) continue;\n const id = Number.parseInt(idStr, 10);\n if (!Number.isFinite(id)) continue;\n abstracts.set(id, { levels: readLevels(absEl) });\n }\n\n // 2. Walk every <w:num>, resolve its abstractNumId, emit a\n // NumberingDefinition carrying the resolved format.\n const out: NumberingDefinition[] = [];\n for (const numEl of wAll(doc, \"num\")) {\n const numIdStr =\n numEl.getAttributeNS(NS.w, \"numId\") ?? numEl.getAttribute(\"w:numId\");\n if (!numIdStr) continue;\n const numId = Number.parseInt(numIdStr, 10);\n if (!Number.isFinite(numId)) continue;\n const ref = wFirst(numEl, \"abstractNumId\");\n const refIdStr = wVal(ref);\n if (!refIdStr) continue;\n const refId = Number.parseInt(refIdStr, 10);\n const abstractFormat = abstracts.get(refId);\n if (!abstractFormat) continue;\n out.push({ numId, abstractFormat });\n }\n\n return out;\n}\n\n// ---------- internals ----------\n\nfunction readLevels(absEl: Element): NumberingLevel[] {\n const out: NumberingLevel[] = [];\n for (const lvlEl of wAll(absEl, \"lvl\")) {\n const ilvlStr =\n lvlEl.getAttributeNS(NS.w, \"ilvl\") ?? lvlEl.getAttribute(\"w:ilvl\");\n if (!ilvlStr) continue;\n const ilvl = Number.parseInt(ilvlStr, 10);\n if (!Number.isFinite(ilvl)) continue;\n\n const numFmtRaw = wVal(wFirst(lvlEl, \"numFmt\"));\n let lvlText = wVal(wFirst(lvlEl, \"lvlText\")) ?? \"\";\n const restartStr = wVal(wFirst(lvlEl, \"lvlRestart\"));\n const restart = restartStr ? Number.parseInt(restartStr, 10) : undefined;\n\n const pPr = wFirst(lvlEl, \"pPr\");\n const paragraphIndent = pPr ? readIndent(pPr) : undefined;\n\n // Wingdings bullets: Word commonly encodes bullet glyphs as PUA\n // chars (U+F0xx) with `<w:rFonts w:ascii=\"Wingdings\"/>` on the\n // level's rPr. Browsers rarely have Wingdings installed and even\n // when they do, the PUA codepoint renders as a missing-glyph\n // square. Map the common Wingdings → standard-Unicode equivalents\n // so the bullet shows up visually correct without needing the\n // Wingdings font (e.g. `` → \"▪\" U+25AA BLACK SMALL SQUARE).\n const rPr = wFirst(lvlEl, \"rPr\");\n const rFonts = rPr ? wFirst(rPr, \"rFonts\") : null;\n const fontAscii = rFonts?.getAttribute(\"w:ascii\") ?? rFonts?.getAttribute(\"w:hAnsi\") ?? \"\";\n if (fontAscii.toLowerCase().includes(\"wingdings\") || fontAscii.toLowerCase().includes(\"symbol\")) {\n lvlText = mapSymbolFontCodepoints(lvlText, fontAscii);\n }\n\n const level: NumberingLevel = {\n level: ilvl,\n format: numFmtRaw ?? \"decimal\",\n text: lvlText,\n };\n if (restart !== undefined && Number.isFinite(restart)) level.restart = restart;\n if (paragraphIndent) level.paragraphIndent = paragraphIndent;\n out.push(level);\n }\n return out;\n}\n\nfunction readIndent(pPr: Element): ParagraphIndent | undefined {\n const ind = wFirst(pPr, \"ind\");\n if (!ind) return undefined;\n const out: ParagraphIndent = {};\n const left = readTwips(ind, \"left\");\n if (left !== null) out.leftTwips = left;\n const right = readTwips(ind, \"right\");\n if (right !== null) out.rightTwips = right;\n const firstLine = readTwips(ind, \"firstLine\");\n if (firstLine !== null) out.firstLineTwips = firstLine;\n const hanging = readTwips(ind, \"hanging\");\n if (hanging !== null) out.hangingTwips = hanging;\n return Object.keys(out).length > 0 ? out : undefined;\n}\n\nfunction readTwips(el: Element, name: string): number | null {\n const v =\n el.getAttributeNS(NS.w, name) ?? el.getAttribute(`w:${name}`);\n if (v === null) return null;\n const n = Number.parseInt(v, 10);\n return Number.isFinite(n) ? n : null;\n}\n\n/**\n * Translate Wingdings / Symbol PUA codepoints to their visual Unicode\n * equivalents so bullet markers render without needing the legacy\n * font installed.\n *\n * Word stores Wingdings glyphs as private-use characters in the\n * U+F000–U+F0FF range (codepoint = 0xF000 + glyph index). Browsers\n * either skip these (missing-glyph box) or, if Wingdings is somehow\n * available, render the correct shape — but bullet markers are\n * common enough that we just substitute the obvious ones.\n *\n * Only the bullets we've observed in real fixtures are mapped; an\n * unknown PUA char falls through unchanged so the renderer still\n * has a chance to draw something.\n */\nfunction mapSymbolFontCodepoints(text: string, font: string): string {\n const isSymbol = font.toLowerCase().startsWith(\"symbol\");\n return text.replace(/[-]/g, (ch) => {\n const cp = ch.charCodeAt(0);\n // Wingdings bullets — the lookup covers the markers that show up\n // in Word's built-in list templates (Office 365 + 2019+).\n const wingdings: Record<number, string> = {\n 0xf06c: \"●\", // BLACK CIRCLE — large dot\n 0xf06e: \"■\", // BLACK SQUARE — large filled square\n 0xf0a7: \"▪\", // BLACK SMALL SQUARE — bullet style used by Google CV template\n 0xf0a8: \"□\", // WHITE SQUARE\n 0xf076: \"❖\", // BLACK DIAMOND MINUS WHITE X\n 0xf0d8: \"▶\", // BLACK RIGHT-POINTING TRIANGLE\n 0xf0d9: \"▷\",\n 0xf0fc: \"✓\", // CHECK MARK\n 0xf0fb: \"✗\", // BALLOT X\n };\n if (wingdings[cp]) return wingdings[cp]!;\n // Symbol font: U+F0B7 is the \"•\" bullet — same shape as U+2022.\n if (isSymbol && cp === 0xf0b7) return \"•\";\n return ch;\n });\n}\n","/**\n * Parse `<w:drawing>` / `<w:pict>` anchored objects out of a document\n * XML and return them as a flat list of `AnchoredFrame`s.\n *\n * Sobree's older \"lifter\" architecture exploded anchored textboxes into\n * synthetic body paragraphs carrying `liftedFromTextBox` metadata, then\n * had the renderer reconstruct floating layout from per-paragraph\n * coordinates. That broke selection, broke multi-shape groups, and\n * coupled the paginator to absolute-positioned content it should never\n * have seen. This module returns frames as their own AST objects;\n * the renderer paints them into a per-paper overlay layer.\n *\n * What we DO parse:\n * - `<w:drawing><wp:anchor>` → top-level AnchoredFrame\n * - `<wpg:wgp>` inside an anchor → AnchoredContent of kind \"group\"\n * - `<wps:wsp>` with `<wps:txbx>` → \"textbox\" content\n * - `<wps:wsp>` with `<wps:spPr>`-only → \"shape\" content (rect / ellipse)\n * - `<pic:pic>` → \"picture\" content\n *\n * What we do NOT parse here (yet):\n * - `<wp:inline>` inline drawings — those stay as `DrawingRun`s in\n * paragraph runs, handled by the inline-run parser.\n * - `<w:pict>` VML — covered by a separate fallback path in a\n * follow-up step; same return type so the renderer is oblivious.\n *\n * Pure function. No side effects on the input XML. The lifter (legacy)\n * still runs alongside during Phase A; Phase B will delete it after\n * the renderer is verified working off this output.\n */\n\nimport type {\n AnchoredContent,\n AnchoredFrame,\n AnchorOrigin,\n Block,\n} from \"../../doc/types\";\nimport { NS } from \"../shared/namespaces\";\n\nexport interface AnchoredFramesContext {\n /** RelationshipId → part path lookup, e.g. `\"rId4\" → \"media/image1.png\"`. */\n rels: Map<string, string>;\n /**\n * Importer's body-block list AT THE TIME this function runs. Used to\n * resolve `paragraphIndex` for the AnchorOrigin: each frame is\n * attributed to the body paragraph that contained its `<w:drawing>`,\n * so the renderer knows which page receives the frame after\n * pagination. May be empty during early-pass parsing; callers can\n * pass `[]` and the renderer will treat all frames as section-relative.\n */\n bodyParagraphIndexByElement?: Map<Element, number>;\n /**\n * Recursive body walker for `<w:txbxContent>`, injected by the caller\n * to avoid an `anchoredFrames ↔ document` import cycle. When present,\n * textbox bodies parse through the SAME pipeline as the document body\n * — real run formatting, paragraph spacing, lists, tables — so a\n * frame whose content flows into the body (see `flowFrames`) keeps\n * its true layout. When absent, falls back to flat text (tests).\n */\n parseBlockBody?: (txbxContent: Element) => Block[];\n}\n\n/**\n * Walk every `<w:drawing>/<wp:anchor>` in the document and return one\n * `AnchoredFrame` per top-level anchored drawing. The returned list is\n * in document order, which matters for z-stacking when frames\n * overlap (later siblings paint on top).\n *\n * The frame's `id` is deterministic: `\"anchor-{N}\"` where N is its\n * document-order index. Selection / persistence rely on this being\n * stable across re-imports of the same source.\n */\nexport function parseAnchoredFrames(\n xmlDoc: Document,\n ctx: AnchoredFramesContext,\n claim = true,\n): AnchoredFrame[] {\n const out: AnchoredFrame[] = [];\n const claimed: Element[] = [];\n const drawings = Array.from(xmlDoc.getElementsByTagNameNS(NS.w, \"drawing\"));\n let counter = 0;\n for (const drawing of drawings) {\n const anchor = firstChildNS(drawing, NS.wp, \"anchor\");\n if (!anchor) continue; // Inline drawings handled elsewhere.\n const frame = parseAnchoredFrame(anchor, drawing, ctx, () => `anchor-${counter++}`);\n if (frame) {\n out.push(frame);\n claimed.push(drawing);\n }\n }\n // Claim pass: remove each successfully-parsed anchored drawing from\n // the XML so the legacy `liftTextBoxContent` can't also lift its\n // textbox content into body flow (which would double-render the\n // text — once in the floating anchor layer, once in the body).\n // Anchored content belongs in the floating overlay, not in flow.\n // `claim: false` lets unit tests inspect frames without mutating XML.\n if (claim) {\n for (const drawing of claimed) drawing.parentNode?.removeChild(drawing);\n }\n return out;\n}\n\n// === implementation ===\n\nfunction parseAnchoredFrame(\n anchor: Element,\n drawing: Element,\n ctx: AnchoredFramesContext,\n nextId: () => string,\n): AnchoredFrame | null {\n const origin = readAnchorOrigin(anchor, drawing, ctx);\n const extent = firstChildNS(anchor, NS.wp, \"extent\");\n if (!extent) return null;\n const widthEmu = numAttr(extent, \"cx\");\n const heightEmu = numAttr(extent, \"cy\");\n if (widthEmu <= 0 || heightEmu <= 0) return null;\n\n const offset = readAnchorOffset(anchor);\n\n const graphicData = anchor.getElementsByTagNameNS(NS.a, \"graphicData\")[0];\n if (!graphicData) return null;\n const content = parseGraphicData(graphicData, ctx, nextId);\n if (!content) return null;\n\n const behindAttr = anchor.getAttribute(\"behindDoc\");\n const out: AnchoredFrame = {\n id: nextId(),\n anchor: origin,\n offsetXEmu: offset.x,\n offsetYEmu: offset.y,\n widthEmu,\n heightEmu,\n content,\n };\n if (behindAttr === \"1\" || behindAttr === \"true\") out.behindText = true;\n const wrap = readWrapType(anchor);\n if (wrap) out.wrap = wrap;\n return out;\n}\n\n/**\n * The text-wrap mode lives as a dedicated child of `<wp:anchor>`:\n * `<wp:wrapSquare>`, `<wp:wrapTopAndBottom>`, `<wp:wrapTight>`,\n * `<wp:wrapThrough>`, or `<wp:wrapNone>`. Returns the mapped enum or\n * `undefined` when no wrap element is present.\n */\nfunction readWrapType(anchor: Element): AnchoredFrame[\"wrap\"] | undefined {\n for (const child of Array.from(anchor.children)) {\n if (child.namespaceURI !== NS.wp) continue;\n switch (child.localName) {\n case \"wrapSquare\":\n return \"square\";\n case \"wrapTopAndBottom\":\n return \"topAndBottom\";\n case \"wrapTight\":\n return \"tight\";\n case \"wrapThrough\":\n return \"through\";\n case \"wrapNone\":\n return \"none\";\n }\n }\n return undefined;\n}\n\nfunction parseGraphicData(\n graphicData: Element,\n ctx: AnchoredFramesContext,\n nextId: () => string,\n): AnchoredContent | null {\n // Three possible shapes:\n // 1. <wpg:wgp> → group of children\n // 2. <wps:wsp> → single shape (textbox or geometric)\n // 3. <pic:pic> → single picture\n // We probe in that order and return the first match.\n const wpg = firstChildNS(graphicData, NS.wpg, \"wgp\");\n if (wpg) return parseGroup(wpg, ctx, nextId);\n\n const wsp = firstChildNS(graphicData, NS.wps, \"wsp\");\n if (wsp) return parseShape(wsp, ctx);\n\n const pic = firstChildNS(graphicData, NS.pic, \"pic\");\n if (pic) return parsePicture(pic, ctx);\n\n return null;\n}\n\nfunction parseGroup(\n wpg: Element,\n ctx: AnchoredFramesContext,\n nextId: () => string,\n): AnchoredContent {\n // The group's own coordinate-system extent lives on\n // `<wpg:grpSpPr><a:xfrm><a:chExt cx cy>`. Children's offsets are\n // expressed in this space, then the group itself can be drawn at\n // any size — the renderer scales.\n const grpSpPr = firstChildNS(wpg, NS.wpg, \"grpSpPr\");\n const xfrm = grpSpPr ? grpSpPr.getElementsByTagNameNS(NS.a, \"xfrm\")[0] : undefined;\n const chExt = xfrm ? xfrm.getElementsByTagNameNS(NS.a, \"chExt\")[0] : undefined;\n const childCoordSystemCx = chExt ? numAttr(chExt, \"cx\") : 0;\n const childCoordSystemCy = chExt ? numAttr(chExt, \"cy\") : 0;\n\n const children: AnchoredFrame[] = [];\n for (const child of Array.from(wpg.children)) {\n if (child.namespaceURI === NS.wps && child.localName === \"wsp\") {\n const frame = synthFrameFromShape(child, ctx, nextId);\n if (frame) children.push(frame);\n } else if (child.namespaceURI === NS.pic && child.localName === \"pic\") {\n const frame = synthFrameFromPicture(child, ctx, nextId);\n if (frame) children.push(frame);\n } else if (child.namespaceURI === NS.wpg && child.localName === \"grpSp\") {\n // Nested group. Recursively flatten — represented as a child\n // group frame with its own coordinate system.\n const frame = synthFrameFromNestedGroup(child, ctx, nextId);\n if (frame) children.push(frame);\n }\n }\n\n return {\n kind: \"group\",\n children,\n childCoordSystemCx,\n childCoordSystemCy,\n };\n}\n\nfunction synthFrameFromShape(\n wsp: Element,\n ctx: AnchoredFramesContext,\n nextId: () => string,\n): AnchoredFrame | null {\n const { off, ext } = readSpPrXfrm(wsp);\n if (!ext) return null;\n const content = parseShape(wsp, ctx);\n return {\n id: nextId(),\n anchor: { sectionIndex: 0, horizontalFrom: \"page\", verticalFrom: \"page\" },\n offsetXEmu: off?.x ?? 0,\n offsetYEmu: off?.y ?? 0,\n widthEmu: ext.cx,\n heightEmu: ext.cy,\n content,\n };\n}\n\nfunction synthFrameFromPicture(\n pic: Element,\n ctx: AnchoredFramesContext,\n nextId: () => string,\n): AnchoredFrame | null {\n const { off, ext } = readSpPrXfrm(pic);\n const content = parsePicture(pic, ctx);\n if (!content) return null;\n if (!ext) return null;\n return {\n id: nextId(),\n anchor: { sectionIndex: 0, horizontalFrom: \"page\", verticalFrom: \"page\" },\n offsetXEmu: off?.x ?? 0,\n offsetYEmu: off?.y ?? 0,\n widthEmu: ext.cx,\n heightEmu: ext.cy,\n content,\n };\n}\n\nfunction synthFrameFromNestedGroup(\n grpSp: Element,\n ctx: AnchoredFramesContext,\n nextId: () => string,\n): AnchoredFrame | null {\n const { off, ext } = readSpPrXfrm(grpSp);\n if (!ext) return null;\n // Treat the nested grpSp as a wpg-like body for the recursion.\n const content = parseGroup(grpSp, ctx, nextId);\n return {\n id: nextId(),\n anchor: { sectionIndex: 0, horizontalFrom: \"page\", verticalFrom: \"page\" },\n offsetXEmu: off?.x ?? 0,\n offsetYEmu: off?.y ?? 0,\n widthEmu: ext.cx,\n heightEmu: ext.cy,\n content,\n };\n}\n\nfunction parseShape(wsp: Element, ctx: AnchoredFramesContext): AnchoredContent {\n // Textbox if there's `<wps:txbx><w:txbxContent>`. Otherwise a\n // geometric shape — `<wps:spPr><a:prstGeom prst=\"...\">` carries\n // the shape kind.\n const txbx = firstChildNS(wsp, NS.wps, \"txbx\");\n if (txbx) {\n const txbxContent = firstChildNS(txbx, NS.w, \"txbxContent\");\n if (txbxContent) {\n const out: AnchoredContent = {\n kind: \"textbox\",\n body: parseTextboxBody(txbxContent, ctx),\n };\n const fill = readSolidFill(wsp);\n if (fill !== undefined) out.fill = fill;\n const border = readBorder(wsp);\n if (border !== undefined) out.border = border;\n return out;\n }\n }\n const out: AnchoredContent = {\n kind: \"shape\",\n geometry: readGeometry(wsp),\n };\n const fill = readSolidFill(wsp);\n if (fill !== undefined) out.fill = fill;\n const border = readBorder(wsp);\n if (border !== undefined) out.border = border;\n return out;\n}\n\nfunction parsePicture(pic: Element, ctx: AnchoredFramesContext): AnchoredContent | null {\n const blip = pic.getElementsByTagNameNS(NS.a, \"blip\")[0];\n if (!blip) return null;\n const rId =\n blip.getAttributeNS(NS.r, \"embed\") ?? blip.getAttribute(\"r:embed\");\n if (!rId) return null;\n const target = ctx.rels.get(rId);\n if (!target) return null;\n const partPath = normalizePartPath(target);\n // <pic:nvPicPr><pic:cNvPr descr=\"...\"/>\n const cNvPr = pic.getElementsByTagNameNS(NS.pic, \"cNvPr\")[0];\n const altText = cNvPr?.getAttribute(\"descr\");\n const out: AnchoredContent = { kind: \"picture\", partPath };\n if (altText) out.altText = altText;\n return out;\n}\n\n/**\n * Parse a `<w:txbxContent>` into body blocks. Prefers the injected\n * full walker (`ctx.parseBlockBody`) so the content carries its real\n * run formatting, paragraph spacing, lists, and tables — essential\n * once a textbox flows into the body (`flowFrames`), and faithful for\n * overlays too. Falls back to flat text-only paragraphs when no walker\n * is injected (unit tests that exercise the parser in isolation).\n */\nfunction parseTextboxBody(\n txbxContent: Element,\n ctx: AnchoredFramesContext,\n): Block[] {\n if (ctx.parseBlockBody) return ctx.parseBlockBody(txbxContent);\n const out: Block[] = [];\n for (const child of Array.from(txbxContent.children)) {\n if (child.namespaceURI !== NS.w) continue;\n if (child.localName === \"p\") {\n const text = (child.textContent ?? \"\").trim();\n out.push({\n kind: \"paragraph\",\n runs: text ? [{ kind: \"text\", text, properties: {} }] : [],\n properties: {},\n });\n }\n }\n return out;\n}\n\n// === low-level OOXML readers ===\n\nfunction readAnchorOrigin(\n anchor: Element,\n drawing: Element,\n ctx: AnchoredFramesContext,\n): AnchorOrigin {\n const posH = firstChildNS(anchor, NS.wp, \"positionH\");\n const posV = firstChildNS(anchor, NS.wp, \"positionV\");\n const horizontalFrom = posH\n ? coerceHRelativeFrom(posH.getAttribute(\"relativeFrom\"))\n : \"page\";\n const verticalFrom = posV\n ? coerceVRelativeFrom(posV.getAttribute(\"relativeFrom\"))\n : \"page\";\n\n // Walk up from the drawing to its containing paragraph and look it\n // up in the caller's `bodyParagraphIndexByElement` map. When the\n // drawing isn't anchored to a body paragraph (e.g. it sits inside\n // a header/footer), the index stays undefined and the renderer\n // treats the frame as section-relative.\n let paragraphIndex: number | undefined;\n if (ctx.bodyParagraphIndexByElement) {\n let p: Element | null = drawing.parentElement;\n while (p && !(p.namespaceURI === NS.w && p.localName === \"p\")) {\n p = p.parentElement;\n }\n if (p) paragraphIndex = ctx.bodyParagraphIndexByElement.get(p);\n }\n\n const origin: AnchorOrigin = {\n sectionIndex: 0,\n horizontalFrom,\n verticalFrom,\n };\n if (paragraphIndex !== undefined) origin.paragraphIndex = paragraphIndex;\n return origin;\n}\n\nfunction readAnchorOffset(anchor: Element): { x: number; y: number } {\n const posH = firstChildNS(anchor, NS.wp, \"positionH\");\n const posV = firstChildNS(anchor, NS.wp, \"positionV\");\n return {\n x: readPosOffset(posH),\n y: readPosOffset(posV),\n };\n}\n\nfunction readPosOffset(positionEl: Element | null): number {\n if (!positionEl) return 0;\n const posOffset = firstChildNS(positionEl, NS.wp, \"posOffset\");\n if (!posOffset) return 0;\n const n = Number(posOffset.textContent ?? \"0\");\n return Number.isFinite(n) ? n : 0;\n}\n\nfunction readSpPrXfrm(shape: Element): {\n off?: { x: number; y: number };\n ext?: { cx: number; cy: number };\n} {\n // Shape's own offset/extent live on `*:spPr > a:xfrm`. The wrapping\n // tag varies — wps:spPr for shapes, pic:spPr for pictures,\n // wpg:grpSpPr for nested groups — but the a:xfrm child is universal.\n const xfrms = Array.from(shape.getElementsByTagNameNS(NS.a, \"xfrm\"));\n // Pick the SHALLOWEST xfrm — the one that's a direct grandchild of\n // the shape, not one nested deeper (e.g. inside a pic's body).\n let best: Element | undefined;\n let bestDepth = Infinity;\n for (const x of xfrms) {\n let depth = 0;\n let cur: Element | null = x;\n while (cur && cur !== shape) {\n depth++;\n cur = cur.parentElement;\n }\n if (depth < bestDepth) {\n bestDepth = depth;\n best = x;\n }\n }\n if (!best) return {};\n const offEl = best.getElementsByTagNameNS(NS.a, \"off\")[0];\n const extEl = best.getElementsByTagNameNS(NS.a, \"ext\")[0];\n const out: { off?: { x: number; y: number }; ext?: { cx: number; cy: number } } = {};\n if (offEl) out.off = { x: numAttr(offEl, \"x\"), y: numAttr(offEl, \"y\") };\n if (extEl) out.ext = { cx: numAttr(extEl, \"cx\"), cy: numAttr(extEl, \"cy\") };\n return out;\n}\n\nfunction readGeometry(\n wsp: Element,\n): \"rect\" | \"ellipse\" | \"roundedRect\" | \"line\" {\n const prstGeom = wsp.getElementsByTagNameNS(NS.a, \"prstGeom\")[0];\n const prst = prstGeom?.getAttribute(\"prst\");\n switch (prst) {\n case \"ellipse\":\n return \"ellipse\";\n case \"roundRect\":\n return \"roundedRect\";\n case \"line\":\n case \"straightConnector1\":\n return \"line\";\n case \"rect\":\n default:\n return \"rect\";\n }\n}\n\nfunction readSolidFill(shape: Element): string | undefined {\n // First `<a:solidFill><a:srgbClr val=\"RRGGBB\"/></a:solidFill>` inside\n // the shape's spPr. Theme colours / scheme references intentionally\n // unsupported — fall through to undefined (renderer treats as no fill).\n const spPr =\n firstChildNS(shape, NS.wps, \"spPr\") ??\n firstChildNS(shape, NS.pic, \"spPr\");\n if (!spPr) return undefined;\n // Use direct descendant traversal so we don't pick up a fill nested\n // deeper inside a child shape.\n for (const fill of Array.from(spPr.children)) {\n if (fill.namespaceURI === NS.a && fill.localName === \"solidFill\") {\n const srgb = firstChildNS(fill, NS.a, \"srgbClr\");\n const val = srgb?.getAttribute(\"val\");\n if (val && /^[0-9A-Fa-f]{6}$/.test(val)) return `#${val.toUpperCase()}`;\n }\n }\n return undefined;\n}\n\nfunction readBorder(shape: Element):\n | { color: string; widthEmu: number; style: \"solid\" | \"dashed\" | \"dotted\" | \"double\" }\n | undefined {\n const spPr =\n firstChildNS(shape, NS.wps, \"spPr\") ??\n firstChildNS(shape, NS.pic, \"spPr\");\n if (!spPr) return undefined;\n const ln = firstChildNS(spPr, NS.a, \"ln\");\n if (!ln) return undefined;\n const widthEmu = numAttr(ln, \"w\");\n const solidFill = firstChildNS(ln, NS.a, \"solidFill\");\n const srgb = solidFill ? firstChildNS(solidFill, NS.a, \"srgbClr\") : null;\n const val = srgb?.getAttribute(\"val\");\n if (!val || !/^[0-9A-Fa-f]{6}$/.test(val)) return undefined;\n const prstDash = firstChildNS(ln, NS.a, \"prstDash\");\n const style = coerceBorderStyle(prstDash?.getAttribute(\"val\"));\n return { color: `#${val.toUpperCase()}`, widthEmu: widthEmu || 0, style };\n}\n\nfunction coerceBorderStyle(\n v: string | null | undefined,\n): \"solid\" | \"dashed\" | \"dotted\" | \"double\" {\n switch (v) {\n case \"dash\":\n case \"lgDash\":\n case \"sysDash\":\n return \"dashed\";\n case \"dot\":\n case \"sysDot\":\n return \"dotted\";\n default:\n return \"solid\";\n }\n}\n\nfunction coerceHRelativeFrom(v: string | null): \"page\" | \"margin\" | \"column\" {\n switch (v) {\n case \"page\":\n case \"margin\":\n case \"column\":\n return v;\n default:\n return \"page\";\n }\n}\n\nfunction coerceVRelativeFrom(v: string | null): \"page\" | \"margin\" | \"paragraph\" {\n switch (v) {\n case \"page\":\n case \"margin\":\n case \"paragraph\":\n return v;\n default:\n return \"page\";\n }\n}\n\nfunction numAttr(el: Element, name: string): number {\n const n = Number(el.getAttribute(name) ?? \"0\");\n return Number.isFinite(n) ? n : 0;\n}\n\nfunction firstChildNS(parent: Element, ns: string, local: string): Element | null {\n for (const child of Array.from(parent.children)) {\n if (child.namespaceURI === ns && child.localName === local) return child;\n }\n return null;\n}\n\nfunction normalizePartPath(target: string): string {\n if (target.startsWith(\"/\")) return target.slice(1);\n if (target.startsWith(\"word/\")) return target;\n return `word/${target}`;\n}\n","/**\n * In-flow conversion for displacing anchored textboxes.\n *\n * Some `<wp:anchor>` drawings are floating *decorations* (a photo\n * placeholder, a watermark) — they belong in the absolute overlay\n * layer. But others are really just **framed body content**: a\n * wrapping textbox (or a group whose payload is textboxes) anchored to\n * a paragraph, carrying a chunk of the document's prose. Word lays\n * those out in the text flow — they push following content down and,\n * crucially, **paginate** when they're taller than the page.\n *\n * Sobree renders anchored frames as a single absolute overlay clipped\n * to its box, and the paginator only splits in-flow content — so a\n * tall content textbox clips at the page bottom instead of continuing\n * on the next page (complex-multipage's project writeups). The fix is\n * to recognise these for what they are and splice their text back into\n * `doc.body` at the anchor paragraph, dropping the overlay. From then\n * on they're ordinary blocks: they flow, paginate, edit, and persist\n * like any other paragraph.\n *\n * Scope is deliberately narrow — only frames that:\n * - displace text (`square`/`topAndBottom`/`tight`/`through` wrap,\n * in front of the text, paragraph-anchored), AND\n * - carry textbox body content, AND\n * - have no box chrome (fill/border) worth preserving.\n * Everything else (pictures, shapes, behind-text, `wrapNone` floats,\n * bordered/filled boxes) stays an overlay, unchanged.\n */\n\nimport type {\n AnchoredFrame,\n AnchoredContent,\n Block,\n DrawingRun,\n Paragraph,\n} from \"../../doc/types\";\n\n/** A frame whose content should flow inline rather than overlay. */\nfunction isFlowable(frame: AnchoredFrame): boolean {\n if (frame.behindText) return false;\n if (frame.anchor.verticalFrom !== \"paragraph\") return false;\n if (frame.anchor.paragraphIndex === undefined) return false;\n switch (frame.wrap) {\n case \"square\":\n case \"topAndBottom\":\n case \"tight\":\n case \"through\":\n break;\n default:\n return false;\n }\n if (hasChrome(frame.content)) return false;\n return hasTextboxContent(frame.content);\n}\n\n/** True when the content paints a VISIBLE box (a border, or a fill\n * that isn't white) that the flat in-flow form would lose — keep\n * those as overlays. A white fill is invisible on the page, so it\n * doesn't count (the \"Project: X\" heading boxes carry `#FFFFFF`). */\nfunction hasChrome(content: AnchoredContent): boolean {\n switch (content.kind) {\n case \"textbox\":\n case \"shape\":\n if (content.border !== undefined) return true;\n return content.fill !== undefined && !isWhite(content.fill);\n case \"group\":\n return content.children.some((c) => hasChrome(c.content));\n default:\n return false;\n }\n}\n\n/** Treat white (Word's default textbox fill) as no visible chrome. */\nfunction isWhite(color: string): boolean {\n const c = color.trim().toLowerCase();\n return c === \"#ffffff\" || c === \"#fff\" || c === \"white\";\n}\n\n/** True when a frame carries any textbox prose (recursively). Cheap\n * predicate for `isFlowable` — the heavy flatten runs only on keepers. */\nfunction hasTextboxContent(content: AnchoredContent): boolean {\n switch (content.kind) {\n case \"textbox\":\n return true;\n case \"group\":\n return content.children.some((c) => hasTextboxContent(c.content));\n default:\n return false;\n }\n}\n\n/**\n * Flatten a frame's content into body blocks in document order.\n * Textbox bodies contribute their paragraphs; groups recurse\n * depth-first (a project group emits its heading textbox then its\n * detail textbox). A decorative picture (the ► project arrow) is\n * carried into the flow as a leading inline image on the next\n * paragraph — so it survives, at its TRUE size.\n *\n * Picture sizing follows the group coordinate chain: a group's\n * children live in its local system (`childCoordSystemCx/Cy`) and\n * render at the frame's extent, so each level multiplies the scale\n * (`frameSize / childCoordSystem`). The scale is applied per-axis —\n * the project group's `ext` is ~3.6× taller than its `chExt`, and that\n * anisotropic factor is exactly what restores the ► arrow's authored\n * shape: its `ext` (≈2.7:1 wide) is squished in the group's local\n * space, and the vertical scale un-squishes it back to the source\n * image's true 0.72:1 (36×50), matching how Word/LO render it.\n */\nfunction flattenFrame(frame: AnchoredFrame): Block[] {\n return flattenContent(frame.content, frame.widthEmu, frame.heightEmu);\n}\n\nfunction flattenContent(\n content: AnchoredContent,\n renderedWidthEmu: number,\n renderedHeightEmu: number,\n): Block[] {\n switch (content.kind) {\n case \"textbox\":\n return content.body;\n case \"group\": {\n const sx =\n content.childCoordSystemCx > 0 ? renderedWidthEmu / content.childCoordSystemCx : 1;\n const sy =\n content.childCoordSystemCy > 0 ? renderedHeightEmu / content.childCoordSystemCy : 1;\n return flattenChildren(content.children, sx, sy);\n }\n default:\n return [];\n }\n}\n\nfunction flattenChildren(\n frames: readonly AnchoredFrame[],\n scaleX: number,\n scaleY: number,\n): Block[] {\n const out: Block[] = [];\n let pending: DrawingRun[] = [];\n for (const frame of frames) {\n const renderedW = frame.widthEmu * scaleX;\n const renderedH = frame.heightEmu * scaleY;\n const content = frame.content;\n if (content.kind === \"picture\") {\n pending.push(toInlineImage(content, renderedW, renderedH));\n } else if (content.kind === \"textbox\") {\n out.push(...withLeadingImages(content.body, pending));\n pending = [];\n } else if (content.kind === \"group\") {\n out.push(...withLeadingImages(flattenContent(content, renderedW, renderedH), pending));\n pending = [];\n }\n // shapes contribute nothing\n }\n return out;\n}\n\n/** An anchored picture → an inline image run at its rendered size. */\nfunction toInlineImage(\n content: Extract<AnchoredContent, { kind: \"picture\" }>,\n widthEmu: number,\n heightEmu: number,\n): DrawingRun {\n const run: DrawingRun = {\n kind: \"drawing\",\n partPath: content.partPath,\n widthEmu: Math.round(widthEmu),\n heightEmu: Math.round(heightEmu),\n placement: \"inline\",\n // The arrow is a heading decoration taller than its label; centre\n // it on the text so the label sits beside its middle, matching how\n // Word renders the (vertically-stretched) heading textbox.\n verticalAlign: \"middle\",\n };\n if (content.altText !== undefined) run.altText = content.altText;\n return run;\n}\n\n/**\n * Prepend `images` to the first paragraph in `blocks` (cloned, so the\n * source AST isn't mutated). When there's no paragraph or no images,\n * the blocks pass through unchanged.\n */\nfunction withLeadingImages(blocks: Block[], images: DrawingRun[]): Block[] {\n if (images.length === 0) return blocks;\n const idx = blocks.findIndex((b) => b.kind === \"paragraph\");\n if (idx === -1) return blocks;\n const target = blocks[idx] as Paragraph;\n const merged: Paragraph = { ...target, runs: [...images, ...target.runs] };\n const out = blocks.slice();\n out[idx] = merged;\n return out;\n}\n\n/**\n * Splice flowable frames' content into `body` at their anchor\n * paragraph and drop them from the overlay set. Returns the rebuilt\n * body and the frames that remain overlays (with `paragraphIndex`\n * remapped to the new body positions). Pure — no mutation of inputs.\n */\nexport function flowDisplacingTextboxes(\n body: readonly Block[],\n frames: readonly AnchoredFrame[],\n): { body: Block[]; frames: AnchoredFrame[] } {\n const flowable = frames.filter(isFlowable);\n if (flowable.length === 0) {\n return { body: body.slice(), frames: frames.slice() };\n }\n\n // Group flowable frames by their anchor paragraph (document order\n // preserved: a paragraph hosting several frames emits them in array\n // order).\n const byAnchor = new Map<number, AnchoredFrame[]>();\n for (const frame of flowable) {\n const pi = frame.anchor.paragraphIndex as number;\n const bucket = byAnchor.get(pi);\n if (bucket) bucket.push(frame);\n else byAnchor.set(pi, [frame]);\n }\n\n // Rebuild the body, inserting each anchor paragraph's flowed content\n // right after it. Track old→new index so the remaining overlay\n // frames' `paragraphIndex` stays valid.\n const newBody: Block[] = [];\n const oldToNew = new Map<number, number>();\n for (let i = 0; i < body.length; i++) {\n oldToNew.set(i, newBody.length);\n newBody.push(body[i]!);\n const here = byAnchor.get(i);\n if (here) {\n for (const frame of here) newBody.push(...flattenFrame(frame));\n }\n }\n\n const flowedIds = new Set(flowable.map((f) => f.id));\n const remaining: AnchoredFrame[] = [];\n for (const frame of frames) {\n if (flowedIds.has(frame.id)) continue;\n const pi = frame.anchor.paragraphIndex;\n const remapped = pi !== undefined ? oldToNew.get(pi) : undefined;\n if (remapped !== undefined && remapped !== pi) {\n remaining.push({ ...frame, anchor: { ...frame.anchor, paragraphIndex: remapped } });\n } else {\n remaining.push(frame);\n }\n }\n\n return { body: newBody, frames: remaining };\n}\n","/**\n * Parse `<w:drawing><wp:inline>` payloads carrying a textbox into a\n * list of `InlineFrame` blocks — one frame per top-level inline\n * drawing in the document.\n *\n * See `packages/core/docs/INLINE_FRAME_DESIGN.md` for the design.\n *\n * **Phase 1.1 status**: this parser is pure and complete; it returns\n * `{ frame, drawingEl, hostParagraphEl }` tuples so the import\n * pipeline can both insert frames into the body AND mark their\n * source drawings so the legacy `liftTextBoxContent` skips them.\n * Phase 1.2 wires the renderer; Phase 1.4 deletes the lifter.\n *\n * What we DO parse:\n * - `<w:drawing><wp:inline>` containing `<wpg:wgp>` with at least\n * one `<wps:wsp><wps:txbx>` — section headings, \"Project: X\"\n * frames, every textbox-bearing inline drawing.\n * - The CONTAINING `<w:p>`'s `<w:pPr>` page-break-before /\n * keep-next directives — they semantically belong to the frame,\n * not the inner content.\n * - Sibling `<pic:pic>` decorations inside the group.\n * - Sibling `<wps:wsp>` shapes (rect / ellipse / line) WITHOUT a\n * textbox payload — pure decoration.\n * - Each textbox's intra-group offset+size from its `<wps:spPr>`\n * `<a:xfrm>`.\n *\n * What we do NOT parse:\n * - `<wp:anchor>` drawings (absolute-positioned) — `anchoredFrames.ts`\n * handles those.\n * - Inline drawings with ONLY a picture (no group, no textbox) —\n * those stay as `DrawingRun` in the paragraph's inline runs.\n * - `<w:pict>` VML legacy fallback.\n *\n * Recursive body parsing is delegated to a caller-supplied\n * `parseBlockBody(txbxContent)` so this module doesn't depend on\n * the body-paragraph walker (which lives in `paragraph.ts`).\n */\n\nimport type { Block, FrameBorder, InlineFrame } from \"../../doc/types\";\nimport { NS } from \"../shared/namespaces\";\n\nexport interface InlineFramesContext {\n /** RelationshipId → part path lookup. */\n rels: Map<string, string>;\n /**\n * Recursive body parser supplied by the caller. The textbox content\n * (`<w:txbxContent>`) is a body of `<w:p>` / `<w:tbl>` children\n * that should parse with the same rules as the document body —\n * paragraph properties, runs, tables, even nested inline frames.\n * Phase 1.1: callers can pass a simple text-only stub; Phase 1.2+\n * will pass the full body walker.\n */\n parseBlockBody: (txbxContent: Element) => Block[];\n /**\n * When true, `<w:lastRenderedPageBreak/>` HINTS inside the textbox\n * content cascade up to set `InlineFrame.pageBreakBefore`. These\n * are stale layout hints Word writes during save, not author-\n * declared directives — ECMA-376 says consumers SHOULD ignore\n * them for layout. We respect them in two cases:\n * 1. The body walker already opted in (heavily-decorated CVs\n * where the hints reliably match LO's reference pagination —\n * threshold is `≥10` total LRPB elements in the doc, decided\n * by `convertDocumentXml` and threaded through here).\n * 2. The frame contains an explicit `<w:pageBreakBefore/>` in\n * the outer paragraph's pPr (always honoured).\n * Without this flag, only explicit directives count.\n */\n honorLastRenderedPageBreaks?: boolean;\n}\n\n/**\n * One InlineFrame plus the source DOM nodes it came from.\n *\n * `drawingEl` is the `<w:drawing>` the importer should TREAT AS\n * REMOVED (legacy lifter will skip it; renderer paints from `frame`).\n *\n * `hostParagraphEl` is the `<w:p>` that contained the drawing — its\n * outer `<w:pPr>` props (pageBreakBefore, keepNext) flowed into the\n * frame. After the new path takes over, this paragraph becomes\n * empty in the source; the importer can treat the InlineFrame as\n * REPLACING it in the body block stream.\n */\nexport interface ParsedInlineFrame {\n frame: InlineFrame;\n drawingEl: Element;\n hostParagraphEl: Element;\n}\n\n/**\n * Walk every `<w:drawing>/<wp:inline>` in the document. For drawings\n * whose payload includes at least one `<wps:txbx>`, emit one\n * `InlineFrame`. Returns the frames in document order.\n *\n * When `claim` is true (the default), each claimed `<w:drawing>` is\n * REMOVED from the input XML so the legacy `liftTextBoxContent` pass\n * downstream can't double-process it. The host paragraph stays in\n * place (now empty) so the body walker still emits a paragraph\n * block at the right position — which the importer then swaps for\n * the corresponding `InlineFrame` via `ConvertOptions.replaceParagraphs`.\n *\n * Set `claim: false` to inspect frames without mutating the XML\n * (used by unit tests).\n */\nexport function parseInlineFrames(\n xmlDoc: Document,\n ctx: InlineFramesContext,\n claim = true,\n): ParsedInlineFrame[] {\n const out: ParsedInlineFrame[] = [];\n const drawings = Array.from(xmlDoc.getElementsByTagNameNS(NS.w, \"drawing\"));\n let counter = 0;\n for (const drawing of drawings) {\n const inline = firstChildNS(drawing, NS.wp, \"inline\");\n if (!inline) continue; // Anchored drawings handled by anchoredFrames.ts\n\n const graphicData = firstNS(inline, NS.a, \"graphicData\");\n if (!graphicData) continue;\n const wpg = firstChildNS(graphicData, NS.wpg, \"wgp\");\n if (!wpg) continue; // No group → handled as plain DrawingRun inline.\n\n // The wgp must contain at least one textbox shape for this\n // parser to claim the drawing. Pure-picture or pure-shape\n // groups (no <wps:txbx>) stay with the legacy DrawingRun path\n // until a later phase migrates those too.\n const wsps = directChildrenNS(wpg, NS.wps, \"wsp\");\n const hasTextbox = wsps.some((w) => firstChildNS(w, NS.wps, \"txbx\") !== null);\n if (!hasTextbox) continue;\n\n const hostP = findAncestor(drawing, NS.w, \"p\");\n if (!hostP) continue;\n\n const frame = buildInlineFrame(wpg, hostP, ctx, () => `inline-${counter++}`);\n if (!frame) continue;\n out.push({ frame, drawingEl: drawing, hostParagraphEl: hostP });\n }\n\n // Claim pass: remove each successfully-parsed drawing AFTER the\n // whole walk so we don't disturb live DOM iteration. The host\n // paragraph stays (often becomes empty); the body walker emits\n // a Paragraph block at its position; the importer's\n // `replaceParagraphs` map then swaps that block for the InlineFrame.\n if (claim) {\n for (const { drawingEl } of out) {\n drawingEl.parentNode?.removeChild(drawingEl);\n }\n }\n\n return out;\n}\n\n// === implementation ===\n\nfunction buildInlineFrame(\n wpg: Element,\n hostP: Element,\n ctx: InlineFramesContext,\n _nextId: () => string,\n): InlineFrame | null {\n // Group's intrinsic coordinate-system extent (where children's\n // offsets live). `<wpg:grpSpPr><a:xfrm><a:chExt>` is the\n // canonical place; `<a:ext>` on the same xfrm is the group's\n // RENDERED size — usually the same as chExt for inline drawings\n // but kept separate per the OOXML model.\n const grpSpPr = firstChildNS(wpg, NS.wpg, \"grpSpPr\");\n const groupXfrm = grpSpPr\n ? firstNS(grpSpPr, NS.a, \"xfrm\")\n : undefined;\n const chExt = groupXfrm ? firstNS(groupXfrm, NS.a, \"chExt\") : undefined;\n const ext = groupXfrm ? firstNS(groupXfrm, NS.a, \"ext\") : undefined;\n const groupExtentEmu = chExt\n ? { wEmu: numAttr(chExt, \"cx\"), hEmu: numAttr(chExt, \"cy\") }\n : { wEmu: numAttr(ext, \"cx\"), hEmu: numAttr(ext, \"cy\") };\n const sizeEmu = ext\n ? { wEmu: numAttr(ext, \"cx\"), hEmu: numAttr(ext, \"cy\") }\n : groupExtentEmu;\n if (groupExtentEmu.wEmu <= 0 || groupExtentEmu.hEmu <= 0) return null;\n\n // Read break / keep-next directives from the CONTAINING paragraph's\n // pPr. The directive semantically belongs to the frame block, not\n // to the inner content paragraph.\n const pPr = firstChildNS(hostP, NS.w, \"pPr\");\n let pageBreakBefore = pPr\n ? firstChildNS(pPr, NS.w, \"pageBreakBefore\") !== null\n : false;\n const keepNext = pPr ? firstChildNS(pPr, NS.w, \"keepNext\") !== null : false;\n\n // When the caller opted into LRPB-as-directive, a `<w:lastRenderedPageBreak/>`\n // hint inside the OUTER paragraph escalates to `pageBreakBefore: true`\n // on the frame. (LRPB hints typically live OUTSIDE the drawing — in\n // sibling `<w:r>` elements of the outer paragraph — exactly where\n // `convertParagraph` finds them for plain body paragraphs.) Skipping\n // `<w:txbxContent>` matches the body walker's behaviour so a hint\n // inside the textbox text doesn't cascade up.\n if (!pageBreakBefore && ctx.honorLastRenderedPageBreaks) {\n if (hasLastRenderedPageBreakSkippingTxbx(hostP)) pageBreakBefore = true;\n }\n\n // Walk group children: each <wps:wsp> is either a textbox or a\n // shape decoration; each <pic:pic> is a picture decoration.\n let textbox: InlineFrame[\"textbox\"];\n const pictures: Array<InlineFrame[\"pictures\"][number]> = [];\n const shapes: Array<InlineFrame[\"shapes\"][number]> = [];\n\n for (const child of Array.from(wpg.children)) {\n if (child.namespaceURI === NS.wps && child.localName === \"wsp\") {\n const txbx = firstChildNS(child, NS.wps, \"txbx\");\n if (txbx) {\n // Textbox-bearing shape → contributes the frame's body.\n // We honour only the FIRST textbox in a group for now; multi-\n // textbox groups are rare in the corpus and need their own\n // model (each becomes its own frame? or the group becomes\n // a \"group of frames\"?). Defer to a follow-up.\n if (textbox) continue;\n const txbxContent = firstChildNS(txbx, NS.w, \"txbxContent\");\n if (!txbxContent) continue;\n const { off, ext: shapeExt } = readShapeXfrm(child);\n textbox = {\n offsetEmu: { xEmu: off.x, yEmu: off.y },\n sizeEmu: { wEmu: shapeExt.cx, hEmu: shapeExt.cy },\n body: ctx.parseBlockBody(txbxContent),\n };\n const fill = readSolidFill(child);\n if (fill !== undefined) textbox.fill = fill;\n const border = readBorder(child);\n if (border !== undefined) textbox.border = border;\n // `<wps:bodyPr>` carries the text insets + vertical anchor. Word\n // centers a single heading line by top-anchoring it inside a\n // short textbox whose insets + centered placement land the line\n // at the pill's middle; dropping the insets floats it too high.\n const bodyPr = firstChildNS(child, NS.wps, \"bodyPr\");\n if (bodyPr) {\n textbox.padding = {\n leftEmu: numAttrOr(bodyPr, \"lIns\", 91440),\n topEmu: numAttrOr(bodyPr, \"tIns\", 45720),\n rightEmu: numAttrOr(bodyPr, \"rIns\", 91440),\n bottomEmu: numAttrOr(bodyPr, \"bIns\", 45720),\n };\n const anchor = bodyPr.getAttribute(\"anchor\");\n if (anchor === \"ctr\") textbox.vAlign = \"center\";\n else if (anchor === \"b\") textbox.vAlign = \"bottom\";\n else textbox.vAlign = \"top\";\n }\n } else {\n // Shape-without-textbox → decoration.\n const { off, ext: shapeExt } = readShapeXfrm(child);\n if (shapeExt.cx <= 0 || shapeExt.cy <= 0) continue;\n const geom = readGeometry(child);\n const fill = readSolidFill(child);\n const border = readBorder(child);\n const decoration: InlineFrame[\"shapes\"][number] = {\n geometry: geom,\n offsetEmu: { xEmu: off.x, yEmu: off.y },\n sizeEmu: { wEmu: shapeExt.cx, hEmu: shapeExt.cy },\n };\n if (fill !== undefined) decoration.fill = fill;\n if (border !== undefined) decoration.border = border;\n shapes.push(decoration);\n }\n } else if (child.namespaceURI === NS.pic && child.localName === \"pic\") {\n const blip = child.getElementsByTagNameNS(NS.a, \"blip\")[0];\n if (!blip) continue;\n const rId =\n blip.getAttributeNS(NS.r, \"embed\") ?? blip.getAttribute(\"r:embed\");\n if (!rId) continue;\n const target = ctx.rels.get(rId);\n if (!target) continue;\n const partPath = normalizePartPath(target);\n const { off, ext: picExt } = readShapeXfrm(child);\n if (picExt.cx <= 0 || picExt.cy <= 0) continue;\n const cNvPr = child.getElementsByTagNameNS(NS.pic, \"cNvPr\")[0];\n const altText = cNvPr?.getAttribute(\"descr\");\n const picture: InlineFrame[\"pictures\"][number] = {\n partPath,\n offsetEmu: { xEmu: off.x, yEmu: off.y },\n sizeEmu: { wEmu: picExt.cx, hEmu: picExt.cy },\n };\n if (altText) picture.altText = altText;\n pictures.push(picture);\n }\n }\n\n const out: InlineFrame = {\n kind: \"inline_frame\",\n groupExtentEmu,\n sizeEmu,\n pictures,\n shapes,\n };\n if (pageBreakBefore) out.pageBreakBefore = true;\n if (keepNext) out.keepNext = true;\n if (textbox) out.textbox = textbox;\n return out;\n}\n\n// === low-level OOXML readers ===\n\nfunction readShapeXfrm(shape: Element): {\n off: { x: number; y: number };\n ext: { cx: number; cy: number };\n} {\n // Shape's own offset/extent live on `*:spPr > a:xfrm`. wps:spPr for\n // shapes, pic:spPr for pictures.\n const spPr =\n firstChildNS(shape, NS.wps, \"spPr\") ?? firstChildNS(shape, NS.pic, \"spPr\");\n if (!spPr) return { off: { x: 0, y: 0 }, ext: { cx: 0, cy: 0 } };\n const xfrm = firstNS(spPr, NS.a, \"xfrm\");\n if (!xfrm) return { off: { x: 0, y: 0 }, ext: { cx: 0, cy: 0 } };\n const offEl = firstNS(xfrm, NS.a, \"off\");\n const extEl = firstNS(xfrm, NS.a, \"ext\");\n return {\n off: offEl ? { x: numAttr(offEl, \"x\"), y: numAttr(offEl, \"y\") } : { x: 0, y: 0 },\n ext: extEl ? { cx: numAttr(extEl, \"cx\"), cy: numAttr(extEl, \"cy\") } : { cx: 0, cy: 0 },\n };\n}\n\nfunction readGeometry(\n wsp: Element,\n): \"rect\" | \"ellipse\" | \"roundedRect\" | \"line\" {\n const prstGeom = wsp.getElementsByTagNameNS(NS.a, \"prstGeom\")[0];\n const prst = prstGeom?.getAttribute(\"prst\");\n switch (prst) {\n case \"ellipse\":\n return \"ellipse\";\n case \"roundRect\":\n return \"roundedRect\";\n case \"line\":\n case \"straightConnector1\":\n return \"line\";\n case \"rect\":\n default:\n return \"rect\";\n }\n}\n\nfunction readSolidFill(shape: Element): string | undefined {\n const spPr =\n firstChildNS(shape, NS.wps, \"spPr\") ?? firstChildNS(shape, NS.pic, \"spPr\");\n if (!spPr) return undefined;\n for (const fill of Array.from(spPr.children)) {\n if (fill.namespaceURI === NS.a && fill.localName === \"solidFill\") {\n const srgb = firstChildNS(fill, NS.a, \"srgbClr\");\n const val = srgb?.getAttribute(\"val\");\n if (val && /^[0-9A-Fa-f]{6}$/.test(val)) return `#${val.toUpperCase()}`;\n }\n }\n return undefined;\n}\n\nfunction readBorder(shape: Element): FrameBorder | undefined {\n const spPr =\n firstChildNS(shape, NS.wps, \"spPr\") ?? firstChildNS(shape, NS.pic, \"spPr\");\n if (!spPr) return undefined;\n const ln = firstChildNS(spPr, NS.a, \"ln\");\n if (!ln) return undefined;\n const widthEmu = numAttr(ln, \"w\");\n const solidFill = firstChildNS(ln, NS.a, \"solidFill\");\n const srgb = solidFill ? firstChildNS(solidFill, NS.a, \"srgbClr\") : null;\n const val = srgb?.getAttribute(\"val\");\n if (!val || !/^[0-9A-Fa-f]{6}$/.test(val)) return undefined;\n const prstDash = firstChildNS(ln, NS.a, \"prstDash\");\n const style = coerceBorderStyle(prstDash?.getAttribute(\"val\"));\n return { color: `#${val.toUpperCase()}`, widthEmu: widthEmu || 0, style };\n}\n\nfunction coerceBorderStyle(\n v: string | null | undefined,\n): \"solid\" | \"dashed\" | \"dotted\" | \"double\" {\n switch (v) {\n case \"dash\":\n case \"lgDash\":\n case \"sysDash\":\n return \"dashed\";\n case \"dot\":\n case \"sysDot\":\n return \"dotted\";\n default:\n return \"solid\";\n }\n}\n\nfunction hasLastRenderedPageBreakSkippingTxbx(root: Element): boolean {\n // Walk descendants of `root`, returning true if any is a\n // `<w:lastRenderedPageBreak/>`. Skips into `<w:txbxContent>` are\n // suppressed — a hint nested INSIDE the textbox content is the\n // textbox's own internal pagination concern, not the host\n // paragraph's. Mirrors `hasLastRenderedPageBreak` in `paragraph.ts`.\n const stack: Element[] = [root];\n while (stack.length > 0) {\n const el = stack.pop()!;\n for (const child of Array.from(el.children)) {\n if (child.localName === \"txbxContent\") continue;\n if (\n child.localName === \"lastRenderedPageBreak\" &&\n child.namespaceURI === NS.w\n ) {\n return true;\n }\n stack.push(child);\n }\n }\n return false;\n}\n\nfunction numAttr(el: Element | undefined | null, name: string): number {\n if (!el) return 0;\n const n = Number(el.getAttribute(name) ?? \"0\");\n return Number.isFinite(n) ? n : 0;\n}\n\n/** Read a numeric attribute, falling back to `fallback` when absent —\n * used for `<wps:bodyPr>` insets whose OOXML defaults are non-zero. */\nfunction numAttrOr(el: Element, name: string, fallback: number): number {\n const raw = el.getAttribute(name);\n if (raw === null) return fallback;\n const n = Number(raw);\n return Number.isFinite(n) ? n : fallback;\n}\n\nfunction firstChildNS(parent: Element, ns: string, local: string): Element | null {\n for (const child of Array.from(parent.children)) {\n if (child.namespaceURI === ns && child.localName === local) return child;\n }\n return null;\n}\n\nfunction firstNS(root: Element, ns: string, local: string): Element | null {\n const found = root.getElementsByTagNameNS(ns, local)[0];\n return found ?? null;\n}\n\nfunction directChildrenNS(parent: Element, ns: string, local: string): Element[] {\n const out: Element[] = [];\n for (const child of Array.from(parent.children)) {\n if (child.namespaceURI === ns && child.localName === local) out.push(child);\n }\n return out;\n}\n\nfunction findAncestor(start: Element, ns: string, local: string): Element | null {\n let el: Element | null = start.parentElement;\n while (el) {\n if (el.namespaceURI === ns && el.localName === local) return el;\n el = el.parentElement;\n }\n return null;\n}\n\nfunction normalizePartPath(target: string): string {\n if (target.startsWith(\"/\")) return target.slice(1);\n if (target.startsWith(\"word/\")) return target;\n return `word/${target}`;\n}\n","import { convertBlocksFromContainer, convertDocumentXml } from \"./document\";\nimport { convertParagraph } from \"./paragraph\";\nimport { readSection } from \"./headers\";\nimport { parseRels } from \"./rels\";\nimport { unzipDocx } from \"./unzip\";\nimport { parseFootnotesXml } from \"./footnotes\";\nimport { parseCommentsXml } from \"./comments\";\nimport { templateToBlocks } from \"../../doc/pageSetupBridge\";\nimport { parseXml } from \"../shared/xml\";\nimport type { DocxImportResult } from \"../types\";\nimport { defaultStyles, emptyDocument } from \"../../doc/builders\";\nimport { parseStylesXml } from \"./styles\";\nimport { parseNumberingXml } from \"./numbering\";\nimport { parseSettingsXml } from \"./settings\";\nimport { mountFontTableFromZip } from \"../../fonts\";\nimport { parseAnchoredFrames } from \"./anchoredFrames\";\nimport { flowDisplacingTextboxes } from \"./flowFrames\";\nimport { parseInlineFrames } from \"./inlineFrames\";\nimport type {\n AnchoredFrame,\n Block,\n InlineFrame,\n SectionProperties,\n SobreeDocument,\n} from \"../../doc/types\";\n\n/**\n * Top-level entry point for importing a .docx file. Returns a native\n * `SobreeDocument` plus any warnings surfaced by the conversion.\n */\nexport async function importDocx(\n src: File | Blob | ArrayBuffer | Uint8Array,\n): Promise<DocxImportResult> {\n const unzipped = await unzipDocx(src);\n const documentXml = unzipped.text[\"word/document.xml\"];\n if (!documentXml) {\n throw new Error(\"Not a valid .docx file — missing word/document.xml\");\n }\n const xml = parseXml(documentXml);\n // Run the same Markup-Compatibility + text-box preprocessing on the\n // document body that we run on header/footer parts. Without this:\n // - `<mc:Fallback>` branches double-walk into the AST (every\n // legacy v:shape clone becomes a duplicate paragraph).\n // - `<w:txbxContent>` text-box content stays buried inside a\n // `<w:drawing>` and never reaches the AST as paragraphs (e.g.\n // jellap.docx's \"* Fénykép\" photo-placeholder text vanishes).\n stripMcFallbacks(xml);\n const relsXml = unzipped.text[\"word/_rels/document.xml.rels\"];\n const rels = relsXml ? parseRels(relsXml) : new Map<string, string>();\n // Walk the body's direct paragraph children FIRST to build a stable\n // element → index map. The new floating-layer parser uses this to\n // attribute each anchored frame to the body paragraph that contained\n // its `<w:drawing>`, so the renderer can put the frame on whichever\n // page that paragraph paginates onto. Must run BEFORE the lifter,\n // which removes drawings from the XML and would orphan the lookup.\n const bodyParagraphIndexByElement = buildBodyParagraphIndex(xml);\n const anchoredFrames: AnchoredFrame[] = parseAnchoredFrames(xml, {\n rels,\n bodyParagraphIndexByElement,\n // Parse textbox bodies through the full body walker so their real\n // paragraph spacing / formatting survives — critical for frames\n // that flow into the body (see flowDisplacingTextboxes), whose\n // pagination depends on honest line heights.\n parseBlockBody: (txbxContent) =>\n convertBlocksFromContainer(txbxContent, { rels }).body,\n });\n // Parse inline-drawing frames (`<w:drawing><wp:inline>` with\n // textbox payload) into the new InlineFrame model. Phase 1.1:\n // parser runs and results land on `doc.inlineFrames` for\n // inspection / future renderer (Phase 1.2). The legacy lifter\n // ALSO still processes these drawings, so the rendered output\n // is unchanged this phase. Phase 1.4 deletes the lifter once\n // the renderer (Phase 1.2) uses the new path exclusively.\n //\n // The textbox body runs through the SAME walker as the document\n // body (`convertBlocksFromContainer`) so its paragraphs carry their\n // real run formatting (bold, font size, colour), lists, and tables —\n // not the flat text the old Phase-1.1 stub produced. The walker is\n // handed a plain `{ rels }` context: style/numbering resolution\n // happens later at render time, exactly as for body paragraphs.\n // LRPB hints INSIDE a textbox are the textbox's own pagination\n // concern, so we don't honour them here (default false).\n //\n // Same LRPB threshold the body walker uses (in `document.ts`),\n // mirrored here so the InlineFrame parser interprets\n // `<w:lastRenderedPageBreak/>` hints consistently.\n const W_NS = \"http://schemas.openxmlformats.org/wordprocessingml/2006/main\";\n const lrpbCount = xml.getElementsByTagNameNS(W_NS, \"lastRenderedPageBreak\").length;\n const honorLastRenderedPageBreaks = lrpbCount >= 10;\n const parsedInlineFrames = parseInlineFrames(xml, {\n rels,\n parseBlockBody: (txbxContent) =>\n convertBlocksFromContainer(txbxContent, { rels }).body,\n honorLastRenderedPageBreaks,\n });\n const inlineFrames: InlineFrame[] = parsedInlineFrames.map((p) => p.frame);\n // Build the replacement map BEFORE the lifter / body walker run.\n // The body walker will encounter each `hostParagraphEl` (its\n // drawing has been removed by `parseInlineFrames` since `claim: true`\n // is the default) and emit the mapped InlineFrame block at that\n // paragraph's document-order position — directly, no DOM-attribute\n // round-trip.\n const replaceParagraphs = new Map<Element, Block>();\n for (const { hostParagraphEl, frame } of parsedInlineFrames) {\n // Capture the host paragraph's resolved properties (spacing,\n // alignment, …) so the renderer reserves the same vertical box\n // Word does — drawing height PLUS the host paragraph's\n // spacing-after (commonly Normal's `<w:spacing w:after=\"200\">`).\n // The drawing was already removed by parseInlineFrames' claim\n // pass, so convertParagraph here parses just the host pPr.\n frame.hostProps = convertParagraph(hostParagraphEl, { rels }).properties;\n replaceParagraphs.set(hostParagraphEl, frame);\n }\n\n const { body: rawBody, warnings, sectPrEls } = convertDocumentXml(xml, { rels }, {\n replaceParagraphs,\n });\n\n // Displacing anchored textboxes (wrapping, paragraph-anchored,\n // content-only boxes) are really framed body content — splice them\n // into the flow at their anchor paragraph so they paginate instead\n // of clipping in the absolute overlay. Pure decorations (pictures,\n // bordered boxes, behind-text, wrapNone floats) stay in\n // `anchoredFrames`.\n const { body, frames: finalAnchoredFrames } = flowDisplacingTextboxes(\n rawBody,\n anchoredFrames,\n );\n\n // Build all sections from the collected sectPrs (inline + body-level).\n // Each section's headerRefs/footerRefs are resolved through `rels` to\n // partIds; the referenced header/footer XML parts are loaded into\n // `headerFooterBodies` so the editor can render them.\n const sections = sectPrEls.map((el) => readSection(el, rels));\n const headerFooterBodies = loadHeaderFooterBodies(sections, unzipped.text, rels);\n\n // Thread `word/*` media and other embedded binary parts through the AST\n // so the export path can round-trip them. Keyed by ZIP-level path so\n // DrawingRun.partPath matches directly.\n const rawParts: Record<string, Uint8Array> = {};\n for (const [path, bytes] of Object.entries(unzipped.binary)) {\n rawParts[path] = bytes;\n }\n\n // Font declarations + embedded font binaries. The bytes are already\n // in `rawParts` from the unzip pass — `mountFontTableFromZip` just\n // parses the metadata so renderer-side @font-face can deobfuscate\n // on demand. Round-trip is byte-faithful when nothing changes.\n const fonts = mountFontTableFromZip(unzipped.text, parseRels);\n\n // Read settings.xml first — `compatibilityMode` + auto-spacing flag\n // gate whether parseStylesXml injects Word's implicit Normal-style\n // baseline (line ≈ 1.08, after = 8pt). See settings.ts for the\n // exact rule.\n const settings = parseSettingsXml(unzipped.text[\"word/settings.xml\"]);\n // Pull the actual style catalogue from `word/styles.xml` so the\n // imported doc's typography (Calibri / Cambria heading, etc.)\n // survives. Only fall back to Sobree's synthesised defaults when\n // the docx genuinely omits styles.xml or the parse fails.\n const importedStyles = parseStylesXml(unzipped.text[\"word/styles.xml\"], settings);\n const footnotes = parseFootnotesXml(unzipped.text[\"word/footnotes.xml\"], { rels });\n const comments = parseCommentsXml(\n unzipped.text[\"word/comments.xml\"],\n { rels },\n unzipped.text[\"word/commentsExtended.xml\"],\n );\n const doc: SobreeDocument = {\n body,\n sections: sections.length > 0 ? sections : [fallbackSection()],\n headerFooterBodies,\n styles: importedStyles ?? defaultStyles(),\n // Parse the docx's numbering definitions so the renderer can apply\n // per-level indentation (matches Word's ruler markers). Without\n // this, every list defaults to a hardcoded CSS padding-left.\n numbering: parseNumberingXml(unzipped.text[\"word/numbering.xml\"]),\n rawParts,\n fonts,\n ...(Object.keys(footnotes).length > 0 ? { footnotes } : {}),\n ...(Object.keys(comments).length > 0 ? { comments } : {}),\n // Surface document-wide layout settings (e.g. defaultTabStop) so\n // the renderer can apply per-document tab geometry instead of\n // falling back to CSS's 8-char default.\n ...(settings.defaultTabStopTwips !== undefined\n ? { settings: { defaultTabStopTwips: settings.defaultTabStopTwips } }\n : {}),\n ...(finalAnchoredFrames.length > 0 ? { anchoredFrames: finalAnchoredFrames } : {}),\n ...(inlineFrames.length > 0 ? { inlineFrames } : {}),\n };\n // If the doc was truly empty, give it a blank paragraph so Word opens it.\n if (doc.body.length === 0) {\n doc.body.push({ kind: \"paragraph\", properties: {}, runs: [] });\n }\n\n return { document: doc, warnings };\n}\n\n/**\n * Load every header/footer XML part referenced from any section,\n * deduped by partId.\n *\n * Each part is converted through the same `convertBlocksFromContainer`\n * walker the document body uses — so headers/footers carry the full\n * rich AST: formatted runs, drawings, tables, comment ranges,\n * revisions. The renderer treats `headerFooterBodies[partId]` exactly\n * like body content.\n *\n * `mc:AlternateContent` fallbacks are stripped first so duplicate\n * paragraphs (one from `mc:Choice`, one from `mc:Fallback`) don't\n * end up in the body twice — see {@link stripMcFallbacks} for the\n * rationale.\n *\n * Per-part `rels` aren't loaded here yet: header/footer references to\n * media use the document-level rels map, which works for embedded\n * images that live in `word/media/*`. If headers ever need their own\n * `_rels/header1.xml.rels` (rare), that's a follow-up.\n */\nfunction loadHeaderFooterBodies(\n sections: readonly SectionProperties[],\n textParts: Record<string, string>,\n rels: Map<string, string>,\n): Record<string, Block[]> {\n const bodies: Record<string, Block[]> = {};\n const seen = new Set<string>();\n for (const section of sections) {\n for (const ref of [...section.headerRefs, ...section.footerRefs]) {\n if (seen.has(ref.partId)) continue;\n seen.add(ref.partId);\n const xml = textParts[`word/${ref.partId}`];\n if (!xml) continue;\n const parsed = parseXml(xml);\n stripMcFallbacks(parsed);\n // Per-part rels: `word/_rels/header1.xml.rels` defines this\n // header's own rId → target mapping (image embeds, hyperlinks,\n // etc.). Without it, a `<a:blip r:embed=\"rId4\">` inside the\n // header gets resolved against the *document*-level rels map,\n // which has an unrelated rId4 — leading to wrong-image-target\n // bugs like jellap.docx's logo pointing at customXml/item1.xml.\n // Fall back to the document rels for headers that don't have a\n // dedicated rels file (rare but valid for header parts with no\n // external references).\n const headerRelsXml = textParts[`word/_rels/${ref.partId}.rels`];\n const headerRels = headerRelsXml\n ? mergeRels(parseRels(headerRelsXml), rels)\n : rels;\n // Header part roots: `<w:hdr>` or `<w:ftr>`. Both wrap a stream\n // of paragraphs + tables identical in shape to `<w:body>`.\n const root = parsed.documentElement;\n const { body } = convertBlocksFromContainer(root, { rels: headerRels });\n bodies[ref.partId] = body;\n }\n }\n return bodies;\n}\n\n/**\n * Merge two rels maps with `primary` taking precedence. Used so a\n * header part's own rels override any document-level rId collision\n * (Word numbers rIds independently per part — the same rId number in\n * document.xml.rels and header1.xml.rels typically points at totally\n * different targets).\n */\nfunction mergeRels(\n primary: Map<string, string>,\n fallback: Map<string, string>,\n): Map<string, string> {\n const out = new Map(fallback);\n for (const [id, target] of primary) out.set(id, target);\n return out;\n}\n\n/**\n * OOXML uses `<mc:AlternateContent>` to provide both a modern\n * `<mc:Choice Requires=\"...\">` branch and a legacy `<mc:Fallback>`\n * branch. A consumer should pick one (per ECMA-376 §23.2); without\n * stripping Fallback first, the walker descends into BOTH and the\n * content (e.g. text-box paragraphs) duplicates.\n */\n/**\n * Walk the document body's direct `<w:p>` children in document order\n * and return an element→index map. Used by the anchored-frames parser\n * to attribute each frame to the body paragraph that contains its\n * `<w:drawing>`, so the renderer can pin the frame to the right page\n * after pagination. Header / footer paragraphs deliberately excluded —\n * they belong to their own zones and don't have body-page positions.\n */\nfunction buildBodyParagraphIndex(doc: Document): Map<Element, number> {\n const W_NS = \"http://schemas.openxmlformats.org/wordprocessingml/2006/main\";\n const map = new Map<Element, number>();\n const body = doc.getElementsByTagNameNS(W_NS, \"body\")[0];\n if (!body) return map;\n let index = 0;\n for (const child of Array.from(body.children)) {\n if (child.namespaceURI === W_NS && child.localName === \"p\") {\n map.set(child, index);\n index++;\n } else if (child.namespaceURI === W_NS && child.localName === \"tbl\") {\n // Tables count toward body block indices too, but we don't need\n // the table element itself in the map — anchored frames live\n // inside paragraphs, not tables. Still advance the counter so\n // the indices line up with how the body-block list will be\n // numbered downstream.\n index++;\n }\n }\n return map;\n}\n\nfunction stripMcFallbacks(doc: Document): void {\n const MC_NS = \"http://schemas.openxmlformats.org/markup-compatibility/2006\";\n const fallbacks = Array.from(doc.getElementsByTagNameNS(MC_NS, \"Fallback\"));\n for (const f of fallbacks) f.parentNode?.removeChild(f);\n}\n\n/**\n * Synthesize a default section when the .docx contains no `<w:sectPr>`\n * at all (degenerate, but Word's reader is permissive). A4 portrait,\n * 1\" margins — same defaults as `defaultSection()`.\n */\nfunction fallbackSection(): SectionProperties {\n return {\n pageSize: { wTwips: 11906, hTwips: 16838, orientation: \"portrait\" },\n pageMargins: {\n topTwips: 1440,\n rightTwips: 1440,\n bottomTwips: 1440,\n leftTwips: 1440,\n headerTwips: 720,\n footerTwips: 720,\n gutterTwips: 0,\n },\n headerRefs: [],\n footerRefs: [],\n };\n}\n\n// Re-export types for callers.\nexport type { Block, SobreeDocument };\nexport { emptyDocument };\n\n// `headerFooterBodies` templating uses `templateToBlocks` internally; expose\n// it for tests and consumers that want the same rendering.\nexport { templateToBlocks };\n","/**\n * BlobStore — the side-channel where binary parts live when an\n * embedder configures Sobree for content-hashed assets (Phase 3.2+).\n *\n * # The contract\n *\n * Tiny, intentionally minimal. Three operations: `put` (returns the\n * content hash under which bytes are stored), `get` (fetches by\n * hash), and optional `has` (cheap existence check). Every reasonable\n * backend — in-memory, IndexedDB, HTTP, S3, R2, Cloudflare Workers,\n * Postgres LO, Redis — implements this.\n *\n * # Why content addressing\n *\n * Two reasons:\n *\n * 1. **Deduplication.** A 5 MB logo pasted into 100 docs lives once\n * in the blob store, not 100 times. Hashes collide on identity,\n * so identical bytes produce identical keys.\n * 2. **Tamper resistance.** A peer can verify any fetched blob by\n * re-hashing — no trust in the storage layer required for\n * integrity. (Confidentiality is a separate concern; encrypt at\n * the transport / storage layer if you need it.)\n *\n * # Hash algorithm\n *\n * Sobree uses SHA-256 hex (lowercase, no separators) for blob\n * addressing. 64 chars per hash. Sized for the next decade or two of\n * web content; not so long that hashes are awkward to log or shove\n * into a URL path.\n *\n * # Concurrency\n *\n * Implementations should be safe for concurrent `put` of the same\n * content (multiple callers, same bytes → same hash, idempotent).\n * `get(hash)` must return the same bytes that were `put`.\n */\n\n/** A SHA-256 hex digest. 64 lowercase hex chars. */\nexport type BlobHash = string;\n\nexport interface BlobStore {\n /**\n * Upload bytes and return their content hash. Idempotent — calling\n * `put` with the same bytes returns the same hash and is a no-op\n * after the first call.\n */\n put(bytes: Uint8Array): Promise<BlobHash>;\n\n /**\n * Fetch bytes by hash. Returns `null` when the blob isn't present\n * (e.g. another peer wrote the partRef but the bytes haven't\n * propagated yet). Implementations should NOT throw on \"not\n * found\" — return null.\n */\n get(hash: BlobHash): Promise<Uint8Array | null>;\n\n /**\n * Optional cheap existence check — returns `true` if a blob with\n * the given hash exists, without transferring its content. Used by\n * `BlobCache` to decide whether to schedule a fetch. Falls back to\n * `(await get(hash)) !== null` if not provided.\n */\n has?(hash: BlobHash): Promise<boolean>;\n\n /**\n * Optional removal. Most production deployments don't expose this\n * over the wire (blob deletion needs care: ref counting, garbage\n * collection, distributed coordination). Provided for tests and\n * local-only deployments where the embedder owns lifecycle.\n */\n delete?(hash: BlobHash): Promise<void>;\n}\n\n/**\n * BlobStoreError is thrown for low-level failures (transport,\n * authorization, integrity check). \"Not found\" is *not* an error —\n * `get` returns `null` for that case so callers can distinguish.\n */\nexport class BlobStoreError extends Error {\n constructor(\n message: string,\n /** Originating exception, network response, etc. */\n readonly cause?: unknown,\n ) {\n super(message);\n this.name = \"BlobStoreError\";\n }\n}\n","/**\n * SHA-256 → lowercase hex helper that works in browsers, Web Workers,\n * and Node 18+ without external deps.\n *\n * # Backends\n *\n * Both code paths produce identical output; we pick by availability:\n *\n * - **Node:** uses `node:crypto.createHash` when detected — fast,\n * synchronous under the hood, immune to cross-realm typed-array\n * issues that occasionally trip jsdom-hosted tests.\n * - **Browser / Worker:** `globalThis.crypto.subtle.digest`. The\n * standard WebCrypto API.\n *\n * The Node detection runs once at module load. The dynamic-import\n * dance keeps `node:crypto` out of browser bundles (bundlers leave\n * the `try { require(...) }` branch untouched and tree-shake it out\n * when targeting non-Node).\n */\n\nimport type { BlobHash } from \"./types\";\n\n/**\n * Compute the SHA-256 hex digest of `bytes`. Lowercase hex, 64 chars.\n *\n * Always returns a Promise — the WebCrypto API is async, and we\n * shape the Node path the same way for API consistency.\n */\nexport async function sha256Hex(bytes: Uint8Array): Promise<BlobHash> {\n const nodeImpl = await getNodeImpl();\n if (nodeImpl) return nodeImpl(bytes);\n return webCryptoSha256Hex(bytes);\n}\n\nasync function webCryptoSha256Hex(bytes: Uint8Array): Promise<BlobHash> {\n const subtle = (globalThis.crypto ?? {}).subtle;\n if (!subtle) {\n throw new Error(\n \"sha256Hex: WebCrypto unavailable. Sobree requires Node 18+ or a modern browser.\",\n );\n }\n // Allocate a fresh current-realm ArrayBuffer and copy bytes\n // byte-by-byte. Avoids cross-realm typed-array checks that\n // SubtleCrypto impls sometimes get strict about (notably in jsdom\n // + Node webcrypto under vitest).\n const ABCtor = globalThis.ArrayBuffer;\n const Ctor = globalThis.Uint8Array;\n const copy = new ABCtor(bytes.byteLength);\n const view = new Ctor(copy);\n for (let i = 0; i < bytes.length; i++) view[i] = bytes[i]!;\n const buf = await subtle.digest(\"SHA-256\", copy);\n return bufToHex(new Ctor(buf));\n}\n\n// === Node detection (cached) ===\n\ntype NodeImpl = (bytes: Uint8Array) => Promise<string>;\nlet nodeImplPromise: Promise<NodeImpl | null> | null = null;\n\nfunction getNodeImpl(): Promise<NodeImpl | null> {\n if (nodeImplPromise !== null) return nodeImplPromise;\n nodeImplPromise = detectNodeImpl();\n return nodeImplPromise;\n}\n\nasync function detectNodeImpl(): Promise<NodeImpl | null> {\n const proc: unknown = (\n globalThis as { process?: { versions?: { node?: string } } }\n ).process;\n const isNode =\n typeof proc === \"object\" &&\n proc !== null &&\n typeof (proc as { versions?: { node?: string } }).versions?.node === \"string\";\n if (!isNode) return null;\n try {\n const c = (await import(/* @vite-ignore */ \"node:crypto\")) as\n | typeof import(\"node:crypto\");\n return async (bytes: Uint8Array) =>\n c.createHash(\"sha256\").update(bytes).digest(\"hex\");\n } catch {\n return null;\n }\n}\n\n// === hex encoding ===\n\nfunction bufToHex(bytes: Uint8Array): string {\n const out = new Array<string>(bytes.length);\n for (let i = 0; i < bytes.length; i++) {\n out[i] = HEX[bytes[i]!] ?? \"\";\n }\n return out.join(\"\");\n}\n\nconst HEX: string[] = (() => {\n const out: string[] = new Array(256);\n for (let i = 0; i < 256; i++) {\n out[i] = i.toString(16).padStart(2, \"0\");\n }\n return out;\n})();\n\n/**\n * Validate that a string looks like a SHA-256 hex digest. Used as a\n * defensive guard when bytes come from the wire.\n */\nexport function isBlobHash(s: string): boolean {\n return s.length === 64 && /^[0-9a-f]{64}$/.test(s);\n}\n","/**\n * In-memory BlobStore. Useful for:\n *\n * - Tests (predictable, no I/O).\n * - Single-tab dev playgrounds where you want partRef semantics\n * without standing up a server.\n * - Layering: production embedders often wrap a remote BlobStore\n * in a memory cache for speed; the in-memory store is the\n * primitive for that pattern.\n *\n * Bytes stored here live for the lifetime of the BlobStore instance.\n * No persistence across reloads (use `attachIndexedDBProvider` or a\n * remote store for that).\n */\n\nimport { sha256Hex } from \"./hash\";\nimport type { BlobHash, BlobStore } from \"./types\";\n\nexport function inMemoryBlobStore(): BlobStore {\n // Defensive copy on `put` and (less importantly) `get` so callers\n // mutating their byte arrays after the call can't corrupt our state.\n // The cost is one Uint8Array allocation per call; negligible vs\n // the cost of moving binary content around.\n const store = new Map<BlobHash, Uint8Array>();\n\n return {\n async put(bytes) {\n const hash = await sha256Hex(bytes);\n if (!store.has(hash)) {\n store.set(hash, new Uint8Array(bytes));\n }\n return hash;\n },\n async get(hash) {\n const found = store.get(hash);\n return found ? new Uint8Array(found) : null;\n },\n async has(hash) {\n return store.has(hash);\n },\n async delete(hash) {\n store.delete(hash);\n },\n };\n}\n","/**\n * HTTP-backed BlobStore — `fetch()` against a base URL.\n *\n * Wire format (hash-addressable):\n *\n * PUT <baseUrl>/<hash> body = bytes, response = 2xx\n * GET <baseUrl>/<hash> response = bytes\n * HEAD <baseUrl>/<hash> response = 2xx (exists) or 404 (missing)\n *\n * The server is responsible for verifying that the URL hash matches\n * the body hash on PUT (server-side hash-trust isn't safe). For\n * Sobree-shipped servers, this validation lives in\n * `@sobree/collab-server` (Phase 3.2.x). For third-party / S3-style\n * stores you'll typically use *path-style content addressing* with\n * pre-signed URLs and skip the server-side check.\n *\n * # Auth\n *\n * The optional `headers` factory runs on every request — return any\n * `Authorization`, `Cookie`, signed-URL params, etc. Recomputed each\n * time so token-refresh patterns work without re-creating the store.\n *\n * # Retry / backoff\n *\n * Not built into the store — that's the `BlobCache` layer's job\n * (where it has context about whether the missing blob is critical\n * or speculative). The store reports failure honestly via thrown\n * `BlobStoreError` (non-2xx) or `null` (404).\n */\n\nimport { isBlobHash } from \"./hash\";\nimport { type BlobHash, type BlobStore, BlobStoreError } from \"./types\";\n\nexport interface FetchBlobStoreOptions {\n /** Base URL — bytes are addressed at `<baseUrl>/<hash>`. No trailing slash. */\n baseUrl: string;\n /**\n * Optional headers factory. Called per request — embed auth tokens,\n * trace IDs, custom content types, etc. Defaults to no extra headers.\n */\n headers?: () => Record<string, string> | Promise<Record<string, string>>;\n /** Pass through to the underlying `fetch` (CORS mode, signal, etc.). */\n fetchInit?: RequestInit;\n /**\n * Override the global `fetch`. Useful for testing or running inside\n * a runtime with a custom fetch impl. Default: `globalThis.fetch`.\n */\n fetch?: typeof fetch;\n}\n\nexport function fetchBlobStore(opts: FetchBlobStoreOptions): BlobStore {\n const baseUrl = opts.baseUrl.replace(/\\/+$/, \"\");\n const fetchImpl = opts.fetch ?? globalThis.fetch;\n if (!fetchImpl) {\n throw new Error(\n \"fetchBlobStore: no global `fetch` available. Pass one via opts.fetch.\",\n );\n }\n\n const buildHeaders = async (\n extra?: Record<string, string>,\n ): Promise<Record<string, string>> => {\n const dynamic = opts.headers ? await opts.headers() : {};\n return { ...dynamic, ...extra };\n };\n\n const url = (hash: BlobHash) => {\n if (!isBlobHash(hash)) {\n throw new BlobStoreError(`fetchBlobStore: invalid hash ${JSON.stringify(hash)}`);\n }\n return `${baseUrl}/${hash}`;\n };\n\n return {\n async put(bytes) {\n // Hash on the client. The server may re-hash to verify; we\n // address by the client-computed hash so the URL is\n // deterministic before the body is read.\n const { sha256Hex } = await import(\"./hash\");\n const hash = await sha256Hex(bytes);\n const headers = await buildHeaders({\n \"content-type\": \"application/octet-stream\",\n });\n const res = await fetchImpl(url(hash), {\n method: \"PUT\",\n body: bytes as unknown as BodyInit,\n headers,\n ...opts.fetchInit,\n });\n if (!res.ok) {\n throw new BlobStoreError(\n `fetchBlobStore.put(${hash}) failed: ${res.status} ${res.statusText}`,\n );\n }\n return hash;\n },\n\n async get(hash) {\n const headers = await buildHeaders();\n const res = await fetchImpl(url(hash), {\n method: \"GET\",\n headers,\n ...opts.fetchInit,\n });\n if (res.status === 404) return null;\n if (!res.ok) {\n throw new BlobStoreError(\n `fetchBlobStore.get(${hash}) failed: ${res.status} ${res.statusText}`,\n );\n }\n const buf = await res.arrayBuffer();\n return new Uint8Array(buf);\n },\n\n async has(hash) {\n const headers = await buildHeaders();\n const res = await fetchImpl(url(hash), {\n method: \"HEAD\",\n headers,\n ...opts.fetchInit,\n });\n if (res.status === 404) return false;\n if (!res.ok) {\n throw new BlobStoreError(\n `fetchBlobStore.has(${hash}) failed: ${res.status} ${res.statusText}`,\n );\n }\n return true;\n },\n\n async delete(hash) {\n const headers = await buildHeaders();\n const res = await fetchImpl(url(hash), {\n method: \"DELETE\",\n headers,\n ...opts.fetchInit,\n });\n if (res.status === 404) return; // already absent\n if (!res.ok) {\n throw new BlobStoreError(\n `fetchBlobStore.delete(${hash}) failed: ${res.status} ${res.statusText}`,\n );\n }\n },\n };\n}\n","/**\n * BlobCache — a per-Editor in-memory cache mediating the Y.Doc-side\n * `partRefs: hash → name` model and the Editor-side `rawParts: name → bytes`\n * shape.\n *\n * # Why a cache\n *\n * The Editor's renderer wants synchronous byte access — when an\n * `<img>` is painted, the bytes have to be there. The `BlobStore` is\n * async (network, disk). The cache is the async-to-sync adapter:\n * embedders call `ensureLoaded(hashes)` to pre-fetch; subsequent\n * `get(hash)` calls are synchronous and cheap.\n *\n * # Lifecycle\n *\n * 1. Editor seeds the cache with bytes the user paste/embedded\n * locally (writes go: `put(bytes)` → cache + background upload).\n * 2. On Y.Doc updates that reference a hash not yet cached, the\n * Editor calls `ensureLoaded([hash])` to fetch in the background.\n * A `change` event refires once the fetch lands so the renderer\n * can re-paint.\n * 3. Embedders concerned with deterministic output (DOCX export,\n * server-side render) `await ensureLoaded(allReferencedHashes)`\n * before reading.\n *\n * # Memory bound\n *\n * Unbounded for v0 — same model as today's inline `rawParts` (which\n * is also unbounded). A future refinement would track hash usage\n * counts (incref on partRef references, decref on remove) and evict\n * unreferenced entries. Out of scope for Phase 3.2 v0.\n *\n * # Concurrent fetches\n *\n * Multiple `ensureLoaded([h])` calls for the same hash share one\n * in-flight Promise — we don't fetch the same blob N times.\n */\n\nimport type { BlobHash, BlobStore } from \"./types\";\n\nexport interface BlobCacheOptions {\n /** Underlying store. */\n store: BlobStore;\n /**\n * Optional listener fired when a previously-missing hash arrives in\n * the cache (background fetch landed). Editors use this to know\n * when to re-render — a `<img>` whose bytes were pending now has\n * something to show.\n */\n onResolved?: (hash: BlobHash) => void;\n /**\n * Optional listener fired when a fetch fails. The cache leaves the\n * hash in \"missing\" state and lets a future `ensureLoaded` retry.\n * Default: warn to console.\n */\n onError?: (hash: BlobHash, err: unknown) => void;\n}\n\nexport class BlobCache {\n private readonly store: BlobStore;\n private readonly onResolved: (h: BlobHash) => void;\n private readonly onError: (h: BlobHash, err: unknown) => void;\n private readonly cache = new Map<BlobHash, Uint8Array>();\n /** In-flight fetches, keyed by hash. Promise resolves to the bytes\n * (cached) or `null` (not found / error). */\n private readonly inflight = new Map<BlobHash, Promise<Uint8Array | null>>();\n\n constructor(opts: BlobCacheOptions) {\n this.store = opts.store;\n this.onResolved = opts.onResolved ?? (() => {});\n this.onError =\n opts.onError ??\n ((h, err) => console.warn(`[blob-cache] fetch ${h} failed:`, err));\n }\n\n /**\n * Synchronously read bytes for a hash. Returns `null` if not yet\n * cached. Doesn't trigger a fetch — call `ensureLoaded` first if\n * you need to wait.\n */\n get(hash: BlobHash): Uint8Array | null {\n return this.cache.get(hash) ?? null;\n }\n\n /** Whether the hash is currently in the cache. */\n has(hash: BlobHash): boolean {\n return this.cache.has(hash);\n }\n\n /**\n * Insert bytes into the cache directly. Used when the embedder\n * already has the bytes in hand (paste image, font embed, DOCX\n * import) and wants the local renderer to find them immediately.\n *\n * Returns the bytes' hash. Caller is responsible for uploading\n * to the BlobStore separately (typically `await store.put(bytes)`\n * with the same input).\n */\n put(hash: BlobHash, bytes: Uint8Array): void {\n if (!this.cache.has(hash)) {\n this.cache.set(hash, new Uint8Array(bytes));\n }\n }\n\n /**\n * Ensure the given hashes are in the cache. Returns a Promise that\n * resolves once every hash is either in the cache or has failed to\n * fetch (failures don't reject the Promise — they're reported via\n * `onError` and skipped, consistent with the \"best-effort\n * availability\" model).\n *\n * Already-cached hashes resolve immediately. Already-in-flight\n * fetches are deduplicated (multiple concurrent callers wait on\n * the same Promise).\n */\n async ensureLoaded(hashes: readonly BlobHash[]): Promise<void> {\n const fetches: Promise<unknown>[] = [];\n for (const hash of hashes) {\n if (this.cache.has(hash)) continue;\n fetches.push(this.fetchOne(hash));\n }\n if (fetches.length === 0) return;\n await Promise.allSettled(fetches);\n }\n\n /**\n * Number of cached blobs. Diagnostic / test helper; production\n * code shouldn't depend on this for correctness.\n */\n size(): number {\n return this.cache.size;\n }\n\n /**\n * Clear all cached blobs. Used by the Editor on `destroy` to free\n * memory; future refinements may auto-evict.\n */\n clear(): void {\n this.cache.clear();\n this.inflight.clear();\n }\n\n // === internals ===\n\n private fetchOne(hash: BlobHash): Promise<Uint8Array | null> {\n const existing = this.inflight.get(hash);\n if (existing) return existing;\n const promise = (async (): Promise<Uint8Array | null> => {\n try {\n const bytes = await this.store.get(hash);\n if (bytes) {\n this.cache.set(hash, bytes);\n this.onResolved(hash);\n }\n return bytes;\n } catch (err) {\n this.onError(hash, err);\n return null;\n } finally {\n this.inflight.delete(hash);\n }\n })();\n this.inflight.set(hash, promise);\n return promise;\n }\n}\n","/**\n * Y.Doc shape used by Sobree.\n *\n * The document is stored as a Y.Doc — that's the source of truth.\n * `SobreeDocument` is a *projection* of this Y.Doc, computed by the\n * helpers in `./project.ts` and cached by the Editor.\n *\n * # Top-level layout\n *\n * ```\n * ydoc\n * ├── getArray(\"body\") : Y.Array<Y.Map> — block list, one Y.Map per block\n * ├── getMap(\"meta\") : Y.Map — sections, styles, numbering,\n * │ headerFooterBodies, fonts.\n * │ Stored as JSON-encoded values\n * │ (rarely edited concurrently).\n * │ Phase 1c may split fields into\n * │ per-key Y types.\n * ├── getMap(\"parts\") : Y.Map<Uint8Array> — inline binary parts\n * │ (legacy / no-BlobStore path).\n * └── getMap(\"partRefs\") : Y.Map<string> — partPath → SHA-256 hex hash\n * (Phase 3.2+ / BlobStore path).\n * Bytes live in a side-channel\n * `BlobStore`; the editor's\n * `BlobCache` resolves hashes.\n * ```\n *\n * Both `parts` and `partRefs` coexist — projection unions them so a\n * Y.Doc with inline legacy bytes alongside hash-addressed refs reads\n * correctly. The editor writes to whichever path matches its config\n * (BlobStore → partRefs, otherwise parts).\n *\n * # Block Y.Map shape — paragraphs (Phase 1b.5+)\n *\n * Paragraph blocks (kind === `\"paragraph\"`, including headings) are\n * structured for char-level CRDT:\n *\n * ```\n * paragraphMap\n * ├── get(\"id\") : string — stable block id (matches BlockRegistry)\n * ├── get(\"kind\") : \"paragraph\" — discriminator\n * ├── get(\"text\") : Y.Text — runs flatten into here, marks for marks,\n * │ embeds for breaks/tabs/fields/drawings,\n * │ `link: { href }` mark for hyperlink chars\n * └── get(\"props\") : string (JSON) — ParagraphProperties (alignment, indent,\n * numbering, …). JSON-encoded for v0.1;\n * concurrent property edits clobber.\n * ```\n *\n * # Block Y.Map shape — non-paragraphs\n *\n * Section breaks and tables stay JSON-encoded — neither has inline\n * text content with concurrent-edit demand. Tables get their own\n * structural CRDT in a future Phase 1c (per-cell Y.Map<body Y.Array>).\n *\n * ```\n * otherBlockMap\n * ├── get(\"id\") : string — stable block id\n * └── get(\"_ast\") : string (JSON) — JSON-encoded Block\n * ```\n *\n * # Y.Text mark conventions\n *\n * The mapping between Sobree's `RunProperties` and Y.Text marks is\n * 1:1 — `bold: true` becomes `{ bold: true }` on each char's\n * attributes. Two special cases:\n *\n * - `link: { href }` — chars inside a `HyperlinkRun` carry this mark.\n * The decoder reconstructs the HyperlinkRun by grouping consecutive\n * same-href chars.\n * - Embeds — non-text runs (break / tab / field / drawing) appear as\n * embed objects (`{ insert: { __sobree: \"<kind>\", … } }` in delta\n * form), occupying one position in the Y.Text.\n *\n * See `./runs.ts` for the conversion helpers and `./textDiff.ts` for\n * the smart Y.Text diff that preserves CRDT semantics across applies.\n */\n\nexport const Y_BODY_KEY = \"body\";\nexport const Y_META_KEY = \"meta\";\n/**\n * Inline binary parts — `Y.Map<string, Uint8Array>` keyed by part\n * path (e.g. `word/media/image1.png`). The legacy path: bytes ride\n * along inside Y updates. Acceptable for small docs; expensive at\n * scale (every peer replicates every byte).\n *\n * Used when no `BlobStore` is configured on the editor.\n */\nexport const Y_PARTS_KEY = \"parts\";\n/**\n * Content-hashed part references — `Y.Map<string, string>` keyed by\n * part path, valued by SHA-256 hex hash. The Phase 3.2+ path: bytes\n * live in a side-channel `BlobStore`, the Y.Doc carries only small\n * 64-char hashes. Y updates stay tiny regardless of image size.\n *\n * Used when a `BlobStore` is configured. The projection's `rawParts`\n * resolves hashes against the editor's local `BlobCache` (which\n * fetches from the store on miss).\n *\n * Both `parts` and `partRefs` coexist — a Y.Doc can carry inline\n * legacy bytes alongside hash-addressed refs. Projection unions\n * them; mixed-mode swarms work.\n */\nexport const Y_PARTREFS_KEY = \"partRefs\";\n\nexport const Y_BLOCK_ID_KEY = \"id\";\n/** Discriminator: `\"paragraph\"` blocks have `text` + `props`; everything\n * else has `_ast`. Defaults to \"_ast\" path for forward-compat with\n * Phase 1a docs that didn't write a kind field. */\nexport const Y_BLOCK_KIND_KEY = \"kind\";\n/** Phase 1b.5+: Y.Text on paragraph blocks. */\nexport const Y_BLOCK_TEXT_KEY = \"text\";\n/** Phase 1b.5+: JSON-encoded ParagraphProperties on paragraph blocks. */\nexport const Y_BLOCK_PROPS_KEY = \"props\";\n/** Phase 1a: JSON-encoded Block on non-paragraph blocks (and on\n * Phase 1a-shaped paragraph blocks for backwards compat). */\nexport const Y_BLOCK_AST_KEY = \"_ast\";\n\n/** Keys stored on the `meta` Y.Map. */\nexport const Y_META_FIELDS = {\n sections: \"sections\",\n headerFooterBodies: \"headerFooterBodies\",\n styles: \"styles\",\n numbering: \"numbering\",\n fonts: \"fonts\",\n} as const;\n","/**\n * Conversion between Sobree's `InlineRun[]` model and Yjs Y.Text's\n * delta representation.\n *\n * # Why deltas\n *\n * Y.Text exposes its content as a \"delta\" — an array of insert\n * operations, each with an optional `attributes` map. Plain text:\n *\n * [{ insert: \"Hello, \" }, { insert: \"world\", attributes: { bold: true } }]\n *\n * Embedded non-string content (line breaks, tabs, drawings, fields)\n * becomes an \"embed\" — an object literal in `insert`:\n *\n * [{ insert: \"Hello\" }, { insert: { __sobree: \"break\", type: \"line\" } }]\n *\n * # Sobree InlineRun ↔ delta mapping\n *\n * | InlineRun kind | Delta op |\n * |-----------------|---------------------------------------------------------------------|\n * | `text` | `{ insert: text, attributes: <run-property marks> }` |\n * | `break` | `{ insert: { __sobree: \"break\", type } }` |\n * | `tab` | `{ insert: { __sobree: \"tab\" } }` |\n * | `field` | `{ insert: { __sobree: \"field\", instruction, cached? } }` |\n * | `drawing` | `{ insert: { __sobree: \"drawing\", partPath, … } }` |\n * | `hyperlink` | recursively expand children; each char gets a `link: { href }` mark |\n *\n * The `__sobree` discriminator prefix avoids any future collision with\n * Yjs-internal attribute names.\n *\n * # Hyperlinks\n *\n * Sobree's model has hyperlinks as a *container* run with nested\n * children — OOXML's `<w:hyperlink>` element. Y.Text marks are flat\n * (per-char attributes). To round-trip:\n *\n * - Encode: every char in a hyperlink's children gets a\n * `link: { href }` attribute. Nested formatting (bold inside link)\n * stays as separate marks alongside `link`.\n * - Decode: walk the delta; group consecutive ops with the same\n * `link.href` into a HyperlinkRun whose children are the inner\n * runs (with the `link` mark stripped).\n *\n * Nested hyperlinks aren't valid OOXML and aren't supported. The\n * decoder treats them as the outermost link.\n */\n\nimport type {\n BreakRun,\n DrawingRun,\n FieldRun,\n HyperlinkRun,\n InlineRun,\n RunProperties,\n TabRun,\n TextRun,\n} from \"../doc/types\";\n\nexport interface DeltaOp {\n /** Text content (string) or embed (object literal). Y.Text differentiates by typeof. */\n insert: string | EmbedContent;\n /** Per-op marks — bold, italic, color, link, etc. Undefined when\n * no marks apply (Yjs preference: omit rather than set empty). */\n attributes?: Record<string, unknown>;\n}\n\n/** All non-text run kinds become embed objects in the delta. */\nexport type EmbedContent =\n | { __sobree: \"break\"; type: BreakRun[\"type\"] }\n | { __sobree: \"tab\" }\n | { __sobree: \"field\"; instruction: string; cached?: string }\n | {\n __sobree: \"drawing\";\n partPath: string;\n widthEmu: number;\n heightEmu: number;\n placement: DrawingRun[\"placement\"];\n altText?: string;\n };\n\n/** The `link` mark — stamped on every char inside a HyperlinkRun. */\nexport interface LinkMark {\n href: string;\n}\n\n// === runs → delta ===\n\nexport function runsToDelta(runs: readonly InlineRun[]): DeltaOp[] {\n const out: DeltaOp[] = [];\n appendRuns(runs, out, undefined);\n return out;\n}\n\nfunction appendRuns(\n runs: readonly InlineRun[],\n out: DeltaOp[],\n parentMarks: Record<string, unknown> | undefined,\n): void {\n for (const run of runs) {\n appendRun(run, out, parentMarks);\n }\n}\n\nfunction appendRun(\n run: InlineRun,\n out: DeltaOp[],\n parentMarks: Record<string, unknown> | undefined,\n): void {\n if (run.kind === \"text\") {\n const marks = mergeMarks(parentMarks, runPropsToAttrs(run.properties));\n pushOp(out, run.text, marks);\n return;\n }\n if (run.kind === \"break\") {\n const marks = mergeMarks(parentMarks, runPropsToAttrs(run.properties));\n pushOp(out, { __sobree: \"break\", type: run.type }, marks);\n return;\n }\n if (run.kind === \"tab\") {\n const marks = mergeMarks(parentMarks, runPropsToAttrs(run.properties));\n pushOp(out, { __sobree: \"tab\" }, marks);\n return;\n }\n if (run.kind === \"field\") {\n const marks = mergeMarks(parentMarks, runPropsToAttrs(run.properties));\n const embed: EmbedContent = {\n __sobree: \"field\",\n instruction: run.instruction,\n };\n if (run.cached !== undefined) (embed as { cached?: string }).cached = run.cached;\n pushOp(out, embed, marks);\n return;\n }\n if (run.kind === \"drawing\") {\n const embed: EmbedContent = {\n __sobree: \"drawing\",\n partPath: run.partPath,\n widthEmu: run.widthEmu,\n heightEmu: run.heightEmu,\n placement: run.placement,\n };\n if (run.altText) (embed as { altText?: string }).altText = run.altText;\n pushOp(out, embed, parentMarks);\n return;\n }\n if (run.kind === \"hyperlink\") {\n const linkMark: LinkMark = { href: run.href };\n const childMarks = mergeMarks(parentMarks, {\n ...runPropsToAttrs(run.properties),\n link: linkMark,\n });\n appendRuns(run.children, out, childMarks);\n return;\n }\n}\n\nfunction pushOp(\n out: DeltaOp[],\n insert: string | EmbedContent,\n attributes: Record<string, unknown> | undefined,\n): void {\n const op: DeltaOp = { insert };\n if (attributes && Object.keys(attributes).length > 0) {\n op.attributes = attributes;\n }\n out.push(op);\n}\n\nfunction mergeMarks(\n parent: Record<string, unknown> | undefined,\n child: Record<string, unknown> | undefined,\n): Record<string, unknown> | undefined {\n if (!parent && !child) return undefined;\n if (!parent) return child;\n if (!child) return parent;\n return { ...parent, ...child };\n}\n\n// === delta → runs ===\n\nexport function deltaToRuns(delta: readonly DeltaOp[]): InlineRun[] {\n const out: InlineRun[] = [];\n let i = 0;\n while (i < delta.length) {\n const op = delta[i];\n if (!op) {\n i++;\n continue;\n }\n const linkAttr = readLinkMark(op.attributes);\n\n if (linkAttr) {\n // Group consecutive ops with the same link.\n const linkChildren: InlineRun[] = [];\n let j = i;\n while (j < delta.length) {\n const peek = delta[j];\n if (!peek) break;\n const peekLink = readLinkMark(peek.attributes);\n if (!peekLink || peekLink.href !== linkAttr.href) break;\n const stripped: DeltaOp = { insert: peek.insert };\n const peekAttrs = peek.attributes;\n if (peekAttrs) {\n const cleaned = stripKey(peekAttrs, \"link\");\n if (Object.keys(cleaned).length > 0) stripped.attributes = cleaned;\n }\n linkChildren.push(...deltaToRuns([stripped]));\n j++;\n }\n const link: HyperlinkRun = {\n kind: \"hyperlink\",\n href: linkAttr.href,\n children: linkChildren,\n };\n out.push(link);\n i = j;\n continue;\n }\n\n out.push(opToRun(op));\n i++;\n }\n return out;\n}\n\nfunction opToRun(op: DeltaOp): InlineRun {\n if (typeof op.insert === \"string\") {\n const properties = attrsToRunProps(op.attributes);\n const text: TextRun = { kind: \"text\", text: op.insert, properties };\n return text;\n }\n // Defensive: if `insert` is missing or unrecognized, fall back to\n // an empty text run rather than crashing. Forward-compat for\n // future unknown embed kinds.\n const embed = op.insert as EmbedContent | undefined;\n if (!embed || typeof embed !== \"object\") {\n return { kind: \"text\", text: \"\", properties: {} };\n }\n if (embed.__sobree === \"break\") {\n const props = attrsToRunProps(op.attributes);\n const r: BreakRun = { kind: \"break\", type: embed.type };\n if (Object.keys(props).length > 0) r.properties = props;\n return r;\n }\n if (embed.__sobree === \"tab\") {\n const props = attrsToRunProps(op.attributes);\n const r: TabRun = { kind: \"tab\" };\n if (Object.keys(props).length > 0) r.properties = props;\n return r;\n }\n if (embed.__sobree === \"field\") {\n const props = attrsToRunProps(op.attributes);\n const r: FieldRun = { kind: \"field\", instruction: embed.instruction };\n if (embed.cached !== undefined) r.cached = embed.cached;\n if (Object.keys(props).length > 0) r.properties = props;\n return r;\n }\n if (embed.__sobree === \"drawing\") {\n const r: DrawingRun = {\n kind: \"drawing\",\n partPath: embed.partPath,\n widthEmu: embed.widthEmu,\n heightEmu: embed.heightEmu,\n placement: embed.placement,\n };\n if (embed.altText) r.altText = embed.altText;\n return r;\n }\n // Unknown embed kind — fall back to empty text run.\n return { kind: \"text\", text: \"\", properties: {} };\n}\n\nfunction readLinkMark(attrs: Record<string, unknown> | undefined): LinkMark | null {\n if (!attrs) return null;\n const link = attrs.link;\n if (!link || typeof link !== \"object\") return null;\n const href = (link as { href?: unknown }).href;\n if (typeof href !== \"string\") return null;\n return { href };\n}\n\nfunction stripKey(\n obj: Record<string, unknown>,\n key: string,\n): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(obj)) {\n if (k !== key) out[k] = v;\n }\n return out;\n}\n\n// === RunProperties ↔ attributes ===\n\n/**\n * Convert RunProperties to a flat attributes object. Empty / undefined\n * fields are omitted entirely (Yjs convention). Returns `undefined`\n * when no marks apply.\n */\nexport function runPropsToAttrs(\n props: RunProperties | undefined,\n): Record<string, unknown> | undefined {\n if (!props) return undefined;\n const out: Record<string, unknown> = {};\n for (const key of Object.keys(props) as Array<keyof RunProperties>) {\n const v = props[key];\n if (v === undefined) continue;\n out[key as string] = v;\n }\n return Object.keys(out).length > 0 ? out : undefined;\n}\n\n/**\n * Inverse of `runPropsToAttrs`. Strips the `link` key (it's handled\n * separately as a hyperlink wrapper). Strips any unknown attributes\n * defensively (forward-compat: a future plugin may add marks Sobree\n * doesn't model).\n */\nexport function attrsToRunProps(\n attrs: Record<string, unknown> | undefined,\n): RunProperties {\n if (!attrs) return {};\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(attrs)) {\n if (k === \"link\") continue;\n out[k] = v;\n }\n return out as RunProperties;\n}\n\n// === structural equality (used by textDiff) ===\n\n/**\n * Deep equality for delta op contents — strings compared as `===`,\n * embeds and attribute objects compared structurally. Used by the\n * Y.Text diff to detect cells that haven't changed.\n */\nexport function deepEqual(a: unknown, b: unknown): boolean {\n if (a === b) return true;\n if (a === null || b === null) return false;\n if (typeof a !== \"object\" || typeof b !== \"object\") return false;\n if (Array.isArray(a) !== Array.isArray(b)) return false;\n if (Array.isArray(a) && Array.isArray(b)) {\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) {\n if (!deepEqual(a[i], b[i])) return false;\n }\n return true;\n }\n const ao = a as Record<string, unknown>;\n const bo = b as Record<string, unknown>;\n const ak = Object.keys(ao);\n const bk = Object.keys(bo);\n if (ak.length !== bk.length) return false;\n for (const k of ak) {\n if (!deepEqual(ao[k], bo[k])) return false;\n }\n return true;\n}\n","import * as Y from \"yjs\";\nimport type { Block, Paragraph, SobreeDocument } from \"../doc/types\";\nimport { runsToDelta } from \"./runs\";\nimport {\n Y_BLOCK_AST_KEY,\n Y_BLOCK_ID_KEY,\n Y_BLOCK_KIND_KEY,\n Y_BLOCK_PROPS_KEY,\n Y_BLOCK_TEXT_KEY,\n Y_BODY_KEY,\n Y_META_FIELDS,\n Y_META_KEY,\n Y_PARTS_KEY,\n} from \"./schema\";\n\n/**\n * Populate a fresh (or reset) Y.Doc with the contents of a SobreeDocument.\n *\n * Caller supplies the parallel `ids` array — these become the stable\n * block ids stored in each block's Y.Map. The Editor's BlockRegistry\n * generates them.\n *\n * Wraps the seed in `Y.Doc.transact` with `origin = \"seed\"` so a\n * UndoManager scoped to the local origin won't push the seed onto its\n * stack.\n */\nexport function seedYDoc(ydoc: Y.Doc, doc: SobreeDocument, ids: readonly string[]): void {\n if (ids.length !== doc.body.length) {\n throw new Error(\n `seedYDoc: ids length (${ids.length}) !== body length (${doc.body.length})`,\n );\n }\n const body = ydoc.getArray<Y.Map<unknown>>(Y_BODY_KEY);\n const meta = ydoc.getMap<string>(Y_META_KEY);\n const parts = ydoc.getMap<Uint8Array>(Y_PARTS_KEY);\n\n ydoc.transact(() => {\n // Reset\n if (body.length > 0) body.delete(0, body.length);\n for (const k of [...meta.keys()]) meta.delete(k);\n for (const k of [...parts.keys()]) parts.delete(k);\n\n // Body — two-phase to keep Yjs happy:\n // Phase 1: build skeleton Y.Maps (id + kind + empty Y.Text for\n // paragraphs), insert them all into body. Yjs integrates\n // each map and its child Y.Text on insert.\n // Phase 2: populate content (applyDelta to the now-integrated\n // Y.Texts; JSON for everything else).\n //\n // Doing applyDelta on an unintegrated Y.Text *works* (Yjs queues the\n // operations) but produces \"Invalid access\" warnings on subsequent\n // reads. Two-phase avoids the noise.\n const blockMaps = doc.body.map((block, i) =>\n buildSkeletonBlockYMap(ids[i] ?? \"\", block),\n );\n if (blockMaps.length > 0) body.insert(0, blockMaps);\n for (let i = 0; i < doc.body.length; i++) {\n populateBlockContent(blockMaps[i]!, doc.body[i]!);\n }\n\n // Meta — JSON-encoded for v0.1\n meta.set(Y_META_FIELDS.sections, JSON.stringify(doc.sections));\n meta.set(Y_META_FIELDS.headerFooterBodies, JSON.stringify(doc.headerFooterBodies));\n meta.set(Y_META_FIELDS.styles, JSON.stringify(doc.styles));\n meta.set(Y_META_FIELDS.numbering, JSON.stringify(doc.numbering));\n meta.set(Y_META_FIELDS.fonts, JSON.stringify(doc.fonts));\n\n // Parts (binary) — Y supports Uint8Array natively\n for (const [path, bytes] of Object.entries(doc.rawParts)) {\n parts.set(path, bytes);\n }\n }, /* origin */ \"seed\");\n}\n\n/**\n * Build a *skeleton* block Y.Map — id + kind discriminator + empty\n * containers, no content. Content lands in `populateBlockContent`\n * after the map is integrated. Used internally by `seedYDoc` and by\n * `applyDocumentToYDoc` (`buildBlockYMap` is a one-shot wrapper that\n * does both phases on an orphan map; only safe for callers that will\n * insert it inside a transact within the same call stack).\n */\nexport function buildSkeletonBlockYMap(id: string, block: Block): Y.Map<unknown> {\n const m = new Y.Map<unknown>();\n m.set(Y_BLOCK_ID_KEY, id);\n if (block.kind === \"paragraph\") {\n m.set(Y_BLOCK_KIND_KEY, \"paragraph\");\n m.set(Y_BLOCK_TEXT_KEY, new Y.Text());\n // Props go in populateBlockContent; we set the kind here so\n // anyone projecting from a skeleton sees the right discriminator.\n }\n // Non-paragraph: no skeleton needed; populateBlockContent will set _ast.\n return m;\n}\n\n/** Populate the content of an *integrated* block Y.Map. */\nexport function populateBlockContent(map: Y.Map<unknown>, block: Block): void {\n if (block.kind === \"paragraph\") {\n populateParagraphContent(map, block);\n } else {\n map.set(Y_BLOCK_AST_KEY, JSON.stringify(block));\n }\n}\n\n/**\n * Populate paragraph content. Caller guarantees the map is integrated\n * (so its child Y.Text is too).\n */\nexport function populateParagraphContent(\n map: Y.Map<unknown>,\n block: Paragraph,\n): void {\n // Properties.\n map.set(Y_BLOCK_PROPS_KEY, JSON.stringify(block.properties));\n // Text — must already exist on the map (set by buildSkeletonBlockYMap).\n let text = map.get(Y_BLOCK_TEXT_KEY);\n if (!(text instanceof Y.Text)) {\n text = new Y.Text();\n map.set(Y_BLOCK_TEXT_KEY, text);\n }\n const delta = runsToDelta(block.runs);\n if (delta.length > 0) {\n (text as Y.Text).applyDelta(\n delta as Array<{ insert: unknown; attributes?: object }>,\n );\n }\n}\n\n/**\n * One-shot block Y.Map builder — skeleton + content in a single call.\n * Only safe inside a `Y.Doc.transact` block where the returned map\n * will be inserted into an integrated parent before any read of its\n * Y.Text. Otherwise prefer the two-phase builders above.\n *\n * Kept for backwards compat with callers that don't yet use the\n * two-phase pattern.\n */\nexport function buildBlockYMap(id: string, block: Block): Y.Map<unknown> {\n const m = buildSkeletonBlockYMap(id, block);\n populateBlockContent(m, block);\n return m;\n}\n\n/**\n * @deprecated Use `populateParagraphContent` (renamed for clarity).\n * Kept as a re-export so callers don't break during migration.\n */\nexport const populateParagraphYMap = populateParagraphContent;\n","import type * as Y from \"yjs\";\nimport * as YModule from \"yjs\";\nimport type {\n Block,\n FontDeclaration,\n NamedStyle,\n NumberingDefinition,\n Paragraph,\n ParagraphProperties,\n SectionProperties,\n SobreeDocument,\n} from \"../doc/types\";\nimport { type DeltaOp, deltaToRuns } from \"./runs\";\nimport {\n Y_BLOCK_AST_KEY,\n Y_BLOCK_ID_KEY,\n Y_BLOCK_KIND_KEY,\n Y_BLOCK_PROPS_KEY,\n Y_BLOCK_TEXT_KEY,\n Y_BODY_KEY,\n Y_META_FIELDS,\n Y_META_KEY,\n Y_PARTREFS_KEY,\n Y_PARTS_KEY,\n} from \"./schema\";\n\n/**\n * Read the SobreeDocument projection out of a Y.Doc.\n *\n * Returns:\n *\n * - **`doc`** — the projected SobreeDocument. Its `rawParts` is\n * populated from the Y.Doc's inline `parts` Y.Map only.\n * - **`ids`** — block-id order matching `doc.body`. The Editor\n * uses this to keep its BlockRegistry in sync.\n * - **`partRefs`** — partPath → hash mappings from the Y.Doc's\n * `partRefs` Y.Map (Phase 3.2+). Empty `{}` when no BlobStore-\n * using peer has touched the doc. **The caller (Editor /\n * HeadlessSobree) is responsible for resolving these through a\n * `BlobCache`** and merging the results into `doc.rawParts`.\n * We keep the resolution decoupled so projection has no async\n * work or external dependency.\n *\n * Two block shapes are supported:\n *\n * - Phase 1b.5+: paragraphs as `{ kind: \"paragraph\", text: Y.Text, props }`.\n * - Phase 1a: everything else as `{ _ast: JSON }`.\n *\n * Backwards compat: a Phase 1a-shaped paragraph (only `_ast`, no `kind`\n * field) projects identically — the JSON is parsed straight to\n * `Paragraph`.\n *\n * This is allocation-heavy; the Editor caches the result and invalidates\n * on Y.Doc updates.\n */\nexport function projectYDoc(ydoc: Y.Doc): {\n doc: SobreeDocument;\n ids: string[];\n partRefs: Record<string, string>;\n} {\n const body = ydoc.getArray<Y.Map<unknown>>(Y_BODY_KEY);\n const meta = ydoc.getMap<string>(Y_META_KEY);\n const parts = ydoc.getMap<Uint8Array>(Y_PARTS_KEY);\n const partRefsMap = ydoc.getMap<string>(Y_PARTREFS_KEY);\n\n const blocks: Block[] = [];\n const ids: string[] = [];\n body.forEach((m) => {\n const id = (m.get(Y_BLOCK_ID_KEY) as string | undefined) ?? \"\";\n const block = projectBlock(m);\n if (!block) return;\n blocks.push(block);\n ids.push(id);\n });\n\n const sections = parseMeta<SectionProperties[]>(meta, Y_META_FIELDS.sections, []);\n const headerFooterBodies = parseMeta<Record<string, Block[]>>(\n meta,\n Y_META_FIELDS.headerFooterBodies,\n {},\n );\n const styles = parseMeta<NamedStyle[]>(meta, Y_META_FIELDS.styles, []);\n const numbering = parseMeta<NumberingDefinition[]>(meta, Y_META_FIELDS.numbering, []);\n const fonts = parseMeta<FontDeclaration[]>(meta, Y_META_FIELDS.fonts, []);\n\n const rawParts: Record<string, Uint8Array> = {};\n parts.forEach((bytes, path) => {\n rawParts[path] = bytes;\n });\n\n const partRefs: Record<string, string> = {};\n partRefsMap.forEach((hash, path) => {\n partRefs[path] = hash;\n });\n\n return {\n doc: {\n body: blocks,\n sections,\n headerFooterBodies,\n styles,\n numbering,\n rawParts,\n fonts,\n },\n ids,\n partRefs,\n };\n}\n\n/**\n * Read a single block Y.Map into a Block. Returns `null` if the map\n * is empty / unrecognizable (defensive — projectYDoc skips nulls).\n */\nexport function projectBlock(map: Y.Map<unknown>): Block | null {\n // Phase 1b.5+: paragraph blocks have `kind === \"paragraph\"` and a\n // Y.Text under `text`.\n const kind = map.get(Y_BLOCK_KIND_KEY) as string | undefined;\n if (kind === \"paragraph\") {\n return projectParagraph(map);\n }\n // Phase 1a fallback: JSON-encoded block under `_ast`.\n const ast = map.get(Y_BLOCK_AST_KEY) as string | undefined;\n if (ast) {\n try {\n return JSON.parse(ast) as Block;\n } catch {\n return null;\n }\n }\n return null;\n}\n\nfunction projectParagraph(map: Y.Map<unknown>): Paragraph | null {\n const textObj = map.get(Y_BLOCK_TEXT_KEY);\n if (!(textObj instanceof YModule.Text)) return null;\n const propsStr = map.get(Y_BLOCK_PROPS_KEY) as string | undefined;\n let properties: ParagraphProperties = {};\n if (propsStr) {\n try {\n properties = JSON.parse(propsStr) as ParagraphProperties;\n } catch {\n properties = {};\n }\n }\n const delta = textObj.toDelta() as DeltaOp[];\n const runs = deltaToRuns(delta);\n return { kind: \"paragraph\", properties, runs };\n}\n\nfunction parseMeta<T>(meta: Y.Map<string>, key: string, fallback: T): T {\n const s = meta.get(key);\n if (!s) return fallback;\n try {\n return JSON.parse(s) as T;\n } catch {\n return fallback;\n }\n}\n","/**\n * Compute the minimal Y.Text mutations to bring a Y.Text in sync with\n * a target delta.\n *\n * # Why minimal mutations matter\n *\n * Y.Text is a CRDT — every `insert` / `delete` / `format` becomes a\n * Yjs operation broadcast to peers. The smaller and more targeted the\n * operations, the less chance of conflicting with concurrent edits.\n *\n * Replacing the whole text on every change destroys CRDT semantics:\n * one peer's \"delete-all-and-reinsert\" wipes whatever the other peer\n * was simultaneously typing. The diff in this module is the\n * difference between \"two peers can type in the same paragraph\" and\n * \"one peer always wins\".\n *\n * # Algorithm\n *\n * Three paths in increasing generality:\n *\n * 1. **No-op** — current and target deltas are structurally identical.\n * Skip entirely. Catches the (common) case where a higher-level\n * mutation didn't actually change this paragraph.\n *\n * 2. **Format-only** — same length and same per-position content\n * (chars or embeds), but at least one position has different\n * attributes. Walk and call `format()` for each changed range.\n * This is the case for \"bold the selection\" — the text content\n * is unchanged, just the marks; using format() instead of\n * delete+reinsert preserves CRDT for any concurrent typing\n * inside the formatted range.\n *\n * 3. **Prefix/suffix replace** — find the longest common prefix and\n * longest common suffix of cells, then `delete()` the differing\n * middle of the old and `insert()` the differing middle of the\n * new. This handles typing (insert at position N), deletion,\n * paste, and any other content-changing edit. Concurrent edits\n * OUTSIDE the changed range are preserved by Y.Text's CRDT.\n *\n * # Cells\n *\n * Both deltas are flattened to a `Cell[]` representation: one cell\n * per character (for string inserts) or per embed (for object inserts).\n * Cells carry the per-position attributes — Y.Text marks that\n * surround the chunk get duplicated onto each cell.\n *\n * This is allocation-heavy for long paragraphs but bounded by\n * paragraph length (rarely > 1000 chars). For Phase 1c we can switch\n * to a streaming diff.\n */\n\nimport type * as Y from \"yjs\";\nimport { type DeltaOp, deepEqual } from \"./runs\";\n\ninterface Cell {\n /** \"char\" or \"embed\" — embeds occupy 1 Y.Text position. */\n kind: \"char\" | \"embed\";\n /** For \"char\": the single character. For \"embed\": empty string. */\n char: string;\n /** For \"embed\": the embed object. For \"char\": null. */\n embed: object | null;\n /** Per-position attributes. Always an object (possibly empty). */\n attrs: Record<string, unknown>;\n}\n\n/**\n * Apply the difference between Y.Text's current state and `targetDelta`\n * as the minimal set of Y.Text mutations. Caller is responsible for\n * wrapping in `Y.Doc.transact` if it wants the changes batched into\n * one transaction with whatever else.\n */\nexport function diffApplyText(yText: Y.Text, targetDelta: readonly DeltaOp[]): void {\n const oldDelta = yText.toDelta() as DeltaOp[];\n const oldCells = flatten(oldDelta);\n const newCells = flatten(targetDelta);\n\n // Path 1: no-op.\n if (cellsEqual(oldCells, newCells)) return;\n\n // Path 2: format-only.\n if (\n oldCells.length === newCells.length &&\n oldCells.every((c, i) => contentEqual(c, newCells[i]!))\n ) {\n applyFormatOnly(yText, oldCells, newCells);\n return;\n }\n\n // Path 3: prefix/suffix replace.\n applyPrefixSuffixDiff(yText, oldCells, newCells);\n}\n\n// === flatten ===\n\nfunction flatten(delta: readonly DeltaOp[]): Cell[] {\n const out: Cell[] = [];\n for (const op of delta) {\n const attrs = (op.attributes ?? {}) as Record<string, unknown>;\n if (typeof op.insert === \"string\") {\n // Iterate characters via spread to handle surrogate pairs as\n // single units. Y.Text positions count UTF-16 code units, so\n // emoji etc. occupy 2 positions; we still emit 2 cells (one per\n // code unit) to keep position math accurate.\n for (let i = 0; i < op.insert.length; i++) {\n out.push({\n kind: \"char\",\n char: op.insert.charAt(i),\n embed: null,\n attrs,\n });\n }\n } else if (op.insert && typeof op.insert === \"object\") {\n out.push({\n kind: \"embed\",\n char: \"\",\n embed: op.insert as object,\n attrs,\n });\n }\n }\n return out;\n}\n\n// === comparisons ===\n\nfunction cellsEqual(a: readonly Cell[], b: readonly Cell[]): boolean {\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) {\n if (!cellEqual(a[i]!, b[i]!)) return false;\n }\n return true;\n}\n\nfunction cellEqual(a: Cell, b: Cell): boolean {\n return contentEqual(a, b) && deepEqual(a.attrs, b.attrs);\n}\n\nfunction contentEqual(a: Cell, b: Cell): boolean {\n if (a.kind !== b.kind) return false;\n if (a.kind === \"char\") return a.char === b.char;\n return deepEqual(a.embed, b.embed);\n}\n\n// === path 2: format-only ===\n\nfunction applyFormatOnly(\n yText: Y.Text,\n oldCells: readonly Cell[],\n newCells: readonly Cell[],\n): void {\n // Walk; group consecutive positions with the same attribute delta\n // into one format() call.\n let i = 0;\n while (i < newCells.length) {\n const oldA = oldCells[i]!.attrs;\n const newA = newCells[i]!.attrs;\n if (deepEqual(oldA, newA)) {\n i++;\n continue;\n }\n // Compute the attr delta at this position.\n const delta = computeAttrDelta(oldA, newA);\n // Extend forward as long as the delta stays the same.\n let j = i + 1;\n while (j < newCells.length) {\n const dj = computeAttrDelta(oldCells[j]!.attrs, newCells[j]!.attrs);\n if (!deepEqual(delta, dj)) break;\n j++;\n }\n yText.format(i, j - i, delta);\n i = j;\n }\n}\n\n/**\n * Compute the delta to transform `oldAttrs` into `newAttrs`. Keys\n * present in oldAttrs but absent in newAttrs map to `null` (Y.Text's\n * convention for \"remove this mark\").\n */\nfunction computeAttrDelta(\n oldAttrs: Record<string, unknown>,\n newAttrs: Record<string, unknown>,\n): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n // Set / change.\n for (const [k, v] of Object.entries(newAttrs)) {\n if (!deepEqual(oldAttrs[k], v)) out[k] = v;\n }\n // Remove.\n for (const k of Object.keys(oldAttrs)) {\n if (!(k in newAttrs)) out[k] = null;\n }\n return out;\n}\n\n// === path 3: prefix/suffix replace ===\n\nfunction applyPrefixSuffixDiff(\n yText: Y.Text,\n oldCells: readonly Cell[],\n newCells: readonly Cell[],\n): void {\n // Common prefix length.\n let prefix = 0;\n while (\n prefix < oldCells.length &&\n prefix < newCells.length &&\n cellEqual(oldCells[prefix]!, newCells[prefix]!)\n ) {\n prefix++;\n }\n // Common suffix length (don't cross into prefix).\n let suffix = 0;\n while (\n suffix < oldCells.length - prefix &&\n suffix < newCells.length - prefix &&\n cellEqual(\n oldCells[oldCells.length - 1 - suffix]!,\n newCells[newCells.length - 1 - suffix]!,\n )\n ) {\n suffix++;\n }\n\n const deleteLen = oldCells.length - prefix - suffix;\n if (deleteLen > 0) {\n yText.delete(prefix, deleteLen);\n }\n\n // Insert the new middle, grouping consecutive same-attribute char\n // cells into single Y.Text.insert calls.\n insertCellsAt(yText, prefix, newCells, prefix, newCells.length - suffix);\n\n // After delete + insert, the prefix and suffix regions are\n // unchanged on the Y.Text — their CRDT identity is preserved.\n}\n\nfunction insertCellsAt(\n yText: Y.Text,\n startPos: number,\n cells: readonly Cell[],\n start: number,\n end: number,\n): void {\n let cursor = startPos;\n let i = start;\n while (i < end) {\n const cell = cells[i]!;\n if (cell.kind === \"embed\") {\n // y-text insertEmbed: 1 position, optional attributes.\n yText.insertEmbed(\n cursor,\n cell.embed as Record<string, unknown>,\n cell.attrs,\n );\n cursor++;\n i++;\n continue;\n }\n // Group consecutive char cells with identical attrs.\n let j = i + 1;\n while (j < end && cells[j]!.kind === \"char\" && deepEqual(cells[j]!.attrs, cell.attrs)) {\n j++;\n }\n const text = cellsToString(cells, i, j);\n yText.insert(cursor, text, cell.attrs);\n cursor += text.length;\n i = j;\n }\n}\n\nfunction cellsToString(cells: readonly Cell[], start: number, end: number): string {\n let out = \"\";\n for (let i = start; i < end; i++) out += cells[i]!.char;\n return out;\n}\n","import * as Y from \"yjs\";\nimport type { Block, Paragraph, SobreeDocument } from \"../doc/types\";\nimport { runsToDelta } from \"./runs\";\nimport {\n buildSkeletonBlockYMap,\n populateBlockContent,\n populateParagraphContent,\n} from \"./seed\";\nimport {\n Y_BLOCK_AST_KEY,\n Y_BLOCK_ID_KEY,\n Y_BLOCK_KIND_KEY,\n Y_BLOCK_PROPS_KEY,\n Y_BLOCK_TEXT_KEY,\n Y_BODY_KEY,\n Y_META_FIELDS,\n Y_META_KEY,\n Y_PARTREFS_KEY,\n Y_PARTS_KEY,\n} from \"./schema\";\nimport { diffApplyText } from \"./textDiff\";\n\n/**\n * Apply a new SobreeDocument to a Y.Doc, diffing against the current\n * Y state by **block id**. The caller supplies the new id-per-block\n * array (typically derived from the BlockRegistry — see\n * `Editor.applyDocument`).\n *\n * # Granularity\n *\n * - **Body blocks:** matched by id; updated in-place / inserted /\n * removed. Concurrent edits to *different* blocks always merge\n * cleanly via the Y.Array CRDT.\n *\n * - **Paragraph block content:** the Y.Text is updated via\n * `diffApplyText` — minimal `insert` / `delete` / `format`\n * mutations matching what changed. Concurrent edits to *different\n * positions* in the same paragraph merge cleanly. Concurrent\n * edits to the *same* position still resolve via Yjs's standard\n * ordering (later op wins per Yjs's deterministic conflict rules).\n *\n * - **Paragraph properties (alignment, indent, …):** stored as a\n * JSON blob on the Y.Map's `props` field. Concurrent property\n * edits clobber. Phase 1c may split into per-key Y.Map.\n *\n * - **Non-paragraph blocks (section_break, table):** stored as\n * JSON-encoded `_ast`. Concurrent edits clobber.\n *\n * - **Document meta (sections, styles, numbering, fonts,\n * headerFooterBodies):** JSON-encoded on a `meta` Y.Map.\n * Clobber on concurrent edits — these change rarely.\n *\n * - **Binary parts (images, fonts):** stored as a Y.Map<Uint8Array>\n * with adds / removes detected by key. Phase 3 moves these to a\n * content-hashed blob store (out-of-band).\n *\n * The whole apply is wrapped in a single `Y.Doc.transact` with the\n * supplied origin so subscribers (UndoManager, change listeners) see\n * one batch.\n */\nexport interface ApplyDocumentOptions {\n /**\n * Part paths to **exclude** from the inline `parts` diff. Used when\n * a `BlobStore` is configured — paths already managed via `partRefs`\n * (or in-flight migration to it) must not also be written inline,\n * or the Y.Doc would carry duplicate bytes.\n *\n * Empty / absent: today's path — every `rawParts` entry mirrors\n * inline (no BlobStore).\n */\n skipPartPaths?: ReadonlySet<string>;\n}\n\nexport function applyDocumentToYDoc(\n ydoc: Y.Doc,\n newDoc: SobreeDocument,\n newIds: readonly string[],\n origin: unknown = \"applyDocument\",\n opts: ApplyDocumentOptions = {},\n): void {\n if (newIds.length !== newDoc.body.length) {\n throw new Error(\n `applyDocumentToYDoc: ids length (${newIds.length}) !== body length (${newDoc.body.length})`,\n );\n }\n const body = ydoc.getArray<Y.Map<unknown>>(Y_BODY_KEY);\n const meta = ydoc.getMap<string>(Y_META_KEY);\n const parts = ydoc.getMap<Uint8Array>(Y_PARTS_KEY);\n\n ydoc.transact(() => {\n diffBody(body, newDoc.body, newIds);\n diffMeta(meta, newDoc);\n diffParts(parts, newDoc.rawParts, opts.skipPartPaths);\n }, origin);\n}\n\n// === body diff ===\n\nfunction diffBody(\n body: Y.Array<Y.Map<unknown>>,\n newBlocks: readonly Block[],\n newIds: readonly string[],\n): void {\n // Determine which old ids survive in the new list.\n const newIdSet = new Set(newIds);\n\n // Walk old → drop blocks no longer present (right-to-left to preserve\n // indices during deletes).\n for (let i = body.length - 1; i >= 0; i--) {\n const m = body.get(i);\n const id = m.get(Y_BLOCK_ID_KEY) as string | undefined;\n if (!id || !newIdSet.has(id)) {\n body.delete(i, 1);\n }\n }\n\n // Walk new — for each desired (id, block) at index i:\n // - if body[i] already has this id → update in-place\n // - else if this id exists later in body → pull it forward\n // (delete + reinsert; loses the Y.Map identity but block-level\n // moves are rare)\n // - else → fresh block; insert\n for (let i = 0; i < newBlocks.length; i++) {\n const desiredId = newIds[i] ?? \"\";\n const desiredBlock = newBlocks[i];\n if (!desiredBlock) continue;\n\n const current = i < body.length ? body.get(i) : undefined;\n const currentId = current\n ? ((current.get(Y_BLOCK_ID_KEY) as string | undefined) ?? \"\")\n : \"\";\n\n if (current && currentId === desiredId) {\n updateBlockInPlace(current, desiredBlock);\n continue;\n }\n\n // Mismatch — does desiredId exist somewhere later in body?\n const existingIdx = findIdAtOrAfter(body, desiredId, i);\n if (existingIdx !== -1) {\n // Pull it forward by delete + reinsert. Y.Array doesn't have a\n // move op, so the Y.Map identity is lost.\n body.delete(existingIdx, 1);\n insertFreshBlock(body, i, desiredId, desiredBlock);\n continue;\n }\n\n // Brand new block — insert.\n insertFreshBlock(body, i, desiredId, desiredBlock);\n }\n\n // Trim any extras (defensive — keeps the invariant\n // body.length === newBlocks.length).\n while (body.length > newBlocks.length) {\n body.delete(body.length - 1, 1);\n }\n}\n\n/**\n * Update an existing block Y.Map in-place to reflect a new Block.\n *\n * Branches by the Y.Map's current shape vs the new block's kind:\n *\n * - both paragraph → diff text via Y.Text.diffApplyText; update\n * props via JSON-set-if-changed\n * - both non-paragraph → JSON-set-if-changed on `_ast`\n * - kind change (paragraph ↔ other) → wipe + repopulate as the new\n * shape (loses CRDT identity; uncommon)\n */\nfunction updateBlockInPlace(map: Y.Map<unknown>, block: Block): void {\n const currentKind = map.get(Y_BLOCK_KIND_KEY) as string | undefined;\n const isCurrentParagraph =\n currentKind === \"paragraph\" || map.get(Y_BLOCK_TEXT_KEY) instanceof Y.Text;\n const isNewParagraph = block.kind === \"paragraph\";\n\n if (isCurrentParagraph && isNewParagraph) {\n updateParagraphInPlace(map, block);\n return;\n }\n\n if (!isCurrentParagraph && !isNewParagraph) {\n // Both non-paragraph: JSON-set if changed.\n const desiredAst = JSON.stringify(block);\n const currentAst = map.get(Y_BLOCK_AST_KEY) as string | undefined;\n if (currentAst !== desiredAst) map.set(Y_BLOCK_AST_KEY, desiredAst);\n return;\n }\n\n // Kind change. Wipe everything except `id` and rebuild. Order\n // matters: clear before re-populating, since a non-paragraph\n // Y.Map will have `_ast` and a paragraph Y.Map will have `text`/\n // `props`/`kind`.\n const id = (map.get(Y_BLOCK_ID_KEY) as string | undefined) ?? \"\";\n for (const key of [...map.keys()]) {\n if (key === Y_BLOCK_ID_KEY) continue;\n map.delete(key);\n }\n if (isNewParagraph) {\n // Set kind discriminator + create the Y.Text. The map IS\n // integrated (we're inside a transact running against a\n // body Y.Array), so the new Y.Text integrates immediately.\n map.set(Y_BLOCK_KIND_KEY, \"paragraph\");\n map.set(Y_BLOCK_TEXT_KEY, new Y.Text());\n populateParagraphContent(map, block);\n } else {\n map.set(Y_BLOCK_AST_KEY, JSON.stringify(block));\n }\n // Re-set id in case it was somehow cleared.\n if (!map.has(Y_BLOCK_ID_KEY)) map.set(Y_BLOCK_ID_KEY, id);\n}\n\nfunction updateParagraphInPlace(map: Y.Map<unknown>, paragraph: Paragraph): void {\n // Make sure the kind discriminator is set (forward-migration from\n // Phase 1a paragraph shape that didn't write a kind).\n if (map.get(Y_BLOCK_KIND_KEY) !== \"paragraph\") {\n map.set(Y_BLOCK_KIND_KEY, \"paragraph\");\n }\n\n // Text content via smart diff. The map is integrated (we're inside\n // a transact on body), so a freshly created Y.Text integrates\n // immediately on map.set — no \"Invalid access\" warnings.\n let text = map.get(Y_BLOCK_TEXT_KEY);\n if (!(text instanceof Y.Text)) {\n // First time on this map (e.g. migrating from Phase 1a `_ast`).\n text = new Y.Text();\n map.set(Y_BLOCK_TEXT_KEY, text);\n if (map.has(Y_BLOCK_AST_KEY)) map.delete(Y_BLOCK_AST_KEY);\n }\n diffApplyText(text as Y.Text, runsToDelta(paragraph.runs));\n\n // Properties via JSON-set if changed.\n const desiredProps = JSON.stringify(paragraph.properties);\n const currentProps = map.get(Y_BLOCK_PROPS_KEY) as string | undefined;\n if (currentProps !== desiredProps) map.set(Y_BLOCK_PROPS_KEY, desiredProps);\n}\n\n/**\n * Insert a fresh block at `index`, two-phase: skeleton in, then\n * content. The skeleton is integrated by the body.insert call, so\n * the subsequent populate operates on integrated Y types (no\n * \"Invalid access\" warnings).\n */\nfunction insertFreshBlock(\n body: Y.Array<Y.Map<unknown>>,\n index: number,\n id: string,\n block: Block,\n): void {\n const skeleton = buildSkeletonBlockYMap(id, block);\n body.insert(index, [skeleton]);\n populateBlockContent(skeleton, block);\n}\n\nfunction findIdAtOrAfter(\n body: Y.Array<Y.Map<unknown>>,\n id: string,\n startIdx: number,\n): number {\n for (let i = startIdx; i < body.length; i++) {\n const m = body.get(i);\n if ((m.get(Y_BLOCK_ID_KEY) as string | undefined) === id) return i;\n }\n return -1;\n}\n\n// === meta diff ===\n\nfunction diffMeta(meta: Y.Map<string>, doc: SobreeDocument): void {\n setIfChanged(meta, Y_META_FIELDS.sections, JSON.stringify(doc.sections));\n setIfChanged(\n meta,\n Y_META_FIELDS.headerFooterBodies,\n JSON.stringify(doc.headerFooterBodies),\n );\n setIfChanged(meta, Y_META_FIELDS.styles, JSON.stringify(doc.styles));\n setIfChanged(meta, Y_META_FIELDS.numbering, JSON.stringify(doc.numbering));\n setIfChanged(meta, Y_META_FIELDS.fonts, JSON.stringify(doc.fonts));\n}\n\nfunction setIfChanged(meta: Y.Map<string>, key: string, value: string): void {\n if (meta.get(key) !== value) meta.set(key, value);\n}\n\n// === parts diff ===\n\nfunction diffParts(\n parts: Y.Map<Uint8Array>,\n next: Record<string, Uint8Array>,\n skip: ReadonlySet<string> | undefined,\n): void {\n // Drop missing — but never touch a skipped path (it's managed via\n // partRefs; an explicit delete elsewhere clears any stale inline\n // entry).\n for (const k of [...parts.keys()]) {\n if (skip?.has(k)) continue;\n if (!(k in next)) parts.delete(k);\n }\n // Add / replace, skipping partRef-managed paths.\n for (const [path, bytes] of Object.entries(next)) {\n if (skip?.has(path)) continue;\n const existing = parts.get(path);\n if (!existing || !bytesEqual(existing, bytes)) parts.set(path, bytes);\n }\n}\n\nfunction bytesEqual(a: Uint8Array, b: Uint8Array): boolean {\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) {\n if (a[i] !== b[i]) return false;\n }\n return true;\n}\n\n// === partRefs (Phase 3.2+) ===\n\n/**\n * Atomically write `partRefs` entries to a Y.Doc's `partRefs` Y.Map.\n * Used by the Editor / HeadlessSobree when a `BlobStore` is configured —\n * the editor hashes new bytes, uploads via the store, then calls this\n * to publish the hash so other peers can fetch it.\n *\n * Wraps in `Y.Doc.transact` with the supplied origin so subscribers\n * (UndoManager, observers) see one batch.\n */\nexport function applyPartRefsToYDoc(\n ydoc: Y.Doc,\n partRefs: Record<string, string>,\n origin: unknown = \"applyPartRefs\",\n): void {\n const target = ydoc.getMap<string>(Y_PARTREFS_KEY);\n ydoc.transact(() => {\n for (const [path, hash] of Object.entries(partRefs)) {\n if (target.get(path) !== hash) target.set(path, hash);\n }\n }, origin);\n}\n\n/**\n * Remove `partRefs` entries from a Y.Doc. Used when an embedder\n * prunes unreferenced parts.\n */\nexport function removePartRefsFromYDoc(\n ydoc: Y.Doc,\n paths: readonly string[],\n origin: unknown = \"removePartRefs\",\n): void {\n if (paths.length === 0) return;\n const target = ydoc.getMap<string>(Y_PARTREFS_KEY);\n ydoc.transact(() => {\n for (const path of paths) target.delete(path);\n }, origin);\n}\n","/**\n * API-level types for talking to the Editor.\n *\n * These describe HOW you address content (positions, ranges, selections)\n * and what guarantees you get back (block versions, edit results). They\n * are JSON-clean so the same shapes work for in-process callers, the\n * forthcoming WebSocket / MCP adapter, and tests.\n *\n * Kept separate from `src/doc/types.ts` (the AST itself) because these\n * are runtime concerns — none of them are persisted to .docx.\n *\n * Design note on why there's no polymorphic `Position` type:\n * - For block-scoped operations (insert / replace / delete) the input\n * is a `BlockRef` paired with a method name that spells out the\n * intent (`insertBlockBefore`, `insertBlockAfter`).\n * - For inline-scoped operations (insert a run, move caret, apply\n * attributes across a span) the input is an `InlinePosition` — a\n * block ref plus a character offset inside that block.\n * This keeps method signatures self-describing for LLM / MCP callers,\n * no discriminated unions to construct.\n */\n\n// === block identity & versioning ===\n\n/**\n * Stable handle to a block, valid for the lifetime of an Editor instance.\n * `id` is allocated by the Editor on first sight (sequential strings like\n * `\"b1\"`, `\"b2\"`); `version` bumps on every modification. Versions reset\n * to 0 when the document is reloaded (`setDocument`, `openDocx`).\n *\n * Pass this to mutating operations to opt in to optimistic locking. The\n * operation fails with `\"optimistic-lock\"` if the live version no longer\n * matches.\n */\nexport interface BlockRef {\n id: string;\n version: number;\n}\n\n/** Version map for blocks an operation should also lock-check. */\nexport type BlockExpectations = Record<string, number>;\n\n// === positions ===\n\n/**\n * Address a point inside a block's content.\n *\n * `offset = 0` is the start of the block's content; `offset = blockLength`\n * is the end. Non-text inlines (drawings, fields, hyperlinks) count as\n * 1 each. A caret always lives inside a block — \"before block N\" is\n * expressed as `{ block: blockN, offset: 0 }`; \"after block N\" is\n * `{ block: blockN, offset: blockLength }`. For inserting NEW blocks\n * adjacent to an existing one, use the `insertBlockBefore` /\n * `insertBlockAfter` methods directly — they take a `BlockRef`, not a\n * position.\n */\nexport interface InlinePosition {\n block: BlockRef;\n offset: number;\n}\n\n/**\n * Inclusive range from `from` to `to` — both inline positions. Single-\n * block ranges have `from.block.id === to.block.id`.\n *\n * For operations that span blocks, pass `opts.expect` with the middle\n * blocks' expected versions — the Range itself only pins the endpoints.\n */\nexport interface Range {\n from: InlinePosition;\n to: InlinePosition;\n}\n\n/**\n * The current cursor / selection state of the editor in model terms.\n * `null` when nothing is selected (e.g. focus is outside the editor).\n */\nexport type Selection =\n | null\n | { kind: \"caret\"; at: InlinePosition }\n | { kind: \"range\"; range: Range };\n\n// === edit results ===\n\n/** Result envelope returned by every mutating operation. */\nexport type EditResult<T = void> =\n | { ok: true; value: T; affected: BlockRef[] }\n | { ok: false; error: EditError };\n\n/** Why an edit didn't apply. Discriminated union — agents switch on `code`. */\nexport type EditError =\n | OptimisticLockError\n | { code: \"invalid-position\"; details: string }\n | { code: \"unknown-block\"; blockId: string }\n | { code: \"range-empty\"; details: string }\n | { code: \"range-out-of-order\"; details: string }\n | { code: \"invalid-state\"; details: string };\n\nexport interface OptimisticLockError {\n code: \"optimistic-lock\";\n /**\n * Per-block conflict info. `actual` is `null` when the block has been\n * deleted between read and write — distinguishable from a stale\n * version on the same block.\n */\n conflicts: Array<{\n blockId: string;\n expected: number;\n actual: number | null;\n }>;\n}\n\n// === helpers (pure data — usable from anywhere) ===\n\n/** Build an inline position inside `block` at the given character offset. */\nexport function inlineAt(block: BlockRef, offset: number): InlinePosition {\n return { block, offset };\n}\n\n/** Construct a range from two positions. */\nexport function makeRange(from: InlinePosition, to: InlinePosition): Range {\n return { from, to };\n}\n\n/** Caret at a single position. */\nexport function caretAt(at: InlinePosition): Selection {\n return { kind: \"caret\", at };\n}\n\n/** True if two inline positions are inside the same block (by id). */\nexport function sameBlock(a: InlinePosition, b: InlinePosition): boolean {\n return a.block.id === b.block.id;\n}\n\n/** Zero-width range — `from` and `to` collapsed onto the same offset. */\nexport function isCollapsedRange(range: Range): boolean {\n return sameBlock(range.from, range.to) && range.from.offset === range.to.offset;\n}\n\n/** Caret selection OR collapsed range. */\nexport function isCaret(sel: Selection): boolean {\n if (!sel) return false;\n if (sel.kind === \"caret\") return true;\n return isCollapsedRange(sel.range);\n}\n\n/** Build an `EditResult` for a successful operation. */\nexport function ok<T>(value: T, affected: BlockRef[] = []): EditResult<T> {\n return { ok: true, value, affected };\n}\n\n/** Build an `EditResult` for a failed operation. */\nexport function fail(error: EditError): EditResult<never> {\n return { ok: false, error };\n}\n\n/** Specialised builder for the common optimistic-lock failure case. */\nexport function lockConflict(\n conflicts: OptimisticLockError[\"conflicts\"],\n): EditResult<never> {\n return fail({ code: \"optimistic-lock\", conflicts });\n}\n","import type { InlineRun, RunProperties } from \"./types\";\n\n/**\n * Character-length of a single InlineRun, matching the counting rules\n * used by the DOM ↔ position map:\n * - TextRun → `text.length`.\n * - BreakRun / TabRun / DrawingRun → 1 each.\n * - FieldRun → length of its cached value (empty = 0).\n * - HyperlinkRun → sum of its children's lengths.\n */\nexport function runLength(run: InlineRun): number {\n switch (run.kind) {\n case \"text\":\n return run.text.length;\n case \"break\":\n case \"tab\":\n case \"drawing\":\n return 1;\n case \"field\":\n return (run.cached ?? \"\").length;\n case \"hyperlink\":\n return runsLength(run.children);\n default:\n return 0;\n }\n}\n\nexport function runsLength(runs: readonly InlineRun[]): number {\n let n = 0;\n for (const r of runs) n += runLength(r);\n return n;\n}\n\n/**\n * Split a runs array at `offset` characters from the start. Returns two\n * arrays whose concatenated content is equivalent to the input. Splitting\n * *through* a TextRun produces two TextRuns with the same properties;\n * splitting through an atom puts the whole atom on one side (ties go to\n * \"before\" at the atom's end boundary and \"after\" at the start boundary).\n */\nexport function splitRunsAt(\n runs: readonly InlineRun[],\n offset: number,\n): { before: InlineRun[]; after: InlineRun[] } {\n const before: InlineRun[] = [];\n const after: InlineRun[] = [];\n let pos = 0;\n\n for (const run of runs) {\n const len = runLength(run);\n if (pos >= offset) {\n after.push(run);\n pos += len;\n continue;\n }\n if (pos + len <= offset) {\n before.push(run);\n pos += len;\n continue;\n }\n const splitAt = offset - pos;\n if (run.kind === \"text\") {\n before.push({ ...run, text: run.text.slice(0, splitAt) });\n after.push({ ...run, text: run.text.slice(splitAt) });\n } else if (run.kind === \"hyperlink\") {\n // Recurse into the hyperlink, preserving the href on both halves.\n const inner = splitRunsAt(run.children, splitAt);\n if (inner.before.length > 0) before.push({ ...run, children: inner.before });\n if (inner.after.length > 0) after.push({ ...run, children: inner.after });\n } else {\n // Atomic run — can't subdivide. Drop on the \"after\" side so the\n // \"before\" array never contains partial atoms.\n after.push(run);\n }\n pos += len;\n }\n return { before, after };\n}\n\n/**\n * Slice a runs array between two character offsets (inclusive `from`,\n * exclusive `to`). Used by `applyRunProperties` / `wrapRange` to isolate\n * the affected middle section.\n */\nexport function sliceRuns(\n runs: readonly InlineRun[],\n from: number,\n to: number,\n): InlineRun[] {\n if (to <= from) return [];\n const head = splitRunsAt(runs, from);\n const tail = splitRunsAt(head.after, to - from);\n return tail.before;\n}\n\n/**\n * Patch shape accepted by `applyRunPropertiesToRuns`. Keys set to\n * `undefined` remove the corresponding property from affected runs;\n * keys set to a value override.\n */\nexport type RunPropertiesPatch = {\n [K in keyof RunProperties]?: RunProperties[K] | undefined;\n};\n\n/**\n * Return a new runs array with `patch` merged into every TextRun's\n * properties (and recursively into HyperlinkRun children). Atoms\n * without `properties` pass through unchanged.\n */\nexport function applyRunPropertiesToRuns(\n runs: readonly InlineRun[],\n patch: RunPropertiesPatch,\n): InlineRun[] {\n return runs.map((r) => applyRunPropertiesToRun(r, patch));\n}\n\nfunction applyRunPropertiesToRun(run: InlineRun, patch: RunPropertiesPatch): InlineRun {\n if (run.kind === \"text\") {\n return { ...run, properties: mergeRunProps(run.properties, patch) };\n }\n if (run.kind === \"hyperlink\") {\n return { ...run, children: applyRunPropertiesToRuns(run.children, patch) };\n }\n return run;\n}\n\nfunction mergeRunProps(prev: RunProperties, patch: RunPropertiesPatch): RunProperties {\n const out: RunProperties = { ...prev };\n for (const [k, v] of Object.entries(patch)) {\n if (v === undefined) delete (out as Record<string, unknown>)[k];\n else (out as Record<string, unknown>)[k] = v;\n }\n return out;\n}\n\n/**\n * Concatenate runs and merge adjacent TextRuns with identical property\n * shapes. Minor cleanup for operations that leave fragmentation behind.\n */\nexport function mergeAdjacentTextRuns(runs: readonly InlineRun[]): InlineRun[] {\n const out: InlineRun[] = [];\n for (const run of runs) {\n const last = out[out.length - 1];\n if (\n run.kind === \"text\" &&\n last &&\n last.kind === \"text\" &&\n JSON.stringify(last.properties) === JSON.stringify(run.properties)\n ) {\n out[out.length - 1] = { ...last, text: last.text + run.text };\n } else {\n out.push(run);\n }\n }\n return out;\n}\n","import type { BlockRef } from \"../../doc/api\";\n\n/**\n * Per-block identity + version tracking.\n *\n * The Editor owns one BlockRegistry for its lifetime. Every block in the\n * current body has a stable `id` (sequential strings like `\"b1\"`, `\"b2\"`)\n * and a `version` that bumps on every modification. Indices shift as\n * blocks are inserted / removed, but `id` doesn't.\n *\n * The whole registry is reset on `setDocument` / `openDocx`. It is NOT\n * persisted — versions are a runtime-only contract.\n *\n * Pure data + arithmetic; no DOM access.\n */\nexport interface BlockRegistryOptions {\n /**\n * Prefix for newly-allocated ids. Default `\"b\"` produces `b1`, `b2`,\n * … — fine for single-peer use. Phase 1b+ embedders pass a\n * peer-unique prefix (e.g. `${ydoc.clientID.toString(36)}_`) so two\n * peers don't both mint `b5` for different blocks.\n */\n idPrefix?: string;\n}\n\nexport class BlockRegistry {\n /** Per-block state, parallel to the body array. */\n private entries: Entry[] = [];\n /** Fast lookup by id. Points at the same objects as `entries`. */\n private byId: Map<string, Entry> = new Map();\n /** Next numeric suffix to allocate. Monotonic across `reset()` calls\n * so an id minted before a reset never collides with one minted\n * after. */\n private nextNum = 1;\n /** String prepended to every newly-minted id. Constant for the\n * lifetime of the registry. */\n private readonly idPrefix: string;\n /** Document-wide monotonic counter — bumps on any modification. */\n private docVersion = 0;\n\n constructor(opts: BlockRegistryOptions = {}) {\n this.idPrefix = opts.idPrefix ?? \"b\";\n }\n\n /** Replace the registry for a fresh document of `blockCount` blocks. */\n reset(blockCount: number): void {\n this.entries = [];\n this.byId.clear();\n for (let i = 0; i < blockCount; i++) this.allocEntry();\n this.docVersion = 0;\n }\n\n /**\n * Replace the registry from an explicit id list — used when adopting\n * an existing Y.Doc's body (Phase 1b: peer joining an active room).\n * The supplied ids are kept verbatim; future inserts continue to use\n * the registry's own `idPrefix` so peer-minted ids stay distinct from\n * adopted-foreign ids.\n *\n * `nextNum` is advanced past any local-prefix collisions found in\n * the adopted set, so a future local insert can't shadow an adopted\n * id.\n */\n adoptIds(ids: readonly string[]): void {\n this.entries = [];\n this.byId.clear();\n for (const id of ids) {\n const e: Entry = { id, version: 0 };\n this.entries.push(e);\n this.byId.set(id, e);\n // If this id matches our local prefix, advance nextNum so we\n // don't re-mint it.\n if (id.startsWith(this.idPrefix)) {\n const suffix = id.slice(this.idPrefix.length);\n const n = Number.parseInt(suffix, 10);\n if (Number.isFinite(n) && n >= this.nextNum) this.nextNum = n + 1;\n }\n }\n this.docVersion = 0;\n }\n\n /** Number of blocks currently tracked. */\n length(): number {\n return this.entries.length;\n }\n\n /** Current document counter (monotonic across all edits). */\n documentVersion(): number {\n return this.docVersion;\n }\n\n /** Ref for the block at `index`. Throws on out-of-range. */\n refAt(index: number): BlockRef {\n const e = this.entries[index];\n if (!e) throw new Error(`BlockRegistry: index ${index} out of range`);\n return { id: e.id, version: e.version };\n }\n\n /** Ref by id, or `null` if the id isn't live. */\n refById(id: string): BlockRef | null {\n const e = this.byId.get(id);\n return e ? { id: e.id, version: e.version } : null;\n }\n\n /** Current body-index of the given id, or `-1` if not found. */\n indexOf(id: string): number {\n const e = this.byId.get(id);\n if (!e) return -1;\n return this.entries.indexOf(e);\n }\n\n /** Whether `id` is currently live (not deleted). */\n has(id: string): boolean {\n return this.byId.has(id);\n }\n\n /**\n * Bump the version of the block at `index`. Bumps doc version too.\n * Returns the new ref for the caller to pass back in results.\n */\n bump(index: number): BlockRef {\n const e = this.entries[index];\n if (!e) throw new Error(`BlockRegistry: bump out of range (${index})`);\n e.version += 1;\n this.docVersion += 1;\n return { id: e.id, version: e.version };\n }\n\n /**\n * Insert a fresh entry at `index`, shifting subsequent entries right.\n * The new entry starts at version 0. Bumps doc version.\n */\n insert(index: number): BlockRef {\n const entry = this.newEntry();\n const clamped = Math.max(0, Math.min(index, this.entries.length));\n this.entries.splice(clamped, 0, entry);\n this.byId.set(entry.id, entry);\n this.docVersion += 1;\n return { id: entry.id, version: entry.version };\n }\n\n /**\n * Remove the entry at `index`, shifting subsequent entries left.\n * Bumps doc version.\n */\n remove(index: number): void {\n const e = this.entries[index];\n if (!e) return;\n this.entries.splice(index, 1);\n this.byId.delete(e.id);\n this.docVersion += 1;\n }\n\n /**\n * Replace everything at once — used by `syncFromDom` when the rebuild\n * preserves identity but content may have changed per-block. Pass a\n * parallel array of \"did this block change\" booleans; true entries\n * bump; the registry itself doesn't know what changed.\n *\n * Callers that know nothing about diffs should call `.reset(n)` instead.\n */\n bumpChanged(changed: readonly boolean[]): void {\n let anyBumped = false;\n for (let i = 0; i < changed.length && i < this.entries.length; i++) {\n if (changed[i]) {\n const e = this.entries[i];\n if (!e) continue;\n e.version += 1;\n anyBumped = true;\n }\n }\n if (anyBumped) this.docVersion += 1;\n }\n\n // === internals ===\n\n private allocEntry(): void {\n const e = this.newEntry();\n this.entries.push(e);\n this.byId.set(e.id, e);\n }\n\n private newEntry(): Entry {\n return { id: `${this.idPrefix}${this.nextNum++}`, version: 0 };\n }\n}\n\ninterface Entry {\n id: string;\n version: number;\n}\n","/**\n * Pure helpers shared between `Editor` (DOM-backed) and `HeadlessSobree`\n * (no-DOM peer for LLMs / agents / automation).\n *\n * These functions don't touch DOM, don't carry state, and don't depend\n * on Y.js — they operate on `SobreeDocument` shapes. Both editors share\n * the same logic for block-level mutations so a headless peer's edits\n * land in the Y.Doc with identical semantics to a browser peer's.\n */\n\nimport type {\n Block,\n ParagraphProperties,\n RunProperties,\n SectionProperties,\n SobreeDocument,\n} from \"../../doc/types\";\nimport type { ParagraphPropertiesPatch, WrapTag } from \"../index\";\nimport type { RunPropertiesPatch } from \"../../doc/runs\";\n\n/**\n * One registry-level operation produced by a mutation. The caller\n * applies these to the BlockRegistry after committing the new doc:\n * `insert` adds an id, `remove` drops one, `bump` keeps the same id\n * but increments its version.\n */\nexport type Mutation =\n | { type: \"bump\"; index: number }\n | { type: \"insert\"; index: number }\n | { type: \"remove\"; index: number };\n\n/**\n * Index in `sections` of the section that ENDS at the section_break at\n * `breakIndex`. Sections are 1:1 with section_breaks; the first\n * section ends at the first break (or at the end of `body` if there's\n * no break).\n *\n * body = [p, p, break, p, break, p]\n * sections = [s0, s1, s2]\n *\n * breakIndex = 2 → 0 (the first break ends section 0)\n * breakIndex = 4 → 1 (the second break ends section 1)\n */\nexport function removedSectionIndex(\n body: readonly Block[],\n breakIndex: number,\n): number {\n let count = 0;\n for (let i = 0; i < breakIndex; i++) {\n if (body[i]?.kind === \"section_break\") count++;\n }\n return count;\n}\n\n/**\n * Drop the section at `endingIndex + 1` from `sections` — that's the\n * section the now-removed break STARTED. The section ENDED by the\n * removed break (at `endingIndex`) absorbs whatever content used to\n * belong to its successor. Properties of the surviving section are\n * preserved verbatim; nothing about the removed section's settings is\n * carried over.\n *\n * If `sections` doesn't have a successor (the removed break was the\n * last one and there's only one section), the array is returned\n * unchanged.\n */\nexport function mergeSectionsAcross(\n sections: readonly SectionProperties[],\n endingIndex: number,\n): SectionProperties[] {\n const next = sections.slice();\n if (endingIndex + 1 >= next.length) return next;\n next.splice(endingIndex + 1, 1);\n return next;\n}\n\n/**\n * Merge a `ParagraphPropertiesPatch` into existing properties.\n * `undefined` in the patch removes a field; everything else\n * overwrites.\n */\nexport function mergeParagraphProps(\n prev: ParagraphProperties,\n patch: ParagraphPropertiesPatch,\n): ParagraphProperties {\n const out: ParagraphProperties = { ...prev };\n for (const [k, v] of Object.entries(patch)) {\n if (v === undefined) delete (out as Record<string, unknown>)[k];\n else (out as Record<string, unknown>)[k] = v;\n }\n return out;\n}\n\n/**\n * Map a semantic \"wrap\" tag to the run-property patch that achieves it.\n * Same mapping the browser editor uses for toolbar buttons.\n */\nexport function wrapTagToPatch(tag: WrapTag): RunPropertiesPatch {\n switch (tag) {\n case \"strong\":\n return { bold: true };\n case \"em\":\n return { italic: true };\n case \"u\":\n return { underline: \"single\" };\n case \"s\":\n return { strike: true };\n case \"sup\":\n return { verticalAlign: \"superscript\" };\n case \"sub\":\n return { verticalAlign: \"subscript\" };\n case \"mark\":\n return { highlight: \"yellow\" };\n }\n}\n\n/** Map an image MIME type to a `.docx` part filename extension. */\nexport function mimeToExtension(mime: string): string {\n const m = mime.toLowerCase();\n if (m === \"image/png\") return \"png\";\n if (m === \"image/jpeg\" || m === \"image/jpg\") return \"jpg\";\n if (m === \"image/gif\") return \"gif\";\n if (m === \"image/webp\") return \"webp\";\n if (m === \"image/svg+xml\") return \"svg\";\n if (m === \"image/bmp\") return \"bmp\";\n return \"bin\";\n}\n\n/** Find the next free `word/media/imageN.<ext>` slot in `rawParts`. */\nexport function allocateMediaPath(doc: SobreeDocument, ext: string): string {\n let n = 1;\n while (doc.rawParts[`word/media/image${n}.${ext}`]) n += 1;\n return `word/media/image${n}.${ext}`;\n}\n\n/** Convert pixels (CSS @ 96 dpi) to OOXML's EMU (914400 per inch). */\nexport function pxToEmu(px: number): number {\n return Math.round((px / 96) * 914400);\n}\n\n// Suppress unused warnings — these types are referenced for JSDoc / future use.\nexport type { RunProperties };\n","import type { InlinePosition, Range as ApiRange, Selection } from \"../../doc/api\";\nimport type { BlockRegistry } from \"./blockRegistry\";\n\n/**\n * Bidirectional mapping between DOM points and the model's\n * `InlinePosition` / `Range` / `Selection`.\n *\n * The Editor owns one of these (implicitly, via the helper functions\n * here) and calls into it whenever the UI side of selection has to talk\n * to the data side, or vice versa. It's the ONLY place that knows how\n * to convert DOM coordinates into character offsets and back.\n *\n * Character counting rules (must match the DOM serializer):\n * - Text node → its `.length` characters.\n * - `<br>`, `<img>` → 1 character each.\n * - Wrapper elements (`<span>`, `<strong>`, `<em>`, `<b>`, `<i>`,\n * `<u>`, `<ins>`, `<s>`, `<del>`, `<strike>`, `<sub>`, `<sup>`,\n * `<mark>`, `<code>`, `<a>`) → transparent; recurse into children.\n *\n * Scope for this module:\n * - Paragraph / heading blocks: full support.\n * - List items: each `<li>` is its own block (matching how the DOM\n * serializer splits `<ul>`/`<ol>` into per-item paragraphs).\n * - Tables: a point inside a table resolves to the enclosing table\n * block with `offset = 0`. Cell-internal positioning comes later.\n */\n\nconst WRAPPER_TAGS = new Set([\n \"span\",\n \"strong\",\n \"b\",\n \"em\",\n \"i\",\n \"u\",\n \"ins\",\n \"s\",\n \"del\",\n \"strike\",\n \"sub\",\n \"sup\",\n \"mark\",\n \"code\",\n \"a\",\n]);\n\nconst ATOM_TAGS = new Set([\"br\", \"img\", \"hr\"]);\n\nconst BLOCK_TAGS = new Set([\n \"p\",\n \"h1\",\n \"h2\",\n \"h3\",\n \"h4\",\n \"h5\",\n \"h6\",\n \"blockquote\",\n \"pre\",\n \"dl\",\n]);\n\nconst LIST_TAGS = new Set([\"ul\", \"ol\"]);\n\n// === reading: DOM → model ===\n\n/**\n * Resolve a DOM point `(node, offset)` to an `InlinePosition`, or null\n * if the point is outside the editor's tracked blocks.\n *\n * For table cells, returns an InlinePosition at the table block with\n * `offset = 0` — block-internal table addressing is a future extension.\n */\nexport function positionFromDomPoint(\n hosts: readonly HTMLElement[],\n registry: BlockRegistry,\n node: Node,\n domOffset: number,\n): InlinePosition | null {\n if (!hosts.some((h) => h.contains(node) || h === node)) return null;\n\n const { blockEl, blockIndex } = findBlockElement(node, hosts);\n if (!blockEl || blockIndex < 0) return null;\n\n let offset: number;\n if (isInsideTable(blockEl)) {\n offset = 0;\n } else {\n offset = charOffsetToPoint(blockEl, node, domOffset);\n }\n\n return { block: registry.refAt(blockIndex), offset };\n}\n\n/** Build an API `Range` from a live DOM `Range`. */\nexport function rangeFromDomRange(\n hosts: readonly HTMLElement[],\n registry: BlockRegistry,\n range: Range,\n): ApiRange | null {\n const from = positionFromDomPoint(hosts, registry, range.startContainer, range.startOffset);\n const to = positionFromDomPoint(hosts, registry, range.endContainer, range.endOffset);\n if (!from || !to) return null;\n return { from, to };\n}\n\n/** Read `window.getSelection()` as a model `Selection`. */\nexport function selectionFromDom(\n hosts: readonly HTMLElement[],\n registry: BlockRegistry,\n): Selection {\n const sel = window.getSelection();\n if (!sel || sel.rangeCount === 0) return null;\n const range = sel.getRangeAt(0);\n const api = rangeFromDomRange(hosts, registry, range);\n if (!api) return null;\n if (sel.isCollapsed) return { kind: \"caret\", at: api.from };\n return { kind: \"range\", range: api };\n}\n\n// === writing: model → DOM ===\n\n/** Resolve an `InlinePosition` to a DOM `{ node, offset }` point. */\nexport function domPointFromPosition(\n hosts: readonly HTMLElement[],\n registry: BlockRegistry,\n pos: InlinePosition,\n): { node: Node; offset: number } | null {\n const index = registry.indexOf(pos.block.id);\n if (index < 0) return null;\n const blockEl = blockElementAtIndex(hosts, index);\n if (!blockEl) return null;\n return findPointAtOffset(blockEl, pos.offset);\n}\n\n/** Apply a model `Selection` to `window.getSelection()`. */\nexport function applySelectionToDom(\n hosts: readonly HTMLElement[],\n registry: BlockRegistry,\n selection: Selection,\n): boolean {\n const sel = window.getSelection();\n if (!sel) return false;\n if (!selection) {\n sel.removeAllRanges();\n return true;\n }\n if (selection.kind === \"caret\") {\n const pt = domPointFromPosition(hosts, registry, selection.at);\n if (!pt) return false;\n const range = document.createRange();\n range.setStart(pt.node, pt.offset);\n range.collapse(true);\n sel.removeAllRanges();\n sel.addRange(range);\n return true;\n }\n const from = domPointFromPosition(hosts, registry, selection.range.from);\n const to = domPointFromPosition(hosts, registry, selection.range.to);\n if (!from || !to) return false;\n const range = document.createRange();\n range.setStart(from.node, from.offset);\n range.setEnd(to.node, to.offset);\n sel.removeAllRanges();\n sel.addRange(range);\n return true;\n}\n\n// === block length (the key utility used by the rest of the API) ===\n\n/** Total character-count length of a block, per the counting rules above. */\nexport function blockLength(blockEl: Element): number {\n return charOffsetToPoint(blockEl, blockEl, blockEl.childNodes.length);\n}\n\n// === block index helpers ===\n\n/**\n * Enumerate blocks across all content hosts in document order,\n * expanding `<ul>`/`<ol>` children as one block each. Returns the\n * total block count.\n */\nexport function countBlocks(hosts: readonly HTMLElement[]): number {\n let n = 0;\n for (const host of hosts) {\n for (const child of Array.from(host.children)) n += blocksInTopChild(child);\n }\n return n;\n}\n\n/**\n * Return the DOM element that hosts a given block index. For list-item\n * blocks this is the `<li>`; for everything else it's the direct host\n * child.\n */\nexport function blockElementAtIndex(\n hosts: readonly HTMLElement[],\n index: number,\n): HTMLElement | null {\n let remaining = index;\n for (const host of hosts) {\n for (const child of Array.from(host.children)) {\n const span = blocksInTopChild(child);\n if (remaining < span) {\n if (LIST_TAGS.has(child.tagName.toLowerCase())) {\n const items = Array.from(child.children).filter(\n (c): c is HTMLElement => c instanceof HTMLElement && c.tagName.toLowerCase() === \"li\",\n );\n return items[remaining] ?? null;\n }\n return child instanceof HTMLElement ? child : null;\n }\n remaining -= span;\n }\n }\n return null;\n}\n\nfunction blocksInTopChild(el: Element): number {\n const tag = el.tagName.toLowerCase();\n if (LIST_TAGS.has(tag)) {\n return Array.from(el.children).filter(\n (c) => c.tagName.toLowerCase() === \"li\",\n ).length;\n }\n return 1;\n}\n\nfunction findBlockElement(\n node: Node,\n hosts: readonly HTMLElement[],\n): { blockEl: HTMLElement | null; blockIndex: number } {\n // Walk up from node. Stop at either:\n // - an element whose parent is a content host → top-level block\n // - an <li> whose grand-parent is a host → list-item block\n let cur: Node | null = node;\n while (cur) {\n if (cur instanceof HTMLElement) {\n const tag = cur.tagName.toLowerCase();\n const parent = cur.parentElement;\n if (parent && hosts.includes(parent) && (BLOCK_TAGS.has(tag) || LIST_TAGS.has(tag) || tag === \"table\" || tag === \"div\")) {\n // Top-level (non-list) block.\n if (LIST_TAGS.has(tag)) {\n // Caret landed on the `<ul>` itself somehow — degenerate case.\n // Resolve to the first list-item index.\n return { blockEl: cur.children[0] instanceof HTMLElement ? cur.children[0] : null, blockIndex: indexOfElement(cur, hosts) };\n }\n return { blockEl: cur, blockIndex: indexOfElement(cur, hosts) };\n }\n if (tag === \"li\" && parent && parent.parentElement && hosts.includes(parent.parentElement)) {\n return { blockEl: cur, blockIndex: indexOfElement(cur, hosts) };\n }\n }\n cur = cur.parentNode;\n }\n return { blockEl: null, blockIndex: -1 };\n}\n\nfunction indexOfElement(target: Element, hosts: readonly HTMLElement[]): number {\n let index = 0;\n for (const host of hosts) {\n for (const child of Array.from(host.children)) {\n const tag = child.tagName.toLowerCase();\n if (LIST_TAGS.has(tag)) {\n const items = Array.from(child.children).filter(\n (c): c is HTMLElement => c instanceof HTMLElement && c.tagName.toLowerCase() === \"li\",\n );\n if (items.includes(target as HTMLElement)) return index + items.indexOf(target as HTMLElement);\n index += items.length;\n continue;\n }\n if (child === target) return index;\n index += 1;\n }\n }\n return -1;\n}\n\nfunction isInsideTable(blockEl: Element): boolean {\n return blockEl.tagName.toLowerCase() === \"table\";\n}\n\n// === atom counting ===\n\nfunction isAtomElement(el: Element): boolean {\n return ATOM_TAGS.has(el.tagName.toLowerCase());\n}\n\nfunction isWrapperElement(el: Element): boolean {\n return WRAPPER_TAGS.has(el.tagName.toLowerCase());\n}\n\n/**\n * Walk `blockEl`'s subtree in document order until `(targetNode,\n * targetOffset)` is reached; return the number of atoms seen so far.\n */\nfunction charOffsetToPoint(\n blockEl: Element,\n targetNode: Node,\n targetOffset: number,\n): number {\n let count = 0;\n let found = false;\n\n const visit = (node: Node): void => {\n if (found) return;\n if (node === targetNode) {\n if (node.nodeType === Node.TEXT_NODE) {\n const text = node as Text;\n count += Math.min(Math.max(0, targetOffset), text.length);\n } else if (node instanceof Element) {\n const children = Array.from(node.childNodes);\n const clamped = Math.max(0, Math.min(targetOffset, children.length));\n for (let i = 0; i < clamped; i++) {\n const c = children[i];\n if (c) visit(c);\n if (found) return;\n }\n }\n found = true;\n return;\n }\n if (node.nodeType === Node.TEXT_NODE) {\n count += (node as Text).length;\n return;\n }\n if (node instanceof Element) {\n if (isAtomElement(node)) {\n count += 1;\n return;\n }\n // Either a wrapper or an otherwise-transparent element (e.g. a\n // stray `<div>` from paste). In either case, recurse.\n if (isWrapperElement(node) || node.tagName.toLowerCase() === \"div\") {\n for (const c of Array.from(node.childNodes)) {\n visit(c);\n if (found) return;\n }\n } else {\n // Unknown element — recurse and hope for the best.\n for (const c of Array.from(node.childNodes)) {\n visit(c);\n if (found) return;\n }\n }\n }\n };\n\n // Kick off the walk from blockEl's children (we don't count blockEl itself).\n if (blockEl === targetNode) {\n // Caret directly at the block level — same handling as the element\n // branch inside visit().\n const children = Array.from(blockEl.childNodes);\n const clamped = Math.max(0, Math.min(targetOffset, children.length));\n for (let i = 0; i < clamped; i++) {\n const c = children[i];\n if (c) visit(c);\n }\n return count;\n }\n for (const c of Array.from(blockEl.childNodes)) {\n visit(c);\n if (found) return count;\n }\n return count;\n}\n\n/**\n * Reverse of `charOffsetToPoint`: find a DOM `(node, offset)` tuple\n * that corresponds to `targetOffset` characters into `blockEl`.\n */\nfunction findPointAtOffset(\n blockEl: Element,\n targetOffset: number,\n): { node: Node; offset: number } {\n let count = 0;\n let result: { node: Node; offset: number } | null = null;\n\n const visit = (node: Node): void => {\n if (result) return;\n\n if (node.nodeType === Node.TEXT_NODE) {\n const text = node as Text;\n if (targetOffset <= count + text.length) {\n result = { node, offset: Math.max(0, targetOffset - count) };\n return;\n }\n count += text.length;\n return;\n }\n\n if (node instanceof Element) {\n if (isAtomElement(node)) {\n // The caret sits at the edge of this atom.\n const parent = node.parentNode;\n if (!parent) return;\n const idx = Array.from(parent.childNodes).indexOf(node);\n if (targetOffset === count) {\n result = { node: parent, offset: idx };\n return;\n }\n if (targetOffset === count + 1) {\n result = { node: parent, offset: idx + 1 };\n return;\n }\n count += 1;\n return;\n }\n for (const c of Array.from(node.childNodes)) {\n visit(c);\n if (result) return;\n }\n }\n };\n\n for (const c of Array.from(blockEl.childNodes)) {\n visit(c);\n if (result) return result;\n }\n\n // Past the end: place caret at the block's tail.\n return { node: blockEl, offset: blockEl.childNodes.length };\n}\n","import type { BlockRef, EditResult } from \"../doc/api\";\nimport { fail } from \"../doc/api\";\nimport type { SobreeDocument } from \"../doc/types\";\nimport type {\n Block,\n ParagraphAlignment,\n Shading,\n Table,\n TableCell,\n TableCellBorders,\n TableProperties,\n TableRow,\n} from \"../doc/types\";\n\n/**\n * Minimal slice of `Editor` that `EditorTable` actually needs. Defining\n * it here (rather than `import type { Editor } from \"./\"`) keeps this\n * module a leaf in the editor/* import graph — no cycle with index.ts.\n */\nexport interface EditorTableHost {\n getDocument(): SobreeDocument;\n getBlockById(id: string): { kind: string; index: number } | null;\n replaceBlock(target: BlockRef, block: Block): EditResult<BlockRef>;\n}\n\n/**\n * Pointer to one cell inside a table. `row`/`col` are **visual** indices\n * (after expanding `gridSpan`) — the grid the user actually sees. Not a\n * stable handle: indices shift as rows/columns are added or removed.\n */\nexport interface CellRef {\n table: BlockRef;\n row: number;\n col: number;\n}\n\n/** Where an insertion should land, relative to an anchor row/column. */\nexport type InsertAt = \"start\" | \"end\" | \"before\" | \"after\";\n\nexport interface InsertRowOpts {\n at: InsertAt;\n /** Required for `\"before\"` / `\"after\"`. Visual row index of the anchor. */\n index?: number;\n /** Custom cells. Defaults to empty paragraphs, one per grid column. */\n cells?: TableCell[];\n}\n\nexport interface InsertColumnOpts {\n at: InsertAt;\n /** Required for `\"before\"` / `\"after\"`. Visual column index of the anchor. */\n index?: number;\n /** Width of the new column in twips. Default: average of existing columns, or 2400. */\n widthTwips?: number;\n /**\n * When the target column falls inside an existing `gridSpan` cell, the\n * default is to **extend** the span (the existing merge grows). Pass\n * `split: true` to split the merge and insert a fresh cell instead.\n */\n split?: boolean;\n}\n\nexport interface MergeCellsOpts {\n /** Visual coordinates of the merge region's top-left corner. */\n row: number;\n col: number;\n /** Number of visual rows the merge should span. Defaults to 1. */\n rowSpan?: number;\n /** Number of visual columns the merge should span. Defaults to 1. */\n colSpan?: number;\n}\n\nconst DEFAULT_COLUMN_WIDTH_TWIPS = 2400;\n\n/**\n * Ergonomic table mutation surface. Lives on `editor.table`.\n *\n * Every method does the same three steps under the hood:\n * 1. Resolve the target table by `BlockRef` (inherits optimistic-lock\n * checking from `editor.replaceBlock`).\n * 2. Clone and mutate the table immutably.\n * 3. Delegate to `editor.replaceBlock(ref, nextTable)`.\n *\n * No new plumbing; lock semantics, affected-block tracking, and event\n * emission come from the underlying core.\n */\nexport class EditorTable {\n constructor(private readonly editor: EditorTableHost) {}\n\n // === row operations ===\n\n insertRow(ref: BlockRef, opts: InsertRowOpts): EditResult<BlockRef> {\n const table = this.getTable(ref);\n if (!table) return fail({ code: \"invalid-state\", details: \"target is not a table\" });\n\n const colCount = columnCount(table);\n const cells = opts.cells ?? defaultRowCells(colCount);\n if (cells.length > colCount) {\n return fail({\n code: \"invalid-state\",\n details: `supplied ${cells.length} cells but table has ${colCount} columns`,\n });\n }\n\n const insertAt = resolveRowInsertIndex(table, opts);\n if (insertAt === null) {\n return fail({ code: \"invalid-position\", details: \"row index out of range\" });\n }\n\n const newRow: TableRow = { cells: padCellsToColumns(cells, colCount) };\n const rows = table.rows.slice();\n rows.splice(insertAt, 0, newRow);\n\n // If a vertical merge spans across the insert point, the new row's\n // cells in those columns must be `vMerge: \"continue\"` — otherwise\n // the merge visually breaks.\n patchVMergeAcrossInsertedRow(rows, insertAt);\n\n return this.editor.replaceBlock(ref, { ...table, rows });\n }\n\n deleteRow(ref: BlockRef, index: number): EditResult<BlockRef> {\n const table = this.getTable(ref);\n if (!table) return fail({ code: \"invalid-state\", details: \"target is not a table\" });\n if (index < 0 || index >= table.rows.length) {\n return fail({ code: \"invalid-position\", details: `row ${index} out of range` });\n }\n\n const rows = table.rows.slice();\n const removed = rows[index];\n rows.splice(index, 1);\n\n if (removed) promoteVMergeContinuations(rows, index, removed);\n\n if (rows.length === 0) {\n // An empty table is invalid; keep at least one row.\n rows.push({ cells: defaultRowCells(columnCount(table)) });\n }\n return this.editor.replaceBlock(ref, { ...table, rows });\n }\n\n // === column operations ===\n\n insertColumn(ref: BlockRef, opts: InsertColumnOpts): EditResult<BlockRef> {\n const table = this.getTable(ref);\n if (!table) return fail({ code: \"invalid-state\", details: \"target is not a table\" });\n\n const colCount = columnCount(table);\n const at = resolveColumnInsertIndex(colCount, opts);\n if (at === null) {\n return fail({ code: \"invalid-position\", details: \"column index out of range\" });\n }\n\n const width = opts.widthTwips ?? averageColumnWidth(table) ?? DEFAULT_COLUMN_WIDTH_TWIPS;\n const grid = table.grid.slice();\n grid.splice(at, 0, width);\n\n const rows = table.rows.map((row) => insertColumnInRow(row, at, !!opts.split));\n return this.editor.replaceBlock(ref, { ...table, grid, rows });\n }\n\n deleteColumn(ref: BlockRef, index: number): EditResult<BlockRef> {\n const table = this.getTable(ref);\n if (!table) return fail({ code: \"invalid-state\", details: \"target is not a table\" });\n const colCount = columnCount(table);\n if (index < 0 || index >= colCount) {\n return fail({ code: \"invalid-position\", details: `column ${index} out of range` });\n }\n if (colCount === 1) {\n return fail({\n code: \"invalid-state\",\n details: \"cannot delete the only column; delete the table instead\",\n });\n }\n\n const grid = table.grid.slice();\n grid.splice(index, 1);\n const rows = table.rows.map((row) => deleteColumnFromRow(row, index));\n return this.editor.replaceBlock(ref, { ...table, grid, rows });\n }\n\n // === merge operations ===\n\n mergeCells(ref: BlockRef, opts: MergeCellsOpts): EditResult<BlockRef> {\n const table = this.getTable(ref);\n if (!table) return fail({ code: \"invalid-state\", details: \"target is not a table\" });\n\n const rowSpan = opts.rowSpan ?? 1;\n const colSpan = opts.colSpan ?? 1;\n if (rowSpan < 1 || colSpan < 1) {\n return fail({ code: \"invalid-state\", details: \"rowSpan/colSpan must be ≥ 1\" });\n }\n if (rowSpan === 1 && colSpan === 1) {\n return fail({ code: \"invalid-state\", details: \"merge target is a single cell\" });\n }\n\n const colCount = columnCount(table);\n if (\n opts.row < 0 ||\n opts.col < 0 ||\n opts.row + rowSpan > table.rows.length ||\n opts.col + colSpan > colCount\n ) {\n return fail({ code: \"invalid-position\", details: \"merge region out of range\" });\n }\n\n // Reject if any cell in the target region already participates in a\n // different merge. Users must unmerge first.\n for (let r = opts.row; r < opts.row + rowSpan; r++) {\n for (let c = opts.col; c < opts.col + colSpan; c++) {\n const hit = cellAtVisual(table, r, c);\n if (!hit) continue;\n const isTopLeft = r === opts.row && c === opts.col;\n const ownsOnlyThis =\n (hit.cell.gridSpan ?? 1) === 1 && !hit.cell.vMerge;\n if (!isTopLeft && !ownsOnlyThis) {\n return fail({\n code: \"invalid-state\",\n details: \"merge region overlaps an existing merge — unmerge it first\",\n });\n }\n }\n }\n\n // Build the merge by mutating each affected row.\n const rows = table.rows.map((row, r) => {\n if (r < opts.row || r >= opts.row + rowSpan) return row;\n return applyMergeToRow(row, r === opts.row, opts.col, colSpan, rowSpan);\n });\n return this.editor.replaceBlock(ref, { ...table, rows });\n }\n\n unmergeCell(cell: CellRef): EditResult<BlockRef> {\n const table = this.getTable(cell.table);\n if (!table) return fail({ code: \"invalid-state\", details: \"target is not a table\" });\n\n const hit = cellAtVisual(table, cell.row, cell.col);\n if (!hit) return fail({ code: \"invalid-position\", details: \"cell not found\" });\n\n const gridSpan = hit.cell.gridSpan ?? 1;\n const isVMergeRoot = hit.cell.vMerge === \"restart\";\n\n if (gridSpan === 1 && !isVMergeRoot) {\n return fail({ code: \"invalid-state\", details: \"cell is not merged\" });\n }\n\n // Undo horizontal: replace the one wide cell with N narrow cells.\n // Undo vertical: set the root's vMerge off; find the continuation\n // chain below and replace each with a fresh 1×1 empty cell covering\n // the same visual columns.\n const rows = table.rows.map((row, r) => {\n if (r < cell.row) return row;\n if (r === cell.row) return unmergeTopRow(row, cell.col, gridSpan);\n if (!isVMergeRoot) return row;\n // Below the root: check if this row has a continuation at our cols.\n return unmergeContinuationRow(row, cell.col, gridSpan);\n });\n\n return this.editor.replaceBlock(cell.table, { ...table, rows });\n }\n\n // === cell ops ===\n\n setCellContent(cell: CellRef, content: Block[]): EditResult<BlockRef> {\n return this.updateCell(cell, (c) => ({ ...c, content }));\n }\n\n setCellProperties(\n cell: CellRef,\n patch: Partial<Omit<TableCell, \"content\">>,\n ): EditResult<BlockRef> {\n return this.updateCell(cell, (c) => mergeCellProps(c, patch));\n }\n\n // === column / row / table ops ===\n\n setColumnWidth(ref: BlockRef, col: number, widthTwips: number): EditResult<BlockRef> {\n const table = this.getTable(ref);\n if (!table) return fail({ code: \"invalid-state\", details: \"target is not a table\" });\n if (col < 0 || col >= table.grid.length) {\n return fail({ code: \"invalid-position\", details: `column ${col} out of range` });\n }\n if (widthTwips <= 0) {\n return fail({ code: \"invalid-state\", details: \"widthTwips must be positive\" });\n }\n const grid = table.grid.slice();\n grid[col] = widthTwips;\n return this.editor.replaceBlock(ref, { ...table, grid });\n }\n\n toggleHeaderRow(ref: BlockRef, row: number): EditResult<BlockRef> {\n const table = this.getTable(ref);\n if (!table) return fail({ code: \"invalid-state\", details: \"target is not a table\" });\n if (row < 0 || row >= table.rows.length) {\n return fail({ code: \"invalid-position\", details: `row ${row} out of range` });\n }\n const rows = table.rows.map((r, i) => {\n if (i !== row) return r;\n return r.isHeader ? { ...r, isHeader: false } : { ...r, isHeader: true };\n });\n return this.editor.replaceBlock(ref, { ...table, rows });\n }\n\n setProperties(ref: BlockRef, patch: Partial<TableProperties>): EditResult<BlockRef> {\n const table = this.getTable(ref);\n if (!table) return fail({ code: \"invalid-state\", details: \"target is not a table\" });\n return this.editor.replaceBlock(ref, {\n ...table,\n properties: { ...table.properties, ...patch },\n });\n }\n\n // === internals ===\n\n private getTable(ref: BlockRef): Table | null {\n const doc = this.editor.getDocument();\n const info = this.editor.getBlockById(ref.id);\n if (!info || info.kind !== \"table\") return null;\n const block = doc.body[info.index];\n if (!block || block.kind !== \"table\") return null;\n return block;\n }\n\n private updateCell(\n cell: CellRef,\n transform: (c: TableCell) => TableCell,\n ): EditResult<BlockRef> {\n const table = this.getTable(cell.table);\n if (!table) return fail({ code: \"invalid-state\", details: \"target is not a table\" });\n const rows = table.rows.slice();\n const target = rows[cell.row];\n if (!target) return fail({ code: \"invalid-position\", details: \"row not found\" });\n const hit = cellAtVisual(table, cell.row, cell.col);\n if (!hit) return fail({ code: \"invalid-position\", details: \"cell not found\" });\n const newCells = target.cells.slice();\n newCells[hit.cellIndex] = transform(hit.cell);\n rows[cell.row] = { ...target, cells: newCells };\n return this.editor.replaceBlock(cell.table, { ...table, rows });\n }\n}\n\n// === helper functions (pure) ===\n\n/** Total visible column count for a table — max over rows. */\nfunction columnCount(table: Table): number {\n return Math.max(\n table.grid.length,\n ...table.rows.map((r) => r.cells.reduce((n, c) => n + (c.gridSpan ?? 1), 0)),\n );\n}\n\n/** Default content for a fresh cell: one empty paragraph. */\nfunction emptyCell(): TableCell {\n return { content: [{ kind: \"paragraph\", properties: {}, runs: [] }] };\n}\n\nfunction defaultRowCells(colCount: number): TableCell[] {\n return Array.from({ length: colCount }, () => emptyCell());\n}\n\nfunction padCellsToColumns(cells: TableCell[], colCount: number): TableCell[] {\n const totalSpan = cells.reduce((n, c) => n + (c.gridSpan ?? 1), 0);\n if (totalSpan >= colCount) return cells;\n const deficit = colCount - totalSpan;\n return [...cells, ...Array.from({ length: deficit }, () => emptyCell())];\n}\n\nfunction averageColumnWidth(table: Table): number | null {\n if (table.grid.length === 0) return null;\n const total = table.grid.reduce((n, w) => n + w, 0);\n return Math.round(total / table.grid.length);\n}\n\nfunction resolveRowInsertIndex(table: Table, opts: InsertRowOpts): number | null {\n if (opts.at === \"start\") return 0;\n if (opts.at === \"end\") return table.rows.length;\n if (opts.index === undefined) return null;\n if (opts.index < 0 || opts.index >= table.rows.length) return null;\n return opts.at === \"before\" ? opts.index : opts.index + 1;\n}\n\nfunction resolveColumnInsertIndex(colCount: number, opts: InsertColumnOpts): number | null {\n if (opts.at === \"start\") return 0;\n if (opts.at === \"end\") return colCount;\n if (opts.index === undefined) return null;\n if (opts.index < 0 || opts.index >= colCount) return null;\n return opts.at === \"before\" ? opts.index : opts.index + 1;\n}\n\n/**\n * Given a `(row, col)` in *visual* coordinates, find which TableCell in\n * `row.cells` holds that position, accounting for `gridSpan`.\n */\nexport function cellAtVisual(\n table: Table,\n row: number,\n col: number,\n): { cellIndex: number; cell: TableCell; startCol: number } | null {\n const r = table.rows[row];\n if (!r) return null;\n let c = 0;\n for (let i = 0; i < r.cells.length; i++) {\n const cell = r.cells[i];\n if (!cell) continue;\n const span = cell.gridSpan ?? 1;\n if (col >= c && col < c + span) {\n return { cellIndex: i, cell, startCol: c };\n }\n c += span;\n }\n return null;\n}\n\n/**\n * After inserting a new row at `insertAt`, scan the surrounding rows: if\n * a vertical merge spanned across the insertion point (restart above,\n * continue below), the newly-inserted row must carry `continue` cells in\n * those columns so the merge doesn't visually break.\n */\nfunction patchVMergeAcrossInsertedRow(rows: TableRow[], insertAt: number): void {\n if (insertAt === 0 || insertAt === rows.length - 0) return;\n const newRow = rows[insertAt];\n const above = rows[insertAt - 1];\n const below = rows[insertAt + 1];\n if (!newRow || !above || !below) return;\n\n let aboveCol = 0;\n for (const aCell of above.cells) {\n const span = aCell.gridSpan ?? 1;\n const isRestartOrContinue =\n aCell.vMerge === \"restart\" || aCell.vMerge === \"continue\";\n const belowCell = cellAtVisual({ rows, grid: [], properties: {}, kind: \"table\" }, 1, aboveCol);\n const belowContinues = belowCell?.cell.vMerge === \"continue\";\n if (isRestartOrContinue && belowContinues) {\n insertContinueInRowAtVisual(newRow, aboveCol, span);\n }\n aboveCol += span;\n }\n}\n\nfunction insertContinueInRowAtVisual(\n row: TableRow,\n startCol: number,\n gridSpan: number,\n): void {\n let c = 0;\n let insertAtIndex = row.cells.length;\n for (let i = 0; i < row.cells.length; i++) {\n if (c >= startCol) {\n insertAtIndex = i;\n break;\n }\n c += row.cells[i]?.gridSpan ?? 1;\n }\n const cell: TableCell = {\n vMerge: \"continue\",\n content: [{ kind: \"paragraph\", properties: {}, runs: [] }],\n };\n if (gridSpan > 1) cell.gridSpan = gridSpan;\n row.cells.splice(insertAtIndex, 0, cell);\n}\n\n/**\n * When a row containing `vMerge: \"restart\"` is removed, the first\n * `continue` below it in each affected column must be promoted to\n * `restart` — otherwise the merge has no anchor.\n */\nfunction promoteVMergeContinuations(\n rows: TableRow[],\n removedIndex: number,\n removed: TableRow,\n): void {\n let col = 0;\n for (const cell of removed.cells) {\n const span = cell.gridSpan ?? 1;\n if (cell.vMerge === \"restart\") {\n // Find the first continuation at `col` among rows[removedIndex ..].\n const successorRowIndex = rows.findIndex((_row, i) => {\n if (i < removedIndex) return false;\n const hit = cellAtVisual({ rows, grid: [], properties: {}, kind: \"table\" }, i, col);\n return hit?.cell.vMerge === \"continue\";\n });\n if (successorRowIndex >= 0) {\n const r = rows[successorRowIndex];\n const hit = cellAtVisual({ rows, grid: [], properties: {}, kind: \"table\" }, successorRowIndex, col);\n if (r && hit) {\n const newCells = r.cells.slice();\n const promoted: TableCell = { ...hit.cell };\n delete (promoted as Partial<TableCell>).vMerge;\n newCells[hit.cellIndex] = promoted;\n rows[successorRowIndex] = { ...r, cells: newCells };\n }\n }\n }\n col += span;\n }\n}\n\n/** Insert a column into a single row at the visual `atCol`. */\nfunction insertColumnInRow(row: TableRow, atCol: number, splitMerge: boolean): TableRow {\n let c = 0;\n const newCells: TableCell[] = [];\n let inserted = false;\n\n for (let i = 0; i < row.cells.length; i++) {\n const cell = row.cells[i];\n if (!cell) continue;\n const span = cell.gridSpan ?? 1;\n\n if (!inserted && atCol <= c) {\n newCells.push(emptyCell());\n inserted = true;\n }\n if (!inserted && atCol > c && atCol < c + span) {\n // Insertion falls inside this cell's span.\n if (splitMerge) {\n // Split: reduce this cell to cover up to atCol-c, insert fresh cell,\n // append a remainder cell covering the rest.\n const leftSpan = atCol - c;\n const rightSpan = span - leftSpan;\n const left: TableCell = { ...cell };\n if (leftSpan > 1) left.gridSpan = leftSpan;\n else delete (left as Partial<TableCell>).gridSpan;\n newCells.push(left);\n newCells.push(emptyCell());\n const right: TableCell = { ...emptyCell() };\n if (rightSpan > 1) right.gridSpan = rightSpan;\n newCells.push(right);\n } else {\n // Extend: grow this cell's span by 1.\n const extended: TableCell = { ...cell, gridSpan: span + 1 };\n newCells.push(extended);\n }\n inserted = true;\n c += span;\n continue;\n }\n\n newCells.push(cell);\n c += span;\n }\n\n if (!inserted) newCells.push(emptyCell());\n return { ...row, cells: newCells };\n}\n\n/** Delete a column from a single row at the visual `atCol`. */\nfunction deleteColumnFromRow(row: TableRow, atCol: number): TableRow {\n let c = 0;\n const newCells: TableCell[] = [];\n\n for (let i = 0; i < row.cells.length; i++) {\n const cell = row.cells[i];\n if (!cell) continue;\n const span = cell.gridSpan ?? 1;\n\n if (atCol >= c && atCol < c + span) {\n if (span === 1) {\n // Drop the cell entirely.\n } else {\n // Shrink the span by one.\n const shrunk: TableCell = { ...cell };\n if (span - 1 > 1) shrunk.gridSpan = span - 1;\n else delete (shrunk as Partial<TableCell>).gridSpan;\n newCells.push(shrunk);\n }\n } else {\n newCells.push(cell);\n }\n\n c += span;\n }\n\n return { ...row, cells: newCells };\n}\n\n/**\n * Apply a rectangle merge across one row. `isTopRow` distinguishes the\n * root (gets `gridSpan` + optional `vMerge: restart`) from the\n * continuation rows (get `continue` placeholders across the merge span).\n */\nfunction applyMergeToRow(\n row: TableRow,\n isTopRow: boolean,\n startCol: number,\n colSpan: number,\n rowSpan: number,\n): TableRow {\n let c = 0;\n const newCells: TableCell[] = [];\n\n for (let i = 0; i < row.cells.length; i++) {\n const cell = row.cells[i];\n if (!cell) continue;\n const span = cell.gridSpan ?? 1;\n\n if (c < startCol) {\n newCells.push(cell);\n } else if (c === startCol && isTopRow) {\n const merged: TableCell = { ...cell };\n if (colSpan > 1) merged.gridSpan = colSpan;\n if (rowSpan > 1) merged.vMerge = \"restart\";\n newCells.push(merged);\n } else if (c >= startCol && c < startCol + colSpan) {\n if (!isTopRow && c === startCol) {\n // Continuation row, left-edge cell: becomes the \"continue\" anchor.\n const cont: TableCell = {\n vMerge: \"continue\",\n content: [{ kind: \"paragraph\", properties: {}, runs: [] }],\n };\n if (colSpan > 1) cont.gridSpan = colSpan;\n newCells.push(cont);\n }\n // Skip other cells inside the merge region.\n } else {\n newCells.push(cell);\n }\n c += span;\n }\n\n return { ...row, cells: newCells };\n}\n\n/** Split the root row of a previously-merged cell back into individual cells. */\nfunction unmergeTopRow(row: TableRow, startCol: number, gridSpan: number): TableRow {\n let c = 0;\n const newCells: TableCell[] = [];\n for (let i = 0; i < row.cells.length; i++) {\n const cell = row.cells[i];\n if (!cell) continue;\n const span = cell.gridSpan ?? 1;\n if (c === startCol) {\n // Replace with `gridSpan` single cells; first keeps content + props\n // minus merge markers.\n const primary: TableCell = { ...cell };\n delete (primary as Partial<TableCell>).gridSpan;\n delete (primary as Partial<TableCell>).vMerge;\n newCells.push(primary);\n for (let k = 1; k < gridSpan; k++) newCells.push(emptyCell());\n } else {\n newCells.push(cell);\n }\n c += span;\n }\n return { ...row, cells: newCells };\n}\n\n/** Replace a vertical-merge continuation row's placeholder with fresh cells. */\nfunction unmergeContinuationRow(row: TableRow, startCol: number, gridSpan: number): TableRow {\n let c = 0;\n const newCells: TableCell[] = [];\n for (let i = 0; i < row.cells.length; i++) {\n const cell = row.cells[i];\n if (!cell) continue;\n const span = cell.gridSpan ?? 1;\n if (c === startCol && cell.vMerge === \"continue\") {\n // Expand to fresh cells covering the same column span.\n for (let k = 0; k < gridSpan; k++) newCells.push(emptyCell());\n } else {\n newCells.push(cell);\n }\n c += span;\n }\n return { ...row, cells: newCells };\n}\n\n/** Merge patch into a TableCell's per-field properties. */\nfunction mergeCellProps(cell: TableCell, patch: Partial<Omit<TableCell, \"content\">>): TableCell {\n const out: TableCell = { ...cell };\n const keys: Array<keyof Omit<TableCell, \"content\">> = [\n \"gridSpan\",\n \"vMerge\",\n \"verticalAlign\",\n \"shading\",\n \"borders\",\n ];\n for (const key of keys) {\n if (key in patch) {\n const value = patch[key];\n if (value === undefined) {\n delete (out as Partial<TableCell>)[key];\n } else {\n assignCellProp(out, key, value);\n }\n }\n }\n return out;\n}\n\nfunction assignCellProp<K extends keyof Omit<TableCell, \"content\">>(\n cell: TableCell,\n key: K,\n value: NonNullable<Omit<TableCell, \"content\">[K]>,\n): void {\n if (key === \"gridSpan\" && typeof value === \"number\") cell.gridSpan = value;\n else if (key === \"vMerge\" && (value === \"restart\" || value === \"continue\")) cell.vMerge = value;\n else if (key === \"verticalAlign\" && typeof value === \"string\")\n cell.verticalAlign = value as \"top\" | \"center\" | \"bottom\";\n else if (key === \"shading\") cell.shading = value as Shading;\n else if (key === \"borders\") cell.borders = value as TableCellBorders;\n}\n\n/** Used internally by alignment setters in the toolbar. */\nexport type TableCellAlignment = ParagraphAlignment;\n","import { renderBlocks } from \"./block\";\nimport type { SobreeDocument } from \"../../../doc/types\";\n\n/**\n * Walk a SobreeDocument's body into `host`, replacing its existing\n * children. List grouping, heading style mapping, and image resolution\n * live downstream in `block.ts` / `inline.ts`.\n *\n * Footnotes (when present) render as an `<aside class=\"sobree-footnotes\">`\n * appended *after* the body content. True per-page pinning of footnotes\n * is a paginator feature deferred until a fixture demands it; for now\n * the references in body text link to footnote bodies at the doc end.\n *\n * Comments are NOT rendered here — core only emits the neutral inline\n * `<span class=\"sobree-comment-range\">` marks (in `inline.ts`). The\n * `@sobree/review` plugin reads those marks + `doc.comments` to render\n * the comment cards. Without the plugin, commented text is still\n * highlighted; there's just no card surface.\n */\nexport function renderSobreeDocument(\n doc: SobreeDocument,\n host: HTMLElement,\n blockIds?: readonly string[],\n): void {\n host.replaceChildren();\n // Per-document layout settings: stamp `<w:defaultTabStop>` as a CSS\n // variable on the host so every paragraph inherits Word's tab\n // geometry by default (paragraphs that declare their own `<w:tabs>`\n // still override via inline `tab-size` set in `applyParagraphProps`).\n // Falls back to Word's factory default 720 twips (0.5\") when the\n // doc's settings.xml omits the element.\n applyDefaultTabStop(host, doc.settings?.defaultTabStopTwips ?? 720);\n renderBlocks(doc.body, host, doc.numbering, doc.styles, doc.rawParts, blockIds, doc.sections);\n if (doc.footnotes && Object.keys(doc.footnotes).length > 0) {\n renderFootnotesAside(doc, host);\n }\n}\n\nfunction applyDefaultTabStop(host: HTMLElement, tabStopTwips: number): void {\n // twips → mm: 1 inch = 25.4 mm = 1440 twips.\n const mm = (tabStopTwips / 1440) * 25.4;\n // Set on the editor root (or paper-stack root, or `host` itself as\n // fallback) so the value reaches BOTH `.paper-content` descendants\n // AND `.paper-header` / `.paper-footer` siblings (which sit outside\n // host but inside the same editor scope). Without walking up, the\n // tab-size only inherits into body paragraphs and header tabs revert\n // to the browser's 8-char default.\n let scope: HTMLElement | null = host;\n while (\n scope &&\n !scope.classList.contains(\"sobree-editor\") &&\n !scope.classList.contains(\"paper-stack\")\n ) {\n scope = scope.parentElement;\n }\n const target = scope ?? host;\n target.style.setProperty(\"tab-size\", `${mm}mm`);\n target.style.setProperty(\"-moz-tab-size\", `${mm}mm`);\n}\n\nfunction renderFootnotesAside(doc: SobreeDocument, host: HTMLElement): void {\n const aside = document.createElement(\"aside\");\n aside.className = \"sobree-footnotes\";\n aside.setAttribute(\"role\", \"doc-endnotes\");\n const list = document.createElement(\"ol\");\n list.className = \"sobree-footnotes__list\";\n const ids = Object.keys(doc.footnotes!)\n .map((s) => Number(s))\n .filter((n) => Number.isFinite(n))\n .sort((a, b) => a - b);\n for (const id of ids) {\n const li = document.createElement(\"li\");\n li.id = `sobree-footnote-${id}`;\n li.value = id;\n li.className = \"sobree-footnotes__item\";\n renderBlocks(doc.footnotes![id]!, li, doc.numbering, doc.styles, doc.rawParts);\n list.appendChild(li);\n }\n aside.appendChild(list);\n host.appendChild(aside);\n}\n","/**\n * History config + depth shape. Phase 1b.6+ — backed by Y.UndoManager;\n * the snapshot-specific types (HistoryEntry, SnapshotPosition,\n * SnapshotSelection) that lived here in Phase 1a are no longer needed.\n */\n\nexport interface HistoryConfig {\n /** Hard cap on entry count (per stack). UndoManager doesn't expose\n * a max-depth knob directly — kept here for forward-compat in case\n * we add LRU-trim ourselves; Yjs's UndoManager grows unboundedly\n * in practice (one entry per coalesced typing burst, so a long\n * session is still bounded). */\n maxDepth: number;\n /**\n * Soft cap on rough memory footprint in bytes. Currently unused\n * (UndoManager doesn't expose memory introspection); kept for\n * future tuning. */\n maxBytesEstimate: number;\n /**\n * Idle time within which consecutive Y operations merge into one\n * undo step (Y.UndoManager's `captureTimeout`). Default 1000ms —\n * Word-style typing-session coalescing.\n */\n coalesceIdleMs: number;\n}\n\nexport const DEFAULT_HISTORY_CONFIG: HistoryConfig = {\n maxDepth: 100,\n maxBytesEstimate: 10 * 1024 * 1024,\n coalesceIdleMs: 1000,\n};\n\n/** Snapshot of `{undoDepth, redoDepth}` for `History.depth()` + change events. */\nexport interface HistoryDepth {\n undo: number;\n redo: number;\n}\n\n// === legacy type aliases (kept so external imports from Phase 1a don't break) ===\n\n/** @deprecated Selection now persists via stable block ids — no\n * snapshot conversion is needed. */\nexport interface SnapshotPosition {\n blockIndex: number;\n offset: number;\n}\n\n/** @deprecated Pass-through alias to keep older imports compiling. */\nexport type SnapshotSelection =\n | null\n | { kind: \"caret\"; at: SnapshotPosition }\n | { kind: \"range\"; from: SnapshotPosition; to: SnapshotPosition };\n\n/** @deprecated Y.UndoManager owns history state; no separate entry\n * shape is exposed. Kept as a permissive type alias so any consumer\n * that imported `HistoryEntry` keeps compiling. */\nexport interface HistoryEntry {\n doc: unknown;\n selection: SnapshotSelection | unknown;\n reason: string;\n timestamp: number;\n}\n","/**\n * Undo / redo for the Sobree editor — backed by `Y.UndoManager`.\n *\n * # Why Y.UndoManager\n *\n * Phase 1b.6 swaps the snapshot-stack History for `Y.UndoManager` so:\n *\n * 1. **Per-peer undo.** UndoManager tracks operations by *origin*.\n * When a peer types, those Y operations are tagged `\"local\"`. A\n * remote peer's edits arrive with a different origin (the\n * provider name), so the local UndoManager doesn't see them as\n * its own. `Cmd+Z` reverses only the local user's edits — exactly\n * what users expect in collab.\n *\n * 2. **CRDT-native.** Undoing produces *inverse Y operations*, which\n * flow through the same broadcast pipeline as forward edits. No\n * separate \"undo over the wire\" protocol needed.\n *\n * 3. **No snapshot stack.** Memory is bounded by Yjs's internal item\n * list; no separate snapshot bookkeeping.\n *\n * # API compatibility\n *\n * The public surface (`undo`, `redo`, `canUndo`, `canRedo`, `clear`,\n * `depth`, `on`) matches the old History so the Editor's command-bus\n * registrations and the keyboard plugin's `Cmd+Z` mapping work\n * unchanged. The legacy `recordCommit` / `recordTyping` / `flush`\n * methods are kept as no-ops — UndoManager auto-tracks ops by origin\n * and `captureTimeout` handles coalescing without explicit \"begin a\n * typing session\" calls.\n *\n * # Selection restore\n *\n * Each undo/redo step needs to restore the cursor to its pre-edit\n * position. We capture the live selection in `stack-item-added`'s\n * `meta` and restore it on `stack-item-popped`. The restore happens\n * AFTER the editor has re-projected and re-rendered (the Y observer\n * fires before the popped event — see `Editor.adoptYDocState`).\n */\n\nimport * as Y from \"yjs\";\nimport type { Selection } from \"../doc/api\";\nimport {\n DEFAULT_HISTORY_CONFIG,\n type HistoryConfig,\n type HistoryDepth,\n} from \"./types\";\n\nexport type HistoryEvent = \"change\";\nexport type HistoryListener = (depth: HistoryDepth) => void;\n\nexport interface HistoryOptions extends Partial<HistoryConfig> {\n /** Y.Doc to track. The UndoManager observes the body, meta, and\n * parts top-level types — every Sobree mutation funnels through\n * one of these, so any local edit produces a stack entry. */\n ydoc: Y.Doc;\n /** Origin string used by the Editor's `mirrorToYDoc()` and other\n * local writes. UndoManager only tracks operations whose origin\n * is in this set. Defaults to `\"local\"`. */\n localOrigin?: unknown;\n /** Capture the *current* live selection — called as a stack item\n * is being added so we can stash it for restore on undo. */\n captureSelection: () => Selection;\n /** Restore a previously-captured selection to the live DOM /\n * EditorSelection. Called on undo / redo after the Y.Doc has been\n * re-projected and re-rendered. */\n restoreSelection: (sel: Selection) => void;\n}\n\n/** Meta key used to stash captured selection on each stack item. */\nconst META_SELECTION_KEY = \"sobree:selection\";\n\nexport class History {\n private readonly mgr: Y.UndoManager;\n private readonly listeners = new Set<HistoryListener>();\n private readonly captureSelection: () => Selection;\n private readonly restoreSelection: (sel: Selection) => void;\n\n constructor(opts: HistoryOptions) {\n this.captureSelection = opts.captureSelection;\n this.restoreSelection = opts.restoreSelection;\n const captureTimeout = opts.coalesceIdleMs ?? DEFAULT_HISTORY_CONFIG.coalesceIdleMs;\n const localOrigin = opts.localOrigin ?? \"local\";\n\n // Track every top-level Y type the editor mutates: body, meta,\n // parts. Anything written by `mirrorToYDoc` or by direct Y.Text\n // operations on a body block lands in one of these — so the\n // UndoManager catches every local mutation.\n //\n // Each Y type's event-handler generic doesn't unify with\n // `AbstractType<unknown>` in TS, but Yjs accepts the array at\n // runtime. Cast at the boundary.\n const tracked = [\n opts.ydoc.getArray(\"body\"),\n opts.ydoc.getMap(\"meta\"),\n opts.ydoc.getMap(\"parts\"),\n ] as unknown as Y.AbstractType<unknown>[];\n\n this.mgr = new Y.UndoManager(tracked, {\n captureTimeout,\n trackedOrigins: new Set([localOrigin]),\n });\n\n // Stash the pre-edit selection on each newly-added stack item.\n // Yjs fires this synchronously at the end of a tracked\n // transaction — after the AST mutator has produced the new state.\n // We capture the LIVE selection here, which is the post-edit\n // position. For undo, we want the *pre-edit* position; that's\n // captured separately via the input listener (see Editor).\n this.mgr.on(\"stack-item-added\", ({ stackItem }) => {\n stackItem.meta.set(META_SELECTION_KEY, this.captureSelection());\n this.fire();\n });\n\n // On pop (undo / redo), the inverse Y ops have already run and\n // the editor's afterTransaction observer has re-projected +\n // re-rendered. Restore selection to whatever was captured.\n this.mgr.on(\"stack-item-popped\", ({ stackItem }) => {\n const sel = stackItem.meta.get(META_SELECTION_KEY) as Selection | undefined;\n if (sel !== undefined) this.restoreSelection(sel);\n this.fire();\n });\n }\n\n // === public API (kept compatible with the snapshot-era History) ===\n\n undo(): boolean {\n return this.mgr.undo() !== null;\n }\n\n redo(): boolean {\n return this.mgr.redo() !== null;\n }\n\n canUndo(): boolean {\n return this.mgr.canUndo();\n }\n\n canRedo(): boolean {\n return this.mgr.canRedo();\n }\n\n clear(): void {\n this.mgr.clear();\n this.fire();\n }\n\n depth(): HistoryDepth {\n return {\n undo: this.mgr.undoStack.length,\n redo: this.mgr.redoStack.length,\n };\n }\n\n on(_event: HistoryEvent, cb: HistoryListener): () => void {\n this.listeners.add(cb);\n return () => this.listeners.delete(cb);\n }\n\n destroy(): void {\n this.mgr.destroy();\n this.listeners.clear();\n }\n\n // === legacy methods kept as no-ops for API compatibility ===\n\n /** @deprecated UndoManager auto-tracks ops by origin; no need to\n * call this. Kept as a no-op so the Editor's existing call sites\n * don't break. */\n recordCommit(): void {\n /* no-op */\n }\n\n /** @deprecated UndoManager's `captureTimeout` coalesces consecutive\n * ops into one stack item. No explicit recording needed. */\n recordTyping(): void {\n /* no-op */\n }\n\n /** @deprecated UndoManager flushes implicitly on `captureTimeout`\n * expiry or on the next non-tracked operation. */\n flush(): void {\n /* no-op */\n }\n\n // === internals ===\n\n private fire(): void {\n const d = this.depth();\n for (const cb of this.listeners) {\n try {\n cb(d);\n } catch (err) {\n console.error(\"[history] listener threw:\", err);\n }\n }\n }\n}\n\n// === legacy re-exports (kept so external code keeps importing) ===\n//\n// `makeEntry` / `selectionToSnapshot` / `snapshotToSelection` were the\n// helpers the snapshot-era History used to convert id-keyed selections\n// to index-keyed snapshots so they survived a `BlockRegistry.reset`.\n// With UndoManager, the registry isn't reset on undo/redo — the\n// adoptIds path keeps ids stable — so id-keyed selections work as-is.\n// These helpers are no-op-shaped pass-throughs kept for backwards-compat.\n\nimport type { Selection as PublicSelection } from \"../doc/api\";\n\n/** @deprecated Selection survives undo/redo via stable block ids now;\n * no snapshot-conversion is needed. Kept as a pass-through stub. */\nexport function makeEntry(\n doc: unknown,\n selection: PublicSelection,\n reason: string,\n _indexOfBlock: (id: string) => number,\n): { doc: unknown; selection: PublicSelection; reason: string; timestamp: number } {\n return { doc, selection, reason, timestamp: Date.now() };\n}\n\n/** @deprecated Pass-through; selection structure is no longer\n * index-keyed in the new history. */\nexport function selectionToSnapshot(\n selection: PublicSelection,\n _indexOfBlock: (id: string) => number,\n): PublicSelection {\n return selection;\n}\n\n/** @deprecated Pass-through; selection survives via stable ids. */\nexport function snapshotToSelection(\n snap: PublicSelection,\n _refAt: (idx: number) => unknown,\n): PublicSelection {\n return snap;\n}\n","import type { Range as ApiRange, BlockRef } from \"../doc/api\";\nimport { sliceRuns } from \"../doc/runs\";\nimport type { InlineRun, RunProperties } from \"../doc/types\";\nimport type { Editor, WrapTag } from \"../editor\";\n\n/**\n * Mark toggle helpers — shared by the floating toolbar's mark buttons\n * and the keyboard plugin (Ctrl+B / I / U / …).\n *\n * \"Mark\" = a boolean / enum run property that maps 1:1 to a `wrapRange`\n * tag: `strong`, `em`, `u`, `s`, `sup`, `sub`. (`mark`/highlight is\n * passthrough — not a toggle, since highlight is colour-valued.)\n */\n\n/** Tag → run property key for toggle detection. */\nexport const MARK_PROP: Record<string, keyof RunProperties> = {\n strong: \"bold\",\n em: \"italic\",\n u: \"underline\",\n s: \"strike\",\n sup: \"verticalAlign\",\n sub: \"verticalAlign\",\n};\n\n/** Expected \"on\" value for each mark. */\nexport const MARK_ON: Record<string, RunProperties[keyof RunProperties]> = {\n strong: true,\n em: true,\n u: \"single\",\n s: true,\n sup: \"superscript\",\n sub: \"subscript\",\n};\n\nexport type ToggleableMark = \"strong\" | \"em\" | \"u\" | \"s\" | \"sup\" | \"sub\";\n\n/**\n * Standard mark commands registered on every Editor's command bus.\n * Owned by the marks module so a single source of truth backs the\n * keyboard plugin (`@sobree/keyboard`), the floating toolbar\n * (`@sobree/block-tools`), and any custom UI / agent dispatch.\n */\nexport interface MarkCommandDef {\n name: string;\n title: string;\n tag: ToggleableMark;\n}\n\nexport const MARK_COMMAND_DEFS: readonly MarkCommandDef[] = [\n { name: \"mark.toggle.bold\", title: \"Bold\", tag: \"strong\" },\n { name: \"mark.toggle.italic\", title: \"Italic\", tag: \"em\" },\n { name: \"mark.toggle.underline\", title: \"Underline\", tag: \"u\" },\n { name: \"mark.toggle.strike\", title: \"Strikethrough\", tag: \"s\" },\n { name: \"mark.toggle.superscript\", title: \"Superscript\", tag: \"sup\" },\n { name: \"mark.toggle.subscript\", title: \"Subscript\", tag: \"sub\" },\n];\n\n/**\n * Apply or clear a mark across `range` based on its current state. If\n * every text run in the range already carries the mark's \"on\" value,\n * the call clears it; otherwise the mark is applied.\n *\n * `mark` (highlight) is delegated straight to `wrapRange` — it's not a\n * toggle since highlight is a colour, not a boolean.\n */\nexport function toggleMark(\n editor: Editor,\n range: ApiRange,\n tag: WrapTag,\n): void {\n if (tag === \"mark\") {\n editor.wrapRange(range, tag);\n return;\n }\n if (isMarkActive(editor, range, tag)) {\n const prop = MARK_PROP[tag];\n if (!prop) return;\n editor.applyRunProperties(range, { [prop]: undefined } as never);\n } else {\n editor.wrapRange(range, tag);\n }\n}\n\n/**\n * True when every non-empty text run inside `range` has the mark's \"on\"\n * value. Walks hyperlink children. Multi-block ranges always read as\n * inactive so a click sets the mark before clearing it.\n */\nexport function isMarkActive(editor: Editor, range: ApiRange, tag: string): boolean {\n const prop = MARK_PROP[tag];\n const onValue = MARK_ON[tag];\n if (!prop) return false;\n if (range.from.block.id !== range.to.block.id) return false;\n const info = editor.getBlockById(range.from.block.id);\n if (!info || info.kind !== \"paragraph\") return false;\n const doc = editor.getDocument();\n const block = doc.body[info.index];\n if (!block || block.kind !== \"paragraph\") return false;\n const from = range.from.offset;\n const to = range.to.offset;\n const runs = from === to ? block.runs : sliceRuns(block.runs, from, to);\n if (runs.length === 0) return false;\n return everyTextRunHas(runs, prop, onValue);\n}\n\nfunction everyTextRunHas(\n runs: readonly InlineRun[],\n prop: keyof RunProperties,\n onValue: unknown,\n): boolean {\n let sawText = false;\n for (const r of runs) {\n if (r.kind === \"text\") {\n if (r.text.length === 0) continue;\n sawText = true;\n const v = (r.properties as Record<string, unknown>)[prop];\n if (v !== onValue) return false;\n } else if (r.kind === \"hyperlink\") {\n if (!everyTextRunHas(r.children, prop, onValue)) return false;\n sawText = true;\n }\n // Other kinds (drawing, break) don't carry run properties — ignore.\n }\n return sawText;\n}\n\n/**\n * Pick the right range to operate on: live DOM selection if any,\n * otherwise the full extent of the block at the caret. Matches the\n * \"press Ctrl+B with caret only → bold the whole paragraph\" UX.\n */\nexport function rangeAtSelection(editor: Editor): ApiRange | null {\n const sel = editor.selection.currentRange();\n if (sel) return sel;\n const caret = editor.selection.currentCaret();\n let block: BlockRef | null = caret?.block ?? null;\n if (!block) {\n const first = editor.getBlocks()[0];\n if (first) block = { id: first.id, version: first.version };\n }\n if (!block) return null;\n const info = editor.getBlockById(block.id);\n const length = info?.length ?? 0;\n return {\n from: { block, offset: 0 },\n to: { block, offset: length },\n };\n}\n","import type { HyperlinkRun, InlineRun, RunProperties } from \"../../../doc/types\";\n\n/**\n * Serialise DOM children of `el` into a flat `InlineRun[]`. Nested\n * formatting wrappers (`<strong><em>...`) are flattened — each leaf text\n * node yields one `TextRun` whose `RunProperties` is the union of all\n * formatting seen on the path from `el` to that text node.\n *\n * `<a>` elements produce a `HyperlinkRun` wrapping recursively-serialised\n * children (their own flat run list).\n */\nexport function serializeInlineChildren(el: HTMLElement): InlineRun[] {\n const out: InlineRun[] = [];\n for (const node of Array.from(el.childNodes)) walk(node, {}, out);\n return out;\n}\n\nfunction walk(node: Node, inherited: RunProperties, out: InlineRun[]): void {\n if (node.nodeType === Node.TEXT_NODE) {\n const text = node.textContent ?? \"\";\n if (text === \"\") return;\n out.push({ kind: \"text\", text, properties: { ...inherited } });\n return;\n }\n if (!(node instanceof HTMLElement)) return;\n if (node.getAttribute(\"contenteditable\") === \"false\") {\n // Skip editor chrome (e.g. table tool bar, page-break markers that\n // re-emerge as synthetic runs in their own right).\n if (node.hasAttribute(\"data-page-break\") || node.dataset.pageBreak !== undefined) {\n out.push({ kind: \"break\", type: \"page\" });\n }\n return;\n }\n\n const tag = node.tagName.toLowerCase();\n\n switch (tag) {\n case \"br\":\n out.push({ kind: \"break\", type: \"line\" });\n return;\n case \"img\": {\n const alt = node.getAttribute(\"alt\") ?? \"\";\n const widthPx = readPxDimension(node.style.width, node.getAttribute(\"width\"));\n const heightPx = readPxDimension(node.style.height, node.getAttribute(\"height\"));\n const drawing: import(\"../../../doc/types\").DrawingRun = {\n kind: \"drawing\",\n partPath: node.dataset.part ?? \"\",\n widthEmu: widthPx > 0 ? Math.round((widthPx / 96) * 914400) : 0,\n heightEmu: heightPx > 0 ? Math.round((heightPx / 96) * 914400) : 0,\n placement: \"inline\",\n };\n if (alt) drawing.altText = alt;\n out.push(drawing);\n return;\n }\n case \"a\": {\n const href = node.getAttribute(\"href\") ?? \"\";\n const children: InlineRun[] = [];\n for (const child of Array.from(node.childNodes)) walk(child, inherited, children);\n const link: HyperlinkRun = { kind: \"hyperlink\", href, children };\n out.push(link);\n return;\n }\n case \"strong\":\n case \"b\":\n descend(node, { ...inherited, bold: true }, out);\n return;\n case \"em\":\n case \"i\":\n descend(node, { ...inherited, italic: true }, out);\n return;\n case \"u\":\n case \"ins\":\n descend(node, { ...inherited, underline: \"single\" }, out);\n return;\n case \"s\":\n case \"del\":\n case \"strike\":\n descend(node, { ...inherited, strike: true }, out);\n return;\n case \"sup\":\n descend(node, { ...inherited, verticalAlign: \"superscript\" }, out);\n return;\n case \"sub\":\n descend(node, { ...inherited, verticalAlign: \"subscript\" }, out);\n return;\n case \"mark\":\n descend(node, { ...inherited, highlight: \"yellow\" }, out);\n return;\n case \"code\":\n descend(node, { ...inherited, fontFamily: \"Consolas\" }, out);\n return;\n case \"span\": {\n const merged = mergeStyleAttribute(inherited, node.getAttribute(\"style\"));\n descend(node, merged, out);\n return;\n }\n default:\n // Unknown wrapper — treat as a transparent span so styling attrs\n // still flow through. This keeps user-added wrappers (e.g. from\n // paste-from-Word) from silently dropping content.\n descend(node, mergeStyleAttribute(inherited, node.getAttribute(\"style\")), out);\n return;\n }\n}\n\nfunction descend(el: HTMLElement, inherited: RunProperties, out: InlineRun[]): void {\n for (const child of Array.from(el.childNodes)) walk(child, inherited, out);\n}\n\n/**\n * Parse an inline `style` attribute's CSS declarations and fold the\n * recognised keys into a `RunProperties`. Unknown declarations drop.\n */\n/**\n * Resolve a dimension to CSS pixels, honoring the `style` value first\n * and falling back to a legacy `width`/`height` attribute. Returns 0\n * when neither is parseable.\n */\nfunction readPxDimension(styleValue: string, attrValue: string | null): number {\n const style = styleValue.trim();\n if (style) {\n const m = style.match(/^([\\d.]+)\\s*(px)?$/i);\n if (m?.[1]) {\n const n = Number(m[1]);\n if (Number.isFinite(n) && n > 0) return n;\n }\n }\n if (attrValue) {\n const n = Number(attrValue);\n if (Number.isFinite(n) && n > 0) return n;\n }\n return 0;\n}\n\nfunction mergeStyleAttribute(\n base: RunProperties,\n styleAttr: string | null,\n): RunProperties {\n if (!styleAttr) return base;\n const out: RunProperties = { ...base };\n for (const decl of styleAttr.split(\";\")) {\n const [rawKey, rawVal] = decl.split(\":\");\n if (!rawKey || !rawVal) continue;\n const key = rawKey.trim().toLowerCase();\n const val = rawVal.trim();\n if (!val) continue;\n if (key === \"color\") out.color = val;\n else if (key === \"background\" || key === \"background-color\") out.highlight = val;\n else if (key === \"font-family\") {\n out.fontFamily =\n val.replace(/^['\"]|['\"]$/g, \"\").split(\",\")[0]?.trim() || val;\n } else if (key === \"font-size\") {\n const m = val.match(/^([\\d.]+)(pt|px)?$/);\n if (m?.[1]) {\n const n = Number(m[1]);\n const pt = m[2] === \"px\" ? n * 0.75 : n;\n if (Number.isFinite(pt) && pt > 0) out.fontSizePt = pt;\n }\n } else if (key === \"font-weight\") {\n if (val === \"bold\" || Number(val) >= 600) out.bold = true;\n } else if (key === \"font-style\") {\n if (val === \"italic\") out.italic = true;\n } else if (key === \"text-decoration\") {\n if (val.includes(\"underline\")) out.underline = \"single\";\n if (val.includes(\"line-through\")) out.strike = true;\n }\n }\n return out;\n}\n","import { serializeInlineChildren } from \"./inline\";\nimport type { ParagraphAlignment, Table, TableCell, TableRow } from \"../../../doc/types\";\n\n/**\n * Convert a `<table>` back into a Table AST node.\n *\n * The AST mirrors OOXML's row/cell model: every visual cell, including\n * cells occluded by a vertical merge above them, is represented as its\n * own `TableCell` (with `vMerge: \"continue\"` for the occluded ones and\n * `vMerge: \"restart\"` on the cell that spans them). The DOM's\n * `rowspan` attribute collapses multiple rows into a single `<td>`; we\n * synthesize the continuation cells back here.\n */\nexport function tableFromElement(el: HTMLElement): Table {\n const domRows = Array.from(\n el.querySelectorAll<HTMLTableRowElement>(\":scope > thead > tr, :scope > tbody > tr\"),\n );\n const pending = new Map<number, number>();\n const rows: TableRow[] = [];\n\n for (const tr of domRows) {\n rows.push(rowFromElement(tr, pending));\n }\n\n const colCount = rows.reduce((n, r) => Math.max(n, countColumns(r.cells)), 0);\n return {\n kind: \"table\",\n grid: Array.from({ length: colCount }, () => 2400),\n rows,\n properties: {},\n };\n}\n\nfunction rowFromElement(\n tr: HTMLTableRowElement,\n pending: Map<number, number>,\n): TableRow {\n const cellEls = Array.from(tr.querySelectorAll<HTMLTableCellElement>(\":scope > th, :scope > td\"));\n const isHeader = tr.parentElement?.tagName.toLowerCase() === \"thead\";\n const cells: TableCell[] = [];\n\n let col = 0;\n let domIdx = 0;\n // Keep going while there's either DOM cells left to place or a pending\n // vertical-merge continuation still owed to this row.\n while (domIdx < cellEls.length || pending.get(col)) {\n const pendingHere = pending.get(col) ?? 0;\n if (pendingHere > 0) {\n cells.push(makeContinueCell());\n pending.set(col, pendingHere - 1);\n col += 1;\n continue;\n }\n const el = cellEls[domIdx++];\n if (!el) break;\n const gridSpan = readSpan(el.getAttribute(\"colspan\"));\n const rowSpan = readSpan(el.getAttribute(\"rowspan\"));\n const cell = cellFromElement(el);\n if (gridSpan > 1) cell.gridSpan = gridSpan;\n if (rowSpan > 1) {\n cell.vMerge = \"restart\";\n for (let i = 0; i < gridSpan; i++) pending.set(col + i, rowSpan - 1);\n }\n cells.push(cell);\n col += gridSpan;\n }\n\n const row: TableRow = { cells };\n if (isHeader) row.isHeader = true;\n return row;\n}\n\nfunction cellFromElement(c: HTMLTableCellElement): TableCell {\n const alignment = parseAlignment(c.style.textAlign);\n const props = alignment ? { alignment } : {};\n\n // Two cell-rendering modes coexist in the wild:\n //\n // 1. Modern (post the table-cell paragraph cascade fix): the cell\n // contains real `<p>` / `<ol>` / `<ul>` / `<table>` children,\n // each carrying its own paragraph properties.\n // 2. Legacy (pre-fix, also any hand-edit / paste case): the cell\n // contains inline nodes interspersed with `<br>` elements; the\n // <br> separators stand in for paragraph boundaries.\n //\n // Detect mode 1 by looking for any block-level child; otherwise fall\n // through to mode 2's <br>-splitter so backward-compat content (and\n // freshly-pasted text) keeps round-tripping.\n const hasBlockChildren = Array.from(c.children).some((child) => {\n const tag = child.tagName;\n return tag === \"P\" || tag === \"OL\" || tag === \"UL\" || tag === \"TABLE\";\n });\n\n const content: TableCell[\"content\"] = [];\n if (hasBlockChildren) {\n for (const child of Array.from(c.children) as HTMLElement[]) {\n const tag = child.tagName;\n if (tag === \"P\") {\n const childAlign = parseAlignment(child.style.textAlign) ?? alignment;\n content.push({\n kind: \"paragraph\",\n properties: childAlign ? { alignment: childAlign } : {},\n runs: serializeInlineChildren(child),\n });\n } else if (tag === \"OL\" || tag === \"UL\") {\n for (const li of Array.from(child.children)) {\n content.push({\n kind: \"paragraph\",\n properties: alignment ? { alignment } : {},\n runs: serializeInlineChildren(li as HTMLElement),\n });\n }\n }\n // Nested tables in cells are a real OOXML feature but rare; we\n // skip them at serialize time for now (they'd round-trip lossily\n // until we plumb full table-in-cell support).\n }\n } else {\n const chunks = splitOnBreaks(c);\n for (const nodes of chunks) {\n const scratch = document.createElement(\"span\");\n for (const n of nodes) scratch.appendChild(n.cloneNode(true));\n content.push({\n kind: \"paragraph\",\n properties: { ...props },\n runs: serializeInlineChildren(scratch),\n });\n }\n }\n\n if (content.length === 0) {\n content.push({ kind: \"paragraph\", properties: { ...props }, runs: [] });\n }\n return { content };\n}\n\nfunction splitOnBreaks(el: HTMLElement): Node[][] {\n const groups: Node[][] = [[]];\n for (const node of Array.from(el.childNodes)) {\n if (node instanceof HTMLBRElement) {\n groups.push([]);\n continue;\n }\n groups[groups.length - 1]!.push(node);\n }\n // Drop trailing empty group (cell ending in `<br>`).\n if (groups[groups.length - 1]?.length === 0 && groups.length > 1) groups.pop();\n return groups;\n}\n\nfunction makeContinueCell(): TableCell {\n return {\n vMerge: \"continue\",\n content: [{ kind: \"paragraph\", properties: {}, runs: [] }],\n };\n}\n\nfunction countColumns(cells: readonly TableCell[]): number {\n let n = 0;\n for (const c of cells) n += c.gridSpan ?? 1;\n return n;\n}\n\nfunction readSpan(attr: string | null): number {\n if (!attr) return 1;\n const n = Number(attr);\n return Number.isFinite(n) && n >= 1 ? n : 1;\n}\n\nfunction parseAlignment(v: string): ParagraphAlignment | undefined {\n const s = v.trim().toLowerCase();\n if (s === \"left\" || s === \"right\" || s === \"center\") return s;\n if (s === \"justify\") return \"both\";\n return undefined;\n}\n","import { serializeInlineChildren } from \"./inline\";\nimport { tableFromElement } from \"./table\";\nimport type { Block, NumberingDefinition, Paragraph, ParagraphProperties } from \"../../../doc/types\";\n\nexport interface BlockSerializeContext {\n /** Accumulated numbering definitions, one per encountered list. */\n numbering: NumberingDefinition[];\n /**\n * Allocated numId for the CURRENT list stream. Reset to `null` by the\n * caller between lists.\n */\n currentList: { numId: number; ordered: boolean } | null;\n}\n\nexport function blocksFromNodes(\n nodes: readonly Node[],\n ctx: BlockSerializeContext,\n): Block[] {\n const out: Block[] = [];\n for (const node of nodes) {\n if (node.nodeType === Node.TEXT_NODE) {\n const text = node.textContent ?? \"\";\n if (text.trim() === \"\") continue;\n out.push({\n kind: \"paragraph\",\n properties: {},\n runs: [{ kind: \"text\", text, properties: {} }],\n });\n continue;\n }\n if (!(node instanceof HTMLElement)) continue;\n\n // Section break — recognise the renderer's marker class and emit a\n // real `SectionBreak` block. Must come BEFORE the\n // `contenteditable=\"false\"` skip below, since the marker is also\n // contenteditable=false but carries semantic meaning.\n if (node.classList.contains(\"sobree-section-break\")) {\n out.push({ kind: \"section_break\", toSectionIndex: 0 });\n ctx.currentList = null;\n continue;\n }\n\n if (node.getAttribute(\"contenteditable\") === \"false\") continue;\n\n const tag = node.tagName.toLowerCase();\n if (tag === \"ul\" || tag === \"ol\") {\n // Allocate a fresh numId for this list's run.\n const numId = ctx.numbering.length + 1;\n const ordered = tag === \"ol\";\n ctx.numbering.push({\n numId,\n abstractFormat: {\n levels: [\n { level: 0, format: ordered ? \"decimal\" : \"bullet\", text: ordered ? \"%1.\" : \"\\u2022\" },\n ],\n },\n });\n for (const li of Array.from(node.querySelectorAll(\":scope > li\"))) {\n if (!(li instanceof HTMLElement)) continue;\n const paragraph: Paragraph = {\n kind: \"paragraph\",\n properties: { numbering: { numId, level: 0 } },\n runs: serializeInlineChildren(li),\n };\n out.push(paragraph);\n }\n ctx.currentList = null;\n continue;\n }\n\n ctx.currentList = null;\n\n if (tag === \"table\") {\n out.push(tableFromElement(node));\n continue;\n }\n\n if (tag === \"hr\" || node.hasAttribute(\"data-page-break\")) {\n out.push({\n kind: \"paragraph\",\n properties: {\n borders: {\n bottom: { style: \"single\", sizeEighthsOfPt: 6, color: \"auto\", spaceTwips: 1 },\n },\n },\n runs: [],\n });\n continue;\n }\n\n if (tag === \"blockquote\") {\n // Render each inner paragraph as a Quote-styled paragraph.\n for (const child of Array.from(node.children)) {\n if (!(child instanceof HTMLElement)) continue;\n const para = paragraphFromElement(child, \"Quote\");\n if (para) out.push(para);\n }\n continue;\n }\n\n const para = paragraphFromElement(node);\n if (para) out.push(para);\n }\n return out;\n}\n\nfunction paragraphFromElement(\n el: HTMLElement,\n forcedStyleId?: string,\n): Paragraph | null {\n const tag = el.tagName.toLowerCase();\n const properties: ParagraphProperties = {};\n\n if (forcedStyleId) {\n properties.styleId = forcedStyleId;\n } else {\n const m = tag.match(/^h([1-6])$/);\n if (m) properties.styleId = `Heading${m[1]}`;\n }\n\n const align = el.style.textAlign;\n if (align === \"left\" || align === \"right\" || align === \"center\") {\n properties.alignment = align;\n } else if (align === \"justify\") {\n properties.alignment = \"both\";\n }\n\n const lineHeight = el.style.lineHeight;\n if (lineHeight) {\n const n = Number(lineHeight);\n if (Number.isFinite(n) && n > 0) {\n properties.spacing = { line: Math.round(240 * n), lineRule: \"auto\" };\n }\n }\n\n // Style classes like `.style-quote` feed back into Word style ids.\n const classStyle = Array.from(el.classList).find((c) => c.startsWith(\"style-\"));\n if (classStyle && !properties.styleId) {\n const id = capitalise(classStyle.slice(6));\n properties.styleId = id;\n }\n\n return {\n kind: \"paragraph\",\n properties,\n runs: serializeInlineChildren(el),\n };\n}\n\nfunction capitalise(s: string): string {\n return s.length === 0 ? s : s[0]!.toUpperCase() + s.slice(1);\n}\n","import { type BlockSerializeContext, blocksFromNodes } from \"./block\";\nimport { defaultStyles } from \"../../../doc/builders\";\nimport type { SobreeDocument } from \"../../../doc/types\";\n\n/**\n * Serialise DOM content across one or more host elements (in document\n * order) into a SobreeDocument. Sections / headers / footers are NOT\n * produced here — the Sobree façade injects those from its current page\n * setup state before handing the document off to the exporter.\n */\nexport function serializeHostsToDocument(hosts: readonly HTMLElement[]): SobreeDocument {\n const ctx: BlockSerializeContext = { numbering: [], currentList: null };\n const body = [];\n for (const host of hosts) {\n body.push(...blocksFromNodes(Array.from(host.childNodes), ctx));\n }\n return {\n body,\n sections: [],\n headerFooterBodies: {},\n styles: defaultStyles(),\n numbering: ctx.numbering,\n rawParts: {},\n fonts: [],\n };\n}\n","import \"./imageResize.css\";\n\n/**\n * Click-to-select-and-drag-the-corner image resizing.\n *\n * Implementation:\n * - Listen for clicks inside the editor host. If an `<img>` was clicked,\n * mark it as selected (one image at a time) and show a single\n * bottom-right corner handle absolutely positioned over its corner.\n * - The handle is a sibling of the image (not a child — the image is\n * replaced/edited as content), tracked in a closure so we can move\n * it as the page scrolls or paginates.\n * - Dragging the handle updates the image's `width`/`height` styles in\n * real time, preserving aspect ratio (hold Shift to free).\n * - On mouseup we dispatch an `input` event on the host so the editor's\n * existing debounced change pipeline picks up the new dimensions.\n */\nexport function attachImageResize(host: HTMLElement): () => void {\n let selected: HTMLImageElement | null = null;\n let handle: HTMLDivElement | null = null;\n\n const onClick = (e: MouseEvent) => {\n const target = e.target as HTMLElement | null;\n if (target instanceof HTMLImageElement && host.contains(target)) {\n select(target);\n return;\n }\n // Click anywhere else inside the host (or outside): deselect, unless\n // the click hit the handle itself.\n if (target && handle && handle.contains(target)) return;\n deselect();\n };\n\n const onScroll = () => positionHandle();\n const onResize = () => positionHandle();\n\n const select = (img: HTMLImageElement) => {\n if (selected === img) {\n positionHandle();\n return;\n }\n deselect();\n selected = img;\n img.classList.add(\"is-selected\");\n handle = createHandle();\n host.appendChild(handle);\n handle.addEventListener(\"mousedown\", onHandleDown);\n positionHandle();\n };\n\n const deselect = () => {\n if (selected) selected.classList.remove(\"is-selected\");\n selected = null;\n if (handle) {\n handle.removeEventListener(\"mousedown\", onHandleDown);\n handle.remove();\n handle = null;\n }\n };\n\n const positionHandle = () => {\n if (!selected || !handle) return;\n const imgRect = selected.getBoundingClientRect();\n const hostRect = host.getBoundingClientRect();\n handle.style.left = `${imgRect.right - hostRect.left + host.scrollLeft - 8}px`;\n handle.style.top = `${imgRect.bottom - hostRect.top + host.scrollTop - 8}px`;\n };\n\n const onHandleDown = (e: MouseEvent) => {\n if (!selected) return;\n e.preventDefault();\n e.stopPropagation();\n const img = selected;\n const startX = e.clientX;\n const startY = e.clientY;\n const startW = img.getBoundingClientRect().width;\n const startH = img.getBoundingClientRect().height;\n const aspect = startW > 0 && startH > 0 ? startW / startH : 1;\n\n const onMove = (m: MouseEvent) => {\n let nextW = Math.max(20, startW + (m.clientX - startX));\n let nextH = Math.max(20, startH + (m.clientY - startY));\n if (!m.shiftKey) {\n // Constrain proportionally — pick the dimension whose change is\n // larger and snap the other to keep aspect.\n const dw = Math.abs(nextW - startW);\n const dh = Math.abs(nextH - startH);\n if (dw >= dh) nextH = nextW / aspect;\n else nextW = nextH * aspect;\n }\n img.style.width = `${Math.round(nextW)}px`;\n img.style.height = `${Math.round(nextH)}px`;\n positionHandle();\n };\n\n const onUp = () => {\n window.removeEventListener(\"mousemove\", onMove);\n window.removeEventListener(\"mouseup\", onUp);\n // Drive the editor's change pipeline so the new size lands in the\n // AST on the next serialise.\n host.dispatchEvent(new Event(\"input\", { bubbles: true }));\n };\n window.addEventListener(\"mousemove\", onMove);\n window.addEventListener(\"mouseup\", onUp);\n };\n\n host.addEventListener(\"click\", onClick);\n // The host is inside the viewport; listening on `window` covers paper\n // scrolls + viewport pans without coupling to the viewport module.\n window.addEventListener(\"scroll\", onScroll, { capture: true });\n window.addEventListener(\"resize\", onResize);\n\n return () => {\n deselect();\n host.removeEventListener(\"click\", onClick);\n window.removeEventListener(\"scroll\", onScroll, { capture: true });\n window.removeEventListener(\"resize\", onResize);\n };\n}\n\nfunction createHandle(): HTMLDivElement {\n const el = document.createElement(\"div\");\n el.className = \"sobree-image-resize-handle\";\n el.setAttribute(\"contenteditable\", \"false\");\n return el;\n}\n","import \"./editor.css\";\nimport * as Y from \"yjs\";\nimport { BlobCache, type BlobStore, sha256Hex } from \"../blob\";\nimport {\n applyDocumentToYDoc,\n applyPartRefsToYDoc,\n projectYDoc,\n seedYDoc,\n Y_PARTS_KEY,\n} from \"../ydoc\";\nimport {\n type BlockRef,\n type EditError,\n type EditResult,\n type InlinePosition,\n type Range as ApiRange,\n type Selection,\n fail,\n lockConflict,\n ok,\n} from \"../doc/api\";\nimport { emptyDocument } from \"../doc/builders\";\nimport { pruneOrphanParts } from \"../doc/parts\";\nimport {\n type EmbedFontFaces,\n type EmbedFontOptions,\n embedFontIntoDoc,\n removeFontFromDoc,\n} from \"../fonts\";\nimport {\n type RunPropertiesPatch,\n applyRunPropertiesToRuns,\n mergeAdjacentTextRuns,\n runLength,\n runsLength,\n splitRunsAt,\n} from \"../doc/runs\";\nimport type {\n Block,\n DrawingRun,\n InlineRun,\n Paragraph,\n ParagraphAlignment,\n ParagraphProperties,\n RevisionMark,\n SobreeDocument,\n Table,\n TableCell,\n TableRow,\n} from \"../doc/types\";\nimport { headingLevelOf, runsToText } from \"../doc/walk\";\nimport { BlockRegistry } from \"./internal/blockRegistry\";\nimport {\n type Mutation,\n allocateMediaPath,\n mergeParagraphProps,\n mergeSectionsAcross,\n mimeToExtension,\n pxToEmu,\n removedSectionIndex,\n wrapTagToPatch,\n} from \"./internal/mutations\";\nimport {\n applySelectionToDom,\n blockElementAtIndex,\n countBlocks,\n selectionFromDom,\n} from \"./internal/positionMap\";\nimport { EditorTable } from \"./table\";\nimport { renderSobreeDocument } from \"./view/docRenderer/index\";\nimport { FontFaceRegistry } from \"../fonts\";\nimport { History } from \"../history\";\nimport {\n MARK_COMMAND_DEFS,\n isMarkActive,\n rangeAtSelection,\n toggleMark,\n} from \"../plugins/marks\";\nimport { serializeHostsToDocument } from \"./view/docSerialize/index\";\nimport { attachImageResize } from \"./view/imageResize\";\n\n// === exported types ===\n\nexport type { BlockRef, EditError, EditResult, InlinePosition, Selection };\nexport type ApiRangeType = ApiRange;\n\n/**\n * One logical tracked change — a maximal run of consecutive inline\n * runs that all carry a `revision` marker by the same author.\n * `getRevisions()` returns these; pass `range` straight to\n * `acceptRevision` / `rejectRevision`.\n *\n * `kinds` is the set of revision types in the span: `[\"ins\"]` or\n * `[\"del\"]` for a plain change, both for a delete-then-insert\n * replacement (which accepts/rejects as a single unit).\n */\nexport interface RevisionSpan {\n range: ApiRange;\n author?: string;\n kinds: (\"ins\" | \"del\")[];\n /** ISO date of the span's first revision run, if recorded. */\n date?: string;\n /**\n * Discriminator between revision levels:\n * `\"inline\"` (default for backwards compat) — the span covers\n * `ins`/`del` text runs inside a block. Pass `range` to\n * `acceptRevision` / `rejectRevision`.\n * `\"paragraph\"` — the span flags the *paragraph mark* itself on\n * `range.from.block`. The range covers offset `[0, length]` of\n * the block so it still selects the right element for UIs, but\n * accept/reject must go through `acceptParagraphRevision` /\n * `rejectParagraphRevision`.\n * `\"format\"` — the span flags a tracked format change\n * (`<w:rPrChange>`) on contiguous runs by the same author.\n * `kinds` always reports `[\"ins\"]` (the marker is binary: a\n * format change exists or not). Pass `range` to\n * `acceptFormatRevision` / `rejectFormatRevision`.\n */\n level?: \"inline\" | \"paragraph\" | \"format\";\n}\n\n/**\n * Track-changes mode. When `enabled` is true, the editor reinterprets\n * authoring mutations as tracked revisions rather than direct edits:\n *\n * - `insertRun` stamps `revision: { type: \"ins\", author }` on the\n * inserted run instead of merging it in plainly.\n * - `deleteRange` stamps `revision: { type: \"del\", author }` on the\n * plain text runs in range instead of dropping them. A run that's\n * already an `ins` *by the same author* is dropped instead — the\n * author cancelling their own pending insert — and runs already\n * carrying a peer's revision are left untouched (the API user must\n * resolve those via `acceptRevision` / `rejectRevision` first).\n * - All other mutations (`applyRunProperties`, block-level ops, etc.)\n * pass through unchanged in this first cut; format-change tracking\n * (`<w:rPrChange>`) and paragraph-mark tracking will land later.\n *\n * `author` is the human-readable name written into the revision\n * marker. Optional — falls back to no `author` field, mirroring the\n * Word semantics for anonymous-author tracked changes.\n *\n * This is the *authoring* side of the review feature. `getRevisions`\n * / `acceptRevision` / `rejectRevision` are the *consumption* side\n * and work the same regardless of this flag.\n */\nexport interface TrackChangesState {\n enabled: boolean;\n author?: string;\n}\nexport type {\n CellRef,\n InsertAt,\n InsertColumnOpts,\n InsertRowOpts,\n MergeCellsOpts,\n} from \"./table\";\n\n/**\n * Payload delivered to `change` subscribers. Plain data — safe to\n * JSON-stringify and ship over a wire. `rawParts` is stripped from\n * `document` so the payload never carries binary Uint8Arrays.\n */\nexport interface ChangePayload {\n doc: SobreeDocument;\n /**\n * @deprecated Use `doc` instead. This alias is kept for backwards\n * compatibility within the pre-1.0 line and will be removed before\n * v1. Same reference as `doc`.\n */\n document: SobreeDocument;\n revision: number;\n documentVersion: number;\n}\n\n/** Summary of a top-level block, for `getBlocks()` and list-style UIs. */\nexport interface BlockInfo {\n /** Current position in the body (unstable across edits). */\n index: number;\n /** Stable id — pair with `version` to form a `BlockRef`. */\n id: string;\n /** Bumps on every modification of this block. */\n version: number;\n kind: Block[\"kind\"];\n styleId?: string;\n alignment?: ParagraphAlignment;\n /** Plain-text preview. */\n text: string;\n /** Total character-length of the block's content (see `runsLength`). */\n length: number;\n}\n\n/** Outline entry — one per heading in document order. */\nexport interface OutlineItem {\n level: number;\n text: string;\n blockIndex: number;\n block: BlockRef;\n}\n\nexport type ParagraphPropertiesPatch = {\n [K in keyof ParagraphProperties]?: ParagraphProperties[K] | undefined;\n};\n\nexport type WrapTag = \"sup\" | \"sub\" | \"strong\" | \"em\" | \"u\" | \"s\" | \"mark\";\n\nexport type EditorEvent =\n | \"change\"\n | \"selection\"\n | \"keydown\"\n | \"track-changes-change\";\nexport type EditorEventPayload = {\n change: ChangePayload;\n selection: SelectionPayload;\n keydown: KeyDownPayload;\n \"track-changes-change\": TrackChangesState;\n};\nexport type Unsubscribe = () => void;\n\n/**\n * Payload delivered to `selection` subscribers. Fires whenever the live\n * DOM selection changes — typing, clicking, arrow-key navigation, focus\n * loss, programmatic restore. Subscribers should subscribe through the\n * editor rather than `document.addEventListener(\"selectionchange\")`\n * directly so cleanup is centralised and the editor can later add\n * dedup / throttling.\n *\n * `selection` is the model shape (`null` when focus is outside the\n * editor). The convenience fields below mirror what `EditorSelection`\n * exposes for ergonomics — read whichever one you need.\n */\nexport interface SelectionPayload {\n selection: Selection;\n range: ApiRange | null;\n caret: InlinePosition | null;\n block: BlockRef | null;\n}\n\n/**\n * Payload delivered to `keydown` subscribers. Fires for every key press\n * inside the editor host. The editor binds NO shortcuts itself —\n * plugins map keys to API calls via `preventDefault()` (stops the\n * browser's default action) and `stopPropagation()` (stops the chain\n * of remaining subscribers). Subscribers fire in registration order.\n */\nexport interface KeyDownPayload {\n /** `KeyboardEvent.key` — `\"b\"`, `\"Enter\"`, `\"ArrowLeft\"`, … (lowercased for letters). */\n key: string;\n /** `KeyboardEvent.code` — `\"KeyB\"`, `\"Enter\"`, `\"ArrowLeft\"`, … (layout-independent). */\n code: string;\n ctrl: boolean;\n shift: boolean;\n alt: boolean;\n meta: boolean;\n /** Stop the browser's default for this key (insertion, navigation, …). */\n preventDefault(): void;\n /** Stop further subscribers from receiving this key. */\n stopPropagation(): void;\n /** Underlying DOM event — for advanced needs (`isComposing`, repeat, …). */\n originalEvent: KeyboardEvent;\n}\n\n/**\n * A registered command — a named, callable unit of editor work that\n * plugins coordinate around. Same definition gets reached by a\n * keyboard shortcut, a toolbar click, a programmatic call from an\n * agent, or an MCP request.\n *\n * The `run` function is the one place the work happens. `isActive` and\n * `isAvailable` let UI plugins paint toggle / disabled state without\n * understanding what the command actually does.\n */\nexport interface CommandDefinition<Args = void> {\n /** Dotted, namespaced — `\"mark.toggle.bold\"`, `\"section.insertBreak\"`, … */\n name: string;\n /** Short human label for tooltips / command palettes. */\n title?: string;\n /** Perform the work. Should be idempotent w.r.t. selection (a second\n * invocation on an already-bold selection clears bold, etc.). */\n run: (args: Args) => void;\n /** True when the command represents an active state (mark already on,\n * block is already a heading, …). Drives toolbar `is-active`. */\n isActive?: () => boolean;\n /** False when the command can't run (e.g. selection is wrong shape).\n * Defaults to true. Drives toolbar `disabled`. */\n isAvailable?: () => boolean;\n}\n\n/** Snapshot of one registered command — what `commands.list()` returns. */\nexport interface CommandSnapshot {\n name: string;\n title: string;\n isActive: boolean;\n isAvailable: boolean;\n}\n\n/**\n * Registry every plugin uses to talk to every other plugin. The\n * editor owns it; plugins register commands on attach and unregister\n * on detach. Keyboard plugins, toolbar plugins, and a future MCP\n * adapter all share the same dispatch path: `editor.commands.execute(name)`.\n */\nexport interface CommandBus {\n /** Register a command. Returns an unsubscribe that removes it. */\n register<Args = void>(def: CommandDefinition<Args>): () => void;\n /** Run a registered command. No-op (with a warning) if unknown. */\n execute<Args = void>(name: string, args?: Args): void;\n /** Snapshot every registered command — for command palettes,\n * toolbars rendering toggle states, accessibility audits. */\n list(): CommandSnapshot[];\n /** Whether the named command is currently registered. */\n has(name: string): boolean;\n}\n\nexport interface EditorOptions {\n initialDocument?: SobreeDocument;\n changeDebounceMs?: number;\n /**\n * Elements whose children are editable blocks, in document order. Called\n * fresh each time — the list can grow/shrink (e.g. during pagination).\n */\n contentHosts?: () => HTMLElement[];\n /**\n * Y.Doc backing the document. Optional — if absent, the editor creates\n * one internally. Embedders pass their own when they need to attach\n * a provider (`y-websocket`, `y-indexeddb`, `y-webrtc`, …) for\n * persistence or collaboration.\n *\n * When supplied, the editor checks whether the Y.Doc already has body\n * content. If empty, it seeds from `initialDocument`. If non-empty\n * (Phase 2+: a peer joined an active room), the existing Y.Doc state\n * wins and `initialDocument` is ignored. See `editor.ydoc` for the\n * public escape hatch.\n */\n ydoc?: Y.Doc;\n /**\n * Optional content-hashed `BlobStore` for binary parts (images, fonts).\n *\n * Without one (default): bytes live inline in the Y.Doc's `parts`\n * Y.Map and replicate to every peer through Y updates. Fine for\n * small docs.\n *\n * With one (Phase 3.2+): the editor hashes binary parts, uploads\n * the bytes to the store, and writes only the hash into the Y.Doc's\n * `partRefs` Y.Map. Y updates stay small regardless of image size.\n * The editor maintains a local `BlobCache` that synchronously serves\n * already-fetched bytes to the renderer; `editor.ensurePartsLoaded()`\n * is the async hook for explicit pre-fetching (e.g. before\n * `toDocx()`).\n *\n * See `@sobree/core/blob` for the interface + reference impls\n * (`inMemoryBlobStore`, `fetchBlobStore`).\n */\n blobStore?: BlobStore;\n /**\n * Initial track-changes mode. When omitted, the editor starts in\n * direct-edit mode (`{ enabled: false }`) and embedders can flip it\n * later via `editor.setTrackChanges`. See `TrackChangesState`.\n */\n trackChanges?: TrackChangesState;\n}\n\nexport { runsLength } from \"../doc/runs\";\nexport type { RunPropertiesPatch };\n\n// === Editor ===\n\n/**\n * Public editor surface.\n *\n * Two entry points for every operation:\n * - Core methods take `BlockRef` / `InlinePosition` / `Range` and\n * return `EditResult`. These are the wire-callable API — same\n * contract for in-process toolbars, headless Y peers (HeadlessSobree),\n * and future MCP wrappers.\n * - \"AtSelection\" sugar reads the live DOM selection, builds the\n * position/range for you, and delegates to the core. Use these in\n * in-process UI code.\n *\n * Mutations enforce optimistic locking via block `version` numbers.\n * Conflicts return `{ ok: false, error: { code: \"optimistic-lock\", … } }`\n * rather than throwing.\n */\nexport class Editor {\n readonly host: HTMLElement;\n readonly selection: EditorSelection;\n /**\n * Ergonomic table operations — row/column insert-delete, cell merge /\n * unmerge, cell-level properties. Every method returns an `EditResult`\n * and inherits optimistic-lock checking via `replaceBlock`.\n */\n readonly table: EditorTable;\n /**\n * Named-command registry — the coordination point between plugins.\n * Plugins register commands on attach and unregister on detach;\n * keyboard / toolbar / agent / MCP all dispatch through `execute()`.\n */\n readonly commands: CommandBus;\n\n /**\n * Y.Doc backing the document. The Editor's `this.doc` field is a\n * cached projection of this Y.Doc — every local mutation mirrors\n * into the Y.Doc inside a transact (`origin: \"local\"`). Embedders\n * read this for escape-hatch wiring (providers, dev tools, custom\n * persistence).\n *\n * Phase 1a: Y.Doc is a faithful mirror of `this.doc` but the Editor\n * still treats `this.doc` as the in-memory truth. Phase 1b inverts\n * this — Y.Doc becomes the truth and `this.doc` becomes a pure\n * cache invalidated by Y observers (which is what enables Phase 2\n * providers to drive the editor from outside).\n */\n readonly ydoc: Y.Doc;\n /**\n * Optional content-hashed blob layer (Phase 3.2+). When set, binary\n * parts (images, fonts) go through this rather than living inline\n * in the Y.Doc. `null` means \"use the inline parts path\" — today's\n * default. See `EditorOptions.blobStore`.\n */\n readonly blobStore: BlobStore | null;\n /**\n * Local cache for blob bytes — synchronously fetchable for the\n * renderer, populated by `blobStore.put` (local writes) or\n * `blobStore.get` (background fetches). `null` when no blobStore\n * is configured.\n */\n readonly blobCache: BlobCache | null;\n /** Most recent projection's partRefs map (path → hash). Used by\n * `ensurePartsLoaded` and by the `onResolved` callback to know\n * which paths reference a freshly-arrived blob. */\n private lastPartRefs: Record<string, string> = {};\n /**\n * Part paths whose bytes are in flight to the BlobStore — the\n * editor wrote them synchronously to `doc.rawParts` (so the local\n * renderer has them), but the async hash + upload + partRef write\n * is still pending. Mirror skips these so they don't end up inline\n * in the Y.Doc.\n */\n private readonly pendingPartRefMigrations: Set<string> = new Set();\n /** Listener that re-projects + re-renders on remote Y.Doc updates.\n * Removed on `destroy()`. */\n private ydocUpdateListener: ((tr: Y.Transaction) => void) | null = null;\n private doc: SobreeDocument;\n private readonly registry: BlockRegistry;\n private readonly debounceMs: number;\n private readonly getContentHosts: () => HTMLElement[];\n private debounceHandle: number | null = null;\n private detachImageResize: (() => void) | null = null;\n private inputListener: ((e: Event) => void) | null = null;\n private beforeInputListener: ((e: Event) => void) | null = null;\n private pasteListener: ((e: ClipboardEvent) => void) | null = null;\n private dragOverListener: ((e: DragEvent) => void) | null = null;\n private dropListener: ((e: DragEvent) => void) | null = null;\n private revision = 0;\n private readonly listeners: {\n change: Set<(p: ChangePayload) => void>;\n selection: Set<(p: SelectionPayload) => void>;\n keydown: Set<(p: KeyDownPayload) => void>;\n \"track-changes-change\": Set<(p: TrackChangesState) => void>;\n } = {\n change: new Set(),\n selection: new Set(),\n keydown: new Set(),\n \"track-changes-change\": new Set(),\n };\n /**\n * Authoring mode for revisions. Off by default — mutations apply\n * plainly. On, `insertRun` / `deleteRange` route through\n * `applyTrackChangesToInsert` / `applyTrackChangesToDelete`. See the\n * `TrackChangesState` type docblock for the full semantics.\n */\n private trackChanges: TrackChangesState = { enabled: false };\n /**\n * One-shot warning set for tracked-mode beforeinput types we don't\n * (yet) route through the API — paragraph splits, paste, IME, etc.\n * We let the browser handle them untracked rather than swallowing\n * the keystroke, but log the inputType the first time we see it so\n * the gap is visible during development.\n */\n private trackedInputWarned = new Set<string>();\n /**\n * Active IME composition state (`compositionstart` → `compositionend`).\n * `null` outside composition or in non-tracked mode.\n *\n * Snapshot-then-restore is the only practical way to track IME-typed\n * text: we can't intercept `beforeinput` during composition without\n * breaking input methods on most platforms, so we let the browser\n * mutate the DOM natively during composition, then on `compositionend`\n * we roll back to the pre-composition AST and re-insert the final\n * composed string (`event.data`) through `insertRun` — which stamps\n * the `revision: ins` marker per `TrackChangesState`.\n */\n private composition: {\n /** AST snapshot taken at `compositionstart`. */\n snapshot: SobreeDocument;\n /** Caret position at `compositionstart`, used as the insertion point. */\n caret: InlinePosition | null;\n } | null = null;\n private compositionStartListener: ((e: CompositionEvent) => void) | null = null;\n private compositionEndListener: ((e: CompositionEvent) => void) | null = null;\n /** Document-level `selectionchange` listener. Funnels every cursor\n * movement (typing, click, arrows, programmatic restore) into the\n * `selection` event so plugins don't each register their own. */\n private selectionChangeListener: (() => void) | null = null;\n /** Host-level `keydown` listener. Funnels every key press into the\n * `keydown` event for plugins (mark shortcuts, navigation, etc.). */\n private keydownListener: ((e: KeyboardEvent) => void) | null = null;\n /** Cached last-seen per-block JSON strings, for diff-based version bumps. */\n private lastSerialisedBlocks: string[] = [];\n /** Tracks `@font-face` registrations for the document's embedded fonts. */\n private readonly fontFaces = new FontFaceRegistry();\n /**\n * Undo / redo orchestrator. Snapshots the document at every commit\n * + at the start of each typing session (coalesced). Memory-efficient:\n * snapshots store doc references, not deep copies — the immutable-block\n * model means consecutive snapshots share most blocks. Public API is\n * `editor.history.undo() / redo() / clear() / depth() / on(...)`.\n */\n readonly history: History;\n /**\n * True when DOM mutations since the last sync were user-driven (typing,\n * paste, drag-drop image). False right after we render from AST — the\n * DOM is then a projection of `this.doc`, and reading it back can't\n * tell us anything the AST doesn't already know, while losing any\n * fidelity the serializer drops (column widths, vAlign, …). `getDocument`\n * and `emitChangeNow` sync only when this flag is set.\n */\n private domDirty = false;\n\n constructor(host: HTMLElement, options: EditorOptions = {}) {\n this.host = host;\n this.debounceMs = options.changeDebounceMs ?? 200;\n this.getContentHosts = options.contentHosts ?? (() => [host]);\n if (options.trackChanges) {\n // Seed silently — no listeners can exist yet, so no event needed.\n this.trackChanges = { ...options.trackChanges };\n }\n\n // Y.Doc backing — either user-provided (for providers / shared docs)\n // or freshly created. The BlockRegistry's id prefix incorporates the\n // Y.Doc's clientID so two peers don't both mint `b5` for different\n // blocks (Phase 1b: collision-safe across peers).\n this.ydoc = options.ydoc ?? new Y.Doc();\n this.registry = new BlockRegistry({\n idPrefix: `${this.ydoc.clientID.toString(36)}_`,\n });\n\n // Optional content-hashed blob layer. The cache's `onResolved`\n // callback patches `this.doc.rawParts` and fires `change` so the\n // renderer picks up a freshly-arrived blob without an explicit\n // refresh from the embedder.\n this.blobStore = options.blobStore ?? null;\n this.blobCache = this.blobStore\n ? new BlobCache({\n store: this.blobStore,\n onResolved: (hash) => this.onBlobResolved(hash),\n })\n : null;\n\n // Two construction paths:\n //\n // A. The provided Y.Doc is empty (or none was provided): seed it\n // from `initialDocument` (or the empty doc). This is the v0.1\n // default — solo embedder, no provider yet.\n //\n // B. The provided Y.Doc has body content already: adopt its\n // state instead of seeding. This is the \"peer joined an\n // active room\" scenario — Phase 2's providers populate the\n // Y.Doc *before* the Editor is constructed, and we trust\n // whatever's there.\n const ydocBody = this.ydoc.getArray<Y.Map<unknown>>(\"body\");\n if (ydocBody.length === 0) {\n // Path A: seed.\n this.doc = options.initialDocument ?? emptyDocument();\n this.registry.reset(this.doc.body.length);\n this.lastSerialisedBlocks = this.doc.body.map((b) => JSON.stringify(b));\n seedYDoc(this.ydoc, this.doc, this.allBlockIds());\n this.lastPartRefs = {};\n } else {\n // Path B: adopt.\n const projected = projectYDoc(this.ydoc);\n this.doc = projected.doc;\n this.registry.adoptIds(projected.ids);\n this.lastSerialisedBlocks = this.doc.body.map((b) => JSON.stringify(b));\n this.lastPartRefs = projected.partRefs;\n this.resolveCachedPartRefsInto(this.doc);\n }\n\n this.selection = new EditorSelection(this);\n this.table = new EditorTable(this);\n this.commands = new EditorCommands();\n\n // History orchestrator — Phase 1b.6: backed by Y.UndoManager. The\n // UndoManager observes the body / meta / parts top-level Y types\n // and tracks operations whose origin matches `localOrigin`. Local\n // edits (mirrored by `mirrorToYDoc()` with origin \"local\") create\n // stack items; remote-provider edits (any other origin) don't —\n // so `Cmd+Z` reverses only this peer's own edits.\n //\n // Selection is captured per stack-item-added (the post-edit\n // selection — the pre-edit selection capture is handled by the\n // beforeInput listener stashing) and restored on stack-item-popped\n // after the Y observer has re-projected and re-rendered.\n this.history = new History({\n ydoc: this.ydoc,\n localOrigin: \"local\",\n captureSelection: () => this.selection.get(),\n restoreSelection: (sel) => {\n if (sel) applySelectionToDom(this._hosts(), this.registry, sel);\n },\n });\n\n // `history.undo` / `history.redo` registered on the command bus\n // here (NOT in the keyboard plugin) so a headless caller — agent,\n // toolbar without keyboard mounted, MCP — can dispatch undo/redo\n // through the same surface as Cmd+Z. The keyboard plugin only maps\n // the keystroke → execute call.\n this.commands.register({\n name: \"history.undo\",\n title: \"Undo\",\n run: () => {\n this.history.undo();\n },\n isActive: () => false,\n isAvailable: () => this.history.canUndo(),\n });\n this.commands.register({\n name: \"history.redo\",\n title: \"Redo\",\n run: () => {\n this.history.redo();\n },\n isActive: () => false,\n isAvailable: () => this.history.canRedo(),\n });\n\n // Mark toggles — same rationale as history commands. Registered in\n // core (not in the keyboard plugin) so disabling keyboard doesn't\n // wipe the toolbar's bold / italic / etc. dispatch path.\n for (const { name, title, tag } of MARK_COMMAND_DEFS) {\n this.commands.register({\n name,\n title,\n run: () => {\n const range = rangeAtSelection(this);\n if (range) toggleMark(this, range, tag);\n },\n isActive: () => {\n const range = rangeAtSelection(this);\n return !!range && isMarkActive(this, range, tag);\n },\n isAvailable: () => this.getBlocks().length > 0,\n });\n }\n\n host.classList.add(\"sobree-editor\");\n host.contentEditable = \"true\";\n host.setAttribute(\"role\", \"textbox\");\n host.setAttribute(\"aria-multiline\", \"true\");\n host.spellcheck = true;\n\n const firstHost = this.getContentHosts()[0] ?? host;\n this.fontFaces.sync(this.doc.fonts, this.doc.rawParts);\n renderSobreeDocument(this.doc, firstHost, this.blockIdsArray());\n\n this.inputListener = () => {\n // Mark DOM dirty + schedule a change; mirroring into the Y.Doc\n // happens inside the debounced sync path. Y.UndoManager\n // observes the resulting Y operations and creates stack items\n // automatically (coalesced via `captureTimeout` — typing within\n // ~1s merges into one undo step).\n this.domDirty = true;\n this.scheduleChange();\n };\n host.addEventListener(\"input\", this.inputListener);\n // beforeinput is the only place we get the chance to intercept\n // the browser's native contentEditable undo/redo\n // (`historyUndo` / `historyRedo`) and route them through our own\n // history layer — otherwise the browser would mutate the DOM in a\n // way that desyncs from the Y.Doc.\n this.beforeInputListener = (e) => {\n const ie = e as InputEvent;\n if (ie.inputType === \"historyUndo\") {\n e.preventDefault();\n this.history.undo();\n return;\n }\n if (ie.inputType === \"historyRedo\") {\n e.preventDefault();\n this.history.redo();\n return;\n }\n // Track-changes authoring path: in tracked mode, the handful of\n // edits we know how to convert into `insertRun` / `deleteRange`\n // get routed through the typed API so the resulting runs carry\n // revision markers. Unhandled inputTypes (Enter, paste, IME, …)\n // fall through to the browser's native contenteditable behaviour\n // — untracked, with a one-shot console warning so the dev sees\n // the gap. See `handleTrackedInput` for the full menu.\n //\n // We *also* take over when tracked mode is OFF but the caret\n // sits inside a revision wrapper (an `<ins>`/`<del>` left over\n // from earlier tracked typing). Without this, the browser's\n // contenteditable insert path puts the new character INSIDE the\n // wrapper, the post-input DOM read sees text inside `<ins>`,\n // and `syncFromDom` stamps the new run as `revision: ins` —\n // tracking edits the user explicitly opted *out* of tracking.\n // Routing through `handleTrackedInput` instead places the new\n // run as a separate AST node next to the wrapper; differing\n // `revision` properties block `mergeAdjacentTextRuns`, so the\n // new plain run stays plain.\n const inTracked = this.trackChanges.enabled;\n const inRevisionWrapper = !inTracked && this.caretInsideRevisionWrapper();\n if ((inTracked || inRevisionWrapper) && this.handleTrackedInput(ie)) {\n e.preventDefault();\n return;\n }\n // Any other inputType is a real edit; let the browser handle\n // the DOM mutation. The input listener will catch the change\n // afterwards.\n };\n host.addEventListener(\"beforeinput\", this.beforeInputListener);\n\n // IME composition — see `this.composition`'s docblock. Listeners\n // are unconditional but the work only happens when tracked mode\n // was on at `compositionstart`.\n this.compositionStartListener = (e) => this.handleCompositionStart(e);\n this.compositionEndListener = (e) => this.handleCompositionEnd(e);\n host.addEventListener(\"compositionstart\", this.compositionStartListener);\n host.addEventListener(\"compositionend\", this.compositionEndListener);\n\n // One global `selectionchange` listener funnels all cursor movement\n // into the editor's `selection` event. Plugins subscribe to the\n // editor instead of fighting over the document-level event.\n this.selectionChangeListener = () => this.fireSelection();\n document.addEventListener(\"selectionchange\", this.selectionChangeListener);\n\n // Host-scoped `keydown` listener — fires the editor's `keydown`\n // event for plugins. The editor binds no shortcuts itself.\n this.keydownListener = (e) => this.fireKeyDown(e);\n host.addEventListener(\"keydown\", this.keydownListener);\n\n this.pasteListener = (e) => this.onPaste(e);\n host.addEventListener(\"paste\", this.pasteListener);\n\n this.dragOverListener = (e) => this.onDragOver(e);\n host.addEventListener(\"dragover\", this.dragOverListener);\n\n this.dropListener = (e) => this.onDrop(e);\n host.addEventListener(\"drop\", this.dropListener);\n\n this.detachImageResize = attachImageResize(host);\n\n // Subscribe to *remote*-origin Y.Doc updates — i.e., updates that\n // came in from a provider (Phase 2: WebSocket peer, IndexedDB\n // restore, WebRTC, …) rather than from this Editor's own\n // mutations. Local mutations carry origin `\"local\"` (set by\n // `mirrorToYDoc()`) and the seed pass carries `\"seed\"`; we skip\n // both since the AST is already current. Anything else is remote\n // and we re-project + re-render.\n this.ydocUpdateListener = (tr: Y.Transaction) => {\n if (tr.origin === \"local\" || tr.origin === \"seed\") return;\n this.adoptYDocState();\n };\n this.ydoc.on(\"afterTransaction\", this.ydocUpdateListener);\n }\n\n /**\n * Re-project the Y.Doc into `this.doc`, sync the BlockRegistry to\n * the projected ids, re-render the DOM, and fire `change`. Called\n * when a remote provider applies an update we didn't initiate.\n */\n private adoptYDocState(): void {\n const projected = projectYDoc(this.ydoc);\n this.doc = projected.doc;\n this.registry.adoptIds(projected.ids);\n this.lastSerialisedBlocks = this.doc.body.map((b) => JSON.stringify(b));\n this.lastPartRefs = projected.partRefs;\n // Resolve hash-addressed parts through the local cache. Hashes\n // not yet cached: kick off background fetches; `onBlobResolved`\n // patches + re-renders when they land.\n this.resolveCachedPartRefsInto(this.doc);\n if (this.blobCache) {\n const missing = Object.values(projected.partRefs).filter(\n (h) => !this.blobCache!.has(h),\n );\n if (missing.length > 0) {\n void this.blobCache.ensureLoaded(missing);\n }\n }\n const hosts = this.getContentHosts();\n for (const h of hosts) h.replaceChildren();\n const firstHost = hosts[0] ?? this.host;\n this.fontFaces.sync(this.doc.fonts, this.doc.rawParts);\n renderSobreeDocument(this.doc, firstHost, this.blockIdsArray());\n this.domDirty = false;\n this.emitChangeNow();\n }\n\n /**\n * Resolve any `partRefs` hashes that are currently cached into the\n * provided document's `rawParts`. In-place mutation — the document\n * is owned by the caller. Missing hashes stay out of `rawParts`;\n * the renderer handles missing parts gracefully (placeholder).\n */\n private resolveCachedPartRefsInto(doc: SobreeDocument): void {\n if (!this.blobCache) return;\n for (const [path, hash] of Object.entries(this.lastPartRefs)) {\n if (doc.rawParts[path]) continue; // inline-parts entry wins if both present\n const bytes = this.blobCache.get(hash);\n if (bytes) doc.rawParts[path] = bytes;\n }\n }\n\n /**\n * Callback fired by the BlobCache when a background fetch lands.\n * Walks `lastPartRefs` to find which paths reference this hash,\n * patches `this.doc.rawParts`, and re-renders so the user sees\n * the part appear.\n */\n private onBlobResolved(hash: string): void {\n if (!this.blobCache) return;\n let touched = false;\n for (const [path, refHash] of Object.entries(this.lastPartRefs)) {\n if (refHash !== hash) continue;\n const bytes = this.blobCache.get(hash);\n if (bytes && !this.doc.rawParts[path]) {\n this.doc.rawParts[path] = bytes;\n touched = true;\n }\n }\n if (!touched) return;\n // Re-render so the renderer picks up the freshly-resolved part.\n // This is a full re-render; future Phase 3.2.x can scope it to\n // just the affected images / fonts.\n const hosts = this.getContentHosts();\n for (const h of hosts) h.replaceChildren();\n const firstHost = hosts[0] ?? this.host;\n this.fontFaces.sync(this.doc.fonts, this.doc.rawParts);\n renderSobreeDocument(this.doc, firstHost, this.blockIdsArray());\n this.emitChangeNow();\n }\n\n /**\n * Wait for every currently-referenced binary part to be available\n * in the local cache. Useful before `toDocx()` so the exported\n * file contains all images / fonts.\n *\n * Returns a resolved Promise immediately when no `blobStore` is\n * configured (today's default — bytes are always inline).\n */\n async ensurePartsLoaded(): Promise<void> {\n if (!this.blobCache) return;\n const hashes = Object.values(this.lastPartRefs);\n if (hashes.length === 0) return;\n await this.blobCache.ensureLoaded(hashes);\n // After fetches land, re-resolve into `this.doc`.\n this.resolveCachedPartRefsInto(this.doc);\n }\n\n // === primary document I/O ===\n\n /**\n * Current document as SobreeDocument.\n *\n * Syncs from the DOM only if the user typed since the last render. If\n * the latest change came from an API call, returns the in-memory AST\n * verbatim — the DOM is a projection of it and reading back would\n * throw away properties the renderer doesn't surface.\n */\n getDocument(): SobreeDocument {\n return this.ensureCurrent();\n }\n\n /** Replace the document. Fires `change` synchronously. */\n setDocument(doc: SobreeDocument): void {\n // Phase 1b.6: Y.UndoManager auto-tracks the resulting Y operations\n // (via origin \"local\" applied by `mirrorToYDoc`), so no explicit\n // history recording is needed here.\n this.applyDocument(doc);\n }\n\n /**\n * Internal apply path shared by `setDocument` and any other\n * full-replace caller. The Y.Doc mirror produces tracked Y\n * operations that Y.UndoManager turns into a single stack item.\n */\n private applyDocument(doc: SobreeDocument): void {\n this.doc = doc;\n this.registry.reset(doc.body.length);\n this.lastSerialisedBlocks = doc.body.map((b) => JSON.stringify(b));\n const hosts = this.getContentHosts();\n for (const h of hosts) h.replaceChildren();\n const firstHost = hosts[0] ?? this.host;\n // Sync `@font-face` registrations BEFORE rendering so newly-embedded\n // fonts are already available to the render pass.\n this.fontFaces.sync(this.doc.fonts, this.doc.rawParts);\n renderSobreeDocument(this.doc, firstHost, this.blockIdsArray());\n this.domDirty = false;\n this.mirrorToYDoc();\n this.emitChangeNow();\n }\n\n /**\n * Drop entries from `rawParts` that nothing in the AST references.\n * Useful after deleting images (or font embeds — Phase 3) to keep the\n * in-memory doc lean. Idempotent; reports the keys that were removed.\n *\n * Not auto-invoked — `exportDocx` already filters at packaging time,\n * so callers only need this when they're keeping the doc around\n * in-memory across many edits.\n */\n pruneUnusedParts(): { kept: number; pruned: string[] } {\n const { doc, kept, pruned } = pruneOrphanParts(this.doc);\n if (pruned.length === 0) return { kept, pruned };\n this.doc = doc;\n this.mirrorToYDoc();\n return { kept, pruned };\n }\n\n /**\n * Embed a TTF/OTF font into the document. Thin wrapper around\n * `embedFontIntoDoc()` from the fonts module — handles the\n * setDocument round so the renderer + `@font-face` registry pick\n * up the new face automatically.\n *\n * Refuses (with a warning) when the font's OS/2 `fsType` field\n * marks it as restricted, unless `opts.allowRestricted` is true.\n * Pass any subset of {regular, bold, italic, boldItalic}; missing\n * faces are simply not embedded.\n */\n embedFont(\n name: string,\n faces: EmbedFontFaces,\n opts: EmbedFontOptions = {},\n ): { warnings: string[] } {\n const before = this.doc.rawParts;\n const result = embedFontIntoDoc(this.doc, name, faces, opts);\n if (result.next !== this.doc) {\n // Diff which part paths the font module just added — they're\n // candidates for migration to the BlobStore (Phase 3.2+).\n const addedPartPaths: Array<{ path: string; bytes: Uint8Array }> = [];\n if (this.blobStore && this.blobCache) {\n for (const [path, bytes] of Object.entries(result.next.rawParts)) {\n if (!before[path]) addedPartPaths.push({ path, bytes });\n }\n for (const { path } of addedPartPaths) {\n this.pendingPartRefMigrations.add(path);\n }\n }\n this.setDocument(result.next);\n // Fire migrations AFTER setDocument so the partPath is\n // already in `lastPartRefs`-adjacent state. Errors are logged\n // inside migratePartToBlobStore.\n for (const { path, bytes } of addedPartPaths) {\n void this.migratePartToBlobStore(path, bytes);\n }\n }\n return { warnings: result.warnings };\n }\n\n /**\n * Drop a font declaration by name. The associated font parts in\n * `rawParts` aren't immediately removed — call `pruneUnusedParts()`\n * (or just export) to GC them.\n */\n removeEmbeddedFont(name: string): void {\n const next = removeFontFromDoc(this.doc, name);\n if (next !== this.doc) this.setDocument(next);\n }\n\n /** Monotonic counter bumped on each `change` event. */\n getRevision(): number {\n return this.revision;\n }\n\n /** Monotonic document-wide version (bumps on any mutation). */\n getDocumentVersion(): number {\n return this.registry.documentVersion();\n }\n\n /** Render current document to an HTML string. */\n toHtml(): string {\n const scratch = document.createElement(\"div\");\n renderSobreeDocument(this.syncFromDom(), scratch);\n return scratch.innerHTML;\n }\n\n // === block-level queries ===\n\n getBlocks(): BlockInfo[] {\n const doc = this.ensureCurrent();\n return doc.body.map((block, index) => this.summariseBlock(block, index));\n }\n\n getBlock(index: number): BlockInfo {\n const blocks = this.getBlocks();\n const b = blocks[index];\n if (!b) throw new Error(`block index ${index} out of range`);\n return b;\n }\n\n /** Same summary, looked up by stable id. Returns `null` if unknown. */\n getBlockById(id: string): BlockInfo | null {\n const index = this.registry.indexOf(id);\n if (index < 0) return null;\n return this.getBlock(index);\n }\n\n getOutline(): OutlineItem[] {\n const doc = this.ensureCurrent();\n const out: OutlineItem[] = [];\n doc.body.forEach((block, index) => {\n if (block.kind !== \"paragraph\") return;\n const level = headingLevelOf(block);\n if (!level) return;\n out.push({\n level,\n text: runsToText(block.runs),\n blockIndex: index,\n block: this.registry.refAt(index),\n });\n });\n return out;\n }\n\n // === core mutations (BlockRef / Position / Range in; EditResult out) ===\n\n /** Replace the block at `target`'s index with `block`. */\n replaceBlock(target: BlockRef, block: Block): EditResult<BlockRef> {\n this.ensureCurrent();\n const lockCheck = this.checkRefs([target]);\n if (lockCheck) return lockCheck;\n const index = this.registry.indexOf(target.id);\n const next = this.doc.body.slice();\n const wasSectionBreak = next[index]?.kind === \"section_break\";\n next[index] = block;\n // If a SectionBreak was the previous block here and the replacement\n // isn't one, the two sections it separated must merge — there's\n // nothing left to delimit them. The earlier section's properties\n // survive.\n const update: Partial<SobreeDocument> = { body: next };\n if (wasSectionBreak && block.kind !== \"section_break\") {\n update.sections = mergeSectionsAcross(this.doc.sections, removedSectionIndex(this.doc.body, index));\n }\n return this.commit(update, [{ type: \"bump\", index }]);\n }\n\n /**\n * Insert `block` before the target block. Returns the new ref.\n *\n * In track-changes mode (see `setTrackChanges`), if `block` is a\n * paragraph it gets stamped with `revision: { type: \"ins\", author }`\n * on its properties — the same paragraph-mark semantics as\n * `splitBlock`. Non-paragraph blocks (table, section_break) don't\n * carry the marker in v1 and insert plain.\n */\n insertBlockBefore(target: BlockRef, block: Block): EditResult<BlockRef> {\n this.ensureCurrent();\n const lockCheck = this.checkRefs([target]);\n if (lockCheck) return lockCheck;\n const index = this.registry.indexOf(target.id);\n const stamped = this.stampInsertedBlockIfTracked(block);\n const next = this.doc.body.slice();\n next.splice(index, 0, stamped);\n return this.commit({ body: next }, [{ type: \"insert\", index }]);\n }\n\n /**\n * Insert `block` after the target block. Returns the new ref.\n * Tracked-mode behaviour matches `insertBlockBefore`.\n */\n insertBlockAfter(target: BlockRef, block: Block): EditResult<BlockRef> {\n this.ensureCurrent();\n const lockCheck = this.checkRefs([target]);\n if (lockCheck) return lockCheck;\n const index = this.registry.indexOf(target.id);\n const stamped = this.stampInsertedBlockIfTracked(block);\n const next = this.doc.body.slice();\n next.splice(index + 1, 0, stamped);\n return this.commit({ body: next }, [{ type: \"insert\", index: index + 1 }]);\n }\n\n /**\n * Delete the target block.\n *\n * In track-changes mode, paragraph blocks aren't removed — their\n * `properties.revision` is stamped `del` (the renderer shows the\n * paragraph mark with a strikethrough ¶ glyph; the body text stays\n * visible). If the paragraph carries the *current author's* pending\n * `ins` marker (a paragraph the user themselves just created), the\n * block is removed outright — cancelling an un-committed insert,\n * matching the inline `deleteRange` semantics. Non-paragraph blocks\n * (tables, section breaks) bypass tracking in v1 — they remove plainly.\n */\n deleteBlock(target: BlockRef): EditResult<void> {\n this.ensureCurrent();\n const lockCheck = this.checkRefs([target]);\n if (lockCheck) return lockCheck;\n const index = this.registry.indexOf(target.id);\n const current = this.doc.body[index];\n\n if (this.trackChanges.enabled && current?.kind === \"paragraph\") {\n const existing = current.properties.revision;\n // Cancelling own pending ins → actually remove.\n if (\n existing?.type === \"ins\" &&\n existing.author === this.trackChanges.author\n ) {\n // fall through to plain remove below\n } else {\n const revision: RevisionMark =\n this.trackChanges.author === undefined\n ? { type: \"del\" }\n : { type: \"del\", author: this.trackChanges.author };\n const next = this.doc.body.slice();\n next[index] = {\n ...current,\n properties: { ...current.properties, revision },\n };\n return this.commit({ body: next }, [{ type: \"bump\", index }]);\n }\n }\n\n const wasSectionBreak = current?.kind === \"section_break\";\n const next = this.doc.body.slice();\n next.splice(index, 1);\n if (next.length === 0) next.push({ kind: \"paragraph\", properties: {}, runs: [] });\n const update: Partial<SobreeDocument> = { body: next };\n if (wasSectionBreak) {\n update.sections = mergeSectionsAcross(this.doc.sections, removedSectionIndex(this.doc.body, index));\n }\n return this.commit(update, [{ type: \"remove\", index }]);\n }\n\n /**\n * Stamp `revision: ins` on a paragraph block if tracked mode is on\n * and the block doesn't already carry one. Helper for\n * `insertBlockBefore` / `insertBlockAfter`. Non-paragraph blocks\n * pass through unchanged.\n */\n private stampInsertedBlockIfTracked(block: Block): Block {\n if (!this.trackChanges.enabled) return block;\n if (block.kind !== \"paragraph\") return block;\n if (block.properties.revision) return block;\n const revision: RevisionMark =\n this.trackChanges.author === undefined\n ? { type: \"ins\" }\n : { type: \"ins\", author: this.trackChanges.author };\n return { ...block, properties: { ...block.properties, revision } };\n }\n\n /** Merge a patch into each target paragraph's properties. */\n applyBlockProperties(\n targets: BlockRef[],\n patch: ParagraphPropertiesPatch,\n ): EditResult<void> {\n this.ensureCurrent();\n const lockCheck = this.checkRefs(targets);\n if (lockCheck) return lockCheck;\n const next = this.doc.body.slice();\n const bumps: Mutation[] = [];\n for (const ref of targets) {\n const index = this.registry.indexOf(ref.id);\n const block = next[index];\n if (!block) continue;\n if (block.kind !== \"paragraph\") {\n return fail({\n code: \"invalid-state\",\n details: `block ${ref.id} is not a paragraph`,\n });\n }\n next[index] = { ...block, properties: mergeParagraphProps(block.properties, patch) };\n bumps.push({ type: \"bump\", index });\n }\n return this.commit({ body: next }, bumps);\n }\n\n /**\n * Apply run-level properties across `range`.\n *\n * In track-changes mode (see `setTrackChanges`), each text run in\n * `range` that doesn't already carry a `revisionFormat` snapshot\n * gets one — capturing the run's properties *before* the patch is\n * applied. Repeated tracked edits don't overwrite the snapshot, so\n * a reject always returns the run to its pre-tracking state.\n * Non-text runs and runs with no concrete properties pass through\n * unchanged.\n */\n applyRunProperties(\n range: ApiRange,\n patch: RunPropertiesPatch,\n opts: { expect?: Record<string, number> } = {},\n ): EditResult<void> {\n this.ensureCurrent();\n const lockCheck = this.checkRange(range, opts.expect);\n if (lockCheck) return lockCheck;\n if (this.trackChanges.enabled) {\n const author = this.trackChanges.author;\n return this.mutateRunsInRange(range, (runs) => {\n const snapshotted = runs.map((r) => snapshotFormatRevision(r, author));\n return applyRunPropertiesToRuns(snapshotted, patch);\n });\n }\n return this.mutateRunsInRange(range, (runs) => applyRunPropertiesToRuns(runs, patch));\n }\n\n /** Wrap the runs in `range` with semantic formatting. */\n wrapRange(\n range: ApiRange,\n tag: WrapTag,\n opts: { expect?: Record<string, number> } = {},\n ): EditResult<void> {\n return this.applyRunProperties(range, wrapTagToPatch(tag), opts);\n }\n\n /**\n * Insert a run at `at`. Splits the run list at the offset.\n *\n * In track-changes mode (see `setTrackChanges`), the run is stamped\n * with `revision: { type: \"ins\", author }` before insertion (unless\n * it already carries a `revision` — caller-provided revisions are\n * never overwritten).\n */\n insertRun(at: InlinePosition, run: InlineRun): EditResult<BlockRef> {\n this.ensureCurrent();\n const lockCheck = this.checkRefs([at.block]);\n if (lockCheck) return lockCheck;\n const index = this.registry.indexOf(at.block.id);\n const block = this.doc.body[index];\n if (!block || block.kind !== \"paragraph\") {\n return fail({ code: \"invalid-position\", details: \"target is not a paragraph\" });\n }\n const stamped = this.trackChanges.enabled\n ? stampInsertRevision(run, this.trackChanges.author)\n : run;\n const { before, after } = splitRunsAt(block.runs, at.offset);\n const merged = mergeAdjacentTextRuns([...before, stamped, ...after]);\n const next = this.doc.body.slice();\n next[index] = { ...block, runs: merged };\n return this.commit({ body: next }, [{ type: \"bump\", index }]);\n }\n\n /**\n * Split a paragraph at `at`. The runs before the offset stay on the\n * original block; the runs after move into a fresh paragraph that's\n * inserted immediately after. The new block inherits the original\n * paragraph's properties (alignment, style, indent, …) so the visual\n * shape of the split is what the user expects from pressing Enter.\n *\n * In track-changes mode (see `setTrackChanges`), the new paragraph's\n * `properties.revision` is stamped `{ type: \"ins\", author }` — the\n * \"this paragraph break is a tracked insert\" marker. The original\n * paragraph is left clean; only the *new* paragraph carries the mark,\n * mirroring how Word stores `<w:rPr><w:ins/></w:rPr>` inside `<w:pPr>`.\n *\n * `at.offset` is clamped to `[0, block-length]`. A split at offset 0\n * inserts an empty paragraph *before* the cursor; a split at the\n * block's full length inserts an empty paragraph *after*.\n *\n * Returns the ref of the *new* (second) block — callers typically\n * place the caret at its offset 0.\n */\n splitBlock(at: InlinePosition): EditResult<BlockRef> {\n this.ensureCurrent();\n const lockCheck = this.checkRefs([at.block]);\n if (lockCheck) return lockCheck;\n const index = this.registry.indexOf(at.block.id);\n const block = this.doc.body[index];\n if (!block || block.kind !== \"paragraph\") {\n return fail({ code: \"invalid-position\", details: \"target is not a paragraph\" });\n }\n const { before, after } = splitRunsAt(block.runs, at.offset);\n const firstHalf: Paragraph = { ...block, runs: mergeAdjacentTextRuns(before) };\n // Build the new paragraph's properties. Inherit everything from the\n // source; in tracked mode, stamp `revision: ins` on top.\n const newProps: ParagraphProperties = this.trackChanges.enabled\n ? {\n ...block.properties,\n revision:\n this.trackChanges.author === undefined\n ? { type: \"ins\" }\n : { type: \"ins\", author: this.trackChanges.author },\n }\n : { ...block.properties };\n const secondHalf: Paragraph = {\n kind: \"paragraph\",\n properties: newProps,\n runs: mergeAdjacentTextRuns(after),\n };\n const next = this.doc.body.slice();\n next.splice(index, 1, firstHalf, secondHalf);\n const result = this.commit({ body: next }, [\n { type: \"bump\", index },\n { type: \"insert\", index: index + 1 },\n ]);\n if (!result.ok) return result;\n // `affected` is `[bumped first half, inserted second half]` in\n // mutation order. Surfacing the new block's ref as `value` lets\n // callers (notably `handleTrackedInput`'s caret placement) consume\n // it without a follow-up `getBlock(index + 1)` round-trip.\n const newRef = result.affected[1] ?? result.affected[0]!;\n return { ok: true, value: newRef, affected: result.affected };\n }\n\n /**\n * Insert an image at `at`. The bytes are stored in `doc.rawParts` under\n * a fresh `word/media/imageN.{ext}` path; a `DrawingRun` referencing\n * that path is inserted at the position.\n *\n * When a `blobStore` is configured (Phase 3.2+), the bytes also\n * migrate in the background: hashed, uploaded to the store, and a\n * `partRefs[partPath] = hash` entry written to the Y.Doc. Once the\n * migration lands, the Y.Doc's inline `parts[partPath]` is cleared —\n * so the bytes ride the side-channel, not the Y update stream.\n * The local renderer keeps reading `doc.rawParts[partPath]`\n * throughout (the value is stable from the moment `insertImage`\n * returns).\n */\n insertImage(\n at: InlinePosition,\n bytes: Uint8Array,\n opts: { mime: string; widthPx?: number; heightPx?: number; altText?: string },\n ): EditResult<BlockRef> {\n this.ensureCurrent();\n const ext = mimeToExtension(opts.mime);\n const partPath = allocateMediaPath(this.doc, ext);\n this.doc.rawParts[partPath] = bytes;\n // Mark for migration BEFORE the insertRun→commit→mirror chain\n // runs, so the mirror's skip-set catches this path on its first\n // pass and doesn't write inline bytes to Y.Doc.\n if (this.blobStore && this.blobCache) {\n this.pendingPartRefMigrations.add(partPath);\n // Fire-and-forget background migration. Errors are logged inside.\n void this.migratePartToBlobStore(partPath, bytes);\n }\n const widthPx = opts.widthPx ?? 200;\n const heightPx = opts.heightPx ?? 150;\n const drawing: DrawingRun = {\n kind: \"drawing\",\n partPath,\n widthEmu: pxToEmu(widthPx),\n heightEmu: pxToEmu(heightPx),\n placement: \"inline\",\n };\n if (opts.altText) drawing.altText = opts.altText;\n return this.insertRun(at, drawing);\n }\n\n /**\n * Delete the content inside `range`. Supports both single-block and\n * cross-paragraph ranges.\n *\n * In track-changes mode (see `setTrackChanges`), the deletion is\n * *recorded* rather than applied:\n * - plain runs are stamped with `revision: { type: \"del\", author }`;\n * - a run already marked as the same author's pending `ins` is\n * dropped (the author cancelling their own un-committed insert\n * — no audit trail because it was never committed);\n * - runs carrying a peer's revision (any author other than the\n * current one) are left untouched: the API user should resolve\n * those via `acceptRevision` / `rejectRevision` first.\n *\n * **Cross-paragraph behaviour**: in tracked mode every paragraph\n * *after* the first one in the range gets its paragraph-mark\n * stamped `del` too, so `acceptAllRevisions` later collapses the\n * range into a single paragraph. In non-tracked mode the\n * intermediate paragraphs are removed outright and the first +\n * last blocks merge into one.\n */\n deleteRange(\n range: ApiRange,\n opts: { expect?: Record<string, number> } = {},\n ): EditResult<void> {\n this.ensureCurrent();\n const lockCheck = this.checkRange(range, opts.expect);\n if (lockCheck) return lockCheck;\n if (range.from.block.id !== range.to.block.id) {\n return this.trackChanges.enabled\n ? this.deleteRangeAcrossBlocksTracked(range)\n : this.deleteRangeAcrossBlocksPlain(range);\n }\n if (this.trackChanges.enabled) {\n const author = this.trackChanges.author;\n return this.mutateRunsInRange(range, (runs) =>\n runs.flatMap((r) => stampDeleteRevision(r, author)),\n );\n }\n return this.mutateRunsInRange(range, () => []);\n }\n\n /**\n * Tracked cross-paragraph delete. Walks each paragraph in the range:\n * stamps `del` on the affected runs (first-block tail / intermediate\n * full / last-block head), and on every paragraph *after the first*\n * stamps the paragraph-mark `del` so `acceptAllRevisions` later\n * merges them all into the first block. Single commit.\n */\n private deleteRangeAcrossBlocksTracked(range: ApiRange): EditResult<void> {\n const fromIdx = this.registry.indexOf(range.from.block.id);\n const toIdx = this.registry.indexOf(range.to.block.id);\n if (fromIdx < 0 || toIdx < 0 || fromIdx > toIdx) {\n return fail({ code: \"range-out-of-order\", details: \"range endpoints\" });\n }\n const author = this.trackChanges.author;\n const nextBody = this.doc.body.slice();\n const bumps: Mutation[] = [];\n\n for (let i = fromIdx; i <= toIdx; i++) {\n const block = nextBody[i];\n if (!block || block.kind !== \"paragraph\") continue;\n\n // Apply the del-stamp to the right slice of this block's runs.\n let newRuns: InlineRun[];\n if (i === fromIdx) {\n const split = splitRunsAt(block.runs, range.from.offset);\n const tailStamped = split.after.flatMap((r) =>\n stampDeleteRevision(r, author),\n );\n newRuns = mergeAdjacentTextRuns([...split.before, ...tailStamped]);\n } else if (i === toIdx) {\n const split = splitRunsAt(block.runs, range.to.offset);\n const headStamped = split.before.flatMap((r) =>\n stampDeleteRevision(r, author),\n );\n newRuns = mergeAdjacentTextRuns([...headStamped, ...split.after]);\n } else {\n newRuns = mergeAdjacentTextRuns(\n block.runs.flatMap((r) => stampDeleteRevision(r, author)),\n );\n }\n\n let nextBlock: Paragraph = { ...block, runs: newRuns };\n\n // Stamp paragraph-mark del on every block AFTER the first — the\n // break between i-1 and i is part of the deletion. Skip if a\n // revision is already present: we don't overwrite peer markers,\n // and own-ins-cancel + del-on-top-of-del don't make sense in\n // a bulk path.\n if (i > fromIdx && !block.properties.revision) {\n const revision: RevisionMark =\n author === undefined ? { type: \"del\" } : { type: \"del\", author };\n nextBlock = {\n ...nextBlock,\n properties: { ...nextBlock.properties, revision },\n };\n }\n\n nextBody[i] = nextBlock;\n bumps.push({ type: \"bump\", index: i });\n }\n\n return this.commit({ body: nextBody }, bumps);\n }\n\n /**\n * Non-tracked cross-paragraph delete. Keeps the head of the first\n * block + the tail of the last block, splices them into the first\n * block as one paragraph, and removes everything in between.\n */\n private deleteRangeAcrossBlocksPlain(range: ApiRange): EditResult<void> {\n const fromIdx = this.registry.indexOf(range.from.block.id);\n const toIdx = this.registry.indexOf(range.to.block.id);\n if (fromIdx < 0 || toIdx < 0 || fromIdx > toIdx) {\n return fail({ code: \"range-out-of-order\", details: \"range endpoints\" });\n }\n const first = this.doc.body[fromIdx];\n const last = this.doc.body[toIdx];\n if (!first || first.kind !== \"paragraph\" || !last || last.kind !== \"paragraph\") {\n return fail({ code: \"invalid-state\", details: \"cross-block delete requires paragraph endpoints\" });\n }\n const head = splitRunsAt(first.runs, range.from.offset).before;\n const tail = splitRunsAt(last.runs, range.to.offset).after;\n const merged = mergeAdjacentTextRuns([...head, ...tail]);\n\n const nextBody = this.doc.body.slice();\n nextBody[fromIdx] = { ...first, runs: merged };\n // Remove blocks (fromIdx+1) .. toIdx — that many entries.\n nextBody.splice(fromIdx + 1, toIdx - fromIdx);\n if (nextBody.length === 0) {\n nextBody.push({ kind: \"paragraph\", properties: {}, runs: [] });\n }\n\n const mutations: Mutation[] = [{ type: \"bump\", index: fromIdx }];\n // Top-down removes so each index stays valid as we shrink the array.\n for (let i = toIdx; i > fromIdx; i--) {\n mutations.push({ type: \"remove\", index: i });\n }\n return this.commit({ body: nextBody }, mutations);\n }\n\n // === tracked changes — authoring mode ===\n\n /**\n * Read the current track-changes mode. Defaults to `{ enabled: false }`\n * — the editor mutates the document plainly. See `setTrackChanges`.\n *\n * Returns a fresh copy each call, so callers can mutate the returned\n * object without affecting editor state.\n */\n getTrackChanges(): TrackChangesState {\n return { ...this.trackChanges };\n }\n\n /**\n * Switch authoring mode. When `enabled`, subsequent `insertRun` and\n * `deleteRange` calls produce tracked revisions instead of direct\n * mutations (see `TrackChangesState`'s docblock for the full rules).\n *\n * Fires `track-changes-change` if the state actually changes\n * (idempotent — re-setting the same enabled+author is a no-op).\n * Listeners receive the new state; embedders typically forward it\n * to a toolbar pill / mode badge.\n */\n setTrackChanges(state: TrackChangesState): void {\n const same =\n this.trackChanges.enabled === state.enabled &&\n this.trackChanges.author === state.author;\n if (same) return;\n this.trackChanges = { ...state };\n for (const cb of this.listeners[\"track-changes-change\"]) {\n try {\n cb({ ...this.trackChanges });\n } catch (err) {\n console.error(\"[editor] track-changes-change listener threw:\", err);\n }\n }\n }\n\n /**\n * Route a tracked-mode `beforeinput` event through the typed API so\n * the resulting runs carry revision markers. Returns `true` if the\n * event was consumed (caller should `preventDefault`), `false` to\n * let the browser handle it natively (untracked).\n *\n * **Handled inputTypes** (the 95% case — typing and deleting text):\n *\n * - `insertText`, `insertReplacementText` — typed characters,\n * dictation / autocomplete replacements.\n * - `deleteContentBackward`, `deleteContentForward` — Backspace\n * and Delete keys, including over a selection.\n * - `deleteWordBackward`, `deleteWordForward` — Option-Backspace\n * style word deletions; the browser collapses to the right\n * selection range before firing, we just delete it.\n * - `deleteByCut` — Cmd-X. The clipboard side has already been\n * populated by the browser; we mark the source range deleted.\n *\n * **Unhandled** (return `false`, fall through, warn-once):\n *\n * - `insertParagraph` / `insertLineBreak` — block split / soft\n * break. Word tracks these as `<w:ins>` on the paragraph mark;\n * the corresponding AST mutation (split-block as a tracked op)\n * hasn't landed yet.\n * - `insertFromPaste` — paste is handled at the `paste` event level\n * in `onPaste` (not here), where tracked-mode plain-text paste is\n * already routed through `insertRun` / `splitBlock`. Rich-paste\n * (HTML) in tracked mode falls back to plain text by design.\n * - `insertCompositionText` — handled separately via\n * `compositionstart` / `compositionend` listeners\n * (`handleCompositionStart` / `handleCompositionEnd`). We let the\n * IME render natively during composition, then on end we restore\n * the pre-composition AST and re-insert the final composed string\n * through this `insertRun` path.\n *\n * Falling through means the keystroke still works (no broken UX in\n * tracked mode), it just doesn't get a revision marker. The console\n * warning makes the gap visible.\n *\n * Caret restoration: after every routed mutation we set the model\n * selection to where the caret would have ended up on the equivalent\n * direct-edit operation. Because tracked-mode `deleteRange` leaves\n * the runs in place (marked `del`), offsets don't shift — caret math\n * is straightforward.\n */\n private handleTrackedInput(ie: InputEvent): boolean {\n const sel = this.selection.get();\n if (!sel) return false;\n\n switch (ie.inputType) {\n case \"insertText\":\n case \"insertReplacementText\": {\n const text = ie.data ?? \"\";\n if (!text) return false;\n const insertAt = this.markedRangeForReplace(sel);\n if (!insertAt) return false;\n const run: InlineRun = { kind: \"text\", text, properties: {} };\n const result = this.insertRun(insertAt, run);\n if (!result.ok) return true; // consumed but failed — don't fall through\n this.placeCaret(insertAt.block.id, insertAt.offset + text.length);\n return true;\n }\n case \"deleteContentBackward\":\n case \"deleteWordBackward\": {\n // Special case: caret at offset 0 of a paragraph in tracked\n // mode. Conceptually the user wants to \"delete the paragraph\n // break before this paragraph\" — i.e. merge this paragraph\n // into the previous one. Stamp the current paragraph's\n // properties.revision = del so accept merges them; reject\n // strips the marker and the split stays. Mirrors how Word\n // surfaces this keystroke as a tracked block-level edit.\n //\n // If the paragraph already carries the current author's own\n // pending `ins` (a paragraph break THEY just created in\n // tracked mode), we drop the marker outright — cancelling\n // their own un-committed split rather than layering del on\n // top of ins. Same intuition as the inline \"own ins\" cancel\n // path in `stampDeleteRevision`.\n if (\n this.trackChanges.enabled &&\n sel.kind === \"caret\" &&\n sel.at.offset === 0\n ) {\n const idx = this.registry.indexOf(sel.at.block.id);\n if (idx > 0) {\n const result = this.markParagraphBreakForDelete(idx);\n if (!result.ok) return true;\n // Caret stays at offset 0 of this block — the paragraph\n // hasn't been removed yet, just flagged.\n this.placeCaret(sel.at.block.id, 0);\n return true;\n }\n // At block 0 — no preceding break to delete. Fall through;\n // the browser's no-op is the right behaviour.\n }\n\n const target = this.rangeForBackwardDelete(sel, ie.inputType);\n if (!target) return false;\n const result = this.deleteRange(target);\n if (!result.ok) return true;\n this.placeCaret(target.from.block.id, target.from.offset);\n return true;\n }\n case \"deleteContentForward\":\n case \"deleteWordForward\": {\n const target = this.rangeForForwardDelete(sel, ie.inputType);\n if (!target) return false;\n const result = this.deleteRange(target);\n if (!result.ok) return true;\n this.placeCaret(target.from.block.id, target.from.offset);\n return true;\n }\n case \"deleteByCut\": {\n if (sel.kind !== \"range\") return false;\n const result = this.deleteRange(sel.range);\n if (!result.ok) return true;\n this.placeCaret(sel.range.from.block.id, sel.range.from.offset);\n return true;\n }\n case \"insertParagraph\": {\n // Enter — split the current paragraph at the caret (replacing\n // any selected range first, matching browser semantics).\n const at = this.markedRangeForReplace(sel);\n if (!at) return false;\n const result = this.splitBlock(at);\n if (!result.ok) return true;\n // result.value is the BlockRef of the new (second) paragraph;\n // caret goes to offset 0 there.\n this.placeCaret(result.value.id, 0);\n return true;\n }\n case \"insertLineBreak\": {\n // Shift+Enter — insert a soft `<br>` BreakRun. In tracked mode\n // it carries `revision: ins` directly on its properties.\n const at = this.markedRangeForReplace(sel);\n if (!at) return false;\n const breakRun: InlineRun = {\n kind: \"break\",\n type: \"line\",\n properties: {\n revision:\n this.trackChanges.author === undefined\n ? { type: \"ins\" }\n : { type: \"ins\", author: this.trackChanges.author },\n },\n };\n const result = this.insertRun(at, breakRun);\n if (!result.ok) return true;\n // BreakRun has length 1 — caret moves one past the break.\n this.placeCaret(at.block.id, at.offset + 1);\n return true;\n }\n default:\n if (!this.trackedInputWarned.has(ie.inputType)) {\n this.trackedInputWarned.add(ie.inputType);\n console.warn(\n `[editor] track-changes: inputType \"${ie.inputType}\" not yet routed through the API — falling through to the browser (this edit will be untracked). Phase B follow-up.`,\n );\n }\n return false;\n }\n }\n\n /**\n * Snapshot the pre-composition AST + caret so `handleCompositionEnd`\n * can roll back the browser's native IME mutations and re-insert the\n * final composed string through the tracked-mode `insertRun`. No-op\n * when tracked mode is off — IME falls through to the browser as\n * always (untracked, but functional).\n */\n private handleCompositionStart(_e: CompositionEvent): void {\n if (!this.trackChanges.enabled) {\n this.composition = null;\n return;\n }\n // `this.doc` is immutable per-commit; capturing the reference is a\n // cheap O(1) snapshot. The browser's DOM mutations during the\n // composition will set `domDirty = true` via the `input` listener,\n // which we'll undo by re-rendering from this snapshot at end.\n this.composition = {\n snapshot: this.doc,\n caret: this.selection.currentCaret(),\n };\n }\n\n /**\n * Commit a tracked IME composition. Restores the AST to its\n * pre-composition snapshot, re-renders, then inserts `event.data`\n * through `insertRun` at the captured caret — so the final composed\n * string lands as a tracked `ins` instead of as plain text from the\n * browser's native IME commit.\n *\n * Bails out (and clears state) if tracked mode was toggled off\n * mid-composition or the snapshot is missing; the browser's native\n * commit then stands as-is (untracked, but functional).\n */\n private handleCompositionEnd(e: CompositionEvent): void {\n const state = this.composition;\n this.composition = null;\n if (!state || !state.caret) return;\n const text = e.data ?? \"\";\n\n // Roll back to the pre-composition AST. We can't trust the DOM\n // state because the IME may have written intermediate composition\n // text that won't be there after this returns — better to re-render\n // from the snapshot and then perform a clean tracked insert.\n this.doc = state.snapshot;\n this.lastSerialisedBlocks = state.snapshot.body.map((b) => JSON.stringify(b));\n this.domDirty = false;\n const hosts = this.getContentHosts();\n for (const h of hosts) h.replaceChildren();\n const firstHost = hosts[0] ?? this.host;\n renderSobreeDocument(this.doc, firstHost, this.blockIdsArray());\n\n // Empty composition — user cancelled / IME committed nothing.\n // Restore the caret and stop here.\n if (text === \"\") {\n this.selection.set({ kind: \"caret\", at: state.caret });\n return;\n }\n\n // Look up a fresh block ref after the re-render (the registry\n // versions haven't changed since the snapshot, but `getBlockById`\n // is the canonical way and keeps this resilient to future changes\n // in commit semantics).\n const info = this.getBlockById(state.caret.block.id);\n if (!info) return;\n const at: InlinePosition = {\n block: { id: info.id, version: info.version },\n offset: state.caret.offset,\n };\n // Restore the caret first so any side-effect that reads selection\n // sees the right place.\n this.selection.set({ kind: \"caret\", at });\n const result = this.insertRun(at, { kind: \"text\", text, properties: {} });\n if (result.ok) {\n this.placeCaret(info.id, at.offset + text.length);\n }\n }\n\n /**\n * Resolve the position to insert at when the user types over the\n * current selection. For a caret, that's the caret itself; for a\n * range, we delete the range first (which in tracked mode marks the\n * runs `del` but keeps them in place — so the `from` offset is still\n * the right insertion point afterwards). Returns `null` if the\n * selection spans blocks or the delete failed.\n */\n private markedRangeForReplace(sel: Selection): InlinePosition | null {\n if (!sel) return null;\n if (sel.kind === \"caret\") {\n return this.refreshedPosition(sel.at);\n }\n if (sel.range.from.block.id !== sel.range.to.block.id) return null;\n const del = this.deleteRange(sel.range);\n if (!del.ok) return null;\n return this.refreshedPosition(sel.range.from);\n }\n\n /**\n * Range a Backspace-style key should delete. Range-selection wins\n * if there is one (just delete the selection). Otherwise step one\n * character left of the caret — at offset 0 we have nothing to do\n * (and Word does nothing in that position too, in v1; cross-block\n * backspace is a follow-up).\n */\n private rangeForBackwardDelete(\n sel: Selection,\n _kind: \"deleteContentBackward\" | \"deleteWordBackward\",\n ): ApiRange | null {\n if (!sel) return null;\n if (sel.kind === \"range\") return sel.range;\n if (sel.at.offset === 0) return null;\n // v1: word-backward still deletes a single char. The browser would\n // hand us a wider range via getTargetRanges() but we'd then need\n // to map DOM ranges back to ApiRanges — kept simple for now.\n const at = this.refreshedPosition(sel.at);\n if (!at) return null;\n return {\n from: { block: at.block, offset: at.offset - 1 },\n to: at,\n };\n }\n\n /** Forward-delete equivalent of `rangeForBackwardDelete`. */\n private rangeForForwardDelete(\n sel: Selection,\n _kind: \"deleteContentForward\" | \"deleteWordForward\",\n ): ApiRange | null {\n if (!sel) return null;\n if (sel.kind === \"range\") return sel.range;\n const at = this.refreshedPosition(sel.at);\n if (!at) return null;\n const info = this.getBlockById(at.block.id);\n if (!info || at.offset >= info.length) return null;\n return {\n from: at,\n to: { block: at.block, offset: at.offset + 1 },\n };\n }\n\n /** Re-lookup the block by id to get a fresh `BlockRef` (current version). */\n private refreshedPosition(at: InlinePosition): InlinePosition | null {\n const info = this.getBlockById(at.block.id);\n if (!info) return null;\n return { block: { id: info.id, version: info.version }, offset: at.offset };\n }\n\n /** Place the caret at `(blockId, offset)` using a fresh block ref. */\n private placeCaret(blockId: string, offset: number): void {\n const info = this.getBlockById(blockId);\n if (!info) return;\n const clamped = Math.max(0, Math.min(offset, info.length));\n this.selection.set({\n kind: \"caret\",\n at: { block: { id: info.id, version: info.version }, offset: clamped },\n });\n }\n\n /**\n * True when the current DOM selection's caret sits inside an `<ins>`,\n * `<del>`, or `<span.sobree-revision-format>` wrapper — the markup\n * the renderer emits for tracked revisions. Used by `beforeinput` in\n * mode-off to detect when we have to take over the insert path: if\n * we don't, the browser's contenteditable inserts the new character\n * INSIDE the wrapper and the post-input DOM-sync stamps it with the\n * wrapper's revision marker (an edit the user explicitly opted out\n * of tracking).\n *\n * Returns `false` for a normal caret in plain text, in a `<strong>`\n * / `<em>` / etc. — those wrappers *should* inherit (formatting),\n * unlike revision wrappers which encode \"edit history.\"\n */\n private caretInsideRevisionWrapper(): boolean {\n const sel = typeof window !== \"undefined\" ? window.getSelection() : null;\n if (!sel || sel.rangeCount === 0) return false;\n const range = sel.getRangeAt(0);\n const { startContainer } = range;\n\n const el =\n startContainer.nodeType === Node.ELEMENT_NODE\n ? (startContainer as Element)\n : startContainer.parentElement;\n if (!el) return false;\n\n // Aggressive but reliable: any caret position in a block that\n // contains *any* revision wrapper triggers the intercept. We\n // started with a tighter \"caret is inside / adjacent to a\n // wrapper\" check, but the browser's contentEditable inheritance\n // rule fires in too many caret configurations to predict — text\n // nodes at the boundary, element-typed startContainers, the\n // moment after a re-render, etc. Intercepting at block scope\n // means the next character lands as a separate AST run no\n // matter where in the block the caret happens to be, and\n // `mergeAdjacentTextRuns` keeps the AST clean by coalescing\n // adjacent runs that share properties.\n //\n // Perf cost: every insert in a partially-tracked block goes\n // through `insertRun` instead of native contentEditable. That's\n // a sync commit + re-render per keystroke; modern browsers\n // handle thousands of these per second, so even a doc with many\n // tracked paragraphs types smoothly. Plain text in *untouched*\n // blocks stays on the fast browser path.\n const block = el.closest<HTMLElement>(\"[data-block-id]\");\n if (!block) return false;\n return !!block.querySelector(\n \"ins.sobree-revision, del.sobree-revision, span.sobree-revision-format\",\n );\n }\n\n // === tracked changes & comments — review actions ===\n\n /**\n * Accept the tracked changes inside `range`: insertions become\n * permanent (the revision marker is stripped, text kept), deletions\n * are applied (the deleted text is dropped). Runs in `range` with no\n * revision marker pass through untouched, so it's safe to pass a\n * range slightly wider than the change.\n */\n acceptRevision(\n range: ApiRange,\n opts: { expect?: Record<string, number> } = {},\n ): EditResult<void> {\n this.ensureCurrent();\n const lockCheck = this.checkRange(range, opts.expect);\n if (lockCheck) return lockCheck;\n return this.mutateRunsInRange(range, (runs) =>\n runs.flatMap((r) => decideRevisionRun(r, \"accept\")),\n );\n }\n\n /**\n * Reject the tracked changes inside `range`: insertions are removed\n * (the inserted text is dropped), deletions are restored (the\n * revision marker is stripped, deleted text kept). The inverse of\n * `acceptRevision`.\n */\n rejectRevision(\n range: ApiRange,\n opts: { expect?: Record<string, number> } = {},\n ): EditResult<void> {\n this.ensureCurrent();\n const lockCheck = this.checkRange(range, opts.expect);\n if (lockCheck) return lockCheck;\n return this.mutateRunsInRange(range, (runs) =>\n runs.flatMap((r) => decideRevisionRun(r, \"reject\")),\n );\n }\n\n /**\n * Accept tracked format changes inside `range`: each text run with a\n * `revisionFormat` snapshot drops the snapshot; the current\n * `properties` stay. Runs without one pass through.\n */\n acceptFormatRevision(\n range: ApiRange,\n opts: { expect?: Record<string, number> } = {},\n ): EditResult<void> {\n this.ensureCurrent();\n const lockCheck = this.checkRange(range, opts.expect);\n if (lockCheck) return lockCheck;\n return this.mutateRunsInRange(range, (runs) =>\n runs.map((r) => decideFormatRun(r, \"accept\")),\n );\n }\n\n /**\n * Reject tracked format changes inside `range`: each run reverts its\n * `properties` to `revisionFormat.before`. Inverse of\n * `acceptFormatRevision`.\n */\n rejectFormatRevision(\n range: ApiRange,\n opts: { expect?: Record<string, number> } = {},\n ): EditResult<void> {\n this.ensureCurrent();\n const lockCheck = this.checkRange(range, opts.expect);\n if (lockCheck) return lockCheck;\n return this.mutateRunsInRange(range, (runs) =>\n runs.map((r) => decideFormatRun(r, \"reject\")),\n );\n }\n\n /**\n * Accept the paragraph-mark revision on `target`.\n *\n * Per the semantics in `ParagraphProperties.revision`'s docblock:\n * - `ins` → strip the marker; the paragraph break stays permanent.\n * - `del` → merge this paragraph's content into the *previous*\n * paragraph; the paragraph break is consumed.\n *\n * Returns `range-empty`-coded failure if the block has no paragraph\n * revision, and `invalid-state` if a `del` accept would require\n * merging with a non-paragraph (table, section break) — those merges\n * aren't well-defined yet.\n */\n acceptParagraphRevision(target: BlockRef): EditResult<void> {\n this.ensureCurrent();\n const lockCheck = this.checkRefs([target]);\n if (lockCheck) return lockCheck;\n const index = this.registry.indexOf(target.id);\n const block = this.doc.body[index];\n if (!block || block.kind !== \"paragraph\") {\n return fail({ code: \"invalid-position\", details: \"target is not a paragraph\" });\n }\n const rev = block.properties.revision;\n if (!rev) {\n return fail({ code: \"range-empty\", details: \"no paragraph-level revision to accept\" });\n }\n if (rev.type === \"ins\") {\n const { revision: _strip, ...rest } = block.properties;\n const next = this.doc.body.slice();\n next[index] = { ...block, properties: rest };\n return this.commit({ body: next }, [{ type: \"bump\", index }]);\n }\n // del → merge into previous paragraph (the break is consumed).\n return this.mergeWithPrevious(index);\n }\n\n /**\n * Reject the paragraph-mark revision on `target`.\n * - `ins` → merge this paragraph into the *previous* one (the split\n * introduced by the tracked Enter is undone).\n * - `del` → strip the marker; the paragraph break stays.\n */\n rejectParagraphRevision(target: BlockRef): EditResult<void> {\n this.ensureCurrent();\n const lockCheck = this.checkRefs([target]);\n if (lockCheck) return lockCheck;\n const index = this.registry.indexOf(target.id);\n const block = this.doc.body[index];\n if (!block || block.kind !== \"paragraph\") {\n return fail({ code: \"invalid-position\", details: \"target is not a paragraph\" });\n }\n const rev = block.properties.revision;\n if (!rev) {\n return fail({ code: \"range-empty\", details: \"no paragraph-level revision to reject\" });\n }\n if (rev.type === \"del\") {\n const { revision: _strip, ...rest } = block.properties;\n const next = this.doc.body.slice();\n next[index] = { ...block, properties: rest };\n return this.commit({ body: next }, [{ type: \"bump\", index }]);\n }\n // ins → undo the split: merge into previous paragraph.\n return this.mergeWithPrevious(index);\n }\n\n /**\n * Concatenate `body[index]`'s runs onto the end of `body[index-1]`\n * and remove `body[index]`. The previous block must be a paragraph;\n * otherwise we bail with `invalid-state`. Used by accept/reject of\n * paragraph-mark revisions where the decision means \"this paragraph\n * break should not exist\".\n */\n private mergeWithPrevious(index: number): EditResult<void> {\n if (index <= 0) {\n // No previous block — the paragraph break before index 0 is\n // implicit (start-of-doc), so a `del` marker there is\n // semantically nonsensical. Strip it instead of leaving the\n // block in a half-state where the marker says \"delete this\n // break\" but nothing happens — the dock then thinks the\n // revision is still unresolved and the user can't progress.\n return this.stripParagraphMarker(index);\n }\n const prev = this.doc.body[index - 1];\n const cur = this.doc.body[index];\n if (!prev || !cur || cur.kind !== \"paragraph\") {\n return fail({ code: \"invalid-state\", details: \"current block is not a paragraph\" });\n }\n if (prev.kind !== \"paragraph\") {\n return fail({\n code: \"invalid-state\",\n details: \"previous block is not a paragraph — cross-kind merge unsupported\",\n });\n }\n const next = this.doc.body.slice();\n next[index - 1] = {\n ...prev,\n runs: mergeAdjacentTextRuns([...prev.runs, ...cur.runs]),\n };\n next.splice(index, 1);\n if (next.length === 0) next.push({ kind: \"paragraph\", properties: {}, runs: [] });\n return this.commit({ body: next }, [\n { type: \"bump\", index: index - 1 },\n { type: \"remove\", index },\n ]);\n }\n\n /**\n * Strip the `revision` marker from `body[index]`'s paragraph\n * properties, leaving the block (and its content) in place.\n * Fallback for merge-impossible cases — see `mergeWithPrevious`'s\n * `index <= 0` branch and `applyAllRevisions`' second pass.\n */\n private stripParagraphMarker(index: number): EditResult<void> {\n const block = this.doc.body[index];\n if (!block || block.kind !== \"paragraph\") {\n return fail({ code: \"invalid-position\", details: \"target is not a paragraph\" });\n }\n if (!block.properties.revision) return ok<void>(undefined as void, []);\n const { revision: _strip, ...rest } = block.properties;\n const next = this.doc.body.slice();\n next[index] = { ...block, properties: rest };\n return this.commit({ body: next }, [{ type: \"bump\", index }]);\n }\n\n /**\n * Flag `body[index]`'s paragraph break as a tracked deletion —\n * stamp `properties.revision = { type: \"del\", author }`. Used by\n * `handleTrackedInput` for the Backspace-at-start-of-paragraph\n * keystroke.\n *\n * Two short-circuits:\n * - If the paragraph already carries the *same author's* pending\n * `ins` (the user is backspacing into a split they themselves\n * just made), drop the marker and merge into the previous\n * paragraph — cancelling an un-committed insert rather than\n * layering del on top of ins.\n * - If the paragraph carries some OTHER revision (peer's ins, an\n * existing del), leave it alone with a no-op success. The\n * reviewer should resolve those via accept/reject first.\n */\n private markParagraphBreakForDelete(index: number): EditResult<void> {\n const block = this.doc.body[index];\n if (!block || block.kind !== \"paragraph\") {\n return fail({ code: \"invalid-position\", details: \"target is not a paragraph\" });\n }\n const author = this.trackChanges.author;\n const existing = block.properties.revision;\n if (existing?.type === \"ins\" && existing.author === author) {\n // Own pending split — cancel it by merging back into previous.\n return this.mergeWithPrevious(index);\n }\n if (existing) {\n // Peer's revision — leave it alone.\n return ok<void>(undefined as void, []);\n }\n const revision: RevisionMark =\n author === undefined ? { type: \"del\" } : { type: \"del\", author };\n const next = this.doc.body.slice();\n next[index] = {\n ...block,\n properties: { ...block.properties, revision },\n };\n return this.commit({ body: next }, [{ type: \"bump\", index }]);\n }\n\n /**\n * Enumerate every logical tracked change in the document.\n *\n * Consecutive revision-bearing runs by the same author coalesce into\n * one `RevisionSpan` — so a delete-then-insert replacement is one\n * span, and two unrelated insertions in a paragraph (separated by\n * plain text) are two. Each span's `range` carries fresh, versioned\n * `BlockRef`s, ready to hand to `acceptRevision` / `rejectRevision`.\n *\n * Call after each `change` — the ranges are positional, so re-query\n * rather than caching across edits.\n */\n getRevisions(): RevisionSpan[] {\n this.ensureCurrent();\n const spans: RevisionSpan[] = [];\n for (let i = 0; i < this.doc.body.length; i++) {\n const block = this.doc.body[i];\n if (!block) continue;\n if (block.kind === \"table\") {\n // Walk into table cells. Cell paragraphs aren't tracked in\n // the registry (they don't have stable BlockRefs), so we\n // surface their revisions under the *containing table's*\n // BlockRef as a sentinel. The dock count + `acceptAllRevisions`\n // see them; per-cell single-accept via the popover isn't\n // supported yet (requires the registry to know about cell\n // paragraphs — a separate refactor).\n const info = this.getBlock(i);\n const tableRef: BlockRef = { id: info.id, version: info.version };\n for (const row of block.rows) {\n for (const cell of row.cells) {\n for (const inner of cell.content) {\n if (inner.kind !== \"paragraph\") continue;\n this.collectParagraphRevisions(inner, tableRef, spans);\n }\n }\n }\n continue;\n }\n if (block.kind !== \"paragraph\") continue;\n const info = this.getBlock(i);\n const ref: BlockRef = { id: info.id, version: info.version };\n // Paragraph-mark revision (level: \"paragraph\"). Surface it first\n // so review UIs see one entry per block, ordered with inline\n // revisions to follow.\n const pRev = block.properties.revision;\n if (pRev) {\n spans.push({\n range: {\n from: { block: ref, offset: 0 },\n to: { block: ref, offset: info.length },\n },\n ...(pRev.author !== undefined ? { author: pRev.author } : {}),\n kinds: [pRev.type],\n ...(pRev.date !== undefined ? { date: pRev.date } : {}),\n level: \"paragraph\",\n });\n }\n let offset = 0;\n let open: {\n start: number;\n end: number;\n author: string | undefined;\n kinds: Set<\"ins\" | \"del\">;\n date: string | undefined;\n } | null = null;\n // Parallel walker for format-change spans — same coalescing rule\n // (same author, contiguous). Format spans don't have ins/del\n // kinds; we use a synthetic `kinds: [\"ins\"]` (with `level:\n // \"format\"`) so the consumer shape stays uniform.\n let openFmt: {\n start: number;\n end: number;\n author: string | undefined;\n date: string | undefined;\n } | null = null;\n const flushFmt = (): void => {\n if (!openFmt) return;\n spans.push({\n range: {\n from: { block: ref, offset: openFmt.start },\n to: { block: ref, offset: openFmt.end },\n },\n ...(openFmt.author !== undefined ? { author: openFmt.author } : {}),\n kinds: [\"ins\"],\n ...(openFmt.date !== undefined ? { date: openFmt.date } : {}),\n level: \"format\",\n });\n openFmt = null;\n };\n const flush = (): void => {\n if (!open) return;\n spans.push({\n range: {\n from: { block: ref, offset: open.start },\n to: { block: ref, offset: open.end },\n },\n ...(open.author !== undefined ? { author: open.author } : {}),\n kinds: [...open.kinds],\n ...(open.date !== undefined ? { date: open.date } : {}),\n level: \"inline\",\n });\n open = null;\n };\n for (const run of block.runs) {\n const len = runLength(run);\n const rev = run.kind === \"text\" ? run.properties.revision : undefined;\n if (rev) {\n // Coalesce into the open span when the author matches (both\n // `undefined` counts as a match — anonymous revisions group).\n if (open && open.author === rev.author) {\n open.end = offset + len;\n open.kinds.add(rev.type);\n } else {\n flush();\n open = {\n start: offset,\n end: offset + len,\n author: rev.author,\n kinds: new Set<\"ins\" | \"del\">([rev.type]),\n date: rev.date,\n };\n }\n } else {\n flush();\n }\n // Format-revision walker — independent of ins/del so a run can\n // be both (e.g. inserted text whose subsequent format was\n // tracked-changed).\n const rf = run.kind === \"text\" ? run.properties.revisionFormat : undefined;\n if (rf) {\n if (openFmt && openFmt.author === rf.author) {\n openFmt.end = offset + len;\n } else {\n flushFmt();\n openFmt = {\n start: offset,\n end: offset + len,\n author: rf.author,\n date: rf.date,\n };\n }\n } else {\n flushFmt();\n }\n offset += len;\n }\n flush();\n flushFmt();\n }\n return spans;\n }\n\n /**\n * Walk one paragraph and append its revision spans to `out`. Used by\n * `getRevisions` for both top-level paragraphs (where `ref` is the\n * paragraph's own BlockRef) and for paragraphs inside table cells\n * (where `ref` is the *containing table's* BlockRef as a sentinel —\n * cell paragraphs don't have their own registry entry yet).\n *\n * Emits the same three-level span shape as the inline walker:\n * paragraph-mark first, then coalesced inline ins/del spans, then\n * coalesced format-change spans.\n */\n private collectParagraphRevisions(\n block: Paragraph,\n ref: BlockRef,\n out: RevisionSpan[],\n ): void {\n const length = runsLength(block.runs);\n\n // Paragraph-mark\n const pRev = block.properties.revision;\n if (pRev) {\n out.push({\n range: {\n from: { block: ref, offset: 0 },\n to: { block: ref, offset: length },\n },\n ...(pRev.author !== undefined ? { author: pRev.author } : {}),\n kinds: [pRev.type],\n ...(pRev.date !== undefined ? { date: pRev.date } : {}),\n level: \"paragraph\",\n });\n }\n\n // Inline + format walkers — same logic as in `getRevisions`'s\n // top-level loop. Kept inline (rather than calling the loop)\n // because the loop manages its own open/openFmt state machines.\n let offset = 0;\n let open: {\n start: number; end: number;\n author: string | undefined;\n kinds: Set<\"ins\" | \"del\">;\n date: string | undefined;\n } | null = null;\n let openFmt: {\n start: number; end: number;\n author: string | undefined;\n date: string | undefined;\n } | null = null;\n const flush = (): void => {\n if (!open) return;\n out.push({\n range: {\n from: { block: ref, offset: open.start },\n to: { block: ref, offset: open.end },\n },\n ...(open.author !== undefined ? { author: open.author } : {}),\n kinds: [...open.kinds],\n ...(open.date !== undefined ? { date: open.date } : {}),\n level: \"inline\",\n });\n open = null;\n };\n const flushFmt = (): void => {\n if (!openFmt) return;\n out.push({\n range: {\n from: { block: ref, offset: openFmt.start },\n to: { block: ref, offset: openFmt.end },\n },\n ...(openFmt.author !== undefined ? { author: openFmt.author } : {}),\n kinds: [\"ins\"],\n ...(openFmt.date !== undefined ? { date: openFmt.date } : {}),\n level: \"format\",\n });\n openFmt = null;\n };\n for (const run of block.runs) {\n const len = runLength(run);\n const rev = run.kind === \"text\" ? run.properties.revision : undefined;\n if (rev) {\n if (open && open.author === rev.author) {\n open.end = offset + len;\n open.kinds.add(rev.type);\n } else {\n flush();\n open = {\n start: offset, end: offset + len,\n author: rev.author,\n kinds: new Set<\"ins\" | \"del\">([rev.type]),\n date: rev.date,\n };\n }\n } else {\n flush();\n }\n const rf = run.kind === \"text\" ? run.properties.revisionFormat : undefined;\n if (rf) {\n if (openFmt && openFmt.author === rf.author) {\n openFmt.end = offset + len;\n } else {\n flushFmt();\n openFmt = { start: offset, end: offset + len, author: rf.author, date: rf.date };\n }\n } else {\n flushFmt();\n }\n offset += len;\n }\n flush();\n flushFmt();\n }\n\n /**\n * Accept every tracked change in the document. With `opts.author`,\n * accept only that author's changes. One commit for the whole sweep.\n */\n acceptAllRevisions(opts: { author?: string } = {}): EditResult<void> {\n return this.applyAllRevisions(\"accept\", opts.author);\n }\n\n /** Reject every tracked change (optionally filtered by author). */\n rejectAllRevisions(opts: { author?: string } = {}): EditResult<void> {\n return this.applyAllRevisions(\"reject\", opts.author);\n }\n\n private applyAllRevisions(\n decision: \"accept\" | \"reject\",\n author: string | undefined,\n ): EditResult<void> {\n this.ensureCurrent();\n // Two passes:\n // 1. Inline revisions on every paragraph's runs (existing path).\n // 2. Paragraph-mark revisions — collected as a list of \"merge with\n // previous\" actions, applied bottom-up so the indices stay valid\n // as paragraphs collapse.\n const nextBody = this.doc.body.slice();\n const bumps: Mutation[] = [];\n const removes: number[] = [];\n\n for (let i = 0; i < nextBody.length; i++) {\n const block = nextBody[i];\n if (!block) continue;\n // Tables: sweep their cell paragraphs too. We process inline +\n // format + paragraph-mark *within* each cell, but `merge with\n // previous` for a cell paragraph-mark del falls back to\n // strip-the-marker (merging across cell paragraph boundaries\n // requires structural cell-content edits we keep separate).\n if (block.kind === \"table\") {\n const tableChanged = this.sweepTableCellRevisions(block, decision, author);\n if (tableChanged.changed) {\n nextBody[i] = tableChanged.next;\n bumps.push({ type: \"bump\", index: i });\n }\n continue;\n }\n if (block.kind !== \"paragraph\") continue;\n\n let changed = false;\n const newRuns = block.runs.flatMap((r) => {\n let next: InlineRun = r;\n // Inline ins/del revision.\n const rev = r.kind === \"text\" ? r.properties.revision : undefined;\n if (rev && (author === undefined || rev.author === author)) {\n const decided = decideRevisionRun(next, decision);\n changed = true;\n // `decideRevisionRun` may return [] (drop) or a single run.\n if (decided.length === 0) return decided;\n next = decided[0]!;\n }\n // Format-change revision (rPrChange).\n const rf = next.kind === \"text\" ? next.properties.revisionFormat : undefined;\n if (rf && (author === undefined || rf.author === author)) {\n next = decideFormatRun(next, decision);\n changed = true;\n }\n return [next];\n });\n let nextBlock: Block = block;\n if (changed) {\n nextBlock = { ...block, runs: mergeAdjacentTextRuns(newRuns) };\n }\n\n // Paragraph-mark revision on this block.\n const pRev = block.properties.revision;\n if (pRev && (author === undefined || pRev.author === author)) {\n // \"Strip the marker\" — accept-ins / reject-del — keeps the\n // paragraph split, just clears the mark.\n // \"Merge with previous\" — accept-del / reject-ins — collapses\n // this paragraph into the previous one.\n const stripMarker =\n (decision === \"accept\" && pRev.type === \"ins\") ||\n (decision === \"reject\" && pRev.type === \"del\");\n if (stripMarker) {\n const { revision: _strip, ...rest } = (nextBlock as Paragraph).properties;\n nextBlock = { ...(nextBlock as Paragraph), properties: rest };\n changed = true;\n } else {\n // Schedule a merge — defer to second pass.\n removes.push(i);\n // Still apply the inline changes for this block first.\n if (changed) nextBody[i] = nextBlock;\n continue;\n }\n }\n\n if (changed) {\n nextBody[i] = nextBlock;\n bumps.push({ type: \"bump\", index: i });\n }\n }\n\n // Second pass — apply paragraph-mark merges bottom-up so indices\n // stay valid as paragraphs collapse. For merge-impossible cases\n // (first block, or previous block is a non-paragraph), strip the\n // marker as a best-effort fallback: the user asked us to resolve\n // all revisions, so leaving a marker in place defeats the intent\n // and traps the reviewer in the dock with \"unresolved\" items they\n // can't actually act on.\n if (removes.length > 0) {\n removes.sort((a, b) => b - a);\n for (const i of removes) {\n const cur = nextBody[i];\n if (!cur || cur.kind !== \"paragraph\") continue;\n const prev = i > 0 ? nextBody[i - 1] : null;\n const canMerge = prev != null && prev.kind === \"paragraph\";\n if (!canMerge) {\n // Strip the marker rather than no-op the merge.\n if (cur.properties.revision) {\n const { revision: _strip, ...rest } = cur.properties;\n nextBody[i] = { ...cur, properties: rest };\n bumps.push({ type: \"bump\", index: i });\n }\n continue;\n }\n nextBody[i - 1] = {\n ...prev,\n runs: mergeAdjacentTextRuns([...prev.runs, ...cur.runs]),\n };\n nextBody.splice(i, 1);\n bumps.push({ type: \"bump\", index: i - 1 });\n bumps.push({ type: \"remove\", index: i });\n }\n if (nextBody.length === 0) nextBody.push({ kind: \"paragraph\", properties: {}, runs: [] });\n }\n\n if (bumps.length === 0) return ok<void>(undefined as void, []);\n return this.commit({ body: nextBody }, bumps);\n }\n\n /**\n * Walk a table's cell paragraphs and apply the accept/reject\n * decision to inline + format + paragraph-mark revisions inside.\n * Returns `{ next, changed }` so the caller knows whether to bump\n * the table block. Paragraph-mark del within a cell falls back to\n * strip-the-marker rather than attempting a cross-cell-paragraph\n * merge — that structural edit is out of v1 scope for tables.\n */\n private sweepTableCellRevisions(\n table: Table,\n decision: \"accept\" | \"reject\",\n author: string | undefined,\n ): { next: Table; changed: boolean } {\n let anyChanged = false;\n const nextRows = table.rows.map((row: TableRow): TableRow => ({\n ...row,\n cells: row.cells.map((cell: TableCell): TableCell => {\n let cellChanged = false;\n const nextContent: Block[] = cell.content.map((inner: Block): Block => {\n if (inner.kind !== \"paragraph\") return inner;\n let pChanged = false;\n const newRuns = inner.runs.flatMap((r) => {\n let next: InlineRun = r;\n const rev = r.kind === \"text\" ? r.properties.revision : undefined;\n if (rev && (author === undefined || rev.author === author)) {\n const decided = decideRevisionRun(next, decision);\n pChanged = true;\n if (decided.length === 0) return decided;\n next = decided[0]!;\n }\n const rf = next.kind === \"text\" ? next.properties.revisionFormat : undefined;\n if (rf && (author === undefined || rf.author === author)) {\n next = decideFormatRun(next, decision);\n pChanged = true;\n }\n return [next];\n });\n let nextPara: Paragraph = pChanged\n ? { ...inner, runs: mergeAdjacentTextRuns(newRuns) }\n : inner;\n // Paragraph-mark — strip-as-fallback only. v1 doesn't merge\n // cell paragraphs across boundaries.\n const pRev = inner.properties.revision;\n if (pRev && (author === undefined || pRev.author === author)) {\n const { revision: _strip, ...rest } = nextPara.properties;\n nextPara = { ...nextPara, properties: rest };\n pChanged = true;\n }\n if (pChanged) {\n cellChanged = true;\n anyChanged = true;\n }\n return nextPara;\n });\n if (!cellChanged) return cell;\n return { ...cell, content: nextContent };\n }),\n }));\n return { next: anyChanged ? { ...table, rows: nextRows } : table, changed: anyChanged };\n }\n\n /** Mark comment `id` resolved (`Comment.done = true`). */\n resolveComment(id: number): EditResult<void> {\n return this.setCommentDone(id, true);\n }\n\n /** Re-open a resolved comment `id` (`Comment.done = false`). */\n reopenComment(id: number): EditResult<void> {\n return this.setCommentDone(id, false);\n }\n\n private setCommentDone(id: number, done: boolean): EditResult<void> {\n this.ensureCurrent();\n const comments = this.doc.comments;\n const target = comments?.[id];\n if (!comments || !target) {\n return fail({ code: \"invalid-state\", details: `no comment with id ${id}` });\n }\n const nextComments = { ...comments, [id]: { ...target, done } };\n // No block bumps — comments live outside the body registry.\n return this.commit({ comments: nextComments }, []);\n }\n\n // === AtSelection sugar — DOM-aware convenience wrappers ===\n\n setBlockPropertiesAtSelection(patch: ParagraphPropertiesPatch): EditResult<void> {\n const blockRef = this.selection.currentBlock();\n if (!blockRef) return fail({ code: \"invalid-position\", details: \"no selection\" });\n return this.applyBlockProperties([blockRef], patch);\n }\n\n setRunPropertiesAtSelection(patch: RunPropertiesPatch): EditResult<void> {\n const range = this.selection.currentRange();\n if (!range) return fail({ code: \"invalid-position\", details: \"no selection\" });\n return this.applyRunProperties(range, patch);\n }\n\n wrapSelection(tag: WrapTag): EditResult<void> {\n const range = this.selection.currentRange();\n if (!range) return fail({ code: \"invalid-position\", details: \"no selection\" });\n return this.wrapRange(range, tag);\n }\n\n insertImageAtSelection(\n bytes: Uint8Array,\n opts: { mime: string; widthPx?: number; heightPx?: number; altText?: string },\n ): EditResult<BlockRef> {\n const pos = this.selection.currentCaret();\n if (!pos) return fail({ code: \"invalid-position\", details: \"no selection\" });\n return this.insertImage(pos, bytes, opts);\n }\n\n /**\n * Unwrap span ancestors intersecting the selection, up to the block.\n * Best-effort DOM-level cleanup — preserves the current in-place UX\n * without re-rendering.\n */\n clearInlineFormattingAtSelection(): void {\n const range = currentDomRangeInsideHosts(this.getContentHosts());\n if (!range) return;\n const block = closestBlockElement(range.startContainer, this.getContentHosts());\n if (!block) return;\n const spans: HTMLSpanElement[] = [];\n const walker = document.createTreeWalker(block, NodeFilter.SHOW_ELEMENT, {\n acceptNode: (n) =>\n n instanceof HTMLSpanElement && range.intersectsNode(n)\n ? NodeFilter.FILTER_ACCEPT\n : NodeFilter.FILTER_SKIP,\n });\n for (let n = walker.nextNode(); n; n = walker.nextNode()) spans.push(n as HTMLSpanElement);\n for (const span of spans) unwrap(span);\n this.scheduleChange();\n }\n\n // === events + lifecycle ===\n\n on<E extends EditorEvent>(event: E, cb: (p: EditorEventPayload[E]) => void): Unsubscribe {\n const set = this.listeners[event] as Set<(p: EditorEventPayload[E]) => void>;\n set.add(cb);\n return () => set.delete(cb);\n }\n\n destroy(): void {\n if (this.debounceHandle !== null) {\n window.clearTimeout(this.debounceHandle);\n this.debounceHandle = null;\n }\n for (const [evt, listener] of [\n [\"input\", this.inputListener] as const,\n [\"beforeinput\", this.beforeInputListener] as const,\n [\"paste\", this.pasteListener] as const,\n [\"dragover\", this.dragOverListener] as const,\n [\"drop\", this.dropListener] as const,\n [\"compositionstart\", this.compositionStartListener] as const,\n [\"compositionend\", this.compositionEndListener] as const,\n ]) {\n if (listener) this.host.removeEventListener(evt, listener as EventListener);\n }\n this.inputListener =\n this.beforeInputListener =\n this.pasteListener =\n this.dragOverListener =\n this.dropListener =\n this.compositionStartListener =\n this.compositionEndListener =\n null;\n this.composition = null;\n if (this.selectionChangeListener) {\n document.removeEventListener(\"selectionchange\", this.selectionChangeListener);\n this.selectionChangeListener = null;\n }\n if (this.keydownListener) {\n this.host.removeEventListener(\"keydown\", this.keydownListener);\n this.keydownListener = null;\n }\n this.detachImageResize?.();\n this.detachImageResize = null;\n if (this.ydocUpdateListener) {\n this.ydoc.off(\"afterTransaction\", this.ydocUpdateListener);\n this.ydocUpdateListener = null;\n }\n this.fontFaces.destroy();\n this.history.destroy();\n for (const h of this.getContentHosts()) h.replaceChildren();\n this.host.removeAttribute(\"contenteditable\");\n this.host.classList.remove(\"sobree-editor\");\n this.listeners.change.clear();\n this.listeners.selection.clear();\n this.listeners.keydown.clear();\n this.listeners[\"track-changes-change\"].clear();\n }\n\n // === internal accessors (used by EditorSelection) ===\n\n /** @internal */\n _hosts(): HTMLElement[] {\n return this.getContentHosts();\n }\n\n /** @internal */\n _registry(): BlockRegistry {\n return this.registry;\n }\n\n /** @internal */\n _blockElementAt(index: number): HTMLElement | null {\n return blockElementAtIndex(this._hosts(), index);\n }\n\n /**\n * Parallel array of live block ids (same length as `doc.body`), used\n * by the renderer to stamp `data-block-id` onto every block element.\n * Lets external tools (block tools, embedders) locate a block's DOM\n * element after the body is re-rendered from scratch.\n */\n private blockIdsArray(): string[] {\n const out: string[] = [];\n for (let i = 0; i < this.doc.body.length; i++) {\n out.push(this.registry.refAt(i).id);\n }\n return out;\n }\n\n // === internals ===\n\n private checkRefs(refs: readonly BlockRef[]): EditResult<never> | null {\n const conflicts: Array<{ blockId: string; expected: number; actual: number | null }> = [];\n for (const ref of refs) {\n const live = this.registry.refById(ref.id);\n if (!live) {\n conflicts.push({ blockId: ref.id, expected: ref.version, actual: null });\n continue;\n }\n if (live.version !== ref.version) {\n conflicts.push({ blockId: ref.id, expected: ref.version, actual: live.version });\n }\n }\n return conflicts.length > 0 ? lockConflict(conflicts) : null;\n }\n\n private checkRange(\n range: ApiRange,\n expect: Record<string, number> | undefined,\n ): EditResult<never> | null {\n const refs: BlockRef[] = [range.from.block, range.to.block];\n if (expect) {\n for (const [id, version] of Object.entries(expect)) refs.push({ id, version });\n }\n return this.checkRefs(refs);\n }\n\n /**\n * Apply a runs transform to the runs covered by `range`. Returns\n * `EditResult<void>`. Handles both single- and multi-block ranges.\n * Assumes locks have already been checked.\n */\n private mutateRunsInRange(\n range: ApiRange,\n transform: (runs: InlineRun[]) => InlineRun[],\n ): EditResult<void> {\n const fromIdx = this.registry.indexOf(range.from.block.id);\n const toIdx = this.registry.indexOf(range.to.block.id);\n if (fromIdx < 0 || toIdx < 0 || fromIdx > toIdx) {\n return fail({ code: \"range-out-of-order\", details: \"range endpoints\" });\n }\n const nextBody = this.doc.body.slice();\n const bumps: Mutation[] = [];\n\n if (fromIdx === toIdx) {\n const block = nextBody[fromIdx];\n if (!block || block.kind !== \"paragraph\") {\n return fail({ code: \"invalid-state\", details: `block ${range.from.block.id} not a paragraph` });\n }\n if (range.from.offset === range.to.offset) {\n return fail({ code: \"range-empty\", details: \"zero-width range\" });\n }\n const headSplit = splitRunsAt(block.runs, range.from.offset);\n const tailSplit = splitRunsAt(headSplit.after, range.to.offset - range.from.offset);\n const middle = transform(tailSplit.before);\n const merged = mergeAdjacentTextRuns([...headSplit.before, ...middle, ...tailSplit.after]);\n nextBody[fromIdx] = { ...block, runs: merged };\n bumps.push({ type: \"bump\", index: fromIdx });\n } else {\n // Multi-block range: first block's tail, all of middle blocks,\n // last block's head get transformed.\n for (let i = fromIdx; i <= toIdx; i++) {\n const block = nextBody[i];\n if (!block || block.kind !== \"paragraph\") continue;\n let newRuns: InlineRun[];\n if (i === fromIdx) {\n const split = splitRunsAt(block.runs, range.from.offset);\n newRuns = mergeAdjacentTextRuns([...split.before, ...transform(split.after)]);\n } else if (i === toIdx) {\n const split = splitRunsAt(block.runs, range.to.offset);\n newRuns = mergeAdjacentTextRuns([...transform(split.before), ...split.after]);\n } else {\n newRuns = mergeAdjacentTextRuns(transform(block.runs));\n }\n nextBody[i] = { ...block, runs: newRuns };\n bumps.push({ type: \"bump\", index: i });\n }\n }\n return this.commit({ body: nextBody }, bumps);\n }\n\n /**\n * Apply a mutation to `this.doc`, update the registry, re-render, fire\n * change. Returns the affected refs (post-bump).\n */\n private commit<T = void>(\n update: Partial<SobreeDocument>,\n mutations: readonly Mutation[],\n value?: T,\n _reason: string = \"commit\",\n ): EditResult<T> {\n const savedSelection = this.selection.get();\n\n // Phase 1b.6: Y.UndoManager auto-tracks the resulting Y operations\n // via origin \"local\" (set by `mirrorToYDoc`). No explicit\n // pre-commit recording needed.\n\n const next: SobreeDocument = { ...this.doc, ...update };\n\n // Apply registry mutations first so `affected` reports new versions.\n const affected: BlockRef[] = [];\n for (const m of mutations) {\n if (m.type === \"insert\") affected.push(this.registry.insert(m.index));\n else if (m.type === \"remove\") this.registry.remove(m.index);\n else if (m.type === \"bump\") affected.push(this.registry.bump(m.index));\n }\n\n this.doc = next;\n this.lastSerialisedBlocks = next.body.map((b) => JSON.stringify(b));\n const hosts = this.getContentHosts();\n for (const h of hosts) h.replaceChildren();\n const firstHost = hosts[0] ?? this.host;\n renderSobreeDocument(this.doc, firstHost, this.blockIdsArray());\n\n // Best-effort selection restore (block must still exist + offset still valid).\n if (savedSelection) applySelectionToDom(this._hosts(), this.registry, savedSelection);\n\n this.domDirty = false;\n this.mirrorToYDoc();\n this.emitChangeNow();\n return ok<T>(value as T, affected);\n }\n\n /**\n * Ensure `this.doc` reflects the latest edits. If the DOM has been\n * dirtied by user typing / paste / drop, pull the latest content out\n * of it and bump affected block versions. If the last mutation came\n * from the API, the AST is already current — skip the (lossy)\n * DOM-to-AST round-trip.\n */\n private ensureCurrent(): SobreeDocument {\n if (!this.domDirty) return this.doc;\n return this.syncFromDom();\n }\n\n private syncFromDom(): SobreeDocument {\n const serialised = serializeHostsToDocument(this.getContentHosts());\n const prevCount = this.registry.length();\n const newCount = serialised.body.length;\n\n if (newCount !== prevCount) {\n // Structural change (user pressed Enter / Backspace, paste inserted\n // blocks): we can't preserve ids across structural shifts cheaply,\n // so re-stamp. Agents that held stale refs will see lock failures.\n this.registry.reset(newCount);\n } else {\n // Same count: detect which blocks' serialised JSON changed.\n const newJson = serialised.body.map((b) => JSON.stringify(b));\n const changed: boolean[] = newJson.map((j, i) => j !== this.lastSerialisedBlocks[i]);\n this.lastSerialisedBlocks = newJson;\n this.registry.bumpChanged(changed);\n }\n this.doc = {\n ...this.doc,\n body: serialised.body,\n numbering: serialised.numbering,\n };\n this.domDirty = false;\n this.mirrorToYDoc();\n return this.doc;\n }\n\n /**\n * Schedule a DOM-driven change emit. Called from the `input` listener\n * when the user types — the DOM is the source of truth and we sync the\n * AST from it before notifying listeners.\n */\n private scheduleChange(): void {\n if (this.debounceHandle !== null) window.clearTimeout(this.debounceHandle);\n this.debounceHandle = window.setTimeout(() => {\n this.debounceHandle = null;\n this.ensureCurrent();\n this.emitChangeNow();\n }, this.debounceMs);\n }\n\n /**\n * Emit a `change` event using the current in-memory AST verbatim. Do\n * NOT sync from DOM — callers that need a DOM sync should call it\n * explicitly (user-typing path does). API mutations have already\n * rendered their AST into the DOM and must not let the lossy DOM-read\n * overwrite properties the renderer doesn't surface\n * (column widths, verticalAlign, table properties, …).\n */\n private emitChangeNow(): void {\n this.revision += 1;\n if (this.listeners.change.size === 0) return;\n const stripped = stripBinary(this.doc);\n const payload: ChangePayload = {\n doc: stripped,\n // Alias for backwards compat — same reference, no clone cost.\n document: stripped,\n revision: this.revision,\n documentVersion: this.registry.documentVersion(),\n };\n for (const cb of this.listeners.change) {\n try {\n cb(payload);\n } catch (err) {\n console.error(\"[sobree] change listener threw:\", err);\n }\n }\n }\n\n // === Y.Doc mirroring ===\n\n /**\n * Snapshot of the live block ids in body order — used both as the\n * input to `applyDocumentToYDoc` (so each Y.Map carries its stable\n * id) and as the `blockIdsArray()` the renderer uses to set the\n * `data-block-id` attribute.\n */\n private allBlockIds(): string[] {\n const out: string[] = [];\n for (let i = 0; i < this.registry.length(); i++) {\n out.push(this.registry.refAt(i).id);\n }\n return out;\n }\n\n /**\n * Mirror the current `this.doc` into the Y.Doc as a single\n * transaction. The diff is performed by `applyDocumentToYDoc`,\n * which matches blocks by id so concurrent edits to *different*\n * blocks merge cleanly via the Y.Array CRDT.\n *\n * Origin is `\"local\"` so a future Y observer can distinguish locally-\n * driven mutations (already rendered) from remote ones (need re-render).\n */\n private mirrorToYDoc(): void {\n // When a BlobStore is configured, any path that's been migrated\n // to a partRef (or is currently being migrated) must not get\n // mirrored inline — that would re-introduce the bytes into the\n // Y.Doc. Without a BlobStore, the skip set is empty and behavior\n // is identical to today.\n const skip = this.computePartPathSkipSet();\n applyDocumentToYDoc(\n this.ydoc,\n this.doc,\n this.allBlockIds(),\n \"local\",\n skip ? { skipPartPaths: skip } : {},\n );\n }\n\n /**\n * Returns the set of part paths that mirror should NOT write\n * inline — they're (or will soon be) tracked via the partRefs\n * Y.Map instead. Returns `undefined` when there's nothing to skip\n * (the common no-BlobStore case) so the mirror takes its\n * fastest path.\n */\n private computePartPathSkipSet(): ReadonlySet<string> | undefined {\n if (this.pendingPartRefMigrations.size === 0) {\n const refKeys = Object.keys(this.lastPartRefs);\n if (refKeys.length === 0) return undefined;\n return new Set(refKeys);\n }\n const out = new Set<string>(Object.keys(this.lastPartRefs));\n for (const p of this.pendingPartRefMigrations) out.add(p);\n return out;\n }\n\n /**\n * Background migrate inline part bytes into the BlobStore. Called\n * by mutators (`insertImage`, `embedFont`) when a `BlobStore` is\n * configured. The local `doc.rawParts` keeps its inline copy so\n * the renderer stays synchronous; the Y.Doc gets a `partRefs`\n * entry referencing the BlobStore content hash, and any stale\n * `parts` entry is deleted.\n *\n * Robust against errors: an upload failure logs and leaves the\n * path in the pending set so a future call can retry. The local\n * renderer is unaffected (bytes are still in `doc.rawParts`).\n */\n private async migratePartToBlobStore(\n partPath: string,\n bytes: Uint8Array,\n ): Promise<void> {\n if (!this.blobStore || !this.blobCache) return;\n this.pendingPartRefMigrations.add(partPath);\n try {\n const hash = await sha256Hex(bytes);\n this.blobCache.put(hash, bytes);\n await this.blobStore.put(bytes);\n this.ydoc.transact(() => {\n // Write the partRef (the new authoritative reference).\n applyPartRefsToYDoc(this.ydoc, { [partPath]: hash }, \"local\");\n // Delete any stale inline parts entry. The mirror's skip set\n // will prevent re-introducing it.\n this.ydoc.getMap<Uint8Array>(Y_PARTS_KEY).delete(partPath);\n }, \"local\");\n this.lastPartRefs = { ...this.lastPartRefs, [partPath]: hash };\n } catch (err) {\n console.error(\n `[sobree] failed to migrate part ${partPath} to BlobStore:`,\n err,\n );\n } finally {\n this.pendingPartRefMigrations.delete(partPath);\n }\n }\n\n /**\n * Compose the current selection into a {@link SelectionPayload} and\n * dispatch to subscribers. Called from the document-level\n * `selectionchange` listener attached in the constructor; safe to fire\n * even when no subscribers exist (the early-return keeps it cheap).\n */\n private fireSelection(): void {\n if (this.listeners.selection.size === 0) return;\n const sel = this.selection.get();\n let range: ApiRange | null = null;\n let caret: InlinePosition | null = null;\n if (sel) {\n if (sel.kind === \"range\") {\n range = sel.range;\n caret = sel.range.from;\n } else {\n caret = sel.at;\n }\n }\n const payload: SelectionPayload = {\n selection: sel,\n range,\n caret,\n block: caret?.block ?? null,\n };\n for (const cb of this.listeners.selection) {\n try {\n cb(payload);\n } catch (err) {\n console.error(\"[sobree] selection listener threw:\", err);\n }\n }\n }\n\n /**\n * Normalise a DOM `KeyboardEvent` into a {@link KeyDownPayload} and\n * dispatch to subscribers in registration order. Subscribers can\n * `preventDefault()` (browser default) and / or `stopPropagation()`\n * (further subscribers). The editor itself binds no shortcuts —\n * everything goes through plugins.\n */\n private fireKeyDown(e: KeyboardEvent): void {\n if (this.listeners.keydown.size === 0) return;\n let stopped = false;\n const payload: KeyDownPayload = {\n key: e.key.length === 1 ? e.key.toLowerCase() : e.key,\n code: e.code,\n ctrl: e.ctrlKey,\n shift: e.shiftKey,\n alt: e.altKey,\n meta: e.metaKey,\n preventDefault: () => e.preventDefault(),\n stopPropagation: () => {\n stopped = true;\n },\n originalEvent: e,\n };\n for (const cb of this.listeners.keydown) {\n if (stopped) break;\n try {\n cb(payload);\n } catch (err) {\n console.error(\"[sobree] keydown listener threw:\", err);\n }\n }\n }\n\n private summariseBlock(block: Block, index: number): BlockInfo {\n const ref = this.registry.refAt(index);\n const baseInfo = {\n index,\n id: ref.id,\n version: ref.version,\n };\n if (block.kind === \"paragraph\") {\n const info: BlockInfo = {\n ...baseInfo,\n kind: \"paragraph\",\n text: runsToText(block.runs),\n length: runsLength(block.runs),\n };\n if (block.properties.styleId) info.styleId = block.properties.styleId;\n if (block.properties.alignment) info.alignment = block.properties.alignment;\n return info;\n }\n if (block.kind === \"table\") {\n const firstCell = block.rows[0]?.cells[0];\n const firstPara = firstCell?.content.find((b): b is Paragraph => b.kind === \"paragraph\");\n const preview = firstPara ? runsToText(firstPara.runs) : \"\";\n return {\n ...baseInfo,\n kind: \"table\",\n text: preview,\n length: 0,\n };\n }\n return { ...baseInfo, kind: block.kind, text: \"\", length: 0 };\n }\n\n // === clipboard / drag-drop image insertion ===\n\n private async onPaste(e: ClipboardEvent): Promise<void> {\n const items = e.clipboardData?.items;\n if (!items) return;\n\n // Image-file paste — handled the same way in tracked and untracked\n // modes; image-as-revision is a follow-up (would need to extend\n // `stampInsertRevision` to drawing runs).\n for (const item of Array.from(items)) {\n if (item.kind !== \"file\") continue;\n if (!item.type.startsWith(\"image/\")) continue;\n e.preventDefault();\n const file = item.getAsFile();\n if (!file) continue;\n await this.insertImageFromFile(file);\n return;\n }\n\n // Tracked-mode text paste — intercept so the inserted runs flow\n // through `insertRun` (which stamps `revision: ins` per\n // `TrackChangesState`'s semantics) instead of the browser's native\n // contentEditable paste, which mutates the DOM directly and the\n // post-input sync would land plain runs with no marker.\n //\n // v1 scope: plain text only (`text/plain`). Multi-line text uses\n // `splitBlock` between lines — each line becomes a tracked paragraph\n // (the splits themselves carry `revision: ins` on the new\n // paragraphs' properties, matching the live-typed Enter path).\n // HTML / rich paste in tracked mode falls back to plain-text — a\n // deliberate trade-off so the marker contract stays tight.\n if (this.trackChanges.enabled) {\n const text = e.clipboardData?.getData(\"text/plain\") ?? \"\";\n if (text === \"\") return;\n e.preventDefault();\n this.pasteTrackedText(text);\n }\n }\n\n /**\n * Insert `text` at the current selection in track-changes mode, with\n * each `\\n` becoming a `splitBlock`. Used by `onPaste` for plain-text\n * paste; could be reused for tracked drop (a follow-up). Splits the\n * line list once up-front and walks it so each insertRun lands at the\n * caret of the *current* paragraph (which may be a fresh one from a\n * preceding splitBlock).\n */\n private pasteTrackedText(text: string): void {\n const sel = this.selection.get();\n // Replace any selection first (same as live-typing path).\n const insertAt = this.markedRangeForReplace(sel);\n if (!insertAt) return;\n // Normalise CRLF/CR → LF so the line walk is uniform.\n const lines = text.replace(/\\r\\n?/g, \"\\n\").split(\"\\n\");\n let pos: InlinePosition | null = insertAt;\n let lastInsertedLength = 0;\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i]!;\n if (line !== \"\" && pos) {\n const r = this.insertRun(pos, { kind: \"text\", text: line, properties: {} });\n if (!r.ok) return;\n lastInsertedLength = line.length;\n } else {\n lastInsertedLength = 0;\n }\n // For every line *except* the last, split — produces the next\n // paragraph (whose properties carry `revision: ins`).\n if (i < lines.length - 1 && pos) {\n const afterInsert = this.refreshedPosition({\n block: pos.block,\n offset: pos.offset + lastInsertedLength,\n });\n if (!afterInsert) return;\n const split = this.splitBlock(afterInsert);\n if (!split.ok) return;\n pos = { block: split.value, offset: 0 };\n } else {\n pos = pos\n ? this.refreshedPosition({\n block: pos.block,\n offset: pos.offset + lastInsertedLength,\n })\n : null;\n }\n }\n // Final caret restoration — past the last inserted character.\n if (pos) this.placeCaret(pos.block.id, pos.offset);\n }\n\n private onDragOver(e: DragEvent): void {\n if (!hasImageInDataTransfer(e.dataTransfer)) return;\n e.preventDefault();\n if (e.dataTransfer) e.dataTransfer.dropEffect = \"copy\";\n }\n\n private async onDrop(e: DragEvent): Promise<void> {\n if (!hasImageInDataTransfer(e.dataTransfer)) return;\n e.preventDefault();\n const dropRange = caretRangeFromPoint(e.clientX, e.clientY);\n if (dropRange) {\n const sel = window.getSelection();\n if (sel) {\n sel.removeAllRanges();\n sel.addRange(dropRange);\n }\n }\n const files = Array.from(e.dataTransfer?.files ?? []).filter((f) =>\n f.type.startsWith(\"image/\"),\n );\n for (const file of files) await this.insertImageFromFile(file);\n }\n\n private async insertImageFromFile(file: File): Promise<void> {\n const bytes = new Uint8Array(await file.arrayBuffer());\n const dims = await readImageDimensions(file);\n this.insertImageAtSelection(bytes, {\n mime: file.type || \"image/png\",\n widthPx: dims.width,\n heightPx: dims.height,\n altText: file.name,\n });\n }\n}\n\n// === Selection namespace ===\n\n/**\n * Read and write the caret / selection in model terms. Lives on\n * `editor.selection`. Wraps `window.getSelection()` so callers never\n * touch the DOM directly.\n */\nexport class EditorSelection {\n constructor(private readonly editor: Editor) {}\n\n /** Current selection as a model `Selection`. Returns `null` when focus is outside. */\n get(): Selection {\n return selectionFromDom(this.editor._hosts(), this.editor._registry());\n }\n\n /** Apply a model selection to the DOM. */\n set(sel: Selection): boolean {\n return applySelectionToDom(this.editor._hosts(), this.editor._registry(), sel);\n }\n\n /** Shortcut: current selection as a `Range`, or `null` when collapsed/absent. */\n currentRange(): ApiRange | null {\n const s = this.get();\n if (!s) return null;\n if (s.kind === \"caret\") return null;\n return s.range;\n }\n\n /** Shortcut: the caret position (collapses a range to its `from`). */\n currentCaret(): InlinePosition | null {\n const s = this.get();\n if (!s) return null;\n if (s.kind === \"caret\") return s.at;\n return s.range.from;\n }\n\n /** Shortcut: ref of the block containing the caret. */\n currentBlock(): BlockRef | null {\n const c = this.currentCaret();\n return c ? c.block : null;\n }\n\n /** Legacy: current block index (for code still using indices). */\n currentBlockIndex(): number | null {\n const b = this.currentBlock();\n if (!b) return null;\n return this.editor._registry().indexOf(b.id);\n }\n}\n\n// removedSectionIndex + mergeSectionsAcross moved to\n// ./internal/mutations.ts so HeadlessSobree can share them.\n\n/**\n * Default {@link CommandBus} implementation. Plain in-memory map; no\n * editor coupling beyond the closure plugins use when registering.\n * Replacing it would mean swapping a field on Editor — the rest of\n * the surface stays the same.\n */\nexport class EditorCommands implements CommandBus {\n // Stored as `unknown` so the same Map can hold commands with\n // different `Args` shapes; callers see the typed wrapper.\n private readonly commands = new Map<string, CommandDefinition<unknown>>();\n\n register<Args = void>(def: CommandDefinition<Args>): () => void {\n if (this.commands.has(def.name)) {\n console.warn(`[sobree] command \"${def.name}\" registered twice — overwriting`);\n }\n this.commands.set(def.name, def as CommandDefinition<unknown>);\n return () => {\n // Only remove if the same definition is still registered — guards\n // against a re-register replacing this one and a later detach\n // accidentally killing the new one.\n if (this.commands.get(def.name) === (def as unknown)) {\n this.commands.delete(def.name);\n }\n };\n }\n\n execute<Args = void>(name: string, args?: Args): void {\n const cmd = this.commands.get(name);\n if (!cmd) {\n console.warn(`[sobree] command \"${name}\" not registered`);\n return;\n }\n if (cmd.isAvailable && !cmd.isAvailable()) return;\n try {\n cmd.run(args as never);\n } catch (err) {\n console.error(`[sobree] command \"${name}\" threw:`, err);\n }\n }\n\n list(): CommandSnapshot[] {\n const out: CommandSnapshot[] = [];\n for (const c of this.commands.values()) {\n out.push({\n name: c.name,\n title: c.title ?? c.name,\n isActive: c.isActive?.() ?? false,\n isAvailable: c.isAvailable?.() ?? true,\n });\n }\n return out;\n }\n\n has(name: string): boolean {\n return this.commands.has(name);\n }\n}\n\n// === helpers ===\n\nconst BLOCK_ELEMENT_TAGS = new Set([\n \"h1\",\n \"h2\",\n \"h3\",\n \"h4\",\n \"h5\",\n \"h6\",\n \"p\",\n \"hr\",\n \"blockquote\",\n \"ul\",\n \"ol\",\n \"pre\",\n \"table\",\n \"div\",\n \"dl\",\n]);\n\nfunction closestBlockElement(node: Node, hosts: HTMLElement[]): HTMLElement | null {\n let cur: Node | null = node;\n while (cur) {\n if (cur instanceof HTMLElement) {\n const parent = cur.parentElement;\n if (parent && hosts.includes(parent)) return cur;\n if (BLOCK_ELEMENT_TAGS.has(cur.tagName.toLowerCase()) && parent && hosts.includes(parent)) {\n return cur;\n }\n }\n cur = cur.parentNode;\n }\n return null;\n}\n\nfunction currentDomRangeInsideHosts(hosts: HTMLElement[]): Range | null {\n const sel = window.getSelection();\n if (!sel || sel.rangeCount === 0) return null;\n const range = sel.getRangeAt(0);\n if (!hosts.some((h) => h.contains(range.startContainer) && h.contains(range.endContainer))) {\n return null;\n }\n return range;\n}\n\n// mergeParagraphProps, wrapTagToPatch, mimeToExtension,\n// allocateMediaPath, pxToEmu — moved to ./internal/mutations.ts so\n// HeadlessSobree can share them.\n\n/**\n * Resolve one run against an accept/reject decision on its tracked\n * change. Returns the replacement run list — `[run]` unchanged,\n * `[stripped]` to keep the text minus the revision marker, or `[]` to\n * drop the run entirely.\n *\n * accept + ins → keep text, strip marker (insertion confirmed)\n * accept + del → drop run (deletion applied)\n * reject + ins → drop run (insertion undone)\n * reject + del → keep text, strip marker (deletion undone)\n *\n * Non-text runs and runs with no `revision` pass through untouched.\n */\nfunction decideRevisionRun(\n run: InlineRun,\n decision: \"accept\" | \"reject\",\n): InlineRun[] {\n if (run.kind !== \"text\") return [run];\n const rev = run.properties.revision;\n if (!rev) return [run];\n const keepText =\n (decision === \"accept\" && rev.type === \"ins\") ||\n (decision === \"reject\" && rev.type === \"del\");\n if (!keepText) return [];\n const { revision: _dropped, ...rest } = run.properties;\n return [{ ...run, properties: rest }];\n}\n\n/**\n * Authoring helper for `insertRun` in track-changes mode. Stamps an\n * `ins` revision on the run if it doesn't already carry one — a caller\n * providing a pre-stamped run (e.g. an import code path replaying a\n * revision) wins. Mirrors `decideRevisionRun`'s text-only contract:\n * non-text runs (drawings, breaks, tabs, …) pass through unchanged in\n * v1 — Word does track drawing inserts as revisions, but layering that\n * on the non-uniform `properties` shape of non-text runs is a follow-up.\n */\nfunction stampInsertRevision(run: InlineRun, author: string | undefined): InlineRun {\n if (run.kind !== \"text\") return run;\n if (run.properties.revision) return run;\n const revision: RevisionMark =\n author === undefined ? { type: \"ins\" } : { type: \"ins\", author };\n return { ...run, properties: { ...run.properties, revision } };\n}\n\n/**\n * Authoring helper for `deleteRange` in track-changes mode. Per the\n * `TrackChangesState` semantics, applied per text run in range:\n * - plain run (no revision) → stamp `del`\n * - already-pending `ins` by same author → drop the run (cancel)\n * - everything else (peer revision, peer → leave untouched\n * `del`, anything pre-marked)\n * Non-text runs pass through unchanged (same text-only contract as\n * `stampInsertRevision`).\n */\n/**\n * Authoring helper for `applyRunProperties` in track-changes mode.\n * Captures the run's current `properties` (excluding any existing\n * `revisionFormat` so the snapshot stays self-contained) as\n * `revisionFormat.before` if no snapshot is already in place.\n * Subsequent tracked format edits skip re-snapshotting — the *original*\n * pre-tracking state always wins on reject.\n */\n/**\n * Consumption helper for `acceptFormatRevision` / `rejectFormatRevision`.\n *\n * accept → drop `revisionFormat`; current `properties` stay.\n * reject → restore `properties` to `revisionFormat.before`; the\n * snapshot is then dropped too (the run is back to its\n * pre-tracking state and there's nothing to undo).\n *\n * Runs without a `revisionFormat` snapshot pass through unchanged.\n */\nfunction decideFormatRun(run: InlineRun, decision: \"accept\" | \"reject\"): InlineRun {\n if (run.kind !== \"text\") return run;\n const rf = run.properties.revisionFormat;\n if (!rf) return run;\n if (decision === \"accept\") {\n const { revisionFormat: _drop, ...rest } = run.properties;\n return { ...run, properties: rest };\n }\n // reject — restore the snapshot, and drop the marker.\n return { ...run, properties: rf.before };\n}\n\nfunction snapshotFormatRevision(run: InlineRun, author: string | undefined): InlineRun {\n if (run.kind !== \"text\") return run;\n if (run.properties.revisionFormat) return run;\n const { revisionFormat: _ignored, ...before } = run.properties;\n const stamp = author === undefined ? { before } : { before, author };\n return { ...run, properties: { ...run.properties, revisionFormat: stamp } };\n}\n\nfunction stampDeleteRevision(run: InlineRun, author: string | undefined): InlineRun[] {\n if (run.kind !== \"text\") return [run];\n const rev = run.properties.revision;\n if (!rev) {\n const revision: RevisionMark =\n author === undefined ? { type: \"del\" } : { type: \"del\", author };\n return [{ ...run, properties: { ...run.properties, revision } }];\n }\n if (rev.type === \"ins\" && rev.author === author) {\n return [];\n }\n return [run];\n}\n\nfunction hasImageInDataTransfer(dt: DataTransfer | null): boolean {\n if (!dt) return false;\n if (dt.types && Array.from(dt.types).includes(\"Files\")) {\n for (const f of Array.from(dt.files ?? [])) {\n if (f.type.startsWith(\"image/\")) return true;\n }\n }\n for (const item of Array.from(dt.items ?? [])) {\n if (item.kind === \"file\" && item.type.startsWith(\"image/\")) return true;\n }\n return false;\n}\n\ninterface CaretPositionish {\n offsetNode: Node;\n offset: number;\n}\n\nfunction caretRangeFromPoint(x: number, y: number): Range | null {\n const docAny = document as Document & {\n caretPositionFromPoint?: (x: number, y: number) => CaretPositionish | null;\n caretRangeFromPoint?: (x: number, y: number) => Range | null;\n };\n if (docAny.caretRangeFromPoint) return docAny.caretRangeFromPoint(x, y);\n const pos = docAny.caretPositionFromPoint?.(x, y);\n if (!pos) return null;\n const range = document.createRange();\n range.setStart(pos.offsetNode, pos.offset);\n range.collapse(true);\n return range;\n}\n\nfunction readImageDimensions(file: File): Promise<{ width: number; height: number }> {\n return new Promise((resolve) => {\n const url = URL.createObjectURL(file);\n const img = new Image();\n img.onload = () => {\n const w = img.naturalWidth || 200;\n const h = img.naturalHeight || 150;\n URL.revokeObjectURL(url);\n resolve({ width: w, height: h });\n };\n img.onerror = () => {\n URL.revokeObjectURL(url);\n resolve({ width: 200, height: 150 });\n };\n img.src = url;\n });\n}\n\nfunction unwrap(el: HTMLElement): void {\n const parent = el.parentNode;\n if (!parent) return;\n while (el.firstChild) parent.insertBefore(el.firstChild, el);\n parent.removeChild(el);\n}\n\n/**\n * Strip binary `rawParts` from a document before emitting on the event\n * stream. Keeps the payload JSON-clean for WebSocket/MCP transport.\n */\nfunction stripBinary(doc: SobreeDocument): SobreeDocument {\n return { ...doc, rawParts: {} };\n}\n\n// Re-export countBlocks for any callers that need it.\nexport { countBlocks };\n","import { attachSections } from \"./plugins/sections\";\nimport { DEFAULT_PAGE_SETUP, type PageSetup } from \"./paperStack/pageSetup\";\nimport { PaperStack } from \"./paperStack/paperStack\";\nimport {\n pageSetupToSection,\n sectionToPageSetup,\n} from \"./doc/pageSetupBridge\";\nimport { exportDocx } from \"./docx/export/index\";\nimport { importDocx } from \"./docx/import/index\";\nimport { Editor, type OutlineItem, type TrackChangesState } from \"./editor\";\nimport type { SectionProperties, SobreeDocument } from \"./doc/types\";\n\nexport type SobreeMode = \"edit\" | \"read\";\n\nexport type SobreeEvent =\n | \"change\"\n | \"paginate\"\n | \"setup\"\n | \"mode-change\"\n | \"track-changes-change\"\n | \"docx:import\"\n | \"docx:export\";\nexport interface SobreeEventPayload {\n change: {\n doc: SobreeDocument;\n /** @deprecated Use `doc`. Alias kept for backwards compatibility. */\n document: SobreeDocument;\n revision: number;\n };\n paginate: { pageCount: number };\n setup: { setup: PageSetup };\n \"mode-change\": { mode: SobreeMode };\n /**\n * Fires when track-changes mode flips on/off or the author changes.\n * Re-emitted from the underlying editor — `Sobree.setTrackChanges`\n * delegates to `editor.setTrackChanges`, so listeners on either\n * surface see the same events.\n */\n \"track-changes-change\": TrackChangesState;\n \"docx:import\": { warnings: string[] };\n \"docx:export\": { warnings: string[] };\n}\nexport type SobreeUnsubscribe = () => void;\n\nexport interface SobreeOptions {\n /** Initial document AST. */\n initialDocument?: SobreeDocument;\n /** Page setup. Falls back to `DEFAULT_PAGE_SETUP`. */\n pageSetup?: PageSetup;\n /** Forwarded to the underlying Editor. */\n changeDebounceMs?: number;\n /**\n * Y.Doc backing the document. Forwarded to the Editor — see\n * `EditorOptions.ydoc` for the contract. Use this when you want to\n * attach a provider (`y-websocket`, `y-indexeddb`, `y-webrtc`) for\n * persistence or collaboration. If absent, the editor creates one\n * internally (still observable via `sobree.editor.ydoc`).\n */\n ydoc?: import(\"yjs\").Doc;\n /**\n * Optional content-hashed `BlobStore` for binary parts. Forwarded\n * to the Editor — see `EditorOptions.blobStore`.\n */\n blobStore?: import(\"./blob\").BlobStore;\n /**\n * Initial track-changes mode. Forwarded to the editor — see\n * `TrackChangesState`. When omitted, the editor starts in\n * direct-edit mode and embedders flip it later with\n * `sobree.setTrackChanges(...)`.\n */\n trackChanges?: TrackChangesState;\n // Plugins are no longer wired through Sobree directly — `createSobree()`\n // owns the pluggable surface and threads the editor + viewport + host\n // into each plugin's `setup(ctx)`. Direct `Sobree` users can still mount\n // plugins manually after construction by calling their `setup({...})`.\n}\n\n/**\n * Top-level embeddable product surface. Composes a framework-free `Editor`\n * with a paginated `PaperStack`, exposing a single wire-ready API for\n * hosting webapps, headless Y peers (HeadlessSobree), and agents.\n *\n * Everything on this class is JSON-clean: plain data in, plain data out.\n * No DOM nodes, Ranges, or function handles cross the public surface.\n */\nexport class Sobree {\n readonly editor: Editor;\n private readonly stack: PaperStack;\n private setup: PageSetup;\n private mode: SobreeMode = \"edit\";\n private readonly listeners: {\n change: Set<(p: SobreeEventPayload[\"change\"]) => void>;\n paginate: Set<(p: SobreeEventPayload[\"paginate\"]) => void>;\n setup: Set<(p: SobreeEventPayload[\"setup\"]) => void>;\n \"mode-change\": Set<(p: SobreeEventPayload[\"mode-change\"]) => void>;\n \"track-changes-change\": Set<\n (p: SobreeEventPayload[\"track-changes-change\"]) => void\n >;\n \"docx:import\": Set<(p: SobreeEventPayload[\"docx:import\"]) => void>;\n \"docx:export\": Set<(p: SobreeEventPayload[\"docx:export\"]) => void>;\n } = {\n change: new Set(),\n paginate: new Set(),\n setup: new Set(),\n \"mode-change\": new Set(),\n \"track-changes-change\": new Set(),\n \"docx:import\": new Set(),\n \"docx:export\": new Set(),\n };\n private readonly detachPaginate: () => void;\n private readonly detachChange: () => void;\n private readonly detachTrackChanges: () => void;\n /** Detachers for default + user-provided plugins, run in reverse on\n * `destroy` so attach order is mirrored on teardown. */\n private readonly pluginDetachers: (() => void)[] = [];\n\n constructor(container: HTMLElement, options: SobreeOptions = {}) {\n this.setup = options.pageSetup ?? deriveSetupFromDocument(options.initialDocument);\n this.stack = new PaperStack(container, this.setup);\n const editorOpts: ConstructorParameters<typeof Editor>[1] = {\n contentHosts: () => this.stack.contentHosts,\n };\n if (options.initialDocument) editorOpts.initialDocument = options.initialDocument;\n if (options.changeDebounceMs !== undefined) editorOpts.changeDebounceMs = options.changeDebounceMs;\n if (options.ydoc) editorOpts.ydoc = options.ydoc;\n if (options.blobStore) editorOpts.blobStore = options.blobStore;\n if (options.trackChanges) editorOpts.trackChanges = options.trackChanges;\n this.editor = new Editor(this.stack.root, editorOpts);\n\n // Mount the always-on `attachSections` plugin internally —\n // `section.insertBreakAfter` drives the section-break popover\n // and Cmd+Shift+Enter, so it ships with every Sobree mount.\n // User-supplied plugins are not handled here; `createSobree()`\n // owns the pluggable surface and threads them in with a richer\n // PluginContext (editor + viewport + host + sobree).\n this.pluginDetachers.push(attachSections(this.editor));\n\n // Seed the stack with the initial document's sections so the very\n // first pagination applies per-section vAlign correctly. Subsequent\n // changes refresh sections through the `change` listener below.\n this.syncStackSections();\n // Re-derive `setup` from doc.sections[0] in case the doc was\n // pre-hydrated (e.g. from IndexedDB) before Sobree existed — the\n // change-event-driven sync below only fires on subsequent edits.\n this.syncSetupFromDocument();\n // Initial pagination. Without this, a doc that was already loaded\n // into the editor (via Y.Doc hydration, pre-seeded `initialDocument`,\n // or any path that didn't fire a `change` after Sobree subscribed)\n // would sit unpaginated — all blocks dumped into paper 0 — until\n // the user typed something. Defer one rAF so the host has laid out\n // the paper element and `offsetHeight` measurements are valid.\n if (typeof requestAnimationFrame !== \"undefined\") {\n requestAnimationFrame(() => {\n if (!this.stack.root.classList.contains(\"is-zone-editing\")) {\n this.stack.repaginate();\n }\n });\n }\n\n this.detachChange = this.editor.on(\"change\", (payload) => {\n // Re-derive `setup` from doc.sections[0] when they diverge — this\n // is the path that makes undo/redo of page-setup edits visually\n // revert the paper. Without it, Y.UndoManager reverses the AST\n // but the renderer keeps using the post-edit `setup`.\n this.syncSetupFromDocument();\n // Keep the stack's per-section overrides in sync. AST sections may\n // have shifted (insert/delete of section breaks, edits to section\n // properties); pull the latest so per-page vAlign stays correct.\n this.syncStackSections();\n // Don't repaginate while a header/footer zone is being edited in place.\n if (!this.stack.root.classList.contains(\"is-zone-editing\")) {\n this.stack.repaginate();\n }\n for (const cb of this.listeners.change) {\n try {\n cb(payload);\n } catch (err) {\n console.error(\"[sobree] change listener threw:\", err);\n }\n }\n });\n this.detachPaginate = this.stack.onPaginate((pageCount) => {\n for (const cb of this.listeners.paginate) {\n try {\n cb({ pageCount });\n } catch (err) {\n console.error(\"[sobree] paginate listener threw:\", err);\n }\n }\n });\n // Re-emit the editor's track-changes-change so listeners attached\n // to the façade see it without having to reach `sobree.editor.on`.\n this.detachTrackChanges = this.editor.on(\n \"track-changes-change\",\n (state) => {\n for (const cb of this.listeners[\"track-changes-change\"]) {\n try {\n cb(state);\n } catch (err) {\n console.error(\n \"[sobree] track-changes-change listener threw:\",\n err,\n );\n }\n }\n },\n );\n }\n\n // === access to the paper stack, for code that still needs it ===\n\n /** Internal stack element — useful for attaching context tools / viewport. */\n get stackRoot(): HTMLElement {\n return this.stack.root;\n }\n\n /** First paper element — useful as a viewport fit target on first layout. */\n get firstPaper(): HTMLElement {\n return this.stack.firstPaper;\n }\n\n /**\n * First paper's outer ROW (paper card + per-page comments sidebar).\n * Use as `fitWidthTarget` so the viewport's fit-to-width scales to\n * include the sidebar — fitting just `.paper` would leave the\n * sidebar clipped by the viewport's overflow.\n */\n get firstPaperRow(): HTMLElement {\n return this.stack.firstPaperRow;\n }\n\n /** Pass through for Viewport's renderTier callback. */\n setRenderTier(tier: number): void {\n this.stack.setRenderTier(tier);\n }\n\n // === wire-ready API surface ===\n\n /** Current page setup for section 0 (JSON-clean). */\n getPageSetup(): PageSetup {\n return structuredClone(this.setup);\n }\n\n /** Number of sections in the current document. Always >= 1. */\n getSectionCount(): number {\n return Math.max(1, this.editor.getDocument().sections.length);\n }\n\n /**\n * Read section `index` projected onto the demo's `PageSetup` shape so\n * the same Page Setup modal can edit any section. Section 0 returns\n * the live `setup`; other sections are projected from the AST.\n *\n * Lossy for properties the bridge doesn't carry (e.g. per-section\n * pages-per-column will surface here as default), but enough for the\n * fields the modal exposes today.\n */\n getSectionSetup(index: number): PageSetup {\n if (index === 0) return this.getPageSetup();\n const section = this.editor.getDocument().sections[index];\n if (!section) return this.getPageSetup();\n const partial = sectionToPageSetup(\n section,\n this.editor.getDocument().headerFooterBodies,\n );\n return { ...structuredClone(DEFAULT_PAGE_SETUP), ...partial };\n }\n\n /**\n * Write back to section `index`. Section 0 funnels through\n * `setPageSetup` (the canonical path); section 1+ goes through the\n * editor's `setDocument` so it round-trips and triggers the change\n * pipeline (repagination, listeners).\n */\n setSectionSetup(index: number, partial: Partial<PageSetup>): void {\n if (index === 0) {\n this.setPageSetup(partial);\n return;\n }\n const doc = this.editor.getDocument();\n if (index < 0 || index >= doc.sections.length) return;\n const current = this.getSectionSetup(index);\n const merged: PageSetup = { ...current, ...partial };\n const { section, headerFooterBodies } = pageSetupToSection(merged);\n const sections = doc.sections.slice();\n sections[index] = section;\n const nextDoc = {\n ...doc,\n sections,\n headerFooterBodies: { ...doc.headerFooterBodies, ...headerFooterBodies },\n };\n this.editor.setDocument(nextDoc);\n }\n\n /**\n * Merge `partial` into the current page setup. Triggers repagination and\n * fires `setup` event. Plain-data argument — safe over the wire.\n */\n setPageSetup(partial: Partial<PageSetup>): void {\n this.setup = { ...this.setup, ...partial };\n this.stack.updateSetup(this.setup);\n // setup represents section[0]; per-section overrides need refreshing\n // so vAlign / titlePage / etc. picked from the modal land on papers.\n this.syncStackSections();\n // Mirror section[0] back into the editor's document AST. Without\n // this, getDocument() / Y providers / setSectionSetup readers see\n // stale section properties — only `exportDocx()` overlays setup at\n // serialize time. The change ripples through `editor.setDocument`,\n // which fires a `change` event and re-runs `syncStackSections` (a\n // cheap no-op now that the doc + setup agree).\n this.writeSetupToSection0();\n for (const cb of this.listeners.setup) {\n try {\n cb({ setup: this.getPageSetup() });\n } catch (err) {\n console.error(\"[sobree] setup listener threw:\", err);\n }\n }\n }\n\n /** Project the current `setup` into `doc.sections[0]` and commit. */\n private writeSetupToSection0(): void {\n const doc = this.editor.getDocument();\n const { section, headerFooterBodies } = pageSetupToSection(this.setup);\n // No-op if section[0] already matches (avoids change-event churn\n // when setPageSetup is called with the same values).\n const current = doc.sections[0];\n if (current && sameSection(current, section)) return;\n const sections = doc.sections.slice();\n sections[0] = section;\n this.editor.setDocument({\n ...doc,\n sections,\n headerFooterBodies: { ...doc.headerFooterBodies, ...headerFooterBodies },\n });\n }\n\n /** Current rendered page count. */\n getPageCount(): number {\n return this.stack.getPageCount();\n }\n\n /**\n * Force a repagination. Normally unnecessary — repagination runs after\n * every `change` event. Useful for hosts that need to ensure pagination\n * after initial mount (layout must be applied first — call from a\n * `requestAnimationFrame` after the container is in the DOM).\n */\n repaginate(): void {\n this.stack.repaginate();\n }\n\n /** Delegate to `editor.getOutline()`. */\n getOutline(): OutlineItem[] {\n return this.editor.getOutline();\n }\n\n // === read/edit mode ===\n\n /** Current mode. Default is `\"edit\"`. */\n getMode(): SobreeMode {\n return this.mode;\n }\n\n /**\n * Switch between edit and read mode. Read mode turns off\n * `contenteditable`, tags the stack root with `is-read-mode` (so the\n * embedder / block tools can hide indicators and toolbars via CSS or\n * by listening to `mode-change`), and fires the `mode-change` event.\n *\n * Selection remains functional for copy / outline / accessibility;\n * only editing is suspended.\n */\n setMode(mode: SobreeMode): void {\n if (this.mode === mode) return;\n this.mode = mode;\n const editable = mode === \"edit\";\n // Toggle contenteditable on every content host (one per paper).\n for (const host of this.stack.contentHosts) {\n host.contentEditable = editable ? \"true\" : \"false\";\n }\n this.stack.root.classList.toggle(\"is-read-mode\", !editable);\n for (const cb of this.listeners[\"mode-change\"]) {\n try {\n cb({ mode });\n } catch (err) {\n console.error(\"[sobree] mode-change listener threw:\", err);\n }\n }\n }\n\n // === track-changes (authoring mode) ===\n\n /**\n * Current track-changes state. See `TrackChangesState`.\n *\n * Thin proxy to `editor.getTrackChanges()` — exposed on the façade\n * so embedders driving the UI don't need to reach `sobree.editor`.\n */\n getTrackChanges(): TrackChangesState {\n return this.editor.getTrackChanges();\n }\n\n /**\n * Switch authoring mode. Delegates to `editor.setTrackChanges`,\n * which fires `track-changes-change`. Listeners attached to either\n * `editor.on(\"track-changes-change\", …)` or\n * `sobree.on(\"track-changes-change\", …)` receive the new state.\n *\n * The mode survives until flipped — it's not bound to selection or\n * document. To run a one-off tracked edit, save the previous state,\n * flip on, mutate, flip back.\n */\n setTrackChanges(state: TrackChangesState): void {\n this.editor.setTrackChanges(state);\n }\n\n // === DOCX I/O ===\n\n /**\n * Read a .docx file and load it into the editor. Fully native: the\n * parsed `SobreeDocument` is handed straight to the editor — no djot\n * intermediate.\n */\n async openDocx(src: File | Blob | ArrayBuffer | Uint8Array): Promise<void> {\n const { document, warnings } = await importDocx(src);\n // setDocument fires `change`; the change handler runs\n // `syncSetupFromDocument`, which re-derives `setup` from\n // `document.sections[0]`. So the imported page size / margins /\n // vAlign / titlePg / header-footer text all flow into the renderer\n // automatically — no explicit `setPageSetup` needed here.\n this.editor.setDocument(document);\n for (const cb of this.listeners[\"docx:import\"]) {\n try {\n cb({ warnings });\n } catch (err) {\n console.error(\"[sobree] docx:import listener threw:\", err);\n }\n }\n }\n\n /**\n * Export the current document as a .docx Blob. Reads the editor's AST\n * directly, overlays the current page setup's section/header/footer,\n * and serialises to OOXML.\n */\n exportDocx(): Blob {\n const doc = this.editor.getDocument();\n // Section 0 is the demo's editable layout (driven by `setup`);\n // anything past it (created via `section.insertBreakAfter` or by\n // import) survives intact. headerFooterBodies are merged so parts\n // referenced from later sections aren't dropped.\n const { section, headerFooterBodies } = pageSetupToSection(this.setup);\n doc.sections = [section, ...doc.sections.slice(1)];\n doc.headerFooterBodies = { ...doc.headerFooterBodies, ...headerFooterBodies };\n const { blob, warnings } = exportDocx(doc);\n for (const cb of this.listeners[\"docx:export\"]) {\n try {\n cb({ warnings });\n } catch (err) {\n console.error(\"[sobree] docx:export listener threw:\", err);\n }\n }\n return blob;\n }\n\n // === events ===\n\n on<E extends SobreeEvent>(\n event: E,\n cb: (payload: SobreeEventPayload[E]) => void,\n ): SobreeUnsubscribe {\n const set = this.listeners[event] as Set<(p: SobreeEventPayload[E]) => void>;\n set.add(cb);\n return () => set.delete(cb);\n }\n\n // === lifecycle ===\n\n /**\n * Compose the sections array the paper stack needs:\n *\n * sections[0] is built from the live `setup` (the demo's UI model\n * represents section 0; setPageSetup updates flow through here).\n * sections[1..] come straight from the document AST — there's no UI\n * for editing them yet, so they're effectively read-only until a\n * future Page Setup section selector lands.\n *\n * Called whenever either source could have changed: constructor,\n * `change` event, `setPageSetup`.\n */\n private syncStackSections(): void {\n const doc = this.editor.getDocument();\n const fromSetup = pageSetupToSection(this.setup).section;\n // Preserve the AST section's header/footer refs over the setup-\n // derived ones. `pageSetupToSection` drops refs when the setup's\n // header / footer template is empty — which is exactly the state\n // on refresh (this.setup defaults to DEFAULT_PAGE_SETUP because\n // `options.initialDocument` is undefined when only `ydoc` is\n // passed). Without this preservation, `pickRichZone` walks back to\n // section 0, finds no refs, returns null, and the renderer falls\n // back to the legacy text-template path — jellap.docx's logo +\n // contact-info textbox header silently degrades to flat text on\n // every page reload.\n const section0 = doc.sections[0];\n if (section0) {\n fromSetup.headerRefs = section0.headerRefs;\n fromSetup.footerRefs = section0.footerRefs;\n if (section0.titlePage !== undefined) fromSetup.titlePage = section0.titlePage;\n }\n const composed: SectionProperties[] = [fromSetup, ...doc.sections.slice(1)];\n this.stack.setSections(composed);\n // Mirror the rich header/footer AST + the dependencies renderBlocks\n // needs (numbering, styles, embedded media) onto the stack so the\n // headers render with their full formatting/images instead of being\n // collapsed to text via blocksToTemplate. When the doc has no\n // header parts at all we clear it — the legacy text-template path\n // (from PageSetup) takes over.\n const hasRichZones =\n Object.keys(doc.headerFooterBodies ?? {}).length > 0;\n this.stack.setRichZones(\n hasRichZones\n ? {\n headerFooterBodies: doc.headerFooterBodies,\n numbering: doc.numbering ?? [],\n styles: doc.styles ?? [],\n rawParts: doc.rawParts ?? {},\n }\n : null,\n );\n // Push anchored frames into the per-paper floating layer. The\n // lifter has been taught to skip anchored drawings (see\n // `liftTextBoxContent` — the `isAnchored` early-continue), so this\n // is the ONLY paint path for anchored content. Inline textboxes\n // are still lifted into body flow (the new layer doesn't yet\n // model inline positioning); that's the next slice of Phase B.\n this.stack.setAnchoredFrames(doc.anchoredFrames ?? null);\n }\n\n /**\n * Re-derive `setup` from `doc.sections[0]` when they diverge.\n *\n * Why: every page-setup edit goes setup → AST via\n * `writeSetupToSection0`. But the inverse — AST → setup — only ran\n * at construction time. That left the renderer's `setup` stuck on\n * the post-edit state when the AST got reverted by undo / redo /\n * external Y-provider edits, so `Cmd+Z` for a margin change reverted\n * the AST silently while the paper kept the new margins.\n *\n * The `sameSetup` early-out is load-bearing — without it, the\n * `change` event fired by `editor.setDocument` inside\n * `writeSetupToSection0` would loop back into here, see the same\n * setup, and waste a re-paginate.\n */\n private syncSetupFromDocument(): void {\n const doc = this.editor.getDocument();\n const section = doc.sections[0];\n if (!section) return;\n const partial = sectionToPageSetup(section, doc.headerFooterBodies);\n const merged: PageSetup = { ...this.setup, ...partial };\n // `partial` only carries `header` / `footer` when the section\n // declares refs. Without explicit clearing here, importing a doc\n // with no footer (jellap.docx) keeps whatever Sobree default\n // (\"Page {page} of {pages}\") was on the prior `setup` — stamping\n // an unauthored footer onto every page. Respect \"the document\n // says no header/footer\" the same way `deriveSetupFromDocument`\n // does at construction.\n if (section.headerRefs.length === 0) merged.header = emptyZone();\n if (section.footerRefs.length === 0) merged.footer = emptyZone();\n if (sameSetup(this.setup, merged)) return;\n this.setup = merged;\n this.stack.updateSetup(this.setup);\n for (const cb of this.listeners.setup) {\n try {\n cb({ setup: this.getPageSetup() });\n } catch (err) {\n console.error(\"[sobree] setup listener threw:\", err);\n }\n }\n }\n\n destroy(): void {\n // Plugins first, in reverse-attach order so the last-mounted is the\n // first-detached — symmetric with how listeners stack.\n for (let i = this.pluginDetachers.length - 1; i >= 0; i--) {\n try {\n this.pluginDetachers[i]?.();\n } catch (err) {\n console.error(\"[sobree] plugin detach threw:\", err);\n }\n }\n this.pluginDetachers.length = 0;\n this.detachChange();\n this.detachPaginate();\n this.detachTrackChanges();\n this.listeners.change.clear();\n this.listeners.paginate.clear();\n this.listeners.setup.clear();\n this.listeners[\"mode-change\"].clear();\n this.listeners[\"track-changes-change\"].clear();\n this.listeners[\"docx:import\"].clear();\n this.listeners[\"docx:export\"].clear();\n this.editor.destroy();\n this.stack.destroy();\n }\n}\n\n/**\n * If `initialDocument` declares a section with headers/footers, project it\n * onto the legacy `PageSetup` shape so the paper stack picks up the\n * header/footer text without a round-trip through the page-setup modal.\n * Falls back to `DEFAULT_PAGE_SETUP` when no document is provided or when\n * the section is silent on layout.\n */\nfunction deriveSetupFromDocument(doc: SobreeDocument | undefined): PageSetup {\n const base = structuredClone(DEFAULT_PAGE_SETUP);\n if (!doc) return base;\n const section = doc.sections[0];\n if (!section) return base;\n const partial = sectionToPageSetup(section, doc.headerFooterBodies);\n const merged: PageSetup = { ...base, ...partial };\n // `sectionToPageSetup` only emits `header` / `footer` when the\n // section declares refs. If it didn't, we used to keep the\n // `DEFAULT_PAGE_SETUP` defaults — which include\n // \"Page {page} of {pages}\" for the footer — and silently stamp them\n // onto every imported doc that has no footer (e.g. jellap.docx).\n // The author's intent was \"no footer here\"; respect it by clearing\n // the merged zone to an empty template.\n if (section.headerRefs.length === 0) merged.header = emptyZone();\n if (section.footerRefs.length === 0) merged.footer = emptyZone();\n return merged;\n}\n\nfunction emptyZone(): PageSetup[\"header\"] {\n return {\n default: \"\",\n first: \"\",\n last: \"\",\n differentFirst: false,\n differentLast: false,\n };\n}\n\n/**\n * Shallow-equal two SectionProperties for the page-setup mirror's\n * early-out. JSON.stringify is fine: both come from `pageSetupToSection`\n * which produces deterministic, finite objects.\n */\nfunction sameSection(a: SectionProperties, b: SectionProperties): boolean {\n return JSON.stringify(a) === JSON.stringify(b);\n}\n\n/** Shallow-equal two PageSetup snapshots — `syncSetupFromDocument`'s\n * early-out, prevents the change → setup → AST → change → … loop. */\nfunction sameSetup(a: PageSetup, b: PageSetup): boolean {\n return JSON.stringify(a) === JSON.stringify(b);\n}\n","import \"./viewport.css\";\n\nexport interface ViewportOptions {\n minScale?: number;\n maxScale?: number;\n /** Scale change per unit of wheel deltaY for shift+wheel. Default 0.005. */\n wheelZoomSensitivity?: number;\n /** Scale change per unit of wheel deltaY for pinch (ctrlKey). Default 0.02. */\n pinchZoomSensitivity?: number;\n onScaleChange?: (scale: number) => void;\n /**\n * Fires when the render tier changes — an integer ≥1 chosen from the\n * current scale. The stage is laid out at that tier via CSS `zoom` so\n * text rasterises at the zoomed size rather than being blitted from a\n * 1× bitmap. Callers that care about layout (e.g. a paginator) should\n * re-run their measurement + layout pass when the tier changes.\n */\n onRenderTierChange?: (tier: number) => void;\n /**\n * Fires whenever the stage transform changes — zoom, pan, programmatic\n * fit, or animated pan. Used by overlays (block toolbar, indicator)\n * that live in viewport coordinates and must follow the page through\n * any transform. Called frequently during gestures, so the handler\n * should be cheap (read-rect-and-write-style cheap).\n */\n onTransformChange?: () => void;\n}\n\n/**\n * A framework-free zoomable / pannable viewport.\n *\n * Layout:\n * container (overflow:hidden, the element passed in)\n * └ stage (absolutely positioned, transform: translate(tx,ty) scale(s))\n * └ slot (where the embedded content lives — caller mounts here)\n *\n * Gestures:\n * - Zoom: wheel with shiftKey OR ctrlKey (macOS pinch emits ctrlKey).\n * The point under the cursor stays under the cursor (zoom-to-cursor).\n * - Pan: wheel without modifiers — two-finger trackpad scroll deltas move\n * the stage. Also supports click-drag with middle mouse or space.\n */\nexport class Viewport {\n readonly container: HTMLElement;\n readonly slot: HTMLElement;\n private readonly stage: HTMLElement;\n private scale = 1;\n private tx = 0;\n private ty = 0;\n private readonly minScale: number;\n private readonly maxScale: number;\n private readonly wheelZoomSensitivity: number;\n private readonly pinchZoomSensitivity: number;\n private readonly onScaleChange: ((s: number) => void) | null;\n private readonly onRenderTierChange: ((t: number) => void) | null;\n private readonly onTransformChange: (() => void) | null;\n private readonly onWheel: (e: WheelEvent) => void;\n /** Current layout-side zoom factor (integer ≥ 1). See ViewportOptions. */\n private renderTier = 1;\n /** Suppresses `onTransformChange` during the constructor's initial\n * `applyTransform` so consumers can capture `viewport` in their\n * callback without TDZ traps. Flipped true at the end of the ctor. */\n private constructed = false;\n // ---------- scroll-axis locking state ----------\n /** Timestamp of the last wheel event, used to delimit gestures. */\n private gestureLastTime = 0;\n /** Dominant axis for the current gesture. Null until detected, cleared at gesture end. */\n private gesturePrimaryAxis: \"x\" | \"y\" | null = null;\n /**\n * Signed cumulative dx within the current gesture. Drives lock release:\n * wobble (±3-5px back-and-forth) cancels out; sustained one-way motion\n * accumulates past the threshold quickly. Reset at gesture end.\n */\n private gestureSignedDx = 0;\n /**\n * Sticky horizontal-lock flag. Engaged by `fitTo` so alignment survives\n * gentle diagonal trackpad gestures; broken when the gesture's signed\n * cumulative dx crosses `X_RELEASE_THRESHOLD` — the user clearly intends\n * sustained horizontal motion.\n */\n private horizontalLock = false;\n\n constructor(container: HTMLElement, options: ViewportOptions = {}) {\n this.container = container;\n this.minScale = options.minScale ?? 0.25;\n this.maxScale = options.maxScale ?? 6;\n this.wheelZoomSensitivity = options.wheelZoomSensitivity ?? 0.005;\n this.pinchZoomSensitivity = options.pinchZoomSensitivity ?? 0.02;\n this.onScaleChange = options.onScaleChange ?? null;\n this.onRenderTierChange = options.onRenderTierChange ?? null;\n this.onTransformChange = options.onTransformChange ?? null;\n\n container.classList.add(\"sobree-viewport\");\n\n this.stage = document.createElement(\"div\");\n this.stage.className = \"sobree-viewport__stage\";\n this.slot = document.createElement(\"div\");\n this.slot.className = \"sobree-viewport__slot\";\n this.stage.appendChild(this.slot);\n container.appendChild(this.stage);\n\n this.onWheel = (e: WheelEvent) => this.handleWheel(e);\n container.addEventListener(\"wheel\", this.onWheel, { passive: false });\n\n this.applyTransform();\n this.constructed = true;\n }\n\n /** Reset pan to origin and scale to 1. */\n reset(): void {\n this.scale = 1;\n this.tx = 0;\n this.ty = 0;\n this.horizontalLock = false;\n this.gesturePrimaryAxis = null;\n this.gestureSignedDx = 0;\n this.applyTransform();\n this.onScaleChange?.(this.scale);\n }\n\n getScale(): number {\n return this.scale;\n }\n\n /** Integer layout-side zoom currently applied via CSS `zoom` on the stage. */\n getRenderTier(): number {\n return this.renderTier;\n }\n\n /**\n * Fit `target` to the viewport.\n * - `\"width\"`: scale so the target fills the viewport horizontally. The\n * vertical centre of the current view is preserved (no jump to the top\n * of the target).\n * - `\"contain\"`: scale so the entire target is visible, centred in both\n * axes.\n * With `animate = true`, the transition runs with a CSS ease curve.\n */\n fitTo(target: HTMLElement, mode: \"width\" | \"contain\", animate = false): void {\n const containerRect = this.container.getBoundingClientRect();\n const targetRect = target.getBoundingClientRect();\n const stageRect = this.stage.getBoundingClientRect();\n const s = this.scale;\n\n const naturalW = targetRect.width / s;\n const naturalH = targetRect.height / s;\n const naturalL = (targetRect.left - stageRect.left) / s;\n const naturalT = (targetRect.top - stageRect.top) / s;\n\n const padding = 32;\n const availW = containerRect.width - padding * 2;\n const availH = containerRect.height - padding * 2;\n\n const sW = availW / naturalW;\n const sH = availH / naturalH;\n const sNew = clamp(mode === \"width\" ? sW : Math.min(sW, sH), this.minScale, this.maxScale);\n\n // Horizontal: centre target in the container.\n const txNew = containerRect.width / 2 - (naturalL + naturalW / 2) * sNew;\n // Vertical: for \"width\", pin whatever's at the current view's vertical\n // centre so the fit feels like a pure zoom — not a scroll-to-top. For\n // \"contain\", centre the target.\n let tyNew: number;\n if (mode === \"width\") {\n const localCenterY = (containerRect.height / 2 - this.ty) / s;\n tyNew = containerRect.height / 2 - localCenterY * sNew;\n } else {\n tyNew = containerRect.height / 2 - (naturalT + naturalH / 2) * sNew;\n }\n\n this.scale = sNew;\n this.tx = txNew;\n this.ty = tyNew;\n // Engage the sticky horizontal-scroll lock: fit-* deliberately picks an\n // X alignment (centre / paper-edge), so we don't want accidental\n // sideways trackpad drift to break it. Heavy horizontal deltas in\n // `applyScrollAxisLock` can still override.\n this.horizontalLock = true;\n if (animate) this.applyTransformAnimated();\n else this.applyTransform();\n this.onScaleChange?.(this.scale);\n }\n\n /**\n * Pan the stage by `(dx, dy)` CSS pixels. Optional `animate` runs the\n * same cubic-ease transition fit-to-page uses; unflagged pans apply\n * instantly.\n */\n panBy(dx: number, dy: number, opts: { animate?: boolean } = {}): void {\n if (dx === 0 && dy === 0) return;\n this.tx += dx;\n this.ty += dy;\n if (opts.animate) this.applyTransformAnimated();\n else this.applyTransform();\n }\n\n /** Zoom to `nextScale`, anchoring the point at (clientX, clientY) in container space. */\n zoomTo(nextScale: number, clientX: number, clientY: number): void {\n const clamped = clamp(nextScale, this.minScale, this.maxScale);\n if (clamped === this.scale) return;\n\n const rect = this.container.getBoundingClientRect();\n const cx = clientX - rect.left;\n const cy = clientY - rect.top;\n\n // World-space point under the cursor before zoom:\n // w = (cursor - translate) / scale\n // After zoom we want that same world point under cursor:\n // cursor = w * newScale + newTranslate\n // => newTranslate = cursor - w * newScale\n const wx = (cx - this.tx) / this.scale;\n const wy = (cy - this.ty) / this.scale;\n\n this.scale = clamped;\n this.tx = cx - wx * clamped;\n this.ty = cy - wy * clamped;\n this.applyTransform();\n this.onScaleChange?.(this.scale);\n }\n\n destroy(): void {\n this.container.removeEventListener(\"wheel\", this.onWheel);\n this.stage.remove();\n this.container.classList.remove(\"sobree-viewport\");\n }\n\n private handleWheel(e: WheelEvent): void {\n // macOS trackpad pinch synthesizes wheel events with ctrlKey=true and small\n // deltaY (~1–10). Discrete mouse wheel ticks with Shift held report ~±100\n // deltaY. They need very different sensitivities or one feels sluggish.\n const isPinch = e.ctrlKey && !e.shiftKey;\n const isWheelZoom = e.shiftKey || e.metaKey;\n if (isPinch || isWheelZoom) {\n e.preventDefault();\n const sensitivity = isPinch ? this.pinchZoomSensitivity : this.wheelZoomSensitivity;\n const factor = Math.exp(-e.deltaY * sensitivity);\n this.zoomTo(this.scale * factor, e.clientX, e.clientY);\n return;\n }\n // Trackpad two-finger scroll — pan the stage, with axis-lock.\n e.preventDefault();\n const { dx, dy } = this.applyScrollAxisLock(e.deltaX, e.deltaY);\n this.tx -= dx;\n this.ty -= dy;\n this.applyTransform();\n }\n\n /**\n * Axis-lock for pan gestures:\n * - Within a gesture (events ≤ GESTURE_GAP_MS apart), a clear dominant\n * axis zeros the other axis so a nearly-vertical swipe doesn't also\n * drift the paper sideways.\n * - An explicit `horizontalLock` set by `fitTo` survives across gestures,\n * keeping fit-page / fit-width alignment stable.\n * - Both locks release when the gesture's signed cumulative dx crosses\n * `X_RELEASE_THRESHOLD`. Signed sum cancels wobble (back-and-forth\n * averages to zero) while sustained one-way motion accumulates fast.\n */\n private applyScrollAxisLock(rawDx: number, rawDy: number): { dx: number; dy: number } {\n const GESTURE_GAP_MS = 150;\n const X_RELEASE_THRESHOLD = 60;\n const AXIS_NOISE = 2;\n const AXIS_RATIO = 2;\n\n const now = performance.now();\n if (now - this.gestureLastTime > GESTURE_GAP_MS) {\n this.gesturePrimaryAxis = null;\n this.gestureSignedDx = 0;\n }\n this.gestureLastTime = now;\n this.gestureSignedDx += rawDx;\n\n // Sustained or strong horizontal intent releases both locks. Wobble\n // (±3-5px events that cancel) stays well below the threshold.\n if (Math.abs(this.gestureSignedDx) >= X_RELEASE_THRESHOLD) {\n this.horizontalLock = false;\n this.gesturePrimaryAxis = null;\n return { dx: rawDx, dy: rawDy };\n }\n\n let dx = rawDx;\n let dy = rawDy;\n const absDx = Math.abs(dx);\n const absDy = Math.abs(dy);\n\n if (this.gesturePrimaryAxis === null && absDx + absDy > AXIS_NOISE) {\n if (absDx * AXIS_RATIO < absDy) this.gesturePrimaryAxis = \"y\";\n else if (absDy * AXIS_RATIO < absDx) this.gesturePrimaryAxis = \"x\";\n }\n\n if (this.gesturePrimaryAxis === \"y\") dx = 0;\n else if (this.gesturePrimaryAxis === \"x\") dy = 0;\n if (this.horizontalLock) dx = 0;\n\n return { dx, dy };\n }\n\n private applyTransform(): void {\n // Pick a layout-side zoom tier so text rasterises at the zoomed size\n // rather than being a bitmap blit. Half-integer boundaries + a 0.5\n // zoom-out tier so low zoom levels also get a matching layout size.\n // scale [0.35, 0.70) → tier 0.5\n // scale [0.70, 1.40) → tier 1\n // scale [1.40, 2.40) → tier 2\n // scale [2.40, 3.40) → tier 3 …\n const nextTier = pickRenderTier(this.scale);\n if (nextTier !== this.renderTier) {\n this.renderTier = nextTier;\n // `zoom` is non-standard but widely supported (Chrome/Safari/Edge,\n // Firefox ≥126). It re-lays-out the subtree at `tier×` size.\n this.stage.style.zoom = String(nextTier);\n this.onRenderTierChange?.(nextTier);\n }\n // Chrome applies transforms in the post-zoom coordinate space, so a\n // `translate(tx, ty)` on an element with `zoom: k` moves by `(tx*k, ty*k)`\n // screen px. We store tx/ty in pre-zoom (container) pixels — the scheme\n // that cursor-anchored zoomTo relies on — so divide by the tier here to\n // cancel out zoom's translate multiplication. Otherwise the paper jumps\n // sideways whenever the tier rolls over.\n const visualScale = this.scale / this.renderTier;\n const translateScale = 1 / this.renderTier;\n this.stage.style.transform = `translate3d(${this.tx * translateScale}px, ${this.ty * translateScale}px, 0) scale(${visualScale})`;\n if (this.constructed) this.onTransformChange?.();\n }\n\n /**\n * Apply the current transform with a CSS transition. Used only for\n * programmatic fits — wheel pan/zoom must stay instant or feel sluggish.\n *\n * If the target scale falls into a different render tier, we DON'T flip\n * `stage.style.zoom` mid-flight: that property can't transition, so the\n * layout would snap instantly while the `transform` keeps easing — the\n * visible \"weird reset\" before the animation. Instead, we keep the\n * current tier through the transition, write the transform expressed in\n * THAT tier's coordinate space (visually identical to the same transform\n * in any other tier — tier is just a layout-quality choice), and snap to\n * the target tier on `transitionend`.\n */\n private applyTransformAnimated(): void {\n const stage = this.stage;\n const targetTier = pickRenderTier(this.scale);\n const tierChange = targetTier !== this.renderTier;\n\n stage.classList.add(\"is-animating\");\n if (tierChange) {\n // Write the target visual transform in the CURRENT tier's space.\n const tier = this.renderTier;\n const visualScale = this.scale / tier;\n const translateScale = 1 / tier;\n stage.style.transform =\n `translate3d(${this.tx * translateScale}px, ${this.ty * translateScale}px, 0) scale(${visualScale})`;\n // applyTransform fires onTransformChange; we skipped it above so\n // overlay listeners still need a kick.\n this.onTransformChange?.();\n } else {\n this.applyTransform();\n }\n\n // Drive overlays (toolbar, indicator) per-frame while the CSS\n // transition is in flight so they track the moving page rather\n // than snapping to the final position at the end.\n let done = false;\n let rafId = 0;\n const tick = () => {\n if (done) return;\n this.onTransformChange?.();\n rafId = requestAnimationFrame(tick);\n };\n rafId = requestAnimationFrame(tick);\n\n const cleanup = () => {\n if (done) return;\n done = true;\n cancelAnimationFrame(rafId);\n stage.classList.remove(\"is-animating\");\n stage.removeEventListener(\"transitionend\", cleanup);\n // Snap layout-side zoom to the right tier now. Visually identical\n // to what's on screen — only re-rasterises text at the new\n // resolution.\n if (tierChange) this.applyTransform();\n // Final tick so overlays land at the exact resting position.\n this.onTransformChange?.();\n };\n stage.addEventListener(\"transitionend\", cleanup);\n // Safety: some transitions don't fire `transitionend` (e.g. same transform).\n window.setTimeout(cleanup, 400);\n }\n}\n\nfunction clamp(n: number, min: number, max: number): number {\n return Math.min(max, Math.max(min, n));\n}\n\n/**\n * Map a visual scale to the closest layout-side zoom tier. Tiers are\n * quantised (not continuous) so we don't re-layout the subtree on every\n * wheel tick — only when the scale crosses a tier boundary. The 0.5 tier\n * handles zoom-out levels so downscaled text is also rendered at a\n * matching layout size.\n */\nfunction pickRenderTier(scale: number): number {\n if (scale < 0.7) return 0.5;\n if (scale < 1.4) return 1;\n return Math.round(scale); // 2 for [1.4, 2.5), 3 for [2.5, 3.5), …\n}\n","/**\n * Tiny Markdown → SobreeDocument parser.\n *\n * **Scope: seed content for hello-world demos.** This is *not* a real\n * Markdown processor — it doesn't round-trip, doesn't handle every\n * CommonMark edge case, and never will. The point is so people can\n * skip the AST builders for example content:\n *\n * ```ts\n * createSobree(\"#editor\", { content: \"# Title\\n\\nFirst paragraph.\" });\n * ```\n *\n * Supported:\n * - ATX headings (`#` … `######`)\n * - Paragraphs (blank-line separated)\n * - Bold (`**...**`), italic (`*...*` or `_..._`), inline code (`` `...` ``)\n * - Hyperlinks `[text](url)`\n * - Hard line break — two trailing spaces\n * - Bulleted lists (`- ` / `* `) — single level\n * - Numbered lists (`1. `) — single level\n *\n * Not supported (use the AST builders instead):\n * - Tables, blockquotes, code fences, images, HTML, nested lists,\n * reference-style links, footnotes, autolinks.\n */\n\nimport {\n appendBlock,\n emptyDocument,\n heading,\n paragraph,\n text,\n} from \"../doc/builders\";\nimport type {\n HyperlinkRun,\n InlineRun,\n NumberingDefinition,\n Paragraph,\n RunProperties,\n SobreeDocument,\n TextRun,\n} from \"../doc/types\";\n\n/**\n * Parse a Markdown string into a `SobreeDocument`. Always returns a\n * valid document; unsupported syntax falls through as plain text.\n */\nexport function parseMarkdown(md: string): SobreeDocument {\n const doc = emptyDocument();\n // emptyDocument() seeds an empty paragraph; drop it so the first\n // converted block is the document's first block.\n doc.body = [];\n // Override the default styles with markdown-friendly typography:\n // 1.15× line + 8pt space-after on Normal so blank-line paragraphs\n // visually separate, and modest before/after on headings so they\n // breathe in body flow. Default builders are intentionally bare\n // (Word-hardcoded-default tight) so docx round-trip stays\n // byte-faithful — markdown content has different expectations and\n // overrides per-style here, on the document itself.\n applyMarkdownStyleOverrides(doc);\n\n const lines = md.replace(/\\r\\n?/g, \"\\n\").split(\"\\n\");\n const ctx: ParseContext = { doc, listState: null };\n\n let i = 0;\n while (i < lines.length) {\n const line = lines[i] ?? \"\";\n\n // Blank line — close any open list and skip.\n if (line.trim() === \"\") {\n ctx.listState = null;\n i++;\n continue;\n }\n\n // Heading?\n const headingMatch = /^(#{1,6})\\s+(.+?)\\s*#*\\s*$/.exec(line);\n if (headingMatch) {\n ctx.listState = null;\n const level = headingMatch[1]!.length;\n const content = headingMatch[2]!;\n appendBlock(doc, heading(level, parseInline(content)));\n i++;\n continue;\n }\n\n // List item?\n const ul = /^(\\s*)([-*])\\s+(.+)$/.exec(line);\n const ol = /^(\\s*)(\\d+)\\.\\s+(.+)$/.exec(line);\n if (ul || ol) {\n const ordered = ol !== null;\n const content = (ol ?? ul)![3]!;\n const para = listItemParagraph(ctx, ordered, parseInline(content));\n appendBlock(doc, para);\n i++;\n continue;\n }\n\n // Paragraph — gather subsequent non-blank, non-special lines.\n ctx.listState = null;\n const buf: string[] = [line];\n let j = i + 1;\n while (j < lines.length) {\n const next = lines[j] ?? \"\";\n if (next.trim() === \"\") break;\n if (/^#{1,6}\\s+/.test(next)) break;\n if (/^\\s*[-*]\\s+/.test(next)) break;\n if (/^\\s*\\d+\\.\\s+/.test(next)) break;\n buf.push(next);\n j++;\n }\n // No explicit spacing — the Normal style's `paragraphDefaults`\n // (set by `defaultStyles()` in builders.ts) provide 8pt space-after\n // and 1.15× line height through the cascade. Empty `properties: {}`\n // means \"use Normal\" — exactly how a bare Word paragraph behaves.\n appendBlock(doc, paragraph(parseInlineWithBreaks(buf)));\n i = j;\n }\n\n // Always end with at least one paragraph so the editor has a caret target.\n if (doc.body.length === 0) doc.body.push(paragraph());\n\n return doc;\n}\n\ninterface ParseContext {\n doc: SobreeDocument;\n /** Open list, or null between lists. Carries the allocated numId. */\n listState: { numId: number; ordered: boolean } | null;\n}\n\nfunction listItemParagraph(\n ctx: ParseContext,\n ordered: boolean,\n runs: InlineRun[],\n): Paragraph {\n // Reuse the open list if the new item matches its kind, otherwise\n // open a fresh one with a freshly-allocated numId.\n if (!ctx.listState || ctx.listState.ordered !== ordered) {\n const numId = allocateNumId(ctx.doc);\n ctx.doc.numbering.push(definitionFor(numId, ordered));\n ctx.listState = { numId, ordered };\n }\n return paragraph(runs, {\n numbering: { numId: ctx.listState.numId, level: 0 },\n });\n}\n\nfunction allocateNumId(doc: SobreeDocument): number {\n if (doc.numbering.length === 0) return 1;\n const max = doc.numbering.reduce((n, def) => Math.max(n, def.numId), 0);\n return max + 1;\n}\n\nfunction definitionFor(numId: number, ordered: boolean): NumberingDefinition {\n return {\n numId,\n abstractFormat: {\n levels: [\n ordered\n ? {\n level: 0,\n format: \"decimal\",\n text: \"%1.\",\n }\n : {\n level: 0,\n format: \"bullet\",\n text: \"•\",\n },\n ],\n },\n };\n}\n\n// === inline parser ===\n\n/**\n * Parse a single line of inline markdown into runs. Handles bold,\n * italic, inline code, and links — with a tiny tokenise / span-match\n * approach. Anything that doesn't match falls through as plain text.\n */\nfunction parseInline(line: string, base: RunProperties = {}): InlineRun[] {\n const out: InlineRun[] = [];\n let i = 0;\n let plain = \"\";\n\n const flushPlain = () => {\n if (plain) {\n out.push(text(plain, base));\n plain = \"\";\n }\n };\n\n while (i < line.length) {\n const rest = line.slice(i);\n\n // Link: [text](href)\n const link = /^\\[([^\\]]+)\\]\\(([^)]+)\\)/.exec(rest);\n if (link) {\n flushPlain();\n const linkRun: HyperlinkRun = {\n kind: \"hyperlink\",\n href: link[2]!,\n children: parseInline(link[1]!, base),\n };\n out.push(linkRun);\n i += link[0].length;\n continue;\n }\n\n // Bold (**...**)\n if (rest.startsWith(\"**\")) {\n const close = rest.indexOf(\"**\", 2);\n if (close > 0) {\n flushPlain();\n const inner = rest.slice(2, close);\n out.push(...parseInline(inner, { ...base, bold: true }));\n i += close + 2;\n continue;\n }\n }\n\n // Italic (*...* or _..._)\n const italicChar = rest[0];\n if (italicChar === \"*\" || italicChar === \"_\") {\n const close = rest.indexOf(italicChar, 1);\n // Avoid empty italics and accidental list-marker matches handled at block level.\n if (close > 1 && rest[close - 1] !== \" \") {\n flushPlain();\n const inner = rest.slice(1, close);\n out.push(...parseInline(inner, { ...base, italic: true }));\n i += close + 1;\n continue;\n }\n }\n\n // Inline code (`...`)\n if (rest[0] === \"`\") {\n const close = rest.indexOf(\"`\", 1);\n if (close > 0) {\n flushPlain();\n const inner = rest.slice(1, close);\n const codeRun: TextRun = {\n kind: \"text\",\n text: inner,\n properties: { ...base, fontFamily: \"Menlo, Consolas, monospace\" },\n };\n out.push(codeRun);\n i += close + 1;\n continue;\n }\n }\n\n // Escape: `\\*`, `\\_`, etc. — keep the next char literal.\n if (rest[0] === \"\\\\\" && rest.length > 1) {\n plain += rest[1];\n i += 2;\n continue;\n }\n\n plain += rest[0];\n i++;\n }\n flushPlain();\n return out;\n}\n\n/**\n * Parse a multi-line paragraph body. Trailing two-space sequences\n * become hard breaks; otherwise lines are joined with a space.\n */\nfunction parseInlineWithBreaks(lines: string[]): InlineRun[] {\n const out: InlineRun[] = [];\n for (let i = 0; i < lines.length; i++) {\n const raw = lines[i] ?? \"\";\n const hardBreak = / $/.test(raw);\n const cleaned = raw.replace(/\\s+$/, \"\");\n out.push(...parseInline(cleaned));\n if (i < lines.length - 1) {\n if (hardBreak) {\n out.push({ kind: \"break\", type: \"line\" });\n } else {\n out.push(text(\" \"));\n }\n }\n }\n return out;\n}\n\n/**\n * Tweak the document's named-style defaults for markdown content:\n * loosens Normal (1.15 line + 8pt after) and gives headings explicit\n * before/after spacing.\n *\n * Mutates `doc.styles` in place. Called once per `parseMarkdown` so\n * every paragraph's style cascade picks up the markdown-flavoured\n * spacing without needing inline `properties.spacing` per block.\n */\nfunction applyMarkdownStyleOverrides(doc: SobreeDocument): void {\n for (const style of doc.styles) {\n if (style.id === \"Normal\") {\n style.paragraphDefaults = {\n ...style.paragraphDefaults,\n spacing: {\n ...(style.paragraphDefaults?.spacing ?? {}),\n line: 276,\n lineRule: \"auto\",\n afterTwips: 160,\n },\n };\n } else if (/^Heading[1-6]$/.test(style.id)) {\n // Heading1 gets the most before-space; lower levels scale down.\n const level = Number.parseInt(style.id.slice(7), 10);\n const before = Math.max(120, 320 - (level - 1) * 40);\n style.paragraphDefaults = {\n ...style.paragraphDefaults,\n spacing: {\n ...(style.paragraphDefaults?.spacing ?? {}),\n line: 276,\n lineRule: \"auto\",\n beforeTwips: before,\n afterTwips: 80,\n },\n };\n }\n }\n}\n","/**\n * `createSobree()` — the blessed factory for the 95% case.\n *\n * Wires `Viewport` + `Sobree` behind a single call, then mounts every\n * plugin the caller passed in `options.plugins`. Returns a flat handle\n * that surfaces the most-used methods directly, with escape hatches\n * (`.editor`, `.sobree`, `.viewport`) for power users.\n *\n * `@sobree/core` ships zero plugin packages — install the ones you\n * want (`@sobree/keyboard`, `@sobree/block-tools`,\n * `@sobree/zoom-controls`) and pass their factories through\n * `plugins: []`. Plugins are mounted in array order and torn down in\n * reverse on `destroy()`. For multi-peer collab, the editor's Y.Doc\n * is what providers (`y-websocket` from `@sobree/collab-providers`)\n * attach to — no separate wire-adapter plugin needed.\n */\n\nimport { Sobree } from \"./sobree\";\nimport type {\n SobreeEvent,\n SobreeEventPayload,\n SobreeOptions,\n SobreeUnsubscribe,\n} from \"./sobree\";\nimport { Editor, type CommandBus } from \"./editor\";\nimport { Viewport } from \"./embed/viewport\";\nimport type {\n PluginContext,\n SobreePlugin,\n SobreePluginInstance,\n} from \"./plugin\";\nimport { exportDocx } from \"./docx/export/index\";\nimport { parseMarkdown } from \"./markdown/parse\";\nimport type { PageSetup } from \"./paperStack/pageSetup\";\nimport type { SobreeDocument } from \"./doc/types\";\n\n/**\n * Initial content. Type detection is automatic:\n * - `string` → seed-quality Markdown (see `parseMarkdown`)\n * - `Blob` / `File` → `.docx` bytes (loaded asynchronously)\n * - `ArrayBuffer` → `.docx` bytes\n * - `Uint8Array` → `.docx` bytes\n * - `SobreeDocument` → AST literal (use the builders to construct)\n *\n * For a `.docx` source, the constructor returns synchronously with an\n * empty editor and `.ready` resolves once the import lands. For all\n * other source types, `.ready` is already resolved when the factory\n * returns.\n */\nexport type SobreeContent =\n | string\n | Blob\n | ArrayBuffer\n | Uint8Array\n | SobreeDocument;\n\n/**\n * How the viewport is fitted on initial mount.\n * - `\"width\"` (default) — first paper fills the host width; you see\n * the document the way you'd read it.\n * - `\"page\"` — first paper is fully contained (whole page visible).\n * - `\"none\"` — leave the viewport at 1:1, no auto-fit.\n */\nexport type FitOnMount = \"width\" | \"page\" | \"none\";\n\nexport interface CreateSobreeOptions {\n /** Initial content. See `SobreeContent`. Default: empty document. */\n content?: SobreeContent;\n /** Page setup. Falls back to A4 portrait with 1in margins. */\n pageSetup?: PageSetup;\n /**\n * Plugins to mount. Each receives a `PluginContext` (editor +\n * viewport + sobree + host) on `setup()` and returns a destroyer.\n * Mounted in array order; destroyed in reverse on\n * `editor.destroy()`. See `@sobree/keyboard`, `@sobree/block-tools`,\n * `@sobree/zoom-controls` for stock factories. (`@sobree/collab-providers`\n * lands in Phase 2 for Yjs persistence / collaboration.)\n */\n plugins?: SobreePlugin[];\n /** Forwarded to the underlying Editor. */\n changeDebounceMs?: number;\n /**\n * Y.Doc backing the document. The editor mirrors every mutation into\n * this Y.Doc; embedders attach providers (`y-websocket`,\n * `y-indexeddb`, `y-webrtc`, …) for persistence / collaboration.\n * If absent, the editor creates one internally — still observable\n * via `editor.editor.ydoc`.\n */\n ydoc?: import(\"yjs\").Doc;\n /**\n * Optional content-hashed `BlobStore` for binary parts (Phase 3.2+).\n * Without one, binary parts (images, fonts) ride inline in the\n * Y.Doc; with one, they go through the BlobStore and the Y.Doc\n * carries only hashes. See `EditorOptions.blobStore` for the full\n * contract.\n */\n blobStore?: import(\"./blob\").BlobStore;\n /**\n * Auto-fit the viewport to the first paper after mount. Default\n * `\"width\"` — what most embedders want for a \"looks right out of the\n * box\" first impression. Pass `\"none\"` if you're driving the\n * viewport yourself.\n */\n fitOnMount?: FitOnMount;\n}\n\n/**\n * The editor handle returned by `createSobree()`. Proxies the most-used\n * methods of the underlying `Sobree` + `Editor` so embedders rarely\n * need to reach through. Power users can use `.sobree` / `.editor` /\n * `.viewport` directly.\n *\n * Plugin instances are NOT exposed on the handle — plugins self-manage\n * after handoff. Reach through `.editor` for command bus / events.\n */\nexport interface SobreeHandle {\n // === escape hatches ===\n readonly sobree: Sobree;\n readonly editor: Editor;\n readonly viewport: Viewport;\n /**\n * The Y.Doc backing the document. Provided so embedders can attach\n * Yjs providers (`y-websocket`, `y-indexeddb`, `y-webrtc`) for\n * persistence / collaboration without reaching through `editor.editor`.\n * Same reference as `editor.editor.ydoc`.\n */\n readonly ydoc: import(\"yjs\").Doc;\n\n // === readiness ===\n /**\n * Resolves when the constructor's `content` has finished loading. For\n * docx content this awaits the import; for everything else this is an\n * already-resolved promise.\n */\n readonly ready: Promise<{ warnings: string[] }>;\n\n // === document I/O ===\n getDocument(): SobreeDocument;\n setDocument(doc: SobreeDocument): void;\n /** Replace the document with one parsed from a Markdown string (seed-quality). */\n loadMarkdown(md: string): void;\n /** Load a `.docx` file. Resolves with any import warnings. */\n loadDocx(src: File | Blob | ArrayBuffer | Uint8Array): Promise<{ warnings: string[] }>;\n /**\n * Serialise the current document to a `.docx` Blob. Uses bytes\n * currently cached locally — if a `blobStore` is configured and\n * some referenced parts aren't yet cached, call\n * `await handle.ensurePartsLoaded()` first to pre-fetch them.\n * Missing parts appear in `warnings`.\n */\n toDocx(): { blob: Blob; warnings: string[] };\n /**\n * Wait for every currently-referenced binary part to be available\n * in the local cache. No-op when no `blobStore` is configured.\n */\n ensurePartsLoaded(): Promise<void>;\n\n // === page setup ===\n getPageSetup(): PageSetup;\n setPageSetup(partial: Partial<PageSetup>): void;\n\n // === commands + events ===\n readonly commands: CommandBus;\n on<E extends SobreeEvent>(\n event: E,\n cb: (payload: SobreeEventPayload[E]) => void,\n ): SobreeUnsubscribe;\n\n // === lifecycle ===\n destroy(): void;\n}\n\n/**\n * Mount a fully-wired Sobree editor (Viewport + Sobree + user plugins)\n * into `target` (CSS selector or HTMLElement). See `CreateSobreeOptions`\n * and `SobreeHandle` for the full surface.\n */\nexport function createSobree(\n target: string | HTMLElement,\n options: CreateSobreeOptions = {},\n): SobreeHandle {\n const host = resolveHost(target);\n\n // Build initial document for the synchronous content paths.\n // Async (docx) starts empty; `ready` reflects the load.\n const { initialDocument, deferredDocx } = resolveInitialContent(options.content);\n\n const viewport = new Viewport(host);\n\n const sobreeOpts: SobreeOptions = {};\n if (initialDocument) sobreeOpts.initialDocument = initialDocument;\n if (options.pageSetup) sobreeOpts.pageSetup = options.pageSetup;\n if (options.changeDebounceMs !== undefined)\n sobreeOpts.changeDebounceMs = options.changeDebounceMs;\n if (options.ydoc) sobreeOpts.ydoc = options.ydoc;\n if (options.blobStore) sobreeOpts.blobStore = options.blobStore;\n\n const sobree = new Sobree(viewport.slot, sobreeOpts);\n\n // Plugin loop. Each plugin's setup runs in array order; their\n // destroyers run in reverse on `destroy()`. A plugin that throws\n // during setup is logged and skipped — its peers still mount.\n const ctx: PluginContext = {\n editor: sobree.editor,\n sobree,\n viewport,\n host,\n };\n const pluginInstances: Array<{\n name: string | undefined;\n instance: SobreePluginInstance;\n }> = [];\n for (const plugin of options.plugins ?? []) {\n try {\n const instance = plugin.setup(ctx);\n pluginInstances.push({ name: plugin.name, instance });\n } catch (err) {\n console.error(\n `[sobree] plugin \"${plugin.name ?? \"?\"}\" setup failed:`,\n err,\n );\n }\n }\n\n // openDocx surfaces import warnings via the `docx:import` event. The\n // sink subscribes BEFORE openDocx fires so the warnings land in-band\n // on the `ready` promise's resolution value.\n let ready: Promise<{ warnings: string[] }>;\n if (deferredDocx) {\n const sink = installWarningSink(sobree);\n ready = sobree\n .openDocx(deferredDocx)\n .then(() => ({ warnings: sink.warnings }));\n } else {\n ready = Promise.resolve({ warnings: [] });\n }\n\n // Auto-fit the viewport once the host has dimensions and the first\n // paper has been laid out. Sync content fits on the next animation\n // frame; deferred docx fits after the import resolves.\n const fitOnMount: FitOnMount = options.fitOnMount ?? \"width\";\n if (fitOnMount !== \"none\") {\n const mode = fitOnMount === \"page\" ? \"contain\" : \"width\";\n const applyFit = (): void => {\n // Skip if the host hasn't been laid out yet (display:none, etc.).\n if (host.clientWidth === 0 || host.clientHeight === 0) return;\n viewport.fitTo(sobree.firstPaper, mode, false);\n };\n if (deferredDocx) {\n ready.then(() => requestAnimationFrame(applyFit));\n } else {\n requestAnimationFrame(applyFit);\n }\n }\n\n const handle: SobreeHandle = {\n sobree,\n get editor() {\n return sobree.editor;\n },\n viewport,\n get ydoc() {\n return sobree.editor.ydoc;\n },\n ready,\n\n getDocument: () => sobree.editor.getDocument(),\n // setDocument / loadMarkdown both flow through editor.setDocument,\n // which fires `change`; Sobree's change handler runs\n // `syncSetupFromDocument` to re-derive page setup from the new\n // `sections[0]`. So an A5 doc (or any doc with non-default page\n // setup) automatically retunes the renderer — no explicit page-\n // setup overlay needed here.\n setDocument: (doc) => sobree.editor.setDocument(doc),\n loadMarkdown: (md) => sobree.editor.setDocument(parseMarkdown(md)),\n loadDocx: async (src) => {\n const sink = installWarningSink(sobree);\n await sobree.openDocx(src);\n return { warnings: sink.warnings };\n },\n toDocx: () => {\n // Sobree.exportDocx() returns just the Blob (warnings via event);\n // we want them in-band. Re-call the underlying serialiser instead.\n return exportDocx(sobree.editor.getDocument());\n },\n ensurePartsLoaded: () => sobree.editor.ensurePartsLoaded(),\n\n getPageSetup: () => sobree.getPageSetup(),\n setPageSetup: (partial) => sobree.setPageSetup(partial),\n\n get commands() {\n return sobree.editor.commands;\n },\n on: (event, cb) => sobree.on(event, cb),\n\n destroy: () => {\n // Plugins down first, in reverse-of-setup order (LIFO). A\n // failing destroy is logged but doesn't stop peers from\n // tearing down — leaks are bad, but partial teardown beats\n // none.\n for (let i = pluginInstances.length - 1; i >= 0; i--) {\n const entry = pluginInstances[i]!;\n try {\n entry.instance.destroy();\n } catch (err) {\n console.error(\n `[sobree] plugin \"${entry.name ?? \"?\"}\" destroy failed:`,\n err,\n );\n }\n }\n sobree.destroy();\n // Viewport doesn't currently expose a destroy; the next page nav\n // tears its listeners down with the host element.\n },\n };\n return handle;\n}\n\n// === helpers ===\n\nfunction resolveHost(target: string | HTMLElement): HTMLElement {\n if (typeof target === \"string\") {\n const el = document.querySelector(target);\n if (!(el instanceof HTMLElement)) {\n throw new Error(\n `[sobree] createSobree: selector \"${target}\" did not match an HTMLElement.`,\n );\n }\n return el;\n }\n return target;\n}\n\ninterface ResolvedContent {\n initialDocument: SobreeDocument | undefined;\n deferredDocx: Blob | ArrayBuffer | Uint8Array | undefined;\n}\n\nfunction resolveInitialContent(content: SobreeContent | undefined): ResolvedContent {\n if (content === undefined) {\n return { initialDocument: undefined, deferredDocx: undefined };\n }\n if (typeof content === \"string\") {\n return { initialDocument: parseMarkdown(content), deferredDocx: undefined };\n }\n if (isDocxSource(content)) {\n return { initialDocument: undefined, deferredDocx: content };\n }\n // Treat as AST literal; final shape-check is delegated to the editor.\n return { initialDocument: content, deferredDocx: undefined };\n}\n\nfunction isDocxSource(\n v: unknown,\n): v is Blob | ArrayBuffer | Uint8Array {\n if (typeof Blob !== \"undefined\" && v instanceof Blob) return true;\n if (v instanceof ArrayBuffer) return true;\n if (v instanceof Uint8Array) return true;\n return false;\n}\n\n/**\n * Subscribe to the next `docx:import` event and stash its warnings so\n * the convenience methods can return them in-band. The returned object's\n * `warnings` field is mutated by the listener.\n */\nfunction installWarningSink(sobree: Sobree): { warnings: string[] } {\n const sink = { warnings: [] as string[] };\n const off = sobree.on(\"docx:import\", (p) => {\n sink.warnings = p.warnings;\n off();\n });\n return sink;\n}\n","/**\n * Shared floating-corner stack — a flex container per (host, corner)\n * that plugins append floating UIs to so multiple docks in the same\n * corner stack predictably instead of overlapping.\n *\n * The problem this solves: `@sobree/zoom-controls`, `@sobree/review`'s\n * dock, a future toast/notification plugin, etc. all want to anchor\n * to a corner of the rendering area. Each appending directly to the\n * host with `position: absolute` makes them collide.\n *\n * Each corner gets its own `<div class=\"sobree-floating-corner\">`\n * created on first use. The container's flex direction is chosen so\n * the *first* item added sits hugging the corner, and subsequent\n * items grow toward the centre:\n *\n * top-right → flex-direction: column (grows downward)\n * top-left → flex-direction: column (grows downward)\n * bottom-right → flex-direction: column-reverse (grows upward)\n * bottom-left → flex-direction: column-reverse (grows upward)\n *\n * This keeps the \"primary\" floating UI nearest the corner regardless\n * of which side it lives on, which matches user intuition for a\n * docked toolbar.\n *\n * The host must be `position: relative` (or otherwise establish a\n * containing block); the corner container uses `position: absolute`\n * to pin to the edge. The function does NOT modify the host's\n * positioning — that's the host owner's responsibility.\n */\n\nexport type FloatingCornerPlacement =\n | \"top-left\"\n | \"top-right\"\n | \"bottom-left\"\n | \"bottom-right\";\n\n/** Margin from the corner edge, in px. Shared so docks line up. */\nconst CORNER_MARGIN = 12;\n/** Gap between stacked items in the corner container, in px. */\nconst STACK_GAP = 8;\n\n/**\n * Return the flex container for `placement` inside `host`, creating\n * it on first call. Subsequent calls with the same `(host, placement)`\n * pair return the same element — so plugins can `appendChild` their\n * floating UI and trust they'll stack with other corner-residents\n * rather than overlap them.\n *\n * The returned element's contents are managed by the appending\n * plugins; this function only owns the container itself. To remove\n * a floating UI, just `.remove()` it — the container stays alive for\n * other tenants. Empty containers are inexpensive (4-byte `<div>` per\n * corner per host) so we don't garbage-collect them.\n */\nexport function getFloatingCorner(\n host: HTMLElement,\n placement: FloatingCornerPlacement,\n): HTMLElement {\n const selector = `:scope > .sobree-floating-corner[data-placement=\"${placement}\"]`;\n const existing = host.querySelector<HTMLElement>(selector);\n if (existing) return existing;\n const corner = document.createElement(\"div\");\n corner.className = \"sobree-floating-corner\";\n corner.dataset.placement = placement;\n Object.assign(corner.style, {\n position: \"absolute\",\n display: \"flex\",\n flexDirection: placement.startsWith(\"bottom-\") ? \"column-reverse\" : \"column\",\n alignItems: placement.endsWith(\"-right\") ? \"flex-end\" : \"flex-start\",\n gap: `${STACK_GAP}px`,\n // Don't intercept clicks on the underlying paper — only the\n // children should be interactive.\n pointerEvents: \"none\",\n // High enough to float above paper content; popovers (which use\n // `position: fixed` on body) stay above this naturally.\n zIndex: \"30\",\n });\n if (placement.startsWith(\"top-\")) corner.style.top = `${CORNER_MARGIN}px`;\n else corner.style.bottom = `${CORNER_MARGIN}px`;\n if (placement.endsWith(\"-right\")) corner.style.right = `${CORNER_MARGIN}px`;\n else corner.style.left = `${CORNER_MARGIN}px`;\n // Re-enable pointer events on every direct child so plugins don't\n // each have to remember.\n const enablePointerOnChildren = new MutationObserver(() => {\n for (const child of Array.from(corner.children)) {\n (child as HTMLElement).style.pointerEvents = \"auto\";\n }\n });\n enablePointerOnChildren.observe(corner, { childList: true });\n host.appendChild(corner);\n return corner;\n}\n","import \"./zoneEdit.css\";\nimport type { PageSetup } from \"../paperStack/pageSetup\";\n\nexport type ZoneKind = \"header\" | \"footer\";\n\nexport interface EnterZoneEditOptions {\n zone: HTMLElement;\n kind: ZoneKind;\n stackRoot: HTMLElement;\n getSetup: () => PageSetup;\n setSetup: (next: PageSetup) => void;\n onExit: () => void;\n}\n\ntype TemplateSlot = \"first\" | \"last\" | \"default\";\n\n/**\n * Make a header or footer zone editable in place. The displayed (substituted)\n * text is swapped for its template (e.g. \"Page {page} of {pages}\") so the user\n * edits the template, not its rendered result. On commit (blur, Enter,\n * Escape, click outside, or the caller invoking the returned `finish()`)\n * the new template is written back to the right slot on the PageSetup.\n *\n * The returned function exits zone-edit programmatically — block tools call\n * it when the user clicks the gutter indicator a second time, so the\n * indicator works as a single toggle for entering and leaving the zone.\n */\nexport function enterZoneEdit(opts: EnterZoneEditOptions): () => void {\n const { zone, kind, stackRoot, getSetup, setSetup, onExit } = opts;\n\n const slot = resolveTemplateSlot(zone, kind, stackRoot, getSetup());\n const setup = getSetup();\n const template = setup[kind][slot];\n\n // Zone content: a single editable text node. No in-zone UI islands — those\n // would confuse the browser's caret placement in an empty text node.\n zone.replaceChildren();\n const textNode = document.createTextNode(template);\n zone.appendChild(textNode);\n zone.classList.remove(\"is-empty\");\n stackRoot.classList.add(\"is-zone-editing\");\n\n // Make the zone the ONLY editable region in the stack. Nested contenteditable\n // makes focus/selection ambiguous: `zone.focus()` can silently fail while the\n // caret stays in whatever was last clicked (e.g. the title). Flipping stack\n // editability off and only zone on leaves one unambiguous target.\n const prevStackEditable = stackRoot.contentEditable;\n stackRoot.contentEditable = \"false\";\n zone.contentEditable = \"true\";\n\n zone.focus();\n placeCursorAtEndOf(textNode);\n\n let finished = false;\n const finish = () => {\n if (finished) return;\n finished = true;\n const newTemplate = (zone.textContent ?? \"\").replace(/\\n+$/, \"\");\n zone.contentEditable = \"false\";\n stackRoot.contentEditable = prevStackEditable || \"true\";\n stackRoot.classList.remove(\"is-zone-editing\");\n zone.removeEventListener(\"blur\", onBlur);\n zone.removeEventListener(\"keydown\", onKey);\n document.removeEventListener(\"mousedown\", onDocMouseDown, true);\n const next = structuredClone(getSetup());\n next[kind][slot] = newTemplate;\n setSetup(next);\n onExit();\n };\n\n const onBlur = () => finish();\n const onKey = (e: KeyboardEvent) => {\n if (e.key === \"Escape\" || e.key === \"Enter\") {\n e.preventDefault();\n finish();\n }\n };\n // Any mousedown outside the zone commits — but spare the gutter\n // indicator: BlockTools wants the click to TOGGLE the zone closed,\n // and that path will call `finish()` itself. If we committed here too\n // we'd double-fire and lose the indicator's mousedown semantics.\n const onDocMouseDown = (e: MouseEvent) => {\n const target = e.target as Node;\n if (zone.contains(target)) return;\n if (target instanceof Element && target.closest(\".sobree-block-indicator\")) return;\n finish();\n };\n\n zone.addEventListener(\"blur\", onBlur);\n zone.addEventListener(\"keydown\", onKey);\n document.addEventListener(\"mousedown\", onDocMouseDown, true);\n\n return finish;\n}\n\nfunction resolveTemplateSlot(\n zone: HTMLElement,\n kind: ZoneKind,\n stackRoot: HTMLElement,\n setup: PageSetup,\n): TemplateSlot {\n const papers = Array.from(stackRoot.querySelectorAll(\".paper\"));\n const paper = zone.closest(\".paper\") as HTMLElement | null;\n if (!paper) return \"default\";\n const pageNum = papers.indexOf(paper) + 1;\n const totalPages = papers.length;\n const cfg = setup[kind];\n if (pageNum === 1 && cfg.differentFirst) return \"first\";\n if (pageNum === totalPages && cfg.differentLast && totalPages > 1) return \"last\";\n return \"default\";\n}\n\nfunction placeCursorAtEndOf(node: Text): void {\n const range = document.createRange();\n range.setStart(node, node.length);\n range.collapse(true);\n const sel = window.getSelection();\n sel?.removeAllRanges();\n sel?.addRange(range);\n}\n","/**\n * HeadlessSobree — a no-DOM Sobree peer for LLM agents, automation,\n * back-end pipelines, MCP servers, anywhere code needs to read or\n * write a Sobree document without rendering it.\n *\n * # The shape\n *\n * Same mental model as the browser editor: hold a `Y.Doc`, read it\n * as a `SobreeDocument` projection, write through typed mutation\n * methods, get a `change` event on every update (local or remote).\n * The difference: no DOM, no rendering, no selection-from-DOM logic.\n * Selection is just a value field you can read / write.\n *\n * # Use cases\n *\n * 1. **LLM agents.** Connect a Y-websocket provider to a Sobree\n * room; the LLM sees the same doc the user sees. It can read\n * structure (paragraphs, headings, runs) and apply edits that\n * propagate back through Y.\n *\n * 2. **Server-side rendering / export.** Run `editor.toDocx()` in\n * a Node worker against a snapshot loaded from `@sobree/collab-server`'s\n * persistence backend.\n *\n * 3. **Automation pipelines.** Cron job that processes inbound\n * content and writes formatted reports to a Sobree doc.\n *\n * 4. **Tests.** Build a fixture document programmatically without\n * a DOM environment.\n *\n * # What it's NOT\n *\n * - A *relay*. That's `@sobree/collab-server` — a Node server that\n * fans out Y messages between many peers. HeadlessSobree is a\n * single peer with its own Y.Doc.\n * - A *full editor*. The browser `Editor` adds DOM rendering,\n * contentEditable event handling, image-resize handles, paste\n * parsing, etc. HeadlessSobree skips all that — if you need\n * them, mount a real Editor.\n * - A *table editor*. The browser `Editor` has a rich table API\n * (`editor.table.insertRow`, etc.). HeadlessSobree v0 doesn't\n * wrap that — operate on `Table` blocks directly via\n * `replaceBlock` until Phase 4.x adds a parallel table API.\n *\n * # Origin tagging\n *\n * Every mutation writes to the Y.Doc with a configurable origin\n * (default `\"headless\"`). The local `Y.UndoManager` tracks only that\n * origin — so the peer's `Cmd+Z`-equivalent (`peer.history.undo()`)\n * reverses only its own edits, not the human peers'. Pick a stable\n * per-peer origin string (e.g. `\"agent:gpt-4-2024-05\"`) if you want\n * post-hoc telemetry to identify the author.\n */\n\nimport * as Y from \"yjs\";\nimport { BlobCache, type BlobStore } from \"./blob\";\nimport {\n type BlockRef,\n type EditError,\n type EditResult,\n type Selection,\n fail,\n lockConflict,\n ok,\n} from \"./doc/api\";\nimport { emptyDocument } from \"./doc/builders\";\nimport { type ParagraphPropertiesPatch, EditorCommands } from \"./editor\";\nimport type { BlockInfo, CommandBus, OutlineItem } from \"./editor\";\nimport { BlockRegistry } from \"./editor/internal/blockRegistry\";\nimport {\n type Mutation,\n mergeParagraphProps,\n mergeSectionsAcross,\n removedSectionIndex,\n} from \"./editor/internal/mutations\";\nimport { runsLength } from \"./doc/runs\";\nimport type {\n Block,\n ParagraphAlignment,\n ParagraphProperties,\n SobreeDocument,\n} from \"./doc/types\";\nimport { headingLevelOf, runsToText } from \"./doc/walk\";\nimport { History } from \"./history\";\nimport { applyDocumentToYDoc, projectYDoc, seedYDoc } from \"./ydoc\";\n\nexport interface HeadlessSobreeOptions {\n /**\n * Origin string used for this peer's Y.Doc mutations. Identifies\n * the source in `afterTransaction` events and scopes the\n * Y.UndoManager. Default `\"headless\"`.\n */\n origin?: string;\n /**\n * Initial document. Used only if the Y.Doc is empty at construction\n * time — same semantics as `Editor.initialDocument`. If the Y.Doc\n * is non-empty (a peer joining an active room), this is ignored\n * and the existing state is adopted.\n */\n initialDocument?: SobreeDocument;\n /**\n * Override the BlockRegistry's id prefix. Default is\n * `${ydoc.clientID.toString(36)}_` — same convention the browser\n * `Editor` uses, so newly-minted block ids never collide across\n * peers.\n */\n idPrefix?: string;\n /**\n * Optional content-hashed `BlobStore` (Phase 3.2+). Without one,\n * binary parts ride inline in the Y.Doc; with one, the headless\n * peer resolves `partRefs` hashes via a local `BlobCache` and\n * fetches missing bytes from the store on demand. See\n * `EditorOptions.blobStore` for the full contract.\n */\n blobStore?: BlobStore;\n}\n\nexport type HeadlessEvent = \"change\";\nexport interface HeadlessChangePayload {\n doc: SobreeDocument;\n /** Whether the change originated from THIS peer's mutations\n * (true) or arrived via a Y provider from a remote peer (false). */\n local: boolean;\n}\nexport type HeadlessUnsubscribe = () => void;\n\n/**\n * Headless Sobree peer. Construct with a `Y.Doc` — typically one\n * you've already attached a provider to. Reads project through the\n * Y.Doc; writes mirror back inside `Y.Doc.transact` with the\n * configured origin.\n */\nexport class HeadlessSobree {\n readonly ydoc: Y.Doc;\n readonly commands: CommandBus;\n readonly history: History;\n readonly origin: string;\n /** Optional content-hashed blob layer. Mirrors the browser `Editor`'s\n * `blobStore` field — null when no store is configured. */\n readonly blobStore: BlobStore | null;\n /** Local cache for blob bytes. Null when no `blobStore` is set. */\n readonly blobCache: BlobCache | null;\n\n private doc: SobreeDocument;\n private readonly registry: BlockRegistry;\n private currentSelection: Selection = null;\n private readonly listeners: { change: Set<(p: HeadlessChangePayload) => void> } = {\n change: new Set(),\n };\n private lastPartRefs: Record<string, string> = {};\n private ydocUpdateListener: ((tr: Y.Transaction) => void) | null = null;\n\n constructor(ydoc: Y.Doc, opts: HeadlessSobreeOptions = {}) {\n this.ydoc = ydoc;\n this.origin = opts.origin ?? \"headless\";\n this.commands = new EditorCommands();\n this.registry = new BlockRegistry({\n idPrefix: opts.idPrefix ?? `${ydoc.clientID.toString(36)}_`,\n });\n\n this.blobStore = opts.blobStore ?? null;\n this.blobCache = this.blobStore\n ? new BlobCache({\n store: this.blobStore,\n onResolved: (hash) => this.onBlobResolved(hash),\n })\n : null;\n\n // Adopt-or-seed: same logic as the browser Editor. If the Y.Doc\n // already has body content, we're a peer joining an active room\n // and we adopt verbatim. Otherwise seed from `initialDocument`\n // (or an empty doc).\n const ydocBody = this.ydoc.getArray<Y.Map<unknown>>(\"body\");\n if (ydocBody.length === 0) {\n this.doc = opts.initialDocument ?? emptyDocument();\n this.registry.reset(this.doc.body.length);\n seedYDoc(this.ydoc, this.doc, this.allBlockIds());\n this.lastPartRefs = {};\n } else {\n const projected = projectYDoc(this.ydoc);\n this.doc = projected.doc;\n this.registry.adoptIds(projected.ids);\n this.lastPartRefs = projected.partRefs;\n this.resolveCachedPartRefsInto(this.doc);\n }\n\n // Y.UndoManager-backed history, scoped to this peer's origin.\n // Selection is captured / restored against `currentSelection` —\n // useful for an agent that wants to remember where it was\n // \"looking\" before each edit and snap back on undo.\n this.history = new History({\n ydoc: this.ydoc,\n localOrigin: this.origin,\n captureSelection: () => this.currentSelection,\n restoreSelection: (sel) => {\n this.currentSelection = sel;\n },\n });\n\n // Register the same history commands as the browser editor — so\n // an MCP wrapper can `commands.execute(\"history.undo\")` and get\n // the same behaviour.\n this.commands.register({\n name: \"history.undo\",\n title: \"Undo\",\n run: () => {\n this.history.undo();\n },\n isAvailable: () => this.history.canUndo(),\n });\n this.commands.register({\n name: \"history.redo\",\n title: \"Redo\",\n run: () => {\n this.history.redo();\n },\n isAvailable: () => this.history.canRedo(),\n });\n\n // Remote-update observer: re-project + fire change for any\n // Y.Doc transaction NOT originating from this peer.\n this.ydocUpdateListener = (tr: Y.Transaction) => {\n if (tr.origin === this.origin || tr.origin === \"seed\") return;\n this.adoptYDocState();\n this.fireChange(false);\n };\n this.ydoc.on(\"afterTransaction\", this.ydocUpdateListener);\n }\n\n // === reads ===\n\n /** Current document — a fresh projection of the Y.Doc state. */\n getDocument(): SobreeDocument {\n return this.doc;\n }\n\n /** Summary of every top-level block. */\n getBlocks(): BlockInfo[] {\n return this.doc.body.map((block, index) => this.summariseBlock(block, index));\n }\n\n getBlock(index: number): BlockInfo {\n const blocks = this.getBlocks();\n const b = blocks[index];\n if (!b) throw new Error(`block index ${index} out of range`);\n return b;\n }\n\n getBlockById(id: string): BlockInfo | null {\n const index = this.registry.indexOf(id);\n if (index < 0) return null;\n return this.getBlock(index);\n }\n\n /** Heading outline — one entry per `paragraph` block whose\n * resolved style identifies it as a heading. */\n getOutline(): OutlineItem[] {\n const out: OutlineItem[] = [];\n this.doc.body.forEach((block, index) => {\n if (block.kind !== \"paragraph\") return;\n const level = headingLevelOf(block);\n if (!level) return;\n out.push({\n level,\n text: runsToText(block.runs),\n blockIndex: index,\n block: this.registry.refAt(index),\n });\n });\n return out;\n }\n\n /** This peer's stored selection — same shape `editor.selection.get()`\n * returns. `null` when no selection is set. */\n getSelection(): Selection {\n return this.currentSelection;\n }\n\n /**\n * Set this peer's selection. Stored as a value (no DOM update);\n * the value is what `history` captures on each mutation and\n * restores on undo.\n */\n setSelection(selection: Selection): void {\n this.currentSelection = selection;\n }\n\n // === mutations ===\n\n /** Replace the document. */\n setDocument(doc: SobreeDocument): void {\n this.doc = doc;\n this.registry.reset(doc.body.length);\n this.mirror();\n this.fireChange(true);\n }\n\n /** Replace the block at `target`'s index with `block`. */\n replaceBlock(target: BlockRef, block: Block): EditResult<BlockRef> {\n const lockCheck = this.checkRefs([target]);\n if (lockCheck) return lockCheck;\n const index = this.registry.indexOf(target.id);\n const next = this.doc.body.slice();\n const wasSectionBreak = next[index]?.kind === \"section_break\";\n next[index] = block;\n const update: Partial<SobreeDocument> = { body: next };\n if (wasSectionBreak && block.kind !== \"section_break\") {\n update.sections = mergeSectionsAcross(\n this.doc.sections,\n removedSectionIndex(this.doc.body, index),\n );\n }\n return this.commit(update, [{ type: \"bump\", index }]);\n }\n\n /** Insert `block` before the target block. */\n insertBlockBefore(target: BlockRef, block: Block): EditResult<BlockRef> {\n const lockCheck = this.checkRefs([target]);\n if (lockCheck) return lockCheck;\n const index = this.registry.indexOf(target.id);\n const next = this.doc.body.slice();\n next.splice(index, 0, block);\n return this.commit({ body: next }, [{ type: \"insert\", index }]);\n }\n\n /** Insert `block` after the target block. */\n insertBlockAfter(target: BlockRef, block: Block): EditResult<BlockRef> {\n const lockCheck = this.checkRefs([target]);\n if (lockCheck) return lockCheck;\n const index = this.registry.indexOf(target.id);\n const next = this.doc.body.slice();\n next.splice(index + 1, 0, block);\n return this.commit({ body: next }, [{ type: \"insert\", index: index + 1 }]);\n }\n\n /** Delete the target block. */\n deleteBlock(target: BlockRef): EditResult<void> {\n const lockCheck = this.checkRefs([target]);\n if (lockCheck) return lockCheck;\n const index = this.registry.indexOf(target.id);\n const wasSectionBreak = this.doc.body[index]?.kind === \"section_break\";\n const next = this.doc.body.slice();\n next.splice(index, 1);\n if (next.length === 0)\n next.push({ kind: \"paragraph\", properties: {}, runs: [] });\n const update: Partial<SobreeDocument> = { body: next };\n if (wasSectionBreak) {\n update.sections = mergeSectionsAcross(\n this.doc.sections,\n removedSectionIndex(this.doc.body, index),\n );\n }\n return this.commit(update, [{ type: \"remove\", index }]);\n }\n\n /** Merge a patch into each target paragraph's properties. */\n applyBlockProperties(\n targets: BlockRef[],\n patch: ParagraphPropertiesPatch,\n ): EditResult<void> {\n const lockCheck = this.checkRefs(targets);\n if (lockCheck) return lockCheck;\n const next = this.doc.body.slice();\n const bumps: Mutation[] = [];\n for (const ref of targets) {\n const index = this.registry.indexOf(ref.id);\n const block = next[index];\n if (!block) continue;\n if (block.kind !== \"paragraph\") {\n return fail({\n code: \"invalid-state\",\n details: `block ${ref.id} is not a paragraph`,\n });\n }\n next[index] = {\n ...block,\n properties: mergeParagraphProps(block.properties, patch),\n };\n bumps.push({ type: \"bump\", index });\n }\n return this.commit({ body: next }, bumps);\n }\n\n // === events ===\n\n on<E extends HeadlessEvent>(\n event: E,\n cb: (payload: HeadlessChangePayload) => void,\n ): HeadlessUnsubscribe {\n if (event !== \"change\") {\n throw new Error(`unknown event: ${String(event)}`);\n }\n this.listeners.change.add(cb);\n return () => this.listeners.change.delete(cb);\n }\n\n // === lifecycle ===\n\n destroy(): void {\n if (this.ydocUpdateListener) {\n this.ydoc.off(\"afterTransaction\", this.ydocUpdateListener);\n this.ydocUpdateListener = null;\n }\n this.history.destroy();\n this.listeners.change.clear();\n }\n\n // === internals ===\n\n private allBlockIds(): string[] {\n const out: string[] = [];\n for (let i = 0; i < this.registry.length(); i++) {\n out.push(this.registry.refAt(i).id);\n }\n return out;\n }\n\n private mirror(): void {\n applyDocumentToYDoc(this.ydoc, this.doc, this.allBlockIds(), this.origin);\n }\n\n private adoptYDocState(): void {\n const projected = projectYDoc(this.ydoc);\n this.doc = projected.doc;\n this.registry.adoptIds(projected.ids);\n this.lastPartRefs = projected.partRefs;\n this.resolveCachedPartRefsInto(this.doc);\n if (this.blobCache) {\n const missing = Object.values(projected.partRefs).filter(\n (h) => !this.blobCache!.has(h),\n );\n if (missing.length > 0) {\n void this.blobCache.ensureLoaded(missing);\n }\n }\n }\n\n private resolveCachedPartRefsInto(doc: SobreeDocument): void {\n if (!this.blobCache) return;\n for (const [path, hash] of Object.entries(this.lastPartRefs)) {\n if (doc.rawParts[path]) continue;\n const bytes = this.blobCache.get(hash);\n if (bytes) doc.rawParts[path] = bytes;\n }\n }\n\n private onBlobResolved(hash: string): void {\n if (!this.blobCache) return;\n let touched = false;\n for (const [path, refHash] of Object.entries(this.lastPartRefs)) {\n if (refHash !== hash) continue;\n const bytes = this.blobCache.get(hash);\n if (bytes && !this.doc.rawParts[path]) {\n this.doc.rawParts[path] = bytes;\n touched = true;\n }\n }\n if (touched) this.fireChange(false);\n }\n\n /**\n * Wait for every currently-referenced binary part to be available\n * in the local cache. No-op when no `blobStore` is configured.\n */\n async ensurePartsLoaded(): Promise<void> {\n if (!this.blobCache) return;\n const hashes = Object.values(this.lastPartRefs);\n if (hashes.length === 0) return;\n await this.blobCache.ensureLoaded(hashes);\n this.resolveCachedPartRefsInto(this.doc);\n }\n\n private checkRefs(refs: readonly BlockRef[]): EditResult<never> | null {\n for (const ref of refs) {\n const current = this.registry.refById(ref.id);\n if (!current) {\n return fail({\n code: \"invalid-position\",\n details: `block id ${ref.id} not found`,\n });\n }\n if (current.version !== ref.version) {\n return lockConflict([\n { blockId: ref.id, expected: ref.version, actual: current.version },\n ]);\n }\n }\n return null;\n }\n\n private commit<T = void>(\n update: Partial<SobreeDocument>,\n mutations: readonly Mutation[],\n value?: T,\n ): EditResult<T> {\n const next: SobreeDocument = { ...this.doc, ...update };\n const affected: BlockRef[] = [];\n for (const m of mutations) {\n if (m.type === \"insert\") affected.push(this.registry.insert(m.index));\n else if (m.type === \"remove\") this.registry.remove(m.index);\n else if (m.type === \"bump\") affected.push(this.registry.bump(m.index));\n }\n this.doc = next;\n this.mirror();\n this.fireChange(true);\n return ok<T>(value as T, affected);\n }\n\n private fireChange(local: boolean): void {\n if (this.listeners.change.size === 0) return;\n const payload: HeadlessChangePayload = { doc: this.doc, local };\n for (const cb of this.listeners.change) {\n try {\n cb(payload);\n } catch (err) {\n console.error(\"[headless] change listener threw:\", err);\n }\n }\n }\n\n private summariseBlock(block: Block, index: number): BlockInfo {\n const ref = this.registry.refAt(index);\n if (block.kind === \"paragraph\") {\n const text = runsToText(block.runs);\n const length = runsLength(block.runs);\n const info: BlockInfo = {\n index,\n id: ref.id,\n version: ref.version,\n kind: block.kind,\n text,\n length,\n };\n if (block.properties.styleId) info.styleId = block.properties.styleId;\n if (block.properties.alignment) info.alignment = block.properties.alignment;\n return info;\n }\n if (block.kind === \"table\") {\n return {\n index,\n id: ref.id,\n version: ref.version,\n kind: block.kind,\n text: \"[table]\",\n length: 0,\n };\n }\n // section_break\n return {\n index,\n id: ref.id,\n version: ref.version,\n kind: block.kind,\n text: \"[section break]\",\n length: 0,\n };\n }\n}\n\n// Re-exports for caller convenience — anything they need to build\n// blocks or read EditResult is here under one import.\nexport type {\n Block,\n BlockInfo,\n BlockRef,\n EditError,\n EditResult,\n ParagraphAlignment,\n ParagraphProperties,\n Selection,\n SobreeDocument,\n};\n","/**\n * Awareness state shapes used by Sobree's presence layer.\n *\n * The Yjs Awareness protocol gives each peer a `Map<clientID, state>`\n * with arbitrary JSON values. Sobree standardizes on the shape below\n * so plugins from different vendors can render compatible overlays.\n *\n * Wire snippet:\n *\n * ```ts\n * awareness.setLocalState({\n * user: { id: \"alice\", name: \"Alice\", color: \"#f59e0b\" },\n * selection: { blockId: \"1f3a_2\", anchor: 4, focus: 12 },\n * });\n * ```\n */\n\nimport type { Selection as EditorSelection } from \"../doc/api\";\n\n/** Per-peer state published to other peers via Yjs awareness. */\nexport interface PresenceState {\n user: PresenceUser;\n /**\n * Where this peer's cursor / range sits. `null` if focus left the\n * editor. Position is identified by `blockId` (stable across the\n * Y.Doc — see `BlockRegistry`) + character offsets, so it survives\n * re-pagination and structural mutations.\n */\n selection: PresenceSelection | null;\n}\n\nexport interface PresenceUser {\n /** Stable peer id (a UUID, an auth user id, etc). Random per page-load\n * is fine if you don't have auth. */\n id: string;\n /** Display name for overlays / tooltips. */\n name: string;\n /** CSS color for the caret / range highlight (e.g. \"#f59e0b\"). */\n color: string;\n}\n\nexport interface PresenceSelection {\n /** BlockRegistry id of the focused block. Both `anchor` and `focus`\n * point inside this block (cross-block selections are clipped to\n * the focus block for the overlay). */\n blockId: string;\n /** Caret position when anchor === focus; range start otherwise. */\n anchor: number;\n /** Caret position when anchor === focus; range end otherwise. */\n focus: number;\n}\n\n/** Convenience view: the editor's live `Selection` collapsed to a\n * `PresenceSelection` referencing the focused block by id. */\nexport function presenceSelectionFromEditor(\n sel: EditorSelection,\n): PresenceSelection | null {\n if (!sel) return null;\n if (sel.kind === \"caret\") {\n return {\n blockId: sel.at.block.id,\n anchor: sel.at.offset,\n focus: sel.at.offset,\n };\n }\n // range — use the focus (`to`) block as the anchor; clip the\n // selection to within that block.\n const focusBlock = sel.range.to.block;\n const sameBlock = sel.range.from.block.id === focusBlock.id;\n return {\n blockId: focusBlock.id,\n anchor: sameBlock ? sel.range.from.offset : sel.range.to.offset,\n focus: sel.range.to.offset,\n };\n}\n\n/** Type guard — narrows an unknown awareness state value to PresenceState. */\nexport function isPresenceState(value: unknown): value is PresenceState {\n if (!value || typeof value !== \"object\") return false;\n const v = value as Record<string, unknown>;\n if (!v.user || typeof v.user !== \"object\") return false;\n const u = v.user as Record<string, unknown>;\n if (typeof u.id !== \"string\" || typeof u.name !== \"string\" || typeof u.color !== \"string\") {\n return false;\n }\n if (v.selection !== null && v.selection !== undefined) {\n if (typeof v.selection !== \"object\") return false;\n const s = v.selection as Record<string, unknown>;\n if (typeof s.blockId !== \"string\") return false;\n if (typeof s.anchor !== \"number\") return false;\n if (typeof s.focus !== \"number\") return false;\n }\n return true;\n}\n","import type { Editor } from \"../editor\";\nimport type { AwarenessLike, AwarenessChanges } from \"./awareness\";\nimport {\n type PresenceState,\n type PresenceUser,\n isPresenceState,\n presenceSelectionFromEditor,\n} from \"./state\";\n\nexport interface AttachPresenceOptions {\n /** This peer's identity. Published in the local state's `user` field. */\n user: PresenceUser;\n /**\n * Called whenever any peer's presence (including this one) changes.\n * The map is `clientID → PresenceState` for every peer currently\n * publishing valid Sobree presence. The local peer's clientID\n * (matches `awareness.clientID` / `editor.ydoc.clientID`) is\n * included so callers can render their own caret too if they want.\n */\n onChange?: (peers: Map<number, PresenceState>) => void;\n /**\n * Default to `true` — publish the local user's selection on every\n * `editor.on(\"selection\")` event. Pass `false` if the caller wants\n * to manage own-selection publishing manually.\n */\n publishOwnSelection?: boolean;\n}\n\nexport interface PresenceHandle {\n /** Snapshot of every peer's current state, keyed by clientID. */\n getPeers(): Map<number, PresenceState>;\n /**\n * Push a manual state update. Useful when the local user changes\n * name / color, or to explicitly clear the selection on focus loss.\n */\n setLocalState(state: Partial<PresenceState>): void;\n /** Tear down: unsubscribe from awareness + editor events. Clears\n * the local published state so peers see the user leave. */\n destroy(): void;\n}\n\n/**\n * Wire an `Awareness` instance into the editor.\n *\n * - Publishes the local user's `user` + `selection` state on every\n * selection change.\n * - Subscribes to remote peers' state via awareness `\"change\"`\n * events; surfaces them via `onChange(peers)` and `getPeers()`.\n *\n * Does NOT render an overlay — pass the peers to your own renderer,\n * or wire `attachPresenceOverlay` from this package for the default\n * caret + range-highlight rendering.\n *\n * Returns a `PresenceHandle` with manual-update and teardown.\n */\nexport function attachPresence(\n editor: Editor,\n awareness: AwarenessLike,\n opts: AttachPresenceOptions,\n): PresenceHandle {\n const publishOwn = opts.publishOwnSelection ?? true;\n\n const initialState: PresenceState = {\n user: opts.user,\n selection: publishOwn\n ? presenceSelectionFromEditor(editor.selection.get())\n : null,\n };\n awareness.setLocalState(initialState as unknown as Record<string, unknown>);\n\n // Re-publish on selection change.\n let detachSelection: (() => void) | null = null;\n if (publishOwn) {\n detachSelection = editor.on(\"selection\", () => {\n const sel = editor.selection.get();\n awareness.setLocalStateField(\n \"selection\",\n presenceSelectionFromEditor(sel) as unknown as Record<string, unknown> | null,\n );\n });\n }\n\n // Notify caller on every change.\n const peers = (): Map<number, PresenceState> => {\n const out = new Map<number, PresenceState>();\n for (const [id, state] of awareness.getStates()) {\n if (isPresenceState(state)) out.set(id, state);\n }\n return out;\n };\n\n const awarenessChangeListener = (_changes: AwarenessChanges) => {\n if (opts.onChange) opts.onChange(peers());\n };\n awareness.on(\"change\", awarenessChangeListener);\n // Fire once initially so the caller sees the starting state.\n if (opts.onChange) opts.onChange(peers());\n\n return {\n getPeers: peers,\n setLocalState(patch: Partial<PresenceState>): void {\n if (patch.user !== undefined) {\n awareness.setLocalStateField(\n \"user\",\n patch.user as unknown as Record<string, unknown>,\n );\n }\n if (\"selection\" in patch) {\n awareness.setLocalStateField(\n \"selection\",\n patch.selection as unknown as Record<string, unknown> | null,\n );\n }\n },\n destroy(): void {\n awareness.off(\"change\", awarenessChangeListener);\n detachSelection?.();\n awareness.setLocalState(null);\n },\n };\n}\n","/**\n * Default DOM overlay for remote peer cursors + selection ranges.\n *\n * Each peer gets one `<div class=\"sobree-caret\">` (a thin vertical\n * bar with the peer's name label) and zero-or-more\n * `<div class=\"sobree-range\">` boxes (one per rendered line inside\n * the selection). Positioned absolute inside a container that\n * `position: relative`'s the editor host. Re-rendered on every\n * presence / editor change.\n *\n * This is best-effort — in jsdom (test env) `getBoundingClientRect`\n * returns zeros, so the overlay is \"drawn\" but invisible. Visual\n * verification belongs in the playground / browser tests.\n */\n\nimport type { Editor } from \"../editor\";\nimport type { AwarenessLike } from \"./awareness\";\nimport { attachPresence, type PresenceHandle, type AttachPresenceOptions } from \"./attach\";\nimport type { PresenceState } from \"./state\";\n\nexport interface AttachPresenceOverlayOptions extends AttachPresenceOptions {\n /**\n * Element to render the overlay into. Should be an ancestor of the\n * paper stack and `position: relative` (or `position: absolute`).\n * The factory adds `position: relative` if `static`. Most embedders\n * pass `createSobree(...).viewport.slot`.\n */\n container: HTMLElement;\n /**\n * Element whose `data-block-id` lookup resolves to block DOM\n * elements. Default: `container`. Pass a different host if your\n * paper stack is layered separately.\n */\n blockHost?: HTMLElement;\n}\n\nexport interface PresenceOverlayHandle {\n /** Underlying presence handle — read peers, push manual updates. */\n readonly presence: PresenceHandle;\n /** Force re-render (e.g. after a layout-affecting change the\n * overlay didn't observe directly). */\n refresh(): void;\n /** Tear down DOM + listeners. */\n destroy(): void;\n}\n\nconst OVERLAY_CLASS = \"sobree-presence-overlay\";\n\nexport function attachPresenceOverlay(\n editor: Editor,\n awareness: AwarenessLike,\n opts: AttachPresenceOverlayOptions,\n): PresenceOverlayHandle {\n const container = opts.container;\n const blockHost = opts.blockHost ?? container;\n\n // Ensure container can position absolute children.\n const cs = getComputedStyle(container);\n if (cs.position === \"static\") container.style.position = \"relative\";\n\n // Mount overlay root.\n const overlay = document.createElement(\"div\");\n overlay.className = OVERLAY_CLASS;\n overlay.style.cssText =\n \"position:absolute; inset:0; pointer-events:none; z-index:2;\";\n container.appendChild(overlay);\n\n let lastPeers: Map<number, PresenceState> = new Map();\n\n const render = (peers: Map<number, PresenceState>): void => {\n lastPeers = peers;\n overlay.replaceChildren();\n for (const [clientId, state] of peers) {\n // Skip the local peer's own caret — they see their real one.\n if (clientId === awareness.clientID) continue;\n if (!state.selection) continue;\n const blockEl = blockHost.querySelector<HTMLElement>(\n `[data-block-id=\"${cssEscape(state.selection.blockId)}\"]`,\n );\n if (!blockEl) continue;\n const rect = blockEl.getBoundingClientRect();\n const cRect = container.getBoundingClientRect();\n // Crude positioning: top-left of the focus block, color band on\n // the left edge. A future iteration will resolve `anchor`/`focus`\n // offsets to character rects via `Range.getClientRects()`.\n const caret = document.createElement(\"div\");\n caret.className = \"sobree-caret\";\n caret.style.cssText =\n `position:absolute; top:${rect.top - cRect.top}px; left:${rect.left - cRect.left - 2}px; ` +\n `width:2px; height:${rect.height}px; background:${state.user.color}; opacity:0.85;`;\n const label = document.createElement(\"span\");\n label.className = \"sobree-caret-label\";\n label.textContent = state.user.name;\n label.style.cssText =\n `position:absolute; top:-1.4em; left:0; padding:0 4px; ` +\n `background:${state.user.color}; color:#fff; font:11px/1.4 system-ui, sans-serif; ` +\n `border-radius:3px 3px 3px 0; white-space:nowrap;`;\n caret.appendChild(label);\n overlay.appendChild(caret);\n }\n };\n\n const presence = attachPresence(editor, awareness, {\n ...opts,\n onChange(peers) {\n render(peers);\n if (opts.onChange) opts.onChange(peers);\n },\n });\n\n // Re-render when the document changes — blocks may have moved /\n // resized; cached rects are stale.\n const detachChange = editor.on(\"change\", () => render(lastPeers));\n\n return {\n presence,\n refresh(): void {\n render(lastPeers);\n },\n destroy(): void {\n detachChange();\n presence.destroy();\n overlay.remove();\n },\n };\n}\n\n// CSS.escape isn't in jsdom by default; fall back to a manual quote.\nfunction cssEscape(s: string): string {\n if (typeof CSS !== \"undefined\" && typeof CSS.escape === \"function\") {\n return CSS.escape(s);\n }\n return s.replace(/([\"\\\\])/g, \"\\\\$1\");\n}\n"],"names":["attachSections","editor","insertSectionBreakAfterCaret","caret","block","result","doc","sections","lastIdx","lastSection","PAGE_SIZES","DEFAULT_PAGE_SETUP","resolvedDimensions","setup","width","height","substituteVariables","template","ctx","zoneTemplateFor","zone","page","pages","measureParagraphLines","el","totalChars","countTextChars","range","lines","clusterLineRects","logicalHeight","starts","i","findLineStartOffset","lineHeight","_l","targetLine","lo","hi","mid","countLinesUpTo","charOffset","pos","nodeAtCharOffset","walker","total","node","last","t","rects","r","key","existing","l","LINE_CLUSTER_TOL_PX","a","b","buildItems","blocks","items","prevBottom","cs","isOutOfFlow","gapBefore","isPageBreakMarker","isKeepTogetherGroup","hasPageBreakBefore","singleBox","paragraphLineItems","ensureParagraphId","listItemBoxes","ensureListId","tableRowBoxes","ensureTableId","extraFlagsFor","fresh","table","tid","trs","section","sec","child","TALL_ROW_THRESHOLD_PX","out","tr","tallRowParagraphBoxes","cells","c","dominant","domCount","cellParagraphs","paras","p","measureBlockHeight","cell","list","lid","lis","li","pid","lineItems","keepNext","line","isLast","box","extra","tag","flags","marginTop","splitElementAtCharOffset","fragment","clone","attr","_a","snapToWordBoundary","offset","text","distributePages","elementPages","pageIdx","it","m","fragments","original","lineMap","splitParagraphAcrossPages","markContinuedFragments","liLogicalNumber","seenSourceLists","sourceList","isListContainer","indexLogicalNumbers","sourceListsTouched","sourceTrsTouched","seen","pageElements","openContainerSource","openContainerClone","openTrSource","openTrClone","sourceTable","sourceTbody","cloneTableContainer","cloneEmptyRow","sourceCell","cellChildren","cellIdx","targetCell","srcCell","tgtCell","startNum","readStartAttr","cloneListContainer","sourceSection","isHeader","target","cloneThead","source","s","pageMap","orderedPages","frag","trClone","cellClone","tbodyClone","raw","logical","lastPid","firstSeen","orderedLines","splitLines","prevPage","currentFragment","currentFragmentStartLine","splitLine","localLine","rawOffset","tail","scoreBreak","start","cfg","leftover","widowOrphanPenalty","keepWithNextPenalty","keepTogetherPenalty","before","nearestBoxBefore","after","nearestBoxAfter","linesAbove","countParagraphLinesOnPageBefore","linesBelow","countParagraphLinesOnNextPage","idxExclusive","idxInclusive","pageEnd","count","nextStart","sumParagraphHeight","startIdx","first","h","next","isGlueBetweenBoxes","idx","isGlueInsideKeepTogether","backOffIfViolates","best","candidates","bestPos","DEFAULTS","resolveConfig","pageHeightAt","paginate","config","pageCfg","fillPage","emit","pickAndEmit","paraH","overflowIdx","bestCost","cost","adjusted","adjustedCost","endExclusive","pageItems","computeUsedHeight","lastBoxIdx","paginateBlocks","pageContentHeightPx","pageHeightsPx","CHAINS","withFallbacks","fontFamily","trimmed","match","chain","needsQuoting","name","appendInlineRuns","parent","runs","rawParts","run","renderRun","renderTextRun","div","span","renderDrawing","renderHyperlink","renderFootnoteRef","renderCommentRef","sup","link","wrap","style","cssFromRunProps","wrapper","d","img","url","partPathToUrl","emuToPx","applyAnchorPositioning","anchor","emuToMm","emu","partPath","bytes","mimeFromPath","bytesToBase64","bytesToObjectUrl","str","path","ext","mime","copy","blob","decls","normaliseHighlight","v","renderTable","numbering","styles","applyTableBorders","rowSpans","computeRowSpans","headRows","x","bodyRows","thead","renderRow","tbody","row","rowIndex","defaultCell","col","gridSpan","renderCell","rs","defaultTag","renderBlocks","tightenDefaultAfterSpacing","cellEl","lh","isGridStyle","explicitlyDeclared","hasAnyBorderSide","inside","insideCss","borderSpecToCss","spec","px","color","grid","maxCol","n","cols","k","j","below","TWIPS_PER_INCH","MM_PER_INCH","EMU_PER_MM","EMU_PER_PX","twipsToMm","twips","resolveStyleCascade","styleId","collectStyleChain","runDefaults","paragraphDefaults","mergeParagraphDefaults","base","over","id","normal","findNormalAnchor","anchorId","byId","byDisplay","applyParagraphProps","props","effectiveStyleId","cascadeRunDefaults","effective","mergeParagraphProperties","naturalLeading","naturalLeadingFor","_b","_c","isLi","_d","_e","side","mapBorderStyle","mapBorderColor","_f","minTwips","paragraphListInfo","num","def","lvl","format","createListContainer","info","sectionIndex","listEl","left","hanging","markerOffset","cssKeyword","cssListStyleForGlyph","glyph","renderInlineFrameBlock","frame","renderBody","scaleX","pic","shape","tb","region","walkBlock","walkRun","inner","runsToText","headingLevelOf","lv","renderParagraph","level","isEmpty","cascadeDominantRunFont","paragraphLeadsWithColumnBreak","paragraphContainsPageBreak","split","splitForTabSpread","isSpaceRun","sepStart","sepEnd","hasText","family","sizePt","host","blockIds","currentList","appendTarget","openColumnContainerIfNeeded","pendingPageBreak","isVisuallyEmptyBlock","flushList","listInfo","stampBlockRevision","nextSectionForBreak","rendered","renderBlock","evictTrailingEmptyParagraphs","collapseSectionTrailerEmpty","sectBreak","trailer","container","isVisuallyEmptyParagraph","nextSection","renderSectionBreak","isContinuous","label","display","rev","renderAnchorLayer","frames","layer","renderFrame","paintContent","paintPicture","paintShape","paintTextbox","paintGroup","content","resolvePictureUrl","applyBorder","frameWidthEmu","frameHeightEmu","scaleY","childEl","border","widthPx","cached","Paper","__publicField","widthMM","heightMM","justifyContentFor","mm","renderZone","headerPx","footerPx","marginTopPx","parsePxFromMm","marginBottomPx","setZoneText","MM_TO_PX","value","substituteFieldNodes","hasContent","pageNumber","totalPages","fields","field","instr","saveSelection","sel","restoreSelection","saved","MAX_REPAGINATE_RETRIES","OVERFLOW_TOLERANCE_PX","PaperStack","cb","tier","baselineBudgetPx","pageHeights","attempt","newHeights","stable","arraysEqual","overflowPx","bodies","footnoteIdFromAttr","aside","paper","refs","ref","budgetPx","firstContent","mergeConsecutiveFragments","consolidatedBlocks","rawPages","collapseTrailingEmptyPages","pageCount","pageBlocks","heights","fnH","budget","max","used","footnoteH","effectiveBudget","overflow","err","firstPaper","contentHeightPx","top","bottom","pageNum","sectionIdx","isFirstOfSection","headerBlocks","footerBlocks","hTpl","fTpl","richZones","perPage","paragraphPage","found","bucket","stamped","paperIdx","kind","lookupIdx","candidate","preferred","canMergeFragments","mergeInto","mergeListItemFragments","mergeTableBodyFragments","tbodies","head","A4_WIDTH_TWIPS","A4_HEIGHT_TWIPS","ONE_INCH_TWIPS","emptyDocument","paragraph","defaultSection","defaultStyles","defaultPageSize","defaultMargins","properties","heading","emphasis","strong","softBreak","pageBreak","headingSizes","size","appendBlock","makeHeaderFooterRef","type","partId","isParagraph","isTable","MM_TO_TWIPS","pageSetupToSection","pageSizeToTwips","marginsToTwips","fileIdx","attach","templateToBlocks","zoneHasContent","sectionToPageSetup","orientation","matchPageSize","roundMm","header","refsToZoneText","footer","differentFirst","body","blocksToTemplate","widthMm","heightMm","widthTwips","heightTwips","portrait","w","bestDist","z","lineToParagraph","regex","textRun","makeExportContext","startRid","nextRevisionId","allocImageRel","toWordRelativePath","nextDocPr","allocHyperlinkRel","href","NS","ROOT_DOCUMENT_ATTRS","parseXml","src","wFirst","root","localName","wAll","wChildren","wVal","xmlDocument","rootXml","attrs","children","escapeAttr","escapeXmlText","REL_TYPES","renderContentTypesXml","overrides","imageExtensions","imageMimeFromExtension","o","renderRootRelsXml","renderDocumentRelsXml","extras","stylesRel","extraRels","e","lower","rId","docPrId","cx","cy","descr","blip","blipFill","nvPicPr","spPr","graphicData","graphic","extent","docPr","inline","halfPtToPt","ptToHalfPt","pt","ooxmlLineHeightToCss","inlinesToRuns","inlines","parts","revisionOf","emitInline","r2","wrapRevision","insideDel","emitTextRun","emitBreak","emitRun","emitField","emitHyperlink","emitDrawing","innerRuns","padded","propsToRPr","bodyElements","rPr","rprChildElements","rf","beforeChildren","innerRPr","stripHash","cssHighlightToWord","hp","css","hex","knownByHex","renderDocumentXml","sectPrXmls","trailingSectPr","computeTrailingSectPr","finalSectPrXml","bodyChildren","trailing","map","xml","pPr","renderPPr","rows","renderTableRow","trPr","renderTableCell","tcPr","HEADER_CT","FOOTER_CT","emitHeadersAndFooters","renderSectPrFallback","partIdToRid","emitForSection","headerRefXml","footerRefXml","renderHeaderFooterXml","renderSectPr","rootTag","headerRefs","footerRefs","renderStylesXml","styleElement","isDefault","runPropertiesToXml","encoder","DOCX_MIME","packageDocx","files","zipSync","fontLivenessPaths","f","collectLivePartPaths","live","collectFromBlock","pruneOrphanParts","pruned","ALL_ZERO_KEY","deobfuscate","fontKey","transform","obfuscate","generateFontKey","isUnobfuscated","normaliseKey","keyToReversedBytes","limit","fwd","readFsType","font","view","sfnt","numTables","dirEnd","OS2_TAG","os2Offset","entry","canEmbed","fsType","licence","noSubset","bitmapOnly","mode","mountFontTableFromZip","textParts","parseRels","fontTableXml","fontTableRelsXml","rels","parseFontTable","fontTableRels","fonts","declarations","embeddedPartPaths","fEl","decl","altName","panose","charset","pitch","sigEl","sig","readSig","embed","readEmbed","get","usb0","usb1","usb2","usb3","csb0","csb1","slots","any","readEmbedRef","embedEl","rid","subsetted","FONT_REL_TYPE","FONT_TABLE_CT","mountFontTableArtifacts","emission","emitFontTable","fontTableRid","hasObfuscatedEmbed","nextRid","fontRels","fontEls","renderFontEl","relEls","allocRid","embedFontIntoDoc","faces","opts","warnings","nextRawParts","allocateFontPath","existingIdx","nextFonts","removeFontFromDoc","FontFaceRegistry","fontList","serialiseKey","rules","descriptor","obfuscated","buildFontFaceRule","escapeFontFamily","exportDocx","documentXml","pkg","readRun","brEl","typeAttr","drawing","readDrawing","vmlContainer","readVmlImage","footnoteRef","idAttr","commentRef","normaliseRunText","readRunFormat","rPrChange","author","date","hasBooleanProperty","u","colorEl","highlight","rFonts","sz","vAlign","V_NS","imagedata","widthPt","parseVmlDimension","heightPt","inlineEl","anchorEl","wpRoot","readAnchor","posH","posV","behindDoc","readPosOffset","normaliseRelativeFromH","normaliseRelativeFromV","positionEl","posOffset","val","readShading","shdEl","fillRaw","pattern","fill","colorRaw","readParagraph","activeComments","collectParagraphChildren","readParagraphFormat","revision","fieldState","fieldInstr","fieldCached","flushField","fldChar","instrText","relId","nextRevision","readRevisionAttrs","readCommentId","innerR","cachedText","readMarkRunFormat","styleVal","jcVal","spacing","rule","numPr","numId","ilvl","tabsEl","stops","tab","posAttr","valAttr","leaderAttr","indEl","readTwipAttr","right","firstLine","pBdrEl","borders","space","styleMap","shading","pPr_rPr","markFormat","insEl","delEl","revEl","convertParagraph","mapParagraphFormat","inlineRuns","itemsToInlines","hasLastRenderedPageBreak","stack","item","pushInline","drawingRunFrom","fieldRun","mapRunFormat","makeTextRun","resolveMediaPath","convertTable","tbl","readGrid","readRow","tblPr","tblBorders","readTableBorders","gridEl","tc","readCell","gridSpanEl","vMergeEl","vAlignVal","cellShading","empty","convertDocumentXml","xmlDoc","hintCount","enrichedCtx","convertBlocksFromContainer","sectPrEls","pendingSectionIndex","directChildren","expandSdtWrappers","replaceParagraphs","replacement","inlineSectPr","inlineSectPrOf","xmlSrc","readTwipsAttr","readSection","sectPr","pgSz","pgMar","typeVal","orientAttr","wTwips","hTwips","collectHeaderFooterRefs","sectionCols","TEXT_EXT","decoder","unzipDocx","buffer","unzipSync","binary","parseFootnotesXml","footnote","NS_W15","NS_W14","parseCommentsXml","extendedXml","parseCommentsExtendedXml","paraIdToCommentId","commentIdToFirstParaId","comment","firstParaId","initials","paraId","meta","parentId","exs","ex","done","paraIdParent","parseSettingsXml","legacy","defaultTabStop","shouldApplyAutoSpacing","settings","parseStylesXml","docDefaults","rPrDefault","pPrDefault","rDef","pDef","readRunProperties","readParagraphProperties","styleEl","mapStyleType","displayName","basedOn","nextStyleId","ensureWordBaseline","applyAutoSpacing","defaultStyleId","hasDocDefaults","resolved","existingRuns","inheritedFontFamily","inheritedFontSize","beforeTwips","ascii","szVal","halfPts","toggleOn","colorVal","jc","alignment","mapAlignment","sp","readNumAttr","lineRule","ind","indent","parseNumberingXml","abstracts","absEl","idStr","readLevels","numEl","numIdStr","refIdStr","refId","abstractFormat","lvlEl","ilvlStr","numFmtRaw","lvlText","restartStr","restart","paragraphIndent","readIndent","fontAscii","mapSymbolFontCodepoints","readTwips","isSymbol","ch","cp","wingdings","parseAnchoredFrames","claim","claimed","drawings","counter","firstChildNS","parseAnchoredFrame","nextId","origin","readAnchorOrigin","widthEmu","numAttr","heightEmu","readAnchorOffset","parseGraphicData","behindAttr","readWrapType","wpg","parseGroup","wsp","parseShape","parsePicture","grpSpPr","xfrm","chExt","childCoordSystemCx","childCoordSystemCy","synthFrameFromShape","synthFrameFromPicture","synthFrameFromNestedGroup","off","readSpPrXfrm","grpSp","txbx","txbxContent","parseTextboxBody","readSolidFill","readBorder","readGeometry","normalizePartPath","cNvPr","altText","horizontalFrom","coerceHRelativeFrom","verticalFrom","coerceVRelativeFrom","paragraphIndex","xfrms","bestDepth","depth","cur","offEl","extEl","prstGeom","srgb","ln","solidFill","prstDash","coerceBorderStyle","ns","local","isFlowable","hasChrome","hasTextboxContent","isWhite","flattenFrame","flattenContent","renderedWidthEmu","renderedHeightEmu","sx","sy","flattenChildren","pending","renderedW","renderedH","toInlineImage","withLeadingImages","images","merged","flowDisplacingTextboxes","flowable","byAnchor","pi","newBody","oldToNew","here","flowedIds","remaining","remapped","parseInlineFrames","firstNS","directChildrenNS","hostP","findAncestor","buildInlineFrame","drawingEl","_nextId","groupXfrm","groupExtentEmu","sizeEmu","pageBreakBefore","hasLastRenderedPageBreakSkippingTxbx","textbox","pictures","shapes","shapeExt","readShapeXfrm","bodyPr","numAttrOr","geom","decoration","picExt","picture","fallback","importDocx","unzipped","stripMcFallbacks","relsXml","bodyParagraphIndexByElement","buildBodyParagraphIndex","anchoredFrames","honorLastRenderedPageBreaks","parsedInlineFrames","inlineFrames","hostParagraphEl","rawBody","finalAnchoredFrames","headerFooterBodies","loadHeaderFooterBodies","importedStyles","footnotes","comments","fallbackSection","parsed","headerRelsXml","headerRels","mergeRels","primary","W_NS","index","fallbacks","BlobStoreError","message","cause","sha256Hex","nodeImpl","getNodeImpl","webCryptoSha256Hex","subtle","ABCtor","Ctor","buf","bufToHex","nodeImplPromise","detectNodeImpl","proc","HEX","isBlobHash","inMemoryBlobStore","store","hash","fetchBlobStore","baseUrl","fetchImpl","buildHeaders","headers","res","BlobCache","hashes","fetches","promise","Y_BODY_KEY","Y_META_KEY","Y_PARTS_KEY","Y_PARTREFS_KEY","Y_BLOCK_ID_KEY","Y_BLOCK_KIND_KEY","Y_BLOCK_TEXT_KEY","Y_BLOCK_PROPS_KEY","Y_BLOCK_AST_KEY","Y_META_FIELDS","runsToDelta","appendRuns","parentMarks","appendRun","marks","mergeMarks","runPropsToAttrs","pushOp","linkMark","childMarks","insert","attributes","op","deltaToRuns","delta","linkAttr","readLinkMark","linkChildren","peek","peekLink","stripped","peekAttrs","cleaned","stripKey","opToRun","attrsToRunProps","obj","deepEqual","ao","bo","ak","bk","seedYDoc","ydoc","ids","blockMaps","buildSkeletonBlockYMap","populateBlockContent","Y","populateParagraphContent","buildBlockYMap","populateParagraphYMap","projectYDoc","partRefsMap","projectBlock","parseMeta","partRefs","projectParagraph","ast","textObj","YModule","propsStr","diffApplyText","yText","targetDelta","oldDelta","oldCells","flatten","newCells","cellsEqual","contentEqual","applyFormatOnly","applyPrefixSuffixDiff","cellEqual","oldA","newA","computeAttrDelta","dj","oldAttrs","newAttrs","prefix","suffix","deleteLen","insertCellsAt","startPos","end","cursor","cellsToString","applyDocumentToYDoc","newDoc","newIds","diffBody","diffMeta","diffParts","newBlocks","newIdSet","desiredId","desiredBlock","current","currentId","updateBlockInPlace","findIdAtOrAfter","insertFreshBlock","isCurrentParagraph","isNewParagraph","updateParagraphInPlace","desiredAst","desiredProps","skeleton","setIfChanged","skip","bytesEqual","applyPartRefsToYDoc","ok","affected","fail","error","lockConflict","conflicts","runLength","runsLength","splitRunsAt","len","splitAt","sliceRuns","from","to","applyRunPropertiesToRuns","patch","applyRunPropertiesToRun","mergeRunProps","prev","mergeAdjacentTextRuns","BlockRegistry","blockCount","clamped","changed","anyBumped","removedSectionIndex","breakIndex","mergeSectionsAcross","endingIndex","mergeParagraphProps","wrapTagToPatch","mimeToExtension","allocateMediaPath","pxToEmu","WRAPPER_TAGS","ATOM_TAGS","BLOCK_TAGS","LIST_TAGS","positionFromDomPoint","hosts","registry","domOffset","blockEl","blockIndex","findBlockElement","isInsideTable","charOffsetToPoint","rangeFromDomRange","selectionFromDom","api","domPointFromPosition","blockElementAtIndex","findPointAtOffset","applySelectionToDom","selection","blocksInTopChild","indexOfElement","isAtomElement","isWrapperElement","targetNode","targetOffset","visit","DEFAULT_COLUMN_WIDTH_TWIPS","EditorTable","colCount","columnCount","defaultRowCells","insertAt","resolveRowInsertIndex","newRow","padCellsToColumns","patchVMergeAcrossInsertedRow","removed","promoteVMergeContinuations","at","resolveColumnInsertIndex","averageColumnWidth","insertColumnInRow","deleteColumnFromRow","rowSpan","colSpan","hit","cellAtVisual","isTopLeft","ownsOnlyThis","applyMergeToRow","isVMergeRoot","unmergeTopRow","unmergeContinuationRow","mergeCellProps","emptyCell","totalSpan","deficit","above","aboveCol","aCell","isRestartOrContinue","belowCell","belowContinues","insertContinueInRowAtVisual","startCol","insertAtIndex","removedIndex","successorRowIndex","_row","promoted","atCol","splitMerge","inserted","leftSpan","rightSpan","extended","shrunk","isTopRow","cont","keys","assignCellProp","renderSobreeDocument","applyDefaultTabStop","renderFootnotesAside","tabStopTwips","scope","DEFAULT_HISTORY_CONFIG","META_SELECTION_KEY","History","captureTimeout","localOrigin","tracked","stackItem","_event","MARK_PROP","MARK_ON","MARK_COMMAND_DEFS","toggleMark","isMarkActive","prop","onValue","everyTextRunHas","sawText","rangeAtSelection","length","serializeInlineChildren","walk","inherited","alt","readPxDimension","heightPx","descend","mergeStyleAttribute","styleValue","attrValue","styleAttr","rawKey","rawVal","tableFromElement","domRows","rowFromElement","countColumns","cellEls","domIdx","pendingHere","makeContinueCell","readSpan","cellFromElement","parseAlignment","hasBlockChildren","childAlign","chunks","splitOnBreaks","nodes","scratch","groups","blocksFromNodes","ordered","para","paragraphFromElement","forcedStyleId","align","classStyle","capitalise","serializeHostsToDocument","attachImageResize","selected","handle","onClick","select","deselect","onScroll","positionHandle","onResize","createHandle","onHandleDown","imgRect","hostRect","startX","startY","startW","startH","aspect","onMove","nextW","nextH","dw","dh","onUp","Editor","options","projected","EditorSelection","EditorCommands","title","firstHost","ie","inTracked","inRevisionWrapper","missing","touched","refHash","kept","addedPartPaths","lockCheck","wasSectionBreak","update","targets","bumps","snapshotted","snapshotFormatRevision","stampInsertRevision","firstHalf","secondHalf","stampDeleteRevision","fromIdx","toIdx","nextBody","newRuns","tailStamped","headStamped","nextBlock","mutations","state","breakRun","_kind","blockId","startContainer","decideRevisionRun","decideFormatRun","_strip","rest","spans","tableRef","pRev","open","openFmt","flushFmt","flush","decision","removes","tableChanged","decided","anyChanged","nextRows","cellChanged","nextContent","pChanged","nextPara","nextComments","blockRef","currentDomRangeInsideHosts","closestBlockElement","unwrap","event","set","evt","listener","expect","version","headSplit","tailSplit","middle","_reason","savedSelection","serialised","prevCount","newCount","newJson","stripBinary","payload","refKeys","stopped","baseInfo","firstCell","firstPara","preview","file","lastInsertedLength","afterInsert","hasImageInDataTransfer","dropRange","caretRangeFromPoint","dims","readImageDimensions","args","cmd","BLOCK_ELEMENT_TAGS","_dropped","_drop","_ignored","stamp","dt","y","docAny","resolve","Sobree","deriveSetupFromDocument","editorOpts","partial","nextDoc","sameSection","editable","document","fromSetup","section0","composed","hasRichZones","emptyZone","sameSetup","Viewport","animate","containerRect","targetRect","stageRect","naturalW","naturalH","naturalL","naturalT","padding","availW","availH","sW","sH","sNew","clamp","txNew","tyNew","localCenterY","dx","dy","nextScale","clientX","clientY","rect","wx","wy","isPinch","isWheelZoom","sensitivity","factor","rawDx","rawDy","now","absDx","absDy","nextTier","pickRenderTier","visualScale","translateScale","stage","tierChange","rafId","tick","cleanup","min","scale","parseMarkdown","md","applyMarkdownStyleOverrides","headingMatch","parseInline","ul","ol","listItemParagraph","parseInlineWithBreaks","allocateNumId","definitionFor","plain","flushPlain","linkRun","close","italicChar","codeRun","hardBreak","createSobree","resolveHost","initialDocument","deferredDocx","resolveInitialContent","viewport","sobreeOpts","sobree","pluginInstances","plugin","instance","ready","sink","installWarningSink","fitOnMount","applyFit","isDocxSource","CORNER_MARGIN","STACK_GAP","getFloatingCorner","placement","selector","corner","enterZoneEdit","stackRoot","getSetup","setSetup","onExit","slot","resolveTemplateSlot","textNode","prevStackEditable","placeCursorAtEndOf","finished","finish","newTemplate","onBlur","onKey","onDocMouseDown","papers","HeadlessSobree","presenceSelectionFromEditor","focusBlock","sameBlock","isPresenceState","attachPresence","awareness","publishOwn","initialState","detachSelection","peers","awarenessChangeListener","_changes","OVERLAY_CLASS","attachPresenceOverlay","blockHost","overlay","lastPeers","render","clientId","cssEscape","cRect","presence","detachChange"],"mappings":"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"}