@beyondwork/docx-react-component 1.0.67 → 1.0.70

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 -932
  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 -4797
  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,532 @@
1
+ /**
2
+ * `DocxSession` — the reserved Layer-01 public entry class.
3
+ *
4
+ * Shape matches `docs/architecture/01-package-session.md` § Public
5
+ * entry points:
6
+ *
7
+ * open(bytes, opts?) → Promise<OpenResult>
8
+ * export(doc, opts?) → Promise<ExportResult>
9
+ * validate(doc, opts?) → ValidationReport
10
+ *
11
+ * Slice-4 state: `open()` is wired and additionally attaches a
12
+ * module-private legacy view (accessed via
13
+ * `src/session/_internal-legacy.ts`) for in-tree services that still
14
+ * need the stateful `exportDocx` closure. `export()` and `validate()`
15
+ * throw `SessionNotWiredError` — the architecture reserves them as
16
+ * stateless, which requires a new pipeline that lands in Slice 5.
17
+ *
18
+ * P6 discipline: `src/session` must not import from `src/runtime`,
19
+ * `src/ui` / `src/ui-tailwind`, `src/core`, or `src/review`. This file transits
20
+ * `src/io/docx-session.ts` (legacy integration orchestrator) under a
21
+ * documented debt exception — the legacy file itself still imports
22
+ * forbidden runtime/review surfaces (see `src/io/docx-session.ts:35–43`
23
+ * and architecture doc §P6 "debt exception"). The exception closes in
24
+ * Slice 5 when the legacy file is deleted; the purity checker warns
25
+ * (not errors) on this specific transit, and a session-transit test
26
+ * pins the exception so any accidental reintroduction is caught.
27
+ */
28
+
29
+ import type { CanonicalDocument } from "../model/canonical-document.ts";
30
+ import { buildCompatibilityReport } from "../validation/compatibility-engine.ts";
31
+ // Slice 5e-8.a: session.ts now reaches the load pipeline directly
32
+ // via `src/session/import/loader.ts` (in-layer). The legacy transit
33
+ // through `src/io/docx-session.ts` is retired along with the
34
+ // debt-exception entry for this file in the purity CI guard.
35
+ import { loadDocxSessionAsync } from "./import/loader.ts";
36
+ import type { LoadDocxEditorSessionAsyncOptions } from "./import/loader-types.ts";
37
+ import { createLoadScheduler } from "../io/load-scheduler.ts";
38
+ // P6 debt exception: the laycache probe lives in
39
+ // `src/runtime/prerender/` because its `CacheEnvelope` type
40
+ // transitively references `CompatibilityReport` (core) and
41
+ // `RuntimePageGraph` (runtime/layout). Relocating those shared
42
+ // types is a layer-04/05 concern. The transit is pinned in
43
+ // `scripts/ci-check-session-layer-purity.mjs::DEBT_EXCEPTIONS` so
44
+ // CI surfaces the debt on every run.
45
+ // Slice 5e-11 item-1: the laycache probe is now dependency-injected
46
+ // via `OpenOptions.laycacheProbe` — session no longer imports from
47
+ // `src/runtime/prerender/**`. This retires the last P6 debt
48
+ // exception for Layer 01. See `docs/plans/cross-layer-coord-01.md §1`.
49
+ // Slice 5e-11 item-4: the WeakMap-backed `_internal-legacy.ts`
50
+ // accessor has been retired. `initialSnapshot`,
51
+ // `initialEditorStatePayload?`, `exportDocx`, and `dispose?` are
52
+ // public fields on `OpenResult` now; `DocxSession.export` + `dispose`
53
+ // route through the captured `lastOpenResult` directly.
54
+ import type { EditorSessionState } from "./session-state.ts";
55
+ import type {
56
+ EmbeddedDocumentManifest,
57
+ ExportOptions,
58
+ ExportResult,
59
+ OpenOptions,
60
+ OpenResult,
61
+ PreservationSnapshot,
62
+ ReopenBarrier,
63
+ ValidateOptions,
64
+ ValidationFinding,
65
+ ValidationReport,
66
+ } from "./types.ts";
67
+ import type { PersistedEditorSnapshot } from "../api/public-types.ts";
68
+ import {
69
+ decodePersistedSourcePackageBytes,
70
+ hasValidPersistedSourcePackageDigest,
71
+ } from "../io/source-package-provenance.ts";
72
+
73
+ /**
74
+ * Thrown from `DocxSession.export` when called on an instance that has
75
+ * not yet called `open()` (or whose capture has been cleared by
76
+ * `dispose()`).
77
+ *
78
+ * Layer-01 export is session-bound: `open()` captures the
79
+ * `ImportedDocxState` (source bytes, `OpcPackage`, per-part
80
+ * relationships, preserved-part manifest) that the export pipeline
81
+ * needs in order to round-trip. Calling `export(sessionState)` on an
82
+ * instance that never ran `open()` has no state to dispatch through.
83
+ *
84
+ * The architecture doc (`docs/architecture/01-package-session.md` §
85
+ * "Public entry points") deliberately reserves `export(sessionState)`
86
+ * rather than `export(doc)`: `EditorSessionState` carries the
87
+ * session-level fields (`workflowOverlay`, `workflowMetadata`,
88
+ * `warningLog`, `protectionSnapshot`) that the layer bakes into
89
+ * `customXml/itemN.xml` on export, which are not part of
90
+ * `CanonicalDocument`. A cross-session `export(doc)` overload is
91
+ * deliberately omitted from this phase — it requires a byte-free
92
+ * preservation contract (Layer-02 extension) that has not shipped.
93
+ */
94
+ export class SessionNotOpenError extends Error {
95
+ constructor() {
96
+ super(
97
+ "DocxSession.export(sessionState) requires DocxSession.open(bytes) to have been called on this session instance first. " +
98
+ "Open the source package through this session, then call export(sessionState) with the runtime's current session state.",
99
+ );
100
+ this.name = "SessionNotOpenError";
101
+ }
102
+ }
103
+
104
+ /**
105
+ * Legacy alias for `SessionNotOpenError`. Kept for one release window
106
+ * so callers that were catching the 5e-4 `SessionNotWiredError` for
107
+ * `export` continue to work.
108
+ *
109
+ * @deprecated use `SessionNotOpenError` — this alias goes away with
110
+ * Slice 5e-9 when the legacy file is deleted.
111
+ */
112
+ export const SessionNotWiredError = SessionNotOpenError;
113
+
114
+ /**
115
+ * Reserved Layer-01 entry class. `open()` and `export()` are async per
116
+ * the architecture reservation; `validate()` is synchronous because it
117
+ * runs against an already-loaded canonical document.
118
+ *
119
+ * Session-bound export model: `open()` captures the `OpenResult`
120
+ * handle (which carries the `LegacyOpenView.exportDocx` closure +
121
+ * `ImportedDocxState`). `export(sessionState, opts)` dispatches
122
+ * through that captured closure with the caller-supplied session
123
+ * state — the runtime's current state, carrying any
124
+ * `workflowOverlay` / `workflowMetadata` edits made between open and
125
+ * export. A session instance that has not been `open`ed throws
126
+ * `SessionNotOpenError`.
127
+ *
128
+ * Re-opening on the same instance overwrites the capture; `export()`
129
+ * always dispatches through the most recent open. In practice hosts
130
+ * construct one `DocxSession` per document and keep it alive for the
131
+ * runtime's lifetime (see `services/debug/lib/doc-loader.ts` for the
132
+ * reference pattern).
133
+ */
134
+ export class DocxSession {
135
+ /**
136
+ * Most recent `open()` result — captured so `export(sessionState)`
137
+ * can reach the `LegacyOpenView.exportDocx` closure via the
138
+ * `src/session/_internal-legacy.ts` WeakMap. Cleared on `dispose()`.
139
+ *
140
+ * The caller's `sessionState` argument to `export()` is passed
141
+ * through verbatim; this slot is only the dispatch anchor, not a
142
+ * source of truth for export data.
143
+ */
144
+ private lastOpenResult?: OpenResult;
145
+
146
+ /**
147
+ * Open a DOCX package from bytes. Returns the canonical document,
148
+ * session state, compatibility report, preservation snapshot, and
149
+ * the embedded-document manifest list populated from
150
+ * `collectEmbeddedDocuments` (P8).
151
+ *
152
+ * Implementation delegates to the legacy async loader
153
+ * (`loadDocxEditorSessionAsync`) while Slices 2–4 extract parse/
154
+ * serialize orchestration into `src/session/import/**` +
155
+ * `src/session/export/**`. The signature here is stable — the body
156
+ * can swap to the new orchestrator without consumers noticing.
157
+ */
158
+ async open(
159
+ bytes: Uint8Array | ArrayBuffer,
160
+ opts: OpenOptions = {},
161
+ ): Promise<OpenResult> {
162
+ // CLAUDE.md Performance Invariant 10: colocate the probe with the
163
+ // loader so nothing blocking can be inserted between them. When
164
+ // `opts.tryLaycacheEnvelope` is true AND `opts.laycacheProbe` is
165
+ // supplied, run the probe here; the result becomes
166
+ // `laycacheEnvelope` on the legacy loader options. An explicitly
167
+ // supplied `opts.laycacheEnvelope` wins over the probe so custom
168
+ // caching layers still work.
169
+ //
170
+ // The probe itself is dependency-injected (Slice 5e-11 item-1) so
171
+ // session does not import from `src/runtime/prerender/**`. The
172
+ // shell / prerender entry point passes `tryReadLaycacheEnvelope`
173
+ // as the probe.
174
+ let resolvedLaycacheEnvelope: unknown = opts.laycacheEnvelope;
175
+ if (
176
+ resolvedLaycacheEnvelope === undefined &&
177
+ opts.tryLaycacheEnvelope === true &&
178
+ opts.laycacheProbe
179
+ ) {
180
+ const probeResult = await opts.laycacheProbe(bytes);
181
+ if (probeResult) {
182
+ resolvedLaycacheEnvelope = probeResult.envelope;
183
+ }
184
+ }
185
+
186
+ // Scheduler created here so its `dispose` can be handed back to
187
+ // the caller via `LegacyOpenView.dispose`. Interactive hosts
188
+ // (editor-runtime-boundary in Slice 5c) call it on unmount to
189
+ // cancel pending idle chart-preview callbacks; one-shot callers
190
+ // like `prerenderDocument` can omit the call because the sync
191
+ // backend has nothing to cancel.
192
+ const scheduler =
193
+ opts.schedulerBackend !== undefined
194
+ ? createLoadScheduler({ backendOverride: opts.schedulerBackend })
195
+ : createLoadScheduler();
196
+
197
+ const legacyOptions: LoadDocxEditorSessionAsyncOptions = {
198
+ bytes,
199
+ documentId: opts.documentId ?? mintTransientDocumentId(),
200
+ ...(opts.sourceLabel !== undefined
201
+ ? { sourceLabel: opts.sourceLabel }
202
+ : {}),
203
+ ...(opts.editorBuild !== undefined
204
+ ? { editorBuild: opts.editorBuild }
205
+ : {}),
206
+ ...(opts.hostAdapter !== undefined
207
+ ? { hostAdapter: opts.hostAdapter }
208
+ : {}),
209
+ ...(opts.offloadEmbeddedDocuments !== undefined
210
+ ? { offloadEmbeddedDocuments: opts.offloadEmbeddedDocuments }
211
+ : {}),
212
+ ...(opts.onLoadStage !== undefined
213
+ ? { onLoadStage: opts.onLoadStage }
214
+ : {}),
215
+ ...(opts.telemetryBus !== undefined
216
+ ? {
217
+ // The public `OpenOptions.telemetryBus` is typed `unknown`
218
+ // so this barrel does not drag runtime-layer types into
219
+ // the session surface. The legacy loader narrows it back
220
+ // to the concrete `TelemetryBus` shape internally.
221
+ telemetryBus:
222
+ opts.telemetryBus as LoadDocxEditorSessionAsyncOptions["telemetryBus"],
223
+ }
224
+ : {}),
225
+ ...(resolvedLaycacheEnvelope !== undefined
226
+ ? {
227
+ laycacheEnvelope:
228
+ resolvedLaycacheEnvelope as LoadDocxEditorSessionAsyncOptions["laycacheEnvelope"],
229
+ }
230
+ : {}),
231
+ ...(opts.onChartPreviewsReady !== undefined
232
+ ? { onChartPreviewsReady: opts.onChartPreviewsReady }
233
+ : {}),
234
+ ...(opts.onProgressiveSnapshot !== undefined
235
+ ? { onProgressiveSnapshot: opts.onProgressiveSnapshot }
236
+ : {}),
237
+ ...(opts.surfaceProjector !== undefined
238
+ ? { surfaceProjector: opts.surfaceProjector }
239
+ : {}),
240
+ // Session class picks the scheduler so callers do not have to.
241
+ // DOM + Node both work: `createLoadScheduler()` auto-detects the
242
+ // best backend (scheduler.yield → MessageChannel → setTimeout →
243
+ // sync). `opts.schedulerBackend` pins the backend when the caller
244
+ // needs determinism (e.g. `prerenderDocument` forces `"sync"`).
245
+ //
246
+ // Scheduler disposal: carried through `LegacyOpenView.dispose`
247
+ // so interactive hosts can cancel pending idle chart-preview
248
+ // callbacks on unmount (Performance Invariant 9). Disposing
249
+ // here would race with the loader's own post-return idle work.
250
+ scheduler,
251
+ };
252
+ const legacy = await loadDocxSessionAsync(legacyOptions);
253
+
254
+ const sessionState = legacy.initialSessionState;
255
+ const canonicalDocument =
256
+ sessionState.canonicalDocument as CanonicalDocument;
257
+ const preservation: PreservationSnapshot =
258
+ canonicalDocument.preservation;
259
+
260
+ // P8 Step 6: embedded-document discovery runs inside the legacy
261
+ // loader and surfaces here via `legacy.embeddedDocumentManifests`.
262
+ // The array is empty when the package has no embeddings or the
263
+ // loader short-circuited on a laycache envelope.
264
+ const embeddedDocuments: readonly EmbeddedDocumentManifest[] =
265
+ legacy.embeddedDocumentManifests ?? [];
266
+
267
+ // Slice 5e-11 item-4: the remaining `LegacyOpenView` fields
268
+ // (`initialSnapshot`, `initialEditorStatePayload`, `exportDocx`,
269
+ // `dispose`) graduate to `OpenResult` directly per L06's
270
+ // `OpenResult` shape decision (`docs/architecture/06-workflow-
271
+ // review.md` §"Open-time surface"). The WeakMap-backed
272
+ // `_internal-legacy.ts` is retired.
273
+ const result: OpenResult = {
274
+ sessionState,
275
+ canonicalDocument,
276
+ compatibility: sessionState.compatibility,
277
+ preservation,
278
+ embeddedDocuments,
279
+ // Slice 5e-1 legacy-view graduates.
280
+ readOnly: legacy.readOnly,
281
+ ...(legacy.fatalError !== undefined
282
+ ? { fatalError: legacy.fatalError }
283
+ : {}),
284
+ protectionSnapshot: legacy.protectionSnapshot,
285
+ ...(resolvedLaycacheEnvelope !== undefined
286
+ ? { laycacheEnvelope: resolvedLaycacheEnvelope }
287
+ : {}),
288
+ // Slice 5e-11 item-4 graduates.
289
+ initialSnapshot: legacy.initialSnapshot,
290
+ ...(legacy.initialEditorStatePayload !== undefined
291
+ ? { initialEditorStatePayload: legacy.initialEditorStatePayload }
292
+ : {}),
293
+ exportDocx: legacy.exportDocx,
294
+ dispose: () => scheduler.dispose(),
295
+ };
296
+
297
+ // Capture the open result so `export(sessionState)` can reach the
298
+ // legacy view's stateful `exportDocx` closure without the caller
299
+ // threading the `OpenResult` back through. Overwritten on re-open;
300
+ // cleared by `dispose()`.
301
+ this.lastOpenResult = result;
302
+
303
+ return result;
304
+ }
305
+
306
+ /**
307
+ * Serialize an editor session state to a DOCX package.
308
+ *
309
+ * Per the architecture (`docs/architecture/01-package-session.md` §
310
+ * "Public entry points"), `export` takes the full `EditorSessionState`
311
+ * — not just a `CanonicalDocument` — because the session-level fields
312
+ * `workflowOverlay`, `workflowMetadata`, `warningLog`, and
313
+ * `protectionSnapshot` are bakery-inputs to `customXml/itemN.xml` on
314
+ * export, and live outside the canonical model. The runtime calls
315
+ * `session.export(runtime.getSessionState(), opts)` and the session
316
+ * passes `sessionState` straight through to the stateful export
317
+ * pipeline — no splice, no hidden state-carry.
318
+ *
319
+ * Dispatch: the method reaches the `LegacyOpenView.exportDocx`
320
+ * closure captured during the most recent `open()` on this instance.
321
+ * That closure holds the `ImportedDocxState` (source bytes,
322
+ * `OpcPackage`, per-part relationships, preserved-part manifest)
323
+ * that the pipeline needs in order to round-trip. When
324
+ * `sessionState.canonicalDocument` is referentially the same object
325
+ * as was produced at open time and every other field is unchanged,
326
+ * the legacy pipeline hits its byte-reuse fast path (identical
327
+ * `initialCanonicalSignature` + `canReuse`) and returns the source
328
+ * bytes directly.
329
+ *
330
+ * Throws `SessionNotOpenError` when called before `open()` on this
331
+ * instance. Cross-session export (a session state whose source bytes
332
+ * this instance never saw) is a later Layer-01 follow-up: it needs a
333
+ * byte-free preservation contract that has not shipped.
334
+ *
335
+ * P6 discipline: dispatch goes through `getLegacyOpenView(result).
336
+ * exportDocx(...)` — the WeakMap-backed accessor at
337
+ * `src/session/_internal-legacy.ts`. No direct import of
338
+ * `exportDocxEditorSession` from `src/io/docx-session.ts` is added
339
+ * beyond the `session.ts → docx-session.ts` debt exception already
340
+ * present for `open()`'s legacy transit.
341
+ */
342
+ async export(
343
+ sessionState: EditorSessionState,
344
+ opts: ExportOptions = {},
345
+ ): Promise<ExportResult> {
346
+ const captured = this.lastOpenResult;
347
+ if (!captured) {
348
+ throw new SessionNotOpenError();
349
+ }
350
+ // Slice 5e-11 item-4: `exportDocx` is a public field on
351
+ // `OpenResult` now. The WeakMap-backed accessor was retired when
352
+ // the remaining legacy-view fields graduated.
353
+ return captured.exportDocx(sessionState, opts);
354
+ }
355
+
356
+ /**
357
+ * Release resources captured by the most recent `open()`. Clears
358
+ * the session's internal capture and forwards to `OpenResult.dispose`
359
+ * (which cancels pending idle chart-preview callbacks scheduled by
360
+ * the load scheduler — CLAUDE.md Performance Invariant 9).
361
+ *
362
+ * After `dispose()`, a subsequent `export(sessionState)` on this
363
+ * instance throws `SessionNotOpenError` until `open()` is called
364
+ * again.
365
+ */
366
+ dispose(): void {
367
+ const captured = this.lastOpenResult;
368
+ if (!captured) return;
369
+ captured.dispose?.();
370
+ this.lastOpenResult = undefined;
371
+ }
372
+
373
+ /**
374
+ * Rehydrate a Layer-01 session from a previously-persisted
375
+ * `EditorSessionState` or `PersistedEditorSnapshot`.
376
+ *
377
+ * Shipped in Slice 5e-11 item-2 in response to the L07 Option-B
378
+ * decision (`docs/architecture/07-runtime-api.md` §Seam-reality).
379
+ * Replaces the manual decode-bytes-and-call-loadDocxSessionSync
380
+ * dance that `src/shell/session-bootstrap.ts::resolvePackageBackedExportSession`
381
+ * had to do by reaching across the Layer-01 boundary.
382
+ *
383
+ * Behaviour:
384
+ * 1. Extract the persisted `sourcePackage` envelope from the
385
+ * snapshot. If absent → return `ReopenBarrier { reason: "no-source-package" }`.
386
+ * 2. Base64-decode the bytes via `decodePersistedSourcePackageBytes`.
387
+ * If decode throws → return `ReopenBarrier { reason: "corrupted-bytes" }`.
388
+ * 3. Digest-check via `hasValidPersistedSourcePackageDigest`. On
389
+ * mismatch → return `ReopenBarrier { reason: "invalid-digest" }`.
390
+ * 4. Forward the bytes to `loadDocxSessionAsync`. Any fatal parse
391
+ * error surfaces on the returned `OpenResult.fatalError` as
392
+ * normal — NOT as a barrier (the rehydrate path produced a
393
+ * valid `OpenResult`; the LOAD decided it was unusable).
394
+ *
395
+ * Never throws. Barriers are always returned as data so callers
396
+ * can diff / display / persist without try-catch envelopes.
397
+ *
398
+ * On success, the returned `OpenResult` is captured on this
399
+ * instance the same way `open()` does — subsequent `export()` and
400
+ * `dispose()` calls operate on the rehydrated session.
401
+ */
402
+ async reopenFromSnapshot(
403
+ snapshot: PersistedEditorSnapshot | EditorSessionState,
404
+ opts: OpenOptions = {},
405
+ ): Promise<OpenResult | ReopenBarrier> {
406
+ const sourcePackage = snapshot.sourcePackage;
407
+ if (!sourcePackage) {
408
+ return {
409
+ reason: "no-source-package",
410
+ message:
411
+ "Cannot reopen from snapshot — the persisted session carries no embedded source-package envelope.",
412
+ };
413
+ }
414
+ let bytes: Uint8Array;
415
+ try {
416
+ bytes = decodePersistedSourcePackageBytes(sourcePackage);
417
+ } catch (err) {
418
+ return {
419
+ reason: "corrupted-bytes",
420
+ message: `Failed to decode source-package bytes: ${err instanceof Error ? err.message : String(err)}`,
421
+ };
422
+ }
423
+ if (!hasValidPersistedSourcePackageDigest(sourcePackage, bytes)) {
424
+ return {
425
+ reason: "invalid-digest",
426
+ message:
427
+ "Source-package digest check failed — the persisted snapshot has been tampered with or the schema drifted.",
428
+ };
429
+ }
430
+ return this.open(bytes, {
431
+ ...opts,
432
+ ...(opts.documentId === undefined
433
+ ? { documentId: snapshot.documentId }
434
+ : {}),
435
+ ...(opts.sourceLabel === undefined && sourcePackage.sourceLabel
436
+ ? { sourceLabel: sourcePackage.sourceLabel }
437
+ : {}),
438
+ ...(opts.editorBuild === undefined && "editorBuild" in snapshot
439
+ ? { editorBuild: snapshot.editorBuild }
440
+ : {}),
441
+ });
442
+ }
443
+
444
+ /**
445
+ * Validate a canonical document against the compatibility engine.
446
+ *
447
+ * Slice 5e-4 wired this through the existing
448
+ * `buildCompatibilityReport` machinery. The method is synchronous
449
+ * per the architecture reservation and is a pure function of the
450
+ * canonical document — no session state is read.
451
+ *
452
+ * `ok` is `false` when the report's `blockExport` flag is set OR
453
+ * any finding carries `severity === "error"`. Consumer surfaces
454
+ * (agents, CI gates) can rely on this single boolean to decide
455
+ * whether the document is export-blocked.
456
+ */
457
+ validate(
458
+ doc: CanonicalDocument,
459
+ _opts: ValidateOptions = {},
460
+ ): ValidationReport {
461
+ const report = buildCompatibilityReport({
462
+ // `buildCompatibilityReport` accepts the richer
463
+ // `CanonicalDocumentEnvelope` shape; a `CanonicalDocument` is a
464
+ // structural subset, so the cast is safe at the boundary.
465
+ document: doc as unknown as Parameters<
466
+ typeof buildCompatibilityReport
467
+ >[0]["document"],
468
+ generatedAt: new Date().toISOString(),
469
+ });
470
+
471
+ const findings: ValidationFinding[] = [
472
+ ...report.featureEntries.map<ValidationFinding>((entry) => ({
473
+ code: entry.featureEntryId,
474
+ severity: featureClassToSeverity(entry.featureClass),
475
+ message: entry.message,
476
+ ...(entry.featureKey !== undefined
477
+ ? { featureKey: entry.featureKey }
478
+ : {}),
479
+ })),
480
+ ...report.warnings.map<ValidationFinding>((warning) => ({
481
+ code: warning.code,
482
+ severity: "warning" as const,
483
+ message: warning.message,
484
+ })),
485
+ ...report.errors.map<ValidationFinding>((error) => ({
486
+ code: error.code,
487
+ severity: "error" as const,
488
+ message: error.message,
489
+ })),
490
+ ];
491
+
492
+ const ok =
493
+ !report.blockExport &&
494
+ !findings.some((finding) => finding.severity === "error");
495
+
496
+ return { ok, findings };
497
+ }
498
+ }
499
+
500
+ /**
501
+ * Map `CompatibilityFeatureClass` onto the narrower severity tri-state
502
+ * the `ValidationFinding` contract exposes. Fatal / blocking classes
503
+ * become `"error"`; partially-supported / warning-esque classes become
504
+ * `"warning"`; everything else is informational.
505
+ */
506
+ function featureClassToSeverity(
507
+ featureClass: string,
508
+ ): ValidationFinding["severity"] {
509
+ if (
510
+ featureClass === "unsupported-fatal" ||
511
+ featureClass === "unsupported-blocking" ||
512
+ featureClass === "unsupported-blocking-export"
513
+ ) {
514
+ return "error";
515
+ }
516
+ if (
517
+ featureClass === "unsupported-partial" ||
518
+ featureClass === "supported-partial" ||
519
+ featureClass === "preserve-only"
520
+ ) {
521
+ return "warning";
522
+ }
523
+ return "info";
524
+ }
525
+
526
+ function mintTransientDocumentId(): string {
527
+ // The session class does not own stable document identity; hosts
528
+ // that need round-trip-stable ids pass `opts.documentId`. This
529
+ // fallback is for ad-hoc opens (tests, one-off conversions).
530
+ const rand = Math.random().toString(36).slice(2, 10);
531
+ return `docx-session-${Date.now().toString(36)}-${rand}`;
532
+ }