@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 v0.
|
|
1
|
+
// @ansonlai/docx-redline-js 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;
|
|
@@ -3968,6 +3968,7 @@ function paragraphMarkPPrXml(kind, ctx = {}) {
|
|
|
3968
3968
|
}
|
|
3969
3969
|
|
|
3970
3970
|
// services/table-reconciliation.js
|
|
3971
|
+
var cellMarkdown = (text2, options) => options?.plainTextContent === true ? { cleanText: String(text2 ?? ""), formatHints: [] } : preprocessMarkdown(text2);
|
|
3971
3972
|
function generateTableOoxml(tableData, options = {}) {
|
|
3972
3973
|
const {
|
|
3973
3974
|
generateRedlines = false,
|
|
@@ -4001,7 +4002,7 @@ function generateTableOoxml(tableData, options = {}) {
|
|
|
4001
4002
|
let cellsXml = "";
|
|
4002
4003
|
for (let c = 0; c < numCols; c++) {
|
|
4003
4004
|
const cellText = row[c] || "";
|
|
4004
|
-
const { cleanText, formatHints } =
|
|
4005
|
+
const { cleanText, formatHints } = cellMarkdown(cellText, options);
|
|
4005
4006
|
const runModel = [{
|
|
4006
4007
|
kind: generateRedlines ? RunKind.INSERTION : RunKind.TEXT,
|
|
4007
4008
|
text: cleanText,
|
|
@@ -4116,7 +4117,7 @@ function serializeVirtualGridToOoxml(grid, operations, options) {
|
|
|
4116
4117
|
let cellsXml = "";
|
|
4117
4118
|
const rowInsertMeta = generateRedlines ? createRevisionMetadata(author, revisionIdAllocator) : null;
|
|
4118
4119
|
for (const cellText of op.cells) {
|
|
4119
|
-
const { cleanText, formatHints } =
|
|
4120
|
+
const { cleanText, formatHints } = cellMarkdown(cellText, options);
|
|
4120
4121
|
const runModel = [{
|
|
4121
4122
|
kind: generateRedlines ? RunKind.INSERTION : RunKind.TEXT,
|
|
4122
4123
|
text: cleanText,
|
|
@@ -4190,7 +4191,7 @@ function reconcileMultiBlockCellContent(cell, newText, options) {
|
|
|
4190
4191
|
}
|
|
4191
4192
|
return;
|
|
4192
4193
|
}
|
|
4193
|
-
const { cleanText, formatHints } =
|
|
4194
|
+
const { cleanText, formatHints } = cellMarkdown(line, options);
|
|
4194
4195
|
const diffOps = computeWordLevelDiffOps(block.acceptedText, cleanText);
|
|
4195
4196
|
const model = applyPatches(splitRunsAtDiffBoundaries(block.runModel, diffOps), diffOps, {
|
|
4196
4197
|
generateRedlines,
|
|
@@ -4200,7 +4201,7 @@ function reconcileMultiBlockCellContent(cell, newText, options) {
|
|
|
4200
4201
|
out += serializeToOoxml(model, block.pPr, formatHints, { author, generateRedlines, revisionIdAllocator });
|
|
4201
4202
|
});
|
|
4202
4203
|
for (let i2 = cell.blocks.length; i2 < lines.length; i2++) {
|
|
4203
|
-
const { cleanText, formatHints } =
|
|
4204
|
+
const { cleanText, formatHints } = cellMarkdown(lines[i2], options);
|
|
4204
4205
|
const runModel = [{
|
|
4205
4206
|
kind: generateRedlines ? RunKind.INSERTION : RunKind.TEXT,
|
|
4206
4207
|
text: cleanText,
|
|
@@ -4219,7 +4220,7 @@ function reconcileCellContent(cell, newText, options) {
|
|
|
4219
4220
|
return reconcileMultiBlockCellContent(cell, newText, options);
|
|
4220
4221
|
}
|
|
4221
4222
|
const { generateRedlines, author, revisionIdAllocator = null } = options;
|
|
4222
|
-
const { cleanText, formatHints } =
|
|
4223
|
+
const { cleanText, formatHints } = cellMarkdown(newText, options);
|
|
4223
4224
|
const oldText = cell.getText();
|
|
4224
4225
|
const diffOps = computeWordLevelDiffOps(oldText, cleanText);
|
|
4225
4226
|
const baseBlock = cell.blocks[0] || { runModel: [], pPr: null };
|
|
@@ -4265,7 +4266,7 @@ function parseTable(text2) {
|
|
|
4265
4266
|
}
|
|
4266
4267
|
const isSeparatorLine = (line) => {
|
|
4267
4268
|
const normalized = line.replace(/\s+/g, "");
|
|
4268
|
-
return /^\|:?-{3,}:?(\|:?-{3,}:?)
|
|
4269
|
+
return /^\|:?-{3,}:?(?:(\|:?-{3,}:?)+\|?|\|)$/.test(normalized);
|
|
4269
4270
|
};
|
|
4270
4271
|
const hasHeader = lines.some(isSeparatorLine);
|
|
4271
4272
|
const dataLines = lines.filter((line) => !isSeparatorLine(line));
|
|
@@ -4849,7 +4850,7 @@ var ReconciliationPipeline = class {
|
|
|
4849
4850
|
};
|
|
4850
4851
|
|
|
4851
4852
|
// pipeline/structured-content.js
|
|
4852
|
-
var TABLE_SEPARATOR = /^\s
|
|
4853
|
+
var TABLE_SEPARATOR = /^\s*(?:\|\s*:?-{3,}:?\s*\||\|?\s*:?-{3,}:?\s*(?:\|\s*:?-{3,}:?\s*)+\|?)\s*$/;
|
|
4853
4854
|
function isBlank(line) {
|
|
4854
4855
|
return !line || line.trim().length === 0;
|
|
4855
4856
|
}
|
|
@@ -8506,7 +8507,7 @@ function keepUnchangedTextOutsideWrappers(diffs, mapping) {
|
|
|
8506
8507
|
function noChanges(serializer, xmlDoc) {
|
|
8507
8508
|
return withOoxmlSourceType({ oxml: serializer.serializeToString(xmlDoc), hasChanges: false });
|
|
8508
8509
|
}
|
|
8509
|
-
function applyTableReconciliation(xmlDoc, modifiedText, serializer, parser, author, generateRedlines = true) {
|
|
8510
|
+
function applyTableReconciliation(xmlDoc, modifiedText, serializer, parser, author, generateRedlines = true, extra = {}) {
|
|
8510
8511
|
const tableNodes = getElementsByTagNSOrTag(xmlDoc, NS_W, "tbl");
|
|
8511
8512
|
const newTableData = parseTable(modifiedText);
|
|
8512
8513
|
const hasNewContent = newTableData.rows.length > 0 || newTableData.headers.length > 0;
|
|
@@ -8522,7 +8523,8 @@ function applyTableReconciliation(xmlDoc, modifiedText, serializer, parser, auth
|
|
|
8522
8523
|
const options = {
|
|
8523
8524
|
generateRedlines,
|
|
8524
8525
|
author,
|
|
8525
|
-
revisionIdAllocator: getRevisionIdAllocatorForDocument(xmlDoc)
|
|
8526
|
+
revisionIdAllocator: getRevisionIdAllocatorForDocument(xmlDoc),
|
|
8527
|
+
plainTextContent: extra.plainTextContent === true
|
|
8526
8528
|
};
|
|
8527
8529
|
const reconciledOxml = serializeVirtualGridToOoxml(oldGrid, operations, options);
|
|
8528
8530
|
const wrappedOxml = `<root xmlns:w="${NS_W}">${reconciledOxml}</root>`;
|
|
@@ -8542,14 +8544,14 @@ function applyTableReconciliation(xmlDoc, modifiedText, serializer, parser, auth
|
|
|
8542
8544
|
targetTable.parentNode.replaceChild(importedTable, targetTable);
|
|
8543
8545
|
return withOoxmlSourceType({ oxml: serializer.serializeToString(xmlDoc), hasChanges: true });
|
|
8544
8546
|
}
|
|
8545
|
-
function applyTextToTableTransformation(xmlDoc, modifiedText, serializer, parser, author, generateRedlines) {
|
|
8547
|
+
function applyTextToTableTransformation(xmlDoc, modifiedText, serializer, parser, author, generateRedlines, extra = {}) {
|
|
8546
8548
|
const revisionIdAllocator = getRevisionIdAllocatorForDocument(xmlDoc);
|
|
8547
8549
|
const tableData = parseTable(modifiedText);
|
|
8548
8550
|
if (!tableData || tableData.rows.length === 0 && tableData.headers.length === 0) {
|
|
8549
8551
|
log("[OxmlEngine] Failed to parse table data from Markdown");
|
|
8550
8552
|
return noChanges(serializer, xmlDoc);
|
|
8551
8553
|
}
|
|
8552
|
-
const tableOoxml = generateTableOoxml(tableData, { generateRedlines, author, revisionIdAllocator, trackAsBlock: true });
|
|
8554
|
+
const tableOoxml = generateTableOoxml(tableData, { generateRedlines, author, revisionIdAllocator, trackAsBlock: true, plainTextContent: extra.plainTextContent === true });
|
|
8553
8555
|
const tableDoc = parseOoxmlSafe(`<root xmlns:w="${NS_W}">${tableOoxml}</root>`, "application/xml").doc;
|
|
8554
8556
|
if (!tableDoc) return noChanges(serializer, xmlDoc);
|
|
8555
8557
|
const tableParseError = getXmlParseError(tableDoc);
|
|
@@ -9748,11 +9750,11 @@ async function applyRedlineToOxml(oxml, originalText, modifiedText, options = {}
|
|
|
9748
9750
|
if (isMarkdownTable && !hasTables) {
|
|
9749
9751
|
recordRouteSelection(options, "table", { transformation: "text-to-table" });
|
|
9750
9752
|
log("[OxmlEngine] Text-to-table transformation: generating new table from Markdown");
|
|
9751
|
-
return finalize(applyTextToTableTransformation(xmlDoc, cleanModifiedText, serializer, null, author, generateRedlines));
|
|
9753
|
+
return finalize(applyTextToTableTransformation(xmlDoc, cleanModifiedText, serializer, null, author, generateRedlines, { plainTextContent: options.plainTextContent === true }));
|
|
9752
9754
|
}
|
|
9753
9755
|
if (hasTables && isMarkdownTable) {
|
|
9754
9756
|
recordRouteSelection(options, "table", { transformation: "table-reconciliation" });
|
|
9755
|
-
return finalize(applyTableReconciliation(xmlDoc, cleanModifiedText, serializer, null, author, generateRedlines));
|
|
9757
|
+
return finalize(applyTableReconciliation(xmlDoc, cleanModifiedText, serializer, null, author, generateRedlines, { plainTextContent: options.plainTextContent === true }));
|
|
9756
9758
|
}
|
|
9757
9759
|
if (usesSurgicalTextMode) {
|
|
9758
9760
|
recordRouteSelection(options, "surgical", { tableScoped: hasTables });
|
|
@@ -14048,232 +14050,6 @@ function applyCommentReplyToParts({ commentsXml, commentsExtendedXml = null, doc
|
|
|
14048
14050
|
};
|
|
14049
14051
|
}
|
|
14050
14052
|
|
|
14051
|
-
// engine/formatting-removal.js
|
|
14052
|
-
function removeNode2(node) {
|
|
14053
|
-
if (node?.parentNode) {
|
|
14054
|
-
node.parentNode.removeChild(node);
|
|
14055
|
-
}
|
|
14056
|
-
}
|
|
14057
|
-
function removeFormattingFromRPr(rPr, formatTypes = ["all"]) {
|
|
14058
|
-
if (!rPr) return null;
|
|
14059
|
-
const rPrClone = rPr.cloneNode(true);
|
|
14060
|
-
if (formatTypes.includes("all")) {
|
|
14061
|
-
const toRemove = [
|
|
14062
|
-
"w:b",
|
|
14063
|
-
"w:i",
|
|
14064
|
-
"w:u",
|
|
14065
|
-
"w:strike",
|
|
14066
|
-
"w:dstrike",
|
|
14067
|
-
"w:color",
|
|
14068
|
-
"w:sz",
|
|
14069
|
-
"w:szCs",
|
|
14070
|
-
"w:rFonts",
|
|
14071
|
-
"w:highlight",
|
|
14072
|
-
"w:vertAlign",
|
|
14073
|
-
"w:spacing",
|
|
14074
|
-
"w:w",
|
|
14075
|
-
"w:kern",
|
|
14076
|
-
"w:position"
|
|
14077
|
-
];
|
|
14078
|
-
toRemove.forEach((tag) => {
|
|
14079
|
-
const elements = rPrClone.querySelectorAll(`${tag}, ${tag.replace("w:", "")}`);
|
|
14080
|
-
elements.forEach(removeNode2);
|
|
14081
|
-
});
|
|
14082
|
-
} else {
|
|
14083
|
-
const tagMap = {
|
|
14084
|
-
"bold": "w:b",
|
|
14085
|
-
"italic": "w:i",
|
|
14086
|
-
"underline": "w:u",
|
|
14087
|
-
"strikethrough": "w:strike",
|
|
14088
|
-
"doubleStrike": "w:dstrike",
|
|
14089
|
-
"color": "w:color",
|
|
14090
|
-
"highlight": "w:highlight",
|
|
14091
|
-
"fontSize": "w:sz",
|
|
14092
|
-
"fontSizeCs": "w:szCs",
|
|
14093
|
-
// Complex script font size
|
|
14094
|
-
"fontFamily": "w:rFonts",
|
|
14095
|
-
"superscript": "w:vertAlign",
|
|
14096
|
-
"subscript": "w:vertAlign"
|
|
14097
|
-
};
|
|
14098
|
-
formatTypes.forEach((type) => {
|
|
14099
|
-
const tag = tagMap[type];
|
|
14100
|
-
if (tag) {
|
|
14101
|
-
const elements = rPrClone.querySelectorAll(`${tag}, ${tag.replace("w:", "")}`);
|
|
14102
|
-
elements.forEach(removeNode2);
|
|
14103
|
-
}
|
|
14104
|
-
});
|
|
14105
|
-
}
|
|
14106
|
-
return rPrClone.children.length > 0 ? rPrClone : null;
|
|
14107
|
-
}
|
|
14108
|
-
function applyFormattingRemovalToOoxml(ooxmlString, targetText, formatTypes) {
|
|
14109
|
-
if (!targetText || !ooxmlString) return ooxmlString;
|
|
14110
|
-
const doc = parseOoxml(ooxmlString);
|
|
14111
|
-
if (!doc) return ooxmlString;
|
|
14112
|
-
const NS_W11 = "http://schemas.openxmlformats.org/wordprocessingml/2006/main";
|
|
14113
|
-
const runs = doc.getElementsByTagNameNS(NS_W11, "r");
|
|
14114
|
-
const insertions = doc.getElementsByTagNameNS(NS_W11, "ins");
|
|
14115
|
-
const allRuns = [...Array.from(runs)];
|
|
14116
|
-
for (const ins of insertions) {
|
|
14117
|
-
const insideRuns = ins.getElementsByTagNameNS(NS_W11, "r");
|
|
14118
|
-
allRuns.push(...Array.from(insideRuns));
|
|
14119
|
-
}
|
|
14120
|
-
for (const run of allRuns) {
|
|
14121
|
-
const textNodes = run.getElementsByTagNameNS(NS_W11, "t");
|
|
14122
|
-
const runText = Array.from(textNodes).map((t) => t.textContent).join("");
|
|
14123
|
-
if (runText.includes(targetText) || runText === targetText) {
|
|
14124
|
-
const rPrElements = run.getElementsByTagNameNS(NS_W11, "rPr");
|
|
14125
|
-
if (rPrElements.length > 0) {
|
|
14126
|
-
const rPr = rPrElements[0];
|
|
14127
|
-
const newRPr = removeFormattingFromRPr(rPr, formatTypes);
|
|
14128
|
-
if (newRPr) {
|
|
14129
|
-
if (rPr.parentNode) {
|
|
14130
|
-
rPr.parentNode.replaceChild(newRPr, rPr);
|
|
14131
|
-
}
|
|
14132
|
-
} else {
|
|
14133
|
-
removeNode2(rPr);
|
|
14134
|
-
}
|
|
14135
|
-
}
|
|
14136
|
-
}
|
|
14137
|
-
}
|
|
14138
|
-
return serializeOoxml(doc);
|
|
14139
|
-
}
|
|
14140
|
-
var HIGHLIGHT_COLOR_MAP = {
|
|
14141
|
-
"yellow": "yellow",
|
|
14142
|
-
"green": "green",
|
|
14143
|
-
"cyan": "cyan",
|
|
14144
|
-
"magenta": "magenta",
|
|
14145
|
-
"blue": "blue",
|
|
14146
|
-
"red": "red",
|
|
14147
|
-
"darkblue": "darkBlue",
|
|
14148
|
-
"darkcyan": "darkCyan",
|
|
14149
|
-
"darkgreen": "darkGreen",
|
|
14150
|
-
"darkmagenta": "darkMagenta",
|
|
14151
|
-
"darkred": "darkRed",
|
|
14152
|
-
"darkyellow": "darkYellow",
|
|
14153
|
-
"gray25": "lightGray",
|
|
14154
|
-
"gray50": "darkGray",
|
|
14155
|
-
"black": "black",
|
|
14156
|
-
"white": "white"
|
|
14157
|
-
};
|
|
14158
|
-
function injectHighlightIntoRPr(doc, rPr, color = "yellow", options = {}) {
|
|
14159
|
-
const NS_W11 = "http://schemas.openxmlformats.org/wordprocessingml/2006/main";
|
|
14160
|
-
const ooxmlColor = HIGHLIGHT_COLOR_MAP[color.toLowerCase()] || "yellow";
|
|
14161
|
-
const generateRedlines = options?.generateRedlines ?? false;
|
|
14162
|
-
const author = options?.author || getDefaultAuthor();
|
|
14163
|
-
let rPrElement = rPr;
|
|
14164
|
-
if (!rPrElement) {
|
|
14165
|
-
rPrElement = createWordElement(doc, "w:rPr");
|
|
14166
|
-
} else {
|
|
14167
|
-
rPrElement = rPr.cloneNode(true);
|
|
14168
|
-
}
|
|
14169
|
-
let previousRPrState = null;
|
|
14170
|
-
if (generateRedlines) {
|
|
14171
|
-
previousRPrState = createWordElement(doc, "w:rPr");
|
|
14172
|
-
Array.from(rPrElement.childNodes).forEach((child) => {
|
|
14173
|
-
if (child.nodeName !== "w:rPrChange") {
|
|
14174
|
-
previousRPrState.appendChild(child.cloneNode(true));
|
|
14175
|
-
}
|
|
14176
|
-
});
|
|
14177
|
-
}
|
|
14178
|
-
const existingHighlight = rPrElement.getElementsByTagNameNS(NS_W11, "highlight");
|
|
14179
|
-
Array.from(existingHighlight).forEach(removeNode2);
|
|
14180
|
-
const highlightEl = createWordElement(doc, "w:highlight");
|
|
14181
|
-
highlightEl.setAttributeNS(NS_W11, "w:val", ooxmlColor);
|
|
14182
|
-
rPrElement.appendChild(highlightEl);
|
|
14183
|
-
if (generateRedlines && previousRPrState) {
|
|
14184
|
-
const rPrChange = createWordElement(doc, "w:rPrChange");
|
|
14185
|
-
const metadata = createRevisionMetadata(author, doc);
|
|
14186
|
-
rPrChange.setAttribute("w:id", String(metadata.id));
|
|
14187
|
-
rPrChange.setAttribute("w:author", metadata.author);
|
|
14188
|
-
rPrChange.setAttribute("w:date", metadata.date);
|
|
14189
|
-
rPrChange.appendChild(previousRPrState);
|
|
14190
|
-
const existingChange = rPrElement.getElementsByTagNameNS(NS_W11, "rPrChange");
|
|
14191
|
-
Array.from(existingChange).forEach(removeNode2);
|
|
14192
|
-
rPrElement.appendChild(rPrChange);
|
|
14193
|
-
}
|
|
14194
|
-
return rPrElement;
|
|
14195
|
-
}
|
|
14196
|
-
function applyHighlightToOoxml(ooxmlString, targetText, color = "yellow", options = {}) {
|
|
14197
|
-
if (!targetText || !ooxmlString) return ooxmlString;
|
|
14198
|
-
const doc = parseOoxml(ooxmlString);
|
|
14199
|
-
if (!doc) return ooxmlString;
|
|
14200
|
-
let revisionIdAllocator;
|
|
14201
|
-
if (options?._revisionIdAllocator instanceof RevisionIdAllocator) {
|
|
14202
|
-
revisionIdAllocator = options._revisionIdAllocator;
|
|
14203
|
-
seedRevisionIdsFromDocument(doc, revisionIdAllocator);
|
|
14204
|
-
} else {
|
|
14205
|
-
revisionIdAllocator = createRevisionIdAllocator(doc);
|
|
14206
|
-
}
|
|
14207
|
-
const NS_W11 = "http://schemas.openxmlformats.org/wordprocessingml/2006/main";
|
|
14208
|
-
const getRunText = (run) => {
|
|
14209
|
-
const textNodes = run.getElementsByTagNameNS(NS_W11, "t");
|
|
14210
|
-
return Array.from(textNodes).map((t) => t.textContent).join("");
|
|
14211
|
-
};
|
|
14212
|
-
const allRuns = Array.from(doc.getElementsByTagNameNS(NS_W11, "r"));
|
|
14213
|
-
const sourceRunsWithClaimedRevisionIds = /* @__PURE__ */ new WeakSet();
|
|
14214
|
-
const cloneRunWithText2 = (sourceRun, text2, shouldHighlight) => {
|
|
14215
|
-
const clonedRun = sourceRun.cloneNode(true);
|
|
14216
|
-
if (sourceRunsWithClaimedRevisionIds.has(sourceRun)) {
|
|
14217
|
-
refreshRunPropertyChangeIds(clonedRun, revisionIdAllocator);
|
|
14218
|
-
} else {
|
|
14219
|
-
sourceRunsWithClaimedRevisionIds.add(sourceRun);
|
|
14220
|
-
}
|
|
14221
|
-
const textNodes = clonedRun.getElementsByTagNameNS(NS_W11, "t");
|
|
14222
|
-
Array.from(textNodes).forEach(removeNode2);
|
|
14223
|
-
const newText = createWordElement(doc, "w:t");
|
|
14224
|
-
newText.setAttribute("xml:space", "preserve");
|
|
14225
|
-
newText.textContent = text2;
|
|
14226
|
-
clonedRun.appendChild(newText);
|
|
14227
|
-
if (shouldHighlight) {
|
|
14228
|
-
const rPrElements = clonedRun.getElementsByTagNameNS(NS_W11, "rPr");
|
|
14229
|
-
const existingRPr = rPrElements.length > 0 ? rPrElements[0] : null;
|
|
14230
|
-
const newRPr = injectHighlightIntoRPr(doc, existingRPr, color, options);
|
|
14231
|
-
if (existingRPr) {
|
|
14232
|
-
clonedRun.replaceChild(newRPr, existingRPr);
|
|
14233
|
-
} else {
|
|
14234
|
-
clonedRun.insertBefore(newRPr, clonedRun.firstChild);
|
|
14235
|
-
}
|
|
14236
|
-
}
|
|
14237
|
-
return clonedRun;
|
|
14238
|
-
};
|
|
14239
|
-
for (const run of allRuns) {
|
|
14240
|
-
const runText = getRunText(run);
|
|
14241
|
-
if (!runText) continue;
|
|
14242
|
-
const matchIndexes = [];
|
|
14243
|
-
let searchOffset = 0;
|
|
14244
|
-
while (searchOffset <= runText.length - targetText.length) {
|
|
14245
|
-
const matchIndex = runText.indexOf(targetText, searchOffset);
|
|
14246
|
-
if (matchIndex === -1) break;
|
|
14247
|
-
matchIndexes.push(matchIndex);
|
|
14248
|
-
searchOffset = matchIndex + targetText.length;
|
|
14249
|
-
}
|
|
14250
|
-
if (matchIndexes.length === 0) continue;
|
|
14251
|
-
const parent = run.parentNode;
|
|
14252
|
-
if (!parent) {
|
|
14253
|
-
console.warn("[Highlight] Run parent is null; skipping. Likely already processed.");
|
|
14254
|
-
continue;
|
|
14255
|
-
}
|
|
14256
|
-
const fragment = doc.createDocumentFragment();
|
|
14257
|
-
let cursor = 0;
|
|
14258
|
-
for (const matchIndex of matchIndexes) {
|
|
14259
|
-
if (matchIndex > cursor) {
|
|
14260
|
-
fragment.appendChild(cloneRunWithText2(run, runText.slice(cursor, matchIndex), false));
|
|
14261
|
-
}
|
|
14262
|
-
fragment.appendChild(cloneRunWithText2(
|
|
14263
|
-
run,
|
|
14264
|
-
runText.slice(matchIndex, matchIndex + targetText.length),
|
|
14265
|
-
true
|
|
14266
|
-
));
|
|
14267
|
-
cursor = matchIndex + targetText.length;
|
|
14268
|
-
}
|
|
14269
|
-
if (cursor < runText.length) {
|
|
14270
|
-
fragment.appendChild(cloneRunWithText2(run, runText.slice(cursor), false));
|
|
14271
|
-
}
|
|
14272
|
-
parent.replaceChild(fragment, run);
|
|
14273
|
-
}
|
|
14274
|
-
return serializeOoxml(doc);
|
|
14275
|
-
}
|
|
14276
|
-
|
|
14277
14053
|
// services/package-parts.js
|
|
14278
14054
|
var WORD_ML = "application/vnd.openxmlformats-officedocument.wordprocessingml";
|
|
14279
14055
|
var REL_OFFICE_DOC = "http://schemas.openxmlformats.org/officeDocument/2006/relationships";
|
|
@@ -14561,31 +14337,34 @@ async function ensureNumberingArtifactsInZip(zip, numberingXmlList, options = {}
|
|
|
14561
14337
|
}
|
|
14562
14338
|
}
|
|
14563
14339
|
}
|
|
14340
|
+
function mergeCommentsPartXml(existingXml, commentsXml, options = {}) {
|
|
14341
|
+
if (!existingXml || options.replaceExisting === true) return commentsXml;
|
|
14342
|
+
const existingDoc = parseXmlStrictStandalone(existingXml, "word/comments.xml (existing)");
|
|
14343
|
+
const incomingDoc = parseXmlStrictStandalone(commentsXml, "word/comments.xml (incoming)");
|
|
14344
|
+
const incomingComments = Array.from(incomingDoc.documentElement.getElementsByTagNameNS(NS_W7, "comment"));
|
|
14345
|
+
if (incomingComments.length === 0) return existingXml;
|
|
14346
|
+
const existingRoot = existingDoc.documentElement;
|
|
14347
|
+
const usedIds = new Set(
|
|
14348
|
+
Array.from(existingRoot.getElementsByTagNameNS(NS_W7, "comment")).map((comment) => comment.getAttribute("w:id") || comment.getAttribute("id")).filter(Boolean)
|
|
14349
|
+
);
|
|
14350
|
+
for (const incomingComment of incomingComments) {
|
|
14351
|
+
const id = incomingComment.getAttribute("w:id") || incomingComment.getAttribute("id");
|
|
14352
|
+
if (id && usedIds.has(id)) {
|
|
14353
|
+
throw Object.assign(new Error(`Duplicate comment id: ${id}`), { code: "DUPLICATE_COMMENT_ID", commentId: id });
|
|
14354
|
+
}
|
|
14355
|
+
if (id) usedIds.add(id);
|
|
14356
|
+
existingRoot.appendChild(existingDoc.importNode(incomingComment, true));
|
|
14357
|
+
}
|
|
14358
|
+
return createSerializer().serializeToString(existingDoc);
|
|
14359
|
+
}
|
|
14564
14360
|
async function ensureCommentsArtifactsInZip(zip, commentsXml, options = {}) {
|
|
14565
14361
|
const onInfo = typeof options?.onInfo === "function" ? options.onInfo : () => {
|
|
14566
14362
|
};
|
|
14567
14363
|
if (!commentsXml) return;
|
|
14568
14364
|
const serializer = createSerializer();
|
|
14569
14365
|
const existingText = await readZipText(zip, COMMENTS_PATH);
|
|
14570
|
-
if (!existingText || options.replaceExisting === true)
|
|
14571
|
-
|
|
14572
|
-
zip.file(COMMENTS_PATH, commentsXml);
|
|
14573
|
-
} else {
|
|
14574
|
-
const existingDoc = parseXmlStrictStandalone(existingText, "word/comments.xml (existing)");
|
|
14575
|
-
const incomingDoc = parseXmlStrictStandalone(commentsXml, "word/comments.xml (incoming)");
|
|
14576
|
-
const existingRoot = existingDoc.documentElement;
|
|
14577
|
-
const existingIds = new Set(
|
|
14578
|
-
Array.from(existingRoot.getElementsByTagNameNS(NS_W7, "comment")).map((comment) => comment.getAttribute("w:id") || comment.getAttribute("id")).filter(Boolean)
|
|
14579
|
-
);
|
|
14580
|
-
for (const incomingComment of Array.from(incomingDoc.documentElement.getElementsByTagNameNS(NS_W7, "comment"))) {
|
|
14581
|
-
const id = incomingComment.getAttribute("w:id") || incomingComment.getAttribute("id");
|
|
14582
|
-
if (id && existingIds.has(id)) {
|
|
14583
|
-
throw new Error(`Duplicate comment id: ${id}`);
|
|
14584
|
-
}
|
|
14585
|
-
existingRoot.appendChild(existingDoc.importNode(incomingComment, true));
|
|
14586
|
-
}
|
|
14587
|
-
zip.file(COMMENTS_PATH, serializer.serializeToString(existingDoc));
|
|
14588
|
-
}
|
|
14366
|
+
if (!existingText || options.replaceExisting === true) onInfo("[Demo] Adding comments.xml");
|
|
14367
|
+
zip.file(COMMENTS_PATH, mergeCommentsPartXml(existingText, commentsXml, options));
|
|
14589
14368
|
const ctText = await readZipText(zip, CONTENT_TYPES_PATH);
|
|
14590
14369
|
if (ctText) {
|
|
14591
14370
|
const ctDoc = parseXmlStrictStandalone(ctText, CONTENT_TYPES_PATH);
|
|
@@ -14821,6 +14600,318 @@ async function validateDocxPackage(zip) {
|
|
|
14821
14600
|
}
|
|
14822
14601
|
}
|
|
14823
14602
|
|
|
14603
|
+
// services/comment-parts-merge.js
|
|
14604
|
+
var PART_KEYS = Object.freeze({
|
|
14605
|
+
comments: "commentsXml",
|
|
14606
|
+
commentsExtended: "commentsExtendedXml",
|
|
14607
|
+
commentsIds: "commentsIdsXml",
|
|
14608
|
+
commentsExtensible: "commentsExtensibleXml"
|
|
14609
|
+
});
|
|
14610
|
+
var partsOf = (parts) => Object.fromEntries(Object.values(PART_KEYS).map((key) => [key, parts?.[key] || null]));
|
|
14611
|
+
function mergeCommentParts(parts = {}, output = {}) {
|
|
14612
|
+
const before = partsOf(parts);
|
|
14613
|
+
const after = { ...before };
|
|
14614
|
+
const fail = (error2) => ({ status: "error", hasChanges: false, parts: before, changedParts: [], addedParts: [], error: error2 });
|
|
14615
|
+
const commentsMode = output?.commentsXmlMode === "replace" ? "replace" : "merge";
|
|
14616
|
+
const extendedMode = output?.commentsExtendedXmlMode === "replace" ? "replace" : "merge";
|
|
14617
|
+
for (const [label, value] of [
|
|
14618
|
+
...Object.values(PART_KEYS).map((key) => [`parts.${key}`, before[key]]),
|
|
14619
|
+
["output.commentsXml", output?.commentsXml],
|
|
14620
|
+
["output.commentsExtendedXml", output?.commentsExtendedXml]
|
|
14621
|
+
]) {
|
|
14622
|
+
if (value != null && typeof value !== "string") {
|
|
14623
|
+
return fail({ code: "INVALID_COMMENT_PARTS", message: `${label} must be an XML string, null or omitted (got ${typeof value}).` });
|
|
14624
|
+
}
|
|
14625
|
+
}
|
|
14626
|
+
const requirePart = (xml, part, rootName, label) => {
|
|
14627
|
+
let doc;
|
|
14628
|
+
try {
|
|
14629
|
+
doc = parseXmlStrictStandalone(xml, label);
|
|
14630
|
+
} catch (error2) {
|
|
14631
|
+
throw Object.assign(error2, { part });
|
|
14632
|
+
}
|
|
14633
|
+
if (doc.documentElement?.localName !== rootName) {
|
|
14634
|
+
throw Object.assign(new Error(`${label}: expected a <${rootName}> root element, found <${doc.documentElement?.localName || "none"}>.`), { code: "INVALID_COMMENT_PARTS", part });
|
|
14635
|
+
}
|
|
14636
|
+
return doc;
|
|
14637
|
+
};
|
|
14638
|
+
try {
|
|
14639
|
+
if (output?.commentsXml) {
|
|
14640
|
+
const incomingDoc = requirePart(output.commentsXml, "comments", "comments", "word/comments.xml (incoming)");
|
|
14641
|
+
const seen = /* @__PURE__ */ new Set();
|
|
14642
|
+
for (const node of Array.from(incomingDoc.getElementsByTagNameNS("*", "comment"))) {
|
|
14643
|
+
const id = node.getAttribute("w:id") || node.getAttribute("id");
|
|
14644
|
+
if (!id) continue;
|
|
14645
|
+
if (seen.has(id)) {
|
|
14646
|
+
throw Object.assign(new Error(`Duplicate comment id: ${id}`), { code: "DUPLICATE_COMMENT_ID", commentId: id, part: "comments" });
|
|
14647
|
+
}
|
|
14648
|
+
seen.add(id);
|
|
14649
|
+
}
|
|
14650
|
+
if (before.commentsXml) requirePart(before.commentsXml, "comments", "comments", "word/comments.xml (existing)");
|
|
14651
|
+
if (before.commentsIdsXml) requirePart(before.commentsIdsXml, "commentsIds", "commentsIds", "word/commentsIds.xml (existing)");
|
|
14652
|
+
if (before.commentsExtensibleXml) requirePart(before.commentsExtensibleXml, "commentsExtensible", "commentsExtensible", "word/commentsExtensible.xml (existing)");
|
|
14653
|
+
after.commentsXml = mergeCommentsPartXml(before.commentsXml, output.commentsXml, { replaceExisting: commentsMode === "replace" });
|
|
14654
|
+
Object.assign(after, reconcileCommentSiblingParts({
|
|
14655
|
+
commentsXml: after.commentsXml,
|
|
14656
|
+
commentsIdsXml: after.commentsIdsXml,
|
|
14657
|
+
commentsExtensibleXml: after.commentsExtensibleXml
|
|
14658
|
+
}));
|
|
14659
|
+
}
|
|
14660
|
+
if (output?.commentsExtendedXml) {
|
|
14661
|
+
if (extendedMode !== "replace" && before.commentsExtendedXml) {
|
|
14662
|
+
return fail({
|
|
14663
|
+
code: "UNSUPPORTED_EXISTING_COMMENT_THREAD_METADATA",
|
|
14664
|
+
part: "commentsExtended",
|
|
14665
|
+
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.'
|
|
14666
|
+
});
|
|
14667
|
+
}
|
|
14668
|
+
requirePart(output.commentsExtendedXml, "commentsExtended", "commentsEx", "word/commentsExtended.xml (incoming)");
|
|
14669
|
+
after.commentsExtendedXml = output.commentsExtendedXml;
|
|
14670
|
+
}
|
|
14671
|
+
} catch (error2) {
|
|
14672
|
+
return fail({
|
|
14673
|
+
code: error2?.code || "COMMENT_PARTS_MERGE_FAILED",
|
|
14674
|
+
message: error2?.message || String(error2),
|
|
14675
|
+
...error2?.part || error2?.code === "DUPLICATE_COMMENT_ID" ? { part: error2.part || "comments" } : {},
|
|
14676
|
+
...error2?.commentId ? { commentId: error2.commentId } : {}
|
|
14677
|
+
});
|
|
14678
|
+
}
|
|
14679
|
+
const changedParts = [];
|
|
14680
|
+
const addedParts = [];
|
|
14681
|
+
for (const [name, key] of Object.entries(PART_KEYS)) {
|
|
14682
|
+
if (after[key] === before[key]) continue;
|
|
14683
|
+
changedParts.push(name);
|
|
14684
|
+
if (!before[key]) addedParts.push(name);
|
|
14685
|
+
}
|
|
14686
|
+
return { status: "ok", hasChanges: changedParts.length > 0, parts: after, changedParts, addedParts };
|
|
14687
|
+
}
|
|
14688
|
+
|
|
14689
|
+
// engine/formatting-removal.js
|
|
14690
|
+
function removeNode2(node) {
|
|
14691
|
+
if (node?.parentNode) {
|
|
14692
|
+
node.parentNode.removeChild(node);
|
|
14693
|
+
}
|
|
14694
|
+
}
|
|
14695
|
+
function removeFormattingFromRPr(rPr, formatTypes = ["all"]) {
|
|
14696
|
+
if (!rPr) return null;
|
|
14697
|
+
const rPrClone = rPr.cloneNode(true);
|
|
14698
|
+
if (formatTypes.includes("all")) {
|
|
14699
|
+
const toRemove = [
|
|
14700
|
+
"w:b",
|
|
14701
|
+
"w:i",
|
|
14702
|
+
"w:u",
|
|
14703
|
+
"w:strike",
|
|
14704
|
+
"w:dstrike",
|
|
14705
|
+
"w:color",
|
|
14706
|
+
"w:sz",
|
|
14707
|
+
"w:szCs",
|
|
14708
|
+
"w:rFonts",
|
|
14709
|
+
"w:highlight",
|
|
14710
|
+
"w:vertAlign",
|
|
14711
|
+
"w:spacing",
|
|
14712
|
+
"w:w",
|
|
14713
|
+
"w:kern",
|
|
14714
|
+
"w:position"
|
|
14715
|
+
];
|
|
14716
|
+
toRemove.forEach((tag) => {
|
|
14717
|
+
const elements = rPrClone.querySelectorAll(`${tag}, ${tag.replace("w:", "")}`);
|
|
14718
|
+
elements.forEach(removeNode2);
|
|
14719
|
+
});
|
|
14720
|
+
} else {
|
|
14721
|
+
const tagMap = {
|
|
14722
|
+
"bold": "w:b",
|
|
14723
|
+
"italic": "w:i",
|
|
14724
|
+
"underline": "w:u",
|
|
14725
|
+
"strikethrough": "w:strike",
|
|
14726
|
+
"doubleStrike": "w:dstrike",
|
|
14727
|
+
"color": "w:color",
|
|
14728
|
+
"highlight": "w:highlight",
|
|
14729
|
+
"fontSize": "w:sz",
|
|
14730
|
+
"fontSizeCs": "w:szCs",
|
|
14731
|
+
// Complex script font size
|
|
14732
|
+
"fontFamily": "w:rFonts",
|
|
14733
|
+
"superscript": "w:vertAlign",
|
|
14734
|
+
"subscript": "w:vertAlign"
|
|
14735
|
+
};
|
|
14736
|
+
formatTypes.forEach((type) => {
|
|
14737
|
+
const tag = tagMap[type];
|
|
14738
|
+
if (tag) {
|
|
14739
|
+
const elements = rPrClone.querySelectorAll(`${tag}, ${tag.replace("w:", "")}`);
|
|
14740
|
+
elements.forEach(removeNode2);
|
|
14741
|
+
}
|
|
14742
|
+
});
|
|
14743
|
+
}
|
|
14744
|
+
return rPrClone.children.length > 0 ? rPrClone : null;
|
|
14745
|
+
}
|
|
14746
|
+
function applyFormattingRemovalToOoxml(ooxmlString, targetText, formatTypes) {
|
|
14747
|
+
if (!targetText || !ooxmlString) return ooxmlString;
|
|
14748
|
+
const doc = parseOoxml(ooxmlString);
|
|
14749
|
+
if (!doc) return ooxmlString;
|
|
14750
|
+
const NS_W11 = "http://schemas.openxmlformats.org/wordprocessingml/2006/main";
|
|
14751
|
+
const runs = doc.getElementsByTagNameNS(NS_W11, "r");
|
|
14752
|
+
const insertions = doc.getElementsByTagNameNS(NS_W11, "ins");
|
|
14753
|
+
const allRuns = [...Array.from(runs)];
|
|
14754
|
+
for (const ins of insertions) {
|
|
14755
|
+
const insideRuns = ins.getElementsByTagNameNS(NS_W11, "r");
|
|
14756
|
+
allRuns.push(...Array.from(insideRuns));
|
|
14757
|
+
}
|
|
14758
|
+
for (const run of allRuns) {
|
|
14759
|
+
const textNodes = run.getElementsByTagNameNS(NS_W11, "t");
|
|
14760
|
+
const runText = Array.from(textNodes).map((t) => t.textContent).join("");
|
|
14761
|
+
if (runText.includes(targetText) || runText === targetText) {
|
|
14762
|
+
const rPrElements = run.getElementsByTagNameNS(NS_W11, "rPr");
|
|
14763
|
+
if (rPrElements.length > 0) {
|
|
14764
|
+
const rPr = rPrElements[0];
|
|
14765
|
+
const newRPr = removeFormattingFromRPr(rPr, formatTypes);
|
|
14766
|
+
if (newRPr) {
|
|
14767
|
+
if (rPr.parentNode) {
|
|
14768
|
+
rPr.parentNode.replaceChild(newRPr, rPr);
|
|
14769
|
+
}
|
|
14770
|
+
} else {
|
|
14771
|
+
removeNode2(rPr);
|
|
14772
|
+
}
|
|
14773
|
+
}
|
|
14774
|
+
}
|
|
14775
|
+
}
|
|
14776
|
+
return serializeOoxml(doc);
|
|
14777
|
+
}
|
|
14778
|
+
var HIGHLIGHT_COLOR_MAP = {
|
|
14779
|
+
"yellow": "yellow",
|
|
14780
|
+
"green": "green",
|
|
14781
|
+
"cyan": "cyan",
|
|
14782
|
+
"magenta": "magenta",
|
|
14783
|
+
"blue": "blue",
|
|
14784
|
+
"red": "red",
|
|
14785
|
+
"darkblue": "darkBlue",
|
|
14786
|
+
"darkcyan": "darkCyan",
|
|
14787
|
+
"darkgreen": "darkGreen",
|
|
14788
|
+
"darkmagenta": "darkMagenta",
|
|
14789
|
+
"darkred": "darkRed",
|
|
14790
|
+
"darkyellow": "darkYellow",
|
|
14791
|
+
"gray25": "lightGray",
|
|
14792
|
+
"gray50": "darkGray",
|
|
14793
|
+
"black": "black",
|
|
14794
|
+
"white": "white"
|
|
14795
|
+
};
|
|
14796
|
+
function injectHighlightIntoRPr(doc, rPr, color = "yellow", options = {}) {
|
|
14797
|
+
const NS_W11 = "http://schemas.openxmlformats.org/wordprocessingml/2006/main";
|
|
14798
|
+
const ooxmlColor = HIGHLIGHT_COLOR_MAP[color.toLowerCase()] || "yellow";
|
|
14799
|
+
const generateRedlines = options?.generateRedlines ?? false;
|
|
14800
|
+
const author = options?.author || getDefaultAuthor();
|
|
14801
|
+
let rPrElement = rPr;
|
|
14802
|
+
if (!rPrElement) {
|
|
14803
|
+
rPrElement = createWordElement(doc, "w:rPr");
|
|
14804
|
+
} else {
|
|
14805
|
+
rPrElement = rPr.cloneNode(true);
|
|
14806
|
+
}
|
|
14807
|
+
let previousRPrState = null;
|
|
14808
|
+
if (generateRedlines) {
|
|
14809
|
+
previousRPrState = createWordElement(doc, "w:rPr");
|
|
14810
|
+
Array.from(rPrElement.childNodes).forEach((child) => {
|
|
14811
|
+
if (child.nodeName !== "w:rPrChange") {
|
|
14812
|
+
previousRPrState.appendChild(child.cloneNode(true));
|
|
14813
|
+
}
|
|
14814
|
+
});
|
|
14815
|
+
}
|
|
14816
|
+
const existingHighlight = rPrElement.getElementsByTagNameNS(NS_W11, "highlight");
|
|
14817
|
+
Array.from(existingHighlight).forEach(removeNode2);
|
|
14818
|
+
const highlightEl = createWordElement(doc, "w:highlight");
|
|
14819
|
+
highlightEl.setAttributeNS(NS_W11, "w:val", ooxmlColor);
|
|
14820
|
+
rPrElement.appendChild(highlightEl);
|
|
14821
|
+
if (generateRedlines && previousRPrState) {
|
|
14822
|
+
const rPrChange = createWordElement(doc, "w:rPrChange");
|
|
14823
|
+
const metadata = createRevisionMetadata(author, doc);
|
|
14824
|
+
rPrChange.setAttribute("w:id", String(metadata.id));
|
|
14825
|
+
rPrChange.setAttribute("w:author", metadata.author);
|
|
14826
|
+
rPrChange.setAttribute("w:date", metadata.date);
|
|
14827
|
+
rPrChange.appendChild(previousRPrState);
|
|
14828
|
+
const existingChange = rPrElement.getElementsByTagNameNS(NS_W11, "rPrChange");
|
|
14829
|
+
Array.from(existingChange).forEach(removeNode2);
|
|
14830
|
+
rPrElement.appendChild(rPrChange);
|
|
14831
|
+
}
|
|
14832
|
+
return rPrElement;
|
|
14833
|
+
}
|
|
14834
|
+
function applyHighlightToOoxml(ooxmlString, targetText, color = "yellow", options = {}) {
|
|
14835
|
+
if (!targetText || !ooxmlString) return ooxmlString;
|
|
14836
|
+
const doc = parseOoxml(ooxmlString);
|
|
14837
|
+
if (!doc) return ooxmlString;
|
|
14838
|
+
let revisionIdAllocator;
|
|
14839
|
+
if (options?._revisionIdAllocator instanceof RevisionIdAllocator) {
|
|
14840
|
+
revisionIdAllocator = options._revisionIdAllocator;
|
|
14841
|
+
seedRevisionIdsFromDocument(doc, revisionIdAllocator);
|
|
14842
|
+
} else {
|
|
14843
|
+
revisionIdAllocator = createRevisionIdAllocator(doc);
|
|
14844
|
+
}
|
|
14845
|
+
const NS_W11 = "http://schemas.openxmlformats.org/wordprocessingml/2006/main";
|
|
14846
|
+
const getRunText = (run) => {
|
|
14847
|
+
const textNodes = run.getElementsByTagNameNS(NS_W11, "t");
|
|
14848
|
+
return Array.from(textNodes).map((t) => t.textContent).join("");
|
|
14849
|
+
};
|
|
14850
|
+
const allRuns = Array.from(doc.getElementsByTagNameNS(NS_W11, "r"));
|
|
14851
|
+
const sourceRunsWithClaimedRevisionIds = /* @__PURE__ */ new WeakSet();
|
|
14852
|
+
const cloneRunWithText2 = (sourceRun, text2, shouldHighlight) => {
|
|
14853
|
+
const clonedRun = sourceRun.cloneNode(true);
|
|
14854
|
+
if (sourceRunsWithClaimedRevisionIds.has(sourceRun)) {
|
|
14855
|
+
refreshRunPropertyChangeIds(clonedRun, revisionIdAllocator);
|
|
14856
|
+
} else {
|
|
14857
|
+
sourceRunsWithClaimedRevisionIds.add(sourceRun);
|
|
14858
|
+
}
|
|
14859
|
+
const textNodes = clonedRun.getElementsByTagNameNS(NS_W11, "t");
|
|
14860
|
+
Array.from(textNodes).forEach(removeNode2);
|
|
14861
|
+
const newText = createWordElement(doc, "w:t");
|
|
14862
|
+
newText.setAttribute("xml:space", "preserve");
|
|
14863
|
+
newText.textContent = text2;
|
|
14864
|
+
clonedRun.appendChild(newText);
|
|
14865
|
+
if (shouldHighlight) {
|
|
14866
|
+
const rPrElements = clonedRun.getElementsByTagNameNS(NS_W11, "rPr");
|
|
14867
|
+
const existingRPr = rPrElements.length > 0 ? rPrElements[0] : null;
|
|
14868
|
+
const newRPr = injectHighlightIntoRPr(doc, existingRPr, color, options);
|
|
14869
|
+
if (existingRPr) {
|
|
14870
|
+
clonedRun.replaceChild(newRPr, existingRPr);
|
|
14871
|
+
} else {
|
|
14872
|
+
clonedRun.insertBefore(newRPr, clonedRun.firstChild);
|
|
14873
|
+
}
|
|
14874
|
+
}
|
|
14875
|
+
return clonedRun;
|
|
14876
|
+
};
|
|
14877
|
+
for (const run of allRuns) {
|
|
14878
|
+
const runText = getRunText(run);
|
|
14879
|
+
if (!runText) continue;
|
|
14880
|
+
const matchIndexes = [];
|
|
14881
|
+
let searchOffset = 0;
|
|
14882
|
+
while (searchOffset <= runText.length - targetText.length) {
|
|
14883
|
+
const matchIndex = runText.indexOf(targetText, searchOffset);
|
|
14884
|
+
if (matchIndex === -1) break;
|
|
14885
|
+
matchIndexes.push(matchIndex);
|
|
14886
|
+
searchOffset = matchIndex + targetText.length;
|
|
14887
|
+
}
|
|
14888
|
+
if (matchIndexes.length === 0) continue;
|
|
14889
|
+
const parent = run.parentNode;
|
|
14890
|
+
if (!parent) {
|
|
14891
|
+
console.warn("[Highlight] Run parent is null; skipping. Likely already processed.");
|
|
14892
|
+
continue;
|
|
14893
|
+
}
|
|
14894
|
+
const fragment = doc.createDocumentFragment();
|
|
14895
|
+
let cursor = 0;
|
|
14896
|
+
for (const matchIndex of matchIndexes) {
|
|
14897
|
+
if (matchIndex > cursor) {
|
|
14898
|
+
fragment.appendChild(cloneRunWithText2(run, runText.slice(cursor, matchIndex), false));
|
|
14899
|
+
}
|
|
14900
|
+
fragment.appendChild(cloneRunWithText2(
|
|
14901
|
+
run,
|
|
14902
|
+
runText.slice(matchIndex, matchIndex + targetText.length),
|
|
14903
|
+
true
|
|
14904
|
+
));
|
|
14905
|
+
cursor = matchIndex + targetText.length;
|
|
14906
|
+
}
|
|
14907
|
+
if (cursor < runText.length) {
|
|
14908
|
+
fragment.appendChild(cloneRunWithText2(run, runText.slice(cursor), false));
|
|
14909
|
+
}
|
|
14910
|
+
parent.replaceChild(fragment, run);
|
|
14911
|
+
}
|
|
14912
|
+
return serializeOoxml(doc);
|
|
14913
|
+
}
|
|
14914
|
+
|
|
14824
14915
|
// orchestration/route-plan.js
|
|
14825
14916
|
var RoutePlanKind = Object.freeze({
|
|
14826
14917
|
STRUCTURED_LIST_DIRECT: "structured_list_direct",
|
|
@@ -18525,7 +18616,7 @@ function deriveParagraphThenTableAppend(modifiedText) {
|
|
|
18525
18616
|
const tableLines = lines.slice(1);
|
|
18526
18617
|
if (!tableLines.every((line) => line.trim().startsWith("|"))) return null;
|
|
18527
18618
|
const separator = tableLines[1].replace(/\s+/g, "");
|
|
18528
|
-
if (!/^\|:?-{3,}:?(\|:?-{3,}:?)
|
|
18619
|
+
if (!/^\|:?-{3,}:?(?:(\|:?-{3,}:?)+\|?|\|)$/.test(separator)) return null;
|
|
18529
18620
|
return { paragraphMarkdown: lines[0].trim(), tableText: tableLines.join("\n") };
|
|
18530
18621
|
}
|
|
18531
18622
|
|
|
@@ -19155,7 +19246,10 @@ async function buildInsertedPlainParagraph(xmlDoc, anchorParagraph, text2, revis
|
|
|
19155
19246
|
String(text2 || ""),
|
|
19156
19247
|
{
|
|
19157
19248
|
author,
|
|
19158
|
-
generateRedlines: false
|
|
19249
|
+
generateRedlines: false,
|
|
19250
|
+
// A caller's structuredContent: false keeps inserted paragraphs plain too: no headings,
|
|
19251
|
+
// and inline Markdown markers stay literal.
|
|
19252
|
+
...options.plainTextContent === true ? { structuredContent: false, plainTextContent: true } : {}
|
|
19159
19253
|
}
|
|
19160
19254
|
);
|
|
19161
19255
|
let sourceParagraph = null;
|
|
@@ -20681,7 +20775,7 @@ async function applyToParagraphByExactText(documentXml, targetText, modifiedText
|
|
|
20681
20775
|
author,
|
|
20682
20776
|
// A wholly inserted paragraph must track its own paragraph mark; otherwise Word's
|
|
20683
20777
|
// Reject All removes the text but leaves an empty paragraph behind.
|
|
20684
|
-
{ generateRedlines, trackParagraphMark: true }
|
|
20778
|
+
{ generateRedlines, trackParagraphMark: true, plainTextContent: options.plainTextContent === true }
|
|
20685
20779
|
);
|
|
20686
20780
|
const imported = xmlDoc.importNode(plainParagraph, true);
|
|
20687
20781
|
ensureParagraphIdsOnImportedNode(imported, operationSession);
|
|
@@ -20820,6 +20914,7 @@ async function applyToParagraphByExactText(documentXml, targetText, modifiedText
|
|
|
20820
20914
|
existingRevisions: options.existingRevisions || "merge-same-author",
|
|
20821
20915
|
structuredContent: options.structuredContent !== false,
|
|
20822
20916
|
explicitStructuredContent: options.explicitStructuredContent === true,
|
|
20917
|
+
plainTextContent: options.plainTextContent === true,
|
|
20823
20918
|
_revisionIdAllocator: revisionIdAllocator,
|
|
20824
20919
|
_isolatedTableCell: useTableScope
|
|
20825
20920
|
}) : await applyRedlineToOxml2(scopedXml, originalTextForApply, effectiveModifiedText, {
|
|
@@ -25716,6 +25811,7 @@ export {
|
|
|
25716
25811
|
isLikelyStructuredTableSourceParagraph,
|
|
25717
25812
|
isMarkdownTableText,
|
|
25718
25813
|
isNodeVisibleInRevisionView,
|
|
25814
|
+
mergeCommentParts,
|
|
25719
25815
|
mergeNumberingParts,
|
|
25720
25816
|
mergeNumberingXmlBySchemaOrder,
|
|
25721
25817
|
normalizeBodySectionOrderStandalone,
|