@extend-ai/react-docx 0.7.0-alpha.4 → 0.7.0-alpha.5

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.
package/dist/index.cjs CHANGED
@@ -713,327 +713,7 @@ var init_src2 = __esm({
713
713
  }
714
714
  });
715
715
 
716
- // src/index.tsx
717
- var index_exports = {};
718
- __export(index_exports, {
719
- DEFAULT_MIN_PARAGRAPH_LINE_HEIGHT_PX: () => DEFAULT_MIN_PARAGRAPH_LINE_HEIGHT_PX,
720
- DEFAULT_PAGE_OVERFLOW_TOLERANCE_PX: () => DEFAULT_PAGE_OVERFLOW_TOLERANCE_PX,
721
- DocxEditorViewer: () => DocxEditorViewer,
722
- ReactDocxViewer: () => ReactDocxViewer,
723
- applyRunStyle: () => applyRunStyle,
724
- buildDocModel: () => buildDocModel,
725
- buildDocModelFromBytes: () => buildDocModelFromBytes,
726
- buildDocumentPageNodeSegments: () => buildDocumentPageNodeSegments2,
727
- buildDocumentPageRanges: () => buildDocumentPageRanges,
728
- buildLayoutSnapshot: () => buildLayoutSnapshot,
729
- cloneDocModel: () => cloneDocModel,
730
- collectDocxEstimatedOverflowBreakStartNodeIndexes: () => collectDocxEstimatedOverflowBreakStartNodeIndexes,
731
- collectDocxHardPageBreakStartNodeIndexes: () => collectDocxHardPageBreakStartNodeIndexes2,
732
- collectDocxLastRenderedPageBreakStartNodeIndexes: () => collectDocxLastRenderedPageBreakStartNodeIndexes,
733
- collectTableExplicitPageBreakInfo: () => collectTableExplicitPageBreakInfo2,
734
- collectTopLevelExplicitPageBreakStartNodeIndexes: () => collectTopLevelExplicitPageBreakStartNodeIndexes2,
735
- copyParagraphs: () => copyParagraphs,
736
- createMinimalDocxPackage: () => createMinimalDocxPackage,
737
- defaultStarterModel: () => defaultStarterModel,
738
- duplicateParagraph: () => duplicateParagraph,
739
- getPart: () => getPart,
740
- initWasm: () => initWasm,
741
- insertParagraph: () => insertParagraph,
742
- layoutDocument: () => layoutDocument,
743
- modelToDocumentXml: () => modelToDocumentXml,
744
- normalizeDocModel: () => normalizeDocModel,
745
- packageToArrayBuffer: () => packageToArrayBuffer,
746
- paragraphAfterSpacingPx: () => paragraphAfterSpacingPx2,
747
- paragraphBeforeSpacingPx: () => paragraphBeforeSpacingPx2,
748
- paragraphHasExplicitPageBreak: () => paragraphHasExplicitPageBreak3,
749
- paragraphHasLastRenderedPageBreak: () => paragraphHasLastRenderedPageBreak2,
750
- paragraphHasPageBreakBefore: () => paragraphHasPageBreakBefore3,
751
- paragraphLetterheadColumnGroupAtSegmentOffset: () => paragraphLetterheadColumnGroupAtSegmentOffset2,
752
- paragraphLetterheadFloatSideAtNodeIndex: () => paragraphLetterheadFloatSideAtNodeIndex,
753
- paragraphStartsWithLastRenderedPageBreak: () => paragraphStartsWithLastRenderedPageBreak2,
754
- parseDocx: () => parseDocx,
755
- parseParagraphsFromClipboard: () => parseParagraphsFromClipboard,
756
- parseSectionLayout: () => parseSectionLayout,
757
- pasteParagraphs: () => pasteParagraphs,
758
- removeParagraph: () => removeParagraph,
759
- replaceText: () => replaceText,
760
- resolveDocumentForLayout: () => resolveDocumentForLayout,
761
- resolveDocumentLayout: () => resolveDocumentLayout,
762
- resolveDocumentPageSegmentStartNodeIndex: () => resolveDocumentPageSegmentStartNodeIndex,
763
- resolveDocumentSectionsFromMetadata: () => resolveDocumentSectionsFromMetadata2,
764
- resolveDocxPageThumbnailResolution: () => resolveDocxPageThumbnailResolution,
765
- resolvePaginationSectionMetricsIndexForNodeIndex: () => resolvePaginationSectionMetricsIndexForNodeIndex2,
766
- resolveParagraphBeforeSpacingPx: () => resolveParagraphBeforeSpacingPx2,
767
- resolveSectionIndexForNodeIndex: () => resolveSectionIndexForNodeIndex2,
768
- resolveSectionPropertiesXmlForNodeIndex: () => resolveSectionPropertiesXmlForNodeIndex,
769
- scalePaginationSectionMetricsHeights: () => scalePaginationSectionMetricsHeights2,
770
- scorePaginationAgainstStoredPageBreaks: () => scorePaginationAgainstStoredPageBreaks,
771
- sectionBreakAfterParagraphStartsNewPage: () => sectionBreakAfterParagraphStartsNewPage2,
772
- sectionBreakPropertiesStartNewPage: () => sectionBreakPropertiesStartNewPage2,
773
- sectionTitlePageEnabled: () => sectionTitlePageEnabled2,
774
- selectSectionVariantForPage: () => selectSectionVariantForPage2,
775
- serializeDocModel: () => serializeDocModel,
776
- serializeDocx: () => serializeDocx,
777
- serializeParagraphsForClipboard: () => serializeParagraphsForClipboard,
778
- setParagraphAlignment: () => setParagraphAlignment,
779
- setParagraphHeading: () => setParagraphHeading,
780
- setRunColor: () => setRunColor,
781
- setRunHighlight: () => setRunHighlight,
782
- setWasmSource: () => setWasmSource,
783
- splitParagraphChildrenAtTextOffsets: () => splitParagraphChildrenAtTextOffsets,
784
- toggleRunStyleFlag: () => toggleRunStyleFlag,
785
- updateParagraphText: () => updateParagraphText,
786
- updateTableCellParagraphText: () => updateTableCellParagraphText,
787
- updateTableCellParagraphTextRecursive: () => updateTableCellParagraphTextRecursive,
788
- updateTableCellText: () => updateTableCellText,
789
- useDocxBorders: () => useDocxBorders,
790
- useDocxDocumentTheme: () => useDocxDocumentTheme,
791
- useDocxEditor: () => useDocxEditor,
792
- useDocxFormFields: () => useDocxFormFields,
793
- useDocxImageWrapMenu: () => useDocxImageWrapMenu,
794
- useDocxLineSpacing: () => useDocxLineSpacing,
795
- useDocxModel: () => useDocxModel,
796
- useDocxPageLayout: () => useDocxPageLayout,
797
- useDocxPageThumbnails: () => useDocxPageThumbnails,
798
- useDocxPagination: () => useDocxPagination,
799
- useDocxParagraphStyles: () => useDocxParagraphStyles,
800
- useDocxTrackChanges: () => useDocxTrackChanges,
801
- useDocxViewerThumbnails: () => useDocxViewerThumbnails,
802
- withPart: () => withPart
803
- });
804
- module.exports = __toCommonJS(index_exports);
805
- init_cjs_shims();
806
- var React2 = __toESM(require("react"), 1);
807
-
808
- // ../layout-engine/src/index.ts
809
- init_cjs_shims();
810
- var DEFAULT_OPTIONS = {
811
- pageWidth: 816,
812
- pageHeight: 1056,
813
- margin: 72,
814
- minLineHeight: 22,
815
- paragraphSpacing: 8,
816
- tableCellPadding: 8
817
- };
818
- function headingScale(level) {
819
- if (!level) {
820
- return 1;
821
- }
822
- switch (level) {
823
- case 1:
824
- return 2.15;
825
- case 2:
826
- return 1.75;
827
- case 3:
828
- return 1.45;
829
- case 4:
830
- return 1.28;
831
- case 5:
832
- return 1.15;
833
- case 6:
834
- return 1.05;
835
- default:
836
- return 1;
837
- }
838
- }
839
- function runHeightPx(run) {
840
- if (run.kind === "image") {
841
- if (run.floating) {
842
- return 0;
843
- }
844
- return run.heightPx ?? 96;
845
- }
846
- const fontSizePt = run.style?.fontSizePt ?? 12;
847
- return Math.round(fontSizePt * 1.6);
848
- }
849
- function lineHeightFromRuns(runs, minLineHeight, headingLevel) {
850
- const base = runs.reduce((largest, run) => {
851
- return Math.max(largest, runHeightPx(run));
852
- }, 12) * headingScale(headingLevel);
853
- return Math.max(minLineHeight, Math.round(base));
854
- }
855
- function spacingForBlock(baseSpacing, headingLevel) {
856
- if (!headingLevel) {
857
- return baseSpacing;
858
- }
859
- return baseSpacing + Math.max(4, (7 - headingLevel) * 2);
860
- }
861
- function formFieldDisplayText(field) {
862
- switch (field.fieldType) {
863
- case "checkbox":
864
- return field.checked ? field.checkedSymbol ?? "\u2612" : field.uncheckedSymbol ?? "\u2610";
865
- case "dropdown":
866
- case "date":
867
- case "text":
868
- default:
869
- return field.value ?? "";
870
- }
871
- }
872
- function paragraphToLayout(paragraph, idPrefix, x, y, width, minLineHeight) {
873
- const runs = paragraph.children.map((child, runIndex) => {
874
- if (child.type === "text") {
875
- return {
876
- kind: "text",
877
- id: `${idPrefix}-run-${runIndex}`,
878
- text: child.text,
879
- style: child.style,
880
- link: child.link
881
- };
882
- }
883
- if (child.type === "form-field") {
884
- return {
885
- kind: "text",
886
- id: `${idPrefix}-run-${runIndex}`,
887
- text: formFieldDisplayText(child),
888
- style: child.style,
889
- link: child.link
890
- };
891
- }
892
- return {
893
- kind: "image",
894
- id: `${idPrefix}-run-${runIndex}`,
895
- src: child.src,
896
- alt: child.alt,
897
- widthPx: child.widthPx,
898
- heightPx: child.heightPx,
899
- contentType: child.contentType,
900
- data: child.data ? new Uint8Array(child.data) : void 0,
901
- floating: Boolean(child.floating)
902
- };
903
- });
904
- return {
905
- kind: "paragraph",
906
- id: idPrefix,
907
- runs,
908
- align: paragraph.style?.align ?? "left",
909
- headingLevel: paragraph.style?.headingLevel,
910
- x,
911
- y,
912
- width,
913
- height: lineHeightFromRuns(runs, minLineHeight, paragraph.style?.headingLevel)
914
- };
915
- }
916
- function paragraphsForTableCellNodes(nodes) {
917
- const paragraphs = [];
918
- const walk = (items) => {
919
- for (const item of items) {
920
- if (item.type === "paragraph") {
921
- paragraphs.push(item);
922
- continue;
923
- }
924
- for (const row of item.rows) {
925
- for (const cell of row.cells) {
926
- walk(cell.nodes);
927
- }
928
- }
929
- }
930
- };
931
- walk(nodes);
932
- return paragraphs;
933
- }
934
- function tableToLayout(table, idPrefix, x, y, width, options) {
935
- const columnCount = Math.max(
936
- 1,
937
- ...table.rows.map(
938
- (row) => row.cells.reduce(
939
- (sum, cell) => sum + (cell.style?.gridSpan && cell.style.gridSpan > 1 ? cell.style.gridSpan : 1),
940
- 0
941
- )
942
- )
943
- );
944
- const baseCellWidth = width / columnCount;
945
- let tableHeight = 0;
946
- const rows = table.rows.map((row, rowIndex) => {
947
- let rowHeight = options.minLineHeight + options.tableCellPadding * 2;
948
- let columnCursor = 0;
949
- const cells = row.cells.map((cell, cellIndex) => {
950
- const colSpan = cell.style?.gridSpan && cell.style.gridSpan > 1 ? cell.style.gridSpan : 1;
951
- const cellWidth = baseCellWidth * colSpan;
952
- const cellParagraphs = paragraphsForTableCellNodes(cell.nodes);
953
- const paragraphBlocks = cellParagraphs.map(
954
- (paragraph, paragraphIndex) => paragraphToLayout(
955
- paragraph,
956
- `${idPrefix}-r${rowIndex}-c${cellIndex}-p${paragraphIndex}`,
957
- x + columnCursor * baseCellWidth,
958
- y,
959
- cellWidth,
960
- options.minLineHeight
961
- )
962
- );
963
- const paragraphsHeight = paragraphBlocks.reduce((sum, block) => sum + block.height, 0);
964
- rowHeight = Math.max(rowHeight, paragraphsHeight + options.tableCellPadding * 2);
965
- columnCursor += colSpan;
966
- return {
967
- id: `${idPrefix}-r${rowIndex}-c${cellIndex}`,
968
- colSpan,
969
- backgroundColor: cell.style?.backgroundColor ?? row.style?.backgroundColor,
970
- paragraphs: paragraphBlocks
971
- };
972
- });
973
- tableHeight += rowHeight;
974
- return {
975
- id: `${idPrefix}-row-${rowIndex}`,
976
- backgroundColor: row.style?.backgroundColor,
977
- cells
978
- };
979
- });
980
- return {
981
- kind: "table",
982
- id: idPrefix,
983
- x,
984
- y,
985
- width,
986
- height: Math.max(tableHeight, options.minLineHeight * 2),
987
- rows
988
- };
989
- }
990
- function estimateBlockHeight(block) {
991
- if (block.kind === "paragraph") {
992
- return block.height;
993
- }
994
- return block.height;
995
- }
996
- function layoutDocument(model, options = {}) {
997
- const resolved = { ...DEFAULT_OPTIONS, ...options };
998
- const pages = [{ number: 1, blocks: [] }];
999
- const contentWidth = resolved.pageWidth - resolved.margin * 2;
1000
- const pageBottom = resolved.pageHeight - resolved.margin;
1001
- let cursorY = resolved.margin;
1002
- for (const [index, node] of model.nodes.entries()) {
1003
- const block = node.type === "paragraph" ? paragraphToLayout(
1004
- node,
1005
- `paragraph-${index}`,
1006
- resolved.margin,
1007
- cursorY,
1008
- contentWidth,
1009
- resolved.minLineHeight
1010
- ) : tableToLayout(node, `table-${index}`, resolved.margin, cursorY, contentWidth, resolved);
1011
- const blockHeight = estimateBlockHeight(block);
1012
- const currentPage = pages[pages.length - 1];
1013
- if (cursorY + blockHeight > pageBottom && currentPage.blocks.length > 0) {
1014
- pages.push({
1015
- number: pages.length + 1,
1016
- blocks: []
1017
- });
1018
- cursorY = resolved.margin;
1019
- if (block.kind === "paragraph") {
1020
- block.y = cursorY;
1021
- } else {
1022
- block.y = cursorY;
1023
- }
1024
- }
1025
- pages[pages.length - 1].blocks.push(block);
1026
- cursorY += blockHeight + (block.kind === "paragraph" ? spacingForBlock(resolved.paragraphSpacing, block.headingLevel) : resolved.paragraphSpacing + 10);
1027
- }
1028
- return pages;
1029
- }
1030
-
1031
- // ../doc-model/src/index.ts
1032
- init_cjs_shims();
1033
- init_src();
1034
-
1035
716
  // ../doc-model/src/normalize.ts
1036
- init_cjs_shims();
1037
717
  function normalizeUint8Array(value) {
1038
718
  if (value instanceof Uint8Array) {
1039
719
  return value;
@@ -1120,12 +800,22 @@ function normalizeDocModel(model) {
1120
800
  }
1121
801
  };
1122
802
  }
803
+ var init_normalize = __esm({
804
+ "../doc-model/src/normalize.ts"() {
805
+ "use strict";
806
+ init_cjs_shims();
807
+ }
808
+ });
1123
809
 
1124
810
  // ../doc-model/src/types.ts
1125
- init_cjs_shims();
811
+ var init_types = __esm({
812
+ "../doc-model/src/types.ts"() {
813
+ "use strict";
814
+ init_cjs_shims();
815
+ }
816
+ });
1126
817
 
1127
818
  // ../doc-model/src/clone.ts
1128
- init_cjs_shims();
1129
819
  function isParagraphCellContent(node) {
1130
820
  return node.type === "paragraph";
1131
821
  }
@@ -1423,12 +1113,26 @@ function cloneDocModel(model) {
1423
1113
  endnotes: model.metadata.endnotes?.map((note) => ({
1424
1114
  ...note,
1425
1115
  nodes: note.nodes?.map(cloneDocNode)
1426
- }))
1116
+ })),
1117
+ comments: model.metadata.comments?.map((comment) => ({ ...comment }))
1427
1118
  }
1428
1119
  };
1429
1120
  }
1121
+ var init_clone = __esm({
1122
+ "../doc-model/src/clone.ts"() {
1123
+ "use strict";
1124
+ init_cjs_shims();
1125
+ }
1126
+ });
1430
1127
 
1431
1128
  // ../doc-model/src/index.ts
1129
+ var src_exports2 = {};
1130
+ __export(src_exports2, {
1131
+ buildDocModel: () => buildDocModel,
1132
+ buildDocModelFromBytes: () => buildDocModelFromBytes,
1133
+ cloneDocModel: () => cloneDocModel,
1134
+ normalizeDocModel: () => normalizeDocModel
1135
+ });
1432
1136
  async function buildDocModel(pkg) {
1433
1137
  const wasmPackage = mapsToWasmPackage({
1434
1138
  parts: pkg.parts,
@@ -1445,9 +1149,474 @@ async function buildDocModelFromBytes(bytes) {
1445
1149
  const model = await buildDocModel(pkg);
1446
1150
  return { package: pkg, model };
1447
1151
  }
1152
+ var init_src3 = __esm({
1153
+ "../doc-model/src/index.ts"() {
1154
+ "use strict";
1155
+ init_cjs_shims();
1156
+ init_src();
1157
+ init_normalize();
1158
+ init_types();
1159
+ init_clone();
1160
+ init_normalize();
1161
+ }
1162
+ });
1448
1163
 
1449
1164
  // src/index.tsx
1450
- init_src2();
1165
+ var index_exports = {};
1166
+ __export(index_exports, {
1167
+ DEFAULT_MIN_PARAGRAPH_LINE_HEIGHT_PX: () => DEFAULT_MIN_PARAGRAPH_LINE_HEIGHT_PX,
1168
+ DEFAULT_PAGE_OVERFLOW_TOLERANCE_PX: () => DEFAULT_PAGE_OVERFLOW_TOLERANCE_PX,
1169
+ DocxEditorViewer: () => DocxEditorViewer,
1170
+ ReactDocxViewer: () => ReactDocxViewer,
1171
+ applyRunStyle: () => applyRunStyle,
1172
+ buildDocModel: () => buildDocModel,
1173
+ buildDocModelFromBytes: () => buildDocModelFromBytes,
1174
+ buildDocumentPageNodeSegments: () => buildDocumentPageNodeSegments2,
1175
+ buildDocumentPageRanges: () => buildDocumentPageRanges,
1176
+ buildLayoutSnapshot: () => buildLayoutSnapshot,
1177
+ cloneDocModel: () => cloneDocModel,
1178
+ collectDocxEstimatedOverflowBreakStartNodeIndexes: () => collectDocxEstimatedOverflowBreakStartNodeIndexes,
1179
+ collectDocxHardPageBreakStartNodeIndexes: () => collectDocxHardPageBreakStartNodeIndexes2,
1180
+ collectDocxLastRenderedPageBreakStartNodeIndexes: () => collectDocxLastRenderedPageBreakStartNodeIndexes,
1181
+ collectTableExplicitPageBreakInfo: () => collectTableExplicitPageBreakInfo2,
1182
+ collectTopLevelExplicitPageBreakStartNodeIndexes: () => collectTopLevelExplicitPageBreakStartNodeIndexes2,
1183
+ copyParagraphs: () => copyParagraphs,
1184
+ createMinimalDocxPackage: () => createMinimalDocxPackage,
1185
+ defaultStarterModel: () => defaultStarterModel,
1186
+ duplicateParagraph: () => duplicateParagraph,
1187
+ getPart: () => getPart,
1188
+ initWasm: () => initWasm,
1189
+ insertParagraph: () => insertParagraph,
1190
+ layoutDocument: () => layoutDocument,
1191
+ modelToDocumentXml: () => modelToDocumentXml,
1192
+ normalizeDocModel: () => normalizeDocModel,
1193
+ packageToArrayBuffer: () => packageToArrayBuffer,
1194
+ paragraphAfterSpacingPx: () => paragraphAfterSpacingPx2,
1195
+ paragraphBeforeSpacingPx: () => paragraphBeforeSpacingPx2,
1196
+ paragraphHasExplicitPageBreak: () => paragraphHasExplicitPageBreak3,
1197
+ paragraphHasLastRenderedPageBreak: () => paragraphHasLastRenderedPageBreak2,
1198
+ paragraphHasPageBreakBefore: () => paragraphHasPageBreakBefore3,
1199
+ paragraphLetterheadColumnGroupAtSegmentOffset: () => paragraphLetterheadColumnGroupAtSegmentOffset2,
1200
+ paragraphLetterheadFloatSideAtNodeIndex: () => paragraphLetterheadFloatSideAtNodeIndex,
1201
+ paragraphStartsWithLastRenderedPageBreak: () => paragraphStartsWithLastRenderedPageBreak2,
1202
+ parseDocx: () => parseDocx,
1203
+ parseParagraphsFromClipboard: () => parseParagraphsFromClipboard,
1204
+ parseSectionLayout: () => parseSectionLayout,
1205
+ pasteParagraphs: () => pasteParagraphs,
1206
+ removeParagraph: () => removeParagraph,
1207
+ replaceText: () => replaceText,
1208
+ resolveDocumentForLayout: () => resolveDocumentForLayout,
1209
+ resolveDocumentLayout: () => resolveDocumentLayout,
1210
+ resolveDocumentPageSegmentStartNodeIndex: () => resolveDocumentPageSegmentStartNodeIndex,
1211
+ resolveDocumentSectionsFromMetadata: () => resolveDocumentSectionsFromMetadata2,
1212
+ resolveDocxPageThumbnailResolution: () => resolveDocxPageThumbnailResolution,
1213
+ resolvePaginationSectionMetricsIndexForNodeIndex: () => resolvePaginationSectionMetricsIndexForNodeIndex2,
1214
+ resolveParagraphBeforeSpacingPx: () => resolveParagraphBeforeSpacingPx2,
1215
+ resolveSectionIndexForNodeIndex: () => resolveSectionIndexForNodeIndex2,
1216
+ resolveSectionPropertiesXmlForNodeIndex: () => resolveSectionPropertiesXmlForNodeIndex,
1217
+ scalePaginationSectionMetricsHeights: () => scalePaginationSectionMetricsHeights2,
1218
+ scorePaginationAgainstStoredPageBreaks: () => scorePaginationAgainstStoredPageBreaks,
1219
+ sectionBreakAfterParagraphStartsNewPage: () => sectionBreakAfterParagraphStartsNewPage2,
1220
+ sectionBreakPropertiesStartNewPage: () => sectionBreakPropertiesStartNewPage2,
1221
+ sectionTitlePageEnabled: () => sectionTitlePageEnabled2,
1222
+ selectSectionVariantForPage: () => selectSectionVariantForPage2,
1223
+ serializeDocModel: () => serializeDocModel,
1224
+ serializeDocx: () => serializeDocx,
1225
+ serializeParagraphsForClipboard: () => serializeParagraphsForClipboard,
1226
+ setParagraphAlignment: () => setParagraphAlignment,
1227
+ setParagraphHeading: () => setParagraphHeading,
1228
+ setRunColor: () => setRunColor,
1229
+ setRunHighlight: () => setRunHighlight,
1230
+ setWasmSource: () => setWasmSource,
1231
+ splitParagraphChildrenAtTextOffsets: () => splitParagraphChildrenAtTextOffsets,
1232
+ toggleRunStyleFlag: () => toggleRunStyleFlag,
1233
+ updateParagraphText: () => updateParagraphText,
1234
+ updateTableCellParagraphText: () => updateTableCellParagraphText,
1235
+ updateTableCellParagraphTextRecursive: () => updateTableCellParagraphTextRecursive,
1236
+ updateTableCellText: () => updateTableCellText,
1237
+ useDocxBorders: () => useDocxBorders,
1238
+ useDocxComments: () => useDocxComments,
1239
+ useDocxDocumentTheme: () => useDocxDocumentTheme,
1240
+ useDocxEditor: () => useDocxEditor,
1241
+ useDocxFormFields: () => useDocxFormFields,
1242
+ useDocxImageWrapMenu: () => useDocxImageWrapMenu,
1243
+ useDocxLineSpacing: () => useDocxLineSpacing,
1244
+ useDocxModel: () => useDocxModel,
1245
+ useDocxPageLayout: () => useDocxPageLayout,
1246
+ useDocxPageThumbnails: () => useDocxPageThumbnails,
1247
+ useDocxPagination: () => useDocxPagination,
1248
+ useDocxParagraphStyles: () => useDocxParagraphStyles,
1249
+ useDocxTrackChanges: () => useDocxTrackChanges,
1250
+ useDocxViewerThumbnails: () => useDocxViewerThumbnails,
1251
+ withPart: () => withPart
1252
+ });
1253
+ module.exports = __toCommonJS(index_exports);
1254
+ init_cjs_shims();
1255
+ var React2 = __toESM(require("react"), 1);
1256
+
1257
+ // ../layout-engine/src/index.ts
1258
+ init_cjs_shims();
1259
+ var DEFAULT_OPTIONS = {
1260
+ pageWidth: 816,
1261
+ pageHeight: 1056,
1262
+ margin: 72,
1263
+ minLineHeight: 22,
1264
+ paragraphSpacing: 8,
1265
+ tableCellPadding: 8
1266
+ };
1267
+ function headingScale(level) {
1268
+ if (!level) {
1269
+ return 1;
1270
+ }
1271
+ switch (level) {
1272
+ case 1:
1273
+ return 2.15;
1274
+ case 2:
1275
+ return 1.75;
1276
+ case 3:
1277
+ return 1.45;
1278
+ case 4:
1279
+ return 1.28;
1280
+ case 5:
1281
+ return 1.15;
1282
+ case 6:
1283
+ return 1.05;
1284
+ default:
1285
+ return 1;
1286
+ }
1287
+ }
1288
+ function runHeightPx(run) {
1289
+ if (run.kind === "image") {
1290
+ if (run.floating) {
1291
+ return 0;
1292
+ }
1293
+ return run.heightPx ?? 96;
1294
+ }
1295
+ const fontSizePt = run.style?.fontSizePt ?? 12;
1296
+ return Math.round(fontSizePt * 1.6);
1297
+ }
1298
+ function lineHeightFromRuns(runs, minLineHeight, headingLevel) {
1299
+ const base = runs.reduce((largest, run) => {
1300
+ return Math.max(largest, runHeightPx(run));
1301
+ }, 12) * headingScale(headingLevel);
1302
+ return Math.max(minLineHeight, Math.round(base));
1303
+ }
1304
+ function spacingForBlock(baseSpacing, headingLevel) {
1305
+ if (!headingLevel) {
1306
+ return baseSpacing;
1307
+ }
1308
+ return baseSpacing + Math.max(4, (7 - headingLevel) * 2);
1309
+ }
1310
+ function formFieldDisplayText(field) {
1311
+ switch (field.fieldType) {
1312
+ case "checkbox":
1313
+ return field.checked ? field.checkedSymbol ?? "\u2612" : field.uncheckedSymbol ?? "\u2610";
1314
+ case "dropdown":
1315
+ case "date":
1316
+ case "text":
1317
+ default:
1318
+ return field.value ?? "";
1319
+ }
1320
+ }
1321
+ function paragraphToLayout(paragraph, idPrefix, x, y, width, minLineHeight) {
1322
+ const runs = paragraph.children.map((child, runIndex) => {
1323
+ if (child.type === "text") {
1324
+ return {
1325
+ kind: "text",
1326
+ id: `${idPrefix}-run-${runIndex}`,
1327
+ text: child.text,
1328
+ style: child.style,
1329
+ link: child.link
1330
+ };
1331
+ }
1332
+ if (child.type === "form-field") {
1333
+ return {
1334
+ kind: "text",
1335
+ id: `${idPrefix}-run-${runIndex}`,
1336
+ text: formFieldDisplayText(child),
1337
+ style: child.style,
1338
+ link: child.link
1339
+ };
1340
+ }
1341
+ return {
1342
+ kind: "image",
1343
+ id: `${idPrefix}-run-${runIndex}`,
1344
+ src: child.src,
1345
+ alt: child.alt,
1346
+ widthPx: child.widthPx,
1347
+ heightPx: child.heightPx,
1348
+ contentType: child.contentType,
1349
+ data: child.data ? new Uint8Array(child.data) : void 0,
1350
+ floating: Boolean(child.floating)
1351
+ };
1352
+ });
1353
+ return {
1354
+ kind: "paragraph",
1355
+ id: idPrefix,
1356
+ runs,
1357
+ align: paragraph.style?.align ?? "left",
1358
+ headingLevel: paragraph.style?.headingLevel,
1359
+ x,
1360
+ y,
1361
+ width,
1362
+ height: lineHeightFromRuns(runs, minLineHeight, paragraph.style?.headingLevel)
1363
+ };
1364
+ }
1365
+ function paragraphsForTableCellNodes(nodes) {
1366
+ const paragraphs = [];
1367
+ const walk = (items) => {
1368
+ for (const item of items) {
1369
+ if (item.type === "paragraph") {
1370
+ paragraphs.push(item);
1371
+ continue;
1372
+ }
1373
+ for (const row of item.rows) {
1374
+ for (const cell of row.cells) {
1375
+ walk(cell.nodes);
1376
+ }
1377
+ }
1378
+ }
1379
+ };
1380
+ walk(nodes);
1381
+ return paragraphs;
1382
+ }
1383
+ function tableToLayout(table, idPrefix, x, y, width, options) {
1384
+ const columnCount = Math.max(
1385
+ 1,
1386
+ ...table.rows.map(
1387
+ (row) => row.cells.reduce(
1388
+ (sum, cell) => sum + (cell.style?.gridSpan && cell.style.gridSpan > 1 ? cell.style.gridSpan : 1),
1389
+ 0
1390
+ )
1391
+ )
1392
+ );
1393
+ const baseCellWidth = width / columnCount;
1394
+ let tableHeight = 0;
1395
+ const rows = table.rows.map((row, rowIndex) => {
1396
+ let rowHeight = options.minLineHeight + options.tableCellPadding * 2;
1397
+ let columnCursor = 0;
1398
+ const cells = row.cells.map((cell, cellIndex) => {
1399
+ const colSpan = cell.style?.gridSpan && cell.style.gridSpan > 1 ? cell.style.gridSpan : 1;
1400
+ const cellWidth = baseCellWidth * colSpan;
1401
+ const cellParagraphs = paragraphsForTableCellNodes(cell.nodes);
1402
+ const paragraphBlocks = cellParagraphs.map(
1403
+ (paragraph, paragraphIndex) => paragraphToLayout(
1404
+ paragraph,
1405
+ `${idPrefix}-r${rowIndex}-c${cellIndex}-p${paragraphIndex}`,
1406
+ x + columnCursor * baseCellWidth,
1407
+ y,
1408
+ cellWidth,
1409
+ options.minLineHeight
1410
+ )
1411
+ );
1412
+ const paragraphsHeight = paragraphBlocks.reduce((sum, block) => sum + block.height, 0);
1413
+ rowHeight = Math.max(rowHeight, paragraphsHeight + options.tableCellPadding * 2);
1414
+ columnCursor += colSpan;
1415
+ return {
1416
+ id: `${idPrefix}-r${rowIndex}-c${cellIndex}`,
1417
+ colSpan,
1418
+ backgroundColor: cell.style?.backgroundColor ?? row.style?.backgroundColor,
1419
+ paragraphs: paragraphBlocks
1420
+ };
1421
+ });
1422
+ tableHeight += rowHeight;
1423
+ return {
1424
+ id: `${idPrefix}-row-${rowIndex}`,
1425
+ backgroundColor: row.style?.backgroundColor,
1426
+ cells
1427
+ };
1428
+ });
1429
+ return {
1430
+ kind: "table",
1431
+ id: idPrefix,
1432
+ x,
1433
+ y,
1434
+ width,
1435
+ height: Math.max(tableHeight, options.minLineHeight * 2),
1436
+ rows
1437
+ };
1438
+ }
1439
+ function estimateBlockHeight(block) {
1440
+ if (block.kind === "paragraph") {
1441
+ return block.height;
1442
+ }
1443
+ return block.height;
1444
+ }
1445
+ function layoutDocument(model, options = {}) {
1446
+ const resolved = { ...DEFAULT_OPTIONS, ...options };
1447
+ const pages = [{ number: 1, blocks: [] }];
1448
+ const contentWidth = resolved.pageWidth - resolved.margin * 2;
1449
+ const pageBottom = resolved.pageHeight - resolved.margin;
1450
+ let cursorY = resolved.margin;
1451
+ for (const [index, node] of model.nodes.entries()) {
1452
+ const block = node.type === "paragraph" ? paragraphToLayout(
1453
+ node,
1454
+ `paragraph-${index}`,
1455
+ resolved.margin,
1456
+ cursorY,
1457
+ contentWidth,
1458
+ resolved.minLineHeight
1459
+ ) : tableToLayout(node, `table-${index}`, resolved.margin, cursorY, contentWidth, resolved);
1460
+ const blockHeight = estimateBlockHeight(block);
1461
+ const currentPage = pages[pages.length - 1];
1462
+ if (cursorY + blockHeight > pageBottom && currentPage.blocks.length > 0) {
1463
+ pages.push({
1464
+ number: pages.length + 1,
1465
+ blocks: []
1466
+ });
1467
+ cursorY = resolved.margin;
1468
+ if (block.kind === "paragraph") {
1469
+ block.y = cursorY;
1470
+ } else {
1471
+ block.y = cursorY;
1472
+ }
1473
+ }
1474
+ pages[pages.length - 1].blocks.push(block);
1475
+ cursorY += blockHeight + (block.kind === "paragraph" ? spacingForBlock(resolved.paragraphSpacing, block.headingLevel) : resolved.paragraphSpacing + 10);
1476
+ }
1477
+ return pages;
1478
+ }
1479
+
1480
+ // src/docx-import.ts
1481
+ init_cjs_shims();
1482
+ var nextImportWorkerRequestId = 1;
1483
+ function createAbortError() {
1484
+ if (typeof DOMException !== "undefined") {
1485
+ return new DOMException("DOCX import was aborted", "AbortError");
1486
+ }
1487
+ const error = new Error("DOCX import was aborted");
1488
+ error.name = "AbortError";
1489
+ return error;
1490
+ }
1491
+ function errorFromWorkerResponse(response) {
1492
+ const error = new Error(response.error.message);
1493
+ error.name = response.error.name ?? "Error";
1494
+ if (response.error.stack) {
1495
+ error.stack = response.error.stack;
1496
+ }
1497
+ return error;
1498
+ }
1499
+ function canUseDocxImportWorker(options) {
1500
+ return options.useWorker !== false && typeof Worker !== "undefined";
1501
+ }
1502
+ function createDocxImportWorker() {
1503
+ return new Worker(new URL("./docx-import-worker.js", importMetaUrl), {
1504
+ type: "module",
1505
+ name: "react-docx-import"
1506
+ });
1507
+ }
1508
+ async function importDocxOnMainThread(buffer, signal) {
1509
+ if (signal?.aborted) {
1510
+ throw createAbortError();
1511
+ }
1512
+ const startedAt = performanceNow();
1513
+ const [{ parseDocx: parseDocx2 }, { buildDocModel: buildDocModel2 }] = await Promise.all([
1514
+ Promise.resolve().then(() => (init_src2(), src_exports)),
1515
+ Promise.resolve().then(() => (init_src3(), src_exports2))
1516
+ ]);
1517
+ const pkg = await parseDocx2(buffer);
1518
+ const parsedAt = performanceNow();
1519
+ if (signal?.aborted) {
1520
+ throw createAbortError();
1521
+ }
1522
+ const model = await buildDocModel2(pkg);
1523
+ const finishedAt = performanceNow();
1524
+ if (signal?.aborted) {
1525
+ throw createAbortError();
1526
+ }
1527
+ return {
1528
+ package: pkg,
1529
+ model,
1530
+ source: "main-thread",
1531
+ timings: {
1532
+ totalMs: finishedAt - startedAt,
1533
+ parseMs: parsedAt - startedAt,
1534
+ buildModelMs: finishedAt - parsedAt
1535
+ }
1536
+ };
1537
+ }
1538
+ function performanceNow() {
1539
+ return typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
1540
+ }
1541
+ async function importDocxBuffer(buffer, options = {}) {
1542
+ if (options.signal?.aborted) {
1543
+ throw createAbortError();
1544
+ }
1545
+ if (!canUseDocxImportWorker(options)) {
1546
+ return importDocxOnMainThread(buffer, options.signal);
1547
+ }
1548
+ let worker;
1549
+ try {
1550
+ worker = createDocxImportWorker();
1551
+ } catch {
1552
+ return importDocxOnMainThread(buffer, options.signal);
1553
+ }
1554
+ const requestId = nextImportWorkerRequestId;
1555
+ nextImportWorkerRequestId += 1;
1556
+ return new Promise((resolve, reject) => {
1557
+ let settled = false;
1558
+ const cleanup = () => {
1559
+ worker.removeEventListener("message", handleMessage);
1560
+ worker.removeEventListener("error", handleError2);
1561
+ worker.removeEventListener("messageerror", handleMessageError);
1562
+ options.signal?.removeEventListener("abort", handleAbort);
1563
+ worker.terminate();
1564
+ };
1565
+ const settle = (resolver) => {
1566
+ if (settled) {
1567
+ return;
1568
+ }
1569
+ settled = true;
1570
+ cleanup();
1571
+ resolver();
1572
+ };
1573
+ const handleAbort = () => {
1574
+ settle(() => reject(createAbortError()));
1575
+ };
1576
+ const handleError2 = (event) => {
1577
+ const message = event.message || "DOCX import worker failed";
1578
+ settle(() => reject(new Error(message)));
1579
+ };
1580
+ const handleMessageError = () => {
1581
+ settle(() => reject(new Error("DOCX import worker returned an unreadable response")));
1582
+ };
1583
+ const handleMessage = (event) => {
1584
+ const response = event.data;
1585
+ if (!response || response.id !== requestId) {
1586
+ return;
1587
+ }
1588
+ if (response.type === "error") {
1589
+ settle(() => reject(errorFromWorkerResponse(response)));
1590
+ return;
1591
+ }
1592
+ settle(
1593
+ () => resolve({
1594
+ package: response.package,
1595
+ model: response.model,
1596
+ source: "worker",
1597
+ timings: response.timings
1598
+ })
1599
+ );
1600
+ };
1601
+ worker.addEventListener("message", handleMessage);
1602
+ worker.addEventListener("error", handleError2);
1603
+ worker.addEventListener("messageerror", handleMessageError);
1604
+ options.signal?.addEventListener("abort", handleAbort, { once: true });
1605
+ try {
1606
+ const request = {
1607
+ id: requestId,
1608
+ type: "import-docx",
1609
+ buffer
1610
+ };
1611
+ const transfer = options.transferBuffer ? [buffer] : [];
1612
+ worker.postMessage(request, transfer);
1613
+ } catch (error) {
1614
+ settle(
1615
+ () => reject(error instanceof Error ? error : new Error("Failed to start DOCX import worker"))
1616
+ );
1617
+ }
1618
+ });
1619
+ }
1451
1620
 
1452
1621
  // src/section-layout.ts
1453
1622
  init_cjs_shims();
@@ -1819,9 +1988,11 @@ var React = __toESM(require("react"), 1);
1819
1988
  var import_react_dom = require("react-dom");
1820
1989
  var import_server = require("react-dom/server");
1821
1990
  var import_react_virtual = require("@tanstack/react-virtual");
1991
+ init_src3();
1822
1992
 
1823
1993
  // ../editor-ops/src/index.ts
1824
1994
  init_cjs_shims();
1995
+ init_src3();
1825
1996
  function paragraphFromText(text, options) {
1826
1997
  return {
1827
1998
  type: "paragraph",
@@ -2804,9 +2975,6 @@ function parseParagraphsFromClipboard(input) {
2804
2975
  }
2805
2976
  }
2806
2977
 
2807
- // src/editor.tsx
2808
- init_src2();
2809
-
2810
2978
  // ../serializer/src/index.ts
2811
2979
  init_cjs_shims();
2812
2980
  init_src();
@@ -3183,6 +3351,13 @@ function reconcilePageCountCandidateToTargetCountByScalingHeight(options) {
3183
3351
  previousScale = scale;
3184
3352
  previousPageCount = candidate.pageCount;
3185
3353
  }
3354
+ if (!needMorePages && selectedCandidate.pageCount !== safeTargetPageCount) {
3355
+ return {
3356
+ pageCount: initialPageCount,
3357
+ pages: initialPages,
3358
+ scale: 1
3359
+ };
3360
+ }
3186
3361
  return selectedCandidate;
3187
3362
  }
3188
3363
 
@@ -4457,6 +4632,20 @@ var SerialIdleTaskQueue = class {
4457
4632
  this.schedulePump();
4458
4633
  });
4459
4634
  }
4635
+ /** Drops queued work for a single key, resolving its waiters. */
4636
+ cancel(key) {
4637
+ const remaining = [];
4638
+ this.pending.forEach((entry) => {
4639
+ if (entry.key === key) {
4640
+ entry.resolvers.forEach((resolveEntry) => {
4641
+ resolveEntry();
4642
+ });
4643
+ return;
4644
+ }
4645
+ remaining.push(entry);
4646
+ });
4647
+ this.pending.splice(0, this.pending.length, ...remaining);
4648
+ }
4460
4649
  /** Drops all queued tasks, resolving their waiters without running them. */
4461
4650
  clear() {
4462
4651
  const dropped = this.pending.splice(0, this.pending.length);
@@ -4641,6 +4830,29 @@ var MEASURED_BODY_FOOTER_OVERLAP_STABILITY_THRESHOLD = 1;
4641
4830
  var WORD_TABLE_CELL_PARAGRAPH_AUTO_LINE_TWIPS = 240;
4642
4831
  var WORD_TABLE_CELL_PARAGRAPH_BEFORE_TWIPS = 0;
4643
4832
  var WORD_TABLE_CELL_PARAGRAPH_AFTER_TWIPS = 0;
4833
+ var DOCX_IMPORT_PERFORMANCE_PREFIX = "react-docx.import";
4834
+ function markDocxImportPerformance(name) {
4835
+ if (typeof performance === "undefined" || typeof performance.mark !== "function") {
4836
+ return;
4837
+ }
4838
+ try {
4839
+ performance.mark(name);
4840
+ } catch {
4841
+ }
4842
+ }
4843
+ function measureDocxImportPerformance(name, startMark, endMark) {
4844
+ if (typeof performance === "undefined" || typeof performance.measure !== "function") {
4845
+ return;
4846
+ }
4847
+ try {
4848
+ performance.measure(name, startMark, endMark);
4849
+ } catch {
4850
+ }
4851
+ }
4852
+ function createDocxImportPerformanceTraceName(fileName) {
4853
+ const normalizedName = fileName.replace(/[^a-z0-9._-]+/gi, "_").slice(0, 80);
4854
+ return `${DOCX_IMPORT_PERFORMANCE_PREFIX}.${Date.now()}.${normalizedName}`;
4855
+ }
4644
4856
  var TABLE_ROW_SLICE_VISUAL_BLEED_PX = 1;
4645
4857
  var TABLE_CELL_SLICE_FULLY_VISIBLE_BOTTOM_BUFFER_PX = 4;
4646
4858
  var DEFAULT_SPLIT_PARAGRAPH_LINE_TWIPS = 259;
@@ -4728,6 +4940,7 @@ var tableEstimatedRowHeightsByNode = /* @__PURE__ */ new WeakMap();
4728
4940
  var paragraphExplicitIndentBySourceXml = /* @__PURE__ */ new Map();
4729
4941
  var paragraphDropCapBySourceXml = /* @__PURE__ */ new Map();
4730
4942
  var paragraphTrackedMarkupBySourceXml = /* @__PURE__ */ new Map();
4943
+ var paragraphCommentMarkupBySourceXml = /* @__PURE__ */ new Map();
4731
4944
  var paragraphMeasureCanvasContext;
4732
4945
  var textWidthByFontAndValue = /* @__PURE__ */ new Map();
4733
4946
  var estimatedTextAdvanceWidthByFontAndValue = /* @__PURE__ */ new Map();
@@ -9729,9 +9942,7 @@ function estimateTabLeaderWrappedLineCountForParagraph(paragraph, maxLineWidthPx
9729
9942
  leadingSegments,
9730
9943
  paragraphBaseFontPx
9731
9944
  );
9732
- const tabStopPositionsPx = (paragraph.style?.tabStops ?? []).map((tabStop) => twipsToPixels(tabStop.positionTwips)).filter(
9733
- (positionPx) => Number.isFinite(positionPx) && positionPx > 0
9734
- ).sort((left, right) => left - right);
9945
+ const tabStopPositionsPx = resolveParagraphFirstLineLeftTabStopsPx(paragraph);
9735
9946
  const explicitLeadingTabStopPx = tableOfContentsLeadingLeftTabStopPx(paragraph);
9736
9947
  const leadingReservationWidthPx = leadingSegments.length === 0 ? 0 : Number.isFinite(explicitLeadingTabStopPx) && explicitLeadingTabStopPx > 0 ? Math.max(
9737
9948
  leadingTextWidthPx,
@@ -13597,10 +13808,7 @@ function tableOfContentsLeadingLeftTabStopPx(paragraph) {
13597
13808
  if (!isTableOfContentsParagraph(paragraph)) {
13598
13809
  return void 0;
13599
13810
  }
13600
- const leftTabStopPositionsPx = (paragraph.style?.tabStops ?? []).filter((tabStop) => tabStop.alignment === "left").map((tabStop) => twipsToPixels(tabStop.positionTwips)).filter(
13601
- (positionPx) => Number.isFinite(positionPx) && positionPx > 0
13602
- ).sort((left, right) => left - right);
13603
- return leftTabStopPositionsPx[0];
13811
+ return resolveParagraphFirstLineLeftTabStopsPx(paragraph)[0];
13604
13812
  }
13605
13813
  function paragraphContainsTabCharacter(paragraph) {
13606
13814
  return paragraph.children.some((child) => {
@@ -14650,6 +14858,81 @@ function resolveParagraphTrackedMarkup(paragraph) {
14650
14858
  setCacheEntry(paragraphTrackedMarkupBySourceXml, sourceXml, resolved);
14651
14859
  return resolved;
14652
14860
  }
14861
+ function resolveParagraphCommentMarkup(paragraph) {
14862
+ const sourceXml = paragraph.sourceXml ?? "";
14863
+ if (!sourceXml || !/commentRange|commentReference/i.test(sourceXml)) {
14864
+ return void 0;
14865
+ }
14866
+ const cached = paragraphCommentMarkupBySourceXml.get(sourceXml);
14867
+ if (cached !== void 0) {
14868
+ return cached ?? void 0;
14869
+ }
14870
+ const rangeStartById = /* @__PURE__ */ new Map();
14871
+ const rangeEndById = /* @__PURE__ */ new Map();
14872
+ for (const match of sourceXml.matchAll(
14873
+ /<w:commentRangeStart\b[^>]*w:id="(-?\d+)"[^>]*\/?>/gi
14874
+ )) {
14875
+ const commentId = Number.parseInt(match[1] ?? "", 10);
14876
+ if (Number.isFinite(commentId) && match.index !== void 0) {
14877
+ rangeStartById.set(commentId, match.index + match[0].length);
14878
+ }
14879
+ }
14880
+ for (const match of sourceXml.matchAll(
14881
+ /<w:commentRangeEnd\b[^>]*w:id="(-?\d+)"[^>]*\/?>/gi
14882
+ )) {
14883
+ const commentId = Number.parseInt(match[1] ?? "", 10);
14884
+ if (Number.isFinite(commentId) && match.index !== void 0) {
14885
+ rangeEndById.set(commentId, match.index);
14886
+ }
14887
+ }
14888
+ const ranges = [];
14889
+ const rangeIds = /* @__PURE__ */ new Set([
14890
+ ...rangeStartById.keys(),
14891
+ ...rangeEndById.keys()
14892
+ ]);
14893
+ rangeIds.forEach((commentId) => {
14894
+ const start = rangeStartById.get(commentId) ?? 0;
14895
+ const end = rangeEndById.get(commentId) ?? sourceXml.length;
14896
+ if (end > start) {
14897
+ ranges.push({ commentId, start, end });
14898
+ }
14899
+ });
14900
+ if (ranges.length === 0) {
14901
+ setCacheEntry(paragraphCommentMarkupBySourceXml, sourceXml, null);
14902
+ return void 0;
14903
+ }
14904
+ const commentIdsByVisibleChildIndex = [];
14905
+ let visibleChildIndex = 0;
14906
+ const runPattern = /<w:r\b[\s\S]*?<\/w:r>/gi;
14907
+ for (const runMatch of sourceXml.matchAll(runPattern)) {
14908
+ const runXml = runMatch[0] ?? "";
14909
+ if (!runXml) {
14910
+ continue;
14911
+ }
14912
+ const runStart = runMatch.index ?? 0;
14913
+ const contentRunXml = stripTextBoxContentFromRunXml(runXml);
14914
+ const visibleTokens = parseTrackedRunTokens(contentRunXml, false);
14915
+ const hasImage = /<w:(?:drawing|pict)\b/i.test(runXml);
14916
+ const visibleChildCount = visibleTokens.filter((token) => token.text.length > 0 || token.isNote).length + (hasImage ? 1 : 0);
14917
+ if (visibleChildCount === 0) {
14918
+ continue;
14919
+ }
14920
+ const activeCommentIds = ranges.filter((range) => runStart >= range.start && runStart < range.end).map((range) => range.commentId);
14921
+ if (activeCommentIds.length > 0) {
14922
+ for (let index = 0; index < visibleChildCount; index += 1) {
14923
+ commentIdsByVisibleChildIndex[visibleChildIndex + index] = activeCommentIds;
14924
+ }
14925
+ }
14926
+ visibleChildIndex += visibleChildCount;
14927
+ }
14928
+ if (commentIdsByVisibleChildIndex.length === 0) {
14929
+ setCacheEntry(paragraphCommentMarkupBySourceXml, sourceXml, null);
14930
+ return void 0;
14931
+ }
14932
+ const resolved = { commentIdsByVisibleChildIndex };
14933
+ setCacheEntry(paragraphCommentMarkupBySourceXml, sourceXml, resolved);
14934
+ return resolved;
14935
+ }
14653
14936
  function instructionTextToPageFieldKind(rawInstruction) {
14654
14937
  const normalized = decodeXmlText(rawInstruction).replace(/\s+/g, " ").trim().toUpperCase();
14655
14938
  if (!normalized || normalized.includes("PAGEREF")) {
@@ -14696,11 +14979,16 @@ function paragraphPageFieldSequence(paragraph) {
14696
14979
  }
14697
14980
  return fields;
14698
14981
  }
14982
+ var pageFieldValueSequenceBySourceXml = /* @__PURE__ */ new Map();
14699
14983
  function paragraphPageFieldValueSequence(paragraph) {
14700
14984
  const xml = paragraph.sourceXml ?? "";
14701
14985
  if (!xml) {
14702
14986
  return [];
14703
14987
  }
14988
+ const cached = pageFieldValueSequenceBySourceXml.get(xml);
14989
+ if (cached) {
14990
+ return cached;
14991
+ }
14704
14992
  const values = [];
14705
14993
  const fieldStack = [];
14706
14994
  const tokenPattern = /<w:fldSimple\b[^>]*\bw:instr="([^"]+)"[^>]*>[\s\S]*?<\/w:fldSimple>|<w:r\b[\s\S]*?<\/w:r>/gi;
@@ -14780,13 +15068,19 @@ function paragraphPageFieldValueSequence(paragraph) {
14780
15068
  fieldStack.pop();
14781
15069
  }
14782
15070
  }
15071
+ setCacheEntry(pageFieldValueSequenceBySourceXml, xml, values);
14783
15072
  return values;
14784
15073
  }
15074
+ var styleRefFieldValueSequenceBySourceXml = /* @__PURE__ */ new Map();
14785
15075
  function paragraphStyleRefFieldValueSequence(paragraph) {
14786
15076
  const xml = paragraph.sourceXml ?? "";
14787
15077
  if (!xml) {
14788
15078
  return [];
14789
15079
  }
15080
+ const cached = styleRefFieldValueSequenceBySourceXml.get(xml);
15081
+ if (cached) {
15082
+ return cached;
15083
+ }
14790
15084
  const values = [];
14791
15085
  const fieldStack = [];
14792
15086
  const tokenPattern = /<w:fldSimple\b[^>]*\bw:instr="([^"]+)"[^>]*>[\s\S]*?<\/w:fldSimple>|<w:r\b[\s\S]*?<\/w:r>/gi;
@@ -14866,6 +15160,7 @@ function paragraphStyleRefFieldValueSequence(paragraph) {
14866
15160
  fieldStack.pop();
14867
15161
  }
14868
15162
  }
15163
+ setCacheEntry(styleRefFieldValueSequenceBySourceXml, xml, values);
14869
15164
  return values;
14870
15165
  }
14871
15166
  function normalizeStyleRefTarget(value) {
@@ -15000,6 +15295,7 @@ function renderParagraphRuns(paragraph, keyPrefix, documentTheme = "light", numb
15000
15295
  const showTrackedChanges = options?.showTrackedChanges === true;
15001
15296
  const showTrackedInlineMarkup = showTrackedChanges && options?.trackedMarkupMode !== "gutter";
15002
15297
  const trackedMarkup = showTrackedInlineMarkup ? resolveParagraphTrackedMarkup(paragraph) : void 0;
15298
+ const commentMarkup = options?.showCommentHighlights === true ? resolveParagraphCommentMarkup(paragraph) : void 0;
15003
15299
  const tocParagraphLevel = tableOfContentsLevel(paragraph);
15004
15300
  const tocLinkColor = tocParagraphLevel ? options?.tocLinkColorByLevel?.[tocParagraphLevel] : void 0;
15005
15301
  const floatingAnchorOriginCorrectionXPx = Number.isFinite(
@@ -15041,8 +15337,19 @@ function renderParagraphRuns(paragraph, keyPrefix, documentTheme = "light", numb
15041
15337
  });
15042
15338
  };
15043
15339
  const currentTrackedInlineChange = () => trackedMarkup?.inlineChangeByVisibleChildIndex[trackedVisibleChildCursor];
15340
+ const currentCommentHighlightStyle = () => {
15341
+ const commentIds = commentMarkup?.commentIdsByVisibleChildIndex[trackedVisibleChildCursor];
15342
+ if (!commentIds || commentIds.length === 0) {
15343
+ return void 0;
15344
+ }
15345
+ const accent = commentAccentColor(documentTheme);
15346
+ return {
15347
+ backgroundColor: documentTheme === "dark" ? "rgba(251, 191, 36, 0.24)" : "rgba(251, 191, 36, 0.3)",
15348
+ borderBottom: `2px solid ${accent}`
15349
+ };
15350
+ };
15044
15351
  const consumeTrackedVisibleChild = (child) => {
15045
- if (!trackedMarkup) {
15352
+ if (!trackedMarkup && !commentMarkup) {
15046
15353
  return;
15047
15354
  }
15048
15355
  if (child.type === "form-field") {
@@ -15211,20 +15518,26 @@ function renderParagraphRuns(paragraph, keyPrefix, documentTheme = "light", numb
15211
15518
  const resolveFieldText = (value, preferredZone) => resolvePageFieldText(resolveStyleRefFieldText(value), preferredZone);
15212
15519
  const trackedLinkStyle = (style, trackedInlineChange) => {
15213
15520
  if (!isTableOfContentsParagraph(paragraph)) {
15214
- return trackedInlineStyle(
15215
- linkStyleToCss(style, documentTheme),
15216
- trackedInlineChange
15217
- );
15521
+ return {
15522
+ ...trackedInlineStyle(
15523
+ linkStyleToCss(style, documentTheme),
15524
+ trackedInlineChange
15525
+ ),
15526
+ ...currentCommentHighlightStyle()
15527
+ };
15218
15528
  }
15219
15529
  const base = runStyleToCss(style, documentTheme);
15220
- return trackedInlineStyle(
15221
- {
15222
- ...base,
15223
- color: tocLinkColor ? themedRunColor(tocLinkColor, documentTheme) : "inherit",
15224
- textDecoration: "none"
15225
- },
15226
- trackedInlineChange
15227
- );
15530
+ return {
15531
+ ...trackedInlineStyle(
15532
+ {
15533
+ ...base,
15534
+ color: tocLinkColor ? themedRunColor(tocLinkColor, documentTheme) : "inherit",
15535
+ textDecoration: "none"
15536
+ },
15537
+ trackedInlineChange
15538
+ ),
15539
+ ...currentCommentHighlightStyle()
15540
+ };
15228
15541
  };
15229
15542
  const usesExternalHorizontalAnchorOrigin = (image) => {
15230
15543
  const horizontalRelativeTo = image.floating?.horizontalRelativeTo?.trim().toLowerCase();
@@ -15271,10 +15584,13 @@ function renderParagraphRuns(paragraph, keyPrefix, documentTheme = "light", numb
15271
15584
  trackTextAdvance(text, child.style);
15272
15585
  return;
15273
15586
  }
15274
- const trackedStyle = trackedInlineStyle(
15275
- runStyleToCss(child.style, documentTheme),
15276
- trackedInlineChange
15277
- );
15587
+ const trackedStyle = {
15588
+ ...trackedInlineStyle(
15589
+ runStyleToCss(child.style, documentTheme),
15590
+ trackedInlineChange
15591
+ ),
15592
+ ...currentCommentHighlightStyle()
15593
+ };
15278
15594
  if (text === " " && !useTabLeaderLayout && !useAnchoredTabLayout) {
15279
15595
  target.push(
15280
15596
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: tabTextStyle(child.style, trackedStyle), children: "\xA0" }, key)
@@ -15530,10 +15846,13 @@ function renderParagraphRuns(paragraph, keyPrefix, documentTheme = "light", numb
15530
15846
  }
15531
15847
  return;
15532
15848
  }
15533
- const textStyle = trackedInlineStyle(
15534
- runStyleToCss(child.style, documentTheme),
15535
- trackedInlineChange
15536
- );
15849
+ const textStyle = {
15850
+ ...trackedInlineStyle(
15851
+ runStyleToCss(child.style, documentTheme),
15852
+ trackedInlineChange
15853
+ ),
15854
+ ...currentCommentHighlightStyle()
15855
+ };
15537
15856
  const noteLabel = noteMarkerLabel(
15538
15857
  child.noteReference,
15539
15858
  safeNoteMarkerIndexes.footnote,
@@ -16822,11 +17141,22 @@ function parseEmbeddedTableRuntimeKey(tableRuntimeKey) {
16822
17141
  descendants
16823
17142
  };
16824
17143
  }
17144
+ var columnWidthsByTable = /* @__PURE__ */ new WeakMap();
16825
17145
  function columnWidthsFromTableDefinition(table, columnCount) {
17146
+ const cachedByCount = columnWidthsByTable.get(table);
17147
+ if (cachedByCount?.has(columnCount)) {
17148
+ return cachedByCount.get(columnCount);
17149
+ }
17150
+ const resolved = computeColumnWidthsFromTableDefinition(table, columnCount);
17151
+ const cache = cachedByCount ?? /* @__PURE__ */ new Map();
17152
+ cache.set(columnCount, resolved);
17153
+ columnWidthsByTable.set(table, cache);
17154
+ return resolved;
17155
+ }
17156
+ function computeColumnWidthsFromTableDefinition(table, columnCount) {
16826
17157
  const gridWidths = table.style?.columnWidthsTwips;
16827
17158
  const rowDerivedWidths = deriveColumnWidthsFromTableRows(table, columnCount);
16828
17159
  if (gridWidths && gridWidths.length === columnCount) {
16829
- console.log("[colw]", columnCount, gridWidths.length, !!rowDerivedWidths, gridConflictsWithRowWidths(table, gridWidths));
16830
17160
  if (rowDerivedWidths && rowDerivedWidths.length > 0 && gridConflictsWithRowWidths(table, gridWidths)) {
16831
17161
  return rowDerivedWidths;
16832
17162
  }
@@ -18367,6 +18697,127 @@ function collectTrackedChangesFromModel(model) {
18367
18697
  });
18368
18698
  return trackedChanges;
18369
18699
  }
18700
+ function decodeCommentRangeText(rangeXml) {
18701
+ const texts = [];
18702
+ for (const match of rangeXml.matchAll(
18703
+ /<w:t\b[^>]*>([\s\S]*?)<\/w:t>/gi
18704
+ )) {
18705
+ texts.push(decodeXmlText(match[1] ?? ""));
18706
+ }
18707
+ const combined = texts.join("").replace(/\s+/g, " ").trim();
18708
+ if (!combined) {
18709
+ return void 0;
18710
+ }
18711
+ return combined.length > 120 ? `${combined.slice(0, 119)}\u2026` : combined;
18712
+ }
18713
+ function resolveCommentAnchorText(sourceXml, commentId) {
18714
+ const startMatch = sourceXml.match(
18715
+ new RegExp(`<w:commentRangeStart\\b[^>]*w:id="${commentId}"[^>]*/?>`, "i")
18716
+ );
18717
+ const endMatch = sourceXml.match(
18718
+ new RegExp(`<w:commentRangeEnd\\b[^>]*w:id="${commentId}"[^>]*/?>`, "i")
18719
+ );
18720
+ const startIndex = startMatch?.index !== void 0 ? startMatch.index + startMatch[0].length : (
18721
+ // Range opened in an earlier paragraph: take from the paragraph start.
18722
+ endMatch?.index !== void 0 ? 0 : void 0
18723
+ );
18724
+ if (startIndex === void 0) {
18725
+ return void 0;
18726
+ }
18727
+ const endIndex = endMatch?.index !== void 0 ? endMatch.index : sourceXml.length;
18728
+ if (endIndex <= startIndex) {
18729
+ return void 0;
18730
+ }
18731
+ return decodeCommentRangeText(sourceXml.slice(startIndex, endIndex));
18732
+ }
18733
+ function collectCommentsFromModel(model) {
18734
+ const definitions = model.metadata.comments ?? [];
18735
+ if (definitions.length === 0) {
18736
+ return [];
18737
+ }
18738
+ const definitionById = new Map(
18739
+ definitions.map((definition) => [definition.id, definition])
18740
+ );
18741
+ const comments = [];
18742
+ const appendParagraphComments = (paragraph, nodeIndex, location) => {
18743
+ const sourceXml = paragraph.sourceXml ?? "";
18744
+ if (!sourceXml || !/commentReference/i.test(sourceXml)) {
18745
+ return;
18746
+ }
18747
+ for (const match of sourceXml.matchAll(
18748
+ /<w:commentReference\b[^>]*w:id="(-?\d+)"/gi
18749
+ )) {
18750
+ const commentId = Number.parseInt(match[1] ?? "", 10);
18751
+ const definition = Number.isFinite(commentId) ? definitionById.get(commentId) : void 0;
18752
+ if (!definition) {
18753
+ continue;
18754
+ }
18755
+ comments.push({
18756
+ id: `${paragraphLocationKey(location)}:comment:${commentId}`,
18757
+ commentId,
18758
+ author: definition.author,
18759
+ initials: definition.initials,
18760
+ date: definition.date,
18761
+ text: definition.text,
18762
+ parentId: definition.parentId,
18763
+ resolved: definition.resolved,
18764
+ anchorText: resolveCommentAnchorText(sourceXml, commentId),
18765
+ nodeIndex,
18766
+ location: location.kind === "paragraph" ? { kind: "paragraph", nodeIndex: location.nodeIndex } : {
18767
+ kind: "table-cell",
18768
+ tableIndex: location.tableIndex,
18769
+ rowIndex: location.rowIndex,
18770
+ cellIndex: location.cellIndex,
18771
+ paragraphIndex: location.paragraphIndex
18772
+ }
18773
+ });
18774
+ }
18775
+ };
18776
+ model.nodes.forEach((node, nodeIndex) => {
18777
+ if (node.type === "paragraph") {
18778
+ appendParagraphComments(node, nodeIndex, {
18779
+ kind: "paragraph",
18780
+ nodeIndex
18781
+ });
18782
+ return;
18783
+ }
18784
+ node.rows.forEach((row, rowIndex) => {
18785
+ row.cells.forEach((cell, cellIndex) => {
18786
+ const directParagraphs = tableCellParagraphs(cell.nodes);
18787
+ directParagraphs.forEach((paragraph, paragraphIndex) => {
18788
+ appendParagraphComments(paragraph, nodeIndex, {
18789
+ kind: "table-cell",
18790
+ tableIndex: nodeIndex,
18791
+ rowIndex,
18792
+ cellIndex,
18793
+ paragraphIndex
18794
+ });
18795
+ });
18796
+ const nestedParagraphs = tableCellParagraphsRecursively(
18797
+ cell.nodes
18798
+ ).filter((paragraph) => !directParagraphs.includes(paragraph));
18799
+ nestedParagraphs.forEach((paragraph, nestedParagraphIndex) => {
18800
+ appendParagraphComments(paragraph, nodeIndex, {
18801
+ kind: "table-cell",
18802
+ tableIndex: nodeIndex,
18803
+ rowIndex,
18804
+ cellIndex,
18805
+ paragraphIndex: -(nestedParagraphIndex + 1)
18806
+ });
18807
+ });
18808
+ });
18809
+ });
18810
+ });
18811
+ return comments;
18812
+ }
18813
+ function commentAccentColor(documentTheme) {
18814
+ return documentTheme === "dark" ? "#fbbf24" : "#d97706";
18815
+ }
18816
+ function estimateCommentCardHeight(comment) {
18817
+ const snippet = comment.text || "Comment";
18818
+ const lines = Math.max(1, Math.ceil(snippet.length / 30));
18819
+ return Math.max(TRACKED_CHANGE_GUTTER_CARD_MIN_HEIGHT_PX, 34 + lines * 14);
18820
+ }
18370
18821
  function trackedChangeKindLabel(kind) {
18371
18822
  switch (kind) {
18372
18823
  case "insertion":
@@ -18415,15 +18866,15 @@ function trackedChangeAccentColor(kind, documentTheme) {
18415
18866
  return palette.format;
18416
18867
  }
18417
18868
  }
18418
- function trackedChangeSortTuple(change) {
18419
- if (change.location.kind === "paragraph") {
18420
- return [change.location.nodeIndex, 0, 0, 0];
18869
+ function gutterAnnotationSortTuple(location) {
18870
+ if (location.kind === "paragraph") {
18871
+ return [location.nodeIndex, 0, 0, 0];
18421
18872
  }
18422
18873
  return [
18423
- change.location.tableIndex,
18424
- change.location.rowIndex,
18425
- change.location.cellIndex,
18426
- change.location.paragraphIndex
18874
+ location.tableIndex,
18875
+ location.rowIndex,
18876
+ location.cellIndex,
18877
+ location.paragraphIndex
18427
18878
  ];
18428
18879
  }
18429
18880
  function trackedChangeBelongsToPageSegments(location, pageSegments) {
@@ -18442,10 +18893,10 @@ function trackedChangeBelongsToPageSegments(location, pageSegments) {
18442
18893
  return location.rowIndex >= segment.tableRowRange.startRowIndex && location.rowIndex < segment.tableRowRange.endRowIndex;
18443
18894
  });
18444
18895
  }
18445
- function resolveTrackedChangePageIndex(change, pageNodeSegmentsByPage) {
18896
+ function resolveGutterAnnotationPageIndex(location, pageNodeSegmentsByPage) {
18446
18897
  for (let pageIndex = 0; pageIndex < pageNodeSegmentsByPage.length; pageIndex += 1) {
18447
18898
  if (trackedChangeBelongsToPageSegments(
18448
- change.location,
18899
+ location,
18449
18900
  pageNodeSegmentsByPage[pageIndex] ?? []
18450
18901
  )) {
18451
18902
  return pageIndex;
@@ -18500,30 +18951,30 @@ function estimateTrackedChangeCardHeight(change) {
18500
18951
  const lines = Math.max(1, Math.ceil(snippet.length / 30));
18501
18952
  return Math.max(TRACKED_CHANGE_GUTTER_CARD_MIN_HEIGHT_PX, 34 + lines * 14);
18502
18953
  }
18503
- function layoutTrackedChangesForPage(changes, anchorByChangeId, cardHeightsByChangeId, pageWidthPx, pageHeightPx) {
18504
- if (changes.length === 0) {
18954
+ function layoutTrackedChangesForPage(annotations, anchorByChangeId, cardHeightsByChangeId, pageWidthPx, pageHeightPx) {
18955
+ if (annotations.length === 0) {
18505
18956
  return [];
18506
18957
  }
18507
18958
  const fallbackStride = Math.max(
18508
18959
  18,
18509
- pageHeightPx / Math.max(1, changes.length + 1)
18960
+ pageHeightPx / Math.max(1, annotations.length + 1)
18510
18961
  );
18511
- const withAnchors = changes.map((change, index) => {
18962
+ const withAnchors = annotations.map((annotation, index) => {
18512
18963
  const defaultAnchor = Math.min(
18513
18964
  Math.max(10, Math.round((index + 1) * fallbackStride)),
18514
18965
  Math.max(10, pageHeightPx - 10)
18515
18966
  );
18516
- const anchorPoint = anchorByChangeId.get(change.id);
18967
+ const anchorPoint = anchorByChangeId.get(annotation.id);
18517
18968
  const anchorY = anchorPoint?.y ?? defaultAnchor;
18518
18969
  const anchorX = anchorPoint?.x ?? Math.max(10, pageWidthPx - 10);
18519
- const measuredHeightPx = cardHeightsByChangeId?.get(change.id);
18520
- const estimatedHeightPx = estimateTrackedChangeCardHeight(change);
18970
+ const measuredHeightPx = cardHeightsByChangeId?.get(annotation.id);
18971
+ const estimatedHeightPx = annotation.trackedChange ? estimateTrackedChangeCardHeight(annotation.trackedChange) : annotation.comment ? estimateCommentCardHeight(annotation.comment) : TRACKED_CHANGE_GUTTER_CARD_MIN_HEIGHT_PX;
18521
18972
  const heightPx = Number.isFinite(measuredHeightPx) && measuredHeightPx > 0 ? Math.max(
18522
18973
  TRACKED_CHANGE_GUTTER_CARD_MIN_HEIGHT_PX,
18523
18974
  Math.round(measuredHeightPx)
18524
18975
  ) : estimatedHeightPx;
18525
18976
  return {
18526
- change,
18977
+ annotation,
18527
18978
  anchorX: clampNumber(
18528
18979
  Math.round(anchorX),
18529
18980
  10,
@@ -19554,10 +20005,14 @@ function useDocxEditor(options = {}) {
19554
20005
  const [isImporting, setIsImporting] = React.useState(false);
19555
20006
  const [documentTheme, setDocumentThemeState] = React.useState(options.initialDocumentTheme ?? "light");
19556
20007
  const [showTrackedChanges, setShowTrackedChangesState] = React.useState(options.initialShowTrackedChanges ?? false);
20008
+ const [showComments, setShowCommentsState] = React.useState(
20009
+ options.initialShowComments ?? false
20010
+ );
19557
20011
  const [paginationInfo, setPaginationInfo] = React.useState({
19558
20012
  currentPage: 1,
19559
20013
  totalPages: 1
19560
20014
  });
20015
+ const activeImportAbortControllerRef = React.useRef(void 0);
19561
20016
  const [history, setHistory] = React.useState({
19562
20017
  past: [],
19563
20018
  future: []
@@ -19870,6 +20325,9 @@ function useDocxEditor(options = {}) {
19870
20325
  () => collectTrackedChangesFromModel(model),
19871
20326
  [model]
19872
20327
  );
20328
+ const comments = React.useMemo(() => collectCommentsFromModel(model), [
20329
+ model
20330
+ ]);
19873
20331
  const hasUnorderedList = selectedListType === "unordered";
19874
20332
  const hasOrderedList = selectedListType === "ordered";
19875
20333
  const canUndo = history.past.length > 0;
@@ -19890,6 +20348,15 @@ function useDocxEditor(options = {}) {
19890
20348
  const toggleShowTrackedChanges = React.useCallback(() => {
19891
20349
  setShowTrackedChangesState((current) => !current);
19892
20350
  }, []);
20351
+ const setShowComments = React.useCallback(
20352
+ (nextShowComments) => {
20353
+ setShowCommentsState(nextShowComments);
20354
+ },
20355
+ []
20356
+ );
20357
+ const toggleShowComments = React.useCallback(() => {
20358
+ setShowCommentsState((current) => !current);
20359
+ }, []);
19893
20360
  const registerPendingExportModelTransformer = React.useCallback(
19894
20361
  (transformer) => {
19895
20362
  pendingExportModelTransformerRef.current = transformer;
@@ -19953,6 +20420,7 @@ function useDocxEditor(options = {}) {
19953
20420
  );
19954
20421
  React.useEffect(() => {
19955
20422
  return () => {
20423
+ activeImportAbortControllerRef.current?.abort();
19956
20424
  unloadEmbeddedFonts();
19957
20425
  };
19958
20426
  }, [unloadEmbeddedFonts]);
@@ -20088,6 +20556,8 @@ function useDocxEditor(options = {}) {
20088
20556
  );
20089
20557
  const importDocxFile = React.useCallback(
20090
20558
  async (file) => {
20559
+ activeImportAbortControllerRef.current?.abort();
20560
+ activeImportAbortControllerRef.current = void 0;
20091
20561
  if (!/\.docx?$/i.test(file.name)) {
20092
20562
  replaceDocumentWithImportError(
20093
20563
  file.name,
@@ -20098,11 +20568,50 @@ function useDocxEditor(options = {}) {
20098
20568
  setIsImporting(true);
20099
20569
  setImportError(void 0);
20100
20570
  setStatus(`Loading ${file.name}...`);
20571
+ const importAbortController = new AbortController();
20572
+ activeImportAbortControllerRef.current = importAbortController;
20573
+ const traceName = createDocxImportPerformanceTraceName(file.name);
20574
+ const startMark = `${traceName}:start`;
20575
+ const bufferStartMark = `${traceName}:arrayBuffer:start`;
20576
+ const bufferEndMark = `${traceName}:arrayBuffer:end`;
20577
+ const workerStartMark = `${traceName}:worker:start`;
20578
+ const workerEndMark = `${traceName}:worker:end`;
20579
+ const fontsStartMark = `${traceName}:fonts:start`;
20580
+ const fontsEndMark = `${traceName}:fonts:end`;
20581
+ const stateStartMark = `${traceName}:state:start`;
20582
+ const stateEndMark = `${traceName}:state:end`;
20583
+ markDocxImportPerformance(startMark);
20101
20584
  try {
20585
+ markDocxImportPerformance(bufferStartMark);
20102
20586
  const buffer = await file.arrayBuffer();
20103
- const pkg = await parseDocx(buffer);
20587
+ markDocxImportPerformance(bufferEndMark);
20588
+ measureDocxImportPerformance(
20589
+ `${traceName}:arrayBuffer`,
20590
+ bufferStartMark,
20591
+ bufferEndMark
20592
+ );
20593
+ markDocxImportPerformance(workerStartMark);
20594
+ const importResult = await importDocxBuffer(buffer, {
20595
+ signal: importAbortController.signal,
20596
+ transferBuffer: true
20597
+ });
20598
+ markDocxImportPerformance(workerEndMark);
20599
+ measureDocxImportPerformance(
20600
+ `${traceName}:${importResult.source}`,
20601
+ workerStartMark,
20602
+ workerEndMark
20603
+ );
20604
+ const pkg = importResult.package;
20605
+ const nextModel = importResult.model;
20606
+ markDocxImportPerformance(fontsStartMark);
20104
20607
  await loadEmbeddedFontsFromPackage(pkg);
20105
- const nextModel = await buildDocModel(pkg);
20608
+ markDocxImportPerformance(fontsEndMark);
20609
+ measureDocxImportPerformance(
20610
+ `${traceName}:fonts`,
20611
+ fontsStartMark,
20612
+ fontsEndMark
20613
+ );
20614
+ markDocxImportPerformance(stateStartMark);
20106
20615
  setModel(nextModel);
20107
20616
  setDocumentLoadNonce((current) => current + 1);
20108
20617
  setHistory({ past: [], future: [] });
@@ -20115,16 +20624,31 @@ function useDocxEditor(options = {}) {
20115
20624
  setSelectedFormFieldLocation(void 0);
20116
20625
  setImportError(void 0);
20117
20626
  setStatus(`Loaded ${file.name}`);
20627
+ markDocxImportPerformance(stateEndMark);
20628
+ measureDocxImportPerformance(
20629
+ `${traceName}:state-dispatch`,
20630
+ stateStartMark,
20631
+ stateEndMark
20632
+ );
20633
+ measureDocxImportPerformance(`${traceName}:total`, startMark, stateEndMark);
20118
20634
  } catch (error) {
20635
+ if (error instanceof Error && error.name === "AbortError" && activeImportAbortControllerRef.current !== importAbortController) {
20636
+ return;
20637
+ }
20119
20638
  const nextError = error instanceof Error ? error : new Error("Unknown error");
20120
20639
  replaceDocumentWithImportError(file.name, nextError);
20121
20640
  } finally {
20122
- setIsImporting(false);
20641
+ if (activeImportAbortControllerRef.current === importAbortController) {
20642
+ activeImportAbortControllerRef.current = void 0;
20643
+ setIsImporting(false);
20644
+ }
20123
20645
  }
20124
20646
  },
20125
20647
  [loadEmbeddedFontsFromPackage, replaceDocumentWithImportError]
20126
20648
  );
20127
20649
  const newDocument = React.useCallback(() => {
20650
+ activeImportAbortControllerRef.current?.abort();
20651
+ activeImportAbortControllerRef.current = void 0;
20128
20652
  unloadEmbeddedFonts();
20129
20653
  setModel(cloneDocModel(starterTemplateRef.current));
20130
20654
  setDocumentLoadNonce((current) => current + 1);
@@ -23050,6 +23574,8 @@ function useDocxEditor(options = {}) {
23050
23574
  documentTheme,
23051
23575
  trackedChanges,
23052
23576
  showTrackedChanges,
23577
+ comments,
23578
+ showComments,
23053
23579
  currentPage: paginationInfo.currentPage,
23054
23580
  totalPages: paginationInfo.totalPages,
23055
23581
  selection,
@@ -23077,8 +23603,10 @@ function useDocxEditor(options = {}) {
23077
23603
  setStatus,
23078
23604
  setDocumentTheme,
23079
23605
  setShowTrackedChanges,
23606
+ setShowComments,
23080
23607
  syncPaginationInfo,
23081
23608
  toggleShowTrackedChanges,
23609
+ toggleShowComments,
23082
23610
  importDocxFile,
23083
23611
  newDocument,
23084
23612
  exportDocx,
@@ -23365,6 +23893,7 @@ function useDocxPageThumbnails(editor, options = {}) {
23365
23893
  if (!targetCanvas) {
23366
23894
  return;
23367
23895
  }
23896
+ const requiresAttachedTarget = canvas === void 0;
23368
23897
  const pageElement = pageSurfaceRegistry.pageElements.get(pageIndex);
23369
23898
  if (!pageElement || !pageElement.isConnected) {
23370
23899
  updatePageThumbnailState(pageIndex, "unavailable");
@@ -23378,6 +23907,9 @@ function useDocxPageThumbnails(editor, options = {}) {
23378
23907
  }
23379
23908
  updatePageThumbnailState(pageIndex, "rendering");
23380
23909
  await ensureThumbnailRasterQueue().enqueue(targetCanvas, async () => {
23910
+ if (requiresAttachedTarget && attachedCanvasByPageRef.current.get(pageIndex) !== targetCanvas) {
23911
+ return;
23912
+ }
23381
23913
  const livePageElement = pageSurfaceRegistry.pageElements.get(pageIndex);
23382
23914
  if (!livePageElement || !livePageElement.isConnected) {
23383
23915
  updatePageThumbnailState(pageIndex, "unavailable");
@@ -23524,6 +24056,10 @@ function useDocxPageThumbnails(editor, options = {}) {
23524
24056
  void renderPageThumbnailToCanvasRef.current(pageIndex, canvas);
23525
24057
  return;
23526
24058
  }
24059
+ const previousCanvas = attachedCanvasByPageRef.current.get(pageIndex);
24060
+ if (previousCanvas) {
24061
+ thumbnailRasterQueueRef.current?.cancel(previousCanvas);
24062
+ }
23527
24063
  attachedCanvasByPageRef.current.delete(pageIndex);
23528
24064
  };
23529
24065
  canvasRefCallbacksRef.current.set(pageIndex, nextCanvasRef);
@@ -23791,6 +24327,42 @@ function useDocxTrackChanges(editor) {
23791
24327
  ]
23792
24328
  );
23793
24329
  }
24330
+ function useDocxComments(editor) {
24331
+ const commentsByLocation = React.useMemo(() => {
24332
+ const grouped = /* @__PURE__ */ new Map();
24333
+ editor.comments.forEach((comment) => {
24334
+ const key = paragraphLocationKey(comment.location);
24335
+ const bucket = grouped.get(key) ?? [];
24336
+ bucket.push(comment);
24337
+ grouped.set(key, bucket);
24338
+ });
24339
+ return grouped;
24340
+ }, [editor.comments]);
24341
+ const getCommentsForLocation = React.useCallback(
24342
+ (location) => {
24343
+ return commentsByLocation.get(paragraphLocationKey(location)) ?? [];
24344
+ },
24345
+ [commentsByLocation]
24346
+ );
24347
+ return React.useMemo(
24348
+ () => ({
24349
+ comments: editor.comments,
24350
+ showComments: editor.showComments,
24351
+ setShowComments: editor.setShowComments,
24352
+ toggleShowComments: editor.toggleShowComments,
24353
+ commentsByLocation,
24354
+ getCommentsForLocation
24355
+ }),
24356
+ [
24357
+ editor.comments,
24358
+ editor.showComments,
24359
+ editor.setShowComments,
24360
+ editor.toggleShowComments,
24361
+ commentsByLocation,
24362
+ getCommentsForLocation
24363
+ ]
24364
+ );
24365
+ }
23794
24366
  function useDocxPageLayout(editor) {
23795
24367
  const primarySectionPropertiesXml = editor.model.metadata.sections?.[0]?.sectionPropertiesXml ?? editor.model.metadata.sectionPropertiesXml;
23796
24368
  const layout = React.useMemo(() => {
@@ -24663,6 +25235,8 @@ function DocxEditorViewer({
24663
25235
  headingStyles,
24664
25236
  showTrackedChanges,
24665
25237
  renderTrackedChangeCard,
25238
+ showComments,
25239
+ renderCommentCard,
24666
25240
  renderTableContextMenu,
24667
25241
  renderContextMenu,
24668
25242
  onFormFieldDoubleClick,
@@ -24671,7 +25245,9 @@ function DocxEditorViewer({
24671
25245
  const pageSurfaceRegistryOwner = docxViewerPageSurfaceRegistryOwner(editor);
24672
25246
  const trackedChangesEnabled = showTrackedChanges ?? editor.showTrackedChanges;
24673
25247
  const hasTrackedChanges = editor.trackedChanges.length > 0;
24674
- const showTrackedChangeGutter = trackedChangesEnabled;
25248
+ const commentsEnabled = showComments ?? editor.showComments;
25249
+ const hasComments = editor.comments.length > 0;
25250
+ const showTrackedChangeGutter = trackedChangesEnabled || commentsEnabled;
24675
25251
  const isReadOnly = mode === "read-only" || trackedChangesEnabled;
24676
25252
  const isNightReaderMode = isReadOnly && editor.documentTheme === "dark";
24677
25253
  const documentContentTheme = isNightReaderMode ? "light" : editor.documentTheme;
@@ -24690,11 +25266,13 @@ function DocxEditorViewer({
24690
25266
  const paragraphRunRenderOptions = React.useMemo(
24691
25267
  () => ({
24692
25268
  showTrackedChanges: trackedChangesEnabled,
25269
+ showCommentHighlights: commentsEnabled,
24693
25270
  numberingDefinitions: editor.model.metadata.numberingDefinitions,
24694
25271
  tocLinkColorByLevel,
24695
25272
  imageFilterSuffix: documentContentFilter
24696
25273
  }),
24697
25274
  [
25275
+ commentsEnabled,
24698
25276
  documentContentFilter,
24699
25277
  editor.model.metadata.numberingDefinitions,
24700
25278
  tocLinkColorByLevel,
@@ -24737,7 +25315,6 @@ function DocxEditorViewer({
24737
25315
  /* @__PURE__ */ new Map()
24738
25316
  );
24739
25317
  const pageElementsRef = React.useRef(/* @__PURE__ */ new Map());
24740
- const pagePlaceholderRefCallbacksRef = React.useRef(/* @__PURE__ */ new Map());
24741
25318
  const pageSurfaceRefCallbacksRef = React.useRef(/* @__PURE__ */ new Map());
24742
25319
  const trackedChangeCardElementsRef = React.useRef(/* @__PURE__ */ new Map());
24743
25320
  const initialPaginationStableSignatureRef = React.useRef(
@@ -24805,26 +25382,6 @@ function DocxEditorViewer({
24805
25382
  measuredPageContentHeightByIndex,
24806
25383
  setMeasuredPageContentHeightByIndex
24807
25384
  ] = React.useState([]);
24808
- const pagePlaceholderRefForIndex = React.useCallback(
24809
- (pageIndex) => {
24810
- const normalizedPageIndex = Math.max(0, Math.round(pageIndex));
24811
- const cached = pagePlaceholderRefCallbacksRef.current.get(normalizedPageIndex);
24812
- if (cached) {
24813
- return cached;
24814
- }
24815
- const nextRef = (element) => {
24816
- if (element) {
24817
- pageElementsRef.current.set(normalizedPageIndex, element);
24818
- } else {
24819
- pageElementsRef.current.delete(normalizedPageIndex);
24820
- }
24821
- registerDocxViewerPageSurface(editor, normalizedPageIndex, void 0);
24822
- };
24823
- pagePlaceholderRefCallbacksRef.current.set(normalizedPageIndex, nextRef);
24824
- return nextRef;
24825
- },
24826
- [pageSurfaceRegistryOwner]
24827
- );
24828
25385
  const pageSurfaceRefForIndex = React.useCallback(
24829
25386
  (pageIndex) => {
24830
25387
  const normalizedPageIndex = Math.max(0, Math.round(pageIndex));
@@ -24846,7 +25403,6 @@ function DocxEditorViewer({
24846
25403
  [pageSurfaceRegistryOwner]
24847
25404
  );
24848
25405
  React.useEffect(() => {
24849
- pagePlaceholderRefCallbacksRef.current.clear();
24850
25406
  pageSurfaceRefCallbacksRef.current.clear();
24851
25407
  }, [pageSurfaceRegistryOwner]);
24852
25408
  const [
@@ -26120,6 +26676,26 @@ function DocxEditorViewer({
26120
26676
  nearestScrollableAncestor(viewerRootRef.current)
26121
26677
  );
26122
26678
  }, [editor.documentLoadNonce, pageCount, trackedChangesEnabled]);
26679
+ const [zoomProbeNonce, setZoomProbeNonce] = React.useState(0);
26680
+ React.useEffect(() => {
26681
+ if (typeof window === "undefined") {
26682
+ return;
26683
+ }
26684
+ const rootElement = viewerRootRef.current;
26685
+ if (!rootElement) {
26686
+ return;
26687
+ }
26688
+ const bumpZoomProbe = () => {
26689
+ setZoomProbeNonce((nonce) => nonce + 1);
26690
+ };
26691
+ const resizeObserver = typeof ResizeObserver === "function" ? new ResizeObserver(bumpZoomProbe) : void 0;
26692
+ resizeObserver?.observe(rootElement);
26693
+ window.addEventListener("resize", bumpZoomProbe);
26694
+ return () => {
26695
+ resizeObserver?.disconnect();
26696
+ window.removeEventListener("resize", bumpZoomProbe);
26697
+ };
26698
+ }, [editor.documentLoadNonce]);
26123
26699
  React.useLayoutEffect(() => {
26124
26700
  if (typeof window === "undefined") {
26125
26701
  return;
@@ -26132,7 +26708,7 @@ function DocxEditorViewer({
26132
26708
  setVirtualizerMeasurementScale(
26133
26709
  (current) => Math.abs(current - nextScale) < 5e-3 ? current : nextScale
26134
26710
  );
26135
- });
26711
+ }, [editor.documentLoadNonce, pageCount, zoomProbeNonce]);
26136
26712
  const internalPageVirtualizationEnabled = internalPageVirtualizationRequested && internalVirtualScrollElement !== null;
26137
26713
  const internalPageVirtualizationPending = typeof window !== "undefined" && internalPageVirtualizationRequested && internalVirtualScrollElement === null;
26138
26714
  const internalVirtualScrollUsesWindow = typeof document !== "undefined" && internalVirtualScrollElement !== null && (internalVirtualScrollElement === document.scrollingElement || internalVirtualScrollElement === document.documentElement || internalVirtualScrollElement === document.body);
@@ -26175,6 +26751,30 @@ function DocxEditorViewer({
26175
26751
  internalWindowPageVirtualizer
26176
26752
  ]);
26177
26753
  const internalVirtualItems = internalPageVirtualizer.getVirtualItems();
26754
+ const internalMostVisiblePageIndex = React.useMemo(() => {
26755
+ if (!internalPageVirtualizationEnabled || internalVirtualItems.length === 0) {
26756
+ return void 0;
26757
+ }
26758
+ const virtualizerMetrics = internalPageVirtualizer;
26759
+ const viewportStart = Number.isFinite(virtualizerMetrics.scrollOffset) ? virtualizerMetrics.scrollOffset : internalVirtualItems[0]?.start ?? 0;
26760
+ const viewportSize = virtualizerMetrics.scrollRect?.height;
26761
+ const viewportEnd = viewportStart + (Number.isFinite(viewportSize) && viewportSize > 0 ? viewportSize : internalVirtualItems[0]?.size ?? 0);
26762
+ let bestPageIndex;
26763
+ let bestVisibleSize = -1;
26764
+ internalVirtualItems.forEach((item) => {
26765
+ const visibleSize = Math.min(item.end, viewportEnd) - Math.max(item.start, viewportStart);
26766
+ if (visibleSize > bestVisibleSize) {
26767
+ bestVisibleSize = visibleSize;
26768
+ bestPageIndex = item.index;
26769
+ }
26770
+ });
26771
+ return bestPageIndex === void 0 ? void 0 : clampNumber(bestPageIndex, 0, pageCount - 1);
26772
+ }, [
26773
+ internalPageVirtualizationEnabled,
26774
+ internalPageVirtualizer,
26775
+ internalVirtualItems,
26776
+ pageCount
26777
+ ]);
26178
26778
  const internalVisiblePageRange = React.useMemo(() => {
26179
26779
  if (!internalPageVirtualizationEnabled) {
26180
26780
  return void 0;
@@ -26465,6 +27065,40 @@ function DocxEditorViewer({
26465
27065
  }
26466
27066
  return indexes;
26467
27067
  }, [pageCount, visiblePageEndIndex, visiblePageStartIndex]);
27068
+ const pageStackVirtualSpacers = React.useMemo(() => {
27069
+ const pageWrapperWidthPxForIndex = (pageIndex) => {
27070
+ const pageLayout = pageSectionInfoByIndex[pageIndex]?.layout ?? documentLayout;
27071
+ return showTrackedChangeGutter ? pageLayout.pageWidthPx + TRACKED_CHANGE_GUTTER_WIDTH_PX : pageLayout.pageWidthPx;
27072
+ };
27073
+ const summarizeSkippedPages = (startPageIndex, endPageIndex) => {
27074
+ if (pageCount <= 0 || endPageIndex < startPageIndex || startPageIndex >= pageCount || endPageIndex < 0) {
27075
+ return { heightPx: 0, widthPx: 0 };
27076
+ }
27077
+ const start = clampNumber(startPageIndex, 0, pageCount - 1);
27078
+ const end = clampNumber(endPageIndex, start, pageCount - 1);
27079
+ let heightPx = 0;
27080
+ let widthPx = 0;
27081
+ for (let pageIndex = start; pageIndex <= end; pageIndex += 1) {
27082
+ const pageLayout = pageSectionInfoByIndex[pageIndex]?.layout ?? documentLayout;
27083
+ heightPx += Math.max(1, Math.round(pageLayout.pageHeightPx));
27084
+ widthPx = Math.max(widthPx, pageWrapperWidthPxForIndex(pageIndex));
27085
+ }
27086
+ const skippedPageCount = end - start + 1;
27087
+ heightPx += Math.max(0, skippedPageCount - 1) * DOC_PAGE_BREAK_GAP;
27088
+ return { heightPx, widthPx };
27089
+ };
27090
+ return {
27091
+ before: summarizeSkippedPages(0, visiblePageStartIndex - 1),
27092
+ after: summarizeSkippedPages(visiblePageEndIndex + 1, pageCount - 1)
27093
+ };
27094
+ }, [
27095
+ documentLayout,
27096
+ pageCount,
27097
+ pageSectionInfoByIndex,
27098
+ showTrackedChangeGutter,
27099
+ visiblePageEndIndex,
27100
+ visiblePageStartIndex
27101
+ ]);
26468
27102
  React.useLayoutEffect(() => {
26469
27103
  if (typeof window === "undefined") {
26470
27104
  return;
@@ -26624,7 +27258,7 @@ function DocxEditorViewer({
26624
27258
  }, [onPageCountChange, stableReportedPageCount]);
26625
27259
  React.useEffect(() => {
26626
27260
  const nextCurrentPage = clampNumber(
26627
- visiblePageStartIndex + 1,
27261
+ (internalMostVisiblePageIndex ?? visiblePageStartIndex) + 1,
26628
27262
  1,
26629
27263
  Math.max(1, stableReportedPageCount)
26630
27264
  );
@@ -26634,6 +27268,7 @@ function DocxEditorViewer({
26634
27268
  });
26635
27269
  }, [
26636
27270
  editor.syncPaginationInfo,
27271
+ internalMostVisiblePageIndex,
26637
27272
  stableReportedPageCount,
26638
27273
  visiblePageStartIndex
26639
27274
  ]);
@@ -26641,20 +27276,38 @@ function DocxEditorViewer({
26641
27276
  const pageBuckets = pageNodeSegmentsByPage.map(
26642
27277
  () => []
26643
27278
  );
26644
- editor.trackedChanges.forEach((change) => {
26645
- const pageIndex = resolveTrackedChangePageIndex(
26646
- change,
27279
+ const placeAnnotation = (annotation) => {
27280
+ const pageIndex = resolveGutterAnnotationPageIndex(
27281
+ annotation.location,
26647
27282
  pageNodeSegmentsByPage
26648
27283
  );
26649
27284
  if (pageIndex < 0 || pageIndex >= pageBuckets.length) {
26650
27285
  return;
26651
27286
  }
26652
- pageBuckets[pageIndex].push(change);
26653
- });
26654
- pageBuckets.forEach((pageChanges) => {
26655
- pageChanges.sort((left, right) => {
26656
- const leftKey = trackedChangeSortTuple(left);
26657
- const rightKey = trackedChangeSortTuple(right);
27287
+ pageBuckets[pageIndex].push(annotation);
27288
+ };
27289
+ if (trackedChangesEnabled) {
27290
+ editor.trackedChanges.forEach((change) => {
27291
+ placeAnnotation({
27292
+ id: change.id,
27293
+ location: change.location,
27294
+ trackedChange: change
27295
+ });
27296
+ });
27297
+ }
27298
+ if (commentsEnabled) {
27299
+ editor.comments.forEach((comment) => {
27300
+ placeAnnotation({
27301
+ id: comment.id,
27302
+ location: comment.location,
27303
+ comment
27304
+ });
27305
+ });
27306
+ }
27307
+ pageBuckets.forEach((pageAnnotations) => {
27308
+ pageAnnotations.sort((left, right) => {
27309
+ const leftKey = gutterAnnotationSortTuple(left.location);
27310
+ const rightKey = gutterAnnotationSortTuple(right.location);
26658
27311
  for (let index = 0; index < leftKey.length; index += 1) {
26659
27312
  if (leftKey[index] === rightKey[index]) {
26660
27313
  continue;
@@ -26665,7 +27318,13 @@ function DocxEditorViewer({
26665
27318
  });
26666
27319
  });
26667
27320
  return pageBuckets;
26668
- }, [editor.trackedChanges, pageNodeSegmentsByPage]);
27321
+ }, [
27322
+ commentsEnabled,
27323
+ editor.comments,
27324
+ editor.trackedChanges,
27325
+ pageNodeSegmentsByPage,
27326
+ trackedChangesEnabled
27327
+ ]);
26669
27328
  const [trackedChangeAnchorByPage, setTrackedChangeAnchorByPage] = React.useState([]);
26670
27329
  const [headerBodyClearanceByPage, setHeaderBodyClearanceByPage] = React.useState({});
26671
27330
  React.useEffect(() => {
@@ -26679,18 +27338,18 @@ function DocxEditorViewer({
26679
27338
  );
26680
27339
  return;
26681
27340
  }
26682
- const nextAnchorMaps = trackedChangesByPage.map((changes, pageIndex) => {
27341
+ const nextAnchorMaps = trackedChangesByPage.map((annotations, pageIndex) => {
26683
27342
  const pageElement = pageElementsRef.current.get(pageIndex);
26684
27343
  const anchorsByChangeId = /* @__PURE__ */ new Map();
26685
- if (!pageElement || changes.length === 0) {
27344
+ if (!pageElement || annotations.length === 0) {
26686
27345
  return anchorsByChangeId;
26687
27346
  }
26688
27347
  const pageHeightPx = Math.max(1, pageElement.offsetHeight);
26689
27348
  const pageWidthPx = Math.max(1, pageElement.offsetWidth);
26690
- changes.forEach((change) => {
27349
+ annotations.forEach((annotation) => {
26691
27350
  const anchorElement = findTrackedChangeAnchorElementInPage(
26692
27351
  pageElement,
26693
- change.location
27352
+ annotation.location
26694
27353
  );
26695
27354
  if (!anchorElement) {
26696
27355
  return;
@@ -26714,7 +27373,7 @@ function DocxEditorViewer({
26714
27373
  10,
26715
27374
  Math.max(10, pageWidthPx - 10)
26716
27375
  );
26717
- anchorsByChangeId.set(change.id, { x: anchorX, y: anchorY });
27376
+ anchorsByChangeId.set(annotation.id, { x: anchorX, y: anchorY });
26718
27377
  });
26719
27378
  return anchorsByChangeId;
26720
27379
  });
@@ -26732,28 +27391,30 @@ function DocxEditorViewer({
26732
27391
  );
26733
27392
  return;
26734
27393
  }
26735
- const nextHeightsByPage = trackedChangesByPage.map((changes, pageIndex) => {
26736
- const pageHeights = /* @__PURE__ */ new Map();
26737
- changes.forEach((change) => {
26738
- const cardElement = trackedChangeCardElementsRef.current.get(
26739
- `${pageIndex}:${change.id}`
26740
- );
26741
- if (!cardElement) {
26742
- return;
26743
- }
26744
- const measuredHeight = Math.round(
26745
- cardElement.getBoundingClientRect().height
26746
- );
26747
- if (!Number.isFinite(measuredHeight) || measuredHeight <= 0) {
26748
- return;
26749
- }
26750
- pageHeights.set(
26751
- change.id,
26752
- Math.max(TRACKED_CHANGE_GUTTER_CARD_MIN_HEIGHT_PX, measuredHeight)
26753
- );
26754
- });
26755
- return pageHeights;
26756
- });
27394
+ const nextHeightsByPage = trackedChangesByPage.map(
27395
+ (annotations, pageIndex) => {
27396
+ const pageHeights = /* @__PURE__ */ new Map();
27397
+ annotations.forEach((annotation) => {
27398
+ const cardElement = trackedChangeCardElementsRef.current.get(
27399
+ `${pageIndex}:${annotation.id}`
27400
+ );
27401
+ if (!cardElement) {
27402
+ return;
27403
+ }
27404
+ const measuredHeight = Math.round(
27405
+ cardElement.getBoundingClientRect().height
27406
+ );
27407
+ if (!Number.isFinite(measuredHeight) || measuredHeight <= 0) {
27408
+ return;
27409
+ }
27410
+ pageHeights.set(
27411
+ annotation.id,
27412
+ Math.max(TRACKED_CHANGE_GUTTER_CARD_MIN_HEIGHT_PX, measuredHeight)
27413
+ );
27414
+ });
27415
+ return pageHeights;
27416
+ }
27417
+ );
26757
27418
  setTrackedChangeCardHeightsByPage((current) => {
26758
27419
  if (current.length === nextHeightsByPage.length) {
26759
27420
  let equal = true;
@@ -27252,9 +27913,14 @@ function DocxEditorViewer({
27252
27913
  },
27253
27914
  []
27254
27915
  );
27916
+ const storedDocumentPageCountForLatch = editor.model.metadata.documentPageCount;
27917
+ const latchTargetPageCount = Number.isFinite(storedDocumentPageCountForLatch) ? Math.max(
27918
+ collectDocxHardPageBreakStartNodeIndexes(editor.model).size + 1,
27919
+ Math.round(storedDocumentPageCountForLatch)
27920
+ ) : void 0;
27255
27921
  const nextMeasuredBodyFooterOverlapLatchState = resolveMeasuredBodyFooterOverlapLatchState({
27256
27922
  pageCount,
27257
- targetPageCount: editor.model.metadata.documentPageCount,
27923
+ targetPageCount: latchTargetPageCount,
27258
27924
  overlappingPageIndexes,
27259
27925
  previousSignature: measuredBodyFooterOverlapCandidateRef.current.signature,
27260
27926
  previousConsecutivePasses: measuredBodyFooterOverlapCandidateRef.current.consecutivePasses,
@@ -34083,7 +34749,8 @@ function DocxEditorViewer({
34083
34749
  const hasFixedPositionWrappedImage = paragraph.children.some(
34084
34750
  (child) => child.type === "image" && isFixedPositionWrappedFloatingImage(child)
34085
34751
  );
34086
- if ((trackedChangesEnabled || useSpecialTabLayout) && !hasFixedPositionWrappedImage) {
34752
+ const paragraphHasCommentAnchors = commentsEnabled && /commentRange|commentReference/i.test(paragraph.sourceXml ?? "");
34753
+ if ((trackedChangesEnabled || useSpecialTabLayout || paragraphHasCommentAnchors) && !hasFixedPositionWrappedImage) {
34087
34754
  return renderParagraphRuns(
34088
34755
  paragraph,
34089
34756
  keyPrefix,
@@ -34096,6 +34763,7 @@ function DocxEditorViewer({
34096
34763
  void 0,
34097
34764
  {
34098
34765
  showTrackedChanges: trackedChangesEnabled,
34766
+ showCommentHighlights: commentsEnabled,
34099
34767
  numberingDefinitions: editor.model.metadata.numberingDefinitions,
34100
34768
  tocLinkColorByLevel,
34101
34769
  paragraphOriginLeftPx: bodyParagraphOriginLeftPx,
@@ -40439,44 +41107,25 @@ ${currentText.slice(end)}`;
40439
41107
  }
40440
41108
  }
40441
41109
  ) : null,
40442
- pageNodeSegmentsByPage.map((pageNodeSegments, pageIndex) => {
41110
+ pageStackVirtualSpacers.before.heightPx > 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
41111
+ "div",
41112
+ {
41113
+ "aria-hidden": "true",
41114
+ "data-docx-page-window-spacer": "before",
41115
+ style: {
41116
+ height: pageStackVirtualSpacers.before.heightPx,
41117
+ width: pageStackVirtualSpacers.before.widthPx,
41118
+ margin: "0 auto",
41119
+ pointerEvents: "none",
41120
+ visibility: hideDocumentUntilPaginationSettled ? "hidden" : void 0
41121
+ }
41122
+ }
41123
+ ) : null,
41124
+ visiblePageIndexes.map((pageIndex) => {
41125
+ const pageNodeSegments = pageNodeSegmentsByPage[pageIndex] ?? [];
40443
41126
  const pageInfo = pageSectionInfoByIndex[pageIndex];
40444
41127
  const pageLayout = pageInfo?.layout ?? documentLayout;
40445
- const pageVisible = isPageVisible(pageIndex);
40446
41128
  const pageWrapperWidthPx = showTrackedChangeGutter ? pageLayout.pageWidthPx + TRACKED_CHANGE_GUTTER_WIDTH_PX : pageLayout.pageWidthPx;
40447
- if (!pageVisible) {
40448
- const placeholderBackgroundColor = pageBackgroundColor ?? editor.model.metadata.documentBackgroundColor ?? pageSurfaceBaseStyle.backgroundColor;
40449
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
40450
- "div",
40451
- {
40452
- "data-docx-page-wrapper": "true",
40453
- "data-docx-page-index": pageIndex,
40454
- "data-index": pageIndex,
40455
- style: {
40456
- width: pageWrapperWidthPx,
40457
- margin: "0 auto",
40458
- visibility: hideDocumentUntilPaginationSettled ? "hidden" : void 0
40459
- },
40460
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
40461
- "div",
40462
- {
40463
- "data-docx-page-placeholder": "true",
40464
- ref: pagePlaceholderRefForIndex(pageIndex),
40465
- style: {
40466
- ...pageSurfaceBaseStyle,
40467
- ...pageMarginPaddingStyle(pageLayout.marginsPx),
40468
- height: pageLayout.pageHeightPx,
40469
- minHeight: pageLayout.pageHeightPx,
40470
- width: pageLayout.pageWidthPx,
40471
- backgroundColor: placeholderBackgroundColor,
40472
- pointerEvents: "none"
40473
- }
40474
- }
40475
- )
40476
- },
40477
- `page-${pageIndex}`
40478
- );
40479
- }
40480
41129
  const pageContentWidthPx = Math.max(
40481
41130
  120,
40482
41131
  pageLayout.pageWidthPx - pageLayout.marginsPx.left - pageLayout.marginsPx.right
@@ -40648,6 +41297,10 @@ ${currentText.slice(end)}`;
40648
41297
  width: pageWrapperWidthPx,
40649
41298
  minHeight: pageLayout.pageHeightPx,
40650
41299
  margin: "0 auto",
41300
+ // Isolate per-page layout/style recalculation so scrolling and
41301
+ // edits on one page don't force whole-document layout passes.
41302
+ // (No paint containment: floats and gutter cards may overhang.)
41303
+ contain: "layout style",
40651
41304
  visibility: hideDocumentUntilPaginationSettled ? "hidden" : void 0
40652
41305
  },
40653
41306
  children: [
@@ -40898,9 +41551,6 @@ ${currentText.slice(end)}`;
40898
41551
  segments: [segment]
40899
41552
  });
40900
41553
  });
40901
- if (typeof window !== "undefined" && window.__docxDebugGroups) {
40902
- console.log("[groups]", pageIndex, JSON.stringify(sectionGroups.map((g) => ({ s: g.sectionIndex, n: g.segments.map((x) => x.nodeIndex) }))));
40903
- }
40904
41554
  return sectionGroups.map((group, groupIndex) => {
40905
41555
  const sectionColumns = sectionColumnsBySectionIndex[group.sectionIndex];
40906
41556
  const isLastGroupOnPage = groupIndex === sectionGroups.length - 1;
@@ -41725,10 +42375,10 @@ ${currentText.slice(end)}`;
41725
42375
  overflow: "visible"
41726
42376
  },
41727
42377
  children: pageTrackedChanges.map((entry) => {
41728
- const accentColor = trackedChangeAccentColor(
41729
- entry.change.kind,
42378
+ const accentColor = entry.annotation.trackedChange ? trackedChangeAccentColor(
42379
+ entry.annotation.trackedChange.kind,
41730
42380
  editor.documentTheme
41731
- );
42381
+ ) : commentAccentColor(editor.documentTheme);
41732
42382
  const cardCenterY = clampNumber(
41733
42383
  Math.round(entry.top + entry.heightPx / 2),
41734
42384
  8,
@@ -41789,12 +42439,12 @@ ${currentText.slice(end)}`;
41789
42439
  )
41790
42440
  ]
41791
42441
  },
41792
- `tracked-connector-${pageIndex}-${entry.change.id}`
42442
+ `tracked-connector-${pageIndex}-${entry.annotation.id}`
41793
42443
  );
41794
42444
  })
41795
42445
  }
41796
42446
  ),
41797
- !hasTrackedChanges && pageIndex === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
42447
+ (!trackedChangesEnabled || !hasTrackedChanges) && (!commentsEnabled || !hasComments) && pageIndex === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
41798
42448
  "p",
41799
42449
  {
41800
42450
  style: {
@@ -41807,19 +42457,21 @@ ${currentText.slice(end)}`;
41807
42457
  lineHeight: 1.35,
41808
42458
  color: "#94a3b8"
41809
42459
  },
41810
- children: "No edits found"
42460
+ children: trackedChangesEnabled && commentsEnabled ? "No edits or comments found" : commentsEnabled ? "No comments found" : "No edits found"
41811
42461
  }
41812
42462
  ) : null,
41813
42463
  pageTrackedChanges.map((entry) => {
41814
- const accentColor = trackedChangeAccentColor(
41815
- entry.change.kind,
42464
+ const trackedChange = entry.annotation.trackedChange;
42465
+ const comment = entry.annotation.comment;
42466
+ const accentColor = trackedChange ? trackedChangeAccentColor(
42467
+ trackedChange.kind,
41816
42468
  editor.documentTheme
41817
- );
42469
+ ) : commentAccentColor(editor.documentTheme);
41818
42470
  const formattedDate = formatTrackedChangeDate(
41819
- entry.change.date
42471
+ trackedChange?.date ?? comment?.date
41820
42472
  );
41821
- const kindLabel = trackedChangeKindLabel(entry.change.kind);
41822
- const snippet = normalizeTrackedChangeSnippet(entry.change.text) ?? (entry.change.kind === "format-change" || entry.change.kind === "paragraph-format-change" ? "Formatting" : "Change");
42473
+ const kindLabel = trackedChange ? trackedChangeKindLabel(trackedChange.kind) : comment?.resolved ? "Comment \xB7 Resolved" : comment?.parentId !== void 0 ? "Reply" : "Comment";
42474
+ const snippet = trackedChange ? normalizeTrackedChangeSnippet(trackedChange.text) ?? (trackedChange.kind === "format-change" || trackedChange.kind === "paragraph-format-change" ? "Formatting" : "Change") : comment?.text || "Comment";
41823
42475
  const cardWidthPx = Math.max(
41824
42476
  140,
41825
42477
  TRACKED_CHANGE_GUTTER_WIDTH_PX - TRACKED_CHANGE_GUTTER_CARD_LEFT_PX - TRACKED_CHANGE_GUTTER_CARD_RIGHT_PX
@@ -41871,7 +42523,7 @@ ${currentText.slice(end)}`;
41871
42523
  fontWeight: 700,
41872
42524
  lineHeight: 1.25
41873
42525
  },
41874
- children: entry.change.author?.trim() || "Unknown author"
42526
+ children: (trackedChange?.author ?? comment?.author)?.trim() || "Unknown author"
41875
42527
  }
41876
42528
  ),
41877
42529
  formattedDate ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
@@ -41890,6 +42542,23 @@ ${currentText.slice(end)}`;
41890
42542
  ]
41891
42543
  }
41892
42544
  ),
42545
+ comment?.anchorText ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
42546
+ "p",
42547
+ {
42548
+ style: {
42549
+ margin: "2px 0 0",
42550
+ fontSize: 11,
42551
+ lineHeight: 1.3,
42552
+ fontStyle: "italic",
42553
+ color: editor.documentTheme === "dark" ? "#94a3b8" : "#6b7280"
42554
+ },
42555
+ children: [
42556
+ "\u201C",
42557
+ comment.anchorText,
42558
+ "\u201D"
42559
+ ]
42560
+ }
42561
+ ) : null,
41893
42562
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
41894
42563
  "p",
41895
42564
  {
@@ -41911,8 +42580,8 @@ ${currentText.slice(end)}`;
41911
42580
  ]
41912
42581
  }
41913
42582
  );
41914
- const renderedCard = renderTrackedChangeCard ? renderTrackedChangeCard({
41915
- change: entry.change,
42583
+ const renderedCard = trackedChange ? renderTrackedChangeCard ? renderTrackedChangeCard({
42584
+ change: trackedChange,
41916
42585
  kindLabel,
41917
42586
  snippet,
41918
42587
  formattedDate,
@@ -41920,12 +42589,21 @@ ${currentText.slice(end)}`;
41920
42589
  documentTheme: editor.documentTheme,
41921
42590
  pageIndex,
41922
42591
  style: cardStyle
42592
+ }) : defaultCard : comment && renderCommentCard ? renderCommentCard({
42593
+ comment,
42594
+ snippet,
42595
+ formattedDate,
42596
+ accentColor,
42597
+ documentTheme: editor.documentTheme,
42598
+ pageIndex,
42599
+ style: cardStyle
41923
42600
  }) : defaultCard;
41924
42601
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
41925
42602
  "div",
41926
42603
  {
42604
+ "data-docx-gutter-annotation": trackedChange ? "tracked-change" : "comment",
41927
42605
  ref: (element) => {
41928
- const elementKey = `${pageIndex}:${entry.change.id}`;
42606
+ const elementKey = `${pageIndex}:${entry.annotation.id}`;
41929
42607
  if (element) {
41930
42608
  trackedChangeCardElementsRef.current.set(
41931
42609
  elementKey,
@@ -41940,7 +42618,7 @@ ${currentText.slice(end)}`;
41940
42618
  style: cardContainerStyle,
41941
42619
  children: renderedCard
41942
42620
  },
41943
- `tracked-card-${pageIndex}-${entry.change.id}`
42621
+ `tracked-card-${pageIndex}-${entry.annotation.id}`
41944
42622
  );
41945
42623
  })
41946
42624
  ]
@@ -41951,6 +42629,20 @@ ${currentText.slice(end)}`;
41951
42629
  `page-${pageIndex}`
41952
42630
  );
41953
42631
  }),
42632
+ pageStackVirtualSpacers.after.heightPx > 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
42633
+ "div",
42634
+ {
42635
+ "aria-hidden": "true",
42636
+ "data-docx-page-window-spacer": "after",
42637
+ style: {
42638
+ height: pageStackVirtualSpacers.after.heightPx,
42639
+ width: pageStackVirtualSpacers.after.widthPx,
42640
+ margin: "0 auto",
42641
+ pointerEvents: "none",
42642
+ visibility: hideDocumentUntilPaginationSettled ? "hidden" : void 0
42643
+ }
42644
+ }
42645
+ ) : null,
41954
42646
  !isReadOnly ? (() => {
41955
42647
  const hasCustomContextMenuState = Boolean(contextMenuState);
41956
42648
  const hasTableContextMenuState = Boolean(tableContextMenuState);
@@ -42307,6 +42999,7 @@ ${currentText.slice(end)}`;
42307
42999
 
42308
43000
  // src/index.tsx
42309
43001
  init_src2();
43002
+ init_src3();
42310
43003
 
42311
43004
  // ../layout-core/src/index.ts
42312
43005
  init_cjs_shims();
@@ -43854,16 +44547,20 @@ function useDocxModel(file) {
43854
44547
  }
43855
44548
  const docxFile = file;
43856
44549
  let isCurrent = true;
44550
+ const abortController = new AbortController();
43857
44551
  async function load() {
43858
44552
  setState({ isLoading: true });
43859
44553
  try {
43860
- const pkg = await parseDocx(docxFile);
44554
+ const { model } = await importDocxBuffer(docxFile, {
44555
+ signal: abortController.signal,
44556
+ transferBuffer: false
44557
+ });
43861
44558
  if (!isCurrent) {
43862
44559
  return;
43863
44560
  }
43864
44561
  setState({
43865
44562
  isLoading: false,
43866
- model: await buildDocModel(pkg)
44563
+ model
43867
44564
  });
43868
44565
  } catch (error) {
43869
44566
  if (!isCurrent) {
@@ -43878,6 +44575,7 @@ function useDocxModel(file) {
43878
44575
  void load();
43879
44576
  return () => {
43880
44577
  isCurrent = false;
44578
+ abortController.abort();
43881
44579
  };
43882
44580
  }, [file]);
43883
44581
  return state;
@@ -44218,6 +44916,7 @@ function ReactDocxViewer({
44218
44916
  updateTableCellParagraphTextRecursive,
44219
44917
  updateTableCellText,
44220
44918
  useDocxBorders,
44919
+ useDocxComments,
44221
44920
  useDocxDocumentTheme,
44222
44921
  useDocxEditor,
44223
44922
  useDocxFormFields,