@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.cjs CHANGED
@@ -2318,7 +2318,7 @@ ${header}`;
2318
2318
  let dom_modified = false;
2319
2319
  const first_real_span = real_spans[0];
2320
2320
  let start_split_adjustment = 0;
2321
- const local_start = start_idx - first_real_span.start + (first_real_span.run_offset || 0);
2321
+ const local_start = Math.max(start_idx, first_real_span.start) - first_real_span.start + (first_real_span.run_offset || 0);
2322
2322
  if (local_start > 0) {
2323
2323
  const split_source = working_runs[0];
2324
2324
  const [, right_run] = this._split_run_at_index(
@@ -4868,7 +4868,7 @@ var RedlineEngine = class _RedlineEngine {
4868
4868
  *
4869
4869
  * Does NOT attach comments; callers handle that.
4870
4870
  */
4871
- _track_insert_multiline(text, anchor_run, anchor_paragraph, reuse_id, positional_anchor_el = null, suppress_emphasis = false) {
4871
+ _track_insert_multiline(text, anchor_run, anchor_paragraph, reuse_id, positional_anchor_el = null, suppress_emphasis = false, insert_before = false) {
4872
4872
  if (!text) {
4873
4873
  return {
4874
4874
  first_node: null,
@@ -4890,7 +4890,11 @@ var RedlineEngine = class _RedlineEngine {
4890
4890
  current_p = walker;
4891
4891
  }
4892
4892
  const suffix_nodes = [];
4893
- const pos_source = positional_anchor_el && positional_anchor_el.parentNode ? positional_anchor_el : anchor_run !== null && anchor_run._element.parentNode ? anchor_run._element : null;
4893
+ const relocatable = /* @__PURE__ */ new Set(["w:r", "w:ins", "w:del"]);
4894
+ const pos_from_positional = positional_anchor_el && positional_anchor_el.parentNode ? positional_anchor_el : null;
4895
+ const pos_from_anchor_run = anchor_run !== null && anchor_run._element.parentNode ? anchor_run._element : null;
4896
+ const pos_source = pos_from_positional ?? pos_from_anchor_run;
4897
+ const suffix_includes_anchor = insert_before && pos_from_positional === null && pos_from_anchor_run !== null;
4894
4898
  if (current_p !== null && pos_source !== null) {
4895
4899
  let pos_anchor = pos_source;
4896
4900
  while (pos_anchor && pos_anchor.parentNode !== current_p) {
@@ -4901,8 +4905,7 @@ var RedlineEngine = class _RedlineEngine {
4901
4905
  }
4902
4906
  }
4903
4907
  if (pos_anchor) {
4904
- const relocatable = /* @__PURE__ */ new Set(["w:r", "w:ins", "w:del"]);
4905
- let nxt = pos_anchor.nextSibling;
4908
+ let nxt = suffix_includes_anchor ? pos_anchor : pos_anchor.nextSibling;
4906
4909
  while (nxt) {
4907
4910
  if (nxt.nodeType === 1 && relocatable.has(nxt.tagName)) {
4908
4911
  suffix_nodes.push(nxt);
@@ -4910,6 +4913,14 @@ var RedlineEngine = class _RedlineEngine {
4910
4913
  nxt = nxt.nextSibling;
4911
4914
  }
4912
4915
  }
4916
+ } else if (current_p !== null && insert_before) {
4917
+ let child = current_p.firstChild;
4918
+ while (child) {
4919
+ if (child.nodeType === 1 && relocatable.has(child.tagName)) {
4920
+ suffix_nodes.push(child);
4921
+ }
4922
+ child = child.nextSibling;
4923
+ }
4913
4924
  }
4914
4925
  while (lines.length > 1 && lines[lines.length - 1] === "" && suffix_nodes.length === 0) {
4915
4926
  lines.pop();
@@ -5648,6 +5659,45 @@ var RedlineEngine = class _RedlineEngine {
5648
5659
  }
5649
5660
  validate_review_actions(actions) {
5650
5661
  const errors = [];
5662
+ const seen_resolutions = /* @__PURE__ */ new Map();
5663
+ const seen_replies = /* @__PURE__ */ new Set();
5664
+ for (let i = 0; i < actions.length; i++) {
5665
+ const action = actions[i];
5666
+ const type = action.type;
5667
+ const target_id = action.target_id ?? "";
5668
+ if (type === "reply") {
5669
+ if (!String(action.text ?? "").trim()) {
5670
+ errors.push(
5671
+ `- 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'.`
5672
+ );
5673
+ continue;
5674
+ }
5675
+ const reply_key = `${target_id}\0${String(action.text).trim()}`;
5676
+ if (seen_replies.has(reply_key)) {
5677
+ errors.push(
5678
+ `- Action ${i + 1} Failed: duplicate reply \u2014 this batch already replies to ${target_id} with the same text. Remove the duplicate action.`
5679
+ );
5680
+ }
5681
+ seen_replies.add(reply_key);
5682
+ } else if (type === "accept" || type === "reject") {
5683
+ const prior = seen_resolutions.get(target_id);
5684
+ if (prior !== void 0) {
5685
+ const [first_idx, first_type] = prior;
5686
+ if (first_type === type) {
5687
+ errors.push(
5688
+ `- 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.`
5689
+ );
5690
+ } else {
5691
+ errors.push(
5692
+ `- 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.`
5693
+ );
5694
+ }
5695
+ } else {
5696
+ seen_resolutions.set(target_id, [i, type]);
5697
+ }
5698
+ }
5699
+ }
5700
+ if (errors.length > 0) return errors;
5651
5701
  for (let i = 0; i < actions.length; i++) {
5652
5702
  const action = actions[i];
5653
5703
  const type = action.type;
@@ -6895,7 +6945,10 @@ var RedlineEngine = class _RedlineEngine {
6895
6945
  anchor_para,
6896
6946
  ins_id,
6897
6947
  null,
6898
- suppress_emphasis
6948
+ suppress_emphasis,
6949
+ // Paragraph-start insertions attach BEFORE the anchor (see
6950
+ // before_anchor below): the suffix relocation must know.
6951
+ start_idx === 0
6899
6952
  );
6900
6953
  if (!result.first_node) return false;
6901
6954
  const is_inline_first = result.first_node.tagName === "w:ins";