@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
@@ -24839,17 +24839,16 @@ const getParagraphSpacing = (node, docx, styleId = "", marks = [], options = {})
24839
24839
  };
24840
24840
  const getDefaultParagraphStyle = (docx, styleId = "") => {
24841
24841
  const styles = docx["word/styles.xml"];
24842
- if (!styles) {
24842
+ const rootElements = styles?.elements?.[0]?.elements;
24843
+ if (!rootElements?.length) {
24843
24844
  return {};
24844
24845
  }
24845
- const defaults = styles.elements[0].elements?.find((el) => el.name === "w:docDefaults");
24846
- const pDefault = defaults.elements.find((el) => el.name === "w:pPrDefault");
24846
+ const defaults = rootElements.find((el) => el.name === "w:docDefaults");
24847
+ const pDefault = defaults?.elements?.find((el) => el.name === "w:pPrDefault") || {};
24847
24848
  const pPrDefault = pDefault?.elements?.find((el) => el.name === "w:pPr");
24848
24849
  const pPrDefaultSpacingTag = pPrDefault?.elements?.find((el) => el.name === "w:spacing") || {};
24849
24850
  const pPrDefaultIndentTag = pPrDefault?.elements?.find((el) => el.name === "w:ind") || {};
24850
- const stylesNormal = styles.elements[0].elements?.find(
24851
- (el) => el.name === "w:style" && el.attributes["w:styleId"] === "Normal"
24852
- );
24851
+ const stylesNormal = rootElements.find((el) => el.name === "w:style" && el.attributes["w:styleId"] === "Normal");
24853
24852
  const pPrNormal = stylesNormal?.elements?.find((el) => el.name === "w:pPr");
24854
24853
  const pPrNormalSpacingTag = pPrNormal?.elements?.find((el) => el.name === "w:spacing") || {};
24855
24854
  const pPrNormalIndentTag = pPrNormal?.elements?.find((el) => el.name === "w:ind") || {};
@@ -24858,9 +24857,7 @@ const getDefaultParagraphStyle = (docx, styleId = "") => {
24858
24857
  let pPrStyleIdIndentTag = {};
24859
24858
  let pPrStyleJc = {};
24860
24859
  if (styleId) {
24861
- const stylesById = styles.elements[0].elements?.find(
24862
- (el) => el.name === "w:style" && el.attributes["w:styleId"] === styleId
24863
- );
24860
+ const stylesById = rootElements.find((el) => el.name === "w:style" && el.attributes["w:styleId"] === styleId);
24864
24861
  const pPrById = stylesById?.elements?.find((el) => el.name === "w:pPr");
24865
24862
  pPrStyleIdSpacingTag = pPrById?.elements?.find((el) => el.name === "w:spacing") || {};
24866
24863
  pPrStyleIdIndentTag = pPrById?.elements?.find((el) => el.name === "w:ind") || {};
@@ -27163,6 +27160,68 @@ const config$a = {
27163
27160
  decode: decode$h
27164
27161
  };
27165
27162
  const translator$a = NodeTranslator.from(config$a);
27163
+ const DEFAULT_PAGE_WIDTH_TWIPS = 12240;
27164
+ const DEFAULT_PAGE_MARGIN_TWIPS = 1440;
27165
+ const DEFAULT_CONTENT_WIDTH_TWIPS = DEFAULT_PAGE_WIDTH_TWIPS - 2 * DEFAULT_PAGE_MARGIN_TWIPS;
27166
+ const MIN_COLUMN_WIDTH_TWIPS = pixelsToTwips(10);
27167
+ const pctToPercent = (value) => {
27168
+ if (value == null) return null;
27169
+ return value / 50;
27170
+ };
27171
+ const resolveContentWidthTwips = () => DEFAULT_CONTENT_WIDTH_TWIPS;
27172
+ const resolveMeasurementWidthPx = (measurement) => {
27173
+ if (!measurement || typeof measurement.value !== "number" || measurement.value <= 0) return null;
27174
+ const { value, type: type2 } = measurement;
27175
+ if (!type2 || type2 === "auto") return null;
27176
+ if (type2 === "dxa") return twipsToPixels(value);
27177
+ if (type2 === "pct") {
27178
+ const percent2 = pctToPercent(value);
27179
+ if (percent2 == null || percent2 <= 0) return null;
27180
+ const widthTwips = resolveContentWidthTwips() * percent2 / 100;
27181
+ return twipsToPixels(widthTwips);
27182
+ }
27183
+ return null;
27184
+ };
27185
+ const countColumnsInRow = (row) => {
27186
+ if (!row?.elements?.length) return 0;
27187
+ return row.elements.reduce((count, element) => {
27188
+ if (element.name !== "w:tc") return count;
27189
+ const tcPr = element.elements?.find((el) => el.name === "w:tcPr");
27190
+ const gridSpan = tcPr?.elements?.find((el) => el.name === "w:gridSpan");
27191
+ const spanValue = parseInt(gridSpan?.attributes?.["w:val"] || "1", 10);
27192
+ return count + (Number.isFinite(spanValue) && spanValue > 0 ? spanValue : 1);
27193
+ }, 0);
27194
+ };
27195
+ const clampColumnWidthTwips = (value) => Math.max(Math.round(value), MIN_COLUMN_WIDTH_TWIPS);
27196
+ const createFallbackGrid = (columnCount, columnWidthTwips) => Array.from({ length: columnCount }, () => ({ col: clampColumnWidthTwips(columnWidthTwips) }));
27197
+ const buildFallbackGridForTable = ({ params: params2, rows, tableWidth, tableWidthMeasurement }) => {
27198
+ const firstRow = rows.find((row) => row.elements?.some((el) => el.name === "w:tc"));
27199
+ const columnCount = countColumnsInRow(firstRow);
27200
+ if (!columnCount) return null;
27201
+ const schemaDefaultPx = getSchemaDefaultColumnWidthPx(
27202
+ /** @type {any} */
27203
+ params2
27204
+ );
27205
+ const minimumColumnWidthPx = Number.isFinite(schemaDefaultPx) && schemaDefaultPx > 0 ? schemaDefaultPx : DEFAULT_COLUMN_WIDTH_PX;
27206
+ let totalWidthPx;
27207
+ if (tableWidthMeasurement) {
27208
+ const resolved = resolveMeasurementWidthPx(tableWidthMeasurement);
27209
+ if (resolved != null) totalWidthPx = resolved;
27210
+ }
27211
+ if (totalWidthPx == null && tableWidth?.width && tableWidth.width > 0) {
27212
+ totalWidthPx = tableWidth.width;
27213
+ }
27214
+ if (totalWidthPx == null) {
27215
+ totalWidthPx = minimumColumnWidthPx * columnCount;
27216
+ }
27217
+ const rawColumnWidthPx = Math.max(totalWidthPx / columnCount, minimumColumnWidthPx);
27218
+ const columnWidthTwips = clampColumnWidthTwips(pixelsToTwips(rawColumnWidthPx));
27219
+ const fallbackColumnWidthPx = twipsToPixels(columnWidthTwips);
27220
+ return {
27221
+ grid: createFallbackGrid(columnCount, columnWidthTwips),
27222
+ columnWidths: Array(columnCount).fill(fallbackColumnWidthPx)
27223
+ };
27224
+ };
27166
27225
  const XML_NODE_NAME$9 = "w:tbl";
27167
27226
  const SD_NODE_NAME$9 = "table";
27168
27227
  const encode$g = (params2, encodedAttrs) => {
@@ -27182,7 +27241,6 @@ const encode$g = (params2, encodedAttrs) => {
27182
27241
  "justification",
27183
27242
  "tableLayout",
27184
27243
  ["tableIndent", ({ value, type: type2 }) => ({ width: twipsToPixels(value), type: type2 })],
27185
- ["tableWidth", ({ value, type: type2 }) => ({ width: twipsToPixels(value), type: type2 })],
27186
27244
  ["tableCellSpacing", ({ value, type: type2 }) => ({ w: String(value), type: type2 })]
27187
27245
  ].forEach((prop) => {
27188
27246
  let key2;
@@ -27200,6 +27258,21 @@ const encode$g = (params2, encodedAttrs) => {
27200
27258
  if (encodedAttrs.tableCellSpacing) {
27201
27259
  encodedAttrs["borderCollapse"] = "separate";
27202
27260
  }
27261
+ if (encodedAttrs.tableProperties?.tableWidth) {
27262
+ const tableWidthMeasurement = encodedAttrs.tableProperties.tableWidth;
27263
+ const widthPx = twipsToPixels(tableWidthMeasurement.value);
27264
+ if (widthPx != null) {
27265
+ encodedAttrs.tableWidth = {
27266
+ width: widthPx,
27267
+ type: tableWidthMeasurement.type
27268
+ };
27269
+ } else if (tableWidthMeasurement.type === "auto") {
27270
+ encodedAttrs.tableWidth = {
27271
+ width: 0,
27272
+ type: tableWidthMeasurement.type
27273
+ };
27274
+ }
27275
+ }
27203
27276
  const { borders, rowBorders } = _processTableBorders(encodedAttrs.tableProperties?.borders || {});
27204
27277
  const referencedStyles = _getReferencedTableStyles(encodedAttrs.tableStyleId, params2);
27205
27278
  if (referencedStyles?.cellMargins && !encodedAttrs.tableProperties?.cellMargins) {
@@ -27213,7 +27286,19 @@ const encode$g = (params2, encodedAttrs) => {
27213
27286
  const borderRowData = Object.assign({}, referencedStyles?.rowBorders || {}, rowBorders || {});
27214
27287
  encodedAttrs["borders"] = borderData;
27215
27288
  const tblStyleTag = tblPr?.elements?.find((el) => el.name === "w:tblStyle");
27216
- const columnWidths = (encodedAttrs["grid"] ?? []).map((item) => twipsToPixels(item.col));
27289
+ let columnWidths = Array.isArray(encodedAttrs["grid"]) ? encodedAttrs["grid"].map((item) => twipsToPixels(item.col)) : [];
27290
+ if (!columnWidths.length) {
27291
+ const fallback = buildFallbackGridForTable({
27292
+ params: params2,
27293
+ rows,
27294
+ tableWidth: encodedAttrs.tableWidth,
27295
+ tableWidthMeasurement: encodedAttrs.tableProperties?.tableWidth
27296
+ });
27297
+ if (fallback) {
27298
+ encodedAttrs.grid = fallback.grid;
27299
+ columnWidths = fallback.columnWidths;
27300
+ }
27301
+ }
27217
27302
  const content = [];
27218
27303
  rows.forEach((row) => {
27219
27304
  const result = translator$G.encode({
@@ -28075,6 +28160,9 @@ function handleStructuredContentNode(params2) {
28075
28160
  const node = nodes[0];
28076
28161
  const sdtPr = node.elements.find((el) => el.name === "w:sdtPr");
28077
28162
  const sdtContent = node.elements.find((el) => el.name === "w:sdtContent");
28163
+ const id = sdtPr?.elements?.find((el) => el.name === "w:id");
28164
+ const tag = sdtPr?.elements?.find((el) => el.name === "w:tag");
28165
+ const alias = sdtPr?.elements?.find((el) => el.name === "w:alias");
28078
28166
  if (!sdtContent) {
28079
28167
  return null;
28080
28168
  }
@@ -28086,15 +28174,16 @@ function handleStructuredContentNode(params2) {
28086
28174
  nodes: sdtContent.elements,
28087
28175
  path: [...params2.path || [], sdtContent]
28088
28176
  });
28089
- let sdtContentType = "structuredContent";
28090
- if (paragraph || table) {
28091
- sdtContentType = "structuredContentBlock";
28092
- }
28177
+ const isBlockNode2 = paragraph || table;
28178
+ const sdtContentType = isBlockNode2 ? "structuredContentBlock" : "structuredContent";
28093
28179
  let result = {
28094
28180
  type: sdtContentType,
28095
28181
  content: translatedContent,
28096
28182
  marks,
28097
28183
  attrs: {
28184
+ id: id?.attributes?.["w:val"] || null,
28185
+ tag: tag?.attributes?.["w:val"] || null,
28186
+ alias: alias?.attributes?.["w:val"] || null,
28098
28187
  sdtPr
28099
28188
  }
28100
28189
  };
@@ -30352,21 +30441,55 @@ const generateSdtPrTagForDocumentSection = (id, title, tag) => {
30352
30441
  };
30353
30442
  function translateStructuredContent(params2) {
30354
30443
  const { node } = params2;
30355
- const { attrs = {} } = node;
30356
30444
  const childContent = translateChildNodes({ ...params2, nodes: node.content });
30357
- const nodeElements = [
30358
- {
30359
- name: "w:sdtContent",
30360
- elements: childContent
30361
- }
30362
- ];
30363
- nodeElements.unshift(attrs.sdtPr);
30445
+ const sdtContent = { name: "w:sdtContent", elements: childContent };
30446
+ const sdtPr = generateSdtPrTagForStructuredContent({ node });
30447
+ const nodeElements = [sdtPr, sdtContent];
30364
30448
  const result = {
30365
30449
  name: "w:sdt",
30366
30450
  elements: nodeElements
30367
30451
  };
30368
30452
  return result;
30369
30453
  }
30454
+ function generateSdtPrTagForStructuredContent({ node }) {
30455
+ const { attrs = {} } = node;
30456
+ const id = {
30457
+ name: "w:id",
30458
+ type: "element",
30459
+ attributes: { "w:val": attrs.id }
30460
+ };
30461
+ const alias = {
30462
+ name: "w:alias",
30463
+ type: "element",
30464
+ attributes: { "w:val": attrs.alias }
30465
+ };
30466
+ const tag = {
30467
+ name: "w:tag",
30468
+ type: "element",
30469
+ attributes: { "w:val": attrs.tag }
30470
+ };
30471
+ const resultElements = [];
30472
+ if (attrs.id) resultElements.push(id);
30473
+ if (attrs.alias) resultElements.push(alias);
30474
+ if (attrs.tag) resultElements.push(tag);
30475
+ if (attrs.sdtPr) {
30476
+ const elements = attrs.sdtPr.elements || [];
30477
+ const elementsToExclude = ["w:id", "w:alias", "w:tag"];
30478
+ const restElements = elements.filter((el) => !elementsToExclude.includes(el.name));
30479
+ const result2 = {
30480
+ name: "w:sdtPr",
30481
+ type: "element",
30482
+ elements: [...resultElements, ...restElements]
30483
+ };
30484
+ return result2;
30485
+ }
30486
+ const result = {
30487
+ name: "w:sdtPr",
30488
+ type: "element",
30489
+ elements: resultElements
30490
+ };
30491
+ return result;
30492
+ }
30370
30493
  const XML_NODE_NAME$3 = "w:sdt";
30371
30494
  const SD_NODE_NAME$3 = ["fieldAnnotation", "structuredContent", "structuredContentBlock", "documentSection"];
30372
30495
  const validXmlAttributes$3 = [];
@@ -30594,6 +30717,63 @@ const config = {
30594
30717
  attributes: validXmlAttributes
30595
30718
  };
30596
30719
  const translator = NodeTranslator.from(config);
30720
+ const DEFAULT_SECTION_PROPS_TWIPS = Object.freeze({
30721
+ pageSize: Object.freeze({ width: "12240", height: "15840" }),
30722
+ pageMargins: Object.freeze({
30723
+ top: "1440",
30724
+ right: "1440",
30725
+ bottom: "1440",
30726
+ left: "1440",
30727
+ header: "720",
30728
+ footer: "720",
30729
+ gutter: "0"
30730
+ })
30731
+ });
30732
+ const ensureSectionLayoutDefaults = (sectPr, converter) => {
30733
+ if (!sectPr) {
30734
+ return {
30735
+ type: "element",
30736
+ name: "w:sectPr",
30737
+ elements: []
30738
+ };
30739
+ }
30740
+ if (!sectPr.elements) sectPr.elements = [];
30741
+ const ensureChild = (name) => {
30742
+ let child = sectPr.elements.find((n) => n.name === name);
30743
+ if (!child) {
30744
+ child = {
30745
+ type: "element",
30746
+ name,
30747
+ elements: [],
30748
+ attributes: {}
30749
+ };
30750
+ sectPr.elements.push(child);
30751
+ } else {
30752
+ if (!child.elements) child.elements = [];
30753
+ if (!child.attributes) child.attributes = {};
30754
+ }
30755
+ return child;
30756
+ };
30757
+ const pageSize = converter?.pageStyles?.pageSize;
30758
+ const pgSz = ensureChild("w:pgSz");
30759
+ if (pageSize?.width != null) pgSz.attributes["w:w"] = String(inchesToTwips(pageSize.width));
30760
+ if (pageSize?.height != null) pgSz.attributes["w:h"] = String(inchesToTwips(pageSize.height));
30761
+ if (pgSz.attributes["w:w"] == null) pgSz.attributes["w:w"] = DEFAULT_SECTION_PROPS_TWIPS.pageSize.width;
30762
+ if (pgSz.attributes["w:h"] == null) pgSz.attributes["w:h"] = DEFAULT_SECTION_PROPS_TWIPS.pageSize.height;
30763
+ const pageMargins = converter?.pageStyles?.pageMargins;
30764
+ const pgMar = ensureChild("w:pgMar");
30765
+ if (pageMargins) {
30766
+ Object.entries(pageMargins).forEach(([key2, value]) => {
30767
+ const converted = inchesToTwips(value);
30768
+ if (converted != null) pgMar.attributes[`w:${key2}`] = String(converted);
30769
+ });
30770
+ }
30771
+ Object.entries(DEFAULT_SECTION_PROPS_TWIPS.pageMargins).forEach(([key2, value]) => {
30772
+ const attrKey = `w:${key2}`;
30773
+ if (pgMar.attributes[attrKey] == null) pgMar.attributes[attrKey] = value;
30774
+ });
30775
+ return sectPr;
30776
+ };
30597
30777
  const isLineBreakOnlyRun = (node) => {
30598
30778
  if (!node) return false;
30599
30779
  if (node.type === "lineBreak" || node.type === "hardBreak") return true;
@@ -30646,28 +30826,30 @@ function exportSchemaToJson(params2) {
30646
30826
  return handler2(params2);
30647
30827
  }
30648
30828
  function translateBodyNode(params2) {
30649
- let sectPr = params2.bodyNode?.elements.find((n) => n.name === "w:sectPr") || {};
30829
+ let sectPr = params2.bodyNode?.elements?.find((n) => n.name === "w:sectPr");
30830
+ if (!sectPr) {
30831
+ sectPr = {
30832
+ type: "element",
30833
+ name: "w:sectPr",
30834
+ elements: []
30835
+ };
30836
+ } else if (!sectPr.elements) {
30837
+ sectPr = { ...sectPr, elements: [] };
30838
+ }
30839
+ sectPr = ensureSectionLayoutDefaults(sectPr, params2.converter);
30650
30840
  if (params2.converter) {
30651
- const hasHeader = sectPr?.elements?.some((n) => n.name === "w:headerReference");
30841
+ const hasHeader = sectPr.elements?.some((n) => n.name === "w:headerReference");
30652
30842
  const hasDefaultHeader = params2.converter.headerIds?.default;
30653
30843
  if (!hasHeader && hasDefaultHeader && !params2.editor.options.isHeaderOrFooter) {
30654
30844
  const defaultHeader = generateDefaultHeaderFooter("header", params2.converter.headerIds?.default);
30655
30845
  sectPr.elements.push(defaultHeader);
30656
30846
  }
30657
- const hasFooter = sectPr?.elements?.some((n) => n.name === "w:footerReference");
30847
+ const hasFooter = sectPr.elements?.some((n) => n.name === "w:footerReference");
30658
30848
  const hasDefaultFooter = params2.converter.footerIds?.default;
30659
30849
  if (!hasFooter && hasDefaultFooter && !params2.editor.options.isHeaderOrFooter) {
30660
30850
  const defaultFooter = generateDefaultHeaderFooter("footer", params2.converter.footerIds?.default);
30661
30851
  sectPr.elements.push(defaultFooter);
30662
30852
  }
30663
- const newMargins = params2.converter.pageStyles.pageMargins;
30664
- const sectPrMargins = sectPr.elements.find((n) => n.name === "w:pgMar");
30665
- const { attributes } = sectPrMargins;
30666
- Object.entries(newMargins).forEach(([key2, value]) => {
30667
- const convertedValue = inchesToTwips(value);
30668
- attributes[`w:${key2}`] = convertedValue;
30669
- });
30670
- sectPrMargins.attributes = attributes;
30671
30853
  }
30672
30854
  const elements = translateChildNodes(params2);
30673
30855
  if (params2.isHeaderFooter) {
@@ -32094,8 +32276,15 @@ const handlePictNode = (params2) => {
32094
32276
  return { nodes: [], consumed: 0 };
32095
32277
  }
32096
32278
  const [pNode] = nodes;
32097
- const run2 = pNode.elements?.find((el) => el.name === "w:r");
32098
- const pict = run2?.elements?.find((el) => el.name === "w:pict");
32279
+ const runs = pNode.elements?.filter((el) => el.name === "w:r") || [];
32280
+ let pict = null;
32281
+ for (const run2 of runs) {
32282
+ const foundPict = run2.elements?.find((el) => el.name === "w:pict");
32283
+ if (foundPict) {
32284
+ pict = foundPict;
32285
+ break;
32286
+ }
32287
+ }
32099
32288
  if (!pict) {
32100
32289
  return { nodes: [], consumed: 0 };
32101
32290
  }
@@ -32682,6 +32871,7 @@ const createDocumentJson = (docx, converter, editor) => {
32682
32871
  const nodeListHandler = defaultNodeListHandler();
32683
32872
  const bodyNode = json.elements[0].elements.find((el) => el.name === "w:body");
32684
32873
  if (bodyNode) {
32874
+ ensureSectionProperties(bodyNode);
32685
32875
  const node = bodyNode;
32686
32876
  const contentElements = node.elements?.filter((n) => n.name !== "w:sectPr") ?? [];
32687
32877
  const content = pruneIgnoredNodes(contentElements);
@@ -32915,6 +33105,59 @@ function getDocumentStyles(node, docx, converter, editor) {
32915
33105
  styles.alternateHeaders = isAlternatingHeadersOddEven(docx);
32916
33106
  return styles;
32917
33107
  }
33108
+ const DEFAULT_SECTION_PROPS = Object.freeze({
33109
+ pageSize: Object.freeze({ width: "12240", height: "15840" }),
33110
+ pageMargins: Object.freeze({
33111
+ top: "1440",
33112
+ right: "1440",
33113
+ bottom: "1440",
33114
+ left: "1440",
33115
+ header: "720",
33116
+ footer: "720",
33117
+ gutter: "0"
33118
+ })
33119
+ });
33120
+ function ensureSectionProperties(bodyNode, converter) {
33121
+ if (!bodyNode.elements) bodyNode.elements = [];
33122
+ let sectPr = bodyNode.elements.find((el) => el.name === "w:sectPr");
33123
+ if (!sectPr) {
33124
+ sectPr = {
33125
+ type: "element",
33126
+ name: "w:sectPr",
33127
+ elements: []
33128
+ };
33129
+ bodyNode.elements.push(sectPr);
33130
+ } else if (!sectPr.elements) {
33131
+ sectPr.elements = [];
33132
+ }
33133
+ const ensureChild = (name, factory) => {
33134
+ let child = sectPr.elements.find((el) => el.name === name);
33135
+ if (!child) {
33136
+ child = factory();
33137
+ sectPr.elements.push(child);
33138
+ } else if (!child.attributes) {
33139
+ child.attributes = {};
33140
+ }
33141
+ return child;
33142
+ };
33143
+ const pgSz = ensureChild("w:pgSz", () => ({
33144
+ type: "element",
33145
+ name: "w:pgSz",
33146
+ attributes: {}
33147
+ }));
33148
+ pgSz.attributes["w:w"] = pgSz.attributes["w:w"] ?? DEFAULT_SECTION_PROPS.pageSize.width;
33149
+ pgSz.attributes["w:h"] = pgSz.attributes["w:h"] ?? DEFAULT_SECTION_PROPS.pageSize.height;
33150
+ const pgMar = ensureChild("w:pgMar", () => ({
33151
+ type: "element",
33152
+ name: "w:pgMar",
33153
+ attributes: {}
33154
+ }));
33155
+ Object.entries(DEFAULT_SECTION_PROPS.pageMargins).forEach(([key2, value]) => {
33156
+ const attrKey = `w:${key2}`;
33157
+ if (pgMar.attributes[attrKey] == null) pgMar.attributes[attrKey] = value;
33158
+ });
33159
+ return sectPr;
33160
+ }
32918
33161
  function getStyleDefinitions(docx) {
32919
33162
  const styles = docx["word/styles.xml"];
32920
33163
  if (!styles) return [];
@@ -33107,6 +33350,36 @@ const FONT_FAMILY_FALLBACKS = Object.freeze({
33107
33350
  auto: "sans-serif"
33108
33351
  });
33109
33352
  const DEFAULT_GENERIC_FALLBACK = "sans-serif";
33353
+ const DEFAULT_FONT_SIZE_PT = 10;
33354
+ const collectRunDefaultProperties = (runProps, { allowOverrideTypeface = true, allowOverrideSize = true, themeResolver, state: state2 }) => {
33355
+ if (!runProps?.elements?.length || !state2) return;
33356
+ const fontsNode = runProps.elements.find((el) => el.name === "w:rFonts");
33357
+ if (fontsNode?.attributes) {
33358
+ const themeName = fontsNode.attributes["w:asciiTheme"];
33359
+ if (themeName) {
33360
+ const themeInfo = themeResolver?.(themeName) || {};
33361
+ if ((allowOverrideTypeface || !state2.typeface) && themeInfo.typeface) state2.typeface = themeInfo.typeface;
33362
+ if ((allowOverrideTypeface || !state2.panose) && themeInfo.panose) state2.panose = themeInfo.panose;
33363
+ }
33364
+ const ascii = fontsNode.attributes["w:ascii"];
33365
+ if ((allowOverrideTypeface || !state2.typeface) && ascii) {
33366
+ state2.typeface = ascii;
33367
+ }
33368
+ }
33369
+ const sizeNode = runProps.elements.find((el) => el.name === "w:sz");
33370
+ if (sizeNode?.attributes?.["w:val"]) {
33371
+ const sizeTwips = Number(sizeNode.attributes["w:val"]);
33372
+ if (Number.isFinite(sizeTwips)) {
33373
+ if (state2.fallbackSzTwips === void 0) state2.fallbackSzTwips = sizeTwips;
33374
+ const sizePt = sizeTwips / 2;
33375
+ if (allowOverrideSize || state2.fontSizePt === void 0) state2.fontSizePt = sizePt;
33376
+ }
33377
+ }
33378
+ const kernNode = runProps.elements.find((el) => el.name === "w:kern");
33379
+ if (kernNode?.attributes?.["w:val"]) {
33380
+ if (allowOverrideSize || state2.kern === void 0) state2.kern = kernNode.attributes["w:val"];
33381
+ }
33382
+ };
33110
33383
  const _SuperConverter = class _SuperConverter2 {
33111
33384
  constructor(params2 = null) {
33112
33385
  __privateAdd$2(this, _SuperConverter_instances);
@@ -33234,49 +33507,45 @@ const _SuperConverter = class _SuperConverter2 {
33234
33507
  }
33235
33508
  getDocumentDefaultStyles() {
33236
33509
  const styles = this.convertedXml["word/styles.xml"];
33237
- if (!styles) return {};
33238
- const defaults = styles.elements[0].elements.find((el) => el.name === "w:docDefaults");
33239
- const rDefault = defaults.elements.find((el) => el.name === "w:rPrDefault");
33240
- if (!rDefault.elements) return {};
33241
- const rElements = rDefault.elements[0].elements;
33242
- const rFonts = rElements?.find((el) => el.name === "w:rFonts");
33243
- if ("elements" in rDefault) {
33244
- const fontThemeName = rElements.find((el) => el.name === "w:rFonts")?.attributes["w:asciiTheme"];
33245
- let typeface, panose, fontSizeNormal;
33246
- if (fontThemeName) {
33247
- const fontInfo = this.getThemeInfo(fontThemeName);
33248
- typeface = fontInfo.typeface;
33249
- panose = fontInfo.panose;
33250
- } else if (rFonts) {
33251
- typeface = rFonts?.attributes["w:ascii"];
33252
- }
33253
- const paragraphDefaults = styles.elements[0].elements.filter((el) => {
33254
- return el.name === "w:style" && el.attributes["w:styleId"] === "Normal";
33255
- }) || [];
33256
- paragraphDefaults.forEach((el) => {
33257
- const rPr = el.elements.find((el2) => el2.name === "w:rPr");
33258
- const fonts = rPr?.elements?.find((el2) => el2.name === "w:rFonts");
33259
- typeface = fonts?.attributes["w:ascii"];
33260
- fontSizeNormal = Number(rPr?.elements?.find((el2) => el2.name === "w:sz")?.attributes["w:val"]) / 2;
33261
- });
33262
- const rPrDefaults = defaults?.elements?.find((el) => el.name === "w:rPrDefault");
33263
- if (rPrDefaults) {
33264
- const rPr = rPrDefaults.elements?.find((el) => el.name === "w:rPr");
33265
- const fonts = rPr?.elements?.find((el) => el.name === "w:rFonts");
33266
- if (fonts?.attributes?.["w:ascii"]) {
33267
- typeface = fonts.attributes["w:ascii"];
33268
- }
33269
- const fontSizeRaw = rPr?.elements?.find((el) => el.name === "w:sz")?.attributes?.["w:val"];
33270
- if (!fontSizeNormal && fontSizeRaw) {
33271
- fontSizeNormal = Number(fontSizeRaw) / 2;
33272
- }
33273
- }
33274
- const fallbackSz = Number(rElements.find((el) => el.name === "w:sz")?.attributes?.["w:val"]);
33275
- const fontSizePt = fontSizeNormal ?? (Number.isFinite(fallbackSz) ? fallbackSz / 2 : void 0) ?? 10;
33276
- const kern = rElements.find((el) => el.name === "w:kern")?.attributes["w:val"];
33277
- const fontFamilyCss = _SuperConverter2.toCssFontFamily(typeface, this.convertedXml);
33278
- return { fontSizePt, kern, typeface, panose, fontFamilyCss };
33279
- }
33510
+ const styleRoot = styles?.elements?.[0];
33511
+ const styleElements = styleRoot?.elements || [];
33512
+ if (!styleElements.length) return {};
33513
+ const defaults = styleElements.find((el) => el.name === "w:docDefaults");
33514
+ const normalStyle = styleElements.find((el) => el.name === "w:style" && el.attributes?.["w:styleId"] === "Normal");
33515
+ const defaultsState = {
33516
+ typeface: void 0,
33517
+ panose: void 0,
33518
+ fontSizePt: void 0,
33519
+ kern: void 0,
33520
+ fallbackSzTwips: void 0
33521
+ };
33522
+ const docDefaultRun = defaults?.elements?.find((el) => el.name === "w:rPrDefault");
33523
+ const docDefaultProps = docDefaultRun?.elements?.find((el) => el.name === "w:rPr") ?? docDefaultRun;
33524
+ collectRunDefaultProperties(docDefaultProps, {
33525
+ allowOverrideTypeface: true,
33526
+ allowOverrideSize: true,
33527
+ themeResolver: (theme) => this.getThemeInfo(theme),
33528
+ state: defaultsState
33529
+ });
33530
+ const normalRunProps = normalStyle?.elements?.find((el) => el.name === "w:rPr") ?? null;
33531
+ collectRunDefaultProperties(normalRunProps, {
33532
+ allowOverrideTypeface: true,
33533
+ allowOverrideSize: true,
33534
+ themeResolver: (theme) => this.getThemeInfo(theme),
33535
+ state: defaultsState
33536
+ });
33537
+ if (defaultsState.fontSizePt === void 0) {
33538
+ if (Number.isFinite(defaultsState.fallbackSzTwips)) defaultsState.fontSizePt = defaultsState.fallbackSzTwips / 2;
33539
+ else defaultsState.fontSizePt = DEFAULT_FONT_SIZE_PT;
33540
+ }
33541
+ const fontFamilyCss = defaultsState.typeface ? _SuperConverter2.toCssFontFamily(defaultsState.typeface, this.convertedXml) : void 0;
33542
+ const result = {};
33543
+ if (defaultsState.fontSizePt !== void 0) result.fontSizePt = defaultsState.fontSizePt;
33544
+ if (defaultsState.kern !== void 0) result.kern = defaultsState.kern;
33545
+ if (defaultsState.typeface) result.typeface = defaultsState.typeface;
33546
+ if (defaultsState.panose) result.panose = defaultsState.panose;
33547
+ if (fontFamilyCss) result.fontFamilyCss = fontFamilyCss;
33548
+ return result;
33280
33549
  }
33281
33550
  getDocumentFonts() {
33282
33551
  const fontTable = this.convertedXml["word/fontTable.xml"];
@@ -36143,14 +36412,19 @@ class DocxZipper {
36143
36412
  /**
36144
36413
  * Update [Content_Types].xml with extensions of new Image annotations
36145
36414
  */
36146
- async updateContentTypes(docx, media, fromJson) {
36415
+ async updateContentTypes(docx, media, fromJson, updatedDocs = {}) {
36416
+ const additionalPartNames = Object.keys(updatedDocs || {});
36147
36417
  const newMediaTypes = Object.keys(media).map((name) => {
36148
36418
  return this.getFileExtension(name);
36149
36419
  }).filter(Boolean);
36150
36420
  const contentTypesPath = "[Content_Types].xml";
36151
36421
  let contentTypesXml;
36152
36422
  if (fromJson) {
36153
- contentTypesXml = docx.files.find((file) => file.name === contentTypesPath)?.content || "";
36423
+ if (Array.isArray(docx.files)) {
36424
+ contentTypesXml = docx.files.find((file) => file.name === contentTypesPath)?.content || "";
36425
+ } else {
36426
+ contentTypesXml = docx.files?.[contentTypesPath] || "";
36427
+ }
36154
36428
  } else contentTypesXml = await docx.file(contentTypesPath).async("string");
36155
36429
  let typesString = "";
36156
36430
  const defaultMediaTypes = getContentTypesFromXml(contentTypesXml);
@@ -36176,24 +36450,39 @@ class DocxZipper {
36176
36450
  const hasCommentsExtensible = types2.elements?.some(
36177
36451
  (el) => el.name === "Override" && el.attributes.PartName === "/word/commentsExtensible.xml"
36178
36452
  );
36179
- if (docx.files["word/comments.xml"]) {
36453
+ const hasFile = (filename) => {
36454
+ if (!docx?.files) return false;
36455
+ if (!fromJson) return Boolean(docx.files[filename]);
36456
+ if (Array.isArray(docx.files)) return docx.files.some((file) => file.name === filename);
36457
+ return Boolean(docx.files[filename]);
36458
+ };
36459
+ if (hasFile("word/comments.xml")) {
36180
36460
  const commentsDef = `<Override PartName="/word/comments.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.comments+xml" />`;
36181
36461
  if (!hasComments) typesString += commentsDef;
36182
36462
  }
36183
- if (docx.files["word/commentsExtended.xml"]) {
36463
+ if (hasFile("word/commentsExtended.xml")) {
36184
36464
  const commentsExtendedDef = `<Override PartName="/word/commentsExtended.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.commentsExtended+xml" />`;
36185
36465
  if (!hasCommentsExtended) typesString += commentsExtendedDef;
36186
36466
  }
36187
- if (docx.files["word/commentsIds.xml"]) {
36467
+ if (hasFile("word/commentsIds.xml")) {
36188
36468
  const commentsIdsDef = `<Override PartName="/word/commentsIds.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.commentsIds+xml" />`;
36189
36469
  if (!hasCommentsIds) typesString += commentsIdsDef;
36190
36470
  }
36191
- if (docx.files["word/commentsExtensible.xml"]) {
36471
+ if (hasFile("word/commentsExtensible.xml")) {
36192
36472
  const commentsExtendedDef = `<Override PartName="/word/commentsExtensible.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.commentsExtensible+xml" />`;
36193
36473
  if (!hasCommentsExtensible) typesString += commentsExtendedDef;
36194
36474
  }
36195
- Object.keys(docx.files).forEach((name) => {
36196
- if (name.includes(".rels") || !name.includes("header") && !name.includes("footer")) return;
36475
+ const partNames = new Set(additionalPartNames);
36476
+ if (docx?.files) {
36477
+ if (fromJson && Array.isArray(docx.files)) {
36478
+ docx.files.forEach((file) => partNames.add(file.name));
36479
+ } else {
36480
+ Object.keys(docx.files).forEach((key2) => partNames.add(key2));
36481
+ }
36482
+ }
36483
+ partNames.forEach((name) => {
36484
+ if (name.includes(".rels")) return;
36485
+ if (!name.includes("header") && !name.includes("footer")) return;
36197
36486
  const hasExtensible = types2.elements?.some(
36198
36487
  (el) => el.name === "Override" && el.attributes.PartName === `/${name}`
36199
36488
  );
@@ -36204,7 +36493,48 @@ class DocxZipper {
36204
36493
  }
36205
36494
  });
36206
36495
  const beginningString = '<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">';
36207
- const updatedContentTypesXml = contentTypesXml.replace(beginningString, `${beginningString}${typesString}`);
36496
+ let updatedContentTypesXml = contentTypesXml.replace(beginningString, `${beginningString}${typesString}`);
36497
+ let relationshipsXml = updatedDocs["word/_rels/document.xml.rels"];
36498
+ if (!relationshipsXml) {
36499
+ if (fromJson) {
36500
+ if (Array.isArray(docx.files)) {
36501
+ relationshipsXml = docx.files.find((file) => file.name === "word/_rels/document.xml.rels")?.content;
36502
+ } else {
36503
+ relationshipsXml = docx.files?.["word/_rels/document.xml.rels"];
36504
+ }
36505
+ } else {
36506
+ relationshipsXml = await docx.file("word/_rels/document.xml.rels")?.async("string");
36507
+ }
36508
+ }
36509
+ if (relationshipsXml) {
36510
+ try {
36511
+ const relJson = xmljs.xml2js(relationshipsXml, { compact: false });
36512
+ const relationships = relJson.elements?.find((el) => el.name === "Relationships");
36513
+ relationships?.elements?.forEach((rel) => {
36514
+ const type2 = rel.attributes?.Type;
36515
+ const target = rel.attributes?.Target;
36516
+ if (!type2 || !target) return;
36517
+ const isHeader = type2.includes("/header");
36518
+ const isFooter = type2.includes("/footer");
36519
+ if (!isHeader && !isFooter) return;
36520
+ let sanitizedTarget = target.replace(/^\.\//, "");
36521
+ if (sanitizedTarget.startsWith("../")) sanitizedTarget = sanitizedTarget.slice(3);
36522
+ if (sanitizedTarget.startsWith("/")) sanitizedTarget = sanitizedTarget.slice(1);
36523
+ const partName = sanitizedTarget.startsWith("word/") ? sanitizedTarget : `word/${sanitizedTarget}`;
36524
+ partNames.add(partName);
36525
+ });
36526
+ } catch (error) {
36527
+ console.warn("Failed to parse document relationships while updating content types", error);
36528
+ }
36529
+ }
36530
+ partNames.forEach((name) => {
36531
+ if (name.includes(".rels")) return;
36532
+ if (!name.includes("header") && !name.includes("footer")) return;
36533
+ if (updatedContentTypesXml.includes(`PartName="/${name}"`)) return;
36534
+ const type2 = name.includes("header") ? "header" : "footer";
36535
+ const extendedDef = `<Override PartName="/${name}" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.${type2}+xml"/>`;
36536
+ updatedContentTypesXml = updatedContentTypesXml.replace("</Types>", `${extendedDef}</Types>`);
36537
+ });
36208
36538
  if (fromJson) return updatedContentTypesXml;
36209
36539
  docx.file(contentTypesPath, updatedContentTypesXml);
36210
36540
  }
@@ -36245,7 +36575,7 @@ class DocxZipper {
36245
36575
  for (const [fontName, fontUintArray] of Object.entries(fonts)) {
36246
36576
  zip.file(fontName, fontUintArray);
36247
36577
  }
36248
- await this.updateContentTypes(zip, media);
36578
+ await this.updateContentTypes(zip, media, false, updatedDocs);
36249
36579
  return zip;
36250
36580
  }
36251
36581
  /**
@@ -36271,7 +36601,7 @@ class DocxZipper {
36271
36601
  Object.keys(media).forEach((path) => {
36272
36602
  unzippedOriginalDocx.file(path, media[path]);
36273
36603
  });
36274
- await this.updateContentTypes(unzippedOriginalDocx, media);
36604
+ await this.updateContentTypes(unzippedOriginalDocx, media, false, updatedDocs);
36275
36605
  return unzippedOriginalDocx;
36276
36606
  }
36277
36607
  }
@@ -36286,7 +36616,7 @@ var __privateGet$1 = (obj, member, getter) => (__accessCheck$1(obj, member, "rea
36286
36616
  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);
36287
36617
  var __privateSet = (obj, member, value, setter) => (__accessCheck$1(obj, member, "write to private field"), member.set(obj, value), value);
36288
36618
  var __privateMethod$1 = (obj, member, method) => (__accessCheck$1(obj, member, "access private method"), method);
36289
- 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;
36619
+ 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;
36290
36620
  var GOOD_LEAF_SIZE = 200;
36291
36621
  var RopeSequence = function RopeSequence2() {
36292
36622
  };
@@ -47929,9 +48259,11 @@ const toggleHeaderFooterEditMode = ({ editor, focusedSectionEditor, isEditMode,
47929
48259
  item.editor.view.dom.setAttribute("documentmode", documentMode);
47930
48260
  });
47931
48261
  if (isEditMode) {
47932
- const pm = document.querySelector(".ProseMirror");
47933
- pm.classList.add("header-footer-edit");
47934
- pm.setAttribute("aria-readonly", true);
48262
+ const pm = editor.view?.dom || editor.options.element?.querySelector?.(".ProseMirror");
48263
+ if (pm) {
48264
+ pm.classList.add("header-footer-edit");
48265
+ pm.setAttribute("aria-readonly", true);
48266
+ }
47935
48267
  }
47936
48268
  if (focusedSectionEditor) {
47937
48269
  focusedSectionEditor.view.focus();
@@ -48359,28 +48691,25 @@ const handleTrackedChangeTransaction = (trackedChangeMeta, trackedChanges, newEd
48359
48691
  if (emitParams) editor.emit("commentsUpdate", emitParams);
48360
48692
  return newTrackedChanges;
48361
48693
  };
48362
- const getTrackedChangeText = ({ state: state2, node, mark, marks, trackedChangeType, isDeletionInsertion }) => {
48694
+ const getTrackedChangeText = ({ state: state2, nodes, mark, marks, trackedChangeType, isDeletionInsertion }) => {
48363
48695
  let trackedChangeText = "";
48364
48696
  let deletionText = "";
48365
48697
  if (trackedChangeType === TrackInsertMarkName) {
48366
- trackedChangeText = node?.text ?? "";
48698
+ trackedChangeText = nodes.reduce((acc, node) => {
48699
+ if (!node.marks.find((nodeMark) => nodeMark.type.name === mark.type.name)) return acc;
48700
+ acc += node?.text || node?.textContent || "";
48701
+ return acc;
48702
+ }, "");
48367
48703
  }
48368
48704
  if (trackedChangeType === TrackFormatMarkName) {
48369
48705
  trackedChangeText = translateFormatChangesToEnglish(mark.attrs);
48370
48706
  }
48371
48707
  if (trackedChangeType === TrackDeleteMarkName || isDeletionInsertion) {
48372
- deletionText = node?.text ?? "";
48373
- if (isDeletionInsertion) {
48374
- let { id } = marks.deletionMark.attrs;
48375
- let deletionNode = findNode$1(state2.doc, (node2) => {
48376
- const { marks: marks2 = [] } = node2;
48377
- const changeMarks = marks2.filter((mark2) => TRACK_CHANGE_MARKS.includes(mark2.type.name));
48378
- if (!changeMarks.length) return false;
48379
- const hasMatchingId = changeMarks.find((mark2) => mark2.attrs.id === id);
48380
- if (hasMatchingId) return true;
48381
- });
48382
- deletionText = deletionNode?.node.text ?? "";
48383
- }
48708
+ deletionText = nodes.reduce((acc, node) => {
48709
+ if (!node.marks.find((nodeMark) => nodeMark.type.name === TrackDeleteMarkName)) return acc;
48710
+ acc += node?.text || node?.textContent || "";
48711
+ return acc;
48712
+ }, "");
48384
48713
  }
48385
48714
  return {
48386
48715
  deletionText,
@@ -48395,18 +48724,17 @@ const createOrUpdateTrackedChangeComment = ({ event, marks, deletionNodes, nodes
48395
48724
  const id = attrs.id;
48396
48725
  const node = nodes[0];
48397
48726
  const isDeletionInsertion = !!(marks.insertedMark && marks.deletionMark);
48398
- let existingNode;
48727
+ let nodesWithMark = [];
48399
48728
  newEditorState.doc.descendants((node2) => {
48400
48729
  const { marks: marks2 = [] } = node2;
48401
48730
  const changeMarks = marks2.filter((mark) => TRACK_CHANGE_MARKS.includes(mark.type.name));
48402
48731
  if (!changeMarks.length) return;
48403
48732
  const hasMatchingId = changeMarks.find((mark) => mark.attrs.id === id);
48404
- if (hasMatchingId) existingNode = node2;
48405
- if (existingNode) return false;
48733
+ if (hasMatchingId) nodesWithMark.push(node2);
48406
48734
  });
48407
48735
  const { deletionText, trackedChangeText } = getTrackedChangeText({
48408
48736
  state: newEditorState,
48409
- node: existingNode || node,
48737
+ nodes: nodesWithMark.length ? nodesWithMark : [node],
48410
48738
  mark: trackedMark,
48411
48739
  marks,
48412
48740
  trackedChangeType,
@@ -48436,14 +48764,6 @@ const createOrUpdateTrackedChangeComment = ({ event, marks, deletionNodes, nodes
48436
48764
  else if (event === "update") params2.event = comments_module_events.UPDATE;
48437
48765
  return params2;
48438
48766
  };
48439
- function findNode$1(node, predicate) {
48440
- let found2 = null;
48441
- node.descendants((node2, pos) => {
48442
- if (predicate(node2)) found2 = { node: node2, pos };
48443
- if (found2) return false;
48444
- });
48445
- return found2;
48446
- }
48447
48767
  function findRangeById(doc2, id) {
48448
48768
  let from2 = null, to = null;
48449
48769
  doc2.descendants((node, pos) => {
@@ -48961,6 +49281,7 @@ const generateTableIfNecessary = ({ tableNode, annotationValues, tr, state: stat
48961
49281
  const mappedRowStart = tr.mapping.map(absoluteRowStart);
48962
49282
  const rowEnd = mappedRowStart + rowNode.nodeSize;
48963
49283
  tr.replaceWith(mappedRowStart, rowEnd, Fragment.from(newRows));
49284
+ tr.setMeta("tableGeneration", true);
48964
49285
  } catch (error) {
48965
49286
  console.error("Error during row generation:", error);
48966
49287
  throw error;
@@ -49365,7 +49686,7 @@ function findFieldAnnotationsBetween(from2, to, doc2) {
49365
49686
  }
49366
49687
  function findRemovedFieldAnnotations(tr) {
49367
49688
  let removedNodes = [];
49368
- 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) {
49689
+ 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) {
49369
49690
  return removedNodes;
49370
49691
  }
49371
49692
  const hasDeletion = transactionDeletedAnything(tr);
@@ -50543,7 +50864,7 @@ const _Editor = class _Editor2 extends EventEmitter$1 {
50543
50864
  setDocumentMode(documentMode) {
50544
50865
  let cleanedMode = documentMode?.toLowerCase() || "editing";
50545
50866
  if (!this.extensionService || !this.state) return;
50546
- const pm = document.querySelector(".ProseMirror");
50867
+ const pm = this.view?.dom || this.options.element?.querySelector?.(".ProseMirror");
50547
50868
  if (this.options.role === "viewer") cleanedMode = "viewing";
50548
50869
  if (this.options.role === "suggester" && cleanedMode === "editing") cleanedMode = "suggesting";
50549
50870
  if (cleanedMode === "viewing") {
@@ -51031,7 +51352,8 @@ const _Editor = class _Editor2 extends EventEmitter$1 {
51031
51352
  files: this.options.content
51032
51353
  },
51033
51354
  media,
51034
- true
51355
+ true,
51356
+ updatedDocs
51035
51357
  );
51036
51358
  return updatedDocs;
51037
51359
  }
@@ -51517,9 +51839,11 @@ createView_fn = function(element) {
51517
51839
  isEditMode: false,
51518
51840
  documentMode: this.options.documentMode
51519
51841
  });
51520
- const pm = document.querySelector(".ProseMirror");
51521
- pm.classList.remove("header-footer-edit");
51522
- pm.setAttribute("aria-readonly", false);
51842
+ const pm = this.view?.dom || this.options.element?.querySelector?.(".ProseMirror");
51843
+ if (pm) {
51844
+ pm.classList.remove("header-footer-edit");
51845
+ pm.setAttribute("aria-readonly", false);
51846
+ }
51523
51847
  }
51524
51848
  setWordSelection(view, pos);
51525
51849
  }
@@ -53067,522 +53391,1536 @@ const SlashMenu = Extension.create({
53067
53391
  return this.editor.options.isHeadless ? [] : [slashMenuPlugin];
53068
53392
  }
53069
53393
  });
53070
- const Document = Node$1.create({
53071
- name: "doc",
53072
- topNode: true,
53073
- content: "block+",
53074
- parseDOM() {
53075
- return [{ tag: "doc" }];
53076
- },
53077
- renderDOM() {
53078
- return ["doc", 0];
53079
- },
53080
- addAttributes() {
53081
- return {
53082
- attributes: {
53083
- rendered: false,
53084
- "aria-label": "Document node"
53085
- }
53086
- };
53087
- },
53088
- addCommands() {
53089
- return {
53090
- /**
53091
- * Get document statistics
53092
- * @category Command
53093
- * @example
53094
- * // Get word and character count
53095
- * const stats = editor.commands.getDocumentStats()
53096
- * console.log(`${stats.words} words, ${stats.characters} characters`)
53097
- * @note Returns word count, character count, and paragraph count
53098
- */
53099
- getDocumentStats: () => ({ editor }) => {
53100
- const text = editor.getText();
53101
- const words = text.split(/\s+/).filter((word) => word.length > 0).length;
53102
- const characters = text.length;
53103
- const paragraphs = editor.state.doc.content.childCount;
53104
- return {
53105
- words,
53106
- characters,
53107
- paragraphs
53108
- };
53109
- },
53110
- /**
53111
- * Clear entire document
53112
- * @category Command
53113
- * @example
53114
- * editor.commands.clearDocument()
53115
- * @note Replaces all content with an empty paragraph
53116
- */
53117
- clearDocument: () => ({ commands: commands2 }) => {
53118
- return commands2.setContent("<p></p>");
53394
+ class StructuredContentViewBase {
53395
+ constructor(props) {
53396
+ __publicField$1(this, "node");
53397
+ __publicField$1(this, "view");
53398
+ __publicField$1(this, "getPos");
53399
+ __publicField$1(this, "decorations");
53400
+ __publicField$1(this, "innerDecorations");
53401
+ __publicField$1(this, "editor");
53402
+ __publicField$1(this, "extension");
53403
+ __publicField$1(this, "htmlAttributes");
53404
+ __publicField$1(this, "root");
53405
+ __publicField$1(this, "isDragging", false);
53406
+ this.node = props.node;
53407
+ this.view = props.editor.view;
53408
+ this.getPos = props.getPos;
53409
+ this.decorations = props.decorations;
53410
+ this.innerDecorations = props.innerDecorations;
53411
+ this.editor = props.editor;
53412
+ this.extension = props.extension;
53413
+ this.htmlAttributes = props.htmlAttributes;
53414
+ this.mount(props);
53415
+ }
53416
+ mount() {
53417
+ return;
53418
+ }
53419
+ get dom() {
53420
+ return this.root;
53421
+ }
53422
+ get contentDOM() {
53423
+ return null;
53424
+ }
53425
+ update(node, decorations, innerDecorations) {
53426
+ if (node.type !== this.node.type) {
53427
+ return false;
53428
+ }
53429
+ this.node = node;
53430
+ this.decorations = decorations;
53431
+ this.innerDecorations = innerDecorations;
53432
+ this.updateHTMLAttributes();
53433
+ return true;
53434
+ }
53435
+ stopEvent(event) {
53436
+ if (!this.dom) return false;
53437
+ const target = event.target;
53438
+ const isInElement = this.dom.contains(target) && !this.contentDOM?.contains(target);
53439
+ if (!isInElement) return false;
53440
+ const isDragEvent = event.type.startsWith("drag");
53441
+ const isDropEvent = event.type === "drop";
53442
+ const isInput = ["INPUT", "BUTTON", "SELECT", "TEXTAREA"].includes(target.tagName) || target.isContentEditable;
53443
+ if (isInput && !isDropEvent && !isDragEvent) return true;
53444
+ const { isEditable } = this.editor;
53445
+ const { isDragging } = this;
53446
+ const isDraggable = !!this.node.type.spec.draggable;
53447
+ const isSelectable = NodeSelection.isSelectable(this.node);
53448
+ const isCopyEvent = event.type === "copy";
53449
+ const isPasteEvent = event.type === "paste";
53450
+ const isCutEvent = event.type === "cut";
53451
+ const isClickEvent = event.type === "mousedown";
53452
+ if (!isDraggable && isSelectable && isDragEvent && event.target === this.dom) {
53453
+ event.preventDefault();
53454
+ }
53455
+ if (isDraggable && isDragEvent && !isDragging && event.target === this.dom) {
53456
+ event.preventDefault();
53457
+ return false;
53458
+ }
53459
+ if (isDraggable && isEditable && !isDragging && isClickEvent) {
53460
+ const dragHandle = target.closest("[data-drag-handle]");
53461
+ const isValidDragHandle = dragHandle && (this.dom === dragHandle || this.dom.contains(dragHandle));
53462
+ if (isValidDragHandle) {
53463
+ this.isDragging = true;
53464
+ document.addEventListener(
53465
+ "dragend",
53466
+ () => {
53467
+ this.isDragging = false;
53468
+ },
53469
+ { once: true }
53470
+ );
53471
+ document.addEventListener(
53472
+ "drop",
53473
+ () => {
53474
+ this.isDragging = false;
53475
+ },
53476
+ { once: true }
53477
+ );
53478
+ document.addEventListener(
53479
+ "mouseup",
53480
+ () => {
53481
+ this.isDragging = false;
53482
+ },
53483
+ { once: true }
53484
+ );
53119
53485
  }
53120
- };
53486
+ }
53487
+ if (isDragging || isDropEvent || isCopyEvent || isPasteEvent || isCutEvent || isClickEvent && isSelectable) {
53488
+ return false;
53489
+ }
53490
+ return true;
53121
53491
  }
53122
- });
53123
- const Text = Node$1.create({
53124
- name: "text",
53125
- group: "inline",
53126
- inline: true,
53127
- addOptions() {
53128
- return {};
53492
+ ignoreMutation(mutation) {
53493
+ if (!this.dom || !this.contentDOM) return true;
53494
+ if (this.node.isLeaf || this.node.isAtom) return true;
53495
+ if (mutation.type === "selection") return false;
53496
+ if (this.contentDOM === mutation.target && mutation.type === "attributes") return true;
53497
+ if (this.contentDOM.contains(mutation.target)) return false;
53498
+ return true;
53129
53499
  }
53130
- });
53131
- const splitRun = () => (props) => {
53132
- const { state: state2, view, tr } = props;
53133
- const { $from, empty: empty2 } = state2.selection;
53134
- if (!empty2) return false;
53135
- if ($from.parent.type.name !== "run") return false;
53136
- const handled = splitBlock(state2, (transaction) => {
53500
+ destroy() {
53501
+ this.dom.remove();
53502
+ this.contentDOM?.remove();
53503
+ }
53504
+ updateAttributes(attrs) {
53505
+ const pos = this.getPos();
53506
+ if (typeof pos !== "number") {
53507
+ return;
53508
+ }
53509
+ return this.view.dispatch(
53510
+ this.view.state.tr.setNodeMarkup(pos, void 0, {
53511
+ ...this.node.attrs,
53512
+ ...attrs
53513
+ })
53514
+ );
53515
+ }
53516
+ updateHTMLAttributes() {
53517
+ const { extensionService } = this.editor;
53518
+ const { attributes } = extensionService;
53519
+ const extensionAttrs = attributes.filter((i) => i.type === this.node.type.name);
53520
+ this.htmlAttributes = Attribute2.getAttributesToRender(this.node, extensionAttrs);
53521
+ }
53522
+ createDragHandle() {
53523
+ const dragHandle = document.createElement("span");
53524
+ dragHandle.classList.add("sd-structured-content-draggable");
53525
+ dragHandle.draggable = true;
53526
+ dragHandle.contentEditable = "false";
53527
+ dragHandle.dataset.dragHandle = "";
53528
+ const textElement = document.createElement("span");
53529
+ textElement.textContent = this.node.attrs.alias || "Structured content";
53530
+ dragHandle.append(textElement);
53531
+ return dragHandle;
53532
+ }
53533
+ onDragStart(event) {
53534
+ const { view } = this.editor;
53535
+ const target = event.target;
53536
+ const dragHandle = target.nodeType === 3 ? target.parentElement?.closest("[data-drag-handle]") : target.closest("[data-drag-handle]");
53537
+ if (!this.dom || this.contentDOM?.contains(target) || !dragHandle) {
53538
+ return;
53539
+ }
53540
+ let x = 0;
53541
+ let y2 = 0;
53542
+ if (this.dom !== dragHandle) {
53543
+ const domBox = this.dom.getBoundingClientRect();
53544
+ const handleBox = dragHandle.getBoundingClientRect();
53545
+ const offsetX = event.offsetX ?? event.nativeEvent?.offsetX;
53546
+ const offsetY = event.offsetY ?? event.nativeEvent?.offsetY;
53547
+ x = handleBox.x - domBox.x + offsetX;
53548
+ y2 = handleBox.y - domBox.y + offsetY;
53549
+ }
53550
+ event.dataTransfer?.setDragImage(this.dom, x, y2);
53551
+ const pos = this.getPos();
53552
+ if (typeof pos !== "number") {
53553
+ return;
53554
+ }
53555
+ const selection = NodeSelection.create(view.state.doc, pos);
53556
+ const transaction = view.state.tr.setSelection(selection);
53137
53557
  view.dispatch(transaction);
53138
- });
53139
- if (handled) {
53140
- tr.setMeta("preventDispatch", true);
53141
53558
  }
53142
- return handled;
53143
- };
53144
- const Run = OxmlNode.create({
53145
- name: "run",
53146
- oXmlName: "w:r",
53147
- group: "inline",
53559
+ }
53560
+ class StructuredContentInlineView extends StructuredContentViewBase {
53561
+ constructor(props) {
53562
+ super(props);
53563
+ }
53564
+ mount() {
53565
+ this.buildView();
53566
+ }
53567
+ get contentDOM() {
53568
+ const contentElement = this.dom?.querySelector(`.${structuredContentInnerClass$1}`);
53569
+ return contentElement || null;
53570
+ }
53571
+ createElement() {
53572
+ const element = document.createElement("span");
53573
+ element.classList.add(structuredContentClass$1);
53574
+ element.setAttribute("data-structured-content", "");
53575
+ const contentElement = document.createElement("span");
53576
+ contentElement.classList.add(structuredContentInnerClass$1);
53577
+ element.append(contentElement);
53578
+ const domAttrs = Attribute2.mergeAttributes(this.htmlAttributes);
53579
+ updateDOMAttributes(element, { ...domAttrs });
53580
+ return { element, contentElement };
53581
+ }
53582
+ buildView() {
53583
+ const { element } = this.createElement();
53584
+ const dragHandle = this.createDragHandle();
53585
+ element.prepend(dragHandle);
53586
+ element.addEventListener("dragstart", (e) => this.onDragStart(e));
53587
+ this.root = element;
53588
+ }
53589
+ updateView() {
53590
+ const domAttrs = Attribute2.mergeAttributes(this.htmlAttributes);
53591
+ updateDOMAttributes(this.dom, { ...domAttrs });
53592
+ }
53593
+ update(node, decorations, innerDecorations) {
53594
+ const result = super.update(node, decorations, innerDecorations);
53595
+ if (!result) return false;
53596
+ this.updateView();
53597
+ return true;
53598
+ }
53599
+ }
53600
+ const structuredContentClass$1 = "sd-structured-content";
53601
+ const structuredContentInnerClass$1 = "sd-structured-content__content";
53602
+ const StructuredContent = Node$1.create({
53603
+ name: "structuredContent",
53604
+ group: "inline structuredContent",
53148
53605
  inline: true,
53149
53606
  content: "inline*",
53150
- selectable: false,
53151
- childToAttributes: ["runProperties"],
53607
+ isolating: true,
53608
+ atom: false,
53609
+ // false - has editable content.
53610
+ draggable: true,
53152
53611
  addOptions() {
53153
53612
  return {
53154
53613
  htmlAttributes: {
53155
- "data-run": "1"
53614
+ class: structuredContentClass$1,
53615
+ "aria-label": "Structured content node"
53156
53616
  }
53157
53617
  };
53158
53618
  },
53159
53619
  addAttributes() {
53160
53620
  return {
53161
- runProperties: {
53621
+ id: {
53162
53622
  default: null,
53163
- rendered: false,
53164
- keepOnSplit: true
53623
+ parseDOM: (elem) => elem.getAttribute("data-id"),
53624
+ renderDOM: (attrs) => {
53625
+ if (!attrs.id) return {};
53626
+ return { "data-id": attrs.id };
53627
+ }
53165
53628
  },
53166
- rsidR: {
53629
+ tag: {
53167
53630
  default: null,
53168
- rendered: false,
53169
- keepOnSplit: true
53631
+ parseDOM: (elem) => elem.getAttribute("data-tag"),
53632
+ renderDOM: (attrs) => {
53633
+ if (!attrs.tag) return {};
53634
+ return { "data-tag": attrs.tag };
53635
+ }
53170
53636
  },
53171
- rsidRPr: {
53637
+ alias: {
53172
53638
  default: null,
53173
- rendered: false,
53174
- keepOnSplit: true
53639
+ parseDOM: (elem) => elem.getAttribute("data-alias"),
53640
+ renderDOM: (attrs) => {
53641
+ if (!attrs.alias) return {};
53642
+ return { "data-alias": attrs.alias };
53643
+ }
53175
53644
  },
53176
- rsidDel: {
53177
- default: null,
53178
- rendered: false,
53179
- keepOnSplit: true
53645
+ sdtPr: {
53646
+ rendered: false
53180
53647
  }
53181
53648
  };
53182
53649
  },
53183
- addCommands() {
53184
- return {
53185
- splitRun
53186
- };
53187
- },
53188
53650
  parseDOM() {
53189
- return [{ tag: "span[data-run]" }];
53651
+ return [{ tag: "span[data-structured-content]" }];
53190
53652
  },
53191
53653
  renderDOM({ htmlAttributes }) {
53192
- const base2 = Attribute2.mergeAttributes(this.options.htmlAttributes, htmlAttributes);
53193
- return ["span", base2, 0];
53194
- }
53195
- });
53196
- const inputRegex$1 = /^\s*([-+*])\s$/;
53197
- const BulletList = Node$1.create({
53198
- name: "bulletList",
53199
- group: "block list",
53200
- selectable: false,
53201
- content() {
53202
- return `${this.options.itemTypeName}+`;
53654
+ return [
53655
+ "span",
53656
+ Attribute2.mergeAttributes(this.options.htmlAttributes, htmlAttributes, {
53657
+ "data-structured-content": ""
53658
+ }),
53659
+ 0
53660
+ ];
53203
53661
  },
53204
- addOptions() {
53205
- return {
53206
- itemTypeName: "listItem",
53207
- htmlAttributes: {
53208
- "aria-label": "Bullet list node"
53209
- },
53210
- keepMarks: true,
53211
- keepAttributes: false
53662
+ addNodeView() {
53663
+ return (props) => {
53664
+ return new StructuredContentInlineView({ ...props });
53212
53665
  };
53213
- },
53214
- parseDOM() {
53215
- return [{ tag: "ul" }];
53216
- },
53217
- renderDOM({ htmlAttributes }) {
53218
- const attributes = Attribute2.mergeAttributes(this.options.htmlAttributes, htmlAttributes);
53219
- return ["ul", attributes, 0];
53220
- },
53221
- addAttributes() {
53222
- return {
53223
- "list-style-type": {
53224
- default: "bullet",
53225
- rendered: false
53226
- },
53227
- listId: {
53228
- rendered: false
53229
- },
53230
- sdBlockId: {
53231
- default: null,
53232
- keepOnSplit: false,
53233
- parseDOM: (elem) => elem.getAttribute("data-sd-block-id"),
53234
- renderDOM: (attrs) => {
53235
- return attrs.sdBlockId ? { "data-sd-block-id": attrs.sdBlockId } : {};
53236
- }
53237
- },
53238
- attributes: {
53239
- rendered: false,
53240
- keepOnSplit: true
53241
- }
53242
- };
53243
- },
53244
- addCommands() {
53245
- return {
53246
- /**
53247
- * Toggle a bullet list at the current selection
53248
- * @category Command
53249
- * @example
53250
- * // Toggle bullet list on selected text
53251
- * editor.commands.toggleBulletList()
53252
- * @note Converts selected paragraphs to list items or removes list formatting
53253
- */
53254
- toggleBulletList: () => (params2) => {
53255
- return toggleList(this.type)(params2);
53256
- }
53257
- };
53258
- },
53259
- addShortcuts() {
53260
- return {
53261
- "Mod-Shift-8": () => {
53262
- return this.editor.commands.toggleBulletList();
53263
- }
53264
- };
53265
- },
53266
- addInputRules() {
53267
- return [
53268
- new InputRule({
53269
- match: inputRegex$1,
53270
- handler: ({ state: state2, range: range2 }) => {
53271
- const $pos = state2.selection.$from;
53272
- const listItemType = state2.schema.nodes.listItem;
53273
- for (let depth = $pos.depth; depth >= 0; depth--) {
53274
- if ($pos.node(depth).type === listItemType) {
53275
- return null;
53276
- }
53277
- }
53278
- const { tr } = state2;
53279
- tr.delete(range2.from, range2.to);
53280
- ListHelpers.createNewList({
53281
- listType: this.type,
53282
- tr,
53283
- editor: this.editor
53284
- });
53285
- }
53286
- })
53287
- ];
53288
53666
  }
53289
53667
  });
53290
- const inputRegex = /^(\d+)\.\s$/;
53291
- const OrderedList = Node$1.create({
53292
- name: "orderedList",
53293
- group: "block list",
53294
- selectable: false,
53295
- content() {
53296
- return `${this.options.itemTypeName}+`;
53297
- },
53668
+ class StructuredContentBlockView extends StructuredContentViewBase {
53669
+ constructor(props) {
53670
+ super(props);
53671
+ }
53672
+ mount() {
53673
+ this.buildView();
53674
+ }
53675
+ get contentDOM() {
53676
+ const contentElement = this.dom?.querySelector(`.${structuredContentInnerClass}`);
53677
+ return contentElement || null;
53678
+ }
53679
+ createElement() {
53680
+ const element = document.createElement("div");
53681
+ element.classList.add(structuredContentClass);
53682
+ element.setAttribute("data-structured-content-block", "");
53683
+ const contentElement = document.createElement("div");
53684
+ contentElement.classList.add(structuredContentInnerClass);
53685
+ element.append(contentElement);
53686
+ const domAttrs = Attribute2.mergeAttributes(this.htmlAttributes);
53687
+ updateDOMAttributes(element, { ...domAttrs });
53688
+ return { element, contentElement };
53689
+ }
53690
+ buildView() {
53691
+ const { element } = this.createElement();
53692
+ const dragHandle = this.createDragHandle();
53693
+ element.prepend(dragHandle);
53694
+ element.addEventListener("dragstart", (e) => this.onDragStart(e));
53695
+ this.root = element;
53696
+ }
53697
+ updateView() {
53698
+ const domAttrs = Attribute2.mergeAttributes(this.htmlAttributes);
53699
+ updateDOMAttributes(this.dom, { ...domAttrs });
53700
+ }
53701
+ update(node, decorations, innerDecorations) {
53702
+ const result = super.update(node, decorations, innerDecorations);
53703
+ if (!result) return false;
53704
+ this.updateView();
53705
+ return true;
53706
+ }
53707
+ }
53708
+ const structuredContentClass = "sd-structured-content-block";
53709
+ const structuredContentInnerClass = "sd-structured-content-block__content";
53710
+ const StructuredContentBlock = Node$1.create({
53711
+ name: "structuredContentBlock",
53712
+ group: "block structuredContent",
53713
+ content: "block*",
53714
+ isolating: true,
53715
+ atom: false,
53716
+ // false - has editable content.
53717
+ draggable: true,
53298
53718
  addOptions() {
53299
53719
  return {
53300
- itemTypeName: "listItem",
53301
53720
  htmlAttributes: {
53302
- "aria-label": "Ordered list node"
53303
- },
53304
- keepMarks: true,
53305
- keepAttributes: false,
53306
- listStyleTypes: ["decimal", "lowerAlpha", "lowerRoman"]
53721
+ class: structuredContentClass,
53722
+ "aria-label": "Structured content block node"
53723
+ }
53307
53724
  };
53308
53725
  },
53309
53726
  addAttributes() {
53310
53727
  return {
53311
- order: {
53312
- default: 1,
53313
- parseDOM: (element) => {
53314
- return element.hasAttribute("start") ? parseInt(element.getAttribute("start") || "", 10) : 1;
53315
- },
53316
- renderDOM: (attrs) => {
53317
- return {
53318
- start: attrs.order
53319
- };
53320
- }
53321
- },
53322
- sdBlockId: {
53728
+ id: {
53323
53729
  default: null,
53324
- keepOnSplit: false,
53325
- parseDOM: (elem) => elem.getAttribute("data-sd-block-id"),
53730
+ parseDOM: (elem) => elem.getAttribute("data-id"),
53326
53731
  renderDOM: (attrs) => {
53327
- return attrs.sdBlockId ? { "data-sd-block-id": attrs.sdBlockId } : {};
53732
+ if (!attrs.id) return {};
53733
+ return { "data-id": attrs.id };
53328
53734
  }
53329
53735
  },
53330
- syncId: {
53736
+ tag: {
53331
53737
  default: null,
53332
- parseDOM: (elem) => elem.getAttribute("data-sync-id"),
53738
+ parseDOM: (elem) => elem.getAttribute("data-tag"),
53333
53739
  renderDOM: (attrs) => {
53334
- if (!attrs.syncId) return {};
53335
- return {
53336
- "data-sync-id": attrs.syncId
53337
- };
53740
+ if (!attrs.tag) return {};
53741
+ return { "data-tag": attrs.tag };
53338
53742
  }
53339
- // rendered: false,
53340
53743
  },
53341
- listId: {
53342
- keepOnSplit: true,
53343
- parseDOM: (elem) => elem.getAttribute("data-list-id"),
53744
+ alias: {
53745
+ default: null,
53746
+ parseDOM: (elem) => elem.getAttribute("data-alias"),
53344
53747
  renderDOM: (attrs) => {
53345
- if (!attrs.listId) return {};
53346
- return {
53347
- "data-list-id": attrs.listId
53348
- };
53748
+ if (!attrs.alias) return {};
53749
+ return { "data-alias": attrs.alias };
53349
53750
  }
53350
53751
  },
53351
- "list-style-type": {
53352
- default: "decimal",
53752
+ sdtPr: {
53353
53753
  rendered: false
53354
- },
53355
- attributes: {
53356
- rendered: false,
53357
- keepOnSplit: true
53358
53754
  }
53359
53755
  };
53360
53756
  },
53361
53757
  parseDOM() {
53362
- return [{ tag: "ol" }];
53758
+ return [{ tag: "div[data-structured-content-block]" }];
53363
53759
  },
53364
53760
  renderDOM({ htmlAttributes }) {
53365
- const { start: start2, ...restAttributes } = htmlAttributes;
53366
- return start2 === 1 ? ["ol", Attribute2.mergeAttributes(this.options.htmlAttributes, restAttributes), 0] : ["ol", Attribute2.mergeAttributes(this.options.htmlAttributes, htmlAttributes), 0];
53761
+ return [
53762
+ "div",
53763
+ Attribute2.mergeAttributes(this.options.htmlAttributes, htmlAttributes, {
53764
+ "data-structured-content-block": ""
53765
+ }),
53766
+ 0
53767
+ ];
53367
53768
  },
53769
+ addNodeView() {
53770
+ return (props) => {
53771
+ return new StructuredContentBlockView({ ...props });
53772
+ };
53773
+ }
53774
+ });
53775
+ function getStructuredContentTagsById(idOrIds, state2) {
53776
+ const result = findChildren$5(state2.doc, (node) => {
53777
+ const isStructuredContent = ["structuredContent", "structuredContentBlock"].includes(node.type.name);
53778
+ if (Array.isArray(idOrIds)) {
53779
+ return isStructuredContent && idOrIds.includes(node.attrs.id);
53780
+ } else {
53781
+ return isStructuredContent && node.attrs.id === idOrIds;
53782
+ }
53783
+ });
53784
+ return result;
53785
+ }
53786
+ function getStructuredContentTags(state2) {
53787
+ const result = findChildren$5(state2.doc, (node) => {
53788
+ return node.type.name === "structuredContent" || node.type.name === "structuredContentBlock";
53789
+ });
53790
+ return result;
53791
+ }
53792
+ function getStructuredContentInlineTags(state2) {
53793
+ const result = findChildren$5(state2.doc, (node) => node.type.name === "structuredContent");
53794
+ return result;
53795
+ }
53796
+ function getStructuredContentBlockTags(state2) {
53797
+ const result = findChildren$5(state2.doc, (node) => node.type.name === "structuredContentBlock");
53798
+ return result;
53799
+ }
53800
+ const structuredContentHelpers = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
53801
+ __proto__: null,
53802
+ getStructuredContentBlockTags,
53803
+ getStructuredContentInlineTags,
53804
+ getStructuredContentTags,
53805
+ getStructuredContentTagsById
53806
+ }, Symbol.toStringTag, { value: "Module" }));
53807
+ const STRUCTURED_CONTENT_NAMES = ["structuredContent", "structuredContentBlock"];
53808
+ const StructuredContentCommands = Extension.create({
53809
+ name: "structuredContentCommands",
53368
53810
  addCommands() {
53369
53811
  return {
53370
53812
  /**
53371
- * Toggle ordered list formatting
53813
+ * Inserts a structured content inline at selection.
53372
53814
  * @category Command
53373
- * @example
53374
- * editor.commands.toggleOrderedList()
53375
- * @note Converts selection to ordered list or back to paragraphs
53815
+ * @param {StructuredContentInlineInsert} options
53376
53816
  */
53377
- toggleOrderedList: () => (params2) => {
53378
- return toggleList(this.type)(params2);
53817
+ insertStructuredContentInline: (options = {}) => ({ editor, dispatch, state: state2, tr }) => {
53818
+ const { schema } = editor;
53819
+ let { from: from2, to } = state2.selection;
53820
+ if (dispatch) {
53821
+ const selectionText = state2.doc.textBetween(from2, to);
53822
+ let content = null;
53823
+ if (selectionText) {
53824
+ content = schema.text(selectionText);
53825
+ }
53826
+ if (options.text) {
53827
+ content = schema.text(options.text);
53828
+ }
53829
+ if (options.json) {
53830
+ content = schema.nodeFromJSON(options.json);
53831
+ }
53832
+ if (!content) {
53833
+ content = schema.text(" ");
53834
+ }
53835
+ const attrs = {
53836
+ ...options.attrs,
53837
+ id: options.attrs?.id || randomId(),
53838
+ tag: "inline_text_sdt",
53839
+ alias: options.attrs?.alias || "Structured content"
53840
+ };
53841
+ const node = schema.nodes.structuredContent.create(attrs, content, null);
53842
+ const parent = findParentNode((node2) => node2.type.name === "structuredContent")(state2.selection);
53843
+ if (parent) {
53844
+ const insertPos = parent.pos + parent.node.nodeSize;
53845
+ from2 = to = insertPos;
53846
+ }
53847
+ tr.replaceWith(from2, to, node);
53848
+ }
53849
+ return true;
53379
53850
  },
53380
53851
  /**
53381
- * Restart list node numbering
53852
+ * Inserts a structured content block at selection.
53382
53853
  * @category Command
53383
- * @param {Array} followingNodes - Nodes to restart
53384
- * @param {number} pos - Starting position
53385
- * @example
53386
- * editor.commands.restartListNodes(nodes, position)
53387
- * @note Resets list numbering for specified nodes
53854
+ * @param {StructuredContentBlockInsert} options
53388
53855
  */
53389
- restartListNodes: (followingNodes, pos) => ({ tr }) => {
53390
- let currentNodePos = pos;
53391
- const nodes = followingNodes.map((node) => {
53392
- const resultNode = {
53393
- node,
53394
- pos: currentNodePos
53856
+ insertStructuredContentBlock: (options = {}) => ({ editor, dispatch, state: state2, tr }) => {
53857
+ const { schema } = editor;
53858
+ let { from: from2, to } = state2.selection;
53859
+ if (dispatch) {
53860
+ const selectionContent = state2.selection.content();
53861
+ let content = null;
53862
+ if (selectionContent.size) {
53863
+ content = selectionContent.content;
53864
+ }
53865
+ if (options.html) {
53866
+ const html = htmlHandler(options.html, editor);
53867
+ const doc2 = DOMParser$1.fromSchema(schema).parse(html);
53868
+ content = doc2.content;
53869
+ }
53870
+ if (options.json) {
53871
+ content = schema.nodeFromJSON(options.json);
53872
+ }
53873
+ if (!content) {
53874
+ content = schema.nodeFromJSON({ type: "paragraph", content: [] });
53875
+ }
53876
+ const attrs = {
53877
+ ...options.attrs,
53878
+ id: options.attrs?.id || randomId(),
53879
+ tag: "block_table_sdt",
53880
+ alias: options.attrs?.alias || "Structured content"
53395
53881
  };
53396
- currentNodePos += node.nodeSize;
53397
- return resultNode;
53398
- });
53399
- nodes.forEach((item) => {
53400
- const { pos: pos2 } = item;
53401
- const newPos = tr.mapping.map(pos2);
53402
- tr.setNodeMarkup(newPos, void 0, {});
53403
- });
53882
+ const node = schema.nodes.structuredContentBlock.create(attrs, content, null);
53883
+ const parent = findParentNode((node2) => node2.type.name === "structuredContentBlock")(state2.selection);
53884
+ if (parent) {
53885
+ const insertPos = parent.pos + parent.node.nodeSize;
53886
+ from2 = to = insertPos;
53887
+ }
53888
+ tr.replaceRangeWith(from2, to, node);
53889
+ }
53404
53890
  return true;
53405
53891
  },
53406
53892
  /**
53407
- * Update ordered list style type based on nesting level
53893
+ * Updates a structured content attributes or content.
53894
+ * If the updated node does not match the schema, it will not be updated.
53408
53895
  * @category Command
53409
- * @example
53410
- * editor.commands.updateOrderedListStyleType()
53411
- * @note Cycles through decimal -> lowerAlpha -> lowerRoman based on depth
53896
+ * @param {string} id
53897
+ * @param {StructuredContentUpdate} options
53412
53898
  */
53413
- updateOrderedListStyleType: () => ({ dispatch, tr }) => {
53414
- let list = findParentNode((node) => node.type.name === this.name)(tr.selection);
53415
- if (!list) {
53899
+ updateStructuredContentById: (id, options = {}) => ({ editor, dispatch, state: state2, tr }) => {
53900
+ const structuredContentTags = getStructuredContentTagsById(id, state2);
53901
+ if (!structuredContentTags.length) {
53416
53902
  return true;
53417
53903
  }
53904
+ const { schema } = editor;
53418
53905
  if (dispatch) {
53419
- let listLevel = (list.depth - 1) / 2;
53420
- let listStyleTypes = this.options.listStyleTypes;
53421
- let listStyle = listStyleTypes[listLevel % listStyleTypes.length];
53422
- let currentListStyle = list.node.attrs["list-style-type"];
53423
- let nodeAtPos = tr.doc.nodeAt(list.pos);
53424
- if (currentListStyle !== listStyle && nodeAtPos.eq(list.node)) {
53425
- tr.setNodeMarkup(list.pos, void 0, {
53426
- ...list.node.attrs,
53427
- ...{
53428
- "list-style-type": listStyle
53429
- }
53430
- });
53906
+ const structuredContent = structuredContentTags[0];
53907
+ const { pos, node } = structuredContent;
53908
+ const posFrom = pos;
53909
+ const posTo = pos + node.nodeSize;
53910
+ let content = null;
53911
+ if (options.text) {
53912
+ content = schema.text(options.text);
53913
+ }
53914
+ if (options.html) {
53915
+ const html = htmlHandler(options.html, editor);
53916
+ const doc2 = DOMParser$1.fromSchema(schema).parse(html);
53917
+ content = doc2.content;
53918
+ }
53919
+ if (options.json) {
53920
+ content = schema.nodeFromJSON(options.json);
53431
53921
  }
53922
+ if (!content) {
53923
+ content = node.content;
53924
+ }
53925
+ const updatedNode = node.type.create({ ...node.attrs, ...options.attrs }, content, node.marks);
53926
+ try {
53927
+ updatedNode.check();
53928
+ } catch {
53929
+ console.error("Updated node does not conform to the schema");
53930
+ return false;
53931
+ }
53932
+ tr.replaceWith(posFrom, posTo, updatedNode);
53933
+ }
53934
+ return true;
53935
+ },
53936
+ /**
53937
+ * Removes a structured content.
53938
+ * @category Command
53939
+ * @param {Array<{ node: Node, pos: number }>} structuredContentTags
53940
+ */
53941
+ deleteStructuredContent: (structuredContentTags) => ({ dispatch, tr }) => {
53942
+ if (!structuredContentTags.length) {
53943
+ return true;
53944
+ }
53945
+ if (dispatch) {
53946
+ structuredContentTags.forEach((structuredContent) => {
53947
+ const { pos, node } = structuredContent;
53948
+ const posFrom = tr.mapping.map(pos);
53949
+ const posTo = tr.mapping.map(pos + node.nodeSize);
53950
+ const currentNode = tr.doc.nodeAt(posFrom);
53951
+ if (currentNode && node.eq(currentNode)) {
53952
+ tr.delete(posFrom, posTo);
53953
+ }
53954
+ });
53955
+ }
53956
+ return true;
53957
+ },
53958
+ /**
53959
+ * Removes a structured content by ID.
53960
+ * @category Command
53961
+ * @param {string | string[]} idOrIds
53962
+ */
53963
+ deleteStructuredContentById: (idOrIds) => ({ dispatch, state: state2, tr }) => {
53964
+ const structuredContentTags = getStructuredContentTagsById(idOrIds, state2);
53965
+ if (!structuredContentTags.length) {
53966
+ return true;
53967
+ }
53968
+ if (dispatch) {
53969
+ structuredContentTags.forEach((structuredContent) => {
53970
+ const { pos, node } = structuredContent;
53971
+ const posFrom = tr.mapping.map(pos);
53972
+ const posTo = tr.mapping.map(pos + node.nodeSize);
53973
+ const currentNode = tr.doc.nodeAt(posFrom);
53974
+ if (currentNode && node.eq(currentNode)) {
53975
+ tr.delete(posFrom, posTo);
53976
+ }
53977
+ });
53978
+ }
53979
+ return true;
53980
+ },
53981
+ /**
53982
+ * Removes a structured content at cursor, preserving its content.
53983
+ * @category Command
53984
+ */
53985
+ deleteStructuredContentAtSelection: () => ({ editor, dispatch, state: state2, tr }) => {
53986
+ const predicate = (node) => STRUCTURED_CONTENT_NAMES.includes(node.type.name);
53987
+ const structuredContent = findParentNode(predicate)(state2.selection);
53988
+ if (!structuredContent) {
53989
+ return true;
53990
+ }
53991
+ if (dispatch) {
53992
+ const { node, pos } = structuredContent;
53993
+ const posFrom = pos;
53994
+ const posTo = posFrom + node.nodeSize;
53995
+ const content = node.content;
53996
+ tr.replaceWith(posFrom, posTo, content);
53432
53997
  }
53433
53998
  return true;
53434
53999
  }
53435
54000
  };
53436
54001
  },
53437
- addShortcuts() {
54002
+ addHelpers() {
53438
54003
  return {
53439
- "Mod-Shift-7": () => {
53440
- return this.editor.commands.toggleOrderedList();
53441
- }
54004
+ ...structuredContentHelpers
53442
54005
  };
53443
- },
53444
- addInputRules() {
53445
- return [
53446
- new InputRule({
53447
- match: inputRegex,
53448
- handler: ({ state: state2, range: range2 }) => {
53449
- const $pos = state2.selection.$from;
53450
- const listItemType = state2.schema.nodes.listItem;
53451
- for (let depth = $pos.depth; depth >= 0; depth--) {
53452
- if ($pos.node(depth).type === listItemType) {
53453
- return null;
53454
- }
53455
- }
53456
- const { tr } = state2;
53457
- tr.delete(range2.from, range2.to);
53458
- ListHelpers.createNewList({
53459
- listType: this.type,
53460
- tr,
53461
- editor: this.editor
53462
- });
53463
- }
53464
- })
53465
- ];
53466
54006
  }
53467
54007
  });
53468
- const generateOrderedListIndex = ({ listLevel, lvlText, listNumberingType, customFormat }) => {
53469
- const handler2 = listIndexMap[listNumberingType];
53470
- return handler2 ? handler2(listLevel, lvlText, customFormat) : null;
53471
- };
53472
- const handleDecimal = (path, lvlText) => generateNumbering(path, lvlText, String);
53473
- const handleRoman = (path, lvlText) => generateNumbering(path, lvlText, intToRoman);
53474
- const handleLowerRoman = (path, lvlText) => handleRoman(path, lvlText).toLowerCase();
53475
- const handleLowerAlpha = (path, lvlText) => handleAlpha(path, lvlText).toLowerCase();
53476
- const handleAlpha = (path, lvlText) => generateNumbering(path, lvlText, (p) => intToAlpha(p));
53477
- const handleOrdinal = (path, lvlText) => generateNumbering(path, lvlText, ordinalFormatter);
53478
- const handleCustom = (path, lvlText, customFormat) => generateFromCustom(path, lvlText, customFormat);
53479
- const handleJapaneseCounting = (path, lvlText) => generateNumbering(path, lvlText, intToJapaneseCounting);
53480
- const listIndexMap = {
53481
- decimal: handleDecimal,
53482
- lowerRoman: handleLowerRoman,
53483
- upperRoman: handleRoman,
53484
- lowerLetter: handleLowerAlpha,
53485
- upperLetter: handleAlpha,
53486
- ordinal: handleOrdinal,
53487
- custom: handleCustom,
53488
- japaneseCounting: handleJapaneseCounting
53489
- };
53490
- const createNumbering = (values, lvlText) => {
53491
- return values.reduce((acc, value, index2) => {
53492
- return value > 9 ? acc.replace(/^0/, "").replace(`%${index2 + 1}`, value) : acc.replace(`%${index2 + 1}`, value);
53493
- }, lvlText);
53494
- };
53495
- const generateNumbering = (path, lvlText, formatter) => {
53496
- const formattedValues = path.map(formatter);
53497
- return createNumbering(formattedValues, lvlText);
54008
+ const randomId = () => {
54009
+ return Math.floor(Math.random() * 4294967295).toString();
53498
54010
  };
53499
- const ordinalFormatter = (level) => {
53500
- const suffixes = ["th", "st", "nd", "rd"];
53501
- const value = level % 100;
53502
- const suffix2 = suffixes[(value - 20) % 10] || suffixes[value] || suffixes[0];
53503
- const p = level + suffix2;
53504
- return p;
54011
+ class DocumentSectionView {
54012
+ constructor(node, getPos, decorations, editor) {
54013
+ __privateAdd$1(this, _DocumentSectionView_instances);
54014
+ this.node = node;
54015
+ this.editor = editor;
54016
+ this.decorations = decorations;
54017
+ this.view = editor.view;
54018
+ this.getPos = getPos;
54019
+ __privateMethod$1(this, _DocumentSectionView_instances, init_fn2).call(this);
54020
+ }
54021
+ }
54022
+ _DocumentSectionView_instances = /* @__PURE__ */ new WeakSet();
54023
+ init_fn2 = function() {
54024
+ const { attrs } = this.node;
54025
+ const { id, title, description } = attrs;
54026
+ this.dom = document.createElement("div");
54027
+ this.dom.className = "sd-document-section-block";
54028
+ this.dom.setAttribute("data-id", id);
54029
+ this.dom.setAttribute("data-title", title);
54030
+ this.dom.setAttribute("data-description", description);
54031
+ this.dom.setAttribute("aria-label", "Document section");
54032
+ __privateMethod$1(this, _DocumentSectionView_instances, addToolTip_fn).call(this);
54033
+ this.contentDOM = document.createElement("div");
54034
+ this.contentDOM.className = "sd-document-section-block-content";
54035
+ this.contentDOM.setAttribute("contenteditable", "true");
54036
+ this.dom.appendChild(this.contentDOM);
53505
54037
  };
53506
- const generateFromCustom = (path, lvlText, customFormat) => {
53507
- if (customFormat !== "001, 002, 003, ...") return generateNumbering(path, lvlText, String);
53508
- const match = customFormat.match(/(\d+)/);
53509
- if (!match) throw new Error("Invalid format string: no numeric pattern found");
53510
- const sample = match[1];
53511
- const digitCount = sample.length;
53512
- const index2 = path.pop();
53513
- return String(index2).padStart(digitCount, "0");
54038
+ addToolTip_fn = function() {
54039
+ const { title } = this.node.attrs;
54040
+ this.infoDiv = document.createElement("div");
54041
+ this.infoDiv.className = "sd-document-section-block-info";
54042
+ const textSpan = document.createElement("span");
54043
+ textSpan.textContent = title || "Document section";
54044
+ this.infoDiv.appendChild(textSpan);
54045
+ this.infoDiv.setAttribute("contenteditable", "false");
54046
+ this.dom.appendChild(this.infoDiv);
53514
54047
  };
53515
- const intToRoman = (num) => {
53516
- const romanNumeralMap = [
53517
- { value: 1e3, numeral: "M" },
53518
- { value: 900, numeral: "CM" },
53519
- { value: 500, numeral: "D" },
53520
- { value: 400, numeral: "CD" },
53521
- { value: 100, numeral: "C" },
53522
- { value: 90, numeral: "XC" },
53523
- { value: 50, numeral: "L" },
53524
- { value: 40, numeral: "XL" },
53525
- { value: 10, numeral: "X" },
53526
- { value: 9, numeral: "IX" },
53527
- { value: 5, numeral: "V" },
53528
- { value: 4, numeral: "IV" },
53529
- { value: 1, numeral: "I" }
53530
- ];
53531
- let result = "";
53532
- for (const { value, numeral } of romanNumeralMap) {
53533
- while (num >= value) {
53534
- result += numeral;
53535
- num -= value;
54048
+ const getAllSections = (editor) => {
54049
+ if (!editor) return [];
54050
+ const type2 = editor.schema.nodes.documentSection;
54051
+ if (!type2) return [];
54052
+ const sections = [];
54053
+ const { state: state2 } = editor;
54054
+ state2.doc.descendants((node, pos) => {
54055
+ if (node.type.name === type2.name) {
54056
+ sections.push({ node, pos });
53536
54057
  }
53537
- }
53538
- return result;
54058
+ });
54059
+ return sections;
53539
54060
  };
53540
- const intToAlpha = (num) => {
53541
- let result = "";
53542
- const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
53543
- while (num > 0) {
53544
- let index2 = (num - 1) % 26;
53545
- result = alphabet[index2] + result;
53546
- num = Math.floor((num - 1) / 26);
53547
- }
54061
+ const exportSectionsToHTML = (editor) => {
54062
+ const sections = getAllSections(editor);
54063
+ const processedSections = /* @__PURE__ */ new Set();
54064
+ const result = [];
54065
+ sections.forEach(({ node }) => {
54066
+ const { attrs } = node;
54067
+ const { id, title, description } = attrs;
54068
+ if (processedSections.has(id)) return;
54069
+ processedSections.add(id);
54070
+ const html = getHTMLFromNode(node, editor);
54071
+ result.push({
54072
+ id,
54073
+ title,
54074
+ description,
54075
+ html
54076
+ });
54077
+ });
53548
54078
  return result;
53549
54079
  };
53550
- const intToJapaneseCounting = (num) => {
53551
- const digits = ["", "一", "二", "三", "四", "五", "六", "七", "八", "九"];
53552
- const units = ["", "十", "百", "千"];
53553
- if (num === 0) return "零";
53554
- if (num < 10) return digits[num];
53555
- let result = "";
53556
- let tempNum = num;
53557
- let unitIndex = 0;
53558
- while (tempNum > 0) {
53559
- const digit = tempNum % 10;
53560
- if (digit !== 0) {
53561
- const digitStr = digit === 1 && unitIndex > 0 ? "" : digits[digit];
53562
- result = digitStr + (unitIndex > 0 ? units[unitIndex] : "") + result;
53563
- } else if (result && tempNum > 0) {
53564
- if (!result.startsWith("零") && tempNum % 100 !== 0) {
53565
- result = "零" + result;
53566
- }
53567
- }
53568
- tempNum = Math.floor(tempNum / 10);
53569
- unitIndex++;
53570
- if (unitIndex > 3) break;
53571
- }
53572
- if (num >= 10 && num < 20) {
53573
- result = result.replace(/^一十/, "十");
53574
- }
54080
+ const getHTMLFromNode = (node, editor) => {
54081
+ const tempDocument = document.implementation.createHTMLDocument();
54082
+ const container = tempDocument.createElement("div");
54083
+ const fragment = DOMSerializer.fromSchema(editor.schema).serializeFragment(node.content);
54084
+ container.appendChild(fragment);
54085
+ let html = container.innerHTML;
54086
+ return html;
54087
+ };
54088
+ const exportSectionsToJSON = (editor) => {
54089
+ const sections = getAllSections(editor);
54090
+ const processedSections = /* @__PURE__ */ new Set();
54091
+ const result = [];
54092
+ sections.forEach(({ node }) => {
54093
+ const { attrs } = node;
54094
+ const { id, title, description } = attrs;
54095
+ if (processedSections.has(id)) return;
54096
+ processedSections.add(id);
54097
+ result.push({
54098
+ id,
54099
+ title,
54100
+ description,
54101
+ content: node.toJSON()
54102
+ });
54103
+ });
53575
54104
  return result;
53576
54105
  };
53577
- const CustomSelectionPluginKey = new PluginKey("CustomSelection");
53578
- const shouldAllowNativeContextMenu = (event) => {
53579
- if (!event) return false;
53580
- if (event.ctrlKey || event.metaKey) {
53581
- return true;
53582
- }
53583
- 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;
53584
- return Boolean(isKeyboardInvocation);
54106
+ const getLinkedSectionEditor = (id, options, editor) => {
54107
+ const sections = getAllSections(editor);
54108
+ const section = sections.find((s) => s.node.attrs.id === id);
54109
+ if (!section) return null;
54110
+ const child = editor.createChildEditor({
54111
+ ...options,
54112
+ onUpdate: ({ editor: childEditor, transaction }) => {
54113
+ const isFromtLinkedParent = transaction.getMeta("fromLinkedParent");
54114
+ if (isFromtLinkedParent) return;
54115
+ const updatedContent = childEditor.state.doc.content;
54116
+ const sectionNode = getAllSections(editor)?.find((s) => s.node.attrs.id === id);
54117
+ if (!sectionNode) return;
54118
+ const { pos, node } = sectionNode;
54119
+ const newNode = node.type.create(node.attrs, updatedContent, node.marks);
54120
+ const tr = editor.state.tr.replaceWith(pos, pos + node.nodeSize, newNode);
54121
+ tr.setMeta("fromLinkedChild", true);
54122
+ editor.view.dispatch(tr);
54123
+ }
54124
+ });
54125
+ editor.on("update", ({ transaction }) => {
54126
+ const isFromLinkedChild = transaction.getMeta("fromLinkedChild");
54127
+ if (isFromLinkedChild) return;
54128
+ const sectionNode = getAllSections(editor)?.find((s) => s.node.attrs.id === id);
54129
+ if (!sectionNode) return;
54130
+ const sectionContent = sectionNode.node.content;
54131
+ const json = {
54132
+ type: "doc",
54133
+ content: sectionContent.content.map((node) => node.toJSON())
54134
+ };
54135
+ const childTr = child.state.tr;
54136
+ childTr.setMeta("fromLinkedParent", true);
54137
+ childTr.replaceWith(0, child.state.doc.content.size, child.schema.nodeFromJSON(json));
54138
+ child.view.dispatch(childTr);
54139
+ });
54140
+ return child;
54141
+ };
54142
+ const SectionHelpers = {
54143
+ getAllSections,
54144
+ exportSectionsToHTML,
54145
+ exportSectionsToJSON,
54146
+ getLinkedSectionEditor
54147
+ };
54148
+ const DocumentSection = Node$1.create({
54149
+ name: "documentSection",
54150
+ group: "block",
54151
+ content: "block*",
54152
+ atom: true,
54153
+ isolating: true,
54154
+ addOptions() {
54155
+ return {
54156
+ htmlAttributes: {
54157
+ class: "sd-document-section-block",
54158
+ "aria-label": "Structured content block"
54159
+ }
54160
+ };
54161
+ },
54162
+ parseDOM() {
54163
+ return [
54164
+ {
54165
+ tag: "div.sd-document-section-block",
54166
+ priority: 60
54167
+ }
54168
+ ];
54169
+ },
54170
+ renderDOM({ htmlAttributes }) {
54171
+ return ["div", Attribute2.mergeAttributes(this.options.htmlAttributes, htmlAttributes), 0];
54172
+ },
54173
+ addAttributes() {
54174
+ return {
54175
+ id: {},
54176
+ sdBlockId: {
54177
+ default: null,
54178
+ keepOnSplit: false,
54179
+ parseDOM: (elem) => elem.getAttribute("data-sd-block-id"),
54180
+ renderDOM: (attrs) => {
54181
+ return attrs.sdBlockId ? { "data-sd-block-id": attrs.sdBlockId } : {};
54182
+ }
54183
+ },
54184
+ title: {},
54185
+ description: {},
54186
+ sectionType: {},
54187
+ isLocked: { default: false }
54188
+ };
54189
+ },
54190
+ addNodeView() {
54191
+ return ({ node, editor, getPos, decorations }) => {
54192
+ return new DocumentSectionView(node, getPos, decorations, editor);
54193
+ };
54194
+ },
54195
+ addCommands() {
54196
+ return {
54197
+ /**
54198
+ * Create a lockable content section
54199
+ * @category Command
54200
+ * @param {SectionCreate} [options={}] - Section configuration
54201
+ * @example
54202
+ * editor.commands.createDocumentSection({
54203
+ * id: 1,
54204
+ * title: 'Terms & Conditions',
54205
+ * isLocked: true,
54206
+ * html: '<p>Legal content...</p>'
54207
+ * })
54208
+ */
54209
+ createDocumentSection: (options = {}) => ({ tr, state: state2, dispatch, editor }) => {
54210
+ const { selection } = state2;
54211
+ let { from: from2, to } = selection;
54212
+ let content = selection.content().content;
54213
+ const { html: optionsHTML, json: optionsJSON } = options;
54214
+ if (optionsHTML) {
54215
+ const html = htmlHandler(optionsHTML, this.editor);
54216
+ const doc2 = DOMParser$1.fromSchema(this.editor.schema).parse(html);
54217
+ content = doc2.content;
54218
+ }
54219
+ if (optionsJSON) {
54220
+ content = this.editor.schema.nodeFromJSON(optionsJSON);
54221
+ }
54222
+ if (!content?.content?.length) {
54223
+ content = this.editor.schema.nodeFromJSON({ type: "paragraph", content: [] });
54224
+ }
54225
+ if (!options.id) {
54226
+ const allSections = SectionHelpers.getAllSections(editor);
54227
+ options.id = allSections.length + 1;
54228
+ }
54229
+ if (!options.title) {
54230
+ options.title = "Document section";
54231
+ }
54232
+ const node = this.type.createAndFill(options, content);
54233
+ if (!node) return false;
54234
+ const isAlreadyInSdtBlock = findParentNode((node2) => node2.type.name === "documentSection")(selection);
54235
+ if (isAlreadyInSdtBlock && isAlreadyInSdtBlock.node) {
54236
+ const insertPos2 = isAlreadyInSdtBlock.pos + isAlreadyInSdtBlock.node.nodeSize;
54237
+ from2 = insertPos2;
54238
+ to = insertPos2;
54239
+ }
54240
+ tr.replaceRangeWith(from2, to, node);
54241
+ const nodeEnd = from2 + node.nodeSize;
54242
+ let shouldInsertParagraph = true;
54243
+ let insertPos = nodeEnd;
54244
+ if (nodeEnd >= tr.doc.content.size) {
54245
+ insertPos = tr.doc.content.size;
54246
+ if (insertPos > 0) {
54247
+ const $endPos = tr.doc.resolve(insertPos);
54248
+ if ($endPos.nodeBefore && $endPos.nodeBefore.type.name === "paragraph") {
54249
+ shouldInsertParagraph = false;
54250
+ }
54251
+ }
54252
+ }
54253
+ if (shouldInsertParagraph) {
54254
+ const emptyParagraph = tr.doc.type.schema.nodes.paragraph.create();
54255
+ tr.insert(insertPos, emptyParagraph);
54256
+ }
54257
+ if (dispatch) {
54258
+ tr.setMeta("documentSection", { action: "create" });
54259
+ dispatch(tr);
54260
+ setTimeout(() => {
54261
+ try {
54262
+ const currentState = editor.state;
54263
+ const docSize = currentState.doc.content.size;
54264
+ let targetPos = from2 + node.nodeSize;
54265
+ if (shouldInsertParagraph) {
54266
+ targetPos += 1;
54267
+ }
54268
+ targetPos = Math.min(targetPos, docSize);
54269
+ if (targetPos < docSize && targetPos > 0) {
54270
+ const newSelection = Selection.near(currentState.doc.resolve(targetPos));
54271
+ const newTr = currentState.tr.setSelection(newSelection);
54272
+ editor.view.dispatch(newTr);
54273
+ }
54274
+ } catch (e) {
54275
+ console.warn("Could not set delayed selection:", e);
54276
+ }
54277
+ }, 0);
54278
+ }
54279
+ return true;
54280
+ },
54281
+ /**
54282
+ * Remove section wrapper at cursor, preserving its content
54283
+ * @category Command
54284
+ * @example
54285
+ * editor.commands.removeSectionAtSelection()
54286
+ * @note Content stays in document, only section wrapper is removed
54287
+ */
54288
+ removeSectionAtSelection: () => ({ tr, dispatch }) => {
54289
+ const sdtNode = findParentNode((node2) => node2.type.name === "documentSection")(tr.selection);
54290
+ if (!sdtNode) return false;
54291
+ const { node, pos } = sdtNode;
54292
+ const nodeStart = pos;
54293
+ const nodeEnd = nodeStart + node.nodeSize;
54294
+ const contentToPreserve = node.content;
54295
+ tr.delete(nodeStart, nodeEnd);
54296
+ if (contentToPreserve.size > 0) {
54297
+ tr.insert(nodeStart, contentToPreserve);
54298
+ }
54299
+ const newPos = Math.min(nodeStart, tr.doc.content.size);
54300
+ tr.setSelection(Selection.near(tr.doc.resolve(newPos)));
54301
+ if (dispatch) {
54302
+ tr.setMeta("documentSection", { action: "delete" });
54303
+ dispatch(tr);
54304
+ }
54305
+ return true;
54306
+ },
54307
+ /**
54308
+ * Delete section and all its content
54309
+ * @category Command
54310
+ * @param {number} id - Section to delete
54311
+ * @example
54312
+ * editor.commands.removeSectionById(123)
54313
+ */
54314
+ removeSectionById: (id) => ({ tr, dispatch }) => {
54315
+ const sections = SectionHelpers.getAllSections(this.editor);
54316
+ const sectionToRemove = sections.find(({ node: node2 }) => node2.attrs.id === id);
54317
+ if (!sectionToRemove) return false;
54318
+ const { pos, node } = sectionToRemove;
54319
+ const nodeStart = pos;
54320
+ const nodeEnd = nodeStart + node.nodeSize;
54321
+ tr.delete(nodeStart, nodeEnd);
54322
+ if (dispatch) {
54323
+ tr.setMeta("documentSection", { action: "delete", id });
54324
+ dispatch(tr);
54325
+ }
54326
+ return true;
54327
+ },
54328
+ /**
54329
+ * Lock section against edits
54330
+ * @category Command
54331
+ * @param {number} id - Section to lock
54332
+ * @example
54333
+ * editor.commands.lockSectionById(123)
54334
+ */
54335
+ lockSectionById: (id) => ({ tr, dispatch }) => {
54336
+ const sections = SectionHelpers.getAllSections(this.editor);
54337
+ const sectionToLock = sections.find(({ node }) => node.attrs.id === id);
54338
+ if (!sectionToLock) return false;
54339
+ tr.setNodeMarkup(sectionToLock.pos, null, { ...sectionToLock.node.attrs, isLocked: true });
54340
+ if (dispatch) {
54341
+ tr.setMeta("documentSection", { action: "lock", id });
54342
+ dispatch(tr);
54343
+ }
54344
+ return true;
54345
+ },
54346
+ /**
54347
+ * Modify section attributes or content
54348
+ * @category Command
54349
+ * @param {SectionUpdate} options - Changes to apply
54350
+ * @example
54351
+ * editor.commands.updateSectionById({ id: 123, attrs: { isLocked: false } })
54352
+ * editor.commands.updateSectionById({ id: 123, html: '<p>New content</p>' })
54353
+ * editor.commands.updateSectionById({
54354
+ * id: 123,
54355
+ * html: '<p>Updated</p>',
54356
+ * attrs: { title: 'New Title' }
54357
+ * })
54358
+ */
54359
+ updateSectionById: ({ id, html, json, attrs }) => ({ tr, dispatch, editor }) => {
54360
+ const sections = SectionHelpers.getAllSections(editor || this.editor);
54361
+ const sectionToUpdate = sections.find(({ node: node2 }) => node2.attrs.id === id);
54362
+ if (!sectionToUpdate) return false;
54363
+ const { pos, node } = sectionToUpdate;
54364
+ let newContent = null;
54365
+ if (html) {
54366
+ const htmlDoc = htmlHandler(html, editor || this.editor);
54367
+ const doc2 = DOMParser$1.fromSchema((editor || this.editor).schema).parse(htmlDoc);
54368
+ newContent = doc2.content;
54369
+ }
54370
+ if (json) {
54371
+ newContent = (editor || this.editor).schema.nodeFromJSON(json);
54372
+ }
54373
+ if (!newContent) {
54374
+ newContent = node.content;
54375
+ }
54376
+ const updatedNode = node.type.create({ ...node.attrs, ...attrs }, newContent, node.marks);
54377
+ tr.replaceWith(pos, pos + node.nodeSize, updatedNode);
54378
+ if (dispatch) {
54379
+ tr.setMeta("documentSection", { action: "update", id, attrs });
54380
+ dispatch(tr);
54381
+ }
54382
+ return true;
54383
+ }
54384
+ };
54385
+ },
54386
+ addHelpers() {
54387
+ return {
54388
+ ...SectionHelpers
54389
+ };
54390
+ }
54391
+ });
54392
+ const Document = Node$1.create({
54393
+ name: "doc",
54394
+ topNode: true,
54395
+ content: "block+",
54396
+ parseDOM() {
54397
+ return [{ tag: "doc" }];
54398
+ },
54399
+ renderDOM() {
54400
+ return ["doc", 0];
54401
+ },
54402
+ addAttributes() {
54403
+ return {
54404
+ attributes: {
54405
+ rendered: false,
54406
+ "aria-label": "Document node"
54407
+ }
54408
+ };
54409
+ },
54410
+ addCommands() {
54411
+ return {
54412
+ /**
54413
+ * Get document statistics
54414
+ * @category Command
54415
+ * @example
54416
+ * // Get word and character count
54417
+ * const stats = editor.commands.getDocumentStats()
54418
+ * console.log(`${stats.words} words, ${stats.characters} characters`)
54419
+ * @note Returns word count, character count, and paragraph count
54420
+ */
54421
+ getDocumentStats: () => ({ editor }) => {
54422
+ const text = editor.getText();
54423
+ const words = text.split(/\s+/).filter((word) => word.length > 0).length;
54424
+ const characters = text.length;
54425
+ const paragraphs = editor.state.doc.content.childCount;
54426
+ return {
54427
+ words,
54428
+ characters,
54429
+ paragraphs
54430
+ };
54431
+ },
54432
+ /**
54433
+ * Clear entire document
54434
+ * @category Command
54435
+ * @example
54436
+ * editor.commands.clearDocument()
54437
+ * @note Replaces all content with an empty paragraph
54438
+ */
54439
+ clearDocument: () => ({ commands: commands2 }) => {
54440
+ return commands2.setContent("<p></p>");
54441
+ }
54442
+ };
54443
+ }
54444
+ });
54445
+ const Text = Node$1.create({
54446
+ name: "text",
54447
+ group: "inline",
54448
+ inline: true,
54449
+ addOptions() {
54450
+ return {};
54451
+ }
54452
+ });
54453
+ const splitRun = () => (props) => {
54454
+ const { state: state2, view, tr } = props;
54455
+ const { $from, empty: empty2 } = state2.selection;
54456
+ if (!empty2) return false;
54457
+ if ($from.parent.type.name !== "run") return false;
54458
+ const handled = splitBlock(state2, (transaction) => {
54459
+ view.dispatch(transaction);
54460
+ });
54461
+ if (handled) {
54462
+ tr.setMeta("preventDispatch", true);
54463
+ }
54464
+ return handled;
54465
+ };
54466
+ const Run = OxmlNode.create({
54467
+ name: "run",
54468
+ oXmlName: "w:r",
54469
+ group: "inline",
54470
+ inline: true,
54471
+ content: "inline*",
54472
+ selectable: false,
54473
+ childToAttributes: ["runProperties"],
54474
+ addOptions() {
54475
+ return {
54476
+ htmlAttributes: {
54477
+ "data-run": "1"
54478
+ }
54479
+ };
54480
+ },
54481
+ addAttributes() {
54482
+ return {
54483
+ runProperties: {
54484
+ default: null,
54485
+ rendered: false,
54486
+ keepOnSplit: true
54487
+ },
54488
+ rsidR: {
54489
+ default: null,
54490
+ rendered: false,
54491
+ keepOnSplit: true
54492
+ },
54493
+ rsidRPr: {
54494
+ default: null,
54495
+ rendered: false,
54496
+ keepOnSplit: true
54497
+ },
54498
+ rsidDel: {
54499
+ default: null,
54500
+ rendered: false,
54501
+ keepOnSplit: true
54502
+ }
54503
+ };
54504
+ },
54505
+ addCommands() {
54506
+ return {
54507
+ splitRun
54508
+ };
54509
+ },
54510
+ parseDOM() {
54511
+ return [{ tag: "span[data-run]" }];
54512
+ },
54513
+ renderDOM({ htmlAttributes }) {
54514
+ const base2 = Attribute2.mergeAttributes(this.options.htmlAttributes, htmlAttributes);
54515
+ return ["span", base2, 0];
54516
+ }
54517
+ });
54518
+ const inputRegex$1 = /^\s*([-+*])\s$/;
54519
+ const BulletList = Node$1.create({
54520
+ name: "bulletList",
54521
+ group: "block list",
54522
+ selectable: false,
54523
+ content() {
54524
+ return `${this.options.itemTypeName}+`;
54525
+ },
54526
+ addOptions() {
54527
+ return {
54528
+ itemTypeName: "listItem",
54529
+ htmlAttributes: {
54530
+ "aria-label": "Bullet list node"
54531
+ },
54532
+ keepMarks: true,
54533
+ keepAttributes: false
54534
+ };
54535
+ },
54536
+ parseDOM() {
54537
+ return [{ tag: "ul" }];
54538
+ },
54539
+ renderDOM({ htmlAttributes }) {
54540
+ const attributes = Attribute2.mergeAttributes(this.options.htmlAttributes, htmlAttributes);
54541
+ return ["ul", attributes, 0];
54542
+ },
54543
+ addAttributes() {
54544
+ return {
54545
+ "list-style-type": {
54546
+ default: "bullet",
54547
+ rendered: false
54548
+ },
54549
+ listId: {
54550
+ rendered: false
54551
+ },
54552
+ sdBlockId: {
54553
+ default: null,
54554
+ keepOnSplit: false,
54555
+ parseDOM: (elem) => elem.getAttribute("data-sd-block-id"),
54556
+ renderDOM: (attrs) => {
54557
+ return attrs.sdBlockId ? { "data-sd-block-id": attrs.sdBlockId } : {};
54558
+ }
54559
+ },
54560
+ attributes: {
54561
+ rendered: false,
54562
+ keepOnSplit: true
54563
+ }
54564
+ };
54565
+ },
54566
+ addCommands() {
54567
+ return {
54568
+ /**
54569
+ * Toggle a bullet list at the current selection
54570
+ * @category Command
54571
+ * @example
54572
+ * // Toggle bullet list on selected text
54573
+ * editor.commands.toggleBulletList()
54574
+ * @note Converts selected paragraphs to list items or removes list formatting
54575
+ */
54576
+ toggleBulletList: () => (params2) => {
54577
+ return toggleList(this.type)(params2);
54578
+ }
54579
+ };
54580
+ },
54581
+ addShortcuts() {
54582
+ return {
54583
+ "Mod-Shift-8": () => {
54584
+ return this.editor.commands.toggleBulletList();
54585
+ }
54586
+ };
54587
+ },
54588
+ addInputRules() {
54589
+ return [
54590
+ new InputRule({
54591
+ match: inputRegex$1,
54592
+ handler: ({ state: state2, range: range2 }) => {
54593
+ const $pos = state2.selection.$from;
54594
+ const listItemType = state2.schema.nodes.listItem;
54595
+ for (let depth = $pos.depth; depth >= 0; depth--) {
54596
+ if ($pos.node(depth).type === listItemType) {
54597
+ return null;
54598
+ }
54599
+ }
54600
+ const { tr } = state2;
54601
+ tr.delete(range2.from, range2.to);
54602
+ ListHelpers.createNewList({
54603
+ listType: this.type,
54604
+ tr,
54605
+ editor: this.editor
54606
+ });
54607
+ }
54608
+ })
54609
+ ];
54610
+ }
54611
+ });
54612
+ const inputRegex = /^(\d+)\.\s$/;
54613
+ const OrderedList = Node$1.create({
54614
+ name: "orderedList",
54615
+ group: "block list",
54616
+ selectable: false,
54617
+ content() {
54618
+ return `${this.options.itemTypeName}+`;
54619
+ },
54620
+ addOptions() {
54621
+ return {
54622
+ itemTypeName: "listItem",
54623
+ htmlAttributes: {
54624
+ "aria-label": "Ordered list node"
54625
+ },
54626
+ keepMarks: true,
54627
+ keepAttributes: false,
54628
+ listStyleTypes: ["decimal", "lowerAlpha", "lowerRoman"]
54629
+ };
54630
+ },
54631
+ addAttributes() {
54632
+ return {
54633
+ order: {
54634
+ default: 1,
54635
+ parseDOM: (element) => {
54636
+ return element.hasAttribute("start") ? parseInt(element.getAttribute("start") || "", 10) : 1;
54637
+ },
54638
+ renderDOM: (attrs) => {
54639
+ return {
54640
+ start: attrs.order
54641
+ };
54642
+ }
54643
+ },
54644
+ sdBlockId: {
54645
+ default: null,
54646
+ keepOnSplit: false,
54647
+ parseDOM: (elem) => elem.getAttribute("data-sd-block-id"),
54648
+ renderDOM: (attrs) => {
54649
+ return attrs.sdBlockId ? { "data-sd-block-id": attrs.sdBlockId } : {};
54650
+ }
54651
+ },
54652
+ syncId: {
54653
+ default: null,
54654
+ parseDOM: (elem) => elem.getAttribute("data-sync-id"),
54655
+ renderDOM: (attrs) => {
54656
+ if (!attrs.syncId) return {};
54657
+ return {
54658
+ "data-sync-id": attrs.syncId
54659
+ };
54660
+ }
54661
+ // rendered: false,
54662
+ },
54663
+ listId: {
54664
+ keepOnSplit: true,
54665
+ parseDOM: (elem) => elem.getAttribute("data-list-id"),
54666
+ renderDOM: (attrs) => {
54667
+ if (!attrs.listId) return {};
54668
+ return {
54669
+ "data-list-id": attrs.listId
54670
+ };
54671
+ }
54672
+ },
54673
+ "list-style-type": {
54674
+ default: "decimal",
54675
+ rendered: false
54676
+ },
54677
+ attributes: {
54678
+ rendered: false,
54679
+ keepOnSplit: true
54680
+ }
54681
+ };
54682
+ },
54683
+ parseDOM() {
54684
+ return [{ tag: "ol" }];
54685
+ },
54686
+ renderDOM({ htmlAttributes }) {
54687
+ const { start: start2, ...restAttributes } = htmlAttributes;
54688
+ return start2 === 1 ? ["ol", Attribute2.mergeAttributes(this.options.htmlAttributes, restAttributes), 0] : ["ol", Attribute2.mergeAttributes(this.options.htmlAttributes, htmlAttributes), 0];
54689
+ },
54690
+ addCommands() {
54691
+ return {
54692
+ /**
54693
+ * Toggle ordered list formatting
54694
+ * @category Command
54695
+ * @example
54696
+ * editor.commands.toggleOrderedList()
54697
+ * @note Converts selection to ordered list or back to paragraphs
54698
+ */
54699
+ toggleOrderedList: () => (params2) => {
54700
+ return toggleList(this.type)(params2);
54701
+ },
54702
+ /**
54703
+ * Restart list node numbering
54704
+ * @category Command
54705
+ * @param {Array} followingNodes - Nodes to restart
54706
+ * @param {number} pos - Starting position
54707
+ * @example
54708
+ * editor.commands.restartListNodes(nodes, position)
54709
+ * @note Resets list numbering for specified nodes
54710
+ */
54711
+ restartListNodes: (followingNodes, pos) => ({ tr }) => {
54712
+ let currentNodePos = pos;
54713
+ const nodes = followingNodes.map((node) => {
54714
+ const resultNode = {
54715
+ node,
54716
+ pos: currentNodePos
54717
+ };
54718
+ currentNodePos += node.nodeSize;
54719
+ return resultNode;
54720
+ });
54721
+ nodes.forEach((item) => {
54722
+ const { pos: pos2 } = item;
54723
+ const newPos = tr.mapping.map(pos2);
54724
+ tr.setNodeMarkup(newPos, void 0, {});
54725
+ });
54726
+ return true;
54727
+ },
54728
+ /**
54729
+ * Update ordered list style type based on nesting level
54730
+ * @category Command
54731
+ * @example
54732
+ * editor.commands.updateOrderedListStyleType()
54733
+ * @note Cycles through decimal -> lowerAlpha -> lowerRoman based on depth
54734
+ */
54735
+ updateOrderedListStyleType: () => ({ dispatch, tr }) => {
54736
+ let list = findParentNode((node) => node.type.name === this.name)(tr.selection);
54737
+ if (!list) {
54738
+ return true;
54739
+ }
54740
+ if (dispatch) {
54741
+ let listLevel = (list.depth - 1) / 2;
54742
+ let listStyleTypes = this.options.listStyleTypes;
54743
+ let listStyle = listStyleTypes[listLevel % listStyleTypes.length];
54744
+ let currentListStyle = list.node.attrs["list-style-type"];
54745
+ let nodeAtPos = tr.doc.nodeAt(list.pos);
54746
+ if (currentListStyle !== listStyle && nodeAtPos.eq(list.node)) {
54747
+ tr.setNodeMarkup(list.pos, void 0, {
54748
+ ...list.node.attrs,
54749
+ ...{
54750
+ "list-style-type": listStyle
54751
+ }
54752
+ });
54753
+ }
54754
+ }
54755
+ return true;
54756
+ }
54757
+ };
54758
+ },
54759
+ addShortcuts() {
54760
+ return {
54761
+ "Mod-Shift-7": () => {
54762
+ return this.editor.commands.toggleOrderedList();
54763
+ }
54764
+ };
54765
+ },
54766
+ addInputRules() {
54767
+ return [
54768
+ new InputRule({
54769
+ match: inputRegex,
54770
+ handler: ({ state: state2, range: range2 }) => {
54771
+ const $pos = state2.selection.$from;
54772
+ const listItemType = state2.schema.nodes.listItem;
54773
+ for (let depth = $pos.depth; depth >= 0; depth--) {
54774
+ if ($pos.node(depth).type === listItemType) {
54775
+ return null;
54776
+ }
54777
+ }
54778
+ const { tr } = state2;
54779
+ tr.delete(range2.from, range2.to);
54780
+ ListHelpers.createNewList({
54781
+ listType: this.type,
54782
+ tr,
54783
+ editor: this.editor
54784
+ });
54785
+ }
54786
+ })
54787
+ ];
54788
+ }
54789
+ });
54790
+ const generateOrderedListIndex = ({ listLevel, lvlText, listNumberingType, customFormat }) => {
54791
+ const handler2 = listIndexMap[listNumberingType];
54792
+ return handler2 ? handler2(listLevel, lvlText, customFormat) : null;
54793
+ };
54794
+ const handleDecimal = (path, lvlText) => generateNumbering(path, lvlText, String);
54795
+ const handleRoman = (path, lvlText) => generateNumbering(path, lvlText, intToRoman);
54796
+ const handleLowerRoman = (path, lvlText) => handleRoman(path, lvlText).toLowerCase();
54797
+ const handleLowerAlpha = (path, lvlText) => handleAlpha(path, lvlText).toLowerCase();
54798
+ const handleAlpha = (path, lvlText) => generateNumbering(path, lvlText, (p) => intToAlpha(p));
54799
+ const handleOrdinal = (path, lvlText) => generateNumbering(path, lvlText, ordinalFormatter);
54800
+ const handleCustom = (path, lvlText, customFormat) => generateFromCustom(path, lvlText, customFormat);
54801
+ const handleJapaneseCounting = (path, lvlText) => generateNumbering(path, lvlText, intToJapaneseCounting);
54802
+ const listIndexMap = {
54803
+ decimal: handleDecimal,
54804
+ lowerRoman: handleLowerRoman,
54805
+ upperRoman: handleRoman,
54806
+ lowerLetter: handleLowerAlpha,
54807
+ upperLetter: handleAlpha,
54808
+ ordinal: handleOrdinal,
54809
+ custom: handleCustom,
54810
+ japaneseCounting: handleJapaneseCounting
54811
+ };
54812
+ const createNumbering = (values, lvlText) => {
54813
+ return values.reduce((acc, value, index2) => {
54814
+ return value > 9 ? acc.replace(/^0/, "").replace(`%${index2 + 1}`, value) : acc.replace(`%${index2 + 1}`, value);
54815
+ }, lvlText);
54816
+ };
54817
+ const generateNumbering = (path, lvlText, formatter) => {
54818
+ const formattedValues = path.map(formatter);
54819
+ return createNumbering(formattedValues, lvlText);
54820
+ };
54821
+ const ordinalFormatter = (level) => {
54822
+ const suffixes = ["th", "st", "nd", "rd"];
54823
+ const value = level % 100;
54824
+ const suffix2 = suffixes[(value - 20) % 10] || suffixes[value] || suffixes[0];
54825
+ const p = level + suffix2;
54826
+ return p;
54827
+ };
54828
+ const generateFromCustom = (path, lvlText, customFormat) => {
54829
+ if (customFormat !== "001, 002, 003, ...") return generateNumbering(path, lvlText, String);
54830
+ const match = customFormat.match(/(\d+)/);
54831
+ if (!match) throw new Error("Invalid format string: no numeric pattern found");
54832
+ const sample = match[1];
54833
+ const digitCount = sample.length;
54834
+ const index2 = path.pop();
54835
+ return String(index2).padStart(digitCount, "0");
53585
54836
  };
54837
+ const intToRoman = (num) => {
54838
+ const romanNumeralMap = [
54839
+ { value: 1e3, numeral: "M" },
54840
+ { value: 900, numeral: "CM" },
54841
+ { value: 500, numeral: "D" },
54842
+ { value: 400, numeral: "CD" },
54843
+ { value: 100, numeral: "C" },
54844
+ { value: 90, numeral: "XC" },
54845
+ { value: 50, numeral: "L" },
54846
+ { value: 40, numeral: "XL" },
54847
+ { value: 10, numeral: "X" },
54848
+ { value: 9, numeral: "IX" },
54849
+ { value: 5, numeral: "V" },
54850
+ { value: 4, numeral: "IV" },
54851
+ { value: 1, numeral: "I" }
54852
+ ];
54853
+ let result = "";
54854
+ for (const { value, numeral } of romanNumeralMap) {
54855
+ while (num >= value) {
54856
+ result += numeral;
54857
+ num -= value;
54858
+ }
54859
+ }
54860
+ return result;
54861
+ };
54862
+ const intToAlpha = (num) => {
54863
+ let result = "";
54864
+ const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
54865
+ while (num > 0) {
54866
+ let index2 = (num - 1) % 26;
54867
+ result = alphabet[index2] + result;
54868
+ num = Math.floor((num - 1) / 26);
54869
+ }
54870
+ return result;
54871
+ };
54872
+ const intToJapaneseCounting = (num) => {
54873
+ const digits = ["", "一", "二", "三", "四", "五", "六", "七", "八", "九"];
54874
+ const units = ["", "十", "百", "千"];
54875
+ if (num === 0) return "零";
54876
+ if (num < 10) return digits[num];
54877
+ let result = "";
54878
+ let tempNum = num;
54879
+ let unitIndex = 0;
54880
+ while (tempNum > 0) {
54881
+ const digit = tempNum % 10;
54882
+ if (digit !== 0) {
54883
+ const digitStr = digit === 1 && unitIndex > 0 ? "" : digits[digit];
54884
+ result = digitStr + (unitIndex > 0 ? units[unitIndex] : "") + result;
54885
+ } else if (result && tempNum > 0) {
54886
+ if (!result.startsWith("零") && tempNum % 100 !== 0) {
54887
+ result = "零" + result;
54888
+ }
54889
+ }
54890
+ tempNum = Math.floor(tempNum / 10);
54891
+ unitIndex++;
54892
+ if (unitIndex > 3) break;
54893
+ }
54894
+ if (num >= 10 && num < 20) {
54895
+ result = result.replace(/^一十/, "十");
54896
+ }
54897
+ return result;
54898
+ };
54899
+ const isKeyboardInvocation = (event) => {
54900
+ return event.type === "contextmenu" && typeof event.detail === "number" && event.detail === 0 && (event.button === 0 || event.button === void 0) && event.clientX === 0 && event.clientY === 0;
54901
+ };
54902
+ const prefersNativeMenu = (event) => {
54903
+ if (!event) return false;
54904
+ if (event.ctrlKey || event.metaKey) {
54905
+ return true;
54906
+ }
54907
+ return isKeyboardInvocation(event);
54908
+ };
54909
+ const shouldAllowNativeContextMenu = (event) => {
54910
+ return prefersNativeMenu(event);
54911
+ };
54912
+ const shouldBypassContextMenu = shouldAllowNativeContextMenu;
54913
+ const DEFAULT_SELECTION_STATE = Object.freeze({
54914
+ focused: false,
54915
+ preservedSelection: null,
54916
+ showVisualSelection: false,
54917
+ skipFocusReset: false
54918
+ });
54919
+ const normalizeSelectionState = (state2 = {}) => ({
54920
+ ...DEFAULT_SELECTION_STATE,
54921
+ ...state2
54922
+ });
54923
+ const CustomSelectionPluginKey = new PluginKey("CustomSelection");
53586
54924
  const handleClickOutside = (event, editor) => {
53587
54925
  const editorElem = editor?.options?.element;
53588
54926
  if (!editorElem) return;
@@ -53619,11 +54957,7 @@ const CustomSelection = Extension.create({
53619
54957
  const customSelectionPlugin = new Plugin({
53620
54958
  key: CustomSelectionPluginKey,
53621
54959
  state: {
53622
- init: () => ({
53623
- focused: false,
53624
- preservedSelection: null,
53625
- showVisualSelection: false
53626
- }),
54960
+ init: () => ({ ...DEFAULT_SELECTION_STATE }),
53627
54961
  apply: (tr, value) => {
53628
54962
  const meta = getFocusMeta(tr);
53629
54963
  if (meta !== void 0) {
@@ -53654,7 +54988,8 @@ const CustomSelection = Extension.create({
53654
54988
  setFocusMeta(view.state.tr, {
53655
54989
  focused: true,
53656
54990
  preservedSelection: selection,
53657
- showVisualSelection: true
54991
+ showVisualSelection: true,
54992
+ skipFocusReset: true
53658
54993
  })
53659
54994
  );
53660
54995
  }
@@ -53675,7 +55010,8 @@ const CustomSelection = Extension.create({
53675
55010
  setFocusMeta(view.state.tr, {
53676
55011
  focused: true,
53677
55012
  preservedSelection: selection2,
53678
- showVisualSelection: true
55013
+ showVisualSelection: true,
55014
+ skipFocusReset: true
53679
55015
  })
53680
55016
  );
53681
55017
  this.editor.setOptions({
@@ -53698,7 +55034,8 @@ const CustomSelection = Extension.create({
53698
55034
  setFocusMeta(view.state.tr, {
53699
55035
  focused: true,
53700
55036
  preservedSelection: selection,
53701
- showVisualSelection: true
55037
+ showVisualSelection: true,
55038
+ skipFocusReset: false
53702
55039
  })
53703
55040
  );
53704
55041
  this.editor.setOptions({
@@ -53716,7 +55053,8 @@ const CustomSelection = Extension.create({
53716
55053
  setFocusMeta(view.state.tr, {
53717
55054
  focused: true,
53718
55055
  preservedSelection: selection,
53719
- showVisualSelection: true
55056
+ showVisualSelection: true,
55057
+ skipFocusReset: false
53720
55058
  })
53721
55059
  );
53722
55060
  }
@@ -53727,7 +55065,8 @@ const CustomSelection = Extension.create({
53727
55065
  setFocusMeta(view.state.tr, {
53728
55066
  focused: false,
53729
55067
  preservedSelection: null,
53730
- showVisualSelection: false
55068
+ showVisualSelection: false,
55069
+ skipFocusReset: false
53731
55070
  })
53732
55071
  );
53733
55072
  if (!selection.empty && !this.editor.options.element?.contains(target)) {
@@ -53744,12 +55083,20 @@ const CustomSelection = Extension.create({
53744
55083
  const isElement2 = target instanceof Element;
53745
55084
  const isToolbarBtn = isElement2 && isToolbarButton(target);
53746
55085
  const isToolbarInp = isElement2 && isToolbarInput(target);
55086
+ const focusState = getFocusState(view.state);
55087
+ if (focusState?.skipFocusReset) {
55088
+ view.dispatch(
55089
+ setFocusMeta(view.state.tr, normalizeSelectionState({ ...focusState, skipFocusReset: false }))
55090
+ );
55091
+ return false;
55092
+ }
53747
55093
  if (!isToolbarBtn && !isToolbarInp) {
53748
55094
  view.dispatch(
53749
55095
  setFocusMeta(view.state.tr, {
53750
55096
  focused: false,
53751
55097
  preservedSelection: null,
53752
- showVisualSelection: false
55098
+ showVisualSelection: false,
55099
+ skipFocusReset: false
53753
55100
  })
53754
55101
  );
53755
55102
  }
@@ -53760,12 +55107,16 @@ const CustomSelection = Extension.create({
53760
55107
  const isToolbarBtn = isElement2 && isToolbarButton(target);
53761
55108
  const isToolbarInp = isElement2 && isToolbarInput(target);
53762
55109
  const state2 = getFocusState(view.state);
55110
+ if (state2?.skipFocusReset) {
55111
+ return false;
55112
+ }
53763
55113
  if (isToolbarBtn || isToolbarInp) {
53764
55114
  view.dispatch(
53765
55115
  setFocusMeta(view.state.tr, {
53766
55116
  focused: true,
53767
55117
  preservedSelection: state2.preservedSelection || view.state.selection,
53768
- showVisualSelection: true
55118
+ showVisualSelection: true,
55119
+ skipFocusReset: false
53769
55120
  })
53770
55121
  );
53771
55122
  } else {
@@ -53773,7 +55124,8 @@ const CustomSelection = Extension.create({
53773
55124
  setFocusMeta(view.state.tr, {
53774
55125
  focused: false,
53775
55126
  preservedSelection: null,
53776
- showVisualSelection: false
55127
+ showVisualSelection: false,
55128
+ skipFocusReset: false
53777
55129
  })
53778
55130
  );
53779
55131
  }
@@ -54619,7 +55971,7 @@ class ListItemNodeView {
54619
55971
  this.decorations = decorations;
54620
55972
  this.view = editor.view;
54621
55973
  this.getPos = getPos;
54622
- __privateMethod$1(this, _ListItemNodeView_instances, init_fn2).call(this);
55974
+ __privateMethod$1(this, _ListItemNodeView_instances, init_fn3).call(this);
54623
55975
  activeListItemNodeViews.add(this);
54624
55976
  }
54625
55977
  refreshIndentStyling() {
@@ -54680,7 +56032,7 @@ class ListItemNodeView {
54680
56032
  }
54681
56033
  }
54682
56034
  _ListItemNodeView_instances = /* @__PURE__ */ new WeakSet();
54683
- init_fn2 = function() {
56035
+ init_fn3 = function() {
54684
56036
  const { attrs } = this.node;
54685
56037
  const { listLevel, listNumberingType, lvlText, numId, level, customFormat } = attrs;
54686
56038
  let orderMarker = "";
@@ -61796,977 +63148,328 @@ const PageNumber = Node$1.create({
61796
63148
  rendered: false
61797
63149
  }
61798
63150
  };
61799
- },
61800
- addNodeView() {
61801
- return ({ node, editor, getPos, decorations }) => {
61802
- const htmlAttributes = this.options.htmlAttributes;
61803
- return new AutoPageNumberNodeView(node, getPos, decorations, editor, htmlAttributes);
61804
- };
61805
- },
61806
- parseDOM() {
61807
- return [{ tag: 'span[data-id="auto-page-number"' }];
61808
- },
61809
- renderDOM({ htmlAttributes }) {
61810
- return ["span", Attribute2.mergeAttributes(this.options.htmlAttributes, htmlAttributes)];
61811
- },
61812
- addCommands() {
61813
- return {
61814
- /**
61815
- * Insert an automatic page number
61816
- * @category Command
61817
- * @returns {Function} Command function
61818
- * @example
61819
- * editor.commands.addAutoPageNumber()
61820
- * @note Only works in header/footer contexts
61821
- */
61822
- addAutoPageNumber: () => ({ tr, dispatch, state: state2, editor }) => {
61823
- const { options } = editor;
61824
- if (!options.isHeaderOrFooter) return false;
61825
- const { schema } = state2;
61826
- const pageNumberType = schema?.nodes?.["page-number"];
61827
- if (!pageNumberType) return false;
61828
- const pageNumberNodeJSON = { type: "page-number" };
61829
- const pageNumberNode = schema.nodeFromJSON(pageNumberNodeJSON);
61830
- if (dispatch) {
61831
- tr.replaceSelectionWith(pageNumberNode, false);
61832
- tr.setMeta("forceUpdatePagination", true);
61833
- }
61834
- return true;
61835
- }
61836
- };
61837
- },
61838
- addShortcuts() {
61839
- return {
61840
- "Mod-Shift-alt-p": () => this.editor.commands.addAutoPageNumber()
61841
- };
61842
- }
61843
- });
61844
- const TotalPageCount = Node$1.create({
61845
- name: "total-page-number",
61846
- group: "inline",
61847
- inline: true,
61848
- atom: true,
61849
- draggable: false,
61850
- selectable: false,
61851
- content: "text*",
61852
- addOptions() {
61853
- return {
61854
- htmlAttributes: {
61855
- contenteditable: false,
61856
- "data-id": "auto-total-pages",
61857
- "aria-label": "Total page count node",
61858
- class: "sd-editor-auto-total-pages"
61859
- }
61860
- };
61861
- },
61862
- addAttributes() {
61863
- return {
61864
- marksAsAttrs: {
61865
- default: null,
61866
- rendered: false
61867
- }
61868
- };
61869
- },
61870
- addNodeView() {
61871
- return ({ node, editor, getPos, decorations }) => {
61872
- const htmlAttributes = this.options.htmlAttributes;
61873
- return new AutoPageNumberNodeView(node, getPos, decorations, editor, htmlAttributes);
61874
- };
61875
- },
61876
- parseDOM() {
61877
- return [{ tag: 'span[data-id="auto-total-pages"' }];
61878
- },
61879
- renderDOM({ htmlAttributes }) {
61880
- return ["span", Attribute2.mergeAttributes(this.options.htmlAttributes, htmlAttributes), 0];
61881
- },
61882
- addCommands() {
61883
- return {
61884
- /**
61885
- * Insert total page count
61886
- * @category Command
61887
- * @returns {Function} Command function
61888
- * @example
61889
- * editor.commands.addTotalPageCount()
61890
- * @note Only works in header/footer contexts
61891
- */
61892
- addTotalPageCount: () => ({ tr, dispatch, state: state2, editor }) => {
61893
- const { options } = editor;
61894
- if (!options.isHeaderOrFooter) return false;
61895
- const { schema } = state2;
61896
- const pageNumberType = schema.nodes?.["total-page-number"];
61897
- if (!pageNumberType) return false;
61898
- const currentPages = editor?.options?.parentEditor?.currentTotalPages || 1;
61899
- const pageNumberNode = {
61900
- type: "total-page-number",
61901
- content: [{ type: "text", text: String(currentPages) }]
61902
- };
61903
- const pageNode = schema.nodeFromJSON(pageNumberNode);
61904
- if (dispatch) {
61905
- tr.replaceSelectionWith(pageNode, false);
61906
- }
61907
- return true;
61908
- }
61909
- };
61910
- },
61911
- addShortcuts() {
61912
- return {
61913
- "Mod-Shift-alt-c": () => this.editor.commands.addTotalPageCount()
61914
- };
61915
- }
61916
- });
61917
- const getNodeAttributes = (nodeName, editor) => {
61918
- switch (nodeName) {
61919
- case "page-number":
61920
- return {
61921
- text: editor.options.currentPageNumber || "1",
61922
- className: "sd-editor-auto-page-number",
61923
- dataId: "auto-page-number",
61924
- ariaLabel: "Page number node"
61925
- };
61926
- case "total-page-number":
61927
- return {
61928
- text: editor.options.parentEditor?.currentTotalPages || "1",
61929
- className: "sd-editor-auto-total-pages",
61930
- dataId: "auto-total-pages",
61931
- ariaLabel: "Total page count node"
61932
- };
61933
- default:
61934
- return {};
61935
- }
61936
- };
61937
- class AutoPageNumberNodeView {
61938
- constructor(node, getPos, decorations, editor, htmlAttributes = {}) {
61939
- __privateAdd$1(this, _AutoPageNumberNodeView_instances);
61940
- this.node = node;
61941
- this.editor = editor;
61942
- this.view = editor.view;
61943
- this.getPos = getPos;
61944
- this.editor = editor;
61945
- this.dom = __privateMethod$1(this, _AutoPageNumberNodeView_instances, renderDom_fn).call(this, node, htmlAttributes);
61946
- }
61947
- update(node) {
61948
- const incomingType = node?.type?.name;
61949
- const currentType = this.node?.type?.name;
61950
- if (!incomingType || incomingType !== currentType) return false;
61951
- this.node = node;
61952
- return true;
61953
- }
61954
- }
61955
- _AutoPageNumberNodeView_instances = /* @__PURE__ */ new WeakSet();
61956
- renderDom_fn = function(node, htmlAttributes) {
61957
- const attrs = getNodeAttributes(this.node.type.name, this.editor);
61958
- const content = document.createTextNode(String(attrs.text));
61959
- const nodeContent = document.createElement("span");
61960
- nodeContent.className = attrs.className;
61961
- nodeContent.setAttribute("data-id", attrs.dataId);
61962
- nodeContent.setAttribute("aria-label", attrs.ariaLabel);
61963
- const currentPos = this.getPos();
61964
- const { styles, marks } = getMarksFromNeighbors(currentPos, this.view);
61965
- __privateMethod$1(this, _AutoPageNumberNodeView_instances, scheduleUpdateNodeStyle_fn).call(this, currentPos, marks);
61966
- Object.assign(nodeContent.style, styles);
61967
- nodeContent.appendChild(content);
61968
- Object.entries(htmlAttributes).forEach(([key2, value]) => {
61969
- if (value) nodeContent.setAttribute(key2, value);
61970
- });
61971
- return nodeContent;
61972
- };
61973
- scheduleUpdateNodeStyle_fn = function(pos, marks) {
61974
- setTimeout(() => {
61975
- const { state: state2 } = this.editor;
61976
- const { dispatch } = this.view;
61977
- const node = state2.doc.nodeAt(pos);
61978
- if (!node || node.isText) return;
61979
- const currentMarks = node.attrs.marksAsAttrs || [];
61980
- const newMarks = marks.map((m2) => ({ type: m2.type.name, attrs: m2.attrs }));
61981
- const isEqual = JSON.stringify(currentMarks) === JSON.stringify(newMarks);
61982
- if (isEqual) return;
61983
- const newAttrs = {
61984
- ...node.attrs,
61985
- marksAsAttrs: newMarks
61986
- };
61987
- const tr = state2.tr.setNodeMarkup(pos, void 0, newAttrs);
61988
- dispatch(tr);
61989
- }, 0);
61990
- };
61991
- const getMarksFromNeighbors = (currentPos, view) => {
61992
- const $pos = view.state.doc.resolve(currentPos);
61993
- const styles = {};
61994
- const marks = [];
61995
- const before = $pos.nodeBefore;
61996
- if (before) {
61997
- Object.assign(styles, processMarks(before.marks));
61998
- marks.push(...before.marks);
61999
- }
62000
- const after = $pos.nodeAfter;
62001
- if (after) {
62002
- Object.assign(styles, { ...styles, ...processMarks(after.marks) });
62003
- marks.push(...after.marks);
62004
- }
62005
- return {
62006
- styles,
62007
- marks
62008
- };
62009
- };
62010
- const processMarks = (marks) => {
62011
- const styles = {};
62012
- marks.forEach((mark) => {
62013
- const { type: type2, attrs } = mark;
62014
- switch (type2.name) {
62015
- case "textStyle":
62016
- if (attrs.fontFamily) styles["font-family"] = attrs.fontFamily;
62017
- if (attrs.fontSize) styles["font-size"] = attrs.fontSize;
62018
- if (attrs.color) styles["color"] = attrs.color;
62019
- if (attrs.backgroundColor) styles["background-color"] = attrs.backgroundColor;
62020
- break;
62021
- case "bold":
62022
- styles["font-weight"] = "bold";
62023
- break;
62024
- case "italic":
62025
- styles["font-style"] = "italic";
62026
- break;
62027
- case "underline":
62028
- styles["text-decoration"] = (styles["text-decoration"] || "") + " underline";
62029
- break;
62030
- case "strike":
62031
- styles["text-decoration"] = (styles["text-decoration"] || "") + " line-through";
62032
- break;
62033
- default:
62034
- if (attrs?.style) {
62035
- Object.entries(attrs.style).forEach(([key2, value]) => {
62036
- styles[key2] = value;
62037
- });
62038
- }
62039
- break;
62040
- }
62041
- });
62042
- return styles;
62043
- };
62044
- const ShapeContainer = Node$1.create({
62045
- name: "shapeContainer",
62046
- group: "block",
62047
- content: "block+",
62048
- isolating: true,
62049
- addOptions() {
62050
- return {
62051
- htmlAttributes: {
62052
- class: "sd-editor-shape-container",
62053
- "aria-label": "Shape container node"
62054
- }
62055
- };
62056
- },
62057
- addAttributes() {
62058
- return {
62059
- fillcolor: {
62060
- renderDOM: (attrs) => {
62061
- if (!attrs.fillcolor) return {};
62062
- return {
62063
- style: `background-color: ${attrs.fillcolor}`
62064
- };
62065
- }
62066
- },
62067
- sdBlockId: {
62068
- default: null,
62069
- keepOnSplit: false,
62070
- parseDOM: (elem) => elem.getAttribute("data-sd-block-id"),
62071
- renderDOM: (attrs) => {
62072
- return attrs.sdBlockId ? { "data-sd-block-id": attrs.sdBlockId } : {};
62073
- }
62074
- },
62075
- style: {
62076
- renderDOM: (attrs) => {
62077
- if (!attrs.style) return {};
62078
- return {
62079
- style: attrs.style
62080
- };
62081
- }
62082
- },
62083
- wrapAttributes: {
62084
- rendered: false
62085
- },
62086
- attributes: {
62087
- rendered: false
62088
- }
62089
- };
62090
- },
62091
- parseDOM() {
62092
- return [
62093
- {
62094
- tag: `div[data-type="${this.name}"]`
62095
- }
62096
- ];
62097
- },
62098
- renderDOM({ htmlAttributes }) {
62099
- return [
62100
- "div",
62101
- Attribute2.mergeAttributes(this.options.htmlAttributes, htmlAttributes, { "data-type": this.name }),
62102
- 0
62103
- ];
62104
- }
62105
- });
62106
- const ShapeTextbox = Node$1.create({
62107
- name: "shapeTextbox",
62108
- group: "block",
62109
- content: "paragraph* block*",
62110
- isolating: true,
62111
- addOptions() {
62112
- return {
62113
- htmlAttributes: {
62114
- class: "sd-editor-shape-textbox",
62115
- "aria-label": "Shape textbox node"
62116
- }
62117
- };
62118
- },
62119
- addAttributes() {
62120
- return {
62121
- sdBlockId: {
62122
- default: null,
62123
- keepOnSplit: false,
62124
- parseDOM: (elem) => elem.getAttribute("data-sd-block-id"),
62125
- renderDOM: (attrs) => {
62126
- return attrs.sdBlockId ? { "data-sd-block-id": attrs.sdBlockId } : {};
62127
- }
62128
- },
62129
- attributes: {
62130
- rendered: false
62131
- }
62132
- };
62133
- },
62134
- parseDOM() {
62135
- return [
62136
- {
62137
- tag: `div[data-type="${this.name}"]`
62138
- }
62139
- ];
62140
- },
62141
- renderDOM({ htmlAttributes }) {
62142
- return [
62143
- "div",
62144
- Attribute2.mergeAttributes(this.options.htmlAttributes, htmlAttributes, { "data-type": this.name }),
62145
- 0
62146
- ];
62147
- }
62148
- });
62149
- const ContentBlock = Node$1.create({
62150
- name: "contentBlock",
62151
- group: "inline",
62152
- content: "",
62153
- isolating: true,
62154
- atom: true,
62155
- inline: true,
62156
- addOptions() {
62157
- return {
62158
- htmlAttributes: {
62159
- contenteditable: false
62160
- }
62161
- };
62162
- },
62163
- addAttributes() {
62164
- return {
62165
- horizontalRule: {
62166
- default: false,
62167
- renderDOM: ({ horizontalRule }) => {
62168
- if (!horizontalRule) return {};
62169
- return { "data-horizontal-rule": "true" };
62170
- }
62171
- },
62172
- size: {
62173
- default: null,
62174
- renderDOM: ({ size: size2 }) => {
62175
- if (!size2) return {};
62176
- let style2 = "";
62177
- if (size2.top) style2 += `top: ${size2.top}px; `;
62178
- if (size2.left) style2 += `left: ${size2.left}px; `;
62179
- if (size2.width) style2 += `width: ${size2.width.toString().endsWith("%") ? size2.width : `${size2.width}px`}; `;
62180
- if (size2.height)
62181
- style2 += `height: ${size2.height.toString().endsWith("%") ? size2.height : `${size2.height}px`}; `;
62182
- return { style: style2 };
62183
- }
62184
- },
62185
- background: {
62186
- default: null,
62187
- renderDOM: (attrs) => {
62188
- if (!attrs.background) return {};
62189
- return {
62190
- style: `background-color: ${attrs.background}`
62191
- };
62192
- }
62193
- },
62194
- drawingContent: {
62195
- rendered: false
62196
- },
62197
- attributes: {
62198
- rendered: false
62199
- }
63151
+ },
63152
+ addNodeView() {
63153
+ return ({ node, editor, getPos, decorations }) => {
63154
+ const htmlAttributes = this.options.htmlAttributes;
63155
+ return new AutoPageNumberNodeView(node, getPos, decorations, editor, htmlAttributes);
62200
63156
  };
62201
63157
  },
62202
63158
  parseDOM() {
62203
- return [
62204
- {
62205
- tag: `div[data-type="${this.name}"]`
62206
- }
62207
- ];
63159
+ return [{ tag: 'span[data-id="auto-page-number"' }];
62208
63160
  },
62209
63161
  renderDOM({ htmlAttributes }) {
62210
- return ["div", Attribute2.mergeAttributes(this.options.htmlAttributes, htmlAttributes, { "data-type": this.name })];
63162
+ return ["span", Attribute2.mergeAttributes(this.options.htmlAttributes, htmlAttributes)];
62211
63163
  },
62212
63164
  addCommands() {
62213
63165
  return {
62214
63166
  /**
62215
- * Insert a horizontal rule
62216
- * @category Command
62217
- * @example
62218
- * editor.commands.insertHorizontalRule()
62219
- * @note Creates a visual separator between content sections
62220
- */
62221
- insertHorizontalRule: () => ({ commands: commands2 }) => {
62222
- return commands2.insertContent({
62223
- type: this.name,
62224
- attrs: {
62225
- horizontalRule: true,
62226
- size: { width: "100%", height: 2 },
62227
- background: "#e5e7eb"
62228
- }
62229
- });
62230
- },
62231
- /**
62232
- * Insert a content block
63167
+ * Insert an automatic page number
62233
63168
  * @category Command
62234
- * @param {ContentBlockConfig} config - Block configuration
62235
- * @example
62236
- * // Insert a spacer block
62237
- * editor.commands.insertContentBlock({ size: { height: 20 } })
62238
- *
63169
+ * @returns {Function} Command function
62239
63170
  * @example
62240
- * // Insert a colored divider
62241
- * editor.commands.insertContentBlock({
62242
- * size: { width: '50%', height: 3 },
62243
- * background: '#3b82f6'
62244
- * })
62245
- * @note Used for spacing, dividers, and special inline content
63171
+ * editor.commands.addAutoPageNumber()
63172
+ * @note Only works in header/footer contexts
62246
63173
  */
62247
- insertContentBlock: (config2) => ({ commands: commands2 }) => {
62248
- return commands2.insertContent({
62249
- type: this.name,
62250
- attrs: config2
62251
- });
63174
+ addAutoPageNumber: () => ({ tr, dispatch, state: state2, editor }) => {
63175
+ const { options } = editor;
63176
+ if (!options.isHeaderOrFooter) return false;
63177
+ const { schema } = state2;
63178
+ const pageNumberType = schema?.nodes?.["page-number"];
63179
+ if (!pageNumberType) return false;
63180
+ const pageNumberNodeJSON = { type: "page-number" };
63181
+ const pageNumberNode = schema.nodeFromJSON(pageNumberNodeJSON);
63182
+ if (dispatch) {
63183
+ tr.replaceSelectionWith(pageNumberNode, false);
63184
+ tr.setMeta("forceUpdatePagination", true);
63185
+ }
63186
+ return true;
62252
63187
  }
62253
63188
  };
63189
+ },
63190
+ addShortcuts() {
63191
+ return {
63192
+ "Mod-Shift-alt-p": () => this.editor.commands.addAutoPageNumber()
63193
+ };
62254
63194
  }
62255
63195
  });
62256
- class StructuredContentViewBase {
62257
- constructor(props) {
62258
- __publicField$1(this, "node");
62259
- __publicField$1(this, "view");
62260
- __publicField$1(this, "getPos");
62261
- __publicField$1(this, "decorations");
62262
- __publicField$1(this, "innerDecorations");
62263
- __publicField$1(this, "editor");
62264
- __publicField$1(this, "extension");
62265
- __publicField$1(this, "htmlAttributes");
62266
- __publicField$1(this, "root");
62267
- __publicField$1(this, "isDragging", false);
62268
- this.node = props.node;
62269
- this.view = props.editor.view;
62270
- this.getPos = props.getPos;
62271
- this.decorations = props.decorations;
62272
- this.innerDecorations = props.innerDecorations;
62273
- this.editor = props.editor;
62274
- this.extension = props.extension;
62275
- this.htmlAttributes = props.htmlAttributes;
62276
- this.mount(props);
62277
- }
62278
- mount() {
62279
- return;
62280
- }
62281
- get dom() {
62282
- return this.root;
62283
- }
62284
- get contentDOM() {
62285
- return null;
62286
- }
62287
- update(node, decorations, innerDecorations) {
62288
- if (node.type !== this.node.type) {
62289
- return false;
62290
- }
62291
- this.node = node;
62292
- this.decorations = decorations;
62293
- this.innerDecorations = innerDecorations;
62294
- this.updateHTMLAttributes();
62295
- return true;
62296
- }
62297
- stopEvent(event) {
62298
- if (!this.dom) return false;
62299
- const target = event.target;
62300
- const isInElement = this.dom.contains(target) && !this.contentDOM?.contains(target);
62301
- if (!isInElement) return false;
62302
- const isDragEvent = event.type.startsWith("drag");
62303
- const isDropEvent = event.type === "drop";
62304
- const isInput = ["INPUT", "BUTTON", "SELECT", "TEXTAREA"].includes(target.tagName) || target.isContentEditable;
62305
- if (isInput && !isDropEvent && !isDragEvent) return true;
62306
- const { isEditable } = this.editor;
62307
- const { isDragging } = this;
62308
- const isDraggable = !!this.node.type.spec.draggable;
62309
- const isSelectable = NodeSelection.isSelectable(this.node);
62310
- const isCopyEvent = event.type === "copy";
62311
- const isPasteEvent = event.type === "paste";
62312
- const isCutEvent = event.type === "cut";
62313
- const isClickEvent = event.type === "mousedown";
62314
- if (!isDraggable && isSelectable && isDragEvent && event.target === this.dom) {
62315
- event.preventDefault();
62316
- }
62317
- if (isDraggable && isDragEvent && !isDragging && event.target === this.dom) {
62318
- event.preventDefault();
62319
- return false;
62320
- }
62321
- if (isDraggable && isEditable && !isDragging && isClickEvent) {
62322
- const dragHandle = target.closest("[data-drag-handle]");
62323
- const isValidDragHandle = dragHandle && (this.dom === dragHandle || this.dom.contains(dragHandle));
62324
- if (isValidDragHandle) {
62325
- this.isDragging = true;
62326
- document.addEventListener(
62327
- "dragend",
62328
- () => {
62329
- this.isDragging = false;
62330
- },
62331
- { once: true }
62332
- );
62333
- document.addEventListener(
62334
- "drop",
62335
- () => {
62336
- this.isDragging = false;
62337
- },
62338
- { once: true }
62339
- );
62340
- document.addEventListener(
62341
- "mouseup",
62342
- () => {
62343
- this.isDragging = false;
62344
- },
62345
- { once: true }
62346
- );
62347
- }
62348
- }
62349
- if (isDragging || isDropEvent || isCopyEvent || isPasteEvent || isCutEvent || isClickEvent && isSelectable) {
62350
- return false;
62351
- }
62352
- return true;
62353
- }
62354
- ignoreMutation(mutation) {
62355
- if (!this.dom || !this.contentDOM) return true;
62356
- if (this.node.isLeaf || this.node.isAtom) return true;
62357
- if (mutation.type === "selection") return false;
62358
- if (this.contentDOM === mutation.target && mutation.type === "attributes") return true;
62359
- if (this.contentDOM.contains(mutation.target)) return false;
62360
- return true;
62361
- }
62362
- destroy() {
62363
- this.dom.remove();
62364
- this.contentDOM?.remove();
62365
- }
62366
- updateAttributes(attrs) {
62367
- const pos = this.getPos();
62368
- if (typeof pos !== "number") {
62369
- return;
62370
- }
62371
- return this.view.dispatch(
62372
- this.view.state.tr.setNodeMarkup(pos, void 0, {
62373
- ...this.node.attrs,
62374
- ...attrs
62375
- })
62376
- );
62377
- }
62378
- updateHTMLAttributes() {
62379
- const { extensionService } = this.editor;
62380
- const { attributes } = extensionService;
62381
- const extensionAttrs = attributes.filter((i) => i.type === this.node.type.name);
62382
- this.htmlAttributes = Attribute2.getAttributesToRender(this.node, extensionAttrs);
62383
- }
62384
- createDragHandle() {
62385
- const dragHandle = document.createElement("span");
62386
- dragHandle.classList.add("sd-structured-content-draggable");
62387
- dragHandle.draggable = true;
62388
- dragHandle.contentEditable = "false";
62389
- dragHandle.dataset.dragHandle = "";
62390
- const textElement = document.createElement("span");
62391
- textElement.textContent = "Structured content";
62392
- dragHandle.append(textElement);
62393
- return dragHandle;
62394
- }
62395
- onDragStart(event) {
62396
- const { view } = this.editor;
62397
- const target = event.target;
62398
- const dragHandle = target.nodeType === 3 ? target.parentElement?.closest("[data-drag-handle]") : target.closest("[data-drag-handle]");
62399
- if (!this.dom || this.contentDOM?.contains(target) || !dragHandle) {
62400
- return;
62401
- }
62402
- let x = 0;
62403
- let y2 = 0;
62404
- if (this.dom !== dragHandle) {
62405
- const domBox = this.dom.getBoundingClientRect();
62406
- const handleBox = dragHandle.getBoundingClientRect();
62407
- const offsetX = event.offsetX ?? event.nativeEvent?.offsetX;
62408
- const offsetY = event.offsetY ?? event.nativeEvent?.offsetY;
62409
- x = handleBox.x - domBox.x + offsetX;
62410
- y2 = handleBox.y - domBox.y + offsetY;
62411
- }
62412
- event.dataTransfer?.setDragImage(this.dom, x, y2);
62413
- const pos = this.getPos();
62414
- if (typeof pos !== "number") {
62415
- return;
62416
- }
62417
- const selection = NodeSelection.create(view.state.doc, pos);
62418
- const transaction = view.state.tr.setSelection(selection);
62419
- view.dispatch(transaction);
62420
- }
62421
- }
62422
- class StructuredContentInlineView extends StructuredContentViewBase {
62423
- constructor(props) {
62424
- super(props);
62425
- }
62426
- mount() {
62427
- this.buildView();
62428
- }
62429
- get contentDOM() {
62430
- const contentElement = this.dom?.querySelector(`.${structuredContentInnerClass$1}`);
62431
- return contentElement || null;
62432
- }
62433
- createElement() {
62434
- const element = document.createElement("span");
62435
- element.classList.add(structuredContentClass$1);
62436
- element.setAttribute("data-structured-content", "");
62437
- const contentElement = document.createElement("span");
62438
- contentElement.classList.add(structuredContentInnerClass$1);
62439
- element.append(contentElement);
62440
- const domAttrs = Attribute2.mergeAttributes(this.htmlAttributes);
62441
- updateDOMAttributes(element, { ...domAttrs });
62442
- return { element, contentElement };
62443
- }
62444
- buildView() {
62445
- const { element } = this.createElement();
62446
- const dragHandle = this.createDragHandle();
62447
- element.prepend(dragHandle);
62448
- element.addEventListener("dragstart", (e) => this.onDragStart(e));
62449
- this.root = element;
62450
- }
62451
- updateView() {
62452
- const domAttrs = Attribute2.mergeAttributes(this.htmlAttributes);
62453
- updateDOMAttributes(this.dom, { ...domAttrs });
62454
- }
62455
- update(node, decorations, innerDecorations) {
62456
- const result = super.update(node, decorations, innerDecorations);
62457
- if (!result) return false;
62458
- this.updateView();
62459
- return true;
62460
- }
62461
- }
62462
- const structuredContentClass$1 = "sd-structured-content";
62463
- const structuredContentInnerClass$1 = "sd-structured-content__content";
62464
- const StructuredContent = Node$1.create({
62465
- name: "structuredContent",
62466
- group: "inline structuredContent",
63196
+ const TotalPageCount = Node$1.create({
63197
+ name: "total-page-number",
63198
+ group: "inline",
62467
63199
  inline: true,
62468
- content: "inline*",
62469
- isolating: true,
62470
- atom: false,
62471
- // false - has editable content.
62472
- draggable: true,
63200
+ atom: true,
63201
+ draggable: false,
63202
+ selectable: false,
63203
+ content: "text*",
62473
63204
  addOptions() {
62474
63205
  return {
62475
63206
  htmlAttributes: {
62476
- class: structuredContentClass$1,
62477
- "aria-label": "Structured content node"
63207
+ contenteditable: false,
63208
+ "data-id": "auto-total-pages",
63209
+ "aria-label": "Total page count node",
63210
+ class: "sd-editor-auto-total-pages"
62478
63211
  }
62479
63212
  };
62480
63213
  },
62481
63214
  addAttributes() {
62482
63215
  return {
62483
- id: {
63216
+ marksAsAttrs: {
62484
63217
  default: null,
62485
- parseDOM: (elem) => elem.getAttribute("data-id"),
62486
- renderDOM: (attrs) => {
62487
- if (!attrs.id) return {};
62488
- return { "data-id": attrs.id };
62489
- }
62490
- },
62491
- sdtPr: {
62492
63218
  rendered: false
62493
63219
  }
62494
63220
  };
62495
63221
  },
63222
+ addNodeView() {
63223
+ return ({ node, editor, getPos, decorations }) => {
63224
+ const htmlAttributes = this.options.htmlAttributes;
63225
+ return new AutoPageNumberNodeView(node, getPos, decorations, editor, htmlAttributes);
63226
+ };
63227
+ },
62496
63228
  parseDOM() {
62497
- return [{ tag: "span[data-structured-content]" }];
63229
+ return [{ tag: 'span[data-id="auto-total-pages"' }];
62498
63230
  },
62499
63231
  renderDOM({ htmlAttributes }) {
62500
- return [
62501
- "span",
62502
- Attribute2.mergeAttributes(this.options.htmlAttributes, htmlAttributes, {
62503
- "data-structured-content": ""
62504
- }),
62505
- 0
62506
- ];
63232
+ return ["span", Attribute2.mergeAttributes(this.options.htmlAttributes, htmlAttributes), 0];
62507
63233
  },
62508
- addNodeView() {
62509
- return (props) => {
62510
- return new StructuredContentInlineView({ ...props });
63234
+ addCommands() {
63235
+ return {
63236
+ /**
63237
+ * Insert total page count
63238
+ * @category Command
63239
+ * @returns {Function} Command function
63240
+ * @example
63241
+ * editor.commands.addTotalPageCount()
63242
+ * @note Only works in header/footer contexts
63243
+ */
63244
+ addTotalPageCount: () => ({ tr, dispatch, state: state2, editor }) => {
63245
+ const { options } = editor;
63246
+ if (!options.isHeaderOrFooter) return false;
63247
+ const { schema } = state2;
63248
+ const pageNumberType = schema.nodes?.["total-page-number"];
63249
+ if (!pageNumberType) return false;
63250
+ const currentPages = editor?.options?.parentEditor?.currentTotalPages || 1;
63251
+ const pageNumberNode = {
63252
+ type: "total-page-number",
63253
+ content: [{ type: "text", text: String(currentPages) }]
63254
+ };
63255
+ const pageNode = schema.nodeFromJSON(pageNumberNode);
63256
+ if (dispatch) {
63257
+ tr.replaceSelectionWith(pageNode, false);
63258
+ }
63259
+ return true;
63260
+ }
63261
+ };
63262
+ },
63263
+ addShortcuts() {
63264
+ return {
63265
+ "Mod-Shift-alt-c": () => this.editor.commands.addTotalPageCount()
62511
63266
  };
62512
63267
  }
62513
63268
  });
62514
- class StructuredContentBlockView extends StructuredContentViewBase {
62515
- constructor(props) {
62516
- super(props);
62517
- }
62518
- mount() {
62519
- this.buildView();
62520
- }
62521
- get contentDOM() {
62522
- const contentElement = this.dom?.querySelector(`.${structuredContentInnerClass}`);
62523
- return contentElement || null;
62524
- }
62525
- createElement() {
62526
- const element = document.createElement("div");
62527
- element.classList.add(structuredContentClass);
62528
- element.setAttribute("data-structured-content-block", "");
62529
- const contentElement = document.createElement("div");
62530
- contentElement.classList.add(structuredContentInnerClass);
62531
- element.append(contentElement);
62532
- const domAttrs = Attribute2.mergeAttributes(this.htmlAttributes);
62533
- updateDOMAttributes(element, { ...domAttrs });
62534
- return { element, contentElement };
62535
- }
62536
- buildView() {
62537
- const { element } = this.createElement();
62538
- const dragHandle = this.createDragHandle();
62539
- element.prepend(dragHandle);
62540
- element.addEventListener("dragstart", (e) => this.onDragStart(e));
62541
- this.root = element;
63269
+ const getNodeAttributes = (nodeName, editor) => {
63270
+ switch (nodeName) {
63271
+ case "page-number":
63272
+ return {
63273
+ text: editor.options.currentPageNumber || "1",
63274
+ className: "sd-editor-auto-page-number",
63275
+ dataId: "auto-page-number",
63276
+ ariaLabel: "Page number node"
63277
+ };
63278
+ case "total-page-number":
63279
+ return {
63280
+ text: editor.options.parentEditor?.currentTotalPages || "1",
63281
+ className: "sd-editor-auto-total-pages",
63282
+ dataId: "auto-total-pages",
63283
+ ariaLabel: "Total page count node"
63284
+ };
63285
+ default:
63286
+ return {};
62542
63287
  }
62543
- updateView() {
62544
- const domAttrs = Attribute2.mergeAttributes(this.htmlAttributes);
62545
- updateDOMAttributes(this.dom, { ...domAttrs });
63288
+ };
63289
+ class AutoPageNumberNodeView {
63290
+ constructor(node, getPos, decorations, editor, htmlAttributes = {}) {
63291
+ __privateAdd$1(this, _AutoPageNumberNodeView_instances);
63292
+ this.node = node;
63293
+ this.editor = editor;
63294
+ this.view = editor.view;
63295
+ this.getPos = getPos;
63296
+ this.editor = editor;
63297
+ this.dom = __privateMethod$1(this, _AutoPageNumberNodeView_instances, renderDom_fn).call(this, node, htmlAttributes);
62546
63298
  }
62547
- update(node, decorations, innerDecorations) {
62548
- const result = super.update(node, decorations, innerDecorations);
62549
- if (!result) return false;
62550
- this.updateView();
63299
+ update(node) {
63300
+ const incomingType = node?.type?.name;
63301
+ const currentType = this.node?.type?.name;
63302
+ if (!incomingType || incomingType !== currentType) return false;
63303
+ this.node = node;
62551
63304
  return true;
62552
63305
  }
62553
63306
  }
62554
- const structuredContentClass = "sd-structured-content-block";
62555
- const structuredContentInnerClass = "sd-structured-content-block__content";
62556
- const StructuredContentBlock = Node$1.create({
62557
- name: "structuredContentBlock",
62558
- group: "block structuredContent",
62559
- content: "block*",
63307
+ _AutoPageNumberNodeView_instances = /* @__PURE__ */ new WeakSet();
63308
+ renderDom_fn = function(node, htmlAttributes) {
63309
+ const attrs = getNodeAttributes(this.node.type.name, this.editor);
63310
+ const content = document.createTextNode(String(attrs.text));
63311
+ const nodeContent = document.createElement("span");
63312
+ nodeContent.className = attrs.className;
63313
+ nodeContent.setAttribute("data-id", attrs.dataId);
63314
+ nodeContent.setAttribute("aria-label", attrs.ariaLabel);
63315
+ const currentPos = this.getPos();
63316
+ const { styles, marks } = getMarksFromNeighbors(currentPos, this.view);
63317
+ __privateMethod$1(this, _AutoPageNumberNodeView_instances, scheduleUpdateNodeStyle_fn).call(this, currentPos, marks);
63318
+ Object.assign(nodeContent.style, styles);
63319
+ nodeContent.appendChild(content);
63320
+ Object.entries(htmlAttributes).forEach(([key2, value]) => {
63321
+ if (value) nodeContent.setAttribute(key2, value);
63322
+ });
63323
+ return nodeContent;
63324
+ };
63325
+ scheduleUpdateNodeStyle_fn = function(pos, marks) {
63326
+ setTimeout(() => {
63327
+ const { state: state2 } = this.editor;
63328
+ const { dispatch } = this.view;
63329
+ const node = state2.doc.nodeAt(pos);
63330
+ if (!node || node.isText) return;
63331
+ const currentMarks = node.attrs.marksAsAttrs || [];
63332
+ const newMarks = marks.map((m2) => ({ type: m2.type.name, attrs: m2.attrs }));
63333
+ const isEqual = JSON.stringify(currentMarks) === JSON.stringify(newMarks);
63334
+ if (isEqual) return;
63335
+ const newAttrs = {
63336
+ ...node.attrs,
63337
+ marksAsAttrs: newMarks
63338
+ };
63339
+ const tr = state2.tr.setNodeMarkup(pos, void 0, newAttrs);
63340
+ dispatch(tr);
63341
+ }, 0);
63342
+ };
63343
+ const getMarksFromNeighbors = (currentPos, view) => {
63344
+ const $pos = view.state.doc.resolve(currentPos);
63345
+ const styles = {};
63346
+ const marks = [];
63347
+ const before = $pos.nodeBefore;
63348
+ if (before) {
63349
+ Object.assign(styles, processMarks(before.marks));
63350
+ marks.push(...before.marks);
63351
+ }
63352
+ const after = $pos.nodeAfter;
63353
+ if (after) {
63354
+ Object.assign(styles, { ...styles, ...processMarks(after.marks) });
63355
+ marks.push(...after.marks);
63356
+ }
63357
+ return {
63358
+ styles,
63359
+ marks
63360
+ };
63361
+ };
63362
+ const processMarks = (marks) => {
63363
+ const styles = {};
63364
+ marks.forEach((mark) => {
63365
+ const { type: type2, attrs } = mark;
63366
+ switch (type2.name) {
63367
+ case "textStyle":
63368
+ if (attrs.fontFamily) styles["font-family"] = attrs.fontFamily;
63369
+ if (attrs.fontSize) styles["font-size"] = attrs.fontSize;
63370
+ if (attrs.color) styles["color"] = attrs.color;
63371
+ if (attrs.backgroundColor) styles["background-color"] = attrs.backgroundColor;
63372
+ break;
63373
+ case "bold":
63374
+ styles["font-weight"] = "bold";
63375
+ break;
63376
+ case "italic":
63377
+ styles["font-style"] = "italic";
63378
+ break;
63379
+ case "underline":
63380
+ styles["text-decoration"] = (styles["text-decoration"] || "") + " underline";
63381
+ break;
63382
+ case "strike":
63383
+ styles["text-decoration"] = (styles["text-decoration"] || "") + " line-through";
63384
+ break;
63385
+ default:
63386
+ if (attrs?.style) {
63387
+ Object.entries(attrs.style).forEach(([key2, value]) => {
63388
+ styles[key2] = value;
63389
+ });
63390
+ }
63391
+ break;
63392
+ }
63393
+ });
63394
+ return styles;
63395
+ };
63396
+ const ShapeContainer = Node$1.create({
63397
+ name: "shapeContainer",
63398
+ group: "block",
63399
+ content: "block+",
62560
63400
  isolating: true,
62561
- atom: false,
62562
- // false - has editable content.
62563
- draggable: true,
62564
63401
  addOptions() {
62565
63402
  return {
62566
63403
  htmlAttributes: {
62567
- class: structuredContentClass,
62568
- "aria-label": "Structured content block node"
63404
+ class: "sd-editor-shape-container",
63405
+ "aria-label": "Shape container node"
62569
63406
  }
62570
63407
  };
62571
63408
  },
62572
63409
  addAttributes() {
62573
63410
  return {
62574
- id: {
63411
+ fillcolor: {
63412
+ renderDOM: (attrs) => {
63413
+ if (!attrs.fillcolor) return {};
63414
+ return {
63415
+ style: `background-color: ${attrs.fillcolor}`
63416
+ };
63417
+ }
63418
+ },
63419
+ sdBlockId: {
62575
63420
  default: null,
62576
- parseDOM: (elem) => elem.getAttribute("data-id"),
63421
+ keepOnSplit: false,
63422
+ parseDOM: (elem) => elem.getAttribute("data-sd-block-id"),
62577
63423
  renderDOM: (attrs) => {
62578
- if (!attrs.id) return {};
62579
- return { "data-id": attrs.id };
63424
+ return attrs.sdBlockId ? { "data-sd-block-id": attrs.sdBlockId } : {};
62580
63425
  }
62581
63426
  },
62582
- sdtPr: {
63427
+ style: {
63428
+ renderDOM: (attrs) => {
63429
+ if (!attrs.style) return {};
63430
+ return {
63431
+ style: attrs.style
63432
+ };
63433
+ }
63434
+ },
63435
+ wrapAttributes: {
63436
+ rendered: false
63437
+ },
63438
+ attributes: {
62583
63439
  rendered: false
62584
63440
  }
62585
63441
  };
62586
63442
  },
62587
63443
  parseDOM() {
62588
- return [{ tag: "div[data-structured-content-block]" }];
63444
+ return [
63445
+ {
63446
+ tag: `div[data-type="${this.name}"]`
63447
+ }
63448
+ ];
62589
63449
  },
62590
63450
  renderDOM({ htmlAttributes }) {
62591
63451
  return [
62592
63452
  "div",
62593
- Attribute2.mergeAttributes(this.options.htmlAttributes, htmlAttributes, {
62594
- "data-structured-content-block": ""
62595
- }),
63453
+ Attribute2.mergeAttributes(this.options.htmlAttributes, htmlAttributes, { "data-type": this.name }),
62596
63454
  0
62597
63455
  ];
62598
- },
62599
- addNodeView() {
62600
- return (props) => {
62601
- return new StructuredContentBlockView({ ...props });
62602
- };
62603
63456
  }
62604
63457
  });
62605
- class DocumentSectionView {
62606
- constructor(node, getPos, decorations, editor) {
62607
- __privateAdd$1(this, _DocumentSectionView_instances);
62608
- this.node = node;
62609
- this.editor = editor;
62610
- this.decorations = decorations;
62611
- this.view = editor.view;
62612
- this.getPos = getPos;
62613
- __privateMethod$1(this, _DocumentSectionView_instances, init_fn3).call(this);
62614
- }
62615
- }
62616
- _DocumentSectionView_instances = /* @__PURE__ */ new WeakSet();
62617
- init_fn3 = function() {
62618
- const { attrs } = this.node;
62619
- const { id, title, description } = attrs;
62620
- this.dom = document.createElement("div");
62621
- this.dom.className = "sd-document-section-block";
62622
- this.dom.setAttribute("data-id", id);
62623
- this.dom.setAttribute("data-title", title);
62624
- this.dom.setAttribute("data-description", description);
62625
- this.dom.setAttribute("aria-label", "Document section");
62626
- __privateMethod$1(this, _DocumentSectionView_instances, addToolTip_fn).call(this);
62627
- this.contentDOM = document.createElement("div");
62628
- this.contentDOM.className = "sd-document-section-block-content";
62629
- this.contentDOM.setAttribute("contenteditable", "true");
62630
- this.dom.appendChild(this.contentDOM);
62631
- };
62632
- addToolTip_fn = function() {
62633
- const { title } = this.node.attrs;
62634
- this.infoDiv = document.createElement("div");
62635
- this.infoDiv.className = "sd-document-section-block-info";
62636
- const textSpan = document.createElement("span");
62637
- textSpan.textContent = title || "Document section";
62638
- this.infoDiv.appendChild(textSpan);
62639
- this.infoDiv.setAttribute("contenteditable", "false");
62640
- this.dom.appendChild(this.infoDiv);
62641
- };
62642
- const getAllSections = (editor) => {
62643
- if (!editor) return [];
62644
- const type2 = editor.schema.nodes.documentSection;
62645
- if (!type2) return [];
62646
- const sections = [];
62647
- const { state: state2 } = editor;
62648
- state2.doc.descendants((node, pos) => {
62649
- if (node.type.name === type2.name) {
62650
- sections.push({ node, pos });
62651
- }
62652
- });
62653
- return sections;
62654
- };
62655
- const exportSectionsToHTML = (editor) => {
62656
- const sections = getAllSections(editor);
62657
- const processedSections = /* @__PURE__ */ new Set();
62658
- const result = [];
62659
- sections.forEach(({ node }) => {
62660
- const { attrs } = node;
62661
- const { id, title, description } = attrs;
62662
- if (processedSections.has(id)) return;
62663
- processedSections.add(id);
62664
- const html = getHTMLFromNode(node, editor);
62665
- result.push({
62666
- id,
62667
- title,
62668
- description,
62669
- html
62670
- });
62671
- });
62672
- return result;
62673
- };
62674
- const getHTMLFromNode = (node, editor) => {
62675
- const tempDocument = document.implementation.createHTMLDocument();
62676
- const container = tempDocument.createElement("div");
62677
- const fragment = DOMSerializer.fromSchema(editor.schema).serializeFragment(node.content);
62678
- container.appendChild(fragment);
62679
- let html = container.innerHTML;
62680
- return html;
62681
- };
62682
- const exportSectionsToJSON = (editor) => {
62683
- const sections = getAllSections(editor);
62684
- const processedSections = /* @__PURE__ */ new Set();
62685
- const result = [];
62686
- sections.forEach(({ node }) => {
62687
- const { attrs } = node;
62688
- const { id, title, description } = attrs;
62689
- if (processedSections.has(id)) return;
62690
- processedSections.add(id);
62691
- result.push({
62692
- id,
62693
- title,
62694
- description,
62695
- content: node.toJSON()
62696
- });
62697
- });
62698
- return result;
62699
- };
62700
- const getLinkedSectionEditor = (id, options, editor) => {
62701
- const sections = getAllSections(editor);
62702
- const section = sections.find((s) => s.node.attrs.id === id);
62703
- if (!section) return null;
62704
- const child = editor.createChildEditor({
62705
- ...options,
62706
- onUpdate: ({ editor: childEditor, transaction }) => {
62707
- const isFromtLinkedParent = transaction.getMeta("fromLinkedParent");
62708
- if (isFromtLinkedParent) return;
62709
- const updatedContent = childEditor.state.doc.content;
62710
- const sectionNode = getAllSections(editor)?.find((s) => s.node.attrs.id === id);
62711
- if (!sectionNode) return;
62712
- const { pos, node } = sectionNode;
62713
- const newNode = node.type.create(node.attrs, updatedContent, node.marks);
62714
- const tr = editor.state.tr.replaceWith(pos, pos + node.nodeSize, newNode);
62715
- tr.setMeta("fromLinkedChild", true);
62716
- editor.view.dispatch(tr);
62717
- }
62718
- });
62719
- editor.on("update", ({ transaction }) => {
62720
- const isFromLinkedChild = transaction.getMeta("fromLinkedChild");
62721
- if (isFromLinkedChild) return;
62722
- const sectionNode = getAllSections(editor)?.find((s) => s.node.attrs.id === id);
62723
- if (!sectionNode) return;
62724
- const sectionContent = sectionNode.node.content;
62725
- const json = {
62726
- type: "doc",
62727
- content: sectionContent.content.map((node) => node.toJSON())
62728
- };
62729
- const childTr = child.state.tr;
62730
- childTr.setMeta("fromLinkedParent", true);
62731
- childTr.replaceWith(0, child.state.doc.content.size, child.schema.nodeFromJSON(json));
62732
- child.view.dispatch(childTr);
62733
- });
62734
- return child;
62735
- };
62736
- const SectionHelpers = {
62737
- getAllSections,
62738
- exportSectionsToHTML,
62739
- exportSectionsToJSON,
62740
- getLinkedSectionEditor
62741
- };
62742
- const DocumentSection = Node$1.create({
62743
- name: "documentSection",
63458
+ const ShapeTextbox = Node$1.create({
63459
+ name: "shapeTextbox",
62744
63460
  group: "block",
62745
- content: "block*",
62746
- atom: true,
63461
+ content: "paragraph* block*",
62747
63462
  isolating: true,
62748
63463
  addOptions() {
62749
63464
  return {
62750
63465
  htmlAttributes: {
62751
- class: "sd-document-section-block",
62752
- "aria-label": "Structured content block"
63466
+ class: "sd-editor-shape-textbox",
63467
+ "aria-label": "Shape textbox node"
62753
63468
  }
62754
63469
  };
62755
63470
  },
62756
- parseDOM() {
62757
- return [
62758
- {
62759
- tag: "div.sd-document-section-block",
62760
- priority: 60
62761
- }
62762
- ];
62763
- },
62764
- renderDOM({ htmlAttributes }) {
62765
- return ["div", Attribute2.mergeAttributes(this.options.htmlAttributes, htmlAttributes), 0];
62766
- },
62767
63471
  addAttributes() {
62768
63472
  return {
62769
- id: {},
62770
63473
  sdBlockId: {
62771
63474
  default: null,
62772
63475
  keepOnSplit: false,
@@ -62775,212 +63478,131 @@ const DocumentSection = Node$1.create({
62775
63478
  return attrs.sdBlockId ? { "data-sd-block-id": attrs.sdBlockId } : {};
62776
63479
  }
62777
63480
  },
62778
- title: {},
62779
- description: {},
62780
- sectionType: {},
62781
- isLocked: { default: false }
63481
+ attributes: {
63482
+ rendered: false
63483
+ }
62782
63484
  };
62783
63485
  },
62784
- addNodeView() {
62785
- return ({ node, editor, getPos, decorations }) => {
62786
- return new DocumentSectionView(node, getPos, decorations, editor);
63486
+ parseDOM() {
63487
+ return [
63488
+ {
63489
+ tag: `div[data-type="${this.name}"]`
63490
+ }
63491
+ ];
63492
+ },
63493
+ renderDOM({ htmlAttributes }) {
63494
+ return [
63495
+ "div",
63496
+ Attribute2.mergeAttributes(this.options.htmlAttributes, htmlAttributes, { "data-type": this.name }),
63497
+ 0
63498
+ ];
63499
+ }
63500
+ });
63501
+ const ContentBlock = Node$1.create({
63502
+ name: "contentBlock",
63503
+ group: "inline",
63504
+ content: "",
63505
+ isolating: true,
63506
+ atom: true,
63507
+ inline: true,
63508
+ addOptions() {
63509
+ return {
63510
+ htmlAttributes: {
63511
+ contenteditable: false
63512
+ }
62787
63513
  };
62788
63514
  },
62789
- addCommands() {
63515
+ addAttributes() {
62790
63516
  return {
62791
- /**
62792
- * Create a lockable content section
62793
- * @category Command
62794
- * @param {SectionCreate} [options={}] - Section configuration
62795
- * @example
62796
- * editor.commands.createDocumentSection({
62797
- * id: 1,
62798
- * title: 'Terms & Conditions',
62799
- * isLocked: true,
62800
- * html: '<p>Legal content...</p>'
62801
- * })
62802
- */
62803
- createDocumentSection: (options = {}) => ({ tr, state: state2, dispatch, editor }) => {
62804
- const { selection } = state2;
62805
- let { from: from2, to } = selection;
62806
- let content = selection.content().content;
62807
- const { html: optionsHTML, json: optionsJSON } = options;
62808
- if (optionsHTML) {
62809
- const html = htmlHandler(optionsHTML, this.editor);
62810
- const doc2 = DOMParser$1.fromSchema(this.editor.schema).parse(html);
62811
- content = doc2.content;
62812
- }
62813
- if (optionsJSON) {
62814
- content = this.editor.schema.nodeFromJSON(optionsJSON);
62815
- }
62816
- if (!content?.content?.length) {
62817
- content = this.editor.schema.nodeFromJSON({ type: "paragraph", content: [] });
62818
- }
62819
- if (!options.id) {
62820
- const allSections = SectionHelpers.getAllSections(editor);
62821
- options.id = allSections.length + 1;
62822
- }
62823
- if (!options.title) {
62824
- options.title = "Document section";
62825
- }
62826
- const node = this.type.createAndFill(options, content);
62827
- if (!node) return false;
62828
- const isAlreadyInSdtBlock = findParentNode((node2) => node2.type.name === "documentSection")(selection);
62829
- if (isAlreadyInSdtBlock && isAlreadyInSdtBlock.node) {
62830
- const insertPos2 = isAlreadyInSdtBlock.pos + isAlreadyInSdtBlock.node.nodeSize;
62831
- from2 = insertPos2;
62832
- to = insertPos2;
62833
- }
62834
- tr.replaceRangeWith(from2, to, node);
62835
- const nodeEnd = from2 + node.nodeSize;
62836
- let shouldInsertParagraph = true;
62837
- let insertPos = nodeEnd;
62838
- if (nodeEnd >= tr.doc.content.size) {
62839
- insertPos = tr.doc.content.size;
62840
- if (insertPos > 0) {
62841
- const $endPos = tr.doc.resolve(insertPos);
62842
- if ($endPos.nodeBefore && $endPos.nodeBefore.type.name === "paragraph") {
62843
- shouldInsertParagraph = false;
62844
- }
62845
- }
62846
- }
62847
- if (shouldInsertParagraph) {
62848
- const emptyParagraph = tr.doc.type.schema.nodes.paragraph.create();
62849
- tr.insert(insertPos, emptyParagraph);
62850
- }
62851
- if (dispatch) {
62852
- tr.setMeta("documentSection", { action: "create" });
62853
- dispatch(tr);
62854
- setTimeout(() => {
62855
- try {
62856
- const currentState = editor.state;
62857
- const docSize = currentState.doc.content.size;
62858
- let targetPos = from2 + node.nodeSize;
62859
- if (shouldInsertParagraph) {
62860
- targetPos += 1;
62861
- }
62862
- targetPos = Math.min(targetPos, docSize);
62863
- if (targetPos < docSize && targetPos > 0) {
62864
- const newSelection = Selection.near(currentState.doc.resolve(targetPos));
62865
- const newTr = currentState.tr.setSelection(newSelection);
62866
- editor.view.dispatch(newTr);
62867
- }
62868
- } catch (e) {
62869
- console.warn("Could not set delayed selection:", e);
62870
- }
62871
- }, 0);
63517
+ horizontalRule: {
63518
+ default: false,
63519
+ renderDOM: ({ horizontalRule }) => {
63520
+ if (!horizontalRule) return {};
63521
+ return { "data-horizontal-rule": "true" };
62872
63522
  }
62873
- return true;
62874
63523
  },
62875
- /**
62876
- * Remove section wrapper at cursor, preserving its content
62877
- * @category Command
62878
- * @example
62879
- * editor.commands.removeSectionAtSelection()
62880
- * @note Content stays in document, only section wrapper is removed
62881
- */
62882
- removeSectionAtSelection: () => ({ tr, dispatch }) => {
62883
- const sdtNode = findParentNode((node2) => node2.type.name === "documentSection")(tr.selection);
62884
- if (!sdtNode) return false;
62885
- const { node, pos } = sdtNode;
62886
- const nodeStart = pos;
62887
- const nodeEnd = nodeStart + node.nodeSize;
62888
- const contentToPreserve = node.content;
62889
- tr.delete(nodeStart, nodeEnd);
62890
- if (contentToPreserve.size > 0) {
62891
- tr.insert(nodeStart, contentToPreserve);
62892
- }
62893
- const newPos = Math.min(nodeStart, tr.doc.content.size);
62894
- tr.setSelection(Selection.near(tr.doc.resolve(newPos)));
62895
- if (dispatch) {
62896
- tr.setMeta("documentSection", { action: "delete" });
62897
- dispatch(tr);
63524
+ size: {
63525
+ default: null,
63526
+ renderDOM: ({ size: size2 }) => {
63527
+ if (!size2) return {};
63528
+ let style2 = "";
63529
+ if (size2.top) style2 += `top: ${size2.top}px; `;
63530
+ if (size2.left) style2 += `left: ${size2.left}px; `;
63531
+ if (size2.width) style2 += `width: ${size2.width.toString().endsWith("%") ? size2.width : `${size2.width}px`}; `;
63532
+ if (size2.height)
63533
+ style2 += `height: ${size2.height.toString().endsWith("%") ? size2.height : `${size2.height}px`}; `;
63534
+ return { style: style2 };
62898
63535
  }
62899
- return true;
62900
63536
  },
62901
- /**
62902
- * Delete section and all its content
62903
- * @category Command
62904
- * @param {number} id - Section to delete
62905
- * @example
62906
- * editor.commands.removeSectionById(123)
62907
- */
62908
- removeSectionById: (id) => ({ tr, dispatch }) => {
62909
- const sections = SectionHelpers.getAllSections(this.editor);
62910
- const sectionToRemove = sections.find(({ node: node2 }) => node2.attrs.id === id);
62911
- if (!sectionToRemove) return false;
62912
- const { pos, node } = sectionToRemove;
62913
- const nodeStart = pos;
62914
- const nodeEnd = nodeStart + node.nodeSize;
62915
- tr.delete(nodeStart, nodeEnd);
62916
- if (dispatch) {
62917
- tr.setMeta("documentSection", { action: "delete", id });
62918
- dispatch(tr);
63537
+ background: {
63538
+ default: null,
63539
+ renderDOM: (attrs) => {
63540
+ if (!attrs.background) return {};
63541
+ return {
63542
+ style: `background-color: ${attrs.background}`
63543
+ };
62919
63544
  }
62920
- return true;
62921
63545
  },
63546
+ drawingContent: {
63547
+ rendered: false
63548
+ },
63549
+ attributes: {
63550
+ rendered: false
63551
+ }
63552
+ };
63553
+ },
63554
+ parseDOM() {
63555
+ return [
63556
+ {
63557
+ tag: `div[data-type="${this.name}"]`
63558
+ }
63559
+ ];
63560
+ },
63561
+ renderDOM({ htmlAttributes }) {
63562
+ return ["div", Attribute2.mergeAttributes(this.options.htmlAttributes, htmlAttributes, { "data-type": this.name })];
63563
+ },
63564
+ addCommands() {
63565
+ return {
62922
63566
  /**
62923
- * Lock section against edits
63567
+ * Insert a horizontal rule
62924
63568
  * @category Command
62925
- * @param {number} id - Section to lock
62926
63569
  * @example
62927
- * editor.commands.lockSectionById(123)
63570
+ * editor.commands.insertHorizontalRule()
63571
+ * @note Creates a visual separator between content sections
62928
63572
  */
62929
- lockSectionById: (id) => ({ tr, dispatch }) => {
62930
- const sections = SectionHelpers.getAllSections(this.editor);
62931
- const sectionToLock = sections.find(({ node }) => node.attrs.id === id);
62932
- if (!sectionToLock) return false;
62933
- tr.setNodeMarkup(sectionToLock.pos, null, { ...sectionToLock.node.attrs, isLocked: true });
62934
- if (dispatch) {
62935
- tr.setMeta("documentSection", { action: "lock", id });
62936
- dispatch(tr);
62937
- }
62938
- return true;
63573
+ insertHorizontalRule: () => ({ commands: commands2 }) => {
63574
+ return commands2.insertContent({
63575
+ type: this.name,
63576
+ attrs: {
63577
+ horizontalRule: true,
63578
+ size: { width: "100%", height: 2 },
63579
+ background: "#e5e7eb"
63580
+ }
63581
+ });
62939
63582
  },
62940
63583
  /**
62941
- * Modify section attributes or content
63584
+ * Insert a content block
62942
63585
  * @category Command
62943
- * @param {SectionUpdate} options - Changes to apply
63586
+ * @param {ContentBlockConfig} config - Block configuration
62944
63587
  * @example
62945
- * editor.commands.updateSectionById({ id: 123, attrs: { isLocked: false } })
62946
- * editor.commands.updateSectionById({ id: 123, html: '<p>New content</p>' })
62947
- * editor.commands.updateSectionById({
62948
- * id: 123,
62949
- * html: '<p>Updated</p>',
62950
- * attrs: { title: 'New Title' }
63588
+ * // Insert a spacer block
63589
+ * editor.commands.insertContentBlock({ size: { height: 20 } })
63590
+ *
63591
+ * @example
63592
+ * // Insert a colored divider
63593
+ * editor.commands.insertContentBlock({
63594
+ * size: { width: '50%', height: 3 },
63595
+ * background: '#3b82f6'
62951
63596
  * })
63597
+ * @note Used for spacing, dividers, and special inline content
62952
63598
  */
62953
- updateSectionById: ({ id, html, json, attrs }) => ({ tr, dispatch, editor }) => {
62954
- const sections = SectionHelpers.getAllSections(editor || this.editor);
62955
- const sectionToUpdate = sections.find(({ node: node2 }) => node2.attrs.id === id);
62956
- if (!sectionToUpdate) return false;
62957
- const { pos, node } = sectionToUpdate;
62958
- let newContent = null;
62959
- if (html) {
62960
- const htmlDoc = htmlHandler(html, editor || this.editor);
62961
- const doc2 = DOMParser$1.fromSchema((editor || this.editor).schema).parse(htmlDoc);
62962
- newContent = doc2.content;
62963
- }
62964
- if (json) {
62965
- newContent = (editor || this.editor).schema.nodeFromJSON(json);
62966
- }
62967
- if (!newContent) {
62968
- newContent = node.content;
62969
- }
62970
- const updatedNode = node.type.create({ ...node.attrs, ...attrs }, newContent, node.marks);
62971
- tr.replaceWith(pos, pos + node.nodeSize, updatedNode);
62972
- if (dispatch) {
62973
- tr.setMeta("documentSection", { action: "update", id, attrs });
62974
- dispatch(tr);
62975
- }
62976
- return true;
63599
+ insertContentBlock: (config2) => ({ commands: commands2 }) => {
63600
+ return commands2.insertContent({
63601
+ type: this.name,
63602
+ attrs: config2
63603
+ });
62977
63604
  }
62978
63605
  };
62979
- },
62980
- addHelpers() {
62981
- return {
62982
- ...SectionHelpers
62983
- };
62984
63606
  }
62985
63607
  });
62986
63608
  const { findChildren } = helpers;
@@ -69548,7 +70170,8 @@ const nodeResizer = (nodeNames = ["image"], editor) => {
69548
70170
  const prevSelection = prevState.selection;
69549
70171
  if (selection.from !== prevSelection.from || selection.to !== prevSelection.to) {
69550
70172
  setTimeout(() => {
69551
- const selectedResizableWrapper = document.querySelector(".sd-editor-resizable-wrapper");
70173
+ const searchRoot = editorView?.dom;
70174
+ const selectedResizableWrapper = searchRoot?.querySelector(".sd-editor-resizable-wrapper");
69552
70175
  if (selectedResizableWrapper) {
69553
70176
  showResizeHandles(view2, selectedResizableWrapper);
69554
70177
  } else {
@@ -69829,6 +70452,7 @@ const getStarterExtensions = () => {
69829
70452
  Search,
69830
70453
  StructuredContent,
69831
70454
  StructuredContentBlock,
70455
+ StructuredContentCommands,
69832
70456
  DocumentSection,
69833
70457
  NodeResizer,
69834
70458
  CustomSelection,
@@ -83084,7 +83708,7 @@ class SuperToolbar extends EventEmitter2 {
83084
83708
  if (!argument) return;
83085
83709
  item.onActivate({ zoom: argument });
83086
83710
  this.emit("superdoc-command", { item, argument });
83087
- const layers = document.querySelector(this.superdoc.config.selector)?.querySelector(".layers");
83711
+ const layers = this.superdoc.element?.querySelector(".layers");
83088
83712
  if (!layers) return;
83089
83713
  const isMobileDevice = typeof screen.orientation !== "undefined";
83090
83714
  const isSmallScreen = window.matchMedia("(max-width: 834px)").matches;
@@ -84146,14 +84770,6 @@ function getStructureFromResolvedPos(state2, pos) {
84146
84770
  return null;
84147
84771
  }
84148
84772
  }
84149
- const shouldBypassContextMenu = (event) => {
84150
- if (!event) return false;
84151
- if (event.ctrlKey || event.metaKey) {
84152
- return true;
84153
- }
84154
- 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;
84155
- return Boolean(isKeyboardInvocation);
84156
- };
84157
84773
  const isModuleEnabled = (editorOptions, moduleName) => {
84158
84774
  switch (moduleName) {
84159
84775
  case "ai":