@beyondwork/docx-react-component 1.0.36 → 1.0.38

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 (107) hide show
  1. package/README.md +103 -13
  2. package/package.json +1 -1
  3. package/src/api/package-version.ts +13 -0
  4. package/src/api/public-types.ts +402 -1
  5. package/src/core/commands/index.ts +18 -1
  6. package/src/core/commands/section-layout-commands.ts +58 -0
  7. package/src/core/commands/table-grid.ts +431 -0
  8. package/src/core/commands/table-structure-commands.ts +815 -55
  9. package/src/core/selection/mapping.ts +6 -0
  10. package/src/io/docx-session.ts +24 -9
  11. package/src/io/export/build-app-properties-xml.ts +88 -0
  12. package/src/io/export/serialize-comments.ts +6 -1
  13. package/src/io/export/serialize-footnotes.ts +10 -9
  14. package/src/io/export/serialize-headers-footers.ts +11 -10
  15. package/src/io/export/serialize-main-document.ts +328 -50
  16. package/src/io/export/serialize-numbering.ts +114 -24
  17. package/src/io/export/serialize-tables.ts +87 -11
  18. package/src/io/export/table-properties-xml.ts +174 -20
  19. package/src/io/export/twip.ts +66 -0
  20. package/src/io/normalize/normalize-text.ts +20 -0
  21. package/src/io/ooxml/parse-footnotes.ts +62 -1
  22. package/src/io/ooxml/parse-headers-footers.ts +62 -1
  23. package/src/io/ooxml/parse-main-document.ts +158 -1
  24. package/src/io/ooxml/parse-tables.ts +249 -0
  25. package/src/legal/bookmarks.ts +78 -0
  26. package/src/model/canonical-document.ts +45 -0
  27. package/src/review/store/scope-tag-diff.ts +130 -0
  28. package/src/runtime/document-layout.ts +4 -2
  29. package/src/runtime/document-navigation.ts +2 -306
  30. package/src/runtime/document-runtime.ts +287 -11
  31. package/src/runtime/layout/default-page-format.ts +96 -0
  32. package/src/runtime/layout/docx-font-loader.ts +143 -0
  33. package/src/runtime/layout/index.ts +233 -0
  34. package/src/runtime/layout/inert-layout-facet.ts +59 -0
  35. package/src/runtime/layout/layout-engine-instance.ts +628 -0
  36. package/src/runtime/layout/layout-invalidation.ts +257 -0
  37. package/src/runtime/layout/layout-measurement-provider.ts +175 -0
  38. package/src/runtime/layout/margin-preset-catalog.ts +178 -0
  39. package/src/runtime/layout/measurement-backend-canvas.ts +307 -0
  40. package/src/runtime/layout/measurement-backend-empirical.ts +208 -0
  41. package/src/runtime/layout/page-format-catalog.ts +233 -0
  42. package/src/runtime/layout/page-fragment-mapper.ts +179 -0
  43. package/src/runtime/layout/page-graph.ts +452 -0
  44. package/src/runtime/layout/page-layout-snapshot-adapter.ts +70 -0
  45. package/src/runtime/layout/page-story-resolver.ts +195 -0
  46. package/src/runtime/layout/paginated-layout-engine.ts +921 -0
  47. package/src/runtime/layout/project-block-fragments.ts +91 -0
  48. package/src/runtime/layout/public-facet.ts +1398 -0
  49. package/src/runtime/layout/resolved-formatting-document.ts +317 -0
  50. package/src/runtime/layout/resolved-formatting-state.ts +430 -0
  51. package/src/runtime/layout/table-render-plan.ts +229 -0
  52. package/src/runtime/render/block-fragment-projection.ts +35 -0
  53. package/src/runtime/render/decoration-resolver.ts +189 -0
  54. package/src/runtime/render/index.ts +57 -0
  55. package/src/runtime/render/pending-op-delta-reader.ts +129 -0
  56. package/src/runtime/render/render-frame-types.ts +317 -0
  57. package/src/runtime/render/render-kernel.ts +755 -0
  58. package/src/runtime/scope-tag-registry.ts +95 -0
  59. package/src/runtime/surface-projection.ts +1 -0
  60. package/src/runtime/text-ack-range.ts +49 -0
  61. package/src/runtime/view-state.ts +67 -0
  62. package/src/runtime/workflow-markup.ts +1 -5
  63. package/src/runtime/workflow-rail-segments.ts +280 -0
  64. package/src/ui/WordReviewEditor.tsx +99 -15
  65. package/src/ui/editor-runtime-boundary.ts +10 -1
  66. package/src/ui/editor-shell-view.tsx +6 -0
  67. package/src/ui/editor-surface-controller.tsx +3 -0
  68. package/src/ui/headless/chrome-registry.ts +501 -0
  69. package/src/ui/headless/scoped-chrome-policy.ts +183 -0
  70. package/src/ui/headless/selection-tool-context.ts +2 -0
  71. package/src/ui/headless/selection-tool-resolver.ts +36 -17
  72. package/src/ui/headless/selection-tool-types.ts +10 -0
  73. package/src/ui-tailwind/chrome/chrome-preset-model.ts +23 -2
  74. package/src/ui-tailwind/chrome/role-action-sets.ts +74 -0
  75. package/src/ui-tailwind/chrome/tw-detach-handle.tsx +147 -0
  76. package/src/ui-tailwind/chrome/tw-selection-anchor-resolver.ts +163 -0
  77. package/src/ui-tailwind/chrome/tw-selection-tool-host.tsx +57 -92
  78. package/src/ui-tailwind/chrome/tw-selection-tool-placement.ts +149 -0
  79. package/src/ui-tailwind/chrome/tw-selection-toolbar.tsx +15 -4
  80. package/src/ui-tailwind/chrome/tw-table-context-toolbar.tsx +274 -138
  81. package/src/ui-tailwind/chrome-overlay/chrome-overlay-projector.ts +90 -0
  82. package/src/ui-tailwind/chrome-overlay/index.ts +22 -0
  83. package/src/ui-tailwind/chrome-overlay/tw-chrome-overlay.tsx +86 -0
  84. package/src/ui-tailwind/chrome-overlay/tw-scope-rail-layer.tsx +178 -0
  85. package/src/ui-tailwind/chrome-overlay/tw-workspace-view-switcher.tsx +95 -0
  86. package/src/ui-tailwind/editor-surface/fast-text-edit-lane.ts +337 -0
  87. package/src/ui-tailwind/editor-surface/local-edit-session-state.ts +100 -0
  88. package/src/ui-tailwind/editor-surface/perf-probe.ts +27 -1
  89. package/src/ui-tailwind/editor-surface/pm-command-bridge.ts +20 -2
  90. package/src/ui-tailwind/editor-surface/pm-decorations.ts +93 -23
  91. package/src/ui-tailwind/editor-surface/predicted-position-map.ts +78 -0
  92. package/src/ui-tailwind/editor-surface/predicted-tag-preflight.ts +63 -0
  93. package/src/ui-tailwind/editor-surface/predicted-tx-gate.ts +39 -0
  94. package/src/ui-tailwind/editor-surface/tw-prosemirror-surface.tsx +176 -6
  95. package/src/ui-tailwind/index.ts +33 -0
  96. package/src/ui-tailwind/review/tw-comment-sidebar.tsx +2 -2
  97. package/src/ui-tailwind/review/tw-rail-card.tsx +150 -0
  98. package/src/ui-tailwind/review/tw-review-rail-footer.tsx +52 -0
  99. package/src/ui-tailwind/review/tw-review-rail.tsx +166 -11
  100. package/src/ui-tailwind/review/tw-workflow-tab.tsx +108 -0
  101. package/src/ui-tailwind/theme/editor-theme.css +505 -144
  102. package/src/ui-tailwind/toolbar/tw-role-action-region.tsx +559 -0
  103. package/src/ui-tailwind/toolbar/tw-scope-posture-menu.tsx +182 -0
  104. package/src/ui-tailwind/toolbar/tw-shell-header.tsx +162 -0
  105. package/src/ui-tailwind/toolbar/tw-toolbar-icon-button.tsx +2 -2
  106. package/src/ui-tailwind/toolbar/tw-toolbar.tsx +304 -166
  107. package/src/ui-tailwind/tw-review-workspace.tsx +163 -2
@@ -16,16 +16,31 @@ import { resolveHighlightColor } from "./highlight-colors.ts";
16
16
  import { classifyFieldInstruction } from "./parse-fields.ts";
17
17
  import {
18
18
  readCellBorders,
19
+ readCellCnfStyle,
20
+ readCellFitText,
21
+ readCellMargins,
22
+ readCellNoWrap,
19
23
  readCellShading,
24
+ readCellTextDirection,
20
25
  readCellVerticalAlign,
21
26
  readCellWidth,
22
27
  readGridColumns as readSharedGridColumns,
28
+ readRowCantSplit,
29
+ readRowCnfStyle,
23
30
  readRowHeight,
24
31
  readRowHeightRule,
32
+ readRowHorizontalAlignment,
25
33
  readRowIsHeader,
26
34
  readTableAlignment,
35
+ readTableBidiVisual,
27
36
  readTableBorders,
37
+ readTableCaption,
28
38
  readTableCellMargins,
39
+ readTableCellSpacing,
40
+ readTableDescription,
41
+ readTableFloating,
42
+ readTableIndent,
43
+ readTableLayoutMode,
29
44
  readTableLook,
30
45
  readTableStyleId,
31
46
  readTableWidth,
@@ -689,7 +704,8 @@ function parseRunProperties(rElement: XmlElementNode): TextMark[] {
689
704
  }
690
705
  case "color": {
691
706
  const colorVal = child.attributes["w:val"] ?? child.attributes.val;
692
- if (colorVal && colorVal !== "auto") {
707
+ // A.9: preserve "auto" verbatim for round-trip.
708
+ if (colorVal) {
693
709
  marks.push({ type: "textColor", color: colorVal });
694
710
  }
695
711
  break;
@@ -926,6 +942,13 @@ function parseSimpleTableElement(tblElement: XmlElementNode): TableNode {
926
942
  let borders: TableNode["borders"];
927
943
  let cellMargins: TableNode["cellMargins"];
928
944
  let tblLook: TableNode["tblLook"];
945
+ let indent: TableNode["indent"];
946
+ let layoutMode: TableNode["layoutMode"];
947
+ let cellSpacing: TableNode["cellSpacing"];
948
+ let caption: TableNode["caption"];
949
+ let description: TableNode["description"];
950
+ let bidiVisual: TableNode["bidiVisual"];
951
+ let floating: TableNode["floating"];
929
952
 
930
953
  for (const child of tblElement.children) {
931
954
  if (child.type !== "element") continue;
@@ -939,6 +962,13 @@ function parseSimpleTableElement(tblElement: XmlElementNode): TableNode {
939
962
  borders = readTableBorders(child);
940
963
  cellMargins = readTableCellMargins(child);
941
964
  tblLook = readTableLook(child);
965
+ indent = readTableIndent(child);
966
+ layoutMode = readTableLayoutMode(child);
967
+ cellSpacing = readTableCellSpacing(child);
968
+ caption = readTableCaption(child);
969
+ description = readTableDescription(child);
970
+ bidiVisual = readTableBidiVisual(child);
971
+ floating = readTableFloating(child);
942
972
  } else if (name === "tblGrid") {
943
973
  gridColumns = readGridColumns(child);
944
974
  } else if (name === "tr") {
@@ -957,6 +987,13 @@ function parseSimpleTableElement(tblElement: XmlElementNode): TableNode {
957
987
  ...(borders ? { borders } : {}),
958
988
  ...(cellMargins ? { cellMargins } : {}),
959
989
  ...(tblLook ? { tblLook } : {}),
990
+ ...(indent ? { indent } : {}),
991
+ ...(layoutMode ? { layoutMode } : {}),
992
+ ...(cellSpacing ? { cellSpacing } : {}),
993
+ ...(caption !== undefined ? { caption } : {}),
994
+ ...(description !== undefined ? { description } : {}),
995
+ ...(bidiVisual !== undefined ? { bidiVisual } : {}),
996
+ ...(floating ? { floating } : {}),
960
997
  };
961
998
  }
962
999
 
@@ -970,6 +1007,9 @@ function parseSimpleTableRow(trElement: XmlElementNode): TableRowNode {
970
1007
  let height: TableRowNode["height"];
971
1008
  let heightRule: TableRowNode["heightRule"];
972
1009
  let isHeader: TableRowNode["isHeader"];
1010
+ let cantSplit: TableRowNode["cantSplit"];
1011
+ let horizontalAlignment: TableRowNode["horizontalAlignment"];
1012
+ let cnfStyle: TableRowNode["cnfStyle"];
973
1013
 
974
1014
  for (const child of trElement.children) {
975
1015
  if (child.type !== "element") continue;
@@ -980,6 +1020,9 @@ function parseSimpleTableRow(trElement: XmlElementNode): TableRowNode {
980
1020
  height = readRowHeight(child);
981
1021
  heightRule = readRowHeightRule(child);
982
1022
  isHeader = readRowIsHeader(child);
1023
+ cantSplit = readRowCantSplit(child);
1024
+ horizontalAlignment = readRowHorizontalAlignment(child);
1025
+ cnfStyle = readRowCnfStyle(child);
983
1026
  } else if (name === "tc") {
984
1027
  cells.push(parseSimpleTableCell(child));
985
1028
  }
@@ -991,6 +1034,9 @@ function parseSimpleTableRow(trElement: XmlElementNode): TableRowNode {
991
1034
  ...(height !== undefined ? { height } : {}),
992
1035
  ...(heightRule ? { heightRule } : {}),
993
1036
  ...(isHeader !== undefined ? { isHeader } : {}),
1037
+ ...(cantSplit !== undefined ? { cantSplit } : {}),
1038
+ ...(horizontalAlignment ? { horizontalAlignment } : {}),
1039
+ ...(cnfStyle ? { cnfStyle } : {}),
994
1040
  cells,
995
1041
  };
996
1042
  }
@@ -1004,6 +1050,11 @@ function parseSimpleTableCell(tcElement: XmlElementNode): TableCellNode {
1004
1050
  let borders: TableCellNode["borders"];
1005
1051
  let shading: TableCellNode["shading"];
1006
1052
  let verticalAlign: TableCellNode["verticalAlign"];
1053
+ let textDirection: TableCellNode["textDirection"];
1054
+ let noWrap: TableCellNode["noWrap"];
1055
+ let fitText: TableCellNode["fitText"];
1056
+ let margins: TableCellNode["margins"];
1057
+ let cnfStyle: TableCellNode["cnfStyle"];
1007
1058
 
1008
1059
  for (const child of tcElement.children) {
1009
1060
  if (child.type !== "element") continue;
@@ -1024,6 +1075,11 @@ function parseSimpleTableCell(tcElement: XmlElementNode): TableCellNode {
1024
1075
  borders = readCellBorders(child);
1025
1076
  shading = readCellShading(child);
1026
1077
  verticalAlign = readCellVerticalAlign(child);
1078
+ textDirection = readCellTextDirection(child);
1079
+ noWrap = readCellNoWrap(child);
1080
+ fitText = readCellFitText(child);
1081
+ margins = readCellMargins(child);
1082
+ cnfStyle = readCellCnfStyle(child);
1027
1083
  } else if (name === "p") {
1028
1084
  children.push(parseParagraphElement(child));
1029
1085
  }
@@ -1038,6 +1094,11 @@ function parseSimpleTableCell(tcElement: XmlElementNode): TableCellNode {
1038
1094
  ...(borders ? { borders } : {}),
1039
1095
  ...(shading ? { shading } : {}),
1040
1096
  ...(verticalAlign ? { verticalAlign } : {}),
1097
+ ...(textDirection ? { textDirection } : {}),
1098
+ ...(noWrap !== undefined ? { noWrap } : {}),
1099
+ ...(fitText !== undefined ? { fitText } : {}),
1100
+ ...(margins ? { margins } : {}),
1101
+ ...(cnfStyle ? { cnfStyle } : {}),
1041
1102
  children: children.length > 0 ? children : [{ type: "paragraph", children: [] }],
1042
1103
  };
1043
1104
  }
@@ -10,6 +10,8 @@ import type {
10
10
  TableBorders,
11
11
  TableCellBorders,
12
12
  TableCellMargins,
13
+ TableFloatingProperties,
14
+ TableIndent,
13
15
  TableLook,
14
16
  TableWidth,
15
17
  SectionProperties,
@@ -35,16 +37,31 @@ import { classifyFieldInstruction } from "./parse-fields.ts";
35
37
  import { resolveHighlightColor } from "./highlight-colors.ts";
36
38
  import {
37
39
  readCellBorders as readSharedCellBorders,
40
+ readCellCnfStyle as readSharedCellCnfStyle,
41
+ readCellFitText as readSharedCellFitText,
42
+ readCellMargins as readSharedCellMargins,
43
+ readCellNoWrap as readSharedCellNoWrap,
38
44
  readCellShading as readSharedCellShading,
45
+ readCellTextDirection as readSharedCellTextDirection,
39
46
  readCellVerticalAlign as readSharedCellVerticalAlign,
40
47
  readCellWidth as readSharedCellWidth,
41
48
  readGridColumns as readSharedGridColumns,
49
+ readRowCantSplit as readSharedRowCantSplit,
50
+ readRowCnfStyle as readSharedRowCnfStyle,
42
51
  readRowHeight as readSharedRowHeight,
43
52
  readRowHeightRule as readSharedRowHeightRule,
53
+ readRowHorizontalAlignment as readSharedRowHorizontalAlignment,
44
54
  readRowIsHeader as readSharedRowIsHeader,
45
55
  readTableAlignment as readSharedTableAlignment,
56
+ readTableBidiVisual as readSharedTableBidiVisual,
46
57
  readTableBorders as readSharedTableBorders,
58
+ readTableCaption as readSharedTableCaption,
47
59
  readTableCellMargins as readSharedTableCellMargins,
60
+ readTableCellSpacing as readSharedTableCellSpacing,
61
+ readTableDescription as readSharedTableDescription,
62
+ readTableFloating as readSharedTableFloating,
63
+ readTableIndent as readSharedTableIndent,
64
+ readTableLayoutMode as readSharedTableLayoutMode,
48
65
  readTableLook as readSharedTableLook,
49
66
  readTableStyleId as readSharedTableStyleId,
50
67
  readTableWidth as readSharedTableWidth,
@@ -92,6 +109,11 @@ export interface ParsedParagraphNode {
92
109
  bidi?: boolean;
93
110
  suppressLineNumbers?: boolean;
94
111
  cnfStyle?: string;
112
+ /** A.7: preserved w14 extension ids (paraId/textId). */
113
+ wordExtensionIds?: {
114
+ paraId?: string;
115
+ textId?: string;
116
+ };
95
117
  sectionProperties?: SectionProperties;
96
118
  sectionPropertiesXml?: string;
97
119
  children: ParsedInlineNode[];
@@ -327,6 +349,13 @@ export interface ParsedTableBlockNode {
327
349
  alignment?: "left" | "center" | "right";
328
350
  borders?: TableBorders;
329
351
  cellMargins?: TableCellMargins;
352
+ indent?: TableIndent;
353
+ layoutMode?: "fixed" | "autofit";
354
+ cellSpacing?: TableWidth;
355
+ caption?: string;
356
+ description?: string;
357
+ bidiVisual?: boolean;
358
+ floating?: TableFloatingProperties;
330
359
  rawXml: string;
331
360
  }
332
361
 
@@ -341,6 +370,9 @@ export interface ParsedTableRowNode {
341
370
  height?: number;
342
371
  heightRule?: "auto" | "atLeast" | "exact";
343
372
  isHeader?: boolean;
373
+ cantSplit?: boolean;
374
+ horizontalAlignment?: "left" | "center" | "right";
375
+ cnfStyle?: string;
344
376
  rawXml: string;
345
377
  }
346
378
 
@@ -354,6 +386,11 @@ export interface ParsedTableCellNode {
354
386
  borders?: TableCellBorders;
355
387
  shading?: CellShading;
356
388
  verticalAlign?: "top" | "center" | "bottom";
389
+ textDirection?: "lrTb" | "tbRl" | "btLr";
390
+ noWrap?: boolean;
391
+ fitText?: boolean;
392
+ margins?: TableCellMargins;
393
+ cnfStyle?: string;
357
394
  rawXml: string;
358
395
  }
359
396
 
@@ -493,6 +530,17 @@ function parseBodyChild(
493
530
  let sectionProperties: SectionProperties | undefined;
494
531
  let sectionPropertiesXml: string | undefined;
495
532
  let paragraphSupported = true;
533
+ // A.7: capture w14:paraId / w14:textId from the <w:p> element attributes
534
+ // so round-trip export can re-emit them unchanged.
535
+ const paraId = node.attributes["w14:paraId"] ?? node.attributes.paraId;
536
+ const textId = node.attributes["w14:textId"] ?? node.attributes.textId;
537
+ const wordExtensionIds: ParsedParagraphNode["wordExtensionIds"] | undefined =
538
+ paraId || textId
539
+ ? {
540
+ ...(paraId ? { paraId: paraId.toUpperCase() } : {}),
541
+ ...(textId ? { textId: textId.toUpperCase() } : {}),
542
+ }
543
+ : undefined;
496
544
  const children: ParsedInlineNode[] = [];
497
545
  let activeComplexField: {
498
546
  instruction: string;
@@ -709,6 +757,7 @@ function parseBodyChild(
709
757
  ...(bidi ? { bidi } : {}),
710
758
  ...(suppressLineNumbers ? { suppressLineNumbers } : {}),
711
759
  ...(cnfStyle ? { cnfStyle } : {}),
760
+ ...(wordExtensionIds ? { wordExtensionIds } : {}),
712
761
  ...(sectionProperties ? { sectionProperties } : {}),
713
762
  ...(sectionPropertiesXml ? { sectionPropertiesXml } : {}),
714
763
  children,
@@ -840,6 +889,13 @@ function parseTableElement(
840
889
  let alignment: ParsedTableBlockNode["alignment"];
841
890
  let borders: TableBorders | undefined;
842
891
  let cellMargins: TableCellMargins | undefined;
892
+ let indent: TableIndent | undefined;
893
+ let layoutMode: ParsedTableBlockNode["layoutMode"];
894
+ let cellSpacing: TableWidth | undefined;
895
+ let caption: string | undefined;
896
+ let description: string | undefined;
897
+ let bidiVisual: boolean | undefined;
898
+ let floating: TableFloatingProperties | undefined;
843
899
  const rows: ParsedTableRowNode[] = [];
844
900
 
845
901
  for (const child of node.children) {
@@ -854,6 +910,13 @@ function parseTableElement(
854
910
  alignment = readTableAlignment(child);
855
911
  borders = readTableBorders(child);
856
912
  cellMargins = readTableCellMargins(child);
913
+ indent = readTableIndent(child);
914
+ layoutMode = readTableLayoutMode(child);
915
+ cellSpacing = readTableCellSpacing(child);
916
+ caption = readTableCaption(child);
917
+ description = readTableDescription(child);
918
+ bidiVisual = readTableBidiVisual(child);
919
+ floating = readTableFloating(child);
857
920
  break;
858
921
  }
859
922
  case "tblGrid": {
@@ -878,6 +941,13 @@ function parseTableElement(
878
941
  ...(alignment ? { alignment } : {}),
879
942
  ...(borders ? { borders } : {}),
880
943
  ...(cellMargins ? { cellMargins } : {}),
944
+ ...(indent ? { indent } : {}),
945
+ ...(layoutMode ? { layoutMode } : {}),
946
+ ...(cellSpacing ? { cellSpacing } : {}),
947
+ ...(caption !== undefined ? { caption } : {}),
948
+ ...(description !== undefined ? { description } : {}),
949
+ ...(bidiVisual !== undefined ? { bidiVisual } : {}),
950
+ ...(floating ? { floating } : {}),
881
951
  rawXml: sourceXml.slice(node.start, node.end),
882
952
  };
883
953
  }
@@ -898,6 +968,9 @@ function parseTableRowElement(
898
968
  let height: number | undefined;
899
969
  let heightRule: ParsedTableRowNode["heightRule"];
900
970
  let isHeader: boolean | undefined;
971
+ let cantSplit: boolean | undefined;
972
+ let horizontalAlignment: ParsedTableRowNode["horizontalAlignment"];
973
+ let cnfStyle: string | undefined;
901
974
  const cells: ParsedTableCellNode[] = [];
902
975
 
903
976
  for (const child of node.children) {
@@ -913,6 +986,9 @@ function parseTableRowElement(
913
986
  height = readRowHeight(child);
914
987
  heightRule = readRowHeightRule(child);
915
988
  isHeader = readRowIsHeader(child);
989
+ cantSplit = readRowCantSplit(child);
990
+ horizontalAlignment = readRowHorizontalAlignment(child);
991
+ cnfStyle = readRowCnfStyle(child);
916
992
  break;
917
993
  case "tc":
918
994
  cells.push(parseTableCellElement(child, sourceXml, relationshipMap, relationships, mediaParts, sourcePartPath));
@@ -930,6 +1006,9 @@ function parseTableRowElement(
930
1006
  ...(height !== undefined ? { height } : {}),
931
1007
  ...(heightRule ? { heightRule } : {}),
932
1008
  ...(isHeader !== undefined ? { isHeader } : {}),
1009
+ ...(cantSplit !== undefined ? { cantSplit } : {}),
1010
+ ...(horizontalAlignment ? { horizontalAlignment } : {}),
1011
+ ...(cnfStyle ? { cnfStyle } : {}),
933
1012
  cells,
934
1013
  rawXml: sourceXml.slice(node.start, node.end),
935
1014
  };
@@ -950,6 +1029,11 @@ function parseTableCellElement(
950
1029
  let borders: TableCellBorders | undefined;
951
1030
  let shading: CellShading | undefined;
952
1031
  let verticalAlign: ParsedTableCellNode["verticalAlign"];
1032
+ let textDirection: ParsedTableCellNode["textDirection"];
1033
+ let noWrap: boolean | undefined;
1034
+ let fitText: boolean | undefined;
1035
+ let margins: TableCellMargins | undefined;
1036
+ let cnfStyle: string | undefined;
953
1037
  const children: ParsedBlockNode[] = [];
954
1038
 
955
1039
  for (const child of node.children) {
@@ -964,6 +1048,11 @@ function parseTableCellElement(
964
1048
  borders = readCellBorders(child);
965
1049
  shading = readCellShading(child);
966
1050
  verticalAlign = readCellVerticalAlign(child);
1051
+ textDirection = readCellTextDirection(child);
1052
+ noWrap = readCellNoWrap(child);
1053
+ fitText = readCellFitText(child);
1054
+ margins = readCellMargins(child);
1055
+ cnfStyle = readCellCnfStyle(child);
967
1056
  break;
968
1057
  }
969
1058
  default: {
@@ -984,6 +1073,11 @@ function parseTableCellElement(
984
1073
  ...(borders ? { borders } : {}),
985
1074
  ...(shading ? { shading } : {}),
986
1075
  ...(verticalAlign ? { verticalAlign } : {}),
1076
+ ...(textDirection ? { textDirection } : {}),
1077
+ ...(noWrap !== undefined ? { noWrap } : {}),
1078
+ ...(fitText !== undefined ? { fitText } : {}),
1079
+ ...(margins ? { margins } : {}),
1080
+ ...(cnfStyle ? { cnfStyle } : {}),
987
1081
  rawXml: sourceXml.slice(node.start, node.end),
988
1082
  };
989
1083
  }
@@ -1257,6 +1351,66 @@ function readCellVerticalAlign(node: XmlElementNode): ParsedTableCellNode["verti
1257
1351
  return readSharedCellVerticalAlign(node);
1258
1352
  }
1259
1353
 
1354
+ function readTableIndent(node: XmlElementNode): TableIndent | undefined {
1355
+ return readSharedTableIndent(node);
1356
+ }
1357
+
1358
+ function readTableLayoutMode(node: XmlElementNode): ParsedTableBlockNode["layoutMode"] {
1359
+ return readSharedTableLayoutMode(node);
1360
+ }
1361
+
1362
+ function readTableCellSpacing(node: XmlElementNode): TableWidth | undefined {
1363
+ return readSharedTableCellSpacing(node);
1364
+ }
1365
+
1366
+ function readTableCaption(node: XmlElementNode): string | undefined {
1367
+ return readSharedTableCaption(node);
1368
+ }
1369
+
1370
+ function readTableDescription(node: XmlElementNode): string | undefined {
1371
+ return readSharedTableDescription(node);
1372
+ }
1373
+
1374
+ function readTableBidiVisual(node: XmlElementNode): boolean | undefined {
1375
+ return readSharedTableBidiVisual(node);
1376
+ }
1377
+
1378
+ function readTableFloating(node: XmlElementNode): TableFloatingProperties | undefined {
1379
+ return readSharedTableFloating(node);
1380
+ }
1381
+
1382
+ function readRowCantSplit(node: XmlElementNode): boolean | undefined {
1383
+ return readSharedRowCantSplit(node);
1384
+ }
1385
+
1386
+ function readRowHorizontalAlignment(node: XmlElementNode): ParsedTableRowNode["horizontalAlignment"] {
1387
+ return readSharedRowHorizontalAlignment(node);
1388
+ }
1389
+
1390
+ function readRowCnfStyle(node: XmlElementNode): string | undefined {
1391
+ return readSharedRowCnfStyle(node);
1392
+ }
1393
+
1394
+ function readCellTextDirection(node: XmlElementNode): ParsedTableCellNode["textDirection"] {
1395
+ return readSharedCellTextDirection(node);
1396
+ }
1397
+
1398
+ function readCellNoWrap(node: XmlElementNode): boolean | undefined {
1399
+ return readSharedCellNoWrap(node);
1400
+ }
1401
+
1402
+ function readCellFitText(node: XmlElementNode): boolean | undefined {
1403
+ return readSharedCellFitText(node);
1404
+ }
1405
+
1406
+ function readCellMargins(node: XmlElementNode): TableCellMargins | undefined {
1407
+ return readSharedCellMargins(node);
1408
+ }
1409
+
1410
+ function readCellCnfStyle(node: XmlElementNode): string | undefined {
1411
+ return readSharedCellCnfStyle(node);
1412
+ }
1413
+
1260
1414
  /**
1261
1415
  * Check if a table's raw XML contains content that cannot safely round-trip
1262
1416
  * through the parsed table path yet. This includes:
@@ -2242,7 +2396,10 @@ function readRunMarks(node: XmlElementNode, sourceXml: string): MarksParseResult
2242
2396
  );
2243
2397
  if (colorNode) {
2244
2398
  const colorVal = colorNode.attributes["w:val"] ?? colorNode.attributes.val;
2245
- if (colorVal && colorVal !== "auto") {
2399
+ // A.9: preserve the literal "auto" marker so the serializer can round-trip
2400
+ // it back. The canonical colour string is free-form; downstream paint
2401
+ // paths treat "auto" as "inherit from theme/style".
2402
+ if (colorVal) {
2246
2403
  marks.push({ type: "textColor", color: colorVal });
2247
2404
  }
2248
2405
  }