@beyondwork/docx-react-component 1.0.85 → 1.0.87

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 (53) hide show
  1. package/package.json +1 -1
  2. package/src/api/public-types.ts +49 -0
  3. package/src/api/v3/ui/chrome-composition.ts +2 -11
  4. package/src/api/v3/ui/chrome.ts +6 -8
  5. package/src/index.ts +5 -0
  6. package/src/io/export/serialize-main-document.ts +215 -6
  7. package/src/io/ooxml/parse-drawing.ts +15 -1
  8. package/src/io/ooxml/parse-fields.ts +410 -12
  9. package/src/model/canonical-document.ts +177 -2
  10. package/src/model/layout/page-layout-snapshot.ts +2 -0
  11. package/src/model/layout/runtime-page-graph-types.ts +6 -0
  12. package/src/preservation/store.ts +4 -5
  13. package/src/runtime/document-outline.ts +80 -0
  14. package/src/runtime/document-runtime.ts +338 -13
  15. package/src/runtime/formatting/field/page-number-format.ts +49 -0
  16. package/src/runtime/formatting/field/resolver.ts +61 -40
  17. package/src/runtime/layout/layout-engine-instance.ts +18 -1
  18. package/src/runtime/layout/layout-engine-version.ts +19 -1
  19. package/src/runtime/layout/measurement-backend-canvas.ts +21 -9
  20. package/src/runtime/layout/measurement-backend-empirical.ts +18 -4
  21. package/src/runtime/layout/page-graph.ts +13 -2
  22. package/src/runtime/layout/paginated-layout-engine.ts +440 -117
  23. package/src/runtime/layout/project-block-fragments.ts +87 -4
  24. package/src/runtime/layout/resolve-page-fields.ts +8 -5
  25. package/src/runtime/layout/table-row-split.ts +97 -23
  26. package/src/runtime/surface-projection.ts +227 -27
  27. package/src/shell/session-bootstrap.ts +6 -1
  28. package/src/ui/WordReviewEditor.tsx +112 -33
  29. package/src/ui/editor-command-bag.ts +4 -0
  30. package/src/ui/editor-shell-view.tsx +1 -0
  31. package/src/ui/editor-surface-controller.tsx +1 -0
  32. package/src/ui/headless/revision-decoration-model.ts +11 -13
  33. package/src/ui-tailwind/chrome/editor-action-registry.ts +7 -26
  34. package/src/ui-tailwind/chrome/responsive-chrome.ts +2 -2
  35. package/src/ui-tailwind/chrome/tw-alert-banner.tsx +27 -0
  36. package/src/ui-tailwind/chrome/tw-detach-handle.tsx +6 -2
  37. package/src/ui-tailwind/editor-surface/fast-text-edit-lane.ts +57 -6
  38. package/src/ui-tailwind/editor-surface/page-slice-util.ts +17 -0
  39. package/src/ui-tailwind/editor-surface/perf-probe.ts +5 -0
  40. package/src/ui-tailwind/editor-surface/pm-contextual-ui.ts +34 -0
  41. package/src/ui-tailwind/editor-surface/pm-decorations.ts +146 -20
  42. package/src/ui-tailwind/editor-surface/pm-position-map.ts +8 -2
  43. package/src/ui-tailwind/editor-surface/tw-prosemirror-surface.tsx +41 -44
  44. package/src/ui-tailwind/page-stack/use-visible-block-range.ts +1 -1
  45. package/src/ui-tailwind/review/tw-comment-sidebar.tsx +8 -3
  46. package/src/ui-tailwind/review/tw-review-rail.tsx +2 -0
  47. package/src/ui-tailwind/review/tw-revision-sidebar.tsx +75 -31
  48. package/src/ui-tailwind/review/tw-workflow-tab.tsx +159 -8
  49. package/src/ui-tailwind/review-workspace/types.ts +4 -0
  50. package/src/ui-tailwind/review-workspace/use-review-rail-state.ts +1 -1
  51. package/src/ui-tailwind/toolbar/tw-role-action-region.tsx +8 -10
  52. package/src/ui-tailwind/toolbar/tw-toolbar.tsx +21 -17
  53. package/src/ui-tailwind/tw-review-workspace.tsx +27 -2
@@ -1000,8 +1000,26 @@
1000
1000
  * layout changes. The facet contract widened by one public method —
1001
1001
  * persisted cache envelopes from v58 must re-derive their page-field
1002
1002
  * projections.
1003
+ *
1004
+ * 60 — Refactor/04 measured layout geometry and page-field drift closure.
1005
+ * Page-stack pagination now
1006
+ * carries measured fragment heights through `fragmentMeasurementsByPageIndex`;
1007
+ * paragraph/table slices carry their measured slice height and column;
1008
+ * `projectLineBoxesForPageFragments` emits body line boxes from those
1009
+ * measurements; and `buildPageGraph` can consume page-index keyed line
1010
+ * boxes for partial incremental rebuilds. Table row-height measurement
1011
+ * gained an opt-in content-aware mode while production pagination keeps
1012
+ * the legacy row-boundary calibration until row-follow fragments exist.
1013
+ * `PageLayoutSnapshot.pageNumbering` also now carries a runtime-derived
1014
+ * `chapterNumber` resolved from active chapter headings during
1015
+ * `buildPageStackWithSplits` and incremental tail pagination. PAGE /
1016
+ * PAGEREF field display consumes that graph truth through
1017
+ * `resolvePageFieldDisplayText` / `formatPageNumberWithChapter` instead
1018
+ * of reconstructing chapter-prefixed numbering in caller-local formulas.
1019
+ * Cache envelopes from v59 invalidate because page graph line-box,
1020
+ * fragment geometry, and page-numbering payloads changed.
1003
1021
  */
1004
- export const LAYOUT_ENGINE_VERSION = 59 as const;
1022
+ export const LAYOUT_ENGINE_VERSION = 60 as const;
1005
1023
 
1006
1024
  /**
1007
1025
  * Serialization schema version for the LayCache payload (the cache envelope
@@ -216,17 +216,22 @@ export function createCanvasBackend(
216
216
  break;
217
217
  case "image": {
218
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.
219
+ // the provider contract is live inside the canvas backend.
220
+ // DrawingFrame images carry anchor extent in EMUs; convert to
221
+ // twips so large inline/floating objects reserve their height.
222
+ const imageHeightTwips = emuToTwips(segment.anchor?.extent.heightEmu);
224
223
  const measured = measureInlineObject({
225
- widthTwips: 0,
226
- heightTwips: 0,
224
+ widthTwips: emuToTwips(segment.anchor?.extent.widthEmu),
225
+ heightTwips: imageHeightTwips,
227
226
  display: segment.display === "floating" ? "floating" : "inline",
228
227
  });
229
- lineCount += Math.max(1, measured.displacedLineCount);
228
+ lineCount += Math.max(
229
+ 1,
230
+ measured.displacedLineCount,
231
+ imageHeightTwips > 0
232
+ ? Math.ceil(imageHeightTwips / Math.max(1, formatting.lineHeight))
233
+ : 0,
234
+ );
230
235
  currentLineWidth = 0;
231
236
  currentCapacity = subsequentLineWidth;
232
237
  break;
@@ -260,10 +265,17 @@ export function createCanvasBackend(
260
265
  return {
261
266
  widthTwips: input.widthTwips,
262
267
  heightTwips: input.heightTwips,
263
- displacedLineCount: input.display === "floating" ? 2 : 1,
268
+ displacedLineCount: Math.max(
269
+ input.display === "floating" ? 2 : 1,
270
+ input.heightTwips > 0 ? Math.ceil(input.heightTwips / 240) : 0,
271
+ ),
264
272
  };
265
273
  }
266
274
 
275
+ function emuToTwips(emu: number | undefined): number {
276
+ return emu && emu > 0 ? Math.round(emu / 635) : 0;
277
+ }
278
+
267
279
  function measureTableBlock(
268
280
  input: MeasureTableBlockInput,
269
281
  ): MeasuredTableBlock {
@@ -107,12 +107,19 @@ function measureLineFragments(
107
107
  currentLineCapacity = subsequentLineCapacity;
108
108
  break;
109
109
  case "image": {
110
+ const imageHeightTwips = emuToTwips(segment.anchor?.extent.heightEmu);
110
111
  const measured = inlineObjectMeasurer({
111
- widthTwips: 0,
112
- heightTwips: 0,
112
+ widthTwips: emuToTwips(segment.anchor?.extent.widthEmu),
113
+ heightTwips: imageHeightTwips,
113
114
  display: segment.display === "floating" ? "floating" : "inline",
114
115
  });
115
- lineCount += Math.max(1, measured.displacedLineCount);
116
+ lineCount += Math.max(
117
+ 1,
118
+ measured.displacedLineCount,
119
+ imageHeightTwips > 0
120
+ ? Math.ceil(imageHeightTwips / Math.max(1, formatting.lineHeight))
121
+ : 0,
122
+ );
116
123
  currentLineChars = 0;
117
124
  currentLineCapacity = subsequentLineCapacity;
118
125
  break;
@@ -151,13 +158,20 @@ function measureLineFragments(
151
158
  };
152
159
  }
153
160
 
161
+ function emuToTwips(emu: number | undefined): number {
162
+ return emu && emu > 0 ? Math.round(emu / 635) : 0;
163
+ }
164
+
154
165
  function measureInlineObject(
155
166
  input: MeasureInlineObjectInput,
156
167
  ): MeasuredInlineObject {
157
168
  return {
158
169
  widthTwips: input.widthTwips,
159
170
  heightTwips: input.heightTwips,
160
- displacedLineCount: input.display === "floating" ? 2 : 1,
171
+ displacedLineCount: Math.max(
172
+ input.display === "floating" ? 2 : 1,
173
+ input.heightTwips > 0 ? Math.ceil(input.heightTwips / MIN_BLOCK_HEIGHT_TWIPS) : 0,
174
+ ),
161
175
  };
162
176
  }
163
177
 
@@ -103,10 +103,16 @@ export function buildPageGraph(
103
103
 
104
104
  const pages: RuntimePageNode[] = [];
105
105
  const aggregatedFragments: RuntimeBlockFragment[] = [...(input.fragments ?? [])];
106
+ const pageIdByGlobalPageIndex = new Map<number, string>();
107
+ for (let index = 0; index < input.pages.length; index += 1) {
108
+ const page = input.pages[index]!;
109
+ pageIdByGlobalPageIndex.set(page.pageIndex, `page-${graphRevision}-${index}`);
110
+ }
106
111
  // Rehydrate fragmentsByPageIndex with the fresh graphRevision's pageIds.
107
112
  if (input.fragmentsByPageIndex) {
108
113
  for (const [pageIndex, fragments] of input.fragmentsByPageIndex) {
109
- const pageId = `page-${graphRevision}-${pageIndex}`;
114
+ const pageId =
115
+ pageIdByGlobalPageIndex.get(pageIndex) ?? `page-${graphRevision}-${pageIndex}`;
110
116
  for (const fragment of fragments) {
111
117
  aggregatedFragments.push({ ...fragment, pageId });
112
118
  }
@@ -153,6 +159,7 @@ export function buildPageGraph(
153
159
  // pageId) falling back to noteAllocations (pageId-keyed, used by the
154
160
  // engine once it emits allocations).
155
161
  const pageNoteAllocations: RuntimeNoteAllocation[] =
162
+ input.noteAllocationsByPageIndex?.get(page.pageIndex) ??
156
163
  input.noteAllocationsByPageIndex?.get(index) ??
157
164
  input.noteAllocations?.get(pageId) ??
158
165
  [];
@@ -167,7 +174,11 @@ export function buildPageGraph(
167
174
  layout: page.layout,
168
175
  stories,
169
176
  regions: buildRegions(page.layout, bodyPageFragments, stories, pageNoteAllocations),
170
- lineBoxes: input.lineBoxes?.get(pageId) ?? [],
177
+ lineBoxes:
178
+ input.lineBoxesByPageIndex?.get(page.pageIndex) ??
179
+ input.lineBoxesByPageIndex?.get(index) ??
180
+ input.lineBoxes?.get(pageId) ??
181
+ [],
171
182
  noteAllocations: pageNoteAllocations,
172
183
  isBlankFiller: page.pageInSection === -1,
173
184
  };