@beyondwork/docx-react-component 1.0.29 → 1.0.31

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 (383) hide show
  1. package/package.json +65 -96
  2. package/src/README.md +85 -0
  3. package/src/api/README.md +26 -0
  4. package/src/api/public-types.ts +1952 -0
  5. package/src/api/session-state.ts +62 -0
  6. package/src/compare/diff-engine.ts +623 -0
  7. package/src/compare/export-redlines.ts +280 -0
  8. package/src/compare/index.ts +25 -0
  9. package/src/compare/snapshot.ts +97 -0
  10. package/src/component-inventory.md +99 -0
  11. package/src/core/README.md +10 -0
  12. package/src/core/commands/README.md +3 -0
  13. package/{dist/chunk-TJBP2K4T.js → src/core/commands/formatting-commands.ts} +536 -196
  14. package/src/core/commands/image-commands.ts +373 -0
  15. package/src/core/commands/index.ts +1879 -0
  16. package/src/core/commands/list-commands.ts +565 -0
  17. package/src/core/commands/paragraph-layout-commands.ts +339 -0
  18. package/src/core/commands/review-commands.ts +108 -0
  19. package/{dist/core/commands/section-layout-commands.cjs → src/core/commands/section-layout-commands.ts} +340 -137
  20. package/src/core/commands/structural-helpers.ts +309 -0
  21. package/{dist/core/commands/style-commands.cjs → src/core/commands/style-commands.ts} +113 -65
  22. package/src/core/commands/table-structure-commands.ts +854 -0
  23. package/{dist/chunk-UZXBISGO.js → src/core/commands/text-commands.ts} +142 -86
  24. package/src/core/schema/README.md +3 -0
  25. package/src/core/schema/text-schema.ts +516 -0
  26. package/src/core/search/search-text.ts +357 -0
  27. package/src/core/selection/README.md +3 -0
  28. package/src/core/selection/mapping.ts +289 -0
  29. package/src/core/selection/review-anchors.ts +183 -0
  30. package/src/core/state/README.md +3 -0
  31. package/src/core/state/editor-state.ts +892 -0
  32. package/src/core/state/text-transaction.ts +869 -0
  33. package/src/formats/xlsx/io/parse-shared-strings.ts +41 -0
  34. package/src/formats/xlsx/io/parse-sheet.ts +459 -0
  35. package/src/formats/xlsx/io/parse-styles.ts +59 -0
  36. package/src/formats/xlsx/io/parse-workbook.ts +75 -0
  37. package/src/formats/xlsx/io/serialize-shared-strings.ts +72 -0
  38. package/src/formats/xlsx/io/serialize-sheet.ts +333 -0
  39. package/src/formats/xlsx/io/serialize-styles.ts +98 -0
  40. package/src/formats/xlsx/io/serialize-workbook.ts +429 -0
  41. package/src/formats/xlsx/io/xlsx-session.ts +314 -0
  42. package/src/formats/xlsx/model/cell.ts +189 -0
  43. package/src/formats/xlsx/model/sheet.ts +326 -0
  44. package/src/formats/xlsx/model/styles.ts +118 -0
  45. package/src/formats/xlsx/model/workbook.ts +453 -0
  46. package/src/formats/xlsx/runtime/cell-commands.ts +567 -0
  47. package/src/formats/xlsx/runtime/sheet-commands.ts +206 -0
  48. package/src/formats/xlsx/runtime/workbook-runtime.ts +177 -0
  49. package/src/formats/xlsx/runtime/workbook-transaction.ts +822 -0
  50. package/src/index.ts +142 -0
  51. package/src/io/README.md +10 -0
  52. package/src/io/docx-session.ts +3175 -0
  53. package/src/io/export/README.md +3 -0
  54. package/src/io/export/export-session.ts +220 -0
  55. package/src/io/export/minimal-docx.ts +115 -0
  56. package/src/io/export/reattach-preserved-parts.ts +54 -0
  57. package/src/io/export/serialize-comments.ts +947 -0
  58. package/src/io/export/serialize-footnotes.ts +394 -0
  59. package/src/io/export/serialize-headers-footers.ts +368 -0
  60. package/src/io/export/serialize-main-document.ts +1342 -0
  61. package/src/io/export/serialize-numbering.ts +218 -0
  62. package/src/io/export/serialize-revisions.ts +389 -0
  63. package/src/io/export/serialize-runtime-revisions.ts +463 -0
  64. package/src/io/export/serialize-tables.ts +174 -0
  65. package/src/io/export/split-review-boundaries.ts +356 -0
  66. package/src/io/export/split-story-blocks-for-runtime-revisions.ts +252 -0
  67. package/src/io/export/table-properties-xml.ts +318 -0
  68. package/src/io/normalize/README.md +3 -0
  69. package/src/io/normalize/normalize-text.ts +670 -0
  70. package/src/io/ooxml/README.md +3 -0
  71. package/src/io/ooxml/highlight-colors.ts +39 -0
  72. package/src/io/ooxml/numbering-sentinels.ts +44 -0
  73. package/src/io/ooxml/parse-comments.ts +852 -0
  74. package/src/io/ooxml/parse-complex-content.ts +287 -0
  75. package/src/io/ooxml/parse-fields.ts +834 -0
  76. package/src/io/ooxml/parse-footnotes.ts +952 -0
  77. package/src/io/ooxml/parse-headers-footers.ts +1212 -0
  78. package/src/io/ooxml/parse-inline-media.ts +461 -0
  79. package/src/io/ooxml/parse-main-document.ts +2947 -0
  80. package/src/io/ooxml/parse-numbering.ts +747 -0
  81. package/src/io/ooxml/parse-revisions.ts +1045 -0
  82. package/src/io/ooxml/parse-settings.ts +184 -0
  83. package/src/io/ooxml/parse-shapes.ts +296 -0
  84. package/src/io/ooxml/parse-styles.ts +639 -0
  85. package/src/io/ooxml/parse-tables.ts +627 -0
  86. package/src/io/ooxml/parse-theme.ts +346 -0
  87. package/src/io/ooxml/part-manifest.ts +136 -0
  88. package/src/io/ooxml/revision-boundaries.ts +475 -0
  89. package/src/io/ooxml/workflow-payload.ts +544 -0
  90. package/src/io/opc/README.md +3 -0
  91. package/src/io/opc/corrupt-package.ts +166 -0
  92. package/src/io/opc/docx-package.ts +74 -0
  93. package/src/io/opc/package-reader.ts +325 -0
  94. package/src/io/opc/package-writer.ts +273 -0
  95. package/src/io/source-package-provenance.ts +241 -0
  96. package/{dist/chunk-RMH72RZI.js → src/legal/bookmarks.ts} +130 -44
  97. package/src/legal/cross-references.ts +414 -0
  98. package/src/legal/defined-terms.ts +203 -0
  99. package/src/legal/index.ts +32 -0
  100. package/src/legal/signature-blocks.ts +259 -0
  101. package/src/model/README.md +3 -0
  102. package/src/model/canonical-document.ts +2722 -0
  103. package/src/model/cds-1.0.0.ts +212 -0
  104. package/src/model/snapshot.ts +760 -0
  105. package/src/preservation/README.md +3 -0
  106. package/src/preservation/markup-compatibility.ts +48 -0
  107. package/src/preservation/opaque-fragment-store.ts +89 -0
  108. package/src/preservation/opaque-region.ts +233 -0
  109. package/src/preservation/package-preservation.ts +113 -0
  110. package/src/preservation/preserved-part-manifest.ts +56 -0
  111. package/src/preservation/relationship-retention.ts +57 -0
  112. package/src/preservation/store.ts +255 -0
  113. package/src/review/README.md +16 -0
  114. package/src/review/store/README.md +3 -0
  115. package/src/review/store/comment-anchors.ts +70 -0
  116. package/src/review/store/comment-remapping.ts +154 -0
  117. package/src/review/store/comment-store.ts +349 -0
  118. package/src/review/store/comment-thread.ts +109 -0
  119. package/src/review/store/revision-actions.ts +423 -0
  120. package/src/review/store/revision-store.ts +323 -0
  121. package/src/review/store/revision-types.ts +182 -0
  122. package/src/review/store/runtime-comment-store.ts +43 -0
  123. package/src/runtime/README.md +3 -0
  124. package/src/runtime/ai-action-policy.ts +764 -0
  125. package/src/runtime/context-analytics.ts +824 -0
  126. package/src/runtime/document-layout.ts +332 -0
  127. package/src/runtime/document-locations.ts +521 -0
  128. package/src/runtime/document-navigation.ts +616 -0
  129. package/src/runtime/document-outline.ts +440 -0
  130. package/src/runtime/document-runtime.ts +4055 -0
  131. package/src/runtime/document-search.ts +145 -0
  132. package/src/runtime/event-refresh-hints.ts +137 -0
  133. package/src/runtime/numbering-prefix.ts +244 -0
  134. package/src/runtime/page-layout-estimation.ts +305 -0
  135. package/src/runtime/read-only-diagnostics-runtime.ts +241 -0
  136. package/src/runtime/resolved-numbering-geometry.ts +293 -0
  137. package/src/runtime/review-runtime.ts +44 -0
  138. package/src/runtime/revision-runtime.ts +107 -0
  139. package/src/runtime/session-capabilities.ts +192 -0
  140. package/src/runtime/story-context.ts +164 -0
  141. package/src/runtime/story-targeting.ts +162 -0
  142. package/src/runtime/suggestions-snapshot.ts +137 -0
  143. package/src/runtime/surface-projection.ts +1553 -0
  144. package/src/runtime/table-commands.ts +173 -0
  145. package/src/runtime/table-schema.ts +309 -0
  146. package/src/runtime/table-style-resolver.ts +409 -0
  147. package/src/runtime/view-state.ts +493 -0
  148. package/src/runtime/virtualized-rendering.ts +258 -0
  149. package/src/runtime/workflow-markup.ts +393 -0
  150. package/src/ui/README.md +30 -0
  151. package/src/ui/WordReviewEditor.tsx +5268 -0
  152. package/src/ui/browser-export.ts +52 -0
  153. package/src/ui/comments/README.md +3 -0
  154. package/src/ui/compatibility/README.md +3 -0
  155. package/src/ui/editor-command-bag.ts +127 -0
  156. package/src/ui/editor-runtime-boundary.ts +1558 -0
  157. package/src/ui/editor-shell-view.tsx +144 -0
  158. package/src/ui/editor-surface/README.md +3 -0
  159. package/src/ui/editor-surface-controller.tsx +66 -0
  160. package/src/ui/headless/comment-decoration-model.ts +124 -0
  161. package/src/ui/headless/preserve-editor-selection.ts +5 -0
  162. package/src/ui/headless/revision-decoration-model.ts +128 -0
  163. package/src/ui/headless/selection-helpers.ts +54 -0
  164. package/src/ui/headless/selection-tool-context.ts +19 -0
  165. package/src/ui/headless/selection-tool-resolver.ts +752 -0
  166. package/src/ui/headless/selection-tool-types.ts +129 -0
  167. package/src/ui/headless/selection-toolbar-model.ts +11 -0
  168. package/src/ui/headless/use-editor-keyboard.ts +103 -0
  169. package/src/ui/review/README.md +3 -0
  170. package/src/ui/runtime-shortcut-dispatch.ts +365 -0
  171. package/src/ui/runtime-snapshot-selectors.ts +197 -0
  172. package/src/ui/shared/revision-filters.ts +31 -0
  173. package/src/ui/status/README.md +3 -0
  174. package/src/ui/theme/README.md +3 -0
  175. package/src/ui/toolbar/README.md +3 -0
  176. package/src/ui/workflow-surface-blocked-rails.ts +94 -0
  177. package/src/ui-tailwind/chrome/chrome-preset-model.ts +107 -0
  178. package/src/ui-tailwind/chrome/chrome-preset-toolbar.tsx +15 -0
  179. package/src/ui-tailwind/chrome/responsive-chrome.ts +46 -0
  180. package/src/ui-tailwind/chrome/review-queue-bar.tsx +97 -0
  181. package/src/ui-tailwind/chrome/tw-alert-banner.tsx +64 -0
  182. package/src/ui-tailwind/chrome/tw-context-analytics-summary.tsx +122 -0
  183. package/src/ui-tailwind/chrome/tw-image-context-toolbar.tsx +121 -0
  184. package/src/ui-tailwind/chrome/tw-layout-panel.tsx +114 -0
  185. package/src/ui-tailwind/chrome/tw-object-context-toolbar.tsx +30 -0
  186. package/src/ui-tailwind/chrome/tw-page-ruler.tsx +365 -0
  187. package/src/ui-tailwind/chrome/tw-selection-tool-blocked.tsx +23 -0
  188. package/src/ui-tailwind/chrome/tw-selection-tool-comment.tsx +35 -0
  189. package/src/ui-tailwind/chrome/tw-selection-tool-formatting.tsx +37 -0
  190. package/src/ui-tailwind/chrome/tw-selection-tool-host.tsx +303 -0
  191. package/src/ui-tailwind/chrome/tw-selection-tool-structure.tsx +116 -0
  192. package/src/ui-tailwind/chrome/tw-selection-tool-suggestion.tsx +29 -0
  193. package/src/ui-tailwind/chrome/tw-selection-tool-workflow.tsx +27 -0
  194. package/src/ui-tailwind/chrome/tw-selection-toolbar.tsx +186 -0
  195. package/src/ui-tailwind/chrome/tw-suggestion-card.tsx +139 -0
  196. package/src/ui-tailwind/chrome/tw-table-context-toolbar.tsx +250 -0
  197. package/src/ui-tailwind/chrome/tw-unsaved-modal.tsx +58 -0
  198. package/src/ui-tailwind/chrome/use-before-unload.ts +20 -0
  199. package/src/ui-tailwind/editor-surface/perf-probe.ts +179 -0
  200. package/src/ui-tailwind/editor-surface/pm-command-bridge.ts +189 -0
  201. package/src/ui-tailwind/editor-surface/pm-contextual-ui.ts +31 -0
  202. package/src/ui-tailwind/editor-surface/pm-decorations.ts +411 -0
  203. package/src/ui-tailwind/editor-surface/pm-position-map.ts +123 -0
  204. package/src/ui-tailwind/editor-surface/pm-schema.ts +927 -0
  205. package/src/ui-tailwind/editor-surface/pm-state-from-snapshot.ts +567 -0
  206. package/src/ui-tailwind/editor-surface/search-plugin.ts +168 -0
  207. package/src/ui-tailwind/editor-surface/surface-build-keys.ts +63 -0
  208. package/src/ui-tailwind/editor-surface/tw-caret.tsx +12 -0
  209. package/src/ui-tailwind/editor-surface/tw-editor-surface.tsx +150 -0
  210. package/src/ui-tailwind/editor-surface/tw-inline-token.tsx +129 -0
  211. package/src/ui-tailwind/editor-surface/tw-opaque-block.tsx +58 -0
  212. package/src/ui-tailwind/editor-surface/tw-paragraph-block.tsx +151 -0
  213. package/src/ui-tailwind/editor-surface/tw-prosemirror-surface.tsx +1047 -0
  214. package/src/ui-tailwind/editor-surface/tw-segment-view.tsx +111 -0
  215. package/src/ui-tailwind/editor-surface/tw-table-node-view.tsx +503 -0
  216. package/src/ui-tailwind/index.ts +62 -0
  217. package/src/ui-tailwind/page-chrome-model.ts +27 -0
  218. package/src/ui-tailwind/review/tw-comment-sidebar.tsx +406 -0
  219. package/src/ui-tailwind/review/tw-health-panel.tsx +149 -0
  220. package/src/ui-tailwind/review/tw-review-rail.tsx +130 -0
  221. package/src/ui-tailwind/review/tw-revision-sidebar.tsx +164 -0
  222. package/src/ui-tailwind/status/tw-status-bar.tsx +65 -0
  223. package/{dist → src}/ui-tailwind/theme/editor-theme.css +58 -40
  224. package/src/ui-tailwind/toolbar/toolbar-layout.ts +47 -0
  225. package/src/ui-tailwind/toolbar/tw-toolbar-icon-button.tsx +52 -0
  226. package/src/ui-tailwind/toolbar/tw-toolbar.tsx +1478 -0
  227. package/src/ui-tailwind/tw-review-workspace.tsx +1587 -0
  228. package/src/validation/README.md +3 -0
  229. package/src/validation/compatibility-engine.ts +878 -0
  230. package/src/validation/compatibility-report.ts +161 -0
  231. package/src/validation/diagnostics.ts +204 -0
  232. package/src/validation/docx-comment-proof.ts +720 -0
  233. package/src/validation/import-diagnostics.ts +128 -0
  234. package/src/validation/low-priority-word-surfaces.ts +373 -0
  235. package/dist/canonical-document-BLEbzL2J.d.cts +0 -844
  236. package/dist/canonical-document-BLEbzL2J.d.ts +0 -844
  237. package/dist/chunk-2FJS5GZM.js +0 -763
  238. package/dist/chunk-2FJS5GZM.js.map +0 -1
  239. package/dist/chunk-2OQBZS3F.js +0 -446
  240. package/dist/chunk-2OQBZS3F.js.map +0 -1
  241. package/dist/chunk-2S7W4KFO.js +0 -127
  242. package/dist/chunk-2S7W4KFO.js.map +0 -1
  243. package/dist/chunk-2TG72QSW.js +0 -3874
  244. package/dist/chunk-2TG72QSW.js.map +0 -1
  245. package/dist/chunk-36QNIZBO.js +0 -532
  246. package/dist/chunk-36QNIZBO.js.map +0 -1
  247. package/dist/chunk-4AQOYAW4.js +0 -3069
  248. package/dist/chunk-4AQOYAW4.js.map +0 -1
  249. package/dist/chunk-4D5EWJ3P.js +0 -77
  250. package/dist/chunk-4D5EWJ3P.js.map +0 -1
  251. package/dist/chunk-5FN54NDH.js +0 -2257
  252. package/dist/chunk-5FN54NDH.js.map +0 -1
  253. package/dist/chunk-BOYGQYRQ.js +0 -7306
  254. package/dist/chunk-BOYGQYRQ.js.map +0 -1
  255. package/dist/chunk-CN3XMECL.js +0 -212
  256. package/dist/chunk-CN3XMECL.js.map +0 -1
  257. package/dist/chunk-EBI3BX6U.js +0 -164
  258. package/dist/chunk-EBI3BX6U.js.map +0 -1
  259. package/dist/chunk-EILUG3VB.js +0 -1275
  260. package/dist/chunk-EILUG3VB.js.map +0 -1
  261. package/dist/chunk-FUDY333O.js +0 -70
  262. package/dist/chunk-FUDY333O.js.map +0 -1
  263. package/dist/chunk-GBVOWFIK.js +0 -1237
  264. package/dist/chunk-GBVOWFIK.js.map +0 -1
  265. package/dist/chunk-H4TQ3H3Y.js +0 -262
  266. package/dist/chunk-H4TQ3H3Y.js.map +0 -1
  267. package/dist/chunk-JGB3IXZO.js +0 -189
  268. package/dist/chunk-JGB3IXZO.js.map +0 -1
  269. package/dist/chunk-KD2QRQPY.js +0 -4342
  270. package/dist/chunk-KD2QRQPY.js.map +0 -1
  271. package/dist/chunk-KLMXQVYK.js +0 -369
  272. package/dist/chunk-KLMXQVYK.js.map +0 -1
  273. package/dist/chunk-KZUG5KFQ.js +0 -214
  274. package/dist/chunk-KZUG5KFQ.js.map +0 -1
  275. package/dist/chunk-QDAQ4CJU.js +0 -345
  276. package/dist/chunk-QDAQ4CJU.js.map +0 -1
  277. package/dist/chunk-RMH72RZI.js.map +0 -1
  278. package/dist/chunk-SWKWQZXM.js +0 -117
  279. package/dist/chunk-SWKWQZXM.js.map +0 -1
  280. package/dist/chunk-TJBP2K4T.js.map +0 -1
  281. package/dist/chunk-TLCEAQDQ.js +0 -542
  282. package/dist/chunk-TLCEAQDQ.js.map +0 -1
  283. package/dist/chunk-UZXBISGO.js.map +0 -1
  284. package/dist/chunk-WGBAKP3Q.js +0 -3220
  285. package/dist/chunk-WGBAKP3Q.js.map +0 -1
  286. package/dist/compare/index.cjs +0 -5475
  287. package/dist/compare/index.cjs.map +0 -1
  288. package/dist/compare/index.d.cts +0 -114
  289. package/dist/compare/index.d.ts +0 -114
  290. package/dist/compare/index.js +0 -731
  291. package/dist/compare/index.js.map +0 -1
  292. package/dist/core/commands/formatting-commands.cjs +0 -828
  293. package/dist/core/commands/formatting-commands.cjs.map +0 -1
  294. package/dist/core/commands/formatting-commands.d.cts +0 -63
  295. package/dist/core/commands/formatting-commands.d.ts +0 -63
  296. package/dist/core/commands/formatting-commands.js +0 -37
  297. package/dist/core/commands/formatting-commands.js.map +0 -1
  298. package/dist/core/commands/image-commands.cjs +0 -2023
  299. package/dist/core/commands/image-commands.cjs.map +0 -1
  300. package/dist/core/commands/image-commands.d.cts +0 -58
  301. package/dist/core/commands/image-commands.d.ts +0 -58
  302. package/dist/core/commands/image-commands.js +0 -18
  303. package/dist/core/commands/image-commands.js.map +0 -1
  304. package/dist/core/commands/section-layout-commands.cjs.map +0 -1
  305. package/dist/core/commands/section-layout-commands.d.cts +0 -62
  306. package/dist/core/commands/section-layout-commands.d.ts +0 -62
  307. package/dist/core/commands/section-layout-commands.js +0 -21
  308. package/dist/core/commands/section-layout-commands.js.map +0 -1
  309. package/dist/core/commands/style-commands.cjs.map +0 -1
  310. package/dist/core/commands/style-commands.d.cts +0 -13
  311. package/dist/core/commands/style-commands.d.ts +0 -13
  312. package/dist/core/commands/style-commands.js +0 -9
  313. package/dist/core/commands/style-commands.js.map +0 -1
  314. package/dist/core/commands/table-structure-commands.cjs +0 -1883
  315. package/dist/core/commands/table-structure-commands.cjs.map +0 -1
  316. package/dist/core/commands/table-structure-commands.d.cts +0 -59
  317. package/dist/core/commands/table-structure-commands.d.ts +0 -59
  318. package/dist/core/commands/table-structure-commands.js +0 -12
  319. package/dist/core/commands/table-structure-commands.js.map +0 -1
  320. package/dist/core/commands/text-commands.cjs +0 -2391
  321. package/dist/core/commands/text-commands.cjs.map +0 -1
  322. package/dist/core/commands/text-commands.d.cts +0 -24
  323. package/dist/core/commands/text-commands.d.ts +0 -24
  324. package/dist/core/commands/text-commands.js +0 -28
  325. package/dist/core/commands/text-commands.js.map +0 -1
  326. package/dist/core/selection/mapping.cjs +0 -200
  327. package/dist/core/selection/mapping.cjs.map +0 -1
  328. package/dist/core/selection/mapping.d.cts +0 -2
  329. package/dist/core/selection/mapping.d.ts +0 -2
  330. package/dist/core/selection/mapping.js +0 -31
  331. package/dist/core/selection/mapping.js.map +0 -1
  332. package/dist/core/state/editor-state.cjs +0 -2278
  333. package/dist/core/state/editor-state.cjs.map +0 -1
  334. package/dist/core/state/editor-state.d.cts +0 -2
  335. package/dist/core/state/editor-state.d.ts +0 -2
  336. package/dist/core/state/editor-state.js +0 -26
  337. package/dist/core/state/editor-state.js.map +0 -1
  338. package/dist/index.cjs +0 -38553
  339. package/dist/index.cjs.map +0 -1
  340. package/dist/index.d.cts +0 -15
  341. package/dist/index.d.ts +0 -15
  342. package/dist/index.js +0 -7856
  343. package/dist/index.js.map +0 -1
  344. package/dist/io/docx-session.cjs +0 -16236
  345. package/dist/io/docx-session.cjs.map +0 -1
  346. package/dist/io/docx-session.d.cts +0 -21
  347. package/dist/io/docx-session.d.ts +0 -21
  348. package/dist/io/docx-session.js +0 -18
  349. package/dist/io/docx-session.js.map +0 -1
  350. package/dist/legal/index.cjs +0 -3900
  351. package/dist/legal/index.cjs.map +0 -1
  352. package/dist/legal/index.d.cts +0 -86
  353. package/dist/legal/index.d.ts +0 -86
  354. package/dist/legal/index.js +0 -616
  355. package/dist/legal/index.js.map +0 -1
  356. package/dist/public-types-7ZL_94cz.d.ts +0 -1573
  357. package/dist/public-types-CeMaDueh.d.cts +0 -1573
  358. package/dist/public-types.cjs +0 -19
  359. package/dist/public-types.cjs.map +0 -1
  360. package/dist/public-types.d.cts +0 -2
  361. package/dist/public-types.d.ts +0 -2
  362. package/dist/public-types.js +0 -1
  363. package/dist/public-types.js.map +0 -1
  364. package/dist/runtime/document-runtime.cjs +0 -11140
  365. package/dist/runtime/document-runtime.cjs.map +0 -1
  366. package/dist/runtime/document-runtime.d.cts +0 -231
  367. package/dist/runtime/document-runtime.d.ts +0 -231
  368. package/dist/runtime/document-runtime.js +0 -21
  369. package/dist/runtime/document-runtime.js.map +0 -1
  370. package/dist/structural-helpers-CilgOVhh.d.cts +0 -10
  371. package/dist/structural-helpers-q0Gd-eBN.d.ts +0 -10
  372. package/dist/ui-tailwind/editor-surface/search-plugin.cjs +0 -313
  373. package/dist/ui-tailwind/editor-surface/search-plugin.cjs.map +0 -1
  374. package/dist/ui-tailwind/editor-surface/search-plugin.d.cts +0 -67
  375. package/dist/ui-tailwind/editor-surface/search-plugin.d.ts +0 -67
  376. package/dist/ui-tailwind/editor-surface/search-plugin.js +0 -23
  377. package/dist/ui-tailwind/editor-surface/search-plugin.js.map +0 -1
  378. package/dist/ui-tailwind/index.cjs +0 -4833
  379. package/dist/ui-tailwind/index.cjs.map +0 -1
  380. package/dist/ui-tailwind/index.d.cts +0 -617
  381. package/dist/ui-tailwind/index.d.ts +0 -617
  382. package/dist/ui-tailwind/index.js +0 -575
  383. package/dist/ui-tailwind/index.js.map +0 -1
@@ -0,0 +1,1342 @@
1
+ import type {
2
+ AltChunkNode,
3
+ BorderSpec,
4
+ CustomXmlNode,
5
+ DocumentRootNode,
6
+ InlineNode,
7
+ MediaCatalog,
8
+ ParagraphNode,
9
+ PreservationStore,
10
+ SdtNode,
11
+ SectionProperties,
12
+ TableNode,
13
+ TableCellNode,
14
+ TextMark,
15
+ } from "../../model/canonical-document.ts";
16
+ import type { OpcRelationship } from "../ooxml/part-manifest.ts";
17
+ import type { RevisionParagraphBoundary } from "../ooxml/revision-boundaries.ts";
18
+ import { getOpaqueFragment } from "../../preservation/store.ts";
19
+ import { retainRelationshipsForFragment } from "../../preservation/relationship-retention.ts";
20
+ import { serializeParagraphNumberingProperties } from "./serialize-numbering.ts";
21
+ import {
22
+ serializeTableCellPropertiesXml,
23
+ serializeTablePropertiesXml,
24
+ serializeTableRowPropertiesXml,
25
+ } from "./table-properties-xml.ts";
26
+
27
+ const HYPERLINK_RELATIONSHIP_TYPE =
28
+ "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink";
29
+
30
+ export interface SerializedMainDocument {
31
+ documentXml: string;
32
+ relationships: OpcRelationship[];
33
+ paragraphBoundaries: RevisionParagraphBoundary[];
34
+ }
35
+
36
+ export interface SerializeMainDocumentOptions {
37
+ documentAttributes?: Record<string, string>;
38
+ media?: MediaCatalog;
39
+ finalSectionProperties?: SectionProperties;
40
+ }
41
+
42
+ interface SerializationState {
43
+ nextHyperlinkRelationshipIndex: number;
44
+ relationships: OpcRelationship[];
45
+ existingRelationshipMap: Map<string, OpcRelationship>;
46
+ retainedRelationshipIds: Set<string>;
47
+ media: MediaCatalog;
48
+ preservation: PreservationStore;
49
+ }
50
+
51
+ interface InlineSerializationResult {
52
+ xml: string;
53
+ cursor: number;
54
+ boundaries: Map<number, number>;
55
+ }
56
+
57
+ interface ParagraphSerializationResult {
58
+ xml: string;
59
+ nextCursor: number;
60
+ boundary: RevisionParagraphBoundary;
61
+ }
62
+
63
+ export function serializeMainDocument(
64
+ content: DocumentRootNode,
65
+ preservation: PreservationStore = { opaqueFragments: {}, packageParts: {} },
66
+ existingRelationships: readonly OpcRelationship[] = [],
67
+ options: SerializeMainDocumentOptions = {},
68
+ ): SerializedMainDocument {
69
+ const nextRelationshipIndex =
70
+ existingRelationships.reduce((maxIndex, relationship) => {
71
+ const match = /^rIdHyperlink(\d+)$/.exec(relationship.id);
72
+ return match ? Math.max(maxIndex, Number.parseInt(match[1] ?? "0", 10)) : maxIndex;
73
+ }, 0) + 1;
74
+ const state: SerializationState = {
75
+ nextHyperlinkRelationshipIndex: nextRelationshipIndex,
76
+ relationships: existingRelationships.filter(
77
+ (relationship) => relationship.type !== HYPERLINK_RELATIONSHIP_TYPE,
78
+ ).map(cloneRelationship),
79
+ existingRelationshipMap: new Map(
80
+ existingRelationships.map((relationship) => [relationship.id, cloneRelationship(relationship)]),
81
+ ),
82
+ retainedRelationshipIds: new Set(
83
+ existingRelationships
84
+ .filter((relationship) => relationship.type !== HYPERLINK_RELATIONSHIP_TYPE)
85
+ .map((relationship) => relationship.id),
86
+ ),
87
+ media: options.media ?? { items: {} },
88
+ preservation,
89
+ };
90
+ const documentOpen = `<w:document${serializeDocumentAttributes(options.documentAttributes, content)}>`;
91
+ const prefix = [
92
+ `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>`,
93
+ documentOpen,
94
+ ` <w:body>`,
95
+ ].join("\n");
96
+ const suffix = `</w:body>\n</w:document>`;
97
+ const bodyPieces: string[] = [];
98
+ const paragraphBoundaries: RevisionParagraphBoundary[] = [];
99
+ let bodyLength = 0;
100
+ let sectionPropertiesXml = options.finalSectionProperties
101
+ ? serializeSectionPropertiesXml(options.finalSectionProperties)
102
+ : "<w:sectPr/>";
103
+ let cursor = 0;
104
+ let paragraphIndex = -1;
105
+ let previousWasParagraph = false;
106
+
107
+ for (const block of content.children) {
108
+ if (block.type === "paragraph") {
109
+ if (previousWasParagraph) {
110
+ cursor += 1;
111
+ }
112
+
113
+ paragraphIndex += 1;
114
+ const serializedParagraph = serializeParagraph(
115
+ block,
116
+ state,
117
+ cursor,
118
+ paragraphIndex,
119
+ );
120
+ const paragraphOffset = prefix.length + bodyLength;
121
+ bodyPieces.push(serializedParagraph.xml);
122
+ bodyLength += serializedParagraph.xml.length;
123
+ paragraphBoundaries.push(
124
+ offsetParagraphBoundary(serializedParagraph.boundary, paragraphOffset),
125
+ );
126
+ cursor = serializedParagraph.nextCursor;
127
+ previousWasParagraph = true;
128
+ continue;
129
+ }
130
+
131
+ if (block.type === "table") {
132
+ const tableXml = serializeTableNode(block, state);
133
+ bodyPieces.push(tableXml);
134
+ bodyLength += tableXml.length;
135
+ cursor += 1;
136
+ previousWasParagraph = false;
137
+ continue;
138
+ }
139
+
140
+ if (block.type === "sdt") {
141
+ const sdtXml = serializeSdtNode(block, state);
142
+ bodyPieces.push(sdtXml);
143
+ bodyLength += sdtXml.length;
144
+ previousWasParagraph = false;
145
+ continue;
146
+ }
147
+
148
+ if (block.type === "custom_xml") {
149
+ const customXml = serializeCustomXmlNode(block, state);
150
+ bodyPieces.push(customXml);
151
+ bodyLength += customXml.length;
152
+ previousWasParagraph = false;
153
+ continue;
154
+ }
155
+
156
+ if (block.type === "alt_chunk") {
157
+ const altChunkXml = serializeAltChunkNode(block);
158
+ bodyPieces.push(altChunkXml);
159
+ bodyLength += altChunkXml.length;
160
+ cursor += 1;
161
+ previousWasParagraph = false;
162
+ continue;
163
+ }
164
+
165
+ if (block.type === "section_break") {
166
+ // Inline section breaks must be emitted as a paragraph with <w:sectPr>
167
+ // in its <w:pPr> element (OOXML compliance). The body-level <w:sectPr>
168
+ // is reserved for the final section only.
169
+ let inlineSectPr: string;
170
+ if (block.sectionPropertiesXml ?? block.propertiesXml) {
171
+ inlineSectPr = block.sectionPropertiesXml ?? block.propertiesXml!;
172
+ } else if (block.sectionProperties) {
173
+ inlineSectPr = serializeSectionPropertiesXml(block.sectionProperties);
174
+ } else {
175
+ inlineSectPr = "<w:sectPr/>";
176
+ }
177
+ const sectionParagraphXml = `<w:p><w:pPr>${inlineSectPr}</w:pPr></w:p>`;
178
+ bodyPieces.push(sectionParagraphXml);
179
+ bodyLength += sectionParagraphXml.length;
180
+ cursor += 1;
181
+ previousWasParagraph = false;
182
+ continue;
183
+ }
184
+
185
+ const blockXml = serializeOpaqueBlock(block, state);
186
+ if (looksLikeSectionPropertiesXml(blockXml)) {
187
+ sectionPropertiesXml = blockXml;
188
+ } else {
189
+ bodyPieces.push(blockXml);
190
+ bodyLength += blockXml.length;
191
+ }
192
+ cursor += 1;
193
+ previousWasParagraph = false;
194
+ }
195
+
196
+ const bodyXml = bodyPieces.join("");
197
+ const documentXml = `${prefix}${bodyXml || "<w:p><w:r><w:t></w:t></w:r></w:p>"}${sectionPropertiesXml}${suffix}`;
198
+
199
+ return {
200
+ documentXml,
201
+ relationships: state.relationships,
202
+ paragraphBoundaries,
203
+ };
204
+ }
205
+
206
+ function serializeOpaqueBlock(
207
+ block: Extract<DocumentRootNode["children"][number], { type: "opaque_block" }>,
208
+ state: SerializationState,
209
+ ): string {
210
+ return lookupOpaqueXml(block.fragmentId, state);
211
+ }
212
+
213
+ function serializeTableNode(
214
+ table: TableNode,
215
+ state: SerializationState,
216
+ ): string {
217
+ const propertiesXml = buildTablePropertiesXml(table);
218
+ const gridXml =
219
+ table.gridColumns.length > 0
220
+ ? `<w:tblGrid>${table.gridColumns
221
+ .map((width) => `<w:gridCol w:w="${width}"/>`)
222
+ .join("")}</w:tblGrid>`
223
+ : "";
224
+ const rowsXml = table.rows
225
+ .map((row) => {
226
+ const rowPropertiesXml = buildTableRowPropertiesXml(row);
227
+ const cellsXml = row.cells
228
+ .map((cell) => serializeTableCellNode(cell, state))
229
+ .join("");
230
+ return `<w:tr>${rowPropertiesXml}${cellsXml}</w:tr>`;
231
+ })
232
+ .join("");
233
+ return `<w:tbl>${propertiesXml}${gridXml}${rowsXml}</w:tbl>`;
234
+ }
235
+
236
+ function serializeTableCellNode(
237
+ cell: TableCellNode,
238
+ state: SerializationState,
239
+ ): string {
240
+ const propertiesXml = buildCellPropertiesXml(cell);
241
+ const blocksXml = cell.children
242
+ .map((child) => serializeBlockNode(child, state))
243
+ .join("");
244
+ return `<w:tc>${propertiesXml}${blocksXml || "<w:p/>"}</w:tc>`;
245
+ }
246
+
247
+ function serializeBlockNode(
248
+ block: DocumentRootNode["children"][number],
249
+ state: SerializationState,
250
+ ): string {
251
+ switch (block.type) {
252
+ case "paragraph":
253
+ return serializeTableCellParagraph(block, state);
254
+ case "table":
255
+ return serializeTableNode(block, state);
256
+ case "sdt":
257
+ return serializeSdtNode(block, state);
258
+ case "custom_xml":
259
+ return serializeCustomXmlNode(block, state);
260
+ case "alt_chunk":
261
+ return serializeAltChunkNode(block);
262
+ case "opaque_block":
263
+ return lookupOpaqueXml(block.fragmentId, state);
264
+ case "section_break":
265
+ return serializeNestedSectionBreak(block);
266
+ }
267
+ }
268
+
269
+ function serializeNestedSectionBreak(
270
+ block: Extract<DocumentRootNode["children"][number], { type: "section_break" }>,
271
+ ): string {
272
+ const sectionPropertiesXml =
273
+ block.sectionPropertiesXml ??
274
+ block.propertiesXml ??
275
+ (block.sectionProperties ? serializeSectionPropertiesXml(block.sectionProperties) : "<w:sectPr/>");
276
+ return `<w:p><w:pPr>${sectionPropertiesXml}</w:pPr></w:p>`;
277
+ }
278
+
279
+ function buildCellPropertiesXml(cell: TableCellNode): string {
280
+ return serializeTableCellPropertiesXml(cell);
281
+ }
282
+
283
+ function buildTableRowPropertiesXml(row: TableNode["rows"][number]): string {
284
+ return serializeTableRowPropertiesXml(row);
285
+ }
286
+
287
+ function serializeSdtNode(
288
+ block: SdtNode,
289
+ state: SerializationState,
290
+ ): string {
291
+ const propertiesXml = block.properties.propertiesXml ?? buildSdtPropertiesXml(block);
292
+ const childrenXml = block.children.map((child) => serializeBlockNode(child, state)).join("") || "<w:p/>";
293
+ return `<w:sdt>${propertiesXml}<w:sdtContent>${childrenXml}</w:sdtContent></w:sdt>`;
294
+ }
295
+
296
+ function serializeCustomXmlNode(
297
+ block: CustomXmlNode,
298
+ state: SerializationState,
299
+ ): string {
300
+ if (block.rawXml) {
301
+ return block.rawXml;
302
+ }
303
+ const attrs: string[] = [];
304
+ if (block.uri) {
305
+ attrs.push(`w:uri="${escapeAttribute(block.uri)}"`);
306
+ }
307
+ if (block.element) {
308
+ attrs.push(`w:element="${escapeAttribute(block.element)}"`);
309
+ }
310
+ const attrXml = attrs.length > 0 ? ` ${attrs.join(" ")}` : "";
311
+ const childrenXml = block.children.map((child) => serializeBlockNode(child, state)).join("");
312
+ return `<w:customXml${attrXml}>${childrenXml || "<w:p/>"}</w:customXml>`;
313
+ }
314
+
315
+ function serializeAltChunkNode(
316
+ block: AltChunkNode,
317
+ ): string {
318
+ return `<w:altChunk r:id="${escapeAttribute(block.relationshipId)}"/>`;
319
+ }
320
+
321
+ function buildSdtPropertiesXml(block: SdtNode): string {
322
+ const children: string[] = [];
323
+ if (block.properties.alias) {
324
+ children.push(`<w:alias w:val="${escapeAttribute(block.properties.alias)}"/>`);
325
+ }
326
+ if (block.properties.tag) {
327
+ children.push(`<w:tag w:val="${escapeAttribute(block.properties.tag)}"/>`);
328
+ }
329
+ if (block.properties.lock) {
330
+ children.push(`<w:lock w:val="${escapeAttribute(block.properties.lock)}"/>`);
331
+ }
332
+ if (block.properties.showingPlcHdr) {
333
+ children.push(`<w:showingPlcHdr/>`);
334
+ }
335
+ if (block.properties.checkbox) {
336
+ const cb = block.properties.checkbox;
337
+ const cbParts: string[] = [];
338
+ cbParts.push(`<w14:checked w14:val="${cb.checked ? "1" : "0"}"/>`);
339
+ if (cb.checkedChar) cbParts.push(`<w14:checkedState w14:val="${escapeAttribute(cb.checkedChar)}"/>`);
340
+ if (cb.uncheckedChar) cbParts.push(`<w14:uncheckedState w14:val="${escapeAttribute(cb.uncheckedChar)}"/>`);
341
+ children.push(`<w14:checkbox>${cbParts.join("")}</w14:checkbox>`);
342
+ } else if (block.properties.datePicker) {
343
+ const dp = block.properties.datePicker;
344
+ const dateAttrs = dp.fullDate ? ` w:fullDate="${escapeAttribute(dp.fullDate)}"` : "";
345
+ const dpParts: string[] = [];
346
+ if (dp.dateFormat) dpParts.push(`<w:dateFormat w:val="${escapeAttribute(dp.dateFormat)}"/>`);
347
+ if (dp.lid) dpParts.push(`<w:lid w:val="${escapeAttribute(dp.lid)}"/>`);
348
+ children.push(dpParts.length > 0 ? `<w:date${dateAttrs}>${dpParts.join("")}</w:date>` : `<w:date${dateAttrs}/>`);
349
+ } else if (block.properties.dropdownList) {
350
+ const items = block.properties.dropdownList.map((item) => {
351
+ const dt = item.displayText ? ` w:displayText="${escapeAttribute(item.displayText)}"` : "";
352
+ return `<w:listItem${dt} w:value="${escapeAttribute(item.value)}"/>`;
353
+ }).join("");
354
+ children.push(`<w:dropDownList>${items}</w:dropDownList>`);
355
+ } else if (block.properties.comboBox) {
356
+ const items = block.properties.comboBox.map((item) => {
357
+ const dt = item.displayText ? ` w:displayText="${escapeAttribute(item.displayText)}"` : "";
358
+ return `<w:listItem${dt} w:value="${escapeAttribute(item.value)}"/>`;
359
+ }).join("");
360
+ children.push(`<w:comboBox>${items}</w:comboBox>`);
361
+ } else if (block.properties.sdtType === "plainText") {
362
+ children.push(`<w:text/>`);
363
+ } else if (block.properties.sdtType === "richText") {
364
+ children.push(`<w:richText/>`);
365
+ } else if (block.properties.sdtType) {
366
+ children.push(`<w:${block.properties.sdtType}/>`);
367
+ }
368
+ return children.length > 0 ? `<w:sdtPr>${children.join("")}</w:sdtPr>` : "<w:sdtPr/>";
369
+ }
370
+
371
+ function serializeTableCellParagraph(
372
+ paragraph: ParagraphNode,
373
+ state: SerializationState,
374
+ ): string {
375
+ let xml = "<w:p>";
376
+ const paragraphPropertiesXml = buildParagraphPropertiesXml(paragraph);
377
+ if (paragraphPropertiesXml.length > 0) {
378
+ xml += paragraphPropertiesXml;
379
+ }
380
+ const childrenXml = paragraph.children.map((child) => serializeTableInlineNode(child, state)).join("");
381
+ xml += childrenXml || "<w:r><w:t></w:t></w:r>";
382
+ xml += "</w:p>";
383
+ return xml;
384
+ }
385
+
386
+ function serializeTableInlineNode(
387
+ node: InlineNode,
388
+ state: SerializationState,
389
+ ): string {
390
+ switch (node.type) {
391
+ case "text": {
392
+ const marks = node.marks;
393
+ const properties = serializeRunPropertiesFromMarks(marks);
394
+ const preserve = requiresPreservedSpace(node.text) ? ` xml:space="preserve"` : "";
395
+ return `<w:r>${properties}<w:t${preserve}>${escapeXml(node.text)}</w:t></w:r>`;
396
+ }
397
+ case "tab":
398
+ return "<w:r><w:tab/></w:r>";
399
+ case "column_break":
400
+ return "<w:r><w:br w:type=\"column\"/></w:r>";
401
+ case "hard_break":
402
+ return "<w:r><w:br/></w:r>";
403
+ case "symbol": {
404
+ const properties = serializeRunPropertiesFromMarks(node.marks);
405
+ const fontAttribute = node.font ? ` w:font="${escapeAttribute(node.font)}"` : "";
406
+ return `<w:r>${properties}<w:sym${fontAttribute} w:char="${escapeAttribute(node.char)}"/></w:r>`;
407
+ }
408
+ case "image":
409
+ return serializeImageNode(node, state);
410
+ case "opaque_inline":
411
+ return lookupOpaqueXml(node.fragmentId, state);
412
+ case "chart_preview":
413
+ case "smartart_preview":
414
+ case "shape":
415
+ case "wordart":
416
+ case "vml_shape":
417
+ return wrapInlineRawXml(node.rawXml);
418
+ case "hyperlink": {
419
+ const hyperlinkOpen = node.href.startsWith("#")
420
+ ? `<w:hyperlink w:anchor="${escapeAttribute(node.href.slice(1))}">`
421
+ : (() => {
422
+ const relationshipId = `rIdHyperlink${state.nextHyperlinkRelationshipIndex}`;
423
+ state.nextHyperlinkRelationshipIndex += 1;
424
+ state.relationships.push({
425
+ id: relationshipId,
426
+ type: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink",
427
+ target: node.href,
428
+ targetMode: "external",
429
+ });
430
+ state.retainedRelationshipIds.add(relationshipId);
431
+ return `<w:hyperlink r:id="${relationshipId}">`;
432
+ })();
433
+ const childrenXml = node.children.map((child) => serializeTableInlineNode(child, state)).join("");
434
+ return `${hyperlinkOpen}${childrenXml}</w:hyperlink>`;
435
+ }
436
+ case "field": {
437
+ if (node.children && node.children.length > 0) {
438
+ const childrenXml = node.children
439
+ .map((child) => serializeTableInlineNode(child, state))
440
+ .join("");
441
+ if (node.fieldType === "complex") {
442
+ return (
443
+ `<w:r><w:fldChar w:fldCharType="begin"/></w:r>` +
444
+ `<w:r><w:instrText xml:space="preserve"> ${escapeXml(node.instruction)} </w:instrText></w:r>` +
445
+ `<w:r><w:fldChar w:fldCharType="separate"/></w:r>` +
446
+ childrenXml +
447
+ `<w:r><w:fldChar w:fldCharType="end"/></w:r>`
448
+ );
449
+ }
450
+ return `<w:fldSimple w:instr="${escapeAttribute(node.instruction)}">${childrenXml}</w:fldSimple>`;
451
+ }
452
+ return `<w:fldSimple w:instr="${escapeAttribute(node.instruction)}"/>`;
453
+ }
454
+ case "bookmark_start":
455
+ return (
456
+ `<w:bookmarkStart w:id="${escapeAttribute(node.bookmarkId)}"` +
457
+ ` w:name="${escapeAttribute(node.name)}"/>`
458
+ );
459
+ case "bookmark_end":
460
+ return `<w:bookmarkEnd w:id="${escapeAttribute(node.bookmarkId)}"/>`;
461
+ case "footnote_ref": {
462
+ const refElement =
463
+ node.noteKind === "footnote"
464
+ ? `<w:footnoteReference w:id="${escapeAttribute(node.noteId)}"/>`
465
+ : `<w:endnoteReference w:id="${escapeAttribute(node.noteId)}"/>`;
466
+ const styleVal =
467
+ node.noteKind === "footnote"
468
+ ? "FootnoteReference"
469
+ : "EndnoteReference";
470
+ return `<w:r><w:rPr><w:rStyle w:val="${styleVal}"/></w:rPr>${refElement}</w:r>`;
471
+ }
472
+ default:
473
+ return "";
474
+ }
475
+ }
476
+
477
+ function buildParagraphPropertiesXml(paragraph: ParagraphNode): string {
478
+ const children: string[] = [];
479
+
480
+ if (paragraph.styleId) {
481
+ children.push(`<w:pStyle w:val="${escapeAttribute(paragraph.styleId)}"/>`);
482
+ }
483
+ if (paragraph.keepNext) {
484
+ children.push("<w:keepNext/>");
485
+ }
486
+ if (paragraph.keepLines) {
487
+ children.push("<w:keepLines/>");
488
+ }
489
+ if (paragraph.pageBreakBefore) {
490
+ children.push("<w:pageBreakBefore/>");
491
+ }
492
+ if (paragraph.widowControl) {
493
+ children.push("<w:widowControl/>");
494
+ }
495
+ if (paragraph.outlineLevel !== undefined) {
496
+ children.push(`<w:outlineLvl w:val="${paragraph.outlineLevel}"/>`);
497
+ }
498
+ if (paragraph.numbering) {
499
+ children.push(serializeParagraphNumberingProperties(paragraph.numbering));
500
+ }
501
+ if (paragraph.spacing) {
502
+ const s = paragraph.spacing;
503
+ const attrs: string[] = [];
504
+ if (s.before !== undefined) attrs.push(`w:before="${s.before}"`);
505
+ if (s.after !== undefined) attrs.push(`w:after="${s.after}"`);
506
+ if (s.line !== undefined) attrs.push(`w:line="${s.line}"`);
507
+ if (s.lineRule !== undefined) attrs.push(`w:lineRule="${s.lineRule}"`);
508
+ if (attrs.length > 0) children.push(`<w:spacing ${attrs.join(" ")}/>`);
509
+ }
510
+ if (paragraph.contextualSpacing !== undefined) {
511
+ children.push(
512
+ paragraph.contextualSpacing
513
+ ? "<w:contextualSpacing/>"
514
+ : `<w:contextualSpacing w:val="0"/>`,
515
+ );
516
+ }
517
+ if (paragraph.indentation) {
518
+ const ind = paragraph.indentation;
519
+ const attrs: string[] = [];
520
+ if (ind.left !== undefined) attrs.push(`w:left="${ind.left}"`);
521
+ if (ind.right !== undefined) attrs.push(`w:right="${ind.right}"`);
522
+ if (ind.firstLine !== undefined) attrs.push(`w:firstLine="${ind.firstLine}"`);
523
+ if (ind.hanging !== undefined) attrs.push(`w:hanging="${ind.hanging}"`);
524
+ if (attrs.length > 0) children.push(`<w:ind ${attrs.join(" ")}/>`);
525
+ }
526
+ if (paragraph.alignment) {
527
+ children.push(`<w:jc w:val="${paragraph.alignment}"/>`);
528
+ }
529
+ if (paragraph.borders) {
530
+ const bordersXml = serializeParagraphBorders(paragraph.borders);
531
+ if (bordersXml) {
532
+ children.push(bordersXml);
533
+ }
534
+ }
535
+ if (paragraph.shading) {
536
+ const shadingXml = serializeParagraphShading(paragraph.shading);
537
+ if (shadingXml) {
538
+ children.push(shadingXml);
539
+ }
540
+ }
541
+ if (paragraph.bidi) {
542
+ children.push("<w:bidi/>");
543
+ }
544
+ if (paragraph.suppressLineNumbers) {
545
+ children.push("<w:suppressLineNumbers/>");
546
+ }
547
+ if (paragraph.cnfStyle) {
548
+ children.push(`<w:cnfStyle w:val="${escapeAttribute(paragraph.cnfStyle)}"/>`);
549
+ }
550
+ if (paragraph.tabStops && paragraph.tabStops.length > 0) {
551
+ const tabsXml = paragraph.tabStops.map((tab) => {
552
+ const leaderAttr = tab.leader ? ` w:leader="${tab.leader}"` : "";
553
+ return `<w:tab w:val="${tab.align}" w:pos="${tab.position}"${leaderAttr}/>`;
554
+ }).join("");
555
+ children.push(`<w:tabs>${tabsXml}</w:tabs>`);
556
+ }
557
+
558
+ if (children.length === 0) return "";
559
+ return `<w:pPr>${children.join("")}</w:pPr>`;
560
+ }
561
+
562
+ function buildTablePropertiesXml(table: TableNode): string {
563
+ return serializeTablePropertiesXml(table);
564
+ }
565
+
566
+ function serializeParagraphBorders(borders: ParagraphNode["borders"]): string {
567
+ if (!borders) {
568
+ return "";
569
+ }
570
+ const parts: string[] = [];
571
+ for (const [name, border] of [
572
+ ["top", borders.top],
573
+ ["left", borders.left],
574
+ ["bottom", borders.bottom],
575
+ ["right", borders.right],
576
+ ["bar", borders.bar],
577
+ ["between", borders.between],
578
+ ] as const) {
579
+ const xml = serializeBorder(name, border);
580
+ if (xml) {
581
+ parts.push(xml);
582
+ }
583
+ }
584
+ return parts.length > 0 ? `<w:pBdr>${parts.join("")}</w:pBdr>` : "";
585
+ }
586
+
587
+ function serializeBorder(name: string, border: BorderSpec | undefined): string {
588
+ if (!border) {
589
+ return "";
590
+ }
591
+ const attrs: string[] = [];
592
+ if (border.value) attrs.push(`w:val="${escapeAttribute(border.value)}"`);
593
+ if (border.size !== undefined) attrs.push(`w:sz="${border.size}"`);
594
+ if (border.space !== undefined) attrs.push(`w:space="${border.space}"`);
595
+ if (border.color) attrs.push(`w:color="${escapeAttribute(border.color)}"`);
596
+ return attrs.length > 0 ? `<w:${name} ${attrs.join(" ")}/>` : "";
597
+ }
598
+
599
+ function serializeParagraphShading(shading: ParagraphNode["shading"]): string {
600
+ if (!shading) {
601
+ return "";
602
+ }
603
+ const attrs: string[] = [];
604
+ if (shading.val) attrs.push(`w:val="${escapeAttribute(shading.val)}"`);
605
+ if (shading.color) attrs.push(`w:color="${escapeAttribute(shading.color)}"`);
606
+ if (shading.fill) attrs.push(`w:fill="${escapeAttribute(shading.fill)}"`);
607
+ return attrs.length > 0 ? `<w:shd ${attrs.join(" ")}/>` : "";
608
+ }
609
+
610
+ function serializeRunPropertiesFromMarks(marks: TextMark[] | undefined): string {
611
+ return serializeRunProperties(marks);
612
+ }
613
+
614
+ function serializeParagraph(
615
+ paragraph: ParagraphNode,
616
+ state: SerializationState,
617
+ cursor: number,
618
+ paragraphIndex: number,
619
+ ): ParagraphSerializationResult {
620
+ let xml = "<w:p>";
621
+ const boundaries = new Map<number, number>();
622
+ const paragraphStart = 0;
623
+ const paragraphStartTagEnd = xml.length;
624
+ boundaries.set(cursor, paragraphStartTagEnd);
625
+
626
+ let paragraphPropertiesStart: number | undefined;
627
+ let paragraphPropertiesEnd: number | undefined;
628
+
629
+ const paragraphPropertiesXml = buildParagraphPropertiesXml(paragraph);
630
+ if (paragraphPropertiesXml.length > 0) {
631
+ paragraphPropertiesStart = xml.length;
632
+ xml += paragraphPropertiesXml;
633
+ paragraphPropertiesEnd = xml.length;
634
+ }
635
+
636
+ const children = serializeParagraphChildren(paragraph.children, state, cursor, xml.length);
637
+ xml += children.xml;
638
+ const contentEmpty = children.xml.length === 0;
639
+ if (contentEmpty) {
640
+ xml += "<w:r><w:t></w:t></w:r>";
641
+ }
642
+ const paragraphEndTagStart = xml.length;
643
+ xml += "</w:p>";
644
+
645
+ if (!children.boundaries.has(children.cursor)) {
646
+ children.boundaries.set(children.cursor, paragraphEndTagStart);
647
+ }
648
+
649
+ return {
650
+ xml,
651
+ nextCursor: children.cursor,
652
+ boundary: {
653
+ paragraphIndex,
654
+ start: cursor,
655
+ end: children.cursor,
656
+ boundaries: children.boundaries,
657
+ paragraphStart,
658
+ paragraphStartTagEnd,
659
+ paragraphEndTagStart,
660
+ paragraphEnd: xml.length,
661
+ ...(paragraphPropertiesStart !== undefined
662
+ ? { paragraphPropertiesStart }
663
+ : {}),
664
+ ...(paragraphPropertiesEnd !== undefined ? { paragraphPropertiesEnd } : {}),
665
+ },
666
+ };
667
+ }
668
+
669
+ function serializeParagraphChildren(
670
+ children: InlineNode[],
671
+ state: SerializationState,
672
+ cursor: number,
673
+ xmlOffset: number,
674
+ ): InlineSerializationResult {
675
+ const pieces: string[] = [];
676
+ const boundaries = new Map<number, number>();
677
+ let nextCursor = cursor;
678
+ let nextOffset = xmlOffset;
679
+ boundaries.set(nextCursor, nextOffset);
680
+
681
+ for (const child of children) {
682
+ const result = serializeInlineNode(child, state, nextCursor, nextOffset);
683
+ pieces.push(result.xml);
684
+ for (const [position, index] of result.boundaries) {
685
+ boundaries.set(position, index);
686
+ }
687
+ nextCursor = result.cursor;
688
+ nextOffset += result.xml.length;
689
+ }
690
+
691
+ return {
692
+ xml: pieces.join(""),
693
+ cursor: nextCursor,
694
+ boundaries,
695
+ };
696
+ }
697
+
698
+ type RunPiece =
699
+ | { kind: "text"; text: string; marks?: TextMark[] }
700
+ | { kind: "tab" }
701
+ | { kind: "hard_break" };
702
+
703
+ function serializeRunPiece(piece: RunPiece): string {
704
+ switch (piece.kind) {
705
+ case "text":
706
+ return serializeText(piece.text);
707
+ case "tab":
708
+ return "<w:tab/>";
709
+ case "hard_break":
710
+ return "<w:br/>";
711
+ }
712
+ }
713
+
714
+ function serializeText(text: string): string {
715
+ const preserve = requiresPreservedSpace(text) ? ` xml:space="preserve"` : "";
716
+ return `<w:t${preserve}>${escapeXml(text)}</w:t>`;
717
+ }
718
+
719
+ function serializeInlineNode(
720
+ node: InlineNode,
721
+ state: SerializationState,
722
+ cursor: number,
723
+ xmlOffset: number,
724
+ ): InlineSerializationResult {
725
+ switch (node.type) {
726
+ case "text": {
727
+ const xml = serializeRun({
728
+ kind: "text",
729
+ text: node.text,
730
+ ...(node.marks && node.marks.length > 0 ? { marks: node.marks } : {}),
731
+ });
732
+ const boundaries = new Map<number, number>();
733
+ boundaries.set(cursor, xmlOffset);
734
+ boundaries.set(cursor + Array.from(node.text).length, xmlOffset + xml.length);
735
+ return {
736
+ xml,
737
+ cursor: cursor + Array.from(node.text).length,
738
+ boundaries,
739
+ };
740
+ }
741
+ case "tab": {
742
+ const xml = serializeRun({ kind: "tab" });
743
+ const boundaries = new Map<number, number>();
744
+ boundaries.set(cursor, xmlOffset);
745
+ boundaries.set(cursor + 1, xmlOffset + xml.length);
746
+ return {
747
+ xml,
748
+ cursor: cursor + 1,
749
+ boundaries,
750
+ };
751
+ }
752
+ case "column_break": {
753
+ const xml = `<w:r><w:br w:type="column"/></w:r>`;
754
+ const boundaries = new Map<number, number>();
755
+ boundaries.set(cursor, xmlOffset);
756
+ boundaries.set(cursor + 1, xmlOffset + xml.length);
757
+ return {
758
+ xml,
759
+ cursor: cursor + 1,
760
+ boundaries,
761
+ };
762
+ }
763
+ case "hard_break": {
764
+ const xml = serializeRun({ kind: "hard_break" });
765
+ const boundaries = new Map<number, number>();
766
+ boundaries.set(cursor, xmlOffset);
767
+ boundaries.set(cursor + 1, xmlOffset + xml.length);
768
+ return {
769
+ xml,
770
+ cursor: cursor + 1,
771
+ boundaries,
772
+ };
773
+ }
774
+ case "symbol": {
775
+ const xml = serializeTableInlineNode(node, state);
776
+ const boundaries = new Map<number, number>();
777
+ boundaries.set(cursor, xmlOffset);
778
+ boundaries.set(cursor + 1, xmlOffset + xml.length);
779
+ return {
780
+ xml,
781
+ cursor: cursor + 1,
782
+ boundaries,
783
+ };
784
+ }
785
+ case "image": {
786
+ const xml = serializeImageNode(node, state);
787
+ const boundaries = new Map<number, number>();
788
+ boundaries.set(cursor, xmlOffset);
789
+ boundaries.set(cursor + 1, xmlOffset + xml.length);
790
+ return {
791
+ xml,
792
+ cursor: cursor + 1,
793
+ boundaries,
794
+ };
795
+ }
796
+ case "opaque_inline": {
797
+ const xml = lookupOpaqueXml(node.fragmentId, state);
798
+ const boundaries = new Map<number, number>();
799
+ boundaries.set(cursor, xmlOffset);
800
+ boundaries.set(cursor + 1, xmlOffset + xml.length);
801
+ return {
802
+ xml,
803
+ cursor: cursor + 1,
804
+ boundaries,
805
+ };
806
+ }
807
+ case "chart_preview":
808
+ case "smartart_preview":
809
+ case "shape":
810
+ case "wordart":
811
+ case "vml_shape": {
812
+ // Reattach original XML unchanged for lossless round-trip.
813
+ const xml = wrapInlineRawXml(node.rawXml);
814
+ const boundaries = new Map<number, number>();
815
+ boundaries.set(cursor, xmlOffset);
816
+ boundaries.set(cursor + 1, xmlOffset + xml.length);
817
+ return {
818
+ xml,
819
+ cursor: cursor + 1,
820
+ boundaries,
821
+ };
822
+ }
823
+ case "hyperlink": {
824
+ const hyperlinkOpen = node.href.startsWith("#")
825
+ ? `<w:hyperlink w:anchor="${escapeAttribute(node.href.slice(1))}">`
826
+ : (() => {
827
+ const relationshipId = `rIdHyperlink${state.nextHyperlinkRelationshipIndex}`;
828
+ state.nextHyperlinkRelationshipIndex += 1;
829
+ state.relationships.push({
830
+ id: relationshipId,
831
+ type: HYPERLINK_RELATIONSHIP_TYPE,
832
+ target: node.href,
833
+ targetMode: "external",
834
+ });
835
+ state.retainedRelationshipIds.add(relationshipId);
836
+ return `<w:hyperlink r:id="${relationshipId}">`;
837
+ })();
838
+ const hyperlinkClose = "</w:hyperlink>";
839
+ const boundaries = new Map<number, number>();
840
+ let nextCursor = cursor;
841
+ let nextOffset = xmlOffset + hyperlinkOpen.length;
842
+ boundaries.set(cursor, nextOffset);
843
+ const children: string[] = [];
844
+
845
+ for (const child of node.children) {
846
+ const result = serializeInlineNode(child, state, nextCursor, nextOffset);
847
+ children.push(result.xml);
848
+ for (const [position, index] of result.boundaries) {
849
+ boundaries.set(position, index);
850
+ }
851
+ nextCursor = result.cursor;
852
+ nextOffset += result.xml.length;
853
+ }
854
+
855
+ boundaries.set(nextCursor, nextOffset);
856
+ return {
857
+ xml: `${hyperlinkOpen}${children.join("")}${hyperlinkClose}`,
858
+ cursor: nextCursor,
859
+ boundaries,
860
+ };
861
+ }
862
+ case "field": {
863
+ if (node.children && node.children.length > 0) {
864
+ const boundaries = new Map<number, number>();
865
+ boundaries.set(cursor, xmlOffset);
866
+ let nextCursor = cursor;
867
+ let nextOffset = xmlOffset;
868
+
869
+ if (node.fieldType === "complex") {
870
+ const beginXml =
871
+ `<w:r><w:fldChar w:fldCharType="begin"/></w:r>` +
872
+ `<w:r><w:instrText xml:space="preserve"> ${escapeXml(node.instruction)} </w:instrText></w:r>` +
873
+ `<w:r><w:fldChar w:fldCharType="separate"/></w:r>`;
874
+ nextOffset += beginXml.length;
875
+
876
+ const children: string[] = [beginXml];
877
+ for (const child of node.children) {
878
+ const result = serializeInlineNode(child, state, nextCursor, nextOffset);
879
+ children.push(result.xml);
880
+ for (const [position, index] of result.boundaries) {
881
+ boundaries.set(position, index);
882
+ }
883
+ nextCursor = result.cursor;
884
+ nextOffset += result.xml.length;
885
+ }
886
+ const endXml = `<w:r><w:fldChar w:fldCharType="end"/></w:r>`;
887
+ children.push(endXml);
888
+ nextOffset += endXml.length;
889
+ boundaries.set(nextCursor, nextOffset);
890
+ return { xml: children.join(""), cursor: nextCursor, boundaries };
891
+ }
892
+
893
+ // Simple field with children
894
+ const openXml = `<w:fldSimple w:instr="${escapeAttribute(node.instruction)}">`;
895
+ nextOffset += openXml.length;
896
+ const children: string[] = [openXml];
897
+ for (const child of node.children) {
898
+ const result = serializeInlineNode(child, state, nextCursor, nextOffset);
899
+ children.push(result.xml);
900
+ for (const [position, index] of result.boundaries) {
901
+ boundaries.set(position, index);
902
+ }
903
+ nextCursor = result.cursor;
904
+ nextOffset += result.xml.length;
905
+ }
906
+ children.push("</w:fldSimple>");
907
+ nextOffset += "</w:fldSimple>".length;
908
+ boundaries.set(nextCursor, nextOffset);
909
+ return { xml: children.join(""), cursor: nextCursor, boundaries };
910
+ }
911
+
912
+ const xml = `<w:fldSimple w:instr="${escapeAttribute(node.instruction)}"/>`;
913
+ const boundaries = new Map<number, number>();
914
+ boundaries.set(cursor, xmlOffset);
915
+ boundaries.set(cursor + 1, xmlOffset + xml.length);
916
+ return {
917
+ xml,
918
+ cursor: cursor + 1,
919
+ boundaries,
920
+ };
921
+ }
922
+ case "bookmark_start": {
923
+ const xml =
924
+ `<w:bookmarkStart w:id="${escapeAttribute(node.bookmarkId)}"` +
925
+ ` w:name="${escapeAttribute(node.name)}"/>`;
926
+ const boundaries = new Map<number, number>();
927
+ boundaries.set(cursor, xmlOffset);
928
+ boundaries.set(cursor, xmlOffset + xml.length);
929
+ return { xml, cursor, boundaries };
930
+ }
931
+ case "bookmark_end": {
932
+ const xml = `<w:bookmarkEnd w:id="${escapeAttribute(node.bookmarkId)}"/>`;
933
+ const boundaries = new Map<number, number>();
934
+ boundaries.set(cursor, xmlOffset);
935
+ boundaries.set(cursor, xmlOffset + xml.length);
936
+ return { xml, cursor, boundaries };
937
+ }
938
+ case "footnote_ref": {
939
+ const refElement =
940
+ node.noteKind === "footnote"
941
+ ? `<w:footnoteReference w:id="${escapeAttribute(node.noteId)}"/>`
942
+ : `<w:endnoteReference w:id="${escapeAttribute(node.noteId)}"/>`;
943
+ const styleVal =
944
+ node.noteKind === "footnote"
945
+ ? "FootnoteReference"
946
+ : "EndnoteReference";
947
+ const xml = `<w:r><w:rPr><w:rStyle w:val="${styleVal}"/></w:rPr>${refElement}</w:r>`;
948
+ const boundaries = new Map<number, number>();
949
+ boundaries.set(cursor, xmlOffset);
950
+ boundaries.set(cursor + 1, xmlOffset + xml.length);
951
+ return {
952
+ xml,
953
+ cursor: cursor + 1,
954
+ boundaries,
955
+ };
956
+ }
957
+ default: {
958
+ const boundaries = new Map<number, number>();
959
+ boundaries.set(cursor, xmlOffset);
960
+ return { xml: "", cursor, boundaries };
961
+ }
962
+ }
963
+ }
964
+
965
+ function serializeImageNode(
966
+ node: Extract<InlineNode, { type: "image" }>,
967
+ state: SerializationState,
968
+ ): string {
969
+ const placementXml = typeof node.placementXml === "string" ? node.placementXml.trim() : "";
970
+ if (placementXml.length > 0) {
971
+ return placementXml.startsWith("<w:r") ? placementXml : `<w:r>${placementXml}</w:r>`;
972
+ }
973
+
974
+ const mediaItem = state.media.items[node.mediaId];
975
+ if (mediaItem?.relationshipId && state.existingRelationshipMap.has(mediaItem.relationshipId)) {
976
+ const altText = node.altText ?? mediaItem.altText ?? mediaItem.filename;
977
+ return `<w:r><w:drawing><wp:inline xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" xmlns:pic="http://schemas.openxmlformats.org/drawingml/2006/picture" xmlns:wp="http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing"><wp:extent cx="9525" cy="9525"/><wp:docPr id="1" name="${escapeAttribute(mediaItem.filename)}" descr="${escapeAttribute(altText ?? "")}"/><wp:cNvGraphicFramePr/><a:graphic><a:graphicData uri="http://schemas.openxmlformats.org/drawingml/2006/picture"><pic:pic><pic:nvPicPr><pic:cNvPr id="0" name="${escapeAttribute(mediaItem.filename)}"/><pic:cNvPicPr/></pic:nvPicPr><pic:blipFill><a:blip r:embed="${escapeAttribute(mediaItem.relationshipId)}"/><a:stretch><a:fillRect/></a:stretch></pic:blipFill><pic:spPr><a:xfrm><a:off x="0" y="0"/><a:ext cx="9525" cy="9525"/></a:xfrm><a:prstGeom prst="rect"><a:avLst/></a:prstGeom></pic:spPr></pic:pic></a:graphicData></a:graphic></wp:inline></w:drawing></w:r>`;
978
+ }
979
+
980
+ return serializeRun({
981
+ kind: "text",
982
+ text: node.altText ?? "[Image]",
983
+ });
984
+ }
985
+
986
+ function serializeRun(piece: RunPiece): string {
987
+ const marks = piece.kind === "text" ? piece.marks : undefined;
988
+ const properties = serializeRunProperties(marks);
989
+ const content = serializeRunPiece(piece);
990
+ return `<w:r>${properties}${content}</w:r>`;
991
+ }
992
+
993
+ function serializeRunProperties(marks: TextMark[] | undefined): string {
994
+ if (!marks || marks.length === 0) {
995
+ return "";
996
+ }
997
+
998
+ const markParts: string[] = [];
999
+ for (const mark of marks) {
1000
+ switch (mark.type) {
1001
+ case "bold":
1002
+ markParts.push("<w:b/>");
1003
+ break;
1004
+ case "italic":
1005
+ markParts.push("<w:i/>");
1006
+ break;
1007
+ case "underline":
1008
+ markParts.push(`<w:u w:val="single"/>`);
1009
+ break;
1010
+ case "strikethrough":
1011
+ markParts.push("<w:strike/>");
1012
+ break;
1013
+ case "doubleStrikethrough":
1014
+ markParts.push("<w:dstrike/>");
1015
+ break;
1016
+ case "vanish":
1017
+ markParts.push("<w:vanish/>");
1018
+ break;
1019
+ case "lang":
1020
+ markParts.push(`<w:lang w:val="${escapeAttribute(mark.val)}"/>`);
1021
+ break;
1022
+ case "highlight":
1023
+ markParts.push(`<w:highlight w:val="${escapeAttribute(mark.val)}"/>`);
1024
+ break;
1025
+ case "backgroundColor":
1026
+ markParts.push(
1027
+ `<w:shd w:val="clear" w:color="auto" w:fill="${escapeAttribute(mark.color)}"/>`,
1028
+ );
1029
+ break;
1030
+ case "charSpacing":
1031
+ markParts.push(`<w:spacing w:val="${mark.val}"/>`);
1032
+ break;
1033
+ case "kerning":
1034
+ markParts.push(`<w:kern w:val="${mark.val}"/>`);
1035
+ break;
1036
+ case "emboss":
1037
+ markParts.push("<w:emboss/>");
1038
+ break;
1039
+ case "imprint":
1040
+ markParts.push("<w:imprint/>");
1041
+ break;
1042
+ case "shadow":
1043
+ markParts.push("<w:shadow/>");
1044
+ break;
1045
+ case "position":
1046
+ markParts.push(`<w:position w:val="${mark.val}"/>`);
1047
+ break;
1048
+ case "textFill":
1049
+ markParts.push(mark.xml);
1050
+ break;
1051
+ case "fontFamily":
1052
+ markParts.push(`<w:rFonts w:ascii="${escapeAttribute(mark.val)}" w:hAnsi="${escapeAttribute(mark.val)}"/>`);
1053
+ break;
1054
+ case "fontSize":
1055
+ markParts.push(`<w:sz w:val="${mark.val}"/>`);
1056
+ break;
1057
+ case "textColor":
1058
+ markParts.push(`<w:color w:val="${escapeAttribute(mark.color)}"/>`);
1059
+ break;
1060
+ case "smallCaps":
1061
+ markParts.push("<w:smallCaps/>");
1062
+ break;
1063
+ case "allCaps":
1064
+ markParts.push("<w:caps/>");
1065
+ break;
1066
+ }
1067
+ }
1068
+
1069
+ const children = markParts.join("");
1070
+ return children.length > 0 ? `<w:rPr>${children}</w:rPr>` : "";
1071
+ }
1072
+
1073
+ function requiresPreservedSpace(text: string): boolean {
1074
+ return /^\s/.test(text) || /\s$/.test(text) || text.includes(" ");
1075
+ }
1076
+
1077
+ function lookupOpaqueXml(fragmentId: string, state: SerializationState): string {
1078
+ const fragment = getOpaqueFragment(state.preservation, fragmentId);
1079
+ if (!fragment || fragment.payloadKind !== "xml-subtree") {
1080
+ throw new Error(`Missing preserved OOXML fragment ${fragmentId} during serialization.`);
1081
+ }
1082
+
1083
+ retainRelationshipsForFragment(
1084
+ fragment,
1085
+ state.relationships,
1086
+ state.existingRelationshipMap,
1087
+ state.retainedRelationshipIds,
1088
+ );
1089
+ return fragment.payloadReference;
1090
+ }
1091
+
1092
+ function cloneRelationship(relationship: OpcRelationship): OpcRelationship {
1093
+ return { ...relationship };
1094
+ }
1095
+
1096
+ function looksLikeSectionPropertiesXml(xml: string): boolean {
1097
+ return /^<[^>]*:?sectPr(?:\s|>|\/)/.test(xml.trim());
1098
+ }
1099
+
1100
+ function escapeXml(value: string): string {
1101
+ return value
1102
+ .replace(/&/g, "&amp;")
1103
+ .replace(/</g, "&lt;")
1104
+ .replace(/>/g, "&gt;");
1105
+ }
1106
+
1107
+ function escapeAttribute(value: string): string {
1108
+ return escapeXml(value).replace(/"/g, "&quot;");
1109
+ }
1110
+
1111
+ function offsetParagraphBoundary(
1112
+ boundary: RevisionParagraphBoundary,
1113
+ offset: number,
1114
+ ): RevisionParagraphBoundary {
1115
+ return {
1116
+ ...boundary,
1117
+ boundaries: new Map(
1118
+ [...boundary.boundaries.entries()].map(([position, index]) => [
1119
+ position,
1120
+ index + offset,
1121
+ ]),
1122
+ ),
1123
+ paragraphStart: boundary.paragraphStart + offset,
1124
+ paragraphStartTagEnd: boundary.paragraphStartTagEnd + offset,
1125
+ paragraphEndTagStart: boundary.paragraphEndTagStart + offset,
1126
+ paragraphEnd: boundary.paragraphEnd + offset,
1127
+ ...(boundary.paragraphPropertiesStart !== undefined
1128
+ ? { paragraphPropertiesStart: boundary.paragraphPropertiesStart + offset }
1129
+ : {}),
1130
+ ...(boundary.paragraphPropertiesEnd !== undefined
1131
+ ? { paragraphPropertiesEnd: boundary.paragraphPropertiesEnd + offset }
1132
+ : {}),
1133
+ ...(boundary.paragraphRunPropertiesStart !== undefined
1134
+ ? { paragraphRunPropertiesStart: boundary.paragraphRunPropertiesStart + offset }
1135
+ : {}),
1136
+ ...(boundary.paragraphRunPropertiesEnd !== undefined
1137
+ ? { paragraphRunPropertiesEnd: boundary.paragraphRunPropertiesEnd + offset }
1138
+ : {}),
1139
+ };
1140
+ }
1141
+
1142
+ function serializeDocumentAttributes(
1143
+ attributes: Record<string, string> | undefined,
1144
+ content?: DocumentRootNode,
1145
+ ): string {
1146
+ const merged = {
1147
+ "xmlns:r": "http://schemas.openxmlformats.org/officeDocument/2006/relationships",
1148
+ "xmlns:w": "http://schemas.openxmlformats.org/wordprocessingml/2006/main",
1149
+ ...(content && documentNeedsW14Namespace(content)
1150
+ ? { "xmlns:w14": "http://schemas.microsoft.com/office/word/2010/wordml" }
1151
+ : {}),
1152
+ ...(attributes ?? {}),
1153
+ };
1154
+
1155
+ return Object.entries(merged)
1156
+ .map(([name, value]) => ` ${name}="${escapeAttribute(value)}"`)
1157
+ .join("");
1158
+ }
1159
+
1160
+ export function serializeSectionPropertiesXml(props: SectionProperties): string {
1161
+ const children: string[] = [];
1162
+
1163
+ // Header references
1164
+ if (props.headerReferences) {
1165
+ for (const ref of props.headerReferences) {
1166
+ children.push(`<w:headerReference w:type="${escapeAttribute(ref.variant)}" r:id="${escapeAttribute(ref.relationshipId)}"/>`);
1167
+ }
1168
+ }
1169
+
1170
+ // Footer references
1171
+ if (props.footerReferences) {
1172
+ for (const ref of props.footerReferences) {
1173
+ children.push(`<w:footerReference w:type="${escapeAttribute(ref.variant)}" r:id="${escapeAttribute(ref.relationshipId)}"/>`);
1174
+ }
1175
+ }
1176
+
1177
+ // Section type
1178
+ if (props.sectionType) {
1179
+ children.push(`<w:type w:val="${escapeAttribute(props.sectionType)}"/>`);
1180
+ }
1181
+
1182
+ // Page size
1183
+ if (props.pageSize) {
1184
+ let pgSz = `<w:pgSz w:w="${props.pageSize.width}" w:h="${props.pageSize.height}"`;
1185
+ if (props.pageSize.orientation) {
1186
+ pgSz += ` w:orient="${props.pageSize.orientation}"`;
1187
+ }
1188
+ pgSz += "/>";
1189
+ children.push(pgSz);
1190
+ }
1191
+
1192
+ // Page margins
1193
+ if (props.pageMargins) {
1194
+ let pgMar = `<w:pgMar w:top="${props.pageMargins.top}" w:right="${props.pageMargins.right}" w:bottom="${props.pageMargins.bottom}" w:left="${props.pageMargins.left}"`;
1195
+ if (props.pageMargins.header !== undefined) pgMar += ` w:header="${props.pageMargins.header}"`;
1196
+ if (props.pageMargins.footer !== undefined) pgMar += ` w:footer="${props.pageMargins.footer}"`;
1197
+ if (props.pageMargins.gutter !== undefined) pgMar += ` w:gutter="${props.pageMargins.gutter}"`;
1198
+ pgMar += "/>";
1199
+ children.push(pgMar);
1200
+ }
1201
+
1202
+ // Columns
1203
+ if (props.columns) {
1204
+ let cols = "<w:cols";
1205
+ if (props.columns.count !== undefined) cols += ` w:num="${props.columns.count}"`;
1206
+ if (props.columns.space !== undefined) cols += ` w:space="${props.columns.space}"`;
1207
+ if (props.columns.equalWidth !== undefined) cols += ` w:equalWidth="${props.columns.equalWidth ? "1" : "0"}"`;
1208
+ if (props.columns.separator) cols += ` w:sep="1"`;
1209
+ if (props.columns.columns && props.columns.columns.length > 0) {
1210
+ cols += ">";
1211
+ for (const col of props.columns.columns) {
1212
+ let colXml = `<w:col w:w="${col.width}"`;
1213
+ if (col.space !== undefined) colXml += ` w:space="${col.space}"`;
1214
+ colXml += "/>";
1215
+ cols += colXml;
1216
+ }
1217
+ cols += "</w:cols>";
1218
+ } else {
1219
+ cols += "/>";
1220
+ }
1221
+ children.push(cols);
1222
+ }
1223
+
1224
+ // Page numbering
1225
+ if (props.pageNumbering) {
1226
+ let pgNum = "<w:pgNumType";
1227
+ if (props.pageNumbering.format) pgNum += ` w:fmt="${escapeAttribute(props.pageNumbering.format)}"`;
1228
+ if (props.pageNumbering.start !== undefined) pgNum += ` w:start="${props.pageNumbering.start}"`;
1229
+ if (props.pageNumbering.chapStyle) pgNum += ` w:chapStyle="${escapeAttribute(props.pageNumbering.chapStyle)}"`;
1230
+ if (props.pageNumbering.chapSep) pgNum += ` w:chapSep="${escapeAttribute(props.pageNumbering.chapSep)}"`;
1231
+ pgNum += "/>";
1232
+ children.push(pgNum);
1233
+ }
1234
+
1235
+ if (props.lineNumbering) {
1236
+ let lineNumbering = "<w:lnNumType";
1237
+ if (props.lineNumbering.countBy !== undefined) {
1238
+ lineNumbering += ` w:countBy="${props.lineNumbering.countBy}"`;
1239
+ }
1240
+ if (props.lineNumbering.start !== undefined) {
1241
+ lineNumbering += ` w:start="${props.lineNumbering.start}"`;
1242
+ }
1243
+ if (props.lineNumbering.distance !== undefined) {
1244
+ lineNumbering += ` w:distance="${props.lineNumbering.distance}"`;
1245
+ }
1246
+ if (props.lineNumbering.restart) {
1247
+ lineNumbering += ` w:restart="${escapeAttribute(props.lineNumbering.restart)}"`;
1248
+ }
1249
+ lineNumbering += "/>";
1250
+ children.push(lineNumbering);
1251
+ }
1252
+
1253
+ if (props.pageBorders) {
1254
+ const attrs: string[] = [];
1255
+ if (props.pageBorders.offsetFrom) {
1256
+ attrs.push(`w:offsetFrom="${escapeAttribute(props.pageBorders.offsetFrom)}"`);
1257
+ }
1258
+ if (props.pageBorders.display) {
1259
+ attrs.push(`w:display="${escapeAttribute(props.pageBorders.display)}"`);
1260
+ }
1261
+ if (props.pageBorders.zOrder) {
1262
+ attrs.push(`w:zOrder="${escapeAttribute(props.pageBorders.zOrder)}"`);
1263
+ }
1264
+ const borderXml = [
1265
+ serializeBorder("top", props.pageBorders.top),
1266
+ serializeBorder("left", props.pageBorders.left),
1267
+ serializeBorder("bottom", props.pageBorders.bottom),
1268
+ serializeBorder("right", props.pageBorders.right),
1269
+ ].filter((entry) => entry.length > 0);
1270
+ if (attrs.length > 0 || borderXml.length > 0) {
1271
+ children.push(
1272
+ `<w:pgBorders${attrs.length > 0 ? ` ${attrs.join(" ")}` : ""}>${borderXml.join("")}</w:pgBorders>`,
1273
+ );
1274
+ }
1275
+ }
1276
+
1277
+ // Title page
1278
+ if (props.titlePage) {
1279
+ children.push("<w:titlePg/>");
1280
+ }
1281
+
1282
+ if (props.documentGrid) {
1283
+ const attrs: string[] = [];
1284
+ if (props.documentGrid.type) {
1285
+ attrs.push(`w:type="${escapeAttribute(props.documentGrid.type)}"`);
1286
+ }
1287
+ if (props.documentGrid.linePitch !== undefined) {
1288
+ attrs.push(`w:linePitch="${props.documentGrid.linePitch}"`);
1289
+ }
1290
+ if (props.documentGrid.charSpace !== undefined) {
1291
+ attrs.push(`w:charSpace="${props.documentGrid.charSpace}"`);
1292
+ }
1293
+ if (attrs.length > 0) {
1294
+ children.push(`<w:docGrid ${attrs.join(" ")}/>`);
1295
+ }
1296
+ }
1297
+
1298
+ if (children.length === 0) {
1299
+ return "<w:sectPr/>";
1300
+ }
1301
+
1302
+ return `<w:sectPr>${children.join("")}</w:sectPr>`;
1303
+ }
1304
+
1305
+ function wrapInlineRawXml(rawXml: string): string {
1306
+ const trimmed = rawXml.trimStart();
1307
+ return trimmed.startsWith("<w:r") ? rawXml : `<w:r>${rawXml}</w:r>`;
1308
+ }
1309
+
1310
+ function documentNeedsW14Namespace(content: DocumentRootNode): boolean {
1311
+ return content.children.some(blockNeedsW14Namespace);
1312
+ }
1313
+
1314
+ function blockNeedsW14Namespace(block: DocumentRootNode["children"][number]): boolean {
1315
+ switch (block.type) {
1316
+ case "paragraph":
1317
+ return block.children.some(inlineNodeNeedsW14Namespace);
1318
+ case "table":
1319
+ return block.rows.some((row) =>
1320
+ row.cells.some((cell) => cell.children.some(blockNeedsW14Namespace)),
1321
+ );
1322
+ case "sdt":
1323
+ return Boolean(block.properties.checkbox) || block.children.some(blockNeedsW14Namespace);
1324
+ case "custom_xml":
1325
+ return block.children.some(blockNeedsW14Namespace);
1326
+ default:
1327
+ return false;
1328
+ }
1329
+ }
1330
+
1331
+ function inlineNodeNeedsW14Namespace(node: InlineNode): boolean {
1332
+ switch (node.type) {
1333
+ case "text":
1334
+ case "symbol":
1335
+ return Boolean(node.marks?.some((mark) => mark.type === "textFill"));
1336
+ case "hyperlink":
1337
+ case "field":
1338
+ return node.children.some(inlineNodeNeedsW14Namespace);
1339
+ default:
1340
+ return false;
1341
+ }
1342
+ }