@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
@@ -221,8 +221,457 @@
221
221
  * `resolved-formatting-document`, and `resolved-formatting-state`.
222
222
  * Persisted prerender/layout caches must invalidate so geometry derived
223
223
  * under v23 is never reused against the merged layout pipeline.
224
+ * 25 — Debug-infra D1 public-facet widening. `src/runtime/layout/public-facet.ts`
225
+ * adds module-level `setActiveLayoutWarningEmitter` setter + private
226
+ * `emitLayoutGuardWarning` helper, with call-site probes at the 10
227
+ * silent-success `return []` guards (powers D1 stage-token lineage
228
+ * for the `warning` telemetry channel). `src/runtime/layout/index.ts`
229
+ * re-exports `setActiveLayoutWarningEmitter`. No pixel-geometry
230
+ * change; the facet interface widened (new exported function), so
231
+ * persisted envelopes from v24 should invalidate cleanly.
232
+ * 26 — Layer-03 Phase D (formatting-semantics refactor, earned bump).
233
+ * `src/runtime/layout/table-render-plan.ts` +
234
+ * `src/runtime/layout/public-facet.ts` flipped their imports from
235
+ * the legacy `src/runtime/table-style-resolver.ts` to the canonical
236
+ * `src/runtime/formatting/table-style-resolver.ts`. The 9 legacy
237
+ * shim files at `src/runtime/*-resolver.ts` + friends were deleted.
238
+ * No pixel-geometry change; cache envelopes from v25 invalidate
239
+ * because the layout-engine import graph materially changed shape.
240
+ * NOTE on prior unearned bumps: Layer-03 Slices 2 + 3 had previously
241
+ * assigned 24 → 25 → 26 as "defensive" versions while the refactor
242
+ * was under way; on code review those bumps were found to be
243
+ * unearned and Phase A1 reverted to 24. With upstream having since
244
+ * re-claimed 25 for debug-infra D1, this earned Layer-03 bump
245
+ * lands at 26.
246
+ * 27 — Refactor/05 Slice 1: reserve `src/runtime/geometry/**` as its own
247
+ * layer. New modules `geometry-types.ts`, `geometry-facet.ts` (thin
248
+ * proxy over the existing layout facet + render kernel), `viewport.ts`
249
+ * (stubbed), `invalidation.ts` (cache-key helper), and barrel
250
+ * `index.ts`. The layout-engine-version CI guard's WATCHED_PREFIXES
251
+ * now includes `src/runtime/geometry/` so future slices must bump
252
+ * this constant when they touch the tree. The companion guard
253
+ * `scripts/ci-check-geometry-no-dom-authority.mjs` ships alongside
254
+ * (enforces G2 — DOM-origin reads forbidden outside `viewport.ts`).
255
+ * No behavior change: the geometry facet delegates to the layout
256
+ * facet's existing `hitTest` / `getAnchorRects`, and caret / envelope
257
+ * / handle methods return null / empty stubs pending Slices 4–5.
258
+ * Cache envelopes from v26 invalidate because the runtime's exported
259
+ * module set changed (new `src/runtime/geometry/index.ts` barrel)
260
+ * even though pixel geometry did not.
261
+ * 28 — Refactor/05 Slice 2a: move `resolveHitTest`, `hitTestRegion`,
262
+ * `containsPoint`, and `resolveAnchorRects` from
263
+ * `src/runtime/layout/public-facet.ts` into
264
+ * `src/runtime/geometry/hit-test.ts` and
265
+ * `src/runtime/geometry/project-anchors.ts`. Layout facet imports
266
+ * from the new locations; its `hitTest` / `getAnchorRects` methods
267
+ * remain wrapping those calls (deprecation + deletion lands in
268
+ * Slice 6). `createGeometryFacet` gains an optional `renderKernel`
269
+ * getter — when supplied, geometry resolves directly against the
270
+ * kernel's current frame, bypassing the layout-facet proxy. New
271
+ * CI guard `scripts/ci-check-no-layout-geometry-leak.mjs` rejects
272
+ * DOM-origin reads inside `src/runtime/layout/**` +
273
+ * `src/runtime/render/**` (dual of the geometry-no-dom-authority
274
+ * guard). No pixel-geometry change; cache envelopes from v27
275
+ * invalidate because the runtime's module exports and the layout
276
+ * facet's internal dependency set changed.
277
+ * 29 — Refactor/05 Slice 2b: move `collectLineBoxesForRegion`,
278
+ * `resolveRegionEntry`, `EMPTY_LINE_BOXES`, and the
279
+ * `RuntimeLineBoxAlias` type from `public-facet.ts` into
280
+ * `src/runtime/geometry/project-fragments.ts`; move
281
+ * `collectScopeRailSegmentsForQuery` into
282
+ * `src/runtime/geometry/project-anchors.ts` (with D1 warning probe
283
+ * ported alongside). Layout facet imports from the new locations.
284
+ * `GeometryFacet` gains `getBlockRects` (new impl — walks the render
285
+ * frame, tags rects `space: "frame"`; required by Slice 5's
286
+ * `v3 runtime.geometry.getBlockRects` live promotion), `getLineBoxes`,
287
+ * `getScopeRailSegments`, and `getAllScopeRailSegments` methods —
288
+ * the line-box and rail-segment methods delegate to the layout
289
+ * facet's orchestration for now and flip to direct engine/graph
290
+ * access during Slice 6. No pixel-geometry change; cache envelopes
291
+ * from v28 invalidate because the layout facet's internal import
292
+ * set changed again.
293
+ * 30 — Refactor/05 Slice 3a: real `src/runtime/geometry/viewport.ts`
294
+ * implementation. `createViewportFromRoot({ root, pxPerTwip })`
295
+ * attaches passive scroll + ResizeObserver listeners to the editor
296
+ * root and rAF-coalesces fanouts — consumers subscribe via
297
+ * `geometryFacet.subscribeViewport`. The Slice-1 stub
298
+ * `createViewport()` is preserved for tests and unwired call sites.
299
+ * `RootViewportHandle.setPxPerTwip` propagates zoom changes
300
+ * synchronously so chrome overlays re-project without waiting for a
301
+ * scroll event. Architecture 05 contract G2 exception is honored:
302
+ * only `scrollLeft` / `scrollTop` are read here, and only from
303
+ * inside `viewport.ts` (enforced by
304
+ * `scripts/ci-check-geometry-no-dom-authority.mjs`).
305
+ *
306
+ * `GeometryFacet` gains `getPage(target)` and `getBlock(blockId)` —
307
+ * the substrate Slice 3b (overlay + scroll-anchor rewire) consumes
308
+ * in place of DOM reads. Implementations are thin projections over
309
+ * `frame.pages` / `frame.pages[*].regions.*.blocks`, tagged
310
+ * `space: "frame"` per G5.
311
+ *
312
+ * No pixel-geometry change; cache envelopes from v29 invalidate
313
+ * because the facet surface widened (new `getPage` / `getBlock`
314
+ * methods + `createViewportFromRoot` export) even though pixel
315
+ * output is unchanged.
316
+ * 31 — Refactor/05 Slice 3b: rewire chrome consumers off DOM authority.
317
+ * `src/ui-tailwind/chrome-overlay/tw-page-stack-overlay-layer.tsx`
318
+ * gains an optional `geometryFacet` prop; when wired (and
319
+ * `getPage(0)` resolves through the kernel), the overlay reads
320
+ * page rects directly from the render frame via the new exported
321
+ * helper `resolvePageOverlayRectsFromGeometry` — zero DOM reads,
322
+ * zero forced layouts. The DOM-measurement path
323
+ * (`measureWidgetsViaBoundingRect` / `ViaOffsetChain`) is kept as
324
+ * the cold-open fallback per architecture 05 Slice 3 exit
325
+ * criteria.
326
+ *
327
+ * `src/ui-tailwind/editor-surface/scroll-anchor.ts` —
328
+ * `findScrollAnchor` + `restoreScrollAnchor` gain an optional
329
+ * `{ geometryFacet, prepaintFallback }` options arg. When wired,
330
+ * block positions are computed from the kernel's frame coordinates
331
+ * (`geometryFacet.getBlock(blockId).rects[0]`); the DOM path runs
332
+ * only on cold-open or when `prepaintFallback: true` is
333
+ * explicitly requested.
334
+ *
335
+ * `scripts/ci-check-geometry-no-dom-authority.mjs` extends
336
+ * `SCOPED_FILES` + `ALLOWED_FILES` to both chrome files so
337
+ * additions to them are tracked; Slice 6 narrows `ALLOWED_FILES`
338
+ * back to `viewport.ts` only.
339
+ *
340
+ * No pixel-geometry change — the geometry-path rects are
341
+ * byte-identical to the kernel frame rects they project from, and
342
+ * the DOM fallback path is unchanged. Cache envelopes from v30
343
+ * invalidate because the overlay and scroll-anchor public
344
+ * signatures widened.
345
+ * 32 — Refactor/05 Slice-3 cleanup: relocate
346
+ * `resolvePageOverlayRectsFromGeometry` from the overlay's `.tsx`
347
+ * into `src/runtime/geometry/overlay-rects.ts` so the projection
348
+ * math lives inside L05. Adds a classifier rule for
349
+ * `test/runtime/geometry/` in `scripts/test-bundles.mjs` (previously
350
+ * the bundle was empty because tests fell through the catch-all).
351
+ * No behavior change; the overlay component re-exports the moved
352
+ * symbol under its original name. Cache envelopes from v31
353
+ * invalidate because the geometry layer's export surface widened
354
+ * (new `overlay-rects.ts` module + new barrel export).
355
+ * 33 — Refactor/05 Slice 4: caret geometry as a first-class operation.
356
+ * `src/runtime/geometry/caret-geometry.ts` ships
357
+ * `resolveCaretGeometry(frame, offset, story?)` and
358
+ * `resolveSelectionRects(frame, range)`. `GeometryFacet.getCaret`
359
+ * and `getSelectionRects` replace their Slice-1 null/[] stubs
360
+ * with real implementations routed through `frame.anchorIndex`
361
+ * (the working substrate for offset-to-rect projection even when
362
+ * per-line `RenderLine` anchors aren't populated).
363
+ *
364
+ * Known Slice-4 caveats (documented in `caret-geometry.ts`):
365
+ * `direction` is always `"ltr"` until run-level writing-direction
366
+ * metadata arrives in a later slice; `getSelectionRects`
367
+ * non-collapsed range returns the kernel's union rect (single
368
+ * entry) until per-line anchors land and enable one-rect-per-line
369
+ * enumeration. `baseline` is approximated at 0.8× height via the
370
+ * cap-height heuristic.
371
+ *
372
+ * No pixel-geometry change — caret rects are byte-identical to
373
+ * the anchor index's projections, just reshaped into
374
+ * `CaretGeometry`. Cache envelopes from v32 invalidate because
375
+ * the geometry facet surface widened.
376
+ * 34 — Refactor/05 Slice 5: replacement envelope + object handles as
377
+ * first-class operations.
378
+ * `src/runtime/geometry/replacement-envelope.ts` ships
379
+ * `resolveReplacementEnvelope(frame, scope, story?)` →
380
+ * `EnvelopeBundle | null`. Range scopes produce `confidence:
381
+ * "exact"`; detached scopes project the `lastKnownRange` with
382
+ * `confidence: "detached"`. `scopeRects` routes through the
383
+ * Slice-4 `resolveSelectionRects` helper so the envelope upgrades
384
+ * to one-rect-per-line automatically when per-run anchors land.
385
+ * `src/runtime/geometry/object-handles.ts` ships
386
+ * `resolveObjectHandles(frame, objectId)` producing 9 zero-size
387
+ * handle points (4 corners + 4 edge midpoints + 1 rotate anchor
388
+ * 20 px above the top-center) projected from the block bbox via
389
+ * `frame.anchorIndex.byBlockId`.
390
+ * `GeometryFacet` adds a `getCanonicalDocument?` input used by
391
+ * `getReplacementEnvelope(scopeId)` to resolve scopes through
392
+ * `src/runtime/workflow/scope-resolver.ts::resolveScope`. `getObjectHandles`
393
+ * routes directly through the kernel frame.
394
+ * v3 promotion: `src/api/v3/runtime/geometry.ts::getBlockRectsMetadata`
395
+ * flips from `mock` to `live-with-adapter`; adapter wraps
396
+ * `layout.getAnchorRects({kind:'block-id', value})` into
397
+ * `BlockRectEntry.rects[]`. Mock shape retained as the headless
398
+ * fallback.
399
+ * No pixel-geometry change — envelope and handle rects are
400
+ * byte-identical projections of existing anchor-index outputs.
401
+ * Cache envelopes from v33 invalidate because the geometry facet
402
+ * surface widened again (new `getCanonicalDocument` input + new
403
+ * export `ReplacementScope`).
404
+ * 35 — Refactor/05 Slice 6 (layer closure): `GeometryDebugEntry` +
405
+ * `buildGeometryDebugEntry` ship at
406
+ * `src/runtime/geometry/projector.ts`, mirroring
407
+ * `src/runtime/formatting/projector.ts`. The entry is wired into
408
+ * `DebugInspectorSnapshot.geometry` as an optional field gated by
409
+ * the `layout` telemetry channel (consumers must call
410
+ * `bus.isEnabled("layout")` before materializing).
411
+ * Plan frontmatter flips `status: PROPOSED → CLOSED` with
412
+ * explicitly-documented deferred follow-ups (consumer flip +
413
+ * gap reconciliation, wrapper deletion, CI-guard tightening,
414
+ * `geometry.projected` emit, per-run anchors, v3 promotions).
415
+ * Wiki pages `rendering-pipeline.md` + `selection-and-cursor.md`
416
+ * bump `lastCuratedCommit` and gain closure status notes.
417
+ * Closure evidence artifact lands at
418
+ * `docs/reference/layer-05-geometry-projection-evidence-2026-04-22.{md,json}`.
419
+ * No pixel-geometry change; cache envelopes from v34 invalidate
420
+ * because a new debug-snapshot optional field is now part of the
421
+ * runtime's exported shape.
422
+ * 36 — Layer-03 production-boundary pass §B. `resolved-formatting-state.ts
423
+ * ::resolveDominantFont` now reads from `segment.resolvedRunFormatting`
424
+ * (canonical L03 output) first, with `segment.markAttrs` fallback only
425
+ * for viewport-culled blocks where the resolved output is absent.
426
+ * Before: layout reconstructed dominant font from the surface
427
+ * projection intermediate (`markAttrs`), bypassing the L03 cascade.
428
+ * After: L04 measurement-path reads authoritative L03 output, matching
429
+ * architecture §C2 ("formatting, layout, and geometry are separate
430
+ * layers"). Pixel-identical on documents whose markAttrs agreed with
431
+ * the cascade; fixes cases where the cascade carried a
432
+ * character-style-chain font (`Heading 1` → `Cambria`) but markAttrs
433
+ * lacked the override — line-height + avg-char-width now inherit the
434
+ * cascade-resolved font. Cache envelopes from v35 invalidate because
435
+ * dominant-font inputs to line-height + widow/keep-next measurement
436
+ * changed shape.
437
+ * 37 — Refactor/04 Slice 3: activate the reserved `layout` telemetry
438
+ * channel. `createLayoutEngine({ telemetryBus })` forwards the
439
+ * granular `page_count_changed` / `page_field_dirtied` /
440
+ * `layout_committed` events to the bus and additionally emits
441
+ * `layout.recompute.completed` at the end of `fullRebuild` /
442
+ * `incrementalRelayout` with `{ layoutEngineVersion, reasonKind,
443
+ * scope, pageCountBefore, pageCountAfter, durationMs }`. Adds a
444
+ * `LayoutDebugEntry` projector + `DebugInspectorSnapshot.layoutDebug`
445
+ * section, both gated on `bus.isEnabled("layout")` — complementing
446
+ * the `geometry?` section that v35 already gates on the same
447
+ * channel. Zero cost when the channel is off (bitmap short-circuit
448
+ * on every emit, `recomputeStart` sample skipped on the cold
449
+ * path). No behavior change to pagination itself. Cache envelopes
450
+ * from v36 invalidate because the `createLayoutEngine` options
451
+ * shape widened (new optional `telemetryBus`) and a new optional
452
+ * debug-snapshot field is now part of the runtime's exported
453
+ * shape.
454
+ * 38 — Refactor/04 Slice 4. Multi-column `nextColumn` + column-aware
455
+ * fragment routing. Pagination now tracks a per-block column
456
+ * placement (`columnByBlockIdByPageIndex`) when the section layout
457
+ * has `columns > 1`. `RuntimeBlockFragment.columnIndex` carries
458
+ * that placement onto every fragment; `buildRegions` partitions
459
+ * fragment ids into `RuntimePageRegions.columns[i].fragmentIds`
460
+ * rather than leaving the per-column lists empty. Behavioral
461
+ * changes: block-level `<w:br w:type="column"/>` inside a multi-
462
+ * column section now routes subsequent fragments into the
463
+ * advanced column region; column overflow pushes a new page that
464
+ * inherits the section's column layout with the active column
465
+ * reset to 0; single-column sections continue to route every
466
+ * fragment to `regions.body` and leave `regions.columns`
467
+ * undefined (F01 + 10 single-column CCEP regression preserved).
468
+ * Section-level `<w:type w:val="nextColumn"/>` still degrades to
469
+ * `continuous`-style page merging — cross-section column-state
470
+ * carry is explicit follow-up work (tracked on react-refactor).
471
+ * Cache envelopes from v37 invalidate because the per-page
472
+ * fragment routing under multi-column layouts changes; single-
473
+ * column envelopes remain shape-compatible but the version bump
474
+ * ensures consumers re-derive regions rather than trust a stale
475
+ * empty `columns[*].fragmentIds`.
476
+ * 39 — Refactor/05 Slice 6 wrapper-deletion pass (2026-04-22). Deletes
477
+ * `hitTest` + `getAnchorRects` from `WordReviewEditorLayoutFacet`
478
+ * interface + impl + inert stub. Geometry consumers now read through
479
+ * `runtime.geometry.hitTest` / `.getAnchorRects`. The geometry-facet's
480
+ * own layout-facet fallback paths were also deleted; unwired facet
481
+ * returns null / [] directly. Persisted LayCache envelopes from v38
482
+ * invalidate because the facet's interface shape changed.
483
+ * 40 — Refactor/06 Slice 4C rail-seam inversion (2026-04-22). The
484
+ * layout facet's `getScopeRailSegments` / `getAllScopeRailSegments`
485
+ * / `getAllScopeCardModels` methods are removed; rail + scope-
486
+ * card composition is now Layer-06-owned via
487
+ * `runtime.workflow.*`. The `getWorkflowRailInput` /
488
+ * `getWorkflowMarkupMetadata` / `getSuggestionsSnapshot` config
489
+ * hooks on `CreateLayoutFacetInput` are removed since the facet
490
+ * no longer consumes workflow state. Geometry-facet's
491
+ * `getScopeRailSegments` / `getAllScopeRailSegments` proxies are
492
+ * removed. No pixel-geometry change, but the public layout
493
+ * surface shape changed, so cache envelopes from v39 invalidate.
494
+ */
495
+ /*
496
+ * v40 (2026-04-22) — refactor/04 post-Slice-4 cross-section column
497
+ * carry. Section-level `<w:type w:val="nextColumn"/>` between two
498
+ * matching multi-column sections now advances column state across
499
+ * the boundary: section N is seeded with `startColumnIndex =
500
+ * prevEndColumn + 1` when prev ended with room; otherwise a new
501
+ * page pushes with column index 0 (matching Word's behavior).
502
+ * Single-column sections still degrade to `continuous` (F01 + CCEP
503
+ * regression preserved). Mismatched column counts degrade to
504
+ * `nextPage`. New optional `startColumnIndex` parameter on
505
+ * `paginateSectionBlocksWithSplits`; new
506
+ * `endColumnByGlobalPageIndex` tracking in
507
+ * `paginateAllSectionsWithSplits` feeds the decision. Persisted
508
+ * LayCache envelopes from v39 invalidate because documents with
509
+ * cross-section multi-column content paginate differently under
510
+ * v40 (blocks that previously landed at `columnIndex: 0` after a
511
+ * `nextColumn` section break now land at `columnIndex: 1+`).
512
+ * Single-column documents are pagination-identical to v39.
513
+ */
514
+ /*
515
+ * v41 (2026-04-22) — refactor/05 cross-lane-coord pass. Adds
516
+ * `getRenderFrame` + `getRenderZoom` to `GeometryFacet` (passthrough
517
+ * to the render kernel) so UI chrome consumers can flip their reads
518
+ * off the layout-facet's now-@deprecated `getRenderFrame` /
519
+ * `getRenderZoom` methods without losing access to the underlying
520
+ * kernel primitives. Per Layer 04's cross-layer-coord §1.1 ask:
521
+ * those two methods are the last remaining geometry-shaped
522
+ * accessors on the layout facet; they exit once UI consumers flip.
523
+ * Shape change on `GeometryFacet` invalidates persisted v40 cache
524
+ * envelopes via the top-level version discriminator.
525
+ */
526
+ /*
527
+ * v42 (2026-04-22) — refactor/05 cross-lane-coord §8.4–§8.6 completion.
528
+ * Deletes `getRenderFrame` + `getRenderZoom` from the layout facet
529
+ * interface + impl + inert stub. Consumers (5 UI chrome files + 4
530
+ * tests) were migrated in the same pass to read through
531
+ * `runtime.geometry.getRenderFrame` / `.getRenderZoom`. Per L04's
532
+ * cross-layer-coord §1.1, the layout facet now exposes layout-only
533
+ * reads — no more geometry-shaped accessors. Cache envelopes from
534
+ * v41 invalidate via the top-level version discriminator.
535
+ */
536
+ /*
537
+ * v43 (2026-04-22) — refactor/04 post-closure Task 4: endnote reservation
538
+ * bug fix. `estimateFootnoteReservation` in `paginated-layout-engine.ts`
539
+ * now skips `noteKind === "endnote"` during per-page reservation math,
540
+ * matching the pre-existing skip in `snapshotNoteAllocations` (the
541
+ * emission path). Prior behavior inflated `reservedNoteHeight` with
542
+ * endnote body estimates on every page that referenced an endnote,
543
+ * squeezing body content onto additional pages even though endnotes
544
+ * flow to the document-end endnote area (P8.2, `getDocumentEndnoteBlocks`).
545
+ * Footnote behavior is unchanged. Cache envelopes from v42 invalidate
546
+ * because documents containing endnotes paginate differently under v43
547
+ * (fewer body pages when endnote bodies are large); footnote-only and
548
+ * note-free documents are pagination-identical to v42.
549
+ */
550
+ /*
551
+ * v44 (2026-04-22) — refactor/04 cross-layer-coord §1.12 Pass 2. Pure
552
+ * type relocation: `RuntimePageGraph`, `RuntimePageNode`, and
553
+ * `BuildPageGraphInput` move from `src/runtime/layout/page-graph.ts`
554
+ * into `src/model/layout/runtime-page-graph-types.ts`, along with
555
+ * their dependencies (`PageLayoutSnapshot`, `EditorStoryTarget`,
556
+ * `DocumentPageSnapshot`, `ResolvedPageStories`,
557
+ * `ResolvedDocumentSection` — the first three re-exported from
558
+ * `src/api/public-types.ts`, the latter two from their prior runtime
559
+ * homes, for consumer-import stability). `src/runtime/prerender/
560
+ * cache-envelope.ts` now imports `RuntimePageGraph` from model
561
+ * rather than runtime — cutting Layer 01's session→runtime transit
562
+ * through `CacheEnvelope` (L01 coord §1 primary ask). Graph
563
+ * construction (`buildPageGraph`, `spliceGraph`, `graphRevision`)
564
+ * stays in `src/runtime/layout/page-graph.ts`. Zero behavior change;
565
+ * cache envelopes from v43 invalidate because the layout-engine
566
+ * import graph materially changed shape (page-graph.ts's type
567
+ * declarations are now re-exports from model).
568
+ */
569
+ /*
570
+ * v47 (2026-04-23) — refactor/04 coord §1.15 / §1.18 — `FONT_AVG_CHAR_WIDTH`
571
+ * calibration against OpenType xAvgCharWidth. Pre-calibration values
572
+ * (Calibri 9.0 twips/half-point, TNR 9.2, etc.) were ~80% higher than
573
+ * published font metrics, producing a 50-73% runtime page overcount
574
+ * vs LibreOffice on CCEP contracts. Post truth-baseline's 2026-04-23
575
+ * oracle re-extraction (bundle 34d860831fe32b38) the miscalibration
576
+ * was isolated to the empirical measurement backend; the canvas
577
+ * backend measures real glyph widths via Canvas2D and is unaffected.
578
+ *
579
+ * New values are derived from each font's published xAvgCharWidth
580
+ * (OpenType OS/2 table) expressed as em-fraction × twips-per-em at
581
+ * the given font size. For Calibri: xAvgCharWidth = 1037/2048 em ≈
582
+ * 0.506em → at 11pt (22 half-points) = 5.57pt = 111 twips per char,
583
+ * matching the new factor of 5.0 × 22 = 110. The remaining 16 fonts
584
+ * in the table were recalibrated on the same basis.
585
+ *
586
+ * Impact on the 5 CCEP docs with re-extracted oracles:
587
+ * aps-short-form-supply-of-services 39 → 25 pages (−3.8% vs oracle 26)
588
+ * aps-supply-of-services 45 → 29 pages (+11.5% vs oracle 26)
589
+ * eu-global-consultancy-services 61 → 40 pages (+5.3% vs oracle 38)
590
+ * eu-global-supply-of-services 41 → 27 pages (+12.5% vs oracle 24)
591
+ * eu-tactical-sourcing-team-agreement 7 → 5 pages (−16.7% vs oracle 6)
592
+ *
593
+ * Baseline was 50-73% overcount across the four large docs; post-
594
+ * calibration deltas are −16.7% to +12.5%. 2/5 docs now within ±10%
595
+ * of oracle; residual over/under is attributable to secondary
596
+ * factors (widow/orphan, keep-with-next, minor per-font refinement).
597
+ *
598
+ * New `test/runtime/layout/ccep-multi-doc-parity-probe.test.ts`
599
+ * loads oracle page counts from the trusted-oracle dumps at
600
+ * `services/debug/scripts/.reports/diagnose-oracle-raw-*.json` at
601
+ * read time — the probe is single-source-of-truth: no oracle page
602
+ * counts are hardcoded anywhere in the runtime or the probe.
603
+ *
604
+ * Two factor-encoding unit tests in
605
+ * `test/runtime/layout/paginated-layout-engine.test.ts` were
606
+ * updated to the new expected values; one `pagination-state.test.ts`
607
+ * test was narrowed to its structural-invariant assertion (the
608
+ * column-definitions survive) rather than the measurement-specific
609
+ * page-count/offset values it had encoded. Cache envelopes from v46
610
+ * invalidate because pagination output changes for every document
611
+ * whose dominant font has a corrected factor (which is effectively
612
+ * all documents — the `DEFAULT_FONT_AVG_CHAR_WIDTH` fallback also
613
+ * shifted 10.0 → 5.5).
614
+ */
615
+ /*
616
+ * v46 (2026-04-23) — refactor/04 post-closure Task 5: compat-input
617
+ * ledger + projector exposure. New module
618
+ * `src/runtime/layout/compat-input-ledger.ts` exports
619
+ * `resolveLayoutCompatInputs(settings)` → typed `LayoutCompatInputs`
620
+ * snapshot covering seven named compat fields (compatibilityMode,
621
+ * doNotExpandShiftReturn, useWord97LineBreakRules, useFELayout,
622
+ * doNotUseHTMLParagraphAutoSpacing,
623
+ * overrideTableStyleFontSizeAndJustification,
624
+ * differentiateMultirowTableHeaders) plus passthrough buckets for
625
+ * every unknown `<w:compatSetting>` / `<w:compat>` boolean flag.
626
+ * `src/runtime/layout/projector.ts::buildLayoutDebugEntry` gains an
627
+ * optional `settings` input; when supplied, the entry's new
628
+ * `compatInputs` field surfaces the resolved ledger.
629
+ * `src/runtime/debug/build-debug-inspector-snapshot.ts` threads
630
+ * `document.subParts.settings` into the projector. LayoutDebugEntry
631
+ * `schemaVersion` bumps 1 → 2. Zero pagination behavior change —
632
+ * the ledger is pure diagnostic wiring today; future behavioral
633
+ * branches that gate on a compat flag now have a canonical access
634
+ * point instead of ad-hoc `settings.compatFlags?.[...]` reads.
635
+ * Cache envelopes from v45 invalidate because the layout module
636
+ * export surface widened (new `compat-input-ledger.ts` module + new
637
+ * optional projector field).
638
+ */
639
+ /*
640
+ * v45 (2026-04-23) — refactor/04 post-closure Task 3: measurement
641
+ * provider becomes authoritative. Three changes land under
642
+ * `src/runtime/layout/**`:
643
+ *
644
+ * 1. `paginated-layout-engine.ts` threads a real
645
+ * `Map<RunId, ResolvedRunFormatting>` into both
646
+ * `measureLineFragments` call sites via the new
647
+ * `buildRunFormattingMap(block)` helper in
648
+ * `resolved-formatting-state.ts`. The empirical backend still
649
+ * ignores `runs` (paragraph-level metrics suffice — same
650
+ * numerical output), but the canvas backend now reads
651
+ * `runs.get("${blockId}:${segmentId}")` for per-run font
652
+ * metrics so paragraphs with mixed inline fonts / sizes /
653
+ * weights produce accurate canvas-measured line counts.
654
+ * 2. `measurement-backend-empirical.ts` + `measurement-backend-
655
+ * canvas.ts` `measureLineFragments` now delegate image-segment
656
+ * line-displacement to their own `measureInlineObject` method
657
+ * instead of inlining `display === "floating" ? 2 : 1`. Zero
658
+ * numerical change; the provider method is now live inside
659
+ * each backend, so future work can specialize displacement
660
+ * math (e.g., canvas-backend ceil(heightTwips/lineHeight))
661
+ * without touching the line-fragments inner loop.
662
+ * 3. `layout-measurement-provider.ts` docstring clarifies that
663
+ * `measureTableBlock` is a coarse standalone helper and
664
+ * production table measurement goes through
665
+ * `src/runtime/layout/table-row-split.ts::measureTableRowHeights`
666
+ * (which already threads this provider through for per-cell
667
+ * paragraph measurement).
668
+ *
669
+ * Persisted envelopes from v44 invalidate because the runs-map
670
+ * input to canvas `measureLineFragments` and the backend
671
+ * dispatch topology changed shape. Empirical-backend numerical
672
+ * output is identical.
224
673
  */
225
- export const LAYOUT_ENGINE_VERSION = 24 as const;
674
+ export const LAYOUT_ENGINE_VERSION = 47 as const;
226
675
 
227
676
  /**
228
677
  * Serialization schema version for the LayCache payload (the cache envelope
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Layer 04 — `LayoutFacet` type surface.
3
+ *
4
+ * Per `docs/architecture/04-layout-semantics.md` and refactor plan Slice 2,
5
+ * `LayoutFacet` is the refactor-era structural name for the semantic layout
6
+ * surface consumers type against. The factory lives at
7
+ * `createLayoutFacet` in `./public-facet.ts`; the interface itself continues
8
+ * to be declared there as `WordReviewEditorLayoutFacet` — the long-standing
9
+ * public-API name — and `LayoutFacet` is a type alias.
10
+ *
11
+ * Why alias rather than relocate:
12
+ * - `WordReviewEditorLayoutFacet` is part of the package's public type
13
+ * surface and is imported by 49 sites plus the consumer-facing public-API
14
+ * reference. Relocating the declaration would churn all of them with no
15
+ * behavioral gain.
16
+ * - Declaring `LayoutFacet` as an alias at a stable import path gives
17
+ * consumers (runtime API, stateless services, debug inspectors, tests)
18
+ * a single short name to depend on during the refactor, independent of
19
+ * the public type that continues to anchor the public API.
20
+ *
21
+ * Consumers imported `WordReviewEditorLayoutFacet` from `./public-facet.ts`
22
+ * before Slice 2; they may switch to `LayoutFacet` from
23
+ * `./layout-facet-types.ts` at their own cadence. Both names resolve to
24
+ * the identical interface and remain assignment-compatible in both
25
+ * directions.
26
+ *
27
+ * Associated read-model types referenced in facet signatures are
28
+ * re-exported here so consumers can pull the whole type surface from one
29
+ * module.
30
+ */
31
+
32
+ export type {
33
+ // Facet surface --------------------------------------------------------
34
+ WordReviewEditorLayoutFacet as LayoutFacet,
35
+ WordReviewEditorLayoutFacet,
36
+
37
+ // Read-model types ----------------------------------------------------
38
+ PublicPageNode,
39
+ PublicPageRegions,
40
+ PublicPageRegion,
41
+ PublicRegionBlock,
42
+ PublicRegionKind,
43
+ PublicBlockFragment,
44
+ PublicLineBox,
45
+ PublicNoteAllocation,
46
+ PublicPageAnchor,
47
+ PublicPageSpan,
48
+ PublicSectionNode,
49
+ PublicResolvedPageStories,
50
+ PublicResolvedParagraphFormatting,
51
+ PublicResolvedRunFormatting,
52
+ PublicBlockMeasurement,
53
+ PublicMeasurementFidelity,
54
+ PublicFieldDirtinessReport,
55
+
56
+ // Events + invalidation ----------------------------------------------
57
+ LayoutFacetEvent,
58
+ LayoutFacetInvalidationReason,
59
+ RenderZoomSummary,
60
+ } from "./public-facet.ts";
@@ -74,6 +74,19 @@ export interface MeasuredTableBlock {
74
74
  rowHeights: number[];
75
75
  }
76
76
 
77
+ /**
78
+ * Task 3 (2026-04-23) note on `measureTableBlock`: the production
79
+ * table-measurement path is `src/runtime/layout/table-row-split.ts
80
+ * ::measureTableRowHeights`, which threads this provider through for
81
+ * per-cell paragraph measurement and integrates with `findTableRowSplit`
82
+ * for pagination. `measureTableBlock` on this contract remains a
83
+ * coarse standalone helper (used when a table's height is needed
84
+ * without row-split context). Both empirical and canvas backends
85
+ * implement it with the same lightweight estimation; consumers that
86
+ * need accurate per-cell measurement should call `measureTableRowHeights`
87
+ * directly.
88
+ */
89
+
77
90
  export interface LayoutMeasurementProvider {
78
91
  readonly fidelity: MeasurementFidelity;
79
92
  /**
@@ -214,11 +214,23 @@ export function createCanvasBackend(
214
214
  currentLineWidth = 0;
215
215
  currentCapacity = subsequentLineWidth;
216
216
  break;
217
- case "image":
218
- lineCount += segment.display === "floating" ? 2 : 1;
217
+ case "image": {
218
+ // Route through this backend's own `measureInlineObject` so
219
+ // the provider contract is live inside the canvas backend
220
+ // (Task 3, 2026-04-23). Intrinsic dimensions are not carried
221
+ // on the surface segment here; backends that later want
222
+ // glyph-height-aware displacement can resolve from the
223
+ // segment's `anchor.extent` at the caller.
224
+ const measured = measureInlineObject({
225
+ widthTwips: 0,
226
+ heightTwips: 0,
227
+ display: segment.display === "floating" ? "floating" : "inline",
228
+ });
229
+ lineCount += Math.max(1, measured.displacedLineCount);
219
230
  currentLineWidth = 0;
220
231
  currentCapacity = subsequentLineWidth;
221
232
  break;
233
+ }
222
234
  case "note_ref":
223
235
  case "opaque_inline":
224
236
  // Treat as a single narrow character.
@@ -31,24 +31,37 @@ const TABLE_ROW_PADDING_TWIPS = 120;
31
31
 
32
32
  export function createEmpiricalBackend(): LayoutMeasurementProvider {
33
33
  const fidelity: MeasurementFidelity = "empirical";
34
- return {
34
+ // `measureLineFragments` dispatches through the backend object's own
35
+ // `measureInlineObject` method at call time (not via closure capture)
36
+ // so consumers can swap the inline measurement strategy after
37
+ // construction — Task 3 (2026-04-23) routed image segments through
38
+ // the provider contract instead of hardcoded displacement math.
39
+ const backend: LayoutMeasurementProvider = {
35
40
  get fidelity() {
36
41
  return fidelity;
37
42
  },
38
43
  whenReady() {
39
44
  return Promise.resolve();
40
45
  },
41
- measureLineFragments,
46
+ measureLineFragments(input) {
47
+ return measureLineFragments(input, (objectInput) =>
48
+ backend.measureInlineObject(objectInput),
49
+ );
50
+ },
42
51
  measureInlineObject,
43
52
  measureTableBlock,
44
53
  invalidateCache() {
45
54
  /* no cache in empirical mode */
46
55
  },
47
56
  };
57
+ return backend;
48
58
  }
49
59
 
50
60
  function measureLineFragments(
51
61
  input: MeasureLineFragmentsInput,
62
+ inlineObjectMeasurer: (
63
+ objectInput: MeasureInlineObjectInput,
64
+ ) => MeasuredInlineObject,
52
65
  ): MeasuredLineFragments {
53
66
  const { block, formatting, columnWidth } = input;
54
67
  const firstLineWidth = resolveTextWidth(formatting, columnWidth, true);
@@ -93,11 +106,17 @@ function measureLineFragments(
93
106
  currentLineChars = 0;
94
107
  currentLineCapacity = subsequentLineCapacity;
95
108
  break;
96
- case "image":
97
- lineCount += segment.display === "floating" ? 2 : 1;
109
+ case "image": {
110
+ const measured = inlineObjectMeasurer({
111
+ widthTwips: 0,
112
+ heightTwips: 0,
113
+ display: segment.display === "floating" ? "floating" : "inline",
114
+ });
115
+ lineCount += Math.max(1, measured.displacedLineCount);
98
116
  currentLineChars = 0;
99
117
  currentLineCapacity = subsequentLineCapacity;
100
118
  break;
119
+ }
101
120
  case "note_ref":
102
121
  currentLineChars += 1;
103
122
  if (currentLineChars > currentLineCapacity) {