@beyondwork/docx-react-component 1.0.106 → 1.0.108

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 (190) hide show
  1. package/package.json +19 -5
  2. package/src/api/geometry-overlay-rects.ts +5 -0
  3. package/src/api/package-version.ts +1 -1
  4. package/src/api/page-anchor-id.ts +5 -0
  5. package/src/api/public-types.ts +16 -9
  6. package/src/api/table-node-specs.ts +6 -0
  7. package/src/api/v3/_create.ts +2 -1
  8. package/src/api/v3/_page-anchor-id.ts +52 -0
  9. package/src/api/v3/_runtime-handle.ts +92 -1
  10. package/src/api/v3/ai/_audit-time.ts +5 -0
  11. package/src/api/v3/ai/_pe2-evidence.ts +38 -0
  12. package/src/api/v3/ai/attach.ts +7 -2
  13. package/src/api/v3/ai/replacement.ts +101 -18
  14. package/src/api/v3/ai/resolve.ts +2 -2
  15. package/src/api/v3/ai/review.ts +177 -3
  16. package/src/api/v3/index.ts +1 -0
  17. package/src/api/v3/runtime/collab.ts +462 -0
  18. package/src/api/v3/runtime/document.ts +503 -20
  19. package/src/api/v3/runtime/geometry.ts +97 -0
  20. package/src/api/v3/runtime/layout.ts +744 -0
  21. package/src/api/v3/runtime/perf-probe.ts +14 -0
  22. package/src/api/v3/runtime/viewport.ts +9 -8
  23. package/src/api/v3/ui/_types.ts +149 -55
  24. package/src/api/v3/ui/chrome-preset-model.ts +5 -5
  25. package/src/api/v3/ui/debug.ts +115 -2
  26. package/src/api/v3/ui/index.ts +13 -0
  27. package/src/api/v3/ui/overlays.ts +0 -8
  28. package/src/api/v3/ui/surface.ts +56 -0
  29. package/src/api/v3/ui/viewport.ts +22 -9
  30. package/src/core/commands/image-commands.ts +1 -0
  31. package/src/core/commands/index.ts +6 -0
  32. package/src/core/schema/text-schema.ts +43 -5
  33. package/src/core/selection/mapping.ts +8 -1
  34. package/src/core/selection/review-anchors.ts +5 -1
  35. package/src/core/state/text-transaction.ts +8 -2
  36. package/src/io/export/serialize-revisions.ts +149 -1
  37. package/src/io/normalize/normalize-text.ts +6 -0
  38. package/src/io/ooxml/parse-bookmark-references.ts +55 -0
  39. package/src/io/ooxml/parse-fields.ts +24 -2
  40. package/src/io/ooxml/parse-headers-footers.ts +38 -5
  41. package/src/io/ooxml/parse-main-document.ts +153 -9
  42. package/src/io/ooxml/parse-numbering.ts +20 -0
  43. package/src/io/ooxml/parse-revisions.ts +19 -8
  44. package/src/io/opc/package-reader.ts +98 -8
  45. package/src/model/anchor.ts +4 -3
  46. package/src/model/canonical-document.ts +220 -2
  47. package/src/model/canonical-hash.ts +221 -0
  48. package/src/model/canonical-layout-inputs.ts +245 -6
  49. package/src/model/layout/index.ts +1 -0
  50. package/src/model/layout/page-graph-types.ts +118 -1
  51. package/src/model/review/revision-types.ts +14 -3
  52. package/src/preservation/store.ts +20 -4
  53. package/src/review/README.md +1 -1
  54. package/src/review/store/revision-actions.ts +14 -2
  55. package/src/runtime/collab/event-types.ts +67 -1
  56. package/src/runtime/collab/runtime-collab-sync.ts +177 -5
  57. package/src/runtime/diagnostics/layout-guard-warning.ts +18 -0
  58. package/src/runtime/document-heading-outline.ts +147 -0
  59. package/src/runtime/document-navigation.ts +8 -243
  60. package/src/runtime/document-runtime.ts +240 -97
  61. package/src/runtime/edit-dispatch/dispatch-text-command.ts +11 -0
  62. package/src/runtime/formatting/layout-inputs.ts +38 -5
  63. package/src/runtime/formatting/numbering/geometry.ts +28 -2
  64. package/src/runtime/geometry/adjacent-geometry-intake.ts +835 -0
  65. package/src/runtime/geometry/caret-geometry.ts +5 -6
  66. package/src/runtime/geometry/geometry-facet.ts +60 -10
  67. package/src/runtime/geometry/geometry-index.ts +591 -20
  68. package/src/runtime/geometry/geometry-types.ts +59 -0
  69. package/src/runtime/geometry/hit-test.ts +11 -1
  70. package/src/runtime/geometry/overlay-rects.ts +5 -3
  71. package/src/runtime/geometry/project-anchors.ts +1 -1
  72. package/src/runtime/geometry/word-layout-v2-line-intake.ts +323 -0
  73. package/src/runtime/layout/index.ts +6 -0
  74. package/src/runtime/layout/layout-engine-instance.ts +6 -1
  75. package/src/runtime/layout/layout-engine-version.ts +181 -16
  76. package/src/runtime/layout/layout-facet-types.ts +6 -0
  77. package/src/runtime/layout/page-graph.ts +21 -4
  78. package/src/runtime/layout/paginated-layout-engine.ts +139 -15
  79. package/src/runtime/layout/project-block-fragments.ts +265 -7
  80. package/src/runtime/layout/public-facet.ts +78 -24
  81. package/src/runtime/layout/table-row-continuation-contract.ts +107 -0
  82. package/src/runtime/layout/table-row-split.ts +92 -35
  83. package/src/runtime/prerender/cache-envelope.ts +2 -2
  84. package/src/runtime/prerender/cache-key.ts +5 -4
  85. package/src/runtime/prerender/customxml-cache.ts +0 -1
  86. package/src/runtime/render/render-kernel.ts +1 -1
  87. package/src/runtime/revision-runtime.ts +112 -10
  88. package/src/runtime/scopes/_scope-dependencies.ts +1 -0
  89. package/src/runtime/scopes/action-validation.ts +22 -2
  90. package/src/runtime/scopes/capabilities.ts +316 -0
  91. package/src/runtime/scopes/compile-scope-bundle.ts +14 -0
  92. package/src/runtime/scopes/compiler-service.ts +108 -4
  93. package/src/runtime/scopes/content-control-evidence.ts +79 -0
  94. package/src/runtime/scopes/create-issue.ts +5 -5
  95. package/src/runtime/scopes/evidence.ts +91 -0
  96. package/src/runtime/scopes/formatting/apply.ts +2 -0
  97. package/src/runtime/scopes/geometry-evidence.ts +130 -0
  98. package/src/runtime/scopes/index.ts +54 -0
  99. package/src/runtime/scopes/issue-lifecycle.ts +224 -0
  100. package/src/runtime/scopes/layout-evidence.ts +374 -0
  101. package/src/runtime/scopes/multi-paragraph-refusal.ts +37 -0
  102. package/src/runtime/scopes/preservation-boundary.ts +7 -1
  103. package/src/runtime/scopes/replacement/apply.ts +97 -34
  104. package/src/runtime/scopes/scope-kinds/paragraph.ts +108 -12
  105. package/src/runtime/scopes/semantic-scope-types.ts +242 -3
  106. package/src/runtime/scopes/visualization.ts +28 -0
  107. package/src/runtime/surface-projection.ts +44 -5
  108. package/src/runtime/telemetry/perf-probe.ts +216 -0
  109. package/src/runtime/virtualized-rendering.ts +36 -1
  110. package/src/runtime/workflow/ai-issue-lifecycle.ts +253 -0
  111. package/src/runtime/workflow/coordinator.ts +39 -11
  112. package/src/runtime/workflow/derived-scope-resolver.ts +63 -9
  113. package/src/runtime/workflow/index.ts +3 -0
  114. package/src/runtime/workflow/overlay-lane-types.ts +58 -0
  115. package/src/runtime/workflow/overlay-lanes.ts +168 -10
  116. package/src/runtime/workflow/overlay-store.ts +2 -2
  117. package/src/runtime/workflow/redline-posture-calibration.ts +257 -0
  118. package/src/runtime/workflow/word-field-matrix-calibration.ts +231 -0
  119. package/src/session/_sync-legacy.ts +17 -27
  120. package/src/session/import/loader.ts +6 -4
  121. package/src/session/import/source-package-evidence.ts +186 -2
  122. package/src/session/index.ts +5 -6
  123. package/src/session/session.ts +30 -56
  124. package/src/session/types.ts +8 -13
  125. package/src/shell/session-bootstrap.ts +155 -81
  126. package/src/ui/WordReviewEditor.tsx +520 -12
  127. package/src/ui/editor-shell-view.tsx +14 -4
  128. package/src/ui/editor-surface-controller.tsx +5 -3
  129. package/src/ui/headless/selection-tool-resolver.ts +1 -2
  130. package/src/ui/presence-overlay-lane.ts +0 -1
  131. package/src/ui/ui-controller-factory.ts +7 -0
  132. package/src/ui-tailwind/chrome/build-context-menu-entries.ts +5 -1
  133. package/src/ui-tailwind/chrome/editor-action-registry.ts +105 -5
  134. package/src/ui-tailwind/chrome/editor-actions-to-palette.ts +7 -0
  135. package/src/ui-tailwind/chrome/layer-debug-contracts.ts +208 -0
  136. package/src/ui-tailwind/chrome/resolve-target-kind.ts +13 -0
  137. package/src/ui-tailwind/chrome/tw-alert-banner.tsx +11 -3
  138. package/src/ui-tailwind/chrome/tw-command-palette.tsx +36 -6
  139. package/src/ui-tailwind/chrome/tw-context-menu.tsx +6 -1
  140. package/src/ui-tailwind/chrome/tw-display-mode-selector.tsx +42 -109
  141. package/src/ui-tailwind/chrome/tw-inline-find-bar.tsx +26 -6
  142. package/src/ui-tailwind/chrome/tw-navigation-command-bar.tsx +328 -0
  143. package/src/ui-tailwind/chrome/tw-object-context-toolbar.tsx +8 -4
  144. package/src/ui-tailwind/chrome/tw-runtime-repl-dialog.tsx +129 -1
  145. package/src/ui-tailwind/chrome/tw-selection-tool-host.tsx +19 -5
  146. package/src/ui-tailwind/chrome/tw-selection-tool-structure.tsx +5 -1
  147. package/src/ui-tailwind/chrome/tw-workspace-chrome-host.tsx +28 -12
  148. package/src/ui-tailwind/chrome-overlay/tw-chrome-overlay.tsx +30 -3
  149. package/src/ui-tailwind/chrome-overlay/tw-object-selection-overlay.tsx +116 -10
  150. package/src/ui-tailwind/chrome-overlay/tw-page-stack-overlay-layer.tsx +223 -94
  151. package/src/ui-tailwind/chrome-overlay/tw-presence-overlay-lane.tsx +157 -0
  152. package/src/ui-tailwind/chrome-overlay/tw-review-overlay-lane-markers.tsx +259 -0
  153. package/src/ui-tailwind/chrome-overlay/tw-scope-card-layer.tsx +5 -2
  154. package/src/ui-tailwind/chrome-overlay/tw-substrate-overlay-lanes.tsx +314 -0
  155. package/src/ui-tailwind/debug/README.md +4 -1
  156. package/src/ui-tailwind/debug/layer11-consumer-readiness.ts +272 -0
  157. package/src/ui-tailwind/debug/layer11-word-field-matrix-evidence.ts +160 -0
  158. package/src/ui-tailwind/editor-surface/perf-probe.ts +14 -215
  159. package/src/ui-tailwind/editor-surface/pm-decorations.ts +42 -0
  160. package/src/ui-tailwind/editor-surface/pm-position-map.ts +38 -2
  161. package/src/ui-tailwind/editor-surface/pm-schema.ts +14 -4
  162. package/src/ui-tailwind/editor-surface/pm-state-from-snapshot.ts +34 -5
  163. package/src/ui-tailwind/editor-surface/runtime-decoration-plugin.ts +9 -19
  164. package/src/ui-tailwind/editor-surface/surface-build-keys.ts +2 -2
  165. package/src/ui-tailwind/editor-surface/tw-page-block-view.helpers.ts +145 -0
  166. package/src/ui-tailwind/editor-surface/tw-page-block-view.tsx +16 -11
  167. package/src/ui-tailwind/editor-surface/tw-prosemirror-surface.tsx +8 -10
  168. package/src/ui-tailwind/editor-surface/tw-table-node-view.tsx +3 -0
  169. package/src/ui-tailwind/page-stack/tw-page-chrome-entry.tsx +4 -2
  170. package/src/ui-tailwind/page-stack/tw-page-stack-chrome-layer.tsx +60 -20
  171. package/src/ui-tailwind/page-stack/tw-region-block-renderer.tsx +16 -11
  172. package/src/ui-tailwind/review/tw-health-panel.tsx +36 -17
  173. package/src/ui-tailwind/review/tw-review-rail.tsx +7 -4
  174. package/src/ui-tailwind/review-workspace/diagnostics-visibility.ts +44 -0
  175. package/src/ui-tailwind/review-workspace/page-shell-metrics.ts +11 -0
  176. package/src/ui-tailwind/review-workspace/tw-review-workspace-rail.tsx +16 -1
  177. package/src/ui-tailwind/review-workspace/types.ts +26 -12
  178. package/src/ui-tailwind/review-workspace/use-diagnostics-signal.ts +40 -11
  179. package/src/ui-tailwind/review-workspace/use-layout-facet-render-signal.ts +2 -1
  180. package/src/ui-tailwind/review-workspace/use-page-markers.ts +15 -26
  181. package/src/ui-tailwind/review-workspace/use-scope-card-state.ts +35 -18
  182. package/src/ui-tailwind/review-workspace/use-selection-toolbar-placement.ts +41 -32
  183. package/src/ui-tailwind/review-workspace/use-status-bar-page-facts.ts +2 -1
  184. package/src/ui-tailwind/review-workspace/use-workspace-side-effects.ts +2 -1
  185. package/src/ui-tailwind/status/tw-status-bar.tsx +6 -5
  186. package/src/ui-tailwind/toolbar/tw-role-action-region.tsx +52 -80
  187. package/src/ui-tailwind/toolbar/tw-shell-header.tsx +12 -48
  188. package/src/ui-tailwind/toolbar/tw-toolbar-icon-button.tsx +9 -4
  189. package/src/ui-tailwind/toolbar/tw-toolbar.tsx +328 -361
  190. package/src/ui-tailwind/tw-review-workspace.tsx +152 -286
@@ -1,18 +1,20 @@
1
1
  /**
2
- * Layout engine version markerbump when **any** file under
3
- * `src/runtime/layout/**` or `src/runtime/render/**` changes, or when
4
- * the page-break widget DOM shape under `src/ui-tailwind/editor-surface/`
5
- * changes in a way that affects cached render-frame diffs or persisted
6
- * layout caches.
2
+ * Layout engine version markersco-touch this file when **any** file under
3
+ * `src/runtime/layout/**` or `src/runtime/render/**` changes, or when the
4
+ * page-break widget DOM shape under `src/ui-tailwind/editor-surface/` changes
5
+ * in a way that affects render-frame diffs or persisted layout caches.
7
6
  *
8
7
  * Enforcement: `scripts/ci-check-layout-engine-version.mjs` inspects the
9
- * PR diff; if any file in the watched trees is touched without this
10
- * constant being co-touched, CI fails. See CLAUDE.md → *Performance
8
+ * PR diff; if any file in the watched trees is touched without this file
9
+ * being co-touched, CI fails. See CLAUDE.md → *Performance
11
10
  * Invariants* for the full contract.
12
11
  *
13
- * Persisted caches should key their stored snapshots on this version so a
14
- * bump automatically invalidates stale entries no corruption path
15
- * exists because the version is the cache's top-level discriminator.
12
+ * `LAYOUT_ENGINE_VERSION` is the layout-output correctness discriminator used
13
+ * by persisted laycache envelopes, projection, telemetry, debug, and public
14
+ * layout readers. Bump it when the same canonical input can produce different
15
+ * pagination, graph semantics, measurement/page geometry, render-frame output,
16
+ * or cache-key semantics. When only the laycache envelope or cached payload
17
+ * shape grows, leave this value alone and bump `LAYCACHE_SCHEMA_VERSION`.
16
18
  *
17
19
  * History:
18
20
  * 1 — initial materialization (L8 Phase B).
@@ -1081,14 +1083,165 @@
1081
1083
  * pagination role, measured extent, and field-family hints where applicable.
1082
1084
  * Cache envelopes from v68 invalidate because fragment payloads can now
1083
1085
  * include layout-object descriptors. Shipped via pe2 commit `9c4417418`.
1086
+ *
1087
+ * 70 — PE2 review follow-up: page-local header/footer anchored-object
1088
+ * ledgers now mint instance-stable object ids for reused image/drawing
1089
+ * relationships and keep media/blip references as metadata. Cache
1090
+ * envelopes from v69 invalidate because page-frame object ids, signatures,
1091
+ * and divergence ids can change when a story reuses the same image
1092
+ * relationship. Shipped via pe2 commit `788bebef3`.
1093
+ *
1094
+ * 71 — PE2 Slice 1 cleanup: the standalone document navigation snapshot
1095
+ * adapter now delegates page stack and active-page derivation to the L04
1096
+ * layout engine graph instead of running its own `buildPageStack` path.
1097
+ * Cache envelopes from v70 invalidate because public navigation snapshots
1098
+ * are now sourced from graph-derived page truth. Shipped via pe2 commit
1099
+ * `cfd568942`.
1100
+ *
1101
+ * 72 — PE2 Slice 2 public graph surface: `PublicPageLocalStoryInstance`
1102
+ * now publishes the graph-owned `resolvedFields` ledger for page-local
1103
+ * header/footer PAGE / NUMPAGES / SECTIONPAGES fields. Cache envelopes
1104
+ * from v71 invalidate so public page-frame consumers cannot observe the
1105
+ * old story projection shape. Shipped via pe2 commit `e887a042d`.
1106
+ *
1107
+ * 73 — PE2 Slice 4 public divergence handles: `PublicLayoutDivergence`
1108
+ * now publishes graph-owned `objectIds` for object-scoped divergences
1109
+ * such as unsupported wrap and preserve-only placeholders. Cache envelopes
1110
+ * from v72 invalidate so public consumers can join divergence rows back
1111
+ * to page-local anchored-object ledgers without reconstructing ids.
1112
+ * Shipped via pe2 commit `e39766d6b`.
1113
+ *
1114
+ * 74 — Ship-side rollup: pe2 commits b58c0d45c (collapsed-selection
1115
+ * caret rect now carries an explicit `precision: "exact"` tag) and
1116
+ * de9b32423 (page-overlay rect projection requires complete frame
1117
+ * geometry) changed geometry projections without bumping the engine
1118
+ * version on pe2. Cache envelopes from v73 invalidate so persisted
1119
+ * selection-rect / page-overlay payloads cannot leak the older
1120
+ * precision-tag and overlay shapes through.
1121
+ *
1122
+ * 75 — Ship-side rollup: pe2 commit d4ac70a06 ("fix(05): tag anchor
1123
+ * hit target rects") added precision tagging to anchor hit-target
1124
+ * rects in `geometry-index.ts` + `hit-test.ts` without bumping the
1125
+ * engine version on pe2. Cache envelopes from v74 invalidate so
1126
+ * persisted hit-target payloads cannot leak the older untagged
1127
+ * shape through.
1128
+ *
1129
+ * 76 — KI-005 precision-geometry prerequisite: fragment layout-object rows
1130
+ * for numbered paragraphs now carry resolved numbering source facts
1131
+ * (marker text/suffix, list instance + level, marker lane, text column, tab
1132
+ * stops, and marker justification) as twips/plain graph truth. Cache
1133
+ * envelopes from v75 invalidate because fragment payloads can now include
1134
+ * numbering facts consumed by geometry/debug projections. Shipped via pe2
1135
+ * commit `8ff418fed`.
1136
+ *
1137
+ * 77 — PE2 L07 blocker service: public page-frame records now carry an
1138
+ * explicit `completeness` discriminator (`complete` / `partial` / `absent`)
1139
+ * derived from the L04 twips region ledger. Cache envelopes from v76
1140
+ * invalidate so runtime/API consumers can distinguish realized empty
1141
+ * page-local story ledgers from missing frame substrate. Shipped via pe2
1142
+ * commits `96b89b221` + `da704503b`.
1143
+ *
1144
+ * 78 — Ship-side rollup: pe2 commit 2b2cd7c2f ("feat(04): expose page-local
1145
+ * object wrap evidence") added a `wrapMode` field to page-local
1146
+ * anchored-object rows in the runtime graph and public page-frame surface,
1147
+ * plus pe2 commits 8d94bb10e and 246b2d299 (event telemetry / export safety)
1148
+ * that brushed `src/runtime/layout/page-graph.ts` and `public-facet.ts`,
1149
+ * without bumping the engine version on pe2. Cache envelopes from v77
1150
+ * invalidate so persisted page-frame story signatures and anchored-object
1151
+ * payloads cannot leak the older shape through.
1152
+ *
1153
+ * 79 — Ship-side rollup: pe2 commit 6389423bb ("perf(05): avoid duplicate
1154
+ * marker projection") changed how geometry coverage summaries read
1155
+ * numbering-marker precision/status metadata without rebuilding the marker
1156
+ * rect projection solely for counts; pe2 also brushed
1157
+ * `src/runtime/layout/public-facet.ts` via 6fcf11477 ("Route L04 oracle
1158
+ * unlock evidence work") without bumping on pe2. Pixel geometry is
1159
+ * unchanged, but geometry is a watched runtime projection surface, so the
1160
+ * version advances with the implementation slice. Cache envelopes from v78
1161
+ * invalidate.
1162
+ *
1163
+ * 80 — Ship-side rollup: pe2 commit b76c9a026 ("test(05): harden geometry
1164
+ * cleanup slice") brushed `src/runtime/layout/public-facet.ts` and the
1165
+ * layout-guard diagnostics helper as part of the L05 guard-warning
1166
+ * dependency cleanup. The pe2 history-block bumped to v76 in isolation, but
1167
+ * ship-preview is already at v79, so this rollup advances to v80 to keep
1168
+ * persisted laycache envelopes from leaking the older facet shape through.
1169
+ * Pixel geometry is unchanged.
1170
+ *
1171
+ * 81 — Ship-side rollup: pe2 commit 118171efd ("feat(10): add object drag
1172
+ * lifecycle") added a UI API drag session for mounted object handles
1173
+ * (update/commit/cancel) and commits image resize/reposition through the
1174
+ * existing runtime mutation path. Layout geometry is invalidated by those
1175
+ * committed mutations; the pe2 history-block bumped to v77 in isolation, so
1176
+ * ship-preview's rollup advances to v81. Cache envelopes from v80
1177
+ * invalidate so object-handle chrome cannot replay against a pre-lifecycle
1178
+ * surface.
1179
+ *
1180
+ * 82 — Ship-side rollup: pe2 commit 348fcaf66 ("feat(05): clip table geometry
1181
+ * to continuation rows") brushed `src/runtime/geometry/geometry-index.ts`
1182
+ * to clip table geometry to continuation rows, without bumping on pe2.
1183
+ * `src/runtime/geometry/**` is in the CI guard's watched-prefixes set; cache
1184
+ * envelopes from v81 invalidate so persisted geometry projections cannot
1185
+ * replay against the pre-clipping shape.
1186
+ *
1187
+ * 83 — Ship-side rollup: pe2 commits cf6f54878 ("Add L04 D-8 table split-row
1188
+ * contract") + b704efa82 ("Implement L04 split-row carry slices") added L04
1189
+ * D-8 table split-row carry: table pagination can now include a splittable
1190
+ * overflowing row on both adjacent row slices, with typed `splitRowCarry`
1191
+ * continuation metadata and runtime projection overlap readbacks. The pe2
1192
+ * history-block bumped to v78 in isolation; ship-preview's rollup advances
1193
+ * to v83. Cache envelopes from v82 invalidate because table fragment row
1194
+ * ranges and continuation cursor payloads can change for splittable
1195
+ * cross-page rows.
1196
+ *
1197
+ * 84 — Ship-side rollup: pe2 commit 763047797 ("Calibrate L04 auto-height
1198
+ * table splits") landed L04 D-8 auto-height table calibration: table row
1199
+ * pagination now uses resolved cell-content height only for materially
1200
+ * taller auto-height rows in non-repeated-header tables, and uses that
1201
+ * calibrated height to emit bounded split-row carry on CCEP D-8 targets.
1202
+ * The pe2 history-block bumped to v79 in isolation; ship-preview's rollup
1203
+ * advances to v84. Cache envelopes from v83 invalidate because auto-height
1204
+ * table row ranges and continuation cursor payloads can change.
1205
+ *
1206
+ * 85 — Ship-side rollup: pe2 commit a1b4a2e10 ("Carry default table rows
1207
+ * across pages") landed L04 D-8 default table-row carry: default
1208
+ * non-`cantSplit` / non-`exact` rows can now emit split-row carry, matching
1209
+ * Word's default row-break posture for newly unlocked clean D-8 table data
1210
+ * without broadening the resolved-height calibration gate. The pe2
1211
+ * history-block bumped to v80 in isolation; ship-preview's rollup advances
1212
+ * to v85. Cache envelopes from v84 invalidate because table row ranges and
1213
+ * continuation cursor payloads can change.
1214
+ *
1215
+ * 86 — Ship-side rollup: pe2 commits 3458b8c53 ("feat(05): expose table
1216
+ * split row carry geometry") + 68e9e8528 ("feat(05): ingest word layout
1217
+ * line geometry") + 089536b59 ("perf(05): cache geometry index
1218
+ * projection") + 805b42af5 ("fix(l11): relocate perf probe telemetry")
1219
+ * brushed `src/runtime/geometry/**` and `src/runtime/render/**` without
1220
+ * bumping on pe2. The pe2 history-block stayed at v80; ship-preview's
1221
+ * rollup advances to v86. Cache envelopes from v85 invalidate because
1222
+ * geometry projections (split-row carry geometry, word layout line
1223
+ * geometry, cached geometry index) and render-side perf probe telemetry
1224
+ * shapes can change.
1225
+ *
1226
+ * 87 — Ship-side rollup: pe2 commit a4dc6f6a3 ("Close L04 final-gap D8
1227
+ * table behavior") landed L04 final-gap D-8 table behavior — long
1228
+ * non-header auto tables now get a bounded per-row soft content-height
1229
+ * allowance, and long compact authored-height tables cap dense auto rows
1230
+ * while suppressing synthetic header reservation for split math. This
1231
+ * moves the repaired clean D-8 table candidates toward Word row ranges
1232
+ * without applying broad resolved heights to every long table. The pe2
1233
+ * history-block bumped to v81 in isolation; ship-preview's rollup advances
1234
+ * to v87. Cache envelopes from v86 invalidate because table row ranges and
1235
+ * continuation cursor payloads can change.
1084
1236
  */
1085
- export const LAYOUT_ENGINE_VERSION = 69 as const;
1237
+ export const LAYOUT_ENGINE_VERSION = 87 as const;
1086
1238
 
1087
1239
  /**
1088
- * Serialization schema version for the LayCache payload (the cache envelope
1089
- * stored in IndexedDB, and post Plan B the customXml editor-state
1090
- * namespace). Bump independently of LAYOUT_ENGINE_VERSION when the
1091
- * envelope shape changes but the layout engine itself has not.
1240
+ * Serialization schema version for the LayCache envelope and cached payload
1241
+ * shape. This is the cache-shape discriminator for IndexedDB and customXml
1242
+ * laycache entries. Bump independently of `LAYOUT_ENGINE_VERSION` when the
1243
+ * envelope shape or cached graph/surface payload grows, but the same canonical
1244
+ * input would still produce layout-compatible pagination output.
1092
1245
  *
1093
1246
  * History:
1094
1247
  * 1 — initial envelope shape: { schemaVersion, engineVersion,
@@ -1110,5 +1263,17 @@ export const LAYOUT_ENGINE_VERSION = 69 as const;
1110
1263
  * written without the report are still valid; readers fall through
1111
1264
  * to the live `buildCompatibilityReport` call. v2 envelopes are
1112
1265
  * rejected cleanly on v3 readers (schemaVersion mismatch).
1266
+ * 4 — Pass-8 versioning discipline: graph/surface payload-shape changes now
1267
+ * route here instead of a separate cache-engine discriminator. The
1268
+ * envelope still stores `engineVersion`, but that field is the
1269
+ * `LAYOUT_ENGINE_VERSION` correctness tag for layout-output changes.
1270
+ * Additive cached-payload fields route to this schema discriminator.
1271
+ * 5 — L04 field-region runtime readback: fragment layout objects can now
1272
+ * carry source/canonical field-region identities for Debug/L05 joins.
1273
+ * Pagination output is unchanged, but cached graph payload shape grows.
1274
+ * 6 — L04 numbering runtime readback: fragment layout objects can now carry
1275
+ * `numberingRows[]` for all numbered paragraphs represented by the
1276
+ * fragment, including table/SDT-nested paragraphs. Pagination output is
1277
+ * unchanged, but cached graph payload shape grows.
1113
1278
  */
1114
- export const LAYCACHE_SCHEMA_VERSION = 3 as const;
1279
+ export const LAYCACHE_SCHEMA_VERSION = 6 as const;
@@ -44,14 +44,20 @@ export type {
44
44
  PublicLineBox,
45
45
  PublicNoteAllocation,
46
46
  PublicPageAnchor,
47
+ PublicPageFrame,
48
+ PublicPageLocalStoryInstance,
47
49
  PublicPageSpan,
48
50
  PublicSectionNode,
49
51
  PublicResolvedPageStories,
52
+ PublicResolvedStoryField,
50
53
  PublicResolvedParagraphFormatting,
51
54
  PublicResolvedRunFormatting,
55
+ PublicStoryAnchoredObject,
56
+ PublicTwipsRect,
52
57
  PublicBlockMeasurement,
53
58
  PublicMeasurementFidelity,
54
59
  PublicFieldDirtinessReport,
60
+ PublicLayoutDivergence,
55
61
 
56
62
  // Events + invalidation ----------------------------------------------
57
63
  LayoutFacetEvent,
@@ -65,6 +65,7 @@ export type {
65
65
  RuntimeLayoutDivergenceKind,
66
66
  RuntimeLayoutDivergence,
67
67
  RuntimePageFrame,
68
+ RuntimePageFrameCompleteness,
68
69
  RuntimePageLocalStoryInstance,
69
70
  RuntimeResolvedStoryField,
70
71
  RuntimeStoryAnchoredObject,
@@ -453,6 +454,7 @@ function buildPageFrame(input: {
453
454
  const divergences = [...input.divergences, ...pageLocalStoryResult.divergences];
454
455
  const pageLocalStories = pageLocalStoryResult.instances;
455
456
  const divergenceIds = divergences.map((d) => d.divergenceId);
457
+ const completeness = resolvePageFrameCompleteness(regions);
456
458
  return {
457
459
  frame: {
458
460
  frameId: input.frameId,
@@ -460,6 +462,7 @@ function buildPageFrame(input: {
460
462
  pageIndex: input.pageIndex,
461
463
  sectionIndex: input.sectionIndex,
462
464
  displayPageNumber: input.displayPageNumber,
465
+ completeness,
463
466
  physicalBoundsTwips: rect(0, 0, input.layout.pageWidth, input.layout.pageHeight),
464
467
  regions,
465
468
  pageLocalStories,
@@ -615,6 +618,8 @@ function buildPageLocalStorySignature(input: {
615
618
  object.extentTwips?.widthTwips ?? "",
616
619
  object.extentTwips?.heightTwips ?? "",
617
620
  object.relationshipIds?.join(",") ?? "",
621
+ object.mediaIds?.join(",") ?? "",
622
+ object.wrapMode ?? "",
618
623
  object.preserveOnly ? "preserve-only" : "renderable",
619
624
  object.divergenceIds.join(","),
620
625
  ].join(":"),
@@ -740,7 +745,6 @@ function collectStoryAnchoredObjects(
740
745
  object: Omit<RuntimeStoryAnchoredObject, "objectId" | "divergenceIds"> & {
741
746
  objectId?: string;
742
747
  preserveHint?: PreserveOnlyObjectSizing;
743
- wrapMode?: string;
744
748
  },
745
749
  ): void => {
746
750
  const objectId =
@@ -782,7 +786,7 @@ function collectStoryAnchoredObjects(
782
786
  });
783
787
  }
784
788
 
785
- const { preserveHint: _preserveHint, wrapMode: _wrapMode, ...ledger } = object;
789
+ const { preserveHint: _preserveHint, ...ledger } = object;
786
790
  objects.push({
787
791
  ...ledger,
788
792
  objectId,
@@ -815,9 +819,10 @@ function collectStoryAnchoredObjects(
815
819
  switch (inline.type) {
816
820
  case "image": {
817
821
  pushObject({
818
- objectId: inline.mediaId,
822
+ objectId: `${context.storyKey}:image-${ordinal}`,
819
823
  sourceType: "image",
820
824
  display: inline.display === "floating" ? "floating" : "inline",
825
+ mediaIds: [inline.mediaId],
821
826
  preserveOnly: false,
822
827
  wrapMode: inline.floating?.wrap,
823
828
  });
@@ -836,6 +841,9 @@ function collectStoryAnchoredObjects(
836
841
  inline.anchor.extent.heightEmu,
837
842
  ),
838
843
  ...(relationshipIds.length > 0 ? { relationshipIds } : {}),
844
+ ...(inline.content.type === "picture" && inline.content.mediaId
845
+ ? { mediaIds: [inline.content.mediaId] }
846
+ : {}),
839
847
  preserveOnly: Boolean(preserveHint),
840
848
  ...(preserveHint ? { preserveHint } : {}),
841
849
  wrapMode: inline.anchor.wrapMode,
@@ -929,7 +937,7 @@ function getDrawingFrameObjectId(
929
937
  ordinal: number,
930
938
  ): string {
931
939
  if (inline.anchor.docPr?.id) return `${storyKey}:drawing-${inline.anchor.docPr.id}`;
932
- if (inline.content.type === "picture") return `${storyKey}:picture-${inline.content.blipRef}`;
940
+ if (inline.content.type === "picture") return `${storyKey}:picture-${ordinal}:${inline.content.blipRef}`;
933
941
  return `${storyKey}:drawing-${ordinal}`;
934
942
  }
935
943
 
@@ -1053,6 +1061,7 @@ function buildPageFrameSignature(
1053
1061
  });
1054
1062
  return [
1055
1063
  "page-frame",
1064
+ resolvePageFrameCompleteness(input.regions),
1056
1065
  input.pageIndex,
1057
1066
  input.sectionIndex,
1058
1067
  input.displayPageNumber,
@@ -1064,6 +1073,14 @@ function buildPageFrameSignature(
1064
1073
  ].join("|");
1065
1074
  }
1066
1075
 
1076
+ function resolvePageFrameCompleteness(
1077
+ regions: RuntimePageRegions,
1078
+ ): RuntimePageFrame["completeness"] {
1079
+ return collectRegions(regions).every((region) => region.rectTwips !== undefined)
1080
+ ? "complete"
1081
+ : "partial";
1082
+ }
1083
+
1067
1084
  function detectFrameDivergences(
1068
1085
  frameId: string,
1069
1086
  regions: RuntimePageRegions,
@@ -86,6 +86,7 @@ import {
86
86
  computeRepeatedHeaderHeight,
87
87
  extractRowFlags,
88
88
  findTableRowSplit,
89
+ measureTableRowHeightProfile,
89
90
  measureTableRowHeights,
90
91
  } from "./table-row-split.ts";
91
92
 
@@ -998,6 +999,9 @@ export function requiresFullRecompute(reason: LayoutInvalidationReason): boolean
998
999
 
999
1000
  const MIN_BLOCK_HEIGHT_TWIPS = 240;
1000
1001
  const FOOTNOTE_REFERENCE_RESERVATION_TWIPS = 180;
1002
+ const MIN_SPLIT_ROW_FRAGMENT_TWIPS = 720;
1003
+ const LONG_TABLE_SOFT_AUTO_ROW_DELTA_TWIPS = 60;
1004
+ const COMPACT_AUTHORED_ROW_HEIGHT_TWIPS = 1500;
1001
1005
 
1002
1006
  /**
1003
1007
  * Per-invocation measurement cache keyed by `(block, columnWidth)`.
@@ -1508,6 +1512,36 @@ interface SectionLocalTableSlice {
1508
1512
  columnIndex?: number;
1509
1513
  }
1510
1514
 
1515
+ interface TablePaginationProgress {
1516
+ startRow: number;
1517
+ carriedRowIndex?: number;
1518
+ carriedRowUsesCalibratedHeight?: boolean;
1519
+ }
1520
+
1521
+ function hasOnlyCompactAuthoredAutoRows(
1522
+ block: Extract<SurfaceBlockSnapshot, { kind: "table" }>,
1523
+ ): boolean {
1524
+ return (
1525
+ block.rows.length > 10 &&
1526
+ block.rows.every((row) => {
1527
+ const heightRule = row.heightRule ?? "auto";
1528
+ return heightRule !== "exact" && (row.height ?? 0) > 0 && (row.height ?? 0) <= 60;
1529
+ })
1530
+ );
1531
+ }
1532
+
1533
+ function shouldUseSoftLongTableAutoHeight(
1534
+ block: Extract<SurfaceBlockSnapshot, { kind: "table" }>,
1535
+ hasRepeatedHeaderRows: boolean,
1536
+ ): boolean {
1537
+ return (
1538
+ block.rows.length > 10 &&
1539
+ !hasRepeatedHeaderRows &&
1540
+ block.rows.every((row) => (row.height ?? 0) <= 1440) &&
1541
+ block.rows.every((row) => (row.heightRule ?? "auto") !== "exact")
1542
+ );
1543
+ }
1544
+
1511
1545
  interface SectionPaginationResult {
1512
1546
  pages: Omit<DocumentPageSnapshot, "pageIndex">[];
1513
1547
  splits: {
@@ -1670,10 +1704,11 @@ export function paginateSectionBlocksWithSplits(
1670
1704
  list.push(slice);
1671
1705
  tableSplitsByBlock.set(blockId, list);
1672
1706
  };
1673
- // P6.c: per-table progress when a table is being split row-by-row
1674
- // across pages. Map<blockId, nextRowIndexToPlace>. Cleared once a
1675
- // table is fully placed.
1676
- const tableProgress = new Map<string, number>();
1707
+ // P6.c/D-8: per-table progress when a table is being split row-by-row
1708
+ // across pages. `startRow` is the next row to place. `carriedRowIndex`
1709
+ // marks a row that already began on the prior page, so the next page must
1710
+ // not immediately re-split that same row and loop forever.
1711
+ const tableProgress = new Map<string, TablePaginationProgress>();
1677
1712
 
1678
1713
  // Contextual-spacing pair mask — precomputed once so the inner pagination
1679
1714
  // loop can fold suppression in O(1) per block without re-scanning neighbors.
@@ -1921,20 +1956,77 @@ export function paginateSectionBlocksWithSplits(
1921
1956
  // degrade to atomic placement (visual overflow, but offset
1922
1957
  // ranges stay clean — same as pre-P6.c behavior).
1923
1958
  if (block.kind === "table") {
1924
- const startRow = tableProgress.get(block.blockId) ?? 0;
1959
+ const progress = tableProgress.get(block.blockId);
1960
+ const startRow = progress?.startRow ?? 0;
1961
+ const carriedRowIndex = progress?.carriedRowIndex;
1925
1962
  const remainingForTable =
1926
1963
  usableHeight - columnHeight - reservedNoteHeight;
1927
- const rowHeights = measureTableRowHeights({
1964
+ const { cantSplitFlags, isHeaderFlags } = extractRowFlags(block);
1965
+ const hasRepeatedHeaderRows = isHeaderFlags.some(Boolean);
1966
+ const hasCompactAuthoredAutoRows = hasOnlyCompactAuthoredAutoRows(block);
1967
+ const canUseAutoHeightCalibration =
1968
+ !hasRepeatedHeaderRows && block.rows.length <= 10;
1969
+ const rowHeightProfile = measureTableRowHeightProfile({
1928
1970
  block,
1929
1971
  columnWidth,
1930
1972
  measurementProvider,
1931
1973
  defaultTabInterval,
1932
1974
  themeFonts,
1975
+ contentMode: canUseAutoHeightCalibration
1976
+ ? "auto-height-calibrated"
1977
+ : "legacy-min",
1933
1978
  });
1934
- const { cantSplitFlags, isHeaderFlags } = extractRowFlags(block);
1979
+ const shouldUseSoftLongTableHeights =
1980
+ shouldUseSoftLongTableAutoHeight(block, hasRepeatedHeaderRows);
1981
+ const resolvedRowHeights =
1982
+ hasCompactAuthoredAutoRows || shouldUseSoftLongTableHeights
1983
+ ? measureTableRowHeights({
1984
+ block,
1985
+ columnWidth,
1986
+ measurementProvider,
1987
+ defaultTabInterval,
1988
+ themeFonts,
1989
+ contentMode: "resolved",
1990
+ })
1991
+ : null;
1992
+ const legacyRowHeights = progress?.carriedRowUsesCalibratedHeight === true
1993
+ ? measureTableRowHeights({
1994
+ block,
1995
+ columnWidth,
1996
+ measurementProvider,
1997
+ defaultTabInterval,
1998
+ themeFonts,
1999
+ })
2000
+ : null;
2001
+ const rowHeights = [...rowHeightProfile.heights];
2002
+ if (resolvedRowHeights) {
2003
+ for (let rowIndex = 0; rowIndex < rowHeights.length; rowIndex += 1) {
2004
+ const resolvedHeight = resolvedRowHeights[rowIndex] ?? rowHeights[rowIndex] ?? 0;
2005
+ if (hasCompactAuthoredAutoRows) {
2006
+ rowHeights[rowIndex] = Math.min(
2007
+ Math.max(rowHeights[rowIndex] ?? 0, resolvedHeight),
2008
+ COMPACT_AUTHORED_ROW_HEIGHT_TWIPS,
2009
+ );
2010
+ } else if (resolvedHeight > (rowHeights[rowIndex] ?? 0)) {
2011
+ rowHeights[rowIndex] = (rowHeights[rowIndex] ?? 0) + LONG_TABLE_SOFT_AUTO_ROW_DELTA_TWIPS;
2012
+ }
2013
+ }
2014
+ }
2015
+ const calibratedAutoRowIndexes = new Set(rowHeightProfile.calibratedAutoRowIndexes);
2016
+ if (
2017
+ carriedRowIndex !== undefined &&
2018
+ progress?.carriedRowUsesCalibratedHeight === true &&
2019
+ carriedRowIndex === startRow &&
2020
+ calibratedAutoRowIndexes.has(carriedRowIndex)
2021
+ ) {
2022
+ rowHeights[carriedRowIndex] = Math.min(
2023
+ rowHeights[carriedRowIndex] ?? 0,
2024
+ legacyRowHeights?.[carriedRowIndex] ?? rowHeights[carriedRowIndex] ?? 0,
2025
+ );
2026
+ }
1935
2027
  const repeatedHeaderHeightTwips = computeRepeatedHeaderHeight(
1936
2028
  rowHeights,
1937
- isHeaderFlags,
2029
+ hasCompactAuthoredAutoRows ? rowHeights.map(() => false) : isHeaderFlags,
1938
2030
  );
1939
2031
  const headerReservation =
1940
2032
  startRow > 0 ? repeatedHeaderHeightTwips : 0;
@@ -1957,6 +2049,13 @@ export function paginateSectionBlocksWithSplits(
1957
2049
  }
1958
2050
  return height;
1959
2051
  };
2052
+ const isRowSplittableAcrossPages = (rowIndex: number): boolean => {
2053
+ const row = block.rows[rowIndex];
2054
+ if (!row) return false;
2055
+ if (cantSplitFlags[rowIndex] === true) return false;
2056
+ if (row.heightRule === "exact") return false;
2057
+ return true;
2058
+ };
1960
2059
 
1961
2060
  // Case 1: remainder fits — place and break.
1962
2061
  if (remainderHeight <= remainingForTable) {
@@ -1990,29 +2089,54 @@ export function paginateSectionBlocksWithSplits(
1990
2089
  });
1991
2090
  if (decision.rowsOnCurrentPage > 0) {
1992
2091
  const splitHeight = sliceHeight(startRow, decision.splitRowIndex);
2092
+ const splitRowIndex = decision.splitRowIndex;
2093
+ const splitRowVisibleHeight = remainingForTable - splitHeight;
2094
+ const canCarrySplitRow =
2095
+ decision.continuationRequired &&
2096
+ splitRowIndex < rowHeights.length &&
2097
+ splitRowIndex !== carriedRowIndex &&
2098
+ isRowSplittableAcrossPages(splitRowIndex) &&
2099
+ splitRowVisibleHeight >= MIN_SPLIT_ROW_FRAGMENT_TWIPS;
2100
+ const currentSliceEndRow = canCarrySplitRow
2101
+ ? Math.min(rowHeights.length, splitRowIndex + 1)
2102
+ : splitRowIndex;
2103
+ const currentSliceHeight = canCarrySplitRow
2104
+ ? splitHeight + splitRowVisibleHeight
2105
+ : splitHeight;
1993
2106
  recordColumnPlacement(block.blockId);
1994
2107
  appendTableSlice(block.blockId, {
1995
2108
  pageInSection,
1996
2109
  rowRange: {
1997
2110
  from: startRow,
1998
- to: decision.splitRowIndex,
2111
+ to: currentSliceEndRow,
1999
2112
  totalRows: rowHeights.length,
2000
2113
  },
2001
- heightTwips: splitHeight,
2114
+ heightTwips: currentSliceHeight,
2002
2115
  ...(isMultiColumn ? { columnIndex } : {}),
2003
2116
  });
2004
2117
  recordFragmentMeasurement(block.blockId, {
2005
- heightTwips: splitHeight,
2118
+ heightTwips: currentSliceHeight,
2006
2119
  widthTwips: columnWidth,
2007
2120
  });
2008
- columnHeight += splitHeight;
2009
- tableProgress.set(block.blockId, decision.splitRowIndex);
2121
+ columnHeight += currentSliceHeight;
2122
+ tableProgress.set(
2123
+ block.blockId,
2124
+ canCarrySplitRow
2125
+ ? {
2126
+ startRow: splitRowIndex,
2127
+ carriedRowIndex: splitRowIndex,
2128
+ ...(calibratedAutoRowIndexes.has(splitRowIndex)
2129
+ ? { carriedRowUsesCalibratedHeight: true }
2130
+ : {}),
2131
+ }
2132
+ : { startRow: splitRowIndex },
2133
+ );
2010
2134
  if (columnIndex < maxColumns - 1) {
2011
2135
  columnIndex += 1;
2012
2136
  columnHeight = 0;
2013
2137
  continue;
2014
2138
  }
2015
- pushPage(rowOffset(decision.splitRowIndex));
2139
+ pushPage(rowOffset(splitRowIndex));
2016
2140
  continue;
2017
2141
  }
2018
2142
 
@@ -2045,7 +2169,7 @@ export function paginateSectionBlocksWithSplits(
2045
2169
  if (index === blocks.length - 1) pushPage(section.end);
2046
2170
  break;
2047
2171
  }
2048
- tableProgress.set(block.blockId, forcedEndRow);
2172
+ tableProgress.set(block.blockId, { startRow: forcedEndRow });
2049
2173
  if (columnIndex < maxColumns - 1) {
2050
2174
  columnIndex += 1;
2051
2175
  columnHeight = 0;