@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.js
CHANGED
|
@@ -1333,7 +1333,19 @@ ${header}`;
|
|
|
1333
1333
|
this._add_virtual_text(" | ", current, null);
|
|
1334
1334
|
current += 3;
|
|
1335
1335
|
}
|
|
1336
|
+
const cell_start = current;
|
|
1336
1337
|
current = this._map_blocks(cell, current);
|
|
1338
|
+
if (!this.clean_view && !this.original_view) {
|
|
1339
|
+
const firstP = cell._element.getElementsByTagName("w:p")[0];
|
|
1340
|
+
const paraId = firstP ? firstP.getAttribute("w14:paraId") : null;
|
|
1341
|
+
if (paraId && firstP) {
|
|
1342
|
+
const cellPara = new Paragraph(firstP, cell);
|
|
1343
|
+
this._add_virtual_text("", current, cellPara);
|
|
1344
|
+
const anchor = `{#cell:${paraId}}`;
|
|
1345
|
+
this._add_virtual_text(anchor, current, cellPara);
|
|
1346
|
+
current += anchor.length;
|
|
1347
|
+
}
|
|
1348
|
+
}
|
|
1337
1349
|
cells_processed += 1;
|
|
1338
1350
|
}
|
|
1339
1351
|
if (ins && !this.clean_view && !this.original_view) {
|
|
@@ -1710,7 +1722,7 @@ ${header}`;
|
|
|
1710
1722
|
target_text = this._strip_markdown_formatting(target_text);
|
|
1711
1723
|
target_text = this._replace_smart_quotes(target_text);
|
|
1712
1724
|
const parts = [];
|
|
1713
|
-
const token_pattern = /(\[_+\])|(\s+)|(['"])|([
|
|
1725
|
+
const token_pattern = /(\[_+\])|(\s+)|(['"])|([.,;:\/\-\[\](){}+=$?*!|#^<>\\%&@~`_])/g;
|
|
1714
1726
|
let last_idx = 0;
|
|
1715
1727
|
let match;
|
|
1716
1728
|
const escapeRegExp = (str) => str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
@@ -2596,7 +2608,7 @@ function _refine_match_boundaries(text, start, end) {
|
|
|
2596
2608
|
function _make_fuzzy_regex(target_text) {
|
|
2597
2609
|
target_text = _replace_smart_quotes(target_text);
|
|
2598
2610
|
const parts = [];
|
|
2599
|
-
const token_pattern = /(_+)|(\s+)|(['"])|([
|
|
2611
|
+
const token_pattern = /(_+)|(\s+)|(['"])|([.,;:\/\-\[\](){}+=$?*!|#^<>\\%&@~`])/g;
|
|
2600
2612
|
const md_noise = "[*_]*";
|
|
2601
2613
|
const structural_noise = "(?:\\s*(?:[*+\\->]|\\d+\\.)\\s+|\\s*\\n\\s*)";
|
|
2602
2614
|
const start_list_marker = "(?:[ \\t]*(?:[*+\\->]|\\d+\\.)\\s+)?";
|
|
@@ -2783,8 +2795,15 @@ function format_ambiguity_error(edit_index, target_text, haystack, match_positio
|
|
|
2783
2795
|
if (remaining > 0) {
|
|
2784
2796
|
lines.push(` ... and ${remaining} more occurrence(s) not shown.`);
|
|
2785
2797
|
}
|
|
2798
|
+
lines.push(" To resolve, re-send this edit using ONE of these strategies:");
|
|
2799
|
+
lines.push(
|
|
2800
|
+
` 1. Set "match_mode": "all" to modify ALL ${total} occurrences (same target_text).`
|
|
2801
|
+
);
|
|
2802
|
+
lines.push(
|
|
2803
|
+
' 2. Set "match_mode": "first" to modify only the FIRST occurrence (same target_text).'
|
|
2804
|
+
);
|
|
2786
2805
|
lines.push(
|
|
2787
|
-
|
|
2806
|
+
' 3. Please provide more surrounding context in your target_text to uniquely identify a single location (keep the default "match_mode": "strict").'
|
|
2788
2807
|
);
|
|
2789
2808
|
return lines.join("\n");
|
|
2790
2809
|
}
|
|
@@ -2811,6 +2830,51 @@ function insertAfter(newNode, refNode) {
|
|
|
2811
2830
|
refNode.parentNode.insertBefore(newNode, refNode.nextSibling);
|
|
2812
2831
|
}
|
|
2813
2832
|
}
|
|
2833
|
+
function takeSnapshot(doc) {
|
|
2834
|
+
const parts = [...doc.pkg.parts];
|
|
2835
|
+
const unzipped = { ...doc.pkg.unzipped };
|
|
2836
|
+
const rels = /* @__PURE__ */ new Map();
|
|
2837
|
+
const elements = /* @__PURE__ */ new Map();
|
|
2838
|
+
for (const part of parts) {
|
|
2839
|
+
rels.set(part, new Map(part.rels));
|
|
2840
|
+
if (part._element) {
|
|
2841
|
+
elements.set(part, part._element.cloneNode(true));
|
|
2842
|
+
}
|
|
2843
|
+
}
|
|
2844
|
+
return { parts, unzipped, rels, elements };
|
|
2845
|
+
}
|
|
2846
|
+
function restoreSnapshot(doc, snapshot) {
|
|
2847
|
+
doc.pkg.parts = [...snapshot.parts];
|
|
2848
|
+
for (const key of Object.keys(doc.pkg.unzipped)) {
|
|
2849
|
+
delete doc.pkg.unzipped[key];
|
|
2850
|
+
}
|
|
2851
|
+
for (const [key, val] of Object.entries(snapshot.unzipped)) {
|
|
2852
|
+
doc.pkg.unzipped[key] = val;
|
|
2853
|
+
}
|
|
2854
|
+
for (const part of snapshot.parts) {
|
|
2855
|
+
part.rels = new Map(snapshot.rels.get(part));
|
|
2856
|
+
const originalEl = snapshot.elements.get(part);
|
|
2857
|
+
if (originalEl && part._element) {
|
|
2858
|
+
const xmlDoc = part._element.ownerDocument;
|
|
2859
|
+
if (xmlDoc && xmlDoc.documentElement) {
|
|
2860
|
+
xmlDoc.replaceChild(originalEl, xmlDoc.documentElement);
|
|
2861
|
+
}
|
|
2862
|
+
part._element = originalEl;
|
|
2863
|
+
}
|
|
2864
|
+
}
|
|
2865
|
+
}
|
|
2866
|
+
function stripMatchingHeadingHashes(target, newText) {
|
|
2867
|
+
if (!target || !newText) return [target, newText];
|
|
2868
|
+
const targetMatch = target.match(/^(#+)\s+/);
|
|
2869
|
+
const newMatch = newText.match(/^(#+)\s+/);
|
|
2870
|
+
if (targetMatch && newMatch && targetMatch[1] === newMatch[1]) {
|
|
2871
|
+
const hashes = targetMatch[1];
|
|
2872
|
+
const targetClean = target.substring(hashes.length).trimStart();
|
|
2873
|
+
const newClean = newText.substring(hashes.length).trimStart();
|
|
2874
|
+
return [targetClean, newClean];
|
|
2875
|
+
}
|
|
2876
|
+
return [target, newText];
|
|
2877
|
+
}
|
|
2814
2878
|
var BatchValidationError = class extends Error {
|
|
2815
2879
|
errors;
|
|
2816
2880
|
constructor(errors) {
|
|
@@ -2819,7 +2883,7 @@ var BatchValidationError = class extends Error {
|
|
|
2819
2883
|
this.errors = errors;
|
|
2820
2884
|
}
|
|
2821
2885
|
};
|
|
2822
|
-
function validate_edit_strings(edits) {
|
|
2886
|
+
function validate_edit_strings(edits, index_offset = 0) {
|
|
2823
2887
|
const errors = [];
|
|
2824
2888
|
for (let i = 0; i < edits.length; i++) {
|
|
2825
2889
|
const edit = edits[i];
|
|
@@ -2827,7 +2891,7 @@ function validate_edit_strings(edits) {
|
|
|
2827
2891
|
const n_text = edit.new_text || "";
|
|
2828
2892
|
if (n_text.includes("{++") || n_text.includes("{--") || n_text.includes("{>>") || n_text.includes("{==")) {
|
|
2829
2893
|
errors.push(
|
|
2830
|
-
`- 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.`
|
|
2894
|
+
`- 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.`
|
|
2831
2895
|
);
|
|
2832
2896
|
}
|
|
2833
2897
|
if (t_text.includes("[^") || n_text.includes("[^")) {
|
|
@@ -2838,11 +2902,11 @@ function validate_edit_strings(edits) {
|
|
|
2838
2902
|
(f) => n_fns.filter((x) => x === f).length > t_fns.filter((x) => x === f).length
|
|
2839
2903
|
)) {
|
|
2840
2904
|
errors.push(
|
|
2841
|
-
`- 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.`
|
|
2905
|
+
`- 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.`
|
|
2842
2906
|
);
|
|
2843
2907
|
} else {
|
|
2844
2908
|
errors.push(
|
|
2845
|
-
`- Edit ${i + 1} Failed: Cannot delete footnote/endnote references via text replace. The marker corresponds to a structural XML element.`
|
|
2909
|
+
`- Edit ${i + 1 + index_offset} Failed: Cannot delete footnote/endnote references via text replace. The marker corresponds to a structural XML element.`
|
|
2846
2910
|
);
|
|
2847
2911
|
}
|
|
2848
2912
|
}
|
|
@@ -2853,16 +2917,16 @@ function validate_edit_strings(edits) {
|
|
|
2853
2917
|
if (t_links.length !== n_links.length) {
|
|
2854
2918
|
if (n_links.length > t_links.length) {
|
|
2855
2919
|
errors.push(
|
|
2856
|
-
`- Edit ${i + 1} Failed: Cannot insert hyperlinks via text replace. Use a dedicated structural operation.`
|
|
2920
|
+
`- Edit ${i + 1 + index_offset} Failed: Cannot insert hyperlinks via text replace. Use a dedicated structural operation.`
|
|
2857
2921
|
);
|
|
2858
2922
|
} else {
|
|
2859
2923
|
errors.push(
|
|
2860
|
-
`- Edit ${i + 1} Failed: Cannot delete hyperlinks via text replace. The marker corresponds to a structural XML element.`
|
|
2924
|
+
`- Edit ${i + 1 + index_offset} Failed: Cannot delete hyperlinks via text replace. The marker corresponds to a structural XML element.`
|
|
2861
2925
|
);
|
|
2862
2926
|
}
|
|
2863
2927
|
} else if (t_links.length > 1 && JSON.stringify(t_links) !== JSON.stringify(n_links)) {
|
|
2864
2928
|
errors.push(
|
|
2865
|
-
`- Edit ${i + 1} Failed: Can only edit or retarget one hyperlink per text replacement. Please split into multiple edits.`
|
|
2929
|
+
`- Edit ${i + 1 + index_offset} Failed: Can only edit or retarget one hyperlink per text replacement. Please split into multiple edits.`
|
|
2866
2930
|
);
|
|
2867
2931
|
}
|
|
2868
2932
|
}
|
|
@@ -2872,17 +2936,17 @@ function validate_edit_strings(edits) {
|
|
|
2872
2936
|
if (t_xrefs.length !== n_xrefs.length) {
|
|
2873
2937
|
if (n_xrefs.length > t_xrefs.length) {
|
|
2874
2938
|
errors.push(
|
|
2875
|
-
`- Edit ${i + 1} Failed: Cannot insert cross-references via text replace. Markers are read-only projections.`
|
|
2939
|
+
`- Edit ${i + 1 + index_offset} Failed: Cannot insert cross-references via text replace. Markers are read-only projections.`
|
|
2876
2940
|
);
|
|
2877
2941
|
} else {
|
|
2878
2942
|
errors.push(
|
|
2879
|
-
`- Edit ${i + 1} Failed: Cannot delete cross-references via text replace. The marker corresponds to a structural XML element.`
|
|
2943
|
+
`- Edit ${i + 1 + index_offset} Failed: Cannot delete cross-references via text replace. The marker corresponds to a structural XML element.`
|
|
2880
2944
|
);
|
|
2881
2945
|
}
|
|
2882
2946
|
} else {
|
|
2883
2947
|
if (JSON.stringify(t_xrefs) !== JSON.stringify(n_xrefs)) {
|
|
2884
2948
|
errors.push(
|
|
2885
|
-
`- Edit ${i + 1} Failed: Modifying or retargeting cross-reference markers is disallowed to prevent dependency corruption.`
|
|
2949
|
+
`- Edit ${i + 1 + index_offset} Failed: Modifying or retargeting cross-reference markers is disallowed to prevent dependency corruption.`
|
|
2886
2950
|
);
|
|
2887
2951
|
}
|
|
2888
2952
|
}
|
|
@@ -2893,7 +2957,7 @@ function validate_edit_strings(edits) {
|
|
|
2893
2957
|
for (const a of n_anchors) {
|
|
2894
2958
|
if (n_anchors.filter((x) => x === a).length > t_anchors.filter((x) => x === a).length) {
|
|
2895
2959
|
errors.push(
|
|
2896
|
-
`- Edit ${i + 1} Failed: Cannot modify or insert internal anchor markers (\`{#...}\`). These represent structural XML bookmarks.`
|
|
2960
|
+
`- Edit ${i + 1 + index_offset} Failed: Cannot modify or insert internal anchor markers (\`{#...}\`). These represent structural XML bookmarks.`
|
|
2897
2961
|
);
|
|
2898
2962
|
break;
|
|
2899
2963
|
}
|
|
@@ -2907,16 +2971,16 @@ function validate_edit_strings(edits) {
|
|
|
2907
2971
|
const level = stripped.length - stripped.replace(/^#+/, "").length;
|
|
2908
2972
|
if (stripped.substring(level).startsWith(" ") || stripped.substring(level) === "") {
|
|
2909
2973
|
errors.push(
|
|
2910
|
-
`- Edit ${i + 1} Failed: Heading level ${level} is not supported (maximum is 6).`
|
|
2974
|
+
`- Edit ${i + 1 + index_offset} Failed: Heading level ${level} is not supported (maximum is 6).`
|
|
2911
2975
|
);
|
|
2912
2976
|
break;
|
|
2913
2977
|
}
|
|
2914
2978
|
}
|
|
2915
2979
|
}
|
|
2916
2980
|
}
|
|
2917
|
-
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)")) {
|
|
2981
|
+
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)")) {
|
|
2918
2982
|
errors.push(
|
|
2919
|
-
`- Edit ${i + 1} Failed: Modification targets the read-only boundary (Structural Appendix). This section cannot be edited.`
|
|
2983
|
+
`- Edit ${i + 1 + index_offset} Failed: Modification targets the read-only boundary (Structural Appendix). This section cannot be edited.`
|
|
2920
2984
|
);
|
|
2921
2985
|
}
|
|
2922
2986
|
}
|
|
@@ -2938,7 +3002,7 @@ var RedlineEngine = class {
|
|
|
2938
3002
|
this.timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d{3}Z$/, "Z");
|
|
2939
3003
|
const w16du_ns = "http://schemas.microsoft.com/office/word/2023/wordml/word16du";
|
|
2940
3004
|
for (const part of this.doc.pkg.parts) {
|
|
2941
|
-
if (part === this.doc.part
|
|
3005
|
+
if (part === this.doc.part) {
|
|
2942
3006
|
if (!part._element.hasAttribute("xmlns:w16du")) {
|
|
2943
3007
|
part._element.setAttribute("xmlns:w16du", w16du_ns);
|
|
2944
3008
|
}
|
|
@@ -2955,15 +3019,59 @@ var RedlineEngine = class {
|
|
|
2955
3019
|
}
|
|
2956
3020
|
return null;
|
|
2957
3021
|
}
|
|
3022
|
+
/**
|
|
3023
|
+
* Best-effort "did you mean" hint for a failed target. The common loop trap
|
|
3024
|
+
* (observed in the field) is an anchored regex like `^\( x \)$` against a
|
|
3025
|
+
* mid-document string: ^/$ bind to the whole full_text, so it never matches
|
|
3026
|
+
* even though the literal `( x )` is present. We strip regex anchoring/escapes
|
|
3027
|
+
* and probe full_text for a literal occurrence; if found, we tell the model
|
|
3028
|
+
* the exact literal that WOULD match so it drops the anchors instead of
|
|
3029
|
+
* escalating the regex further.
|
|
3030
|
+
*/
|
|
3031
|
+
_nearest_match_hint(target_text, is_regex) {
|
|
3032
|
+
if (!target_text) return "";
|
|
3033
|
+
let probe = target_text;
|
|
3034
|
+
if (is_regex) {
|
|
3035
|
+
probe = probe.replace(/^\^/, "").replace(/\$$/, "");
|
|
3036
|
+
probe = probe.replace(/^\\s\*/, "").replace(/\\s\*$/, "");
|
|
3037
|
+
probe = probe.replace(/\\([.^$*+?()[\]{}|\\/])/g, "$1");
|
|
3038
|
+
}
|
|
3039
|
+
probe = probe.trim();
|
|
3040
|
+
if (!probe || probe === target_text) {
|
|
3041
|
+
if (!is_regex) return "";
|
|
3042
|
+
}
|
|
3043
|
+
const idx = this.mapper.full_text.indexOf(probe);
|
|
3044
|
+
if (idx !== -1) {
|
|
3045
|
+
const ctx_start = Math.max(0, idx - 15);
|
|
3046
|
+
const ctx_end = Math.min(
|
|
3047
|
+
this.mapper.full_text.length,
|
|
3048
|
+
idx + probe.length + 15
|
|
3049
|
+
);
|
|
3050
|
+
const ctx = this.mapper.full_text.substring(ctx_start, ctx_end).replace(/\n/g, " ");
|
|
3051
|
+
return `
|
|
3052
|
+
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.`;
|
|
3053
|
+
}
|
|
3054
|
+
return "";
|
|
3055
|
+
}
|
|
2958
3056
|
_build_edit_context_previews(edit) {
|
|
2959
3057
|
if (edit.type !== "modify") return [null, null];
|
|
2960
3058
|
if (edit._resolved_proxy_edit) {
|
|
2961
3059
|
edit = edit._resolved_proxy_edit;
|
|
2962
3060
|
}
|
|
2963
|
-
|
|
3061
|
+
let start_idx = edit._resolved_start_idx;
|
|
2964
3062
|
if (start_idx === void 0 || start_idx === null) return [null, null];
|
|
2965
|
-
|
|
2966
|
-
|
|
3063
|
+
let target_text = edit.target_text || "";
|
|
3064
|
+
let new_text = edit.new_text || "";
|
|
3065
|
+
const [clean_target, target_style] = this._parse_markdown_style(target_text);
|
|
3066
|
+
if (target_style && target_style.startsWith("Heading")) {
|
|
3067
|
+
const prefix_len = target_text.length - clean_target.length;
|
|
3068
|
+
start_idx += prefix_len;
|
|
3069
|
+
target_text = clean_target;
|
|
3070
|
+
}
|
|
3071
|
+
const [clean_new, new_style] = this._parse_markdown_style(new_text);
|
|
3072
|
+
if (new_style && new_style.startsWith("Heading")) {
|
|
3073
|
+
new_text = clean_new;
|
|
3074
|
+
}
|
|
2967
3075
|
const length = target_text.length;
|
|
2968
3076
|
const active_mapper = edit._active_mapper_ref || this.mapper;
|
|
2969
3077
|
const full_text = active_mapper.full_text;
|
|
@@ -3715,28 +3823,34 @@ var RedlineEngine = class {
|
|
|
3715
3823
|
}
|
|
3716
3824
|
}
|
|
3717
3825
|
}
|
|
3718
|
-
validate_edits(edits) {
|
|
3826
|
+
validate_edits(edits, index_offset = 0) {
|
|
3719
3827
|
const errors = [];
|
|
3720
3828
|
if (!this.mapper.full_text) this.mapper["_build_map"]();
|
|
3721
|
-
errors.push(...validate_edit_strings(edits));
|
|
3829
|
+
errors.push(...validate_edit_strings(edits, index_offset));
|
|
3722
3830
|
for (let i = 0; i < edits.length; i++) {
|
|
3723
3831
|
const edit = edits[i];
|
|
3724
3832
|
if (typeof edit !== "object" || edit === null) {
|
|
3725
3833
|
errors.push(
|
|
3726
|
-
`- Edit ${i + 1} Failed: Invalid change format. Expected a JSON object, but received a primitive ${typeof edit}. Do not pass raw strings.`
|
|
3834
|
+
`- Edit ${i + 1 + index_offset} Failed: Invalid change format. Expected a JSON object, but received a primitive ${typeof edit}. Do not pass raw strings.`
|
|
3727
3835
|
);
|
|
3728
3836
|
continue;
|
|
3729
3837
|
}
|
|
3730
3838
|
if (!edit.target_text) continue;
|
|
3731
3839
|
const is_regex = edit.regex || false;
|
|
3732
3840
|
const match_mode = edit.match_mode || "strict";
|
|
3733
|
-
let matches = this.mapper.find_all_match_indices(
|
|
3841
|
+
let matches = this.mapper.find_all_match_indices(
|
|
3842
|
+
edit.target_text,
|
|
3843
|
+
is_regex
|
|
3844
|
+
);
|
|
3734
3845
|
let activeText = this.mapper.full_text;
|
|
3735
3846
|
let target_mapper = this.mapper;
|
|
3736
3847
|
if (matches.length === 0) {
|
|
3737
3848
|
if (!this.clean_mapper)
|
|
3738
3849
|
this.clean_mapper = new DocumentMapper(this.doc, true);
|
|
3739
|
-
matches = this.clean_mapper.find_all_match_indices(
|
|
3850
|
+
matches = this.clean_mapper.find_all_match_indices(
|
|
3851
|
+
edit.target_text,
|
|
3852
|
+
is_regex
|
|
3853
|
+
);
|
|
3740
3854
|
if (matches.length > 0) {
|
|
3741
3855
|
activeText = this.clean_mapper.full_text;
|
|
3742
3856
|
target_mapper = this.clean_mapper;
|
|
@@ -3758,7 +3872,10 @@ var RedlineEngine = class {
|
|
|
3758
3872
|
if (!this.original_mapper) {
|
|
3759
3873
|
this.original_mapper = new DocumentMapper(this.doc, false, true);
|
|
3760
3874
|
}
|
|
3761
|
-
const orig_matches = this.original_mapper.find_all_match_indices(
|
|
3875
|
+
const orig_matches = this.original_mapper.find_all_match_indices(
|
|
3876
|
+
edit.target_text,
|
|
3877
|
+
is_regex
|
|
3878
|
+
);
|
|
3762
3879
|
if (orig_matches.length > 0) {
|
|
3763
3880
|
is_deleted_text = true;
|
|
3764
3881
|
for (const [start, length] of orig_matches) {
|
|
@@ -3787,27 +3904,32 @@ var RedlineEngine = class {
|
|
|
3787
3904
|
if (is_deleted_text) {
|
|
3788
3905
|
const author_phrase = deleted_authors.size > 0 ? `by ${Array.from(deleted_authors).sort().join(", ")}` : "by an existing revision";
|
|
3789
3906
|
errors.push(
|
|
3790
|
-
`- 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.`
|
|
3907
|
+
`- 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.`
|
|
3791
3908
|
);
|
|
3792
3909
|
} else {
|
|
3910
|
+
const hint = this._nearest_match_hint(edit.target_text, is_regex);
|
|
3793
3911
|
errors.push(
|
|
3794
|
-
`- Edit ${i + 1} Failed: Target text not found in document:
|
|
3795
|
-
"${edit.target_text}"`
|
|
3912
|
+
`- Edit ${i + 1 + index_offset} Failed: Target text not found in document:
|
|
3913
|
+
"${edit.target_text}"${hint}`
|
|
3796
3914
|
);
|
|
3797
3915
|
}
|
|
3798
3916
|
} else if (matches.length > 1 && match_mode === "strict") {
|
|
3799
|
-
|
|
3800
|
-
|
|
3801
|
-
|
|
3802
|
-
|
|
3803
|
-
|
|
3804
|
-
|
|
3805
|
-
|
|
3806
|
-
|
|
3807
|
-
|
|
3808
|
-
|
|
3809
|
-
|
|
3810
|
-
|
|
3917
|
+
if (edit.target_text.includes("|")) {
|
|
3918
|
+
matches = matches.slice(0, 1);
|
|
3919
|
+
} else {
|
|
3920
|
+
const positions = matches.map(([start, length]) => [
|
|
3921
|
+
start,
|
|
3922
|
+
start + length
|
|
3923
|
+
]);
|
|
3924
|
+
errors.push(
|
|
3925
|
+
format_ambiguity_error(
|
|
3926
|
+
i + 1 + index_offset,
|
|
3927
|
+
edit.target_text,
|
|
3928
|
+
activeText,
|
|
3929
|
+
positions
|
|
3930
|
+
)
|
|
3931
|
+
);
|
|
3932
|
+
}
|
|
3811
3933
|
}
|
|
3812
3934
|
if (matches.length === 1) {
|
|
3813
3935
|
const [m_start, m_len] = matches[0];
|
|
@@ -3824,14 +3946,14 @@ var RedlineEngine = class {
|
|
|
3824
3946
|
const parts = matched.split("\n\n");
|
|
3825
3947
|
if (parts.length >= 2 && parts[0].trim() !== "" && parts[parts.length - 1].trim() !== "") {
|
|
3826
3948
|
errors.push(
|
|
3827
|
-
`- 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.`
|
|
3949
|
+
`- 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.`
|
|
3828
3950
|
);
|
|
3829
3951
|
}
|
|
3830
3952
|
} else {
|
|
3831
3953
|
const parts = final_target.split("\n\n");
|
|
3832
3954
|
if (parts.length >= 2 && parts[0].trim() !== "" && parts[parts.length - 1].trim() !== "") {
|
|
3833
3955
|
errors.push(
|
|
3834
|
-
`- 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.`
|
|
3956
|
+
`- 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.`
|
|
3835
3957
|
);
|
|
3836
3958
|
}
|
|
3837
3959
|
}
|
|
@@ -3850,7 +3972,13 @@ var RedlineEngine = class {
|
|
|
3850
3972
|
).filter((n) => n.getAttribute("w:id") === s.ins_id);
|
|
3851
3973
|
if (insNodes.length > 0) {
|
|
3852
3974
|
const auth = insNodes[0].getAttribute("w:author");
|
|
3853
|
-
if (auth && auth !== this.author)
|
|
3975
|
+
if (auth && auth !== this.author) {
|
|
3976
|
+
const is_fully_contained_in_ins = start >= s.start && start + length <= s.end;
|
|
3977
|
+
const is_lockout = is_fully_contained_in_ins || match_mode === "all";
|
|
3978
|
+
if (is_lockout) {
|
|
3979
|
+
nestedAuthors.add(auth);
|
|
3980
|
+
}
|
|
3981
|
+
}
|
|
3854
3982
|
}
|
|
3855
3983
|
}
|
|
3856
3984
|
if (s.comment_ids) {
|
|
@@ -3864,7 +3992,7 @@ var RedlineEngine = class {
|
|
|
3864
3992
|
}
|
|
3865
3993
|
if (nestedAuthors.size > 0) {
|
|
3866
3994
|
errors.push(
|
|
3867
|
-
`- 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.`
|
|
3995
|
+
`- 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.`
|
|
3868
3996
|
);
|
|
3869
3997
|
}
|
|
3870
3998
|
}
|
|
@@ -3926,22 +4054,13 @@ var RedlineEngine = class {
|
|
|
3926
4054
|
});
|
|
3927
4055
|
}
|
|
3928
4056
|
if (dry_run) {
|
|
3929
|
-
const
|
|
3930
|
-
|
|
3931
|
-
if (part._element) {
|
|
3932
|
-
baselines.set(part, part._element.cloneNode(true));
|
|
3933
|
-
}
|
|
3934
|
-
}
|
|
4057
|
+
const snapshot = takeSnapshot(this.doc);
|
|
4058
|
+
const originalCurrentId = this.current_id;
|
|
3935
4059
|
try {
|
|
3936
4060
|
return this._process_batch_internal(changes, true);
|
|
3937
4061
|
} finally {
|
|
3938
|
-
|
|
3939
|
-
|
|
3940
|
-
if (doc && doc.documentElement) {
|
|
3941
|
-
doc.replaceChild(originalEl, doc.documentElement);
|
|
3942
|
-
}
|
|
3943
|
-
part._element = originalEl;
|
|
3944
|
-
}
|
|
4062
|
+
restoreSnapshot(this.doc, snapshot);
|
|
4063
|
+
this.current_id = originalCurrentId;
|
|
3945
4064
|
this.mapper = new DocumentMapper(this.doc);
|
|
3946
4065
|
this.comments_manager = new CommentsManager(this.doc);
|
|
3947
4066
|
this.clean_mapper = null;
|
|
@@ -3951,6 +4070,13 @@ var RedlineEngine = class {
|
|
|
3951
4070
|
}
|
|
3952
4071
|
}
|
|
3953
4072
|
_process_batch_internal(changes, dry_run_mode = false) {
|
|
4073
|
+
for (const c of changes) {
|
|
4074
|
+
if (c && typeof c === "object" && c.type === "modify" && c.target_text && c.new_text) {
|
|
4075
|
+
const [strippedTarget, strippedNew] = stripMatchingHeadingHashes(c.target_text, c.new_text);
|
|
4076
|
+
c.target_text = strippedTarget;
|
|
4077
|
+
c.new_text = strippedNew;
|
|
4078
|
+
}
|
|
4079
|
+
}
|
|
3954
4080
|
this.skipped_details = [];
|
|
3955
4081
|
const actions = changes.filter(
|
|
3956
4082
|
(c) => c !== null && typeof c === "object" && ["accept", "reject", "reply"].includes(c.type)
|
|
@@ -3958,14 +4084,67 @@ var RedlineEngine = class {
|
|
|
3958
4084
|
const edits = changes.filter(
|
|
3959
4085
|
(c) => c === null || typeof c !== "object" || !["accept", "reject", "reply"].includes(c.type)
|
|
3960
4086
|
);
|
|
3961
|
-
|
|
3962
|
-
|
|
3963
|
-
if (
|
|
3964
|
-
|
|
4087
|
+
let mapper_dirty = false;
|
|
4088
|
+
for (const edit of edits) {
|
|
4089
|
+
if (typeof edit !== "object" || edit === null || !edit.target_text) continue;
|
|
4090
|
+
const is_regex = edit.regex || false;
|
|
4091
|
+
let matches = this.mapper.find_all_match_indices(edit.target_text, is_regex);
|
|
4092
|
+
let target_mapper = this.mapper;
|
|
4093
|
+
if (matches.length === 0) {
|
|
4094
|
+
if (!this.clean_mapper) {
|
|
4095
|
+
this.clean_mapper = new DocumentMapper(this.doc, true);
|
|
4096
|
+
}
|
|
4097
|
+
matches = this.clean_mapper.find_all_match_indices(edit.target_text, is_regex);
|
|
4098
|
+
target_mapper = this.clean_mapper;
|
|
3965
4099
|
}
|
|
3966
|
-
|
|
3967
|
-
|
|
4100
|
+
for (const [start, length] of matches) {
|
|
4101
|
+
const spans = target_mapper.spans.filter(
|
|
4102
|
+
(s) => s.end > start && s.start < start + length
|
|
4103
|
+
);
|
|
4104
|
+
for (const s of spans) {
|
|
4105
|
+
if (s.ins_id) {
|
|
4106
|
+
const insNodes = findAllDescendants(this.doc.element, "w:ins").filter(
|
|
4107
|
+
(n) => n.getAttribute("w:id") === s.ins_id
|
|
4108
|
+
);
|
|
4109
|
+
if (insNodes.length > 0) {
|
|
4110
|
+
const auth = insNodes[0].getAttribute("w:author");
|
|
4111
|
+
if (auth && auth !== this.author) {
|
|
4112
|
+
const is_fully_contained_in_ins = start >= s.start && start + length <= s.end;
|
|
4113
|
+
const match_mode = edit.match_mode || "strict";
|
|
4114
|
+
if (!is_fully_contained_in_ins && match_mode !== "all") {
|
|
4115
|
+
const node = insNodes[0];
|
|
4116
|
+
this._clean_wrapping_comments(node);
|
|
4117
|
+
const parent = node.parentNode;
|
|
4118
|
+
if (parent) {
|
|
4119
|
+
if (parent.tagName === "w:trPr") {
|
|
4120
|
+
parent.removeChild(node);
|
|
4121
|
+
} else {
|
|
4122
|
+
while (node.firstChild) {
|
|
4123
|
+
parent.insertBefore(node.firstChild, node);
|
|
4124
|
+
}
|
|
4125
|
+
parent.removeChild(node);
|
|
4126
|
+
}
|
|
4127
|
+
}
|
|
4128
|
+
mapper_dirty = true;
|
|
4129
|
+
}
|
|
4130
|
+
}
|
|
4131
|
+
}
|
|
4132
|
+
}
|
|
4133
|
+
}
|
|
3968
4134
|
}
|
|
4135
|
+
}
|
|
4136
|
+
if (mapper_dirty) {
|
|
4137
|
+
this.mapper = new DocumentMapper(this.doc);
|
|
4138
|
+
this.clean_mapper = null;
|
|
4139
|
+
}
|
|
4140
|
+
if (!dry_run_mode) {
|
|
4141
|
+
const action_errors = actions.length > 0 ? this.validate_review_actions(actions) : [];
|
|
4142
|
+
const validate_edits_now = edits.length > 0 && action_errors.length > 0;
|
|
4143
|
+
const edit_errors = validate_edits_now ? this.validate_edits(edits) : [];
|
|
4144
|
+
const all_errors = [
|
|
4145
|
+
...action_errors,
|
|
4146
|
+
...edit_errors
|
|
4147
|
+
];
|
|
3969
4148
|
if (all_errors.length > 0) {
|
|
3970
4149
|
throw new BatchValidationError(all_errors);
|
|
3971
4150
|
}
|
|
@@ -3999,8 +4178,9 @@ var RedlineEngine = class {
|
|
|
3999
4178
|
let skipped_edits = 0;
|
|
4000
4179
|
if (edits.length > 0) {
|
|
4001
4180
|
if (dry_run_mode) {
|
|
4002
|
-
for (
|
|
4003
|
-
const
|
|
4181
|
+
for (let i = 0; i < edits.length; i++) {
|
|
4182
|
+
const edit = edits[i];
|
|
4183
|
+
const single_errors = this.validate_edits([edit], i);
|
|
4004
4184
|
const warning = this._check_punctuation_warning(
|
|
4005
4185
|
edit.target_text || ""
|
|
4006
4186
|
);
|
|
@@ -4034,6 +4214,8 @@ var RedlineEngine = class {
|
|
|
4034
4214
|
occurrences_modified: edit._occurrences_modified || 0,
|
|
4035
4215
|
match_mode: edit.match_mode || "strict"
|
|
4036
4216
|
});
|
|
4217
|
+
this.mapper = new DocumentMapper(this.doc);
|
|
4218
|
+
this.clean_mapper = null;
|
|
4037
4219
|
} else {
|
|
4038
4220
|
skipped_edits++;
|
|
4039
4221
|
const error_msg = this.skipped_details.length > 0 ? this.skipped_details[this.skipped_details.length - 1] : "Failed to apply edit";
|
|
@@ -4049,15 +4231,37 @@ var RedlineEngine = class {
|
|
|
4049
4231
|
}
|
|
4050
4232
|
}
|
|
4051
4233
|
} else {
|
|
4052
|
-
const
|
|
4053
|
-
|
|
4054
|
-
|
|
4234
|
+
const snapshot = takeSnapshot(this.doc);
|
|
4235
|
+
const originalCurrentId = this.current_id;
|
|
4236
|
+
try {
|
|
4237
|
+
const sequential_errors = [];
|
|
4238
|
+
for (let i = 0; i < edits.length; i++) {
|
|
4239
|
+
const edit = edits[i];
|
|
4240
|
+
const single_errors = this.validate_edits([edit], i);
|
|
4241
|
+
if (single_errors.length > 0) {
|
|
4242
|
+
sequential_errors.push(...single_errors);
|
|
4243
|
+
} else {
|
|
4244
|
+
this.apply_edits([edit], page_offsets);
|
|
4245
|
+
this.mapper = new DocumentMapper(this.doc);
|
|
4246
|
+
this.clean_mapper = null;
|
|
4247
|
+
}
|
|
4248
|
+
}
|
|
4249
|
+
if (sequential_errors.length > 0) {
|
|
4250
|
+
throw new BatchValidationError(sequential_errors);
|
|
4251
|
+
}
|
|
4252
|
+
} catch (err) {
|
|
4253
|
+
restoreSnapshot(this.doc, snapshot);
|
|
4254
|
+
this.current_id = originalCurrentId;
|
|
4255
|
+
this.mapper = new DocumentMapper(this.doc);
|
|
4256
|
+
this.comments_manager = new CommentsManager(this.doc);
|
|
4257
|
+
this.clean_mapper = null;
|
|
4258
|
+
throw err;
|
|
4055
4259
|
}
|
|
4056
|
-
|
|
4057
|
-
|
|
4058
|
-
|
|
4059
|
-
skipped_edits =
|
|
4060
|
-
for (const edit of
|
|
4260
|
+
applied_edits = edits.filter(
|
|
4261
|
+
(e) => e._applied_status
|
|
4262
|
+
).length;
|
|
4263
|
+
skipped_edits = edits.length - applied_edits;
|
|
4264
|
+
for (const edit of edits) {
|
|
4061
4265
|
const success = edit._applied_status || false;
|
|
4062
4266
|
const error_msg = edit._error_msg || null;
|
|
4063
4267
|
const warning = this._check_punctuation_warning(
|
|
@@ -4211,14 +4415,22 @@ var RedlineEngine = class {
|
|
|
4211
4415
|
if (parent) {
|
|
4212
4416
|
parent._applied_status = true;
|
|
4213
4417
|
parent._occurrences_modified = (parent._occurrences_modified || 0) + 1;
|
|
4214
|
-
const [path, page] = this._get_heading_path_and_page(
|
|
4418
|
+
const [path, page] = this._get_heading_path_and_page(
|
|
4419
|
+
start,
|
|
4420
|
+
this.mapper.full_text,
|
|
4421
|
+
page_offsets
|
|
4422
|
+
);
|
|
4215
4423
|
const pages = parent._pages || [];
|
|
4216
4424
|
if (!pages.includes(page)) pages.unshift(page);
|
|
4217
4425
|
parent._pages = pages;
|
|
4218
4426
|
parent._heading_path = path;
|
|
4219
4427
|
} else {
|
|
4220
4428
|
edit._occurrences_modified = (edit._occurrences_modified || 0) + 1;
|
|
4221
|
-
const [path, page] = this._get_heading_path_and_page(
|
|
4429
|
+
const [path, page] = this._get_heading_path_and_page(
|
|
4430
|
+
start,
|
|
4431
|
+
this.mapper.full_text,
|
|
4432
|
+
page_offsets
|
|
4433
|
+
);
|
|
4222
4434
|
const pages = edit._pages || [];
|
|
4223
4435
|
if (!pages.includes(page)) pages.unshift(page);
|
|
4224
4436
|
edit._pages = pages;
|
|
@@ -4379,12 +4591,18 @@ var RedlineEngine = class {
|
|
|
4379
4591
|
if (!edit.target_text) return null;
|
|
4380
4592
|
const is_regex = edit.regex || false;
|
|
4381
4593
|
const match_mode = edit.match_mode || "strict";
|
|
4382
|
-
let matches = this.mapper.find_all_match_indices(
|
|
4594
|
+
let matches = this.mapper.find_all_match_indices(
|
|
4595
|
+
edit.target_text,
|
|
4596
|
+
is_regex
|
|
4597
|
+
);
|
|
4383
4598
|
let use_clean_map = false;
|
|
4384
4599
|
if (matches.length === 0) {
|
|
4385
4600
|
if (!this.clean_mapper)
|
|
4386
4601
|
this.clean_mapper = new DocumentMapper(this.doc, true);
|
|
4387
|
-
matches = this.clean_mapper.find_all_match_indices(
|
|
4602
|
+
matches = this.clean_mapper.find_all_match_indices(
|
|
4603
|
+
edit.target_text,
|
|
4604
|
+
is_regex
|
|
4605
|
+
);
|
|
4388
4606
|
if (matches.length > 0) use_clean_map = true;
|
|
4389
4607
|
else return null;
|
|
4390
4608
|
}
|
|
@@ -4409,14 +4627,49 @@ var RedlineEngine = class {
|
|
|
4409
4627
|
start_idx + match_len
|
|
4410
4628
|
);
|
|
4411
4629
|
let current_effective_new_text = edit.new_text || "";
|
|
4630
|
+
if (/^\{#cell:[^}]+\}$/.test(actual_doc_text.trim())) {
|
|
4631
|
+
let ins_text = current_effective_new_text;
|
|
4632
|
+
ins_text = ins_text.split(actual_doc_text.trim()).join("");
|
|
4633
|
+
if (ins_text) {
|
|
4634
|
+
all_sub_edits.push({
|
|
4635
|
+
type: "modify",
|
|
4636
|
+
target_text: "",
|
|
4637
|
+
new_text: ins_text,
|
|
4638
|
+
comment: edit.comment,
|
|
4639
|
+
// Insert at the anchor token's start so the new run lands inside
|
|
4640
|
+
// the cell paragraph that get_insertion_anchor resolves there.
|
|
4641
|
+
_match_start_index: start_idx,
|
|
4642
|
+
_internal_op: "INSERTION",
|
|
4643
|
+
_active_mapper_ref: active_mapper
|
|
4644
|
+
});
|
|
4645
|
+
} else if (edit.comment) {
|
|
4646
|
+
all_sub_edits.push({
|
|
4647
|
+
type: "modify",
|
|
4648
|
+
target_text: "",
|
|
4649
|
+
new_text: "",
|
|
4650
|
+
comment: edit.comment,
|
|
4651
|
+
_match_start_index: start_idx,
|
|
4652
|
+
_internal_op: "COMMENT_ONLY",
|
|
4653
|
+
_active_mapper_ref: active_mapper
|
|
4654
|
+
});
|
|
4655
|
+
}
|
|
4656
|
+
continue;
|
|
4657
|
+
}
|
|
4412
4658
|
if (is_regex && current_effective_new_text) {
|
|
4413
4659
|
try {
|
|
4414
|
-
current_effective_new_text = actual_doc_text.replace(
|
|
4660
|
+
current_effective_new_text = actual_doc_text.replace(
|
|
4661
|
+
new RegExp(edit.target_text),
|
|
4662
|
+
current_effective_new_text
|
|
4663
|
+
);
|
|
4415
4664
|
} catch (e) {
|
|
4416
4665
|
}
|
|
4417
4666
|
}
|
|
4418
|
-
const [edit_target_clean, edit_target_style] = this._parse_markdown_style(
|
|
4419
|
-
|
|
4667
|
+
const [edit_target_clean, edit_target_style] = this._parse_markdown_style(
|
|
4668
|
+
edit.target_text
|
|
4669
|
+
);
|
|
4670
|
+
const [edit_new_clean, edit_new_style] = this._parse_markdown_style(
|
|
4671
|
+
current_effective_new_text
|
|
4672
|
+
);
|
|
4420
4673
|
if (edit_target_style !== edit_new_style) {
|
|
4421
4674
|
const [actual_clean] = this._parse_markdown_style(actual_doc_text);
|
|
4422
4675
|
const final_target2 = actual_clean;
|
|
@@ -4455,7 +4708,9 @@ var RedlineEngine = class {
|
|
|
4455
4708
|
let effective_start_idx = start_idx;
|
|
4456
4709
|
if (current_effective_new_text.startsWith(actual_doc_text)) {
|
|
4457
4710
|
effective_op = "INSERTION";
|
|
4458
|
-
final_new = current_effective_new_text.substring(
|
|
4711
|
+
final_new = current_effective_new_text.substring(
|
|
4712
|
+
actual_doc_text.length
|
|
4713
|
+
);
|
|
4459
4714
|
effective_start_idx = start_idx + match_len;
|
|
4460
4715
|
} else {
|
|
4461
4716
|
const [prefix_len, suffix_len] = trim_common_context(
|
|
@@ -4536,7 +4791,12 @@ var RedlineEngine = class {
|
|
|
4536
4791
|
const first_anchor = ascend_to_paragraph_child(first_el, start_p);
|
|
4537
4792
|
const last_anchor = ascend_to_paragraph_child(last_el, end_p);
|
|
4538
4793
|
if (start_p === end_p) {
|
|
4539
|
-
this._attach_comment(
|
|
4794
|
+
this._attach_comment(
|
|
4795
|
+
start_p,
|
|
4796
|
+
first_anchor,
|
|
4797
|
+
last_anchor,
|
|
4798
|
+
edit.comment
|
|
4799
|
+
);
|
|
4540
4800
|
} else {
|
|
4541
4801
|
this._attach_comment_spanning(
|
|
4542
4802
|
start_p,
|
|
@@ -5825,13 +6085,21 @@ function extract_table(table, comments_map, cleanView, cursor, paragraph_offsets
|
|
|
5825
6085
|
if (seen_cells.has(cell)) continue;
|
|
5826
6086
|
seen_cells.add(cell);
|
|
5827
6087
|
if (!first_cell) cell_cursor += 3;
|
|
5828
|
-
|
|
6088
|
+
let cell_content = _extract_blocks(
|
|
5829
6089
|
cell,
|
|
5830
6090
|
comments_map,
|
|
5831
6091
|
cleanView,
|
|
5832
6092
|
cell_cursor,
|
|
5833
6093
|
paragraph_offsets
|
|
5834
6094
|
);
|
|
6095
|
+
if (!cleanView) {
|
|
6096
|
+
const firstP = cell._element.getElementsByTagName("w:p")[0];
|
|
6097
|
+
const paraId = firstP ? firstP.getAttribute("w14:paraId") : null;
|
|
6098
|
+
if (paraId) {
|
|
6099
|
+
const anchor = `{#cell:${paraId}}`;
|
|
6100
|
+
cell_content = cell_content + anchor;
|
|
6101
|
+
}
|
|
6102
|
+
}
|
|
5835
6103
|
cell_texts.push(cell_content);
|
|
5836
6104
|
cell_cursor += cell_content.length;
|
|
5837
6105
|
first_cell = false;
|