@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,188 @@
1
+ /**
2
+ * Layer-06 Slice 3 — `createScopeFromBlockId` adapter.
3
+ *
4
+ * Resolves a `blockId` (paragraph/table/sdt-index identifier minted by
5
+ * `src/runtime/surface-projection.ts`) into the `EditorAnchorProjection`
6
+ * that `runtime.addScope` expects, then delegates to the existing
7
+ * marker-backed scope creator.
8
+ *
9
+ * Resolution walks the canonical document directly, not the surface
10
+ * snapshot. Going through `runtime.getRenderSnapshot().surface.blocks`
11
+ * would miss blocks the surface replaces with `placeholder-culled-*`
12
+ * entries under viewport culling — a real reproducibility failure on
13
+ * CCEP-size documents.
14
+ *
15
+ * This adapter unblocks `v3 runtime.workflow.createScope` graduation
16
+ * from `mock` → `live-with-adapter`. Consumers that already carry a
17
+ * full `EditorAnchorProjection` should continue calling
18
+ * `runtime.addScope` directly.
19
+ */
20
+
21
+ import type {
22
+ BlockNode,
23
+ CanonicalDocument,
24
+ DocumentRootNode,
25
+ InlineNode,
26
+ ParagraphNode,
27
+ } from "../../model/canonical-document.ts";
28
+ import type { CanonicalDocumentEnvelope } from "../../core/state/editor-state.ts";
29
+ import type {
30
+ AddScopeParams,
31
+ AddScopeResult,
32
+ EditorAnchorProjection,
33
+ EditorStoryTarget,
34
+ RuntimeRenderSnapshot,
35
+ WorkflowMetadataEntry,
36
+ WorkflowMetadataPersistence,
37
+ WorkflowScopeMode,
38
+ } from "../../api/public-types.ts";
39
+
40
+ /**
41
+ * Minimal runtime surface the scope writer needs. Narrowed so layer-06
42
+ * does not import the full `DocumentRuntime` (which would reach up into
43
+ * layer-07/orchestration and violate contract W9).
44
+ */
45
+ export interface ScopeWriterRuntime {
46
+ getCanonicalDocument(): CanonicalDocumentEnvelope;
47
+ getRenderSnapshot(): RuntimeRenderSnapshot;
48
+ addScope(params: AddScopeParams): AddScopeResult;
49
+ }
50
+
51
+ export interface CreateScopeFromBlockIdInput {
52
+ readonly blockId: string;
53
+ readonly mode?: WorkflowScopeMode;
54
+ readonly label?: string;
55
+ readonly scopeId?: string;
56
+ readonly persistence?: WorkflowMetadataPersistence;
57
+ readonly metadata?: Partial<WorkflowMetadataEntry>;
58
+ readonly storyTarget?: EditorStoryTarget;
59
+ /**
60
+ * Coord-06 §13d — per-scope edge stickiness for the range anchor.
61
+ * Defaults to `{ start: 1, end: -1 }` (greedy — absorbs boundary
62
+ * inserts, the pre-§13d shipped shape). Use `{ start: -1, end: 1 }`
63
+ * for a fixed-position anchor (signature-block / template-slot /
64
+ * system-paragraph semantics — rejects boundary inserts; cursor work
65
+ * doesn't leak in). The two diagonal variants `{ 1, 1 }` and
66
+ * `{ -1, -1 }` produce asymmetric edge behaviour (one sticky side,
67
+ * one fixed side) — uncommon in practice but accepted for
68
+ * completeness since `BoundaryAssoc` typing allows them.
69
+ */
70
+ readonly assoc?: { readonly start: -1 | 1; readonly end: -1 | 1 };
71
+ }
72
+
73
+ export type CreateScopeFromBlockIdResult =
74
+ | { readonly status: "created"; readonly scopeId: string; readonly anchor: EditorAnchorProjection }
75
+ | { readonly status: "block-not-found"; readonly blockId: string };
76
+
77
+ function inlineLength(node: InlineNode): number {
78
+ switch (node.type) {
79
+ case "text":
80
+ return Array.from(node.text).length;
81
+ case "hyperlink":
82
+ case "field":
83
+ return node.children.reduce(
84
+ (total, child) => total + inlineLength(child as InlineNode),
85
+ 0,
86
+ );
87
+ case "bookmark_start":
88
+ case "bookmark_end":
89
+ case "scope_marker_start":
90
+ case "scope_marker_end":
91
+ return 0;
92
+ default:
93
+ return 1;
94
+ }
95
+ }
96
+
97
+ function paragraphLength(paragraph: ParagraphNode): number {
98
+ return paragraph.children.reduce(
99
+ (total, child) => total + inlineLength(child as InlineNode),
100
+ 0,
101
+ );
102
+ }
103
+
104
+ /**
105
+ * Resolve a surface-level `blockId` (e.g. `paragraph-3`, `table-0`,
106
+ * `sdt-1`) into an `EditorAnchorProjection` by walking the canonical
107
+ * document in surface-projection order. Returns `null` when no block
108
+ * matches — viewport culling cannot produce false negatives here
109
+ * because the walk is independent of the render snapshot.
110
+ */
111
+ function resolveBlockAnchorFromCanonical(
112
+ document: CanonicalDocumentEnvelope,
113
+ blockId: string,
114
+ assoc: { readonly start: -1 | 1; readonly end: -1 | 1 } = { start: 1, end: -1 },
115
+ ): EditorAnchorProjection | null {
116
+ const root = document.content as DocumentRootNode;
117
+ let paragraphIndex = 0;
118
+ let tableIndex = 0;
119
+ let sdtIndex = 0;
120
+ let cursor = 0;
121
+ const blocks = root.children;
122
+ for (let i = 0; i < blocks.length; i += 1) {
123
+ const block: BlockNode = blocks[i]!;
124
+ let thisBlockLength: number;
125
+ let thisBlockId: string | null = null;
126
+ if (block.type === "paragraph") {
127
+ thisBlockLength = paragraphLength(block);
128
+ thisBlockId = `paragraph-${paragraphIndex}`;
129
+ paragraphIndex += 1;
130
+ } else if (block.type === "table") {
131
+ thisBlockLength = 1;
132
+ thisBlockId = `table-${tableIndex}`;
133
+ tableIndex += 1;
134
+ } else if (block.type === "sdt") {
135
+ thisBlockLength = 1;
136
+ thisBlockId = `sdt-${sdtIndex}`;
137
+ sdtIndex += 1;
138
+ } else {
139
+ thisBlockLength = 1;
140
+ }
141
+ if (thisBlockId === blockId) {
142
+ return {
143
+ kind: "range",
144
+ from: cursor,
145
+ to: cursor + thisBlockLength,
146
+ assoc,
147
+ };
148
+ }
149
+ cursor += thisBlockLength;
150
+ // Surface-projection order inserts a 1-unit gap between sibling
151
+ // blocks except after the last one. Mirror that so paragraph
152
+ // offsets match `surface.blocks[i].{from,to}` for visible rows.
153
+ if (i < blocks.length - 1) cursor += 1;
154
+ }
155
+ return null;
156
+ }
157
+
158
+ /**
159
+ * Create a workflow scope covering the block identified by `blockId`.
160
+ * Uses the runtime's own `addScope` pipeline so markers, overlay state,
161
+ * audit records, and collab sync all follow the canonical path.
162
+ */
163
+ export function createScopeFromBlockId(
164
+ runtime: ScopeWriterRuntime,
165
+ input: CreateScopeFromBlockIdInput,
166
+ ): CreateScopeFromBlockIdResult {
167
+ const anchor = resolveBlockAnchorFromCanonical(
168
+ runtime.getCanonicalDocument(),
169
+ input.blockId,
170
+ input.assoc,
171
+ );
172
+ if (!anchor) {
173
+ return { status: "block-not-found", blockId: input.blockId };
174
+ }
175
+ const result: AddScopeResult = runtime.addScope({
176
+ anchor,
177
+ mode: input.mode,
178
+ scopeId: input.scopeId,
179
+ persistence: input.persistence,
180
+ metadata: input.metadata,
181
+ storyTarget: input.storyTarget,
182
+ label: input.label,
183
+ });
184
+ return { status: "created", scopeId: result.scopeId, anchor: result.anchor };
185
+ }
186
+
187
+ // Exported for tests that need to verify the resolver independently.
188
+ export { resolveBlockAnchorFromCanonical };
@@ -2,7 +2,7 @@ import {
2
2
  verifyWorkflowPayloadXml,
3
3
  type PayloadSignature,
4
4
  type PayloadVerifier,
5
- } from "../io/ooxml/payload-signature.ts";
5
+ } from "../../io/ooxml/payload-signature.ts";
6
6
 
7
7
  /**
8
8
  * Runtime metadata-integrity states.
@@ -0,0 +1,129 @@
1
+ /**
2
+ * W10 · Overlay-visibility policy (class-A state) — runtime helpers.
3
+ *
4
+ * Document-authored visibility policy for every overlay kind (scope rail,
5
+ * tracked-changes markup, comment threads, suggestion halos, debug panel,
6
+ * presence halos). The policy is the class-A input to the class-C local
7
+ * preference composition that lives in Layer 10 (`ui.overlays.getVisibility`);
8
+ * this module does NOT compose the two — that is strictly L10's job.
9
+ *
10
+ * Spec: `docs/architecture/06-workflow-review.md` §W10 and
11
+ * `docs/plans/refactor/state-classes-cross-cutting.md` §X1. The public
12
+ * types live in `src/api/public-types.ts` (shared vocabulary); the
13
+ * runtime-internal helpers live here.
14
+ *
15
+ * Invariants:
16
+ * - Policies are keyed by `OverlayKind`; at most one policy per kind.
17
+ * - `enforcement: "always"` / `"never"` overrides any local preference.
18
+ * - `enforcement: "authored-default"` cedes to the local preference; the
19
+ * `defaultOn` field is only consulted when the user has not expressed one.
20
+ * - Mutations flow through the coordinator so they persist via
21
+ * `customXml/item1.xml` (see `workflow-payload.ts` `bw:overlayVisibility`
22
+ * block) and broadcast via the Yjs CRDT (shared-workflow state carries them).
23
+ */
24
+
25
+ import type {
26
+ OverlayKind,
27
+ OverlayVisibilityAuthorship,
28
+ OverlayVisibilityEnforcement,
29
+ OverlayVisibilityPolicy,
30
+ } from "../../api/public-types.ts";
31
+
32
+ export type {
33
+ OverlayKind,
34
+ OverlayVisibilityAuthorship,
35
+ OverlayVisibilityEnforcement,
36
+ OverlayVisibilityPolicy,
37
+ };
38
+
39
+ /** The closed set — used by the CI guard + the serializer. */
40
+ export const OVERLAY_KINDS: readonly OverlayKind[] = [
41
+ "scope-rail",
42
+ "comments",
43
+ "tracked-changes",
44
+ "suggestions",
45
+ "debug-panel",
46
+ "presence",
47
+ ];
48
+
49
+ const OVERLAY_KIND_SET: ReadonlySet<OverlayKind> = new Set(OVERLAY_KINDS);
50
+ const ENFORCEMENT_SET: ReadonlySet<OverlayVisibilityEnforcement> = new Set([
51
+ "authored-default",
52
+ "always",
53
+ "never",
54
+ ]);
55
+
56
+ export function isOverlayKind(value: unknown): value is OverlayKind {
57
+ return typeof value === "string" && OVERLAY_KIND_SET.has(value as OverlayKind);
58
+ }
59
+
60
+ export function isOverlayVisibilityEnforcement(
61
+ value: unknown,
62
+ ): value is OverlayVisibilityEnforcement {
63
+ return (
64
+ typeof value === "string" &&
65
+ ENFORCEMENT_SET.has(value as OverlayVisibilityEnforcement)
66
+ );
67
+ }
68
+
69
+ /** Type-guard — shallow validation. Authorship is optional and not checked. */
70
+ export function isOverlayVisibilityPolicy(
71
+ value: unknown,
72
+ ): value is OverlayVisibilityPolicy {
73
+ if (value === null || typeof value !== "object") return false;
74
+ const candidate = value as { kind?: unknown; enforcement?: unknown; defaultOn?: unknown };
75
+ return (
76
+ isOverlayKind(candidate.kind) &&
77
+ isOverlayVisibilityEnforcement(candidate.enforcement) &&
78
+ typeof candidate.defaultOn === "boolean"
79
+ );
80
+ }
81
+
82
+ /**
83
+ * Factory. Applies structural clone so callers cannot mutate the record
84
+ * after registration.
85
+ */
86
+ export function createOverlayVisibilityPolicy(input: {
87
+ kind: OverlayKind;
88
+ enforcement: OverlayVisibilityEnforcement;
89
+ defaultOn: boolean;
90
+ authoredBy?: OverlayVisibilityAuthorship;
91
+ }): OverlayVisibilityPolicy {
92
+ return {
93
+ kind: input.kind,
94
+ enforcement: input.enforcement,
95
+ defaultOn: input.defaultOn,
96
+ ...(input.authoredBy ? { authoredBy: { ...input.authoredBy } } : {}),
97
+ };
98
+ }
99
+
100
+ /**
101
+ * Structural equality — used by the store to short-circuit no-op
102
+ * `setVisibilityPolicy` calls (no spurious subscriber wakeups).
103
+ */
104
+ export function overlayVisibilityPoliciesEqual(
105
+ left: OverlayVisibilityPolicy | null | undefined,
106
+ right: OverlayVisibilityPolicy | null | undefined,
107
+ ): boolean {
108
+ if (left === right) return true;
109
+ if (!left || !right) return false;
110
+ if (left.kind !== right.kind) return false;
111
+ if (left.enforcement !== right.enforcement) return false;
112
+ if (left.defaultOn !== right.defaultOn) return false;
113
+ const la = left.authoredBy;
114
+ const ra = right.authoredBy;
115
+ if (!la && !ra) return true;
116
+ if (!la || !ra) return false;
117
+ return la.actorId === ra.actorId && la.at === ra.at;
118
+ }
119
+
120
+ /**
121
+ * Deterministic ordering for serialization — by the fixed `OVERLAY_KINDS`
122
+ * order, so re-export of an unchanged policy set is byte-identical.
123
+ */
124
+ export function sortOverlayVisibilityPolicies(
125
+ policies: readonly OverlayVisibilityPolicy[],
126
+ ): readonly OverlayVisibilityPolicy[] {
127
+ const indexOf = (kind: OverlayKind): number => OVERLAY_KINDS.indexOf(kind);
128
+ return [...policies].sort((a, b) => indexOf(a.kind) - indexOf(b.kind));
129
+ }
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Transitional sync entry — wraps `loadDocxEditorSession` from the
3
+ * legacy `src/io/docx-session.ts` for the two sync call sites inside
4
+ * `src/ui/editor-runtime-boundary.ts` that cannot (yet) await.
5
+ *
6
+ * Why this module exists.
7
+ * =======================
8
+ * `DocxSession.open()` is async per the architecture (the loader
9
+ * yields between parse stages so the browser can paint). Two
10
+ * `editor-runtime-boundary` paths need a sync load:
11
+ *
12
+ * 1. `createRuntime`'s SSR / Node fallback — runs when no
13
+ * `preloadedDocxSession` was produced by the async warm path but
14
+ * `initialDocx` bytes are present. `createRuntime` is sync, so a
15
+ * sync loader is the only entry that keeps the mount simple.
16
+ * Slice 5e-9 is expected to eliminate this fallback altogether
17
+ * by requiring the async warm path to run, OR by making
18
+ * `createRuntime` async-capable.
19
+ *
20
+ * 2. `resolvePackageBackedExportSession` — resolves an embedded
21
+ * source-package blob to decide the export barrier for
22
+ * snapshot-only mounts. The result drives the compatibility
23
+ * report that shows on first paint; deferring to first export
24
+ * would regress the blocked-export UX. Slice 5e-9 may thread a
25
+ * preloaded barrier session through the async warm path
26
+ * instead.
27
+ *
28
+ * Isolation.
29
+ * ==========
30
+ * By routing both sync call sites through this one module, the
31
+ * `src/ui/editor-runtime-boundary.ts` file no longer imports directly
32
+ * from `src/io/docx-session.ts`. The transit is pinned as a
33
+ * WARNING-level debt exception in
34
+ * `scripts/ci-check-session-layer-purity.mjs`, so the legacy-import
35
+ * surface shrinks to one narrow file that Slice 5e-9 deletes whole.
36
+ *
37
+ * Not exported from the session barrel.
38
+ * =====================================
39
+ * `src/session/index.ts` intentionally does NOT re-export this
40
+ * module; it is an in-tree implementation seam. External consumers
41
+ * never see it. Agents: do not widen its surface.
42
+ */
43
+
44
+ // Slice 5e-8.a: reach the sync loader body directly (in-layer),
45
+ // retiring the `src/io/docx-session.ts` transit for this shim.
46
+ import { loadDocxSessionSync } from "./import/loader.ts";
47
+
48
+ /**
49
+ * Sync docx load — returns the legacy-shaped handle the two
50
+ * `editor-runtime-boundary` sync call sites currently consume.
51
+ *
52
+ * Options + result are typed via `Parameters<>` / `ReturnType<>` so
53
+ * this module does not force its loader-types dependency to widen
54
+ * publicly.
55
+ *
56
+ * Removed in Slice 5e-9 when the sync call sites either flip to
57
+ * `await DocxSession.open(...)` or rely on a preloaded session
58
+ * produced by the async warm path.
59
+ */
60
+ export type SyncLegacyOpenOptions = Parameters<typeof loadDocxSessionSync>[0];
61
+
62
+ export function openDocxSync(
63
+ opts: SyncLegacyOpenOptions,
64
+ ): ReturnType<typeof loadDocxSessionSync> {
65
+ return loadDocxSessionSync(opts);
66
+ }
@@ -0,0 +1,104 @@
1
+ /**
2
+ * P8 Step 7 — export-time reconstitution of offloaded embedded
3
+ * documents back into the OPC package.
4
+ *
5
+ * Mirrors `src/session/import/embedded-offload.ts`. For every
6
+ * `EmbeddingOffloadEntry` the session captured at open time:
7
+ * 1. Call `hostAdapter.loadEmbeddedDocument(entry.storageReference)`
8
+ * → on `Uint8Array` success, use those bytes;
9
+ * → on `null` / rejection / thrown exception, decode the entry's
10
+ * `inlineBytes` base64 fallback and use that.
11
+ * 2. Write the bytes back to the OPC package at `entry.sourcePartPath`
12
+ * via `exportSession.replaceOwnedPart`.
13
+ *
14
+ * The source part path was added to the export session's owned-paths
15
+ * list at construction, so `replaceOwnedPart` is valid for it. The
16
+ * preservation path does NOT carry these parts — `buildImportPreservation`
17
+ * excluded them based on the `ownedPartPaths` the offload module
18
+ * returned at load time.
19
+ *
20
+ * Export never fails on storage unavailability: every entry carries
21
+ * the inline-bytes fallback inline (the whole point of P3). A
22
+ * ubiquitous host-storage outage simply results in the exported
23
+ * package looking identical to the source — bytes just round-trip
24
+ * via customXml instead of via preservation.
25
+ */
26
+
27
+ import type {
28
+ EditorHostAdapter,
29
+ StorageReference,
30
+ } from "../../api/public-types.ts";
31
+ import { sha256Hex } from "../../io/source-package-provenance.ts";
32
+ import {
33
+ decodeInlineBytes,
34
+ type EmbeddingOffloadEntry,
35
+ } from "../import/embedded-offload.ts";
36
+ import type { createExportSession } from "../../io/export/export-session.ts";
37
+
38
+ /**
39
+ * Call-shape of the `exportSession` argument. Typed via `ReturnType`
40
+ * so this module does not drag internal export-session implementation
41
+ * into the public surface.
42
+ */
43
+ type ExportSession = ReturnType<typeof createExportSession>;
44
+
45
+ export interface ReconstituteEmbeddedParams {
46
+ readonly exportSession: ExportSession;
47
+ readonly entries: readonly EmbeddingOffloadEntry[];
48
+ readonly hostAdapter: EditorHostAdapter | undefined;
49
+ }
50
+
51
+ /**
52
+ * Rebuild the OPC parts for every offload entry. No-op when `entries`
53
+ * is empty (no offload was performed on this session, OR the package
54
+ * had no offloadable embeddings).
55
+ */
56
+ export async function reconstituteEmbeddedDocuments(
57
+ params: ReconstituteEmbeddedParams,
58
+ ): Promise<void> {
59
+ const { exportSession, entries, hostAdapter } = params;
60
+ if (entries.length === 0) return;
61
+ for (const entry of entries) {
62
+ const bytes = await resolveEntryBytes(hostAdapter, entry);
63
+ exportSession.replaceOwnedPart({
64
+ path: entry.sourcePartPath,
65
+ bytes,
66
+ contentType: entry.contentType,
67
+ });
68
+ }
69
+ }
70
+
71
+ async function resolveEntryBytes(
72
+ hostAdapter: EditorHostAdapter | undefined,
73
+ entry: EmbeddingOffloadEntry,
74
+ ): Promise<Uint8Array> {
75
+ if (hostAdapter?.loadEmbeddedDocument) {
76
+ try {
77
+ const fetched = await hostAdapter.loadEmbeddedDocument(entry.storageReference);
78
+ if (fetched && fetched.byteLength > 0) {
79
+ // Tamper check: the `StorageReference.sha256` contract in
80
+ // `public-types.ts` promises the session computes the sha
81
+ // so hosts can detect tampering on rehydrate. Verify the
82
+ // loaded payload matches the sha we captured at offload
83
+ // time. On mismatch, treat it identically to a null /
84
+ // failed load and fall through to the inline-bytes
85
+ // fallback — export still ships honest bytes.
86
+ if (sha256Hex(fetched) === entry.sha256) {
87
+ return fetched;
88
+ }
89
+ }
90
+ } catch {
91
+ // Treat identically to `null` — fall through to the inline-
92
+ // bytes fallback. The architecture guarantees export never
93
+ // fails on storage unavailability.
94
+ }
95
+ }
96
+ return decodeInlineBytes(entry.inlineBytes);
97
+ }
98
+
99
+ /**
100
+ * Re-export for convenience so consumers of the reconstitute module
101
+ * that need to chain additional logic can reach the `StorageReference`
102
+ * shape without a separate import.
103
+ */
104
+ export type { StorageReference };
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Typed export diagnostic producers.
3
+ *
4
+ * Slice 4a of refactor/01 package-session. The legacy export path in
5
+ * `src/io/docx-session.ts` currently throws plain `Error` objects when
6
+ * the canonical document cannot be serialized — callers get a string
7
+ * message but no stable code to react against.
8
+ *
9
+ * This module defines a single `ExportBlockedError` class carrying a
10
+ * stable `code` + `featureKey` + `details`, so:
11
+ * - the runtime can surface targeted UX (block dialog, rail badge)
12
+ * - CI / truth-baseline can assert on the code, not the message
13
+ * - export-validation.ts's `assert*` predicates throw these directly
14
+ *
15
+ * Contract P6 clean. Depends on no runtime / ui / core / review code.
16
+ */
17
+
18
+ /**
19
+ * Stable classification codes used across the export path.
20
+ * Consumers snapshot on `code` rather than `message`; the message
21
+ * format may change for i18n without breaking callers.
22
+ */
23
+ export type ExportBlockedCode =
24
+ /** Export is blocked by the active compatibility report. */
25
+ | "compatibility-blocked"
26
+ /** Preserve-only comment anchors cannot be remapped safely. */
27
+ | "preserve-only-comments"
28
+ /** Active revisions overlap unsupported serialization boundaries. */
29
+ | "revision-boundary"
30
+ /** One or more comments no longer map to a serializable range. */
31
+ | "comment-range-lost";
32
+
33
+ export interface ExportBlockedDetails {
34
+ /** Count of items that contributed to the block (when applicable). */
35
+ readonly count?: number;
36
+ /**
37
+ * Arbitrary identifiers (revision ids, comment ids, feature keys)
38
+ * the runtime can cite in its block-dialog UX. Empty when the
39
+ * block reason is a single global flag (compatibility-blocked).
40
+ */
41
+ readonly ids?: readonly string[];
42
+ }
43
+
44
+ /**
45
+ * Thrown from the export path to signal a structural block. The
46
+ * runtime catches this, reads `code`, and drives UX / telemetry
47
+ * accordingly. Message is human-facing; the machine-readable
48
+ * projection is the `code` + `details` pair.
49
+ */
50
+ export class ExportBlockedError extends Error {
51
+ readonly code: ExportBlockedCode;
52
+ readonly details: ExportBlockedDetails;
53
+
54
+ constructor(
55
+ code: ExportBlockedCode,
56
+ message: string,
57
+ details: ExportBlockedDetails = {},
58
+ ) {
59
+ super(message);
60
+ this.name = "ExportBlockedError";
61
+ this.code = code;
62
+ this.details = details;
63
+ }
64
+ }
65
+
66
+ /**
67
+ * Produce a human-facing message with a count prefix. Kept in a
68
+ * shared helper so both the assertion functions in export-validation
69
+ * and downstream diagnostic-panel UX format the string identically.
70
+ */
71
+ export function formatBlockedExportMessage(
72
+ code: ExportBlockedCode,
73
+ count?: number,
74
+ ): string {
75
+ switch (code) {
76
+ case "compatibility-blocked":
77
+ return "DOCX export is blocked by the current compatibility report.";
78
+ case "preserve-only-comments":
79
+ return `DOCX export is blocked because ${count ?? 0} preserve-only comment anchors cannot be safely remapped after runtime edits.`;
80
+ case "revision-boundary":
81
+ return `DOCX export is blocked because ${count ?? 0} active revisions overlap unsupported serialization boundaries.`;
82
+ case "comment-range-lost":
83
+ return `DOCX export is blocked because ${count ?? 0} comments no longer map to serializable ranges.`;
84
+ }
85
+ }
@@ -0,0 +1,110 @@
1
+ /**
2
+ * Export-validation helpers for the session-layer export path.
3
+ *
4
+ * Slice 4a of refactor/01 package-session. Extracted from the four
5
+ * inline `throw new Error(...)` points in
6
+ * `exportDocxEditorSession` (`src/io/docx-session.ts:2021`, `:2066`,
7
+ * `:2109`, `:2148`). Each inline throw becomes a single `assert*`
8
+ * call that routes through the typed `ExportBlockedError` from
9
+ * `export-diagnostics.ts`.
10
+ *
11
+ * Pure functions over primitives + typed value objects; no IO, no
12
+ * runtime imports, no review-store reads. Contract P6 clean.
13
+ */
14
+
15
+ import type { CompatibilityReport } from "../../api/public-types.ts";
16
+ import {
17
+ ExportBlockedError,
18
+ formatBlockedExportMessage,
19
+ } from "./export-diagnostics.ts";
20
+
21
+ /**
22
+ * Block export when the compatibility report flags the document as
23
+ * unsafe to re-serialize. This is the earliest structural gate — if
24
+ * compatibility blocks, nothing else matters.
25
+ */
26
+ export function assertExportNotBlockedByCompatibility(
27
+ compatibility: Pick<CompatibilityReport, "blockExport">,
28
+ ): void {
29
+ if (compatibility.blockExport) {
30
+ throw new ExportBlockedError(
31
+ "compatibility-blocked",
32
+ formatBlockedExportMessage("compatibility-blocked"),
33
+ );
34
+ }
35
+ }
36
+
37
+ /**
38
+ * Block export when the loader captured preserve-only comment anchors
39
+ * that no longer map to serializable ranges after runtime edits.
40
+ *
41
+ * `count` is the greater of:
42
+ * - `state.blockingCommentDiagnostics.length`
43
+ * - `state.preservedCommentDefinitions.length` (deduped)
44
+ *
45
+ * Kept as a scalar count input so callers can decide the combination
46
+ * policy before calling in. Callers that need to cite specific
47
+ * comment ids pass them via `ids` for inclusion in the error's
48
+ * machine-readable payload.
49
+ */
50
+ export function assertNoBlockingPreservedComments(
51
+ count: number,
52
+ ids?: readonly string[],
53
+ ): void {
54
+ if (count > 0) {
55
+ throw new ExportBlockedError(
56
+ "preserve-only-comments",
57
+ formatBlockedExportMessage("preserve-only-comments", count),
58
+ {
59
+ count,
60
+ ...(ids !== undefined ? { ids } : {}),
61
+ },
62
+ );
63
+ }
64
+ }
65
+
66
+ /**
67
+ * Block export when runtime revisions overlap unsupported
68
+ * serialization boundaries. `skippedRevisionIds` comes from
69
+ * `serializeRuntimeRevisionsIntoDocumentXml` — the serializer
70
+ * returns the list of ids it could not safely rewrite, and any
71
+ * non-empty list blocks export.
72
+ */
73
+ export function assertNoSkippedRevisions(
74
+ skippedRevisionIds: readonly string[],
75
+ ): void {
76
+ if (skippedRevisionIds.length > 0) {
77
+ throw new ExportBlockedError(
78
+ "revision-boundary",
79
+ formatBlockedExportMessage("revision-boundary", skippedRevisionIds.length),
80
+ {
81
+ count: skippedRevisionIds.length,
82
+ ids: skippedRevisionIds,
83
+ },
84
+ );
85
+ }
86
+ }
87
+
88
+ /**
89
+ * Block export when comment-anchor serialization dropped comments
90
+ * whose anchor is not merely "detached" (detached comments are
91
+ * acceptable; others signal a serializer bug / unexpected runtime
92
+ * state).
93
+ */
94
+ export function assertNoBlockingSkippedComments(
95
+ blockingSkippedCommentIds: readonly string[],
96
+ ): void {
97
+ if (blockingSkippedCommentIds.length > 0) {
98
+ throw new ExportBlockedError(
99
+ "comment-range-lost",
100
+ formatBlockedExportMessage(
101
+ "comment-range-lost",
102
+ blockingSkippedCommentIds.length,
103
+ ),
104
+ {
105
+ count: blockingSkippedCommentIds.length,
106
+ ids: blockingSkippedCommentIds,
107
+ },
108
+ );
109
+ }
110
+ }