@adeu/core 1.26.0 → 1.27.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/dist/index.js CHANGED
@@ -2259,7 +2259,7 @@ ${header}`;
2259
2259
  let dom_modified = false;
2260
2260
  const first_real_span = real_spans[0];
2261
2261
  let start_split_adjustment = 0;
2262
- const local_start = start_idx - first_real_span.start + (first_real_span.run_offset || 0);
2262
+ const local_start = Math.max(start_idx, first_real_span.start) - first_real_span.start + (first_real_span.run_offset || 0);
2263
2263
  if (local_start > 0) {
2264
2264
  const split_source = working_runs[0];
2265
2265
  const [, right_run] = this._split_run_at_index(
@@ -4809,7 +4809,7 @@ var RedlineEngine = class _RedlineEngine {
4809
4809
  *
4810
4810
  * Does NOT attach comments; callers handle that.
4811
4811
  */
4812
- _track_insert_multiline(text, anchor_run, anchor_paragraph, reuse_id, positional_anchor_el = null, suppress_emphasis = false) {
4812
+ _track_insert_multiline(text, anchor_run, anchor_paragraph, reuse_id, positional_anchor_el = null, suppress_emphasis = false, insert_before = false) {
4813
4813
  if (!text) {
4814
4814
  return {
4815
4815
  first_node: null,
@@ -4831,7 +4831,11 @@ var RedlineEngine = class _RedlineEngine {
4831
4831
  current_p = walker;
4832
4832
  }
4833
4833
  const suffix_nodes = [];
4834
- const pos_source = positional_anchor_el && positional_anchor_el.parentNode ? positional_anchor_el : anchor_run !== null && anchor_run._element.parentNode ? anchor_run._element : null;
4834
+ const relocatable = /* @__PURE__ */ new Set(["w:r", "w:ins", "w:del"]);
4835
+ const pos_from_positional = positional_anchor_el && positional_anchor_el.parentNode ? positional_anchor_el : null;
4836
+ const pos_from_anchor_run = anchor_run !== null && anchor_run._element.parentNode ? anchor_run._element : null;
4837
+ const pos_source = pos_from_positional ?? pos_from_anchor_run;
4838
+ const suffix_includes_anchor = insert_before && pos_from_positional === null && pos_from_anchor_run !== null;
4835
4839
  if (current_p !== null && pos_source !== null) {
4836
4840
  let pos_anchor = pos_source;
4837
4841
  while (pos_anchor && pos_anchor.parentNode !== current_p) {
@@ -4842,8 +4846,7 @@ var RedlineEngine = class _RedlineEngine {
4842
4846
  }
4843
4847
  }
4844
4848
  if (pos_anchor) {
4845
- const relocatable = /* @__PURE__ */ new Set(["w:r", "w:ins", "w:del"]);
4846
- let nxt = pos_anchor.nextSibling;
4849
+ let nxt = suffix_includes_anchor ? pos_anchor : pos_anchor.nextSibling;
4847
4850
  while (nxt) {
4848
4851
  if (nxt.nodeType === 1 && relocatable.has(nxt.tagName)) {
4849
4852
  suffix_nodes.push(nxt);
@@ -4851,6 +4854,14 @@ var RedlineEngine = class _RedlineEngine {
4851
4854
  nxt = nxt.nextSibling;
4852
4855
  }
4853
4856
  }
4857
+ } else if (current_p !== null && insert_before) {
4858
+ let child = current_p.firstChild;
4859
+ while (child) {
4860
+ if (child.nodeType === 1 && relocatable.has(child.tagName)) {
4861
+ suffix_nodes.push(child);
4862
+ }
4863
+ child = child.nextSibling;
4864
+ }
4854
4865
  }
4855
4866
  while (lines.length > 1 && lines[lines.length - 1] === "" && suffix_nodes.length === 0) {
4856
4867
  lines.pop();
@@ -5589,6 +5600,45 @@ var RedlineEngine = class _RedlineEngine {
5589
5600
  }
5590
5601
  validate_review_actions(actions) {
5591
5602
  const errors = [];
5603
+ const seen_resolutions = /* @__PURE__ */ new Map();
5604
+ const seen_replies = /* @__PURE__ */ new Set();
5605
+ for (let i = 0; i < actions.length; i++) {
5606
+ const action = actions[i];
5607
+ const type = action.type;
5608
+ const target_id = action.target_id ?? "";
5609
+ if (type === "reply") {
5610
+ if (!String(action.text ?? "").trim()) {
5611
+ errors.push(
5612
+ `- Action ${i + 1} Failed: reply text for ${target_id} is empty or whitespace-only. Word would show a blank comment bubble \u2014 provide the reply content in 'text'.`
5613
+ );
5614
+ continue;
5615
+ }
5616
+ const reply_key = `${target_id}\0${String(action.text).trim()}`;
5617
+ if (seen_replies.has(reply_key)) {
5618
+ errors.push(
5619
+ `- Action ${i + 1} Failed: duplicate reply \u2014 this batch already replies to ${target_id} with the same text. Remove the duplicate action.`
5620
+ );
5621
+ }
5622
+ seen_replies.add(reply_key);
5623
+ } else if (type === "accept" || type === "reject") {
5624
+ const prior = seen_resolutions.get(target_id);
5625
+ if (prior !== void 0) {
5626
+ const [first_idx, first_type] = prior;
5627
+ if (first_type === type) {
5628
+ errors.push(
5629
+ `- Action ${i + 1} Failed: duplicate action \u2014 Action ${first_idx + 1} in this batch already applies '${type}' to ${target_id}. A change can only be resolved once; remove the duplicate action.`
5630
+ );
5631
+ } else {
5632
+ errors.push(
5633
+ `- Action ${i + 1} Failed: conflicting actions \u2014 Action ${first_idx + 1} in this batch applies '${first_type}' to ${target_id}, but this action applies '${type}'. Decide the outcome and keep exactly one of them.`
5634
+ );
5635
+ }
5636
+ } else {
5637
+ seen_resolutions.set(target_id, [i, type]);
5638
+ }
5639
+ }
5640
+ }
5641
+ if (errors.length > 0) return errors;
5592
5642
  for (let i = 0; i < actions.length; i++) {
5593
5643
  const action = actions[i];
5594
5644
  const type = action.type;
@@ -6836,7 +6886,10 @@ var RedlineEngine = class _RedlineEngine {
6836
6886
  anchor_para,
6837
6887
  ins_id,
6838
6888
  null,
6839
- suppress_emphasis
6889
+ suppress_emphasis,
6890
+ // Paragraph-start insertions attach BEFORE the anchor (see
6891
+ // before_anchor below): the suffix relocation must know.
6892
+ start_idx === 0
6840
6893
  );
6841
6894
  if (!result.first_node) return false;
6842
6895
  const is_inline_first = result.first_node.tagName === "w:ins";