@ansonlai/docx-redline-js 0.8.1 → 0.8.3

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.
@@ -1,4 +1,4 @@
1
- // @ansonlai/docx-redline-js standalone bundle v0.8.1 — https://github.com/AnsonLai/docx-redline-js
1
+ // @ansonlai/docx-redline-js standalone bundle v0.8.3 — https://github.com/AnsonLai/docx-redline-js
2
2
  var __create = Object.create;
3
3
  var __defProp = Object.defineProperty;
4
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
@@ -9001,15 +9001,29 @@ function createDiffEngine(options = {}) {
9001
9001
  engine.Diff_Timeout = timeout;
9002
9002
  return engine;
9003
9003
  }
9004
- function tokenize(text2) {
9004
+ function tokenize(text2, atomicChars = null) {
9005
9005
  const tokens = [];
9006
+ const pushWord = (word) => {
9007
+ if (!atomicChars || atomicChars.size === 0) {
9008
+ tokens.push(word);
9009
+ return;
9010
+ }
9011
+ let start = 0;
9012
+ for (let index = 0; index < word.length; index++) {
9013
+ if (!atomicChars.has(word[index])) continue;
9014
+ if (index > start) tokens.push(word.slice(start, index));
9015
+ tokens.push(word[index]);
9016
+ start = index + 1;
9017
+ }
9018
+ if (start < word.length) tokens.push(word.slice(start));
9019
+ };
9006
9020
  const leading = text2.match(/^\s+/);
9007
9021
  if (leading) tokens.push(leading[0]);
9008
9022
  const regex = /(\S+)(\s*)/g;
9009
9023
  regex.lastIndex = leading?.[0].length || 0;
9010
9024
  let match;
9011
9025
  while ((match = regex.exec(text2)) !== null) {
9012
- if (match[1]) tokens.push(match[1]);
9026
+ if (match[1]) pushWord(match[1]);
9013
9027
  if (match[2]) tokens.push(match[2]);
9014
9028
  }
9015
9029
  return tokens;
@@ -9037,8 +9051,8 @@ function wordsToChars(text1, text2, options = {}) {
9037
9051
  }
9038
9052
  return { chars, tokenIds };
9039
9053
  }
9040
- const tokens1 = tokenize(text1);
9041
- const tokens2 = tokenize(text2);
9054
+ const tokens1 = tokenize(text1, options.atomicChars);
9055
+ const tokens2 = tokenize(text2, options.atomicChars);
9042
9056
  const encoded1 = mapTokensToChars(tokens1);
9043
9057
  const encoded2 = mapTokensToChars(tokens2);
9044
9058
  return {
@@ -10438,7 +10452,37 @@ async function executeListGeneration(options) {
10438
10452
  }];
10439
10453
  }
10440
10454
  }
10441
- if (generateRedlines && deletionRuns.length > 0) {
10455
+ const sourceParagraphCount = (originalRunModel || []).filter((run) => run.kind === RunKind.PARAGRAPH_START).length;
10456
+ if (generateRedlines && sourceParagraphCount > 0 && (sourceParagraphCount > 1 || deletionRuns.length === 0)) {
10457
+ let segment = null;
10458
+ const flushSegment = () => {
10459
+ if (!segment) return;
10460
+ results.push(serializeToOoxml(segment, null, [], {
10461
+ author,
10462
+ generateRedlines,
10463
+ revisionIdAllocator
10464
+ }));
10465
+ segment = null;
10466
+ };
10467
+ for (const run of originalRunModel) {
10468
+ if (run.kind === RunKind.PARAGRAPH_START) {
10469
+ flushSegment();
10470
+ segment = [{
10471
+ ...run,
10472
+ pPrElement: null,
10473
+ pPrXml: addParagraphMarkRevision(
10474
+ run.pPrElement || run.pPrXml || null,
10475
+ "del",
10476
+ author,
10477
+ revisionIdAllocator
10478
+ )
10479
+ }];
10480
+ } else if (segment && (run.kind === "text" || run.kind === "run")) {
10481
+ segment.push({ ...run, kind: "deletion", author });
10482
+ }
10483
+ }
10484
+ flushSegment();
10485
+ } else if (generateRedlines && deletionRuns.length > 0) {
10442
10486
  const deletedPPr = addParagraphMarkRevision(sourcePPr, "del", author, revisionIdAllocator);
10443
10487
  const deletedParagraph = serializeToOoxml(deletionRuns, deletedPPr, [], {
10444
10488
  author,
@@ -14443,7 +14487,11 @@ function applyReconstructionMode(xmlDoc, originalText, modifiedText, serializer,
14443
14487
  if (mapping.paragraphs.length === 0) {
14444
14488
  return withOoxmlSourceType({ oxml: serializer.serializeToString(xmlDoc), hasChanges: false });
14445
14489
  }
14446
- const diffs = computeWordDiffs(mapping.originalFullText, mapping.processedModifiedText, diffOptions);
14490
+ const wordDiffs = computeWordDiffs(mapping.originalFullText, mapping.processedModifiedText, {
14491
+ ...diffOptions,
14492
+ atomicChars: new Set(mapping.referenceMap.keys())
14493
+ });
14494
+ const diffs = keepUnchangedTextOutsideWrappers(anchorSharedReferences(wordDiffs, mapping.referenceMap), mapping);
14447
14495
  return withOoxmlSourceType(applyReconstructionDiffs(
14448
14496
  xmlDoc,
14449
14497
  diffs,
@@ -14455,6 +14503,109 @@ function applyReconstructionMode(xmlDoc, originalText, modifiedText, serializer,
14455
14503
  options
14456
14504
  ));
14457
14505
  }
14506
+ function forEachChangeGroup(diffs, onEqual, onGroup) {
14507
+ let index = 0;
14508
+ while (index < diffs.length) {
14509
+ if (diffs[index][0] === 0) {
14510
+ onEqual(diffs[index]);
14511
+ index++;
14512
+ continue;
14513
+ }
14514
+ const group = [];
14515
+ while (index < diffs.length && diffs[index][0] !== 0) group.push(diffs[index++]);
14516
+ onGroup(
14517
+ group,
14518
+ group.filter(([op]) => op === -1).map(([, text2]) => text2).join(""),
14519
+ group.filter(([op]) => op === 1).map(([, text2]) => text2).join("")
14520
+ );
14521
+ }
14522
+ }
14523
+ function pushChange(result, deleted, inserted) {
14524
+ if (deleted === inserted) {
14525
+ if (deleted) result.push([0, deleted]);
14526
+ return;
14527
+ }
14528
+ if (deleted) result.push([-1, deleted]);
14529
+ if (inserted) result.push([1, inserted]);
14530
+ }
14531
+ function anchorSharedReferences(diffs, referenceMap) {
14532
+ if (referenceMap.size === 0) return diffs;
14533
+ const result = [];
14534
+ forEachChangeGroup(diffs, (part) => result.push(part), (group, deleted, inserted) => {
14535
+ const deletedRefs = [...deleted].filter((char) => referenceMap.has(char) && inserted.includes(char));
14536
+ const insertedRefs = [...inserted].filter((char) => deletedRefs.includes(char));
14537
+ if (deletedRefs.length === 0 || deletedRefs.join("") !== insertedRefs.join("")) {
14538
+ result.push(...group);
14539
+ return;
14540
+ }
14541
+ let deletedStart = 0;
14542
+ let insertedStart = 0;
14543
+ for (const ref of deletedRefs) {
14544
+ const deletedAt = deleted.indexOf(ref, deletedStart);
14545
+ const insertedAt = inserted.indexOf(ref, insertedStart);
14546
+ pushChange(result, deleted.slice(deletedStart, deletedAt), inserted.slice(insertedStart, insertedAt));
14547
+ result.push([0, ref]);
14548
+ deletedStart = deletedAt + 1;
14549
+ insertedStart = insertedAt + 1;
14550
+ }
14551
+ pushChange(result, deleted.slice(deletedStart), inserted.slice(insertedStart));
14552
+ });
14553
+ return result;
14554
+ }
14555
+ function commonPrefixLength(left, right) {
14556
+ const limit = Math.min(left.length, right.length);
14557
+ let length = 0;
14558
+ while (length < limit && left[length] === right[length]) length++;
14559
+ return length;
14560
+ }
14561
+ function commonSuffixLength(left, right, limit) {
14562
+ let length = 0;
14563
+ while (length < limit && left[left.length - 1 - length] === right[right.length - 1 - length]) length++;
14564
+ return length;
14565
+ }
14566
+ function keepUnchangedTextOutsideWrappers(diffs, mapping) {
14567
+ const wrapperAt = (index) => mapping.getRunProperties(index).wrapper || null;
14568
+ const result = [];
14569
+ let originalOffset = 0;
14570
+ forEachChangeGroup(diffs, (part) => {
14571
+ result.push(part);
14572
+ originalOffset += part[1].length;
14573
+ }, (group, deleted, inserted) => {
14574
+ const start = originalOffset;
14575
+ const end = start + deleted.length;
14576
+ originalOffset = end;
14577
+ const isWrapperBoundary = (index) => index > start && index < end && wrapperAt(index - 1) !== wrapperAt(index);
14578
+ let prefix = 0;
14579
+ let suffix = 0;
14580
+ if (deleted && inserted) {
14581
+ const sharedSuffix = commonSuffixLength(deleted, inserted, Math.min(deleted.length, inserted.length));
14582
+ for (let length = sharedSuffix; length > 0; length--) {
14583
+ if (isWrapperBoundary(end - length)) {
14584
+ suffix = length;
14585
+ break;
14586
+ }
14587
+ }
14588
+ const sharedPrefix = Math.min(
14589
+ commonPrefixLength(deleted, inserted),
14590
+ Math.min(deleted.length, inserted.length) - suffix
14591
+ );
14592
+ for (let length = sharedPrefix; length > 0; length--) {
14593
+ if (isWrapperBoundary(start + length)) {
14594
+ prefix = length;
14595
+ break;
14596
+ }
14597
+ }
14598
+ }
14599
+ if (prefix === 0 && suffix === 0) {
14600
+ result.push(...group);
14601
+ return;
14602
+ }
14603
+ if (prefix > 0) result.push([0, deleted.slice(0, prefix)]);
14604
+ pushChange(result, deleted.slice(prefix, deleted.length - suffix), inserted.slice(prefix, inserted.length - suffix));
14605
+ if (suffix > 0) result.push([0, deleted.slice(deleted.length - suffix)]);
14606
+ });
14607
+ return result;
14608
+ }
14458
14609
 
14459
14610
  // engine/table-mode.js
14460
14611
  function noChanges(serializer, xmlDoc) {
@@ -16529,12 +16680,12 @@ function validateParagraphBoundaryMutation(targetParagraph, modifiedText, option
16529
16680
  }
16530
16681
 
16531
16682
  // core/list-targeting.js
16532
- function getFirstDescendantByLocalName(node, localName2) {
16533
- if (!node || typeof node.getElementsByTagNameNS !== "function") return null;
16534
- const namespaced = node.getElementsByTagNameNS(WORD_MAIN_NS, localName2);
16535
- if (namespaced.length > 0) return namespaced[0];
16536
- const anyNs = node.getElementsByTagNameNS("*", localName2);
16537
- return anyNs.length > 0 ? anyNs[0] : null;
16683
+ function getDirectChildByLocalName(node, localName2) {
16684
+ if (!node) return null;
16685
+ for (const child of Array.from(node.childNodes || [])) {
16686
+ if (child?.nodeType === 1 && child.localName === localName2) return child;
16687
+ }
16688
+ return null;
16538
16689
  }
16539
16690
  function readValAttribute(element) {
16540
16691
  if (!element) return null;
@@ -16651,15 +16802,15 @@ function buildListEntriesForInsertion(parsedItems, normalizedTargetText, anchorL
16651
16802
  }
16652
16803
  function getParagraphListInfo(paragraph) {
16653
16804
  if (!paragraph) return null;
16654
- const pPr = getFirstDescendantByLocalName(paragraph, "pPr");
16805
+ const pPr = getDirectChildByLocalName(paragraph, "pPr");
16655
16806
  if (!pPr) return null;
16656
- const numPr = getFirstDescendantByLocalName(pPr, "numPr");
16807
+ const numPr = getDirectChildByLocalName(pPr, "numPr");
16657
16808
  if (!numPr) return null;
16658
- const numIdEl = getFirstDescendantByLocalName(numPr, "numId");
16809
+ const numIdEl = getDirectChildByLocalName(numPr, "numId");
16659
16810
  if (!numIdEl) return null;
16660
16811
  const numId = readValAttribute(numIdEl);
16661
16812
  if (!numId || numId === "0") return null;
16662
- const ilvlEl = getFirstDescendantByLocalName(numPr, "ilvl");
16813
+ const ilvlEl = getDirectChildByLocalName(numPr, "ilvl");
16663
16814
  const ilvlRaw = readValAttribute(ilvlEl);
16664
16815
  const ilvl = Number.parseInt(ilvlRaw || "0", 10);
16665
16816
  return {
@@ -17069,8 +17220,13 @@ function enforceListBindingOnParagraphNodes(nodes, options = {}) {
17069
17220
  }
17070
17221
  return updated;
17071
17222
  }
17223
+ function parseOxmlFragment(oxml) {
17224
+ const text2 = String(oxml || "");
17225
+ const declared = /xmlns:w\s*=/.test(text2);
17226
+ return parseOoxmlSafe(declared ? text2 : `<w:root xmlns:w="${NS_W}">${text2}</w:root>`, "application/xml").doc;
17227
+ }
17072
17228
  function getFirstParagraphFromOxml(oxml) {
17073
- const doc = parseOoxmlSafe(oxml, "application/xml").doc;
17229
+ const doc = parseOxmlFragment(oxml);
17074
17230
  if (!doc) return null;
17075
17231
  const parseError = getXmlParseError(doc);
17076
17232
  if (parseError) return null;
@@ -17097,17 +17253,17 @@ function setElementVal(element, value) {
17097
17253
  element.setAttribute("w:val", String(value));
17098
17254
  }
17099
17255
  function extractFirstParagraphNumIdFromOxml(oxml) {
17100
- const doc = parseOoxmlSafe(oxml, "application/xml").doc;
17256
+ const doc = parseOxmlFragment(oxml);
17101
17257
  if (!doc) return null;
17102
17258
  const parseError = getXmlParseError(doc);
17103
17259
  if (parseError) return null;
17104
17260
  const paragraphs = getDocumentParagraphNodes(doc);
17105
- const firstParagraph = paragraphs[0] || null;
17106
- if (!firstParagraph) return null;
17107
- const numIdNodes = Array.from(firstParagraph.getElementsByTagNameNS("*", "numId"));
17108
- for (const numIdNode of numIdNodes) {
17109
- const numId = getElementId(numIdNode, ["w:val", "val"]);
17110
- if (numId != null) return String(numId);
17261
+ for (const paragraph of paragraphs) {
17262
+ const numIdNodes = Array.from(paragraph.getElementsByTagNameNS("*", "numId"));
17263
+ for (const numIdNode of numIdNodes) {
17264
+ const numId = getElementId(numIdNode, ["w:val", "val"]);
17265
+ if (numId != null) return String(numId);
17266
+ }
17111
17267
  }
17112
17268
  return null;
17113
17269
  }
@@ -17603,6 +17759,8 @@ function createDynamicNumberingIdState(numberingXml, options = {}) {
17603
17759
  const baseNumId = Math.max(minId, maxUsedNumId + 1);
17604
17760
  const baseAbstractNumId = Math.max(minId, maxUsedAbstractNumId + 1);
17605
17761
  return {
17762
+ // Kept so list edits can recognise which existing definition a source list uses and continue it.
17763
+ sourceNumberingXml: String(numberingXml || ""),
17606
17764
  nextNumId: nextAvailableId(baseNumId, usedNumIds, maxPreferred),
17607
17765
  nextAbstractNumId: nextAvailableId(baseAbstractNumId, usedAbstractNumIds, maxPreferred),
17608
17766
  usedNumIds,
@@ -17743,6 +17901,82 @@ function buildExplicitDecimalMultilevelNumberingXml(numId, abstractNumId, startA
17743
17901
  </w:num>
17744
17902
  </w:numbering>`.trim();
17745
17903
  }
17904
+ function isTrackedDeletedParagraph(node) {
17905
+ if (!node || node.nodeType !== 1 || node.localName !== "p") return false;
17906
+ const pPr = Array.from(node.childNodes || []).find((child) => isDirectWordChild(child, "pPr"));
17907
+ const rPr = pPr && Array.from(pPr.childNodes || []).find((child) => isDirectWordChild(child, "rPr"));
17908
+ return !!rPr && Array.from(rPr.childNodes || []).some((child) => isDirectWordChild(child, "del"));
17909
+ }
17910
+ function paragraphNumIdNodes(node) {
17911
+ return Array.from(node?.getElementsByTagNameNS?.("*", "numId") || []).filter((numIdNode) => numIdNode.parentNode?.localName === "numPr");
17912
+ }
17913
+ function levelFormats(abstractNum) {
17914
+ const formats = /* @__PURE__ */ new Map();
17915
+ for (const lvl of Array.from(abstractNum?.getElementsByTagNameNS?.("*", "lvl") || [])) {
17916
+ const ilvl = getElementId2(lvl, ["w:ilvl", "ilvl"]);
17917
+ const fmt = Array.from(lvl.getElementsByTagNameNS("*", "numFmt"))[0]?.getAttribute("w:val") || null;
17918
+ if (ilvl != null && fmt) formats.set(ilvl, fmt);
17919
+ }
17920
+ return formats;
17921
+ }
17922
+ function normalizeListFormat(format) {
17923
+ return format === "bullet" || format === "circle" || format === "square" ? "bullet" : format;
17924
+ }
17925
+ function abstractNumForNumId(numberingDoc, numId) {
17926
+ const num = Array.from(numberingDoc.getElementsByTagNameNS("*", "num")).find((candidate) => getElementId2(candidate, ["w:numId", "numId"]) === numId);
17927
+ const abstractId = num && getElementId2(Array.from(num.getElementsByTagNameNS("*", "abstractNumId"))[0], ["w:val", "val"]);
17928
+ if (abstractId == null) return null;
17929
+ return Array.from(numberingDoc.getElementsByTagNameNS("*", "abstractNum")).find((candidate) => getElementId2(candidate, ["w:abstractNumId", "abstractNumId"]) === abstractId) || null;
17930
+ }
17931
+ function reuseSourceListNumbering(numberingXml, replacementNodes, sourceParagraphs, numberingIdState) {
17932
+ const sourceXml = numberingIdState?.sourceNumberingXml;
17933
+ if (!numberingXml || !String(sourceXml || "").trim() || !Array.isArray(replacementNodes)) return null;
17934
+ const sourceNumIds = /* @__PURE__ */ new Set();
17935
+ for (const paragraph of sourceParagraphs || []) {
17936
+ for (const numIdNode of paragraphNumIdNodes(paragraph)) {
17937
+ const id = getElementId2(numIdNode, ["w:val", "val"]);
17938
+ if (id != null && id !== 0) sourceNumIds.add(id);
17939
+ }
17940
+ }
17941
+ if (sourceNumIds.size !== 1) return null;
17942
+ const [sourceNumId] = sourceNumIds;
17943
+ const sourceDoc = parseOoxml(sourceXml);
17944
+ const payloadDoc = parseOoxml(numberingXml);
17945
+ if (hasXmlParseError(sourceDoc) || hasXmlParseError(payloadDoc)) return null;
17946
+ const sourceAbstract = abstractNumForNumId(sourceDoc, sourceNumId);
17947
+ if (!sourceAbstract) return null;
17948
+ const sourceFormats = levelFormats(sourceAbstract);
17949
+ const newParagraphNumIds = [];
17950
+ for (const node of replacementNodes) {
17951
+ if (isTrackedDeletedParagraph(node)) continue;
17952
+ for (const numIdNode of paragraphNumIdNodes(node)) {
17953
+ const ilvlNode = Array.from(numIdNode.parentNode.childNodes || []).find((child) => child.localName === "ilvl");
17954
+ newParagraphNumIds.push({
17955
+ numId: getElementId2(numIdNode, ["w:val", "val"]),
17956
+ ilvl: ilvlNode ? getElementId2(ilvlNode, ["w:val", "val"]) ?? 0 : 0
17957
+ });
17958
+ }
17959
+ }
17960
+ if (newParagraphNumIds.length === 0) return null;
17961
+ const payloadFormats = /* @__PURE__ */ new Map();
17962
+ for (const { numId, ilvl } of newParagraphNumIds) {
17963
+ if (numId == null) return null;
17964
+ if (!payloadFormats.has(numId)) {
17965
+ const abstract = abstractNumForNumId(payloadDoc, numId);
17966
+ if (!abstract) return null;
17967
+ payloadFormats.set(numId, levelFormats(abstract));
17968
+ }
17969
+ const generated = payloadFormats.get(numId).get(ilvl);
17970
+ const existing = sourceFormats.get(ilvl);
17971
+ if (!generated || !existing || normalizeListFormat(generated) !== normalizeListFormat(existing)) return null;
17972
+ }
17973
+ const clonedNodes = replacementNodes.map((node) => node?.cloneNode ? node.cloneNode(true) : node);
17974
+ for (const node of clonedNodes) {
17975
+ if (isTrackedDeletedParagraph(node)) continue;
17976
+ for (const numIdNode of paragraphNumIdNodes(node)) setElementVal2(numIdNode, sourceNumId);
17977
+ }
17978
+ return { numId: sourceNumId, replacementNodes: clonedNodes };
17979
+ }
17746
17980
  function remapNumberingPayloadForDocument(numberingXml, replacementNodes, numberingIdState) {
17747
17981
  const numberingDoc = parseOoxml(numberingXml);
17748
17982
  if (hasXmlParseError(numberingDoc)) {
@@ -17780,6 +18014,7 @@ function remapNumberingPayloadForDocument(numberingXml, replacementNodes, number
17780
18014
  }
17781
18015
  const clonedNodes = Array.isArray(replacementNodes) ? replacementNodes.map((node) => node?.cloneNode ? node.cloneNode(true) : node) : [];
17782
18016
  for (const node of clonedNodes) {
18017
+ if (isTrackedDeletedParagraph(node)) continue;
17783
18018
  const numIdNodes = Array.from(node?.getElementsByTagNameNS?.("*", "numId") || []);
17784
18019
  for (const numIdNode of numIdNodes) {
17785
18020
  const oldNumRef = getElementId2(numIdNode, ["w:val", "val"]);
@@ -18600,6 +18835,8 @@ function areRevisionTokensEqual(a, b) {
18600
18835
  var attr2 = (node, name) => node?.getAttribute?.(`w:${name}`) || node?.getAttribute?.(name) || "";
18601
18836
  var descendants = (node, name) => Array.from(node?.getElementsByTagNameNS?.(NS_W, name) || []);
18602
18837
  var first = (node, name) => descendants(node, name)[0] || null;
18838
+ var directChild2 = (node, name) => Array.from(node?.childNodes || []).find((child) => child?.nodeType === 1 && child.localName === name && (!child.namespaceURI || child.namespaceURI === NS_W)) || null;
18839
+ var currentPPr = (paragraph) => directChild2(paragraph, "pPr");
18603
18840
  function hasAncestor(node, localName2) {
18604
18841
  let cursor = node?.parentNode;
18605
18842
  while (cursor) {
@@ -18615,11 +18852,11 @@ function parseXml2(xml, partName, required = false) {
18615
18852
  return { doc: parsed.doc, warnings: parsed.warnings || [] };
18616
18853
  }
18617
18854
  function paragraphListProperties(paragraph) {
18618
- const numPr = first(first(paragraph, "pPr"), "numPr");
18855
+ const numPr = directChild2(currentPPr(paragraph), "numPr");
18619
18856
  if (!numPr) return null;
18620
- const numId = attr2(first(numPr, "numId"), "val");
18857
+ const numId = attr2(directChild2(numPr, "numId"), "val");
18621
18858
  if (!numId || numId === "0") return null;
18622
- return { numId, level: Number.parseInt(attr2(first(numPr, "ilvl"), "val") || "0", 10) || 0 };
18859
+ return { numId, level: Number.parseInt(attr2(directChild2(numPr, "ilvl"), "val") || "0", 10) || 0 };
18623
18860
  }
18624
18861
  function parseNumbering(numberingDoc) {
18625
18862
  const abstracts = /* @__PURE__ */ new Map();
@@ -18704,11 +18941,11 @@ function createNumberingResolver(numberingDoc) {
18704
18941
  };
18705
18942
  }
18706
18943
  function headingLevel(paragraph) {
18707
- const pPr = first(paragraph, "pPr");
18708
- const style = attr2(first(pPr, "pStyle"), "val");
18944
+ const pPr = currentPPr(paragraph);
18945
+ const style = attr2(directChild2(pPr, "pStyle"), "val");
18709
18946
  const match = style.match(/^heading\s*([1-9])$/i);
18710
18947
  if (match) return Math.min(Number(match[1]), 6);
18711
- const outline = Number.parseInt(attr2(first(pPr, "outlineLvl"), "val"), 10);
18948
+ const outline = Number.parseInt(attr2(directChild2(pPr, "outlineLvl"), "val"), 10);
18712
18949
  return Number.isInteger(outline) ? Math.min(outline + 1, 6) : null;
18713
18950
  }
18714
18951
  function structuralContext(paragraph, text2) {
@@ -18887,7 +19124,7 @@ function inspectDocumentParts(parts, options = {}) {
18887
19124
  const ids = [...new Set([...descendants(paragraph, "commentRangeStart"), ...descendants(paragraph, "commentReference")].map((node) => attr2(node, "id")).filter(Boolean))];
18888
19125
  const authors = revisionAuthors(paragraph);
18889
19126
  const list = resolveNumbering(paragraphListProperties(paragraph));
18890
- const styleId = attr2(first(first(paragraph, "pPr"), "pStyle"), "val") || null;
19127
+ const styleId = attr2(directChild2(currentPPr(paragraph), "pStyle"), "val") || null;
18891
19128
  const structure = structuralContext(paragraph, text2);
18892
19129
  const index = zeroIndex + 1;
18893
19130
  const provision = list?.label && list.format !== "bullet" ? list.label : null;
@@ -24288,7 +24525,9 @@ async function applyToParagraphByExactText(documentXml, targetText, modifiedText
24288
24525
  paragraphText,
24289
24526
  generateRedlines ? createRevisionMetadata(author, xmlDoc) : null,
24290
24527
  author,
24291
- { generateRedlines }
24528
+ // A wholly inserted paragraph must track its own paragraph mark; otherwise Word's
24529
+ // Reject All removes the text but leaves an empty paragraph behind.
24530
+ { generateRedlines, trackParagraphMark: true }
24292
24531
  );
24293
24532
  const imported = xmlDoc.importNode(plainParagraph, true);
24294
24533
  ensureParagraphIdsOnImportedNode(imported, operationSession);
@@ -24419,6 +24658,14 @@ async function applyToParagraphByExactText(documentXml, targetText, modifiedText
24419
24658
  const extracted = extractReplacementNodes(result.oxml);
24420
24659
  let replacementNodes = removeListPackagingSentinel(extracted.replacementNodes, result.warnings);
24421
24660
  let numberingXml = extracted.numberingXml;
24661
+ if (numberingXml && runtimeContext?.numberingIdState && !useTableScope) {
24662
+ const sourceParagraphs = useListScope ? listScopeEdit.paragraphs : explicitRangeParagraphs || [targetParagraph];
24663
+ const continued = reuseSourceListNumbering(numberingXml, replacementNodes, sourceParagraphs, runtimeContext.numberingIdState);
24664
+ if (continued) {
24665
+ replacementNodes = continued.replacementNodes;
24666
+ numberingXml = null;
24667
+ }
24668
+ }
24422
24669
  if (numberingXml && runtimeContext?.numberingIdState) {
24423
24670
  const normalizedNumbering = remapNumberingPayloadForDocument(numberingXml, replacementNodes, runtimeContext.numberingIdState);
24424
24671
  replacementNodes = normalizedNumbering.replacementNodes;