@extend-ai/react-docx 0.7.0-alpha.3 → 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.js CHANGED
@@ -1,16 +1,23 @@
1
1
  import {
2
2
  createMinimalDocxPackage,
3
3
  getPart,
4
- initWasm,
5
- mapsToWasmPackage,
6
4
  packageToArrayBuffer,
7
5
  parseDocx,
6
+ withPart
7
+ } from "./chunk-QOXSE6WY.js";
8
+ import {
9
+ buildDocModel,
10
+ buildDocModelFromBytes,
11
+ cloneDocModel,
12
+ normalizeDocModel
13
+ } from "./chunk-P3B3Q7Y6.js";
14
+ import {
15
+ initWasm,
16
+ mapsToWasmPackage,
8
17
  setWasmSource,
9
- wasmBuildDocModelFromPackage,
10
18
  wasmModelToDocumentXml,
11
- wasmSerializeDocx,
12
- withPart
13
- } from "./chunk-BGCGPO6Q.js";
19
+ wasmSerializeDocx
20
+ } from "./chunk-2SXGXGWO.js";
14
21
 
15
22
  // src/index.tsx
16
23
  import * as React2 from "react";
@@ -237,413 +244,144 @@ function layoutDocument(model, options = {}) {
237
244
  return pages;
238
245
  }
239
246
 
240
- // ../doc-model/src/normalize.ts
241
- function normalizeUint8Array(value) {
242
- if (value instanceof Uint8Array) {
243
- return value;
244
- }
245
- if (Array.isArray(value)) {
246
- return Uint8Array.from(value);
247
- }
248
- return void 0;
247
+ // src/docx-import.ts
248
+ var nextImportWorkerRequestId = 1;
249
+ function createAbortError() {
250
+ if (typeof DOMException !== "undefined") {
251
+ return new DOMException("DOCX import was aborted", "AbortError");
252
+ }
253
+ const error = new Error("DOCX import was aborted");
254
+ error.name = "AbortError";
255
+ return error;
256
+ }
257
+ function errorFromWorkerResponse(response) {
258
+ const error = new Error(response.error.message);
259
+ error.name = response.error.name ?? "Error";
260
+ if (response.error.stack) {
261
+ error.stack = response.error.stack;
262
+ }
263
+ return error;
264
+ }
265
+ function canUseDocxImportWorker(options) {
266
+ return options.useWorker !== false && typeof Worker !== "undefined";
267
+ }
268
+ function createDocxImportWorker() {
269
+ return new Worker(new URL("./docx-import-worker.js", import.meta.url), {
270
+ type: "module",
271
+ name: "react-docx-import"
272
+ });
249
273
  }
250
- function normalizeParagraphChild(child) {
251
- if (child.type !== "image") {
252
- return child;
253
- }
254
- const image = child;
255
- const data = normalizeUint8Array(image.data);
256
- if (data === image.data) {
257
- return child;
274
+ async function importDocxOnMainThread(buffer, signal) {
275
+ if (signal?.aborted) {
276
+ throw createAbortError();
258
277
  }
259
- return {
260
- ...image,
261
- data
262
- };
263
- }
264
- function normalizeTableCellContent(node) {
265
- if (node.type === "table") {
266
- return normalizeDocNode(node);
278
+ const startedAt = performanceNow();
279
+ const [{ parseDocx: parseDocx2 }, { buildDocModel: buildDocModel2 }] = await Promise.all([
280
+ import("./src-PJYTN6DB.js"),
281
+ import("./src-NDPFDRVM.js")
282
+ ]);
283
+ const pkg = await parseDocx2(buffer);
284
+ const parsedAt = performanceNow();
285
+ if (signal?.aborted) {
286
+ throw createAbortError();
267
287
  }
268
- return {
269
- ...node,
270
- children: node.children.map(normalizeParagraphChild)
271
- };
272
- }
273
- function normalizeDocNode(node) {
274
- if (node.type === "paragraph") {
275
- return {
276
- ...node,
277
- children: node.children.map(normalizeParagraphChild)
278
- };
288
+ const model = await buildDocModel2(pkg);
289
+ const finishedAt = performanceNow();
290
+ if (signal?.aborted) {
291
+ throw createAbortError();
279
292
  }
280
293
  return {
281
- ...node,
282
- rows: node.rows.map((row) => ({
283
- ...row,
284
- cells: row.cells.map((cell) => ({
285
- ...cell,
286
- nodes: cell.nodes.map(normalizeTableCellContent)
287
- }))
288
- }))
289
- };
290
- }
291
- function normalizeDocModel(model) {
292
- return {
293
- ...model,
294
- nodes: model.nodes.map(normalizeDocNode),
295
- metadata: {
296
- ...model.metadata,
297
- headerSections: model.metadata.headerSections.map((section) => ({
298
- ...section,
299
- nodes: section.nodes.map(normalizeDocNode)
300
- })),
301
- footerSections: model.metadata.footerSections.map((section) => ({
302
- ...section,
303
- nodes: section.nodes.map(normalizeDocNode)
304
- })),
305
- sections: model.metadata.sections?.map((section) => ({
306
- ...section,
307
- headerSections: section.headerSections.map((header) => ({
308
- ...header,
309
- nodes: header.nodes.map(normalizeDocNode)
310
- })),
311
- footerSections: section.footerSections.map((footer) => ({
312
- ...footer,
313
- nodes: footer.nodes.map(normalizeDocNode)
314
- }))
315
- })),
316
- footnotes: model.metadata.footnotes?.map((note) => ({
317
- ...note,
318
- nodes: note.nodes?.map(normalizeDocNode)
319
- })),
320
- endnotes: model.metadata.endnotes?.map((note) => ({
321
- ...note,
322
- nodes: note.nodes?.map(normalizeDocNode)
323
- }))
294
+ package: pkg,
295
+ model,
296
+ source: "main-thread",
297
+ timings: {
298
+ totalMs: finishedAt - startedAt,
299
+ parseMs: parsedAt - startedAt,
300
+ buildModelMs: finishedAt - parsedAt
324
301
  }
325
302
  };
326
303
  }
327
-
328
- // ../doc-model/src/clone.ts
329
- function isParagraphCellContent(node) {
330
- return node.type === "paragraph";
331
- }
332
- function isTableCellContentTable(node) {
333
- return node.type === "table";
304
+ function performanceNow() {
305
+ return typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
334
306
  }
335
- function cloneTableCellContent(nodes) {
336
- return nodes.map((node) => {
337
- if (isParagraphCellContent(node)) {
338
- return cloneParagraph(node);
339
- }
340
- if (isTableCellContentTable(node)) {
341
- return cloneTable(node);
342
- }
343
- return node;
344
- });
345
- }
346
- function cloneParagraphNumbering(numbering) {
347
- return numbering ? { ...numbering } : void 0;
348
- }
349
- function cloneParagraphSpacing(spacing) {
350
- return spacing ? { ...spacing } : void 0;
351
- }
352
- function cloneParagraphIndent(indent) {
353
- return indent ? { ...indent } : void 0;
354
- }
355
- function cloneParagraphBorderStyle(border) {
356
- return border ? { ...border } : void 0;
357
- }
358
- function cloneParagraphBorderSet(borders) {
359
- if (!borders) {
360
- return void 0;
307
+ async function importDocxBuffer(buffer, options = {}) {
308
+ if (options.signal?.aborted) {
309
+ throw createAbortError();
361
310
  }
362
- return {
363
- top: cloneParagraphBorderStyle(borders.top),
364
- right: cloneParagraphBorderStyle(borders.right),
365
- bottom: cloneParagraphBorderStyle(borders.bottom),
366
- left: cloneParagraphBorderStyle(borders.left),
367
- between: cloneParagraphBorderStyle(borders.between),
368
- bar: cloneParagraphBorderStyle(borders.bar)
369
- };
370
- }
371
- function cloneParagraphStyle(style) {
372
- if (!style) {
373
- return void 0;
311
+ if (!canUseDocxImportWorker(options)) {
312
+ return importDocxOnMainThread(buffer, options.signal);
374
313
  }
375
- return {
376
- ...style,
377
- numbering: cloneParagraphNumbering(style.numbering),
378
- spacing: cloneParagraphSpacing(style.spacing),
379
- indent: cloneParagraphIndent(style.indent),
380
- borders: cloneParagraphBorderSet(style.borders),
381
- dropCap: style.dropCap ? {
382
- ...style.dropCap
383
- } : void 0
384
- };
385
- }
386
- function cloneParagraph(paragraph) {
387
- return {
388
- type: "paragraph",
389
- style: cloneParagraphStyle(paragraph.style),
390
- paragraphMarkDeleted: paragraph.paragraphMarkDeleted,
391
- sourceXml: paragraph.sourceXml,
392
- children: paragraph.children.map((child) => {
393
- if (child.type === "text") {
394
- return {
395
- type: "text",
396
- text: child.text,
397
- style: child.style ? { ...child.style } : void 0,
398
- link: child.link
399
- };
314
+ let worker;
315
+ try {
316
+ worker = createDocxImportWorker();
317
+ } catch {
318
+ return importDocxOnMainThread(buffer, options.signal);
319
+ }
320
+ const requestId = nextImportWorkerRequestId;
321
+ nextImportWorkerRequestId += 1;
322
+ return new Promise((resolve, reject) => {
323
+ let settled = false;
324
+ const cleanup = () => {
325
+ worker.removeEventListener("message", handleMessage);
326
+ worker.removeEventListener("error", handleError);
327
+ worker.removeEventListener("messageerror", handleMessageError);
328
+ options.signal?.removeEventListener("abort", handleAbort);
329
+ worker.terminate();
330
+ };
331
+ const settle = (resolver) => {
332
+ if (settled) {
333
+ return;
400
334
  }
401
- if (child.type === "form-field") {
402
- return {
403
- type: "form-field",
404
- fieldType: child.fieldType,
405
- sourceKind: child.sourceKind,
406
- id: child.id,
407
- tag: child.tag,
408
- title: child.title,
409
- placeholder: child.placeholder,
410
- checked: child.checked,
411
- value: child.value,
412
- options: child.options?.map((option) => ({
413
- displayText: option.displayText,
414
- value: option.value
415
- })),
416
- widget: child.widget ? {
417
- name: child.widget.name,
418
- enabled: child.widget.enabled,
419
- calcOnExit: child.widget.calcOnExit,
420
- text: child.widget.text ? {
421
- inputType: child.widget.text.inputType,
422
- defaultText: child.widget.text.defaultText,
423
- maxLength: child.widget.text.maxLength,
424
- textFormat: child.widget.text.textFormat
425
- } : void 0,
426
- checkbox: child.widget.checkbox ? {
427
- defaultChecked: child.widget.checkbox.defaultChecked,
428
- sizeMode: child.widget.checkbox.sizeMode,
429
- sizePt: child.widget.checkbox.sizePt
430
- } : void 0,
431
- dropdown: child.widget.dropdown ? {
432
- defaultValue: child.widget.dropdown.defaultValue
433
- } : void 0
434
- } : void 0,
435
- checkedSymbol: child.checkedSymbol,
436
- uncheckedSymbol: child.uncheckedSymbol,
437
- style: child.style ? { ...child.style } : void 0,
438
- link: child.link,
439
- sourceXml: child.sourceXml
440
- };
335
+ settled = true;
336
+ cleanup();
337
+ resolver();
338
+ };
339
+ const handleAbort = () => {
340
+ settle(() => reject(createAbortError()));
341
+ };
342
+ const handleError = (event) => {
343
+ const message = event.message || "DOCX import worker failed";
344
+ settle(() => reject(new Error(message)));
345
+ };
346
+ const handleMessageError = () => {
347
+ settle(() => reject(new Error("DOCX import worker returned an unreadable response")));
348
+ };
349
+ const handleMessage = (event) => {
350
+ const response = event.data;
351
+ if (!response || response.id !== requestId) {
352
+ return;
441
353
  }
442
- return {
443
- type: "image",
444
- src: child.src,
445
- alt: child.alt,
446
- widthPx: child.widthPx,
447
- heightPx: child.heightPx,
448
- partName: child.partName,
449
- contentType: child.contentType,
450
- data: child.data ? new Uint8Array(child.data) : void 0,
451
- sourceXml: child.sourceXml,
452
- crop: child.crop ? { ...child.crop } : void 0,
453
- cssFilter: child.cssFilter,
454
- cssOpacity: child.cssOpacity,
455
- floating: child.floating ? { ...child.floating } : void 0,
456
- syntheticTextBox: child.syntheticTextBox,
457
- textBoxText: child.textBoxText
354
+ if (response.type === "error") {
355
+ settle(() => reject(errorFromWorkerResponse(response)));
356
+ return;
357
+ }
358
+ settle(
359
+ () => resolve({
360
+ package: response.package,
361
+ model: response.model,
362
+ source: "worker",
363
+ timings: response.timings
364
+ })
365
+ );
366
+ };
367
+ worker.addEventListener("message", handleMessage);
368
+ worker.addEventListener("error", handleError);
369
+ worker.addEventListener("messageerror", handleMessageError);
370
+ options.signal?.addEventListener("abort", handleAbort, { once: true });
371
+ try {
372
+ const request = {
373
+ id: requestId,
374
+ type: "import-docx",
375
+ buffer
458
376
  };
459
- })
460
- };
461
- }
462
- function cloneTableBoxSpacing(spacing) {
463
- if (!spacing) {
464
- return void 0;
465
- }
466
- return {
467
- topTwips: spacing.topTwips,
468
- rightTwips: spacing.rightTwips,
469
- bottomTwips: spacing.bottomTwips,
470
- leftTwips: spacing.leftTwips
471
- };
472
- }
473
- function cloneTableBorderStyle(border) {
474
- if (!border) {
475
- return void 0;
476
- }
477
- return {
478
- type: border.type,
479
- color: border.color,
480
- sizeEighthPt: border.sizeEighthPt
481
- };
482
- }
483
- function cloneTableBorderSet(borders) {
484
- if (!borders) {
485
- return void 0;
486
- }
487
- return {
488
- top: cloneTableBorderStyle(borders.top),
489
- right: cloneTableBorderStyle(borders.right),
490
- bottom: cloneTableBorderStyle(borders.bottom),
491
- left: cloneTableBorderStyle(borders.left),
492
- insideH: cloneTableBorderStyle(borders.insideH),
493
- insideV: cloneTableBorderStyle(borders.insideV),
494
- tl2br: cloneTableBorderStyle(borders.tl2br),
495
- tr2bl: cloneTableBorderStyle(borders.tr2bl)
496
- };
497
- }
498
- function cloneTableFloatingStyle(floating) {
499
- if (!floating) {
500
- return void 0;
501
- }
502
- return {
503
- xTwips: floating.xTwips,
504
- yTwips: floating.yTwips,
505
- leftFromTextTwips: floating.leftFromTextTwips,
506
- rightFromTextTwips: floating.rightFromTextTwips,
507
- topFromTextTwips: floating.topFromTextTwips,
508
- bottomFromTextTwips: floating.bottomFromTextTwips,
509
- horizontalAnchor: floating.horizontalAnchor,
510
- verticalAnchor: floating.verticalAnchor,
511
- horizontalAlign: floating.horizontalAlign,
512
- verticalAlign: floating.verticalAlign
513
- };
514
- }
515
- function cloneTable(table) {
516
- return {
517
- type: "table",
518
- sourceXml: table.sourceXml,
519
- style: table.style ? {
520
- widthTwips: table.style.widthTwips,
521
- indentTwips: table.style.indentTwips,
522
- layout: table.style.layout,
523
- cellSpacingTwips: table.style.cellSpacingTwips,
524
- floating: cloneTableFloatingStyle(table.style.floating),
525
- cellMarginTwips: cloneTableBoxSpacing(table.style.cellMarginTwips),
526
- columnWidthsTwips: table.style.columnWidthsTwips ? [...table.style.columnWidthsTwips] : void 0,
527
- borders: cloneTableBorderSet(table.style.borders)
528
- } : void 0,
529
- rows: table.rows.map((row) => ({
530
- type: "table-row",
531
- style: row.style ? { ...row.style } : void 0,
532
- cells: row.cells.map((cell) => ({
533
- type: "table-cell",
534
- style: cell.style ? {
535
- ...cell.style,
536
- marginTwips: cloneTableBoxSpacing(cell.style.marginTwips),
537
- borders: cloneTableBorderSet(cell.style.borders)
538
- } : void 0,
539
- nodes: cloneTableCellContent(cell.nodes)
540
- }))
541
- }))
542
- };
543
- }
544
- function cloneDocNode(node) {
545
- return node.type === "paragraph" ? cloneParagraph(node) : cloneTable(node);
546
- }
547
- function cloneNumberingDefinitions(numberingDefinitions) {
548
- if (!numberingDefinitions) {
549
- return void 0;
550
- }
551
- return {
552
- abstracts: numberingDefinitions.abstracts.map((abstractDefinition) => ({
553
- abstractNumId: abstractDefinition.abstractNumId,
554
- levels: abstractDefinition.levels.map((level) => ({
555
- ...level,
556
- runStyle: level.runStyle ? { ...level.runStyle } : void 0,
557
- pictureBullet: level.pictureBullet ? { ...level.pictureBullet } : void 0
558
- }))
559
- })),
560
- instances: numberingDefinitions.instances.map((instanceDefinition) => ({
561
- numId: instanceDefinition.numId,
562
- abstractNumId: instanceDefinition.abstractNumId,
563
- levelStartOverrides: instanceDefinition.levelStartOverrides ? { ...instanceDefinition.levelStartOverrides } : void 0,
564
- levelOverrides: instanceDefinition.levelOverrides ? instanceDefinition.levelOverrides.map((level) => ({
565
- ...level,
566
- runStyle: level.runStyle ? { ...level.runStyle } : void 0,
567
- pictureBullet: level.pictureBullet ? { ...level.pictureBullet } : void 0
568
- })) : void 0
569
- }))
570
- };
571
- }
572
- function cloneDocModel(model) {
573
- return {
574
- nodes: model.nodes.map(cloneDocNode),
575
- metadata: {
576
- sourceParts: model.metadata.sourceParts,
577
- warnings: [...model.metadata.warnings],
578
- documentPageCount: model.metadata.documentPageCount,
579
- documentOpenTag: model.metadata.documentOpenTag,
580
- documentBackgroundColor: model.metadata.documentBackgroundColor,
581
- sectionPropertiesXml: model.metadata.sectionPropertiesXml,
582
- sections: model.metadata.sections?.map((section) => ({
583
- startNodeIndex: section.startNodeIndex,
584
- sectionPropertiesXml: section.sectionPropertiesXml,
585
- headerSections: (section.headerSections ?? []).map((headerSection) => ({
586
- partName: headerSection.partName,
587
- referenceType: headerSection.referenceType,
588
- nodes: headerSection.nodes.map(cloneDocNode)
589
- })),
590
- footerSections: (section.footerSections ?? []).map((footerSection) => ({
591
- partName: footerSection.partName,
592
- referenceType: footerSection.referenceType,
593
- nodes: footerSection.nodes.map(cloneDocNode)
594
- }))
595
- })),
596
- headerSections: (model.metadata.headerSections ?? []).map((section) => ({
597
- partName: section.partName,
598
- referenceType: section.referenceType,
599
- nodes: section.nodes.map(cloneDocNode)
600
- })),
601
- footerSections: (model.metadata.footerSections ?? []).map((section) => ({
602
- partName: section.partName,
603
- referenceType: section.referenceType,
604
- nodes: section.nodes.map(cloneDocNode)
605
- })),
606
- paragraphStyles: (model.metadata.paragraphStyles ?? []).map((style) => ({
607
- ...style,
608
- runStyle: style.runStyle ? { ...style.runStyle } : void 0,
609
- numbering: cloneParagraphNumbering(style.numbering),
610
- spacing: cloneParagraphSpacing(style.spacing),
611
- indent: cloneParagraphIndent(style.indent),
612
- borders: cloneParagraphBorderSet(style.borders)
613
- })),
614
- defaultParagraphStyleId: model.metadata.defaultParagraphStyleId,
615
- numberingDefinitions: cloneNumberingDefinitions(
616
- model.metadata.numberingDefinitions
617
- ),
618
- compatibility: model.metadata.compatibility ? { ...model.metadata.compatibility } : void 0,
619
- footnotes: model.metadata.footnotes?.map((note) => ({
620
- ...note,
621
- nodes: note.nodes?.map(cloneDocNode)
622
- })),
623
- endnotes: model.metadata.endnotes?.map((note) => ({
624
- ...note,
625
- nodes: note.nodes?.map(cloneDocNode)
626
- }))
377
+ const transfer = options.transferBuffer ? [buffer] : [];
378
+ worker.postMessage(request, transfer);
379
+ } catch (error) {
380
+ settle(
381
+ () => reject(error instanceof Error ? error : new Error("Failed to start DOCX import worker"))
382
+ );
627
383
  }
628
- };
629
- }
630
-
631
- // ../doc-model/src/index.ts
632
- async function buildDocModel(pkg) {
633
- const wasmPackage = mapsToWasmPackage({
634
- parts: pkg.parts,
635
- binaryAssets: pkg.binaryAssets
636
384
  });
637
- const model = await wasmBuildDocModelFromPackage(wasmPackage);
638
- return normalizeDocModel(model);
639
- }
640
- async function buildDocModelFromBytes(bytes) {
641
- const { parseDocx: parseDocx2 } = await import("./src-XA2NEWYM.js");
642
- const payload = bytes instanceof Uint8Array ? bytes : new Uint8Array(bytes);
643
- const buffer = payload.buffer.slice(payload.byteOffset, payload.byteOffset + payload.byteLength);
644
- const pkg = await parseDocx2(buffer);
645
- const model = await buildDocModel(pkg);
646
- return { package: pkg, model };
647
385
  }
648
386
 
649
387
  // src/section-layout.ts
@@ -1600,7 +1338,7 @@ function splitParagraphChildrenAtTextOffsets(paragraph, text, startOffset, endOf
1600
1338
  afterChildren
1601
1339
  };
1602
1340
  }
1603
- function cloneParagraph2(paragraph) {
1341
+ function cloneParagraph(paragraph) {
1604
1342
  return {
1605
1343
  type: "paragraph",
1606
1344
  style: paragraph.style ? { ...paragraph.style } : void 0,
@@ -1632,7 +1370,7 @@ function duplicateParagraph(model, index) {
1632
1370
  if (!node) {
1633
1371
  return next;
1634
1372
  }
1635
- next.nodes.splice(index + 1, 0, cloneParagraph2(node));
1373
+ next.nodes.splice(index + 1, 0, cloneParagraph(node));
1636
1374
  return next;
1637
1375
  }
1638
1376
  function updateParagraphText(model, index, text, options) {
@@ -1844,7 +1582,7 @@ function copyParagraphs(model, startIndex, endIndex = startIndex) {
1844
1582
  for (let index = start; index <= end; index += 1) {
1845
1583
  const node = model.nodes[index];
1846
1584
  if (node?.type === "paragraph") {
1847
- paragraphs.push(cloneParagraph2(node));
1585
+ paragraphs.push(cloneParagraph(node));
1848
1586
  }
1849
1587
  }
1850
1588
  return paragraphs;
@@ -1852,7 +1590,7 @@ function copyParagraphs(model, startIndex, endIndex = startIndex) {
1852
1590
  function pasteParagraphs(model, index, paragraphs) {
1853
1591
  const next = cloneDocModel(model);
1854
1592
  const safeIndex = Math.max(0, Math.min(index, next.nodes.length));
1855
- const copies = paragraphs.map(cloneParagraph2);
1593
+ const copies = paragraphs.map(cloneParagraph);
1856
1594
  next.nodes.splice(safeIndex, 0, ...copies);
1857
1595
  return next;
1858
1596
  }
@@ -2009,7 +1747,7 @@ async function serializeDocModel(model, basePackage) {
2009
1747
  model,
2010
1748
  basePackage ? mapsToWasmPackage(basePackage) : void 0
2011
1749
  );
2012
- const { parseDocx: parseDocx2 } = await import("./src-XA2NEWYM.js");
1750
+ const { parseDocx: parseDocx2 } = await import("./src-PJYTN6DB.js");
2013
1751
  return parseDocx2(bytes);
2014
1752
  }
2015
1753
  async function serializeDocx(model, basePackage) {
@@ -2369,6 +2107,13 @@ function reconcilePageCountCandidateToTargetCountByScalingHeight(options) {
2369
2107
  previousScale = scale;
2370
2108
  previousPageCount = candidate.pageCount;
2371
2109
  }
2110
+ if (!needMorePages && selectedCandidate.pageCount !== safeTargetPageCount) {
2111
+ return {
2112
+ pageCount: initialPageCount,
2113
+ pages: initialPages,
2114
+ scale: 1
2115
+ };
2116
+ }
2372
2117
  return selectedCandidate;
2373
2118
  }
2374
2119
 
@@ -3644,6 +3389,20 @@ var SerialIdleTaskQueue = class {
3644
3389
  this.schedulePump();
3645
3390
  });
3646
3391
  }
3392
+ /** Drops queued work for a single key, resolving its waiters. */
3393
+ cancel(key) {
3394
+ const remaining = [];
3395
+ this.pending.forEach((entry) => {
3396
+ if (entry.key === key) {
3397
+ entry.resolvers.forEach((resolveEntry) => {
3398
+ resolveEntry();
3399
+ });
3400
+ return;
3401
+ }
3402
+ remaining.push(entry);
3403
+ });
3404
+ this.pending.splice(0, this.pending.length, ...remaining);
3405
+ }
3647
3406
  /** Drops all queued tasks, resolving their waiters without running them. */
3648
3407
  clear() {
3649
3408
  const dropped = this.pending.splice(0, this.pending.length);
@@ -3828,6 +3587,29 @@ var MEASURED_BODY_FOOTER_OVERLAP_STABILITY_THRESHOLD = 1;
3828
3587
  var WORD_TABLE_CELL_PARAGRAPH_AUTO_LINE_TWIPS = 240;
3829
3588
  var WORD_TABLE_CELL_PARAGRAPH_BEFORE_TWIPS = 0;
3830
3589
  var WORD_TABLE_CELL_PARAGRAPH_AFTER_TWIPS = 0;
3590
+ var DOCX_IMPORT_PERFORMANCE_PREFIX = "react-docx.import";
3591
+ function markDocxImportPerformance(name) {
3592
+ if (typeof performance === "undefined" || typeof performance.mark !== "function") {
3593
+ return;
3594
+ }
3595
+ try {
3596
+ performance.mark(name);
3597
+ } catch {
3598
+ }
3599
+ }
3600
+ function measureDocxImportPerformance(name, startMark, endMark) {
3601
+ if (typeof performance === "undefined" || typeof performance.measure !== "function") {
3602
+ return;
3603
+ }
3604
+ try {
3605
+ performance.measure(name, startMark, endMark);
3606
+ } catch {
3607
+ }
3608
+ }
3609
+ function createDocxImportPerformanceTraceName(fileName) {
3610
+ const normalizedName = fileName.replace(/[^a-z0-9._-]+/gi, "_").slice(0, 80);
3611
+ return `${DOCX_IMPORT_PERFORMANCE_PREFIX}.${Date.now()}.${normalizedName}`;
3612
+ }
3831
3613
  var TABLE_ROW_SLICE_VISUAL_BLEED_PX = 1;
3832
3614
  var TABLE_CELL_SLICE_FULLY_VISIBLE_BOTTOM_BUFFER_PX = 4;
3833
3615
  var DEFAULT_SPLIT_PARAGRAPH_LINE_TWIPS = 259;
@@ -3915,6 +3697,7 @@ var tableEstimatedRowHeightsByNode = /* @__PURE__ */ new WeakMap();
3915
3697
  var paragraphExplicitIndentBySourceXml = /* @__PURE__ */ new Map();
3916
3698
  var paragraphDropCapBySourceXml = /* @__PURE__ */ new Map();
3917
3699
  var paragraphTrackedMarkupBySourceXml = /* @__PURE__ */ new Map();
3700
+ var paragraphCommentMarkupBySourceXml = /* @__PURE__ */ new Map();
3918
3701
  var paragraphMeasureCanvasContext;
3919
3702
  var textWidthByFontAndValue = /* @__PURE__ */ new Map();
3920
3703
  var estimatedTextAdvanceWidthByFontAndValue = /* @__PURE__ */ new Map();
@@ -6412,15 +6195,6 @@ function paragraphHasOnlyWhitespaceText(paragraph) {
6412
6195
  function paragraphContainsSectionBreakProperties(paragraph) {
6413
6196
  return /<w:sectPr\b/i.test(paragraph.sourceXml ?? "");
6414
6197
  }
6415
- function paragraphAbsoluteFloatingAnchorsDependOnParagraphFlow(paragraph) {
6416
- return paragraph.children.some((child) => {
6417
- if (child.type !== "image" || !shouldRenderAbsoluteFloatingImage(child) || child.syntheticTextBox !== true || !floatingTextBoxVisibleTextFromImage(child) || child.floating?.behindDocument !== true) {
6418
- return false;
6419
- }
6420
- const verticalRelativeTo = child.floating?.verticalRelativeTo?.trim().toLowerCase();
6421
- return verticalRelativeTo === void 0 || verticalRelativeTo === "" || verticalRelativeTo === "paragraph" || verticalRelativeTo === "line";
6422
- });
6423
- }
6424
6198
  function likelyFullPageCoverImageRelativeToContentBox(image, pageContentWidthPx, pageContentHeightPx) {
6425
6199
  if (!shouldRenderAbsoluteFloatingImage(image) || !image.floating) {
6426
6200
  return false;
@@ -7216,7 +6990,7 @@ function splitParagraphAtExplicitColumnBreaks(paragraph) {
7216
6990
  }
7217
6991
  appendCurrentSegment();
7218
6992
  return paragraphChildren.map((children, segmentIndex) => {
7219
- const nextStyle = cloneParagraphStyle2(paragraph.style);
6993
+ const nextStyle = cloneParagraphStyle(paragraph.style);
7220
6994
  if (segmentIndex > 0 && nextStyle?.numbering) {
7221
6995
  nextStyle.numbering = void 0;
7222
6996
  }
@@ -8648,8 +8422,11 @@ function emptyParagraphLineScaleForFontFamily(fontFamily) {
8648
8422
  }
8649
8423
  return WORD_EMPTY_PARAGRAPH_LINE_SCALE;
8650
8424
  }
8425
+ function paragraphRendersTextFreeLine(paragraph) {
8426
+ return paragraphHasOnlyWhitespaceText(paragraph) || paragraphIsFloatingImageAnchorOnly(paragraph);
8427
+ }
8651
8428
  function resolveParagraphSingleLineAutoScale(paragraph, fontFamily) {
8652
- if (paragraphHasOnlyWhitespaceText(paragraph)) {
8429
+ if (paragraphRendersTextFreeLine(paragraph)) {
8653
8430
  return emptyParagraphLineScaleForFontFamily(fontFamily);
8654
8431
  }
8655
8432
  const baseScale = singleLineAutoScaleForFontFamily(fontFamily);
@@ -8922,9 +8699,7 @@ function estimateTabLeaderWrappedLineCountForParagraph(paragraph, maxLineWidthPx
8922
8699
  leadingSegments,
8923
8700
  paragraphBaseFontPx
8924
8701
  );
8925
- const tabStopPositionsPx = (paragraph.style?.tabStops ?? []).map((tabStop) => twipsToPixels(tabStop.positionTwips)).filter(
8926
- (positionPx) => Number.isFinite(positionPx) && positionPx > 0
8927
- ).sort((left, right) => left - right);
8702
+ const tabStopPositionsPx = resolveParagraphFirstLineLeftTabStopsPx(paragraph);
8928
8703
  const explicitLeadingTabStopPx = tableOfContentsLeadingLeftTabStopPx(paragraph);
8929
8704
  const leadingReservationWidthPx = leadingSegments.length === 0 ? 0 : Number.isFinite(explicitLeadingTabStopPx) && explicitLeadingTabStopPx > 0 ? Math.max(
8930
8705
  leadingTextWidthPx,
@@ -9467,7 +9242,7 @@ function estimateParagraphLineHeightPx(paragraph, docGridLinePitchPx, disableDoc
9467
9242
  lineTwips,
9468
9243
  defaultLineMultiple
9469
9244
  );
9470
- const multiple = paragraphHasOnlyWhitespaceText(paragraph) ? Math.max(
9245
+ const multiple = paragraphRendersTextFreeLine(paragraph) ? Math.max(
9471
9246
  MIN_AUTO_LINE_MULTIPLE,
9472
9247
  Number((resolvedAutoMultiple * singleLineScale).toFixed(3))
9473
9248
  ) : calibrateAutoLineSpacingMultiple(
@@ -9526,10 +9301,7 @@ function estimateParagraphHeightPx(paragraph, availableWidthPx, numberingDefinit
9526
9301
  numberingLabel
9527
9302
  );
9528
9303
  const absoluteFloatingAnchorOnlyParagraph = paragraphIsAbsoluteFloatingImageAnchorOnly(paragraph);
9529
- const sectionBreakAnchorCarryoverParagraph = paragraphIsSectionBreakAnchorCarryover(paragraph);
9530
- const collapsibleAbsoluteFloatingAnchorOnlyParagraph = absoluteFloatingAnchorOnlyParagraph && (!paragraphAbsoluteFloatingAnchorsDependOnParagraphFlow(paragraph) || sectionBreakAnchorCarryoverParagraph);
9531
- const paragraphFlowAnchoredAbsoluteFloatingAnchorOnlyParagraph = absoluteFloatingAnchorOnlyParagraph && !collapsibleAbsoluteFloatingAnchorOnlyParagraph;
9532
- const decorativeBehindTextAnchorOnlyParagraph = paragraphActsAsDecorativeBehindTextBackgroundOverlay(paragraph);
9304
+ const collapsibleAbsoluteFloatingAnchorOnlyParagraph = absoluteFloatingAnchorOnlyParagraph && paragraphIsSectionBreakAnchorCarryover(paragraph);
9533
9305
  const inlineImageHeightPx = paragraph.children.reduce((largest, child) => {
9534
9306
  if (child.type !== "image") {
9535
9307
  return largest;
@@ -9548,14 +9320,14 @@ function estimateParagraphHeightPx(paragraph, availableWidthPx, numberingDefinit
9548
9320
  estimateWrappedFloatingImageFootprintPx(paragraph, child)
9549
9321
  );
9550
9322
  }, 0);
9551
- const emptyParagraphHeightPx = decorativeBehindTextAnchorOnlyParagraph ? 0 : paragraphIsEffectivelyEmpty(paragraph) ? lineHeightPx + EMPTY_PARAGRAPH_EXTRA_HEIGHT_PX : 0;
9323
+ const emptyParagraphHeightPx = paragraphIsEffectivelyEmpty(paragraph) ? lineHeightPx + EMPTY_PARAGRAPH_EXTRA_HEIGHT_PX : 0;
9552
9324
  const topBorderInsetPx = paragraphBorderInsetPx(
9553
9325
  paragraph.style?.borders?.top
9554
9326
  );
9555
9327
  const bottomBorderInsetPx = paragraphBorderInsetPx(
9556
9328
  paragraph.style?.borders?.bottom
9557
9329
  );
9558
- const textFlowHeightPx = collapsibleAbsoluteFloatingAnchorOnlyParagraph ? 0 : paragraphFlowAnchoredAbsoluteFloatingAnchorOnlyParagraph ? MIN_PARAGRAPH_LINE_HEIGHT_PX : (
9330
+ const textFlowHeightPx = collapsibleAbsoluteFloatingAnchorOnlyParagraph ? 0 : (
9559
9331
  // When excluding the wrapped-float footprint, the dual-wrapped block
9560
9332
  // height spans the image; but the rendered paragraph only occupies its
9561
9333
  // text lines while the float overhangs. Use the text-line height so the
@@ -9563,7 +9335,7 @@ function estimateParagraphHeightPx(paragraph, availableWidthPx, numberingDefinit
9563
9335
  dualWrappedLayout && !excludeWrappedFloatingImageFootprint ? wrappedPretextParagraphBlockHeightPx(dualWrappedLayout.layout) : lineHeightPx * lineCount
9564
9336
  );
9565
9337
  const contentHeightPx = Math.max(
9566
- collapsibleAbsoluteFloatingAnchorOnlyParagraph || decorativeBehindTextAnchorOnlyParagraph ? 0 : paragraphFlowAnchoredAbsoluteFloatingAnchorOnlyParagraph ? MIN_PARAGRAPH_LINE_HEIGHT_PX : lineHeightPx,
9338
+ collapsibleAbsoluteFloatingAnchorOnlyParagraph ? 0 : lineHeightPx,
9567
9339
  textFlowHeightPx,
9568
9340
  inlineImageHeightPx,
9569
9341
  wrappedFloatingImageHeightPx,
@@ -10890,11 +10662,6 @@ function buildDocumentPageNodeSegments(model, pageContentHeightPx, pageContentWi
10890
10662
  previousParagraphAfterPx = 0;
10891
10663
  continue;
10892
10664
  }
10893
- if (node.type === "paragraph" && paragraphActsAsDecorativeBehindTextBackgroundOverlay(node)) {
10894
- currentPageSegments.push({ nodeIndex });
10895
- previousParagraphAfterPx = 0;
10896
- continue;
10897
- }
10898
10665
  if (node.type === "paragraph" && paragraphCollapsesIntoPreviousParagraph(node, model.nodes[nodeIndex - 1])) {
10899
10666
  continue;
10900
10667
  }
@@ -12168,7 +11935,7 @@ function paragraphLineHeight(paragraph, docGridLinePitchPx, disableDocGridSnap =
12168
11935
  lineTwips,
12169
11936
  DEFAULT_PARAGRAPH_LINE_MULTIPLE
12170
11937
  );
12171
- const lineMultiple = paragraphHasOnlyWhitespaceText(paragraph) ? Math.max(
11938
+ const lineMultiple = paragraphRendersTextFreeLine(paragraph) ? Math.max(
12172
11939
  MIN_AUTO_LINE_MULTIPLE,
12173
11940
  Number((resolvedAutoMultiple * singleLineScale).toFixed(3))
12174
11941
  ) : calibrateAutoLineSpacingMultiple(
@@ -12515,8 +12282,7 @@ function paragraphBlockStyle(paragraph, numberingDefinitions, headingStyles, doc
12515
12282
  const suppressTocNumberingTextIndent = isTableOfContentsParagraph(paragraph) && paragraphHasNumbering(paragraph);
12516
12283
  const suppressIndentForFloatingAnchorOnlyParagraph = paragraphIsFloatingImageAnchorOnly(paragraph);
12517
12284
  const suppressStackingContextForBehindTextAnchorOnlyParagraph = paragraphIsBehindTextAbsoluteFloatingImageAnchorOnly(paragraph);
12518
- const suppressFlowFootprintForBehindTextAnchorOnlyParagraph = paragraphActsAsDecorativeBehindTextBackgroundOverlay(paragraph);
12519
- const reservedMinHeightPx = suppressFlowFootprintForBehindTextAnchorOnlyParagraph ? void 0 : paragraphIsEffectivelyEmpty(paragraph) ? estimateParagraphLineHeightPx(
12285
+ const reservedMinHeightPx = paragraphIsEffectivelyEmpty(paragraph) ? estimateParagraphLineHeightPx(
12520
12286
  paragraph,
12521
12287
  docGridLinePitchPx,
12522
12288
  disableDocGridSnap
@@ -12535,15 +12301,13 @@ function paragraphBlockStyle(paragraph, numberingDefinitions, headingStyles, doc
12535
12301
  // line-box strut tracks the actual content instead of the browser's
12536
12302
  // 16px default, which inflates lines for sub-12pt paragraphs.
12537
12303
  fontSize: `${paragraphBaseFontSizePx(paragraph)}px`,
12538
- lineHeight: suppressFlowFootprintForBehindTextAnchorOnlyParagraph ? 0 : paragraphLineHeight(paragraph, docGridLinePitchPx, disableDocGridSnap),
12539
- ...suppressFlowFootprintForBehindTextAnchorOnlyParagraph ? {
12540
- height: 0,
12541
- marginTop: 0,
12542
- marginBottom: 0
12543
- } : {
12544
- marginTop: beforeSpacing,
12545
- marginBottom: afterSpacing
12546
- },
12304
+ lineHeight: paragraphLineHeight(
12305
+ paragraph,
12306
+ docGridLinePitchPx,
12307
+ disableDocGridSnap
12308
+ ),
12309
+ marginTop: beforeSpacing,
12310
+ marginBottom: afterSpacing,
12547
12311
  marginLeft: suppressIndentForFloatingAnchorOnlyParagraph ? 0 : leftIndent,
12548
12312
  marginRight: suppressIndentForFloatingAnchorOnlyParagraph ? 0 : rightIndent,
12549
12313
  backgroundColor: paragraph.style?.backgroundColor,
@@ -12801,10 +12565,7 @@ function tableOfContentsLeadingLeftTabStopPx(paragraph) {
12801
12565
  if (!isTableOfContentsParagraph(paragraph)) {
12802
12566
  return void 0;
12803
12567
  }
12804
- const leftTabStopPositionsPx = (paragraph.style?.tabStops ?? []).filter((tabStop) => tabStop.alignment === "left").map((tabStop) => twipsToPixels(tabStop.positionTwips)).filter(
12805
- (positionPx) => Number.isFinite(positionPx) && positionPx > 0
12806
- ).sort((left, right) => left - right);
12807
- return leftTabStopPositionsPx[0];
12568
+ return resolveParagraphFirstLineLeftTabStopsPx(paragraph)[0];
12808
12569
  }
12809
12570
  function paragraphContainsTabCharacter(paragraph) {
12810
12571
  return paragraph.children.some((child) => {
@@ -13854,6 +13615,81 @@ function resolveParagraphTrackedMarkup(paragraph) {
13854
13615
  setCacheEntry(paragraphTrackedMarkupBySourceXml, sourceXml, resolved);
13855
13616
  return resolved;
13856
13617
  }
13618
+ function resolveParagraphCommentMarkup(paragraph) {
13619
+ const sourceXml = paragraph.sourceXml ?? "";
13620
+ if (!sourceXml || !/commentRange|commentReference/i.test(sourceXml)) {
13621
+ return void 0;
13622
+ }
13623
+ const cached = paragraphCommentMarkupBySourceXml.get(sourceXml);
13624
+ if (cached !== void 0) {
13625
+ return cached ?? void 0;
13626
+ }
13627
+ const rangeStartById = /* @__PURE__ */ new Map();
13628
+ const rangeEndById = /* @__PURE__ */ new Map();
13629
+ for (const match of sourceXml.matchAll(
13630
+ /<w:commentRangeStart\b[^>]*w:id="(-?\d+)"[^>]*\/?>/gi
13631
+ )) {
13632
+ const commentId = Number.parseInt(match[1] ?? "", 10);
13633
+ if (Number.isFinite(commentId) && match.index !== void 0) {
13634
+ rangeStartById.set(commentId, match.index + match[0].length);
13635
+ }
13636
+ }
13637
+ for (const match of sourceXml.matchAll(
13638
+ /<w:commentRangeEnd\b[^>]*w:id="(-?\d+)"[^>]*\/?>/gi
13639
+ )) {
13640
+ const commentId = Number.parseInt(match[1] ?? "", 10);
13641
+ if (Number.isFinite(commentId) && match.index !== void 0) {
13642
+ rangeEndById.set(commentId, match.index);
13643
+ }
13644
+ }
13645
+ const ranges = [];
13646
+ const rangeIds = /* @__PURE__ */ new Set([
13647
+ ...rangeStartById.keys(),
13648
+ ...rangeEndById.keys()
13649
+ ]);
13650
+ rangeIds.forEach((commentId) => {
13651
+ const start = rangeStartById.get(commentId) ?? 0;
13652
+ const end = rangeEndById.get(commentId) ?? sourceXml.length;
13653
+ if (end > start) {
13654
+ ranges.push({ commentId, start, end });
13655
+ }
13656
+ });
13657
+ if (ranges.length === 0) {
13658
+ setCacheEntry(paragraphCommentMarkupBySourceXml, sourceXml, null);
13659
+ return void 0;
13660
+ }
13661
+ const commentIdsByVisibleChildIndex = [];
13662
+ let visibleChildIndex = 0;
13663
+ const runPattern = /<w:r\b[\s\S]*?<\/w:r>/gi;
13664
+ for (const runMatch of sourceXml.matchAll(runPattern)) {
13665
+ const runXml = runMatch[0] ?? "";
13666
+ if (!runXml) {
13667
+ continue;
13668
+ }
13669
+ const runStart = runMatch.index ?? 0;
13670
+ const contentRunXml = stripTextBoxContentFromRunXml(runXml);
13671
+ const visibleTokens = parseTrackedRunTokens(contentRunXml, false);
13672
+ const hasImage = /<w:(?:drawing|pict)\b/i.test(runXml);
13673
+ const visibleChildCount = visibleTokens.filter((token) => token.text.length > 0 || token.isNote).length + (hasImage ? 1 : 0);
13674
+ if (visibleChildCount === 0) {
13675
+ continue;
13676
+ }
13677
+ const activeCommentIds = ranges.filter((range) => runStart >= range.start && runStart < range.end).map((range) => range.commentId);
13678
+ if (activeCommentIds.length > 0) {
13679
+ for (let index = 0; index < visibleChildCount; index += 1) {
13680
+ commentIdsByVisibleChildIndex[visibleChildIndex + index] = activeCommentIds;
13681
+ }
13682
+ }
13683
+ visibleChildIndex += visibleChildCount;
13684
+ }
13685
+ if (commentIdsByVisibleChildIndex.length === 0) {
13686
+ setCacheEntry(paragraphCommentMarkupBySourceXml, sourceXml, null);
13687
+ return void 0;
13688
+ }
13689
+ const resolved = { commentIdsByVisibleChildIndex };
13690
+ setCacheEntry(paragraphCommentMarkupBySourceXml, sourceXml, resolved);
13691
+ return resolved;
13692
+ }
13857
13693
  function instructionTextToPageFieldKind(rawInstruction) {
13858
13694
  const normalized = decodeXmlText(rawInstruction).replace(/\s+/g, " ").trim().toUpperCase();
13859
13695
  if (!normalized || normalized.includes("PAGEREF")) {
@@ -13900,11 +13736,16 @@ function paragraphPageFieldSequence(paragraph) {
13900
13736
  }
13901
13737
  return fields;
13902
13738
  }
13739
+ var pageFieldValueSequenceBySourceXml = /* @__PURE__ */ new Map();
13903
13740
  function paragraphPageFieldValueSequence(paragraph) {
13904
13741
  const xml = paragraph.sourceXml ?? "";
13905
13742
  if (!xml) {
13906
13743
  return [];
13907
13744
  }
13745
+ const cached = pageFieldValueSequenceBySourceXml.get(xml);
13746
+ if (cached) {
13747
+ return cached;
13748
+ }
13908
13749
  const values = [];
13909
13750
  const fieldStack = [];
13910
13751
  const tokenPattern = /<w:fldSimple\b[^>]*\bw:instr="([^"]+)"[^>]*>[\s\S]*?<\/w:fldSimple>|<w:r\b[\s\S]*?<\/w:r>/gi;
@@ -13984,13 +13825,19 @@ function paragraphPageFieldValueSequence(paragraph) {
13984
13825
  fieldStack.pop();
13985
13826
  }
13986
13827
  }
13828
+ setCacheEntry(pageFieldValueSequenceBySourceXml, xml, values);
13987
13829
  return values;
13988
13830
  }
13831
+ var styleRefFieldValueSequenceBySourceXml = /* @__PURE__ */ new Map();
13989
13832
  function paragraphStyleRefFieldValueSequence(paragraph) {
13990
13833
  const xml = paragraph.sourceXml ?? "";
13991
13834
  if (!xml) {
13992
13835
  return [];
13993
13836
  }
13837
+ const cached = styleRefFieldValueSequenceBySourceXml.get(xml);
13838
+ if (cached) {
13839
+ return cached;
13840
+ }
13994
13841
  const values = [];
13995
13842
  const fieldStack = [];
13996
13843
  const tokenPattern = /<w:fldSimple\b[^>]*\bw:instr="([^"]+)"[^>]*>[\s\S]*?<\/w:fldSimple>|<w:r\b[\s\S]*?<\/w:r>/gi;
@@ -14070,6 +13917,7 @@ function paragraphStyleRefFieldValueSequence(paragraph) {
14070
13917
  fieldStack.pop();
14071
13918
  }
14072
13919
  }
13920
+ setCacheEntry(styleRefFieldValueSequenceBySourceXml, xml, values);
14073
13921
  return values;
14074
13922
  }
14075
13923
  function normalizeStyleRefTarget(value) {
@@ -14204,6 +14052,7 @@ function renderParagraphRuns(paragraph, keyPrefix, documentTheme = "light", numb
14204
14052
  const showTrackedChanges = options?.showTrackedChanges === true;
14205
14053
  const showTrackedInlineMarkup = showTrackedChanges && options?.trackedMarkupMode !== "gutter";
14206
14054
  const trackedMarkup = showTrackedInlineMarkup ? resolveParagraphTrackedMarkup(paragraph) : void 0;
14055
+ const commentMarkup = options?.showCommentHighlights === true ? resolveParagraphCommentMarkup(paragraph) : void 0;
14207
14056
  const tocParagraphLevel = tableOfContentsLevel(paragraph);
14208
14057
  const tocLinkColor = tocParagraphLevel ? options?.tocLinkColorByLevel?.[tocParagraphLevel] : void 0;
14209
14058
  const floatingAnchorOriginCorrectionXPx = Number.isFinite(
@@ -14245,8 +14094,19 @@ function renderParagraphRuns(paragraph, keyPrefix, documentTheme = "light", numb
14245
14094
  });
14246
14095
  };
14247
14096
  const currentTrackedInlineChange = () => trackedMarkup?.inlineChangeByVisibleChildIndex[trackedVisibleChildCursor];
14097
+ const currentCommentHighlightStyle = () => {
14098
+ const commentIds = commentMarkup?.commentIdsByVisibleChildIndex[trackedVisibleChildCursor];
14099
+ if (!commentIds || commentIds.length === 0) {
14100
+ return void 0;
14101
+ }
14102
+ const accent = commentAccentColor(documentTheme);
14103
+ return {
14104
+ backgroundColor: documentTheme === "dark" ? "rgba(251, 191, 36, 0.24)" : "rgba(251, 191, 36, 0.3)",
14105
+ borderBottom: `2px solid ${accent}`
14106
+ };
14107
+ };
14248
14108
  const consumeTrackedVisibleChild = (child) => {
14249
- if (!trackedMarkup) {
14109
+ if (!trackedMarkup && !commentMarkup) {
14250
14110
  return;
14251
14111
  }
14252
14112
  if (child.type === "form-field") {
@@ -14415,20 +14275,26 @@ function renderParagraphRuns(paragraph, keyPrefix, documentTheme = "light", numb
14415
14275
  const resolveFieldText = (value, preferredZone) => resolvePageFieldText(resolveStyleRefFieldText(value), preferredZone);
14416
14276
  const trackedLinkStyle = (style, trackedInlineChange) => {
14417
14277
  if (!isTableOfContentsParagraph(paragraph)) {
14418
- return trackedInlineStyle(
14419
- linkStyleToCss(style, documentTheme),
14420
- trackedInlineChange
14421
- );
14278
+ return {
14279
+ ...trackedInlineStyle(
14280
+ linkStyleToCss(style, documentTheme),
14281
+ trackedInlineChange
14282
+ ),
14283
+ ...currentCommentHighlightStyle()
14284
+ };
14422
14285
  }
14423
14286
  const base = runStyleToCss(style, documentTheme);
14424
- return trackedInlineStyle(
14425
- {
14426
- ...base,
14427
- color: tocLinkColor ? themedRunColor(tocLinkColor, documentTheme) : "inherit",
14428
- textDecoration: "none"
14429
- },
14430
- trackedInlineChange
14431
- );
14287
+ return {
14288
+ ...trackedInlineStyle(
14289
+ {
14290
+ ...base,
14291
+ color: tocLinkColor ? themedRunColor(tocLinkColor, documentTheme) : "inherit",
14292
+ textDecoration: "none"
14293
+ },
14294
+ trackedInlineChange
14295
+ ),
14296
+ ...currentCommentHighlightStyle()
14297
+ };
14432
14298
  };
14433
14299
  const usesExternalHorizontalAnchorOrigin = (image) => {
14434
14300
  const horizontalRelativeTo = image.floating?.horizontalRelativeTo?.trim().toLowerCase();
@@ -14475,10 +14341,13 @@ function renderParagraphRuns(paragraph, keyPrefix, documentTheme = "light", numb
14475
14341
  trackTextAdvance(text, child.style);
14476
14342
  return;
14477
14343
  }
14478
- const trackedStyle = trackedInlineStyle(
14479
- runStyleToCss(child.style, documentTheme),
14480
- trackedInlineChange
14481
- );
14344
+ const trackedStyle = {
14345
+ ...trackedInlineStyle(
14346
+ runStyleToCss(child.style, documentTheme),
14347
+ trackedInlineChange
14348
+ ),
14349
+ ...currentCommentHighlightStyle()
14350
+ };
14482
14351
  if (text === " " && !useTabLeaderLayout && !useAnchoredTabLayout) {
14483
14352
  target.push(
14484
14353
  /* @__PURE__ */ jsx("span", { style: tabTextStyle(child.style, trackedStyle), children: "\xA0" }, key)
@@ -14734,10 +14603,13 @@ function renderParagraphRuns(paragraph, keyPrefix, documentTheme = "light", numb
14734
14603
  }
14735
14604
  return;
14736
14605
  }
14737
- const textStyle = trackedInlineStyle(
14738
- runStyleToCss(child.style, documentTheme),
14739
- trackedInlineChange
14740
- );
14606
+ const textStyle = {
14607
+ ...trackedInlineStyle(
14608
+ runStyleToCss(child.style, documentTheme),
14609
+ trackedInlineChange
14610
+ ),
14611
+ ...currentCommentHighlightStyle()
14612
+ };
14741
14613
  const noteLabel = noteMarkerLabel(
14742
14614
  child.noteReference,
14743
14615
  safeNoteMarkerIndexes.footnote,
@@ -15702,23 +15574,23 @@ function textWithListType(text, listType) {
15702
15574
  const normalized = stripListPrefix(text);
15703
15575
  return listType === "unordered" ? `\u2022 ${normalized}` : `1. ${normalized}`;
15704
15576
  }
15705
- function cloneParagraphBorderStyle2(border) {
15577
+ function cloneParagraphBorderStyle(border) {
15706
15578
  return border ? { ...border } : void 0;
15707
15579
  }
15708
- function cloneParagraphBorderSet2(borders) {
15580
+ function cloneParagraphBorderSet(borders) {
15709
15581
  if (!borders) {
15710
15582
  return void 0;
15711
15583
  }
15712
15584
  return {
15713
- top: cloneParagraphBorderStyle2(borders.top),
15714
- right: cloneParagraphBorderStyle2(borders.right),
15715
- bottom: cloneParagraphBorderStyle2(borders.bottom),
15716
- left: cloneParagraphBorderStyle2(borders.left),
15717
- between: cloneParagraphBorderStyle2(borders.between),
15718
- bar: cloneParagraphBorderStyle2(borders.bar)
15585
+ top: cloneParagraphBorderStyle(borders.top),
15586
+ right: cloneParagraphBorderStyle(borders.right),
15587
+ bottom: cloneParagraphBorderStyle(borders.bottom),
15588
+ left: cloneParagraphBorderStyle(borders.left),
15589
+ between: cloneParagraphBorderStyle(borders.between),
15590
+ bar: cloneParagraphBorderStyle(borders.bar)
15719
15591
  };
15720
15592
  }
15721
- function cloneParagraphStyle2(style) {
15593
+ function cloneParagraphStyle(style) {
15722
15594
  if (!style) {
15723
15595
  return void 0;
15724
15596
  }
@@ -15728,12 +15600,12 @@ function cloneParagraphStyle2(style) {
15728
15600
  spacing: style.spacing ? { ...style.spacing } : void 0,
15729
15601
  indent: style.indent ? { ...style.indent } : void 0,
15730
15602
  tabStops: style.tabStops ? style.tabStops.map((tabStop) => ({ ...tabStop })) : void 0,
15731
- borders: cloneParagraphBorderSet2(style.borders),
15603
+ borders: cloneParagraphBorderSet(style.borders),
15732
15604
  dropCap: style.dropCap ? { ...style.dropCap } : void 0
15733
15605
  };
15734
15606
  }
15735
15607
  function splitParagraphStyleWithDefaultSpacing(style, sourceXml) {
15736
- const clonedStyle = cloneParagraphStyle2(style);
15608
+ const clonedStyle = cloneParagraphStyle(style);
15737
15609
  if (sourceXml || clonedStyle?.styleId) {
15738
15610
  return clonedStyle;
15739
15611
  }
@@ -15752,7 +15624,7 @@ function splitParagraphStyleWithDefaultSpacing(style, sourceXml) {
15752
15624
  }
15753
15625
  };
15754
15626
  }
15755
- function cloneTableBoxSpacing2(spacing) {
15627
+ function cloneTableBoxSpacing(spacing) {
15756
15628
  if (!spacing) {
15757
15629
  return void 0;
15758
15630
  }
@@ -15763,7 +15635,7 @@ function cloneTableBoxSpacing2(spacing) {
15763
15635
  leftTwips: spacing.leftTwips
15764
15636
  };
15765
15637
  }
15766
- function cloneTableBorderStyle2(border) {
15638
+ function cloneTableBorderStyle(border) {
15767
15639
  if (!border) {
15768
15640
  return void 0;
15769
15641
  }
@@ -15773,19 +15645,19 @@ function cloneTableBorderStyle2(border) {
15773
15645
  sizeEighthPt: border.sizeEighthPt
15774
15646
  };
15775
15647
  }
15776
- function cloneTableBorderSet2(borders) {
15648
+ function cloneTableBorderSet(borders) {
15777
15649
  if (!borders) {
15778
15650
  return void 0;
15779
15651
  }
15780
15652
  return {
15781
- top: cloneTableBorderStyle2(borders.top),
15782
- right: cloneTableBorderStyle2(borders.right),
15783
- bottom: cloneTableBorderStyle2(borders.bottom),
15784
- left: cloneTableBorderStyle2(borders.left),
15785
- insideH: cloneTableBorderStyle2(borders.insideH),
15786
- insideV: cloneTableBorderStyle2(borders.insideV),
15787
- tl2br: cloneTableBorderStyle2(borders.tl2br),
15788
- tr2bl: cloneTableBorderStyle2(borders.tr2bl)
15653
+ top: cloneTableBorderStyle(borders.top),
15654
+ right: cloneTableBorderStyle(borders.right),
15655
+ bottom: cloneTableBorderStyle(borders.bottom),
15656
+ left: cloneTableBorderStyle(borders.left),
15657
+ insideH: cloneTableBorderStyle(borders.insideH),
15658
+ insideV: cloneTableBorderStyle(borders.insideV),
15659
+ tl2br: cloneTableBorderStyle(borders.tl2br),
15660
+ tr2bl: cloneTableBorderStyle(borders.tr2bl)
15789
15661
  };
15790
15662
  }
15791
15663
  function cloneTableCellStyle(style) {
@@ -15794,8 +15666,8 @@ function cloneTableCellStyle(style) {
15794
15666
  }
15795
15667
  return {
15796
15668
  ...style,
15797
- marginTwips: cloneTableBoxSpacing2(style.marginTwips),
15798
- borders: cloneTableBorderSet2(style.borders)
15669
+ marginTwips: cloneTableBoxSpacing(style.marginTwips),
15670
+ borders: cloneTableBorderSet(style.borders)
15799
15671
  };
15800
15672
  }
15801
15673
  function cloneTableRowStyle(style) {
@@ -15810,7 +15682,7 @@ function createEmptyParagraphFromTemplate(template) {
15810
15682
  const nextTextStyle = cloneTextStyle2(firstRunStyle(template));
15811
15683
  return {
15812
15684
  type: "paragraph",
15813
- style: cloneParagraphStyle2(template?.style),
15685
+ style: cloneParagraphStyle(template?.style),
15814
15686
  children: [
15815
15687
  {
15816
15688
  type: "text",
@@ -15879,7 +15751,7 @@ function shiftListIndent(indent, levelDelta) {
15879
15751
  };
15880
15752
  }
15881
15753
  function ensurePrefixListIndent(paragraph) {
15882
- const clonedStyle = cloneParagraphStyle2(paragraph.style) ?? {};
15754
+ const clonedStyle = cloneParagraphStyle(paragraph.style) ?? {};
15883
15755
  const existingIndent = clonedStyle.indent;
15884
15756
  const hasMeaningfulLeftIndent = Number.isFinite(existingIndent?.leftTwips) && Math.abs(existingIndent?.leftTwips ?? 0) > 0;
15885
15757
  const hasMeaningfulFirstLine = Number.isFinite(existingIndent?.firstLineTwips) && Math.abs(existingIndent?.firstLineTwips ?? 0) > 0;
@@ -15899,7 +15771,7 @@ function ensurePrefixListIndent(paragraph) {
15899
15771
  return true;
15900
15772
  }
15901
15773
  function clearAutoPrefixListIndent(paragraph) {
15902
- const clonedStyle = cloneParagraphStyle2(paragraph.style);
15774
+ const clonedStyle = cloneParagraphStyle(paragraph.style);
15903
15775
  const existingIndent = clonedStyle?.indent;
15904
15776
  if (!clonedStyle || !existingIndent) {
15905
15777
  return false;
@@ -16026,11 +15898,22 @@ function parseEmbeddedTableRuntimeKey(tableRuntimeKey) {
16026
15898
  descendants
16027
15899
  };
16028
15900
  }
15901
+ var columnWidthsByTable = /* @__PURE__ */ new WeakMap();
16029
15902
  function columnWidthsFromTableDefinition(table, columnCount) {
15903
+ const cachedByCount = columnWidthsByTable.get(table);
15904
+ if (cachedByCount?.has(columnCount)) {
15905
+ return cachedByCount.get(columnCount);
15906
+ }
15907
+ const resolved = computeColumnWidthsFromTableDefinition(table, columnCount);
15908
+ const cache = cachedByCount ?? /* @__PURE__ */ new Map();
15909
+ cache.set(columnCount, resolved);
15910
+ columnWidthsByTable.set(table, cache);
15911
+ return resolved;
15912
+ }
15913
+ function computeColumnWidthsFromTableDefinition(table, columnCount) {
16030
15914
  const gridWidths = table.style?.columnWidthsTwips;
16031
15915
  const rowDerivedWidths = deriveColumnWidthsFromTableRows(table, columnCount);
16032
15916
  if (gridWidths && gridWidths.length === columnCount) {
16033
- console.log("[colw]", columnCount, gridWidths.length, !!rowDerivedWidths, gridConflictsWithRowWidths(table, gridWidths));
16034
15917
  if (rowDerivedWidths && rowDerivedWidths.length > 0 && gridConflictsWithRowWidths(table, gridWidths)) {
16035
15918
  return rowDerivedWidths;
16036
15919
  }
@@ -16517,7 +16400,7 @@ function applyParagraphBorderPresetForRangeEntry(borders, preset, remove, index,
16517
16400
  if (total <= 1) {
16518
16401
  return applyParagraphBorderPreset(borders, preset, remove);
16519
16402
  }
16520
- const nextBorders = cloneParagraphBorderSet2(borders) ?? {};
16403
+ const nextBorders = cloneParagraphBorderSet(borders) ?? {};
16521
16404
  const visibleBorder = toolbarParagraphBorderStyle(
16522
16405
  resolvePreferredParagraphBorder(nextBorders)
16523
16406
  );
@@ -16598,7 +16481,7 @@ function tableBorderPresetState(borders, selectedCellBorders) {
16598
16481
  };
16599
16482
  }
16600
16483
  function applyParagraphBorderPreset(borders, preset, remove = false) {
16601
- const nextBorders = cloneParagraphBorderSet2(borders) ?? {};
16484
+ const nextBorders = cloneParagraphBorderSet(borders) ?? {};
16602
16485
  const visibleBorder = toolbarParagraphBorderStyle(
16603
16486
  resolvePreferredParagraphBorder(nextBorders)
16604
16487
  );
@@ -16654,7 +16537,7 @@ function applyParagraphBorderPreset(borders, preset, remove = false) {
16654
16537
  return nextBorders;
16655
16538
  }
16656
16539
  function applyTableBorderPreset(borders, preset, remove = false) {
16657
- const nextBorders = cloneTableBorderSet2(borders) ?? {};
16540
+ const nextBorders = cloneTableBorderSet(borders) ?? {};
16658
16541
  const visibleBorder = toolbarTableBorderStyle(
16659
16542
  resolvePreferredTableBorder(nextBorders)
16660
16543
  );
@@ -17571,6 +17454,127 @@ function collectTrackedChangesFromModel(model) {
17571
17454
  });
17572
17455
  return trackedChanges;
17573
17456
  }
17457
+ function decodeCommentRangeText(rangeXml) {
17458
+ const texts = [];
17459
+ for (const match of rangeXml.matchAll(
17460
+ /<w:t\b[^>]*>([\s\S]*?)<\/w:t>/gi
17461
+ )) {
17462
+ texts.push(decodeXmlText(match[1] ?? ""));
17463
+ }
17464
+ const combined = texts.join("").replace(/\s+/g, " ").trim();
17465
+ if (!combined) {
17466
+ return void 0;
17467
+ }
17468
+ return combined.length > 120 ? `${combined.slice(0, 119)}\u2026` : combined;
17469
+ }
17470
+ function resolveCommentAnchorText(sourceXml, commentId) {
17471
+ const startMatch = sourceXml.match(
17472
+ new RegExp(`<w:commentRangeStart\\b[^>]*w:id="${commentId}"[^>]*/?>`, "i")
17473
+ );
17474
+ const endMatch = sourceXml.match(
17475
+ new RegExp(`<w:commentRangeEnd\\b[^>]*w:id="${commentId}"[^>]*/?>`, "i")
17476
+ );
17477
+ const startIndex = startMatch?.index !== void 0 ? startMatch.index + startMatch[0].length : (
17478
+ // Range opened in an earlier paragraph: take from the paragraph start.
17479
+ endMatch?.index !== void 0 ? 0 : void 0
17480
+ );
17481
+ if (startIndex === void 0) {
17482
+ return void 0;
17483
+ }
17484
+ const endIndex = endMatch?.index !== void 0 ? endMatch.index : sourceXml.length;
17485
+ if (endIndex <= startIndex) {
17486
+ return void 0;
17487
+ }
17488
+ return decodeCommentRangeText(sourceXml.slice(startIndex, endIndex));
17489
+ }
17490
+ function collectCommentsFromModel(model) {
17491
+ const definitions = model.metadata.comments ?? [];
17492
+ if (definitions.length === 0) {
17493
+ return [];
17494
+ }
17495
+ const definitionById = new Map(
17496
+ definitions.map((definition) => [definition.id, definition])
17497
+ );
17498
+ const comments = [];
17499
+ const appendParagraphComments = (paragraph, nodeIndex, location) => {
17500
+ const sourceXml = paragraph.sourceXml ?? "";
17501
+ if (!sourceXml || !/commentReference/i.test(sourceXml)) {
17502
+ return;
17503
+ }
17504
+ for (const match of sourceXml.matchAll(
17505
+ /<w:commentReference\b[^>]*w:id="(-?\d+)"/gi
17506
+ )) {
17507
+ const commentId = Number.parseInt(match[1] ?? "", 10);
17508
+ const definition = Number.isFinite(commentId) ? definitionById.get(commentId) : void 0;
17509
+ if (!definition) {
17510
+ continue;
17511
+ }
17512
+ comments.push({
17513
+ id: `${paragraphLocationKey(location)}:comment:${commentId}`,
17514
+ commentId,
17515
+ author: definition.author,
17516
+ initials: definition.initials,
17517
+ date: definition.date,
17518
+ text: definition.text,
17519
+ parentId: definition.parentId,
17520
+ resolved: definition.resolved,
17521
+ anchorText: resolveCommentAnchorText(sourceXml, commentId),
17522
+ nodeIndex,
17523
+ location: location.kind === "paragraph" ? { kind: "paragraph", nodeIndex: location.nodeIndex } : {
17524
+ kind: "table-cell",
17525
+ tableIndex: location.tableIndex,
17526
+ rowIndex: location.rowIndex,
17527
+ cellIndex: location.cellIndex,
17528
+ paragraphIndex: location.paragraphIndex
17529
+ }
17530
+ });
17531
+ }
17532
+ };
17533
+ model.nodes.forEach((node, nodeIndex) => {
17534
+ if (node.type === "paragraph") {
17535
+ appendParagraphComments(node, nodeIndex, {
17536
+ kind: "paragraph",
17537
+ nodeIndex
17538
+ });
17539
+ return;
17540
+ }
17541
+ node.rows.forEach((row, rowIndex) => {
17542
+ row.cells.forEach((cell, cellIndex) => {
17543
+ const directParagraphs = tableCellParagraphs(cell.nodes);
17544
+ directParagraphs.forEach((paragraph, paragraphIndex) => {
17545
+ appendParagraphComments(paragraph, nodeIndex, {
17546
+ kind: "table-cell",
17547
+ tableIndex: nodeIndex,
17548
+ rowIndex,
17549
+ cellIndex,
17550
+ paragraphIndex
17551
+ });
17552
+ });
17553
+ const nestedParagraphs = tableCellParagraphsRecursively(
17554
+ cell.nodes
17555
+ ).filter((paragraph) => !directParagraphs.includes(paragraph));
17556
+ nestedParagraphs.forEach((paragraph, nestedParagraphIndex) => {
17557
+ appendParagraphComments(paragraph, nodeIndex, {
17558
+ kind: "table-cell",
17559
+ tableIndex: nodeIndex,
17560
+ rowIndex,
17561
+ cellIndex,
17562
+ paragraphIndex: -(nestedParagraphIndex + 1)
17563
+ });
17564
+ });
17565
+ });
17566
+ });
17567
+ });
17568
+ return comments;
17569
+ }
17570
+ function commentAccentColor(documentTheme) {
17571
+ return documentTheme === "dark" ? "#fbbf24" : "#d97706";
17572
+ }
17573
+ function estimateCommentCardHeight(comment) {
17574
+ const snippet = comment.text || "Comment";
17575
+ const lines = Math.max(1, Math.ceil(snippet.length / 30));
17576
+ return Math.max(TRACKED_CHANGE_GUTTER_CARD_MIN_HEIGHT_PX, 34 + lines * 14);
17577
+ }
17574
17578
  function trackedChangeKindLabel(kind) {
17575
17579
  switch (kind) {
17576
17580
  case "insertion":
@@ -17619,15 +17623,15 @@ function trackedChangeAccentColor(kind, documentTheme) {
17619
17623
  return palette.format;
17620
17624
  }
17621
17625
  }
17622
- function trackedChangeSortTuple(change) {
17623
- if (change.location.kind === "paragraph") {
17624
- return [change.location.nodeIndex, 0, 0, 0];
17626
+ function gutterAnnotationSortTuple(location) {
17627
+ if (location.kind === "paragraph") {
17628
+ return [location.nodeIndex, 0, 0, 0];
17625
17629
  }
17626
17630
  return [
17627
- change.location.tableIndex,
17628
- change.location.rowIndex,
17629
- change.location.cellIndex,
17630
- change.location.paragraphIndex
17631
+ location.tableIndex,
17632
+ location.rowIndex,
17633
+ location.cellIndex,
17634
+ location.paragraphIndex
17631
17635
  ];
17632
17636
  }
17633
17637
  function trackedChangeBelongsToPageSegments(location, pageSegments) {
@@ -17646,10 +17650,10 @@ function trackedChangeBelongsToPageSegments(location, pageSegments) {
17646
17650
  return location.rowIndex >= segment.tableRowRange.startRowIndex && location.rowIndex < segment.tableRowRange.endRowIndex;
17647
17651
  });
17648
17652
  }
17649
- function resolveTrackedChangePageIndex(change, pageNodeSegmentsByPage) {
17653
+ function resolveGutterAnnotationPageIndex(location, pageNodeSegmentsByPage) {
17650
17654
  for (let pageIndex = 0; pageIndex < pageNodeSegmentsByPage.length; pageIndex += 1) {
17651
17655
  if (trackedChangeBelongsToPageSegments(
17652
- change.location,
17656
+ location,
17653
17657
  pageNodeSegmentsByPage[pageIndex] ?? []
17654
17658
  )) {
17655
17659
  return pageIndex;
@@ -17704,30 +17708,30 @@ function estimateTrackedChangeCardHeight(change) {
17704
17708
  const lines = Math.max(1, Math.ceil(snippet.length / 30));
17705
17709
  return Math.max(TRACKED_CHANGE_GUTTER_CARD_MIN_HEIGHT_PX, 34 + lines * 14);
17706
17710
  }
17707
- function layoutTrackedChangesForPage(changes, anchorByChangeId, cardHeightsByChangeId, pageWidthPx, pageHeightPx) {
17708
- if (changes.length === 0) {
17711
+ function layoutTrackedChangesForPage(annotations, anchorByChangeId, cardHeightsByChangeId, pageWidthPx, pageHeightPx) {
17712
+ if (annotations.length === 0) {
17709
17713
  return [];
17710
17714
  }
17711
17715
  const fallbackStride = Math.max(
17712
17716
  18,
17713
- pageHeightPx / Math.max(1, changes.length + 1)
17717
+ pageHeightPx / Math.max(1, annotations.length + 1)
17714
17718
  );
17715
- const withAnchors = changes.map((change, index) => {
17719
+ const withAnchors = annotations.map((annotation, index) => {
17716
17720
  const defaultAnchor = Math.min(
17717
17721
  Math.max(10, Math.round((index + 1) * fallbackStride)),
17718
17722
  Math.max(10, pageHeightPx - 10)
17719
17723
  );
17720
- const anchorPoint = anchorByChangeId.get(change.id);
17724
+ const anchorPoint = anchorByChangeId.get(annotation.id);
17721
17725
  const anchorY = anchorPoint?.y ?? defaultAnchor;
17722
17726
  const anchorX = anchorPoint?.x ?? Math.max(10, pageWidthPx - 10);
17723
- const measuredHeightPx = cardHeightsByChangeId?.get(change.id);
17724
- const estimatedHeightPx = estimateTrackedChangeCardHeight(change);
17727
+ const measuredHeightPx = cardHeightsByChangeId?.get(annotation.id);
17728
+ const estimatedHeightPx = annotation.trackedChange ? estimateTrackedChangeCardHeight(annotation.trackedChange) : annotation.comment ? estimateCommentCardHeight(annotation.comment) : TRACKED_CHANGE_GUTTER_CARD_MIN_HEIGHT_PX;
17725
17729
  const heightPx = Number.isFinite(measuredHeightPx) && measuredHeightPx > 0 ? Math.max(
17726
17730
  TRACKED_CHANGE_GUTTER_CARD_MIN_HEIGHT_PX,
17727
17731
  Math.round(measuredHeightPx)
17728
17732
  ) : estimatedHeightPx;
17729
17733
  return {
17730
- change,
17734
+ annotation,
17731
17735
  anchorX: clampNumber(
17732
17736
  Math.round(anchorX),
17733
17737
  10,
@@ -18758,10 +18762,14 @@ function useDocxEditor(options = {}) {
18758
18762
  const [isImporting, setIsImporting] = React.useState(false);
18759
18763
  const [documentTheme, setDocumentThemeState] = React.useState(options.initialDocumentTheme ?? "light");
18760
18764
  const [showTrackedChanges, setShowTrackedChangesState] = React.useState(options.initialShowTrackedChanges ?? false);
18765
+ const [showComments, setShowCommentsState] = React.useState(
18766
+ options.initialShowComments ?? false
18767
+ );
18761
18768
  const [paginationInfo, setPaginationInfo] = React.useState({
18762
18769
  currentPage: 1,
18763
18770
  totalPages: 1
18764
18771
  });
18772
+ const activeImportAbortControllerRef = React.useRef(void 0);
18765
18773
  const [history, setHistory] = React.useState({
18766
18774
  past: [],
18767
18775
  future: []
@@ -19074,6 +19082,9 @@ function useDocxEditor(options = {}) {
19074
19082
  () => collectTrackedChangesFromModel(model),
19075
19083
  [model]
19076
19084
  );
19085
+ const comments = React.useMemo(() => collectCommentsFromModel(model), [
19086
+ model
19087
+ ]);
19077
19088
  const hasUnorderedList = selectedListType === "unordered";
19078
19089
  const hasOrderedList = selectedListType === "ordered";
19079
19090
  const canUndo = history.past.length > 0;
@@ -19094,6 +19105,15 @@ function useDocxEditor(options = {}) {
19094
19105
  const toggleShowTrackedChanges = React.useCallback(() => {
19095
19106
  setShowTrackedChangesState((current) => !current);
19096
19107
  }, []);
19108
+ const setShowComments = React.useCallback(
19109
+ (nextShowComments) => {
19110
+ setShowCommentsState(nextShowComments);
19111
+ },
19112
+ []
19113
+ );
19114
+ const toggleShowComments = React.useCallback(() => {
19115
+ setShowCommentsState((current) => !current);
19116
+ }, []);
19097
19117
  const registerPendingExportModelTransformer = React.useCallback(
19098
19118
  (transformer) => {
19099
19119
  pendingExportModelTransformerRef.current = transformer;
@@ -19157,6 +19177,7 @@ function useDocxEditor(options = {}) {
19157
19177
  );
19158
19178
  React.useEffect(() => {
19159
19179
  return () => {
19180
+ activeImportAbortControllerRef.current?.abort();
19160
19181
  unloadEmbeddedFonts();
19161
19182
  };
19162
19183
  }, [unloadEmbeddedFonts]);
@@ -19292,6 +19313,8 @@ function useDocxEditor(options = {}) {
19292
19313
  );
19293
19314
  const importDocxFile = React.useCallback(
19294
19315
  async (file) => {
19316
+ activeImportAbortControllerRef.current?.abort();
19317
+ activeImportAbortControllerRef.current = void 0;
19295
19318
  if (!/\.docx?$/i.test(file.name)) {
19296
19319
  replaceDocumentWithImportError(
19297
19320
  file.name,
@@ -19302,11 +19325,50 @@ function useDocxEditor(options = {}) {
19302
19325
  setIsImporting(true);
19303
19326
  setImportError(void 0);
19304
19327
  setStatus(`Loading ${file.name}...`);
19328
+ const importAbortController = new AbortController();
19329
+ activeImportAbortControllerRef.current = importAbortController;
19330
+ const traceName = createDocxImportPerformanceTraceName(file.name);
19331
+ const startMark = `${traceName}:start`;
19332
+ const bufferStartMark = `${traceName}:arrayBuffer:start`;
19333
+ const bufferEndMark = `${traceName}:arrayBuffer:end`;
19334
+ const workerStartMark = `${traceName}:worker:start`;
19335
+ const workerEndMark = `${traceName}:worker:end`;
19336
+ const fontsStartMark = `${traceName}:fonts:start`;
19337
+ const fontsEndMark = `${traceName}:fonts:end`;
19338
+ const stateStartMark = `${traceName}:state:start`;
19339
+ const stateEndMark = `${traceName}:state:end`;
19340
+ markDocxImportPerformance(startMark);
19305
19341
  try {
19342
+ markDocxImportPerformance(bufferStartMark);
19306
19343
  const buffer = await file.arrayBuffer();
19307
- const pkg = await parseDocx(buffer);
19344
+ markDocxImportPerformance(bufferEndMark);
19345
+ measureDocxImportPerformance(
19346
+ `${traceName}:arrayBuffer`,
19347
+ bufferStartMark,
19348
+ bufferEndMark
19349
+ );
19350
+ markDocxImportPerformance(workerStartMark);
19351
+ const importResult = await importDocxBuffer(buffer, {
19352
+ signal: importAbortController.signal,
19353
+ transferBuffer: true
19354
+ });
19355
+ markDocxImportPerformance(workerEndMark);
19356
+ measureDocxImportPerformance(
19357
+ `${traceName}:${importResult.source}`,
19358
+ workerStartMark,
19359
+ workerEndMark
19360
+ );
19361
+ const pkg = importResult.package;
19362
+ const nextModel = importResult.model;
19363
+ markDocxImportPerformance(fontsStartMark);
19308
19364
  await loadEmbeddedFontsFromPackage(pkg);
19309
- const nextModel = await buildDocModel(pkg);
19365
+ markDocxImportPerformance(fontsEndMark);
19366
+ measureDocxImportPerformance(
19367
+ `${traceName}:fonts`,
19368
+ fontsStartMark,
19369
+ fontsEndMark
19370
+ );
19371
+ markDocxImportPerformance(stateStartMark);
19310
19372
  setModel(nextModel);
19311
19373
  setDocumentLoadNonce((current) => current + 1);
19312
19374
  setHistory({ past: [], future: [] });
@@ -19319,16 +19381,31 @@ function useDocxEditor(options = {}) {
19319
19381
  setSelectedFormFieldLocation(void 0);
19320
19382
  setImportError(void 0);
19321
19383
  setStatus(`Loaded ${file.name}`);
19384
+ markDocxImportPerformance(stateEndMark);
19385
+ measureDocxImportPerformance(
19386
+ `${traceName}:state-dispatch`,
19387
+ stateStartMark,
19388
+ stateEndMark
19389
+ );
19390
+ measureDocxImportPerformance(`${traceName}:total`, startMark, stateEndMark);
19322
19391
  } catch (error) {
19392
+ if (error instanceof Error && error.name === "AbortError" && activeImportAbortControllerRef.current !== importAbortController) {
19393
+ return;
19394
+ }
19323
19395
  const nextError = error instanceof Error ? error : new Error("Unknown error");
19324
19396
  replaceDocumentWithImportError(file.name, nextError);
19325
19397
  } finally {
19326
- setIsImporting(false);
19398
+ if (activeImportAbortControllerRef.current === importAbortController) {
19399
+ activeImportAbortControllerRef.current = void 0;
19400
+ setIsImporting(false);
19401
+ }
19327
19402
  }
19328
19403
  },
19329
19404
  [loadEmbeddedFontsFromPackage, replaceDocumentWithImportError]
19330
19405
  );
19331
19406
  const newDocument = React.useCallback(() => {
19407
+ activeImportAbortControllerRef.current?.abort();
19408
+ activeImportAbortControllerRef.current = void 0;
19332
19409
  unloadEmbeddedFonts();
19333
19410
  setModel(cloneDocModel(starterTemplateRef.current));
19334
19411
  setDocumentLoadNonce((current) => current + 1);
@@ -19941,7 +20018,7 @@ function useDocxEditor(options = {}) {
19941
20018
  return;
19942
20019
  }
19943
20020
  paragraph.style = {
19944
- ...cloneParagraphStyle2(paragraph.style) ?? {},
20021
+ ...cloneParagraphStyle(paragraph.style) ?? {},
19945
20022
  numbering: void 0
19946
20023
  };
19947
20024
  paragraph.sourceXml = void 0;
@@ -19997,7 +20074,7 @@ function useDocxEditor(options = {}) {
19997
20074
  normalizedRange.end.offset
19998
20075
  );
19999
20076
  collapsedOffset = safeStart + replacementText.length;
20000
- const endParagraphStyleForMerge = cloneParagraphStyle2(
20077
+ const endParagraphStyleForMerge = cloneParagraphStyle(
20001
20078
  endParagraph.style
20002
20079
  );
20003
20080
  const insertedStyle = cloneTextStyle2(
@@ -20084,7 +20161,7 @@ function useDocxEditor(options = {}) {
20084
20161
  normalizedRange.start.location
20085
20162
  );
20086
20163
  if (mergedLookup.paragraph) {
20087
- mergedLookup.paragraph.style = endParagraphStyleForMerge ? cloneParagraphStyle2(endParagraphStyleForMerge) : void 0;
20164
+ mergedLookup.paragraph.style = endParagraphStyleForMerge ? cloneParagraphStyle(endParagraphStyleForMerge) : void 0;
20088
20165
  mergedLookup.paragraph.sourceXml = void 0;
20089
20166
  if (mergedLookup.tableNode) {
20090
20167
  mergedLookup.tableNode.sourceXml = void 0;
@@ -20401,7 +20478,7 @@ function useDocxEditor(options = {}) {
20401
20478
  const removePreset2 = preset !== "none" && presetState2[preset];
20402
20479
  if (preset === "diagonal-down" || preset === "diagonal-up") {
20403
20480
  const clonedCellStyle = cloneTableCellStyle(targetCell.style) ?? {};
20404
- const nextCellBorders = cloneTableBorderSet2(clonedCellStyle.borders) ?? {};
20481
+ const nextCellBorders = cloneTableBorderSet(clonedCellStyle.borders) ?? {};
20405
20482
  const visibleBorder = removePreset2 ? nilTableBorderStyle() : toolbarTableBorderStyle(
20406
20483
  resolvePreferredTableBorder(nextCellBorders) ?? resolvePreferredTableBorder(tableNode2.style?.borders)
20407
20484
  );
@@ -20431,7 +20508,7 @@ function useDocxEditor(options = {}) {
20431
20508
  borders: nextBorders2
20432
20509
  };
20433
20510
  if (preset === "none" && targetCell.style?.borders) {
20434
- const nextCellBorders = cloneTableBorderSet2(targetCell.style.borders) ?? {};
20511
+ const nextCellBorders = cloneTableBorderSet(targetCell.style.borders) ?? {};
20435
20512
  const nilBorder = nilTableBorderStyle();
20436
20513
  nextCellBorders.tl2br = { ...nilBorder };
20437
20514
  nextCellBorders.tr2bl = { ...nilBorder };
@@ -20482,7 +20559,7 @@ function useDocxEditor(options = {}) {
20482
20559
  if (!paragraph2) {
20483
20560
  continue;
20484
20561
  }
20485
- const clonedStyle2 = cloneParagraphStyle2(paragraph2.style) ?? {};
20562
+ const clonedStyle2 = cloneParagraphStyle(paragraph2.style) ?? {};
20486
20563
  const nextBorders2 = applyParagraphBorderPresetForRangeEntry(
20487
20564
  clonedStyle2.borders,
20488
20565
  preset,
@@ -20514,7 +20591,7 @@ function useDocxEditor(options = {}) {
20514
20591
  if (!paragraph) {
20515
20592
  return current;
20516
20593
  }
20517
- const clonedStyle = cloneParagraphStyle2(paragraph.style) ?? {};
20594
+ const clonedStyle = cloneParagraphStyle(paragraph.style) ?? {};
20518
20595
  const presetState = paragraphBorderPresetState(clonedStyle.borders);
20519
20596
  const removePreset = preset !== "none" && presetState[preset];
20520
20597
  const nextBorders = applyParagraphBorderPreset(
@@ -20673,7 +20750,7 @@ function useDocxEditor(options = {}) {
20673
20750
  const currentText = paragraphText(paragraph);
20674
20751
  if (shouldRemove) {
20675
20752
  if (paragraphHasNumbering(paragraph)) {
20676
- const clonedStyle = cloneParagraphStyle2(paragraph.style);
20753
+ const clonedStyle = cloneParagraphStyle(paragraph.style);
20677
20754
  if (clonedStyle?.numbering) {
20678
20755
  clonedStyle.numbering = void 0;
20679
20756
  paragraph.style = clonedStyle;
@@ -20819,7 +20896,7 @@ function useDocxEditor(options = {}) {
20819
20896
  return current;
20820
20897
  }
20821
20898
  paragraphNode.style = {
20822
- ...cloneParagraphStyle2(paragraphNode.style) ?? {},
20899
+ ...cloneParagraphStyle(paragraphNode.style) ?? {},
20823
20900
  numbering: {
20824
20901
  ...numbering,
20825
20902
  ilvl: nextLevel
@@ -20935,7 +21012,7 @@ function useDocxEditor(options = {}) {
20935
21012
  paragraphNode.sourceXml = void 0;
20936
21013
  next.nodes.splice(insertionIndex, 0, {
20937
21014
  type: "paragraph",
20938
- style: cloneParagraphStyle2(paragraphNode.style),
21015
+ style: cloneParagraphStyle(paragraphNode.style),
20939
21016
  children: [
20940
21017
  {
20941
21018
  type: "text",
@@ -21026,12 +21103,12 @@ function useDocxEditor(options = {}) {
21026
21103
  afterInsertedStyle: inheritedRunStyle
21027
21104
  }
21028
21105
  );
21029
- paragraphNode.style = cloneParagraphStyle2(splitParagraphStyle);
21106
+ paragraphNode.style = cloneParagraphStyle(splitParagraphStyle);
21030
21107
  paragraphNode.children = splitChildren.beforeChildren;
21031
21108
  paragraphNode.sourceXml = void 0;
21032
21109
  next.nodes.splice(insertionIndex, 0, {
21033
21110
  type: "paragraph",
21034
- style: cloneParagraphStyle2(splitParagraphStyle),
21111
+ style: cloneParagraphStyle(splitParagraphStyle),
21035
21112
  children: splitChildren.afterChildren
21036
21113
  });
21037
21114
  splitResult = {
@@ -21365,7 +21442,7 @@ function useDocxEditor(options = {}) {
21365
21442
  if (!currentDropCap) {
21366
21443
  return current;
21367
21444
  }
21368
- paragraph.style = cloneParagraphStyle2(paragraph.style) ?? {};
21445
+ paragraph.style = cloneParagraphStyle(paragraph.style) ?? {};
21369
21446
  paragraph.style.dropCap = {
21370
21447
  ...currentDropCap,
21371
21448
  ...patch
@@ -21441,7 +21518,7 @@ function useDocxEditor(options = {}) {
21441
21518
  link: preservedLink
21442
21519
  }
21443
21520
  ];
21444
- paragraph.style = cloneParagraphStyle2(paragraph.style) ?? {};
21521
+ paragraph.style = cloneParagraphStyle(paragraph.style) ?? {};
21445
21522
  paragraph.style.dropCap = {
21446
21523
  ...currentDropCap
21447
21524
  };
@@ -21595,7 +21672,7 @@ function useDocxEditor(options = {}) {
21595
21672
  const firstParagraph = paragraphs[0];
21596
21673
  const nextParagraph = {
21597
21674
  type: "paragraph",
21598
- style: cloneParagraphStyle2(firstParagraph?.style),
21675
+ style: cloneParagraphStyle(firstParagraph?.style),
21599
21676
  children: [
21600
21677
  {
21601
21678
  type: "text",
@@ -22254,6 +22331,8 @@ function useDocxEditor(options = {}) {
22254
22331
  documentTheme,
22255
22332
  trackedChanges,
22256
22333
  showTrackedChanges,
22334
+ comments,
22335
+ showComments,
22257
22336
  currentPage: paginationInfo.currentPage,
22258
22337
  totalPages: paginationInfo.totalPages,
22259
22338
  selection,
@@ -22281,8 +22360,10 @@ function useDocxEditor(options = {}) {
22281
22360
  setStatus,
22282
22361
  setDocumentTheme,
22283
22362
  setShowTrackedChanges,
22363
+ setShowComments,
22284
22364
  syncPaginationInfo,
22285
22365
  toggleShowTrackedChanges,
22366
+ toggleShowComments,
22286
22367
  importDocxFile,
22287
22368
  newDocument,
22288
22369
  exportDocx,
@@ -22569,6 +22650,7 @@ function useDocxPageThumbnails(editor, options = {}) {
22569
22650
  if (!targetCanvas) {
22570
22651
  return;
22571
22652
  }
22653
+ const requiresAttachedTarget = canvas === void 0;
22572
22654
  const pageElement = pageSurfaceRegistry.pageElements.get(pageIndex);
22573
22655
  if (!pageElement || !pageElement.isConnected) {
22574
22656
  updatePageThumbnailState(pageIndex, "unavailable");
@@ -22582,6 +22664,9 @@ function useDocxPageThumbnails(editor, options = {}) {
22582
22664
  }
22583
22665
  updatePageThumbnailState(pageIndex, "rendering");
22584
22666
  await ensureThumbnailRasterQueue().enqueue(targetCanvas, async () => {
22667
+ if (requiresAttachedTarget && attachedCanvasByPageRef.current.get(pageIndex) !== targetCanvas) {
22668
+ return;
22669
+ }
22585
22670
  const livePageElement = pageSurfaceRegistry.pageElements.get(pageIndex);
22586
22671
  if (!livePageElement || !livePageElement.isConnected) {
22587
22672
  updatePageThumbnailState(pageIndex, "unavailable");
@@ -22728,6 +22813,10 @@ function useDocxPageThumbnails(editor, options = {}) {
22728
22813
  void renderPageThumbnailToCanvasRef.current(pageIndex, canvas);
22729
22814
  return;
22730
22815
  }
22816
+ const previousCanvas = attachedCanvasByPageRef.current.get(pageIndex);
22817
+ if (previousCanvas) {
22818
+ thumbnailRasterQueueRef.current?.cancel(previousCanvas);
22819
+ }
22731
22820
  attachedCanvasByPageRef.current.delete(pageIndex);
22732
22821
  };
22733
22822
  canvasRefCallbacksRef.current.set(pageIndex, nextCanvasRef);
@@ -22995,6 +23084,42 @@ function useDocxTrackChanges(editor) {
22995
23084
  ]
22996
23085
  );
22997
23086
  }
23087
+ function useDocxComments(editor) {
23088
+ const commentsByLocation = React.useMemo(() => {
23089
+ const grouped = /* @__PURE__ */ new Map();
23090
+ editor.comments.forEach((comment) => {
23091
+ const key = paragraphLocationKey(comment.location);
23092
+ const bucket = grouped.get(key) ?? [];
23093
+ bucket.push(comment);
23094
+ grouped.set(key, bucket);
23095
+ });
23096
+ return grouped;
23097
+ }, [editor.comments]);
23098
+ const getCommentsForLocation = React.useCallback(
23099
+ (location) => {
23100
+ return commentsByLocation.get(paragraphLocationKey(location)) ?? [];
23101
+ },
23102
+ [commentsByLocation]
23103
+ );
23104
+ return React.useMemo(
23105
+ () => ({
23106
+ comments: editor.comments,
23107
+ showComments: editor.showComments,
23108
+ setShowComments: editor.setShowComments,
23109
+ toggleShowComments: editor.toggleShowComments,
23110
+ commentsByLocation,
23111
+ getCommentsForLocation
23112
+ }),
23113
+ [
23114
+ editor.comments,
23115
+ editor.showComments,
23116
+ editor.setShowComments,
23117
+ editor.toggleShowComments,
23118
+ commentsByLocation,
23119
+ getCommentsForLocation
23120
+ ]
23121
+ );
23122
+ }
22998
23123
  function useDocxPageLayout(editor) {
22999
23124
  const primarySectionPropertiesXml = editor.model.metadata.sections?.[0]?.sectionPropertiesXml ?? editor.model.metadata.sectionPropertiesXml;
23000
23125
  const layout = React.useMemo(() => {
@@ -23867,6 +23992,8 @@ function DocxEditorViewer({
23867
23992
  headingStyles,
23868
23993
  showTrackedChanges,
23869
23994
  renderTrackedChangeCard,
23995
+ showComments,
23996
+ renderCommentCard,
23870
23997
  renderTableContextMenu,
23871
23998
  renderContextMenu,
23872
23999
  onFormFieldDoubleClick,
@@ -23875,7 +24002,9 @@ function DocxEditorViewer({
23875
24002
  const pageSurfaceRegistryOwner = docxViewerPageSurfaceRegistryOwner(editor);
23876
24003
  const trackedChangesEnabled = showTrackedChanges ?? editor.showTrackedChanges;
23877
24004
  const hasTrackedChanges = editor.trackedChanges.length > 0;
23878
- const showTrackedChangeGutter = trackedChangesEnabled;
24005
+ const commentsEnabled = showComments ?? editor.showComments;
24006
+ const hasComments = editor.comments.length > 0;
24007
+ const showTrackedChangeGutter = trackedChangesEnabled || commentsEnabled;
23879
24008
  const isReadOnly = mode === "read-only" || trackedChangesEnabled;
23880
24009
  const isNightReaderMode = isReadOnly && editor.documentTheme === "dark";
23881
24010
  const documentContentTheme = isNightReaderMode ? "light" : editor.documentTheme;
@@ -23894,11 +24023,13 @@ function DocxEditorViewer({
23894
24023
  const paragraphRunRenderOptions = React.useMemo(
23895
24024
  () => ({
23896
24025
  showTrackedChanges: trackedChangesEnabled,
24026
+ showCommentHighlights: commentsEnabled,
23897
24027
  numberingDefinitions: editor.model.metadata.numberingDefinitions,
23898
24028
  tocLinkColorByLevel,
23899
24029
  imageFilterSuffix: documentContentFilter
23900
24030
  }),
23901
24031
  [
24032
+ commentsEnabled,
23902
24033
  documentContentFilter,
23903
24034
  editor.model.metadata.numberingDefinitions,
23904
24035
  tocLinkColorByLevel,
@@ -23941,7 +24072,6 @@ function DocxEditorViewer({
23941
24072
  /* @__PURE__ */ new Map()
23942
24073
  );
23943
24074
  const pageElementsRef = React.useRef(/* @__PURE__ */ new Map());
23944
- const pagePlaceholderRefCallbacksRef = React.useRef(/* @__PURE__ */ new Map());
23945
24075
  const pageSurfaceRefCallbacksRef = React.useRef(/* @__PURE__ */ new Map());
23946
24076
  const trackedChangeCardElementsRef = React.useRef(/* @__PURE__ */ new Map());
23947
24077
  const initialPaginationStableSignatureRef = React.useRef(
@@ -24009,26 +24139,6 @@ function DocxEditorViewer({
24009
24139
  measuredPageContentHeightByIndex,
24010
24140
  setMeasuredPageContentHeightByIndex
24011
24141
  ] = React.useState([]);
24012
- const pagePlaceholderRefForIndex = React.useCallback(
24013
- (pageIndex) => {
24014
- const normalizedPageIndex = Math.max(0, Math.round(pageIndex));
24015
- const cached = pagePlaceholderRefCallbacksRef.current.get(normalizedPageIndex);
24016
- if (cached) {
24017
- return cached;
24018
- }
24019
- const nextRef = (element) => {
24020
- if (element) {
24021
- pageElementsRef.current.set(normalizedPageIndex, element);
24022
- } else {
24023
- pageElementsRef.current.delete(normalizedPageIndex);
24024
- }
24025
- registerDocxViewerPageSurface(editor, normalizedPageIndex, void 0);
24026
- };
24027
- pagePlaceholderRefCallbacksRef.current.set(normalizedPageIndex, nextRef);
24028
- return nextRef;
24029
- },
24030
- [pageSurfaceRegistryOwner]
24031
- );
24032
24142
  const pageSurfaceRefForIndex = React.useCallback(
24033
24143
  (pageIndex) => {
24034
24144
  const normalizedPageIndex = Math.max(0, Math.round(pageIndex));
@@ -24050,7 +24160,6 @@ function DocxEditorViewer({
24050
24160
  [pageSurfaceRegistryOwner]
24051
24161
  );
24052
24162
  React.useEffect(() => {
24053
- pagePlaceholderRefCallbacksRef.current.clear();
24054
24163
  pageSurfaceRefCallbacksRef.current.clear();
24055
24164
  }, [pageSurfaceRegistryOwner]);
24056
24165
  const [
@@ -25324,6 +25433,26 @@ function DocxEditorViewer({
25324
25433
  nearestScrollableAncestor(viewerRootRef.current)
25325
25434
  );
25326
25435
  }, [editor.documentLoadNonce, pageCount, trackedChangesEnabled]);
25436
+ const [zoomProbeNonce, setZoomProbeNonce] = React.useState(0);
25437
+ React.useEffect(() => {
25438
+ if (typeof window === "undefined") {
25439
+ return;
25440
+ }
25441
+ const rootElement = viewerRootRef.current;
25442
+ if (!rootElement) {
25443
+ return;
25444
+ }
25445
+ const bumpZoomProbe = () => {
25446
+ setZoomProbeNonce((nonce) => nonce + 1);
25447
+ };
25448
+ const resizeObserver = typeof ResizeObserver === "function" ? new ResizeObserver(bumpZoomProbe) : void 0;
25449
+ resizeObserver?.observe(rootElement);
25450
+ window.addEventListener("resize", bumpZoomProbe);
25451
+ return () => {
25452
+ resizeObserver?.disconnect();
25453
+ window.removeEventListener("resize", bumpZoomProbe);
25454
+ };
25455
+ }, [editor.documentLoadNonce]);
25327
25456
  React.useLayoutEffect(() => {
25328
25457
  if (typeof window === "undefined") {
25329
25458
  return;
@@ -25336,7 +25465,7 @@ function DocxEditorViewer({
25336
25465
  setVirtualizerMeasurementScale(
25337
25466
  (current) => Math.abs(current - nextScale) < 5e-3 ? current : nextScale
25338
25467
  );
25339
- });
25468
+ }, [editor.documentLoadNonce, pageCount, zoomProbeNonce]);
25340
25469
  const internalPageVirtualizationEnabled = internalPageVirtualizationRequested && internalVirtualScrollElement !== null;
25341
25470
  const internalPageVirtualizationPending = typeof window !== "undefined" && internalPageVirtualizationRequested && internalVirtualScrollElement === null;
25342
25471
  const internalVirtualScrollUsesWindow = typeof document !== "undefined" && internalVirtualScrollElement !== null && (internalVirtualScrollElement === document.scrollingElement || internalVirtualScrollElement === document.documentElement || internalVirtualScrollElement === document.body);
@@ -25370,7 +25499,39 @@ function DocxEditorViewer({
25370
25499
  overscan: pageVirtualizationOverscan
25371
25500
  });
25372
25501
  const internalPageVirtualizer = internalVirtualScrollUsesWindow ? internalWindowPageVirtualizer : internalElementPageVirtualizer;
25502
+ React.useLayoutEffect(() => {
25503
+ internalElementPageVirtualizer.measure();
25504
+ internalWindowPageVirtualizer.measure();
25505
+ }, [
25506
+ estimateVirtualPageSize,
25507
+ internalElementPageVirtualizer,
25508
+ internalWindowPageVirtualizer
25509
+ ]);
25373
25510
  const internalVirtualItems = internalPageVirtualizer.getVirtualItems();
25511
+ const internalMostVisiblePageIndex = React.useMemo(() => {
25512
+ if (!internalPageVirtualizationEnabled || internalVirtualItems.length === 0) {
25513
+ return void 0;
25514
+ }
25515
+ const virtualizerMetrics = internalPageVirtualizer;
25516
+ const viewportStart = Number.isFinite(virtualizerMetrics.scrollOffset) ? virtualizerMetrics.scrollOffset : internalVirtualItems[0]?.start ?? 0;
25517
+ const viewportSize = virtualizerMetrics.scrollRect?.height;
25518
+ const viewportEnd = viewportStart + (Number.isFinite(viewportSize) && viewportSize > 0 ? viewportSize : internalVirtualItems[0]?.size ?? 0);
25519
+ let bestPageIndex;
25520
+ let bestVisibleSize = -1;
25521
+ internalVirtualItems.forEach((item) => {
25522
+ const visibleSize = Math.min(item.end, viewportEnd) - Math.max(item.start, viewportStart);
25523
+ if (visibleSize > bestVisibleSize) {
25524
+ bestVisibleSize = visibleSize;
25525
+ bestPageIndex = item.index;
25526
+ }
25527
+ });
25528
+ return bestPageIndex === void 0 ? void 0 : clampNumber(bestPageIndex, 0, pageCount - 1);
25529
+ }, [
25530
+ internalPageVirtualizationEnabled,
25531
+ internalPageVirtualizer,
25532
+ internalVirtualItems,
25533
+ pageCount
25534
+ ]);
25374
25535
  const internalVisiblePageRange = React.useMemo(() => {
25375
25536
  if (!internalPageVirtualizationEnabled) {
25376
25537
  return void 0;
@@ -25412,8 +25573,8 @@ function DocxEditorViewer({
25412
25573
  return internalVisiblePageRange;
25413
25574
  }
25414
25575
  const scrollDirection = internalPageVirtualizer.scrollDirection;
25415
- const renderPreviousPage = scrollDirection !== "backward";
25416
- const renderNextPage = scrollDirection === "backward";
25576
+ const renderPreviousPage = scrollDirection !== "forward";
25577
+ const renderNextPage = scrollDirection !== "backward";
25417
25578
  const startPageIndex = clampNumber(
25418
25579
  internalVisiblePageRange.startPageIndex - (renderPreviousPage ? LARGE_TABLE_PAGE_ADJACENT_RENDER_COUNT : 0),
25419
25580
  0,
@@ -25661,6 +25822,40 @@ function DocxEditorViewer({
25661
25822
  }
25662
25823
  return indexes;
25663
25824
  }, [pageCount, visiblePageEndIndex, visiblePageStartIndex]);
25825
+ const pageStackVirtualSpacers = React.useMemo(() => {
25826
+ const pageWrapperWidthPxForIndex = (pageIndex) => {
25827
+ const pageLayout = pageSectionInfoByIndex[pageIndex]?.layout ?? documentLayout;
25828
+ return showTrackedChangeGutter ? pageLayout.pageWidthPx + TRACKED_CHANGE_GUTTER_WIDTH_PX : pageLayout.pageWidthPx;
25829
+ };
25830
+ const summarizeSkippedPages = (startPageIndex, endPageIndex) => {
25831
+ if (pageCount <= 0 || endPageIndex < startPageIndex || startPageIndex >= pageCount || endPageIndex < 0) {
25832
+ return { heightPx: 0, widthPx: 0 };
25833
+ }
25834
+ const start = clampNumber(startPageIndex, 0, pageCount - 1);
25835
+ const end = clampNumber(endPageIndex, start, pageCount - 1);
25836
+ let heightPx = 0;
25837
+ let widthPx = 0;
25838
+ for (let pageIndex = start; pageIndex <= end; pageIndex += 1) {
25839
+ const pageLayout = pageSectionInfoByIndex[pageIndex]?.layout ?? documentLayout;
25840
+ heightPx += Math.max(1, Math.round(pageLayout.pageHeightPx));
25841
+ widthPx = Math.max(widthPx, pageWrapperWidthPxForIndex(pageIndex));
25842
+ }
25843
+ const skippedPageCount = end - start + 1;
25844
+ heightPx += Math.max(0, skippedPageCount - 1) * DOC_PAGE_BREAK_GAP;
25845
+ return { heightPx, widthPx };
25846
+ };
25847
+ return {
25848
+ before: summarizeSkippedPages(0, visiblePageStartIndex - 1),
25849
+ after: summarizeSkippedPages(visiblePageEndIndex + 1, pageCount - 1)
25850
+ };
25851
+ }, [
25852
+ documentLayout,
25853
+ pageCount,
25854
+ pageSectionInfoByIndex,
25855
+ showTrackedChangeGutter,
25856
+ visiblePageEndIndex,
25857
+ visiblePageStartIndex
25858
+ ]);
25664
25859
  React.useLayoutEffect(() => {
25665
25860
  if (typeof window === "undefined") {
25666
25861
  return;
@@ -25820,7 +26015,7 @@ function DocxEditorViewer({
25820
26015
  }, [onPageCountChange, stableReportedPageCount]);
25821
26016
  React.useEffect(() => {
25822
26017
  const nextCurrentPage = clampNumber(
25823
- visiblePageStartIndex + 1,
26018
+ (internalMostVisiblePageIndex ?? visiblePageStartIndex) + 1,
25824
26019
  1,
25825
26020
  Math.max(1, stableReportedPageCount)
25826
26021
  );
@@ -25830,6 +26025,7 @@ function DocxEditorViewer({
25830
26025
  });
25831
26026
  }, [
25832
26027
  editor.syncPaginationInfo,
26028
+ internalMostVisiblePageIndex,
25833
26029
  stableReportedPageCount,
25834
26030
  visiblePageStartIndex
25835
26031
  ]);
@@ -25837,20 +26033,38 @@ function DocxEditorViewer({
25837
26033
  const pageBuckets = pageNodeSegmentsByPage.map(
25838
26034
  () => []
25839
26035
  );
25840
- editor.trackedChanges.forEach((change) => {
25841
- const pageIndex = resolveTrackedChangePageIndex(
25842
- change,
26036
+ const placeAnnotation = (annotation) => {
26037
+ const pageIndex = resolveGutterAnnotationPageIndex(
26038
+ annotation.location,
25843
26039
  pageNodeSegmentsByPage
25844
26040
  );
25845
26041
  if (pageIndex < 0 || pageIndex >= pageBuckets.length) {
25846
26042
  return;
25847
26043
  }
25848
- pageBuckets[pageIndex].push(change);
25849
- });
25850
- pageBuckets.forEach((pageChanges) => {
25851
- pageChanges.sort((left, right) => {
25852
- const leftKey = trackedChangeSortTuple(left);
25853
- const rightKey = trackedChangeSortTuple(right);
26044
+ pageBuckets[pageIndex].push(annotation);
26045
+ };
26046
+ if (trackedChangesEnabled) {
26047
+ editor.trackedChanges.forEach((change) => {
26048
+ placeAnnotation({
26049
+ id: change.id,
26050
+ location: change.location,
26051
+ trackedChange: change
26052
+ });
26053
+ });
26054
+ }
26055
+ if (commentsEnabled) {
26056
+ editor.comments.forEach((comment) => {
26057
+ placeAnnotation({
26058
+ id: comment.id,
26059
+ location: comment.location,
26060
+ comment
26061
+ });
26062
+ });
26063
+ }
26064
+ pageBuckets.forEach((pageAnnotations) => {
26065
+ pageAnnotations.sort((left, right) => {
26066
+ const leftKey = gutterAnnotationSortTuple(left.location);
26067
+ const rightKey = gutterAnnotationSortTuple(right.location);
25854
26068
  for (let index = 0; index < leftKey.length; index += 1) {
25855
26069
  if (leftKey[index] === rightKey[index]) {
25856
26070
  continue;
@@ -25861,7 +26075,13 @@ function DocxEditorViewer({
25861
26075
  });
25862
26076
  });
25863
26077
  return pageBuckets;
25864
- }, [editor.trackedChanges, pageNodeSegmentsByPage]);
26078
+ }, [
26079
+ commentsEnabled,
26080
+ editor.comments,
26081
+ editor.trackedChanges,
26082
+ pageNodeSegmentsByPage,
26083
+ trackedChangesEnabled
26084
+ ]);
25865
26085
  const [trackedChangeAnchorByPage, setTrackedChangeAnchorByPage] = React.useState([]);
25866
26086
  const [headerBodyClearanceByPage, setHeaderBodyClearanceByPage] = React.useState({});
25867
26087
  React.useEffect(() => {
@@ -25875,18 +26095,18 @@ function DocxEditorViewer({
25875
26095
  );
25876
26096
  return;
25877
26097
  }
25878
- const nextAnchorMaps = trackedChangesByPage.map((changes, pageIndex) => {
26098
+ const nextAnchorMaps = trackedChangesByPage.map((annotations, pageIndex) => {
25879
26099
  const pageElement = pageElementsRef.current.get(pageIndex);
25880
26100
  const anchorsByChangeId = /* @__PURE__ */ new Map();
25881
- if (!pageElement || changes.length === 0) {
26101
+ if (!pageElement || annotations.length === 0) {
25882
26102
  return anchorsByChangeId;
25883
26103
  }
25884
26104
  const pageHeightPx = Math.max(1, pageElement.offsetHeight);
25885
26105
  const pageWidthPx = Math.max(1, pageElement.offsetWidth);
25886
- changes.forEach((change) => {
26106
+ annotations.forEach((annotation) => {
25887
26107
  const anchorElement = findTrackedChangeAnchorElementInPage(
25888
26108
  pageElement,
25889
- change.location
26109
+ annotation.location
25890
26110
  );
25891
26111
  if (!anchorElement) {
25892
26112
  return;
@@ -25910,7 +26130,7 @@ function DocxEditorViewer({
25910
26130
  10,
25911
26131
  Math.max(10, pageWidthPx - 10)
25912
26132
  );
25913
- anchorsByChangeId.set(change.id, { x: anchorX, y: anchorY });
26133
+ anchorsByChangeId.set(annotation.id, { x: anchorX, y: anchorY });
25914
26134
  });
25915
26135
  return anchorsByChangeId;
25916
26136
  });
@@ -25928,28 +26148,30 @@ function DocxEditorViewer({
25928
26148
  );
25929
26149
  return;
25930
26150
  }
25931
- const nextHeightsByPage = trackedChangesByPage.map((changes, pageIndex) => {
25932
- const pageHeights = /* @__PURE__ */ new Map();
25933
- changes.forEach((change) => {
25934
- const cardElement = trackedChangeCardElementsRef.current.get(
25935
- `${pageIndex}:${change.id}`
25936
- );
25937
- if (!cardElement) {
25938
- return;
25939
- }
25940
- const measuredHeight = Math.round(
25941
- cardElement.getBoundingClientRect().height
25942
- );
25943
- if (!Number.isFinite(measuredHeight) || measuredHeight <= 0) {
25944
- return;
25945
- }
25946
- pageHeights.set(
25947
- change.id,
25948
- Math.max(TRACKED_CHANGE_GUTTER_CARD_MIN_HEIGHT_PX, measuredHeight)
25949
- );
25950
- });
25951
- return pageHeights;
25952
- });
26151
+ const nextHeightsByPage = trackedChangesByPage.map(
26152
+ (annotations, pageIndex) => {
26153
+ const pageHeights = /* @__PURE__ */ new Map();
26154
+ annotations.forEach((annotation) => {
26155
+ const cardElement = trackedChangeCardElementsRef.current.get(
26156
+ `${pageIndex}:${annotation.id}`
26157
+ );
26158
+ if (!cardElement) {
26159
+ return;
26160
+ }
26161
+ const measuredHeight = Math.round(
26162
+ cardElement.getBoundingClientRect().height
26163
+ );
26164
+ if (!Number.isFinite(measuredHeight) || measuredHeight <= 0) {
26165
+ return;
26166
+ }
26167
+ pageHeights.set(
26168
+ annotation.id,
26169
+ Math.max(TRACKED_CHANGE_GUTTER_CARD_MIN_HEIGHT_PX, measuredHeight)
26170
+ );
26171
+ });
26172
+ return pageHeights;
26173
+ }
26174
+ );
25953
26175
  setTrackedChangeCardHeightsByPage((current) => {
25954
26176
  if (current.length === nextHeightsByPage.length) {
25955
26177
  let equal = true;
@@ -26448,9 +26670,14 @@ function DocxEditorViewer({
26448
26670
  },
26449
26671
  []
26450
26672
  );
26673
+ const storedDocumentPageCountForLatch = editor.model.metadata.documentPageCount;
26674
+ const latchTargetPageCount = Number.isFinite(storedDocumentPageCountForLatch) ? Math.max(
26675
+ collectDocxHardPageBreakStartNodeIndexes(editor.model).size + 1,
26676
+ Math.round(storedDocumentPageCountForLatch)
26677
+ ) : void 0;
26451
26678
  const nextMeasuredBodyFooterOverlapLatchState = resolveMeasuredBodyFooterOverlapLatchState({
26452
26679
  pageCount,
26453
- targetPageCount: editor.model.metadata.documentPageCount,
26680
+ targetPageCount: latchTargetPageCount,
26454
26681
  overlappingPageIndexes,
26455
26682
  previousSignature: measuredBodyFooterOverlapCandidateRef.current.signature,
26456
26683
  previousConsecutivePasses: measuredBodyFooterOverlapCandidateRef.current.consecutivePasses,
@@ -33221,7 +33448,10 @@ function DocxEditorViewer({
33221
33448
  Math.round(options?.pageFlowTopPx ?? 0)
33222
33449
  );
33223
33450
  const bodyParagraphOriginLeftPx = interactiveBodyFloatingPageOriginPx ? paragraphPageLayout.marginsPx.left : void 0;
33224
- const bodyParagraphOriginTopPx = interactiveBodyFloatingPageOriginPx ? paragraphPageLayout.marginsPx.top + paragraphPageFlowTopPx : void 0;
33451
+ const bodyParagraphOriginTopPx = interactiveBodyFloatingPageOriginPx ? paragraphActsAsPageAnchoredCoverOverlayHost(
33452
+ paragraph,
33453
+ paragraphPageLayout
33454
+ ) ? paragraphPageLayout.marginsPx.top + paragraphPageFlowTopPx : 0 : void 0;
33225
33455
  const resizedWidthPxByImageIndex = /* @__PURE__ */ new Map();
33226
33456
  const resizedHeightPxByImageIndex = /* @__PURE__ */ new Map();
33227
33457
  const movePreviewByImageIndex = /* @__PURE__ */ new Map();
@@ -33276,7 +33506,8 @@ function DocxEditorViewer({
33276
33506
  const hasFixedPositionWrappedImage = paragraph.children.some(
33277
33507
  (child) => child.type === "image" && isFixedPositionWrappedFloatingImage(child)
33278
33508
  );
33279
- if ((trackedChangesEnabled || useSpecialTabLayout) && !hasFixedPositionWrappedImage) {
33509
+ const paragraphHasCommentAnchors = commentsEnabled && /commentRange|commentReference/i.test(paragraph.sourceXml ?? "");
33510
+ if ((trackedChangesEnabled || useSpecialTabLayout || paragraphHasCommentAnchors) && !hasFixedPositionWrappedImage) {
33280
33511
  return renderParagraphRuns(
33281
33512
  paragraph,
33282
33513
  keyPrefix,
@@ -33289,6 +33520,7 @@ function DocxEditorViewer({
33289
33520
  void 0,
33290
33521
  {
33291
33522
  showTrackedChanges: trackedChangesEnabled,
33523
+ showCommentHighlights: commentsEnabled,
33292
33524
  numberingDefinitions: editor.model.metadata.numberingDefinitions,
33293
33525
  tocLinkColorByLevel,
33294
33526
  paragraphOriginLeftPx: bodyParagraphOriginLeftPx,
@@ -36108,9 +36340,10 @@ function DocxEditorViewer({
36108
36340
  marginTop: (typeof baseParagraphStyle.marginTop === "number" ? baseParagraphStyle.marginTop : 0) - (pageAnchoredCoverOverlayParagraph ? resolvedPageLayout.marginsPx.top : 0),
36109
36341
  marginLeft: -resolvedPageLayout.marginsPx.left,
36110
36342
  marginRight: -resolvedPageLayout.marginsPx.right,
36111
- minHeight: 0,
36112
- height: 0,
36113
- lineHeight: 0,
36343
+ // A cover-overlay host is remapped to the page surface and must
36344
+ // not displace flow; an ordinary anchor host still occupies its
36345
+ // one-line paragraph box (Word semantics).
36346
+ ...pageAnchoredCoverOverlayParagraph ? { minHeight: 0, height: 0, lineHeight: 0 } : void 0,
36114
36347
  overflow: "visible"
36115
36348
  } : requiresPageAbsoluteContext ? { position: "static" } : requiresLocalAbsoluteContext ? { position: "relative" } : hasDualWrappedFloatingImage ? { position: "relative" } : void 0,
36116
36349
  // Carry the paragraph's run font on the editable host so text typed into
@@ -39631,44 +39864,25 @@ ${currentText.slice(end)}`;
39631
39864
  }
39632
39865
  }
39633
39866
  ) : null,
39634
- pageNodeSegmentsByPage.map((pageNodeSegments, pageIndex) => {
39867
+ pageStackVirtualSpacers.before.heightPx > 0 ? /* @__PURE__ */ jsx(
39868
+ "div",
39869
+ {
39870
+ "aria-hidden": "true",
39871
+ "data-docx-page-window-spacer": "before",
39872
+ style: {
39873
+ height: pageStackVirtualSpacers.before.heightPx,
39874
+ width: pageStackVirtualSpacers.before.widthPx,
39875
+ margin: "0 auto",
39876
+ pointerEvents: "none",
39877
+ visibility: hideDocumentUntilPaginationSettled ? "hidden" : void 0
39878
+ }
39879
+ }
39880
+ ) : null,
39881
+ visiblePageIndexes.map((pageIndex) => {
39882
+ const pageNodeSegments = pageNodeSegmentsByPage[pageIndex] ?? [];
39635
39883
  const pageInfo = pageSectionInfoByIndex[pageIndex];
39636
39884
  const pageLayout = pageInfo?.layout ?? documentLayout;
39637
- const pageVisible = isPageVisible(pageIndex);
39638
39885
  const pageWrapperWidthPx = showTrackedChangeGutter ? pageLayout.pageWidthPx + TRACKED_CHANGE_GUTTER_WIDTH_PX : pageLayout.pageWidthPx;
39639
- if (!pageVisible) {
39640
- const placeholderBackgroundColor = pageBackgroundColor ?? editor.model.metadata.documentBackgroundColor ?? pageSurfaceBaseStyle.backgroundColor;
39641
- return /* @__PURE__ */ jsx(
39642
- "div",
39643
- {
39644
- "data-docx-page-wrapper": "true",
39645
- "data-docx-page-index": pageIndex,
39646
- "data-index": pageIndex,
39647
- style: {
39648
- width: pageWrapperWidthPx,
39649
- margin: "0 auto",
39650
- visibility: hideDocumentUntilPaginationSettled ? "hidden" : void 0
39651
- },
39652
- children: /* @__PURE__ */ jsx(
39653
- "div",
39654
- {
39655
- "data-docx-page-placeholder": "true",
39656
- ref: pagePlaceholderRefForIndex(pageIndex),
39657
- style: {
39658
- ...pageSurfaceBaseStyle,
39659
- ...pageMarginPaddingStyle(pageLayout.marginsPx),
39660
- height: pageLayout.pageHeightPx,
39661
- minHeight: pageLayout.pageHeightPx,
39662
- width: pageLayout.pageWidthPx,
39663
- backgroundColor: placeholderBackgroundColor,
39664
- pointerEvents: "none"
39665
- }
39666
- }
39667
- )
39668
- },
39669
- `page-${pageIndex}`
39670
- );
39671
- }
39672
39886
  const pageContentWidthPx = Math.max(
39673
39887
  120,
39674
39888
  pageLayout.pageWidthPx - pageLayout.marginsPx.left - pageLayout.marginsPx.right
@@ -39840,6 +40054,10 @@ ${currentText.slice(end)}`;
39840
40054
  width: pageWrapperWidthPx,
39841
40055
  minHeight: pageLayout.pageHeightPx,
39842
40056
  margin: "0 auto",
40057
+ // Isolate per-page layout/style recalculation so scrolling and
40058
+ // edits on one page don't force whole-document layout passes.
40059
+ // (No paint containment: floats and gutter cards may overhang.)
40060
+ contain: "layout style",
39843
40061
  visibility: hideDocumentUntilPaginationSettled ? "hidden" : void 0
39844
40062
  },
39845
40063
  children: [
@@ -40090,9 +40308,6 @@ ${currentText.slice(end)}`;
40090
40308
  segments: [segment]
40091
40309
  });
40092
40310
  });
40093
- if (typeof window !== "undefined" && window.__docxDebugGroups) {
40094
- console.log("[groups]", pageIndex, JSON.stringify(sectionGroups.map((g) => ({ s: g.sectionIndex, n: g.segments.map((x) => x.nodeIndex) }))));
40095
- }
40096
40311
  return sectionGroups.map((group, groupIndex) => {
40097
40312
  const sectionColumns = sectionColumnsBySectionIndex[group.sectionIndex];
40098
40313
  const isLastGroupOnPage = groupIndex === sectionGroups.length - 1;
@@ -40917,10 +41132,10 @@ ${currentText.slice(end)}`;
40917
41132
  overflow: "visible"
40918
41133
  },
40919
41134
  children: pageTrackedChanges.map((entry) => {
40920
- const accentColor = trackedChangeAccentColor(
40921
- entry.change.kind,
41135
+ const accentColor = entry.annotation.trackedChange ? trackedChangeAccentColor(
41136
+ entry.annotation.trackedChange.kind,
40922
41137
  editor.documentTheme
40923
- );
41138
+ ) : commentAccentColor(editor.documentTheme);
40924
41139
  const cardCenterY = clampNumber(
40925
41140
  Math.round(entry.top + entry.heightPx / 2),
40926
41141
  8,
@@ -40981,12 +41196,12 @@ ${currentText.slice(end)}`;
40981
41196
  )
40982
41197
  ]
40983
41198
  },
40984
- `tracked-connector-${pageIndex}-${entry.change.id}`
41199
+ `tracked-connector-${pageIndex}-${entry.annotation.id}`
40985
41200
  );
40986
41201
  })
40987
41202
  }
40988
41203
  ),
40989
- !hasTrackedChanges && pageIndex === 0 ? /* @__PURE__ */ jsx(
41204
+ (!trackedChangesEnabled || !hasTrackedChanges) && (!commentsEnabled || !hasComments) && pageIndex === 0 ? /* @__PURE__ */ jsx(
40990
41205
  "p",
40991
41206
  {
40992
41207
  style: {
@@ -40999,19 +41214,21 @@ ${currentText.slice(end)}`;
40999
41214
  lineHeight: 1.35,
41000
41215
  color: "#94a3b8"
41001
41216
  },
41002
- children: "No edits found"
41217
+ children: trackedChangesEnabled && commentsEnabled ? "No edits or comments found" : commentsEnabled ? "No comments found" : "No edits found"
41003
41218
  }
41004
41219
  ) : null,
41005
41220
  pageTrackedChanges.map((entry) => {
41006
- const accentColor = trackedChangeAccentColor(
41007
- entry.change.kind,
41221
+ const trackedChange = entry.annotation.trackedChange;
41222
+ const comment = entry.annotation.comment;
41223
+ const accentColor = trackedChange ? trackedChangeAccentColor(
41224
+ trackedChange.kind,
41008
41225
  editor.documentTheme
41009
- );
41226
+ ) : commentAccentColor(editor.documentTheme);
41010
41227
  const formattedDate = formatTrackedChangeDate(
41011
- entry.change.date
41228
+ trackedChange?.date ?? comment?.date
41012
41229
  );
41013
- const kindLabel = trackedChangeKindLabel(entry.change.kind);
41014
- const snippet = normalizeTrackedChangeSnippet(entry.change.text) ?? (entry.change.kind === "format-change" || entry.change.kind === "paragraph-format-change" ? "Formatting" : "Change");
41230
+ const kindLabel = trackedChange ? trackedChangeKindLabel(trackedChange.kind) : comment?.resolved ? "Comment \xB7 Resolved" : comment?.parentId !== void 0 ? "Reply" : "Comment";
41231
+ const snippet = trackedChange ? normalizeTrackedChangeSnippet(trackedChange.text) ?? (trackedChange.kind === "format-change" || trackedChange.kind === "paragraph-format-change" ? "Formatting" : "Change") : comment?.text || "Comment";
41015
41232
  const cardWidthPx = Math.max(
41016
41233
  140,
41017
41234
  TRACKED_CHANGE_GUTTER_WIDTH_PX - TRACKED_CHANGE_GUTTER_CARD_LEFT_PX - TRACKED_CHANGE_GUTTER_CARD_RIGHT_PX
@@ -41063,7 +41280,7 @@ ${currentText.slice(end)}`;
41063
41280
  fontWeight: 700,
41064
41281
  lineHeight: 1.25
41065
41282
  },
41066
- children: entry.change.author?.trim() || "Unknown author"
41283
+ children: (trackedChange?.author ?? comment?.author)?.trim() || "Unknown author"
41067
41284
  }
41068
41285
  ),
41069
41286
  formattedDate ? /* @__PURE__ */ jsx(
@@ -41082,6 +41299,23 @@ ${currentText.slice(end)}`;
41082
41299
  ]
41083
41300
  }
41084
41301
  ),
41302
+ comment?.anchorText ? /* @__PURE__ */ jsxs(
41303
+ "p",
41304
+ {
41305
+ style: {
41306
+ margin: "2px 0 0",
41307
+ fontSize: 11,
41308
+ lineHeight: 1.3,
41309
+ fontStyle: "italic",
41310
+ color: editor.documentTheme === "dark" ? "#94a3b8" : "#6b7280"
41311
+ },
41312
+ children: [
41313
+ "\u201C",
41314
+ comment.anchorText,
41315
+ "\u201D"
41316
+ ]
41317
+ }
41318
+ ) : null,
41085
41319
  /* @__PURE__ */ jsxs(
41086
41320
  "p",
41087
41321
  {
@@ -41103,8 +41337,8 @@ ${currentText.slice(end)}`;
41103
41337
  ]
41104
41338
  }
41105
41339
  );
41106
- const renderedCard = renderTrackedChangeCard ? renderTrackedChangeCard({
41107
- change: entry.change,
41340
+ const renderedCard = trackedChange ? renderTrackedChangeCard ? renderTrackedChangeCard({
41341
+ change: trackedChange,
41108
41342
  kindLabel,
41109
41343
  snippet,
41110
41344
  formattedDate,
@@ -41112,12 +41346,21 @@ ${currentText.slice(end)}`;
41112
41346
  documentTheme: editor.documentTheme,
41113
41347
  pageIndex,
41114
41348
  style: cardStyle
41349
+ }) : defaultCard : comment && renderCommentCard ? renderCommentCard({
41350
+ comment,
41351
+ snippet,
41352
+ formattedDate,
41353
+ accentColor,
41354
+ documentTheme: editor.documentTheme,
41355
+ pageIndex,
41356
+ style: cardStyle
41115
41357
  }) : defaultCard;
41116
41358
  return /* @__PURE__ */ jsx(
41117
41359
  "div",
41118
41360
  {
41361
+ "data-docx-gutter-annotation": trackedChange ? "tracked-change" : "comment",
41119
41362
  ref: (element) => {
41120
- const elementKey = `${pageIndex}:${entry.change.id}`;
41363
+ const elementKey = `${pageIndex}:${entry.annotation.id}`;
41121
41364
  if (element) {
41122
41365
  trackedChangeCardElementsRef.current.set(
41123
41366
  elementKey,
@@ -41132,7 +41375,7 @@ ${currentText.slice(end)}`;
41132
41375
  style: cardContainerStyle,
41133
41376
  children: renderedCard
41134
41377
  },
41135
- `tracked-card-${pageIndex}-${entry.change.id}`
41378
+ `tracked-card-${pageIndex}-${entry.annotation.id}`
41136
41379
  );
41137
41380
  })
41138
41381
  ]
@@ -41143,6 +41386,20 @@ ${currentText.slice(end)}`;
41143
41386
  `page-${pageIndex}`
41144
41387
  );
41145
41388
  }),
41389
+ pageStackVirtualSpacers.after.heightPx > 0 ? /* @__PURE__ */ jsx(
41390
+ "div",
41391
+ {
41392
+ "aria-hidden": "true",
41393
+ "data-docx-page-window-spacer": "after",
41394
+ style: {
41395
+ height: pageStackVirtualSpacers.after.heightPx,
41396
+ width: pageStackVirtualSpacers.after.widthPx,
41397
+ margin: "0 auto",
41398
+ pointerEvents: "none",
41399
+ visibility: hideDocumentUntilPaginationSettled ? "hidden" : void 0
41400
+ }
41401
+ }
41402
+ ) : null,
41146
41403
  !isReadOnly ? (() => {
41147
41404
  const hasCustomContextMenuState = Boolean(contextMenuState);
41148
41405
  const hasTableContextMenuState = Boolean(tableContextMenuState);
@@ -43037,16 +43294,20 @@ function useDocxModel(file) {
43037
43294
  }
43038
43295
  const docxFile = file;
43039
43296
  let isCurrent = true;
43297
+ const abortController = new AbortController();
43040
43298
  async function load() {
43041
43299
  setState({ isLoading: true });
43042
43300
  try {
43043
- const pkg = await parseDocx(docxFile);
43301
+ const { model } = await importDocxBuffer(docxFile, {
43302
+ signal: abortController.signal,
43303
+ transferBuffer: false
43304
+ });
43044
43305
  if (!isCurrent) {
43045
43306
  return;
43046
43307
  }
43047
43308
  setState({
43048
43309
  isLoading: false,
43049
- model: await buildDocModel(pkg)
43310
+ model
43050
43311
  });
43051
43312
  } catch (error) {
43052
43313
  if (!isCurrent) {
@@ -43061,6 +43322,7 @@ function useDocxModel(file) {
43061
43322
  void load();
43062
43323
  return () => {
43063
43324
  isCurrent = false;
43325
+ abortController.abort();
43064
43326
  };
43065
43327
  }, [file]);
43066
43328
  return state;
@@ -43400,6 +43662,7 @@ export {
43400
43662
  updateTableCellParagraphTextRecursive,
43401
43663
  updateTableCellText,
43402
43664
  useDocxBorders,
43665
+ useDocxComments,
43403
43666
  useDocxDocumentTheme,
43404
43667
  useDocxEditor,
43405
43668
  useDocxFormFields,