@harbour-enterprises/superdoc 1.4.1-next.2 → 1.5.0-next.1

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.
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  const jszip = require("./jszip-C8_CqJxM.cjs");
3
3
  const helpers$1 = require("./helpers-nOdwpmwb.cjs");
4
- const superEditor_converter = require("./SuperConverter-Bukam51x.cjs");
4
+ const superEditor_converter = require("./SuperConverter-BVAV03p6.cjs");
5
5
  const vue = require("./vue-De9wkgLl.cjs");
6
6
  require("./jszip.min-BPh2MMAa.cjs");
7
7
  const eventemitter3 = require("./eventemitter3-BQuRcMPI.cjs");
@@ -15516,7 +15516,7 @@ const canUseDOM = () => {
15516
15516
  return false;
15517
15517
  }
15518
15518
  };
15519
- const summaryVersion = "1.4.1-next.2";
15519
+ const summaryVersion = "1.5.0-next.1";
15520
15520
  const nodeKeys = ["group", "content", "marks", "inline", "atom", "defining", "code", "tableRole", "summary"];
15521
15521
  const markKeys = ["group", "inclusive", "excludes", "spanning", "code"];
15522
15522
  function mapAttributes(attrs) {
@@ -16826,6 +16826,31 @@ class Editor extends EventEmitter {
16826
16826
  }
16827
16827
  return null;
16828
16828
  }
16829
+ /**
16830
+ * Get the DOM element for a document position.
16831
+ * In presentation mode, returns the painted element.
16832
+ */
16833
+ getElementAtPos(pos, options = {}) {
16834
+ if (this.presentationEditor) {
16835
+ return this.presentationEditor.getElementAtPos(pos, options);
16836
+ }
16837
+ if (!this.view) return null;
16838
+ if (!Number.isFinite(pos)) return null;
16839
+ const maxPos = this.view.state.doc.content.size;
16840
+ const clampedPos = Math.max(0, Math.min(pos, maxPos));
16841
+ try {
16842
+ const { node } = this.view.domAtPos(clampedPos);
16843
+ if (node && node.nodeType === 1) {
16844
+ return node;
16845
+ }
16846
+ if (node && node.nodeType === 3) {
16847
+ return node.parentElement;
16848
+ }
16849
+ return node?.parentElement ?? null;
16850
+ } catch {
16851
+ return null;
16852
+ }
16853
+ }
16829
16854
  /**
16830
16855
  * Get position from client-space coordinates.
16831
16856
  * In layout/presentation mode, uses PresentationEditor hit testing for accurate coordinate mapping.
@@ -18148,7 +18173,7 @@ class Editor extends EventEmitter {
18148
18173
  * Process collaboration migrations
18149
18174
  */
18150
18175
  processCollaborationMigrations() {
18151
- console.debug("[checkVersionMigrations] Current editor version", "1.4.1-next.2");
18176
+ console.debug("[checkVersionMigrations] Current editor version", "1.5.0-next.1");
18152
18177
  if (!this.options.ydoc) return;
18153
18178
  const metaMap = this.options.ydoc.getMap("meta");
18154
18179
  let docVersion = metaMap.get("version");
@@ -23172,27 +23197,33 @@ const SDT_CONTAINER_STYLES = `
23172
23197
  align-items: center;
23173
23198
  }
23174
23199
 
23175
- /* Continuation styling: first fragment has top corners, last has bottom corners */
23176
- .superdoc-document-section[data-sdt-container-start="true"] {
23177
- border-radius: 4px 4px 0 0;
23200
+ /* Continuation styling: SDT container boundary handling for multi-fragment document sections */
23201
+ /* Single fragment (both start and end): full border radius */
23202
+ .superdoc-document-section[data-sdt-container-start="true"][data-sdt-container-end="true"] {
23203
+ border-radius: 4px;
23178
23204
  }
23179
23205
 
23180
- .superdoc-document-section[data-sdt-container-end="true"] {
23181
- border-radius: 0 0 4px 4px;
23206
+ /* First fragment of a multi-fragment SDT: top corners, no bottom border */
23207
+ .superdoc-document-section[data-sdt-container-start="true"]:not([data-sdt-container-end="true"]) {
23208
+ border-radius: 4px 4px 0 0;
23209
+ border-bottom: none;
23182
23210
  }
23183
23211
 
23184
- .superdoc-document-section[data-sdt-container-start="true"][data-sdt-container-end="true"] {
23185
- border-radius: 4px;
23212
+ /* Last fragment of a multi-fragment SDT: bottom corners, no top border */
23213
+ .superdoc-document-section[data-sdt-container-end="true"]:not([data-sdt-container-start="true"]) {
23214
+ border-radius: 0 0 4px 4px;
23215
+ border-top: none;
23186
23216
  }
23187
23217
 
23188
23218
  .superdoc-document-section[data-sdt-container-start="true"]:hover {
23189
23219
  border-radius: 0 4px 0 0;
23190
23220
  }
23191
23221
 
23192
- /* Middle fragments have no border radius */
23222
+ /* Middle fragments (neither start nor end): no corners, no top/bottom borders */
23193
23223
  .superdoc-document-section:not([data-sdt-container-start="true"]):not([data-sdt-container-end="true"]) {
23194
23224
  border-radius: 0;
23195
23225
  border-top: none;
23226
+ border-bottom: none;
23196
23227
  }
23197
23228
 
23198
23229
  /* Structured Content Block - Blue border container */
@@ -23239,21 +23270,28 @@ const SDT_CONTAINER_STYLES = `
23239
23270
  }
23240
23271
 
23241
23272
  /* Continuation styling for structured content blocks */
23242
- .superdoc-structured-content-block[data-sdt-container-start="true"] {
23243
- border-radius: 4px 4px 0 0;
23273
+ /* Single fragment (both start and end): full border radius */
23274
+ .superdoc-structured-content-block[data-sdt-container-start="true"][data-sdt-container-end="true"] {
23275
+ border-radius: 4px;
23244
23276
  }
23245
23277
 
23246
- .superdoc-structured-content-block[data-sdt-container-end="true"] {
23247
- border-radius: 0 0 4px 4px;
23278
+ /* First fragment of a multi-fragment SDT: top corners, no bottom border */
23279
+ .superdoc-structured-content-block[data-sdt-container-start="true"]:not([data-sdt-container-end="true"]) {
23280
+ border-radius: 4px 4px 0 0;
23281
+ border-bottom: none;
23248
23282
  }
23249
23283
 
23250
- .superdoc-structured-content-block[data-sdt-container-start="true"][data-sdt-container-end="true"] {
23251
- border-radius: 4px;
23284
+ /* Last fragment of a multi-fragment SDT: bottom corners, no top border */
23285
+ .superdoc-structured-content-block[data-sdt-container-end="true"]:not([data-sdt-container-start="true"]) {
23286
+ border-radius: 0 0 4px 4px;
23287
+ border-top: none;
23252
23288
  }
23253
23289
 
23290
+ /* Middle fragment (neither start nor end): no corners, no top/bottom borders */
23254
23291
  .superdoc-structured-content-block:not([data-sdt-container-start="true"]):not([data-sdt-container-end="true"]) {
23255
23292
  border-radius: 0;
23256
23293
  border-top: none;
23294
+ border-bottom: none;
23257
23295
  }
23258
23296
 
23259
23297
  /* Structured Content Inline - Inline wrapper with blue border */
@@ -23722,6 +23760,78 @@ const resolveTableCellBorders = (tableBorders, rowIndex, colIndex, totalRows, to
23722
23760
  right: borderValueToSpec(isLastCol ? tableBorders?.right : null)
23723
23761
  };
23724
23762
  };
23763
+ function isStructuredContentMetadata(sdt) {
23764
+ return sdt !== null && sdt !== void 0 && typeof sdt === "object" && "type" in sdt && sdt.type === "structuredContent";
23765
+ }
23766
+ function isDocumentSectionMetadata(sdt) {
23767
+ return sdt !== null && sdt !== void 0 && typeof sdt === "object" && "type" in sdt && sdt.type === "documentSection";
23768
+ }
23769
+ function getSdtContainerConfig(sdt) {
23770
+ if (isDocumentSectionMetadata(sdt)) {
23771
+ return {
23772
+ className: "superdoc-document-section",
23773
+ labelText: sdt.title ?? "Document section",
23774
+ labelClassName: "superdoc-document-section__tooltip",
23775
+ isStart: true,
23776
+ isEnd: true
23777
+ };
23778
+ }
23779
+ if (isStructuredContentMetadata(sdt) && sdt.scope === "block") {
23780
+ return {
23781
+ className: "superdoc-structured-content-block",
23782
+ labelText: sdt.alias ?? "Structured content",
23783
+ labelClassName: "superdoc-structured-content__label",
23784
+ isStart: true,
23785
+ isEnd: true
23786
+ };
23787
+ }
23788
+ return null;
23789
+ }
23790
+ function getSdtContainerMetadata(sdt, containerSdt) {
23791
+ if (getSdtContainerConfig(sdt)) return sdt ?? null;
23792
+ if (getSdtContainerConfig(containerSdt)) return containerSdt ?? null;
23793
+ return null;
23794
+ }
23795
+ function getSdtContainerKey(sdt, containerSdt) {
23796
+ const metadata = getSdtContainerMetadata(sdt, containerSdt);
23797
+ if (!metadata) return null;
23798
+ if (metadata.type === "structuredContent") {
23799
+ if (metadata.scope !== "block") return null;
23800
+ if (!metadata.id) {
23801
+ return null;
23802
+ }
23803
+ return `structuredContent:${metadata.id}`;
23804
+ }
23805
+ if (metadata.type === "documentSection") {
23806
+ const sectionId = metadata.id ?? metadata.sdBlockId;
23807
+ if (!sectionId) {
23808
+ return null;
23809
+ }
23810
+ return `documentSection:${sectionId}`;
23811
+ }
23812
+ return null;
23813
+ }
23814
+ function applySdtContainerStyling(doc2, container, sdt, containerSdt, boundaryOptions) {
23815
+ let config = getSdtContainerConfig(sdt);
23816
+ if (!config && containerSdt) {
23817
+ config = getSdtContainerConfig(containerSdt);
23818
+ }
23819
+ if (!config) return;
23820
+ const isStart = boundaryOptions?.isStart ?? config.isStart;
23821
+ const isEnd = boundaryOptions?.isEnd ?? config.isEnd;
23822
+ container.classList.add(config.className);
23823
+ container.dataset.sdtContainerStart = String(isStart);
23824
+ container.dataset.sdtContainerEnd = String(isEnd);
23825
+ container.style.overflow = "visible";
23826
+ if (isStart) {
23827
+ const labelEl = doc2.createElement("div");
23828
+ labelEl.className = config.labelClassName;
23829
+ const labelText = doc2.createElement("span");
23830
+ labelText.textContent = config.labelText;
23831
+ labelEl.appendChild(labelText);
23832
+ container.appendChild(labelEl);
23833
+ }
23834
+ }
23725
23835
  const LIST_MARKER_GAP$3 = 8;
23726
23836
  function renderListMarker(params2) {
23727
23837
  const { doc: doc2, lineEl, markerLayout, markerMeasure, indentLeftPx } = params2;
@@ -23759,6 +23869,54 @@ function renderListMarker(params2) {
23759
23869
  lineContainer.appendChild(lineEl);
23760
23870
  return lineContainer;
23761
23871
  }
23872
+ const applyInlineStyles = (el, styles) => {
23873
+ Object.entries(styles).forEach(([key2, value]) => {
23874
+ if (value != null && value !== "" && key2 in el.style) {
23875
+ el.style[key2] = String(value);
23876
+ }
23877
+ });
23878
+ };
23879
+ const EMBEDDED_TABLE_VERSION = "embedded-table";
23880
+ const renderEmbeddedTable = (params2) => {
23881
+ const { doc: doc2, table, measure, context, renderLine, renderDrawingContent, applySdtDataset } = params2;
23882
+ const fragment = {
23883
+ kind: "table",
23884
+ blockId: table.id,
23885
+ fromRow: 0,
23886
+ toRow: table.rows.length,
23887
+ x: 0,
23888
+ y: 0,
23889
+ width: measure.totalWidth,
23890
+ height: measure.totalHeight
23891
+ };
23892
+ const blockLookup = /* @__PURE__ */ new Map([
23893
+ [
23894
+ table.id,
23895
+ {
23896
+ block: table,
23897
+ measure,
23898
+ version: EMBEDDED_TABLE_VERSION
23899
+ }
23900
+ ]
23901
+ ]);
23902
+ const applyFragmentFrame = (el, frag) => {
23903
+ el.style.left = `${frag.x}px`;
23904
+ el.style.top = `${frag.y}px`;
23905
+ el.style.width = `${frag.width}px`;
23906
+ el.dataset.blockId = frag.blockId;
23907
+ };
23908
+ return renderTableFragment({
23909
+ doc: doc2,
23910
+ fragment,
23911
+ context,
23912
+ blockLookup,
23913
+ renderLine,
23914
+ renderDrawingContent,
23915
+ applyFragmentFrame,
23916
+ applySdtDataset,
23917
+ applyStyles: applyInlineStyles
23918
+ });
23919
+ };
23762
23920
  function applyParagraphBordersAndShading(paraWrapper, block) {
23763
23921
  const borders = block.attrs?.borders;
23764
23922
  if (borders) {
@@ -23799,10 +23957,12 @@ const renderTableCell = (deps) => {
23799
23957
  cellMeasure,
23800
23958
  cell,
23801
23959
  borders,
23960
+ useDefaultBorder,
23802
23961
  renderLine,
23803
23962
  renderDrawingContent,
23804
23963
  context,
23805
23964
  applySdtDataset,
23965
+ tableSdt,
23806
23966
  fromLine,
23807
23967
  toLine
23808
23968
  } = deps;
@@ -23826,12 +23986,46 @@ const renderTableCell = (deps) => {
23826
23986
  cellEl.style.paddingBottom = `${paddingBottom}px`;
23827
23987
  if (borders) {
23828
23988
  applyCellBorders(cellEl, borders);
23989
+ } else if (useDefaultBorder) {
23990
+ cellEl.style.border = "1px solid rgba(0,0,0,0.6)";
23829
23991
  }
23830
23992
  if (cell?.attrs?.background) {
23831
23993
  cellEl.style.backgroundColor = cell.attrs.background;
23832
23994
  }
23833
23995
  const cellBlocks = cell?.blocks ?? (cell?.paragraph ? [cell.paragraph] : []);
23834
23996
  const blockMeasures = cellMeasure?.blocks ?? (cellMeasure?.paragraph ? [cellMeasure.paragraph] : []);
23997
+ const sdtContainerKeys = cellBlocks.map((block) => {
23998
+ if (block.kind !== "paragraph") {
23999
+ return null;
24000
+ }
24001
+ const attrs2 = block.attrs;
24002
+ return getSdtContainerKey(attrs2?.sdt, attrs2?.containerSdt);
24003
+ });
24004
+ const sdtBoundaries = sdtContainerKeys.map((key2, index2) => {
24005
+ if (!key2) return void 0;
24006
+ const prev = index2 > 0 ? sdtContainerKeys[index2 - 1] : null;
24007
+ const next = index2 < sdtContainerKeys.length - 1 ? sdtContainerKeys[index2 + 1] : null;
24008
+ return { isStart: key2 !== prev, isEnd: key2 !== next };
24009
+ });
24010
+ const tableSdtKey = tableSdt ? getSdtContainerKey(tableSdt, null) : null;
24011
+ const shouldApplySdtContainerStyling = (sdt, containerSdt, blockKey) => {
24012
+ const resolvedKey = blockKey ?? getSdtContainerKey(sdt, containerSdt);
24013
+ if (tableSdtKey && resolvedKey && tableSdtKey === resolvedKey) {
24014
+ return false;
24015
+ }
24016
+ if (tableSdt && (sdt === tableSdt || containerSdt === tableSdt)) {
24017
+ return false;
24018
+ }
24019
+ return Boolean(getSdtContainerConfig(sdt) || getSdtContainerConfig(containerSdt));
24020
+ };
24021
+ const hasSdtContainer = cellBlocks.some((block, index2) => {
24022
+ const attrs2 = block.attrs;
24023
+ const blockKey = sdtContainerKeys[index2] ?? null;
24024
+ return shouldApplySdtContainerStyling(attrs2?.sdt, attrs2?.containerSdt, blockKey);
24025
+ });
24026
+ if (hasSdtContainer) {
24027
+ cellEl.style.overflow = "visible";
24028
+ }
23835
24029
  if (cellBlocks.length > 0 && blockMeasures.length > 0) {
23836
24030
  const content = doc2.createElement("div");
23837
24031
  content.style.position = "relative";
@@ -23863,6 +24057,26 @@ const renderTableCell = (deps) => {
23863
24057
  for (let i = 0; i < Math.min(blockMeasures.length, cellBlocks.length); i++) {
23864
24058
  const blockMeasure = blockMeasures[i];
23865
24059
  const block = cellBlocks[i];
24060
+ if (blockMeasure.kind === "table" && block?.kind === "table") {
24061
+ const tableMeasure = blockMeasure;
24062
+ const tableWrapper = doc2.createElement("div");
24063
+ tableWrapper.style.position = "relative";
24064
+ tableWrapper.style.width = "100%";
24065
+ tableWrapper.style.height = `${tableMeasure.totalHeight}px`;
24066
+ tableWrapper.style.boxSizing = "border-box";
24067
+ const tableEl = renderEmbeddedTable({
24068
+ doc: doc2,
24069
+ table: block,
24070
+ measure: tableMeasure,
24071
+ context: { ...context, section: "body" },
24072
+ renderLine,
24073
+ renderDrawingContent,
24074
+ applySdtDataset
24075
+ });
24076
+ tableWrapper.appendChild(tableEl);
24077
+ content.appendChild(tableWrapper);
24078
+ continue;
24079
+ }
23866
24080
  if (blockMeasure.kind === "image" && block?.kind === "image") {
23867
24081
  const imageWrapper = doc2.createElement("div");
23868
24082
  imageWrapper.style.position = "relative";
@@ -23958,6 +24172,11 @@ const renderTableCell = (deps) => {
23958
24172
  paraWrapper.style.left = "0";
23959
24173
  paraWrapper.style.width = "100%";
23960
24174
  applySdtDataset(paraWrapper, block.attrs?.sdt);
24175
+ const sdtBoundary = sdtBoundaries[i];
24176
+ const blockKey = sdtContainerKeys[i] ?? null;
24177
+ if (shouldApplySdtContainerStyling(block.attrs?.sdt, block.attrs?.containerSdt, blockKey)) {
24178
+ applySdtContainerStyling(doc2, paraWrapper, block.attrs?.sdt, block.attrs?.containerSdt, sdtBoundary);
24179
+ }
23961
24180
  applyParagraphBordersAndShading(paraWrapper, block);
23962
24181
  applyParagraphBorderStyles(paraWrapper, block.attrs?.borders);
23963
24182
  applyParagraphShadingStyles(paraWrapper, block.attrs?.shading);
@@ -24032,8 +24251,6 @@ const renderTableCell = (deps) => {
24032
24251
  }
24033
24252
  }
24034
24253
  cumulativeLineCount += blockLineCount;
24035
- } else {
24036
- cumulativeLineCount += 0;
24037
24254
  }
24038
24255
  }
24039
24256
  }
@@ -24055,6 +24272,7 @@ const renderTableRow = (deps) => {
24055
24272
  renderLine,
24056
24273
  renderDrawingContent,
24057
24274
  applySdtDataset,
24275
+ tableSdt,
24058
24276
  continuesFromPrev,
24059
24277
  continuesOnNext,
24060
24278
  partialRow
@@ -24149,60 +24367,18 @@ const renderTableRow = (deps) => {
24149
24367
  cellMeasure,
24150
24368
  cell,
24151
24369
  borders: resolvedBorders,
24370
+ useDefaultBorder: false,
24152
24371
  renderLine,
24153
24372
  renderDrawingContent,
24154
24373
  context,
24155
24374
  applySdtDataset,
24375
+ tableSdt,
24156
24376
  fromLine,
24157
24377
  toLine
24158
24378
  });
24159
24379
  container.appendChild(cellElement);
24160
24380
  }
24161
24381
  };
24162
- function isStructuredContentMetadata(sdt) {
24163
- return sdt !== null && sdt !== void 0 && typeof sdt === "object" && "type" in sdt && sdt.type === "structuredContent";
24164
- }
24165
- function isDocumentSectionMetadata(sdt) {
24166
- return sdt !== null && sdt !== void 0 && typeof sdt === "object" && "type" in sdt && sdt.type === "documentSection";
24167
- }
24168
- function getSdtContainerConfig(sdt) {
24169
- if (isDocumentSectionMetadata(sdt)) {
24170
- return {
24171
- className: "superdoc-document-section",
24172
- labelText: sdt.title ?? "Document section",
24173
- labelClassName: "superdoc-document-section__tooltip",
24174
- isStart: true,
24175
- isEnd: true
24176
- };
24177
- }
24178
- if (isStructuredContentMetadata(sdt) && sdt.scope === "block") {
24179
- return {
24180
- className: "superdoc-structured-content-block",
24181
- labelText: sdt.alias ?? "Structured content",
24182
- labelClassName: "superdoc-structured-content__label",
24183
- isStart: true,
24184
- isEnd: true
24185
- };
24186
- }
24187
- return null;
24188
- }
24189
- function applySdtContainerStyling(doc2, container, sdt, containerSdt) {
24190
- let config = getSdtContainerConfig(sdt);
24191
- if (!config && containerSdt) {
24192
- config = getSdtContainerConfig(containerSdt);
24193
- }
24194
- if (!config) return;
24195
- container.classList.add(config.className);
24196
- container.dataset.sdtContainerStart = String(config.isStart);
24197
- container.dataset.sdtContainerEnd = String(config.isEnd);
24198
- container.style.overflow = "visible";
24199
- const labelEl = doc2.createElement("div");
24200
- labelEl.className = config.labelClassName;
24201
- const labelText = doc2.createElement("span");
24202
- labelText.textContent = config.labelText;
24203
- labelEl.appendChild(labelText);
24204
- container.appendChild(labelEl);
24205
- }
24206
24382
  const renderTableFragment = (deps) => {
24207
24383
  const {
24208
24384
  doc: doc2,
@@ -24346,6 +24522,7 @@ const renderTableFragment = (deps) => {
24346
24522
  renderLine,
24347
24523
  renderDrawingContent,
24348
24524
  applySdtDataset,
24525
+ tableSdt: block.attrs?.sdt ?? null,
24349
24526
  // Headers are always rendered as-is (no border suppression)
24350
24527
  continuesFromPrev: false,
24351
24528
  continuesOnNext: false
@@ -24376,6 +24553,7 @@ const renderTableFragment = (deps) => {
24376
24553
  renderLine,
24377
24554
  renderDrawingContent,
24378
24555
  applySdtDataset,
24556
+ tableSdt: block.attrs?.sdt ?? null,
24379
24557
  // Draw top border if table continues from previous fragment (MS Word behavior)
24380
24558
  continuesFromPrev: isFirstRenderedBodyRow && fragment.continuesFromPrev === true,
24381
24559
  // Draw bottom border if table continues on next fragment (MS Word behavior)
@@ -25627,8 +25805,10 @@ class DomPainter {
25627
25805
  section: "body",
25628
25806
  pageNumberText: page.numberText
25629
25807
  };
25630
- page.fragments.forEach((fragment) => {
25631
- el.appendChild(this.renderFragment(fragment, contextBase));
25808
+ const sdtBoundaries = computeSdtBoundaries(page.fragments, this.blockLookup);
25809
+ page.fragments.forEach((fragment, index2) => {
25810
+ const sdtBoundary = sdtBoundaries.get(index2);
25811
+ el.appendChild(this.renderFragment(fragment, contextBase, sdtBoundary));
25632
25812
  });
25633
25813
  this.renderDecorationsForPage(el, page);
25634
25814
  return el;
@@ -25859,6 +26039,7 @@ class DomPainter {
25859
26039
  pageEl.dataset.layoutEpoch = String(this.layoutEpoch);
25860
26040
  const existing = new Map(state.fragments.map((frag) => [frag.key, frag]));
25861
26041
  const nextFragments = [];
26042
+ const sdtBoundaries = computeSdtBoundaries(page.fragments, this.blockLookup);
25862
26043
  const contextBase = {
25863
26044
  pageNumber: page.number,
25864
26045
  totalPages: this.totalPages,
@@ -25868,11 +26049,13 @@ class DomPainter {
25868
26049
  page.fragments.forEach((fragment, index2) => {
25869
26050
  const key2 = fragmentKey(fragment);
25870
26051
  const current = existing.get(key2);
26052
+ const sdtBoundary = sdtBoundaries.get(index2);
25871
26053
  if (current) {
25872
26054
  existing.delete(key2);
25873
- const needsRebuild = this.changedBlocks.has(fragment.blockId) || current.signature !== fragmentSignature(fragment, this.blockLookup);
26055
+ const sdtBoundaryMismatch = shouldRebuildForSdtBoundary(current.element, sdtBoundary);
26056
+ const needsRebuild = this.changedBlocks.has(fragment.blockId) || current.signature !== fragmentSignature(fragment, this.blockLookup) || sdtBoundaryMismatch;
25874
26057
  if (needsRebuild) {
25875
- const replacement = this.renderFragment(fragment, contextBase);
26058
+ const replacement = this.renderFragment(fragment, contextBase, sdtBoundary);
25876
26059
  pageEl.replaceChild(replacement, current.element);
25877
26060
  current.element = replacement;
25878
26061
  current.signature = fragmentSignature(fragment, this.blockLookup);
@@ -25886,7 +26069,7 @@ class DomPainter {
25886
26069
  nextFragments.push(current);
25887
26070
  return;
25888
26071
  }
25889
- const fresh = this.renderFragment(fragment, contextBase);
26072
+ const fresh = this.renderFragment(fragment, contextBase, sdtBoundary);
25890
26073
  pageEl.insertBefore(fresh, pageEl.children[index2] ?? null);
25891
26074
  nextFragments.push({
25892
26075
  key: key2,
@@ -25958,8 +26141,10 @@ class DomPainter {
25958
26141
  totalPages: this.totalPages,
25959
26142
  section: "body"
25960
26143
  };
25961
- const fragments = page.fragments.map((fragment) => {
25962
- const fragmentEl = this.renderFragment(fragment, contextBase);
26144
+ const sdtBoundaries = computeSdtBoundaries(page.fragments, this.blockLookup);
26145
+ const fragments = page.fragments.map((fragment, index2) => {
26146
+ const sdtBoundary = sdtBoundaries.get(index2);
26147
+ const fragmentEl = this.renderFragment(fragment, contextBase, sdtBoundary);
25963
26148
  el.appendChild(fragmentEl);
25964
26149
  return {
25965
26150
  key: fragmentKey(fragment),
@@ -25979,12 +26164,12 @@ class DomPainter {
25979
26164
  }
25980
26165
  return this.options.pageStyles;
25981
26166
  }
25982
- renderFragment(fragment, context) {
26167
+ renderFragment(fragment, context, sdtBoundary) {
25983
26168
  if (fragment.kind === "para") {
25984
- return this.renderParagraphFragment(fragment, context);
26169
+ return this.renderParagraphFragment(fragment, context, sdtBoundary);
25985
26170
  }
25986
26171
  if (fragment.kind === "list-item") {
25987
- return this.renderListItemFragment(fragment, context);
26172
+ return this.renderListItemFragment(fragment, context, sdtBoundary);
25988
26173
  }
25989
26174
  if (fragment.kind === "image") {
25990
26175
  return this.renderImageFragment(fragment, context);
@@ -26003,9 +26188,10 @@ class DomPainter {
26003
26188
  *
26004
26189
  * @param fragment - The paragraph fragment to render
26005
26190
  * @param context - Rendering context with page and column information
26191
+ * @param sdtBoundary - Optional SDT boundary overrides for multi-fragment containers
26006
26192
  * @returns HTMLElement containing the rendered fragment or error placeholder
26007
26193
  */
26008
- renderParagraphFragment(fragment, context) {
26194
+ renderParagraphFragment(fragment, context, sdtBoundary) {
26009
26195
  try {
26010
26196
  const lookup = this.blockLookup.get(fragment.blockId);
26011
26197
  if (!lookup || lookup.block.kind !== "paragraph" || lookup.measure.kind !== "paragraph") {
@@ -26052,7 +26238,7 @@ class DomPainter {
26052
26238
  }
26053
26239
  this.applySdtDataset(fragmentEl, block.attrs?.sdt);
26054
26240
  this.applyContainerSdtDataset(fragmentEl, block.attrs?.containerSdt);
26055
- applySdtContainerStyling(this.doc, fragmentEl, block.attrs?.sdt, block.attrs?.containerSdt);
26241
+ applySdtContainerStyling(this.doc, fragmentEl, block.attrs?.sdt, block.attrs?.containerSdt, sdtBoundary);
26056
26242
  const dropCapDescriptor = block.attrs?.dropCapDescriptor;
26057
26243
  const dropCapMeasure = measure.dropCap;
26058
26244
  if (dropCapDescriptor && dropCapMeasure && !fragment.continuesFromPrev) {
@@ -26366,7 +26552,7 @@ class DomPainter {
26366
26552
  }
26367
26553
  return dropCapEl;
26368
26554
  }
26369
- renderListItemFragment(fragment, context) {
26555
+ renderListItemFragment(fragment, context, sdtBoundary) {
26370
26556
  try {
26371
26557
  const lookup = this.blockLookup.get(fragment.blockId);
26372
26558
  if (!lookup || lookup.block.kind !== "list" || lookup.measure.kind !== "list") {
@@ -26392,7 +26578,13 @@ class DomPainter {
26392
26578
  fragmentEl.dataset.itemId = fragment.itemId;
26393
26579
  const paragraphMetadata = item.paragraph.attrs?.sdt;
26394
26580
  this.applySdtDataset(fragmentEl, paragraphMetadata);
26395
- applySdtContainerStyling(this.doc, fragmentEl, paragraphMetadata, item.paragraph.attrs?.containerSdt);
26581
+ applySdtContainerStyling(
26582
+ this.doc,
26583
+ fragmentEl,
26584
+ paragraphMetadata,
26585
+ item.paragraph.attrs?.containerSdt,
26586
+ sdtBoundary
26587
+ );
26396
26588
  if (fragment.continuesFromPrev) {
26397
26589
  fragmentEl.dataset.continuesFromPrev = "true";
26398
26590
  }
@@ -28322,6 +28514,46 @@ class DomPainter {
28322
28514
  }
28323
28515
  }
28324
28516
  }
28517
+ const getFragmentSdtContainerKey = (fragment, blockLookup) => {
28518
+ const lookup = blockLookup.get(fragment.blockId);
28519
+ if (!lookup) return null;
28520
+ const block = lookup.block;
28521
+ if (fragment.kind === "para" && block.kind === "paragraph") {
28522
+ const attrs = block.attrs;
28523
+ return getSdtContainerKey(attrs?.sdt, attrs?.containerSdt);
28524
+ }
28525
+ if (fragment.kind === "list-item" && block.kind === "list") {
28526
+ const item = block.items.find((listItem) => listItem.id === fragment.itemId);
28527
+ const attrs = item?.paragraph.attrs;
28528
+ return getSdtContainerKey(attrs?.sdt, attrs?.containerSdt);
28529
+ }
28530
+ return null;
28531
+ };
28532
+ const computeSdtBoundaries = (fragments, blockLookup) => {
28533
+ const boundaries = /* @__PURE__ */ new Map();
28534
+ const containerKeys = fragments.map((fragment) => getFragmentSdtContainerKey(fragment, blockLookup));
28535
+ for (let i = 0; i < fragments.length; i += 1) {
28536
+ const currentKey = containerKeys[i];
28537
+ if (!currentKey) continue;
28538
+ const prev = i > 0 ? containerKeys[i - 1] : null;
28539
+ const next = i < fragments.length - 1 ? containerKeys[i + 1] : null;
28540
+ const isStart = currentKey !== prev;
28541
+ const isEnd = currentKey !== next;
28542
+ boundaries.set(i, { isStart, isEnd });
28543
+ }
28544
+ return boundaries;
28545
+ };
28546
+ const shouldRebuildForSdtBoundary = (element, boundary) => {
28547
+ if (!boundary) return false;
28548
+ const startAttr = element.dataset.sdtContainerStart;
28549
+ const endAttr = element.dataset.sdtContainerEnd;
28550
+ const expectedStart = String(boundary.isStart ?? true);
28551
+ const expectedEnd = String(boundary.isEnd ?? true);
28552
+ if (startAttr === void 0 || endAttr === void 0) {
28553
+ return true;
28554
+ }
28555
+ return startAttr !== expectedStart || endAttr !== expectedEnd;
28556
+ };
28325
28557
  const fragmentKey = (fragment) => {
28326
28558
  if (fragment.kind === "para") {
28327
28559
  return `para:${fragment.blockId}:${fragment.fromLine}:${fragment.toLine}`;
@@ -33991,8 +34223,15 @@ function layoutDocument(blocks, measures, options = {}) {
33991
34223
  const paraBlock = block;
33992
34224
  const isEmpty2 = !paraBlock.runs || paraBlock.runs.length === 0 || paraBlock.runs.length === 1 && (!paraBlock.runs[0].kind || paraBlock.runs[0].kind === "text") && (!paraBlock.runs[0].text || paraBlock.runs[0].text === "");
33993
34225
  if (isEmpty2) {
34226
+ const isSectPrMarker = paraBlock.attrs?.sectPrMarker === true;
33994
34227
  const prevBlock = index2 > 0 ? blocks[index2 - 1] : null;
33995
34228
  const nextBlock = index2 < blocks.length - 1 ? blocks[index2 + 1] : null;
34229
+ const nextSectionBreak = nextBlock?.kind === "sectionBreak" ? nextBlock : null;
34230
+ const nextBreakType = nextSectionBreak?.type ?? (nextSectionBreak?.attrs?.source === "sectPr" ? "nextPage" : void 0);
34231
+ const nextBreakForcesPage = nextSectionBreak && (nextBreakType === "nextPage" || nextBreakType === "evenPage" || nextBreakType === "oddPage" || nextSectionBreak.attrs?.requirePageBoundary === true);
34232
+ if (isSectPrMarker && nextBreakForcesPage) {
34233
+ continue;
34234
+ }
33996
34235
  if (prevBlock?.kind === "pageBreak" && nextBlock?.kind === "sectionBreak") {
33997
34236
  continue;
33998
34237
  }
@@ -41726,16 +41965,6 @@ function tokenNodeToRun(node, positions, defaultFont, defaultSize, inheritedMark
41726
41965
  if (marksAsAttrs.length > 0) {
41727
41966
  run._explicitFont = true;
41728
41967
  }
41729
- console.debug("[token-debug] tokenNodeToRun", {
41730
- token,
41731
- fontFamily: run.fontFamily,
41732
- fontSize: run.fontSize,
41733
- defaultFont,
41734
- defaultSize,
41735
- nodeMarksCount: nodeMarks.length,
41736
- marksAsAttrsCount: marksAsAttrs.length,
41737
- inheritedMarksCount: inheritedMarks?.length ?? 0
41738
- });
41739
41968
  return run;
41740
41969
  }
41741
41970
  const EIGHTHS_PER_POINT = 8;
@@ -44131,6 +44360,16 @@ function applySdtMetadataToTableBlock(tableBlock, metadata) {
44131
44360
  table.attrs.sdt = metadata;
44132
44361
  table.rows?.forEach((row) => {
44133
44362
  row.cells?.forEach((cell) => {
44363
+ const cellBlocks = cell.blocks;
44364
+ if (cellBlocks && cellBlocks.length > 0) {
44365
+ applySdtMetadataToParagraphBlocks(cellBlocks, metadata);
44366
+ cellBlocks.forEach((block) => {
44367
+ if (block.kind === "table") {
44368
+ applySdtMetadataToTableBlock(block, metadata);
44369
+ }
44370
+ });
44371
+ return;
44372
+ }
44134
44373
  if (cell.paragraph) {
44135
44374
  applySdtMetadataToParagraphBlocks([cell.paragraph], metadata);
44136
44375
  }
@@ -45354,6 +45593,10 @@ function paragraphToFlowBlocks$1(para, nextBlockId, positions, defaultFont, defa
45354
45593
  }
45355
45594
  const linkedStyleResolver = createLinkedStyleResolver(converterContext?.linkedStyles);
45356
45595
  const blocks = [];
45596
+ const paraAttrs = para.attrs ?? {};
45597
+ const rawParagraphProps = typeof paraAttrs.paragraphProperties === "object" && paraAttrs.paragraphProperties !== null ? paraAttrs.paragraphProperties : void 0;
45598
+ const hasSectPr2 = Boolean(rawParagraphProps?.sectPr);
45599
+ const isSectPrMarker = hasSectPr2 || paraAttrs.pageBreakSource === "sectPr";
45357
45600
  if (hasPageBreakBefore(para)) {
45358
45601
  blocks.push({
45359
45602
  kind: "pageBreak",
@@ -45375,11 +45618,19 @@ function paragraphToFlowBlocks$1(para, nextBlockId, positions, defaultFont, defa
45375
45618
  emptyRun.pmStart = paraPos.start + 1;
45376
45619
  emptyRun.pmEnd = paraPos.start + 1;
45377
45620
  }
45621
+ let emptyParagraphAttrs = cloneParagraphAttrs(paragraphAttrs);
45622
+ if (isSectPrMarker) {
45623
+ if (emptyParagraphAttrs) {
45624
+ emptyParagraphAttrs.sectPrMarker = true;
45625
+ } else {
45626
+ emptyParagraphAttrs = { sectPrMarker: true };
45627
+ }
45628
+ }
45378
45629
  blocks.push({
45379
45630
  kind: "paragraph",
45380
45631
  id: baseBlockId,
45381
45632
  runs: [emptyRun],
45382
- attrs: cloneParagraphAttrs(paragraphAttrs)
45633
+ attrs: emptyParagraphAttrs
45383
45634
  });
45384
45635
  return blocks;
45385
45636
  }
@@ -45633,14 +45884,6 @@ function paragraphToFlowBlocks$1(para, nextBlockId, positions, defaultFont, defa
45633
45884
  enableComments
45634
45885
  );
45635
45886
  }
45636
- console.debug("[token-debug] paragraph-token-run", {
45637
- token: tokenRun.token,
45638
- fontFamily: tokenRun.fontFamily,
45639
- fontSize: tokenRun.fontSize,
45640
- inlineStyleId,
45641
- runStyleId: activeRunStyleId,
45642
- mergedMarksCount: mergedMarks.length
45643
- });
45644
45887
  applyInlineRunProperties(tokenRun, activeRunProperties);
45645
45888
  currentRuns.push(tokenRun);
45646
45889
  }
@@ -46296,7 +46539,19 @@ const parseTableCell = (args) => {
46296
46539
  ...cellBackgroundColor && { backgroundColor: cellBackgroundColor }
46297
46540
  } : context.converterContext;
46298
46541
  const paragraphToFlowBlocks2 = context.converters?.paragraphToFlowBlocks ?? context.paragraphToFlowBlocks;
46542
+ const tableNodeToBlock2 = context.converters?.tableNodeToBlock;
46299
46543
  const listCounterContext = context.listCounterContext;
46544
+ const appendParagraphBlocks = (paragraphBlocks, sdtMetadata) => {
46545
+ applySdtMetadataToParagraphBlocks(
46546
+ paragraphBlocks.filter((block) => block.kind === "paragraph"),
46547
+ sdtMetadata
46548
+ );
46549
+ paragraphBlocks.forEach((block) => {
46550
+ if (block.kind === "paragraph" || block.kind === "image" || block.kind === "drawing") {
46551
+ blocks.push(block);
46552
+ }
46553
+ });
46554
+ };
46300
46555
  for (const childNode of cellNode.content) {
46301
46556
  if (childNode.type === "paragraph") {
46302
46557
  if (!paragraphToFlowBlocks2) continue;
@@ -46314,11 +46569,75 @@ const parseTableCell = (args) => {
46314
46569
  context.themeColors,
46315
46570
  cellConverterContext
46316
46571
  );
46317
- paragraphBlocks.forEach((block) => {
46318
- if (block.kind === "paragraph" || block.kind === "image" || block.kind === "drawing") {
46319
- blocks.push(block);
46572
+ appendParagraphBlocks(paragraphBlocks);
46573
+ continue;
46574
+ }
46575
+ if (childNode.type === "structuredContentBlock" && Array.isArray(childNode.content)) {
46576
+ const structuredContentMetadata = resolveNodeSdtMetadata(childNode, "structuredContentBlock");
46577
+ for (const nestedNode of childNode.content) {
46578
+ if (nestedNode.type === "paragraph") {
46579
+ if (!paragraphToFlowBlocks2) continue;
46580
+ const paragraphBlocks = paragraphToFlowBlocks2(
46581
+ nestedNode,
46582
+ context.nextBlockId,
46583
+ context.positions,
46584
+ context.defaultFont,
46585
+ context.defaultSize,
46586
+ context.styleContext,
46587
+ listCounterContext,
46588
+ context.trackedChanges,
46589
+ context.bookmarks,
46590
+ context.hyperlinkConfig,
46591
+ context.themeColors,
46592
+ cellConverterContext
46593
+ );
46594
+ appendParagraphBlocks(paragraphBlocks, structuredContentMetadata);
46595
+ continue;
46320
46596
  }
46321
- });
46597
+ if (nestedNode.type === "table" && tableNodeToBlock2) {
46598
+ const tableBlock = tableNodeToBlock2(
46599
+ nestedNode,
46600
+ context.nextBlockId,
46601
+ context.positions,
46602
+ context.defaultFont,
46603
+ context.defaultSize,
46604
+ context.styleContext,
46605
+ context.trackedChanges,
46606
+ context.bookmarks,
46607
+ context.hyperlinkConfig,
46608
+ context.themeColors,
46609
+ paragraphToFlowBlocks2,
46610
+ context.converterContext,
46611
+ { listCounterContext, converters: context.converters }
46612
+ );
46613
+ if (tableBlock && tableBlock.kind === "table") {
46614
+ applySdtMetadataToTableBlock(tableBlock, structuredContentMetadata);
46615
+ blocks.push(tableBlock);
46616
+ }
46617
+ continue;
46618
+ }
46619
+ }
46620
+ continue;
46621
+ }
46622
+ if (childNode.type === "table" && tableNodeToBlock2) {
46623
+ const tableBlock = tableNodeToBlock2(
46624
+ childNode,
46625
+ context.nextBlockId,
46626
+ context.positions,
46627
+ context.defaultFont,
46628
+ context.defaultSize,
46629
+ context.styleContext,
46630
+ context.trackedChanges,
46631
+ context.bookmarks,
46632
+ context.hyperlinkConfig,
46633
+ context.themeColors,
46634
+ paragraphToFlowBlocks2,
46635
+ context.converterContext,
46636
+ { listCounterContext, converters: context.converters }
46637
+ );
46638
+ if (tableBlock && tableBlock.kind === "table") {
46639
+ blocks.push(tableBlock);
46640
+ }
46322
46641
  continue;
46323
46642
  }
46324
46643
  if (childNode.type === "image" && context.converters?.imageNodeToBlock) {
@@ -46382,6 +46701,7 @@ const parseTableCell = (args) => {
46382
46701
  if (drawingBlock && drawingBlock.kind === "drawing") {
46383
46702
  blocks.push(drawingBlock);
46384
46703
  }
46704
+ continue;
46385
46705
  }
46386
46706
  }
46387
46707
  if (blocks.length === 0) {
@@ -51925,6 +52245,39 @@ class PresentationEditor extends EventEmitter {
51925
52245
  height: rect.height
51926
52246
  };
51927
52247
  }
52248
+ /**
52249
+ * Get the painted DOM element that contains a document position (body only).
52250
+ *
52251
+ * Uses the DomPositionIndex which maps data-pm-start/end attributes to rendered
52252
+ * elements. Returns null when the position is not currently mounted (virtualization)
52253
+ * or when in header/footer mode.
52254
+ *
52255
+ * @param pos - Document position in the active editor
52256
+ * @param options.forceRebuild - Rebuild the index before lookup
52257
+ * @param options.fallbackToCoords - Use elementFromPoint with layout rects if index lookup fails
52258
+ * @returns The nearest painted DOM element for the position, or null if unavailable
52259
+ */
52260
+ getElementAtPos(pos, options = {}) {
52261
+ if (!Number.isFinite(pos)) return null;
52262
+ if (!this.#painterHost) return null;
52263
+ if (this.#session.mode !== "body") return null;
52264
+ if (options.forceRebuild || this.#domPositionIndex.size === 0) {
52265
+ this.#rebuildDomPositionIndex();
52266
+ }
52267
+ const indexed = this.#domPositionIndex.findElementAtPosition(pos);
52268
+ if (indexed) return indexed;
52269
+ if (!options.fallbackToCoords) return null;
52270
+ const rects = this.getRangeRects(pos, pos);
52271
+ if (!rects.length) return null;
52272
+ const doc2 = this.#visibleHost.ownerDocument ?? document;
52273
+ for (const rect of rects) {
52274
+ const el = doc2.elementFromPoint(rect.left + rect.width / 2, rect.top + rect.height / 2);
52275
+ if (el instanceof HTMLElement && this.#painterHost.contains(el)) {
52276
+ return el.closest("[data-pm-start][data-pm-end]") ?? el;
52277
+ }
52278
+ }
52279
+ return null;
52280
+ }
51928
52281
  /**
51929
52282
  * Scroll the visible host so a given document position is brought into view.
51930
52283
  *