@harbour-enterprises/superdoc 0.22.0-next.9 → 0.22.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/dist/chunks/{PdfViewer-HN-tp5RN.es.js → PdfViewer-CBmOiHsj.es.js} +1 -1
  2. package/dist/chunks/{PdfViewer-DyWe33pN.cjs → PdfViewer-D2BPnEBG.cjs} +1 -1
  3. package/dist/chunks/{index-BeVpZc19.cjs → index-B0CI50e4.cjs} +3 -3
  4. package/dist/chunks/{index-ir6efMuz.es.js → index-BSDcfSFc.es.js} +3 -3
  5. package/dist/chunks/{super-editor.es-BwqYS285.es.js → super-editor.es-BW1T3N2-.es.js} +1586 -1278
  6. package/dist/chunks/{super-editor.es-CKfdmK-8.cjs → super-editor.es-DOOwW-Iv.cjs} +1586 -1278
  7. package/dist/core/types/index.d.ts.map +1 -1
  8. package/dist/style.css +1 -0
  9. package/dist/super-editor/ai-writer.es.js +2 -2
  10. package/dist/super-editor/chunks/{converter-BgedUNCW.js → converter-UuZxU-p8.js} +154 -108
  11. package/dist/super-editor/chunks/{docx-zipper-ByLK3trM.js → docx-zipper-Bss48sB0.js} +1 -1
  12. package/dist/super-editor/chunks/{editor-CFqh_xBx.js → editor--v3HbUU0.js} +1437 -1173
  13. package/dist/super-editor/chunks/{toolbar-DdfyWgZF.js → toolbar-BeXlvdV8.js} +2 -2
  14. package/dist/super-editor/converter.es.js +1 -1
  15. package/dist/super-editor/docx-zipper.es.js +2 -2
  16. package/dist/super-editor/editor.es.js +3 -3
  17. package/dist/super-editor/file-zipper.es.js +1 -1
  18. package/dist/super-editor/src/core/helpers/generateDocxRandomId.d.ts +5 -0
  19. package/dist/super-editor/src/extensions/index.d.ts +2 -1
  20. package/dist/super-editor/src/extensions/structured-content/index.d.ts +1 -0
  21. package/dist/super-editor/src/extensions/structured-content/structured-content-commands.d.ts +67 -0
  22. package/dist/super-editor/src/extensions/structured-content/structuredContentHelpers/getStructuredContentBlockTags.d.ts +6 -0
  23. package/dist/super-editor/src/extensions/structured-content/structuredContentHelpers/getStructuredContentInlineTags.d.ts +6 -0
  24. package/dist/super-editor/src/extensions/structured-content/structuredContentHelpers/getStructuredContentTags.d.ts +6 -0
  25. package/dist/super-editor/src/extensions/structured-content/structuredContentHelpers/getStructuredContentTagsById.d.ts +7 -0
  26. package/dist/super-editor/src/extensions/structured-content/structuredContentHelpers/index.d.ts +4 -0
  27. package/dist/super-editor/style.css +1 -0
  28. package/dist/super-editor/super-editor.es.js +7 -7
  29. package/dist/super-editor/toolbar.es.js +2 -2
  30. package/dist/super-editor.cjs +1 -1
  31. package/dist/super-editor.es.js +1 -1
  32. package/dist/superdoc.cjs +2 -2
  33. package/dist/superdoc.es.js +2 -2
  34. package/dist/superdoc.umd.js +1587 -1279
  35. package/dist/superdoc.umd.js.map +1 -1
  36. package/package.json +1 -1
@@ -22730,6 +22730,10 @@
22730
22730
  }
22731
22731
  return id.join("");
22732
22732
  }
22733
+ function generateRandomSigned32BitIntStrId() {
22734
+ const val = Math.floor(Math.random() * 2147483647);
22735
+ return val.toString();
22736
+ }
22733
22737
  function generateRandom32BitHex() {
22734
22738
  const val = Math.floor(Math.random() * 2147483647);
22735
22739
  return val.toString(16).toUpperCase().padStart(8, "0");
@@ -30308,6 +30312,7 @@ Please report this to https://github.com/markedjs/marked.`, e) {
30308
30312
  generateDocxListAttributes,
30309
30313
  generateDocxRandomId,
30310
30314
  generateRandom32BitHex,
30315
+ generateRandomSigned32BitIntStrId,
30311
30316
  getActiveFormatting,
30312
30317
  getExtensionConfigField,
30313
30318
  getMarkRange,
@@ -35902,6 +35907,9 @@ Please report this to https://github.com/markedjs/marked.`, e) {
35902
35907
  const node = nodes[0];
35903
35908
  const sdtPr = node.elements.find((el) => el.name === "w:sdtPr");
35904
35909
  const sdtContent = node.elements.find((el) => el.name === "w:sdtContent");
35910
+ const id = sdtPr?.elements?.find((el) => el.name === "w:id");
35911
+ const tag = sdtPr?.elements?.find((el) => el.name === "w:tag");
35912
+ const alias = sdtPr?.elements?.find((el) => el.name === "w:alias");
35905
35913
  if (!sdtContent) {
35906
35914
  return null;
35907
35915
  }
@@ -35913,15 +35921,16 @@ Please report this to https://github.com/markedjs/marked.`, e) {
35913
35921
  nodes: sdtContent.elements,
35914
35922
  path: [...params2.path || [], sdtContent]
35915
35923
  });
35916
- let sdtContentType = "structuredContent";
35917
- if (paragraph || table) {
35918
- sdtContentType = "structuredContentBlock";
35919
- }
35924
+ const isBlockNode2 = paragraph || table;
35925
+ const sdtContentType = isBlockNode2 ? "structuredContentBlock" : "structuredContent";
35920
35926
  let result = {
35921
35927
  type: sdtContentType,
35922
35928
  content: translatedContent,
35923
35929
  marks,
35924
35930
  attrs: {
35931
+ id: id?.attributes?.["w:val"] || null,
35932
+ tag: tag?.attributes?.["w:val"] || null,
35933
+ alias: alias?.attributes?.["w:val"] || null,
35925
35934
  sdtPr
35926
35935
  }
35927
35936
  };
@@ -38179,21 +38188,55 @@ Please report this to https://github.com/markedjs/marked.`, e) {
38179
38188
  };
38180
38189
  function translateStructuredContent(params2) {
38181
38190
  const { node } = params2;
38182
- const { attrs = {} } = node;
38183
38191
  const childContent = translateChildNodes({ ...params2, nodes: node.content });
38184
- const nodeElements = [
38185
- {
38186
- name: "w:sdtContent",
38187
- elements: childContent
38188
- }
38189
- ];
38190
- nodeElements.unshift(attrs.sdtPr);
38192
+ const sdtContent = { name: "w:sdtContent", elements: childContent };
38193
+ const sdtPr = generateSdtPrTagForStructuredContent({ node });
38194
+ const nodeElements = [sdtPr, sdtContent];
38191
38195
  const result = {
38192
38196
  name: "w:sdt",
38193
38197
  elements: nodeElements
38194
38198
  };
38195
38199
  return result;
38196
38200
  }
38201
+ function generateSdtPrTagForStructuredContent({ node }) {
38202
+ const { attrs = {} } = node;
38203
+ const id = {
38204
+ name: "w:id",
38205
+ type: "element",
38206
+ attributes: { "w:val": attrs.id }
38207
+ };
38208
+ const alias = {
38209
+ name: "w:alias",
38210
+ type: "element",
38211
+ attributes: { "w:val": attrs.alias }
38212
+ };
38213
+ const tag = {
38214
+ name: "w:tag",
38215
+ type: "element",
38216
+ attributes: { "w:val": attrs.tag }
38217
+ };
38218
+ const resultElements = [];
38219
+ if (attrs.id) resultElements.push(id);
38220
+ if (attrs.alias) resultElements.push(alias);
38221
+ if (attrs.tag) resultElements.push(tag);
38222
+ if (attrs.sdtPr) {
38223
+ const elements = attrs.sdtPr.elements || [];
38224
+ const elementsToExclude = ["w:id", "w:alias", "w:tag"];
38225
+ const restElements = elements.filter((el) => !elementsToExclude.includes(el.name));
38226
+ const result2 = {
38227
+ name: "w:sdtPr",
38228
+ type: "element",
38229
+ elements: [...resultElements, ...restElements]
38230
+ };
38231
+ return result2;
38232
+ }
38233
+ const result = {
38234
+ name: "w:sdtPr",
38235
+ type: "element",
38236
+ elements: resultElements
38237
+ };
38238
+ return result;
38239
+ }
38197
38240
  const XML_NODE_NAME$3 = "w:sdt";
38198
38241
  const SD_NODE_NAME$3 = ["fieldAnnotation", "structuredContent", "structuredContentBlock", "documentSection"];
38199
38242
  const validXmlAttributes$3 = [];
@@ -39254,7 +39297,7 @@ Please report this to https://github.com/markedjs/marked.`, e) {
39254
39297
  const pict = {
39255
39298
  name: "w:pict",
39256
39299
  attributes: {
39257
- "w14:anchorId": Math.floor(Math.random() * 4294967295).toString()
39300
+ "w14:anchorId": generateRandomSigned32BitIntStrId()
39258
39301
  },
39259
39302
  elements: [shape]
39260
39303
  };
@@ -39321,7 +39364,7 @@ Please report this to https://github.com/markedjs/marked.`, e) {
39321
39364
  const pict = {
39322
39365
  name: "w:pict",
39323
39366
  attributes: {
39324
- "w14:anchorId": Math.floor(Math.random() * 4294967295).toString()
39367
+ "w14:anchorId": generateRandomSigned32BitIntStrId()
39325
39368
  },
39326
39369
  elements: [rect]
39327
39370
  };
@@ -39368,7 +39411,8 @@ Please report this to https://github.com/markedjs/marked.`, e) {
39368
39411
  }
39369
39412
  if (elements) {
39370
39413
  if (name === "w:instrText") {
39371
- tags.push(elements[0].text);
39414
+ const textContent2 = (elements || []).map((child) => typeof child?.text === "string" ? child.text : "").join("");
39415
+ tags.push(__privateMethod$2(this, _DocxExporter_instances, replaceSpecialCharacters_fn).call(this, textContent2));
39372
39416
  } else if (name === "w:t" || name === "w:delText" || name === "wp:posOffset") {
39373
39417
  try {
39374
39418
  let text = String(elements[0].text);
@@ -40794,7 +40838,7 @@ Please report this to https://github.com/markedjs/marked.`, e) {
40794
40838
  gutter: "0"
40795
40839
  })
40796
40840
  });
40797
- function ensureSectionProperties(bodyNode, converter) {
40841
+ function ensureSectionProperties(bodyNode) {
40798
40842
  if (!bodyNode.elements) bodyNode.elements = [];
40799
40843
  let sectPr = bodyNode.elements.find((el) => el.name === "w:sectPr");
40800
40844
  if (!sectPr) {
@@ -41162,7 +41206,7 @@ Please report this to https://github.com/markedjs/marked.`, e) {
41162
41206
  return;
41163
41207
  }
41164
41208
  }
41165
- static updateDocumentVersion(docx = this.convertedXml, version2 = "0.21.0") {
41209
+ static updateDocumentVersion(docx = this.convertedXml, version2 = "0.22.0") {
41166
41210
  const customLocation = "docProps/custom.xml";
41167
41211
  if (!docx[customLocation]) {
41168
41212
  docx[customLocation] = generateCustomXml();
@@ -41651,7 +41695,7 @@ Please report this to https://github.com/markedjs/marked.`, e) {
41651
41695
  function generateCustomXml() {
41652
41696
  return DEFAULT_CUSTOM_XML;
41653
41697
  }
41654
- function generateSuperdocVersion(pid = 2, version2 = "0.21.0") {
41698
+ function generateSuperdocVersion(pid = 2, version2 = "0.22.0") {
41655
41699
  return {
41656
41700
  type: "element",
41657
41701
  name: "property",
@@ -44293,7 +44337,7 @@ Please report this to https://github.com/markedjs/marked.`, e) {
44293
44337
  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);
44294
44338
  var __privateSet = (obj, member, value, setter) => (__accessCheck$1(obj, member, "write to private field"), member.set(obj, value), value);
44295
44339
  var __privateMethod$1 = (obj, member, method) => (__accessCheck$1(obj, member, "access private method"), method);
44296
- 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;
44340
+ 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;
44297
44341
  var GOOD_LEAF_SIZE = 200;
44298
44342
  var RopeSequence = function RopeSequence2() {
44299
44343
  };
@@ -56368,7 +56412,7 @@ Please report this to https://github.com/markedjs/marked.`, e) {
56368
56412
  if (emitParams) editor.emit("commentsUpdate", emitParams);
56369
56413
  return newTrackedChanges;
56370
56414
  };
56371
- const getTrackedChangeText = ({ state: state2, nodes, mark, marks, trackedChangeType, isDeletionInsertion }) => {
56415
+ const getTrackedChangeText = ({ nodes, mark, trackedChangeType, isDeletionInsertion }) => {
56372
56416
  let trackedChangeText = "";
56373
56417
  let deletionText = "";
56374
56418
  if (trackedChangeType === TrackInsertMarkName) {
@@ -56410,10 +56454,8 @@ Please report this to https://github.com/markedjs/marked.`, e) {
56410
56454
  if (hasMatchingId) nodesWithMark.push(node2);
56411
56455
  });
56412
56456
  const { deletionText, trackedChangeText } = getTrackedChangeText({
56413
- state: newEditorState,
56414
56457
  nodes: nodesWithMark.length ? nodesWithMark : [node],
56415
56458
  mark: trackedMark,
56416
- marks,
56417
56459
  trackedChangeType,
56418
56460
  isDeletionInsertion
56419
56461
  });
@@ -59096,7 +59138,7 @@ Please report this to https://github.com/markedjs/marked.`, e) {
59096
59138
  * @returns {Object | void} Migration results
59097
59139
  */
59098
59140
  processCollaborationMigrations() {
59099
- console.debug("[checkVersionMigrations] Current editor version", "0.21.0");
59141
+ console.debug("[checkVersionMigrations] Current editor version", "0.22.0");
59100
59142
  if (!this.options.ydoc) return;
59101
59143
  const metaMap = this.options.ydoc.getMap("meta");
59102
59144
  let docVersion = metaMap.get("version");
@@ -61068,228 +61110,1223 @@ Please report this to https://github.com/markedjs/marked.`, e) {
61068
61110
  return this.editor.options.isHeadless ? [] : [slashMenuPlugin];
61069
61111
  }
61070
61112
  });
61071
- const Document = Node$1.create({
61072
- name: "doc",
61073
- topNode: true,
61074
- content: "block+",
61075
- parseDOM() {
61076
- return [{ tag: "doc" }];
61077
- },
61078
- renderDOM() {
61079
- return ["doc", 0];
61080
- },
61081
- addAttributes() {
61082
- return {
61083
- attributes: {
61084
- rendered: false,
61085
- "aria-label": "Document node"
61086
- }
61087
- };
61088
- },
61089
- addCommands() {
61090
- return {
61091
- /**
61092
- * Get document statistics
61093
- * @category Command
61094
- * @example
61095
- * // Get word and character count
61096
- * const stats = editor.commands.getDocumentStats()
61097
- * console.log(`${stats.words} words, ${stats.characters} characters`)
61098
- * @note Returns word count, character count, and paragraph count
61099
- */
61100
- getDocumentStats: () => ({ editor }) => {
61101
- const text = editor.getText();
61102
- const words = text.split(/\s+/).filter((word) => word.length > 0).length;
61103
- const characters = text.length;
61104
- const paragraphs = editor.state.doc.content.childCount;
61105
- return {
61106
- words,
61107
- characters,
61108
- paragraphs
61109
- };
61110
- },
61111
- /**
61112
- * Clear entire document
61113
- * @category Command
61114
- * @example
61115
- * editor.commands.clearDocument()
61116
- * @note Replaces all content with an empty paragraph
61117
- */
61118
- clearDocument: () => ({ commands: commands2 }) => {
61119
- return commands2.setContent("<p></p>");
61113
+ class StructuredContentViewBase {
61114
+ constructor(props) {
61115
+ __publicField$1(this, "node");
61116
+ __publicField$1(this, "view");
61117
+ __publicField$1(this, "getPos");
61118
+ __publicField$1(this, "decorations");
61119
+ __publicField$1(this, "innerDecorations");
61120
+ __publicField$1(this, "editor");
61121
+ __publicField$1(this, "extension");
61122
+ __publicField$1(this, "htmlAttributes");
61123
+ __publicField$1(this, "root");
61124
+ __publicField$1(this, "isDragging", false);
61125
+ this.node = props.node;
61126
+ this.view = props.editor.view;
61127
+ this.getPos = props.getPos;
61128
+ this.decorations = props.decorations;
61129
+ this.innerDecorations = props.innerDecorations;
61130
+ this.editor = props.editor;
61131
+ this.extension = props.extension;
61132
+ this.htmlAttributes = props.htmlAttributes;
61133
+ this.mount(props);
61134
+ }
61135
+ mount() {
61136
+ return;
61137
+ }
61138
+ get dom() {
61139
+ return this.root;
61140
+ }
61141
+ get contentDOM() {
61142
+ return null;
61143
+ }
61144
+ update(node, decorations, innerDecorations) {
61145
+ if (node.type !== this.node.type) {
61146
+ return false;
61147
+ }
61148
+ this.node = node;
61149
+ this.decorations = decorations;
61150
+ this.innerDecorations = innerDecorations;
61151
+ this.updateHTMLAttributes();
61152
+ return true;
61153
+ }
61154
+ stopEvent(event) {
61155
+ if (!this.dom) return false;
61156
+ const target = event.target;
61157
+ const isInElement = this.dom.contains(target) && !this.contentDOM?.contains(target);
61158
+ if (!isInElement) return false;
61159
+ const isDragEvent = event.type.startsWith("drag");
61160
+ const isDropEvent = event.type === "drop";
61161
+ const isInput = ["INPUT", "BUTTON", "SELECT", "TEXTAREA"].includes(target.tagName) || target.isContentEditable;
61162
+ if (isInput && !isDropEvent && !isDragEvent) return true;
61163
+ const { isEditable } = this.editor;
61164
+ const { isDragging } = this;
61165
+ const isDraggable = !!this.node.type.spec.draggable;
61166
+ const isSelectable = NodeSelection.isSelectable(this.node);
61167
+ const isCopyEvent = event.type === "copy";
61168
+ const isPasteEvent = event.type === "paste";
61169
+ const isCutEvent = event.type === "cut";
61170
+ const isClickEvent = event.type === "mousedown";
61171
+ if (!isDraggable && isSelectable && isDragEvent && event.target === this.dom) {
61172
+ event.preventDefault();
61173
+ }
61174
+ if (isDraggable && isDragEvent && !isDragging && event.target === this.dom) {
61175
+ event.preventDefault();
61176
+ return false;
61177
+ }
61178
+ if (isDraggable && isEditable && !isDragging && isClickEvent) {
61179
+ const dragHandle = target.closest("[data-drag-handle]");
61180
+ const isValidDragHandle = dragHandle && (this.dom === dragHandle || this.dom.contains(dragHandle));
61181
+ if (isValidDragHandle) {
61182
+ this.isDragging = true;
61183
+ document.addEventListener(
61184
+ "dragend",
61185
+ () => {
61186
+ this.isDragging = false;
61187
+ },
61188
+ { once: true }
61189
+ );
61190
+ document.addEventListener(
61191
+ "drop",
61192
+ () => {
61193
+ this.isDragging = false;
61194
+ },
61195
+ { once: true }
61196
+ );
61197
+ document.addEventListener(
61198
+ "mouseup",
61199
+ () => {
61200
+ this.isDragging = false;
61201
+ },
61202
+ { once: true }
61203
+ );
61120
61204
  }
61121
- };
61205
+ }
61206
+ if (isDragging || isDropEvent || isCopyEvent || isPasteEvent || isCutEvent || isClickEvent && isSelectable) {
61207
+ return false;
61208
+ }
61209
+ return true;
61122
61210
  }
61123
- });
61124
- const Text = Node$1.create({
61125
- name: "text",
61126
- group: "inline",
61127
- inline: true,
61128
- addOptions() {
61129
- return {};
61211
+ ignoreMutation(mutation) {
61212
+ if (!this.dom || !this.contentDOM) return true;
61213
+ if (this.node.isLeaf || this.node.isAtom) return true;
61214
+ if (mutation.type === "selection") return false;
61215
+ if (this.contentDOM === mutation.target && mutation.type === "attributes") return true;
61216
+ if (this.contentDOM.contains(mutation.target)) return false;
61217
+ return true;
61130
61218
  }
61131
- });
61132
- const splitRun = () => (props) => {
61133
- const { state: state2, view, tr } = props;
61134
- const { $from, empty: empty2 } = state2.selection;
61135
- if (!empty2) return false;
61136
- if ($from.parent.type.name !== "run") return false;
61137
- const handled = splitBlock(state2, (transaction) => {
61219
+ destroy() {
61220
+ this.dom.remove();
61221
+ this.contentDOM?.remove();
61222
+ }
61223
+ updateAttributes(attrs) {
61224
+ const pos = this.getPos();
61225
+ if (typeof pos !== "number") {
61226
+ return;
61227
+ }
61228
+ return this.view.dispatch(
61229
+ this.view.state.tr.setNodeMarkup(pos, void 0, {
61230
+ ...this.node.attrs,
61231
+ ...attrs
61232
+ })
61233
+ );
61234
+ }
61235
+ updateHTMLAttributes() {
61236
+ const { extensionService } = this.editor;
61237
+ const { attributes } = extensionService;
61238
+ const extensionAttrs = attributes.filter((i2) => i2.type === this.node.type.name);
61239
+ this.htmlAttributes = Attribute.getAttributesToRender(this.node, extensionAttrs);
61240
+ }
61241
+ createDragHandle() {
61242
+ const dragHandle = document.createElement("span");
61243
+ dragHandle.classList.add("sd-structured-content-draggable");
61244
+ dragHandle.draggable = true;
61245
+ dragHandle.contentEditable = "false";
61246
+ dragHandle.dataset.dragHandle = "";
61247
+ const textElement = document.createElement("span");
61248
+ textElement.textContent = this.node.attrs.alias || "Structured content";
61249
+ dragHandle.append(textElement);
61250
+ return dragHandle;
61251
+ }
61252
+ onDragStart(event) {
61253
+ const { view } = this.editor;
61254
+ const target = event.target;
61255
+ const dragHandle = target.nodeType === 3 ? target.parentElement?.closest("[data-drag-handle]") : target.closest("[data-drag-handle]");
61256
+ if (!this.dom || this.contentDOM?.contains(target) || !dragHandle) {
61257
+ return;
61258
+ }
61259
+ let x = 0;
61260
+ let y2 = 0;
61261
+ if (this.dom !== dragHandle) {
61262
+ const domBox = this.dom.getBoundingClientRect();
61263
+ const handleBox = dragHandle.getBoundingClientRect();
61264
+ const offsetX = event.offsetX ?? event.nativeEvent?.offsetX;
61265
+ const offsetY = event.offsetY ?? event.nativeEvent?.offsetY;
61266
+ x = handleBox.x - domBox.x + offsetX;
61267
+ y2 = handleBox.y - domBox.y + offsetY;
61268
+ }
61269
+ event.dataTransfer?.setDragImage(this.dom, x, y2);
61270
+ const pos = this.getPos();
61271
+ if (typeof pos !== "number") {
61272
+ return;
61273
+ }
61274
+ const selection = NodeSelection.create(view.state.doc, pos);
61275
+ const transaction = view.state.tr.setSelection(selection);
61138
61276
  view.dispatch(transaction);
61139
- });
61140
- if (handled) {
61141
- tr.setMeta("preventDispatch", true);
61142
61277
  }
61143
- return handled;
61144
- };
61145
- const Run = OxmlNode.create({
61146
- name: "run",
61147
- oXmlName: "w:r",
61148
- group: "inline",
61278
+ }
61279
+ class StructuredContentInlineView extends StructuredContentViewBase {
61280
+ constructor(props) {
61281
+ super(props);
61282
+ }
61283
+ mount() {
61284
+ this.buildView();
61285
+ }
61286
+ get contentDOM() {
61287
+ const contentElement = this.dom?.querySelector(`.${structuredContentInnerClass$1}`);
61288
+ return contentElement || null;
61289
+ }
61290
+ createElement() {
61291
+ const element = document.createElement("span");
61292
+ element.classList.add(structuredContentClass$1);
61293
+ element.setAttribute("data-structured-content", "");
61294
+ const contentElement = document.createElement("span");
61295
+ contentElement.classList.add(structuredContentInnerClass$1);
61296
+ element.append(contentElement);
61297
+ const domAttrs = Attribute.mergeAttributes(this.htmlAttributes);
61298
+ updateDOMAttributes(element, { ...domAttrs });
61299
+ return { element, contentElement };
61300
+ }
61301
+ buildView() {
61302
+ const { element } = this.createElement();
61303
+ const dragHandle = this.createDragHandle();
61304
+ element.prepend(dragHandle);
61305
+ element.addEventListener("dragstart", (e) => this.onDragStart(e));
61306
+ this.root = element;
61307
+ }
61308
+ updateView() {
61309
+ const domAttrs = Attribute.mergeAttributes(this.htmlAttributes);
61310
+ updateDOMAttributes(this.dom, { ...domAttrs });
61311
+ }
61312
+ update(node, decorations, innerDecorations) {
61313
+ const result = super.update(node, decorations, innerDecorations);
61314
+ if (!result) return false;
61315
+ this.updateView();
61316
+ return true;
61317
+ }
61318
+ }
61319
+ const structuredContentClass$1 = "sd-structured-content";
61320
+ const structuredContentInnerClass$1 = "sd-structured-content__content";
61321
+ const StructuredContent = Node$1.create({
61322
+ name: "structuredContent",
61323
+ group: "inline structuredContent",
61149
61324
  inline: true,
61150
61325
  content: "inline*",
61151
- selectable: false,
61152
- childToAttributes: ["runProperties"],
61326
+ isolating: true,
61327
+ atom: false,
61328
+ // false - has editable content.
61329
+ draggable: true,
61153
61330
  addOptions() {
61154
61331
  return {
61155
61332
  htmlAttributes: {
61156
- "data-run": "1"
61333
+ class: structuredContentClass$1,
61334
+ "aria-label": "Structured content node"
61157
61335
  }
61158
61336
  };
61159
61337
  },
61160
61338
  addAttributes() {
61161
61339
  return {
61162
- runProperties: {
61340
+ id: {
61163
61341
  default: null,
61164
- rendered: false,
61165
- keepOnSplit: true
61342
+ parseDOM: (elem) => elem.getAttribute("data-id"),
61343
+ renderDOM: (attrs) => {
61344
+ if (!attrs.id) return {};
61345
+ return { "data-id": attrs.id };
61346
+ }
61166
61347
  },
61167
- rsidR: {
61348
+ tag: {
61168
61349
  default: null,
61169
- rendered: false,
61170
- keepOnSplit: true
61350
+ parseDOM: (elem) => elem.getAttribute("data-tag"),
61351
+ renderDOM: (attrs) => {
61352
+ if (!attrs.tag) return {};
61353
+ return { "data-tag": attrs.tag };
61354
+ }
61171
61355
  },
61172
- rsidRPr: {
61356
+ alias: {
61173
61357
  default: null,
61174
- rendered: false,
61175
- keepOnSplit: true
61358
+ parseDOM: (elem) => elem.getAttribute("data-alias"),
61359
+ renderDOM: (attrs) => {
61360
+ if (!attrs.alias) return {};
61361
+ return { "data-alias": attrs.alias };
61362
+ }
61176
61363
  },
61177
- rsidDel: {
61178
- default: null,
61179
- rendered: false,
61180
- keepOnSplit: true
61364
+ sdtPr: {
61365
+ rendered: false
61181
61366
  }
61182
61367
  };
61183
61368
  },
61184
- addCommands() {
61185
- return {
61186
- splitRun
61187
- };
61188
- },
61189
61369
  parseDOM() {
61190
- return [{ tag: "span[data-run]" }];
61370
+ return [{ tag: "span[data-structured-content]" }];
61191
61371
  },
61192
61372
  renderDOM({ htmlAttributes }) {
61193
- const base2 = Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes);
61194
- return ["span", base2, 0];
61373
+ return [
61374
+ "span",
61375
+ Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes, {
61376
+ "data-structured-content": ""
61377
+ }),
61378
+ 0
61379
+ ];
61380
+ },
61381
+ addNodeView() {
61382
+ return (props) => {
61383
+ return new StructuredContentInlineView({ ...props });
61384
+ };
61195
61385
  }
61196
61386
  });
61197
- const inputRegex$1 = /^\s*([-+*])\s$/;
61198
- const BulletList = Node$1.create({
61199
- name: "bulletList",
61200
- group: "block list",
61201
- selectable: false,
61202
- content() {
61203
- return `${this.options.itemTypeName}+`;
61204
- },
61387
+ class StructuredContentBlockView extends StructuredContentViewBase {
61388
+ constructor(props) {
61389
+ super(props);
61390
+ }
61391
+ mount() {
61392
+ this.buildView();
61393
+ }
61394
+ get contentDOM() {
61395
+ const contentElement = this.dom?.querySelector(`.${structuredContentInnerClass}`);
61396
+ return contentElement || null;
61397
+ }
61398
+ createElement() {
61399
+ const element = document.createElement("div");
61400
+ element.classList.add(structuredContentClass);
61401
+ element.setAttribute("data-structured-content-block", "");
61402
+ const contentElement = document.createElement("div");
61403
+ contentElement.classList.add(structuredContentInnerClass);
61404
+ element.append(contentElement);
61405
+ const domAttrs = Attribute.mergeAttributes(this.htmlAttributes);
61406
+ updateDOMAttributes(element, { ...domAttrs });
61407
+ return { element, contentElement };
61408
+ }
61409
+ buildView() {
61410
+ const { element } = this.createElement();
61411
+ const dragHandle = this.createDragHandle();
61412
+ element.prepend(dragHandle);
61413
+ element.addEventListener("dragstart", (e) => this.onDragStart(e));
61414
+ this.root = element;
61415
+ }
61416
+ updateView() {
61417
+ const domAttrs = Attribute.mergeAttributes(this.htmlAttributes);
61418
+ updateDOMAttributes(this.dom, { ...domAttrs });
61419
+ }
61420
+ update(node, decorations, innerDecorations) {
61421
+ const result = super.update(node, decorations, innerDecorations);
61422
+ if (!result) return false;
61423
+ this.updateView();
61424
+ return true;
61425
+ }
61426
+ }
61427
+ const structuredContentClass = "sd-structured-content-block";
61428
+ const structuredContentInnerClass = "sd-structured-content-block__content";
61429
+ const StructuredContentBlock = Node$1.create({
61430
+ name: "structuredContentBlock",
61431
+ group: "block structuredContent",
61432
+ content: "block*",
61433
+ isolating: true,
61434
+ atom: false,
61435
+ // false - has editable content.
61436
+ draggable: true,
61205
61437
  addOptions() {
61206
61438
  return {
61207
- itemTypeName: "listItem",
61208
61439
  htmlAttributes: {
61209
- "aria-label": "Bullet list node"
61210
- },
61211
- keepMarks: true,
61212
- keepAttributes: false
61440
+ class: structuredContentClass,
61441
+ "aria-label": "Structured content block node"
61442
+ }
61213
61443
  };
61214
61444
  },
61215
- parseDOM() {
61216
- return [{ tag: "ul" }];
61217
- },
61218
- renderDOM({ htmlAttributes }) {
61219
- const attributes = Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes);
61220
- return ["ul", attributes, 0];
61221
- },
61222
61445
  addAttributes() {
61223
61446
  return {
61224
- "list-style-type": {
61225
- default: "bullet",
61226
- rendered: false
61447
+ id: {
61448
+ default: null,
61449
+ parseDOM: (elem) => elem.getAttribute("data-id"),
61450
+ renderDOM: (attrs) => {
61451
+ if (!attrs.id) return {};
61452
+ return { "data-id": attrs.id };
61453
+ }
61227
61454
  },
61228
- listId: {
61229
- rendered: false
61455
+ tag: {
61456
+ default: null,
61457
+ parseDOM: (elem) => elem.getAttribute("data-tag"),
61458
+ renderDOM: (attrs) => {
61459
+ if (!attrs.tag) return {};
61460
+ return { "data-tag": attrs.tag };
61461
+ }
61230
61462
  },
61231
- sdBlockId: {
61463
+ alias: {
61232
61464
  default: null,
61233
- keepOnSplit: false,
61234
- parseDOM: (elem) => elem.getAttribute("data-sd-block-id"),
61465
+ parseDOM: (elem) => elem.getAttribute("data-alias"),
61235
61466
  renderDOM: (attrs) => {
61236
- return attrs.sdBlockId ? { "data-sd-block-id": attrs.sdBlockId } : {};
61467
+ if (!attrs.alias) return {};
61468
+ return { "data-alias": attrs.alias };
61237
61469
  }
61238
61470
  },
61239
- attributes: {
61240
- rendered: false,
61241
- keepOnSplit: true
61242
- }
61243
- };
61244
- },
61245
- addCommands() {
61246
- return {
61247
- /**
61248
- * Toggle a bullet list at the current selection
61249
- * @category Command
61250
- * @example
61251
- * // Toggle bullet list on selected text
61252
- * editor.commands.toggleBulletList()
61253
- * @note Converts selected paragraphs to list items or removes list formatting
61254
- */
61255
- toggleBulletList: () => (params2) => {
61256
- return toggleList(this.type)(params2);
61471
+ sdtPr: {
61472
+ rendered: false
61257
61473
  }
61258
61474
  };
61259
61475
  },
61260
- addShortcuts() {
61261
- return {
61262
- "Mod-Shift-8": () => {
61263
- return this.editor.commands.toggleBulletList();
61264
- }
61265
- };
61476
+ parseDOM() {
61477
+ return [{ tag: "div[data-structured-content-block]" }];
61266
61478
  },
61267
- addInputRules() {
61479
+ renderDOM({ htmlAttributes }) {
61268
61480
  return [
61269
- new InputRule({
61270
- match: inputRegex$1,
61271
- handler: ({ state: state2, range: range2 }) => {
61272
- const $pos = state2.selection.$from;
61273
- const listItemType = state2.schema.nodes.listItem;
61274
- for (let depth = $pos.depth; depth >= 0; depth--) {
61275
- if ($pos.node(depth).type === listItemType) {
61276
- return null;
61277
- }
61278
- }
61279
- const { tr } = state2;
61280
- tr.delete(range2.from, range2.to);
61281
- ListHelpers.createNewList({
61282
- listType: this.type,
61283
- tr,
61284
- editor: this.editor
61285
- });
61286
- }
61287
- })
61481
+ "div",
61482
+ Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes, {
61483
+ "data-structured-content-block": ""
61484
+ }),
61485
+ 0
61288
61486
  ];
61487
+ },
61488
+ addNodeView() {
61489
+ return (props) => {
61490
+ return new StructuredContentBlockView({ ...props });
61491
+ };
61289
61492
  }
61290
61493
  });
61291
- const inputRegex = /^(\d+)\.\s$/;
61292
- const OrderedList = Node$1.create({
61494
+ function getStructuredContentTagsById(idOrIds, state2) {
61495
+ const result = findChildren$5(state2.doc, (node) => {
61496
+ const isStructuredContent = ["structuredContent", "structuredContentBlock"].includes(node.type.name);
61497
+ if (Array.isArray(idOrIds)) {
61498
+ return isStructuredContent && idOrIds.includes(node.attrs.id);
61499
+ } else {
61500
+ return isStructuredContent && node.attrs.id === idOrIds;
61501
+ }
61502
+ });
61503
+ return result;
61504
+ }
61505
+ function getStructuredContentTags(state2) {
61506
+ const result = findChildren$5(state2.doc, (node) => {
61507
+ return node.type.name === "structuredContent" || node.type.name === "structuredContentBlock";
61508
+ });
61509
+ return result;
61510
+ }
61511
+ function getStructuredContentInlineTags(state2) {
61512
+ const result = findChildren$5(state2.doc, (node) => node.type.name === "structuredContent");
61513
+ return result;
61514
+ }
61515
+ function getStructuredContentBlockTags(state2) {
61516
+ const result = findChildren$5(state2.doc, (node) => node.type.name === "structuredContentBlock");
61517
+ return result;
61518
+ }
61519
+ const structuredContentHelpers = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
61520
+ __proto__: null,
61521
+ getStructuredContentBlockTags,
61522
+ getStructuredContentInlineTags,
61523
+ getStructuredContentTags,
61524
+ getStructuredContentTagsById
61525
+ }, Symbol.toStringTag, { value: "Module" }));
61526
+ const STRUCTURED_CONTENT_NAMES = ["structuredContent", "structuredContentBlock"];
61527
+ const StructuredContentCommands = Extension.create({
61528
+ name: "structuredContentCommands",
61529
+ addCommands() {
61530
+ return {
61531
+ /**
61532
+ * Inserts a structured content inline at selection.
61533
+ * @category Command
61534
+ * @param {StructuredContentInlineInsert} options
61535
+ */
61536
+ insertStructuredContentInline: (options = {}) => ({ editor, dispatch, state: state2, tr }) => {
61537
+ const { schema } = editor;
61538
+ let { from: from2, to } = state2.selection;
61539
+ if (dispatch) {
61540
+ const selectionText = state2.doc.textBetween(from2, to);
61541
+ let content = null;
61542
+ if (selectionText) {
61543
+ content = schema.text(selectionText);
61544
+ }
61545
+ if (options.text) {
61546
+ content = schema.text(options.text);
61547
+ }
61548
+ if (options.json) {
61549
+ content = schema.nodeFromJSON(options.json);
61550
+ }
61551
+ if (!content) {
61552
+ content = schema.text(" ");
61553
+ }
61554
+ const attrs = {
61555
+ ...options.attrs,
61556
+ id: options.attrs?.id || generateRandomSigned32BitIntStrId(),
61557
+ tag: "inline_text_sdt",
61558
+ alias: options.attrs?.alias || "Structured content"
61559
+ };
61560
+ const node = schema.nodes.structuredContent.create(attrs, content, null);
61561
+ const parent = findParentNode((node2) => node2.type.name === "structuredContent")(state2.selection);
61562
+ if (parent) {
61563
+ const insertPos = parent.pos + parent.node.nodeSize;
61564
+ from2 = to = insertPos;
61565
+ }
61566
+ tr.replaceWith(from2, to, node);
61567
+ }
61568
+ return true;
61569
+ },
61570
+ /**
61571
+ * Inserts a structured content block at selection.
61572
+ * @category Command
61573
+ * @param {StructuredContentBlockInsert} options
61574
+ */
61575
+ insertStructuredContentBlock: (options = {}) => ({ editor, dispatch, state: state2, tr }) => {
61576
+ const { schema } = editor;
61577
+ let { from: from2, to } = state2.selection;
61578
+ if (dispatch) {
61579
+ const selectionContent = state2.selection.content();
61580
+ let content = null;
61581
+ if (selectionContent.size) {
61582
+ content = selectionContent.content;
61583
+ }
61584
+ if (options.html) {
61585
+ const html = htmlHandler(options.html, editor);
61586
+ const doc2 = DOMParser$1.fromSchema(schema).parse(html);
61587
+ content = doc2.content;
61588
+ }
61589
+ if (options.json) {
61590
+ content = schema.nodeFromJSON(options.json);
61591
+ }
61592
+ if (!content) {
61593
+ content = schema.nodeFromJSON({ type: "paragraph", content: [] });
61594
+ }
61595
+ const attrs = {
61596
+ ...options.attrs,
61597
+ id: options.attrs?.id || generateRandomSigned32BitIntStrId(),
61598
+ tag: "block_table_sdt",
61599
+ alias: options.attrs?.alias || "Structured content"
61600
+ };
61601
+ const node = schema.nodes.structuredContentBlock.create(attrs, content, null);
61602
+ const parent = findParentNode((node2) => node2.type.name === "structuredContentBlock")(state2.selection);
61603
+ if (parent) {
61604
+ const insertPos = parent.pos + parent.node.nodeSize;
61605
+ from2 = to = insertPos;
61606
+ }
61607
+ tr.replaceRangeWith(from2, to, node);
61608
+ }
61609
+ return true;
61610
+ },
61611
+ /**
61612
+ * Updates a structured content attributes or content.
61613
+ * If the updated node does not match the schema, it will not be updated.
61614
+ * @category Command
61615
+ * @param {string} id
61616
+ * @param {StructuredContentUpdate} options
61617
+ */
61618
+ updateStructuredContentById: (id, options = {}) => ({ editor, dispatch, state: state2, tr }) => {
61619
+ const structuredContentTags = getStructuredContentTagsById(id, state2);
61620
+ if (!structuredContentTags.length) {
61621
+ return true;
61622
+ }
61623
+ const { schema } = editor;
61624
+ if (dispatch) {
61625
+ const structuredContent = structuredContentTags[0];
61626
+ const { pos, node } = structuredContent;
61627
+ const posFrom = pos;
61628
+ const posTo = pos + node.nodeSize;
61629
+ let content = null;
61630
+ if (options.text) {
61631
+ content = schema.text(options.text);
61632
+ }
61633
+ if (options.html) {
61634
+ const html = htmlHandler(options.html, editor);
61635
+ const doc2 = DOMParser$1.fromSchema(schema).parse(html);
61636
+ content = doc2.content;
61637
+ }
61638
+ if (options.json) {
61639
+ content = schema.nodeFromJSON(options.json);
61640
+ }
61641
+ if (!content) {
61642
+ content = node.content;
61643
+ }
61644
+ const updatedNode = node.type.create({ ...node.attrs, ...options.attrs }, content, node.marks);
61645
+ try {
61646
+ updatedNode.check();
61647
+ } catch {
61648
+ console.error("Updated node does not conform to the schema");
61649
+ return false;
61650
+ }
61651
+ tr.replaceWith(posFrom, posTo, updatedNode);
61652
+ }
61653
+ return true;
61654
+ },
61655
+ /**
61656
+ * Removes a structured content.
61657
+ * @category Command
61658
+ * @param {Array<{ node: Node, pos: number }>} structuredContentTags
61659
+ */
61660
+ deleteStructuredContent: (structuredContentTags) => ({ dispatch, tr }) => {
61661
+ if (!structuredContentTags.length) {
61662
+ return true;
61663
+ }
61664
+ if (dispatch) {
61665
+ structuredContentTags.forEach((structuredContent) => {
61666
+ const { pos, node } = structuredContent;
61667
+ const posFrom = tr.mapping.map(pos);
61668
+ const posTo = tr.mapping.map(pos + node.nodeSize);
61669
+ const currentNode = tr.doc.nodeAt(posFrom);
61670
+ if (currentNode && node.eq(currentNode)) {
61671
+ tr.delete(posFrom, posTo);
61672
+ }
61673
+ });
61674
+ }
61675
+ return true;
61676
+ },
61677
+ /**
61678
+ * Removes a structured content by ID.
61679
+ * @category Command
61680
+ * @param {string | string[]} idOrIds
61681
+ */
61682
+ deleteStructuredContentById: (idOrIds) => ({ dispatch, state: state2, tr }) => {
61683
+ const structuredContentTags = getStructuredContentTagsById(idOrIds, state2);
61684
+ if (!structuredContentTags.length) {
61685
+ return true;
61686
+ }
61687
+ if (dispatch) {
61688
+ structuredContentTags.forEach((structuredContent) => {
61689
+ const { pos, node } = structuredContent;
61690
+ const posFrom = tr.mapping.map(pos);
61691
+ const posTo = tr.mapping.map(pos + node.nodeSize);
61692
+ const currentNode = tr.doc.nodeAt(posFrom);
61693
+ if (currentNode && node.eq(currentNode)) {
61694
+ tr.delete(posFrom, posTo);
61695
+ }
61696
+ });
61697
+ }
61698
+ return true;
61699
+ },
61700
+ /**
61701
+ * Removes a structured content at cursor, preserving its content.
61702
+ * @category Command
61703
+ */
61704
+ deleteStructuredContentAtSelection: () => ({ dispatch, state: state2, tr }) => {
61705
+ const predicate = (node) => STRUCTURED_CONTENT_NAMES.includes(node.type.name);
61706
+ const structuredContent = findParentNode(predicate)(state2.selection);
61707
+ if (!structuredContent) {
61708
+ return true;
61709
+ }
61710
+ if (dispatch) {
61711
+ const { node, pos } = structuredContent;
61712
+ const posFrom = pos;
61713
+ const posTo = posFrom + node.nodeSize;
61714
+ const content = node.content;
61715
+ tr.replaceWith(posFrom, posTo, content);
61716
+ }
61717
+ return true;
61718
+ }
61719
+ };
61720
+ },
61721
+ addHelpers() {
61722
+ return {
61723
+ ...structuredContentHelpers
61724
+ };
61725
+ }
61726
+ });
61727
+ class DocumentSectionView {
61728
+ constructor(node, getPos, decorations, editor) {
61729
+ __privateAdd$1(this, _DocumentSectionView_instances);
61730
+ this.node = node;
61731
+ this.editor = editor;
61732
+ this.decorations = decorations;
61733
+ this.view = editor.view;
61734
+ this.getPos = getPos;
61735
+ __privateMethod$1(this, _DocumentSectionView_instances, init_fn2).call(this);
61736
+ }
61737
+ }
61738
+ _DocumentSectionView_instances = /* @__PURE__ */ new WeakSet();
61739
+ init_fn2 = function() {
61740
+ const { attrs } = this.node;
61741
+ const { id, title, description } = attrs;
61742
+ this.dom = document.createElement("div");
61743
+ this.dom.className = "sd-document-section-block";
61744
+ this.dom.setAttribute("data-id", id);
61745
+ this.dom.setAttribute("data-title", title);
61746
+ this.dom.setAttribute("data-description", description);
61747
+ this.dom.setAttribute("aria-label", "Document section");
61748
+ __privateMethod$1(this, _DocumentSectionView_instances, addToolTip_fn).call(this);
61749
+ this.contentDOM = document.createElement("div");
61750
+ this.contentDOM.className = "sd-document-section-block-content";
61751
+ this.contentDOM.setAttribute("contenteditable", "true");
61752
+ this.dom.appendChild(this.contentDOM);
61753
+ };
61754
+ addToolTip_fn = function() {
61755
+ const { title } = this.node.attrs;
61756
+ this.infoDiv = document.createElement("div");
61757
+ this.infoDiv.className = "sd-document-section-block-info";
61758
+ const textSpan = document.createElement("span");
61759
+ textSpan.textContent = title || "Document section";
61760
+ this.infoDiv.appendChild(textSpan);
61761
+ this.infoDiv.setAttribute("contenteditable", "false");
61762
+ this.dom.appendChild(this.infoDiv);
61763
+ };
61764
+ const getAllSections = (editor) => {
61765
+ if (!editor) return [];
61766
+ const type2 = editor.schema.nodes.documentSection;
61767
+ if (!type2) return [];
61768
+ const sections = [];
61769
+ const { state: state2 } = editor;
61770
+ state2.doc.descendants((node, pos) => {
61771
+ if (node.type.name === type2.name) {
61772
+ sections.push({ node, pos });
61773
+ }
61774
+ });
61775
+ return sections;
61776
+ };
61777
+ const exportSectionsToHTML = (editor) => {
61778
+ const sections = getAllSections(editor);
61779
+ const processedSections = /* @__PURE__ */ new Set();
61780
+ const result = [];
61781
+ sections.forEach(({ node }) => {
61782
+ const { attrs } = node;
61783
+ const { id, title, description } = attrs;
61784
+ if (processedSections.has(id)) return;
61785
+ processedSections.add(id);
61786
+ const html = getHTMLFromNode(node, editor);
61787
+ result.push({
61788
+ id,
61789
+ title,
61790
+ description,
61791
+ html
61792
+ });
61793
+ });
61794
+ return result;
61795
+ };
61796
+ const getHTMLFromNode = (node, editor) => {
61797
+ const tempDocument = document.implementation.createHTMLDocument();
61798
+ const container = tempDocument.createElement("div");
61799
+ const fragment = DOMSerializer.fromSchema(editor.schema).serializeFragment(node.content);
61800
+ container.appendChild(fragment);
61801
+ let html = container.innerHTML;
61802
+ return html;
61803
+ };
61804
+ const exportSectionsToJSON = (editor) => {
61805
+ const sections = getAllSections(editor);
61806
+ const processedSections = /* @__PURE__ */ new Set();
61807
+ const result = [];
61808
+ sections.forEach(({ node }) => {
61809
+ const { attrs } = node;
61810
+ const { id, title, description } = attrs;
61811
+ if (processedSections.has(id)) return;
61812
+ processedSections.add(id);
61813
+ result.push({
61814
+ id,
61815
+ title,
61816
+ description,
61817
+ content: node.toJSON()
61818
+ });
61819
+ });
61820
+ return result;
61821
+ };
61822
+ const getLinkedSectionEditor = (id, options, editor) => {
61823
+ const sections = getAllSections(editor);
61824
+ const section = sections.find((s) => s.node.attrs.id === id);
61825
+ if (!section) return null;
61826
+ const child = editor.createChildEditor({
61827
+ ...options,
61828
+ onUpdate: ({ editor: childEditor, transaction }) => {
61829
+ const isFromtLinkedParent = transaction.getMeta("fromLinkedParent");
61830
+ if (isFromtLinkedParent) return;
61831
+ const updatedContent = childEditor.state.doc.content;
61832
+ const sectionNode = getAllSections(editor)?.find((s) => s.node.attrs.id === id);
61833
+ if (!sectionNode) return;
61834
+ const { pos, node } = sectionNode;
61835
+ const newNode = node.type.create(node.attrs, updatedContent, node.marks);
61836
+ const tr = editor.state.tr.replaceWith(pos, pos + node.nodeSize, newNode);
61837
+ tr.setMeta("fromLinkedChild", true);
61838
+ editor.view.dispatch(tr);
61839
+ }
61840
+ });
61841
+ editor.on("update", ({ transaction }) => {
61842
+ const isFromLinkedChild = transaction.getMeta("fromLinkedChild");
61843
+ if (isFromLinkedChild) return;
61844
+ const sectionNode = getAllSections(editor)?.find((s) => s.node.attrs.id === id);
61845
+ if (!sectionNode) return;
61846
+ const sectionContent = sectionNode.node.content;
61847
+ const json = {
61848
+ type: "doc",
61849
+ content: sectionContent.content.map((node) => node.toJSON())
61850
+ };
61851
+ const childTr = child.state.tr;
61852
+ childTr.setMeta("fromLinkedParent", true);
61853
+ childTr.replaceWith(0, child.state.doc.content.size, child.schema.nodeFromJSON(json));
61854
+ child.view.dispatch(childTr);
61855
+ });
61856
+ return child;
61857
+ };
61858
+ const SectionHelpers = {
61859
+ getAllSections,
61860
+ exportSectionsToHTML,
61861
+ exportSectionsToJSON,
61862
+ getLinkedSectionEditor
61863
+ };
61864
+ const DocumentSection = Node$1.create({
61865
+ name: "documentSection",
61866
+ group: "block",
61867
+ content: "block*",
61868
+ atom: true,
61869
+ isolating: true,
61870
+ addOptions() {
61871
+ return {
61872
+ htmlAttributes: {
61873
+ class: "sd-document-section-block",
61874
+ "aria-label": "Structured content block"
61875
+ }
61876
+ };
61877
+ },
61878
+ parseDOM() {
61879
+ return [
61880
+ {
61881
+ tag: "div.sd-document-section-block",
61882
+ priority: 60
61883
+ }
61884
+ ];
61885
+ },
61886
+ renderDOM({ htmlAttributes }) {
61887
+ return ["div", Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes), 0];
61888
+ },
61889
+ addAttributes() {
61890
+ return {
61891
+ id: {},
61892
+ sdBlockId: {
61893
+ default: null,
61894
+ keepOnSplit: false,
61895
+ parseDOM: (elem) => elem.getAttribute("data-sd-block-id"),
61896
+ renderDOM: (attrs) => {
61897
+ return attrs.sdBlockId ? { "data-sd-block-id": attrs.sdBlockId } : {};
61898
+ }
61899
+ },
61900
+ title: {},
61901
+ description: {},
61902
+ sectionType: {},
61903
+ isLocked: { default: false }
61904
+ };
61905
+ },
61906
+ addNodeView() {
61907
+ return ({ node, editor, getPos, decorations }) => {
61908
+ return new DocumentSectionView(node, getPos, decorations, editor);
61909
+ };
61910
+ },
61911
+ addCommands() {
61912
+ return {
61913
+ /**
61914
+ * Create a lockable content section
61915
+ * @category Command
61916
+ * @param {SectionCreate} [options={}] - Section configuration
61917
+ * @example
61918
+ * editor.commands.createDocumentSection({
61919
+ * id: 1,
61920
+ * title: 'Terms & Conditions',
61921
+ * isLocked: true,
61922
+ * html: '<p>Legal content...</p>'
61923
+ * })
61924
+ */
61925
+ createDocumentSection: (options = {}) => ({ tr, state: state2, dispatch, editor }) => {
61926
+ const { selection } = state2;
61927
+ let { from: from2, to } = selection;
61928
+ let content = selection.content().content;
61929
+ const { html: optionsHTML, json: optionsJSON } = options;
61930
+ if (optionsHTML) {
61931
+ const html = htmlHandler(optionsHTML, this.editor);
61932
+ const doc2 = DOMParser$1.fromSchema(this.editor.schema).parse(html);
61933
+ content = doc2.content;
61934
+ }
61935
+ if (optionsJSON) {
61936
+ content = this.editor.schema.nodeFromJSON(optionsJSON);
61937
+ }
61938
+ if (!content?.content?.length) {
61939
+ content = this.editor.schema.nodeFromJSON({ type: "paragraph", content: [] });
61940
+ }
61941
+ if (!options.id) {
61942
+ const allSections = SectionHelpers.getAllSections(editor);
61943
+ options.id = allSections.length + 1;
61944
+ }
61945
+ if (!options.title) {
61946
+ options.title = "Document section";
61947
+ }
61948
+ const node = this.type.createAndFill(options, content);
61949
+ if (!node) return false;
61950
+ const isAlreadyInSdtBlock = findParentNode((node2) => node2.type.name === "documentSection")(selection);
61951
+ if (isAlreadyInSdtBlock && isAlreadyInSdtBlock.node) {
61952
+ const insertPos2 = isAlreadyInSdtBlock.pos + isAlreadyInSdtBlock.node.nodeSize;
61953
+ from2 = insertPos2;
61954
+ to = insertPos2;
61955
+ }
61956
+ tr.replaceRangeWith(from2, to, node);
61957
+ const nodeEnd = from2 + node.nodeSize;
61958
+ let shouldInsertParagraph = true;
61959
+ let insertPos = nodeEnd;
61960
+ if (nodeEnd >= tr.doc.content.size) {
61961
+ insertPos = tr.doc.content.size;
61962
+ if (insertPos > 0) {
61963
+ const $endPos = tr.doc.resolve(insertPos);
61964
+ if ($endPos.nodeBefore && $endPos.nodeBefore.type.name === "paragraph") {
61965
+ shouldInsertParagraph = false;
61966
+ }
61967
+ }
61968
+ }
61969
+ if (shouldInsertParagraph) {
61970
+ const emptyParagraph = tr.doc.type.schema.nodes.paragraph.create();
61971
+ tr.insert(insertPos, emptyParagraph);
61972
+ }
61973
+ if (dispatch) {
61974
+ tr.setMeta("documentSection", { action: "create" });
61975
+ dispatch(tr);
61976
+ setTimeout(() => {
61977
+ try {
61978
+ const currentState = editor.state;
61979
+ const docSize = currentState.doc.content.size;
61980
+ let targetPos = from2 + node.nodeSize;
61981
+ if (shouldInsertParagraph) {
61982
+ targetPos += 1;
61983
+ }
61984
+ targetPos = Math.min(targetPos, docSize);
61985
+ if (targetPos < docSize && targetPos > 0) {
61986
+ const newSelection = Selection.near(currentState.doc.resolve(targetPos));
61987
+ const newTr = currentState.tr.setSelection(newSelection);
61988
+ editor.view.dispatch(newTr);
61989
+ }
61990
+ } catch (e) {
61991
+ console.warn("Could not set delayed selection:", e);
61992
+ }
61993
+ }, 0);
61994
+ }
61995
+ return true;
61996
+ },
61997
+ /**
61998
+ * Remove section wrapper at cursor, preserving its content
61999
+ * @category Command
62000
+ * @example
62001
+ * editor.commands.removeSectionAtSelection()
62002
+ * @note Content stays in document, only section wrapper is removed
62003
+ */
62004
+ removeSectionAtSelection: () => ({ tr, dispatch }) => {
62005
+ const sdtNode = findParentNode((node2) => node2.type.name === "documentSection")(tr.selection);
62006
+ if (!sdtNode) return false;
62007
+ const { node, pos } = sdtNode;
62008
+ const nodeStart = pos;
62009
+ const nodeEnd = nodeStart + node.nodeSize;
62010
+ const contentToPreserve = node.content;
62011
+ tr.delete(nodeStart, nodeEnd);
62012
+ if (contentToPreserve.size > 0) {
62013
+ tr.insert(nodeStart, contentToPreserve);
62014
+ }
62015
+ const newPos = Math.min(nodeStart, tr.doc.content.size);
62016
+ tr.setSelection(Selection.near(tr.doc.resolve(newPos)));
62017
+ if (dispatch) {
62018
+ tr.setMeta("documentSection", { action: "delete" });
62019
+ dispatch(tr);
62020
+ }
62021
+ return true;
62022
+ },
62023
+ /**
62024
+ * Delete section and all its content
62025
+ * @category Command
62026
+ * @param {number} id - Section to delete
62027
+ * @example
62028
+ * editor.commands.removeSectionById(123)
62029
+ */
62030
+ removeSectionById: (id) => ({ tr, dispatch }) => {
62031
+ const sections = SectionHelpers.getAllSections(this.editor);
62032
+ const sectionToRemove = sections.find(({ node: node2 }) => node2.attrs.id === id);
62033
+ if (!sectionToRemove) return false;
62034
+ const { pos, node } = sectionToRemove;
62035
+ const nodeStart = pos;
62036
+ const nodeEnd = nodeStart + node.nodeSize;
62037
+ tr.delete(nodeStart, nodeEnd);
62038
+ if (dispatch) {
62039
+ tr.setMeta("documentSection", { action: "delete", id });
62040
+ dispatch(tr);
62041
+ }
62042
+ return true;
62043
+ },
62044
+ /**
62045
+ * Lock section against edits
62046
+ * @category Command
62047
+ * @param {number} id - Section to lock
62048
+ * @example
62049
+ * editor.commands.lockSectionById(123)
62050
+ */
62051
+ lockSectionById: (id) => ({ tr, dispatch }) => {
62052
+ const sections = SectionHelpers.getAllSections(this.editor);
62053
+ const sectionToLock = sections.find(({ node }) => node.attrs.id === id);
62054
+ if (!sectionToLock) return false;
62055
+ tr.setNodeMarkup(sectionToLock.pos, null, { ...sectionToLock.node.attrs, isLocked: true });
62056
+ if (dispatch) {
62057
+ tr.setMeta("documentSection", { action: "lock", id });
62058
+ dispatch(tr);
62059
+ }
62060
+ return true;
62061
+ },
62062
+ /**
62063
+ * Modify section attributes or content
62064
+ * @category Command
62065
+ * @param {SectionUpdate} options - Changes to apply
62066
+ * @example
62067
+ * editor.commands.updateSectionById({ id: 123, attrs: { isLocked: false } })
62068
+ * editor.commands.updateSectionById({ id: 123, html: '<p>New content</p>' })
62069
+ * editor.commands.updateSectionById({
62070
+ * id: 123,
62071
+ * html: '<p>Updated</p>',
62072
+ * attrs: { title: 'New Title' }
62073
+ * })
62074
+ */
62075
+ updateSectionById: ({ id, html, json, attrs }) => ({ tr, dispatch, editor }) => {
62076
+ const sections = SectionHelpers.getAllSections(editor || this.editor);
62077
+ const sectionToUpdate = sections.find(({ node: node2 }) => node2.attrs.id === id);
62078
+ if (!sectionToUpdate) return false;
62079
+ const { pos, node } = sectionToUpdate;
62080
+ let newContent = null;
62081
+ if (html) {
62082
+ const htmlDoc = htmlHandler(html, editor || this.editor);
62083
+ const doc2 = DOMParser$1.fromSchema((editor || this.editor).schema).parse(htmlDoc);
62084
+ newContent = doc2.content;
62085
+ }
62086
+ if (json) {
62087
+ newContent = (editor || this.editor).schema.nodeFromJSON(json);
62088
+ }
62089
+ if (!newContent) {
62090
+ newContent = node.content;
62091
+ }
62092
+ const updatedNode = node.type.create({ ...node.attrs, ...attrs }, newContent, node.marks);
62093
+ tr.replaceWith(pos, pos + node.nodeSize, updatedNode);
62094
+ if (dispatch) {
62095
+ tr.setMeta("documentSection", { action: "update", id, attrs });
62096
+ dispatch(tr);
62097
+ }
62098
+ return true;
62099
+ }
62100
+ };
62101
+ },
62102
+ addHelpers() {
62103
+ return {
62104
+ ...SectionHelpers
62105
+ };
62106
+ }
62107
+ });
62108
+ const Document = Node$1.create({
62109
+ name: "doc",
62110
+ topNode: true,
62111
+ content: "block+",
62112
+ parseDOM() {
62113
+ return [{ tag: "doc" }];
62114
+ },
62115
+ renderDOM() {
62116
+ return ["doc", 0];
62117
+ },
62118
+ addAttributes() {
62119
+ return {
62120
+ attributes: {
62121
+ rendered: false,
62122
+ "aria-label": "Document node"
62123
+ }
62124
+ };
62125
+ },
62126
+ addCommands() {
62127
+ return {
62128
+ /**
62129
+ * Get document statistics
62130
+ * @category Command
62131
+ * @example
62132
+ * // Get word and character count
62133
+ * const stats = editor.commands.getDocumentStats()
62134
+ * console.log(`${stats.words} words, ${stats.characters} characters`)
62135
+ * @note Returns word count, character count, and paragraph count
62136
+ */
62137
+ getDocumentStats: () => ({ editor }) => {
62138
+ const text = editor.getText();
62139
+ const words = text.split(/\s+/).filter((word) => word.length > 0).length;
62140
+ const characters = text.length;
62141
+ const paragraphs = editor.state.doc.content.childCount;
62142
+ return {
62143
+ words,
62144
+ characters,
62145
+ paragraphs
62146
+ };
62147
+ },
62148
+ /**
62149
+ * Clear entire document
62150
+ * @category Command
62151
+ * @example
62152
+ * editor.commands.clearDocument()
62153
+ * @note Replaces all content with an empty paragraph
62154
+ */
62155
+ clearDocument: () => ({ commands: commands2 }) => {
62156
+ return commands2.setContent("<p></p>");
62157
+ }
62158
+ };
62159
+ }
62160
+ });
62161
+ const Text = Node$1.create({
62162
+ name: "text",
62163
+ group: "inline",
62164
+ inline: true,
62165
+ addOptions() {
62166
+ return {};
62167
+ }
62168
+ });
62169
+ const splitRun = () => (props) => {
62170
+ const { state: state2, view, tr } = props;
62171
+ const { $from, empty: empty2 } = state2.selection;
62172
+ if (!empty2) return false;
62173
+ if ($from.parent.type.name !== "run") return false;
62174
+ const handled = splitBlock(state2, (transaction) => {
62175
+ view.dispatch(transaction);
62176
+ });
62177
+ if (handled) {
62178
+ tr.setMeta("preventDispatch", true);
62179
+ }
62180
+ return handled;
62181
+ };
62182
+ const Run = OxmlNode.create({
62183
+ name: "run",
62184
+ oXmlName: "w:r",
62185
+ group: "inline",
62186
+ inline: true,
62187
+ content: "inline*",
62188
+ selectable: false,
62189
+ childToAttributes: ["runProperties"],
62190
+ addOptions() {
62191
+ return {
62192
+ htmlAttributes: {
62193
+ "data-run": "1"
62194
+ }
62195
+ };
62196
+ },
62197
+ addAttributes() {
62198
+ return {
62199
+ runProperties: {
62200
+ default: null,
62201
+ rendered: false,
62202
+ keepOnSplit: true
62203
+ },
62204
+ rsidR: {
62205
+ default: null,
62206
+ rendered: false,
62207
+ keepOnSplit: true
62208
+ },
62209
+ rsidRPr: {
62210
+ default: null,
62211
+ rendered: false,
62212
+ keepOnSplit: true
62213
+ },
62214
+ rsidDel: {
62215
+ default: null,
62216
+ rendered: false,
62217
+ keepOnSplit: true
62218
+ }
62219
+ };
62220
+ },
62221
+ addCommands() {
62222
+ return {
62223
+ splitRun
62224
+ };
62225
+ },
62226
+ parseDOM() {
62227
+ return [{ tag: "span[data-run]" }];
62228
+ },
62229
+ renderDOM({ htmlAttributes }) {
62230
+ const base2 = Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes);
62231
+ return ["span", base2, 0];
62232
+ }
62233
+ });
62234
+ const inputRegex$1 = /^\s*([-+*])\s$/;
62235
+ const BulletList = Node$1.create({
62236
+ name: "bulletList",
62237
+ group: "block list",
62238
+ selectable: false,
62239
+ content() {
62240
+ return `${this.options.itemTypeName}+`;
62241
+ },
62242
+ addOptions() {
62243
+ return {
62244
+ itemTypeName: "listItem",
62245
+ htmlAttributes: {
62246
+ "aria-label": "Bullet list node"
62247
+ },
62248
+ keepMarks: true,
62249
+ keepAttributes: false
62250
+ };
62251
+ },
62252
+ parseDOM() {
62253
+ return [{ tag: "ul" }];
62254
+ },
62255
+ renderDOM({ htmlAttributes }) {
62256
+ const attributes = Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes);
62257
+ return ["ul", attributes, 0];
62258
+ },
62259
+ addAttributes() {
62260
+ return {
62261
+ "list-style-type": {
62262
+ default: "bullet",
62263
+ rendered: false
62264
+ },
62265
+ listId: {
62266
+ rendered: false
62267
+ },
62268
+ sdBlockId: {
62269
+ default: null,
62270
+ keepOnSplit: false,
62271
+ parseDOM: (elem) => elem.getAttribute("data-sd-block-id"),
62272
+ renderDOM: (attrs) => {
62273
+ return attrs.sdBlockId ? { "data-sd-block-id": attrs.sdBlockId } : {};
62274
+ }
62275
+ },
62276
+ attributes: {
62277
+ rendered: false,
62278
+ keepOnSplit: true
62279
+ }
62280
+ };
62281
+ },
62282
+ addCommands() {
62283
+ return {
62284
+ /**
62285
+ * Toggle a bullet list at the current selection
62286
+ * @category Command
62287
+ * @example
62288
+ * // Toggle bullet list on selected text
62289
+ * editor.commands.toggleBulletList()
62290
+ * @note Converts selected paragraphs to list items or removes list formatting
62291
+ */
62292
+ toggleBulletList: () => (params2) => {
62293
+ return toggleList(this.type)(params2);
62294
+ }
62295
+ };
62296
+ },
62297
+ addShortcuts() {
62298
+ return {
62299
+ "Mod-Shift-8": () => {
62300
+ return this.editor.commands.toggleBulletList();
62301
+ }
62302
+ };
62303
+ },
62304
+ addInputRules() {
62305
+ return [
62306
+ new InputRule({
62307
+ match: inputRegex$1,
62308
+ handler: ({ state: state2, range: range2 }) => {
62309
+ const $pos = state2.selection.$from;
62310
+ const listItemType = state2.schema.nodes.listItem;
62311
+ for (let depth = $pos.depth; depth >= 0; depth--) {
62312
+ if ($pos.node(depth).type === listItemType) {
62313
+ return null;
62314
+ }
62315
+ }
62316
+ const { tr } = state2;
62317
+ tr.delete(range2.from, range2.to);
62318
+ ListHelpers.createNewList({
62319
+ listType: this.type,
62320
+ tr,
62321
+ editor: this.editor
62322
+ });
62323
+ }
62324
+ })
62325
+ ];
62326
+ }
62327
+ });
62328
+ const inputRegex = /^(\d+)\.\s$/;
62329
+ const OrderedList = Node$1.create({
61293
62330
  name: "orderedList",
61294
62331
  group: "block list",
61295
62332
  selectable: false,
@@ -62650,7 +63687,7 @@ Please report this to https://github.com/markedjs/marked.`, e) {
62650
63687
  this.decorations = decorations;
62651
63688
  this.view = editor.view;
62652
63689
  this.getPos = getPos;
62653
- __privateMethod$1(this, _ListItemNodeView_instances, init_fn2).call(this);
63690
+ __privateMethod$1(this, _ListItemNodeView_instances, init_fn3).call(this);
62654
63691
  activeListItemNodeViews.add(this);
62655
63692
  }
62656
63693
  refreshIndentStyling() {
@@ -62711,7 +63748,7 @@ Please report this to https://github.com/markedjs/marked.`, e) {
62711
63748
  }
62712
63749
  }
62713
63750
  _ListItemNodeView_instances = /* @__PURE__ */ new WeakSet();
62714
- init_fn2 = function() {
63751
+ init_fn3 = function() {
62715
63752
  const { attrs } = this.node;
62716
63753
  const { listLevel, listNumberingType, lvlText, numId, level, customFormat } = attrs;
62717
63754
  let orderMarker = "";
@@ -69820,984 +70857,335 @@ Please report this to https://github.com/markedjs/marked.`, e) {
69820
70857
  }
69821
70858
  };
69822
70859
  },
69823
- addAttributes() {
69824
- return {
69825
- marksAsAttrs: {
69826
- default: null,
69827
- rendered: false
69828
- }
69829
- };
69830
- },
69831
- addNodeView() {
69832
- return ({ node, editor, getPos, decorations }) => {
69833
- const htmlAttributes = this.options.htmlAttributes;
69834
- return new AutoPageNumberNodeView(node, getPos, decorations, editor, htmlAttributes);
69835
- };
69836
- },
69837
- parseDOM() {
69838
- return [{ tag: 'span[data-id="auto-page-number"' }];
69839
- },
69840
- renderDOM({ htmlAttributes }) {
69841
- return ["span", Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes)];
69842
- },
69843
- addCommands() {
69844
- return {
69845
- /**
69846
- * Insert an automatic page number
69847
- * @category Command
69848
- * @returns {Function} Command function
69849
- * @example
69850
- * editor.commands.addAutoPageNumber()
69851
- * @note Only works in header/footer contexts
69852
- */
69853
- addAutoPageNumber: () => ({ tr, dispatch, state: state2, editor }) => {
69854
- const { options } = editor;
69855
- if (!options.isHeaderOrFooter) return false;
69856
- const { schema } = state2;
69857
- const pageNumberType = schema?.nodes?.["page-number"];
69858
- if (!pageNumberType) return false;
69859
- const pageNumberNodeJSON = { type: "page-number" };
69860
- const pageNumberNode = schema.nodeFromJSON(pageNumberNodeJSON);
69861
- if (dispatch) {
69862
- tr.replaceSelectionWith(pageNumberNode, false);
69863
- tr.setMeta("forceUpdatePagination", true);
69864
- }
69865
- return true;
69866
- }
69867
- };
69868
- },
69869
- addShortcuts() {
69870
- return {
69871
- "Mod-Shift-alt-p": () => this.editor.commands.addAutoPageNumber()
69872
- };
69873
- }
69874
- });
69875
- const TotalPageCount = Node$1.create({
69876
- name: "total-page-number",
69877
- group: "inline",
69878
- inline: true,
69879
- atom: true,
69880
- draggable: false,
69881
- selectable: false,
69882
- content: "text*",
69883
- addOptions() {
69884
- return {
69885
- htmlAttributes: {
69886
- contenteditable: false,
69887
- "data-id": "auto-total-pages",
69888
- "aria-label": "Total page count node",
69889
- class: "sd-editor-auto-total-pages"
69890
- }
69891
- };
69892
- },
69893
- addAttributes() {
69894
- return {
69895
- marksAsAttrs: {
69896
- default: null,
69897
- rendered: false
69898
- }
69899
- };
69900
- },
69901
- addNodeView() {
69902
- return ({ node, editor, getPos, decorations }) => {
69903
- const htmlAttributes = this.options.htmlAttributes;
69904
- return new AutoPageNumberNodeView(node, getPos, decorations, editor, htmlAttributes);
69905
- };
69906
- },
69907
- parseDOM() {
69908
- return [{ tag: 'span[data-id="auto-total-pages"' }];
69909
- },
69910
- renderDOM({ htmlAttributes }) {
69911
- return ["span", Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes), 0];
69912
- },
69913
- addCommands() {
69914
- return {
69915
- /**
69916
- * Insert total page count
69917
- * @category Command
69918
- * @returns {Function} Command function
69919
- * @example
69920
- * editor.commands.addTotalPageCount()
69921
- * @note Only works in header/footer contexts
69922
- */
69923
- addTotalPageCount: () => ({ tr, dispatch, state: state2, editor }) => {
69924
- const { options } = editor;
69925
- if (!options.isHeaderOrFooter) return false;
69926
- const { schema } = state2;
69927
- const pageNumberType = schema.nodes?.["total-page-number"];
69928
- if (!pageNumberType) return false;
69929
- const currentPages = editor?.options?.parentEditor?.currentTotalPages || 1;
69930
- const pageNumberNode = {
69931
- type: "total-page-number",
69932
- content: [{ type: "text", text: String(currentPages) }]
69933
- };
69934
- const pageNode = schema.nodeFromJSON(pageNumberNode);
69935
- if (dispatch) {
69936
- tr.replaceSelectionWith(pageNode, false);
69937
- }
69938
- return true;
69939
- }
69940
- };
69941
- },
69942
- addShortcuts() {
69943
- return {
69944
- "Mod-Shift-alt-c": () => this.editor.commands.addTotalPageCount()
69945
- };
69946
- }
69947
- });
69948
- const getNodeAttributes = (nodeName, editor) => {
69949
- switch (nodeName) {
69950
- case "page-number":
69951
- return {
69952
- text: editor.options.currentPageNumber || "1",
69953
- className: "sd-editor-auto-page-number",
69954
- dataId: "auto-page-number",
69955
- ariaLabel: "Page number node"
69956
- };
69957
- case "total-page-number":
69958
- return {
69959
- text: editor.options.parentEditor?.currentTotalPages || "1",
69960
- className: "sd-editor-auto-total-pages",
69961
- dataId: "auto-total-pages",
69962
- ariaLabel: "Total page count node"
69963
- };
69964
- default:
69965
- return {};
69966
- }
69967
- };
69968
- class AutoPageNumberNodeView {
69969
- constructor(node, getPos, decorations, editor, htmlAttributes = {}) {
69970
- __privateAdd$1(this, _AutoPageNumberNodeView_instances);
69971
- this.node = node;
69972
- this.editor = editor;
69973
- this.view = editor.view;
69974
- this.getPos = getPos;
69975
- this.editor = editor;
69976
- this.dom = __privateMethod$1(this, _AutoPageNumberNodeView_instances, renderDom_fn).call(this, node, htmlAttributes);
69977
- }
69978
- update(node) {
69979
- const incomingType = node?.type?.name;
69980
- const currentType = this.node?.type?.name;
69981
- if (!incomingType || incomingType !== currentType) return false;
69982
- this.node = node;
69983
- return true;
69984
- }
69985
- }
69986
- _AutoPageNumberNodeView_instances = /* @__PURE__ */ new WeakSet();
69987
- renderDom_fn = function(node, htmlAttributes) {
69988
- const attrs = getNodeAttributes(this.node.type.name, this.editor);
69989
- const content = document.createTextNode(String(attrs.text));
69990
- const nodeContent = document.createElement("span");
69991
- nodeContent.className = attrs.className;
69992
- nodeContent.setAttribute("data-id", attrs.dataId);
69993
- nodeContent.setAttribute("aria-label", attrs.ariaLabel);
69994
- const currentPos = this.getPos();
69995
- const { styles, marks } = getMarksFromNeighbors(currentPos, this.view);
69996
- __privateMethod$1(this, _AutoPageNumberNodeView_instances, scheduleUpdateNodeStyle_fn).call(this, currentPos, marks);
69997
- Object.assign(nodeContent.style, styles);
69998
- nodeContent.appendChild(content);
69999
- Object.entries(htmlAttributes).forEach(([key2, value]) => {
70000
- if (value) nodeContent.setAttribute(key2, value);
70001
- });
70002
- return nodeContent;
70003
- };
70004
- scheduleUpdateNodeStyle_fn = function(pos, marks) {
70005
- setTimeout(() => {
70006
- const { state: state2 } = this.editor;
70007
- const { dispatch } = this.view;
70008
- const node = state2.doc.nodeAt(pos);
70009
- if (!node || node.isText) return;
70010
- const currentMarks = node.attrs.marksAsAttrs || [];
70011
- const newMarks = marks.map((m2) => ({ type: m2.type.name, attrs: m2.attrs }));
70012
- const isEqual = JSON.stringify(currentMarks) === JSON.stringify(newMarks);
70013
- if (isEqual) return;
70014
- const newAttrs = {
70015
- ...node.attrs,
70016
- marksAsAttrs: newMarks
70017
- };
70018
- const tr = state2.tr.setNodeMarkup(pos, void 0, newAttrs);
70019
- dispatch(tr);
70020
- }, 0);
70021
- };
70022
- const getMarksFromNeighbors = (currentPos, view) => {
70023
- const $pos = view.state.doc.resolve(currentPos);
70024
- const styles = {};
70025
- const marks = [];
70026
- const before = $pos.nodeBefore;
70027
- if (before) {
70028
- Object.assign(styles, processMarks(before.marks));
70029
- marks.push(...before.marks);
70030
- }
70031
- const after = $pos.nodeAfter;
70032
- if (after) {
70033
- Object.assign(styles, { ...styles, ...processMarks(after.marks) });
70034
- marks.push(...after.marks);
70035
- }
70036
- return {
70037
- styles,
70038
- marks
70039
- };
70040
- };
70041
- const processMarks = (marks) => {
70042
- const styles = {};
70043
- marks.forEach((mark) => {
70044
- const { type: type2, attrs } = mark;
70045
- switch (type2.name) {
70046
- case "textStyle":
70047
- if (attrs.fontFamily) styles["font-family"] = attrs.fontFamily;
70048
- if (attrs.fontSize) styles["font-size"] = attrs.fontSize;
70049
- if (attrs.color) styles["color"] = attrs.color;
70050
- if (attrs.backgroundColor) styles["background-color"] = attrs.backgroundColor;
70051
- break;
70052
- case "bold":
70053
- styles["font-weight"] = "bold";
70054
- break;
70055
- case "italic":
70056
- styles["font-style"] = "italic";
70057
- break;
70058
- case "underline":
70059
- styles["text-decoration"] = (styles["text-decoration"] || "") + " underline";
70060
- break;
70061
- case "strike":
70062
- styles["text-decoration"] = (styles["text-decoration"] || "") + " line-through";
70063
- break;
70064
- default:
70065
- if (attrs?.style) {
70066
- Object.entries(attrs.style).forEach(([key2, value]) => {
70067
- styles[key2] = value;
70068
- });
70069
- }
70070
- break;
70071
- }
70072
- });
70073
- return styles;
70074
- };
70075
- const ShapeContainer = Node$1.create({
70076
- name: "shapeContainer",
70077
- group: "block",
70078
- content: "block+",
70079
- isolating: true,
70080
- addOptions() {
70081
- return {
70082
- htmlAttributes: {
70083
- class: "sd-editor-shape-container",
70084
- "aria-label": "Shape container node"
70085
- }
70086
- };
70087
- },
70088
- addAttributes() {
70089
- return {
70090
- fillcolor: {
70091
- renderDOM: (attrs) => {
70092
- if (!attrs.fillcolor) return {};
70093
- return {
70094
- style: `background-color: ${attrs.fillcolor}`
70095
- };
70096
- }
70097
- },
70098
- sdBlockId: {
70099
- default: null,
70100
- keepOnSplit: false,
70101
- parseDOM: (elem) => elem.getAttribute("data-sd-block-id"),
70102
- renderDOM: (attrs) => {
70103
- return attrs.sdBlockId ? { "data-sd-block-id": attrs.sdBlockId } : {};
70104
- }
70105
- },
70106
- style: {
70107
- renderDOM: (attrs) => {
70108
- if (!attrs.style) return {};
70109
- return {
70110
- style: attrs.style
70111
- };
70112
- }
70113
- },
70114
- wrapAttributes: {
70115
- rendered: false
70116
- },
70117
- attributes: {
70118
- rendered: false
70119
- }
70120
- };
70121
- },
70122
- parseDOM() {
70123
- return [
70124
- {
70125
- tag: `div[data-type="${this.name}"]`
70126
- }
70127
- ];
70128
- },
70129
- renderDOM({ htmlAttributes }) {
70130
- return [
70131
- "div",
70132
- Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes, { "data-type": this.name }),
70133
- 0
70134
- ];
70135
- }
70136
- });
70137
- const ShapeTextbox = Node$1.create({
70138
- name: "shapeTextbox",
70139
- group: "block",
70140
- content: "paragraph* block*",
70141
- isolating: true,
70142
- addOptions() {
70143
- return {
70144
- htmlAttributes: {
70145
- class: "sd-editor-shape-textbox",
70146
- "aria-label": "Shape textbox node"
70147
- }
70148
- };
70149
- },
70150
- addAttributes() {
70151
- return {
70152
- sdBlockId: {
70153
- default: null,
70154
- keepOnSplit: false,
70155
- parseDOM: (elem) => elem.getAttribute("data-sd-block-id"),
70156
- renderDOM: (attrs) => {
70157
- return attrs.sdBlockId ? { "data-sd-block-id": attrs.sdBlockId } : {};
70158
- }
70159
- },
70160
- attributes: {
70161
- rendered: false
70162
- }
70163
- };
70164
- },
70165
- parseDOM() {
70166
- return [
70167
- {
70168
- tag: `div[data-type="${this.name}"]`
70169
- }
70170
- ];
70171
- },
70172
- renderDOM({ htmlAttributes }) {
70173
- return [
70174
- "div",
70175
- Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes, { "data-type": this.name }),
70176
- 0
70177
- ];
70178
- }
70179
- });
70180
- const ContentBlock = Node$1.create({
70181
- name: "contentBlock",
70182
- group: "inline",
70183
- content: "",
70184
- isolating: true,
70185
- atom: true,
70186
- inline: true,
70187
- addOptions() {
70188
- return {
70189
- htmlAttributes: {
70190
- contenteditable: false
70191
- }
70192
- };
70193
- },
70194
- addAttributes() {
70195
- return {
70196
- horizontalRule: {
70197
- default: false,
70198
- renderDOM: ({ horizontalRule }) => {
70199
- if (!horizontalRule) return {};
70200
- return { "data-horizontal-rule": "true" };
70201
- }
70202
- },
70203
- size: {
70204
- default: null,
70205
- renderDOM: ({ size: size2 }) => {
70206
- if (!size2) return {};
70207
- let style2 = "";
70208
- if (size2.top) style2 += `top: ${size2.top}px; `;
70209
- if (size2.left) style2 += `left: ${size2.left}px; `;
70210
- if (size2.width) style2 += `width: ${size2.width.toString().endsWith("%") ? size2.width : `${size2.width}px`}; `;
70211
- if (size2.height)
70212
- style2 += `height: ${size2.height.toString().endsWith("%") ? size2.height : `${size2.height}px`}; `;
70213
- return { style: style2 };
70214
- }
70215
- },
70216
- background: {
70217
- default: null,
70218
- renderDOM: (attrs) => {
70219
- if (!attrs.background) return {};
70220
- return {
70221
- style: `background-color: ${attrs.background}`
70222
- };
70223
- }
70224
- },
70225
- drawingContent: {
70226
- rendered: false
70227
- },
70228
- attributes: {
70860
+ addAttributes() {
70861
+ return {
70862
+ marksAsAttrs: {
70863
+ default: null,
70229
70864
  rendered: false
70230
70865
  }
70231
70866
  };
70232
70867
  },
70868
+ addNodeView() {
70869
+ return ({ node, editor, getPos, decorations }) => {
70870
+ const htmlAttributes = this.options.htmlAttributes;
70871
+ return new AutoPageNumberNodeView(node, getPos, decorations, editor, htmlAttributes);
70872
+ };
70873
+ },
70233
70874
  parseDOM() {
70234
- return [
70235
- {
70236
- tag: `div[data-type="${this.name}"]`
70237
- }
70238
- ];
70875
+ return [{ tag: 'span[data-id="auto-page-number"' }];
70239
70876
  },
70240
70877
  renderDOM({ htmlAttributes }) {
70241
- return ["div", Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes, { "data-type": this.name })];
70878
+ return ["span", Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes)];
70242
70879
  },
70243
70880
  addCommands() {
70244
70881
  return {
70245
70882
  /**
70246
- * Insert a horizontal rule
70247
- * @category Command
70248
- * @example
70249
- * editor.commands.insertHorizontalRule()
70250
- * @note Creates a visual separator between content sections
70251
- */
70252
- insertHorizontalRule: () => ({ commands: commands2 }) => {
70253
- return commands2.insertContent({
70254
- type: this.name,
70255
- attrs: {
70256
- horizontalRule: true,
70257
- size: { width: "100%", height: 2 },
70258
- background: "#e5e7eb"
70259
- }
70260
- });
70261
- },
70262
- /**
70263
- * Insert a content block
70883
+ * Insert an automatic page number
70264
70884
  * @category Command
70265
- * @param {ContentBlockConfig} config - Block configuration
70266
- * @example
70267
- * // Insert a spacer block
70268
- * editor.commands.insertContentBlock({ size: { height: 20 } })
70269
- *
70885
+ * @returns {Function} Command function
70270
70886
  * @example
70271
- * // Insert a colored divider
70272
- * editor.commands.insertContentBlock({
70273
- * size: { width: '50%', height: 3 },
70274
- * background: '#3b82f6'
70275
- * })
70276
- * @note Used for spacing, dividers, and special inline content
70887
+ * editor.commands.addAutoPageNumber()
70888
+ * @note Only works in header/footer contexts
70277
70889
  */
70278
- insertContentBlock: (config2) => ({ commands: commands2 }) => {
70279
- return commands2.insertContent({
70280
- type: this.name,
70281
- attrs: config2
70282
- });
70890
+ addAutoPageNumber: () => ({ tr, dispatch, state: state2, editor }) => {
70891
+ const { options } = editor;
70892
+ if (!options.isHeaderOrFooter) return false;
70893
+ const { schema } = state2;
70894
+ const pageNumberType = schema?.nodes?.["page-number"];
70895
+ if (!pageNumberType) return false;
70896
+ const pageNumberNodeJSON = { type: "page-number" };
70897
+ const pageNumberNode = schema.nodeFromJSON(pageNumberNodeJSON);
70898
+ if (dispatch) {
70899
+ tr.replaceSelectionWith(pageNumberNode, false);
70900
+ tr.setMeta("forceUpdatePagination", true);
70901
+ }
70902
+ return true;
70283
70903
  }
70284
70904
  };
70905
+ },
70906
+ addShortcuts() {
70907
+ return {
70908
+ "Mod-Shift-alt-p": () => this.editor.commands.addAutoPageNumber()
70909
+ };
70285
70910
  }
70286
70911
  });
70287
- class StructuredContentViewBase {
70288
- constructor(props) {
70289
- __publicField$1(this, "node");
70290
- __publicField$1(this, "view");
70291
- __publicField$1(this, "getPos");
70292
- __publicField$1(this, "decorations");
70293
- __publicField$1(this, "innerDecorations");
70294
- __publicField$1(this, "editor");
70295
- __publicField$1(this, "extension");
70296
- __publicField$1(this, "htmlAttributes");
70297
- __publicField$1(this, "root");
70298
- __publicField$1(this, "isDragging", false);
70299
- this.node = props.node;
70300
- this.view = props.editor.view;
70301
- this.getPos = props.getPos;
70302
- this.decorations = props.decorations;
70303
- this.innerDecorations = props.innerDecorations;
70304
- this.editor = props.editor;
70305
- this.extension = props.extension;
70306
- this.htmlAttributes = props.htmlAttributes;
70307
- this.mount(props);
70308
- }
70309
- mount() {
70310
- return;
70311
- }
70312
- get dom() {
70313
- return this.root;
70314
- }
70315
- get contentDOM() {
70316
- return null;
70317
- }
70318
- update(node, decorations, innerDecorations) {
70319
- if (node.type !== this.node.type) {
70320
- return false;
70321
- }
70322
- this.node = node;
70323
- this.decorations = decorations;
70324
- this.innerDecorations = innerDecorations;
70325
- this.updateHTMLAttributes();
70326
- return true;
70327
- }
70328
- stopEvent(event) {
70329
- if (!this.dom) return false;
70330
- const target = event.target;
70331
- const isInElement = this.dom.contains(target) && !this.contentDOM?.contains(target);
70332
- if (!isInElement) return false;
70333
- const isDragEvent = event.type.startsWith("drag");
70334
- const isDropEvent = event.type === "drop";
70335
- const isInput = ["INPUT", "BUTTON", "SELECT", "TEXTAREA"].includes(target.tagName) || target.isContentEditable;
70336
- if (isInput && !isDropEvent && !isDragEvent) return true;
70337
- const { isEditable } = this.editor;
70338
- const { isDragging } = this;
70339
- const isDraggable = !!this.node.type.spec.draggable;
70340
- const isSelectable = NodeSelection.isSelectable(this.node);
70341
- const isCopyEvent = event.type === "copy";
70342
- const isPasteEvent = event.type === "paste";
70343
- const isCutEvent = event.type === "cut";
70344
- const isClickEvent = event.type === "mousedown";
70345
- if (!isDraggable && isSelectable && isDragEvent && event.target === this.dom) {
70346
- event.preventDefault();
70347
- }
70348
- if (isDraggable && isDragEvent && !isDragging && event.target === this.dom) {
70349
- event.preventDefault();
70350
- return false;
70351
- }
70352
- if (isDraggable && isEditable && !isDragging && isClickEvent) {
70353
- const dragHandle = target.closest("[data-drag-handle]");
70354
- const isValidDragHandle = dragHandle && (this.dom === dragHandle || this.dom.contains(dragHandle));
70355
- if (isValidDragHandle) {
70356
- this.isDragging = true;
70357
- document.addEventListener(
70358
- "dragend",
70359
- () => {
70360
- this.isDragging = false;
70361
- },
70362
- { once: true }
70363
- );
70364
- document.addEventListener(
70365
- "drop",
70366
- () => {
70367
- this.isDragging = false;
70368
- },
70369
- { once: true }
70370
- );
70371
- document.addEventListener(
70372
- "mouseup",
70373
- () => {
70374
- this.isDragging = false;
70375
- },
70376
- { once: true }
70377
- );
70378
- }
70379
- }
70380
- if (isDragging || isDropEvent || isCopyEvent || isPasteEvent || isCutEvent || isClickEvent && isSelectable) {
70381
- return false;
70382
- }
70383
- return true;
70384
- }
70385
- ignoreMutation(mutation) {
70386
- if (!this.dom || !this.contentDOM) return true;
70387
- if (this.node.isLeaf || this.node.isAtom) return true;
70388
- if (mutation.type === "selection") return false;
70389
- if (this.contentDOM === mutation.target && mutation.type === "attributes") return true;
70390
- if (this.contentDOM.contains(mutation.target)) return false;
70391
- return true;
70392
- }
70393
- destroy() {
70394
- this.dom.remove();
70395
- this.contentDOM?.remove();
70396
- }
70397
- updateAttributes(attrs) {
70398
- const pos = this.getPos();
70399
- if (typeof pos !== "number") {
70400
- return;
70401
- }
70402
- return this.view.dispatch(
70403
- this.view.state.tr.setNodeMarkup(pos, void 0, {
70404
- ...this.node.attrs,
70405
- ...attrs
70406
- })
70407
- );
70408
- }
70409
- updateHTMLAttributes() {
70410
- const { extensionService } = this.editor;
70411
- const { attributes } = extensionService;
70412
- const extensionAttrs = attributes.filter((i2) => i2.type === this.node.type.name);
70413
- this.htmlAttributes = Attribute.getAttributesToRender(this.node, extensionAttrs);
70414
- }
70415
- createDragHandle() {
70416
- const dragHandle = document.createElement("span");
70417
- dragHandle.classList.add("sd-structured-content-draggable");
70418
- dragHandle.draggable = true;
70419
- dragHandle.contentEditable = "false";
70420
- dragHandle.dataset.dragHandle = "";
70421
- const textElement = document.createElement("span");
70422
- textElement.textContent = "Structured content";
70423
- dragHandle.append(textElement);
70424
- return dragHandle;
70425
- }
70426
- onDragStart(event) {
70427
- const { view } = this.editor;
70428
- const target = event.target;
70429
- const dragHandle = target.nodeType === 3 ? target.parentElement?.closest("[data-drag-handle]") : target.closest("[data-drag-handle]");
70430
- if (!this.dom || this.contentDOM?.contains(target) || !dragHandle) {
70431
- return;
70432
- }
70433
- let x = 0;
70434
- let y2 = 0;
70435
- if (this.dom !== dragHandle) {
70436
- const domBox = this.dom.getBoundingClientRect();
70437
- const handleBox = dragHandle.getBoundingClientRect();
70438
- const offsetX = event.offsetX ?? event.nativeEvent?.offsetX;
70439
- const offsetY = event.offsetY ?? event.nativeEvent?.offsetY;
70440
- x = handleBox.x - domBox.x + offsetX;
70441
- y2 = handleBox.y - domBox.y + offsetY;
70442
- }
70443
- event.dataTransfer?.setDragImage(this.dom, x, y2);
70444
- const pos = this.getPos();
70445
- if (typeof pos !== "number") {
70446
- return;
70447
- }
70448
- const selection = NodeSelection.create(view.state.doc, pos);
70449
- const transaction = view.state.tr.setSelection(selection);
70450
- view.dispatch(transaction);
70451
- }
70452
- }
70453
- class StructuredContentInlineView extends StructuredContentViewBase {
70454
- constructor(props) {
70455
- super(props);
70456
- }
70457
- mount() {
70458
- this.buildView();
70459
- }
70460
- get contentDOM() {
70461
- const contentElement = this.dom?.querySelector(`.${structuredContentInnerClass$1}`);
70462
- return contentElement || null;
70463
- }
70464
- createElement() {
70465
- const element = document.createElement("span");
70466
- element.classList.add(structuredContentClass$1);
70467
- element.setAttribute("data-structured-content", "");
70468
- const contentElement = document.createElement("span");
70469
- contentElement.classList.add(structuredContentInnerClass$1);
70470
- element.append(contentElement);
70471
- const domAttrs = Attribute.mergeAttributes(this.htmlAttributes);
70472
- updateDOMAttributes(element, { ...domAttrs });
70473
- return { element, contentElement };
70474
- }
70475
- buildView() {
70476
- const { element } = this.createElement();
70477
- const dragHandle = this.createDragHandle();
70478
- element.prepend(dragHandle);
70479
- element.addEventListener("dragstart", (e) => this.onDragStart(e));
70480
- this.root = element;
70481
- }
70482
- updateView() {
70483
- const domAttrs = Attribute.mergeAttributes(this.htmlAttributes);
70484
- updateDOMAttributes(this.dom, { ...domAttrs });
70485
- }
70486
- update(node, decorations, innerDecorations) {
70487
- const result = super.update(node, decorations, innerDecorations);
70488
- if (!result) return false;
70489
- this.updateView();
70490
- return true;
70491
- }
70492
- }
70493
- const structuredContentClass$1 = "sd-structured-content";
70494
- const structuredContentInnerClass$1 = "sd-structured-content__content";
70495
- const StructuredContent = Node$1.create({
70496
- name: "structuredContent",
70497
- group: "inline structuredContent",
70912
+ const TotalPageCount = Node$1.create({
70913
+ name: "total-page-number",
70914
+ group: "inline",
70498
70915
  inline: true,
70499
- content: "inline*",
70500
- isolating: true,
70501
- atom: false,
70502
- // false - has editable content.
70503
- draggable: true,
70916
+ atom: true,
70917
+ draggable: false,
70918
+ selectable: false,
70919
+ content: "text*",
70504
70920
  addOptions() {
70505
70921
  return {
70506
70922
  htmlAttributes: {
70507
- class: structuredContentClass$1,
70508
- "aria-label": "Structured content node"
70923
+ contenteditable: false,
70924
+ "data-id": "auto-total-pages",
70925
+ "aria-label": "Total page count node",
70926
+ class: "sd-editor-auto-total-pages"
70509
70927
  }
70510
70928
  };
70511
70929
  },
70512
70930
  addAttributes() {
70513
70931
  return {
70514
- id: {
70932
+ marksAsAttrs: {
70515
70933
  default: null,
70516
- parseDOM: (elem) => elem.getAttribute("data-id"),
70517
- renderDOM: (attrs) => {
70518
- if (!attrs.id) return {};
70519
- return { "data-id": attrs.id };
70520
- }
70521
- },
70522
- sdtPr: {
70523
70934
  rendered: false
70524
70935
  }
70525
70936
  };
70526
70937
  },
70938
+ addNodeView() {
70939
+ return ({ node, editor, getPos, decorations }) => {
70940
+ const htmlAttributes = this.options.htmlAttributes;
70941
+ return new AutoPageNumberNodeView(node, getPos, decorations, editor, htmlAttributes);
70942
+ };
70943
+ },
70527
70944
  parseDOM() {
70528
- return [{ tag: "span[data-structured-content]" }];
70945
+ return [{ tag: 'span[data-id="auto-total-pages"' }];
70529
70946
  },
70530
70947
  renderDOM({ htmlAttributes }) {
70531
- return [
70532
- "span",
70533
- Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes, {
70534
- "data-structured-content": ""
70535
- }),
70536
- 0
70537
- ];
70948
+ return ["span", Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes), 0];
70538
70949
  },
70539
- addNodeView() {
70540
- return (props) => {
70541
- return new StructuredContentInlineView({ ...props });
70950
+ addCommands() {
70951
+ return {
70952
+ /**
70953
+ * Insert total page count
70954
+ * @category Command
70955
+ * @returns {Function} Command function
70956
+ * @example
70957
+ * editor.commands.addTotalPageCount()
70958
+ * @note Only works in header/footer contexts
70959
+ */
70960
+ addTotalPageCount: () => ({ tr, dispatch, state: state2, editor }) => {
70961
+ const { options } = editor;
70962
+ if (!options.isHeaderOrFooter) return false;
70963
+ const { schema } = state2;
70964
+ const pageNumberType = schema.nodes?.["total-page-number"];
70965
+ if (!pageNumberType) return false;
70966
+ const currentPages = editor?.options?.parentEditor?.currentTotalPages || 1;
70967
+ const pageNumberNode = {
70968
+ type: "total-page-number",
70969
+ content: [{ type: "text", text: String(currentPages) }]
70970
+ };
70971
+ const pageNode = schema.nodeFromJSON(pageNumberNode);
70972
+ if (dispatch) {
70973
+ tr.replaceSelectionWith(pageNode, false);
70974
+ }
70975
+ return true;
70976
+ }
70977
+ };
70978
+ },
70979
+ addShortcuts() {
70980
+ return {
70981
+ "Mod-Shift-alt-c": () => this.editor.commands.addTotalPageCount()
70542
70982
  };
70543
70983
  }
70544
70984
  });
70545
- class StructuredContentBlockView extends StructuredContentViewBase {
70546
- constructor(props) {
70547
- super(props);
70548
- }
70549
- mount() {
70550
- this.buildView();
70551
- }
70552
- get contentDOM() {
70553
- const contentElement = this.dom?.querySelector(`.${structuredContentInnerClass}`);
70554
- return contentElement || null;
70555
- }
70556
- createElement() {
70557
- const element = document.createElement("div");
70558
- element.classList.add(structuredContentClass);
70559
- element.setAttribute("data-structured-content-block", "");
70560
- const contentElement = document.createElement("div");
70561
- contentElement.classList.add(structuredContentInnerClass);
70562
- element.append(contentElement);
70563
- const domAttrs = Attribute.mergeAttributes(this.htmlAttributes);
70564
- updateDOMAttributes(element, { ...domAttrs });
70565
- return { element, contentElement };
70566
- }
70567
- buildView() {
70568
- const { element } = this.createElement();
70569
- const dragHandle = this.createDragHandle();
70570
- element.prepend(dragHandle);
70571
- element.addEventListener("dragstart", (e) => this.onDragStart(e));
70572
- this.root = element;
70985
+ const getNodeAttributes = (nodeName, editor) => {
70986
+ switch (nodeName) {
70987
+ case "page-number":
70988
+ return {
70989
+ text: editor.options.currentPageNumber || "1",
70990
+ className: "sd-editor-auto-page-number",
70991
+ dataId: "auto-page-number",
70992
+ ariaLabel: "Page number node"
70993
+ };
70994
+ case "total-page-number":
70995
+ return {
70996
+ text: editor.options.parentEditor?.currentTotalPages || "1",
70997
+ className: "sd-editor-auto-total-pages",
70998
+ dataId: "auto-total-pages",
70999
+ ariaLabel: "Total page count node"
71000
+ };
71001
+ default:
71002
+ return {};
70573
71003
  }
70574
- updateView() {
70575
- const domAttrs = Attribute.mergeAttributes(this.htmlAttributes);
70576
- updateDOMAttributes(this.dom, { ...domAttrs });
71004
+ };
71005
+ class AutoPageNumberNodeView {
71006
+ constructor(node, getPos, decorations, editor, htmlAttributes = {}) {
71007
+ __privateAdd$1(this, _AutoPageNumberNodeView_instances);
71008
+ this.node = node;
71009
+ this.editor = editor;
71010
+ this.view = editor.view;
71011
+ this.getPos = getPos;
71012
+ this.editor = editor;
71013
+ this.dom = __privateMethod$1(this, _AutoPageNumberNodeView_instances, renderDom_fn).call(this, node, htmlAttributes);
70577
71014
  }
70578
- update(node, decorations, innerDecorations) {
70579
- const result = super.update(node, decorations, innerDecorations);
70580
- if (!result) return false;
70581
- this.updateView();
71015
+ update(node) {
71016
+ const incomingType = node?.type?.name;
71017
+ const currentType = this.node?.type?.name;
71018
+ if (!incomingType || incomingType !== currentType) return false;
71019
+ this.node = node;
70582
71020
  return true;
70583
71021
  }
70584
71022
  }
70585
- const structuredContentClass = "sd-structured-content-block";
70586
- const structuredContentInnerClass = "sd-structured-content-block__content";
70587
- const StructuredContentBlock = Node$1.create({
70588
- name: "structuredContentBlock",
70589
- group: "block structuredContent",
70590
- content: "block*",
71023
+ _AutoPageNumberNodeView_instances = /* @__PURE__ */ new WeakSet();
71024
+ renderDom_fn = function(node, htmlAttributes) {
71025
+ const attrs = getNodeAttributes(this.node.type.name, this.editor);
71026
+ const content = document.createTextNode(String(attrs.text));
71027
+ const nodeContent = document.createElement("span");
71028
+ nodeContent.className = attrs.className;
71029
+ nodeContent.setAttribute("data-id", attrs.dataId);
71030
+ nodeContent.setAttribute("aria-label", attrs.ariaLabel);
71031
+ const currentPos = this.getPos();
71032
+ const { styles, marks } = getMarksFromNeighbors(currentPos, this.view);
71033
+ __privateMethod$1(this, _AutoPageNumberNodeView_instances, scheduleUpdateNodeStyle_fn).call(this, currentPos, marks);
71034
+ Object.assign(nodeContent.style, styles);
71035
+ nodeContent.appendChild(content);
71036
+ Object.entries(htmlAttributes).forEach(([key2, value]) => {
71037
+ if (value) nodeContent.setAttribute(key2, value);
71038
+ });
71039
+ return nodeContent;
71040
+ };
71041
+ scheduleUpdateNodeStyle_fn = function(pos, marks) {
71042
+ setTimeout(() => {
71043
+ const { state: state2 } = this.editor;
71044
+ const { dispatch } = this.view;
71045
+ const node = state2.doc.nodeAt(pos);
71046
+ if (!node || node.isText) return;
71047
+ const currentMarks = node.attrs.marksAsAttrs || [];
71048
+ const newMarks = marks.map((m2) => ({ type: m2.type.name, attrs: m2.attrs }));
71049
+ const isEqual = JSON.stringify(currentMarks) === JSON.stringify(newMarks);
71050
+ if (isEqual) return;
71051
+ const newAttrs = {
71052
+ ...node.attrs,
71053
+ marksAsAttrs: newMarks
71054
+ };
71055
+ const tr = state2.tr.setNodeMarkup(pos, void 0, newAttrs);
71056
+ dispatch(tr);
71057
+ }, 0);
71058
+ };
71059
+ const getMarksFromNeighbors = (currentPos, view) => {
71060
+ const $pos = view.state.doc.resolve(currentPos);
71061
+ const styles = {};
71062
+ const marks = [];
71063
+ const before = $pos.nodeBefore;
71064
+ if (before) {
71065
+ Object.assign(styles, processMarks(before.marks));
71066
+ marks.push(...before.marks);
71067
+ }
71068
+ const after = $pos.nodeAfter;
71069
+ if (after) {
71070
+ Object.assign(styles, { ...styles, ...processMarks(after.marks) });
71071
+ marks.push(...after.marks);
71072
+ }
71073
+ return {
71074
+ styles,
71075
+ marks
71076
+ };
71077
+ };
71078
+ const processMarks = (marks) => {
71079
+ const styles = {};
71080
+ marks.forEach((mark) => {
71081
+ const { type: type2, attrs } = mark;
71082
+ switch (type2.name) {
71083
+ case "textStyle":
71084
+ if (attrs.fontFamily) styles["font-family"] = attrs.fontFamily;
71085
+ if (attrs.fontSize) styles["font-size"] = attrs.fontSize;
71086
+ if (attrs.color) styles["color"] = attrs.color;
71087
+ if (attrs.backgroundColor) styles["background-color"] = attrs.backgroundColor;
71088
+ break;
71089
+ case "bold":
71090
+ styles["font-weight"] = "bold";
71091
+ break;
71092
+ case "italic":
71093
+ styles["font-style"] = "italic";
71094
+ break;
71095
+ case "underline":
71096
+ styles["text-decoration"] = (styles["text-decoration"] || "") + " underline";
71097
+ break;
71098
+ case "strike":
71099
+ styles["text-decoration"] = (styles["text-decoration"] || "") + " line-through";
71100
+ break;
71101
+ default:
71102
+ if (attrs?.style) {
71103
+ Object.entries(attrs.style).forEach(([key2, value]) => {
71104
+ styles[key2] = value;
71105
+ });
71106
+ }
71107
+ break;
71108
+ }
71109
+ });
71110
+ return styles;
71111
+ };
71112
+ const ShapeContainer = Node$1.create({
71113
+ name: "shapeContainer",
71114
+ group: "block",
71115
+ content: "block+",
70591
71116
  isolating: true,
70592
- atom: false,
70593
- // false - has editable content.
70594
- draggable: true,
70595
71117
  addOptions() {
70596
71118
  return {
70597
71119
  htmlAttributes: {
70598
- class: structuredContentClass,
70599
- "aria-label": "Structured content block node"
71120
+ class: "sd-editor-shape-container",
71121
+ "aria-label": "Shape container node"
70600
71122
  }
70601
71123
  };
70602
71124
  },
70603
71125
  addAttributes() {
70604
71126
  return {
70605
- id: {
71127
+ fillcolor: {
71128
+ renderDOM: (attrs) => {
71129
+ if (!attrs.fillcolor) return {};
71130
+ return {
71131
+ style: `background-color: ${attrs.fillcolor}`
71132
+ };
71133
+ }
71134
+ },
71135
+ sdBlockId: {
70606
71136
  default: null,
70607
- parseDOM: (elem) => elem.getAttribute("data-id"),
71137
+ keepOnSplit: false,
71138
+ parseDOM: (elem) => elem.getAttribute("data-sd-block-id"),
70608
71139
  renderDOM: (attrs) => {
70609
- if (!attrs.id) return {};
70610
- return { "data-id": attrs.id };
71140
+ return attrs.sdBlockId ? { "data-sd-block-id": attrs.sdBlockId } : {};
70611
71141
  }
70612
71142
  },
70613
- sdtPr: {
71143
+ style: {
71144
+ renderDOM: (attrs) => {
71145
+ if (!attrs.style) return {};
71146
+ return {
71147
+ style: attrs.style
71148
+ };
71149
+ }
71150
+ },
71151
+ wrapAttributes: {
71152
+ rendered: false
71153
+ },
71154
+ attributes: {
70614
71155
  rendered: false
70615
71156
  }
70616
71157
  };
70617
71158
  },
70618
71159
  parseDOM() {
70619
- return [{ tag: "div[data-structured-content-block]" }];
71160
+ return [
71161
+ {
71162
+ tag: `div[data-type="${this.name}"]`
71163
+ }
71164
+ ];
70620
71165
  },
70621
71166
  renderDOM({ htmlAttributes }) {
70622
71167
  return [
70623
71168
  "div",
70624
- Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes, {
70625
- "data-structured-content-block": ""
70626
- }),
71169
+ Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes, { "data-type": this.name }),
70627
71170
  0
70628
71171
  ];
70629
- },
70630
- addNodeView() {
70631
- return (props) => {
70632
- return new StructuredContentBlockView({ ...props });
70633
- };
70634
71172
  }
70635
71173
  });
70636
- class DocumentSectionView {
70637
- constructor(node, getPos, decorations, editor) {
70638
- __privateAdd$1(this, _DocumentSectionView_instances);
70639
- this.node = node;
70640
- this.editor = editor;
70641
- this.decorations = decorations;
70642
- this.view = editor.view;
70643
- this.getPos = getPos;
70644
- __privateMethod$1(this, _DocumentSectionView_instances, init_fn3).call(this);
70645
- }
70646
- }
70647
- _DocumentSectionView_instances = /* @__PURE__ */ new WeakSet();
70648
- init_fn3 = function() {
70649
- const { attrs } = this.node;
70650
- const { id, title, description } = attrs;
70651
- this.dom = document.createElement("div");
70652
- this.dom.className = "sd-document-section-block";
70653
- this.dom.setAttribute("data-id", id);
70654
- this.dom.setAttribute("data-title", title);
70655
- this.dom.setAttribute("data-description", description);
70656
- this.dom.setAttribute("aria-label", "Document section");
70657
- __privateMethod$1(this, _DocumentSectionView_instances, addToolTip_fn).call(this);
70658
- this.contentDOM = document.createElement("div");
70659
- this.contentDOM.className = "sd-document-section-block-content";
70660
- this.contentDOM.setAttribute("contenteditable", "true");
70661
- this.dom.appendChild(this.contentDOM);
70662
- };
70663
- addToolTip_fn = function() {
70664
- const { title } = this.node.attrs;
70665
- this.infoDiv = document.createElement("div");
70666
- this.infoDiv.className = "sd-document-section-block-info";
70667
- const textSpan = document.createElement("span");
70668
- textSpan.textContent = title || "Document section";
70669
- this.infoDiv.appendChild(textSpan);
70670
- this.infoDiv.setAttribute("contenteditable", "false");
70671
- this.dom.appendChild(this.infoDiv);
70672
- };
70673
- const getAllSections = (editor) => {
70674
- if (!editor) return [];
70675
- const type2 = editor.schema.nodes.documentSection;
70676
- if (!type2) return [];
70677
- const sections = [];
70678
- const { state: state2 } = editor;
70679
- state2.doc.descendants((node, pos) => {
70680
- if (node.type.name === type2.name) {
70681
- sections.push({ node, pos });
70682
- }
70683
- });
70684
- return sections;
70685
- };
70686
- const exportSectionsToHTML = (editor) => {
70687
- const sections = getAllSections(editor);
70688
- const processedSections = /* @__PURE__ */ new Set();
70689
- const result = [];
70690
- sections.forEach(({ node }) => {
70691
- const { attrs } = node;
70692
- const { id, title, description } = attrs;
70693
- if (processedSections.has(id)) return;
70694
- processedSections.add(id);
70695
- const html = getHTMLFromNode(node, editor);
70696
- result.push({
70697
- id,
70698
- title,
70699
- description,
70700
- html
70701
- });
70702
- });
70703
- return result;
70704
- };
70705
- const getHTMLFromNode = (node, editor) => {
70706
- const tempDocument = document.implementation.createHTMLDocument();
70707
- const container = tempDocument.createElement("div");
70708
- const fragment = DOMSerializer.fromSchema(editor.schema).serializeFragment(node.content);
70709
- container.appendChild(fragment);
70710
- let html = container.innerHTML;
70711
- return html;
70712
- };
70713
- const exportSectionsToJSON = (editor) => {
70714
- const sections = getAllSections(editor);
70715
- const processedSections = /* @__PURE__ */ new Set();
70716
- const result = [];
70717
- sections.forEach(({ node }) => {
70718
- const { attrs } = node;
70719
- const { id, title, description } = attrs;
70720
- if (processedSections.has(id)) return;
70721
- processedSections.add(id);
70722
- result.push({
70723
- id,
70724
- title,
70725
- description,
70726
- content: node.toJSON()
70727
- });
70728
- });
70729
- return result;
70730
- };
70731
- const getLinkedSectionEditor = (id, options, editor) => {
70732
- const sections = getAllSections(editor);
70733
- const section = sections.find((s) => s.node.attrs.id === id);
70734
- if (!section) return null;
70735
- const child = editor.createChildEditor({
70736
- ...options,
70737
- onUpdate: ({ editor: childEditor, transaction }) => {
70738
- const isFromtLinkedParent = transaction.getMeta("fromLinkedParent");
70739
- if (isFromtLinkedParent) return;
70740
- const updatedContent = childEditor.state.doc.content;
70741
- const sectionNode = getAllSections(editor)?.find((s) => s.node.attrs.id === id);
70742
- if (!sectionNode) return;
70743
- const { pos, node } = sectionNode;
70744
- const newNode = node.type.create(node.attrs, updatedContent, node.marks);
70745
- const tr = editor.state.tr.replaceWith(pos, pos + node.nodeSize, newNode);
70746
- tr.setMeta("fromLinkedChild", true);
70747
- editor.view.dispatch(tr);
70748
- }
70749
- });
70750
- editor.on("update", ({ transaction }) => {
70751
- const isFromLinkedChild = transaction.getMeta("fromLinkedChild");
70752
- if (isFromLinkedChild) return;
70753
- const sectionNode = getAllSections(editor)?.find((s) => s.node.attrs.id === id);
70754
- if (!sectionNode) return;
70755
- const sectionContent = sectionNode.node.content;
70756
- const json = {
70757
- type: "doc",
70758
- content: sectionContent.content.map((node) => node.toJSON())
70759
- };
70760
- const childTr = child.state.tr;
70761
- childTr.setMeta("fromLinkedParent", true);
70762
- childTr.replaceWith(0, child.state.doc.content.size, child.schema.nodeFromJSON(json));
70763
- child.view.dispatch(childTr);
70764
- });
70765
- return child;
70766
- };
70767
- const SectionHelpers = {
70768
- getAllSections,
70769
- exportSectionsToHTML,
70770
- exportSectionsToJSON,
70771
- getLinkedSectionEditor
70772
- };
70773
- const DocumentSection = Node$1.create({
70774
- name: "documentSection",
71174
+ const ShapeTextbox = Node$1.create({
71175
+ name: "shapeTextbox",
70775
71176
  group: "block",
70776
- content: "block*",
70777
- atom: true,
71177
+ content: "paragraph* block*",
70778
71178
  isolating: true,
70779
71179
  addOptions() {
70780
71180
  return {
70781
71181
  htmlAttributes: {
70782
- class: "sd-document-section-block",
70783
- "aria-label": "Structured content block"
71182
+ class: "sd-editor-shape-textbox",
71183
+ "aria-label": "Shape textbox node"
70784
71184
  }
70785
71185
  };
70786
71186
  },
70787
- parseDOM() {
70788
- return [
70789
- {
70790
- tag: "div.sd-document-section-block",
70791
- priority: 60
70792
- }
70793
- ];
70794
- },
70795
- renderDOM({ htmlAttributes }) {
70796
- return ["div", Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes), 0];
70797
- },
70798
71187
  addAttributes() {
70799
71188
  return {
70800
- id: {},
70801
71189
  sdBlockId: {
70802
71190
  default: null,
70803
71191
  keepOnSplit: false,
@@ -70806,212 +71194,131 @@ Please report this to https://github.com/markedjs/marked.`, e) {
70806
71194
  return attrs.sdBlockId ? { "data-sd-block-id": attrs.sdBlockId } : {};
70807
71195
  }
70808
71196
  },
70809
- title: {},
70810
- description: {},
70811
- sectionType: {},
70812
- isLocked: { default: false }
71197
+ attributes: {
71198
+ rendered: false
71199
+ }
70813
71200
  };
70814
71201
  },
70815
- addNodeView() {
70816
- return ({ node, editor, getPos, decorations }) => {
70817
- return new DocumentSectionView(node, getPos, decorations, editor);
71202
+ parseDOM() {
71203
+ return [
71204
+ {
71205
+ tag: `div[data-type="${this.name}"]`
71206
+ }
71207
+ ];
71208
+ },
71209
+ renderDOM({ htmlAttributes }) {
71210
+ return [
71211
+ "div",
71212
+ Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes, { "data-type": this.name }),
71213
+ 0
71214
+ ];
71215
+ }
71216
+ });
71217
+ const ContentBlock = Node$1.create({
71218
+ name: "contentBlock",
71219
+ group: "inline",
71220
+ content: "",
71221
+ isolating: true,
71222
+ atom: true,
71223
+ inline: true,
71224
+ addOptions() {
71225
+ return {
71226
+ htmlAttributes: {
71227
+ contenteditable: false
71228
+ }
70818
71229
  };
70819
71230
  },
70820
- addCommands() {
71231
+ addAttributes() {
70821
71232
  return {
70822
- /**
70823
- * Create a lockable content section
70824
- * @category Command
70825
- * @param {SectionCreate} [options={}] - Section configuration
70826
- * @example
70827
- * editor.commands.createDocumentSection({
70828
- * id: 1,
70829
- * title: 'Terms & Conditions',
70830
- * isLocked: true,
70831
- * html: '<p>Legal content...</p>'
70832
- * })
70833
- */
70834
- createDocumentSection: (options = {}) => ({ tr, state: state2, dispatch, editor }) => {
70835
- const { selection } = state2;
70836
- let { from: from2, to } = selection;
70837
- let content = selection.content().content;
70838
- const { html: optionsHTML, json: optionsJSON } = options;
70839
- if (optionsHTML) {
70840
- const html = htmlHandler(optionsHTML, this.editor);
70841
- const doc2 = DOMParser$1.fromSchema(this.editor.schema).parse(html);
70842
- content = doc2.content;
70843
- }
70844
- if (optionsJSON) {
70845
- content = this.editor.schema.nodeFromJSON(optionsJSON);
70846
- }
70847
- if (!content?.content?.length) {
70848
- content = this.editor.schema.nodeFromJSON({ type: "paragraph", content: [] });
70849
- }
70850
- if (!options.id) {
70851
- const allSections = SectionHelpers.getAllSections(editor);
70852
- options.id = allSections.length + 1;
70853
- }
70854
- if (!options.title) {
70855
- options.title = "Document section";
70856
- }
70857
- const node = this.type.createAndFill(options, content);
70858
- if (!node) return false;
70859
- const isAlreadyInSdtBlock = findParentNode((node2) => node2.type.name === "documentSection")(selection);
70860
- if (isAlreadyInSdtBlock && isAlreadyInSdtBlock.node) {
70861
- const insertPos2 = isAlreadyInSdtBlock.pos + isAlreadyInSdtBlock.node.nodeSize;
70862
- from2 = insertPos2;
70863
- to = insertPos2;
70864
- }
70865
- tr.replaceRangeWith(from2, to, node);
70866
- const nodeEnd = from2 + node.nodeSize;
70867
- let shouldInsertParagraph = true;
70868
- let insertPos = nodeEnd;
70869
- if (nodeEnd >= tr.doc.content.size) {
70870
- insertPos = tr.doc.content.size;
70871
- if (insertPos > 0) {
70872
- const $endPos = tr.doc.resolve(insertPos);
70873
- if ($endPos.nodeBefore && $endPos.nodeBefore.type.name === "paragraph") {
70874
- shouldInsertParagraph = false;
70875
- }
70876
- }
70877
- }
70878
- if (shouldInsertParagraph) {
70879
- const emptyParagraph = tr.doc.type.schema.nodes.paragraph.create();
70880
- tr.insert(insertPos, emptyParagraph);
70881
- }
70882
- if (dispatch) {
70883
- tr.setMeta("documentSection", { action: "create" });
70884
- dispatch(tr);
70885
- setTimeout(() => {
70886
- try {
70887
- const currentState = editor.state;
70888
- const docSize = currentState.doc.content.size;
70889
- let targetPos = from2 + node.nodeSize;
70890
- if (shouldInsertParagraph) {
70891
- targetPos += 1;
70892
- }
70893
- targetPos = Math.min(targetPos, docSize);
70894
- if (targetPos < docSize && targetPos > 0) {
70895
- const newSelection = Selection.near(currentState.doc.resolve(targetPos));
70896
- const newTr = currentState.tr.setSelection(newSelection);
70897
- editor.view.dispatch(newTr);
70898
- }
70899
- } catch (e) {
70900
- console.warn("Could not set delayed selection:", e);
70901
- }
70902
- }, 0);
71233
+ horizontalRule: {
71234
+ default: false,
71235
+ renderDOM: ({ horizontalRule }) => {
71236
+ if (!horizontalRule) return {};
71237
+ return { "data-horizontal-rule": "true" };
70903
71238
  }
70904
- return true;
70905
71239
  },
70906
- /**
70907
- * Remove section wrapper at cursor, preserving its content
70908
- * @category Command
70909
- * @example
70910
- * editor.commands.removeSectionAtSelection()
70911
- * @note Content stays in document, only section wrapper is removed
70912
- */
70913
- removeSectionAtSelection: () => ({ tr, dispatch }) => {
70914
- const sdtNode = findParentNode((node2) => node2.type.name === "documentSection")(tr.selection);
70915
- if (!sdtNode) return false;
70916
- const { node, pos } = sdtNode;
70917
- const nodeStart = pos;
70918
- const nodeEnd = nodeStart + node.nodeSize;
70919
- const contentToPreserve = node.content;
70920
- tr.delete(nodeStart, nodeEnd);
70921
- if (contentToPreserve.size > 0) {
70922
- tr.insert(nodeStart, contentToPreserve);
70923
- }
70924
- const newPos = Math.min(nodeStart, tr.doc.content.size);
70925
- tr.setSelection(Selection.near(tr.doc.resolve(newPos)));
70926
- if (dispatch) {
70927
- tr.setMeta("documentSection", { action: "delete" });
70928
- dispatch(tr);
71240
+ size: {
71241
+ default: null,
71242
+ renderDOM: ({ size: size2 }) => {
71243
+ if (!size2) return {};
71244
+ let style2 = "";
71245
+ if (size2.top) style2 += `top: ${size2.top}px; `;
71246
+ if (size2.left) style2 += `left: ${size2.left}px; `;
71247
+ if (size2.width) style2 += `width: ${size2.width.toString().endsWith("%") ? size2.width : `${size2.width}px`}; `;
71248
+ if (size2.height)
71249
+ style2 += `height: ${size2.height.toString().endsWith("%") ? size2.height : `${size2.height}px`}; `;
71250
+ return { style: style2 };
70929
71251
  }
70930
- return true;
70931
71252
  },
70932
- /**
70933
- * Delete section and all its content
70934
- * @category Command
70935
- * @param {number} id - Section to delete
70936
- * @example
70937
- * editor.commands.removeSectionById(123)
70938
- */
70939
- removeSectionById: (id) => ({ tr, dispatch }) => {
70940
- const sections = SectionHelpers.getAllSections(this.editor);
70941
- const sectionToRemove = sections.find(({ node: node2 }) => node2.attrs.id === id);
70942
- if (!sectionToRemove) return false;
70943
- const { pos, node } = sectionToRemove;
70944
- const nodeStart = pos;
70945
- const nodeEnd = nodeStart + node.nodeSize;
70946
- tr.delete(nodeStart, nodeEnd);
70947
- if (dispatch) {
70948
- tr.setMeta("documentSection", { action: "delete", id });
70949
- dispatch(tr);
71253
+ background: {
71254
+ default: null,
71255
+ renderDOM: (attrs) => {
71256
+ if (!attrs.background) return {};
71257
+ return {
71258
+ style: `background-color: ${attrs.background}`
71259
+ };
70950
71260
  }
70951
- return true;
70952
71261
  },
71262
+ drawingContent: {
71263
+ rendered: false
71264
+ },
71265
+ attributes: {
71266
+ rendered: false
71267
+ }
71268
+ };
71269
+ },
71270
+ parseDOM() {
71271
+ return [
71272
+ {
71273
+ tag: `div[data-type="${this.name}"]`
71274
+ }
71275
+ ];
71276
+ },
71277
+ renderDOM({ htmlAttributes }) {
71278
+ return ["div", Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes, { "data-type": this.name })];
71279
+ },
71280
+ addCommands() {
71281
+ return {
70953
71282
  /**
70954
- * Lock section against edits
71283
+ * Insert a horizontal rule
70955
71284
  * @category Command
70956
- * @param {number} id - Section to lock
70957
71285
  * @example
70958
- * editor.commands.lockSectionById(123)
71286
+ * editor.commands.insertHorizontalRule()
71287
+ * @note Creates a visual separator between content sections
70959
71288
  */
70960
- lockSectionById: (id) => ({ tr, dispatch }) => {
70961
- const sections = SectionHelpers.getAllSections(this.editor);
70962
- const sectionToLock = sections.find(({ node }) => node.attrs.id === id);
70963
- if (!sectionToLock) return false;
70964
- tr.setNodeMarkup(sectionToLock.pos, null, { ...sectionToLock.node.attrs, isLocked: true });
70965
- if (dispatch) {
70966
- tr.setMeta("documentSection", { action: "lock", id });
70967
- dispatch(tr);
70968
- }
70969
- return true;
71289
+ insertHorizontalRule: () => ({ commands: commands2 }) => {
71290
+ return commands2.insertContent({
71291
+ type: this.name,
71292
+ attrs: {
71293
+ horizontalRule: true,
71294
+ size: { width: "100%", height: 2 },
71295
+ background: "#e5e7eb"
71296
+ }
71297
+ });
70970
71298
  },
70971
71299
  /**
70972
- * Modify section attributes or content
71300
+ * Insert a content block
70973
71301
  * @category Command
70974
- * @param {SectionUpdate} options - Changes to apply
71302
+ * @param {ContentBlockConfig} config - Block configuration
70975
71303
  * @example
70976
- * editor.commands.updateSectionById({ id: 123, attrs: { isLocked: false } })
70977
- * editor.commands.updateSectionById({ id: 123, html: '<p>New content</p>' })
70978
- * editor.commands.updateSectionById({
70979
- * id: 123,
70980
- * html: '<p>Updated</p>',
70981
- * attrs: { title: 'New Title' }
71304
+ * // Insert a spacer block
71305
+ * editor.commands.insertContentBlock({ size: { height: 20 } })
71306
+ *
71307
+ * @example
71308
+ * // Insert a colored divider
71309
+ * editor.commands.insertContentBlock({
71310
+ * size: { width: '50%', height: 3 },
71311
+ * background: '#3b82f6'
70982
71312
  * })
71313
+ * @note Used for spacing, dividers, and special inline content
70983
71314
  */
70984
- updateSectionById: ({ id, html, json, attrs }) => ({ tr, dispatch, editor }) => {
70985
- const sections = SectionHelpers.getAllSections(editor || this.editor);
70986
- const sectionToUpdate = sections.find(({ node: node2 }) => node2.attrs.id === id);
70987
- if (!sectionToUpdate) return false;
70988
- const { pos, node } = sectionToUpdate;
70989
- let newContent = null;
70990
- if (html) {
70991
- const htmlDoc = htmlHandler(html, editor || this.editor);
70992
- const doc2 = DOMParser$1.fromSchema((editor || this.editor).schema).parse(htmlDoc);
70993
- newContent = doc2.content;
70994
- }
70995
- if (json) {
70996
- newContent = (editor || this.editor).schema.nodeFromJSON(json);
70997
- }
70998
- if (!newContent) {
70999
- newContent = node.content;
71000
- }
71001
- const updatedNode = node.type.create({ ...node.attrs, ...attrs }, newContent, node.marks);
71002
- tr.replaceWith(pos, pos + node.nodeSize, updatedNode);
71003
- if (dispatch) {
71004
- tr.setMeta("documentSection", { action: "update", id, attrs });
71005
- dispatch(tr);
71006
- }
71007
- return true;
71315
+ insertContentBlock: (config2) => ({ commands: commands2 }) => {
71316
+ return commands2.insertContent({
71317
+ type: this.name,
71318
+ attrs: config2
71319
+ });
71008
71320
  }
71009
71321
  };
71010
- },
71011
- addHelpers() {
71012
- return {
71013
- ...SectionHelpers
71014
- };
71015
71322
  }
71016
71323
  });
71017
71324
  const { findChildren } = helpers;
@@ -77861,6 +78168,7 @@ Please report this to https://github.com/markedjs/marked.`, e) {
77861
78168
  Search,
77862
78169
  StructuredContent,
77863
78170
  StructuredContentBlock,
78171
+ StructuredContentCommands,
77864
78172
  DocumentSection,
77865
78173
  NodeResizer,
77866
78174
  CustomSelection,
@@ -110777,7 +111085,7 @@ ${style2}
110777
111085
  this.config.colors = shuffleArray(this.config.colors);
110778
111086
  this.userColorMap = /* @__PURE__ */ new Map();
110779
111087
  this.colorIndex = 0;
110780
- this.version = "0.21.0";
111088
+ this.version = "0.22.0";
110781
111089
  this.#log("🦋 [superdoc] Using SuperDoc version:", this.version);
110782
111090
  this.superdocId = config2.superdocId || v4();
110783
111091
  this.colors = this.config.colors;