@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.cjs CHANGED
@@ -41,6 +41,23 @@ var init_cjs_shims = __esm({
41
41
  });
42
42
 
43
43
  // ../wasm/src/generated/docx_wasm.js
44
+ function build_doc_model_from_bytes(bytes) {
45
+ try {
46
+ const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
47
+ const ptr0 = passArray8ToWasm0(bytes, wasm.__wbindgen_export);
48
+ const len0 = WASM_VECTOR_LEN;
49
+ wasm.build_doc_model_from_bytes(retptr, ptr0, len0);
50
+ var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
51
+ var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
52
+ var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true);
53
+ if (r2) {
54
+ throw takeObject(r1);
55
+ }
56
+ return takeObject(r0);
57
+ } finally {
58
+ wasm.__wbindgen_add_to_stack_pointer(16);
59
+ }
60
+ }
44
61
  function build_doc_model_from_package(_package) {
45
62
  let deferred2_0;
46
63
  let deferred2_1;
@@ -254,6 +271,10 @@ function __wbg_get_imports() {
254
271
  const ret = new Uint8Array(getArrayU8FromWasm0(arg0, arg1));
255
272
  return addHeapObject(ret);
256
273
  },
274
+ __wbg_now_c1f9831c87b83a0b: function() {
275
+ const ret = performance.now();
276
+ return ret;
277
+ },
257
278
  __wbg_parse_246201845d0eb98c: function() {
258
279
  return handleError(function(arg0, arg1) {
259
280
  const ret = JSON.parse(getStringFromWasm0(arg0, arg1));
@@ -279,7 +300,11 @@ function __wbg_get_imports() {
279
300
  return addHeapObject(ret);
280
301
  }, arguments);
281
302
  },
282
- __wbindgen_cast_0000000000000001: function(arg0, arg1) {
303
+ __wbindgen_cast_0000000000000001: function(arg0) {
304
+ const ret = arg0;
305
+ return addHeapObject(ret);
306
+ },
307
+ __wbindgen_cast_0000000000000002: function(arg0, arg1) {
283
308
  const ret = getStringFromWasm0(arg0, arg1);
284
309
  return addHeapObject(ret);
285
310
  },
@@ -641,6 +666,11 @@ async function wasmBuildDocModelFromPackage(pkg) {
641
666
  const json = build_doc_model_from_package(pkg);
642
667
  return JSON.parse(json);
643
668
  }
669
+ async function wasmBuildDocModelFromBytes(bytes) {
670
+ await initWasm();
671
+ const payload = bytes instanceof Uint8Array ? bytes : new Uint8Array(bytes);
672
+ return build_doc_model_from_bytes(payload);
673
+ }
644
674
  async function wasmSerializeDocx(model, basePackage) {
645
675
  await initWasm();
646
676
  const modelJson = docModelToWasmJson(model);
@@ -665,81 +695,6 @@ var init_src = __esm({
665
695
  }
666
696
  });
667
697
 
668
- // ../ooxml-core/src/index.ts
669
- var src_exports = {};
670
- __export(src_exports, {
671
- createMinimalDocxPackage: () => createMinimalDocxPackage,
672
- getPart: () => getPart,
673
- packageToArrayBuffer: () => packageToArrayBuffer,
674
- parseDocx: () => parseDocx,
675
- withPart: () => withPart
676
- });
677
- async function parseDocx(input) {
678
- const wasmPackage = await wasmParseDocx(input);
679
- const { parts, binaryAssets, warnings } = wasmPackageToMaps(wasmPackage);
680
- return { parts, binaryAssets, ...warnings.length > 0 ? { warnings } : {} };
681
- }
682
- async function packageToArrayBuffer(pkg) {
683
- return wasmPackageToArrayBuffer(mapsToWasmPackage(pkg));
684
- }
685
- function createMinimalDocxPackage(documentXml = DEFAULT_DOCUMENT_XML) {
686
- return {
687
- parts: /* @__PURE__ */ new Map([
688
- ["[Content_Types].xml", { name: "[Content_Types].xml", content: DEFAULT_CONTENT_TYPES_XML }],
689
- ["_rels/.rels", { name: "_rels/.rels", content: DEFAULT_ROOT_RELS_XML }],
690
- ["word/document.xml", { name: "word/document.xml", content: documentXml }],
691
- [
692
- "word/_rels/document.xml.rels",
693
- { name: "word/_rels/document.xml.rels", content: DEFAULT_DOCUMENT_RELS_XML }
694
- ]
695
- ]),
696
- binaryAssets: /* @__PURE__ */ new Map()
697
- };
698
- }
699
- function getPart(pkg, partName) {
700
- return pkg.parts.get(partName);
701
- }
702
- function withPart(pkg, part) {
703
- const parts = new Map(pkg.parts);
704
- parts.set(part.name, part);
705
- return {
706
- parts,
707
- binaryAssets: new Map(pkg.binaryAssets),
708
- ...pkg.warnings && pkg.warnings.length > 0 ? { warnings: [...pkg.warnings] } : {}
709
- };
710
- }
711
- var WORD_MAIN_NS, DEFAULT_DOCUMENT_XML, DEFAULT_CONTENT_TYPES_XML, DEFAULT_ROOT_RELS_XML, DEFAULT_DOCUMENT_RELS_XML;
712
- var init_src2 = __esm({
713
- "../ooxml-core/src/index.ts"() {
714
- "use strict";
715
- init_cjs_shims();
716
- init_src();
717
- WORD_MAIN_NS = "http://schemas.openxmlformats.org/wordprocessingml/2006/main";
718
- DEFAULT_DOCUMENT_XML = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
719
- <w:document xmlns:w="${WORD_MAIN_NS}">
720
- <w:body>
721
- <w:p><w:r><w:t/></w:r></w:p>
722
- <w:sectPr>
723
- <w:pgSz w:w="12240" w:h="15840"/>
724
- <w:pgMar w:top="1440" w:right="1440" w:bottom="1440" w:left="1440" w:header="708" w:footer="708" w:gutter="0"/>
725
- </w:sectPr>
726
- </w:body>
727
- </w:document>`;
728
- DEFAULT_CONTENT_TYPES_XML = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
729
- <Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
730
- <Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>
731
- <Default Extension="xml" ContentType="application/xml"/>
732
- <Override PartName="/word/document.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"/>
733
- </Types>`;
734
- DEFAULT_ROOT_RELS_XML = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
735
- <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
736
- <Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="word/document.xml"/>
737
- </Relationships>`;
738
- DEFAULT_DOCUMENT_RELS_XML = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
739
- <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"/>`;
740
- }
741
- });
742
-
743
698
  // ../doc-model/src/normalize.ts
744
699
  function normalizeUint8Array(value) {
745
700
  if (value instanceof Uint8Array) {
@@ -1315,8 +1270,8 @@ var init_freeze = __esm({
1315
1270
  });
1316
1271
 
1317
1272
  // ../doc-model/src/index.ts
1318
- var src_exports2 = {};
1319
- __export(src_exports2, {
1273
+ var src_exports = {};
1274
+ __export(src_exports, {
1320
1275
  allocateBlockId: () => allocateBlockId,
1321
1276
  buildDocModel: () => buildDocModel,
1322
1277
  buildDocModelFromBytes: () => buildDocModelFromBytes,
@@ -1338,14 +1293,25 @@ async function buildDocModel(pkg) {
1338
1293
  return normalizeDocModel(model);
1339
1294
  }
1340
1295
  async function buildDocModelFromBytes(bytes) {
1341
- const { parseDocx: parseDocx2 } = await Promise.resolve().then(() => (init_src2(), src_exports));
1342
- const payload = bytes instanceof Uint8Array ? bytes : new Uint8Array(bytes);
1343
- const buffer = payload.buffer.slice(payload.byteOffset, payload.byteOffset + payload.byteLength);
1344
- const pkg = await parseDocx2(buffer);
1345
- const model = await buildDocModel(pkg);
1346
- return { package: pkg, model };
1296
+ const startedAt = performance.now();
1297
+ const result = await wasmBuildDocModelFromBytes(bytes);
1298
+ const builtAt = performance.now();
1299
+ const { parts, binaryAssets, warnings } = wasmPackageToMaps(result.package);
1300
+ const pkg = { parts, binaryAssets, ...warnings.length > 0 ? { warnings } : {} };
1301
+ const model = normalizeDocModel(result.model);
1302
+ const finishedAt = performance.now();
1303
+ const buildModelMs = result.timings.buildModelMs + finishedAt - builtAt;
1304
+ return {
1305
+ package: pkg,
1306
+ model,
1307
+ timings: {
1308
+ parseMs: Math.max(0, builtAt - startedAt - result.timings.buildModelMs),
1309
+ buildModelMs,
1310
+ totalMs: finishedAt - startedAt
1311
+ }
1312
+ };
1347
1313
  }
1348
- var init_src3 = __esm({
1314
+ var init_src2 = __esm({
1349
1315
  "../doc-model/src/index.ts"() {
1350
1316
  "use strict";
1351
1317
  init_cjs_shims();
@@ -1359,6 +1325,81 @@ var init_src3 = __esm({
1359
1325
  }
1360
1326
  });
1361
1327
 
1328
+ // ../ooxml-core/src/index.ts
1329
+ var src_exports2 = {};
1330
+ __export(src_exports2, {
1331
+ createMinimalDocxPackage: () => createMinimalDocxPackage,
1332
+ getPart: () => getPart,
1333
+ packageToArrayBuffer: () => packageToArrayBuffer,
1334
+ parseDocx: () => parseDocx,
1335
+ withPart: () => withPart
1336
+ });
1337
+ async function parseDocx(input) {
1338
+ const wasmPackage = await wasmParseDocx(input);
1339
+ const { parts, binaryAssets, warnings } = wasmPackageToMaps(wasmPackage);
1340
+ return { parts, binaryAssets, ...warnings.length > 0 ? { warnings } : {} };
1341
+ }
1342
+ async function packageToArrayBuffer(pkg) {
1343
+ return wasmPackageToArrayBuffer(mapsToWasmPackage(pkg));
1344
+ }
1345
+ function createMinimalDocxPackage(documentXml = DEFAULT_DOCUMENT_XML) {
1346
+ return {
1347
+ parts: /* @__PURE__ */ new Map([
1348
+ ["[Content_Types].xml", { name: "[Content_Types].xml", content: DEFAULT_CONTENT_TYPES_XML }],
1349
+ ["_rels/.rels", { name: "_rels/.rels", content: DEFAULT_ROOT_RELS_XML }],
1350
+ ["word/document.xml", { name: "word/document.xml", content: documentXml }],
1351
+ [
1352
+ "word/_rels/document.xml.rels",
1353
+ { name: "word/_rels/document.xml.rels", content: DEFAULT_DOCUMENT_RELS_XML }
1354
+ ]
1355
+ ]),
1356
+ binaryAssets: /* @__PURE__ */ new Map()
1357
+ };
1358
+ }
1359
+ function getPart(pkg, partName) {
1360
+ return pkg.parts.get(partName);
1361
+ }
1362
+ function withPart(pkg, part) {
1363
+ const parts = new Map(pkg.parts);
1364
+ parts.set(part.name, part);
1365
+ return {
1366
+ parts,
1367
+ binaryAssets: new Map(pkg.binaryAssets),
1368
+ ...pkg.warnings && pkg.warnings.length > 0 ? { warnings: [...pkg.warnings] } : {}
1369
+ };
1370
+ }
1371
+ var WORD_MAIN_NS, DEFAULT_DOCUMENT_XML, DEFAULT_CONTENT_TYPES_XML, DEFAULT_ROOT_RELS_XML, DEFAULT_DOCUMENT_RELS_XML;
1372
+ var init_src3 = __esm({
1373
+ "../ooxml-core/src/index.ts"() {
1374
+ "use strict";
1375
+ init_cjs_shims();
1376
+ init_src();
1377
+ WORD_MAIN_NS = "http://schemas.openxmlformats.org/wordprocessingml/2006/main";
1378
+ DEFAULT_DOCUMENT_XML = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
1379
+ <w:document xmlns:w="${WORD_MAIN_NS}">
1380
+ <w:body>
1381
+ <w:p><w:r><w:t/></w:r></w:p>
1382
+ <w:sectPr>
1383
+ <w:pgSz w:w="12240" w:h="15840"/>
1384
+ <w:pgMar w:top="1440" w:right="1440" w:bottom="1440" w:left="1440" w:header="708" w:footer="708" w:gutter="0"/>
1385
+ </w:sectPr>
1386
+ </w:body>
1387
+ </w:document>`;
1388
+ DEFAULT_CONTENT_TYPES_XML = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
1389
+ <Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
1390
+ <Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>
1391
+ <Default Extension="xml" ContentType="application/xml"/>
1392
+ <Override PartName="/word/document.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"/>
1393
+ </Types>`;
1394
+ DEFAULT_ROOT_RELS_XML = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
1395
+ <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
1396
+ <Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="word/document.xml"/>
1397
+ </Relationships>`;
1398
+ DEFAULT_DOCUMENT_RELS_XML = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
1399
+ <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"/>`;
1400
+ }
1401
+ });
1402
+
1362
1403
  // src/index.tsx
1363
1404
  var index_exports = {};
1364
1405
  __export(index_exports, {
@@ -1775,28 +1816,24 @@ async function importDocxOnMainThread(buffer, signal) {
1775
1816
  throw createAbortError();
1776
1817
  }
1777
1818
  const startedAt = performanceNow();
1778
- const [{ parseDocx: parseDocx2 }, { buildDocModel: buildDocModel2, ensureDocModelBlockIds: ensureDocModelBlockIds2 }] = await Promise.all([
1779
- Promise.resolve().then(() => (init_src2(), src_exports)),
1780
- Promise.resolve().then(() => (init_src3(), src_exports2))
1781
- ]);
1782
- const pkg = await parseDocx2(buffer);
1783
- const parsedAt = performanceNow();
1819
+ const { buildDocModelFromBytes: buildDocModelFromBytes2, ensureDocModelBlockIds: ensureDocModelBlockIds2 } = await Promise.resolve().then(() => (init_src2(), src_exports));
1784
1820
  if (signal?.aborted) {
1785
1821
  throw createAbortError();
1786
1822
  }
1787
- const model = ensureDocModelBlockIds2(await buildDocModel2(pkg));
1823
+ const result = await buildDocModelFromBytes2(buffer);
1824
+ const model = ensureDocModelBlockIds2(result.model);
1788
1825
  const finishedAt = performanceNow();
1789
1826
  if (signal?.aborted) {
1790
1827
  throw createAbortError();
1791
1828
  }
1792
1829
  return {
1793
- package: pkg,
1830
+ package: result.package,
1794
1831
  model,
1795
1832
  source: "main-thread",
1796
1833
  timings: {
1797
1834
  totalMs: finishedAt - startedAt,
1798
- parseMs: parsedAt - startedAt,
1799
- buildModelMs: finishedAt - parsedAt
1835
+ parseMs: result.timings.parseMs,
1836
+ buildModelMs: result.timings.buildModelMs
1800
1837
  }
1801
1838
  };
1802
1839
  }
@@ -1808,12 +1845,18 @@ async function importDocxBuffer(buffer, options = {}) {
1808
1845
  throw createAbortError();
1809
1846
  }
1810
1847
  if (!canUseDocxImportWorker(options)) {
1848
+ if (options.useWorker === "required") {
1849
+ throw new Error("DOCX import requires a Web Worker and a worker-compatible WASM source");
1850
+ }
1811
1851
  return importDocxOnMainThread(buffer, options.signal);
1812
1852
  }
1813
1853
  let worker;
1814
1854
  try {
1815
1855
  worker = createDocxImportWorker();
1816
- } catch {
1856
+ } catch (error) {
1857
+ if (options.useWorker === "required") {
1858
+ throw new Error("Unable to start the DOCX import worker", { cause: error });
1859
+ }
1817
1860
  return importDocxOnMainThread(buffer, options.signal);
1818
1861
  }
1819
1862
  const requestId = nextImportWorkerRequestId;
@@ -1883,7 +1926,7 @@ async function importDocxBuffer(buffer, options = {}) {
1883
1926
  }
1884
1927
  });
1885
1928
  const result = await workerResult;
1886
- const { ensureDocModelBlockIds: ensureDocModelBlockIds2 } = await Promise.resolve().then(() => (init_src3(), src_exports2));
1929
+ const { ensureDocModelBlockIds: ensureDocModelBlockIds2 } = await Promise.resolve().then(() => (init_src2(), src_exports));
1887
1930
  ensureDocModelBlockIds2(result.model);
1888
1931
  return result;
1889
1932
  }
@@ -1892,17 +1935,282 @@ async function importDocxBuffer(buffer, options = {}) {
1892
1935
  init_cjs_shims();
1893
1936
  var React = __toESM(require("react"), 1);
1894
1937
  var import_react_dom = require("react-dom");
1938
+
1939
+ // src/pointer-drag.ts
1940
+ init_cjs_shims();
1941
+ function startObjectDrag(options) {
1942
+ const { event, element } = options;
1943
+ const doc = element.ownerDocument;
1944
+ const win = doc.defaultView;
1945
+ const rect = element.getBoundingClientRect();
1946
+ const grabX = event.clientX - rect.left;
1947
+ const grabY = event.clientY - rect.top;
1948
+ let clientX = event.clientX;
1949
+ let clientY = event.clientY;
1950
+ let active = false;
1951
+ let ended = false;
1952
+ let animationFrame = 0;
1953
+ let ghost;
1954
+ let previousTime = 0;
1955
+ const hiddenElements = /* @__PURE__ */ new Map();
1956
+ const scrollContainers = [];
1957
+ for (let parent = element.parentElement; parent; parent = parent.parentElement) {
1958
+ if (/(auto|scroll)/.test(win.getComputedStyle(parent).overflowY)) {
1959
+ scrollContainers.push(parent);
1960
+ }
1961
+ }
1962
+ const scrollingElement = doc.scrollingElement;
1963
+ if (scrollingElement && !scrollContainers.includes(scrollingElement)) {
1964
+ scrollContainers.push(scrollingElement);
1965
+ }
1966
+ const scrollAnchors = /* @__PURE__ */ new Map();
1967
+ const frame = () => ({
1968
+ clientX,
1969
+ clientY,
1970
+ left: clientX - grabX,
1971
+ top: clientY - grabY,
1972
+ width: rect.width,
1973
+ height: rect.height
1974
+ });
1975
+ const hideSource = () => {
1976
+ const source = options.resolveElement();
1977
+ if (source && !hiddenElements.has(source)) {
1978
+ hiddenElements.set(source, source.style.visibility);
1979
+ source.style.visibility = "hidden";
1980
+ }
1981
+ };
1982
+ const observer = new MutationObserver(() => {
1983
+ if (active && !ended) hideSource();
1984
+ });
1985
+ observer.observe(
1986
+ element.closest("[data-testid=docx-editor-viewer]") ?? element.parentElement ?? element,
1987
+ { childList: true, subtree: true }
1988
+ );
1989
+ const tick = (time) => {
1990
+ if (ended) return;
1991
+ const elapsed = previousTime ? Math.min(32, time - previousTime) : 16;
1992
+ previousTime = time;
1993
+ for (const container of scrollContainers) {
1994
+ const bounds = container === scrollingElement ? { top: 0, bottom: win.innerHeight, left: 0, right: win.innerWidth } : container.getBoundingClientRect();
1995
+ if (clientX < bounds.left || clientX > bounds.right) continue;
1996
+ const edge = 40;
1997
+ 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;
1998
+ const before = container.scrollTop;
1999
+ container.scrollTop += speed * elapsed * 0.7;
2000
+ if (container.scrollTop !== before) break;
2001
+ }
2002
+ hideSource();
2003
+ const next = frame();
2004
+ if (ghost) {
2005
+ ghost.style.left = `${next.left}px`;
2006
+ ghost.style.top = `${next.top}px`;
2007
+ }
2008
+ options.onFrame(next);
2009
+ animationFrame = win.requestAnimationFrame(tick);
2010
+ };
2011
+ const cleanup = () => {
2012
+ ended = true;
2013
+ win.cancelAnimationFrame(animationFrame);
2014
+ win.removeEventListener("pointermove", move, true);
2015
+ win.removeEventListener("pointerup", up, true);
2016
+ win.removeEventListener("pointercancel", cancelPointer, true);
2017
+ win.removeEventListener("keydown", keydown);
2018
+ win.removeEventListener("blur", cancel);
2019
+ observer.disconnect();
2020
+ win.requestAnimationFrame(
2021
+ () => win.requestAnimationFrame(() => {
2022
+ scrollAnchors.forEach((value, container) => {
2023
+ container.style.overflowAnchor = value;
2024
+ });
2025
+ })
2026
+ );
2027
+ ghost?.remove();
2028
+ hiddenElements.forEach((visibility, source) => {
2029
+ source.style.visibility = visibility;
2030
+ });
2031
+ };
2032
+ const cancel = () => {
2033
+ if (ended) return;
2034
+ cleanup();
2035
+ options.onCancel();
2036
+ };
2037
+ const cancelPointer = (next) => {
2038
+ if (next.pointerId === event.pointerId) cancel();
2039
+ };
2040
+ const keydown = (next) => {
2041
+ if (next.key === "Escape") {
2042
+ next.preventDefault();
2043
+ cancel();
2044
+ }
2045
+ };
2046
+ const move = (next) => {
2047
+ if (next.pointerId !== event.pointerId || ended) return;
2048
+ clientX = next.clientX;
2049
+ clientY = next.clientY;
2050
+ if (!active) {
2051
+ if (Math.hypot(clientX - event.clientX, clientY - event.clientY) < 4)
2052
+ return;
2053
+ active = true;
2054
+ for (const container of scrollContainers) {
2055
+ scrollAnchors.set(container, container.style.overflowAnchor);
2056
+ container.style.overflowAnchor = "none";
2057
+ }
2058
+ ghost = element.cloneNode(true);
2059
+ ghost.querySelectorAll(
2060
+ "[data-image-resize-handle], [data-docx-table-move-handle]"
2061
+ ).forEach((handle) => handle.remove());
2062
+ for (const node of [ghost, ...ghost.querySelectorAll("*")]) {
2063
+ node.removeAttribute("id");
2064
+ for (const attribute of [...node.attributes]) {
2065
+ if (attribute.name.startsWith("data-docx-"))
2066
+ node.removeAttribute(attribute.name);
2067
+ }
2068
+ node.setAttribute("contenteditable", "false");
2069
+ }
2070
+ const style = win.getComputedStyle(element);
2071
+ Object.assign(ghost.style, {
2072
+ position: "fixed",
2073
+ margin: "0",
2074
+ left: `${rect.left}px`,
2075
+ top: `${rect.top}px`,
2076
+ width: `${rect.width / options.zoom}px`,
2077
+ height: `${rect.height / options.zoom}px`,
2078
+ transform: `scale(${options.zoom})`,
2079
+ transformOrigin: "top left",
2080
+ visibility: "visible",
2081
+ pointerEvents: "none",
2082
+ zIndex: "2147483647",
2083
+ opacity: "0.9",
2084
+ userSelect: "none",
2085
+ font: style.font,
2086
+ color: style.color
2087
+ });
2088
+ ghost.setAttribute("aria-hidden", "true");
2089
+ ghost.setAttribute("data-docx-drag-ghost", "true");
2090
+ doc.body.appendChild(ghost);
2091
+ hideSource();
2092
+ animationFrame = win.requestAnimationFrame(tick);
2093
+ }
2094
+ next.preventDefault();
2095
+ next.stopPropagation();
2096
+ };
2097
+ const up = (next) => {
2098
+ if (next.pointerId !== event.pointerId || ended) return;
2099
+ clientX = next.clientX;
2100
+ clientY = next.clientY;
2101
+ const finalFrame = frame();
2102
+ if (active) {
2103
+ next.preventDefault();
2104
+ next.stopPropagation();
2105
+ }
2106
+ try {
2107
+ if (active) options.onDrop(finalFrame);
2108
+ else options.onCancel();
2109
+ } finally {
2110
+ cleanup();
2111
+ }
2112
+ };
2113
+ win.addEventListener("pointermove", move, { passive: false, capture: true });
2114
+ win.addEventListener("pointerup", up, true);
2115
+ win.addEventListener("pointercancel", cancelPointer, true);
2116
+ win.addEventListener("keydown", keydown);
2117
+ win.addEventListener("blur", cancel);
2118
+ return cancel;
2119
+ }
2120
+
2121
+ // src/image-drop.ts
2122
+ init_cjs_shims();
2123
+ function copyModelForParagraphEdits(model, locations) {
2124
+ const copies = /* @__PURE__ */ new Map();
2125
+ const copy = (value) => {
2126
+ const existing = copies.get(value);
2127
+ if (existing) return existing;
2128
+ const next2 = Array.isArray(value) ? [...value] : { ...value };
2129
+ copies.set(value, next2);
2130
+ copies.set(next2, next2);
2131
+ return next2;
2132
+ };
2133
+ const next = { ...model, nodes: [...model.nodes] };
2134
+ for (const location of locations) {
2135
+ if (location.kind === "paragraph") {
2136
+ const node = next.nodes[location.nodeIndex];
2137
+ if (node?.type !== "paragraph") continue;
2138
+ const paragraph = copy(node);
2139
+ paragraph.children = copy(paragraph.children);
2140
+ next.nodes[location.nodeIndex] = paragraph;
2141
+ } else {
2142
+ const node = next.nodes[location.tableIndex];
2143
+ if (node?.type !== "table") continue;
2144
+ const row = node.rows[location.rowIndex];
2145
+ const cell = row?.cells[location.cellIndex];
2146
+ if (!cell) continue;
2147
+ let paragraphIndex = -1;
2148
+ const contentIndex = cell.nodes.findIndex((content) => {
2149
+ if (content.type === "paragraph") paragraphIndex++;
2150
+ return content.type === "paragraph" && paragraphIndex === location.paragraphIndex;
2151
+ });
2152
+ const paragraph = cell.nodes[contentIndex];
2153
+ if (paragraph?.type !== "paragraph") continue;
2154
+ const nextTable = copy(node);
2155
+ next.nodes[location.tableIndex] = nextTable;
2156
+ nextTable.rows = copy(node.rows);
2157
+ const nextRow = copy(row);
2158
+ nextTable.rows[location.rowIndex] = nextRow;
2159
+ nextRow.cells = copy(row.cells);
2160
+ const nextCell = copy(cell);
2161
+ nextRow.cells[location.cellIndex] = nextCell;
2162
+ nextCell.nodes = copy(cell.nodes);
2163
+ const nextParagraph = copy(paragraph);
2164
+ nextCell.nodes[contentIndex] = nextParagraph;
2165
+ nextParagraph.children = copy(paragraph.children);
2166
+ }
2167
+ }
2168
+ return next;
2169
+ }
2170
+ function imageDropPositionAtTextOffset(paragraph, textOffset, options) {
2171
+ let remaining = Math.max(0, Math.round(textOffset));
2172
+ for (let childIndex = 0; childIndex < paragraph.children.length; childIndex++) {
2173
+ const child = paragraph.children[childIndex];
2174
+ const length = child.type === "image" ? !child.floating && options?.countInlineImages ? 1 : 0 : child.type === "text" ? child.text.length : (options?.fieldText?.(child) ?? child.value ?? "").length;
2175
+ if (remaining <= length && length > 0) {
2176
+ if (remaining === 0) return { childIndex };
2177
+ if (remaining === length) return { childIndex: childIndex + 1 };
2178
+ return child.type === "text" ? { childIndex, textOffset: remaining } : { childIndex: childIndex + 1 };
2179
+ }
2180
+ remaining -= length;
2181
+ }
2182
+ return { childIndex: paragraph.children.length };
2183
+ }
2184
+ function insertImageAtDropPosition(paragraph, image, childIndex, textOffset) {
2185
+ const index = Math.max(0, Math.min(childIndex, paragraph.children.length));
2186
+ const child = paragraph.children[index];
2187
+ if (child?.type === "text" && textOffset !== void 0) {
2188
+ const offset = Math.max(0, Math.min(textOffset, child.text.length));
2189
+ const before = child.text.slice(0, offset);
2190
+ const after = child.text.slice(offset);
2191
+ const children = [];
2192
+ if (before) children.push({ ...child, text: before });
2193
+ children.push(image);
2194
+ if (after) children.push({ ...child, text: after });
2195
+ paragraph.children.splice(index, 1, ...children);
2196
+ return index + (before ? 1 : 0);
2197
+ }
2198
+ paragraph.children.splice(index, 0, image);
2199
+ return index;
2200
+ }
2201
+
2202
+ // src/editor.tsx
1895
2203
  var import_server = require("react-dom/server");
1896
2204
  var import_react_virtual = require("@tanstack/react-virtual");
1897
- init_src3();
2205
+ init_src2();
1898
2206
 
1899
2207
  // ../editor-ops/src/index.ts
1900
2208
  init_cjs_shims();
1901
- init_src3();
2209
+ init_src2();
1902
2210
 
1903
2211
  // ../editor-ops/src/annotations.ts
1904
2212
  init_cjs_shims();
1905
- init_src3();
2213
+ init_src2();
1906
2214
 
1907
2215
  // ../editor-ops/src/xml.ts
1908
2216
  init_cjs_shims();
@@ -2352,11 +2660,11 @@ function safeParagraphContext(model, nodeIndex) {
2352
2660
  return { ok: false, reason: "unsupported" };
2353
2661
  }
2354
2662
  const paragraphRange = paragraphs[0];
2355
- for (const revision of [
2663
+ for (const revision2 of [
2356
2664
  ...elementsNamed(scan, "w:ins"),
2357
2665
  ...elementsNamed(scan, "w:del")
2358
2666
  ]) {
2359
- if (revision.parentStart !== paragraphRange.start) {
2667
+ if (revision2.parentStart !== paragraphRange.start) {
2360
2668
  return { ok: false, reason: "unsupported" };
2361
2669
  }
2362
2670
  }
@@ -2393,9 +2701,9 @@ function hasNonWhitespaceOutsideChildren(xml, parent, children) {
2393
2701
  }
2394
2702
  return xml.slice(cursor, parent.closeStart).trim().length > 0;
2395
2703
  }
2396
- function revisionRangeIsSafe(context, revision, kind) {
2397
- const runs = directChildElements(context.scan, revision);
2398
- if (runs.length === 0 || runs.some((run) => run.tagName !== "w:r") || hasNonWhitespaceOutsideChildren(context.sourceXml, revision, runs)) {
2704
+ function revisionRangeIsSafe(context, revision2, kind) {
2705
+ const runs = directChildElements(context.scan, revision2);
2706
+ if (runs.length === 0 || runs.some((run) => run.tagName !== "w:r") || hasNonWhitespaceOutsideChildren(context.sourceXml, revision2, runs)) {
2399
2707
  return false;
2400
2708
  }
2401
2709
  const expectedTextTag = kind === "insertion" ? "w:t" : "w:delText";
@@ -3154,12 +3462,10 @@ function updatedParagraphNodeText(source, text, options) {
3154
3462
  if (paragraphEditableText(source) === text) {
3155
3463
  return source;
3156
3464
  }
3157
- const paragraph = cloneParagraphNode(source);
3158
- paragraph.children = distributeTextAcrossParagraphChildren(
3159
- source,
3160
- text,
3161
- options
3162
- );
3465
+ const paragraph = {
3466
+ ...source,
3467
+ children: distributeTextAcrossParagraphChildren(source, text, options)
3468
+ };
3163
3469
  paragraph.sourceTextPatch = sourceTextPatchForUpdate(source, paragraph);
3164
3470
  if (!paragraph.sourceTextPatch) {
3165
3471
  paragraph.sourceXml = void 0;
@@ -3205,6 +3511,15 @@ function discardTableSourceXml(table) {
3205
3511
  table.sourceXml = void 0;
3206
3512
  table.sourceTextPatches = void 0;
3207
3513
  }
3514
+ function copyTableForCellTextEdit(table, rowIndex, cellIndex) {
3515
+ const row = table.rows[rowIndex];
3516
+ const cell = row.cells[cellIndex];
3517
+ const rows = [...table.rows];
3518
+ const cells = [...row.cells];
3519
+ cells[cellIndex] = { ...cell, nodes: [...cell.nodes] };
3520
+ rows[rowIndex] = { ...row, cells };
3521
+ return { ...table, rows };
3522
+ }
3208
3523
  function directParagraphNodeIndex(nodes, paragraphIndex) {
3209
3524
  const target = Math.max(0, Math.round(paragraphIndex));
3210
3525
  let cursor = 0;
@@ -3387,9 +3702,7 @@ function distributeTextAcrossParagraphChildren(paragraph, text, options) {
3387
3702
  }
3388
3703
  textGroups.push(currentGroup);
3389
3704
  currentGroup = [];
3390
- anchors.push(
3391
- child.type === "form-field" ? cloneFormFieldRun(child) : cloneImageRun(child)
3392
- );
3705
+ anchors.push(child);
3393
3706
  }
3394
3707
  textGroups.push(currentGroup);
3395
3708
  const allAnchorsAreImages = anchors.length > 0 && anchors.every((anchor) => anchor.type === "image");
@@ -3468,7 +3781,7 @@ function distributeTextAcrossParagraphChildren(paragraph, text, options) {
3468
3781
  });
3469
3782
  }
3470
3783
  if (index < anchors.length) {
3471
- nextChildren2.push(cloneParagraphChildRun(anchors[index]));
3784
+ nextChildren2.push(anchors[index]);
3472
3785
  }
3473
3786
  }
3474
3787
  if (nextChildren2.length > 0) {
@@ -3512,7 +3825,7 @@ function distributeTextAcrossParagraphChildren(paragraph, text, options) {
3512
3825
  });
3513
3826
  }
3514
3827
  if (index < anchors.length) {
3515
- nextChildren.push(cloneParagraphChildRun(anchors[index]));
3828
+ nextChildren.push(anchors[index]);
3516
3829
  }
3517
3830
  }
3518
3831
  if (nextChildren.length > 0) {
@@ -3833,7 +4146,7 @@ function updateTableCellText(model, tableIndex, rowIndex, cellIndex, text, optio
3833
4146
  )) {
3834
4147
  return model;
3835
4148
  }
3836
- const tableNode = cloneTableNode(sourceTable);
4149
+ const tableNode = copyTableForCellTextEdit(sourceTable, rowIndex, cellIndex);
3837
4150
  const row = tableNode.rows[rowIndex];
3838
4151
  const cell = row?.cells[cellIndex];
3839
4152
  if (!cell) {
@@ -3897,7 +4210,7 @@ function updateTableCellParagraphText(model, tableIndex, rowIndex, cellIndex, pa
3897
4210
  const sourceCell = sourceTable.rows[rowIndex]?.cells[cellIndex];
3898
4211
  const sourceParagraphNodeIndex = sourceCell ? directParagraphNodeIndex(sourceCell.nodes, paragraphIndex) : void 0;
3899
4212
  const sourceParagraph = sourceParagraphNodeIndex === void 0 ? void 0 : sourceCell?.nodes[sourceParagraphNodeIndex];
3900
- if (!sourceCell || !sourceParagraph || sourceParagraph.type !== "paragraph") {
4213
+ if (!sourceCell || !sourceParagraph || sourceParagraph.type !== "paragraph" || sourceParagraphNodeIndex === void 0) {
3901
4214
  return { ...model, nodes: [...model.nodes] };
3902
4215
  }
3903
4216
  if (paragraphTextEditIsFailClosed(sourceParagraph, text)) {
@@ -3906,17 +4219,15 @@ function updateTableCellParagraphText(model, tableIndex, rowIndex, cellIndex, pa
3906
4219
  if (paragraphEditableText(sourceParagraph) === text) {
3907
4220
  return model;
3908
4221
  }
3909
- const tableNode = cloneTableNode(sourceTable);
3910
- const row = tableNode.rows[rowIndex];
3911
- const cell = row?.cells[cellIndex];
3912
- const paragraphNodeIndex = cell ? directParagraphNodeIndex(cell.nodes, paragraphIndex) : void 0;
3913
- const paragraph = paragraphNodeIndex === void 0 ? void 0 : cell?.nodes[paragraphNodeIndex];
3914
- if (!cell || !paragraph || paragraph.type !== "paragraph" || paragraphNodeIndex === void 0) {
3915
- return { ...model, nodes: [...model.nodes] };
3916
- }
3917
- const updatedParagraph = updatedParagraphNodeText(paragraph, text, options);
3918
- cell.nodes[paragraphNodeIndex] = updatedParagraph;
3919
- if (!retainTableSourceTextPatch(tableNode, paragraph, updatedParagraph)) {
4222
+ const tableNode = copyTableForCellTextEdit(sourceTable, rowIndex, cellIndex);
4223
+ const cell = tableNode.rows[rowIndex].cells[cellIndex];
4224
+ const updatedParagraph = updatedParagraphNodeText(
4225
+ sourceParagraph,
4226
+ text,
4227
+ options
4228
+ );
4229
+ cell.nodes[sourceParagraphNodeIndex] = updatedParagraph;
4230
+ if (!retainTableSourceTextPatch(tableNode, sourceParagraph, updatedParagraph)) {
3920
4231
  discardTableSourceXml(tableNode);
3921
4232
  }
3922
4233
  const nextNodes = [...model.nodes];
@@ -3930,7 +4241,7 @@ function updateTableCellParagraphTextRecursive(model, tableIndex, rowIndex, cell
3930
4241
  }
3931
4242
  const sourceCell = sourceTable.rows[rowIndex]?.cells[cellIndex];
3932
4243
  const sourceParagraph = sourceCell ? recursiveParagraphAtIndex(sourceCell.nodes, paragraphIndex) : void 0;
3933
- if (!sourceParagraph) {
4244
+ if (!sourceCell || !sourceParagraph) {
3934
4245
  return { ...model, nodes: [...model.nodes] };
3935
4246
  }
3936
4247
  if (paragraphTextEditIsFailClosed(sourceParagraph, text)) {
@@ -3939,53 +4250,49 @@ function updateTableCellParagraphTextRecursive(model, tableIndex, rowIndex, cell
3939
4250
  if (paragraphEditableText(sourceParagraph) === text) {
3940
4251
  return model;
3941
4252
  }
3942
- const next = cloneDocModel(model);
3943
- const tableNode = next.nodes[tableIndex];
3944
- if (!tableNode || tableNode.type !== "table") {
3945
- return next;
3946
- }
3947
- const row = tableNode.rows[rowIndex];
3948
- const cell = row?.cells[cellIndex];
3949
- if (!cell) {
3950
- return next;
3951
- }
3952
- const targetParagraphIndex = Math.max(0, Math.round(paragraphIndex));
3953
- let paragraphCursor = 0;
3954
- const updateInNodes = (nodes, ancestorTables) => {
4253
+ const updatedParagraph = updatedParagraphNodeText(
4254
+ sourceParagraph,
4255
+ text,
4256
+ options
4257
+ );
4258
+ const retainSource = (table) => {
4259
+ if (!retainTableSourceTextPatch(table, sourceParagraph, updatedParagraph)) {
4260
+ discardTableSourceXml(table);
4261
+ }
4262
+ };
4263
+ const updateInNodes = (nodes) => {
3955
4264
  for (let nodeIndex = 0; nodeIndex < nodes.length; nodeIndex += 1) {
3956
4265
  const node = nodes[nodeIndex];
3957
- if (!node) {
3958
- continue;
3959
- }
3960
- if (node.type === "paragraph") {
3961
- if (paragraphCursor !== targetParagraphIndex) {
3962
- paragraphCursor += 1;
3963
- continue;
3964
- }
3965
- const updatedParagraph = updatedParagraphNodeText(node, text, options);
3966
- nodes[nodeIndex] = updatedParagraph;
3967
- ancestorTables.forEach((ancestorTable) => {
3968
- if (!retainTableSourceTextPatch(ancestorTable, node, updatedParagraph)) {
3969
- discardTableSourceXml(ancestorTable);
3970
- }
3971
- });
3972
- return true;
3973
- }
3974
- ancestorTables.push(node);
3975
- for (const nestedRow of node.rows) {
3976
- for (const nestedCell of nestedRow.cells) {
3977
- if (updateInNodes(nestedCell.nodes, ancestorTables)) {
3978
- ancestorTables.pop();
3979
- return true;
3980
- }
4266
+ if (node === sourceParagraph) {
4267
+ const nextNodes2 = [...nodes];
4268
+ nextNodes2[nodeIndex] = updatedParagraph;
4269
+ return nextNodes2;
4270
+ }
4271
+ if (node.type !== "table") continue;
4272
+ for (let rowIndex2 = 0; rowIndex2 < node.rows.length; rowIndex2 += 1) {
4273
+ const row = node.rows[rowIndex2];
4274
+ for (let cellIndex2 = 0; cellIndex2 < row.cells.length; cellIndex2 += 1) {
4275
+ const updatedNodes2 = updateInNodes(row.cells[cellIndex2].nodes);
4276
+ if (!updatedNodes2) continue;
4277
+ const table = copyTableForCellTextEdit(node, rowIndex2, cellIndex2);
4278
+ table.rows[rowIndex2].cells[cellIndex2].nodes = updatedNodes2;
4279
+ retainSource(table);
4280
+ const nextNodes2 = [...nodes];
4281
+ nextNodes2[nodeIndex] = table;
4282
+ return nextNodes2;
3981
4283
  }
3982
4284
  }
3983
- ancestorTables.pop();
3984
4285
  }
3985
- return false;
4286
+ return void 0;
3986
4287
  };
3987
- updateInNodes(cell.nodes, [tableNode]);
3988
- return next;
4288
+ const updatedNodes = updateInNodes(sourceCell.nodes);
4289
+ if (!updatedNodes) return model;
4290
+ const tableNode = copyTableForCellTextEdit(sourceTable, rowIndex, cellIndex);
4291
+ tableNode.rows[rowIndex].cells[cellIndex].nodes = updatedNodes;
4292
+ retainSource(tableNode);
4293
+ const nextNodes = [...model.nodes];
4294
+ nextNodes[tableIndex] = tableNode;
4295
+ return { ...model, nodes: nextNodes };
3989
4296
  }
3990
4297
  function mutateParagraphTextRuns(model, transform) {
3991
4298
  for (const node of model.nodes) {
@@ -4260,13 +4567,52 @@ async function serializeDocModel(model, basePackage) {
4260
4567
  model,
4261
4568
  basePackage ? mapsToWasmPackage(basePackage) : void 0
4262
4569
  );
4263
- const { parseDocx: parseDocx2 } = await Promise.resolve().then(() => (init_src2(), src_exports));
4570
+ const { parseDocx: parseDocx2 } = await Promise.resolve().then(() => (init_src3(), src_exports2));
4264
4571
  return parseDocx2(bytes);
4265
4572
  }
4266
4573
  async function serializeDocx(model, basePackage) {
4267
4574
  return wasmSerializeDocx(model, basePackage ? mapsToWasmPackage(basePackage) : void 0);
4268
4575
  }
4269
4576
 
4577
+ // src/font-metrics.ts
4578
+ init_cjs_shims();
4579
+ var revision = 0;
4580
+ var observedFonts;
4581
+ var invalidators = /* @__PURE__ */ new Set();
4582
+ var listeners = /* @__PURE__ */ new Set();
4583
+ function invalidateFontMetrics() {
4584
+ revision += 1;
4585
+ invalidators.forEach((invalidate) => invalidate());
4586
+ listeners.forEach((listener) => listener());
4587
+ }
4588
+ function observeFonts() {
4589
+ const fonts = typeof document !== "undefined" ? document.fonts : void 0;
4590
+ if (fonts === observedFonts || !fonts?.addEventListener) {
4591
+ return;
4592
+ }
4593
+ observedFonts?.removeEventListener("loadingdone", invalidateFontMetrics);
4594
+ observedFonts = fonts;
4595
+ fonts.addEventListener("loadingdone", invalidateFontMetrics);
4596
+ }
4597
+ function registerFontMetricCache(invalidate) {
4598
+ invalidators.add(invalidate);
4599
+ observeFonts();
4600
+ }
4601
+ function subscribeFontMetrics(listener) {
4602
+ observeFonts();
4603
+ listeners.add(listener);
4604
+ return () => {
4605
+ listeners.delete(listener);
4606
+ };
4607
+ }
4608
+ function getFontMetricsRevision() {
4609
+ observeFonts();
4610
+ return revision;
4611
+ }
4612
+ function getServerFontMetricsRevision() {
4613
+ return 0;
4614
+ }
4615
+
4270
4616
  // src/editing-intents.ts
4271
4617
  init_cjs_shims();
4272
4618
  function resolveEditingIntent(source) {
@@ -5365,6 +5711,18 @@ function segmentTextByDocxScriptFont(text, style) {
5365
5711
  if (!text) {
5366
5712
  return [];
5367
5713
  }
5714
+ if (/^[\x00-\x7f]+$/.test(text)) {
5715
+ const script = classifyDocxFontScript(text[0], style) ?? "ascii";
5716
+ return [
5717
+ {
5718
+ text,
5719
+ startOffset: 0,
5720
+ endOffset: text.length,
5721
+ script,
5722
+ fontFamily: resolveDocxScriptFontFamily(style, script)
5723
+ }
5724
+ ];
5725
+ }
5368
5726
  const tokens = [];
5369
5727
  let offset = 0;
5370
5728
  for (const character of text) {
@@ -5398,15 +5756,24 @@ function segmentTextByDocxScriptFont(text, style) {
5398
5756
  previousScript = token.script;
5399
5757
  }
5400
5758
  const segments = [];
5759
+ const families = /* @__PURE__ */ new Map();
5760
+ let previousFamilyKey;
5401
5761
  for (const token of tokens) {
5402
5762
  const script = token.script ?? fallbackScript;
5403
- const fontFamily = resolveDocxScriptFontFamily(style, script);
5763
+ let resolved = families.get(script);
5764
+ if (!resolved) {
5765
+ const family = resolveDocxScriptFontFamily(style, script);
5766
+ resolved = { family, key: normalizedFamilyKey(family) };
5767
+ families.set(script, resolved);
5768
+ }
5769
+ const fontFamily = resolved.family;
5404
5770
  const previous = segments[segments.length - 1];
5405
- if (previous && normalizedFamilyKey(previous.fontFamily) === normalizedFamilyKey(fontFamily)) {
5771
+ if (previous && previousFamilyKey === resolved.key) {
5406
5772
  previous.text += token.text;
5407
5773
  previous.endOffset = token.endOffset;
5408
5774
  continue;
5409
5775
  }
5776
+ previousFamilyKey = resolved.key;
5410
5777
  segments.push({
5411
5778
  text: token.text,
5412
5779
  startOffset: token.startOffset,
@@ -5418,9 +5785,15 @@ function segmentTextByDocxScriptFont(text, style) {
5418
5785
  return segments;
5419
5786
  }
5420
5787
  function resolveDocxTextFontFamily(text, style) {
5421
- return segmentTextByDocxScriptFont(text, style).find(
5422
- (segment) => segment.fontFamily
5423
- )?.fontFamily ?? normalizeFamily(style?.fontFamily);
5788
+ let hasStrongScript = false;
5789
+ for (const character of text) {
5790
+ const script = classifyDocxFontScript(character, style);
5791
+ if (!script) continue;
5792
+ hasStrongScript = true;
5793
+ const family = resolveDocxScriptFontFamily(style, script);
5794
+ if (family) return family;
5795
+ }
5796
+ return (text && !hasStrongScript ? resolveDocxScriptFontFamily(style, neutralScript(style)) : void 0) ?? normalizeFamily(style?.fontFamily);
5424
5797
  }
5425
5798
 
5426
5799
  // src/editor-transaction-state.ts
@@ -5546,6 +5919,13 @@ var layoutByKey = /* @__PURE__ */ new Map();
5546
5919
  var lineCountByKey = /* @__PURE__ */ new Map();
5547
5920
  var fragmentOffsetAdvancesByFragment = /* @__PURE__ */ new WeakMap();
5548
5921
  var graphemeOffsetsByText = /* @__PURE__ */ new Map();
5922
+ registerFontMetricCache(() => {
5923
+ (0, import_pretext.clearCache)();
5924
+ preparedTextByKey.clear();
5925
+ layoutByKey.clear();
5926
+ lineCountByKey.clear();
5927
+ fragmentOffsetAdvancesByFragment = /* @__PURE__ */ new WeakMap();
5928
+ });
5549
5929
  var measureCanvas;
5550
5930
  var measureCanvasContext;
5551
5931
  var graphemeSegmenter;
@@ -6703,6 +7083,7 @@ var THUMBNAIL_DIRECT_TOKEN_REGEX = /(\r\n|\n|\t|[^\S\r\n\t]+|[^\s\r\n\t]+)/g;
6703
7083
  var THUMBNAIL_DIRECT_LEADING_WHITESPACE_REGEX = /^\s/;
6704
7084
  var THUMBNAIL_DIRECT_TEXT_MEASURE_CACHE_MAX_ENTRIES = 4096;
6705
7085
  var directThumbnailTextMeasureCache = /* @__PURE__ */ new Map();
7086
+ registerFontMetricCache(() => directThumbnailTextMeasureCache.clear());
6706
7087
  function measureDirectThumbnailToken(context, font, text) {
6707
7088
  if (!text) {
6708
7089
  return 0;
@@ -7573,6 +7954,15 @@ var estimatedTextAdvanceWidthByFontAndValue = /* @__PURE__ */ new Map();
7573
7954
  var pretextWordBreakModeByText = /* @__PURE__ */ new Map();
7574
7955
  var paragraphBaseFontSizePxByParagraph = /* @__PURE__ */ new WeakMap();
7575
7956
  var paragraphDominantFontFamilyByParagraph = /* @__PURE__ */ new WeakMap();
7957
+ registerFontMetricCache(() => {
7958
+ paragraphEstimatedHeightBySourceXml.clear();
7959
+ tableEstimatedHeightBySourceXml.clear();
7960
+ textWidthByFontAndValue.clear();
7961
+ estimatedTextAdvanceWidthByFontAndValue.clear();
7962
+ tableEstimatedRowHeightsByNode = /* @__PURE__ */ new WeakMap();
7963
+ wrappedLineCountByParagraph = /* @__PURE__ */ new WeakMap();
7964
+ paragraphPretextLayoutSourceCache = /* @__PURE__ */ new WeakMap();
7965
+ });
7576
7966
  function setCacheEntry(cache, key, value) {
7577
7967
  if (!cache.has(key) && cache.size >= XML_CACHE_MAX_ENTRIES) {
7578
7968
  const oldestKey = cache.keys().next().value;
@@ -8146,7 +8536,7 @@ function selectionOffsetsWithinElementDom(element) {
8146
8536
  try {
8147
8537
  const textLengthWithoutNumberingLabels = (range) => {
8148
8538
  const fragment = range.cloneContents();
8149
- fragment.querySelectorAll("[data-docx-numbering-label='true']").forEach((label) => {
8539
+ fragment.querySelectorAll("[data-docx-numbering-label='true'],[data-docx-image-location]").forEach((label) => {
8150
8540
  label.remove();
8151
8541
  });
8152
8542
  return fragment.textContent?.length ?? 0;
@@ -8174,7 +8564,7 @@ function staticRangeOffsetsWithinElementDom(element, staticRange) {
8174
8564
  try {
8175
8565
  const textLengthWithoutNumberingLabels = (range) => {
8176
8566
  const fragment = range.cloneContents();
8177
- fragment.querySelectorAll("[data-docx-numbering-label='true']").forEach((label) => {
8567
+ fragment.querySelectorAll("[data-docx-numbering-label='true'],[data-docx-image-location]").forEach((label) => {
8178
8568
  label.remove();
8179
8569
  });
8180
8570
  return fragment.textContent?.length ?? 0;
@@ -8207,7 +8597,7 @@ function setSelectionWithinElementByTextOffsetsDom(element, startOffset, endOffs
8207
8597
  let currentTextNode = walker.nextNode();
8208
8598
  while (currentTextNode) {
8209
8599
  if (currentTextNode instanceof Text && currentTextNode.parentElement?.closest(
8210
- "[data-docx-numbering-label='true']"
8600
+ "[data-docx-numbering-label='true'],[data-docx-image-location]"
8211
8601
  )) {
8212
8602
  currentTextNode = walker.nextNode();
8213
8603
  continue;
@@ -9504,7 +9894,7 @@ function tableSelectionCoversWholeTable(table, range) {
9504
9894
  }
9505
9895
  function textLengthFromRange(range) {
9506
9896
  const fragment = range.cloneContents();
9507
- fragment.querySelectorAll("[data-docx-numbering-label='true']").forEach((label) => {
9897
+ fragment.querySelectorAll("[data-docx-numbering-label='true'],[data-docx-image-location]").forEach((label) => {
9508
9898
  label.remove();
9509
9899
  });
9510
9900
  return fragment.textContent?.length ?? 0;
@@ -9800,7 +10190,7 @@ function replaceTabLayoutMarkersWithTabText(root) {
9800
10190
  function editableTextFromElement(element) {
9801
10191
  const clone = element.cloneNode(true);
9802
10192
  replaceTabLayoutMarkersWithTabText(clone);
9803
- clone.querySelectorAll("[data-docx-numbering-label='true']").forEach((label) => {
10193
+ clone.querySelectorAll("[data-docx-numbering-label='true'],[data-docx-image-location]").forEach((label) => {
9804
10194
  label.remove();
9805
10195
  });
9806
10196
  clone.querySelectorAll("br").forEach((lineBreak) => {
@@ -10178,9 +10568,15 @@ function paragraphHasOnlyWhitespaceText(paragraph) {
10178
10568
  if (child.type !== "text") {
10179
10569
  return false;
10180
10570
  }
10181
- return child.text.replace(/[\s\u00a0]+/g, "").length === 0;
10571
+ return !/[^\s\u00a0]/.test(child.text);
10182
10572
  });
10183
10573
  }
10574
+ function paragraphHasActiveNumbering(paragraph) {
10575
+ const numbering = paragraph.style?.numbering;
10576
+ return Boolean(
10577
+ numbering && Number.isFinite(numbering.numId) && Math.round(numbering.numId) > 0
10578
+ );
10579
+ }
10184
10580
  function paragraphContainsSectionBreakProperties(paragraph) {
10185
10581
  return /<w:sectPr\b/i.test(paragraph.sourceXml ?? "");
10186
10582
  }
@@ -10371,7 +10767,8 @@ function resolveForeignWrapExclusionsForFlowRange(obstacles, sourceNodeIndex, pa
10371
10767
  left: obstacle.left,
10372
10768
  right: obstacle.right,
10373
10769
  top: Math.max(0, Math.round(obstacle.top - pageFlowTopPx)),
10374
- bottom: Math.max(0, Math.round(obstacle.bottom - pageFlowTopPx))
10770
+ bottom: Math.max(0, Math.round(obstacle.bottom - pageFlowTopPx)),
10771
+ ...obstacle.fromDragPreview ? { fromDragPreview: true } : void 0
10375
10772
  });
10376
10773
  }
10377
10774
  return foreignExclusions;
@@ -10513,6 +10910,7 @@ function precomputePageSegmentForeignWrapExclusions(segments, model, availableWi
10513
10910
  };
10514
10911
  const baseFlowHeightPxBySegmentIndex = segments.map((segment) => {
10515
10912
  const node = model.nodes[segment.nodeIndex];
10913
+ if (segment.nodeIndex === interaction?.draggedTableIndex) return 0;
10516
10914
  return estimateRenderedPageSegmentHeightPx(
10517
10915
  node,
10518
10916
  segment,
@@ -10529,10 +10927,69 @@ function precomputePageSegmentForeignWrapExclusions(segments, model, availableWi
10529
10927
  );
10530
10928
  });
10531
10929
  const collectObstacles = (flowTopPxBySegmentIndex2, flowHeightPxBySegmentIndex2, previousObstacles) => {
10532
- const obstacles2 = [];
10930
+ const obstacles2 = interaction?.dragObstacle ? [
10931
+ {
10932
+ ...interaction.dragObstacle,
10933
+ sourceNodeIndex: -1,
10934
+ appliesToSourceNode: true
10935
+ }
10936
+ ] : [];
10533
10937
  for (let segmentIndex = 0; segmentIndex < segments.length; segmentIndex += 1) {
10534
10938
  const segment = segments[segmentIndex];
10535
- const node = model.nodes[segment.nodeIndex];
10939
+ let node = model.nodes[segment.nodeIndex];
10940
+ if (segment.nodeIndex === interaction?.draggedTableIndex) continue;
10941
+ if (node?.type === "paragraph" && interaction?.draggedImageKey?.startsWith(`p:${segment.nodeIndex}:`)) {
10942
+ const childIndex = Number(interaction.draggedImageKey.split(":")[2]);
10943
+ node = {
10944
+ ...node,
10945
+ children: node.children.map(
10946
+ (child, index) => index === childIndex && child.type === "image" ? {
10947
+ ...child,
10948
+ floating: {
10949
+ ...child.floating,
10950
+ wrapType: "none",
10951
+ behindDocument: true
10952
+ }
10953
+ } : child
10954
+ )
10955
+ };
10956
+ }
10957
+ const lineRange = segment.paragraphLineRange;
10958
+ const sliceTop = (lineRange?.startLineIndex ?? 0) * (lineRange?.lineHeightPx ?? 0);
10959
+ if (node?.type === "paragraph" && lineRange) {
10960
+ const paragraph = node;
10961
+ const source = buildParagraphPretextLayoutSource(paragraph);
10962
+ const unwrapped = source ? layoutParagraphPretextSource(
10963
+ paragraph,
10964
+ source,
10965
+ paragraphAvailableTextWidthPx(
10966
+ paragraph,
10967
+ segmentWidthPxAt(segment.nodeIndex),
10968
+ numberingDefinitions
10969
+ ),
10970
+ lineRange.lineHeightPx,
10971
+ []
10972
+ ) : void 0;
10973
+ node = {
10974
+ ...paragraph,
10975
+ children: paragraph.children.map((child, index) => {
10976
+ if (child.type !== "image" || !child.floating || !unwrapped)
10977
+ return child;
10978
+ const anchorTop = resolveCaretRectAtOffset(
10979
+ unwrapped,
10980
+ paragraphChildAnchorOffset(paragraph, index) + 1
10981
+ )?.top ?? 0;
10982
+ return anchorTop >= sliceTop && anchorTop < lineRange.endLineIndex * lineRange.lineHeightPx ? child : {
10983
+ ...child,
10984
+ floating: {
10985
+ ...child.floating,
10986
+ wrapType: "none",
10987
+ behindDocument: true
10988
+ }
10989
+ };
10990
+ })
10991
+ };
10992
+ }
10536
10993
  if (node?.type === "table" && node.style?.floating && !segment.tableRowRange) {
10537
10994
  const tableContainerWidthPx = segmentWidthPxAt(segment.nodeIndex);
10538
10995
  const tableFlowTopPx = flowTopPxBySegmentIndex2[segmentIndex] ?? 0;
@@ -10633,7 +11090,9 @@ function precomputePageSegmentForeignWrapExclusions(segments, model, availableWi
10633
11090
  bottom: band.bottomPx + (floating.distBPx ?? 0)
10634
11091
  });
10635
11092
  });
10636
- const flowTopPx = measuredBandAt(`para:${segment.nodeIndex}`)?.topPx ?? flowTopPxBySegmentIndex2[segmentIndex] ?? 0;
11093
+ const flowTopPx = (measuredBandAt(
11094
+ `para:${segment.nodeIndex}:${lineRange?.startLineIndex ?? 0}`
11095
+ )?.topPx ?? (!lineRange ? measuredBandAt(`para:${segment.nodeIndex}`)?.topPx : void 0) ?? flowTopPxBySegmentIndex2[segmentIndex] ?? 0) - sliceTop;
10637
11096
  obstacles2.push(
10638
11097
  ...collectPageFlowWrapObstaclesForParagraph(
10639
11098
  node,
@@ -10676,7 +11135,7 @@ function precomputePageSegmentForeignWrapExclusions(segments, model, availableWi
10676
11135
  let flowHeightPxBySegmentIndex = baseFlowHeightPxBySegmentIndex;
10677
11136
  let obstacles = firstPassObstacles;
10678
11137
  const hasInteractionPreview = Boolean(
10679
- interaction?.floatingMovePreview || interaction?.resizePreview
11138
+ interaction?.floatingMovePreview || interaction?.resizePreview || interaction?.dragObstacle
10680
11139
  );
10681
11140
  const hasFloatingTableObstacle = segments.some((segment) => {
10682
11141
  const node = model.nodes[segment.nodeIndex];
@@ -10758,7 +11217,9 @@ function precomputePageSegmentForeignWrapExclusions(segments, model, availableWi
10758
11217
  const result = segments.map((segment, segmentIndex) => {
10759
11218
  const segmentLineRange = segment.paragraphLineRange;
10760
11219
  const segmentCoversWholeParagraph = !segmentLineRange || segmentLineRange.startLineIndex === 0 && segmentLineRange.endLineIndex >= segmentLineRange.totalLineCount;
10761
- const measuredParagraphBand = segmentCoversWholeParagraph ? measuredBandAt(`para:${segment.nodeIndex}`) : void 0;
11220
+ const measuredParagraphBand = measuredBandAt(
11221
+ `para:${segment.nodeIndex}:${segmentLineRange?.startLineIndex ?? 0}`
11222
+ ) ?? (segmentCoversWholeParagraph ? measuredBandAt(`para:${segment.nodeIndex}`) : void 0);
10762
11223
  const flowTopPx = measuredParagraphBand?.topPx ?? flowTopPxBySegmentIndex[segmentIndex] ?? 0;
10763
11224
  const flowBottomPx = measuredParagraphBand?.bottomPx ?? flowTopPx + (flowHeightPxBySegmentIndex[segmentIndex] ?? 0);
10764
11225
  return resolveForeignWrapExclusionsForFlowRange(
@@ -11472,13 +11933,7 @@ function buildMeasureSegmentsPretextLayoutItems(segments, paragraphBaseFontPx, t
11472
11933
  }).filter((item) => item.endOffset > item.startOffset);
11473
11934
  }
11474
11935
  function layoutParagraphPretextSource(paragraph, source, containerWidthPx, lineHeightPx, exclusions) {
11475
- const fallbackFont = resolveMeasureFont(
11476
- firstRunStyle(paragraph),
11477
- paragraphBaseFontSizePx(paragraph),
11478
- source.text
11479
- );
11480
11936
  const wordBreak = pretextWordBreakModeForText(source.text);
11481
- const items = buildParagraphPretextLayoutItems(paragraph, source);
11482
11937
  const firstLineIndentPx = twipsToSignedPixels(
11483
11938
  paragraph.style?.indent?.firstLineTwips
11484
11939
  );
@@ -11494,6 +11949,23 @@ function layoutParagraphPretextSource(paragraph, source, containerWidthPx, lineH
11494
11949
  bottom: 1
11495
11950
  }
11496
11951
  ] : exclusions;
11952
+ const uniformFont = resolveUniformPretextSourceFont(paragraph, source);
11953
+ if (uniformFont) {
11954
+ return layoutTextWithPretextAroundExclusions(
11955
+ source.text,
11956
+ uniformFont,
11957
+ containerWidthPx,
11958
+ lineHeightPx,
11959
+ layoutExclusions,
11960
+ { wordBreak }
11961
+ );
11962
+ }
11963
+ const fallbackFont = resolveMeasureFont(
11964
+ firstRunStyle(paragraph),
11965
+ paragraphBaseFontSizePx(paragraph),
11966
+ source.text
11967
+ );
11968
+ const items = buildParagraphPretextLayoutItems(paragraph, source);
11497
11969
  const richLayout = items.length > 0 ? layoutItemsWithPretextAroundExclusions(
11498
11970
  source.text,
11499
11971
  items,
@@ -11629,19 +12101,20 @@ function resolveDualWrappedFloatingImageGeometry(image, containerWidthPx, option
11629
12101
  const paragraphLocalBaseTopPx = !hasExplicitBaseTopPx && paragraphTopPx !== void 0 && (normalizedVerticalRelativeTo === "margin" || normalizedVerticalRelativeTo === "page") ? Math.round(
11630
12102
  (normalizedVerticalRelativeTo === "page" ? baseTopPx - pageMarginTopPx : baseTopPx) - paragraphTopPx
11631
12103
  ) : baseTopPx;
11632
- const imageLeftPx = clampNumber(
11633
- baseLeftPx + deltaX,
11634
- 0,
11635
- Math.max(0, safeContainerWidthPx - imageWidthPx)
11636
- );
12104
+ const imageLeftPx = baseLeftPx + deltaX;
11637
12105
  const rawImageTopPx = (hasExplicitBaseTopPx ? baseTopPx : paragraphLocalBaseTopPx + (isInlinePreview ? 0 : distTPx)) + deltaY;
11638
12106
  const modelAllowsNegativeTop = Number.isFinite(floating?.yPx) && floating?.yPx < 0;
11639
12107
  const imageTopPx = options?.allowNegativeImageTop || modelAllowsNegativeTop ? Math.round(rawImageTopPx) : Math.max(0, Math.round(rawImageTopPx));
11640
12108
  const horizontalAlignIsOperative = !hasExplicitBaseLeftPx && !Number.isFinite(floating?.xPx);
11641
- let exclusionLeftPx = Math.max(0, imageLeftPx - distLPx);
11642
- let exclusionRightPx = Math.min(
11643
- safeContainerWidthPx,
11644
- imageLeftPx + imageWidthPx + distRPx
12109
+ let exclusionLeftPx = clampNumber(
12110
+ imageLeftPx - distLPx,
12111
+ 0,
12112
+ safeContainerWidthPx
12113
+ );
12114
+ let exclusionRightPx = clampNumber(
12115
+ imageLeftPx + imageWidthPx + distRPx,
12116
+ 0,
12117
+ safeContainerWidthPx
11645
12118
  );
11646
12119
  if (wrapType === "topAndBottom") {
11647
12120
  exclusionLeftPx = 0;
@@ -11657,11 +12130,10 @@ function resolveDualWrappedFloatingImageGeometry(image, containerWidthPx, option
11657
12130
  0,
11658
12131
  safeContainerWidthPx - exclusionRightPx
11659
12132
  );
11660
- const spansInteriorGap = exclusionLeftPx > 0 && exclusionRightPx < safeContainerWidthPx && leftBandWidthPx >= MIN_DUAL_WRAPPED_INTERIOR_BAND_PX && rightBandWidthPx >= MIN_DUAL_WRAPPED_INTERIOR_BAND_PX;
11661
- const spansSideFloat = exclusionLeftPx === 0 || exclusionRightPx === safeContainerWidthPx;
11662
- if (!spansInteriorGap && !spansSideFloat) {
11663
- return void 0;
11664
- }
12133
+ if (leftBandWidthPx < MIN_DUAL_WRAPPED_INTERIOR_BAND_PX)
12134
+ exclusionLeftPx = 0;
12135
+ if (rightBandWidthPx < MIN_DUAL_WRAPPED_INTERIOR_BAND_PX)
12136
+ exclusionRightPx = safeContainerWidthPx;
11665
12137
  }
11666
12138
  return {
11667
12139
  image,
@@ -11737,6 +12209,18 @@ function resolveParagraphDualWrappedTextLayout(paragraph, containerWidthPx, line
11737
12209
  }
11738
12210
  };
11739
12211
  }
12212
+ if (unexcludedLayout && verticalRelativeTo === "line" && anchorOffset > 0 && !options?.movePreviewByImageIndex?.has(geometry.imageIndex)) {
12213
+ const anchorTop = resolveCaretRectAtOffset(unexcludedLayout, anchorOffset + 1)?.top ?? 0;
12214
+ return {
12215
+ ...geometry,
12216
+ imageTopPx: geometry.imageTopPx + anchorTop,
12217
+ exclusion: {
12218
+ ...geometry.exclusion,
12219
+ top: geometry.exclusion.top + anchorTop,
12220
+ bottom: geometry.exclusion.bottom + anchorTop
12221
+ }
12222
+ };
12223
+ }
11740
12224
  if (!unexcludedLayout || wrapType === "topandbottom" || verticalRelativeTo !== "margin" && verticalRelativeTo !== "page") {
11741
12225
  return geometry;
11742
12226
  }
@@ -11764,7 +12248,7 @@ function resolveParagraphDualWrappedTextLayout(paragraph, containerWidthPx, line
11764
12248
  });
11765
12249
  const mergedExclusions = [
11766
12250
  ...options?.foreignExclusions ?? [],
11767
- ...anchorAdjustedGeometries.map((geometry) => geometry.exclusion)
12251
+ ...anchorAdjustedGeometries.filter((geometry) => geometry.imageIndex !== options?.excludedImageIndex).map((geometry) => geometry.exclusion)
11768
12252
  ];
11769
12253
  const layout = resolveParagraphPretextExclusionLayout(
11770
12254
  paragraph,
@@ -12152,13 +12636,17 @@ function paragraphBookmarkNames(paragraph) {
12152
12636
  );
12153
12637
  return [...new Set(names)];
12154
12638
  }
12639
+ var paragraphNoteReferences = /* @__PURE__ */ new WeakMap();
12155
12640
  function paragraphReferencedNoteIds(paragraph, noteType) {
12641
+ const cached = paragraphNoteReferences.get(paragraph);
12642
+ if (cached?.[noteType]) return cached[noteType];
12643
+ const references = [];
12644
+ paragraphNoteReferences.set(paragraph, { ...cached, [noteType]: references });
12156
12645
  const sourceXml = paragraph.sourceXml ?? "";
12157
12646
  if (!sourceXml) {
12158
- return [];
12647
+ return references;
12159
12648
  }
12160
12649
  const pattern = noteType === "footnote" ? new RegExp(FOOTNOTE_REFERENCE_XML_PATTERN.source, "gi") : new RegExp(ENDNOTE_REFERENCE_XML_PATTERN.source, "gi");
12161
- const references = [];
12162
12650
  for (const match of sourceXml.matchAll(pattern)) {
12163
12651
  const rawId = Number(match[1]);
12164
12652
  if (!Number.isFinite(rawId) || rawId < 0) {
@@ -12196,7 +12684,7 @@ function eventTargetIsInteractiveControl(target) {
12196
12684
  return false;
12197
12685
  }
12198
12686
  return Boolean(
12199
- target.closest("a[href],button,input,select,textarea,[role='checkbox']")
12687
+ target.closest("a[href],button,input,select,textarea,[role='checkbox'],[data-docx-textbox-editor='true']")
12200
12688
  );
12201
12689
  }
12202
12690
  function eventTargetIsNestedTableParagraphEditor(target) {
@@ -14383,6 +14871,111 @@ function paragraphCanSplitAcrossPages(paragraph, lineCount, options) {
14383
14871
  }
14384
14872
  return true;
14385
14873
  }
14874
+ function createTextEditPaginationMemo() {
14875
+ const entries = /* @__PURE__ */ new Map();
14876
+ const containsFloatingObjects = /* @__PURE__ */ new WeakMap();
14877
+ const hasFloatingObjects = (node) => {
14878
+ const cached = containsFloatingObjects.get(node);
14879
+ if (cached !== void 0) return cached;
14880
+ const result = node.type === "paragraph" ? node.children.some(
14881
+ (child) => child.type === "image" && child.floating
14882
+ ) : Boolean(node.style?.floating) || node.rows.some(
14883
+ (row) => row.cells.some((cell) => cell.nodes.some(hasFloatingObjects))
14884
+ );
14885
+ containsFloatingObjects.set(node, result);
14886
+ return result;
14887
+ };
14888
+ return (model, height, width, numberingDefinitions, metrics, options) => {
14889
+ if (model.nodes.some(hasFloatingObjects)) {
14890
+ return buildDocumentPageNodeSegments(
14891
+ model,
14892
+ height,
14893
+ width,
14894
+ numberingDefinitions,
14895
+ metrics,
14896
+ options
14897
+ );
14898
+ }
14899
+ const key = JSON.stringify([
14900
+ height,
14901
+ width,
14902
+ metrics,
14903
+ getFontMetricsRevision(),
14904
+ {
14905
+ ...options,
14906
+ measuredParagraphOuterHeightsPxByNodeIndex: options?.measuredParagraphOuterHeightsPxByNodeIndex ? [...options.measuredParagraphOuterHeightsPxByNodeIndex] : void 0,
14907
+ precomputedNumberingLabels: options?.precomputedNumberingLabels ? [...options.precomputedNumberingLabels] : void 0
14908
+ }
14909
+ ]);
14910
+ const cached = entries.get(key);
14911
+ const sameGeometry = cached && cached.model.metadata === model.metadata && cached.numberingDefinitions === numberingDefinitions && cached.model.nodes.length === model.nodes.length && model.nodes.every((node, index) => {
14912
+ const previous = cached.model.nodes[index];
14913
+ if (node === previous) return true;
14914
+ if (node.type !== "paragraph" || previous?.type !== "paragraph" || node.sourceXml !== previous.sourceXml || JSON.stringify(node.style) !== JSON.stringify(previous.style) || ![node, previous].every(
14915
+ (paragraph) => paragraph.children.every(
14916
+ (child) => child.type === "text" && !child.noteReference && !/[\r\n\t\f\v\u2028\u2029]/.test(child.text)
14917
+ )
14918
+ ) || paragraphHasOnlyWhitespaceText(node) !== paragraphHasOnlyWhitespaceText(previous)) {
14919
+ return false;
14920
+ }
14921
+ const source = buildParagraphPretextLayoutSource(node);
14922
+ const previousSource = buildParagraphPretextLayoutSource(previous);
14923
+ if (!source || !previousSource || !resolveUniformPretextSourceFont(node, source) || !resolveUniformPretextSourceFont(previous, previousSource))
14924
+ return false;
14925
+ const label = options?.precomputedNumberingLabels?.get(`p:${index}`);
14926
+ if (!label && paragraphHasActiveNumbering(node)) return false;
14927
+ const sectionIndex = resolvePaginationSectionMetricsIndexForNodeIndex(
14928
+ metrics ?? [],
14929
+ index,
14930
+ 0
14931
+ );
14932
+ const section = metrics?.[sectionIndex];
14933
+ const contentWidth = section?.pageContentWidthPx ?? width;
14934
+ const pitch = section?.docGridLinePitchPx;
14935
+ return estimateParagraphLineHeightPx(node, pitch) === estimateParagraphLineHeightPx(previous, pitch) && paragraphLineCountWithinWidth(
14936
+ node,
14937
+ contentWidth,
14938
+ numberingDefinitions,
14939
+ label
14940
+ ) === paragraphLineCountWithinWidth(
14941
+ previous,
14942
+ contentWidth,
14943
+ numberingDefinitions,
14944
+ label
14945
+ ) && estimateParagraphHeightPx(
14946
+ node,
14947
+ contentWidth,
14948
+ numberingDefinitions,
14949
+ pitch,
14950
+ false,
14951
+ label
14952
+ ) === estimateParagraphHeightPx(
14953
+ previous,
14954
+ contentWidth,
14955
+ numberingDefinitions,
14956
+ pitch,
14957
+ false,
14958
+ label
14959
+ );
14960
+ });
14961
+ if (sameGeometry && cached) {
14962
+ cached.model = model;
14963
+ return cached.pages;
14964
+ }
14965
+ const pages = buildDocumentPageNodeSegments(
14966
+ model,
14967
+ height,
14968
+ width,
14969
+ numberingDefinitions,
14970
+ metrics,
14971
+ options
14972
+ );
14973
+ if (entries.size >= 8 && !entries.has(key))
14974
+ entries.delete(entries.keys().next().value);
14975
+ entries.set(key, { model, numberingDefinitions, pages });
14976
+ return pages;
14977
+ };
14978
+ }
14386
14979
  function paragraphSegmentHasPartialLineRange(paragraphLineRange) {
14387
14980
  if (!paragraphLineRange) {
14388
14981
  return false;
@@ -15792,6 +16385,30 @@ function buildDocumentPageNodeSegments(model, pageContentHeightPx, pageContentWi
15792
16385
  if (currentPageSegments.length > 0 || pages.length === 0) {
15793
16386
  pages.push(currentPageSegments);
15794
16387
  }
16388
+ return keepTextAnchoredTablesWithFollowingParagraph(pages, model);
16389
+ }
16390
+ function keepTextAnchoredTablesWithFollowingParagraph(pages, model) {
16391
+ for (let pageIndex = 0; pageIndex < pages.length - 1; pageIndex++) {
16392
+ const current = pages[pageIndex];
16393
+ const next = pages[pageIndex + 1];
16394
+ const first = next[0];
16395
+ const paragraph = first && model.nodes[first.nodeIndex];
16396
+ if (paragraph?.type !== "paragraph" || (first.paragraphLineRange?.startLineIndex ?? 0) > 0 || model.metadata.sections?.some(
16397
+ (section) => section.startNodeIndex === first.nodeIndex
16398
+ ))
16399
+ continue;
16400
+ while (current.length) {
16401
+ const last = current[current.length - 1];
16402
+ const table = model.nodes[last.nodeIndex];
16403
+ const anchor = table?.type === "table" ? table.style?.floating?.verticalAnchor : void 0;
16404
+ if (anchor !== "text" || last.nodeIndex + 1 !== next[0].nodeIndex) break;
16405
+ next.unshift(current.pop());
16406
+ }
16407
+ if (!current.length) {
16408
+ pages.splice(pageIndex, 1);
16409
+ pageIndex--;
16410
+ }
16411
+ }
15795
16412
  return pages;
15796
16413
  }
15797
16414
  function shouldRenderWrappedFloatingImage(image) {
@@ -16104,7 +16721,7 @@ function resolveWrappedFloatingImageDropPatch(image, hostWidth, movedLeft, moved
16104
16721
  );
16105
16722
  const previewUsesSideFloat = previewGeometry === void 0 || previewGeometry.exclusion.left <= 0 || previewGeometry.exclusion.right >= hostWidth;
16106
16723
  const explicitTopPx = Math.round(
16107
- movedTop - Math.round(baseFloating.distTPx ?? 0)
16724
+ movedTop - Math.round(baseFloating.distTPx ?? 2)
16108
16725
  );
16109
16726
  return {
16110
16727
  wrapType,
@@ -16112,8 +16729,8 @@ function resolveWrappedFloatingImageDropPatch(image, hostWidth, movedLeft, moved
16112
16729
  horizontalAlign: preserveAlignedHorizontalPlacement ? baseFloating.horizontalAlign : previewUsesSideFloat ? side : wrapText === "bothSides" || wrapText === "largest" ? void 0 : side,
16113
16730
  xPx: preserveAlignedHorizontalPlacement ? void 0 : Math.round(movedLeft),
16114
16731
  yPx: explicitTopPx,
16115
- distLPx: Math.max(4, Math.round(baseFloating.distLPx ?? 8)),
16116
- distRPx: Math.max(4, Math.round(baseFloating.distRPx ?? 8)),
16732
+ distLPx: Math.max(0, Math.round(baseFloating.distLPx ?? 8)),
16733
+ distRPx: Math.max(0, Math.round(baseFloating.distRPx ?? 8)),
16117
16734
  distTPx: Math.max(0, Math.round(baseFloating.distTPx ?? 2)),
16118
16735
  distBPx: Math.max(0, Math.round(baseFloating.distBPx ?? 4)),
16119
16736
  horizontalRelativeTo: convertFixedPositionToMoveWithText ? "column" : baseFloating.horizontalRelativeTo ?? "column",
@@ -16155,13 +16772,6 @@ function resolveWrappedDropAnchorParagraphHost(wrapperElement, droppedTopScreenP
16155
16772
  }
16156
16773
  return best;
16157
16774
  }
16158
- function wrappedDragCanReanchorToParagraph(floating) {
16159
- if (!floatingImageMovesWithText(floating)) {
16160
- return true;
16161
- }
16162
- const verticalRelativeTo = floating.verticalRelativeTo?.trim().toLowerCase() ?? "";
16163
- return verticalRelativeTo === "" || verticalRelativeTo === "paragraph" || verticalRelativeTo === "line";
16164
- }
16165
16775
  function resolvePageSpanningAbsoluteFloatingDimensions(image, widthPx, heightPx, floatingPageOriginPx) {
16166
16776
  const floating = image.floating;
16167
16777
  const pageWidthPx = Number.isFinite(floatingPageOriginPx?.pageWidth) ? Math.max(1, Math.round(floatingPageOriginPx?.pageWidth)) : void 0;
@@ -18409,6 +19019,7 @@ function paragraphStyleRefFieldValueSequence(paragraph) {
18409
19019
  function normalizeStyleRefTarget(value) {
18410
19020
  return value.replace(/\s+/g, " ").trim().toLowerCase();
18411
19021
  }
19022
+ var styledRunTextByParagraph = /* @__PURE__ */ new WeakMap();
18412
19023
  function paragraphStyledRunText(paragraph, styleRefTarget) {
18413
19024
  const xml = paragraph.sourceXml ?? "";
18414
19025
  if (!xml) {
@@ -18418,6 +19029,12 @@ function paragraphStyledRunText(paragraph, styleRefTarget) {
18418
19029
  if (!normalizedTarget) {
18419
19030
  return void 0;
18420
19031
  }
19032
+ let cached = styledRunTextByParagraph.get(paragraph);
19033
+ if (cached?.has(normalizedTarget)) return cached.get(normalizedTarget);
19034
+ if (!cached) {
19035
+ cached = /* @__PURE__ */ new Map();
19036
+ styledRunTextByParagraph.set(paragraph, cached);
19037
+ }
18421
19038
  const textChunks = [];
18422
19039
  for (const runMatch of xml.matchAll(/<w:r\b[\s\S]*?<\/w:r>/gi)) {
18423
19040
  const runXml = runMatch[0] ?? "";
@@ -18442,7 +19059,9 @@ function paragraphStyledRunText(paragraph, styleRefTarget) {
18442
19059
  }
18443
19060
  }
18444
19061
  const joined = textChunks.join("").trim();
18445
- return joined.length > 0 ? joined : void 0;
19062
+ const result = joined.length > 0 ? joined : void 0;
19063
+ setCacheEntry(cached, normalizedTarget, result);
19064
+ return result;
18446
19065
  }
18447
19066
  function tabLeaderStyle(leader, color) {
18448
19067
  const normalizedLeader = leader === "middleDot" ? "dot" : leader;
@@ -22011,45 +22630,6 @@ function imageLocationKey(location) {
22011
22630
  function dropTargetKey(target) {
22012
22631
  return `${paragraphLocationKey(target)}:${target.childIndex}`;
22013
22632
  }
22014
- function parseImageDropTargetFromDataset(dataset) {
22015
- const kind = dataset.docxTargetKind;
22016
- const childIndex = Number.parseInt(dataset.docxChildIndex ?? "", 10);
22017
- if (!Number.isFinite(childIndex) || childIndex < 0) {
22018
- return void 0;
22019
- }
22020
- if (kind === "paragraph") {
22021
- const nodeIndex = Number.parseInt(dataset.docxNodeIndex ?? "", 10);
22022
- if (!Number.isFinite(nodeIndex) || nodeIndex < 0) {
22023
- return void 0;
22024
- }
22025
- return {
22026
- kind: "paragraph",
22027
- nodeIndex,
22028
- childIndex
22029
- };
22030
- }
22031
- if (kind === "table-cell") {
22032
- const tableIndex = Number.parseInt(dataset.docxTableIndex ?? "", 10);
22033
- const rowIndex = Number.parseInt(dataset.docxRowIndex ?? "", 10);
22034
- const cellIndex = Number.parseInt(dataset.docxCellIndex ?? "", 10);
22035
- const paragraphIndex = Number.parseInt(
22036
- dataset.docxParagraphIndex ?? "",
22037
- 10
22038
- );
22039
- if (!Number.isFinite(tableIndex) || !Number.isFinite(rowIndex) || !Number.isFinite(cellIndex) || !Number.isFinite(paragraphIndex) || tableIndex < 0 || rowIndex < 0 || cellIndex < 0 || paragraphIndex < 0) {
22040
- return void 0;
22041
- }
22042
- return {
22043
- kind: "table-cell",
22044
- tableIndex,
22045
- rowIndex,
22046
- cellIndex,
22047
- paragraphIndex,
22048
- childIndex
22049
- };
22050
- }
22051
- return void 0;
22052
- }
22053
22633
  function firstTableCellAnchorLocation(table, tableIndex) {
22054
22634
  for (let rowIndex = 0; rowIndex < table.rows.length; rowIndex += 1) {
22055
22635
  const row = table.rows[rowIndex];
@@ -23736,19 +24316,18 @@ function updateSectionImageFloatingAtLocation(model, location, patch) {
23736
24316
  return changed ? next : model;
23737
24317
  }
23738
24318
  function useDocxEditor(options = {}) {
23739
- const starterTemplateRef = React.useRef(
23740
- cloneDocModelWithBlockIds(options.starterModel ?? defaultStarterModel)
24319
+ const [starterTemplate] = React.useState(
24320
+ () => cloneDocModelWithBlockIds(options.starterModel ?? defaultStarterModel)
23741
24321
  );
23742
- const initialModelRef = React.useRef(
23743
- cloneDocModelWithBlockIds(
23744
- options.document?.model ?? starterTemplateRef.current
23745
- )
24322
+ const starterTemplateRef = React.useRef(starterTemplate);
24323
+ const [initialModel] = React.useState(
24324
+ () => cloneDocModelWithBlockIds(options.document?.model ?? starterTemplate)
23746
24325
  );
23747
24326
  const [editorState, dispatchCanonicalEditorState] = React.useReducer(
23748
24327
  reduceDocxEditorState,
23749
- initialModelRef.current,
23750
- (initialModel) => ({
23751
- model: cloneDocModelWithBlockIds(initialModel),
24328
+ initialModel,
24329
+ (initialModel2) => ({
24330
+ model: cloneDocModelWithBlockIds(initialModel2),
23752
24331
  selection: {
23753
24332
  kind: "paragraph",
23754
24333
  nodeIndex: 0
@@ -23950,8 +24529,10 @@ function useDocxEditor(options = {}) {
23950
24529
  window.clearTimeout(selectionSessionTimeoutRef.current);
23951
24530
  }
23952
24531
  selectionSessionTimeoutRef.current = null;
23953
- selectionSessionRef.current = "idle";
23954
- setSelectionSessionKind("idle");
24532
+ if (selectionSessionRef.current !== "idle") {
24533
+ selectionSessionRef.current = "idle";
24534
+ setSelectionSessionKind("idle");
24535
+ }
23955
24536
  },
23956
24537
  []
23957
24538
  );
@@ -23961,8 +24542,10 @@ function useDocxEditor(options = {}) {
23961
24542
  window.clearTimeout(selectionSessionTimeoutRef.current);
23962
24543
  }
23963
24544
  selectionSessionTimeoutRef.current = null;
23964
- selectionSessionRef.current = kind;
23965
- setSelectionSessionKind(kind);
24545
+ if (selectionSessionRef.current !== kind) {
24546
+ selectionSessionRef.current = kind;
24547
+ setSelectionSessionKind(kind);
24548
+ }
23966
24549
  if (Number.isFinite(options2?.settleAfterMs) && options2?.settleAfterMs > 0 && typeof window !== "undefined") {
23967
24550
  const expectedKind = kind;
23968
24551
  selectionSessionTimeoutRef.current = window.setTimeout(() => {
@@ -24211,6 +24794,9 @@ function useDocxEditor(options = {}) {
24211
24794
  }
24212
24795
  });
24213
24796
  }
24797
+ if (loadedEmbeddedFontFacesRef.current.length > 0) {
24798
+ invalidateFontMetrics();
24799
+ }
24214
24800
  loadedEmbeddedFontFacesRef.current = [];
24215
24801
  }, []);
24216
24802
  const loadEmbeddedFontsFromPackage = React.useCallback(
@@ -24250,6 +24836,9 @@ function useDocxEditor(options = {}) {
24250
24836
  document.fonts.add(fontFace);
24251
24837
  });
24252
24838
  loadedEmbeddedFontFacesRef.current = loadedFontFaces;
24839
+ if (loadedFontFaces.length > 0) {
24840
+ invalidateFontMetrics();
24841
+ }
24253
24842
  try {
24254
24843
  await document.fonts.ready;
24255
24844
  } catch {
@@ -24353,7 +24942,8 @@ function useDocxEditor(options = {}) {
24353
24942
  setHistoryRestoreRequest({
24354
24943
  nonce: nextNonce,
24355
24944
  selection: cloneEditorSelection(next.selection),
24356
- activeTextRange: cloneTextRange(next.activeTextRange)
24945
+ activeTextRange: cloneTextRange(next.activeTextRange),
24946
+ textInput: patch.textInput
24357
24947
  });
24358
24948
  }
24359
24949
  if (patch.clearSelectedFormField) {
@@ -24637,7 +25227,8 @@ function useDocxEditor(options = {}) {
24637
25227
  markDocxImportPerformance(workerStartMark);
24638
25228
  const importResult = await importDocxBuffer(buffer, {
24639
25229
  signal: importAbortController.signal,
24640
- transferBuffer: true
25230
+ transferBuffer: true,
25231
+ useWorker: "required"
24641
25232
  });
24642
25233
  markDocxImportPerformance(workerEndMark);
24643
25234
  measureDocxImportPerformance(
@@ -26710,7 +27301,16 @@ function useDocxEditor(options = {}) {
26710
27301
  const moveFloatingImage = React.useCallback(
26711
27302
  (location, patch, options2) => {
26712
27303
  applyModelChange((current) => {
26713
- const next = cloneDocModel(current);
27304
+ const sourceLocation = imageLocationToParagraphLocation(location);
27305
+ const next = copyModelForParagraphEdits(current, [
27306
+ sourceLocation,
27307
+ ...options2?.reparentToParagraphNodeIndex !== void 0 ? [
27308
+ {
27309
+ kind: "paragraph",
27310
+ nodeIndex: options2.reparentToParagraphNodeIndex
27311
+ }
27312
+ ] : []
27313
+ ]);
26714
27314
  const { paragraph, tableNode } = getParagraphAtLocation(
26715
27315
  next,
26716
27316
  imageLocationToParagraphLocation(location)
@@ -26718,22 +27318,32 @@ function useDocxEditor(options = {}) {
26718
27318
  if (!paragraph) {
26719
27319
  return current;
26720
27320
  }
26721
- const child = paragraph.children[location.childIndex];
26722
- if (!child || child.type !== "image") {
27321
+ const sourceChild = paragraph.children[location.childIndex];
27322
+ if (!sourceChild || sourceChild.type !== "image") {
26723
27323
  return current;
26724
27324
  }
26725
- child.floating = {
26726
- ...child.floating ?? {},
26727
- ...patch
27325
+ const child = {
27326
+ ...sourceChild,
27327
+ floating: { ...sourceChild.floating, ...patch }
26728
27328
  };
27329
+ paragraph.children[location.childIndex] = child;
26729
27330
  const reparentNodeIndex = options2?.reparentToParagraphNodeIndex;
26730
27331
  const reparentTarget = reparentNodeIndex !== void 0 ? next.nodes[reparentNodeIndex] : void 0;
26731
- if (reparentTarget && reparentTarget.type === "paragraph" && reparentTarget !== paragraph) {
27332
+ if (reparentTarget?.type === "paragraph" && (reparentTarget !== paragraph || options2?.anchorTextOffset !== void 0)) {
26732
27333
  paragraph.children.splice(location.childIndex, 1);
26733
- if (paragraph.children.length === 0) {
27334
+ const target = imageDropPositionAtTextOffset(
27335
+ reparentTarget,
27336
+ options2?.anchorTextOffset ?? 0,
27337
+ { fieldText: formFieldDisplayValue2 }
27338
+ );
27339
+ insertImageAtDropPosition(
27340
+ reparentTarget,
27341
+ child,
27342
+ target.childIndex,
27343
+ target.textOffset
27344
+ );
27345
+ if (paragraph.children.length === 0)
26734
27346
  paragraph.children.push({ type: "text", text: "" });
26735
- }
26736
- reparentTarget.children.unshift(child);
26737
27347
  reparentTarget.sourceXml = void 0;
26738
27348
  }
26739
27349
  paragraph.sourceXml = void 0;
@@ -26867,11 +27477,14 @@ function useDocxEditor(options = {}) {
26867
27477
  if (sameParagraphLocation(
26868
27478
  sourceParagraphLocation,
26869
27479
  targetParagraphLocation
26870
- ) && (target.childIndex === source.childIndex || target.childIndex === source.childIndex + 1)) {
27480
+ ) && target.textOffset === void 0 && (target.childIndex === source.childIndex || target.childIndex === source.childIndex + 1)) {
26871
27481
  return;
26872
27482
  }
26873
27483
  applyModelChange((current) => {
26874
- const next = cloneDocModel(current);
27484
+ const next = copyModelForParagraphEdits(current, [
27485
+ sourceParagraphLocation,
27486
+ targetParagraphLocation
27487
+ ]);
26875
27488
  const sourceLookup = getParagraphAtLocation(
26876
27489
  next,
26877
27490
  sourceParagraphLocation
@@ -26891,21 +27504,19 @@ function useDocxEditor(options = {}) {
26891
27504
  }
26892
27505
  const movedImage = {
26893
27506
  ...sourceChild,
26894
- data: sourceChild.data ? new Uint8Array(sourceChild.data) : void 0,
26895
27507
  floating: sourceChild.floating ? { ...sourceChild.floating } : void 0
26896
27508
  };
26897
27509
  sourceParagraph.children.splice(source.childIndex, 1);
26898
- let insertionIndex = Math.max(
26899
- 0,
26900
- Math.min(target.childIndex, targetParagraph.children.length)
26901
- );
26902
- if (sameParagraphLocation(
27510
+ const insertionIndex = target.childIndex - (sameParagraphLocation(
26903
27511
  sourceParagraphLocation,
26904
27512
  targetParagraphLocation
26905
- ) && source.childIndex < insertionIndex) {
26906
- insertionIndex -= 1;
26907
- }
26908
- targetParagraph.children.splice(insertionIndex, 0, movedImage);
27513
+ ) && source.childIndex < target.childIndex ? 1 : 0);
27514
+ insertImageAtDropPosition(
27515
+ targetParagraph,
27516
+ movedImage,
27517
+ insertionIndex,
27518
+ target.textOffset
27519
+ );
26909
27520
  if (sourceParagraph.children.length === 0) {
26910
27521
  sourceParagraph.children.push({ type: "text", text: "" });
26911
27522
  }
@@ -27166,11 +27777,12 @@ function useDocxEditor(options = {}) {
27166
27777
  if (!tableNode || tableNode.type !== "table") {
27167
27778
  return current;
27168
27779
  }
27169
- const next = cloneDocModel(current);
27170
- const nextTable = next.nodes[tableIndex];
27171
- if (!nextTable || nextTable.type !== "table") {
27172
- return current;
27173
- }
27780
+ const next = copyModelForParagraphEdits(
27781
+ current,
27782
+ options2?.moveToNodeIndex !== void 0 ? [{ kind: "paragraph", nodeIndex: options2.moveToNodeIndex }] : []
27783
+ );
27784
+ const nextTable = { ...tableNode };
27785
+ next.nodes[tableIndex] = nextTable;
27174
27786
  nextTable.style = {
27175
27787
  ...nextTable.style ?? {},
27176
27788
  floating: {
@@ -27179,25 +27791,43 @@ function useDocxEditor(options = {}) {
27179
27791
  }
27180
27792
  };
27181
27793
  nextTable.sourceXml = void 0;
27182
- const moveToNodeIndex = options2?.moveToNodeIndex;
27183
- if (moveToNodeIndex !== void 0 && moveToNodeIndex !== tableIndex && moveToNodeIndex !== tableIndex + 1) {
27184
- const clampedTarget = clampNumber(
27185
- moveToNodeIndex,
27794
+ let moveToNodeIndex = options2?.moveToNodeIndex;
27795
+ const anchorParagraph = moveToNodeIndex !== void 0 ? next.nodes[moveToNodeIndex] : void 0;
27796
+ if (anchorParagraph?.type === "paragraph" && options2?.anchorTextOffset !== void 0 && options2.anchorTextOffset > 0) {
27797
+ const split = splitParagraphChildrenAtTextOffsets(
27798
+ anchorParagraph,
27799
+ paragraphText(anchorParagraph),
27800
+ options2.anchorTextOffset,
27801
+ options2.anchorTextOffset
27802
+ );
27803
+ const after = {
27804
+ ...anchorParagraph,
27805
+ blockId: allocateBlockId(),
27806
+ children: split.afterChildren,
27807
+ sourceXml: void 0,
27808
+ style: {
27809
+ ...anchorParagraph.style,
27810
+ spacing: { ...anchorParagraph.style?.spacing, beforeTwips: 0 }
27811
+ }
27812
+ };
27813
+ anchorParagraph.children = split.beforeChildren;
27814
+ anchorParagraph.sourceXml = void 0;
27815
+ anchorParagraph.style = {
27816
+ ...anchorParagraph.style,
27817
+ spacing: { ...anchorParagraph.style?.spacing, afterTwips: 0 }
27818
+ };
27819
+ moveToNodeIndex = moveToNodeIndex + 1;
27820
+ next.nodes.splice(moveToNodeIndex, 0, after);
27821
+ }
27822
+ const sourceIndex = next.nodes.indexOf(nextTable);
27823
+ if (moveToNodeIndex !== void 0 && moveToNodeIndex !== sourceIndex && moveToNodeIndex !== sourceIndex + 1) {
27824
+ const target = clampNumber(moveToNodeIndex, 0, next.nodes.length);
27825
+ next.nodes.splice(sourceIndex, 1);
27826
+ next.nodes.splice(
27827
+ target > sourceIndex ? target - 1 : target,
27186
27828
  0,
27187
- next.nodes.length
27829
+ nextTable
27188
27830
  );
27189
- const extracted = next.nodes.splice(tableIndex, 1)[0];
27190
- if (extracted) {
27191
- next.nodes.splice(
27192
- clampNumber(
27193
- clampedTarget > tableIndex ? clampedTarget - 1 : clampedTarget,
27194
- 0,
27195
- next.nodes.length
27196
- ),
27197
- 0,
27198
- extracted
27199
- );
27200
- }
27201
27831
  }
27202
27832
  return next;
27203
27833
  }, "Moved table");
@@ -27693,7 +28323,8 @@ function useDocxEditor(options = {}) {
27693
28323
  }
27694
28324
  },
27695
28325
  pushHistory: !coalesce,
27696
- clearSelectedFormField: true
28326
+ clearSelectedFormField: true,
28327
+ textInput: true
27697
28328
  };
27698
28329
  });
27699
28330
  },
@@ -27745,7 +28376,8 @@ function useDocxEditor(options = {}) {
27745
28376
  offset: caret.end
27746
28377
  }
27747
28378
  },
27748
- clearSelectedFormField: true
28379
+ clearSelectedFormField: true,
28380
+ textInput: true
27749
28381
  };
27750
28382
  });
27751
28383
  },
@@ -28652,11 +29284,16 @@ function useDocxPageThumbnails(editor, options = {}) {
28652
29284
  () => ensureDocxViewerPageSurfaceRegistry(pageSurfaceRegistryEditor),
28653
29285
  [pageSurfaceRegistryEditor]
28654
29286
  );
29287
+ const hasRequestedThumbnails = options.pageIndexes?.length !== 0;
28655
29288
  React.useEffect(
28656
- () => subscribeDocxViewerPageSurfaces(pageSurfaceRegistryEditor, () => {
29289
+ () => {
29290
+ if (!hasRequestedThumbnails) return;
28657
29291
  setPageSurfaceEpoch((current) => current + 1);
28658
- }),
28659
- [pageSurfaceRegistryEditor]
29292
+ return subscribeDocxViewerPageSurfaces(pageSurfaceRegistryEditor, () => {
29293
+ setPageSurfaceEpoch((current) => current + 1);
29294
+ });
29295
+ },
29296
+ [hasRequestedThumbnails, pageSurfaceRegistryEditor]
28660
29297
  );
28661
29298
  const mountedPageElements = React.useMemo(
28662
29299
  () => new Map(pageSurfaceRegistry.pageElements),
@@ -30360,6 +30997,11 @@ function DocxEditorViewer({
30360
30997
  onFormFieldDoubleClick,
30361
30998
  mode = "edit"
30362
30999
  }) {
31000
+ const fontMetricsRevision = React.useSyncExternalStore(
31001
+ subscribeFontMetrics,
31002
+ getFontMetricsRevision,
31003
+ getServerFontMetricsRevision
31004
+ );
30363
31005
  const pageSurfaceRegistryOwner = docxViewerPageSurfaceRegistryOwner(editor);
30364
31006
  const trackedChangesEnabled = showTrackedChanges ?? editor.showTrackedChanges;
30365
31007
  const hasTrackedChanges = editor.trackedChanges.length > 0;
@@ -30399,6 +31041,9 @@ function DocxEditorViewer({
30399
31041
  );
30400
31042
  const viewerRootRef = React.useRef(null);
30401
31043
  const tableCellDraftsRef = React.useRef(/* @__PURE__ */ new Map());
31044
+ const plainParagraphHtmlCacheRef = React.useRef(
31045
+ /* @__PURE__ */ new WeakMap()
31046
+ );
30402
31047
  const editableParagraphHtmlPropsRef = React.useRef(/* @__PURE__ */ new Map());
30403
31048
  const editableTableCellHtmlPropsRef = React.useRef(/* @__PURE__ */ new Map());
30404
31049
  const tableCellParagraphDraftsRef = React.useRef(
@@ -30693,7 +31338,9 @@ function DocxEditorViewer({
30693
31338
  const suppressNextTableCellClickRef = React.useRef(false);
30694
31339
  const [tableCellSelectionRange, setTableCellSelectionRange] = React.useState();
30695
31340
  const [tableMoveDropPreview, setTableMoveDropPreview] = React.useState();
30696
- const [tableFloatingDragPreview, setTableFloatingDragPreview] = React.useState();
31341
+ const [objectWrapDragPreview, setObjectWrapDragPreview] = React.useState();
31342
+ const objectDragCleanupRef = React.useRef(void 0);
31343
+ React.useEffect(() => () => objectDragCleanupRef.current?.(), [editor.documentLoadNonce, editor.model, isReadOnly]);
30697
31344
  const [tableContextMenuState, setTableContextMenuState] = React.useState();
30698
31345
  const [contextMenuState, setContextMenuState] = React.useState();
30699
31346
  const [activeHeaderFooterEdit, setActiveHeaderFooterEdit] = React.useState();
@@ -30738,7 +31385,6 @@ function DocxEditorViewer({
30738
31385
  if (typeof window === "undefined") {
30739
31386
  return;
30740
31387
  }
30741
- setPostImportPaginationUnlocked(true);
30742
31388
  const safeDelayMs = Math.max(
30743
31389
  48,
30744
31390
  Math.round(delayMs ?? PAGINATION_MEASUREMENT_INTERACTION_DEBOUNCE_MS)
@@ -30749,6 +31395,7 @@ function DocxEditorViewer({
30749
31395
  }
30750
31396
  paginationMeasurementResumeTimeoutRef.current = window.setTimeout(() => {
30751
31397
  paginationMeasurementResumeTimeoutRef.current = null;
31398
+ setPostImportPaginationUnlocked(true);
30752
31399
  bumpPaginationMeasurementEpoch();
30753
31400
  }, safeDelayMs + 16);
30754
31401
  },
@@ -30828,7 +31475,6 @@ function DocxEditorViewer({
30828
31475
  setHoveredEmbeddedTableKey(void 0);
30829
31476
  setFocusedEmbeddedTableKey(void 0);
30830
31477
  setTableMoveDropPreview(void 0);
30831
- setTableFloatingDragPreview(void 0);
30832
31478
  tableMoveDragRef.current = void 0;
30833
31479
  setTableContextMenuState(void 0);
30834
31480
  setContextMenuState(void 0);
@@ -30949,10 +31595,26 @@ function DocxEditorViewer({
30949
31595
  Math.round(documentLayout.marginsPx.left),
30950
31596
  Math.round(documentLayout.marginsPx.right),
30951
31597
  trackedChangesEnabled ? "tc1" : "tc0",
31598
+ fontMetricsRevision,
30952
31599
  docNodeContentSignature(paginationSectionMetrics)
30953
31600
  ].join("|"),
30954
- [documentLayout, paginationSectionMetrics, trackedChangesEnabled]
31601
+ [
31602
+ documentLayout,
31603
+ fontMetricsRevision,
31604
+ paginationSectionMetrics,
31605
+ trackedChangesEnabled
31606
+ ]
30955
31607
  );
31608
+ React.useEffect(() => {
31609
+ setMeasuredPageContentHeightByIndex((current) => current.length ? [] : current);
31610
+ setMeasuredPageContentIdentityKeysByIndex(
31611
+ (current) => current.length ? [] : current
31612
+ );
31613
+ setMeasuredPageContentValidationByIndex(
31614
+ (current) => current.length ? [] : current
31615
+ );
31616
+ setHasMeasuredBodyFooterOverlap(false);
31617
+ }, [fontMetricsRevision]);
30956
31618
  React.useEffect(() => {
30957
31619
  setMeasuredParagraphOuterHeightsPxByBlockId(
30958
31620
  (current) => current.size === 0 ? current : /* @__PURE__ */ new Map()
@@ -31011,7 +31673,7 @@ function DocxEditorViewer({
31011
31673
  pageContentWidthPxByNodeIndex: widthByNodeIndex,
31012
31674
  pageContentHeightPxByNodeIndex: heightByNodeIndex
31013
31675
  };
31014
- }, [editor.model.nodes, paginationSectionMetrics]);
31676
+ }, [editor.model.nodes.length, paginationSectionMetrics]);
31015
31677
  const sectionColumnsBySectionIndex = React.useMemo(
31016
31678
  () => documentSections.map(
31017
31679
  (section) => parseSectionColumns(section.sectionPropertiesXml)
@@ -31022,6 +31684,23 @@ function DocxEditorViewer({
31022
31684
  () => buildParagraphNumberingLabels(editor.model),
31023
31685
  [editor.model]
31024
31686
  );
31687
+ const noteMarkerIndexes = React.useMemo(
31688
+ () => ({
31689
+ footnote: new Map(
31690
+ editor.model.metadata.footnotes?.map((note, index) => [
31691
+ note.id,
31692
+ index + 1
31693
+ ])
31694
+ ),
31695
+ endnote: new Map(
31696
+ editor.model.metadata.endnotes?.map((note, index) => [
31697
+ note.id,
31698
+ index + 1
31699
+ ])
31700
+ )
31701
+ }),
31702
+ [editor.model.metadata.footnotes, editor.model.metadata.endnotes]
31703
+ );
31025
31704
  const bookmarkTargetNodeIndexByName = React.useMemo(() => {
31026
31705
  const targets = /* @__PURE__ */ new Map();
31027
31706
  editor.model.nodes.forEach((node, nodeIndex) => {
@@ -31078,6 +31757,9 @@ function DocxEditorViewer({
31078
31757
  tableDraftLayoutEpoch,
31079
31758
  tableMeasuredRowHeights
31080
31759
  ]);
31760
+ const textEditPaginationMemo = React.useRef(null);
31761
+ if (!textEditPaginationMemo.current)
31762
+ textEditPaginationMemo.current = createTextEditPaginationMemo();
31081
31763
  const pageSegmentationPlan = React.useMemo(() => {
31082
31764
  const pageContentHeightPx = Math.max(
31083
31765
  120,
@@ -31098,7 +31780,7 @@ function DocxEditorViewer({
31098
31780
  const canUseMeasuredPageContentHeights = !disableMeasuredImportPagination && !floatingMovePreview && !dropCapMovePreview && (measuredPageContentHeightsPxForPagination?.length ?? 0) > 0;
31099
31781
  const buildEstimatedPagesForInputs = createEstimatedPagesBuildMemo(
31100
31782
  (measuredTableRowHeightsByNodeIndex, measuredPageContentHeightsPx, strictLastRenderedParagraphStartBreaks) => {
31101
- const buildPagesWithHeights = (resolvedPageContentHeights) => buildDocumentPageNodeSegments(
31783
+ const buildPagesWithHeights = (resolvedPageContentHeights) => textEditPaginationMemo.current(
31102
31784
  editor.model,
31103
31785
  pageContentHeightPx,
31104
31786
  pageContentWidthPx,
@@ -31913,7 +32595,7 @@ function DocxEditorViewer({
31913
32595
  [explicitPageVirtualizationZoomScale]
31914
32596
  );
31915
32597
  React.useEffect(() => {
31916
- if (floatingMovePreview || resizePreview || tableFloatingDragPreview || tableMoveDragRef.current) {
32598
+ if (floatingMovePreview || objectWrapDragPreview || resizePreview || tableMoveDragRef.current) {
31917
32599
  return;
31918
32600
  }
31919
32601
  const modelChanged = wrapBandReconcileModelRef.current !== editor.model;
@@ -31951,6 +32633,8 @@ function DocxEditorViewer({
31951
32633
  }
31952
32634
  pageHasFloatingObject = true;
31953
32635
  fresh.set(`tbl:${nodeIndex}`, toBand(element.getBoundingClientRect()));
32636
+ const host = element.closest("[data-docx-floating-table-host]");
32637
+ if (host) fresh.set(`tblanchor:${nodeIndex}`, toBand(host.getBoundingClientRect()));
31954
32638
  });
31955
32639
  bodyElement.querySelectorAll("[data-docx-image-location]").forEach((span) => {
31956
32640
  if (!(span instanceof HTMLElement)) {
@@ -32001,6 +32685,10 @@ function DocxEditorViewer({
32001
32685
  }
32002
32686
  fresh.set(`para:${nodeIndex}`, toBand(rect));
32003
32687
  });
32688
+ bodyElement.querySelectorAll("[data-docx-paragraph-host='true'][data-docx-paragraph-kind='paragraph']").forEach((element) => {
32689
+ const rect = element.getBoundingClientRect();
32690
+ if (rect.height > 0) fresh.set(`para:${element.dataset.docxParagraphNodeIndex}:${element.dataset.docxParagraphStartLine ?? 0}`, toBand(rect));
32691
+ });
32004
32692
  });
32005
32693
  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;
32006
32694
  const next = /* @__PURE__ */ new Map();
@@ -32153,11 +32841,18 @@ function DocxEditorViewer({
32153
32841
  overscan: pageVirtualizationOverscan
32154
32842
  });
32155
32843
  const internalPageVirtualizer = internalVirtualScrollUsesWindow ? internalWindowPageVirtualizer : internalElementPageVirtualizer;
32844
+ const virtualPageHeightSignature = pageSectionInfoByIndex.map(
32845
+ ({ layout }) => Math.round(
32846
+ (layout.pageHeightPx + DOC_PAGE_BREAK_GAP) * virtualizerMeasurementScale
32847
+ )
32848
+ ).join("|");
32156
32849
  React.useLayoutEffect(() => {
32157
32850
  internalElementPageVirtualizer.measure();
32158
32851
  internalWindowPageVirtualizer.measure();
32159
32852
  }, [
32160
- estimateVirtualPageSize,
32853
+ virtualPageHeightSignature,
32854
+ documentLayout.pageHeightPx,
32855
+ virtualizerMeasurementScale,
32161
32856
  internalElementPageVirtualizer,
32162
32857
  internalWindowPageVirtualizer
32163
32858
  ]);
@@ -33818,16 +34513,126 @@ function DocxEditorViewer({
33818
34513
  },
33819
34514
  [editor.model.nodes.length]
33820
34515
  );
34516
+ const objectWrapDragSignatureRef = React.useRef("");
34517
+ const objectWrapDragLatestRef = React.useRef(void 0);
34518
+ const updateObjectWrapDragPreview = React.useCallback(
34519
+ (frame, source, distances) => {
34520
+ const bandsByPage = /* @__PURE__ */ new Map();
34521
+ let pageIndex = -1;
34522
+ let closestDistance = Infinity;
34523
+ let obstacle;
34524
+ pageBodyElementsRef.current.forEach((body, index) => {
34525
+ if (!body.isConnected) return;
34526
+ const origin = body.getBoundingClientRect();
34527
+ const scale = resolveViewerMeasurementZoomScale(body, 1);
34528
+ const surface = body.closest("[data-docx-page-surface='true']")?.getBoundingClientRect() ?? origin;
34529
+ if (surface.bottom < -frame.height || surface.top > window.innerHeight + frame.height)
34530
+ return;
34531
+ const distance = Math.max(surface.top - frame.top, frame.top - surface.bottom, 0) + Math.max(surface.left - frame.left, frame.left - surface.right, 0);
34532
+ if (distance < closestDistance) {
34533
+ closestDistance = distance;
34534
+ pageIndex = index;
34535
+ if (distances) {
34536
+ const width = origin.width / scale;
34537
+ let left = Math.max(
34538
+ 0,
34539
+ (frame.left - origin.left) / scale - distances.left
34540
+ );
34541
+ let right = Math.min(
34542
+ width,
34543
+ (frame.left + frame.width - origin.left) / scale + distances.right
34544
+ );
34545
+ if (distances.fullWidth || left < MIN_DUAL_WRAPPED_INTERIOR_BAND_PX)
34546
+ left = 0;
34547
+ if (distances.fullWidth || width - right < MIN_DUAL_WRAPPED_INTERIOR_BAND_PX)
34548
+ right = width;
34549
+ obstacle = {
34550
+ left,
34551
+ right,
34552
+ top: (frame.top - origin.top) / scale - distances.top,
34553
+ bottom: (frame.top + frame.height - origin.top) / scale + distances.bottom,
34554
+ fromDragPreview: true
34555
+ };
34556
+ }
34557
+ }
34558
+ const bands = /* @__PURE__ */ new Map();
34559
+ const record = (key, element) => {
34560
+ if (!element.isConnected || !body.contains(element)) return;
34561
+ const rect = element.getBoundingClientRect();
34562
+ bands.set(key, {
34563
+ topPx: (rect.top - origin.top) / scale,
34564
+ bottomPx: (rect.bottom - origin.top) / scale,
34565
+ leftPx: (rect.left - origin.left) / scale,
34566
+ rightPx: (rect.right - origin.left) / scale
34567
+ });
34568
+ };
34569
+ paragraphElementsRef.current.forEach(
34570
+ (element, nodeIndex) => record(`para:${nodeIndex}`, element)
34571
+ );
34572
+ body.querySelectorAll(
34573
+ "[data-docx-paragraph-host='true'][data-docx-paragraph-kind='paragraph']"
34574
+ ).forEach((element) => {
34575
+ record(
34576
+ `para:${element.dataset.docxParagraphNodeIndex}:${element.dataset.docxParagraphStartLine ?? 0}`,
34577
+ element
34578
+ );
34579
+ });
34580
+ tableElementsRef.current.forEach((element, nodeIndex) => {
34581
+ record(`tbl:${nodeIndex}`, element);
34582
+ const host = element.closest(
34583
+ "[data-docx-floating-table-host]"
34584
+ );
34585
+ if (host) record(`tblanchor:${nodeIndex}`, host);
34586
+ });
34587
+ body.querySelectorAll("[data-docx-image-location]").forEach((element) => {
34588
+ record(`img:${element.dataset.docxImageLocation}`, element);
34589
+ });
34590
+ bandsByPage.set(index, bands);
34591
+ });
34592
+ const preview = { ...source, pageIndex, obstacle, bandsByPage };
34593
+ objectWrapDragLatestRef.current = preview;
34594
+ const signature = JSON.stringify([
34595
+ source,
34596
+ pageIndex,
34597
+ obstacle,
34598
+ [...bandsByPage].map(([index, bands]) => [index, [...bands]])
34599
+ ]);
34600
+ if (objectWrapDragSignatureRef.current !== signature) {
34601
+ objectWrapDragSignatureRef.current = signature;
34602
+ setObjectWrapDragPreview(preview);
34603
+ }
34604
+ },
34605
+ [resolveViewerMeasurementZoomScale]
34606
+ );
34607
+ const clearObjectDragPreview = React.useCallback(
34608
+ (preserveMeasurements = false) => {
34609
+ if (preserveMeasurements && objectWrapDragLatestRef.current) {
34610
+ const next = new Map(measuredWrapBands);
34611
+ objectWrapDragLatestRef.current.bandsByPage.forEach((bands) => {
34612
+ bands.forEach((band, key) => {
34613
+ if (!key.startsWith("img:")) next.set(key, band);
34614
+ });
34615
+ });
34616
+ setMeasuredWrapBands(next);
34617
+ }
34618
+ objectWrapDragLatestRef.current = void 0;
34619
+ objectWrapDragSignatureRef.current = "";
34620
+ objectDragCleanupRef.current = void 0;
34621
+ setObjectWrapDragPreview(void 0);
34622
+ setFloatingMovePreview(void 0);
34623
+ tableMoveDragRef.current = void 0;
34624
+ },
34625
+ [measuredWrapBands]
34626
+ );
33821
34627
  const beginTableMoveDrag = React.useCallback(
33822
34628
  (event, tableIndex) => {
33823
- if (isReadOnly) {
33824
- return;
33825
- }
33826
- if (event.button !== 0 && event.button !== void 0) {
33827
- return;
33828
- }
34629
+ if (isReadOnly || event.button !== 0) return;
34630
+ const element = tableElementsRef.current.get(tableIndex);
34631
+ const table = editor.model.nodes[tableIndex];
34632
+ if (!element || table?.type !== "table") return;
33829
34633
  event.preventDefault();
33830
34634
  event.stopPropagation();
34635
+ objectDragCleanupRef.current?.();
33831
34636
  selectWholeTable(tableIndex);
33832
34637
  setHoveredTableHandleTableIndex(tableIndex);
33833
34638
  tableMoveDragRef.current = {
@@ -33837,108 +34642,82 @@ function DocxEditorViewer({
33837
34642
  startY: event.clientY,
33838
34643
  hasMoved: false
33839
34644
  };
33840
- const zoomScale = resolveViewerMeasurementZoomScale(
33841
- viewerRootRef.current,
33842
- 1
33843
- );
33844
- const tableElementAtStart = tableElementsRef.current.get(tableIndex);
33845
- const tableRectAtStart = tableElementAtStart?.getBoundingClientRect();
33846
- const floatingHostRectAtStart = tableElementAtStart?.closest("[data-docx-floating-table-host]")?.getBoundingClientRect();
33847
- const onPointerMove = (pointerMoveEvent) => {
33848
- const dragState = tableMoveDragRef.current;
33849
- if (!dragState || pointerMoveEvent.pointerId !== dragState.pointerId) {
33850
- return;
33851
- }
33852
- const movedX = pointerMoveEvent.clientX - dragState.startX;
33853
- const movedY = pointerMoveEvent.clientY - dragState.startY;
33854
- if (!dragState.hasMoved && Math.abs(movedX) + Math.abs(movedY) < TABLE_MOVE_DRAG_THRESHOLD_PX) {
33855
- return;
33856
- }
33857
- dragState.hasMoved = true;
33858
- setTableFloatingDragPreview({
33859
- tableIndex: dragState.tableIndex,
33860
- deltaX: normalizeFloatingDragDeltaForZoom(movedX, zoomScale),
33861
- deltaY: normalizeFloatingDragDeltaForZoom(movedY, zoomScale)
33862
- });
33863
- pointerMoveEvent.preventDefault();
33864
- };
33865
- const onPointerUp = (pointerUpEvent) => {
33866
- const dragState = tableMoveDragRef.current;
33867
- if (!dragState || pointerUpEvent.pointerId !== dragState.pointerId) {
33868
- return;
33869
- }
33870
- window.removeEventListener("pointermove", onPointerMove);
33871
- window.removeEventListener("pointerup", onPointerUp);
33872
- window.removeEventListener("pointercancel", onPointerUp);
33873
- tableMoveDragRef.current = void 0;
33874
- const pointerTarget = document.elementFromPoint(
33875
- pointerUpEvent.clientX,
33876
- pointerUpEvent.clientY
33877
- );
33878
- const tableElement = tableElementsRef.current.get(dragState.tableIndex);
33879
- const pointerStillOverTable = Boolean(
33880
- pointerTarget && tableElement?.contains(pointerTarget)
33881
- );
33882
- setHoveredTableHandleTableIndex(
33883
- pointerStillOverTable ? dragState.tableIndex : void 0
33884
- );
33885
- setTableFloatingDragPreview(void 0);
33886
- if (!dragState.hasMoved || !tableRectAtStart) {
33887
- return;
33888
- }
33889
- const deltaX = normalizeFloatingDragDeltaForZoom(
33890
- pointerUpEvent.clientX - dragState.startX,
33891
- zoomScale
33892
- );
33893
- const deltaY = normalizeFloatingDragDeltaForZoom(
33894
- pointerUpEvent.clientY - dragState.startY,
33895
- zoomScale
33896
- );
33897
- const tableNode = editor.model.nodes[dragState.tableIndex];
33898
- if (!tableNode || tableNode.type !== "table") {
33899
- return;
34645
+ const zoom = resolveViewerMeasurementZoomScale(element, 1);
34646
+ const floating = table.style?.floating;
34647
+ objectDragCleanupRef.current = startObjectDrag({
34648
+ event,
34649
+ element,
34650
+ zoom,
34651
+ resolveElement: () => tableElementsRef.current.get(tableIndex),
34652
+ onFrame: (frame) => {
34653
+ if (tableMoveDragRef.current)
34654
+ tableMoveDragRef.current.hasMoved = true;
34655
+ updateObjectWrapDragPreview(
34656
+ frame,
34657
+ { tableIndex },
34658
+ {
34659
+ left: twipsToPixels(floating?.leftFromTextTwips) ?? 12,
34660
+ right: twipsToPixels(floating?.rightFromTextTwips) ?? 12,
34661
+ top: twipsToPixels(floating?.topFromTextTwips) ?? 0,
34662
+ bottom: twipsToPixels(floating?.bottomFromTextTwips) ?? 0
34663
+ }
34664
+ );
34665
+ },
34666
+ onCancel: clearObjectDragPreview,
34667
+ onDrop: (frame) => {
34668
+ const anchor = resolveWrappedDropAnchorParagraphHost(
34669
+ element,
34670
+ frame.top,
34671
+ frame.left
34672
+ );
34673
+ const body = anchor?.host.closest("[data-docx-page-surface='true']")?.querySelector("[data-docx-page-body]");
34674
+ const host = tableElementsRef.current.get(tableIndex)?.closest("[data-docx-floating-table-host]");
34675
+ const anchorRect = anchor?.rect ?? host?.getBoundingClientRect();
34676
+ const bodyRect = body?.getBoundingClientRect();
34677
+ const startLine = Number(
34678
+ anchor?.host.dataset.docxParagraphStartLine ?? 0
34679
+ );
34680
+ const anchorParagraph = anchor ? editor.model.nodes[anchor.nodeIndex] : void 0;
34681
+ const anchorSource = anchorParagraph?.type === "paragraph" ? buildParagraphPretextLayoutSource(anchorParagraph) : void 0;
34682
+ const anchorLayout = anchorParagraph?.type === "paragraph" && anchorSource && anchorRect ? layoutParagraphPretextSource(
34683
+ anchorParagraph,
34684
+ anchorSource,
34685
+ anchorRect.width / zoom,
34686
+ Number(anchor?.host.dataset.docxParagraphLineHeight) || estimateParagraphLineHeightPx(anchorParagraph),
34687
+ []
34688
+ ) : void 0;
34689
+ const anchorTextOffset = startLine > 0 ? anchorLayout?.lines[startLine]?.fragments[0]?.startOffset : void 0;
34690
+ clearObjectDragPreview();
34691
+ if (!anchorRect) return;
34692
+ editor.setTableFloating(
34693
+ tableIndex,
34694
+ {
34695
+ xTwips: pixelsToTwips(
34696
+ (frame.left - (bodyRect?.left ?? anchorRect.left)) / zoom
34697
+ ),
34698
+ yTwips: pixelsToTwips((frame.top - anchorRect.top) / zoom),
34699
+ horizontalAnchor: "margin",
34700
+ verticalAnchor: "text",
34701
+ horizontalAlign: void 0,
34702
+ verticalAlign: void 0,
34703
+ leftFromTextTwips: floating?.leftFromTextTwips ?? 180,
34704
+ rightFromTextTwips: floating?.rightFromTextTwips ?? 180,
34705
+ topFromTextTwips: floating?.topFromTextTwips ?? 0,
34706
+ bottomFromTextTwips: floating?.bottomFromTextTwips ?? 0
34707
+ },
34708
+ anchor ? { moveToNodeIndex: anchor.nodeIndex, anchorTextOffset } : void 0
34709
+ );
33900
34710
  }
33901
- const currentFloating = tableNode.style?.floating;
33902
- const baseLeftPx = floatingHostRectAtStart ? (tableRectAtStart.left - floatingHostRectAtStart.left) / zoomScale : twipsToSignedPixels(tableNode.style?.indentTwips) ?? 0;
33903
- const baseTopPx = floatingHostRectAtStart ? (tableRectAtStart.top - floatingHostRectAtStart.top) / zoomScale : 0;
33904
- const dropTarget = resolveTableMoveDropTarget(dragState.tableIndex, {
33905
- x: pointerUpEvent.clientX,
33906
- y: pointerUpEvent.clientY
33907
- });
33908
- const rootRect = viewerRootRef.current?.getBoundingClientRect();
33909
- const shouldReanchor = dropTarget !== void 0 && rootRect !== void 0 && dropTarget.targetNodeIndex !== dragState.tableIndex && dropTarget.targetNodeIndex !== dragState.tableIndex + 1;
33910
- let committedTopPx = baseTopPx + deltaY;
33911
- if (shouldReanchor && dropTarget && rootRect) {
33912
- let targetScreenTop = rootRect.top + dropTarget.preview.top;
33913
- if (!floatingHostRectAtStart && dropTarget.targetNodeIndex > dragState.tableIndex) {
33914
- targetScreenTop -= tableRectAtStart.height;
33915
- }
33916
- const droppedTopScreenPx = tableRectAtStart.top + (pointerUpEvent.clientY - dragState.startY);
33917
- committedTopPx = (droppedTopScreenPx - targetScreenTop) / zoomScale;
33918
- }
33919
- committedTopPx = Math.max(0, committedTopPx);
33920
- editor.setTableFloating(
33921
- dragState.tableIndex,
33922
- {
33923
- xTwips: pixelsToTwips(baseLeftPx + deltaX),
33924
- yTwips: pixelsToTwips(committedTopPx),
33925
- horizontalAnchor: "margin",
33926
- verticalAnchor: "text",
33927
- horizontalAlign: void 0,
33928
- verticalAlign: void 0,
33929
- leftFromTextTwips: currentFloating?.leftFromTextTwips ?? 187,
33930
- rightFromTextTwips: currentFloating?.rightFromTextTwips ?? 187,
33931
- topFromTextTwips: currentFloating?.topFromTextTwips ?? 0,
33932
- bottomFromTextTwips: currentFloating?.bottomFromTextTwips ?? 0
33933
- },
33934
- shouldReanchor && dropTarget ? { moveToNodeIndex: dropTarget.targetNodeIndex } : void 0
33935
- );
33936
- };
33937
- window.addEventListener("pointermove", onPointerMove);
33938
- window.addEventListener("pointerup", onPointerUp, { once: true });
33939
- window.addEventListener("pointercancel", onPointerUp, { once: true });
34711
+ });
33940
34712
  },
33941
- [editor, isReadOnly, resolveTableMoveDropTarget, selectWholeTable]
34713
+ [
34714
+ editor,
34715
+ isReadOnly,
34716
+ selectWholeTable,
34717
+ resolveViewerMeasurementZoomScale,
34718
+ updateObjectWrapDragPreview,
34719
+ clearObjectDragPreview
34720
+ ]
33942
34721
  );
33943
34722
  const closeTableContextMenu = React.useCallback(() => {
33944
34723
  setTableContextMenuState(void 0);
@@ -34145,12 +34924,13 @@ function DocxEditorViewer({
34145
34924
  let range2;
34146
34925
  if (typeof documentWithCaret.caretPositionFromPoint === "function") {
34147
34926
  const position = documentWithCaret.caretPositionFromPoint(x, y);
34148
- if (position) {
34927
+ if (position && position.offset >= 0 && position.offset <= (position.offsetNode.nodeType === Node.TEXT_NODE ? position.offsetNode.textContent?.length ?? 0 : position.offsetNode.childNodes.length)) {
34149
34928
  range2 = document.createRange();
34150
34929
  range2.setStart(position.offsetNode, position.offset);
34151
34930
  range2.collapse(true);
34152
34931
  }
34153
- } else if (typeof documentWithCaret.caretRangeFromPoint === "function") {
34932
+ }
34933
+ if (!range2 && typeof documentWithCaret.caretRangeFromPoint === "function") {
34154
34934
  const pointRange = documentWithCaret.caretRangeFromPoint(x, y);
34155
34935
  if (pointRange) {
34156
34936
  range2 = pointRange;
@@ -34167,7 +34947,7 @@ function DocxEditorViewer({
34167
34947
  let bestDistance = Number.POSITIVE_INFINITY;
34168
34948
  let node = walker.nextNode();
34169
34949
  while (node) {
34170
- if (node instanceof Text && node.parentElement?.closest("[data-docx-numbering-label='true']")) {
34950
+ if (node instanceof Text && node.parentElement?.closest("[data-docx-numbering-label='true'],[data-docx-image-location]")) {
34171
34951
  node = walker.nextNode();
34172
34952
  continue;
34173
34953
  }
@@ -34329,7 +35109,7 @@ function DocxEditorViewer({
34329
35109
  let currentTextNode = walker.nextNode();
34330
35110
  while (currentTextNode) {
34331
35111
  if (currentTextNode instanceof Text && currentTextNode.parentElement?.closest(
34332
- "[data-docx-numbering-label='true']"
35112
+ "[data-docx-numbering-label='true'],[data-docx-image-location]"
34333
35113
  )) {
34334
35114
  currentTextNode = walker.nextNode();
34335
35115
  continue;
@@ -34446,7 +35226,7 @@ function DocxEditorViewer({
34446
35226
  let currentTextNode = walker.nextNode();
34447
35227
  while (currentTextNode) {
34448
35228
  if (currentTextNode instanceof Text && currentTextNode.parentElement?.closest(
34449
- "[data-docx-numbering-label='true']"
35229
+ "[data-docx-numbering-label='true'],[data-docx-image-location]"
34450
35230
  )) {
34451
35231
  currentTextNode = walker.nextNode();
34452
35232
  continue;
@@ -34737,8 +35517,8 @@ function DocxEditorViewer({
34737
35517
  const offset = clampNumber(
34738
35518
  resolveOffsetAtPoint(
34739
35519
  registeredSurface.layout,
34740
- point.x - surfaceRect.left,
34741
- point.y - surfaceRect.top
35520
+ (point.x - surfaceRect.left) / resolveEffectiveZoomScale(registeredSurface.element),
35521
+ (point.y - surfaceRect.top) / resolveEffectiveZoomScale(registeredSurface.element)
34742
35522
  ),
34743
35523
  0,
34744
35524
  registeredSurface.textLength
@@ -34791,6 +35571,7 @@ function DocxEditorViewer({
34791
35571
  return void 0;
34792
35572
  }
34793
35573
  const referenceElement = container instanceof Element ? container : container.parentElement;
35574
+ if (referenceElement?.closest("[data-docx-textbox-editor='true']")) return void 0;
34794
35575
  const paragraphElement = referenceElement?.closest(
34795
35576
  "[data-docx-paragraph-kind]"
34796
35577
  );
@@ -35459,7 +36240,7 @@ function DocxEditorViewer({
35459
36240
  if (!textarea || !textarea.isConnected) {
35460
36241
  return;
35461
36242
  }
35462
- textarea.focus();
36243
+ textarea.focus({ preventScroll: true });
35463
36244
  const pendingSelection = wrappedParagraphPendingSelectionRef.current;
35464
36245
  const session = activeWrappedParagraphSession;
35465
36246
  const selectionSource = pendingSelection && (!session || pendingSelection.locationKey === session.locationKey) ? pendingSelection : session ? {
@@ -35550,6 +36331,54 @@ function DocxEditorViewer({
35550
36331
  flushActiveRangeFromSelection();
35551
36332
  }, 80);
35552
36333
  }, [flushActiveRangeFromSelection]);
36334
+ React.useLayoutEffect(() => {
36335
+ const request = editor.historyRestoreRequest;
36336
+ if (!request?.textInput || !request.activeTextRange || request.nonce === appliedHistoryRestoreNonceRef.current || compositionActiveRef.current) {
36337
+ return;
36338
+ }
36339
+ const { start, end } = request.activeTextRange;
36340
+ const host = resolveParagraphHostElement(start.location);
36341
+ const startPosition = resolveDomPositionFromBoundary(start);
36342
+ const endPosition = resolveDomPositionFromBoundary(end);
36343
+ const selection = window.getSelection();
36344
+ if (!host?.isContentEditable || !startPosition || !endPosition || !selection) {
36345
+ return;
36346
+ }
36347
+ try {
36348
+ beginSelectionIntent();
36349
+ const focusedElement = document.activeElement;
36350
+ if (!(focusedElement instanceof HTMLElement) || !focusedElement.isContentEditable || !focusedElement.contains(host)) {
36351
+ const editableRoot = host.closest("[contenteditable='true']") ?? host;
36352
+ editableRoot.focus({ preventScroll: true });
36353
+ }
36354
+ if (selection.anchorNode !== startPosition.node || selection.anchorOffset !== startPosition.offset || selection.focusNode !== endPosition.node || selection.focusOffset !== endPosition.offset) {
36355
+ if (typeof selection.setBaseAndExtent === "function") {
36356
+ selection.setBaseAndExtent(
36357
+ startPosition.node,
36358
+ startPosition.offset,
36359
+ endPosition.node,
36360
+ endPosition.offset
36361
+ );
36362
+ } else {
36363
+ const range = document.createRange();
36364
+ const forward = compareTextRangeBoundaries(start, end) <= 0;
36365
+ const first = forward ? startPosition : endPosition;
36366
+ const last = forward ? endPosition : startPosition;
36367
+ range.setStart(first.node, first.offset);
36368
+ range.setEnd(last.node, last.offset);
36369
+ selection.removeAllRanges();
36370
+ selection.addRange(range);
36371
+ }
36372
+ }
36373
+ appliedHistoryRestoreNonceRef.current = request.nonce;
36374
+ } catch {
36375
+ }
36376
+ }, [
36377
+ beginSelectionIntent,
36378
+ editor.historyRestoreRequest,
36379
+ resolveDomPositionFromBoundary,
36380
+ resolveParagraphHostElement
36381
+ ]);
35553
36382
  React.useEffect(() => {
35554
36383
  const restoreRequest = editor.historyRestoreRequest;
35555
36384
  if (!restoreRequest) {
@@ -36055,6 +36884,33 @@ function DocxEditorViewer({
36055
36884
  tableContextMenuState
36056
36885
  ]
36057
36886
  );
36887
+ const onViewerFormFieldDoubleClick = React.useCallback(
36888
+ (event) => {
36889
+ if (isReadOnly || !(event.target instanceof Element)) {
36890
+ return;
36891
+ }
36892
+ const fieldElement = event.target.closest("[data-docx-form-field='true']");
36893
+ const paragraphLocation = parseParagraphLocationFromElement(
36894
+ fieldElement?.closest("[data-docx-paragraph-kind]") ?? null
36895
+ );
36896
+ const childIndex = parseDocumentIndexFromAttribute(
36897
+ fieldElement?.getAttribute("data-docx-form-field-child-index") ?? null
36898
+ );
36899
+ if (!paragraphLocation || childIndex === void 0) {
36900
+ return;
36901
+ }
36902
+ const field = getParagraphAtLocation(editor.model, paragraphLocation).paragraph?.children[childIndex];
36903
+ if (field?.type !== "form-field") {
36904
+ return;
36905
+ }
36906
+ event.preventDefault();
36907
+ event.stopPropagation();
36908
+ const location = { ...paragraphLocation, childIndex };
36909
+ editor.selectFormField(location);
36910
+ onFormFieldDoubleClick?.(location);
36911
+ },
36912
+ [editor.model, editor.selectFormField, isReadOnly, onFormFieldDoubleClick]
36913
+ );
36058
36914
  const onViewerContextMenu = React.useCallback(
36059
36915
  (event) => {
36060
36916
  if (isReadOnly || event.defaultPrevented) {
@@ -37346,7 +38202,7 @@ function DocxEditorViewer({
37346
38202
  try {
37347
38203
  const textLengthWithoutNumberingLabels = (range) => {
37348
38204
  const fragment = range.cloneContents();
37349
- fragment.querySelectorAll("[data-docx-numbering-label='true']").forEach((label) => {
38205
+ fragment.querySelectorAll("[data-docx-numbering-label='true'],[data-docx-image-location]").forEach((label) => {
37350
38206
  label.remove();
37351
38207
  });
37352
38208
  return fragment.textContent?.length ?? 0;
@@ -37880,7 +38736,7 @@ function DocxEditorViewer({
37880
38736
  if (!targetElement) {
37881
38737
  return;
37882
38738
  }
37883
- if (targetElement.closest("textarea, input") || targetElement.closest("[data-docx-header-footer-region]")) {
38739
+ if (targetElement.closest("textarea, input, [data-docx-textbox-editor='true']") || targetElement.closest("[data-docx-header-footer-region]")) {
37884
38740
  return;
37885
38741
  }
37886
38742
  const intent = resolveEditingIntent({
@@ -39027,288 +39883,229 @@ function DocxEditorViewer({
39027
39883
  );
39028
39884
  const beginFloatingImageMove = React.useCallback(
39029
39885
  (event, location, image, isWrappedFloatingImage, isAbsoluteFloatingImage) => {
39030
- if (isReadOnly) {
39031
- return;
39032
- }
39033
- if (event.button !== 0) {
39034
- return;
39035
- }
39036
- if (event.target.closest(
39886
+ if (isReadOnly || event.button !== 0 || event.target.closest(
39037
39887
  "[data-image-resize-handle='true']"
39038
- )) {
39888
+ ))
39039
39889
  return;
39040
- }
39041
- const isInlineImage = !isWrappedFloatingImage && !isAbsoluteFloatingImage;
39042
39890
  event.preventDefault();
39043
39891
  event.stopPropagation();
39892
+ objectDragCleanupRef.current?.();
39044
39893
  setSelectedImage(location);
39894
+ const element = event.currentTarget;
39045
39895
  const imageKey = imageLocationKey(location);
39046
- const startX = event.clientX;
39047
- const startY = event.clientY;
39048
- const inlineDragActivationThresholdPx = 4;
39049
- const wrapperElement = event.currentTarget;
39050
- const wrapperRect = wrapperElement.getBoundingClientRect();
39051
- const pageSurface = wrapperElement.closest(
39052
- "[data-docx-page-surface='true']"
39053
- );
39054
- const pageSurfaceRect = pageSurface?.getBoundingClientRect();
39055
- const paragraphHost = wrapperElement.closest(
39056
- "[data-docx-paragraph-host='true']"
39057
- );
39058
- const paragraphRect = paragraphHost?.getBoundingClientRect();
39059
- const zoomScale = resolveViewerMeasurementZoomScale(wrapperElement, 1);
39060
- const baseFloating = image.floating ? { ...image.floating } : {};
39061
- const dragStartAnchor = isWrappedFloatingImage && location.kind === "paragraph" ? resolveWrappedDropAnchorParagraphHost(
39062
- wrapperElement,
39063
- wrapperRect.top,
39064
- wrapperRect.left
39065
- ) : void 0;
39066
- let latestDeltaX = 0;
39067
- let latestDeltaY = 0;
39068
- let frameId;
39069
- let inlineDragActivated = false;
39070
- let latestInlineDropTarget;
39071
- let latestInlineDropTargetKey;
39072
- const measuredBands = (() => {
39073
- if (!isWrappedFloatingImage || !pageSurface || !pageSurfaceRect) {
39074
- return void 0;
39075
- }
39076
- const hostByNodeIndex = {};
39077
- for (const host of Array.from(
39078
- pageSurface.querySelectorAll(
39079
- "[data-docx-paragraph-host='true'][data-docx-paragraph-kind='paragraph'][data-docx-paragraph-node-index]"
39080
- )
39081
- )) {
39082
- const nodeIndex = Number.parseInt(
39083
- host.getAttribute("data-docx-paragraph-node-index") ?? "",
39084
- 10
39085
- );
39086
- if (!Number.isFinite(nodeIndex)) {
39087
- continue;
39088
- }
39089
- const rect = host.getBoundingClientRect();
39090
- if (rect.height <= 0) {
39091
- continue;
39092
- }
39093
- hostByNodeIndex[nodeIndex] = {
39094
- topPx: (rect.top - pageSurfaceRect.top) / zoomScale,
39095
- bottomPx: (rect.bottom - pageSurfaceRect.top) / zoomScale
39096
- };
39097
- }
39098
- return Object.keys(hostByNodeIndex).length > 0 ? { hostByNodeIndex } : void 0;
39099
- })();
39100
- const updatePreview = () => {
39101
- setFloatingMovePreview({
39102
- imageKey,
39103
- deltaX: latestDeltaX,
39104
- deltaY: latestDeltaY,
39105
- measuredBands,
39106
- ...paragraphRect ? {
39107
- baseLeftPx: Math.round(
39108
- (wrapperRect.left - paragraphRect.left) / zoomScale
39109
- ),
39110
- baseTopPx: Math.round(
39111
- (wrapperRect.top - paragraphRect.top) / zoomScale
39112
- )
39113
- } : void 0
39114
- });
39115
- };
39116
- const onPointerMove = (pointerEvent) => {
39117
- latestDeltaX = normalizeFloatingDragDeltaForZoom(
39118
- pointerEvent.clientX - startX,
39119
- zoomScale
39896
+ const zoom = resolveViewerMeasurementZoomScale(element, 1);
39897
+ const isInline = !isWrappedFloatingImage && !isAbsoluteFloatingImage;
39898
+ const floating = image.floating;
39899
+ let caret;
39900
+ const resolveInlineTarget = (frame) => {
39901
+ const point = { x: frame.clientX, y: frame.clientY };
39902
+ const boundary = resolveBoundaryFromPoint(point);
39903
+ if (!boundary) return void 0;
39904
+ const paragraph = getParagraphAtLocation(
39905
+ editor.model,
39906
+ boundary.location
39907
+ ).paragraph;
39908
+ if (!paragraph) return void 0;
39909
+ const wrapped = wrappedParagraphSurfaceRegistryRef.current.get(
39910
+ paragraphLocationKey(boundary.location)
39120
39911
  );
39121
- latestDeltaY = normalizeFloatingDragDeltaForZoom(
39122
- pointerEvent.clientY - startY,
39123
- zoomScale
39912
+ const layoutSource = wrapped ? buildParagraphPretextLayoutSource(paragraph) : void 0;
39913
+ const run = layoutSource?.runs.find(
39914
+ (candidate) => candidate.endOffset >= boundary.offset
39124
39915
  );
39125
- if (isInlineImage) {
39126
- if (!inlineDragActivated) {
39127
- const movementDistancePx = Math.abs(latestDeltaX) + Math.abs(latestDeltaY);
39128
- if (movementDistancePx < inlineDragActivationThresholdPx) {
39129
- return;
39130
- }
39131
- inlineDragActivated = true;
39132
- draggedImageRef.current = location;
39133
- setIsDraggingImage(true);
39134
- setActiveDropTarget(void 0);
39135
- }
39136
- const candidate = document.elementFromPoint(pointerEvent.clientX, pointerEvent.clientY)?.closest(
39137
- "[data-docx-image-drop-zone='true']"
39916
+ const runIndex = run ? Number(/^run-(\d+)/.exec(run.key)?.[1]) : void 0;
39917
+ const position = run && runIndex !== void 0 && Number.isFinite(runIndex) ? run.kind === "image" ? {
39918
+ childIndex: runIndex + (boundary.offset > (run.startOffset + run.endOffset) / 2 ? 1 : 0)
39919
+ } : {
39920
+ childIndex: runIndex,
39921
+ textOffset: Math.max(0, boundary.offset - run.startOffset)
39922
+ } : imageDropPositionAtTextOffset(paragraph, boundary.offset, {
39923
+ fieldText: formFieldDisplayValue2
39924
+ });
39925
+ const target = { ...boundary.location, ...position };
39926
+ let rect;
39927
+ if (wrapped) {
39928
+ const local = resolveCaretRectAtOffset(
39929
+ wrapped.layout,
39930
+ boundary.offset
39138
39931
  );
39139
- const parsedTarget = candidate ? parseImageDropTargetFromDataset(candidate.dataset) : void 0;
39140
- const parsedTargetKey = parsedTarget ? dropTargetKey(parsedTarget) : void 0;
39141
- if (parsedTargetKey !== latestInlineDropTargetKey) {
39142
- latestInlineDropTarget = parsedTarget;
39143
- latestInlineDropTargetKey = parsedTargetKey;
39144
- setActiveDropTarget(parsedTarget);
39145
- }
39146
- }
39147
- if (frameId !== void 0) {
39148
- window.cancelAnimationFrame(frameId);
39932
+ const origin = wrapped.element.getBoundingClientRect();
39933
+ const scale = resolveViewerMeasurementZoomScale(wrapped.element, 1);
39934
+ if (local)
39935
+ rect = {
39936
+ left: origin.left + local.left * scale,
39937
+ top: origin.top + local.top * scale,
39938
+ height: local.height * scale
39939
+ };
39940
+ } else {
39941
+ const doc = document;
39942
+ const range = doc.caretRangeFromPoint?.(point.x, point.y);
39943
+ const candidate = range?.getBoundingClientRect();
39944
+ if (candidate?.height) rect = candidate;
39945
+ }
39946
+ if (!caret) {
39947
+ caret = document.createElement("div");
39948
+ caret.setAttribute("data-docx-image-drop-caret", "true");
39949
+ Object.assign(caret.style, {
39950
+ position: "fixed",
39951
+ pointerEvents: "none",
39952
+ width: "2px",
39953
+ background: "#2563eb",
39954
+ zIndex: "2147483647"
39955
+ });
39956
+ document.body.appendChild(caret);
39149
39957
  }
39150
- frameId = window.requestAnimationFrame(updatePreview);
39958
+ Object.assign(
39959
+ caret.style,
39960
+ rect ? {
39961
+ display: "block",
39962
+ left: `${rect.left}px`,
39963
+ top: `${rect.top}px`,
39964
+ height: `${rect.height}px`
39965
+ } : { display: "none" }
39966
+ );
39967
+ return target;
39151
39968
  };
39152
- const onPointerUp = () => {
39153
- window.removeEventListener("pointermove", onPointerMove);
39154
- window.removeEventListener("pointerup", onPointerUp);
39155
- window.removeEventListener("pointercancel", onPointerUp);
39156
- if (frameId !== void 0) {
39157
- window.cancelAnimationFrame(frameId);
39158
- }
39159
- setFloatingMovePreview(void 0);
39160
- if (isInlineImage) {
39161
- if (inlineDragActivated) {
39162
- setIsDraggingImage(false);
39163
- draggedImageRef.current = null;
39164
- setActiveDropTarget(void 0);
39165
- }
39166
- if (Math.abs(latestDeltaX) < 1 && Math.abs(latestDeltaY) < 1) {
39167
- return;
39168
- }
39169
- if (!inlineDragActivated) {
39969
+ const clear = (preserveMeasurements = false) => {
39970
+ caret?.remove();
39971
+ clearObjectDragPreview(preserveMeasurements);
39972
+ };
39973
+ objectDragCleanupRef.current = startObjectDrag({
39974
+ event,
39975
+ element,
39976
+ zoom,
39977
+ resolveElement: () => viewerRootRef.current?.querySelector(
39978
+ `[data-docx-image-location="${imageKey}"]`
39979
+ ) ?? void 0,
39980
+ onFrame: (frame) => {
39981
+ if (isInline) {
39982
+ resolveInlineTarget(frame);
39170
39983
  return;
39171
39984
  }
39172
- if (!latestInlineDropTarget) {
39985
+ updateObjectWrapDragPreview(
39986
+ frame,
39987
+ { imageKey },
39988
+ floating?.wrapType && floating.wrapType !== "none" && !floating.behindDocument ? {
39989
+ left: floating.distLPx ?? 0,
39990
+ right: floating.distRPx ?? 0,
39991
+ top: floating.distTPx ?? 0,
39992
+ bottom: floating.distBPx ?? 0,
39993
+ fullWidth: floating.wrapType === "topAndBottom"
39994
+ } : void 0
39995
+ );
39996
+ },
39997
+ onCancel: clear,
39998
+ onDrop: (frame) => {
39999
+ if (isInline) {
40000
+ const target = resolveInlineTarget(frame);
40001
+ clear();
40002
+ if (!target) return;
40003
+ editor.moveImage(location, target);
40004
+ const sameParagraph = sameParagraphLocation(location, target);
40005
+ setSelectedImage({
40006
+ ...target,
40007
+ childIndex: target.childIndex - (sameParagraph && location.childIndex < target.childIndex ? 1 : 0) + (target.textOffset ? 1 : 0)
40008
+ });
39173
40009
  return;
39174
40010
  }
39175
- editor.moveImage(location, latestInlineDropTarget);
39176
- const sourceParagraphLocation = imageLocationToParagraphLocation(location);
39177
- const targetParagraphLocation = latestInlineDropTarget.kind === "paragraph" ? {
39178
- kind: "paragraph",
39179
- nodeIndex: latestInlineDropTarget.nodeIndex
39180
- } : {
39181
- kind: "table-cell",
39182
- tableIndex: latestInlineDropTarget.tableIndex,
39183
- rowIndex: latestInlineDropTarget.rowIndex,
39184
- cellIndex: latestInlineDropTarget.cellIndex,
39185
- paragraphIndex: latestInlineDropTarget.paragraphIndex
39186
- };
39187
- const nextChildIndex = sameParagraphLocation(
39188
- sourceParagraphLocation,
39189
- targetParagraphLocation
39190
- ) && location.childIndex < latestInlineDropTarget.childIndex ? latestInlineDropTarget.childIndex - 1 : latestInlineDropTarget.childIndex;
39191
- setSelectedImage({
39192
- ...latestInlineDropTarget,
39193
- childIndex: Math.max(0, nextChildIndex)
39194
- });
39195
- return;
39196
- }
39197
- if (Math.abs(latestDeltaX) < 1 && Math.abs(latestDeltaY) < 1) {
39198
- return;
39199
- }
39200
- if (isWrappedFloatingImage) {
39201
- const hostWidth = paragraphRect ? paragraphRect.width / zoomScale : DEFAULT_DOC_PAGE_WIDTH;
39202
- const imageWidth = image.widthPx ?? Math.max(24, Math.round(wrapperRect.width / zoomScale));
39203
- const imageHeight = image.heightPx ?? Math.max(24, Math.round(wrapperRect.height / zoomScale));
39204
- const baseGeometry = resolveDualWrappedFloatingImageGeometry(
39205
- image,
39206
- hostWidth,
39207
- {
39208
- widthPx: imageWidth,
39209
- heightPx: imageHeight,
39210
- ...paragraphRect ? {
39211
- baseLeftPx: Math.round(
39212
- (wrapperRect.left - paragraphRect.left) / zoomScale
40011
+ const anchor = location.kind === "paragraph" ? resolveWrappedDropAnchorParagraphHost(
40012
+ element,
40013
+ frame.top,
40014
+ frame.left
40015
+ ) : void 0;
40016
+ const host = anchor?.host ?? element.closest("[data-docx-paragraph-host='true']");
40017
+ const hostRect = host?.getBoundingClientRect();
40018
+ clear(true);
40019
+ if (!hostRect) return;
40020
+ let selectedChildIndex = location.childIndex;
40021
+ if (anchor && (location.kind !== "paragraph" || anchor.nodeIndex !== location.nodeIndex)) {
40022
+ const targetParagraph = editor.model.nodes[anchor.nodeIndex];
40023
+ if (targetParagraph.type === "paragraph")
40024
+ selectedChildIndex = imageDropPositionAtTextOffset(
40025
+ targetParagraph,
40026
+ 0
40027
+ ).childIndex;
40028
+ }
40029
+ if (isWrappedFloatingImage) {
40030
+ const startLine = Number(host?.dataset.docxParagraphStartLine ?? 0);
40031
+ const anchorParagraph = anchor ? editor.model.nodes[anchor.nodeIndex] : void 0;
40032
+ const anchorSource = anchorParagraph?.type === "paragraph" ? buildParagraphPretextLayoutSource(anchorParagraph) : void 0;
40033
+ const anchorLayout = anchorParagraph?.type === "paragraph" && anchorSource ? layoutParagraphPretextSource(
40034
+ anchorParagraph,
40035
+ anchorSource,
40036
+ hostRect.width / zoom,
40037
+ Number(host?.dataset.docxParagraphLineHeight) || estimateParagraphLineHeightPx(anchorParagraph),
40038
+ []
40039
+ ) : void 0;
40040
+ const anchorTextOffset = startLine > 0 ? anchorLayout?.lines[startLine]?.fragments[0]?.startOffset : void 0;
40041
+ if (anchorParagraph?.type === "paragraph" && anchor && (location.kind !== "paragraph" || anchor.nodeIndex !== location.nodeIndex || anchorTextOffset !== void 0)) {
40042
+ const position = imageDropPositionAtTextOffset(
40043
+ anchorParagraph,
40044
+ anchorTextOffset ?? 0,
40045
+ { fieldText: formFieldDisplayValue2 }
40046
+ );
40047
+ selectedChildIndex = position.childIndex + (position.textOffset ? 1 : 0) - (location.kind === "paragraph" && anchor.nodeIndex === location.nodeIndex && location.childIndex < position.childIndex ? 1 : 0);
40048
+ }
40049
+ editor.moveFloatingImage(
40050
+ location,
40051
+ {
40052
+ ...resolveWrappedFloatingImageDropPatch(
40053
+ image,
40054
+ hostRect.width / zoom,
40055
+ (frame.left - hostRect.left) / zoom,
40056
+ (frame.top - hostRect.top) / zoom
39213
40057
  ),
39214
- baseTopPx: Math.round(
39215
- (wrapperRect.top - paragraphRect.top) / zoomScale
39216
- )
40058
+ horizontalAlign: void 0,
40059
+ verticalAlign: void 0,
40060
+ horizontalRelativeTo: "column",
40061
+ verticalRelativeTo: anchorTextOffset !== void 0 ? "line" : "paragraph"
40062
+ },
40063
+ anchor ? {
40064
+ reparentToParagraphNodeIndex: anchor.nodeIndex,
40065
+ anchorTextOffset
39217
40066
  } : void 0
39218
- }
39219
- );
39220
- const movedLeft = clampNumber(
39221
- (baseGeometry?.imageLeftPx ?? 0) + latestDeltaX,
39222
- 0,
39223
- Math.max(0, hostWidth - imageWidth)
39224
- );
39225
- const movedTop = Math.round(
39226
- (baseGeometry?.imageTopPx ?? 0) + latestDeltaY
39227
- );
39228
- const droppedTopScreenPx = wrapperRect.top + latestDeltaY * zoomScale;
39229
- const droppedLeftScreenPx = wrapperRect.left + latestDeltaX * zoomScale;
39230
- const dropAnchor = location.kind === "paragraph" && wrappedDragCanReanchorToParagraph(baseFloating) ? resolveWrappedDropAnchorParagraphHost(
39231
- wrapperElement,
39232
- droppedTopScreenPx,
39233
- droppedLeftScreenPx
39234
- ) : void 0;
39235
- if (dropAnchor && location.kind === "paragraph" && dropAnchor.nodeIndex !== location.nodeIndex && dropAnchor.nodeIndex !== dragStartAnchor?.nodeIndex) {
39236
- const targetHostWidth = dropAnchor.rect.width / zoomScale;
39237
- const targetMovedLeft = clampNumber(
39238
- Math.round(
39239
- (droppedLeftScreenPx - dropAnchor.rect.left) / zoomScale
39240
- ),
39241
- 0,
39242
- Math.max(0, targetHostWidth - imageWidth)
39243
- );
39244
- const targetMovedTop = Math.round(
39245
- (droppedTopScreenPx - dropAnchor.rect.top) / zoomScale
39246
40067
  );
40068
+ } else {
40069
+ const surfaceRect = host?.closest("[data-docx-page-surface='true']")?.getBoundingClientRect();
39247
40070
  editor.moveFloatingImage(
39248
40071
  location,
39249
- resolveWrappedFloatingImageDropPatch(
39250
- image,
39251
- targetHostWidth,
39252
- targetMovedLeft,
39253
- targetMovedTop,
40072
+ resolveAbsoluteFloatingImageDropPatch(
40073
+ floating ?? {},
40074
+ documentLayout,
39254
40075
  {
39255
- widthPx: imageWidth,
39256
- heightPx: imageHeight
40076
+ wrapperRect: {
40077
+ left: frame.left / zoom,
40078
+ top: frame.top / zoom,
40079
+ width: frame.width / zoom,
40080
+ height: frame.height / zoom
40081
+ },
40082
+ pageSurfaceRect: surfaceRect ? normalizeFloatingDropRectForZoom(surfaceRect, zoom) : void 0,
40083
+ deltaX: 0,
40084
+ deltaY: 0
39257
40085
  }
39258
40086
  ),
39259
- { reparentToParagraphNodeIndex: dropAnchor.nodeIndex }
40087
+ anchor ? { reparentToParagraphNodeIndex: anchor.nodeIndex } : void 0
39260
40088
  );
40089
+ }
40090
+ if (anchor) {
39261
40091
  setSelectedImage({
39262
40092
  kind: "paragraph",
39263
- nodeIndex: dropAnchor.nodeIndex,
39264
- childIndex: 0
40093
+ nodeIndex: anchor.nodeIndex,
40094
+ childIndex: selectedChildIndex
39265
40095
  });
39266
- return;
39267
40096
  }
39268
- editor.moveFloatingImage(
39269
- location,
39270
- resolveWrappedFloatingImageDropPatch(
39271
- image,
39272
- hostWidth,
39273
- movedLeft,
39274
- movedTop,
39275
- {
39276
- widthPx: imageWidth,
39277
- heightPx: imageHeight
39278
- }
39279
- )
39280
- );
39281
- return;
39282
40097
  }
39283
- if (isAbsoluteFloatingImage) {
39284
- const dropPageSurfaceRect = pageSurface && pageSurface.isConnected ? pageSurface.getBoundingClientRect() : pageSurfaceRect;
39285
- editor.moveFloatingImage(
39286
- location,
39287
- resolveAbsoluteFloatingImageDropPatch(
39288
- baseFloating,
39289
- documentLayout,
39290
- {
39291
- wrapperRect: normalizeFloatingDropRectForZoom(
39292
- wrapperRect,
39293
- zoomScale
39294
- ),
39295
- pageSurfaceRect: dropPageSurfaceRect ? normalizeFloatingDropRectForZoom(
39296
- dropPageSurfaceRect,
39297
- zoomScale
39298
- ) : void 0,
39299
- deltaX: latestDeltaX,
39300
- deltaY: latestDeltaY
39301
- }
39302
- )
39303
- );
39304
- return;
39305
- }
39306
- };
39307
- window.addEventListener("pointermove", onPointerMove);
39308
- window.addEventListener("pointerup", onPointerUp, { once: true });
39309
- window.addEventListener("pointercancel", onPointerUp, { once: true });
40098
+ });
39310
40099
  },
39311
- [documentLayout, editor, isReadOnly, resolveViewerMeasurementZoomScale]
40100
+ [
40101
+ documentLayout,
40102
+ editor,
40103
+ isReadOnly,
40104
+ resolveViewerMeasurementZoomScale,
40105
+ resolveBoundaryFromPoint,
40106
+ updateObjectWrapDragPreview,
40107
+ clearObjectDragPreview
40108
+ ]
39312
40109
  );
39313
40110
  const beginDropCapMove = React.useCallback(
39314
40111
  (event, nodeIndex, dropCap) => {
@@ -40128,7 +40925,20 @@ function DocxEditorViewer({
40128
40925
  src: renderableImageSrc,
40129
40926
  alt: run.image.alt ?? "DOCX image",
40130
40927
  draggable: false,
40928
+ "data-docx-image-location": imageLocationKey({ ...location, childIndex: Number(/^run-(\d+)/.exec(run.key)?.[1]) }),
40929
+ onPointerDown: (event) => {
40930
+ if (!isReadOnly && run.image) beginFloatingImageMove(
40931
+ event,
40932
+ { ...location, childIndex: Number(/^run-(\d+)/.exec(run.key)?.[1]) },
40933
+ run.image,
40934
+ false,
40935
+ false
40936
+ );
40937
+ },
40131
40938
  style: {
40939
+ position: "relative",
40940
+ zIndex: 3,
40941
+ touchAction: "none",
40132
40942
  width: run.image.widthPx ? `${run.image.widthPx}px` : void 0,
40133
40943
  height: run.image.heightPx ? `${run.image.heightPx}px` : void 0,
40134
40944
  verticalAlign: "middle",
@@ -40745,6 +41555,22 @@ function DocxEditorViewer({
40745
41555
  };
40746
41556
  const renderInteractiveParagraphRuns = React.useCallback(
40747
41557
  (paragraph, keyPrefix, location, options) => {
41558
+ const lineRange = options?.paragraphLineRange;
41559
+ const sliceTop = (lineRange?.startLineIndex ?? 0) * (lineRange?.lineHeightPx ?? 0);
41560
+ const sliceEnd = (lineRange?.endLineIndex ?? Infinity) * (lineRange?.lineHeightPx ?? 1);
41561
+ const imageIsInSlice = (childIndex) => {
41562
+ if (!lineRange) return true;
41563
+ const source = buildParagraphPretextLayoutSource(paragraph);
41564
+ const unwrapped = source ? layoutParagraphPretextSource(
41565
+ paragraph,
41566
+ source,
41567
+ paragraphAvailableTextWidthPx(paragraph, pageContentWidthPxByNodeIndex.get(nodeIndexFromParagraphLocation(location)) ?? documentContentWidthPx, editor.model.metadata.numberingDefinitions),
41568
+ lineRange.lineHeightPx,
41569
+ []
41570
+ ) : void 0;
41571
+ const anchorTop = unwrapped ? resolveCaretRectAtOffset(unwrapped, paragraphChildAnchorOffset(paragraph, childIndex) + 1)?.top ?? 0 : 0;
41572
+ return anchorTop >= sliceTop && anchorTop < sliceEnd;
41573
+ };
40748
41574
  const nodes = [];
40749
41575
  const checkboxChoiceRow = paragraphLooksLikeCheckboxChoiceRow(paragraph);
40750
41576
  const fallbackTabWidthPx = checkboxChoiceRow ? checkboxChoiceRowTabWidthPx(paragraph) : DEFAULT_TAB_STOP_PX;
@@ -40821,16 +41647,6 @@ function DocxEditorViewer({
40821
41647
  }
40822
41648
  });
40823
41649
  };
40824
- const noteMarkerIndexes = {
40825
- footnote: /* @__PURE__ */ new Map(),
40826
- endnote: /* @__PURE__ */ new Map()
40827
- };
40828
- editor.model.metadata.footnotes?.forEach((note, index) => {
40829
- noteMarkerIndexes.footnote.set(note.id, index + 1);
40830
- });
40831
- editor.model.metadata.endnotes?.forEach((note, index) => {
40832
- noteMarkerIndexes.endnote.set(note.id, index + 1);
40833
- });
40834
41650
  const numberingLabel = paragraphNumberingLabels.get(
40835
41651
  paragraphLocationKey(location)
40836
41652
  );
@@ -40942,17 +41758,18 @@ function DocxEditorViewer({
40942
41758
  }
40943
41759
  );
40944
41760
  }
40945
- const paragraphLineHeightPx = estimateParagraphLineHeightPx(paragraph);
41761
+ const paragraphLineHeightPx = options?.paragraphLineRange?.lineHeightPx ?? estimateParagraphLineHeightPx(paragraph);
40946
41762
  const manualDualWrappedLayout = !numberingLabel ? resolveParagraphDualWrappedTextLayout(
40947
41763
  paragraph,
40948
41764
  paragraphRenderTextWidthPx,
40949
41765
  paragraphLineHeightPx,
40950
41766
  {
41767
+ excludedImageIndex: objectWrapDragPreview?.imageKey?.startsWith(`${paragraphLocationKey(location)}:`) ? Number(objectWrapDragPreview.imageKey.split(":").at(-1)) : void 0,
40951
41768
  widthPxByImageIndex: resizedWidthPxByImageIndex,
40952
41769
  heightPxByImageIndex: resizedHeightPxByImageIndex,
40953
41770
  paragraphTopPx: paragraphPageFlowTopPx,
40954
41771
  pageMarginTopPx: paragraphPageLayout.marginsPx.top,
40955
- foreignExclusions: options?.pageFlowForeignExclusions,
41772
+ foreignExclusions: options?.pageFlowForeignExclusions?.map((exclusion) => ({ ...exclusion, top: exclusion.top + sliceTop, bottom: exclusion.bottom + sliceTop })),
40956
41773
  movePreviewByImageIndex,
40957
41774
  allowNegativeImageTop: movePreviewByImageIndex.size > 0
40958
41775
  }
@@ -40961,7 +41778,7 @@ function DocxEditorViewer({
40961
41778
  paragraph,
40962
41779
  paragraphRenderTextWidthPx,
40963
41780
  paragraphLineHeightPx,
40964
- options?.pageFlowForeignExclusions ?? []
41781
+ (options?.pageFlowForeignExclusions ?? []).map((exclusion) => ({ ...exclusion, top: exclusion.top + sliceTop, bottom: exclusion.bottom + sliceTop }))
40965
41782
  ) : void 0;
40966
41783
  const activeDualWrappedLayout = manualDualWrappedLayout ?? foreignOnlyWrappedLayout;
40967
41784
  if (activeDualWrappedLayout) {
@@ -41000,7 +41817,20 @@ function DocxEditorViewer({
41000
41817
  src: renderableImageSrc,
41001
41818
  alt: run.image.alt ?? "DOCX image",
41002
41819
  draggable: false,
41820
+ "data-docx-image-location": imageLocationKey({ ...location, childIndex: Number(/^run-(\d+)/.exec(run.key)?.[1]) }),
41821
+ onPointerDown: (event) => {
41822
+ if (!isReadOnly && run.image) beginFloatingImageMove(
41823
+ event,
41824
+ { ...location, childIndex: Number(/^run-(\d+)/.exec(run.key)?.[1]) },
41825
+ run.image,
41826
+ false,
41827
+ false
41828
+ );
41829
+ },
41003
41830
  style: {
41831
+ position: "relative",
41832
+ zIndex: 3,
41833
+ touchAction: "none",
41004
41834
  width: run.image.widthPx ? `${run.image.widthPx}px` : void 0,
41005
41835
  height: run.image.heightPx ? `${run.image.heightPx}px` : void 0,
41006
41836
  verticalAlign: "middle",
@@ -41090,7 +41920,7 @@ function DocxEditorViewer({
41090
41920
  };
41091
41921
  const renderManualImage = (geometry) => {
41092
41922
  const manualImage = previewParagraph.children[geometry.imageIndex];
41093
- if (manualImage?.type !== "image") {
41923
+ if (manualImage?.type !== "image" || !imageIsInSlice(geometry.imageIndex)) {
41094
41924
  return null;
41095
41925
  }
41096
41926
  const manualImageLocation = {
@@ -41119,7 +41949,7 @@ function DocxEditorViewer({
41119
41949
  display: "inline-block",
41120
41950
  position: "absolute",
41121
41951
  left: geometry.imageLeftPx,
41122
- top: geometry.imageTopPx,
41952
+ top: geometry.imageTopPx - sliceTop,
41123
41953
  width: widthPx ? `${widthPx}px` : void 0,
41124
41954
  height: heightPx ? `${heightPx}px` : void 0,
41125
41955
  zIndex: 3
@@ -41780,17 +42610,15 @@ function DocxEditorViewer({
41780
42610
  return child?.type === "image" && isPageOrMarginAnchoredWrappedFloatingImage(child);
41781
42611
  }).map((geometry) => geometry.imageIndex)
41782
42612
  );
41783
- const activeWrappedSource = activeSession ? buildSyntheticPretextLayoutSource(
42613
+ const activeWrappedSource = activeSession && activeSession.text !== activeDualWrappedLayout.source.text ? buildSyntheticPretextLayoutSource(
41784
42614
  activeSession.text,
41785
42615
  firstRunStyle(previewParagraph)
41786
42616
  ) : activeDualWrappedLayout.source;
41787
42617
  const activeWrappedExclusions = [
41788
42618
  ...options?.pageFlowForeignExclusions ?? [],
41789
- ...activeDualWrappedLayout.geometries.map(
41790
- (geometry) => geometry.exclusion
41791
- )
42619
+ ...activeDualWrappedLayout.geometries.map((geometry) => geometry.exclusion)
41792
42620
  ];
41793
- const activeWrappedLayout = activeSession ? layoutParagraphPretextSource(
42621
+ const activeWrappedLayout = activeWrappedSource !== activeDualWrappedLayout.source ? layoutParagraphPretextSource(
41794
42622
  previewParagraph,
41795
42623
  activeWrappedSource,
41796
42624
  paragraphRenderTextWidthPx,
@@ -41801,9 +42629,13 @@ function DocxEditorViewer({
41801
42629
  location,
41802
42630
  keyPrefix: `${keyPrefix}-dual-wrap-manual`,
41803
42631
  source: activeWrappedSource,
41804
- layout: activeWrappedLayout,
42632
+ layout: lineRange ? sliceLayoutToLineRange(
42633
+ activeWrappedLayout,
42634
+ lineRange.startLineIndex,
42635
+ lineRange.endLineIndex >= lineRange.totalLineCount ? activeWrappedLayout.lineCount : lineRange.endLineIndex
42636
+ ) : activeWrappedLayout,
41805
42637
  lineHeightPx: activeDualWrappedLayout.lineHeightPx,
41806
- blockHeightPx: resolveDragPreviewAwareWrapBlockHeightPx(
42638
+ blockHeightPx: lineRange ? void 0 : resolveDragPreviewAwareWrapBlockHeightPx(
41807
42639
  activeWrappedLayout,
41808
42640
  activeDualWrappedLayout.geometries,
41809
42641
  options?.pageFlowForeignExclusions
@@ -42090,6 +42922,12 @@ function DocxEditorViewer({
42090
42922
  contentEditable: !isReadOnly,
42091
42923
  suppressContentEditableWarning: true,
42092
42924
  spellCheck: false,
42925
+ onInput: (event) => event.stopPropagation(),
42926
+ onKeyDown: (event) => event.stopPropagation(),
42927
+ onKeyUp: (event) => event.stopPropagation(),
42928
+ onPaste: (event) => event.stopPropagation(),
42929
+ onCompositionStart: (event) => event.stopPropagation(),
42930
+ onCompositionEnd: (event) => event.stopPropagation(),
42093
42931
  onFocus: (event) => {
42094
42932
  event.stopPropagation();
42095
42933
  if (!isReadOnly) {
@@ -42469,6 +43307,7 @@ function DocxEditorViewer({
42469
43307
  {
42470
43308
  contentEditable: false,
42471
43309
  "data-docx-form-field": "true",
43310
+ "data-docx-form-field-child-index": childIndex,
42472
43311
  "data-docx-form-field-type": "checkbox",
42473
43312
  role: "checkbox",
42474
43313
  "aria-checked": Boolean(child.checked),
@@ -42547,6 +43386,7 @@ function DocxEditorViewer({
42547
43386
  {
42548
43387
  contentEditable: false,
42549
43388
  "data-docx-form-field": "true",
43389
+ "data-docx-form-field-child-index": childIndex,
42550
43390
  "data-docx-form-field-type": "dropdown",
42551
43391
  value: selectedValue,
42552
43392
  onClick: (event) => {
@@ -42603,6 +43443,7 @@ function DocxEditorViewer({
42603
43443
  {
42604
43444
  contentEditable: false,
42605
43445
  "data-docx-form-field": "true",
43446
+ "data-docx-form-field-child-index": childIndex,
42606
43447
  "data-docx-form-field-type": "date",
42607
43448
  type: compactFieldLayout ? "text" : "date",
42608
43449
  value: normalizedDate,
@@ -42647,6 +43488,7 @@ function DocxEditorViewer({
42647
43488
  {
42648
43489
  contentEditable: false,
42649
43490
  "data-docx-form-field": "true",
43491
+ "data-docx-form-field-child-index": childIndex,
42650
43492
  "data-docx-form-field-type": "date",
42651
43493
  type: "text",
42652
43494
  value: child.value ?? "",
@@ -42693,6 +43535,7 @@ function DocxEditorViewer({
42693
43535
  {
42694
43536
  contentEditable: false,
42695
43537
  "data-docx-form-field": "true",
43538
+ "data-docx-form-field-child-index": childIndex,
42696
43539
  "data-docx-form-field-type": "text",
42697
43540
  type: "text",
42698
43541
  value: child.value ?? "",
@@ -42887,6 +43730,7 @@ function DocxEditorViewer({
42887
43730
  beginImageResize,
42888
43731
  clearTableCellSelection,
42889
43732
  floatingMovePreview,
43733
+ objectWrapDragPreview,
42890
43734
  isDraggingImage,
42891
43735
  openContextMenu,
42892
43736
  onImageDragEnd,
@@ -42905,6 +43749,7 @@ function DocxEditorViewer({
42905
43749
  onFormFieldDoubleClick,
42906
43750
  isReadOnly,
42907
43751
  paragraphNumberingLabels,
43752
+ noteMarkerIndexes,
42908
43753
  scrollToBookmark,
42909
43754
  documentLayout,
42910
43755
  tocLinkColorByLevel,
@@ -43709,7 +44554,10 @@ function DocxEditorViewer({
43709
44554
  const resolvedAfterSpacingPx = Number.isFinite(
43710
44555
  options?.overrideAfterSpacingPx
43711
44556
  ) ? Math.max(0, Math.round(options?.overrideAfterSpacingPx)) : void 0;
43712
- const editable = !hasImage && !isReadOnly && options?.forceReadOnly !== true && (!hasPartialLineRange || paragraphSegmentIsActiveEditable) && !isManualPageBreakParagraph;
44557
+ const hasOnlyAbsoluteImages = hasImage && node.children.every(
44558
+ (child) => child.type !== "image" || shouldRenderAbsoluteFloatingImage(child)
44559
+ );
44560
+ const editable = (!hasImage || hasOnlyAbsoluteImages) && !isReadOnly && options?.forceReadOnly !== true && (!hasPartialLineRange || paragraphSegmentIsActiveEditable) && !isManualPageBreakParagraph;
43713
44561
  const resolvedPageLayout = options?.pageLayout ?? documentLayout;
43714
44562
  const paragraphContainsLikelyFullPageCoverImage = node.children.some(
43715
44563
  (child) => child.type === "image" && shouldRenderAbsoluteFloatingImage(child) && likelyFullPageCoverImageRelativeToContentBox(
@@ -43832,6 +44680,7 @@ function DocxEditorViewer({
43832
44680
  nodeIndex
43833
44681
  },
43834
44682
  {
44683
+ paragraphLineRange: hasPartialLineRange ? paragraphLineRange : void 0,
43835
44684
  pageFlowTopPx: paragraphPageFlowTopPx,
43836
44685
  pageFlowForeignExclusions: options?.pageFlowForeignExclusions,
43837
44686
  pageLayout: resolvedPageLayout,
@@ -43841,7 +44690,26 @@ function DocxEditorViewer({
43841
44690
  }
43842
44691
  return fallbackParagraphRuns;
43843
44692
  };
43844
- const renderedInteractiveParagraphContent = shouldRenderParagraphSegmentWithPretext ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
44693
+ const canReusePlainParagraphHtml = editable && !hasPartialLineRange && !hasImage && !hasDualWrappedFloatingImage && !isDraggingImage && !paragraphHasFormField2(node) && !node.children.some(
44694
+ (child) => child.type === "text" && child.noteReference
44695
+ ) && (options?.pageFlowForeignExclusions?.length ?? 0) === 0;
44696
+ const plainParagraphHtmlContext = canReusePlainParagraphHtml ? [
44697
+ nodeIndex,
44698
+ fontMetricsRevision,
44699
+ documentContentTheme,
44700
+ editor.model.metadata.numberingDefinitions,
44701
+ numberingLabel?.text,
44702
+ numberingLabel?.imageSrc,
44703
+ paragraphRenderTextWidthPx,
44704
+ commentsEnabled,
44705
+ editor.model.metadata.comments,
44706
+ JSON.stringify(tocLinkColorByLevel)
44707
+ ] : void 0;
44708
+ const cachedPlainParagraph = canReusePlainParagraphHtml ? plainParagraphHtmlCacheRef.current.get(node) : void 0;
44709
+ const reusablePlainParagraph = cachedPlainParagraph && plainParagraphHtmlContext?.every(
44710
+ (value, index) => Object.is(value, cachedPlainParagraph.context[index])
44711
+ ) ? cachedPlainParagraph : void 0;
44712
+ const renderedInteractiveParagraphContent = reusablePlainParagraph ? null : shouldRenderParagraphSegmentWithPretext && hasDualWrappedFloatingImage ? getFallbackParagraphRuns() : shouldRenderParagraphSegmentWithPretext ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
43845
44713
  "div",
43846
44714
  {
43847
44715
  style: {
@@ -43886,20 +44754,25 @@ function DocxEditorViewer({
43886
44754
  )
43887
44755
  }
43888
44756
  ) : getFallbackParagraphRuns();
43889
- const renderedEditableParagraphHtml = renderStaticHtml(
43890
- renderedInteractiveParagraphContent
43891
- );
44757
+ const renderedEditableParagraphHtml = reusablePlainParagraph?.html ?? (editable && !hasImage ? renderStaticHtml(renderedInteractiveParagraphContent) : "");
44758
+ if (plainParagraphHtmlContext && !reusablePlainParagraph) {
44759
+ plainParagraphHtmlCacheRef.current.set(node, {
44760
+ context: plainParagraphHtmlContext,
44761
+ html: renderedEditableParagraphHtml
44762
+ });
44763
+ }
43892
44764
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
43893
44765
  "div",
43894
44766
  {
43895
44767
  "data-docx-paragraph-host": "true",
43896
44768
  "data-docx-paragraph-kind": "paragraph",
43897
44769
  "data-docx-paragraph-node-index": nodeIndex,
44770
+ "data-docx-paragraph-line-height": paragraphSegmentLineHeightPx,
43898
44771
  "data-docx-paragraph-start-line": paragraphSegmentStartLine,
43899
44772
  "data-docx-paragraph-end-line": paragraphSegmentEndLine,
43900
44773
  "data-docx-paragraph-partial-line-range": hasPartialLineRange ? "true" : void 0,
43901
44774
  style: paragraphStyle,
43902
- dangerouslySetInnerHTML: editable ? stableInnerHtmlProp(
44775
+ dangerouslySetInnerHTML: editable && !hasImage ? stableInnerHtmlProp(
43903
44776
  editableParagraphHtmlPropsRef.current,
43904
44777
  nodeIndex,
43905
44778
  renderedEditableParagraphHtml
@@ -44473,7 +45346,7 @@ ${currentText.slice(end)}`;
44473
45346
  }
44474
45347
  }
44475
45348
  },
44476
- children: !editable ? renderedInteractiveParagraphContent : null
45349
+ children: !editable || hasImage ? renderedInteractiveParagraphContent : null
44477
45350
  },
44478
45351
  paragraphLineRange ? `epoch-${paragraphStructureEpoch}-node-${nodeIndex}-lines-${paragraphSegmentStartLine}-${paragraphSegmentEndLine}${options?.syntheticKeySuffix ? `-${options.syntheticKeySuffix}` : ""}` : `epoch-${paragraphStructureEpoch}-node-${nodeIndex}${options?.syntheticKeySuffix ? `-${options.syntheticKeySuffix}` : ""}`
44479
45352
  );
@@ -44482,7 +45355,7 @@ ${currentText.slice(end)}`;
44482
45355
  const tableRowSliceTopBleedPxForWrapper = tableRowSlice && tableRowSliceOffsetPxForWrapper > 0 ? TABLE_ROW_SLICE_VISUAL_BLEED_PX : 0;
44483
45356
  const tableRowSliceBottomBleedPxForWrapper = tableRowSlice ? TABLE_ROW_SLICE_VISUAL_BLEED_PX : 0;
44484
45357
  const floatingTablePageLayout = options?.pageLayout ?? documentLayout;
44485
- const floatingTableGeometry = node.style?.floating && !tableRowRange && !tableRowSlice ? resolveFloatingTableGeometry(node, nodeContentWidthPx, {
45358
+ const floatingTableGeometry = (node.style?.floating || objectWrapDragPreview?.tableIndex === nodeIndex) && !tableRowRange && !tableRowSlice ? resolveFloatingTableGeometry(node.style?.floating ? node : { ...node, style: { ...node.style, floating: {} } }, nodeContentWidthPx, {
44486
45359
  tableWidthPx: estimateFloatingTableWidthPx(
44487
45360
  node,
44488
45361
  nodeContentWidthPx
@@ -44503,6 +45376,16 @@ ${currentText.slice(end)}`;
44503
45376
  }
44504
45377
  if (element) {
44505
45378
  tableElementsRef.current.set(nodeIndex, element);
45379
+ if (floatingTableGeometry) element.style.top = `${floatingTableGeometry.topPx}px`;
45380
+ const anchor = node.style?.floating?.verticalAnchor;
45381
+ const yPx = twipsToSignedPixels(node.style?.floating?.yTwips);
45382
+ const surface = element.closest("[data-docx-page-surface='true']");
45383
+ if (floatingTableGeometry && surface && element.parentElement && yPx !== void 0 && (anchor === "page" || anchor === "margin")) {
45384
+ const scale = resolveViewerMeasurementZoomScale(element, 1);
45385
+ const origin = surface.getBoundingClientRect().top;
45386
+ const hostTop = element.parentElement.getBoundingClientRect().top;
45387
+ element.style.top = `${(origin - hostTop) / scale + yPx + (anchor === "margin" ? floatingTablePageLayout.marginsPx.top : 0)}px`;
45388
+ }
44506
45389
  } else {
44507
45390
  tableElementsRef.current.delete(nodeIndex);
44508
45391
  }
@@ -44518,11 +45401,6 @@ ${currentText.slice(end)}`;
44518
45401
  node,
44519
45402
  twipsToSignedPixels(node.style?.indentTwips) ?? 0
44520
45403
  ),
44521
- ...tableFloatingDragPreview?.tableIndex === nodeIndex ? {
44522
- transform: `translate(${tableFloatingDragPreview.deltaX}px, ${tableFloatingDragPreview.deltaY}px)`,
44523
- zIndex: 30,
44524
- opacity: 0.9
44525
- } : void 0,
44526
45404
  ...tableRowSlice ? {
44527
45405
  height: Math.max(
44528
45406
  MIN_PARAGRAPH_LINE_HEIGHT_PX,
@@ -45180,7 +46058,10 @@ ${currentText.slice(end)}`;
45180
46058
  if (!cellEditorElement || !cellEditorElement.isConnected) {
45181
46059
  return;
45182
46060
  }
45183
- cellEditorElement.focus();
46061
+ if (document.activeElement === cellEditorElement) {
46062
+ return;
46063
+ }
46064
+ cellEditorElement.focus({ preventScroll: true });
45184
46065
  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]`;
45185
46066
  const paragraphElements2 = Array.from(
45186
46067
  cellEditorElement.querySelectorAll(
@@ -45469,12 +46350,12 @@ ${currentText.slice(end)}`;
45469
46350
  return;
45470
46351
  }
45471
46352
  }
45472
- window.requestAnimationFrame(() => {
46353
+ const applyPendingFocus = () => {
45473
46354
  const latestPendingFocus = pendingTableCellFocusRef.current;
45474
46355
  if (!latestPendingFocus || latestPendingFocus.cellElementKey !== cellElementKey || !element.isConnected) {
45475
46356
  return;
45476
46357
  }
45477
- element.focus();
46358
+ element.focus({ preventScroll: true });
45478
46359
  const boundary = latestPendingFocus.boundary;
45479
46360
  const boundaryInCurrentCell = Boolean(
45480
46361
  boundary && boundary.location.kind === "table-cell" && boundary.location.tableIndex === nodeIndex && boundary.location.rowIndex === rowIndex && boundary.location.cellIndex === cellIndex
@@ -45536,7 +46417,8 @@ ${currentText.slice(end)}`;
45536
46417
  cellIndex
45537
46418
  );
45538
46419
  pendingTableCellFocusRef.current = void 0;
45539
- });
46420
+ };
46421
+ applyPendingFocus();
45540
46422
  }
45541
46423
  },
45542
46424
  onInput: (event) => {
@@ -46462,11 +47344,28 @@ ${currentText.slice(end)}`;
46462
47344
  }
46463
47345
  return renderedTableBlock;
46464
47346
  };
47347
+ const objectDragPageRenderCacheRef = React.useRef(
47348
+ /* @__PURE__ */ new Map()
47349
+ );
47350
+ if (!objectWrapDragPreview) objectDragPageRenderCacheRef.current.clear();
46465
47351
  const renderNodeSegments = React.useCallback(
46466
47352
  (nodeSegments, options) => {
47353
+ const dragSourceIndex = objectWrapDragPreview?.tableIndex ?? (objectWrapDragPreview?.imageKey?.startsWith("p:") ? Number(objectWrapDragPreview.imageKey.split(":")[1]) : -1);
47354
+ const dragPagePreview = objectWrapDragPreview?.bandsByPage.has(
47355
+ options?.pageIndex ?? -1
47356
+ ) ? objectWrapDragPreview : objectWrapDragPreview && nodeSegments.some((segment) => segment.nodeIndex === dragSourceIndex) ? "source" : void 0;
47357
+ const cacheKey = JSON.stringify([
47358
+ options?.pageIndex,
47359
+ nodeSegments,
47360
+ options?.pageLayout
47361
+ ]);
47362
+ const cached = objectDragPageRenderCacheRef.current.get(cacheKey);
47363
+ if (objectWrapDragPreview && cached?.model === editor.model && cached.fontRevision === fontMetricsRevision && cached.theme === documentContentTheme && cached.preview === dragPagePreview)
47364
+ return cached.rendered;
46467
47365
  const rendered = [];
46468
47366
  let pageFlowTopPx = 0;
46469
47367
  const resolvedPageLayout = options?.pageLayout ?? documentLayout;
47368
+ const liveWrapBands = objectWrapDragPreview?.bandsByPage.get(options?.pageIndex ?? -1) ?? measuredWrapBands;
46470
47369
  const foreignWrapExclusionsBySegmentIndex = precomputePageSegmentForeignWrapExclusions(
46471
47370
  nodeSegments,
46472
47371
  editor.model,
@@ -46478,7 +47377,10 @@ ${currentText.slice(end)}`;
46478
47377
  {
46479
47378
  floatingMovePreview,
46480
47379
  resizePreview,
46481
- measuredWrapBands
47380
+ measuredWrapBands: liveWrapBands,
47381
+ draggedImageKey: objectWrapDragPreview?.imageKey,
47382
+ draggedTableIndex: objectWrapDragPreview?.tableIndex,
47383
+ dragObstacle: objectWrapDragPreview?.pageIndex === options?.pageIndex ? objectWrapDragPreview?.obstacle : void 0
46482
47384
  }
46483
47385
  );
46484
47386
  for (let offset = 0; offset < nodeSegments.length; offset += 1) {
@@ -47132,7 +48034,7 @@ ${currentText.slice(end)}`;
47132
48034
  }
47133
48035
  }
47134
48036
  const segmentParagraphLineRange = segment.paragraphLineRange;
47135
- const measuredSegmentFlowTopPx = node.type === "paragraph" && (!segmentParagraphLineRange || segmentParagraphLineRange.startLineIndex === 0 && segmentParagraphLineRange.endLineIndex >= segmentParagraphLineRange.totalLineCount) ? measuredWrapBands.get(`para:${nodeIndex}`)?.topPx : void 0;
48037
+ 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;
47136
48038
  rendered.push(
47137
48039
  renderDocumentNode(
47138
48040
  node,
@@ -47157,10 +48059,19 @@ ${currentText.slice(end)}`;
47157
48059
  docGridLinePitchPxByNodeIndex.get(nodeIndex)
47158
48060
  );
47159
48061
  }
48062
+ objectDragPageRenderCacheRef.current.set(cacheKey, {
48063
+ model: editor.model,
48064
+ fontRevision: fontMetricsRevision,
48065
+ theme: documentContentTheme,
48066
+ preview: dragPagePreview,
48067
+ rendered
48068
+ });
47160
48069
  return rendered;
47161
48070
  },
47162
48071
  [
47163
48072
  activeEditableParagraphSegment,
48073
+ fontMetricsRevision,
48074
+ documentContentTheme,
47164
48075
  beginDropCapMove,
47165
48076
  beginDropCapResize,
47166
48077
  clearObjectSelectionForParagraphEntry,
@@ -47171,6 +48082,7 @@ ${currentText.slice(end)}`;
47171
48082
  dropCapResizePreview,
47172
48083
  editor,
47173
48084
  floatingMovePreview,
48085
+ objectWrapDragPreview,
47174
48086
  focusDropCapWrappedParagraph,
47175
48087
  hoveredDropCapNodeIndex,
47176
48088
  isReadOnly,
@@ -47383,6 +48295,7 @@ ${currentText.slice(end)}`;
47383
48295
  onDragOver: onCanvasDragOver,
47384
48296
  onDragLeave: onCanvasDragLeave,
47385
48297
  onDrop: (event) => void onCanvasDrop(event),
48298
+ onDoubleClickCapture: onViewerFormFieldDoubleClick,
47386
48299
  onPointerDownCapture: onViewerPointerDown,
47387
48300
  onPointerMoveCapture: onViewerPointerMove,
47388
48301
  onPointerLeave: onViewerPointerLeave,
@@ -47895,6 +48808,7 @@ ${currentText.slice(end)}`;
47895
48808
  ref: (element) => {
47896
48809
  if (element) {
47897
48810
  pageBodyElementsRef.current.set(pageIndex, element);
48811
+ element.dataset.docxPageBody = "true";
47898
48812
  } else {
47899
48813
  pageBodyElementsRef.current.delete(pageIndex);
47900
48814
  }
@@ -48071,6 +48985,7 @@ ${currentText.slice(end)}`;
48071
48985
  React.Fragment,
48072
48986
  {
48073
48987
  children: renderNodeSegments(group.segments, {
48988
+ pageIndex,
48074
48989
  pageLayout,
48075
48990
  suppressLikelyFullPageCoverImageKeys: suppressedLikelyFullPageCoverImageKeys
48076
48991
  })
@@ -49601,7 +50516,7 @@ async function parseDocxForViewer(source, options = {}) {
49601
50516
  const readFinishedAt = now();
49602
50517
  const importResult = await importDocxBuffer(buffer, {
49603
50518
  signal: options.signal,
49604
- transferBuffer: options.transferBuffer,
50519
+ transferBuffer: options.transferBuffer ?? !(source instanceof ArrayBuffer),
49605
50520
  useWorker: options.useWorker
49606
50521
  });
49607
50522
  const importFinishedAt = now();
@@ -49618,6 +50533,9 @@ async function parseDocxForViewer(source, options = {}) {
49618
50533
  }
49619
50534
  });
49620
50535
  }
50536
+ if (faces.length > 0) {
50537
+ invalidateFontMetrics();
50538
+ }
49621
50539
  faces = [];
49622
50540
  };
49623
50541
  const loadEmbeddedFonts = () => {
@@ -49651,6 +50569,9 @@ async function parseDocxForViewer(source, options = {}) {
49651
50569
  )
49652
50570
  )).filter((face) => Boolean(face));
49653
50571
  loaded.forEach((face) => document.fonts.add(face));
50572
+ if (loaded.length > 0) {
50573
+ invalidateFontMetrics();
50574
+ }
49654
50575
  faces = loaded;
49655
50576
  try {
49656
50577
  await document.fonts.ready;
@@ -49691,8 +50612,8 @@ async function parseDocxForViewer(source, options = {}) {
49691
50612
  }
49692
50613
 
49693
50614
  // src/index.tsx
49694
- init_src2();
49695
50615
  init_src3();
50616
+ init_src2();
49696
50617
 
49697
50618
  // ../layout-core/src/index.ts
49698
50619
  init_cjs_shims();
@@ -51245,7 +52166,8 @@ function useDocxModel(file) {
51245
52166
  try {
51246
52167
  const { model } = await importDocxBuffer(docxFile, {
51247
52168
  signal: abortController.signal,
51248
- transferBuffer: false
52169
+ transferBuffer: false,
52170
+ useWorker: "required"
51249
52171
  });
51250
52172
  if (!isCurrent) {
51251
52173
  return;