@beyondwork/docx-react-component 1.0.18 → 1.0.20

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 (105) hide show
  1. package/README.md +8 -2
  2. package/package.json +24 -34
  3. package/src/api/README.md +5 -1
  4. package/src/api/public-types.ts +710 -4
  5. package/src/api/session-state.ts +60 -0
  6. package/src/core/commands/formatting-commands.ts +2 -1
  7. package/src/core/commands/image-commands.ts +147 -0
  8. package/src/core/commands/index.ts +19 -3
  9. package/src/core/commands/list-commands.ts +231 -36
  10. package/src/core/commands/paragraph-layout-commands.ts +339 -0
  11. package/src/core/commands/section-layout-commands.ts +680 -0
  12. package/src/core/commands/style-commands.ts +262 -0
  13. package/src/core/search/search-text.ts +357 -0
  14. package/src/core/selection/mapping.ts +41 -0
  15. package/src/core/state/editor-state.ts +4 -1
  16. package/src/index.ts +51 -0
  17. package/src/io/docx-session.ts +623 -56
  18. package/src/io/export/serialize-comments.ts +104 -34
  19. package/src/io/export/serialize-footnotes.ts +198 -1
  20. package/src/io/export/serialize-headers-footers.ts +203 -10
  21. package/src/io/export/serialize-main-document.ts +285 -8
  22. package/src/io/export/serialize-numbering.ts +28 -7
  23. package/src/io/export/split-review-boundaries.ts +181 -19
  24. package/src/io/normalize/normalize-text.ts +144 -32
  25. package/src/io/ooxml/highlight-colors.ts +39 -0
  26. package/src/io/ooxml/numbering-sentinels.ts +44 -0
  27. package/src/io/ooxml/parse-comments.ts +85 -19
  28. package/src/io/ooxml/parse-fields.ts +396 -0
  29. package/src/io/ooxml/parse-footnotes.ts +452 -22
  30. package/src/io/ooxml/parse-headers-footers.ts +657 -29
  31. package/src/io/ooxml/parse-inline-media.ts +30 -0
  32. package/src/io/ooxml/parse-main-document.ts +807 -20
  33. package/src/io/ooxml/parse-numbering.ts +7 -0
  34. package/src/io/ooxml/parse-revisions.ts +317 -38
  35. package/src/io/ooxml/parse-settings.ts +184 -0
  36. package/src/io/ooxml/parse-shapes.ts +25 -0
  37. package/src/io/ooxml/parse-styles.ts +463 -0
  38. package/src/io/ooxml/parse-theme.ts +32 -0
  39. package/src/legal/bookmarks.ts +44 -0
  40. package/src/legal/cross-references.ts +59 -1
  41. package/src/model/canonical-document.ts +250 -4
  42. package/src/model/cds-1.0.0.ts +13 -0
  43. package/src/model/snapshot.ts +87 -2
  44. package/src/review/store/revision-store.ts +6 -0
  45. package/src/review/store/revision-types.ts +1 -0
  46. package/src/runtime/document-layout.ts +332 -0
  47. package/src/runtime/document-navigation.ts +603 -0
  48. package/src/runtime/document-runtime.ts +1754 -78
  49. package/src/runtime/document-search.ts +145 -0
  50. package/src/runtime/numbering-prefix.ts +47 -26
  51. package/src/runtime/page-layout-estimation.ts +212 -0
  52. package/src/runtime/read-only-diagnostics-runtime.ts +9 -0
  53. package/src/runtime/session-capabilities.ts +35 -3
  54. package/src/runtime/story-context.ts +164 -0
  55. package/src/runtime/story-targeting.ts +162 -0
  56. package/src/runtime/surface-projection.ts +324 -36
  57. package/src/runtime/table-schema.ts +89 -7
  58. package/src/runtime/view-state.ts +477 -0
  59. package/src/runtime/workflow-markup.ts +349 -0
  60. package/src/ui/WordReviewEditor.tsx +2469 -1344
  61. package/src/ui/browser-export.ts +52 -0
  62. package/src/ui/editor-command-bag.ts +120 -0
  63. package/src/ui/editor-runtime-boundary.ts +1422 -0
  64. package/src/ui/editor-shell-view.tsx +134 -0
  65. package/src/ui/editor-surface-controller.tsx +51 -0
  66. package/src/ui/headless/preserve-editor-selection.ts +5 -0
  67. package/src/ui/headless/revision-decoration-model.ts +4 -4
  68. package/src/ui/headless/selection-helpers.ts +20 -0
  69. package/src/ui/headless/selection-toolbar-model.ts +22 -0
  70. package/src/ui/headless/use-editor-keyboard.ts +6 -1
  71. package/src/ui/runtime-snapshot-selectors.ts +197 -0
  72. package/src/ui-tailwind/chrome/tw-alert-banner.tsx +18 -2
  73. package/src/ui-tailwind/chrome/tw-image-context-toolbar.tsx +129 -0
  74. package/src/ui-tailwind/chrome/tw-layout-panel.tsx +114 -0
  75. package/src/ui-tailwind/chrome/tw-object-context-toolbar.tsx +34 -0
  76. package/src/ui-tailwind/chrome/tw-page-ruler.tsx +386 -0
  77. package/src/ui-tailwind/chrome/tw-selection-toolbar.tsx +150 -14
  78. package/src/ui-tailwind/chrome/tw-table-context-toolbar.tsx +128 -0
  79. package/src/ui-tailwind/editor-surface/perf-probe.ts +179 -0
  80. package/src/ui-tailwind/editor-surface/pm-command-bridge.ts +46 -7
  81. package/src/ui-tailwind/editor-surface/pm-contextual-ui.ts +31 -0
  82. package/src/ui-tailwind/editor-surface/pm-decorations.ts +35 -0
  83. package/src/ui-tailwind/editor-surface/pm-position-map.ts +3 -3
  84. package/src/ui-tailwind/editor-surface/pm-schema.ts +186 -13
  85. package/src/ui-tailwind/editor-surface/pm-state-from-snapshot.ts +191 -68
  86. package/src/ui-tailwind/editor-surface/search-plugin.ts +19 -68
  87. package/src/ui-tailwind/editor-surface/surface-build-keys.ts +51 -0
  88. package/src/ui-tailwind/editor-surface/tw-inline-token.tsx +11 -0
  89. package/src/ui-tailwind/editor-surface/tw-opaque-block.tsx +7 -1
  90. package/src/ui-tailwind/editor-surface/tw-prosemirror-surface.tsx +528 -85
  91. package/src/ui-tailwind/editor-surface/tw-table-node-view.tsx +0 -1
  92. package/src/ui-tailwind/index.ts +2 -1
  93. package/src/ui-tailwind/page-chrome-model.ts +27 -0
  94. package/src/ui-tailwind/review/tw-comment-sidebar.tsx +277 -147
  95. package/src/ui-tailwind/review/tw-health-panel.tsx +31 -2
  96. package/src/ui-tailwind/review/tw-review-rail.tsx +8 -8
  97. package/src/ui-tailwind/review/tw-revision-sidebar.tsx +15 -15
  98. package/src/ui-tailwind/theme/editor-theme.css +127 -0
  99. package/src/ui-tailwind/toolbar/tw-toolbar-icon-button.tsx +4 -0
  100. package/src/ui-tailwind/toolbar/tw-toolbar.tsx +829 -12
  101. package/src/ui-tailwind/tw-review-workspace.tsx +1238 -42
  102. package/src/validation/compatibility-engine.ts +119 -24
  103. package/src/validation/compatibility-report.ts +1 -0
  104. package/src/validation/diagnostics.ts +1 -0
  105. package/src/validation/docx-comment-proof.ts +707 -0
@@ -104,23 +104,13 @@ function collectContentFeatures(
104
104
  lists: false,
105
105
  hyperlinks: false,
106
106
  images: false,
107
+ tables: false,
108
+ sections: false,
109
+ contentControls: false,
107
110
  };
108
111
 
109
112
  for (let index = 0; index < content.children.length; index += 1) {
110
- const block = content.children[index];
111
- if (block.type !== "paragraph") {
112
- continue;
113
- }
114
-
115
- flags.paragraphs = true;
116
- if (block.styleId?.toLowerCase().startsWith("heading")) {
117
- flags.headings = true;
118
- }
119
- if (block.numbering) {
120
- flags.lists = true;
121
- }
122
-
123
- measureParagraph(block, flags);
113
+ measureBlock(content.children[index], flags);
124
114
  }
125
115
 
126
116
  const entries: CompatibilityFeatureEntry[] = [];
@@ -145,16 +135,94 @@ function collectContentFeatures(
145
135
  if (flags.images) {
146
136
  entries.push(supportedEntry("inline-images", "Inline image placements stay attached to preserved media parts."));
147
137
  }
138
+ if (flags.tables) {
139
+ entries.push(
140
+ supportedEntry(
141
+ "tables",
142
+ "Structured tables keep cell topology and common visual properties through runtime editing and export.",
143
+ ),
144
+ );
145
+ }
146
+ if (flags.sections) {
147
+ entries.push(
148
+ supportedEntry(
149
+ "sections",
150
+ "Section boundaries and page-layout properties remain structured and export-safe.",
151
+ ),
152
+ );
153
+ }
154
+ if (flags.contentControls) {
155
+ entries.push(
156
+ supportedEntry(
157
+ "content-controls",
158
+ "Common content controls render through structured SDT state and round-trip without flattening to opaque placeholders.",
159
+ ),
160
+ );
161
+ }
148
162
  return entries;
149
163
  }
150
164
 
165
+ function measureBlock(
166
+ block: BlockNode,
167
+ flags: {
168
+ paragraphs: boolean;
169
+ runs: boolean;
170
+ whitespace: boolean;
171
+ headings: boolean;
172
+ lists: boolean;
173
+ hyperlinks: boolean;
174
+ images: boolean;
175
+ tables: boolean;
176
+ sections: boolean;
177
+ contentControls: boolean;
178
+ },
179
+ ): number {
180
+ switch (block.type) {
181
+ case "paragraph":
182
+ flags.paragraphs = true;
183
+ if (block.styleId?.toLowerCase().startsWith("heading")) {
184
+ flags.headings = true;
185
+ }
186
+ if (block.numbering) {
187
+ flags.lists = true;
188
+ }
189
+ return measureParagraph(block, flags);
190
+ case "table":
191
+ flags.tables = true;
192
+ return block.rows.reduce(
193
+ (size, row) =>
194
+ size +
195
+ row.cells.reduce(
196
+ (cellSize, cell) =>
197
+ cellSize + cell.children.reduce((childSize, child) => childSize + measureBlock(child, flags), 0),
198
+ 0,
199
+ ),
200
+ 0,
201
+ );
202
+ case "sdt":
203
+ flags.contentControls = true;
204
+ return block.children.reduce((size, child) => size + measureBlock(child, flags), 0);
205
+ case "custom_xml":
206
+ return block.children.reduce((size, child) => size + measureBlock(child, flags), 0);
207
+ case "section_break":
208
+ flags.sections = true;
209
+ return 1;
210
+ default:
211
+ return 0;
212
+ }
213
+ }
214
+
151
215
  function measureParagraph(
152
216
  paragraph: ParagraphNode,
153
217
  flags: {
218
+ paragraphs: boolean;
154
219
  runs: boolean;
155
220
  whitespace: boolean;
156
221
  hyperlinks: boolean;
157
222
  images: boolean;
223
+ tables: boolean;
224
+ sections: boolean;
225
+ contentControls: boolean;
158
226
  },
159
227
  ): number {
160
228
  let size = 0;
@@ -170,10 +238,14 @@ function measureParagraph(
170
238
  function measureInlineNode(
171
239
  node: InlineNode,
172
240
  flags: {
241
+ paragraphs: boolean;
173
242
  runs: boolean;
174
243
  whitespace: boolean;
175
244
  hyperlinks: boolean;
176
245
  images: boolean;
246
+ tables: boolean;
247
+ sections: boolean;
248
+ contentControls: boolean;
177
249
  },
178
250
  ): number {
179
251
  switch (node.type) {
@@ -310,12 +382,12 @@ function collectSubPartFeatures(
310
382
  Object.keys(subParts.footnoteCollection?.footnotes ?? {}).length +
311
383
  Object.keys(subParts.footnoteCollection?.endnotes ?? {}).length;
312
384
 
313
- if (hasHeaderFooterContent) {
385
+ if (hasHeaderFooterContent && !entries.some((entry) => entry.featureKey === "headers-footers-lossy")) {
314
386
  entries.push({
315
387
  featureEntryId: "feature:subparts:headers-footers",
316
388
  featureKey: "headers-footers",
317
- featureClass: "preserve-only",
318
- message: "Headers and footers are preserved through structured sub-part ownership.",
389
+ featureClass: "supported-roundtrip",
390
+ message: "Headers and footers resolve as structured secondary stories with export-safe ownership.",
319
391
  details: {
320
392
  headerCount: subParts.headers?.length ?? 0,
321
393
  footerCount: subParts.footers?.length ?? 0,
@@ -323,12 +395,12 @@ function collectSubPartFeatures(
323
395
  });
324
396
  }
325
397
 
326
- if (noteCount > 0) {
398
+ if (noteCount > 0 && !entries.some((entry) => entry.featureKey === "notes-lossy")) {
327
399
  entries.push({
328
400
  featureEntryId: "feature:subparts:notes",
329
401
  featureKey: "notes",
330
- featureClass: "preserve-only",
331
- message: "Footnotes and endnotes are preserved through structured sub-part ownership.",
402
+ featureClass: "supported-roundtrip",
403
+ message: "Footnotes and endnotes resolve as structured secondary stories with export-safe ownership.",
332
404
  details: {
333
405
  footnoteCount: Object.keys(subParts.footnoteCollection?.footnotes ?? {}).length,
334
406
  endnoteCount: Object.keys(subParts.footnoteCollection?.endnotes ?? {}).length,
@@ -407,6 +479,15 @@ function collectLossyBlocks(
407
479
  const issues: string[] = [];
408
480
 
409
481
  for (const block of blocks) {
482
+ if (block.type === "table") {
483
+ for (const row of block.rows) {
484
+ for (const cell of row.cells) {
485
+ issues.push(...collectLossyBlocks(cell.children, `${surface}:table-cell`));
486
+ }
487
+ }
488
+ continue;
489
+ }
490
+
410
491
  if (block.type !== "paragraph") {
411
492
  issues.push(`${surface}:${block.type}`);
412
493
  continue;
@@ -414,9 +495,6 @@ function collectLossyBlocks(
414
495
 
415
496
  if (
416
497
  block.numbering !== undefined ||
417
- block.spacing !== undefined ||
418
- block.indentation !== undefined ||
419
- block.tabStops !== undefined ||
420
498
  block.keepNext !== undefined ||
421
499
  block.keepLines !== undefined ||
422
500
  block.outlineLevel !== undefined ||
@@ -445,6 +523,8 @@ function collectLossyInlineContent(
445
523
  ): string[] {
446
524
  switch (node.type) {
447
525
  case "text": {
526
+ const allowSecondaryStoryColorMarks =
527
+ surface.startsWith("header:") || surface.startsWith("footer:");
448
528
  const unsupportedMarks = (node.marks ?? [])
449
529
  .filter(
450
530
  (mark) =>
@@ -452,7 +532,14 @@ function collectLossyInlineContent(
452
532
  mark.type !== "italic" &&
453
533
  mark.type !== "underline" &&
454
534
  mark.type !== "strikethrough" &&
455
- mark.type !== "doubleStrikethrough",
535
+ mark.type !== "doubleStrikethrough" &&
536
+ mark.type !== "fontFamily" &&
537
+ mark.type !== "fontSize" &&
538
+ mark.type !== "textColor" &&
539
+ (!allowSecondaryStoryColorMarks || mark.type !== "backgroundColor") &&
540
+ (!allowSecondaryStoryColorMarks || mark.type !== "highlight") &&
541
+ mark.type !== "smallCaps" &&
542
+ mark.type !== "allCaps",
456
543
  )
457
544
  .map((mark) => `${surface}:mark:${mark.type}`);
458
545
  return unsupportedMarks;
@@ -460,6 +547,14 @@ function collectLossyInlineContent(
460
547
  case "tab":
461
548
  case "hard_break":
462
549
  case "footnote_ref":
550
+ case "field":
551
+ case "bookmark_start":
552
+ case "bookmark_end":
553
+ case "shape":
554
+ case "wordart":
555
+ case "vml_shape":
556
+ case "chart_preview":
557
+ case "smartart_preview":
463
558
  return [];
464
559
  default:
465
560
  return [`${surface}:${node.type}`];
@@ -44,6 +44,7 @@ export const COMPATIBILITY_FEATURE_KEYS = [
44
44
  "unknown-package-parts",
45
45
  "package-integrity",
46
46
  "relationship-integrity",
47
+ "workflow-scopes",
47
48
  ] as const;
48
49
 
49
50
  export type CompatibilityFeatureKey =
@@ -48,6 +48,7 @@ export const EDITOR_ERROR_SOURCES = [
48
48
  "runtime",
49
49
  "validation",
50
50
  "datastore",
51
+ "host",
51
52
  "export",
52
53
  ] as const;
53
54