wordinweb 0.1.21 → 0.1.22

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.d.ts CHANGED
@@ -1281,6 +1281,10 @@ declare class DocxDocument {
1281
1281
  before: Paragraph;
1282
1282
  after: Paragraph;
1283
1283
  } | null;
1284
+ /** Insert a new paragraph immediately before a retained body paragraph
1285
+ * without rebuilding the complete document model. Used by Enter at the
1286
+ * exact paragraph start, where the existing paragraph itself is unchanged. */
1287
+ insertDirectBodyParagraphBefore(referenceSource: XmlElement, insertedSource: XmlElement): Paragraph | null;
1284
1288
  /** Reparse a body paragraph plus several new siblings created by
1285
1289
  * click-and-type without rebuilding the complete document model. */
1286
1290
  reparseDirectBodyParagraphSplits(beforeSource: XmlElement, afterSources: XmlElement[]): Paragraph[] | null;
package/dist/index.js CHANGED
@@ -6364,6 +6364,45 @@ var DocxDocument = class _DocxDocument {
6364
6364
  const parsed = this.reparseDirectBodyParagraphSplits(beforeSource, [afterSource]);
6365
6365
  return parsed ? { before: parsed[0], after: parsed[1] } : null;
6366
6366
  }
6367
+ /** Insert a new paragraph immediately before a retained body paragraph
6368
+ * without rebuilding the complete document model. Used by Enter at the
6369
+ * exact paragraph start, where the existing paragraph itself is unchanged. */
6370
+ insertDirectBodyParagraphBefore(referenceSource, insertedSource) {
6371
+ const parent = this.findParentOf(referenceSource);
6372
+ if (!parent) return null;
6373
+ const referenceIndex = parent.children.indexOf(referenceSource);
6374
+ if (referenceIndex < 1 || parent.children[referenceIndex - 1] !== insertedSource) return null;
6375
+ if (localName(referenceSource.name) !== "p" || localName(insertedSource.name) !== "p") return null;
6376
+ const findBlockList = (blocks) => {
6377
+ const index = blocks.findIndex((block) => block.type === "paragraph" && block.src === referenceSource);
6378
+ if (index >= 0) return { blocks, index };
6379
+ for (const block of blocks) {
6380
+ if (block.type !== "table") continue;
6381
+ for (const row of block.rows) {
6382
+ for (const cell of row.cells) {
6383
+ const found = findBlockList(cell.blocks);
6384
+ if (found) return found;
6385
+ }
6386
+ }
6387
+ }
6388
+ return null;
6389
+ };
6390
+ let location = null;
6391
+ for (const section of this.sections) {
6392
+ location = findBlockList(section.blocks);
6393
+ if (location) break;
6394
+ }
6395
+ if (!location || location.blocks.some((block) => block.src === insertedSource)) return null;
6396
+ const paragraph = parseParagraph(insertedSource, {
6397
+ ...this.ctxBase,
6398
+ rels: this.documentRels,
6399
+ readPart: (part) => this.readXmlOptional(part),
6400
+ independentTextboxStories: true
6401
+ });
6402
+ if (paragraph.revisionHidden || paragraph.sectionBreak) return null;
6403
+ location.blocks.splice(location.index, 0, paragraph);
6404
+ return paragraph;
6405
+ }
6367
6406
  /** Reparse a body paragraph plus several new siblings created by
6368
6407
  * click-and-type without rebuilding the complete document model. */
6369
6408
  reparseDirectBodyParagraphSplits(beforeSource, afterSources) {
@@ -6687,6 +6726,8 @@ var DocxDocument = class _DocxDocument {
6687
6726
  }
6688
6727
  markNumberingChanged() {
6689
6728
  this.numberingDirty = true;
6729
+ this.numbering = parseNumbering(this.numberingRoot ?? void 0, this.ctxBase);
6730
+ this._layoutGlobalSig = null;
6690
6731
  }
6691
6732
  /**
6692
6733
  * Retained footnotes tree. With create=true, a missing footnotes.xml part
@@ -11719,6 +11760,7 @@ var Engine = class {
11719
11760
  /** Per top-level block content signature, recorded during a captured layout
11720
11761
  * so the next incremental layout can find the first changed block. */
11721
11762
  this.incrSigs = null;
11763
+ this.incrLastNumberingUse = null;
11722
11764
  /** Reuse points captured at clean page tops: the block index that starts the
11723
11765
  * page and a snapshot of engine state to resume from. */
11724
11766
  this.incrPoints = null;
@@ -11871,7 +11913,9 @@ var Engine = class {
11871
11913
  }
11872
11914
  startRun() {
11873
11915
  if (this.incrEligible()) {
11874
- this.incrSigs = this.doc.sections[0].blocks.map((b) => this.blockSig(b));
11916
+ const blocks = this.doc.sections[0].blocks;
11917
+ this.incrSigs = blocks.map((b) => this.blockSig(b));
11918
+ this.incrLastNumberingUse = this.lastNumberingUse(blocks);
11875
11919
  this.incrPoints = [];
11876
11920
  }
11877
11921
  this.assignNoteNumbers();
@@ -11945,6 +11989,7 @@ var Engine = class {
11945
11989
  result._incr = {
11946
11990
  sigs: this.incrSigs,
11947
11991
  points: this.incrPoints,
11992
+ lastNumberingUse: this.incrLastNumberingUse,
11948
11993
  pages: this.pages,
11949
11994
  bookmarks: this.bookmarkPages,
11950
11995
  bookmarkPageIndices: this.bookmarkPageIndices,
@@ -12055,6 +12100,43 @@ var Engine = class {
12055
12100
  blockSig(block) {
12056
12101
  return block.src ? hashXml(block.src) : "\0nosrc";
12057
12102
  }
12103
+ blockNumberingKeys(block, keys) {
12104
+ if (block.type === "paragraph") {
12105
+ const numbering = this.doc.effectiveParaProps(block).numbering;
12106
+ const instance = numbering ? this.doc.numberingInstance(numbering.numId) : void 0;
12107
+ if (instance) keys.add(instance.abstractNumId);
12108
+ return;
12109
+ }
12110
+ for (const row of block.rows) {
12111
+ for (const cell of row.cells) {
12112
+ for (const nested of cell.blocks) this.blockNumberingKeys(nested, keys);
12113
+ }
12114
+ }
12115
+ }
12116
+ lastNumberingUse(blocks) {
12117
+ const lastUse = /* @__PURE__ */ new Map();
12118
+ for (let index = 0; index < blocks.length; index++) {
12119
+ const keys = /* @__PURE__ */ new Set();
12120
+ this.blockNumberingKeys(blocks[index], keys);
12121
+ for (const key of keys) lastUse.set(key, index);
12122
+ }
12123
+ return lastUse;
12124
+ }
12125
+ numberingCountersAffectLabels(abstractNumId) {
12126
+ const abstract = this.doc.numbering.abstract.get(abstractNumId);
12127
+ if (!abstract) return true;
12128
+ for (const level of abstract.levels.values()) {
12129
+ if (level.format !== "bullet" && level.format !== "none") return true;
12130
+ }
12131
+ for (const instance of this.doc.numbering.instances.values()) {
12132
+ if (instance.abstractNumId !== abstractNumId) continue;
12133
+ for (const override of instance.overrides.values()) {
12134
+ const format = override.level?.format;
12135
+ if (format && format !== "bullet" && format !== "none") return true;
12136
+ }
12137
+ }
12138
+ return false;
12139
+ }
12058
12140
  /** PAGEREF rewrite: replace stale cached field text with the bookmark's real
12059
12141
  * page. The right edge stays fixed so TOC right-tab page numbers keep align. */
12060
12142
  rewritePageRefs(pages) {
@@ -12122,7 +12204,7 @@ var Engine = class {
12122
12204
  if (this.incrPrevPoints && blockIdx > this.incrFirstDirty) {
12123
12205
  const pp = this.incrPrevPoints.get(blockIdx);
12124
12206
  const pageShift = pp ? globalPageIdx - pp.pageCount : 0;
12125
- if (pp && this.statesMatch(pp.state, pageShift)) {
12207
+ if (pp && this.statesMatch(pp.state, pageShift, blockIdx)) {
12126
12208
  __incrStats.convergedBlock = blockIdx;
12127
12209
  __incrStats.convergedPage = globalPageIdx;
12128
12210
  __incrStats.pageShift = pageShift;
@@ -12152,17 +12234,27 @@ var Engine = class {
12152
12234
  /** True when the current engine state at a clean page top exactly matches a
12153
12235
  * prev-layout capture point — i.e. the relaid tail has re-converged and the
12154
12236
  * rest of the layout is guaranteed identical. */
12155
- statesMatch(s, pageShift = 0) {
12237
+ statesMatch(s, pageShift = 0, blockIdx = 0) {
12156
12238
  const p = this.cur;
12157
12239
  if (this.col !== s.col || this.y !== s.y || this.sectionFirstPagePhys !== s.sectionFirstPagePhys || this.lastParaSpacingAfter !== s.lastParaSpacingAfter || this.lastParaAfterPad !== s.lastParaAfterPad || this.lastParaWasEmpty !== s.lastParaWasEmpty || this.trailingSectionBreakMarkGap !== s.trailingSectionBreakMarkGap || this.suppressNextSpaceBefore !== s.suppressNextSpaceBefore || this.docGridDropBefore !== s.docGridDropBefore || this.gridResyncPending !== s.gridResyncPending || this.verticalGridFlow !== s.verticalGridFlow || this.bannerSlotUsed !== s.bannerSlotUsed || p.physIndex !== s.page.physIndex + pageShift || p.displayNumber !== s.page.displayNumber + pageShift || p.bodyTop !== s.page.bodyTop || p.bodyBottom !== s.page.bodyBottom) {
12158
12240
  return false;
12159
12241
  }
12160
- if (this.seenNumIds.size !== s.seenNumIds.length) return false;
12161
- for (const id of s.seenNumIds) if (!this.seenNumIds.has(id)) return false;
12162
- if (this.counters.size !== s.counters.length) return false;
12163
- for (const [k, w] of s.counters) {
12164
- const v = this.counters.get(k);
12165
- if (!v || w.length !== v.length) return false;
12242
+ const numberingActive = (abstractNumId) => this.numberingCountersAffectLabels(abstractNumId) && (!this.incrLastNumberingUse || (this.incrLastNumberingUse.get(abstractNumId) ?? -1) >= blockIdx);
12243
+ const numIdActive = (numId) => {
12244
+ const instance = this.doc.numberingInstance(numId);
12245
+ return !instance || numberingActive(instance.abstractNumId);
12246
+ };
12247
+ const oldSeen = s.seenNumIds.filter(numIdActive);
12248
+ const newSeen = [...this.seenNumIds].filter(numIdActive);
12249
+ if (oldSeen.length !== newSeen.length) return false;
12250
+ for (const id of oldSeen) if (!this.seenNumIds.has(id)) return false;
12251
+ const oldCounters = new Map(s.counters);
12252
+ const counterKeys = /* @__PURE__ */ new Set([...oldCounters.keys(), ...this.counters.keys()]);
12253
+ for (const key of counterKeys) {
12254
+ if (!numberingActive(key)) continue;
12255
+ const w = oldCounters.get(key);
12256
+ const v = this.counters.get(key);
12257
+ if (!v || !w || w.length !== v.length) return false;
12166
12258
  for (let i = 0; i < v.length; i++) if (v[i] !== w[i]) return false;
12167
12259
  }
12168
12260
  return true;
@@ -12366,6 +12458,20 @@ var Engine = class {
12366
12458
  __incrStats.blocksHashed = blocks.length;
12367
12459
  __incrStats.firstDirty = firstDirty;
12368
12460
  }
12461
+ this.incrLastNumberingUse = new Map(
12462
+ [...inc.lastNumberingUse].map(([key, index]) => [
12463
+ key,
12464
+ index > this.incrBlockShiftAfter ? index + this.incrBlockDelta : index
12465
+ ])
12466
+ );
12467
+ const changedEnd = Math.min(blocks.length - 1, firstDirty + Math.max(0, this.incrBlockDelta));
12468
+ for (let index = firstDirty; index <= changedEnd; index++) {
12469
+ const keys = /* @__PURE__ */ new Set();
12470
+ this.blockNumberingKeys(blocks[index], keys);
12471
+ for (const key of keys) {
12472
+ this.incrLastNumberingUse.set(key, Math.max(index, this.incrLastNumberingUse.get(key) ?? -1));
12473
+ }
12474
+ }
12369
12475
  let rp;
12370
12476
  for (const pt of inc.points) {
12371
12477
  if (pt.blockIdx <= firstDirty) rp = pt;
@@ -12504,6 +12610,7 @@ var Engine = class {
12504
12610
  _incr: {
12505
12611
  sigs: newSigs,
12506
12612
  points,
12613
+ lastNumberingUse: this.incrLastNumberingUse,
12507
12614
  pages: outInternal,
12508
12615
  bookmarks: mergedBookmarks,
12509
12616
  bookmarkPageIndices: mergedBookmarkPageIndices,
@@ -20957,13 +21064,7 @@ function listTypeAt(doc, target) {
20957
21064
  const lvl = doc.numberingLevel(id, parseInt(attr(ilvl ?? { name: "", attrs: {}, children: [], text: "" }, "val") ?? "0", 10) || 0);
20958
21065
  return lvl?.format === "bullet" ? "bullet" : "number";
20959
21066
  }
20960
- function ensureListNum(doc, kind) {
20961
- for (const [numId2, inst] of doc.numbering.instances) {
20962
- const lvl = doc.numberingLevel(numId2, 0);
20963
- if (!lvl) continue;
20964
- if (kind === "bullet" ? lvl.format === "bullet" : lvl.format === "decimal") return numId2;
20965
- void inst;
20966
- }
21067
+ function createListNum(doc, kind) {
20967
21068
  const root = doc.numberingTree(true);
20968
21069
  if (!root) return null;
20969
21070
  const w = prefixOf6(root);
@@ -21037,7 +21138,7 @@ function setListType(doc, targets, kind) {
21037
21138
  if (p) paragraphs.add(p);
21038
21139
  }
21039
21140
  if (paragraphs.size === 0) return false;
21040
- const numId = kind === null ? null : ensureListNum(doc, kind);
21141
+ const numId = kind === null ? null : createListNum(doc, kind);
21041
21142
  if (kind !== null && numId === null) return false;
21042
21143
  let touched = false;
21043
21144
  for (const pEl of paragraphs) {
@@ -21067,7 +21168,17 @@ function setListType(doc, targets, kind) {
21067
21168
  }
21068
21169
  touched = true;
21069
21170
  }
21070
- if (touched) doc.refresh();
21171
+ if (touched) {
21172
+ let reparsed = true;
21173
+ for (const paragraph of paragraphs) {
21174
+ if (!doc.reparseBodyParagraph(paragraph)) {
21175
+ reparsed = false;
21176
+ break;
21177
+ }
21178
+ invalidateParagraphSignature(paragraph);
21179
+ }
21180
+ if (!reparsed) doc.refresh();
21181
+ }
21071
21182
  return touched;
21072
21183
  }
21073
21184
 
@@ -28554,11 +28665,39 @@ var _DocxEditor = class _DocxEditor {
28554
28665
  if (this.caret) {
28555
28666
  const paragraph = paragraphOf(this.host.doc, this.caret.t);
28556
28667
  if (paragraph && textElements(paragraph).every((text) => text.text.length === 0) && clearListParagraphFormatting(this.host.doc, paragraph)) {
28557
- this.commit();
28668
+ const reparsed2 = this.host.doc.reparseBodyParagraph(paragraph);
28669
+ if (reparsed2) {
28670
+ for (const child4 of reparsed2.children) {
28671
+ const runs = child4.type === "run" ? [child4] : child4.runs;
28672
+ const run = runs.find(
28673
+ (candidate) => candidate.content.some((content) => content.kind === "text" && content.srcT === this.caret.t)
28674
+ );
28675
+ if (run) {
28676
+ this.caret.run = run;
28677
+ break;
28678
+ }
28679
+ }
28680
+ invalidateParagraphSignature(paragraph);
28681
+ }
28682
+ this.commit(false, "local", !!reparsed2);
28558
28683
  this.focusText();
28559
28684
  return;
28560
28685
  }
28561
28686
  }
28687
+ const insertedBefore = this.insertBlankParagraphBeforeAtStart();
28688
+ if (insertedBefore) {
28689
+ const reparsed2 = this.host.doc.insertDirectBodyParagraphBefore(insertedBefore.reference, insertedBefore.blank);
28690
+ if (reparsed2) {
28691
+ const run = reparsed2.children.flatMap((child4) => child4.type === "run" ? [child4] : child4.runs).find(
28692
+ (candidate) => candidate.content.some((content) => content.kind === "text" && content.srcT === insertedBefore.text)
28693
+ );
28694
+ if (run) this.caret = { t: insertedBefore.text, run, offset: 0 };
28695
+ invalidateParagraphSignature(insertedBefore.blank);
28696
+ }
28697
+ this.commit(false, "local", !!reparsed2);
28698
+ this.focusText();
28699
+ return;
28700
+ }
28562
28701
  const split = this.splitParagraphCore();
28563
28702
  const reparsed = split ? this.host.doc.reparseDirectBodyParagraphSplit(split.before, split.after) : null;
28564
28703
  if (reparsed && split && this.caret) {
@@ -28578,6 +28717,49 @@ var _DocxEditor = class _DocxEditor {
28578
28717
  this.commit(false, "local", !!reparsed);
28579
28718
  this.focusText();
28580
28719
  }
28720
+ /** Enter at the exact paragraph start only needs a blank paragraph before
28721
+ * the existing content. Retaining the original paragraph keeps its parsed
28722
+ * model, fields, and bookmarks valid for incremental pagination. */
28723
+ insertBlankParagraphBeforeAtStart() {
28724
+ const caret = this.caret;
28725
+ if (!caret || caret.offset !== 0) return null;
28726
+ const rEl = this.host.doc.findParentOf(caret.t);
28727
+ if (!rEl || localName(rEl.name) !== "r") return null;
28728
+ let pEl = this.host.doc.findParentOf(rEl);
28729
+ while (pEl && localName(pEl.name) !== "p") pEl = this.host.doc.findParentOf(pEl);
28730
+ if (!pEl) return null;
28731
+ const texts = textElements(pEl);
28732
+ const textIndex = texts.indexOf(caret.t);
28733
+ if (textIndex < 0 || texts.slice(0, textIndex).some((text) => text.text.length > 0)) return null;
28734
+ const parent = this.host.doc.findParentOf(pEl);
28735
+ if (!parent) return null;
28736
+ const prefix = pEl.name.includes(":") ? pEl.name.slice(0, pEl.name.indexOf(":") + 1) : "";
28737
+ const pPr = pEl.children.find((child4) => localName(child4.name) === "pPr");
28738
+ const blankPPr = pPr ? cloneXml(pPr) : void 0;
28739
+ if (blankPPr) blankPPr.children = blankPPr.children.filter((child4) => localName(child4.name) !== "sectPr");
28740
+ const rPr = rEl.children.find((child4) => localName(child4.name) === "rPr");
28741
+ const blankText = {
28742
+ name: caret.t.name,
28743
+ attrs: { ...caret.t.attrs, "xml:space": "preserve" },
28744
+ text: "",
28745
+ children: []
28746
+ };
28747
+ const blankRun = {
28748
+ name: rEl.name,
28749
+ attrs: { ...rEl.attrs },
28750
+ text: "",
28751
+ children: [...rPr ? [cloneXml(rPr)] : [], blankText]
28752
+ };
28753
+ const blank = {
28754
+ name: prefix + "p",
28755
+ attrs: {},
28756
+ text: "",
28757
+ children: [...blankPPr ? [blankPPr] : [], blankRun]
28758
+ };
28759
+ parent.children.splice(parent.children.indexOf(pEl), 0, blank);
28760
+ if (this.suggesting) markParagraphGlyph(blank, "ins", this.revMeta());
28761
+ return { blank, reference: pEl, text: blankText };
28762
+ }
28581
28763
  /** Split the paragraph at the caret without history or commit — shared by
28582
28764
  * Enter and multi-line paste. */
28583
28765
  splitParagraphCore() {
@@ -35374,9 +35556,10 @@ function DocxView({
35374
35556
  const targets = segs.length > 0 ? segs.map((sg) => sg.t).filter((t) => !!t) : caret ? [caret.t] : [];
35375
35557
  if (targets.length === 0) return;
35376
35558
  const current = listTypeAt(doc, targets[0]);
35559
+ const dirtyBlock = targets.length === 1 ? topLevelBlockOf(doc, targets[0]) ?? void 0 : void 0;
35377
35560
  history.checkpoint();
35378
35561
  if (setListType(doc, targets, current === kind ? null : kind)) {
35379
- pages = rerender(doc);
35562
+ pages = rerender(doc, dirtyBlock);
35380
35563
  document.dispatchEvent(new CustomEvent("dxw-selection"));
35381
35564
  }
35382
35565
  },