superdoc 1.2.2-next.1 → 1.3.0-next.2

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.
@@ -12173,8 +12173,18 @@
12173
12173
  if (color2) add("text-decoration-color", color2);
12174
12174
  return parts.join("; ");
12175
12175
  }
12176
- const INLINE_OVERRIDE_PROPERTIES = ["fontSize", "bold", "italic", "strike", "underline", "letterSpacing"];
12176
+ const INLINE_OVERRIDE_PROPERTIES = [
12177
+ "fontSize",
12178
+ "bold",
12179
+ "italic",
12180
+ "strike",
12181
+ "underline",
12182
+ "letterSpacing",
12183
+ "vertAlign",
12184
+ "position"
12185
+ ];
12177
12186
  const DEFAULT_FONT_SIZE_HALF_POINTS = 20;
12187
+ const SUBSCRIPT_SUPERSCRIPT_SCALE$1 = 0.65;
12178
12188
  const resolveRunProperties = (params2, inlineRpr, resolvedPpr, isListNumber = false, numberingDefinedInline = false) => {
12179
12189
  const paragraphStyleId = resolvedPpr?.styleId;
12180
12190
  const paragraphStyleProps = resolveStyleChain$1(params2, paragraphStyleId, translator$1O);
@@ -12522,6 +12532,21 @@
12522
12532
  }
12523
12533
  break;
12524
12534
  }
12535
+ case "vertAlign": {
12536
+ if (value) {
12537
+ textStyleAttrs.vertAlign = value;
12538
+ }
12539
+ break;
12540
+ }
12541
+ case "position": {
12542
+ if (value != null && Number.isFinite(value)) {
12543
+ const points2 = halfPointToPoints(value);
12544
+ if (Number.isFinite(points2)) {
12545
+ textStyleAttrs.position = `${points2}pt`;
12546
+ }
12547
+ }
12548
+ break;
12549
+ }
12525
12550
  }
12526
12551
  });
12527
12552
  if (Object.keys(textStyleAttrs).length) {
@@ -12636,6 +12661,8 @@
12636
12661
  let hasTextDecorationNone = false;
12637
12662
  let highlightColor = null;
12638
12663
  let hasHighlightTag = false;
12664
+ let verticalAlignValue;
12665
+ let fontSizeOverride;
12639
12666
  Object.keys(runProperties).forEach((key2) => {
12640
12667
  const value = runProperties[key2];
12641
12668
  switch (key2) {
@@ -12766,6 +12793,35 @@
12766
12793
  }
12767
12794
  break;
12768
12795
  }
12796
+ case "vertAlign": {
12797
+ if (runProperties.position != null && Number.isFinite(runProperties.position)) {
12798
+ break;
12799
+ }
12800
+ if (value === "superscript" || value === "subscript") {
12801
+ verticalAlignValue = value === "superscript" ? "super" : "sub";
12802
+ if (runProperties.fontSize != null && Number.isFinite(runProperties.fontSize)) {
12803
+ const scaledPoints = halfPointToPoints(runProperties.fontSize * SUBSCRIPT_SUPERSCRIPT_SCALE$1);
12804
+ if (Number.isFinite(scaledPoints)) {
12805
+ fontSizeOverride = `${scaledPoints}pt`;
12806
+ }
12807
+ } else {
12808
+ fontSizeOverride = `${SUBSCRIPT_SUPERSCRIPT_SCALE$1 * 100}%`;
12809
+ }
12810
+ } else if (value === "baseline") {
12811
+ verticalAlignValue = "baseline";
12812
+ }
12813
+ break;
12814
+ }
12815
+ case "position": {
12816
+ if (value != null && Number.isFinite(value)) {
12817
+ const points = halfPointToPoints(value);
12818
+ if (Number.isFinite(points)) {
12819
+ verticalAlignValue = `${points}pt`;
12820
+ fontSizeOverride = void 0;
12821
+ }
12822
+ }
12823
+ break;
12824
+ }
12769
12825
  }
12770
12826
  });
12771
12827
  if (!hasTextDecorationNone && textDecorationLines.size) {
@@ -12780,6 +12836,12 @@
12780
12836
  css["color"] = "inherit";
12781
12837
  }
12782
12838
  }
12839
+ if (fontSizeOverride) {
12840
+ css["font-size"] = fontSizeOverride;
12841
+ }
12842
+ if (verticalAlignValue) {
12843
+ css["vertical-align"] = verticalAlignValue;
12844
+ }
12783
12845
  return css;
12784
12846
  }
12785
12847
  function decodeRPrFromMarks(marks) {
@@ -12856,6 +12918,20 @@
12856
12918
  runProperties.fontFamily = result;
12857
12919
  }
12858
12920
  break;
12921
+ case "vertAlign":
12922
+ if (value != null) {
12923
+ runProperties.vertAlign = value;
12924
+ }
12925
+ break;
12926
+ case "position": {
12927
+ if (value != null) {
12928
+ const numeric = parseFloat(value);
12929
+ if (!isNaN(numeric)) {
12930
+ runProperties.position = numeric * 2;
12931
+ }
12932
+ }
12933
+ break;
12934
+ }
12859
12935
  }
12860
12936
  });
12861
12937
  break;
@@ -35843,7 +35919,7 @@ Please report this to https://github.com/markedjs/marked.`, e) {
35843
35919
  static getStoredSuperdocVersion(docx) {
35844
35920
  return SuperConverter.getStoredCustomProperty(docx, "SuperdocVersion");
35845
35921
  }
35846
- static setStoredSuperdocVersion(docx = this.convertedXml, version2 = "1.2.2-next.1") {
35922
+ static setStoredSuperdocVersion(docx = this.convertedXml, version2 = "1.3.0-next.2") {
35847
35923
  return SuperConverter.setStoredCustomProperty(docx, "SuperdocVersion", version2, false);
35848
35924
  }
35849
35925
  /**
@@ -61465,7 +61541,7 @@ Please report this to https://github.com/markedjs/marked.`, e) {
61465
61541
  return false;
61466
61542
  }
61467
61543
  };
61468
- const summaryVersion = "1.2.2-next.1";
61544
+ const summaryVersion = "1.3.0-next.2";
61469
61545
  const nodeKeys = ["group", "content", "marks", "inline", "atom", "defining", "code", "tableRole", "summary"];
61470
61546
  const markKeys = ["group", "inclusive", "excludes", "spanning", "code"];
61471
61547
  function mapAttributes(attrs) {
@@ -64099,7 +64175,7 @@ Please report this to https://github.com/markedjs/marked.`, e) {
64099
64175
  * Process collaboration migrations
64100
64176
  */
64101
64177
  processCollaborationMigrations() {
64102
- console.debug("[checkVersionMigrations] Current editor version", "1.2.2-next.1");
64178
+ console.debug("[checkVersionMigrations] Current editor version", "1.3.0-next.2");
64103
64179
  if (!this.options.ydoc) return;
64104
64180
  const metaMap = this.options.ydoc.getMap("meta");
64105
64181
  let docVersion = metaMap.get("version");
@@ -66519,18 +66595,18 @@ Please report this to https://github.com/markedjs/marked.`, e) {
66519
66595
  case "decimal":
66520
66596
  return num.toString();
66521
66597
  case "lowerLetter":
66522
- return toLetter$1(num, false);
66598
+ return toLetter(num, false);
66523
66599
  case "upperLetter":
66524
- return toLetter$1(num, true);
66600
+ return toLetter(num, true);
66525
66601
  case "lowerRoman":
66526
- return toRoman$2(num).toLowerCase();
66602
+ return toRoman$1(num).toLowerCase();
66527
66603
  case "upperRoman":
66528
- return toRoman$2(num);
66604
+ return toRoman$1(num);
66529
66605
  default:
66530
66606
  return num.toString();
66531
66607
  }
66532
66608
  }
66533
- function toLetter$1(num, uppercase) {
66609
+ function toLetter(num, uppercase) {
66534
66610
  let result = "";
66535
66611
  let n = num;
66536
66612
  while (n > 0) {
@@ -66541,7 +66617,7 @@ Please report this to https://github.com/markedjs/marked.`, e) {
66541
66617
  }
66542
66618
  return result || (uppercase ? "A" : "a");
66543
66619
  }
66544
- function toRoman$2(num) {
66620
+ function toRoman$1(num) {
66545
66621
  const lookup = [
66546
66622
  [1e3, "M"],
66547
66623
  [900, "CM"],
@@ -71945,7 +72021,14 @@ ${l}
71945
72021
  fragmentEl.dataset.continuesOnNext = "true";
71946
72022
  }
71947
72023
  const lines = fragment.lines ?? measure.lines.slice(fragment.fromLine, fragment.toLine);
71948
- applyParagraphBlockStyles(fragmentEl, block.attrs);
72024
+ applyParagraphBlockStyles(fragmentEl, block.attrs, { includeBorders: false, includeShading: false });
72025
+ const { shadingLayer, borderLayer } = createParagraphDecorationLayers(this.doc, fragment.width, block.attrs);
72026
+ if (shadingLayer) {
72027
+ fragmentEl.appendChild(shadingLayer);
72028
+ }
72029
+ if (borderLayer) {
72030
+ fragmentEl.appendChild(borderLayer);
72031
+ }
71949
72032
  if (block.attrs?.styleId) {
71950
72033
  fragmentEl.dataset.styleId = block.attrs.styleId;
71951
72034
  fragmentEl.setAttribute("styleid", block.attrs.styleId);
@@ -72329,10 +72412,18 @@ ${l}
72329
72412
  contentEl.classList.add("superdoc-list-content");
72330
72413
  this.applySdtDataset(contentEl, paragraphMetadata);
72331
72414
  contentEl.style.display = "inline-block";
72415
+ contentEl.style.position = "relative";
72332
72416
  contentEl.style.width = `${fragment.width}px`;
72333
72417
  const lines = itemMeasure.paragraph.lines.slice(fragment.fromLine, fragment.toLine);
72334
72418
  const contentAttrs = wordLayout ? item.paragraph.attrs : stripListIndent(item.paragraph.attrs);
72335
- applyParagraphBlockStyles(contentEl, contentAttrs);
72419
+ applyParagraphBlockStyles(contentEl, contentAttrs, { includeBorders: false, includeShading: false });
72420
+ const { shadingLayer, borderLayer } = createParagraphDecorationLayers(this.doc, fragment.width, contentAttrs);
72421
+ if (shadingLayer) {
72422
+ contentEl.appendChild(shadingLayer);
72423
+ }
72424
+ if (borderLayer) {
72425
+ contentEl.appendChild(borderLayer);
72426
+ }
72336
72427
  contentEl.style.textAlign = "left";
72337
72428
  const paraForList = {
72338
72429
  ...item.paragraph,
@@ -74556,7 +74647,7 @@ ${l}
74556
74647
  }
74557
74648
  });
74558
74649
  };
74559
- const applyParagraphBlockStyles = (element2, attrs) => {
74650
+ const applyParagraphBlockStyles = (element2, attrs, options = {}) => {
74560
74651
  if (!attrs) return;
74561
74652
  if (attrs.styleId) {
74562
74653
  element2.setAttribute("styleid", attrs.styleId);
@@ -74583,8 +74674,55 @@ ${l}
74583
74674
  }
74584
74675
  }
74585
74676
  }
74586
- applyParagraphBorderStyles(element2, attrs.borders);
74587
- applyParagraphShadingStyles(element2, attrs.shading);
74677
+ if (options.includeBorders ?? true) {
74678
+ applyParagraphBorderStyles(element2, attrs.borders);
74679
+ }
74680
+ if (options.includeShading ?? true) {
74681
+ applyParagraphShadingStyles(element2, attrs.shading);
74682
+ }
74683
+ };
74684
+ const getParagraphBorderBox = (fragmentWidth, indent2) => {
74685
+ const indentLeft = Number.isFinite(indent2?.left) ? indent2.left : 0;
74686
+ const indentRight = Number.isFinite(indent2?.right) ? indent2.right : 0;
74687
+ const firstLine = Number.isFinite(indent2?.firstLine) ? indent2.firstLine : 0;
74688
+ const hanging = Number.isFinite(indent2?.hanging) ? indent2.hanging : 0;
74689
+ const firstLineOffset = firstLine - hanging;
74690
+ const minLeftInset = Math.min(indentLeft, indentLeft + firstLineOffset);
74691
+ const leftInset = Math.max(0, minLeftInset);
74692
+ const rightInset = Math.max(0, indentRight);
74693
+ return {
74694
+ leftInset,
74695
+ width: Math.max(0, fragmentWidth - leftInset - rightInset)
74696
+ };
74697
+ };
74698
+ const createParagraphDecorationLayers = (doc2, fragmentWidth, attrs) => {
74699
+ if (!attrs?.borders && !attrs?.shading) return {};
74700
+ const borderBox = getParagraphBorderBox(fragmentWidth, attrs.indent);
74701
+ const baseStyles = {
74702
+ position: "absolute",
74703
+ top: "0px",
74704
+ bottom: "0px",
74705
+ left: `${borderBox.leftInset}px`,
74706
+ width: `${borderBox.width}px`,
74707
+ pointerEvents: "none",
74708
+ boxSizing: "border-box"
74709
+ };
74710
+ let shadingLayer;
74711
+ if (attrs.shading) {
74712
+ shadingLayer = doc2.createElement("div");
74713
+ shadingLayer.classList.add("superdoc-paragraph-shading");
74714
+ Object.assign(shadingLayer.style, baseStyles);
74715
+ applyParagraphShadingStyles(shadingLayer, attrs.shading);
74716
+ }
74717
+ let borderLayer;
74718
+ if (attrs.borders) {
74719
+ borderLayer = doc2.createElement("div");
74720
+ borderLayer.classList.add("superdoc-paragraph-border");
74721
+ Object.assign(borderLayer.style, baseStyles);
74722
+ borderLayer.style.zIndex = "1";
74723
+ applyParagraphBorderStyles(borderLayer, attrs.borders);
74724
+ }
74725
+ return { shadingLayer, borderLayer };
74588
74726
  };
74589
74727
  const BORDER_SIDES = ["top", "right", "bottom", "left"];
74590
74728
  const applyParagraphBorderStyles = (element2, borders) => {
@@ -78235,33 +78373,13 @@ ${l}
78235
78373
  toLineByCell.push(cutLine);
78236
78374
  heightByCell.push(cumulativeHeight);
78237
78375
  }
78238
- const allCellsCompleteInFirstPass = toLineByCell.every((cutLine, idx) => {
78239
- const totalLines = getCellTotalLines(row2.cells[idx]);
78240
- return cutLine >= totalLines;
78241
- });
78242
- const lineAdvancements = toLineByCell.map((cutLine, idx) => cutLine - (startLines[idx] || 0));
78243
- const positiveAdvancements = lineAdvancements.filter((adv) => adv > 0);
78244
- const minLineAdvancement = positiveAdvancements.length > 0 ? Math.min(...positiveAdvancements) : 0;
78245
78376
  let actualPartialHeight = 0;
78246
78377
  let maxPaddingTotal = 0;
78247
78378
  for (let cellIdx = 0; cellIdx < cellCount; cellIdx++) {
78248
- const cell2 = row2.cells[cellIdx];
78249
- const startLine = startLines[cellIdx] || 0;
78250
- const lines = getCellLines(cell2);
78251
78379
  const cellPadding = cellPaddings[cellIdx];
78252
78380
  const paddingTotal = cellPadding.top + cellPadding.bottom;
78253
78381
  maxPaddingTotal = Math.max(maxPaddingTotal, paddingTotal);
78254
- if (allCellsCompleteInFirstPass) {
78255
- actualPartialHeight = Math.max(actualPartialHeight, heightByCell[cellIdx] + paddingTotal);
78256
- } else {
78257
- const targetLine = Math.min(startLine + minLineAdvancement, lines.length);
78258
- let cumulativeHeight = 0;
78259
- for (let i2 = startLine; i2 < targetLine; i2++) {
78260
- cumulativeHeight += lines[i2].lineHeight || 0;
78261
- }
78262
- toLineByCell[cellIdx] = targetLine;
78263
- actualPartialHeight = Math.max(actualPartialHeight, cumulativeHeight + paddingTotal);
78264
- }
78382
+ actualPartialHeight = Math.max(actualPartialHeight, heightByCell[cellIdx] + paddingTotal);
78265
78383
  }
78266
78384
  const madeProgress = toLineByCell.some((cutLine, idx) => cutLine > (startLines[idx] || 0));
78267
78385
  const isFirstPart = startLines.every((l) => l === 0);
@@ -78814,7 +78932,7 @@ ${l}
78814
78932
  function toLowerLetter(num) {
78815
78933
  return toUpperLetter(num).toLowerCase();
78816
78934
  }
78817
- function formatPageNumber$1(pageNumber, format) {
78935
+ function formatPageNumber(pageNumber, format) {
78818
78936
  const num = Math.max(1, pageNumber);
78819
78937
  switch (format) {
78820
78938
  case "decimal":
@@ -78827,6 +78945,8 @@ ${l}
78827
78945
  return toUpperLetter(num);
78828
78946
  case "lowerLetter":
78829
78947
  return toLowerLetter(num);
78948
+ case "numberInDash":
78949
+ return `-${num}-`;
78830
78950
  default:
78831
78951
  return String(num);
78832
78952
  }
@@ -78855,7 +78975,7 @@ ${l}
78855
78975
  const sectionMetadata = sectionMap.get(pageSectionIndex);
78856
78976
  const format = sectionMetadata?.numbering?.format ?? "decimal";
78857
78977
  const displayNumber = runningCounter;
78858
- const displayText = formatPageNumber$1(displayNumber, format);
78978
+ const displayText = formatPageNumber(displayNumber, format);
78859
78979
  result.push({
78860
78980
  physicalPage: page.number,
78861
78981
  displayNumber,
@@ -78991,59 +79111,6 @@ ${l}
78991
79111
  if (!layoutDebugEnabled$1) return;
78992
79112
  console.log(...args);
78993
79113
  };
78994
- function formatPageNumber(num, format) {
78995
- switch (format) {
78996
- case "decimal":
78997
- return String(num);
78998
- case "lowerLetter":
78999
- return toLetter(num, false);
79000
- case "upperLetter":
79001
- return toLetter(num, true);
79002
- case "lowerRoman":
79003
- return toRoman$1(num).toLowerCase();
79004
- case "upperRoman":
79005
- return toRoman$1(num);
79006
- default:
79007
- return String(num);
79008
- }
79009
- }
79010
- function toLetter(num, uppercase) {
79011
- let result = "";
79012
- let n = Math.max(1, Math.floor(num));
79013
- while (n > 0) {
79014
- const remainder = (n - 1) % 26;
79015
- const char = String.fromCharCode((uppercase ? 65 : 97) + remainder);
79016
- result = char + result;
79017
- n = Math.floor((n - 1) / 26);
79018
- }
79019
- return result;
79020
- }
79021
- function toRoman$1(num) {
79022
- const lookup = [
79023
- [1e3, "M"],
79024
- [900, "CM"],
79025
- [500, "D"],
79026
- [400, "CD"],
79027
- [100, "C"],
79028
- [90, "XC"],
79029
- [50, "L"],
79030
- [40, "XL"],
79031
- [10, "X"],
79032
- [9, "IX"],
79033
- [5, "V"],
79034
- [4, "IV"],
79035
- [1, "I"]
79036
- ];
79037
- let result = "";
79038
- let n = Math.max(1, Math.floor(num));
79039
- for (const [value, numeral] of lookup) {
79040
- while (n >= value) {
79041
- result += numeral;
79042
- n -= value;
79043
- }
79044
- }
79045
- return result;
79046
- }
79047
79114
  function layoutDocument(blocks2, measures, options = {}) {
79048
79115
  if (blocks2.length !== measures.length) {
79049
79116
  throw new Error(
@@ -80499,7 +80566,7 @@ ${l}
80499
80566
  return `${block.id}@${safeWidth}x${safeHeight}:${hash2}`;
80500
80567
  }
80501
80568
  }
80502
- function resolveHeaderFooterTokens(blocks2, pageNumber, totalPages) {
80569
+ function resolveHeaderFooterTokens(blocks2, pageNumber, totalPages, pageNumberText) {
80503
80570
  if (!blocks2 || blocks2.length === 0) {
80504
80571
  return;
80505
80572
  }
@@ -80511,7 +80578,7 @@ ${l}
80511
80578
  console.warn("[resolveHeaderFooterTokens] Invalid totalPages:", totalPages, "- using 1 as fallback");
80512
80579
  totalPages = 1;
80513
80580
  }
80514
- const pageNumberStr = String(pageNumber);
80581
+ const pageNumberStr = pageNumberText ?? String(pageNumber);
80515
80582
  const totalPagesStr = String(totalPages);
80516
80583
  for (const block of blocks2) {
80517
80584
  if (block.kind !== "paragraph") continue;
@@ -80935,15 +81002,31 @@ ${l}
80935
81002
  for (const pageNum of pagesToLayout) {
80936
81003
  const clonedBlocks = cloneHeaderFooterBlocks(blocks2);
80937
81004
  const { displayText, totalPages: totalPagesForPage } = pageResolver(pageNum);
80938
- const resolvedPageNum = parseInt(displayText, 10) || pageNum;
80939
- resolveHeaderFooterTokens(clonedBlocks, resolvedPageNum, totalPagesForPage);
81005
+ resolveHeaderFooterTokens(clonedBlocks, pageNum, totalPagesForPage, displayText);
80940
81006
  const measures = await cache2.measureBlocks(clonedBlocks, constraints, measureBlock2);
80941
81007
  const pageLayout = layoutHeaderFooter(clonedBlocks, measures, constraints);
81008
+ const measuresById = /* @__PURE__ */ new Map();
81009
+ for (let i2 = 0; i2 < clonedBlocks.length; i2 += 1) {
81010
+ measuresById.set(clonedBlocks[i2].id, measures[i2]);
81011
+ }
81012
+ const fragmentsWithLines = pageLayout.pages[0]?.fragments.map((fragment) => {
81013
+ if (fragment.kind !== "para") {
81014
+ return fragment;
81015
+ }
81016
+ const measure = measuresById.get(fragment.blockId);
81017
+ if (!measure || measure.kind !== "paragraph") {
81018
+ return fragment;
81019
+ }
81020
+ return {
81021
+ ...fragment,
81022
+ lines: measure.lines.slice(fragment.fromLine, fragment.toLine)
81023
+ };
81024
+ }) ?? [];
80942
81025
  pages.push({
80943
81026
  number: pageNum,
80944
81027
  blocks: clonedBlocks,
80945
81028
  measures,
80946
- fragments: pageLayout.pages[0]?.fragments ?? []
81029
+ fragments: fragmentsWithLines
80947
81030
  });
80948
81031
  }
80949
81032
  const firstPageLayout = pages[0] ? layoutHeaderFooter(pages[0].blocks, pages[0].measures, constraints) : { height: 0 };
@@ -85822,7 +85905,14 @@ ${l}
85822
85905
  const pgNumType = elements.find((el) => el?.name === "w:pgNumType");
85823
85906
  if (!pgNumType?.attributes) return void 0;
85824
85907
  const fmtRaw = pgNumType.attributes["w:fmt"];
85825
- const validFormats = ["decimal", "lowerLetter", "upperLetter", "lowerRoman", "upperRoman"];
85908
+ const validFormats = [
85909
+ "decimal",
85910
+ "lowerLetter",
85911
+ "upperLetter",
85912
+ "lowerRoman",
85913
+ "upperRoman",
85914
+ "numberInDash"
85915
+ ];
85826
85916
  const fmt = validFormats.includes(fmtRaw) ? fmtRaw : void 0;
85827
85917
  const startRaw = pgNumType.attributes["w:start"];
85828
85918
  const startNum = startRaw != null ? Number(startRaw) : void 0;
@@ -95818,6 +95908,7 @@ ${l}
95818
95908
  const TrackInsertMarkName = "trackInsert";
95819
95909
  const TrackDeleteMarkName = "trackDelete";
95820
95910
  const TrackFormatMarkName = "trackFormat";
95911
+ const SUBSCRIPT_SUPERSCRIPT_SCALE = 0.65;
95821
95912
  const DEFAULT_PAGE_SIZE = { w: 612, h: 792 };
95822
95913
  const DEFAULT_MARGINS = { top: 72, right: 72, bottom: 72, left: 72 };
95823
95914
  const DEFAULT_VIRTUALIZED_PAGE_GAP = 72;
@@ -99033,6 +99124,7 @@ ${l}
99033
99124
  );
99034
99125
  this.#domIndexObserverManager?.pause();
99035
99126
  painter.paint(layout, this.#painterHost);
99127
+ this.#applyVertAlignToLayout();
99036
99128
  this.#rebuildDomPositionIndex();
99037
99129
  this.#domIndexObserverManager?.resume();
99038
99130
  this.#layoutEpoch = layoutEpoch;
@@ -100777,6 +100869,90 @@ ${l}
100777
100869
  this.#errorBanner = null;
100778
100870
  this.#errorBannerMessage = null;
100779
100871
  }
100872
+ /**
100873
+ * Applies vertical alignment and font scaling to layout DOM elements for subscript/superscript rendering.
100874
+ *
100875
+ * This method post-processes the painted DOM layout to apply vertical alignment styles
100876
+ * (super, sub, baseline, or custom position) based on run properties and text style marks.
100877
+ * It handles both DOCX-style vertAlign ('superscript', 'subscript', 'baseline') and
100878
+ * custom position offsets (in half-points).
100879
+ *
100880
+ * Processing logic:
100881
+ * 1. Queries all text spans with ProseMirror position markers
100882
+ * 2. For each span, resolves the ProseMirror position to find the containing run node
100883
+ * 3. Extracts vertAlign and position from run properties and/or text style marks
100884
+ * 4. Applies CSS vertical-align and font-size styles based on the extracted properties
100885
+ * 5. Position takes precedence over vertAlign when both are present
100886
+ *
100887
+ * @throws Does not throw - DOM manipulation errors are silently caught to prevent layout corruption
100888
+ * @private
100889
+ */
100890
+ #applyVertAlignToLayout() {
100891
+ const doc2 = this.#editor?.state?.doc;
100892
+ if (!doc2 || !this.#painterHost) return;
100893
+ try {
100894
+ const spans = this.#painterHost.querySelectorAll(".superdoc-line span[data-pm-start]");
100895
+ spans.forEach((span) => {
100896
+ try {
100897
+ if (span.closest(".superdoc-page-header, .superdoc-page-footer")) return;
100898
+ const pmStart = Number(span.dataset.pmStart ?? "NaN");
100899
+ if (!Number.isFinite(pmStart)) return;
100900
+ const pos = Math.max(0, Math.min(pmStart, doc2.content.size));
100901
+ const $pos = doc2.resolve(pos);
100902
+ let runNode = null;
100903
+ for (let depth = $pos.depth; depth >= 0; depth--) {
100904
+ const node2 = $pos.node(depth);
100905
+ if (node2.type.name === "run") {
100906
+ runNode = node2;
100907
+ break;
100908
+ }
100909
+ }
100910
+ let vertAlign = runNode?.attrs?.runProperties?.vertAlign ?? null;
100911
+ let position2 = runNode?.attrs?.runProperties?.position ?? null;
100912
+ let fontSizeHalfPts = runNode?.attrs?.runProperties?.fontSize ?? null;
100913
+ if (!vertAlign && position2 == null && runNode) {
100914
+ runNode.forEach((child) => {
100915
+ if (!child.isText || !child.marks?.length) return;
100916
+ const rpr = decodeRPrFromMarks(child.marks);
100917
+ if (rpr.vertAlign && !vertAlign) vertAlign = rpr.vertAlign;
100918
+ if (rpr.position != null && position2 == null) position2 = rpr.position;
100919
+ if (rpr.fontSize != null && fontSizeHalfPts == null) fontSizeHalfPts = rpr.fontSize;
100920
+ });
100921
+ }
100922
+ if (vertAlign == null && position2 == null) return;
100923
+ const styleEntries = [];
100924
+ if (position2 != null && Number.isFinite(position2)) {
100925
+ const pts = halfPointToPoints(position2);
100926
+ if (Number.isFinite(pts)) {
100927
+ styleEntries.push(`vertical-align: ${pts}pt`);
100928
+ }
100929
+ } else if (vertAlign === "superscript" || vertAlign === "subscript") {
100930
+ styleEntries.push(`vertical-align: ${vertAlign === "superscript" ? "super" : "sub"}`);
100931
+ if (fontSizeHalfPts != null && Number.isFinite(fontSizeHalfPts)) {
100932
+ const scaledPts = halfPointToPoints(fontSizeHalfPts * SUBSCRIPT_SUPERSCRIPT_SCALE);
100933
+ if (Number.isFinite(scaledPts)) {
100934
+ styleEntries.push(`font-size: ${scaledPts}pt`);
100935
+ } else {
100936
+ styleEntries.push(`font-size: ${SUBSCRIPT_SUPERSCRIPT_SCALE * 100}%`);
100937
+ }
100938
+ } else {
100939
+ styleEntries.push(`font-size: ${SUBSCRIPT_SUPERSCRIPT_SCALE * 100}%`);
100940
+ }
100941
+ } else if (vertAlign === "baseline") {
100942
+ styleEntries.push("vertical-align: baseline");
100943
+ }
100944
+ if (!styleEntries.length) return;
100945
+ const existing = span.getAttribute("style");
100946
+ const merged = existing ? `${existing}; ${styleEntries.join("; ")}` : styleEntries.join("; ");
100947
+ span.setAttribute("style", merged);
100948
+ } catch (error) {
100949
+ console.error("Failed to apply vertical alignment to span:", error);
100950
+ }
100951
+ });
100952
+ } catch (error) {
100953
+ console.error("Failed to apply vertical alignment to layout:", error);
100954
+ }
100955
+ }
100780
100956
  }
100781
100957
  const Color = Extension.create({
100782
100958
  name: "color",
@@ -112309,7 +112485,63 @@ ${l}
112309
112485
  * @category Attribute
112310
112486
  * @param {string} [styleId] - Style identifier for referencing predefined styles
112311
112487
  */
112312
- styleId: {}
112488
+ styleId: {},
112489
+ /**
112490
+ * Vertical alignment for subscript/superscript text (DOCX w:vertAlign).
112491
+ * Standard values: 'superscript', 'subscript', 'baseline'.
112492
+ * When both vertAlign and position are present, position takes precedence.
112493
+ * Renders as CSS vertical-align with 65% font-size scaling for super/subscript.
112494
+ * @category Attribute
112495
+ * @param {string} [vertAlign] - Vertical alignment mode ('superscript' | 'subscript' | 'baseline')
112496
+ */
112497
+ vertAlign: {
112498
+ default: null,
112499
+ renderDOM: (attrs) => {
112500
+ if (!attrs.vertAlign || attrs.position) return {};
112501
+ if (attrs.vertAlign === "superscript") {
112502
+ return { style: "vertical-align: super; font-size: 65%;" };
112503
+ }
112504
+ if (attrs.vertAlign === "subscript") {
112505
+ return { style: "vertical-align: sub; font-size: 65%;" };
112506
+ }
112507
+ if (attrs.vertAlign === "baseline") {
112508
+ return { style: "vertical-align: baseline;" };
112509
+ }
112510
+ return {};
112511
+ },
112512
+ parseDOM: (el) => {
112513
+ const va = el.style?.verticalAlign;
112514
+ if (va === "super") return "superscript";
112515
+ if (va === "sub") return "subscript";
112516
+ if (va === "baseline") return "baseline";
112517
+ return null;
112518
+ }
112519
+ },
112520
+ /**
112521
+ * Custom vertical position offset in points (DOCX w:position).
112522
+ * Numeric value specifying vertical offset (positive raises, negative lowers).
112523
+ * Format: '{number}pt' (e.g., '2pt', '-1.5pt').
112524
+ * Takes precedence over vertAlign when both are present.
112525
+ * Renders as CSS vertical-align with the exact offset value.
112526
+ * @category Attribute
112527
+ * @param {string} [position] - Vertical position offset (e.g., '2pt', '-1pt')
112528
+ */
112529
+ position: {
112530
+ default: null,
112531
+ renderDOM: (attrs) => {
112532
+ if (!attrs.position) return {};
112533
+ return { style: `vertical-align: ${attrs.position};` };
112534
+ },
112535
+ parseDOM: (el) => {
112536
+ const va = el.style?.verticalAlign;
112537
+ if (!va) return null;
112538
+ const numeric = parseFloat(va);
112539
+ if (!Number.isNaN(numeric)) {
112540
+ return `${numeric}pt`;
112541
+ }
112542
+ return null;
112543
+ }
112544
+ }
112313
112545
  };
112314
112546
  },
112315
112547
  addCommands() {
@@ -142960,7 +143192,7 @@ ${reason}`);
142960
143192
  this.config.colors = shuffleArray(this.config.colors);
142961
143193
  this.userColorMap = /* @__PURE__ */ new Map();
142962
143194
  this.colorIndex = 0;
142963
- this.version = "1.2.2-next.1";
143195
+ this.version = "1.3.0-next.2";
142964
143196
  this.#log("🦋 [superdoc] Using SuperDoc version:", this.version);
142965
143197
  this.superdocId = config2.superdocId || v4();
142966
143198
  this.colors = this.config.colors;