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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.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-XLGR633U.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";
334
- }
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;
304
+ function performanceNow() {
305
+ return typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
348
306
  }
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-IT7QNDVM.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-IT7QNDVM.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();
@@ -7207,7 +6990,7 @@ function splitParagraphAtExplicitColumnBreaks(paragraph) {
7207
6990
  }
7208
6991
  appendCurrentSegment();
7209
6992
  return paragraphChildren.map((children, segmentIndex) => {
7210
- const nextStyle = cloneParagraphStyle2(paragraph.style);
6993
+ const nextStyle = cloneParagraphStyle(paragraph.style);
7211
6994
  if (segmentIndex > 0 && nextStyle?.numbering) {
7212
6995
  nextStyle.numbering = void 0;
7213
6996
  }
@@ -8916,9 +8699,7 @@ function estimateTabLeaderWrappedLineCountForParagraph(paragraph, maxLineWidthPx
8916
8699
  leadingSegments,
8917
8700
  paragraphBaseFontPx
8918
8701
  );
8919
- const tabStopPositionsPx = (paragraph.style?.tabStops ?? []).map((tabStop) => twipsToPixels(tabStop.positionTwips)).filter(
8920
- (positionPx) => Number.isFinite(positionPx) && positionPx > 0
8921
- ).sort((left, right) => left - right);
8702
+ const tabStopPositionsPx = resolveParagraphFirstLineLeftTabStopsPx(paragraph);
8922
8703
  const explicitLeadingTabStopPx = tableOfContentsLeadingLeftTabStopPx(paragraph);
8923
8704
  const leadingReservationWidthPx = leadingSegments.length === 0 ? 0 : Number.isFinite(explicitLeadingTabStopPx) && explicitLeadingTabStopPx > 0 ? Math.max(
8924
8705
  leadingTextWidthPx,
@@ -12784,10 +12565,7 @@ function tableOfContentsLeadingLeftTabStopPx(paragraph) {
12784
12565
  if (!isTableOfContentsParagraph(paragraph)) {
12785
12566
  return void 0;
12786
12567
  }
12787
- const leftTabStopPositionsPx = (paragraph.style?.tabStops ?? []).filter((tabStop) => tabStop.alignment === "left").map((tabStop) => twipsToPixels(tabStop.positionTwips)).filter(
12788
- (positionPx) => Number.isFinite(positionPx) && positionPx > 0
12789
- ).sort((left, right) => left - right);
12790
- return leftTabStopPositionsPx[0];
12568
+ return resolveParagraphFirstLineLeftTabStopsPx(paragraph)[0];
12791
12569
  }
12792
12570
  function paragraphContainsTabCharacter(paragraph) {
12793
12571
  return paragraph.children.some((child) => {
@@ -13837,6 +13615,81 @@ function resolveParagraphTrackedMarkup(paragraph) {
13837
13615
  setCacheEntry(paragraphTrackedMarkupBySourceXml, sourceXml, resolved);
13838
13616
  return resolved;
13839
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
+ }
13840
13693
  function instructionTextToPageFieldKind(rawInstruction) {
13841
13694
  const normalized = decodeXmlText(rawInstruction).replace(/\s+/g, " ").trim().toUpperCase();
13842
13695
  if (!normalized || normalized.includes("PAGEREF")) {
@@ -13883,11 +13736,16 @@ function paragraphPageFieldSequence(paragraph) {
13883
13736
  }
13884
13737
  return fields;
13885
13738
  }
13739
+ var pageFieldValueSequenceBySourceXml = /* @__PURE__ */ new Map();
13886
13740
  function paragraphPageFieldValueSequence(paragraph) {
13887
13741
  const xml = paragraph.sourceXml ?? "";
13888
13742
  if (!xml) {
13889
13743
  return [];
13890
13744
  }
13745
+ const cached = pageFieldValueSequenceBySourceXml.get(xml);
13746
+ if (cached) {
13747
+ return cached;
13748
+ }
13891
13749
  const values = [];
13892
13750
  const fieldStack = [];
13893
13751
  const tokenPattern = /<w:fldSimple\b[^>]*\bw:instr="([^"]+)"[^>]*>[\s\S]*?<\/w:fldSimple>|<w:r\b[\s\S]*?<\/w:r>/gi;
@@ -13967,13 +13825,19 @@ function paragraphPageFieldValueSequence(paragraph) {
13967
13825
  fieldStack.pop();
13968
13826
  }
13969
13827
  }
13828
+ setCacheEntry(pageFieldValueSequenceBySourceXml, xml, values);
13970
13829
  return values;
13971
13830
  }
13831
+ var styleRefFieldValueSequenceBySourceXml = /* @__PURE__ */ new Map();
13972
13832
  function paragraphStyleRefFieldValueSequence(paragraph) {
13973
13833
  const xml = paragraph.sourceXml ?? "";
13974
13834
  if (!xml) {
13975
13835
  return [];
13976
13836
  }
13837
+ const cached = styleRefFieldValueSequenceBySourceXml.get(xml);
13838
+ if (cached) {
13839
+ return cached;
13840
+ }
13977
13841
  const values = [];
13978
13842
  const fieldStack = [];
13979
13843
  const tokenPattern = /<w:fldSimple\b[^>]*\bw:instr="([^"]+)"[^>]*>[\s\S]*?<\/w:fldSimple>|<w:r\b[\s\S]*?<\/w:r>/gi;
@@ -14053,6 +13917,7 @@ function paragraphStyleRefFieldValueSequence(paragraph) {
14053
13917
  fieldStack.pop();
14054
13918
  }
14055
13919
  }
13920
+ setCacheEntry(styleRefFieldValueSequenceBySourceXml, xml, values);
14056
13921
  return values;
14057
13922
  }
14058
13923
  function normalizeStyleRefTarget(value) {
@@ -14187,6 +14052,7 @@ function renderParagraphRuns(paragraph, keyPrefix, documentTheme = "light", numb
14187
14052
  const showTrackedChanges = options?.showTrackedChanges === true;
14188
14053
  const showTrackedInlineMarkup = showTrackedChanges && options?.trackedMarkupMode !== "gutter";
14189
14054
  const trackedMarkup = showTrackedInlineMarkup ? resolveParagraphTrackedMarkup(paragraph) : void 0;
14055
+ const commentMarkup = options?.showCommentHighlights === true ? resolveParagraphCommentMarkup(paragraph) : void 0;
14190
14056
  const tocParagraphLevel = tableOfContentsLevel(paragraph);
14191
14057
  const tocLinkColor = tocParagraphLevel ? options?.tocLinkColorByLevel?.[tocParagraphLevel] : void 0;
14192
14058
  const floatingAnchorOriginCorrectionXPx = Number.isFinite(
@@ -14228,8 +14094,19 @@ function renderParagraphRuns(paragraph, keyPrefix, documentTheme = "light", numb
14228
14094
  });
14229
14095
  };
14230
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
+ };
14231
14108
  const consumeTrackedVisibleChild = (child) => {
14232
- if (!trackedMarkup) {
14109
+ if (!trackedMarkup && !commentMarkup) {
14233
14110
  return;
14234
14111
  }
14235
14112
  if (child.type === "form-field") {
@@ -14398,20 +14275,26 @@ function renderParagraphRuns(paragraph, keyPrefix, documentTheme = "light", numb
14398
14275
  const resolveFieldText = (value, preferredZone) => resolvePageFieldText(resolveStyleRefFieldText(value), preferredZone);
14399
14276
  const trackedLinkStyle = (style, trackedInlineChange) => {
14400
14277
  if (!isTableOfContentsParagraph(paragraph)) {
14401
- return trackedInlineStyle(
14402
- linkStyleToCss(style, documentTheme),
14403
- trackedInlineChange
14404
- );
14278
+ return {
14279
+ ...trackedInlineStyle(
14280
+ linkStyleToCss(style, documentTheme),
14281
+ trackedInlineChange
14282
+ ),
14283
+ ...currentCommentHighlightStyle()
14284
+ };
14405
14285
  }
14406
14286
  const base = runStyleToCss(style, documentTheme);
14407
- return trackedInlineStyle(
14408
- {
14409
- ...base,
14410
- color: tocLinkColor ? themedRunColor(tocLinkColor, documentTheme) : "inherit",
14411
- textDecoration: "none"
14412
- },
14413
- trackedInlineChange
14414
- );
14287
+ return {
14288
+ ...trackedInlineStyle(
14289
+ {
14290
+ ...base,
14291
+ color: tocLinkColor ? themedRunColor(tocLinkColor, documentTheme) : "inherit",
14292
+ textDecoration: "none"
14293
+ },
14294
+ trackedInlineChange
14295
+ ),
14296
+ ...currentCommentHighlightStyle()
14297
+ };
14415
14298
  };
14416
14299
  const usesExternalHorizontalAnchorOrigin = (image) => {
14417
14300
  const horizontalRelativeTo = image.floating?.horizontalRelativeTo?.trim().toLowerCase();
@@ -14458,10 +14341,13 @@ function renderParagraphRuns(paragraph, keyPrefix, documentTheme = "light", numb
14458
14341
  trackTextAdvance(text, child.style);
14459
14342
  return;
14460
14343
  }
14461
- const trackedStyle = trackedInlineStyle(
14462
- runStyleToCss(child.style, documentTheme),
14463
- trackedInlineChange
14464
- );
14344
+ const trackedStyle = {
14345
+ ...trackedInlineStyle(
14346
+ runStyleToCss(child.style, documentTheme),
14347
+ trackedInlineChange
14348
+ ),
14349
+ ...currentCommentHighlightStyle()
14350
+ };
14465
14351
  if (text === " " && !useTabLeaderLayout && !useAnchoredTabLayout) {
14466
14352
  target.push(
14467
14353
  /* @__PURE__ */ jsx("span", { style: tabTextStyle(child.style, trackedStyle), children: "\xA0" }, key)
@@ -14717,10 +14603,13 @@ function renderParagraphRuns(paragraph, keyPrefix, documentTheme = "light", numb
14717
14603
  }
14718
14604
  return;
14719
14605
  }
14720
- const textStyle = trackedInlineStyle(
14721
- runStyleToCss(child.style, documentTheme),
14722
- trackedInlineChange
14723
- );
14606
+ const textStyle = {
14607
+ ...trackedInlineStyle(
14608
+ runStyleToCss(child.style, documentTheme),
14609
+ trackedInlineChange
14610
+ ),
14611
+ ...currentCommentHighlightStyle()
14612
+ };
14724
14613
  const noteLabel = noteMarkerLabel(
14725
14614
  child.noteReference,
14726
14615
  safeNoteMarkerIndexes.footnote,
@@ -15685,23 +15574,23 @@ function textWithListType(text, listType) {
15685
15574
  const normalized = stripListPrefix(text);
15686
15575
  return listType === "unordered" ? `\u2022 ${normalized}` : `1. ${normalized}`;
15687
15576
  }
15688
- function cloneParagraphBorderStyle2(border) {
15577
+ function cloneParagraphBorderStyle(border) {
15689
15578
  return border ? { ...border } : void 0;
15690
15579
  }
15691
- function cloneParagraphBorderSet2(borders) {
15580
+ function cloneParagraphBorderSet(borders) {
15692
15581
  if (!borders) {
15693
15582
  return void 0;
15694
15583
  }
15695
15584
  return {
15696
- top: cloneParagraphBorderStyle2(borders.top),
15697
- right: cloneParagraphBorderStyle2(borders.right),
15698
- bottom: cloneParagraphBorderStyle2(borders.bottom),
15699
- left: cloneParagraphBorderStyle2(borders.left),
15700
- between: cloneParagraphBorderStyle2(borders.between),
15701
- 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)
15702
15591
  };
15703
15592
  }
15704
- function cloneParagraphStyle2(style) {
15593
+ function cloneParagraphStyle(style) {
15705
15594
  if (!style) {
15706
15595
  return void 0;
15707
15596
  }
@@ -15711,12 +15600,12 @@ function cloneParagraphStyle2(style) {
15711
15600
  spacing: style.spacing ? { ...style.spacing } : void 0,
15712
15601
  indent: style.indent ? { ...style.indent } : void 0,
15713
15602
  tabStops: style.tabStops ? style.tabStops.map((tabStop) => ({ ...tabStop })) : void 0,
15714
- borders: cloneParagraphBorderSet2(style.borders),
15603
+ borders: cloneParagraphBorderSet(style.borders),
15715
15604
  dropCap: style.dropCap ? { ...style.dropCap } : void 0
15716
15605
  };
15717
15606
  }
15718
15607
  function splitParagraphStyleWithDefaultSpacing(style, sourceXml) {
15719
- const clonedStyle = cloneParagraphStyle2(style);
15608
+ const clonedStyle = cloneParagraphStyle(style);
15720
15609
  if (sourceXml || clonedStyle?.styleId) {
15721
15610
  return clonedStyle;
15722
15611
  }
@@ -15735,7 +15624,7 @@ function splitParagraphStyleWithDefaultSpacing(style, sourceXml) {
15735
15624
  }
15736
15625
  };
15737
15626
  }
15738
- function cloneTableBoxSpacing2(spacing) {
15627
+ function cloneTableBoxSpacing(spacing) {
15739
15628
  if (!spacing) {
15740
15629
  return void 0;
15741
15630
  }
@@ -15746,7 +15635,7 @@ function cloneTableBoxSpacing2(spacing) {
15746
15635
  leftTwips: spacing.leftTwips
15747
15636
  };
15748
15637
  }
15749
- function cloneTableBorderStyle2(border) {
15638
+ function cloneTableBorderStyle(border) {
15750
15639
  if (!border) {
15751
15640
  return void 0;
15752
15641
  }
@@ -15756,19 +15645,19 @@ function cloneTableBorderStyle2(border) {
15756
15645
  sizeEighthPt: border.sizeEighthPt
15757
15646
  };
15758
15647
  }
15759
- function cloneTableBorderSet2(borders) {
15648
+ function cloneTableBorderSet(borders) {
15760
15649
  if (!borders) {
15761
15650
  return void 0;
15762
15651
  }
15763
15652
  return {
15764
- top: cloneTableBorderStyle2(borders.top),
15765
- right: cloneTableBorderStyle2(borders.right),
15766
- bottom: cloneTableBorderStyle2(borders.bottom),
15767
- left: cloneTableBorderStyle2(borders.left),
15768
- insideH: cloneTableBorderStyle2(borders.insideH),
15769
- insideV: cloneTableBorderStyle2(borders.insideV),
15770
- tl2br: cloneTableBorderStyle2(borders.tl2br),
15771
- 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)
15772
15661
  };
15773
15662
  }
15774
15663
  function cloneTableCellStyle(style) {
@@ -15777,8 +15666,8 @@ function cloneTableCellStyle(style) {
15777
15666
  }
15778
15667
  return {
15779
15668
  ...style,
15780
- marginTwips: cloneTableBoxSpacing2(style.marginTwips),
15781
- borders: cloneTableBorderSet2(style.borders)
15669
+ marginTwips: cloneTableBoxSpacing(style.marginTwips),
15670
+ borders: cloneTableBorderSet(style.borders)
15782
15671
  };
15783
15672
  }
15784
15673
  function cloneTableRowStyle(style) {
@@ -15793,7 +15682,7 @@ function createEmptyParagraphFromTemplate(template) {
15793
15682
  const nextTextStyle = cloneTextStyle2(firstRunStyle(template));
15794
15683
  return {
15795
15684
  type: "paragraph",
15796
- style: cloneParagraphStyle2(template?.style),
15685
+ style: cloneParagraphStyle(template?.style),
15797
15686
  children: [
15798
15687
  {
15799
15688
  type: "text",
@@ -15862,7 +15751,7 @@ function shiftListIndent(indent, levelDelta) {
15862
15751
  };
15863
15752
  }
15864
15753
  function ensurePrefixListIndent(paragraph) {
15865
- const clonedStyle = cloneParagraphStyle2(paragraph.style) ?? {};
15754
+ const clonedStyle = cloneParagraphStyle(paragraph.style) ?? {};
15866
15755
  const existingIndent = clonedStyle.indent;
15867
15756
  const hasMeaningfulLeftIndent = Number.isFinite(existingIndent?.leftTwips) && Math.abs(existingIndent?.leftTwips ?? 0) > 0;
15868
15757
  const hasMeaningfulFirstLine = Number.isFinite(existingIndent?.firstLineTwips) && Math.abs(existingIndent?.firstLineTwips ?? 0) > 0;
@@ -15882,7 +15771,7 @@ function ensurePrefixListIndent(paragraph) {
15882
15771
  return true;
15883
15772
  }
15884
15773
  function clearAutoPrefixListIndent(paragraph) {
15885
- const clonedStyle = cloneParagraphStyle2(paragraph.style);
15774
+ const clonedStyle = cloneParagraphStyle(paragraph.style);
15886
15775
  const existingIndent = clonedStyle?.indent;
15887
15776
  if (!clonedStyle || !existingIndent) {
15888
15777
  return false;
@@ -16009,11 +15898,22 @@ function parseEmbeddedTableRuntimeKey(tableRuntimeKey) {
16009
15898
  descendants
16010
15899
  };
16011
15900
  }
15901
+ var columnWidthsByTable = /* @__PURE__ */ new WeakMap();
16012
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) {
16013
15914
  const gridWidths = table.style?.columnWidthsTwips;
16014
15915
  const rowDerivedWidths = deriveColumnWidthsFromTableRows(table, columnCount);
16015
15916
  if (gridWidths && gridWidths.length === columnCount) {
16016
- console.log("[colw]", columnCount, gridWidths.length, !!rowDerivedWidths, gridConflictsWithRowWidths(table, gridWidths));
16017
15917
  if (rowDerivedWidths && rowDerivedWidths.length > 0 && gridConflictsWithRowWidths(table, gridWidths)) {
16018
15918
  return rowDerivedWidths;
16019
15919
  }
@@ -16500,7 +16400,7 @@ function applyParagraphBorderPresetForRangeEntry(borders, preset, remove, index,
16500
16400
  if (total <= 1) {
16501
16401
  return applyParagraphBorderPreset(borders, preset, remove);
16502
16402
  }
16503
- const nextBorders = cloneParagraphBorderSet2(borders) ?? {};
16403
+ const nextBorders = cloneParagraphBorderSet(borders) ?? {};
16504
16404
  const visibleBorder = toolbarParagraphBorderStyle(
16505
16405
  resolvePreferredParagraphBorder(nextBorders)
16506
16406
  );
@@ -16581,7 +16481,7 @@ function tableBorderPresetState(borders, selectedCellBorders) {
16581
16481
  };
16582
16482
  }
16583
16483
  function applyParagraphBorderPreset(borders, preset, remove = false) {
16584
- const nextBorders = cloneParagraphBorderSet2(borders) ?? {};
16484
+ const nextBorders = cloneParagraphBorderSet(borders) ?? {};
16585
16485
  const visibleBorder = toolbarParagraphBorderStyle(
16586
16486
  resolvePreferredParagraphBorder(nextBorders)
16587
16487
  );
@@ -16637,7 +16537,7 @@ function applyParagraphBorderPreset(borders, preset, remove = false) {
16637
16537
  return nextBorders;
16638
16538
  }
16639
16539
  function applyTableBorderPreset(borders, preset, remove = false) {
16640
- const nextBorders = cloneTableBorderSet2(borders) ?? {};
16540
+ const nextBorders = cloneTableBorderSet(borders) ?? {};
16641
16541
  const visibleBorder = toolbarTableBorderStyle(
16642
16542
  resolvePreferredTableBorder(nextBorders)
16643
16543
  );
@@ -17554,6 +17454,127 @@ function collectTrackedChangesFromModel(model) {
17554
17454
  });
17555
17455
  return trackedChanges;
17556
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
+ }
17557
17578
  function trackedChangeKindLabel(kind) {
17558
17579
  switch (kind) {
17559
17580
  case "insertion":
@@ -17602,15 +17623,15 @@ function trackedChangeAccentColor(kind, documentTheme) {
17602
17623
  return palette.format;
17603
17624
  }
17604
17625
  }
17605
- function trackedChangeSortTuple(change) {
17606
- if (change.location.kind === "paragraph") {
17607
- return [change.location.nodeIndex, 0, 0, 0];
17626
+ function gutterAnnotationSortTuple(location) {
17627
+ if (location.kind === "paragraph") {
17628
+ return [location.nodeIndex, 0, 0, 0];
17608
17629
  }
17609
17630
  return [
17610
- change.location.tableIndex,
17611
- change.location.rowIndex,
17612
- change.location.cellIndex,
17613
- change.location.paragraphIndex
17631
+ location.tableIndex,
17632
+ location.rowIndex,
17633
+ location.cellIndex,
17634
+ location.paragraphIndex
17614
17635
  ];
17615
17636
  }
17616
17637
  function trackedChangeBelongsToPageSegments(location, pageSegments) {
@@ -17629,10 +17650,10 @@ function trackedChangeBelongsToPageSegments(location, pageSegments) {
17629
17650
  return location.rowIndex >= segment.tableRowRange.startRowIndex && location.rowIndex < segment.tableRowRange.endRowIndex;
17630
17651
  });
17631
17652
  }
17632
- function resolveTrackedChangePageIndex(change, pageNodeSegmentsByPage) {
17653
+ function resolveGutterAnnotationPageIndex(location, pageNodeSegmentsByPage) {
17633
17654
  for (let pageIndex = 0; pageIndex < pageNodeSegmentsByPage.length; pageIndex += 1) {
17634
17655
  if (trackedChangeBelongsToPageSegments(
17635
- change.location,
17656
+ location,
17636
17657
  pageNodeSegmentsByPage[pageIndex] ?? []
17637
17658
  )) {
17638
17659
  return pageIndex;
@@ -17687,30 +17708,30 @@ function estimateTrackedChangeCardHeight(change) {
17687
17708
  const lines = Math.max(1, Math.ceil(snippet.length / 30));
17688
17709
  return Math.max(TRACKED_CHANGE_GUTTER_CARD_MIN_HEIGHT_PX, 34 + lines * 14);
17689
17710
  }
17690
- function layoutTrackedChangesForPage(changes, anchorByChangeId, cardHeightsByChangeId, pageWidthPx, pageHeightPx) {
17691
- if (changes.length === 0) {
17711
+ function layoutTrackedChangesForPage(annotations, anchorByChangeId, cardHeightsByChangeId, pageWidthPx, pageHeightPx) {
17712
+ if (annotations.length === 0) {
17692
17713
  return [];
17693
17714
  }
17694
17715
  const fallbackStride = Math.max(
17695
17716
  18,
17696
- pageHeightPx / Math.max(1, changes.length + 1)
17717
+ pageHeightPx / Math.max(1, annotations.length + 1)
17697
17718
  );
17698
- const withAnchors = changes.map((change, index) => {
17719
+ const withAnchors = annotations.map((annotation, index) => {
17699
17720
  const defaultAnchor = Math.min(
17700
17721
  Math.max(10, Math.round((index + 1) * fallbackStride)),
17701
17722
  Math.max(10, pageHeightPx - 10)
17702
17723
  );
17703
- const anchorPoint = anchorByChangeId.get(change.id);
17724
+ const anchorPoint = anchorByChangeId.get(annotation.id);
17704
17725
  const anchorY = anchorPoint?.y ?? defaultAnchor;
17705
17726
  const anchorX = anchorPoint?.x ?? Math.max(10, pageWidthPx - 10);
17706
- const measuredHeightPx = cardHeightsByChangeId?.get(change.id);
17707
- 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;
17708
17729
  const heightPx = Number.isFinite(measuredHeightPx) && measuredHeightPx > 0 ? Math.max(
17709
17730
  TRACKED_CHANGE_GUTTER_CARD_MIN_HEIGHT_PX,
17710
17731
  Math.round(measuredHeightPx)
17711
17732
  ) : estimatedHeightPx;
17712
17733
  return {
17713
- change,
17734
+ annotation,
17714
17735
  anchorX: clampNumber(
17715
17736
  Math.round(anchorX),
17716
17737
  10,
@@ -18741,10 +18762,14 @@ function useDocxEditor(options = {}) {
18741
18762
  const [isImporting, setIsImporting] = React.useState(false);
18742
18763
  const [documentTheme, setDocumentThemeState] = React.useState(options.initialDocumentTheme ?? "light");
18743
18764
  const [showTrackedChanges, setShowTrackedChangesState] = React.useState(options.initialShowTrackedChanges ?? false);
18765
+ const [showComments, setShowCommentsState] = React.useState(
18766
+ options.initialShowComments ?? false
18767
+ );
18744
18768
  const [paginationInfo, setPaginationInfo] = React.useState({
18745
18769
  currentPage: 1,
18746
18770
  totalPages: 1
18747
18771
  });
18772
+ const activeImportAbortControllerRef = React.useRef(void 0);
18748
18773
  const [history, setHistory] = React.useState({
18749
18774
  past: [],
18750
18775
  future: []
@@ -19057,6 +19082,9 @@ function useDocxEditor(options = {}) {
19057
19082
  () => collectTrackedChangesFromModel(model),
19058
19083
  [model]
19059
19084
  );
19085
+ const comments = React.useMemo(() => collectCommentsFromModel(model), [
19086
+ model
19087
+ ]);
19060
19088
  const hasUnorderedList = selectedListType === "unordered";
19061
19089
  const hasOrderedList = selectedListType === "ordered";
19062
19090
  const canUndo = history.past.length > 0;
@@ -19077,6 +19105,15 @@ function useDocxEditor(options = {}) {
19077
19105
  const toggleShowTrackedChanges = React.useCallback(() => {
19078
19106
  setShowTrackedChangesState((current) => !current);
19079
19107
  }, []);
19108
+ const setShowComments = React.useCallback(
19109
+ (nextShowComments) => {
19110
+ setShowCommentsState(nextShowComments);
19111
+ },
19112
+ []
19113
+ );
19114
+ const toggleShowComments = React.useCallback(() => {
19115
+ setShowCommentsState((current) => !current);
19116
+ }, []);
19080
19117
  const registerPendingExportModelTransformer = React.useCallback(
19081
19118
  (transformer) => {
19082
19119
  pendingExportModelTransformerRef.current = transformer;
@@ -19140,6 +19177,7 @@ function useDocxEditor(options = {}) {
19140
19177
  );
19141
19178
  React.useEffect(() => {
19142
19179
  return () => {
19180
+ activeImportAbortControllerRef.current?.abort();
19143
19181
  unloadEmbeddedFonts();
19144
19182
  };
19145
19183
  }, [unloadEmbeddedFonts]);
@@ -19275,6 +19313,8 @@ function useDocxEditor(options = {}) {
19275
19313
  );
19276
19314
  const importDocxFile = React.useCallback(
19277
19315
  async (file) => {
19316
+ activeImportAbortControllerRef.current?.abort();
19317
+ activeImportAbortControllerRef.current = void 0;
19278
19318
  if (!/\.docx?$/i.test(file.name)) {
19279
19319
  replaceDocumentWithImportError(
19280
19320
  file.name,
@@ -19285,11 +19325,50 @@ function useDocxEditor(options = {}) {
19285
19325
  setIsImporting(true);
19286
19326
  setImportError(void 0);
19287
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);
19288
19341
  try {
19342
+ markDocxImportPerformance(bufferStartMark);
19289
19343
  const buffer = await file.arrayBuffer();
19290
- 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);
19291
19364
  await loadEmbeddedFontsFromPackage(pkg);
19292
- const nextModel = await buildDocModel(pkg);
19365
+ markDocxImportPerformance(fontsEndMark);
19366
+ measureDocxImportPerformance(
19367
+ `${traceName}:fonts`,
19368
+ fontsStartMark,
19369
+ fontsEndMark
19370
+ );
19371
+ markDocxImportPerformance(stateStartMark);
19293
19372
  setModel(nextModel);
19294
19373
  setDocumentLoadNonce((current) => current + 1);
19295
19374
  setHistory({ past: [], future: [] });
@@ -19302,16 +19381,31 @@ function useDocxEditor(options = {}) {
19302
19381
  setSelectedFormFieldLocation(void 0);
19303
19382
  setImportError(void 0);
19304
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);
19305
19391
  } catch (error) {
19392
+ if (error instanceof Error && error.name === "AbortError" && activeImportAbortControllerRef.current !== importAbortController) {
19393
+ return;
19394
+ }
19306
19395
  const nextError = error instanceof Error ? error : new Error("Unknown error");
19307
19396
  replaceDocumentWithImportError(file.name, nextError);
19308
19397
  } finally {
19309
- setIsImporting(false);
19398
+ if (activeImportAbortControllerRef.current === importAbortController) {
19399
+ activeImportAbortControllerRef.current = void 0;
19400
+ setIsImporting(false);
19401
+ }
19310
19402
  }
19311
19403
  },
19312
19404
  [loadEmbeddedFontsFromPackage, replaceDocumentWithImportError]
19313
19405
  );
19314
19406
  const newDocument = React.useCallback(() => {
19407
+ activeImportAbortControllerRef.current?.abort();
19408
+ activeImportAbortControllerRef.current = void 0;
19315
19409
  unloadEmbeddedFonts();
19316
19410
  setModel(cloneDocModel(starterTemplateRef.current));
19317
19411
  setDocumentLoadNonce((current) => current + 1);
@@ -19924,7 +20018,7 @@ function useDocxEditor(options = {}) {
19924
20018
  return;
19925
20019
  }
19926
20020
  paragraph.style = {
19927
- ...cloneParagraphStyle2(paragraph.style) ?? {},
20021
+ ...cloneParagraphStyle(paragraph.style) ?? {},
19928
20022
  numbering: void 0
19929
20023
  };
19930
20024
  paragraph.sourceXml = void 0;
@@ -19980,7 +20074,7 @@ function useDocxEditor(options = {}) {
19980
20074
  normalizedRange.end.offset
19981
20075
  );
19982
20076
  collapsedOffset = safeStart + replacementText.length;
19983
- const endParagraphStyleForMerge = cloneParagraphStyle2(
20077
+ const endParagraphStyleForMerge = cloneParagraphStyle(
19984
20078
  endParagraph.style
19985
20079
  );
19986
20080
  const insertedStyle = cloneTextStyle2(
@@ -20067,7 +20161,7 @@ function useDocxEditor(options = {}) {
20067
20161
  normalizedRange.start.location
20068
20162
  );
20069
20163
  if (mergedLookup.paragraph) {
20070
- mergedLookup.paragraph.style = endParagraphStyleForMerge ? cloneParagraphStyle2(endParagraphStyleForMerge) : void 0;
20164
+ mergedLookup.paragraph.style = endParagraphStyleForMerge ? cloneParagraphStyle(endParagraphStyleForMerge) : void 0;
20071
20165
  mergedLookup.paragraph.sourceXml = void 0;
20072
20166
  if (mergedLookup.tableNode) {
20073
20167
  mergedLookup.tableNode.sourceXml = void 0;
@@ -20384,7 +20478,7 @@ function useDocxEditor(options = {}) {
20384
20478
  const removePreset2 = preset !== "none" && presetState2[preset];
20385
20479
  if (preset === "diagonal-down" || preset === "diagonal-up") {
20386
20480
  const clonedCellStyle = cloneTableCellStyle(targetCell.style) ?? {};
20387
- const nextCellBorders = cloneTableBorderSet2(clonedCellStyle.borders) ?? {};
20481
+ const nextCellBorders = cloneTableBorderSet(clonedCellStyle.borders) ?? {};
20388
20482
  const visibleBorder = removePreset2 ? nilTableBorderStyle() : toolbarTableBorderStyle(
20389
20483
  resolvePreferredTableBorder(nextCellBorders) ?? resolvePreferredTableBorder(tableNode2.style?.borders)
20390
20484
  );
@@ -20414,7 +20508,7 @@ function useDocxEditor(options = {}) {
20414
20508
  borders: nextBorders2
20415
20509
  };
20416
20510
  if (preset === "none" && targetCell.style?.borders) {
20417
- const nextCellBorders = cloneTableBorderSet2(targetCell.style.borders) ?? {};
20511
+ const nextCellBorders = cloneTableBorderSet(targetCell.style.borders) ?? {};
20418
20512
  const nilBorder = nilTableBorderStyle();
20419
20513
  nextCellBorders.tl2br = { ...nilBorder };
20420
20514
  nextCellBorders.tr2bl = { ...nilBorder };
@@ -20465,7 +20559,7 @@ function useDocxEditor(options = {}) {
20465
20559
  if (!paragraph2) {
20466
20560
  continue;
20467
20561
  }
20468
- const clonedStyle2 = cloneParagraphStyle2(paragraph2.style) ?? {};
20562
+ const clonedStyle2 = cloneParagraphStyle(paragraph2.style) ?? {};
20469
20563
  const nextBorders2 = applyParagraphBorderPresetForRangeEntry(
20470
20564
  clonedStyle2.borders,
20471
20565
  preset,
@@ -20497,7 +20591,7 @@ function useDocxEditor(options = {}) {
20497
20591
  if (!paragraph) {
20498
20592
  return current;
20499
20593
  }
20500
- const clonedStyle = cloneParagraphStyle2(paragraph.style) ?? {};
20594
+ const clonedStyle = cloneParagraphStyle(paragraph.style) ?? {};
20501
20595
  const presetState = paragraphBorderPresetState(clonedStyle.borders);
20502
20596
  const removePreset = preset !== "none" && presetState[preset];
20503
20597
  const nextBorders = applyParagraphBorderPreset(
@@ -20656,7 +20750,7 @@ function useDocxEditor(options = {}) {
20656
20750
  const currentText = paragraphText(paragraph);
20657
20751
  if (shouldRemove) {
20658
20752
  if (paragraphHasNumbering(paragraph)) {
20659
- const clonedStyle = cloneParagraphStyle2(paragraph.style);
20753
+ const clonedStyle = cloneParagraphStyle(paragraph.style);
20660
20754
  if (clonedStyle?.numbering) {
20661
20755
  clonedStyle.numbering = void 0;
20662
20756
  paragraph.style = clonedStyle;
@@ -20802,7 +20896,7 @@ function useDocxEditor(options = {}) {
20802
20896
  return current;
20803
20897
  }
20804
20898
  paragraphNode.style = {
20805
- ...cloneParagraphStyle2(paragraphNode.style) ?? {},
20899
+ ...cloneParagraphStyle(paragraphNode.style) ?? {},
20806
20900
  numbering: {
20807
20901
  ...numbering,
20808
20902
  ilvl: nextLevel
@@ -20918,7 +21012,7 @@ function useDocxEditor(options = {}) {
20918
21012
  paragraphNode.sourceXml = void 0;
20919
21013
  next.nodes.splice(insertionIndex, 0, {
20920
21014
  type: "paragraph",
20921
- style: cloneParagraphStyle2(paragraphNode.style),
21015
+ style: cloneParagraphStyle(paragraphNode.style),
20922
21016
  children: [
20923
21017
  {
20924
21018
  type: "text",
@@ -21009,12 +21103,12 @@ function useDocxEditor(options = {}) {
21009
21103
  afterInsertedStyle: inheritedRunStyle
21010
21104
  }
21011
21105
  );
21012
- paragraphNode.style = cloneParagraphStyle2(splitParagraphStyle);
21106
+ paragraphNode.style = cloneParagraphStyle(splitParagraphStyle);
21013
21107
  paragraphNode.children = splitChildren.beforeChildren;
21014
21108
  paragraphNode.sourceXml = void 0;
21015
21109
  next.nodes.splice(insertionIndex, 0, {
21016
21110
  type: "paragraph",
21017
- style: cloneParagraphStyle2(splitParagraphStyle),
21111
+ style: cloneParagraphStyle(splitParagraphStyle),
21018
21112
  children: splitChildren.afterChildren
21019
21113
  });
21020
21114
  splitResult = {
@@ -21348,7 +21442,7 @@ function useDocxEditor(options = {}) {
21348
21442
  if (!currentDropCap) {
21349
21443
  return current;
21350
21444
  }
21351
- paragraph.style = cloneParagraphStyle2(paragraph.style) ?? {};
21445
+ paragraph.style = cloneParagraphStyle(paragraph.style) ?? {};
21352
21446
  paragraph.style.dropCap = {
21353
21447
  ...currentDropCap,
21354
21448
  ...patch
@@ -21424,7 +21518,7 @@ function useDocxEditor(options = {}) {
21424
21518
  link: preservedLink
21425
21519
  }
21426
21520
  ];
21427
- paragraph.style = cloneParagraphStyle2(paragraph.style) ?? {};
21521
+ paragraph.style = cloneParagraphStyle(paragraph.style) ?? {};
21428
21522
  paragraph.style.dropCap = {
21429
21523
  ...currentDropCap
21430
21524
  };
@@ -21578,7 +21672,7 @@ function useDocxEditor(options = {}) {
21578
21672
  const firstParagraph = paragraphs[0];
21579
21673
  const nextParagraph = {
21580
21674
  type: "paragraph",
21581
- style: cloneParagraphStyle2(firstParagraph?.style),
21675
+ style: cloneParagraphStyle(firstParagraph?.style),
21582
21676
  children: [
21583
21677
  {
21584
21678
  type: "text",
@@ -22237,6 +22331,8 @@ function useDocxEditor(options = {}) {
22237
22331
  documentTheme,
22238
22332
  trackedChanges,
22239
22333
  showTrackedChanges,
22334
+ comments,
22335
+ showComments,
22240
22336
  currentPage: paginationInfo.currentPage,
22241
22337
  totalPages: paginationInfo.totalPages,
22242
22338
  selection,
@@ -22264,8 +22360,10 @@ function useDocxEditor(options = {}) {
22264
22360
  setStatus,
22265
22361
  setDocumentTheme,
22266
22362
  setShowTrackedChanges,
22363
+ setShowComments,
22267
22364
  syncPaginationInfo,
22268
22365
  toggleShowTrackedChanges,
22366
+ toggleShowComments,
22269
22367
  importDocxFile,
22270
22368
  newDocument,
22271
22369
  exportDocx,
@@ -22552,6 +22650,7 @@ function useDocxPageThumbnails(editor, options = {}) {
22552
22650
  if (!targetCanvas) {
22553
22651
  return;
22554
22652
  }
22653
+ const requiresAttachedTarget = canvas === void 0;
22555
22654
  const pageElement = pageSurfaceRegistry.pageElements.get(pageIndex);
22556
22655
  if (!pageElement || !pageElement.isConnected) {
22557
22656
  updatePageThumbnailState(pageIndex, "unavailable");
@@ -22565,6 +22664,9 @@ function useDocxPageThumbnails(editor, options = {}) {
22565
22664
  }
22566
22665
  updatePageThumbnailState(pageIndex, "rendering");
22567
22666
  await ensureThumbnailRasterQueue().enqueue(targetCanvas, async () => {
22667
+ if (requiresAttachedTarget && attachedCanvasByPageRef.current.get(pageIndex) !== targetCanvas) {
22668
+ return;
22669
+ }
22568
22670
  const livePageElement = pageSurfaceRegistry.pageElements.get(pageIndex);
22569
22671
  if (!livePageElement || !livePageElement.isConnected) {
22570
22672
  updatePageThumbnailState(pageIndex, "unavailable");
@@ -22711,6 +22813,10 @@ function useDocxPageThumbnails(editor, options = {}) {
22711
22813
  void renderPageThumbnailToCanvasRef.current(pageIndex, canvas);
22712
22814
  return;
22713
22815
  }
22816
+ const previousCanvas = attachedCanvasByPageRef.current.get(pageIndex);
22817
+ if (previousCanvas) {
22818
+ thumbnailRasterQueueRef.current?.cancel(previousCanvas);
22819
+ }
22714
22820
  attachedCanvasByPageRef.current.delete(pageIndex);
22715
22821
  };
22716
22822
  canvasRefCallbacksRef.current.set(pageIndex, nextCanvasRef);
@@ -22978,6 +23084,42 @@ function useDocxTrackChanges(editor) {
22978
23084
  ]
22979
23085
  );
22980
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
+ }
22981
23123
  function useDocxPageLayout(editor) {
22982
23124
  const primarySectionPropertiesXml = editor.model.metadata.sections?.[0]?.sectionPropertiesXml ?? editor.model.metadata.sectionPropertiesXml;
22983
23125
  const layout = React.useMemo(() => {
@@ -23850,6 +23992,8 @@ function DocxEditorViewer({
23850
23992
  headingStyles,
23851
23993
  showTrackedChanges,
23852
23994
  renderTrackedChangeCard,
23995
+ showComments,
23996
+ renderCommentCard,
23853
23997
  renderTableContextMenu,
23854
23998
  renderContextMenu,
23855
23999
  onFormFieldDoubleClick,
@@ -23858,7 +24002,9 @@ function DocxEditorViewer({
23858
24002
  const pageSurfaceRegistryOwner = docxViewerPageSurfaceRegistryOwner(editor);
23859
24003
  const trackedChangesEnabled = showTrackedChanges ?? editor.showTrackedChanges;
23860
24004
  const hasTrackedChanges = editor.trackedChanges.length > 0;
23861
- const showTrackedChangeGutter = trackedChangesEnabled;
24005
+ const commentsEnabled = showComments ?? editor.showComments;
24006
+ const hasComments = editor.comments.length > 0;
24007
+ const showTrackedChangeGutter = trackedChangesEnabled || commentsEnabled;
23862
24008
  const isReadOnly = mode === "read-only" || trackedChangesEnabled;
23863
24009
  const isNightReaderMode = isReadOnly && editor.documentTheme === "dark";
23864
24010
  const documentContentTheme = isNightReaderMode ? "light" : editor.documentTheme;
@@ -23877,11 +24023,13 @@ function DocxEditorViewer({
23877
24023
  const paragraphRunRenderOptions = React.useMemo(
23878
24024
  () => ({
23879
24025
  showTrackedChanges: trackedChangesEnabled,
24026
+ showCommentHighlights: commentsEnabled,
23880
24027
  numberingDefinitions: editor.model.metadata.numberingDefinitions,
23881
24028
  tocLinkColorByLevel,
23882
24029
  imageFilterSuffix: documentContentFilter
23883
24030
  }),
23884
24031
  [
24032
+ commentsEnabled,
23885
24033
  documentContentFilter,
23886
24034
  editor.model.metadata.numberingDefinitions,
23887
24035
  tocLinkColorByLevel,
@@ -23924,7 +24072,6 @@ function DocxEditorViewer({
23924
24072
  /* @__PURE__ */ new Map()
23925
24073
  );
23926
24074
  const pageElementsRef = React.useRef(/* @__PURE__ */ new Map());
23927
- const pagePlaceholderRefCallbacksRef = React.useRef(/* @__PURE__ */ new Map());
23928
24075
  const pageSurfaceRefCallbacksRef = React.useRef(/* @__PURE__ */ new Map());
23929
24076
  const trackedChangeCardElementsRef = React.useRef(/* @__PURE__ */ new Map());
23930
24077
  const initialPaginationStableSignatureRef = React.useRef(
@@ -23992,26 +24139,6 @@ function DocxEditorViewer({
23992
24139
  measuredPageContentHeightByIndex,
23993
24140
  setMeasuredPageContentHeightByIndex
23994
24141
  ] = React.useState([]);
23995
- const pagePlaceholderRefForIndex = React.useCallback(
23996
- (pageIndex) => {
23997
- const normalizedPageIndex = Math.max(0, Math.round(pageIndex));
23998
- const cached = pagePlaceholderRefCallbacksRef.current.get(normalizedPageIndex);
23999
- if (cached) {
24000
- return cached;
24001
- }
24002
- const nextRef = (element) => {
24003
- if (element) {
24004
- pageElementsRef.current.set(normalizedPageIndex, element);
24005
- } else {
24006
- pageElementsRef.current.delete(normalizedPageIndex);
24007
- }
24008
- registerDocxViewerPageSurface(editor, normalizedPageIndex, void 0);
24009
- };
24010
- pagePlaceholderRefCallbacksRef.current.set(normalizedPageIndex, nextRef);
24011
- return nextRef;
24012
- },
24013
- [pageSurfaceRegistryOwner]
24014
- );
24015
24142
  const pageSurfaceRefForIndex = React.useCallback(
24016
24143
  (pageIndex) => {
24017
24144
  const normalizedPageIndex = Math.max(0, Math.round(pageIndex));
@@ -24033,7 +24160,6 @@ function DocxEditorViewer({
24033
24160
  [pageSurfaceRegistryOwner]
24034
24161
  );
24035
24162
  React.useEffect(() => {
24036
- pagePlaceholderRefCallbacksRef.current.clear();
24037
24163
  pageSurfaceRefCallbacksRef.current.clear();
24038
24164
  }, [pageSurfaceRegistryOwner]);
24039
24165
  const [
@@ -25307,6 +25433,26 @@ function DocxEditorViewer({
25307
25433
  nearestScrollableAncestor(viewerRootRef.current)
25308
25434
  );
25309
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]);
25310
25456
  React.useLayoutEffect(() => {
25311
25457
  if (typeof window === "undefined") {
25312
25458
  return;
@@ -25319,7 +25465,7 @@ function DocxEditorViewer({
25319
25465
  setVirtualizerMeasurementScale(
25320
25466
  (current) => Math.abs(current - nextScale) < 5e-3 ? current : nextScale
25321
25467
  );
25322
- });
25468
+ }, [editor.documentLoadNonce, pageCount, zoomProbeNonce]);
25323
25469
  const internalPageVirtualizationEnabled = internalPageVirtualizationRequested && internalVirtualScrollElement !== null;
25324
25470
  const internalPageVirtualizationPending = typeof window !== "undefined" && internalPageVirtualizationRequested && internalVirtualScrollElement === null;
25325
25471
  const internalVirtualScrollUsesWindow = typeof document !== "undefined" && internalVirtualScrollElement !== null && (internalVirtualScrollElement === document.scrollingElement || internalVirtualScrollElement === document.documentElement || internalVirtualScrollElement === document.body);
@@ -25362,6 +25508,30 @@ function DocxEditorViewer({
25362
25508
  internalWindowPageVirtualizer
25363
25509
  ]);
25364
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
+ ]);
25365
25535
  const internalVisiblePageRange = React.useMemo(() => {
25366
25536
  if (!internalPageVirtualizationEnabled) {
25367
25537
  return void 0;
@@ -25652,6 +25822,40 @@ function DocxEditorViewer({
25652
25822
  }
25653
25823
  return indexes;
25654
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
+ ]);
25655
25859
  React.useLayoutEffect(() => {
25656
25860
  if (typeof window === "undefined") {
25657
25861
  return;
@@ -25811,7 +26015,7 @@ function DocxEditorViewer({
25811
26015
  }, [onPageCountChange, stableReportedPageCount]);
25812
26016
  React.useEffect(() => {
25813
26017
  const nextCurrentPage = clampNumber(
25814
- visiblePageStartIndex + 1,
26018
+ (internalMostVisiblePageIndex ?? visiblePageStartIndex) + 1,
25815
26019
  1,
25816
26020
  Math.max(1, stableReportedPageCount)
25817
26021
  );
@@ -25821,6 +26025,7 @@ function DocxEditorViewer({
25821
26025
  });
25822
26026
  }, [
25823
26027
  editor.syncPaginationInfo,
26028
+ internalMostVisiblePageIndex,
25824
26029
  stableReportedPageCount,
25825
26030
  visiblePageStartIndex
25826
26031
  ]);
@@ -25828,20 +26033,38 @@ function DocxEditorViewer({
25828
26033
  const pageBuckets = pageNodeSegmentsByPage.map(
25829
26034
  () => []
25830
26035
  );
25831
- editor.trackedChanges.forEach((change) => {
25832
- const pageIndex = resolveTrackedChangePageIndex(
25833
- change,
26036
+ const placeAnnotation = (annotation) => {
26037
+ const pageIndex = resolveGutterAnnotationPageIndex(
26038
+ annotation.location,
25834
26039
  pageNodeSegmentsByPage
25835
26040
  );
25836
26041
  if (pageIndex < 0 || pageIndex >= pageBuckets.length) {
25837
26042
  return;
25838
26043
  }
25839
- pageBuckets[pageIndex].push(change);
25840
- });
25841
- pageBuckets.forEach((pageChanges) => {
25842
- pageChanges.sort((left, right) => {
25843
- const leftKey = trackedChangeSortTuple(left);
25844
- 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);
25845
26068
  for (let index = 0; index < leftKey.length; index += 1) {
25846
26069
  if (leftKey[index] === rightKey[index]) {
25847
26070
  continue;
@@ -25852,7 +26075,13 @@ function DocxEditorViewer({
25852
26075
  });
25853
26076
  });
25854
26077
  return pageBuckets;
25855
- }, [editor.trackedChanges, pageNodeSegmentsByPage]);
26078
+ }, [
26079
+ commentsEnabled,
26080
+ editor.comments,
26081
+ editor.trackedChanges,
26082
+ pageNodeSegmentsByPage,
26083
+ trackedChangesEnabled
26084
+ ]);
25856
26085
  const [trackedChangeAnchorByPage, setTrackedChangeAnchorByPage] = React.useState([]);
25857
26086
  const [headerBodyClearanceByPage, setHeaderBodyClearanceByPage] = React.useState({});
25858
26087
  React.useEffect(() => {
@@ -25866,18 +26095,18 @@ function DocxEditorViewer({
25866
26095
  );
25867
26096
  return;
25868
26097
  }
25869
- const nextAnchorMaps = trackedChangesByPage.map((changes, pageIndex) => {
26098
+ const nextAnchorMaps = trackedChangesByPage.map((annotations, pageIndex) => {
25870
26099
  const pageElement = pageElementsRef.current.get(pageIndex);
25871
26100
  const anchorsByChangeId = /* @__PURE__ */ new Map();
25872
- if (!pageElement || changes.length === 0) {
26101
+ if (!pageElement || annotations.length === 0) {
25873
26102
  return anchorsByChangeId;
25874
26103
  }
25875
26104
  const pageHeightPx = Math.max(1, pageElement.offsetHeight);
25876
26105
  const pageWidthPx = Math.max(1, pageElement.offsetWidth);
25877
- changes.forEach((change) => {
26106
+ annotations.forEach((annotation) => {
25878
26107
  const anchorElement = findTrackedChangeAnchorElementInPage(
25879
26108
  pageElement,
25880
- change.location
26109
+ annotation.location
25881
26110
  );
25882
26111
  if (!anchorElement) {
25883
26112
  return;
@@ -25901,7 +26130,7 @@ function DocxEditorViewer({
25901
26130
  10,
25902
26131
  Math.max(10, pageWidthPx - 10)
25903
26132
  );
25904
- anchorsByChangeId.set(change.id, { x: anchorX, y: anchorY });
26133
+ anchorsByChangeId.set(annotation.id, { x: anchorX, y: anchorY });
25905
26134
  });
25906
26135
  return anchorsByChangeId;
25907
26136
  });
@@ -25919,28 +26148,30 @@ function DocxEditorViewer({
25919
26148
  );
25920
26149
  return;
25921
26150
  }
25922
- const nextHeightsByPage = trackedChangesByPage.map((changes, pageIndex) => {
25923
- const pageHeights = /* @__PURE__ */ new Map();
25924
- changes.forEach((change) => {
25925
- const cardElement = trackedChangeCardElementsRef.current.get(
25926
- `${pageIndex}:${change.id}`
25927
- );
25928
- if (!cardElement) {
25929
- return;
25930
- }
25931
- const measuredHeight = Math.round(
25932
- cardElement.getBoundingClientRect().height
25933
- );
25934
- if (!Number.isFinite(measuredHeight) || measuredHeight <= 0) {
25935
- return;
25936
- }
25937
- pageHeights.set(
25938
- change.id,
25939
- Math.max(TRACKED_CHANGE_GUTTER_CARD_MIN_HEIGHT_PX, measuredHeight)
25940
- );
25941
- });
25942
- return pageHeights;
25943
- });
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
+ );
25944
26175
  setTrackedChangeCardHeightsByPage((current) => {
25945
26176
  if (current.length === nextHeightsByPage.length) {
25946
26177
  let equal = true;
@@ -26439,9 +26670,14 @@ function DocxEditorViewer({
26439
26670
  },
26440
26671
  []
26441
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;
26442
26678
  const nextMeasuredBodyFooterOverlapLatchState = resolveMeasuredBodyFooterOverlapLatchState({
26443
26679
  pageCount,
26444
- targetPageCount: editor.model.metadata.documentPageCount,
26680
+ targetPageCount: latchTargetPageCount,
26445
26681
  overlappingPageIndexes,
26446
26682
  previousSignature: measuredBodyFooterOverlapCandidateRef.current.signature,
26447
26683
  previousConsecutivePasses: measuredBodyFooterOverlapCandidateRef.current.consecutivePasses,
@@ -33270,7 +33506,8 @@ function DocxEditorViewer({
33270
33506
  const hasFixedPositionWrappedImage = paragraph.children.some(
33271
33507
  (child) => child.type === "image" && isFixedPositionWrappedFloatingImage(child)
33272
33508
  );
33273
- if ((trackedChangesEnabled || useSpecialTabLayout) && !hasFixedPositionWrappedImage) {
33509
+ const paragraphHasCommentAnchors = commentsEnabled && /commentRange|commentReference/i.test(paragraph.sourceXml ?? "");
33510
+ if ((trackedChangesEnabled || useSpecialTabLayout || paragraphHasCommentAnchors) && !hasFixedPositionWrappedImage) {
33274
33511
  return renderParagraphRuns(
33275
33512
  paragraph,
33276
33513
  keyPrefix,
@@ -33283,6 +33520,7 @@ function DocxEditorViewer({
33283
33520
  void 0,
33284
33521
  {
33285
33522
  showTrackedChanges: trackedChangesEnabled,
33523
+ showCommentHighlights: commentsEnabled,
33286
33524
  numberingDefinitions: editor.model.metadata.numberingDefinitions,
33287
33525
  tocLinkColorByLevel,
33288
33526
  paragraphOriginLeftPx: bodyParagraphOriginLeftPx,
@@ -39626,44 +39864,25 @@ ${currentText.slice(end)}`;
39626
39864
  }
39627
39865
  }
39628
39866
  ) : null,
39629
- 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] ?? [];
39630
39883
  const pageInfo = pageSectionInfoByIndex[pageIndex];
39631
39884
  const pageLayout = pageInfo?.layout ?? documentLayout;
39632
- const pageVisible = isPageVisible(pageIndex);
39633
39885
  const pageWrapperWidthPx = showTrackedChangeGutter ? pageLayout.pageWidthPx + TRACKED_CHANGE_GUTTER_WIDTH_PX : pageLayout.pageWidthPx;
39634
- if (!pageVisible) {
39635
- const placeholderBackgroundColor = pageBackgroundColor ?? editor.model.metadata.documentBackgroundColor ?? pageSurfaceBaseStyle.backgroundColor;
39636
- return /* @__PURE__ */ jsx(
39637
- "div",
39638
- {
39639
- "data-docx-page-wrapper": "true",
39640
- "data-docx-page-index": pageIndex,
39641
- "data-index": pageIndex,
39642
- style: {
39643
- width: pageWrapperWidthPx,
39644
- margin: "0 auto",
39645
- visibility: hideDocumentUntilPaginationSettled ? "hidden" : void 0
39646
- },
39647
- children: /* @__PURE__ */ jsx(
39648
- "div",
39649
- {
39650
- "data-docx-page-placeholder": "true",
39651
- ref: pagePlaceholderRefForIndex(pageIndex),
39652
- style: {
39653
- ...pageSurfaceBaseStyle,
39654
- ...pageMarginPaddingStyle(pageLayout.marginsPx),
39655
- height: pageLayout.pageHeightPx,
39656
- minHeight: pageLayout.pageHeightPx,
39657
- width: pageLayout.pageWidthPx,
39658
- backgroundColor: placeholderBackgroundColor,
39659
- pointerEvents: "none"
39660
- }
39661
- }
39662
- )
39663
- },
39664
- `page-${pageIndex}`
39665
- );
39666
- }
39667
39886
  const pageContentWidthPx = Math.max(
39668
39887
  120,
39669
39888
  pageLayout.pageWidthPx - pageLayout.marginsPx.left - pageLayout.marginsPx.right
@@ -39835,6 +40054,10 @@ ${currentText.slice(end)}`;
39835
40054
  width: pageWrapperWidthPx,
39836
40055
  minHeight: pageLayout.pageHeightPx,
39837
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",
39838
40061
  visibility: hideDocumentUntilPaginationSettled ? "hidden" : void 0
39839
40062
  },
39840
40063
  children: [
@@ -40085,9 +40308,6 @@ ${currentText.slice(end)}`;
40085
40308
  segments: [segment]
40086
40309
  });
40087
40310
  });
40088
- if (typeof window !== "undefined" && window.__docxDebugGroups) {
40089
- console.log("[groups]", pageIndex, JSON.stringify(sectionGroups.map((g) => ({ s: g.sectionIndex, n: g.segments.map((x) => x.nodeIndex) }))));
40090
- }
40091
40311
  return sectionGroups.map((group, groupIndex) => {
40092
40312
  const sectionColumns = sectionColumnsBySectionIndex[group.sectionIndex];
40093
40313
  const isLastGroupOnPage = groupIndex === sectionGroups.length - 1;
@@ -40912,10 +41132,10 @@ ${currentText.slice(end)}`;
40912
41132
  overflow: "visible"
40913
41133
  },
40914
41134
  children: pageTrackedChanges.map((entry) => {
40915
- const accentColor = trackedChangeAccentColor(
40916
- entry.change.kind,
41135
+ const accentColor = entry.annotation.trackedChange ? trackedChangeAccentColor(
41136
+ entry.annotation.trackedChange.kind,
40917
41137
  editor.documentTheme
40918
- );
41138
+ ) : commentAccentColor(editor.documentTheme);
40919
41139
  const cardCenterY = clampNumber(
40920
41140
  Math.round(entry.top + entry.heightPx / 2),
40921
41141
  8,
@@ -40976,12 +41196,12 @@ ${currentText.slice(end)}`;
40976
41196
  )
40977
41197
  ]
40978
41198
  },
40979
- `tracked-connector-${pageIndex}-${entry.change.id}`
41199
+ `tracked-connector-${pageIndex}-${entry.annotation.id}`
40980
41200
  );
40981
41201
  })
40982
41202
  }
40983
41203
  ),
40984
- !hasTrackedChanges && pageIndex === 0 ? /* @__PURE__ */ jsx(
41204
+ (!trackedChangesEnabled || !hasTrackedChanges) && (!commentsEnabled || !hasComments) && pageIndex === 0 ? /* @__PURE__ */ jsx(
40985
41205
  "p",
40986
41206
  {
40987
41207
  style: {
@@ -40994,19 +41214,21 @@ ${currentText.slice(end)}`;
40994
41214
  lineHeight: 1.35,
40995
41215
  color: "#94a3b8"
40996
41216
  },
40997
- children: "No edits found"
41217
+ children: trackedChangesEnabled && commentsEnabled ? "No edits or comments found" : commentsEnabled ? "No comments found" : "No edits found"
40998
41218
  }
40999
41219
  ) : null,
41000
41220
  pageTrackedChanges.map((entry) => {
41001
- const accentColor = trackedChangeAccentColor(
41002
- entry.change.kind,
41221
+ const trackedChange = entry.annotation.trackedChange;
41222
+ const comment = entry.annotation.comment;
41223
+ const accentColor = trackedChange ? trackedChangeAccentColor(
41224
+ trackedChange.kind,
41003
41225
  editor.documentTheme
41004
- );
41226
+ ) : commentAccentColor(editor.documentTheme);
41005
41227
  const formattedDate = formatTrackedChangeDate(
41006
- entry.change.date
41228
+ trackedChange?.date ?? comment?.date
41007
41229
  );
41008
- const kindLabel = trackedChangeKindLabel(entry.change.kind);
41009
- 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";
41010
41232
  const cardWidthPx = Math.max(
41011
41233
  140,
41012
41234
  TRACKED_CHANGE_GUTTER_WIDTH_PX - TRACKED_CHANGE_GUTTER_CARD_LEFT_PX - TRACKED_CHANGE_GUTTER_CARD_RIGHT_PX
@@ -41058,7 +41280,7 @@ ${currentText.slice(end)}`;
41058
41280
  fontWeight: 700,
41059
41281
  lineHeight: 1.25
41060
41282
  },
41061
- children: entry.change.author?.trim() || "Unknown author"
41283
+ children: (trackedChange?.author ?? comment?.author)?.trim() || "Unknown author"
41062
41284
  }
41063
41285
  ),
41064
41286
  formattedDate ? /* @__PURE__ */ jsx(
@@ -41077,6 +41299,23 @@ ${currentText.slice(end)}`;
41077
41299
  ]
41078
41300
  }
41079
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,
41080
41319
  /* @__PURE__ */ jsxs(
41081
41320
  "p",
41082
41321
  {
@@ -41098,8 +41337,8 @@ ${currentText.slice(end)}`;
41098
41337
  ]
41099
41338
  }
41100
41339
  );
41101
- const renderedCard = renderTrackedChangeCard ? renderTrackedChangeCard({
41102
- change: entry.change,
41340
+ const renderedCard = trackedChange ? renderTrackedChangeCard ? renderTrackedChangeCard({
41341
+ change: trackedChange,
41103
41342
  kindLabel,
41104
41343
  snippet,
41105
41344
  formattedDate,
@@ -41107,12 +41346,21 @@ ${currentText.slice(end)}`;
41107
41346
  documentTheme: editor.documentTheme,
41108
41347
  pageIndex,
41109
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
41110
41357
  }) : defaultCard;
41111
41358
  return /* @__PURE__ */ jsx(
41112
41359
  "div",
41113
41360
  {
41361
+ "data-docx-gutter-annotation": trackedChange ? "tracked-change" : "comment",
41114
41362
  ref: (element) => {
41115
- const elementKey = `${pageIndex}:${entry.change.id}`;
41363
+ const elementKey = `${pageIndex}:${entry.annotation.id}`;
41116
41364
  if (element) {
41117
41365
  trackedChangeCardElementsRef.current.set(
41118
41366
  elementKey,
@@ -41127,7 +41375,7 @@ ${currentText.slice(end)}`;
41127
41375
  style: cardContainerStyle,
41128
41376
  children: renderedCard
41129
41377
  },
41130
- `tracked-card-${pageIndex}-${entry.change.id}`
41378
+ `tracked-card-${pageIndex}-${entry.annotation.id}`
41131
41379
  );
41132
41380
  })
41133
41381
  ]
@@ -41138,6 +41386,20 @@ ${currentText.slice(end)}`;
41138
41386
  `page-${pageIndex}`
41139
41387
  );
41140
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,
41141
41403
  !isReadOnly ? (() => {
41142
41404
  const hasCustomContextMenuState = Boolean(contextMenuState);
41143
41405
  const hasTableContextMenuState = Boolean(tableContextMenuState);
@@ -43032,16 +43294,20 @@ function useDocxModel(file) {
43032
43294
  }
43033
43295
  const docxFile = file;
43034
43296
  let isCurrent = true;
43297
+ const abortController = new AbortController();
43035
43298
  async function load() {
43036
43299
  setState({ isLoading: true });
43037
43300
  try {
43038
- const pkg = await parseDocx(docxFile);
43301
+ const { model } = await importDocxBuffer(docxFile, {
43302
+ signal: abortController.signal,
43303
+ transferBuffer: false
43304
+ });
43039
43305
  if (!isCurrent) {
43040
43306
  return;
43041
43307
  }
43042
43308
  setState({
43043
43309
  isLoading: false,
43044
- model: await buildDocModel(pkg)
43310
+ model
43045
43311
  });
43046
43312
  } catch (error) {
43047
43313
  if (!isCurrent) {
@@ -43056,6 +43322,7 @@ function useDocxModel(file) {
43056
43322
  void load();
43057
43323
  return () => {
43058
43324
  isCurrent = false;
43325
+ abortController.abort();
43059
43326
  };
43060
43327
  }, [file]);
43061
43328
  return state;
@@ -43395,6 +43662,7 @@ export {
43395
43662
  updateTableCellParagraphTextRecursive,
43396
43663
  updateTableCellText,
43397
43664
  useDocxBorders,
43665
+ useDocxComments,
43398
43666
  useDocxDocumentTheme,
43399
43667
  useDocxEditor,
43400
43668
  useDocxFormFields,