@adeu/core 1.17.0 → 1.17.2
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/README.md +1 -0
- package/dist/index.cjs +353 -85
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +11 -1
- package/dist/index.d.ts +11 -1
- package/dist/index.js +353 -85
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/engine.qa.test.ts +4 -4
- package/src/engine.tables.test.ts +138 -36
- package/src/engine.ts +433 -105
- package/src/ingest.ts +16 -1
- package/src/mapper.ts +23 -1
- package/src/markup.test.ts +41 -6
- package/src/markup.ts +13 -2
- package/src/repro.feedback.test.ts +160 -0
- package/src/repro.report.test.ts +130 -0
- package/src/repro_qa_report_v3.test.ts +194 -0
- package/src/search_write_engine.test.ts +41 -0
package/dist/index.cjs
CHANGED
|
@@ -1384,7 +1384,19 @@ ${header}`;
|
|
|
1384
1384
|
this._add_virtual_text(" | ", current, null);
|
|
1385
1385
|
current += 3;
|
|
1386
1386
|
}
|
|
1387
|
+
const cell_start = current;
|
|
1387
1388
|
current = this._map_blocks(cell, current);
|
|
1389
|
+
if (!this.clean_view && !this.original_view) {
|
|
1390
|
+
const firstP = cell._element.getElementsByTagName("w:p")[0];
|
|
1391
|
+
const paraId = firstP ? firstP.getAttribute("w14:paraId") : null;
|
|
1392
|
+
if (paraId && firstP) {
|
|
1393
|
+
const cellPara = new Paragraph(firstP, cell);
|
|
1394
|
+
this._add_virtual_text("", current, cellPara);
|
|
1395
|
+
const anchor = `{#cell:${paraId}}`;
|
|
1396
|
+
this._add_virtual_text(anchor, current, cellPara);
|
|
1397
|
+
current += anchor.length;
|
|
1398
|
+
}
|
|
1399
|
+
}
|
|
1388
1400
|
cells_processed += 1;
|
|
1389
1401
|
}
|
|
1390
1402
|
if (ins && !this.clean_view && !this.original_view) {
|
|
@@ -1761,7 +1773,7 @@ ${header}`;
|
|
|
1761
1773
|
target_text = this._strip_markdown_formatting(target_text);
|
|
1762
1774
|
target_text = this._replace_smart_quotes(target_text);
|
|
1763
1775
|
const parts = [];
|
|
1764
|
-
const token_pattern = /(\[_+\])|(\s+)|(['"])|([
|
|
1776
|
+
const token_pattern = /(\[_+\])|(\s+)|(['"])|([.,;:\/\-\[\](){}+=$?*!|#^<>\\%&@~`_])/g;
|
|
1765
1777
|
let last_idx = 0;
|
|
1766
1778
|
let match;
|
|
1767
1779
|
const escapeRegExp = (str) => str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
@@ -2647,7 +2659,7 @@ function _refine_match_boundaries(text, start, end) {
|
|
|
2647
2659
|
function _make_fuzzy_regex(target_text) {
|
|
2648
2660
|
target_text = _replace_smart_quotes(target_text);
|
|
2649
2661
|
const parts = [];
|
|
2650
|
-
const token_pattern = /(_+)|(\s+)|(['"])|([
|
|
2662
|
+
const token_pattern = /(_+)|(\s+)|(['"])|([.,;:\/\-\[\](){}+=$?*!|#^<>\\%&@~`])/g;
|
|
2651
2663
|
const md_noise = "[*_]*";
|
|
2652
2664
|
const structural_noise = "(?:\\s*(?:[*+\\->]|\\d+\\.)\\s+|\\s*\\n\\s*)";
|
|
2653
2665
|
const start_list_marker = "(?:[ \\t]*(?:[*+\\->]|\\d+\\.)\\s+)?";
|
|
@@ -2834,8 +2846,15 @@ function format_ambiguity_error(edit_index, target_text, haystack, match_positio
|
|
|
2834
2846
|
if (remaining > 0) {
|
|
2835
2847
|
lines.push(` ... and ${remaining} more occurrence(s) not shown.`);
|
|
2836
2848
|
}
|
|
2849
|
+
lines.push(" To resolve, re-send this edit using ONE of these strategies:");
|
|
2850
|
+
lines.push(
|
|
2851
|
+
` 1. Set "match_mode": "all" to modify ALL ${total} occurrences (same target_text).`
|
|
2852
|
+
);
|
|
2853
|
+
lines.push(
|
|
2854
|
+
' 2. Set "match_mode": "first" to modify only the FIRST occurrence (same target_text).'
|
|
2855
|
+
);
|
|
2837
2856
|
lines.push(
|
|
2838
|
-
|
|
2857
|
+
' 3. Please provide more surrounding context in your target_text to uniquely identify a single location (keep the default "match_mode": "strict").'
|
|
2839
2858
|
);
|
|
2840
2859
|
return lines.join("\n");
|
|
2841
2860
|
}
|
|
@@ -2862,6 +2881,51 @@ function insertAfter(newNode, refNode) {
|
|
|
2862
2881
|
refNode.parentNode.insertBefore(newNode, refNode.nextSibling);
|
|
2863
2882
|
}
|
|
2864
2883
|
}
|
|
2884
|
+
function takeSnapshot(doc) {
|
|
2885
|
+
const parts = [...doc.pkg.parts];
|
|
2886
|
+
const unzipped = { ...doc.pkg.unzipped };
|
|
2887
|
+
const rels = /* @__PURE__ */ new Map();
|
|
2888
|
+
const elements = /* @__PURE__ */ new Map();
|
|
2889
|
+
for (const part of parts) {
|
|
2890
|
+
rels.set(part, new Map(part.rels));
|
|
2891
|
+
if (part._element) {
|
|
2892
|
+
elements.set(part, part._element.cloneNode(true));
|
|
2893
|
+
}
|
|
2894
|
+
}
|
|
2895
|
+
return { parts, unzipped, rels, elements };
|
|
2896
|
+
}
|
|
2897
|
+
function restoreSnapshot(doc, snapshot) {
|
|
2898
|
+
doc.pkg.parts = [...snapshot.parts];
|
|
2899
|
+
for (const key of Object.keys(doc.pkg.unzipped)) {
|
|
2900
|
+
delete doc.pkg.unzipped[key];
|
|
2901
|
+
}
|
|
2902
|
+
for (const [key, val] of Object.entries(snapshot.unzipped)) {
|
|
2903
|
+
doc.pkg.unzipped[key] = val;
|
|
2904
|
+
}
|
|
2905
|
+
for (const part of snapshot.parts) {
|
|
2906
|
+
part.rels = new Map(snapshot.rels.get(part));
|
|
2907
|
+
const originalEl = snapshot.elements.get(part);
|
|
2908
|
+
if (originalEl && part._element) {
|
|
2909
|
+
const xmlDoc = part._element.ownerDocument;
|
|
2910
|
+
if (xmlDoc && xmlDoc.documentElement) {
|
|
2911
|
+
xmlDoc.replaceChild(originalEl, xmlDoc.documentElement);
|
|
2912
|
+
}
|
|
2913
|
+
part._element = originalEl;
|
|
2914
|
+
}
|
|
2915
|
+
}
|
|
2916
|
+
}
|
|
2917
|
+
function stripMatchingHeadingHashes(target, newText) {
|
|
2918
|
+
if (!target || !newText) return [target, newText];
|
|
2919
|
+
const targetMatch = target.match(/^(#+)\s+/);
|
|
2920
|
+
const newMatch = newText.match(/^(#+)\s+/);
|
|
2921
|
+
if (targetMatch && newMatch && targetMatch[1] === newMatch[1]) {
|
|
2922
|
+
const hashes = targetMatch[1];
|
|
2923
|
+
const targetClean = target.substring(hashes.length).trimStart();
|
|
2924
|
+
const newClean = newText.substring(hashes.length).trimStart();
|
|
2925
|
+
return [targetClean, newClean];
|
|
2926
|
+
}
|
|
2927
|
+
return [target, newText];
|
|
2928
|
+
}
|
|
2865
2929
|
var BatchValidationError = class extends Error {
|
|
2866
2930
|
errors;
|
|
2867
2931
|
constructor(errors) {
|
|
@@ -2870,7 +2934,7 @@ var BatchValidationError = class extends Error {
|
|
|
2870
2934
|
this.errors = errors;
|
|
2871
2935
|
}
|
|
2872
2936
|
};
|
|
2873
|
-
function validate_edit_strings(edits) {
|
|
2937
|
+
function validate_edit_strings(edits, index_offset = 0) {
|
|
2874
2938
|
const errors = [];
|
|
2875
2939
|
for (let i = 0; i < edits.length; i++) {
|
|
2876
2940
|
const edit = edits[i];
|
|
@@ -2878,7 +2942,7 @@ function validate_edit_strings(edits) {
|
|
|
2878
2942
|
const n_text = edit.new_text || "";
|
|
2879
2943
|
if (n_text.includes("{++") || n_text.includes("{--") || n_text.includes("{>>") || n_text.includes("{==")) {
|
|
2880
2944
|
errors.push(
|
|
2881
|
-
`- Edit ${i + 1} Failed: Do not manually write CriticMarkup tags ({++, {--, {>>, {==) in \`new_text\`. The engine handles redlining automatically. To add a comment, use the \`comment\` parameter.`
|
|
2945
|
+
`- Edit ${i + 1 + index_offset} Failed: Do not manually write CriticMarkup tags ({++, {--, {>>, {==) in \`new_text\`. The engine handles redlining automatically. To add a comment, use the \`comment\` parameter.`
|
|
2882
2946
|
);
|
|
2883
2947
|
}
|
|
2884
2948
|
if (t_text.includes("[^") || n_text.includes("[^")) {
|
|
@@ -2889,11 +2953,11 @@ function validate_edit_strings(edits) {
|
|
|
2889
2953
|
(f) => n_fns.filter((x) => x === f).length > t_fns.filter((x) => x === f).length
|
|
2890
2954
|
)) {
|
|
2891
2955
|
errors.push(
|
|
2892
|
-
`- Edit ${i + 1} Failed: Cannot insert footnote/endnote markers via text replace. Markers like \`[^fn-N]\` are read-only projections. Use Word's References menu.`
|
|
2956
|
+
`- Edit ${i + 1 + index_offset} Failed: Cannot insert footnote/endnote markers via text replace. Markers like \`[^fn-N]\` are read-only projections. Use Word's References menu.`
|
|
2893
2957
|
);
|
|
2894
2958
|
} else {
|
|
2895
2959
|
errors.push(
|
|
2896
|
-
`- Edit ${i + 1} Failed: Cannot delete footnote/endnote references via text replace. The marker corresponds to a structural XML element.`
|
|
2960
|
+
`- Edit ${i + 1 + index_offset} Failed: Cannot delete footnote/endnote references via text replace. The marker corresponds to a structural XML element.`
|
|
2897
2961
|
);
|
|
2898
2962
|
}
|
|
2899
2963
|
}
|
|
@@ -2904,16 +2968,16 @@ function validate_edit_strings(edits) {
|
|
|
2904
2968
|
if (t_links.length !== n_links.length) {
|
|
2905
2969
|
if (n_links.length > t_links.length) {
|
|
2906
2970
|
errors.push(
|
|
2907
|
-
`- Edit ${i + 1} Failed: Cannot insert hyperlinks via text replace. Use a dedicated structural operation.`
|
|
2971
|
+
`- Edit ${i + 1 + index_offset} Failed: Cannot insert hyperlinks via text replace. Use a dedicated structural operation.`
|
|
2908
2972
|
);
|
|
2909
2973
|
} else {
|
|
2910
2974
|
errors.push(
|
|
2911
|
-
`- Edit ${i + 1} Failed: Cannot delete hyperlinks via text replace. The marker corresponds to a structural XML element.`
|
|
2975
|
+
`- Edit ${i + 1 + index_offset} Failed: Cannot delete hyperlinks via text replace. The marker corresponds to a structural XML element.`
|
|
2912
2976
|
);
|
|
2913
2977
|
}
|
|
2914
2978
|
} else if (t_links.length > 1 && JSON.stringify(t_links) !== JSON.stringify(n_links)) {
|
|
2915
2979
|
errors.push(
|
|
2916
|
-
`- Edit ${i + 1} Failed: Can only edit or retarget one hyperlink per text replacement. Please split into multiple edits.`
|
|
2980
|
+
`- Edit ${i + 1 + index_offset} Failed: Can only edit or retarget one hyperlink per text replacement. Please split into multiple edits.`
|
|
2917
2981
|
);
|
|
2918
2982
|
}
|
|
2919
2983
|
}
|
|
@@ -2923,17 +2987,17 @@ function validate_edit_strings(edits) {
|
|
|
2923
2987
|
if (t_xrefs.length !== n_xrefs.length) {
|
|
2924
2988
|
if (n_xrefs.length > t_xrefs.length) {
|
|
2925
2989
|
errors.push(
|
|
2926
|
-
`- Edit ${i + 1} Failed: Cannot insert cross-references via text replace. Markers are read-only projections.`
|
|
2990
|
+
`- Edit ${i + 1 + index_offset} Failed: Cannot insert cross-references via text replace. Markers are read-only projections.`
|
|
2927
2991
|
);
|
|
2928
2992
|
} else {
|
|
2929
2993
|
errors.push(
|
|
2930
|
-
`- Edit ${i + 1} Failed: Cannot delete cross-references via text replace. The marker corresponds to a structural XML element.`
|
|
2994
|
+
`- Edit ${i + 1 + index_offset} Failed: Cannot delete cross-references via text replace. The marker corresponds to a structural XML element.`
|
|
2931
2995
|
);
|
|
2932
2996
|
}
|
|
2933
2997
|
} else {
|
|
2934
2998
|
if (JSON.stringify(t_xrefs) !== JSON.stringify(n_xrefs)) {
|
|
2935
2999
|
errors.push(
|
|
2936
|
-
`- Edit ${i + 1} Failed: Modifying or retargeting cross-reference markers is disallowed to prevent dependency corruption.`
|
|
3000
|
+
`- Edit ${i + 1 + index_offset} Failed: Modifying or retargeting cross-reference markers is disallowed to prevent dependency corruption.`
|
|
2937
3001
|
);
|
|
2938
3002
|
}
|
|
2939
3003
|
}
|
|
@@ -2944,7 +3008,7 @@ function validate_edit_strings(edits) {
|
|
|
2944
3008
|
for (const a of n_anchors) {
|
|
2945
3009
|
if (n_anchors.filter((x) => x === a).length > t_anchors.filter((x) => x === a).length) {
|
|
2946
3010
|
errors.push(
|
|
2947
|
-
`- Edit ${i + 1} Failed: Cannot modify or insert internal anchor markers (\`{#...}\`). These represent structural XML bookmarks.`
|
|
3011
|
+
`- Edit ${i + 1 + index_offset} Failed: Cannot modify or insert internal anchor markers (\`{#...}\`). These represent structural XML bookmarks.`
|
|
2948
3012
|
);
|
|
2949
3013
|
break;
|
|
2950
3014
|
}
|
|
@@ -2958,16 +3022,16 @@ function validate_edit_strings(edits) {
|
|
|
2958
3022
|
const level = stripped.length - stripped.replace(/^#+/, "").length;
|
|
2959
3023
|
if (stripped.substring(level).startsWith(" ") || stripped.substring(level) === "") {
|
|
2960
3024
|
errors.push(
|
|
2961
|
-
`- Edit ${i + 1} Failed: Heading level ${level} is not supported (maximum is 6).`
|
|
3025
|
+
`- Edit ${i + 1 + index_offset} Failed: Heading level ${level} is not supported (maximum is 6).`
|
|
2962
3026
|
);
|
|
2963
3027
|
break;
|
|
2964
3028
|
}
|
|
2965
3029
|
}
|
|
2966
3030
|
}
|
|
2967
3031
|
}
|
|
2968
|
-
if (t_text.includes("READONLY_BOUNDARY_START") || n_text.includes("READONLY_BOUNDARY_START") || t_text.includes("# Document Structure (Read-Only)") || n_text.includes("# Document Structure (Read-Only)")) {
|
|
3032
|
+
if (t_text.includes("READONLY_BOUNDARY_START") || n_text.includes("READONLY_BOUNDARY_START") || t_text.includes("# Document Structure (Read-Only)") || n_text.includes("# Document Structure (Read-Only)") || t_text.includes("Document Structure (Read-Only)") || n_text.includes("Document Structure (Read-Only)")) {
|
|
2969
3033
|
errors.push(
|
|
2970
|
-
`- Edit ${i + 1} Failed: Modification targets the read-only boundary (Structural Appendix). This section cannot be edited.`
|
|
3034
|
+
`- Edit ${i + 1 + index_offset} Failed: Modification targets the read-only boundary (Structural Appendix). This section cannot be edited.`
|
|
2971
3035
|
);
|
|
2972
3036
|
}
|
|
2973
3037
|
}
|
|
@@ -2989,7 +3053,7 @@ var RedlineEngine = class {
|
|
|
2989
3053
|
this.timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d{3}Z$/, "Z");
|
|
2990
3054
|
const w16du_ns = "http://schemas.microsoft.com/office/word/2023/wordml/word16du";
|
|
2991
3055
|
for (const part of this.doc.pkg.parts) {
|
|
2992
|
-
if (part === this.doc.part
|
|
3056
|
+
if (part === this.doc.part) {
|
|
2993
3057
|
if (!part._element.hasAttribute("xmlns:w16du")) {
|
|
2994
3058
|
part._element.setAttribute("xmlns:w16du", w16du_ns);
|
|
2995
3059
|
}
|
|
@@ -3006,15 +3070,59 @@ var RedlineEngine = class {
|
|
|
3006
3070
|
}
|
|
3007
3071
|
return null;
|
|
3008
3072
|
}
|
|
3073
|
+
/**
|
|
3074
|
+
* Best-effort "did you mean" hint for a failed target. The common loop trap
|
|
3075
|
+
* (observed in the field) is an anchored regex like `^\( x \)$` against a
|
|
3076
|
+
* mid-document string: ^/$ bind to the whole full_text, so it never matches
|
|
3077
|
+
* even though the literal `( x )` is present. We strip regex anchoring/escapes
|
|
3078
|
+
* and probe full_text for a literal occurrence; if found, we tell the model
|
|
3079
|
+
* the exact literal that WOULD match so it drops the anchors instead of
|
|
3080
|
+
* escalating the regex further.
|
|
3081
|
+
*/
|
|
3082
|
+
_nearest_match_hint(target_text, is_regex) {
|
|
3083
|
+
if (!target_text) return "";
|
|
3084
|
+
let probe = target_text;
|
|
3085
|
+
if (is_regex) {
|
|
3086
|
+
probe = probe.replace(/^\^/, "").replace(/\$$/, "");
|
|
3087
|
+
probe = probe.replace(/^\\s\*/, "").replace(/\\s\*$/, "");
|
|
3088
|
+
probe = probe.replace(/\\([.^$*+?()[\]{}|\\/])/g, "$1");
|
|
3089
|
+
}
|
|
3090
|
+
probe = probe.trim();
|
|
3091
|
+
if (!probe || probe === target_text) {
|
|
3092
|
+
if (!is_regex) return "";
|
|
3093
|
+
}
|
|
3094
|
+
const idx = this.mapper.full_text.indexOf(probe);
|
|
3095
|
+
if (idx !== -1) {
|
|
3096
|
+
const ctx_start = Math.max(0, idx - 15);
|
|
3097
|
+
const ctx_end = Math.min(
|
|
3098
|
+
this.mapper.full_text.length,
|
|
3099
|
+
idx + probe.length + 15
|
|
3100
|
+
);
|
|
3101
|
+
const ctx = this.mapper.full_text.substring(ctx_start, ctx_end).replace(/\n/g, " ");
|
|
3102
|
+
return `
|
|
3103
|
+
Did you mean the literal "${probe}"? It appears in the document (\u2026${ctx}\u2026). If you used a regex, drop the ^/$ anchors \u2014 they match the start/end of the entire document, not a line.`;
|
|
3104
|
+
}
|
|
3105
|
+
return "";
|
|
3106
|
+
}
|
|
3009
3107
|
_build_edit_context_previews(edit) {
|
|
3010
3108
|
if (edit.type !== "modify") return [null, null];
|
|
3011
3109
|
if (edit._resolved_proxy_edit) {
|
|
3012
3110
|
edit = edit._resolved_proxy_edit;
|
|
3013
3111
|
}
|
|
3014
|
-
|
|
3112
|
+
let start_idx = edit._resolved_start_idx;
|
|
3015
3113
|
if (start_idx === void 0 || start_idx === null) return [null, null];
|
|
3016
|
-
|
|
3017
|
-
|
|
3114
|
+
let target_text = edit.target_text || "";
|
|
3115
|
+
let new_text = edit.new_text || "";
|
|
3116
|
+
const [clean_target, target_style] = this._parse_markdown_style(target_text);
|
|
3117
|
+
if (target_style && target_style.startsWith("Heading")) {
|
|
3118
|
+
const prefix_len = target_text.length - clean_target.length;
|
|
3119
|
+
start_idx += prefix_len;
|
|
3120
|
+
target_text = clean_target;
|
|
3121
|
+
}
|
|
3122
|
+
const [clean_new, new_style] = this._parse_markdown_style(new_text);
|
|
3123
|
+
if (new_style && new_style.startsWith("Heading")) {
|
|
3124
|
+
new_text = clean_new;
|
|
3125
|
+
}
|
|
3018
3126
|
const length = target_text.length;
|
|
3019
3127
|
const active_mapper = edit._active_mapper_ref || this.mapper;
|
|
3020
3128
|
const full_text = active_mapper.full_text;
|
|
@@ -3766,28 +3874,34 @@ var RedlineEngine = class {
|
|
|
3766
3874
|
}
|
|
3767
3875
|
}
|
|
3768
3876
|
}
|
|
3769
|
-
validate_edits(edits) {
|
|
3877
|
+
validate_edits(edits, index_offset = 0) {
|
|
3770
3878
|
const errors = [];
|
|
3771
3879
|
if (!this.mapper.full_text) this.mapper["_build_map"]();
|
|
3772
|
-
errors.push(...validate_edit_strings(edits));
|
|
3880
|
+
errors.push(...validate_edit_strings(edits, index_offset));
|
|
3773
3881
|
for (let i = 0; i < edits.length; i++) {
|
|
3774
3882
|
const edit = edits[i];
|
|
3775
3883
|
if (typeof edit !== "object" || edit === null) {
|
|
3776
3884
|
errors.push(
|
|
3777
|
-
`- Edit ${i + 1} Failed: Invalid change format. Expected a JSON object, but received a primitive ${typeof edit}. Do not pass raw strings.`
|
|
3885
|
+
`- Edit ${i + 1 + index_offset} Failed: Invalid change format. Expected a JSON object, but received a primitive ${typeof edit}. Do not pass raw strings.`
|
|
3778
3886
|
);
|
|
3779
3887
|
continue;
|
|
3780
3888
|
}
|
|
3781
3889
|
if (!edit.target_text) continue;
|
|
3782
3890
|
const is_regex = edit.regex || false;
|
|
3783
3891
|
const match_mode = edit.match_mode || "strict";
|
|
3784
|
-
let matches = this.mapper.find_all_match_indices(
|
|
3892
|
+
let matches = this.mapper.find_all_match_indices(
|
|
3893
|
+
edit.target_text,
|
|
3894
|
+
is_regex
|
|
3895
|
+
);
|
|
3785
3896
|
let activeText = this.mapper.full_text;
|
|
3786
3897
|
let target_mapper = this.mapper;
|
|
3787
3898
|
if (matches.length === 0) {
|
|
3788
3899
|
if (!this.clean_mapper)
|
|
3789
3900
|
this.clean_mapper = new DocumentMapper(this.doc, true);
|
|
3790
|
-
matches = this.clean_mapper.find_all_match_indices(
|
|
3901
|
+
matches = this.clean_mapper.find_all_match_indices(
|
|
3902
|
+
edit.target_text,
|
|
3903
|
+
is_regex
|
|
3904
|
+
);
|
|
3791
3905
|
if (matches.length > 0) {
|
|
3792
3906
|
activeText = this.clean_mapper.full_text;
|
|
3793
3907
|
target_mapper = this.clean_mapper;
|
|
@@ -3809,7 +3923,10 @@ var RedlineEngine = class {
|
|
|
3809
3923
|
if (!this.original_mapper) {
|
|
3810
3924
|
this.original_mapper = new DocumentMapper(this.doc, false, true);
|
|
3811
3925
|
}
|
|
3812
|
-
const orig_matches = this.original_mapper.find_all_match_indices(
|
|
3926
|
+
const orig_matches = this.original_mapper.find_all_match_indices(
|
|
3927
|
+
edit.target_text,
|
|
3928
|
+
is_regex
|
|
3929
|
+
);
|
|
3813
3930
|
if (orig_matches.length > 0) {
|
|
3814
3931
|
is_deleted_text = true;
|
|
3815
3932
|
for (const [start, length] of orig_matches) {
|
|
@@ -3838,27 +3955,32 @@ var RedlineEngine = class {
|
|
|
3838
3955
|
if (is_deleted_text) {
|
|
3839
3956
|
const author_phrase = deleted_authors.size > 0 ? `by ${Array.from(deleted_authors).sort().join(", ")}` : "by an existing revision";
|
|
3840
3957
|
errors.push(
|
|
3841
|
-
`- Edit ${i + 1} Failed: Target text matches text inside a tracked deletion ${author_phrase}. Reject/accept that change first or target the active replacement text instead.`
|
|
3958
|
+
`- Edit ${i + 1 + index_offset} Failed: Target text matches text inside a tracked deletion ${author_phrase}. Reject/accept that change first or target the active replacement text instead.`
|
|
3842
3959
|
);
|
|
3843
3960
|
} else {
|
|
3961
|
+
const hint = this._nearest_match_hint(edit.target_text, is_regex);
|
|
3844
3962
|
errors.push(
|
|
3845
|
-
`- Edit ${i + 1} Failed: Target text not found in document:
|
|
3846
|
-
"${edit.target_text}"`
|
|
3963
|
+
`- Edit ${i + 1 + index_offset} Failed: Target text not found in document:
|
|
3964
|
+
"${edit.target_text}"${hint}`
|
|
3847
3965
|
);
|
|
3848
3966
|
}
|
|
3849
3967
|
} else if (matches.length > 1 && match_mode === "strict") {
|
|
3850
|
-
|
|
3851
|
-
|
|
3852
|
-
|
|
3853
|
-
|
|
3854
|
-
|
|
3855
|
-
|
|
3856
|
-
|
|
3857
|
-
|
|
3858
|
-
|
|
3859
|
-
|
|
3860
|
-
|
|
3861
|
-
|
|
3968
|
+
if (edit.target_text.includes("|")) {
|
|
3969
|
+
matches = matches.slice(0, 1);
|
|
3970
|
+
} else {
|
|
3971
|
+
const positions = matches.map(([start, length]) => [
|
|
3972
|
+
start,
|
|
3973
|
+
start + length
|
|
3974
|
+
]);
|
|
3975
|
+
errors.push(
|
|
3976
|
+
format_ambiguity_error(
|
|
3977
|
+
i + 1 + index_offset,
|
|
3978
|
+
edit.target_text,
|
|
3979
|
+
activeText,
|
|
3980
|
+
positions
|
|
3981
|
+
)
|
|
3982
|
+
);
|
|
3983
|
+
}
|
|
3862
3984
|
}
|
|
3863
3985
|
if (matches.length === 1) {
|
|
3864
3986
|
const [m_start, m_len] = matches[0];
|
|
@@ -3875,14 +3997,14 @@ var RedlineEngine = class {
|
|
|
3875
3997
|
const parts = matched.split("\n\n");
|
|
3876
3998
|
if (parts.length >= 2 && parts[0].trim() !== "" && parts[parts.length - 1].trim() !== "") {
|
|
3877
3999
|
errors.push(
|
|
3878
|
-
`- Edit ${i + 1} Failed: target_text spans a paragraph boundary with body text on both sides. The paragraph break is a structural element, not literal text, so it cannot be replaced as a single span without corrupting the document. Split this into one edit per paragraph.`
|
|
4000
|
+
`- Edit ${i + 1 + index_offset} Failed: target_text spans a paragraph boundary with body text on both sides. The paragraph break is a structural element, not literal text, so it cannot be replaced as a single span without corrupting the document. Split this into one edit per paragraph.`
|
|
3879
4001
|
);
|
|
3880
4002
|
}
|
|
3881
4003
|
} else {
|
|
3882
4004
|
const parts = final_target.split("\n\n");
|
|
3883
4005
|
if (parts.length >= 2 && parts[0].trim() !== "" && parts[parts.length - 1].trim() !== "") {
|
|
3884
4006
|
errors.push(
|
|
3885
|
-
`- Edit ${i + 1} Failed: target_text spans a paragraph boundary with body text on both sides. The paragraph break is a structural element, not literal text, so it cannot be replaced as a single span without corrupting the document. Split this into one edit per paragraph.`
|
|
4007
|
+
`- Edit ${i + 1 + index_offset} Failed: target_text spans a paragraph boundary with body text on both sides. The paragraph break is a structural element, not literal text, so it cannot be replaced as a single span without corrupting the document. Split this into one edit per paragraph.`
|
|
3886
4008
|
);
|
|
3887
4009
|
}
|
|
3888
4010
|
}
|
|
@@ -3901,7 +4023,13 @@ var RedlineEngine = class {
|
|
|
3901
4023
|
).filter((n) => n.getAttribute("w:id") === s.ins_id);
|
|
3902
4024
|
if (insNodes.length > 0) {
|
|
3903
4025
|
const auth = insNodes[0].getAttribute("w:author");
|
|
3904
|
-
if (auth && auth !== this.author)
|
|
4026
|
+
if (auth && auth !== this.author) {
|
|
4027
|
+
const is_fully_contained_in_ins = start >= s.start && start + length <= s.end;
|
|
4028
|
+
const is_lockout = is_fully_contained_in_ins || match_mode === "all";
|
|
4029
|
+
if (is_lockout) {
|
|
4030
|
+
nestedAuthors.add(auth);
|
|
4031
|
+
}
|
|
4032
|
+
}
|
|
3905
4033
|
}
|
|
3906
4034
|
}
|
|
3907
4035
|
if (s.comment_ids) {
|
|
@@ -3915,7 +4043,7 @@ var RedlineEngine = class {
|
|
|
3915
4043
|
}
|
|
3916
4044
|
if (nestedAuthors.size > 0) {
|
|
3917
4045
|
errors.push(
|
|
3918
|
-
`- Edit ${i + 1} Failed: Modification targets an active insertion from another author (${Array.from(nestedAuthors).join(", ")}). Accept that change first or scope your edit outside of it.`
|
|
4046
|
+
`- Edit ${i + 1 + index_offset} Failed: Modification targets an active insertion from another author (${Array.from(nestedAuthors).join(", ")}). Accept that change first or scope your edit outside of it.`
|
|
3919
4047
|
);
|
|
3920
4048
|
}
|
|
3921
4049
|
}
|
|
@@ -3977,22 +4105,13 @@ var RedlineEngine = class {
|
|
|
3977
4105
|
});
|
|
3978
4106
|
}
|
|
3979
4107
|
if (dry_run) {
|
|
3980
|
-
const
|
|
3981
|
-
|
|
3982
|
-
if (part._element) {
|
|
3983
|
-
baselines.set(part, part._element.cloneNode(true));
|
|
3984
|
-
}
|
|
3985
|
-
}
|
|
4108
|
+
const snapshot = takeSnapshot(this.doc);
|
|
4109
|
+
const originalCurrentId = this.current_id;
|
|
3986
4110
|
try {
|
|
3987
4111
|
return this._process_batch_internal(changes, true);
|
|
3988
4112
|
} finally {
|
|
3989
|
-
|
|
3990
|
-
|
|
3991
|
-
if (doc && doc.documentElement) {
|
|
3992
|
-
doc.replaceChild(originalEl, doc.documentElement);
|
|
3993
|
-
}
|
|
3994
|
-
part._element = originalEl;
|
|
3995
|
-
}
|
|
4113
|
+
restoreSnapshot(this.doc, snapshot);
|
|
4114
|
+
this.current_id = originalCurrentId;
|
|
3996
4115
|
this.mapper = new DocumentMapper(this.doc);
|
|
3997
4116
|
this.comments_manager = new CommentsManager(this.doc);
|
|
3998
4117
|
this.clean_mapper = null;
|
|
@@ -4002,6 +4121,13 @@ var RedlineEngine = class {
|
|
|
4002
4121
|
}
|
|
4003
4122
|
}
|
|
4004
4123
|
_process_batch_internal(changes, dry_run_mode = false) {
|
|
4124
|
+
for (const c of changes) {
|
|
4125
|
+
if (c && typeof c === "object" && c.type === "modify" && c.target_text && c.new_text) {
|
|
4126
|
+
const [strippedTarget, strippedNew] = stripMatchingHeadingHashes(c.target_text, c.new_text);
|
|
4127
|
+
c.target_text = strippedTarget;
|
|
4128
|
+
c.new_text = strippedNew;
|
|
4129
|
+
}
|
|
4130
|
+
}
|
|
4005
4131
|
this.skipped_details = [];
|
|
4006
4132
|
const actions = changes.filter(
|
|
4007
4133
|
(c) => c !== null && typeof c === "object" && ["accept", "reject", "reply"].includes(c.type)
|
|
@@ -4009,14 +4135,67 @@ var RedlineEngine = class {
|
|
|
4009
4135
|
const edits = changes.filter(
|
|
4010
4136
|
(c) => c === null || typeof c !== "object" || !["accept", "reject", "reply"].includes(c.type)
|
|
4011
4137
|
);
|
|
4012
|
-
|
|
4013
|
-
|
|
4014
|
-
if (
|
|
4015
|
-
|
|
4138
|
+
let mapper_dirty = false;
|
|
4139
|
+
for (const edit of edits) {
|
|
4140
|
+
if (typeof edit !== "object" || edit === null || !edit.target_text) continue;
|
|
4141
|
+
const is_regex = edit.regex || false;
|
|
4142
|
+
let matches = this.mapper.find_all_match_indices(edit.target_text, is_regex);
|
|
4143
|
+
let target_mapper = this.mapper;
|
|
4144
|
+
if (matches.length === 0) {
|
|
4145
|
+
if (!this.clean_mapper) {
|
|
4146
|
+
this.clean_mapper = new DocumentMapper(this.doc, true);
|
|
4147
|
+
}
|
|
4148
|
+
matches = this.clean_mapper.find_all_match_indices(edit.target_text, is_regex);
|
|
4149
|
+
target_mapper = this.clean_mapper;
|
|
4016
4150
|
}
|
|
4017
|
-
|
|
4018
|
-
|
|
4151
|
+
for (const [start, length] of matches) {
|
|
4152
|
+
const spans = target_mapper.spans.filter(
|
|
4153
|
+
(s) => s.end > start && s.start < start + length
|
|
4154
|
+
);
|
|
4155
|
+
for (const s of spans) {
|
|
4156
|
+
if (s.ins_id) {
|
|
4157
|
+
const insNodes = findAllDescendants(this.doc.element, "w:ins").filter(
|
|
4158
|
+
(n) => n.getAttribute("w:id") === s.ins_id
|
|
4159
|
+
);
|
|
4160
|
+
if (insNodes.length > 0) {
|
|
4161
|
+
const auth = insNodes[0].getAttribute("w:author");
|
|
4162
|
+
if (auth && auth !== this.author) {
|
|
4163
|
+
const is_fully_contained_in_ins = start >= s.start && start + length <= s.end;
|
|
4164
|
+
const match_mode = edit.match_mode || "strict";
|
|
4165
|
+
if (!is_fully_contained_in_ins && match_mode !== "all") {
|
|
4166
|
+
const node = insNodes[0];
|
|
4167
|
+
this._clean_wrapping_comments(node);
|
|
4168
|
+
const parent = node.parentNode;
|
|
4169
|
+
if (parent) {
|
|
4170
|
+
if (parent.tagName === "w:trPr") {
|
|
4171
|
+
parent.removeChild(node);
|
|
4172
|
+
} else {
|
|
4173
|
+
while (node.firstChild) {
|
|
4174
|
+
parent.insertBefore(node.firstChild, node);
|
|
4175
|
+
}
|
|
4176
|
+
parent.removeChild(node);
|
|
4177
|
+
}
|
|
4178
|
+
}
|
|
4179
|
+
mapper_dirty = true;
|
|
4180
|
+
}
|
|
4181
|
+
}
|
|
4182
|
+
}
|
|
4183
|
+
}
|
|
4184
|
+
}
|
|
4019
4185
|
}
|
|
4186
|
+
}
|
|
4187
|
+
if (mapper_dirty) {
|
|
4188
|
+
this.mapper = new DocumentMapper(this.doc);
|
|
4189
|
+
this.clean_mapper = null;
|
|
4190
|
+
}
|
|
4191
|
+
if (!dry_run_mode) {
|
|
4192
|
+
const action_errors = actions.length > 0 ? this.validate_review_actions(actions) : [];
|
|
4193
|
+
const validate_edits_now = edits.length > 0 && action_errors.length > 0;
|
|
4194
|
+
const edit_errors = validate_edits_now ? this.validate_edits(edits) : [];
|
|
4195
|
+
const all_errors = [
|
|
4196
|
+
...action_errors,
|
|
4197
|
+
...edit_errors
|
|
4198
|
+
];
|
|
4020
4199
|
if (all_errors.length > 0) {
|
|
4021
4200
|
throw new BatchValidationError(all_errors);
|
|
4022
4201
|
}
|
|
@@ -4050,8 +4229,9 @@ var RedlineEngine = class {
|
|
|
4050
4229
|
let skipped_edits = 0;
|
|
4051
4230
|
if (edits.length > 0) {
|
|
4052
4231
|
if (dry_run_mode) {
|
|
4053
|
-
for (
|
|
4054
|
-
const
|
|
4232
|
+
for (let i = 0; i < edits.length; i++) {
|
|
4233
|
+
const edit = edits[i];
|
|
4234
|
+
const single_errors = this.validate_edits([edit], i);
|
|
4055
4235
|
const warning = this._check_punctuation_warning(
|
|
4056
4236
|
edit.target_text || ""
|
|
4057
4237
|
);
|
|
@@ -4085,6 +4265,8 @@ var RedlineEngine = class {
|
|
|
4085
4265
|
occurrences_modified: edit._occurrences_modified || 0,
|
|
4086
4266
|
match_mode: edit.match_mode || "strict"
|
|
4087
4267
|
});
|
|
4268
|
+
this.mapper = new DocumentMapper(this.doc);
|
|
4269
|
+
this.clean_mapper = null;
|
|
4088
4270
|
} else {
|
|
4089
4271
|
skipped_edits++;
|
|
4090
4272
|
const error_msg = this.skipped_details.length > 0 ? this.skipped_details[this.skipped_details.length - 1] : "Failed to apply edit";
|
|
@@ -4100,15 +4282,37 @@ var RedlineEngine = class {
|
|
|
4100
4282
|
}
|
|
4101
4283
|
}
|
|
4102
4284
|
} else {
|
|
4103
|
-
const
|
|
4104
|
-
|
|
4105
|
-
|
|
4285
|
+
const snapshot = takeSnapshot(this.doc);
|
|
4286
|
+
const originalCurrentId = this.current_id;
|
|
4287
|
+
try {
|
|
4288
|
+
const sequential_errors = [];
|
|
4289
|
+
for (let i = 0; i < edits.length; i++) {
|
|
4290
|
+
const edit = edits[i];
|
|
4291
|
+
const single_errors = this.validate_edits([edit], i);
|
|
4292
|
+
if (single_errors.length > 0) {
|
|
4293
|
+
sequential_errors.push(...single_errors);
|
|
4294
|
+
} else {
|
|
4295
|
+
this.apply_edits([edit], page_offsets);
|
|
4296
|
+
this.mapper = new DocumentMapper(this.doc);
|
|
4297
|
+
this.clean_mapper = null;
|
|
4298
|
+
}
|
|
4299
|
+
}
|
|
4300
|
+
if (sequential_errors.length > 0) {
|
|
4301
|
+
throw new BatchValidationError(sequential_errors);
|
|
4302
|
+
}
|
|
4303
|
+
} catch (err) {
|
|
4304
|
+
restoreSnapshot(this.doc, snapshot);
|
|
4305
|
+
this.current_id = originalCurrentId;
|
|
4306
|
+
this.mapper = new DocumentMapper(this.doc);
|
|
4307
|
+
this.comments_manager = new CommentsManager(this.doc);
|
|
4308
|
+
this.clean_mapper = null;
|
|
4309
|
+
throw err;
|
|
4106
4310
|
}
|
|
4107
|
-
|
|
4108
|
-
|
|
4109
|
-
|
|
4110
|
-
skipped_edits =
|
|
4111
|
-
for (const edit of
|
|
4311
|
+
applied_edits = edits.filter(
|
|
4312
|
+
(e) => e._applied_status
|
|
4313
|
+
).length;
|
|
4314
|
+
skipped_edits = edits.length - applied_edits;
|
|
4315
|
+
for (const edit of edits) {
|
|
4112
4316
|
const success = edit._applied_status || false;
|
|
4113
4317
|
const error_msg = edit._error_msg || null;
|
|
4114
4318
|
const warning = this._check_punctuation_warning(
|
|
@@ -4262,14 +4466,22 @@ var RedlineEngine = class {
|
|
|
4262
4466
|
if (parent) {
|
|
4263
4467
|
parent._applied_status = true;
|
|
4264
4468
|
parent._occurrences_modified = (parent._occurrences_modified || 0) + 1;
|
|
4265
|
-
const [path, page] = this._get_heading_path_and_page(
|
|
4469
|
+
const [path, page] = this._get_heading_path_and_page(
|
|
4470
|
+
start,
|
|
4471
|
+
this.mapper.full_text,
|
|
4472
|
+
page_offsets
|
|
4473
|
+
);
|
|
4266
4474
|
const pages = parent._pages || [];
|
|
4267
4475
|
if (!pages.includes(page)) pages.unshift(page);
|
|
4268
4476
|
parent._pages = pages;
|
|
4269
4477
|
parent._heading_path = path;
|
|
4270
4478
|
} else {
|
|
4271
4479
|
edit._occurrences_modified = (edit._occurrences_modified || 0) + 1;
|
|
4272
|
-
const [path, page] = this._get_heading_path_and_page(
|
|
4480
|
+
const [path, page] = this._get_heading_path_and_page(
|
|
4481
|
+
start,
|
|
4482
|
+
this.mapper.full_text,
|
|
4483
|
+
page_offsets
|
|
4484
|
+
);
|
|
4273
4485
|
const pages = edit._pages || [];
|
|
4274
4486
|
if (!pages.includes(page)) pages.unshift(page);
|
|
4275
4487
|
edit._pages = pages;
|
|
@@ -4430,12 +4642,18 @@ var RedlineEngine = class {
|
|
|
4430
4642
|
if (!edit.target_text) return null;
|
|
4431
4643
|
const is_regex = edit.regex || false;
|
|
4432
4644
|
const match_mode = edit.match_mode || "strict";
|
|
4433
|
-
let matches = this.mapper.find_all_match_indices(
|
|
4645
|
+
let matches = this.mapper.find_all_match_indices(
|
|
4646
|
+
edit.target_text,
|
|
4647
|
+
is_regex
|
|
4648
|
+
);
|
|
4434
4649
|
let use_clean_map = false;
|
|
4435
4650
|
if (matches.length === 0) {
|
|
4436
4651
|
if (!this.clean_mapper)
|
|
4437
4652
|
this.clean_mapper = new DocumentMapper(this.doc, true);
|
|
4438
|
-
matches = this.clean_mapper.find_all_match_indices(
|
|
4653
|
+
matches = this.clean_mapper.find_all_match_indices(
|
|
4654
|
+
edit.target_text,
|
|
4655
|
+
is_regex
|
|
4656
|
+
);
|
|
4439
4657
|
if (matches.length > 0) use_clean_map = true;
|
|
4440
4658
|
else return null;
|
|
4441
4659
|
}
|
|
@@ -4460,14 +4678,49 @@ var RedlineEngine = class {
|
|
|
4460
4678
|
start_idx + match_len
|
|
4461
4679
|
);
|
|
4462
4680
|
let current_effective_new_text = edit.new_text || "";
|
|
4681
|
+
if (/^\{#cell:[^}]+\}$/.test(actual_doc_text.trim())) {
|
|
4682
|
+
let ins_text = current_effective_new_text;
|
|
4683
|
+
ins_text = ins_text.split(actual_doc_text.trim()).join("");
|
|
4684
|
+
if (ins_text) {
|
|
4685
|
+
all_sub_edits.push({
|
|
4686
|
+
type: "modify",
|
|
4687
|
+
target_text: "",
|
|
4688
|
+
new_text: ins_text,
|
|
4689
|
+
comment: edit.comment,
|
|
4690
|
+
// Insert at the anchor token's start so the new run lands inside
|
|
4691
|
+
// the cell paragraph that get_insertion_anchor resolves there.
|
|
4692
|
+
_match_start_index: start_idx,
|
|
4693
|
+
_internal_op: "INSERTION",
|
|
4694
|
+
_active_mapper_ref: active_mapper
|
|
4695
|
+
});
|
|
4696
|
+
} else if (edit.comment) {
|
|
4697
|
+
all_sub_edits.push({
|
|
4698
|
+
type: "modify",
|
|
4699
|
+
target_text: "",
|
|
4700
|
+
new_text: "",
|
|
4701
|
+
comment: edit.comment,
|
|
4702
|
+
_match_start_index: start_idx,
|
|
4703
|
+
_internal_op: "COMMENT_ONLY",
|
|
4704
|
+
_active_mapper_ref: active_mapper
|
|
4705
|
+
});
|
|
4706
|
+
}
|
|
4707
|
+
continue;
|
|
4708
|
+
}
|
|
4463
4709
|
if (is_regex && current_effective_new_text) {
|
|
4464
4710
|
try {
|
|
4465
|
-
current_effective_new_text = actual_doc_text.replace(
|
|
4711
|
+
current_effective_new_text = actual_doc_text.replace(
|
|
4712
|
+
new RegExp(edit.target_text),
|
|
4713
|
+
current_effective_new_text
|
|
4714
|
+
);
|
|
4466
4715
|
} catch (e) {
|
|
4467
4716
|
}
|
|
4468
4717
|
}
|
|
4469
|
-
const [edit_target_clean, edit_target_style] = this._parse_markdown_style(
|
|
4470
|
-
|
|
4718
|
+
const [edit_target_clean, edit_target_style] = this._parse_markdown_style(
|
|
4719
|
+
edit.target_text
|
|
4720
|
+
);
|
|
4721
|
+
const [edit_new_clean, edit_new_style] = this._parse_markdown_style(
|
|
4722
|
+
current_effective_new_text
|
|
4723
|
+
);
|
|
4471
4724
|
if (edit_target_style !== edit_new_style) {
|
|
4472
4725
|
const [actual_clean] = this._parse_markdown_style(actual_doc_text);
|
|
4473
4726
|
const final_target2 = actual_clean;
|
|
@@ -4506,7 +4759,9 @@ var RedlineEngine = class {
|
|
|
4506
4759
|
let effective_start_idx = start_idx;
|
|
4507
4760
|
if (current_effective_new_text.startsWith(actual_doc_text)) {
|
|
4508
4761
|
effective_op = "INSERTION";
|
|
4509
|
-
final_new = current_effective_new_text.substring(
|
|
4762
|
+
final_new = current_effective_new_text.substring(
|
|
4763
|
+
actual_doc_text.length
|
|
4764
|
+
);
|
|
4510
4765
|
effective_start_idx = start_idx + match_len;
|
|
4511
4766
|
} else {
|
|
4512
4767
|
const [prefix_len, suffix_len] = trim_common_context(
|
|
@@ -4587,7 +4842,12 @@ var RedlineEngine = class {
|
|
|
4587
4842
|
const first_anchor = ascend_to_paragraph_child(first_el, start_p);
|
|
4588
4843
|
const last_anchor = ascend_to_paragraph_child(last_el, end_p);
|
|
4589
4844
|
if (start_p === end_p) {
|
|
4590
|
-
this._attach_comment(
|
|
4845
|
+
this._attach_comment(
|
|
4846
|
+
start_p,
|
|
4847
|
+
first_anchor,
|
|
4848
|
+
last_anchor,
|
|
4849
|
+
edit.comment
|
|
4850
|
+
);
|
|
4591
4851
|
} else {
|
|
4592
4852
|
this._attach_comment_spanning(
|
|
4593
4853
|
start_p,
|
|
@@ -5876,13 +6136,21 @@ function extract_table(table, comments_map, cleanView, cursor, paragraph_offsets
|
|
|
5876
6136
|
if (seen_cells.has(cell)) continue;
|
|
5877
6137
|
seen_cells.add(cell);
|
|
5878
6138
|
if (!first_cell) cell_cursor += 3;
|
|
5879
|
-
|
|
6139
|
+
let cell_content = _extract_blocks(
|
|
5880
6140
|
cell,
|
|
5881
6141
|
comments_map,
|
|
5882
6142
|
cleanView,
|
|
5883
6143
|
cell_cursor,
|
|
5884
6144
|
paragraph_offsets
|
|
5885
6145
|
);
|
|
6146
|
+
if (!cleanView) {
|
|
6147
|
+
const firstP = cell._element.getElementsByTagName("w:p")[0];
|
|
6148
|
+
const paraId = firstP ? firstP.getAttribute("w14:paraId") : null;
|
|
6149
|
+
if (paraId) {
|
|
6150
|
+
const anchor = `{#cell:${paraId}}`;
|
|
6151
|
+
cell_content = cell_content + anchor;
|
|
6152
|
+
}
|
|
6153
|
+
}
|
|
5886
6154
|
cell_texts.push(cell_content);
|
|
5887
6155
|
cell_cursor += cell_content.length;
|
|
5888
6156
|
first_cell = false;
|