@wordinweb/agent 0.2.3 → 0.2.5

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.
@@ -407,6 +407,9 @@ function serializeXml(el26, declaration = false) {
407
407
  write2(el26);
408
408
  return out.join("");
409
409
  }
410
+ function isEmptyPlaceholder(e) {
411
+ return !!e.omitWhenEmpty && Object.keys(e.attrs).length === 0 && e.text.length === 0 && e.children.length === 1 && localName(e.children[0].name) === "t" && e.children[0].children.length === 0 && e.children[0].text.length === 0 && Object.keys(e.children[0].attrs).every((key) => key === "xml:space" && e.children[0].attrs[key] === "preserve");
412
+ }
410
413
  function cloneXml(el26) {
411
414
  return {
412
415
  name: el26.name,
@@ -452,6 +455,12 @@ var Package = class _Package {
452
455
  static from(data) {
453
456
  return new _Package(data instanceof Uint8Array ? data : new Uint8Array(data));
454
457
  }
458
+ /** A package over an already-unzipped entry map (DocxDocument.clone). */
459
+ static fromFiles(files) {
460
+ const pkg = Object.create(_Package.prototype);
461
+ pkg.files = files;
462
+ return pkg;
463
+ }
455
464
  has(name) {
456
465
  return this.normalize(name) in this.files;
457
466
  }
@@ -474,6 +483,13 @@ var Package = class _Package {
474
483
  return name.startsWith("/") ? name.slice(1) : name;
475
484
  }
476
485
  };
486
+ function relativePartPath(fromPart, toPart) {
487
+ const fromDir = fromPart.split("/").slice(0, -1);
488
+ const to = toPart.split("/");
489
+ let i = 0;
490
+ while (i < fromDir.length && i < to.length - 1 && fromDir[i] === to[i]) i++;
491
+ return [...fromDir.slice(i).map(() => ".."), ...to.slice(i)].join("/");
492
+ }
477
493
  function resolvePartPath(sourcePart, target) {
478
494
  if (target.startsWith("/")) return target.slice(1);
479
495
  const baseDir = sourcePart.includes("/") ? sourcePart.slice(0, sourcePart.lastIndexOf("/")) : "";
@@ -2695,6 +2711,11 @@ function parseParagraph(p, ctx) {
2695
2711
  }
2696
2712
  }
2697
2713
  ensureCaretAnchor(p, para);
2714
+ if (ctx.revisionView === "markup" && pPr) {
2715
+ const markRPr = child(pPr, "rPr");
2716
+ if (markRPr && child(markRPr, "del")) para.markRevision = "del";
2717
+ else if (markRPr && child(markRPr, "ins")) para.markRevision = "ins";
2718
+ }
2698
2719
  return para;
2699
2720
  }
2700
2721
  function hasVisibleContent(c) {
@@ -7025,6 +7046,10 @@ var _DocxDocument = class _DocxDocument {
7025
7046
  /** The header part roots, in package order. A watermark is authored into
7026
7047
  * every one of them: Word paints it on every page, and a document with a
7027
7048
  * first-page or even-page header has more than one header part. */
7049
+ /** The root of the header or footer part at `target`, if this document has one. */
7050
+ headerFooterRoot(target) {
7051
+ return this.hfParts.find((p) => p.target === target)?.root;
7052
+ }
7028
7053
  headerRoots() {
7029
7054
  return this.hfParts.filter((p) => p.isHeader).map((p) => p.root);
7030
7055
  }
@@ -7188,6 +7213,22 @@ var _DocxDocument = class _DocxDocument {
7188
7213
  this.saveJournal = null;
7189
7214
  }
7190
7215
  }
7216
+ /**
7217
+ * An independent copy: what `DocxDocument.load(doc.save())` gives, without
7218
+ * zipping the package and unzipping it again. compareDocuments starts from a
7219
+ * copy of the revised document, and the round trip through the zip was a
7220
+ * third of its time on a document that had not changed at all.
7221
+ */
7222
+ clone() {
7223
+ const journal = [];
7224
+ this.saveJournal = journal;
7225
+ try {
7226
+ return new _DocxDocument(Package.fromFiles(this.buildPackageFiles()));
7227
+ } finally {
7228
+ for (let i = journal.length - 1; i >= 0; i--) journal[i]();
7229
+ this.saveJournal = null;
7230
+ }
7231
+ }
7191
7232
  saveAsync() {
7192
7233
  const journal = [];
7193
7234
  this.saveJournal = journal;
@@ -7389,8 +7430,91 @@ var _DocxDocument = class _DocxDocument {
7389
7430
  return this.relsRoot;
7390
7431
  }
7391
7432
  /** Register an external hyperlink relationship and return its rId. */
7392
- addHyperlinkRel(url) {
7393
- const rels = this.ensureRelsRoot();
7433
+ /**
7434
+ * Copy the part that `source`'s main-document relationship `relId` points
7435
+ * at into this package, with every part it references in turn (a chart's
7436
+ * embedded workbook, SmartArt's layout, style and colour parts) and their
7437
+ * content types, and add a relationship to it. Returns the new
7438
+ * relationship id, or null when the source has no such internal part.
7439
+ *
7440
+ * compareDocuments needs this for struck content: a removed picture keeps
7441
+ * its w:drawing, and without its image part the result's reject-all
7442
+ * brought the picture back as an empty frame.
7443
+ */
7444
+ adoptRelFrom(source, relId, scope = {}) {
7445
+ const rel = source.relationshipsOf(scope.from).get(relId);
7446
+ if (!rel || rel.external) return null;
7447
+ const copied = /* @__PURE__ */ new Map();
7448
+ const sameBytes = (path2, bytes) => {
7449
+ const have = this.pkg.binary(path2);
7450
+ return !!have && have.length === bytes.length && have.every((b, i) => b === bytes[i]);
7451
+ };
7452
+ const copyPart = (from) => {
7453
+ const known = copied.get(from);
7454
+ if (known) return known;
7455
+ const bytes = source.pkg.binary(from);
7456
+ if (!bytes) return null;
7457
+ let to = from;
7458
+ for (let n = 2; this.pkg.has(to) && !sameBytes(to, bytes); n++) to = from.replace(/(\.[^./]+)?$/, `-${n}$1`);
7459
+ copied.set(from, to);
7460
+ if (this.pkg.has(to)) return to;
7461
+ this.pkg.raw()[to] = bytes;
7462
+ this.adoptContentType(source, from, to);
7463
+ const relsXml = source.pkg.text(relsPathFor(from));
7464
+ if (relsXml) {
7465
+ const root = parseXml(relsXml);
7466
+ for (const r of root.children) {
7467
+ if (r.attrs.TargetMode === "External" || !r.attrs.Target) continue;
7468
+ const sub = copyPart(resolvePartPath(from, r.attrs.Target));
7469
+ if (sub) r.attrs.Target = relativePartPath(to, sub);
7470
+ }
7471
+ this.pkg.raw()[relsPathFor(to)] = strToU82(serializeXml(root, true));
7472
+ }
7473
+ return to;
7474
+ };
7475
+ const part = copyPart(rel.target);
7476
+ if (!part) return null;
7477
+ const owner = this.relsOwnerOf(scope.into);
7478
+ const rels = owner.relsRoot;
7479
+ let maxId = 0;
7480
+ for (const r of rels.children) {
7481
+ const m = /^rId(\d+)$/.exec(r.attrs["Id"] ?? "");
7482
+ if (m) maxId = Math.max(maxId, parseInt(m[1], 10));
7483
+ }
7484
+ const id = `rId${maxId + 1}`;
7485
+ rels.children.push({
7486
+ name: "Relationship",
7487
+ attrs: { Id: id, Type: rel.type, Target: relativePartPath(owner.partPath, part) },
7488
+ children: [],
7489
+ text: ""
7490
+ });
7491
+ owner.relationships.set(id, { id, type: rel.type, target: part, external: false });
7492
+ return id;
7493
+ }
7494
+ /** Give a part copied from `source` the content type it had there. */
7495
+ adoptContentType(source, from, to) {
7496
+ const types = this.contentTypesRoot;
7497
+ const theirs = source.contentTypesRoot;
7498
+ if (!types || !theirs) return;
7499
+ const override = theirs.children.find((c) => localName(c.name) === "Override" && c.attrs.PartName === `/${from}`);
7500
+ const prefixEnd = types.name.indexOf(":") + 1;
7501
+ const prefix = prefixEnd > 0 ? types.name.slice(0, prefixEnd) : "";
7502
+ if (override) {
7503
+ if (!types.children.some((c) => localName(c.name) === "Override" && c.attrs.PartName === `/${to}`)) {
7504
+ types.children.push({ name: `${prefix}Override`, attrs: { PartName: `/${to}`, ContentType: override.attrs.ContentType }, children: [], text: "" });
7505
+ }
7506
+ return;
7507
+ }
7508
+ const ext = from.slice(from.lastIndexOf(".") + 1).toLowerCase();
7509
+ const byExt = (root) => root.children.find((c) => localName(c.name) === "Default" && c.attrs.Extension?.toLowerCase() === ext);
7510
+ const fallback = byExt(theirs);
7511
+ if (fallback && !byExt(types)) {
7512
+ types.children.unshift({ name: `${prefix}Default`, attrs: { Extension: ext, ContentType: fallback.attrs.ContentType }, children: [], text: "" });
7513
+ }
7514
+ }
7515
+ addHyperlinkRel(url, ownerPart) {
7516
+ const owner = this.relsOwnerOf(ownerPart);
7517
+ const rels = owner.relsRoot;
7394
7518
  let maxId = 0;
7395
7519
  for (const r of rels.children) {
7396
7520
  const m = /^rId(\d+)$/.exec(r.attrs["Id"] ?? "");
@@ -7408,7 +7532,7 @@ var _DocxDocument = class _DocxDocument {
7408
7532
  children: [],
7409
7533
  text: ""
7410
7534
  });
7411
- this.documentRels.set(id, { id, type: "hyperlink", target: url, external: true });
7535
+ owner.relationships.set(id, { id, type: "hyperlink", target: url, external: true });
7412
7536
  return id;
7413
7537
  }
7414
7538
  /** Retarget an existing external relationship (hyperlink href edit). */
@@ -7420,6 +7544,23 @@ var _DocxDocument = class _DocxDocument {
7420
7544
  if (el26) el26.attrs["Target"] = url;
7421
7545
  return true;
7422
7546
  }
7547
+ /** The relationships owned by `part` (a header or footer part), or by the
7548
+ * document part when null; a header or footer gets a rels root on demand. */
7549
+ relsOwnerOf(part) {
7550
+ const owner = part ? this.hfParts.find((p) => p.target === part) : void 0;
7551
+ if (!owner) return { relsRoot: this.ensureRelsRoot(), relationships: this.documentRels, partPath: this.docPart };
7552
+ owner.relsRoot ?? (owner.relsRoot = {
7553
+ name: "Relationships",
7554
+ attrs: { xmlns: "http://schemas.openxmlformats.org/package/2006/relationships" },
7555
+ children: [],
7556
+ text: ""
7557
+ });
7558
+ return { relsRoot: owner.relsRoot, relationships: owner.rels, partPath: owner.target };
7559
+ }
7560
+ /** The relationships of `part` (a header or footer), or of the document part. */
7561
+ relationshipsOf(part) {
7562
+ return (part ? this.hfParts.find((p) => p.target === part)?.rels : void 0) ?? this.documentRels;
7563
+ }
7423
7564
  /** The relationships of the part that holds `source`: a header or footer
7424
7565
  * part when the element is inside one, else the document part. */
7425
7566
  imageRelsOwner(source) {
@@ -11921,6 +12062,20 @@ function buildAtoms(doc, para, measurer, fields, fallbackFamily, gridPitch) {
11921
12062
  if (childEl.type === "run") pushRun(childEl);
11922
12063
  else for (const r of childEl.runs) pushRun(r, childEl.href ?? (childEl.anchor ? "#" + childEl.anchor : void 0));
11923
12064
  }
12065
+ if (para.markRevision) {
12066
+ const last = para.children.at(-1);
12067
+ const lastRun = last?.type === "hyperlink" ? last.runs.at(-1) : last;
12068
+ const deleted = para.markRevision === "del";
12069
+ pushRun({
12070
+ type: "run",
12071
+ props: {
12072
+ ...lastRun?.props ?? {},
12073
+ color: deleted ? "#D93025" : "#188038",
12074
+ ...deleted ? { strike: true } : { underline: "single" }
12075
+ },
12076
+ content: [{ kind: "text", text: "\xB6" }]
12077
+ });
12078
+ }
11924
12079
  if (gridPitch) {
11925
12080
  for (let i = 0; i + 1 < atoms.length; i++) {
11926
12081
  const punct = atoms[i];
@@ -17423,15 +17578,20 @@ var Engine = class {
17423
17578
  while (out.length < nCols) out.push(bw(fallback));
17424
17579
  return out;
17425
17580
  };
17581
+ const colTopPads = (r) => {
17582
+ const fallback = this.cellMarginsOf(tbl, false).top ?? 0;
17583
+ return rows[r].cells.flatMap((c) => Array(Math.max(1, c.props.gridSpan)).fill(c.props.margins?.top ?? fallback));
17584
+ };
17426
17585
  const boundary = (k) => {
17427
17586
  if (k === 0) return Math.max(0, ...colWidths(0, "top", tb?.top));
17428
17587
  if (k === rows.length)
17429
17588
  return Math.max(0, ...colWidths(rows.length - 1, "bottom", tb?.bottom));
17430
17589
  const above = colWidths(k - 1, "bottom", tb?.insideH);
17431
17590
  const below = colWidths(k, "top", tb?.insideH);
17591
+ const topPads = colTopPads(k);
17432
17592
  const n = Math.max(above.length, below.length);
17433
17593
  let w = 0;
17434
- for (let i = 0; i < n; i++) w = Math.max(w, Math.max(above[i] ?? 0, below[i] ?? 0));
17594
+ for (let i = 0; i < n; i++) w = Math.max(w, below[i] ?? 0, (above[i] ?? 0) - (topPads[i] ?? 0));
17435
17595
  return w;
17436
17596
  };
17437
17597
  return { top: boundary(ri), bottom: boundary(ri + 1) };
@@ -19206,7 +19366,8 @@ function ensurePPrRPr(pEl) {
19206
19366
  let rPr = pPr.children.find((c) => localName(c.name) === "rPr");
19207
19367
  if (!rPr) {
19208
19368
  rPr = el2(`${w}rPr`);
19209
- pPr.children.push(rPr);
19369
+ const at = pPr.children.findIndex((c) => localName(c.name) === "sectPr" || localName(c.name) === "pPrChange");
19370
+ pPr.children.splice(at === -1 ? pPr.children.length : at, 0, rPr);
19210
19371
  }
19211
19372
  return rPr;
19212
19373
  }
@@ -19344,6 +19505,8 @@ function convertDelTextToText(el26) {
19344
19505
  const walk2 = (e) => {
19345
19506
  if (localName(e.name) === "delText") {
19346
19507
  e.name = e.name.replace(/delText$/, "t");
19508
+ } else if (localName(e.name) === "delInstrText") {
19509
+ e.name = e.name.replace(/delInstrText$/, "instrText");
19347
19510
  }
19348
19511
  for (const c of e.children) walk2(c);
19349
19512
  };
@@ -19365,7 +19528,7 @@ function removeEl(doc, el26) {
19365
19528
  parent.children.splice(idx, 1);
19366
19529
  return true;
19367
19530
  }
19368
- var RUN_CONTAINERS = ["hyperlink", "smartTag", "dir", "bdo"];
19531
+ var RUN_CONTAINERS = ["hyperlink", "smartTag", "dir", "bdo", "fldSimple"];
19369
19532
  function removeRevisionAndEmptyHost(doc, el26) {
19370
19533
  const parent = doc.findParentOf(el26);
19371
19534
  if (!removeEl(doc, el26)) return false;
@@ -19374,26 +19537,57 @@ function removeRevisionAndEmptyHost(doc, el26) {
19374
19537
  }
19375
19538
  return true;
19376
19539
  }
19377
- function joinWithNext(doc, pEl) {
19540
+ function joinWithNext(doc, pEl, gone) {
19378
19541
  const parent = doc.findParentOf(pEl);
19379
19542
  if (!parent) return false;
19380
19543
  const idx = parent.children.indexOf(pEl);
19381
- const next = parent.children[idx + 1];
19382
- if (!next || localName(next.name) !== "p") return false;
19544
+ let at = idx + 1;
19545
+ while (at < parent.children.length && MARKERS.includes(localName(parent.children[at].name))) at++;
19546
+ const next = parent.children[at];
19547
+ if (!next || localName(next.name) !== "p") {
19548
+ const goes = gone === "del" ? ["pPr", "del", "moveFrom", "proofErr", ...MOVE_RANGES] : ["pPr", "ins", "moveTo", "proofErr", ...MOVE_RANGES];
19549
+ const stays = MARKERS.filter((m) => m !== "proofErr");
19550
+ const removable = (c) => goes.includes(localName(c.name)) || stays.includes(localName(c.name)) || isEmptyPlaceholder(c) || RUN_CONTAINERS.includes(localName(c.name)) && c.children.every(removable);
19551
+ if (!pEl.children.every(removable)) return false;
19552
+ parent.children.splice(idx, 1, ...pEl.children.filter((c) => stays.includes(localName(c.name))));
19553
+ dropEmptiedControl(doc, parent);
19554
+ return true;
19555
+ }
19556
+ const between = parent.children.slice(idx + 1, at);
19383
19557
  const moved = next.children.filter((c) => localName(c.name) !== "pPr");
19384
- pEl.children.push(...moved);
19385
- parent.children.splice(idx + 1, 1);
19558
+ pEl.children.push(...between, ...moved);
19559
+ parent.children.splice(idx + 1, at - idx);
19560
+ carrySectionBreak(pEl, next);
19386
19561
  return true;
19387
19562
  }
19563
+ var MARKERS = ["bookmarkStart", "bookmarkEnd", "commentRangeStart", "commentRangeEnd", "permStart", "permEnd", "proofErr"];
19564
+ function carrySectionBreak(pEl, next) {
19565
+ const pPrOf = (p) => p.children.find((c) => localName(c.name) === "pPr");
19566
+ const sectOf = (p) => pPrOf(p)?.children.find((c) => localName(c.name) === "sectPr");
19567
+ const mine = sectOf(pEl);
19568
+ const theirs = sectOf(next);
19569
+ if (!mine && !theirs) return;
19570
+ let pPr = pPrOf(pEl);
19571
+ if (!pPr) {
19572
+ pPr = { name: "w:pPr", attrs: {}, children: [], text: "" };
19573
+ pEl.children.unshift(pPr);
19574
+ }
19575
+ pPr.children = pPr.children.filter((c) => c !== mine);
19576
+ if (theirs) {
19577
+ const at = pPr.children.findIndex((c) => localName(c.name) === "pPrChange");
19578
+ pPr.children.splice(at === -1 ? pPr.children.length : at, 0, theirs);
19579
+ }
19580
+ if (pPr.children.length === 0) pEl.children.splice(pEl.children.indexOf(pPr), 1);
19581
+ }
19388
19582
  function collectRevisions(doc) {
19389
19583
  const out = [];
19390
19584
  const walk2 = (el26) => {
19391
19585
  const ln = localName(el26.name);
19392
19586
  if (ln === "pPr") return;
19393
- if (ln === "ins" || ln === "del") {
19587
+ if (ln === "ins" || ln === "del" || ln === "moveTo" || ln === "moveFrom") {
19394
19588
  out.push({
19395
19589
  el: el26,
19396
- kind: ln === "ins" ? "insertion" : "deletion",
19590
+ kind: ln === "ins" || ln === "moveTo" ? "insertion" : "deletion",
19397
19591
  author: attrByLocal(el26, "author")
19398
19592
  });
19399
19593
  }
@@ -19427,10 +19621,10 @@ function collectRevisions(doc) {
19427
19621
  const rPr = pPr?.children.find((c) => localName(c.name) === "rPr");
19428
19622
  for (const c of rPr?.children ?? []) {
19429
19623
  const cn = localName(c.name);
19430
- if (cn === "ins" || cn === "del") {
19624
+ if (cn === "ins" || cn === "del" || cn === "moveTo" || cn === "moveFrom") {
19431
19625
  out.push({
19432
19626
  el: c,
19433
- kind: cn === "ins" ? "markInsertion" : "markDeletion",
19627
+ kind: cn === "ins" || cn === "moveTo" ? "markInsertion" : "markDeletion",
19434
19628
  paragraph: el26,
19435
19629
  author: attrByLocal(c, "author")
19436
19630
  });
@@ -19452,7 +19646,7 @@ function applyAccept(doc, ref) {
19452
19646
  case "markDeletion":
19453
19647
  if (!ref.paragraph) return false;
19454
19648
  removeMarkRevision(ref);
19455
- return joinWithNext(doc, ref.paragraph);
19649
+ return joinWithNext(doc, ref.paragraph, "del");
19456
19650
  case "rowInsertion":
19457
19651
  return removeRowRevision(doc, ref);
19458
19652
  case "rowDeletion":
@@ -19523,7 +19717,7 @@ function applyReject(doc, ref) {
19523
19717
  case "markInsertion":
19524
19718
  if (!ref.paragraph) return false;
19525
19719
  removeMarkRevision(ref);
19526
- return joinWithNext(doc, ref.paragraph);
19720
+ return joinWithNext(doc, ref.paragraph, "ins");
19527
19721
  case "markDeletion":
19528
19722
  return removeMarkRevision(ref);
19529
19723
  case "rowInsertion":
@@ -19574,11 +19768,23 @@ function applyToAll(doc, apply, author) {
19574
19768
  for (const part of held) notes.add(part);
19575
19769
  }
19576
19770
  if (n > 0) {
19771
+ dropSettledMoveRanges(doc);
19577
19772
  doc.markNotePartsChanged([...notes]);
19578
19773
  doc.refresh();
19579
19774
  }
19580
19775
  return n;
19581
19776
  }
19777
+ var MOVE_RANGES = ["moveFromRangeStart", "moveFromRangeEnd", "moveToRangeStart", "moveToRangeEnd"];
19778
+ function dropSettledMoveRanges(doc) {
19779
+ const roots = doc.revisionRoots();
19780
+ const has = (el26) => ["moveFrom", "moveTo"].includes(localName(el26.name)) || el26.children.some(has);
19781
+ if (roots.some(has)) return;
19782
+ const strip = (el26) => {
19783
+ el26.children = el26.children.filter((c) => !MOVE_RANGES.includes(localName(c.name)));
19784
+ for (const c of el26.children) strip(c);
19785
+ };
19786
+ for (const root of roots) strip(root);
19787
+ }
19582
19788
  function removeMarkRevision(ref) {
19583
19789
  const pPr = ref.paragraph?.children.find((c) => localName(c.name) === "pPr");
19584
19790
  const rPr = pPr?.children.find((c) => localName(c.name) === "rPr");
@@ -19610,9 +19816,24 @@ function removeRow(doc, ref) {
19610
19816
  const idx = tbl.children.indexOf(ref.row);
19611
19817
  if (idx === -1) return false;
19612
19818
  tbl.children.splice(idx, 1);
19613
- if (!tbl.children.some((c) => localName(c.name) === "tr")) removeEl(doc, tbl);
19819
+ if (!tbl.children.some((c) => localName(c.name) === "tr")) {
19820
+ const holder = doc.findParentOf(tbl);
19821
+ removeEl(doc, tbl);
19822
+ if (holder) dropEmptiedControl(doc, holder);
19823
+ }
19614
19824
  return true;
19615
19825
  }
19826
+ function dropEmptiedControl(doc, container) {
19827
+ const ln = localName(container.name);
19828
+ const control = ln === "sdtContent" ? doc.findParentOf(container) : ln === "customXml" ? container : void 0;
19829
+ if (!control) return;
19830
+ const left = container.children.filter((c) => localName(c.name) !== "customXmlPr");
19831
+ if (left.some((c) => !MARKERS.includes(localName(c.name)))) return;
19832
+ const holder = doc.findParentOf(control);
19833
+ if (!holder) return;
19834
+ holder.children.splice(holder.children.indexOf(control), 1, ...left.filter((c) => localName(c.name) !== "proofErr"));
19835
+ dropEmptiedControl(doc, holder);
19836
+ }
19616
19837
 
19617
19838
  // ../core/src/edit/blocks.ts
19618
19839
  function el3(name, attrs = {}, children2 = [], text = "") {
@@ -19956,8 +20177,32 @@ function mergeParagraphBackward(doc, pEl) {
19956
20177
  const moved = pEl.children.filter((c) => localName(c.name) !== "pPr" && !isPlaceholderRun(c));
19957
20178
  prev.children.push(...moved);
19958
20179
  parent.children.splice(idx, 1);
20180
+ carryMarkRevision(prev, pEl);
19959
20181
  return finishMerge(prev);
19960
20182
  }
20183
+ function carryMarkRevision(survivor, gone) {
20184
+ const markOf = (p) => p.children.find((c) => localName(c.name) === "pPr")?.children.find((c) => localName(c.name) === "rPr");
20185
+ const isRev = (c) => localName(c.name) === "ins" || localName(c.name) === "del";
20186
+ const theirs = markOf(gone)?.children.filter(isRev) ?? [];
20187
+ let pPr = survivor.children.find((c) => localName(c.name) === "pPr");
20188
+ let rPr = markOf(survivor);
20189
+ if (!rPr && theirs.length === 0) return;
20190
+ if (!rPr) {
20191
+ const w = survivor.name.includes(":") ? survivor.name.slice(0, survivor.name.indexOf(":") + 1) : "";
20192
+ if (!pPr) {
20193
+ pPr = { name: `${w}pPr`, attrs: {}, children: [], text: "" };
20194
+ survivor.children.unshift(pPr);
20195
+ }
20196
+ rPr = { name: `${w}rPr`, attrs: {}, children: [], text: "" };
20197
+ const at = pPr.children.findIndex((c) => localName(c.name) === "sectPr" || localName(c.name) === "pPrChange");
20198
+ pPr.children.splice(at === -1 ? pPr.children.length : at, 0, rPr);
20199
+ }
20200
+ rPr.children = [...theirs, ...rPr.children.filter((c) => !isRev(c))];
20201
+ if (rPr.children.length === 0 && pPr) {
20202
+ pPr.children.splice(pPr.children.indexOf(rPr), 1);
20203
+ if (pPr.children.length === 0) survivor.children.splice(survivor.children.indexOf(pPr), 1);
20204
+ }
20205
+ }
19961
20206
  function firstTextOf(pEl) {
19962
20207
  const walk2 = (el26) => {
19963
20208
  for (const c of el26.children) {
@@ -35997,17 +36242,7 @@ var _DocxEditor = class _DocxEditor {
35997
36242
  if (pEl && firstTextOf(pEl) === caret.t) {
35998
36243
  this.host.history?.checkpoint();
35999
36244
  if (this.clearListAtCaret()) return;
36000
- const prev = siblingParagraph(this.host.doc, pEl, -1);
36001
- if (!prev) return;
36002
- const junction = lastTextOf(prev);
36003
- const mergeBlockId = this.host.onIntent && this.host.doc.stableIds ? this.host.doc.stableIds.idOf(pEl) : void 0;
36004
- if (mergeParagraphBackward(this.host.doc, pEl)) {
36005
- if (mergeBlockId !== void 0) this.host.onIntent?.({ kind: "mergeParagraph", blockId: mergeBlockId });
36006
- const target = junction && paragraphOf(this.host.doc, junction) ? junction : caret.t;
36007
- this.caret = { t: target, run: caret.run, offset: target === junction ? target.text.length : 0 };
36008
- invalidateParagraphSignature(paragraphOf(this.host.doc, target) ?? pEl);
36009
- this.commit(false, "local", true);
36010
- }
36245
+ this.mergeIntoPrevious(pEl);
36011
36246
  return;
36012
36247
  }
36013
36248
  if (!this.stepToNeighbor(-1)) return;
@@ -36023,19 +36258,8 @@ var _DocxEditor = class _DocxEditor {
36023
36258
  if (caret.offset >= caret.t.text.length) {
36024
36259
  const pEl = paragraphOf(this.host.doc, caret.t);
36025
36260
  if (pEl && lastTextOf(pEl) === caret.t) {
36026
- const next = siblingParagraph(this.host.doc, pEl, 1);
36027
- if (!next) return;
36028
36261
  this.host.history?.checkpoint();
36029
- const mergeNextId = this.host.onIntent && this.host.doc.stableIds ? this.host.doc.stableIds.idOf(next) : void 0;
36030
- if (mergeParagraphBackward(this.host.doc, next)) {
36031
- if (mergeNextId !== void 0) this.host.onIntent?.({ kind: "mergeParagraph", blockId: mergeNextId });
36032
- if (!paragraphOf(this.host.doc, caret.t)) {
36033
- const t = firstTextOf(next);
36034
- if (t) this.caret = { t, run: caret.run, offset: 0 };
36035
- }
36036
- invalidateParagraphSignature(paragraphOf(this.host.doc, this.caret?.t ?? caret.t) ?? pEl);
36037
- this.commit(false, "local", true);
36038
- }
36262
+ this.mergeNextInto(pEl);
36039
36263
  return;
36040
36264
  }
36041
36265
  if (!this.stepToNeighbor(1)) return;
@@ -36053,6 +36277,44 @@ var _DocxEditor = class _DocxEditor {
36053
36277
  emitDelete(blockId, runId, start, end) {
36054
36278
  if (end > start) this.host.onIntent?.({ kind: "deleteText", blockId, runId, start, end });
36055
36279
  }
36280
+ /** Backspace at the start of `pEl`: merge it into the previous paragraph. */
36281
+ mergeIntoPrevious(pEl) {
36282
+ const caret = this.caret;
36283
+ if (!caret) return;
36284
+ const prev = siblingParagraph(this.host.doc, pEl, -1);
36285
+ if (!prev) return;
36286
+ const junction = lastTextOf(prev);
36287
+ const mergeBlockId = this.host.onIntent && this.host.doc.stableIds ? this.host.doc.stableIds.idOf(pEl) : void 0;
36288
+ if (mergeParagraphBackward(this.host.doc, pEl)) {
36289
+ if (mergeBlockId !== void 0) this.host.onIntent?.({ kind: "mergeParagraph", blockId: mergeBlockId });
36290
+ const target = junction && paragraphOf(this.host.doc, junction) ? junction : caret.t;
36291
+ this.caret = { t: target, run: caret.run, offset: target === junction ? target.text.length : 0 };
36292
+ invalidateParagraphSignature(paragraphOf(this.host.doc, target) ?? pEl);
36293
+ this.commit(false, "local", true);
36294
+ }
36295
+ }
36296
+ /** Delete at the end of `pEl`: merge the next paragraph into it. */
36297
+ mergeNextInto(pEl) {
36298
+ const caret = this.caret;
36299
+ if (!caret) return;
36300
+ const next = siblingParagraph(this.host.doc, pEl, 1);
36301
+ if (!next) return;
36302
+ const mergeNextId = this.host.onIntent && this.host.doc.stableIds ? this.host.doc.stableIds.idOf(next) : void 0;
36303
+ if (mergeParagraphBackward(this.host.doc, next)) {
36304
+ if (mergeNextId !== void 0) this.host.onIntent?.({ kind: "mergeParagraph", blockId: mergeNextId });
36305
+ if (!paragraphOf(this.host.doc, caret.t)) {
36306
+ const t = firstTextOf(next);
36307
+ if (t) this.caret = { t, run: caret.run, offset: 0 };
36308
+ }
36309
+ invalidateParagraphSignature(paragraphOf(this.host.doc, this.caret?.t ?? caret.t) ?? pEl);
36310
+ this.commit(false, "local", true);
36311
+ }
36312
+ }
36313
+ /** True when `pEl`'s paragraph mark is a break this author suggested. */
36314
+ ownSuggestedBreak(pEl) {
36315
+ const mark = paragraphGlyphRevision(pEl, "ins");
36316
+ return !!mark && attr(mark, "author") === this.revisionAuthor;
36317
+ }
36056
36318
  /** Backspace/Delete in suggesting mode: mark one character (or a paragraph
36057
36319
  * mark at a boundary) as a tracked deletion instead of removing it. Skips over
36058
36320
  * text already struck by this author so a repeated press keeps eating real
@@ -36073,6 +36335,10 @@ var _DocxEditor = class _DocxEditor {
36073
36335
  if (this.suggestUnbulletAtCaret()) return;
36074
36336
  const prev = siblingParagraph(doc, pEl, -1);
36075
36337
  if (!prev) return;
36338
+ if (this.ownSuggestedBreak(prev)) {
36339
+ this.mergeIntoPrevious(pEl);
36340
+ return;
36341
+ }
36076
36342
  if (paragraphGlyphRevision(prev, "del")) {
36077
36343
  this.stepToNeighbor(-1);
36078
36344
  this.positionCaret();
@@ -36102,6 +36368,10 @@ var _DocxEditor = class _DocxEditor {
36102
36368
  if (caret.offset >= caret.t.text.length) {
36103
36369
  const pEl = paragraphOf(doc, caret.t);
36104
36370
  if (pEl && lastTextOf(pEl) === caret.t) {
36371
+ if (this.ownSuggestedBreak(pEl)) {
36372
+ this.mergeNextInto(pEl);
36373
+ return;
36374
+ }
36105
36375
  if (paragraphGlyphRevision(pEl, "del")) {
36106
36376
  this.stepToNeighbor(1);
36107
36377
  this.positionCaret();
@@ -36979,6 +37249,9 @@ function nearestInstance(list, near) {
36979
37249
  return best;
36980
37250
  }
36981
37251
 
37252
+ // ../core/src/edit/compare/index.ts
37253
+ var GAP_PAIR_CAP = 200 * 200;
37254
+
36982
37255
  // ../core/src/edit/comments.ts
36983
37256
  function el24(name, attrs = {}, children2 = [], text = "") {
36984
37257
  return { name, attrs, children: children2, text };
@@ -45418,4 +45691,4 @@ export {
45418
45691
  AgentDocument,
45419
45692
  collaborativeAgentTarget
45420
45693
  };
45421
- //# sourceMappingURL=chunk-RQ74KQML.js.map
45694
+ //# sourceMappingURL=chunk-QDZRRLHW.js.map