@beyondwork/docx-react-component 1.0.66 → 1.0.69

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 (384) hide show
  1. package/README.md +75 -931
  2. package/package.json +26 -27
  3. package/src/api/anchor-conversion.ts +43 -0
  4. package/src/api/editor-state-types.ts +2 -1
  5. package/src/api/public-types.ts +504 -101
  6. package/src/api/session-state.ts +4 -0
  7. package/src/api/v3/README.md +91 -0
  8. package/src/api/v3/_create.ts +146 -0
  9. package/src/api/v3/_layer-metadata.ts +362 -0
  10. package/src/api/v3/_mocks.ts +84 -0
  11. package/src/api/v3/_runtime-handle.ts +162 -0
  12. package/src/api/v3/_ux-response.ts +73 -0
  13. package/src/api/v3/ai/_metadata-audit.ts +225 -0
  14. package/src/api/v3/ai/attach.ts +235 -0
  15. package/src/api/v3/ai/bundle.ts +132 -0
  16. package/src/api/v3/ai/explain.ts +144 -0
  17. package/src/api/v3/ai/export.ts +54 -0
  18. package/src/api/v3/ai/inspect.ts +118 -0
  19. package/src/api/v3/ai/policy.ts +77 -0
  20. package/src/api/v3/ai/replacement.ts +341 -0
  21. package/src/api/v3/ai/resolve.ts +133 -0
  22. package/src/api/v3/index.ts +79 -0
  23. package/src/api/v3/runtime/chart.ts +310 -0
  24. package/src/api/v3/runtime/clipboard.ts +81 -0
  25. package/src/api/v3/runtime/collab.ts +331 -0
  26. package/src/api/v3/runtime/content.ts +236 -0
  27. package/src/api/v3/runtime/document.ts +282 -0
  28. package/src/api/v3/runtime/formatting.ts +186 -0
  29. package/src/api/v3/runtime/geometry.ts +349 -0
  30. package/src/api/v3/runtime/layout.ts +108 -0
  31. package/src/api/v3/runtime/review.ts +129 -0
  32. package/src/api/v3/runtime/search.ts +74 -0
  33. package/src/api/v3/runtime/table.ts +63 -0
  34. package/src/api/v3/runtime/workflow.ts +434 -0
  35. package/src/api/v3/ui/_context.ts +86 -0
  36. package/src/api/v3/ui/_create.ts +65 -0
  37. package/src/api/v3/ui/_types.ts +520 -0
  38. package/src/api/v3/ui/chrome-composition.ts +342 -0
  39. package/src/{ui-tailwind/chrome → api/v3/ui}/chrome-preset-model.ts +11 -1
  40. package/src/api/v3/ui/chrome.ts +476 -0
  41. package/src/api/v3/ui/debug.ts +124 -0
  42. package/src/api/v3/ui/index.ts +64 -0
  43. package/src/api/v3/ui/overlays-visibility.ts +170 -0
  44. package/src/api/v3/ui/overlays.ts +427 -0
  45. package/src/api/v3/ui/scope.ts +71 -0
  46. package/src/api/v3/ui/session.ts +100 -0
  47. package/src/api/v3/ui/surface.ts +170 -0
  48. package/src/api/v3/ui/viewport.ts +303 -0
  49. package/src/core/commands/index.ts +28 -6
  50. package/src/core/commands/list-commands.ts +3 -2
  51. package/src/core/commands/section-layout-commands.ts +9 -8
  52. package/src/core/schema/text-schema.ts +16 -0
  53. package/src/core/selection/mapping.ts +33 -72
  54. package/src/core/state/editor-state.ts +96 -189
  55. package/src/index.ts +23 -4
  56. package/src/io/chart-preview-resolver.ts +1 -1
  57. package/src/io/docx-session.ts +36 -4795
  58. package/src/io/export/build-app-properties-xml.ts +1 -1
  59. package/src/io/export/serialize-comments.ts +1 -1
  60. package/src/io/export/serialize-headers-footers.ts +6 -1
  61. package/src/io/export/serialize-main-document.ts +45 -0
  62. package/src/io/export/serialize-run-formatting.ts +17 -2
  63. package/src/io/export/twip.ts +1 -1
  64. package/src/io/normalize/normalize-text.ts +27 -20
  65. package/src/io/ooxml/chart/parse-series.ts +1 -1
  66. package/src/io/ooxml/chart/resolve-color.ts +2 -2
  67. package/src/io/ooxml/chart/types.ts +1 -1
  68. package/src/io/ooxml/classify-embedding.ts +83 -33
  69. package/src/io/ooxml/parse-fill.ts +1 -1
  70. package/src/io/ooxml/parse-main-document.ts +71 -1
  71. package/src/io/ooxml/parse-object.ts +14 -10
  72. package/src/io/ooxml/parse-run-formatting.ts +47 -1
  73. package/src/io/ooxml/property-grab-bag.ts +2 -2
  74. package/src/io/ooxml/units.ts +11 -0
  75. package/src/io/ooxml/workflow-payload.ts +282 -7
  76. package/src/model/anchor.ts +85 -0
  77. package/src/model/canonical-document.ts +351 -15
  78. package/src/model/chart-types.ts +1 -1
  79. package/src/model/layout/index.ts +83 -0
  80. package/src/model/layout/page-graph-types.ts +181 -0
  81. package/src/model/layout/page-layout-snapshot.ts +105 -0
  82. package/src/model/layout/resolved-layout-types.ts +47 -0
  83. package/src/model/layout/runtime-page-graph-types.ts +102 -0
  84. package/src/model/paragraph-scope-ids.ts +72 -0
  85. package/src/model/review/comment-types.ts +112 -0
  86. package/src/model/review/index.ts +2 -0
  87. package/src/model/review/revision-types.ts +215 -0
  88. package/src/model/snapshot.ts +32 -0
  89. package/src/review/store/comment-store.ts +21 -47
  90. package/src/review/store/revision-types.ts +40 -198
  91. package/src/runtime/collab/base-doc-fingerprint.ts +6 -1
  92. package/src/runtime/collab/runtime-collab-sync.ts +13 -3
  93. package/src/runtime/collab-session.ts +1 -1
  94. package/src/runtime/debug/build-debug-inspector-snapshot.ts +686 -0
  95. package/src/runtime/debug/event-ring-buffer.ts +64 -0
  96. package/src/runtime/debug/probability-sampler.ts +18 -0
  97. package/src/runtime/debug/runtime-debug-facet.ts +67 -0
  98. package/src/runtime/debug/stage-tokens.ts +31 -0
  99. package/src/runtime/debug/telemetry-bus.ts +271 -0
  100. package/src/runtime/debug/types.ts +275 -0
  101. package/src/runtime/debug/wrap-ref-for-telemetry.ts +118 -0
  102. package/src/runtime/document-layout.ts +8 -6
  103. package/src/runtime/document-runtime.ts +843 -1141
  104. package/src/runtime/document-search.ts +1 -1
  105. package/src/runtime/edit-ops/index.ts +1 -1
  106. package/src/runtime/external-send-runtime.ts +1 -1
  107. package/src/runtime/formatting/document-lookup.ts +235 -0
  108. package/src/runtime/formatting/field/registry.ts +41 -0
  109. package/src/runtime/{field-resolver.ts → formatting/field/resolver.ts} +27 -2
  110. package/src/runtime/formatting/font-resolution.ts +83 -0
  111. package/src/runtime/formatting/formatting-context.ts +903 -0
  112. package/src/runtime/formatting/formatting-types.ts +157 -0
  113. package/src/runtime/{hyperlink-color-resolver.ts → formatting/hyperlink-color.ts} +2 -2
  114. package/src/runtime/formatting/index.ts +125 -0
  115. package/src/runtime/{resolved-numbering-geometry.ts → formatting/numbering/geometry.ts} +1 -1
  116. package/src/runtime/{numbering-prefix.ts → formatting/numbering/prefix.ts} +170 -3
  117. package/src/runtime/formatting/paragraph-style-resolver.ts +92 -0
  118. package/src/runtime/formatting/projector.ts +75 -0
  119. package/src/runtime/formatting/resolve-effective.ts +407 -0
  120. package/src/runtime/formatting/revision-display.ts +105 -0
  121. package/src/runtime/{paragraph-style-resolver.ts → formatting/style-cascade.ts} +84 -141
  122. package/src/runtime/{table-style-resolver.ts → formatting/table-style-resolver.ts} +1 -1
  123. package/src/runtime/formatting/telemetry-bridge.ts +106 -0
  124. package/src/runtime/{theme-color-resolver.ts → formatting/theme-color.ts} +2 -30
  125. package/src/runtime/geometry/caret-geometry.ts +164 -0
  126. package/src/runtime/geometry/geometry-facet.ts +364 -0
  127. package/src/runtime/geometry/geometry-types.ts +256 -0
  128. package/src/runtime/geometry/hit-test.ts +125 -0
  129. package/src/runtime/geometry/index.ts +71 -0
  130. package/src/runtime/geometry/inert-geometry-facet.ts +43 -0
  131. package/src/runtime/geometry/invalidation.ts +35 -0
  132. package/src/runtime/geometry/object-handles.ts +77 -0
  133. package/src/runtime/geometry/overlay-rects.ts +85 -0
  134. package/src/runtime/geometry/project-anchors.ts +100 -0
  135. package/src/runtime/geometry/project-fragments.ts +216 -0
  136. package/src/runtime/geometry/projector.ts +129 -0
  137. package/src/runtime/geometry/replacement-envelope.ts +130 -0
  138. package/src/runtime/geometry/viewport.ts +218 -0
  139. package/src/runtime/layout/compat-input-ledger.ts +211 -0
  140. package/src/runtime/layout/index.ts +6 -1
  141. package/src/runtime/layout/inert-layout-facet.ts +12 -7
  142. package/src/runtime/layout/layout-engine-instance.ts +189 -11
  143. package/src/runtime/layout/layout-engine-version.ts +450 -1
  144. package/src/runtime/layout/layout-facet-types.ts +60 -0
  145. package/src/runtime/layout/layout-measurement-provider.ts +13 -0
  146. package/src/runtime/layout/measurement-backend-canvas.ts +14 -2
  147. package/src/runtime/layout/measurement-backend-empirical.ts +23 -4
  148. package/src/runtime/layout/page-graph.ts +62 -209
  149. package/src/runtime/layout/page-story-resolver.ts +7 -12
  150. package/src/runtime/layout/paginated-layout-engine.ts +186 -11
  151. package/src/runtime/layout/project-block-fragments.ts +11 -0
  152. package/src/runtime/layout/projector.ts +90 -0
  153. package/src/runtime/layout/public-facet.ts +187 -442
  154. package/src/runtime/layout/resolved-formatting-state.ts +158 -26
  155. package/src/runtime/layout/table-render-plan.ts +1 -1
  156. package/src/runtime/prerender/cache-envelope.ts +6 -1
  157. package/src/runtime/prerender/prerender-document.ts +18 -23
  158. package/src/runtime/render/decoration-resolver.ts +1 -1
  159. package/src/runtime/render/render-frame-types.ts +20 -0
  160. package/src/runtime/render/render-kernel.ts +94 -25
  161. package/src/runtime/scopes/_formatting-seam.ts +262 -0
  162. package/src/runtime/scopes/_scope-dependencies.ts +49 -0
  163. package/src/runtime/scopes/action-validation.ts +356 -0
  164. package/src/runtime/scopes/attach-explanation.ts +102 -0
  165. package/src/runtime/scopes/audit-bundle.ts +71 -0
  166. package/src/runtime/scopes/compile-scope-bundle.ts +163 -0
  167. package/src/runtime/scopes/compile-scope.ts +262 -0
  168. package/src/runtime/scopes/compiler-service.ts +431 -0
  169. package/src/runtime/scopes/create-issue.ts +107 -0
  170. package/src/runtime/scopes/enumerate-scopes.ts +543 -0
  171. package/src/runtime/scopes/evidence.ts +233 -0
  172. package/src/runtime/scopes/index.ts +150 -0
  173. package/src/runtime/scopes/position-map.ts +214 -0
  174. package/src/runtime/scopes/preservation-boundary.ts +91 -0
  175. package/src/runtime/scopes/projector.ts +49 -0
  176. package/src/runtime/scopes/replaceability.ts +87 -0
  177. package/src/runtime/scopes/replacement/apply.ts +228 -0
  178. package/src/runtime/scopes/replacement/compile.ts +59 -0
  179. package/src/runtime/scopes/replacement/propose.ts +42 -0
  180. package/src/runtime/scopes/resolve-reference.ts +347 -0
  181. package/src/runtime/scopes/review-bundle.ts +141 -0
  182. package/src/runtime/scopes/scope-kinds/_paragraph-text.ts +57 -0
  183. package/src/runtime/scopes/scope-kinds/_table-text.ts +42 -0
  184. package/src/runtime/scopes/scope-kinds/comment-thread.ts +59 -0
  185. package/src/runtime/scopes/scope-kinds/field.ts +65 -0
  186. package/src/runtime/scopes/scope-kinds/heading.ts +84 -0
  187. package/src/runtime/scopes/scope-kinds/list-item.ts +77 -0
  188. package/src/runtime/scopes/scope-kinds/paragraph.ts +182 -0
  189. package/src/runtime/scopes/scope-kinds/revision.ts +62 -0
  190. package/src/runtime/scopes/scope-kinds/table-cell.ts +57 -0
  191. package/src/runtime/scopes/scope-kinds/table-row.ts +61 -0
  192. package/src/runtime/scopes/scope-kinds/table.ts +55 -0
  193. package/src/runtime/scopes/scope-range.ts +208 -0
  194. package/src/runtime/scopes/semantic-scope-types.ts +454 -0
  195. package/src/runtime/scopes/workflow-overlap.ts +92 -0
  196. package/src/runtime/selection/index.ts +1 -1
  197. package/src/runtime/structure-ops/fragment-insert.ts +1 -1
  198. package/src/runtime/structure-ops/index.ts +1 -1
  199. package/src/runtime/surface-projection.ts +232 -262
  200. package/src/runtime/units.ts +4 -2
  201. package/src/runtime/workflow/coordinator.ts +1348 -0
  202. package/src/runtime/workflow/derived-scope-resolver.ts +125 -0
  203. package/src/runtime/workflow/index.ts +25 -0
  204. package/src/runtime/workflow/markup-mode-policy.ts +98 -0
  205. package/src/runtime/{workflow-markup.ts → workflow/markup.ts} +6 -6
  206. package/src/runtime/workflow/metadata-persistence.ts +306 -0
  207. package/src/runtime/workflow/metadata-writer.ts +123 -0
  208. package/src/runtime/workflow/overlay-store.ts +690 -0
  209. package/src/runtime/workflow/projector.ts +127 -0
  210. package/src/runtime/{query-scopes.ts → workflow/query-scopes.ts} +3 -3
  211. package/src/runtime/{workflow-rail-segments.ts → workflow/rail/compose.ts} +60 -165
  212. package/src/runtime/workflow/rail/types.ts +198 -0
  213. package/src/runtime/workflow/scope-rail-composer.ts +39 -0
  214. package/src/runtime/{scope-resolver.ts → workflow/scope-resolver.ts} +3 -3
  215. package/src/runtime/workflow/scope-writer.ts +188 -0
  216. package/src/runtime/{tamper-gate.ts → workflow/tamper-gate.ts} +1 -1
  217. package/src/runtime/workflow/visibility-policy.ts +129 -0
  218. package/src/session/_sync-legacy.ts +66 -0
  219. package/src/session/export/embedded-reconstitute.ts +104 -0
  220. package/src/session/export/export-diagnostics.ts +85 -0
  221. package/src/session/export/export-validation.ts +110 -0
  222. package/src/session/export/index.ts +34 -0
  223. package/src/session/export/preservation-reattach.ts +30 -0
  224. package/src/session/export/serialize-dispatch.ts +165 -0
  225. package/src/session/export/stateful-export-pipeline.ts +432 -0
  226. package/src/session/export/stateful-export.ts +684 -0
  227. package/src/session/import/canonical-assembly.ts +227 -0
  228. package/src/session/import/diagnostics-session.ts +54 -0
  229. package/src/session/import/embedded-discovery.ts +225 -0
  230. package/src/session/import/embedded-offload.ts +337 -0
  231. package/src/session/import/import-diagnostics.ts +69 -0
  232. package/src/session/import/loader-types.ts +313 -0
  233. package/src/session/import/loader.ts +1834 -0
  234. package/src/session/import/normalize.ts +195 -0
  235. package/src/session/import/package-parts.ts +217 -0
  236. package/src/session/import/package-read.ts +195 -0
  237. package/src/session/import/parse-orchestration.ts +105 -0
  238. package/src/session/import/part-constants.ts +70 -0
  239. package/src/session/import/part-discovery.ts +94 -0
  240. package/src/session/import/preservation-index.ts +46 -0
  241. package/src/{runtime/read-only-diagnostics-runtime.ts → session/import/read-only-diagnostics.ts} +24 -3
  242. package/src/session/import/review-import.ts +508 -0
  243. package/src/session/import/styles-consolidation.ts +281 -0
  244. package/src/session/import/workflow-scope-import.ts +256 -0
  245. package/src/session/index.ts +37 -0
  246. package/src/session/session-state.ts +69 -0
  247. package/src/session/session.ts +532 -0
  248. package/src/session/shared/protection.ts +228 -0
  249. package/src/session/shared/session-utils.ts +82 -0
  250. package/src/session/types.ts +499 -0
  251. package/src/shell/chart-snapshots.ts +96 -0
  252. package/src/shell/media-previews.ts +85 -0
  253. package/src/shell/overlay-anchor-bridge.ts +53 -0
  254. package/src/shell/paste-adapter.ts +23 -0
  255. package/src/shell/ref-commands.ts +1697 -0
  256. package/src/shell/ref-utilities.ts +48 -0
  257. package/src/shell/search.ts +51 -0
  258. package/src/{ui/editor-runtime-boundary.ts → shell/session-bootstrap.ts} +243 -67
  259. package/src/shell/ui-subscriber-channels.ts +81 -0
  260. package/src/shell/use-collab-sync.ts +116 -0
  261. package/src/ui/WordReviewEditor.tsx +496 -2051
  262. package/src/ui/editor-shell-view.tsx +30 -1
  263. package/src/ui/editor-surface-controller.tsx +49 -1
  264. package/src/ui/headless/revision-decoration-model.ts +83 -0
  265. package/src/{ui-tailwind/chrome → ui/headless}/role-action-sets.ts +1 -1
  266. package/src/ui/headless/scoped-chrome-policy.ts +2 -2
  267. package/src/ui/headless/selection-tool-context.ts +1 -1
  268. package/src/ui/headless/selection-tool-resolver.ts +1 -1
  269. package/src/ui/runtime-shortcut-dispatch.ts +46 -1
  270. package/src/ui/ui-controller-factory.ts +221 -0
  271. package/src/ui-tailwind/chart/ChartSurface.tsx +2 -2
  272. package/src/ui-tailwind/chart/layout/legend-layout.ts +1 -1
  273. package/src/ui-tailwind/chart/layout/plot-area.ts +2 -2
  274. package/src/ui-tailwind/chart/layout/title-layout.ts +1 -1
  275. package/src/ui-tailwind/chart/render/area.tsx +3 -3
  276. package/src/ui-tailwind/chart/render/bar-column.tsx +3 -3
  277. package/src/ui-tailwind/chart/render/bubble.tsx +3 -3
  278. package/src/ui-tailwind/chart/render/combo.tsx +2 -2
  279. package/src/ui-tailwind/chart/render/data-labels.tsx +2 -2
  280. package/src/ui-tailwind/chart/render/font-metrics.ts +2 -2
  281. package/src/ui-tailwind/chart/render/line.tsx +3 -3
  282. package/src/ui-tailwind/chart/render/pie.tsx +6 -6
  283. package/src/ui-tailwind/chart/render/scatter.tsx +3 -3
  284. package/src/ui-tailwind/chart/render/svg-primitives.ts +3 -3
  285. package/src/ui-tailwind/chart/render/unsupported.tsx +2 -2
  286. package/src/ui-tailwind/chrome/build-context-menu-entries.ts +88 -0
  287. package/src/ui-tailwind/chrome/chrome-preset-toolbar.tsx +1 -1
  288. package/src/ui-tailwind/chrome/collab-send-to-supplier-button.tsx +1 -1
  289. package/src/ui-tailwind/chrome/collab-tamper-banner.tsx +1 -1
  290. package/src/ui-tailwind/chrome/collab-top-nav-container.tsx +1 -1
  291. package/src/ui-tailwind/chrome/editor-action-registry.ts +553 -0
  292. package/src/ui-tailwind/chrome/editor-actions-to-palette.ts +182 -0
  293. package/src/ui-tailwind/chrome/local-surface-arbiter.ts +534 -0
  294. package/src/ui-tailwind/chrome/resolve-target-kind.ts +226 -0
  295. package/src/ui-tailwind/chrome/tw-alert-banner.tsx +38 -4
  296. package/src/ui-tailwind/chrome/tw-context-band.tsx +125 -0
  297. package/src/ui-tailwind/chrome/tw-context-menu-portal.tsx +248 -0
  298. package/src/ui-tailwind/chrome/tw-image-context-toolbar.tsx +42 -1
  299. package/src/ui-tailwind/chrome/tw-selection-anchor-resolver.ts +8 -7
  300. package/src/ui-tailwind/chrome/tw-selection-tool-blocked.tsx +38 -4
  301. package/src/ui-tailwind/chrome/tw-selection-tool-comment.tsx +104 -6
  302. package/src/ui-tailwind/chrome/tw-selection-tool-host.tsx +66 -7
  303. package/src/ui-tailwind/chrome/tw-selection-tool-workflow.tsx +54 -8
  304. package/src/ui-tailwind/chrome/tw-shortcut-hint.tsx +7 -1
  305. package/src/ui-tailwind/chrome/tw-suggestion-card.tsx +33 -0
  306. package/src/ui-tailwind/chrome/tw-table-context-toolbar.tsx +78 -1
  307. package/src/ui-tailwind/chrome/tw-table-grip-layer.tsx +16 -8
  308. package/src/ui-tailwind/chrome/tw-workspace-chrome-host.tsx +276 -0
  309. package/src/ui-tailwind/chrome/use-context-menu-controller.ts +201 -0
  310. package/src/ui-tailwind/chrome-overlay/chrome-overlay-projector.ts +1 -1
  311. package/src/ui-tailwind/chrome-overlay/tw-chrome-overlay.tsx +22 -4
  312. package/src/ui-tailwind/chrome-overlay/tw-comment-balloon-layer.tsx +1 -1
  313. package/src/ui-tailwind/chrome-overlay/tw-locked-block-layer.tsx +1 -1
  314. package/src/ui-tailwind/chrome-overlay/tw-object-selection-overlay.tsx +11 -5
  315. package/src/ui-tailwind/chrome-overlay/tw-page-stack-overlay-layer.tsx +197 -3
  316. package/src/ui-tailwind/chrome-overlay/tw-revision-margin-bar-layer.tsx +1 -1
  317. package/src/ui-tailwind/chrome-overlay/tw-scope-card-layer.tsx +35 -6
  318. package/src/ui-tailwind/chrome-overlay/tw-scope-rail-layer.tsx +24 -16
  319. package/src/ui-tailwind/chrome-overlay/tw-table-continuation-header.tsx +1 -1
  320. package/src/ui-tailwind/debug/README.md +57 -0
  321. package/src/ui-tailwind/debug/index.ts +3 -0
  322. package/src/ui-tailwind/debug/tw-debug-overlay.tsx +186 -0
  323. package/src/ui-tailwind/debug/tw-debug-presentation.tsx +80 -0
  324. package/src/ui-tailwind/debug/tw-debug-top-bar.tsx +83 -0
  325. package/src/ui-tailwind/editor-surface/chart-node-view.tsx +2 -2
  326. package/src/ui-tailwind/editor-surface/float-wrap-resolver.ts +1 -1
  327. package/src/ui-tailwind/editor-surface/pm-command-bridge.ts +135 -10
  328. package/src/ui-tailwind/editor-surface/pm-decorations.ts +40 -13
  329. package/src/ui-tailwind/editor-surface/pm-page-break-decorations.ts +1 -1
  330. package/src/ui-tailwind/editor-surface/pm-schema.ts +1 -1
  331. package/src/ui-tailwind/editor-surface/pm-state-from-snapshot.ts +3 -3
  332. package/src/ui-tailwind/editor-surface/predicted-tag-preflight.ts +1 -1
  333. package/src/ui-tailwind/editor-surface/remote-cursor-plugin.ts +2 -2
  334. package/src/ui-tailwind/editor-surface/scroll-anchor.ts +91 -9
  335. package/src/ui-tailwind/editor-surface/shape-renderer.ts +1 -1
  336. package/src/ui-tailwind/editor-surface/surface-layer.ts +1 -1
  337. package/src/ui-tailwind/editor-surface/tw-opaque-block.tsx +1 -1
  338. package/src/ui-tailwind/editor-surface/tw-page-block-view.helpers.ts +23 -6
  339. package/src/ui-tailwind/editor-surface/tw-prosemirror-surface.tsx +132 -22
  340. package/src/ui-tailwind/editor-surface/tw-table-node-view.tsx +1 -1
  341. package/src/ui-tailwind/index.ts +0 -5
  342. package/src/ui-tailwind/overlay-anchor-bridge-context.tsx +33 -0
  343. package/src/ui-tailwind/page-stack/floating-image-overlay-model.ts +66 -29
  344. package/src/ui-tailwind/page-stack/tw-floating-image-layer.tsx +25 -2
  345. package/src/ui-tailwind/review/comment-markdown-renderer.tsx +15 -0
  346. package/src/ui-tailwind/review/tw-review-rail.tsx +92 -4
  347. package/src/ui-tailwind/review/tw-workflow-tab.tsx +1 -1
  348. package/src/ui-tailwind/review-workspace/page-chrome.ts +210 -0
  349. package/src/ui-tailwind/review-workspace/page-shell-metrics.ts +101 -0
  350. package/src/ui-tailwind/review-workspace/paragraph-layout.ts +115 -0
  351. package/src/ui-tailwind/review-workspace/selection-toolbar-placement.ts +97 -0
  352. package/src/ui-tailwind/review-workspace/tw-review-workspace-navigator.tsx +130 -0
  353. package/src/ui-tailwind/review-workspace/tw-review-workspace-page-toolbar.tsx +240 -0
  354. package/src/ui-tailwind/review-workspace/tw-review-workspace-rail.tsx +59 -0
  355. package/src/ui-tailwind/review-workspace/types.ts +408 -0
  356. package/src/ui-tailwind/review-workspace/use-chrome-policy.ts +104 -0
  357. package/src/ui-tailwind/review-workspace/use-derived-view-state.ts +151 -0
  358. package/src/ui-tailwind/review-workspace/use-diagnostics-signal.ts +70 -0
  359. package/src/ui-tailwind/review-workspace/use-grabbed-segment-offsets.ts +40 -0
  360. package/src/ui-tailwind/review-workspace/use-layout-facet-render-signal.ts +55 -0
  361. package/src/ui-tailwind/review-workspace/use-page-markers.ts +130 -0
  362. package/src/ui-tailwind/review-workspace/use-pm-surface-capture.ts +60 -0
  363. package/src/ui-tailwind/review-workspace/use-review-rail-state.ts +63 -0
  364. package/src/ui-tailwind/review-workspace/use-scope-card-state.ts +170 -0
  365. package/src/ui-tailwind/review-workspace/use-scroll-root-capture.ts +28 -0
  366. package/src/ui-tailwind/review-workspace/use-selection-toolbar-placement.ts +113 -0
  367. package/src/ui-tailwind/review-workspace/use-shell-selection-anchor-bridge.ts +120 -0
  368. package/src/ui-tailwind/review-workspace/use-status-bar-page-facts.ts +55 -0
  369. package/src/ui-tailwind/review-workspace/use-viewport-dimensions.ts +43 -0
  370. package/src/ui-tailwind/review-workspace/use-workspace-arbiter.ts +25 -0
  371. package/src/ui-tailwind/review-workspace/use-workspace-composition.ts +86 -0
  372. package/src/ui-tailwind/review-workspace/use-workspace-side-effects.ts +150 -0
  373. package/src/ui-tailwind/theme/editor-theme.css +25 -0
  374. package/src/ui-tailwind/toolbar/tw-role-action-region.tsx +2 -2
  375. package/src/ui-tailwind/toolbar/tw-toolbar.tsx +61 -98
  376. package/src/ui-tailwind/tw-review-workspace.tsx +521 -1802
  377. package/src/ui-tailwind/ui-api-context.tsx +43 -0
  378. package/src/ui-tailwind/ui-shell-channels-context.tsx +49 -0
  379. package/src/validation/compatibility-engine.ts +6 -6
  380. package/src/runtime/styles-cascade.ts +0 -33
  381. package/src/ui-tailwind/chrome/tw-mode-dock.tsx +0 -85
  382. /package/src/runtime/{page-number-format.ts → formatting/field/page-number-format.ts} +0 -0
  383. /package/src/runtime/{ai-action-policy.ts → workflow/ai-action-policy.ts} +0 -0
  384. /package/src/runtime/{scope-tag-registry.ts → workflow/scope-tag-registry.ts} +0 -0
@@ -0,0 +1,684 @@
1
+ /**
2
+ * `runStatefulExport` — session-layer owner of the stateful export
3
+ * pipeline.
4
+ *
5
+ * Slice 5e-7.f relocates the body of `exportDocxEditorSession` from
6
+ * `src/io/docx-session.ts` into this module. Legacy callers reach the
7
+ * pipeline through `LoadedDocxEditorSession.exportDocx` (closure
8
+ * captured at open time by the session-layer loader) or directly via
9
+ * `DocxSession.export()` — no public `exportDocxEditorSession` export
10
+ * exists anymore; the function was private to `docx-session.ts`.
11
+ *
12
+ * Stateful: the pipeline needs the `ImportedDocxState` captured at
13
+ * open time (source bytes, `OpcPackage`, per-part relationships,
14
+ * preserved-part manifest, `sourceDocumentRelationships`,
15
+ * `sourceDocumentAttributes`, `initialCanonicalSignature`, etc.). The
16
+ * byte-reuse fast path (signature match + `canReuseSourceBytesForCurrentDocument`)
17
+ * returns the original bytes unchanged when no edits have been made.
18
+ */
19
+
20
+ import type {
21
+ EditorSessionState,
22
+ ExportDocxOptions,
23
+ ExportResult,
24
+ PersistedEditorSnapshot,
25
+ } from "../../api/public-types.ts";
26
+ import type {
27
+ CanonicalDocument,
28
+ MediaCatalog,
29
+ NumberingCatalog,
30
+ SubPartsCatalog,
31
+ } from "../../model/canonical-document.ts";
32
+ import {
33
+ commentRecordsToReviewThreads,
34
+ } from "../../model/review/comment-types.ts";
35
+ import {
36
+ getRevisionActionability,
37
+ } from "../../model/review/revision-types.ts";
38
+ import { DOCX_MIME_TYPE } from "../../io/opc/docx-package.ts";
39
+ import { createExportSession } from "../../io/export/export-session.ts";
40
+ import { serializeMainDocument } from "../../io/export/serialize-main-document.ts";
41
+ import {
42
+ serializeSettingsXml,
43
+ WORD_SETTINGS_CONTENT_TYPE,
44
+ } from "../../io/export/serialize-settings.ts";
45
+ import {
46
+ createCommentExportIdMap,
47
+ serializeCommentAnchorsIntoDocumentXml,
48
+ serializeMergedCommentsXml,
49
+ } from "../../io/export/serialize-comments.ts";
50
+ import { splitDocumentAtReviewBoundaries } from "../../io/export/split-review-boundaries.ts";
51
+ import { splitStoryBlocksForRuntimeRevisions } from "../../io/export/split-story-blocks-for-runtime-revisions.ts";
52
+ import {
53
+ serializeRuntimeRevisionsIntoDocumentXml,
54
+ } from "../../io/export/serialize-runtime-revisions.ts";
55
+ import {
56
+ WORD_NUMBERING_CONTENT_TYPE,
57
+ hasSerializableNumberingEntries,
58
+ serializeNumberingXml,
59
+ } from "../../io/export/serialize-numbering.ts";
60
+ import {
61
+ serializeHeaderXmlWithRevisions,
62
+ serializeFooterXmlWithRevisions,
63
+ WORD_HEADER_CONTENT_TYPE,
64
+ WORD_FOOTER_CONTENT_TYPE,
65
+ } from "../../io/export/serialize-headers-footers.ts";
66
+ import {
67
+ serializeFootnotesXml,
68
+ serializeEndnotesXml,
69
+ WORD_FOOTNOTES_CONTENT_TYPE,
70
+ WORD_ENDNOTES_CONTENT_TYPE,
71
+ } from "../../io/export/serialize-footnotes.ts";
72
+ import {
73
+ getDocumentBackedWorkflowMetadata,
74
+ resolvePayloadPartPath,
75
+ resolveWorkflowPayloadPartPaths,
76
+ WORKFLOW_PAYLOAD_CUSTOM_PROPS_PART_PATH,
77
+ } from "../../io/ooxml/workflow-payload.ts";
78
+ import {
79
+ serializeSecondaryStoryWithRuntimeRevisions,
80
+ stripCommentMarkup,
81
+ toReviewRevisionRecords,
82
+ } from "../import/review-import.ts";
83
+ import { toEditorSessionState } from "../import/canonical-assembly.ts";
84
+ import {
85
+ serializeCanonicalDocumentForExport,
86
+ } from "../shared/session-utils.ts";
87
+ import { withDocumentRelatedParts } from "../import/package-parts.ts";
88
+ import {
89
+ APP_PROPERTIES_PART_PATH,
90
+ CORE_PROPERTIES_PART_PATH,
91
+ canReuseSourceBytesForCurrentDocument,
92
+ ensureHostMetadataParts,
93
+ ensureWorkflowPayloadParts,
94
+ serializeProtectionRangesIntoDocumentXml,
95
+ } from "./stateful-export-pipeline.ts";
96
+ import {
97
+ assertExportNotBlockedByCompatibility,
98
+ assertNoBlockingPreservedComments,
99
+ assertNoBlockingSkippedComments,
100
+ assertNoSkippedRevisions,
101
+ } from "./export-validation.ts";
102
+ import {
103
+ groupFooterRevisionsByPartPath,
104
+ groupHeaderRevisionsByPartPath,
105
+ groupNoteRevisionsByNoteId,
106
+ } from "./serialize-dispatch.ts";
107
+ import {
108
+ COMMENTS_CONTENT_TYPE,
109
+ COMMENTS_EXTENDED_CONTENT_TYPE,
110
+ COMMENTS_EXTENDED_PART_PATH,
111
+ COMMENTS_EXTENDED_RELATIONSHIP_TYPE,
112
+ COMMENTS_IDS_CONTENT_TYPE,
113
+ COMMENTS_IDS_PART_PATH,
114
+ COMMENTS_IDS_RELATIONSHIP_TYPE,
115
+ MAIN_DOCUMENT_CONTENT_TYPE,
116
+ NUMBERING_PART_PATH,
117
+ NUMBERING_RELATIONSHIP_TYPE,
118
+ PEOPLE_CONTENT_TYPE,
119
+ PEOPLE_PART_PATH,
120
+ PEOPLE_RELATIONSHIP_TYPE,
121
+ SETTINGS_PART_PATH,
122
+ } from "../import/part-constants.ts";
123
+ import {
124
+ COMMENTS_PART_PATH,
125
+ COMMENTS_RELATIONSHIP_TYPE,
126
+ } from "../import/part-discovery.ts";
127
+ import type { ImportedDocxState } from "../import/loader-types.ts";
128
+ import {
129
+ buildEmbeddingsInlinePayload,
130
+ type EmbeddingOffloadEntry,
131
+ } from "../import/embedded-offload.ts";
132
+ import { reconstituteEmbeddedDocuments } from "./embedded-reconstitute.ts";
133
+
134
+ /**
135
+ * Stateful export orchestrator. Body is byte-for-byte the pre-5e-7.f
136
+ * `exportDocxEditorSession` that was private to
137
+ * `src/io/docx-session.ts`; only the imports were redirected to
138
+ * session-layer homes.
139
+ */
140
+ export async function runStatefulExport(
141
+ state: ImportedDocxState,
142
+ sessionStateOrSnapshot: EditorSessionState | PersistedEditorSnapshot,
143
+ options?: ExportDocxOptions,
144
+ ): Promise<ExportResult> {
145
+ const sessionState = toEditorSessionState(sessionStateOrSnapshot);
146
+
147
+ assertExportNotBlockedByCompatibility(sessionState.compatibility);
148
+
149
+ const currentDocument = sessionState.canonicalDocument as CanonicalDocument;
150
+ const signatureMatch = serializeCanonicalDocumentForExport(currentDocument) ===
151
+ state.initialCanonicalSignature;
152
+ const canReuse = canReuseSourceBytesForCurrentDocument(state, currentDocument);
153
+ const durableWorkflowMetadata = getDocumentBackedWorkflowMetadata(sessionState.workflowMetadata);
154
+ const hasWorkflowOverlay = Boolean(sessionState.workflowOverlay);
155
+ const sourceHasWorkflowPayload = resolvePayloadPartPath(state.sourcePackage) !== null;
156
+ // Schema 1.2: runtime may have registered metadata entries via
157
+ // `setWorkflowMetadataEntries` (or `addScope({persistence:'document-metadata'})`)
158
+ // that don't pass the document-backed filter but still represent session
159
+ // state that must survive export → reload via the editorState namespace.
160
+ // W10: class-A overlay-visibility policies also force a rewrite —
161
+ // otherwise authored policy set via `handle.workflow.setVisibilityPolicy`
162
+ // on an unchanged document silently drops on export.
163
+ const hasRuntimeWorkflowMetadata = Boolean(
164
+ (sessionState.workflowMetadata?.definitions.length ?? 0) > 0 ||
165
+ (sessionState.workflowMetadata?.entries.length ?? 0) > 0 ||
166
+ (sessionState.visibilityPolicies?.length ?? 0) > 0 ||
167
+ sessionState.markupModePolicy != null,
168
+ );
169
+ const commentCount = Object.keys(currentDocument.review?.comments ?? {}).length;
170
+ const currentRevisions = toReviewRevisionRecords(currentDocument.review.revisions);
171
+ const hasActiveImportedPreserveOnlyRevisions = currentRevisions.some(
172
+ (revision) =>
173
+ revision.status === "active" &&
174
+ revision.metadata.source === "import" &&
175
+ typeof revision.metadata.preserveOnlyReason === "string" &&
176
+ revision.metadata.preserveOnlyReason.length > 0,
177
+ );
178
+
179
+ if (
180
+ signatureMatch &&
181
+ canReuse &&
182
+ durableWorkflowMetadata.definitions.length === 0 &&
183
+ durableWorkflowMetadata.entries.length === 0 &&
184
+ !hasWorkflowOverlay &&
185
+ !sourceHasWorkflowPayload &&
186
+ !hasRuntimeWorkflowMetadata
187
+ ) {
188
+ return {
189
+ bytes: new Uint8Array(state.sourceBytes),
190
+ mimeType: DOCX_MIME_TYPE,
191
+ fileName: options?.fileName ?? `${sessionState.documentId}.docx`,
192
+ delivery: {
193
+ mode: "exported-bytes-only",
194
+ },
195
+ };
196
+ }
197
+ const preservedCommentIds = new Set(
198
+ state.preservedCommentDefinitions.map((definition) => definition.commentId),
199
+ );
200
+ const blockingCommentCount = Math.max(
201
+ state.blockingCommentDiagnostics.length,
202
+ preservedCommentIds.size,
203
+ );
204
+ assertNoBlockingPreservedComments(
205
+ blockingCommentCount,
206
+ [...preservedCommentIds],
207
+ );
208
+ const actionableRevisions = currentRevisions.filter(
209
+ (revision) => getRevisionActionability(revision) === "actionable",
210
+ );
211
+ const mainStoryActionableRevisions = actionableRevisions.filter((revision) =>
212
+ !revision.metadata.storyTarget?.kind || revision.metadata.storyTarget.kind === "main"
213
+ );
214
+ const commentThreads = commentRecordsToReviewThreads(
215
+ currentDocument.review.comments,
216
+ );
217
+ const ownedCommentThreads = commentThreads.filter(
218
+ (thread) => !preservedCommentIds.has(thread.commentId),
219
+ );
220
+ const revisionReadyMainContent = {
221
+ ...currentDocument.content,
222
+ children: splitStoryBlocksForRuntimeRevisions(
223
+ currentDocument.content.children,
224
+ mainStoryActionableRevisions,
225
+ ),
226
+ };
227
+ const serialized = serializeMainDocument(
228
+ splitDocumentAtReviewBoundaries(
229
+ revisionReadyMainContent as never,
230
+ ownedCommentThreads,
231
+ mainStoryActionableRevisions,
232
+ ) as never,
233
+ currentDocument.preservation as never,
234
+ state.sourceDocumentRelationships,
235
+ {
236
+ documentAttributes: state.sourceDocumentAttributes,
237
+ media: currentDocument.media as MediaCatalog,
238
+ finalSectionProperties: currentDocument.subParts?.finalSectionProperties,
239
+ namespaceFlavor: options?.exportStrictOoxml ? "strict" : "transitional",
240
+ },
241
+ );
242
+ const revisionDocument = serializeRuntimeRevisionsIntoDocumentXml(
243
+ serialized.documentXml,
244
+ mainStoryActionableRevisions,
245
+ );
246
+ assertNoSkippedRevisions(revisionDocument.skippedRevisionIds);
247
+
248
+ const strippedDocumentXml = stripCommentMarkup(
249
+ revisionDocument.documentXml,
250
+ ownedCommentThreads.map((thread) => thread.commentId),
251
+ );
252
+ const exportCommentIds = createCommentExportIdMap(
253
+ ownedCommentThreads,
254
+ state.preservedCommentDefinitions,
255
+ );
256
+ const serializedComments = serializeMergedCommentsXml(ownedCommentThreads, {
257
+ exportCommentIds,
258
+ preservedDefinitions: state.preservedCommentDefinitions,
259
+ sourceRootTag: state.sourceCommentsRootTag,
260
+ sourceExtendedRootTag: state.sourceCommentsExtendedRootTag,
261
+ sourceIdsRootTag: state.sourceCommentsIdsRootTag,
262
+ sourcePeopleRootTag: state.sourcePeopleRootTag,
263
+ peopleAuthors: state.sourcePeopleAuthors,
264
+ });
265
+ const annotatedDocument = serializeCommentAnchorsIntoDocumentXml(
266
+ strippedDocumentXml,
267
+ ownedCommentThreads,
268
+ undefined,
269
+ {
270
+ exportCommentIds,
271
+ },
272
+ );
273
+ const protectedDocumentXml = serializeProtectionRangesIntoDocumentXml(
274
+ annotatedDocument.documentXml,
275
+ state.protectionSnapshot,
276
+ );
277
+ const blockingSkippedCommentIds = annotatedDocument.skippedCommentIds.filter((commentId) => {
278
+ const thread = ownedCommentThreads.find((candidate) => candidate.commentId === commentId);
279
+ return !thread || thread.anchor.kind !== "detached";
280
+ });
281
+ assertNoBlockingSkippedComments(blockingSkippedCommentIds);
282
+ const commentsPartPath =
283
+ state.sourceCommentsPartPath ?? COMMENTS_PART_PATH;
284
+ const commentsExtendedPartPath =
285
+ state.sourceCommentsExtendedPartPath ?? COMMENTS_EXTENDED_PART_PATH;
286
+ const commentsIdsPartPath =
287
+ state.sourceCommentsIdsPartPath ?? COMMENTS_IDS_PART_PATH;
288
+ const peoplePartPath =
289
+ state.sourcePeoplePartPath ?? PEOPLE_PART_PATH;
290
+ const numberingPartPath =
291
+ state.sourceNumberingPartPath ?? NUMBERING_PART_PATH;
292
+ const serializedNumberingXml = hasSerializableNumberingEntries(
293
+ currentDocument.numbering as NumberingCatalog,
294
+ )
295
+ ? serializeNumberingXml(currentDocument.numbering as NumberingCatalog)
296
+ : undefined;
297
+ const nextRelationships = withDocumentRelatedParts(
298
+ serialized.relationships,
299
+ [
300
+ {
301
+ relationshipType: NUMBERING_RELATIONSHIP_TYPE,
302
+ partPath: numberingPartPath,
303
+ existingRelationshipId: state.sourceNumberingRelationshipId,
304
+ include:
305
+ Boolean(serializedNumberingXml) ||
306
+ Boolean(state.sourceNumberingPartPath),
307
+ },
308
+ {
309
+ relationshipType: COMMENTS_RELATIONSHIP_TYPE,
310
+ partPath: commentsPartPath,
311
+ existingRelationshipId: state.sourceCommentsRelationshipId,
312
+ include:
313
+ serializedComments.serializedCommentIds.length > 0 ||
314
+ Boolean(state.sourceCommentsPartPath),
315
+ },
316
+ {
317
+ relationshipType: COMMENTS_EXTENDED_RELATIONSHIP_TYPE,
318
+ partPath: commentsExtendedPartPath,
319
+ existingRelationshipId: state.sourceCommentsExtendedRelationshipId,
320
+ include:
321
+ Boolean(serializedComments.commentsExtendedXml) ||
322
+ Boolean(state.sourceCommentsExtendedPartPath),
323
+ },
324
+ {
325
+ relationshipType: COMMENTS_IDS_RELATIONSHIP_TYPE,
326
+ partPath: commentsIdsPartPath,
327
+ existingRelationshipId: state.sourceCommentsIdsRelationshipId,
328
+ include:
329
+ Boolean(serializedComments.commentsIdsXml) ||
330
+ Boolean(state.sourceCommentsIdsPartPath),
331
+ },
332
+ {
333
+ relationshipType: PEOPLE_RELATIONSHIP_TYPE,
334
+ partPath: peoplePartPath,
335
+ existingRelationshipId: state.sourcePeopleRelationshipId,
336
+ include:
337
+ Boolean(serializedComments.peopleXml) ||
338
+ Boolean(state.sourcePeoplePartPath),
339
+ },
340
+ ],
341
+ );
342
+
343
+ const exportedSubParts = currentDocument.subParts as SubPartsCatalog | undefined;
344
+ const subPartOwnedPaths: string[] = [];
345
+ if (exportedSubParts) {
346
+ for (const header of exportedSubParts.headers) {
347
+ subPartOwnedPaths.push(header.partPath);
348
+ }
349
+ for (const footer of exportedSubParts.footers) {
350
+ subPartOwnedPaths.push(footer.partPath);
351
+ }
352
+ if (exportedSubParts.footnoteCollection) {
353
+ if (state.sourceSubPartPaths.footnotesPartPath) {
354
+ subPartOwnedPaths.push(state.sourceSubPartPaths.footnotesPartPath);
355
+ }
356
+ if (state.sourceSubPartPaths.endnotesPartPath) {
357
+ subPartOwnedPaths.push(state.sourceSubPartPaths.endnotesPartPath);
358
+ }
359
+ }
360
+ if (exportedSubParts.theme && state.sourceSubPartPaths.themePartPath) {
361
+ subPartOwnedPaths.push(state.sourceSubPartPaths.themePartPath);
362
+ }
363
+ }
364
+
365
+ // Settings.xml is owned when the source package carried one OR the canonical
366
+ // model carries settings we need to re-emit. The `canReuse && signatureMatch`
367
+ // short-circuit above already skips re-export entirely for no-edit sessions,
368
+ // so every path that reaches here is willing to emit a rebuilt settings.xml.
369
+ const settingsPartPath =
370
+ state.sourceSettingsPartPath ?? SETTINGS_PART_PATH;
371
+ const hasSettingsSurface =
372
+ Boolean(state.sourceSettingsPartPath) ||
373
+ exportedSubParts?.settings !== undefined;
374
+ const workflowPayloadPartPaths = resolveWorkflowPayloadPartPaths(
375
+ state.sourcePackage,
376
+ sessionState.documentId,
377
+ );
378
+ const internalEditorState = (
379
+ options as { _editorState?: import("../../io/ooxml/workflow-payload.ts").EditorStatePayload } | undefined
380
+ )?._editorState;
381
+
382
+ // P8 Step 7: narrow the opaque `state.embeddingOffloadEntries` list
383
+ // (typed `readonly unknown[]` on `ImportedDocxState` to keep the
384
+ // session-layer types file P6-clean) into the concrete entry shape
385
+ // the reconstitute pass needs. The entries were already validated
386
+ // by `hydrateOffloadEntriesFromPayload` or produced by
387
+ // `processEmbeddedOffload` — they are structurally sound.
388
+ const offloadEntries = state.embeddingOffloadEntries as readonly EmbeddingOffloadEntry[];
389
+ const offloadOwnedPaths = offloadEntries.map((entry) => entry.sourcePartPath);
390
+
391
+ const exportSession = createExportSession(state.sourcePackage, [
392
+ state.sourceDocumentPartPath,
393
+ APP_PROPERTIES_PART_PATH,
394
+ CORE_PROPERTIES_PART_PATH,
395
+ workflowPayloadPartPaths.payloadPartPath,
396
+ workflowPayloadPartPaths.itemPropsPartPath,
397
+ WORKFLOW_PAYLOAD_CUSTOM_PROPS_PART_PATH,
398
+ numberingPartPath,
399
+ commentsPartPath,
400
+ commentsExtendedPartPath,
401
+ commentsIdsPartPath,
402
+ peoplePartPath,
403
+ ...(hasSettingsSurface ? [settingsPartPath] : []),
404
+ ...subPartOwnedPaths,
405
+ ...offloadOwnedPaths,
406
+ ]);
407
+
408
+ const mainDocumentXmlForExport =
409
+ signatureMatch &&
410
+ durableWorkflowMetadata.definitions.length === 0 &&
411
+ durableWorkflowMetadata.entries.length === 0 &&
412
+ !hasWorkflowOverlay &&
413
+ commentCount === 0 &&
414
+ hasActiveImportedPreserveOnlyRevisions
415
+ ? state.sourceDocumentXml
416
+ : protectedDocumentXml;
417
+
418
+ exportSession.replaceOwnedPart({
419
+ path: state.sourceDocumentPartPath,
420
+ bytes: new TextEncoder().encode(mainDocumentXmlForExport),
421
+ contentType: MAIN_DOCUMENT_CONTENT_TYPE,
422
+ relationships: nextRelationships,
423
+ });
424
+
425
+ if (serializedNumberingXml || state.sourceNumberingPartPath) {
426
+ exportSession.replaceOwnedPart({
427
+ path: numberingPartPath,
428
+ bytes: new TextEncoder().encode(
429
+ serializedNumberingXml ??
430
+ `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n<w:numbering xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main"></w:numbering>`,
431
+ ),
432
+ contentType:
433
+ state.sourcePackage.parts.get(numberingPartPath)?.contentType ??
434
+ WORD_NUMBERING_CONTENT_TYPE,
435
+ });
436
+ }
437
+
438
+ if (hasSettingsSurface) {
439
+ // Canonical settings ∅ + no source settings → omit the owned-part write
440
+ // (hasSettingsSurface is already false in that case). Otherwise route
441
+ // through the graft serializer so unmodelled children round-trip via
442
+ // source bytes while canonical mutations land.
443
+ const canonicalSettings = exportedSubParts?.settings ?? {};
444
+ const settingsXml = serializeSettingsXml(canonicalSettings, state.sourceSettingsXml);
445
+ exportSession.replaceOwnedPart({
446
+ path: settingsPartPath,
447
+ bytes: new TextEncoder().encode(settingsXml),
448
+ contentType:
449
+ state.sourcePackage.parts.get(settingsPartPath)?.contentType ??
450
+ WORD_SETTINGS_CONTENT_TYPE,
451
+ });
452
+ }
453
+
454
+ if (serializedComments.serializedCommentIds.length > 0 || state.sourceCommentsPartPath) {
455
+ exportSession.replaceOwnedPart({
456
+ path: commentsPartPath,
457
+ bytes: new TextEncoder().encode(serializedComments.commentsXml),
458
+ contentType:
459
+ state.sourcePackage.parts.get(commentsPartPath)?.contentType ?? COMMENTS_CONTENT_TYPE,
460
+ });
461
+ }
462
+
463
+ if (serializedComments.commentsExtendedXml || state.sourceCommentsExtendedPartPath) {
464
+ exportSession.replaceOwnedPart({
465
+ path: commentsExtendedPartPath,
466
+ bytes: new TextEncoder().encode(
467
+ serializedComments.commentsExtendedXml ?? `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n<w15:commentsEx xmlns:w15="http://schemas.microsoft.com/office/word/2012/wordml"></w15:commentsEx>`,
468
+ ),
469
+ contentType:
470
+ state.sourcePackage.parts.get(commentsExtendedPartPath)?.contentType ??
471
+ COMMENTS_EXTENDED_CONTENT_TYPE,
472
+ });
473
+ }
474
+
475
+ if (serializedComments.commentsIdsXml || state.sourceCommentsIdsPartPath) {
476
+ exportSession.replaceOwnedPart({
477
+ path: commentsIdsPartPath,
478
+ bytes: new TextEncoder().encode(
479
+ serializedComments.commentsIdsXml ?? `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n<w16cid:commentsIds xmlns:w16cid="http://schemas.microsoft.com/office/word/2016/wordml/cid"></w16cid:commentsIds>`,
480
+ ),
481
+ contentType:
482
+ state.sourcePackage.parts.get(commentsIdsPartPath)?.contentType ??
483
+ COMMENTS_IDS_CONTENT_TYPE,
484
+ });
485
+ }
486
+
487
+ if (serializedComments.peopleXml || state.sourcePeoplePartPath) {
488
+ exportSession.replaceOwnedPart({
489
+ path: peoplePartPath,
490
+ bytes: new TextEncoder().encode(
491
+ serializedComments.peopleXml ?? `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n<w15:people xmlns:w15="http://schemas.microsoft.com/office/word/2012/wordml"></w15:people>`,
492
+ ),
493
+ contentType:
494
+ state.sourcePackage.parts.get(peoplePartPath)?.contentType ??
495
+ PEOPLE_CONTENT_TYPE,
496
+ });
497
+ }
498
+
499
+ if (exportedSubParts) {
500
+ const headersByPartPath = groupHeaderRevisionsByPartPath(
501
+ exportedSubParts.headers,
502
+ actionableRevisions,
503
+ (revision) => revision.metadata.storyTarget,
504
+ );
505
+ for (const { header, revisions } of headersByPartPath.values()) {
506
+ const serializedHeaderXml = serializeSecondaryStoryWithRuntimeRevisions(
507
+ serializeHeaderXmlWithRevisions(header, revisions),
508
+ revisions,
509
+ `header ${header.partPath}`,
510
+ );
511
+ exportSession.replaceOwnedPart({
512
+ path: header.partPath,
513
+ bytes: new TextEncoder().encode(serializedHeaderXml),
514
+ contentType:
515
+ state.sourcePackage.parts.get(header.partPath)?.contentType ?? WORD_HEADER_CONTENT_TYPE,
516
+ });
517
+ }
518
+ const footersByPartPath = groupFooterRevisionsByPartPath(
519
+ exportedSubParts.footers,
520
+ actionableRevisions,
521
+ (revision) => revision.metadata.storyTarget,
522
+ );
523
+ for (const { footer, revisions } of footersByPartPath.values()) {
524
+ const serializedFooterXml = serializeSecondaryStoryWithRuntimeRevisions(
525
+ serializeFooterXmlWithRevisions(footer, revisions),
526
+ revisions,
527
+ `footer ${footer.partPath}`,
528
+ );
529
+ exportSession.replaceOwnedPart({
530
+ path: footer.partPath,
531
+ bytes: new TextEncoder().encode(serializedFooterXml),
532
+ contentType:
533
+ state.sourcePackage.parts.get(footer.partPath)?.contentType ?? WORD_FOOTER_CONTENT_TYPE,
534
+ });
535
+ }
536
+ if (exportedSubParts.footnoteCollection) {
537
+ if (state.sourceSubPartPaths.footnotesPartPath) {
538
+ const serializedFootnotesXml = serializeFootnotesXml(
539
+ exportedSubParts.footnoteCollection,
540
+ groupNoteRevisionsByNoteId(
541
+ actionableRevisions,
542
+ "footnote",
543
+ (revision) => revision.metadata.storyTarget,
544
+ ),
545
+ );
546
+ exportSession.replaceOwnedPart({
547
+ path: state.sourceSubPartPaths.footnotesPartPath,
548
+ bytes: new TextEncoder().encode(serializedFootnotesXml),
549
+ contentType:
550
+ state.sourcePackage.parts.get(state.sourceSubPartPaths.footnotesPartPath)?.contentType ??
551
+ WORD_FOOTNOTES_CONTENT_TYPE,
552
+ });
553
+ }
554
+ if (state.sourceSubPartPaths.endnotesPartPath) {
555
+ const serializedEndnotesXml = serializeEndnotesXml(
556
+ exportedSubParts.footnoteCollection,
557
+ groupNoteRevisionsByNoteId(
558
+ actionableRevisions,
559
+ "endnote",
560
+ (revision) => revision.metadata.storyTarget,
561
+ ),
562
+ );
563
+ exportSession.replaceOwnedPart({
564
+ path: state.sourceSubPartPaths.endnotesPartPath,
565
+ bytes: new TextEncoder().encode(serializedEndnotesXml),
566
+ contentType:
567
+ state.sourcePackage.parts.get(state.sourceSubPartPaths.endnotesPartPath)?.contentType ??
568
+ WORD_ENDNOTES_CONTENT_TYPE,
569
+ });
570
+ }
571
+ }
572
+ if (exportedSubParts.theme && state.sourceSubPartPaths.themePartPath) {
573
+ const sourceThemePart = state.sourcePackage.parts.get(state.sourceSubPartPaths.themePartPath);
574
+ if (sourceThemePart) {
575
+ exportSession.replaceOwnedPart({
576
+ path: state.sourceSubPartPaths.themePartPath,
577
+ bytes: sourceThemePart.bytes,
578
+ contentType:
579
+ sourceThemePart.contentType ??
580
+ "application/vnd.openxmlformats-officedocument.theme+xml",
581
+ relationships: sourceThemePart.relationships,
582
+ compression: sourceThemePart.compression,
583
+ });
584
+ }
585
+ }
586
+ }
587
+
588
+ ensureHostMetadataParts(exportSession, state.sourcePackage, currentDocument);
589
+
590
+ // P8 Step 7: reconstitute offloaded embedded documents back into
591
+ // the OPC package. For each entry captured at open time, try the
592
+ // host's `loadEmbeddedDocument` first; on null / rejection / throw,
593
+ // fall back to the inline-bytes base64 copy the entry carries. The
594
+ // source part paths were added to the export-session owned list
595
+ // above so `replaceOwnedPart` is valid. No-op when `offloadEntries`
596
+ // is empty (no offload occurred or the package carried no
597
+ // offloadable embeddings).
598
+ await reconstituteEmbeddedDocuments({
599
+ exportSession,
600
+ entries: offloadEntries,
601
+ hostAdapter: state.hostAdapter,
602
+ });
603
+
604
+ // P8 Step 7: fold the offload entries into the editor-state payload
605
+ // so the `bw:embeddings` customXml namespace round-trips to the
606
+ // next open. When no offload happened we pass through whatever the
607
+ // runtime collected unchanged.
608
+ const editorStateWithEmbeddings = extendEditorStateWithEmbeddings(
609
+ internalEditorState,
610
+ offloadEntries,
611
+ );
612
+
613
+ // Schema 1.2: pass through editorState payload collected by the
614
+ // runtime channel, with any offload entries folded in above.
615
+ ensureWorkflowPayloadParts(
616
+ exportSession,
617
+ sessionState,
618
+ currentDocument,
619
+ state.sourcePackage,
620
+ workflowPayloadPartPaths,
621
+ editorStateWithEmbeddings,
622
+ );
623
+
624
+ return {
625
+ bytes: exportSession.serialize(),
626
+ mimeType: DOCX_MIME_TYPE,
627
+ fileName: options?.fileName ?? `${sessionState.documentId}.docx`,
628
+ delivery: {
629
+ mode: "exported-bytes-only",
630
+ },
631
+ };
632
+ }
633
+
634
+ /**
635
+ * Fold P8 offload entries into the editor-state payload that
636
+ * `ensureWorkflowPayloadParts` writes into `customXml/item1.xml`.
637
+ *
638
+ * Schema: the `"embeddings"` namespace entry carries an inline JSON
639
+ * blob matching `EmbeddingsInlineSchema` (shape:
640
+ * `{ version: 1, entries: EmbeddingOffloadEntry[] }`). On reopen,
641
+ * `hydrateOffloadEntriesFromPayload` parses it back and the session
642
+ * skips the fresh `storeEmbeddedDocument` pass.
643
+ *
644
+ * Three cases:
645
+ * 1. No offload entries AND no existing payload — return undefined;
646
+ * nothing to write, the customXml namespace is omitted.
647
+ * 2. No offload entries BUT there is a runtime-supplied payload —
648
+ * pass it through unchanged.
649
+ * 3. Offload entries present — ensure the payload has an
650
+ * `"embeddings"` namespace entry with the inline schema. Any
651
+ * runtime-supplied `"embeddings"` entry is replaced (the
652
+ * session owns this namespace per §P8).
653
+ */
654
+ function extendEditorStateWithEmbeddings(
655
+ payload:
656
+ | import("../../io/ooxml/workflow-payload.ts").EditorStatePayload
657
+ | undefined,
658
+ offloadEntries: readonly EmbeddingOffloadEntry[],
659
+ ):
660
+ | import("../../io/ooxml/workflow-payload.ts").EditorStatePayload
661
+ | undefined {
662
+ if (offloadEntries.length === 0) {
663
+ return payload;
664
+ }
665
+ const inlineSchema = buildEmbeddingsInlinePayload(offloadEntries);
666
+ const embeddingsEntry: import(
667
+ "../../io/ooxml/workflow-payload.ts"
668
+ ).EditorStatePayloadNamespaceEntry = {
669
+ namespace: "embeddings",
670
+ schemaVersion: "bw-embeddings/1",
671
+ inline: inlineSchema,
672
+ };
673
+ if (!payload) {
674
+ return { entries: [embeddingsEntry] };
675
+ }
676
+ const filtered = payload.entries.filter(
677
+ (entry) => entry.namespace !== "embeddings",
678
+ );
679
+ const next = {
680
+ ...payload,
681
+ entries: [...filtered, embeddingsEntry],
682
+ };
683
+ return next;
684
+ }