@usejunior/docx-core 0.9.1 → 0.11.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 (335) hide show
  1. package/LICENSE +202 -21
  2. package/NOTICE +2 -0
  3. package/README.md +2 -2
  4. package/dist/.tsbuildinfo +1 -1
  5. package/dist/atomizer.d.ts +28 -8
  6. package/dist/atomizer.d.ts.map +1 -1
  7. package/dist/atomizer.js +96 -25
  8. package/dist/atomizer.js.map +1 -1
  9. package/dist/baselines/atomizer/auxiliaryIdCollision.d.ts +99 -0
  10. package/dist/baselines/atomizer/auxiliaryIdCollision.d.ts.map +1 -0
  11. package/dist/baselines/atomizer/auxiliaryIdCollision.js +415 -0
  12. package/dist/baselines/atomizer/auxiliaryIdCollision.js.map +1 -0
  13. package/dist/baselines/atomizer/documentReconstructor.d.ts.map +1 -1
  14. package/dist/baselines/atomizer/documentReconstructor.js +333 -112
  15. package/dist/baselines/atomizer/documentReconstructor.js.map +1 -1
  16. package/dist/baselines/atomizer/formattingFidelity.d.ts +99 -0
  17. package/dist/baselines/atomizer/formattingFidelity.d.ts.map +1 -0
  18. package/dist/baselines/atomizer/formattingFidelity.js +449 -0
  19. package/dist/baselines/atomizer/formattingFidelity.js.map +1 -0
  20. package/dist/baselines/atomizer/inPlaceModifier-bookmarks.d.ts +37 -0
  21. package/dist/baselines/atomizer/inPlaceModifier-bookmarks.d.ts.map +1 -0
  22. package/dist/baselines/atomizer/inPlaceModifier-bookmarks.js +189 -0
  23. package/dist/baselines/atomizer/inPlaceModifier-bookmarks.js.map +1 -0
  24. package/dist/baselines/atomizer/inPlaceModifier-containers.d.ts +74 -0
  25. package/dist/baselines/atomizer/inPlaceModifier-containers.d.ts.map +1 -0
  26. package/dist/baselines/atomizer/inPlaceModifier-containers.js +171 -0
  27. package/dist/baselines/atomizer/inPlaceModifier-containers.js.map +1 -0
  28. package/dist/baselines/atomizer/inPlaceModifier-deletion.d.ts +88 -0
  29. package/dist/baselines/atomizer/inPlaceModifier-deletion.d.ts.map +1 -0
  30. package/dist/baselines/atomizer/inPlaceModifier-deletion.js +326 -0
  31. package/dist/baselines/atomizer/inPlaceModifier-deletion.js.map +1 -0
  32. package/dist/baselines/atomizer/inPlaceModifier-postprocess.d.ts +85 -0
  33. package/dist/baselines/atomizer/inPlaceModifier-postprocess.d.ts.map +1 -0
  34. package/dist/baselines/atomizer/inPlaceModifier-postprocess.js +402 -0
  35. package/dist/baselines/atomizer/inPlaceModifier-postprocess.js.map +1 -0
  36. package/dist/baselines/atomizer/inPlaceModifier-presplit.d.ts +39 -0
  37. package/dist/baselines/atomizer/inPlaceModifier-presplit.d.ts.map +1 -0
  38. package/dist/baselines/atomizer/inPlaceModifier-presplit.js +265 -0
  39. package/dist/baselines/atomizer/inPlaceModifier-presplit.js.map +1 -0
  40. package/dist/baselines/atomizer/inPlaceModifier-shared.d.ts +62 -0
  41. package/dist/baselines/atomizer/inPlaceModifier-shared.d.ts.map +1 -0
  42. package/dist/baselines/atomizer/inPlaceModifier-shared.js +139 -0
  43. package/dist/baselines/atomizer/inPlaceModifier-shared.js.map +1 -0
  44. package/dist/baselines/atomizer/inPlaceModifier-wrappers.d.ts +198 -0
  45. package/dist/baselines/atomizer/inPlaceModifier-wrappers.d.ts.map +1 -0
  46. package/dist/baselines/atomizer/inPlaceModifier-wrappers.js +475 -0
  47. package/dist/baselines/atomizer/inPlaceModifier-wrappers.js.map +1 -0
  48. package/dist/baselines/atomizer/inPlaceModifier.d.ts +6 -290
  49. package/dist/baselines/atomizer/inPlaceModifier.d.ts.map +1 -1
  50. package/dist/baselines/atomizer/inPlaceModifier.js +23 -1828
  51. package/dist/baselines/atomizer/inPlaceModifier.js.map +1 -1
  52. package/dist/baselines/atomizer/pipeline.d.ts +36 -2
  53. package/dist/baselines/atomizer/pipeline.d.ts.map +1 -1
  54. package/dist/baselines/atomizer/pipeline.js +216 -144
  55. package/dist/baselines/atomizer/pipeline.js.map +1 -1
  56. package/dist/baselines/atomizer/trackChangesAcceptorAst.d.ts.map +1 -1
  57. package/dist/baselines/atomizer/trackChangesAcceptorAst.js +199 -173
  58. package/dist/baselines/atomizer/trackChangesAcceptorAst.js.map +1 -1
  59. package/dist/baselines/wmlcomparer/DotnetCli.d.ts.map +1 -1
  60. package/dist/baselines/wmlcomparer/DotnetCli.js +7 -0
  61. package/dist/baselines/wmlcomparer/DotnetCli.js.map +1 -1
  62. package/dist/cli/compare-two.d.ts.map +1 -1
  63. package/dist/cli/compare-two.js +3 -1
  64. package/dist/cli/compare-two.js.map +1 -1
  65. package/dist/cli/conformance-adapter.d.ts +3 -0
  66. package/dist/cli/conformance-adapter.d.ts.map +1 -0
  67. package/dist/cli/conformance-adapter.js +93 -0
  68. package/dist/cli/conformance-adapter.js.map +1 -0
  69. package/dist/cli/index.d.ts.map +1 -1
  70. package/dist/cli/index.js +5 -1
  71. package/dist/cli/index.js.map +1 -1
  72. package/dist/compare-types.d.ts +197 -0
  73. package/dist/compare-types.d.ts.map +1 -0
  74. package/dist/compare-types.js +2 -0
  75. package/dist/compare-types.js.map +1 -0
  76. package/dist/core-types.d.ts +5 -1
  77. package/dist/core-types.d.ts.map +1 -1
  78. package/dist/core-types.js +5 -1
  79. package/dist/core-types.js.map +1 -1
  80. package/dist/footnotes.d.ts +8 -3
  81. package/dist/footnotes.d.ts.map +1 -1
  82. package/dist/footnotes.js +8 -3
  83. package/dist/footnotes.js.map +1 -1
  84. package/dist/generation/compile.d.ts +21 -0
  85. package/dist/generation/compile.d.ts.map +1 -0
  86. package/dist/generation/compile.js +46 -0
  87. package/dist/generation/compile.js.map +1 -0
  88. package/dist/generation/context.d.ts +42 -0
  89. package/dist/generation/context.d.ts.map +1 -0
  90. package/dist/generation/context.js +65 -0
  91. package/dist/generation/context.js.map +1 -0
  92. package/dist/generation/emit/comments-part.d.ts +36 -0
  93. package/dist/generation/emit/comments-part.d.ts.map +1 -0
  94. package/dist/generation/emit/comments-part.js +116 -0
  95. package/dist/generation/emit/comments-part.js.map +1 -0
  96. package/dist/generation/emit/document-part.d.ts +24 -0
  97. package/dist/generation/emit/document-part.d.ts.map +1 -0
  98. package/dist/generation/emit/document-part.js +60 -0
  99. package/dist/generation/emit/document-part.js.map +1 -0
  100. package/dist/generation/emit/emit-context.d.ts +26 -0
  101. package/dist/generation/emit/emit-context.d.ts.map +1 -0
  102. package/dist/generation/emit/emit-context.js +19 -0
  103. package/dist/generation/emit/emit-context.js.map +1 -0
  104. package/dist/generation/emit/header-footer-part.d.ts +23 -0
  105. package/dist/generation/emit/header-footer-part.d.ts.map +1 -0
  106. package/dist/generation/emit/header-footer-part.js +57 -0
  107. package/dist/generation/emit/header-footer-part.js.map +1 -0
  108. package/dist/generation/emit/numbering-part.d.ts +29 -0
  109. package/dist/generation/emit/numbering-part.d.ts.map +1 -0
  110. package/dist/generation/emit/numbering-part.js +100 -0
  111. package/dist/generation/emit/numbering-part.js.map +1 -0
  112. package/dist/generation/emit/package-parts.d.ts +24 -0
  113. package/dist/generation/emit/package-parts.d.ts.map +1 -0
  114. package/dist/generation/emit/package-parts.js +121 -0
  115. package/dist/generation/emit/package-parts.js.map +1 -0
  116. package/dist/generation/emit/paragraph.d.ts +24 -0
  117. package/dist/generation/emit/paragraph.d.ts.map +1 -0
  118. package/dist/generation/emit/paragraph.js +63 -0
  119. package/dist/generation/emit/paragraph.js.map +1 -0
  120. package/dist/generation/emit/properties.d.ts +34 -0
  121. package/dist/generation/emit/properties.d.ts.map +1 -0
  122. package/dist/generation/emit/properties.js +138 -0
  123. package/dist/generation/emit/properties.js.map +1 -0
  124. package/dist/generation/emit/run.d.ts +15 -0
  125. package/dist/generation/emit/run.d.ts.map +1 -0
  126. package/dist/generation/emit/run.js +71 -0
  127. package/dist/generation/emit/run.js.map +1 -0
  128. package/dist/generation/emit/section.d.ts +29 -0
  129. package/dist/generation/emit/section.d.ts.map +1 -0
  130. package/dist/generation/emit/section.js +117 -0
  131. package/dist/generation/emit/section.js.map +1 -0
  132. package/dist/generation/emit/settings-part.d.ts +13 -0
  133. package/dist/generation/emit/settings-part.d.ts.map +1 -0
  134. package/dist/generation/emit/settings-part.js +24 -0
  135. package/dist/generation/emit/settings-part.js.map +1 -0
  136. package/dist/generation/emit/styles-part.d.ts +16 -0
  137. package/dist/generation/emit/styles-part.d.ts.map +1 -0
  138. package/dist/generation/emit/styles-part.js +80 -0
  139. package/dist/generation/emit/styles-part.js.map +1 -0
  140. package/dist/generation/emit/table.d.ts +26 -0
  141. package/dist/generation/emit/table.d.ts.map +1 -0
  142. package/dist/generation/emit/table.js +196 -0
  143. package/dist/generation/emit/table.js.map +1 -0
  144. package/dist/generation/errors.d.ts +22 -0
  145. package/dist/generation/errors.d.ts.map +1 -0
  146. package/dist/generation/errors.js +29 -0
  147. package/dist/generation/errors.js.map +1 -0
  148. package/dist/generation/index.d.ts +13 -0
  149. package/dist/generation/index.d.ts.map +1 -0
  150. package/dist/generation/index.js +12 -0
  151. package/dist/generation/index.js.map +1 -0
  152. package/dist/generation/ordering.d.ts +46 -0
  153. package/dist/generation/ordering.d.ts.map +1 -0
  154. package/dist/generation/ordering.js +119 -0
  155. package/dist/generation/ordering.js.map +1 -0
  156. package/dist/generation/recipes.d.ts +47 -0
  157. package/dist/generation/recipes.d.ts.map +1 -0
  158. package/dist/generation/recipes.js +84 -0
  159. package/dist/generation/recipes.js.map +1 -0
  160. package/dist/generation/structural-checks.d.ts +24 -0
  161. package/dist/generation/structural-checks.d.ts.map +1 -0
  162. package/dist/generation/structural-checks.js +318 -0
  163. package/dist/generation/structural-checks.js.map +1 -0
  164. package/dist/generation/types.d.ts +217 -0
  165. package/dist/generation/types.d.ts.map +1 -0
  166. package/dist/generation/types.js +16 -0
  167. package/dist/generation/types.js.map +1 -0
  168. package/dist/generation/validate-spec.d.ts +27 -0
  169. package/dist/generation/validate-spec.d.ts.map +1 -0
  170. package/dist/generation/validate-spec.js +307 -0
  171. package/dist/generation/validate-spec.js.map +1 -0
  172. package/dist/index.d.ts +9 -150
  173. package/dist/index.d.ts.map +1 -1
  174. package/dist/index.js +14 -0
  175. package/dist/index.js.map +1 -1
  176. package/dist/integration/generation-probes.d.ts +15 -0
  177. package/dist/integration/generation-probes.d.ts.map +1 -0
  178. package/dist/integration/generation-probes.js +84 -0
  179. package/dist/integration/generation-probes.js.map +1 -0
  180. package/dist/integration/libreoffice-oracle.d.ts +49 -0
  181. package/dist/integration/libreoffice-oracle.d.ts.map +1 -0
  182. package/dist/integration/libreoffice-oracle.js +290 -0
  183. package/dist/integration/libreoffice-oracle.js.map +1 -0
  184. package/dist/integration/synthetic-docx-fixture.d.ts +72 -0
  185. package/dist/integration/synthetic-docx-fixture.d.ts.map +1 -1
  186. package/dist/integration/synthetic-docx-fixture.js +131 -4
  187. package/dist/integration/synthetic-docx-fixture.js.map +1 -1
  188. package/dist/primitives/accept_changes.d.ts +4 -3
  189. package/dist/primitives/accept_changes.d.ts.map +1 -1
  190. package/dist/primitives/accept_changes.js +163 -77
  191. package/dist/primitives/accept_changes.js.map +1 -1
  192. package/dist/primitives/comments.d.ts +12 -3
  193. package/dist/primitives/comments.d.ts.map +1 -1
  194. package/dist/primitives/comments.js +374 -97
  195. package/dist/primitives/comments.js.map +1 -1
  196. package/dist/primitives/content_fingerprint.d.ts +29 -0
  197. package/dist/primitives/content_fingerprint.d.ts.map +1 -0
  198. package/dist/primitives/content_fingerprint.js +63 -0
  199. package/dist/primitives/content_fingerprint.js.map +1 -0
  200. package/dist/primitives/document.d.ts +94 -15
  201. package/dist/primitives/document.d.ts.map +1 -1
  202. package/dist/primitives/document.js +373 -36
  203. package/dist/primitives/document.js.map +1 -1
  204. package/dist/primitives/document_view-comments.d.ts +18 -0
  205. package/dist/primitives/document_view-comments.d.ts.map +1 -0
  206. package/dist/primitives/document_view-comments.js +160 -0
  207. package/dist/primitives/document_view-comments.js.map +1 -0
  208. package/dist/primitives/document_view-headings.d.ts +45 -0
  209. package/dist/primitives/document_view-headings.d.ts.map +1 -0
  210. package/dist/primitives/document_view-headings.js +247 -0
  211. package/dist/primitives/document_view-headings.js.map +1 -0
  212. package/dist/primitives/document_view-styles.d.ts +11 -0
  213. package/dist/primitives/document_view-styles.d.ts.map +1 -0
  214. package/dist/primitives/document_view-styles.js +104 -0
  215. package/dist/primitives/document_view-styles.js.map +1 -0
  216. package/dist/primitives/document_view-toon.d.ts +37 -0
  217. package/dist/primitives/document_view-toon.d.ts.map +1 -0
  218. package/dist/primitives/document_view-toon.js +199 -0
  219. package/dist/primitives/document_view-toon.js.map +1 -0
  220. package/dist/primitives/document_view-types.d.ts +152 -0
  221. package/dist/primitives/document_view-types.d.ts.map +1 -0
  222. package/dist/primitives/document_view-types.js +2 -0
  223. package/dist/primitives/document_view-types.js.map +1 -0
  224. package/dist/primitives/document_view.d.ts +8 -106
  225. package/dist/primitives/document_view.d.ts.map +1 -1
  226. package/dist/primitives/document_view.js +153 -312
  227. package/dist/primitives/document_view.js.map +1 -1
  228. package/dist/primitives/dom-helpers.d.ts +9 -0
  229. package/dist/primitives/dom-helpers.d.ts.map +1 -1
  230. package/dist/primitives/dom-helpers.js +10 -1
  231. package/dist/primitives/dom-helpers.js.map +1 -1
  232. package/dist/primitives/footnotes.d.ts +4 -3
  233. package/dist/primitives/footnotes.d.ts.map +1 -1
  234. package/dist/primitives/footnotes.js +232 -44
  235. package/dist/primitives/footnotes.js.map +1 -1
  236. package/dist/primitives/formatting_tags.d.ts +7 -0
  237. package/dist/primitives/formatting_tags.d.ts.map +1 -1
  238. package/dist/primitives/formatting_tags.js +22 -11
  239. package/dist/primitives/formatting_tags.js.map +1 -1
  240. package/dist/primitives/index.d.ts +10 -0
  241. package/dist/primitives/index.d.ts.map +1 -1
  242. package/dist/primitives/index.js +9 -0
  243. package/dist/primitives/index.js.map +1 -1
  244. package/dist/primitives/layout.d.ts +4 -3
  245. package/dist/primitives/layout.d.ts.map +1 -1
  246. package/dist/primitives/layout.js +45 -3
  247. package/dist/primitives/layout.js.map +1 -1
  248. package/dist/primitives/merge_runs.d.ts +21 -3
  249. package/dist/primitives/merge_runs.d.ts.map +1 -1
  250. package/dist/primitives/merge_runs.js +32 -10
  251. package/dist/primitives/merge_runs.js.map +1 -1
  252. package/dist/primitives/minimal_save.d.ts +38 -0
  253. package/dist/primitives/minimal_save.d.ts.map +1 -0
  254. package/dist/primitives/minimal_save.js +323 -0
  255. package/dist/primitives/minimal_save.js.map +1 -0
  256. package/dist/primitives/namespaces.d.ts +47 -0
  257. package/dist/primitives/namespaces.d.ts.map +1 -1
  258. package/dist/primitives/namespaces.js +52 -0
  259. package/dist/primitives/namespaces.js.map +1 -1
  260. package/dist/primitives/reject_changes.d.ts +6 -4
  261. package/dist/primitives/reject_changes.d.ts.map +1 -1
  262. package/dist/primitives/reject_changes.js +187 -91
  263. package/dist/primitives/reject_changes.js.map +1 -1
  264. package/dist/primitives/revision-parts.d.ts +7 -0
  265. package/dist/primitives/revision-parts.d.ts.map +1 -0
  266. package/dist/primitives/revision-parts.js +27 -0
  267. package/dist/primitives/revision-parts.js.map +1 -0
  268. package/dist/primitives/revision-vocabulary.d.ts +7 -0
  269. package/dist/primitives/revision-vocabulary.d.ts.map +1 -0
  270. package/dist/primitives/revision-vocabulary.js +39 -0
  271. package/dist/primitives/revision-vocabulary.js.map +1 -0
  272. package/dist/primitives/schema-corpus-capture.d.ts +19 -0
  273. package/dist/primitives/schema-corpus-capture.d.ts.map +1 -0
  274. package/dist/primitives/schema-corpus-capture.js +29 -0
  275. package/dist/primitives/schema-corpus-capture.js.map +1 -0
  276. package/dist/primitives/sectPrAudit.d.ts +19 -0
  277. package/dist/primitives/sectPrAudit.d.ts.map +1 -0
  278. package/dist/primitives/sectPrAudit.js +165 -0
  279. package/dist/primitives/sectPrAudit.js.map +1 -0
  280. package/dist/primitives/semantic_tags.d.ts +7 -0
  281. package/dist/primitives/semantic_tags.d.ts.map +1 -1
  282. package/dist/primitives/semantic_tags.js +23 -4
  283. package/dist/primitives/semantic_tags.js.map +1 -1
  284. package/dist/primitives/serialize_html.d.ts +37 -0
  285. package/dist/primitives/serialize_html.d.ts.map +1 -0
  286. package/dist/primitives/serialize_html.js +395 -0
  287. package/dist/primitives/serialize_html.js.map +1 -0
  288. package/dist/primitives/serialize_markdown.d.ts +16 -0
  289. package/dist/primitives/serialize_markdown.d.ts.map +1 -0
  290. package/dist/primitives/serialize_markdown.js +300 -0
  291. package/dist/primitives/serialize_markdown.js.map +1 -0
  292. package/dist/primitives/serialize_plaintext.d.ts +15 -0
  293. package/dist/primitives/serialize_plaintext.d.ts.map +1 -0
  294. package/dist/primitives/serialize_plaintext.js +154 -0
  295. package/dist/primitives/serialize_plaintext.js.map +1 -0
  296. package/dist/primitives/styles.d.ts +15 -0
  297. package/dist/primitives/styles.d.ts.map +1 -1
  298. package/dist/primitives/styles.js +33 -22
  299. package/dist/primitives/styles.js.map +1 -1
  300. package/dist/primitives/tables.d.ts.map +1 -1
  301. package/dist/primitives/tables.js +13 -3
  302. package/dist/primitives/tables.js.map +1 -1
  303. package/dist/primitives/text.d.ts +2 -1
  304. package/dist/primitives/text.d.ts.map +1 -1
  305. package/dist/primitives/text.js +116 -12
  306. package/dist/primitives/text.js.map +1 -1
  307. package/dist/primitives/track-changes-emitter.d.ts +148 -0
  308. package/dist/primitives/track-changes-emitter.d.ts.map +1 -0
  309. package/dist/primitives/track-changes-emitter.js +291 -0
  310. package/dist/primitives/track-changes-emitter.js.map +1 -0
  311. package/dist/primitives/validate_ai_revisions.d.ts +35 -0
  312. package/dist/primitives/validate_ai_revisions.d.ts.map +1 -0
  313. package/dist/primitives/validate_ai_revisions.js +323 -0
  314. package/dist/primitives/validate_ai_revisions.js.map +1 -0
  315. package/dist/primitives/xml-helpers.d.ts +29 -0
  316. package/dist/primitives/xml-helpers.d.ts.map +1 -0
  317. package/dist/primitives/xml-helpers.js +35 -0
  318. package/dist/primitives/xml-helpers.js.map +1 -0
  319. package/dist/primitives/xml.d.ts +5 -0
  320. package/dist/primitives/xml.d.ts.map +1 -1
  321. package/dist/primitives/xml.js +5 -0
  322. package/dist/primitives/xml.js.map +1 -1
  323. package/dist/primitives/zip.d.ts +1 -0
  324. package/dist/primitives/zip.d.ts.map +1 -1
  325. package/dist/primitives/zip.js +21 -3
  326. package/dist/primitives/zip.js.map +1 -1
  327. package/dist/shared/field-structure.d.ts +14 -0
  328. package/dist/shared/field-structure.d.ts.map +1 -0
  329. package/dist/shared/field-structure.js +166 -0
  330. package/dist/shared/field-structure.js.map +1 -0
  331. package/dist/shared/ooxml/namespaces.d.ts +4 -1
  332. package/dist/shared/ooxml/namespaces.d.ts.map +1 -1
  333. package/dist/shared/ooxml/namespaces.js +4 -1
  334. package/dist/shared/ooxml/namespaces.js.map +1 -1
  335. package/package.json +13 -9
@@ -0,0 +1,198 @@
1
+ /**
2
+ * In-Place AST Modifier
3
+ *
4
+ * Modifies the revised document's AST in-place to add track changes markup.
5
+ * This replaces the reconstruction-based approach with direct tree manipulation.
6
+ */
7
+ import type { ComparisonUnitAtom } from '../../core-types.js';
8
+ import { type RevisionIdState } from './inPlaceModifier-shared.js';
9
+ export type TrackChangeTag = 'w:ins' | 'w:del' | 'w:moveFrom' | 'w:moveTo';
10
+ export declare const TRACK_CHANGE_WRAPPERS: Set<TrackChangeTag>;
11
+ export type AtomRunBoundary = 'start' | 'end';
12
+ /**
13
+ * Resolve the run associated with an atom boundary.
14
+ *
15
+ * For collapsed field atoms, sourceRunElement points at the first run in the
16
+ * field sequence. For insertion-point tracking we often need the trailing run,
17
+ * otherwise deleted/moved fragments can be inserted inside the field sequence.
18
+ */
19
+ export declare function getAtomRunAtBoundary(atom: ComparisonUnitAtom, boundary: AtomRunBoundary): Element | undefined;
20
+ /**
21
+ * Resolve all run elements represented by an atom.
22
+ *
23
+ * For collapsed-field atoms, we must treat the entire field run sequence as a
24
+ * single logical unit. Wrapping only the first run leaves trailing field-code
25
+ * runs untracked, which can leak revised field text after Reject All.
26
+ */
27
+ export declare function getAtomRuns(atom: ComparisonUnitAtom): Element[];
28
+ /**
29
+ * True iff `atom` represents a collapsed-field sequence (a complex field
30
+ * captured as a single logical atom). See {@link getAtomRuns} for the
31
+ * multi-run resolution.
32
+ */
33
+ export declare function isCollapsedFieldAtom(atom: ComparisonUnitAtom): boolean;
34
+ /**
35
+ * Convert a run node to the correct insertion anchor.
36
+ *
37
+ * If the run is wrapped in a track-change container, the insertion anchor
38
+ * must be the wrapper (a paragraph child), not the nested run.
39
+ */
40
+ export declare function getRunInsertionAnchor(run: Element): Element;
41
+ /**
42
+ * Options for wrapping a run with track change markup.
43
+ */
44
+ export interface WrapRunOptions {
45
+ /** The run element to wrap */
46
+ run: Element;
47
+ /** The track change tag name */
48
+ tagName: TrackChangeTag;
49
+ /** Author name for track changes */
50
+ author: string;
51
+ /** Formatted date string */
52
+ dateStr: string;
53
+ /** Revision ID state */
54
+ state: RevisionIdState;
55
+ /** Whether to convert w:t to w:delText (for deleted/moveFrom content) */
56
+ convertTextToDelText?: boolean;
57
+ }
58
+ /**
59
+ * Wrap a run element with track change markup.
60
+ *
61
+ * This is the shared implementation for wrapAsInserted, wrapAsDeleted,
62
+ * and the inner wrapping logic of move operations.
63
+ *
64
+ * @param options - Wrapping options
65
+ * @returns true if wrapped, false if run was already wrapped or has no parent
66
+ */
67
+ export declare function wrapRunWithTrackChange(options: WrapRunOptions): boolean;
68
+ /**
69
+ * Ensure w:pPr/w:rPr exists and add a paragraph-mark revision marker (w:ins/w:del)
70
+ * in the paragraph properties.
71
+ *
72
+ * This is the critical piece for whole-paragraph insert/delete idempotency:
73
+ * - Reject All should remove inserted paragraphs entirely (no stub breaks)
74
+ * - Accept All should remove deleted paragraphs entirely
75
+ */
76
+ export declare function addParagraphMarkRevisionMarker(paragraph: Element, markerTag: 'w:ins' | 'w:del', author: string, dateStr: string, state: RevisionIdState): void;
77
+ /**
78
+ * Position a paragraph-mark revision marker in its schema-correct rPr slot.
79
+ *
80
+ * CT_ParaRPr ordering: the tracked-change group (w:ins, w:del, w:moveFrom,
81
+ * w:moveTo — in that order) comes before every formatting child (w:rStyle,
82
+ * w:rFonts, ...). So w:ins always goes first, and w:del goes right after a
83
+ * w:ins sibling when one exists, else first.
84
+ */
85
+ export declare function placeParagraphMarkRevisionMarker(rPr: Element, marker: Element, markerTag: 'w:ins' | 'w:del'): void;
86
+ export declare function wrapAsInserted(run: Element, author: string, dateStr: string, state: RevisionIdState): boolean;
87
+ /**
88
+ * Wrap a run element with <w:del> to mark it as deleted.
89
+ * Also converts w:t to w:delText within the run.
90
+ *
91
+ * @param run - The w:r element to wrap
92
+ * @param author - Author name for track changes
93
+ * @param dateStr - Formatted date string
94
+ * @param state - Revision ID state
95
+ * @returns true if wrapped, false if run was already wrapped or has no parent
96
+ */
97
+ export declare function wrapAsDeleted(run: Element, author: string, dateStr: string, state: RevisionIdState): boolean;
98
+ export type MoveDirection = 'from' | 'to';
99
+ /**
100
+ * Configuration for move wrapping based on direction.
101
+ */
102
+ export interface MoveWrapConfig {
103
+ wrapperTag: 'w:moveFrom' | 'w:moveTo';
104
+ rangeStartTag: 'w:moveFromRangeStart' | 'w:moveToRangeStart';
105
+ rangeEndTag: 'w:moveFromRangeEnd' | 'w:moveToRangeEnd';
106
+ rangeIdKey: 'sourceRangeId' | 'destRangeId';
107
+ convertTextToDelText: boolean;
108
+ }
109
+ export declare const MOVE_CONFIG: Record<MoveDirection, MoveWrapConfig>;
110
+ /**
111
+ * Wrap a run element with move tracking (shared implementation for moveFrom/moveTo).
112
+ *
113
+ * @param run - The w:r element to wrap
114
+ * @param moveName - Name for linking source and destination
115
+ * @param direction - 'from' for moveFrom, 'to' for moveTo
116
+ * @param author - Author name
117
+ * @param dateStr - Formatted date
118
+ * @param state - Revision ID state
119
+ * @returns true if wrapped
120
+ */
121
+ export declare function wrapAsMove(run: Element, moveName: string, direction: MoveDirection, author: string, dateStr: string, state: RevisionIdState): boolean;
122
+ /**
123
+ * Wrap a run element with <w:moveFrom> for moved-from content.
124
+ *
125
+ * @param run - The w:r element to wrap
126
+ * @param moveName - Name for linking source and destination
127
+ * @param author - Author name
128
+ * @param dateStr - Formatted date
129
+ * @param state - Revision ID state
130
+ * @returns true if wrapped
131
+ */
132
+ export declare function wrapAsMoveFrom(run: Element, moveName: string, author: string, dateStr: string, state: RevisionIdState): boolean;
133
+ /**
134
+ * Wrap a run element with <w:moveTo> for moved-to content.
135
+ *
136
+ * @param run - The w:r element to wrap
137
+ * @param moveName - Name for linking source and destination
138
+ * @param author - Author name
139
+ * @param dateStr - Formatted date
140
+ * @param state - Revision ID state
141
+ * @returns true if wrapped
142
+ */
143
+ export declare function wrapAsMoveTo(run: Element, moveName: string, author: string, dateStr: string, state: RevisionIdState): boolean;
144
+ /**
145
+ * Add format change tracking to a run's properties.
146
+ *
147
+ * @param run - The w:r element with changed formatting
148
+ * @param oldRunProperties - The original run properties (w:rPr)
149
+ * @param author - Author name
150
+ * @param dateStr - Formatted date
151
+ * @param state - Revision ID state
152
+ */
153
+ export declare function addFormatChange(run: Element, oldRunProperties: Element | null, author: string, dateStr: string, state: RevisionIdState): void;
154
+ /**
155
+ * Add a paragraph property change element (w:pPrChange) to record the "before"
156
+ * state of paragraph properties. This is needed for Google Docs to display
157
+ * inserted paragraphs as tracked changes.
158
+ *
159
+ * The child `<w:pPr>` inside `w:pPrChange` must conform to CT_PPrBase — it
160
+ * MUST NOT contain w:rPr, w:sectPr, or w:pPrChange.
161
+ *
162
+ * @param paragraph - The w:p element
163
+ * @param author - Author name
164
+ * @param dateStr - Formatted date
165
+ * @param state - Revision ID state
166
+ */
167
+ export declare function addParagraphPropertyChange(paragraph: Element, author: string, dateStr: string, state: RevisionIdState): void;
168
+ /**
169
+ * Tag names that represent visible content inside a w:r element.
170
+ * A run containing at least one of these is considered substantive (non-empty).
171
+ */
172
+ export declare const RUN_VISIBLE_CONTENT_TAGS: ReadonlySet<string>;
173
+ /**
174
+ * Returns true if a w:r element contains at least one visible content child.
175
+ * Empty runs (containing only w:rPr or nothing) return false.
176
+ */
177
+ export declare function runHasVisibleContent(run: Element): boolean;
178
+ /**
179
+ * Wrap an inserted empty paragraph with <w:ins>.
180
+ *
181
+ * For empty paragraphs (no content, only pPr), we wrap the entire paragraph.
182
+ *
183
+ * @param paragraph - The w:p element
184
+ * @param author - Author name
185
+ * @param dateStr - Formatted date
186
+ * @param state - Revision ID state
187
+ */
188
+ export declare function wrapParagraphAsInserted(paragraph: Element, author: string, dateStr: string, state: RevisionIdState): boolean;
189
+ /**
190
+ * Wrap a deleted empty paragraph with <w:del>.
191
+ *
192
+ * @param paragraph - The w:p element
193
+ * @param author - Author name
194
+ * @param dateStr - Formatted date
195
+ * @param state - Revision ID state
196
+ */
197
+ export declare function wrapParagraphAsDeleted(paragraph: Element, author: string, dateStr: string, state: RevisionIdState): boolean;
198
+ //# sourceMappingURL=inPlaceModifier-wrappers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inPlaceModifier-wrappers.d.ts","sourceRoot":"","sources":["../../../src/baselines/atomizer/inPlaceModifier-wrappers.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,475 @@
1
+ /**
2
+ * In-Place AST Modifier
3
+ *
4
+ * Modifies the revised document's AST in-place to add track changes markup.
5
+ * This replaces the reconstruction-based approach with direct tree manipulation.
6
+ */
7
+ import { childElements, findChildByTagName, insertAfterElement, wrapElement } from '../../primitives/index.js';
8
+ import { allocateRevisionId, convertToDelText, createEl, findAncestorByTag, getMoveRangeIds, parentElement, } from './inPlaceModifier-shared.js';
9
+ export const TRACK_CHANGE_WRAPPERS = new Set([
10
+ 'w:ins',
11
+ 'w:del',
12
+ 'w:moveFrom',
13
+ 'w:moveTo',
14
+ ]);
15
+ /**
16
+ * Resolve the run associated with an atom boundary.
17
+ *
18
+ * For collapsed field atoms, sourceRunElement points at the first run in the
19
+ * field sequence. For insertion-point tracking we often need the trailing run,
20
+ * otherwise deleted/moved fragments can be inserted inside the field sequence.
21
+ */
22
+ export function getAtomRunAtBoundary(atom, boundary) {
23
+ if (atom.collapsedFieldAtoms && atom.collapsedFieldAtoms.length > 0) {
24
+ const fieldAtoms = boundary === 'start'
25
+ ? atom.collapsedFieldAtoms
26
+ : [...atom.collapsedFieldAtoms].reverse();
27
+ for (const fieldAtom of fieldAtoms) {
28
+ const run = fieldAtom.sourceRunElement ?? findAncestorByTag(fieldAtom, 'w:r');
29
+ if (run)
30
+ return run;
31
+ }
32
+ }
33
+ return atom.sourceRunElement ?? findAncestorByTag(atom, 'w:r');
34
+ }
35
+ /**
36
+ * Resolve all run elements represented by an atom.
37
+ *
38
+ * For collapsed-field atoms, we must treat the entire field run sequence as a
39
+ * single logical unit. Wrapping only the first run leaves trailing field-code
40
+ * runs untracked, which can leak revised field text after Reject All.
41
+ */
42
+ export function getAtomRuns(atom) {
43
+ if (!atom.collapsedFieldAtoms || atom.collapsedFieldAtoms.length === 0) {
44
+ const run = atom.sourceRunElement ?? findAncestorByTag(atom, 'w:r');
45
+ return run ? [run] : [];
46
+ }
47
+ const runs = [];
48
+ const seen = new Set();
49
+ for (const fieldAtom of atom.collapsedFieldAtoms) {
50
+ const run = fieldAtom.sourceRunElement ?? findAncestorByTag(fieldAtom, 'w:r');
51
+ if (!run || seen.has(run))
52
+ continue;
53
+ seen.add(run);
54
+ runs.push(run);
55
+ }
56
+ return runs;
57
+ }
58
+ /**
59
+ * True iff `atom` represents a collapsed-field sequence (a complex field
60
+ * captured as a single logical atom). See {@link getAtomRuns} for the
61
+ * multi-run resolution.
62
+ */
63
+ export function isCollapsedFieldAtom(atom) {
64
+ return !!(atom.collapsedFieldAtoms && atom.collapsedFieldAtoms.length > 0);
65
+ }
66
+ /**
67
+ * Convert a run node to the correct insertion anchor.
68
+ *
69
+ * If the run is wrapped in a track-change container, the insertion anchor
70
+ * must be the wrapper (a paragraph child), not the nested run.
71
+ */
72
+ export function getRunInsertionAnchor(run) {
73
+ const parent = parentElement(run);
74
+ if (parent && TRACK_CHANGE_WRAPPERS.has(parent.tagName)) {
75
+ return parent;
76
+ }
77
+ return run;
78
+ }
79
+ /**
80
+ * Wrap a run element with track change markup.
81
+ *
82
+ * This is the shared implementation for wrapAsInserted, wrapAsDeleted,
83
+ * and the inner wrapping logic of move operations.
84
+ *
85
+ * @param options - Wrapping options
86
+ * @returns true if wrapped, false if run was already wrapped or has no parent
87
+ */
88
+ export function wrapRunWithTrackChange(options) {
89
+ const { run, tagName, author, dateStr, state, convertTextToDelText: convertText = false } = options;
90
+ // Skip if already wrapped
91
+ if (state.wrappedRuns.has(run)) {
92
+ return false;
93
+ }
94
+ // Skip if the run has no parent in the tree
95
+ if (!run.parentNode) {
96
+ return false;
97
+ }
98
+ // Convert w:t to w:delText if requested (for deleted content)
99
+ if (convertText) {
100
+ convertToDelText(run);
101
+ }
102
+ const id = allocateRevisionId(state);
103
+ const wrapper = createEl(tagName, {
104
+ 'w:id': String(id),
105
+ 'w:author': author,
106
+ 'w:date': dateStr,
107
+ });
108
+ wrapElement(run, wrapper);
109
+ state.wrappedRuns.add(run);
110
+ return true;
111
+ }
112
+ /**
113
+ * Ensure w:pPr/w:rPr exists and add a paragraph-mark revision marker (w:ins/w:del)
114
+ * in the paragraph properties.
115
+ *
116
+ * This is the critical piece for whole-paragraph insert/delete idempotency:
117
+ * - Reject All should remove inserted paragraphs entirely (no stub breaks)
118
+ * - Accept All should remove deleted paragraphs entirely
119
+ */
120
+ export function addParagraphMarkRevisionMarker(paragraph, markerTag, author, dateStr, state) {
121
+ // Find or create pPr.
122
+ let pPr = findChildByTagName(paragraph, 'w:pPr');
123
+ if (!pPr) {
124
+ pPr = createEl('w:pPr');
125
+ // pPr should be the first child in a paragraph.
126
+ paragraph.insertBefore(pPr, paragraph.firstChild);
127
+ }
128
+ const existingMarker = findParagraphMarkRevisionMarker(pPr, markerTag);
129
+ // Find or create rPr within pPr (paragraph mark properties).
130
+ let rPr = findChildByTagName(pPr, 'w:rPr');
131
+ if (!rPr) {
132
+ rPr = createEl('w:rPr');
133
+ // CT_PPr ordering: ... base props ..., w:rPr, w:sectPr?, w:pPrChange?
134
+ // Insert rPr in schema-correct position (before sectPr/pPrChange).
135
+ const sectPr = findChildByTagName(pPr, 'w:sectPr');
136
+ const pPrChange = findChildByTagName(pPr, 'w:pPrChange');
137
+ const insertBefore = sectPr ?? pPrChange ?? null;
138
+ if (insertBefore) {
139
+ pPr.insertBefore(rPr, insertBefore);
140
+ }
141
+ else {
142
+ pPr.appendChild(rPr);
143
+ }
144
+ }
145
+ // Avoid duplicating markers. A legacy/bypass path may already have put the
146
+ // paragraph-mark marker in another w:rPr under the same pPr; keep that marker
147
+ // and normalize its revision context instead of adding a second CT_ParaRPr child.
148
+ if (existingMarker) {
149
+ existingMarker.setAttribute('w:author', author);
150
+ existingMarker.setAttribute('w:date', dateStr);
151
+ if (!existingMarker.getAttribute('w:id')) {
152
+ existingMarker.setAttribute('w:id', String(allocateRevisionId(state)));
153
+ }
154
+ // The bypass path may have left the marker mid-sequence (or in another
155
+ // w:rPr); move it to the schema-correct slot in the canonical rPr.
156
+ placeParagraphMarkRevisionMarker(rPr, existingMarker, markerTag);
157
+ return;
158
+ }
159
+ const id = allocateRevisionId(state);
160
+ const marker = createEl(markerTag, {
161
+ 'w:id': String(id),
162
+ 'w:author': author,
163
+ 'w:date': dateStr,
164
+ });
165
+ placeParagraphMarkRevisionMarker(rPr, marker, markerTag);
166
+ }
167
+ /**
168
+ * Position a paragraph-mark revision marker in its schema-correct rPr slot.
169
+ *
170
+ * CT_ParaRPr ordering: the tracked-change group (w:ins, w:del, w:moveFrom,
171
+ * w:moveTo — in that order) comes before every formatting child (w:rStyle,
172
+ * w:rFonts, ...). So w:ins always goes first, and w:del goes right after a
173
+ * w:ins sibling when one exists, else first.
174
+ */
175
+ export function placeParagraphMarkRevisionMarker(rPr, marker, markerTag) {
176
+ const insSibling = markerTag === 'w:del' ? findChildByTagName(rPr, 'w:ins') : null;
177
+ if (insSibling) {
178
+ if (insSibling.nextSibling !== marker) {
179
+ insertAfterElement(insSibling, marker);
180
+ }
181
+ }
182
+ else if (rPr.firstChild !== marker) {
183
+ rPr.insertBefore(marker, rPr.firstChild);
184
+ }
185
+ }
186
+ function findParagraphMarkRevisionMarker(pPr, markerTag) {
187
+ for (const child of childElements(pPr)) {
188
+ if (child.tagName !== 'w:rPr')
189
+ continue;
190
+ const marker = findChildByTagName(child, markerTag);
191
+ if (marker)
192
+ return marker;
193
+ }
194
+ return null;
195
+ }
196
+ // @lean-segment: field-wrapper-emission
197
+ // Lean traceability anchor — cited by verification/lean/LeanSpike/Spec.lean and
198
+ // packages/docx-core/src/integration/lean-spec-bridge.test.ts. These wrapping
199
+ // primitives (wrapAsInserted/wrapAsDeleted and the move/format variants below)
200
+ // emit whole field sequences as SINGLE track-change wrappers, which is why the
201
+ // engine currently satisfies the stronger `fieldContextNeutral ∀ ctx` property.
202
+ // When ECMA-376 field fragmentation lands (#217) this anchor marks the code the
203
+ // Lean predicate-strength choice depends on. Grep this anchor, not line numbers.
204
+ export function wrapAsInserted(run, author, dateStr, state) {
205
+ return wrapRunWithTrackChange({
206
+ run,
207
+ tagName: 'w:ins',
208
+ author,
209
+ dateStr,
210
+ state,
211
+ });
212
+ }
213
+ /**
214
+ * Wrap a run element with <w:del> to mark it as deleted.
215
+ * Also converts w:t to w:delText within the run.
216
+ *
217
+ * @param run - The w:r element to wrap
218
+ * @param author - Author name for track changes
219
+ * @param dateStr - Formatted date string
220
+ * @param state - Revision ID state
221
+ * @returns true if wrapped, false if run was already wrapped or has no parent
222
+ */
223
+ export function wrapAsDeleted(run, author, dateStr, state) {
224
+ return wrapRunWithTrackChange({
225
+ run,
226
+ tagName: 'w:del',
227
+ author,
228
+ dateStr,
229
+ state,
230
+ convertTextToDelText: true,
231
+ });
232
+ }
233
+ export const MOVE_CONFIG = {
234
+ from: {
235
+ wrapperTag: 'w:moveFrom',
236
+ rangeStartTag: 'w:moveFromRangeStart',
237
+ rangeEndTag: 'w:moveFromRangeEnd',
238
+ rangeIdKey: 'sourceRangeId',
239
+ convertTextToDelText: true, // Moved-from content appears as deleted
240
+ },
241
+ to: {
242
+ wrapperTag: 'w:moveTo',
243
+ rangeStartTag: 'w:moveToRangeStart',
244
+ rangeEndTag: 'w:moveToRangeEnd',
245
+ rangeIdKey: 'destRangeId',
246
+ convertTextToDelText: false, // Moved-to content keeps w:t
247
+ },
248
+ };
249
+ /**
250
+ * Wrap a run element with move tracking (shared implementation for moveFrom/moveTo).
251
+ *
252
+ * @param run - The w:r element to wrap
253
+ * @param moveName - Name for linking source and destination
254
+ * @param direction - 'from' for moveFrom, 'to' for moveTo
255
+ * @param author - Author name
256
+ * @param dateStr - Formatted date
257
+ * @param state - Revision ID state
258
+ * @returns true if wrapped
259
+ */
260
+ export function wrapAsMove(run, moveName, direction, author, dateStr, state) {
261
+ if (state.wrappedRuns.has(run)) {
262
+ return false;
263
+ }
264
+ const parent = parentElement(run);
265
+ if (!parent) {
266
+ return false;
267
+ }
268
+ const config = MOVE_CONFIG[direction];
269
+ const ids = getMoveRangeIds(state, moveName);
270
+ const moveId = allocateRevisionId(state);
271
+ const rangeId = ids[config.rangeIdKey];
272
+ // Convert w:t to w:delText if needed (for moveFrom content)
273
+ if (config.convertTextToDelText) {
274
+ convertToDelText(run);
275
+ }
276
+ // Create range start marker
277
+ const rangeStart = createEl(config.rangeStartTag, {
278
+ 'w:id': String(rangeId),
279
+ 'w:name': moveName,
280
+ 'w:author': author,
281
+ 'w:date': dateStr,
282
+ });
283
+ // Create move wrapper
284
+ const moveWrapper = createEl(config.wrapperTag, {
285
+ 'w:id': String(moveId),
286
+ 'w:author': author,
287
+ 'w:date': dateStr,
288
+ });
289
+ // Create range end marker
290
+ const rangeEnd = createEl(config.rangeEndTag, {
291
+ 'w:id': String(rangeId),
292
+ });
293
+ // Insert: rangeStart -> moveWrapper(run) -> rangeEnd
294
+ run.parentNode.insertBefore(rangeStart, run);
295
+ wrapElement(run, moveWrapper);
296
+ insertAfterElement(moveWrapper, rangeEnd);
297
+ state.wrappedRuns.add(run);
298
+ return true;
299
+ }
300
+ /**
301
+ * Wrap a run element with <w:moveFrom> for moved-from content.
302
+ *
303
+ * @param run - The w:r element to wrap
304
+ * @param moveName - Name for linking source and destination
305
+ * @param author - Author name
306
+ * @param dateStr - Formatted date
307
+ * @param state - Revision ID state
308
+ * @returns true if wrapped
309
+ */
310
+ export function wrapAsMoveFrom(run, moveName, author, dateStr, state) {
311
+ return wrapAsMove(run, moveName, 'from', author, dateStr, state);
312
+ }
313
+ /**
314
+ * Wrap a run element with <w:moveTo> for moved-to content.
315
+ *
316
+ * @param run - The w:r element to wrap
317
+ * @param moveName - Name for linking source and destination
318
+ * @param author - Author name
319
+ * @param dateStr - Formatted date
320
+ * @param state - Revision ID state
321
+ * @returns true if wrapped
322
+ */
323
+ export function wrapAsMoveTo(run, moveName, author, dateStr, state) {
324
+ return wrapAsMove(run, moveName, 'to', author, dateStr, state);
325
+ }
326
+ /**
327
+ * Add format change tracking to a run's properties.
328
+ *
329
+ * @param run - The w:r element with changed formatting
330
+ * @param oldRunProperties - The original run properties (w:rPr)
331
+ * @param author - Author name
332
+ * @param dateStr - Formatted date
333
+ * @param state - Revision ID state
334
+ */
335
+ export function addFormatChange(run, oldRunProperties, author, dateStr, state) {
336
+ // Find or create w:rPr
337
+ let rPr = findChildByTagName(run, 'w:rPr');
338
+ if (!rPr) {
339
+ rPr = createEl('w:rPr');
340
+ // Insert rPr at the beginning of run's children
341
+ run.insertBefore(rPr, run.firstChild);
342
+ }
343
+ // CT_RPr permits at most one w:rPrChange. Comparison can visit multiple
344
+ // format-changed atoms from the same split run, so keep the latest snapshot
345
+ // instead of stacking invalid siblings.
346
+ for (const child of childElements(rPr)) {
347
+ if (child.tagName === 'w:rPrChange') {
348
+ rPr.removeChild(child);
349
+ }
350
+ }
351
+ // Create rPrChange
352
+ const id = allocateRevisionId(state);
353
+ const rPrChange = createEl('w:rPrChange', {
354
+ 'w:id': String(id),
355
+ 'w:author': author,
356
+ 'w:date': dateStr,
357
+ });
358
+ // Clone old properties into a w:rPr wrapper inside rPrChange (OOXML spec requires
359
+ // rPrChange to contain a single w:rPr child holding the previous formatting).
360
+ if (oldRunProperties) {
361
+ const oldRPr = createEl('w:rPr');
362
+ for (const child of childElements(oldRunProperties)) {
363
+ const cloned = child.cloneNode(true);
364
+ oldRPr.appendChild(cloned);
365
+ }
366
+ rPrChange.appendChild(oldRPr);
367
+ }
368
+ // Add rPrChange to rPr
369
+ rPr.appendChild(rPrChange);
370
+ }
371
+ /**
372
+ * Add a paragraph property change element (w:pPrChange) to record the "before"
373
+ * state of paragraph properties. This is needed for Google Docs to display
374
+ * inserted paragraphs as tracked changes.
375
+ *
376
+ * The child `<w:pPr>` inside `w:pPrChange` must conform to CT_PPrBase — it
377
+ * MUST NOT contain w:rPr, w:sectPr, or w:pPrChange.
378
+ *
379
+ * @param paragraph - The w:p element
380
+ * @param author - Author name
381
+ * @param dateStr - Formatted date
382
+ * @param state - Revision ID state
383
+ */
384
+ export function addParagraphPropertyChange(paragraph, author, dateStr, state) {
385
+ let pPr = findChildByTagName(paragraph, 'w:pPr');
386
+ if (!pPr) {
387
+ pPr = createEl('w:pPr');
388
+ paragraph.insertBefore(pPr, paragraph.firstChild);
389
+ }
390
+ // Idempotent — don't add a second pPrChange.
391
+ if (findChildByTagName(pPr, 'w:pPrChange'))
392
+ return;
393
+ const id = allocateRevisionId(state);
394
+ const pPrChange = createEl('w:pPrChange', {
395
+ 'w:id': String(id),
396
+ 'w:author': author,
397
+ 'w:date': dateStr,
398
+ });
399
+ // Clone current pPr content as "before" snapshot.
400
+ // pPrChange child pPr must be CT_PPrBase — exclude rPr, rPrChange, sectPr, pPrChange.
401
+ const EXCLUDED = new Set(['w:rPr', 'w:rPrChange', 'w:pPrChange', 'w:sectPr']);
402
+ const oldPPr = createEl('w:pPr');
403
+ for (const child of childElements(pPr)) {
404
+ if (!EXCLUDED.has(child.tagName))
405
+ oldPPr.appendChild(child.cloneNode(true));
406
+ }
407
+ pPrChange.appendChild(oldPPr);
408
+ pPr.appendChild(pPrChange); // pPrChange goes last in pPr per schema
409
+ }
410
+ /**
411
+ * Tag names that represent visible content inside a w:r element.
412
+ * A run containing at least one of these is considered substantive (non-empty).
413
+ */
414
+ export const RUN_VISIBLE_CONTENT_TAGS = new Set([
415
+ 'w:t', 'w:tab', 'w:br', 'w:cr', 'w:drawing', 'w:object', 'w:pict',
416
+ 'w:sym', 'w:fldChar', 'w:instrText',
417
+ ]);
418
+ /**
419
+ * Returns true if a w:r element contains at least one visible content child.
420
+ * Empty runs (containing only w:rPr or nothing) return false.
421
+ */
422
+ export function runHasVisibleContent(run) {
423
+ for (let i = 0; i < run.childNodes.length; i++) {
424
+ const child = run.childNodes[i];
425
+ if (child.nodeType === 1 && RUN_VISIBLE_CONTENT_TAGS.has(child.tagName)) {
426
+ return true;
427
+ }
428
+ }
429
+ return false;
430
+ }
431
+ /**
432
+ * Wrap an inserted empty paragraph with <w:ins>.
433
+ *
434
+ * For empty paragraphs (no content, only pPr), we wrap the entire paragraph.
435
+ *
436
+ * @param paragraph - The w:p element
437
+ * @param author - Author name
438
+ * @param dateStr - Formatted date
439
+ * @param state - Revision ID state
440
+ */
441
+ export function wrapParagraphAsInserted(paragraph, author, dateStr, state) {
442
+ // Mark the paragraph MARK as inserted (<w:pPr><w:rPr><w:ins/>). For a genuinely
443
+ // inserted paragraph the paragraph break itself is a tracked insertion, so this
444
+ // marker is what makes Reject All remove the whole paragraph (mark + content) and
445
+ // Accept All keep it. The individual runs are wrapped in <w:ins> separately; this
446
+ // function only adds the paragraph-mark marker.
447
+ //
448
+ // We ALWAYS emit the marker, including for non-empty paragraphs. A prior heuristic
449
+ // omitted it when the paragraph had substantive run content, on the (uncited) belief
450
+ // that Google Docs hides w:ins-wrapped runs that coexist with a PPR-INS marker. That
451
+ // is false — Google Docs renders the inserted runs identically with or without
452
+ // PPR-INS, and rejecting WITH the marker is cleaner there (it leaves no empty
453
+ // paragraph). Omitting the marker forced Reject All to guess via a content-based
454
+ // "all content is inside w:ins" heuristic that over-deleted foreign paragraphs whose
455
+ // mark is untracked (i.e. text inserted into a pre-existing paragraph, which Word and
456
+ // LibreOffice keep as an empty paragraph on reject). Reject is now purely mark-based
457
+ // (see rejectAllChanges / rejectChanges), which requires this marker to be present.
458
+ addParagraphMarkRevisionMarker(paragraph, 'w:ins', author, dateStr, state);
459
+ return true;
460
+ }
461
+ /**
462
+ * Wrap a deleted empty paragraph with <w:del>.
463
+ *
464
+ * @param paragraph - The w:p element
465
+ * @param author - Author name
466
+ * @param dateStr - Formatted date
467
+ * @param state - Revision ID state
468
+ */
469
+ export function wrapParagraphAsDeleted(paragraph, author, dateStr, state) {
470
+ // See wrapParagraphAsInserted: represent paragraph deletion via a paragraph-mark
471
+ // revision marker in w:pPr/w:rPr so Accept/Reject All behaves correctly.
472
+ addParagraphMarkRevisionMarker(paragraph, 'w:del', author, dateStr, state);
473
+ return true;
474
+ }
475
+ //# sourceMappingURL=inPlaceModifier-wrappers.js.map