@harbour-enterprises/superdoc 0.22.0-next.1 → 0.22.0-next.10

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 (47) hide show
  1. package/dist/chunks/{PdfViewer-BpwMPbUj.es.js → PdfViewer-CJdQmuIm.es.js} +1 -1
  2. package/dist/chunks/{PdfViewer-B3KmcDup.cjs → PdfViewer-DE1NR4Ve.cjs} +1 -1
  3. package/dist/chunks/{index-Cw4YywoD.es.js → index-B9sHxXr_.es.js} +53 -26
  4. package/dist/chunks/{index-BOf6E2I4.cjs → index-nfoifSpX.cjs} +53 -26
  5. package/dist/chunks/{super-editor.es-DHDx2fsy.cjs → super-editor.es-DAP-fnHo.cjs} +2264 -1648
  6. package/dist/chunks/{super-editor.es-vfoWxyZL.es.js → super-editor.es-_iVPQ8J8.es.js} +2264 -1648
  7. package/dist/core/SuperDoc.d.ts +5 -0
  8. package/dist/core/SuperDoc.d.ts.map +1 -1
  9. package/dist/core/types/index.d.ts +4 -4
  10. package/dist/core/types/index.d.ts.map +1 -1
  11. package/dist/stores/comments-store.d.ts +4 -1
  12. package/dist/stores/comments-store.d.ts.map +1 -1
  13. package/dist/style.css +48 -44
  14. package/dist/super-editor/ai-writer.es.js +2 -2
  15. package/dist/super-editor/chunks/{converter-BcqEfCTg.js → converter-DK1NMJZB.js} +439 -169
  16. package/dist/super-editor/chunks/{docx-zipper-DZ9ph0iQ.js → docx-zipper-CmK8TyNb.js} +73 -12
  17. package/dist/super-editor/chunks/{editor-BC2sSIVa.js → editor-YR4uV-dp.js} +1902 -1607
  18. package/dist/super-editor/chunks/{toolbar-DNTo5DDf.js → toolbar-DzJyRvb0.js} +2 -2
  19. package/dist/super-editor/converter.es.js +1 -1
  20. package/dist/super-editor/docx-zipper.es.js +2 -2
  21. package/dist/super-editor/editor.es.js +3 -3
  22. package/dist/super-editor/file-zipper.es.js +1 -1
  23. package/dist/super-editor/src/core/DocxZipper.d.ts +1 -1
  24. package/dist/super-editor/src/core/super-converter/SuperConverter.d.ts +1 -13
  25. package/dist/super-editor/src/core/super-converter/exporter.d.ts +1 -0
  26. package/dist/super-editor/src/core/super-converter/helpers/tableFallbackHelpers.d.ts +24 -0
  27. package/dist/super-editor/src/extensions/custom-selection/custom-selection.d.ts +5 -1
  28. package/dist/super-editor/src/extensions/index.d.ts +2 -1
  29. package/dist/super-editor/src/extensions/structured-content/index.d.ts +1 -0
  30. package/dist/super-editor/src/extensions/structured-content/structured-content-commands.d.ts +67 -0
  31. package/dist/super-editor/src/extensions/structured-content/structuredContentHelpers/getStructuredContentBlockTags.d.ts +7 -0
  32. package/dist/super-editor/src/extensions/structured-content/structuredContentHelpers/getStructuredContentInlineTags.d.ts +7 -0
  33. package/dist/super-editor/src/extensions/structured-content/structuredContentHelpers/getStructuredContentTags.d.ts +7 -0
  34. package/dist/super-editor/src/extensions/structured-content/structuredContentHelpers/getStructuredContentTagsById.d.ts +8 -0
  35. package/dist/super-editor/src/extensions/structured-content/structuredContentHelpers/index.d.ts +4 -0
  36. package/dist/super-editor/src/utils/contextmenu-helpers.d.ts +24 -0
  37. package/dist/super-editor/style.css +4 -0
  38. package/dist/super-editor/super-editor.es.js +8 -16
  39. package/dist/super-editor/toolbar.es.js +2 -2
  40. package/dist/super-editor.cjs +1 -1
  41. package/dist/super-editor.es.js +1 -1
  42. package/dist/superdoc.cjs +2 -2
  43. package/dist/superdoc.es.js +2 -2
  44. package/dist/superdoc.umd.js +1665 -1022
  45. package/dist/superdoc.umd.js.map +1 -1
  46. package/package.json +1 -1
  47. package/dist/super-editor/src/components/slash-menu/contextmenu-helpers.d.ts +0 -1
@@ -24856,17 +24856,16 @@ const getParagraphSpacing = (node, docx, styleId = "", marks = [], options = {})
24856
24856
  };
24857
24857
  const getDefaultParagraphStyle = (docx, styleId = "") => {
24858
24858
  const styles = docx["word/styles.xml"];
24859
- if (!styles) {
24859
+ const rootElements = styles?.elements?.[0]?.elements;
24860
+ if (!rootElements?.length) {
24860
24861
  return {};
24861
24862
  }
24862
- const defaults = styles.elements[0].elements?.find((el) => el.name === "w:docDefaults");
24863
- const pDefault = defaults.elements.find((el) => el.name === "w:pPrDefault");
24863
+ const defaults = rootElements.find((el) => el.name === "w:docDefaults");
24864
+ const pDefault = defaults?.elements?.find((el) => el.name === "w:pPrDefault") || {};
24864
24865
  const pPrDefault = pDefault?.elements?.find((el) => el.name === "w:pPr");
24865
24866
  const pPrDefaultSpacingTag = pPrDefault?.elements?.find((el) => el.name === "w:spacing") || {};
24866
24867
  const pPrDefaultIndentTag = pPrDefault?.elements?.find((el) => el.name === "w:ind") || {};
24867
- const stylesNormal = styles.elements[0].elements?.find(
24868
- (el) => el.name === "w:style" && el.attributes["w:styleId"] === "Normal"
24869
- );
24868
+ const stylesNormal = rootElements.find((el) => el.name === "w:style" && el.attributes["w:styleId"] === "Normal");
24870
24869
  const pPrNormal = stylesNormal?.elements?.find((el) => el.name === "w:pPr");
24871
24870
  const pPrNormalSpacingTag = pPrNormal?.elements?.find((el) => el.name === "w:spacing") || {};
24872
24871
  const pPrNormalIndentTag = pPrNormal?.elements?.find((el) => el.name === "w:ind") || {};
@@ -24875,9 +24874,7 @@ const getDefaultParagraphStyle = (docx, styleId = "") => {
24875
24874
  let pPrStyleIdIndentTag = {};
24876
24875
  let pPrStyleJc = {};
24877
24876
  if (styleId) {
24878
- const stylesById = styles.elements[0].elements?.find(
24879
- (el) => el.name === "w:style" && el.attributes["w:styleId"] === styleId
24880
- );
24877
+ const stylesById = rootElements.find((el) => el.name === "w:style" && el.attributes["w:styleId"] === styleId);
24881
24878
  const pPrById = stylesById?.elements?.find((el) => el.name === "w:pPr");
24882
24879
  pPrStyleIdSpacingTag = pPrById?.elements?.find((el) => el.name === "w:spacing") || {};
24883
24880
  pPrStyleIdIndentTag = pPrById?.elements?.find((el) => el.name === "w:ind") || {};
@@ -27180,6 +27177,68 @@ const config$a = {
27180
27177
  decode: decode$h
27181
27178
  };
27182
27179
  const translator$a = NodeTranslator.from(config$a);
27180
+ const DEFAULT_PAGE_WIDTH_TWIPS = 12240;
27181
+ const DEFAULT_PAGE_MARGIN_TWIPS = 1440;
27182
+ const DEFAULT_CONTENT_WIDTH_TWIPS = DEFAULT_PAGE_WIDTH_TWIPS - 2 * DEFAULT_PAGE_MARGIN_TWIPS;
27183
+ const MIN_COLUMN_WIDTH_TWIPS = pixelsToTwips(10);
27184
+ const pctToPercent = (value) => {
27185
+ if (value == null) return null;
27186
+ return value / 50;
27187
+ };
27188
+ const resolveContentWidthTwips = () => DEFAULT_CONTENT_WIDTH_TWIPS;
27189
+ const resolveMeasurementWidthPx = (measurement) => {
27190
+ if (!measurement || typeof measurement.value !== "number" || measurement.value <= 0) return null;
27191
+ const { value, type: type2 } = measurement;
27192
+ if (!type2 || type2 === "auto") return null;
27193
+ if (type2 === "dxa") return twipsToPixels(value);
27194
+ if (type2 === "pct") {
27195
+ const percent2 = pctToPercent(value);
27196
+ if (percent2 == null || percent2 <= 0) return null;
27197
+ const widthTwips = resolveContentWidthTwips() * percent2 / 100;
27198
+ return twipsToPixels(widthTwips);
27199
+ }
27200
+ return null;
27201
+ };
27202
+ const countColumnsInRow = (row) => {
27203
+ if (!row?.elements?.length) return 0;
27204
+ return row.elements.reduce((count, element) => {
27205
+ if (element.name !== "w:tc") return count;
27206
+ const tcPr = element.elements?.find((el) => el.name === "w:tcPr");
27207
+ const gridSpan = tcPr?.elements?.find((el) => el.name === "w:gridSpan");
27208
+ const spanValue = parseInt(gridSpan?.attributes?.["w:val"] || "1", 10);
27209
+ return count + (Number.isFinite(spanValue) && spanValue > 0 ? spanValue : 1);
27210
+ }, 0);
27211
+ };
27212
+ const clampColumnWidthTwips = (value) => Math.max(Math.round(value), MIN_COLUMN_WIDTH_TWIPS);
27213
+ const createFallbackGrid = (columnCount, columnWidthTwips) => Array.from({ length: columnCount }, () => ({ col: clampColumnWidthTwips(columnWidthTwips) }));
27214
+ const buildFallbackGridForTable = ({ params: params2, rows, tableWidth, tableWidthMeasurement }) => {
27215
+ const firstRow = rows.find((row) => row.elements?.some((el) => el.name === "w:tc"));
27216
+ const columnCount = countColumnsInRow(firstRow);
27217
+ if (!columnCount) return null;
27218
+ const schemaDefaultPx = getSchemaDefaultColumnWidthPx(
27219
+ /** @type {any} */
27220
+ params2
27221
+ );
27222
+ const minimumColumnWidthPx = Number.isFinite(schemaDefaultPx) && schemaDefaultPx > 0 ? schemaDefaultPx : DEFAULT_COLUMN_WIDTH_PX;
27223
+ let totalWidthPx;
27224
+ if (tableWidthMeasurement) {
27225
+ const resolved = resolveMeasurementWidthPx(tableWidthMeasurement);
27226
+ if (resolved != null) totalWidthPx = resolved;
27227
+ }
27228
+ if (totalWidthPx == null && tableWidth?.width && tableWidth.width > 0) {
27229
+ totalWidthPx = tableWidth.width;
27230
+ }
27231
+ if (totalWidthPx == null) {
27232
+ totalWidthPx = minimumColumnWidthPx * columnCount;
27233
+ }
27234
+ const rawColumnWidthPx = Math.max(totalWidthPx / columnCount, minimumColumnWidthPx);
27235
+ const columnWidthTwips = clampColumnWidthTwips(pixelsToTwips(rawColumnWidthPx));
27236
+ const fallbackColumnWidthPx = twipsToPixels(columnWidthTwips);
27237
+ return {
27238
+ grid: createFallbackGrid(columnCount, columnWidthTwips),
27239
+ columnWidths: Array(columnCount).fill(fallbackColumnWidthPx)
27240
+ };
27241
+ };
27183
27242
  const XML_NODE_NAME$9 = "w:tbl";
27184
27243
  const SD_NODE_NAME$9 = "table";
27185
27244
  const encode$g = (params2, encodedAttrs) => {
@@ -27199,7 +27258,6 @@ const encode$g = (params2, encodedAttrs) => {
27199
27258
  "justification",
27200
27259
  "tableLayout",
27201
27260
  ["tableIndent", ({ value, type: type2 }) => ({ width: twipsToPixels(value), type: type2 })],
27202
- ["tableWidth", ({ value, type: type2 }) => ({ width: twipsToPixels(value), type: type2 })],
27203
27261
  ["tableCellSpacing", ({ value, type: type2 }) => ({ w: String(value), type: type2 })]
27204
27262
  ].forEach((prop) => {
27205
27263
  let key2;
@@ -27217,6 +27275,21 @@ const encode$g = (params2, encodedAttrs) => {
27217
27275
  if (encodedAttrs.tableCellSpacing) {
27218
27276
  encodedAttrs["borderCollapse"] = "separate";
27219
27277
  }
27278
+ if (encodedAttrs.tableProperties?.tableWidth) {
27279
+ const tableWidthMeasurement = encodedAttrs.tableProperties.tableWidth;
27280
+ const widthPx = twipsToPixels(tableWidthMeasurement.value);
27281
+ if (widthPx != null) {
27282
+ encodedAttrs.tableWidth = {
27283
+ width: widthPx,
27284
+ type: tableWidthMeasurement.type
27285
+ };
27286
+ } else if (tableWidthMeasurement.type === "auto") {
27287
+ encodedAttrs.tableWidth = {
27288
+ width: 0,
27289
+ type: tableWidthMeasurement.type
27290
+ };
27291
+ }
27292
+ }
27220
27293
  const { borders, rowBorders } = _processTableBorders(encodedAttrs.tableProperties?.borders || {});
27221
27294
  const referencedStyles = _getReferencedTableStyles(encodedAttrs.tableStyleId, params2);
27222
27295
  if (referencedStyles?.cellMargins && !encodedAttrs.tableProperties?.cellMargins) {
@@ -27230,7 +27303,19 @@ const encode$g = (params2, encodedAttrs) => {
27230
27303
  const borderRowData = Object.assign({}, referencedStyles?.rowBorders || {}, rowBorders || {});
27231
27304
  encodedAttrs["borders"] = borderData;
27232
27305
  const tblStyleTag = tblPr?.elements?.find((el) => el.name === "w:tblStyle");
27233
- const columnWidths = (encodedAttrs["grid"] ?? []).map((item) => twipsToPixels(item.col));
27306
+ let columnWidths = Array.isArray(encodedAttrs["grid"]) ? encodedAttrs["grid"].map((item) => twipsToPixels(item.col)) : [];
27307
+ if (!columnWidths.length) {
27308
+ const fallback = buildFallbackGridForTable({
27309
+ params: params2,
27310
+ rows,
27311
+ tableWidth: encodedAttrs.tableWidth,
27312
+ tableWidthMeasurement: encodedAttrs.tableProperties?.tableWidth
27313
+ });
27314
+ if (fallback) {
27315
+ encodedAttrs.grid = fallback.grid;
27316
+ columnWidths = fallback.columnWidths;
27317
+ }
27318
+ }
27234
27319
  const content = [];
27235
27320
  rows.forEach((row) => {
27236
27321
  const result = translator$G.encode({
@@ -28092,6 +28177,9 @@ function handleStructuredContentNode(params2) {
28092
28177
  const node = nodes[0];
28093
28178
  const sdtPr = node.elements.find((el) => el.name === "w:sdtPr");
28094
28179
  const sdtContent = node.elements.find((el) => el.name === "w:sdtContent");
28180
+ const id = sdtPr?.elements?.find((el) => el.name === "w:id");
28181
+ const tag = sdtPr?.elements?.find((el) => el.name === "w:tag");
28182
+ const alias = sdtPr?.elements?.find((el) => el.name === "w:alias");
28095
28183
  if (!sdtContent) {
28096
28184
  return null;
28097
28185
  }
@@ -28103,15 +28191,16 @@ function handleStructuredContentNode(params2) {
28103
28191
  nodes: sdtContent.elements,
28104
28192
  path: [...params2.path || [], sdtContent]
28105
28193
  });
28106
- let sdtContentType = "structuredContent";
28107
- if (paragraph || table) {
28108
- sdtContentType = "structuredContentBlock";
28109
- }
28194
+ const isBlockNode2 = paragraph || table;
28195
+ const sdtContentType = isBlockNode2 ? "structuredContentBlock" : "structuredContent";
28110
28196
  let result = {
28111
28197
  type: sdtContentType,
28112
28198
  content: translatedContent,
28113
28199
  marks,
28114
28200
  attrs: {
28201
+ id: id?.attributes?.["w:val"] || null,
28202
+ tag: tag?.attributes?.["w:val"] || null,
28203
+ alias: alias?.attributes?.["w:val"] || null,
28115
28204
  sdtPr
28116
28205
  }
28117
28206
  };
@@ -30369,21 +30458,55 @@ const generateSdtPrTagForDocumentSection = (id, title, tag) => {
30369
30458
  };
30370
30459
  function translateStructuredContent(params2) {
30371
30460
  const { node } = params2;
30372
- const { attrs = {} } = node;
30373
30461
  const childContent = translateChildNodes({ ...params2, nodes: node.content });
30374
- const nodeElements = [
30375
- {
30376
- name: "w:sdtContent",
30377
- elements: childContent
30378
- }
30379
- ];
30380
- nodeElements.unshift(attrs.sdtPr);
30462
+ const sdtContent = { name: "w:sdtContent", elements: childContent };
30463
+ const sdtPr = generateSdtPrTagForStructuredContent({ node });
30464
+ const nodeElements = [sdtPr, sdtContent];
30381
30465
  const result = {
30382
30466
  name: "w:sdt",
30383
30467
  elements: nodeElements
30384
30468
  };
30385
30469
  return result;
30386
30470
  }
30471
+ function generateSdtPrTagForStructuredContent({ node }) {
30472
+ const { attrs = {} } = node;
30473
+ const id = {
30474
+ name: "w:id",
30475
+ type: "element",
30476
+ attributes: { "w:val": attrs.id }
30477
+ };
30478
+ const alias = {
30479
+ name: "w:alias",
30480
+ type: "element",
30481
+ attributes: { "w:val": attrs.alias }
30482
+ };
30483
+ const tag = {
30484
+ name: "w:tag",
30485
+ type: "element",
30486
+ attributes: { "w:val": attrs.tag }
30487
+ };
30488
+ const resultElements = [];
30489
+ if (attrs.id) resultElements.push(id);
30490
+ if (attrs.alias) resultElements.push(alias);
30491
+ if (attrs.tag) resultElements.push(tag);
30492
+ if (attrs.sdtPr) {
30493
+ const elements = attrs.sdtPr.elements || [];
30494
+ const elementsToExclude = ["w:id", "w:alias", "w:tag"];
30495
+ const restElements = elements.filter((el) => !elementsToExclude.includes(el.name));
30496
+ const result2 = {
30497
+ name: "w:sdtPr",
30498
+ type: "element",
30499
+ elements: [...resultElements, ...restElements]
30500
+ };
30501
+ return result2;
30502
+ }
30503
+ const result = {
30504
+ name: "w:sdtPr",
30505
+ type: "element",
30506
+ elements: resultElements
30507
+ };
30508
+ return result;
30509
+ }
30387
30510
  const XML_NODE_NAME$3 = "w:sdt";
30388
30511
  const SD_NODE_NAME$3 = ["fieldAnnotation", "structuredContent", "structuredContentBlock", "documentSection"];
30389
30512
  const validXmlAttributes$3 = [];
@@ -30611,6 +30734,63 @@ const config = {
30611
30734
  attributes: validXmlAttributes
30612
30735
  };
30613
30736
  const translator = NodeTranslator.from(config);
30737
+ const DEFAULT_SECTION_PROPS_TWIPS = Object.freeze({
30738
+ pageSize: Object.freeze({ width: "12240", height: "15840" }),
30739
+ pageMargins: Object.freeze({
30740
+ top: "1440",
30741
+ right: "1440",
30742
+ bottom: "1440",
30743
+ left: "1440",
30744
+ header: "720",
30745
+ footer: "720",
30746
+ gutter: "0"
30747
+ })
30748
+ });
30749
+ const ensureSectionLayoutDefaults = (sectPr, converter) => {
30750
+ if (!sectPr) {
30751
+ return {
30752
+ type: "element",
30753
+ name: "w:sectPr",
30754
+ elements: []
30755
+ };
30756
+ }
30757
+ if (!sectPr.elements) sectPr.elements = [];
30758
+ const ensureChild = (name) => {
30759
+ let child = sectPr.elements.find((n) => n.name === name);
30760
+ if (!child) {
30761
+ child = {
30762
+ type: "element",
30763
+ name,
30764
+ elements: [],
30765
+ attributes: {}
30766
+ };
30767
+ sectPr.elements.push(child);
30768
+ } else {
30769
+ if (!child.elements) child.elements = [];
30770
+ if (!child.attributes) child.attributes = {};
30771
+ }
30772
+ return child;
30773
+ };
30774
+ const pageSize = converter?.pageStyles?.pageSize;
30775
+ const pgSz = ensureChild("w:pgSz");
30776
+ if (pageSize?.width != null) pgSz.attributes["w:w"] = String(inchesToTwips(pageSize.width));
30777
+ if (pageSize?.height != null) pgSz.attributes["w:h"] = String(inchesToTwips(pageSize.height));
30778
+ if (pgSz.attributes["w:w"] == null) pgSz.attributes["w:w"] = DEFAULT_SECTION_PROPS_TWIPS.pageSize.width;
30779
+ if (pgSz.attributes["w:h"] == null) pgSz.attributes["w:h"] = DEFAULT_SECTION_PROPS_TWIPS.pageSize.height;
30780
+ const pageMargins = converter?.pageStyles?.pageMargins;
30781
+ const pgMar = ensureChild("w:pgMar");
30782
+ if (pageMargins) {
30783
+ Object.entries(pageMargins).forEach(([key2, value]) => {
30784
+ const converted = inchesToTwips(value);
30785
+ if (converted != null) pgMar.attributes[`w:${key2}`] = String(converted);
30786
+ });
30787
+ }
30788
+ Object.entries(DEFAULT_SECTION_PROPS_TWIPS.pageMargins).forEach(([key2, value]) => {
30789
+ const attrKey = `w:${key2}`;
30790
+ if (pgMar.attributes[attrKey] == null) pgMar.attributes[attrKey] = value;
30791
+ });
30792
+ return sectPr;
30793
+ };
30614
30794
  const isLineBreakOnlyRun = (node) => {
30615
30795
  if (!node) return false;
30616
30796
  if (node.type === "lineBreak" || node.type === "hardBreak") return true;
@@ -30663,28 +30843,30 @@ function exportSchemaToJson(params2) {
30663
30843
  return handler2(params2);
30664
30844
  }
30665
30845
  function translateBodyNode(params2) {
30666
- let sectPr = params2.bodyNode?.elements.find((n) => n.name === "w:sectPr") || {};
30846
+ let sectPr = params2.bodyNode?.elements?.find((n) => n.name === "w:sectPr");
30847
+ if (!sectPr) {
30848
+ sectPr = {
30849
+ type: "element",
30850
+ name: "w:sectPr",
30851
+ elements: []
30852
+ };
30853
+ } else if (!sectPr.elements) {
30854
+ sectPr = { ...sectPr, elements: [] };
30855
+ }
30856
+ sectPr = ensureSectionLayoutDefaults(sectPr, params2.converter);
30667
30857
  if (params2.converter) {
30668
- const hasHeader = sectPr?.elements?.some((n) => n.name === "w:headerReference");
30858
+ const hasHeader = sectPr.elements?.some((n) => n.name === "w:headerReference");
30669
30859
  const hasDefaultHeader = params2.converter.headerIds?.default;
30670
30860
  if (!hasHeader && hasDefaultHeader && !params2.editor.options.isHeaderOrFooter) {
30671
30861
  const defaultHeader = generateDefaultHeaderFooter("header", params2.converter.headerIds?.default);
30672
30862
  sectPr.elements.push(defaultHeader);
30673
30863
  }
30674
- const hasFooter = sectPr?.elements?.some((n) => n.name === "w:footerReference");
30864
+ const hasFooter = sectPr.elements?.some((n) => n.name === "w:footerReference");
30675
30865
  const hasDefaultFooter = params2.converter.footerIds?.default;
30676
30866
  if (!hasFooter && hasDefaultFooter && !params2.editor.options.isHeaderOrFooter) {
30677
30867
  const defaultFooter = generateDefaultHeaderFooter("footer", params2.converter.footerIds?.default);
30678
30868
  sectPr.elements.push(defaultFooter);
30679
30869
  }
30680
- const newMargins = params2.converter.pageStyles.pageMargins;
30681
- const sectPrMargins = sectPr.elements.find((n) => n.name === "w:pgMar");
30682
- const { attributes } = sectPrMargins;
30683
- Object.entries(newMargins).forEach(([key2, value]) => {
30684
- const convertedValue = inchesToTwips(value);
30685
- attributes[`w:${key2}`] = convertedValue;
30686
- });
30687
- sectPrMargins.attributes = attributes;
30688
30870
  }
30689
30871
  const elements = translateChildNodes(params2);
30690
30872
  if (params2.isHeaderFooter) {
@@ -32111,8 +32293,15 @@ const handlePictNode = (params2) => {
32111
32293
  return { nodes: [], consumed: 0 };
32112
32294
  }
32113
32295
  const [pNode] = nodes;
32114
- const run2 = pNode.elements?.find((el) => el.name === "w:r");
32115
- const pict = run2?.elements?.find((el) => el.name === "w:pict");
32296
+ const runs = pNode.elements?.filter((el) => el.name === "w:r") || [];
32297
+ let pict = null;
32298
+ for (const run2 of runs) {
32299
+ const foundPict = run2.elements?.find((el) => el.name === "w:pict");
32300
+ if (foundPict) {
32301
+ pict = foundPict;
32302
+ break;
32303
+ }
32304
+ }
32116
32305
  if (!pict) {
32117
32306
  return { nodes: [], consumed: 0 };
32118
32307
  }
@@ -32699,6 +32888,7 @@ const createDocumentJson = (docx, converter, editor) => {
32699
32888
  const nodeListHandler = defaultNodeListHandler();
32700
32889
  const bodyNode = json.elements[0].elements.find((el) => el.name === "w:body");
32701
32890
  if (bodyNode) {
32891
+ ensureSectionProperties(bodyNode);
32702
32892
  const node = bodyNode;
32703
32893
  const contentElements = node.elements?.filter((n) => n.name !== "w:sectPr") ?? [];
32704
32894
  const content = pruneIgnoredNodes(contentElements);
@@ -32932,6 +33122,59 @@ function getDocumentStyles(node, docx, converter, editor) {
32932
33122
  styles.alternateHeaders = isAlternatingHeadersOddEven(docx);
32933
33123
  return styles;
32934
33124
  }
33125
+ const DEFAULT_SECTION_PROPS = Object.freeze({
33126
+ pageSize: Object.freeze({ width: "12240", height: "15840" }),
33127
+ pageMargins: Object.freeze({
33128
+ top: "1440",
33129
+ right: "1440",
33130
+ bottom: "1440",
33131
+ left: "1440",
33132
+ header: "720",
33133
+ footer: "720",
33134
+ gutter: "0"
33135
+ })
33136
+ });
33137
+ function ensureSectionProperties(bodyNode, converter) {
33138
+ if (!bodyNode.elements) bodyNode.elements = [];
33139
+ let sectPr = bodyNode.elements.find((el) => el.name === "w:sectPr");
33140
+ if (!sectPr) {
33141
+ sectPr = {
33142
+ type: "element",
33143
+ name: "w:sectPr",
33144
+ elements: []
33145
+ };
33146
+ bodyNode.elements.push(sectPr);
33147
+ } else if (!sectPr.elements) {
33148
+ sectPr.elements = [];
33149
+ }
33150
+ const ensureChild = (name, factory) => {
33151
+ let child = sectPr.elements.find((el) => el.name === name);
33152
+ if (!child) {
33153
+ child = factory();
33154
+ sectPr.elements.push(child);
33155
+ } else if (!child.attributes) {
33156
+ child.attributes = {};
33157
+ }
33158
+ return child;
33159
+ };
33160
+ const pgSz = ensureChild("w:pgSz", () => ({
33161
+ type: "element",
33162
+ name: "w:pgSz",
33163
+ attributes: {}
33164
+ }));
33165
+ pgSz.attributes["w:w"] = pgSz.attributes["w:w"] ?? DEFAULT_SECTION_PROPS.pageSize.width;
33166
+ pgSz.attributes["w:h"] = pgSz.attributes["w:h"] ?? DEFAULT_SECTION_PROPS.pageSize.height;
33167
+ const pgMar = ensureChild("w:pgMar", () => ({
33168
+ type: "element",
33169
+ name: "w:pgMar",
33170
+ attributes: {}
33171
+ }));
33172
+ Object.entries(DEFAULT_SECTION_PROPS.pageMargins).forEach(([key2, value]) => {
33173
+ const attrKey = `w:${key2}`;
33174
+ if (pgMar.attributes[attrKey] == null) pgMar.attributes[attrKey] = value;
33175
+ });
33176
+ return sectPr;
33177
+ }
32935
33178
  function getStyleDefinitions(docx) {
32936
33179
  const styles = docx["word/styles.xml"];
32937
33180
  if (!styles) return [];
@@ -33124,6 +33367,36 @@ const FONT_FAMILY_FALLBACKS = Object.freeze({
33124
33367
  auto: "sans-serif"
33125
33368
  });
33126
33369
  const DEFAULT_GENERIC_FALLBACK = "sans-serif";
33370
+ const DEFAULT_FONT_SIZE_PT = 10;
33371
+ const collectRunDefaultProperties = (runProps, { allowOverrideTypeface = true, allowOverrideSize = true, themeResolver, state: state2 }) => {
33372
+ if (!runProps?.elements?.length || !state2) return;
33373
+ const fontsNode = runProps.elements.find((el) => el.name === "w:rFonts");
33374
+ if (fontsNode?.attributes) {
33375
+ const themeName = fontsNode.attributes["w:asciiTheme"];
33376
+ if (themeName) {
33377
+ const themeInfo = themeResolver?.(themeName) || {};
33378
+ if ((allowOverrideTypeface || !state2.typeface) && themeInfo.typeface) state2.typeface = themeInfo.typeface;
33379
+ if ((allowOverrideTypeface || !state2.panose) && themeInfo.panose) state2.panose = themeInfo.panose;
33380
+ }
33381
+ const ascii = fontsNode.attributes["w:ascii"];
33382
+ if ((allowOverrideTypeface || !state2.typeface) && ascii) {
33383
+ state2.typeface = ascii;
33384
+ }
33385
+ }
33386
+ const sizeNode = runProps.elements.find((el) => el.name === "w:sz");
33387
+ if (sizeNode?.attributes?.["w:val"]) {
33388
+ const sizeTwips = Number(sizeNode.attributes["w:val"]);
33389
+ if (Number.isFinite(sizeTwips)) {
33390
+ if (state2.fallbackSzTwips === void 0) state2.fallbackSzTwips = sizeTwips;
33391
+ const sizePt = sizeTwips / 2;
33392
+ if (allowOverrideSize || state2.fontSizePt === void 0) state2.fontSizePt = sizePt;
33393
+ }
33394
+ }
33395
+ const kernNode = runProps.elements.find((el) => el.name === "w:kern");
33396
+ if (kernNode?.attributes?.["w:val"]) {
33397
+ if (allowOverrideSize || state2.kern === void 0) state2.kern = kernNode.attributes["w:val"];
33398
+ }
33399
+ };
33127
33400
  const _SuperConverter = class _SuperConverter2 {
33128
33401
  constructor(params2 = null) {
33129
33402
  __privateAdd$2(this, _SuperConverter_instances);
@@ -33251,49 +33524,45 @@ const _SuperConverter = class _SuperConverter2 {
33251
33524
  }
33252
33525
  getDocumentDefaultStyles() {
33253
33526
  const styles = this.convertedXml["word/styles.xml"];
33254
- if (!styles) return {};
33255
- const defaults = styles.elements[0].elements.find((el) => el.name === "w:docDefaults");
33256
- const rDefault = defaults.elements.find((el) => el.name === "w:rPrDefault");
33257
- if (!rDefault.elements) return {};
33258
- const rElements = rDefault.elements[0].elements;
33259
- const rFonts = rElements?.find((el) => el.name === "w:rFonts");
33260
- if ("elements" in rDefault) {
33261
- const fontThemeName = rElements.find((el) => el.name === "w:rFonts")?.attributes["w:asciiTheme"];
33262
- let typeface, panose, fontSizeNormal;
33263
- if (fontThemeName) {
33264
- const fontInfo = this.getThemeInfo(fontThemeName);
33265
- typeface = fontInfo.typeface;
33266
- panose = fontInfo.panose;
33267
- } else if (rFonts) {
33268
- typeface = rFonts?.attributes["w:ascii"];
33269
- }
33270
- const paragraphDefaults = styles.elements[0].elements.filter((el) => {
33271
- return el.name === "w:style" && el.attributes["w:styleId"] === "Normal";
33272
- }) || [];
33273
- paragraphDefaults.forEach((el) => {
33274
- const rPr = el.elements.find((el2) => el2.name === "w:rPr");
33275
- const fonts = rPr?.elements?.find((el2) => el2.name === "w:rFonts");
33276
- typeface = fonts?.attributes["w:ascii"];
33277
- fontSizeNormal = Number(rPr?.elements?.find((el2) => el2.name === "w:sz")?.attributes["w:val"]) / 2;
33278
- });
33279
- const rPrDefaults = defaults?.elements?.find((el) => el.name === "w:rPrDefault");
33280
- if (rPrDefaults) {
33281
- const rPr = rPrDefaults.elements?.find((el) => el.name === "w:rPr");
33282
- const fonts = rPr?.elements?.find((el) => el.name === "w:rFonts");
33283
- if (fonts?.attributes?.["w:ascii"]) {
33284
- typeface = fonts.attributes["w:ascii"];
33285
- }
33286
- const fontSizeRaw = rPr?.elements?.find((el) => el.name === "w:sz")?.attributes?.["w:val"];
33287
- if (!fontSizeNormal && fontSizeRaw) {
33288
- fontSizeNormal = Number(fontSizeRaw) / 2;
33289
- }
33290
- }
33291
- const fallbackSz = Number(rElements.find((el) => el.name === "w:sz")?.attributes?.["w:val"]);
33292
- const fontSizePt = fontSizeNormal ?? (Number.isFinite(fallbackSz) ? fallbackSz / 2 : void 0) ?? 10;
33293
- const kern = rElements.find((el) => el.name === "w:kern")?.attributes["w:val"];
33294
- const fontFamilyCss = _SuperConverter2.toCssFontFamily(typeface, this.convertedXml);
33295
- return { fontSizePt, kern, typeface, panose, fontFamilyCss };
33296
- }
33527
+ const styleRoot = styles?.elements?.[0];
33528
+ const styleElements = styleRoot?.elements || [];
33529
+ if (!styleElements.length) return {};
33530
+ const defaults = styleElements.find((el) => el.name === "w:docDefaults");
33531
+ const normalStyle = styleElements.find((el) => el.name === "w:style" && el.attributes?.["w:styleId"] === "Normal");
33532
+ const defaultsState = {
33533
+ typeface: void 0,
33534
+ panose: void 0,
33535
+ fontSizePt: void 0,
33536
+ kern: void 0,
33537
+ fallbackSzTwips: void 0
33538
+ };
33539
+ const docDefaultRun = defaults?.elements?.find((el) => el.name === "w:rPrDefault");
33540
+ const docDefaultProps = docDefaultRun?.elements?.find((el) => el.name === "w:rPr") ?? docDefaultRun;
33541
+ collectRunDefaultProperties(docDefaultProps, {
33542
+ allowOverrideTypeface: true,
33543
+ allowOverrideSize: true,
33544
+ themeResolver: (theme) => this.getThemeInfo(theme),
33545
+ state: defaultsState
33546
+ });
33547
+ const normalRunProps = normalStyle?.elements?.find((el) => el.name === "w:rPr") ?? null;
33548
+ collectRunDefaultProperties(normalRunProps, {
33549
+ allowOverrideTypeface: true,
33550
+ allowOverrideSize: true,
33551
+ themeResolver: (theme) => this.getThemeInfo(theme),
33552
+ state: defaultsState
33553
+ });
33554
+ if (defaultsState.fontSizePt === void 0) {
33555
+ if (Number.isFinite(defaultsState.fallbackSzTwips)) defaultsState.fontSizePt = defaultsState.fallbackSzTwips / 2;
33556
+ else defaultsState.fontSizePt = DEFAULT_FONT_SIZE_PT;
33557
+ }
33558
+ const fontFamilyCss = defaultsState.typeface ? _SuperConverter2.toCssFontFamily(defaultsState.typeface, this.convertedXml) : void 0;
33559
+ const result = {};
33560
+ if (defaultsState.fontSizePt !== void 0) result.fontSizePt = defaultsState.fontSizePt;
33561
+ if (defaultsState.kern !== void 0) result.kern = defaultsState.kern;
33562
+ if (defaultsState.typeface) result.typeface = defaultsState.typeface;
33563
+ if (defaultsState.panose) result.panose = defaultsState.panose;
33564
+ if (fontFamilyCss) result.fontFamilyCss = fontFamilyCss;
33565
+ return result;
33297
33566
  }
33298
33567
  getDocumentFonts() {
33299
33568
  const fontTable = this.convertedXml["word/fontTable.xml"];
@@ -36160,14 +36429,19 @@ class DocxZipper {
36160
36429
  /**
36161
36430
  * Update [Content_Types].xml with extensions of new Image annotations
36162
36431
  */
36163
- async updateContentTypes(docx, media, fromJson) {
36432
+ async updateContentTypes(docx, media, fromJson, updatedDocs = {}) {
36433
+ const additionalPartNames = Object.keys(updatedDocs || {});
36164
36434
  const newMediaTypes = Object.keys(media).map((name) => {
36165
36435
  return this.getFileExtension(name);
36166
36436
  }).filter(Boolean);
36167
36437
  const contentTypesPath = "[Content_Types].xml";
36168
36438
  let contentTypesXml;
36169
36439
  if (fromJson) {
36170
- contentTypesXml = docx.files.find((file) => file.name === contentTypesPath)?.content || "";
36440
+ if (Array.isArray(docx.files)) {
36441
+ contentTypesXml = docx.files.find((file) => file.name === contentTypesPath)?.content || "";
36442
+ } else {
36443
+ contentTypesXml = docx.files?.[contentTypesPath] || "";
36444
+ }
36171
36445
  } else contentTypesXml = await docx.file(contentTypesPath).async("string");
36172
36446
  let typesString = "";
36173
36447
  const defaultMediaTypes = getContentTypesFromXml(contentTypesXml);
@@ -36193,24 +36467,39 @@ class DocxZipper {
36193
36467
  const hasCommentsExtensible = types2.elements?.some(
36194
36468
  (el) => el.name === "Override" && el.attributes.PartName === "/word/commentsExtensible.xml"
36195
36469
  );
36196
- if (docx.files["word/comments.xml"]) {
36470
+ const hasFile = (filename) => {
36471
+ if (!docx?.files) return false;
36472
+ if (!fromJson) return Boolean(docx.files[filename]);
36473
+ if (Array.isArray(docx.files)) return docx.files.some((file) => file.name === filename);
36474
+ return Boolean(docx.files[filename]);
36475
+ };
36476
+ if (hasFile("word/comments.xml")) {
36197
36477
  const commentsDef = `<Override PartName="/word/comments.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.comments+xml" />`;
36198
36478
  if (!hasComments) typesString += commentsDef;
36199
36479
  }
36200
- if (docx.files["word/commentsExtended.xml"]) {
36480
+ if (hasFile("word/commentsExtended.xml")) {
36201
36481
  const commentsExtendedDef = `<Override PartName="/word/commentsExtended.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.commentsExtended+xml" />`;
36202
36482
  if (!hasCommentsExtended) typesString += commentsExtendedDef;
36203
36483
  }
36204
- if (docx.files["word/commentsIds.xml"]) {
36484
+ if (hasFile("word/commentsIds.xml")) {
36205
36485
  const commentsIdsDef = `<Override PartName="/word/commentsIds.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.commentsIds+xml" />`;
36206
36486
  if (!hasCommentsIds) typesString += commentsIdsDef;
36207
36487
  }
36208
- if (docx.files["word/commentsExtensible.xml"]) {
36488
+ if (hasFile("word/commentsExtensible.xml")) {
36209
36489
  const commentsExtendedDef = `<Override PartName="/word/commentsExtensible.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.commentsExtensible+xml" />`;
36210
36490
  if (!hasCommentsExtensible) typesString += commentsExtendedDef;
36211
36491
  }
36212
- Object.keys(docx.files).forEach((name) => {
36213
- if (name.includes(".rels") || !name.includes("header") && !name.includes("footer")) return;
36492
+ const partNames = new Set(additionalPartNames);
36493
+ if (docx?.files) {
36494
+ if (fromJson && Array.isArray(docx.files)) {
36495
+ docx.files.forEach((file) => partNames.add(file.name));
36496
+ } else {
36497
+ Object.keys(docx.files).forEach((key2) => partNames.add(key2));
36498
+ }
36499
+ }
36500
+ partNames.forEach((name) => {
36501
+ if (name.includes(".rels")) return;
36502
+ if (!name.includes("header") && !name.includes("footer")) return;
36214
36503
  const hasExtensible = types2.elements?.some(
36215
36504
  (el) => el.name === "Override" && el.attributes.PartName === `/${name}`
36216
36505
  );
@@ -36221,7 +36510,48 @@ class DocxZipper {
36221
36510
  }
36222
36511
  });
36223
36512
  const beginningString = '<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">';
36224
- const updatedContentTypesXml = contentTypesXml.replace(beginningString, `${beginningString}${typesString}`);
36513
+ let updatedContentTypesXml = contentTypesXml.replace(beginningString, `${beginningString}${typesString}`);
36514
+ let relationshipsXml = updatedDocs["word/_rels/document.xml.rels"];
36515
+ if (!relationshipsXml) {
36516
+ if (fromJson) {
36517
+ if (Array.isArray(docx.files)) {
36518
+ relationshipsXml = docx.files.find((file) => file.name === "word/_rels/document.xml.rels")?.content;
36519
+ } else {
36520
+ relationshipsXml = docx.files?.["word/_rels/document.xml.rels"];
36521
+ }
36522
+ } else {
36523
+ relationshipsXml = await docx.file("word/_rels/document.xml.rels")?.async("string");
36524
+ }
36525
+ }
36526
+ if (relationshipsXml) {
36527
+ try {
36528
+ const relJson = xmljs.xml2js(relationshipsXml, { compact: false });
36529
+ const relationships = relJson.elements?.find((el) => el.name === "Relationships");
36530
+ relationships?.elements?.forEach((rel) => {
36531
+ const type2 = rel.attributes?.Type;
36532
+ const target = rel.attributes?.Target;
36533
+ if (!type2 || !target) return;
36534
+ const isHeader = type2.includes("/header");
36535
+ const isFooter = type2.includes("/footer");
36536
+ if (!isHeader && !isFooter) return;
36537
+ let sanitizedTarget = target.replace(/^\.\//, "");
36538
+ if (sanitizedTarget.startsWith("../")) sanitizedTarget = sanitizedTarget.slice(3);
36539
+ if (sanitizedTarget.startsWith("/")) sanitizedTarget = sanitizedTarget.slice(1);
36540
+ const partName = sanitizedTarget.startsWith("word/") ? sanitizedTarget : `word/${sanitizedTarget}`;
36541
+ partNames.add(partName);
36542
+ });
36543
+ } catch (error) {
36544
+ console.warn("Failed to parse document relationships while updating content types", error);
36545
+ }
36546
+ }
36547
+ partNames.forEach((name) => {
36548
+ if (name.includes(".rels")) return;
36549
+ if (!name.includes("header") && !name.includes("footer")) return;
36550
+ if (updatedContentTypesXml.includes(`PartName="/${name}"`)) return;
36551
+ const type2 = name.includes("header") ? "header" : "footer";
36552
+ const extendedDef = `<Override PartName="/${name}" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.${type2}+xml"/>`;
36553
+ updatedContentTypesXml = updatedContentTypesXml.replace("</Types>", `${extendedDef}</Types>`);
36554
+ });
36225
36555
  if (fromJson) return updatedContentTypesXml;
36226
36556
  docx.file(contentTypesPath, updatedContentTypesXml);
36227
36557
  }
@@ -36262,7 +36592,7 @@ class DocxZipper {
36262
36592
  for (const [fontName, fontUintArray] of Object.entries(fonts)) {
36263
36593
  zip.file(fontName, fontUintArray);
36264
36594
  }
36265
- await this.updateContentTypes(zip, media);
36595
+ await this.updateContentTypes(zip, media, false, updatedDocs);
36266
36596
  return zip;
36267
36597
  }
36268
36598
  /**
@@ -36288,7 +36618,7 @@ class DocxZipper {
36288
36618
  Object.keys(media).forEach((path) => {
36289
36619
  unzippedOriginalDocx.file(path, media[path]);
36290
36620
  });
36291
- await this.updateContentTypes(unzippedOriginalDocx, media);
36621
+ await this.updateContentTypes(unzippedOriginalDocx, media, false, updatedDocs);
36292
36622
  return unzippedOriginalDocx;
36293
36623
  }
36294
36624
  }
@@ -36303,7 +36633,7 @@ var __privateGet$1 = (obj, member, getter) => (__accessCheck$1(obj, member, "rea
36303
36633
  var __privateAdd$1 = (obj, member, value) => member.has(obj) ? __typeError$1("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
36304
36634
  var __privateSet = (obj, member, value, setter) => (__accessCheck$1(obj, member, "write to private field"), member.set(obj, value), value);
36305
36635
  var __privateMethod$1 = (obj, member, method) => (__accessCheck$1(obj, member, "access private method"), method);
36306
- var _Attribute_static, getGlobalAttributes_fn, getNodeAndMarksAttributes_fn, _Schema_static, createNodesSchema_fn, createMarksSchema_fn, _events, _ExtensionService_instances, setupExtensions_fn, attachEditorEvents_fn, _editor, _stateValidators, _xmlValidators, _requiredNodeTypes, _requiredMarkTypes, _SuperValidator_instances, initializeValidators_fn, collectValidatorRequirements_fn, analyzeDocument_fn, _commandService, _Editor_instances, initContainerElement_fn, init_fn, initRichText_fn, onFocus_fn, checkHeadless_fn, registerCopyHandler_fn, insertNewFileData_fn, registerPluginByNameIfNotExists_fn, createExtensionService_fn, createCommandService_fn, createConverter_fn, initMedia_fn, initFonts_fn, createSchema_fn, generatePmData_fn, createView_fn, onCollaborationReady_fn, initComments_fn, initPagination_fn, dispatchTransaction_fn, handleNodeSelection_fn, prepareDocumentForImport_fn, prepareDocumentForExport_fn, endCollaboration_fn, validateDocumentInit_fn, validateDocumentExport_fn, initDevTools_fn, _ListItemNodeView_instances, init_fn2, _FieldAnnotationView_instances, createAnnotation_fn, _AutoPageNumberNodeView_instances, renderDom_fn, scheduleUpdateNodeStyle_fn, _DocumentSectionView_instances, init_fn3, addToolTip_fn;
36636
+ var _Attribute_static, getGlobalAttributes_fn, getNodeAndMarksAttributes_fn, _Schema_static, createNodesSchema_fn, createMarksSchema_fn, _events, _ExtensionService_instances, setupExtensions_fn, attachEditorEvents_fn, _editor, _stateValidators, _xmlValidators, _requiredNodeTypes, _requiredMarkTypes, _SuperValidator_instances, initializeValidators_fn, collectValidatorRequirements_fn, analyzeDocument_fn, _commandService, _Editor_instances, initContainerElement_fn, init_fn, initRichText_fn, onFocus_fn, checkHeadless_fn, registerCopyHandler_fn, insertNewFileData_fn, registerPluginByNameIfNotExists_fn, createExtensionService_fn, createCommandService_fn, createConverter_fn, initMedia_fn, initFonts_fn, createSchema_fn, generatePmData_fn, createView_fn, onCollaborationReady_fn, initComments_fn, initPagination_fn, dispatchTransaction_fn, handleNodeSelection_fn, prepareDocumentForImport_fn, prepareDocumentForExport_fn, endCollaboration_fn, validateDocumentInit_fn, validateDocumentExport_fn, initDevTools_fn, _DocumentSectionView_instances, init_fn2, addToolTip_fn, _ListItemNodeView_instances, init_fn3, _FieldAnnotationView_instances, createAnnotation_fn, _AutoPageNumberNodeView_instances, renderDom_fn, scheduleUpdateNodeStyle_fn;
36307
36637
  var GOOD_LEAF_SIZE = 200;
36308
36638
  var RopeSequence = function RopeSequence2() {
36309
36639
  };
@@ -47946,9 +48276,11 @@ const toggleHeaderFooterEditMode = ({ editor, focusedSectionEditor, isEditMode,
47946
48276
  item.editor.view.dom.setAttribute("documentmode", documentMode);
47947
48277
  });
47948
48278
  if (isEditMode) {
47949
- const pm = document.querySelector(".ProseMirror");
47950
- pm.classList.add("header-footer-edit");
47951
- pm.setAttribute("aria-readonly", true);
48279
+ const pm = editor.view?.dom || editor.options.element?.querySelector?.(".ProseMirror");
48280
+ if (pm) {
48281
+ pm.classList.add("header-footer-edit");
48282
+ pm.setAttribute("aria-readonly", true);
48283
+ }
47952
48284
  }
47953
48285
  if (focusedSectionEditor) {
47954
48286
  focusedSectionEditor.view.focus();
@@ -48376,28 +48708,25 @@ const handleTrackedChangeTransaction = (trackedChangeMeta, trackedChanges, newEd
48376
48708
  if (emitParams) editor.emit("commentsUpdate", emitParams);
48377
48709
  return newTrackedChanges;
48378
48710
  };
48379
- const getTrackedChangeText = ({ state: state2, node, mark, marks, trackedChangeType, isDeletionInsertion }) => {
48711
+ const getTrackedChangeText = ({ state: state2, nodes, mark, marks, trackedChangeType, isDeletionInsertion }) => {
48380
48712
  let trackedChangeText = "";
48381
48713
  let deletionText = "";
48382
48714
  if (trackedChangeType === TrackInsertMarkName) {
48383
- trackedChangeText = node?.text ?? "";
48715
+ trackedChangeText = nodes.reduce((acc, node) => {
48716
+ if (!node.marks.find((nodeMark) => nodeMark.type.name === mark.type.name)) return acc;
48717
+ acc += node?.text || node?.textContent || "";
48718
+ return acc;
48719
+ }, "");
48384
48720
  }
48385
48721
  if (trackedChangeType === TrackFormatMarkName) {
48386
48722
  trackedChangeText = translateFormatChangesToEnglish(mark.attrs);
48387
48723
  }
48388
48724
  if (trackedChangeType === TrackDeleteMarkName || isDeletionInsertion) {
48389
- deletionText = node?.text ?? "";
48390
- if (isDeletionInsertion) {
48391
- let { id } = marks.deletionMark.attrs;
48392
- let deletionNode = findNode$1(state2.doc, (node2) => {
48393
- const { marks: marks2 = [] } = node2;
48394
- const changeMarks = marks2.filter((mark2) => TRACK_CHANGE_MARKS.includes(mark2.type.name));
48395
- if (!changeMarks.length) return false;
48396
- const hasMatchingId = changeMarks.find((mark2) => mark2.attrs.id === id);
48397
- if (hasMatchingId) return true;
48398
- });
48399
- deletionText = deletionNode?.node.text ?? "";
48400
- }
48725
+ deletionText = nodes.reduce((acc, node) => {
48726
+ if (!node.marks.find((nodeMark) => nodeMark.type.name === TrackDeleteMarkName)) return acc;
48727
+ acc += node?.text || node?.textContent || "";
48728
+ return acc;
48729
+ }, "");
48401
48730
  }
48402
48731
  return {
48403
48732
  deletionText,
@@ -48412,18 +48741,17 @@ const createOrUpdateTrackedChangeComment = ({ event, marks, deletionNodes, nodes
48412
48741
  const id = attrs.id;
48413
48742
  const node = nodes[0];
48414
48743
  const isDeletionInsertion = !!(marks.insertedMark && marks.deletionMark);
48415
- let existingNode;
48744
+ let nodesWithMark = [];
48416
48745
  newEditorState.doc.descendants((node2) => {
48417
48746
  const { marks: marks2 = [] } = node2;
48418
48747
  const changeMarks = marks2.filter((mark) => TRACK_CHANGE_MARKS.includes(mark.type.name));
48419
48748
  if (!changeMarks.length) return;
48420
48749
  const hasMatchingId = changeMarks.find((mark) => mark.attrs.id === id);
48421
- if (hasMatchingId) existingNode = node2;
48422
- if (existingNode) return false;
48750
+ if (hasMatchingId) nodesWithMark.push(node2);
48423
48751
  });
48424
48752
  const { deletionText, trackedChangeText } = getTrackedChangeText({
48425
48753
  state: newEditorState,
48426
- node: existingNode || node,
48754
+ nodes: nodesWithMark.length ? nodesWithMark : [node],
48427
48755
  mark: trackedMark,
48428
48756
  marks,
48429
48757
  trackedChangeType,
@@ -48453,14 +48781,6 @@ const createOrUpdateTrackedChangeComment = ({ event, marks, deletionNodes, nodes
48453
48781
  else if (event === "update") params2.event = comments_module_events.UPDATE;
48454
48782
  return params2;
48455
48783
  };
48456
- function findNode$1(node, predicate) {
48457
- let found2 = null;
48458
- node.descendants((node2, pos) => {
48459
- if (predicate(node2)) found2 = { node: node2, pos };
48460
- if (found2) return false;
48461
- });
48462
- return found2;
48463
- }
48464
48784
  function findRangeById(doc2, id) {
48465
48785
  let from2 = null, to = null;
48466
48786
  doc2.descendants((node, pos) => {
@@ -48978,6 +49298,7 @@ const generateTableIfNecessary = ({ tableNode, annotationValues, tr, state: stat
48978
49298
  const mappedRowStart = tr.mapping.map(absoluteRowStart);
48979
49299
  const rowEnd = mappedRowStart + rowNode.nodeSize;
48980
49300
  tr.replaceWith(mappedRowStart, rowEnd, Fragment.from(newRows));
49301
+ tr.setMeta("tableGeneration", true);
48981
49302
  } catch (error) {
48982
49303
  console.error("Error during row generation:", error);
48983
49304
  throw error;
@@ -49382,7 +49703,7 @@ function findFieldAnnotationsBetween(from2, to, doc2) {
49382
49703
  }
49383
49704
  function findRemovedFieldAnnotations(tr) {
49384
49705
  let removedNodes = [];
49385
- if (!tr.steps.length || tr.meta && !Object.keys(tr.meta).every((meta) => ["inputType", "uiEvent", "paste"].includes(meta)) || ["historyUndo", "historyRedo"].includes(tr.getMeta("inputType")) || ["drop"].includes(tr.getMeta("uiEvent")) || tr.getMeta("fieldAnnotationUpdate") === true) {
49706
+ if (!tr.steps.length || tr.meta && !Object.keys(tr.meta).every((meta) => ["inputType", "uiEvent", "paste"].includes(meta)) || ["historyUndo", "historyRedo"].includes(tr.getMeta("inputType")) || ["drop"].includes(tr.getMeta("uiEvent")) || tr.getMeta("fieldAnnotationUpdate") === true || tr.getMeta("tableGeneration") === true) {
49386
49707
  return removedNodes;
49387
49708
  }
49388
49709
  const hasDeletion = transactionDeletedAnything(tr);
@@ -50560,7 +50881,7 @@ const _Editor = class _Editor2 extends EventEmitter$1 {
50560
50881
  setDocumentMode(documentMode) {
50561
50882
  let cleanedMode = documentMode?.toLowerCase() || "editing";
50562
50883
  if (!this.extensionService || !this.state) return;
50563
- const pm = document.querySelector(".ProseMirror");
50884
+ const pm = this.view?.dom || this.options.element?.querySelector?.(".ProseMirror");
50564
50885
  if (this.options.role === "viewer") cleanedMode = "viewing";
50565
50886
  if (this.options.role === "suggester" && cleanedMode === "editing") cleanedMode = "suggesting";
50566
50887
  if (cleanedMode === "viewing") {
@@ -51048,7 +51369,8 @@ const _Editor = class _Editor2 extends EventEmitter$1 {
51048
51369
  files: this.options.content
51049
51370
  },
51050
51371
  media,
51051
- true
51372
+ true,
51373
+ updatedDocs
51052
51374
  );
51053
51375
  return updatedDocs;
51054
51376
  }
@@ -51534,9 +51856,11 @@ createView_fn = function(element) {
51534
51856
  isEditMode: false,
51535
51857
  documentMode: this.options.documentMode
51536
51858
  });
51537
- const pm = document.querySelector(".ProseMirror");
51538
- pm.classList.remove("header-footer-edit");
51539
- pm.setAttribute("aria-readonly", false);
51859
+ const pm = this.view?.dom || this.options.element?.querySelector?.(".ProseMirror");
51860
+ if (pm) {
51861
+ pm.classList.remove("header-footer-edit");
51862
+ pm.setAttribute("aria-readonly", false);
51863
+ }
51540
51864
  }
51541
51865
  setWordSelection(view, pos);
51542
51866
  }
@@ -53084,522 +53408,1536 @@ const SlashMenu = Extension.create({
53084
53408
  return this.editor.options.isHeadless ? [] : [slashMenuPlugin];
53085
53409
  }
53086
53410
  });
53087
- const Document = Node$1.create({
53088
- name: "doc",
53089
- topNode: true,
53090
- content: "block+",
53091
- parseDOM() {
53092
- return [{ tag: "doc" }];
53093
- },
53094
- renderDOM() {
53095
- return ["doc", 0];
53096
- },
53097
- addAttributes() {
53098
- return {
53099
- attributes: {
53100
- rendered: false,
53101
- "aria-label": "Document node"
53102
- }
53103
- };
53104
- },
53105
- addCommands() {
53106
- return {
53107
- /**
53108
- * Get document statistics
53109
- * @category Command
53110
- * @example
53111
- * // Get word and character count
53112
- * const stats = editor.commands.getDocumentStats()
53113
- * console.log(`${stats.words} words, ${stats.characters} characters`)
53114
- * @note Returns word count, character count, and paragraph count
53115
- */
53116
- getDocumentStats: () => ({ editor }) => {
53117
- const text = editor.getText();
53118
- const words = text.split(/\s+/).filter((word) => word.length > 0).length;
53119
- const characters = text.length;
53120
- const paragraphs = editor.state.doc.content.childCount;
53121
- return {
53122
- words,
53123
- characters,
53124
- paragraphs
53125
- };
53126
- },
53127
- /**
53128
- * Clear entire document
53129
- * @category Command
53130
- * @example
53131
- * editor.commands.clearDocument()
53132
- * @note Replaces all content with an empty paragraph
53133
- */
53134
- clearDocument: () => ({ commands: commands2 }) => {
53135
- return commands2.setContent("<p></p>");
53411
+ class StructuredContentViewBase {
53412
+ constructor(props) {
53413
+ __publicField$1(this, "node");
53414
+ __publicField$1(this, "view");
53415
+ __publicField$1(this, "getPos");
53416
+ __publicField$1(this, "decorations");
53417
+ __publicField$1(this, "innerDecorations");
53418
+ __publicField$1(this, "editor");
53419
+ __publicField$1(this, "extension");
53420
+ __publicField$1(this, "htmlAttributes");
53421
+ __publicField$1(this, "root");
53422
+ __publicField$1(this, "isDragging", false);
53423
+ this.node = props.node;
53424
+ this.view = props.editor.view;
53425
+ this.getPos = props.getPos;
53426
+ this.decorations = props.decorations;
53427
+ this.innerDecorations = props.innerDecorations;
53428
+ this.editor = props.editor;
53429
+ this.extension = props.extension;
53430
+ this.htmlAttributes = props.htmlAttributes;
53431
+ this.mount(props);
53432
+ }
53433
+ mount() {
53434
+ return;
53435
+ }
53436
+ get dom() {
53437
+ return this.root;
53438
+ }
53439
+ get contentDOM() {
53440
+ return null;
53441
+ }
53442
+ update(node, decorations, innerDecorations) {
53443
+ if (node.type !== this.node.type) {
53444
+ return false;
53445
+ }
53446
+ this.node = node;
53447
+ this.decorations = decorations;
53448
+ this.innerDecorations = innerDecorations;
53449
+ this.updateHTMLAttributes();
53450
+ return true;
53451
+ }
53452
+ stopEvent(event) {
53453
+ if (!this.dom) return false;
53454
+ const target = event.target;
53455
+ const isInElement = this.dom.contains(target) && !this.contentDOM?.contains(target);
53456
+ if (!isInElement) return false;
53457
+ const isDragEvent = event.type.startsWith("drag");
53458
+ const isDropEvent = event.type === "drop";
53459
+ const isInput = ["INPUT", "BUTTON", "SELECT", "TEXTAREA"].includes(target.tagName) || target.isContentEditable;
53460
+ if (isInput && !isDropEvent && !isDragEvent) return true;
53461
+ const { isEditable } = this.editor;
53462
+ const { isDragging } = this;
53463
+ const isDraggable = !!this.node.type.spec.draggable;
53464
+ const isSelectable = NodeSelection.isSelectable(this.node);
53465
+ const isCopyEvent = event.type === "copy";
53466
+ const isPasteEvent = event.type === "paste";
53467
+ const isCutEvent = event.type === "cut";
53468
+ const isClickEvent = event.type === "mousedown";
53469
+ if (!isDraggable && isSelectable && isDragEvent && event.target === this.dom) {
53470
+ event.preventDefault();
53471
+ }
53472
+ if (isDraggable && isDragEvent && !isDragging && event.target === this.dom) {
53473
+ event.preventDefault();
53474
+ return false;
53475
+ }
53476
+ if (isDraggable && isEditable && !isDragging && isClickEvent) {
53477
+ const dragHandle = target.closest("[data-drag-handle]");
53478
+ const isValidDragHandle = dragHandle && (this.dom === dragHandle || this.dom.contains(dragHandle));
53479
+ if (isValidDragHandle) {
53480
+ this.isDragging = true;
53481
+ document.addEventListener(
53482
+ "dragend",
53483
+ () => {
53484
+ this.isDragging = false;
53485
+ },
53486
+ { once: true }
53487
+ );
53488
+ document.addEventListener(
53489
+ "drop",
53490
+ () => {
53491
+ this.isDragging = false;
53492
+ },
53493
+ { once: true }
53494
+ );
53495
+ document.addEventListener(
53496
+ "mouseup",
53497
+ () => {
53498
+ this.isDragging = false;
53499
+ },
53500
+ { once: true }
53501
+ );
53136
53502
  }
53137
- };
53503
+ }
53504
+ if (isDragging || isDropEvent || isCopyEvent || isPasteEvent || isCutEvent || isClickEvent && isSelectable) {
53505
+ return false;
53506
+ }
53507
+ return true;
53138
53508
  }
53139
- });
53140
- const Text = Node$1.create({
53141
- name: "text",
53142
- group: "inline",
53143
- inline: true,
53144
- addOptions() {
53145
- return {};
53509
+ ignoreMutation(mutation) {
53510
+ if (!this.dom || !this.contentDOM) return true;
53511
+ if (this.node.isLeaf || this.node.isAtom) return true;
53512
+ if (mutation.type === "selection") return false;
53513
+ if (this.contentDOM === mutation.target && mutation.type === "attributes") return true;
53514
+ if (this.contentDOM.contains(mutation.target)) return false;
53515
+ return true;
53146
53516
  }
53147
- });
53148
- const splitRun = () => (props) => {
53149
- const { state: state2, view, tr } = props;
53150
- const { $from, empty: empty2 } = state2.selection;
53151
- if (!empty2) return false;
53152
- if ($from.parent.type.name !== "run") return false;
53153
- const handled = splitBlock(state2, (transaction) => {
53517
+ destroy() {
53518
+ this.dom.remove();
53519
+ this.contentDOM?.remove();
53520
+ }
53521
+ updateAttributes(attrs) {
53522
+ const pos = this.getPos();
53523
+ if (typeof pos !== "number") {
53524
+ return;
53525
+ }
53526
+ return this.view.dispatch(
53527
+ this.view.state.tr.setNodeMarkup(pos, void 0, {
53528
+ ...this.node.attrs,
53529
+ ...attrs
53530
+ })
53531
+ );
53532
+ }
53533
+ updateHTMLAttributes() {
53534
+ const { extensionService } = this.editor;
53535
+ const { attributes } = extensionService;
53536
+ const extensionAttrs = attributes.filter((i) => i.type === this.node.type.name);
53537
+ this.htmlAttributes = Attribute2.getAttributesToRender(this.node, extensionAttrs);
53538
+ }
53539
+ createDragHandle() {
53540
+ const dragHandle = document.createElement("span");
53541
+ dragHandle.classList.add("sd-structured-content-draggable");
53542
+ dragHandle.draggable = true;
53543
+ dragHandle.contentEditable = "false";
53544
+ dragHandle.dataset.dragHandle = "";
53545
+ const textElement = document.createElement("span");
53546
+ textElement.textContent = this.node.attrs.alias || "Structured content";
53547
+ dragHandle.append(textElement);
53548
+ return dragHandle;
53549
+ }
53550
+ onDragStart(event) {
53551
+ const { view } = this.editor;
53552
+ const target = event.target;
53553
+ const dragHandle = target.nodeType === 3 ? target.parentElement?.closest("[data-drag-handle]") : target.closest("[data-drag-handle]");
53554
+ if (!this.dom || this.contentDOM?.contains(target) || !dragHandle) {
53555
+ return;
53556
+ }
53557
+ let x = 0;
53558
+ let y2 = 0;
53559
+ if (this.dom !== dragHandle) {
53560
+ const domBox = this.dom.getBoundingClientRect();
53561
+ const handleBox = dragHandle.getBoundingClientRect();
53562
+ const offsetX = event.offsetX ?? event.nativeEvent?.offsetX;
53563
+ const offsetY = event.offsetY ?? event.nativeEvent?.offsetY;
53564
+ x = handleBox.x - domBox.x + offsetX;
53565
+ y2 = handleBox.y - domBox.y + offsetY;
53566
+ }
53567
+ event.dataTransfer?.setDragImage(this.dom, x, y2);
53568
+ const pos = this.getPos();
53569
+ if (typeof pos !== "number") {
53570
+ return;
53571
+ }
53572
+ const selection = NodeSelection.create(view.state.doc, pos);
53573
+ const transaction = view.state.tr.setSelection(selection);
53154
53574
  view.dispatch(transaction);
53155
- });
53156
- if (handled) {
53157
- tr.setMeta("preventDispatch", true);
53158
53575
  }
53159
- return handled;
53160
- };
53161
- const Run = OxmlNode.create({
53162
- name: "run",
53163
- oXmlName: "w:r",
53164
- group: "inline",
53576
+ }
53577
+ class StructuredContentInlineView extends StructuredContentViewBase {
53578
+ constructor(props) {
53579
+ super(props);
53580
+ }
53581
+ mount() {
53582
+ this.buildView();
53583
+ }
53584
+ get contentDOM() {
53585
+ const contentElement = this.dom?.querySelector(`.${structuredContentInnerClass$1}`);
53586
+ return contentElement || null;
53587
+ }
53588
+ createElement() {
53589
+ const element = document.createElement("span");
53590
+ element.classList.add(structuredContentClass$1);
53591
+ element.setAttribute("data-structured-content", "");
53592
+ const contentElement = document.createElement("span");
53593
+ contentElement.classList.add(structuredContentInnerClass$1);
53594
+ element.append(contentElement);
53595
+ const domAttrs = Attribute2.mergeAttributes(this.htmlAttributes);
53596
+ updateDOMAttributes(element, { ...domAttrs });
53597
+ return { element, contentElement };
53598
+ }
53599
+ buildView() {
53600
+ const { element } = this.createElement();
53601
+ const dragHandle = this.createDragHandle();
53602
+ element.prepend(dragHandle);
53603
+ element.addEventListener("dragstart", (e) => this.onDragStart(e));
53604
+ this.root = element;
53605
+ }
53606
+ updateView() {
53607
+ const domAttrs = Attribute2.mergeAttributes(this.htmlAttributes);
53608
+ updateDOMAttributes(this.dom, { ...domAttrs });
53609
+ }
53610
+ update(node, decorations, innerDecorations) {
53611
+ const result = super.update(node, decorations, innerDecorations);
53612
+ if (!result) return false;
53613
+ this.updateView();
53614
+ return true;
53615
+ }
53616
+ }
53617
+ const structuredContentClass$1 = "sd-structured-content";
53618
+ const structuredContentInnerClass$1 = "sd-structured-content__content";
53619
+ const StructuredContent = Node$1.create({
53620
+ name: "structuredContent",
53621
+ group: "inline structuredContent",
53165
53622
  inline: true,
53166
53623
  content: "inline*",
53167
- selectable: false,
53168
- childToAttributes: ["runProperties"],
53624
+ isolating: true,
53625
+ atom: false,
53626
+ // false - has editable content.
53627
+ draggable: true,
53169
53628
  addOptions() {
53170
53629
  return {
53171
53630
  htmlAttributes: {
53172
- "data-run": "1"
53631
+ class: structuredContentClass$1,
53632
+ "aria-label": "Structured content node"
53173
53633
  }
53174
53634
  };
53175
53635
  },
53176
53636
  addAttributes() {
53177
53637
  return {
53178
- runProperties: {
53638
+ id: {
53179
53639
  default: null,
53180
- rendered: false,
53181
- keepOnSplit: true
53640
+ parseDOM: (elem) => elem.getAttribute("data-id"),
53641
+ renderDOM: (attrs) => {
53642
+ if (!attrs.id) return {};
53643
+ return { "data-id": attrs.id };
53644
+ }
53182
53645
  },
53183
- rsidR: {
53646
+ tag: {
53184
53647
  default: null,
53185
- rendered: false,
53186
- keepOnSplit: true
53648
+ parseDOM: (elem) => elem.getAttribute("data-tag"),
53649
+ renderDOM: (attrs) => {
53650
+ if (!attrs.tag) return {};
53651
+ return { "data-tag": attrs.tag };
53652
+ }
53187
53653
  },
53188
- rsidRPr: {
53654
+ alias: {
53189
53655
  default: null,
53190
- rendered: false,
53191
- keepOnSplit: true
53656
+ parseDOM: (elem) => elem.getAttribute("data-alias"),
53657
+ renderDOM: (attrs) => {
53658
+ if (!attrs.alias) return {};
53659
+ return { "data-alias": attrs.alias };
53660
+ }
53192
53661
  },
53193
- rsidDel: {
53194
- default: null,
53195
- rendered: false,
53196
- keepOnSplit: true
53662
+ sdtPr: {
53663
+ rendered: false
53197
53664
  }
53198
53665
  };
53199
53666
  },
53200
- addCommands() {
53201
- return {
53202
- splitRun
53203
- };
53204
- },
53205
53667
  parseDOM() {
53206
- return [{ tag: "span[data-run]" }];
53668
+ return [{ tag: "span[data-structured-content]" }];
53207
53669
  },
53208
53670
  renderDOM({ htmlAttributes }) {
53209
- const base2 = Attribute2.mergeAttributes(this.options.htmlAttributes, htmlAttributes);
53210
- return ["span", base2, 0];
53211
- }
53212
- });
53213
- const inputRegex$1 = /^\s*([-+*])\s$/;
53214
- const BulletList = Node$1.create({
53215
- name: "bulletList",
53216
- group: "block list",
53217
- selectable: false,
53218
- content() {
53219
- return `${this.options.itemTypeName}+`;
53671
+ return [
53672
+ "span",
53673
+ Attribute2.mergeAttributes(this.options.htmlAttributes, htmlAttributes, {
53674
+ "data-structured-content": ""
53675
+ }),
53676
+ 0
53677
+ ];
53220
53678
  },
53221
- addOptions() {
53222
- return {
53223
- itemTypeName: "listItem",
53224
- htmlAttributes: {
53225
- "aria-label": "Bullet list node"
53226
- },
53227
- keepMarks: true,
53228
- keepAttributes: false
53679
+ addNodeView() {
53680
+ return (props) => {
53681
+ return new StructuredContentInlineView({ ...props });
53229
53682
  };
53230
- },
53231
- parseDOM() {
53232
- return [{ tag: "ul" }];
53233
- },
53234
- renderDOM({ htmlAttributes }) {
53235
- const attributes = Attribute2.mergeAttributes(this.options.htmlAttributes, htmlAttributes);
53236
- return ["ul", attributes, 0];
53237
- },
53238
- addAttributes() {
53239
- return {
53240
- "list-style-type": {
53241
- default: "bullet",
53242
- rendered: false
53243
- },
53244
- listId: {
53245
- rendered: false
53246
- },
53247
- sdBlockId: {
53248
- default: null,
53249
- keepOnSplit: false,
53250
- parseDOM: (elem) => elem.getAttribute("data-sd-block-id"),
53251
- renderDOM: (attrs) => {
53252
- return attrs.sdBlockId ? { "data-sd-block-id": attrs.sdBlockId } : {};
53253
- }
53254
- },
53255
- attributes: {
53256
- rendered: false,
53257
- keepOnSplit: true
53258
- }
53259
- };
53260
- },
53261
- addCommands() {
53262
- return {
53263
- /**
53264
- * Toggle a bullet list at the current selection
53265
- * @category Command
53266
- * @example
53267
- * // Toggle bullet list on selected text
53268
- * editor.commands.toggleBulletList()
53269
- * @note Converts selected paragraphs to list items or removes list formatting
53270
- */
53271
- toggleBulletList: () => (params2) => {
53272
- return toggleList(this.type)(params2);
53273
- }
53274
- };
53275
- },
53276
- addShortcuts() {
53277
- return {
53278
- "Mod-Shift-8": () => {
53279
- return this.editor.commands.toggleBulletList();
53280
- }
53281
- };
53282
- },
53283
- addInputRules() {
53284
- return [
53285
- new InputRule({
53286
- match: inputRegex$1,
53287
- handler: ({ state: state2, range: range2 }) => {
53288
- const $pos = state2.selection.$from;
53289
- const listItemType = state2.schema.nodes.listItem;
53290
- for (let depth = $pos.depth; depth >= 0; depth--) {
53291
- if ($pos.node(depth).type === listItemType) {
53292
- return null;
53293
- }
53294
- }
53295
- const { tr } = state2;
53296
- tr.delete(range2.from, range2.to);
53297
- ListHelpers.createNewList({
53298
- listType: this.type,
53299
- tr,
53300
- editor: this.editor
53301
- });
53302
- }
53303
- })
53304
- ];
53305
53683
  }
53306
53684
  });
53307
- const inputRegex = /^(\d+)\.\s$/;
53308
- const OrderedList = Node$1.create({
53309
- name: "orderedList",
53310
- group: "block list",
53311
- selectable: false,
53312
- content() {
53313
- return `${this.options.itemTypeName}+`;
53314
- },
53685
+ class StructuredContentBlockView extends StructuredContentViewBase {
53686
+ constructor(props) {
53687
+ super(props);
53688
+ }
53689
+ mount() {
53690
+ this.buildView();
53691
+ }
53692
+ get contentDOM() {
53693
+ const contentElement = this.dom?.querySelector(`.${structuredContentInnerClass}`);
53694
+ return contentElement || null;
53695
+ }
53696
+ createElement() {
53697
+ const element = document.createElement("div");
53698
+ element.classList.add(structuredContentClass);
53699
+ element.setAttribute("data-structured-content-block", "");
53700
+ const contentElement = document.createElement("div");
53701
+ contentElement.classList.add(structuredContentInnerClass);
53702
+ element.append(contentElement);
53703
+ const domAttrs = Attribute2.mergeAttributes(this.htmlAttributes);
53704
+ updateDOMAttributes(element, { ...domAttrs });
53705
+ return { element, contentElement };
53706
+ }
53707
+ buildView() {
53708
+ const { element } = this.createElement();
53709
+ const dragHandle = this.createDragHandle();
53710
+ element.prepend(dragHandle);
53711
+ element.addEventListener("dragstart", (e) => this.onDragStart(e));
53712
+ this.root = element;
53713
+ }
53714
+ updateView() {
53715
+ const domAttrs = Attribute2.mergeAttributes(this.htmlAttributes);
53716
+ updateDOMAttributes(this.dom, { ...domAttrs });
53717
+ }
53718
+ update(node, decorations, innerDecorations) {
53719
+ const result = super.update(node, decorations, innerDecorations);
53720
+ if (!result) return false;
53721
+ this.updateView();
53722
+ return true;
53723
+ }
53724
+ }
53725
+ const structuredContentClass = "sd-structured-content-block";
53726
+ const structuredContentInnerClass = "sd-structured-content-block__content";
53727
+ const StructuredContentBlock = Node$1.create({
53728
+ name: "structuredContentBlock",
53729
+ group: "block structuredContent",
53730
+ content: "block*",
53731
+ isolating: true,
53732
+ atom: false,
53733
+ // false - has editable content.
53734
+ draggable: true,
53315
53735
  addOptions() {
53316
53736
  return {
53317
- itemTypeName: "listItem",
53318
53737
  htmlAttributes: {
53319
- "aria-label": "Ordered list node"
53320
- },
53321
- keepMarks: true,
53322
- keepAttributes: false,
53323
- listStyleTypes: ["decimal", "lowerAlpha", "lowerRoman"]
53738
+ class: structuredContentClass,
53739
+ "aria-label": "Structured content block node"
53740
+ }
53324
53741
  };
53325
53742
  },
53326
53743
  addAttributes() {
53327
53744
  return {
53328
- order: {
53329
- default: 1,
53330
- parseDOM: (element) => {
53331
- return element.hasAttribute("start") ? parseInt(element.getAttribute("start") || "", 10) : 1;
53332
- },
53333
- renderDOM: (attrs) => {
53334
- return {
53335
- start: attrs.order
53336
- };
53337
- }
53338
- },
53339
- sdBlockId: {
53745
+ id: {
53340
53746
  default: null,
53341
- keepOnSplit: false,
53342
- parseDOM: (elem) => elem.getAttribute("data-sd-block-id"),
53747
+ parseDOM: (elem) => elem.getAttribute("data-id"),
53343
53748
  renderDOM: (attrs) => {
53344
- return attrs.sdBlockId ? { "data-sd-block-id": attrs.sdBlockId } : {};
53749
+ if (!attrs.id) return {};
53750
+ return { "data-id": attrs.id };
53345
53751
  }
53346
53752
  },
53347
- syncId: {
53753
+ tag: {
53348
53754
  default: null,
53349
- parseDOM: (elem) => elem.getAttribute("data-sync-id"),
53755
+ parseDOM: (elem) => elem.getAttribute("data-tag"),
53350
53756
  renderDOM: (attrs) => {
53351
- if (!attrs.syncId) return {};
53352
- return {
53353
- "data-sync-id": attrs.syncId
53354
- };
53757
+ if (!attrs.tag) return {};
53758
+ return { "data-tag": attrs.tag };
53355
53759
  }
53356
- // rendered: false,
53357
53760
  },
53358
- listId: {
53359
- keepOnSplit: true,
53360
- parseDOM: (elem) => elem.getAttribute("data-list-id"),
53761
+ alias: {
53762
+ default: null,
53763
+ parseDOM: (elem) => elem.getAttribute("data-alias"),
53361
53764
  renderDOM: (attrs) => {
53362
- if (!attrs.listId) return {};
53363
- return {
53364
- "data-list-id": attrs.listId
53365
- };
53765
+ if (!attrs.alias) return {};
53766
+ return { "data-alias": attrs.alias };
53366
53767
  }
53367
53768
  },
53368
- "list-style-type": {
53369
- default: "decimal",
53769
+ sdtPr: {
53370
53770
  rendered: false
53371
- },
53372
- attributes: {
53373
- rendered: false,
53374
- keepOnSplit: true
53375
53771
  }
53376
53772
  };
53377
53773
  },
53378
53774
  parseDOM() {
53379
- return [{ tag: "ol" }];
53775
+ return [{ tag: "div[data-structured-content-block]" }];
53380
53776
  },
53381
53777
  renderDOM({ htmlAttributes }) {
53382
- const { start: start2, ...restAttributes } = htmlAttributes;
53383
- return start2 === 1 ? ["ol", Attribute2.mergeAttributes(this.options.htmlAttributes, restAttributes), 0] : ["ol", Attribute2.mergeAttributes(this.options.htmlAttributes, htmlAttributes), 0];
53778
+ return [
53779
+ "div",
53780
+ Attribute2.mergeAttributes(this.options.htmlAttributes, htmlAttributes, {
53781
+ "data-structured-content-block": ""
53782
+ }),
53783
+ 0
53784
+ ];
53384
53785
  },
53786
+ addNodeView() {
53787
+ return (props) => {
53788
+ return new StructuredContentBlockView({ ...props });
53789
+ };
53790
+ }
53791
+ });
53792
+ function getStructuredContentTagsById(idOrIds, state2) {
53793
+ const result = findChildren$5(state2.doc, (node) => {
53794
+ const isStructuredContent = ["structuredContent", "structuredContentBlock"].includes(node.type.name);
53795
+ if (Array.isArray(idOrIds)) {
53796
+ return isStructuredContent && idOrIds.includes(node.attrs.id);
53797
+ } else {
53798
+ return isStructuredContent && node.attrs.id === idOrIds;
53799
+ }
53800
+ });
53801
+ return result;
53802
+ }
53803
+ function getStructuredContentTags(state2) {
53804
+ const result = findChildren$5(state2.doc, (node) => {
53805
+ return node.type.name === "structuredContent" || node.type.name === "structuredContentBlock";
53806
+ });
53807
+ return result;
53808
+ }
53809
+ function getStructuredContentInlineTags(state2) {
53810
+ const result = findChildren$5(state2.doc, (node) => node.type.name === "structuredContent");
53811
+ return result;
53812
+ }
53813
+ function getStructuredContentBlockTags(state2) {
53814
+ const result = findChildren$5(state2.doc, (node) => node.type.name === "structuredContentBlock");
53815
+ return result;
53816
+ }
53817
+ const structuredContentHelpers = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
53818
+ __proto__: null,
53819
+ getStructuredContentBlockTags,
53820
+ getStructuredContentInlineTags,
53821
+ getStructuredContentTags,
53822
+ getStructuredContentTagsById
53823
+ }, Symbol.toStringTag, { value: "Module" }));
53824
+ const STRUCTURED_CONTENT_NAMES = ["structuredContent", "structuredContentBlock"];
53825
+ const StructuredContentCommands = Extension.create({
53826
+ name: "structuredContentCommands",
53385
53827
  addCommands() {
53386
53828
  return {
53387
53829
  /**
53388
- * Toggle ordered list formatting
53830
+ * Inserts a structured content inline at selection.
53389
53831
  * @category Command
53390
- * @example
53391
- * editor.commands.toggleOrderedList()
53392
- * @note Converts selection to ordered list or back to paragraphs
53832
+ * @param {StructuredContentInlineInsert} options
53393
53833
  */
53394
- toggleOrderedList: () => (params2) => {
53395
- return toggleList(this.type)(params2);
53834
+ insertStructuredContentInline: (options = {}) => ({ editor, dispatch, state: state2, tr }) => {
53835
+ const { schema } = editor;
53836
+ let { from: from2, to } = state2.selection;
53837
+ if (dispatch) {
53838
+ const selectionText = state2.doc.textBetween(from2, to);
53839
+ let content = null;
53840
+ if (selectionText) {
53841
+ content = schema.text(selectionText);
53842
+ }
53843
+ if (options.text) {
53844
+ content = schema.text(options.text);
53845
+ }
53846
+ if (options.json) {
53847
+ content = schema.nodeFromJSON(options.json);
53848
+ }
53849
+ if (!content) {
53850
+ content = schema.text(" ");
53851
+ }
53852
+ const attrs = {
53853
+ ...options.attrs,
53854
+ id: options.attrs?.id || randomId(),
53855
+ tag: "inline_text_sdt",
53856
+ alias: options.attrs?.alias || "Structured content"
53857
+ };
53858
+ const node = schema.nodes.structuredContent.create(attrs, content, null);
53859
+ const parent = findParentNode((node2) => node2.type.name === "structuredContent")(state2.selection);
53860
+ if (parent) {
53861
+ const insertPos = parent.pos + parent.node.nodeSize;
53862
+ from2 = to = insertPos;
53863
+ }
53864
+ tr.replaceWith(from2, to, node);
53865
+ }
53866
+ return true;
53396
53867
  },
53397
53868
  /**
53398
- * Restart list node numbering
53869
+ * Inserts a structured content block at selection.
53399
53870
  * @category Command
53400
- * @param {Array} followingNodes - Nodes to restart
53401
- * @param {number} pos - Starting position
53402
- * @example
53403
- * editor.commands.restartListNodes(nodes, position)
53404
- * @note Resets list numbering for specified nodes
53871
+ * @param {StructuredContentBlockInsert} options
53405
53872
  */
53406
- restartListNodes: (followingNodes, pos) => ({ tr }) => {
53407
- let currentNodePos = pos;
53408
- const nodes = followingNodes.map((node) => {
53409
- const resultNode = {
53410
- node,
53411
- pos: currentNodePos
53873
+ insertStructuredContentBlock: (options = {}) => ({ editor, dispatch, state: state2, tr }) => {
53874
+ const { schema } = editor;
53875
+ let { from: from2, to } = state2.selection;
53876
+ if (dispatch) {
53877
+ const selectionContent = state2.selection.content();
53878
+ let content = null;
53879
+ if (selectionContent.size) {
53880
+ content = selectionContent.content;
53881
+ }
53882
+ if (options.html) {
53883
+ const html = htmlHandler(options.html, editor);
53884
+ const doc2 = DOMParser$1.fromSchema(schema).parse(html);
53885
+ content = doc2.content;
53886
+ }
53887
+ if (options.json) {
53888
+ content = schema.nodeFromJSON(options.json);
53889
+ }
53890
+ if (!content) {
53891
+ content = schema.nodeFromJSON({ type: "paragraph", content: [] });
53892
+ }
53893
+ const attrs = {
53894
+ ...options.attrs,
53895
+ id: options.attrs?.id || randomId(),
53896
+ tag: "block_table_sdt",
53897
+ alias: options.attrs?.alias || "Structured content"
53412
53898
  };
53413
- currentNodePos += node.nodeSize;
53414
- return resultNode;
53415
- });
53416
- nodes.forEach((item) => {
53417
- const { pos: pos2 } = item;
53418
- const newPos = tr.mapping.map(pos2);
53419
- tr.setNodeMarkup(newPos, void 0, {});
53420
- });
53899
+ const node = schema.nodes.structuredContentBlock.create(attrs, content, null);
53900
+ const parent = findParentNode((node2) => node2.type.name === "structuredContentBlock")(state2.selection);
53901
+ if (parent) {
53902
+ const insertPos = parent.pos + parent.node.nodeSize;
53903
+ from2 = to = insertPos;
53904
+ }
53905
+ tr.replaceRangeWith(from2, to, node);
53906
+ }
53421
53907
  return true;
53422
53908
  },
53423
53909
  /**
53424
- * Update ordered list style type based on nesting level
53910
+ * Updates a structured content attributes or content.
53911
+ * If the updated node does not match the schema, it will not be updated.
53425
53912
  * @category Command
53426
- * @example
53427
- * editor.commands.updateOrderedListStyleType()
53428
- * @note Cycles through decimal -> lowerAlpha -> lowerRoman based on depth
53913
+ * @param {string} id
53914
+ * @param {StructuredContentUpdate} options
53429
53915
  */
53430
- updateOrderedListStyleType: () => ({ dispatch, tr }) => {
53431
- let list = findParentNode((node) => node.type.name === this.name)(tr.selection);
53432
- if (!list) {
53916
+ updateStructuredContentById: (id, options = {}) => ({ editor, dispatch, state: state2, tr }) => {
53917
+ const structuredContentTags = getStructuredContentTagsById(id, state2);
53918
+ if (!structuredContentTags.length) {
53433
53919
  return true;
53434
53920
  }
53921
+ const { schema } = editor;
53435
53922
  if (dispatch) {
53436
- let listLevel = (list.depth - 1) / 2;
53437
- let listStyleTypes = this.options.listStyleTypes;
53438
- let listStyle = listStyleTypes[listLevel % listStyleTypes.length];
53439
- let currentListStyle = list.node.attrs["list-style-type"];
53440
- let nodeAtPos = tr.doc.nodeAt(list.pos);
53441
- if (currentListStyle !== listStyle && nodeAtPos.eq(list.node)) {
53442
- tr.setNodeMarkup(list.pos, void 0, {
53443
- ...list.node.attrs,
53444
- ...{
53445
- "list-style-type": listStyle
53446
- }
53447
- });
53923
+ const structuredContent = structuredContentTags[0];
53924
+ const { pos, node } = structuredContent;
53925
+ const posFrom = pos;
53926
+ const posTo = pos + node.nodeSize;
53927
+ let content = null;
53928
+ if (options.text) {
53929
+ content = schema.text(options.text);
53930
+ }
53931
+ if (options.html) {
53932
+ const html = htmlHandler(options.html, editor);
53933
+ const doc2 = DOMParser$1.fromSchema(schema).parse(html);
53934
+ content = doc2.content;
53935
+ }
53936
+ if (options.json) {
53937
+ content = schema.nodeFromJSON(options.json);
53448
53938
  }
53939
+ if (!content) {
53940
+ content = node.content;
53941
+ }
53942
+ const updatedNode = node.type.create({ ...node.attrs, ...options.attrs }, content, node.marks);
53943
+ try {
53944
+ updatedNode.check();
53945
+ } catch {
53946
+ console.error("Updated node does not conform to the schema");
53947
+ return false;
53948
+ }
53949
+ tr.replaceWith(posFrom, posTo, updatedNode);
53950
+ }
53951
+ return true;
53952
+ },
53953
+ /**
53954
+ * Removes a structured content.
53955
+ * @category Command
53956
+ * @param {Array<{ node: Node, pos: number }>} structuredContentTags
53957
+ */
53958
+ deleteStructuredContent: (structuredContentTags) => ({ dispatch, tr }) => {
53959
+ if (!structuredContentTags.length) {
53960
+ return true;
53961
+ }
53962
+ if (dispatch) {
53963
+ structuredContentTags.forEach((structuredContent) => {
53964
+ const { pos, node } = structuredContent;
53965
+ const posFrom = tr.mapping.map(pos);
53966
+ const posTo = tr.mapping.map(pos + node.nodeSize);
53967
+ const currentNode = tr.doc.nodeAt(posFrom);
53968
+ if (currentNode && node.eq(currentNode)) {
53969
+ tr.delete(posFrom, posTo);
53970
+ }
53971
+ });
53972
+ }
53973
+ return true;
53974
+ },
53975
+ /**
53976
+ * Removes a structured content by ID.
53977
+ * @category Command
53978
+ * @param {string | string[]} idOrIds
53979
+ */
53980
+ deleteStructuredContentById: (idOrIds) => ({ dispatch, state: state2, tr }) => {
53981
+ const structuredContentTags = getStructuredContentTagsById(idOrIds, state2);
53982
+ if (!structuredContentTags.length) {
53983
+ return true;
53984
+ }
53985
+ if (dispatch) {
53986
+ structuredContentTags.forEach((structuredContent) => {
53987
+ const { pos, node } = structuredContent;
53988
+ const posFrom = tr.mapping.map(pos);
53989
+ const posTo = tr.mapping.map(pos + node.nodeSize);
53990
+ const currentNode = tr.doc.nodeAt(posFrom);
53991
+ if (currentNode && node.eq(currentNode)) {
53992
+ tr.delete(posFrom, posTo);
53993
+ }
53994
+ });
53995
+ }
53996
+ return true;
53997
+ },
53998
+ /**
53999
+ * Removes a structured content at cursor, preserving its content.
54000
+ * @category Command
54001
+ */
54002
+ deleteStructuredContentAtSelection: () => ({ editor, dispatch, state: state2, tr }) => {
54003
+ const predicate = (node) => STRUCTURED_CONTENT_NAMES.includes(node.type.name);
54004
+ const structuredContent = findParentNode(predicate)(state2.selection);
54005
+ if (!structuredContent) {
54006
+ return true;
54007
+ }
54008
+ if (dispatch) {
54009
+ const { node, pos } = structuredContent;
54010
+ const posFrom = pos;
54011
+ const posTo = posFrom + node.nodeSize;
54012
+ const content = node.content;
54013
+ tr.replaceWith(posFrom, posTo, content);
53449
54014
  }
53450
54015
  return true;
53451
54016
  }
53452
54017
  };
53453
54018
  },
53454
- addShortcuts() {
54019
+ addHelpers() {
53455
54020
  return {
53456
- "Mod-Shift-7": () => {
53457
- return this.editor.commands.toggleOrderedList();
53458
- }
54021
+ ...structuredContentHelpers
53459
54022
  };
53460
- },
53461
- addInputRules() {
53462
- return [
53463
- new InputRule({
53464
- match: inputRegex,
53465
- handler: ({ state: state2, range: range2 }) => {
53466
- const $pos = state2.selection.$from;
53467
- const listItemType = state2.schema.nodes.listItem;
53468
- for (let depth = $pos.depth; depth >= 0; depth--) {
53469
- if ($pos.node(depth).type === listItemType) {
53470
- return null;
53471
- }
53472
- }
53473
- const { tr } = state2;
53474
- tr.delete(range2.from, range2.to);
53475
- ListHelpers.createNewList({
53476
- listType: this.type,
53477
- tr,
53478
- editor: this.editor
53479
- });
53480
- }
53481
- })
53482
- ];
53483
54023
  }
53484
54024
  });
53485
- const generateOrderedListIndex = ({ listLevel, lvlText, listNumberingType, customFormat }) => {
53486
- const handler2 = listIndexMap[listNumberingType];
53487
- return handler2 ? handler2(listLevel, lvlText, customFormat) : null;
53488
- };
53489
- const handleDecimal = (path, lvlText) => generateNumbering(path, lvlText, String);
53490
- const handleRoman = (path, lvlText) => generateNumbering(path, lvlText, intToRoman);
53491
- const handleLowerRoman = (path, lvlText) => handleRoman(path, lvlText).toLowerCase();
53492
- const handleLowerAlpha = (path, lvlText) => handleAlpha(path, lvlText).toLowerCase();
53493
- const handleAlpha = (path, lvlText) => generateNumbering(path, lvlText, (p) => intToAlpha(p));
53494
- const handleOrdinal = (path, lvlText) => generateNumbering(path, lvlText, ordinalFormatter);
53495
- const handleCustom = (path, lvlText, customFormat) => generateFromCustom(path, lvlText, customFormat);
53496
- const handleJapaneseCounting = (path, lvlText) => generateNumbering(path, lvlText, intToJapaneseCounting);
53497
- const listIndexMap = {
53498
- decimal: handleDecimal,
53499
- lowerRoman: handleLowerRoman,
53500
- upperRoman: handleRoman,
53501
- lowerLetter: handleLowerAlpha,
53502
- upperLetter: handleAlpha,
53503
- ordinal: handleOrdinal,
53504
- custom: handleCustom,
53505
- japaneseCounting: handleJapaneseCounting
53506
- };
53507
- const createNumbering = (values, lvlText) => {
53508
- return values.reduce((acc, value, index2) => {
53509
- return value > 9 ? acc.replace(/^0/, "").replace(`%${index2 + 1}`, value) : acc.replace(`%${index2 + 1}`, value);
53510
- }, lvlText);
53511
- };
53512
- const generateNumbering = (path, lvlText, formatter) => {
53513
- const formattedValues = path.map(formatter);
53514
- return createNumbering(formattedValues, lvlText);
54025
+ const randomId = () => {
54026
+ return Math.floor(Math.random() * 4294967295).toString();
53515
54027
  };
53516
- const ordinalFormatter = (level) => {
53517
- const suffixes = ["th", "st", "nd", "rd"];
53518
- const value = level % 100;
53519
- const suffix2 = suffixes[(value - 20) % 10] || suffixes[value] || suffixes[0];
53520
- const p = level + suffix2;
53521
- return p;
54028
+ class DocumentSectionView {
54029
+ constructor(node, getPos, decorations, editor) {
54030
+ __privateAdd$1(this, _DocumentSectionView_instances);
54031
+ this.node = node;
54032
+ this.editor = editor;
54033
+ this.decorations = decorations;
54034
+ this.view = editor.view;
54035
+ this.getPos = getPos;
54036
+ __privateMethod$1(this, _DocumentSectionView_instances, init_fn2).call(this);
54037
+ }
54038
+ }
54039
+ _DocumentSectionView_instances = /* @__PURE__ */ new WeakSet();
54040
+ init_fn2 = function() {
54041
+ const { attrs } = this.node;
54042
+ const { id, title, description } = attrs;
54043
+ this.dom = document.createElement("div");
54044
+ this.dom.className = "sd-document-section-block";
54045
+ this.dom.setAttribute("data-id", id);
54046
+ this.dom.setAttribute("data-title", title);
54047
+ this.dom.setAttribute("data-description", description);
54048
+ this.dom.setAttribute("aria-label", "Document section");
54049
+ __privateMethod$1(this, _DocumentSectionView_instances, addToolTip_fn).call(this);
54050
+ this.contentDOM = document.createElement("div");
54051
+ this.contentDOM.className = "sd-document-section-block-content";
54052
+ this.contentDOM.setAttribute("contenteditable", "true");
54053
+ this.dom.appendChild(this.contentDOM);
53522
54054
  };
53523
- const generateFromCustom = (path, lvlText, customFormat) => {
53524
- if (customFormat !== "001, 002, 003, ...") return generateNumbering(path, lvlText, String);
53525
- const match = customFormat.match(/(\d+)/);
53526
- if (!match) throw new Error("Invalid format string: no numeric pattern found");
53527
- const sample = match[1];
53528
- const digitCount = sample.length;
53529
- const index2 = path.pop();
53530
- return String(index2).padStart(digitCount, "0");
54055
+ addToolTip_fn = function() {
54056
+ const { title } = this.node.attrs;
54057
+ this.infoDiv = document.createElement("div");
54058
+ this.infoDiv.className = "sd-document-section-block-info";
54059
+ const textSpan = document.createElement("span");
54060
+ textSpan.textContent = title || "Document section";
54061
+ this.infoDiv.appendChild(textSpan);
54062
+ this.infoDiv.setAttribute("contenteditable", "false");
54063
+ this.dom.appendChild(this.infoDiv);
53531
54064
  };
53532
- const intToRoman = (num) => {
53533
- const romanNumeralMap = [
53534
- { value: 1e3, numeral: "M" },
53535
- { value: 900, numeral: "CM" },
53536
- { value: 500, numeral: "D" },
53537
- { value: 400, numeral: "CD" },
53538
- { value: 100, numeral: "C" },
53539
- { value: 90, numeral: "XC" },
53540
- { value: 50, numeral: "L" },
53541
- { value: 40, numeral: "XL" },
53542
- { value: 10, numeral: "X" },
53543
- { value: 9, numeral: "IX" },
53544
- { value: 5, numeral: "V" },
53545
- { value: 4, numeral: "IV" },
53546
- { value: 1, numeral: "I" }
53547
- ];
53548
- let result = "";
53549
- for (const { value, numeral } of romanNumeralMap) {
53550
- while (num >= value) {
53551
- result += numeral;
53552
- num -= value;
54065
+ const getAllSections = (editor) => {
54066
+ if (!editor) return [];
54067
+ const type2 = editor.schema.nodes.documentSection;
54068
+ if (!type2) return [];
54069
+ const sections = [];
54070
+ const { state: state2 } = editor;
54071
+ state2.doc.descendants((node, pos) => {
54072
+ if (node.type.name === type2.name) {
54073
+ sections.push({ node, pos });
53553
54074
  }
53554
- }
53555
- return result;
54075
+ });
54076
+ return sections;
53556
54077
  };
53557
- const intToAlpha = (num) => {
53558
- let result = "";
53559
- const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
53560
- while (num > 0) {
53561
- let index2 = (num - 1) % 26;
53562
- result = alphabet[index2] + result;
53563
- num = Math.floor((num - 1) / 26);
53564
- }
54078
+ const exportSectionsToHTML = (editor) => {
54079
+ const sections = getAllSections(editor);
54080
+ const processedSections = /* @__PURE__ */ new Set();
54081
+ const result = [];
54082
+ sections.forEach(({ node }) => {
54083
+ const { attrs } = node;
54084
+ const { id, title, description } = attrs;
54085
+ if (processedSections.has(id)) return;
54086
+ processedSections.add(id);
54087
+ const html = getHTMLFromNode(node, editor);
54088
+ result.push({
54089
+ id,
54090
+ title,
54091
+ description,
54092
+ html
54093
+ });
54094
+ });
53565
54095
  return result;
53566
54096
  };
53567
- const intToJapaneseCounting = (num) => {
53568
- const digits = ["", "一", "二", "三", "四", "五", "六", "七", "八", "九"];
53569
- const units = ["", "十", "百", "千"];
53570
- if (num === 0) return "零";
53571
- if (num < 10) return digits[num];
53572
- let result = "";
53573
- let tempNum = num;
53574
- let unitIndex = 0;
53575
- while (tempNum > 0) {
53576
- const digit = tempNum % 10;
53577
- if (digit !== 0) {
53578
- const digitStr = digit === 1 && unitIndex > 0 ? "" : digits[digit];
53579
- result = digitStr + (unitIndex > 0 ? units[unitIndex] : "") + result;
53580
- } else if (result && tempNum > 0) {
53581
- if (!result.startsWith("零") && tempNum % 100 !== 0) {
53582
- result = "零" + result;
53583
- }
53584
- }
53585
- tempNum = Math.floor(tempNum / 10);
53586
- unitIndex++;
53587
- if (unitIndex > 3) break;
53588
- }
53589
- if (num >= 10 && num < 20) {
53590
- result = result.replace(/^一十/, "十");
53591
- }
54097
+ const getHTMLFromNode = (node, editor) => {
54098
+ const tempDocument = document.implementation.createHTMLDocument();
54099
+ const container = tempDocument.createElement("div");
54100
+ const fragment = DOMSerializer.fromSchema(editor.schema).serializeFragment(node.content);
54101
+ container.appendChild(fragment);
54102
+ let html = container.innerHTML;
54103
+ return html;
54104
+ };
54105
+ const exportSectionsToJSON = (editor) => {
54106
+ const sections = getAllSections(editor);
54107
+ const processedSections = /* @__PURE__ */ new Set();
54108
+ const result = [];
54109
+ sections.forEach(({ node }) => {
54110
+ const { attrs } = node;
54111
+ const { id, title, description } = attrs;
54112
+ if (processedSections.has(id)) return;
54113
+ processedSections.add(id);
54114
+ result.push({
54115
+ id,
54116
+ title,
54117
+ description,
54118
+ content: node.toJSON()
54119
+ });
54120
+ });
53592
54121
  return result;
53593
54122
  };
53594
- const CustomSelectionPluginKey = new PluginKey("CustomSelection");
53595
- const shouldAllowNativeContextMenu = (event) => {
53596
- if (!event) return false;
53597
- if (event.ctrlKey || event.metaKey) {
53598
- return true;
53599
- }
53600
- const isKeyboardInvocation = event.type === "contextmenu" && typeof event.detail === "number" && event.detail === 0 && (event.button === 0 || event.button === void 0) && event.clientX === 0 && event.clientY === 0;
53601
- return Boolean(isKeyboardInvocation);
54123
+ const getLinkedSectionEditor = (id, options, editor) => {
54124
+ const sections = getAllSections(editor);
54125
+ const section = sections.find((s) => s.node.attrs.id === id);
54126
+ if (!section) return null;
54127
+ const child = editor.createChildEditor({
54128
+ ...options,
54129
+ onUpdate: ({ editor: childEditor, transaction }) => {
54130
+ const isFromtLinkedParent = transaction.getMeta("fromLinkedParent");
54131
+ if (isFromtLinkedParent) return;
54132
+ const updatedContent = childEditor.state.doc.content;
54133
+ const sectionNode = getAllSections(editor)?.find((s) => s.node.attrs.id === id);
54134
+ if (!sectionNode) return;
54135
+ const { pos, node } = sectionNode;
54136
+ const newNode = node.type.create(node.attrs, updatedContent, node.marks);
54137
+ const tr = editor.state.tr.replaceWith(pos, pos + node.nodeSize, newNode);
54138
+ tr.setMeta("fromLinkedChild", true);
54139
+ editor.view.dispatch(tr);
54140
+ }
54141
+ });
54142
+ editor.on("update", ({ transaction }) => {
54143
+ const isFromLinkedChild = transaction.getMeta("fromLinkedChild");
54144
+ if (isFromLinkedChild) return;
54145
+ const sectionNode = getAllSections(editor)?.find((s) => s.node.attrs.id === id);
54146
+ if (!sectionNode) return;
54147
+ const sectionContent = sectionNode.node.content;
54148
+ const json = {
54149
+ type: "doc",
54150
+ content: sectionContent.content.map((node) => node.toJSON())
54151
+ };
54152
+ const childTr = child.state.tr;
54153
+ childTr.setMeta("fromLinkedParent", true);
54154
+ childTr.replaceWith(0, child.state.doc.content.size, child.schema.nodeFromJSON(json));
54155
+ child.view.dispatch(childTr);
54156
+ });
54157
+ return child;
54158
+ };
54159
+ const SectionHelpers = {
54160
+ getAllSections,
54161
+ exportSectionsToHTML,
54162
+ exportSectionsToJSON,
54163
+ getLinkedSectionEditor
54164
+ };
54165
+ const DocumentSection = Node$1.create({
54166
+ name: "documentSection",
54167
+ group: "block",
54168
+ content: "block*",
54169
+ atom: true,
54170
+ isolating: true,
54171
+ addOptions() {
54172
+ return {
54173
+ htmlAttributes: {
54174
+ class: "sd-document-section-block",
54175
+ "aria-label": "Structured content block"
54176
+ }
54177
+ };
54178
+ },
54179
+ parseDOM() {
54180
+ return [
54181
+ {
54182
+ tag: "div.sd-document-section-block",
54183
+ priority: 60
54184
+ }
54185
+ ];
54186
+ },
54187
+ renderDOM({ htmlAttributes }) {
54188
+ return ["div", Attribute2.mergeAttributes(this.options.htmlAttributes, htmlAttributes), 0];
54189
+ },
54190
+ addAttributes() {
54191
+ return {
54192
+ id: {},
54193
+ sdBlockId: {
54194
+ default: null,
54195
+ keepOnSplit: false,
54196
+ parseDOM: (elem) => elem.getAttribute("data-sd-block-id"),
54197
+ renderDOM: (attrs) => {
54198
+ return attrs.sdBlockId ? { "data-sd-block-id": attrs.sdBlockId } : {};
54199
+ }
54200
+ },
54201
+ title: {},
54202
+ description: {},
54203
+ sectionType: {},
54204
+ isLocked: { default: false }
54205
+ };
54206
+ },
54207
+ addNodeView() {
54208
+ return ({ node, editor, getPos, decorations }) => {
54209
+ return new DocumentSectionView(node, getPos, decorations, editor);
54210
+ };
54211
+ },
54212
+ addCommands() {
54213
+ return {
54214
+ /**
54215
+ * Create a lockable content section
54216
+ * @category Command
54217
+ * @param {SectionCreate} [options={}] - Section configuration
54218
+ * @example
54219
+ * editor.commands.createDocumentSection({
54220
+ * id: 1,
54221
+ * title: 'Terms & Conditions',
54222
+ * isLocked: true,
54223
+ * html: '<p>Legal content...</p>'
54224
+ * })
54225
+ */
54226
+ createDocumentSection: (options = {}) => ({ tr, state: state2, dispatch, editor }) => {
54227
+ const { selection } = state2;
54228
+ let { from: from2, to } = selection;
54229
+ let content = selection.content().content;
54230
+ const { html: optionsHTML, json: optionsJSON } = options;
54231
+ if (optionsHTML) {
54232
+ const html = htmlHandler(optionsHTML, this.editor);
54233
+ const doc2 = DOMParser$1.fromSchema(this.editor.schema).parse(html);
54234
+ content = doc2.content;
54235
+ }
54236
+ if (optionsJSON) {
54237
+ content = this.editor.schema.nodeFromJSON(optionsJSON);
54238
+ }
54239
+ if (!content?.content?.length) {
54240
+ content = this.editor.schema.nodeFromJSON({ type: "paragraph", content: [] });
54241
+ }
54242
+ if (!options.id) {
54243
+ const allSections = SectionHelpers.getAllSections(editor);
54244
+ options.id = allSections.length + 1;
54245
+ }
54246
+ if (!options.title) {
54247
+ options.title = "Document section";
54248
+ }
54249
+ const node = this.type.createAndFill(options, content);
54250
+ if (!node) return false;
54251
+ const isAlreadyInSdtBlock = findParentNode((node2) => node2.type.name === "documentSection")(selection);
54252
+ if (isAlreadyInSdtBlock && isAlreadyInSdtBlock.node) {
54253
+ const insertPos2 = isAlreadyInSdtBlock.pos + isAlreadyInSdtBlock.node.nodeSize;
54254
+ from2 = insertPos2;
54255
+ to = insertPos2;
54256
+ }
54257
+ tr.replaceRangeWith(from2, to, node);
54258
+ const nodeEnd = from2 + node.nodeSize;
54259
+ let shouldInsertParagraph = true;
54260
+ let insertPos = nodeEnd;
54261
+ if (nodeEnd >= tr.doc.content.size) {
54262
+ insertPos = tr.doc.content.size;
54263
+ if (insertPos > 0) {
54264
+ const $endPos = tr.doc.resolve(insertPos);
54265
+ if ($endPos.nodeBefore && $endPos.nodeBefore.type.name === "paragraph") {
54266
+ shouldInsertParagraph = false;
54267
+ }
54268
+ }
54269
+ }
54270
+ if (shouldInsertParagraph) {
54271
+ const emptyParagraph = tr.doc.type.schema.nodes.paragraph.create();
54272
+ tr.insert(insertPos, emptyParagraph);
54273
+ }
54274
+ if (dispatch) {
54275
+ tr.setMeta("documentSection", { action: "create" });
54276
+ dispatch(tr);
54277
+ setTimeout(() => {
54278
+ try {
54279
+ const currentState = editor.state;
54280
+ const docSize = currentState.doc.content.size;
54281
+ let targetPos = from2 + node.nodeSize;
54282
+ if (shouldInsertParagraph) {
54283
+ targetPos += 1;
54284
+ }
54285
+ targetPos = Math.min(targetPos, docSize);
54286
+ if (targetPos < docSize && targetPos > 0) {
54287
+ const newSelection = Selection.near(currentState.doc.resolve(targetPos));
54288
+ const newTr = currentState.tr.setSelection(newSelection);
54289
+ editor.view.dispatch(newTr);
54290
+ }
54291
+ } catch (e) {
54292
+ console.warn("Could not set delayed selection:", e);
54293
+ }
54294
+ }, 0);
54295
+ }
54296
+ return true;
54297
+ },
54298
+ /**
54299
+ * Remove section wrapper at cursor, preserving its content
54300
+ * @category Command
54301
+ * @example
54302
+ * editor.commands.removeSectionAtSelection()
54303
+ * @note Content stays in document, only section wrapper is removed
54304
+ */
54305
+ removeSectionAtSelection: () => ({ tr, dispatch }) => {
54306
+ const sdtNode = findParentNode((node2) => node2.type.name === "documentSection")(tr.selection);
54307
+ if (!sdtNode) return false;
54308
+ const { node, pos } = sdtNode;
54309
+ const nodeStart = pos;
54310
+ const nodeEnd = nodeStart + node.nodeSize;
54311
+ const contentToPreserve = node.content;
54312
+ tr.delete(nodeStart, nodeEnd);
54313
+ if (contentToPreserve.size > 0) {
54314
+ tr.insert(nodeStart, contentToPreserve);
54315
+ }
54316
+ const newPos = Math.min(nodeStart, tr.doc.content.size);
54317
+ tr.setSelection(Selection.near(tr.doc.resolve(newPos)));
54318
+ if (dispatch) {
54319
+ tr.setMeta("documentSection", { action: "delete" });
54320
+ dispatch(tr);
54321
+ }
54322
+ return true;
54323
+ },
54324
+ /**
54325
+ * Delete section and all its content
54326
+ * @category Command
54327
+ * @param {number} id - Section to delete
54328
+ * @example
54329
+ * editor.commands.removeSectionById(123)
54330
+ */
54331
+ removeSectionById: (id) => ({ tr, dispatch }) => {
54332
+ const sections = SectionHelpers.getAllSections(this.editor);
54333
+ const sectionToRemove = sections.find(({ node: node2 }) => node2.attrs.id === id);
54334
+ if (!sectionToRemove) return false;
54335
+ const { pos, node } = sectionToRemove;
54336
+ const nodeStart = pos;
54337
+ const nodeEnd = nodeStart + node.nodeSize;
54338
+ tr.delete(nodeStart, nodeEnd);
54339
+ if (dispatch) {
54340
+ tr.setMeta("documentSection", { action: "delete", id });
54341
+ dispatch(tr);
54342
+ }
54343
+ return true;
54344
+ },
54345
+ /**
54346
+ * Lock section against edits
54347
+ * @category Command
54348
+ * @param {number} id - Section to lock
54349
+ * @example
54350
+ * editor.commands.lockSectionById(123)
54351
+ */
54352
+ lockSectionById: (id) => ({ tr, dispatch }) => {
54353
+ const sections = SectionHelpers.getAllSections(this.editor);
54354
+ const sectionToLock = sections.find(({ node }) => node.attrs.id === id);
54355
+ if (!sectionToLock) return false;
54356
+ tr.setNodeMarkup(sectionToLock.pos, null, { ...sectionToLock.node.attrs, isLocked: true });
54357
+ if (dispatch) {
54358
+ tr.setMeta("documentSection", { action: "lock", id });
54359
+ dispatch(tr);
54360
+ }
54361
+ return true;
54362
+ },
54363
+ /**
54364
+ * Modify section attributes or content
54365
+ * @category Command
54366
+ * @param {SectionUpdate} options - Changes to apply
54367
+ * @example
54368
+ * editor.commands.updateSectionById({ id: 123, attrs: { isLocked: false } })
54369
+ * editor.commands.updateSectionById({ id: 123, html: '<p>New content</p>' })
54370
+ * editor.commands.updateSectionById({
54371
+ * id: 123,
54372
+ * html: '<p>Updated</p>',
54373
+ * attrs: { title: 'New Title' }
54374
+ * })
54375
+ */
54376
+ updateSectionById: ({ id, html, json, attrs }) => ({ tr, dispatch, editor }) => {
54377
+ const sections = SectionHelpers.getAllSections(editor || this.editor);
54378
+ const sectionToUpdate = sections.find(({ node: node2 }) => node2.attrs.id === id);
54379
+ if (!sectionToUpdate) return false;
54380
+ const { pos, node } = sectionToUpdate;
54381
+ let newContent = null;
54382
+ if (html) {
54383
+ const htmlDoc = htmlHandler(html, editor || this.editor);
54384
+ const doc2 = DOMParser$1.fromSchema((editor || this.editor).schema).parse(htmlDoc);
54385
+ newContent = doc2.content;
54386
+ }
54387
+ if (json) {
54388
+ newContent = (editor || this.editor).schema.nodeFromJSON(json);
54389
+ }
54390
+ if (!newContent) {
54391
+ newContent = node.content;
54392
+ }
54393
+ const updatedNode = node.type.create({ ...node.attrs, ...attrs }, newContent, node.marks);
54394
+ tr.replaceWith(pos, pos + node.nodeSize, updatedNode);
54395
+ if (dispatch) {
54396
+ tr.setMeta("documentSection", { action: "update", id, attrs });
54397
+ dispatch(tr);
54398
+ }
54399
+ return true;
54400
+ }
54401
+ };
54402
+ },
54403
+ addHelpers() {
54404
+ return {
54405
+ ...SectionHelpers
54406
+ };
54407
+ }
54408
+ });
54409
+ const Document = Node$1.create({
54410
+ name: "doc",
54411
+ topNode: true,
54412
+ content: "block+",
54413
+ parseDOM() {
54414
+ return [{ tag: "doc" }];
54415
+ },
54416
+ renderDOM() {
54417
+ return ["doc", 0];
54418
+ },
54419
+ addAttributes() {
54420
+ return {
54421
+ attributes: {
54422
+ rendered: false,
54423
+ "aria-label": "Document node"
54424
+ }
54425
+ };
54426
+ },
54427
+ addCommands() {
54428
+ return {
54429
+ /**
54430
+ * Get document statistics
54431
+ * @category Command
54432
+ * @example
54433
+ * // Get word and character count
54434
+ * const stats = editor.commands.getDocumentStats()
54435
+ * console.log(`${stats.words} words, ${stats.characters} characters`)
54436
+ * @note Returns word count, character count, and paragraph count
54437
+ */
54438
+ getDocumentStats: () => ({ editor }) => {
54439
+ const text = editor.getText();
54440
+ const words = text.split(/\s+/).filter((word) => word.length > 0).length;
54441
+ const characters = text.length;
54442
+ const paragraphs = editor.state.doc.content.childCount;
54443
+ return {
54444
+ words,
54445
+ characters,
54446
+ paragraphs
54447
+ };
54448
+ },
54449
+ /**
54450
+ * Clear entire document
54451
+ * @category Command
54452
+ * @example
54453
+ * editor.commands.clearDocument()
54454
+ * @note Replaces all content with an empty paragraph
54455
+ */
54456
+ clearDocument: () => ({ commands: commands2 }) => {
54457
+ return commands2.setContent("<p></p>");
54458
+ }
54459
+ };
54460
+ }
54461
+ });
54462
+ const Text = Node$1.create({
54463
+ name: "text",
54464
+ group: "inline",
54465
+ inline: true,
54466
+ addOptions() {
54467
+ return {};
54468
+ }
54469
+ });
54470
+ const splitRun = () => (props) => {
54471
+ const { state: state2, view, tr } = props;
54472
+ const { $from, empty: empty2 } = state2.selection;
54473
+ if (!empty2) return false;
54474
+ if ($from.parent.type.name !== "run") return false;
54475
+ const handled = splitBlock(state2, (transaction) => {
54476
+ view.dispatch(transaction);
54477
+ });
54478
+ if (handled) {
54479
+ tr.setMeta("preventDispatch", true);
54480
+ }
54481
+ return handled;
54482
+ };
54483
+ const Run = OxmlNode.create({
54484
+ name: "run",
54485
+ oXmlName: "w:r",
54486
+ group: "inline",
54487
+ inline: true,
54488
+ content: "inline*",
54489
+ selectable: false,
54490
+ childToAttributes: ["runProperties"],
54491
+ addOptions() {
54492
+ return {
54493
+ htmlAttributes: {
54494
+ "data-run": "1"
54495
+ }
54496
+ };
54497
+ },
54498
+ addAttributes() {
54499
+ return {
54500
+ runProperties: {
54501
+ default: null,
54502
+ rendered: false,
54503
+ keepOnSplit: true
54504
+ },
54505
+ rsidR: {
54506
+ default: null,
54507
+ rendered: false,
54508
+ keepOnSplit: true
54509
+ },
54510
+ rsidRPr: {
54511
+ default: null,
54512
+ rendered: false,
54513
+ keepOnSplit: true
54514
+ },
54515
+ rsidDel: {
54516
+ default: null,
54517
+ rendered: false,
54518
+ keepOnSplit: true
54519
+ }
54520
+ };
54521
+ },
54522
+ addCommands() {
54523
+ return {
54524
+ splitRun
54525
+ };
54526
+ },
54527
+ parseDOM() {
54528
+ return [{ tag: "span[data-run]" }];
54529
+ },
54530
+ renderDOM({ htmlAttributes }) {
54531
+ const base2 = Attribute2.mergeAttributes(this.options.htmlAttributes, htmlAttributes);
54532
+ return ["span", base2, 0];
54533
+ }
54534
+ });
54535
+ const inputRegex$1 = /^\s*([-+*])\s$/;
54536
+ const BulletList = Node$1.create({
54537
+ name: "bulletList",
54538
+ group: "block list",
54539
+ selectable: false,
54540
+ content() {
54541
+ return `${this.options.itemTypeName}+`;
54542
+ },
54543
+ addOptions() {
54544
+ return {
54545
+ itemTypeName: "listItem",
54546
+ htmlAttributes: {
54547
+ "aria-label": "Bullet list node"
54548
+ },
54549
+ keepMarks: true,
54550
+ keepAttributes: false
54551
+ };
54552
+ },
54553
+ parseDOM() {
54554
+ return [{ tag: "ul" }];
54555
+ },
54556
+ renderDOM({ htmlAttributes }) {
54557
+ const attributes = Attribute2.mergeAttributes(this.options.htmlAttributes, htmlAttributes);
54558
+ return ["ul", attributes, 0];
54559
+ },
54560
+ addAttributes() {
54561
+ return {
54562
+ "list-style-type": {
54563
+ default: "bullet",
54564
+ rendered: false
54565
+ },
54566
+ listId: {
54567
+ rendered: false
54568
+ },
54569
+ sdBlockId: {
54570
+ default: null,
54571
+ keepOnSplit: false,
54572
+ parseDOM: (elem) => elem.getAttribute("data-sd-block-id"),
54573
+ renderDOM: (attrs) => {
54574
+ return attrs.sdBlockId ? { "data-sd-block-id": attrs.sdBlockId } : {};
54575
+ }
54576
+ },
54577
+ attributes: {
54578
+ rendered: false,
54579
+ keepOnSplit: true
54580
+ }
54581
+ };
54582
+ },
54583
+ addCommands() {
54584
+ return {
54585
+ /**
54586
+ * Toggle a bullet list at the current selection
54587
+ * @category Command
54588
+ * @example
54589
+ * // Toggle bullet list on selected text
54590
+ * editor.commands.toggleBulletList()
54591
+ * @note Converts selected paragraphs to list items or removes list formatting
54592
+ */
54593
+ toggleBulletList: () => (params2) => {
54594
+ return toggleList(this.type)(params2);
54595
+ }
54596
+ };
54597
+ },
54598
+ addShortcuts() {
54599
+ return {
54600
+ "Mod-Shift-8": () => {
54601
+ return this.editor.commands.toggleBulletList();
54602
+ }
54603
+ };
54604
+ },
54605
+ addInputRules() {
54606
+ return [
54607
+ new InputRule({
54608
+ match: inputRegex$1,
54609
+ handler: ({ state: state2, range: range2 }) => {
54610
+ const $pos = state2.selection.$from;
54611
+ const listItemType = state2.schema.nodes.listItem;
54612
+ for (let depth = $pos.depth; depth >= 0; depth--) {
54613
+ if ($pos.node(depth).type === listItemType) {
54614
+ return null;
54615
+ }
54616
+ }
54617
+ const { tr } = state2;
54618
+ tr.delete(range2.from, range2.to);
54619
+ ListHelpers.createNewList({
54620
+ listType: this.type,
54621
+ tr,
54622
+ editor: this.editor
54623
+ });
54624
+ }
54625
+ })
54626
+ ];
54627
+ }
54628
+ });
54629
+ const inputRegex = /^(\d+)\.\s$/;
54630
+ const OrderedList = Node$1.create({
54631
+ name: "orderedList",
54632
+ group: "block list",
54633
+ selectable: false,
54634
+ content() {
54635
+ return `${this.options.itemTypeName}+`;
54636
+ },
54637
+ addOptions() {
54638
+ return {
54639
+ itemTypeName: "listItem",
54640
+ htmlAttributes: {
54641
+ "aria-label": "Ordered list node"
54642
+ },
54643
+ keepMarks: true,
54644
+ keepAttributes: false,
54645
+ listStyleTypes: ["decimal", "lowerAlpha", "lowerRoman"]
54646
+ };
54647
+ },
54648
+ addAttributes() {
54649
+ return {
54650
+ order: {
54651
+ default: 1,
54652
+ parseDOM: (element) => {
54653
+ return element.hasAttribute("start") ? parseInt(element.getAttribute("start") || "", 10) : 1;
54654
+ },
54655
+ renderDOM: (attrs) => {
54656
+ return {
54657
+ start: attrs.order
54658
+ };
54659
+ }
54660
+ },
54661
+ sdBlockId: {
54662
+ default: null,
54663
+ keepOnSplit: false,
54664
+ parseDOM: (elem) => elem.getAttribute("data-sd-block-id"),
54665
+ renderDOM: (attrs) => {
54666
+ return attrs.sdBlockId ? { "data-sd-block-id": attrs.sdBlockId } : {};
54667
+ }
54668
+ },
54669
+ syncId: {
54670
+ default: null,
54671
+ parseDOM: (elem) => elem.getAttribute("data-sync-id"),
54672
+ renderDOM: (attrs) => {
54673
+ if (!attrs.syncId) return {};
54674
+ return {
54675
+ "data-sync-id": attrs.syncId
54676
+ };
54677
+ }
54678
+ // rendered: false,
54679
+ },
54680
+ listId: {
54681
+ keepOnSplit: true,
54682
+ parseDOM: (elem) => elem.getAttribute("data-list-id"),
54683
+ renderDOM: (attrs) => {
54684
+ if (!attrs.listId) return {};
54685
+ return {
54686
+ "data-list-id": attrs.listId
54687
+ };
54688
+ }
54689
+ },
54690
+ "list-style-type": {
54691
+ default: "decimal",
54692
+ rendered: false
54693
+ },
54694
+ attributes: {
54695
+ rendered: false,
54696
+ keepOnSplit: true
54697
+ }
54698
+ };
54699
+ },
54700
+ parseDOM() {
54701
+ return [{ tag: "ol" }];
54702
+ },
54703
+ renderDOM({ htmlAttributes }) {
54704
+ const { start: start2, ...restAttributes } = htmlAttributes;
54705
+ return start2 === 1 ? ["ol", Attribute2.mergeAttributes(this.options.htmlAttributes, restAttributes), 0] : ["ol", Attribute2.mergeAttributes(this.options.htmlAttributes, htmlAttributes), 0];
54706
+ },
54707
+ addCommands() {
54708
+ return {
54709
+ /**
54710
+ * Toggle ordered list formatting
54711
+ * @category Command
54712
+ * @example
54713
+ * editor.commands.toggleOrderedList()
54714
+ * @note Converts selection to ordered list or back to paragraphs
54715
+ */
54716
+ toggleOrderedList: () => (params2) => {
54717
+ return toggleList(this.type)(params2);
54718
+ },
54719
+ /**
54720
+ * Restart list node numbering
54721
+ * @category Command
54722
+ * @param {Array} followingNodes - Nodes to restart
54723
+ * @param {number} pos - Starting position
54724
+ * @example
54725
+ * editor.commands.restartListNodes(nodes, position)
54726
+ * @note Resets list numbering for specified nodes
54727
+ */
54728
+ restartListNodes: (followingNodes, pos) => ({ tr }) => {
54729
+ let currentNodePos = pos;
54730
+ const nodes = followingNodes.map((node) => {
54731
+ const resultNode = {
54732
+ node,
54733
+ pos: currentNodePos
54734
+ };
54735
+ currentNodePos += node.nodeSize;
54736
+ return resultNode;
54737
+ });
54738
+ nodes.forEach((item) => {
54739
+ const { pos: pos2 } = item;
54740
+ const newPos = tr.mapping.map(pos2);
54741
+ tr.setNodeMarkup(newPos, void 0, {});
54742
+ });
54743
+ return true;
54744
+ },
54745
+ /**
54746
+ * Update ordered list style type based on nesting level
54747
+ * @category Command
54748
+ * @example
54749
+ * editor.commands.updateOrderedListStyleType()
54750
+ * @note Cycles through decimal -> lowerAlpha -> lowerRoman based on depth
54751
+ */
54752
+ updateOrderedListStyleType: () => ({ dispatch, tr }) => {
54753
+ let list = findParentNode((node) => node.type.name === this.name)(tr.selection);
54754
+ if (!list) {
54755
+ return true;
54756
+ }
54757
+ if (dispatch) {
54758
+ let listLevel = (list.depth - 1) / 2;
54759
+ let listStyleTypes = this.options.listStyleTypes;
54760
+ let listStyle = listStyleTypes[listLevel % listStyleTypes.length];
54761
+ let currentListStyle = list.node.attrs["list-style-type"];
54762
+ let nodeAtPos = tr.doc.nodeAt(list.pos);
54763
+ if (currentListStyle !== listStyle && nodeAtPos.eq(list.node)) {
54764
+ tr.setNodeMarkup(list.pos, void 0, {
54765
+ ...list.node.attrs,
54766
+ ...{
54767
+ "list-style-type": listStyle
54768
+ }
54769
+ });
54770
+ }
54771
+ }
54772
+ return true;
54773
+ }
54774
+ };
54775
+ },
54776
+ addShortcuts() {
54777
+ return {
54778
+ "Mod-Shift-7": () => {
54779
+ return this.editor.commands.toggleOrderedList();
54780
+ }
54781
+ };
54782
+ },
54783
+ addInputRules() {
54784
+ return [
54785
+ new InputRule({
54786
+ match: inputRegex,
54787
+ handler: ({ state: state2, range: range2 }) => {
54788
+ const $pos = state2.selection.$from;
54789
+ const listItemType = state2.schema.nodes.listItem;
54790
+ for (let depth = $pos.depth; depth >= 0; depth--) {
54791
+ if ($pos.node(depth).type === listItemType) {
54792
+ return null;
54793
+ }
54794
+ }
54795
+ const { tr } = state2;
54796
+ tr.delete(range2.from, range2.to);
54797
+ ListHelpers.createNewList({
54798
+ listType: this.type,
54799
+ tr,
54800
+ editor: this.editor
54801
+ });
54802
+ }
54803
+ })
54804
+ ];
54805
+ }
54806
+ });
54807
+ const generateOrderedListIndex = ({ listLevel, lvlText, listNumberingType, customFormat }) => {
54808
+ const handler2 = listIndexMap[listNumberingType];
54809
+ return handler2 ? handler2(listLevel, lvlText, customFormat) : null;
54810
+ };
54811
+ const handleDecimal = (path, lvlText) => generateNumbering(path, lvlText, String);
54812
+ const handleRoman = (path, lvlText) => generateNumbering(path, lvlText, intToRoman);
54813
+ const handleLowerRoman = (path, lvlText) => handleRoman(path, lvlText).toLowerCase();
54814
+ const handleLowerAlpha = (path, lvlText) => handleAlpha(path, lvlText).toLowerCase();
54815
+ const handleAlpha = (path, lvlText) => generateNumbering(path, lvlText, (p) => intToAlpha(p));
54816
+ const handleOrdinal = (path, lvlText) => generateNumbering(path, lvlText, ordinalFormatter);
54817
+ const handleCustom = (path, lvlText, customFormat) => generateFromCustom(path, lvlText, customFormat);
54818
+ const handleJapaneseCounting = (path, lvlText) => generateNumbering(path, lvlText, intToJapaneseCounting);
54819
+ const listIndexMap = {
54820
+ decimal: handleDecimal,
54821
+ lowerRoman: handleLowerRoman,
54822
+ upperRoman: handleRoman,
54823
+ lowerLetter: handleLowerAlpha,
54824
+ upperLetter: handleAlpha,
54825
+ ordinal: handleOrdinal,
54826
+ custom: handleCustom,
54827
+ japaneseCounting: handleJapaneseCounting
54828
+ };
54829
+ const createNumbering = (values, lvlText) => {
54830
+ return values.reduce((acc, value, index2) => {
54831
+ return value > 9 ? acc.replace(/^0/, "").replace(`%${index2 + 1}`, value) : acc.replace(`%${index2 + 1}`, value);
54832
+ }, lvlText);
54833
+ };
54834
+ const generateNumbering = (path, lvlText, formatter) => {
54835
+ const formattedValues = path.map(formatter);
54836
+ return createNumbering(formattedValues, lvlText);
54837
+ };
54838
+ const ordinalFormatter = (level) => {
54839
+ const suffixes = ["th", "st", "nd", "rd"];
54840
+ const value = level % 100;
54841
+ const suffix2 = suffixes[(value - 20) % 10] || suffixes[value] || suffixes[0];
54842
+ const p = level + suffix2;
54843
+ return p;
54844
+ };
54845
+ const generateFromCustom = (path, lvlText, customFormat) => {
54846
+ if (customFormat !== "001, 002, 003, ...") return generateNumbering(path, lvlText, String);
54847
+ const match = customFormat.match(/(\d+)/);
54848
+ if (!match) throw new Error("Invalid format string: no numeric pattern found");
54849
+ const sample = match[1];
54850
+ const digitCount = sample.length;
54851
+ const index2 = path.pop();
54852
+ return String(index2).padStart(digitCount, "0");
53602
54853
  };
54854
+ const intToRoman = (num) => {
54855
+ const romanNumeralMap = [
54856
+ { value: 1e3, numeral: "M" },
54857
+ { value: 900, numeral: "CM" },
54858
+ { value: 500, numeral: "D" },
54859
+ { value: 400, numeral: "CD" },
54860
+ { value: 100, numeral: "C" },
54861
+ { value: 90, numeral: "XC" },
54862
+ { value: 50, numeral: "L" },
54863
+ { value: 40, numeral: "XL" },
54864
+ { value: 10, numeral: "X" },
54865
+ { value: 9, numeral: "IX" },
54866
+ { value: 5, numeral: "V" },
54867
+ { value: 4, numeral: "IV" },
54868
+ { value: 1, numeral: "I" }
54869
+ ];
54870
+ let result = "";
54871
+ for (const { value, numeral } of romanNumeralMap) {
54872
+ while (num >= value) {
54873
+ result += numeral;
54874
+ num -= value;
54875
+ }
54876
+ }
54877
+ return result;
54878
+ };
54879
+ const intToAlpha = (num) => {
54880
+ let result = "";
54881
+ const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
54882
+ while (num > 0) {
54883
+ let index2 = (num - 1) % 26;
54884
+ result = alphabet[index2] + result;
54885
+ num = Math.floor((num - 1) / 26);
54886
+ }
54887
+ return result;
54888
+ };
54889
+ const intToJapaneseCounting = (num) => {
54890
+ const digits = ["", "一", "二", "三", "四", "五", "六", "七", "八", "九"];
54891
+ const units = ["", "十", "百", "千"];
54892
+ if (num === 0) return "零";
54893
+ if (num < 10) return digits[num];
54894
+ let result = "";
54895
+ let tempNum = num;
54896
+ let unitIndex = 0;
54897
+ while (tempNum > 0) {
54898
+ const digit = tempNum % 10;
54899
+ if (digit !== 0) {
54900
+ const digitStr = digit === 1 && unitIndex > 0 ? "" : digits[digit];
54901
+ result = digitStr + (unitIndex > 0 ? units[unitIndex] : "") + result;
54902
+ } else if (result && tempNum > 0) {
54903
+ if (!result.startsWith("零") && tempNum % 100 !== 0) {
54904
+ result = "零" + result;
54905
+ }
54906
+ }
54907
+ tempNum = Math.floor(tempNum / 10);
54908
+ unitIndex++;
54909
+ if (unitIndex > 3) break;
54910
+ }
54911
+ if (num >= 10 && num < 20) {
54912
+ result = result.replace(/^一十/, "十");
54913
+ }
54914
+ return result;
54915
+ };
54916
+ const isKeyboardInvocation = (event) => {
54917
+ return event.type === "contextmenu" && typeof event.detail === "number" && event.detail === 0 && (event.button === 0 || event.button === void 0) && event.clientX === 0 && event.clientY === 0;
54918
+ };
54919
+ const prefersNativeMenu = (event) => {
54920
+ if (!event) return false;
54921
+ if (event.ctrlKey || event.metaKey) {
54922
+ return true;
54923
+ }
54924
+ return isKeyboardInvocation(event);
54925
+ };
54926
+ const shouldAllowNativeContextMenu = (event) => {
54927
+ return prefersNativeMenu(event);
54928
+ };
54929
+ const shouldBypassContextMenu = shouldAllowNativeContextMenu;
54930
+ const DEFAULT_SELECTION_STATE = Object.freeze({
54931
+ focused: false,
54932
+ preservedSelection: null,
54933
+ showVisualSelection: false,
54934
+ skipFocusReset: false
54935
+ });
54936
+ const normalizeSelectionState = (state2 = {}) => ({
54937
+ ...DEFAULT_SELECTION_STATE,
54938
+ ...state2
54939
+ });
54940
+ const CustomSelectionPluginKey = new PluginKey("CustomSelection");
53603
54941
  const handleClickOutside = (event, editor) => {
53604
54942
  const editorElem = editor?.options?.element;
53605
54943
  if (!editorElem) return;
@@ -53636,11 +54974,7 @@ const CustomSelection = Extension.create({
53636
54974
  const customSelectionPlugin = new Plugin({
53637
54975
  key: CustomSelectionPluginKey,
53638
54976
  state: {
53639
- init: () => ({
53640
- focused: false,
53641
- preservedSelection: null,
53642
- showVisualSelection: false
53643
- }),
54977
+ init: () => ({ ...DEFAULT_SELECTION_STATE }),
53644
54978
  apply: (tr, value) => {
53645
54979
  const meta = getFocusMeta(tr);
53646
54980
  if (meta !== void 0) {
@@ -53671,7 +55005,8 @@ const CustomSelection = Extension.create({
53671
55005
  setFocusMeta(view.state.tr, {
53672
55006
  focused: true,
53673
55007
  preservedSelection: selection,
53674
- showVisualSelection: true
55008
+ showVisualSelection: true,
55009
+ skipFocusReset: true
53675
55010
  })
53676
55011
  );
53677
55012
  }
@@ -53692,7 +55027,8 @@ const CustomSelection = Extension.create({
53692
55027
  setFocusMeta(view.state.tr, {
53693
55028
  focused: true,
53694
55029
  preservedSelection: selection2,
53695
- showVisualSelection: true
55030
+ showVisualSelection: true,
55031
+ skipFocusReset: true
53696
55032
  })
53697
55033
  );
53698
55034
  this.editor.setOptions({
@@ -53715,7 +55051,8 @@ const CustomSelection = Extension.create({
53715
55051
  setFocusMeta(view.state.tr, {
53716
55052
  focused: true,
53717
55053
  preservedSelection: selection,
53718
- showVisualSelection: true
55054
+ showVisualSelection: true,
55055
+ skipFocusReset: false
53719
55056
  })
53720
55057
  );
53721
55058
  this.editor.setOptions({
@@ -53733,7 +55070,8 @@ const CustomSelection = Extension.create({
53733
55070
  setFocusMeta(view.state.tr, {
53734
55071
  focused: true,
53735
55072
  preservedSelection: selection,
53736
- showVisualSelection: true
55073
+ showVisualSelection: true,
55074
+ skipFocusReset: false
53737
55075
  })
53738
55076
  );
53739
55077
  }
@@ -53744,7 +55082,8 @@ const CustomSelection = Extension.create({
53744
55082
  setFocusMeta(view.state.tr, {
53745
55083
  focused: false,
53746
55084
  preservedSelection: null,
53747
- showVisualSelection: false
55085
+ showVisualSelection: false,
55086
+ skipFocusReset: false
53748
55087
  })
53749
55088
  );
53750
55089
  if (!selection.empty && !this.editor.options.element?.contains(target)) {
@@ -53761,12 +55100,20 @@ const CustomSelection = Extension.create({
53761
55100
  const isElement2 = target instanceof Element;
53762
55101
  const isToolbarBtn = isElement2 && isToolbarButton(target);
53763
55102
  const isToolbarInp = isElement2 && isToolbarInput(target);
55103
+ const focusState = getFocusState(view.state);
55104
+ if (focusState?.skipFocusReset) {
55105
+ view.dispatch(
55106
+ setFocusMeta(view.state.tr, normalizeSelectionState({ ...focusState, skipFocusReset: false }))
55107
+ );
55108
+ return false;
55109
+ }
53764
55110
  if (!isToolbarBtn && !isToolbarInp) {
53765
55111
  view.dispatch(
53766
55112
  setFocusMeta(view.state.tr, {
53767
55113
  focused: false,
53768
55114
  preservedSelection: null,
53769
- showVisualSelection: false
55115
+ showVisualSelection: false,
55116
+ skipFocusReset: false
53770
55117
  })
53771
55118
  );
53772
55119
  }
@@ -53777,12 +55124,16 @@ const CustomSelection = Extension.create({
53777
55124
  const isToolbarBtn = isElement2 && isToolbarButton(target);
53778
55125
  const isToolbarInp = isElement2 && isToolbarInput(target);
53779
55126
  const state2 = getFocusState(view.state);
55127
+ if (state2?.skipFocusReset) {
55128
+ return false;
55129
+ }
53780
55130
  if (isToolbarBtn || isToolbarInp) {
53781
55131
  view.dispatch(
53782
55132
  setFocusMeta(view.state.tr, {
53783
55133
  focused: true,
53784
55134
  preservedSelection: state2.preservedSelection || view.state.selection,
53785
- showVisualSelection: true
55135
+ showVisualSelection: true,
55136
+ skipFocusReset: false
53786
55137
  })
53787
55138
  );
53788
55139
  } else {
@@ -53790,7 +55141,8 @@ const CustomSelection = Extension.create({
53790
55141
  setFocusMeta(view.state.tr, {
53791
55142
  focused: false,
53792
55143
  preservedSelection: null,
53793
- showVisualSelection: false
55144
+ showVisualSelection: false,
55145
+ skipFocusReset: false
53794
55146
  })
53795
55147
  );
53796
55148
  }
@@ -54636,7 +55988,7 @@ class ListItemNodeView {
54636
55988
  this.decorations = decorations;
54637
55989
  this.view = editor.view;
54638
55990
  this.getPos = getPos;
54639
- __privateMethod$1(this, _ListItemNodeView_instances, init_fn2).call(this);
55991
+ __privateMethod$1(this, _ListItemNodeView_instances, init_fn3).call(this);
54640
55992
  activeListItemNodeViews.add(this);
54641
55993
  }
54642
55994
  refreshIndentStyling() {
@@ -54697,7 +56049,7 @@ class ListItemNodeView {
54697
56049
  }
54698
56050
  }
54699
56051
  _ListItemNodeView_instances = /* @__PURE__ */ new WeakSet();
54700
- init_fn2 = function() {
56052
+ init_fn3 = function() {
54701
56053
  const { attrs } = this.node;
54702
56054
  const { listLevel, listNumberingType, lvlText, numId, level, customFormat } = attrs;
54703
56055
  let orderMarker = "";
@@ -61813,977 +63165,328 @@ const PageNumber = Node$1.create({
61813
63165
  rendered: false
61814
63166
  }
61815
63167
  };
61816
- },
61817
- addNodeView() {
61818
- return ({ node, editor, getPos, decorations }) => {
61819
- const htmlAttributes = this.options.htmlAttributes;
61820
- return new AutoPageNumberNodeView(node, getPos, decorations, editor, htmlAttributes);
61821
- };
61822
- },
61823
- parseDOM() {
61824
- return [{ tag: 'span[data-id="auto-page-number"' }];
61825
- },
61826
- renderDOM({ htmlAttributes }) {
61827
- return ["span", Attribute2.mergeAttributes(this.options.htmlAttributes, htmlAttributes)];
61828
- },
61829
- addCommands() {
61830
- return {
61831
- /**
61832
- * Insert an automatic page number
61833
- * @category Command
61834
- * @returns {Function} Command function
61835
- * @example
61836
- * editor.commands.addAutoPageNumber()
61837
- * @note Only works in header/footer contexts
61838
- */
61839
- addAutoPageNumber: () => ({ tr, dispatch, state: state2, editor }) => {
61840
- const { options } = editor;
61841
- if (!options.isHeaderOrFooter) return false;
61842
- const { schema } = state2;
61843
- const pageNumberType = schema?.nodes?.["page-number"];
61844
- if (!pageNumberType) return false;
61845
- const pageNumberNodeJSON = { type: "page-number" };
61846
- const pageNumberNode = schema.nodeFromJSON(pageNumberNodeJSON);
61847
- if (dispatch) {
61848
- tr.replaceSelectionWith(pageNumberNode, false);
61849
- tr.setMeta("forceUpdatePagination", true);
61850
- }
61851
- return true;
61852
- }
61853
- };
61854
- },
61855
- addShortcuts() {
61856
- return {
61857
- "Mod-Shift-alt-p": () => this.editor.commands.addAutoPageNumber()
61858
- };
61859
- }
61860
- });
61861
- const TotalPageCount = Node$1.create({
61862
- name: "total-page-number",
61863
- group: "inline",
61864
- inline: true,
61865
- atom: true,
61866
- draggable: false,
61867
- selectable: false,
61868
- content: "text*",
61869
- addOptions() {
61870
- return {
61871
- htmlAttributes: {
61872
- contenteditable: false,
61873
- "data-id": "auto-total-pages",
61874
- "aria-label": "Total page count node",
61875
- class: "sd-editor-auto-total-pages"
61876
- }
61877
- };
61878
- },
61879
- addAttributes() {
61880
- return {
61881
- marksAsAttrs: {
61882
- default: null,
61883
- rendered: false
61884
- }
61885
- };
61886
- },
61887
- addNodeView() {
61888
- return ({ node, editor, getPos, decorations }) => {
61889
- const htmlAttributes = this.options.htmlAttributes;
61890
- return new AutoPageNumberNodeView(node, getPos, decorations, editor, htmlAttributes);
61891
- };
61892
- },
61893
- parseDOM() {
61894
- return [{ tag: 'span[data-id="auto-total-pages"' }];
61895
- },
61896
- renderDOM({ htmlAttributes }) {
61897
- return ["span", Attribute2.mergeAttributes(this.options.htmlAttributes, htmlAttributes), 0];
61898
- },
61899
- addCommands() {
61900
- return {
61901
- /**
61902
- * Insert total page count
61903
- * @category Command
61904
- * @returns {Function} Command function
61905
- * @example
61906
- * editor.commands.addTotalPageCount()
61907
- * @note Only works in header/footer contexts
61908
- */
61909
- addTotalPageCount: () => ({ tr, dispatch, state: state2, editor }) => {
61910
- const { options } = editor;
61911
- if (!options.isHeaderOrFooter) return false;
61912
- const { schema } = state2;
61913
- const pageNumberType = schema.nodes?.["total-page-number"];
61914
- if (!pageNumberType) return false;
61915
- const currentPages = editor?.options?.parentEditor?.currentTotalPages || 1;
61916
- const pageNumberNode = {
61917
- type: "total-page-number",
61918
- content: [{ type: "text", text: String(currentPages) }]
61919
- };
61920
- const pageNode = schema.nodeFromJSON(pageNumberNode);
61921
- if (dispatch) {
61922
- tr.replaceSelectionWith(pageNode, false);
61923
- }
61924
- return true;
61925
- }
61926
- };
61927
- },
61928
- addShortcuts() {
61929
- return {
61930
- "Mod-Shift-alt-c": () => this.editor.commands.addTotalPageCount()
61931
- };
61932
- }
61933
- });
61934
- const getNodeAttributes = (nodeName, editor) => {
61935
- switch (nodeName) {
61936
- case "page-number":
61937
- return {
61938
- text: editor.options.currentPageNumber || "1",
61939
- className: "sd-editor-auto-page-number",
61940
- dataId: "auto-page-number",
61941
- ariaLabel: "Page number node"
61942
- };
61943
- case "total-page-number":
61944
- return {
61945
- text: editor.options.parentEditor?.currentTotalPages || "1",
61946
- className: "sd-editor-auto-total-pages",
61947
- dataId: "auto-total-pages",
61948
- ariaLabel: "Total page count node"
61949
- };
61950
- default:
61951
- return {};
61952
- }
61953
- };
61954
- class AutoPageNumberNodeView {
61955
- constructor(node, getPos, decorations, editor, htmlAttributes = {}) {
61956
- __privateAdd$1(this, _AutoPageNumberNodeView_instances);
61957
- this.node = node;
61958
- this.editor = editor;
61959
- this.view = editor.view;
61960
- this.getPos = getPos;
61961
- this.editor = editor;
61962
- this.dom = __privateMethod$1(this, _AutoPageNumberNodeView_instances, renderDom_fn).call(this, node, htmlAttributes);
61963
- }
61964
- update(node) {
61965
- const incomingType = node?.type?.name;
61966
- const currentType = this.node?.type?.name;
61967
- if (!incomingType || incomingType !== currentType) return false;
61968
- this.node = node;
61969
- return true;
61970
- }
61971
- }
61972
- _AutoPageNumberNodeView_instances = /* @__PURE__ */ new WeakSet();
61973
- renderDom_fn = function(node, htmlAttributes) {
61974
- const attrs = getNodeAttributes(this.node.type.name, this.editor);
61975
- const content = document.createTextNode(String(attrs.text));
61976
- const nodeContent = document.createElement("span");
61977
- nodeContent.className = attrs.className;
61978
- nodeContent.setAttribute("data-id", attrs.dataId);
61979
- nodeContent.setAttribute("aria-label", attrs.ariaLabel);
61980
- const currentPos = this.getPos();
61981
- const { styles, marks } = getMarksFromNeighbors(currentPos, this.view);
61982
- __privateMethod$1(this, _AutoPageNumberNodeView_instances, scheduleUpdateNodeStyle_fn).call(this, currentPos, marks);
61983
- Object.assign(nodeContent.style, styles);
61984
- nodeContent.appendChild(content);
61985
- Object.entries(htmlAttributes).forEach(([key2, value]) => {
61986
- if (value) nodeContent.setAttribute(key2, value);
61987
- });
61988
- return nodeContent;
61989
- };
61990
- scheduleUpdateNodeStyle_fn = function(pos, marks) {
61991
- setTimeout(() => {
61992
- const { state: state2 } = this.editor;
61993
- const { dispatch } = this.view;
61994
- const node = state2.doc.nodeAt(pos);
61995
- if (!node || node.isText) return;
61996
- const currentMarks = node.attrs.marksAsAttrs || [];
61997
- const newMarks = marks.map((m2) => ({ type: m2.type.name, attrs: m2.attrs }));
61998
- const isEqual = JSON.stringify(currentMarks) === JSON.stringify(newMarks);
61999
- if (isEqual) return;
62000
- const newAttrs = {
62001
- ...node.attrs,
62002
- marksAsAttrs: newMarks
62003
- };
62004
- const tr = state2.tr.setNodeMarkup(pos, void 0, newAttrs);
62005
- dispatch(tr);
62006
- }, 0);
62007
- };
62008
- const getMarksFromNeighbors = (currentPos, view) => {
62009
- const $pos = view.state.doc.resolve(currentPos);
62010
- const styles = {};
62011
- const marks = [];
62012
- const before = $pos.nodeBefore;
62013
- if (before) {
62014
- Object.assign(styles, processMarks(before.marks));
62015
- marks.push(...before.marks);
62016
- }
62017
- const after = $pos.nodeAfter;
62018
- if (after) {
62019
- Object.assign(styles, { ...styles, ...processMarks(after.marks) });
62020
- marks.push(...after.marks);
62021
- }
62022
- return {
62023
- styles,
62024
- marks
62025
- };
62026
- };
62027
- const processMarks = (marks) => {
62028
- const styles = {};
62029
- marks.forEach((mark) => {
62030
- const { type: type2, attrs } = mark;
62031
- switch (type2.name) {
62032
- case "textStyle":
62033
- if (attrs.fontFamily) styles["font-family"] = attrs.fontFamily;
62034
- if (attrs.fontSize) styles["font-size"] = attrs.fontSize;
62035
- if (attrs.color) styles["color"] = attrs.color;
62036
- if (attrs.backgroundColor) styles["background-color"] = attrs.backgroundColor;
62037
- break;
62038
- case "bold":
62039
- styles["font-weight"] = "bold";
62040
- break;
62041
- case "italic":
62042
- styles["font-style"] = "italic";
62043
- break;
62044
- case "underline":
62045
- styles["text-decoration"] = (styles["text-decoration"] || "") + " underline";
62046
- break;
62047
- case "strike":
62048
- styles["text-decoration"] = (styles["text-decoration"] || "") + " line-through";
62049
- break;
62050
- default:
62051
- if (attrs?.style) {
62052
- Object.entries(attrs.style).forEach(([key2, value]) => {
62053
- styles[key2] = value;
62054
- });
62055
- }
62056
- break;
62057
- }
62058
- });
62059
- return styles;
62060
- };
62061
- const ShapeContainer = Node$1.create({
62062
- name: "shapeContainer",
62063
- group: "block",
62064
- content: "block+",
62065
- isolating: true,
62066
- addOptions() {
62067
- return {
62068
- htmlAttributes: {
62069
- class: "sd-editor-shape-container",
62070
- "aria-label": "Shape container node"
62071
- }
62072
- };
62073
- },
62074
- addAttributes() {
62075
- return {
62076
- fillcolor: {
62077
- renderDOM: (attrs) => {
62078
- if (!attrs.fillcolor) return {};
62079
- return {
62080
- style: `background-color: ${attrs.fillcolor}`
62081
- };
62082
- }
62083
- },
62084
- sdBlockId: {
62085
- default: null,
62086
- keepOnSplit: false,
62087
- parseDOM: (elem) => elem.getAttribute("data-sd-block-id"),
62088
- renderDOM: (attrs) => {
62089
- return attrs.sdBlockId ? { "data-sd-block-id": attrs.sdBlockId } : {};
62090
- }
62091
- },
62092
- style: {
62093
- renderDOM: (attrs) => {
62094
- if (!attrs.style) return {};
62095
- return {
62096
- style: attrs.style
62097
- };
62098
- }
62099
- },
62100
- wrapAttributes: {
62101
- rendered: false
62102
- },
62103
- attributes: {
62104
- rendered: false
62105
- }
62106
- };
62107
- },
62108
- parseDOM() {
62109
- return [
62110
- {
62111
- tag: `div[data-type="${this.name}"]`
62112
- }
62113
- ];
62114
- },
62115
- renderDOM({ htmlAttributes }) {
62116
- return [
62117
- "div",
62118
- Attribute2.mergeAttributes(this.options.htmlAttributes, htmlAttributes, { "data-type": this.name }),
62119
- 0
62120
- ];
62121
- }
62122
- });
62123
- const ShapeTextbox = Node$1.create({
62124
- name: "shapeTextbox",
62125
- group: "block",
62126
- content: "paragraph* block*",
62127
- isolating: true,
62128
- addOptions() {
62129
- return {
62130
- htmlAttributes: {
62131
- class: "sd-editor-shape-textbox",
62132
- "aria-label": "Shape textbox node"
62133
- }
62134
- };
62135
- },
62136
- addAttributes() {
62137
- return {
62138
- sdBlockId: {
62139
- default: null,
62140
- keepOnSplit: false,
62141
- parseDOM: (elem) => elem.getAttribute("data-sd-block-id"),
62142
- renderDOM: (attrs) => {
62143
- return attrs.sdBlockId ? { "data-sd-block-id": attrs.sdBlockId } : {};
62144
- }
62145
- },
62146
- attributes: {
62147
- rendered: false
62148
- }
62149
- };
62150
- },
62151
- parseDOM() {
62152
- return [
62153
- {
62154
- tag: `div[data-type="${this.name}"]`
62155
- }
62156
- ];
62157
- },
62158
- renderDOM({ htmlAttributes }) {
62159
- return [
62160
- "div",
62161
- Attribute2.mergeAttributes(this.options.htmlAttributes, htmlAttributes, { "data-type": this.name }),
62162
- 0
62163
- ];
62164
- }
62165
- });
62166
- const ContentBlock = Node$1.create({
62167
- name: "contentBlock",
62168
- group: "inline",
62169
- content: "",
62170
- isolating: true,
62171
- atom: true,
62172
- inline: true,
62173
- addOptions() {
62174
- return {
62175
- htmlAttributes: {
62176
- contenteditable: false
62177
- }
62178
- };
62179
- },
62180
- addAttributes() {
62181
- return {
62182
- horizontalRule: {
62183
- default: false,
62184
- renderDOM: ({ horizontalRule }) => {
62185
- if (!horizontalRule) return {};
62186
- return { "data-horizontal-rule": "true" };
62187
- }
62188
- },
62189
- size: {
62190
- default: null,
62191
- renderDOM: ({ size: size2 }) => {
62192
- if (!size2) return {};
62193
- let style2 = "";
62194
- if (size2.top) style2 += `top: ${size2.top}px; `;
62195
- if (size2.left) style2 += `left: ${size2.left}px; `;
62196
- if (size2.width) style2 += `width: ${size2.width.toString().endsWith("%") ? size2.width : `${size2.width}px`}; `;
62197
- if (size2.height)
62198
- style2 += `height: ${size2.height.toString().endsWith("%") ? size2.height : `${size2.height}px`}; `;
62199
- return { style: style2 };
62200
- }
62201
- },
62202
- background: {
62203
- default: null,
62204
- renderDOM: (attrs) => {
62205
- if (!attrs.background) return {};
62206
- return {
62207
- style: `background-color: ${attrs.background}`
62208
- };
62209
- }
62210
- },
62211
- drawingContent: {
62212
- rendered: false
62213
- },
62214
- attributes: {
62215
- rendered: false
62216
- }
63168
+ },
63169
+ addNodeView() {
63170
+ return ({ node, editor, getPos, decorations }) => {
63171
+ const htmlAttributes = this.options.htmlAttributes;
63172
+ return new AutoPageNumberNodeView(node, getPos, decorations, editor, htmlAttributes);
62217
63173
  };
62218
63174
  },
62219
63175
  parseDOM() {
62220
- return [
62221
- {
62222
- tag: `div[data-type="${this.name}"]`
62223
- }
62224
- ];
63176
+ return [{ tag: 'span[data-id="auto-page-number"' }];
62225
63177
  },
62226
63178
  renderDOM({ htmlAttributes }) {
62227
- return ["div", Attribute2.mergeAttributes(this.options.htmlAttributes, htmlAttributes, { "data-type": this.name })];
63179
+ return ["span", Attribute2.mergeAttributes(this.options.htmlAttributes, htmlAttributes)];
62228
63180
  },
62229
63181
  addCommands() {
62230
63182
  return {
62231
63183
  /**
62232
- * Insert a horizontal rule
62233
- * @category Command
62234
- * @example
62235
- * editor.commands.insertHorizontalRule()
62236
- * @note Creates a visual separator between content sections
62237
- */
62238
- insertHorizontalRule: () => ({ commands: commands2 }) => {
62239
- return commands2.insertContent({
62240
- type: this.name,
62241
- attrs: {
62242
- horizontalRule: true,
62243
- size: { width: "100%", height: 2 },
62244
- background: "#e5e7eb"
62245
- }
62246
- });
62247
- },
62248
- /**
62249
- * Insert a content block
63184
+ * Insert an automatic page number
62250
63185
  * @category Command
62251
- * @param {ContentBlockConfig} config - Block configuration
62252
- * @example
62253
- * // Insert a spacer block
62254
- * editor.commands.insertContentBlock({ size: { height: 20 } })
62255
- *
63186
+ * @returns {Function} Command function
62256
63187
  * @example
62257
- * // Insert a colored divider
62258
- * editor.commands.insertContentBlock({
62259
- * size: { width: '50%', height: 3 },
62260
- * background: '#3b82f6'
62261
- * })
62262
- * @note Used for spacing, dividers, and special inline content
63188
+ * editor.commands.addAutoPageNumber()
63189
+ * @note Only works in header/footer contexts
62263
63190
  */
62264
- insertContentBlock: (config2) => ({ commands: commands2 }) => {
62265
- return commands2.insertContent({
62266
- type: this.name,
62267
- attrs: config2
62268
- });
63191
+ addAutoPageNumber: () => ({ tr, dispatch, state: state2, editor }) => {
63192
+ const { options } = editor;
63193
+ if (!options.isHeaderOrFooter) return false;
63194
+ const { schema } = state2;
63195
+ const pageNumberType = schema?.nodes?.["page-number"];
63196
+ if (!pageNumberType) return false;
63197
+ const pageNumberNodeJSON = { type: "page-number" };
63198
+ const pageNumberNode = schema.nodeFromJSON(pageNumberNodeJSON);
63199
+ if (dispatch) {
63200
+ tr.replaceSelectionWith(pageNumberNode, false);
63201
+ tr.setMeta("forceUpdatePagination", true);
63202
+ }
63203
+ return true;
62269
63204
  }
62270
63205
  };
63206
+ },
63207
+ addShortcuts() {
63208
+ return {
63209
+ "Mod-Shift-alt-p": () => this.editor.commands.addAutoPageNumber()
63210
+ };
62271
63211
  }
62272
63212
  });
62273
- class StructuredContentViewBase {
62274
- constructor(props) {
62275
- __publicField$1(this, "node");
62276
- __publicField$1(this, "view");
62277
- __publicField$1(this, "getPos");
62278
- __publicField$1(this, "decorations");
62279
- __publicField$1(this, "innerDecorations");
62280
- __publicField$1(this, "editor");
62281
- __publicField$1(this, "extension");
62282
- __publicField$1(this, "htmlAttributes");
62283
- __publicField$1(this, "root");
62284
- __publicField$1(this, "isDragging", false);
62285
- this.node = props.node;
62286
- this.view = props.editor.view;
62287
- this.getPos = props.getPos;
62288
- this.decorations = props.decorations;
62289
- this.innerDecorations = props.innerDecorations;
62290
- this.editor = props.editor;
62291
- this.extension = props.extension;
62292
- this.htmlAttributes = props.htmlAttributes;
62293
- this.mount(props);
62294
- }
62295
- mount() {
62296
- return;
62297
- }
62298
- get dom() {
62299
- return this.root;
62300
- }
62301
- get contentDOM() {
62302
- return null;
62303
- }
62304
- update(node, decorations, innerDecorations) {
62305
- if (node.type !== this.node.type) {
62306
- return false;
62307
- }
62308
- this.node = node;
62309
- this.decorations = decorations;
62310
- this.innerDecorations = innerDecorations;
62311
- this.updateHTMLAttributes();
62312
- return true;
62313
- }
62314
- stopEvent(event) {
62315
- if (!this.dom) return false;
62316
- const target = event.target;
62317
- const isInElement = this.dom.contains(target) && !this.contentDOM?.contains(target);
62318
- if (!isInElement) return false;
62319
- const isDragEvent = event.type.startsWith("drag");
62320
- const isDropEvent = event.type === "drop";
62321
- const isInput = ["INPUT", "BUTTON", "SELECT", "TEXTAREA"].includes(target.tagName) || target.isContentEditable;
62322
- if (isInput && !isDropEvent && !isDragEvent) return true;
62323
- const { isEditable } = this.editor;
62324
- const { isDragging } = this;
62325
- const isDraggable = !!this.node.type.spec.draggable;
62326
- const isSelectable = NodeSelection.isSelectable(this.node);
62327
- const isCopyEvent = event.type === "copy";
62328
- const isPasteEvent = event.type === "paste";
62329
- const isCutEvent = event.type === "cut";
62330
- const isClickEvent = event.type === "mousedown";
62331
- if (!isDraggable && isSelectable && isDragEvent && event.target === this.dom) {
62332
- event.preventDefault();
62333
- }
62334
- if (isDraggable && isDragEvent && !isDragging && event.target === this.dom) {
62335
- event.preventDefault();
62336
- return false;
62337
- }
62338
- if (isDraggable && isEditable && !isDragging && isClickEvent) {
62339
- const dragHandle = target.closest("[data-drag-handle]");
62340
- const isValidDragHandle = dragHandle && (this.dom === dragHandle || this.dom.contains(dragHandle));
62341
- if (isValidDragHandle) {
62342
- this.isDragging = true;
62343
- document.addEventListener(
62344
- "dragend",
62345
- () => {
62346
- this.isDragging = false;
62347
- },
62348
- { once: true }
62349
- );
62350
- document.addEventListener(
62351
- "drop",
62352
- () => {
62353
- this.isDragging = false;
62354
- },
62355
- { once: true }
62356
- );
62357
- document.addEventListener(
62358
- "mouseup",
62359
- () => {
62360
- this.isDragging = false;
62361
- },
62362
- { once: true }
62363
- );
62364
- }
62365
- }
62366
- if (isDragging || isDropEvent || isCopyEvent || isPasteEvent || isCutEvent || isClickEvent && isSelectable) {
62367
- return false;
62368
- }
62369
- return true;
62370
- }
62371
- ignoreMutation(mutation) {
62372
- if (!this.dom || !this.contentDOM) return true;
62373
- if (this.node.isLeaf || this.node.isAtom) return true;
62374
- if (mutation.type === "selection") return false;
62375
- if (this.contentDOM === mutation.target && mutation.type === "attributes") return true;
62376
- if (this.contentDOM.contains(mutation.target)) return false;
62377
- return true;
62378
- }
62379
- destroy() {
62380
- this.dom.remove();
62381
- this.contentDOM?.remove();
62382
- }
62383
- updateAttributes(attrs) {
62384
- const pos = this.getPos();
62385
- if (typeof pos !== "number") {
62386
- return;
62387
- }
62388
- return this.view.dispatch(
62389
- this.view.state.tr.setNodeMarkup(pos, void 0, {
62390
- ...this.node.attrs,
62391
- ...attrs
62392
- })
62393
- );
62394
- }
62395
- updateHTMLAttributes() {
62396
- const { extensionService } = this.editor;
62397
- const { attributes } = extensionService;
62398
- const extensionAttrs = attributes.filter((i) => i.type === this.node.type.name);
62399
- this.htmlAttributes = Attribute2.getAttributesToRender(this.node, extensionAttrs);
62400
- }
62401
- createDragHandle() {
62402
- const dragHandle = document.createElement("span");
62403
- dragHandle.classList.add("sd-structured-content-draggable");
62404
- dragHandle.draggable = true;
62405
- dragHandle.contentEditable = "false";
62406
- dragHandle.dataset.dragHandle = "";
62407
- const textElement = document.createElement("span");
62408
- textElement.textContent = "Structured content";
62409
- dragHandle.append(textElement);
62410
- return dragHandle;
62411
- }
62412
- onDragStart(event) {
62413
- const { view } = this.editor;
62414
- const target = event.target;
62415
- const dragHandle = target.nodeType === 3 ? target.parentElement?.closest("[data-drag-handle]") : target.closest("[data-drag-handle]");
62416
- if (!this.dom || this.contentDOM?.contains(target) || !dragHandle) {
62417
- return;
62418
- }
62419
- let x = 0;
62420
- let y2 = 0;
62421
- if (this.dom !== dragHandle) {
62422
- const domBox = this.dom.getBoundingClientRect();
62423
- const handleBox = dragHandle.getBoundingClientRect();
62424
- const offsetX = event.offsetX ?? event.nativeEvent?.offsetX;
62425
- const offsetY = event.offsetY ?? event.nativeEvent?.offsetY;
62426
- x = handleBox.x - domBox.x + offsetX;
62427
- y2 = handleBox.y - domBox.y + offsetY;
62428
- }
62429
- event.dataTransfer?.setDragImage(this.dom, x, y2);
62430
- const pos = this.getPos();
62431
- if (typeof pos !== "number") {
62432
- return;
62433
- }
62434
- const selection = NodeSelection.create(view.state.doc, pos);
62435
- const transaction = view.state.tr.setSelection(selection);
62436
- view.dispatch(transaction);
62437
- }
62438
- }
62439
- class StructuredContentInlineView extends StructuredContentViewBase {
62440
- constructor(props) {
62441
- super(props);
62442
- }
62443
- mount() {
62444
- this.buildView();
62445
- }
62446
- get contentDOM() {
62447
- const contentElement = this.dom?.querySelector(`.${structuredContentInnerClass$1}`);
62448
- return contentElement || null;
62449
- }
62450
- createElement() {
62451
- const element = document.createElement("span");
62452
- element.classList.add(structuredContentClass$1);
62453
- element.setAttribute("data-structured-content", "");
62454
- const contentElement = document.createElement("span");
62455
- contentElement.classList.add(structuredContentInnerClass$1);
62456
- element.append(contentElement);
62457
- const domAttrs = Attribute2.mergeAttributes(this.htmlAttributes);
62458
- updateDOMAttributes(element, { ...domAttrs });
62459
- return { element, contentElement };
62460
- }
62461
- buildView() {
62462
- const { element } = this.createElement();
62463
- const dragHandle = this.createDragHandle();
62464
- element.prepend(dragHandle);
62465
- element.addEventListener("dragstart", (e) => this.onDragStart(e));
62466
- this.root = element;
62467
- }
62468
- updateView() {
62469
- const domAttrs = Attribute2.mergeAttributes(this.htmlAttributes);
62470
- updateDOMAttributes(this.dom, { ...domAttrs });
62471
- }
62472
- update(node, decorations, innerDecorations) {
62473
- const result = super.update(node, decorations, innerDecorations);
62474
- if (!result) return false;
62475
- this.updateView();
62476
- return true;
62477
- }
62478
- }
62479
- const structuredContentClass$1 = "sd-structured-content";
62480
- const structuredContentInnerClass$1 = "sd-structured-content__content";
62481
- const StructuredContent = Node$1.create({
62482
- name: "structuredContent",
62483
- group: "inline structuredContent",
63213
+ const TotalPageCount = Node$1.create({
63214
+ name: "total-page-number",
63215
+ group: "inline",
62484
63216
  inline: true,
62485
- content: "inline*",
62486
- isolating: true,
62487
- atom: false,
62488
- // false - has editable content.
62489
- draggable: true,
63217
+ atom: true,
63218
+ draggable: false,
63219
+ selectable: false,
63220
+ content: "text*",
62490
63221
  addOptions() {
62491
63222
  return {
62492
63223
  htmlAttributes: {
62493
- class: structuredContentClass$1,
62494
- "aria-label": "Structured content node"
63224
+ contenteditable: false,
63225
+ "data-id": "auto-total-pages",
63226
+ "aria-label": "Total page count node",
63227
+ class: "sd-editor-auto-total-pages"
62495
63228
  }
62496
63229
  };
62497
63230
  },
62498
63231
  addAttributes() {
62499
63232
  return {
62500
- id: {
63233
+ marksAsAttrs: {
62501
63234
  default: null,
62502
- parseDOM: (elem) => elem.getAttribute("data-id"),
62503
- renderDOM: (attrs) => {
62504
- if (!attrs.id) return {};
62505
- return { "data-id": attrs.id };
62506
- }
62507
- },
62508
- sdtPr: {
62509
63235
  rendered: false
62510
63236
  }
62511
63237
  };
62512
63238
  },
63239
+ addNodeView() {
63240
+ return ({ node, editor, getPos, decorations }) => {
63241
+ const htmlAttributes = this.options.htmlAttributes;
63242
+ return new AutoPageNumberNodeView(node, getPos, decorations, editor, htmlAttributes);
63243
+ };
63244
+ },
62513
63245
  parseDOM() {
62514
- return [{ tag: "span[data-structured-content]" }];
63246
+ return [{ tag: 'span[data-id="auto-total-pages"' }];
62515
63247
  },
62516
63248
  renderDOM({ htmlAttributes }) {
62517
- return [
62518
- "span",
62519
- Attribute2.mergeAttributes(this.options.htmlAttributes, htmlAttributes, {
62520
- "data-structured-content": ""
62521
- }),
62522
- 0
62523
- ];
63249
+ return ["span", Attribute2.mergeAttributes(this.options.htmlAttributes, htmlAttributes), 0];
62524
63250
  },
62525
- addNodeView() {
62526
- return (props) => {
62527
- return new StructuredContentInlineView({ ...props });
63251
+ addCommands() {
63252
+ return {
63253
+ /**
63254
+ * Insert total page count
63255
+ * @category Command
63256
+ * @returns {Function} Command function
63257
+ * @example
63258
+ * editor.commands.addTotalPageCount()
63259
+ * @note Only works in header/footer contexts
63260
+ */
63261
+ addTotalPageCount: () => ({ tr, dispatch, state: state2, editor }) => {
63262
+ const { options } = editor;
63263
+ if (!options.isHeaderOrFooter) return false;
63264
+ const { schema } = state2;
63265
+ const pageNumberType = schema.nodes?.["total-page-number"];
63266
+ if (!pageNumberType) return false;
63267
+ const currentPages = editor?.options?.parentEditor?.currentTotalPages || 1;
63268
+ const pageNumberNode = {
63269
+ type: "total-page-number",
63270
+ content: [{ type: "text", text: String(currentPages) }]
63271
+ };
63272
+ const pageNode = schema.nodeFromJSON(pageNumberNode);
63273
+ if (dispatch) {
63274
+ tr.replaceSelectionWith(pageNode, false);
63275
+ }
63276
+ return true;
63277
+ }
63278
+ };
63279
+ },
63280
+ addShortcuts() {
63281
+ return {
63282
+ "Mod-Shift-alt-c": () => this.editor.commands.addTotalPageCount()
62528
63283
  };
62529
63284
  }
62530
63285
  });
62531
- class StructuredContentBlockView extends StructuredContentViewBase {
62532
- constructor(props) {
62533
- super(props);
62534
- }
62535
- mount() {
62536
- this.buildView();
62537
- }
62538
- get contentDOM() {
62539
- const contentElement = this.dom?.querySelector(`.${structuredContentInnerClass}`);
62540
- return contentElement || null;
62541
- }
62542
- createElement() {
62543
- const element = document.createElement("div");
62544
- element.classList.add(structuredContentClass);
62545
- element.setAttribute("data-structured-content-block", "");
62546
- const contentElement = document.createElement("div");
62547
- contentElement.classList.add(structuredContentInnerClass);
62548
- element.append(contentElement);
62549
- const domAttrs = Attribute2.mergeAttributes(this.htmlAttributes);
62550
- updateDOMAttributes(element, { ...domAttrs });
62551
- return { element, contentElement };
62552
- }
62553
- buildView() {
62554
- const { element } = this.createElement();
62555
- const dragHandle = this.createDragHandle();
62556
- element.prepend(dragHandle);
62557
- element.addEventListener("dragstart", (e) => this.onDragStart(e));
62558
- this.root = element;
63286
+ const getNodeAttributes = (nodeName, editor) => {
63287
+ switch (nodeName) {
63288
+ case "page-number":
63289
+ return {
63290
+ text: editor.options.currentPageNumber || "1",
63291
+ className: "sd-editor-auto-page-number",
63292
+ dataId: "auto-page-number",
63293
+ ariaLabel: "Page number node"
63294
+ };
63295
+ case "total-page-number":
63296
+ return {
63297
+ text: editor.options.parentEditor?.currentTotalPages || "1",
63298
+ className: "sd-editor-auto-total-pages",
63299
+ dataId: "auto-total-pages",
63300
+ ariaLabel: "Total page count node"
63301
+ };
63302
+ default:
63303
+ return {};
62559
63304
  }
62560
- updateView() {
62561
- const domAttrs = Attribute2.mergeAttributes(this.htmlAttributes);
62562
- updateDOMAttributes(this.dom, { ...domAttrs });
63305
+ };
63306
+ class AutoPageNumberNodeView {
63307
+ constructor(node, getPos, decorations, editor, htmlAttributes = {}) {
63308
+ __privateAdd$1(this, _AutoPageNumberNodeView_instances);
63309
+ this.node = node;
63310
+ this.editor = editor;
63311
+ this.view = editor.view;
63312
+ this.getPos = getPos;
63313
+ this.editor = editor;
63314
+ this.dom = __privateMethod$1(this, _AutoPageNumberNodeView_instances, renderDom_fn).call(this, node, htmlAttributes);
62563
63315
  }
62564
- update(node, decorations, innerDecorations) {
62565
- const result = super.update(node, decorations, innerDecorations);
62566
- if (!result) return false;
62567
- this.updateView();
63316
+ update(node) {
63317
+ const incomingType = node?.type?.name;
63318
+ const currentType = this.node?.type?.name;
63319
+ if (!incomingType || incomingType !== currentType) return false;
63320
+ this.node = node;
62568
63321
  return true;
62569
63322
  }
62570
63323
  }
62571
- const structuredContentClass = "sd-structured-content-block";
62572
- const structuredContentInnerClass = "sd-structured-content-block__content";
62573
- const StructuredContentBlock = Node$1.create({
62574
- name: "structuredContentBlock",
62575
- group: "block structuredContent",
62576
- content: "block*",
63324
+ _AutoPageNumberNodeView_instances = /* @__PURE__ */ new WeakSet();
63325
+ renderDom_fn = function(node, htmlAttributes) {
63326
+ const attrs = getNodeAttributes(this.node.type.name, this.editor);
63327
+ const content = document.createTextNode(String(attrs.text));
63328
+ const nodeContent = document.createElement("span");
63329
+ nodeContent.className = attrs.className;
63330
+ nodeContent.setAttribute("data-id", attrs.dataId);
63331
+ nodeContent.setAttribute("aria-label", attrs.ariaLabel);
63332
+ const currentPos = this.getPos();
63333
+ const { styles, marks } = getMarksFromNeighbors(currentPos, this.view);
63334
+ __privateMethod$1(this, _AutoPageNumberNodeView_instances, scheduleUpdateNodeStyle_fn).call(this, currentPos, marks);
63335
+ Object.assign(nodeContent.style, styles);
63336
+ nodeContent.appendChild(content);
63337
+ Object.entries(htmlAttributes).forEach(([key2, value]) => {
63338
+ if (value) nodeContent.setAttribute(key2, value);
63339
+ });
63340
+ return nodeContent;
63341
+ };
63342
+ scheduleUpdateNodeStyle_fn = function(pos, marks) {
63343
+ setTimeout(() => {
63344
+ const { state: state2 } = this.editor;
63345
+ const { dispatch } = this.view;
63346
+ const node = state2.doc.nodeAt(pos);
63347
+ if (!node || node.isText) return;
63348
+ const currentMarks = node.attrs.marksAsAttrs || [];
63349
+ const newMarks = marks.map((m2) => ({ type: m2.type.name, attrs: m2.attrs }));
63350
+ const isEqual = JSON.stringify(currentMarks) === JSON.stringify(newMarks);
63351
+ if (isEqual) return;
63352
+ const newAttrs = {
63353
+ ...node.attrs,
63354
+ marksAsAttrs: newMarks
63355
+ };
63356
+ const tr = state2.tr.setNodeMarkup(pos, void 0, newAttrs);
63357
+ dispatch(tr);
63358
+ }, 0);
63359
+ };
63360
+ const getMarksFromNeighbors = (currentPos, view) => {
63361
+ const $pos = view.state.doc.resolve(currentPos);
63362
+ const styles = {};
63363
+ const marks = [];
63364
+ const before = $pos.nodeBefore;
63365
+ if (before) {
63366
+ Object.assign(styles, processMarks(before.marks));
63367
+ marks.push(...before.marks);
63368
+ }
63369
+ const after = $pos.nodeAfter;
63370
+ if (after) {
63371
+ Object.assign(styles, { ...styles, ...processMarks(after.marks) });
63372
+ marks.push(...after.marks);
63373
+ }
63374
+ return {
63375
+ styles,
63376
+ marks
63377
+ };
63378
+ };
63379
+ const processMarks = (marks) => {
63380
+ const styles = {};
63381
+ marks.forEach((mark) => {
63382
+ const { type: type2, attrs } = mark;
63383
+ switch (type2.name) {
63384
+ case "textStyle":
63385
+ if (attrs.fontFamily) styles["font-family"] = attrs.fontFamily;
63386
+ if (attrs.fontSize) styles["font-size"] = attrs.fontSize;
63387
+ if (attrs.color) styles["color"] = attrs.color;
63388
+ if (attrs.backgroundColor) styles["background-color"] = attrs.backgroundColor;
63389
+ break;
63390
+ case "bold":
63391
+ styles["font-weight"] = "bold";
63392
+ break;
63393
+ case "italic":
63394
+ styles["font-style"] = "italic";
63395
+ break;
63396
+ case "underline":
63397
+ styles["text-decoration"] = (styles["text-decoration"] || "") + " underline";
63398
+ break;
63399
+ case "strike":
63400
+ styles["text-decoration"] = (styles["text-decoration"] || "") + " line-through";
63401
+ break;
63402
+ default:
63403
+ if (attrs?.style) {
63404
+ Object.entries(attrs.style).forEach(([key2, value]) => {
63405
+ styles[key2] = value;
63406
+ });
63407
+ }
63408
+ break;
63409
+ }
63410
+ });
63411
+ return styles;
63412
+ };
63413
+ const ShapeContainer = Node$1.create({
63414
+ name: "shapeContainer",
63415
+ group: "block",
63416
+ content: "block+",
62577
63417
  isolating: true,
62578
- atom: false,
62579
- // false - has editable content.
62580
- draggable: true,
62581
63418
  addOptions() {
62582
63419
  return {
62583
63420
  htmlAttributes: {
62584
- class: structuredContentClass,
62585
- "aria-label": "Structured content block node"
63421
+ class: "sd-editor-shape-container",
63422
+ "aria-label": "Shape container node"
62586
63423
  }
62587
63424
  };
62588
63425
  },
62589
63426
  addAttributes() {
62590
63427
  return {
62591
- id: {
63428
+ fillcolor: {
63429
+ renderDOM: (attrs) => {
63430
+ if (!attrs.fillcolor) return {};
63431
+ return {
63432
+ style: `background-color: ${attrs.fillcolor}`
63433
+ };
63434
+ }
63435
+ },
63436
+ sdBlockId: {
62592
63437
  default: null,
62593
- parseDOM: (elem) => elem.getAttribute("data-id"),
63438
+ keepOnSplit: false,
63439
+ parseDOM: (elem) => elem.getAttribute("data-sd-block-id"),
62594
63440
  renderDOM: (attrs) => {
62595
- if (!attrs.id) return {};
62596
- return { "data-id": attrs.id };
63441
+ return attrs.sdBlockId ? { "data-sd-block-id": attrs.sdBlockId } : {};
62597
63442
  }
62598
63443
  },
62599
- sdtPr: {
63444
+ style: {
63445
+ renderDOM: (attrs) => {
63446
+ if (!attrs.style) return {};
63447
+ return {
63448
+ style: attrs.style
63449
+ };
63450
+ }
63451
+ },
63452
+ wrapAttributes: {
63453
+ rendered: false
63454
+ },
63455
+ attributes: {
62600
63456
  rendered: false
62601
63457
  }
62602
63458
  };
62603
63459
  },
62604
63460
  parseDOM() {
62605
- return [{ tag: "div[data-structured-content-block]" }];
63461
+ return [
63462
+ {
63463
+ tag: `div[data-type="${this.name}"]`
63464
+ }
63465
+ ];
62606
63466
  },
62607
63467
  renderDOM({ htmlAttributes }) {
62608
63468
  return [
62609
63469
  "div",
62610
- Attribute2.mergeAttributes(this.options.htmlAttributes, htmlAttributes, {
62611
- "data-structured-content-block": ""
62612
- }),
63470
+ Attribute2.mergeAttributes(this.options.htmlAttributes, htmlAttributes, { "data-type": this.name }),
62613
63471
  0
62614
63472
  ];
62615
- },
62616
- addNodeView() {
62617
- return (props) => {
62618
- return new StructuredContentBlockView({ ...props });
62619
- };
62620
63473
  }
62621
63474
  });
62622
- class DocumentSectionView {
62623
- constructor(node, getPos, decorations, editor) {
62624
- __privateAdd$1(this, _DocumentSectionView_instances);
62625
- this.node = node;
62626
- this.editor = editor;
62627
- this.decorations = decorations;
62628
- this.view = editor.view;
62629
- this.getPos = getPos;
62630
- __privateMethod$1(this, _DocumentSectionView_instances, init_fn3).call(this);
62631
- }
62632
- }
62633
- _DocumentSectionView_instances = /* @__PURE__ */ new WeakSet();
62634
- init_fn3 = function() {
62635
- const { attrs } = this.node;
62636
- const { id, title, description } = attrs;
62637
- this.dom = document.createElement("div");
62638
- this.dom.className = "sd-document-section-block";
62639
- this.dom.setAttribute("data-id", id);
62640
- this.dom.setAttribute("data-title", title);
62641
- this.dom.setAttribute("data-description", description);
62642
- this.dom.setAttribute("aria-label", "Document section");
62643
- __privateMethod$1(this, _DocumentSectionView_instances, addToolTip_fn).call(this);
62644
- this.contentDOM = document.createElement("div");
62645
- this.contentDOM.className = "sd-document-section-block-content";
62646
- this.contentDOM.setAttribute("contenteditable", "true");
62647
- this.dom.appendChild(this.contentDOM);
62648
- };
62649
- addToolTip_fn = function() {
62650
- const { title } = this.node.attrs;
62651
- this.infoDiv = document.createElement("div");
62652
- this.infoDiv.className = "sd-document-section-block-info";
62653
- const textSpan = document.createElement("span");
62654
- textSpan.textContent = title || "Document section";
62655
- this.infoDiv.appendChild(textSpan);
62656
- this.infoDiv.setAttribute("contenteditable", "false");
62657
- this.dom.appendChild(this.infoDiv);
62658
- };
62659
- const getAllSections = (editor) => {
62660
- if (!editor) return [];
62661
- const type2 = editor.schema.nodes.documentSection;
62662
- if (!type2) return [];
62663
- const sections = [];
62664
- const { state: state2 } = editor;
62665
- state2.doc.descendants((node, pos) => {
62666
- if (node.type.name === type2.name) {
62667
- sections.push({ node, pos });
62668
- }
62669
- });
62670
- return sections;
62671
- };
62672
- const exportSectionsToHTML = (editor) => {
62673
- const sections = getAllSections(editor);
62674
- const processedSections = /* @__PURE__ */ new Set();
62675
- const result = [];
62676
- sections.forEach(({ node }) => {
62677
- const { attrs } = node;
62678
- const { id, title, description } = attrs;
62679
- if (processedSections.has(id)) return;
62680
- processedSections.add(id);
62681
- const html = getHTMLFromNode(node, editor);
62682
- result.push({
62683
- id,
62684
- title,
62685
- description,
62686
- html
62687
- });
62688
- });
62689
- return result;
62690
- };
62691
- const getHTMLFromNode = (node, editor) => {
62692
- const tempDocument = document.implementation.createHTMLDocument();
62693
- const container = tempDocument.createElement("div");
62694
- const fragment = DOMSerializer.fromSchema(editor.schema).serializeFragment(node.content);
62695
- container.appendChild(fragment);
62696
- let html = container.innerHTML;
62697
- return html;
62698
- };
62699
- const exportSectionsToJSON = (editor) => {
62700
- const sections = getAllSections(editor);
62701
- const processedSections = /* @__PURE__ */ new Set();
62702
- const result = [];
62703
- sections.forEach(({ node }) => {
62704
- const { attrs } = node;
62705
- const { id, title, description } = attrs;
62706
- if (processedSections.has(id)) return;
62707
- processedSections.add(id);
62708
- result.push({
62709
- id,
62710
- title,
62711
- description,
62712
- content: node.toJSON()
62713
- });
62714
- });
62715
- return result;
62716
- };
62717
- const getLinkedSectionEditor = (id, options, editor) => {
62718
- const sections = getAllSections(editor);
62719
- const section = sections.find((s) => s.node.attrs.id === id);
62720
- if (!section) return null;
62721
- const child = editor.createChildEditor({
62722
- ...options,
62723
- onUpdate: ({ editor: childEditor, transaction }) => {
62724
- const isFromtLinkedParent = transaction.getMeta("fromLinkedParent");
62725
- if (isFromtLinkedParent) return;
62726
- const updatedContent = childEditor.state.doc.content;
62727
- const sectionNode = getAllSections(editor)?.find((s) => s.node.attrs.id === id);
62728
- if (!sectionNode) return;
62729
- const { pos, node } = sectionNode;
62730
- const newNode = node.type.create(node.attrs, updatedContent, node.marks);
62731
- const tr = editor.state.tr.replaceWith(pos, pos + node.nodeSize, newNode);
62732
- tr.setMeta("fromLinkedChild", true);
62733
- editor.view.dispatch(tr);
62734
- }
62735
- });
62736
- editor.on("update", ({ transaction }) => {
62737
- const isFromLinkedChild = transaction.getMeta("fromLinkedChild");
62738
- if (isFromLinkedChild) return;
62739
- const sectionNode = getAllSections(editor)?.find((s) => s.node.attrs.id === id);
62740
- if (!sectionNode) return;
62741
- const sectionContent = sectionNode.node.content;
62742
- const json = {
62743
- type: "doc",
62744
- content: sectionContent.content.map((node) => node.toJSON())
62745
- };
62746
- const childTr = child.state.tr;
62747
- childTr.setMeta("fromLinkedParent", true);
62748
- childTr.replaceWith(0, child.state.doc.content.size, child.schema.nodeFromJSON(json));
62749
- child.view.dispatch(childTr);
62750
- });
62751
- return child;
62752
- };
62753
- const SectionHelpers = {
62754
- getAllSections,
62755
- exportSectionsToHTML,
62756
- exportSectionsToJSON,
62757
- getLinkedSectionEditor
62758
- };
62759
- const DocumentSection = Node$1.create({
62760
- name: "documentSection",
63475
+ const ShapeTextbox = Node$1.create({
63476
+ name: "shapeTextbox",
62761
63477
  group: "block",
62762
- content: "block*",
62763
- atom: true,
63478
+ content: "paragraph* block*",
62764
63479
  isolating: true,
62765
63480
  addOptions() {
62766
63481
  return {
62767
63482
  htmlAttributes: {
62768
- class: "sd-document-section-block",
62769
- "aria-label": "Structured content block"
63483
+ class: "sd-editor-shape-textbox",
63484
+ "aria-label": "Shape textbox node"
62770
63485
  }
62771
63486
  };
62772
63487
  },
62773
- parseDOM() {
62774
- return [
62775
- {
62776
- tag: "div.sd-document-section-block",
62777
- priority: 60
62778
- }
62779
- ];
62780
- },
62781
- renderDOM({ htmlAttributes }) {
62782
- return ["div", Attribute2.mergeAttributes(this.options.htmlAttributes, htmlAttributes), 0];
62783
- },
62784
63488
  addAttributes() {
62785
63489
  return {
62786
- id: {},
62787
63490
  sdBlockId: {
62788
63491
  default: null,
62789
63492
  keepOnSplit: false,
@@ -62792,212 +63495,131 @@ const DocumentSection = Node$1.create({
62792
63495
  return attrs.sdBlockId ? { "data-sd-block-id": attrs.sdBlockId } : {};
62793
63496
  }
62794
63497
  },
62795
- title: {},
62796
- description: {},
62797
- sectionType: {},
62798
- isLocked: { default: false }
63498
+ attributes: {
63499
+ rendered: false
63500
+ }
62799
63501
  };
62800
63502
  },
62801
- addNodeView() {
62802
- return ({ node, editor, getPos, decorations }) => {
62803
- return new DocumentSectionView(node, getPos, decorations, editor);
63503
+ parseDOM() {
63504
+ return [
63505
+ {
63506
+ tag: `div[data-type="${this.name}"]`
63507
+ }
63508
+ ];
63509
+ },
63510
+ renderDOM({ htmlAttributes }) {
63511
+ return [
63512
+ "div",
63513
+ Attribute2.mergeAttributes(this.options.htmlAttributes, htmlAttributes, { "data-type": this.name }),
63514
+ 0
63515
+ ];
63516
+ }
63517
+ });
63518
+ const ContentBlock = Node$1.create({
63519
+ name: "contentBlock",
63520
+ group: "inline",
63521
+ content: "",
63522
+ isolating: true,
63523
+ atom: true,
63524
+ inline: true,
63525
+ addOptions() {
63526
+ return {
63527
+ htmlAttributes: {
63528
+ contenteditable: false
63529
+ }
62804
63530
  };
62805
63531
  },
62806
- addCommands() {
63532
+ addAttributes() {
62807
63533
  return {
62808
- /**
62809
- * Create a lockable content section
62810
- * @category Command
62811
- * @param {SectionCreate} [options={}] - Section configuration
62812
- * @example
62813
- * editor.commands.createDocumentSection({
62814
- * id: 1,
62815
- * title: 'Terms & Conditions',
62816
- * isLocked: true,
62817
- * html: '<p>Legal content...</p>'
62818
- * })
62819
- */
62820
- createDocumentSection: (options = {}) => ({ tr, state: state2, dispatch, editor }) => {
62821
- const { selection } = state2;
62822
- let { from: from2, to } = selection;
62823
- let content = selection.content().content;
62824
- const { html: optionsHTML, json: optionsJSON } = options;
62825
- if (optionsHTML) {
62826
- const html = htmlHandler(optionsHTML, this.editor);
62827
- const doc2 = DOMParser$1.fromSchema(this.editor.schema).parse(html);
62828
- content = doc2.content;
62829
- }
62830
- if (optionsJSON) {
62831
- content = this.editor.schema.nodeFromJSON(optionsJSON);
62832
- }
62833
- if (!content?.content?.length) {
62834
- content = this.editor.schema.nodeFromJSON({ type: "paragraph", content: [] });
62835
- }
62836
- if (!options.id) {
62837
- const allSections = SectionHelpers.getAllSections(editor);
62838
- options.id = allSections.length + 1;
62839
- }
62840
- if (!options.title) {
62841
- options.title = "Document section";
62842
- }
62843
- const node = this.type.createAndFill(options, content);
62844
- if (!node) return false;
62845
- const isAlreadyInSdtBlock = findParentNode((node2) => node2.type.name === "documentSection")(selection);
62846
- if (isAlreadyInSdtBlock && isAlreadyInSdtBlock.node) {
62847
- const insertPos2 = isAlreadyInSdtBlock.pos + isAlreadyInSdtBlock.node.nodeSize;
62848
- from2 = insertPos2;
62849
- to = insertPos2;
62850
- }
62851
- tr.replaceRangeWith(from2, to, node);
62852
- const nodeEnd = from2 + node.nodeSize;
62853
- let shouldInsertParagraph = true;
62854
- let insertPos = nodeEnd;
62855
- if (nodeEnd >= tr.doc.content.size) {
62856
- insertPos = tr.doc.content.size;
62857
- if (insertPos > 0) {
62858
- const $endPos = tr.doc.resolve(insertPos);
62859
- if ($endPos.nodeBefore && $endPos.nodeBefore.type.name === "paragraph") {
62860
- shouldInsertParagraph = false;
62861
- }
62862
- }
62863
- }
62864
- if (shouldInsertParagraph) {
62865
- const emptyParagraph = tr.doc.type.schema.nodes.paragraph.create();
62866
- tr.insert(insertPos, emptyParagraph);
62867
- }
62868
- if (dispatch) {
62869
- tr.setMeta("documentSection", { action: "create" });
62870
- dispatch(tr);
62871
- setTimeout(() => {
62872
- try {
62873
- const currentState = editor.state;
62874
- const docSize = currentState.doc.content.size;
62875
- let targetPos = from2 + node.nodeSize;
62876
- if (shouldInsertParagraph) {
62877
- targetPos += 1;
62878
- }
62879
- targetPos = Math.min(targetPos, docSize);
62880
- if (targetPos < docSize && targetPos > 0) {
62881
- const newSelection = Selection.near(currentState.doc.resolve(targetPos));
62882
- const newTr = currentState.tr.setSelection(newSelection);
62883
- editor.view.dispatch(newTr);
62884
- }
62885
- } catch (e) {
62886
- console.warn("Could not set delayed selection:", e);
62887
- }
62888
- }, 0);
63534
+ horizontalRule: {
63535
+ default: false,
63536
+ renderDOM: ({ horizontalRule }) => {
63537
+ if (!horizontalRule) return {};
63538
+ return { "data-horizontal-rule": "true" };
62889
63539
  }
62890
- return true;
62891
63540
  },
62892
- /**
62893
- * Remove section wrapper at cursor, preserving its content
62894
- * @category Command
62895
- * @example
62896
- * editor.commands.removeSectionAtSelection()
62897
- * @note Content stays in document, only section wrapper is removed
62898
- */
62899
- removeSectionAtSelection: () => ({ tr, dispatch }) => {
62900
- const sdtNode = findParentNode((node2) => node2.type.name === "documentSection")(tr.selection);
62901
- if (!sdtNode) return false;
62902
- const { node, pos } = sdtNode;
62903
- const nodeStart = pos;
62904
- const nodeEnd = nodeStart + node.nodeSize;
62905
- const contentToPreserve = node.content;
62906
- tr.delete(nodeStart, nodeEnd);
62907
- if (contentToPreserve.size > 0) {
62908
- tr.insert(nodeStart, contentToPreserve);
62909
- }
62910
- const newPos = Math.min(nodeStart, tr.doc.content.size);
62911
- tr.setSelection(Selection.near(tr.doc.resolve(newPos)));
62912
- if (dispatch) {
62913
- tr.setMeta("documentSection", { action: "delete" });
62914
- dispatch(tr);
63541
+ size: {
63542
+ default: null,
63543
+ renderDOM: ({ size: size2 }) => {
63544
+ if (!size2) return {};
63545
+ let style2 = "";
63546
+ if (size2.top) style2 += `top: ${size2.top}px; `;
63547
+ if (size2.left) style2 += `left: ${size2.left}px; `;
63548
+ if (size2.width) style2 += `width: ${size2.width.toString().endsWith("%") ? size2.width : `${size2.width}px`}; `;
63549
+ if (size2.height)
63550
+ style2 += `height: ${size2.height.toString().endsWith("%") ? size2.height : `${size2.height}px`}; `;
63551
+ return { style: style2 };
62915
63552
  }
62916
- return true;
62917
63553
  },
62918
- /**
62919
- * Delete section and all its content
62920
- * @category Command
62921
- * @param {number} id - Section to delete
62922
- * @example
62923
- * editor.commands.removeSectionById(123)
62924
- */
62925
- removeSectionById: (id) => ({ tr, dispatch }) => {
62926
- const sections = SectionHelpers.getAllSections(this.editor);
62927
- const sectionToRemove = sections.find(({ node: node2 }) => node2.attrs.id === id);
62928
- if (!sectionToRemove) return false;
62929
- const { pos, node } = sectionToRemove;
62930
- const nodeStart = pos;
62931
- const nodeEnd = nodeStart + node.nodeSize;
62932
- tr.delete(nodeStart, nodeEnd);
62933
- if (dispatch) {
62934
- tr.setMeta("documentSection", { action: "delete", id });
62935
- dispatch(tr);
63554
+ background: {
63555
+ default: null,
63556
+ renderDOM: (attrs) => {
63557
+ if (!attrs.background) return {};
63558
+ return {
63559
+ style: `background-color: ${attrs.background}`
63560
+ };
62936
63561
  }
62937
- return true;
62938
63562
  },
63563
+ drawingContent: {
63564
+ rendered: false
63565
+ },
63566
+ attributes: {
63567
+ rendered: false
63568
+ }
63569
+ };
63570
+ },
63571
+ parseDOM() {
63572
+ return [
63573
+ {
63574
+ tag: `div[data-type="${this.name}"]`
63575
+ }
63576
+ ];
63577
+ },
63578
+ renderDOM({ htmlAttributes }) {
63579
+ return ["div", Attribute2.mergeAttributes(this.options.htmlAttributes, htmlAttributes, { "data-type": this.name })];
63580
+ },
63581
+ addCommands() {
63582
+ return {
62939
63583
  /**
62940
- * Lock section against edits
63584
+ * Insert a horizontal rule
62941
63585
  * @category Command
62942
- * @param {number} id - Section to lock
62943
63586
  * @example
62944
- * editor.commands.lockSectionById(123)
63587
+ * editor.commands.insertHorizontalRule()
63588
+ * @note Creates a visual separator between content sections
62945
63589
  */
62946
- lockSectionById: (id) => ({ tr, dispatch }) => {
62947
- const sections = SectionHelpers.getAllSections(this.editor);
62948
- const sectionToLock = sections.find(({ node }) => node.attrs.id === id);
62949
- if (!sectionToLock) return false;
62950
- tr.setNodeMarkup(sectionToLock.pos, null, { ...sectionToLock.node.attrs, isLocked: true });
62951
- if (dispatch) {
62952
- tr.setMeta("documentSection", { action: "lock", id });
62953
- dispatch(tr);
62954
- }
62955
- return true;
63590
+ insertHorizontalRule: () => ({ commands: commands2 }) => {
63591
+ return commands2.insertContent({
63592
+ type: this.name,
63593
+ attrs: {
63594
+ horizontalRule: true,
63595
+ size: { width: "100%", height: 2 },
63596
+ background: "#e5e7eb"
63597
+ }
63598
+ });
62956
63599
  },
62957
63600
  /**
62958
- * Modify section attributes or content
63601
+ * Insert a content block
62959
63602
  * @category Command
62960
- * @param {SectionUpdate} options - Changes to apply
63603
+ * @param {ContentBlockConfig} config - Block configuration
62961
63604
  * @example
62962
- * editor.commands.updateSectionById({ id: 123, attrs: { isLocked: false } })
62963
- * editor.commands.updateSectionById({ id: 123, html: '<p>New content</p>' })
62964
- * editor.commands.updateSectionById({
62965
- * id: 123,
62966
- * html: '<p>Updated</p>',
62967
- * attrs: { title: 'New Title' }
63605
+ * // Insert a spacer block
63606
+ * editor.commands.insertContentBlock({ size: { height: 20 } })
63607
+ *
63608
+ * @example
63609
+ * // Insert a colored divider
63610
+ * editor.commands.insertContentBlock({
63611
+ * size: { width: '50%', height: 3 },
63612
+ * background: '#3b82f6'
62968
63613
  * })
63614
+ * @note Used for spacing, dividers, and special inline content
62969
63615
  */
62970
- updateSectionById: ({ id, html, json, attrs }) => ({ tr, dispatch, editor }) => {
62971
- const sections = SectionHelpers.getAllSections(editor || this.editor);
62972
- const sectionToUpdate = sections.find(({ node: node2 }) => node2.attrs.id === id);
62973
- if (!sectionToUpdate) return false;
62974
- const { pos, node } = sectionToUpdate;
62975
- let newContent = null;
62976
- if (html) {
62977
- const htmlDoc = htmlHandler(html, editor || this.editor);
62978
- const doc2 = DOMParser$1.fromSchema((editor || this.editor).schema).parse(htmlDoc);
62979
- newContent = doc2.content;
62980
- }
62981
- if (json) {
62982
- newContent = (editor || this.editor).schema.nodeFromJSON(json);
62983
- }
62984
- if (!newContent) {
62985
- newContent = node.content;
62986
- }
62987
- const updatedNode = node.type.create({ ...node.attrs, ...attrs }, newContent, node.marks);
62988
- tr.replaceWith(pos, pos + node.nodeSize, updatedNode);
62989
- if (dispatch) {
62990
- tr.setMeta("documentSection", { action: "update", id, attrs });
62991
- dispatch(tr);
62992
- }
62993
- return true;
63616
+ insertContentBlock: (config2) => ({ commands: commands2 }) => {
63617
+ return commands2.insertContent({
63618
+ type: this.name,
63619
+ attrs: config2
63620
+ });
62994
63621
  }
62995
63622
  };
62996
- },
62997
- addHelpers() {
62998
- return {
62999
- ...SectionHelpers
63000
- };
63001
63623
  }
63002
63624
  });
63003
63625
  const { findChildren } = helpers;
@@ -69565,7 +70187,8 @@ const nodeResizer = (nodeNames = ["image"], editor) => {
69565
70187
  const prevSelection = prevState.selection;
69566
70188
  if (selection.from !== prevSelection.from || selection.to !== prevSelection.to) {
69567
70189
  setTimeout(() => {
69568
- const selectedResizableWrapper = document.querySelector(".sd-editor-resizable-wrapper");
70190
+ const searchRoot = editorView?.dom;
70191
+ const selectedResizableWrapper = searchRoot?.querySelector(".sd-editor-resizable-wrapper");
69569
70192
  if (selectedResizableWrapper) {
69570
70193
  showResizeHandles(view2, selectedResizableWrapper);
69571
70194
  } else {
@@ -69846,6 +70469,7 @@ const getStarterExtensions = () => {
69846
70469
  Search,
69847
70470
  StructuredContent,
69848
70471
  StructuredContentBlock,
70472
+ StructuredContentCommands,
69849
70473
  DocumentSection,
69850
70474
  NodeResizer,
69851
70475
  CustomSelection,
@@ -83101,7 +83725,7 @@ class SuperToolbar extends EventEmitter2 {
83101
83725
  if (!argument) return;
83102
83726
  item.onActivate({ zoom: argument });
83103
83727
  this.emit("superdoc-command", { item, argument });
83104
- const layers = document.querySelector(this.superdoc.config.selector)?.querySelector(".layers");
83728
+ const layers = this.superdoc.element?.querySelector(".layers");
83105
83729
  if (!layers) return;
83106
83730
  const isMobileDevice = typeof screen.orientation !== "undefined";
83107
83731
  const isSmallScreen = window.matchMedia("(max-width: 834px)").matches;
@@ -84163,14 +84787,6 @@ function getStructureFromResolvedPos(state2, pos) {
84163
84787
  return null;
84164
84788
  }
84165
84789
  }
84166
- const shouldBypassContextMenu = (event) => {
84167
- if (!event) return false;
84168
- if (event.ctrlKey || event.metaKey) {
84169
- return true;
84170
- }
84171
- const isKeyboardInvocation = event.type === "contextmenu" && typeof event.detail === "number" && event.detail === 0 && (event.button === 0 || event.button === void 0) && event.clientX === 0 && event.clientY === 0;
84172
- return Boolean(isKeyboardInvocation);
84173
- };
84174
84790
  const isModuleEnabled = (editorOptions, moduleName) => {
84175
84791
  switch (moduleName) {
84176
84792
  case "ai":