@extend-ai/react-docx 0.9.0 → 0.9.1

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,10 +1,3 @@
1
- import {
2
- createMinimalDocxPackage,
3
- getPart,
4
- packageToArrayBuffer,
5
- parseDocx,
6
- withPart
7
- } from "./chunk-KB6YHU6O.js";
8
1
  import {
9
2
  allocateBlockId,
10
3
  buildDocModel,
@@ -16,14 +9,21 @@ import {
16
9
  deepFreezeDocModel,
17
10
  ensureDocModelBlockIds,
18
11
  normalizeDocModel
19
- } from "./chunk-4CPXLVFB.js";
12
+ } from "./chunk-GRQPIBJJ.js";
13
+ import {
14
+ createMinimalDocxPackage,
15
+ getPart,
16
+ packageToArrayBuffer,
17
+ parseDocx,
18
+ withPart
19
+ } from "./chunk-6NVXVLTW.js";
20
20
  import {
21
21
  initWasm,
22
22
  mapsToWasmPackage,
23
23
  setWasmSource,
24
24
  wasmModelToDocumentXml,
25
25
  wasmSerializeDocx
26
- } from "./chunk-THNPK4N7.js";
26
+ } from "./chunk-AKDFE4ZH.js";
27
27
 
28
28
  // src/index.tsx
29
29
  import * as React2 from "react";
@@ -333,28 +333,24 @@ async function importDocxOnMainThread(buffer, signal) {
333
333
  throw createAbortError();
334
334
  }
335
335
  const startedAt = performanceNow();
336
- const [{ parseDocx: parseDocx2 }, { buildDocModel: buildDocModel2, ensureDocModelBlockIds: ensureDocModelBlockIds2 }] = await Promise.all([
337
- import("./src-JS3OX73I.js"),
338
- import("./src-KC2H22CB.js")
339
- ]);
340
- const pkg = await parseDocx2(buffer);
341
- const parsedAt = performanceNow();
336
+ const { buildDocModelFromBytes: buildDocModelFromBytes2, ensureDocModelBlockIds: ensureDocModelBlockIds2 } = await import("./src-D3NRHPLE.js");
342
337
  if (signal?.aborted) {
343
338
  throw createAbortError();
344
339
  }
345
- const model = ensureDocModelBlockIds2(await buildDocModel2(pkg));
340
+ const result = await buildDocModelFromBytes2(buffer);
341
+ const model = ensureDocModelBlockIds2(result.model);
346
342
  const finishedAt = performanceNow();
347
343
  if (signal?.aborted) {
348
344
  throw createAbortError();
349
345
  }
350
346
  return {
351
- package: pkg,
347
+ package: result.package,
352
348
  model,
353
349
  source: "main-thread",
354
350
  timings: {
355
351
  totalMs: finishedAt - startedAt,
356
- parseMs: parsedAt - startedAt,
357
- buildModelMs: finishedAt - parsedAt
352
+ parseMs: result.timings.parseMs,
353
+ buildModelMs: result.timings.buildModelMs
358
354
  }
359
355
  };
360
356
  }
@@ -366,12 +362,18 @@ async function importDocxBuffer(buffer, options = {}) {
366
362
  throw createAbortError();
367
363
  }
368
364
  if (!canUseDocxImportWorker(options)) {
365
+ if (options.useWorker === "required") {
366
+ throw new Error("DOCX import requires a Web Worker and a worker-compatible WASM source");
367
+ }
369
368
  return importDocxOnMainThread(buffer, options.signal);
370
369
  }
371
370
  let worker;
372
371
  try {
373
372
  worker = createDocxImportWorker();
374
- } catch {
373
+ } catch (error) {
374
+ if (options.useWorker === "required") {
375
+ throw new Error("Unable to start the DOCX import worker", { cause: error });
376
+ }
375
377
  return importDocxOnMainThread(buffer, options.signal);
376
378
  }
377
379
  const requestId = nextImportWorkerRequestId;
@@ -441,7 +443,7 @@ async function importDocxBuffer(buffer, options = {}) {
441
443
  }
442
444
  });
443
445
  const result = await workerResult;
444
- const { ensureDocModelBlockIds: ensureDocModelBlockIds2 } = await import("./src-KC2H22CB.js");
446
+ const { ensureDocModelBlockIds: ensureDocModelBlockIds2 } = await import("./src-D3NRHPLE.js");
445
447
  ensureDocModelBlockIds2(result.model);
446
448
  return result;
447
449
  }
@@ -449,6 +451,269 @@ async function importDocxBuffer(buffer, options = {}) {
449
451
  // src/editor.tsx
450
452
  import * as React from "react";
451
453
  import { flushSync } from "react-dom";
454
+
455
+ // src/pointer-drag.ts
456
+ function startObjectDrag(options) {
457
+ const { event, element } = options;
458
+ const doc = element.ownerDocument;
459
+ const win = doc.defaultView;
460
+ const rect = element.getBoundingClientRect();
461
+ const grabX = event.clientX - rect.left;
462
+ const grabY = event.clientY - rect.top;
463
+ let clientX = event.clientX;
464
+ let clientY = event.clientY;
465
+ let active = false;
466
+ let ended = false;
467
+ let animationFrame = 0;
468
+ let ghost;
469
+ let previousTime = 0;
470
+ const hiddenElements = /* @__PURE__ */ new Map();
471
+ const scrollContainers = [];
472
+ for (let parent = element.parentElement; parent; parent = parent.parentElement) {
473
+ if (/(auto|scroll)/.test(win.getComputedStyle(parent).overflowY)) {
474
+ scrollContainers.push(parent);
475
+ }
476
+ }
477
+ const scrollingElement = doc.scrollingElement;
478
+ if (scrollingElement && !scrollContainers.includes(scrollingElement)) {
479
+ scrollContainers.push(scrollingElement);
480
+ }
481
+ const scrollAnchors = /* @__PURE__ */ new Map();
482
+ const frame = () => ({
483
+ clientX,
484
+ clientY,
485
+ left: clientX - grabX,
486
+ top: clientY - grabY,
487
+ width: rect.width,
488
+ height: rect.height
489
+ });
490
+ const hideSource = () => {
491
+ const source = options.resolveElement();
492
+ if (source && !hiddenElements.has(source)) {
493
+ hiddenElements.set(source, source.style.visibility);
494
+ source.style.visibility = "hidden";
495
+ }
496
+ };
497
+ const observer = new MutationObserver(() => {
498
+ if (active && !ended) hideSource();
499
+ });
500
+ observer.observe(
501
+ element.closest("[data-testid=docx-editor-viewer]") ?? element.parentElement ?? element,
502
+ { childList: true, subtree: true }
503
+ );
504
+ const tick = (time) => {
505
+ if (ended) return;
506
+ const elapsed = previousTime ? Math.min(32, time - previousTime) : 16;
507
+ previousTime = time;
508
+ for (const container of scrollContainers) {
509
+ const bounds = container === scrollingElement ? { top: 0, bottom: win.innerHeight, left: 0, right: win.innerWidth } : container.getBoundingClientRect();
510
+ if (clientX < bounds.left || clientX > bounds.right) continue;
511
+ const edge = 40;
512
+ const speed = clientY < bounds.top + edge ? -Math.min(1, (bounds.top + edge - clientY) / edge) : clientY > bounds.bottom - edge ? Math.min(1, (clientY - bounds.bottom + edge) / edge) : 0;
513
+ const before = container.scrollTop;
514
+ container.scrollTop += speed * elapsed * 0.7;
515
+ if (container.scrollTop !== before) break;
516
+ }
517
+ hideSource();
518
+ const next = frame();
519
+ if (ghost) {
520
+ ghost.style.left = `${next.left}px`;
521
+ ghost.style.top = `${next.top}px`;
522
+ }
523
+ options.onFrame(next);
524
+ animationFrame = win.requestAnimationFrame(tick);
525
+ };
526
+ const cleanup = () => {
527
+ ended = true;
528
+ win.cancelAnimationFrame(animationFrame);
529
+ win.removeEventListener("pointermove", move, true);
530
+ win.removeEventListener("pointerup", up, true);
531
+ win.removeEventListener("pointercancel", cancelPointer, true);
532
+ win.removeEventListener("keydown", keydown);
533
+ win.removeEventListener("blur", cancel);
534
+ observer.disconnect();
535
+ win.requestAnimationFrame(
536
+ () => win.requestAnimationFrame(() => {
537
+ scrollAnchors.forEach((value, container) => {
538
+ container.style.overflowAnchor = value;
539
+ });
540
+ })
541
+ );
542
+ ghost?.remove();
543
+ hiddenElements.forEach((visibility, source) => {
544
+ source.style.visibility = visibility;
545
+ });
546
+ };
547
+ const cancel = () => {
548
+ if (ended) return;
549
+ cleanup();
550
+ options.onCancel();
551
+ };
552
+ const cancelPointer = (next) => {
553
+ if (next.pointerId === event.pointerId) cancel();
554
+ };
555
+ const keydown = (next) => {
556
+ if (next.key === "Escape") {
557
+ next.preventDefault();
558
+ cancel();
559
+ }
560
+ };
561
+ const move = (next) => {
562
+ if (next.pointerId !== event.pointerId || ended) return;
563
+ clientX = next.clientX;
564
+ clientY = next.clientY;
565
+ if (!active) {
566
+ if (Math.hypot(clientX - event.clientX, clientY - event.clientY) < 4)
567
+ return;
568
+ active = true;
569
+ for (const container of scrollContainers) {
570
+ scrollAnchors.set(container, container.style.overflowAnchor);
571
+ container.style.overflowAnchor = "none";
572
+ }
573
+ ghost = element.cloneNode(true);
574
+ ghost.querySelectorAll(
575
+ "[data-image-resize-handle], [data-docx-table-move-handle]"
576
+ ).forEach((handle) => handle.remove());
577
+ for (const node of [ghost, ...ghost.querySelectorAll("*")]) {
578
+ node.removeAttribute("id");
579
+ for (const attribute of [...node.attributes]) {
580
+ if (attribute.name.startsWith("data-docx-"))
581
+ node.removeAttribute(attribute.name);
582
+ }
583
+ node.setAttribute("contenteditable", "false");
584
+ }
585
+ const style = win.getComputedStyle(element);
586
+ Object.assign(ghost.style, {
587
+ position: "fixed",
588
+ margin: "0",
589
+ left: `${rect.left}px`,
590
+ top: `${rect.top}px`,
591
+ width: `${rect.width / options.zoom}px`,
592
+ height: `${rect.height / options.zoom}px`,
593
+ transform: `scale(${options.zoom})`,
594
+ transformOrigin: "top left",
595
+ visibility: "visible",
596
+ pointerEvents: "none",
597
+ zIndex: "2147483647",
598
+ opacity: "0.9",
599
+ userSelect: "none",
600
+ font: style.font,
601
+ color: style.color
602
+ });
603
+ ghost.setAttribute("aria-hidden", "true");
604
+ ghost.setAttribute("data-docx-drag-ghost", "true");
605
+ doc.body.appendChild(ghost);
606
+ hideSource();
607
+ animationFrame = win.requestAnimationFrame(tick);
608
+ }
609
+ next.preventDefault();
610
+ next.stopPropagation();
611
+ };
612
+ const up = (next) => {
613
+ if (next.pointerId !== event.pointerId || ended) return;
614
+ clientX = next.clientX;
615
+ clientY = next.clientY;
616
+ const finalFrame = frame();
617
+ if (active) {
618
+ next.preventDefault();
619
+ next.stopPropagation();
620
+ }
621
+ try {
622
+ if (active) options.onDrop(finalFrame);
623
+ else options.onCancel();
624
+ } finally {
625
+ cleanup();
626
+ }
627
+ };
628
+ win.addEventListener("pointermove", move, { passive: false, capture: true });
629
+ win.addEventListener("pointerup", up, true);
630
+ win.addEventListener("pointercancel", cancelPointer, true);
631
+ win.addEventListener("keydown", keydown);
632
+ win.addEventListener("blur", cancel);
633
+ return cancel;
634
+ }
635
+
636
+ // src/image-drop.ts
637
+ function copyModelForParagraphEdits(model, locations) {
638
+ const copies = /* @__PURE__ */ new Map();
639
+ const copy = (value) => {
640
+ const existing = copies.get(value);
641
+ if (existing) return existing;
642
+ const next2 = Array.isArray(value) ? [...value] : { ...value };
643
+ copies.set(value, next2);
644
+ copies.set(next2, next2);
645
+ return next2;
646
+ };
647
+ const next = { ...model, nodes: [...model.nodes] };
648
+ for (const location of locations) {
649
+ if (location.kind === "paragraph") {
650
+ const node = next.nodes[location.nodeIndex];
651
+ if (node?.type !== "paragraph") continue;
652
+ const paragraph = copy(node);
653
+ paragraph.children = copy(paragraph.children);
654
+ next.nodes[location.nodeIndex] = paragraph;
655
+ } else {
656
+ const node = next.nodes[location.tableIndex];
657
+ if (node?.type !== "table") continue;
658
+ const row = node.rows[location.rowIndex];
659
+ const cell = row?.cells[location.cellIndex];
660
+ if (!cell) continue;
661
+ let paragraphIndex = -1;
662
+ const contentIndex = cell.nodes.findIndex((content) => {
663
+ if (content.type === "paragraph") paragraphIndex++;
664
+ return content.type === "paragraph" && paragraphIndex === location.paragraphIndex;
665
+ });
666
+ const paragraph = cell.nodes[contentIndex];
667
+ if (paragraph?.type !== "paragraph") continue;
668
+ const nextTable = copy(node);
669
+ next.nodes[location.tableIndex] = nextTable;
670
+ nextTable.rows = copy(node.rows);
671
+ const nextRow = copy(row);
672
+ nextTable.rows[location.rowIndex] = nextRow;
673
+ nextRow.cells = copy(row.cells);
674
+ const nextCell = copy(cell);
675
+ nextRow.cells[location.cellIndex] = nextCell;
676
+ nextCell.nodes = copy(cell.nodes);
677
+ const nextParagraph = copy(paragraph);
678
+ nextCell.nodes[contentIndex] = nextParagraph;
679
+ nextParagraph.children = copy(paragraph.children);
680
+ }
681
+ }
682
+ return next;
683
+ }
684
+ function imageDropPositionAtTextOffset(paragraph, textOffset, options) {
685
+ let remaining = Math.max(0, Math.round(textOffset));
686
+ for (let childIndex = 0; childIndex < paragraph.children.length; childIndex++) {
687
+ const child = paragraph.children[childIndex];
688
+ const length = child.type === "image" ? !child.floating && options?.countInlineImages ? 1 : 0 : child.type === "text" ? child.text.length : (options?.fieldText?.(child) ?? child.value ?? "").length;
689
+ if (remaining <= length && length > 0) {
690
+ if (remaining === 0) return { childIndex };
691
+ if (remaining === length) return { childIndex: childIndex + 1 };
692
+ return child.type === "text" ? { childIndex, textOffset: remaining } : { childIndex: childIndex + 1 };
693
+ }
694
+ remaining -= length;
695
+ }
696
+ return { childIndex: paragraph.children.length };
697
+ }
698
+ function insertImageAtDropPosition(paragraph, image, childIndex, textOffset) {
699
+ const index = Math.max(0, Math.min(childIndex, paragraph.children.length));
700
+ const child = paragraph.children[index];
701
+ if (child?.type === "text" && textOffset !== void 0) {
702
+ const offset = Math.max(0, Math.min(textOffset, child.text.length));
703
+ const before = child.text.slice(0, offset);
704
+ const after = child.text.slice(offset);
705
+ const children = [];
706
+ if (before) children.push({ ...child, text: before });
707
+ children.push(image);
708
+ if (after) children.push({ ...child, text: after });
709
+ paragraph.children.splice(index, 1, ...children);
710
+ return index + (before ? 1 : 0);
711
+ }
712
+ paragraph.children.splice(index, 0, image);
713
+ return index;
714
+ }
715
+
716
+ // src/editor.tsx
452
717
  import { renderToStaticMarkup } from "react-dom/server";
453
718
  import { useVirtualizer, useWindowVirtualizer } from "@tanstack/react-virtual";
454
719
 
@@ -899,11 +1164,11 @@ function safeParagraphContext(model, nodeIndex) {
899
1164
  return { ok: false, reason: "unsupported" };
900
1165
  }
901
1166
  const paragraphRange = paragraphs[0];
902
- for (const revision of [
1167
+ for (const revision2 of [
903
1168
  ...elementsNamed(scan, "w:ins"),
904
1169
  ...elementsNamed(scan, "w:del")
905
1170
  ]) {
906
- if (revision.parentStart !== paragraphRange.start) {
1171
+ if (revision2.parentStart !== paragraphRange.start) {
907
1172
  return { ok: false, reason: "unsupported" };
908
1173
  }
909
1174
  }
@@ -940,9 +1205,9 @@ function hasNonWhitespaceOutsideChildren(xml, parent, children) {
940
1205
  }
941
1206
  return xml.slice(cursor, parent.closeStart).trim().length > 0;
942
1207
  }
943
- function revisionRangeIsSafe(context, revision, kind) {
944
- const runs = directChildElements(context.scan, revision);
945
- if (runs.length === 0 || runs.some((run) => run.tagName !== "w:r") || hasNonWhitespaceOutsideChildren(context.sourceXml, revision, runs)) {
1208
+ function revisionRangeIsSafe(context, revision2, kind) {
1209
+ const runs = directChildElements(context.scan, revision2);
1210
+ if (runs.length === 0 || runs.some((run) => run.tagName !== "w:r") || hasNonWhitespaceOutsideChildren(context.sourceXml, revision2, runs)) {
946
1211
  return false;
947
1212
  }
948
1213
  const expectedTextTag = kind === "insertion" ? "w:t" : "w:delText";
@@ -1701,12 +1966,10 @@ function updatedParagraphNodeText(source, text, options) {
1701
1966
  if (paragraphEditableText(source) === text) {
1702
1967
  return source;
1703
1968
  }
1704
- const paragraph = cloneParagraphNode(source);
1705
- paragraph.children = distributeTextAcrossParagraphChildren(
1706
- source,
1707
- text,
1708
- options
1709
- );
1969
+ const paragraph = {
1970
+ ...source,
1971
+ children: distributeTextAcrossParagraphChildren(source, text, options)
1972
+ };
1710
1973
  paragraph.sourceTextPatch = sourceTextPatchForUpdate(source, paragraph);
1711
1974
  if (!paragraph.sourceTextPatch) {
1712
1975
  paragraph.sourceXml = void 0;
@@ -1752,6 +2015,15 @@ function discardTableSourceXml(table) {
1752
2015
  table.sourceXml = void 0;
1753
2016
  table.sourceTextPatches = void 0;
1754
2017
  }
2018
+ function copyTableForCellTextEdit(table, rowIndex, cellIndex) {
2019
+ const row = table.rows[rowIndex];
2020
+ const cell = row.cells[cellIndex];
2021
+ const rows = [...table.rows];
2022
+ const cells = [...row.cells];
2023
+ cells[cellIndex] = { ...cell, nodes: [...cell.nodes] };
2024
+ rows[rowIndex] = { ...row, cells };
2025
+ return { ...table, rows };
2026
+ }
1755
2027
  function directParagraphNodeIndex(nodes, paragraphIndex) {
1756
2028
  const target = Math.max(0, Math.round(paragraphIndex));
1757
2029
  let cursor = 0;
@@ -1934,9 +2206,7 @@ function distributeTextAcrossParagraphChildren(paragraph, text, options) {
1934
2206
  }
1935
2207
  textGroups.push(currentGroup);
1936
2208
  currentGroup = [];
1937
- anchors.push(
1938
- child.type === "form-field" ? cloneFormFieldRun(child) : cloneImageRun(child)
1939
- );
2209
+ anchors.push(child);
1940
2210
  }
1941
2211
  textGroups.push(currentGroup);
1942
2212
  const allAnchorsAreImages = anchors.length > 0 && anchors.every((anchor) => anchor.type === "image");
@@ -2015,7 +2285,7 @@ function distributeTextAcrossParagraphChildren(paragraph, text, options) {
2015
2285
  });
2016
2286
  }
2017
2287
  if (index < anchors.length) {
2018
- nextChildren2.push(cloneParagraphChildRun(anchors[index]));
2288
+ nextChildren2.push(anchors[index]);
2019
2289
  }
2020
2290
  }
2021
2291
  if (nextChildren2.length > 0) {
@@ -2059,7 +2329,7 @@ function distributeTextAcrossParagraphChildren(paragraph, text, options) {
2059
2329
  });
2060
2330
  }
2061
2331
  if (index < anchors.length) {
2062
- nextChildren.push(cloneParagraphChildRun(anchors[index]));
2332
+ nextChildren.push(anchors[index]);
2063
2333
  }
2064
2334
  }
2065
2335
  if (nextChildren.length > 0) {
@@ -2380,7 +2650,7 @@ function updateTableCellText(model, tableIndex, rowIndex, cellIndex, text, optio
2380
2650
  )) {
2381
2651
  return model;
2382
2652
  }
2383
- const tableNode = cloneTableNode(sourceTable);
2653
+ const tableNode = copyTableForCellTextEdit(sourceTable, rowIndex, cellIndex);
2384
2654
  const row = tableNode.rows[rowIndex];
2385
2655
  const cell = row?.cells[cellIndex];
2386
2656
  if (!cell) {
@@ -2444,7 +2714,7 @@ function updateTableCellParagraphText(model, tableIndex, rowIndex, cellIndex, pa
2444
2714
  const sourceCell = sourceTable.rows[rowIndex]?.cells[cellIndex];
2445
2715
  const sourceParagraphNodeIndex = sourceCell ? directParagraphNodeIndex(sourceCell.nodes, paragraphIndex) : void 0;
2446
2716
  const sourceParagraph = sourceParagraphNodeIndex === void 0 ? void 0 : sourceCell?.nodes[sourceParagraphNodeIndex];
2447
- if (!sourceCell || !sourceParagraph || sourceParagraph.type !== "paragraph") {
2717
+ if (!sourceCell || !sourceParagraph || sourceParagraph.type !== "paragraph" || sourceParagraphNodeIndex === void 0) {
2448
2718
  return { ...model, nodes: [...model.nodes] };
2449
2719
  }
2450
2720
  if (paragraphTextEditIsFailClosed(sourceParagraph, text)) {
@@ -2453,17 +2723,15 @@ function updateTableCellParagraphText(model, tableIndex, rowIndex, cellIndex, pa
2453
2723
  if (paragraphEditableText(sourceParagraph) === text) {
2454
2724
  return model;
2455
2725
  }
2456
- const tableNode = cloneTableNode(sourceTable);
2457
- const row = tableNode.rows[rowIndex];
2458
- const cell = row?.cells[cellIndex];
2459
- const paragraphNodeIndex = cell ? directParagraphNodeIndex(cell.nodes, paragraphIndex) : void 0;
2460
- const paragraph = paragraphNodeIndex === void 0 ? void 0 : cell?.nodes[paragraphNodeIndex];
2461
- if (!cell || !paragraph || paragraph.type !== "paragraph" || paragraphNodeIndex === void 0) {
2462
- return { ...model, nodes: [...model.nodes] };
2463
- }
2464
- const updatedParagraph = updatedParagraphNodeText(paragraph, text, options);
2465
- cell.nodes[paragraphNodeIndex] = updatedParagraph;
2466
- if (!retainTableSourceTextPatch(tableNode, paragraph, updatedParagraph)) {
2726
+ const tableNode = copyTableForCellTextEdit(sourceTable, rowIndex, cellIndex);
2727
+ const cell = tableNode.rows[rowIndex].cells[cellIndex];
2728
+ const updatedParagraph = updatedParagraphNodeText(
2729
+ sourceParagraph,
2730
+ text,
2731
+ options
2732
+ );
2733
+ cell.nodes[sourceParagraphNodeIndex] = updatedParagraph;
2734
+ if (!retainTableSourceTextPatch(tableNode, sourceParagraph, updatedParagraph)) {
2467
2735
  discardTableSourceXml(tableNode);
2468
2736
  }
2469
2737
  const nextNodes = [...model.nodes];
@@ -2477,7 +2745,7 @@ function updateTableCellParagraphTextRecursive(model, tableIndex, rowIndex, cell
2477
2745
  }
2478
2746
  const sourceCell = sourceTable.rows[rowIndex]?.cells[cellIndex];
2479
2747
  const sourceParagraph = sourceCell ? recursiveParagraphAtIndex(sourceCell.nodes, paragraphIndex) : void 0;
2480
- if (!sourceParagraph) {
2748
+ if (!sourceCell || !sourceParagraph) {
2481
2749
  return { ...model, nodes: [...model.nodes] };
2482
2750
  }
2483
2751
  if (paragraphTextEditIsFailClosed(sourceParagraph, text)) {
@@ -2486,53 +2754,49 @@ function updateTableCellParagraphTextRecursive(model, tableIndex, rowIndex, cell
2486
2754
  if (paragraphEditableText(sourceParagraph) === text) {
2487
2755
  return model;
2488
2756
  }
2489
- const next = cloneDocModel(model);
2490
- const tableNode = next.nodes[tableIndex];
2491
- if (!tableNode || tableNode.type !== "table") {
2492
- return next;
2493
- }
2494
- const row = tableNode.rows[rowIndex];
2495
- const cell = row?.cells[cellIndex];
2496
- if (!cell) {
2497
- return next;
2498
- }
2499
- const targetParagraphIndex = Math.max(0, Math.round(paragraphIndex));
2500
- let paragraphCursor = 0;
2501
- const updateInNodes = (nodes, ancestorTables) => {
2757
+ const updatedParagraph = updatedParagraphNodeText(
2758
+ sourceParagraph,
2759
+ text,
2760
+ options
2761
+ );
2762
+ const retainSource = (table) => {
2763
+ if (!retainTableSourceTextPatch(table, sourceParagraph, updatedParagraph)) {
2764
+ discardTableSourceXml(table);
2765
+ }
2766
+ };
2767
+ const updateInNodes = (nodes) => {
2502
2768
  for (let nodeIndex = 0; nodeIndex < nodes.length; nodeIndex += 1) {
2503
2769
  const node = nodes[nodeIndex];
2504
- if (!node) {
2505
- continue;
2506
- }
2507
- if (node.type === "paragraph") {
2508
- if (paragraphCursor !== targetParagraphIndex) {
2509
- paragraphCursor += 1;
2510
- continue;
2511
- }
2512
- const updatedParagraph = updatedParagraphNodeText(node, text, options);
2513
- nodes[nodeIndex] = updatedParagraph;
2514
- ancestorTables.forEach((ancestorTable) => {
2515
- if (!retainTableSourceTextPatch(ancestorTable, node, updatedParagraph)) {
2516
- discardTableSourceXml(ancestorTable);
2517
- }
2518
- });
2519
- return true;
2520
- }
2521
- ancestorTables.push(node);
2522
- for (const nestedRow of node.rows) {
2523
- for (const nestedCell of nestedRow.cells) {
2524
- if (updateInNodes(nestedCell.nodes, ancestorTables)) {
2525
- ancestorTables.pop();
2526
- return true;
2527
- }
2770
+ if (node === sourceParagraph) {
2771
+ const nextNodes2 = [...nodes];
2772
+ nextNodes2[nodeIndex] = updatedParagraph;
2773
+ return nextNodes2;
2774
+ }
2775
+ if (node.type !== "table") continue;
2776
+ for (let rowIndex2 = 0; rowIndex2 < node.rows.length; rowIndex2 += 1) {
2777
+ const row = node.rows[rowIndex2];
2778
+ for (let cellIndex2 = 0; cellIndex2 < row.cells.length; cellIndex2 += 1) {
2779
+ const updatedNodes2 = updateInNodes(row.cells[cellIndex2].nodes);
2780
+ if (!updatedNodes2) continue;
2781
+ const table = copyTableForCellTextEdit(node, rowIndex2, cellIndex2);
2782
+ table.rows[rowIndex2].cells[cellIndex2].nodes = updatedNodes2;
2783
+ retainSource(table);
2784
+ const nextNodes2 = [...nodes];
2785
+ nextNodes2[nodeIndex] = table;
2786
+ return nextNodes2;
2528
2787
  }
2529
2788
  }
2530
- ancestorTables.pop();
2531
2789
  }
2532
- return false;
2790
+ return void 0;
2533
2791
  };
2534
- updateInNodes(cell.nodes, [tableNode]);
2535
- return next;
2792
+ const updatedNodes = updateInNodes(sourceCell.nodes);
2793
+ if (!updatedNodes) return model;
2794
+ const tableNode = copyTableForCellTextEdit(sourceTable, rowIndex, cellIndex);
2795
+ tableNode.rows[rowIndex].cells[cellIndex].nodes = updatedNodes;
2796
+ retainSource(tableNode);
2797
+ const nextNodes = [...model.nodes];
2798
+ nextNodes[tableIndex] = tableNode;
2799
+ return { ...model, nodes: nextNodes };
2536
2800
  }
2537
2801
  function mutateParagraphTextRuns(model, transform) {
2538
2802
  for (const node of model.nodes) {
@@ -2805,13 +3069,51 @@ async function serializeDocModel(model, basePackage) {
2805
3069
  model,
2806
3070
  basePackage ? mapsToWasmPackage(basePackage) : void 0
2807
3071
  );
2808
- const { parseDocx: parseDocx2 } = await import("./src-JS3OX73I.js");
3072
+ const { parseDocx: parseDocx2 } = await import("./src-DEWCGIL2.js");
2809
3073
  return parseDocx2(bytes);
2810
3074
  }
2811
3075
  async function serializeDocx(model, basePackage) {
2812
3076
  return wasmSerializeDocx(model, basePackage ? mapsToWasmPackage(basePackage) : void 0);
2813
3077
  }
2814
3078
 
3079
+ // src/font-metrics.ts
3080
+ var revision = 0;
3081
+ var observedFonts;
3082
+ var invalidators = /* @__PURE__ */ new Set();
3083
+ var listeners = /* @__PURE__ */ new Set();
3084
+ function invalidateFontMetrics() {
3085
+ revision += 1;
3086
+ invalidators.forEach((invalidate) => invalidate());
3087
+ listeners.forEach((listener) => listener());
3088
+ }
3089
+ function observeFonts() {
3090
+ const fonts = typeof document !== "undefined" ? document.fonts : void 0;
3091
+ if (fonts === observedFonts || !fonts?.addEventListener) {
3092
+ return;
3093
+ }
3094
+ observedFonts?.removeEventListener("loadingdone", invalidateFontMetrics);
3095
+ observedFonts = fonts;
3096
+ fonts.addEventListener("loadingdone", invalidateFontMetrics);
3097
+ }
3098
+ function registerFontMetricCache(invalidate) {
3099
+ invalidators.add(invalidate);
3100
+ observeFonts();
3101
+ }
3102
+ function subscribeFontMetrics(listener) {
3103
+ observeFonts();
3104
+ listeners.add(listener);
3105
+ return () => {
3106
+ listeners.delete(listener);
3107
+ };
3108
+ }
3109
+ function getFontMetricsRevision() {
3110
+ observeFonts();
3111
+ return revision;
3112
+ }
3113
+ function getServerFontMetricsRevision() {
3114
+ return 0;
3115
+ }
3116
+
2815
3117
  // src/editing-intents.ts
2816
3118
  function resolveEditingIntent(source) {
2817
3119
  const insertedText = source.data ?? source.dataTransferText ?? "";
@@ -3904,6 +4206,18 @@ function segmentTextByDocxScriptFont(text, style) {
3904
4206
  if (!text) {
3905
4207
  return [];
3906
4208
  }
4209
+ if (/^[\x00-\x7f]+$/.test(text)) {
4210
+ const script = classifyDocxFontScript(text[0], style) ?? "ascii";
4211
+ return [
4212
+ {
4213
+ text,
4214
+ startOffset: 0,
4215
+ endOffset: text.length,
4216
+ script,
4217
+ fontFamily: resolveDocxScriptFontFamily(style, script)
4218
+ }
4219
+ ];
4220
+ }
3907
4221
  const tokens = [];
3908
4222
  let offset = 0;
3909
4223
  for (const character of text) {
@@ -3937,15 +4251,24 @@ function segmentTextByDocxScriptFont(text, style) {
3937
4251
  previousScript = token.script;
3938
4252
  }
3939
4253
  const segments = [];
4254
+ const families = /* @__PURE__ */ new Map();
4255
+ let previousFamilyKey;
3940
4256
  for (const token of tokens) {
3941
4257
  const script = token.script ?? fallbackScript;
3942
- const fontFamily = resolveDocxScriptFontFamily(style, script);
4258
+ let resolved = families.get(script);
4259
+ if (!resolved) {
4260
+ const family = resolveDocxScriptFontFamily(style, script);
4261
+ resolved = { family, key: normalizedFamilyKey(family) };
4262
+ families.set(script, resolved);
4263
+ }
4264
+ const fontFamily = resolved.family;
3943
4265
  const previous = segments[segments.length - 1];
3944
- if (previous && normalizedFamilyKey(previous.fontFamily) === normalizedFamilyKey(fontFamily)) {
4266
+ if (previous && previousFamilyKey === resolved.key) {
3945
4267
  previous.text += token.text;
3946
4268
  previous.endOffset = token.endOffset;
3947
4269
  continue;
3948
4270
  }
4271
+ previousFamilyKey = resolved.key;
3949
4272
  segments.push({
3950
4273
  text: token.text,
3951
4274
  startOffset: token.startOffset,
@@ -3957,9 +4280,15 @@ function segmentTextByDocxScriptFont(text, style) {
3957
4280
  return segments;
3958
4281
  }
3959
4282
  function resolveDocxTextFontFamily(text, style) {
3960
- return segmentTextByDocxScriptFont(text, style).find(
3961
- (segment) => segment.fontFamily
3962
- )?.fontFamily ?? normalizeFamily(style?.fontFamily);
4283
+ let hasStrongScript = false;
4284
+ for (const character of text) {
4285
+ const script = classifyDocxFontScript(character, style);
4286
+ if (!script) continue;
4287
+ hasStrongScript = true;
4288
+ const family = resolveDocxScriptFontFamily(style, script);
4289
+ if (family) return family;
4290
+ }
4291
+ return (text && !hasStrongScript ? resolveDocxScriptFontFamily(style, neutralScript(style)) : void 0) ?? normalizeFamily(style?.fontFamily);
3963
4292
  }
3964
4293
 
3965
4294
  // src/editor-transaction-state.ts
@@ -4075,6 +4404,7 @@ function createTransactionalEditorStateReducer(adapter) {
4075
4404
 
4076
4405
  // src/pretext-layout.ts
4077
4406
  import {
4407
+ clearCache,
4078
4408
  layoutNextLine,
4079
4409
  measureLineStats,
4080
4410
  prepareWithSegments
@@ -4087,6 +4417,13 @@ var layoutByKey = /* @__PURE__ */ new Map();
4087
4417
  var lineCountByKey = /* @__PURE__ */ new Map();
4088
4418
  var fragmentOffsetAdvancesByFragment = /* @__PURE__ */ new WeakMap();
4089
4419
  var graphemeOffsetsByText = /* @__PURE__ */ new Map();
4420
+ registerFontMetricCache(() => {
4421
+ clearCache();
4422
+ preparedTextByKey.clear();
4423
+ layoutByKey.clear();
4424
+ lineCountByKey.clear();
4425
+ fragmentOffsetAdvancesByFragment = /* @__PURE__ */ new WeakMap();
4426
+ });
4090
4427
  var measureCanvas;
4091
4428
  var measureCanvasContext;
4092
4429
  var graphemeSegmenter;
@@ -5242,6 +5579,7 @@ var THUMBNAIL_DIRECT_TOKEN_REGEX = /(\r\n|\n|\t|[^\S\r\n\t]+|[^\s\r\n\t]+)/g;
5242
5579
  var THUMBNAIL_DIRECT_LEADING_WHITESPACE_REGEX = /^\s/;
5243
5580
  var THUMBNAIL_DIRECT_TEXT_MEASURE_CACHE_MAX_ENTRIES = 4096;
5244
5581
  var directThumbnailTextMeasureCache = /* @__PURE__ */ new Map();
5582
+ registerFontMetricCache(() => directThumbnailTextMeasureCache.clear());
5245
5583
  function measureDirectThumbnailToken(context, font, text) {
5246
5584
  if (!text) {
5247
5585
  return 0;
@@ -6112,6 +6450,15 @@ var estimatedTextAdvanceWidthByFontAndValue = /* @__PURE__ */ new Map();
6112
6450
  var pretextWordBreakModeByText = /* @__PURE__ */ new Map();
6113
6451
  var paragraphBaseFontSizePxByParagraph = /* @__PURE__ */ new WeakMap();
6114
6452
  var paragraphDominantFontFamilyByParagraph = /* @__PURE__ */ new WeakMap();
6453
+ registerFontMetricCache(() => {
6454
+ paragraphEstimatedHeightBySourceXml.clear();
6455
+ tableEstimatedHeightBySourceXml.clear();
6456
+ textWidthByFontAndValue.clear();
6457
+ estimatedTextAdvanceWidthByFontAndValue.clear();
6458
+ tableEstimatedRowHeightsByNode = /* @__PURE__ */ new WeakMap();
6459
+ wrappedLineCountByParagraph = /* @__PURE__ */ new WeakMap();
6460
+ paragraphPretextLayoutSourceCache = /* @__PURE__ */ new WeakMap();
6461
+ });
6115
6462
  function setCacheEntry(cache, key, value) {
6116
6463
  if (!cache.has(key) && cache.size >= XML_CACHE_MAX_ENTRIES) {
6117
6464
  const oldestKey = cache.keys().next().value;
@@ -6685,7 +7032,7 @@ function selectionOffsetsWithinElementDom(element) {
6685
7032
  try {
6686
7033
  const textLengthWithoutNumberingLabels = (range) => {
6687
7034
  const fragment = range.cloneContents();
6688
- fragment.querySelectorAll("[data-docx-numbering-label='true']").forEach((label) => {
7035
+ fragment.querySelectorAll("[data-docx-numbering-label='true'],[data-docx-image-location]").forEach((label) => {
6689
7036
  label.remove();
6690
7037
  });
6691
7038
  return fragment.textContent?.length ?? 0;
@@ -6713,7 +7060,7 @@ function staticRangeOffsetsWithinElementDom(element, staticRange) {
6713
7060
  try {
6714
7061
  const textLengthWithoutNumberingLabels = (range) => {
6715
7062
  const fragment = range.cloneContents();
6716
- fragment.querySelectorAll("[data-docx-numbering-label='true']").forEach((label) => {
7063
+ fragment.querySelectorAll("[data-docx-numbering-label='true'],[data-docx-image-location]").forEach((label) => {
6717
7064
  label.remove();
6718
7065
  });
6719
7066
  return fragment.textContent?.length ?? 0;
@@ -6746,7 +7093,7 @@ function setSelectionWithinElementByTextOffsetsDom(element, startOffset, endOffs
6746
7093
  let currentTextNode = walker.nextNode();
6747
7094
  while (currentTextNode) {
6748
7095
  if (currentTextNode instanceof Text && currentTextNode.parentElement?.closest(
6749
- "[data-docx-numbering-label='true']"
7096
+ "[data-docx-numbering-label='true'],[data-docx-image-location]"
6750
7097
  )) {
6751
7098
  currentTextNode = walker.nextNode();
6752
7099
  continue;
@@ -8043,7 +8390,7 @@ function tableSelectionCoversWholeTable(table, range) {
8043
8390
  }
8044
8391
  function textLengthFromRange(range) {
8045
8392
  const fragment = range.cloneContents();
8046
- fragment.querySelectorAll("[data-docx-numbering-label='true']").forEach((label) => {
8393
+ fragment.querySelectorAll("[data-docx-numbering-label='true'],[data-docx-image-location]").forEach((label) => {
8047
8394
  label.remove();
8048
8395
  });
8049
8396
  return fragment.textContent?.length ?? 0;
@@ -8339,7 +8686,7 @@ function replaceTabLayoutMarkersWithTabText(root) {
8339
8686
  function editableTextFromElement(element) {
8340
8687
  const clone = element.cloneNode(true);
8341
8688
  replaceTabLayoutMarkersWithTabText(clone);
8342
- clone.querySelectorAll("[data-docx-numbering-label='true']").forEach((label) => {
8689
+ clone.querySelectorAll("[data-docx-numbering-label='true'],[data-docx-image-location]").forEach((label) => {
8343
8690
  label.remove();
8344
8691
  });
8345
8692
  clone.querySelectorAll("br").forEach((lineBreak) => {
@@ -8717,9 +9064,15 @@ function paragraphHasOnlyWhitespaceText(paragraph) {
8717
9064
  if (child.type !== "text") {
8718
9065
  return false;
8719
9066
  }
8720
- return child.text.replace(/[\s\u00a0]+/g, "").length === 0;
9067
+ return !/[^\s\u00a0]/.test(child.text);
8721
9068
  });
8722
9069
  }
9070
+ function paragraphHasActiveNumbering(paragraph) {
9071
+ const numbering = paragraph.style?.numbering;
9072
+ return Boolean(
9073
+ numbering && Number.isFinite(numbering.numId) && Math.round(numbering.numId) > 0
9074
+ );
9075
+ }
8723
9076
  function paragraphContainsSectionBreakProperties(paragraph) {
8724
9077
  return /<w:sectPr\b/i.test(paragraph.sourceXml ?? "");
8725
9078
  }
@@ -8910,7 +9263,8 @@ function resolveForeignWrapExclusionsForFlowRange(obstacles, sourceNodeIndex, pa
8910
9263
  left: obstacle.left,
8911
9264
  right: obstacle.right,
8912
9265
  top: Math.max(0, Math.round(obstacle.top - pageFlowTopPx)),
8913
- bottom: Math.max(0, Math.round(obstacle.bottom - pageFlowTopPx))
9266
+ bottom: Math.max(0, Math.round(obstacle.bottom - pageFlowTopPx)),
9267
+ ...obstacle.fromDragPreview ? { fromDragPreview: true } : void 0
8914
9268
  });
8915
9269
  }
8916
9270
  return foreignExclusions;
@@ -9052,6 +9406,7 @@ function precomputePageSegmentForeignWrapExclusions(segments, model, availableWi
9052
9406
  };
9053
9407
  const baseFlowHeightPxBySegmentIndex = segments.map((segment) => {
9054
9408
  const node = model.nodes[segment.nodeIndex];
9409
+ if (segment.nodeIndex === interaction?.draggedTableIndex) return 0;
9055
9410
  return estimateRenderedPageSegmentHeightPx(
9056
9411
  node,
9057
9412
  segment,
@@ -9068,10 +9423,69 @@ function precomputePageSegmentForeignWrapExclusions(segments, model, availableWi
9068
9423
  );
9069
9424
  });
9070
9425
  const collectObstacles = (flowTopPxBySegmentIndex2, flowHeightPxBySegmentIndex2, previousObstacles) => {
9071
- const obstacles2 = [];
9426
+ const obstacles2 = interaction?.dragObstacle ? [
9427
+ {
9428
+ ...interaction.dragObstacle,
9429
+ sourceNodeIndex: -1,
9430
+ appliesToSourceNode: true
9431
+ }
9432
+ ] : [];
9072
9433
  for (let segmentIndex = 0; segmentIndex < segments.length; segmentIndex += 1) {
9073
9434
  const segment = segments[segmentIndex];
9074
- const node = model.nodes[segment.nodeIndex];
9435
+ let node = model.nodes[segment.nodeIndex];
9436
+ if (segment.nodeIndex === interaction?.draggedTableIndex) continue;
9437
+ if (node?.type === "paragraph" && interaction?.draggedImageKey?.startsWith(`p:${segment.nodeIndex}:`)) {
9438
+ const childIndex = Number(interaction.draggedImageKey.split(":")[2]);
9439
+ node = {
9440
+ ...node,
9441
+ children: node.children.map(
9442
+ (child, index) => index === childIndex && child.type === "image" ? {
9443
+ ...child,
9444
+ floating: {
9445
+ ...child.floating,
9446
+ wrapType: "none",
9447
+ behindDocument: true
9448
+ }
9449
+ } : child
9450
+ )
9451
+ };
9452
+ }
9453
+ const lineRange = segment.paragraphLineRange;
9454
+ const sliceTop = (lineRange?.startLineIndex ?? 0) * (lineRange?.lineHeightPx ?? 0);
9455
+ if (node?.type === "paragraph" && lineRange) {
9456
+ const paragraph = node;
9457
+ const source = buildParagraphPretextLayoutSource(paragraph);
9458
+ const unwrapped = source ? layoutParagraphPretextSource(
9459
+ paragraph,
9460
+ source,
9461
+ paragraphAvailableTextWidthPx(
9462
+ paragraph,
9463
+ segmentWidthPxAt(segment.nodeIndex),
9464
+ numberingDefinitions
9465
+ ),
9466
+ lineRange.lineHeightPx,
9467
+ []
9468
+ ) : void 0;
9469
+ node = {
9470
+ ...paragraph,
9471
+ children: paragraph.children.map((child, index) => {
9472
+ if (child.type !== "image" || !child.floating || !unwrapped)
9473
+ return child;
9474
+ const anchorTop = resolveCaretRectAtOffset(
9475
+ unwrapped,
9476
+ paragraphChildAnchorOffset(paragraph, index) + 1
9477
+ )?.top ?? 0;
9478
+ return anchorTop >= sliceTop && anchorTop < lineRange.endLineIndex * lineRange.lineHeightPx ? child : {
9479
+ ...child,
9480
+ floating: {
9481
+ ...child.floating,
9482
+ wrapType: "none",
9483
+ behindDocument: true
9484
+ }
9485
+ };
9486
+ })
9487
+ };
9488
+ }
9075
9489
  if (node?.type === "table" && node.style?.floating && !segment.tableRowRange) {
9076
9490
  const tableContainerWidthPx = segmentWidthPxAt(segment.nodeIndex);
9077
9491
  const tableFlowTopPx = flowTopPxBySegmentIndex2[segmentIndex] ?? 0;
@@ -9172,7 +9586,9 @@ function precomputePageSegmentForeignWrapExclusions(segments, model, availableWi
9172
9586
  bottom: band.bottomPx + (floating.distBPx ?? 0)
9173
9587
  });
9174
9588
  });
9175
- const flowTopPx = measuredBandAt(`para:${segment.nodeIndex}`)?.topPx ?? flowTopPxBySegmentIndex2[segmentIndex] ?? 0;
9589
+ const flowTopPx = (measuredBandAt(
9590
+ `para:${segment.nodeIndex}:${lineRange?.startLineIndex ?? 0}`
9591
+ )?.topPx ?? (!lineRange ? measuredBandAt(`para:${segment.nodeIndex}`)?.topPx : void 0) ?? flowTopPxBySegmentIndex2[segmentIndex] ?? 0) - sliceTop;
9176
9592
  obstacles2.push(
9177
9593
  ...collectPageFlowWrapObstaclesForParagraph(
9178
9594
  node,
@@ -9215,7 +9631,7 @@ function precomputePageSegmentForeignWrapExclusions(segments, model, availableWi
9215
9631
  let flowHeightPxBySegmentIndex = baseFlowHeightPxBySegmentIndex;
9216
9632
  let obstacles = firstPassObstacles;
9217
9633
  const hasInteractionPreview = Boolean(
9218
- interaction?.floatingMovePreview || interaction?.resizePreview
9634
+ interaction?.floatingMovePreview || interaction?.resizePreview || interaction?.dragObstacle
9219
9635
  );
9220
9636
  const hasFloatingTableObstacle = segments.some((segment) => {
9221
9637
  const node = model.nodes[segment.nodeIndex];
@@ -9297,7 +9713,9 @@ function precomputePageSegmentForeignWrapExclusions(segments, model, availableWi
9297
9713
  const result = segments.map((segment, segmentIndex) => {
9298
9714
  const segmentLineRange = segment.paragraphLineRange;
9299
9715
  const segmentCoversWholeParagraph = !segmentLineRange || segmentLineRange.startLineIndex === 0 && segmentLineRange.endLineIndex >= segmentLineRange.totalLineCount;
9300
- const measuredParagraphBand = segmentCoversWholeParagraph ? measuredBandAt(`para:${segment.nodeIndex}`) : void 0;
9716
+ const measuredParagraphBand = measuredBandAt(
9717
+ `para:${segment.nodeIndex}:${segmentLineRange?.startLineIndex ?? 0}`
9718
+ ) ?? (segmentCoversWholeParagraph ? measuredBandAt(`para:${segment.nodeIndex}`) : void 0);
9301
9719
  const flowTopPx = measuredParagraphBand?.topPx ?? flowTopPxBySegmentIndex[segmentIndex] ?? 0;
9302
9720
  const flowBottomPx = measuredParagraphBand?.bottomPx ?? flowTopPx + (flowHeightPxBySegmentIndex[segmentIndex] ?? 0);
9303
9721
  return resolveForeignWrapExclusionsForFlowRange(
@@ -10011,13 +10429,7 @@ function buildMeasureSegmentsPretextLayoutItems(segments, paragraphBaseFontPx, t
10011
10429
  }).filter((item) => item.endOffset > item.startOffset);
10012
10430
  }
10013
10431
  function layoutParagraphPretextSource(paragraph, source, containerWidthPx, lineHeightPx, exclusions) {
10014
- const fallbackFont = resolveMeasureFont(
10015
- firstRunStyle(paragraph),
10016
- paragraphBaseFontSizePx(paragraph),
10017
- source.text
10018
- );
10019
10432
  const wordBreak = pretextWordBreakModeForText(source.text);
10020
- const items = buildParagraphPretextLayoutItems(paragraph, source);
10021
10433
  const firstLineIndentPx = twipsToSignedPixels(
10022
10434
  paragraph.style?.indent?.firstLineTwips
10023
10435
  );
@@ -10033,6 +10445,23 @@ function layoutParagraphPretextSource(paragraph, source, containerWidthPx, lineH
10033
10445
  bottom: 1
10034
10446
  }
10035
10447
  ] : exclusions;
10448
+ const uniformFont = resolveUniformPretextSourceFont(paragraph, source);
10449
+ if (uniformFont) {
10450
+ return layoutTextWithPretextAroundExclusions(
10451
+ source.text,
10452
+ uniformFont,
10453
+ containerWidthPx,
10454
+ lineHeightPx,
10455
+ layoutExclusions,
10456
+ { wordBreak }
10457
+ );
10458
+ }
10459
+ const fallbackFont = resolveMeasureFont(
10460
+ firstRunStyle(paragraph),
10461
+ paragraphBaseFontSizePx(paragraph),
10462
+ source.text
10463
+ );
10464
+ const items = buildParagraphPretextLayoutItems(paragraph, source);
10036
10465
  const richLayout = items.length > 0 ? layoutItemsWithPretextAroundExclusions(
10037
10466
  source.text,
10038
10467
  items,
@@ -10168,19 +10597,20 @@ function resolveDualWrappedFloatingImageGeometry(image, containerWidthPx, option
10168
10597
  const paragraphLocalBaseTopPx = !hasExplicitBaseTopPx && paragraphTopPx !== void 0 && (normalizedVerticalRelativeTo === "margin" || normalizedVerticalRelativeTo === "page") ? Math.round(
10169
10598
  (normalizedVerticalRelativeTo === "page" ? baseTopPx - pageMarginTopPx : baseTopPx) - paragraphTopPx
10170
10599
  ) : baseTopPx;
10171
- const imageLeftPx = clampNumber(
10172
- baseLeftPx + deltaX,
10173
- 0,
10174
- Math.max(0, safeContainerWidthPx - imageWidthPx)
10175
- );
10600
+ const imageLeftPx = baseLeftPx + deltaX;
10176
10601
  const rawImageTopPx = (hasExplicitBaseTopPx ? baseTopPx : paragraphLocalBaseTopPx + (isInlinePreview ? 0 : distTPx)) + deltaY;
10177
10602
  const modelAllowsNegativeTop = Number.isFinite(floating?.yPx) && floating?.yPx < 0;
10178
10603
  const imageTopPx = options?.allowNegativeImageTop || modelAllowsNegativeTop ? Math.round(rawImageTopPx) : Math.max(0, Math.round(rawImageTopPx));
10179
10604
  const horizontalAlignIsOperative = !hasExplicitBaseLeftPx && !Number.isFinite(floating?.xPx);
10180
- let exclusionLeftPx = Math.max(0, imageLeftPx - distLPx);
10181
- let exclusionRightPx = Math.min(
10182
- safeContainerWidthPx,
10183
- imageLeftPx + imageWidthPx + distRPx
10605
+ let exclusionLeftPx = clampNumber(
10606
+ imageLeftPx - distLPx,
10607
+ 0,
10608
+ safeContainerWidthPx
10609
+ );
10610
+ let exclusionRightPx = clampNumber(
10611
+ imageLeftPx + imageWidthPx + distRPx,
10612
+ 0,
10613
+ safeContainerWidthPx
10184
10614
  );
10185
10615
  if (wrapType === "topAndBottom") {
10186
10616
  exclusionLeftPx = 0;
@@ -10196,11 +10626,10 @@ function resolveDualWrappedFloatingImageGeometry(image, containerWidthPx, option
10196
10626
  0,
10197
10627
  safeContainerWidthPx - exclusionRightPx
10198
10628
  );
10199
- const spansInteriorGap = exclusionLeftPx > 0 && exclusionRightPx < safeContainerWidthPx && leftBandWidthPx >= MIN_DUAL_WRAPPED_INTERIOR_BAND_PX && rightBandWidthPx >= MIN_DUAL_WRAPPED_INTERIOR_BAND_PX;
10200
- const spansSideFloat = exclusionLeftPx === 0 || exclusionRightPx === safeContainerWidthPx;
10201
- if (!spansInteriorGap && !spansSideFloat) {
10202
- return void 0;
10203
- }
10629
+ if (leftBandWidthPx < MIN_DUAL_WRAPPED_INTERIOR_BAND_PX)
10630
+ exclusionLeftPx = 0;
10631
+ if (rightBandWidthPx < MIN_DUAL_WRAPPED_INTERIOR_BAND_PX)
10632
+ exclusionRightPx = safeContainerWidthPx;
10204
10633
  }
10205
10634
  return {
10206
10635
  image,
@@ -10276,6 +10705,18 @@ function resolveParagraphDualWrappedTextLayout(paragraph, containerWidthPx, line
10276
10705
  }
10277
10706
  };
10278
10707
  }
10708
+ if (unexcludedLayout && verticalRelativeTo === "line" && anchorOffset > 0 && !options?.movePreviewByImageIndex?.has(geometry.imageIndex)) {
10709
+ const anchorTop = resolveCaretRectAtOffset(unexcludedLayout, anchorOffset + 1)?.top ?? 0;
10710
+ return {
10711
+ ...geometry,
10712
+ imageTopPx: geometry.imageTopPx + anchorTop,
10713
+ exclusion: {
10714
+ ...geometry.exclusion,
10715
+ top: geometry.exclusion.top + anchorTop,
10716
+ bottom: geometry.exclusion.bottom + anchorTop
10717
+ }
10718
+ };
10719
+ }
10279
10720
  if (!unexcludedLayout || wrapType === "topandbottom" || verticalRelativeTo !== "margin" && verticalRelativeTo !== "page") {
10280
10721
  return geometry;
10281
10722
  }
@@ -10303,7 +10744,7 @@ function resolveParagraphDualWrappedTextLayout(paragraph, containerWidthPx, line
10303
10744
  });
10304
10745
  const mergedExclusions = [
10305
10746
  ...options?.foreignExclusions ?? [],
10306
- ...anchorAdjustedGeometries.map((geometry) => geometry.exclusion)
10747
+ ...anchorAdjustedGeometries.filter((geometry) => geometry.imageIndex !== options?.excludedImageIndex).map((geometry) => geometry.exclusion)
10307
10748
  ];
10308
10749
  const layout = resolveParagraphPretextExclusionLayout(
10309
10750
  paragraph,
@@ -10691,13 +11132,17 @@ function paragraphBookmarkNames(paragraph) {
10691
11132
  );
10692
11133
  return [...new Set(names)];
10693
11134
  }
11135
+ var paragraphNoteReferences = /* @__PURE__ */ new WeakMap();
10694
11136
  function paragraphReferencedNoteIds(paragraph, noteType) {
11137
+ const cached = paragraphNoteReferences.get(paragraph);
11138
+ if (cached?.[noteType]) return cached[noteType];
11139
+ const references = [];
11140
+ paragraphNoteReferences.set(paragraph, { ...cached, [noteType]: references });
10695
11141
  const sourceXml = paragraph.sourceXml ?? "";
10696
11142
  if (!sourceXml) {
10697
- return [];
11143
+ return references;
10698
11144
  }
10699
11145
  const pattern = noteType === "footnote" ? new RegExp(FOOTNOTE_REFERENCE_XML_PATTERN.source, "gi") : new RegExp(ENDNOTE_REFERENCE_XML_PATTERN.source, "gi");
10700
- const references = [];
10701
11146
  for (const match of sourceXml.matchAll(pattern)) {
10702
11147
  const rawId = Number(match[1]);
10703
11148
  if (!Number.isFinite(rawId) || rawId < 0) {
@@ -10735,7 +11180,7 @@ function eventTargetIsInteractiveControl(target) {
10735
11180
  return false;
10736
11181
  }
10737
11182
  return Boolean(
10738
- target.closest("a[href],button,input,select,textarea,[role='checkbox']")
11183
+ target.closest("a[href],button,input,select,textarea,[role='checkbox'],[data-docx-textbox-editor='true']")
10739
11184
  );
10740
11185
  }
10741
11186
  function eventTargetIsNestedTableParagraphEditor(target) {
@@ -12922,6 +13367,111 @@ function paragraphCanSplitAcrossPages(paragraph, lineCount, options) {
12922
13367
  }
12923
13368
  return true;
12924
13369
  }
13370
+ function createTextEditPaginationMemo() {
13371
+ const entries = /* @__PURE__ */ new Map();
13372
+ const containsFloatingObjects = /* @__PURE__ */ new WeakMap();
13373
+ const hasFloatingObjects = (node) => {
13374
+ const cached = containsFloatingObjects.get(node);
13375
+ if (cached !== void 0) return cached;
13376
+ const result = node.type === "paragraph" ? node.children.some(
13377
+ (child) => child.type === "image" && child.floating
13378
+ ) : Boolean(node.style?.floating) || node.rows.some(
13379
+ (row) => row.cells.some((cell) => cell.nodes.some(hasFloatingObjects))
13380
+ );
13381
+ containsFloatingObjects.set(node, result);
13382
+ return result;
13383
+ };
13384
+ return (model, height, width, numberingDefinitions, metrics, options) => {
13385
+ if (model.nodes.some(hasFloatingObjects)) {
13386
+ return buildDocumentPageNodeSegments(
13387
+ model,
13388
+ height,
13389
+ width,
13390
+ numberingDefinitions,
13391
+ metrics,
13392
+ options
13393
+ );
13394
+ }
13395
+ const key = JSON.stringify([
13396
+ height,
13397
+ width,
13398
+ metrics,
13399
+ getFontMetricsRevision(),
13400
+ {
13401
+ ...options,
13402
+ measuredParagraphOuterHeightsPxByNodeIndex: options?.measuredParagraphOuterHeightsPxByNodeIndex ? [...options.measuredParagraphOuterHeightsPxByNodeIndex] : void 0,
13403
+ precomputedNumberingLabels: options?.precomputedNumberingLabels ? [...options.precomputedNumberingLabels] : void 0
13404
+ }
13405
+ ]);
13406
+ const cached = entries.get(key);
13407
+ const sameGeometry = cached && cached.model.metadata === model.metadata && cached.numberingDefinitions === numberingDefinitions && cached.model.nodes.length === model.nodes.length && model.nodes.every((node, index) => {
13408
+ const previous = cached.model.nodes[index];
13409
+ if (node === previous) return true;
13410
+ if (node.type !== "paragraph" || previous?.type !== "paragraph" || node.sourceXml !== previous.sourceXml || JSON.stringify(node.style) !== JSON.stringify(previous.style) || ![node, previous].every(
13411
+ (paragraph) => paragraph.children.every(
13412
+ (child) => child.type === "text" && !child.noteReference && !/[\r\n\t\f\v\u2028\u2029]/.test(child.text)
13413
+ )
13414
+ ) || paragraphHasOnlyWhitespaceText(node) !== paragraphHasOnlyWhitespaceText(previous)) {
13415
+ return false;
13416
+ }
13417
+ const source = buildParagraphPretextLayoutSource(node);
13418
+ const previousSource = buildParagraphPretextLayoutSource(previous);
13419
+ if (!source || !previousSource || !resolveUniformPretextSourceFont(node, source) || !resolveUniformPretextSourceFont(previous, previousSource))
13420
+ return false;
13421
+ const label = options?.precomputedNumberingLabels?.get(`p:${index}`);
13422
+ if (!label && paragraphHasActiveNumbering(node)) return false;
13423
+ const sectionIndex = resolvePaginationSectionMetricsIndexForNodeIndex(
13424
+ metrics ?? [],
13425
+ index,
13426
+ 0
13427
+ );
13428
+ const section = metrics?.[sectionIndex];
13429
+ const contentWidth = section?.pageContentWidthPx ?? width;
13430
+ const pitch = section?.docGridLinePitchPx;
13431
+ return estimateParagraphLineHeightPx(node, pitch) === estimateParagraphLineHeightPx(previous, pitch) && paragraphLineCountWithinWidth(
13432
+ node,
13433
+ contentWidth,
13434
+ numberingDefinitions,
13435
+ label
13436
+ ) === paragraphLineCountWithinWidth(
13437
+ previous,
13438
+ contentWidth,
13439
+ numberingDefinitions,
13440
+ label
13441
+ ) && estimateParagraphHeightPx(
13442
+ node,
13443
+ contentWidth,
13444
+ numberingDefinitions,
13445
+ pitch,
13446
+ false,
13447
+ label
13448
+ ) === estimateParagraphHeightPx(
13449
+ previous,
13450
+ contentWidth,
13451
+ numberingDefinitions,
13452
+ pitch,
13453
+ false,
13454
+ label
13455
+ );
13456
+ });
13457
+ if (sameGeometry && cached) {
13458
+ cached.model = model;
13459
+ return cached.pages;
13460
+ }
13461
+ const pages = buildDocumentPageNodeSegments(
13462
+ model,
13463
+ height,
13464
+ width,
13465
+ numberingDefinitions,
13466
+ metrics,
13467
+ options
13468
+ );
13469
+ if (entries.size >= 8 && !entries.has(key))
13470
+ entries.delete(entries.keys().next().value);
13471
+ entries.set(key, { model, numberingDefinitions, pages });
13472
+ return pages;
13473
+ };
13474
+ }
12925
13475
  function paragraphSegmentHasPartialLineRange(paragraphLineRange) {
12926
13476
  if (!paragraphLineRange) {
12927
13477
  return false;
@@ -14331,6 +14881,30 @@ function buildDocumentPageNodeSegments(model, pageContentHeightPx, pageContentWi
14331
14881
  if (currentPageSegments.length > 0 || pages.length === 0) {
14332
14882
  pages.push(currentPageSegments);
14333
14883
  }
14884
+ return keepTextAnchoredTablesWithFollowingParagraph(pages, model);
14885
+ }
14886
+ function keepTextAnchoredTablesWithFollowingParagraph(pages, model) {
14887
+ for (let pageIndex = 0; pageIndex < pages.length - 1; pageIndex++) {
14888
+ const current = pages[pageIndex];
14889
+ const next = pages[pageIndex + 1];
14890
+ const first = next[0];
14891
+ const paragraph = first && model.nodes[first.nodeIndex];
14892
+ if (paragraph?.type !== "paragraph" || (first.paragraphLineRange?.startLineIndex ?? 0) > 0 || model.metadata.sections?.some(
14893
+ (section) => section.startNodeIndex === first.nodeIndex
14894
+ ))
14895
+ continue;
14896
+ while (current.length) {
14897
+ const last = current[current.length - 1];
14898
+ const table = model.nodes[last.nodeIndex];
14899
+ const anchor = table?.type === "table" ? table.style?.floating?.verticalAnchor : void 0;
14900
+ if (anchor !== "text" || last.nodeIndex + 1 !== next[0].nodeIndex) break;
14901
+ next.unshift(current.pop());
14902
+ }
14903
+ if (!current.length) {
14904
+ pages.splice(pageIndex, 1);
14905
+ pageIndex--;
14906
+ }
14907
+ }
14334
14908
  return pages;
14335
14909
  }
14336
14910
  function shouldRenderWrappedFloatingImage(image) {
@@ -14643,7 +15217,7 @@ function resolveWrappedFloatingImageDropPatch(image, hostWidth, movedLeft, moved
14643
15217
  );
14644
15218
  const previewUsesSideFloat = previewGeometry === void 0 || previewGeometry.exclusion.left <= 0 || previewGeometry.exclusion.right >= hostWidth;
14645
15219
  const explicitTopPx = Math.round(
14646
- movedTop - Math.round(baseFloating.distTPx ?? 0)
15220
+ movedTop - Math.round(baseFloating.distTPx ?? 2)
14647
15221
  );
14648
15222
  return {
14649
15223
  wrapType,
@@ -14651,8 +15225,8 @@ function resolveWrappedFloatingImageDropPatch(image, hostWidth, movedLeft, moved
14651
15225
  horizontalAlign: preserveAlignedHorizontalPlacement ? baseFloating.horizontalAlign : previewUsesSideFloat ? side : wrapText === "bothSides" || wrapText === "largest" ? void 0 : side,
14652
15226
  xPx: preserveAlignedHorizontalPlacement ? void 0 : Math.round(movedLeft),
14653
15227
  yPx: explicitTopPx,
14654
- distLPx: Math.max(4, Math.round(baseFloating.distLPx ?? 8)),
14655
- distRPx: Math.max(4, Math.round(baseFloating.distRPx ?? 8)),
15228
+ distLPx: Math.max(0, Math.round(baseFloating.distLPx ?? 8)),
15229
+ distRPx: Math.max(0, Math.round(baseFloating.distRPx ?? 8)),
14656
15230
  distTPx: Math.max(0, Math.round(baseFloating.distTPx ?? 2)),
14657
15231
  distBPx: Math.max(0, Math.round(baseFloating.distBPx ?? 4)),
14658
15232
  horizontalRelativeTo: convertFixedPositionToMoveWithText ? "column" : baseFloating.horizontalRelativeTo ?? "column",
@@ -14694,13 +15268,6 @@ function resolveWrappedDropAnchorParagraphHost(wrapperElement, droppedTopScreenP
14694
15268
  }
14695
15269
  return best;
14696
15270
  }
14697
- function wrappedDragCanReanchorToParagraph(floating) {
14698
- if (!floatingImageMovesWithText(floating)) {
14699
- return true;
14700
- }
14701
- const verticalRelativeTo = floating.verticalRelativeTo?.trim().toLowerCase() ?? "";
14702
- return verticalRelativeTo === "" || verticalRelativeTo === "paragraph" || verticalRelativeTo === "line";
14703
- }
14704
15271
  function resolvePageSpanningAbsoluteFloatingDimensions(image, widthPx, heightPx, floatingPageOriginPx) {
14705
15272
  const floating = image.floating;
14706
15273
  const pageWidthPx = Number.isFinite(floatingPageOriginPx?.pageWidth) ? Math.max(1, Math.round(floatingPageOriginPx?.pageWidth)) : void 0;
@@ -16948,6 +17515,7 @@ function paragraphStyleRefFieldValueSequence(paragraph) {
16948
17515
  function normalizeStyleRefTarget(value) {
16949
17516
  return value.replace(/\s+/g, " ").trim().toLowerCase();
16950
17517
  }
17518
+ var styledRunTextByParagraph = /* @__PURE__ */ new WeakMap();
16951
17519
  function paragraphStyledRunText(paragraph, styleRefTarget) {
16952
17520
  const xml = paragraph.sourceXml ?? "";
16953
17521
  if (!xml) {
@@ -16957,6 +17525,12 @@ function paragraphStyledRunText(paragraph, styleRefTarget) {
16957
17525
  if (!normalizedTarget) {
16958
17526
  return void 0;
16959
17527
  }
17528
+ let cached = styledRunTextByParagraph.get(paragraph);
17529
+ if (cached?.has(normalizedTarget)) return cached.get(normalizedTarget);
17530
+ if (!cached) {
17531
+ cached = /* @__PURE__ */ new Map();
17532
+ styledRunTextByParagraph.set(paragraph, cached);
17533
+ }
16960
17534
  const textChunks = [];
16961
17535
  for (const runMatch of xml.matchAll(/<w:r\b[\s\S]*?<\/w:r>/gi)) {
16962
17536
  const runXml = runMatch[0] ?? "";
@@ -16981,7 +17555,9 @@ function paragraphStyledRunText(paragraph, styleRefTarget) {
16981
17555
  }
16982
17556
  }
16983
17557
  const joined = textChunks.join("").trim();
16984
- return joined.length > 0 ? joined : void 0;
17558
+ const result = joined.length > 0 ? joined : void 0;
17559
+ setCacheEntry(cached, normalizedTarget, result);
17560
+ return result;
16985
17561
  }
16986
17562
  function tabLeaderStyle(leader, color) {
16987
17563
  const normalizedLeader = leader === "middleDot" ? "dot" : leader;
@@ -20550,45 +21126,6 @@ function imageLocationKey(location) {
20550
21126
  function dropTargetKey(target) {
20551
21127
  return `${paragraphLocationKey(target)}:${target.childIndex}`;
20552
21128
  }
20553
- function parseImageDropTargetFromDataset(dataset) {
20554
- const kind = dataset.docxTargetKind;
20555
- const childIndex = Number.parseInt(dataset.docxChildIndex ?? "", 10);
20556
- if (!Number.isFinite(childIndex) || childIndex < 0) {
20557
- return void 0;
20558
- }
20559
- if (kind === "paragraph") {
20560
- const nodeIndex = Number.parseInt(dataset.docxNodeIndex ?? "", 10);
20561
- if (!Number.isFinite(nodeIndex) || nodeIndex < 0) {
20562
- return void 0;
20563
- }
20564
- return {
20565
- kind: "paragraph",
20566
- nodeIndex,
20567
- childIndex
20568
- };
20569
- }
20570
- if (kind === "table-cell") {
20571
- const tableIndex = Number.parseInt(dataset.docxTableIndex ?? "", 10);
20572
- const rowIndex = Number.parseInt(dataset.docxRowIndex ?? "", 10);
20573
- const cellIndex = Number.parseInt(dataset.docxCellIndex ?? "", 10);
20574
- const paragraphIndex = Number.parseInt(
20575
- dataset.docxParagraphIndex ?? "",
20576
- 10
20577
- );
20578
- if (!Number.isFinite(tableIndex) || !Number.isFinite(rowIndex) || !Number.isFinite(cellIndex) || !Number.isFinite(paragraphIndex) || tableIndex < 0 || rowIndex < 0 || cellIndex < 0 || paragraphIndex < 0) {
20579
- return void 0;
20580
- }
20581
- return {
20582
- kind: "table-cell",
20583
- tableIndex,
20584
- rowIndex,
20585
- cellIndex,
20586
- paragraphIndex,
20587
- childIndex
20588
- };
20589
- }
20590
- return void 0;
20591
- }
20592
21129
  function firstTableCellAnchorLocation(table, tableIndex) {
20593
21130
  for (let rowIndex = 0; rowIndex < table.rows.length; rowIndex += 1) {
20594
21131
  const row = table.rows[rowIndex];
@@ -22275,19 +22812,18 @@ function updateSectionImageFloatingAtLocation(model, location, patch) {
22275
22812
  return changed ? next : model;
22276
22813
  }
22277
22814
  function useDocxEditor(options = {}) {
22278
- const starterTemplateRef = React.useRef(
22279
- cloneDocModelWithBlockIds(options.starterModel ?? defaultStarterModel)
22815
+ const [starterTemplate] = React.useState(
22816
+ () => cloneDocModelWithBlockIds(options.starterModel ?? defaultStarterModel)
22280
22817
  );
22281
- const initialModelRef = React.useRef(
22282
- cloneDocModelWithBlockIds(
22283
- options.document?.model ?? starterTemplateRef.current
22284
- )
22818
+ const starterTemplateRef = React.useRef(starterTemplate);
22819
+ const [initialModel] = React.useState(
22820
+ () => cloneDocModelWithBlockIds(options.document?.model ?? starterTemplate)
22285
22821
  );
22286
22822
  const [editorState, dispatchCanonicalEditorState] = React.useReducer(
22287
22823
  reduceDocxEditorState,
22288
- initialModelRef.current,
22289
- (initialModel) => ({
22290
- model: cloneDocModelWithBlockIds(initialModel),
22824
+ initialModel,
22825
+ (initialModel2) => ({
22826
+ model: cloneDocModelWithBlockIds(initialModel2),
22291
22827
  selection: {
22292
22828
  kind: "paragraph",
22293
22829
  nodeIndex: 0
@@ -22489,8 +23025,10 @@ function useDocxEditor(options = {}) {
22489
23025
  window.clearTimeout(selectionSessionTimeoutRef.current);
22490
23026
  }
22491
23027
  selectionSessionTimeoutRef.current = null;
22492
- selectionSessionRef.current = "idle";
22493
- setSelectionSessionKind("idle");
23028
+ if (selectionSessionRef.current !== "idle") {
23029
+ selectionSessionRef.current = "idle";
23030
+ setSelectionSessionKind("idle");
23031
+ }
22494
23032
  },
22495
23033
  []
22496
23034
  );
@@ -22500,8 +23038,10 @@ function useDocxEditor(options = {}) {
22500
23038
  window.clearTimeout(selectionSessionTimeoutRef.current);
22501
23039
  }
22502
23040
  selectionSessionTimeoutRef.current = null;
22503
- selectionSessionRef.current = kind;
22504
- setSelectionSessionKind(kind);
23041
+ if (selectionSessionRef.current !== kind) {
23042
+ selectionSessionRef.current = kind;
23043
+ setSelectionSessionKind(kind);
23044
+ }
22505
23045
  if (Number.isFinite(options2?.settleAfterMs) && options2?.settleAfterMs > 0 && typeof window !== "undefined") {
22506
23046
  const expectedKind = kind;
22507
23047
  selectionSessionTimeoutRef.current = window.setTimeout(() => {
@@ -22750,6 +23290,9 @@ function useDocxEditor(options = {}) {
22750
23290
  }
22751
23291
  });
22752
23292
  }
23293
+ if (loadedEmbeddedFontFacesRef.current.length > 0) {
23294
+ invalidateFontMetrics();
23295
+ }
22753
23296
  loadedEmbeddedFontFacesRef.current = [];
22754
23297
  }, []);
22755
23298
  const loadEmbeddedFontsFromPackage = React.useCallback(
@@ -22789,6 +23332,9 @@ function useDocxEditor(options = {}) {
22789
23332
  document.fonts.add(fontFace);
22790
23333
  });
22791
23334
  loadedEmbeddedFontFacesRef.current = loadedFontFaces;
23335
+ if (loadedFontFaces.length > 0) {
23336
+ invalidateFontMetrics();
23337
+ }
22792
23338
  try {
22793
23339
  await document.fonts.ready;
22794
23340
  } catch {
@@ -22892,7 +23438,8 @@ function useDocxEditor(options = {}) {
22892
23438
  setHistoryRestoreRequest({
22893
23439
  nonce: nextNonce,
22894
23440
  selection: cloneEditorSelection(next.selection),
22895
- activeTextRange: cloneTextRange(next.activeTextRange)
23441
+ activeTextRange: cloneTextRange(next.activeTextRange),
23442
+ textInput: patch.textInput
22896
23443
  });
22897
23444
  }
22898
23445
  if (patch.clearSelectedFormField) {
@@ -23176,7 +23723,8 @@ function useDocxEditor(options = {}) {
23176
23723
  markDocxImportPerformance(workerStartMark);
23177
23724
  const importResult = await importDocxBuffer(buffer, {
23178
23725
  signal: importAbortController.signal,
23179
- transferBuffer: true
23726
+ transferBuffer: true,
23727
+ useWorker: "required"
23180
23728
  });
23181
23729
  markDocxImportPerformance(workerEndMark);
23182
23730
  measureDocxImportPerformance(
@@ -25249,7 +25797,16 @@ function useDocxEditor(options = {}) {
25249
25797
  const moveFloatingImage = React.useCallback(
25250
25798
  (location, patch, options2) => {
25251
25799
  applyModelChange((current) => {
25252
- const next = cloneDocModel(current);
25800
+ const sourceLocation = imageLocationToParagraphLocation(location);
25801
+ const next = copyModelForParagraphEdits(current, [
25802
+ sourceLocation,
25803
+ ...options2?.reparentToParagraphNodeIndex !== void 0 ? [
25804
+ {
25805
+ kind: "paragraph",
25806
+ nodeIndex: options2.reparentToParagraphNodeIndex
25807
+ }
25808
+ ] : []
25809
+ ]);
25253
25810
  const { paragraph, tableNode } = getParagraphAtLocation(
25254
25811
  next,
25255
25812
  imageLocationToParagraphLocation(location)
@@ -25257,22 +25814,32 @@ function useDocxEditor(options = {}) {
25257
25814
  if (!paragraph) {
25258
25815
  return current;
25259
25816
  }
25260
- const child = paragraph.children[location.childIndex];
25261
- if (!child || child.type !== "image") {
25817
+ const sourceChild = paragraph.children[location.childIndex];
25818
+ if (!sourceChild || sourceChild.type !== "image") {
25262
25819
  return current;
25263
25820
  }
25264
- child.floating = {
25265
- ...child.floating ?? {},
25266
- ...patch
25821
+ const child = {
25822
+ ...sourceChild,
25823
+ floating: { ...sourceChild.floating, ...patch }
25267
25824
  };
25825
+ paragraph.children[location.childIndex] = child;
25268
25826
  const reparentNodeIndex = options2?.reparentToParagraphNodeIndex;
25269
25827
  const reparentTarget = reparentNodeIndex !== void 0 ? next.nodes[reparentNodeIndex] : void 0;
25270
- if (reparentTarget && reparentTarget.type === "paragraph" && reparentTarget !== paragraph) {
25828
+ if (reparentTarget?.type === "paragraph" && (reparentTarget !== paragraph || options2?.anchorTextOffset !== void 0)) {
25271
25829
  paragraph.children.splice(location.childIndex, 1);
25272
- if (paragraph.children.length === 0) {
25830
+ const target = imageDropPositionAtTextOffset(
25831
+ reparentTarget,
25832
+ options2?.anchorTextOffset ?? 0,
25833
+ { fieldText: formFieldDisplayValue2 }
25834
+ );
25835
+ insertImageAtDropPosition(
25836
+ reparentTarget,
25837
+ child,
25838
+ target.childIndex,
25839
+ target.textOffset
25840
+ );
25841
+ if (paragraph.children.length === 0)
25273
25842
  paragraph.children.push({ type: "text", text: "" });
25274
- }
25275
- reparentTarget.children.unshift(child);
25276
25843
  reparentTarget.sourceXml = void 0;
25277
25844
  }
25278
25845
  paragraph.sourceXml = void 0;
@@ -25406,11 +25973,14 @@ function useDocxEditor(options = {}) {
25406
25973
  if (sameParagraphLocation(
25407
25974
  sourceParagraphLocation,
25408
25975
  targetParagraphLocation
25409
- ) && (target.childIndex === source.childIndex || target.childIndex === source.childIndex + 1)) {
25976
+ ) && target.textOffset === void 0 && (target.childIndex === source.childIndex || target.childIndex === source.childIndex + 1)) {
25410
25977
  return;
25411
25978
  }
25412
25979
  applyModelChange((current) => {
25413
- const next = cloneDocModel(current);
25980
+ const next = copyModelForParagraphEdits(current, [
25981
+ sourceParagraphLocation,
25982
+ targetParagraphLocation
25983
+ ]);
25414
25984
  const sourceLookup = getParagraphAtLocation(
25415
25985
  next,
25416
25986
  sourceParagraphLocation
@@ -25430,21 +26000,19 @@ function useDocxEditor(options = {}) {
25430
26000
  }
25431
26001
  const movedImage = {
25432
26002
  ...sourceChild,
25433
- data: sourceChild.data ? new Uint8Array(sourceChild.data) : void 0,
25434
26003
  floating: sourceChild.floating ? { ...sourceChild.floating } : void 0
25435
26004
  };
25436
26005
  sourceParagraph.children.splice(source.childIndex, 1);
25437
- let insertionIndex = Math.max(
25438
- 0,
25439
- Math.min(target.childIndex, targetParagraph.children.length)
25440
- );
25441
- if (sameParagraphLocation(
26006
+ const insertionIndex = target.childIndex - (sameParagraphLocation(
25442
26007
  sourceParagraphLocation,
25443
26008
  targetParagraphLocation
25444
- ) && source.childIndex < insertionIndex) {
25445
- insertionIndex -= 1;
25446
- }
25447
- targetParagraph.children.splice(insertionIndex, 0, movedImage);
26009
+ ) && source.childIndex < target.childIndex ? 1 : 0);
26010
+ insertImageAtDropPosition(
26011
+ targetParagraph,
26012
+ movedImage,
26013
+ insertionIndex,
26014
+ target.textOffset
26015
+ );
25448
26016
  if (sourceParagraph.children.length === 0) {
25449
26017
  sourceParagraph.children.push({ type: "text", text: "" });
25450
26018
  }
@@ -25705,11 +26273,12 @@ function useDocxEditor(options = {}) {
25705
26273
  if (!tableNode || tableNode.type !== "table") {
25706
26274
  return current;
25707
26275
  }
25708
- const next = cloneDocModel(current);
25709
- const nextTable = next.nodes[tableIndex];
25710
- if (!nextTable || nextTable.type !== "table") {
25711
- return current;
25712
- }
26276
+ const next = copyModelForParagraphEdits(
26277
+ current,
26278
+ options2?.moveToNodeIndex !== void 0 ? [{ kind: "paragraph", nodeIndex: options2.moveToNodeIndex }] : []
26279
+ );
26280
+ const nextTable = { ...tableNode };
26281
+ next.nodes[tableIndex] = nextTable;
25713
26282
  nextTable.style = {
25714
26283
  ...nextTable.style ?? {},
25715
26284
  floating: {
@@ -25718,25 +26287,43 @@ function useDocxEditor(options = {}) {
25718
26287
  }
25719
26288
  };
25720
26289
  nextTable.sourceXml = void 0;
25721
- const moveToNodeIndex = options2?.moveToNodeIndex;
25722
- if (moveToNodeIndex !== void 0 && moveToNodeIndex !== tableIndex && moveToNodeIndex !== tableIndex + 1) {
25723
- const clampedTarget = clampNumber(
25724
- moveToNodeIndex,
26290
+ let moveToNodeIndex = options2?.moveToNodeIndex;
26291
+ const anchorParagraph = moveToNodeIndex !== void 0 ? next.nodes[moveToNodeIndex] : void 0;
26292
+ if (anchorParagraph?.type === "paragraph" && options2?.anchorTextOffset !== void 0 && options2.anchorTextOffset > 0) {
26293
+ const split = splitParagraphChildrenAtTextOffsets(
26294
+ anchorParagraph,
26295
+ paragraphText(anchorParagraph),
26296
+ options2.anchorTextOffset,
26297
+ options2.anchorTextOffset
26298
+ );
26299
+ const after = {
26300
+ ...anchorParagraph,
26301
+ blockId: allocateBlockId(),
26302
+ children: split.afterChildren,
26303
+ sourceXml: void 0,
26304
+ style: {
26305
+ ...anchorParagraph.style,
26306
+ spacing: { ...anchorParagraph.style?.spacing, beforeTwips: 0 }
26307
+ }
26308
+ };
26309
+ anchorParagraph.children = split.beforeChildren;
26310
+ anchorParagraph.sourceXml = void 0;
26311
+ anchorParagraph.style = {
26312
+ ...anchorParagraph.style,
26313
+ spacing: { ...anchorParagraph.style?.spacing, afterTwips: 0 }
26314
+ };
26315
+ moveToNodeIndex = moveToNodeIndex + 1;
26316
+ next.nodes.splice(moveToNodeIndex, 0, after);
26317
+ }
26318
+ const sourceIndex = next.nodes.indexOf(nextTable);
26319
+ if (moveToNodeIndex !== void 0 && moveToNodeIndex !== sourceIndex && moveToNodeIndex !== sourceIndex + 1) {
26320
+ const target = clampNumber(moveToNodeIndex, 0, next.nodes.length);
26321
+ next.nodes.splice(sourceIndex, 1);
26322
+ next.nodes.splice(
26323
+ target > sourceIndex ? target - 1 : target,
25725
26324
  0,
25726
- next.nodes.length
26325
+ nextTable
25727
26326
  );
25728
- const extracted = next.nodes.splice(tableIndex, 1)[0];
25729
- if (extracted) {
25730
- next.nodes.splice(
25731
- clampNumber(
25732
- clampedTarget > tableIndex ? clampedTarget - 1 : clampedTarget,
25733
- 0,
25734
- next.nodes.length
25735
- ),
25736
- 0,
25737
- extracted
25738
- );
25739
- }
25740
26327
  }
25741
26328
  return next;
25742
26329
  }, "Moved table");
@@ -26232,7 +26819,8 @@ function useDocxEditor(options = {}) {
26232
26819
  }
26233
26820
  },
26234
26821
  pushHistory: !coalesce,
26235
- clearSelectedFormField: true
26822
+ clearSelectedFormField: true,
26823
+ textInput: true
26236
26824
  };
26237
26825
  });
26238
26826
  },
@@ -26284,7 +26872,8 @@ function useDocxEditor(options = {}) {
26284
26872
  offset: caret.end
26285
26873
  }
26286
26874
  },
26287
- clearSelectedFormField: true
26875
+ clearSelectedFormField: true,
26876
+ textInput: true
26288
26877
  };
26289
26878
  });
26290
26879
  },
@@ -27191,11 +27780,16 @@ function useDocxPageThumbnails(editor, options = {}) {
27191
27780
  () => ensureDocxViewerPageSurfaceRegistry(pageSurfaceRegistryEditor),
27192
27781
  [pageSurfaceRegistryEditor]
27193
27782
  );
27783
+ const hasRequestedThumbnails = options.pageIndexes?.length !== 0;
27194
27784
  React.useEffect(
27195
- () => subscribeDocxViewerPageSurfaces(pageSurfaceRegistryEditor, () => {
27785
+ () => {
27786
+ if (!hasRequestedThumbnails) return;
27196
27787
  setPageSurfaceEpoch((current) => current + 1);
27197
- }),
27198
- [pageSurfaceRegistryEditor]
27788
+ return subscribeDocxViewerPageSurfaces(pageSurfaceRegistryEditor, () => {
27789
+ setPageSurfaceEpoch((current) => current + 1);
27790
+ });
27791
+ },
27792
+ [hasRequestedThumbnails, pageSurfaceRegistryEditor]
27199
27793
  );
27200
27794
  const mountedPageElements = React.useMemo(
27201
27795
  () => new Map(pageSurfaceRegistry.pageElements),
@@ -28899,6 +29493,11 @@ function DocxEditorViewer({
28899
29493
  onFormFieldDoubleClick,
28900
29494
  mode = "edit"
28901
29495
  }) {
29496
+ const fontMetricsRevision = React.useSyncExternalStore(
29497
+ subscribeFontMetrics,
29498
+ getFontMetricsRevision,
29499
+ getServerFontMetricsRevision
29500
+ );
28902
29501
  const pageSurfaceRegistryOwner = docxViewerPageSurfaceRegistryOwner(editor);
28903
29502
  const trackedChangesEnabled = showTrackedChanges ?? editor.showTrackedChanges;
28904
29503
  const hasTrackedChanges = editor.trackedChanges.length > 0;
@@ -28938,6 +29537,9 @@ function DocxEditorViewer({
28938
29537
  );
28939
29538
  const viewerRootRef = React.useRef(null);
28940
29539
  const tableCellDraftsRef = React.useRef(/* @__PURE__ */ new Map());
29540
+ const plainParagraphHtmlCacheRef = React.useRef(
29541
+ /* @__PURE__ */ new WeakMap()
29542
+ );
28941
29543
  const editableParagraphHtmlPropsRef = React.useRef(/* @__PURE__ */ new Map());
28942
29544
  const editableTableCellHtmlPropsRef = React.useRef(/* @__PURE__ */ new Map());
28943
29545
  const tableCellParagraphDraftsRef = React.useRef(
@@ -29232,7 +29834,9 @@ function DocxEditorViewer({
29232
29834
  const suppressNextTableCellClickRef = React.useRef(false);
29233
29835
  const [tableCellSelectionRange, setTableCellSelectionRange] = React.useState();
29234
29836
  const [tableMoveDropPreview, setTableMoveDropPreview] = React.useState();
29235
- const [tableFloatingDragPreview, setTableFloatingDragPreview] = React.useState();
29837
+ const [objectWrapDragPreview, setObjectWrapDragPreview] = React.useState();
29838
+ const objectDragCleanupRef = React.useRef(void 0);
29839
+ React.useEffect(() => () => objectDragCleanupRef.current?.(), [editor.documentLoadNonce, editor.model, isReadOnly]);
29236
29840
  const [tableContextMenuState, setTableContextMenuState] = React.useState();
29237
29841
  const [contextMenuState, setContextMenuState] = React.useState();
29238
29842
  const [activeHeaderFooterEdit, setActiveHeaderFooterEdit] = React.useState();
@@ -29277,7 +29881,6 @@ function DocxEditorViewer({
29277
29881
  if (typeof window === "undefined") {
29278
29882
  return;
29279
29883
  }
29280
- setPostImportPaginationUnlocked(true);
29281
29884
  const safeDelayMs = Math.max(
29282
29885
  48,
29283
29886
  Math.round(delayMs ?? PAGINATION_MEASUREMENT_INTERACTION_DEBOUNCE_MS)
@@ -29288,6 +29891,7 @@ function DocxEditorViewer({
29288
29891
  }
29289
29892
  paginationMeasurementResumeTimeoutRef.current = window.setTimeout(() => {
29290
29893
  paginationMeasurementResumeTimeoutRef.current = null;
29894
+ setPostImportPaginationUnlocked(true);
29291
29895
  bumpPaginationMeasurementEpoch();
29292
29896
  }, safeDelayMs + 16);
29293
29897
  },
@@ -29367,7 +29971,6 @@ function DocxEditorViewer({
29367
29971
  setHoveredEmbeddedTableKey(void 0);
29368
29972
  setFocusedEmbeddedTableKey(void 0);
29369
29973
  setTableMoveDropPreview(void 0);
29370
- setTableFloatingDragPreview(void 0);
29371
29974
  tableMoveDragRef.current = void 0;
29372
29975
  setTableContextMenuState(void 0);
29373
29976
  setContextMenuState(void 0);
@@ -29488,10 +30091,26 @@ function DocxEditorViewer({
29488
30091
  Math.round(documentLayout.marginsPx.left),
29489
30092
  Math.round(documentLayout.marginsPx.right),
29490
30093
  trackedChangesEnabled ? "tc1" : "tc0",
30094
+ fontMetricsRevision,
29491
30095
  docNodeContentSignature(paginationSectionMetrics)
29492
30096
  ].join("|"),
29493
- [documentLayout, paginationSectionMetrics, trackedChangesEnabled]
30097
+ [
30098
+ documentLayout,
30099
+ fontMetricsRevision,
30100
+ paginationSectionMetrics,
30101
+ trackedChangesEnabled
30102
+ ]
29494
30103
  );
30104
+ React.useEffect(() => {
30105
+ setMeasuredPageContentHeightByIndex((current) => current.length ? [] : current);
30106
+ setMeasuredPageContentIdentityKeysByIndex(
30107
+ (current) => current.length ? [] : current
30108
+ );
30109
+ setMeasuredPageContentValidationByIndex(
30110
+ (current) => current.length ? [] : current
30111
+ );
30112
+ setHasMeasuredBodyFooterOverlap(false);
30113
+ }, [fontMetricsRevision]);
29495
30114
  React.useEffect(() => {
29496
30115
  setMeasuredParagraphOuterHeightsPxByBlockId(
29497
30116
  (current) => current.size === 0 ? current : /* @__PURE__ */ new Map()
@@ -29550,7 +30169,7 @@ function DocxEditorViewer({
29550
30169
  pageContentWidthPxByNodeIndex: widthByNodeIndex,
29551
30170
  pageContentHeightPxByNodeIndex: heightByNodeIndex
29552
30171
  };
29553
- }, [editor.model.nodes, paginationSectionMetrics]);
30172
+ }, [editor.model.nodes.length, paginationSectionMetrics]);
29554
30173
  const sectionColumnsBySectionIndex = React.useMemo(
29555
30174
  () => documentSections.map(
29556
30175
  (section) => parseSectionColumns(section.sectionPropertiesXml)
@@ -29561,6 +30180,23 @@ function DocxEditorViewer({
29561
30180
  () => buildParagraphNumberingLabels(editor.model),
29562
30181
  [editor.model]
29563
30182
  );
30183
+ const noteMarkerIndexes = React.useMemo(
30184
+ () => ({
30185
+ footnote: new Map(
30186
+ editor.model.metadata.footnotes?.map((note, index) => [
30187
+ note.id,
30188
+ index + 1
30189
+ ])
30190
+ ),
30191
+ endnote: new Map(
30192
+ editor.model.metadata.endnotes?.map((note, index) => [
30193
+ note.id,
30194
+ index + 1
30195
+ ])
30196
+ )
30197
+ }),
30198
+ [editor.model.metadata.footnotes, editor.model.metadata.endnotes]
30199
+ );
29564
30200
  const bookmarkTargetNodeIndexByName = React.useMemo(() => {
29565
30201
  const targets = /* @__PURE__ */ new Map();
29566
30202
  editor.model.nodes.forEach((node, nodeIndex) => {
@@ -29617,6 +30253,9 @@ function DocxEditorViewer({
29617
30253
  tableDraftLayoutEpoch,
29618
30254
  tableMeasuredRowHeights
29619
30255
  ]);
30256
+ const textEditPaginationMemo = React.useRef(null);
30257
+ if (!textEditPaginationMemo.current)
30258
+ textEditPaginationMemo.current = createTextEditPaginationMemo();
29620
30259
  const pageSegmentationPlan = React.useMemo(() => {
29621
30260
  const pageContentHeightPx = Math.max(
29622
30261
  120,
@@ -29637,7 +30276,7 @@ function DocxEditorViewer({
29637
30276
  const canUseMeasuredPageContentHeights = !disableMeasuredImportPagination && !floatingMovePreview && !dropCapMovePreview && (measuredPageContentHeightsPxForPagination?.length ?? 0) > 0;
29638
30277
  const buildEstimatedPagesForInputs = createEstimatedPagesBuildMemo(
29639
30278
  (measuredTableRowHeightsByNodeIndex, measuredPageContentHeightsPx, strictLastRenderedParagraphStartBreaks) => {
29640
- const buildPagesWithHeights = (resolvedPageContentHeights) => buildDocumentPageNodeSegments(
30279
+ const buildPagesWithHeights = (resolvedPageContentHeights) => textEditPaginationMemo.current(
29641
30280
  editor.model,
29642
30281
  pageContentHeightPx,
29643
30282
  pageContentWidthPx,
@@ -30452,7 +31091,7 @@ function DocxEditorViewer({
30452
31091
  [explicitPageVirtualizationZoomScale]
30453
31092
  );
30454
31093
  React.useEffect(() => {
30455
- if (floatingMovePreview || resizePreview || tableFloatingDragPreview || tableMoveDragRef.current) {
31094
+ if (floatingMovePreview || objectWrapDragPreview || resizePreview || tableMoveDragRef.current) {
30456
31095
  return;
30457
31096
  }
30458
31097
  const modelChanged = wrapBandReconcileModelRef.current !== editor.model;
@@ -30490,6 +31129,8 @@ function DocxEditorViewer({
30490
31129
  }
30491
31130
  pageHasFloatingObject = true;
30492
31131
  fresh.set(`tbl:${nodeIndex}`, toBand(element.getBoundingClientRect()));
31132
+ const host = element.closest("[data-docx-floating-table-host]");
31133
+ if (host) fresh.set(`tblanchor:${nodeIndex}`, toBand(host.getBoundingClientRect()));
30493
31134
  });
30494
31135
  bodyElement.querySelectorAll("[data-docx-image-location]").forEach((span) => {
30495
31136
  if (!(span instanceof HTMLElement)) {
@@ -30540,6 +31181,10 @@ function DocxEditorViewer({
30540
31181
  }
30541
31182
  fresh.set(`para:${nodeIndex}`, toBand(rect));
30542
31183
  });
31184
+ bodyElement.querySelectorAll("[data-docx-paragraph-host='true'][data-docx-paragraph-kind='paragraph']").forEach((element) => {
31185
+ const rect = element.getBoundingClientRect();
31186
+ if (rect.height > 0) fresh.set(`para:${element.dataset.docxParagraphNodeIndex}:${element.dataset.docxParagraphStartLine ?? 0}`, toBand(rect));
31187
+ });
30543
31188
  });
30544
31189
  const withinTolerance = (previous, candidate) => Math.abs(previous.topPx - candidate.topPx) <= WRAP_BAND_RECONCILE_TOLERANCE_PX && Math.abs(previous.bottomPx - candidate.bottomPx) <= WRAP_BAND_RECONCILE_TOLERANCE_PX && Math.abs(previous.leftPx - candidate.leftPx) <= WRAP_BAND_RECONCILE_TOLERANCE_PX && Math.abs(previous.rightPx - candidate.rightPx) <= WRAP_BAND_RECONCILE_TOLERANCE_PX;
30545
31190
  const next = /* @__PURE__ */ new Map();
@@ -30692,11 +31337,18 @@ function DocxEditorViewer({
30692
31337
  overscan: pageVirtualizationOverscan
30693
31338
  });
30694
31339
  const internalPageVirtualizer = internalVirtualScrollUsesWindow ? internalWindowPageVirtualizer : internalElementPageVirtualizer;
31340
+ const virtualPageHeightSignature = pageSectionInfoByIndex.map(
31341
+ ({ layout }) => Math.round(
31342
+ (layout.pageHeightPx + DOC_PAGE_BREAK_GAP) * virtualizerMeasurementScale
31343
+ )
31344
+ ).join("|");
30695
31345
  React.useLayoutEffect(() => {
30696
31346
  internalElementPageVirtualizer.measure();
30697
31347
  internalWindowPageVirtualizer.measure();
30698
31348
  }, [
30699
- estimateVirtualPageSize,
31349
+ virtualPageHeightSignature,
31350
+ documentLayout.pageHeightPx,
31351
+ virtualizerMeasurementScale,
30700
31352
  internalElementPageVirtualizer,
30701
31353
  internalWindowPageVirtualizer
30702
31354
  ]);
@@ -32357,16 +33009,126 @@ function DocxEditorViewer({
32357
33009
  },
32358
33010
  [editor.model.nodes.length]
32359
33011
  );
33012
+ const objectWrapDragSignatureRef = React.useRef("");
33013
+ const objectWrapDragLatestRef = React.useRef(void 0);
33014
+ const updateObjectWrapDragPreview = React.useCallback(
33015
+ (frame, source, distances) => {
33016
+ const bandsByPage = /* @__PURE__ */ new Map();
33017
+ let pageIndex = -1;
33018
+ let closestDistance = Infinity;
33019
+ let obstacle;
33020
+ pageBodyElementsRef.current.forEach((body, index) => {
33021
+ if (!body.isConnected) return;
33022
+ const origin = body.getBoundingClientRect();
33023
+ const scale = resolveViewerMeasurementZoomScale(body, 1);
33024
+ const surface = body.closest("[data-docx-page-surface='true']")?.getBoundingClientRect() ?? origin;
33025
+ if (surface.bottom < -frame.height || surface.top > window.innerHeight + frame.height)
33026
+ return;
33027
+ const distance = Math.max(surface.top - frame.top, frame.top - surface.bottom, 0) + Math.max(surface.left - frame.left, frame.left - surface.right, 0);
33028
+ if (distance < closestDistance) {
33029
+ closestDistance = distance;
33030
+ pageIndex = index;
33031
+ if (distances) {
33032
+ const width = origin.width / scale;
33033
+ let left = Math.max(
33034
+ 0,
33035
+ (frame.left - origin.left) / scale - distances.left
33036
+ );
33037
+ let right = Math.min(
33038
+ width,
33039
+ (frame.left + frame.width - origin.left) / scale + distances.right
33040
+ );
33041
+ if (distances.fullWidth || left < MIN_DUAL_WRAPPED_INTERIOR_BAND_PX)
33042
+ left = 0;
33043
+ if (distances.fullWidth || width - right < MIN_DUAL_WRAPPED_INTERIOR_BAND_PX)
33044
+ right = width;
33045
+ obstacle = {
33046
+ left,
33047
+ right,
33048
+ top: (frame.top - origin.top) / scale - distances.top,
33049
+ bottom: (frame.top + frame.height - origin.top) / scale + distances.bottom,
33050
+ fromDragPreview: true
33051
+ };
33052
+ }
33053
+ }
33054
+ const bands = /* @__PURE__ */ new Map();
33055
+ const record = (key, element) => {
33056
+ if (!element.isConnected || !body.contains(element)) return;
33057
+ const rect = element.getBoundingClientRect();
33058
+ bands.set(key, {
33059
+ topPx: (rect.top - origin.top) / scale,
33060
+ bottomPx: (rect.bottom - origin.top) / scale,
33061
+ leftPx: (rect.left - origin.left) / scale,
33062
+ rightPx: (rect.right - origin.left) / scale
33063
+ });
33064
+ };
33065
+ paragraphElementsRef.current.forEach(
33066
+ (element, nodeIndex) => record(`para:${nodeIndex}`, element)
33067
+ );
33068
+ body.querySelectorAll(
33069
+ "[data-docx-paragraph-host='true'][data-docx-paragraph-kind='paragraph']"
33070
+ ).forEach((element) => {
33071
+ record(
33072
+ `para:${element.dataset.docxParagraphNodeIndex}:${element.dataset.docxParagraphStartLine ?? 0}`,
33073
+ element
33074
+ );
33075
+ });
33076
+ tableElementsRef.current.forEach((element, nodeIndex) => {
33077
+ record(`tbl:${nodeIndex}`, element);
33078
+ const host = element.closest(
33079
+ "[data-docx-floating-table-host]"
33080
+ );
33081
+ if (host) record(`tblanchor:${nodeIndex}`, host);
33082
+ });
33083
+ body.querySelectorAll("[data-docx-image-location]").forEach((element) => {
33084
+ record(`img:${element.dataset.docxImageLocation}`, element);
33085
+ });
33086
+ bandsByPage.set(index, bands);
33087
+ });
33088
+ const preview = { ...source, pageIndex, obstacle, bandsByPage };
33089
+ objectWrapDragLatestRef.current = preview;
33090
+ const signature = JSON.stringify([
33091
+ source,
33092
+ pageIndex,
33093
+ obstacle,
33094
+ [...bandsByPage].map(([index, bands]) => [index, [...bands]])
33095
+ ]);
33096
+ if (objectWrapDragSignatureRef.current !== signature) {
33097
+ objectWrapDragSignatureRef.current = signature;
33098
+ setObjectWrapDragPreview(preview);
33099
+ }
33100
+ },
33101
+ [resolveViewerMeasurementZoomScale]
33102
+ );
33103
+ const clearObjectDragPreview = React.useCallback(
33104
+ (preserveMeasurements = false) => {
33105
+ if (preserveMeasurements && objectWrapDragLatestRef.current) {
33106
+ const next = new Map(measuredWrapBands);
33107
+ objectWrapDragLatestRef.current.bandsByPage.forEach((bands) => {
33108
+ bands.forEach((band, key) => {
33109
+ if (!key.startsWith("img:")) next.set(key, band);
33110
+ });
33111
+ });
33112
+ setMeasuredWrapBands(next);
33113
+ }
33114
+ objectWrapDragLatestRef.current = void 0;
33115
+ objectWrapDragSignatureRef.current = "";
33116
+ objectDragCleanupRef.current = void 0;
33117
+ setObjectWrapDragPreview(void 0);
33118
+ setFloatingMovePreview(void 0);
33119
+ tableMoveDragRef.current = void 0;
33120
+ },
33121
+ [measuredWrapBands]
33122
+ );
32360
33123
  const beginTableMoveDrag = React.useCallback(
32361
33124
  (event, tableIndex) => {
32362
- if (isReadOnly) {
32363
- return;
32364
- }
32365
- if (event.button !== 0 && event.button !== void 0) {
32366
- return;
32367
- }
33125
+ if (isReadOnly || event.button !== 0) return;
33126
+ const element = tableElementsRef.current.get(tableIndex);
33127
+ const table = editor.model.nodes[tableIndex];
33128
+ if (!element || table?.type !== "table") return;
32368
33129
  event.preventDefault();
32369
33130
  event.stopPropagation();
33131
+ objectDragCleanupRef.current?.();
32370
33132
  selectWholeTable(tableIndex);
32371
33133
  setHoveredTableHandleTableIndex(tableIndex);
32372
33134
  tableMoveDragRef.current = {
@@ -32376,108 +33138,82 @@ function DocxEditorViewer({
32376
33138
  startY: event.clientY,
32377
33139
  hasMoved: false
32378
33140
  };
32379
- const zoomScale = resolveViewerMeasurementZoomScale(
32380
- viewerRootRef.current,
32381
- 1
32382
- );
32383
- const tableElementAtStart = tableElementsRef.current.get(tableIndex);
32384
- const tableRectAtStart = tableElementAtStart?.getBoundingClientRect();
32385
- const floatingHostRectAtStart = tableElementAtStart?.closest("[data-docx-floating-table-host]")?.getBoundingClientRect();
32386
- const onPointerMove = (pointerMoveEvent) => {
32387
- const dragState = tableMoveDragRef.current;
32388
- if (!dragState || pointerMoveEvent.pointerId !== dragState.pointerId) {
32389
- return;
32390
- }
32391
- const movedX = pointerMoveEvent.clientX - dragState.startX;
32392
- const movedY = pointerMoveEvent.clientY - dragState.startY;
32393
- if (!dragState.hasMoved && Math.abs(movedX) + Math.abs(movedY) < TABLE_MOVE_DRAG_THRESHOLD_PX) {
32394
- return;
32395
- }
32396
- dragState.hasMoved = true;
32397
- setTableFloatingDragPreview({
32398
- tableIndex: dragState.tableIndex,
32399
- deltaX: normalizeFloatingDragDeltaForZoom(movedX, zoomScale),
32400
- deltaY: normalizeFloatingDragDeltaForZoom(movedY, zoomScale)
32401
- });
32402
- pointerMoveEvent.preventDefault();
32403
- };
32404
- const onPointerUp = (pointerUpEvent) => {
32405
- const dragState = tableMoveDragRef.current;
32406
- if (!dragState || pointerUpEvent.pointerId !== dragState.pointerId) {
32407
- return;
32408
- }
32409
- window.removeEventListener("pointermove", onPointerMove);
32410
- window.removeEventListener("pointerup", onPointerUp);
32411
- window.removeEventListener("pointercancel", onPointerUp);
32412
- tableMoveDragRef.current = void 0;
32413
- const pointerTarget = document.elementFromPoint(
32414
- pointerUpEvent.clientX,
32415
- pointerUpEvent.clientY
32416
- );
32417
- const tableElement = tableElementsRef.current.get(dragState.tableIndex);
32418
- const pointerStillOverTable = Boolean(
32419
- pointerTarget && tableElement?.contains(pointerTarget)
32420
- );
32421
- setHoveredTableHandleTableIndex(
32422
- pointerStillOverTable ? dragState.tableIndex : void 0
32423
- );
32424
- setTableFloatingDragPreview(void 0);
32425
- if (!dragState.hasMoved || !tableRectAtStart) {
32426
- return;
32427
- }
32428
- const deltaX = normalizeFloatingDragDeltaForZoom(
32429
- pointerUpEvent.clientX - dragState.startX,
32430
- zoomScale
32431
- );
32432
- const deltaY = normalizeFloatingDragDeltaForZoom(
32433
- pointerUpEvent.clientY - dragState.startY,
32434
- zoomScale
32435
- );
32436
- const tableNode = editor.model.nodes[dragState.tableIndex];
32437
- if (!tableNode || tableNode.type !== "table") {
32438
- return;
33141
+ const zoom = resolveViewerMeasurementZoomScale(element, 1);
33142
+ const floating = table.style?.floating;
33143
+ objectDragCleanupRef.current = startObjectDrag({
33144
+ event,
33145
+ element,
33146
+ zoom,
33147
+ resolveElement: () => tableElementsRef.current.get(tableIndex),
33148
+ onFrame: (frame) => {
33149
+ if (tableMoveDragRef.current)
33150
+ tableMoveDragRef.current.hasMoved = true;
33151
+ updateObjectWrapDragPreview(
33152
+ frame,
33153
+ { tableIndex },
33154
+ {
33155
+ left: twipsToPixels(floating?.leftFromTextTwips) ?? 12,
33156
+ right: twipsToPixels(floating?.rightFromTextTwips) ?? 12,
33157
+ top: twipsToPixels(floating?.topFromTextTwips) ?? 0,
33158
+ bottom: twipsToPixels(floating?.bottomFromTextTwips) ?? 0
33159
+ }
33160
+ );
33161
+ },
33162
+ onCancel: clearObjectDragPreview,
33163
+ onDrop: (frame) => {
33164
+ const anchor = resolveWrappedDropAnchorParagraphHost(
33165
+ element,
33166
+ frame.top,
33167
+ frame.left
33168
+ );
33169
+ const body = anchor?.host.closest("[data-docx-page-surface='true']")?.querySelector("[data-docx-page-body]");
33170
+ const host = tableElementsRef.current.get(tableIndex)?.closest("[data-docx-floating-table-host]");
33171
+ const anchorRect = anchor?.rect ?? host?.getBoundingClientRect();
33172
+ const bodyRect = body?.getBoundingClientRect();
33173
+ const startLine = Number(
33174
+ anchor?.host.dataset.docxParagraphStartLine ?? 0
33175
+ );
33176
+ const anchorParagraph = anchor ? editor.model.nodes[anchor.nodeIndex] : void 0;
33177
+ const anchorSource = anchorParagraph?.type === "paragraph" ? buildParagraphPretextLayoutSource(anchorParagraph) : void 0;
33178
+ const anchorLayout = anchorParagraph?.type === "paragraph" && anchorSource && anchorRect ? layoutParagraphPretextSource(
33179
+ anchorParagraph,
33180
+ anchorSource,
33181
+ anchorRect.width / zoom,
33182
+ Number(anchor?.host.dataset.docxParagraphLineHeight) || estimateParagraphLineHeightPx(anchorParagraph),
33183
+ []
33184
+ ) : void 0;
33185
+ const anchorTextOffset = startLine > 0 ? anchorLayout?.lines[startLine]?.fragments[0]?.startOffset : void 0;
33186
+ clearObjectDragPreview();
33187
+ if (!anchorRect) return;
33188
+ editor.setTableFloating(
33189
+ tableIndex,
33190
+ {
33191
+ xTwips: pixelsToTwips(
33192
+ (frame.left - (bodyRect?.left ?? anchorRect.left)) / zoom
33193
+ ),
33194
+ yTwips: pixelsToTwips((frame.top - anchorRect.top) / zoom),
33195
+ horizontalAnchor: "margin",
33196
+ verticalAnchor: "text",
33197
+ horizontalAlign: void 0,
33198
+ verticalAlign: void 0,
33199
+ leftFromTextTwips: floating?.leftFromTextTwips ?? 180,
33200
+ rightFromTextTwips: floating?.rightFromTextTwips ?? 180,
33201
+ topFromTextTwips: floating?.topFromTextTwips ?? 0,
33202
+ bottomFromTextTwips: floating?.bottomFromTextTwips ?? 0
33203
+ },
33204
+ anchor ? { moveToNodeIndex: anchor.nodeIndex, anchorTextOffset } : void 0
33205
+ );
32439
33206
  }
32440
- const currentFloating = tableNode.style?.floating;
32441
- const baseLeftPx = floatingHostRectAtStart ? (tableRectAtStart.left - floatingHostRectAtStart.left) / zoomScale : twipsToSignedPixels(tableNode.style?.indentTwips) ?? 0;
32442
- const baseTopPx = floatingHostRectAtStart ? (tableRectAtStart.top - floatingHostRectAtStart.top) / zoomScale : 0;
32443
- const dropTarget = resolveTableMoveDropTarget(dragState.tableIndex, {
32444
- x: pointerUpEvent.clientX,
32445
- y: pointerUpEvent.clientY
32446
- });
32447
- const rootRect = viewerRootRef.current?.getBoundingClientRect();
32448
- const shouldReanchor = dropTarget !== void 0 && rootRect !== void 0 && dropTarget.targetNodeIndex !== dragState.tableIndex && dropTarget.targetNodeIndex !== dragState.tableIndex + 1;
32449
- let committedTopPx = baseTopPx + deltaY;
32450
- if (shouldReanchor && dropTarget && rootRect) {
32451
- let targetScreenTop = rootRect.top + dropTarget.preview.top;
32452
- if (!floatingHostRectAtStart && dropTarget.targetNodeIndex > dragState.tableIndex) {
32453
- targetScreenTop -= tableRectAtStart.height;
32454
- }
32455
- const droppedTopScreenPx = tableRectAtStart.top + (pointerUpEvent.clientY - dragState.startY);
32456
- committedTopPx = (droppedTopScreenPx - targetScreenTop) / zoomScale;
32457
- }
32458
- committedTopPx = Math.max(0, committedTopPx);
32459
- editor.setTableFloating(
32460
- dragState.tableIndex,
32461
- {
32462
- xTwips: pixelsToTwips(baseLeftPx + deltaX),
32463
- yTwips: pixelsToTwips(committedTopPx),
32464
- horizontalAnchor: "margin",
32465
- verticalAnchor: "text",
32466
- horizontalAlign: void 0,
32467
- verticalAlign: void 0,
32468
- leftFromTextTwips: currentFloating?.leftFromTextTwips ?? 187,
32469
- rightFromTextTwips: currentFloating?.rightFromTextTwips ?? 187,
32470
- topFromTextTwips: currentFloating?.topFromTextTwips ?? 0,
32471
- bottomFromTextTwips: currentFloating?.bottomFromTextTwips ?? 0
32472
- },
32473
- shouldReanchor && dropTarget ? { moveToNodeIndex: dropTarget.targetNodeIndex } : void 0
32474
- );
32475
- };
32476
- window.addEventListener("pointermove", onPointerMove);
32477
- window.addEventListener("pointerup", onPointerUp, { once: true });
32478
- window.addEventListener("pointercancel", onPointerUp, { once: true });
33207
+ });
32479
33208
  },
32480
- [editor, isReadOnly, resolveTableMoveDropTarget, selectWholeTable]
33209
+ [
33210
+ editor,
33211
+ isReadOnly,
33212
+ selectWholeTable,
33213
+ resolveViewerMeasurementZoomScale,
33214
+ updateObjectWrapDragPreview,
33215
+ clearObjectDragPreview
33216
+ ]
32481
33217
  );
32482
33218
  const closeTableContextMenu = React.useCallback(() => {
32483
33219
  setTableContextMenuState(void 0);
@@ -32684,12 +33420,13 @@ function DocxEditorViewer({
32684
33420
  let range2;
32685
33421
  if (typeof documentWithCaret.caretPositionFromPoint === "function") {
32686
33422
  const position = documentWithCaret.caretPositionFromPoint(x, y);
32687
- if (position) {
33423
+ if (position && position.offset >= 0 && position.offset <= (position.offsetNode.nodeType === Node.TEXT_NODE ? position.offsetNode.textContent?.length ?? 0 : position.offsetNode.childNodes.length)) {
32688
33424
  range2 = document.createRange();
32689
33425
  range2.setStart(position.offsetNode, position.offset);
32690
33426
  range2.collapse(true);
32691
33427
  }
32692
- } else if (typeof documentWithCaret.caretRangeFromPoint === "function") {
33428
+ }
33429
+ if (!range2 && typeof documentWithCaret.caretRangeFromPoint === "function") {
32693
33430
  const pointRange = documentWithCaret.caretRangeFromPoint(x, y);
32694
33431
  if (pointRange) {
32695
33432
  range2 = pointRange;
@@ -32706,7 +33443,7 @@ function DocxEditorViewer({
32706
33443
  let bestDistance = Number.POSITIVE_INFINITY;
32707
33444
  let node = walker.nextNode();
32708
33445
  while (node) {
32709
- if (node instanceof Text && node.parentElement?.closest("[data-docx-numbering-label='true']")) {
33446
+ if (node instanceof Text && node.parentElement?.closest("[data-docx-numbering-label='true'],[data-docx-image-location]")) {
32710
33447
  node = walker.nextNode();
32711
33448
  continue;
32712
33449
  }
@@ -32868,7 +33605,7 @@ function DocxEditorViewer({
32868
33605
  let currentTextNode = walker.nextNode();
32869
33606
  while (currentTextNode) {
32870
33607
  if (currentTextNode instanceof Text && currentTextNode.parentElement?.closest(
32871
- "[data-docx-numbering-label='true']"
33608
+ "[data-docx-numbering-label='true'],[data-docx-image-location]"
32872
33609
  )) {
32873
33610
  currentTextNode = walker.nextNode();
32874
33611
  continue;
@@ -32985,7 +33722,7 @@ function DocxEditorViewer({
32985
33722
  let currentTextNode = walker.nextNode();
32986
33723
  while (currentTextNode) {
32987
33724
  if (currentTextNode instanceof Text && currentTextNode.parentElement?.closest(
32988
- "[data-docx-numbering-label='true']"
33725
+ "[data-docx-numbering-label='true'],[data-docx-image-location]"
32989
33726
  )) {
32990
33727
  currentTextNode = walker.nextNode();
32991
33728
  continue;
@@ -33276,8 +34013,8 @@ function DocxEditorViewer({
33276
34013
  const offset = clampNumber(
33277
34014
  resolveOffsetAtPoint(
33278
34015
  registeredSurface.layout,
33279
- point.x - surfaceRect.left,
33280
- point.y - surfaceRect.top
34016
+ (point.x - surfaceRect.left) / resolveEffectiveZoomScale(registeredSurface.element),
34017
+ (point.y - surfaceRect.top) / resolveEffectiveZoomScale(registeredSurface.element)
33281
34018
  ),
33282
34019
  0,
33283
34020
  registeredSurface.textLength
@@ -33330,6 +34067,7 @@ function DocxEditorViewer({
33330
34067
  return void 0;
33331
34068
  }
33332
34069
  const referenceElement = container instanceof Element ? container : container.parentElement;
34070
+ if (referenceElement?.closest("[data-docx-textbox-editor='true']")) return void 0;
33333
34071
  const paragraphElement = referenceElement?.closest(
33334
34072
  "[data-docx-paragraph-kind]"
33335
34073
  );
@@ -33998,7 +34736,7 @@ function DocxEditorViewer({
33998
34736
  if (!textarea || !textarea.isConnected) {
33999
34737
  return;
34000
34738
  }
34001
- textarea.focus();
34739
+ textarea.focus({ preventScroll: true });
34002
34740
  const pendingSelection = wrappedParagraphPendingSelectionRef.current;
34003
34741
  const session = activeWrappedParagraphSession;
34004
34742
  const selectionSource = pendingSelection && (!session || pendingSelection.locationKey === session.locationKey) ? pendingSelection : session ? {
@@ -34089,6 +34827,54 @@ function DocxEditorViewer({
34089
34827
  flushActiveRangeFromSelection();
34090
34828
  }, 80);
34091
34829
  }, [flushActiveRangeFromSelection]);
34830
+ React.useLayoutEffect(() => {
34831
+ const request = editor.historyRestoreRequest;
34832
+ if (!request?.textInput || !request.activeTextRange || request.nonce === appliedHistoryRestoreNonceRef.current || compositionActiveRef.current) {
34833
+ return;
34834
+ }
34835
+ const { start, end } = request.activeTextRange;
34836
+ const host = resolveParagraphHostElement(start.location);
34837
+ const startPosition = resolveDomPositionFromBoundary(start);
34838
+ const endPosition = resolveDomPositionFromBoundary(end);
34839
+ const selection = window.getSelection();
34840
+ if (!host?.isContentEditable || !startPosition || !endPosition || !selection) {
34841
+ return;
34842
+ }
34843
+ try {
34844
+ beginSelectionIntent();
34845
+ const focusedElement = document.activeElement;
34846
+ if (!(focusedElement instanceof HTMLElement) || !focusedElement.isContentEditable || !focusedElement.contains(host)) {
34847
+ const editableRoot = host.closest("[contenteditable='true']") ?? host;
34848
+ editableRoot.focus({ preventScroll: true });
34849
+ }
34850
+ if (selection.anchorNode !== startPosition.node || selection.anchorOffset !== startPosition.offset || selection.focusNode !== endPosition.node || selection.focusOffset !== endPosition.offset) {
34851
+ if (typeof selection.setBaseAndExtent === "function") {
34852
+ selection.setBaseAndExtent(
34853
+ startPosition.node,
34854
+ startPosition.offset,
34855
+ endPosition.node,
34856
+ endPosition.offset
34857
+ );
34858
+ } else {
34859
+ const range = document.createRange();
34860
+ const forward = compareTextRangeBoundaries(start, end) <= 0;
34861
+ const first = forward ? startPosition : endPosition;
34862
+ const last = forward ? endPosition : startPosition;
34863
+ range.setStart(first.node, first.offset);
34864
+ range.setEnd(last.node, last.offset);
34865
+ selection.removeAllRanges();
34866
+ selection.addRange(range);
34867
+ }
34868
+ }
34869
+ appliedHistoryRestoreNonceRef.current = request.nonce;
34870
+ } catch {
34871
+ }
34872
+ }, [
34873
+ beginSelectionIntent,
34874
+ editor.historyRestoreRequest,
34875
+ resolveDomPositionFromBoundary,
34876
+ resolveParagraphHostElement
34877
+ ]);
34092
34878
  React.useEffect(() => {
34093
34879
  const restoreRequest = editor.historyRestoreRequest;
34094
34880
  if (!restoreRequest) {
@@ -34594,6 +35380,33 @@ function DocxEditorViewer({
34594
35380
  tableContextMenuState
34595
35381
  ]
34596
35382
  );
35383
+ const onViewerFormFieldDoubleClick = React.useCallback(
35384
+ (event) => {
35385
+ if (isReadOnly || !(event.target instanceof Element)) {
35386
+ return;
35387
+ }
35388
+ const fieldElement = event.target.closest("[data-docx-form-field='true']");
35389
+ const paragraphLocation = parseParagraphLocationFromElement(
35390
+ fieldElement?.closest("[data-docx-paragraph-kind]") ?? null
35391
+ );
35392
+ const childIndex = parseDocumentIndexFromAttribute(
35393
+ fieldElement?.getAttribute("data-docx-form-field-child-index") ?? null
35394
+ );
35395
+ if (!paragraphLocation || childIndex === void 0) {
35396
+ return;
35397
+ }
35398
+ const field = getParagraphAtLocation(editor.model, paragraphLocation).paragraph?.children[childIndex];
35399
+ if (field?.type !== "form-field") {
35400
+ return;
35401
+ }
35402
+ event.preventDefault();
35403
+ event.stopPropagation();
35404
+ const location = { ...paragraphLocation, childIndex };
35405
+ editor.selectFormField(location);
35406
+ onFormFieldDoubleClick?.(location);
35407
+ },
35408
+ [editor.model, editor.selectFormField, isReadOnly, onFormFieldDoubleClick]
35409
+ );
34597
35410
  const onViewerContextMenu = React.useCallback(
34598
35411
  (event) => {
34599
35412
  if (isReadOnly || event.defaultPrevented) {
@@ -35885,7 +36698,7 @@ function DocxEditorViewer({
35885
36698
  try {
35886
36699
  const textLengthWithoutNumberingLabels = (range) => {
35887
36700
  const fragment = range.cloneContents();
35888
- fragment.querySelectorAll("[data-docx-numbering-label='true']").forEach((label) => {
36701
+ fragment.querySelectorAll("[data-docx-numbering-label='true'],[data-docx-image-location]").forEach((label) => {
35889
36702
  label.remove();
35890
36703
  });
35891
36704
  return fragment.textContent?.length ?? 0;
@@ -36419,7 +37232,7 @@ function DocxEditorViewer({
36419
37232
  if (!targetElement) {
36420
37233
  return;
36421
37234
  }
36422
- if (targetElement.closest("textarea, input") || targetElement.closest("[data-docx-header-footer-region]")) {
37235
+ if (targetElement.closest("textarea, input, [data-docx-textbox-editor='true']") || targetElement.closest("[data-docx-header-footer-region]")) {
36423
37236
  return;
36424
37237
  }
36425
37238
  const intent = resolveEditingIntent({
@@ -37566,288 +38379,229 @@ function DocxEditorViewer({
37566
38379
  );
37567
38380
  const beginFloatingImageMove = React.useCallback(
37568
38381
  (event, location, image, isWrappedFloatingImage, isAbsoluteFloatingImage) => {
37569
- if (isReadOnly) {
37570
- return;
37571
- }
37572
- if (event.button !== 0) {
37573
- return;
37574
- }
37575
- if (event.target.closest(
38382
+ if (isReadOnly || event.button !== 0 || event.target.closest(
37576
38383
  "[data-image-resize-handle='true']"
37577
- )) {
38384
+ ))
37578
38385
  return;
37579
- }
37580
- const isInlineImage = !isWrappedFloatingImage && !isAbsoluteFloatingImage;
37581
38386
  event.preventDefault();
37582
38387
  event.stopPropagation();
38388
+ objectDragCleanupRef.current?.();
37583
38389
  setSelectedImage(location);
38390
+ const element = event.currentTarget;
37584
38391
  const imageKey = imageLocationKey(location);
37585
- const startX = event.clientX;
37586
- const startY = event.clientY;
37587
- const inlineDragActivationThresholdPx = 4;
37588
- const wrapperElement = event.currentTarget;
37589
- const wrapperRect = wrapperElement.getBoundingClientRect();
37590
- const pageSurface = wrapperElement.closest(
37591
- "[data-docx-page-surface='true']"
37592
- );
37593
- const pageSurfaceRect = pageSurface?.getBoundingClientRect();
37594
- const paragraphHost = wrapperElement.closest(
37595
- "[data-docx-paragraph-host='true']"
37596
- );
37597
- const paragraphRect = paragraphHost?.getBoundingClientRect();
37598
- const zoomScale = resolveViewerMeasurementZoomScale(wrapperElement, 1);
37599
- const baseFloating = image.floating ? { ...image.floating } : {};
37600
- const dragStartAnchor = isWrappedFloatingImage && location.kind === "paragraph" ? resolveWrappedDropAnchorParagraphHost(
37601
- wrapperElement,
37602
- wrapperRect.top,
37603
- wrapperRect.left
37604
- ) : void 0;
37605
- let latestDeltaX = 0;
37606
- let latestDeltaY = 0;
37607
- let frameId;
37608
- let inlineDragActivated = false;
37609
- let latestInlineDropTarget;
37610
- let latestInlineDropTargetKey;
37611
- const measuredBands = (() => {
37612
- if (!isWrappedFloatingImage || !pageSurface || !pageSurfaceRect) {
37613
- return void 0;
37614
- }
37615
- const hostByNodeIndex = {};
37616
- for (const host of Array.from(
37617
- pageSurface.querySelectorAll(
37618
- "[data-docx-paragraph-host='true'][data-docx-paragraph-kind='paragraph'][data-docx-paragraph-node-index]"
37619
- )
37620
- )) {
37621
- const nodeIndex = Number.parseInt(
37622
- host.getAttribute("data-docx-paragraph-node-index") ?? "",
37623
- 10
37624
- );
37625
- if (!Number.isFinite(nodeIndex)) {
37626
- continue;
37627
- }
37628
- const rect = host.getBoundingClientRect();
37629
- if (rect.height <= 0) {
37630
- continue;
37631
- }
37632
- hostByNodeIndex[nodeIndex] = {
37633
- topPx: (rect.top - pageSurfaceRect.top) / zoomScale,
37634
- bottomPx: (rect.bottom - pageSurfaceRect.top) / zoomScale
37635
- };
37636
- }
37637
- return Object.keys(hostByNodeIndex).length > 0 ? { hostByNodeIndex } : void 0;
37638
- })();
37639
- const updatePreview = () => {
37640
- setFloatingMovePreview({
37641
- imageKey,
37642
- deltaX: latestDeltaX,
37643
- deltaY: latestDeltaY,
37644
- measuredBands,
37645
- ...paragraphRect ? {
37646
- baseLeftPx: Math.round(
37647
- (wrapperRect.left - paragraphRect.left) / zoomScale
37648
- ),
37649
- baseTopPx: Math.round(
37650
- (wrapperRect.top - paragraphRect.top) / zoomScale
37651
- )
37652
- } : void 0
37653
- });
37654
- };
37655
- const onPointerMove = (pointerEvent) => {
37656
- latestDeltaX = normalizeFloatingDragDeltaForZoom(
37657
- pointerEvent.clientX - startX,
37658
- zoomScale
38392
+ const zoom = resolveViewerMeasurementZoomScale(element, 1);
38393
+ const isInline = !isWrappedFloatingImage && !isAbsoluteFloatingImage;
38394
+ const floating = image.floating;
38395
+ let caret;
38396
+ const resolveInlineTarget = (frame) => {
38397
+ const point = { x: frame.clientX, y: frame.clientY };
38398
+ const boundary = resolveBoundaryFromPoint(point);
38399
+ if (!boundary) return void 0;
38400
+ const paragraph = getParagraphAtLocation(
38401
+ editor.model,
38402
+ boundary.location
38403
+ ).paragraph;
38404
+ if (!paragraph) return void 0;
38405
+ const wrapped = wrappedParagraphSurfaceRegistryRef.current.get(
38406
+ paragraphLocationKey(boundary.location)
37659
38407
  );
37660
- latestDeltaY = normalizeFloatingDragDeltaForZoom(
37661
- pointerEvent.clientY - startY,
37662
- zoomScale
38408
+ const layoutSource = wrapped ? buildParagraphPretextLayoutSource(paragraph) : void 0;
38409
+ const run = layoutSource?.runs.find(
38410
+ (candidate) => candidate.endOffset >= boundary.offset
37663
38411
  );
37664
- if (isInlineImage) {
37665
- if (!inlineDragActivated) {
37666
- const movementDistancePx = Math.abs(latestDeltaX) + Math.abs(latestDeltaY);
37667
- if (movementDistancePx < inlineDragActivationThresholdPx) {
37668
- return;
37669
- }
37670
- inlineDragActivated = true;
37671
- draggedImageRef.current = location;
37672
- setIsDraggingImage(true);
37673
- setActiveDropTarget(void 0);
37674
- }
37675
- const candidate = document.elementFromPoint(pointerEvent.clientX, pointerEvent.clientY)?.closest(
37676
- "[data-docx-image-drop-zone='true']"
38412
+ const runIndex = run ? Number(/^run-(\d+)/.exec(run.key)?.[1]) : void 0;
38413
+ const position = run && runIndex !== void 0 && Number.isFinite(runIndex) ? run.kind === "image" ? {
38414
+ childIndex: runIndex + (boundary.offset > (run.startOffset + run.endOffset) / 2 ? 1 : 0)
38415
+ } : {
38416
+ childIndex: runIndex,
38417
+ textOffset: Math.max(0, boundary.offset - run.startOffset)
38418
+ } : imageDropPositionAtTextOffset(paragraph, boundary.offset, {
38419
+ fieldText: formFieldDisplayValue2
38420
+ });
38421
+ const target = { ...boundary.location, ...position };
38422
+ let rect;
38423
+ if (wrapped) {
38424
+ const local = resolveCaretRectAtOffset(
38425
+ wrapped.layout,
38426
+ boundary.offset
37677
38427
  );
37678
- const parsedTarget = candidate ? parseImageDropTargetFromDataset(candidate.dataset) : void 0;
37679
- const parsedTargetKey = parsedTarget ? dropTargetKey(parsedTarget) : void 0;
37680
- if (parsedTargetKey !== latestInlineDropTargetKey) {
37681
- latestInlineDropTarget = parsedTarget;
37682
- latestInlineDropTargetKey = parsedTargetKey;
37683
- setActiveDropTarget(parsedTarget);
37684
- }
37685
- }
37686
- if (frameId !== void 0) {
37687
- window.cancelAnimationFrame(frameId);
38428
+ const origin = wrapped.element.getBoundingClientRect();
38429
+ const scale = resolveViewerMeasurementZoomScale(wrapped.element, 1);
38430
+ if (local)
38431
+ rect = {
38432
+ left: origin.left + local.left * scale,
38433
+ top: origin.top + local.top * scale,
38434
+ height: local.height * scale
38435
+ };
38436
+ } else {
38437
+ const doc = document;
38438
+ const range = doc.caretRangeFromPoint?.(point.x, point.y);
38439
+ const candidate = range?.getBoundingClientRect();
38440
+ if (candidate?.height) rect = candidate;
38441
+ }
38442
+ if (!caret) {
38443
+ caret = document.createElement("div");
38444
+ caret.setAttribute("data-docx-image-drop-caret", "true");
38445
+ Object.assign(caret.style, {
38446
+ position: "fixed",
38447
+ pointerEvents: "none",
38448
+ width: "2px",
38449
+ background: "#2563eb",
38450
+ zIndex: "2147483647"
38451
+ });
38452
+ document.body.appendChild(caret);
37688
38453
  }
37689
- frameId = window.requestAnimationFrame(updatePreview);
38454
+ Object.assign(
38455
+ caret.style,
38456
+ rect ? {
38457
+ display: "block",
38458
+ left: `${rect.left}px`,
38459
+ top: `${rect.top}px`,
38460
+ height: `${rect.height}px`
38461
+ } : { display: "none" }
38462
+ );
38463
+ return target;
37690
38464
  };
37691
- const onPointerUp = () => {
37692
- window.removeEventListener("pointermove", onPointerMove);
37693
- window.removeEventListener("pointerup", onPointerUp);
37694
- window.removeEventListener("pointercancel", onPointerUp);
37695
- if (frameId !== void 0) {
37696
- window.cancelAnimationFrame(frameId);
37697
- }
37698
- setFloatingMovePreview(void 0);
37699
- if (isInlineImage) {
37700
- if (inlineDragActivated) {
37701
- setIsDraggingImage(false);
37702
- draggedImageRef.current = null;
37703
- setActiveDropTarget(void 0);
37704
- }
37705
- if (Math.abs(latestDeltaX) < 1 && Math.abs(latestDeltaY) < 1) {
37706
- return;
37707
- }
37708
- if (!inlineDragActivated) {
38465
+ const clear = (preserveMeasurements = false) => {
38466
+ caret?.remove();
38467
+ clearObjectDragPreview(preserveMeasurements);
38468
+ };
38469
+ objectDragCleanupRef.current = startObjectDrag({
38470
+ event,
38471
+ element,
38472
+ zoom,
38473
+ resolveElement: () => viewerRootRef.current?.querySelector(
38474
+ `[data-docx-image-location="${imageKey}"]`
38475
+ ) ?? void 0,
38476
+ onFrame: (frame) => {
38477
+ if (isInline) {
38478
+ resolveInlineTarget(frame);
37709
38479
  return;
37710
38480
  }
37711
- if (!latestInlineDropTarget) {
38481
+ updateObjectWrapDragPreview(
38482
+ frame,
38483
+ { imageKey },
38484
+ floating?.wrapType && floating.wrapType !== "none" && !floating.behindDocument ? {
38485
+ left: floating.distLPx ?? 0,
38486
+ right: floating.distRPx ?? 0,
38487
+ top: floating.distTPx ?? 0,
38488
+ bottom: floating.distBPx ?? 0,
38489
+ fullWidth: floating.wrapType === "topAndBottom"
38490
+ } : void 0
38491
+ );
38492
+ },
38493
+ onCancel: clear,
38494
+ onDrop: (frame) => {
38495
+ if (isInline) {
38496
+ const target = resolveInlineTarget(frame);
38497
+ clear();
38498
+ if (!target) return;
38499
+ editor.moveImage(location, target);
38500
+ const sameParagraph = sameParagraphLocation(location, target);
38501
+ setSelectedImage({
38502
+ ...target,
38503
+ childIndex: target.childIndex - (sameParagraph && location.childIndex < target.childIndex ? 1 : 0) + (target.textOffset ? 1 : 0)
38504
+ });
37712
38505
  return;
37713
38506
  }
37714
- editor.moveImage(location, latestInlineDropTarget);
37715
- const sourceParagraphLocation = imageLocationToParagraphLocation(location);
37716
- const targetParagraphLocation = latestInlineDropTarget.kind === "paragraph" ? {
37717
- kind: "paragraph",
37718
- nodeIndex: latestInlineDropTarget.nodeIndex
37719
- } : {
37720
- kind: "table-cell",
37721
- tableIndex: latestInlineDropTarget.tableIndex,
37722
- rowIndex: latestInlineDropTarget.rowIndex,
37723
- cellIndex: latestInlineDropTarget.cellIndex,
37724
- paragraphIndex: latestInlineDropTarget.paragraphIndex
37725
- };
37726
- const nextChildIndex = sameParagraphLocation(
37727
- sourceParagraphLocation,
37728
- targetParagraphLocation
37729
- ) && location.childIndex < latestInlineDropTarget.childIndex ? latestInlineDropTarget.childIndex - 1 : latestInlineDropTarget.childIndex;
37730
- setSelectedImage({
37731
- ...latestInlineDropTarget,
37732
- childIndex: Math.max(0, nextChildIndex)
37733
- });
37734
- return;
37735
- }
37736
- if (Math.abs(latestDeltaX) < 1 && Math.abs(latestDeltaY) < 1) {
37737
- return;
37738
- }
37739
- if (isWrappedFloatingImage) {
37740
- const hostWidth = paragraphRect ? paragraphRect.width / zoomScale : DEFAULT_DOC_PAGE_WIDTH;
37741
- const imageWidth = image.widthPx ?? Math.max(24, Math.round(wrapperRect.width / zoomScale));
37742
- const imageHeight = image.heightPx ?? Math.max(24, Math.round(wrapperRect.height / zoomScale));
37743
- const baseGeometry = resolveDualWrappedFloatingImageGeometry(
37744
- image,
37745
- hostWidth,
37746
- {
37747
- widthPx: imageWidth,
37748
- heightPx: imageHeight,
37749
- ...paragraphRect ? {
37750
- baseLeftPx: Math.round(
37751
- (wrapperRect.left - paragraphRect.left) / zoomScale
38507
+ const anchor = location.kind === "paragraph" ? resolveWrappedDropAnchorParagraphHost(
38508
+ element,
38509
+ frame.top,
38510
+ frame.left
38511
+ ) : void 0;
38512
+ const host = anchor?.host ?? element.closest("[data-docx-paragraph-host='true']");
38513
+ const hostRect = host?.getBoundingClientRect();
38514
+ clear(true);
38515
+ if (!hostRect) return;
38516
+ let selectedChildIndex = location.childIndex;
38517
+ if (anchor && (location.kind !== "paragraph" || anchor.nodeIndex !== location.nodeIndex)) {
38518
+ const targetParagraph = editor.model.nodes[anchor.nodeIndex];
38519
+ if (targetParagraph.type === "paragraph")
38520
+ selectedChildIndex = imageDropPositionAtTextOffset(
38521
+ targetParagraph,
38522
+ 0
38523
+ ).childIndex;
38524
+ }
38525
+ if (isWrappedFloatingImage) {
38526
+ const startLine = Number(host?.dataset.docxParagraphStartLine ?? 0);
38527
+ const anchorParagraph = anchor ? editor.model.nodes[anchor.nodeIndex] : void 0;
38528
+ const anchorSource = anchorParagraph?.type === "paragraph" ? buildParagraphPretextLayoutSource(anchorParagraph) : void 0;
38529
+ const anchorLayout = anchorParagraph?.type === "paragraph" && anchorSource ? layoutParagraphPretextSource(
38530
+ anchorParagraph,
38531
+ anchorSource,
38532
+ hostRect.width / zoom,
38533
+ Number(host?.dataset.docxParagraphLineHeight) || estimateParagraphLineHeightPx(anchorParagraph),
38534
+ []
38535
+ ) : void 0;
38536
+ const anchorTextOffset = startLine > 0 ? anchorLayout?.lines[startLine]?.fragments[0]?.startOffset : void 0;
38537
+ if (anchorParagraph?.type === "paragraph" && anchor && (location.kind !== "paragraph" || anchor.nodeIndex !== location.nodeIndex || anchorTextOffset !== void 0)) {
38538
+ const position = imageDropPositionAtTextOffset(
38539
+ anchorParagraph,
38540
+ anchorTextOffset ?? 0,
38541
+ { fieldText: formFieldDisplayValue2 }
38542
+ );
38543
+ selectedChildIndex = position.childIndex + (position.textOffset ? 1 : 0) - (location.kind === "paragraph" && anchor.nodeIndex === location.nodeIndex && location.childIndex < position.childIndex ? 1 : 0);
38544
+ }
38545
+ editor.moveFloatingImage(
38546
+ location,
38547
+ {
38548
+ ...resolveWrappedFloatingImageDropPatch(
38549
+ image,
38550
+ hostRect.width / zoom,
38551
+ (frame.left - hostRect.left) / zoom,
38552
+ (frame.top - hostRect.top) / zoom
37752
38553
  ),
37753
- baseTopPx: Math.round(
37754
- (wrapperRect.top - paragraphRect.top) / zoomScale
37755
- )
38554
+ horizontalAlign: void 0,
38555
+ verticalAlign: void 0,
38556
+ horizontalRelativeTo: "column",
38557
+ verticalRelativeTo: anchorTextOffset !== void 0 ? "line" : "paragraph"
38558
+ },
38559
+ anchor ? {
38560
+ reparentToParagraphNodeIndex: anchor.nodeIndex,
38561
+ anchorTextOffset
37756
38562
  } : void 0
37757
- }
37758
- );
37759
- const movedLeft = clampNumber(
37760
- (baseGeometry?.imageLeftPx ?? 0) + latestDeltaX,
37761
- 0,
37762
- Math.max(0, hostWidth - imageWidth)
37763
- );
37764
- const movedTop = Math.round(
37765
- (baseGeometry?.imageTopPx ?? 0) + latestDeltaY
37766
- );
37767
- const droppedTopScreenPx = wrapperRect.top + latestDeltaY * zoomScale;
37768
- const droppedLeftScreenPx = wrapperRect.left + latestDeltaX * zoomScale;
37769
- const dropAnchor = location.kind === "paragraph" && wrappedDragCanReanchorToParagraph(baseFloating) ? resolveWrappedDropAnchorParagraphHost(
37770
- wrapperElement,
37771
- droppedTopScreenPx,
37772
- droppedLeftScreenPx
37773
- ) : void 0;
37774
- if (dropAnchor && location.kind === "paragraph" && dropAnchor.nodeIndex !== location.nodeIndex && dropAnchor.nodeIndex !== dragStartAnchor?.nodeIndex) {
37775
- const targetHostWidth = dropAnchor.rect.width / zoomScale;
37776
- const targetMovedLeft = clampNumber(
37777
- Math.round(
37778
- (droppedLeftScreenPx - dropAnchor.rect.left) / zoomScale
37779
- ),
37780
- 0,
37781
- Math.max(0, targetHostWidth - imageWidth)
37782
- );
37783
- const targetMovedTop = Math.round(
37784
- (droppedTopScreenPx - dropAnchor.rect.top) / zoomScale
37785
38563
  );
38564
+ } else {
38565
+ const surfaceRect = host?.closest("[data-docx-page-surface='true']")?.getBoundingClientRect();
37786
38566
  editor.moveFloatingImage(
37787
38567
  location,
37788
- resolveWrappedFloatingImageDropPatch(
37789
- image,
37790
- targetHostWidth,
37791
- targetMovedLeft,
37792
- targetMovedTop,
38568
+ resolveAbsoluteFloatingImageDropPatch(
38569
+ floating ?? {},
38570
+ documentLayout,
37793
38571
  {
37794
- widthPx: imageWidth,
37795
- heightPx: imageHeight
38572
+ wrapperRect: {
38573
+ left: frame.left / zoom,
38574
+ top: frame.top / zoom,
38575
+ width: frame.width / zoom,
38576
+ height: frame.height / zoom
38577
+ },
38578
+ pageSurfaceRect: surfaceRect ? normalizeFloatingDropRectForZoom(surfaceRect, zoom) : void 0,
38579
+ deltaX: 0,
38580
+ deltaY: 0
37796
38581
  }
37797
38582
  ),
37798
- { reparentToParagraphNodeIndex: dropAnchor.nodeIndex }
38583
+ anchor ? { reparentToParagraphNodeIndex: anchor.nodeIndex } : void 0
37799
38584
  );
38585
+ }
38586
+ if (anchor) {
37800
38587
  setSelectedImage({
37801
38588
  kind: "paragraph",
37802
- nodeIndex: dropAnchor.nodeIndex,
37803
- childIndex: 0
38589
+ nodeIndex: anchor.nodeIndex,
38590
+ childIndex: selectedChildIndex
37804
38591
  });
37805
- return;
37806
38592
  }
37807
- editor.moveFloatingImage(
37808
- location,
37809
- resolveWrappedFloatingImageDropPatch(
37810
- image,
37811
- hostWidth,
37812
- movedLeft,
37813
- movedTop,
37814
- {
37815
- widthPx: imageWidth,
37816
- heightPx: imageHeight
37817
- }
37818
- )
37819
- );
37820
- return;
37821
- }
37822
- if (isAbsoluteFloatingImage) {
37823
- const dropPageSurfaceRect = pageSurface && pageSurface.isConnected ? pageSurface.getBoundingClientRect() : pageSurfaceRect;
37824
- editor.moveFloatingImage(
37825
- location,
37826
- resolveAbsoluteFloatingImageDropPatch(
37827
- baseFloating,
37828
- documentLayout,
37829
- {
37830
- wrapperRect: normalizeFloatingDropRectForZoom(
37831
- wrapperRect,
37832
- zoomScale
37833
- ),
37834
- pageSurfaceRect: dropPageSurfaceRect ? normalizeFloatingDropRectForZoom(
37835
- dropPageSurfaceRect,
37836
- zoomScale
37837
- ) : void 0,
37838
- deltaX: latestDeltaX,
37839
- deltaY: latestDeltaY
37840
- }
37841
- )
37842
- );
37843
- return;
37844
38593
  }
37845
- };
37846
- window.addEventListener("pointermove", onPointerMove);
37847
- window.addEventListener("pointerup", onPointerUp, { once: true });
37848
- window.addEventListener("pointercancel", onPointerUp, { once: true });
38594
+ });
37849
38595
  },
37850
- [documentLayout, editor, isReadOnly, resolveViewerMeasurementZoomScale]
38596
+ [
38597
+ documentLayout,
38598
+ editor,
38599
+ isReadOnly,
38600
+ resolveViewerMeasurementZoomScale,
38601
+ resolveBoundaryFromPoint,
38602
+ updateObjectWrapDragPreview,
38603
+ clearObjectDragPreview
38604
+ ]
37851
38605
  );
37852
38606
  const beginDropCapMove = React.useCallback(
37853
38607
  (event, nodeIndex, dropCap) => {
@@ -38667,7 +39421,20 @@ function DocxEditorViewer({
38667
39421
  src: renderableImageSrc,
38668
39422
  alt: run.image.alt ?? "DOCX image",
38669
39423
  draggable: false,
39424
+ "data-docx-image-location": imageLocationKey({ ...location, childIndex: Number(/^run-(\d+)/.exec(run.key)?.[1]) }),
39425
+ onPointerDown: (event) => {
39426
+ if (!isReadOnly && run.image) beginFloatingImageMove(
39427
+ event,
39428
+ { ...location, childIndex: Number(/^run-(\d+)/.exec(run.key)?.[1]) },
39429
+ run.image,
39430
+ false,
39431
+ false
39432
+ );
39433
+ },
38670
39434
  style: {
39435
+ position: "relative",
39436
+ zIndex: 3,
39437
+ touchAction: "none",
38671
39438
  width: run.image.widthPx ? `${run.image.widthPx}px` : void 0,
38672
39439
  height: run.image.heightPx ? `${run.image.heightPx}px` : void 0,
38673
39440
  verticalAlign: "middle",
@@ -39284,6 +40051,22 @@ function DocxEditorViewer({
39284
40051
  };
39285
40052
  const renderInteractiveParagraphRuns = React.useCallback(
39286
40053
  (paragraph, keyPrefix, location, options) => {
40054
+ const lineRange = options?.paragraphLineRange;
40055
+ const sliceTop = (lineRange?.startLineIndex ?? 0) * (lineRange?.lineHeightPx ?? 0);
40056
+ const sliceEnd = (lineRange?.endLineIndex ?? Infinity) * (lineRange?.lineHeightPx ?? 1);
40057
+ const imageIsInSlice = (childIndex) => {
40058
+ if (!lineRange) return true;
40059
+ const source = buildParagraphPretextLayoutSource(paragraph);
40060
+ const unwrapped = source ? layoutParagraphPretextSource(
40061
+ paragraph,
40062
+ source,
40063
+ paragraphAvailableTextWidthPx(paragraph, pageContentWidthPxByNodeIndex.get(nodeIndexFromParagraphLocation(location)) ?? documentContentWidthPx, editor.model.metadata.numberingDefinitions),
40064
+ lineRange.lineHeightPx,
40065
+ []
40066
+ ) : void 0;
40067
+ const anchorTop = unwrapped ? resolveCaretRectAtOffset(unwrapped, paragraphChildAnchorOffset(paragraph, childIndex) + 1)?.top ?? 0 : 0;
40068
+ return anchorTop >= sliceTop && anchorTop < sliceEnd;
40069
+ };
39287
40070
  const nodes = [];
39288
40071
  const checkboxChoiceRow = paragraphLooksLikeCheckboxChoiceRow(paragraph);
39289
40072
  const fallbackTabWidthPx = checkboxChoiceRow ? checkboxChoiceRowTabWidthPx(paragraph) : DEFAULT_TAB_STOP_PX;
@@ -39360,16 +40143,6 @@ function DocxEditorViewer({
39360
40143
  }
39361
40144
  });
39362
40145
  };
39363
- const noteMarkerIndexes = {
39364
- footnote: /* @__PURE__ */ new Map(),
39365
- endnote: /* @__PURE__ */ new Map()
39366
- };
39367
- editor.model.metadata.footnotes?.forEach((note, index) => {
39368
- noteMarkerIndexes.footnote.set(note.id, index + 1);
39369
- });
39370
- editor.model.metadata.endnotes?.forEach((note, index) => {
39371
- noteMarkerIndexes.endnote.set(note.id, index + 1);
39372
- });
39373
40146
  const numberingLabel = paragraphNumberingLabels.get(
39374
40147
  paragraphLocationKey(location)
39375
40148
  );
@@ -39481,17 +40254,18 @@ function DocxEditorViewer({
39481
40254
  }
39482
40255
  );
39483
40256
  }
39484
- const paragraphLineHeightPx = estimateParagraphLineHeightPx(paragraph);
40257
+ const paragraphLineHeightPx = options?.paragraphLineRange?.lineHeightPx ?? estimateParagraphLineHeightPx(paragraph);
39485
40258
  const manualDualWrappedLayout = !numberingLabel ? resolveParagraphDualWrappedTextLayout(
39486
40259
  paragraph,
39487
40260
  paragraphRenderTextWidthPx,
39488
40261
  paragraphLineHeightPx,
39489
40262
  {
40263
+ excludedImageIndex: objectWrapDragPreview?.imageKey?.startsWith(`${paragraphLocationKey(location)}:`) ? Number(objectWrapDragPreview.imageKey.split(":").at(-1)) : void 0,
39490
40264
  widthPxByImageIndex: resizedWidthPxByImageIndex,
39491
40265
  heightPxByImageIndex: resizedHeightPxByImageIndex,
39492
40266
  paragraphTopPx: paragraphPageFlowTopPx,
39493
40267
  pageMarginTopPx: paragraphPageLayout.marginsPx.top,
39494
- foreignExclusions: options?.pageFlowForeignExclusions,
40268
+ foreignExclusions: options?.pageFlowForeignExclusions?.map((exclusion) => ({ ...exclusion, top: exclusion.top + sliceTop, bottom: exclusion.bottom + sliceTop })),
39495
40269
  movePreviewByImageIndex,
39496
40270
  allowNegativeImageTop: movePreviewByImageIndex.size > 0
39497
40271
  }
@@ -39500,7 +40274,7 @@ function DocxEditorViewer({
39500
40274
  paragraph,
39501
40275
  paragraphRenderTextWidthPx,
39502
40276
  paragraphLineHeightPx,
39503
- options?.pageFlowForeignExclusions ?? []
40277
+ (options?.pageFlowForeignExclusions ?? []).map((exclusion) => ({ ...exclusion, top: exclusion.top + sliceTop, bottom: exclusion.bottom + sliceTop }))
39504
40278
  ) : void 0;
39505
40279
  const activeDualWrappedLayout = manualDualWrappedLayout ?? foreignOnlyWrappedLayout;
39506
40280
  if (activeDualWrappedLayout) {
@@ -39539,7 +40313,20 @@ function DocxEditorViewer({
39539
40313
  src: renderableImageSrc,
39540
40314
  alt: run.image.alt ?? "DOCX image",
39541
40315
  draggable: false,
40316
+ "data-docx-image-location": imageLocationKey({ ...location, childIndex: Number(/^run-(\d+)/.exec(run.key)?.[1]) }),
40317
+ onPointerDown: (event) => {
40318
+ if (!isReadOnly && run.image) beginFloatingImageMove(
40319
+ event,
40320
+ { ...location, childIndex: Number(/^run-(\d+)/.exec(run.key)?.[1]) },
40321
+ run.image,
40322
+ false,
40323
+ false
40324
+ );
40325
+ },
39542
40326
  style: {
40327
+ position: "relative",
40328
+ zIndex: 3,
40329
+ touchAction: "none",
39543
40330
  width: run.image.widthPx ? `${run.image.widthPx}px` : void 0,
39544
40331
  height: run.image.heightPx ? `${run.image.heightPx}px` : void 0,
39545
40332
  verticalAlign: "middle",
@@ -39629,7 +40416,7 @@ function DocxEditorViewer({
39629
40416
  };
39630
40417
  const renderManualImage = (geometry) => {
39631
40418
  const manualImage = previewParagraph.children[geometry.imageIndex];
39632
- if (manualImage?.type !== "image") {
40419
+ if (manualImage?.type !== "image" || !imageIsInSlice(geometry.imageIndex)) {
39633
40420
  return null;
39634
40421
  }
39635
40422
  const manualImageLocation = {
@@ -39658,7 +40445,7 @@ function DocxEditorViewer({
39658
40445
  display: "inline-block",
39659
40446
  position: "absolute",
39660
40447
  left: geometry.imageLeftPx,
39661
- top: geometry.imageTopPx,
40448
+ top: geometry.imageTopPx - sliceTop,
39662
40449
  width: widthPx ? `${widthPx}px` : void 0,
39663
40450
  height: heightPx ? `${heightPx}px` : void 0,
39664
40451
  zIndex: 3
@@ -40319,17 +41106,15 @@ function DocxEditorViewer({
40319
41106
  return child?.type === "image" && isPageOrMarginAnchoredWrappedFloatingImage(child);
40320
41107
  }).map((geometry) => geometry.imageIndex)
40321
41108
  );
40322
- const activeWrappedSource = activeSession ? buildSyntheticPretextLayoutSource(
41109
+ const activeWrappedSource = activeSession && activeSession.text !== activeDualWrappedLayout.source.text ? buildSyntheticPretextLayoutSource(
40323
41110
  activeSession.text,
40324
41111
  firstRunStyle(previewParagraph)
40325
41112
  ) : activeDualWrappedLayout.source;
40326
41113
  const activeWrappedExclusions = [
40327
41114
  ...options?.pageFlowForeignExclusions ?? [],
40328
- ...activeDualWrappedLayout.geometries.map(
40329
- (geometry) => geometry.exclusion
40330
- )
41115
+ ...activeDualWrappedLayout.geometries.map((geometry) => geometry.exclusion)
40331
41116
  ];
40332
- const activeWrappedLayout = activeSession ? layoutParagraphPretextSource(
41117
+ const activeWrappedLayout = activeWrappedSource !== activeDualWrappedLayout.source ? layoutParagraphPretextSource(
40333
41118
  previewParagraph,
40334
41119
  activeWrappedSource,
40335
41120
  paragraphRenderTextWidthPx,
@@ -40340,9 +41125,13 @@ function DocxEditorViewer({
40340
41125
  location,
40341
41126
  keyPrefix: `${keyPrefix}-dual-wrap-manual`,
40342
41127
  source: activeWrappedSource,
40343
- layout: activeWrappedLayout,
41128
+ layout: lineRange ? sliceLayoutToLineRange(
41129
+ activeWrappedLayout,
41130
+ lineRange.startLineIndex,
41131
+ lineRange.endLineIndex >= lineRange.totalLineCount ? activeWrappedLayout.lineCount : lineRange.endLineIndex
41132
+ ) : activeWrappedLayout,
40344
41133
  lineHeightPx: activeDualWrappedLayout.lineHeightPx,
40345
- blockHeightPx: resolveDragPreviewAwareWrapBlockHeightPx(
41134
+ blockHeightPx: lineRange ? void 0 : resolveDragPreviewAwareWrapBlockHeightPx(
40346
41135
  activeWrappedLayout,
40347
41136
  activeDualWrappedLayout.geometries,
40348
41137
  options?.pageFlowForeignExclusions
@@ -40629,6 +41418,12 @@ function DocxEditorViewer({
40629
41418
  contentEditable: !isReadOnly,
40630
41419
  suppressContentEditableWarning: true,
40631
41420
  spellCheck: false,
41421
+ onInput: (event) => event.stopPropagation(),
41422
+ onKeyDown: (event) => event.stopPropagation(),
41423
+ onKeyUp: (event) => event.stopPropagation(),
41424
+ onPaste: (event) => event.stopPropagation(),
41425
+ onCompositionStart: (event) => event.stopPropagation(),
41426
+ onCompositionEnd: (event) => event.stopPropagation(),
40632
41427
  onFocus: (event) => {
40633
41428
  event.stopPropagation();
40634
41429
  if (!isReadOnly) {
@@ -41008,6 +41803,7 @@ function DocxEditorViewer({
41008
41803
  {
41009
41804
  contentEditable: false,
41010
41805
  "data-docx-form-field": "true",
41806
+ "data-docx-form-field-child-index": childIndex,
41011
41807
  "data-docx-form-field-type": "checkbox",
41012
41808
  role: "checkbox",
41013
41809
  "aria-checked": Boolean(child.checked),
@@ -41086,6 +41882,7 @@ function DocxEditorViewer({
41086
41882
  {
41087
41883
  contentEditable: false,
41088
41884
  "data-docx-form-field": "true",
41885
+ "data-docx-form-field-child-index": childIndex,
41089
41886
  "data-docx-form-field-type": "dropdown",
41090
41887
  value: selectedValue,
41091
41888
  onClick: (event) => {
@@ -41142,6 +41939,7 @@ function DocxEditorViewer({
41142
41939
  {
41143
41940
  contentEditable: false,
41144
41941
  "data-docx-form-field": "true",
41942
+ "data-docx-form-field-child-index": childIndex,
41145
41943
  "data-docx-form-field-type": "date",
41146
41944
  type: compactFieldLayout ? "text" : "date",
41147
41945
  value: normalizedDate,
@@ -41186,6 +41984,7 @@ function DocxEditorViewer({
41186
41984
  {
41187
41985
  contentEditable: false,
41188
41986
  "data-docx-form-field": "true",
41987
+ "data-docx-form-field-child-index": childIndex,
41189
41988
  "data-docx-form-field-type": "date",
41190
41989
  type: "text",
41191
41990
  value: child.value ?? "",
@@ -41232,6 +42031,7 @@ function DocxEditorViewer({
41232
42031
  {
41233
42032
  contentEditable: false,
41234
42033
  "data-docx-form-field": "true",
42034
+ "data-docx-form-field-child-index": childIndex,
41235
42035
  "data-docx-form-field-type": "text",
41236
42036
  type: "text",
41237
42037
  value: child.value ?? "",
@@ -41426,6 +42226,7 @@ function DocxEditorViewer({
41426
42226
  beginImageResize,
41427
42227
  clearTableCellSelection,
41428
42228
  floatingMovePreview,
42229
+ objectWrapDragPreview,
41429
42230
  isDraggingImage,
41430
42231
  openContextMenu,
41431
42232
  onImageDragEnd,
@@ -41444,6 +42245,7 @@ function DocxEditorViewer({
41444
42245
  onFormFieldDoubleClick,
41445
42246
  isReadOnly,
41446
42247
  paragraphNumberingLabels,
42248
+ noteMarkerIndexes,
41447
42249
  scrollToBookmark,
41448
42250
  documentLayout,
41449
42251
  tocLinkColorByLevel,
@@ -42248,7 +43050,10 @@ function DocxEditorViewer({
42248
43050
  const resolvedAfterSpacingPx = Number.isFinite(
42249
43051
  options?.overrideAfterSpacingPx
42250
43052
  ) ? Math.max(0, Math.round(options?.overrideAfterSpacingPx)) : void 0;
42251
- const editable = !hasImage && !isReadOnly && options?.forceReadOnly !== true && (!hasPartialLineRange || paragraphSegmentIsActiveEditable) && !isManualPageBreakParagraph;
43053
+ const hasOnlyAbsoluteImages = hasImage && node.children.every(
43054
+ (child) => child.type !== "image" || shouldRenderAbsoluteFloatingImage(child)
43055
+ );
43056
+ const editable = (!hasImage || hasOnlyAbsoluteImages) && !isReadOnly && options?.forceReadOnly !== true && (!hasPartialLineRange || paragraphSegmentIsActiveEditable) && !isManualPageBreakParagraph;
42252
43057
  const resolvedPageLayout = options?.pageLayout ?? documentLayout;
42253
43058
  const paragraphContainsLikelyFullPageCoverImage = node.children.some(
42254
43059
  (child) => child.type === "image" && shouldRenderAbsoluteFloatingImage(child) && likelyFullPageCoverImageRelativeToContentBox(
@@ -42371,6 +43176,7 @@ function DocxEditorViewer({
42371
43176
  nodeIndex
42372
43177
  },
42373
43178
  {
43179
+ paragraphLineRange: hasPartialLineRange ? paragraphLineRange : void 0,
42374
43180
  pageFlowTopPx: paragraphPageFlowTopPx,
42375
43181
  pageFlowForeignExclusions: options?.pageFlowForeignExclusions,
42376
43182
  pageLayout: resolvedPageLayout,
@@ -42380,7 +43186,26 @@ function DocxEditorViewer({
42380
43186
  }
42381
43187
  return fallbackParagraphRuns;
42382
43188
  };
42383
- const renderedInteractiveParagraphContent = shouldRenderParagraphSegmentWithPretext ? /* @__PURE__ */ jsx(
43189
+ const canReusePlainParagraphHtml = editable && !hasPartialLineRange && !hasImage && !hasDualWrappedFloatingImage && !isDraggingImage && !paragraphHasFormField2(node) && !node.children.some(
43190
+ (child) => child.type === "text" && child.noteReference
43191
+ ) && (options?.pageFlowForeignExclusions?.length ?? 0) === 0;
43192
+ const plainParagraphHtmlContext = canReusePlainParagraphHtml ? [
43193
+ nodeIndex,
43194
+ fontMetricsRevision,
43195
+ documentContentTheme,
43196
+ editor.model.metadata.numberingDefinitions,
43197
+ numberingLabel?.text,
43198
+ numberingLabel?.imageSrc,
43199
+ paragraphRenderTextWidthPx,
43200
+ commentsEnabled,
43201
+ editor.model.metadata.comments,
43202
+ JSON.stringify(tocLinkColorByLevel)
43203
+ ] : void 0;
43204
+ const cachedPlainParagraph = canReusePlainParagraphHtml ? plainParagraphHtmlCacheRef.current.get(node) : void 0;
43205
+ const reusablePlainParagraph = cachedPlainParagraph && plainParagraphHtmlContext?.every(
43206
+ (value, index) => Object.is(value, cachedPlainParagraph.context[index])
43207
+ ) ? cachedPlainParagraph : void 0;
43208
+ const renderedInteractiveParagraphContent = reusablePlainParagraph ? null : shouldRenderParagraphSegmentWithPretext && hasDualWrappedFloatingImage ? getFallbackParagraphRuns() : shouldRenderParagraphSegmentWithPretext ? /* @__PURE__ */ jsx(
42384
43209
  "div",
42385
43210
  {
42386
43211
  style: {
@@ -42425,20 +43250,25 @@ function DocxEditorViewer({
42425
43250
  )
42426
43251
  }
42427
43252
  ) : getFallbackParagraphRuns();
42428
- const renderedEditableParagraphHtml = renderStaticHtml(
42429
- renderedInteractiveParagraphContent
42430
- );
43253
+ const renderedEditableParagraphHtml = reusablePlainParagraph?.html ?? (editable && !hasImage ? renderStaticHtml(renderedInteractiveParagraphContent) : "");
43254
+ if (plainParagraphHtmlContext && !reusablePlainParagraph) {
43255
+ plainParagraphHtmlCacheRef.current.set(node, {
43256
+ context: plainParagraphHtmlContext,
43257
+ html: renderedEditableParagraphHtml
43258
+ });
43259
+ }
42431
43260
  return /* @__PURE__ */ jsx(
42432
43261
  "div",
42433
43262
  {
42434
43263
  "data-docx-paragraph-host": "true",
42435
43264
  "data-docx-paragraph-kind": "paragraph",
42436
43265
  "data-docx-paragraph-node-index": nodeIndex,
43266
+ "data-docx-paragraph-line-height": paragraphSegmentLineHeightPx,
42437
43267
  "data-docx-paragraph-start-line": paragraphSegmentStartLine,
42438
43268
  "data-docx-paragraph-end-line": paragraphSegmentEndLine,
42439
43269
  "data-docx-paragraph-partial-line-range": hasPartialLineRange ? "true" : void 0,
42440
43270
  style: paragraphStyle,
42441
- dangerouslySetInnerHTML: editable ? stableInnerHtmlProp(
43271
+ dangerouslySetInnerHTML: editable && !hasImage ? stableInnerHtmlProp(
42442
43272
  editableParagraphHtmlPropsRef.current,
42443
43273
  nodeIndex,
42444
43274
  renderedEditableParagraphHtml
@@ -43012,7 +43842,7 @@ ${currentText.slice(end)}`;
43012
43842
  }
43013
43843
  }
43014
43844
  },
43015
- children: !editable ? renderedInteractiveParagraphContent : null
43845
+ children: !editable || hasImage ? renderedInteractiveParagraphContent : null
43016
43846
  },
43017
43847
  paragraphLineRange ? `epoch-${paragraphStructureEpoch}-node-${nodeIndex}-lines-${paragraphSegmentStartLine}-${paragraphSegmentEndLine}${options?.syntheticKeySuffix ? `-${options.syntheticKeySuffix}` : ""}` : `epoch-${paragraphStructureEpoch}-node-${nodeIndex}${options?.syntheticKeySuffix ? `-${options.syntheticKeySuffix}` : ""}`
43018
43848
  );
@@ -43021,7 +43851,7 @@ ${currentText.slice(end)}`;
43021
43851
  const tableRowSliceTopBleedPxForWrapper = tableRowSlice && tableRowSliceOffsetPxForWrapper > 0 ? TABLE_ROW_SLICE_VISUAL_BLEED_PX : 0;
43022
43852
  const tableRowSliceBottomBleedPxForWrapper = tableRowSlice ? TABLE_ROW_SLICE_VISUAL_BLEED_PX : 0;
43023
43853
  const floatingTablePageLayout = options?.pageLayout ?? documentLayout;
43024
- const floatingTableGeometry = node.style?.floating && !tableRowRange && !tableRowSlice ? resolveFloatingTableGeometry(node, nodeContentWidthPx, {
43854
+ const floatingTableGeometry = (node.style?.floating || objectWrapDragPreview?.tableIndex === nodeIndex) && !tableRowRange && !tableRowSlice ? resolveFloatingTableGeometry(node.style?.floating ? node : { ...node, style: { ...node.style, floating: {} } }, nodeContentWidthPx, {
43025
43855
  tableWidthPx: estimateFloatingTableWidthPx(
43026
43856
  node,
43027
43857
  nodeContentWidthPx
@@ -43042,6 +43872,16 @@ ${currentText.slice(end)}`;
43042
43872
  }
43043
43873
  if (element) {
43044
43874
  tableElementsRef.current.set(nodeIndex, element);
43875
+ if (floatingTableGeometry) element.style.top = `${floatingTableGeometry.topPx}px`;
43876
+ const anchor = node.style?.floating?.verticalAnchor;
43877
+ const yPx = twipsToSignedPixels(node.style?.floating?.yTwips);
43878
+ const surface = element.closest("[data-docx-page-surface='true']");
43879
+ if (floatingTableGeometry && surface && element.parentElement && yPx !== void 0 && (anchor === "page" || anchor === "margin")) {
43880
+ const scale = resolveViewerMeasurementZoomScale(element, 1);
43881
+ const origin = surface.getBoundingClientRect().top;
43882
+ const hostTop = element.parentElement.getBoundingClientRect().top;
43883
+ element.style.top = `${(origin - hostTop) / scale + yPx + (anchor === "margin" ? floatingTablePageLayout.marginsPx.top : 0)}px`;
43884
+ }
43045
43885
  } else {
43046
43886
  tableElementsRef.current.delete(nodeIndex);
43047
43887
  }
@@ -43057,11 +43897,6 @@ ${currentText.slice(end)}`;
43057
43897
  node,
43058
43898
  twipsToSignedPixels(node.style?.indentTwips) ?? 0
43059
43899
  ),
43060
- ...tableFloatingDragPreview?.tableIndex === nodeIndex ? {
43061
- transform: `translate(${tableFloatingDragPreview.deltaX}px, ${tableFloatingDragPreview.deltaY}px)`,
43062
- zIndex: 30,
43063
- opacity: 0.9
43064
- } : void 0,
43065
43900
  ...tableRowSlice ? {
43066
43901
  height: Math.max(
43067
43902
  MIN_PARAGRAPH_LINE_HEIGHT_PX,
@@ -43719,7 +44554,10 @@ ${currentText.slice(end)}`;
43719
44554
  if (!cellEditorElement || !cellEditorElement.isConnected) {
43720
44555
  return;
43721
44556
  }
43722
- cellEditorElement.focus();
44557
+ if (document.activeElement === cellEditorElement) {
44558
+ return;
44559
+ }
44560
+ cellEditorElement.focus({ preventScroll: true });
43723
44561
  const paragraphSelector2 = `[data-docx-paragraph-kind='table-cell'][data-docx-table-index='${nodeIndex}'][data-docx-row-index='${rowIndex}'][data-docx-cell-index='${cellIndex}'][data-docx-table-paragraph-index]`;
43724
44562
  const paragraphElements2 = Array.from(
43725
44563
  cellEditorElement.querySelectorAll(
@@ -44008,12 +44846,12 @@ ${currentText.slice(end)}`;
44008
44846
  return;
44009
44847
  }
44010
44848
  }
44011
- window.requestAnimationFrame(() => {
44849
+ const applyPendingFocus = () => {
44012
44850
  const latestPendingFocus = pendingTableCellFocusRef.current;
44013
44851
  if (!latestPendingFocus || latestPendingFocus.cellElementKey !== cellElementKey || !element.isConnected) {
44014
44852
  return;
44015
44853
  }
44016
- element.focus();
44854
+ element.focus({ preventScroll: true });
44017
44855
  const boundary = latestPendingFocus.boundary;
44018
44856
  const boundaryInCurrentCell = Boolean(
44019
44857
  boundary && boundary.location.kind === "table-cell" && boundary.location.tableIndex === nodeIndex && boundary.location.rowIndex === rowIndex && boundary.location.cellIndex === cellIndex
@@ -44075,7 +44913,8 @@ ${currentText.slice(end)}`;
44075
44913
  cellIndex
44076
44914
  );
44077
44915
  pendingTableCellFocusRef.current = void 0;
44078
- });
44916
+ };
44917
+ applyPendingFocus();
44079
44918
  }
44080
44919
  },
44081
44920
  onInput: (event) => {
@@ -45001,11 +45840,28 @@ ${currentText.slice(end)}`;
45001
45840
  }
45002
45841
  return renderedTableBlock;
45003
45842
  };
45843
+ const objectDragPageRenderCacheRef = React.useRef(
45844
+ /* @__PURE__ */ new Map()
45845
+ );
45846
+ if (!objectWrapDragPreview) objectDragPageRenderCacheRef.current.clear();
45004
45847
  const renderNodeSegments = React.useCallback(
45005
45848
  (nodeSegments, options) => {
45849
+ const dragSourceIndex = objectWrapDragPreview?.tableIndex ?? (objectWrapDragPreview?.imageKey?.startsWith("p:") ? Number(objectWrapDragPreview.imageKey.split(":")[1]) : -1);
45850
+ const dragPagePreview = objectWrapDragPreview?.bandsByPage.has(
45851
+ options?.pageIndex ?? -1
45852
+ ) ? objectWrapDragPreview : objectWrapDragPreview && nodeSegments.some((segment) => segment.nodeIndex === dragSourceIndex) ? "source" : void 0;
45853
+ const cacheKey = JSON.stringify([
45854
+ options?.pageIndex,
45855
+ nodeSegments,
45856
+ options?.pageLayout
45857
+ ]);
45858
+ const cached = objectDragPageRenderCacheRef.current.get(cacheKey);
45859
+ if (objectWrapDragPreview && cached?.model === editor.model && cached.fontRevision === fontMetricsRevision && cached.theme === documentContentTheme && cached.preview === dragPagePreview)
45860
+ return cached.rendered;
45006
45861
  const rendered = [];
45007
45862
  let pageFlowTopPx = 0;
45008
45863
  const resolvedPageLayout = options?.pageLayout ?? documentLayout;
45864
+ const liveWrapBands = objectWrapDragPreview?.bandsByPage.get(options?.pageIndex ?? -1) ?? measuredWrapBands;
45009
45865
  const foreignWrapExclusionsBySegmentIndex = precomputePageSegmentForeignWrapExclusions(
45010
45866
  nodeSegments,
45011
45867
  editor.model,
@@ -45017,7 +45873,10 @@ ${currentText.slice(end)}`;
45017
45873
  {
45018
45874
  floatingMovePreview,
45019
45875
  resizePreview,
45020
- measuredWrapBands
45876
+ measuredWrapBands: liveWrapBands,
45877
+ draggedImageKey: objectWrapDragPreview?.imageKey,
45878
+ draggedTableIndex: objectWrapDragPreview?.tableIndex,
45879
+ dragObstacle: objectWrapDragPreview?.pageIndex === options?.pageIndex ? objectWrapDragPreview?.obstacle : void 0
45021
45880
  }
45022
45881
  );
45023
45882
  for (let offset = 0; offset < nodeSegments.length; offset += 1) {
@@ -45671,7 +46530,7 @@ ${currentText.slice(end)}`;
45671
46530
  }
45672
46531
  }
45673
46532
  const segmentParagraphLineRange = segment.paragraphLineRange;
45674
- const measuredSegmentFlowTopPx = node.type === "paragraph" && (!segmentParagraphLineRange || segmentParagraphLineRange.startLineIndex === 0 && segmentParagraphLineRange.endLineIndex >= segmentParagraphLineRange.totalLineCount) ? measuredWrapBands.get(`para:${nodeIndex}`)?.topPx : void 0;
46533
+ const measuredSegmentFlowTopPx = node.type === "table" && node.style?.floating ? liveWrapBands.get(`tblanchor:${nodeIndex}`)?.topPx : node.type === "paragraph" && (!segmentParagraphLineRange || segmentParagraphLineRange.startLineIndex === 0 && segmentParagraphLineRange.endLineIndex >= segmentParagraphLineRange.totalLineCount) ? liveWrapBands.get(`para:${nodeIndex}`)?.topPx : void 0;
45675
46534
  rendered.push(
45676
46535
  renderDocumentNode(
45677
46536
  node,
@@ -45696,10 +46555,19 @@ ${currentText.slice(end)}`;
45696
46555
  docGridLinePitchPxByNodeIndex.get(nodeIndex)
45697
46556
  );
45698
46557
  }
46558
+ objectDragPageRenderCacheRef.current.set(cacheKey, {
46559
+ model: editor.model,
46560
+ fontRevision: fontMetricsRevision,
46561
+ theme: documentContentTheme,
46562
+ preview: dragPagePreview,
46563
+ rendered
46564
+ });
45699
46565
  return rendered;
45700
46566
  },
45701
46567
  [
45702
46568
  activeEditableParagraphSegment,
46569
+ fontMetricsRevision,
46570
+ documentContentTheme,
45703
46571
  beginDropCapMove,
45704
46572
  beginDropCapResize,
45705
46573
  clearObjectSelectionForParagraphEntry,
@@ -45710,6 +46578,7 @@ ${currentText.slice(end)}`;
45710
46578
  dropCapResizePreview,
45711
46579
  editor,
45712
46580
  floatingMovePreview,
46581
+ objectWrapDragPreview,
45713
46582
  focusDropCapWrappedParagraph,
45714
46583
  hoveredDropCapNodeIndex,
45715
46584
  isReadOnly,
@@ -45922,6 +46791,7 @@ ${currentText.slice(end)}`;
45922
46791
  onDragOver: onCanvasDragOver,
45923
46792
  onDragLeave: onCanvasDragLeave,
45924
46793
  onDrop: (event) => void onCanvasDrop(event),
46794
+ onDoubleClickCapture: onViewerFormFieldDoubleClick,
45925
46795
  onPointerDownCapture: onViewerPointerDown,
45926
46796
  onPointerMoveCapture: onViewerPointerMove,
45927
46797
  onPointerLeave: onViewerPointerLeave,
@@ -46434,6 +47304,7 @@ ${currentText.slice(end)}`;
46434
47304
  ref: (element) => {
46435
47305
  if (element) {
46436
47306
  pageBodyElementsRef.current.set(pageIndex, element);
47307
+ element.dataset.docxPageBody = "true";
46437
47308
  } else {
46438
47309
  pageBodyElementsRef.current.delete(pageIndex);
46439
47310
  }
@@ -46610,6 +47481,7 @@ ${currentText.slice(end)}`;
46610
47481
  React.Fragment,
46611
47482
  {
46612
47483
  children: renderNodeSegments(group.segments, {
47484
+ pageIndex,
46613
47485
  pageLayout,
46614
47486
  suppressLikelyFullPageCoverImageKeys: suppressedLikelyFullPageCoverImageKeys
46615
47487
  })
@@ -48139,7 +49011,7 @@ async function parseDocxForViewer(source, options = {}) {
48139
49011
  const readFinishedAt = now();
48140
49012
  const importResult = await importDocxBuffer(buffer, {
48141
49013
  signal: options.signal,
48142
- transferBuffer: options.transferBuffer,
49014
+ transferBuffer: options.transferBuffer ?? !(source instanceof ArrayBuffer),
48143
49015
  useWorker: options.useWorker
48144
49016
  });
48145
49017
  const importFinishedAt = now();
@@ -48156,6 +49028,9 @@ async function parseDocxForViewer(source, options = {}) {
48156
49028
  }
48157
49029
  });
48158
49030
  }
49031
+ if (faces.length > 0) {
49032
+ invalidateFontMetrics();
49033
+ }
48159
49034
  faces = [];
48160
49035
  };
48161
49036
  const loadEmbeddedFonts = () => {
@@ -48189,6 +49064,9 @@ async function parseDocxForViewer(source, options = {}) {
48189
49064
  )
48190
49065
  )).filter((face) => Boolean(face));
48191
49066
  loaded.forEach((face) => document.fonts.add(face));
49067
+ if (loaded.length > 0) {
49068
+ invalidateFontMetrics();
49069
+ }
48192
49070
  faces = loaded;
48193
49071
  try {
48194
49072
  await document.fonts.ready;
@@ -49774,7 +50652,8 @@ function useDocxModel(file) {
49774
50652
  try {
49775
50653
  const { model } = await importDocxBuffer(docxFile, {
49776
50654
  signal: abortController.signal,
49777
- transferBuffer: false
50655
+ transferBuffer: false,
50656
+ useWorker: "required"
49778
50657
  });
49779
50658
  if (!isCurrent) {
49780
50659
  return;