@superdoc/mcp 1.10.0-next.5 → 1.10.0-next.6

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.
Files changed (2) hide show
  1. package/dist/index.js +210 -95
  2. package/package.json +4 -4
package/dist/index.js CHANGED
@@ -31630,7 +31630,7 @@ var init_host = __esm(() => {
31630
31630
  });
31631
31631
 
31632
31632
  // ../../packages/sdk/langs/node/src/runtime/sdk-version.generated.ts
31633
- var SDK_VERSION = "2.23.0-next.5";
31633
+ var SDK_VERSION = "2.23.0-next.6";
31634
31634
 
31635
31635
  // ../../packages/sdk/langs/node/src/runtime/embedded-platform.ts
31636
31636
  import { accessSync, chmodSync, constants, existsSync, statSync } from "node:fs";
@@ -384950,7 +384950,7 @@ function bytesProveNoContentControlCarrier(bytes) {
384950
384950
  }
384951
384951
  return true;
384952
384952
  }
384953
- function listContentControlPartSources(context) {
384953
+ function listContentControlPartSources(context, rangeCache) {
384954
384954
  const body = context.resolveFullStoryLocator({ kind: "story", storyType: "body" });
384955
384955
  if ("error" in body)
384956
384956
  throw operationError("TARGET_NOT_FOUND", "body story is not available.");
@@ -384970,8 +384970,83 @@ function listContentControlPartSources(context) {
384970
384970
  story
384971
384971
  });
384972
384972
  }
384973
+ const textboxes = listSDDocumentSessionInternalStories(context.session).filter((textbox) => textbox.storyKind === "textbox" && textbox.textboxId && textbox.storyByteRange);
384974
+ const registry3 = getSDDocumentSessionInternalTextboxRegistry(context.session);
384975
+ const progress = getSDDocumentSessionInternalTextboxRegistryProgressFingerprint(context.session);
384976
+ for (const partUri of rangeCache.keys())
384977
+ if (!seen.has(partUri))
384978
+ rangeCache.delete(partUri);
384979
+ for (const owner of [...sources]) {
384980
+ const owned = textboxes.filter((textbox) => textbox.partUri === owner.partUri);
384981
+ const bytes = owned.length ? readPartBytes2(context, owner.partUri) : null;
384982
+ if (!bytes) {
384983
+ rangeCache.delete(owner.partUri);
384984
+ continue;
384985
+ }
384986
+ const inactive = owned.flatMap((textbox) => {
384987
+ const [start, end] = textbox.storyByteRange;
384988
+ return (registry3.byTextboxId.get(textbox.textboxId)?.alternateContentCounterparts ?? []).filter((range) => !(range[0] <= start && range[1] >= end));
384989
+ });
384990
+ const boundaries = [...new Set([
384991
+ ...owned.flatMap((textbox) => [...textbox.storyByteRange]),
384992
+ ...inactive.flatMap((range) => [...range])
384993
+ ])].sort((left, right) => left - right);
384994
+ let cached2 = rangeCache.get(owner.partUri);
384995
+ if (!cached2 || cached2.bytes !== bytes || cached2.registry !== registry3 || cached2.progress !== progress || cached2.boundaries.length !== boundaries.length || cached2.boundaries.some((boundary, index2) => boundary !== boundaries[index2])) {
384996
+ const offsets2 = new Map;
384997
+ let byteOffset = 0;
384998
+ let charOffset = 0;
384999
+ for (const boundary of boundaries) {
385000
+ charOffset += decodeXml(bytes.subarray(byteOffset, boundary)).length;
385001
+ offsets2.set(boundary, charOffset);
385002
+ byteOffset = boundary;
385003
+ }
385004
+ cached2 = { bytes, registry: registry3, progress, boundaries, offsets: offsets2 };
385005
+ rangeCache.set(owner.partUri, cached2);
385006
+ }
385007
+ const { offsets } = cached2;
385008
+ const characterRange = ([start, end]) => [offsets.get(start), offsets.get(end)];
385009
+ if (inactive.length)
385010
+ owner.excludedXmlRanges = inactive.map(characterRange);
385011
+ for (const textbox of owned) {
385012
+ sources.push({
385013
+ partUri: textbox.partUri,
385014
+ storyId: textbox.storyId,
385015
+ story: { kind: "story", storyType: "textbox", textboxId: textbox.textboxId },
385016
+ xmlRange: characterRange(textbox.storyByteRange)
385017
+ });
385018
+ }
385019
+ }
384973
385020
  return sources;
384974
385021
  }
385022
+ function partitionContentControlScan(xml, sources) {
385023
+ const hiddenControls = collectHiddenNativeContentControlStarts(xml);
385024
+ const trackedWrapperRanges = buildTrackedWrapperRanges(xml);
385025
+ const scans = new Map(sources.map((source) => [source, { ranges: [], hiddenControls, trackedWrapperRanges }]));
385026
+ const owner = sources.find((source) => !source.xmlRange);
385027
+ const textboxes = sources.filter((source) => source.xmlRange).sort((left, right) => left.xmlRange[0] - right.xmlRange[0]);
385028
+ const inactive = [...owner.excludedXmlRanges ?? []].sort((left, right) => left[0] - right[0]);
385029
+ const containing = (items, rangeOf, start, end) => {
385030
+ let low = 0;
385031
+ let high = items.length;
385032
+ while (low < high) {
385033
+ const middle = low + high >>> 1;
385034
+ if (rangeOf(items[middle])[0] <= start)
385035
+ low = middle + 1;
385036
+ else
385037
+ high = middle;
385038
+ }
385039
+ const candidate2 = items[low - 1];
385040
+ return candidate2 !== undefined && rangeOf(candidate2)[1] >= end ? candidate2 : undefined;
385041
+ };
385042
+ for (const range of collectContentControlRanges(xml)) {
385043
+ const textbox = containing(textboxes, (source) => source.xmlRange, range.start, range.end);
385044
+ if (!textbox && containing(inactive, (range2) => range2, range.start, range.end))
385045
+ continue;
385046
+ scans.get(textbox ?? owner).ranges.push(range);
385047
+ }
385048
+ return scans;
385049
+ }
384975
385050
 
384976
385051
  class V2ContentControlsAdapter {
384977
385052
  constructor(context) {
@@ -385062,17 +385137,23 @@ class V2ContentControlsAdapter {
385062
385137
  ungroup: (input, options) => this.unwrap(input, options)
385063
385138
  };
385064
385139
  this.loadedCache = null;
385140
+ this.textboxCharacterRanges = new Map;
385065
385141
  this.readCatalog = null;
385066
385142
  this.partsWithoutControlCarriers = new WeakSet;
385067
385143
  }
385144
+ hasCurrentLoadedCache(revision) {
385145
+ return this.loadedCache?.revision === revision && this.loadedCache.registry === getSDDocumentSessionInternalTextboxRegistry(this.context.session) && this.loadedCache.registryProgress === getSDDocumentSessionInternalTextboxRegistryProgressFingerprint(this.context.session);
385146
+ }
385068
385147
  currentReadCatalog() {
385069
385148
  const cached2 = this.readCatalog;
385070
385149
  const session = this.context.session;
385071
385150
  if (!cached2 || getSDDocumentSessionInternalSourceCompletePosture(session) !== "source-complete" || cached2.identity !== getSDDocumentSessionInternalResidentMainControlInventoryIdentity(session))
385072
385151
  return null;
385073
- const sources = listContentControlPartSources(this.context);
385074
- if (JSON.stringify(sources) !== JSON.stringify(cached2.sources) || sources.some((source, i2) => i2 > 0 && getSDDocumentSessionInternalPartBytes(session, source.partUri) !== cached2.secondaryBytes[i2]))
385152
+ const sources = listContentControlPartSources(this.context, this.textboxCharacterRanges);
385153
+ if (JSON.stringify(sources) !== JSON.stringify(cached2.sources) || sources.some((source, i2) => i2 > 0 && getSDDocumentSessionInternalPartBytes(session, source.partUri) !== cached2.secondaryBytes[i2])) {
385154
+ this.loadedCache = null;
385075
385155
  return null;
385156
+ }
385076
385157
  if (this.loadedCache?.revision !== this.context.currentPublicRevision())
385077
385158
  this.loadedCache = null;
385078
385159
  return cached2;
@@ -385084,7 +385165,7 @@ class V2ContentControlsAdapter {
385084
385165
  this.readCatalog = null;
385085
385166
  return;
385086
385167
  }
385087
- const sources = listContentControlPartSources(this.context);
385168
+ const sources = listContentControlPartSources(this.context, this.textboxCharacterRanges);
385088
385169
  this.readCatalog = {
385089
385170
  identity,
385090
385171
  sources,
@@ -385113,7 +385194,7 @@ class V2ContentControlsAdapter {
385113
385194
  const relationships = listSDDocumentSessionInternalHeaderFooterRels(session);
385114
385195
  if (bodyDescriptor.relationships.status === "missing" && relationships.length > 0)
385115
385196
  return false;
385116
- const sources = listContentControlPartSources(this.context);
385197
+ const sources = listContentControlPartSources(this.context, this.textboxCharacterRanges);
385117
385198
  if (relationships.some((rel) => !sources.some((source) => source.story && source.partUri === rel.partUri))) {
385118
385199
  return false;
385119
385200
  }
@@ -385165,8 +385246,17 @@ class V2ContentControlsAdapter {
385165
385246
  throw error48;
385166
385247
  return { success: false, failure: { code: error48.code, message: error48.message } };
385167
385248
  }
385249
+ if (part.story?.storyType === "textbox") {
385250
+ const textbox = getSDDocumentSessionInternalTextboxRegistry(this.context.session).byTextboxId.get(part.story.textboxId);
385251
+ if (textbox && textbox.alternateContentCounterparts.length > 1) {
385252
+ return { success: false, failure: {
385253
+ code: "CAPABILITY_UNSUPPORTED",
385254
+ message: "Content-control creation in mirrored AlternateContent textbox stories is unsupported; no changes were made."
385255
+ } };
385256
+ }
385257
+ }
385168
385258
  if (structuralReplacementXml !== undefined && input.at?.start.kind === "text") {
385169
- const anchor = findParagraphById3(part.xml, input.at.start.blockId);
385259
+ const anchor = findParagraphById3(part.xml, input.at.start.blockId, part.xmlRange);
385170
385260
  const ranges = collectXmlElementRanges(part.xml).ranges;
385171
385261
  const paragraph4 = anchor && ranges.find((range) => range.start === anchor.start && range.end === anchor.end);
385172
385262
  const parent = paragraph4 && ranges.filter((range) => range.start < paragraph4.start && range.end > paragraph4.end).sort((left, right) => left.end - left.start - (right.end - right.start))[0];
@@ -385236,7 +385326,7 @@ class V2ContentControlsAdapter {
385236
385326
  if (!trackedStamp) {
385237
385327
  return { success: false, failure: { code: "PRECONDITION_FAILED", message: "no-author-configured" } };
385238
385328
  }
385239
- const preview = wrapSelectionInXml(this.context, part.xml, input.at, wrapTrackedStructuralXml({ innerXml: baseSdtXml, kind: "ins", stamp: trackedStamp }), input.kind, input.content ?? "", input.content !== undefined);
385329
+ const preview = wrapSelectionInXml(this.context, part.xml, input.at, wrapTrackedStructuralXml({ innerXml: baseSdtXml, kind: "ins", stamp: trackedStamp }), input.kind, input.content ?? "", input.content !== undefined, undefined, false, part.xmlRange);
385240
385330
  if (!preview.ok)
385241
385331
  return { success: false, failure: preview.failure };
385242
385332
  if (!dryRun) {
@@ -385257,7 +385347,7 @@ class V2ContentControlsAdapter {
385257
385347
  }
385258
385348
  }
385259
385349
  if (input.at) {
385260
- const anchor = input.at.start.kind === "text" ? findParagraphById3(part.xml, input.at.start.blockId) : null;
385350
+ const anchor = input.at.start.kind === "text" ? findParagraphById3(part.xml, input.at.start.blockId, part.xmlRange) : null;
385261
385351
  const hasPendingRevisions = anchor && /<(?:[\w.-]+:)?(?:ins|del|moveFrom|moveTo)\b/.test(anchor.xml);
385262
385352
  const pendingRevisionFailure = {
385263
385353
  success: false,
@@ -385268,7 +385358,7 @@ class V2ContentControlsAdapter {
385268
385358
  };
385269
385359
  if (hasPendingRevisions && !supportsTrackedInlineTextCreate(input))
385270
385360
  return pendingRevisionFailure;
385271
- const preview = wrapSelectionInXml(this.context, part.xml, input.at, baseSdtXml, "inline", input.content ?? "", input.content !== undefined);
385361
+ const preview = wrapSelectionInXml(this.context, part.xml, input.at, baseSdtXml, "inline", input.content ?? "", input.content !== undefined, undefined, false, part.xmlRange);
385272
385362
  if (!preview.ok)
385273
385363
  return { success: false, failure: preview.failure };
385274
385364
  if (hasPendingRevisions) {
@@ -385280,10 +385370,10 @@ class V2ContentControlsAdapter {
385280
385370
  let beforeXml = "";
385281
385371
  let namespaceBindings = new Map(Object.entries(collectXmlElementRanges(part.xml).ranges[0]?.namespaceBindings ?? {}));
385282
385372
  if (input.at?.start.kind === "text") {
385283
- const anchor = findParagraphById3(part.xml, input.at.start.blockId);
385373
+ const anchor = findParagraphById3(part.xml, input.at.start.blockId, part.xmlRange);
385284
385374
  if (anchor) {
385285
385375
  namespaceBindings = new Map(contentControlNamespaceBindings(part.xml, anchor.start, true));
385286
- if (input.kind === "block" && extractVisibleTextFromXml(anchor.xml).trim().length === 0) {
385376
+ if (input.kind === "block" && isReplaceableEmptyParagraph(anchor.xml)) {
385287
385377
  beforeXml = anchor.xml;
385288
385378
  }
385289
385379
  }
@@ -385322,7 +385412,7 @@ class V2ContentControlsAdapter {
385322
385412
  if (wholeBlocks.kind === "failure") {
385323
385413
  return { success: false, failure: { code: wholeBlocks.code, message: wholeBlocks.message } };
385324
385414
  }
385325
- const anchor = at.start.kind === "text" ? findParagraphById3(part.xml, at.start.blockId) : null;
385415
+ const anchor = at.start.kind === "text" ? findParagraphById3(part.xml, at.start.blockId, part.xmlRange) : null;
385326
385416
  if (wholeBlocks.kind === "ok") {
385327
385417
  const selectedXml = part.xml.slice(wholeBlocks.start, wholeBlocks.end);
385328
385418
  const wrapped = replaceSdtContentInTemplate(sdtXml, buildInnerXml(selectedXml));
@@ -385354,8 +385444,8 @@ class V2ContentControlsAdapter {
385354
385444
  })();
385355
385445
  let left = split.slice(0, splitAt) + "</w:p>";
385356
385446
  let right = anchor.xml.slice(0, openEnd) + properties2 + split.slice(splitAt + splitMarker.length);
385357
- const keepLeft = parseParagraphInlineMap(left).visibleLength > 0;
385358
- const keepRight = parseParagraphInlineMap(right).visibleLength > 0;
385447
+ const keepLeft = parseParagraphInlineMap(left).visibleLength > 0 || hasParagraphObjects(left);
385448
+ const keepRight = parseParagraphInlineMap(right).visibleLength > 0 || hasParagraphObjects(right);
385359
385449
  const allocateNextSplitId = createParaIdAllocator(this.context);
385360
385450
  const reservedReplacementIds = new Set(structuralReplacementXml === undefined ? [] : Array.from(structuralReplacementXml.matchAll(/\bw14:paraId="([^"]+)"/g), (match) => match[1]));
385361
385451
  const allocateSplitId = () => {
@@ -385406,7 +385496,7 @@ class V2ContentControlsAdapter {
385406
385496
  targetBlockIds2 = [at.start.blockId];
385407
385497
  }
385408
385498
  } else {
385409
- const wrapped = wrapSelectionInXml(this.context, part.xml, input.at, sdtXml, input.kind, input.content ?? "", input.content !== undefined, repeatingSectionItemId ? (text4) => buildInnerXml(buildContentXml(text4, input.kind, createParaIdAllocator(this.context))) : undefined, !tracked && !part.story && input.html === undefined && input.json === undefined && structuralReplacementXml === undefined);
385499
+ const wrapped = wrapSelectionInXml(this.context, part.xml, input.at, sdtXml, input.kind, input.content ?? "", input.content !== undefined, repeatingSectionItemId ? (text4) => buildInnerXml(buildContentXml(text4, input.kind, createParaIdAllocator(this.context))) : undefined, !tracked && !part.story && input.html === undefined && input.json === undefined && structuralReplacementXml === undefined, part.xmlRange);
385410
385500
  if (!wrapped.ok) {
385411
385501
  return { success: false, failure: wrapped.failure };
385412
385502
  }
@@ -385436,7 +385526,7 @@ class V2ContentControlsAdapter {
385436
385526
  }))
385437
385527
  };
385438
385528
  }
385439
- nextXml = insertAtDocumentEnd(part.xml, wrapped);
385529
+ nextXml = insertAtDocumentEnd(part.xml, wrapped, part.xmlRange);
385440
385530
  }
385441
385531
  if (dryRun)
385442
385532
  return this.validateDryRun(input, options, target);
@@ -386219,7 +386309,7 @@ class V2ContentControlsAdapter {
386219
386309
  if (at.start.kind !== "text" || at.end.kind !== "text" || at.start.blockId !== at.end.blockId) {
386220
386310
  throw operationError("CAPABILITY_UNAVAILABLE", "Content-control creation requires a selection within one paragraph.");
386221
386311
  }
386222
- const paragraph4 = findParagraphById3(part.xml, at.start.blockId);
386312
+ const paragraph4 = findParagraphById3(part.xml, at.start.blockId, part.xmlRange);
386223
386313
  if (!paragraph4)
386224
386314
  throw operationError("TARGET_NOT_FOUND", `Paragraph ${at.start.blockId} was not found in the target story.`);
386225
386315
  if (![at.start.offset, at.end.offset].every((offset) => Number.isInteger(offset) && offset >= 0 && offset <= parseParagraphInlineMap(paragraph4.xml).visibleLength) || at.start.offset > at.end.offset) {
@@ -386742,22 +386832,30 @@ class V2ContentControlsAdapter {
386742
386832
  }
386743
386833
  load() {
386744
386834
  const revision = this.context.currentPublicRevision();
386745
- if (this.loadedCache?.revision === revision)
386835
+ if (this.hasCurrentLoadedCache(revision))
386746
386836
  return this.loadedCache.value;
386747
386837
  const documentXml = readPartXml(this.context, DOCUMENT_PART_URI5);
386748
386838
  if (!documentXml)
386749
386839
  throw operationError("PRECONDITION_FAILED", "document.xml bytes are unavailable.");
386750
386840
  const parts = [];
386751
- for (const source of listContentControlPartSources(this.context)) {
386752
- const xml = source.partUri === DOCUMENT_PART_URI5 ? documentXml : readPartXml(this.context, source.partUri);
386841
+ const sources = listContentControlPartSources(this.context, this.textboxCharacterRanges);
386842
+ const captures = new Map;
386843
+ for (const partUri of new Set(sources.map((source) => source.partUri))) {
386844
+ const xml = partUri === DOCUMENT_PART_URI5 ? documentXml : readPartXml(this.context, partUri);
386753
386845
  if (!xml)
386754
386846
  continue;
386755
- const paragraphs2 = parseParagraphWindows(xml);
386847
+ captures.set(partUri, { xml, scans: partitionContentControlScan(xml, sources.filter((source) => source.partUri === partUri)) });
386848
+ }
386849
+ for (const source of sources) {
386850
+ const capture = captures.get(source.partUri);
386851
+ if (!capture)
386852
+ continue;
386853
+ const paragraphs2 = parseParagraphWindows(capture.xml, source.xmlRange);
386756
386854
  parts.push({
386757
386855
  ...source,
386758
- xml,
386856
+ xml: capture.xml,
386759
386857
  paragraphs: paragraphs2,
386760
- controls: parseContentControls(xml, paragraphs2, source.storyId, source.story)
386858
+ controls: parseContentControls(capture.xml, paragraphs2, source.storyId, source.story, capture.scans.get(source))
386761
386859
  });
386762
386860
  }
386763
386861
  const body = parts.find((part) => part.partUri === DOCUMENT_PART_URI5);
@@ -386767,7 +386865,12 @@ class V2ContentControlsAdapter {
386767
386865
  controls: parts.flatMap((part) => part.controls),
386768
386866
  parts
386769
386867
  };
386770
- this.loadedCache = { revision, value: value3 };
386868
+ this.loadedCache = {
386869
+ revision,
386870
+ value: value3,
386871
+ registry: getSDDocumentSessionInternalTextboxRegistry(this.context.session),
386872
+ registryProgress: getSDDocumentSessionInternalTextboxRegistryProgressFingerprint(this.context.session)
386873
+ };
386771
386874
  this.captureReadCatalog(value3);
386772
386875
  return value3;
386773
386876
  }
@@ -386776,29 +386879,45 @@ class V2ContentControlsAdapter {
386776
386879
  return;
386777
386880
  for (let attempt = 0;attempt <= maxRestarts; attempt += 1) {
386778
386881
  const revision = this.context.currentPublicRevision();
386779
- if (this.loadedCache?.revision === revision)
386882
+ if (this.hasCurrentLoadedCache(revision))
386780
386883
  return;
386884
+ const registry3 = getSDDocumentSessionInternalTextboxRegistry(this.context.session);
386885
+ const registryProgress = getSDDocumentSessionInternalTextboxRegistryProgressFingerprint(this.context.session);
386886
+ const captureIsCurrent = () => this.context.currentPublicRevision() === revision && getSDDocumentSessionInternalTextboxRegistry(this.context.session) === registry3 && getSDDocumentSessionInternalTextboxRegistryProgressFingerprint(this.context.session) === registryProgress;
386781
386887
  const documentXml = readPartXml(this.context, DOCUMENT_PART_URI5);
386782
386888
  if (!documentXml)
386783
386889
  return;
386784
- const captures = listContentControlPartSources(this.context).flatMap((source) => {
386785
- const xml = source.partUri === DOCUMENT_PART_URI5 ? documentXml : readPartXml(this.context, source.partUri);
386786
- return xml ? [{ source, xml }] : [];
386890
+ const sources = listContentControlPartSources(this.context, this.textboxCharacterRanges);
386891
+ const physicalParts = new Map;
386892
+ for (const partUri of new Set(sources.map((source) => source.partUri))) {
386893
+ const xml = partUri === DOCUMENT_PART_URI5 ? documentXml : readPartXml(this.context, partUri);
386894
+ if (!xml)
386895
+ continue;
386896
+ physicalParts.set(partUri, { xml, scans: partitionContentControlScan(xml, sources.filter((source) => source.partUri === partUri)) });
386897
+ }
386898
+ const captures = sources.flatMap((source) => {
386899
+ const part = physicalParts.get(source.partUri);
386900
+ return part ? [{ source, xml: part.xml, scan: part.scans.get(source) }] : [];
386787
386901
  });
386788
386902
  const parts = [];
386789
386903
  let preempted = false;
386790
386904
  for (const capture of captures) {
386791
- const paragraphs2 = await parseParagraphWindowsCooperatively(capture.xml, yieldPoint);
386792
- if (this.context.currentPublicRevision() !== revision) {
386905
+ const paragraphs2 = await parseParagraphWindowsCooperatively(capture.xml, yieldPoint, capture.source.xmlRange);
386906
+ if (!captureIsCurrent()) {
386793
386907
  preempted = true;
386794
386908
  break;
386795
386909
  }
386796
- const controls = await parseContentControlsCooperatively(capture.xml, paragraphs2, capture.source.storyId, capture.source.story, yieldPoint);
386797
- if (this.context.currentPublicRevision() !== revision) {
386910
+ const controls = await parseContentControlsCooperatively(capture.xml, paragraphs2, capture.source.storyId, capture.source.story, yieldPoint, capture.scan);
386911
+ if (!captureIsCurrent()) {
386798
386912
  preempted = true;
386799
386913
  break;
386800
386914
  }
386801
- parts.push({ ...capture.source, xml: capture.xml, paragraphs: paragraphs2, controls });
386915
+ parts.push({
386916
+ ...capture.source,
386917
+ xml: capture.xml,
386918
+ paragraphs: paragraphs2,
386919
+ controls
386920
+ });
386802
386921
  }
386803
386922
  if (preempted)
386804
386923
  continue;
@@ -386811,7 +386930,7 @@ class V2ContentControlsAdapter {
386811
386930
  controls: parts.flatMap((part) => part.controls),
386812
386931
  parts
386813
386932
  };
386814
- this.loadedCache = { revision, value: value3 };
386933
+ this.loadedCache = { revision, value: value3, registry: registry3, registryProgress };
386815
386934
  this.captureReadCatalog(value3);
386816
386935
  return;
386817
386936
  }
@@ -386880,7 +386999,7 @@ class V2ContentControlsAdapter {
386880
386999
  semanticStyleDependencies: commitOptions.semanticStyleDependencies,
386881
387000
  storySurface: commitOptions.storySurface,
386882
387001
  signalLanesToRebuild: commitOptions.signalLanesToRebuild,
386883
- storyIds: commitOptions.storySurface?.contentControlRewrite ? this.load().parts.filter((part) => part.partUri === partUri).map((part) => part.storyId) : undefined,
387002
+ storyIds: commitOptions.storySurface?.contentControlRewrite ? this.load().parts.filter((part) => part.partUri === partUri && !part.xmlRange).map((part) => part.storyId) : undefined,
386884
387003
  createdRefs: commitOptions.createdRefs
386885
387004
  });
386886
387005
  if (!committed.ok)
@@ -386913,53 +387032,42 @@ function contentControlTextBlockIds(info) {
386913
387032
  return [];
386914
387033
  return [...new Set([target.start.blockId, target.end.blockId])];
386915
387034
  }
386916
- function parseParagraphWindows(documentXml) {
386917
- const paragraphs2 = [];
386918
- const paragraphRe = /<w:p\b[^>]*?(?:\/>|>[\s\S]*?<\/w:p>)/g;
386919
- for (let match = paragraphRe.exec(documentXml);match; match = paragraphRe.exec(documentXml)) {
386920
- const xml = match[0];
386921
- const blockId = /w14:paraId="([0-9A-Fa-f]{8})"/.exec(xml)?.[1];
387035
+ function* iterateParagraphWindows(documentXml, xmlRange) {
387036
+ const paragraphStart = /<w:p(?=[\s/>])/g;
387037
+ paragraphStart.lastIndex = xmlRange?.[0] ?? 0;
387038
+ for (let match = paragraphStart.exec(documentXml);match && match.index < (xmlRange?.[1] ?? documentXml.length); match = paragraphStart.exec(documentXml)) {
387039
+ const node4 = readXmlNodeAt(documentXml, match.index);
387040
+ if (!node4 || node4.kind !== "element" || node4.end > (xmlRange?.[1] ?? documentXml.length))
387041
+ continue;
387042
+ paragraphStart.lastIndex = node4.end;
387043
+ const blockId = readAttrs(documentXml.slice(match.index, node4.openEnd))["w14:paraId"];
386922
387044
  if (!blockId)
386923
387045
  continue;
386924
- paragraphs2.push({
386925
- blockId,
386926
- start: match.index,
386927
- end: paragraphRe.lastIndex,
386928
- xml,
386929
- textLength: extractVisibleTextFromXml(xml).length
386930
- });
387046
+ const xml = documentXml.slice(match.index, node4.end);
387047
+ yield { blockId, start: match.index, end: node4.end, xml, textLength: extractVisibleTextFromXml(xml).length };
386931
387048
  }
386932
- return paragraphs2;
386933
387049
  }
386934
- async function parseParagraphWindowsCooperatively(documentXml, yieldPoint) {
387050
+ function parseParagraphWindows(documentXml, xmlRange) {
387051
+ return [...iterateParagraphWindows(documentXml, xmlRange)].filter((paragraph4) => /^[0-9A-Fa-f]{8}$/.test(paragraph4.blockId));
387052
+ }
387053
+ async function parseParagraphWindowsCooperatively(documentXml, yieldPoint, xmlRange) {
386935
387054
  const paragraphs2 = [];
386936
- const paragraphRe = /<w:p\b[^>]*?(?:\/>|>[\s\S]*?<\/w:p>)/g;
386937
387055
  let sinceYield = 0;
386938
- for (let match = paragraphRe.exec(documentXml);match; match = paragraphRe.exec(documentXml)) {
386939
- const xml = match[0];
386940
- const blockId = /w14:paraId="([0-9A-Fa-f]{8})"/.exec(xml)?.[1];
386941
- if (!blockId)
387056
+ for (const paragraph4 of iterateParagraphWindows(documentXml, xmlRange)) {
387057
+ if (!/^[0-9A-Fa-f]{8}$/.test(paragraph4.blockId))
386942
387058
  continue;
386943
- paragraphs2.push({
386944
- blockId,
386945
- start: match.index,
386946
- end: paragraphRe.lastIndex,
386947
- xml,
386948
- textLength: extractVisibleTextFromXml(xml).length
386949
- });
387059
+ paragraphs2.push(paragraph4);
386950
387060
  if ((sinceYield += 1) >= COOPERATIVE_PARSE_SLICE_PARAGRAPHS) {
386951
387061
  sinceYield = 0;
386952
- const resumeFrom = paragraphRe.lastIndex;
386953
387062
  await yieldPoint();
386954
- paragraphRe.lastIndex = resumeFrom;
386955
387063
  }
386956
387064
  }
386957
387065
  return paragraphs2;
386958
387066
  }
386959
- function parseContentControls(documentXml, paragraphs2, storyId, story) {
386960
- const ranges = collectContentControlRanges(documentXml);
386961
- const hiddenControls = collectHiddenNativeContentControlStarts(documentXml);
386962
- const trackedWrapperRanges = buildTrackedWrapperRanges(documentXml);
387067
+ function parseContentControls(documentXml, paragraphs2, storyId, story, scan) {
387068
+ const ranges = scan?.ranges ?? collectContentControlRanges(documentXml);
387069
+ const hiddenControls = scan?.hiddenControls ?? collectHiddenNativeContentControlStarts(documentXml);
387070
+ const trackedWrapperRanges = scan?.trackedWrapperRanges ?? buildTrackedWrapperRanges(documentXml);
386963
387071
  const parsed = ranges.filter((range) => {
386964
387072
  if (hiddenControls.has(range.start))
386965
387073
  return false;
@@ -386985,10 +387093,10 @@ function collectContentControlRanges(documentXml) {
386985
387093
  ranges.sort((a, b) => a.start - b.start || b.end - a.end);
386986
387094
  return ranges;
386987
387095
  }
386988
- async function parseContentControlsCooperatively(documentXml, paragraphs2, storyId, story, yieldPoint) {
386989
- const ranges = collectContentControlRanges(documentXml);
386990
- const hiddenControls = collectHiddenNativeContentControlStarts(documentXml);
386991
- const trackedWrapperRanges = buildTrackedWrapperRanges(documentXml);
387096
+ async function parseContentControlsCooperatively(documentXml, paragraphs2, storyId, story, yieldPoint, scan) {
387097
+ const ranges = scan?.ranges ?? collectContentControlRanges(documentXml);
387098
+ const hiddenControls = scan?.hiddenControls ?? collectHiddenNativeContentControlStarts(documentXml);
387099
+ const trackedWrapperRanges = scan?.trackedWrapperRanges ?? buildTrackedWrapperRanges(documentXml);
386992
387100
  const parsed = [];
386993
387101
  let sinceYield = 0;
386994
387102
  for (const range of ranges) {
@@ -388026,8 +388134,9 @@ function buildContentXml(text4, kind, allocParaId) {
388026
388134
  function buildAdjacentContent(text4, kind, allocParaId) {
388027
388135
  return kind === "inline" ? `<w:r><w:t>${xmlEscapeText(text4)}</w:t></w:r>` : buildParaIdParagraphXml(allocParaId(), `<w:r><w:t>${xmlEscapeText(text4)}</w:t></w:r>`);
388028
388136
  }
388029
- function insertAtDocumentEnd(documentXml, fragment4) {
388030
- const offset = documentEndInsertionOffset(documentXml);
388137
+ function insertAtDocumentEnd(documentXml, fragment4, xmlRange) {
388138
+ const container = xmlRange ? readXmlNodeAt(documentXml, xmlRange[0]) : null;
388139
+ const offset = container?.kind === "element" ? container.closeStart : documentEndInsertionOffset(documentXml);
388031
388140
  return `${documentXml.slice(0, offset)}${fragment4}${documentXml.slice(offset)}`;
388032
388141
  }
388033
388142
  function replaceSdtPrXml(sdtXml, prXml) {
@@ -388218,6 +388327,7 @@ function controlPlacement(xml, control2) {
388218
388327
  case "body":
388219
388328
  case "hdr":
388220
388329
  case "ftr":
388330
+ case "txbxContent":
388221
388331
  return "block";
388222
388332
  case "sdtContent":
388223
388333
  case "sdt":
@@ -388625,11 +388735,11 @@ function supportsTrackedInlineTextCreate(input) {
388625
388735
  const at = input.at;
388626
388736
  return input.kind === "inline" && input.controlType === "text" && input.content !== undefined && input.html === undefined && input.json === undefined && input.ooxml === undefined && at?.kind === "selection" && at.coordinateSpace !== "tracked" && at.start.kind === "text" && at.end.kind === "text" && at.start.blockId === at.end.blockId && at.start.offset === at.end.offset;
388627
388737
  }
388628
- function wrapSelectionInXml(context, documentXml, target, sdtXml, kind, replacementContent, hasReplacementContent, buildReplacementContentXml2 = (text4) => buildContentXml(text4, kind, createParaIdAllocator(context)), allowBoundarySelection = false) {
388738
+ function wrapSelectionInXml(context, documentXml, target, sdtXml, kind, replacementContent, hasReplacementContent, buildReplacementContentXml2 = (text4) => buildContentXml(text4, kind, createParaIdAllocator(context)), allowBoundarySelection = false, xmlRange) {
388629
388739
  if (target?.kind !== "selection" || target.start?.kind !== "text" || target.end?.kind !== "text") {
388630
388740
  return { ok: false, failure: { code: "INVALID_TARGET", message: "A text selection is required." } };
388631
388741
  }
388632
- const para = findParagraphById3(documentXml, target.start.blockId);
388742
+ const para = findParagraphById3(documentXml, target.start.blockId, xmlRange);
388633
388743
  if (!para)
388634
388744
  return { ok: false, failure: { code: "TARGET_NOT_FOUND", message: "Target paragraph was not found." } };
388635
388745
  if (kind === "inline" && target.start.offset === target.end.offset) {
@@ -388769,11 +388879,17 @@ function insertBlockContentControlAtSelection(documentXml, paragraphs2, target,
388769
388879
  if (!anchor)
388770
388880
  throw operationError("TARGET_NOT_FOUND", "Target paragraph was not found.");
388771
388881
  const collapsed = target.start.blockId === target.end.blockId && target.start.offset === target.end.offset;
388772
- if (collapsed && extractVisibleTextFromXml(anchor.xml).trim().length === 0) {
388882
+ if (collapsed && isReplaceableEmptyParagraph(anchor.xml)) {
388773
388883
  return replaceRange3(documentXml, anchor.start, anchor.end, sdtXml);
388774
388884
  }
388775
388885
  return `${documentXml.slice(0, anchor.end)}${sdtXml}${documentXml.slice(anchor.end)}`;
388776
388886
  }
388887
+ function isReplaceableEmptyParagraph(paragraphXml) {
388888
+ return extractVisibleTextFromXml(paragraphXml).trim().length === 0 && !hasParagraphObjects(paragraphXml);
388889
+ }
388890
+ function hasParagraphObjects(paragraphXml) {
388891
+ return /<(?:[\w.-]+:)?(?:drawing|pict|object)\b/.test(paragraphXml);
388892
+ }
388777
388893
  function resolveWholeBlockSelection(documentXml, paragraphs2, target) {
388778
388894
  let startBlockId;
388779
388895
  let endBlockId;
@@ -388899,12 +389015,10 @@ function findFirstSdtElementRange(xml) {
388899
389015
  }
388900
389016
  return null;
388901
389017
  }
388902
- function findParagraphById3(documentXml, blockId) {
388903
- const paraRe = /<w:p\b[^>]*?(?:\/>|>[\s\S]*?<\/w:p>)/g;
388904
- for (let match = paraRe.exec(documentXml);match; match = paraRe.exec(documentXml)) {
388905
- if (match[0].includes(`w14:paraId="${blockId}"`)) {
388906
- return { start: match.index, end: paraRe.lastIndex, xml: match[0] };
388907
- }
389018
+ function findParagraphById3(documentXml, blockId, xmlRange) {
389019
+ for (const paragraph4 of iterateParagraphWindows(documentXml, xmlRange)) {
389020
+ if (paragraph4.blockId === blockId)
389021
+ return paragraph4;
388908
389022
  }
388909
389023
  return null;
388910
389024
  }
@@ -388991,26 +389105,27 @@ function splitTextRunAtVisibleOffset(paragraphXml, piece, localOffset, insertXml
388991
389105
  return `${paragraphXml.slice(0, piece.runCharStart)}${beforeRun}${insertXml}${afterRun}${paragraphXml.slice(piece.runCharEnd)}`;
388992
389106
  }
388993
389107
  function replaceTextRangeInParagraph2(paragraphXml, start, end, replacement2) {
388994
- const textRe = /<w:t\b[^>]*>([\s\S]*?)<\/w:t>/g;
388995
- let offset = 0;
388996
- for (let match = textRe.exec(paragraphXml);match; match = textRe.exec(paragraphXml)) {
388997
- const text4 = decodeXmlEntities(match[1]);
388998
- const nextOffset = offset + text4.length;
388999
- if (start >= offset && end <= nextOffset) {
389000
- const localStart = start - offset;
389001
- const localEnd = end - offset;
389108
+ const pieces = Array.from(parseParagraphInlineMap(paragraphXml).leaves());
389109
+ for (const piece of pieces) {
389110
+ if (!isSplittableText(piece))
389111
+ continue;
389112
+ if (start >= piece.visibleStart && end <= piece.visibleEnd) {
389113
+ if (hasRunNeighborBefore(pieces, piece) || hasRunNeighborAfter(pieces, piece))
389114
+ return paragraphXml;
389115
+ const text4 = piece.decodedText;
389116
+ const localStart = start - piece.visibleStart;
389117
+ const localEnd = end - piece.visibleStart;
389002
389118
  const before = text4.slice(0, localStart);
389003
389119
  const selected = text4.slice(localStart, localEnd);
389004
389120
  const after = text4.slice(localEnd);
389005
- const runStart = findEnclosingRunStart(paragraphXml, match.index);
389006
- const runEnd = paragraphXml.indexOf("</w:r>", textRe.lastIndex) + "</w:r>".length;
389121
+ const runStart = piece.runCharStart;
389122
+ const runEnd = piece.runCharEnd;
389007
389123
  const { runOpenTag, runProperties } = readRunOpenTagAndProperties(paragraphXml, runStart);
389008
389124
  const mkRun = (segment) => `${runOpenTag}${runProperties}<w:t xml:space="preserve">${xmlEscapeText(segment)}</w:t></w:r>`;
389009
389125
  const beforeRun = before ? mkRun(before) : "";
389010
389126
  const afterRun = after ? mkRun(after) : "";
389011
389127
  return `${paragraphXml.slice(0, runStart)}${beforeRun}${replacement2(selected)}${afterRun}${paragraphXml.slice(runEnd)}`;
389012
389128
  }
389013
- offset = nextOffset;
389014
389129
  }
389015
389130
  return paragraphXml;
389016
389131
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@superdoc/mcp",
3
- "version": "1.10.0-next.5",
3
+ "version": "1.10.0-next.6",
4
4
  "description": "Model Context Protocol server for SuperDoc, giving an AI agent tools to read and edit .docx files.",
5
5
  "type": "module",
6
6
  "license": "AGPL-3.0",
@@ -24,13 +24,13 @@
24
24
  ],
25
25
  "dependencies": {
26
26
  "@modelcontextprotocol/sdk": "^1.26.0",
27
- "@superdoc/sdk": "2.23.0-next.5",
27
+ "@superdoc/sdk": "2.23.0-next.6",
28
28
  "debug": "^4.4.3",
29
29
  "zod": "^4.3.6"
30
30
  },
31
31
  "devDependencies": {
32
- "superdoc": "2.26.0-next.6",
33
- "@superdoc/docx-engine": "0.25.0-next.6",
32
+ "superdoc": "2.26.0-next.7",
33
+ "@superdoc/docx-engine": "0.25.0-next.7",
34
34
  "@superdoc/document-api": "0.1.0-alpha.0",
35
35
  "@types/bun": "^1.3.8",
36
36
  "@types/node": "22.19.2",