@superdoc/mcp 1.7.0-next.15 → 1.7.0-next.16

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 +339 -32
  2. package/package.json +4 -4
package/dist/index.js CHANGED
@@ -31600,7 +31600,7 @@ var init_host = __esm(() => {
31600
31600
  });
31601
31601
 
31602
31602
  // ../../packages/sdk/langs/node/src/runtime/sdk-version.generated.ts
31603
- var SDK_VERSION = "2.20.0-next.15";
31603
+ var SDK_VERSION = "2.20.0-next.16";
31604
31604
 
31605
31605
  // ../../packages/sdk/langs/node/src/runtime/embedded-platform.ts
31606
31606
  import { accessSync, chmodSync, constants, existsSync, statSync } from "node:fs";
@@ -121556,6 +121556,12 @@ function validateClipboardPlan(plan, operationName) {
121556
121556
  if (!Array.isArray(plan.diagnostics)) {
121557
121557
  throw new DocumentApiValidationError("INVALID_INPUT", `${operationName} plan.diagnostics must be an array.`);
121558
121558
  }
121559
+ if (plan.sourceStyleContext !== undefined && plan.sourceStyleContext !== null) {
121560
+ const source = plan.sourceStyleContext;
121561
+ if (typeof source !== "object" || Array.isArray(source) || typeof source.documentEpoch !== "string" || source.documentEpoch.length === 0 || source.documentEpoch.length > 255 || typeof source.digest !== "string" || !/^[a-f0-9]{64}$/.test(source.digest)) {
121562
+ throw new DocumentApiValidationError("INVALID_INPUT", `${operationName} plan.sourceStyleContext is invalid.`);
121563
+ }
121564
+ }
121559
121565
  }
121560
121566
  function validatePasteFragment(fragment2, operationName) {
121561
121567
  if (!fragment2 || typeof fragment2 !== "object") {
@@ -195904,7 +195910,7 @@ function isValidPasteRunMarks(value) {
195904
195910
  if (marks[key2] !== undefined && typeof marks[key2] !== "boolean")
195905
195911
  return false;
195906
195912
  }
195907
- if (marks.color !== undefined && (typeof marks.color !== "string" || !/^[0-9A-F]{6}$/i.test(marks.color))) {
195913
+ if (marks.color !== undefined && (typeof marks.color !== "string" || marks.color !== "auto" && !/^[0-9A-F]{6}$/i.test(marks.color))) {
195908
195914
  return false;
195909
195915
  }
195910
195916
  if (marks.highlight !== undefined) {
@@ -196138,9 +196144,11 @@ function hasOnlyKeys(value, keys) {
196138
196144
  return Object.keys(value).every((key2) => allowed.has(key2));
196139
196145
  }
196140
196146
  function toPasteTextRunSpec(run) {
196147
+ const characterStyleId = run.characterStyleId ?? run.styleRef;
196141
196148
  return {
196142
196149
  text: run.text,
196143
196150
  ...run.marks ? { marks: run.marks } : {},
196151
+ ...characterStyleId ? { characterStyleId } : {},
196144
196152
  ...run.commentIds && run.commentIds.length > 0 ? { commentIds: run.commentIds } : {},
196145
196153
  ...run.trackedInsertion ? { trackedInsertion: true } : {}
196146
196154
  };
@@ -196284,12 +196292,14 @@ function renderPasteRun(run, options = {}) {
196284
196292
  const local = run.kind === "footnoteRef" ? "footnoteReference" : "endnoteReference";
196285
196293
  return `<w:r><w:rPr><w:rStyle w:val="${style}"/></w:rPr><w:${local} w:id="${escapeXmlAttribute(run.noteId)}"/></w:r>`;
196286
196294
  }
196295
+ const characterStyleId = (run.kind === undefined || run.kind === "text" ? run.characterStyleId : undefined) ?? options.characterStyleId;
196296
+ const runOptions = characterStyleId ? { characterStyleId } : options;
196287
196297
  if (run.kind === "lineBreak") {
196288
- return `<w:r>${renderPasteRunProperties(run, options)}<w:br/></w:r>`;
196298
+ return `<w:r>${renderPasteRunProperties(run, runOptions)}<w:br/></w:r>`;
196289
196299
  }
196290
196300
  if (!("text" in run))
196291
196301
  return "";
196292
- const rPr = renderPasteRunProperties(run, options);
196302
+ const rPr = renderPasteRunProperties(run, runOptions);
196293
196303
  if (run.text.length === 0)
196294
196304
  return "";
196295
196305
  const segments = run.text.split("\t");
@@ -196361,7 +196371,11 @@ function renderRunPropertiesXml(marks, options = {}) {
196361
196371
  }
196362
196372
  if (marks.strike !== undefined)
196363
196373
  parts.push(marks.strike ? "<w:strike/>" : '<w:strike w:val="0"/>');
196364
- if (marks.color && isHexColor(marks.color)) {
196374
+ if (marks.strike === false && options.characterStyleId)
196375
+ parts.push('<w:dstrike w:val="0"/>');
196376
+ if (marks.color === "auto") {
196377
+ parts.push('<w:color w:val="auto"/>');
196378
+ } else if (marks.color && isHexColor(marks.color)) {
196365
196379
  parts.push(`<w:color w:val="${escapeXmlAttribute(marks.color.toUpperCase())}"/>`);
196366
196380
  }
196367
196381
  if (typeof marks.fontSizePt === "number" && Number.isFinite(marks.fontSizePt) && marks.fontSizePt > 0) {
@@ -196372,7 +196386,7 @@ function renderRunPropertiesXml(marks, options = {}) {
196372
196386
  parts.push(`<w:szCs w:val="${Math.round(marks.fontSizeCsPt * 2)}"/>`);
196373
196387
  if (marks.underline !== undefined)
196374
196388
  parts.push(`<w:u w:val="${marks.underline ? "single" : "none"}"/>`);
196375
- const highlightXml = renderHighlightOrShadingXml(marks.highlight);
196389
+ const highlightXml = marks.highlight?.trim().toLowerCase() === "none" ? '<w:highlight w:val="none"/>' : renderHighlightOrShadingXml(marks.highlight);
196376
196390
  if (highlightXml)
196377
196391
  parts.push(highlightXml);
196378
196392
  if (isPasteVerticalAlign(marks.verticalAlign)) {
@@ -241698,15 +241712,26 @@ function normalizeMatchDestinationParagraph(block) {
241698
241712
  runs: block.runs.map(withoutSourceMarks),
241699
241713
  ...block.sourcePath ? { sourcePath: block.sourcePath } : {},
241700
241714
  ...block.inlines ? {
241701
- inlines: block.inlines.map((inline) => inline.kind === "text" ? withoutSourceMarks(inline) : inline.kind === "hyperlink" ? {
241702
- ...inline,
241703
- inlines: inline.inlines.map((child) => child.kind === "text" ? withoutSourceMarks(child) : child)
241704
- } : inline)
241715
+ inlines: block.inlines.map(withoutSourceInlineFormatting)
241705
241716
  } : {}
241706
241717
  };
241707
241718
  }
241719
+ function withoutSourceInlineFormatting(inline) {
241720
+ if (inline.kind === "text")
241721
+ return withoutSourceMarks(inline);
241722
+ if (inline.kind === "sdt") {
241723
+ return withoutSourceSdtFormatting(inline);
241724
+ }
241725
+ if (inline.kind === "hyperlink") {
241726
+ return { ...inline, inlines: inline.inlines.map((child) => child.kind === "text" ? withoutSourceMarks(child) : child) };
241727
+ }
241728
+ return inline;
241729
+ }
241730
+ function withoutSourceSdtFormatting(inline) {
241731
+ return { ...inline, inlines: inline.inlines.map((child) => child.kind === "text" ? withoutSourceMarks(child) : child.kind === "sdt" ? withoutSourceSdtFormatting(child) : child) };
241732
+ }
241708
241733
  function withoutSourceMarks(run) {
241709
- const { marks: _sourceMarks, ...retained } = run;
241734
+ const { marks: _sourceMarks, styleRef: _sourceStyle, ...retained } = run;
241710
241735
  return retained;
241711
241736
  }
241712
241737
  var init_match_destination_paste = __esm(() => {
@@ -245978,6 +246003,9 @@ function explicitPasteStyleRefsExist(state, blocks) {
245978
246003
  const inspectParagraph = (block) => {
245979
246004
  if (block.styleRef && !block.pPrXml)
245980
246005
  references.push({ styleId: block.styleRef, type: "paragraph" });
246006
+ for (const styleId of characterStyleRefsInParagraph(block)) {
246007
+ references.push({ styleId, type: "character" });
246008
+ }
245981
246009
  };
245982
246010
  for (const block of walkPasteBlocks(blocks)) {
245983
246011
  if (block.kind === "paragraph")
@@ -245987,6 +246015,28 @@ function explicitPasteStyleRefsExist(state, blocks) {
245987
246015
  }
245988
246016
  return explicitStyleReferencesExist(state, references);
245989
246017
  }
246018
+ function characterStyleRefsInParagraph(block) {
246019
+ const refs = [];
246020
+ for (const run of block.runs) {
246021
+ if (typeof run.styleRef === "string" && run.styleRef.length > 0)
246022
+ refs.push(run.styleRef);
246023
+ }
246024
+ const walkInlines = (inlines) => {
246025
+ if (!inlines)
246026
+ return;
246027
+ for (const inline of inlines) {
246028
+ if (inline.kind === "text") {
246029
+ if (typeof inline.styleRef === "string" && inline.styleRef.length > 0)
246030
+ refs.push(inline.styleRef);
246031
+ continue;
246032
+ }
246033
+ if (inline.kind === "hyperlink" || inline.kind === "sdt")
246034
+ walkInlines(inline.inlines);
246035
+ }
246036
+ };
246037
+ walkInlines(block.inlines);
246038
+ return refs;
246039
+ }
245990
246040
  function resolveSemanticStyleBlocks(blocks, styleIdByRole) {
245991
246041
  const paragraph = (block) => {
245992
246042
  if (!block.semanticStyleRole)
@@ -246505,6 +246555,8 @@ function validateParagraphBlock(block, budget) {
246505
246555
  for (const run of block.runs) {
246506
246556
  if (typeof run.text !== "string")
246507
246557
  return { ok: false, reason: "paste-structure-unsupported" };
246558
+ if (!isValidPasteCharacterStyleRef(run.styleRef))
246559
+ return { ok: false, reason: "paste-structure-unsupported" };
246508
246560
  if (!isValidPasteRunMarks(run.marks))
246509
246561
  return { ok: false, reason: "paste-structure-unsupported" };
246510
246562
  if (block.inlines === undefined && !recordSourcePath(run.sourcePath, budget.sourcePaths)) {
@@ -246527,7 +246579,7 @@ function validatePasteInline(inline, budget, depth = 0) {
246527
246579
  return (inline.controlType === "text" || inline.controlType === "richText") && isValidOptionalXmlAttributeText(inline.tag, 255) && isValidOptionalXmlAttributeText(inline.alias, 255) && Array.isArray(inline.inlines) && inline.inlines.length > 0 && inline.inlines.every((child) => child.kind === "text" || child.kind === "lineBreak" || child.kind === "sdt") && inline.inlines.every((child) => validatePasteInline(child, budget, depth + 1));
246528
246580
  }
246529
246581
  if (inline.kind === "text") {
246530
- return typeof inline.text === "string" && !hasUnsupportedControlChar(inline.text) && isValidPasteRunMarks(inline.marks);
246582
+ return typeof inline.text === "string" && isValidPasteCharacterStyleRef(inline.styleRef) && !hasUnsupportedControlChar(inline.text) && isValidPasteRunMarks(inline.marks);
246531
246583
  }
246532
246584
  if (inline.kind === "lineBreak")
246533
246585
  return true;
@@ -246572,12 +246624,15 @@ function hasMatchingInlineTextProjection(block) {
246572
246624
  });
246573
246625
  return textInlines.length === block.runs.length && textInlines.every((inline, index) => {
246574
246626
  const run = block.runs[index];
246575
- return inline.text === run.text && haveEqualRunMarks(inline.marks, run.marks);
246627
+ return inline.text === run.text && inline.styleRef === run.styleRef && haveEqualRunMarks(inline.marks, run.marks);
246576
246628
  });
246577
246629
  }
246578
246630
  function haveEqualRunMarks(left, right) {
246579
246631
  return left?.boldCs === right?.boldCs && left?.italicCs === right?.italicCs && left?.caps === right?.caps && left?.smallCaps === right?.smallCaps && left?.fontSizeCsPt === right?.fontSizeCsPt && left?.bold === right?.bold && left?.italic === right?.italic && left?.underline === right?.underline && left?.strike === right?.strike && left?.color === right?.color && left?.highlight === right?.highlight && left?.fontSizePt === right?.fontSizePt && left?.fontFamily === right?.fontFamily && left?.verticalAlign === right?.verticalAlign;
246580
246632
  }
246633
+ function isValidPasteCharacterStyleRef(value) {
246634
+ return value === undefined || typeof value === "string" && value.length > 0 && isValidOptionalXmlAttributeText(value, 255);
246635
+ }
246581
246636
  function isValidOptionalXmlAttributeText(value, maxLength) {
246582
246637
  return value === undefined || typeof value === "string" && value.length <= maxLength && !hasUnsupportedXmlAttributeChar(value);
246583
246638
  }
@@ -326648,6 +326703,38 @@ var init_parse_v1_slice = __esm(() => {
326648
326703
  init_src3();
326649
326704
  });
326650
326705
 
326706
+ // ../../../v2/document-api-v2-adapter/dist/clipboard/style-context.js
326707
+ function clipboardStyleContext(context) {
326708
+ const parts = [
326709
+ readStyleDefinitionsPartBytes(context),
326710
+ ...["/word/numbering.xml", "/word/theme/theme1.xml", "/word/settings.xml", "/word/fontTable.xml"].map((uri) => getSDDocumentSessionInternalPartBytes(context.session, uri) ?? null)
326711
+ ];
326712
+ return {
326713
+ documentEpoch: getSDDocumentSessionInternalSourceCommit(context.session).documentEpoch,
326714
+ digest: sha256HexSync(new TextEncoder().encode(JSON.stringify(parts.map((bytes) => bytes ? sha256HexSync(bytes) : null))))
326715
+ };
326716
+ }
326717
+ function clipboardStyleContextMatches(context, value2) {
326718
+ const source = normalizeClipboardStyleContext(value2);
326719
+ if (!source)
326720
+ return false;
326721
+ if (source.documentEpoch === getSDDocumentSessionInternalSourceCommit(context.session).documentEpoch)
326722
+ return true;
326723
+ return source.digest === clipboardStyleContext(context).digest;
326724
+ }
326725
+ function normalizeClipboardStyleContext(value2) {
326726
+ if (!value2 || typeof value2 !== "object" || Array.isArray(value2))
326727
+ return null;
326728
+ const source = value2;
326729
+ if (typeof source.documentEpoch !== "string" || source.documentEpoch.length === 0 || source.documentEpoch.length > 255 || typeof source.digest !== "string" || !/^[a-f0-9]{64}$/.test(source.digest))
326730
+ return null;
326731
+ return { documentEpoch: source.documentEpoch, digest: source.digest };
326732
+ }
326733
+ var init_style_context = __esm(() => {
326734
+ init_sd_document_session_internal();
326735
+ init_part();
326736
+ });
326737
+
326651
326738
  // ../../../v2/document-api-v2-adapter/dist/clipboard/parse-v2-fragment.js
326652
326739
  function parseV2FragmentPayload(payload, mime = SUPERDOC_V2_CLIPBOARD_MIME) {
326653
326740
  const item = payload.items.find((candidate2) => candidate2.type === mime);
@@ -326683,9 +326770,11 @@ function parseV2FragmentPayload(payload, mime = SUPERDOC_V2_CLIPBOARD_MIME) {
326683
326770
  ]
326684
326771
  };
326685
326772
  }
326773
+ const prepared = prepareClipboardPlan(fragment4.fragment, "superdoc-v2-fragment", fragment4.fragment.diagnostics ?? []);
326686
326774
  return {
326687
326775
  ok: true,
326688
- ...prepareClipboardPlan(fragment4.fragment, "superdoc-v2-fragment", fragment4.fragment.diagnostics ?? [])
326776
+ ...prepared,
326777
+ plan: { ...prepared.plan, sourceStyleContext: normalizeClipboardStyleContext(parsed.sourceStyleContext) }
326689
326778
  };
326690
326779
  }
326691
326780
  function normalizeV2WireFragment(value2) {
@@ -326809,9 +326898,13 @@ function normalizeParagraphBlock(value2, path7, version2, allowHeading) {
326809
326898
  const review = normalizeRunReviewFields(run, [...path7, "runs", runIndex]);
326810
326899
  if (isPlanFailure(review))
326811
326900
  return review;
326901
+ const styleRef = normalizeCharacterStyleRef(run.styleRef, [...path7, "runs", runIndex, "styleRef"]);
326902
+ if (isPlanFailure(styleRef))
326903
+ return styleRef;
326812
326904
  runs.push({
326813
326905
  text: run.text,
326814
326906
  ...marks ? { marks } : {},
326907
+ ...styleRef ? { styleRef } : {},
326815
326908
  ...sourcePath2 ? { sourcePath: sourcePath2 } : {},
326816
326909
  ...review
326817
326910
  });
@@ -326829,7 +326922,7 @@ function normalizeParagraphBlock(value2, path7, version2, allowHeading) {
326829
326922
  inlines.push(normalized);
326830
326923
  }
326831
326924
  const projected = inlines.flatMap(projectInlineText);
326832
- if (projected.length !== runs.length || projected.some((inline, runIndex) => inline.text !== runs[runIndex]?.text || JSON.stringify(inline.marks ?? {}) !== JSON.stringify(runs[runIndex]?.marks ?? {}))) {
326925
+ if (projected.length !== runs.length || projected.some((inline, runIndex) => inline.text !== runs[runIndex]?.text || JSON.stringify(inline.marks ?? {}) !== JSON.stringify(runs[runIndex]?.marks ?? {}) || (inline.styleRef ?? undefined) !== (runs[runIndex]?.styleRef ?? undefined))) {
326833
326926
  return structureFailure("Paragraph runs must equal the text projection of inlines.", path7);
326834
326927
  }
326835
326928
  }
@@ -326905,6 +326998,14 @@ function normalizeBlockSdts(value2, path7) {
326905
326998
  function validSdtMetadata(value2) {
326906
326999
  return (value2.lockMode === undefined || value2.lockMode === "unlocked" || value2.lockMode === "sdtLocked" || value2.lockMode === "contentLocked" || value2.lockMode === "sdtContentLocked") && (value2.multiline === undefined || value2.controlType === "text" && typeof value2.multiline === "boolean");
326907
327000
  }
327001
+ function normalizeCharacterStyleRef(value2, path7) {
327002
+ if (value2 === undefined)
327003
+ return;
327004
+ if (typeof value2 !== "string" || value2.length === 0 || !isXml10SafeAttributeValue(value2) || value2.length > 255) {
327005
+ return structureFailure("Paste run styleRef must be a non-empty XML-safe style id of at most 255 characters.", path7);
327006
+ }
327007
+ return value2;
327008
+ }
326908
327009
  function projectInlineText(inline) {
326909
327010
  if (inline.kind === "text")
326910
327011
  return [inline];
@@ -326960,10 +327061,14 @@ function normalizeInline(value2, path7, allowHyperlink, depth = 0) {
326960
327061
  const review = normalizeRunReviewFields(value2, path7);
326961
327062
  if (isPlanFailure(review))
326962
327063
  return review;
327064
+ const styleRef = normalizeCharacterStyleRef(value2.styleRef, [...path7, "styleRef"]);
327065
+ if (isPlanFailure(styleRef))
327066
+ return styleRef;
326963
327067
  return {
326964
327068
  kind: "text",
326965
327069
  text: value2.text,
326966
327070
  ...marks ? { marks } : {},
327071
+ ...styleRef ? { styleRef } : {},
326967
327072
  ...sourcePath ? { sourcePath } : {},
326968
327073
  ...review
326969
327074
  };
@@ -327545,8 +327650,8 @@ function normalizeMarks2(value2, path7) {
327545
327650
  out[key2] = value2[key2];
327546
327651
  }
327547
327652
  if (value2.color !== undefined) {
327548
- if (typeof value2.color !== "string" || !/^#?[0-9A-Fa-f]{6}$/.test(value2.color)) {
327549
- return structureFailure("Paste run color must be a six-digit RGB value.", [...path7, "color"]);
327653
+ if (typeof value2.color !== "string" || value2.color !== "auto" && !/^#?[0-9A-Fa-f]{6}$/.test(value2.color)) {
327654
+ return structureFailure("Paste run color must be a six-digit RGB value or auto.", [...path7, "color"]);
327550
327655
  }
327551
327656
  out.color = value2.color.replace(/^#/, "");
327552
327657
  }
@@ -327606,6 +327711,7 @@ var init_parse_v2_fragment = __esm(() => {
327606
327711
  init_contracts();
327607
327712
  init_fragment_output_budget();
327608
327713
  init_hyperlink_values();
327714
+ init_style_context();
327609
327715
  RUN_MARK_KEYS = new Set([
327610
327716
  "boldCs",
327611
327717
  "italicCs",
@@ -327982,10 +328088,23 @@ function planFromPlainText(text4, plainFallback) {
327982
328088
  ...prepareClipboardPlan(fragment4, "plain-text", [], plainFallback)
327983
328089
  };
327984
328090
  }
327985
- function planFromHtml(payload, html, options) {
328091
+ function createPortableHtmlInsertPlan(payload, options = {}) {
328092
+ const portable = { ...payload, items: payload.items.filter((item) => item.type === "text/html" || item.type === "text/plain") };
328093
+ if (estimatePayloadBytes(portable.items) > (options.maxBytes ?? DEFAULT_MAX_CLIPBOARD_BYTES)) {
328094
+ return failPlan("paste-payload-too-large", "Clipboard payload exceeds the configured size cap.", "html");
328095
+ }
328096
+ const html = readStringItem(portable, "text/html");
328097
+ if (html === null)
328098
+ return failPlan("paste-no-faithful-representation", "No portable HTML flavor is available.", "html");
328099
+ return planFromHtml(portable, html, options, true);
328100
+ }
328101
+ function planFromHtml(payload, html, options, rejectUnsafeContent = false) {
327986
328102
  const rewritten = rewriteWordNoteClipboardHtml(html);
327987
328103
  const converted = convertContentToFragment({ format: "html", source: rewritten });
327988
328104
  const diagnostics = converted.diagnostics.map(toClipboardHtmlDiagnostic);
328105
+ if (rejectUnsafeContent && diagnostics.some((entry) => entry.reason === "paste-security-rejected")) {
328106
+ return failPlan("paste-security-rejected", "The portable HTML flavor contains unsafe content.", "html");
328107
+ }
327989
328108
  const fatal = converted.diagnostics.find((entry) => entry.severity === "error");
327990
328109
  if (fatal) {
327991
328110
  return fallbackFromFatalHtml(payload, options, fatal, diagnostics);
@@ -328689,7 +328808,11 @@ function* serializeSelectionRangeSteps(context, target, includeHtml, options) {
328689
328808
  const selectedInlineControl = options?.wholeParagraphs !== true && startEntry.ordinal === endEntry.ordinal && map3.pieces.some((piece) => piece.kind === "container" && piece.containerKind === "sdt" && piece.visibleStart === from && piece.visibleEnd === to);
328690
328809
  const fullyCovered = !selectedInlineControl && from === 0 && to === map3.visibleLength && (options?.wholeParagraphs === true || startEntry.ordinal !== endEntry.ordinal || properties.styleRef !== undefined || properties.numbering !== undefined || hasParagraphProperties(paragraphXml));
328691
328810
  const styleModel = copyStyleModel ?? (copyStyleModel = compileCopyStyleModel(context));
328692
- const styleContext = copyStyleContext(styleModel, properties.styleRef);
328811
+ const styleContext = {
328812
+ ...copyStyleContext(styleModel, properties.styleRef),
328813
+ materializeParagraphFormatting: !fullyCovered,
328814
+ effectiveTypingMarks: options?.effectiveTypingMarks
328815
+ };
328693
328816
  const semanticStyleRole = portableSemanticStyleRole(styleModel, properties.styleRef);
328694
328817
  const semanticHeadingLevel = semanticStyleRole?.startsWith("heading-") ? Number(semanticStyleRole.slice("heading-".length)) : null;
328695
328818
  const parts = [];
@@ -328866,13 +328989,14 @@ function* serializeSelectionRangeSteps(context, target, includeHtml, options) {
328866
328989
  })), notes.diagnostics());
328867
328990
  plan = buildPlan(fallbackFragment, "plain-text", notes.diagnostics(), true);
328868
328991
  } else {
328992
+ const sourceStyleContext = clipboardStyleContext(context);
328869
328993
  const hasPreservedControls = blocks.some((block) => !!block.blockSdts?.length || block.parts.some((part) => part.kind === "sdt"));
328870
328994
  const legacyDiagnostics = hasPreservedControls ? [...notes.diagnostics(), diagnostic("paste-no-faithful-representation", "The v2.1 clipboard flavor retains visible text but not content-control structure; use the v2.2 flavor to preserve controls.", "superdoc-v2-fragment", "warning")] : notes.diagnostics();
328871
328995
  const fragment4 = withPasteComments(buildFragment(grouped.map(fragmentBlockFor), legacyDiagnostics), commentsById);
328872
328996
  items.push({
328873
328997
  type: SUPERDOC_V2_CLIPBOARD_MIME,
328874
328998
  kind: "string",
328875
- data: JSON.stringify(fragment4)
328999
+ data: JSON.stringify({ ...fragment4, sourceStyleContext })
328876
329000
  });
328877
329001
  const assets = [...rich.assetsByPartUri.values()];
328878
329002
  const hasRichInlineStructure = blocks.some((block) => !!block.blockSdts?.length || block.parts.some((part) => part.kind === "hyperlink" || part.kind === "image" || part.kind === "sdt"));
@@ -328885,11 +329009,12 @@ function* serializeSelectionRangeSteps(context, target, includeHtml, options) {
328885
329009
  assets: assets.map((asset) => asset.wire),
328886
329010
  diagnostics: notes.diagnostics()
328887
329011
  }, commentsById);
328888
- items.push({ type: SUPERDOC_V22_CLIPBOARD_MIME, kind: "string", data: JSON.stringify(wireFragment) });
329012
+ items.push({ type: SUPERDOC_V22_CLIPBOARD_MIME, kind: "string", data: JSON.stringify({ ...wireFragment, sourceStyleContext }) });
328889
329013
  plan = buildPlan({ ...wireFragment, assets: assets.map((asset) => asset.decoded) }, "superdoc-v2-fragment", notes.diagnostics());
328890
329014
  } else {
328891
329015
  plan = buildPlan(fragment4, "superdoc-v2-fragment", notes.diagnostics());
328892
329016
  }
329017
+ plan = { ...plan, sourceStyleContext };
328893
329018
  }
328894
329019
  return {
328895
329020
  ok: true,
@@ -329259,22 +329384,26 @@ function walkPieces(pieces, from, to, out, notes, styleContext, review) {
329259
329384
  const sliceEnd = Math.min(to, piece.visibleEnd) - piece.visibleStart;
329260
329385
  const text4 = decoded.slice(sliceStart, sliceEnd);
329261
329386
  if (text4.length > 0) {
329262
- const marks = resolveRunMarks(piece.runProperties, styleContext);
329387
+ const characterStyleRef = styleContext?.effectiveTypingMarks ? undefined : parseCharacterStyleRef(piece.runProperties);
329388
+ const marks = characterStyleRef ? parseNamedStyleRunMarks(piece.runProperties, styleContext) : resolveRunMarks(piece.runProperties, styleContext);
329263
329389
  const spanStart = piece.visibleStart + sliceStart;
329264
329390
  const spanEnd = piece.visibleStart + sliceEnd;
329265
329391
  out.push({
329266
329392
  kind: "run",
329267
- run: serializePasteRun(text4, marks, piece, spanStart, spanEnd, review)
329393
+ run: serializePasteRun(text4, marks, piece, spanStart, spanEnd, review, characterStyleRef),
329394
+ ...characterStyleRef ? { htmlMarks: resolveNamedStyleHtmlMarks(piece.runProperties, styleContext) } : {}
329268
329395
  });
329269
329396
  }
329270
329397
  break;
329271
329398
  }
329272
329399
  case "control": {
329273
329400
  if (piece.control === "tab" || piece.control === "symbol") {
329274
- const marks = resolveRunMarks(piece.runProperties, styleContext);
329401
+ const characterStyleRef = styleContext?.effectiveTypingMarks ? undefined : parseCharacterStyleRef(piece.runProperties);
329402
+ const marks = characterStyleRef ? parseNamedStyleRunMarks(piece.runProperties, styleContext) : resolveRunMarks(piece.runProperties, styleContext);
329275
329403
  out.push({
329276
329404
  kind: "run",
329277
- run: serializePasteRun(piece.symbolText ?? "\t", marks, piece, piece.visibleStart, piece.visibleEnd, review)
329405
+ ...characterStyleRef ? { htmlMarks: resolveNamedStyleHtmlMarks(piece.runProperties, styleContext) } : {},
329406
+ run: serializePasteRun(piece.symbolText ?? "\t", marks, piece, piece.visibleStart, piece.visibleEnd, review, characterStyleRef)
329278
329407
  });
329279
329408
  } else {
329280
329409
  out.push({ kind: "break" });
@@ -329335,16 +329464,21 @@ function selectionPreservesComment(markerId, from, to, review) {
329335
329464
  return false;
329336
329465
  return review.commentRanges.some((range) => range.id === markerId && range.start < to && range.end > from);
329337
329466
  }
329338
- function serializePasteRun(text4, marks, piece, spanStart, spanEnd, review) {
329467
+ function serializePasteRun(text4, marks, piece, spanStart, spanEnd, review, styleRef) {
329339
329468
  const commentIds = resolveCommentIdsForSpan(spanStart, spanEnd, review);
329340
329469
  const trackedInsertion = piece.containerStack.some((scope) => scope.kind === "ins");
329341
329470
  return {
329342
329471
  text: text4,
329343
329472
  ...marks ? { marks } : {},
329473
+ ...styleRef ? { styleRef } : {},
329344
329474
  ...commentIds.length > 0 ? { commentIds } : {},
329345
329475
  ...trackedInsertion ? { trackedInsertion: true } : {}
329346
329476
  };
329347
329477
  }
329478
+ function parseCharacterStyleRef(rPrXml) {
329479
+ const value2 = parseWordRunPropertiesXml(rPrXml).styleId;
329480
+ return value2 && value2.length > 0 ? value2 : undefined;
329481
+ }
329348
329482
  function resolveCommentIdsForSpan(spanStart, spanEnd, review) {
329349
329483
  if (!review)
329350
329484
  return [];
@@ -329681,6 +329815,115 @@ function parseRunPropertiesMarks(rPrXml) {
329681
329815
  marks.verticalAlign = verticalAlign;
329682
329816
  return Object.keys(marks).length > 0 ? marks : undefined;
329683
329817
  }
329818
+ function resolveNamedStyleHtmlMarks(rPrXml, styleContext) {
329819
+ const marks = resolveRunMarks(rPrXml, styleContext);
329820
+ if (!marks?.color && parseWordRunPropertiesXml(rPrXml).color?.val?.toLowerCase() === "auto") {
329821
+ return { ...marks, color: "auto" };
329822
+ }
329823
+ return marks;
329824
+ }
329825
+ function parseNamedStyleRunMarks(rPrXml, styleContext) {
329826
+ const direct = parseWordRunPropertiesXml(rPrXml);
329827
+ const marks = {};
329828
+ if (direct.bold !== undefined)
329829
+ marks.bold = direct.bold === true;
329830
+ if (direct.boldCs !== undefined)
329831
+ marks.boldCs = direct.boldCs === true;
329832
+ if (direct.italic !== undefined)
329833
+ marks.italic = direct.italic === true;
329834
+ if (direct.iCs !== undefined)
329835
+ marks.italicCs = direct.iCs === true;
329836
+ if (direct.textTransform !== undefined)
329837
+ marks.caps = direct.textTransform === "uppercase";
329838
+ if (direct.smallCaps !== undefined)
329839
+ marks.smallCaps = direct.smallCaps === true;
329840
+ if (direct.underline !== undefined)
329841
+ marks.underline = direct.underline["w:val"] !== "none";
329842
+ if (typeof direct.fontSize === "number" && direct.fontSize > 0)
329843
+ marks.fontSizePt = direct.fontSize / 2;
329844
+ if (typeof direct.fontSizeCs === "number" && direct.fontSizeCs > 0)
329845
+ marks.fontSizeCsPt = direct.fontSizeCs / 2;
329846
+ if (isPortableVerticalAlign(direct.vertAlign))
329847
+ marks.verticalAlign = direct.vertAlign;
329848
+ const highlight = normalizePasteHighlight(direct.highlight?.["w:val"], readHighlightFallbackShdFill(rPrXml));
329849
+ if (highlight)
329850
+ marks.highlight = highlight;
329851
+ if (direct.highlight?.["w:val"]?.toLowerCase() === "none")
329852
+ marks.highlight = "none";
329853
+ if (direct.strike !== undefined || direct.dstrike !== undefined) {
329854
+ const resolved = styleContext?.model.resolveRun({
329855
+ inlineRpr: direct,
329856
+ resolvedParagraphProperties: styleContext.paragraphProperties
329857
+ }) ?? direct;
329858
+ marks.strike = resolved.strike === true || resolved.dstrike === true;
329859
+ }
329860
+ if (rPrXml) {
329861
+ const normalized = normalizeRunAttrsFromOoxml(direct, {
329862
+ themeColors: styleContext?.model.themeColors,
329863
+ themeFontScheme: styleContext?.model.themeFontScheme
329864
+ });
329865
+ if (!marks.color && normalized.color && /^#[0-9a-f]{6}$/i.test(normalized.color)) {
329866
+ marks.color = normalized.color.slice(1).toUpperCase();
329867
+ }
329868
+ if (!marks.color && direct.color?.val?.toLowerCase() === "auto")
329869
+ marks.color = "auto";
329870
+ const fontProperties = direct.fontFamily && styleContext ? styleContext.model.resolveRun({ inlineRpr: direct, resolvedParagraphProperties: styleContext.paragraphProperties }) : direct;
329871
+ const fontNormalized = normalizeRunAttrsFromOoxml(fontProperties, {
329872
+ themeColors: styleContext?.model.themeColors,
329873
+ themeFontScheme: styleContext?.model.themeFontScheme
329874
+ });
329875
+ const fontFamily = direct.fontFamily ? fontNormalized.fontFamily?.split(",")[0]?.trim().replace(/^['"]|['"]$/g, "") : undefined;
329876
+ if (fontFamily)
329877
+ marks.fontFamily = fontFamily;
329878
+ }
329879
+ if (styleContext?.materializeParagraphFormatting) {
329880
+ const source = resolveRunMarks(rPrXml, styleContext);
329881
+ const defaults = resolveRunMarks(rPrXml, {
329882
+ ...styleContext,
329883
+ paragraphProperties: styleContext.model.resolveParagraph({ inlineProps: null }),
329884
+ styleRunProperties: {}
329885
+ });
329886
+ const owned = namedStyleMarkKeys(rPrXml, styleContext.model);
329887
+ for (const key2 of Object.keys(source ?? {})) {
329888
+ if (!owned.has(key2) || source[key2] !== defaults?.[key2])
329889
+ Object.assign(marks, { [key2]: source[key2] });
329890
+ }
329891
+ }
329892
+ return Object.keys(marks).length > 0 ? marks : undefined;
329893
+ }
329894
+ function namedStyleMarkKeys(rPrXml, model) {
329895
+ const keys = new Set;
329896
+ const propertiesByMark = {
329897
+ bold: ["bold"],
329898
+ boldCs: ["boldCs"],
329899
+ italic: ["italic"],
329900
+ italicCs: ["iCs"],
329901
+ caps: ["textTransform"],
329902
+ smallCaps: ["smallCaps"],
329903
+ strike: ["strike", "dstrike"],
329904
+ underline: ["underline"],
329905
+ color: ["color"],
329906
+ fontFamily: ["fontFamily"],
329907
+ fontSizePt: ["fontSize"],
329908
+ fontSizeCsPt: ["fontSizeCs"],
329909
+ highlight: ["highlight", "shading"],
329910
+ verticalAlign: ["vertAlign"]
329911
+ };
329912
+ const seen = new Set;
329913
+ let id = parseCharacterStyleRef(rPrXml);
329914
+ while (id && !seen.has(id)) {
329915
+ seen.add(id);
329916
+ const style = model.translatedLinkedStyles.styles[id];
329917
+ if (!style)
329918
+ break;
329919
+ for (const mark of Object.keys(propertiesByMark)) {
329920
+ if (propertiesByMark[mark].some((property2) => style.runProperties?.[property2] !== undefined))
329921
+ keys.add(mark);
329922
+ }
329923
+ id = style.basedOn;
329924
+ }
329925
+ return keys;
329926
+ }
329684
329927
  function normalizePasteHighlight(rawHighlight, fallbackHighlight) {
329685
329928
  if (rawHighlight) {
329686
329929
  const wordHighlight = normalizeWordHighlightValue(rawHighlight);
@@ -329971,7 +330214,14 @@ function serializedLeafPartInline(part) {
329971
330214
  return part.image;
329972
330215
  if (part.kind === "break")
329973
330216
  return { kind: "lineBreak" };
329974
- return { kind: "text", text: part.run.text, ...part.run.marks ? { marks: part.run.marks } : {}, ...part.run.commentIds ? { commentIds: part.run.commentIds } : {}, ...part.run.trackedInsertion ? { trackedInsertion: true } : {} };
330217
+ return {
330218
+ kind: "text",
330219
+ text: part.run.text,
330220
+ ...part.run.marks ? { marks: part.run.marks } : {},
330221
+ ...part.run.styleRef ? { styleRef: part.run.styleRef } : {},
330222
+ ...part.run.commentIds ? { commentIds: part.run.commentIds } : {},
330223
+ ...part.run.trackedInsertion ? { trackedInsertion: true } : {}
330224
+ };
329975
330225
  }
329976
330226
  function fragmentBlockFor(grouped) {
329977
330227
  if (grouped.kind === "paragraph")
@@ -330043,8 +330293,9 @@ function blockInlineHtml(block, assets, defaultTextColor) {
330043
330293
  function renderHtmlPart(part, assets, defaultTextColor) {
330044
330294
  if (part.kind === "break")
330045
330295
  return "<br/>";
330046
- if (part.kind === "run")
330047
- return renderHtmlRun(part.run, defaultTextColor);
330296
+ if (part.kind === "run") {
330297
+ return renderHtmlRun(part.htmlMarks ? { ...part.run, marks: part.htmlMarks } : part.run, defaultTextColor);
330298
+ }
330048
330299
  if (part.kind === "image") {
330049
330300
  const asset = assets.get(part.image.assetId);
330050
330301
  if (!asset)
@@ -330214,7 +330465,7 @@ function renderHtmlRun(run, defaultTextColor) {
330214
330465
  const styles = [];
330215
330466
  const color2 = marks.color ?? defaultTextColor;
330216
330467
  if (color2)
330217
- styles.push(`color: #${color2}`);
330468
+ styles.push(color2 === "auto" ? "color: initial" : `color: #${color2}`);
330218
330469
  if (marks.fontSizePt)
330219
330470
  styles.push(`font-size: ${marks.fontSizePt}pt`);
330220
330471
  if (marks.fontFamily)
@@ -330257,6 +330508,7 @@ var init_serialize = __esm(() => {
330257
330508
  init_common();
330258
330509
  init_paragraph_property_order();
330259
330510
  init_contracts();
330511
+ init_style_context();
330260
330512
  init_public_model();
330261
330513
  init_hyperlink_values();
330262
330514
  init_footnote_range();
@@ -334407,7 +334659,7 @@ class V2SelectionMutationAdapter {
334407
334659
  start: { kind: "text", blockId: plan.startBlockId, offset: from },
334408
334660
  end: { kind: "text", blockId: plan.startBlockId, offset: from + 1 },
334409
334661
  ...storyLocator ? { story: storyLocator } : {}
334410
- }, false, { offsetSpace: "kernel" });
334662
+ }, false, { offsetSpace: "kernel", effectiveTypingMarks: true });
334411
334663
  if (!source.ok)
334412
334664
  return failureWithoutResolution(source.code, source.message);
334413
334665
  const block = source.result.plan?.fragment.blocks[0];
@@ -403160,6 +403412,9 @@ class V2ClipboardAdapter {
403160
403412
  }
403161
403413
  insert(input, options) {
403162
403414
  const planned = resolveInsertPlan(input);
403415
+ return this.insertPrepared(input, options, planned);
403416
+ }
403417
+ insertPrepared(input, options, planned) {
403163
403418
  if (!planned.ok) {
403164
403419
  if (input.payload)
403165
403420
  return failureFromParse(this.parse(input.payload, { fallback: input.fallback }));
@@ -403191,6 +403446,27 @@ class V2ClipboardAdapter {
403191
403446
  formattingMode: planned.plan.summary.sourceKind === "plain-text" ? "match-destination" : input.formattingMode ?? "preserve-source",
403192
403447
  fragment: planned.applyPlan
403193
403448
  };
403449
+ if (input.plan && planned.plan.sourceStyleContext !== undefined && operation.formattingMode === "preserve-source" && hasNamedCharacterStyles(planned.plan.fragment.blocks) && !clipboardStyleContextMatches(this.context, planned.plan.sourceStyleContext)) {
403450
+ return failure25(publicFailureCodeForPasteReason("paste-no-faithful-representation"), "The generated native plan has incompatible source styles; insert the original payload for portable recovery.", "paste-no-faithful-representation");
403451
+ }
403452
+ if (input.payload && !input.plan && operation.formattingMode === "preserve-source" && planned.plan.summary.sourceKind === "superdoc-v2-fragment" && hasNamedCharacterStyles(planned.plan.fragment.blocks)) {
403453
+ const preflight = preflightSDDocumentSessionInternalFragmentApply(this.context.session, operation);
403454
+ const missingStyles = !preflight.ok && preflight.reason === "style-reference-not-found";
403455
+ const conflictingContext = preflight.ok && !clipboardStyleContextMatches(this.context, planned.plan.sourceStyleContext);
403456
+ if (missingStyles || conflictingContext) {
403457
+ if (planned.plan.fragment.comments?.length || hasNativeReviewOrControls(planned.plan.fragment.blocks)) {
403458
+ return failure25(publicFailureCodeForPasteReason("paste-no-faithful-representation"), "Portable style recovery would discard comments, tracked insertions, or content controls.", "paste-no-faithful-representation", planned.plan.diagnostics);
403459
+ }
403460
+ const portable = createPortableHtmlInsertPlan(input.payload, { fallback: input.fallback });
403461
+ if (portable.ok) {
403462
+ const warning = diagnostic("paste-no-faithful-representation", "Source style definitions are unavailable or differ; the portable HTML appearance was used.", "html", "warning");
403463
+ return this.insertPrepared({ ...input, payload: undefined, fragment: undefined, plan: undefined }, options, { ...portable, plan: { ...portable.plan, diagnostics: [...planned.plan.diagnostics, ...portable.plan.diagnostics, warning] } });
403464
+ }
403465
+ if (conflictingContext) {
403466
+ return failure25(publicFailureCodeForPasteReason(portable.reason), portable.diagnostics[0]?.message ?? portable.reason, portable.reason, portable.diagnostics);
403467
+ }
403468
+ }
403469
+ }
403194
403470
  if (options?.dryRun) {
403195
403471
  const preflight = preflightSDDocumentSessionInternalFragmentApply(this.context.session, operation);
403196
403472
  if (!preflight.ok) {
@@ -403321,6 +403597,34 @@ class V2ClipboardAdapter {
403321
403597
  return attachMutationPublicRevision(serialized.result, this.context.currentPublicRevision());
403322
403598
  }
403323
403599
  }
403600
+ function hasNamedCharacterStyles(blocks) {
403601
+ return blocks.some((block) => {
403602
+ if (block.kind === "table") {
403603
+ return block.rows.some((row) => row.cells.some((cell) => hasNamedCharacterStyles(cell.blocks)));
403604
+ }
403605
+ if (block.kind !== "paragraph" && block.kind !== "heading")
403606
+ return false;
403607
+ return block.runs.some((run) => !!run.styleRef) || inlinesHaveNamedCharacterStyles(block.inlines ?? []);
403608
+ });
403609
+ }
403610
+ function hasNativeReviewOrControls(blocks) {
403611
+ return blocks.some((block) => {
403612
+ if ("blockSdts" in block && block.blockSdts?.length)
403613
+ return true;
403614
+ if (block.kind === "table") {
403615
+ return block.rows.some((row) => row.cells.some((cell) => hasNativeReviewOrControls(cell.blocks)));
403616
+ }
403617
+ if (block.kind !== "paragraph" && block.kind !== "heading")
403618
+ return false;
403619
+ return block.runs.some((run) => run.commentIds?.length || run.trackedInsertion) || inlinesHaveNativeReviewOrControls(block.inlines ?? []);
403620
+ });
403621
+ }
403622
+ function inlinesHaveNativeReviewOrControls(inlines) {
403623
+ return inlines.some((inline) => inline.kind === "sdt" || (inline.kind === "text" ? !!inline.commentIds?.length || inline.trackedInsertion === true : inline.kind === "hyperlink" && inlinesHaveNativeReviewOrControls(inline.inlines)));
403624
+ }
403625
+ function inlinesHaveNamedCharacterStyles(inlines) {
403626
+ return inlines.some((inline) => inline.kind === "text" ? !!inline.styleRef : (inline.kind === "hyperlink" || inline.kind === "sdt") && inlinesHaveNamedCharacterStyles(inline.inlines));
403627
+ }
403324
403628
  function captureClipboardReadState(context) {
403325
403629
  return {
403326
403630
  source: getSDDocumentSessionInternalSourceCommit(context.session),
@@ -403491,7 +403795,8 @@ function resolveInsertPlan(input) {
403491
403795
  const prepared = prepareClipboardPlan(normalized.fragment, input.plan.summary.sourceKind, input.plan.diagnostics, input.plan.summary.plainFallback);
403492
403796
  return {
403493
403797
  ok: true,
403494
- ...prepared
403798
+ ...prepared,
403799
+ plan: { ...prepared.plan, ...input.plan.sourceStyleContext !== undefined ? { sourceStyleContext: input.plan.sourceStyleContext } : {} }
403495
403800
  };
403496
403801
  }
403497
403802
  if (input.payload) {
@@ -403771,6 +404076,8 @@ var init_adapter37 = __esm(() => {
403771
404076
  init_adapter7();
403772
404077
  init_contracts();
403773
404078
  init_parse5();
404079
+ init_plan3();
404080
+ init_style_context();
403774
404081
  init_parse_v2_fragment();
403775
404082
  init_serialize();
403776
404083
  CLIPBOARD_TD = new TextDecoder;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@superdoc/mcp",
3
- "version": "1.7.0-next.15",
3
+ "version": "1.7.0-next.16",
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.20.0-next.15",
27
+ "@superdoc/sdk": "2.20.0-next.16",
28
28
  "debug": "^4.4.3",
29
29
  "zod": "^4.3.6"
30
30
  },
31
31
  "devDependencies": {
32
- "superdoc": "2.23.0-next.15",
33
- "@superdoc/docx-engine": "0.22.0-next.15",
32
+ "superdoc": "2.23.0-next.16",
33
+ "@superdoc/docx-engine": "0.22.0-next.16",
34
34
  "@superdoc/document-api": "0.1.0-alpha.0",
35
35
  "@types/bun": "^1.3.8",
36
36
  "@types/node": "22.19.2",