@ansonlai/docx-redline-js 0.10.0 → 0.11.0
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/ARCHITECTURE.md +13 -1
- package/CHANGELOG.md +35 -0
- package/README.md +12 -4
- package/dist/docx-redline-js.esm.js +358 -262
- package/dist/docx-redline-js.esm.js.map +4 -4
- package/dist/docx-redline-js.esm.min.js +92 -92
- package/dist/docx-redline-js.esm.min.js.map +4 -4
- package/dist/docx-redline.bundle.cjs +358 -262
- package/dist/docx-redline.bundle.cjs.map +4 -4
- package/dist/docx-redline.bundle.js +358 -262
- package/dist/docx-redline.bundle.js.map +4 -4
- package/docs/AGENT_KNOWLEDGE_BASE.md +35 -2
- package/docs/TESTING.md +6 -1
- package/engine/oxml-engine.js +2 -2
- package/engine/table-mode.js +7 -4
- package/index.d.ts +23 -1
- package/index.js +1 -0
- package/package.json +1 -1
- package/pipeline/content-analysis.js +2 -1
- package/pipeline/structured-content.js +2 -1
- package/services/comment-parts-merge.js +124 -0
- package/services/document-operation-mutations.js +6 -2
- package/services/operation-heuristics.js +1 -1
- package/services/standalone-docx-plumbing.js +38 -22
- package/services/table-reconciliation.js +11 -6
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// @ansonlai/docx-redline-js standalone bundle v0.
|
|
1
|
+
// @ansonlai/docx-redline-js standalone bundle v0.11.0 — 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;
|
|
@@ -10447,6 +10447,7 @@ function paragraphMarkPPrXml(kind, ctx = {}) {
|
|
|
10447
10447
|
}
|
|
10448
10448
|
|
|
10449
10449
|
// services/table-reconciliation.js
|
|
10450
|
+
var cellMarkdown = (text2, options) => options?.plainTextContent === true ? { cleanText: String(text2 ?? ""), formatHints: [] } : preprocessMarkdown(text2);
|
|
10450
10451
|
function generateTableOoxml(tableData, options = {}) {
|
|
10451
10452
|
const {
|
|
10452
10453
|
generateRedlines = false,
|
|
@@ -10480,7 +10481,7 @@ function generateTableOoxml(tableData, options = {}) {
|
|
|
10480
10481
|
let cellsXml = "";
|
|
10481
10482
|
for (let c = 0; c < numCols; c++) {
|
|
10482
10483
|
const cellText = row[c] || "";
|
|
10483
|
-
const { cleanText, formatHints } =
|
|
10484
|
+
const { cleanText, formatHints } = cellMarkdown(cellText, options);
|
|
10484
10485
|
const runModel = [{
|
|
10485
10486
|
kind: generateRedlines ? RunKind.INSERTION : RunKind.TEXT,
|
|
10486
10487
|
text: cleanText,
|
|
@@ -10595,7 +10596,7 @@ function serializeVirtualGridToOoxml(grid, operations, options) {
|
|
|
10595
10596
|
let cellsXml = "";
|
|
10596
10597
|
const rowInsertMeta = generateRedlines ? createRevisionMetadata(author, revisionIdAllocator) : null;
|
|
10597
10598
|
for (const cellText of op.cells) {
|
|
10598
|
-
const { cleanText, formatHints } =
|
|
10599
|
+
const { cleanText, formatHints } = cellMarkdown(cellText, options);
|
|
10599
10600
|
const runModel = [{
|
|
10600
10601
|
kind: generateRedlines ? RunKind.INSERTION : RunKind.TEXT,
|
|
10601
10602
|
text: cleanText,
|
|
@@ -10669,7 +10670,7 @@ function reconcileMultiBlockCellContent(cell, newText, options) {
|
|
|
10669
10670
|
}
|
|
10670
10671
|
return;
|
|
10671
10672
|
}
|
|
10672
|
-
const { cleanText, formatHints } =
|
|
10673
|
+
const { cleanText, formatHints } = cellMarkdown(line, options);
|
|
10673
10674
|
const diffOps = computeWordLevelDiffOps(block.acceptedText, cleanText);
|
|
10674
10675
|
const model = applyPatches(splitRunsAtDiffBoundaries(block.runModel, diffOps), diffOps, {
|
|
10675
10676
|
generateRedlines,
|
|
@@ -10679,7 +10680,7 @@ function reconcileMultiBlockCellContent(cell, newText, options) {
|
|
|
10679
10680
|
out += serializeToOoxml(model, block.pPr, formatHints, { author, generateRedlines, revisionIdAllocator });
|
|
10680
10681
|
});
|
|
10681
10682
|
for (let i2 = cell.blocks.length; i2 < lines.length; i2++) {
|
|
10682
|
-
const { cleanText, formatHints } =
|
|
10683
|
+
const { cleanText, formatHints } = cellMarkdown(lines[i2], options);
|
|
10683
10684
|
const runModel = [{
|
|
10684
10685
|
kind: generateRedlines ? RunKind.INSERTION : RunKind.TEXT,
|
|
10685
10686
|
text: cleanText,
|
|
@@ -10698,7 +10699,7 @@ function reconcileCellContent(cell, newText, options) {
|
|
|
10698
10699
|
return reconcileMultiBlockCellContent(cell, newText, options);
|
|
10699
10700
|
}
|
|
10700
10701
|
const { generateRedlines, author, revisionIdAllocator = null } = options;
|
|
10701
|
-
const { cleanText, formatHints } =
|
|
10702
|
+
const { cleanText, formatHints } = cellMarkdown(newText, options);
|
|
10702
10703
|
const oldText = cell.getText();
|
|
10703
10704
|
const diffOps = computeWordLevelDiffOps(oldText, cleanText);
|
|
10704
10705
|
const baseBlock = cell.blocks[0] || { runModel: [], pPr: null };
|
|
@@ -10744,7 +10745,7 @@ function parseTable(text2) {
|
|
|
10744
10745
|
}
|
|
10745
10746
|
const isSeparatorLine = (line) => {
|
|
10746
10747
|
const normalized = line.replace(/\s+/g, "");
|
|
10747
|
-
return /^\|:?-{3,}:?(\|:?-{3,}:?)
|
|
10748
|
+
return /^\|:?-{3,}:?(?:(\|:?-{3,}:?)+\|?|\|)$/.test(normalized);
|
|
10748
10749
|
};
|
|
10749
10750
|
const hasHeader = lines.some(isSeparatorLine);
|
|
10750
10751
|
const dataLines = lines.filter((line) => !isSeparatorLine(line));
|
|
@@ -11328,7 +11329,7 @@ var ReconciliationPipeline = class {
|
|
|
11328
11329
|
};
|
|
11329
11330
|
|
|
11330
11331
|
// pipeline/structured-content.js
|
|
11331
|
-
var TABLE_SEPARATOR = /^\s
|
|
11332
|
+
var TABLE_SEPARATOR = /^\s*(?:\|\s*:?-{3,}:?\s*\||\|?\s*:?-{3,}:?\s*(?:\|\s*:?-{3,}:?\s*)+\|?)\s*$/;
|
|
11332
11333
|
function isBlank(line) {
|
|
11333
11334
|
return !line || line.trim().length === 0;
|
|
11334
11335
|
}
|
|
@@ -14985,7 +14986,7 @@ function keepUnchangedTextOutsideWrappers(diffs, mapping) {
|
|
|
14985
14986
|
function noChanges(serializer, xmlDoc) {
|
|
14986
14987
|
return withOoxmlSourceType({ oxml: serializer.serializeToString(xmlDoc), hasChanges: false });
|
|
14987
14988
|
}
|
|
14988
|
-
function applyTableReconciliation(xmlDoc, modifiedText, serializer, parser, author, generateRedlines = true) {
|
|
14989
|
+
function applyTableReconciliation(xmlDoc, modifiedText, serializer, parser, author, generateRedlines = true, extra = {}) {
|
|
14989
14990
|
const tableNodes = getElementsByTagNSOrTag(xmlDoc, NS_W, "tbl");
|
|
14990
14991
|
const newTableData = parseTable(modifiedText);
|
|
14991
14992
|
const hasNewContent = newTableData.rows.length > 0 || newTableData.headers.length > 0;
|
|
@@ -15001,7 +15002,8 @@ function applyTableReconciliation(xmlDoc, modifiedText, serializer, parser, auth
|
|
|
15001
15002
|
const options = {
|
|
15002
15003
|
generateRedlines,
|
|
15003
15004
|
author,
|
|
15004
|
-
revisionIdAllocator: getRevisionIdAllocatorForDocument(xmlDoc)
|
|
15005
|
+
revisionIdAllocator: getRevisionIdAllocatorForDocument(xmlDoc),
|
|
15006
|
+
plainTextContent: extra.plainTextContent === true
|
|
15005
15007
|
};
|
|
15006
15008
|
const reconciledOxml = serializeVirtualGridToOoxml(oldGrid, operations, options);
|
|
15007
15009
|
const wrappedOxml = `<root xmlns:w="${NS_W}">${reconciledOxml}</root>`;
|
|
@@ -15021,14 +15023,14 @@ function applyTableReconciliation(xmlDoc, modifiedText, serializer, parser, auth
|
|
|
15021
15023
|
targetTable.parentNode.replaceChild(importedTable, targetTable);
|
|
15022
15024
|
return withOoxmlSourceType({ oxml: serializer.serializeToString(xmlDoc), hasChanges: true });
|
|
15023
15025
|
}
|
|
15024
|
-
function applyTextToTableTransformation(xmlDoc, modifiedText, serializer, parser, author, generateRedlines) {
|
|
15026
|
+
function applyTextToTableTransformation(xmlDoc, modifiedText, serializer, parser, author, generateRedlines, extra = {}) {
|
|
15025
15027
|
const revisionIdAllocator = getRevisionIdAllocatorForDocument(xmlDoc);
|
|
15026
15028
|
const tableData = parseTable(modifiedText);
|
|
15027
15029
|
if (!tableData || tableData.rows.length === 0 && tableData.headers.length === 0) {
|
|
15028
15030
|
log("[OxmlEngine] Failed to parse table data from Markdown");
|
|
15029
15031
|
return noChanges(serializer, xmlDoc);
|
|
15030
15032
|
}
|
|
15031
|
-
const tableOoxml = generateTableOoxml(tableData, { generateRedlines, author, revisionIdAllocator, trackAsBlock: true });
|
|
15033
|
+
const tableOoxml = generateTableOoxml(tableData, { generateRedlines, author, revisionIdAllocator, trackAsBlock: true, plainTextContent: extra.plainTextContent === true });
|
|
15032
15034
|
const tableDoc = parseOoxmlSafe(`<root xmlns:w="${NS_W}">${tableOoxml}</root>`, "application/xml").doc;
|
|
15033
15035
|
if (!tableDoc) return noChanges(serializer, xmlDoc);
|
|
15034
15036
|
const tableParseError = getXmlParseError(tableDoc);
|
|
@@ -16227,11 +16229,11 @@ async function applyRedlineToOxml(oxml, originalText, modifiedText, options = {}
|
|
|
16227
16229
|
if (isMarkdownTable && !hasTables) {
|
|
16228
16230
|
recordRouteSelection(options, "table", { transformation: "text-to-table" });
|
|
16229
16231
|
log("[OxmlEngine] Text-to-table transformation: generating new table from Markdown");
|
|
16230
|
-
return finalize(applyTextToTableTransformation(xmlDoc, cleanModifiedText, serializer, null, author, generateRedlines));
|
|
16232
|
+
return finalize(applyTextToTableTransformation(xmlDoc, cleanModifiedText, serializer, null, author, generateRedlines, { plainTextContent: options.plainTextContent === true }));
|
|
16231
16233
|
}
|
|
16232
16234
|
if (hasTables && isMarkdownTable) {
|
|
16233
16235
|
recordRouteSelection(options, "table", { transformation: "table-reconciliation" });
|
|
16234
|
-
return finalize(applyTableReconciliation(xmlDoc, cleanModifiedText, serializer, null, author, generateRedlines));
|
|
16236
|
+
return finalize(applyTableReconciliation(xmlDoc, cleanModifiedText, serializer, null, author, generateRedlines, { plainTextContent: options.plainTextContent === true }));
|
|
16235
16237
|
}
|
|
16236
16238
|
if (usesSurgicalTextMode) {
|
|
16237
16239
|
recordRouteSelection(options, "surgical", { tableScoped: hasTables });
|
|
@@ -20527,232 +20529,6 @@ function applyCommentReplyToParts({ commentsXml, commentsExtendedXml = null, doc
|
|
|
20527
20529
|
};
|
|
20528
20530
|
}
|
|
20529
20531
|
|
|
20530
|
-
// engine/formatting-removal.js
|
|
20531
|
-
function removeNode2(node) {
|
|
20532
|
-
if (node?.parentNode) {
|
|
20533
|
-
node.parentNode.removeChild(node);
|
|
20534
|
-
}
|
|
20535
|
-
}
|
|
20536
|
-
function removeFormattingFromRPr(rPr, formatTypes = ["all"]) {
|
|
20537
|
-
if (!rPr) return null;
|
|
20538
|
-
const rPrClone = rPr.cloneNode(true);
|
|
20539
|
-
if (formatTypes.includes("all")) {
|
|
20540
|
-
const toRemove = [
|
|
20541
|
-
"w:b",
|
|
20542
|
-
"w:i",
|
|
20543
|
-
"w:u",
|
|
20544
|
-
"w:strike",
|
|
20545
|
-
"w:dstrike",
|
|
20546
|
-
"w:color",
|
|
20547
|
-
"w:sz",
|
|
20548
|
-
"w:szCs",
|
|
20549
|
-
"w:rFonts",
|
|
20550
|
-
"w:highlight",
|
|
20551
|
-
"w:vertAlign",
|
|
20552
|
-
"w:spacing",
|
|
20553
|
-
"w:w",
|
|
20554
|
-
"w:kern",
|
|
20555
|
-
"w:position"
|
|
20556
|
-
];
|
|
20557
|
-
toRemove.forEach((tag) => {
|
|
20558
|
-
const elements = rPrClone.querySelectorAll(`${tag}, ${tag.replace("w:", "")}`);
|
|
20559
|
-
elements.forEach(removeNode2);
|
|
20560
|
-
});
|
|
20561
|
-
} else {
|
|
20562
|
-
const tagMap = {
|
|
20563
|
-
"bold": "w:b",
|
|
20564
|
-
"italic": "w:i",
|
|
20565
|
-
"underline": "w:u",
|
|
20566
|
-
"strikethrough": "w:strike",
|
|
20567
|
-
"doubleStrike": "w:dstrike",
|
|
20568
|
-
"color": "w:color",
|
|
20569
|
-
"highlight": "w:highlight",
|
|
20570
|
-
"fontSize": "w:sz",
|
|
20571
|
-
"fontSizeCs": "w:szCs",
|
|
20572
|
-
// Complex script font size
|
|
20573
|
-
"fontFamily": "w:rFonts",
|
|
20574
|
-
"superscript": "w:vertAlign",
|
|
20575
|
-
"subscript": "w:vertAlign"
|
|
20576
|
-
};
|
|
20577
|
-
formatTypes.forEach((type) => {
|
|
20578
|
-
const tag = tagMap[type];
|
|
20579
|
-
if (tag) {
|
|
20580
|
-
const elements = rPrClone.querySelectorAll(`${tag}, ${tag.replace("w:", "")}`);
|
|
20581
|
-
elements.forEach(removeNode2);
|
|
20582
|
-
}
|
|
20583
|
-
});
|
|
20584
|
-
}
|
|
20585
|
-
return rPrClone.children.length > 0 ? rPrClone : null;
|
|
20586
|
-
}
|
|
20587
|
-
function applyFormattingRemovalToOoxml(ooxmlString, targetText, formatTypes) {
|
|
20588
|
-
if (!targetText || !ooxmlString) return ooxmlString;
|
|
20589
|
-
const doc = parseOoxml(ooxmlString);
|
|
20590
|
-
if (!doc) return ooxmlString;
|
|
20591
|
-
const NS_W11 = "http://schemas.openxmlformats.org/wordprocessingml/2006/main";
|
|
20592
|
-
const runs = doc.getElementsByTagNameNS(NS_W11, "r");
|
|
20593
|
-
const insertions = doc.getElementsByTagNameNS(NS_W11, "ins");
|
|
20594
|
-
const allRuns = [...Array.from(runs)];
|
|
20595
|
-
for (const ins of insertions) {
|
|
20596
|
-
const insideRuns = ins.getElementsByTagNameNS(NS_W11, "r");
|
|
20597
|
-
allRuns.push(...Array.from(insideRuns));
|
|
20598
|
-
}
|
|
20599
|
-
for (const run of allRuns) {
|
|
20600
|
-
const textNodes = run.getElementsByTagNameNS(NS_W11, "t");
|
|
20601
|
-
const runText = Array.from(textNodes).map((t) => t.textContent).join("");
|
|
20602
|
-
if (runText.includes(targetText) || runText === targetText) {
|
|
20603
|
-
const rPrElements = run.getElementsByTagNameNS(NS_W11, "rPr");
|
|
20604
|
-
if (rPrElements.length > 0) {
|
|
20605
|
-
const rPr = rPrElements[0];
|
|
20606
|
-
const newRPr = removeFormattingFromRPr(rPr, formatTypes);
|
|
20607
|
-
if (newRPr) {
|
|
20608
|
-
if (rPr.parentNode) {
|
|
20609
|
-
rPr.parentNode.replaceChild(newRPr, rPr);
|
|
20610
|
-
}
|
|
20611
|
-
} else {
|
|
20612
|
-
removeNode2(rPr);
|
|
20613
|
-
}
|
|
20614
|
-
}
|
|
20615
|
-
}
|
|
20616
|
-
}
|
|
20617
|
-
return serializeOoxml(doc);
|
|
20618
|
-
}
|
|
20619
|
-
var HIGHLIGHT_COLOR_MAP = {
|
|
20620
|
-
"yellow": "yellow",
|
|
20621
|
-
"green": "green",
|
|
20622
|
-
"cyan": "cyan",
|
|
20623
|
-
"magenta": "magenta",
|
|
20624
|
-
"blue": "blue",
|
|
20625
|
-
"red": "red",
|
|
20626
|
-
"darkblue": "darkBlue",
|
|
20627
|
-
"darkcyan": "darkCyan",
|
|
20628
|
-
"darkgreen": "darkGreen",
|
|
20629
|
-
"darkmagenta": "darkMagenta",
|
|
20630
|
-
"darkred": "darkRed",
|
|
20631
|
-
"darkyellow": "darkYellow",
|
|
20632
|
-
"gray25": "lightGray",
|
|
20633
|
-
"gray50": "darkGray",
|
|
20634
|
-
"black": "black",
|
|
20635
|
-
"white": "white"
|
|
20636
|
-
};
|
|
20637
|
-
function injectHighlightIntoRPr(doc, rPr, color = "yellow", options = {}) {
|
|
20638
|
-
const NS_W11 = "http://schemas.openxmlformats.org/wordprocessingml/2006/main";
|
|
20639
|
-
const ooxmlColor = HIGHLIGHT_COLOR_MAP[color.toLowerCase()] || "yellow";
|
|
20640
|
-
const generateRedlines = options?.generateRedlines ?? false;
|
|
20641
|
-
const author = options?.author || getDefaultAuthor();
|
|
20642
|
-
let rPrElement = rPr;
|
|
20643
|
-
if (!rPrElement) {
|
|
20644
|
-
rPrElement = createWordElement(doc, "w:rPr");
|
|
20645
|
-
} else {
|
|
20646
|
-
rPrElement = rPr.cloneNode(true);
|
|
20647
|
-
}
|
|
20648
|
-
let previousRPrState = null;
|
|
20649
|
-
if (generateRedlines) {
|
|
20650
|
-
previousRPrState = createWordElement(doc, "w:rPr");
|
|
20651
|
-
Array.from(rPrElement.childNodes).forEach((child) => {
|
|
20652
|
-
if (child.nodeName !== "w:rPrChange") {
|
|
20653
|
-
previousRPrState.appendChild(child.cloneNode(true));
|
|
20654
|
-
}
|
|
20655
|
-
});
|
|
20656
|
-
}
|
|
20657
|
-
const existingHighlight = rPrElement.getElementsByTagNameNS(NS_W11, "highlight");
|
|
20658
|
-
Array.from(existingHighlight).forEach(removeNode2);
|
|
20659
|
-
const highlightEl = createWordElement(doc, "w:highlight");
|
|
20660
|
-
highlightEl.setAttributeNS(NS_W11, "w:val", ooxmlColor);
|
|
20661
|
-
rPrElement.appendChild(highlightEl);
|
|
20662
|
-
if (generateRedlines && previousRPrState) {
|
|
20663
|
-
const rPrChange = createWordElement(doc, "w:rPrChange");
|
|
20664
|
-
const metadata = createRevisionMetadata(author, doc);
|
|
20665
|
-
rPrChange.setAttribute("w:id", String(metadata.id));
|
|
20666
|
-
rPrChange.setAttribute("w:author", metadata.author);
|
|
20667
|
-
rPrChange.setAttribute("w:date", metadata.date);
|
|
20668
|
-
rPrChange.appendChild(previousRPrState);
|
|
20669
|
-
const existingChange = rPrElement.getElementsByTagNameNS(NS_W11, "rPrChange");
|
|
20670
|
-
Array.from(existingChange).forEach(removeNode2);
|
|
20671
|
-
rPrElement.appendChild(rPrChange);
|
|
20672
|
-
}
|
|
20673
|
-
return rPrElement;
|
|
20674
|
-
}
|
|
20675
|
-
function applyHighlightToOoxml(ooxmlString, targetText, color = "yellow", options = {}) {
|
|
20676
|
-
if (!targetText || !ooxmlString) return ooxmlString;
|
|
20677
|
-
const doc = parseOoxml(ooxmlString);
|
|
20678
|
-
if (!doc) return ooxmlString;
|
|
20679
|
-
let revisionIdAllocator;
|
|
20680
|
-
if (options?._revisionIdAllocator instanceof RevisionIdAllocator) {
|
|
20681
|
-
revisionIdAllocator = options._revisionIdAllocator;
|
|
20682
|
-
seedRevisionIdsFromDocument(doc, revisionIdAllocator);
|
|
20683
|
-
} else {
|
|
20684
|
-
revisionIdAllocator = createRevisionIdAllocator(doc);
|
|
20685
|
-
}
|
|
20686
|
-
const NS_W11 = "http://schemas.openxmlformats.org/wordprocessingml/2006/main";
|
|
20687
|
-
const getRunText = (run) => {
|
|
20688
|
-
const textNodes = run.getElementsByTagNameNS(NS_W11, "t");
|
|
20689
|
-
return Array.from(textNodes).map((t) => t.textContent).join("");
|
|
20690
|
-
};
|
|
20691
|
-
const allRuns = Array.from(doc.getElementsByTagNameNS(NS_W11, "r"));
|
|
20692
|
-
const sourceRunsWithClaimedRevisionIds = /* @__PURE__ */ new WeakSet();
|
|
20693
|
-
const cloneRunWithText2 = (sourceRun, text2, shouldHighlight) => {
|
|
20694
|
-
const clonedRun = sourceRun.cloneNode(true);
|
|
20695
|
-
if (sourceRunsWithClaimedRevisionIds.has(sourceRun)) {
|
|
20696
|
-
refreshRunPropertyChangeIds(clonedRun, revisionIdAllocator);
|
|
20697
|
-
} else {
|
|
20698
|
-
sourceRunsWithClaimedRevisionIds.add(sourceRun);
|
|
20699
|
-
}
|
|
20700
|
-
const textNodes = clonedRun.getElementsByTagNameNS(NS_W11, "t");
|
|
20701
|
-
Array.from(textNodes).forEach(removeNode2);
|
|
20702
|
-
const newText = createWordElement(doc, "w:t");
|
|
20703
|
-
newText.setAttribute("xml:space", "preserve");
|
|
20704
|
-
newText.textContent = text2;
|
|
20705
|
-
clonedRun.appendChild(newText);
|
|
20706
|
-
if (shouldHighlight) {
|
|
20707
|
-
const rPrElements = clonedRun.getElementsByTagNameNS(NS_W11, "rPr");
|
|
20708
|
-
const existingRPr = rPrElements.length > 0 ? rPrElements[0] : null;
|
|
20709
|
-
const newRPr = injectHighlightIntoRPr(doc, existingRPr, color, options);
|
|
20710
|
-
if (existingRPr) {
|
|
20711
|
-
clonedRun.replaceChild(newRPr, existingRPr);
|
|
20712
|
-
} else {
|
|
20713
|
-
clonedRun.insertBefore(newRPr, clonedRun.firstChild);
|
|
20714
|
-
}
|
|
20715
|
-
}
|
|
20716
|
-
return clonedRun;
|
|
20717
|
-
};
|
|
20718
|
-
for (const run of allRuns) {
|
|
20719
|
-
const runText = getRunText(run);
|
|
20720
|
-
if (!runText) continue;
|
|
20721
|
-
const matchIndexes = [];
|
|
20722
|
-
let searchOffset = 0;
|
|
20723
|
-
while (searchOffset <= runText.length - targetText.length) {
|
|
20724
|
-
const matchIndex = runText.indexOf(targetText, searchOffset);
|
|
20725
|
-
if (matchIndex === -1) break;
|
|
20726
|
-
matchIndexes.push(matchIndex);
|
|
20727
|
-
searchOffset = matchIndex + targetText.length;
|
|
20728
|
-
}
|
|
20729
|
-
if (matchIndexes.length === 0) continue;
|
|
20730
|
-
const parent = run.parentNode;
|
|
20731
|
-
if (!parent) {
|
|
20732
|
-
console.warn("[Highlight] Run parent is null; skipping. Likely already processed.");
|
|
20733
|
-
continue;
|
|
20734
|
-
}
|
|
20735
|
-
const fragment = doc.createDocumentFragment();
|
|
20736
|
-
let cursor = 0;
|
|
20737
|
-
for (const matchIndex of matchIndexes) {
|
|
20738
|
-
if (matchIndex > cursor) {
|
|
20739
|
-
fragment.appendChild(cloneRunWithText2(run, runText.slice(cursor, matchIndex), false));
|
|
20740
|
-
}
|
|
20741
|
-
fragment.appendChild(cloneRunWithText2(
|
|
20742
|
-
run,
|
|
20743
|
-
runText.slice(matchIndex, matchIndex + targetText.length),
|
|
20744
|
-
true
|
|
20745
|
-
));
|
|
20746
|
-
cursor = matchIndex + targetText.length;
|
|
20747
|
-
}
|
|
20748
|
-
if (cursor < runText.length) {
|
|
20749
|
-
fragment.appendChild(cloneRunWithText2(run, runText.slice(cursor), false));
|
|
20750
|
-
}
|
|
20751
|
-
parent.replaceChild(fragment, run);
|
|
20752
|
-
}
|
|
20753
|
-
return serializeOoxml(doc);
|
|
20754
|
-
}
|
|
20755
|
-
|
|
20756
20532
|
// services/package-parts.js
|
|
20757
20533
|
var WORD_ML = "application/vnd.openxmlformats-officedocument.wordprocessingml";
|
|
20758
20534
|
var REL_OFFICE_DOC = "http://schemas.openxmlformats.org/officeDocument/2006/relationships";
|
|
@@ -21040,31 +20816,34 @@ async function ensureNumberingArtifactsInZip(zip, numberingXmlList, options = {}
|
|
|
21040
20816
|
}
|
|
21041
20817
|
}
|
|
21042
20818
|
}
|
|
20819
|
+
function mergeCommentsPartXml(existingXml, commentsXml, options = {}) {
|
|
20820
|
+
if (!existingXml || options.replaceExisting === true) return commentsXml;
|
|
20821
|
+
const existingDoc = parseXmlStrictStandalone(existingXml, "word/comments.xml (existing)");
|
|
20822
|
+
const incomingDoc = parseXmlStrictStandalone(commentsXml, "word/comments.xml (incoming)");
|
|
20823
|
+
const incomingComments = Array.from(incomingDoc.documentElement.getElementsByTagNameNS(NS_W7, "comment"));
|
|
20824
|
+
if (incomingComments.length === 0) return existingXml;
|
|
20825
|
+
const existingRoot = existingDoc.documentElement;
|
|
20826
|
+
const usedIds = new Set(
|
|
20827
|
+
Array.from(existingRoot.getElementsByTagNameNS(NS_W7, "comment")).map((comment) => comment.getAttribute("w:id") || comment.getAttribute("id")).filter(Boolean)
|
|
20828
|
+
);
|
|
20829
|
+
for (const incomingComment of incomingComments) {
|
|
20830
|
+
const id = incomingComment.getAttribute("w:id") || incomingComment.getAttribute("id");
|
|
20831
|
+
if (id && usedIds.has(id)) {
|
|
20832
|
+
throw Object.assign(new Error(`Duplicate comment id: ${id}`), { code: "DUPLICATE_COMMENT_ID", commentId: id });
|
|
20833
|
+
}
|
|
20834
|
+
if (id) usedIds.add(id);
|
|
20835
|
+
existingRoot.appendChild(existingDoc.importNode(incomingComment, true));
|
|
20836
|
+
}
|
|
20837
|
+
return createSerializer().serializeToString(existingDoc);
|
|
20838
|
+
}
|
|
21043
20839
|
async function ensureCommentsArtifactsInZip(zip, commentsXml, options = {}) {
|
|
21044
20840
|
const onInfo = typeof options?.onInfo === "function" ? options.onInfo : () => {
|
|
21045
20841
|
};
|
|
21046
20842
|
if (!commentsXml) return;
|
|
21047
20843
|
const serializer = createSerializer();
|
|
21048
20844
|
const existingText = await readZipText(zip, COMMENTS_PATH);
|
|
21049
|
-
if (!existingText || options.replaceExisting === true)
|
|
21050
|
-
|
|
21051
|
-
zip.file(COMMENTS_PATH, commentsXml);
|
|
21052
|
-
} else {
|
|
21053
|
-
const existingDoc = parseXmlStrictStandalone(existingText, "word/comments.xml (existing)");
|
|
21054
|
-
const incomingDoc = parseXmlStrictStandalone(commentsXml, "word/comments.xml (incoming)");
|
|
21055
|
-
const existingRoot = existingDoc.documentElement;
|
|
21056
|
-
const existingIds = new Set(
|
|
21057
|
-
Array.from(existingRoot.getElementsByTagNameNS(NS_W7, "comment")).map((comment) => comment.getAttribute("w:id") || comment.getAttribute("id")).filter(Boolean)
|
|
21058
|
-
);
|
|
21059
|
-
for (const incomingComment of Array.from(incomingDoc.documentElement.getElementsByTagNameNS(NS_W7, "comment"))) {
|
|
21060
|
-
const id = incomingComment.getAttribute("w:id") || incomingComment.getAttribute("id");
|
|
21061
|
-
if (id && existingIds.has(id)) {
|
|
21062
|
-
throw new Error(`Duplicate comment id: ${id}`);
|
|
21063
|
-
}
|
|
21064
|
-
existingRoot.appendChild(existingDoc.importNode(incomingComment, true));
|
|
21065
|
-
}
|
|
21066
|
-
zip.file(COMMENTS_PATH, serializer.serializeToString(existingDoc));
|
|
21067
|
-
}
|
|
20845
|
+
if (!existingText || options.replaceExisting === true) onInfo("[Demo] Adding comments.xml");
|
|
20846
|
+
zip.file(COMMENTS_PATH, mergeCommentsPartXml(existingText, commentsXml, options));
|
|
21068
20847
|
const ctText = await readZipText(zip, CONTENT_TYPES_PATH);
|
|
21069
20848
|
if (ctText) {
|
|
21070
20849
|
const ctDoc = parseXmlStrictStandalone(ctText, CONTENT_TYPES_PATH);
|
|
@@ -21300,6 +21079,318 @@ async function validateDocxPackage(zip) {
|
|
|
21300
21079
|
}
|
|
21301
21080
|
}
|
|
21302
21081
|
|
|
21082
|
+
// services/comment-parts-merge.js
|
|
21083
|
+
var PART_KEYS = Object.freeze({
|
|
21084
|
+
comments: "commentsXml",
|
|
21085
|
+
commentsExtended: "commentsExtendedXml",
|
|
21086
|
+
commentsIds: "commentsIdsXml",
|
|
21087
|
+
commentsExtensible: "commentsExtensibleXml"
|
|
21088
|
+
});
|
|
21089
|
+
var partsOf = (parts) => Object.fromEntries(Object.values(PART_KEYS).map((key) => [key, parts?.[key] || null]));
|
|
21090
|
+
function mergeCommentParts(parts = {}, output = {}) {
|
|
21091
|
+
const before = partsOf(parts);
|
|
21092
|
+
const after = { ...before };
|
|
21093
|
+
const fail = (error2) => ({ status: "error", hasChanges: false, parts: before, changedParts: [], addedParts: [], error: error2 });
|
|
21094
|
+
const commentsMode = output?.commentsXmlMode === "replace" ? "replace" : "merge";
|
|
21095
|
+
const extendedMode = output?.commentsExtendedXmlMode === "replace" ? "replace" : "merge";
|
|
21096
|
+
for (const [label, value] of [
|
|
21097
|
+
...Object.values(PART_KEYS).map((key) => [`parts.${key}`, before[key]]),
|
|
21098
|
+
["output.commentsXml", output?.commentsXml],
|
|
21099
|
+
["output.commentsExtendedXml", output?.commentsExtendedXml]
|
|
21100
|
+
]) {
|
|
21101
|
+
if (value != null && typeof value !== "string") {
|
|
21102
|
+
return fail({ code: "INVALID_COMMENT_PARTS", message: `${label} must be an XML string, null or omitted (got ${typeof value}).` });
|
|
21103
|
+
}
|
|
21104
|
+
}
|
|
21105
|
+
const requirePart = (xml, part, rootName, label) => {
|
|
21106
|
+
let doc;
|
|
21107
|
+
try {
|
|
21108
|
+
doc = parseXmlStrictStandalone(xml, label);
|
|
21109
|
+
} catch (error2) {
|
|
21110
|
+
throw Object.assign(error2, { part });
|
|
21111
|
+
}
|
|
21112
|
+
if (doc.documentElement?.localName !== rootName) {
|
|
21113
|
+
throw Object.assign(new Error(`${label}: expected a <${rootName}> root element, found <${doc.documentElement?.localName || "none"}>.`), { code: "INVALID_COMMENT_PARTS", part });
|
|
21114
|
+
}
|
|
21115
|
+
return doc;
|
|
21116
|
+
};
|
|
21117
|
+
try {
|
|
21118
|
+
if (output?.commentsXml) {
|
|
21119
|
+
const incomingDoc = requirePart(output.commentsXml, "comments", "comments", "word/comments.xml (incoming)");
|
|
21120
|
+
const seen = /* @__PURE__ */ new Set();
|
|
21121
|
+
for (const node of Array.from(incomingDoc.getElementsByTagNameNS("*", "comment"))) {
|
|
21122
|
+
const id = node.getAttribute("w:id") || node.getAttribute("id");
|
|
21123
|
+
if (!id) continue;
|
|
21124
|
+
if (seen.has(id)) {
|
|
21125
|
+
throw Object.assign(new Error(`Duplicate comment id: ${id}`), { code: "DUPLICATE_COMMENT_ID", commentId: id, part: "comments" });
|
|
21126
|
+
}
|
|
21127
|
+
seen.add(id);
|
|
21128
|
+
}
|
|
21129
|
+
if (before.commentsXml) requirePart(before.commentsXml, "comments", "comments", "word/comments.xml (existing)");
|
|
21130
|
+
if (before.commentsIdsXml) requirePart(before.commentsIdsXml, "commentsIds", "commentsIds", "word/commentsIds.xml (existing)");
|
|
21131
|
+
if (before.commentsExtensibleXml) requirePart(before.commentsExtensibleXml, "commentsExtensible", "commentsExtensible", "word/commentsExtensible.xml (existing)");
|
|
21132
|
+
after.commentsXml = mergeCommentsPartXml(before.commentsXml, output.commentsXml, { replaceExisting: commentsMode === "replace" });
|
|
21133
|
+
Object.assign(after, reconcileCommentSiblingParts({
|
|
21134
|
+
commentsXml: after.commentsXml,
|
|
21135
|
+
commentsIdsXml: after.commentsIdsXml,
|
|
21136
|
+
commentsExtensibleXml: after.commentsExtensibleXml
|
|
21137
|
+
}));
|
|
21138
|
+
}
|
|
21139
|
+
if (output?.commentsExtendedXml) {
|
|
21140
|
+
if (extendedMode !== "replace" && before.commentsExtendedXml) {
|
|
21141
|
+
return fail({
|
|
21142
|
+
code: "UNSUPPORTED_EXISTING_COMMENT_THREAD_METADATA",
|
|
21143
|
+
part: "commentsExtended",
|
|
21144
|
+
message: 'The output carries a commentsExtended delta (mode "merge") for parts that already have thread metadata; a delta cannot be merged into an existing commentsExtended.xml. Nothing was merged.'
|
|
21145
|
+
});
|
|
21146
|
+
}
|
|
21147
|
+
requirePart(output.commentsExtendedXml, "commentsExtended", "commentsEx", "word/commentsExtended.xml (incoming)");
|
|
21148
|
+
after.commentsExtendedXml = output.commentsExtendedXml;
|
|
21149
|
+
}
|
|
21150
|
+
} catch (error2) {
|
|
21151
|
+
return fail({
|
|
21152
|
+
code: error2?.code || "COMMENT_PARTS_MERGE_FAILED",
|
|
21153
|
+
message: error2?.message || String(error2),
|
|
21154
|
+
...error2?.part || error2?.code === "DUPLICATE_COMMENT_ID" ? { part: error2.part || "comments" } : {},
|
|
21155
|
+
...error2?.commentId ? { commentId: error2.commentId } : {}
|
|
21156
|
+
});
|
|
21157
|
+
}
|
|
21158
|
+
const changedParts = [];
|
|
21159
|
+
const addedParts = [];
|
|
21160
|
+
for (const [name, key] of Object.entries(PART_KEYS)) {
|
|
21161
|
+
if (after[key] === before[key]) continue;
|
|
21162
|
+
changedParts.push(name);
|
|
21163
|
+
if (!before[key]) addedParts.push(name);
|
|
21164
|
+
}
|
|
21165
|
+
return { status: "ok", hasChanges: changedParts.length > 0, parts: after, changedParts, addedParts };
|
|
21166
|
+
}
|
|
21167
|
+
|
|
21168
|
+
// engine/formatting-removal.js
|
|
21169
|
+
function removeNode2(node) {
|
|
21170
|
+
if (node?.parentNode) {
|
|
21171
|
+
node.parentNode.removeChild(node);
|
|
21172
|
+
}
|
|
21173
|
+
}
|
|
21174
|
+
function removeFormattingFromRPr(rPr, formatTypes = ["all"]) {
|
|
21175
|
+
if (!rPr) return null;
|
|
21176
|
+
const rPrClone = rPr.cloneNode(true);
|
|
21177
|
+
if (formatTypes.includes("all")) {
|
|
21178
|
+
const toRemove = [
|
|
21179
|
+
"w:b",
|
|
21180
|
+
"w:i",
|
|
21181
|
+
"w:u",
|
|
21182
|
+
"w:strike",
|
|
21183
|
+
"w:dstrike",
|
|
21184
|
+
"w:color",
|
|
21185
|
+
"w:sz",
|
|
21186
|
+
"w:szCs",
|
|
21187
|
+
"w:rFonts",
|
|
21188
|
+
"w:highlight",
|
|
21189
|
+
"w:vertAlign",
|
|
21190
|
+
"w:spacing",
|
|
21191
|
+
"w:w",
|
|
21192
|
+
"w:kern",
|
|
21193
|
+
"w:position"
|
|
21194
|
+
];
|
|
21195
|
+
toRemove.forEach((tag) => {
|
|
21196
|
+
const elements = rPrClone.querySelectorAll(`${tag}, ${tag.replace("w:", "")}`);
|
|
21197
|
+
elements.forEach(removeNode2);
|
|
21198
|
+
});
|
|
21199
|
+
} else {
|
|
21200
|
+
const tagMap = {
|
|
21201
|
+
"bold": "w:b",
|
|
21202
|
+
"italic": "w:i",
|
|
21203
|
+
"underline": "w:u",
|
|
21204
|
+
"strikethrough": "w:strike",
|
|
21205
|
+
"doubleStrike": "w:dstrike",
|
|
21206
|
+
"color": "w:color",
|
|
21207
|
+
"highlight": "w:highlight",
|
|
21208
|
+
"fontSize": "w:sz",
|
|
21209
|
+
"fontSizeCs": "w:szCs",
|
|
21210
|
+
// Complex script font size
|
|
21211
|
+
"fontFamily": "w:rFonts",
|
|
21212
|
+
"superscript": "w:vertAlign",
|
|
21213
|
+
"subscript": "w:vertAlign"
|
|
21214
|
+
};
|
|
21215
|
+
formatTypes.forEach((type) => {
|
|
21216
|
+
const tag = tagMap[type];
|
|
21217
|
+
if (tag) {
|
|
21218
|
+
const elements = rPrClone.querySelectorAll(`${tag}, ${tag.replace("w:", "")}`);
|
|
21219
|
+
elements.forEach(removeNode2);
|
|
21220
|
+
}
|
|
21221
|
+
});
|
|
21222
|
+
}
|
|
21223
|
+
return rPrClone.children.length > 0 ? rPrClone : null;
|
|
21224
|
+
}
|
|
21225
|
+
function applyFormattingRemovalToOoxml(ooxmlString, targetText, formatTypes) {
|
|
21226
|
+
if (!targetText || !ooxmlString) return ooxmlString;
|
|
21227
|
+
const doc = parseOoxml(ooxmlString);
|
|
21228
|
+
if (!doc) return ooxmlString;
|
|
21229
|
+
const NS_W11 = "http://schemas.openxmlformats.org/wordprocessingml/2006/main";
|
|
21230
|
+
const runs = doc.getElementsByTagNameNS(NS_W11, "r");
|
|
21231
|
+
const insertions = doc.getElementsByTagNameNS(NS_W11, "ins");
|
|
21232
|
+
const allRuns = [...Array.from(runs)];
|
|
21233
|
+
for (const ins of insertions) {
|
|
21234
|
+
const insideRuns = ins.getElementsByTagNameNS(NS_W11, "r");
|
|
21235
|
+
allRuns.push(...Array.from(insideRuns));
|
|
21236
|
+
}
|
|
21237
|
+
for (const run of allRuns) {
|
|
21238
|
+
const textNodes = run.getElementsByTagNameNS(NS_W11, "t");
|
|
21239
|
+
const runText = Array.from(textNodes).map((t) => t.textContent).join("");
|
|
21240
|
+
if (runText.includes(targetText) || runText === targetText) {
|
|
21241
|
+
const rPrElements = run.getElementsByTagNameNS(NS_W11, "rPr");
|
|
21242
|
+
if (rPrElements.length > 0) {
|
|
21243
|
+
const rPr = rPrElements[0];
|
|
21244
|
+
const newRPr = removeFormattingFromRPr(rPr, formatTypes);
|
|
21245
|
+
if (newRPr) {
|
|
21246
|
+
if (rPr.parentNode) {
|
|
21247
|
+
rPr.parentNode.replaceChild(newRPr, rPr);
|
|
21248
|
+
}
|
|
21249
|
+
} else {
|
|
21250
|
+
removeNode2(rPr);
|
|
21251
|
+
}
|
|
21252
|
+
}
|
|
21253
|
+
}
|
|
21254
|
+
}
|
|
21255
|
+
return serializeOoxml(doc);
|
|
21256
|
+
}
|
|
21257
|
+
var HIGHLIGHT_COLOR_MAP = {
|
|
21258
|
+
"yellow": "yellow",
|
|
21259
|
+
"green": "green",
|
|
21260
|
+
"cyan": "cyan",
|
|
21261
|
+
"magenta": "magenta",
|
|
21262
|
+
"blue": "blue",
|
|
21263
|
+
"red": "red",
|
|
21264
|
+
"darkblue": "darkBlue",
|
|
21265
|
+
"darkcyan": "darkCyan",
|
|
21266
|
+
"darkgreen": "darkGreen",
|
|
21267
|
+
"darkmagenta": "darkMagenta",
|
|
21268
|
+
"darkred": "darkRed",
|
|
21269
|
+
"darkyellow": "darkYellow",
|
|
21270
|
+
"gray25": "lightGray",
|
|
21271
|
+
"gray50": "darkGray",
|
|
21272
|
+
"black": "black",
|
|
21273
|
+
"white": "white"
|
|
21274
|
+
};
|
|
21275
|
+
function injectHighlightIntoRPr(doc, rPr, color = "yellow", options = {}) {
|
|
21276
|
+
const NS_W11 = "http://schemas.openxmlformats.org/wordprocessingml/2006/main";
|
|
21277
|
+
const ooxmlColor = HIGHLIGHT_COLOR_MAP[color.toLowerCase()] || "yellow";
|
|
21278
|
+
const generateRedlines = options?.generateRedlines ?? false;
|
|
21279
|
+
const author = options?.author || getDefaultAuthor();
|
|
21280
|
+
let rPrElement = rPr;
|
|
21281
|
+
if (!rPrElement) {
|
|
21282
|
+
rPrElement = createWordElement(doc, "w:rPr");
|
|
21283
|
+
} else {
|
|
21284
|
+
rPrElement = rPr.cloneNode(true);
|
|
21285
|
+
}
|
|
21286
|
+
let previousRPrState = null;
|
|
21287
|
+
if (generateRedlines) {
|
|
21288
|
+
previousRPrState = createWordElement(doc, "w:rPr");
|
|
21289
|
+
Array.from(rPrElement.childNodes).forEach((child) => {
|
|
21290
|
+
if (child.nodeName !== "w:rPrChange") {
|
|
21291
|
+
previousRPrState.appendChild(child.cloneNode(true));
|
|
21292
|
+
}
|
|
21293
|
+
});
|
|
21294
|
+
}
|
|
21295
|
+
const existingHighlight = rPrElement.getElementsByTagNameNS(NS_W11, "highlight");
|
|
21296
|
+
Array.from(existingHighlight).forEach(removeNode2);
|
|
21297
|
+
const highlightEl = createWordElement(doc, "w:highlight");
|
|
21298
|
+
highlightEl.setAttributeNS(NS_W11, "w:val", ooxmlColor);
|
|
21299
|
+
rPrElement.appendChild(highlightEl);
|
|
21300
|
+
if (generateRedlines && previousRPrState) {
|
|
21301
|
+
const rPrChange = createWordElement(doc, "w:rPrChange");
|
|
21302
|
+
const metadata = createRevisionMetadata(author, doc);
|
|
21303
|
+
rPrChange.setAttribute("w:id", String(metadata.id));
|
|
21304
|
+
rPrChange.setAttribute("w:author", metadata.author);
|
|
21305
|
+
rPrChange.setAttribute("w:date", metadata.date);
|
|
21306
|
+
rPrChange.appendChild(previousRPrState);
|
|
21307
|
+
const existingChange = rPrElement.getElementsByTagNameNS(NS_W11, "rPrChange");
|
|
21308
|
+
Array.from(existingChange).forEach(removeNode2);
|
|
21309
|
+
rPrElement.appendChild(rPrChange);
|
|
21310
|
+
}
|
|
21311
|
+
return rPrElement;
|
|
21312
|
+
}
|
|
21313
|
+
function applyHighlightToOoxml(ooxmlString, targetText, color = "yellow", options = {}) {
|
|
21314
|
+
if (!targetText || !ooxmlString) return ooxmlString;
|
|
21315
|
+
const doc = parseOoxml(ooxmlString);
|
|
21316
|
+
if (!doc) return ooxmlString;
|
|
21317
|
+
let revisionIdAllocator;
|
|
21318
|
+
if (options?._revisionIdAllocator instanceof RevisionIdAllocator) {
|
|
21319
|
+
revisionIdAllocator = options._revisionIdAllocator;
|
|
21320
|
+
seedRevisionIdsFromDocument(doc, revisionIdAllocator);
|
|
21321
|
+
} else {
|
|
21322
|
+
revisionIdAllocator = createRevisionIdAllocator(doc);
|
|
21323
|
+
}
|
|
21324
|
+
const NS_W11 = "http://schemas.openxmlformats.org/wordprocessingml/2006/main";
|
|
21325
|
+
const getRunText = (run) => {
|
|
21326
|
+
const textNodes = run.getElementsByTagNameNS(NS_W11, "t");
|
|
21327
|
+
return Array.from(textNodes).map((t) => t.textContent).join("");
|
|
21328
|
+
};
|
|
21329
|
+
const allRuns = Array.from(doc.getElementsByTagNameNS(NS_W11, "r"));
|
|
21330
|
+
const sourceRunsWithClaimedRevisionIds = /* @__PURE__ */ new WeakSet();
|
|
21331
|
+
const cloneRunWithText2 = (sourceRun, text2, shouldHighlight) => {
|
|
21332
|
+
const clonedRun = sourceRun.cloneNode(true);
|
|
21333
|
+
if (sourceRunsWithClaimedRevisionIds.has(sourceRun)) {
|
|
21334
|
+
refreshRunPropertyChangeIds(clonedRun, revisionIdAllocator);
|
|
21335
|
+
} else {
|
|
21336
|
+
sourceRunsWithClaimedRevisionIds.add(sourceRun);
|
|
21337
|
+
}
|
|
21338
|
+
const textNodes = clonedRun.getElementsByTagNameNS(NS_W11, "t");
|
|
21339
|
+
Array.from(textNodes).forEach(removeNode2);
|
|
21340
|
+
const newText = createWordElement(doc, "w:t");
|
|
21341
|
+
newText.setAttribute("xml:space", "preserve");
|
|
21342
|
+
newText.textContent = text2;
|
|
21343
|
+
clonedRun.appendChild(newText);
|
|
21344
|
+
if (shouldHighlight) {
|
|
21345
|
+
const rPrElements = clonedRun.getElementsByTagNameNS(NS_W11, "rPr");
|
|
21346
|
+
const existingRPr = rPrElements.length > 0 ? rPrElements[0] : null;
|
|
21347
|
+
const newRPr = injectHighlightIntoRPr(doc, existingRPr, color, options);
|
|
21348
|
+
if (existingRPr) {
|
|
21349
|
+
clonedRun.replaceChild(newRPr, existingRPr);
|
|
21350
|
+
} else {
|
|
21351
|
+
clonedRun.insertBefore(newRPr, clonedRun.firstChild);
|
|
21352
|
+
}
|
|
21353
|
+
}
|
|
21354
|
+
return clonedRun;
|
|
21355
|
+
};
|
|
21356
|
+
for (const run of allRuns) {
|
|
21357
|
+
const runText = getRunText(run);
|
|
21358
|
+
if (!runText) continue;
|
|
21359
|
+
const matchIndexes = [];
|
|
21360
|
+
let searchOffset = 0;
|
|
21361
|
+
while (searchOffset <= runText.length - targetText.length) {
|
|
21362
|
+
const matchIndex = runText.indexOf(targetText, searchOffset);
|
|
21363
|
+
if (matchIndex === -1) break;
|
|
21364
|
+
matchIndexes.push(matchIndex);
|
|
21365
|
+
searchOffset = matchIndex + targetText.length;
|
|
21366
|
+
}
|
|
21367
|
+
if (matchIndexes.length === 0) continue;
|
|
21368
|
+
const parent = run.parentNode;
|
|
21369
|
+
if (!parent) {
|
|
21370
|
+
console.warn("[Highlight] Run parent is null; skipping. Likely already processed.");
|
|
21371
|
+
continue;
|
|
21372
|
+
}
|
|
21373
|
+
const fragment = doc.createDocumentFragment();
|
|
21374
|
+
let cursor = 0;
|
|
21375
|
+
for (const matchIndex of matchIndexes) {
|
|
21376
|
+
if (matchIndex > cursor) {
|
|
21377
|
+
fragment.appendChild(cloneRunWithText2(run, runText.slice(cursor, matchIndex), false));
|
|
21378
|
+
}
|
|
21379
|
+
fragment.appendChild(cloneRunWithText2(
|
|
21380
|
+
run,
|
|
21381
|
+
runText.slice(matchIndex, matchIndex + targetText.length),
|
|
21382
|
+
true
|
|
21383
|
+
));
|
|
21384
|
+
cursor = matchIndex + targetText.length;
|
|
21385
|
+
}
|
|
21386
|
+
if (cursor < runText.length) {
|
|
21387
|
+
fragment.appendChild(cloneRunWithText2(run, runText.slice(cursor), false));
|
|
21388
|
+
}
|
|
21389
|
+
parent.replaceChild(fragment, run);
|
|
21390
|
+
}
|
|
21391
|
+
return serializeOoxml(doc);
|
|
21392
|
+
}
|
|
21393
|
+
|
|
21303
21394
|
// orchestration/route-plan.js
|
|
21304
21395
|
var RoutePlanKind = Object.freeze({
|
|
21305
21396
|
STRUCTURED_LIST_DIRECT: "structured_list_direct",
|
|
@@ -25004,7 +25095,7 @@ function deriveParagraphThenTableAppend(modifiedText) {
|
|
|
25004
25095
|
const tableLines = lines.slice(1);
|
|
25005
25096
|
if (!tableLines.every((line) => line.trim().startsWith("|"))) return null;
|
|
25006
25097
|
const separator = tableLines[1].replace(/\s+/g, "");
|
|
25007
|
-
if (!/^\|:?-{3,}:?(\|:?-{3,}:?)
|
|
25098
|
+
if (!/^\|:?-{3,}:?(?:(\|:?-{3,}:?)+\|?|\|)$/.test(separator)) return null;
|
|
25008
25099
|
return { paragraphMarkdown: lines[0].trim(), tableText: tableLines.join("\n") };
|
|
25009
25100
|
}
|
|
25010
25101
|
|
|
@@ -25634,7 +25725,10 @@ async function buildInsertedPlainParagraph(xmlDoc, anchorParagraph, text2, revis
|
|
|
25634
25725
|
String(text2 || ""),
|
|
25635
25726
|
{
|
|
25636
25727
|
author,
|
|
25637
|
-
generateRedlines: false
|
|
25728
|
+
generateRedlines: false,
|
|
25729
|
+
// A caller's structuredContent: false keeps inserted paragraphs plain too: no headings,
|
|
25730
|
+
// and inline Markdown markers stay literal.
|
|
25731
|
+
...options.plainTextContent === true ? { structuredContent: false, plainTextContent: true } : {}
|
|
25638
25732
|
}
|
|
25639
25733
|
);
|
|
25640
25734
|
let sourceParagraph = null;
|
|
@@ -27160,7 +27254,7 @@ async function applyToParagraphByExactText(documentXml, targetText, modifiedText
|
|
|
27160
27254
|
author,
|
|
27161
27255
|
// A wholly inserted paragraph must track its own paragraph mark; otherwise Word's
|
|
27162
27256
|
// Reject All removes the text but leaves an empty paragraph behind.
|
|
27163
|
-
{ generateRedlines, trackParagraphMark: true }
|
|
27257
|
+
{ generateRedlines, trackParagraphMark: true, plainTextContent: options.plainTextContent === true }
|
|
27164
27258
|
);
|
|
27165
27259
|
const imported = xmlDoc.importNode(plainParagraph, true);
|
|
27166
27260
|
ensureParagraphIdsOnImportedNode(imported, operationSession);
|
|
@@ -27299,6 +27393,7 @@ async function applyToParagraphByExactText(documentXml, targetText, modifiedText
|
|
|
27299
27393
|
existingRevisions: options.existingRevisions || "merge-same-author",
|
|
27300
27394
|
structuredContent: options.structuredContent !== false,
|
|
27301
27395
|
explicitStructuredContent: options.explicitStructuredContent === true,
|
|
27396
|
+
plainTextContent: options.plainTextContent === true,
|
|
27302
27397
|
_revisionIdAllocator: revisionIdAllocator,
|
|
27303
27398
|
_isolatedTableCell: useTableScope
|
|
27304
27399
|
}) : await applyRedlineToOxml2(scopedXml, originalTextForApply, effectiveModifiedText, {
|
|
@@ -32195,6 +32290,7 @@ export {
|
|
|
32195
32290
|
isLikelyStructuredTableSourceParagraph,
|
|
32196
32291
|
isMarkdownTableText,
|
|
32197
32292
|
isNodeVisibleInRevisionView,
|
|
32293
|
+
mergeCommentParts,
|
|
32198
32294
|
mergeNumberingParts,
|
|
32199
32295
|
mergeNumberingXmlBySchemaOrder,
|
|
32200
32296
|
normalizeBodySectionOrderStandalone,
|