@beyondwork/docx-react-component 1.0.0 → 1.0.1

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 (704) hide show
  1. package/dist/chunk-32W6IVQE.js +7725 -0
  2. package/dist/chunk-32W6IVQE.js.map +1 -0
  3. package/dist/index.cjs +23722 -0
  4. package/dist/index.cjs.map +1 -0
  5. package/dist/index.d.cts +7 -0
  6. package/dist/index.d.ts +7 -0
  7. package/dist/index.js +16011 -0
  8. package/dist/index.js.map +1 -0
  9. package/dist/public-types-DqCURAz8.d.cts +1152 -0
  10. package/dist/public-types-DqCURAz8.d.ts +1152 -0
  11. package/dist/tailwind.cjs +8295 -0
  12. package/dist/tailwind.cjs.map +1 -0
  13. package/dist/tailwind.d.cts +323 -0
  14. package/dist/tailwind.d.ts +323 -0
  15. package/dist/tailwind.js +553 -0
  16. package/dist/tailwind.js.map +1 -0
  17. package/package.json +52 -31
  18. package/.codex/config.toml +0 -5
  19. package/.corepack/v1/pnpm/10.30.3/.corepack +0 -1
  20. package/.corepack/v1/pnpm/10.30.3/LICENSE +0 -22
  21. package/.corepack/v1/pnpm/10.30.3/README.md +0 -240
  22. package/.corepack/v1/pnpm/10.30.3/dist/node-gyp-bin/node-gyp +0 -6
  23. package/.corepack/v1/pnpm/10.30.3/dist/node-gyp-bin/node-gyp.cmd +0 -5
  24. package/.corepack/v1/pnpm/10.30.3/dist/pnpm.cjs +0 -195400
  25. package/.corepack/v1/pnpm/10.30.3/dist/pnpmrc +0 -2
  26. package/.corepack/v1/pnpm/10.30.3/dist/reflink.darwin-arm64-2HJ4WGO6.node +0 -0
  27. package/.corepack/v1/pnpm/10.30.3/dist/reflink.darwin-x64-3G3H6IW4.node +0 -0
  28. package/.corepack/v1/pnpm/10.30.3/dist/reflink.win32-arm64-msvc-Q6BARPPB.node +0 -0
  29. package/.corepack/v1/pnpm/10.30.3/dist/reflink.win32-x64-msvc-J2TZHRQI.node +0 -0
  30. package/.corepack/v1/pnpm/10.30.3/dist/templates/completion.bash +0 -31
  31. package/.corepack/v1/pnpm/10.30.3/dist/templates/completion.fish +0 -22
  32. package/.corepack/v1/pnpm/10.30.3/dist/templates/completion.ps1 +0 -193
  33. package/.corepack/v1/pnpm/10.30.3/dist/templates/completion.zsh +0 -27
  34. package/.corepack/v1/pnpm/10.30.3/dist/vendor/fastlist-0.3.0-x64.exe +0 -0
  35. package/.corepack/v1/pnpm/10.30.3/dist/vendor/fastlist-0.3.0-x86.exe +0 -0
  36. package/.corepack/v1/pnpm/10.30.3/dist/worker.js +0 -10119
  37. package/.corepack/v1/pnpm/10.30.3/package.json +0 -192
  38. package/.cursor/mcp.json +0 -7
  39. package/.github/workflows/ci.yml +0 -35
  40. package/.mcp.json +0 -7
  41. package/.openclaw/workspace-state.json +0 -4
  42. package/.pnpmrc.json +0 -1
  43. package/.wave-launch.sh +0 -7
  44. package/.workspace-marker +0 -1
  45. package/AGENTS.md +0 -78
  46. package/CHANGELOG.md +0 -177
  47. package/DESIGN.md +0 -929
  48. package/HEARTBEAT.md +0 -7
  49. package/IDENTITY.md +0 -23
  50. package/SOUL.md +0 -36
  51. package/TOOLS.md +0 -40
  52. package/USER.md +0 -17
  53. package/docs/README.md +0 -107
  54. package/docs/agents/wave-cont-eval-role.md +0 -36
  55. package/docs/agents/wave-cont-qa-role.md +0 -52
  56. package/docs/agents/wave-deploy-verifier-role.md +0 -34
  57. package/docs/agents/wave-design-role.md +0 -47
  58. package/docs/agents/wave-documentation-role.md +0 -34
  59. package/docs/agents/wave-infra-role.md +0 -34
  60. package/docs/agents/wave-integration-role.md +0 -37
  61. package/docs/agents/wave-launcher-role.md +0 -41
  62. package/docs/agents/wave-orchestrator-role.md +0 -52
  63. package/docs/agents/wave-planner-role.md +0 -39
  64. package/docs/agents/wave-security-role.md +0 -40
  65. package/docs/architecture/docx/README.md +0 -10
  66. package/docs/architecture/future/README.md +0 -8
  67. package/docs/architecture/ooxml-upgrade-analysis.md +0 -134
  68. package/docs/architecture/platform/shared-openxml-editor-platform.md +0 -153
  69. package/docs/architecture/xlsx/canonical-workbook-model-and-commands.md +0 -187
  70. package/docs/architecture/xlsx/spreadsheet-editor-frontend-architecture.md +0 -150
  71. package/docs/comment-redline-overview.md +0 -350
  72. package/docs/concepts/context7-vs-skills.md +0 -118
  73. package/docs/concepts/operating-modes.md +0 -91
  74. package/docs/concepts/runtime-agnostic-orchestration.md +0 -111
  75. package/docs/concepts/what-is-a-wave.md +0 -217
  76. package/docs/context7/bundles.json +0 -222
  77. package/docs/context7/planner-agent/README.md +0 -28
  78. package/docs/context7/planner-agent/manifest.json +0 -83
  79. package/docs/context7/planner-agent/papers/cooperbench-why-coding-agents-cannot-be-your-teammates-yet.md +0 -3283
  80. package/docs/context7/planner-agent/papers/dova-deliberation-first-multi-agent-orchestration-for-autonomous-research-automation.md +0 -1699
  81. package/docs/context7/planner-agent/papers/dpbench-large-language-models-struggle-with-simultaneous-coordination.md +0 -2251
  82. package/docs/context7/planner-agent/papers/incremental-planning-to-control-a-blackboard-based-problem-solver.md +0 -1729
  83. package/docs/context7/planner-agent/papers/silo-bench-a-scalable-environment-for-evaluating-distributed-coordination-in-multi-agent-llm-systems.md +0 -3747
  84. package/docs/context7/planner-agent/papers/todoevolve-learning-to-architect-agent-planning-systems.md +0 -1675
  85. package/docs/context7/planner-agent/papers/verified-multi-agent-orchestration-a-plan-execute-verify-replan-framework-for-complex-query-resolution.md +0 -1173
  86. package/docs/context7/planner-agent/papers/why-do-multi-agent-llm-systems-fail.md +0 -5211
  87. package/docs/context7/planner-agent/topics/planning-and-orchestration.md +0 -24
  88. package/docs/evals/arm-templates/README.md +0 -13
  89. package/docs/evals/arm-templates/full-wave.json +0 -15
  90. package/docs/evals/arm-templates/single-agent.json +0 -15
  91. package/docs/evals/benchmark-catalog.json +0 -670
  92. package/docs/evals/cases/README.md +0 -47
  93. package/docs/evals/cases/wave-blackboard-inbox-targeting.json +0 -73
  94. package/docs/evals/cases/wave-contradiction-conflict.json +0 -104
  95. package/docs/evals/cases/wave-expert-routing-preservation.json +0 -69
  96. package/docs/evals/cases/wave-hidden-profile-private-evidence.json +0 -81
  97. package/docs/evals/cases/wave-premature-closure-guard.json +0 -71
  98. package/docs/evals/cases/wave-silo-cross-agent-state.json +0 -77
  99. package/docs/evals/cases/wave-simultaneous-lockstep.json +0 -92
  100. package/docs/evals/external-benchmarks.json +0 -85
  101. package/docs/evals/external-command-config.sample.json +0 -9
  102. package/docs/evals/external-command-config.swe-bench-pro.json +0 -8
  103. package/docs/evals/pilots/README.md +0 -47
  104. package/docs/evals/pilots/swe-bench-pro-public-full-wave-review-10.json +0 -64
  105. package/docs/evals/pilots/swe-bench-pro-public-pilot.json +0 -111
  106. package/docs/evals/wave-benchmark-program.md +0 -302
  107. package/docs/guides/planner.md +0 -220
  108. package/docs/guides/recommendations-0.8.9.md +0 -133
  109. package/docs/guides/signal-wrappers.md +0 -165
  110. package/docs/guides/terminal-surfaces.md +0 -96
  111. package/docs/image copy.png +0 -0
  112. package/docs/image.png +0 -0
  113. package/docs/images/image.png +0 -0
  114. package/docs/legal-feedback-architecture.md +0 -498
  115. package/docs/plans/component-cutover-matrix.json +0 -1072
  116. package/docs/plans/component-cutover-matrix.md +0 -307
  117. package/docs/plans/context7-wave-orchestrator.md +0 -155
  118. package/docs/plans/current-state.md +0 -198
  119. package/docs/plans/docx/README.md +0 -9
  120. package/docs/plans/examples/wave-benchmark-improvement.md +0 -108
  121. package/docs/plans/examples/wave-example-live-proof.md +0 -435
  122. package/docs/plans/master-plan.md +0 -224
  123. package/docs/plans/migration.md +0 -538
  124. package/docs/plans/operations/README.md +0 -7
  125. package/docs/plans/operations/wave-10-word-certification.md +0 -87
  126. package/docs/plans/operations/wave-8-railway-staging.md +0 -153
  127. package/docs/plans/operations/wave-9-manual-certification.md +0 -73
  128. package/docs/plans/platform/README.md +0 -9
  129. package/docs/plans/reference/legal-checklist-coverage.md +0 -258
  130. package/docs/plans/wave-orchestrator.md +0 -423
  131. package/docs/plans/waves/README.md +0 -75
  132. package/docs/plans/waves/completed/wave-0.md +0 -195
  133. package/docs/plans/waves/completed/wave-1.md +0 -379
  134. package/docs/plans/waves/completed/wave-10.md +0 -670
  135. package/docs/plans/waves/completed/wave-11.md +0 -335
  136. package/docs/plans/waves/completed/wave-12.md +0 -417
  137. package/docs/plans/waves/completed/wave-13.md +0 -316
  138. package/docs/plans/waves/completed/wave-14.md +0 -319
  139. package/docs/plans/waves/completed/wave-15.md +0 -321
  140. package/docs/plans/waves/completed/wave-16.md +0 -316
  141. package/docs/plans/waves/completed/wave-17.md +0 -331
  142. package/docs/plans/waves/completed/wave-18.md +0 -328
  143. package/docs/plans/waves/completed/wave-2.md +0 -438
  144. package/docs/plans/waves/completed/wave-3.md +0 -435
  145. package/docs/plans/waves/completed/wave-4.md +0 -430
  146. package/docs/plans/waves/completed/wave-5.md +0 -430
  147. package/docs/plans/waves/completed/wave-6.md +0 -430
  148. package/docs/plans/waves/completed/wave-7.md +0 -526
  149. package/docs/plans/waves/completed/wave-8.md +0 -596
  150. package/docs/plans/waves/completed/wave-9.md +0 -552
  151. package/docs/plans/waves/deferred/README.md +0 -14
  152. package/docs/plans/waves/deferred/encrypted-intake-contracts.md +0 -282
  153. package/docs/plans/waves/deferred/legal-feedback-wave-expansion.md +0 -308
  154. package/docs/plans/waves/deferred/wave-encrypted-intake.md +0 -451
  155. package/docs/plans/waves/design/README.md +0 -5
  156. package/docs/plans/waves/design/wave-1-a1.md +0 -309
  157. package/docs/plans/waves/reviews/README.md +0 -5
  158. package/docs/plans/waves/reviews/wave-0-cont-qa.md +0 -151
  159. package/docs/plans/waves/reviews/wave-1-cont-qa.md +0 -46
  160. package/docs/plans/waves/reviews/wave-10-accessibility-and-design.md +0 -51
  161. package/docs/plans/waves/reviews/wave-10-cont-qa.md +0 -24
  162. package/docs/plans/waves/reviews/wave-10-dashboard-proof.md +0 -46
  163. package/docs/plans/waves/reviews/wave-10-performance-signoff.md +0 -55
  164. package/docs/plans/waves/reviews/wave-10-regression-proof.md +0 -23
  165. package/docs/plans/waves/reviews/wave-10-release-audit.md +0 -31
  166. package/docs/plans/waves/reviews/wave-10-service-proof.md +0 -83
  167. package/docs/plans/waves/reviews/wave-10-word-certification.md +0 -31
  168. package/docs/plans/waves/reviews/wave-18-ai-contract-closure.md +0 -277
  169. package/docs/plans/waves/reviews/wave-18-cont-qa.md +0 -255
  170. package/docs/plans/waves/reviews/wave-18-parity-proof.md +0 -271
  171. package/docs/plans/waves/reviews/wave-19-cont-qa.md +0 -59
  172. package/docs/plans/waves/reviews/wave-2-cont-qa.md +0 -72
  173. package/docs/plans/waves/reviews/wave-20-cont-qa.md +0 -60
  174. package/docs/plans/waves/reviews/wave-25-cont-qa.md +0 -48
  175. package/docs/plans/waves/reviews/wave-28-cont-qa.md +0 -46
  176. package/docs/plans/waves/reviews/wave-29-cont-qa.md +0 -53
  177. package/docs/plans/waves/reviews/wave-3-cont-qa.md +0 -53
  178. package/docs/plans/waves/reviews/wave-3-core-proof.md +0 -77
  179. package/docs/plans/waves/reviews/wave-3-validator-proof.md +0 -73
  180. package/docs/plans/waves/reviews/wave-32-cont-qa.md +0 -43
  181. package/docs/plans/waves/reviews/wave-33-cont-qa.md +0 -526
  182. package/docs/plans/waves/reviews/wave-34-cont-qa.md +0 -100
  183. package/docs/plans/waves/reviews/wave-35-cont-qa.md +0 -145
  184. package/docs/plans/waves/reviews/wave-4-cont-qa.md +0 -47
  185. package/docs/plans/waves/reviews/wave-4-structure-proof.md +0 -69
  186. package/docs/plans/waves/reviews/wave-5-comment-proof.md +0 -158
  187. package/docs/plans/waves/reviews/wave-5-cont-qa.md +0 -68
  188. package/docs/plans/waves/reviews/wave-6-cont-qa.md +0 -416
  189. package/docs/plans/waves/reviews/wave-6-redline-proof.md +0 -130
  190. package/docs/plans/waves/reviews/wave-7-cont-qa.md +0 -82
  191. package/docs/plans/waves/reviews/wave-7-ooxml-compliance.md +0 -85
  192. package/docs/plans/waves/reviews/wave-7-preservation-proof.md +0 -119
  193. package/docs/plans/waves/reviews/wave-7-trust-ux.md +0 -87
  194. package/docs/plans/waves/reviews/wave-8-accessibility-and-design.md +0 -128
  195. package/docs/plans/waves/reviews/wave-8-cont-qa.md +0 -92
  196. package/docs/plans/waves/reviews/wave-8-live-proof.md +0 -140
  197. package/docs/plans/waves/reviews/wave-8-security.md +0 -47
  198. package/docs/plans/waves/reviews/wave-9-editor-embedding.md +0 -39
  199. package/docs/plans/waves/reviews/wave-9-fixture-runner.md +0 -56
  200. package/docs/plans/waves/reviews/wave-9-live-proof.md +0 -105
  201. package/docs/plans/waves/reviews/wave-9-usability-and-performance.md +0 -152
  202. package/docs/plans/waves/specs/README.md +0 -5
  203. package/docs/plans/waves/specs/wave-1-component-boundaries.md +0 -322
  204. package/docs/plans/waves/specs/wave-1-ooxml-contracts.md +0 -323
  205. package/docs/plans/waves/specs/wave-1-review-and-ui-contracts.md +0 -339
  206. package/docs/plans/waves/specs/wave-1-runtime-contracts.md +0 -509
  207. package/docs/plans/waves/wave-19.md +0 -341
  208. package/docs/plans/waves/wave-20.md +0 -308
  209. package/docs/plans/waves/wave-21.md +0 -289
  210. package/docs/plans/waves/wave-22.md +0 -221
  211. package/docs/plans/waves/wave-23.md +0 -295
  212. package/docs/plans/waves/wave-24.md +0 -286
  213. package/docs/plans/waves/wave-25.md +0 -313
  214. package/docs/plans/waves/wave-26.md +0 -300
  215. package/docs/plans/waves/wave-27.md +0 -299
  216. package/docs/plans/waves/wave-28.md +0 -368
  217. package/docs/plans/waves/wave-29.md +0 -303
  218. package/docs/plans/waves/wave-30.md +0 -307
  219. package/docs/plans/waves/wave-31.md +0 -231
  220. package/docs/plans/waves/wave-32.md +0 -152
  221. package/docs/plans/waves/wave-33.md +0 -147
  222. package/docs/plans/waves/wave-34.md +0 -148
  223. package/docs/plans/waves/wave-35.md +0 -141
  224. package/docs/plans/waves/wave-36.md +0 -146
  225. package/docs/plans/xlsx/README.md +0 -14
  226. package/docs/plans/xlsx/xlsx-fixture-corpus-and-certification-plan.md +0 -126
  227. package/docs/reference/cli-reference.md +0 -600
  228. package/docs/reference/coordination-and-closure.md +0 -487
  229. package/docs/reference/deep-research-report (15).md +0 -25
  230. package/docs/reference/docx/README.md +0 -10
  231. package/docs/reference/legal-checklist.md +0 -445
  232. package/docs/reference/live-proof-waves.md +0 -199
  233. package/docs/reference/ooxml-compliance.md +0 -129
  234. package/docs/reference/ooxml-feature-parity-matrix.md +0 -172
  235. package/docs/reference/platform/shared-ooxml-platform-guidance.md +0 -77
  236. package/docs/reference/prototype-agent-prompt-legal-fidelity.md +0 -155
  237. package/docs/reference/public-api.md +0 -456
  238. package/docs/reference/repository-guidance.md +0 -58
  239. package/docs/reference/runtime-config/README.md +0 -182
  240. package/docs/reference/runtime-config/claude.md +0 -110
  241. package/docs/reference/runtime-config/codex.md +0 -82
  242. package/docs/reference/runtime-config/opencode.md +0 -93
  243. package/docs/reference/sample-waves.md +0 -105
  244. package/docs/reference/skills.md +0 -237
  245. package/docs/reference/templates/AGENTS.md +0 -78
  246. package/docs/reference/templates/HEARTBEAT.md +0 -7
  247. package/docs/reference/templates/IDENTITY.md +0 -23
  248. package/docs/reference/templates/SOUL.md +0 -36
  249. package/docs/reference/templates/TOOLS.md +0 -40
  250. package/docs/reference/templates/USER.md +0 -17
  251. package/docs/reference/wave-control.md +0 -184
  252. package/docs/reference/wave-planning-lessons.md +0 -167
  253. package/docs/reference/word-review-editor-frontend-architecture.md +0 -479
  254. package/docs/reference/word-review-editor-ux-guide.md +0 -253
  255. package/docs/reference/xlsx/xlsx-ooxml-compliance.md +0 -137
  256. package/docs/research/agent-context-sources.md +0 -178
  257. package/docs/research/coordination-failure-review.md +0 -290
  258. package/docs/research/docx-react-component/Canonical Document Schema Specification for a React-based Word-compatible Editor.md +0 -2317
  259. package/docs/research/docx-react-component/Feature Compatibility Matrix for a React Word Compatible Legal Editor v1.md +0 -219
  260. package/docs/research/docx-react-component/React Component Architecture and Front-End Structure Specification for a Word-Compatible Legal Review Editor.md +0 -1112
  261. package/docs/research/docx-react-component/document_compatibility_and_testing_spec.md +0 -751
  262. package/docs/research/xlsx/raw/README.md +0 -13
  263. package/docs/roadmap.md +0 -174
  264. package/docs/superpowers/plans/2026-03-28-harness-control-bar.md +0 -677
  265. package/docs/superpowers/specs/2026-03-28-harness-control-bar-design.md +0 -274
  266. package/docs/xlsx-react/README.md +0 -38
  267. package/docs/xlsx-react/agent-llm-interaction-layer-docx-xlsx.md +0 -621
  268. package/docs/xlsx-react/canonical-workbook-model-and-commands.md +0 -948
  269. package/docs/xlsx-react/shared-openxml-editor-platform-docx-xlsx.md +0 -228
  270. package/docs/xlsx-react/spreadsheet-editor-component-architecture.md +0 -809
  271. package/docs/xlsx-react/spreadsheet-editor-frontend-architecture.md +0 -537
  272. package/docs/xlsx-react/spreadsheet-editor-ux-guide.md +0 -520
  273. package/docs/xlsx-react/xlsx-editor-research-pack.md +0 -871
  274. package/docs/xlsx-react/xlsx-fixture-corpus-and-certification-plan.md +0 -436
  275. package/docs/xlsx-react/xlsx-ooxml-compliance.md +0 -320
  276. package/examples/README.md +0 -16
  277. package/memory/MEMORY.md +0 -24
  278. package/pnpm-workspace.yaml +0 -4
  279. package/scripts/check-no-authored-js.sh +0 -13
  280. package/scripts/context7-api-check.sh +0 -65
  281. package/scripts/context7-export-env.sh +0 -42
  282. package/scripts/run-context7-mcp.sh +0 -8
  283. package/scripts/run-workspace-tests.sh +0 -15
  284. package/scripts/start-wave-10-local.sh +0 -189
  285. package/scripts/wave-agent-attach.sh +0 -47
  286. package/scripts/wave-auto-answer.sh +0 -118
  287. package/scripts/wave-dashboard-attach.sh +0 -13
  288. package/scripts/wave-launch.sh +0 -273
  289. package/scripts/wave-overnight-supervisor.sh +0 -145
  290. package/scripts/wave-status.sh +0 -379
  291. package/scripts/wave-watch.sh +0 -231
  292. package/services/README.md +0 -17
  293. package/services/openxml-validator/Dockerfile +0 -29
  294. package/services/openxml-validator/OpenXmlValidator.Api.csproj +0 -12
  295. package/services/openxml-validator/Program.cs +0 -436
  296. package/services/openxml-validator/README.md +0 -152
  297. package/services/openxml-validator/railway.json +0 -16
  298. package/services/react-word-editor/.tmp-a4/src/api/public-types.ts +0 -318
  299. package/services/react-word-editor/.tmp-a4/src/ui/WordReviewEditor.tsx +0 -1302
  300. package/services/react-word-editor/.tmp-a4/src/ui/editor-surface/editor-surface.tsx +0 -546
  301. package/services/react-word-editor/.tmp-a4/test/ui/word-review-editor.test.tsx +0 -146
  302. package/services/react-word-editor/.tmp-a4-build/src/api/public-types.js +0 -2
  303. package/services/react-word-editor/.tmp-a4-build/src/ui/WordReviewEditor.js +0 -818
  304. package/services/react-word-editor/.tmp-a4-build/src/ui/editor-surface/editor-surface.js +0 -229
  305. package/services/react-word-editor/.tmp-a4-build/test/ui/word-review-editor.test.js +0 -121
  306. package/services/react-word-editor/.tmp-wave-4-a3-tsconfig.json +0 -21
  307. package/services/react-word-editor/.tmp-wave-4-a3-tsconfig.tsbuildinfo +0 -1
  308. package/services/react-word-editor/Dockerfile +0 -26
  309. package/services/react-word-editor/README.md +0 -254
  310. package/services/react-word-editor/app/api/certification/route.ts +0 -79
  311. package/services/react-word-editor/app/api/demo-sessions/route.ts +0 -109
  312. package/services/react-word-editor/app/api/deploy-health/route.ts +0 -23
  313. package/services/react-word-editor/app/api/exports/[exportId]/route.ts +0 -34
  314. package/services/react-word-editor/app/api/exports/route.ts +0 -81
  315. package/services/react-word-editor/app/api/fixtures/[fixtureId]/run/route.ts +0 -100
  316. package/services/react-word-editor/app/api/health/route.ts +0 -70
  317. package/services/react-word-editor/app/api/runs/[runId]/route.ts +0 -36
  318. package/services/react-word-editor/app/api/scenarios/[scenarioId]/run/route.ts +0 -85
  319. package/services/react-word-editor/app/api/sessions/[sessionId]/route.ts +0 -199
  320. package/services/react-word-editor/app/api/sessions/[sessionId]/source/route.ts +0 -45
  321. package/services/react-word-editor/app/api/uploads/route.ts +0 -70
  322. package/services/react-word-editor/app/api/validate/route.ts +0 -310
  323. package/services/react-word-editor/app/certification/[runId]/page.tsx +0 -14
  324. package/services/react-word-editor/app/certification/page.tsx +0 -32
  325. package/services/react-word-editor/app/dashboard/page.tsx +0 -7
  326. package/services/react-word-editor/app/demo/page.tsx +0 -30
  327. package/services/react-word-editor/app/demo/prototype-client.tsx +0 -1080
  328. package/services/react-word-editor/app/editor/[sessionId]/page.tsx +0 -33
  329. package/services/react-word-editor/app/fixtures/page.tsx +0 -7
  330. package/services/react-word-editor/app/globals.css +0 -121
  331. package/services/react-word-editor/app/layout.tsx +0 -32
  332. package/services/react-word-editor/app/page.tsx +0 -30
  333. package/services/react-word-editor/app/runs/[runId]/page.tsx +0 -34
  334. package/services/react-word-editor/app/wave-10-word-review/page.tsx +0 -7
  335. package/services/react-word-editor/components/harness-control-bar.tsx +0 -289
  336. package/services/react-word-editor/components/harness-editor-session-client.tsx +0 -1214
  337. package/services/react-word-editor/components/harness-workspace-page.tsx +0 -715
  338. package/services/react-word-editor/components/reduced-motion-toggle.tsx +0 -79
  339. package/services/react-word-editor/components/workspace-certification-panel.tsx +0 -307
  340. package/services/react-word-editor/lib/certification-bundle.ts +0 -796
  341. package/services/react-word-editor/lib/certification-store.ts +0 -661
  342. package/services/react-word-editor/lib/demo-fixtures.test.mjs +0 -195
  343. package/services/react-word-editor/lib/demo-fixtures.ts +0 -1519
  344. package/services/react-word-editor/lib/editor-session-summary.test.mjs +0 -68
  345. package/services/react-word-editor/lib/editor-session-summary.ts +0 -14
  346. package/services/react-word-editor/lib/editor-session.ts +0 -228
  347. package/services/react-word-editor/lib/exports-route.test.mjs +0 -32
  348. package/services/react-word-editor/lib/harness-client.ts +0 -347
  349. package/services/react-word-editor/lib/harness-config.json +0 -30
  350. package/services/react-word-editor/lib/harness-config.test.mjs +0 -31
  351. package/services/react-word-editor/lib/harness-config.ts +0 -21
  352. package/services/react-word-editor/lib/harness-editor-datastore.test.mjs +0 -220
  353. package/services/react-word-editor/lib/harness-editor-datastore.ts +0 -161
  354. package/services/react-word-editor/lib/private-mode.test.mjs +0 -42
  355. package/services/react-word-editor/lib/private-mode.ts +0 -61
  356. package/services/react-word-editor/lib/regression-report.test.mjs +0 -352
  357. package/services/react-word-editor/lib/regression-report.ts +0 -896
  358. package/services/react-word-editor/lib/run-artifacts.ts +0 -934
  359. package/services/react-word-editor/lib/run-history.ts +0 -755
  360. package/services/react-word-editor/lib/scenario-artifacts.test.mjs +0 -41
  361. package/services/react-word-editor/lib/scenario-artifacts.ts +0 -44
  362. package/services/react-word-editor/lib/storage.ts +0 -953
  363. package/services/react-word-editor/lib/validator-client.test.mjs +0 -54
  364. package/services/react-word-editor/lib/validator-client.ts +0 -95
  365. package/services/react-word-editor/lib/workspace-navigation.ts +0 -79
  366. package/services/react-word-editor/middleware.ts +0 -35
  367. package/services/react-word-editor/next-env.d.ts +0 -6
  368. package/services/react-word-editor/next.config.mjs +0 -15
  369. package/services/react-word-editor/package.json +0 -38
  370. package/services/react-word-editor/postcss.config.mjs +0 -8
  371. package/services/react-word-editor/railway.json +0 -21
  372. package/services/react-word-editor/scripts/wave-10-certification.mjs +0 -101
  373. package/services/react-word-editor/scripts/wave-9-live-usability-pilot.mjs +0 -911
  374. package/services/react-word-editor/tsconfig.json +0 -39
  375. package/services/react-word-editor/tsconfig.tsbuildinfo +0 -1
  376. package/skills/README.md +0 -48
  377. package/skills/domain-docx-compatibility/SKILL.md +0 -44
  378. package/skills/domain-docx-compatibility/skill.json +0 -19
  379. package/skills/domain-editor-architecture/SKILL.md +0 -49
  380. package/skills/domain-editor-architecture/skill.json +0 -19
  381. package/skills/domain-legal-review/SKILL.md +0 -39
  382. package/skills/domain-legal-review/skill.json +0 -19
  383. package/skills/provider-aws/SKILL.md +0 -117
  384. package/skills/provider-aws/adapters/claude.md +0 -1
  385. package/skills/provider-aws/adapters/codex.md +0 -1
  386. package/skills/provider-aws/references/service-verification.md +0 -39
  387. package/skills/provider-aws/skill.json +0 -54
  388. package/skills/provider-custom-deploy/SKILL.md +0 -64
  389. package/skills/provider-custom-deploy/skill.json +0 -50
  390. package/skills/provider-docker-compose/SKILL.md +0 -96
  391. package/skills/provider-docker-compose/adapters/local.md +0 -1
  392. package/skills/provider-docker-compose/skill.json +0 -53
  393. package/skills/provider-github-release/SKILL.md +0 -121
  394. package/skills/provider-github-release/adapters/claude.md +0 -1
  395. package/skills/provider-github-release/adapters/codex.md +0 -1
  396. package/skills/provider-github-release/skill.json +0 -55
  397. package/skills/provider-kubernetes/SKILL.md +0 -143
  398. package/skills/provider-kubernetes/adapters/claude.md +0 -1
  399. package/skills/provider-kubernetes/adapters/codex.md +0 -1
  400. package/skills/provider-kubernetes/references/kubectl-patterns.md +0 -58
  401. package/skills/provider-kubernetes/skill.json +0 -52
  402. package/skills/provider-railway/SKILL.md +0 -123
  403. package/skills/provider-railway/adapters/claude.md +0 -1
  404. package/skills/provider-railway/adapters/codex.md +0 -1
  405. package/skills/provider-railway/adapters/local.md +0 -1
  406. package/skills/provider-railway/adapters/opencode.md +0 -1
  407. package/skills/provider-railway/references/verification-commands.md +0 -39
  408. package/skills/provider-railway/skill.json +0 -71
  409. package/skills/provider-ssh-manual/SKILL.md +0 -97
  410. package/skills/provider-ssh-manual/skill.json +0 -54
  411. package/skills/repo-coding-rules/SKILL.md +0 -55
  412. package/skills/repo-coding-rules/skill.json +0 -34
  413. package/skills/role-cont-eval/SKILL.md +0 -91
  414. package/skills/role-cont-eval/adapters/codex.md +0 -1
  415. package/skills/role-cont-eval/skill.json +0 -36
  416. package/skills/role-cont-qa/SKILL.md +0 -100
  417. package/skills/role-cont-qa/adapters/claude.md +0 -1
  418. package/skills/role-cont-qa/skill.json +0 -36
  419. package/skills/role-deploy/SKILL.md +0 -97
  420. package/skills/role-deploy/skill.json +0 -36
  421. package/skills/role-design/SKILL.md +0 -50
  422. package/skills/role-design/skill.json +0 -36
  423. package/skills/role-documentation/SKILL.md +0 -76
  424. package/skills/role-documentation/skill.json +0 -36
  425. package/skills/role-implementation/SKILL.md +0 -45
  426. package/skills/role-implementation/skill.json +0 -36
  427. package/skills/role-infra/SKILL.md +0 -81
  428. package/skills/role-infra/skill.json +0 -36
  429. package/skills/role-integration/SKILL.md +0 -91
  430. package/skills/role-integration/skill.json +0 -36
  431. package/skills/role-planner/SKILL.md +0 -39
  432. package/skills/role-planner/skill.json +0 -21
  433. package/skills/role-research/SKILL.md +0 -65
  434. package/skills/role-research/skill.json +0 -36
  435. package/skills/role-security/SKILL.md +0 -60
  436. package/skills/role-security/skill.json +0 -36
  437. package/skills/runtime-claude/SKILL.md +0 -66
  438. package/skills/runtime-claude/skill.json +0 -36
  439. package/skills/runtime-codex/SKILL.md +0 -58
  440. package/skills/runtime-codex/skill.json +0 -36
  441. package/skills/runtime-local/SKILL.md +0 -46
  442. package/skills/runtime-local/skill.json +0 -36
  443. package/skills/runtime-opencode/SKILL.md +0 -58
  444. package/skills/runtime-opencode/skill.json +0 -36
  445. package/skills/signal-hygiene/SKILL.md +0 -51
  446. package/skills/signal-hygiene/skill.json +0 -20
  447. package/skills/tui-design/SKILL.md +0 -77
  448. package/skills/tui-design/references/tui-design.md +0 -259
  449. package/skills/tui-design/skill.json +0 -36
  450. package/skills/wave-core/SKILL.md +0 -141
  451. package/skills/wave-core/references/marker-syntax.md +0 -70
  452. package/skills/wave-core/skill.json +0 -35
  453. package/src/README.md +0 -85
  454. package/src/api/README.md +0 -22
  455. package/src/api/public-types.ts +0 -525
  456. package/src/component-inventory.md +0 -99
  457. package/src/core/README.md +0 -10
  458. package/src/core/commands/README.md +0 -3
  459. package/src/core/commands/formatting-commands.ts +0 -161
  460. package/src/core/commands/image-commands.ts +0 -144
  461. package/src/core/commands/index.ts +0 -1013
  462. package/src/core/commands/list-commands.ts +0 -370
  463. package/src/core/commands/review-commands.ts +0 -108
  464. package/src/core/commands/text-commands.ts +0 -119
  465. package/src/core/schema/README.md +0 -3
  466. package/src/core/schema/text-schema.ts +0 -512
  467. package/src/core/selection/README.md +0 -3
  468. package/src/core/selection/mapping.ts +0 -238
  469. package/src/core/selection/review-anchors.ts +0 -94
  470. package/src/core/state/README.md +0 -3
  471. package/src/core/state/editor-state.ts +0 -580
  472. package/src/core/state/text-transaction.ts +0 -276
  473. package/src/formats/xlsx/io/parse-shared-strings.ts +0 -41
  474. package/src/formats/xlsx/io/parse-sheet.ts +0 -289
  475. package/src/formats/xlsx/io/parse-styles.ts +0 -57
  476. package/src/formats/xlsx/io/parse-workbook.ts +0 -75
  477. package/src/formats/xlsx/io/xlsx-session.ts +0 -306
  478. package/src/formats/xlsx/model/cell.ts +0 -189
  479. package/src/formats/xlsx/model/sheet.ts +0 -244
  480. package/src/formats/xlsx/model/styles.ts +0 -118
  481. package/src/formats/xlsx/model/workbook.ts +0 -449
  482. package/src/io/README.md +0 -10
  483. package/src/io/docx-session.ts +0 -1763
  484. package/src/io/export/README.md +0 -3
  485. package/src/io/export/export-session.ts +0 -165
  486. package/src/io/export/minimal-docx.ts +0 -115
  487. package/src/io/export/reattach-preserved-parts.ts +0 -54
  488. package/src/io/export/serialize-comments.ts +0 -876
  489. package/src/io/export/serialize-footnotes.ts +0 -217
  490. package/src/io/export/serialize-headers-footers.ts +0 -200
  491. package/src/io/export/serialize-main-document.ts +0 -982
  492. package/src/io/export/serialize-numbering.ts +0 -97
  493. package/src/io/export/serialize-revisions.ts +0 -389
  494. package/src/io/export/serialize-runtime-revisions.ts +0 -265
  495. package/src/io/export/serialize-tables.ts +0 -147
  496. package/src/io/export/split-review-boundaries.ts +0 -194
  497. package/src/io/normalize/README.md +0 -3
  498. package/src/io/normalize/normalize-text.ts +0 -437
  499. package/src/io/ooxml/README.md +0 -3
  500. package/src/io/ooxml/parse-comments.ts +0 -779
  501. package/src/io/ooxml/parse-complex-content.ts +0 -287
  502. package/src/io/ooxml/parse-fields.ts +0 -438
  503. package/src/io/ooxml/parse-footnotes.ts +0 -403
  504. package/src/io/ooxml/parse-headers-footers.ts +0 -483
  505. package/src/io/ooxml/parse-inline-media.ts +0 -431
  506. package/src/io/ooxml/parse-main-document.ts +0 -1846
  507. package/src/io/ooxml/parse-numbering.ts +0 -425
  508. package/src/io/ooxml/parse-revisions.ts +0 -658
  509. package/src/io/ooxml/parse-shapes.ts +0 -271
  510. package/src/io/ooxml/parse-tables.ts +0 -568
  511. package/src/io/ooxml/parse-theme.ts +0 -314
  512. package/src/io/ooxml/part-manifest.ts +0 -136
  513. package/src/io/ooxml/revision-boundaries.ts +0 -351
  514. package/src/io/opc/README.md +0 -3
  515. package/src/io/opc/corrupt-package.ts +0 -166
  516. package/src/io/opc/docx-package.ts +0 -74
  517. package/src/io/opc/package-reader.ts +0 -320
  518. package/src/io/opc/package-writer.ts +0 -273
  519. package/src/model/README.md +0 -3
  520. package/src/model/canonical-document.ts +0 -1911
  521. package/src/model/cds-1.0.0.ts +0 -196
  522. package/src/model/snapshot.ts +0 -393
  523. package/src/preservation/README.md +0 -3
  524. package/src/preservation/markup-compatibility.ts +0 -48
  525. package/src/preservation/opaque-fragment-store.ts +0 -89
  526. package/src/preservation/opaque-region.ts +0 -233
  527. package/src/preservation/package-preservation.ts +0 -120
  528. package/src/preservation/preserved-part-manifest.ts +0 -56
  529. package/src/preservation/relationship-retention.ts +0 -57
  530. package/src/preservation/store.ts +0 -185
  531. package/src/review/README.md +0 -16
  532. package/src/review/store/README.md +0 -3
  533. package/src/review/store/comment-anchors.ts +0 -70
  534. package/src/review/store/comment-remapping.ts +0 -154
  535. package/src/review/store/comment-store.ts +0 -331
  536. package/src/review/store/comment-thread.ts +0 -109
  537. package/src/review/store/revision-actions.ts +0 -394
  538. package/src/review/store/revision-store.ts +0 -303
  539. package/src/review/store/revision-types.ts +0 -168
  540. package/src/review/store/runtime-comment-store.ts +0 -43
  541. package/src/runtime/README.md +0 -3
  542. package/src/runtime/ai-action-policy.ts +0 -764
  543. package/src/runtime/document-runtime.ts +0 -969
  544. package/src/runtime/read-only-diagnostics-runtime.ts +0 -232
  545. package/src/runtime/review-runtime.ts +0 -44
  546. package/src/runtime/revision-runtime.ts +0 -107
  547. package/src/runtime/session-capabilities.ts +0 -138
  548. package/src/runtime/surface-projection.ts +0 -570
  549. package/src/runtime/table-commands.ts +0 -84
  550. package/src/runtime/table-schema.ts +0 -125
  551. package/src/ui/README.md +0 -30
  552. package/src/ui/WordReviewEditor.tsx +0 -1283
  553. package/src/ui/comments/README.md +0 -3
  554. package/src/ui/compatibility/README.md +0 -3
  555. package/src/ui/editor-surface/README.md +0 -3
  556. package/src/ui/headless/comment-decoration-model.ts +0 -124
  557. package/src/ui/headless/revision-decoration-model.ts +0 -128
  558. package/src/ui/headless/selection-helpers.ts +0 -34
  559. package/src/ui/headless/use-editor-keyboard.ts +0 -98
  560. package/src/ui/review/README.md +0 -3
  561. package/src/ui/shared/revision-filters.ts +0 -31
  562. package/src/ui/status/README.md +0 -3
  563. package/src/ui/theme/README.md +0 -3
  564. package/src/ui/toolbar/README.md +0 -3
  565. package/src/ui-tailwind/chrome/tw-alert-banner.tsx +0 -48
  566. package/src/ui-tailwind/chrome/tw-selection-toolbar.tsx +0 -44
  567. package/src/ui-tailwind/chrome/tw-unsaved-modal.tsx +0 -58
  568. package/src/ui-tailwind/chrome/use-before-unload.ts +0 -20
  569. package/src/ui-tailwind/editor-surface/pm-command-bridge.ts +0 -139
  570. package/src/ui-tailwind/editor-surface/pm-decorations.ts +0 -98
  571. package/src/ui-tailwind/editor-surface/pm-position-map.ts +0 -123
  572. package/src/ui-tailwind/editor-surface/pm-schema.ts +0 -452
  573. package/src/ui-tailwind/editor-surface/pm-state-from-snapshot.ts +0 -327
  574. package/src/ui-tailwind/editor-surface/search-plugin.ts +0 -157
  575. package/src/ui-tailwind/editor-surface/tw-caret.tsx +0 -12
  576. package/src/ui-tailwind/editor-surface/tw-editor-surface.tsx +0 -150
  577. package/src/ui-tailwind/editor-surface/tw-inline-token.tsx +0 -118
  578. package/src/ui-tailwind/editor-surface/tw-opaque-block.tsx +0 -52
  579. package/src/ui-tailwind/editor-surface/tw-paragraph-block.tsx +0 -151
  580. package/src/ui-tailwind/editor-surface/tw-prosemirror-surface.tsx +0 -215
  581. package/src/ui-tailwind/editor-surface/tw-segment-view.tsx +0 -111
  582. package/src/ui-tailwind/editor-surface/tw-table-node-view.tsx +0 -108
  583. package/src/ui-tailwind/index.ts +0 -61
  584. package/src/ui-tailwind/review/tw-comment-sidebar.tsx +0 -276
  585. package/src/ui-tailwind/review/tw-health-panel.tsx +0 -120
  586. package/src/ui-tailwind/review/tw-review-rail.tsx +0 -120
  587. package/src/ui-tailwind/review/tw-revision-sidebar.tsx +0 -164
  588. package/src/ui-tailwind/status/tw-status-bar.tsx +0 -58
  589. package/src/ui-tailwind/theme/editor-theme.css +0 -190
  590. package/src/ui-tailwind/toolbar/tw-toolbar-icon-button.tsx +0 -48
  591. package/src/ui-tailwind/toolbar/tw-toolbar.tsx +0 -231
  592. package/src/ui-tailwind/tw-review-workspace.tsx +0 -140
  593. package/src/validation/README.md +0 -3
  594. package/src/validation/compatibility-engine.ts +0 -317
  595. package/src/validation/compatibility-report.ts +0 -160
  596. package/src/validation/diagnostics.ts +0 -203
  597. package/src/validation/import-diagnostics.ts +0 -128
  598. package/src/validation/low-priority-word-surfaces.ts +0 -373
  599. package/test/README.md +0 -16
  600. package/test/core/formatting-commands.test.ts +0 -285
  601. package/test/core/image-commands.test.ts +0 -298
  602. package/test/core/mapping.test.ts +0 -186
  603. package/test/core/text-commands.test.ts +0 -176
  604. package/test/fixtures/docx/F01-basic-contract.docx +0 -0
  605. package/test/fixtures/docx/F01-basic-contract.md +0 -33
  606. package/test/fixtures/docx/F02-headings-styles.docx +0 -0
  607. package/test/fixtures/docx/F02-headings-styles.md +0 -33
  608. package/test/fixtures/docx/F03-legal-outline-numbering.docx +0 -0
  609. package/test/fixtures/docx/F03-legal-outline-numbering.md +0 -34
  610. package/test/fixtures/docx/F04-restart-numbering-schedules.docx +0 -0
  611. package/test/fixtures/docx/F04-restart-numbering-schedules.md +0 -33
  612. package/test/fixtures/docx/F05-table-heavy-agreement.docx +0 -0
  613. package/test/fixtures/docx/F05-table-heavy-agreement.md +0 -34
  614. package/test/fixtures/docx/F06-merged-cells-signature-table.docx +0 -0
  615. package/test/fixtures/docx/F06-merged-cells-signature-table.md +0 -34
  616. package/test/fixtures/docx/F07-inline-images-exhibit.docx +0 -0
  617. package/test/fixtures/docx/F07-inline-images-exhibit.md +0 -34
  618. package/test/fixtures/docx/F08-hyperlinks.docx +0 -0
  619. package/test/fixtures/docx/F08-hyperlinks.md +0 -33
  620. package/test/fixtures/docx/F09-comments-single-paragraph.docx +0 -0
  621. package/test/fixtures/docx/F09-comments-single-paragraph.md +0 -33
  622. package/test/fixtures/docx/F10-threaded-comments-resolve.docx +0 -0
  623. package/test/fixtures/docx/F10-threaded-comments-resolve.md +0 -33
  624. package/test/fixtures/docx/F11-redlines-basic.docx +0 -0
  625. package/test/fixtures/docx/F11-redlines-basic.md +0 -33
  626. package/test/fixtures/docx/F12-redlines-paragraph-joins-splits.docx +0 -0
  627. package/test/fixtures/docx/F12-redlines-paragraph-joins-splits.md +0 -33
  628. package/test/fixtures/docx/F13-comments-on-deleted-text.docx +0 -0
  629. package/test/fixtures/docx/F13-comments-on-deleted-text.md +0 -33
  630. package/test/fixtures/docx/F14-revisions-in-tables-and-lists.docx +0 -0
  631. package/test/fixtures/docx/F14-revisions-in-tables-and-lists.md +0 -33
  632. package/test/fixtures/docx/F15-sections-headers-footers.docx +0 -0
  633. package/test/fixtures/docx/F15-sections-headers-footers.md +0 -33
  634. package/test/fixtures/docx/F16-footnotes-endnotes.docx +0 -0
  635. package/test/fixtures/docx/F16-footnotes-endnotes.md +0 -33
  636. package/test/fixtures/docx/F17-fields-and-toc.docx +0 -0
  637. package/test/fixtures/docx/F17-fields-and-toc.md +0 -33
  638. package/test/fixtures/docx/F18-content-controls-template.docx +0 -0
  639. package/test/fixtures/docx/F18-content-controls-template.md +0 -33
  640. package/test/fixtures/docx/F19-custom-xml-doc-assembly.docx +0 -0
  641. package/test/fixtures/docx/F19-custom-xml-doc-assembly.md +0 -35
  642. package/test/fixtures/docx/F20-unknown-ooxml-and-alternatecontent.docx +0 -0
  643. package/test/fixtures/docx/F20-unknown-ooxml-and-alternatecontent.md +0 -33
  644. package/test/fixtures/docx/F21-malformed-broken-docx.docx +0 -0
  645. package/test/fixtures/docx/F21-malformed-broken-docx.md +0 -33
  646. package/test/fixtures/docx/README.md +0 -74
  647. package/test/fixtures/docx/certification-manifest.json +0 -104
  648. package/test/fixtures/docx/fixtures.manifest.json +0 -196
  649. package/test/fixtures/encrypted-docx/README.md +0 -27
  650. package/test/fixtures/encrypted-docx/certification-manifest.json +0 -9
  651. package/test/fixtures/encrypted-docx/fixtures.manifest.json +0 -47
  652. package/test/fixtures/scenarios/docx/README.md +0 -25
  653. package/test/fixtures/scenarios/docx/S01-sow-template.docx +0 -0
  654. package/test/fixtures/scenarios/docx/S01-sow-template.md +0 -30
  655. package/test/fixtures/scenarios/docx/S02-bw-partner-user-licence-agreement-redlines.docx +0 -0
  656. package/test/fixtures/scenarios/docx/S02-bw-partner-user-licence-agreement-redlines.md +0 -32
  657. package/test/fixtures/scenarios/docx/scenario-manifest.json +0 -53
  658. package/test/formats/xlsx/io/xlsx-import.test.ts +0 -766
  659. package/test/formats/xlsx/model/workbook.test.ts +0 -669
  660. package/test/helpers/dom-setup.ts +0 -124
  661. package/test/io/comment-roundtrip.test.ts +0 -272
  662. package/test/io/complex-content-roundtrip.test.ts +0 -632
  663. package/test/io/docx-compatibility-regression.test.ts +0 -199
  664. package/test/io/docx-session.test.ts +0 -1495
  665. package/test/io/footnotes-roundtrip.test.ts +0 -318
  666. package/test/io/headers-footers-roundtrip.test.ts +0 -547
  667. package/test/io/numbering-roundtrip.test.ts +0 -234
  668. package/test/io/package-reader.test.ts +0 -199
  669. package/test/io/paragraph-properties-roundtrip.test.ts +0 -129
  670. package/test/io/preserved-package-roundtrip.test.ts +0 -365
  671. package/test/io/property-completeness.test.ts +0 -292
  672. package/test/io/revision-roundtrip.test.ts +0 -347
  673. package/test/io/structural-blocks.test.ts +0 -202
  674. package/test/io/table-media-roundtrip.test.ts +0 -448
  675. package/test/io/table-properties-roundtrip.test.ts +0 -569
  676. package/test/io/table-roundtrip.test.ts +0 -302
  677. package/test/io/text-roundtrip.test.ts +0 -344
  678. package/test/model/canonical-document.test.ts +0 -285
  679. package/test/preservation/opaque-fragment-store.test.ts +0 -121
  680. package/test/preservation/package-preservation.test.ts +0 -395
  681. package/test/preservation/store.test.ts +0 -84
  682. package/test/review/comment-remapping.test.ts +0 -220
  683. package/test/review/comment-store.test.ts +0 -180
  684. package/test/review/move-revisions.test.ts +0 -143
  685. package/test/review/property-change-revisions.test.ts +0 -225
  686. package/test/review/revision-actions.test.ts +0 -330
  687. package/test/review/revision-store.test.ts +0 -193
  688. package/test/runtime/session-capabilities.test.ts +0 -260
  689. package/test/runtime/table-commands.test.ts +0 -356
  690. package/test/runtime/table-schema.test.ts +0 -221
  691. package/test/runtime/tracked-changes-toggle.test.ts +0 -107
  692. package/test/ui/comment-review-surface.test.tsx +0 -114
  693. package/test/ui/reduced-motion-toggle.test.tsx +0 -137
  694. package/test/ui/word-review-editor.imported-scenarios.test.tsx +0 -169
  695. package/test/ui/word-review-editor.interaction.test.tsx +0 -1198
  696. package/test/ui/word-review-editor.test.js +0 -188
  697. package/test/ui/word-review-editor.test.tsx +0 -280
  698. package/test/ui-tailwind/search-plugin.test.ts +0 -286
  699. package/test/validation/compatibility-engine.test.ts +0 -336
  700. package/test/validation/compatibility-report.test.ts +0 -189
  701. package/test/validation/low-priority-word-surfaces.test.ts +0 -282
  702. package/test/validation/malformed-doc.test.ts +0 -113
  703. package/test-results/.last-run.json +0 -4
  704. package/wave.config.json +0 -406
@@ -1,2317 +0,0 @@
1
- # Canonical Document Schema Specification for a React-based Word-compatible Editor
2
-
3
- ## Schema principles and compatibility objectives
4
-
5
- ### Compatibility contract
6
-
7
- This specification defines a **Canonical Document Schema (CDS)** that is the **single source of truth** for:
8
-
9
- - structured editing in a React-based editor,
10
- - comments and anchored annotations,
11
- - tracked changes (revisions),
12
- - `.docx` (OOXML WordprocessingML) import/export,
13
- - validation and repair,
14
- - loss-minimized preservation of unsupported OOXML.
15
-
16
- The `.docx` container is an **Open Packaging Conventions (OPC)** ZIP package composed of **parts** (XML and binary) connected by **relationships**. citeturn0search2 The CDS must therefore preserve both (a) **document semantics** and (b) sufficient **package-level information** to reconstruct a Word-openable `.docx`.
17
-
18
- ### Separation of content vs. annotations
19
-
20
- **Rule P1 (Content purity):** The CDS **content tree** contains only structural content nodes and explicit “opaque/preserved content placeholders.” It **does not** embed comments or revision wrappers as structural nodes.
21
-
22
- - **Comments** and **revisions** are stored in dedicated top-level stores referencing **ranges/anchors** in the content coordinate space.
23
- - Export materializes WordprocessingML comment/revision markup from these stores (e.g., comment anchors/range markers, revision containers), rather than storing them inline in the canonical content.
24
-
25
- This aligns with how WordprocessingML treats paragraph content as including properties, run content, and **annotations (comments/revisions/bookmarks)** as separate markup within the paragraph stream. citeturn3search0turn11search19
26
-
27
- ### Immutability vs. mutability model
28
-
29
- **Rule P2 (Immutable state, transactional edits):**
30
-
31
- - The editor operates on an **immutable** `CanonicalDocument` value.
32
- - Updates occur via **Transactions** consisting of **Steps**.
33
- - Applying a transaction yields a new document with structural sharing permitted at implementation level.
34
-
35
- This mirrors well-established editor-engine practice: applying atomic steps yields a new document, and each step provides a position mapping. citeturn6search0turn9search4
36
-
37
- ### Deterministic structure (canonicalization)
38
-
39
- **Rule P3 (Canonical JSON):** For any in-memory CDS instance, serialization must be deterministic:
40
-
41
- - Object keys are emitted in a stable order (defined below).
42
- - Arrays with semantic ordering (children, marks, steps) preserve order.
43
- - Marks are canonical-sorted (defined below).
44
- - Normalization eliminates representational ambiguity (no empty text nodes, merged adjacent text nodes where allowed, etc.).
45
-
46
- ### Compatibility-first design
47
-
48
- **Rule P4 (Word correctness > editor convenience):** When a choice conflicts between “nice internal modeling” and “Word-openable export,” the schema and invariants prioritize generating OOXML that:
49
-
50
- - opens in recent Word without repair warnings,
51
- - preserves required stories/parts and relationships,
52
- - respects OOXML constraints for comments/revisions/sections/tables.
53
-
54
- ### Lossless round-trip requirements
55
-
56
- **Rule P5 (Round-trip tiers):** The system distinguishes three fidelity tiers:
57
-
58
- - **Tier A (Lossless):** Supported CDS features round-trip `.docx → CDS → .docx → Word` with no semantic loss.
59
- - **Tier B (Preserved opaque):** Unsupported OOXML is preserved either as untouched parts or as opaque fragments re-injected at export, provided the user did not structurally edit inside those fragments.
60
- - **Tier C (Degraded with diagnostics):** If edits invalidate preservation reattachment, content may be dropped or flattened, but the system must emit **deterministic diagnostics** describing the loss.
61
-
62
- Markup Compatibility constructs like `mc:AlternateContent` are explicitly designed for forward/feature compatibility; they must be preserved in Tier B when unsupported. citeturn2search3turn2search7
63
-
64
- ### Mapping stability under edits
65
-
66
- **Rule P6 (Stable coordinate space):** All anchors (comments, revisions, preservation bindings) use the **same global position space** (defined below). Every transaction produces a **Mapping** allowing deterministic remapping of:
67
-
68
- - selections,
69
- - comment anchors,
70
- - revision target ranges,
71
- - preservation bindings.
72
-
73
- This is required because `.docx` comment anchors and revision markup are location-based in the content stream (e.g., comment range start/end markers link to a comment id). citeturn0search8turn0search4
74
-
75
- ## Canonical document envelope and shared types
76
-
77
- ### Fundamental scalar types
78
-
79
- All types are expressed in TypeScript-style interfaces.
80
-
81
- ```ts
82
- // Scalars
83
- export type CDS_SchemaVersion = "cds/1.0.0";
84
-
85
- // Identifiers
86
- export type Id = string; // non-empty, UTF-8 JSON string
87
- export type UUID = string; // RFC 4122 textual form (implementation must validate) citeturn12search0
88
- export type PartName = string; // OPC part name, e.g. "/word/document.xml"
89
- export type RelationshipId = string; // e.g. "rId10"
90
- export type ISO8601DateTime = string; // e.g. "2026-03-25T10:15:30.000Z"
91
- export type HexColorRGB = string; // "RRGGBB" uppercase
92
- export type Base64 = string;
93
-
94
- // Numeric units aligned with WordprocessingML conventions
95
- export type Twips = number; // integer, 1/20 pt
96
- export type HalfPoints = number; // integer, 1/2 pt (e.g., w:sz) citeturn8search2turn8search14
97
- ```
98
-
99
- ### Top-level document model
100
-
101
- #### CanonicalDocument interface
102
-
103
- ```ts
104
- export interface CanonicalDocument {
105
- schemaVersion: CDS_SchemaVersion;
106
-
107
- // Identity
108
- docId: UUID;
109
-
110
- // Lifecycle metadata (system-managed)
111
- createdAt: ISO8601DateTime;
112
- updatedAt: ISO8601DateTime;
113
-
114
- // Document-wide metadata (user/authored + imported)
115
- metadata: DocumentMetadata;
116
-
117
- // Main editable story (Word main document body)
118
- content: DocNode;
119
-
120
- // Style and numbering catalogs (editable subset + preserved extras)
121
- styles: StylesCatalog;
122
- numbering: NumberingCatalog;
123
-
124
- // Media registry
125
- media: MediaCatalog;
126
-
127
- // Annotations
128
- comments: CommentStore;
129
- revisions: RevisionStore;
130
-
131
- // Preservation and package fidelity
132
- preservation: PreservationStore;
133
-
134
- // Validation/repair output and non-fatal issues
135
- diagnostics: DiagnosticStore;
136
- }
137
- ```
138
-
139
- #### Required invariants
140
-
141
- **Invariant D1:** `schemaVersion` MUST be exactly `"cds/1.0.0"` for this spec.
142
-
143
- **Invariant D2:** `content.type` MUST be `"doc"`.
144
-
145
- **Invariant D3:** All IDs in each namespace MUST be unique:
146
-
147
- - node ids unique across the content tree,
148
- - `media.items[*].mediaId` unique,
149
- - `comments.threads[*].threadId` unique,
150
- - `revisions.items[*].revisionId` unique,
151
- - `preservation.fragments[*].fragmentId` unique,
152
- - `preservation.opc.parts[*].partName` unique.
153
-
154
- **Invariant D4:** `updatedAt` MUST be ≥ `createdAt`.
155
-
156
- ### Document metadata
157
-
158
- ```ts
159
- export interface DocumentMetadata {
160
- // Human-facing name
161
- title?: string;
162
-
163
- // OOXML core/app/custom properties pass-through
164
- coreProperties?: Record<string, string>;
165
- appProperties?: Record<string, string>;
166
- customProperties?: Record<string, string | number | boolean | ISO8601DateTime>;
167
-
168
- // Actors used by comments/revisions
169
- actors: Record<Id, Actor>;
170
-
171
- // Import provenance (stable across exports unless reimported)
172
- provenance?: ImportProvenance;
173
- }
174
-
175
- export interface Actor {
176
- actorId: Id; // key == actorId
177
- displayName: string; // non-empty
178
- email?: string;
179
- externalId?: string; // optional mapping to identity provider
180
- }
181
-
182
- export interface ImportProvenance {
183
- importedFrom: "docx";
184
- importedAt: ISO8601DateTime;
185
- sourceFingerprint: string; // implementation-defined stable hash of original package bytes
186
- }
187
- ```
188
-
189
- ### Styles catalog
190
-
191
- WordprocessingML styles are defined in the styles part (`styles.xml`) and applied via `w:pStyle` (paragraph) and `w:rStyle` (character). citeturn4search0turn4search5turn4search8
192
-
193
- ```ts
194
- export interface StylesCatalog {
195
- // Defaults extracted from docDefaults (if present)
196
- defaults: {
197
- paragraph?: ParagraphStyleProps;
198
- run?: RunStyleProps;
199
- };
200
-
201
- paragraphStyles: Record<string, ParagraphStyleDef>; // key = styleId
202
- characterStyles: Record<string, CharacterStyleDef>; // key = styleId
203
- tableStyles: Record<string, TableStyleDef>; // key = styleId
204
-
205
- // Unmodeled style XML preserved losslessly
206
- ooxmlExtras?: {
207
- rawStylesXmlBase64?: Base64; // original /word/styles.xml (optional)
208
- };
209
- }
210
-
211
- export interface StyleDefBase {
212
- styleId: string; // Word styleId
213
- name?: string;
214
- basedOn?: string; // styleId
215
- next?: string; // styleId
216
- linked?: string; // styleId (linked paragraph/character style)
217
- isDefault?: boolean;
218
- isCustom?: boolean;
219
-
220
- // Preservation hook for unknown style attributes/elements
221
- ooxmlUnknown?: PreservedXmlRef;
222
- }
223
-
224
- export interface ParagraphStyleDef extends StyleDefBase {
225
- type: "paragraph";
226
- pPr?: ParagraphStyleProps;
227
- rPr?: RunStyleProps; // run defaults for this paragraph style
228
- }
229
-
230
- export interface CharacterStyleDef extends StyleDefBase {
231
- type: "character";
232
- rPr?: RunStyleProps;
233
- }
234
-
235
- export interface TableStyleDef extends StyleDefBase {
236
- type: "table";
237
- tblPr?: TableProps;
238
- trPr?: TableRowProps;
239
- tcPr?: TableCellProps;
240
- }
241
- ```
242
-
243
- **Style precedence model (required):** When computing effective formatting, apply in this order (lowest to highest), consistent with OOXML style hierarchy: document defaults → table styles → numbering styles → paragraph styles (`w:pStyle`) → character styles (`w:rStyle`) → direct formatting. citeturn4search0turn4search5turn11search20
244
-
245
- ### Numbering catalog
246
-
247
- Numbering in WordprocessingML is referenced from paragraphs via `w:numPr`, including `w:numId` and `w:ilvl`. citeturn4search6turn4search17turn4search2
248
-
249
- ```ts
250
- export interface NumberingCatalog {
251
- abstractNums: Record<string, AbstractNumDef>; // key = abstractNumId
252
- nums: Record<string, NumInstanceDef>; // key = numId
253
-
254
- ooxmlExtras?: {
255
- rawNumberingXmlBase64?: Base64; // original /word/numbering.xml (optional)
256
- };
257
- }
258
-
259
- export interface AbstractNumDef {
260
- abstractNumId: string;
261
- levels: Record<number, NumberingLevelDef>; // level 0..8
262
- ooxmlUnknown?: PreservedXmlRef;
263
- }
264
-
265
- export interface NumInstanceDef {
266
- numId: string;
267
- abstractNumId: string;
268
- levelOverrides?: Record<number, NumberingLevelDef>;
269
- ooxmlUnknown?: PreservedXmlRef;
270
- }
271
-
272
- export interface NumberingLevelDef {
273
- level: number; // 0..8
274
- numFmt: "bullet" | "decimal" | "lowerLetter" | "upperLetter" | "lowerRoman" | "upperRoman" | "other";
275
- lvlText?: string; // e.g. "%1."
276
- start?: number; // default start value
277
- pPr?: ParagraphStyleProps;
278
- rPr?: RunStyleProps;
279
- ooxmlUnknown?: PreservedXmlRef;
280
- }
281
- ```
282
-
283
- ### Media catalog
284
-
285
- Images in WordprocessingML are represented as DrawingML objects inside a run (`w:r/w:drawing`), commonly using `wp:inline` (in-line with text). citeturn2search10
286
-
287
- ```ts
288
- export interface MediaCatalog {
289
- items: Record<Id, MediaItem>; // key = mediaId
290
-
291
- // Relationship and part naming policy must be deterministic on export.
292
- exportPolicy: {
293
- mediaFolder: "/word/media";
294
- filenameStrategy: "stableByMediaId";
295
- relationshipIdStrategy: "stableByOrder";
296
- };
297
- }
298
-
299
- export interface MediaItem {
300
- mediaId: Id;
301
- kind: "image";
302
- mimeType: "image/png" | "image/jpeg" | "image/gif" | "image/webp" | "image/svg+xml";
303
- sha256: string; // hex lowercase
304
- bytesBase64?: Base64; // present if the CDS is self-contained; optional if stored externally
305
- externalUrl?: string; // optional pointer if bytes are not embedded
306
-
307
- // Sizing metadata (used for initial layout hints)
308
- pixelWidth?: number;
309
- pixelHeight?: number;
310
- dpiX?: number;
311
- dpiY?: number;
312
-
313
- // OOXML preservation for DrawingML details not modeled
314
- ooxmlUnknown?: PreservedXmlRef;
315
- }
316
- ```
317
-
318
- ### Diagnostics and warnings
319
-
320
- ```ts
321
- export interface DiagnosticStore {
322
- items: Diagnostic[];
323
- }
324
-
325
- export type DiagnosticSeverity = "info" | "warning" | "error" | "fatal";
326
-
327
- export interface Diagnostic {
328
- diagnosticId: UUID;
329
- severity: DiagnosticSeverity;
330
- code: string; // stable machine-readable code
331
- message: string; // human-readable
332
- createdAt: ISO8601DateTime;
333
-
334
- // Optional localization of where it happened
335
- location?: {
336
- kind: "docRange" | "nodeId" | "partName";
337
- fromPos?: DocPos;
338
- toPos?: DocPos;
339
- nodeId?: Id;
340
- partName?: PartName;
341
- };
342
-
343
- // Repair action (if any)
344
- repair?: {
345
- applied: boolean;
346
- description: string;
347
- };
348
- }
349
- ```
350
-
351
- ## Content model: nodes, marks, and attribute systems
352
-
353
- ### Global node model
354
-
355
- All content is represented as a tree of nodes. Nodes are either **block** or **inline**. Text is inline.
356
-
357
- ```ts
358
- export type Node =
359
- | DocNode
360
- | ParagraphNode
361
- | HeadingNode
362
- | BlockquoteNode
363
- | OrderedListNode
364
- | BulletListNode
365
- | ListItemNode
366
- | TableNode
367
- | TableRowNode
368
- | TableCellNode
369
- | ImageBlockNode
370
- | HorizontalRuleNode
371
- | SectionBreakNode
372
- | OoxmlBlockNode
373
- | TextNode
374
- | HardBreakNode
375
- | HyperlinkNode
376
- | InlineImageNode
377
- | AnchorPlaceholderNode
378
- | OoxmlInlineNode;
379
-
380
- export interface NodeBase {
381
- id: Id; // unique within document
382
- type: string; // discriminator
383
- }
384
- ```
385
-
386
- ### Block node specifications
387
-
388
- WordprocessingML block-level content in the main body is primarily paragraphs (`w:p`) and tables (`w:tbl`). citeturn3search0turn2search5
389
-
390
- Each block node below includes: attributes, allowed children, constraints, WordprocessingML mapping, and editing invariants.
391
-
392
- #### Document root
393
-
394
- ```ts
395
- export interface DocNode extends NodeBase {
396
- type: "doc";
397
- children: BlockNode[];
398
-
399
- attrs: {
400
- // Document-wide section defaults (last section properties in Word)
401
- defaultSection?: SectionPropsRef;
402
-
403
- // Track changes flag (mirrors settings.xml w:trackRevisions)
404
- trackRevisionsDefault?: boolean;
405
- };
406
- }
407
- export type BlockNode =
408
- | ParagraphNode
409
- | HeadingNode
410
- | BlockquoteNode
411
- | OrderedListNode
412
- | BulletListNode
413
- | TableNode
414
- | ImageBlockNode
415
- | HorizontalRuleNode
416
- | SectionBreakNode
417
- | OoxmlBlockNode;
418
- ```
419
-
420
- **Allowed children:** `BlockNode[]` (at least one block is allowed but not required).
421
-
422
- **Constraints:** `doc.children` MAY be empty.
423
-
424
- **WordprocessingML mapping:** `doc` maps to main document part `w:document/w:body` story. WordprocessingML is composed of “stories,” with the main document being required. citeturn3search4
425
-
426
- **Editing invariants:** The editor MUST maintain a valid block sequence; normalization rules below repair invalid blocks.
427
-
428
- #### Paragraph
429
-
430
- ```ts
431
- export interface ParagraphNode extends NodeBase {
432
- type: "paragraph";
433
- children: InlineNode[];
434
-
435
- attrs: ParagraphAttrs;
436
- }
437
-
438
- export interface ParagraphAttrs {
439
- styleId?: string; // maps to w:pPr/w:pStyle citeturn4search0
440
- alignment?: ParagraphAlignment; // maps to w:pPr/w:jc citeturn5search0
441
- indent?: ParagraphIndent; // maps to w:pPr/w:ind citeturn5search1
442
- spacing?: ParagraphSpacing; // maps to w:pPr/w:spacing citeturn5search2
443
- numbering?: ParagraphNumbering; // maps to w:pPr/w:numPr citeturn4search6
444
-
445
- // Paragraph mark formatting (Word stores paragraph mark run props)
446
- paragraphMarkRunStyle?: RunStyleProps; // corresponds to w:pPr/w:rPr (paragraph mark) citeturn4search1turn7search12
447
-
448
- // Preservation of unmodeled pPr sub-elements/attrs
449
- ooxmlUnknownPPr?: PreservedXmlRef;
450
- }
451
-
452
- export type ParagraphAlignment = "left" | "center" | "right" | "both" | "start" | "end";
453
- export interface ParagraphIndent {
454
- leftTwips?: Twips;
455
- rightTwips?: Twips;
456
- firstLineTwips?: Twips;
457
- hangingTwips?: Twips;
458
- }
459
- export interface ParagraphSpacing {
460
- beforeTwips?: Twips;
461
- afterTwips?: Twips;
462
- line?: { rule: "auto" | "atLeast" | "exact"; valueTwips?: Twips; value240thLines?: number };
463
- beforeAutoSpacing?: boolean;
464
- afterAutoSpacing?: boolean;
465
- }
466
- export interface ParagraphNumbering {
467
- numId: string; // numbering instance id (w:numId) citeturn4search17
468
- ilvl: number; // 0..8 (w:ilvl) citeturn4search2
469
- }
470
- ```
471
-
472
- **Allowed children:** `InlineNode[]`.
473
-
474
- **Constraints:**
475
- - Paragraph MUST contain at least one inline node after normalization. If empty, insert a single `AnchorPlaceholderNode` (see inline nodes) to keep selection addressable.
476
- - `attrs.numbering.ilvl` MUST be integer 0..8.
477
- - If `attrs.numbering` is present, export MUST emit `w:pPr/w:numPr` with both `w:numId` and `w:ilvl`. citeturn4search6turn4search17turn4search2
478
-
479
- **WordprocessingML mapping:** `paragraph` maps to `w:p` with:
480
- - paragraph properties in `w:pPr` (alignment `w:jc`, indent `w:ind`, spacing `w:spacing`, numbering `w:numPr`, paragraph style `w:pStyle`). citeturn3search0turn5search0turn5search1turn5search2turn4search0turn4search6
481
- - run-level content (`w:r`, `w:hyperlink`, etc.) as children. citeturn3search0turn3search1turn1search2
482
-
483
- **Editing invariants:** Splitting a paragraph MUST produce two valid paragraphs, each normalized to contain at least one inline.
484
-
485
- #### Heading
486
-
487
- Headings are represented as a dedicated node to provide stable editor semantics. On export/import, headings map to paragraphs with heading semantics via styleId and/or outline-level conventions.
488
-
489
- ```ts
490
- export interface HeadingNode extends NodeBase {
491
- type: "heading";
492
- children: InlineNode[];
493
-
494
- attrs: ParagraphAttrs & {
495
- level: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9;
496
- };
497
- }
498
- ```
499
-
500
- **Allowed children:** `InlineNode[]`.
501
-
502
- **Constraints:**
503
- - `level` MUST be 1..9.
504
- - If `attrs.styleId` is absent at export, exporter MUST map `level` to `Heading1..Heading9` styleId by default (unless the styles catalog defines a different configured mapping).
505
- - If `attrs.styleId` is present, it MUST be preserved exactly; `level` is an editor semantic and MUST still export a `w:p` (not a special element) because Word stores headings as paragraph formatting (style/properties). citeturn3search0turn4search0
506
-
507
- **WordprocessingML mapping:** `heading` exports as `w:p` with `w:pPr/w:pStyle` set to the chosen heading style (or preserved). citeturn4search0
508
-
509
- **Editing invariants:** Converting between `paragraph` and `heading` MUST be a pure attribute/node-type change (content unchanged).
510
-
511
- #### Blockquote
512
-
513
- ```ts
514
- export interface BlockquoteNode extends NodeBase {
515
- type: "blockquote";
516
- children: BlockNode[];
517
-
518
- attrs: {
519
- // export strategy: apply quoteStyleId to contained paragraphs if present
520
- quoteStyleId?: string;
521
- // optional indentation applied when quoteStyleId absent
522
- indentTwips?: Twips;
523
-
524
- ooxmlUnknown?: PreservedXmlRef;
525
- };
526
- }
527
- ```
528
-
529
- **Allowed children:** `BlockNode[]` (MUST contain at least one block).
530
-
531
- **Constraints:**
532
- - Children MUST NOT include `SectionBreakNode` directly. If encountered, normalization hoists `SectionBreakNode` out of the blockquote boundary (see repair rules).
533
- - If `quoteStyleId` is set, exporter MUST apply it as `w:pStyle` to each contained paragraph/heading unless that child already has an explicit `styleId`. citeturn4search0
534
-
535
- **WordprocessingML mapping:** WordprocessingML has no native blockquote element; exporter MUST emit a sequence of `w:p`/`w:tbl` content in the body. Blockquote semantics are encoded via paragraph properties (style and/or indentation). citeturn3search0turn5search1turn4search0
536
-
537
- **Editing invariants:** Blockquote boundaries MUST be stable under paragraph edits inside; splitting/merging at the boundary MUST either expand or shrink blockquote deterministically (defined in normalization).
538
-
539
- #### Ordered list and bullet list
540
-
541
- Word uses numbering properties on paragraphs (`w:numPr`) referencing numbering definitions (`numId`, `ilvl`). citeturn4search6turn4search17turn4search2
542
-
543
- ```ts
544
- export interface OrderedListNode extends NodeBase {
545
- type: "orderedList";
546
- children: ListItemNode[];
547
-
548
- attrs: ListAttrs & {
549
- kind: "ordered";
550
- };
551
- }
552
-
553
- export interface BulletListNode extends NodeBase {
554
- type: "bulletList";
555
- children: ListItemNode[];
556
-
557
- attrs: ListAttrs & {
558
- kind: "bullet";
559
- };
560
- }
561
-
562
- export interface ListAttrs {
563
- // Word numbering instance id. Required for stable round-trip.
564
- numId: string;
565
-
566
- // Base level for this list (0..8). Nested lists add to this.
567
- baseIlvl: number;
568
-
569
- // Stable list restart semantics
570
- restart?: {
571
- atIndex: number; // list item index where restart starts (0-based)
572
- startValue: number;
573
- };
574
-
575
- // Preservation for advanced numbering not modeled
576
- ooxmlUnknown?: PreservedXmlRef;
577
- }
578
- ```
579
-
580
- **Allowed children:** `ListItemNode[]` (MUST contain ≥ 1 item).
581
-
582
- **Constraints:**
583
- - `attrs.baseIlvl` MUST be 0..8.
584
- - Each list item’s first block MUST be a `paragraph` or `heading`.
585
- - Export MUST produce `w:p` for each list paragraph with `w:numPr/w:numId` = `attrs.numId` and `w:ilvl` = `attrs.baseIlvl + nestingDepth`. citeturn4search6turn4search2turn4search17
586
-
587
- **WordprocessingML mapping:** A list is serialized as ordinary paragraphs bearing numbering properties. citeturn4search6turn4search17turn4search2
588
-
589
- **Editing invariants:**
590
- - Adjacent lists with identical `(kind, numId, baseIlvl)` MUST be merged by normalization.
591
- - Lifting a list item out MUST adjust numbering levels deterministically (see structural repair).
592
-
593
- #### List item
594
-
595
- ```ts
596
- export interface ListItemNode extends NodeBase {
597
- type: "listItem";
598
- children: BlockNode[];
599
-
600
- attrs: {
601
- // Optional override of ilvl for this item (rare; used for malformed imports)
602
- ilvlOverride?: number; // 0..8
603
- // Optional stable identity for Word paragraph rsid retention (preserved, not interpreted)
604
- ooxmlUnknown?: PreservedXmlRef;
605
- };
606
- }
607
- ```
608
-
609
- **Allowed children:** `BlockNode[]` (MUST contain ≥ 1).
610
-
611
- **Constraints:**
612
- - First child MUST be `paragraph` or `heading`.
613
- - A `listItem` MUST NOT contain a `SectionBreakNode`; normalization hoists it out.
614
-
615
- **WordprocessingML mapping:** Each `listItem` exports as one or more paragraphs/tables; the first paragraph carries the list numbering properties (`w:numPr`). citeturn4search6turn3search0
616
-
617
- **Editing invariants:** Adding/removing blocks inside a list item MUST not change list identity; only numbering props on affected paragraphs may change.
618
-
619
- #### Table
620
-
621
- Tables in WordprocessingML are represented by `<w:tbl>` with optional grid definitions (`<w:tblGrid>`). citeturn2search5turn4search7turn4search3
622
-
623
- ```ts
624
- export interface TableNode extends NodeBase {
625
- type: "table";
626
- children: TableRowNode[];
627
-
628
- attrs: TableProps;
629
- }
630
-
631
- export interface TableProps {
632
- styleId?: string; // table styleId (w:tblStyle)
633
- alignment?: "left" | "center" | "right"; // table alignment conceptually maps to w:jc at tblPr citeturn5search8
634
- widthTwips?: Twips;
635
-
636
- // Grid model for stable cell width semantics
637
- grid?: {
638
- colWidthsTwips: Twips[]; // length >= 1
639
- };
640
-
641
- // Preservation for tblPr/tblLook/tblBorders etc not modeled
642
- ooxmlUnknownTblPr?: PreservedXmlRef;
643
- }
644
- ```
645
-
646
- **Allowed children:** `TableRowNode[]` (MUST contain ≥ 1 row).
647
-
648
- **Constraints:**
649
- - If `attrs.grid` present, length MUST equal `max(columns referenced by cells after applying gridSpan)`.
650
- - Export MUST emit `<w:tbl>` and, if `grid` present, a `<w:tblGrid>` with `<w:gridCol>` entries matching widths. citeturn4search7turn4search3turn4search21
651
-
652
- **WordprocessingML mapping:** `<w:tbl>` container, `<w:tr>` rows, `<w:tc>` cells. citeturn2search5turn2search9
653
-
654
- **Editing invariants:** Table cell content edits MUST not alter the table grid unless explicitly performing a column/row operation.
655
-
656
- #### Table row
657
-
658
- ```ts
659
- export interface TableRowNode extends NodeBase {
660
- type: "tableRow";
661
- children: TableCellNode[];
662
-
663
- attrs: TableRowProps;
664
- }
665
-
666
- export interface TableRowProps {
667
- isHeader?: boolean;
668
- heightTwips?: Twips;
669
- // Tracked row insertion/deletion in Word may be represented separately; preserved here.
670
- ooxmlUnknownTrPr?: PreservedXmlRef;
671
- }
672
- ```
673
-
674
- **Allowed children:** `TableCellNode[]` (MUST contain ≥ 1 cell).
675
-
676
- **Constraints:** All rows in the same table MUST normalize to consistent column counts after considering `gridSpan`/`vMerge`.
677
-
678
- **WordprocessingML mapping:** `<w:tr>` with `<w:trPr>`; cells `<w:tc>`. citeturn2search9turn2search5
679
-
680
- **Editing invariants:** Row operations are structural steps; plain text edits inside cells do not affect row identity.
681
-
682
- #### Table cell
683
-
684
- WordprocessingML cell properties are stored in `<w:tcPr>` and override higher-level table/row properties on conflict. citeturn5search7
685
-
686
- ```ts
687
- export interface TableCellNode extends NodeBase {
688
- type: "tableCell";
689
- children: BlockNode[];
690
-
691
- attrs: TableCellProps;
692
- }
693
-
694
- export interface TableCellProps {
695
- gridSpan?: number; // >= 1, maps to w:gridSpan (colspan concept) citeturn5search15
696
- vMerge?: "restart" | "continue"; // maps to w:vMerge semantics citeturn5search11
697
-
698
- widthTwips?: Twips;
699
- shading?: { fill?: HexColorRGB };
700
-
701
- // Cell-level padding/borders not modeled must be preserved
702
- ooxmlUnknownTcPr?: PreservedXmlRef;
703
- }
704
- ```
705
-
706
- **Allowed children:** `BlockNode[]` (MUST contain ≥ 1).
707
-
708
- **Constraints:**
709
- - First child MUST be a `paragraph` or `heading` after normalization (Word requires block content in cells and commonly at least one paragraph).
710
- - If `vMerge="continue"`, the cell MUST be vertically merged with the cell above at same column position after normalization.
711
-
712
- **WordprocessingML mapping:** `<w:tc>` with `<w:tcPr>` including `w:gridSpan`, `w:vMerge`, widths, etc. citeturn5search7turn5search11
713
-
714
- **Editing invariants:** Deleting the last paragraph in a cell inserts an empty paragraph placeholder.
715
-
716
- #### Image block
717
-
718
- ```ts
719
- export interface ImageBlockNode extends NodeBase {
720
- type: "imageBlock";
721
- children: []; // leaf block (caption is separate paragraph/node)
722
-
723
- attrs: {
724
- mediaId: Id;
725
- altText?: string;
726
-
727
- // Layout hints
728
- widthTwips?: Twips;
729
- heightTwips?: Twips;
730
-
731
- // Default export is inline drawing inside its own paragraph
732
- exportAs: "ownParagraphInlineDrawing";
733
-
734
- ooxmlUnknown?: PreservedXmlRef;
735
- };
736
- }
737
- ```
738
-
739
- **Allowed children:** none.
740
-
741
- **Constraints:** `mediaId` MUST exist in `media.items`.
742
-
743
- **WordprocessingML mapping:** Exports as a paragraph containing a run with a DrawingML `w:drawing` and `wp:inline` referencing the image relationship. `wp:inline` indicates the object is positioned inline with text. citeturn2search10
744
-
745
- **Editing invariants:** Treated as an atomic block for selection and deletion.
746
-
747
- #### Horizontal rule
748
-
749
- ```ts
750
- export interface HorizontalRuleNode extends NodeBase {
751
- type: "horizontalRule";
752
- children: [];
753
-
754
- attrs: {
755
- // Export strategy uses paragraph borders (not shapes)
756
- thicknessTwips?: Twips;
757
- color?: HexColorRGB;
758
- spacingBeforeTwips?: Twips;
759
- spacingAfterTwips?: Twips;
760
-
761
- ooxmlUnknown?: PreservedXmlRef;
762
- };
763
- }
764
- ```
765
-
766
- **Allowed children:** none.
767
-
768
- **Constraints:** None.
769
-
770
- **WordprocessingML mapping:** Exports as an empty paragraph with `pPr` specifying border(s) (typically bottom border). Because OOXML does not define a “horizontal rule” element, this uses paragraph properties. citeturn3search0turn4search4
771
-
772
- **Editing invariants:** Atomic block.
773
-
774
- #### Section break placeholder
775
-
776
- Sections in WordprocessingML store section properties in a `<w:sectPr>` element. For all sections except the last, the `sectPr` is stored as a child of the last paragraph of the section. citeturn0search15turn0search3
777
-
778
- ```ts
779
- export interface SectionBreakNode extends NodeBase {
780
- type: "sectionBreak";
781
- children: [];
782
-
783
- attrs: {
784
- // Reference to preserved or generated sectPr XML
785
- sectPr: SectionPropsRef;
786
-
787
- // Break type (nextPage, continuous, etc.)
788
- kind: "nextPage" | "continuous" | "column";
789
-
790
- ooxmlUnknown?: PreservedXmlRef;
791
- };
792
- }
793
-
794
- export interface SectionPropsRef {
795
- mode: "preservedXml" | "generated";
796
- preservedFragmentId?: Id; // references preservation.fragments
797
- generated?: {
798
- // Minimal modeled section props (optional; everything else preserved)
799
- pageSize?: { widthTwips: Twips; heightTwips: Twips };
800
- margins?: { topTwips: Twips; rightTwips: Twips; bottomTwips: Twips; leftTwips: Twips };
801
- };
802
- }
803
- ```
804
-
805
- **Allowed children:** none.
806
-
807
- **Constraints:**
808
- - `sectPr.mode="preservedXml"` requires `preservedFragmentId`.
809
- - Export MUST place section property markup in the correct OOXML location: associated with a paragraph boundary (the paragraph ending the section). citeturn0search15turn0search3
810
- - If the CDS contains `SectionBreakNode` between blocks, exporter MUST ensure there is a paragraph to carry the `<w:sectPr>` if required by the chosen break kind.
811
-
812
- **Editing invariants:** Treated as atomic. Insert/remove must update section association deterministically (defined in export algorithm notes under preservation).
813
-
814
- #### Opaque preserved block node
815
-
816
- ```ts
817
- export interface OoxmlBlockNode extends NodeBase {
818
- type: "ooxmlBlock";
819
- children: [];
820
-
821
- attrs: {
822
- fragmentId: Id; // references preservation.fragments
823
- editability: "locked"; // always locked in this schema version
824
- description?: string;
825
- };
826
- }
827
- ```
828
-
829
- **Purpose:** Represents unsupported/unmodeled block-level OOXML content in the main body (e.g., complex content controls, altChunk, etc.) preserved for round-trip. Markup Compatibility constructs must be preserved when not supported. citeturn2search3turn2search7
830
-
831
- **Constraints:** `fragmentId` MUST exist; exporter MUST re-inject without modification.
832
-
833
- ### Inline node specifications
834
-
835
- Inline nodes occur inside paragraphs/headings.
836
-
837
- ```ts
838
- export type InlineNode =
839
- | TextNode
840
- | HardBreakNode
841
- | HyperlinkNode
842
- | InlineImageNode
843
- | AnchorPlaceholderNode
844
- | OoxmlInlineNode;
845
- ```
846
-
847
- #### Text
848
-
849
- ```ts
850
- export interface TextNode extends NodeBase {
851
- type: "text";
852
- text: string; // Unicode string (may include spaces)
853
- marks: Mark[]; // canonical sorted
854
- attrs?: {
855
- // If true, exporter MUST preserve whitespace equivalently to xml:space="preserve" where needed
856
- preserveWhiteSpace?: boolean;
857
- };
858
- }
859
- ```
860
-
861
- **Constraints:**
862
- - `text` MUST be non-empty after normalization (empty text nodes forbidden).
863
- - Adjacent text nodes with identical `marks` and identical `attrs.preserveWhiteSpace` MUST be merged.
864
- - Export MUST handle whitespace correctly within `w:t`, using XML whitespace preservation rules (`xml:space="preserve"`) when needed. WordprocessingML text handling depends on XML space preservation semantics. citeturn3search9turn12search11
865
-
866
- **WordprocessingML mapping:** `text` maps to `<w:r><w:t>...</w:t></w:r>` with `w:rPr` derived from marks. `w:r` is the container for run content. citeturn3search1turn7search11
867
-
868
- #### Hard break
869
-
870
- ```ts
871
- export interface HardBreakNode extends NodeBase {
872
- type: "hardBreak";
873
- attrs: {
874
- // Word supports variants; default is line break
875
- break: "line";
876
- };
877
- }
878
- ```
879
-
880
- **WordprocessingML mapping:** `<w:br/>` in run content. A break overrides normal line breaking. citeturn3search3turn3search17
881
-
882
- #### Hyperlink
883
-
884
- Word hyperlinks are represented by `<w:hyperlink>` with either a relationship id (`r:id`) for external targets or an `anchor` for internal targets; if `id` exists it supersedes `anchor`. citeturn1search2
885
-
886
- ```ts
887
- export interface HyperlinkNode extends NodeBase {
888
- type: "hyperlink";
889
- children: InlineNode[]; // typically text + marks
890
-
891
- attrs: {
892
- href?: string; // external target
893
- relationshipId?: RelationshipId; // optional; if absent, exporter generates a relationship
894
- anchor?: string; // internal target bookmark name
895
- history?: boolean;
896
- tooltip?: string;
897
- targetFrame?: string;
898
-
899
- // Optional explicit character style for hyperlink runs
900
- characterStyleId?: string; // maps to run style w:rStyle in contained runs citeturn4search5
901
-
902
- ooxmlUnknown?: PreservedXmlRef;
903
- };
904
- }
905
- ```
906
-
907
- **Constraints:**
908
- - Must contain at least one inline child after normalization (if empty, insert a single `TextNode` with `" "`).
909
- - If `relationshipId` present, exporter MUST create/update a relationship with that id in the main document relationships part for the hyperlink target.
910
- - If both `href` and `anchor` set, exporter MUST prefer external relationship semantics (consistent with OOXML “id supersedes anchor”). citeturn1search2
911
-
912
- **WordprocessingML mapping:** `<w:hyperlink r:id="..."> ... </w:hyperlink>`. citeturn1search2
913
-
914
- #### Inline image
915
-
916
- ```ts
917
- export interface InlineImageNode extends NodeBase {
918
- type: "inlineImage";
919
- children: []; // leaf inline atom
920
-
921
- attrs: {
922
- mediaId: Id;
923
- altText?: string;
924
-
925
- widthTwips?: Twips;
926
- heightTwips?: Twips;
927
-
928
- // Export uses DrawingML inline
929
- drawing: "wp:inline";
930
-
931
- ooxmlUnknown?: PreservedXmlRef;
932
- };
933
- }
934
- ```
935
-
936
- **Constraints:** `mediaId` MUST exist.
937
-
938
- **WordprocessingML mapping:** Run with `w:drawing/wp:inline` referencing an embedded relationship to the image part. `wp:inline` indicates in-line positioning. citeturn2search10turn2search14
939
-
940
- #### Anchor placeholder
941
-
942
- This is a **zero-width addressable inline atom** used to ensure valid selections and to provide stable “point anchors” in cases where empty paragraphs/cells must remain selectable.
943
-
944
- ```ts
945
- export interface AnchorPlaceholderNode extends NodeBase {
946
- type: "anchor";
947
- children: [];
948
-
949
- attrs: {
950
- role: "selection" | "emptyParagraph" | "emptyCell";
951
- };
952
- }
953
- ```
954
-
955
- **Export behavior:** Exporter MUST NOT emit any visible glyph. It may emit an empty run (`<w:r/>`) if necessary to satisfy Word’s XML constraints for empty paragraphs.
956
-
957
- #### Opaque preserved inline node
958
-
959
- ```ts
960
- export interface OoxmlInlineNode extends NodeBase {
961
- type: "ooxmlInline";
962
- children: [];
963
-
964
- attrs: {
965
- fragmentId: Id;
966
- editability: "locked";
967
- description?: string;
968
- };
969
- }
970
- ```
971
-
972
- Used for unsupported inline OOXML (complex fields, content controls inline, markup-compat alternate branches, etc.). Markup compatibility preservation applies. citeturn2search3turn2search7
973
-
974
- ### Marks (inline formatting)
975
-
976
- Run properties in Word are encoded using `<w:rPr>` elements inside runs. citeturn7search11turn8search3turn8search1turn8search2
977
-
978
- ```ts
979
- export type Mark =
980
- | BoldMark
981
- | ItalicMark
982
- | UnderlineMark
983
- | StrikeMark
984
- | CodeMark
985
- | SubscriptMark
986
- | SuperscriptMark
987
- | TextStyleMark;
988
-
989
- export interface MarkBase {
990
- type: string;
991
- attrs?: Record<string, unknown>;
992
- }
993
- ```
994
-
995
- #### Bold
996
-
997
- ```ts
998
- export interface BoldMark extends MarkBase {
999
- type: "bold";
1000
- }
1001
- ```
1002
-
1003
- **WordprocessingML mapping:** `<w:b/>` toggle. citeturn7search0turn7search7
1004
-
1005
- #### Italic
1006
-
1007
- ```ts
1008
- export interface ItalicMark extends MarkBase {
1009
- type: "italic";
1010
- }
1011
- ```
1012
-
1013
- **WordprocessingML mapping:** `<w:i/>`. citeturn7search8turn7search11
1014
-
1015
- #### Underline
1016
-
1017
- ```ts
1018
- export interface UnderlineMark extends MarkBase {
1019
- type: "underline";
1020
- attrs: {
1021
- style: "single" | "double" | "dotted" | "dash" | "wave" | "none";
1022
- };
1023
- }
1024
- ```
1025
-
1026
- **WordprocessingML mapping:** `<w:u w:val="..."/>`, where `val` is an underline style enumeration and `none` is equivalent to no underline. citeturn7search2turn7search20
1027
-
1028
- #### Strike
1029
-
1030
- ```ts
1031
- export interface StrikeMark extends MarkBase {
1032
- type: "strike";
1033
- }
1034
- ```
1035
-
1036
- **WordprocessingML mapping:** `<w:strike/>` toggle. citeturn8search0turn8search4
1037
-
1038
- #### Code
1039
-
1040
- ```ts
1041
- export interface CodeMark extends MarkBase {
1042
- type: "code";
1043
- attrs?: {
1044
- // If set, reference a character style intended for code formatting.
1045
- characterStyleId?: string;
1046
- };
1047
- }
1048
- ```
1049
-
1050
- **WordprocessingML mapping (deterministic):**
1051
- - If `characterStyleId` provided, exporter MUST emit `w:rPr/w:rStyle` for runs in the code span. citeturn4search5turn2search4
1052
- - Otherwise exporter MUST emit a monospace font via `w:rPr/w:rFonts` for ASCII + hAnsi at minimum, preserving any existing font theme attributes if present. `w:rFonts` controls fonts for Unicode subsets. citeturn8search3turn8search7turn8search15
1053
-
1054
- #### Subscript and superscript
1055
-
1056
- ```ts
1057
- export interface SubscriptMark extends MarkBase {
1058
- type: "subscript";
1059
- }
1060
- export interface SuperscriptMark extends MarkBase {
1061
- type: "superscript";
1062
- }
1063
- ```
1064
-
1065
- **WordprocessingML mapping:** `<w:vertAlign w:val="subscript|superscript"/>`. citeturn7search3turn7search10
1066
-
1067
- #### Text style (font, color, size, highlight)
1068
-
1069
- ```ts
1070
- export interface TextStyleMark extends MarkBase {
1071
- type: "textStyle";
1072
- attrs: {
1073
- // Font: either explicit family or theme-based; exporter must preserve theme attributes if present.
1074
- font?: {
1075
- ascii?: string;
1076
- hAnsi?: string;
1077
- eastAsia?: string;
1078
- cs?: string;
1079
- asciiTheme?: string;
1080
- hAnsiTheme?: string;
1081
- eastAsiaTheme?: string;
1082
- csTheme?: string;
1083
- };
1084
-
1085
- // Color: explicit RGB or "auto"
1086
- color?: { val: HexColorRGB | "auto" };
1087
-
1088
- // Font size in half-points, per OOXML sz semantics
1089
- size?: { halfPoints: HalfPoints };
1090
-
1091
- // Highlight
1092
- highlight?: { val: string };
1093
-
1094
- // Preservation
1095
- ooxmlUnknown?: PreservedXmlRef;
1096
- };
1097
- }
1098
- ```
1099
-
1100
- **WordprocessingML mapping:**
1101
- - Font via `w:rFonts` (font selection per Unicode range). citeturn8search3turn8search7turn8search15
1102
- - Color via `w:color`. citeturn8search1turn8search13
1103
- - Size via `w:sz` half-point values. citeturn8search2turn8search14
1104
- - Highlight via `w:highlight`. citeturn8search5
1105
-
1106
- ### Mark stacking, exclusivity, and normalization
1107
-
1108
- **Rule M1 (Canonical mark order):** Marks on a `TextNode` MUST be sorted by this stable order:
1109
- `bold`, `italic`, `underline`, `strike`, `code`, `subscript`, `superscript`, `textStyle`.
1110
-
1111
- **Rule M2 (Exclusivity):**
1112
- - `subscript` and `superscript` are mutually exclusive. If both appear, normalization MUST keep the later-applied mark and drop the other, emitting a warning diagnostic.
1113
-
1114
- **Rule M3 (Merge adjacent text):**
1115
- Two adjacent `TextNode`s MUST be merged if:
1116
- - their mark arrays are deeply equal,
1117
- - their `attrs.preserveWhiteSpace` values are equal.
1118
-
1119
- **Rule M4 (Empty text forbidden):**
1120
- Any `TextNode` with `text.length === 0` MUST be removed. If that would leave a paragraph empty, insert an `AnchorPlaceholderNode`.
1121
-
1122
- ## Structural rules and normalization
1123
-
1124
- ### Allowed nesting graph (complete)
1125
-
1126
- The allowed parent → child relationships are:
1127
-
1128
- - `doc` → any `BlockNode`.
1129
- - `paragraph` / `heading` → any `InlineNode`.
1130
- - `blockquote` → any `BlockNode` except `SectionBreakNode` (normalized by hoisting).
1131
- - `orderedList` / `bulletList` → `listItem`.
1132
- - `listItem` → any `BlockNode` except `SectionBreakNode` (hoisted).
1133
- - `table` → `tableRow`.
1134
- - `tableRow` → `tableCell`.
1135
- - `tableCell` → any `BlockNode` (but normalized to contain at least one paragraph/heading).
1136
- - `ooxmlBlock` → no children.
1137
- - `hyperlink` → `InlineNode` (but not `hyperlink` nested inside `hyperlink`; normalization unwraps inner link).
1138
- - `inlineImage`, `hardBreak`, `anchor`, `ooxmlInline` → no children.
1139
-
1140
- ### Illegal structures (and deterministic repairs)
1141
-
1142
- The engine MUST run normalization after:
1143
- - import,
1144
- - each transaction,
1145
- - before export.
1146
-
1147
- Normalization MUST be deterministic and idempotent.
1148
-
1149
- #### Repair rules
1150
-
1151
- **Repair R1 (Doc root type):** If `content.type !== "doc"`, reject as fatal validation error.
1152
-
1153
- **Repair R2 (Paragraph empty):** If `paragraph.children.length === 0`, set to `[ {type:"anchor", role:"emptyParagraph"} ]`.
1154
-
1155
- **Repair R3 (Table cell empty):** If `tableCell.children.length === 0`, insert a single empty paragraph containing an anchor placeholder.
1156
-
1157
- **Repair R4 (List item first block):** If first child of `listItem` is not `paragraph|heading`, insert an empty paragraph at index 0.
1158
-
1159
- **Repair R5 (Nested hyperlink):** If a `hyperlink` contains a descendant `hyperlink`, normalization MUST unwrap the inner hyperlink by replacing it with its children.
1160
-
1161
- **Repair R6 (Section break inside containers):** If `SectionBreakNode` appears within `blockquote` or `listItem`, hoist it to the nearest ancestor that is a direct child of `doc`. Hoisting preserves relative order by inserting immediately after the container block that contained it.
1162
-
1163
- **Repair R7 (Adjacent lists):** Adjacent lists of same `kind|numId|baseIlvl` MUST be merged.
1164
-
1165
- **Repair R8 (Marks):** Enforce M1–M4; emit diagnostics on conflicts.
1166
-
1167
- ### Explicit examples of valid vs invalid structures
1168
-
1169
- #### Valid
1170
-
1171
- ```json
1172
- {
1173
- "type": "paragraph",
1174
- "children": [
1175
- { "type": "text", "text": "Hello", "marks": [], "id": "t1" },
1176
- { "type": "hardBreak", "attrs": { "break": "line" }, "id": "br1" },
1177
- { "type": "text", "text": "World", "marks": [{ "type": "bold" }], "id": "t2" }
1178
- ],
1179
- "attrs": {},
1180
- "id": "p1"
1181
- }
1182
- ```
1183
-
1184
- #### Invalid → corrected (empty cell)
1185
-
1186
- Invalid:
1187
-
1188
- ```json
1189
- { "type": "tableCell", "children": [], "attrs": {}, "id": "c1" }
1190
- ```
1191
-
1192
- Corrected by R3:
1193
-
1194
- ```json
1195
- {
1196
- "type": "tableCell",
1197
- "children": [
1198
- {
1199
- "type": "paragraph",
1200
- "children": [{ "type": "anchor", "children": [], "attrs": { "role": "emptyCell" }, "id": "a1" }],
1201
- "attrs": {},
1202
- "id": "p1"
1203
- }
1204
- ],
1205
- "attrs": {},
1206
- "id": "c1"
1207
- }
1208
- ```
1209
-
1210
- ## Positions, ranges, selections, and transactions
1211
-
1212
- ### Global position indexing model
1213
-
1214
- CDS uses a **single integer-based coordinate space** over the content tree, based on tokenized traversal rules (ProseMirror-style linear positions). This avoids path fragility and supports deterministic mapping across edits. Positions are similar to the documented concept where entering/leaving non-leaf nodes counts as tokens and each character counts as one token. citeturn9search4
1215
-
1216
- #### Node size rules
1217
-
1218
- Let `size(node)` be defined:
1219
-
1220
- - For `TextNode`: `size = numberOfUnicodeCodePoints(text)`.
1221
- - For inline leaf atom nodes (`hardBreak`, `inlineImage`, `anchor`, `ooxmlInline`): `size = 1`.
1222
- - For block leaf nodes (`imageBlock`, `horizontalRule`, `sectionBreak`, `ooxmlBlock`): `size = 1`.
1223
- - For container nodes (all others):
1224
- `size = 2 + sum(size(child) for child in children)`
1225
- where the `2` accounts for the container’s **start** and **end** tokens.
1226
-
1227
- This is consistent with the notion that entering/leaving a node counts as tokens and leaf nodes count as a single token. citeturn9search4turn9search19
1228
-
1229
- #### Position domain
1230
-
1231
- ```ts
1232
- export type DocPos = number; // integer
1233
-
1234
- export interface DocRange {
1235
- from: DocPos; // inclusive
1236
- to: DocPos; // exclusive, must satisfy from <= to
1237
- }
1238
- ```
1239
-
1240
- **Invariant PZ1:** `0 <= from <= to <= size(doc)`.
1241
-
1242
- ### Selections
1243
-
1244
- ```ts
1245
- export type Selection =
1246
- | TextSelection
1247
- | NodeSelection
1248
- | BlockSelection;
1249
-
1250
- export interface TextSelection {
1251
- kind: "text";
1252
- range: DocRange;
1253
- // assoc controls stickiness when mapping through insertions at boundaries
1254
- assoc: { start: -1 | 1; end: -1 | 1 };
1255
- }
1256
-
1257
- export interface NodeSelection {
1258
- kind: "node";
1259
- // selects exactly one atomic node token (leaf: size 1)
1260
- at: DocPos;
1261
- }
1262
-
1263
- export interface BlockSelection {
1264
- kind: "block";
1265
- // selects whole blocks by spanning their token ranges
1266
- range: DocRange;
1267
- }
1268
- ```
1269
-
1270
- **Collapsed vs expanded:**
1271
- - Collapsed selection: `range.from === range.to`.
1272
- - Expanded selection: `range.from < range.to`.
1273
-
1274
- ### Transactions and steps
1275
-
1276
- Transactions are the only permitted mutation mechanism.
1277
-
1278
- ```ts
1279
- export interface Transaction {
1280
- txId: UUID;
1281
- createdAt: ISO8601DateTime;
1282
- actorId: Id;
1283
-
1284
- // Steps applied in order
1285
- steps: Step[];
1286
-
1287
- // Selection before/after (optional but recommended)
1288
- selectionBefore?: Selection;
1289
- selectionAfter?: Selection;
1290
-
1291
- // Arbitrary metadata
1292
- meta?: Record<string, unknown>;
1293
- }
1294
- ```
1295
-
1296
- #### Steps
1297
-
1298
- ```ts
1299
- export type Step =
1300
- | ReplaceStep
1301
- | AddMarkStep
1302
- | RemoveMarkStep
1303
- | SetNodeAttrsStep;
1304
-
1305
- export interface ReplaceStep {
1306
- type: "replace";
1307
- from: DocPos;
1308
- to: DocPos;
1309
- slice: Slice; // content fragment to insert
1310
- }
1311
-
1312
- export interface AddMarkStep {
1313
- type: "addMark";
1314
- from: DocPos;
1315
- to: DocPos;
1316
- mark: Mark;
1317
- }
1318
-
1319
- export interface RemoveMarkStep {
1320
- type: "removeMark";
1321
- from: DocPos;
1322
- to: DocPos;
1323
- markType: Mark["type"];
1324
- }
1325
-
1326
- export interface SetNodeAttrsStep {
1327
- type: "setNodeAttrs";
1328
- // position that resolves to a node boundary (start token of node)
1329
- at: DocPos;
1330
- // shallow partial patch (implementation must validate types)
1331
- patch: Record<string, unknown>;
1332
- }
1333
- ```
1334
-
1335
- **Slice definition:**
1336
-
1337
- ```ts
1338
- export interface Slice {
1339
- // A slice is a fragment of block or inline content with open depth metadata.
1340
- // For this CDS version, slices are always "closed" (openStart=openEnd=0).
1341
- openStart: 0;
1342
- openEnd: 0;
1343
-
1344
- // Content to insert
1345
- content: Node[]; // must be valid at insertion point post-normalization
1346
- }
1347
- ```
1348
-
1349
- ### Mapping model
1350
-
1351
- Every step produces a `StepMap` that maps old positions to new positions.
1352
-
1353
- ```ts
1354
- export interface StepMap {
1355
- // A list of [start, oldSize, newSize] triples describing replacements
1356
- ranges: Array<[number, number, number]>;
1357
- }
1358
-
1359
- export interface Mapping {
1360
- maps: StepMap[];
1361
- }
1362
- ```
1363
-
1364
- **Mapping behavior:** Applying a step shifts positions after the replaced range by `(newSize - oldSize)`. This aligns with the well-established “step provides a change map mapping positions” pattern. citeturn6search0turn9search4
1365
-
1366
- **Mapping function (normative):**
1367
-
1368
- ```ts
1369
- export function mapPos(pos: DocPos, map: StepMap, assoc: -1|1): DocPos;
1370
- export function mapRange(range: DocRange, mapping: Mapping, assoc: {start:-1|1, end:-1|1}): DocRange;
1371
- ```
1372
-
1373
- Normative rules:
1374
-
1375
- - For each `[start, oldSize, newSize]`:
1376
- - Let `end = start + oldSize`.
1377
- - If `pos < start`: unchanged.
1378
- - If `pos > end`: `pos += (newSize - oldSize)`.
1379
- - If `start <= pos <= end` (inside replaced region):
1380
- - If `assoc === -1`, map to `start`.
1381
- - If `assoc === 1`, map to `start + newSize`.
1382
-
1383
- After applying all ranges in order, return final `pos`.
1384
-
1385
- **Transaction guarantees:**
1386
- - After applying all steps and normalization, the engine MUST remap:
1387
- - `selectionAfter` if omitted (derive by mapping `selectionBefore`),
1388
- - all comment anchors and revision targets (rules below),
1389
- - preservation bindings (rules below),
1390
- - and MUST ensure invariants hold or emit diagnostics.
1391
-
1392
- ## Annotations: comments and tracked changes
1393
-
1394
- ### Comment anchor model
1395
-
1396
- Word comments consist of:
1397
- - a comment body stored in the comments part, and
1398
- - anchor/range markup in the main document linking by id. citeturn0search4turn0search8
1399
-
1400
- A comment not referenced by content via a matching id may be ignored by consumers, so linkage integrity is mandatory on export. citeturn0search4
1401
-
1402
- #### Data structures
1403
-
1404
- ```ts
1405
- export interface CommentStore {
1406
- threads: Record<Id, CommentThread>; // key = threadId
1407
- comments: Record<Id, Comment>; // key = commentId
1408
- }
1409
-
1410
- export interface CommentThread {
1411
- threadId: Id;
1412
-
1413
- // Anchor for the thread in the main document
1414
- anchor: CommentAnchor;
1415
-
1416
- // Ordered list of commentIds (first is root comment)
1417
- commentIds: Id[];
1418
-
1419
- // Status
1420
- resolved?: boolean;
1421
- resolvedAt?: ISO8601DateTime;
1422
- resolvedBy?: Id;
1423
-
1424
- // Import/export fidelity
1425
- ooxmlCommentId?: number; // Word w:id (integer)
1426
- }
1427
-
1428
- export interface Comment {
1429
- commentId: Id;
1430
- threadId: Id;
1431
- authorId: Id;
1432
- createdAt: ISO8601DateTime;
1433
-
1434
- // Rich content stored as a mini-doc fragment
1435
- body: CommentBody;
1436
-
1437
- // Threading
1438
- parentCommentId?: Id;
1439
-
1440
- // Optional edits
1441
- editedAt?: ISO8601DateTime;
1442
-
1443
- // OOXML preservation
1444
- ooxmlUnknown?: PreservedXmlRef;
1445
- }
1446
-
1447
- export interface CommentBody {
1448
- blocks: BlockNode[]; // same block model; must normalize to valid mini-doc
1449
- }
1450
- ```
1451
-
1452
- #### Anchor representation
1453
-
1454
- ```ts
1455
- export type CommentAnchor =
1456
- | RangeAnchor
1457
- | NodeAnchor
1458
- | OrphanAnchor;
1459
-
1460
- export interface RangeAnchor {
1461
- kind: "range";
1462
- range: DocRange;
1463
- assoc: { start: -1 | 1; end: -1 | 1 };
1464
-
1465
- // Context used for degraded reattachment
1466
- quote?: AnchorQuote;
1467
- }
1468
-
1469
- export interface NodeAnchor {
1470
- kind: "node";
1471
- // Selects an atomic node token, such as imageBlock/table/ooxmlBlock
1472
- at: DocPos;
1473
- assoc: -1 | 1;
1474
- quote?: AnchorQuote;
1475
- }
1476
-
1477
- export interface OrphanAnchor {
1478
- kind: "orphan";
1479
- lastKnownRange: DocRange;
1480
- orphanedAt: ISO8601DateTime;
1481
- reason: "deleted" | "invalidatedByStructureChange" | "importAmbiguity";
1482
- quote?: AnchorQuote;
1483
- }
1484
-
1485
- export interface AnchorQuote {
1486
- selectedText?: string; // up to 64 Unicode code points
1487
- prefix?: string; // up to 32 code points before
1488
- suffix?: string; // up to 32 code points after
1489
- }
1490
- ```
1491
-
1492
- #### Anchor survival rules under edits
1493
-
1494
- **Rule C1 (Primary remap):** On every transaction, if anchor is `range` or `node`, remap positions using the transaction’s `Mapping`.
1495
-
1496
- **Rule C2 (Deletion collapse):** If a `RangeAnchor` maps to `range.from === range.to` and the pre-map range length was > 0, mark it as **degraded**:
1497
- - keep as `RangeAnchor` but set `quote.selectedText` unchanged,
1498
- - and add a warning diagnostic, unless content-based reattachment succeeds.
1499
-
1500
- **Rule C3 (Orphaning):** If mapping produces out-of-bounds, or if the anchor points into a region that no longer corresponds to valid selectable content (e.g., removed node), convert to `OrphanAnchor`.
1501
-
1502
- **Rule C4 (Content-based reattachment):** If anchor becomes degraded/orphaned and has a `quote.selectedText`, attempt deterministic reattachment:
1503
- - search for exact occurrences of `selectedText` within ±1000 positions of `lastKnownRange.from` in the document’s extracted plain text,
1504
- - filter by matching `prefix` and `suffix` when present,
1505
- - choose the match with smallest distance; tie-break by lowest position,
1506
- - if exactly one winner, re-anchor to that range; else remain orphaned.
1507
-
1508
- **Rule C5 (Block targets):** To anchor to blocks (tables/images), use `NodeAnchor` selecting the atomic leaf token that represents the block (size 1).
1509
-
1510
- #### WordprocessingML export expectations for comments
1511
-
1512
- Exporter MUST generate:
1513
-
1514
- - `commentRangeStart` at the mapped `from`,
1515
- - `commentRangeEnd` at the mapped `to`,
1516
- - a `commentReference` run linked by the same id, as required to create a link to a comment. citeturn0search0turn0search8
1517
- - a corresponding `<w:comment w:id="...">...</w:comment>` in the comments part. citeturn0search4
1518
-
1519
- ### Revision (tracked changes) model
1520
-
1521
- Word’s track revisions setting indicates that revisions are recorded so they can be viewed/accepted/reverted; OOXML defines specific revision markup syntax. citeturn10search3turn10search5
1522
-
1523
- #### Core structures
1524
-
1525
- ```ts
1526
- export interface RevisionStore {
1527
- trackRevisions: boolean; // maps to settings.xml w:trackRevisions citeturn10search3turn10search13
1528
-
1529
- items: Record<Id, Revision>; // key = revisionId
1530
- }
1531
-
1532
- export type Revision =
1533
- | InsertionRevision
1534
- | DeletionRevision
1535
- | FormatRevision
1536
- | MoveRevision;
1537
-
1538
- export interface RevisionBase {
1539
- revisionId: Id;
1540
- authorId: Id;
1541
- createdAt: ISO8601DateTime;
1542
-
1543
- state: "active" | "accepted" | "rejected";
1544
-
1545
- // Word revision identifier (w:id). Required for stable docx export.
1546
- ooxmlRevisionId?: number;
1547
-
1548
- // Optional preservation
1549
- ooxmlUnknown?: PreservedXmlRef;
1550
- }
1551
- ```
1552
-
1553
- #### Insertion
1554
-
1555
- Inserted run content is represented using `w:ins` containers in WordprocessingML. citeturn1search0turn11search19
1556
-
1557
- ```ts
1558
- export interface InsertionRevision extends RevisionBase {
1559
- kind: "insertion";
1560
- range: DocRange;
1561
- assoc: { start: -1 | 1; end: -1 | 1 };
1562
- }
1563
- ```
1564
-
1565
- **Invariant R-INS1:** `range.from < range.to` when `state="active"`.
1566
-
1567
- #### Deletion
1568
-
1569
- Deleted run content is represented by `w:del` containing the deleted content (often using `w:delText`). citeturn1search1turn1search5turn10search6
1570
-
1571
- ```ts
1572
- export interface DeletionRevision extends RevisionBase {
1573
- kind: "deletion";
1574
-
1575
- // Point where deletion occurred in the *current* content
1576
- at: DocPos;
1577
- assoc: -1 | 1;
1578
-
1579
- // Payload is the deleted content captured at deletion time
1580
- deletedSlice: Slice;
1581
-
1582
- // Optional original range (for UI); may be used for reattachment
1583
- originalRange?: DocRange;
1584
- quote?: AnchorQuote;
1585
- }
1586
- ```
1587
-
1588
- **Invariant R-DEL1:** `deletedSlice.content.length > 0` when `state="active"`.
1589
-
1590
- #### Formatting revisions
1591
-
1592
- Word tracks some formatting changes via `pPrChange` and `rPrChange`, which store the previous property set and revision metadata. citeturn11search1turn11search0
1593
-
1594
- ```ts
1595
- export interface FormatRevision extends RevisionBase {
1596
- kind: "format";
1597
-
1598
- scope: "run" | "paragraph";
1599
- range: DocRange;
1600
-
1601
- // Canonical before/after representation of formatting
1602
- before: { marks?: Mark[]; paragraphAttrs?: Partial<ParagraphAttrs> };
1603
- after: { marks?: Mark[]; paragraphAttrs?: Partial<ParagraphAttrs> };
1604
- }
1605
- ```
1606
-
1607
- #### Move operations (model-aware, optional)
1608
-
1609
- Word supports move tracking with move-range markers and move containers. citeturn11search6turn11search3turn11search11turn11search7
1610
-
1611
- ```ts
1612
- export interface MoveRevision extends RevisionBase {
1613
- kind: "move";
1614
-
1615
- fromRange: DocRange;
1616
- toRange: DocRange;
1617
-
1618
- // Optional payload snapshot for robustness
1619
- movedSlice?: Slice;
1620
- }
1621
- ```
1622
-
1623
- ### Revision representation without corrupting structure
1624
-
1625
- **Rule TR1 (No structural wrappers):** Revisions MUST NOT wrap content nodes in the CDS tree. They are separate records referencing positions/ranges.
1626
-
1627
- **Rule TR2 (Export materialization):**
1628
- - Active insertions MUST export as `w:ins` containers around run-level content. citeturn1search0
1629
- - Active deletions MUST export as `w:del` containers with deleted run content using `w:delText` where appropriate. citeturn1search1turn1search5
1630
- - Paragraph mark deletions MUST export using the correct WordprocessingML form for deleted paragraphs/paragraph marks. WordprocessingML defines deleted paragraph semantics. citeturn10search0
1631
-
1632
- ### Overlaps, nesting, and invariants
1633
-
1634
- **Invariant TR-O1 (No conflicting active insertion overlap):** Two active insertion revisions MUST NOT overlap in position space. If a new insertion overlaps, normalization MUST split/merge deterministically:
1635
- - If same author and createdAt within 5 minutes and insertion is adjacent/overlapping due to mapping, merge into one range.
1636
- - Otherwise split such that ranges are disjoint and preserve order.
1637
-
1638
- **Invariant TR-O2 (Deletion anchored at a point):** Deletions are point-anchored and can coexist with insertions, but exporter must order emitted OOXML deterministically: deletions first at a position, then insertions.
1639
-
1640
- **Invariant TR-O3 (Format revisions):** Format revisions may overlap insertions/deletions, but export must emit `rPrChange/pPrChange` only for scope-specific changes, preserving OOXML constraints. citeturn11search0turn11search1
1641
-
1642
- ### Accept/reject rules (exact)
1643
-
1644
- Let `rev` be a revision.
1645
-
1646
- #### Accept insertion
1647
-
1648
- - Preconditions: `rev.kind="insertion"`.
1649
- - Effect:
1650
- 1. Remove `rev` from `revisions.items`.
1651
- 2. Content remains unchanged (inserted text becomes “normal”).
1652
- 3. Emit transaction steps:
1653
- - none on content,
1654
- - a revision-store removal event (implementation-defined but must be atomic with document update).
1655
-
1656
- #### Reject insertion
1657
-
1658
- - Preconditions: `rev.kind="insertion"`.
1659
- - Effect:
1660
- 1. Delete the content in `rev.range` from the document using a `ReplaceStep(from, to, emptySlice)`.
1661
- 2. Remove `rev` from revisions.
1662
- 3. Remap all other anchors through the produced mapping.
1663
-
1664
- #### Accept deletion
1665
-
1666
- - Preconditions: `rev.kind="deletion"`.
1667
- - Effect:
1668
- 1. Remove `rev` from revisions.
1669
- 2. Content remains unchanged (deleted content stays deleted).
1670
-
1671
- #### Reject deletion
1672
-
1673
- - Preconditions: `rev.kind="deletion"`.
1674
- - Effect:
1675
- 1. Insert `rev.deletedSlice` at `rev.at` using `ReplaceStep(at, at, deletedSlice)`.
1676
- 2. Remove `rev`.
1677
- 3. Remap all anchors.
1678
-
1679
- #### Merge revisions
1680
-
1681
- Two active revisions `A` and `B` of same kind MAY merge only if:
1682
- - same authorId,
1683
- - same kind,
1684
- - contiguous after mapping (`A.to === B.from` for insertions; `A.at === B.at` and slice concatenation for deletions),
1685
- - createdAt within merge window (default 5 minutes).
1686
-
1687
- Merging is deterministic: choose smallest revisionId lexicographically as survivor.
1688
-
1689
- ## Round-trip preservation, validation, migration, and examples
1690
-
1691
- ### Preservation model
1692
-
1693
- #### Goals
1694
-
1695
- Preserve unsupported OOXML in two forms:
1696
-
1697
- 1. **Untouched parts**: parts not edited by the CDS editor (headers, footers, footnotes, endnotes, themes, settings, etc.) are preserved as raw bytes and copied back during export.
1698
- 2. **Opaque fragments** inside edited parts: unsupported markup embedded in the main document story is preserved as **fragments**, referenced by `ooxmlBlock/ooxmlInline` nodes or by `PreservedXmlRef` fields.
1699
-
1700
- Because `.docx` is an OPC package defined in terms of parts and relationships, preserving unknown parts/relationships is fundamental for compatibility. citeturn0search2turn3search4
1701
-
1702
- Markup Compatibility (e.g., `mc:AlternateContent`) must be preserved when not explicitly supported. citeturn2search3turn2search7
1703
-
1704
- #### Data structures
1705
-
1706
- ```ts
1707
- export interface PreservationStore {
1708
- // XML fragments that can be re-injected into edited parts
1709
- fragments: Record<Id, PreservedXmlFragment>;
1710
-
1711
- // OPC package-level preservation
1712
- opc: OpcPreservation;
1713
- }
1714
-
1715
- export interface PreservedXmlRef {
1716
- fragmentId: Id;
1717
- }
1718
-
1719
- export interface PreservedXmlFragment {
1720
- fragmentId: Id;
1721
- kind: "xmlElement" | "xmlFragment";
1722
- xmlns: Record<string, string>; // prefix->namespace URI
1723
- xml: string; // UTF-8 XML text; must be well-formed if wrapped in <cds:frag> by implementation
1724
-
1725
- // Merge policy
1726
- policy: "readOnly" | "mergeable";
1727
-
1728
- // Original location hint for diagnostics
1729
- source?: { partName: PartName; xpath?: string };
1730
- }
1731
-
1732
- export interface OpcPreservation {
1733
- // Original [Content_Types].xml bytes
1734
- contentTypesXmlBase64: Base64;
1735
-
1736
- // All original parts and relationships
1737
- parts: Record<PartName, PreservedPart>;
1738
- relationships: Record<string, Relationship[]>; // key = "package" or partName
1739
-
1740
- // Parts that CDS regenerates (must be listed explicitly)
1741
- regeneratedParts: {
1742
- mainDocument: "/word/document.xml";
1743
- styles?: "/word/styles.xml";
1744
- numbering?: "/word/numbering.xml";
1745
- comments?: "/word/comments.xml";
1746
- relsMainDocument: "/word/_rels/document.xml.rels";
1747
- };
1748
- }
1749
-
1750
- export interface PreservedPart {
1751
- partName: PartName;
1752
- contentType: string;
1753
- bytesBase64: Base64;
1754
- editable: false; // parts are not edited unless explicitly regenerated
1755
- }
1756
-
1757
- export interface Relationship {
1758
- id: RelationshipId;
1759
- type: string; // relationship type URI
1760
- target: string; // part URI or external URI
1761
- targetMode?: "External";
1762
- }
1763
- ```
1764
-
1765
- ### Preservation linking and edit constraints
1766
-
1767
- **Rule PR1 (Fragment integrity):** If a content node references `fragmentId`, that fragment MUST exist and MUST be re-emitted verbatim on export.
1768
-
1769
- **Rule PR2 (Locked regions):** `ooxmlBlock` and `ooxmlInline` nodes are non-editable; attempts to edit inside MUST be rejected and surfaced as a recoverable error diagnostic (editor can still delete the whole atomic node if allowed).
1770
-
1771
- **Rule PR3 (Reattachment):** Opaque fragments represented as nodes reattach automatically because they occupy a position in the content tree. Fragments referenced from `ooxmlUnknown...` fields reattach to their owning node’s corresponding OOXML property container:
1772
- - paragraph `ooxmlUnknownPPr` re-injects into `w:pPr`,
1773
- - run-level unknown re-injects into `w:rPr` for the specific run created from that text span,
1774
- - numbering/style unknown re-injects into the corresponding style/numbering element.
1775
-
1776
- ### Validation rules
1777
-
1778
- Validation must classify issues as fatal vs recoverable.
1779
-
1780
- #### Structural validation (fatal unless repairable)
1781
-
1782
- - **V-S1:** Node types and required fields must match unions.
1783
- - **V-S2:** Nesting constraints must hold (graph above).
1784
- - **V-S3:** All IDs must be unique per D3.
1785
-
1786
- If a repair rule exists (R1–R8), the engine MUST repair and emit a warning diagnostic; otherwise fatal.
1787
-
1788
- #### Attribute validation
1789
-
1790
- - **V-A1:** `ParagraphNumbering.ilvl` 0..8.
1791
- - **V-A2:** Heading level 1..9.
1792
- - **V-A3:** Color values must be 6 hex digits or `"auto"`.
1793
- - **V-A4:** Font sizes must be positive half-points. citeturn8search14
1794
-
1795
- #### Anchor validity
1796
-
1797
- - **V-C1:** All anchors positions within 0..size(doc).
1798
- - **V-C2:** Range anchors must satisfy `from<=to`.
1799
- - **V-C3:** Node anchors must resolve to an atomic node token (leaf size=1).
1800
-
1801
- Invalid anchors become `OrphanAnchor` with a warning diagnostic.
1802
-
1803
- #### Revision validity
1804
-
1805
- - **V-R1:** Insertions must have non-empty range.
1806
- - **V-R2:** Deletions must have non-empty deletedSlice.
1807
- - **V-R3:** No overlapping active insertion ranges.
1808
-
1809
- Violations repaired by splitting/merging per deterministic rules, with diagnostics.
1810
-
1811
- #### Preservation integrity
1812
-
1813
- - **V-P1:** All referenced fragment ids exist.
1814
- - **V-P2:** OPC parts referenced in relationships exist unless external.
1815
- - **V-P3:** Regenerated parts must not be simultaneously marked preserved-only.
1816
-
1817
- ### Versioning and migration
1818
-
1819
- #### Schema version field
1820
-
1821
- `CanonicalDocument.schemaVersion` is the migration discriminator.
1822
-
1823
- #### Backward compatibility rules
1824
-
1825
- - A loader MUST support `"cds/1.0.0"` documents.
1826
- - The loader MAY accept older versions if a migration function exists; otherwise reject.
1827
-
1828
- #### Forward compatibility handling
1829
-
1830
- If `schemaVersion` is unknown:
1831
- - Reject as fatal, **unless** `preservation.opc` contains the original `.docx` package; in that case the system MAY still allow re-export of the preserved `.docx` without editing (read-only compatibility mode), emitting a fatal diagnostic for editing.
1832
-
1833
- #### Migration strategy (normative)
1834
-
1835
- A migration is defined as:
1836
-
1837
- ```ts
1838
- export interface Migration {
1839
- from: CDS_SchemaVersion;
1840
- to: CDS_SchemaVersion;
1841
- migrate(doc: CanonicalDocument): CanonicalDocument;
1842
- }
1843
- ```
1844
-
1845
- Rules:
1846
- - Migrations MUST be pure (no external state), deterministic, and idempotent.
1847
- - Each migration MUST run full normalization and validation at the end, emitting diagnostics.
1848
- - Stored documents MUST be migrated incrementally version-by-version (no skipping).
1849
-
1850
- ### Examples
1851
-
1852
- All examples below are fully specified CDS JSON documents. IDs are illustrative.
1853
-
1854
- #### Simple document example
1855
-
1856
- ```json
1857
- {
1858
- "schemaVersion": "cds/1.0.0",
1859
- "docId": "2d6e1c2e-7d15-45a4-8b7a-5f422f18bb3b",
1860
- "createdAt": "2026-03-25T10:00:00.000Z",
1861
- "updatedAt": "2026-03-25T10:00:00.000Z",
1862
- "metadata": {
1863
- "title": "Simple Doc",
1864
- "actors": {
1865
- "u1": { "actorId": "u1", "displayName": "Alex Editor" }
1866
- }
1867
- },
1868
- "content": {
1869
- "id": "n_doc",
1870
- "type": "doc",
1871
- "attrs": { "trackRevisionsDefault": false },
1872
- "children": [
1873
- {
1874
- "id": "p1",
1875
- "type": "paragraph",
1876
- "attrs": { "alignment": "left" },
1877
- "children": [
1878
- { "id": "t1", "type": "text", "text": "Hello, world!", "marks": [] }
1879
- ]
1880
- }
1881
- ]
1882
- },
1883
- "styles": {
1884
- "defaults": {},
1885
- "paragraphStyles": {},
1886
- "characterStyles": {},
1887
- "tableStyles": {}
1888
- },
1889
- "numbering": {
1890
- "abstractNums": {},
1891
- "nums": {}
1892
- },
1893
- "media": {
1894
- "items": {},
1895
- "exportPolicy": {
1896
- "mediaFolder": "/word/media",
1897
- "filenameStrategy": "stableByMediaId",
1898
- "relationshipIdStrategy": "stableByOrder"
1899
- }
1900
- },
1901
- "comments": { "threads": {}, "comments": {} },
1902
- "revisions": { "trackRevisions": false, "items": {} },
1903
- "preservation": {
1904
- "fragments": {},
1905
- "opc": {
1906
- "contentTypesXmlBase64": "",
1907
- "parts": {},
1908
- "relationships": {},
1909
- "regeneratedParts": {
1910
- "mainDocument": "/word/document.xml",
1911
- "relsMainDocument": "/word/_rels/document.xml.rels"
1912
- }
1913
- }
1914
- },
1915
- "diagnostics": { "items": [] }
1916
- }
1917
- ```
1918
-
1919
- #### Document with lists and tables
1920
-
1921
- ```json
1922
- {
1923
- "schemaVersion": "cds/1.0.0",
1924
- "docId": "9c2e7d12-0c8b-4bb5-9f0a-5f2d0d0f7d2e",
1925
- "createdAt": "2026-03-25T10:05:00.000Z",
1926
- "updatedAt": "2026-03-25T10:05:00.000Z",
1927
- "metadata": {
1928
- "title": "Lists and Tables",
1929
- "actors": {
1930
- "u1": { "actorId": "u1", "displayName": "Alex Editor" }
1931
- }
1932
- },
1933
- "content": {
1934
- "id": "doc",
1935
- "type": "doc",
1936
- "attrs": { "trackRevisionsDefault": false },
1937
- "children": [
1938
- {
1939
- "id": "h1",
1940
- "type": "heading",
1941
- "attrs": { "level": 1, "styleId": "Heading1" },
1942
- "children": [{ "id": "ht1", "type": "text", "text": "Project Status", "marks": [] }]
1943
- },
1944
- {
1945
- "id": "ol1",
1946
- "type": "orderedList",
1947
- "attrs": { "kind": "ordered", "numId": "num1", "baseIlvl": 0 },
1948
- "children": [
1949
- {
1950
- "id": "li1",
1951
- "type": "listItem",
1952
- "attrs": {},
1953
- "children": [
1954
- {
1955
- "id": "p_li1",
1956
- "type": "paragraph",
1957
- "attrs": { "numbering": { "numId": "num1", "ilvl": 0 } },
1958
- "children": [{ "id": "t_li1", "type": "text", "text": "Define scope", "marks": [] }]
1959
- }
1960
- ]
1961
- },
1962
- {
1963
- "id": "li2",
1964
- "type": "listItem",
1965
- "attrs": {},
1966
- "children": [
1967
- {
1968
- "id": "p_li2",
1969
- "type": "paragraph",
1970
- "attrs": { "numbering": { "numId": "num1", "ilvl": 0 } },
1971
- "children": [{ "id": "t_li2", "type": "text", "text": "Build prototype", "marks": [] }]
1972
- }
1973
- ]
1974
- }
1975
- ]
1976
- },
1977
- {
1978
- "id": "tbl1",
1979
- "type": "table",
1980
- "attrs": { "grid": { "colWidthsTwips": [3000, 5000] } },
1981
- "children": [
1982
- {
1983
- "id": "tr1",
1984
- "type": "tableRow",
1985
- "attrs": {},
1986
- "children": [
1987
- {
1988
- "id": "tc1",
1989
- "type": "tableCell",
1990
- "attrs": {},
1991
- "children": [
1992
- {
1993
- "id": "p_tc1",
1994
- "type": "paragraph",
1995
- "attrs": {},
1996
- "children": [{ "id": "t_tc1", "type": "text", "text": "Owner", "marks": [{ "type": "bold" }] }]
1997
- }
1998
- ]
1999
- },
2000
- {
2001
- "id": "tc2",
2002
- "type": "tableCell",
2003
- "attrs": {},
2004
- "children": [
2005
- {
2006
- "id": "p_tc2",
2007
- "type": "paragraph",
2008
- "attrs": {},
2009
- "children": [{ "id": "t_tc2", "type": "text", "text": "Alex", "marks": [] }]
2010
- }
2011
- ]
2012
- }
2013
- ]
2014
- }
2015
- ]
2016
- }
2017
- ]
2018
- },
2019
- "styles": { "defaults": {}, "paragraphStyles": {}, "characterStyles": {}, "tableStyles": {} },
2020
- "numbering": {
2021
- "abstractNums": {
2022
- "abs1": { "abstractNumId": "abs1", "levels": { "0": { "level": 0, "numFmt": "decimal", "lvlText": "%1." } } }
2023
- },
2024
- "nums": { "num1": { "numId": "num1", "abstractNumId": "abs1" } }
2025
- },
2026
- "media": {
2027
- "items": {},
2028
- "exportPolicy": { "mediaFolder": "/word/media", "filenameStrategy": "stableByMediaId", "relationshipIdStrategy": "stableByOrder" }
2029
- },
2030
- "comments": { "threads": {}, "comments": {} },
2031
- "revisions": { "trackRevisions": false, "items": {} },
2032
- "preservation": {
2033
- "fragments": {},
2034
- "opc": {
2035
- "contentTypesXmlBase64": "",
2036
- "parts": {},
2037
- "relationships": {},
2038
- "regeneratedParts": { "mainDocument": "/word/document.xml", "relsMainDocument": "/word/_rels/document.xml.rels" }
2039
- }
2040
- },
2041
- "diagnostics": { "items": [] }
2042
- }
2043
- ```
2044
-
2045
- #### Document with comments
2046
-
2047
- ```json
2048
- {
2049
- "schemaVersion": "cds/1.0.0",
2050
- "docId": "f88c2c0a-5f7a-4a1a-8a9c-8f0a2a4b3c11",
2051
- "createdAt": "2026-03-25T10:10:00.000Z",
2052
- "updatedAt": "2026-03-25T10:10:00.000Z",
2053
- "metadata": {
2054
- "title": "Comments Example",
2055
- "actors": {
2056
- "u1": { "actorId": "u1", "displayName": "Alex Editor" },
2057
- "u2": { "actorId": "u2", "displayName": "Casey Reviewer" }
2058
- }
2059
- },
2060
- "content": {
2061
- "id": "doc",
2062
- "type": "doc",
2063
- "attrs": { "trackRevisionsDefault": false },
2064
- "children": [
2065
- {
2066
- "id": "p1",
2067
- "type": "paragraph",
2068
- "attrs": {},
2069
- "children": [
2070
- { "id": "t1", "type": "text", "text": "Please review this sentence.", "marks": [] }
2071
- ]
2072
- }
2073
- ]
2074
- },
2075
- "styles": { "defaults": {}, "paragraphStyles": {}, "characterStyles": {}, "tableStyles": {} },
2076
- "numbering": { "abstractNums": {}, "nums": {} },
2077
- "media": {
2078
- "items": {},
2079
- "exportPolicy": { "mediaFolder": "/word/media", "filenameStrategy": "stableByMediaId", "relationshipIdStrategy": "stableByOrder" }
2080
- },
2081
- "comments": {
2082
- "threads": {
2083
- "th1": {
2084
- "threadId": "th1",
2085
- "anchor": {
2086
- "kind": "range",
2087
- "range": { "from": 2, "to": 28 },
2088
- "assoc": { "start": -1, "end": 1 },
2089
- "quote": { "selectedText": "review this sentence" }
2090
- },
2091
- "commentIds": ["c1"],
2092
- "resolved": false,
2093
- "ooxmlCommentId": 0
2094
- }
2095
- },
2096
- "comments": {
2097
- "c1": {
2098
- "commentId": "c1",
2099
- "threadId": "th1",
2100
- "authorId": "u2",
2101
- "createdAt": "2026-03-25T10:10:00.000Z",
2102
- "body": {
2103
- "blocks": [
2104
- {
2105
- "id": "cp1",
2106
- "type": "paragraph",
2107
- "attrs": {},
2108
- "children": [{ "id": "ct1", "type": "text", "text": "Looks good, but consider tightening wording.", "marks": [] }]
2109
- }
2110
- ]
2111
- }
2112
- }
2113
- }
2114
- },
2115
- "revisions": { "trackRevisions": false, "items": {} },
2116
- "preservation": {
2117
- "fragments": {},
2118
- "opc": {
2119
- "contentTypesXmlBase64": "",
2120
- "parts": {},
2121
- "relationships": {},
2122
- "regeneratedParts": { "mainDocument": "/word/document.xml", "comments": "/word/comments.xml", "relsMainDocument": "/word/_rels/document.xml.rels" }
2123
- }
2124
- },
2125
- "diagnostics": { "items": [] }
2126
- }
2127
- ```
2128
-
2129
- #### Document with tracked changes
2130
-
2131
- ```json
2132
- {
2133
- "schemaVersion": "cds/1.0.0",
2134
- "docId": "c1a76d6b-2f15-4c5f-9a4b-b3c9a7d1a0e2",
2135
- "createdAt": "2026-03-25T10:15:00.000Z",
2136
- "updatedAt": "2026-03-25T10:15:00.000Z",
2137
- "metadata": {
2138
- "title": "Tracked Changes Example",
2139
- "actors": {
2140
- "u1": { "actorId": "u1", "displayName": "Alex Editor" }
2141
- }
2142
- },
2143
- "content": {
2144
- "id": "doc",
2145
- "type": "doc",
2146
- "attrs": { "trackRevisionsDefault": true },
2147
- "children": [
2148
- {
2149
- "id": "p1",
2150
- "type": "paragraph",
2151
- "attrs": {},
2152
- "children": [
2153
- { "id": "t1", "type": "text", "text": "The quick brown fox.", "marks": [] }
2154
- ]
2155
- }
2156
- ]
2157
- },
2158
- "styles": { "defaults": {}, "paragraphStyles": {}, "characterStyles": {}, "tableStyles": {} },
2159
- "numbering": { "abstractNums": {}, "nums": {} },
2160
- "media": {
2161
- "items": {},
2162
- "exportPolicy": { "mediaFolder": "/word/media", "filenameStrategy": "stableByMediaId", "relationshipIdStrategy": "stableByOrder" }
2163
- },
2164
- "comments": { "threads": {}, "comments": {} },
2165
- "revisions": {
2166
- "trackRevisions": true,
2167
- "items": {
2168
- "r_ins_1": {
2169
- "revisionId": "r_ins_1",
2170
- "kind": "insertion",
2171
- "authorId": "u1",
2172
- "createdAt": "2026-03-25T10:15:00.000Z",
2173
- "state": "active",
2174
- "ooxmlRevisionId": 1,
2175
- "range": { "from": 6, "to": 11 },
2176
- "assoc": { "start": -1, "end": 1 }
2177
- },
2178
- "r_del_1": {
2179
- "revisionId": "r_del_1",
2180
- "kind": "deletion",
2181
- "authorId": "u1",
2182
- "createdAt": "2026-03-25T10:15:00.000Z",
2183
- "state": "active",
2184
- "ooxmlRevisionId": 2,
2185
- "at": 16,
2186
- "assoc": -1,
2187
- "deletedSlice": {
2188
- "openStart": 0,
2189
- "openEnd": 0,
2190
- "content": [
2191
- { "id": "dt1", "type": "text", "text": "brown ", "marks": [] }
2192
- ]
2193
- },
2194
- "quote": { "selectedText": "brown " }
2195
- }
2196
- }
2197
- },
2198
- "preservation": {
2199
- "fragments": {},
2200
- "opc": {
2201
- "contentTypesXmlBase64": "",
2202
- "parts": {},
2203
- "relationships": {},
2204
- "regeneratedParts": { "mainDocument": "/word/document.xml", "relsMainDocument": "/word/_rels/document.xml.rels" }
2205
- }
2206
- },
2207
- "diagnostics": { "items": [] }
2208
- }
2209
- ```
2210
-
2211
- #### Document with preserved unsupported content
2212
-
2213
- ```json
2214
- {
2215
- "schemaVersion": "cds/1.0.0",
2216
- "docId": "1f5ef4e7-9e2c-4f77-9a4e-1a0d4f6c9c31",
2217
- "createdAt": "2026-03-25T10:20:00.000Z",
2218
- "updatedAt": "2026-03-25T10:20:00.000Z",
2219
- "metadata": {
2220
- "title": "Preservation Example",
2221
- "actors": {
2222
- "u1": { "actorId": "u1", "displayName": "Alex Editor" }
2223
- }
2224
- },
2225
- "content": {
2226
- "id": "doc",
2227
- "type": "doc",
2228
- "attrs": {},
2229
- "children": [
2230
- {
2231
- "id": "p1",
2232
- "type": "paragraph",
2233
- "attrs": {},
2234
- "children": [{ "id": "t1", "type": "text", "text": "Before preserved block.", "marks": [] }]
2235
- },
2236
- {
2237
- "id": "x1",
2238
- "type": "ooxmlBlock",
2239
- "attrs": {
2240
- "fragmentId": "frag_altcontent_1",
2241
- "editability": "locked",
2242
- "description": "Unsupported mc:AlternateContent payload"
2243
- },
2244
- "children": []
2245
- },
2246
- {
2247
- "id": "p2",
2248
- "type": "paragraph",
2249
- "attrs": {},
2250
- "children": [{ "id": "t2", "type": "text", "text": "After preserved block.", "marks": [] }]
2251
- }
2252
- ]
2253
- },
2254
- "styles": { "defaults": {}, "paragraphStyles": {}, "characterStyles": {}, "tableStyles": {} },
2255
- "numbering": { "abstractNums": {}, "nums": {} },
2256
- "media": {
2257
- "items": {},
2258
- "exportPolicy": { "mediaFolder": "/word/media", "filenameStrategy": "stableByMediaId", "relationshipIdStrategy": "stableByOrder" }
2259
- },
2260
- "comments": { "threads": {}, "comments": {} },
2261
- "revisions": { "trackRevisions": false, "items": {} },
2262
- "preservation": {
2263
- "fragments": {
2264
- "frag_altcontent_1": {
2265
- "fragmentId": "frag_altcontent_1",
2266
- "kind": "xmlElement",
2267
- "xmlns": { "mc": "http://schemas.openxmlformats.org/markup-compatibility/2006" },
2268
- "xml": "<mc:AlternateContent mc:Ignorable=\"w14\"><mc:Choice Requires=\"w14\"><w14:someFutureFeature xmlns:w14=\"http://schemas.microsoft.com/office/word/2010/wordml\">...</w14:someFutureFeature></mc:Choice><mc:Fallback><w:r xmlns:w=\"http://schemas.openxmlformats.org/wordprocessingml/2006/main\"><w:t>Fallback</w:t></w:r></mc:Fallback></mc:AlternateContent>",
2269
- "policy": "readOnly",
2270
- "source": { "partName": "/word/document.xml", "xpath": "/w:document/w:body/*[2]" }
2271
- }
2272
- },
2273
- "opc": {
2274
- "contentTypesXmlBase64": "",
2275
- "parts": {},
2276
- "relationships": {},
2277
- "regeneratedParts": { "mainDocument": "/word/document.xml", "relsMainDocument": "/word/_rels/document.xml.rels" }
2278
- }
2279
- },
2280
- "diagnostics": {
2281
- "items": [
2282
- {
2283
- "diagnosticId": "6d0b0b6a-8d5b-4d9b-9a39-8b1b1e1f2d2a",
2284
- "severity": "warning",
2285
- "code": "PRESERVE_LOCKED_BLOCK",
2286
- "message": "Document contains preserved unsupported OOXML block fragment; it is locked and will be round-tripped verbatim.",
2287
- "createdAt": "2026-03-25T10:20:00.000Z",
2288
- "location": { "kind": "nodeId", "nodeId": "x1" }
2289
- }
2290
- ]
2291
- }
2292
- }
2293
- ```
2294
-
2295
- ### Non-goals
2296
-
2297
- The CDS explicitly does **not** attempt to represent:
2298
-
2299
- - exact Word pagination, line breaking, or layout fidelity (these depend on Word’s layout engine and fonts),
2300
- - advanced Word stories as editable content (headers, footers, footnotes, endnotes, text boxes); these are preserved as OPC parts unless explicitly modeled,
2301
- - full fidelity for all OOXML constructs (content controls, complex fields, advanced DrawingML anchoring); unsupported constructs are preserved as opaque fragments or untouched parts,
2302
- - collaborative CRDT semantics (this spec is transactional and mapping-based, single-thread main-thread execution).
2303
-
2304
- ### Output quality bar (normative acceptance criteria)
2305
-
2306
- An implementation is conformant to this spec iff:
2307
-
2308
- 1. **Schema validity:** It can parse, validate, normalize, and serialize CDS documents matching these interfaces and invariants.
2309
- 2. **Deterministic normalization:** Running normalization twice yields identical JSON.
2310
- 3. **Anchor stability:** Comment and revision anchors remap through any transaction using the mapping rules, with orphaning only under defined conditions.
2311
- 4. **Word-openable export:** Exported `.docx` opens in Word without structural corruption for all modeled features, including:
2312
- - paragraphs/runs/text/breaks, citeturn3search0turn3search1turn3search3
2313
- - hyperlinks via `w:hyperlink`, citeturn1search2
2314
- - comments via range markers and comment part linkage, citeturn0search8turn0search4turn0search0
2315
- - tracked insertions/deletions via revision markup, citeturn1search0turn1search1turn10search3turn11search19
2316
- - tables via `w:tbl` and grid/cell props where modeled. citeturn2search5turn4search7turn5search7
2317
- 5. **Preservation correctness:** Unsupported OOXML is preserved via locked fragments or untouched parts, including Markup Compatibility constructs. citeturn2search3turn2search7turn0search2