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

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-BNWaI4WI.es.js} +1 -1
  2. package/dist/chunks/{PdfViewer-DyWe33pN.cjs → PdfViewer-DpkgwUPi.cjs} +1 -1
  3. package/dist/chunks/{index-BeVpZc19.cjs → index-BbGPYtNy.cjs} +2 -2
  4. package/dist/chunks/{index-ir6efMuz.es.js → index-DWKEKmiB.es.js} +2 -2
  5. package/dist/chunks/{super-editor.es-BwqYS285.es.js → super-editor.es-BVxfhpAJ.es.js} +1581 -1274
  6. package/dist/chunks/{super-editor.es-CKfdmK-8.cjs → super-editor.es-BoUJEZaF.cjs} +1581 -1274
  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-C-yWLpFM.js} +150 -105
  11. package/dist/super-editor/chunks/{docx-zipper-ByLK3trM.js → docx-zipper-CmGlSUQM.js} +1 -1
  12. package/dist/super-editor/chunks/{editor-CFqh_xBx.js → editor-BBnC1DzI.js} +1436 -1172
  13. package/dist/super-editor/chunks/{toolbar-DdfyWgZF.js → toolbar-QJANo61B.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 +1581 -1274
  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
  };
@@ -40794,7 +40837,7 @@ Please report this to https://github.com/markedjs/marked.`, e) {
40794
40837
  gutter: "0"
40795
40838
  })
40796
40839
  });
40797
- function ensureSectionProperties(bodyNode, converter) {
40840
+ function ensureSectionProperties(bodyNode) {
40798
40841
  if (!bodyNode.elements) bodyNode.elements = [];
40799
40842
  let sectPr = bodyNode.elements.find((el) => el.name === "w:sectPr");
40800
40843
  if (!sectPr) {
@@ -44293,7 +44336,7 @@ Please report this to https://github.com/markedjs/marked.`, e) {
44293
44336
  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
44337
  var __privateSet = (obj, member, value, setter) => (__accessCheck$1(obj, member, "write to private field"), member.set(obj, value), value);
44295
44338
  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;
44339
+ 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
44340
  var GOOD_LEAF_SIZE = 200;
44298
44341
  var RopeSequence = function RopeSequence2() {
44299
44342
  };
@@ -56368,7 +56411,7 @@ Please report this to https://github.com/markedjs/marked.`, e) {
56368
56411
  if (emitParams) editor.emit("commentsUpdate", emitParams);
56369
56412
  return newTrackedChanges;
56370
56413
  };
56371
- const getTrackedChangeText = ({ state: state2, nodes, mark, marks, trackedChangeType, isDeletionInsertion }) => {
56414
+ const getTrackedChangeText = ({ nodes, mark, trackedChangeType, isDeletionInsertion }) => {
56372
56415
  let trackedChangeText = "";
56373
56416
  let deletionText = "";
56374
56417
  if (trackedChangeType === TrackInsertMarkName) {
@@ -56410,10 +56453,8 @@ Please report this to https://github.com/markedjs/marked.`, e) {
56410
56453
  if (hasMatchingId) nodesWithMark.push(node2);
56411
56454
  });
56412
56455
  const { deletionText, trackedChangeText } = getTrackedChangeText({
56413
- state: newEditorState,
56414
56456
  nodes: nodesWithMark.length ? nodesWithMark : [node],
56415
56457
  mark: trackedMark,
56416
- marks,
56417
56458
  trackedChangeType,
56418
56459
  isDeletionInsertion
56419
56460
  });
@@ -61068,228 +61109,1223 @@ Please report this to https://github.com/markedjs/marked.`, e) {
61068
61109
  return this.editor.options.isHeadless ? [] : [slashMenuPlugin];
61069
61110
  }
61070
61111
  });
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>");
61112
+ class StructuredContentViewBase {
61113
+ constructor(props) {
61114
+ __publicField$1(this, "node");
61115
+ __publicField$1(this, "view");
61116
+ __publicField$1(this, "getPos");
61117
+ __publicField$1(this, "decorations");
61118
+ __publicField$1(this, "innerDecorations");
61119
+ __publicField$1(this, "editor");
61120
+ __publicField$1(this, "extension");
61121
+ __publicField$1(this, "htmlAttributes");
61122
+ __publicField$1(this, "root");
61123
+ __publicField$1(this, "isDragging", false);
61124
+ this.node = props.node;
61125
+ this.view = props.editor.view;
61126
+ this.getPos = props.getPos;
61127
+ this.decorations = props.decorations;
61128
+ this.innerDecorations = props.innerDecorations;
61129
+ this.editor = props.editor;
61130
+ this.extension = props.extension;
61131
+ this.htmlAttributes = props.htmlAttributes;
61132
+ this.mount(props);
61133
+ }
61134
+ mount() {
61135
+ return;
61136
+ }
61137
+ get dom() {
61138
+ return this.root;
61139
+ }
61140
+ get contentDOM() {
61141
+ return null;
61142
+ }
61143
+ update(node, decorations, innerDecorations) {
61144
+ if (node.type !== this.node.type) {
61145
+ return false;
61146
+ }
61147
+ this.node = node;
61148
+ this.decorations = decorations;
61149
+ this.innerDecorations = innerDecorations;
61150
+ this.updateHTMLAttributes();
61151
+ return true;
61152
+ }
61153
+ stopEvent(event) {
61154
+ if (!this.dom) return false;
61155
+ const target = event.target;
61156
+ const isInElement = this.dom.contains(target) && !this.contentDOM?.contains(target);
61157
+ if (!isInElement) return false;
61158
+ const isDragEvent = event.type.startsWith("drag");
61159
+ const isDropEvent = event.type === "drop";
61160
+ const isInput = ["INPUT", "BUTTON", "SELECT", "TEXTAREA"].includes(target.tagName) || target.isContentEditable;
61161
+ if (isInput && !isDropEvent && !isDragEvent) return true;
61162
+ const { isEditable } = this.editor;
61163
+ const { isDragging } = this;
61164
+ const isDraggable = !!this.node.type.spec.draggable;
61165
+ const isSelectable = NodeSelection.isSelectable(this.node);
61166
+ const isCopyEvent = event.type === "copy";
61167
+ const isPasteEvent = event.type === "paste";
61168
+ const isCutEvent = event.type === "cut";
61169
+ const isClickEvent = event.type === "mousedown";
61170
+ if (!isDraggable && isSelectable && isDragEvent && event.target === this.dom) {
61171
+ event.preventDefault();
61172
+ }
61173
+ if (isDraggable && isDragEvent && !isDragging && event.target === this.dom) {
61174
+ event.preventDefault();
61175
+ return false;
61176
+ }
61177
+ if (isDraggable && isEditable && !isDragging && isClickEvent) {
61178
+ const dragHandle = target.closest("[data-drag-handle]");
61179
+ const isValidDragHandle = dragHandle && (this.dom === dragHandle || this.dom.contains(dragHandle));
61180
+ if (isValidDragHandle) {
61181
+ this.isDragging = true;
61182
+ document.addEventListener(
61183
+ "dragend",
61184
+ () => {
61185
+ this.isDragging = false;
61186
+ },
61187
+ { once: true }
61188
+ );
61189
+ document.addEventListener(
61190
+ "drop",
61191
+ () => {
61192
+ this.isDragging = false;
61193
+ },
61194
+ { once: true }
61195
+ );
61196
+ document.addEventListener(
61197
+ "mouseup",
61198
+ () => {
61199
+ this.isDragging = false;
61200
+ },
61201
+ { once: true }
61202
+ );
61120
61203
  }
61121
- };
61204
+ }
61205
+ if (isDragging || isDropEvent || isCopyEvent || isPasteEvent || isCutEvent || isClickEvent && isSelectable) {
61206
+ return false;
61207
+ }
61208
+ return true;
61122
61209
  }
61123
- });
61124
- const Text = Node$1.create({
61125
- name: "text",
61126
- group: "inline",
61127
- inline: true,
61128
- addOptions() {
61129
- return {};
61210
+ ignoreMutation(mutation) {
61211
+ if (!this.dom || !this.contentDOM) return true;
61212
+ if (this.node.isLeaf || this.node.isAtom) return true;
61213
+ if (mutation.type === "selection") return false;
61214
+ if (this.contentDOM === mutation.target && mutation.type === "attributes") return true;
61215
+ if (this.contentDOM.contains(mutation.target)) return false;
61216
+ return true;
61130
61217
  }
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) => {
61218
+ destroy() {
61219
+ this.dom.remove();
61220
+ this.contentDOM?.remove();
61221
+ }
61222
+ updateAttributes(attrs) {
61223
+ const pos = this.getPos();
61224
+ if (typeof pos !== "number") {
61225
+ return;
61226
+ }
61227
+ return this.view.dispatch(
61228
+ this.view.state.tr.setNodeMarkup(pos, void 0, {
61229
+ ...this.node.attrs,
61230
+ ...attrs
61231
+ })
61232
+ );
61233
+ }
61234
+ updateHTMLAttributes() {
61235
+ const { extensionService } = this.editor;
61236
+ const { attributes } = extensionService;
61237
+ const extensionAttrs = attributes.filter((i2) => i2.type === this.node.type.name);
61238
+ this.htmlAttributes = Attribute.getAttributesToRender(this.node, extensionAttrs);
61239
+ }
61240
+ createDragHandle() {
61241
+ const dragHandle = document.createElement("span");
61242
+ dragHandle.classList.add("sd-structured-content-draggable");
61243
+ dragHandle.draggable = true;
61244
+ dragHandle.contentEditable = "false";
61245
+ dragHandle.dataset.dragHandle = "";
61246
+ const textElement = document.createElement("span");
61247
+ textElement.textContent = this.node.attrs.alias || "Structured content";
61248
+ dragHandle.append(textElement);
61249
+ return dragHandle;
61250
+ }
61251
+ onDragStart(event) {
61252
+ const { view } = this.editor;
61253
+ const target = event.target;
61254
+ const dragHandle = target.nodeType === 3 ? target.parentElement?.closest("[data-drag-handle]") : target.closest("[data-drag-handle]");
61255
+ if (!this.dom || this.contentDOM?.contains(target) || !dragHandle) {
61256
+ return;
61257
+ }
61258
+ let x = 0;
61259
+ let y2 = 0;
61260
+ if (this.dom !== dragHandle) {
61261
+ const domBox = this.dom.getBoundingClientRect();
61262
+ const handleBox = dragHandle.getBoundingClientRect();
61263
+ const offsetX = event.offsetX ?? event.nativeEvent?.offsetX;
61264
+ const offsetY = event.offsetY ?? event.nativeEvent?.offsetY;
61265
+ x = handleBox.x - domBox.x + offsetX;
61266
+ y2 = handleBox.y - domBox.y + offsetY;
61267
+ }
61268
+ event.dataTransfer?.setDragImage(this.dom, x, y2);
61269
+ const pos = this.getPos();
61270
+ if (typeof pos !== "number") {
61271
+ return;
61272
+ }
61273
+ const selection = NodeSelection.create(view.state.doc, pos);
61274
+ const transaction = view.state.tr.setSelection(selection);
61138
61275
  view.dispatch(transaction);
61139
- });
61140
- if (handled) {
61141
- tr.setMeta("preventDispatch", true);
61142
61276
  }
61143
- return handled;
61144
- };
61145
- const Run = OxmlNode.create({
61146
- name: "run",
61147
- oXmlName: "w:r",
61148
- group: "inline",
61277
+ }
61278
+ class StructuredContentInlineView extends StructuredContentViewBase {
61279
+ constructor(props) {
61280
+ super(props);
61281
+ }
61282
+ mount() {
61283
+ this.buildView();
61284
+ }
61285
+ get contentDOM() {
61286
+ const contentElement = this.dom?.querySelector(`.${structuredContentInnerClass$1}`);
61287
+ return contentElement || null;
61288
+ }
61289
+ createElement() {
61290
+ const element = document.createElement("span");
61291
+ element.classList.add(structuredContentClass$1);
61292
+ element.setAttribute("data-structured-content", "");
61293
+ const contentElement = document.createElement("span");
61294
+ contentElement.classList.add(structuredContentInnerClass$1);
61295
+ element.append(contentElement);
61296
+ const domAttrs = Attribute.mergeAttributes(this.htmlAttributes);
61297
+ updateDOMAttributes(element, { ...domAttrs });
61298
+ return { element, contentElement };
61299
+ }
61300
+ buildView() {
61301
+ const { element } = this.createElement();
61302
+ const dragHandle = this.createDragHandle();
61303
+ element.prepend(dragHandle);
61304
+ element.addEventListener("dragstart", (e) => this.onDragStart(e));
61305
+ this.root = element;
61306
+ }
61307
+ updateView() {
61308
+ const domAttrs = Attribute.mergeAttributes(this.htmlAttributes);
61309
+ updateDOMAttributes(this.dom, { ...domAttrs });
61310
+ }
61311
+ update(node, decorations, innerDecorations) {
61312
+ const result = super.update(node, decorations, innerDecorations);
61313
+ if (!result) return false;
61314
+ this.updateView();
61315
+ return true;
61316
+ }
61317
+ }
61318
+ const structuredContentClass$1 = "sd-structured-content";
61319
+ const structuredContentInnerClass$1 = "sd-structured-content__content";
61320
+ const StructuredContent = Node$1.create({
61321
+ name: "structuredContent",
61322
+ group: "inline structuredContent",
61149
61323
  inline: true,
61150
61324
  content: "inline*",
61151
- selectable: false,
61152
- childToAttributes: ["runProperties"],
61325
+ isolating: true,
61326
+ atom: false,
61327
+ // false - has editable content.
61328
+ draggable: true,
61153
61329
  addOptions() {
61154
61330
  return {
61155
61331
  htmlAttributes: {
61156
- "data-run": "1"
61332
+ class: structuredContentClass$1,
61333
+ "aria-label": "Structured content node"
61157
61334
  }
61158
61335
  };
61159
61336
  },
61160
61337
  addAttributes() {
61161
61338
  return {
61162
- runProperties: {
61339
+ id: {
61163
61340
  default: null,
61164
- rendered: false,
61165
- keepOnSplit: true
61341
+ parseDOM: (elem) => elem.getAttribute("data-id"),
61342
+ renderDOM: (attrs) => {
61343
+ if (!attrs.id) return {};
61344
+ return { "data-id": attrs.id };
61345
+ }
61166
61346
  },
61167
- rsidR: {
61347
+ tag: {
61168
61348
  default: null,
61169
- rendered: false,
61170
- keepOnSplit: true
61349
+ parseDOM: (elem) => elem.getAttribute("data-tag"),
61350
+ renderDOM: (attrs) => {
61351
+ if (!attrs.tag) return {};
61352
+ return { "data-tag": attrs.tag };
61353
+ }
61171
61354
  },
61172
- rsidRPr: {
61355
+ alias: {
61173
61356
  default: null,
61174
- rendered: false,
61175
- keepOnSplit: true
61357
+ parseDOM: (elem) => elem.getAttribute("data-alias"),
61358
+ renderDOM: (attrs) => {
61359
+ if (!attrs.alias) return {};
61360
+ return { "data-alias": attrs.alias };
61361
+ }
61176
61362
  },
61177
- rsidDel: {
61178
- default: null,
61179
- rendered: false,
61180
- keepOnSplit: true
61363
+ sdtPr: {
61364
+ rendered: false
61181
61365
  }
61182
61366
  };
61183
61367
  },
61184
- addCommands() {
61185
- return {
61186
- splitRun
61187
- };
61188
- },
61189
61368
  parseDOM() {
61190
- return [{ tag: "span[data-run]" }];
61369
+ return [{ tag: "span[data-structured-content]" }];
61191
61370
  },
61192
61371
  renderDOM({ htmlAttributes }) {
61193
- const base2 = Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes);
61194
- return ["span", base2, 0];
61372
+ return [
61373
+ "span",
61374
+ Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes, {
61375
+ "data-structured-content": ""
61376
+ }),
61377
+ 0
61378
+ ];
61379
+ },
61380
+ addNodeView() {
61381
+ return (props) => {
61382
+ return new StructuredContentInlineView({ ...props });
61383
+ };
61195
61384
  }
61196
61385
  });
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
- },
61386
+ class StructuredContentBlockView extends StructuredContentViewBase {
61387
+ constructor(props) {
61388
+ super(props);
61389
+ }
61390
+ mount() {
61391
+ this.buildView();
61392
+ }
61393
+ get contentDOM() {
61394
+ const contentElement = this.dom?.querySelector(`.${structuredContentInnerClass}`);
61395
+ return contentElement || null;
61396
+ }
61397
+ createElement() {
61398
+ const element = document.createElement("div");
61399
+ element.classList.add(structuredContentClass);
61400
+ element.setAttribute("data-structured-content-block", "");
61401
+ const contentElement = document.createElement("div");
61402
+ contentElement.classList.add(structuredContentInnerClass);
61403
+ element.append(contentElement);
61404
+ const domAttrs = Attribute.mergeAttributes(this.htmlAttributes);
61405
+ updateDOMAttributes(element, { ...domAttrs });
61406
+ return { element, contentElement };
61407
+ }
61408
+ buildView() {
61409
+ const { element } = this.createElement();
61410
+ const dragHandle = this.createDragHandle();
61411
+ element.prepend(dragHandle);
61412
+ element.addEventListener("dragstart", (e) => this.onDragStart(e));
61413
+ this.root = element;
61414
+ }
61415
+ updateView() {
61416
+ const domAttrs = Attribute.mergeAttributes(this.htmlAttributes);
61417
+ updateDOMAttributes(this.dom, { ...domAttrs });
61418
+ }
61419
+ update(node, decorations, innerDecorations) {
61420
+ const result = super.update(node, decorations, innerDecorations);
61421
+ if (!result) return false;
61422
+ this.updateView();
61423
+ return true;
61424
+ }
61425
+ }
61426
+ const structuredContentClass = "sd-structured-content-block";
61427
+ const structuredContentInnerClass = "sd-structured-content-block__content";
61428
+ const StructuredContentBlock = Node$1.create({
61429
+ name: "structuredContentBlock",
61430
+ group: "block structuredContent",
61431
+ content: "block*",
61432
+ isolating: true,
61433
+ atom: false,
61434
+ // false - has editable content.
61435
+ draggable: true,
61205
61436
  addOptions() {
61206
61437
  return {
61207
- itemTypeName: "listItem",
61208
61438
  htmlAttributes: {
61209
- "aria-label": "Bullet list node"
61210
- },
61211
- keepMarks: true,
61212
- keepAttributes: false
61439
+ class: structuredContentClass,
61440
+ "aria-label": "Structured content block node"
61441
+ }
61213
61442
  };
61214
61443
  },
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
61444
  addAttributes() {
61223
61445
  return {
61224
- "list-style-type": {
61225
- default: "bullet",
61226
- rendered: false
61446
+ id: {
61447
+ default: null,
61448
+ parseDOM: (elem) => elem.getAttribute("data-id"),
61449
+ renderDOM: (attrs) => {
61450
+ if (!attrs.id) return {};
61451
+ return { "data-id": attrs.id };
61452
+ }
61227
61453
  },
61228
- listId: {
61229
- rendered: false
61454
+ tag: {
61455
+ default: null,
61456
+ parseDOM: (elem) => elem.getAttribute("data-tag"),
61457
+ renderDOM: (attrs) => {
61458
+ if (!attrs.tag) return {};
61459
+ return { "data-tag": attrs.tag };
61460
+ }
61230
61461
  },
61231
- sdBlockId: {
61462
+ alias: {
61232
61463
  default: null,
61233
- keepOnSplit: false,
61234
- parseDOM: (elem) => elem.getAttribute("data-sd-block-id"),
61464
+ parseDOM: (elem) => elem.getAttribute("data-alias"),
61235
61465
  renderDOM: (attrs) => {
61236
- return attrs.sdBlockId ? { "data-sd-block-id": attrs.sdBlockId } : {};
61466
+ if (!attrs.alias) return {};
61467
+ return { "data-alias": attrs.alias };
61237
61468
  }
61238
61469
  },
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);
61470
+ sdtPr: {
61471
+ rendered: false
61257
61472
  }
61258
61473
  };
61259
61474
  },
61260
- addShortcuts() {
61261
- return {
61262
- "Mod-Shift-8": () => {
61263
- return this.editor.commands.toggleBulletList();
61264
- }
61265
- };
61475
+ parseDOM() {
61476
+ return [{ tag: "div[data-structured-content-block]" }];
61266
61477
  },
61267
- addInputRules() {
61478
+ renderDOM({ htmlAttributes }) {
61268
61479
  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
- })
61480
+ "div",
61481
+ Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes, {
61482
+ "data-structured-content-block": ""
61483
+ }),
61484
+ 0
61288
61485
  ];
61486
+ },
61487
+ addNodeView() {
61488
+ return (props) => {
61489
+ return new StructuredContentBlockView({ ...props });
61490
+ };
61289
61491
  }
61290
61492
  });
61291
- const inputRegex = /^(\d+)\.\s$/;
61292
- const OrderedList = Node$1.create({
61493
+ function getStructuredContentTagsById(idOrIds, state2) {
61494
+ const result = findChildren$5(state2.doc, (node) => {
61495
+ const isStructuredContent = ["structuredContent", "structuredContentBlock"].includes(node.type.name);
61496
+ if (Array.isArray(idOrIds)) {
61497
+ return isStructuredContent && idOrIds.includes(node.attrs.id);
61498
+ } else {
61499
+ return isStructuredContent && node.attrs.id === idOrIds;
61500
+ }
61501
+ });
61502
+ return result;
61503
+ }
61504
+ function getStructuredContentTags(state2) {
61505
+ const result = findChildren$5(state2.doc, (node) => {
61506
+ return node.type.name === "structuredContent" || node.type.name === "structuredContentBlock";
61507
+ });
61508
+ return result;
61509
+ }
61510
+ function getStructuredContentInlineTags(state2) {
61511
+ const result = findChildren$5(state2.doc, (node) => node.type.name === "structuredContent");
61512
+ return result;
61513
+ }
61514
+ function getStructuredContentBlockTags(state2) {
61515
+ const result = findChildren$5(state2.doc, (node) => node.type.name === "structuredContentBlock");
61516
+ return result;
61517
+ }
61518
+ const structuredContentHelpers = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
61519
+ __proto__: null,
61520
+ getStructuredContentBlockTags,
61521
+ getStructuredContentInlineTags,
61522
+ getStructuredContentTags,
61523
+ getStructuredContentTagsById
61524
+ }, Symbol.toStringTag, { value: "Module" }));
61525
+ const STRUCTURED_CONTENT_NAMES = ["structuredContent", "structuredContentBlock"];
61526
+ const StructuredContentCommands = Extension.create({
61527
+ name: "structuredContentCommands",
61528
+ addCommands() {
61529
+ return {
61530
+ /**
61531
+ * Inserts a structured content inline at selection.
61532
+ * @category Command
61533
+ * @param {StructuredContentInlineInsert} options
61534
+ */
61535
+ insertStructuredContentInline: (options = {}) => ({ editor, dispatch, state: state2, tr }) => {
61536
+ const { schema } = editor;
61537
+ let { from: from2, to } = state2.selection;
61538
+ if (dispatch) {
61539
+ const selectionText = state2.doc.textBetween(from2, to);
61540
+ let content = null;
61541
+ if (selectionText) {
61542
+ content = schema.text(selectionText);
61543
+ }
61544
+ if (options.text) {
61545
+ content = schema.text(options.text);
61546
+ }
61547
+ if (options.json) {
61548
+ content = schema.nodeFromJSON(options.json);
61549
+ }
61550
+ if (!content) {
61551
+ content = schema.text(" ");
61552
+ }
61553
+ const attrs = {
61554
+ ...options.attrs,
61555
+ id: options.attrs?.id || generateRandomSigned32BitIntStrId(),
61556
+ tag: "inline_text_sdt",
61557
+ alias: options.attrs?.alias || "Structured content"
61558
+ };
61559
+ const node = schema.nodes.structuredContent.create(attrs, content, null);
61560
+ const parent = findParentNode((node2) => node2.type.name === "structuredContent")(state2.selection);
61561
+ if (parent) {
61562
+ const insertPos = parent.pos + parent.node.nodeSize;
61563
+ from2 = to = insertPos;
61564
+ }
61565
+ tr.replaceWith(from2, to, node);
61566
+ }
61567
+ return true;
61568
+ },
61569
+ /**
61570
+ * Inserts a structured content block at selection.
61571
+ * @category Command
61572
+ * @param {StructuredContentBlockInsert} options
61573
+ */
61574
+ insertStructuredContentBlock: (options = {}) => ({ editor, dispatch, state: state2, tr }) => {
61575
+ const { schema } = editor;
61576
+ let { from: from2, to } = state2.selection;
61577
+ if (dispatch) {
61578
+ const selectionContent = state2.selection.content();
61579
+ let content = null;
61580
+ if (selectionContent.size) {
61581
+ content = selectionContent.content;
61582
+ }
61583
+ if (options.html) {
61584
+ const html = htmlHandler(options.html, editor);
61585
+ const doc2 = DOMParser$1.fromSchema(schema).parse(html);
61586
+ content = doc2.content;
61587
+ }
61588
+ if (options.json) {
61589
+ content = schema.nodeFromJSON(options.json);
61590
+ }
61591
+ if (!content) {
61592
+ content = schema.nodeFromJSON({ type: "paragraph", content: [] });
61593
+ }
61594
+ const attrs = {
61595
+ ...options.attrs,
61596
+ id: options.attrs?.id || generateRandomSigned32BitIntStrId(),
61597
+ tag: "block_table_sdt",
61598
+ alias: options.attrs?.alias || "Structured content"
61599
+ };
61600
+ const node = schema.nodes.structuredContentBlock.create(attrs, content, null);
61601
+ const parent = findParentNode((node2) => node2.type.name === "structuredContentBlock")(state2.selection);
61602
+ if (parent) {
61603
+ const insertPos = parent.pos + parent.node.nodeSize;
61604
+ from2 = to = insertPos;
61605
+ }
61606
+ tr.replaceRangeWith(from2, to, node);
61607
+ }
61608
+ return true;
61609
+ },
61610
+ /**
61611
+ * Updates a structured content attributes or content.
61612
+ * If the updated node does not match the schema, it will not be updated.
61613
+ * @category Command
61614
+ * @param {string} id
61615
+ * @param {StructuredContentUpdate} options
61616
+ */
61617
+ updateStructuredContentById: (id, options = {}) => ({ editor, dispatch, state: state2, tr }) => {
61618
+ const structuredContentTags = getStructuredContentTagsById(id, state2);
61619
+ if (!structuredContentTags.length) {
61620
+ return true;
61621
+ }
61622
+ const { schema } = editor;
61623
+ if (dispatch) {
61624
+ const structuredContent = structuredContentTags[0];
61625
+ const { pos, node } = structuredContent;
61626
+ const posFrom = pos;
61627
+ const posTo = pos + node.nodeSize;
61628
+ let content = null;
61629
+ if (options.text) {
61630
+ content = schema.text(options.text);
61631
+ }
61632
+ if (options.html) {
61633
+ const html = htmlHandler(options.html, editor);
61634
+ const doc2 = DOMParser$1.fromSchema(schema).parse(html);
61635
+ content = doc2.content;
61636
+ }
61637
+ if (options.json) {
61638
+ content = schema.nodeFromJSON(options.json);
61639
+ }
61640
+ if (!content) {
61641
+ content = node.content;
61642
+ }
61643
+ const updatedNode = node.type.create({ ...node.attrs, ...options.attrs }, content, node.marks);
61644
+ try {
61645
+ updatedNode.check();
61646
+ } catch {
61647
+ console.error("Updated node does not conform to the schema");
61648
+ return false;
61649
+ }
61650
+ tr.replaceWith(posFrom, posTo, updatedNode);
61651
+ }
61652
+ return true;
61653
+ },
61654
+ /**
61655
+ * Removes a structured content.
61656
+ * @category Command
61657
+ * @param {Array<{ node: Node, pos: number }>} structuredContentTags
61658
+ */
61659
+ deleteStructuredContent: (structuredContentTags) => ({ dispatch, tr }) => {
61660
+ if (!structuredContentTags.length) {
61661
+ return true;
61662
+ }
61663
+ if (dispatch) {
61664
+ structuredContentTags.forEach((structuredContent) => {
61665
+ const { pos, node } = structuredContent;
61666
+ const posFrom = tr.mapping.map(pos);
61667
+ const posTo = tr.mapping.map(pos + node.nodeSize);
61668
+ const currentNode = tr.doc.nodeAt(posFrom);
61669
+ if (currentNode && node.eq(currentNode)) {
61670
+ tr.delete(posFrom, posTo);
61671
+ }
61672
+ });
61673
+ }
61674
+ return true;
61675
+ },
61676
+ /**
61677
+ * Removes a structured content by ID.
61678
+ * @category Command
61679
+ * @param {string | string[]} idOrIds
61680
+ */
61681
+ deleteStructuredContentById: (idOrIds) => ({ dispatch, state: state2, tr }) => {
61682
+ const structuredContentTags = getStructuredContentTagsById(idOrIds, state2);
61683
+ if (!structuredContentTags.length) {
61684
+ return true;
61685
+ }
61686
+ if (dispatch) {
61687
+ structuredContentTags.forEach((structuredContent) => {
61688
+ const { pos, node } = structuredContent;
61689
+ const posFrom = tr.mapping.map(pos);
61690
+ const posTo = tr.mapping.map(pos + node.nodeSize);
61691
+ const currentNode = tr.doc.nodeAt(posFrom);
61692
+ if (currentNode && node.eq(currentNode)) {
61693
+ tr.delete(posFrom, posTo);
61694
+ }
61695
+ });
61696
+ }
61697
+ return true;
61698
+ },
61699
+ /**
61700
+ * Removes a structured content at cursor, preserving its content.
61701
+ * @category Command
61702
+ */
61703
+ deleteStructuredContentAtSelection: () => ({ dispatch, state: state2, tr }) => {
61704
+ const predicate = (node) => STRUCTURED_CONTENT_NAMES.includes(node.type.name);
61705
+ const structuredContent = findParentNode(predicate)(state2.selection);
61706
+ if (!structuredContent) {
61707
+ return true;
61708
+ }
61709
+ if (dispatch) {
61710
+ const { node, pos } = structuredContent;
61711
+ const posFrom = pos;
61712
+ const posTo = posFrom + node.nodeSize;
61713
+ const content = node.content;
61714
+ tr.replaceWith(posFrom, posTo, content);
61715
+ }
61716
+ return true;
61717
+ }
61718
+ };
61719
+ },
61720
+ addHelpers() {
61721
+ return {
61722
+ ...structuredContentHelpers
61723
+ };
61724
+ }
61725
+ });
61726
+ class DocumentSectionView {
61727
+ constructor(node, getPos, decorations, editor) {
61728
+ __privateAdd$1(this, _DocumentSectionView_instances);
61729
+ this.node = node;
61730
+ this.editor = editor;
61731
+ this.decorations = decorations;
61732
+ this.view = editor.view;
61733
+ this.getPos = getPos;
61734
+ __privateMethod$1(this, _DocumentSectionView_instances, init_fn2).call(this);
61735
+ }
61736
+ }
61737
+ _DocumentSectionView_instances = /* @__PURE__ */ new WeakSet();
61738
+ init_fn2 = function() {
61739
+ const { attrs } = this.node;
61740
+ const { id, title, description } = attrs;
61741
+ this.dom = document.createElement("div");
61742
+ this.dom.className = "sd-document-section-block";
61743
+ this.dom.setAttribute("data-id", id);
61744
+ this.dom.setAttribute("data-title", title);
61745
+ this.dom.setAttribute("data-description", description);
61746
+ this.dom.setAttribute("aria-label", "Document section");
61747
+ __privateMethod$1(this, _DocumentSectionView_instances, addToolTip_fn).call(this);
61748
+ this.contentDOM = document.createElement("div");
61749
+ this.contentDOM.className = "sd-document-section-block-content";
61750
+ this.contentDOM.setAttribute("contenteditable", "true");
61751
+ this.dom.appendChild(this.contentDOM);
61752
+ };
61753
+ addToolTip_fn = function() {
61754
+ const { title } = this.node.attrs;
61755
+ this.infoDiv = document.createElement("div");
61756
+ this.infoDiv.className = "sd-document-section-block-info";
61757
+ const textSpan = document.createElement("span");
61758
+ textSpan.textContent = title || "Document section";
61759
+ this.infoDiv.appendChild(textSpan);
61760
+ this.infoDiv.setAttribute("contenteditable", "false");
61761
+ this.dom.appendChild(this.infoDiv);
61762
+ };
61763
+ const getAllSections = (editor) => {
61764
+ if (!editor) return [];
61765
+ const type2 = editor.schema.nodes.documentSection;
61766
+ if (!type2) return [];
61767
+ const sections = [];
61768
+ const { state: state2 } = editor;
61769
+ state2.doc.descendants((node, pos) => {
61770
+ if (node.type.name === type2.name) {
61771
+ sections.push({ node, pos });
61772
+ }
61773
+ });
61774
+ return sections;
61775
+ };
61776
+ const exportSectionsToHTML = (editor) => {
61777
+ const sections = getAllSections(editor);
61778
+ const processedSections = /* @__PURE__ */ new Set();
61779
+ const result = [];
61780
+ sections.forEach(({ node }) => {
61781
+ const { attrs } = node;
61782
+ const { id, title, description } = attrs;
61783
+ if (processedSections.has(id)) return;
61784
+ processedSections.add(id);
61785
+ const html = getHTMLFromNode(node, editor);
61786
+ result.push({
61787
+ id,
61788
+ title,
61789
+ description,
61790
+ html
61791
+ });
61792
+ });
61793
+ return result;
61794
+ };
61795
+ const getHTMLFromNode = (node, editor) => {
61796
+ const tempDocument = document.implementation.createHTMLDocument();
61797
+ const container = tempDocument.createElement("div");
61798
+ const fragment = DOMSerializer.fromSchema(editor.schema).serializeFragment(node.content);
61799
+ container.appendChild(fragment);
61800
+ let html = container.innerHTML;
61801
+ return html;
61802
+ };
61803
+ const exportSectionsToJSON = (editor) => {
61804
+ const sections = getAllSections(editor);
61805
+ const processedSections = /* @__PURE__ */ new Set();
61806
+ const result = [];
61807
+ sections.forEach(({ node }) => {
61808
+ const { attrs } = node;
61809
+ const { id, title, description } = attrs;
61810
+ if (processedSections.has(id)) return;
61811
+ processedSections.add(id);
61812
+ result.push({
61813
+ id,
61814
+ title,
61815
+ description,
61816
+ content: node.toJSON()
61817
+ });
61818
+ });
61819
+ return result;
61820
+ };
61821
+ const getLinkedSectionEditor = (id, options, editor) => {
61822
+ const sections = getAllSections(editor);
61823
+ const section = sections.find((s) => s.node.attrs.id === id);
61824
+ if (!section) return null;
61825
+ const child = editor.createChildEditor({
61826
+ ...options,
61827
+ onUpdate: ({ editor: childEditor, transaction }) => {
61828
+ const isFromtLinkedParent = transaction.getMeta("fromLinkedParent");
61829
+ if (isFromtLinkedParent) return;
61830
+ const updatedContent = childEditor.state.doc.content;
61831
+ const sectionNode = getAllSections(editor)?.find((s) => s.node.attrs.id === id);
61832
+ if (!sectionNode) return;
61833
+ const { pos, node } = sectionNode;
61834
+ const newNode = node.type.create(node.attrs, updatedContent, node.marks);
61835
+ const tr = editor.state.tr.replaceWith(pos, pos + node.nodeSize, newNode);
61836
+ tr.setMeta("fromLinkedChild", true);
61837
+ editor.view.dispatch(tr);
61838
+ }
61839
+ });
61840
+ editor.on("update", ({ transaction }) => {
61841
+ const isFromLinkedChild = transaction.getMeta("fromLinkedChild");
61842
+ if (isFromLinkedChild) return;
61843
+ const sectionNode = getAllSections(editor)?.find((s) => s.node.attrs.id === id);
61844
+ if (!sectionNode) return;
61845
+ const sectionContent = sectionNode.node.content;
61846
+ const json = {
61847
+ type: "doc",
61848
+ content: sectionContent.content.map((node) => node.toJSON())
61849
+ };
61850
+ const childTr = child.state.tr;
61851
+ childTr.setMeta("fromLinkedParent", true);
61852
+ childTr.replaceWith(0, child.state.doc.content.size, child.schema.nodeFromJSON(json));
61853
+ child.view.dispatch(childTr);
61854
+ });
61855
+ return child;
61856
+ };
61857
+ const SectionHelpers = {
61858
+ getAllSections,
61859
+ exportSectionsToHTML,
61860
+ exportSectionsToJSON,
61861
+ getLinkedSectionEditor
61862
+ };
61863
+ const DocumentSection = Node$1.create({
61864
+ name: "documentSection",
61865
+ group: "block",
61866
+ content: "block*",
61867
+ atom: true,
61868
+ isolating: true,
61869
+ addOptions() {
61870
+ return {
61871
+ htmlAttributes: {
61872
+ class: "sd-document-section-block",
61873
+ "aria-label": "Structured content block"
61874
+ }
61875
+ };
61876
+ },
61877
+ parseDOM() {
61878
+ return [
61879
+ {
61880
+ tag: "div.sd-document-section-block",
61881
+ priority: 60
61882
+ }
61883
+ ];
61884
+ },
61885
+ renderDOM({ htmlAttributes }) {
61886
+ return ["div", Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes), 0];
61887
+ },
61888
+ addAttributes() {
61889
+ return {
61890
+ id: {},
61891
+ sdBlockId: {
61892
+ default: null,
61893
+ keepOnSplit: false,
61894
+ parseDOM: (elem) => elem.getAttribute("data-sd-block-id"),
61895
+ renderDOM: (attrs) => {
61896
+ return attrs.sdBlockId ? { "data-sd-block-id": attrs.sdBlockId } : {};
61897
+ }
61898
+ },
61899
+ title: {},
61900
+ description: {},
61901
+ sectionType: {},
61902
+ isLocked: { default: false }
61903
+ };
61904
+ },
61905
+ addNodeView() {
61906
+ return ({ node, editor, getPos, decorations }) => {
61907
+ return new DocumentSectionView(node, getPos, decorations, editor);
61908
+ };
61909
+ },
61910
+ addCommands() {
61911
+ return {
61912
+ /**
61913
+ * Create a lockable content section
61914
+ * @category Command
61915
+ * @param {SectionCreate} [options={}] - Section configuration
61916
+ * @example
61917
+ * editor.commands.createDocumentSection({
61918
+ * id: 1,
61919
+ * title: 'Terms & Conditions',
61920
+ * isLocked: true,
61921
+ * html: '<p>Legal content...</p>'
61922
+ * })
61923
+ */
61924
+ createDocumentSection: (options = {}) => ({ tr, state: state2, dispatch, editor }) => {
61925
+ const { selection } = state2;
61926
+ let { from: from2, to } = selection;
61927
+ let content = selection.content().content;
61928
+ const { html: optionsHTML, json: optionsJSON } = options;
61929
+ if (optionsHTML) {
61930
+ const html = htmlHandler(optionsHTML, this.editor);
61931
+ const doc2 = DOMParser$1.fromSchema(this.editor.schema).parse(html);
61932
+ content = doc2.content;
61933
+ }
61934
+ if (optionsJSON) {
61935
+ content = this.editor.schema.nodeFromJSON(optionsJSON);
61936
+ }
61937
+ if (!content?.content?.length) {
61938
+ content = this.editor.schema.nodeFromJSON({ type: "paragraph", content: [] });
61939
+ }
61940
+ if (!options.id) {
61941
+ const allSections = SectionHelpers.getAllSections(editor);
61942
+ options.id = allSections.length + 1;
61943
+ }
61944
+ if (!options.title) {
61945
+ options.title = "Document section";
61946
+ }
61947
+ const node = this.type.createAndFill(options, content);
61948
+ if (!node) return false;
61949
+ const isAlreadyInSdtBlock = findParentNode((node2) => node2.type.name === "documentSection")(selection);
61950
+ if (isAlreadyInSdtBlock && isAlreadyInSdtBlock.node) {
61951
+ const insertPos2 = isAlreadyInSdtBlock.pos + isAlreadyInSdtBlock.node.nodeSize;
61952
+ from2 = insertPos2;
61953
+ to = insertPos2;
61954
+ }
61955
+ tr.replaceRangeWith(from2, to, node);
61956
+ const nodeEnd = from2 + node.nodeSize;
61957
+ let shouldInsertParagraph = true;
61958
+ let insertPos = nodeEnd;
61959
+ if (nodeEnd >= tr.doc.content.size) {
61960
+ insertPos = tr.doc.content.size;
61961
+ if (insertPos > 0) {
61962
+ const $endPos = tr.doc.resolve(insertPos);
61963
+ if ($endPos.nodeBefore && $endPos.nodeBefore.type.name === "paragraph") {
61964
+ shouldInsertParagraph = false;
61965
+ }
61966
+ }
61967
+ }
61968
+ if (shouldInsertParagraph) {
61969
+ const emptyParagraph = tr.doc.type.schema.nodes.paragraph.create();
61970
+ tr.insert(insertPos, emptyParagraph);
61971
+ }
61972
+ if (dispatch) {
61973
+ tr.setMeta("documentSection", { action: "create" });
61974
+ dispatch(tr);
61975
+ setTimeout(() => {
61976
+ try {
61977
+ const currentState = editor.state;
61978
+ const docSize = currentState.doc.content.size;
61979
+ let targetPos = from2 + node.nodeSize;
61980
+ if (shouldInsertParagraph) {
61981
+ targetPos += 1;
61982
+ }
61983
+ targetPos = Math.min(targetPos, docSize);
61984
+ if (targetPos < docSize && targetPos > 0) {
61985
+ const newSelection = Selection.near(currentState.doc.resolve(targetPos));
61986
+ const newTr = currentState.tr.setSelection(newSelection);
61987
+ editor.view.dispatch(newTr);
61988
+ }
61989
+ } catch (e) {
61990
+ console.warn("Could not set delayed selection:", e);
61991
+ }
61992
+ }, 0);
61993
+ }
61994
+ return true;
61995
+ },
61996
+ /**
61997
+ * Remove section wrapper at cursor, preserving its content
61998
+ * @category Command
61999
+ * @example
62000
+ * editor.commands.removeSectionAtSelection()
62001
+ * @note Content stays in document, only section wrapper is removed
62002
+ */
62003
+ removeSectionAtSelection: () => ({ tr, dispatch }) => {
62004
+ const sdtNode = findParentNode((node2) => node2.type.name === "documentSection")(tr.selection);
62005
+ if (!sdtNode) return false;
62006
+ const { node, pos } = sdtNode;
62007
+ const nodeStart = pos;
62008
+ const nodeEnd = nodeStart + node.nodeSize;
62009
+ const contentToPreserve = node.content;
62010
+ tr.delete(nodeStart, nodeEnd);
62011
+ if (contentToPreserve.size > 0) {
62012
+ tr.insert(nodeStart, contentToPreserve);
62013
+ }
62014
+ const newPos = Math.min(nodeStart, tr.doc.content.size);
62015
+ tr.setSelection(Selection.near(tr.doc.resolve(newPos)));
62016
+ if (dispatch) {
62017
+ tr.setMeta("documentSection", { action: "delete" });
62018
+ dispatch(tr);
62019
+ }
62020
+ return true;
62021
+ },
62022
+ /**
62023
+ * Delete section and all its content
62024
+ * @category Command
62025
+ * @param {number} id - Section to delete
62026
+ * @example
62027
+ * editor.commands.removeSectionById(123)
62028
+ */
62029
+ removeSectionById: (id) => ({ tr, dispatch }) => {
62030
+ const sections = SectionHelpers.getAllSections(this.editor);
62031
+ const sectionToRemove = sections.find(({ node: node2 }) => node2.attrs.id === id);
62032
+ if (!sectionToRemove) return false;
62033
+ const { pos, node } = sectionToRemove;
62034
+ const nodeStart = pos;
62035
+ const nodeEnd = nodeStart + node.nodeSize;
62036
+ tr.delete(nodeStart, nodeEnd);
62037
+ if (dispatch) {
62038
+ tr.setMeta("documentSection", { action: "delete", id });
62039
+ dispatch(tr);
62040
+ }
62041
+ return true;
62042
+ },
62043
+ /**
62044
+ * Lock section against edits
62045
+ * @category Command
62046
+ * @param {number} id - Section to lock
62047
+ * @example
62048
+ * editor.commands.lockSectionById(123)
62049
+ */
62050
+ lockSectionById: (id) => ({ tr, dispatch }) => {
62051
+ const sections = SectionHelpers.getAllSections(this.editor);
62052
+ const sectionToLock = sections.find(({ node }) => node.attrs.id === id);
62053
+ if (!sectionToLock) return false;
62054
+ tr.setNodeMarkup(sectionToLock.pos, null, { ...sectionToLock.node.attrs, isLocked: true });
62055
+ if (dispatch) {
62056
+ tr.setMeta("documentSection", { action: "lock", id });
62057
+ dispatch(tr);
62058
+ }
62059
+ return true;
62060
+ },
62061
+ /**
62062
+ * Modify section attributes or content
62063
+ * @category Command
62064
+ * @param {SectionUpdate} options - Changes to apply
62065
+ * @example
62066
+ * editor.commands.updateSectionById({ id: 123, attrs: { isLocked: false } })
62067
+ * editor.commands.updateSectionById({ id: 123, html: '<p>New content</p>' })
62068
+ * editor.commands.updateSectionById({
62069
+ * id: 123,
62070
+ * html: '<p>Updated</p>',
62071
+ * attrs: { title: 'New Title' }
62072
+ * })
62073
+ */
62074
+ updateSectionById: ({ id, html, json, attrs }) => ({ tr, dispatch, editor }) => {
62075
+ const sections = SectionHelpers.getAllSections(editor || this.editor);
62076
+ const sectionToUpdate = sections.find(({ node: node2 }) => node2.attrs.id === id);
62077
+ if (!sectionToUpdate) return false;
62078
+ const { pos, node } = sectionToUpdate;
62079
+ let newContent = null;
62080
+ if (html) {
62081
+ const htmlDoc = htmlHandler(html, editor || this.editor);
62082
+ const doc2 = DOMParser$1.fromSchema((editor || this.editor).schema).parse(htmlDoc);
62083
+ newContent = doc2.content;
62084
+ }
62085
+ if (json) {
62086
+ newContent = (editor || this.editor).schema.nodeFromJSON(json);
62087
+ }
62088
+ if (!newContent) {
62089
+ newContent = node.content;
62090
+ }
62091
+ const updatedNode = node.type.create({ ...node.attrs, ...attrs }, newContent, node.marks);
62092
+ tr.replaceWith(pos, pos + node.nodeSize, updatedNode);
62093
+ if (dispatch) {
62094
+ tr.setMeta("documentSection", { action: "update", id, attrs });
62095
+ dispatch(tr);
62096
+ }
62097
+ return true;
62098
+ }
62099
+ };
62100
+ },
62101
+ addHelpers() {
62102
+ return {
62103
+ ...SectionHelpers
62104
+ };
62105
+ }
62106
+ });
62107
+ const Document = Node$1.create({
62108
+ name: "doc",
62109
+ topNode: true,
62110
+ content: "block+",
62111
+ parseDOM() {
62112
+ return [{ tag: "doc" }];
62113
+ },
62114
+ renderDOM() {
62115
+ return ["doc", 0];
62116
+ },
62117
+ addAttributes() {
62118
+ return {
62119
+ attributes: {
62120
+ rendered: false,
62121
+ "aria-label": "Document node"
62122
+ }
62123
+ };
62124
+ },
62125
+ addCommands() {
62126
+ return {
62127
+ /**
62128
+ * Get document statistics
62129
+ * @category Command
62130
+ * @example
62131
+ * // Get word and character count
62132
+ * const stats = editor.commands.getDocumentStats()
62133
+ * console.log(`${stats.words} words, ${stats.characters} characters`)
62134
+ * @note Returns word count, character count, and paragraph count
62135
+ */
62136
+ getDocumentStats: () => ({ editor }) => {
62137
+ const text = editor.getText();
62138
+ const words = text.split(/\s+/).filter((word) => word.length > 0).length;
62139
+ const characters = text.length;
62140
+ const paragraphs = editor.state.doc.content.childCount;
62141
+ return {
62142
+ words,
62143
+ characters,
62144
+ paragraphs
62145
+ };
62146
+ },
62147
+ /**
62148
+ * Clear entire document
62149
+ * @category Command
62150
+ * @example
62151
+ * editor.commands.clearDocument()
62152
+ * @note Replaces all content with an empty paragraph
62153
+ */
62154
+ clearDocument: () => ({ commands: commands2 }) => {
62155
+ return commands2.setContent("<p></p>");
62156
+ }
62157
+ };
62158
+ }
62159
+ });
62160
+ const Text = Node$1.create({
62161
+ name: "text",
62162
+ group: "inline",
62163
+ inline: true,
62164
+ addOptions() {
62165
+ return {};
62166
+ }
62167
+ });
62168
+ const splitRun = () => (props) => {
62169
+ const { state: state2, view, tr } = props;
62170
+ const { $from, empty: empty2 } = state2.selection;
62171
+ if (!empty2) return false;
62172
+ if ($from.parent.type.name !== "run") return false;
62173
+ const handled = splitBlock(state2, (transaction) => {
62174
+ view.dispatch(transaction);
62175
+ });
62176
+ if (handled) {
62177
+ tr.setMeta("preventDispatch", true);
62178
+ }
62179
+ return handled;
62180
+ };
62181
+ const Run = OxmlNode.create({
62182
+ name: "run",
62183
+ oXmlName: "w:r",
62184
+ group: "inline",
62185
+ inline: true,
62186
+ content: "inline*",
62187
+ selectable: false,
62188
+ childToAttributes: ["runProperties"],
62189
+ addOptions() {
62190
+ return {
62191
+ htmlAttributes: {
62192
+ "data-run": "1"
62193
+ }
62194
+ };
62195
+ },
62196
+ addAttributes() {
62197
+ return {
62198
+ runProperties: {
62199
+ default: null,
62200
+ rendered: false,
62201
+ keepOnSplit: true
62202
+ },
62203
+ rsidR: {
62204
+ default: null,
62205
+ rendered: false,
62206
+ keepOnSplit: true
62207
+ },
62208
+ rsidRPr: {
62209
+ default: null,
62210
+ rendered: false,
62211
+ keepOnSplit: true
62212
+ },
62213
+ rsidDel: {
62214
+ default: null,
62215
+ rendered: false,
62216
+ keepOnSplit: true
62217
+ }
62218
+ };
62219
+ },
62220
+ addCommands() {
62221
+ return {
62222
+ splitRun
62223
+ };
62224
+ },
62225
+ parseDOM() {
62226
+ return [{ tag: "span[data-run]" }];
62227
+ },
62228
+ renderDOM({ htmlAttributes }) {
62229
+ const base2 = Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes);
62230
+ return ["span", base2, 0];
62231
+ }
62232
+ });
62233
+ const inputRegex$1 = /^\s*([-+*])\s$/;
62234
+ const BulletList = Node$1.create({
62235
+ name: "bulletList",
62236
+ group: "block list",
62237
+ selectable: false,
62238
+ content() {
62239
+ return `${this.options.itemTypeName}+`;
62240
+ },
62241
+ addOptions() {
62242
+ return {
62243
+ itemTypeName: "listItem",
62244
+ htmlAttributes: {
62245
+ "aria-label": "Bullet list node"
62246
+ },
62247
+ keepMarks: true,
62248
+ keepAttributes: false
62249
+ };
62250
+ },
62251
+ parseDOM() {
62252
+ return [{ tag: "ul" }];
62253
+ },
62254
+ renderDOM({ htmlAttributes }) {
62255
+ const attributes = Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes);
62256
+ return ["ul", attributes, 0];
62257
+ },
62258
+ addAttributes() {
62259
+ return {
62260
+ "list-style-type": {
62261
+ default: "bullet",
62262
+ rendered: false
62263
+ },
62264
+ listId: {
62265
+ rendered: false
62266
+ },
62267
+ sdBlockId: {
62268
+ default: null,
62269
+ keepOnSplit: false,
62270
+ parseDOM: (elem) => elem.getAttribute("data-sd-block-id"),
62271
+ renderDOM: (attrs) => {
62272
+ return attrs.sdBlockId ? { "data-sd-block-id": attrs.sdBlockId } : {};
62273
+ }
62274
+ },
62275
+ attributes: {
62276
+ rendered: false,
62277
+ keepOnSplit: true
62278
+ }
62279
+ };
62280
+ },
62281
+ addCommands() {
62282
+ return {
62283
+ /**
62284
+ * Toggle a bullet list at the current selection
62285
+ * @category Command
62286
+ * @example
62287
+ * // Toggle bullet list on selected text
62288
+ * editor.commands.toggleBulletList()
62289
+ * @note Converts selected paragraphs to list items or removes list formatting
62290
+ */
62291
+ toggleBulletList: () => (params2) => {
62292
+ return toggleList(this.type)(params2);
62293
+ }
62294
+ };
62295
+ },
62296
+ addShortcuts() {
62297
+ return {
62298
+ "Mod-Shift-8": () => {
62299
+ return this.editor.commands.toggleBulletList();
62300
+ }
62301
+ };
62302
+ },
62303
+ addInputRules() {
62304
+ return [
62305
+ new InputRule({
62306
+ match: inputRegex$1,
62307
+ handler: ({ state: state2, range: range2 }) => {
62308
+ const $pos = state2.selection.$from;
62309
+ const listItemType = state2.schema.nodes.listItem;
62310
+ for (let depth = $pos.depth; depth >= 0; depth--) {
62311
+ if ($pos.node(depth).type === listItemType) {
62312
+ return null;
62313
+ }
62314
+ }
62315
+ const { tr } = state2;
62316
+ tr.delete(range2.from, range2.to);
62317
+ ListHelpers.createNewList({
62318
+ listType: this.type,
62319
+ tr,
62320
+ editor: this.editor
62321
+ });
62322
+ }
62323
+ })
62324
+ ];
62325
+ }
62326
+ });
62327
+ const inputRegex = /^(\d+)\.\s$/;
62328
+ const OrderedList = Node$1.create({
61293
62329
  name: "orderedList",
61294
62330
  group: "block list",
61295
62331
  selectable: false,
@@ -62650,7 +63686,7 @@ Please report this to https://github.com/markedjs/marked.`, e) {
62650
63686
  this.decorations = decorations;
62651
63687
  this.view = editor.view;
62652
63688
  this.getPos = getPos;
62653
- __privateMethod$1(this, _ListItemNodeView_instances, init_fn2).call(this);
63689
+ __privateMethod$1(this, _ListItemNodeView_instances, init_fn3).call(this);
62654
63690
  activeListItemNodeViews.add(this);
62655
63691
  }
62656
63692
  refreshIndentStyling() {
@@ -62711,7 +63747,7 @@ Please report this to https://github.com/markedjs/marked.`, e) {
62711
63747
  }
62712
63748
  }
62713
63749
  _ListItemNodeView_instances = /* @__PURE__ */ new WeakSet();
62714
- init_fn2 = function() {
63750
+ init_fn3 = function() {
62715
63751
  const { attrs } = this.node;
62716
63752
  const { listLevel, listNumberingType, lvlText, numId, level, customFormat } = attrs;
62717
63753
  let orderMarker = "";
@@ -69820,984 +70856,335 @@ Please report this to https://github.com/markedjs/marked.`, e) {
69820
70856
  }
69821
70857
  };
69822
70858
  },
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: {
70859
+ addAttributes() {
70860
+ return {
70861
+ marksAsAttrs: {
70862
+ default: null,
70229
70863
  rendered: false
70230
70864
  }
70231
70865
  };
70232
70866
  },
70867
+ addNodeView() {
70868
+ return ({ node, editor, getPos, decorations }) => {
70869
+ const htmlAttributes = this.options.htmlAttributes;
70870
+ return new AutoPageNumberNodeView(node, getPos, decorations, editor, htmlAttributes);
70871
+ };
70872
+ },
70233
70873
  parseDOM() {
70234
- return [
70235
- {
70236
- tag: `div[data-type="${this.name}"]`
70237
- }
70238
- ];
70874
+ return [{ tag: 'span[data-id="auto-page-number"' }];
70239
70875
  },
70240
70876
  renderDOM({ htmlAttributes }) {
70241
- return ["div", Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes, { "data-type": this.name })];
70877
+ return ["span", Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes)];
70242
70878
  },
70243
70879
  addCommands() {
70244
70880
  return {
70245
70881
  /**
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
70882
+ * Insert an automatic page number
70264
70883
  * @category Command
70265
- * @param {ContentBlockConfig} config - Block configuration
70266
- * @example
70267
- * // Insert a spacer block
70268
- * editor.commands.insertContentBlock({ size: { height: 20 } })
70269
- *
70884
+ * @returns {Function} Command function
70270
70885
  * @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
70886
+ * editor.commands.addAutoPageNumber()
70887
+ * @note Only works in header/footer contexts
70277
70888
  */
70278
- insertContentBlock: (config2) => ({ commands: commands2 }) => {
70279
- return commands2.insertContent({
70280
- type: this.name,
70281
- attrs: config2
70282
- });
70889
+ addAutoPageNumber: () => ({ tr, dispatch, state: state2, editor }) => {
70890
+ const { options } = editor;
70891
+ if (!options.isHeaderOrFooter) return false;
70892
+ const { schema } = state2;
70893
+ const pageNumberType = schema?.nodes?.["page-number"];
70894
+ if (!pageNumberType) return false;
70895
+ const pageNumberNodeJSON = { type: "page-number" };
70896
+ const pageNumberNode = schema.nodeFromJSON(pageNumberNodeJSON);
70897
+ if (dispatch) {
70898
+ tr.replaceSelectionWith(pageNumberNode, false);
70899
+ tr.setMeta("forceUpdatePagination", true);
70900
+ }
70901
+ return true;
70283
70902
  }
70284
70903
  };
70904
+ },
70905
+ addShortcuts() {
70906
+ return {
70907
+ "Mod-Shift-alt-p": () => this.editor.commands.addAutoPageNumber()
70908
+ };
70285
70909
  }
70286
70910
  });
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",
70911
+ const TotalPageCount = Node$1.create({
70912
+ name: "total-page-number",
70913
+ group: "inline",
70498
70914
  inline: true,
70499
- content: "inline*",
70500
- isolating: true,
70501
- atom: false,
70502
- // false - has editable content.
70503
- draggable: true,
70915
+ atom: true,
70916
+ draggable: false,
70917
+ selectable: false,
70918
+ content: "text*",
70504
70919
  addOptions() {
70505
70920
  return {
70506
70921
  htmlAttributes: {
70507
- class: structuredContentClass$1,
70508
- "aria-label": "Structured content node"
70922
+ contenteditable: false,
70923
+ "data-id": "auto-total-pages",
70924
+ "aria-label": "Total page count node",
70925
+ class: "sd-editor-auto-total-pages"
70509
70926
  }
70510
70927
  };
70511
70928
  },
70512
70929
  addAttributes() {
70513
70930
  return {
70514
- id: {
70931
+ marksAsAttrs: {
70515
70932
  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
70933
  rendered: false
70524
70934
  }
70525
70935
  };
70526
70936
  },
70937
+ addNodeView() {
70938
+ return ({ node, editor, getPos, decorations }) => {
70939
+ const htmlAttributes = this.options.htmlAttributes;
70940
+ return new AutoPageNumberNodeView(node, getPos, decorations, editor, htmlAttributes);
70941
+ };
70942
+ },
70527
70943
  parseDOM() {
70528
- return [{ tag: "span[data-structured-content]" }];
70944
+ return [{ tag: 'span[data-id="auto-total-pages"' }];
70529
70945
  },
70530
70946
  renderDOM({ htmlAttributes }) {
70531
- return [
70532
- "span",
70533
- Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes, {
70534
- "data-structured-content": ""
70535
- }),
70536
- 0
70537
- ];
70947
+ return ["span", Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes), 0];
70538
70948
  },
70539
- addNodeView() {
70540
- return (props) => {
70541
- return new StructuredContentInlineView({ ...props });
70949
+ addCommands() {
70950
+ return {
70951
+ /**
70952
+ * Insert total page count
70953
+ * @category Command
70954
+ * @returns {Function} Command function
70955
+ * @example
70956
+ * editor.commands.addTotalPageCount()
70957
+ * @note Only works in header/footer contexts
70958
+ */
70959
+ addTotalPageCount: () => ({ tr, dispatch, state: state2, editor }) => {
70960
+ const { options } = editor;
70961
+ if (!options.isHeaderOrFooter) return false;
70962
+ const { schema } = state2;
70963
+ const pageNumberType = schema.nodes?.["total-page-number"];
70964
+ if (!pageNumberType) return false;
70965
+ const currentPages = editor?.options?.parentEditor?.currentTotalPages || 1;
70966
+ const pageNumberNode = {
70967
+ type: "total-page-number",
70968
+ content: [{ type: "text", text: String(currentPages) }]
70969
+ };
70970
+ const pageNode = schema.nodeFromJSON(pageNumberNode);
70971
+ if (dispatch) {
70972
+ tr.replaceSelectionWith(pageNode, false);
70973
+ }
70974
+ return true;
70975
+ }
70976
+ };
70977
+ },
70978
+ addShortcuts() {
70979
+ return {
70980
+ "Mod-Shift-alt-c": () => this.editor.commands.addTotalPageCount()
70542
70981
  };
70543
70982
  }
70544
70983
  });
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;
70984
+ const getNodeAttributes = (nodeName, editor) => {
70985
+ switch (nodeName) {
70986
+ case "page-number":
70987
+ return {
70988
+ text: editor.options.currentPageNumber || "1",
70989
+ className: "sd-editor-auto-page-number",
70990
+ dataId: "auto-page-number",
70991
+ ariaLabel: "Page number node"
70992
+ };
70993
+ case "total-page-number":
70994
+ return {
70995
+ text: editor.options.parentEditor?.currentTotalPages || "1",
70996
+ className: "sd-editor-auto-total-pages",
70997
+ dataId: "auto-total-pages",
70998
+ ariaLabel: "Total page count node"
70999
+ };
71000
+ default:
71001
+ return {};
70573
71002
  }
70574
- updateView() {
70575
- const domAttrs = Attribute.mergeAttributes(this.htmlAttributes);
70576
- updateDOMAttributes(this.dom, { ...domAttrs });
71003
+ };
71004
+ class AutoPageNumberNodeView {
71005
+ constructor(node, getPos, decorations, editor, htmlAttributes = {}) {
71006
+ __privateAdd$1(this, _AutoPageNumberNodeView_instances);
71007
+ this.node = node;
71008
+ this.editor = editor;
71009
+ this.view = editor.view;
71010
+ this.getPos = getPos;
71011
+ this.editor = editor;
71012
+ this.dom = __privateMethod$1(this, _AutoPageNumberNodeView_instances, renderDom_fn).call(this, node, htmlAttributes);
70577
71013
  }
70578
- update(node, decorations, innerDecorations) {
70579
- const result = super.update(node, decorations, innerDecorations);
70580
- if (!result) return false;
70581
- this.updateView();
71014
+ update(node) {
71015
+ const incomingType = node?.type?.name;
71016
+ const currentType = this.node?.type?.name;
71017
+ if (!incomingType || incomingType !== currentType) return false;
71018
+ this.node = node;
70582
71019
  return true;
70583
71020
  }
70584
71021
  }
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*",
71022
+ _AutoPageNumberNodeView_instances = /* @__PURE__ */ new WeakSet();
71023
+ renderDom_fn = function(node, htmlAttributes) {
71024
+ const attrs = getNodeAttributes(this.node.type.name, this.editor);
71025
+ const content = document.createTextNode(String(attrs.text));
71026
+ const nodeContent = document.createElement("span");
71027
+ nodeContent.className = attrs.className;
71028
+ nodeContent.setAttribute("data-id", attrs.dataId);
71029
+ nodeContent.setAttribute("aria-label", attrs.ariaLabel);
71030
+ const currentPos = this.getPos();
71031
+ const { styles, marks } = getMarksFromNeighbors(currentPos, this.view);
71032
+ __privateMethod$1(this, _AutoPageNumberNodeView_instances, scheduleUpdateNodeStyle_fn).call(this, currentPos, marks);
71033
+ Object.assign(nodeContent.style, styles);
71034
+ nodeContent.appendChild(content);
71035
+ Object.entries(htmlAttributes).forEach(([key2, value]) => {
71036
+ if (value) nodeContent.setAttribute(key2, value);
71037
+ });
71038
+ return nodeContent;
71039
+ };
71040
+ scheduleUpdateNodeStyle_fn = function(pos, marks) {
71041
+ setTimeout(() => {
71042
+ const { state: state2 } = this.editor;
71043
+ const { dispatch } = this.view;
71044
+ const node = state2.doc.nodeAt(pos);
71045
+ if (!node || node.isText) return;
71046
+ const currentMarks = node.attrs.marksAsAttrs || [];
71047
+ const newMarks = marks.map((m2) => ({ type: m2.type.name, attrs: m2.attrs }));
71048
+ const isEqual = JSON.stringify(currentMarks) === JSON.stringify(newMarks);
71049
+ if (isEqual) return;
71050
+ const newAttrs = {
71051
+ ...node.attrs,
71052
+ marksAsAttrs: newMarks
71053
+ };
71054
+ const tr = state2.tr.setNodeMarkup(pos, void 0, newAttrs);
71055
+ dispatch(tr);
71056
+ }, 0);
71057
+ };
71058
+ const getMarksFromNeighbors = (currentPos, view) => {
71059
+ const $pos = view.state.doc.resolve(currentPos);
71060
+ const styles = {};
71061
+ const marks = [];
71062
+ const before = $pos.nodeBefore;
71063
+ if (before) {
71064
+ Object.assign(styles, processMarks(before.marks));
71065
+ marks.push(...before.marks);
71066
+ }
71067
+ const after = $pos.nodeAfter;
71068
+ if (after) {
71069
+ Object.assign(styles, { ...styles, ...processMarks(after.marks) });
71070
+ marks.push(...after.marks);
71071
+ }
71072
+ return {
71073
+ styles,
71074
+ marks
71075
+ };
71076
+ };
71077
+ const processMarks = (marks) => {
71078
+ const styles = {};
71079
+ marks.forEach((mark) => {
71080
+ const { type: type2, attrs } = mark;
71081
+ switch (type2.name) {
71082
+ case "textStyle":
71083
+ if (attrs.fontFamily) styles["font-family"] = attrs.fontFamily;
71084
+ if (attrs.fontSize) styles["font-size"] = attrs.fontSize;
71085
+ if (attrs.color) styles["color"] = attrs.color;
71086
+ if (attrs.backgroundColor) styles["background-color"] = attrs.backgroundColor;
71087
+ break;
71088
+ case "bold":
71089
+ styles["font-weight"] = "bold";
71090
+ break;
71091
+ case "italic":
71092
+ styles["font-style"] = "italic";
71093
+ break;
71094
+ case "underline":
71095
+ styles["text-decoration"] = (styles["text-decoration"] || "") + " underline";
71096
+ break;
71097
+ case "strike":
71098
+ styles["text-decoration"] = (styles["text-decoration"] || "") + " line-through";
71099
+ break;
71100
+ default:
71101
+ if (attrs?.style) {
71102
+ Object.entries(attrs.style).forEach(([key2, value]) => {
71103
+ styles[key2] = value;
71104
+ });
71105
+ }
71106
+ break;
71107
+ }
71108
+ });
71109
+ return styles;
71110
+ };
71111
+ const ShapeContainer = Node$1.create({
71112
+ name: "shapeContainer",
71113
+ group: "block",
71114
+ content: "block+",
70591
71115
  isolating: true,
70592
- atom: false,
70593
- // false - has editable content.
70594
- draggable: true,
70595
71116
  addOptions() {
70596
71117
  return {
70597
71118
  htmlAttributes: {
70598
- class: structuredContentClass,
70599
- "aria-label": "Structured content block node"
71119
+ class: "sd-editor-shape-container",
71120
+ "aria-label": "Shape container node"
70600
71121
  }
70601
71122
  };
70602
71123
  },
70603
71124
  addAttributes() {
70604
71125
  return {
70605
- id: {
71126
+ fillcolor: {
71127
+ renderDOM: (attrs) => {
71128
+ if (!attrs.fillcolor) return {};
71129
+ return {
71130
+ style: `background-color: ${attrs.fillcolor}`
71131
+ };
71132
+ }
71133
+ },
71134
+ sdBlockId: {
70606
71135
  default: null,
70607
- parseDOM: (elem) => elem.getAttribute("data-id"),
71136
+ keepOnSplit: false,
71137
+ parseDOM: (elem) => elem.getAttribute("data-sd-block-id"),
70608
71138
  renderDOM: (attrs) => {
70609
- if (!attrs.id) return {};
70610
- return { "data-id": attrs.id };
71139
+ return attrs.sdBlockId ? { "data-sd-block-id": attrs.sdBlockId } : {};
70611
71140
  }
70612
71141
  },
70613
- sdtPr: {
71142
+ style: {
71143
+ renderDOM: (attrs) => {
71144
+ if (!attrs.style) return {};
71145
+ return {
71146
+ style: attrs.style
71147
+ };
71148
+ }
71149
+ },
71150
+ wrapAttributes: {
71151
+ rendered: false
71152
+ },
71153
+ attributes: {
70614
71154
  rendered: false
70615
71155
  }
70616
71156
  };
70617
71157
  },
70618
71158
  parseDOM() {
70619
- return [{ tag: "div[data-structured-content-block]" }];
71159
+ return [
71160
+ {
71161
+ tag: `div[data-type="${this.name}"]`
71162
+ }
71163
+ ];
70620
71164
  },
70621
71165
  renderDOM({ htmlAttributes }) {
70622
71166
  return [
70623
71167
  "div",
70624
- Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes, {
70625
- "data-structured-content-block": ""
70626
- }),
71168
+ Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes, { "data-type": this.name }),
70627
71169
  0
70628
71170
  ];
70629
- },
70630
- addNodeView() {
70631
- return (props) => {
70632
- return new StructuredContentBlockView({ ...props });
70633
- };
70634
71171
  }
70635
71172
  });
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",
71173
+ const ShapeTextbox = Node$1.create({
71174
+ name: "shapeTextbox",
70775
71175
  group: "block",
70776
- content: "block*",
70777
- atom: true,
71176
+ content: "paragraph* block*",
70778
71177
  isolating: true,
70779
71178
  addOptions() {
70780
71179
  return {
70781
71180
  htmlAttributes: {
70782
- class: "sd-document-section-block",
70783
- "aria-label": "Structured content block"
71181
+ class: "sd-editor-shape-textbox",
71182
+ "aria-label": "Shape textbox node"
70784
71183
  }
70785
71184
  };
70786
71185
  },
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
71186
  addAttributes() {
70799
71187
  return {
70800
- id: {},
70801
71188
  sdBlockId: {
70802
71189
  default: null,
70803
71190
  keepOnSplit: false,
@@ -70806,212 +71193,131 @@ Please report this to https://github.com/markedjs/marked.`, e) {
70806
71193
  return attrs.sdBlockId ? { "data-sd-block-id": attrs.sdBlockId } : {};
70807
71194
  }
70808
71195
  },
70809
- title: {},
70810
- description: {},
70811
- sectionType: {},
70812
- isLocked: { default: false }
71196
+ attributes: {
71197
+ rendered: false
71198
+ }
70813
71199
  };
70814
71200
  },
70815
- addNodeView() {
70816
- return ({ node, editor, getPos, decorations }) => {
70817
- return new DocumentSectionView(node, getPos, decorations, editor);
71201
+ parseDOM() {
71202
+ return [
71203
+ {
71204
+ tag: `div[data-type="${this.name}"]`
71205
+ }
71206
+ ];
71207
+ },
71208
+ renderDOM({ htmlAttributes }) {
71209
+ return [
71210
+ "div",
71211
+ Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes, { "data-type": this.name }),
71212
+ 0
71213
+ ];
71214
+ }
71215
+ });
71216
+ const ContentBlock = Node$1.create({
71217
+ name: "contentBlock",
71218
+ group: "inline",
71219
+ content: "",
71220
+ isolating: true,
71221
+ atom: true,
71222
+ inline: true,
71223
+ addOptions() {
71224
+ return {
71225
+ htmlAttributes: {
71226
+ contenteditable: false
71227
+ }
70818
71228
  };
70819
71229
  },
70820
- addCommands() {
71230
+ addAttributes() {
70821
71231
  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);
71232
+ horizontalRule: {
71233
+ default: false,
71234
+ renderDOM: ({ horizontalRule }) => {
71235
+ if (!horizontalRule) return {};
71236
+ return { "data-horizontal-rule": "true" };
70903
71237
  }
70904
- return true;
70905
71238
  },
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);
71239
+ size: {
71240
+ default: null,
71241
+ renderDOM: ({ size: size2 }) => {
71242
+ if (!size2) return {};
71243
+ let style2 = "";
71244
+ if (size2.top) style2 += `top: ${size2.top}px; `;
71245
+ if (size2.left) style2 += `left: ${size2.left}px; `;
71246
+ if (size2.width) style2 += `width: ${size2.width.toString().endsWith("%") ? size2.width : `${size2.width}px`}; `;
71247
+ if (size2.height)
71248
+ style2 += `height: ${size2.height.toString().endsWith("%") ? size2.height : `${size2.height}px`}; `;
71249
+ return { style: style2 };
70929
71250
  }
70930
- return true;
70931
71251
  },
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);
71252
+ background: {
71253
+ default: null,
71254
+ renderDOM: (attrs) => {
71255
+ if (!attrs.background) return {};
71256
+ return {
71257
+ style: `background-color: ${attrs.background}`
71258
+ };
70950
71259
  }
70951
- return true;
70952
71260
  },
71261
+ drawingContent: {
71262
+ rendered: false
71263
+ },
71264
+ attributes: {
71265
+ rendered: false
71266
+ }
71267
+ };
71268
+ },
71269
+ parseDOM() {
71270
+ return [
71271
+ {
71272
+ tag: `div[data-type="${this.name}"]`
71273
+ }
71274
+ ];
71275
+ },
71276
+ renderDOM({ htmlAttributes }) {
71277
+ return ["div", Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes, { "data-type": this.name })];
71278
+ },
71279
+ addCommands() {
71280
+ return {
70953
71281
  /**
70954
- * Lock section against edits
71282
+ * Insert a horizontal rule
70955
71283
  * @category Command
70956
- * @param {number} id - Section to lock
70957
71284
  * @example
70958
- * editor.commands.lockSectionById(123)
71285
+ * editor.commands.insertHorizontalRule()
71286
+ * @note Creates a visual separator between content sections
70959
71287
  */
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;
71288
+ insertHorizontalRule: () => ({ commands: commands2 }) => {
71289
+ return commands2.insertContent({
71290
+ type: this.name,
71291
+ attrs: {
71292
+ horizontalRule: true,
71293
+ size: { width: "100%", height: 2 },
71294
+ background: "#e5e7eb"
71295
+ }
71296
+ });
70970
71297
  },
70971
71298
  /**
70972
- * Modify section attributes or content
71299
+ * Insert a content block
70973
71300
  * @category Command
70974
- * @param {SectionUpdate} options - Changes to apply
71301
+ * @param {ContentBlockConfig} config - Block configuration
70975
71302
  * @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' }
71303
+ * // Insert a spacer block
71304
+ * editor.commands.insertContentBlock({ size: { height: 20 } })
71305
+ *
71306
+ * @example
71307
+ * // Insert a colored divider
71308
+ * editor.commands.insertContentBlock({
71309
+ * size: { width: '50%', height: 3 },
71310
+ * background: '#3b82f6'
70982
71311
  * })
71312
+ * @note Used for spacing, dividers, and special inline content
70983
71313
  */
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;
71314
+ insertContentBlock: (config2) => ({ commands: commands2 }) => {
71315
+ return commands2.insertContent({
71316
+ type: this.name,
71317
+ attrs: config2
71318
+ });
71008
71319
  }
71009
71320
  };
71010
- },
71011
- addHelpers() {
71012
- return {
71013
- ...SectionHelpers
71014
- };
71015
71321
  }
71016
71322
  });
71017
71323
  const { findChildren } = helpers;
@@ -77861,6 +78167,7 @@ Please report this to https://github.com/markedjs/marked.`, e) {
77861
78167
  Search,
77862
78168
  StructuredContent,
77863
78169
  StructuredContentBlock,
78170
+ StructuredContentCommands,
77864
78171
  DocumentSection,
77865
78172
  NodeResizer,
77866
78173
  CustomSelection,