@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 CommonJS bundle v0.8.1 — https://github.com/AnsonLai/docx-redline-js
1
+ // @ansonlai/docx-redline-js standalone CommonJS 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;
@@ -9139,15 +9139,29 @@ function createDiffEngine(options = {}) {
9139
9139
  engine.Diff_Timeout = timeout;
9140
9140
  return engine;
9141
9141
  }
9142
- function tokenize(text2) {
9142
+ function tokenize(text2, atomicChars = null) {
9143
9143
  const tokens = [];
9144
+ const pushWord = (word) => {
9145
+ if (!atomicChars || atomicChars.size === 0) {
9146
+ tokens.push(word);
9147
+ return;
9148
+ }
9149
+ let start = 0;
9150
+ for (let index = 0; index < word.length; index++) {
9151
+ if (!atomicChars.has(word[index])) continue;
9152
+ if (index > start) tokens.push(word.slice(start, index));
9153
+ tokens.push(word[index]);
9154
+ start = index + 1;
9155
+ }
9156
+ if (start < word.length) tokens.push(word.slice(start));
9157
+ };
9144
9158
  const leading = text2.match(/^\s+/);
9145
9159
  if (leading) tokens.push(leading[0]);
9146
9160
  const regex = /(\S+)(\s*)/g;
9147
9161
  regex.lastIndex = leading?.[0].length || 0;
9148
9162
  let match;
9149
9163
  while ((match = regex.exec(text2)) !== null) {
9150
- if (match[1]) tokens.push(match[1]);
9164
+ if (match[1]) pushWord(match[1]);
9151
9165
  if (match[2]) tokens.push(match[2]);
9152
9166
  }
9153
9167
  return tokens;
@@ -9175,8 +9189,8 @@ function wordsToChars(text1, text2, options = {}) {
9175
9189
  }
9176
9190
  return { chars, tokenIds };
9177
9191
  }
9178
- const tokens1 = tokenize(text1);
9179
- const tokens2 = tokenize(text2);
9192
+ const tokens1 = tokenize(text1, options.atomicChars);
9193
+ const tokens2 = tokenize(text2, options.atomicChars);
9180
9194
  const encoded1 = mapTokensToChars(tokens1);
9181
9195
  const encoded2 = mapTokensToChars(tokens2);
9182
9196
  return {
@@ -10576,7 +10590,37 @@ async function executeListGeneration(options) {
10576
10590
  }];
10577
10591
  }
10578
10592
  }
10579
- if (generateRedlines && deletionRuns.length > 0) {
10593
+ const sourceParagraphCount = (originalRunModel || []).filter((run) => run.kind === RunKind.PARAGRAPH_START).length;
10594
+ if (generateRedlines && sourceParagraphCount > 0 && (sourceParagraphCount > 1 || deletionRuns.length === 0)) {
10595
+ let segment = null;
10596
+ const flushSegment = () => {
10597
+ if (!segment) return;
10598
+ results.push(serializeToOoxml(segment, null, [], {
10599
+ author,
10600
+ generateRedlines,
10601
+ revisionIdAllocator
10602
+ }));
10603
+ segment = null;
10604
+ };
10605
+ for (const run of originalRunModel) {
10606
+ if (run.kind === RunKind.PARAGRAPH_START) {
10607
+ flushSegment();
10608
+ segment = [{
10609
+ ...run,
10610
+ pPrElement: null,
10611
+ pPrXml: addParagraphMarkRevision(
10612
+ run.pPrElement || run.pPrXml || null,
10613
+ "del",
10614
+ author,
10615
+ revisionIdAllocator
10616
+ )
10617
+ }];
10618
+ } else if (segment && (run.kind === "text" || run.kind === "run")) {
10619
+ segment.push({ ...run, kind: "deletion", author });
10620
+ }
10621
+ }
10622
+ flushSegment();
10623
+ } else if (generateRedlines && deletionRuns.length > 0) {
10580
10624
  const deletedPPr = addParagraphMarkRevision(sourcePPr, "del", author, revisionIdAllocator);
10581
10625
  const deletedParagraph = serializeToOoxml(deletionRuns, deletedPPr, [], {
10582
10626
  author,
@@ -14581,7 +14625,11 @@ function applyReconstructionMode(xmlDoc, originalText, modifiedText, serializer,
14581
14625
  if (mapping.paragraphs.length === 0) {
14582
14626
  return withOoxmlSourceType({ oxml: serializer.serializeToString(xmlDoc), hasChanges: false });
14583
14627
  }
14584
- const diffs = computeWordDiffs(mapping.originalFullText, mapping.processedModifiedText, diffOptions);
14628
+ const wordDiffs = computeWordDiffs(mapping.originalFullText, mapping.processedModifiedText, {
14629
+ ...diffOptions,
14630
+ atomicChars: new Set(mapping.referenceMap.keys())
14631
+ });
14632
+ const diffs = keepUnchangedTextOutsideWrappers(anchorSharedReferences(wordDiffs, mapping.referenceMap), mapping);
14585
14633
  return withOoxmlSourceType(applyReconstructionDiffs(
14586
14634
  xmlDoc,
14587
14635
  diffs,
@@ -14593,6 +14641,109 @@ function applyReconstructionMode(xmlDoc, originalText, modifiedText, serializer,
14593
14641
  options
14594
14642
  ));
14595
14643
  }
14644
+ function forEachChangeGroup(diffs, onEqual, onGroup) {
14645
+ let index = 0;
14646
+ while (index < diffs.length) {
14647
+ if (diffs[index][0] === 0) {
14648
+ onEqual(diffs[index]);
14649
+ index++;
14650
+ continue;
14651
+ }
14652
+ const group = [];
14653
+ while (index < diffs.length && diffs[index][0] !== 0) group.push(diffs[index++]);
14654
+ onGroup(
14655
+ group,
14656
+ group.filter(([op]) => op === -1).map(([, text2]) => text2).join(""),
14657
+ group.filter(([op]) => op === 1).map(([, text2]) => text2).join("")
14658
+ );
14659
+ }
14660
+ }
14661
+ function pushChange(result, deleted, inserted) {
14662
+ if (deleted === inserted) {
14663
+ if (deleted) result.push([0, deleted]);
14664
+ return;
14665
+ }
14666
+ if (deleted) result.push([-1, deleted]);
14667
+ if (inserted) result.push([1, inserted]);
14668
+ }
14669
+ function anchorSharedReferences(diffs, referenceMap) {
14670
+ if (referenceMap.size === 0) return diffs;
14671
+ const result = [];
14672
+ forEachChangeGroup(diffs, (part) => result.push(part), (group, deleted, inserted) => {
14673
+ const deletedRefs = [...deleted].filter((char) => referenceMap.has(char) && inserted.includes(char));
14674
+ const insertedRefs = [...inserted].filter((char) => deletedRefs.includes(char));
14675
+ if (deletedRefs.length === 0 || deletedRefs.join("") !== insertedRefs.join("")) {
14676
+ result.push(...group);
14677
+ return;
14678
+ }
14679
+ let deletedStart = 0;
14680
+ let insertedStart = 0;
14681
+ for (const ref of deletedRefs) {
14682
+ const deletedAt = deleted.indexOf(ref, deletedStart);
14683
+ const insertedAt = inserted.indexOf(ref, insertedStart);
14684
+ pushChange(result, deleted.slice(deletedStart, deletedAt), inserted.slice(insertedStart, insertedAt));
14685
+ result.push([0, ref]);
14686
+ deletedStart = deletedAt + 1;
14687
+ insertedStart = insertedAt + 1;
14688
+ }
14689
+ pushChange(result, deleted.slice(deletedStart), inserted.slice(insertedStart));
14690
+ });
14691
+ return result;
14692
+ }
14693
+ function commonPrefixLength(left, right) {
14694
+ const limit = Math.min(left.length, right.length);
14695
+ let length = 0;
14696
+ while (length < limit && left[length] === right[length]) length++;
14697
+ return length;
14698
+ }
14699
+ function commonSuffixLength(left, right, limit) {
14700
+ let length = 0;
14701
+ while (length < limit && left[left.length - 1 - length] === right[right.length - 1 - length]) length++;
14702
+ return length;
14703
+ }
14704
+ function keepUnchangedTextOutsideWrappers(diffs, mapping) {
14705
+ const wrapperAt = (index) => mapping.getRunProperties(index).wrapper || null;
14706
+ const result = [];
14707
+ let originalOffset = 0;
14708
+ forEachChangeGroup(diffs, (part) => {
14709
+ result.push(part);
14710
+ originalOffset += part[1].length;
14711
+ }, (group, deleted, inserted) => {
14712
+ const start = originalOffset;
14713
+ const end = start + deleted.length;
14714
+ originalOffset = end;
14715
+ const isWrapperBoundary = (index) => index > start && index < end && wrapperAt(index - 1) !== wrapperAt(index);
14716
+ let prefix = 0;
14717
+ let suffix = 0;
14718
+ if (deleted && inserted) {
14719
+ const sharedSuffix = commonSuffixLength(deleted, inserted, Math.min(deleted.length, inserted.length));
14720
+ for (let length = sharedSuffix; length > 0; length--) {
14721
+ if (isWrapperBoundary(end - length)) {
14722
+ suffix = length;
14723
+ break;
14724
+ }
14725
+ }
14726
+ const sharedPrefix = Math.min(
14727
+ commonPrefixLength(deleted, inserted),
14728
+ Math.min(deleted.length, inserted.length) - suffix
14729
+ );
14730
+ for (let length = sharedPrefix; length > 0; length--) {
14731
+ if (isWrapperBoundary(start + length)) {
14732
+ prefix = length;
14733
+ break;
14734
+ }
14735
+ }
14736
+ }
14737
+ if (prefix === 0 && suffix === 0) {
14738
+ result.push(...group);
14739
+ return;
14740
+ }
14741
+ if (prefix > 0) result.push([0, deleted.slice(0, prefix)]);
14742
+ pushChange(result, deleted.slice(prefix, deleted.length - suffix), inserted.slice(prefix, inserted.length - suffix));
14743
+ if (suffix > 0) result.push([0, deleted.slice(deleted.length - suffix)]);
14744
+ });
14745
+ return result;
14746
+ }
14596
14747
 
14597
14748
  // engine/table-mode.js
14598
14749
  function noChanges(serializer, xmlDoc) {
@@ -16667,12 +16818,12 @@ function validateParagraphBoundaryMutation(targetParagraph, modifiedText, option
16667
16818
  }
16668
16819
 
16669
16820
  // core/list-targeting.js
16670
- function getFirstDescendantByLocalName(node, localName2) {
16671
- if (!node || typeof node.getElementsByTagNameNS !== "function") return null;
16672
- const namespaced = node.getElementsByTagNameNS(WORD_MAIN_NS, localName2);
16673
- if (namespaced.length > 0) return namespaced[0];
16674
- const anyNs = node.getElementsByTagNameNS("*", localName2);
16675
- return anyNs.length > 0 ? anyNs[0] : null;
16821
+ function getDirectChildByLocalName(node, localName2) {
16822
+ if (!node) return null;
16823
+ for (const child of Array.from(node.childNodes || [])) {
16824
+ if (child?.nodeType === 1 && child.localName === localName2) return child;
16825
+ }
16826
+ return null;
16676
16827
  }
16677
16828
  function readValAttribute(element) {
16678
16829
  if (!element) return null;
@@ -16789,15 +16940,15 @@ function buildListEntriesForInsertion(parsedItems, normalizedTargetText, anchorL
16789
16940
  }
16790
16941
  function getParagraphListInfo(paragraph) {
16791
16942
  if (!paragraph) return null;
16792
- const pPr = getFirstDescendantByLocalName(paragraph, "pPr");
16943
+ const pPr = getDirectChildByLocalName(paragraph, "pPr");
16793
16944
  if (!pPr) return null;
16794
- const numPr = getFirstDescendantByLocalName(pPr, "numPr");
16945
+ const numPr = getDirectChildByLocalName(pPr, "numPr");
16795
16946
  if (!numPr) return null;
16796
- const numIdEl = getFirstDescendantByLocalName(numPr, "numId");
16947
+ const numIdEl = getDirectChildByLocalName(numPr, "numId");
16797
16948
  if (!numIdEl) return null;
16798
16949
  const numId = readValAttribute(numIdEl);
16799
16950
  if (!numId || numId === "0") return null;
16800
- const ilvlEl = getFirstDescendantByLocalName(numPr, "ilvl");
16951
+ const ilvlEl = getDirectChildByLocalName(numPr, "ilvl");
16801
16952
  const ilvlRaw = readValAttribute(ilvlEl);
16802
16953
  const ilvl = Number.parseInt(ilvlRaw || "0", 10);
16803
16954
  return {
@@ -17207,8 +17358,13 @@ function enforceListBindingOnParagraphNodes(nodes, options = {}) {
17207
17358
  }
17208
17359
  return updated;
17209
17360
  }
17361
+ function parseOxmlFragment(oxml) {
17362
+ const text2 = String(oxml || "");
17363
+ const declared = /xmlns:w\s*=/.test(text2);
17364
+ return parseOoxmlSafe(declared ? text2 : `<w:root xmlns:w="${NS_W}">${text2}</w:root>`, "application/xml").doc;
17365
+ }
17210
17366
  function getFirstParagraphFromOxml(oxml) {
17211
- const doc = parseOoxmlSafe(oxml, "application/xml").doc;
17367
+ const doc = parseOxmlFragment(oxml);
17212
17368
  if (!doc) return null;
17213
17369
  const parseError = getXmlParseError(doc);
17214
17370
  if (parseError) return null;
@@ -17235,17 +17391,17 @@ function setElementVal(element, value) {
17235
17391
  element.setAttribute("w:val", String(value));
17236
17392
  }
17237
17393
  function extractFirstParagraphNumIdFromOxml(oxml) {
17238
- const doc = parseOoxmlSafe(oxml, "application/xml").doc;
17394
+ const doc = parseOxmlFragment(oxml);
17239
17395
  if (!doc) return null;
17240
17396
  const parseError = getXmlParseError(doc);
17241
17397
  if (parseError) return null;
17242
17398
  const paragraphs = getDocumentParagraphNodes(doc);
17243
- const firstParagraph = paragraphs[0] || null;
17244
- if (!firstParagraph) return null;
17245
- const numIdNodes = Array.from(firstParagraph.getElementsByTagNameNS("*", "numId"));
17246
- for (const numIdNode of numIdNodes) {
17247
- const numId = getElementId(numIdNode, ["w:val", "val"]);
17248
- if (numId != null) return String(numId);
17399
+ for (const paragraph of paragraphs) {
17400
+ const numIdNodes = Array.from(paragraph.getElementsByTagNameNS("*", "numId"));
17401
+ for (const numIdNode of numIdNodes) {
17402
+ const numId = getElementId(numIdNode, ["w:val", "val"]);
17403
+ if (numId != null) return String(numId);
17404
+ }
17249
17405
  }
17250
17406
  return null;
17251
17407
  }
@@ -17741,6 +17897,8 @@ function createDynamicNumberingIdState(numberingXml, options = {}) {
17741
17897
  const baseNumId = Math.max(minId, maxUsedNumId + 1);
17742
17898
  const baseAbstractNumId = Math.max(minId, maxUsedAbstractNumId + 1);
17743
17899
  return {
17900
+ // Kept so list edits can recognise which existing definition a source list uses and continue it.
17901
+ sourceNumberingXml: String(numberingXml || ""),
17744
17902
  nextNumId: nextAvailableId(baseNumId, usedNumIds, maxPreferred),
17745
17903
  nextAbstractNumId: nextAvailableId(baseAbstractNumId, usedAbstractNumIds, maxPreferred),
17746
17904
  usedNumIds,
@@ -17881,6 +18039,82 @@ function buildExplicitDecimalMultilevelNumberingXml(numId, abstractNumId, startA
17881
18039
  </w:num>
17882
18040
  </w:numbering>`.trim();
17883
18041
  }
18042
+ function isTrackedDeletedParagraph(node) {
18043
+ if (!node || node.nodeType !== 1 || node.localName !== "p") return false;
18044
+ const pPr = Array.from(node.childNodes || []).find((child) => isDirectWordChild(child, "pPr"));
18045
+ const rPr = pPr && Array.from(pPr.childNodes || []).find((child) => isDirectWordChild(child, "rPr"));
18046
+ return !!rPr && Array.from(rPr.childNodes || []).some((child) => isDirectWordChild(child, "del"));
18047
+ }
18048
+ function paragraphNumIdNodes(node) {
18049
+ return Array.from(node?.getElementsByTagNameNS?.("*", "numId") || []).filter((numIdNode) => numIdNode.parentNode?.localName === "numPr");
18050
+ }
18051
+ function levelFormats(abstractNum) {
18052
+ const formats = /* @__PURE__ */ new Map();
18053
+ for (const lvl of Array.from(abstractNum?.getElementsByTagNameNS?.("*", "lvl") || [])) {
18054
+ const ilvl = getElementId2(lvl, ["w:ilvl", "ilvl"]);
18055
+ const fmt = Array.from(lvl.getElementsByTagNameNS("*", "numFmt"))[0]?.getAttribute("w:val") || null;
18056
+ if (ilvl != null && fmt) formats.set(ilvl, fmt);
18057
+ }
18058
+ return formats;
18059
+ }
18060
+ function normalizeListFormat(format) {
18061
+ return format === "bullet" || format === "circle" || format === "square" ? "bullet" : format;
18062
+ }
18063
+ function abstractNumForNumId(numberingDoc, numId) {
18064
+ const num = Array.from(numberingDoc.getElementsByTagNameNS("*", "num")).find((candidate) => getElementId2(candidate, ["w:numId", "numId"]) === numId);
18065
+ const abstractId = num && getElementId2(Array.from(num.getElementsByTagNameNS("*", "abstractNumId"))[0], ["w:val", "val"]);
18066
+ if (abstractId == null) return null;
18067
+ return Array.from(numberingDoc.getElementsByTagNameNS("*", "abstractNum")).find((candidate) => getElementId2(candidate, ["w:abstractNumId", "abstractNumId"]) === abstractId) || null;
18068
+ }
18069
+ function reuseSourceListNumbering(numberingXml, replacementNodes, sourceParagraphs, numberingIdState) {
18070
+ const sourceXml = numberingIdState?.sourceNumberingXml;
18071
+ if (!numberingXml || !String(sourceXml || "").trim() || !Array.isArray(replacementNodes)) return null;
18072
+ const sourceNumIds = /* @__PURE__ */ new Set();
18073
+ for (const paragraph of sourceParagraphs || []) {
18074
+ for (const numIdNode of paragraphNumIdNodes(paragraph)) {
18075
+ const id = getElementId2(numIdNode, ["w:val", "val"]);
18076
+ if (id != null && id !== 0) sourceNumIds.add(id);
18077
+ }
18078
+ }
18079
+ if (sourceNumIds.size !== 1) return null;
18080
+ const [sourceNumId] = sourceNumIds;
18081
+ const sourceDoc = parseOoxml(sourceXml);
18082
+ const payloadDoc = parseOoxml(numberingXml);
18083
+ if (hasXmlParseError(sourceDoc) || hasXmlParseError(payloadDoc)) return null;
18084
+ const sourceAbstract = abstractNumForNumId(sourceDoc, sourceNumId);
18085
+ if (!sourceAbstract) return null;
18086
+ const sourceFormats = levelFormats(sourceAbstract);
18087
+ const newParagraphNumIds = [];
18088
+ for (const node of replacementNodes) {
18089
+ if (isTrackedDeletedParagraph(node)) continue;
18090
+ for (const numIdNode of paragraphNumIdNodes(node)) {
18091
+ const ilvlNode = Array.from(numIdNode.parentNode.childNodes || []).find((child) => child.localName === "ilvl");
18092
+ newParagraphNumIds.push({
18093
+ numId: getElementId2(numIdNode, ["w:val", "val"]),
18094
+ ilvl: ilvlNode ? getElementId2(ilvlNode, ["w:val", "val"]) ?? 0 : 0
18095
+ });
18096
+ }
18097
+ }
18098
+ if (newParagraphNumIds.length === 0) return null;
18099
+ const payloadFormats = /* @__PURE__ */ new Map();
18100
+ for (const { numId, ilvl } of newParagraphNumIds) {
18101
+ if (numId == null) return null;
18102
+ if (!payloadFormats.has(numId)) {
18103
+ const abstract = abstractNumForNumId(payloadDoc, numId);
18104
+ if (!abstract) return null;
18105
+ payloadFormats.set(numId, levelFormats(abstract));
18106
+ }
18107
+ const generated = payloadFormats.get(numId).get(ilvl);
18108
+ const existing = sourceFormats.get(ilvl);
18109
+ if (!generated || !existing || normalizeListFormat(generated) !== normalizeListFormat(existing)) return null;
18110
+ }
18111
+ const clonedNodes = replacementNodes.map((node) => node?.cloneNode ? node.cloneNode(true) : node);
18112
+ for (const node of clonedNodes) {
18113
+ if (isTrackedDeletedParagraph(node)) continue;
18114
+ for (const numIdNode of paragraphNumIdNodes(node)) setElementVal2(numIdNode, sourceNumId);
18115
+ }
18116
+ return { numId: sourceNumId, replacementNodes: clonedNodes };
18117
+ }
17884
18118
  function remapNumberingPayloadForDocument(numberingXml, replacementNodes, numberingIdState) {
17885
18119
  const numberingDoc = parseOoxml(numberingXml);
17886
18120
  if (hasXmlParseError(numberingDoc)) {
@@ -17918,6 +18152,7 @@ function remapNumberingPayloadForDocument(numberingXml, replacementNodes, number
17918
18152
  }
17919
18153
  const clonedNodes = Array.isArray(replacementNodes) ? replacementNodes.map((node) => node?.cloneNode ? node.cloneNode(true) : node) : [];
17920
18154
  for (const node of clonedNodes) {
18155
+ if (isTrackedDeletedParagraph(node)) continue;
17921
18156
  const numIdNodes = Array.from(node?.getElementsByTagNameNS?.("*", "numId") || []);
17922
18157
  for (const numIdNode of numIdNodes) {
17923
18158
  const oldNumRef = getElementId2(numIdNode, ["w:val", "val"]);
@@ -18738,6 +18973,8 @@ function areRevisionTokensEqual(a, b) {
18738
18973
  var attr2 = (node, name) => node?.getAttribute?.(`w:${name}`) || node?.getAttribute?.(name) || "";
18739
18974
  var descendants = (node, name) => Array.from(node?.getElementsByTagNameNS?.(NS_W, name) || []);
18740
18975
  var first = (node, name) => descendants(node, name)[0] || null;
18976
+ var directChild2 = (node, name) => Array.from(node?.childNodes || []).find((child) => child?.nodeType === 1 && child.localName === name && (!child.namespaceURI || child.namespaceURI === NS_W)) || null;
18977
+ var currentPPr = (paragraph) => directChild2(paragraph, "pPr");
18741
18978
  function hasAncestor(node, localName2) {
18742
18979
  let cursor = node?.parentNode;
18743
18980
  while (cursor) {
@@ -18753,11 +18990,11 @@ function parseXml2(xml, partName, required = false) {
18753
18990
  return { doc: parsed.doc, warnings: parsed.warnings || [] };
18754
18991
  }
18755
18992
  function paragraphListProperties(paragraph) {
18756
- const numPr = first(first(paragraph, "pPr"), "numPr");
18993
+ const numPr = directChild2(currentPPr(paragraph), "numPr");
18757
18994
  if (!numPr) return null;
18758
- const numId = attr2(first(numPr, "numId"), "val");
18995
+ const numId = attr2(directChild2(numPr, "numId"), "val");
18759
18996
  if (!numId || numId === "0") return null;
18760
- return { numId, level: Number.parseInt(attr2(first(numPr, "ilvl"), "val") || "0", 10) || 0 };
18997
+ return { numId, level: Number.parseInt(attr2(directChild2(numPr, "ilvl"), "val") || "0", 10) || 0 };
18761
18998
  }
18762
18999
  function parseNumbering(numberingDoc) {
18763
19000
  const abstracts = /* @__PURE__ */ new Map();
@@ -18842,11 +19079,11 @@ function createNumberingResolver(numberingDoc) {
18842
19079
  };
18843
19080
  }
18844
19081
  function headingLevel(paragraph) {
18845
- const pPr = first(paragraph, "pPr");
18846
- const style = attr2(first(pPr, "pStyle"), "val");
19082
+ const pPr = currentPPr(paragraph);
19083
+ const style = attr2(directChild2(pPr, "pStyle"), "val");
18847
19084
  const match = style.match(/^heading\s*([1-9])$/i);
18848
19085
  if (match) return Math.min(Number(match[1]), 6);
18849
- const outline = Number.parseInt(attr2(first(pPr, "outlineLvl"), "val"), 10);
19086
+ const outline = Number.parseInt(attr2(directChild2(pPr, "outlineLvl"), "val"), 10);
18850
19087
  return Number.isInteger(outline) ? Math.min(outline + 1, 6) : null;
18851
19088
  }
18852
19089
  function structuralContext(paragraph, text2) {
@@ -19025,7 +19262,7 @@ function inspectDocumentParts(parts, options = {}) {
19025
19262
  const ids = [...new Set([...descendants(paragraph, "commentRangeStart"), ...descendants(paragraph, "commentReference")].map((node) => attr2(node, "id")).filter(Boolean))];
19026
19263
  const authors = revisionAuthors(paragraph);
19027
19264
  const list = resolveNumbering(paragraphListProperties(paragraph));
19028
- const styleId = attr2(first(first(paragraph, "pPr"), "pStyle"), "val") || null;
19265
+ const styleId = attr2(directChild2(currentPPr(paragraph), "pStyle"), "val") || null;
19029
19266
  const structure = structuralContext(paragraph, text2);
19030
19267
  const index = zeroIndex + 1;
19031
19268
  const provision = list?.label && list.format !== "bullet" ? list.label : null;
@@ -24426,7 +24663,9 @@ async function applyToParagraphByExactText(documentXml, targetText, modifiedText
24426
24663
  paragraphText,
24427
24664
  generateRedlines ? createRevisionMetadata(author, xmlDoc) : null,
24428
24665
  author,
24429
- { generateRedlines }
24666
+ // A wholly inserted paragraph must track its own paragraph mark; otherwise Word's
24667
+ // Reject All removes the text but leaves an empty paragraph behind.
24668
+ { generateRedlines, trackParagraphMark: true }
24430
24669
  );
24431
24670
  const imported = xmlDoc.importNode(plainParagraph, true);
24432
24671
  ensureParagraphIdsOnImportedNode(imported, operationSession);
@@ -24557,6 +24796,14 @@ async function applyToParagraphByExactText(documentXml, targetText, modifiedText
24557
24796
  const extracted = extractReplacementNodes(result.oxml);
24558
24797
  let replacementNodes = removeListPackagingSentinel(extracted.replacementNodes, result.warnings);
24559
24798
  let numberingXml = extracted.numberingXml;
24799
+ if (numberingXml && runtimeContext?.numberingIdState && !useTableScope) {
24800
+ const sourceParagraphs = useListScope ? listScopeEdit.paragraphs : explicitRangeParagraphs || [targetParagraph];
24801
+ const continued = reuseSourceListNumbering(numberingXml, replacementNodes, sourceParagraphs, runtimeContext.numberingIdState);
24802
+ if (continued) {
24803
+ replacementNodes = continued.replacementNodes;
24804
+ numberingXml = null;
24805
+ }
24806
+ }
24560
24807
  if (numberingXml && runtimeContext?.numberingIdState) {
24561
24808
  const normalizedNumbering = remapNumberingPayloadForDocument(numberingXml, replacementNodes, runtimeContext.numberingIdState);
24562
24809
  replacementNodes = normalizedNumbering.replacementNodes;